summaryrefslogtreecommitdiffstats
path: root/private/net/svcdlls
diff options
context:
space:
mode:
authorAdam <you@example.com>2020-05-17 05:51:50 +0200
committerAdam <you@example.com>2020-05-17 05:51:50 +0200
commite611b132f9b8abe35b362e5870b74bce94a1e58e (patch)
treea5781d2ec0e085eeca33cf350cf878f2efea6fe5 /private/net/svcdlls
downloadNT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.gz
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.bz2
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.lz
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.xz
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.zst
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.zip
Diffstat (limited to 'private/net/svcdlls')
-rw-r--r--private/net/svcdlls/alrsvc/al.h199
-rw-r--r--private/net/svcdlls/alrsvc/alconfig.c295
-rw-r--r--private/net/svcdlls/alrsvc/alconfig.h33
-rw-r--r--private/net/svcdlls/alrsvc/alformat.c1782
-rw-r--r--private/net/svcdlls/alrsvc/alformat.h54
-rw-r--r--private/net/svcdlls/alrsvc/almain.c721
-rw-r--r--private/net/svcdlls/alrsvc/almain.h85
-rw-r--r--private/net/svcdlls/alrsvc/alrsvc.def6
-rw-r--r--private/net/svcdlls/alrsvc/alrsvc.rc12
-rw-r--r--private/net/svcdlls/alrsvc/altest.c309
-rw-r--r--private/net/svcdlls/alrsvc/lmnetlib.h74
-rw-r--r--private/net/svcdlls/alrsvc/makefile6
-rw-r--r--private/net/svcdlls/alrsvc/sources59
-rw-r--r--private/net/svcdlls/at/atcmd/at.rc12
-rw-r--r--private/net/svcdlls/at/atcmd/atcmd.c2290
-rw-r--r--private/net/svcdlls/at/atcmd/lmatmsg.mc193
-rw-r--r--private/net/svcdlls/at/atcmd/makefile6
-rw-r--r--private/net/svcdlls/at/atcmd/makefile.inc4
-rw-r--r--private/net/svcdlls/at/atcmd/sources32
-rw-r--r--private/net/svcdlls/at/atnames.h24
-rw-r--r--private/net/svcdlls/at/atsvc.acf11
-rw-r--r--private/net/svcdlls/at/atsvc.idl127
-rw-r--r--private/net/svcdlls/at/client/atbind.c113
-rw-r--r--private/net/svcdlls/at/client/atclient.h44
-rw-r--r--private/net/svcdlls/at/client/atstub.c302
-rw-r--r--private/net/svcdlls/at/client/atstub.def12
-rw-r--r--private/net/svcdlls/at/client/fmt.c163
-rw-r--r--private/net/svcdlls/at/client/handle.c182
-rw-r--r--private/net/svcdlls/at/client/makefile6
-rw-r--r--private/net/svcdlls/at/client/sources35
-rw-r--r--private/net/svcdlls/at/dirs30
-rw-r--r--private/net/svcdlls/at/imports.h41
-rw-r--r--private/net/svcdlls/at/imports.idl67
-rw-r--r--private/net/svcdlls/at/makefil062
-rw-r--r--private/net/svcdlls/at/server/at.h384
-rw-r--r--private/net/svcdlls/at/server/atapi.c636
-rw-r--r--private/net/svcdlls/at/server/atdebug.c295
-rw-r--r--private/net/svcdlls/at/server/atenv.c176
-rw-r--r--private/net/svcdlls/at/server/atmain.c1060
-rw-r--r--private/net/svcdlls/at/server/atreg.c603
-rw-r--r--private/net/svcdlls/at/server/atsec.c246
-rw-r--r--private/net/svcdlls/at/server/attime.c355
-rw-r--r--private/net/svcdlls/at/server/makefile6
-rw-r--r--private/net/svcdlls/at/server/run.c242
-rw-r--r--private/net/svcdlls/at/server/sources38
-rw-r--r--private/net/svcdlls/browser/bchk/bchk.c1902
-rw-r--r--private/net/svcdlls/browser/bchk/bchk.h305
-rw-r--r--private/net/svcdlls/browser/bchk/makefile6
-rw-r--r--private/net/svcdlls/browser/bchk/sources53
-rw-r--r--private/net/svcdlls/browser/bchk/support.c449
-rw-r--r--private/net/svcdlls/browser/bowser.acf11
-rw-r--r--private/net/svcdlls/browser/bowser.idl222
-rw-r--r--private/net/svcdlls/browser/bperf/bperf.c359
-rw-r--r--private/net/svcdlls/browser/bperf/makefile6
-rw-r--r--private/net/svcdlls/browser/bperf/sources49
-rw-r--r--private/net/svcdlls/browser/brcommon.h335
-rw-r--r--private/net/svcdlls/browser/brnames.h21
-rw-r--r--private/net/svcdlls/browser/browtest/browtest.cht120
-rw-r--r--private/net/svcdlls/browser/browtest/dirs26
-rw-r--r--private/net/svcdlls/browser/browtest/src/browfunc.c564
-rw-r--r--private/net/svcdlls/browser/browtest/src/browfunc.h212
-rw-r--r--private/net/svcdlls/browser/browtest/src/browglob.h62
-rw-r--r--private/net/svcdlls/browser/browtest/src/browmdom.c1165
-rw-r--r--private/net/svcdlls/browser/browtest/src/browmdom.h24
-rw-r--r--private/net/svcdlls/browser/browtest/src/browstrs.c534
-rw-r--r--private/net/svcdlls/browser/browtest/src/browstrs.h24
-rw-r--r--private/net/svcdlls/browser/browtest/src/browtest.c1947
-rw-r--r--private/net/svcdlls/browser/browtest/src/browtest.h148
-rw-r--r--private/net/svcdlls/browser/browtest/src/browtest.inp56
-rw-r--r--private/net/svcdlls/browser/browtest/src/browutil.c780
-rw-r--r--private/net/svcdlls/browser/browtest/src/browutil.h16
-rw-r--r--private/net/svcdlls/browser/browtest/src/makefile6
-rw-r--r--private/net/svcdlls/browser/browtest/src/makefile.inc0
-rw-r--r--private/net/svcdlls/browser/browtest/src/sources70
-rw-r--r--private/net/svcdlls/browser/bwatch/bwatch.c704
-rw-r--r--private/net/svcdlls/browser/bwatch/bwatch.h120
-rw-r--r--private/net/svcdlls/browser/bwatch/makefile6
-rw-r--r--private/net/svcdlls/browser/bwatch/sources52
-rw-r--r--private/net/svcdlls/browser/client/brclient.h71
-rw-r--r--private/net/svcdlls/browser/client/browbind.c186
-rw-r--r--private/net/svcdlls/browser/client/browdeb.c4474
-rw-r--r--private/net/svcdlls/browser/client/browdeb.rc12
-rw-r--r--private/net/svcdlls/browser/client/browstat.c3
-rw-r--r--private/net/svcdlls/browser/client/browstat.rc12
-rw-r--r--private/net/svcdlls/browser/client/browstub.c1493
-rw-r--r--private/net/svcdlls/browser/client/brtsenum.c590
-rw-r--r--private/net/svcdlls/browser/client/bwatch.c351
-rw-r--r--private/net/svcdlls/browser/client/daytona/makefile6
-rw-r--r--private/net/svcdlls/browser/client/daytona/makefile.inc5
-rw-r--r--private/net/svcdlls/browser/client/daytona/sources86
-rw-r--r--private/net/svcdlls/browser/client/dirs25
-rw-r--r--private/net/svcdlls/browser/common/daytona/makefile6
-rw-r--r--private/net/svcdlls/browser/common/daytona/sources55
-rw-r--r--private/net/svcdlls/browser/common/dirs25
-rw-r--r--private/net/svcdlls/browser/common/interim.c976
-rw-r--r--private/net/svcdlls/browser/common/utils.c969
-rw-r--r--private/net/svcdlls/browser/dirs30
-rw-r--r--private/net/svcdlls/browser/imports.h46
-rw-r--r--private/net/svcdlls/browser/imports.idl67
-rw-r--r--private/net/svcdlls/browser/makefil061
-rw-r--r--private/net/svcdlls/browser/server/bowqueue.c644
-rw-r--r--private/net/svcdlls/browser/server/bowqueue.h101
-rw-r--r--private/net/svcdlls/browser/server/bowsvc.rc12
-rw-r--r--private/net/svcdlls/browser/server/br.h137
-rw-r--r--private/net/svcdlls/browser/server/brbackup.h78
-rw-r--r--private/net/svcdlls/browser/server/brconfig.c734
-rw-r--r--private/net/svcdlls/browser/server/brconfig.h134
-rw-r--r--private/net/svcdlls/browser/server/brconst.h98
-rw-r--r--private/net/svcdlls/browser/server/brdevice.c686
-rw-r--r--private/net/svcdlls/browser/server/brdevice.h138
-rw-r--r--private/net/svcdlls/browser/server/brdmmstr.c420
-rw-r--r--private/net/svcdlls/browser/server/brmain.c1336
-rw-r--r--private/net/svcdlls/browser/server/brmain.h166
-rw-r--r--private/net/svcdlls/browser/server/brmaster.c1541
-rw-r--r--private/net/svcdlls/browser/server/brmaster.h137
-rw-r--r--private/net/svcdlls/browser/server/browsdom.c32
-rw-r--r--private/net/svcdlls/browser/server/browsdom.h46
-rw-r--r--private/net/svcdlls/browser/server/browser.c1591
-rw-r--r--private/net/svcdlls/browser/server/browslst.c70
-rw-r--r--private/net/svcdlls/browser/server/browslst.h95
-rw-r--r--private/net/svcdlls/browser/server/browsnet.c870
-rw-r--r--private/net/svcdlls/browser/server/browsnet.h299
-rw-r--r--private/net/svcdlls/browser/server/brsec.h71
-rw-r--r--private/net/svcdlls/browser/server/brutil.c491
-rw-r--r--private/net/svcdlls/browser/server/brutil.h104
-rw-r--r--private/net/svcdlls/browser/server/brwan.c392
-rw-r--r--private/net/svcdlls/browser/server/brwan.h68
-rw-r--r--private/net/svcdlls/browser/server/brwins.c483
-rw-r--r--private/net/svcdlls/browser/server/brwins.h46
-rw-r--r--private/net/svcdlls/browser/server/daytona/browser.def7
-rw-r--r--private/net/svcdlls/browser/server/daytona/browser.prf33
-rw-r--r--private/net/svcdlls/browser/server/daytona/makefile6
-rw-r--r--private/net/svcdlls/browser/server/daytona/makefile.inc10
-rw-r--r--private/net/svcdlls/browser/server/daytona/sources83
-rw-r--r--private/net/svcdlls/browser/server/dirs25
-rw-r--r--private/net/svcdlls/browser/server/precomp.h38
-rw-r--r--private/net/svcdlls/browser/server/srvenum.c2263
-rw-r--r--private/net/svcdlls/browser/server/srvenum.h117
-rw-r--r--private/net/svcdlls/browser2/bchk/bchk.c1902
-rw-r--r--private/net/svcdlls/browser2/bchk/bchk.h305
-rw-r--r--private/net/svcdlls/browser2/bchk/makefile6
-rw-r--r--private/net/svcdlls/browser2/bchk/sources53
-rw-r--r--private/net/svcdlls/browser2/bchk/support.c447
-rw-r--r--private/net/svcdlls/browser2/bowser.acf11
-rw-r--r--private/net/svcdlls/browser2/bowser.idl222
-rw-r--r--private/net/svcdlls/browser2/bperf/bperf.c357
-rw-r--r--private/net/svcdlls/browser2/bperf/makefile6
-rw-r--r--private/net/svcdlls/browser2/bperf/sources49
-rw-r--r--private/net/svcdlls/browser2/brcommon.h312
-rw-r--r--private/net/svcdlls/browser2/brnames.h21
-rw-r--r--private/net/svcdlls/browser2/browtest/browfunc.c569
-rw-r--r--private/net/svcdlls/browser2/browtest/browfunc.h212
-rw-r--r--private/net/svcdlls/browser2/browtest/browglob.h62
-rw-r--r--private/net/svcdlls/browser2/browtest/browmdom.c1165
-rw-r--r--private/net/svcdlls/browser2/browtest/browmdom.h24
-rw-r--r--private/net/svcdlls/browser2/browtest/browstrs.c534
-rw-r--r--private/net/svcdlls/browser2/browtest/browstrs.h24
-rw-r--r--private/net/svcdlls/browser2/browtest/browtest.c1947
-rw-r--r--private/net/svcdlls/browser2/browtest/browtest.cht120
-rw-r--r--private/net/svcdlls/browser2/browtest/browtest.h148
-rw-r--r--private/net/svcdlls/browser2/browtest/browtest.inp56
-rw-r--r--private/net/svcdlls/browser2/browtest/browutil.c780
-rw-r--r--private/net/svcdlls/browser2/browtest/browutil.h16
-rw-r--r--private/net/svcdlls/browser2/browtest/makefile6
-rw-r--r--private/net/svcdlls/browser2/browtest/makefile.inc0
-rw-r--r--private/net/svcdlls/browser2/browtest/sources67
-rw-r--r--private/net/svcdlls/browser2/bwatch/bwatch.c704
-rw-r--r--private/net/svcdlls/browser2/bwatch/bwatch.h120
-rw-r--r--private/net/svcdlls/browser2/bwatch/makefile6
-rw-r--r--private/net/svcdlls/browser2/bwatch/sources52
-rw-r--r--private/net/svcdlls/browser2/client/brclient.h71
-rw-r--r--private/net/svcdlls/browser2/client/browbind.c186
-rw-r--r--private/net/svcdlls/browser2/client/browdeb.c4222
-rw-r--r--private/net/svcdlls/browser2/client/browdeb.rc12
-rw-r--r--private/net/svcdlls/browser2/client/browstat.c3
-rw-r--r--private/net/svcdlls/browser2/client/browstat.rc12
-rw-r--r--private/net/svcdlls/browser2/client/browstub.c1483
-rw-r--r--private/net/svcdlls/browser2/client/brtsenum.c590
-rw-r--r--private/net/svcdlls/browser2/client/makefile6
-rw-r--r--private/net/svcdlls/browser2/client/makefile.inc5
-rw-r--r--private/net/svcdlls/browser2/client/sources78
-rw-r--r--private/net/svcdlls/browser2/common/interim.c976
-rw-r--r--private/net/svcdlls/browser2/common/makefile6
-rw-r--r--private/net/svcdlls/browser2/common/sources53
-rw-r--r--private/net/svcdlls/browser2/common/utils.c957
-rw-r--r--private/net/svcdlls/browser2/dirs30
-rw-r--r--private/net/svcdlls/browser2/imports.h46
-rw-r--r--private/net/svcdlls/browser2/imports.idl67
-rw-r--r--private/net/svcdlls/browser2/makefil061
-rw-r--r--private/net/svcdlls/browser2/server/bowqueue.c672
-rw-r--r--private/net/svcdlls/browser2/server/bowqueue.h101
-rw-r--r--private/net/svcdlls/browser2/server/bowsvc.rc12
-rw-r--r--private/net/svcdlls/browser2/server/br.h119
-rw-r--r--private/net/svcdlls/browser2/server/brbackup.h73
-rw-r--r--private/net/svcdlls/browser2/server/brconfig.c656
-rw-r--r--private/net/svcdlls/browser2/server/brconfig.h128
-rw-r--r--private/net/svcdlls/browser2/server/brconst.h98
-rw-r--r--private/net/svcdlls/browser2/server/brdevice.c896
-rw-r--r--private/net/svcdlls/browser2/server/brdevice.h148
-rw-r--r--private/net/svcdlls/browser2/server/brdmmstr.c456
-rw-r--r--private/net/svcdlls/browser2/server/brdomain.c790
-rw-r--r--private/net/svcdlls/browser2/server/brdomain.h112
-rw-r--r--private/net/svcdlls/browser2/server/brmain.c1243
-rw-r--r--private/net/svcdlls/browser2/server/brmain.h175
-rw-r--r--private/net/svcdlls/browser2/server/brmaster.c1864
-rw-r--r--private/net/svcdlls/browser2/server/brmaster.h121
-rw-r--r--private/net/svcdlls/browser2/server/browsdom.c32
-rw-r--r--private/net/svcdlls/browser2/server/browsdom.h46
-rw-r--r--private/net/svcdlls/browser2/server/browser.c1633
-rw-r--r--private/net/svcdlls/browser2/server/browser.def7
-rw-r--r--private/net/svcdlls/browser2/server/browser.prf33
-rw-r--r--private/net/svcdlls/browser2/server/browslst.c70
-rw-r--r--private/net/svcdlls/browser2/server/browslst.h95
-rw-r--r--private/net/svcdlls/browser2/server/browsnet.c1349
-rw-r--r--private/net/svcdlls/browser2/server/browsnet.h330
-rw-r--r--private/net/svcdlls/browser2/server/brsec.h71
-rw-r--r--private/net/svcdlls/browser2/server/brutil.c546
-rw-r--r--private/net/svcdlls/browser2/server/brutil.h106
-rw-r--r--private/net/svcdlls/browser2/server/brwan.c303
-rw-r--r--private/net/svcdlls/browser2/server/brwan.h58
-rw-r--r--private/net/svcdlls/browser2/server/brwins.c484
-rw-r--r--private/net/svcdlls/browser2/server/brwins.h46
-rw-r--r--private/net/svcdlls/browser2/server/makefile6
-rw-r--r--private/net/svcdlls/browser2/server/makefile.inc10
-rw-r--r--private/net/svcdlls/browser2/server/precomp.h38
-rw-r--r--private/net/svcdlls/browser2/server/sources85
-rw-r--r--private/net/svcdlls/browser2/server/srvenum.c2422
-rw-r--r--private/net/svcdlls/browser2/server/srvenum.h117
-rw-r--r--private/net/svcdlls/dfs/client/dfsstub.c1194
-rw-r--r--private/net/svcdlls/dfs/client/domain.c551
-rw-r--r--private/net/svcdlls/dfs/client/domain.h24
-rw-r--r--private/net/svcdlls/dfs/client/makefile15
-rw-r--r--private/net/svcdlls/dfs/client/sources42
-rw-r--r--private/net/svcdlls/dfs/dfscli.acf10
-rw-r--r--private/net/svcdlls/dfs/dfssrv.acf7
-rw-r--r--private/net/svcdlls/dfs/dirs29
-rw-r--r--private/net/svcdlls/dfs/import.h38
-rw-r--r--private/net/svcdlls/dfs/import.idl58
-rw-r--r--private/net/svcdlls/dfs/makefil058
-rw-r--r--private/net/svcdlls/dfs/netdfs.idl173
-rw-r--r--private/net/svcdlls/dfs/server/makefile15
-rw-r--r--private/net/svcdlls/dfs/server/sources40
-rw-r--r--private/net/svcdlls/dfs/utest/makefile28
-rw-r--r--private/net/svcdlls/dfs/utest/netdfs.c904
-rw-r--r--private/net/svcdlls/dfs/utest/netdfs.rc10
-rw-r--r--private/net/svcdlls/dfs/utest/sources47
-rw-r--r--private/net/svcdlls/dirs58
-rw-r--r--private/net/svcdlls/fpnw/client/encrypt.c314
-rw-r--r--private/net/svcdlls/fpnw/client/fpnwclnt.def56
-rw-r--r--private/net/svcdlls/fpnw/client/fpnwclnt.rc10
-rw-r--r--private/net/svcdlls/fpnw/client/logon.c1131
-rw-r--r--private/net/svcdlls/fpnw/client/makefile6
-rw-r--r--private/net/svcdlls/fpnw/client/makefile.inc1
-rw-r--r--private/net/svcdlls/fpnw/client/ncpbind.c117
-rw-r--r--private/net/svcdlls/fpnw/client/ncpstub.c1435
-rw-r--r--private/net/svcdlls/fpnw/client/notify.c543
-rw-r--r--private/net/svcdlls/fpnw/client/nwsutil.c213
-rw-r--r--private/net/svcdlls/fpnw/client/sources69
-rw-r--r--private/net/svcdlls/fpnw/client/usrprop.c831
-rw-r--r--private/net/svcdlls/fpnw/dirs24
-rw-r--r--private/net/svcdlls/fpnw/imports.idl57
-rw-r--r--private/net/svcdlls/fpnw/inc/fpnwapi.h347
-rw-r--r--private/net/svcdlls/fpnw/inc/imports.h9
-rw-r--r--private/net/svcdlls/fpnw/inc/nwprint.h157
-rw-r--r--private/net/svcdlls/fpnw/inc/nwstruct.h183
-rw-r--r--private/net/svcdlls/fpnw/inc/nwsutil.h64
-rw-r--r--private/net/svcdlls/fpnw/inc/srvnames.h21
-rw-r--r--private/net/svcdlls/fpnw/inc/usrprop.h70
-rw-r--r--private/net/svcdlls/fpnw/makefil072
-rw-r--r--private/net/svcdlls/fpnw/ncpsvc.acf20
-rw-r--r--private/net/svcdlls/fpnw/ncpsvc.idl242
-rw-r--r--private/net/svcdlls/fpnw/nwsplace.txt1
-rw-r--r--private/net/svcdlls/lls/ccfapi32/ccfapi.cpp354
-rw-r--r--private/net/svcdlls/lls/ccfapi32/ccfapi.h110
-rw-r--r--private/net/svcdlls/lls/ccfapi32/ccfapi32.def13
-rw-r--r--private/net/svcdlls/lls/ccfapi32/ccfapi32.rc346
-rw-r--r--private/net/svcdlls/lls/ccfapi32/exports.cpp382
-rw-r--r--private/net/svcdlls/lls/ccfapi32/imagelst.h38
-rw-r--r--private/net/svcdlls/lls/ccfapi32/licobj.cpp308
-rw-r--r--private/net/svcdlls/lls/ccfapi32/licobj.h76
-rw-r--r--private/net/svcdlls/lls/ccfapi32/makefile6
-rw-r--r--private/net/svcdlls/lls/ccfapi32/md4.h120
-rw-r--r--private/net/svcdlls/lls/ccfapi32/md4c.cpp291
-rw-r--r--private/net/svcdlls/lls/ccfapi32/nlicdlg.cpp1386
-rw-r--r--private/net/svcdlls/lls/ccfapi32/nlicdlg.h102
-rw-r--r--private/net/svcdlls/lls/ccfapi32/paper.cpp1389
-rw-r--r--private/net/svcdlls/lls/ccfapi32/paper.h145
-rw-r--r--private/net/svcdlls/lls/ccfapi32/pseatdlg.cpp154
-rw-r--r--private/net/svcdlls/lls/ccfapi32/pseatdlg.h62
-rw-r--r--private/net/svcdlls/lls/ccfapi32/psrvdlg.cpp152
-rw-r--r--private/net/svcdlls/lls/ccfapi32/psrvdlg.h63
-rw-r--r--private/net/svcdlls/lls/ccfapi32/remdlg.cpp1364
-rw-r--r--private/net/svcdlls/lls/ccfapi32/remdlg.h105
-rw-r--r--private/net/svcdlls/lls/ccfapi32/res/ccfapi32.rc224
-rw-r--r--private/net/svcdlls/lls/ccfapi32/res/license.icobin0 -> 1086 bytes
-rw-r--r--private/net/svcdlls/lls/ccfapi32/res/smicons.bmpbin0 -> 334 bytes
-rw-r--r--private/net/svcdlls/lls/ccfapi32/res/warning.icobin0 -> 766 bytes
-rw-r--r--private/net/svcdlls/lls/ccfapi32/resource.h105
-rw-r--r--private/net/svcdlls/lls/ccfapi32/source.cpp706
-rw-r--r--private/net/svcdlls/lls/ccfapi32/source.h83
-rw-r--r--private/net/svcdlls/lls/ccfapi32/sources51
-rw-r--r--private/net/svcdlls/lls/ccfapi32/srclist.cpp393
-rw-r--r--private/net/svcdlls/lls/ccfapi32/srclist.h47
-rw-r--r--private/net/svcdlls/lls/ccfapi32/stdafx.h46
-rw-r--r--private/net/svcdlls/lls/ccfapi32/utils.cpp713
-rw-r--r--private/net/svcdlls/lls/ccfapi32/utils.h93
-rw-r--r--private/net/svcdlls/lls/client/llsevent.mc176
-rw-r--r--private/net/svcdlls/lls/client/llsrpc.c6600
-rw-r--r--private/net/svcdlls/lls/client/llsrpc.def106
-rw-r--r--private/net/svcdlls/lls/client/llsrpc.rc30
-rw-r--r--private/net/svcdlls/lls/client/makefile6
-rw-r--r--private/net/svcdlls/lls/client/makefile.inc8
-rw-r--r--private/net/svcdlls/lls/client/ntver.rc53
-rw-r--r--private/net/svcdlls/lls/client/sources68
-rw-r--r--private/net/svcdlls/lls/common/debug.c19
-rw-r--r--private/net/svcdlls/lls/common/makefile6
-rw-r--r--private/net/svcdlls/lls/common/sources44
-rw-r--r--private/net/svcdlls/lls/dirs1
-rw-r--r--private/net/svcdlls/lls/inc/debug.h40
-rw-r--r--private/net/svcdlls/lls/inc/llsapi.h1441
-rw-r--r--private/net/svcdlls/lls/inc/llsconst.h14
-rw-r--r--private/net/svcdlls/lls/inc/llsimp.h24
-rw-r--r--private/net/svcdlls/lls/inc/lpcstub.h38
-rw-r--r--private/net/svcdlls/lls/inc/rpcutil.h76
-rw-r--r--private/net/svcdlls/lls/llscli.acf46
-rw-r--r--private/net/svcdlls/lls/llsdbg.idl113
-rw-r--r--private/net/svcdlls/lls/llsimp.idl47
-rw-r--r--private/net/svcdlls/lls/llsrpc.idl2041
-rw-r--r--private/net/svcdlls/lls/llssrv.acf57
-rw-r--r--private/net/svcdlls/lls/lsapi.idl88
-rw-r--r--private/net/svcdlls/lls/lsapicli.acf46
-rw-r--r--private/net/svcdlls/lls/lsapisrv.acf49
-rw-r--r--private/net/svcdlls/lls/lsdbgcli.acf46
-rw-r--r--private/net/svcdlls/lls/lsdbgsrv.acf49
-rw-r--r--private/net/svcdlls/lls/makefil0151
-rw-r--r--private/net/svcdlls/lls/ntlsapi/main.c57
-rw-r--r--private/net/svcdlls/lls/ntlsapi/makefile6
-rw-r--r--private/net/svcdlls/lls/ntlsapi/ntlsapi.c1150
-rw-r--r--private/net/svcdlls/lls/ntlsapi/ntlsapi.def19
-rw-r--r--private/net/svcdlls/lls/ntlsapi/ntlsapi.rc12
-rw-r--r--private/net/svcdlls/lls/ntlsapi/rpcstub.c399
-rw-r--r--private/net/svcdlls/lls/ntlsapi/sources66
-rw-r--r--private/net/svcdlls/lls/server/certdb.c1511
-rw-r--r--private/net/svcdlls/lls/server/certdb.h129
-rw-r--r--private/net/svcdlls/lls/server/llssrv.c1087
-rw-r--r--private/net/svcdlls/lls/server/llssrv.h109
-rw-r--r--private/net/svcdlls/lls/server/llssrv.rc12
-rw-r--r--private/net/svcdlls/lls/server/llsutil.c982
-rw-r--r--private/net/svcdlls/lls/server/llsutil.h63
-rw-r--r--private/net/svcdlls/lls/server/makefile6
-rw-r--r--private/net/svcdlls/lls/server/makefile.inc1
-rw-r--r--private/net/svcdlls/lls/server/mapping.c756
-rw-r--r--private/net/svcdlls/lls/server/mapping.h70
-rw-r--r--private/net/svcdlls/lls/server/msvctbl.c712
-rw-r--r--private/net/svcdlls/lls/server/msvctbl.h151
-rw-r--r--private/net/svcdlls/lls/server/pack.c4306
-rw-r--r--private/net/svcdlls/lls/server/pack.h195
-rw-r--r--private/net/svcdlls/lls/server/perseat.c3351
-rw-r--r--private/net/svcdlls/lls/server/perseat.h235
-rw-r--r--private/net/svcdlls/lls/server/purchase.c865
-rw-r--r--private/net/svcdlls/lls/server/purchase.h114
-rw-r--r--private/net/svcdlls/lls/server/registry.c1707
-rw-r--r--private/net/svcdlls/lls/server/registry.h83
-rw-r--r--private/net/svcdlls/lls/server/repl.c641
-rw-r--r--private/net/svcdlls/lls/server/repl.h47
-rw-r--r--private/net/svcdlls/lls/server/rpc.c6121
-rw-r--r--private/net/svcdlls/lls/server/scaven.c198
-rw-r--r--private/net/svcdlls/lls/server/scaven.h38
-rw-r--r--private/net/svcdlls/lls/server/server.c754
-rw-r--r--private/net/svcdlls/lls/server/server.h84
-rw-r--r--private/net/svcdlls/lls/server/service.c625
-rw-r--r--private/net/svcdlls/lls/server/service.h59
-rw-r--r--private/net/svcdlls/lls/server/sources84
-rw-r--r--private/net/svcdlls/lls/server/svctbl.c902
-rw-r--r--private/net/svcdlls/lls/server/svctbl.h74
-rw-r--r--private/net/svcdlls/lls/test/common/llsdbg.c441
-rw-r--r--private/net/svcdlls/lls/test/common/llsdbg.h47
-rw-r--r--private/net/svcdlls/lls/test/common/makefile6
-rw-r--r--private/net/svcdlls/lls/test/common/sources45
-rw-r--r--private/net/svcdlls/lls/test/ct/initdata.bat70
-rw-r--r--private/net/svcdlls/lls/test/ct/lic.bat186
-rw-r--r--private/net/svcdlls/lls/test/ct/lic1.bat186
-rw-r--r--private/net/svcdlls/lls/test/ct/lic2.bat186
-rw-r--r--private/net/svcdlls/lls/test/ct/license.txt248
-rw-r--r--private/net/svcdlls/lls/test/ct/lls.docbin0 -> 195072 bytes
-rw-r--r--private/net/svcdlls/lls/test/ct/manyp.bat76
-rw-r--r--private/net/svcdlls/lls/test/ct/map1.bat95
-rw-r--r--private/net/svcdlls/lls/test/ct/mp.bat14
-rw-r--r--private/net/svcdlls/lls/test/ct/n.dat1689
-rw-r--r--private/net/svcdlls/lls/test/ct/n1.dat1000
-rw-r--r--private/net/svcdlls/lls/test/ct/n10.dat1000
-rw-r--r--private/net/svcdlls/lls/test/ct/n2.dat1000
-rw-r--r--private/net/svcdlls/lls/test/ct/n3.dat1000
-rw-r--r--private/net/svcdlls/lls/test/ct/n4.dat1000
-rw-r--r--private/net/svcdlls/lls/test/ct/n5.dat1000
-rw-r--r--private/net/svcdlls/lls/test/ct/n6.dat1000
-rw-r--r--private/net/svcdlls/lls/test/ct/n7.dat1000
-rw-r--r--private/net/svcdlls/lls/test/ct/n8.dat1000
-rw-r--r--private/net/svcdlls/lls/test/ct/n9.dat1000
-rw-r--r--private/net/svcdlls/lls/test/ct/p1.bat198
-rw-r--r--private/net/svcdlls/lls/test/ct/p2.bat186
-rw-r--r--private/net/svcdlls/lls/test/ct/p3.bat186
-rw-r--r--private/net/svcdlls/lls/test/ct/p4.bat186
-rw-r--r--private/net/svcdlls/lls/test/ct/p5.bat186
-rw-r--r--private/net/svcdlls/lls/test/ct/prod.bat186
-rw-r--r--private/net/svcdlls/lls/test/ct/prod.dat186
-rw-r--r--private/net/svcdlls/lls/test/ct/rpc.bat139
-rw-r--r--private/net/svcdlls/lls/test/dirs1
-rw-r--r--private/net/svcdlls/lls/test/llscmd/llscmd.c1616
-rw-r--r--private/net/svcdlls/lls/test/llscmd/llscmd.rc12
-rw-r--r--private/net/svcdlls/lls/test/llscmd/makefile6
-rw-r--r--private/net/svcdlls/lls/test/llscmd/sources72
-rw-r--r--private/net/svcdlls/lls/test/llsdbg/llsdbg.c448
-rw-r--r--private/net/svcdlls/lls/test/llsdbg/llsdbg.icobin0 -> 1078 bytes
-rw-r--r--private/net/svcdlls/lls/test/llsdbg/llsdbg.rc89
-rw-r--r--private/net/svcdlls/lls/test/llsdbg/makefile6
-rw-r--r--private/net/svcdlls/lls/test/llsdbg/resource.h55
-rw-r--r--private/net/svcdlls/lls/test/llsdbg/sources23
-rw-r--r--private/net/svcdlls/logonsrv/client/daytona/makefile6
-rw-r--r--private/net/svcdlls/logonsrv/client/daytona/sources93
-rw-r--r--private/net/svcdlls/logonsrv/client/dirs32
-rw-r--r--private/net/svcdlls/logonsrv/client/getdclst.c437
-rw-r--r--private/net/svcdlls/logonsrv/client/getdcnam.c849
-rw-r--r--private/net/svcdlls/logonsrv/client/logonapi.c502
-rw-r--r--private/net/svcdlls/logonsrv/client/rpcbind.c625
-rw-r--r--private/net/svcdlls/logonsrv/client/ssiapi.c1446
-rw-r--r--private/net/svcdlls/logonsrv/dirs47
-rw-r--r--private/net/svcdlls/logonsrv/imports.h40
-rw-r--r--private/net/svcdlls/logonsrv/imports.idl58
-rw-r--r--private/net/svcdlls/logonsrv/logon.idl857
-rw-r--r--private/net/svcdlls/logonsrv/logoncli.acf12
-rw-r--r--private/net/svcdlls/logonsrv/logonsrv.acf10
-rw-r--r--private/net/svcdlls/logonsrv/makefil080
-rw-r--r--private/net/svcdlls/logonsrv/monitor/makefile6
-rw-r--r--private/net/svcdlls/logonsrv/monitor/makefile.inc2
-rw-r--r--private/net/svcdlls/logonsrv/monitor/monutil.c3232
-rw-r--r--private/net/svcdlls/logonsrv/monitor/nlmon.c922
-rw-r--r--private/net/svcdlls/logonsrv/monitor/nlmon.rc12
-rw-r--r--private/net/svcdlls/logonsrv/monitor/sources106
-rw-r--r--private/net/svcdlls/logonsrv/monitor/winutil.c922
-rw-r--r--private/net/svcdlls/logonsrv/nlbind.h45
-rw-r--r--private/net/svcdlls/logonsrv/server/announce.c1243
-rw-r--r--private/net/svcdlls/logonsrv/server/changelg.c1818
-rw-r--r--private/net/svcdlls/logonsrv/server/changelg.h224
-rw-r--r--private/net/svcdlls/logonsrv/server/chutil.c4337
-rw-r--r--private/net/svcdlls/logonsrv/server/chutil.h495
-rw-r--r--private/net/svcdlls/logonsrv/server/chworker.c1714
-rw-r--r--private/net/svcdlls/logonsrv/server/chworker.h215
-rw-r--r--private/net/svcdlls/logonsrv/server/error.c820
-rw-r--r--private/net/svcdlls/logonsrv/server/iniparm.h325
-rw-r--r--private/net/svcdlls/logonsrv/server/logonapi.c3426
-rw-r--r--private/net/svcdlls/logonsrv/server/logonsrv.h392
-rw-r--r--private/net/svcdlls/logonsrv/server/lsarepl.c2184
-rw-r--r--private/net/svcdlls/logonsrv/server/lsarepl.h100
-rw-r--r--private/net/svcdlls/logonsrv/server/lsrvdata.h228
-rw-r--r--private/net/svcdlls/logonsrv/server/lsrvrepl.c4700
-rw-r--r--private/net/svcdlls/logonsrv/server/lsrvutil.c3055
-rw-r--r--private/net/svcdlls/logonsrv/server/mailslot.c1238
-rw-r--r--private/net/svcdlls/logonsrv/server/makefile6
-rw-r--r--private/net/svcdlls/logonsrv/server/makefile.inc1
-rw-r--r--private/net/svcdlls/logonsrv/server/netlogon.c4427
-rw-r--r--private/net/svcdlls/logonsrv/server/netlogon.def15
-rw-r--r--private/net/svcdlls/logonsrv/server/netlogon.prf507
-rw-r--r--private/net/svcdlls/logonsrv/server/netlogon.rc12
-rw-r--r--private/net/svcdlls/logonsrv/server/nldebug.h180
-rw-r--r--private/net/svcdlls/logonsrv/server/nlp.c1246
-rw-r--r--private/net/svcdlls/logonsrv/server/nlp.h95
-rw-r--r--private/net/svcdlls/logonsrv/server/nlsecure.c104
-rw-r--r--private/net/svcdlls/logonsrv/server/nlsecure.h88
-rw-r--r--private/net/svcdlls/logonsrv/server/nltest.c3664
-rw-r--r--private/net/svcdlls/logonsrv/server/nltest.prf1
-rw-r--r--private/net/svcdlls/logonsrv/server/nltest.rc12
-rw-r--r--private/net/svcdlls/logonsrv/server/nltest1.c522
-rw-r--r--private/net/svcdlls/logonsrv/server/oldstub.c457
-rw-r--r--private/net/svcdlls/logonsrv/server/parse.c464
-rw-r--r--private/net/svcdlls/logonsrv/server/repluas.c2452
-rw-r--r--private/net/svcdlls/logonsrv/server/replutil.c3608
-rw-r--r--private/net/svcdlls/logonsrv/server/replutil.h235
-rw-r--r--private/net/svcdlls/logonsrv/server/sources115
-rw-r--r--private/net/svcdlls/logonsrv/server/srvsess.c1728
-rw-r--r--private/net/svcdlls/logonsrv/server/ssiapi.c6401
-rw-r--r--private/net/svcdlls/logonsrv/server/ssiapi.h81
-rw-r--r--private/net/svcdlls/logonsrv/server/ssiauth.c788
-rw-r--r--private/net/svcdlls/logonsrv/server/ssidelta.h164
-rw-r--r--private/net/svcdlls/logonsrv/server/ssiinit.h1375
-rw-r--r--private/net/svcdlls/logonsrv/server/trustutl.c4976
-rw-r--r--private/net/svcdlls/msgsvc/client/makefile6
-rw-r--r--private/net/svcdlls/msgsvc/client/msg.c475
-rw-r--r--private/net/svcdlls/msgsvc/client/msgbind.c117
-rw-r--r--private/net/svcdlls/msgsvc/client/msgstub.c333
-rw-r--r--private/net/svcdlls/msgsvc/client/sources60
-rw-r--r--private/net/svcdlls/msgsvc/dirs28
-rw-r--r--private/net/svcdlls/msgsvc/docs/msgdata.pptbin0 -> 19491 bytes
-rw-r--r--private/net/svcdlls/msgsvc/imports.h10
-rw-r--r--private/net/svcdlls/msgsvc/imports.idl67
-rw-r--r--private/net/svcdlls/msgsvc/makefil058
-rw-r--r--private/net/svcdlls/msgsvc/msgsvc.acf10
-rw-r--r--private/net/svcdlls/msgsvc/msgsvc.idl134
-rw-r--r--private/net/svcdlls/msgsvc/server/apidata.c98
-rw-r--r--private/net/svcdlls/msgsvc/server/apiutil.c496
-rw-r--r--private/net/svcdlls/msgsvc/server/apiutil.h75
-rw-r--r--private/net/svcdlls/msgsvc/server/data.c642
-rw-r--r--private/net/svcdlls/msgsvc/server/display.c830
-rw-r--r--private/net/svcdlls/msgsvc/server/fmtncbna.c190
-rw-r--r--private/net/svcdlls/msgsvc/server/grpmsngr.c352
-rw-r--r--private/net/svcdlls/msgsvc/server/heap.c187
-rw-r--r--private/net/svcdlls/msgsvc/server/heap.h54
-rw-r--r--private/net/svcdlls/msgsvc/server/makefile6
-rw-r--r--private/net/svcdlls/msgsvc/server/meslog.c869
-rw-r--r--private/net/svcdlls/msgsvc/server/mesprint.c742
-rw-r--r--private/net/svcdlls/msgsvc/server/msgapi.c1103
-rw-r--r--private/net/svcdlls/msgsvc/server/msgdata.h66
-rw-r--r--private/net/svcdlls/msgsvc/server/msgdbg.h104
-rw-r--r--private/net/svcdlls/msgsvc/server/msginit.c1251
-rw-r--r--private/net/svcdlls/msgsvc/server/msgmain.c865
-rw-r--r--private/net/svcdlls/msgsvc/server/msgnames.h29
-rw-r--r--private/net/svcdlls/msgsvc/server/msgnbios.c299
-rw-r--r--private/net/svcdlls/msgsvc/server/msgsec.c122
-rw-r--r--private/net/svcdlls/msgsvc/server/msgsec.h71
-rw-r--r--private/net/svcdlls/msgsvc/server/msgsvc.def7
-rw-r--r--private/net/svcdlls/msgsvc/server/msgsvc.rc13
-rw-r--r--private/net/svcdlls/msgsvc/server/msrv.h716
-rw-r--r--private/net/svcdlls/msgsvc/server/servenam.c223
-rw-r--r--private/net/svcdlls/msgsvc/server/servencb.c2023
-rw-r--r--private/net/svcdlls/msgsvc/server/sighandl.c98
-rw-r--r--private/net/svcdlls/msgsvc/server/smbcheck.c220
-rw-r--r--private/net/svcdlls/msgsvc/server/sources84
-rw-r--r--private/net/svcdlls/msgsvc/server/threads.c544
-rw-r--r--private/net/svcdlls/msgsvc/server/wakupsem.c162
-rw-r--r--private/net/svcdlls/ntlmssp/client/crc32.c91
-rw-r--r--private/net/svcdlls/ntlmssp/client/crc32.h37
-rw-r--r--private/net/svcdlls/ntlmssp/client/dirs27
-rw-r--r--private/net/svcdlls/ntlmssp/client/export/makefile6
-rw-r--r--private/net/svcdlls/ntlmssp/client/export/security.def41
-rw-r--r--private/net/svcdlls/ntlmssp/client/export/sources9
-rw-r--r--private/net/svcdlls/ntlmssp/client/init.c556
-rw-r--r--private/net/svcdlls/ntlmssp/client/ntlmsspc.h89
-rw-r--r--private/net/svcdlls/ntlmssp/client/security.rc14
-rw-r--r--private/net/svcdlls/ntlmssp/client/sign.c1811
-rw-r--r--private/net/svcdlls/ntlmssp/client/sources.inc56
-rw-r--r--private/net/svcdlls/ntlmssp/client/stub.c2708
-rw-r--r--private/net/svcdlls/ntlmssp/client/support.c311
-rw-r--r--private/net/svcdlls/ntlmssp/common/context.c4646
-rw-r--r--private/net/svcdlls/ntlmssp/common/credhand.c1119
-rw-r--r--private/net/svcdlls/ntlmssp/common/dirs27
-rw-r--r--private/net/svcdlls/ntlmssp/common/dom/makefile6
-rw-r--r--private/net/svcdlls/ntlmssp/common/dom/sources2
-rw-r--r--private/net/svcdlls/ntlmssp/common/encrypt.c142
-rw-r--r--private/net/svcdlls/ntlmssp/common/export/makefile6
-rw-r--r--private/net/svcdlls/ntlmssp/common/export/sources3
-rw-r--r--private/net/svcdlls/ntlmssp/common/initcomn.c310
-rw-r--r--private/net/svcdlls/ntlmssp/common/ntlmsspi.h526
-rw-r--r--private/net/svcdlls/ntlmssp/common/rng.c181
-rw-r--r--private/net/svcdlls/ntlmssp/common/sources.inc122
-rw-r--r--private/net/svcdlls/ntlmssp/common/utility.c633
-rw-r--r--private/net/svcdlls/ntlmssp/debug.h142
-rw-r--r--private/net/svcdlls/ntlmssp/dirs49
-rw-r--r--private/net/svcdlls/ntlmssp/hello/client/helloc.c190
-rw-r--r--private/net/svcdlls/ntlmssp/hello/client/makefile6
-rw-r--r--private/net/svcdlls/ntlmssp/hello/client/sources48
-rw-r--r--private/net/svcdlls/ntlmssp/hello/dirs47
-rw-r--r--private/net/svcdlls/ntlmssp/hello/hello.acf5
-rw-r--r--private/net/svcdlls/ntlmssp/hello/hello.idl8
-rw-r--r--private/net/svcdlls/ntlmssp/hello/makefile27
-rw-r--r--private/net/svcdlls/ntlmssp/hello/ntwin32.mak64
-rw-r--r--private/net/svcdlls/ntlmssp/hello/server/hellos.c208
-rw-r--r--private/net/svcdlls/ntlmssp/hello/server/makefile6
-rw-r--r--private/net/svcdlls/ntlmssp/hello/server/sources46
-rw-r--r--private/net/svcdlls/ntlmssp/ntlmcomn.h498
-rw-r--r--private/net/svcdlls/ntlmssp/ntlmitf.h253
-rw-r--r--private/net/svcdlls/ntlmssp/ntlmssp.h106
-rw-r--r--private/net/svcdlls/ntlmssp/ntlmsspd.h197
-rw-r--r--private/net/svcdlls/ntlmssp/package/makefile6
-rw-r--r--private/net/svcdlls/ntlmssp/package/package.c1538
-rw-r--r--private/net/svcdlls/ntlmssp/package/secdll.h95
-rw-r--r--private/net/svcdlls/ntlmssp/package/sources42
-rw-r--r--private/net/svcdlls/ntlmssp/package/stubs.c812
-rw-r--r--private/net/svcdlls/ntlmssp/package/stubsa.c516
-rw-r--r--private/net/svcdlls/ntlmssp/package/userstba.c104
-rw-r--r--private/net/svcdlls/ntlmssp/package/userstub.c464
-rw-r--r--private/net/svcdlls/ntlmssp/server/api.c859
-rw-r--r--private/net/svcdlls/ntlmssp/server/dirs27
-rw-r--r--private/net/svcdlls/ntlmssp/server/error.c1062
-rw-r--r--private/net/svcdlls/ntlmssp/server/export/makefile6
-rw-r--r--private/net/svcdlls/ntlmssp/server/export/makefile.inc2
-rw-r--r--private/net/svcdlls/ntlmssp/server/export/ntlmssps.def9
-rw-r--r--private/net/svcdlls/ntlmssp/server/export/sources9
-rw-r--r--private/net/svcdlls/ntlmssp/server/init.c532
-rw-r--r--private/net/svcdlls/ntlmssp/server/lpc.c1591
-rw-r--r--private/net/svcdlls/ntlmssp/server/ntlmssps.def9
-rw-r--r--private/net/svcdlls/ntlmssp/server/ntlmssps.h255
-rw-r--r--private/net/svcdlls/ntlmssp/server/ntlmssps.rc17
-rw-r--r--private/net/svcdlls/ntlmssp/server/sources.inc74
-rw-r--r--private/net/svcdlls/ntlmssp/server/ssptest.c1577
-rw-r--r--private/net/svcdlls/ntlmssp/server/ssptest.rc12
-rw-r--r--private/net/svcdlls/nwsap/client/advapi.c184
-rw-r--r--private/net/svcdlls/nwsap/client/bindlib.c267
-rw-r--r--private/net/svcdlls/nwsap/client/init.c135
-rw-r--r--private/net/svcdlls/nwsap/client/makefile6
-rw-r--r--private/net/svcdlls/nwsap/client/precomp.h46
-rw-r--r--private/net/svcdlls/nwsap/client/sources32
-rw-r--r--private/net/svcdlls/nwsap/dirs26
-rw-r--r--private/net/svcdlls/nwsap/saplpc.h101
-rw-r--r--private/net/svcdlls/nwsap/server/advapi.c331
-rw-r--r--private/net/svcdlls/nwsap/server/bindlib.c402
-rw-r--r--private/net/svcdlls/nwsap/server/dump.c206
-rw-r--r--private/net/svcdlls/nwsap/server/externs.h344
-rw-r--r--private/net/svcdlls/nwsap/server/filter.c458
-rw-r--r--private/net/svcdlls/nwsap/server/globals.c126
-rw-r--r--private/net/svcdlls/nwsap/server/makefile6
-rw-r--r--private/net/svcdlls/nwsap/server/network.c1615
-rw-r--r--private/net/svcdlls/nwsap/server/nwsap.c1789
-rw-r--r--private/net/svcdlls/nwsap/server/nwsap.def6
-rw-r--r--private/net/svcdlls/nwsap/server/nwsap.rc11
-rw-r--r--private/net/svcdlls/nwsap/server/nwsapp.h487
-rw-r--r--private/net/svcdlls/nwsap/server/precomp.h6
-rw-r--r--private/net/svcdlls/nwsap/server/registry.c1110
-rw-r--r--private/net/svcdlls/nwsap/server/sapdebug.c518
-rw-r--r--private/net/svcdlls/nwsap/server/saplpc.c768
-rw-r--r--private/net/svcdlls/nwsap/server/sdmd.c2136
-rw-r--r--private/net/svcdlls/nwsap/server/sdmd.h67
-rw-r--r--private/net/svcdlls/nwsap/server/sdmdp.h288
-rw-r--r--private/net/svcdlls/nwsap/server/sdmdsupp.c704
-rw-r--r--private/net/svcdlls/nwsap/server/sources48
-rw-r--r--private/net/svcdlls/nwsap/server/ssdebug.h101
-rw-r--r--private/net/svcdlls/nwsap/server/sssubs.c203
-rw-r--r--private/net/svcdlls/nwsap/server/svcctrl.c532
-rw-r--r--private/net/svcdlls/nwsap/server/wancheck.c988
-rw-r--r--private/net/svcdlls/repl/client/expstub.c585
-rw-r--r--private/net/svcdlls/repl/client/impstub.c507
-rw-r--r--private/net/svcdlls/repl/client/makefile6
-rw-r--r--private/net/svcdlls/repl/client/replbind.c275
-rw-r--r--private/net/svcdlls/repl/client/replstub.c369
-rw-r--r--private/net/svcdlls/repl/client/report.c101
-rw-r--r--private/net/svcdlls/repl/client/sources95
-rw-r--r--private/net/svcdlls/repl/common/abspath.c86
-rw-r--r--private/net/svcdlls/repl/common/allowrol.c130
-rw-r--r--private/net/svcdlls/repl/common/chnglock.c131
-rw-r--r--private/net/svcdlls/repl/common/chngnot.c424
-rw-r--r--private/net/svcdlls/repl/common/chngnot.h85
-rw-r--r--private/net/svcdlls/repl/common/data.c45
-rw-r--r--private/net/svcdlls/repl/common/delfile.c262
-rw-r--r--private/net/svcdlls/repl/common/dirname.c112
-rw-r--r--private/net/svcdlls/repl/common/dirname.h64
-rw-r--r--private/net/svcdlls/repl/common/easize.c236
-rw-r--r--private/net/svcdlls/repl/common/expalloc.c120
-rw-r--r--private/net/svcdlls/repl/common/expbuild.c160
-rw-r--r--private/net/svcdlls/repl/common/expconf.c694
-rw-r--r--private/net/svcdlls/repl/common/expdir.h265
-rw-r--r--private/net/svcdlls/repl/common/expenum.c298
-rw-r--r--private/net/svcdlls/repl/common/expget.c110
-rw-r--r--private/net/svcdlls/repl/common/explock.c162
-rw-r--r--private/net/svcdlls/repl/common/expset.c195
-rw-r--r--private/net/svcdlls/repl/common/expvalid.c177
-rw-r--r--private/net/svcdlls/repl/common/fixlocks.c194
-rw-r--r--private/net/svcdlls/repl/common/fsresolu.c220
-rw-r--r--private/net/svcdlls/repl/common/ignorenm.c100
-rw-r--r--private/net/svcdlls/repl/common/impalloc.c118
-rw-r--r--private/net/svcdlls/repl/common/impbuild.c203
-rw-r--r--private/net/svcdlls/repl/common/impconf.c720
-rw-r--r--private/net/svcdlls/repl/common/impdir.h259
-rw-r--r--private/net/svcdlls/repl/common/impenum.c321
-rw-r--r--private/net/svcdlls/repl/common/impget.c149
-rw-r--r--private/net/svcdlls/repl/common/implock.c172
-rw-r--r--private/net/svcdlls/repl/common/imports.h58
-rw-r--r--private/net/svcdlls/repl/common/impstate.c124
-rw-r--r--private/net/svcdlls/repl/common/impvalid.c118
-rw-r--r--private/net/svcdlls/repl/common/iniparm.h87
-rw-r--r--private/net/svcdlls/repl/common/lstvalid.c162
-rw-r--r--private/net/svcdlls/repl/common/makefile6
-rw-r--r--private/net/svcdlls/repl/common/replbld.c190
-rw-r--r--private/net/svcdlls/repl/common/replconf.c666
-rw-r--r--private/net/svcdlls/repl/common/replconf.h167
-rw-r--r--private/net/svcdlls/repl/common/repldefs.h831
-rw-r--r--private/net/svcdlls/repl/common/replerr.c298
-rw-r--r--private/net/svcdlls/repl/common/replgbl.h139
-rw-r--r--private/net/svcdlls/repl/common/replname.h35
-rw-r--r--private/net/svcdlls/repl/common/replp.c271
-rw-r--r--private/net/svcdlls/repl/common/replp.h70
-rw-r--r--private/net/svcdlls/repl/common/repvalid.c198
-rw-r--r--private/net/svcdlls/repl/common/sources133
-rw-r--r--private/net/svcdlls/repl/common/userlock.c158
-rw-r--r--private/net/svcdlls/repl/dirs49
-rw-r--r--private/net/svcdlls/repl/imports.idl67
-rw-r--r--private/net/svcdlls/repl/makefil063
-rw-r--r--private/net/svcdlls/repl/repl.acf18
-rw-r--r--private/net/svcdlls/repl/repl.idl307
-rw-r--r--private/net/svcdlls/repl/repltest/cli_pars.c281
-rw-r--r--private/net/svcdlls/repl/repltest/dispmail.c159
-rw-r--r--private/net/svcdlls/repl/repltest/fakefind.c125
-rw-r--r--private/net/svcdlls/repl/repltest/makefile6
-rw-r--r--private/net/svcdlls/repl/repltest/mas_pars.c278
-rw-r--r--private/net/svcdlls/repl/repltest/mtest1.c119
-rw-r--r--private/net/svcdlls/repl/repltest/mtest2.c253
-rw-r--r--private/net/svcdlls/repl/repltest/queryea.c372
-rw-r--r--private/net/svcdlls/repl/repltest/repldel.c165
-rw-r--r--private/net/svcdlls/repl/repltest/repldir.c69
-rw-r--r--private/net/svcdlls/repl/repltest/replmain.c106
-rw-r--r--private/net/svcdlls/repl/repltest/replsum.c221
-rw-r--r--private/net/svcdlls/repl/repltest/replsvc.c84
-rw-r--r--private/net/svcdlls/repl/repltest/repltest.c234
-rw-r--r--private/net/svcdlls/repl/repltest/repltest.h107
-rw-r--r--private/net/svcdlls/repl/repltest/setea.c130
-rw-r--r--private/net/svcdlls/repl/repltest/showftim.c81
-rw-r--r--private/net/svcdlls/repl/repltest/showtime.c105
-rw-r--r--private/net/svcdlls/repl/repltest/sources121
-rw-r--r--private/net/svcdlls/repl/repltest/test.h84
-rw-r--r--private/net/svcdlls/repl/repltest/test1.c76
-rw-r--r--private/net/svcdlls/repl/repltest/test2.c184
-rw-r--r--private/net/svcdlls/repl/repltest/test3.c87
-rw-r--r--private/net/svcdlls/repl/repltest/test4.c85
-rw-r--r--private/net/svcdlls/repl/repltest/test5.c190
-rw-r--r--private/net/svcdlls/repl/repltest/test6.c140
-rw-r--r--private/net/svcdlls/repl/repltest/test7.c59
-rw-r--r--private/net/svcdlls/repl/repltest/testaft.c90
-rw-r--r--private/net/svcdlls/repl/repltest/testcopy.c268
-rw-r--r--private/net/svcdlls/repl/repltest/testexp.c631
-rw-r--r--private/net/svcdlls/repl/repltest/testexp0.c120
-rw-r--r--private/net/svcdlls/repl/repltest/testimp.c399
-rw-r--r--private/net/svcdlls/repl/repltest/testimp0.c115
-rw-r--r--private/net/svcdlls/repl/repltest/testrep0.c119
-rw-r--r--private/net/svcdlls/repl/repltest/testrepl.c379
-rw-r--r--private/net/svcdlls/repl/repltest/wait.c161
-rw-r--r--private/net/svcdlls/repl/repltest/watch.c231
-rw-r--r--private/net/svcdlls/repl/repltest/watchl.c205
-rw-r--r--private/net/svcdlls/repl/server/cachetim.c113
-rw-r--r--private/net/svcdlls/repl/server/checksum.c671
-rw-r--r--private/net/svcdlls/repl/server/checksum.h93
-rw-r--r--private/net/svcdlls/repl/server/chklocks.c249
-rw-r--r--private/net/svcdlls/repl/server/chngrole.c296
-rw-r--r--private/net/svcdlls/repl/server/cli_dupl.c497
-rw-r--r--private/net/svcdlls/repl/server/cli_list.c850
-rw-r--r--private/net/svcdlls/repl/server/client.c1200
-rw-r--r--private/net/svcdlls/repl/server/client.h604
-rw-r--r--private/net/svcdlls/repl/server/cliquery.c217
-rw-r--r--private/net/svcdlls/repl/server/copydir.c542
-rw-r--r--private/net/svcdlls/repl/server/copyfile.c553
-rw-r--r--private/net/svcdlls/repl/server/copytime.c527
-rw-r--r--private/net/svcdlls/repl/server/copytree.c409
-rw-r--r--private/net/svcdlls/repl/server/entcount.c241
-rw-r--r--private/net/svcdlls/repl/server/error.c566
-rw-r--r--private/net/svcdlls/repl/server/expadd.c254
-rw-r--r--private/net/svcdlls/repl/server/expdel.c154
-rw-r--r--private/net/svcdlls/repl/server/expenum.c311
-rw-r--r--private/net/svcdlls/repl/server/expget.c213
-rw-r--r--private/net/svcdlls/repl/server/explock.c333
-rw-r--r--private/net/svcdlls/repl/server/expread.c423
-rw-r--r--private/net/svcdlls/repl/server/expset.c323
-rw-r--r--private/net/svcdlls/repl/server/expstart.c197
-rw-r--r--private/net/svcdlls/repl/server/expstop.c126
-rw-r--r--private/net/svcdlls/repl/server/filefind.c359
-rw-r--r--private/net/svcdlls/repl/server/filefind.h97
-rw-r--r--private/net/svcdlls/repl/server/getparm.c506
-rw-r--r--private/net/svcdlls/repl/server/impadd.c245
-rw-r--r--private/net/svcdlls/repl/server/impdel.c160
-rw-r--r--private/net/svcdlls/repl/server/impenum.c305
-rw-r--r--private/net/svcdlls/repl/server/impget.c235
-rw-r--r--private/net/svcdlls/repl/server/implock.c303
-rw-r--r--private/net/svcdlls/repl/server/impread.c223
-rw-r--r--private/net/svcdlls/repl/server/impstart.c227
-rw-r--r--private/net/svcdlls/repl/server/impstop.c125
-rw-r--r--private/net/svcdlls/repl/server/initany.c235
-rw-r--r--private/net/svcdlls/repl/server/makefile6
-rw-r--r--private/net/svcdlls/repl/server/marshall.c817
-rw-r--r--private/net/svcdlls/repl/server/masproto.h234
-rw-r--r--private/net/svcdlls/repl/server/master.c1043
-rw-r--r--private/net/svcdlls/repl/server/master.h133
-rw-r--r--private/net/svcdlls/repl/server/mksecatt.c258
-rw-r--r--private/net/svcdlls/repl/server/parse.c1360
-rw-r--r--private/net/svcdlls/repl/server/permit.c218
-rw-r--r--private/net/svcdlls/repl/server/puls_msg.c1367
-rw-r--r--private/net/svcdlls/repl/server/pulser.c1556
-rw-r--r--private/net/svcdlls/repl/server/pulser.h69
-rw-r--r--private/net/svcdlls/repl/server/repl.c925
-rw-r--r--private/net/svcdlls/repl/server/replget.c111
-rw-r--r--private/net/svcdlls/repl/server/repllock.h87
-rw-r--r--private/net/svcdlls/repl/server/replpack.h99
-rw-r--r--private/net/svcdlls/repl/server/replset.c516
-rw-r--r--private/net/svcdlls/repl/server/replstal.c137
-rw-r--r--private/net/svcdlls/repl/server/replstop.c121
-rw-r--r--private/net/svcdlls/repl/server/scanqs.c252
-rw-r--r--private/net/svcdlls/repl/server/sources176
-rw-r--r--private/net/svcdlls/repl/server/syncer.c1664
-rw-r--r--private/net/svcdlls/repl/server/syncmisc.c1214
-rw-r--r--private/net/svcdlls/repl/server/synctree.c2842
-rw-r--r--private/net/svcdlls/repl/server/watchd.c461
-rw-r--r--private/net/svcdlls/repl/server/wcslocal.c152
-rw-r--r--private/net/svcdlls/repl/server/wcslocal.h67
-rw-r--r--private/net/svcdlls/repl/testdll/fakestub.c166
-rw-r--r--private/net/svcdlls/repl/testdll/makefile6
-rw-r--r--private/net/svcdlls/repl/testdll/sources110
-rw-r--r--private/net/svcdlls/repl/testdll/testdll.c75
-rw-r--r--private/net/svcdlls/rpl/client/makefile6
-rw-r--r--private/net/svcdlls/rpl/client/rplapi.c1893
-rw-r--r--private/net/svcdlls/rpl/client/rplbind.c119
-rw-r--r--private/net/svcdlls/rpl/client/rplstub.c1279
-rw-r--r--private/net/svcdlls/rpl/client/sources62
-rw-r--r--private/net/svcdlls/rpl/command/makefile6
-rw-r--r--private/net/svcdlls/rpl/command/makefile.inc4
-rw-r--r--private/net/svcdlls/rpl/command/rplcmd.c2408
-rw-r--r--private/net/svcdlls/rpl/command/rplmsg.mc610
-rw-r--r--private/net/svcdlls/rpl/command/rplver.rc12
-rw-r--r--private/net/svcdlls/rpl/command/sources65
-rw-r--r--private/net/svcdlls/rpl/convert/adapter.c71
-rw-r--r--private/net/svcdlls/rpl/convert/boot.c306
-rw-r--r--private/net/svcdlls/rpl/convert/config.c527
-rw-r--r--private/net/svcdlls/rpl/convert/convert.rc12
-rw-r--r--private/net/svcdlls/rpl/convert/debug.c76
-rw-r--r--private/net/svcdlls/rpl/convert/library.c330
-rw-r--r--private/net/svcdlls/rpl/convert/local.h227
-rw-r--r--private/net/svcdlls/rpl/convert/makefile6
-rw-r--r--private/net/svcdlls/rpl/convert/makefile.inc4
-rw-r--r--private/net/svcdlls/rpl/convert/nlstxt.mc214
-rw-r--r--private/net/svcdlls/rpl/convert/profile.c270
-rw-r--r--private/net/svcdlls/rpl/convert/rpldata.h47
-rw-r--r--private/net/svcdlls/rpl/convert/rplmain.c740
-rw-r--r--private/net/svcdlls/rpl/convert/sources51
-rw-r--r--private/net/svcdlls/rpl/convert/vendor.c139
-rw-r--r--private/net/svcdlls/rpl/convert/wksta.c565
-rw-r--r--private/net/svcdlls/rpl/dirs33
-rw-r--r--private/net/svcdlls/rpl/dll/buffer.c124
-rw-r--r--private/net/svcdlls/rpl/dll/emulator.c110
-rw-r--r--private/net/svcdlls/rpl/dll/ether.c498
-rw-r--r--private/net/svcdlls/rpl/dll/fdr.c181
-rw-r--r--private/net/svcdlls/rpl/dll/find.c312
-rw-r--r--private/net/svcdlls/rpl/dll/found.c159
-rw-r--r--private/net/svcdlls/rpl/dll/init.c545
-rw-r--r--private/net/svcdlls/rpl/dll/local.h498
-rw-r--r--private/net/svcdlls/rpl/dll/makefile6
-rw-r--r--private/net/svcdlls/rpl/dll/report.c60
-rw-r--r--private/net/svcdlls/rpl/dll/sfr.c499
-rw-r--r--private/net/svcdlls/rpl/dll/sizeof.c174
-rw-r--r--private/net/svcdlls/rpl/dll/sources37
-rw-r--r--private/net/svcdlls/rpl/dll/xns.c520
-rw-r--r--private/net/svcdlls/rpl/doc/lm21ddk.docbin0 -> 168367 bytes
-rw-r--r--private/net/svcdlls/rpl/doc/rpl.docbin0 -> 385024 bytes
-rw-r--r--private/net/svcdlls/rpl/exe/makefile6
-rw-r--r--private/net/svcdlls/rpl/exe/rplsvc.c146
-rw-r--r--private/net/svcdlls/rpl/exe/rplsvc.rc11
-rw-r--r--private/net/svcdlls/rpl/exe/sources97
-rw-r--r--private/net/svcdlls/rpl/imports.idl63
-rw-r--r--private/net/svcdlls/rpl/inc/adapter.h52
-rw-r--r--private/net/svcdlls/rpl/inc/boot.h59
-rw-r--r--private/net/svcdlls/rpl/inc/config.h95
-rw-r--r--private/net/svcdlls/rpl/inc/i_lmrpl.h26
-rw-r--r--private/net/svcdlls/rpl/inc/imports.h45
-rw-r--r--private/net/svcdlls/rpl/inc/profile.h62
-rw-r--r--private/net/svcdlls/rpl/inc/riplcons.h130
-rw-r--r--private/net/svcdlls/rpl/inc/ripltyps.h137
-rw-r--r--private/net/svcdlls/rpl/inc/rpldb.h30
-rw-r--r--private/net/svcdlls/rpl/inc/rpldebug.h88
-rw-r--r--private/net/svcdlls/rpl/inc/rpldll.h144
-rw-r--r--private/net/svcdlls/rpl/inc/rpllib.h94
-rw-r--r--private/net/svcdlls/rpl/inc/rplnames.h24
-rw-r--r--private/net/svcdlls/rpl/inc/vendor.h53
-rw-r--r--private/net/svcdlls/rpl/inc/wksta.h105
-rw-r--r--private/net/svcdlls/rpl/lib/adapter.c120
-rw-r--r--private/net/svcdlls/rpl/lib/addkey.c31
-rw-r--r--private/net/svcdlls/rpl/lib/cmdline.c249
-rw-r--r--private/net/svcdlls/rpl/lib/jeterror.c66
-rw-r--r--private/net/svcdlls/rpl/lib/local.h54
-rw-r--r--private/net/svcdlls/rpl/lib/makefile6
-rw-r--r--private/net/svcdlls/rpl/lib/reg.c271
-rw-r--r--private/net/svcdlls/rpl/lib/report.c111
-rw-r--r--private/net/svcdlls/rpl/lib/sources36
-rw-r--r--private/net/svcdlls/rpl/lib/tcpip.c68
-rw-r--r--private/net/svcdlls/rpl/makefil073
-rw-r--r--private/net/svcdlls/rpl/makefile12
-rw-r--r--private/net/svcdlls/rpl/rplcnv/local.h64
-rw-r--r--private/net/svcdlls/rpl/rplcnv/makefile6
-rw-r--r--private/net/svcdlls/rpl/rplcnv/rplcnv.c37
-rw-r--r--private/net/svcdlls/rpl/rplcnv/sources27
-rw-r--r--private/net/svcdlls/rpl/rplinst/debug.c137
-rw-r--r--private/net/svcdlls/rpl/rplinst/debug.h28
-rw-r--r--private/net/svcdlls/rpl/rplinst/local.h77
-rw-r--r--private/net/svcdlls/rpl/rplinst/makefile6
-rw-r--r--private/net/svcdlls/rpl/rplinst/makefile.inc8
-rw-r--r--private/net/svcdlls/rpl/rplinst/querydir.c136
-rw-r--r--private/net/svcdlls/rpl/rplinst/rplimsg.h55
-rw-r--r--private/net/svcdlls/rpl/rplinst/rplinst.c137
-rw-r--r--private/net/svcdlls/rpl/rplinst/rplinst.rc14
-rw-r--r--private/net/svcdlls/rpl/rplinst/security.c1567
-rw-r--r--private/net/svcdlls/rpl/rplinst/security.h17
-rw-r--r--private/net/svcdlls/rpl/rplinst/sources38
-rw-r--r--private/net/svcdlls/rpl/rplinst/tree.c2
-rw-r--r--private/net/svcdlls/rpl/rplinst/tree.h28
-rw-r--r--private/net/svcdlls/rpl/rplinst/treei.h51
-rw-r--r--private/net/svcdlls/rpl/rplstart/local.h66
-rw-r--r--private/net/svcdlls/rpl/rplstart/makefile6
-rw-r--r--private/net/svcdlls/rpl/rplstart/rplstart.c139
-rw-r--r--private/net/svcdlls/rpl/rplstart/sources32
-rw-r--r--private/net/svcdlls/rpl/rplsvc.idl543
-rw-r--r--private/net/svcdlls/rpl/rplsvc_c.acf33
-rw-r--r--private/net/svcdlls/rpl/rplsvc_s.acf7
-rw-r--r--private/net/svcdlls/rpl/server/adapter.c502
-rw-r--r--private/net/svcdlls/rpl/server/apisec.c124
-rw-r--r--private/net/svcdlls/rpl/server/apisec.h52
-rw-r--r--private/net/svcdlls/rpl/server/bbcfile.c1059
-rw-r--r--private/net/svcdlls/rpl/server/bbcfile.h28
-rw-r--r--private/net/svcdlls/rpl/server/boot.c832
-rw-r--r--private/net/svcdlls/rpl/server/config.c711
-rw-r--r--private/net/svcdlls/rpl/server/database.c998
-rw-r--r--private/net/svcdlls/rpl/server/database.h33
-rw-r--r--private/net/svcdlls/rpl/server/db.h263
-rw-r--r--private/net/svcdlls/rpl/server/dblib.c386
-rw-r--r--private/net/svcdlls/rpl/server/dblib.h66
-rw-r--r--private/net/svcdlls/rpl/server/debug.c141
-rw-r--r--private/net/svcdlls/rpl/server/debug.h28
-rw-r--r--private/net/svcdlls/rpl/server/disk.c318
-rw-r--r--private/net/svcdlls/rpl/server/fitfile.c442
-rw-r--r--private/net/svcdlls/rpl/server/fitfile.h27
-rw-r--r--private/net/svcdlls/rpl/server/library.c273
-rw-r--r--private/net/svcdlls/rpl/server/local.h556
-rw-r--r--private/net/svcdlls/rpl/server/makefile6
-rw-r--r--private/net/svcdlls/rpl/server/memory.c188
-rw-r--r--private/net/svcdlls/rpl/server/open.c600
-rw-r--r--private/net/svcdlls/rpl/server/open.h27
-rw-r--r--private/net/svcdlls/rpl/server/profile.c1139
-rw-r--r--private/net/svcdlls/rpl/server/read.c409
-rw-r--r--private/net/svcdlls/rpl/server/read.h29
-rw-r--r--private/net/svcdlls/rpl/server/report.c199
-rw-r--r--private/net/svcdlls/rpl/server/report.h34
-rw-r--r--private/net/svcdlls/rpl/server/request.c697
-rw-r--r--private/net/svcdlls/rpl/server/request.h26
-rw-r--r--private/net/svcdlls/rpl/server/resume.c308
-rw-r--r--private/net/svcdlls/rpl/server/resume.h61
-rw-r--r--private/net/svcdlls/rpl/server/rpldata.h161
-rw-r--r--private/net/svcdlls/rpl/server/rplmain.c982
-rw-r--r--private/net/svcdlls/rpl/server/rplrest.c36
-rw-r--r--private/net/svcdlls/rpl/server/rplsvc.c122
-rw-r--r--private/net/svcdlls/rpl/server/rplsvc.rc12
-rw-r--r--private/net/svcdlls/rpl/server/security.c1005
-rw-r--r--private/net/svcdlls/rpl/server/service.c207
-rw-r--r--private/net/svcdlls/rpl/server/setup.c716
-rw-r--r--private/net/svcdlls/rpl/server/setup.h34
-rw-r--r--private/net/svcdlls/rpl/server/sources83
-rw-r--r--private/net/svcdlls/rpl/server/tree.c353
-rw-r--r--private/net/svcdlls/rpl/server/tree.h28
-rw-r--r--private/net/svcdlls/rpl/server/treei.h47
-rw-r--r--private/net/svcdlls/rpl/server/vendor.c528
-rw-r--r--private/net/svcdlls/rpl/server/wcst.c54
-rw-r--r--private/net/svcdlls/rpl/server/wksta.c1471
-rw-r--r--private/net/svcdlls/rpl/server/worker.c572
-rw-r--r--private/net/svcdlls/rpl/server/worker.h35
-rw-r--r--private/net/svcdlls/srvsvc/adtcomn.h92
-rw-r--r--private/net/svcdlls/srvsvc/client/adtwrap.c561
-rw-r--r--private/net/svcdlls/srvsvc/client/dfsstub.c342
-rw-r--r--private/net/svcdlls/srvsvc/client/makefile6
-rw-r--r--private/net/svcdlls/srvsvc/client/radmin.c556
-rw-r--r--private/net/svcdlls/srvsvc/client/sources57
-rw-r--r--private/net/svcdlls/srvsvc/client/srvbind.c111
-rw-r--r--private/net/svcdlls/srvsvc/client/srvstub.c3573
-rw-r--r--private/net/svcdlls/srvsvc/client/tsrvsvc.c90
-rw-r--r--private/net/svcdlls/srvsvc/dirs30
-rw-r--r--private/net/svcdlls/srvsvc/import.h55
-rw-r--r--private/net/svcdlls/srvsvc/import.idl58
-rw-r--r--private/net/svcdlls/srvsvc/lib/adtcomn.c216
-rw-r--r--private/net/svcdlls/srvsvc/lib/makefile6
-rw-r--r--private/net/svcdlls/srvsvc/lib/sources38
-rw-r--r--private/net/svcdlls/srvsvc/makefil060
-rw-r--r--private/net/svcdlls/srvsvc/server/adtsrv.c450
-rw-r--r--private/net/svcdlls/srvsvc/server/canon.c259
-rw-r--r--private/net/svcdlls/srvsvc/server/cdev.c341
-rw-r--r--private/net/svcdlls/srvsvc/server/cdevq.c215
-rw-r--r--private/net/svcdlls/srvsvc/server/cmdline.c335
-rw-r--r--private/net/svcdlls/srvsvc/server/conn.c152
-rw-r--r--private/net/svcdlls/srvsvc/server/daytona/makefile6
-rw-r--r--private/net/svcdlls/srvsvc/server/daytona/sources89
-rw-r--r--private/net/svcdlls/srvsvc/server/dfs.c835
-rw-r--r--private/net/svcdlls/srvsvc/server/dirs3
-rw-r--r--private/net/svcdlls/srvsvc/server/disk.c173
-rw-r--r--private/net/svcdlls/srvsvc/server/file.c329
-rw-r--r--private/net/svcdlls/srvsvc/server/internal.c214
-rw-r--r--private/net/svcdlls/srvsvc/server/registry.c2799
-rw-r--r--private/net/svcdlls/srvsvc/server/scavengr.c1319
-rw-r--r--private/net/svcdlls/srvsvc/server/security.c902
-rw-r--r--private/net/svcdlls/srvsvc/server/sess.c293
-rw-r--r--private/net/svcdlls/srvsvc/server/share.c2897
-rw-r--r--private/net/svcdlls/srvsvc/server/srvconfg.h648
-rw-r--r--private/net/svcdlls/srvsvc/server/srvinfo.c677
-rw-r--r--private/net/svcdlls/srvsvc/server/srvmain.c680
-rw-r--r--private/net/svcdlls/srvsvc/server/srvsvc.def6
-rw-r--r--private/net/svcdlls/srvsvc/server/srvsvc.rc12
-rw-r--r--private/net/svcdlls/srvsvc/server/srvsvcp.h406
-rw-r--r--private/net/svcdlls/srvsvc/server/ssdata.c229
-rw-r--r--private/net/svcdlls/srvsvc/server/ssdata.h125
-rw-r--r--private/net/svcdlls/srvsvc/server/ssdebug.h90
-rw-r--r--private/net/svcdlls/srvsvc/server/ssinit.c1524
-rw-r--r--private/net/svcdlls/srvsvc/server/ssreg.h116
-rw-r--r--private/net/svcdlls/srvsvc/server/sssec.h174
-rw-r--r--private/net/svcdlls/srvsvc/server/sssubs.c1064
-rw-r--r--private/net/svcdlls/srvsvc/server/stats.c113
-rw-r--r--private/net/svcdlls/srvsvc/server/tod.c204
-rw-r--r--private/net/svcdlls/srvsvc/server/xport.c689
-rw-r--r--private/net/svcdlls/srvsvc/server/xsdata.c301
-rw-r--r--private/net/svcdlls/srvsvc/server/xsdata.h69
-rw-r--r--private/net/svcdlls/srvsvc/server/xsinit.c536
-rw-r--r--private/net/svcdlls/srvsvc/server/xsproc.c1092
-rw-r--r--private/net/svcdlls/srvsvc/srvnames.h21
-rw-r--r--private/net/svcdlls/srvsvc/srvsvc.acf44
-rw-r--r--private/net/svcdlls/srvsvc/srvsvc.idl1078
-rw-r--r--private/net/svcdlls/upssvc/install.c57
-rw-r--r--private/net/svcdlls/upssvc/makefile6
-rw-r--r--private/net/svcdlls/upssvc/ntshutio.c94
-rw-r--r--private/net/svcdlls/upssvc/remove.c47
-rw-r--r--private/net/svcdlls/upssvc/sources31
-rw-r--r--private/net/svcdlls/upssvc/spec195
-rw-r--r--private/net/svcdlls/upssvc/subr.c686
-rw-r--r--private/net/svcdlls/upssvc/toggle.c118
-rw-r--r--private/net/svcdlls/upssvc/ups.c1303
-rw-r--r--private/net/svcdlls/upssvc/ups.h130
-rw-r--r--private/net/svcdlls/upssvc/upssvc.rc11
-rw-r--r--private/net/svcdlls/wkssvc/client/makefile6
-rw-r--r--private/net/svcdlls/wkssvc/client/sources56
-rw-r--r--private/net/svcdlls/wkssvc/client/wksbind.c195
-rw-r--r--private/net/svcdlls/wkssvc/client/wksstub.c1425
-rw-r--r--private/net/svcdlls/wkssvc/client/wksstub.def19
-rw-r--r--private/net/svcdlls/wkssvc/client/wsclient.h81
-rw-r--r--private/net/svcdlls/wkssvc/client/wstest.c981
-rw-r--r--private/net/svcdlls/wkssvc/client/wstinv.c449
-rw-r--r--private/net/svcdlls/wkssvc/client/wstsend.c102
-rw-r--r--private/net/svcdlls/wkssvc/client/wstsenum.c580
-rw-r--r--private/net/svcdlls/wkssvc/dirs29
-rw-r--r--private/net/svcdlls/wkssvc/imports.h46
-rw-r--r--private/net/svcdlls/wkssvc/imports.idl67
-rw-r--r--private/net/svcdlls/wkssvc/makefil062
-rw-r--r--private/net/svcdlls/wkssvc/server/daytona/makefile6
-rw-r--r--private/net/svcdlls/wkssvc/server/daytona/sources84
-rw-r--r--private/net/svcdlls/wkssvc/server/dfsgluon.h139
-rw-r--r--private/net/svcdlls/wkssvc/server/dfsmrshl.c1429
-rw-r--r--private/net/svcdlls/wkssvc/server/dfsmrshl.h406
-rw-r--r--private/net/svcdlls/wkssvc/server/dirs32
-rw-r--r--private/net/svcdlls/wkssvc/server/dominfo.c1628
-rw-r--r--private/net/svcdlls/wkssvc/server/dominfo.h42
-rw-r--r--private/net/svcdlls/wkssvc/server/message.c458
-rw-r--r--private/net/svcdlls/wkssvc/server/msgutil.c497
-rw-r--r--private/net/svcdlls/wkssvc/server/useaddel.c1554
-rw-r--r--private/net/svcdlls/wkssvc/server/usegenum.c1364
-rw-r--r--private/net/svcdlls/wkssvc/server/user.c1623
-rw-r--r--private/net/svcdlls/wkssvc/server/useutil.c1683
-rw-r--r--private/net/svcdlls/wkssvc/server/wkssvc.def6
-rw-r--r--private/net/svcdlls/wkssvc/server/wkssvc.rc12
-rw-r--r--private/net/svcdlls/wkssvc/server/wksta.c1392
-rw-r--r--private/net/svcdlls/wkssvc/server/ws.h137
-rw-r--r--private/net/svcdlls/wkssvc/server/wsbind.h64
-rw-r--r--private/net/svcdlls/wkssvc/server/wsconfig.c1385
-rw-r--r--private/net/svcdlls/wkssvc/server/wsconfig.h99
-rw-r--r--private/net/svcdlls/wkssvc/server/wsdevice.c1822
-rw-r--r--private/net/svcdlls/wkssvc/server/wsdevice.h178
-rw-r--r--private/net/svcdlls/wkssvc/server/wsdfs.c298
-rw-r--r--private/net/svcdlls/wkssvc/server/wsdfs.h34
-rw-r--r--private/net/svcdlls/wkssvc/server/wslogon.c101
-rw-r--r--private/net/svcdlls/wkssvc/server/wslsa.c349
-rw-r--r--private/net/svcdlls/wkssvc/server/wslsa.h54
-rw-r--r--private/net/svcdlls/wkssvc/server/wsmain.c1694
-rw-r--r--private/net/svcdlls/wkssvc/server/wsmain.h135
-rw-r--r--private/net/svcdlls/wkssvc/server/wsmsg.h116
-rw-r--r--private/net/svcdlls/wkssvc/server/wssec.c389
-rw-r--r--private/net/svcdlls/wkssvc/server/wssec.h107
-rw-r--r--private/net/svcdlls/wkssvc/server/wssend.c814
-rw-r--r--private/net/svcdlls/wkssvc/server/wsstats.c251
-rw-r--r--private/net/svcdlls/wkssvc/server/wsuse.h293
-rw-r--r--private/net/svcdlls/wkssvc/server/wsutil.c692
-rw-r--r--private/net/svcdlls/wkssvc/server/wsutil.h157
-rw-r--r--private/net/svcdlls/wkssvc/server/wswksta.h80
-rw-r--r--private/net/svcdlls/wkssvc/wkssvc.acf53
-rw-r--r--private/net/svcdlls/wkssvc/wkssvc.idl420
-rw-r--r--private/net/svcdlls/wkssvc/wsnames.h21
1068 files changed, 419072 insertions, 0 deletions
diff --git a/private/net/svcdlls/alrsvc/al.h b/private/net/svcdlls/alrsvc/al.h
new file mode 100644
index 000000000..bfd6dab88
--- /dev/null
+++ b/private/net/svcdlls/alrsvc/al.h
@@ -0,0 +1,199 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ al.h
+
+Abstract:
+
+ Private header file for the NT Alerter service included by every module
+ of the Alerter service.
+
+Author:
+
+ Rita Wong (ritaw) 01-July-1991
+
+Revision History:
+
+--*/
+
+#ifndef _AL_INCLUDED_
+#define _AL_INCLUDED_
+
+#include <nt.h> // NT definitions
+#include <ntrtl.h> // NT runtime library definitions
+#include <nturtl.h>
+
+#include <windef.h> // Win32 type definitions
+#include <winbase.h> // Win32 base API prototypes
+
+#include <lmcons.h> // LAN Manager common definitions
+#include <lmerr.h> // LAN Manager network error definitions
+#include <lmapibuf.h> // NetApiBufferFree
+#include <lmerrlog.h> // NELOG_
+
+#include <lmalert.h> // LAN Manager alert structures
+#include <icanon.h> // I_NetXxxCanonicalize routines
+
+#include <netlib.h> // LAN Man utility routines
+#include <netlibnt.h> // NetpNtStatusToApiStatus
+#include <netdebug.h> // NetpDbgPrint
+#include <tstring.h> // Transitional string functions
+
+
+#define AL_NULL_CHAR '\0'
+#define AL_SPACE_CHAR ' '
+
+//
+// Debug trace level bits for turning on/off trace statements in the
+// Alerter service
+//
+
+//
+// Utility trace statements
+//
+#define ALERTER_DEBUG_UTIL 0x00000001
+
+//
+// Configuration trace statements
+//
+#define ALERTER_DEBUG_CONFIG 0x00000002
+
+//
+// Main service functionality
+//
+#define ALERTER_DEBUG_MAIN 0x00000004
+
+//
+// Format message trace statements
+//
+#define ALERTER_DEBUG_FORMAT 0x00000008
+
+//
+// All debug flags on
+//
+#define ALERTER_DEBUG_ALL 0xFFFFFFFF
+
+
+#if DBG
+
+#define STATIC
+
+extern DWORD AlerterTrace;
+
+#define DEBUG if (TRUE)
+
+#define IF_DEBUG(Function) if (AlerterTrace & ALERTER_DEBUG_ ## Function)
+
+#else
+
+#define STATIC static
+
+#define DEBUG if (FALSE)
+
+#define IF_DEBUG(Function) if (FALSE)
+
+#endif // DBG
+
+
+//-------------------------------------------------------------------//
+// //
+// Type definitions //
+// //
+//-------------------------------------------------------------------//
+
+typedef enum _AL_ERROR_CONDITION {
+ AlErrorRegisterControlHandler = 0,
+ AlErrorCreateMailslot,
+ AlErrorGetComputerName,
+ AlErrorNotifyServiceController,
+ AlErrorSendMessage
+} AL_ERROR_CONDITION, *PAL_ERROR_CONDITION;
+
+
+//-------------------------------------------------------------------//
+// //
+// Function prototypes //
+// //
+//-------------------------------------------------------------------//
+
+//
+// From almain.c
+//
+VOID
+AlHandleError(
+ IN AL_ERROR_CONDITION FailingCondition,
+ IN NET_API_STATUS Status,
+ IN LPTSTR MessageAlias OPTIONAL
+ );
+
+//
+// From alformat.c
+//
+VOID
+AlAdminFormatAndSend(
+ IN PSTD_ALERT Alert
+ );
+
+VOID
+AlUserFormatAndSend(
+ IN PSTD_ALERT Alert
+ );
+
+VOID
+AlPrintFormatAndSend(
+ IN PSTD_ALERT Alert
+ );
+
+VOID
+AlFormatErrorMessage(
+ IN NET_API_STATUS Status,
+ IN LPTSTR MessageAlias,
+ OUT LPTSTR ErrorMessage,
+ IN DWORD ErrorMessageBufferSize
+ );
+
+NET_API_STATUS
+AlCanonicalizeMessageAlias(
+ LPTSTR MessageAlias
+ );
+
+#if DBG
+VOID
+AlHexDump(
+ LPBYTE Buffer,
+ DWORD BufferSize
+ );
+#endif
+
+
+//
+// From alconfig.c
+//
+NET_API_STATUS
+AlGetAlerterConfiguration(
+ VOID
+ );
+
+VOID
+AlLogEvent(
+ DWORD MessageId,
+ DWORD NumberOfSubStrings,
+ LPWSTR *SubStrings
+ );
+
+
+//-------------------------------------------------------------------//
+// //
+// Global variables //
+// //
+//-------------------------------------------------------------------//
+
+extern LPSTR AlertNamesA;
+extern LPTSTR AlertNamesW;
+extern LPSTR AlLocalComputerNameA;
+extern LPTSTR AlLocalComputerNameW;
+
+#endif // ifdef _AL_INCLUDED_
diff --git a/private/net/svcdlls/alrsvc/alconfig.c b/private/net/svcdlls/alrsvc/alconfig.c
new file mode 100644
index 000000000..632574d3d
--- /dev/null
+++ b/private/net/svcdlls/alrsvc/alconfig.c
@@ -0,0 +1,295 @@
+/*++
+
+Copyright (c) 1991-92 Microsoft Corporation
+
+Module Name:
+
+ alconfig.c
+
+Abstract:
+
+ This module contains the Alerter service configuration routines.
+
+Author:
+
+ Rita Wong (ritaw) 16-July-1991
+
+Revision History:
+
+--*/
+
+#include "alconfig.h"
+#include <tstr.h> // STRCPY(), etc.
+
+STATIC
+NET_API_STATUS
+AlGetLocalComputerName(
+ VOID
+ );
+
+//-------------------------------------------------------------------//
+// //
+
+// Global variables //
+// //
+//-------------------------------------------------------------------//
+
+//
+// Alert names
+//
+LPSTR AlertNamesA; // For inclusion into message text (space-separated)
+LPTSTR AlertNamesW; // For sending message to (NULL-separated)
+
+//
+// Local computer name
+//
+LPSTR AlLocalComputerNameA;
+LPTSTR AlLocalComputerNameW;
+
+
+
+NET_API_STATUS
+AlGetAlerterConfiguration(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This routine reads in alerter configuration info which is the alert names.
+ If a failure occurs, or alert names could not be found, the error is
+ logged but it will not prevent the Alerter service from starting up.
+
+Arguments:
+
+ AlUicCode - Supplies the termination code to the Service Controller.
+
+Return Value:
+
+ NERR_Success or error getting the computer name.
+
+--*/
+{
+ NET_API_STATUS status;
+ LPNET_CONFIG_HANDLE AlerterSection;
+ LPTSTR UnicodeAlertNames;
+ LPSTR AnsiAlertNames;
+#ifdef UNICODE
+ LPSTR Name; // for conversion from Unicode to ANSI
+#endif
+ DWORD AlertNamesSize;
+ LPWSTR SubString[1];
+ TCHAR StatusString[25];
+
+
+ AlertNamesA = NULL;
+ AlertNamesW = NULL;
+
+ //
+ // Get the computer name.
+ //
+ if ((status = AlGetLocalComputerName()) != NERR_Success) {
+ return status;
+ }
+
+ //
+ // Open config file and get handle to the Alerter section
+ //
+ if ((status = NetpOpenConfigData(
+ &AlerterSection,
+ NULL, // local server
+ SECT_NT_ALERTER,
+ TRUE // read-only
+ )) != NERR_Success) {
+ NetpKdPrint(("[Alerter] Could not open config section %lu\n", status));
+
+ SubString[0] = ultow(status, StatusString, 10);
+ AlLogEvent(
+ NELOG_Build_Name,
+ 1,
+ SubString
+ );
+ return NO_ERROR;
+ }
+
+ //
+ // Get the alert names from the configuration file
+ //
+ if ((status = NetpGetConfigTStrArray(
+ AlerterSection,
+
+ ALERTER_KEYWORD_ALERTNAMES,
+ &AlertNamesW // alloc and set ptr
+ )) != NERR_Success) {
+ NetpKdPrint(("[Alerter] Could not get alert names %lu\n", status));
+
+ SubString[0] = ultow(status, StatusString, 10);
+ AlLogEvent(
+ NELOG_Build_Name,
+ 1,
+ SubString
+ );
+
+ AlertNamesW = NULL;
+ goto CloseConfigFile;
+ }
+
+ AlertNamesSize = NetpTStrArraySize(AlertNamesW) / sizeof(TCHAR) * sizeof(CHAR);
+
+ if ((AlertNamesA = (LPSTR) LocalAlloc(
+ LMEM_ZEROINIT,
+ AlertNamesSize
+ )) == NULL) {
+ NetpKdPrint(("[Alerter] Error allocating AlertNamesA %lu\n", GetLastError()));
+ NetApiBufferFree(AlertNamesW);
+ AlertNamesW = NULL;
+ goto CloseConfigFile;
+ }
+
+ AnsiAlertNames = AlertNamesA;
+ UnicodeAlertNames = AlertNamesW;
+
+ //
+ // Canonicalize alert names, and convert the unicode names to ANSI
+ //
+ while (*UnicodeAlertNames != TCHAR_EOS) {
+
+ AlCanonicalizeMessageAlias(UnicodeAlertNames);
+
+#ifdef UNICODE
+ Name = NetpAllocStrFromWStr(UnicodeAlertNames);
+ if (Name != NULL) {
+ (void) strcpy(AnsiAlertNames, Name);
+ AnsiAlertNames += (strlen(AnsiAlertNames) + 1);
+ }
+ (void) NetApiBufferFree(Name);
+#else
+ (void) strcpy(AnsiAlertNames, UnicodeAlertNames);
+ AnsiAlertNames += (strlen(AnsiAlertNames) + 1);
+#endif
+
+ UnicodeAlertNames += (STRLEN(UnicodeAlertNames) + 1);
+ }
+
+
+ //
+ // Substitute the NULL terminators, which separate the alert names,
+ // in AlertNamesA with spaces. There's a space after the last alert
+ // name.
+ //
+ AnsiAlertNames = AlertNamesA;
+ while (*AnsiAlertNames != AL_NULL_CHAR) {
+ AnsiAlertNames = strchr(AnsiAlertNames, AL_NULL_CHAR);
+ *AnsiAlertNames++ = AL_SPACE_CHAR;
+ }
+
+CloseConfigFile:
+ (void) NetpCloseConfigData( AlerterSection );
+
+ //
+ // Errors from reading AlertNames should be ignored so we always
+ // return success here.
+ //
+ return NERR_Success;
+}
+
+
+STATIC
+NET_API_STATUS
+AlGetLocalComputerName(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This function gets the local computer name and stores both the ANSI
+ and Unicode versions of it.
+
+Arguments:
+
+ None. Sets the global pointers AlLocalComputerNameA and
+ AlLocalComputerNameW.
+
+Return Value:
+
+ NERR_Success or error getting the local computer name.
+
+--*/
+{
+ NET_API_STATUS status;
+
+
+ AlLocalComputerNameA = NULL;
+ AlLocalComputerNameW = NULL;
+
+ if ((status = NetpGetComputerName(
+ &AlLocalComputerNameW
+ )) != NERR_Success) {
+ AlLocalComputerNameW = NULL;
+ return status;
+ }
+
+ AlCanonicalizeMessageAlias(AlLocalComputerNameW);
+
+ //
+ // Convert the computer name into ANSI
+ //
+#ifdef UNICODE
+ AlLocalComputerNameA = NetpAllocStrFromWStr(AlLocalComputerNameW);
+
+ if (AlLocalComputerNameA == NULL) {
+ status = ERROR_NOT_ENOUGH_MEMORY;
+ }
+#else
+ status = NetApiBufferAllocate(
+ STRSIZE(AlLocalComputerNameW),
+ &AlLocalComputerNameA
+ );
+ if (status == NERR_Success) {
+ (void) strcpy(AlLocalComputerNameA, AlLocalComputerNameW);
+ }
+ else {
+ AlLocalComputerNameA = NULL;
+ }
+#endif
+
+ return status;
+}
+
+
+VOID
+AlLogEvent(
+ DWORD MessageId,
+ DWORD NumberOfSubStrings,
+ LPWSTR *SubStrings
+ )
+{
+ HANDLE LogHandle;
+
+
+ LogHandle = RegisterEventSourceW (
+ NULL,
+ SERVICE_ALERTER
+ );
+
+ if (LogHandle == NULL) {
+ NetpKdPrint(("[Alerter] RegisterEventSourceW failed %lu\n",
+ GetLastError()));
+ return;
+ }
+
+ (void) ReportEventW(
+ LogHandle,
+ EVENTLOG_ERROR_TYPE,
+ 0, // event category
+ MessageId,
+ (PSID) NULL, // no SID
+ (WORD)NumberOfSubStrings,
+ 0,
+ SubStrings,
+ (PVOID) NULL
+ );
+
+ DeregisterEventSource(LogHandle);
+}
diff --git a/private/net/svcdlls/alrsvc/alconfig.h b/private/net/svcdlls/alrsvc/alconfig.h
new file mode 100644
index 000000000..093dec3dd
--- /dev/null
+++ b/private/net/svcdlls/alrsvc/alconfig.h
@@ -0,0 +1,33 @@
+/*++
+
+Copyright (c) 1991-92 Microsoft Corporation
+
+Module Name:
+
+ alconfig.h
+
+Abstract:
+
+ Private header file for the Alerter service module that reads in
+ configuration information.
+
+Author:
+
+ Rita Wong (ritaw) 16-July-1991
+
+Revision History:
+
+--*/
+
+
+#ifndef _ALCONFIG_INCLUDED_
+#define _ALCONFIG_INCLUDED_
+
+#include "al.h" // Common include file for Alerter service
+#include <config.h> // Net config helpers in netlib.
+#include <confname.h> // SECT_NT_ALERTER, ALERT_KEYWORD_ stuff.
+#include <lmserver.h>
+#include <lmwksta.h>
+#include <lmapibuf.h>
+
+#endif
diff --git a/private/net/svcdlls/alrsvc/alformat.c b/private/net/svcdlls/alrsvc/alformat.c
new file mode 100644
index 000000000..2ae3ea16b
--- /dev/null
+++ b/private/net/svcdlls/alrsvc/alformat.c
@@ -0,0 +1,1782 @@
+/*++
+
+Copyright (c) 1987-1991 Microsoft Corporation
+
+Module Name:
+
+ alformat.c
+
+Abstract:
+
+ This module contains routines to format alert messages sent out by the
+ Alerter service.
+
+Author:
+
+ Ported from LAN Man 2.0
+
+Environment:
+
+ User mode only.
+ Contains NT-specific code.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+ 08-July-1991 (ritaw)
+ Ported to NT. Converted to NT style.
+
+--*/
+
+#include "alformat.h" // Constant definitions
+
+//-------------------------------------------------------------------//
+// //
+// Global variables //
+// //
+//-------------------------------------------------------------------//
+
+CHAR MessageBuffer[MAX_ALERTER_MESSAGE_SIZE];
+
+//-------------------------------------------------------------------//
+// //
+// Local function prototypes //
+// //
+//-------------------------------------------------------------------//
+
+STATIC
+NET_API_STATUS
+AlMakeMessageHeader(
+ IN LPTSTR From,
+ IN LPTSTR To,
+ IN DWORD MessageSubjectId,
+ IN DWORD AlertNotificationTime,
+ IN BOOL IsAdminAlert
+ );
+
+STATIC
+NET_API_STATUS
+AlAppendMessage(
+ IN DWORD MessageId,
+ OUT LPSTR MessageBuffer,
+ IN LPSTR *SubstituteStrings,
+ IN DWORD NumberOfSubstituteStrings,
+ IN BOOL StripLead
+ );
+
+STATIC
+NET_API_STATUS
+AlMakeMessageBody(
+ IN DWORD MessageId,
+ IN LPTSTR MergeStrings,
+ IN DWORD NumberOfMergeStrings
+ );
+
+STATIC
+VOID
+AlMakePrintMessage(
+ IN PSTD_ALERT Alert,
+ IN PPRINT_OTHER_INFO PrintInfo,
+ IN LPTSTR ComputerName,
+ IN LPTSTR PrintQueue,
+ IN LPTSTR PortName,
+ IN LPTSTR ErrorStatusText,
+ IN BOOL AdminAlso
+ );
+
+STATIC
+NET_API_STATUS
+AlSendMessage(
+ IN LPTSTR MessageAlias
+ );
+
+STATIC
+BOOL
+IsDuplicateReceiver(
+ LPTSTR Name
+ );
+
+STATIC
+DWORD
+AlGetCountry(
+ LPTSTR Username
+ );
+
+
+STATIC
+NET_API_STATUS
+AlMakeMessageHeader(
+ IN LPTSTR From,
+ IN LPTSTR To,
+ IN DWORD MessageSubjectId,
+ IN DWORD AlertNotificationTime,
+ IN BOOL IsAdminAlert
+ )
+/*++
+
+Routine Description:
+
+ This function creates the header of an alert message, and puts it in the
+ MessageBuffer. A message header takes the following form:
+
+ From: SPOOLER at \\PRT41130
+ To: DAISY
+ Subj: ** PRINTING NOTIFICATION **
+ Date: 06-23-91 01:16am
+
+Arguments:
+
+ From - Supplies the component that raised the alert.
+
+ To - Supplies the message alias name of the recipient.
+
+ MessageSubjectId - Supplies an id which indicates the subject of the
+ alert.
+
+ AlertNotificationTime - Supplies the time of the alert notification.
+
+ IsAdminAlert - Supplies a flag which if TRUE indicates that the To line
+ should be created for multiple recipients.
+
+Return Value:
+
+ ERROR_NOT_ENOUGH_MEMORY - if Unicode to ANSI conversion buffer cannot
+ be allocated.
+
+ NERR_Success - if successful.
+
+--*/
+{
+ //
+ // Array of subtitution strings for an alert message
+ //
+ LPSTR SubstituteStrings[2];
+
+ //
+ // Tab read in from the message file
+ //
+ static CHAR TabMessage[80] = "";
+
+ LPSTR AnsiTo;
+ DWORD ToLineLength; // Number of chars on the To line
+ WORD MessageLength; // Returned by DosGetMessage
+
+ LPSTR AnsiAlertNames;
+
+ LPSTR FormatPointer1;
+ LPSTR FormatPointer2;
+ CHAR SaveChar;
+
+ CHAR TimeAndDate[26]; // Returned by net_ctime
+
+ LPSTR PlaceHolder = "X";
+ LPSTR PointerToPlaceHolder = NULL;
+
+
+ //
+ // Read in the message tab, if necessary
+ //
+ if (*TabMessage == AL_NULL_CHAR) {
+
+ if (DosGetMessage(
+ NULL, // String substitution table
+ 0, // Number of entries in table above
+ (LPBYTE) TabMessage, // Buffer receiving message
+ sizeof(TabMessage), // Size of buffer receiving message
+ APE2_ALERTER_TAB, // Message number to retrieve
+ MESSAGE_FILENAME, // Name of message file
+ &MessageLength // Number of bytes returned
+ )) {
+
+ *TabMessage = AL_NULL_CHAR;
+ }
+
+ }
+
+ //
+ // Creating a new alert message
+ //
+ MessageBuffer[0] = AL_NULL_CHAR;
+
+ //
+ // "From: <From> at \\<AlLocalComputerName>"
+ //
+ // Alerts received by the Alerter service all come from the local server.
+ //
+#ifdef UNICODE
+ SubstituteStrings[0] = NetpAllocStrFromWStr(From);
+
+ if (SubstituteStrings[0] == NULL) {
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+#else
+ SubstituteStrings[0] = From;
+#endif
+
+ if (AlLocalComputerNameA != NULL) {
+ SubstituteStrings[1] = AlLocalComputerNameA;
+ }
+ else {
+ return ERROR_GEN_FAILURE;
+ }
+
+ //
+ // Put the from line into the message buffer
+ //
+ AlAppendMessage(
+ APE2_ALERTER_FROM,
+ MessageBuffer,
+ SubstituteStrings,
+ 2, // Number of strings to substitute into message
+ FALSE // Flag to strip leading "NETxxxx: " from the
+ // message
+ );
+
+#ifdef UNICODE
+ NetApiBufferFree(SubstituteStrings[0]);
+#endif
+
+ //
+ // "To: X"
+ //
+ // The 'X' is a place holder for the To message so that DosGetMessage
+ // can perform the substitution. We are not putting the real string
+ // because the message can either be sent to one recipient (non-admin
+ // alerts or to many recipient (admin alert).
+ //
+ SubstituteStrings[0] = PlaceHolder;
+
+ AlAppendMessage(
+ APE2_ALERTER_TO,
+ MessageBuffer,
+ SubstituteStrings, // Number of strings to substitute into message
+ 1, // Flag to strip leading "NETxxxx: " from the
+ FALSE // message
+ );
+
+ //
+ // Search for the place holder character and replace with zero terminator
+ //
+ PointerToPlaceHolder = strrchr(MessageBuffer, *PlaceHolder);
+
+ //
+ // If PointerToPlaceHolder == NULL, we have a big problem, but rather than
+ // choke, we'll just continue. The resulting message will be
+ // mis-formated (the place holder will still be there and the To name(s)
+ // will be on the next line) but it will still be sent.
+ //
+
+ if (PointerToPlaceHolder != NULL) {
+ *PointerToPlaceHolder = AL_NULL_CHAR;
+ }
+
+#ifdef UNICODE
+ if (To != NULL) {
+ AnsiTo = NetpAllocStrFromWStr(To);
+ if (AnsiTo == NULL) {
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+ }
+ else {
+ AnsiTo = NULL;
+ }
+#else
+ AnsiTo = To;
+#endif
+
+ if (IsAdminAlert) {
+
+ //
+ // Do not send the message to the same person twice, since it is
+ // possible that the person is on the alert names list, as well as
+ // specified by To.
+ //
+
+ if (To != NULL && ! IsDuplicateReceiver(To)) {
+
+ //
+ // Print admin alerts, like printer is offline or out of paper,
+ // will be sent to the person who's printing as well as admins
+ // on the alertnames list.
+ //
+
+ strcat(MessageBuffer, AnsiTo);
+ strcat(MessageBuffer, " ");
+ ToLineLength = strlen(TabMessage) + strlen(AnsiTo) + sizeof(CHAR);
+
+ }
+ else {
+
+ //
+ // All admin alerts will have a NULL To field, except print admin
+ // alerts, e.g. ran out of paper while printing.
+ //
+
+ ToLineLength = strlen(TabMessage);
+ }
+
+ if (AlertNamesA != NULL) {
+
+ //
+ // AlertNamesA is space-separated.
+ //
+
+ AnsiAlertNames = AlertNamesA;
+
+ //
+ // Wrap the names to the next line if width of all alert names exceeds
+ // screen display width.
+ //
+ while ((strlen(AnsiAlertNames) + ToLineLength) > MESSAGE_WIDTH) {
+
+ FormatPointer1 = AnsiAlertNames + 1 +
+ (MESSAGE_WIDTH - ToLineLength);
+ SaveChar = *FormatPointer1;
+ *FormatPointer1 = AL_NULL_CHAR;
+ FormatPointer2 = strrchr(AnsiAlertNames, AL_SPACE_CHAR);
+ *FormatPointer2 = AL_NULL_CHAR;
+ strcat(MessageBuffer, AnsiAlertNames);
+ *FormatPointer2++ = AL_SPACE_CHAR;
+ *FormatPointer1 = SaveChar;
+ strcat(MessageBuffer, AL_EOL_STRING);
+ strcat(MessageBuffer, TabMessage);
+ AnsiAlertNames = FormatPointer2;
+ ToLineLength = strlen(TabMessage);
+ }
+
+ strcat(MessageBuffer, AnsiAlertNames);
+ }
+ }
+ else {
+
+ //
+ // Non-admin alert
+ //
+
+ if (To != NULL) {
+ strcat(MessageBuffer, AnsiTo);
+ }
+ }
+
+#ifdef UNICODE
+ if (AnsiTo != NULL) {
+ NetApiBufferFree(AnsiTo);
+ }
+#endif
+
+ strcat(MessageBuffer, AL_EOL_STRING);
+
+ //
+ // Append subject line to MessageBuffer
+ //
+ // "Subj: <Message string of MessageSubjectId>"
+ //
+ AlAppendMessage(
+ MessageSubjectId,
+ MessageBuffer,
+ SubstituteStrings,
+ 0, // No substitution strings
+ FALSE
+ );
+
+ net_ctime(
+ &AlertNotificationTime,
+ TimeAndDate,
+ sizeof(TimeAndDate),
+ 2
+ );
+
+ SubstituteStrings[0] = TimeAndDate;
+
+ //
+ // "Date: <mm/dd/yy hh:mm>"
+ //
+ AlAppendMessage(
+ APE2_ALERTER_DATE,
+ MessageBuffer,
+ SubstituteStrings,
+ 1,
+ FALSE
+ );
+
+ strcat(MessageBuffer, AL_EOL_STRING);
+
+ return NERR_Success;
+}
+
+
+VOID
+AlAdminFormatAndSend(
+ IN PSTD_ALERT Alert
+ )
+/*++
+
+Routine Description:
+
+ This function takes an admin alert notification, formats it into an alert
+ message, and sends it to the admins with message aliases that are listed
+ on the alert names entry in the configuration.
+
+ An admin alert notification (arrived via the mailslot) has the following
+ form:
+
+ Timestamp of the alert event
+ "ADMIN"
+ "SERVER"
+
+ Message id of message
+ Number of merge strings which will be substituted into message
+ Merge strings, each separated by a zero terminator.
+
+Arguments:
+
+ Alert - Supplies a pointer to the alert notification structure.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ NET_API_STATUS status;
+
+ LPTSTR AdminToAlert;
+ PADMIN_OTHER_INFO AdminInfo = (PADMIN_OTHER_INFO) ALERT_OTHER_INFO(Alert);
+
+ DWORD ThisCountry = MAXULONG;
+ DWORD LastCountry = MAXULONG;
+
+ AdminToAlert = AlertNamesW;
+
+
+ IF_DEBUG(FORMAT) {
+ NetpKdPrint(("[Alerter] Got admin alert\n"));
+ }
+
+ while (AdminToAlert != NULL && *AdminToAlert != TCHAR_EOS) {
+
+ //
+ // We need to format the message if this is the first time it has
+ // been seen or if the country code for this name is different than
+ // the country code for the last name. If the message file is not
+ // available for the new code, we use the default message file. We
+ // could be smarter about deciding when to reformat (only when
+ // no one in the list is left that will receive the message in
+ // the current language). We save this optimization for the
+ // future.
+ //
+
+ if ( ((ThisCountry = AlGetCountry(AdminToAlert)) != LastCountry) ||
+ (LastCountry == MAXULONG) ) {
+
+ LastCountry = ThisCountry;
+
+ //
+ // Gets the correct message file name; each user may need the
+ // message in a different language
+ //
+ // AlSetupMessageFile(ThisCountry);
+
+ //
+ // Format the message for this alert name
+ //
+ status = AlMakeMessageHeader(
+ Alert->alrt_servicename,
+ NULL, // The To field is always NULL
+ APE2_ALERTER_ADMN_SUBJ,
+ Alert->alrt_timestamp,
+ TRUE // Admin alert
+ );
+
+ if (status != NERR_Success) {
+ NetpKdPrint((
+ "[Alerter] Alert not sent. Error making message header %lu\n",
+ status
+ ));
+ return;
+ }
+
+ AlMakeMessageBody(
+ AdminInfo->alrtad_errcode,
+ (LPTSTR) ALERT_VAR_DATA(AdminInfo),
+ AdminInfo->alrtad_numstrings
+ );
+
+ }
+
+ //
+ // Send the message
+ //
+ (void) AlSendMessage(AdminToAlert);
+
+ AdminToAlert += (STRLEN(AdminToAlert) + 1);
+ }
+}
+
+
+
+STATIC
+DWORD
+AlGetCountry(
+ IN LPTSTR Username
+ )
+/*++
+
+Routine Description:
+
+ This function retrieves the country code of a user given the username.
+ If the user is not found, 0 is returned to indicate the default country
+ code of the local machine.
+
+Arguments:
+
+ Username - Supplies a pointer to the name of a user.
+
+Return Value:
+
+ Returns the country code of the user, or 0 if the user is not found.
+
+--*/
+{
+ UNREFERENCED_PARAMETER(Username);
+
+ return 0;
+
+ /*
+ struct user_info_11 user;
+ API_RET_TYPE ret_cd;
+ unsigned short avail;
+
+ if ( ret_cd = NetUserGetInfo(NULL, name, 11, (char far *)&user,
+ sizeof(struct user_info_11), &avail) )
+ {
+ if ( (ret_cd != ERROR_MORE_DATA) &&
+ (ret_cd != NERR_UserNotFound) )
+ return 0;
+ }
+
+ return ( user.usri11_country_code );
+ */
+}
+
+
+
+STATIC
+NET_API_STATUS
+AlMakeMessageBody(
+ IN DWORD MessageId,
+ IN LPTSTR MergeStrings,
+ IN DWORD NumberOfMergeStrings
+ )
+/*++
+
+Routine Description:
+
+ This function creates the body of an alert message and append it to the
+ header already in MessageBuffer.
+
+Arguments:
+
+ MessageId - Supplies a message id of the core message to be sent.
+
+ MergeStrings - Supplies a pointer to strings that would make up the message
+ to be sent. The strings are separated by zero terminators.
+
+ NumberOfMergeStrings - Supplies the number of strings pointed to by
+ MergeStrings.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+
+{
+
+ NET_API_STATUS status;
+ DWORD i;
+
+ LPSTR AdminMessage;
+ LPSTR MergeTable[9];
+ CHAR String[34];
+ LPSTR SubstituteStrings[2];
+
+ LPSTR CRPointer;
+ LPSTR EndPointer;
+
+
+ //
+ // Message utility can only handle substitution of up to 9 strings.
+ //
+ if (NumberOfMergeStrings > 9) {
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ //
+ // Allocate memory for the buffer which receives the message from the
+ // message file.
+ //
+ if ((AdminMessage = (LPSTR) LocalAlloc(
+ LMEM_ZEROINIT,
+ MAX_ALERTER_MESSAGE_SIZE
+ )) == NULL) {
+ return GetLastError();
+ }
+
+ if (MessageId == NO_MESSAGE) {
+
+ //
+ // Merge strings are the literal message (don't format). Print one
+ // per line.
+ //
+ for (i = 0; i < NumberOfMergeStrings; i++) {
+
+#ifdef UNICODE
+ SubstituteStrings[0] = NetpAllocStrFromWStr(MergeStrings);
+ if (SubstituteStrings[0] == NULL) {
+ (void) LocalFree(AdminMessage);
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+#else
+ SubstituteStrings[0] = MergeStrings;
+#endif
+ strcat(MessageBuffer, SubstituteStrings[0]);
+
+#ifdef UNICODE
+ NetApiBufferFree(SubstituteStrings[0]);
+#endif
+ strcat(MessageBuffer, AL_EOL_STRING);
+ MergeStrings = STRCHR(MergeStrings, TCHAR_EOS);
+ MergeStrings++;
+ }
+
+ }
+ else {
+
+ //
+ // Set up the MergeStrings table for the call to AlAppendMessage
+ //
+ for (i = 0; i < NumberOfMergeStrings; i++) {
+
+ IF_DEBUG(FORMAT) {
+ NetpKdPrint(("Merge string #%lu: " FORMAT_LPTSTR "\n", i, MergeStrings));
+ }
+
+#ifdef UNICODE
+ MergeTable[i] = NetpAllocStrFromWStr(MergeStrings);
+ if (MergeTable[i] == NULL) {
+ DWORD j;
+
+ (void) LocalFree(AdminMessage);
+ LocalFree(AdminMessage);
+ for (j = 0; j < i; j++) {
+ (void) LocalFree(MergeTable[j]);
+ }
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+#else
+ MergeTable[i] = MergeStrings;
+#endif
+ MergeStrings = STRCHR(MergeStrings, TCHAR_EOS);
+ MergeStrings++;
+ }
+
+
+ status = AlAppendMessage(
+ MessageId,
+ AdminMessage,
+ MergeTable,
+ NumberOfMergeStrings,
+ TRUE // Strip leading "NETxxxx: "
+ );
+
+#ifdef UNICODE
+ //
+ // Free memory allocated for Unicode to ANSI conversion
+ //
+ for (i = 0; i < NumberOfMergeStrings; i++) {
+ (void) LocalFree(MergeTable[i]);
+ }
+#endif
+
+ if (status != NERR_Success) {
+
+ //
+ // Could not find message of MessageId in message file. An error
+ // message will be sent.
+ //
+ _itoa(MessageId, String, 10);
+ SubstituteStrings[0] = String;
+ AlAppendMessage(
+ APE2_ALERTER_ERROR_MSG,
+ MessageBuffer,
+ SubstituteStrings,
+ 1,
+ FALSE
+ );
+
+ }
+ else {
+
+ //
+ // Got the message
+ //
+
+ //
+ // Process the message from DosGetMessage to replace the CR and
+ // LF with equivalent display characters.
+ //
+ CRPointer = strchr(AdminMessage, AL_CR_CHAR);
+ EndPointer = CRPointer;
+
+ while (CRPointer) {
+
+ *CRPointer = '\040';
+ CRPointer++;
+
+ //
+ // Check for end of message
+ //
+ if (*CRPointer != AL_NULL_CHAR) {
+
+ //
+ // Use display end-of-line character
+ //
+ *CRPointer = AL_EOL_CHAR;
+ }
+
+ EndPointer = CRPointer;
+ CRPointer = strchr(CRPointer, AL_CR_CHAR);
+ }
+
+ //
+ // Wipe out rest of garbage in the message
+ //
+ if (EndPointer) {
+ *EndPointer = AL_EOL_CHAR;
+ *(EndPointer + 1) = AL_NULL_CHAR;
+ }
+
+ strcat(MessageBuffer, AdminMessage);
+ }
+ }
+}
+
+
+
+VOID
+AlUserFormatAndSend(
+ IN PSTD_ALERT Alert
+ )
+/*++
+
+Routine Description:
+
+ This function takes a user alert notification, formats it into an alert
+ message, and sends it to the user. If there is an error sending to the
+ user message alias, the message is send to the computer name.
+
+ A user alert notification (arrived via the mailslot) has the following
+ form:
+
+ Timestamp of the alert event
+ "USER"
+ "SERVER"
+
+ Message id of message
+ Number of merge strings which will be substituted into message
+ Merge strings, each separated by a zero terminator.
+
+ Username
+ \\ComputerNameOfUser
+
+Arguments:
+
+ Alert - Supplies a pointer to the alert notification structure.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ NET_API_STATUS status;
+
+ PUSER_OTHER_INFO UserInfo = (PUSER_OTHER_INFO) ALERT_OTHER_INFO(Alert);
+
+ DWORD i;
+
+ LPTSTR MergeStrings;
+ LPTSTR Username;
+ LPTSTR ComputerNameOfUser;
+ LPTSTR To = NULL;
+
+
+ IF_DEBUG(FORMAT) {
+ NetpKdPrint(("[Alerter] Got user alert\n"));
+ }
+
+ MergeStrings = (LPTSTR) ALERT_VAR_DATA(UserInfo);
+
+ //
+ // Name of user to be notified of the alert is found after the merge
+ // strings.
+ //
+ for (Username = MergeStrings, i = 0; i < UserInfo->alrtus_numstrings; i++) {
+ Username += STRLEN(Username) + 1;
+ }
+
+ //
+ // Computer name of user is in the alert structure after the user name.
+ //
+ ComputerNameOfUser = Username + STRLEN(Username) + 1;
+
+ //
+ // If both username and computer name are not specified, cannot send the
+ // message
+ //
+ if (*Username == TCHAR_EOS && *ComputerNameOfUser == TCHAR_EOS) {
+ NetpKdPrint((
+ "[Alerter] Alert not sent. Username or computername not specified.\n"
+ ));
+ return;
+ }
+
+ //
+ // Setup the To pointer to point to the message alias that canonicalize
+ // properly. If there's a problem with the username, we will send the
+ // message to the computer name.
+ //
+
+ if (AlCanonicalizeMessageAlias(Username) == NERR_Success) {
+ To = Username;
+ }
+
+ //
+ // Computer name may or may not be preceeded by backslashes
+ //
+ if (*ComputerNameOfUser == TCHAR_BACKSLASH &&
+ *(ComputerNameOfUser + 1) == TCHAR_BACKSLASH) {
+ ComputerNameOfUser += 2;
+ }
+
+ if (AlCanonicalizeMessageAlias(ComputerNameOfUser) == NERR_Success &&
+ To == NULL) {
+ To = ComputerNameOfUser;
+ }
+
+ //
+ // Both username and computer name are not acceptable.
+ //
+ if (To == NULL) {
+ NetpKdPrint((
+ "[Alerter] Alert not sent. Username & computername are not acceptable.\n"
+ ));
+ return;
+ }
+
+ //
+ // Gets the correct message file name for the country code of the user
+ //
+ // AlSetUpMessageFile(AlGetCountry(Username));
+
+ status = AlMakeMessageHeader(
+ Alert->alrt_servicename,
+ To,
+ APE2_ALERTER_USER_SUBJ,
+ Alert->alrt_timestamp,
+ FALSE // Not an admin alert
+ );
+
+ if (status != NERR_Success) {
+ NetpKdPrint((
+ "[Alerter] Alert not sent. Error making message header %lu\n",
+ status
+ ));
+ return;
+ }
+
+ AlMakeMessageBody(
+ UserInfo->alrtus_errcode,
+ MergeStrings,
+ UserInfo->alrtus_numstrings
+ );
+
+ //
+ // Send the message
+ //
+ if (AlSendMessage(To) == NERR_Success) {
+ return;
+ }
+
+ //
+ // If To points to the Username and the send was not successful, try
+ // sending to the computer name of user.
+ //
+ if (To == Username) {
+ (void) AlSendMessage(ComputerNameOfUser);
+ }
+}
+
+
+
+VOID
+AlPrintFormatAndSend(
+ IN PSTD_ALERT Alert
+ )
+/*++
+
+Routine Description:
+
+ This function takes a print alert notification, formats it into an alert
+ message, and sends it to the computer name which the print job submitter
+ is on. If the print alert is for certain type of printing error, like
+ the printer is offline, the admins on the alert names list gets the alert
+ message as well.
+
+ A print alert notification (arrived via the mailslot) has the following
+ form:
+
+ Timestamp of the alert event
+ "PRINTING"
+ "SPOOLER"
+
+ Print job id
+ Status of print job which determines the alert message
+ Time which print job was submitted
+ Size of print job
+
+ \\ComputerNameOfSubmitter
+ Username
+ Print queue name
+ Port name through which print job was sent
+ Error status message if print error occured
+
+Arguments:
+
+ Alert - Supplies a pointer to the alert notification structure.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PPRINT_OTHER_INFO PrintInfo = (PPRINT_OTHER_INFO) ALERT_OTHER_INFO(Alert);
+
+ LPTSTR ComputerName;
+ LPTSTR Username;
+ LPTSTR PrintQueue;
+ LPTSTR PortName;
+ LPTSTR ErrorStatusText;
+
+ BOOL AdminAlso;
+
+ DWORD ThisCountry = MAXULONG;
+ DWORD LastCountry = MAXULONG;
+
+ LPTSTR AdminToAlert;
+
+
+ IF_DEBUG(FORMAT) {
+ NetpKdPrint(("[Alerter] Got print alert\n"));
+ }
+
+ ComputerName = (LPTSTR) ALERT_VAR_DATA(PrintInfo);
+ Username = ComputerName + STRLEN(ComputerName) + 1;
+ PrintQueue = Username + STRLEN(Username) + 1;
+ PortName = PrintQueue + STRLEN(PrintQueue) + 1;
+ ErrorStatusText = PortName + STRLEN(PortName) + 1;
+
+ //
+ // Get the country of print job submitter to create the message in the
+ // proper language.
+ //
+ ThisCountry = LastCountry = AlGetCountry(Username);
+ // AlSetupMessageFile(ThisCountry);
+
+ //
+ // If error, notify admins on the alert names list also, besides the print
+ // job submitter.
+ //
+ AdminAlso = (PrintInfo->alrtpr_status &
+ (PRJOB_DESTOFFLINE | PRJOB_INTERV | PRJOB_ERROR));
+
+ //
+ // Computer name may or may not be preceeded by backslashes
+ //
+ if (*ComputerName == TCHAR_BACKSLASH &&
+ *(ComputerName + 1) == TCHAR_BACKSLASH) {
+ ComputerName += 2;
+ }
+
+ (VOID) AlCanonicalizeMessageAlias(ComputerName);
+
+ //
+ // Format message for the print job submitter
+ //
+ AlMakePrintMessage(
+ Alert,
+ PrintInfo,
+ ComputerName,
+ PrintQueue,
+ PortName,
+ ErrorStatusText,
+ AdminAlso
+ );
+
+ //
+ // If the print job submitter is one of admins specified on the alert
+ // names list, don't send the message to the submitter yet. All the
+ // admins on alert names list will receive the message during admin
+ // processing below.
+ //
+ if ((AdminAlso && (! (IsDuplicateReceiver(ComputerName) ||
+ IsDuplicateReceiver(Username)) )) ||
+ ! AdminAlso) {
+ (void) AlSendMessage(ComputerName);
+ }
+
+ //
+ // We are done if this is not an admin related alert.
+ //
+ if (! AdminAlso) {
+ return;
+ }
+
+ AdminToAlert = AlertNamesW;
+
+ //
+ // Send alert message to every admin on the alert names list.
+ //
+ while (AdminToAlert != NULL && *AdminToAlert != TCHAR_EOS) {
+
+ //
+ // Format a message for an alert name his/her language is different
+ // from the one we've already created.
+ //
+ if ((ThisCountry = AlGetCountry(AdminToAlert)) != LastCountry) {
+
+ LastCountry = ThisCountry;
+
+ // AlSetupMessageFile(ThisCountry);
+
+ AlMakePrintMessage(
+ Alert,
+ PrintInfo,
+ ComputerName,
+ PrintQueue,
+ PortName,
+ ErrorStatusText,
+ AdminAlso
+ );
+ }
+
+ //
+ // Send message to the admin.
+ //
+ (void) AlSendMessage(AdminToAlert);
+
+ AdminToAlert += (STRLEN(AdminToAlert) + 1);
+ }
+}
+
+
+
+STATIC
+VOID
+AlMakePrintMessage(
+ IN PSTD_ALERT Alert,
+ IN PPRINT_OTHER_INFO PrintInfo,
+ IN LPTSTR ComputerName,
+ IN LPTSTR PrintQueue,
+ IN LPTSTR PortName,
+ IN LPTSTR ErrorStatusText,
+ IN BOOL AdminAlso
+ )
+/*++
+
+Routine Description:
+
+ This function creates the print alert message based on the print status.
+
+ An example of a print message generated by this function is:
+
+ "Print job <JobId> has finished printing on <PortName>.
+ Job was queued to <PrintQueue> on <mm/dd/yy hh:mm>.
+ Size of job is <PrintJobSize>."
+
+Arguments:
+
+ Alert - Supplies a pointer to the print alert notification.
+
+ PrintInfo - Supplies a pointer to print job information.
+
+ ComputerName - Supplies a pointer to the name of computer of print job
+ submitter without the preceeding backslashes.
+
+ PrintQueue - Supplies the name of the print queue the print job was
+ submitted to.
+
+ PortName - Supplies the name of the local device through which the print
+ job was sent.
+
+ ErrorStatusText - Supplies additional description of a print error. This
+ may be an empty string.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ NET_API_STATUS status;
+
+ CHAR TimeAndDate[26]; // for net_ctime
+ CHAR TempString[34];
+
+ LPSTR SubstituteStrings[2];
+
+ DWORD MessageId;
+
+
+ status = AlMakeMessageHeader(
+ Alert->alrt_servicename,
+ ComputerName,
+ APE2_ALERTER_PRNT_SUBJ,
+ Alert->alrt_timestamp,
+ AdminAlso
+ );
+
+ if (status != NERR_Success) {
+ NetpKdPrint((
+ "[Alerter] Alert not sent. Error making message header %lu\n",
+ status
+ ));
+ return;
+ }
+
+ _itoa(PrintInfo->alrtpr_jobid, TempString, 10);
+ SubstituteStrings[0] = TempString;
+
+ if (PrintInfo->alrtpr_status & PRJOB_DELETED) {
+
+ if ((PrintInfo->alrtpr_status & PRJOB_QSTATUS ) == PRJOB_QS_PRINTING) {
+
+#ifdef UNICODE
+ SubstituteStrings[1] = NetpAllocStrFromWStr(PortName);
+ if (SubstituteStrings[1] == NULL) {
+ return;
+ }
+#else
+ SubstituteStrings[1] = PortName;
+#endif
+ AlAppendMessage(
+ APE2_ALERTER_CANCELLED,
+ MessageBuffer,
+ SubstituteStrings,
+ 2,
+ FALSE
+ );
+
+#ifdef UNICODE
+ NetApiBufferFree(SubstituteStrings[1]);
+#endif
+
+ }
+ else {
+
+ AlAppendMessage(
+ APE2_ALERTER_DELETED,
+ MessageBuffer,
+ SubstituteStrings,
+ 1,
+ FALSE
+ );
+ }
+ }
+ else {
+
+ //
+ // Examine the status of the print queue
+ //
+ switch (PrintInfo->alrtpr_status & PRJOB_QSTATUS) {
+
+ case PRJOB_QS_PRINTING :
+
+#ifdef UNICODE
+ SubstituteStrings[1] = NetpAllocStrFromWStr(PortName);
+ if (SubstituteStrings[1] == NULL) {
+ return;
+ }
+#else
+ SubstituteStrings[1] = PortName;
+#endif
+
+ if (PrintInfo->alrtpr_status & PRJOB_COMPLETE) {
+ MessageId = APE2_ALERTER_FINISHED;
+ }
+ else if (PrintInfo->alrtpr_status & PRJOB_QS_PAUSED) {
+ MessageId = APE2_ALERTER_PAUSED;
+ }
+ else if (! (PrintInfo->alrtpr_status & PRJOB_COMPLETE) &&
+ (PrintInfo->alrtpr_status & PRJOB_ERROR)) {
+ MessageId = APE2_ALERTER_INCOMPL;
+ }
+ else {
+ MessageId = APE2_ALERTER_PRINTING;
+ }
+
+ AlAppendMessage(
+ MessageId,
+ MessageBuffer,
+ SubstituteStrings,
+ 2,
+ FALSE
+ );
+
+#ifdef UNICODE
+ NetApiBufferFree(SubstituteStrings[1]);
+#endif
+ //
+ // Check for any additional errors
+ //
+ MessageId = 0;
+
+ if (PrintInfo->alrtpr_status & PRJOB_DESTNOPAPER) {
+ MessageId = APE2_ALERTER_NOPAPER;
+ }
+ else if (PrintInfo->alrtpr_status & PRJOB_DESTOFFLINE) {
+ MessageId = APE2_ALERTER_OFFLINE;
+ }
+ else if (PrintInfo->alrtpr_status & PRJOB_ERROR) {
+ MessageId = APE2_ALERTER_ERRORS;
+ }
+ else if (PrintInfo->alrtpr_status & PRJOB_INTERV) {
+ MessageId = APE2_ALERTER_HUMAN;
+ }
+
+ if (MessageId) {
+ AlAppendMessage(
+ MessageId,
+ MessageBuffer,
+ NULL,
+ 0,
+ FALSE
+ );
+ }
+
+ //
+ // Also include error status message sent over by the spooler
+ //
+ if (! (PrintInfo->alrtpr_status & PRJOB_COMPLETE) &&
+ ErrorStatusText[0] != AL_NULL_CHAR) {
+
+#ifdef UNICODE
+ SubstituteStrings[1] = NetpAllocStrFromWStr(ErrorStatusText);
+ if (SubstituteStrings[1] == NULL) {
+ return;
+ }
+#else
+ SubstituteStrings[1] = ErrorStatusText;
+#endif
+ strcat(MessageBuffer, SubstituteStrings[1]);
+ strcat(MessageBuffer, AL_EOL_STRING);
+#ifdef UNICODE
+ NetApiBufferFree(SubstituteStrings[1]);
+#endif
+ }
+
+ break;
+
+ case PRJOB_QS_PAUSED :
+
+ AlAppendMessage(
+ APE2_ALERTER_HELD,
+ MessageBuffer,
+ SubstituteStrings,
+ 1,
+ FALSE
+ );
+ break;
+
+ case PRJOB_QS_QUEUED :
+ case PRJOB_QS_SPOOLING :
+
+ AlAppendMessage(
+ APE2_ALERTER_QUEUED,
+ MessageBuffer,
+ SubstituteStrings,
+ 1,
+ FALSE
+ );
+ break;
+ }
+
+ }
+
+ //
+ // Format print job submission time
+ //
+ net_ctime(
+ &PrintInfo->alrtpr_submitted,
+ TimeAndDate,
+ sizeof(TimeAndDate),
+ 2
+ );
+
+#ifdef UNICODE
+ SubstituteStrings[0] = NetpAllocStrFromWStr(PrintQueue);
+ if (SubstituteStrings[0] == NULL) {
+ return;
+ }
+#else
+ SubstituteStrings[0] = PrintQueue;
+#endif
+
+ SubstituteStrings[1] = TimeAndDate;
+
+ AlAppendMessage(
+ APE2_ALERTER_QUEUEDTO,
+ MessageBuffer,
+ SubstituteStrings,
+ 2,
+ FALSE
+ );
+
+#ifdef UNICODE
+ NetApiBufferFree(SubstituteStrings[0]);
+#endif
+
+ //
+ // Format size of print job
+ //
+ if (PrintInfo->alrtpr_size != MAXULONG) {
+
+ _ltoa(PrintInfo->alrtpr_size, TempString, 10);
+ SubstituteStrings[0] = TempString;
+
+ AlAppendMessage(
+ APE2_ALERTER_SIZE,
+ MessageBuffer,
+ SubstituteStrings,
+ 1,
+ FALSE
+ );
+ }
+}
+
+
+
+STATIC
+NET_API_STATUS
+AlSendMessage(
+ IN LPTSTR MessageAlias
+ )
+/*++
+
+Routine Description:
+
+ This function sends the message in MessageBuffer to the specified
+ message alias. If an error occurs while sending the message, it will
+ be logged to the error log file.
+
+Arguments:
+
+ MessageAlias - Supplies the message alias of recipient of the message.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ static NET_API_STATUS PreviousStatus = NERR_Success;
+ NET_API_STATUS Status;
+
+ LPWSTR MessageW;
+#ifdef DBCS // AlSendMessage():
+ // fixes alterts to DOS clients and garbage in NT to NT altert message
+ DWORD MessageSize;
+#else
+ DWORD MessageSize = strlen(MessageBuffer) * sizeof(WCHAR);
+#endif // DBCS
+
+ MessageW = NetpAllocWStrFromStr(MessageBuffer);
+ if (MessageW == NULL) {
+ NetpKdPrint(("[Alerter] AlSendMessage: NetpAllocWStrFromStr failed\n"));
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+#ifdef DBCS // AlSendMessage():
+ // fixes alterts to DOS clients and garbage in NT to NT altert message
+ MessageSize = wcslen( MessageW ) * sizeof(WCHAR) ;
+#endif // DBCS
+ //
+ // Send a directed message to the specified message alias
+ //
+
+ IF_DEBUG(FORMAT) {
+ NetpKdPrint(("\n\nMessage To " FORMAT_LPTSTR "\n\n", MessageAlias));
+ NetpDbgHexDump((LPBYTE) MessageW, MessageSize);
+ }
+
+ if ((Status = NetMessageBufferSend(
+ NULL,
+ MessageAlias,
+ AlLocalComputerNameW,
+ (LPBYTE) MessageW,
+ MessageSize
+ )) != NERR_Success) {
+
+ NetpKdPrint(("[Alerter] Error sending message to "
+ FORMAT_LPTSTR " %lu\n", MessageAlias, Status));
+
+ if (Status != NERR_NameNotFound &&
+ Status != NERR_BadReceive &&
+ Status != NERR_UnknownServer &&
+ Status != PreviousStatus) {
+
+ AlHandleError(AlErrorSendMessage, Status, MessageAlias);
+ PreviousStatus = Status;
+ }
+
+ }
+
+ NetApiBufferFree(MessageW);
+
+ return Status;
+}
+
+
+
+STATIC
+NET_API_STATUS
+AlAppendMessage(
+ IN DWORD MessageId,
+ OUT LPSTR MessageBuffer,
+ IN LPSTR *SubstituteStrings,
+ IN DWORD NumberOfSubstituteStrings,
+ IN BOOL StripLead
+ )
+/*++
+
+Routine Description:
+
+ This function gets the message, as specified by the message id, from a
+ predetermined message file. It then appends the message to the
+ MessageBuffer.
+
+Arguments:
+
+ MessageId - Supplies the message id of message to get from message file.
+
+ MessageBuffer - Supplies a pointer to the buffer which the message is
+ appended to.
+
+ SubstituteStrings - Supplies an array of strings to substitute into the
+ message.
+
+ NumberOfSubstituteStrings - Supplies the number of strings in array of
+ SunstituteStrings
+
+ StripLead - Supplies a flag which if TRUE indicates to strip the first
+ string and the spaces that follow the it before appending the message
+ to the output message buffer.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ WORD MessageLength = 0;
+ NET_API_STATUS status;
+
+ LPBYTE RetrievedMessage;
+ LPBYTE ResultMessage = NULL;
+
+
+ //
+ // Allocate memory for the buffer which receives the message from the
+ // message file.
+ //
+ if ((RetrievedMessage = (LPBYTE) LocalAlloc(
+ 0,
+ MAX_ALERTER_MESSAGE_SIZE+1
+ )) == NULL) {
+ return GetLastError();
+ }
+
+
+ if ((status = (NET_API_STATUS) DosGetMessage(
+ SubstituteStrings,
+ (WORD) NumberOfSubstituteStrings,
+ RetrievedMessage,
+ MAX_ALERTER_MESSAGE_SIZE,
+ (WORD) MessageId,
+ MESSAGE_FILENAME,
+ &MessageLength
+ )) != 0) {
+ goto CleanUp;
+ }
+
+ RetrievedMessage[MessageLength] = AL_NULL_CHAR;
+
+ //
+ // If StripLead is TRUE, we want to remove the leading string and the
+ // spaces that follow it. This functionality is mainly for stripping the
+ // leading "NETxxxx: " on a message so that the same message can be used
+ // for displaying a network error as well as in an alert notification.
+ //
+ if (StripLead) {
+
+ /* BUGBUG: Currently all messages don't have the "NETxxxx:" word
+ in the message file to strip
+
+ //
+ // Find first space
+ //
+ ResultMessage = strchr(RetrievedMessage, AL_SPACE_CHAR);
+
+ //
+ // Skip spaces
+ //
+ if (ResultMessage != NULL) {
+ ResultMessage += strspn(ResultMessage, " ") ;
+ }
+ */
+ }
+
+ //
+ // Set the result message to the beginning of message if there are no
+ // spaces in the message or StripLead is FALSE.
+ //
+ if (ResultMessage == NULL) {
+ ResultMessage = RetrievedMessage;
+ }
+
+ strcat(MessageBuffer, ResultMessage);
+
+CleanUp:
+ (void) LocalFree(RetrievedMessage);
+
+ return status;
+}
+
+
+STATIC
+BOOL
+IsDuplicateReceiver(
+ LPTSTR Name
+ )
+/*++
+
+Routine Description:
+
+ This function compares the specified name with the names on the alert
+ names list and returns TRUE if there is a match; FALSE otherwise.
+
+Arguments:
+
+ Name - Supplies a name to compare with the alert names list.
+
+Return Value:
+
+ TRUE if match any name; FALSE otherwise.
+
+--*/
+{
+ LPTSTR AdminToAlert = AlertNamesW;
+
+
+ while (AdminToAlert != NULL && *AdminToAlert != TCHAR_EOS) {
+
+ if (STRICMP(Name, AdminToAlert) == 0) {
+ return TRUE;
+ }
+
+ AdminToAlert = STRCHR(AdminToAlert, TCHAR_EOS);
+ AdminToAlert++;
+ }
+
+ return FALSE;
+}
+
+
+VOID
+AlFormatErrorMessage(
+ IN NET_API_STATUS Status,
+ IN LPTSTR MessageAlias,
+ OUT LPTSTR ErrorMessage,
+ IN DWORD ErrorMessageBufferSize
+ )
+/*++
+
+Routine Description:
+
+ This function formats 3 strings and place them, NULL separated, into
+ the supplied ErrorMessage buffer. The strings appear in the following
+ order:
+ Status
+ MessageAlias
+ Message which was not send
+
+Arguments:
+
+ Status - Supplies the status code of the error.
+
+ MessageAlias - Supplies the message alias of the intended recipient.
+
+ ErrorMessage - Returns the formatted error message in this buffer.
+
+ ErrorMessageBufferSize - Supplies the size of the error message buffer
+ in bytes.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ LPTSTR MessageBufferPointer;
+ LPTSTR MBPtr;
+ LPTSTR MBPtr2;
+
+ DWORD SizeOfString;
+
+ LPTSTR MessageBufferW;
+
+ CHAR MessageBufferTmp[MAX_ALERTER_MESSAGE_SIZE];
+
+
+ RtlZeroMemory(ErrorMessage, ErrorMessageBufferSize);
+
+ //
+ // Don't muck with the actual message buffer itself because it
+ // may still be in use.
+ //
+ strcpy(MessageBufferTmp, MessageBuffer);
+
+ //
+ // Put status in error message buffer
+ //
+#ifdef UNICODE
+ ultow(Status, ErrorMessage, 10);
+#else
+ _ltoa(Status, ErrorMessage, 10);
+#endif
+
+ MBPtr = &ErrorMessage[STRLEN(ErrorMessage) + 1];
+
+ //
+ // Put message alias in error message buffer
+ //
+ STRCPY(MBPtr, MessageAlias);
+ MBPtr += (STRLEN(MessageAlias) + 1);
+
+ //
+ // Put the message that failed to send in error message buffer
+ //
+
+#ifdef UNICODE
+ MessageBufferW = NetpAllocWStrFromStr(MessageBufferTmp);
+ if (MessageBufferW == NULL) {
+ return;
+ }
+#else
+ MessageBufferW = MessageBufferTmp;
+#endif
+
+ MessageBufferPointer = MessageBufferW;
+
+ while (MBPtr2 = STRCHR(MessageBufferPointer, AL_EOL_WCHAR)) {
+
+ *MBPtr2++ = TCHAR_EOS;
+ SizeOfString = (DWORD) ((LPBYTE)MBPtr2 - (LPBYTE)MessageBufferPointer) + sizeof(TCHAR);
+
+ //
+ // Check for error message buffer overflow
+ //
+ if ((LPBYTE)MBPtr - (LPBYTE)ErrorMessage + SizeOfString >=
+ ErrorMessageBufferSize) {
+ break;
+ }
+
+ STRCPY(MBPtr, MessageBufferPointer);
+ STRCAT(MBPtr, AL_CRLF_STRING);
+ MBPtr += SizeOfString / sizeof(TCHAR);
+
+ MessageBufferPointer = MBPtr2;
+ }
+
+ if (((LPBYTE)MBPtr - (LPBYTE)ErrorMessage +
+ STRLEN(MessageBufferPointer) * sizeof(TCHAR)) >
+ ErrorMessageBufferSize) {
+
+ //
+ // Put as much info into the error message buffer as possible
+ //
+ STRNCPY(MBPtr,
+ MessageBufferPointer,
+ ErrorMessageBufferSize/sizeof(TCHAR) - (MBPtr - ErrorMessage));
+ ErrorMessage[(ErrorMessageBufferSize/sizeof(TCHAR))-1] = TCHAR_EOS;
+
+ } else {
+ STRCPY(MBPtr, MessageBufferPointer);
+ }
+
+#ifdef UNICODE
+ NetApiBufferFree(MessageBufferW);
+#endif
+}
+
+
+
+NET_API_STATUS
+AlCanonicalizeMessageAlias(
+ LPTSTR MessageAlias
+ )
+/*++
+
+Routine Description:
+
+ This function canonicalizes the message alias by calling
+ I_NetNameCanonicalize.
+
+Arguments:
+
+ MessageAlias - Supplies the message alias of an intended recipient for
+ the alert message.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NET_API_STATUS Status;
+
+
+ //
+ // Canonicalize message alias which will receive message
+ //
+ Status = I_NetNameCanonicalize(
+ NULL,
+ MessageAlias,
+ MessageAlias,
+ (STRLEN(MessageAlias) + 1) * sizeof(TCHAR),
+ NAMETYPE_USER,
+ 0
+ );
+
+ if (Status != NERR_Success) {
+
+ NetpKdPrint(("[Alerter] Error canonicalizing message alias " FORMAT_LPTSTR " %lu\n",
+ MessageAlias, Status));
+ AlHandleError(AlErrorSendMessage, Status, MessageAlias);
+ }
+
+ return Status;
+}
diff --git a/private/net/svcdlls/alrsvc/alformat.h b/private/net/svcdlls/alrsvc/alformat.h
new file mode 100644
index 000000000..e92064292
--- /dev/null
+++ b/private/net/svcdlls/alrsvc/alformat.h
@@ -0,0 +1,54 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ alformat.h
+
+Abstract:
+
+ Private header file which defines the values used for formatting alert
+ messages.
+
+Author:
+
+ Rita Wong (ritaw) 09-July-1991
+
+Revision History:
+
+--*/
+
+#ifndef _ALFORMAT_INCLUDED_
+#define _ALFORMAT_INCLUDED_
+
+#include "al.h" // Common include file for Alerter service
+#include <lmmsg.h> // NetMessageBufferSend
+
+#include <alertmsg.h>
+#include <apperr2.h>
+
+#include <timelib.h> // time functions in netlib
+
+//
+// Maximum size of a message send by the Alerter service in number of bytes
+//
+#define MAX_ALERTER_MESSAGE_SIZE 600
+
+#define FILENAME_SIZE 128
+
+//
+// Maximum message width. Text will wrap around if exceed this width.
+//
+#define MESSAGE_WIDTH 55
+
+#define NO_MESSAGE MAXULONG
+
+#define AL_CR_CHAR '\r'
+#define AL_EOL_CHAR '\024' // hex 14, decimal 20, USE \024 !
+#define AL_EOL_WCHAR TEXT('\024') // hex 14, decimal 20, USE \024 !
+#define AL_EOL_STRING "\024"
+#define AL_CRLF_STRING TEXT("\r\n")
+
+
+#endif // ifndef _ALFORMAT_INCLUDED_
diff --git a/private/net/svcdlls/alrsvc/almain.c b/private/net/svcdlls/alrsvc/almain.c
new file mode 100644
index 000000000..240ccd692
--- /dev/null
+++ b/private/net/svcdlls/alrsvc/almain.c
@@ -0,0 +1,721 @@
+/*++
+
+Copyright (c) 1990 Microsoft Corporation
+
+Module Name:
+
+ almain.c
+
+Abstract:
+
+ This is the main routine for the NT LAN Manager Alerter service
+
+Author:
+
+ Rita Wong (ritaw) 01-July-1991
+
+Environment:
+
+ User Mode - Win32
+
+Revision History:
+
+--*/
+
+#include "almain.h" // Main module definitions
+
+#include <services.h> // LMSVCS_ENTRY_POINT
+#include <secobj.h> // ACE_DATA
+
+//-------------------------------------------------------------------//
+// //
+// Global variables //
+// //
+//-------------------------------------------------------------------//
+
+AL_GLOBAL_DATA AlGlobalData;
+PLMSVCS_GLOBAL_DATA AlLmsvcsGlobalData;
+
+STATIC BOOL AlDone = FALSE;
+
+//
+// Debug trace flag for selecting which trace statements to output
+//
+#if DBG
+
+DWORD AlerterTrace = 0;
+
+#endif
+
+
+//-------------------------------------------------------------------//
+// //
+// Function prototypes //
+// //
+//-------------------------------------------------------------------//
+
+STATIC
+NET_API_STATUS
+AlInitializeAlerter(
+ VOID
+ );
+
+STATIC
+VOID
+AlProcessAlertNotification(
+ VOID
+ );
+
+STATIC
+VOID
+AlShutdownAlerter(
+ IN NET_API_STATUS ErrorCode
+ );
+
+STATIC
+NET_API_STATUS
+AlUpdateStatus(
+ VOID
+ );
+
+VOID
+AlerterControlHandler(
+ IN DWORD Opcode
+ );
+
+
+
+VOID
+LMSVCS_ENTRY_POINT( // (ALERTER_main)
+ DWORD NumArgs,
+ LPTSTR *ArgsArray,
+ PLMSVCS_GLOBAL_DATA LmsvcsGlobalData,
+ HANDLE SvcRefHandle
+ )
+/*++
+
+Routine Description:
+
+ This is the main routine of the Alerter Service which registers
+ itself as an RPC server and notifies the Service Controller of the
+ Alerter service control entry point.
+
+Arguments:
+
+ NumArgs - Supplies the number of strings specified in ArgsArray.
+
+ ArgsArray - Supplies string arguments that are specified in the
+ StartService API call. This parameter is ignored by the
+ Alerter service.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ DWORD AlInitState = 0;
+
+
+ UNREFERENCED_PARAMETER(NumArgs);
+ UNREFERENCED_PARAMETER(ArgsArray);
+ UNREFERENCED_PARAMETER(SvcRefHandle);
+
+ //
+ // Save the LMSVCS global data for future use.
+ //
+ AlLmsvcsGlobalData = LmsvcsGlobalData;
+
+ IF_DEBUG(MAIN) {
+ NetpKdPrint(("In the alerter service!!\n"));
+ }
+
+ AlDone = FALSE;
+
+ if (AlInitializeAlerter() != NERR_Success) {
+ return;
+ }
+
+ AlProcessAlertNotification();
+
+ return;
+}
+
+
+STATIC
+NET_API_STATUS
+AlInitializeAlerter(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This routine initializes the Alerter service.
+
+Arguments:
+
+ AlInitState - Returns a flag to indicate how far we got with initializing
+ the Alerter service before an error occured.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NET_API_STATUS status;
+ NTSTATUS ntstatus;
+ PSECURITY_DESCRIPTOR Sd;
+ SECURITY_ATTRIBUTES Sa;
+ ACE_DATA AceData[1] = {
+ {ACCESS_ALLOWED_ACE_TYPE, 0, 0,
+ GENERIC_READ | GENERIC_WRITE, &AlLmsvcsGlobalData->WorldSid}
+ };
+
+
+
+ AlGlobalData.MailslotHandle = (HANDLE) MAXULONG;
+
+ //
+ // Initialize Alerter to receive service requests by registering the
+ // control handler.
+ //
+ if ((AlGlobalData.StatusHandle = RegisterServiceCtrlHandler(
+ SERVICE_ALERTER,
+ AlerterControlHandler
+ )) == (SERVICE_STATUS_HANDLE) NULL) {
+
+ status = GetLastError();
+ AlHandleError(AlErrorRegisterControlHandler, status, NULL);
+ return status;
+ }
+
+ //
+ // Initialize all the status fields so that subsequent calls to
+ // SetServiceStatus need to only update fields that changed.
+ //
+ AlGlobalData.Status.dwServiceType = SERVICE_WIN32;
+ AlGlobalData.Status.dwCurrentState = SERVICE_START_PENDING;
+ AlGlobalData.Status.dwControlsAccepted = 0;
+ AlGlobalData.Status.dwCheckPoint = 1;
+ AlGlobalData.Status.dwWaitHint = 10000; // 10 secs
+
+ SET_SERVICE_EXITCODE(
+ NO_ERROR,
+ AlGlobalData.Status.dwWin32ExitCode,
+ AlGlobalData.Status.dwServiceSpecificExitCode
+ );
+
+ //
+ // Tell the Service Controller that we are start-pending
+ //
+ if ((status = AlUpdateStatus()) != NERR_Success) {
+
+ AlHandleError(AlErrorNotifyServiceController, status, NULL);
+ return status;
+ }
+
+ //
+ // Get the configured alert names
+ //
+ if ((status = AlGetAlerterConfiguration()) != NERR_Success) {
+
+ AlHandleError(AlErrorGetComputerName, status, NULL);
+ return status;
+ }
+
+ //
+ // Create the security descriptor for the security attributes structure
+ //
+ ntstatus = NetpCreateSecurityDescriptor(
+ AceData,
+ 1,
+ AlLmsvcsGlobalData->LocalSystemSid,
+ AlLmsvcsGlobalData->LocalSystemSid,
+ &Sd
+ );
+
+ if (! NT_SUCCESS(ntstatus)) {
+ status = NetpNtStatusToApiStatus(ntstatus);
+ AlHandleError(AlErrorCreateMailslot, status, NULL);
+ return status;
+ }
+
+ Sa.nLength = sizeof(SECURITY_ATTRIBUTES);
+ Sa.lpSecurityDescriptor = Sd;
+ Sa.bInheritHandle = FALSE;
+
+ //
+ // Create mailslot to listen on alert notifications from the Server
+ // service and the Spooler.
+ //
+ AlGlobalData.MailslotHandle = CreateMailslot(
+ ALERTER_MAILSLOT,
+ MAX_MAILSLOT_MESSAGE_SIZE,
+ MAILSLOT_WAIT_FOREVER,
+ &Sa
+ );
+
+ NetpMemoryFree(Sd);
+
+ if (AlGlobalData.MailslotHandle == (HANDLE) MAXULONG) {
+ status = GetLastError();
+ AlHandleError(AlErrorCreateMailslot, status, NULL);
+ return status;
+ }
+ else {
+ IF_DEBUG(MAIN) {
+ NetpKdPrint(("Mailslot %ws created, handle=x%08lx\n",
+ ALERTER_MAILSLOT, AlGlobalData.MailslotHandle));
+ }
+ }
+
+ //
+ // Tell the Service Controller that we are started.
+ //
+ AlGlobalData.Status.dwCurrentState = SERVICE_RUNNING;
+ AlGlobalData.Status.dwControlsAccepted = SERVICE_ACCEPT_STOP;
+ AlGlobalData.Status.dwCheckPoint = 0;
+ AlGlobalData.Status.dwWaitHint = 0;
+
+ if ((status = AlUpdateStatus()) != NERR_Success) {
+
+ AlHandleError(AlErrorNotifyServiceController, status, NULL);
+ return status;
+ }
+
+ IF_DEBUG(MAIN) {
+ NetpKdPrint(("[Alerter] Successful Initialization\n"));
+ }
+
+ return NERR_Success;
+}
+
+
+STATIC
+VOID
+AlProcessAlertNotification(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This routine processes incoming mailslot alert notifications, which is
+ the core function of the Alerter service.
+
+Arguments:
+
+ AlUicCode - Supplies the termination code to the Service Controller.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ NET_API_STATUS status = NERR_Success;
+ TCHAR AlertMailslotBuffer[MAX_MAILSLOT_MESSAGE_SIZE];
+ DWORD NumberOfBytesRead;
+
+ PSTD_ALERT Alert;
+
+
+ //
+ // Loop reading the Alerter mailslot; it will terminate when the mailslot
+ // is destroyed by closing the one and only handle to it.
+ //
+ do {
+
+ //
+ // Zero out the buffer before getting a new alert notification
+ //
+ RtlZeroMemory(AlertMailslotBuffer, MAX_MAILSLOT_MESSAGE_SIZE *
+ sizeof(TCHAR));
+
+ if (ReadFile(
+ AlGlobalData.MailslotHandle,
+ (LPVOID) AlertMailslotBuffer,
+ MAX_MAILSLOT_MESSAGE_SIZE * sizeof(TCHAR),
+ &NumberOfBytesRead,
+ NULL
+ ) == FALSE) {
+
+ //
+ // Failed in reading mailslot
+ //
+ status = GetLastError();
+
+ if (status == ERROR_HANDLE_EOF) {
+ while (! AlDone) {
+ Sleep(2000);
+ }
+ return;
+ }
+
+ NetpKdPrint(("[Alerter] Error reading from mailslot %lu\n", status));
+ }
+ else {
+
+ //
+ // Successfully received a mailslot alert notification
+ //
+
+ IF_DEBUG(MAIN) {
+ NetpKdPrint(("[Alerter] Successfully read %lu bytes from mailslot\n",
+ NumberOfBytesRead));
+ }
+
+ try {
+
+ //
+ // Handle alert notification for admin, print, and user alerts.
+ //
+ Alert = (PSTD_ALERT) AlertMailslotBuffer;
+
+ if (! I_NetNameCompare(
+ NULL,
+ Alert->alrt_eventname,
+ ALERT_ADMIN_EVENT,
+ NAMETYPE_EVENT,
+ 0
+ )) {
+
+ AlAdminFormatAndSend(Alert);
+ }
+ else if (! I_NetNameCompare(
+ NULL,
+ Alert->alrt_eventname,
+ ALERT_PRINT_EVENT,
+ NAMETYPE_EVENT,
+ 0
+ )) {
+
+ AlPrintFormatAndSend(Alert);
+ }
+ else if (! I_NetNameCompare(
+ NULL,
+ Alert->alrt_eventname,
+ ALERT_USER_EVENT,
+ NAMETYPE_EVENT,
+ 0L
+ )) {
+
+ AlUserFormatAndSend(Alert);
+ }
+
+ }
+ except (EXCEPTION_EXECUTE_HANDLER) {
+ NetpKdPrint(("[Alerter] Exception occurred processing alerts\n"));
+ }
+ }
+
+ } while (TRUE);
+
+}
+
+
+STATIC
+VOID
+AlShutdownAlerter(
+ IN NET_API_STATUS ErrorCode
+ )
+/*++
+
+Routine Description:
+
+ This routine shuts down the Alerter service.
+
+Arguments:
+
+ ErrorCode - Supplies the error for terminating the Alerter service.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ //
+ // Free memory allocated to hold the computer name
+ //
+ if (AlLocalComputerNameA != NULL) {
+ (void) NetApiBufferFree(AlLocalComputerNameA);
+ AlLocalComputerNameA = NULL;
+ }
+ if (AlLocalComputerNameW != NULL) {
+ (void) NetApiBufferFree(AlLocalComputerNameW);
+ AlLocalComputerNameW = NULL;
+ }
+
+ //
+ // Free memory allocated for alert names
+ //
+ if (AlertNamesA != NULL) {
+ (void) LocalFree(AlertNamesA);
+ AlertNamesA = NULL;
+ }
+ if (AlertNamesW != NULL) {
+ (void) NetApiBufferFree(AlertNamesW);
+ AlertNamesW = NULL;
+ }
+
+ //
+ // Destroy Alerter mailslot if created.
+ //
+ if (AlGlobalData.MailslotHandle != (HANDLE) MAXULONG) {
+
+ if (! CloseHandle(AlGlobalData.MailslotHandle)) {
+ NetpKdPrint(("[Alerter]] Could not remove mailslot %lu\n",
+ GetLastError()));
+ }
+
+ AlGlobalData.MailslotHandle = (HANDLE) MAXULONG;
+ }
+
+ //
+ // We are done with cleaning up. Tell Service Controller that we are
+ // stopped.
+ //
+ AlGlobalData.Status.dwCurrentState = SERVICE_STOPPED;
+ AlGlobalData.Status.dwCheckPoint = 0;
+ AlGlobalData.Status.dwWaitHint = 0;
+
+ SET_SERVICE_EXITCODE(
+ ErrorCode,
+ AlGlobalData.Status.dwWin32ExitCode,
+ AlGlobalData.Status.dwServiceSpecificExitCode
+ );
+
+ (void) AlUpdateStatus();
+
+ AlDone = TRUE;
+}
+
+
+VOID
+AlHandleError(
+ IN AL_ERROR_CONDITION FailingCondition,
+ IN NET_API_STATUS Status,
+ IN LPTSTR MessageAlias OPTIONAL
+ )
+/*++
+
+Routine Description:
+
+ This routine handles a Alerter service error condition. I*f the error
+ condition is fatal, then it shuts down the Alerter service.
+
+Arguments:
+
+ FailingCondition - Supplies a value which indicates what the failure is.
+
+ Status - Supplies the status code for the failure.
+
+ MessageAlias - Supplies the message alias name which the alert message
+ failed in sending. This only applies to the message send error.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ LPWSTR SubString[3];
+ TCHAR StatusString[STRINGS_MAXIMUM + 1];
+ DWORD NumberOfStrings;
+
+ switch (FailingCondition) {
+
+ case AlErrorRegisterControlHandler:
+
+ NetpKdPrint(("[Alerter] Cannot register control handler "
+ FORMAT_API_STATUS "\n", Status));
+
+ SubString[0] = ultow(Status, StatusString, 10);
+ AlLogEvent(
+ NELOG_FailedToRegisterSC,
+ 1,
+ SubString
+ );
+
+ AlShutdownAlerter(Status);
+ break;
+
+ case AlErrorCreateMailslot:
+
+ NetpKdPrint(("[Alerter] Cannot create mailslot " FORMAT_API_STATUS "\n",
+ Status));
+ SubString[0] = ultow(Status, StatusString, 10);
+ AlLogEvent(
+ NELOG_Mail_Slt_Err,
+ 1,
+ SubString
+ );
+
+ AlShutdownAlerter(Status);
+ break;
+
+ case AlErrorNotifyServiceController:
+
+ NetpKdPrint(("[Alerter] SetServiceStatus error %lu\n", Status));
+
+ SubString[0] = ultow(Status, StatusString, 10);
+ AlLogEvent(
+ NELOG_FailedToSetServiceStatus,
+ 1,
+ SubString
+ );
+
+ AlShutdownAlerter(Status);
+ break;
+
+ case AlErrorGetComputerName:
+
+ NetpKdPrint(("[Alerter] Error in getting computer name %lu.\n", Status));
+
+ SubString[0] = ultow(Status, StatusString, 10);
+ AlLogEvent(
+ NELOG_FailedToGetComputerName,
+ 1,
+ SubString
+ );
+
+ AlShutdownAlerter(Status);
+ break;
+
+ case AlErrorSendMessage :
+
+ AlFormatErrorMessage(
+ Status,
+ MessageAlias,
+ StatusString,
+ (STRINGS_MAXIMUM + 1) * sizeof(TCHAR)
+ );
+
+ SubString[0] = StatusString;
+ SubString[1] = StatusString + STRLEN(StatusString) + 1;
+ SubString[2] = SubString[1] + STRLEN(SubString[1]) + 1;
+
+ AlLogEvent(
+ NELOG_Message_Send,
+ 3,
+ SubString
+ );
+
+ break;
+
+ default:
+ NetpKdPrint(("[Alerter] AlHandleError: unknown error condition %lu\n",
+ FailingCondition));
+
+ NetpAssert(FALSE);
+ }
+
+}
+
+
+STATIC
+NET_API_STATUS
+AlUpdateStatus(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This routine updates the Alerter service status with the Service
+ Controller.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NET_API_STATUS status = NERR_Success;
+
+
+ if (AlGlobalData.StatusHandle == (SERVICE_STATUS_HANDLE) NULL) {
+ NetpKdPrint((
+ "[Alerter] Cannot call SetServiceStatus, no status handle.\n"
+ ));
+
+ return ERROR_INVALID_HANDLE;
+ }
+
+ if (! SetServiceStatus(AlGlobalData.StatusHandle, &AlGlobalData.Status)) {
+
+ status = GetLastError();
+
+ IF_DEBUG(MAIN) {
+ NetpKdPrint(("[Alerter] SetServiceStatus error %lu\n", status));
+ }
+ }
+
+ return status;
+}
+
+
+
+VOID
+AlerterControlHandler(
+ IN DWORD Opcode
+ )
+/*++
+
+Routine Description:
+
+ This is the service control handler of the Alerter service.
+
+Arguments:
+
+ Opcode - Supplies a value which specifies the action for the Alerter
+ service to perform.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ IF_DEBUG(MAIN) {
+ NetpKdPrint(("[Alerter] In Control Handler\n"));
+ }
+
+ switch (Opcode) {
+
+ case SERVICE_CONTROL_STOP:
+
+ if (AlGlobalData.Status.dwCurrentState != SERVICE_STOP_PENDING) {
+
+ IF_DEBUG(MAIN) {
+ NetpKdPrint(("[Alerter] Stopping alerter...\n"));
+ }
+
+ AlShutdownAlerter(NERR_Success);
+
+ }
+
+ return;
+
+ case SERVICE_CONTROL_INTERROGATE:
+ break;
+
+ default:
+ IF_DEBUG(MAIN) {
+ NetpKdPrint(("Unknown alerter opcode " FORMAT_HEX_DWORD
+ "\n", Opcode));
+ }
+ }
+
+ //
+ // Send the status response.
+ //
+ (void) AlUpdateStatus();
+}
diff --git a/private/net/svcdlls/alrsvc/almain.h b/private/net/svcdlls/alrsvc/almain.h
new file mode 100644
index 000000000..1df994aa0
--- /dev/null
+++ b/private/net/svcdlls/alrsvc/almain.h
@@ -0,0 +1,85 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ almain.h
+
+Abstract:
+
+ Private header file which defines the global data used for communication
+ between the service control handler and the rest of the Alerter service.
+
+Author:
+
+ Rita Wong (ritaw) 01-July-1991
+
+Revision History:
+
+--*/
+
+#ifndef _ALMAIN_INCLUDED_
+#define _ALMAIN_INCLUDED_
+
+#include "al.h" // Common include file for Alerter service
+#include <winsvc.h> // Service control APIs
+#include <lmsname.h> // SERVICE_ALERTER
+#include <lmerrlog.h> // Error log codes
+
+//
+// Maximum incoming mailslot message size in number of bytes
+//
+#define MAX_MAILSLOT_MESSAGE_SIZE 512
+
+//
+// Size of error message buffer
+//
+#define STRINGS_MAXIMUM 256
+
+//
+// Call AlHandleError with the appropriate error condition
+//
+#define AL_HANDLE_ERROR(ErrorCondition) \
+ AlHandleError( \
+ ErrorCondition, \
+ status \
+ );
+
+//
+// Call AlShutdownWorkstation with the appropriate termination code
+//
+#define AL_SHUTDOWN_WORKSTATION(TerminationCode) \
+ AlShutdownWorkstation( \
+ TerminationCode \
+ );
+
+
+//-------------------------------------------------------------------//
+// //
+// Type definitions //
+// //
+//-------------------------------------------------------------------//
+
+typedef struct _AL_GLOBAL_DATA {
+
+ //
+ // Alerter service status
+ //
+ SERVICE_STATUS Status;
+
+ //
+ // Handle to set service status
+ //
+ SERVICE_STATUS_HANDLE StatusHandle;
+
+ //
+ // Handle to the Alerter service mailslot which receives alert
+ // notifications from the Server service and Spooler
+ //
+ HANDLE MailslotHandle;
+
+} AL_GLOBAL_DATA, *PAL_GLOBAL_DATA;
+
+
+#endif // ifndef _ALMAIN_INCLUDED_
diff --git a/private/net/svcdlls/alrsvc/alrsvc.def b/private/net/svcdlls/alrsvc/alrsvc.def
new file mode 100644
index 000000000..df3cd1883
--- /dev/null
+++ b/private/net/svcdlls/alrsvc/alrsvc.def
@@ -0,0 +1,6 @@
+NAME alrsvc.dll
+
+DESCRIPTION 'NT LAN Manager Alerter Service'
+
+EXPORTS
+ ServiceEntry
diff --git a/private/net/svcdlls/alrsvc/alrsvc.rc b/private/net/svcdlls/alrsvc/alrsvc.rc
new file mode 100644
index 000000000..7e9effcac
--- /dev/null
+++ b/private/net/svcdlls/alrsvc/alrsvc.rc
@@ -0,0 +1,12 @@
+#include <windows.h>
+
+#include <ntverp.h>
+
+#define VER_FILETYPE VFT_DLL
+#define VER_FILESUBTYPE VFT2_UNKNOWN
+#define VER_FILEDESCRIPTION_STR "Alerter Service DLL"
+#define VER_INTERNALNAME_STR "ALRSVC.DLL"
+#define VER_ORIGINALFILENAME_STR "ALRSVC.DLL"
+
+#include "common.ver"
+
diff --git a/private/net/svcdlls/alrsvc/altest.c b/private/net/svcdlls/alrsvc/altest.c
new file mode 100644
index 000000000..c0b11bd5e
--- /dev/null
+++ b/private/net/svcdlls/alrsvc/altest.c
@@ -0,0 +1,309 @@
+/*++
+
+Copyright (c) 1991-92 Microsoft Corporation
+
+Module Name:
+
+ altest.c
+
+Abstract:
+
+ Test program for the Alerter service.
+
+Author:
+
+ Rita Wong (ritaw) 17-July-1991
+
+Revision History:
+
+--*/
+
+#include <stdio.h>
+#include <string.h>
+
+#include <nt.h> // NT definitions
+#include <ntrtl.h> // NT runtime library definitions
+#include <nturtl.h>
+
+#include <windef.h> // Win32 type definitions
+#include <winbase.h> // Win32 base API prototypes
+
+#include <alertmsg.h> // ALERT_ equates.
+#include <lmcons.h> // LAN Manager common definitions
+#include <lmalert.h> // LAN Manager alert structures and APIs
+#include <lmerr.h> // LAN Manager network error definitions
+#include <netdebug.h> // FORMAT_ equates.
+
+#include <tstring.h> // Transitional string functions
+#include <conio.h>
+#include <time.h>
+//#include "lmspool.h"
+
+//
+// Global buffer
+//
+TCHAR VariableInfo[1024];
+
+
+//------------------------------------------------------------------------//
+
+VOID _CRTAPI1
+main(
+ int argc,
+ char *argv[]
+ )
+{
+ NET_API_STATUS ApiStatus;
+ PADMIN_OTHER_INFO Admin;
+ PUSER_OTHER_INFO User;
+ PPRINT_OTHER_INFO Print;
+ LPWSTR pString;
+ DWORD dwTime;
+
+ DWORD VariableInfoSize;
+ DWORD TmpSize; // Size of var portion plus 1 mergestring
+
+ CHAR response;
+ DWORD i;
+
+ UNREFERENCED_PARAMETER(argc);
+ UNREFERENCED_PARAMETER(argv);
+
+ (VOID) printf( "AlTest: starting up...\n" );
+
+ //
+ // Alerter service should not crash if username and computer name are
+ // not part of the mailslot message
+ //
+ User = (PUSER_OTHER_INFO) VariableInfo;
+ User->alrtus_errcode = ALERT_CloseBehindError;
+ User->alrtus_numstrings = 1;
+
+ VariableInfoSize = sizeof(USER_OTHER_INFO);
+
+ //
+ // Write the mergestring into buffer. In the case of a close behind error
+ // it is the name of the file which failed to close.
+ //
+#define FILENAME TEXT("PLUTO.TXT")
+ STRCPY((LPTSTR) ((DWORD) User + VariableInfoSize), FILENAME);
+ VariableInfoSize += ((STRLEN(FILENAME) + 1) * sizeof(TCHAR));
+
+ TmpSize = VariableInfoSize;
+
+ //
+ // This should not cause a message to be sent because neither the user
+ // nor computer name is specified.
+ //
+ ApiStatus = NetAlertRaise(
+ ALERT_USER_EVENT, // alert event name
+ VariableInfo,
+ VariableInfoSize );
+
+ (VOID) printf( "AlTest 1(USER_ALERT): done NetAlertRaise, status=" FORMAT_API_STATUS
+ ", expect=" FORMAT_API_STATUS ".\n",
+ ApiStatus, ERROR_INVALID_PARAMETER );
+
+ for (i = 1; i < 3; i ++) {
+
+ VariableInfoSize = TmpSize;
+
+ //
+ // Now include username and computer name
+ //
+#define USERNAME TEXT("DANL")
+
+#define COMPUTERNAME TEXT("danl1")
+
+ STRCPY((LPTSTR) ((DWORD) User + VariableInfoSize), USERNAME);
+ VariableInfoSize += ((STRLEN(USERNAME) + 1) * sizeof(TCHAR));
+
+ //
+ // Put a sequence number at the end of the username
+ //
+ //VariableInfo[STRLEN(USERNAME) - 1] = i + '1';
+
+ STRCPY((LPTSTR) ((DWORD) User + VariableInfoSize), COMPUTERNAME);
+ VariableInfoSize += ((STRLEN(COMPUTERNAME) + 1) * sizeof(TCHAR));
+
+ //
+ // User alert should be raised successfully.
+ //
+ ApiStatus = NetAlertRaiseEx(
+ ALERT_USER_EVENT,
+ VariableInfo,
+ VariableInfoSize,
+ TEXT("SERVER") ); // service name
+
+ (VOID) printf( "AlTest 2(USER_ALERT): done NetAlertRaiseEx, status=" FORMAT_API_STATUS
+ ".\n", ApiStatus );
+
+ }
+
+ //--------------------------
+ // Raise an admin alert
+ // (audit log full)
+ //--------------------------
+ Admin = (PADMIN_OTHER_INFO) VariableInfo;
+ Admin->alrtad_errcode = ALERT_AuditLogFull;
+ Admin->alrtad_numstrings = 0;
+
+ ApiStatus = NetAlertRaiseEx(
+ ALERT_ADMIN_EVENT,
+ VariableInfo,
+ sizeof(ADMIN_OTHER_INFO),
+ TEXT("SERVER") ); // service name
+
+ (VOID) printf( "AlTest 3(ADMIN_ALERT): done NetAlertRaiseEx(admin), status="
+ FORMAT_API_STATUS ".\n", ApiStatus );
+
+ //--------------------------
+ // Raise an admin alert
+ // (user-defined text)
+ //--------------------------
+ Admin = (PADMIN_OTHER_INFO) VariableInfo;
+ Admin->alrtad_errcode = MAXULONG;
+ Admin->alrtad_numstrings = 1;
+ pString = (LPWSTR)((LPBYTE)VariableInfo+sizeof(ADMIN_OTHER_INFO));
+ wcscpy(pString,L"This is a User-Defined Message");
+
+ ApiStatus = NetAlertRaiseEx(
+ ALERT_ADMIN_EVENT,
+ VariableInfo,
+ sizeof(ADMIN_OTHER_INFO)+WCSSIZE(pString),
+ TEXT("SERVER") ); // service name
+
+ (VOID) printf( "AlTest 3.5(ADMIN_ALERT): done NetAlertRaiseEx(admin), status="
+ FORMAT_API_STATUS ".\n", ApiStatus );
+
+ //---------------------------------
+ // Raise an alert (NON-EX version)
+ // (user-defined text)
+ //---------------------------------
+ {
+ LPSTD_ALERT pStdAlert = (LPSTD_ALERT)VariableInfo;
+
+ pStdAlert->alrt_timestamp = 1;
+ wcscpy(pStdAlert->alrt_eventname,ALERT_ADMIN_EVENT);
+ wcscpy(pStdAlert->alrt_servicename, L"Dan'sSvc");
+ Admin = (LPADMIN_OTHER_INFO)((LPBYTE)VariableInfo + sizeof(STD_ALERT));
+ pString = (LPWSTR)((LPBYTE)VariableInfo + sizeof(STD_ALERT) +
+ sizeof(ADMIN_OTHER_INFO));
+ Admin->alrtad_errcode = MAXULONG;
+ Admin->alrtad_numstrings = 1;
+ wcscpy(pString, L"Some User Data");
+ VariableInfoSize = sizeof(STD_ALERT)+ sizeof(ADMIN_OTHER_INFO) +
+ STRSIZE(pString);
+
+ ApiStatus = NetAlertRaise(
+ ALERT_ADMIN_EVENT, // alert event name
+ VariableInfo,
+ VariableInfoSize );
+
+ (VOID) printf( "AlTest 3.6(ADMIN_ALERT): done NetAlertRaise(admin), status="
+ FORMAT_API_STATUS ".\n", ApiStatus );
+
+ }
+ //---------------------------------
+ // PRINT ALERT: PRINTER OFFLINE
+ // (queued for printing)
+ //---------------------------------
+ time((time_t *)&dwTime);
+ printf("time=%d\n",dwTime);
+ Print = (PPRINT_OTHER_INFO) VariableInfo;
+ Print->alrtpr_jobid = 626;
+ Print->alrtpr_status = PRJOB_DESTOFFLINE;
+ Print->alrtpr_submitted = dwTime;
+ Print->alrtpr_size = 72496;
+
+ //
+ // All print Alerts have the PRINT_OTHER_INFO structure
+ // followed by these same strings in this order...
+ //
+ VariableInfoSize = sizeof(PRINT_OTHER_INFO);
+
+ // Computername
+ STRCPY((LPTSTR) ((DWORD) Print + VariableInfoSize), TEXT("DANL2-SHAUNAB"));
+ VariableInfoSize += ((STRLEN(TEXT("DANL2-SHAUNAB")) + 1) * sizeof(TCHAR));
+
+ // username
+ STRCPY((LPTSTR) ((DWORD) Print + VariableInfoSize), TEXT("DANL1"));
+ VariableInfoSize += ((STRLEN(TEXT("DANL1")) + 1) * sizeof(TCHAR));
+
+ // queuename
+ STRCPY((LPTSTR) ((DWORD) Print + VariableInfoSize), TEXT("8/1154 CORPA 14DBDE"));
+ VariableInfoSize += ((STRLEN(TEXT("8/1154 CORPA 14DBDE")) + 1) * sizeof(TCHAR));
+
+ // destination
+ STRCPY((LPTSTR) ((DWORD) Print + VariableInfoSize), TEXT("8/1154 CORPA 14DBDE(CORPA)"));
+ VariableInfoSize += ((STRLEN(TEXT("8/1154 CORPA 14DBDE(CORPA)")) + 1) * sizeof(TCHAR));
+
+ // status string
+ STRCPY((LPTSTR) ((DWORD) Print + VariableInfoSize), TEXT("ERROR"));
+ VariableInfoSize += ((STRLEN(TEXT("ERROR")) + 1) * sizeof(TCHAR));
+
+#ifdef REMOVE
+ ApiStatus = NetAlertRaiseEx(
+ ALERT_PRINT_EVENT,
+ VariableInfo,
+ VariableInfoSize,
+ TEXT("SPOOLER") ); // service name
+
+ (VOID) printf( "AlTest 4(PRINT_ALERT): done NetAlertRaiseEx(print), status="
+ FORMAT_API_STATUS ".\n", ApiStatus );
+
+ //
+ // Wait for user reponse before continuing.
+ //
+ printf("continue?....\n");
+ response = _getch();
+ if ((response == 'n') || (response == 'N')) {
+ return;
+ }
+#endif //REMOVE
+ //---------------------------------------
+ // PRINT ALERT: JOB_COMPLETE
+ //---------------------------------------
+ Print->alrtpr_status = PRJOB_COMPLETE | PRJOB_QS_PRINTING;
+ Print->alrtpr_jobid = 2434;
+
+ ApiStatus = NetAlertRaiseEx(
+ ALERT_PRINT_EVENT,
+ VariableInfo,
+ VariableInfoSize,
+ TEXT("SPOOLER") ); // service name
+
+ (VOID) printf( "AlTest 4.5(PRINT_ALERT): done NetAlertRaiseEx(print), status="
+ FORMAT_API_STATUS ".\n", ApiStatus );
+
+ //---------------------------------------
+ // PRINT ALERT: NO PAPER - JOB_QUEUED
+ //---------------------------------------
+ Print->alrtpr_status = PRJOB_DESTNOPAPER;
+ Print->alrtpr_jobid = 628;
+
+ ApiStatus = NetAlertRaiseEx(
+ ALERT_PRINT_EVENT,
+ VariableInfo,
+ VariableInfoSize,
+ TEXT("SPOOLER") ); // service name
+
+ (VOID) printf( "AlTest 5(PRINT_ALERT): done NetAlertRaiseEx(print), status="
+ FORMAT_API_STATUS ".\n", ApiStatus );
+
+ //------------------------------------------
+ // PRINT ALERT: NO PAPER - JOB PRINTING
+ //------------------------------------------
+ Print->alrtpr_status = PRJOB_DESTNOPAPER | PRJOB_QS_PRINTING;
+ Print->alrtpr_jobid = 629;
+
+ ApiStatus = NetAlertRaiseEx(
+ ALERT_PRINT_EVENT,
+ VariableInfo,
+ VariableInfoSize,
+ TEXT("SPOOLER") ); // service name
+
+ (VOID) printf( "AlTest 5(PRINT_ALERT): done NetAlertRaiseEx(print), status="
+ FORMAT_API_STATUS ".\n", ApiStatus );
+
+}
diff --git a/private/net/svcdlls/alrsvc/lmnetlib.h b/private/net/svcdlls/alrsvc/lmnetlib.h
new file mode 100644
index 000000000..e85f8dfa8
--- /dev/null
+++ b/private/net/svcdlls/alrsvc/lmnetlib.h
@@ -0,0 +1,74 @@
+/*++ BUILD Version: 0001 // Increment this if a change has global effects
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ netlib.h
+
+Abstract:
+
+ Include file for netlib routines.
+
+Author:
+
+ Dan Hinsley (danhi) 8-Jun-1991
+
+Environment:
+
+ User Mode - Win32
+ Portable to any flat, 32-bit environment. (Uses Win32 typedefs.)
+ Requires ANSI C extensions: slash-slash comments.
+
+--*/
+#include <string.h>
+#include <time.h>
+
+#define struprf strupr
+#define strspnf strspn
+#define strlenf strlen
+#define strpbrkf strpbrk
+#define strtokf strtok
+#define strdupf strdup
+#define strcatf strcat
+#define strcspnf strcspn
+#define strrevf strrev
+
+#define strstrf strstr
+
+#define strcpyf strcpy
+#define strncpyf strncpy
+
+#define strcmpf strcmp
+#define stricmpf stricmp
+#define strncmpf strncmp
+#define strnicmpf strnicmp
+
+#define strchrf strchr
+#define strrchrf strrchr
+
+#define memsetf memset
+#define memcpyf memcpy
+
+#define nsprintf sprintf
+
+#define NetISort qsort
+// function prototypes
+WORD
+DosGetMessage(
+ LPSTR * VTable,
+ WORD VCount,
+ LPBYTE Buffer,
+ WORD BufferLength,
+ WORD MessageNumber,
+ LPSTR FileName,
+ PWORD pMessageLength);
+
+#define NET_CTIME_FMT2_LEN 22
+
+int cdecl net_ctime(ULONG *, CHAR *, int, int);
+
+PCHAR stristrf(PCHAR, PCHAR);
+WORD ClearCurDirs(void);
+WORD NetUserRestrict ( WORD access_mode );
+int net_gmtime(time_t * timp, struct tm *tb);
diff --git a/private/net/svcdlls/alrsvc/makefile b/private/net/svcdlls/alrsvc/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/net/svcdlls/alrsvc/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/net/svcdlls/alrsvc/sources b/private/net/svcdlls/alrsvc/sources
new file mode 100644
index 000000000..fa65fb3a8
--- /dev/null
+++ b/private/net/svcdlls/alrsvc/sources
@@ -0,0 +1,59 @@
+!IF 0
+
+Copyright (c) 1989-92 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+
+Author:
+
+ Steve Wood (stevewo) 12-Apr-1990
+
+NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl
+
+!ENDIF
+
+MAJORCOMP=net
+MINORCOMP=alrsvc
+
+TARGETNAME=alrsvc
+TARGETPATH=\nt\public\sdk\lib
+TARGETTYPE=DYNLINK
+
+TARGETLIBS= \
+ $(BASEDIR)\Public\Sdk\Lib\*\netlib.lib \
+ $(BASEDIR)\public\sdk\lib\*\kernel32.lib \
+ $(BASEDIR)\public\sdk\lib\*\netapi32.lib \
+ $(BASEDIR)\public\sdk\lib\*\advapi32.lib \
+ $(BASEDIR)\public\sdk\lib\*\rpcrt4.lib
+
+INCLUDES=..\..\inc;..\..\..\inc;..\..\api
+
+!IFNDEF DISABLE_NET_UNICODE
+UNICODE=1
+NET_C_DEFINES=-DUNICODE
+!ENDIF
+
+SOURCES= almain.c \
+ alformat.c \
+ alconfig.c \
+ alrsvc.rc
+
+C_DEFINES= -DINCL_32= -DNT
+
+USE_CRTDLL=1
+
+UMTYPE=console
+
+UMTEST=altest
+
+UMLIBS=$(BASEDIR)\public\sdk\lib\*\netapi32.lib
diff --git a/private/net/svcdlls/at/atcmd/at.rc b/private/net/svcdlls/at/atcmd/at.rc
new file mode 100644
index 000000000..22e35e70a
--- /dev/null
+++ b/private/net/svcdlls/at/atcmd/at.rc
@@ -0,0 +1,12 @@
+#include <windows.h>
+#include <ntverp.h>
+
+#define VER_FILETYPE VFT_APP
+#define VER_FILESUBTYPE VFT2_UNKNOWN
+#define VER_FILEDESCRIPTION_STR "Schedule service command line interface"
+#define VER_INTERNALNAME_STR "AT.EXE"
+#define VER_ORIGINALFILENAME_STR "AT.EXE"
+
+#include "common.ver"
+#include "lmatmsg.rc"
+
diff --git a/private/net/svcdlls/at/atcmd/atcmd.c b/private/net/svcdlls/at/atcmd/atcmd.c
new file mode 100644
index 000000000..29dc9455d
--- /dev/null
+++ b/private/net/svcdlls/at/atcmd/atcmd.c
@@ -0,0 +1,2290 @@
+/*++
+
+Copyright (c) 1987-1992 Microsoft Corporation
+
+Module Name:
+
+ atcmd.c
+
+Abstract:
+
+ Code for AT command, to be used with SCHEDULE service on Windows NT.
+
+ The module was taken from LanManager\at.c and then modified considerably
+ to work with NT Schedule service.
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 06 - November - 1992
+
+Environment:
+
+ User Mode - Win32
+
+Revision History:
+
+ 06-Nov-1992 vladimv
+ Created
+
+ 20-Feb-1993 yihsins
+ Get rid of hard coded strings and parse/print time according
+ to user profile
+
+ 25-May-1993 RonaldM
+ Convert strings to OEM before printing to the console, since
+ the console doesn't yet support unicode.
+
+ 28-Jun-1993 RonaldM
+ Added the "confirm" yes and no strings, which are meant to be
+ localised. The original yes and no strings cannot be localised,
+ because this would create batch file incompatibilities.
+
+ 07-Jul-1994 vladimv
+ Added support for interactive switch. Replaced "confirm" strings
+ with APE2_GEN_* strings - to eliminate redundancy. The rule is
+ that switches are not internationalizable while switch values are.
+
+--*/
+
+#include <nt.h> // DbgPrint prototype
+#include <ntrtl.h> // DbgPrint
+#include <nturtl.h> // Needed by winbase.h
+
+#include <windows.h>
+#include <winnls.h>
+#include <shellapi.h>
+
+#include <lmcons.h> // NET_API_STATUS
+#include <lmerr.h> // NetError codes
+#include <icanon.h> // NetpNameValidate
+
+#include "lmatmsg.h" // private AT error codes & messages
+#include <lmat.h> // AT_INFO
+#include <stdlib.h> // exit()
+#include <stdio.h> // printf
+#include <wchar.h> // wcslen
+#include <apperr.h> // APE_AT_USAGE
+#include <apperr2.h> // APE2_GEN_MONDAY + APE2_*
+#include <lmapibuf.h> // NetApiBufferFree
+#include <timelib.h> // NetpGetTimeFormat
+#include <luidate.h> // LUI_ParseTimeSinceStartOfDay
+
+
+#define YES_FLAG 0
+#define NO_FLAG 1
+#define INVALID_FLAG -1
+
+#define DUMP_ALL 0
+#define DUMP_ID 1
+#define ADD_TO_SCHEDULE 2
+#define ADD_ONETIME 3
+#define DEL_ID 4
+#define DEL_ALL 5
+#define ACTION_USAGE 6
+#define MAX_COMMAND_LEN 128
+#define MAX_SCHED_FIELD_LENGTH 24
+
+
+#define PutNewLine() GenOutput( TEXT("\n"))
+#define PutNewLine2() GenOutput( TEXT("\n\n"))
+
+#define MAX_MSG_BUFFER 1024
+
+WCHAR ConBuf[MAX_MSG_BUFFER];
+
+#define GenOutput(fmt) \
+ {wcscpy(ConBuf, fmt); \
+ ConsolePrint(ConBuf, wcslen(ConBuf));}
+
+#define GenOutputArg(fmt, a1) \
+ {wsprintf(ConBuf, fmt, a1); \
+ ConsolePrint(ConBuf, wcslen(ConBuf));}
+//
+// Formats used by printf.
+//
+#define DUMP_FMT1 TEXT("%-7.7ws")
+
+//
+// DUMP_FMT2 is chosen so that the most common case (id numbers less than 100)
+// looks good: two spaces for a number, three spaces for blanks.
+// Larger numbers just like in LM21 will result to shifted display.
+//
+#define DUMP_FMT2 TEXT("%2d ")
+#define MAX_TIME_FIELD_LENGTH 14
+#define DUMP_FMT3 TEXT("%ws") // for printing JobTime
+
+#define NULLC L'\0'
+#define BLANK L' '
+#define SLASH L'/'
+#define BACKSLASH L'\\'
+#define ELLIPSIS L"..."
+
+#define QUESTION_SW L"/?"
+#define QUESTION_SW_TOO L"-?"
+#define SCHED_TOK_DELIM L"," // string of valid delimiters for days & dates
+#define ARG_SEP_CHR L':'
+
+typedef struct _SEARCH_LIST {
+ WCHAR * String;
+ DWORD MessageId;
+ DWORD Value;
+} SEARCH_LIST, *PSEARCH_LIST, *LPSEARCH_LIST;
+
+//
+// All of the values below must be bitmasks. MatchString() depends on that!
+//
+#define AT_YES_VALUE 0x0001
+#define AT_DELETE_VALUE 0x0002
+#define AT_EVERY_VALUE 0x0004
+#define AT_NEXT_VALUE 0x0008
+#define AT_NO_VALUE 0x0010
+#define AT_CONFIRM_YES_VALUE 0x0020
+#define AT_CONFIRM_NO_VALUE 0x0040
+#define AT_INTERACTIVE 0x0080
+
+SEARCH_LIST GlobalListTable[] = {
+ { NULL, IDS_YES, AT_YES_VALUE},
+ { NULL, IDS_DELETE, AT_DELETE_VALUE},
+ { NULL, IDS_EVERY, AT_EVERY_VALUE},
+ { NULL, IDS_NEXT, AT_NEXT_VALUE},
+ { NULL, IDS_NO, AT_NO_VALUE},
+ { NULL, APE2_GEN_YES, AT_CONFIRM_YES_VALUE},
+ { NULL, APE2_GEN_NO, AT_CONFIRM_NO_VALUE},
+ { NULL, IDS_INTERACTIVE, AT_INTERACTIVE},
+ { NULL, 0, 0 }
+};
+
+SEARCH_LIST DaysOfWeekSearchList[] = {
+ { NULL, APE2_GEN_MONDAY_ABBREV, 0},
+ { NULL, APE2_GEN_TUESDAY_ABBREV, 1},
+ { NULL, APE2_GEN_WEDNSDAY_ABBREV, 2},
+ { NULL, APE2_GEN_THURSDAY_ABBREV, 3},
+ { NULL, APE2_GEN_FRIDAY_ABBREV, 4},
+ { NULL, APE2_GEN_SATURDAY_ABBREV, 5},
+ { NULL, APE2_TIME_SATURDAY_ABBREV2, 5},
+ { NULL, APE2_GEN_SUNDAY_ABBREV, 6},
+ { NULL, APE2_GEN_MONDAY, 0},
+ { NULL, APE2_GEN_TUESDAY, 1},
+ { NULL, APE2_GEN_WEDNSDAY, 2},
+ { NULL, APE2_GEN_THURSDAY, 3},
+ { NULL, APE2_GEN_FRIDAY, 4},
+ { NULL, APE2_GEN_SATURDAY, 5},
+ { NULL, APE2_GEN_SUNDAY, 6},
+ { NULL, APE2_GEN_NONLOCALIZED_MONDAY_ABBREV, 0},
+ { NULL, APE2_GEN_NONLOCALIZED_TUESDAY_ABBREV, 1},
+ { NULL, APE2_GEN_NONLOCALIZED_WEDNSDAY_ABBREV, 2},
+ { NULL, APE2_GEN_NONLOCALIZED_THURSDAY_ABBREV, 3},
+ { NULL, APE2_GEN_NONLOCALIZED_FRIDAY_ABBREV, 4},
+ { NULL, APE2_GEN_NONLOCALIZED_SATURDAY_ABBREV, 5},
+ { NULL, APE2_GEN_NONLOCALIZED_SATURDAY_ABBREV2, 5},
+ { NULL, APE2_GEN_NONLOCALIZED_SUNDAY_ABBREV, 6},
+ { NULL, APE2_GEN_NONLOCALIZED_MONDAY, 0},
+ { NULL, APE2_GEN_NONLOCALIZED_TUESDAY, 1},
+ { NULL, APE2_GEN_NONLOCALIZED_WEDNSDAY, 2},
+ { NULL, APE2_GEN_NONLOCALIZED_THURSDAY, 3},
+ { NULL, APE2_GEN_NONLOCALIZED_FRIDAY, 4},
+ { NULL, APE2_GEN_NONLOCALIZED_SATURDAY, 5},
+ { NULL, APE2_GEN_NONLOCALIZED_SUNDAY, 6},
+ { NULL, 0, 0 }
+ };
+
+BOOL
+AreYouSure(
+ VOID
+ );
+BOOL
+ArgIsServerName(
+ WCHAR * string
+ );
+BOOL
+ArgIsTime(
+ IN WCHAR * timestr,
+ OUT PDWORD pJobTime
+ );
+BOOL
+ArgIsDecimalString(
+ IN WCHAR * pDecimalString,
+ OUT PDWORD pNumber
+ );
+DWORD
+ConsolePrint(
+ IN LPWSTR pch,
+ IN int cch
+ );
+int
+FileIsConsole(
+ int fh
+ );
+BOOL
+IsDayOfMonth(
+ IN WCHAR * pToken,
+ OUT PDWORD pDay
+ );
+BOOL
+IsDayOfWeek(
+ IN WCHAR * pToken,
+ OUT PDWORD pDay
+ );
+NET_API_STATUS
+JobAdd(
+ VOID
+ );
+NET_API_STATUS
+JobEnum(
+ VOID
+ );
+NET_API_STATUS
+JobGetInfo(
+ VOID
+ );
+DWORD
+MatchString(
+ WCHAR * name,
+ DWORD mask
+ );
+DWORD
+MessageGet(
+ IN DWORD MessageId,
+ IN LPWSTR *buffer,
+ IN DWORD Size
+ );
+DWORD
+MessagePrint(
+ IN DWORD MessageId,
+ ...
+ );
+BOOL
+ParseJobIdArgs(
+ WCHAR ** argv,
+ int argc,
+ int argno,
+ PBOOL pDeleteFound
+ );
+BOOL
+ParseTimeArgs(
+ WCHAR ** argv,
+ int argc,
+ int argno,
+ int * pargno
+ );
+VOID
+PrintDay(
+ int type,
+ DWORD DaysOfMonth,
+ UCHAR DaysOfWeek,
+ UCHAR Flags
+ );
+VOID
+PrintLine(
+ VOID
+ );
+VOID
+PrintTime(
+ DWORD JobTime
+ );
+BOOL
+TraverseSearchList(
+ PWCHAR String,
+ PSEARCH_LIST SearchList,
+ PDWORD pValue
+ );
+VOID
+Usage(
+ BOOL GoodCommand
+ );
+BOOL
+ValidateCommand(
+ IN int argc,
+ IN WCHAR ** argv,
+ OUT int * pCommand
+ );
+
+VOID
+GetTimeString(
+ DWORD Time,
+ WCHAR *Buffer,
+ int BufferLength
+ );
+
+BOOL
+InitList(
+ PSEARCH_LIST SearchList
+ );
+
+VOID
+TermList(
+ PSEARCH_LIST SearchList
+ );
+
+DWORD
+GetStringColumn(
+ WCHAR *
+ );
+
+AT_INFO GlobalAtInfo; // buffer for scheduling new jobs
+WCHAR GlobalAtInfoCommand[ MAX_COMMAND_LEN + 1];
+
+DWORD GlobalJobId; // id of job in question
+PWSTR GlobalServerName;
+HANDLE GlobalMessageHandle;
+BOOL GlobalYes;
+BOOL GlobalDeleteAll;
+BOOL GlobalErrorReported;
+BOOL bDBCS;
+
+CHAR ** GlobalCharArgv; // keeps original input
+
+NET_TIME_FORMAT GlobalTimeFormat = {0};
+
+// In OS/2 it used to be OK to call "exit()" with a negative number. In
+// NT however, "exit()" should be called with a positive number only (a
+// valid windows error code?!). Note that OS/2 AT command used to call
+// exit(+1) for bad user input, and exit(-1) where -1 would get mapped to
+// 255 for other errors. To keep things simple and to avoid calling exit()
+// with a negative number, NT AT command calls exit(+1) for all possible
+// errors.
+
+#define AT_GENERIC_ERROR 1
+
+
+VOID _CRTAPI1
+main(
+ int argc,
+ CHAR ** charArgv
+ )
+/*++
+
+Routine Description:
+
+ Main module. Note that strings (for now) arrive as asciiz even
+ if you compile app for UNICODE.
+
+Arguments:
+
+ argc - argument count
+ charArgv - array of ascii strings
+
+Return Value:
+
+ None.
+
+--*/
+{
+ NET_API_STATUS status = NERR_Success;
+ int command; // what to do
+ WCHAR ** argv;
+ DWORD cp;
+ CPINFO CurrentCPInfo;
+
+ GlobalYes = FALSE;
+ GlobalDeleteAll = FALSE;
+ GlobalErrorReported = FALSE;
+ GlobalCharArgv = charArgv;
+
+ /*
+ Added for bilingual message support. This is needed for FormatMessage
+ to work correctly. (Called from DosGetMessage).
+ Get current CodePage Info. We need this to decide whether
+ or not to use half-width characters.
+ */
+
+
+ GetCPInfo(cp=GetConsoleOutputCP(), &CurrentCPInfo);
+ switch ( cp ) {
+ case 932:
+ case 936:
+ case 949:
+ case 950:
+ SetThreadLocale(
+ MAKELCID(
+ MAKELANGID(
+ PRIMARYLANGID(GetSystemDefaultLangID()),
+ SUBLANG_ENGLISH_US ),
+ SORT_DEFAULT
+ )
+ );
+ bDBCS = TRUE;
+ break;
+
+ default:
+ SetThreadLocale(
+ MAKELCID(
+ MAKELANGID( LANG_ENGLISH, SUBLANG_ENGLISH_US ),
+ SORT_DEFAULT
+ )
+ );
+ bDBCS = FALSE;
+ break;
+ }
+
+ GlobalMessageHandle = LoadLibrary( L"netmsg.dll");
+ if ( GlobalMessageHandle == NULL) {
+ MessagePrint( IDS_LOAD_LIBRARY_FAILURE, GetLastError());
+ exit( AT_GENERIC_ERROR);
+ }
+
+ if ( ( argv = CommandLineToArgvW( GetCommandLineW(), &argc)) == NULL) {
+ MessagePrint( IDS_UNABLE_TO_MAP_TO_UNICODE );
+ exit( AT_GENERIC_ERROR);
+ }
+
+ if ( ValidateCommand( argc, argv, &command) == FALSE) {
+ Usage( FALSE);
+ exit( AT_GENERIC_ERROR);
+ }
+
+ switch( command) {
+
+ case DUMP_ALL:
+ status = JobEnum();
+ break;
+
+ case DUMP_ID:
+ status = JobGetInfo();
+ break;
+
+ case ADD_TO_SCHEDULE:
+ status = JobAdd();
+ break;
+
+ case DEL_ALL:
+ if ( AreYouSure() == FALSE) {
+ break;
+ }
+ status = NetScheduleJobDel(
+ GlobalServerName,
+ 0,
+ (DWORD)-1
+ );
+ if ( status == NERR_Success || status == APE_AT_ID_NOT_FOUND) {
+ break;
+ }
+ MessagePrint( status );
+ break;
+
+ case DEL_ID:
+ status = NetScheduleJobDel(
+ GlobalServerName,
+ GlobalJobId,
+ GlobalJobId
+ );
+ if ( status == NERR_Success) {
+ break;
+ }
+ MessagePrint( status );
+ break;
+
+ case ACTION_USAGE:
+ Usage( TRUE);
+ status = NERR_Success;
+ break;
+ }
+
+ TermList( GlobalListTable);
+ TermList( DaysOfWeekSearchList);
+ LocalFree( GlobalTimeFormat.AMString );
+ LocalFree( GlobalTimeFormat.PMString );
+ LocalFree( GlobalTimeFormat.DateFormat );
+ LocalFree( GlobalTimeFormat.TimeSeparator );
+ exit( status == NERR_Success ? ERROR_SUCCESS : AT_GENERIC_ERROR);
+}
+
+
+
+BOOL
+AreYouSure(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ Make sure user really wants to delete all jobs.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ TRUE if user really wants to go ahead.
+ FALSE otherwise.
+
+--*/
+{
+ register int retries = 0;
+ WCHAR rbuf[ 16];
+ WCHAR * smallBuffer = NULL;
+ DWORD Value;
+ int cch;
+ int retc;
+
+ if ( GlobalYes == TRUE) {
+ return( TRUE);
+ }
+
+ if ( MessagePrint( APE2_AT_DEL_WARNING ) == 0) {
+ exit( AT_GENERIC_ERROR);
+ }
+
+ for ( ; ;) {
+
+ if ( MessageGet(
+ APE2_GEN_DEFAULT_NO, // MessageId
+ &smallBuffer, // lpBuffer
+ 0
+ ) == 0) {
+ exit( AT_GENERIC_ERROR);
+ }
+
+ if ( MessagePrint( APE_OkToProceed, smallBuffer) == 0) {
+ exit( AT_GENERIC_ERROR);
+ }
+
+ LocalFree( smallBuffer );
+
+ if (FileIsConsole(STD_INPUT_HANDLE)) {
+ retc = ReadConsole(GetStdHandle(STD_INPUT_HANDLE),rbuf,16,&cch,0);
+ if (retc) {
+ //
+ // Get rid of cr/lf
+ //
+ if (wcschr(rbuf, TEXT('\r')) == NULL) {
+ if (wcschr(rbuf, TEXT('\n')))
+ *wcschr(rbuf, TEXT('\n')) = NULLC;
+ }
+ else
+ *wcschr(rbuf, TEXT('\r')) = NULLC;
+ }
+ }
+ else {
+ CHAR oemBuf[ 16 ];
+
+ retc = (int)gets(oemBuf);
+#if DBG
+ fprintf(stderr, "got >%s<\n", oemBuf);
+#endif
+ cch = 0;
+ if (retc || strlen(oemBuf)+1 > 16)
+ cch = MultiByteToWideChar(CP_OEMCP, MB_PRECOMPOSED,
+ oemBuf, strlen(oemBuf)+1, rbuf, 16);
+ }
+
+#if DBG
+ fprintf(stderr, "cch = %d, retc = %d\n", cch, retc);
+#endif
+ if (!retc || cch == 0)
+ return( FALSE);
+#if DBG
+ fprintf(stderr, "converted to >%ws<\n", rbuf);
+#endif
+
+ Value = MatchString(_wcsupr(rbuf), AT_CONFIRM_NO_VALUE | AT_CONFIRM_YES_VALUE);
+
+ if ( Value == AT_CONFIRM_NO_VALUE) {
+ return( FALSE);
+ } else if ( Value == AT_CONFIRM_YES_VALUE) {
+ break;
+ }
+
+ if ( ++retries >= 3) {
+ MessagePrint( APE_NoGoodResponse );
+ return( FALSE);
+ }
+
+ if ( MessagePrint( APE_UtilInvalidResponse ) == 0) {
+ exit( AT_GENERIC_ERROR);
+ }
+ }
+ return( TRUE);
+}
+
+
+
+BOOL
+ArgIsServerName(
+ WCHAR * string
+ )
+/*++
+
+Routine Description:
+
+ Checks if string is a server name. Validation is really primitive, eg
+ strings like "\\\threeslashes" pass the test.
+
+Arguments:
+
+ string - pointer to string that may represent a server name
+
+Return Value:
+
+ TRUE - string is (or might be) a valid server name
+ FALSE - string is not a valid server name
+
+--*/
+{
+
+ NET_API_STATUS ApiStatus;
+
+ if (string[0] == BACKSLASH && string[1] == BACKSLASH && string[2] != 0) {
+ ApiStatus = NetpNameValidate(
+ NULL, // no server name.
+ &string[2], // name to validate
+ NAMETYPE_COMPUTER,
+ LM2X_COMPATIBLE); // flags
+ if (ApiStatus != NO_ERROR) {
+ return (FALSE);
+ }
+ GlobalServerName = string;
+ return( TRUE);
+ }
+
+ return( FALSE); // GlobalServerName is NULL at load time
+}
+
+
+
+BOOL
+ArgIsTime(
+ IN WCHAR * timestr,
+ OUT PDWORD pJobTime
+ )
+/*++
+
+Routine Description:
+
+ Determines whether string is a time or not. Validates that string
+ passed into it is in the form of HH:MM. It searches the string for
+ a ":" and then validates that the preceeding data is numeric & in a
+ valid range for hours. It then validates the string after the ":"
+ is numeric & in a validate range for minutes. If all the tests are
+ passed the TRUE is returned.
+
+Arguments:
+
+ timestr - string to check whether it is a time
+ JobTime - ptr to number of miliseconds
+
+Return Value:
+
+ TRUE - timestr was a time in HH:MM format
+ FALSE - timestr wasn't at time
+
+--*/
+{
+ CHAR buffer[MAX_TIME_SIZE];
+ USHORT ParseLen;
+ BOOL fDummy;
+
+ if ( timestr == NULL )
+ return FALSE;
+
+ if ( !WideCharToMultiByte( CP_ACP,
+ 0,
+ timestr,
+ -1,
+ buffer,
+ sizeof( buffer )/sizeof(CHAR),
+ NULL,
+ &fDummy ))
+ {
+ return FALSE;
+ }
+
+ if ( LUI_ParseTimeSinceStartOfDay( buffer, pJobTime, &ParseLen, 0) )
+ return FALSE;
+
+ // LUI_ParseTimeSinceStartOfDay returns the time in seconds.
+ // Hence, we need to convert it to microseconds.
+ *pJobTime *= 1000;
+
+ return( TRUE);
+}
+
+
+
+BOOL
+ArgIsDecimalString(
+ IN WCHAR * pDecimalString,
+ OUT PDWORD pNumber
+ )
+/*++
+
+Routine Description:
+
+ This routine converts a string into a DWORD if it possibly can.
+ The conversion is successful if string is decimal numeric and
+ does not lead to an overflow.
+
+Arguments:
+
+ pDecimalString ptr to decimal string
+ pNumber ptr to number
+
+Return Value:
+
+ FALSE invalid number
+ TRUE valid number
+
+--*/
+{
+ DWORD Value;
+ DWORD OldValue;
+ DWORD digit;
+
+ if ( pDecimalString == NULL || *pDecimalString == 0) {
+ return( FALSE);
+ }
+
+ Value = 0;
+
+ while ( (digit = *pDecimalString++) != 0) {
+
+ if ( digit < L'0' || digit > L'9') {
+ return( FALSE); // not a decimal string
+ }
+
+ OldValue = Value;
+ Value = digit - L'0' + 10 * Value;
+ if ( Value < OldValue) {
+ return( FALSE); // overflow
+ }
+ }
+
+ *pNumber = Value;
+ return( TRUE);
+}
+
+
+
+BOOL
+IsDayOfMonth(
+ IN WCHAR * pToken,
+ OUT PDWORD pDay
+ )
+/*++
+
+Routine Description:
+
+ Converts a string into a number for the day of the month, if it can
+ possibly do so. Note that "first" == 1, ...
+
+Arguments:
+
+ pToken pointer to schedule token for the day of the month
+ pDay pointer to index of day in a month
+
+Return Value:
+
+ TRUE if a valid schedule token
+ FALSE otherwise
+
+--*/
+{
+ return ( ArgIsDecimalString( pToken, pDay) == TRUE && *pDay >= 1
+ && *pDay <= 31);
+}
+
+
+
+BOOL
+IsDayOfWeek(
+ WCHAR * pToken,
+ PDWORD pDay
+ )
+/*++
+
+Routine Description:
+
+ This routine converts a string day of the week into a integer
+ offset into the week if it possibly can. Note that Monday==0,
+ ..., Sunday == 6.
+
+Arguments:
+
+ pToken pointer to schedule token for the day of a week
+ pDay pointer to index of day in a month
+
+Return Value:
+
+ TRUE if a valid schedule token
+ FALSE otherwise
+
+--*/
+{
+ if ( !InitList( DaysOfWeekSearchList ) )
+ {
+ // Error already reported
+ exit( -1 );
+ }
+
+ return( TraverseSearchList(
+ pToken,
+ DaysOfWeekSearchList,
+ pDay
+ ));
+}
+
+
+
+NET_API_STATUS
+JobAdd(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ Adds a new item to schedule.
+
+Arguments:
+
+ None. Uses globals.
+
+Return Value:
+
+ NET_API_STATUS return value of remote api call
+
+--*/
+{
+ NET_API_STATUS status;
+
+ for ( ; ; ) {
+ status = NetScheduleJobAdd(
+ GlobalServerName,
+ (LPBYTE)&GlobalAtInfo,
+ &GlobalJobId
+ );
+ if ( status == ERROR_INVALID_PARAMETER &&
+ GlobalAtInfo.Flags & JOB_NONINTERACTIVE) {
+ //
+ // We may have failed because we are talking to a down level
+ // server that does not know about JOB_NONINTERACTIVE bit.
+ // Clear the bit, and try again.
+ // A better approach would be to check the version of the
+ // server before making NetScheduleJobAdd() call, adjust the
+ // bit appropriately and only then call NetScheduleJobAdd().
+ //
+ GlobalAtInfo.Flags &= ~JOB_NONINTERACTIVE;
+ } else {
+ break;
+ }
+
+ }
+ if ( status == NERR_Success) {
+ MessagePrint( IDS_ADD_NEW_JOB, GlobalJobId );
+ } else {
+ if ( MessagePrint( status ) == 0) {
+ exit( AT_GENERIC_ERROR);
+ }
+ }
+
+ return( status);
+}
+
+
+
+NET_API_STATUS
+JobEnum(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This does all of the processing necessary to dump out the entire
+ schedule file. It loops through on each record and formats its
+ information for printing and then goes to the next.
+
+Arguments:
+
+ None. Uses globals.
+
+Return Value:
+
+ ERROR_SUCCESS if everything enumerated OK
+ error returned by remote api otherwise
+
+--*/
+{
+ BOOL first = TRUE;
+ DWORD ResumeJobId = 0;
+ NET_API_STATUS status = NERR_Success;
+ PAT_ENUM pAtEnum;
+ DWORD EntriesRead;
+ DWORD TotalEntries;
+ LPVOID EnumBuffer;
+ DWORD length;
+ WCHAR * smallBuffer = NULL;
+
+ for ( ; ;) {
+
+ status = NetScheduleJobEnum(
+ GlobalServerName,
+ (LPBYTE *)&EnumBuffer,
+ (DWORD)-1,
+ &EntriesRead,
+ &TotalEntries,
+ &ResumeJobId
+ );
+
+ if ( status != ERROR_SUCCESS && status != ERROR_MORE_DATA) {
+ length = MessagePrint( status );
+ if ( length == 0) {
+ exit( AT_GENERIC_ERROR);
+ }
+ return( status);
+ }
+
+ ASSERT( status == ERROR_SUCCESS ? TotalEntries == EntriesRead
+ : TotalEntries > EntriesRead);
+
+ if ( TotalEntries == 0) {
+ break; // no items found
+ }
+
+ if ( first == TRUE) {
+ length = MessagePrint( APE2_AT_DUMP_HEADER );
+ if ( length == 0) {
+ exit( AT_GENERIC_ERROR);
+ }
+ PrintLine(); // line across screen
+ first = FALSE;
+ }
+
+ for ( pAtEnum = EnumBuffer; EntriesRead-- > 0; pAtEnum++) {
+ if ( pAtEnum->Flags & JOB_EXEC_ERROR) {
+ if ( MessageGet( APE2_GEN_ERROR, &smallBuffer, 0 ) == 0) {
+ // error reported already
+ exit( AT_GENERIC_ERROR);
+ }
+ GenOutputArg( DUMP_FMT1, smallBuffer );
+ LocalFree( smallBuffer );
+ } else {
+ GenOutputArg( DUMP_FMT1, L"");
+ }
+ GenOutputArg( DUMP_FMT2, pAtEnum->JobId);
+ PrintDay( DUMP_ALL, pAtEnum->DaysOfMonth, pAtEnum->DaysOfWeek,
+ pAtEnum->Flags);
+ PrintTime( pAtEnum->JobTime);
+ GenOutputArg( TEXT("%ws\n"), pAtEnum->Command);
+ }
+
+ if ( EnumBuffer != NULL) {
+ (VOID)NetApiBufferFree( (LPVOID)EnumBuffer);
+ EnumBuffer = NULL;
+ }
+
+ if ( status == ERROR_SUCCESS) {
+ break; // we have read & displayed all the items
+ }
+ }
+
+ if ( first == TRUE) {
+ MessagePrint( APE_EmptyList );
+ }
+
+ return( ERROR_SUCCESS);
+}
+
+
+
+NET_API_STATUS
+JobGetInfo(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This prints out the schedule of an individual items schedule.
+
+Arguments:
+
+ None. Uses globals.
+
+Return Value:
+
+ NET_API_STATUS value returned by remote api
+
+--*/
+
+{
+ PAT_INFO pAtInfo = NULL;
+ NET_API_STATUS status;
+
+ status = NetScheduleJobGetInfo(
+ GlobalServerName,
+ GlobalJobId,
+ (LPBYTE *)&pAtInfo
+ );
+ if ( status != NERR_Success) {
+ MessagePrint( status );
+ return( status);
+ }
+
+ PutNewLine();
+ MessagePrint( APE2_AT_DI_TASK );
+ GenOutputArg( TEXT("%d"), GlobalJobId);
+ PutNewLine();
+
+ MessagePrint( APE2_AT_DI_STATUS );
+ MessagePrint( (pAtInfo->Flags & JOB_EXEC_ERROR) != 0 ?
+ APE2_GEN_ERROR : APE2_GEN_OK );
+ PutNewLine();
+
+ MessagePrint( APE2_AT_DI_SCHEDULE );
+ PrintDay( DUMP_ID, pAtInfo->DaysOfMonth, pAtInfo->DaysOfWeek,
+ pAtInfo->Flags);
+ PutNewLine();
+
+ MessagePrint( APE2_AT_DI_TIMEOFDAY );
+ PrintTime( pAtInfo->JobTime);
+ PutNewLine();
+
+ MessagePrint( APE2_AT_DI_INTERACTIVE);
+ MessagePrint( (pAtInfo->Flags & JOB_NONINTERACTIVE) == 0 ?
+ APE2_GEN_YES : APE2_GEN_NO );
+ PutNewLine();
+
+ MessagePrint( APE2_AT_DI_COMMAND );
+ GenOutputArg( TEXT("%ws\n"), pAtInfo->Command);
+ PutNewLine2();
+
+ (VOID)NetApiBufferFree( (LPVOID)pAtInfo);
+
+ return( NERR_Success);
+}
+
+
+
+DWORD
+MatchString(
+ WCHAR * name,
+ DWORD Values
+ )
+/*++
+Routine Description:
+
+ Parses switch string and returns NULL for an invalid switch,
+ and -1 for an ambiguous switch.
+
+Arguments:
+
+ name - pointer to string we need to examine
+ Values - bitmask of values of interest
+
+Return Value:
+
+ Pointer to command, or NULL or -1.
+--*/
+{
+ WCHAR * String;
+ PSEARCH_LIST pCurrentList;
+ WCHAR * CurrentString;
+ DWORD FoundValue;
+ int nmatches;
+ int longest;
+
+ if ( !InitList( GlobalListTable ) )
+ {
+ // Error already reported
+ exit( -1 );
+ }
+
+ for ( pCurrentList = GlobalListTable,
+ longest = nmatches = 0,
+ FoundValue = 0;
+ (CurrentString = pCurrentList->String) != NULL;
+ pCurrentList++) {
+
+ if ( (Values & pCurrentList->Value) == 0) {
+ continue; // skip this List
+ }
+
+ for ( String = name; *String == *CurrentString++; String++) {
+ if ( *String == 0) {
+ return( pCurrentList->Value); // exact match
+ }
+ }
+
+ if ( !*String) {
+
+ if ( String - name > longest) {
+
+ longest = String - name;
+ nmatches = 1;
+ FoundValue = pCurrentList->Value;
+
+ } else if ( String - name == longest) {
+
+ nmatches++;
+ }
+ }
+ }
+
+ // 0 corresponds to no match at all (invalid List)
+ // while -1 corresponds to multiple match (ambiguous List).
+
+ if ( nmatches != 1) {
+ return ( (nmatches == 0) ? 0 : -1);
+ }
+
+ return( FoundValue);
+}
+
+
+DWORD
+MessageGet(
+ IN DWORD MessageId,
+ OUT LPWSTR *buffer,
+ IN DWORD Size
+ )
+/*++
+
+Routine Description:
+
+ Fills in the unicode message corresponding to a given message id,
+ provided that a message can be found and that it fits in a supplied
+ buffer.
+
+Arguments:
+
+ MessageId - message id
+ buffer - pointer to caller supplied buffer
+ Size - size (always in bytes) of supplied buffer,
+ If size is 0, buffer will be allocated by FormatMessage.
+
+Return Value:
+
+ Count of characters, not counting the terminating null character,
+ returned in the buffer. Zero return value indicates failure.
+
+--*/
+{
+ DWORD length;
+ LPVOID lpSource;
+ DWORD dwFlags;
+
+ if ( MessageId < NERR_BASE) {
+ //
+ // Get message from system.
+ //
+ lpSource = NULL; // redundant step according to FormatMessage() spec
+ dwFlags = FORMAT_MESSAGE_FROM_SYSTEM;
+
+ } else if ( ( MessageId >= APE2_AT_DEL_WARNING
+ && MessageId <= APE2_AT_DI_INTERACTIVE)
+ || ( MessageId >= IDS_LOAD_LIBRARY_FAILURE
+ && MessageId <= IDS_INTERACTIVE )) {
+ //
+ // Get message from this module.
+ //
+ lpSource = NULL;
+ dwFlags = FORMAT_MESSAGE_FROM_HMODULE;
+
+ } else {
+ //
+ // Get message from netmsg.dll.
+ //
+ lpSource = GlobalMessageHandle;
+ dwFlags = FORMAT_MESSAGE_FROM_HMODULE;
+ }
+
+ if ( Size == 0 )
+ dwFlags |= FORMAT_MESSAGE_ALLOCATE_BUFFER;
+
+ length = FormatMessage(
+ dwFlags, // dwFlags
+ lpSource, // lpSource
+ MessageId, // MessageId
+ 0, // dwLanguageId
+ (LPWSTR) buffer, // lpBuffer
+ Size, // nSize
+ NULL // lpArguments
+ );
+
+ if ( length == 0) {
+ MessagePrint( IDS_MESSAGE_GET_ERROR, MessageId, GetLastError());
+ }
+ return( length);
+
+} // MessageGet()
+
+
+
+int
+FileIsConsole(
+ int fh
+ )
+{
+ unsigned htype ;
+
+ htype = GetFileType(GetStdHandle(fh));
+ htype &= ~FILE_TYPE_REMOTE;
+ return htype == FILE_TYPE_CHAR;
+}
+
+
+
+DWORD
+ConsolePrint(
+ LPWSTR pch,
+ int cch
+ )
+{
+ int cchOut;
+ int err;
+ CHAR *pchOemBuffer;
+
+ if (FileIsConsole(STD_OUTPUT_HANDLE)) {
+ err = WriteConsole(
+ GetStdHandle(STD_OUTPUT_HANDLE),
+ pch, cch,
+ &cchOut, NULL);
+ if (!err || cchOut != cch)
+ goto try_again;
+ }
+ else if ( cch != 0) {
+try_again:
+ cchOut = WideCharToMultiByte(CP_OEMCP, 0, pch, cch, NULL, 0, NULL,NULL);
+ if (cchOut == 0)
+ return 0;
+
+ if ((pchOemBuffer = (CHAR *)malloc(cchOut)) != NULL) {
+ WideCharToMultiByte(CP_OEMCP, 0, pch, cch,
+ pchOemBuffer, cchOut, NULL, NULL);
+ WriteFile(GetStdHandle(STD_OUTPUT_HANDLE),
+ pchOemBuffer, cchOut, &cch, NULL);
+ free(pchOemBuffer);
+ }
+ }
+ return cchOut;
+}
+
+
+
+DWORD
+MessagePrint(
+ IN DWORD MessageId,
+ ...
+ )
+/*++
+
+Routine Description:
+
+ Finds the unicode message corresponding to the supplied message id,
+ merges it with caller supplied string(s), and prints the resulting
+ string.
+
+Arguments:
+
+ MessageId - message id
+
+Return Value:
+
+ Count of characters, not counting the terminating null character,
+ printed by this routine. Zero return value indicates failure.
+
+--*/
+{
+ va_list arglist;
+ WCHAR * buffer = NULL;
+ DWORD length;
+ LPVOID lpSource;
+ DWORD dwFlags = FORMAT_MESSAGE_ALLOCATE_BUFFER;
+
+
+ va_start( arglist, MessageId );
+
+ if ( MessageId < NERR_BASE) {
+ //
+ // Get message from system.
+ //
+ lpSource = NULL; // redundant step according to FormatMessage() spec
+ dwFlags |= FORMAT_MESSAGE_FROM_SYSTEM;
+
+ } else if ( ( MessageId >= APE2_AT_DEL_WARNING
+ && MessageId <= APE2_AT_DI_INTERACTIVE)
+ || ( MessageId >= IDS_LOAD_LIBRARY_FAILURE
+ && MessageId <= IDS_INTERACTIVE )) {
+ //
+ // Get message from this module.
+ //
+ lpSource = NULL;
+ dwFlags |= FORMAT_MESSAGE_FROM_HMODULE;
+
+ } else {
+ //
+ // Get message from netmsg.dll.
+ //
+ lpSource = GlobalMessageHandle;
+ dwFlags |= FORMAT_MESSAGE_FROM_HMODULE;
+ }
+
+ length = FormatMessage(
+ dwFlags, // dwFlags
+ lpSource, // lpSource
+ MessageId, // MessageId
+ 0L, // dwLanguageId
+ (LPTSTR)&buffer, // lpBuffer
+ 0, // size
+ &arglist // lpArguments
+ );
+
+ length = ConsolePrint(buffer, length);
+
+ LocalFree(buffer);
+
+ return( length);
+
+} // MessagePrint()
+
+
+
+BOOL
+ParseJobIdArgs(
+ WCHAR ** argv,
+ int argc,
+ int argno,
+ PBOOL pDeleteFound
+ )
+/*++
+
+Routine Description:
+
+ Parses arguments for commands containing JobId (these can be JobGetInfo
+ and JobDel commands). It loops through JobId arguments making sure that
+ we have at most one "yes-no" switch and at most one "delete" switch and
+ nothing else.
+
+Arguments:
+
+ argv argument list
+ argc number of arguments to parse
+ argno index of argument to begin parsing from
+ pDeleteFound did we find a delete switch or not
+
+Return Value:
+
+ FALSE invalid argument found
+ TRUE valid arguments
+
+--*/
+{
+ BOOL FoundDeleteSwitch;
+
+ for ( FoundDeleteSwitch = FALSE; argno < argc; argno++) {
+
+ WCHAR * argp;
+ DWORD length;
+ DWORD Value;
+
+ argp = argv[ argno];
+
+ if ( *argp++ != SLASH) {
+ return( FALSE); // not a switch
+ }
+
+ _wcsupr( argp);
+ length = wcslen( argp);
+
+ Value = MatchString( argp, AT_YES_VALUE | AT_DELETE_VALUE);
+
+ if ( Value == AT_YES_VALUE) {
+
+ if ( GlobalYes == TRUE) {
+ return( FALSE); // multiple instances of yes switch
+ }
+
+ GlobalYes = TRUE;
+ continue;
+ }
+
+ if ( Value == AT_DELETE_VALUE) {
+
+ if ( FoundDeleteSwitch == TRUE) {
+ return( FALSE); // duplicate delete switch
+ }
+ FoundDeleteSwitch = TRUE;
+ continue;
+ }
+
+ return( FALSE); // an unknown switch
+ }
+
+ *pDeleteFound = FoundDeleteSwitch;
+ return( TRUE);
+} // ParseJobIdArgs()
+
+
+
+BOOL
+ParseTimeArgs(
+ WCHAR ** argv,
+ int argc,
+ int argno,
+ int * pargno
+ )
+/*++
+
+Routine Description:
+
+ Parses arguments for command addition.
+
+Arguments:
+
+ argv argument list
+ argc count of args
+ argno index of the first arg to validate
+ pargno ptr to the index of the first non-switch arg
+
+Return Value:
+
+ TRUE all arguments are valid
+ FALSE otherwise
+
+--*/
+{
+ DWORD day_no; // day number for scheduling
+ DWORD NextCount = 0; // count of next switches
+ DWORD EveryCount = 0; // count of every switches
+ WCHAR * argp; // ptr to arg string
+ WCHAR * schedp; // work ptr to arg string
+ DWORD Value; // bitmask
+
+ for ( NOTHING; argno < argc; argno++) {
+
+ argp = argv[ argno];
+
+ if ( *argp++ != SLASH) {
+ break; // found non-switch, we are done
+ }
+
+
+ schedp = wcschr( argp, ARG_SEP_CHR);
+
+ if ( schedp == NULL) {
+ return( FALSE);
+ }
+
+ _wcsupr( argp); // upper case entire input, not just the switch name
+
+ *schedp = 0;
+
+ Value = MatchString( argp, AT_NEXT_VALUE | AT_EVERY_VALUE);
+
+ if ( Value == AT_NEXT_VALUE) {
+
+ NextCount++;
+
+ } else if ( Value == AT_EVERY_VALUE) {
+
+ EveryCount++;
+ GlobalAtInfo.Flags |= JOB_RUN_PERIODICALLY;
+
+ } else {
+
+ return( FALSE); // an unexpected switch
+ }
+
+ if ( NextCount + EveryCount > 1) {
+ return( FALSE); // repeated switch option
+ }
+
+ *schedp++ = ARG_SEP_CHR;
+
+ schedp = wcstok( schedp, SCHED_TOK_DELIM);
+
+ if ( schedp == NULL) {
+ GlobalAtInfo.Flags |= JOB_ADD_CURRENT_DATE;
+ continue;
+ }
+
+ while( schedp != NULL) {
+
+ if ( IsDayOfMonth( schedp, &day_no) == TRUE) {
+
+ GlobalAtInfo.DaysOfMonth |= (1 << (day_no - 1));
+
+ } else if ( IsDayOfWeek( schedp, &day_no) == TRUE) {
+
+ GlobalAtInfo.DaysOfWeek |= (1 << day_no);
+
+ } else {
+ MessagePrint( APE_InvalidSwitchArg );
+ GlobalErrorReported = TRUE;
+ return( FALSE);
+ }
+
+ schedp = wcstok( NULL, SCHED_TOK_DELIM);
+ }
+ }
+
+ if ( argno == argc) {
+ return( FALSE); // all switches, no command
+ }
+
+ *pargno = argno;
+ return( TRUE);
+}
+
+
+
+BOOL
+ParseInteractiveArg(
+ IN OUT WCHAR * argp
+ )
+/*++
+
+Routine Description:
+
+ Returns TRUE if argp is an interactive switch.
+
+--*/
+{
+ DWORD Value; // bitmask
+
+ if ( *argp++ != SLASH) {
+ return( FALSE); // not a switch
+ }
+
+ _wcsupr( argp); // all AT command switches can be safely uppercased
+
+ Value = MatchString( argp, AT_INTERACTIVE);
+
+ if ( Value == AT_INTERACTIVE) {
+ GlobalAtInfo.Flags &= ~JOB_NONINTERACTIVE; // clear noninteractive flag
+ return( TRUE);
+ }
+
+ return( FALSE); // some other switch
+}
+
+
+
+VOID
+PrintDay(
+ int type,
+ DWORD DaysOfMonth,
+ UCHAR DaysOfWeek,
+ UCHAR Flags
+ )
+/*++
+
+Routine Description:
+
+ Print out schedule days. This routine converts a schedule bit map
+ to the literals that represent the schedule.
+
+Arguments:
+
+ type whether this is for JobEnum or not
+ DaysOfMonth bitmask for days of month
+ DaysOfWeek bitmaks for days of week
+ Flags extra info about the job
+
+Return Value:
+
+ None.
+
+--*/
+{
+ int i;
+ WCHAR Buffer[ 128];
+ DWORD BufferLength;
+ DWORD Length;
+ DWORD TotalLength = 0;
+ DWORD TotalColumnLength = 0;
+ WCHAR * LastSpace;
+ DWORD MessageId;
+ BOOL OverFlow = TRUE;
+ static int Ape2GenWeekdayLong[] = {
+ APE2_GEN_MONDAY,
+ APE2_GEN_TUESDAY,
+ APE2_GEN_WEDNSDAY,
+ APE2_GEN_THURSDAY,
+ APE2_GEN_FRIDAY,
+ APE2_GEN_SATURDAY,
+ APE2_GEN_SUNDAY
+ };
+ static int Ape2GenWeekdayAbbrev[] = {
+ APE2_GEN_MONDAY_ABBREV,
+ APE2_GEN_TUESDAY_ABBREV,
+ APE2_GEN_WEDNSDAY_ABBREV,
+ APE2_GEN_THURSDAY_ABBREV,
+ APE2_GEN_FRIDAY_ABBREV,
+ APE2_GEN_SATURDAY_ABBREV,
+ APE2_GEN_SUNDAY_ABBREV
+ };
+
+ //
+ // Subtract 4 to guard against days of week or days of month overflow.
+ //
+ BufferLength = sizeof( Buffer)/ sizeof( WCHAR) - 4;
+ if ( type == DUMP_ALL && BufferLength > MAX_SCHED_FIELD_LENGTH) {
+ BufferLength = MAX_SCHED_FIELD_LENGTH;
+ }
+
+ //
+ // First do the descriptive bit (eg. EACH, NEXT, etc) with the days.
+ //
+
+ if ( Flags & JOB_RUN_PERIODICALLY) {
+
+ MessageId = APE2_AT_EACH;
+
+ } else if ( (DaysOfWeek != 0) || (DaysOfMonth != 0)) {
+
+ MessageId = APE2_AT_NEXT;
+
+ } else if ( Flags & JOB_RUNS_TODAY) {
+
+ MessageId = APE2_AT_TODAY;
+
+ } else {
+
+ MessageId = APE2_AT_TOMORROW;
+ }
+
+ Length = MessageGet(
+ MessageId,
+ (LPWSTR *) &Buffer[TotalLength],
+ BufferLength
+ );
+ if ( Length == 0) {
+ goto PrintDay_exit; // Assume this is due to lack of space
+ }
+ TotalColumnLength = GetStringColumn( &Buffer[TotalLength] );
+ TotalLength = Length;
+
+ if ( DaysOfWeek != 0) {
+
+ for ( i = 0; i < 7; i++) {
+
+ if ( ( DaysOfWeek & (1 << i)) != 0) {
+
+ if( bDBCS ) {
+ Length = MessageGet(
+ Ape2GenWeekdayLong[ i],
+ (LPWSTR *) &Buffer[TotalLength],
+ BufferLength - TotalLength
+ );
+ } else {
+ Length = MessageGet(
+ Ape2GenWeekdayAbbrev[ i],
+ (LPWSTR *) &Buffer[TotalLength],
+ BufferLength - TotalLength
+ );
+ }
+ if ( Length == 0) {
+ //
+ // Not enough room for WeekDay symbol
+ //
+ goto PrintDay_exit;
+
+ }
+ //
+ // Get how many columns will be needed for display.
+ //
+ TotalColumnLength += GetStringColumn( &Buffer[TotalLength] );
+
+ if ( TotalColumnLength >= BufferLength) {
+ //
+ // Not enough room for space following WeekDay symbol
+ //
+ goto PrintDay_exit;
+ }
+ TotalLength +=Length;
+ Buffer[ TotalLength++] = BLANK;
+ TotalColumnLength++;
+ }
+ }
+ }
+
+ if ( DaysOfMonth != 0) {
+
+ for ( i = 0; i < 31; i++) {
+
+ if ( ( DaysOfMonth & (1L << i)) != 0) {
+
+ Length = swprintf(
+ &Buffer[ TotalLength],
+ L"%d ",
+ i + 1
+ );
+ if ( TotalLength + Length > BufferLength) {
+ //
+ // Not enough room for MonthDay symbol followed by space
+ //
+ goto PrintDay_exit;
+ }
+ TotalLength +=Length;
+ TotalColumnLength +=Length;
+ }
+ }
+ }
+
+ OverFlow = FALSE;
+
+PrintDay_exit:
+
+ Buffer[ TotalLength] = NULLC;
+
+ if ( OverFlow == TRUE) {
+
+ if ( TotalLength > 0 && Buffer[ TotalLength - 1] == BLANK) {
+ //
+ // Eliminate trailing space if there is one.
+ //
+ Buffer[ TotalLength - 1] = NULLC;
+ }
+
+ //
+ // Then get rid of the rightmost token (or even whole thing).
+ //
+
+ LastSpace = wcsrchr( Buffer, BLANK);
+
+ wcscpy( LastSpace != NULL ? LastSpace : Buffer, ELLIPSIS);
+
+ TotalLength = wcslen( Buffer);
+
+ }
+
+ if ( type == DUMP_ALL) {
+ while( TotalColumnLength++ < MAX_SCHED_FIELD_LENGTH) {
+ Buffer[ TotalLength++] = BLANK;
+ }
+ Buffer[ TotalLength] = UNICODE_NULL;
+ }
+
+ GenOutputArg( TEXT("%ws"), Buffer);
+}
+
+
+VOID
+PrintLine(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ Prints a line accross screen.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+Note:
+
+ BUGBUG Is this treatment valid for UniCode? See also LUI_PrintLine()
+ BUGBUG in ui\common\src\lui\lui\border.c
+
+--*/
+#define SINGLE_HORIZONTAL L'\x02d'
+#define SCREEN_WIDTH 79
+{
+ WCHAR string[ SCREEN_WIDTH + 1];
+ DWORD offset;
+
+
+ for ( offset = 0; offset < SCREEN_WIDTH; offset++) {
+ string[ offset] = SINGLE_HORIZONTAL;
+ }
+
+ string[ SCREEN_WIDTH] = NULLC;
+ GenOutputArg(TEXT("%ws\n"), string);
+}
+
+
+VOID
+PrintTime(
+ DWORD JobTime
+ )
+/*++
+
+Routine Description:
+
+ Prints time of a job in HH:MM{A,P}M format.
+
+Arguments:
+
+ JobTime - time in miliseconds (measured from midnight)
+
+Return Value:
+
+ None.
+
+Note:
+
+ BUGBUG this does not make sure that JobTime is within the bounds.
+ BUGBUG Also, there is nothing unicode about printing this output.
+
+--*/
+{
+ WCHAR Buffer[15];
+
+ GetTimeString( JobTime, Buffer, sizeof( Buffer)/sizeof( WCHAR) );
+ GenOutputArg( DUMP_FMT3, Buffer );
+}
+
+
+
+BOOL
+TraverseSearchList(
+ IN PWCHAR String,
+ IN PSEARCH_LIST SearchList,
+ OUT PDWORD pValue
+ )
+/*++
+
+Routine Description:
+
+ Examines search list until it find the correct entry, then returns
+ the value corresponding to this entry.
+
+Arguments:
+
+ String - string to match
+ SearchList - array of entries containing valid strings
+ pValue - value corresponding to a matching valid string
+
+Return Value:
+
+ TRUE a matching entry was found
+ FALSE otherwise
+
+--*/
+{
+ if ( SearchList != NULL) {
+
+ for ( NOTHING; SearchList->String != NULL; SearchList++) {
+
+ if ( _wcsicmp( String, SearchList->String) == 0) {
+ *pValue = SearchList->Value;
+ return( TRUE) ;
+ }
+ }
+ }
+ return( FALSE) ;
+}
+
+
+
+VOID
+Usage(
+ BOOL GoodCommand
+ )
+/*++
+
+Routine Description:
+
+ Usage of AT command.
+
+Arguments:
+
+ GoodCommand - TRUE if we have a good command input (request for help)
+ FALSE if we have a bad command input
+
+Return Value:
+
+ None.
+
+--*/
+{
+ if ( GlobalErrorReported == TRUE) {
+ PutNewLine();
+ } else if ( GoodCommand == FALSE) {
+ MessagePrint( IDS_INVALID_COMMAND );
+ }
+
+ MessagePrint( IDS_USAGE );
+}
+
+#define REG_SCHEDULE_PARMS TEXT("System\\CurrentControlSet\\Services\\Schedule\\Parameters")
+
+#define REG_SCHEDULE_USE_OLD TEXT("UseOldParsing")
+
+BOOL
+UseOldParsing()
+/*++
+
+Routine Description:
+
+ Checks the registry for
+
+ HKLM\CurrentControlSet\Services\Schedule\parameters\UseOldParsing
+
+ If present and equal to 1, then revert to 3.51 level of command line
+ parsing. Spaces in filenames will not work with this option. This is
+ intended as a migration path for customers who cannot change all their
+ command scripts that use AT.EXE right away.
+
+--*/
+{
+ BOOL fUseOld = FALSE;
+ LONG err = 0;
+
+ do { // Error breakout loop
+
+ HKEY hkeyScheduleParms;
+ DWORD dwType;
+ DWORD dwData = 0;
+ DWORD cbData = sizeof(dwData);
+
+ // Break out on any error and use the default, FALSE.
+
+ if (err = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
+ REG_SCHEDULE_PARMS,
+ 0,
+ KEY_READ,
+ &hkeyScheduleParms))
+ {
+ break;
+ }
+ if (err = RegQueryValueEx(hkeyScheduleParms,
+ REG_SCHEDULE_USE_OLD,
+ NULL,
+ &dwType,
+ (LPBYTE)&dwData,
+ &cbData ))
+ {
+ RegCloseKey( hkeyScheduleParms );
+ break;
+ }
+
+ if ( dwType == REG_DWORD && dwData == 1 )
+ {
+ fUseOld = TRUE;
+ }
+
+ RegCloseKey( hkeyScheduleParms );
+
+
+ } while (FALSE) ;
+
+ return fUseOld;
+
+}
+
+
+
+BOOL
+ValidateCommand(
+ IN int argc,
+ IN WCHAR ** argv,
+ OUT int * pCommand
+ )
+/*++
+
+Routine Description:
+
+ Examines command line to see what to do. This validates the command
+ line passed into the AT command processor. If this routine finds any
+ invalid data, the program exits with an appropriate error message.
+
+Arguments:
+
+ pCommand - pointer to command
+ argc - count of arguments
+ argv - pointer to table of arguments
+
+Return Value:
+
+ FALSE - if failure, i.e. command will not be executed
+ TRUE - if success
+
+Comment:
+
+ Parsing assumes:
+
+ non-switch (positional) parameters come first and order among these
+ parameters is important
+
+ switch parameters come second and order among these parameters is
+ NOT important
+
+ command (if present) comes last
+
+--*/
+{
+ int i; // loop index
+ int next; // index of Time or JobId argument
+ int argno; // where to start in arg string
+ BOOL DeleteFound; // did we find a delete switch
+ WCHAR * recdatap; // ptr used to build atr_command
+ DWORD recdata_len; // len of arg to put in atr_command
+ DWORD JobTime;
+ WCHAR wszTmp[MAX_COMMAND_LEN];
+ BOOL fUseOldParsing = FALSE;
+
+ if (argc == 1) {
+ *pCommand = DUMP_ALL;
+ return( TRUE);
+ }
+
+ // First look for a help switch on the command line.
+
+ for ( i = 1; i < argc; i++ ) {
+
+ if ( !_wcsicmp( argv[i], QUESTION_SW)
+ || !_wcsicmp( argv[i], QUESTION_SW_TOO)) {
+
+ *pCommand = ACTION_USAGE;
+ return( TRUE);
+ }
+ }
+
+ next = ( ArgIsServerName( argv[ 1]) == TRUE) ? 2 : 1;
+ if ( argc == next) {
+ *pCommand = DUMP_ALL;
+ return( TRUE);
+ }
+
+ if ( (ArgIsDecimalString( argv[ next], &GlobalJobId)) == TRUE) {
+
+ if ( argc == next + 1) {
+ *pCommand = DUMP_ID;
+ return( TRUE);
+ }
+
+ if ( ParseJobIdArgs( argv, argc, next + 1, &DeleteFound) == FALSE) {
+ return( FALSE); // an invalid argument
+ }
+
+ *pCommand = (DeleteFound == FALSE) ? DUMP_ID : DEL_ID;
+ return( TRUE);
+ }
+
+ //
+ // Try some variation of "AT [\\ServerName [/DELETE]"
+ //
+ if ( ParseJobIdArgs( argv, argc, next, &DeleteFound) == TRUE) {
+ *pCommand = (DeleteFound == FALSE) ? DUMP_ALL : DEL_ALL;
+ return( TRUE);
+ }
+
+ if ( ArgIsTime( argv[ next], &JobTime) == TRUE) {
+
+ *pCommand = ADD_TO_SCHEDULE;
+
+ if ( argc < next + 2) {
+ return( FALSE); // need something to do, not just time
+ }
+
+ memset( (PBYTE)&GlobalAtInfo, '\0', sizeof(GlobalAtInfo)); // initialize
+ GlobalAtInfo.Flags |= JOB_NONINTERACTIVE; // the default
+
+ if ( ParseInteractiveArg( argv[ next + 1])) {
+ next++;
+ }
+ if ( argc < next + 2) {
+ return( FALSE); // once more with feeling
+ }
+
+ if ( ParseTimeArgs( argv, argc, next + 1, &argno) == FALSE) {
+ return( FALSE);
+ }
+
+ // Copy argument strings to record.
+
+ recdatap = GlobalAtInfo.Command = GlobalAtInfoCommand;
+ recdata_len = 0;
+
+ fUseOldParsing = UseOldParsing();
+
+ for ( i = argno; i < argc; i++) {
+
+ DWORD temp;
+
+ //
+ // Fix for bug 22068 "AT command does not handle filenames with
+ // spaces." The command processor takes a quoted command line arg
+ // and puts everything between the quotes into one argv string.
+ // The quotes are stripped out. Thus, if any of the string args
+ // contain whitespace, then they must be requoted before being
+ // concatonated into the command value.
+ //
+ if (!fUseOldParsing && wcschr(argv[i], L' ') != NULL)
+ {
+ wcscpy(wszTmp, L"\"");
+ wcscat(wszTmp, argv[i]);
+ wcscat(wszTmp, L"\"");
+ }
+ else
+ {
+ wcscpy(wszTmp, argv[i]);
+ }
+
+ temp = wcslen(wszTmp) + 1;
+
+ recdata_len += temp;
+
+ if ( recdata_len > MAX_COMMAND_LEN) {
+ MessagePrint( APE_AT_COMMAND_TOO_LONG );
+ return( FALSE);
+ }
+
+ wcscpy( recdatap, wszTmp);
+ recdatap += temp;
+
+ // To construct lpszCommandLine argument to CreateProcess call
+ // we replace nuls with spaces.
+
+ *(recdatap - 1) = BLANK;
+
+ }
+
+ // Reset space back to null on last argument in string.
+
+ *(recdatap - 1) = NULLC;
+ GlobalAtInfo.JobTime = JobTime;
+ return( TRUE);
+ }
+
+ return( FALSE);
+}
+
+
+VOID
+GetTimeString(
+ DWORD Time,
+ WCHAR *Buffer,
+ int BufferLength
+ )
+/*++
+
+Routine Description:
+
+ This function converts a dword time to an ASCII string.
+
+Arguments:
+
+ Time - Time difference in dword from start of the day (i.e. 12am
+ midnight ) in milliseconds
+
+ Buffer - Pointer to the buffer to place the ASCII representation.
+
+ BufferLength - The length of buffer in bytes.
+
+Return Value:
+
+ None.
+
+--*/
+#define MINUTES_IN_HOUR 60
+#define SECONDS_IN_MINUTE 60
+{
+ WCHAR szTimeString[MAX_TIME_SIZE];
+ WCHAR *p = &szTimeString[1];
+ DWORD seconds, minutes, hours;
+ int numChars;
+ DWORD flags;
+ SYSTEMTIME st;
+
+ GetSystemTime(&st);
+ *p = NULLC;
+
+ // Check if the time format is initialized. If not, initialize it.
+ if ( GlobalTimeFormat.AMString == NULL )
+ NetpGetTimeFormat( &GlobalTimeFormat );
+
+ // Convert the time to hours, minutes, seconds
+ seconds = (Time/1000);
+ hours = seconds / (MINUTES_IN_HOUR * SECONDS_IN_MINUTE );
+ seconds -= hours * MINUTES_IN_HOUR * SECONDS_IN_MINUTE;
+ minutes = seconds / SECONDS_IN_MINUTE;
+ seconds -= minutes * SECONDS_IN_MINUTE;
+
+ st.wHour = (WORD)(hours);
+ st.wMinute = (WORD)(minutes);
+ st.wSecond = (WORD)(seconds);
+ st.wMilliseconds = 0;
+
+ flags = TIME_NOSECONDS;
+ if (!GlobalTimeFormat.TwelveHour)
+ flags |= TIME_FORCE24HOURFORMAT;
+
+ numChars = GetTimeFormatW(GetThreadLocale(),
+ flags, &st, NULL, p, MAX_TIME_SIZE-1);
+
+ if ( numChars > BufferLength )
+ numChars = BufferLength;
+
+ if (*(p+1) == ARG_SEP_CHR && GlobalTimeFormat.LeadingZero) {
+ *(--p) = TEXT('0');
+ numChars++;
+ }
+ wcsncpy( Buffer, p, numChars );
+ // Append spece for align print format. column based.
+ {
+ DWORD ColumnLength;
+
+ // character counts -> array index.
+ numChars--;
+
+ ColumnLength = GetStringColumn( Buffer );
+
+ while( ColumnLength++ < MAX_TIME_FIELD_LENGTH) {
+ Buffer[ numChars++] = BLANK;
+ }
+ Buffer[ numChars] = UNICODE_NULL;
+ }
+}
+
+
+BOOL
+InitList( PSEARCH_LIST SearchList )
+{
+ if ( SearchList != NULL) {
+
+ if ( SearchList->String != NULL ) // Already initialized
+ return TRUE;
+
+ for ( NOTHING; SearchList->MessageId != 0; SearchList++) {
+ if ( MessageGet( SearchList->MessageId,
+ &SearchList->String,
+ 0 ) == 0 )
+ {
+ return FALSE;
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+
+VOID
+TermList( PSEARCH_LIST SearchList )
+{
+ if ( SearchList != NULL) {
+
+ if ( SearchList->String == NULL ) // Not initialized
+ return;
+
+ for ( NOTHING; SearchList->String != NULL; SearchList++) {
+ LocalFree( SearchList->String );
+ }
+ }
+}
+
+
+DWORD
+GetStringColumn( WCHAR *lpwstr )
+{
+ int cchNeed;
+
+ cchNeed = WideCharToMultiByte( GetConsoleOutputCP() , 0 ,
+ lpwstr , -1 ,
+ NULL , 0 ,
+ NULL , NULL );
+
+ return( (DWORD) cchNeed - 1 ); // - 1 : remove NULL
+}
diff --git a/private/net/svcdlls/at/atcmd/lmatmsg.mc b/private/net/svcdlls/at/atcmd/lmatmsg.mc
new file mode 100644
index 000000000..edd658fcf
--- /dev/null
+++ b/private/net/svcdlls/at/atcmd/lmatmsg.mc
@@ -0,0 +1,193 @@
+;/*++
+;
+;Copyright (c) 1992 Microsoft Corporation
+;
+;Module Name:
+;
+; lmatmsg.h
+;
+;Abstract:
+;
+; Definitions for AT command private errors.
+;
+;Author:
+;
+; Vladimir Z. Vulovic (vladimv) 06 - November - 1992
+;
+;Revision History:
+;
+; 06-Nov-1992 vladimv
+; Created
+; 20-Feb-1993 yihsins
+; Move hard-coded strings from atcmd.c to here.
+;
+;Notes:
+;
+; This file is generated by the MC tool from the lmatmsg.mc file.
+;
+;
+; In lanman2.1 and before, messages for AT utility used to be defined in
+; common\h\apperr2.h. In NT windows, error codes belonging to AT utility
+; have been reused by redir statistics. Therefore, we have to invent a
+; whole new message file for these lost messages!
+;
+; Note that %1 was removed from messages for APE2_AT_EACH & APE2_AT_NEXT.
+;
+;--*/
+
+MessageId=4630 SymbolicName=APE2_AT_DEL_WARNING
+Language=English
+This operation will delete all scheduled jobs.
+.
+
+MessageId=+1 SymbolicName=APE2_AT_SCHED_FILE_CLEARED
+Language=English
+The AT schedule file was cleared.
+.
+
+MessageId=+1 SymbolicName=APE2_AT_TODAY
+Language=English
+Today %0
+.
+
+MessageId=+1 SymbolicName=APE2_AT_TOMORROW
+Language=English
+Tomorrow %0
+.
+
+MessageId=+1 SymbolicName=APE2_AT_NEXT
+Language=English
+Next %0
+.
+
+MessageId=+1 SymbolicName=APE2_AT_EACH
+Language=English
+Each %0
+.
+
+;// ++++++++++++++++++++++++++++ WARNING ++++++++++++++++++++++++++
+;//
+;// The following messages up to APE2_AT_DI_INTERACTIVE are aligned to
+;// look right when printed on the screen (they appear in a table).
+;// If they are changed, make sure they occupy the same number of
+;// chars (text+space). In the case of AT_DUMP_HEADER, the column
+;// headers should start at the same place (left justified).
+;//
+;// ---------------------------------------------------------------
+
+
+MessageId=+1 SymbolicName=APE2_AT_DUMP_HEADER
+Language=English
+Status ID Day Time Command Line
+.
+
+MessageId=+1 SymbolicName=APE2_AT_DI_TASK
+Language=English
+Task ID: %0
+.
+
+MessageId=+1 SymbolicName=APE2_AT_DI_STATUS
+Language=English
+Status: %0
+.
+
+MessageId=+1 SymbolicName=APE2_AT_DI_SCHEDULE
+Language=English
+Schedule: %0
+.
+
+MessageId=+1 SymbolicName=APE2_AT_DI_TIMEOFDAY
+Language=English
+Time of day: %0
+.
+
+MessageId=+1 SymbolicName=APE2_AT_DI_COMMAND
+Language=English
+Command: %0
+.
+
+MessageId=+1 SymbolicName=APE2_AT_DI_INTERACTIVE
+Language=English
+Interactive: %0
+.
+
+MessageId=10000 SymbolicName=IDS_LOAD_LIBRARY_FAILURE
+Language=English
+LoadLibrary() fails with winError = %1!d!
+.
+MessageId=10001 SymbolicName=IDS_UNABLE_TO_MAP_TO_UNICODE
+Language=English
+Failed to convert input strings to unicode strings.
+.
+MessageId=10002 SymbolicName=IDS_UNABLE_TO_MAKE_STRING_TO_UNICODE
+Language=English
+Failed to make unicode string out of %1.
+.
+MessageId=10003 SymbolicName=IDS_ADD_NEW_JOB
+Language=English
+Added a new job with job ID = %1!d!
+.
+MessageId=10004 SymbolicName=IDS_MESSAGE_GET_ERROR
+Language=English
+FormatMessage(%1!d!) fails with winError = %2!d!
+.
+MessageId=10005 SymbolicName=IDS_MESSAGE_PRINT_ERROR
+Language=English
+FormatMessage(%1!d!) fails with winError = %2!d!
+.
+MessageId=10006 SymbolicName=IDS_INVALID_COMMAND
+Language=English
+Invalid command.%n
+.
+MessageId=10007 SymbolicName=IDS_YES
+Language=English
+YES%0
+.
+MessageId=10008 SymbolicName=IDS_DELETE
+Language=English
+DELETE%0
+.
+MessageId=10009 SymbolicName=IDS_EVERY
+Language=English
+EVERY%0
+.
+MessageId=10010 SymbolicName=IDS_NEXT
+Language=English
+NEXT%0
+.
+MessageId=10011 SymbolicName=IDS_NO
+Language=English
+NO%0
+.
+MessageId=10014 SymbolicName=IDS_USAGE
+Language=English
+The AT command schedules commands and programs to run on a computer at
+a specified time and date. The Schedule service must be running to use
+the AT command.%n
+AT [\\computername] [ [id] [/DELETE] | /DELETE [/YES]]
+AT [\\computername] time [/INTERACTIVE]
+ [ /EVERY:date[,...] | /NEXT:date[,...]] "command"%n
+\\computername Specifies a remote computer. Commands are scheduled on the
+ local computer if this parameter is omitted.
+id Is an identification number assigned to a scheduled
+ command.
+/delete Cancels a scheduled command. If id is omitted, all the
+ scheduled commands on the computer are canceled.
+/yes Used with cancel all jobs command when no further
+ confirmation is desired.
+time Specifies the time when command is to run.
+/interactive Allows the job to interact with the desktop of the user
+ who is logged on at the time the job runs.
+/every:date[,...] Runs the command on each specified day(s) of the week or
+ month. If date is omitted, the current day of the month
+ is assumed.
+/next:date[,...] Runs the specified command on the next occurrence of the
+ day (for example, next Thursday). If date is omitted, the
+ current day of the month is assumed.
+"command" Is the Windows NT command, or batch program to be run.%n
+.
+MessageId=10015 SymbolicName=IDS_INTERACTIVE
+Language=English
+INTERACTIVE%0
+.
+ \ No newline at end of file
diff --git a/private/net/svcdlls/at/atcmd/makefile b/private/net/svcdlls/at/atcmd/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/net/svcdlls/at/atcmd/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/net/svcdlls/at/atcmd/makefile.inc b/private/net/svcdlls/at/atcmd/makefile.inc
new file mode 100644
index 000000000..5157fa663
--- /dev/null
+++ b/private/net/svcdlls/at/atcmd/makefile.inc
@@ -0,0 +1,4 @@
+lmatmsg.rc: msg00001.bin
+
+lmatmsg.h msg00001.bin: lmatmsg.mc
+ mc -v lmatmsg.mc
diff --git a/private/net/svcdlls/at/atcmd/sources b/private/net/svcdlls/at/atcmd/sources
new file mode 100644
index 000000000..d931aa3e6
--- /dev/null
+++ b/private/net/svcdlls/at/atcmd/sources
@@ -0,0 +1,32 @@
+MAJORCOMP = bozo
+MINORCOMP = bozo
+
+TARGETNAME=at
+
+TARGETPATH=obj
+TARGETTYPE=PROGRAM
+
+INCLUDES=..;..\..\..\inc;..\..\..\..\inc
+
+C_DEFINES=-DRPC_NO_WINDOWS_H -DUNICODE -D_UNICODE
+
+USE_CRTDLL=1
+
+WARNING_LEVEL=-W4
+
+SOURCES= \
+ atcmd.c \
+ at.rc
+
+UMTYPE=console
+
+TARGETLIBS= \
+ $(BASEDIR)\public\sdk\lib\*\ntdll.lib \
+ $(BASEDIR)\Public\Sdk\Lib\*\netlib.lib \
+ $(BASEDIR)\public\sdk\lib\*\netapi32.lib \
+ $(BASEDIR)\Public\Sdk\Lib\*\shell32.lib \
+ $(BASEDIR)\public\sdk\lib\*\user32.lib
+
+NTTARGETFILE0= \
+ lmatmsg.h \
+ lmatmsg.mc
diff --git a/private/net/svcdlls/at/atnames.h b/private/net/svcdlls/at/atnames.h
new file mode 100644
index 000000000..a7afaa316
--- /dev/null
+++ b/private/net/svcdlls/at/atnames.h
@@ -0,0 +1,24 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ atnames.h
+
+Abstract:
+
+ Private header file which defines the SCHEDULE service names.
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 06 - November - 1992
+
+Revision History:
+
+ 06-Nov-1992 vladimv
+ Created
+
+--*/
+
+#define AT_INTERFACE_NAME TEXT("atsvc")
diff --git a/private/net/svcdlls/at/atsvc.acf b/private/net/svcdlls/at/atsvc.acf
new file mode 100644
index 000000000..0d07370ee
--- /dev/null
+++ b/private/net/svcdlls/at/atsvc.acf
@@ -0,0 +1,11 @@
+[ implicit_handle( handle_t atsvc_handle)]
+
+interface atsvc
+
+{
+
+typedef [allocate(all_nodes)] LPAT_INFO;
+typedef [allocate(all_nodes)] LPAT_ENUM;
+
+}
+ \ No newline at end of file
diff --git a/private/net/svcdlls/at/atsvc.idl b/private/net/svcdlls/at/atsvc.idl
new file mode 100644
index 000000000..be227eec4
--- /dev/null
+++ b/private/net/svcdlls/at/atsvc.idl
@@ -0,0 +1,127 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ atsvc.idl
+
+Abstract:
+
+ This is the IDL file that describes the RPC interface for the (remotable)
+ APIs that reside in the schedule == scheduler == at == job service.
+ This consists of the following APIs:
+
+ NetJobEnum
+ NetJobAdd
+ NetJobDel
+ NetJobControl - BUGBUG not implemented yet
+
+ Also contains the RPC specific data structures for these APIs.
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 06 - November - 1992
+
+Environment:
+
+ User Mode - Win32 - MIDL
+
+Revision History:
+
+ 06-Nov-1992 vladimv
+ Created
+
+--*/
+
+//
+// Interface Attributes
+//
+
+[
+ uuid(1FF70682-0A51-30E8-076D-740BE8CEE98B),
+ version(1.0),
+#ifdef __midl
+ ms_union,
+#endif // __midl
+ pointer_default(unique)
+]
+
+
+//
+// Interface Keyword
+//
+
+interface atsvc
+
+
+//
+// Interface Body
+//
+
+{
+
+import "imports.idl";
+#include <lmcons.h>
+
+//
+// BUGBUG - take this definition out when midl understands LPWSTR etc
+//
+
+#ifdef UNICODE
+#define LPTSTR wchar_t *
+#endif
+
+
+//
+// ---------------------------------------------------------------//
+//
+
+
+typedef [handle] LPCWSTR ATSVC_HANDLE;
+
+
+//
+// Schedule service data structures.
+//
+
+typedef struct _AT_ENUM_CONTAINER {
+ DWORD EntriesRead;
+ [size_is( EntriesRead)] LPAT_ENUM Buffer;
+} AT_ENUM_CONTAINER, *PAT_ENUM_CONTAINER, *LPAT_ENUM_CONTAINER;
+
+
+//
+// Schedule service API-s.
+//
+
+NET_API_STATUS
+NetrJobAdd(
+ [in,string,unique] ATSVC_HANDLE ServerName,
+ [in] LPAT_INFO pAtInfo,
+ [out] LPDWORD pJobId
+ );
+
+NET_API_STATUS
+NetrJobDel(
+ [in,string,unique] ATSVC_HANDLE ServerName,
+ [in] DWORD MinJobId,
+ [in] DWORD MaxJobId
+ );
+
+NET_API_STATUS
+NetrJobEnum(
+ [in,string,unique] ATSVC_HANDLE ServerName,
+ [in,out] LPAT_ENUM_CONTAINER pEnumContainer,
+ [in] DWORD PreferedMaximumLength,
+ [out] LPDWORD pTotalEntries,
+ [in,out,unique] LPDWORD pResumeHandle
+ );
+
+NET_API_STATUS
+NetrJobGetInfo(
+ [in,string,unique] ATSVC_HANDLE ServerName,
+ [in] DWORD JobId,
+ [out] LPAT_INFO * ppAtInfo
+ );
+}
diff --git a/private/net/svcdlls/at/client/atbind.c b/private/net/svcdlls/at/client/atbind.c
new file mode 100644
index 000000000..61037ab23
--- /dev/null
+++ b/private/net/svcdlls/at/client/atbind.c
@@ -0,0 +1,113 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ atbind.c
+
+Abstract:
+
+ Routines which use RPC to bind and unbind the client to the schedule
+ service.
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 06 - November - 1992
+
+Environment:
+
+ User Mode -Win32
+
+Revision History:
+
+ 06-Nov-1992 vladimv
+ Created
+
+--*/
+
+#include "atclient.h"
+
+
+handle_t
+ATSVC_HANDLE_bind(
+ ATSVC_HANDLE ServerName
+ )
+
+/*++
+
+Routine Description:
+
+ This routine calls a common bind routine that is shared by all services.
+ This routine is called from the schedule service client stubs when
+ it is necessary to bind to a server.
+
+Arguments:
+
+ ServerName - A pointer to a string containing the name of the server
+ to bind with.
+
+Return Value:
+
+ The binding handle is returned to the stub routine. If the bind is
+ unsuccessful, a NULL will be returned.
+
+--*/
+{
+ handle_t BindingHandle;
+ RPC_STATUS RpcStatus;
+
+ RpcStatus = NetpBindRpc (
+ (LPTSTR)ServerName,
+ AT_INTERFACE_NAME,
+ 0,
+ &BindingHandle
+ );
+
+#ifdef DEBUG
+ if ( RpcStatus != ERRROR_SUCCESS) {
+ DbgPrint("ATSVC_HANDLE_bind:NetpBindRpc RpcStatus=%d\n",RpcStatus);
+ }
+ DbgPrint("ATSVC_HANDLE_bind: handle=%d\n", BindingHandle);
+#endif
+
+ return( BindingHandle);
+}
+
+
+
+void
+ATSVC_HANDLE_unbind(
+ ATSVC_HANDLE ServerName,
+ handle_t BindingHandle
+ )
+
+/*++
+
+Routine Description:
+
+ This routine calls a common unbind routine that is shared by all services.
+ This routine is called from the Workstation service client stubs when it is
+ necessary to unbind from the server end.
+
+Arguments:
+
+ ServerName - This is the name of the server from which to unbind.
+
+ BindingHandle - This is the binding handle that is to be closed.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ UNREFERENCED_PARAMETER( ServerName);
+
+#ifdef DEBUG
+ DbgPrint(" ATSVC_HANDLE_unbind: handle= 0x%x\n", BindingHandle);
+#endif // DEBUG
+
+ NetpUnbindRpc( BindingHandle);
+}
+
diff --git a/private/net/svcdlls/at/client/atclient.h b/private/net/svcdlls/at/client/atclient.h
new file mode 100644
index 000000000..ddda6b701
--- /dev/null
+++ b/private/net/svcdlls/at/client/atclient.h
@@ -0,0 +1,44 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ atclient.h
+
+Abstract:
+
+ Private header file for the client end of the schedule service
+ modules.
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 06 - November - 1992
+
+Revision History:
+
+ 06-Nov-1992 vladimv
+ Created
+
+--*/
+
+#include <nt.h> // DbgPrint prototype
+#include <ntrtl.h> // DbgPrint
+#include <nturtl.h> // Needed by winbase.h
+
+#include <windef.h> // DWORD
+#include <winbase.h> // LocalFree
+
+#include <rpc.h> // DataTypes and runtime APIs
+#include <rpcutil.h> // GENERIC_ENUM_STRUCT
+
+#include <lmcons.h> // NET_API_STATUS
+#include <lmerr.h> // NetError codes
+#include <lmremutl.h> // SUPPORTS_RPC
+
+#include <netlibnt.h> // NetpNtStatusToApiStatus
+#include <netdebug.h> // NetpDbgPrint
+
+#include <atsvc.h> // generated by the MIDL complier
+#include <atnames.h> // Service and interface names
+
diff --git a/private/net/svcdlls/at/client/atstub.c b/private/net/svcdlls/at/client/atstub.c
new file mode 100644
index 000000000..6b0c8d751
--- /dev/null
+++ b/private/net/svcdlls/at/client/atstub.c
@@ -0,0 +1,302 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ atstub.c
+
+Abstract:
+
+ Client stubs of the Schedule service APIs.
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 06 - November - 1992
+
+Environment:
+
+ User Mode - Win32
+
+Revision History:
+
+ 06-Nov-1992 vladimv
+ Created
+
+--*/
+
+#include "atclient.h"
+#undef IF_DEBUG // avoid wsclient.h vs. debuglib.h conflicts.
+#include <debuglib.h> // IF_DEBUG() (needed by netrpc.h).
+#include <lmserver.h>
+#include <lmsvc.h>
+#include <netlib.h> // NetpServiceIsStarted() (needed by netrpc.h).
+#include <netrpc.h> // NET_REMOTE macros.
+#include <lmstats.h>
+
+
+
+NET_API_STATUS
+NetScheduleJobAdd(
+ IN LPCWSTR ServerName OPTIONAL,
+ IN LPBYTE Buffer,
+ OUT LPDWORD pJobId
+ )
+/*++
+
+Routine Description:
+
+ This is the DLL entrypoint for NetScheduleJobAdd. This API adds a job
+ to the schedule.
+
+Arguments:
+
+ ServerName - Pointer to a string containing the name of the computer
+ that is to execute the API function.
+
+ Buffer - Pointer to a buffer containing information about the job
+
+ pJobId - Pointer to JobId of a newly added job.
+
+
+Return Value:
+
+ NET_API_STATUS
+
+--*/
+{
+ NET_API_STATUS status;
+
+ NET_REMOTE_TRY_RPC
+
+ //
+ // Try RPC (local or remote) version of API.
+ //
+ status = NetrJobAdd(
+ ServerName,
+ (LPAT_INFO)Buffer,
+ pJobId
+ );
+
+ NET_REMOTE_RPC_FAILED(
+ "NetScheduleJobAdd",
+ ServerName,
+ status,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_SCHEDULE
+ )
+
+ status = ERROR_NOT_SUPPORTED;
+
+ NET_REMOTE_END
+
+ return( status);
+
+} // NetScheduleJobAdd
+
+
+NET_API_STATUS
+NetScheduleJobDel(
+ IN LPCWSTR ServerName OPTIONAL,
+ IN DWORD MinJobId,
+ IN DWORD MaxJobId
+ )
+/*++
+
+Routine Description:
+
+ This is the DLL entrypoint for NetScheduleJobDel. This API removes
+ from the schedule all jobs whose job ids are:
+
+ - greater than or equal to the minimum job id
+
+ and
+
+ - less than or equal to the maximum job id
+
+Arguments:
+
+ ServerName - Pointer to a string containing the name of the computer
+ that is to execute the API function.
+
+ MinJobId - minumum job id
+
+ MaxJobId - maxumum job id
+
+Return Value:
+
+ NET_API_STATUS
+
+--*/
+{
+ NET_API_STATUS status;
+
+ NET_REMOTE_TRY_RPC
+
+ //
+ // Try RPC (local or remote) version of API.
+ //
+ status = NetrJobDel(
+ ServerName,
+ MinJobId,
+ MaxJobId
+ );
+
+ NET_REMOTE_RPC_FAILED(
+ "NetScheduleJobDel",
+ ServerName,
+ status,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_SCHEDULE
+ )
+
+ status = ERROR_NOT_SUPPORTED;
+
+ NET_REMOTE_END
+
+ return( status);
+
+} // NetScheduleJobDel
+
+
+NET_API_STATUS
+NetScheduleJobEnum(
+ IN LPCWSTR ServerName OPTIONAL,
+ OUT LPBYTE * PointerToBuffer,
+ IN DWORD PreferredMaximumLength,
+ OUT LPDWORD EntriesRead,
+ OUT LPDWORD TotalEntries,
+ IN OUT LPDWORD ResumeHandle
+ )
+/*++
+
+Routine Description:
+
+ This is the DLL entrypoint for NetScheduleJobEnum. This API enumarates
+ all jobs in the schedule.
+
+Arguments:
+
+ ServerName - Pointer to a string containing the name of the computer
+ that is to execute the API function.
+
+ PointerToBuffer - Pointer to location where pointer to returned data will
+ be stored
+
+ PreferredMaximumLength - Indicates a maximum size limit that the caller
+ will allow for the return buffer.
+
+ EntriesRead - A pointer to the location where the number of entries
+ (data structures)read is to be returned.
+
+ TotalEntries - A pointer to the location which upon return indicates
+ the total number of entries in the table.
+
+ ResumeHandle - Pointer to a value that indicates where to resume
+ enumerating data.
+
+Return Value:
+
+ NET_API_STATUS
+
+--*/
+{
+ NET_API_STATUS status;
+ AT_ENUM_CONTAINER EnumContainer;
+
+ EnumContainer.EntriesRead = 0;
+ EnumContainer.Buffer = NULL;
+
+ NET_REMOTE_TRY_RPC
+
+ //
+ // Try RPC (local or remote) version of API.
+ //
+ status = NetrJobEnum(
+ ServerName,
+ &EnumContainer,
+ PreferredMaximumLength,
+ TotalEntries,
+ ResumeHandle
+ );
+
+ if ( status == NERR_Success || status == ERROR_MORE_DATA) {
+ *EntriesRead = EnumContainer.EntriesRead;
+ *PointerToBuffer = (LPBYTE)EnumContainer.Buffer;
+ }
+
+ NET_REMOTE_RPC_FAILED(
+ "NetScheduleJobEnum",
+ ServerName,
+ status,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_SCHEDULE
+ )
+
+ status = ERROR_NOT_SUPPORTED;
+
+ NET_REMOTE_END
+
+ return( status);
+
+} // NetScheduleJobEnum
+
+
+NET_API_STATUS
+NetScheduleJobGetInfo(
+ IN LPCWSTR ServerName OPTIONAL,
+ IN DWORD JobId,
+ OUT LPBYTE * PointerToBuffer
+ )
+/*++
+
+Routine Description:
+
+ This is the DLL entrypoint for NetScheduleGetInfo. This API obtains
+ information about a particular job in the schedule.
+
+Arguments:
+
+ ServerName - Pointer to a string containing the name of the computer
+ that is to execute the API function.
+
+ JobId - Id of job of interest.
+
+ PointerToBuffer - Pointer to location where pointer to returned data will
+ be stored
+
+Return Value:
+
+ NET_API_STATUS
+
+--*/
+{
+ NET_API_STATUS status;
+
+ NET_REMOTE_TRY_RPC
+
+ //
+ // Try RPC (local or remote) version of API.
+ //
+ status = NetrJobGetInfo(
+ ServerName,
+ JobId,
+ (LPAT_INFO *)PointerToBuffer
+ );
+
+ NET_REMOTE_RPC_FAILED(
+ "NetScheduleJobGetInfo",
+ ServerName,
+ status,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_SCHEDULE
+ )
+
+ status = ERROR_NOT_SUPPORTED;
+
+ NET_REMOTE_END
+
+ return( status);
+
+} // NetScheduleJobGetInfo
+
diff --git a/private/net/svcdlls/at/client/atstub.def b/private/net/svcdlls/at/client/atstub.def
new file mode 100644
index 000000000..2a1fc0caa
--- /dev/null
+++ b/private/net/svcdlls/at/client/atstub.def
@@ -0,0 +1,12 @@
+NAME ATSTUB
+
+DESCRIPTION 'ATSTUB'
+
+EXPORTS
+
+ NetScheduleJobAdd
+ NetScheduleJobDel
+ NetScheduleJobEnum
+ NetScheduleJobGetInfo
+
+DATA SINGLE SHARED
diff --git a/private/net/svcdlls/at/client/fmt.c b/private/net/svcdlls/at/client/fmt.c
new file mode 100644
index 000000000..440b705d9
--- /dev/null
+++ b/private/net/svcdlls/at/client/fmt.c
@@ -0,0 +1,163 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ fmt.c
+
+Abstract:
+
+ Example of a test code using FormatMessage() api.
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 06 - November - 1992
+
+Environment:
+
+ User Mode - Win32
+
+Revision History:
+
+ 06-Nov-1992 vladimv
+ Created
+
+--*/
+
+#include "atclient.h"
+#include <stdlib.h> // exit()
+#include <stdio.h> // printf
+#include <wchar.h> // wcslen
+// #include <tstring.h> // wcslen
+#include <apperr.h> // APE_AT_USAGE
+#include <apperr2.h> // APE2_GEN_MONDAY + APE2_*
+#include <lmapibuf.h> // NetApiBufferFree
+
+
+BOOL
+DecimalStringToDword(
+ IN CHAR * pDecimalString,
+ OUT PDWORD pNumber
+ )
+/*++
+
+ This routine converts a string into a DWORD if it possibly can.
+ The conversion is successful if string is decimal numeric and
+ does not lead to an overflow.
+
+ pDecimalString ptr to decimal string
+ pNumber ptr to number
+
+ FALSE invalid number
+ TRUE valid number
+
+--*/
+{
+ DWORD Value;
+ DWORD OldValue;
+ DWORD digit;
+
+ if ( pDecimalString == NULL || *pDecimalString == 0) {
+ return( FALSE);
+ }
+
+ Value = 0;
+
+ while ( (digit = *pDecimalString++) != 0) {
+
+ if ( digit < L'0' || digit > L'9') {
+ return( FALSE); // not a decimal string
+ }
+
+ OldValue = Value;
+ Value = digit - L'0' + 10 * Value;
+ if ( Value < OldValue) {
+ return( FALSE); // overflow
+ }
+ }
+
+ *pNumber = Value;
+ return( TRUE);
+}
+
+
+VOID _CRTAPI1
+main(
+ int argc,
+ CHAR ** argv
+ )
+{
+ LPVOID lpSource;
+ DWORD length;
+ WCHAR buffer[ 256];
+ BOOL success;
+ DWORD number;
+ WCHAR * strings[ 1];
+
+ if ( argc != 2) {
+ printf( "%s", "Usage: fmt error_number\n");
+ exit( -1);
+ }
+
+
+ success = DecimalStringToDword( argv[ 1], &number);
+ if ( success != TRUE) {
+ printf( "Usage: fmt decimal_string_for_error_number\n");
+ exit( -1);
+ }
+
+ lpSource = LoadLibrary( L"netmsg.dll");
+ if ( lpSource == NULL) {
+ printf( " LoadLibrary() fails with winError = %d\n", GetLastError());
+ exit( -1);
+ }
+
+ length = FormatMessage(
+ FORMAT_MESSAGE_FROM_HMODULE | // dwFlags
+ FORMAT_MESSAGE_ARGUMENT_ARRAY,
+ lpSource,
+ number, // MessageId
+ 0, // dwLanguageId
+ buffer, // lpBuffer
+ sizeof( buffer), // nSize
+ NULL
+ );
+
+ if ( length == 0) {
+ printf( " FormatMessage() fails with winError = %d\n", GetLastError());
+ }
+
+ printf( "%s", buffer);
+
+ if ( number != APE2_GEN_DEFAULT_NO) {
+ goto exit;
+ }
+
+ strings[ 0] = buffer;
+
+ printf( "\nSpecial case of APE_GEN_DEFAULT_NO inserted in APE_OkToProceed\n");
+
+ length = FormatMessage(
+ FORMAT_MESSAGE_FROM_HMODULE | // dwFlags
+ FORMAT_MESSAGE_ARGUMENT_ARRAY,
+ lpSource,
+ APE_OkToProceed, // MessageId
+ 0, // dwLanguageId
+ buffer, // lpBuffer
+ sizeof( buffer), // nSize
+ (va_list *)strings // lpArguments
+ );
+
+ if ( length == 0) {
+ printf( " FormatMessage() fails with winError = %d\n", GetLastError());
+ }
+
+ printf( "%s", buffer);
+
+exit:
+ success = FreeLibrary( lpSource);
+ if ( success != TRUE) {
+ printf( " FreeLibrary() fails with winError = %d\n", GetLastError());
+ }
+}
diff --git a/private/net/svcdlls/at/client/handle.c b/private/net/svcdlls/at/client/handle.c
new file mode 100644
index 000000000..d381134ea
--- /dev/null
+++ b/private/net/svcdlls/at/client/handle.c
@@ -0,0 +1,182 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ handle.c
+
+Abstract:
+
+ Example of a test code for detached, background, no-window command
+ exectution.
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 06 - November - 1992
+
+Environment:
+
+ User Mode - Win32
+
+Revision History:
+
+ 06-Nov-1992 vladimv
+ Created
+
+--*/
+
+#include "atclient.h"
+#include <stdlib.h> // exit()
+#include <stdio.h> // printf
+#include <wincon.h> // FreeConsole()
+
+
+
+WCHAR *
+AsciizToUnicode(
+ IN CHAR * Asciiz
+ )
+/*++
+ No attempt is made to clean up if we fail half way along. Cleanup
+ will be done at the exit process time.
+--*/
+{
+ UNICODE_STRING UnicodeString;
+ BOOL success;
+
+ RtlInitUnicodeString(
+ &UnicodeString,
+ NULL
+ );
+
+ //
+ // This call will attempt to allocate memory for UNICODE string.
+ //
+
+ success = RtlCreateUnicodeStringFromAsciiz(
+ &UnicodeString,
+ Asciiz
+ );
+
+ if ( success != TRUE) {
+ printf( "Failed to make unicode string out of %s\n", Asciiz);
+ return( NULL);
+ }
+
+ return( UnicodeString.Buffer);
+}
+
+
+WCHAR **
+ArgvToUnicode(
+ IN int argc,
+ IN CHAR ** charArgv
+ )
+/*++
+ No attempt is made to clean up if we fail half way along. Cleanup
+ will be done at the exit process time.
+--*/
+{
+ WCHAR ** argv;
+ int index;
+
+ argv = (WCHAR **)LocalAlloc(
+ LMEM_FIXED,
+ argc * sizeof( WCHAR *)
+ );
+ if ( argv == NULL) {
+ return( NULL);
+ }
+
+ for ( index = 0; index < argc; index++ ) {
+
+ if ( ( argv[ index] = AsciizToUnicode( charArgv[ index])) == NULL) {
+ return( NULL);
+ }
+ }
+
+ return( argv);
+}
+
+
+VOID _CRTAPI1
+main(
+ int argc,
+ CHAR ** charArgv
+ )
+{
+#define THIRTY_SECONDS (30 * 1000)
+
+ PROCESS_INFORMATION ProcessInformation;
+ STARTUPINFO StartupInfo;
+ HANDLE FileHandle;
+ WCHAR ** argv;
+
+ if ( argc < 2 || argc > 3) {
+ printf(" Usage: handle command_to_execute [log_file_to_open]\n");
+ exit( -1);
+ }
+ if ( ( argv = ArgvToUnicode( argc, charArgv)) == NULL) {
+ printf( "Failed to map input strings to unicode.\n");
+ exit( -1);
+ }
+
+ if ( !FreeConsole()) {
+ KdPrint(( " FreeConsole() fails with WinError = %d\n", GetLastError()));
+ exit( -1);
+ }
+
+ FileHandle = CreateFile(
+ argc > 2 ? argv[ 2] : L"out.txt", // lpszName
+ GENERIC_READ | GENERIC_WRITE, // fdwAccess
+ FILE_SHARE_READ | FILE_SHARE_WRITE, // fdwShareMode
+ NULL, // lpsa
+ OPEN_ALWAYS, // fdwCreate
+ FILE_ATTRIBUTE_NORMAL, // fdwAttrAndFlags
+ NULL // hTemplateFile
+ );
+
+ if ( FileHandle == INVALID_HANDLE_VALUE) {
+ KdPrint(( " CreateFile() fails with WinError = %d\n", GetLastError()));
+ exit( -1);
+ }
+
+ if ( !SetStdHandle( STD_INPUT_HANDLE, FileHandle)) {
+ KdPrint(( " SetStdHandle( STDIN) fails with WinError = %d\n", GetLastError()));
+ exit( -1);
+ }
+
+ if ( !SetStdHandle( STD_OUTPUT_HANDLE, FileHandle)) {
+ KdPrint(( " SetStdHandle( STDOUT) fails with WinError = %d\n", GetLastError()));
+ exit( -1);
+ }
+
+ if ( !SetStdHandle( STD_ERROR_HANDLE, FileHandle)) {
+ KdPrint(( " SetStdHandle( STDERR) fails with WinError = %d\n", GetLastError()));
+ exit( -1);
+ }
+
+ GetStartupInfo( &StartupInfo);
+ StartupInfo.lpTitle = NULL;
+
+ if ( !CreateProcess(
+ NULL, // image name is imbedded in the command line
+ argv[ 1], // command line
+ NULL, // pSecAttrProcess
+ NULL, // pSecAttrThread
+ TRUE, // this process will not inherit our handles
+ DETACHED_PROCESS | CREATE_NO_WINDOW, // we have no access to the console either
+ NULL, // pEnvironment
+ NULL, // pCurrentDirectory
+ &StartupInfo,
+ &ProcessInformation
+ )) {
+ KdPrint(( " CreateProcess() fails with winError = %d\n", GetLastError()));
+ exit( -1);
+ }
+
+ WaitForSingleObject( ProcessInformation.hProcess, THIRTY_SECONDS);
+
+ exit( 0);
+}
diff --git a/private/net/svcdlls/at/client/makefile b/private/net/svcdlls/at/client/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/net/svcdlls/at/client/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/net/svcdlls/at/client/sources b/private/net/svcdlls/at/client/sources
new file mode 100644
index 000000000..aa79ef35c
--- /dev/null
+++ b/private/net/svcdlls/at/client/sources
@@ -0,0 +1,35 @@
+MAJORCOMP = net
+MINORCOMP = atclient
+
+
+NTPROFILEINPUT=yes
+
+TARGETNAME=atsvc
+TARGETPATH=obj
+TARGETTYPE=LIBRARY
+
+
+INCLUDES=..;..\..\..\inc;..\..\..\..\inc
+
+
+!IFNDEF DISABLE_NET_UNICODE
+UNICODE=1
+NET_C_DEFINES=-DUNICODE
+!ENDIF
+
+SOURCES=atstub.c \
+ atbind.c \
+ atsvc_c.c
+
+C_DEFINES=-DRPC_NO_WINDOWS_H
+
+WARNING_LEVEL=-W4
+
+UMTYPE=console
+UMTEST=fmt*handle
+UMLIBS= obj\*\atsvc.lib \
+ $(BASEDIR)\Public\Sdk\Lib\*\netlib.lib \
+ $(BASEDIR)\public\sdk\lib\*\netapi32.lib \
+ $(BASEDIR)\public\sdk\lib\*\rpcrt4.lib \
+ $(BASEDIR)\public\sdk\lib\*\rpcutil.lib \
+ $(BASEDIR)\public\sdk\lib\*\rpcndr.lib
diff --git a/private/net/svcdlls/at/dirs b/private/net/svcdlls/at/dirs
new file mode 100644
index 000000000..8daff9d2f
--- /dev/null
+++ b/private/net/svcdlls/at/dirs
@@ -0,0 +1,30 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ dirs.
+
+Abstract:
+
+ This file specifies the subdirectories of the current directory that
+ contain component makefiles.
+
+
+Author:
+
+ Steve Wood (stevewo) 17-Apr-1990
+
+NOTE: Commented description of this file is in \nt\bak\bin\dirs.tpl
+
+!ENDIF
+
+
+DIRS= server \
+ client \
+ atcmd
+
+
+#OPTIONAL_DIRS=dir8 \
+# dir9
diff --git a/private/net/svcdlls/at/imports.h b/private/net/svcdlls/at/imports.h
new file mode 100644
index 000000000..553783a2e
--- /dev/null
+++ b/private/net/svcdlls/at/imports.h
@@ -0,0 +1,41 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ imports.h
+
+Abstract:
+
+ This file allows us to include standard system header files in the
+ .idl file. The main .idl file imports a file called import.idl.
+ This allows the .idl file to use the types defined in these header
+ files. It also causes the following line to be added in the
+ MIDL generated header file:
+
+ #include "imports.h"
+
+ Thus these types are available to the RPC stub routines as well.
+
+Author:
+
+ Dan Lafferty (danl) 07-May-1991
+
+Revision History:
+
+
+--*/
+
+
+#include <windef.h>
+#include <lmcons.h>
+
+#ifdef MIDL_PASS
+#define LPSTR [string] LPSTR
+#define LPTSTR [string] wchar_t *
+#define LPWSTR [string] wchar_t *
+#define BOOL DWORD
+#endif // MIDL_PASS
+
+#include <lmat.h>
diff --git a/private/net/svcdlls/at/imports.idl b/private/net/svcdlls/at/imports.idl
new file mode 100644
index 000000000..78f31d3f4
--- /dev/null
+++ b/private/net/svcdlls/at/imports.idl
@@ -0,0 +1,67 @@
+/*++
+
+Copyright (c) 1991-1992 Microsoft Corporation
+
+Module Name:
+
+ imports.idl
+
+Abstract:
+
+ This file is useful for creating RPC interfaces that require the use
+ of windef types. The .idl file for the RPC product should contain a
+ line in the interface body that imports this file. For example:
+
+ import "imports.h";
+
+ Doing this causes the MIDL generated header file to contain the
+ following line:
+
+ #include "imports.h"
+
+ If this technique is not used, and instead the .idl file for the RPC
+ product simply contains #include <importsf.h>, then the contents of
+ imports.h will be expanded in the MIDL generated header file. This
+ can lead to duplicate definition problems later when the RPC client
+ or RPC server code needs to include both the MIDL generated header file
+ and a file that is included in imports.h.
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 06 - November - 1992
+
+Environment:
+
+ User Mode - Win32 - for use with the MIDL compiler
+
+Revision History:
+
+ 06-Nov-1992 vladimv
+ Created
+
+--*/
+
+[
+ uuid(12345678-1234-ABCD-EF00-9948756789AB),
+ version(1.0),
+#ifdef __midl
+ ms_union,
+#endif // __midl
+ pointer_default(unique)
+]
+interface imports
+
+{
+#define MIDL_PASS
+#include "imports.h"
+
+//
+// All .idl files need to contain at least one function prototype
+//
+
+DWORD
+Dummy(
+ [in] DWORD DummyParm);
+
+
+}
diff --git a/private/net/svcdlls/at/makefil0 b/private/net/svcdlls/at/makefil0
new file mode 100644
index 000000000..9104e0122
--- /dev/null
+++ b/private/net/svcdlls/at/makefil0
@@ -0,0 +1,62 @@
+#
+# This is the MIDL compile phase of the build process.
+#
+# The following is where you put the name of your .idl file without
+# the .idl extension:
+#
+
+!INCLUDE $(NTMAKEENV)\makefile.plt
+
+IDL_NAME = atsvc
+
+!IFNDEF DISABLE_NET_UNICODE
+UNICODE=1
+NET_C_DEFINES=-DUNICODE
+!ENDIF
+
+SDKINC = $(BASEDIR)\public\sdk\inc
+NETINC = $(BASEDIR)\private\net\inc
+SDKCRTINC = $(BASEDIR)\public\sdk\inc\crt
+PRIVINC = $(BASEDIR)\private\inc
+
+INCS = -I$(SDKINC) -I$(SDKCRTINC) -I$(PRIVINC) -I$(NETINC)
+
+CLIENT_TARGETS = client\$(IDL_NAME)_c.c \
+ .\$(IDL_NAME).h
+
+SERVER_TARGETS = server\$(IDL_NAME)_s.c
+
+EXTRN_DEPENDS = $(SDKINC)\lmcons.h \
+ $(SDKINC)\windef.h \
+ $(SDKINC)\lmuse.h \
+ $(SDKINC)\lmat.h \
+ $(SDKINC)\lmstats.h \
+ $(IDL_NAME).acf
+
+CPP = -cpp_cmd "$(MIDL_CPP)" $(MIDL_FLAGS) $(C_DEFINES) $(NET_C_DEFINES)
+
+#
+# Define Products and Dependencies
+#
+
+all: $(CLIENT_TARGETS) $(SERVER_TARGETS) $(EXTRN_DEPENDS)
+!IF "$(BUILDMSG)" != ""
+ @ech ; $(BUILDMSG) ;
+!ENDIF
+
+clean: delsrc all
+
+delsrc:
+ erase $(CLIENT_TARGETS) $(SERVER_TARGETS)
+
+#
+# MIDL COMPILE
+#
+
+$(CLIENT_TARGETS) : .\$(IDL_NAME).idl $(EXTRN_DEPENDS)
+ midl -Oi -server none -oldnames -error allocation -error ref -ms_ext -c_ext $(CPP) .\$(IDL_NAME).idl $(INCS)
+ IF EXIST $(IDL_NAME)_c.c copy $(IDL_NAME)_c.c .\client & del $(IDL_NAME)_c.c
+
+$(SERVER_TARGETS) : .\$(IDL_NAME).idl $(EXTRN_DEPENDS)
+ midl -client none -oldnames -error stub_data -error allocation -error ref -ms_ext -c_ext $(CPP) .\$(IDL_NAME).idl $(INCS)
+ IF EXIST $(IDL_NAME)_s.c copy $(IDL_NAME)_s.c .\server & del $(IDL_NAME)_s.c
diff --git a/private/net/svcdlls/at/server/at.h b/private/net/svcdlls/at/server/at.h
new file mode 100644
index 000000000..fb49cf7a2
--- /dev/null
+++ b/private/net/svcdlls/at/server/at.h
@@ -0,0 +1,384 @@
+/*++
+
+Copyright (c) 1987-92 Microsoft Corporation
+
+Module Name:
+
+ at.h
+
+Abstract:
+
+ Local include file for the SCHEDULE service.
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 06 - November - 1992
+
+Revision History:
+
+ 06-Nov-1992 vladimv
+ Created
+
+--*/
+
+#include <nt.h> // NT definitions
+#include <ntrtl.h> // NT runtime library definitions
+#include <nturtl.h>
+
+#include <netevent.h>
+#include <windef.h> // Win32 type definitions
+#include <winbase.h> // Win32 base API prototypes
+#include <winsvc.h> // Win32 service control APIs
+#include <winreg.h> // HKEY
+
+#include <lmcons.h> // LAN Manager common definitions
+#include <lmerr.h> // LAN Manager network error definitions
+#include <lmsname.h> // LAN Manager service names
+#include <lmapibuf.h> // NetApiBufferFree
+
+#include <netlib.h> // LAN Man utility routines
+#include <netlibnt.h> // NetpNtStatusToApiStatus
+#include <netdebug.h> // NetpDbgPrint
+#include <tstring.h> // Transitional string functions
+#include <icanon.h> // I_Net canonicalize functions
+#include <align.h> // ROUND_UP_COUNT macro
+
+#include <services.h> // LMSVCS_GLOBAL_DATA
+#include <apperr.h> // APE_AT_ID_NOT_FOUND
+
+#include <rpc.h> // DataTypes and runtime APIs
+#include <rpcutil.h> // Prototypes for MIDL user functions
+#include <atsvc.h> // Generated by the MIDL complier
+
+
+#if DBG
+#define AT_DEBUG
+#endif // DBG
+
+
+//
+// atmain.c will #include this file with ATDATA_ALLOCATE defined.
+// That will cause each of these variables to be allocated.
+//
+#ifdef ATDATA_ALLOCATE
+#define EXTERN
+#define INIT( _x) = _x
+#else
+#define EXTERN extern
+#define INIT( _x)
+#endif
+
+//
+// Server side data structures.
+//
+
+typedef struct _AT_RECORD {
+ LIST_ENTRY RuntimeList; // queue this on a doubly linked list
+ LIST_ENTRY JobIdList; // queue this on a doubly linked list
+ LARGE_INTEGER Runtime; // next time to run (secs from 01.Jan.1970)
+ DWORD JobId; // unique job id
+ PWCHAR Name; // name of this task in registry
+ WORD CommandSize; // in bytes, including NULL terminator
+ WORD NameSize; // not really needed, but fill in to DWORD
+
+ DWORD JobTime; // time of day to run, in miliseconds from midnight
+ DWORD DaysOfMonth; // bitmask for days of month to run
+ UCHAR DaysOfWeek; // bitmask for days of week to run
+ UCHAR Flags; // see lmat.h
+ UCHAR JobDay; // index of WeekDay or MonthDay when to run
+#ifdef AT_DEBUG
+ UCHAR Debug; // for debugging purposes
+#endif // AT_DEBUG
+ PWCHAR Command; // command & data to execute
+} AT_RECORD, *PAT_RECORD, *LPAT_RECORD;
+
+typedef struct _AT_SCHEDULE {
+ DWORD JobTime; // time of day to run, in seconds from midnight
+ DWORD DaysOfMonth; // bitmask for days of month to run
+ UCHAR DaysOfWeek; // bitmask for days of week to run
+ UCHAR Flags; // see lmat.h
+ WORD Reserved; // padding, since registry pads them as well
+} AT_SCHEDULE, *PAT_SCHEDULE, *LPAT_SCHEDULE;
+
+
+typedef struct _AT_TIME {
+ LARGE_INTEGER LargeInteger; // time since Jan.01,1970 (in NT_TICK-s)
+ DWORD TickCount; // time since most recent boot (in WINDOWS_TICK-s)
+ DWORD CurrentTime; // time in 24h day, (in WINDOWS_TICK-s)
+ WORD CurrentYear;
+ WORD CurrentMonth; // January=1, February=2, ...
+ WORD CurrentDayOfWeek; // Monday=0, Tuesday=1, ...
+ WORD CurrentDay; // first=1, second=2, ...
+} AT_TIME, *PAT_TIME, *LPAT_TIME;
+
+
+
+//
+// Functions exported by atmain.c
+//
+VOID AtReportEvent(
+ IN WORD EventType,
+ IN DWORD MessageId,
+ IN WORD StringsCount,
+ IN LPWSTR * StringArray,
+ IN DWORD RawDataBufferLength OPTIONAL,
+ IN LPVOID RawDataBuffer
+ );
+
+
+//
+// Functions exported by atenv.c
+//
+VOID AtSetEnvironment( LPSTARTUPINFO pStartupInfo);
+
+//
+// Functions exported by attime.c
+//
+VOID AtCalculateRuntime(
+ IN OUT PAT_RECORD pRecord,
+ IN PAT_TIME pTime
+ );
+VOID AtTimeGetCurrents( IN OUT PAT_TIME pTime);
+VOID AtTimeGet( OUT PAT_TIME pTime);
+
+
+//
+// Functions exported by atreg.c
+//
+DWORD AtCreateKey( PAT_RECORD pRecord);
+BOOL AtDeleteKey( PAT_RECORD pRecord);
+VOID AtInsertRecord(
+ PAT_RECORD pNewRecord,
+ DWORD QueueMask
+ );
+NET_API_STATUS AtMakeDataFromRegistry( IN PAT_TIME pTime);
+BOOL AtPermitServerOperators( VOID);
+VOID AtRemoveRecord(
+ PAT_RECORD pNewRecord,
+ DWORD QueueMask
+ );
+BOOL AtSystemInteractive( VOID);
+
+
+//
+// Functions exported by atsec.c
+//
+NET_API_STATUS
+AtCheckSecurity(
+ ACCESS_MASK DesiredAccess
+ );
+NET_API_STATUS
+AtCreateSecurityObject(
+ VOID
+ );
+NET_API_STATUS
+AtDeleteSecurityObject(
+ VOID
+ );
+
+#ifdef NOT_YET
+//
+// Is job enabled or disabled.
+//
+// This flag is not supported yet - i.e. code to disable (and then re-enable)
+// jobs is missing.
+//
+#define JOB_IS_ENABLED 0x40 // set if enabled
+#endif // NOT_YET
+
+
+
+//
+// Bitmask used with AtGlobalTasks. They describe outstanding tasks to be
+// done by the main schedule service thread once it wakes up.
+//
+
+#define AT_SERVICE_SHUTDOWN 0x0004
+
+
+//
+// Bitmask describing on what global queues we should operate on.
+//
+
+#define RUNTIME_QUEUE 0x0001
+#define JOBID_QUEUE 0x0002
+#define BOTH_QUEUES (RUNTIME_QUEUE | JOBID_QUEUE)
+
+
+//
+// For a /NEXT type of program, JOB_CLEAR_WEEKDAY flag determines whether
+// to clear WEEKLY or MONTHLY schedule.
+//
+#define JOB_CLEAR_WEEKDAY 0x80 // set if WEEKLY schedule
+
+
+#define JOB_INVALID_DAY 0xFF
+
+
+
+#define MAXIMUM_COMMAND_LENGTH (MAX_PATH - 1) // == 259, cmd.exe uses this
+
+#define AT_REGISTRY_PATH L"System\\CurrentControlSet\\Services\\Schedule"
+#define WINDOWS_REGISTRY_PATH L"System\\CurrentControlSet\\Control\\Windows"
+#define WINDOWS_VALUE_NAME L"NoInteractiveServices"
+#define INTERACTIVE_DESKTOP L"WinSta0\\Default"
+#define LSA_REGISTRY_PATH L"System\\CurrentControlSet\\Control\\Lsa"
+#define LSA_SUBMIT_CONTROL L"SubmitControl"
+#define LSA_SERVER_OPERATORS 0x00000001 // can they submit jobs, etc.
+
+#define MAXIMUM_JOB_TIME (24 * 60 * 60 * 1000 - 1)
+#define DAYS_OF_WEEK 0x7F // 7 bits for 7 days
+#define DAYS_OF_MONTH 0x7FFFFFFF // 31 bits for 31 days
+
+#define AT_SCHEDULE_NAME L"Schedule"
+#define AT_COMMAND_NAME L"Command"
+
+//
+// WINDOWS_TICK is one milisecond, i.e. 1e-3 seconds
+// NT_TICK is hundred nanoseconds, i.e. 1e-7 seconds
+//
+#define NT_TICKS_IN_WINDOWS_TICK 10000L
+#define ONE_MINUTE_IN_NT_TICKS (60000L * NT_TICKS_IN_WINDOWS_TICK)
+#define MAXIMUM_FINITE_SLEEP_TIME ((DWORD)-2) // in windows ticks
+#define MAX_BUSY_TIMEOUT (300*1000L) // 5 MIN in WINDOWS_TICK-s
+#define MAX_LAZY_TIMEOUT (3600*1000L); // 60 MIN in WINDOWS_TICK-s
+
+#define AT_WAIT_HINT_TIME 5000L
+
+
+//
+// The following are used only when we create new keys via NetrJobAdd.
+// A user can edit registry directly and define key names larger than this.
+//
+#define AT_KEY_NAME_MAX_LEN 8
+#define AT_KEY_NAME_SIZE ((AT_KEY_NAME_MAX_LEN + 1) * sizeof( WCHAR))
+
+
+
+//
+// BUGBUG Need to define ERROR_SERVICE_PAUSED in one of common include
+// BUGBUG files. This error means that request is denied because service
+// BUGBUG is paused. For now, set it to 65535.
+//
+#define ERROR_SERVICE_PAUSED 0xFFFF
+
+
+
+//
+// Object specific access masks
+//
+
+#define AT_JOB_ADD 0x0001
+#define AT_JOB_DEL 0x0002
+#define AT_JOB_ENUM 0x0004
+#define AT_JOB_GET_INFO 0x0008
+
+
+//
+// Defines used to indicate how far we managed to initialize the Schedule
+// service before an error is encountered and the extent of clean up needed
+//
+
+#define AT_EVENT_CREATED 0x00000001
+#define AT_QUEUES_CREATED 0x00000002
+#define AT_RPC_SERVER_STARTED 0x00000004
+#define AT_SECURITY_OBJECT_CREATED 0x00000008
+
+
+//-------------------------------------------------------------------//
+// //
+// Global variables //
+// //
+//-------------------------------------------------------------------//
+
+
+EXTERN CRITICAL_SECTION AtGlobalCriticalSection;
+EXTERN CRITICAL_SECTION AtGlobalProtectLogFile;
+EXTERN DWORD AtGlobalTasks;
+EXTERN SERVICE_STATUS AtGlobalServiceStatus;
+EXTERN SERVICE_STATUS_HANDLE AtGlobalServiceStatusHandle;
+EXTERN DWORD AtGlobalJobId;
+EXTERN LIST_ENTRY AtGlobalRuntimeListHead;
+EXTERN LIST_ENTRY AtGlobalJobIdListHead;
+EXTERN HANDLE AtGlobalEvent;
+EXTERN HANDLE AtGlobalLogFile;
+EXTERN DWORD AtGlobalSeed;
+EXTERN AT_TIME AtGlobalSleepTime;
+EXTERN AT_TIME AtGlobalGetupTime;
+EXTERN HANDLE AtGlobalEvent;
+EXTERN DWORD AtGlobalSeed;
+EXTERN HKEY AtGlobalKey;
+EXTERN BOOL AtGlobalHaveWindowsKey;
+EXTERN BOOL AtGlobalPermitServerOperators;
+EXTERN HKEY AtGlobalWindowsKey;
+EXTERN STARTUPINFO AtGlobalStartupInfo;
+EXTERN CHAR AtGlobalDebugBuffer[ 1024]; // arbitrary
+
+
+#ifdef AT_DEBUG
+
+EXTERN DWORD AtGlobalDebug;
+
+////////////////////////////////////////////////////////////////////////
+//
+// Debug Definititions
+//
+////////////////////////////////////////////////////////////////////////
+
+#define AT_DEBUG_MAIN 0x00000001
+#define AT_DEBUG_UTIL 0x00000002
+#define AT_DEBUG_CRITICAL 0x00000004
+
+//
+// Control bits.
+//
+
+#define AT_TIMESTAMP 0x10000000 // TimeStamp each output line
+
+#define IF_DEBUG(Function) \
+ if (AtGlobalDebug & AT_ ## Function)
+
+VOID AtDebugCreate( VOID);
+VOID AtDebugDelete( VOID);
+
+VOID AtAssertFailed(
+ IN PVOID FailedAssertion,
+ IN PVOID FileName,
+ IN ULONG LineNumber,
+ IN PCHAR Message OPTIONAL
+ );
+
+#define AtAssert( Predicate) \
+ { \
+ if (!(Predicate)) \
+ AtAssertFailed( #Predicate, __FILE__, __LINE__, NULL ); \
+ }
+
+VOID AtLogRoutine(
+ IN DWORD DebugFlag,
+ IN LPSTR Format, // PRINTF()-STYLE FORMAT STRING.
+ ... // OTHER ARGUMENTS ARE POSSIBLE.
+ );
+VOID AtLogRuntimeList( IN PCHAR Comment);
+VOID AtLogTimeout( IN DWORD timeout);
+
+#define AtLog( _x_) AtLogRoutine _x_
+
+#else // AT_DEBUG
+
+#define AtAssert( condition)
+
+#define AtLog( _x_)
+#define AtLogRuntimeList( _x_)
+#define AtLogTimeout( _x_)
+
+#endif // AT_DEBUG
+
+
+
+//
+// SCHEDULE_EVENTLOG_NAME is the name of registry key (under EventLog service)
+// used to interpret events for schedule service.
+//
+
+#define SCHEDULE_EVENTLOG_NAME TEXT( "Schedule")
diff --git a/private/net/svcdlls/at/server/atapi.c b/private/net/svcdlls/at/server/atapi.c
new file mode 100644
index 000000000..0217221b2
--- /dev/null
+++ b/private/net/svcdlls/at/server/atapi.c
@@ -0,0 +1,636 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ atapi.c
+
+Abstract:
+
+ This module contains the worker routines for all APIs implemented
+ in the Schedule service.
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 06 - November - 1992
+
+Revision History:
+
+ 06-Nov-1992 vladimv
+ Created
+
+--*/
+
+#include "at.h"
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetrJobAdd(
+ IN LPCWSTR ServerName OPTIONAL,
+ IN LPAT_INFO pAtInfo,
+ OUT LPDWORD pJobId
+ )
+/*++
+
+Routine Description:
+
+ This function is the NetJobAdd entry point in the Schedule service.
+ Given the info about a new job, its creates a new job and returns the
+ id of the new job.
+
+Arguments:
+
+ ServerName - IGNORED
+ pAtInfo - pointer to information about the job to be added
+ pJobId - pointer to id of the newly added job
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NET_API_STATUS status;
+ PAT_RECORD pRecord;
+ DWORD CommandLength;
+ DWORD CommandSize;
+ AT_TIME time;
+
+
+ UNREFERENCED_PARAMETER( ServerName);
+
+ status = AtCheckSecurity( AT_JOB_ADD);
+ if ( status != NERR_Success) {
+ return( ERROR_ACCESS_DENIED);
+ }
+
+ //
+ // Is it safe to calculate string length below?
+ // Should RPC supply string length parameter?
+ // Note that wcslen() returns length of UNICODE string in WCHAR-s.
+ // Thus storage needed for Command is (CommandLength+1)*sizeof(WCHAR)
+ //
+ CommandLength = wcslen( pAtInfo->Command);
+
+ if ( ( CommandLength > MAXIMUM_COMMAND_LENGTH) ||
+ ( pAtInfo->JobTime > MAXIMUM_JOB_TIME) ||
+ ( pAtInfo->DaysOfWeek & ~DAYS_OF_WEEK) != 0 ||
+ ( pAtInfo->DaysOfMonth & ~DAYS_OF_MONTH) != 0 ||
+ ( pAtInfo->Flags & ~JOB_INPUT_FLAGS) != 0 ) {
+ return( ERROR_INVALID_PARAMETER);
+ }
+
+ CommandSize = ( CommandLength + 1) * sizeof( WCHAR);
+
+ pRecord = (PAT_RECORD)LocalAlloc(
+ LMEM_FIXED,
+ sizeof( AT_RECORD) + AT_KEY_NAME_SIZE + CommandSize
+ );
+ if ( pRecord == NULL) {
+ return( ERROR_NOT_ENOUGH_MEMORY);
+ }
+
+ pRecord->CommandSize = (WORD)CommandSize;
+ pRecord->NameSize = AT_KEY_NAME_SIZE; // max possible
+
+ pRecord->JobTime = pAtInfo->JobTime;
+ pRecord->JobDay = JOB_INVALID_DAY; // the default
+#ifdef AT_DEBUG
+ pRecord->Debug = 0;
+#endif // AT_DEBUG
+
+ pRecord->Name = (PWCHAR)( (PBYTE)pRecord + sizeof( AT_RECORD));
+ memset( pRecord->Name, 0, AT_KEY_NAME_SIZE);
+
+ pRecord->Command = (PWCHAR)( (PBYTE)&pRecord->Name[0] + AT_KEY_NAME_SIZE);
+ memcpy( pRecord->Command, pAtInfo->Command, CommandSize);
+
+ EnterCriticalSection( &AtGlobalCriticalSection);
+
+ AtLog(( AT_DEBUG_MAIN, "++JobAdd: Command=%ws\n", pRecord->Command));
+
+ AtTimeGet( &time); // needed in what follows
+
+ if ( pAtInfo->Flags & JOB_ADD_CURRENT_DATE) {
+ pAtInfo->Flags &= ~JOB_ADD_CURRENT_DATE;
+ pAtInfo->DaysOfMonth |= 1 << ( time.CurrentDay - 1);
+ }
+
+ pRecord->Flags = pAtInfo->Flags;
+ pRecord->DaysOfMonth = pAtInfo->DaysOfMonth;
+ pRecord->DaysOfWeek = pAtInfo->DaysOfWeek;
+
+ //
+ // BUGBUG Should we have a more stringent test that makes sure
+ // BUGBUG that service state is SERVICE_RUNNING ?
+ //
+ if ( AtGlobalServiceStatus.dwCurrentState == SERVICE_PAUSED) {
+ //
+ // BUGBUG Error ERROR_SERVICE_PAUSED is not defined properly for now.
+ //
+ status = ERROR_SERVICE_PAUSED;
+ goto error_exit;
+ }
+ ASSERT( AtGlobalServiceStatus.dwCurrentState == SERVICE_RUNNING);
+
+
+ *pJobId = pRecord->JobId = AtGlobalJobId++;
+
+ status = AtCreateKey( pRecord);
+ if ( status != NERR_Success) {
+ LocalFree( pRecord);
+ goto error_exit;
+ }
+
+ AtCalculateRuntime( pRecord, &time);
+ AtInsertRecord( pRecord, BOTH_QUEUES);
+
+error_exit:
+ AtLog(( AT_DEBUG_MAIN, "--JobAdd: Command=%ws status=%d JobId=%d\n",
+ pRecord->Command, status, pRecord->JobId));
+ LeaveCriticalSection( &AtGlobalCriticalSection);
+ SetEvent( AtGlobalEvent); // to calculate new timeout
+ return( status);
+
+} // NetrJobAdd
+
+
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetrJobDel(
+ IN LPCWSTR ServerName OPTIONAL,
+ IN DWORD MinJobId,
+ IN DWORD MaxJobId
+ )
+/*++
+
+Routine Description:
+
+ This function is the NetJobDel entry point in the Schedule service.
+ Given the minimum and maximum job id, this routines deletes all jobs
+ whose job id is greater than or equal to the minimum job id and
+ less than or equal to the maximum job id.
+
+Arguments:
+
+ ServerName - IGNORED
+ MinJobId - minimum job id
+ MaxJobId - maximum job id
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NET_API_STATUS status;
+ PLIST_ENTRY pListEntry;
+ PAT_RECORD pRecord;
+ BOOL JobDeleted;
+
+ UNREFERENCED_PARAMETER(ServerName);
+
+ status = AtCheckSecurity( AT_JOB_DEL);
+ if ( status != NERR_Success) {
+ return( status);
+ }
+
+ if ( MinJobId > MaxJobId) {
+ return( ERROR_INVALID_PARAMETER);
+ }
+
+ EnterCriticalSection( &AtGlobalCriticalSection);
+
+ AtLog(( AT_DEBUG_MAIN, "++JobDel: MinJobId=%d MaxJobId=%d\n",
+ MinJobId, MaxJobId));
+
+ for ( JobDeleted = FALSE, pListEntry = AtGlobalJobIdListHead.Flink;
+ pListEntry != &AtGlobalJobIdListHead;
+ NOTHING) {
+
+ pRecord = CONTAINING_RECORD(
+ pListEntry,
+ AT_RECORD,
+ JobIdList
+ );
+
+ if ( pRecord->JobId > MaxJobId) {
+ break; // JobId is too larger, we are done
+ }
+
+ pListEntry = pListEntry->Flink; // actual iteration statement
+
+ if ( pRecord->JobId < MinJobId) {
+ continue; // JobId is too small, look further
+ }
+
+ JobDeleted = TRUE;
+
+ AtDeleteKey( pRecord);
+ AtRemoveRecord( pRecord, BOTH_QUEUES);
+ (VOID)LocalFree( pRecord);
+ }
+
+ status = JobDeleted == TRUE ? NERR_Success : APE_AT_ID_NOT_FOUND;
+
+ AtLog(( AT_DEBUG_MAIN, "--JobDel: MinJobId=%d MaxJobId=%d status=%d\n",
+ MinJobId, MaxJobId, status));
+
+ LeaveCriticalSection( &AtGlobalCriticalSection);
+ SetEvent( AtGlobalEvent); // to calculate new timeout
+ return( status);
+
+} // NetrJobDelete
+
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetrJobEnum(
+ IN LPCWSTR ServerName OPTIONAL,
+ IN OUT LPAT_ENUM_CONTAINER pEnumContainer,
+ IN DWORD PreferredMaximumLength,
+ OUT LPDWORD TotalEntries,
+ IN OUT LPDWORD ResumeHandle OPTIONAL
+ )
+/*++
+
+Routine Description:
+
+ This function is the NetJobEnum entry point in the Schedule service.
+ It returns information about jobs starting with a job id given by
+ resume handle, or if resume handle is absent, starting with a job
+ with the lowest job id.
+
+Arguments:
+
+ ServerName - IGNORED
+
+ pEnumContainer - pointer to enumeration container which contains the
+ array of job information structures and size of that array.
+
+ PreferedMaximumLength - Supplies the number of bytes of information
+ to return in the buffer. If this value is MAXULONG, all available
+ information will be returned.
+
+ TotalEntries - Returns the total number of entries available. This value
+ is only valid if the return code is NERR_Success or ERROR_MORE_DATA.
+
+ ResumeHandle - Supplies a handle to resume the enumeration from where it
+ left off the last time through. Returns the resume handle if return
+ code is ERROR_MORE_DATA.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NET_API_STATUS status;
+ DWORD JobId; // resume job id
+ DWORD JobCount;
+ DWORD BufferSize;
+ PLIST_ENTRY pListEntry;
+ PAT_RECORD pRecord;
+ LPBYTE Buffer;
+ LPAT_ENUM pAtEnum;
+ LPBYTE StringBuffer;
+ DWORD EntriesRead;
+ BOOL success;
+ AT_TIME time;
+
+ UNREFERENCED_PARAMETER(ServerName);
+
+ status = AtCheckSecurity( AT_JOB_ENUM);
+ if ( status != NERR_Success) {
+ return( ERROR_ACCESS_DENIED);
+ }
+
+ JobId = (ARGUMENT_PRESENT( ResumeHandle)) ? *ResumeHandle : 0;
+ Buffer = NULL;
+ EntriesRead = 0;
+
+
+ EnterCriticalSection( &AtGlobalCriticalSection);
+
+ AtLog(( AT_DEBUG_MAIN, "++JobEnum: JobId=%d\n", JobId));
+
+ AtTimeGet( &time); // needed in what follows
+
+ for ( JobCount = 0, BufferSize = 0,
+ pListEntry = AtGlobalJobIdListHead.Blink;
+ pListEntry != &AtGlobalJobIdListHead;
+ JobCount++, BufferSize += pRecord->CommandSize,
+ pListEntry = pListEntry->Blink) {
+
+ pRecord = CONTAINING_RECORD(
+ pListEntry,
+ AT_RECORD,
+ JobIdList
+ );
+
+ if ( pRecord->JobId < JobId) {
+ break; // reached first stale record
+ }
+ }
+
+
+ *TotalEntries = JobCount;
+
+ if ( JobCount == 0) {
+ goto error_exit;
+ }
+
+ if ( PreferredMaximumLength == -1) {
+ //
+ // If the caller has not specified a size, calculate a size
+ // that will hold the entire enumeration.
+ //
+ BufferSize += JobCount * sizeof( AT_ENUM);
+
+ } else {
+ BufferSize = PreferredMaximumLength;
+ }
+
+ Buffer = (LPBYTE)MIDL_user_allocate( BufferSize);
+ if ( Buffer == NULL) {
+ status = ERROR_NOT_ENOUGH_MEMORY;
+ goto error_exit;
+ }
+
+ //
+ // When we arrive here "pListEntry" points either to the head of the
+ // list (if we enumerate from the beginning) or to the first stale
+ // record.
+ //
+ for ( pListEntry = pListEntry->Flink, pAtEnum = (PAT_ENUM)Buffer,
+ StringBuffer = Buffer + BufferSize;
+ pListEntry != &AtGlobalJobIdListHead;
+ pListEntry = pListEntry->Flink, pAtEnum++,
+ EntriesRead++) {
+
+ pRecord = CONTAINING_RECORD(
+ pListEntry,
+ AT_RECORD,
+ JobIdList
+ );
+
+ if ( StringBuffer <= (LPBYTE)pAtEnum + sizeof( AT_ENUM)) {
+ status = ERROR_MORE_DATA;
+ break; // the buffer is full
+ }
+
+ pAtEnum->JobId = pRecord->JobId;
+ pAtEnum->JobTime = pRecord->JobTime;
+ pAtEnum->DaysOfMonth = pRecord->DaysOfMonth;
+ pAtEnum->DaysOfWeek = pRecord->DaysOfWeek;
+ pAtEnum->Flags = pRecord->Flags;
+
+ if ( time.CurrentTime < pRecord->JobTime) {
+ pAtEnum->Flags |= JOB_RUNS_TODAY;
+ }
+
+ success = NetpCopyStringToBuffer(
+ pRecord->Command,
+ pRecord->CommandSize / sizeof( WCHAR) - 1,
+ (LPBYTE)(pAtEnum+1),
+ (LPWSTR *)&StringBuffer,
+ &pAtEnum->Command
+ );
+
+ if ( success == FALSE) {
+ status = ERROR_MORE_DATA;
+ KdPrint(( "[Job] NetrJobEnum: Not enough room\n"));
+ break;
+ }
+ }
+
+ if ( status == ERROR_MORE_DATA) {
+ JobId = pRecord->JobId; // JobId of first one we have not read
+ } else {
+ JobId = 0; // we have read everything, reset resume handle
+ }
+
+error_exit:
+
+ AtLog(( AT_DEBUG_MAIN, "--JobEnum: JobId=%d\n", JobId));
+ LeaveCriticalSection( &AtGlobalCriticalSection);
+ SetEvent( AtGlobalEvent); // to calculate new timeout
+
+ pEnumContainer->EntriesRead = EntriesRead;
+
+ if ( EntriesRead == 0 && Buffer != NULL) {
+
+ MIDL_user_free( Buffer);
+ Buffer = NULL;
+
+ }
+
+ pEnumContainer->Buffer = (LPAT_ENUM)Buffer;
+
+ if ( ARGUMENT_PRESENT( ResumeHandle)) {
+ *ResumeHandle = JobId;
+ }
+
+ return( status);
+
+} // NetrJobEnum
+
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetrJobGetInfo(
+ IN LPCWSTR ServerName OPTIONAL,
+ IN DWORD JobId,
+ OUT LPAT_INFO * ppAtInfo
+ )
+/*++
+
+Routine Description:
+
+ This function is the NetJobGetInfo entry point in the Schedule service.
+ It returns information about a job corresponding to the supplied job id.
+
+Arguments:
+
+ ServerName - IGNORED
+ JobId - job id of a job we are interested in
+ ppAtInfo - pointer to pointer to data about the job in question
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NET_API_STATUS status;
+ PAT_RECORD pRecord;
+ PLIST_ENTRY pListEntry;
+ LPAT_INFO pAtInfo;
+ AT_TIME time;
+
+ UNREFERENCED_PARAMETER(ServerName);
+
+#ifdef AT_DEBUG
+ if ( JobId == 70503010) {
+ DbgUserBreakPoint();
+ }
+#endif // AT_DEBUG
+
+ status = AtCheckSecurity( AT_JOB_GET_INFO);
+ if ( status != NERR_Success) {
+ return( ERROR_ACCESS_DENIED);
+ }
+
+ pAtInfo = NULL;
+
+ EnterCriticalSection( &AtGlobalCriticalSection);
+ AtLog(( AT_DEBUG_MAIN, "++JobGetInfo: JobId=%d\n", JobId));
+
+ AtTimeGet( &time); // needed in what follows
+
+ for ( pListEntry = AtGlobalJobIdListHead.Flink;
+ pListEntry != &AtGlobalJobIdListHead;
+ pListEntry = pListEntry->Flink) {
+
+ pRecord = CONTAINING_RECORD(
+ pListEntry,
+ AT_RECORD,
+ JobIdList
+ );
+
+ if ( pRecord->JobId == JobId) {
+ break;
+ }
+ }
+
+ if ( pListEntry == &AtGlobalJobIdListHead) {
+ status = APE_AT_ID_NOT_FOUND;
+ goto error_exit;
+ }
+
+ pAtInfo = (PAT_INFO)MIDL_user_allocate(
+ sizeof( AT_INFO) + pRecord->CommandSize
+ );
+ if ( pAtInfo == NULL) {
+ status = ERROR_NOT_ENOUGH_MEMORY;
+ goto error_exit;
+ }
+
+ pAtInfo->JobTime = pRecord->JobTime;
+ pAtInfo->DaysOfMonth = pRecord->DaysOfMonth;
+ pAtInfo->DaysOfWeek = pRecord->DaysOfWeek;
+ pAtInfo->Flags = pRecord->Flags;
+
+ if ( time.CurrentTime < pRecord->JobTime) {
+ pAtInfo->Flags |= JOB_RUNS_TODAY;
+ }
+
+ pAtInfo->Command = (LPWSTR)( pAtInfo + 1);
+
+ memcpy( pAtInfo->Command, pRecord->Command, pRecord->CommandSize);
+
+error_exit:
+
+ AtLog(( AT_DEBUG_MAIN, "--JobGetInfo: JobId=%d\n", JobId));
+ LeaveCriticalSection( &AtGlobalCriticalSection);
+ SetEvent( AtGlobalEvent); // to calculate new timeout
+
+ *ppAtInfo = pAtInfo;
+
+ return( status);
+
+} // NetrJobGetInfo
+
+
+#ifdef NOT_YET
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetrJobControl(
+ IN LPCWSTR ServerName OPTIONAL,
+ IN OUT LPJOB_CONTROL_INFO ControlInfo,
+ IN DWORD Opcode
+ )
+/*++
+
+Routine Description:
+
+ This function is the NetJobControl entry point in the Schedule service.
+
+Arguments:
+
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+Comments:
+
+ BUGBUG JobControl contains old stuff - need to clean it up & make it work.
+
+--*/
+{
+ NET_API_STATUS status;
+
+ UNREFERENCED_PARAMETER(ServerName);
+
+ status = AtCheckSecurity( AT_JOB_CONTROL);
+ if ( status != NERR_Success) {
+ return( ERROR_ACCESS_DENIED);
+ }
+
+ EnterCriticalSection( &AtGlobalCriticalSection);
+
+ switch ( opcode) {
+ case ENABLE:
+ case DISABLE:
+ break;
+ default:
+ status = ERROR_INVALID_REQUEST;
+ goto error_exit;
+ }
+
+ pJob = FindExactJob( pDelRequest);
+ if ( pJob == NULL) {
+ status = ERROR_JOB_NOT_FOUND;
+ goto error_exit;
+ }
+
+ if ( opcode == pJob->State) { // case of stupid request
+ status = STATUS_SUCCESS;
+ goto error_exit;
+ }
+
+ pJob->State = opcode;
+ DeleteJob( pJob);
+
+ switch( opcode) {
+ case ENABLED:
+ // Evaluate the next run time and queue the job near the front
+ // of the list where all enabled jobs reside.
+ break;
+ case DISABLED:
+ // Optionally set next run time to infinity, then queue the job
+ // near the back of the list where all the disabled jobs reside.
+ break;
+ }
+
+ status = STATUS_SUCCESS;
+
+
+ // Queue a job delete request to a registry queue.
+
+
+error_exit:
+
+ LeaveCriticalSection( &AtGlobalCriticalSection);
+ SetEvent( AtGlobalEvent); // to calculate new timeout
+ return( status);
+
+} // NetrJobControl
+
+#endif // NOT_YET
+
+
diff --git a/private/net/svcdlls/at/server/atdebug.c b/private/net/svcdlls/at/server/atdebug.c
new file mode 100644
index 000000000..e3d88fd6a
--- /dev/null
+++ b/private/net/svcdlls/at/server/atdebug.c
@@ -0,0 +1,295 @@
+/*++
+
+Copyright (c) 1991-1993 Microsoft Corporation
+
+Module Name:
+
+ atdebug.c
+
+Abstract:
+
+ Debugging module.
+
+Author:
+
+ Vladimir Z. Vulovic 27 - July - 1993
+
+Environment:
+
+ User mode
+
+Revision History :
+
+--*/
+
+#include "at.h"
+#include <stdio.h> // vsprintf
+
+#ifdef AT_DEBUG
+
+#define ATSVC_LOG_FILE L"%SystemRoot%\\atsvc.log"
+
+
+VOID AtDebugDelete( VOID)
+{
+ DeleteCriticalSection( &AtGlobalProtectLogFile);
+}
+
+
+VOID AtDebugCreate( VOID)
+{
+ WCHAR Buffer[ MAX_PATH];
+ DWORD Length;
+
+ AtGlobalLogFile = INVALID_HANDLE_VALUE;
+ AtGlobalDebug = (DWORD)(-1); // max debug by default
+ InitializeCriticalSection( &AtGlobalProtectLogFile );
+
+ //
+ // Length returned by ExpandEnvironmentalStrings includes terminating
+ // NULL byte.
+ //
+ Length = ExpandEnvironmentStrings( ATSVC_LOG_FILE, Buffer, sizeof( Buffer));
+ if ( Length == 0) {
+ AtLog(( AT_DEBUG_CRITICAL, "Error=%d", GetLastError()));
+ return;
+ }
+ if ( Length > sizeof( Buffer) || Length != wcslen(Buffer) + 1) {
+ AtLog(( AT_DEBUG_CRITICAL, "Buffer=%x, Length = %d", Buffer, Length));
+ return;
+ }
+
+ AtGlobalLogFile = CreateFileW( Buffer,
+ GENERIC_WRITE,
+ FILE_SHARE_READ,
+ NULL,
+ OPEN_ALWAYS,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL );
+
+ if ( AtGlobalLogFile == INVALID_HANDLE_VALUE ) {
+ AtLog(( AT_DEBUG_CRITICAL, "Cannot open %ws", Buffer ));
+ return;
+ }
+
+ //
+ // Position the log file at the end
+ //
+ (VOID) SetFilePointer( AtGlobalLogFile,
+ 0,
+ NULL,
+ FILE_END );
+
+}
+
+
+VOID AtLogRoutine(
+ IN DWORD DebugFlag,
+ IN LPSTR Format,
+ ...
+ )
+
+{
+ va_list arglist;
+ ULONG length;
+ DWORD BytesWritten;
+
+ //
+ // If we aren't debugging this functionality, just return.
+ //
+ if ( DebugFlag != 0 && (AtGlobalDebug & DebugFlag) == 0 ) {
+ return;
+ }
+
+ //
+ // vsprintf isn't multithreaded + we don't want to intermingle output
+ // from different threads. Therefore we can use just a single output
+ // debug buffer.
+ //
+
+ EnterCriticalSection( &AtGlobalProtectLogFile );
+ length = 0;
+
+ //
+ // Handle the beginning of a new line.
+ //
+ //
+
+ //
+ // Put the timestamp at the begining of the line.
+ //
+ IF_DEBUG( TIMESTAMP ) {
+ SYSTEMTIME SystemTime;
+ GetLocalTime( &SystemTime );
+ length += (ULONG) sprintf( &AtGlobalDebugBuffer[length],
+ "%02u/%02u %02u:%02u:%02u ",
+ SystemTime.wMonth,
+ SystemTime.wDay,
+ SystemTime.wHour,
+ SystemTime.wMinute,
+ SystemTime.wSecond );
+ }
+
+ //
+ // Indicate the type of message on the line
+ //
+ {
+ char *Text;
+
+ switch (DebugFlag) {
+ case AT_DEBUG_MAIN:
+ Text = "MAIN";
+ break;
+ case AT_DEBUG_UTIL:
+ Text = "UTIL";
+ break;
+ default:
+ Text = NULL;
+ break;
+ }
+ if ( Text != NULL ) {
+ length += (ULONG) sprintf( &AtGlobalDebugBuffer[length], "[%s] ", Text );
+ }
+ }
+
+ //
+ // Put a the information requested by the caller onto the line
+ //
+
+ va_start( arglist, Format);
+
+ length += (ULONG) vsprintf( &AtGlobalDebugBuffer[length], Format, arglist);
+
+ va_end(arglist);
+
+ AtAssert(length <= sizeof(AtGlobalDebugBuffer));
+
+
+ //
+ // If the log file isn't open, just output to the debug terminal.
+ //
+
+ if ( AtGlobalLogFile == INVALID_HANDLE_VALUE ) {
+ (void) DbgPrint( (PCH) AtGlobalDebugBuffer);
+
+ //
+ // Write the debug info to the log file.
+ //
+
+ } else {
+ if ( !WriteFile( AtGlobalLogFile,
+ AtGlobalDebugBuffer,
+ length,
+ &BytesWritten,
+ NULL ) ) {
+ (void) DbgPrint( (PCH) AtGlobalDebugBuffer);
+ }
+
+ }
+
+ LeaveCriticalSection( &AtGlobalProtectLogFile );
+}
+
+VOID AtAssertFailed(
+ IN PVOID FailedAssertion,
+ IN PVOID FileName,
+ IN ULONG LineNumber,
+ IN PCHAR Message OPTIONAL
+ )
+/*++
+
+ Have my own version of RtlAssert so debug versions of netlogon really assert on
+ free builds.
+
+--*/
+{
+ char Response[ 2 ];
+
+ for ( ; ; ) {
+ DbgPrint( "\n*** Assertion failed: %s%s\n*** Source File: %s, line %ld\n\n",
+ Message ? Message : "",
+ FailedAssertion,
+ FileName,
+ LineNumber
+ );
+
+ DbgPrompt( "Break, Ignore, terminate Process, Sleep 30 seconds, or terminate Thread (bipst)? ",
+ Response, sizeof( Response));
+ switch ( toupper(Response[0])) {
+ case 'B':
+ DbgBreakPoint();
+ break;
+ case 'I':
+ return;
+ break;
+ case 'P':
+ NtTerminateProcess( NtCurrentProcess(), STATUS_UNSUCCESSFUL );
+ break;
+ case 'S':
+ Sleep( 30000L);
+ break;
+ case 'T':
+ NtTerminateThread( NtCurrentThread(), STATUS_UNSUCCESSFUL );
+ break;
+ }
+ }
+
+ DbgBreakPoint();
+ NtTerminateProcess( NtCurrentProcess(), STATUS_UNSUCCESSFUL );
+}
+
+
+VOID AtLogRuntimeList( IN PCHAR Comment)
+{
+ PLIST_ENTRY pListEntry;
+ PAT_RECORD pRecord;
+ TIME_FIELDS TimeFields;
+
+ AtLog(( AT_DEBUG_MAIN, "%s\n", Comment));
+
+ for ( pListEntry = AtGlobalRuntimeListHead.Flink;
+ pListEntry != &AtGlobalRuntimeListHead;
+ pListEntry = pListEntry->Flink) {
+
+ pRecord = CONTAINING_RECORD(
+ pListEntry,
+ AT_RECORD,
+ RuntimeList
+ );
+ RtlTimeToTimeFields( &pRecord->Runtime, &TimeFields);
+ AtLog(( AT_DEBUG_MAIN,
+ "LogRecord: JobId=%d Command=%ws Runtime=%02u/%02u %02u:%02u:%02u\n",
+ pRecord->JobId,
+ pRecord->Command,
+ TimeFields.Month,
+ TimeFields.Day,
+ TimeFields.Hour,
+ TimeFields.Minute,
+ TimeFields.Second
+ ));
+ }
+}
+
+VOID AtLogTimeout( IN DWORD timeout)
+{
+ int Second, Minute, Hour, Day;
+ Second = timeout / 1000;
+ Minute = Second / 60;
+ Second -= Minute * 60;
+ Hour = Minute / 60;
+ Minute -= Hour * 60;
+ Day = Hour / 24;
+ Hour -= Day * 24;
+ AtLog(( AT_DEBUG_MAIN,
+ "Sleep for: 00/%02u %02u:%02u:%02u\n",
+ Day,
+ Hour,
+ Minute,
+ Second
+ ));
+}
+
+#endif // AT_DEBUG
+
+
+
diff --git a/private/net/svcdlls/at/server/atenv.c b/private/net/svcdlls/at/server/atenv.c
new file mode 100644
index 000000000..c82980279
--- /dev/null
+++ b/private/net/svcdlls/at/server/atenv.c
@@ -0,0 +1,176 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ atenv.c
+
+Abstract:
+
+ Initializes all system and some user environment variables. Code is
+ borrowed from "shell\library\regenv.c" & "user\winlogon\usrenv.c".
+
+ Unlike shell & user code here we do not attempt to set env variables
+ related to user profile. It is harder to obtain user profile for
+ Schedule service than for the interactively logged on user. And, amount
+ of work is too big one month away from Daytona ship date.
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 23 - June - 1994
+
+Environment:
+
+ User Mode - Win32
+
+Revision History:
+
+ 23-Jun-1994 vladimv
+ Created
+
+--*/
+
+#include "at.h"
+
+#define COMPUTERNAME_VARIABLE TEXT("COMPUTERNAME")
+#define USERNAME_VARIABLE TEXT("USERNAME")
+#define USERDOMAIN_VARIABLE TEXT("USERDOMAIN")
+#define OS_VARIABLE TEXT("OS")
+#define PROCESSOR_VARIABLE TEXT("PROCESSOR_ARCHITECTURE")
+#define PROCESSOR_LEVEL_VARIABLE TEXT("PROCESSOR_LEVEL")
+
+DBGSTATIC BOOL
+GetUserNameAndDomain(
+ OUT LPTSTR * UserName,
+ OUT LPTSTR * UserDomain
+ )
+{
+ HANDLE hToken;
+ DWORD cbTokenBuffer = 0;
+ PTOKEN_USER pUserToken;
+ LPTSTR lpUserName = NULL;
+ LPTSTR lpUserDomain = NULL;
+ DWORD cbAccountName = 0;
+ DWORD cbUserDomain = 0;
+ SID_NAME_USE SidNameUse;
+ BOOL bRet = FALSE;
+
+ if (!OpenProcessToken(GetCurrentProcess(),
+ TOKEN_QUERY,
+ &hToken) ){
+ return(FALSE);
+ }
+
+ //
+ // Get space needed for token information
+ //
+ if (!GetTokenInformation(hToken,
+ TokenUser,
+ NULL,
+ 0,
+ &cbTokenBuffer) ) {
+
+ if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
+ CloseHandle(hToken);
+ return(FALSE);
+ }
+ }
+
+ //
+ // Get the actual token information
+ //
+ pUserToken = (PTOKEN_USER)LocalAlloc(LPTR, cbTokenBuffer*sizeof(WCHAR));
+ if (pUserToken == NULL) {
+ CloseHandle(hToken);
+ return(FALSE);
+ }
+
+ if (!GetTokenInformation(hToken,
+ TokenUser,
+ pUserToken,
+ cbTokenBuffer,
+ &cbTokenBuffer) ) {
+ goto Error;
+ }
+
+ //
+ // Get the space needed for the User name and the Domain name
+ //
+ if (!LookupAccountSid(NULL,
+ pUserToken->User.Sid,
+ NULL, &cbAccountName,
+ NULL, &cbUserDomain,
+ &SidNameUse
+ ) ) {
+ if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
+ goto Error;
+ }
+ }
+ lpUserName = (LPTSTR)LocalAlloc(LPTR, cbAccountName*sizeof(WCHAR));
+ if (!lpUserName) {
+ goto Error;
+ }
+
+ lpUserDomain = (LPTSTR)LocalAlloc(LPTR, cbUserDomain*sizeof(WCHAR));
+ if (!lpUserDomain) {
+ LocalFree(lpUserName);
+ goto Error;
+ }
+
+ //
+ // Now get the user name and domain name
+ //
+ if (!LookupAccountSid(NULL,
+ pUserToken->User.Sid,
+ lpUserName, &cbAccountName,
+ lpUserDomain, &cbUserDomain,
+ &SidNameUse
+ ) ) {
+
+ LocalFree(lpUserName);
+ LocalFree(lpUserDomain);
+ goto Error;
+ }
+
+ *UserName = lpUserName;
+ *UserDomain = lpUserDomain;
+ bRet = TRUE;
+
+Error:
+ LocalFree(pUserToken);
+ CloseHandle(hToken);
+
+ return(bRet);
+}
+
+
+
+VOID AtSetEnvironment( LPSTARTUPINFO pStartupInfo)
+/*++
+ Get startup info & set the environment for us & our children.
+--*/
+{
+ LPTSTR UserName = NULL;
+ LPTSTR UserDomain = NULL;
+ TCHAR szComputerName[MAX_COMPUTERNAME_LENGTH+1];
+ DWORD dwComputerNameSize = MAX_COMPUTERNAME_LENGTH+1;
+
+ GetStartupInfo( pStartupInfo);
+ pStartupInfo->lpTitle = NULL;
+
+ //
+ // Changes to the initial environment made in user\winlogon\usrenv.c
+ // should be reflected below. In particular, when processor types
+ // are added in usrenv.c, they also need to be added here.
+ //
+
+ if (GetComputerName (szComputerName, &dwComputerNameSize)) {
+ SetEnvironmentVariable(COMPUTERNAME_VARIABLE, (LPTSTR) szComputerName);
+ }
+ GetUserNameAndDomain(&UserName, &UserDomain);
+ SetEnvironmentVariable( USERNAME_VARIABLE, UserName);
+ SetEnvironmentVariable( USERDOMAIN_VARIABLE, UserDomain);
+ LocalFree( UserName);
+ LocalFree( UserDomain);
+}
diff --git a/private/net/svcdlls/at/server/atmain.c b/private/net/svcdlls/at/server/atmain.c
new file mode 100644
index 000000000..5f326a12e
--- /dev/null
+++ b/private/net/svcdlls/at/server/atmain.c
@@ -0,0 +1,1060 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ atmain.c
+
+Abstract:
+
+ This is the main routine for the NT LAN Manager Schedule service.
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 06 - November - 1992
+
+Environment:
+
+ User Mode - Win32
+
+Revision History:
+
+ 06-Nov-1992 vladimv
+ Created
+
+--*/
+
+#define ATDATA_ALLOCATE
+#include "at.h"
+#undef ATDATA_ALLOCATE
+#include <atnames.h> // AT_INTERFACE_NAME
+#include <winuser.h> // for winuserp.h
+#include <wingdi.h>
+#include <..\..\..\..\windows\inc\winuserp.h> // for STARTF_DESKTOPINHERIT
+
+
+typedef enum _AT_ERROR_CONDITION {
+ AtErrorRegisterControlHandler = 0,
+ AtErrorCreateEvent,
+ AtErrorNotifyServiceController,
+ AtErrorStartRpcServer,
+ AtErrorCreateSecurityObject,
+ AtErrorMakeDataFromRegistry
+} AT_ERROR_CONDITION, *PAT_ERROR_CONDITION;
+
+
+
+
+DBGSTATIC NET_API_STATUS AtUpdateStatus( VOID)
+/*++
+
+Routine Description:
+
+ This function updates the Schedule service status with the Service
+ Controller.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NET_API_STATUS status = NERR_Success;
+
+ if (AtGlobalServiceStatusHandle == (SERVICE_STATUS_HANDLE) NULL) {
+ AtLog(( AT_DEBUG_MAIN, "Cannot call SetServiceStatus, no status handle.\n"));
+ return( ERROR_INVALID_HANDLE);
+ }
+
+ if ( ! SetServiceStatus( AtGlobalServiceStatusHandle, &AtGlobalServiceStatus)) {
+ status = GetLastError();
+ AtLog(( AT_DEBUG_MAIN, "SetServiceStatus error %lu\n", status));
+ }
+
+ return( status);
+}
+
+
+
+DBGSTATIC VOID AtShutdown(
+ IN NET_API_STATUS ErrorCode,
+ IN DWORD initState
+ )
+/*++
+
+Routine Description:
+
+ This function shuts down the Schedule service.
+
+Arguments:
+
+ ErrorCode - Supplies the error code of the failure
+
+ initState - Supplies a flag to indicate how far we got with initializing
+ the Schedule service before an error occured, thus the amount of
+ clean up needed.
+
+Return Value:
+
+ None.
+
+Note:
+
+ BUGBUG There is a possible race condition between main thread shutting
+ BUGBUG down & some of the rpc-api thread trying to complete their work.
+
+--*/
+{
+ //
+ // Service stop still pending. Update checkpoint counter and the
+ // status with the Service Controller.
+ //
+ (AtGlobalServiceStatus.dwCheckPoint)++;
+ (VOID) AtUpdateStatus();
+
+
+ if ( initState & AT_RPC_SERVER_STARTED) {
+ //
+ // Stop the RPC server
+ //
+ NetpStopRpcServer( atsvc_ServerIfHandle);
+ }
+
+ if ( initState & AT_SECURITY_OBJECT_CREATED) {
+ //
+ // Destroy schedule security object
+ //
+ AtDeleteSecurityObject();
+ }
+
+ //
+ // Service stop still pending. Update checkpoint counter and the
+ // status with the Service Controller.
+ //
+ (AtGlobalServiceStatus.dwCheckPoint)++;
+ (VOID) AtUpdateStatus();
+
+ if ( initState & AT_EVENT_CREATED) {
+ //
+ // Close handle to the event. Note that we do not need to signal
+ // the event. We assume AtShutdown() will be called from the main
+ // schedule service thread and only when it is safe - i.e. when
+ // main service thread is outside its main loop. This avoids some
+ // ugly competition between main thread & shutdown.
+ //
+ CloseHandle( AtGlobalEvent);
+ }
+
+ //
+ // We are done with cleaning up. Tell Service Controller that we are
+ // stopped.
+ //
+ AtGlobalServiceStatus.dwCurrentState = SERVICE_STOPPED;
+ AtGlobalServiceStatus.dwControlsAccepted = 0;
+
+
+ SET_SERVICE_EXITCODE(
+ ErrorCode,
+ AtGlobalServiceStatus.dwWin32ExitCode,
+ AtGlobalServiceStatus.dwServiceSpecificExitCode
+ );
+
+ AtGlobalServiceStatus.dwCheckPoint = 0;
+ AtGlobalServiceStatus.dwWaitHint = 0;
+
+ (VOID) AtUpdateStatus();
+}
+
+
+
+DBGSTATIC VOID AtHandleError(
+ IN AT_ERROR_CONDITION failingCondition,
+ IN NET_API_STATUS status,
+ IN DWORD initState
+ )
+/*++
+
+Routine Description:
+
+ This function handles a Schedule service error condition. If the error
+ condition is fatal, it shuts down the Schedule service.
+
+Arguments:
+
+ failingCondition - Supplies a value which indicates what the failure is.
+
+ status - Supplies the status code for the failure.
+
+ initState - Supplies a flag to indicate how far we got with initializing
+ the Schedule service before an error occured, thus the amount of
+ clean up needed.
+
+Return Value:
+
+ None.
+
+--*/
+{
+
+#ifdef AT_DEBUG
+
+ PCHAR string;
+
+ switch (failingCondition) {
+
+ case AtErrorRegisterControlHandler:
+ string = "Cannot register control handler";
+ break;
+
+ case AtErrorCreateEvent:
+ string = "Cannot create event";
+ break;
+
+ case AtErrorNotifyServiceController:
+ string = "SetServiceStatus error";
+ break;
+
+ case AtErrorStartRpcServer:
+ string = "Cannot start RPC server";
+ break;
+
+ case AtErrorCreateSecurityObject:
+ string = "Error in creating security object";
+ break;
+
+ case AtErrorMakeDataFromRegistry:
+ string = "Severe error while initializing from registry";
+ break;
+
+ default:
+ string = "Unknown error condition %lu\n";
+ NetpAssert(FALSE);
+ }
+
+ NetpKdPrint((
+ "[Job] %s " FORMAT_API_STATUS "\n",
+ string,
+ status
+ ));
+#endif // AT_DEBUG
+
+ AtShutdown( status, initState);
+}
+
+
+VOID AtControlHandler( IN DWORD Opcode)
+/*++
+
+Routine Description:
+
+ This is the service control handler of the Schedule service.
+
+Arguments:
+
+ Opcode - Supplies a value which specifies the action for the Schedule
+ service to perform.
+
+Return Value:
+
+ None.
+
+Comments:
+
+ Note that AtGlobalCriticalSection is used to protect changes both to
+ AtGlobalTasks & AtGlobalServiceStatus.dwCurrentState.
+
+--*/
+{
+ AtLog(( AT_DEBUG_MAIN, "In Control Handler\n"));
+
+ switch (Opcode) {
+
+ case SERVICE_CONTROL_PAUSE:
+
+ // Take critical section and set status so that no new requests
+ // can be submitted. Current requests will still be honored.
+ // Then release critical seciton.
+
+ EnterCriticalSection( &AtGlobalCriticalSection);
+ AtGlobalServiceStatus.dwCurrentState = SERVICE_PAUSED;
+ LeaveCriticalSection( &AtGlobalCriticalSection);
+
+ break;
+
+ case SERVICE_CONTROL_CONTINUE:
+
+ // Take critical section and set status so that new requests
+ // can be submitted. Then release critical seciton.
+
+ EnterCriticalSection( &AtGlobalCriticalSection);
+ AtGlobalServiceStatus.dwCurrentState = SERVICE_RUNNING;
+ LeaveCriticalSection( &AtGlobalCriticalSection);
+
+ break;
+
+ case SERVICE_CONTROL_STOP:
+
+ if (AtGlobalServiceStatus.dwCurrentState != SERVICE_STOP_PENDING) {
+
+ AtLog(( AT_DEBUG_MAIN, "Stopping schedule service...\n"));
+
+ AtGlobalServiceStatus.dwCurrentState = SERVICE_STOP_PENDING;
+ AtGlobalServiceStatus.dwCheckPoint = 1;
+ AtGlobalServiceStatus.dwWaitHint = AT_WAIT_HINT_TIME;
+
+ //
+ // Send the status response.
+ //
+ (VOID) AtUpdateStatus();
+
+ EnterCriticalSection( &AtGlobalCriticalSection);
+ AtGlobalTasks |= AT_SERVICE_SHUTDOWN;
+ LeaveCriticalSection( &AtGlobalCriticalSection);
+
+ if (! SetEvent( AtGlobalEvent)) {
+ AtLog(( AT_DEBUG_CRITICAL, "Error setting the event 0x%x\n", GetLastError()));
+ AtAssert( FALSE);
+ }
+
+ return;
+ }
+ break;
+
+ case SERVICE_CONTROL_INTERROGATE:
+ break;
+
+ default:
+ AtLog(( AT_DEBUG_CRITICAL, "Unknown schedule service opcode 0x%x\n", Opcode));
+ break;
+ }
+
+ //
+ // Send the status response.
+ //
+ (VOID) AtUpdateStatus();
+}
+
+
+
+DBGSTATIC NET_API_STATUS AtInitialize(
+ IN PAT_TIME pTime,
+ OUT LPDWORD pInitState
+ )
+/*++
+
+Routine Description:
+
+ This function initializes the Schedule service.
+
+Arguments:
+
+ pInitState - Returns a flag to indicate how far we got with initializing
+ the Schedule service before an error occured.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NET_API_STATUS status;
+
+
+ //
+ // Initialize all the status fields so that subsequent calls to
+ // SetServiceStatus need to only update fields that changed.
+ //
+
+ AtGlobalServiceStatus.dwServiceType = SERVICE_WIN32;
+ AtGlobalServiceStatus.dwCurrentState = SERVICE_START_PENDING;
+ AtGlobalServiceStatus.dwControlsAccepted = 0;
+ AtGlobalServiceStatus.dwCheckPoint = 1;
+ AtGlobalServiceStatus.dwWaitHint = AT_WAIT_HINT_TIME;
+
+ SET_SERVICE_EXITCODE(
+ NO_ERROR,
+ AtGlobalServiceStatus.dwWin32ExitCode,
+ AtGlobalServiceStatus.dwServiceSpecificExitCode
+ );
+
+ //
+ // Initialize schedule service to receive service requests by registering
+ // the control handler.
+ //
+ if ((AtGlobalServiceStatusHandle = RegisterServiceCtrlHandler(
+ SERVICE_SCHEDULE,
+ AtControlHandler
+ )) == (SERVICE_STATUS_HANDLE) NULL) {
+
+ status = GetLastError();
+ AtHandleError( AtErrorRegisterControlHandler, status, *pInitState);
+ return( status);
+ }
+
+ //
+ // Read the registry path & build queue of things to do.
+ //
+ status = AtMakeDataFromRegistry( pTime);
+ if ( status != NERR_Success) {
+ AtHandleError( AtErrorMakeDataFromRegistry, status, *pInitState);
+ return( status);
+ }
+ (*pInitState) |= AT_QUEUES_CREATED;
+
+ AtGlobalPermitServerOperators = AtPermitServerOperators();
+
+ //
+ // Create the only event used by schedule service.
+ //
+ if ((AtGlobalEvent =
+ CreateEvent(
+ NULL, // Event attributes
+ FALSE, // Event will be automaticallly reset
+ FALSE,
+ NULL // Initial state not signalled
+ )) == NULL) {
+
+ status = GetLastError();
+ AtHandleError( AtErrorCreateEvent, status, *pInitState);
+ return( status);
+ }
+ (*pInitState) |= AT_EVENT_CREATED;
+
+
+ //
+ // Notify the Service Controller for the first time that we are alive
+ // and we are start pending
+ //
+ if ((status = AtUpdateStatus()) != NERR_Success) {
+
+ AtHandleError( AtErrorNotifyServiceController, status, *pInitState);
+ return( status);
+ }
+
+ AtSetEnvironment( &AtGlobalStartupInfo);
+
+ //
+ // Create Schedule service security object
+ //
+ if ((status = AtCreateSecurityObject()) != NERR_Success) {
+
+ AtHandleError( AtErrorCreateSecurityObject, status, *pInitState);
+ return( status);
+ }
+ (*pInitState) |= AT_SECURITY_OBJECT_CREATED;
+
+
+ //
+ // Initialize the schedule service to receive RPC requests.
+ // Note that interface name is defined in atsvc.idl file.
+ //
+ if ((status = NetpStartRpcServer(
+ AT_INTERFACE_NAME,
+ atsvc_ServerIfHandle
+ )) != NERR_Success) {
+
+ AtHandleError( AtErrorStartRpcServer, status, *pInitState);
+ return( status);
+ }
+ (*pInitState) |= AT_RPC_SERVER_STARTED;
+
+
+ //
+ // We are done with starting the Schedule service. Tell Service
+ // Controller our new status.
+ //
+
+ AtGlobalServiceStatus.dwCurrentState = SERVICE_RUNNING;
+ AtGlobalServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP |
+ SERVICE_ACCEPT_PAUSE_CONTINUE;
+ AtGlobalServiceStatus.dwCheckPoint = 0;
+ AtGlobalServiceStatus.dwWaitHint = 0;
+
+ if ((status = AtUpdateStatus()) != NERR_Success) {
+
+ AtHandleError( AtErrorNotifyServiceController, status, *pInitState);
+ return( status);
+ }
+
+ AtLog(( AT_DEBUG_MAIN, "Successful Initialization\n"));
+
+ return( NERR_Success);
+}
+
+
+
+DBGSTATIC DWORD AtCalculateTimeout(
+ LARGE_INTEGER Runtime,
+ LARGE_INTEGER TimeLargeInteger
+ )
+/*++
+
+Routine Description:
+
+ Given the current time and the next time to run, this routine returns
+ the number of miliseconds to sleep before the next runttime.
+
+Arguments:
+
+ Runtime - next time to run a job
+ TimeLargeInteger - current time
+
+Return Value:
+
+ Number of miliseconds to sleep before the next runtime.
+
+--*/
+{
+ DWORD Remainder;
+
+ Runtime.QuadPart = Runtime.QuadPart - TimeLargeInteger.QuadPart;
+ Runtime = RtlExtendedLargeIntegerDivide(
+ Runtime,
+ NT_TICKS_IN_WINDOWS_TICK,
+ &Remainder
+ );
+ if ( Runtime.HighPart != 0 || Runtime.LowPart > MAX_BUSY_TIMEOUT) {
+ return( MAX_BUSY_TIMEOUT);
+ } else {
+ return( Runtime.LowPart);
+ }
+}
+
+
+DBGSTATIC BOOL AtBatOrCmd( IN OUT PWCHAR Command)
+/*++
+
+ Returns true if we have .BAT or .CMD file.
+
+--*/
+{
+ WCHAR NameBuffer[ MAX_PATH];
+ PWCHAR Temp;
+ DWORD Length;
+ DWORD Index;
+ PWCHAR BatOrCmd[] = { L".bat", L".cmd"};
+
+ for ( Temp = Command; *Temp != 0; Temp++) {
+ if ( *Temp == L' ' || *Temp == L'\t') {
+ *Temp = 0;
+ break;
+ }
+ }
+
+ for ( Index = 0; Index < sizeof(BatOrCmd)/sizeof(BatOrCmd[0]); Index++) {
+ Temp = NULL;
+ Length = SearchPathW(
+ NULL,
+ Command,
+ BatOrCmd[ Index],
+ sizeof( NameBuffer) / sizeof( WCHAR),
+ NameBuffer,
+ &Temp
+ );
+ if ( Length == 0 || Length >= MAX_PATH || Temp == NULL) {
+ continue;
+ }
+#ifdef AT_DEBUG
+ Temp = wcschr( Temp, L'.');
+ if ( Temp == NULL || _wcsicmp( Temp, BatOrCmd[ Index])) {
+ AtLog(( AT_DEBUG_CRITICAL, "BatOrCmd: Command=%ws Extension=%ws\n",
+ Command, BatOrCmd[ Index]));
+ }
+#endif
+ return( TRUE);
+ }
+ return( FALSE);
+}
+
+
+VOID AtReportEvent(
+ IN WORD EventType,
+ IN DWORD MessageId,
+ IN WORD StringCount,
+ IN LPWSTR * StringArray,
+ IN DWORD RawDataBufferLength OPTIONAL,
+ IN LPVOID RawDataBuffer
+ )
+/*++
+
+Routine Description:
+
+ Writes an error message and ascii string to the error log. Also,
+ writes the data in the data buf if there are any.
+
+Arguments:
+
+ EventType - warning, error, ...
+
+ MessageId - Message ID
+
+ StringCount - number of strings to use in the string array
+
+ StringArray - array of insertion strings
+
+ RawDataBufferLength - size of data to be printed from the auxiliary data
+ buffer. May be zero, in which case the actual value depends on
+ "RawDataBuffer". It is 0 if "RawDataBuffer" is NULL, else it is
+ "sizeof( wchar) * wcslen( RawDataBuffer)".
+
+ RawDataBuffer - data buffer containing secondary error code & some
+ other useful info. Must be NULL terminated string or NULL if
+ "RawDataBufferLength" is zero.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ HANDLE logHandle;
+
+ logHandle = RegisterEventSource( NULL, SCHEDULE_EVENTLOG_NAME);
+
+ // If the event log cannot be opened, just return.
+
+ if ( logHandle == NULL) {
+#ifdef AT_DEBUG
+ AtLog(( AT_DEBUG_CRITICAL, "ReportEvent: RegisterEventSource() failed with error %d",
+ GetLastError()));
+#endif
+ return;
+ }
+
+ //
+ // Use default for RawDataBufferLength if caller requested us so.
+ //
+ if ( RawDataBufferLength == 0 && RawDataBuffer != NULL) {
+ RawDataBufferLength = sizeof( TCHAR) * wcslen( (LPWSTR)RawDataBuffer);
+ }
+
+ if ( !ReportEvent(
+ logHandle,
+ EventType,
+ 0, // event category
+ MessageId, // event id
+ NULL, // user SID. We're local system - uninteresting
+ StringCount, // number of strings
+ RawDataBufferLength, // raw data size
+ StringArray, // string array
+ RawDataBuffer // raw data buffer
+ )) {
+#ifdef AT_DEBUG
+ AtLog(( AT_DEBUG_CRITICAL, "ReportEvent: ReportEvent() failed with error %d",
+ GetLastError()));
+#endif // NOT_YET
+ }
+
+ DeregisterEventSource( logHandle);
+}
+
+
+
+DBGSTATIC VOID AtCreateProcess( PAT_RECORD pRecord)
+/*++
+
+Routine Description:
+
+ Creates a process for a given record.
+
+Arguments:
+
+ pRecord - pointer to AT_RECORD
+
+--*/
+#define CMD_EXE L"cmd /c "
+{
+ PROCESS_INFORMATION ProcessInformation;
+ BOOL success;
+ BOOL JobInteractive;
+ LPWSTR StringArray[ 2];
+ WCHAR ErrorCodeString[ 25];
+ WCHAR Command[ sizeof( CMD_EXE) + MAXIMUM_COMMAND_LENGTH];
+
+ wcscpy( Command, pRecord->Command);
+
+ if ( AtBatOrCmd( Command)) {
+ wcscpy( Command, CMD_EXE);
+ wcscat( Command, pRecord->Command);
+ } else {
+ wcscpy( Command, pRecord->Command);
+ }
+
+ if ( pRecord->Flags & JOB_NONINTERACTIVE) {
+ JobInteractive = FALSE;
+ } else {
+ //
+ // Job is to be executed interactively only if the global system flag
+ // allows that.
+ //
+ JobInteractive = AtSystemInteractive();
+ if ( !JobInteractive) {
+ StringArray[ 0] = pRecord->Command;
+ AtReportEvent( EVENTLOG_WARNING_TYPE, EVENT_COMMAND_NOT_INTERACTIVE,
+ 1, StringArray, 0, NULL);
+ }
+ }
+
+ //
+ // If the process is to be interactive, set the appropriate flags.
+ //
+ if ( JobInteractive) {
+ AtGlobalStartupInfo.dwFlags |= STARTF_DESKTOPINHERIT;
+ AtGlobalStartupInfo.lpDesktop = INTERACTIVE_DESKTOP;
+ }
+ else {
+ AtGlobalStartupInfo.dwFlags &= (~STARTF_DESKTOPINHERIT);
+ AtGlobalStartupInfo.lpDesktop = NULL;
+ }
+
+ success = CreateProcess(
+ NULL, // image name is imbedded in the command line
+ Command, // command line
+ NULL, // pSecAttrProcess
+ NULL, // pSecAttrThread
+ FALSE, // this process will not inherit our handles
+ CREATE_NEW_CONSOLE | CREATE_SEPARATE_WOW_VDM,
+// CREATE_NO_WINDOW, // the only choice that works
+ NULL, // pEnvironment
+ NULL, // pCurrentDirectory
+ &AtGlobalStartupInfo,
+ &ProcessInformation
+ );
+
+ if ( success == FALSE) {
+
+ DWORD winError;
+
+ winError = GetLastError();
+
+ AtLog(( AT_DEBUG_CRITICAL, "CreateProcess( %ws) fails with winError = %d\n",
+ pRecord->Command, winError));
+
+ pRecord->Flags |= JOB_EXEC_ERROR;
+
+ StringArray[ 0] = pRecord->Command;
+ wcscpy( ErrorCodeString, L"%%"); // tell event viewer to expand this error
+ ultow( winError, ErrorCodeString + 2, 10);
+ StringArray[ 1] = ErrorCodeString;
+ AtReportEvent( EVENTLOG_ERROR_TYPE, EVENT_COMMAND_START_FAILED,
+ 2, StringArray, 0, NULL);
+
+ } else {
+ AtLog(( AT_DEBUG_MAIN,
+ "CreateProcess( %ws) succeeded\t"
+ "ProcessInformation=%x %x %x %x JobId=%x JobDay=%x Runtime=%x:%x\n",
+ pRecord->Command,
+ ProcessInformation.hProcess,
+ ProcessInformation.hThread,
+ ProcessInformation.dwProcessId,
+ ProcessInformation.dwThreadId,
+ pRecord->JobId,
+ pRecord->JobDay,
+ pRecord->Runtime.HighPart,
+ pRecord->Runtime.LowPart
+ ));
+ CloseHandle( ProcessInformation.hThread);
+ CloseHandle( ProcessInformation.hProcess);
+ pRecord->Flags &= ~JOB_EXEC_ERROR;
+ }
+
+ ASSERT( pRecord->Debug++ < 20);
+
+ if ( pRecord->JobDay != JOB_INVALID_DAY) {
+
+ ASSERT( (pRecord->Flags & JOB_RUN_PERIODICALLY) == 0 &&
+ ( pRecord->DaysOfWeek != 0 || pRecord->DaysOfMonth != 0));
+
+ if ( pRecord->Flags & JOB_CLEAR_WEEKDAY) {
+ pRecord->DaysOfWeek &= ~( 1<< pRecord->JobDay);
+ } else {
+ pRecord->DaysOfMonth &= ~( 1<< ( pRecord->JobDay - 1));
+ }
+ pRecord->JobDay = JOB_INVALID_DAY;
+
+ } else {
+
+ ASSERT( (pRecord->Flags & JOB_RUN_PERIODICALLY) != 0 ||
+ ( pRecord->DaysOfWeek == 0 && pRecord->DaysOfMonth == 0));
+
+ }
+}
+
+
+
+DBGSTATIC VOID AtUpdateRecords( IN PAT_TIME pTime)
+/*++
+
+Routine Description:
+
+ Reshufles runtime queue to reflect the time change.
+
+Arguments:
+
+ pTime - pointer to the new system time
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PLIST_ENTRY pListEntry;
+ PAT_RECORD pRecord;
+
+ AtLog(( AT_DEBUG_MAIN, "Update runtime queue.\n"));
+
+ //
+ // If we want to simulate OS/2 behavior, here we can insert a block
+ // which runs all records whose Runtimes are earlier than the new
+ // Jan.01,70 system time.
+ //
+
+ if ( IsListEmpty( &AtGlobalJobIdListHead) == TRUE) {
+ return; // do not bother waking up the master if queue is empty
+ }
+
+ for ( pListEntry = AtGlobalJobIdListHead.Flink;
+ pListEntry != &AtGlobalJobIdListHead;
+ pListEntry = pListEntry->Flink) {
+
+ pRecord = CONTAINING_RECORD(
+ pListEntry,
+ AT_RECORD,
+ JobIdList
+ );
+
+ AtCalculateRuntime( pRecord, pTime);
+ AtRemoveRecord( pRecord, RUNTIME_QUEUE);
+ AtInsertRecord( pRecord, RUNTIME_QUEUE);
+ }
+}
+
+
+
+DBGSTATIC BOOL AtTimeChanged(
+ IN PAT_TIME pGetupTime,
+ IN OUT PAT_TIME pSleepTime
+ )
+/*++
+
+Routine Description:
+
+ Detects if there was a change in system time since we went to sleep.
+ If there was a sustem time it revises SleepTime to match GetupTime.
+
+Arguments:
+
+ pGetupTime - points to AT_TIME structure for the Getup moment
+ pSleepTime - points to AT_TIME structure for the Sleep moment
+
+
+Return Value:
+
+ TRUE - if there was a time change
+ FALSE - otherwise
+
+--*/
+
+{
+ //
+ // DateDelta says how long we slept in units that CAN be adjusted
+ // by user.
+ //
+ LARGE_INTEGER DateDelta;
+ //
+ // BootDelta says how long we slept in units that CANNOT be adjusted
+ // by user.
+ //
+ LARGE_INTEGER BootDelta;
+#ifdef AT_DEBUG
+ PCHAR Format;
+#endif
+
+ DateDelta.QuadPart = pGetupTime->LargeInteger.QuadPart -
+ pSleepTime->LargeInteger.QuadPart;
+ BootDelta = RtlEnlargedUnsignedMultiply(
+ pGetupTime->TickCount - pSleepTime->TickCount,
+ NT_TICKS_IN_WINDOWS_TICK
+ );
+
+ if ( DateDelta.QuadPart < 0) {
+#ifdef AT_DEBUG
+ Format = "TimeChanged: negative DateDelta: Getup=%x:%x|%x Sleep=%x:%x|%x\n";
+#endif
+ goto time_changed;
+ }
+
+ if ( DateDelta.QuadPart >= BootDelta.QuadPart ) {
+ DateDelta.QuadPart = DateDelta.QuadPart - BootDelta.QuadPart;
+ } else {
+ DateDelta.QuadPart = BootDelta.QuadPart - DateDelta.QuadPart;
+ }
+
+ if ( DateDelta.HighPart != 0 ||
+ DateDelta.LowPart > ONE_MINUTE_IN_NT_TICKS) {
+#ifdef AT_DEBUG
+ Format = "TimeChanged: large DateDelta: Getup=%x:%x!%x Sleep=%x:%x!%x\n";
+#endif
+ goto time_changed;
+ }
+ return( FALSE);
+
+time_changed:
+
+ AtLog(( AT_DEBUG_MAIN, Format,
+ pGetupTime->LargeInteger.HighPart, pGetupTime->LargeInteger.LowPart, pGetupTime->TickCount,
+ pSleepTime->LargeInteger.HighPart, pSleepTime->LargeInteger.LowPart, pSleepTime->TickCount));
+ pSleepTime->LargeInteger.QuadPart = pGetupTime->LargeInteger.QuadPart - BootDelta.QuadPart;
+ AtTimeGetCurrents( pSleepTime);
+ AtLog(( AT_DEBUG_MAIN, "TimeChanged: revised Sleep=%x:%x|%x\n",
+ pSleepTime->LargeInteger.HighPart, pSleepTime->LargeInteger.LowPart, pSleepTime->TickCount));
+ return( TRUE);
+}
+
+
+VOID SCHEDULE_main(
+ DWORD argc,
+ LPWSTR * argv
+ )
+/*++
+
+Routine Description:
+
+ This is the main routine of the Schedule Service which registers
+ itself as an RPC server and notifies the Service Controller of the
+ Schedule service control entry point.
+
+ After the Schedule Service is started, this thread is used to spawn
+ processes corresponding to records waiting in the queue.
+
+Arguments:
+
+ argc - Supplies the number of strings specified in argv
+
+ argv - Supplies string arguments that are specified in the
+ StartService API call.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ DWORD initState = 0;
+
+ UNREFERENCED_PARAMETER( argc);
+ UNREFERENCED_PARAMETER( argv);
+
+#ifdef AT_DEBUG
+ AtDebugCreate();
+#endif
+
+#ifdef NOT_YET
+ if ( !AllocConsole()) {
+ AT_DEBUG_PRINT(
+ AT_DEBUG_MAIN,(
+ "[Job] AllocConsole() returns WinError = %d\n",
+ GetLastError()
+ ));
+ }
+#endif // NOT_YET
+
+ //
+ // Perhaps one should use something that builds in current day & time
+ // to be even more random.
+ //
+ AtGlobalSeed = GetTickCount();
+
+ InitializeCriticalSection( &AtGlobalCriticalSection);
+
+ //
+ // Initialize the scheduler.
+ //
+ AtTimeGet( &AtGlobalGetupTime); // initialize AtGlobalGetupTime
+ if ( AtInitialize( &AtGlobalGetupTime, &initState) != NERR_Success) {
+ return;
+ }
+
+ //
+ // We enter and leave loop while holding the critical section.
+ //
+
+ EnterCriticalSection( &AtGlobalCriticalSection);
+
+ for ( ; ; ) {
+
+ DWORD timeout; // in milliseconds
+ PAT_RECORD pRecord;
+
+ if ( AtGlobalTasks & AT_SERVICE_SHUTDOWN) {
+ AtLog(( AT_DEBUG_MAIN, "Service shutdown.\n"));
+ break;
+ }
+
+ AtLogRuntimeList( "Before executions");
+
+ timeout = MAX_LAZY_TIMEOUT;
+
+ while ( IsListEmpty( &AtGlobalRuntimeListHead) == FALSE) {
+
+ pRecord = CONTAINING_RECORD(
+ AtGlobalRuntimeListHead.Flink,
+ AT_RECORD,
+ RuntimeList
+ );
+
+ if (pRecord->Runtime.QuadPart > AtGlobalGetupTime.LargeInteger.QuadPart) {
+ timeout = AtCalculateTimeout( pRecord->Runtime, AtGlobalGetupTime.LargeInteger);
+ break; // nothing to do for now
+ }
+
+ AtCreateProcess( pRecord);
+
+ if ( pRecord->DaysOfWeek == 0 && pRecord->DaysOfMonth == 0) {
+ AtDeleteKey( pRecord);
+ AtRemoveRecord( pRecord, BOTH_QUEUES);
+ (VOID)LocalFree( pRecord);
+ } else {
+ AtCalculateRuntime( pRecord, &AtGlobalGetupTime);
+ AtRemoveRecord( pRecord, RUNTIME_QUEUE);
+ AtInsertRecord( pRecord, RUNTIME_QUEUE);
+ }
+ }
+
+ AtGlobalSleepTime = AtGlobalGetupTime;
+ AtLogRuntimeList( "After executions");
+
+ LeaveCriticalSection( &AtGlobalCriticalSection);
+
+ AtLogTimeout( timeout);
+ (VOID)WaitForSingleObject( AtGlobalEvent, timeout);
+
+ EnterCriticalSection( &AtGlobalCriticalSection);
+
+ AtTimeGet( &AtGlobalGetupTime);
+ if ( AtTimeChanged( &AtGlobalGetupTime, &AtGlobalSleepTime) == TRUE) {
+ //
+ // SleepTime has been revised and is now expressed in GetupTime
+ // units. Reorder the queue with respect to revised SleepTime
+ // and run whatever needs to be run.
+ // Above has the same effect as if a time change occured
+ // JUST BEFORE the last time main thread ordered the runtime
+ // queue and went to sleep.
+ //
+ AtUpdateRecords( &AtGlobalSleepTime);
+ }
+ }
+
+ LeaveCriticalSection( &AtGlobalCriticalSection);
+
+ AtShutdown( NERR_Success, initState);
+#ifdef AT_DEBUG
+ AtDebugDelete();
+#endif
+}
diff --git a/private/net/svcdlls/at/server/atreg.c b/private/net/svcdlls/at/server/atreg.c
new file mode 100644
index 000000000..4e04a61f3
--- /dev/null
+++ b/private/net/svcdlls/at/server/atreg.c
@@ -0,0 +1,603 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ atreg.c
+
+Abstract:
+
+ Routines that deal with the registry & queues.
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 06 - November - 1992
+
+Environment:
+
+ User Mode - Win32
+
+Revision History:
+
+ 06-Nov-1992 vladimv
+ Created
+
+--*/
+
+#include "at.h"
+
+
+DWORD AtCreateKey( PAT_RECORD pRecord)
+/*++
+
+Routine Description:
+
+ This routine first loops trying to find an unused name in AT part
+ of the registry. Then it saves the data from the in memory record
+ into the new registry key. Finally, it saves the name of the
+ registry key in the in memory record.
+
+Arguments:
+
+ pRecord - pointer to record for which we need to create registry key
+
+Return Value:
+
+ TRUE if success
+ FALSE otherwise
+
+Note:
+
+
+--*/
+{
+ DWORD random;
+ DWORD status;
+ HKEY Key;
+ DWORD disposition;
+ DWORD Length;
+ PWCHAR Name;
+ WCHAR Buffer[ AT_KEY_NAME_MAX_LEN + 1];
+ AT_SCHEDULE Schedule;
+
+
+ for ( ; ;) {
+
+ random = RtlRandom( &AtGlobalSeed);
+
+ Name = &Buffer[ AT_KEY_NAME_MAX_LEN];
+ *Name = 0; // null terminate the string
+
+ while ( random != 0) {
+
+ *--Name = L"0123456789ABCDEF"[ random & 0xF];
+ random >>= 4;
+ }
+
+ status = RegCreateKeyEx(
+ AtGlobalKey,
+ Name,
+ 0,
+ NULL,
+ REG_OPTION_NON_VOLATILE,
+ KEY_READ | KEY_WRITE,
+ NULL,
+ &Key,
+ &disposition
+ );
+ if (status != ERROR_SUCCESS){
+ KdPrint((
+ "[Job] Cannot create Name = %ws, status = %d\n",
+ Name,
+ status
+ ));
+ return( status );
+ }
+ if ( disposition == REG_CREATED_NEW_KEY) {
+ break;
+ }
+
+ KdPrint((
+ "[Job] Key name collision Name = %ws\n",
+ Name
+ ));
+ }
+
+ wcscpy( pRecord->Name, Name);
+ Schedule.JobTime = pRecord->JobTime;
+ Schedule.DaysOfMonth = pRecord->DaysOfMonth;
+ Schedule.DaysOfWeek = pRecord->DaysOfWeek;
+ Schedule.Flags = pRecord->Flags;
+ Schedule.Reserved = 0;
+
+ status = RegSetValueEx(
+ Key,
+ AT_SCHEDULE_NAME,
+ 0,
+ REG_BINARY,
+ (LPBYTE)&Schedule,
+ sizeof( Schedule)
+ );
+ if ( status != STATUS_SUCCESS) {
+ return( status );
+ }
+
+ status = RegSetValueEx(
+ Key,
+ AT_COMMAND_NAME,
+ 0,
+ REG_SZ,
+ (LPBYTE)&pRecord->Command[0],
+ pRecord->CommandSize
+ );
+ if ( status != STATUS_SUCCESS) {
+ return( status );
+ }
+
+#ifdef AT_DEBUG
+ {
+ WCHAR Command[ MAXIMUM_COMMAND_LENGTH + 1];
+ DWORD type;
+
+ memset( Command, 0, sizeof( Command));
+ Length = sizeof( Command);
+ status = RegQueryValueEx(
+ Key,
+ AT_COMMAND_NAME,
+ NULL,
+ &type,
+ (LPBYTE)Command,
+ &Length
+ );
+ if ( status != ERROR_SUCCESS ||
+ type != REG_SZ ||
+ wcscmp( pRecord->Command, Command) != 0 ||
+ (DWORD)pRecord->CommandSize != Length ) {
+ ASSERT( FALSE);
+ KdPrint((
+ "[Job] Registry bug: status=%d, type=%x, "
+ " pRecord->Command=%ws, Command=%ws, "
+ " pRecord->CommandSize=%d, Length=%d\n",
+ status,
+ type,
+ pRecord->Command,
+ Command,
+ pRecord->CommandSize,
+ Length
+ ));
+ }
+ }
+#endif // AT_DEBUG
+
+ (VOID)RegCloseKey( Key);
+
+ return( NERR_Success );
+}
+
+
+
+BOOL AtDeleteKey( PAT_RECORD pRecord)
+/*++
+
+Routine Description:
+
+ Given an in memory record this routine deletes the registry key
+ with corresponding name.
+
+Arguments:
+
+ pRecord - pointer to record for which we need to delete registry key
+
+Return Value:
+
+ TRUE if success
+ FALSE otherwise
+
+--*/
+{
+ DWORD status;
+
+ status = RegDeleteKey(
+ AtGlobalKey,
+ pRecord->Name
+ );
+ if (status != ERROR_SUCCESS) {
+ KdPrint((
+ "[Job] Cannot delete pRecord->Name = %ws, status = %d\n",
+ pRecord->Name,
+ status
+ ));
+ return( FALSE);
+ }
+ return( TRUE);
+}
+
+
+
+VOID AtInsertRecord(
+ PAT_RECORD pNewRecord,
+ DWORD QueueMask
+ )
+/*++
+
+Routine Description:
+
+ Depending on the value of a QueueMask argument, this function does
+ one or both of the following:
+
+ - inserts record in a doubly linked list sorted by Runtime
+ - inserts record in a doubly linked list sorted by JobId
+
+ Since newly added jobs have ever increasing JobId-s, JobId queue
+ insertion is done by inserting job at the end of the JobId queue.
+
+
+Arguments:
+
+ pNewRecord - pointer to record to be inserted
+ QueueMask - mask of queues where this record should be inserted to.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PAT_RECORD pRecord;
+ PLIST_ENTRY pListEntry;
+
+ if ( QueueMask & RUNTIME_QUEUE) {
+
+ for ( pListEntry = AtGlobalRuntimeListHead.Flink;
+ pListEntry != &AtGlobalRuntimeListHead;
+ pListEntry = pListEntry->Flink) {
+
+ pRecord = CONTAINING_RECORD(
+ pListEntry,
+ AT_RECORD,
+ RuntimeList
+ );
+
+ if ( pRecord->Runtime.QuadPart >= pNewRecord->Runtime.QuadPart) {
+ InsertTailList( &pRecord->RuntimeList, &pNewRecord->RuntimeList);
+ break;
+ }
+ }
+
+ if ( pListEntry == &AtGlobalRuntimeListHead) {
+ InsertTailList( &AtGlobalRuntimeListHead, &pNewRecord->RuntimeList);
+ }
+ }
+
+ if ( QueueMask & JOBID_QUEUE) {
+ InsertTailList( &AtGlobalJobIdListHead, &pNewRecord->JobIdList);
+ }
+
+}
+
+
+
+NET_API_STATUS AtMakeDataFromRegistry( IN PAT_TIME pTime)
+{
+ NET_API_STATUS status;
+ HKEY Key;
+ DWORD index;
+ WCHAR NameBuffer[ 20]; // some arbitrary value
+ FILETIME lastWriteTime;
+ PAT_RECORD pRecord;
+ WCHAR Command[ MAXIMUM_COMMAND_LENGTH + 1];
+ AT_SCHEDULE Schedule;
+ DWORD Length;
+ DWORD type;
+ DWORD NameSize;
+ DWORD CommandSize;
+
+ InitializeListHead( &AtGlobalRuntimeListHead);
+ InitializeListHead( &AtGlobalJobIdListHead);
+
+
+ // First open the AT service registry tree.
+
+ status = RegOpenKeyEx(
+ HKEY_LOCAL_MACHINE,
+ AT_REGISTRY_PATH,
+ 0,
+ KEY_READ,
+ &AtGlobalKey
+ );
+ if (status != ERROR_SUCCESS){
+ KdPrint(("[Job] Cannot open AtGlobalKey, status = %d\n", status));
+ return( status);
+ }
+
+ for ( index = 0; ; index++) {
+
+ //
+ // Regedit can sometimes display other keys in addition to keys
+ // found here. Also, it often fails to display last character in
+ // the Command and after a refresh it may not display some of the
+ // spurious keys.
+ //
+ Length = sizeof( NameBuffer);
+ status = RegEnumKeyEx(
+ AtGlobalKey,
+ index,
+ NameBuffer, // lpName
+ &Length, // lpcbName
+ 0, // lpReserved
+ NULL, // lpClass
+ NULL, // lpcbClass
+ &lastWriteTime
+ );
+ if ( status != ERROR_SUCCESS) {
+ if ( status == ERROR_NO_MORE_ITEMS) {
+ status = ERROR_SUCCESS; // no more items to enumerate
+ }
+ break; // the only exit point from this loop
+ }
+ //
+ // Length returned is the number of characters in a UNICODE string
+ // representing the key name (not counting the terminating NULL
+ // character which is also supplied).
+ //
+ NameSize = ( Length + 1) * sizeof( WCHAR);
+
+ status = RegOpenKeyEx(
+ AtGlobalKey,
+ NameBuffer,
+ 0L,
+ KEY_READ,
+ &Key
+ );
+ if ( status != ERROR_SUCCESS) {
+ KdPrint((
+ "[Job] RegOpenKeyEx( %ws) returns %d\n",
+ NameBuffer,
+ status
+ ));
+ (VOID)RegDeleteKey( AtGlobalKey, NameBuffer);
+ continue;
+ }
+
+ Length = sizeof( Schedule);
+ status = RegQueryValueEx(
+ Key,
+ AT_SCHEDULE_NAME,
+ NULL,
+ &type,
+ (LPBYTE)&Schedule,
+ &Length
+ );
+ if ( status != ERROR_SUCCESS) {
+ KdPrint((
+ "[Job] RegQueryValueEx( AT_SCHEDULE_NAME, %ws) returns %d\n",
+ NameBuffer,
+ status
+ ));
+ (VOID)RegCloseKey( Key);
+ (VOID)RegDeleteKey( AtGlobalKey, NameBuffer);
+ continue;
+ }
+ if ( type != REG_BINARY ||
+ Length != sizeof( AT_SCHEDULE) ||
+ (Schedule.DaysOfWeek & ~DAYS_OF_WEEK) != 0 ||
+ (Schedule.DaysOfMonth & ~DAYS_OF_MONTH) != 0 ||
+ Schedule.JobTime >= MAXIMUM_JOB_TIME ) {
+ KdPrint((
+ "[Job] RegQueryValueEx( %ws) returns invalid schedule info\n"
+ ));
+ (VOID)RegCloseKey( Key);
+ (VOID)RegDeleteKey( AtGlobalKey, NameBuffer);
+ continue;
+ }
+
+ Length = sizeof( Command);
+ status = RegQueryValueEx(
+ Key,
+ AT_COMMAND_NAME,
+ NULL,
+ &type,
+ (LPBYTE)Command,
+ &Length
+ );
+ if ( status != ERROR_SUCCESS) {
+ KdPrint((
+ "[Job] RegQueryValueEx( AT_COMMAND_NAME, %ws) returns %d\n",
+ NameBuffer,
+ status
+ ));
+ (VOID)RegCloseKey( Key);
+ (VOID)RegDeleteKey( AtGlobalKey, NameBuffer);
+ continue;
+ }
+ if ( type != REG_SZ) {
+ KdPrint((
+ "[Job] RegQueryValueEx( %ws): Command is not of REG_SZ type\n"
+ ));
+ (VOID)RegCloseKey( Key);
+ (VOID)RegDeleteKey( AtGlobalKey, NameBuffer);
+ continue;
+ }
+ //
+ // Above call should already return the actual length of the null
+ // terminated string. However, because of bugs in reg apis it may
+ // return larger length than the actual length. Thus this:
+ //
+ Length = wcslen( Command);
+ CommandSize = (Length + 1) * sizeof( WCHAR);
+
+ pRecord = (PAT_RECORD)LocalAlloc(
+ LMEM_FIXED,
+ sizeof( AT_RECORD) + NameSize + CommandSize
+ );
+ if ( pRecord == NULL) {
+ KdPrint((
+ "[Job] LocalAlloc fails\n"
+ ));
+ (VOID)RegCloseKey( Key);
+ (VOID)RegDeleteKey( AtGlobalKey, NameBuffer);
+ continue;
+ }
+
+ pRecord->JobId = AtGlobalJobId++;
+
+ pRecord->JobTime = Schedule.JobTime;
+ pRecord->DaysOfMonth = Schedule.DaysOfMonth;
+ pRecord->DaysOfWeek = Schedule.DaysOfWeek;
+ pRecord->Flags = Schedule.Flags;
+#ifdef AT_DEBUG
+ pRecord->Debug = 0;
+#endif // AT_DEBUG
+ pRecord->CommandSize = (WORD)CommandSize;
+ pRecord->NameSize = (WORD)NameSize;
+
+ pRecord->JobDay = JOB_INVALID_DAY;
+
+ pRecord->Name = (PWCHAR)( pRecord + 1);
+ memcpy( pRecord->Name, NameBuffer, NameSize);
+
+ pRecord->Command = (PWCHAR)( (LPBYTE)&pRecord->Name[0] + NameSize);
+ memcpy( pRecord->Command, Command, CommandSize);
+
+ AtCalculateRuntime( pRecord, pTime);
+
+ AtInsertRecord( pRecord, BOTH_QUEUES);
+
+ (VOID)RegCloseKey( Key);
+ }
+ return( status);
+}
+
+
+
+BOOL AtPermitServerOperators( VOID)
+/*++
+ We permit server operators to manage schedule service only if the key
+ exists the proper flag is set. In all other case we do not permit server
+ operators to manage schedule service.
+--*/
+{
+ DWORD SubmitControl;
+ DWORD status;
+ DWORD type;
+ DWORD Length;
+ HKEY LsaKey;
+
+ status = RegOpenKeyEx(
+ HKEY_LOCAL_MACHINE,
+ LSA_REGISTRY_PATH,
+ 0L,
+ KEY_READ,
+ &LsaKey
+ );
+ if ( status != ERROR_SUCCESS) {
+ AtLog(( AT_DEBUG_CRITICAL, "RegOpenKeyEx( LsaKey) returns %d\n", status));
+ return( FALSE);
+ }
+
+ Length = sizeof( SubmitControl);
+ status = RegQueryValueEx(
+ LsaKey,
+ LSA_SUBMIT_CONTROL,
+ NULL,
+ &type,
+ (LPBYTE)&SubmitControl,
+ &Length
+ );
+ (VOID)RegCloseKey( LsaKey);
+
+ if ( status != ERROR_SUCCESS || type != REG_DWORD
+ || Length != sizeof( SubmitControl)) {
+ AtLog(( AT_DEBUG_MAIN, "LsaKey query: status=%d type=0x%x Length=%d value=0x%x\n",
+ status, type, Length, SubmitControl));
+ return( FALSE);
+ }
+
+ return( (SubmitControl & LSA_SERVER_OPERATORS) != 0);
+}
+
+
+
+VOID AtRemoveRecord(
+ PAT_RECORD pRecord,
+ DWORD QueueMask
+ )
+/*++
+
+Routine Description:
+
+ Depending on the value of a QueueMask argument, this function does
+ one or both of the following:
+
+ - removes record from a doubly linked list sorted by Runtime
+ - removes record from a doubly linked list sorted by JobId
+
+Arguments:
+
+ pRecord - pointer to record to be removed
+ QueueMask - mask of queues where this record should be removed from
+
+Return Value:
+
+ None.
+
+Note:
+
+ This routine should probably be a macro.
+
+--*/
+{
+ if ( QueueMask & RUNTIME_QUEUE) {
+ RemoveEntryList( &pRecord->RuntimeList);
+ }
+
+ if ( QueueMask & JOBID_QUEUE) {
+ RemoveEntryList( &pRecord->JobIdList);
+ }
+}
+
+
+
+BOOL AtSystemInteractive( VOID)
+{
+ DWORD NoInteractiveServices;
+ DWORD status;
+ DWORD type;
+ DWORD Length;
+
+ if ( AtGlobalHaveWindowsKey == FALSE) {
+ status = RegOpenKeyEx(
+ HKEY_LOCAL_MACHINE,
+ WINDOWS_REGISTRY_PATH,
+ 0L,
+ KEY_READ,
+ &AtGlobalWindowsKey
+ );
+ if ( status != ERROR_SUCCESS) {
+ AtLog(( AT_DEBUG_CRITICAL, "RegOpenKeyEx( WindowsKey) returns %d\n", status));
+ return( TRUE);
+ }
+ AtGlobalHaveWindowsKey = TRUE;
+ }
+
+ Length = sizeof( NoInteractiveServices);
+ status = RegQueryValueEx(
+ AtGlobalWindowsKey,
+ WINDOWS_VALUE_NAME,
+ NULL,
+ &type,
+ (LPBYTE)&NoInteractiveServices,
+ &Length
+ );
+ if ( status == ERROR_SUCCESS && type == REG_DWORD
+ && Length == sizeof( NoInteractiveServices)) {
+ return( NoInteractiveServices == 0);
+ }
+
+ AtLog(( AT_DEBUG_MAIN, "WindowsKey query: status=%d type=0x%x Length=%d value=0x%x\n",
+ status, type, Length, NoInteractiveServices));
+ return( TRUE);
+}
+
+
diff --git a/private/net/svcdlls/at/server/atsec.c b/private/net/svcdlls/at/server/atsec.c
new file mode 100644
index 000000000..5b62a14b3
--- /dev/null
+++ b/private/net/svcdlls/at/server/atsec.c
@@ -0,0 +1,246 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ atsec.c
+
+Abstract:
+
+ This module contains the schedule service support routines
+ which create security objects and enforce security _access checking.
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 06 - November - 1992
+
+Revision History:
+
+ 06-Nov-1992 vladimv
+ Created
+
+--*/
+
+#include "at.h"
+#include <secobj.h> // ACE_DATA
+
+
+
+
+
+#define AT_JOB_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | \
+ AT_JOB_ADD | \
+ AT_JOB_DEL | \
+ AT_JOB_ENUM | \
+ AT_JOB_GET_INFO )
+
+//
+// Security descriptors of schedule service to control user accesses
+// to the schedule service configuration information.
+//
+PSECURITY_DESCRIPTOR AtGlobalSecurityDescriptor;
+
+//
+// Structure that describes the mapping of Generic access rights to
+// object specific access rights for the schedule service security object.
+//
+GENERIC_MAPPING AtGlobalInformationMapping = {
+
+ STANDARD_RIGHTS_READ | // Generic read
+ AT_JOB_ENUM |
+ AT_JOB_GET_INFO,
+ STANDARD_RIGHTS_WRITE | // Generic write
+ AT_JOB_ADD |
+ AT_JOB_DEL,
+ STANDARD_RIGHTS_EXECUTE, // Generic execute
+ AT_JOB_ALL_ACCESS // Generic all
+ };
+
+
+NET_API_STATUS
+AtCheckSecurity(
+ ACCESS_MASK DesiredAccess
+ )
+/*++
+
+Routine Description:
+
+ This routine checks if an rpc caller is allowed to perform a given
+ operation on AT service. Currently, members of groups LocalAdmin
+ and LocalBackupOperators are allowed to do all operations and everybody
+ else is not allowed to do anything.
+
+ This routine calls NtAccessCheck() and not NtAccessCheckAndAuditAlarm()
+ because:
+
+ - we do not need the audit part anyway
+
+ - NtAccessCheckAndAuditAlarm() cannot be issued if a service is running
+ in an account which is NOT a LocalSystem account
+
+ The second reason prohibits a use of NetpAccessCheckAndAudit() netlib
+ function.
+
+Arguments:
+
+ ACCESS_MASK - Desired Access
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NTSTATUS NtStatus;
+ NET_API_STATUS Status;
+ HANDLE ClientToken;
+ LPWSTR StringArray[ 2];
+ WCHAR ErrorCodeString[ 25];
+
+ if ( ( Status = RpcImpersonateClient(NULL)) != NERR_Success) {
+ AtLog(( AT_DEBUG_CRITICAL, "RpcImpersonateClient() returns WinError = %d\n", Status));
+ return( Status);
+ }
+
+ NtStatus = NtOpenThreadToken(
+ NtCurrentThread(),
+ TOKEN_QUERY,
+ (BOOLEAN)TRUE,
+ &ClientToken
+ );
+
+ if ( NtStatus != STATUS_SUCCESS) {
+ AtLog(( AT_DEBUG_CRITICAL, "NtOpenThreadToken() returns NtStatus = 0x%x\n", NtStatus));
+ } else {
+
+ PRIVILEGE_SET PrivilegeSet;
+ DWORD PrivilegeSetLength;
+ ACCESS_MASK GrantedAccess;
+ NTSTATUS AccessStatus;
+
+ PrivilegeSetLength = sizeof( PrivilegeSet);
+
+ // NtAccessCheck() returns STATUS_SUCCESS if parameters
+ // are correct. Whether or not access is allowed is
+ // governed by the returned value of AccessStatus.
+
+ NtStatus = NtAccessCheck(
+ AtGlobalSecurityDescriptor, // SecurityDescriptor
+ ClientToken, // ClientToken
+ DesiredAccess, // DesiredAccess
+ &AtGlobalInformationMapping, // GenericMapping
+ &PrivilegeSet,
+ &PrivilegeSetLength,
+ &GrantedAccess, // GrantedAccess
+ &AccessStatus // AccessStatus
+ );
+ if ( NtStatus != STATUS_SUCCESS) {
+ AtLog(( AT_DEBUG_CRITICAL, "NtAccessCheck() returns NtStatus = 0x%x\n", NtStatus));
+ } else {
+ NtStatus = AccessStatus;
+ }
+ (VOID)NtClose( ClientToken);
+ }
+
+ if ( ( Status = RpcRevertToSelf()) != NERR_Success) {
+ StringArray[ 0] = L"RpcRevertToSelf";
+ wcscpy( ErrorCodeString, L"%%"); // tell event viewer to expand this error
+ ultow( Status, ErrorCodeString + 2, 10);
+ StringArray[ 1] = ErrorCodeString;
+ AtReportEvent( EVENTLOG_ERROR_TYPE, EVENT_CALL_TO_FUNCTION_FAILED,
+ 2, StringArray, 0, NULL);
+ EnterCriticalSection( &AtGlobalCriticalSection);
+ AtGlobalTasks |= AT_SERVICE_SHUTDOWN;
+ LeaveCriticalSection( &AtGlobalCriticalSection);
+ SetEvent( AtGlobalEvent);
+ return( Status);
+ }
+
+ return( NetpNtStatusToApiStatus( NtStatus));
+}
+
+
+NET_API_STATUS
+AtCreateSecurityObject(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This function creates the scheduler user-mode configuration
+ information object which is represented by a security descriptors.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NET_API_STATUS code
+
+--*/
+{
+ NTSTATUS status;
+
+ //
+ // Order matters! These ACEs are inserted into the DACL in the
+ // following order. Security access is granted or denied based on
+ // the order of the ACEs in the DACL.
+ //
+ // In win3.1 both LocalGroupAdmins and LocalGroupSystemOps were
+ // allowed to perform all Schedule Service operations. In win3.5
+ // LocalGroupSystemOps may be disallowed (this is the default case).
+ //
+
+ ACE_DATA aceData[] = {
+ {ACCESS_ALLOWED_ACE_TYPE, 0, 0, GENERIC_ALL, &AliasAdminsSid},
+ {ACCESS_ALLOWED_ACE_TYPE, 0, 0, GENERIC_ALL, &AliasSystemOpsSid}
+ };
+
+
+ status = NetpCreateSecurityObject(
+ aceData, // pAceData
+ AtGlobalPermitServerOperators ? 2 : 1, // countAceData
+ NULL, // OwnerSid
+ NULL, // PrimaryGroupSid
+ &AtGlobalInformationMapping, // GenericToSpecificMapping
+ &AtGlobalSecurityDescriptor // ppNewDescriptor
+ );
+
+ if ( ! NT_SUCCESS (status)) {
+ AtLog(( AT_DEBUG_CRITICAL, "Failure to create security object\n"));
+ return NetpNtStatusToApiStatus( status);
+ }
+
+ return NERR_Success;
+}
+
+
+
+NET_API_STATUS
+AtDeleteSecurityObject(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This function destroys the schedule service user-mode configuration
+ information object which is represented by a security descriptors.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NET_API_STATUS code
+
+--*/
+{
+ return( NetpDeleteSecurityObject( &AtGlobalSecurityDescriptor));
+}
+
+
diff --git a/private/net/svcdlls/at/server/attime.c b/private/net/svcdlls/at/server/attime.c
new file mode 100644
index 000000000..c9004ab38
--- /dev/null
+++ b/private/net/svcdlls/at/server/attime.c
@@ -0,0 +1,355 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ attime.c
+
+Abstract:
+
+ This module contains routines for calculating times & runtimes.
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 06 - November - 1992
+
+Revision History:
+
+ 06-Nov-1992 vladimv
+ Created
+
+--*/
+
+
+#include "at.h"
+
+#define DAYS_IN_WEEK 7
+#define SECONDS_PER_DAY 60L*60L*24L
+
+
+DBGSTATIC INT DaysInMonth(
+ INT month,
+ INT year
+ )
+/*++
+
+Routine Description:
+
+ Returns number of days in a given month - the only exceptional case is
+ a leap year February.
+
+Arguments:
+
+ month - integer index, must be between 0 and 11
+ year - integer index, any value allowed
+
+Return Value:
+
+ Number of days in a particular month.
+
+--*/
+{
+ // JAN FEB MAR APR MAY JUN JUL AUG SEP OCT NOV DEC
+ static int DaysInMonth[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+
+ return( month == 1 && ( (year%4 == 0 && year%100 != 0) || year%400 == 0)
+ ? 29 : DaysInMonth[ month]);
+}
+
+
+
+VOID AtCalculateRuntime(
+ IN OUT PAT_RECORD pRecord,
+ IN PAT_TIME pTime
+ )
+/*++
+
+Routine Description:
+
+ Fills in the next runtime for a given record.
+
+Arguments:
+
+ pRecord pointer to AT schedule record
+ pTime pointer to AT time structure
+
+Return Value:
+
+ None.
+
+--*/
+{
+ DWORD BestDelta; // number of days before the best runtime day
+ DWORD Delta; // number of days before a possible runtime day
+
+ DWORD BestJobDay; // the JobDay when to run
+ DWORD JobDay; // index of day in week or day in month
+ DWORD CurrentDay; // index of the current day (week or month)
+
+ DWORD DayMask; // mask for days in week or days in month
+ BOOL Weekday; // TRUE if next runtime is for a week day
+
+ DWORD CurrentTime;// day time in miliseconds for the current day
+ DWORD JobTime; // day time in miliseconds for this job
+
+ LARGE_INTEGER Runtime;
+
+#ifdef NOT_YET
+ if ( (pRecord->Flags & JOB_IS_ENABLED) == 0) {
+ //
+ // This job is disabled, so this will have the effect of putting
+ // it at the end of the queue.
+ //
+ pRecord->Runtime.LowPart = MAXULONG;
+ pRecord->Runtime.HighPart = MAXLONG;
+ return;
+ }
+#endif // NOT_YET
+
+
+ CurrentTime = pTime->CurrentTime;
+ JobTime = pRecord->JobTime;
+
+
+ if ( (pRecord->DaysOfWeek == 0) && (pRecord->DaysOfMonth == 0)) {
+
+ BestDelta = (CurrentTime > JobTime) ? 1 : 0; // case of single runtime
+
+ } else {
+
+ BestDelta = MAXULONG; // initialize to an invalid value
+
+ if ( (DayMask = pRecord->DaysOfWeek) != 0) {
+
+ CurrentDay = pTime->CurrentDayOfWeek; // current day of week
+ JobDay = 0; // running day of week, starting from Monday
+
+ // Because of a bug caused by wrong compiler optimization
+ // (compiler obtained in mid January 93), the test for DayMask
+ // equal to 0 and loop increments have to burried within a body
+ // of the for loop. (see bug #7414). An alternative was to
+ // disable global optimization for this routine.
+
+ for ( ; ;) {
+
+ if ( (DayMask & 1) != 0) {
+
+ if ( CurrentDay > JobDay ||
+ CurrentDay == JobDay && CurrentTime >= JobTime) {
+
+ Delta = DAYS_IN_WEEK - CurrentDay + JobDay;
+
+ } else {
+
+ Delta = JobDay - CurrentDay;
+
+ }
+
+ if ( Delta < BestDelta) {
+ BestDelta = Delta;
+ BestJobDay = JobDay;
+ Weekday = TRUE;
+ }
+ }
+
+ DayMask >>= 1;
+
+ if ( DayMask == 0) {
+ break;
+ }
+
+ JobDay++;
+ }
+ }
+
+ if ( (DayMask = pRecord->DaysOfMonth) != 0) {
+
+ DWORD ThisMonthLength;
+ DWORD NextMonthLength;
+
+ ThisMonthLength = DaysInMonth(
+ pTime->CurrentMonth - 1,
+ pTime->CurrentYear
+ );
+ NextMonthLength = DaysInMonth(
+ (pTime->CurrentMonth) % 12,
+ pTime->CurrentYear
+ );
+
+ CurrentDay = pTime->CurrentDay; // current day of month
+ JobDay = 1; // running day of month, starting from 1-st day
+
+ for ( ; ;) {
+
+ if ( (DayMask & 1) != 0) {
+
+ if ( CurrentDay > JobDay ||
+ CurrentDay == JobDay && CurrentTime >= JobTime) {
+
+ //
+ // Must look in the next two months.
+ //
+
+ if ( NextMonthLength >= JobDay) {
+
+ Delta = ThisMonthLength - CurrentDay + JobDay;
+
+ } else {
+
+ Delta = ThisMonthLength - CurrentDay
+ + NextMonthLength + JobDay;
+
+ }
+
+ } else {
+
+ //
+ // Must look in the current month and the next month.
+ //
+
+ if ( ThisMonthLength >= JobDay) {
+
+ Delta = JobDay - CurrentDay;
+
+ } else {
+
+ Delta = ThisMonthLength - CurrentDay + JobDay;
+
+ }
+ }
+
+ if ( Delta < BestDelta) {
+ BestDelta = Delta;
+ BestJobDay = JobDay;
+ Weekday = FALSE;
+ }
+ }
+
+ DayMask >>= 1;
+
+ if ( DayMask == 0) {
+ break;
+ }
+
+ JobDay++;
+ }
+ }
+
+ //
+ // Make sure we found a valid solution.
+ //
+ ASSERT( BestDelta != MAXULONG && BestJobDay != JOB_INVALID_DAY);
+
+ //
+ // If this is a NEXT type of job, remember the day to clear when
+ // we create the process for this job.
+ //
+ if ( (pRecord->Flags & JOB_RUN_PERIODICALLY) == 0) {
+ if ( Weekday == TRUE) {
+ pRecord->Flags |= JOB_CLEAR_WEEKDAY;
+ } else {
+ pRecord->Flags &= ~JOB_CLEAR_WEEKDAY;
+ }
+ pRecord->JobDay = (UCHAR)BestJobDay;
+ }
+ }
+
+ //
+ // The first operation requires large integer when BestDelta > 49.
+ //
+ Runtime.QuadPart = UInt32x32To64(BestDelta, 24L * 3600L * 1000L);
+ // count of miliseconds in a day
+ Runtime.QuadPart += JobTime;
+ Runtime.QuadPart -= CurrentTime;
+ Runtime.QuadPart = Runtime.QuadPart * NT_TICKS_IN_WINDOWS_TICK;
+ pRecord->Runtime.QuadPart = pTime->LargeInteger.QuadPart +
+ Runtime.QuadPart;
+
+// AtLog(( AT_DEBUG_MAIN, "CalculateRuntime: JobId=%d Command=%ws Runtime=%x:%x\n",
+// pRecord->JobId, pRecord->Command, pRecord->Runtime.HighPart, pRecord->Runtime.LowPart));
+ //
+ // Make sure we return a runtime that is after the current time!
+ //
+ ASSERT( pRecord->Runtime.QuadPart > pTime->LargeInteger.QuadPart);
+}
+
+VOID AtTimeGetCurrents( IN OUT PAT_TIME pTime)
+/*++
+
+Routine Description:
+
+ LargeInteger & TickCount fields in input data structure
+ are known at input. Here we just fill in Current fields
+ using the input value of LargeInteger field.
+
+Arguments:
+
+ pTime - points to AT_TIME structure.
+
+ Fields in SYSTEMTIME Structure:
+WORD wYear - the current year.
+WORD wMonth - the current month with January equal to 1.
+WORD wDayOfWeek - the current day of the week where 0=Sunday, 1=Monday...
+WORD wDay - the current day of the month.
+WORD wHour - the current hour.
+WORD wMinute - the current minute within the hour.
+WORD wSecond - the current second within the minute.
+WORD wMilliseconds - the current millisecond within the second.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ TIME_FIELDS TimeFields;
+
+ RtlTimeToTimeFields( &pTime->LargeInteger, &TimeFields);
+
+ pTime->CurrentYear = TimeFields.Year;
+ pTime->CurrentMonth = TimeFields.Month;
+ //
+ // Note that Windows structure has Sunday=0, Monday=1, ...
+ // while AT command uses: Monday=0, Tuesday=1, ... Here
+ // we convert from windows into AT command DayOfWeek units.
+ //
+ if ( TimeFields.Weekday == 0) {
+ pTime->CurrentDayOfWeek = 6; // Sunday
+ } else {
+ pTime->CurrentDayOfWeek = (WORD)(TimeFields.Weekday - 1);
+ }
+ pTime->CurrentDay = TimeFields.Day;
+ pTime->CurrentTime = (DWORD)TimeFields.Milliseconds +
+ 1000 * ( TimeFields.Second +
+ 60 * ( TimeFields.Minute + 60 * TimeFields.Hour));
+}
+
+
+VOID AtTimeGet( OUT PAT_TIME pTime)
+/*++
+
+Routine Description:
+
+ Returns all time information needed by AT service.
+
+Arguments:
+
+ pTime - points to AT_TIME structure
+
+Return Value:
+
+ None.
+
+--*/
+{
+ LARGE_INTEGER UniversalTime;
+
+ NtQuerySystemTime( &UniversalTime);
+ pTime->TickCount = GetTickCount();
+ RtlSystemTimeToLocalTime( &UniversalTime, &pTime->LargeInteger);
+ AtTimeGetCurrents( pTime);
+}
+
+
+
diff --git a/private/net/svcdlls/at/server/makefile b/private/net/svcdlls/at/server/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/net/svcdlls/at/server/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/net/svcdlls/at/server/run.c b/private/net/svcdlls/at/server/run.c
new file mode 100644
index 000000000..3386485bb
--- /dev/null
+++ b/private/net/svcdlls/at/server/run.c
@@ -0,0 +1,242 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ run.c
+
+Abstract:
+
+ Example of a test code for command execution.
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 06 - November - 1992
+
+Revision History:
+
+ 06-Nov-1992 vladimv
+ Created
+
+--*/
+
+#include "at.h"
+#include <stdio.h> // printf
+
+DBGSTATIC WCHAR **
+ArgvToUnicode(
+ IN int argc,
+ IN CHAR ** charArgv
+ );
+DBGSTATIC WCHAR *
+AsciizToUnicode(
+ IN CHAR * Asciiz
+ );
+DBGSTATIC BOOL
+FillCommand(
+ IN int argc,
+ IN WCHAR ** argv
+ );
+
+#define MAX_COMMAND_LEN 128
+
+WCHAR Command[ MAX_COMMAND_LEN + 1];
+
+
+VOID _CRTAPI1
+main(
+ int argc,
+ CHAR ** charArgv
+ )
+{
+ PROCESS_INFORMATION ProcessInformation;
+ BOOL success;
+ WCHAR ** argv;
+ STARTUPINFO StartupInfo;
+
+
+
+ if ( ( argv = ArgvToUnicode( argc, charArgv)) == NULL) {
+ printf( "Failed to map input strings to unicode.\n");
+ exit( -1);
+ }
+
+ if ( FillCommand( argc, argv) == FALSE) {
+ exit( -1);
+ }
+
+ GetStartupInfo( &StartupInfo);
+
+// StartupInfo.lpTitle = "just a test";
+ StartupInfo.lpTitle = NULL;
+
+ success = CreateProcess(
+ NULL, // image name is imbedded in the command line
+ Command, // command line
+ NULL, // pSecAttrProcess
+ NULL, // pSecAttrThread
+ FALSE, // this process will not inherit our handles
+ DETACHED_PROCESS, // instead of DETACHED_PROCESS
+ NULL, // pEnvironment
+ NULL, // pCurrentDirectory
+ &StartupInfo, // instead of NULL
+ &ProcessInformation
+ );
+
+ if ( success == FALSE) {
+
+ DWORD winError;
+
+ winError = GetLastError();
+
+ printf(
+ "CreateProcess( %ws) fails with winError = %d\n",
+ Command,
+ winError
+ );
+
+ } else {
+
+ printf(
+ "CreateProcess( %ws) succeeds\n"
+ " ProcessInformation = %x %x %x %x\n",
+ Command,
+ ProcessInformation.hProcess,
+ ProcessInformation.hThread,
+ ProcessInformation.dwProcessId,
+ ProcessInformation.dwThreadId
+ );
+ }
+}
+
+
+DBGSTATIC WCHAR **
+ArgvToUnicode(
+ IN int argc,
+ IN CHAR ** charArgv
+ )
+/*++
+ No attempt is made to clean up if we fail half way along. Cleanup
+ will be done at the exit process time.
+--*/
+{
+ WCHAR ** argv;
+ int index;
+
+ argv = (WCHAR **)LocalAlloc(
+ LMEM_FIXED,
+ argc * sizeof( WCHAR *)
+ );
+ if ( argv == NULL) {
+ return( NULL);
+ }
+
+ for ( index = 0; index < argc; index++ ) {
+
+ if ( ( argv[ index] = AsciizToUnicode( charArgv[ index])) == NULL) {
+ return( NULL);
+ }
+ }
+
+ return( argv);
+}
+
+
+
+DBGSTATIC WCHAR *
+AsciizToUnicode(
+ IN CHAR * Asciiz
+ )
+/*++
+ No attempt is made to clean up if we fail half way along. Cleanup
+ will be done at the exit process time.
+--*/
+{
+ UNICODE_STRING UnicodeString;
+ BOOL success;
+
+ RtlInitUnicodeString(
+ &UnicodeString,
+ NULL
+ );
+
+ success = RtlCreateUnicodeStringFromAsciiz(
+ &UnicodeString,
+ Asciiz
+ );
+
+ if ( success != TRUE) {
+ printf( "Failed to make unicode string out of %s\n", Asciiz);
+ return( NULL);
+ }
+
+ return( UnicodeString.Buffer);
+}
+
+
+#define DEFAULT_CMD_EXE L"cmd"
+#define SLASH_C_APPEND L" /c "
+#define SLASH_C_APPEND_LENGTH ( sizeof( SLASH_C_APPEND) / sizeof( WCHAR) - 1)
+DBGSTATIC BOOL
+FillCommand(
+ IN int argc,
+ IN WCHAR ** argv
+ )
+{
+ WCHAR * recdatap; // ptr used to build atr_command
+ DWORD recdata_len; // len of arg to put in atr_command
+ int i;
+ WCHAR cmdexe[ MAX_PATH + SLASH_C_APPEND_LENGTH];
+ DWORD length;
+
+ //
+ // This is really bogus behavior, but "length" returned is count of
+ // BYTES, not count of UNICODE characters, and it does not include the
+ // terminating NULL UNICODE character.
+ //
+ length = GetEnvironmentVariable( L"ComSpec", cmdexe, MAX_PATH);
+ if ( length == 0) {
+ //
+ // We arrive here if somebody undefined ComSpec environment
+ // variable. Then, DEFAULT_CMD_EXE helps provided there is
+ // no bogus file with cmd.exe name on the search path before
+ // the real cmd.exe (e.g. a bogus file in the current directory).
+ //
+ wcscpy( cmdexe, DEFAULT_CMD_EXE);
+ }
+ wcscat( cmdexe, SLASH_C_APPEND);
+
+ wcscpy( Command, cmdexe);
+ recdatap = Command + wcslen( Command);
+ recdata_len = 0;
+
+ for ( i = 1; i < argc; i++) {
+
+ DWORD temp;
+
+ temp = wcslen( argv[i]) + 1;
+
+ recdata_len += temp;
+
+ if ( recdata_len > MAX_COMMAND_LEN) {
+ printf( "Command too long\n");
+ return( FALSE);
+ }
+
+ wcscpy( recdatap, argv[i]);
+ recdatap += temp;
+
+ // To construct lpszCommandLine argument to CreateProcess call
+ // we replace nuls with spaces.
+
+ *(recdatap - 1) = ' ';
+ }
+
+ // Reset space back to null on last argument in string.
+
+ *(recdatap - 1) = '\0';
+
+ return( TRUE);
+}
+
diff --git a/private/net/svcdlls/at/server/sources b/private/net/svcdlls/at/server/sources
new file mode 100644
index 000000000..ffeeb36c0
--- /dev/null
+++ b/private/net/svcdlls/at/server/sources
@@ -0,0 +1,38 @@
+MAJORCOMP=net
+MINORCOMP=atserver
+
+TARGETPATH=obj
+TARGETNAME=atsvc
+TARGETTYPE=LIBRARY
+
+TARGETLIBS= \
+ $(BASEDIR)\Public\Sdk\Lib\*\netlib.lib \
+ $(BASEDIR)\public\sdk\lib\*\kernel32.lib \
+ $(BASEDIR)\public\sdk\lib\*\advapi32.lib \
+ $(BASEDIR)\public\sdk\lib\*\rpcndr.lib \
+ $(BASEDIR)\public\sdk\lib\*\rpcrt4.lib
+
+INCLUDES=..;..\..\..\inc;..\..\..\..\inc;..\..\..\api
+
+!IFNDEF DISABLE_NET_UNICODE
+UNICODE=1
+NET_C_DEFINES=-DUNICODE
+!ENDIF
+
+SOURCES= \
+ atdebug.c \
+ atenv.c \
+ atmain.c \
+ atapi.c \
+ atreg.c \
+ atsec.c \
+ attime.c \
+ atsvc_s.c
+
+C_DEFINES= -DINCL_32= -DNT -DRPC_NO_WINDOWS_H
+
+#386_WARNING_LEVEL=-W4
+
+UMTYPE=console
+UMTEST=run
+UMLIBS=
diff --git a/private/net/svcdlls/browser/bchk/bchk.c b/private/net/svcdlls/browser/bchk/bchk.c
new file mode 100644
index 000000000..a7444f274
--- /dev/null
+++ b/private/net/svcdlls/browser/bchk/bchk.c
@@ -0,0 +1,1902 @@
+/*++
+
+Copyright (c) 1993 Micorsoft Corporation
+
+Module Name:
+
+ bchk.c
+
+Abstract:
+
+ Browser Monitor main program.
+
+Author:
+
+ Congpa You (CongpaY) 10-Feb-1993
+
+Revision History:
+
+--*/
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <sys\types.h>
+#include <sys\stat.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <windows.h>
+#include <lm.h>
+#include <ntddbrow.h>
+#include <brcommon.h> // svcdll\browser
+#include <rap.h>
+#include <rxserver.h>
+#include <winerror.h> // inc
+#include <rpcutil.h> // for MIDL_user_free. net\inc
+#include "bchk.h"
+
+// global data for counting.
+INT nCountFail[ERRORTYPENUM];
+INT nCountSuccess[ERRORTYPENUM];
+INT nTimes;
+PERRORLIST pErrorList[ERRORTYPENUM];
+FILE * pLOGFILE;
+
+void _CRTAPI1 main()
+{
+ DWORD dwVal; // The value returned from function calls.
+ PWSTR * BrowserList = NULL; // The browser list returned from Master Browser.
+ ULONG BrowserListLength = 0;
+ INITPARAM InitParam;
+ struct _stat buf;
+ PLMDR_TRANSPORT_LIST TransportList = NULL;
+ PLMDR_TRANSPORT_LIST TransportEntry = NULL;
+
+ // Set trap on ctrl-c.
+ if (!SetConsoleCtrlHandler (ConvertFile, TRUE))
+ {
+ printf("Error %lu occured in SetConsoleCtrlHandler", GetLastError());
+ return;
+ }
+
+ try
+ {
+ // Get nTolerance, nSleepTime, lpUser, lpDomain from bchk.ini.
+ if (!Init (&InitParam))
+ return;
+
+ // Create a logfile for writing all the errors and other information to.
+ pLOGFILE = fopen (szBCHKLOG, "w+");
+
+ // Set all count to 0.
+ InitAllCount();
+
+ do // This runs forever.
+ {
+ fprintf(stdout, "BCHK is running.\n") ;
+
+ // Print the header in the bchklog file.
+ PrintHeader (pLOGFILE);
+
+ // Count the times of the check.
+ nTimes++;
+
+ // Find all transports that we have.
+ dwVal = GetBrowserTransportList (&TransportList);
+ if (dwVal != NERR_Success)
+ {
+ if (TransportList != NULL)
+ {
+ MIDL_user_free (TransportList);
+ }
+ MyMessageBox (dwVal);
+ return;
+ }
+
+ TransportEntry = TransportList;
+
+ // Enumerate on the transports.
+ while (TransportEntry != NULL)
+ {
+ TCHAR lpDomain[STRINGLEN]; // The array of domain names.
+ LPTSTR lpDomainName; // One domain's name.
+ UNICODE_STRING TransportName;
+
+ TransportName.Buffer = TransportEntry->TransportName;
+ TransportName.Length = (USHORT) TransportEntry->TransportNameLength;
+ TransportName.MaximumLength = (USHORT) TransportEntry->TransportNameLength;
+
+ // Enumerate all the Domains that we want. All domains are in one string.
+ // They are separated by space.
+ lstrcpy (lpDomain, InitParam.lpDomain);
+ lpDomainName = strtokf (lpDomain, szSeps);
+
+ while (lpDomainName != NULL)
+ {
+ BOOL fFoundMaster = FALSE; // See if we have master browser's name.
+ TCHAR lpMasterName[UNCLEN+1];
+ {
+ SYSTEMTIME systime;
+
+ GetLocalTime(&systime);
+
+ fprintf(pLOGFILE, "Run %ws %ws started at %2.2d/%2.2d/%4.4d %2.2d:%2.2d:%2.2d:%4.4d\n",
+ lpDomainName,
+ TransportName.Buffer,
+ systime.wMonth,
+ systime.wDay,
+ systime.wYear,
+ systime.wHour,
+ systime.wMinute,
+ systime.wSecond,
+ systime.wMilliseconds);
+
+ }
+
+
+ // Check for error type 1: Browser master is not returning
+ // backup browser list.
+
+ dwVal = GetBrowserServerList (&TransportName,
+ lpDomainName,
+ &BrowserList,
+ &BrowserListLength,
+ TRUE);
+
+ if (dwVal != NERR_Success) // Type 1 error occured.
+ {
+ //
+ // Send a magic bullet on this failure.
+ //
+
+ SendMagicBullet();
+
+ ReportError (InitParam.lpUser,
+ TransportName.Buffer,
+ lpDomainName,
+ lpMasterName,
+ NULL,
+ dwVal,
+ NO_MASTER_RUNNING); //Error type number.
+
+ // Try to get Domain Master's name.
+ if (GetMasterName (lpMasterName,
+ TransportName.Buffer,
+ lpDomainName) == NERR_Success)
+ {
+ ReportError (InitParam.lpUser,
+ TransportName.Buffer,
+ lpDomainName,
+ NULL,
+ lpMasterName,
+ dwVal,
+ INVALID_MASTER); //Error type number.
+ }
+ else
+ {
+ nCountSuccess[INVALID_MASTER]++;
+ }
+ }
+ else if (BrowserListLength == 1) //Type 1 error didnot occur but there is only one browser on the domain.
+ {
+ nCountSuccess[NO_MASTER_RUNNING]++;
+ ReportError (InitParam.lpUser,
+ TransportName.Buffer,
+ lpDomainName,
+ BrowserList[0],
+ NULL,
+ 0,
+ NO_BACKUP);
+ }
+ else // Type 1 error didn't occur and there are backup browsers.
+ {
+ WriteBrowserList (pLOGFILE, BrowserList, BrowserListLength);
+
+ nCountSuccess[NO_MASTER_RUNNING]++;
+ nCountSuccess[NO_BACKUP]++;
+
+ // Try to get Domain Master's name.
+ dwVal = GetMasterName (lpMasterName,
+ TransportName.Buffer,
+ lpDomainName);
+ if (dwVal == NERR_Success)
+ {
+ nCountSuccess[NO_MASTER_NAME]++;
+ fFoundMaster = TRUE;
+ }
+ else // Try other ways to find the master browser's name.
+ {
+ ReportError (InitParam.lpUser,
+ TransportName.Buffer,
+ lpDomainName,
+ NULL,
+ NULL,
+ dwVal,
+ NO_MASTER_NAME); //Error type number.
+
+ fFoundMaster = RxGetMasterName (InitParam.lpUser,
+ lpMasterName,
+ TransportName.Buffer,
+ lpDomainName,
+ BrowserList,
+ BrowserListLength,
+ InitParam.nTimeLimit);
+ }
+
+ if (fFoundMaster) // Report error if we donot have master name
+ {
+ // Log the master browser's name.
+ LogMaster (pLOGFILE,
+ TransportName.Buffer,
+ lpDomainName,
+ lpMasterName);
+
+ // Check for error type 2: Incorrect server lists returned by
+ // backup browsers (and master browsers). This includes stale
+ // servers in the list.
+ CheckErrorType2 (InitParam.lpUser,
+ TransportName.Buffer,
+ lpDomainName,
+ lpMasterName,
+ BrowserList,
+ BrowserListLength,
+ InitParam.nTolerance,
+ InitParam.nTimeLimit);
+
+ // Check for error type 3: Incorrect number of browser servers.
+ CheckErrorType3 (InitParam.lpUser,
+ TransportName.Buffer,
+ lpDomainName,
+ lpMasterName);
+
+ // Check for error type 4: Master is not PDC.
+ CheckErrorType4 (InitParam.lpUser,
+ TransportName.Buffer,
+ lpDomainName,
+ lpMasterName);
+
+ } // End of else when we have the master name.
+
+ } // End of else when type 1 error didnot occur and there are backup browsers.
+
+ if (BrowserList != NULL)
+ {
+ MIDL_user_free(BrowserList);
+ }
+
+ {
+ SYSTEMTIME systime;
+
+ GetLocalTime(&systime);
+
+ fprintf(pLOGFILE, "Run %ws %ws completed at %2.2d/%2.2d/%4.4d %2.2d:%2.2d:%2.2d:%4.4d\n",
+ lpDomainName,
+ TransportName.Buffer,
+ systime.wMonth,
+ systime.wDay,
+ systime.wYear,
+ systime.wHour,
+ systime.wMinute,
+ systime.wSecond,
+ systime.wMilliseconds);
+
+ }
+
+ // print out == between domains.
+
+ PrintSeparation (pLOGFILE);
+
+ lpDomainName = strtokf (NULL, szSeps);
+
+ } // End of the enumeration of domains.
+
+ if (TransportEntry->NextEntryOffset == 0)
+ TransportEntry = NULL;
+ else
+ {
+ TransportEntry = (PLMDR_TRANSPORT_LIST) ((PCHAR) TransportEntry
+ +TransportEntry->NextEntryOffset);
+ }
+
+ } // End of the enumeration of transports.
+
+ // Free memory.
+ MIDL_user_free (TransportList);
+
+ // Write the result to the summory file.
+ WriteSummory();
+
+ // Make sure alert is writen to the disk.
+ fflush (pLOGFILE);
+
+ // Check if the file size is too big. Reopen it if it's so.
+ if (_fstat (_fileno(pLOGFILE), &buf) != -1)
+ {
+ printf("File size = %ld, Limit = %d\n", buf.st_size, InitParam.nFileSizeLimit);
+
+ if (buf.st_size > InitParam.nFileSizeLimit)
+ {
+ fclose (pLOGFILE);
+
+ if (!MoveFileEx (szLBCHKLOG, szLBACKUP, MOVEFILE_REPLACE_EXISTING))
+ {
+ pLOGFILE = fopen (szBCHKLOG, "w+");
+ LogError (pLOGFILE, GetError(GetLastError()));
+ }
+ else
+ pLOGFILE = fopen (szBCHKLOG, "w+");
+ }
+ }
+
+ // Wait for a while before start again.
+ fprintf(stdout, "bchk is sleeping for %d secs\n", InitParam.nSleepTime/1000) ;
+ Sleep (InitParam.nSleepTime);
+
+ } while (TRUE);
+ }
+ finally
+ {
+ ConvertFile(0);
+ LocalFree (InitParam.lpUser);
+ LocalFree (InitParam.lpDomain);
+ }
+ return;
+}
+
+// Get nTolerance, nSleepTime, lpUser, lpDomain from bchk.ini.
+BOOL Init (INITPARAM * pInitParam)
+{
+ DWORD dwVal;
+ DWORD dwComputerNameLength = STRINGLEN;
+ LPTSTR lpUser;
+ LPTSTR lpTemp;
+
+ pInitParam->lpUser = (LPTSTR) LocalAlloc (LPTR, STRINGLEN);
+ pInitParam->lpDomain = (LPTSTR) LocalAlloc (LPTR, STRINGLEN);
+ lpUser = (LPTSTR) LocalAlloc (LPTR, STRINGLEN);
+ lpTemp = (LPTSTR) LocalAlloc (LPTR, INTLEN);
+
+ if ((pInitParam->lpUser == NULL) ||
+ (pInitParam->lpDomain == NULL) ||
+ (lpUser == NULL) ||
+ (lpTemp == NULL))
+ {
+ MyMessageBox (ERROR_NOT_ENOUGH_MEMORY);
+ return(FALSE);
+ }
+
+ // Get the current computer name. We will always notify the current user
+ // when an error occurs. This computer name is for sending message.
+ if (!GetComputerName (pInitParam->lpUser, &dwComputerNameLength))
+ {
+ MyMessageBox (GetLastError());
+ return(FALSE);
+ }
+
+ // Read nTolerance from bchk.ini. We allow backup browser return
+ // nTolerance different entries from those from master browser.
+ GetPrivateProfileString (szAPPNAME,
+ szTOLERANCE,
+ szDefaultTolerance,
+ lpTemp,
+ INTLEN,
+ szFILENAME);
+
+ pInitParam->nTolerance = atoi(toansi(lpTemp));
+
+ // Read nTolerance from bchk.ini. We allow backup browser return
+ // nTolerance different entries from those from master browser.
+ GetPrivateProfileString (szAPPNAME,
+ szTIMELIMIT,
+ szDefaultTimeLimit,
+ lpTemp,
+ INTLEN,
+ szFILENAME);
+
+ pInitParam->nTimeLimit = atoi(toansi(lpTemp));
+
+ // Get the sleep time from bchk.ini.
+ GetPrivateProfileString (szAPPNAME,
+ szSLEEPTIME,
+ szDefaultSleepTime, //millisecond.
+ lpTemp,
+ INTLEN,
+ szFILENAME);
+
+ pInitParam->nSleepTime = atoi(toansi(lpTemp));
+
+ // Get the file size limit from bchk.ini.
+ GetPrivateProfileString (szAPPNAME,
+ szFILESIZE,
+ szDefaultFileSize, //millisecond.
+ lpTemp,
+ INTLEN,
+ szFILENAME);
+
+ pInitParam->nFileSizeLimit = atoi(toansi(lpTemp));
+
+ // Read the people we want to notify when an browser error occurs from bchk.ini.
+ dwVal = GetPrivateProfileString (szAPPNAME,
+ szOTHERUSERS,
+ szDefaultOtherUser,
+ lpUser,
+ STRINGLEN,
+ szFILENAME);
+
+ if (dwVal >= (STRINGLEN-2)) // The memory assigned to lpUser is not big enough.
+ {
+ MyMessageBox (ERROR_NOT_ENOUGH_MEMORY);
+ return(FALSE);
+ }
+ else
+ {
+ lstrcat (pInitParam->lpUser, L" ");
+ lstrcat (pInitParam->lpUser, lpUser);
+ }
+
+ // Read all the domains that we want to check from bchk.ini
+ dwVal = GetPrivateProfileString (szAPPNAME,
+ szDOMAINS,
+ szDefaultDomain,
+ pInitParam->lpDomain,
+ STRINGLEN,
+ szFILENAME);
+
+ if (dwVal >= (STRINGLEN-2)) // The memory assigned to lpDomainList is not big enough.
+ {
+ MyMessageBox (ERROR_NOT_ENOUGH_MEMORY);
+ return(FALSE);
+ }
+
+ //
+ // Make sure that the domain name is upper cased.
+ //
+
+ _wcsupr(pInitParam->lpDomain);
+
+ LocalFree (lpUser);
+ LocalFree (lpTemp);
+
+ return(TRUE);
+}
+
+void InitAllCount ()
+{
+ INT i;
+
+ nTimes = 0;
+
+ for (i = 0; i< ERRORTYPENUM ; i++)
+ {
+ nCountFail[i] = 0;
+ nCountSuccess[i] = 0;
+ }
+}
+
+// Try to get the Domain Master's name using GetNetBiosMasterName.
+NET_API_STATUS GetMasterName (LPTSTR lpMasterName,
+ LPTSTR lpTransportName,
+ LPTSTR lpDomainName)
+{
+ TCHAR lpTempMaster[UNCLEN+1];
+ DWORD dwStartTime;
+ DWORD dwEndTime;
+ NET_API_STATUS dwVal;
+
+ dwStartTime = GetTickCount();
+
+ dwVal = GetNetBiosMasterName (lpTransportName,
+ lpDomainName,
+ lpTempMaster,
+ NULL );
+
+ dwEndTime = GetTickCount();
+
+ if (dwVal == NERR_Success)
+ {
+ // lpTempMaster has lots of space following
+ // the characters. We want to make a null terminated
+ // string, and add \\ in front of master name.
+ lstrcpy (lpMasterName, L"\\\\");
+ KillSpace (lpTempMaster);
+ lstrcat (lpMasterName, lpTempMaster);
+ }
+ else
+ {
+ fprintf (pLOGFILE, "GetNetBiosMasterName failed after %d milliseconds.\n",
+ dwEndTime-dwStartTime);
+
+ wsprintf (lpMasterName, L"unknown");
+ }
+ return(dwVal);
+}
+
+// Use RxNetServerEnum call to get Domain Master's name.
+BOOL RxGetMasterName (LPTSTR lpUser,
+ LPTSTR lpMasterName,
+ LPTSTR lpTransportName,
+ LPTSTR lpDomainName,
+ PWSTR * BrowserList,
+ DWORD BrowserListLength,
+ DWORD nTimeLimit)
+{
+ INT i = 0;
+ DWORD dwVal;
+ DWORD dwTotalEntries; // Total servers returned from Backup Browser.
+ DWORD dwEntriesRead; // Total servers read from Backup Browser.
+ LPVOID lpBrowserList;
+// TCHAR lpErrorMessage[STRINGLEN];
+ PSERVER_INFO_101 pBrowserServerInfo101;
+ DWORD dwStartTime;
+ DWORD dwEndTime;
+
+ for (i = 0 ; i < (INT) BrowserListLength ; i++)
+ {
+ dwStartTime = GetTickCount();
+
+ dwVal = MyRxNetServerEnum (BrowserList[i],
+ lpTransportName,
+ 101,
+ (LPBYTE *) &lpBrowserList,
+ 0xffffffff,
+ &dwEntriesRead,
+ &dwTotalEntries,
+ SV_TYPE_MASTER_BROWSER,
+ lpDomainName,
+ NULL);
+
+ dwEndTime = GetTickCount();
+
+ if (dwVal == NERR_Success) // Get the master browser list.
+ {
+ nCountSuccess[FAIL_RETURN_MASTER]++;
+
+ if ((dwEndTime - dwStartTime) > nTimeLimit) {
+
+ dwVal = dwEndTime - dwStartTime;
+
+ //
+ // If this took "too long", log it as an error.
+ //
+
+ ReportError (lpUser,
+ lpTransportName,
+ lpDomainName,
+ NULL,
+ BrowserList[i],
+ dwVal,
+ BROWSE_TOO_LONG);
+
+ if ((dwEndTime - dwStartTime) > 2*nTimeLimit) {
+ SendMagicBullet();
+ }
+
+
+ } else {
+ nCountSuccess[BROWSE_TOO_LONG] += 1;
+ }
+
+ if (dwEntriesRead != 1) // Wrong master browser list
+ {
+ WriteList (pLOGFILE, lpBrowserList, dwEntriesRead);
+
+ MIDL_user_free (lpBrowserList);
+
+ ReportError (lpUser,
+ lpTransportName,
+ lpDomainName,
+ NULL,
+ BrowserList[i],
+ dwEntriesRead,
+ WRONG_NUM_MASTER); //Error type number.
+
+ }
+ else //Found the master.
+ {
+ nCountSuccess[WRONG_NUM_MASTER]++;
+ nCountSuccess[UNKNOWN_MASTER]++;
+
+ pBrowserServerInfo101 = lpBrowserList;
+ wsprintf(lpMasterName, L"\\\\%s", pBrowserServerInfo101[0].sv101_name);
+ MIDL_user_free (lpBrowserList);
+ return(TRUE);
+ }
+
+ } // End of if when we get the master browser list.
+ else
+ {
+ if (lpBrowserList != NULL)
+ {
+ MIDL_user_free (lpBrowserList);
+ }
+
+ ReportError (NULL,
+ lpTransportName,
+ lpDomainName,
+ NULL,
+ BrowserList[i],
+ dwVal,
+ FAIL_RETURN_MASTER);
+ }
+
+ } // End of the for loop
+
+ ReportError (lpUser,
+ lpTransportName,
+ lpDomainName,
+ NULL,
+ NULL,
+ 0,
+ UNKNOWN_MASTER); //Error type number.
+
+ return(FALSE);
+}
+
+// Generate an error message, write to the logfile and send to all the users.
+void ReportError (LPTSTR lpUser,
+ LPTSTR lpTransportName,
+ LPTSTR lpDomainName,
+ LPTSTR lpMasterName,
+ LPTSTR lpBrowserName,
+ DWORD dwError,
+ INT nErrorType)
+{
+ TCHAR lpErrorMessage[STRINGLEN];
+ DWORD dwNSGI = 0;
+ PSERVER_INFO_101 pSV101 = NULL;
+
+ static BOOL fError[ERRORTYPENUM] = {FALSE, FALSE, FALSE, FALSE, FALSE,
+ FALSE, FALSE, FALSE, FALSE, FALSE,
+ FALSE, FALSE, FALSE, FALSE, FALSE,
+ FALSE, FALSE, FALSE, FALSE};
+
+ nCountFail[nErrorType]++;
+
+ switch (nErrorType)
+ {
+ case NO_MASTER_RUNNING:
+ GetMasterName (lpMasterName,
+ lpTransportName,
+ lpDomainName);
+
+ wsprintf (lpErrorMessage,
+ L"No browser master is running on domain %s, transport %s. \nThe browser master's name is %ws.\n",
+ lpDomainName,
+ lpTransportName,
+ lpMasterName);
+ lstrcat (lpErrorMessage, GetError (dwError));
+
+ break;
+
+ case INVALID_MASTER:
+ wsprintf (lpErrorMessage,
+ L"Browser added a not-running master %s on domain %s transport %s.\n",
+ lpBrowserName,
+ lpDomainName,
+ lpTransportName);
+ break;
+
+ case NO_BACKUP:
+ wsprintf (lpErrorMessage,
+ L"There are no backup browsers on domain %s transport %s.\nThe domain master is owned by %s.\n",
+ lpDomainName,
+ lpTransportName,
+ lpMasterName);
+ break;
+
+ case NO_MASTER_NAME:
+ wsprintf (lpErrorMessage,
+ L"GetNetBiosMasterName failed on domain %s transport %s.\n",
+ lpDomainName,
+ lpTransportName);
+ lstrcat (lpErrorMessage, GetError (dwError));
+ break;
+
+ case WRONG_NUM_MASTER:
+ wsprintf (lpErrorMessage,
+ L"%lu browser masters are returned from %s.\nCannot determine the master browser on domain %s, transport%s through %s.\n",
+ dwError, // = dwEntriesRead
+ lpBrowserName,
+ lpDomainName,
+ lpTransportName,
+ lpBrowserName);
+ break;
+
+ case FAIL_RETURN_MASTER:
+ wsprintf (lpErrorMessage,
+ L"Browser %s on domain %s, transport %s failed to return its master.\n",
+ lpBrowserName,
+ lpDomainName,
+ lpTransportName);
+ lstrcat (lpErrorMessage, GetError (dwError));
+ break;
+
+ case UNKNOWN_MASTER:
+ wsprintf (lpErrorMessage,
+ L"Cannot determine the master browser on domain %s, transport %s.\n",
+ lpDomainName,
+ lpTransportName);
+ break;
+
+ case MASTER_NO_SERVER_LIST:
+ wsprintf (lpErrorMessage,
+ L"The master browser %s on domain %s transport %s failed to return the server list.\n",
+ lpMasterName,
+ lpDomainName,
+ lpTransportName);
+
+ lstrcat (lpErrorMessage, GetError (dwError));
+ break;
+
+ case BROWSER_NO_SERVER_LIST:
+ wsprintf (lpErrorMessage,
+ L"Browser %s on domain %s transport %s failed to return the server list.\nThe domain master is owned by %s.\n",
+ lpBrowserName,
+ lpDomainName,
+ lpTransportName,
+ lpMasterName);
+
+ lstrcat (lpErrorMessage, GetError (dwError));
+ break;
+
+ case MASTER_NO_DOMAIN_LIST:
+ wsprintf (lpErrorMessage,
+ L"The master browser %s on domain %s transport %s failed to return the domain list.\n",
+ lpMasterName,
+ lpDomainName,
+ lpTransportName);
+
+ lstrcat (lpErrorMessage, GetError (dwError));
+ break;
+
+ case BROWSER_NO_DOMAIN_LIST:
+ wsprintf (lpErrorMessage,
+ L"Browser %s on domain %s transport %s failed to return the domain list.\nThe domain master is owned by %s.\n",
+ lpBrowserName,
+ lpDomainName,
+ lpTransportName,
+ lpMasterName);
+
+ lstrcat (lpErrorMessage, GetError (dwError));
+ break;
+
+ case STALE_SERVER:
+ wsprintf (lpErrorMessage,
+ L"Browser %s on domain %s,transport %s returned an incorrect number of servers. \nThe domain master is owned by %s.\n",
+ lpBrowserName,
+ lpDomainName,
+ lpTransportName,
+ lpMasterName);
+ break;
+
+ case STALE_DOMAIN:
+ wsprintf (lpErrorMessage,
+ L"Browser %s on domain %s,transport %s returned an incorrect number of domains. \nThe domain master is owned by %s.\n",
+ lpBrowserName,
+ lpDomainName,
+ lpTransportName,
+ lpMasterName);
+ break;
+
+ case MASTER_NO_LOCAL_LIST:
+ wsprintf (lpErrorMessage,
+ L"The master browser %s on domain %s transport %s failed to return the local list.\n",
+ lpMasterName,
+ lpDomainName,
+ lpTransportName);
+
+ lstrcat (lpErrorMessage, GetError (dwError));
+ break;
+
+ case WRONG_NUM_BACKUP:
+ wsprintf (lpErrorMessage,
+ L"Incorrect number of Backup Browsers on domain %s transport %s \n nNTMachine = %d \n nBackupBrowser = %d \n nServer = %d \n Expected %d backups \n",
+ lpDomainName,
+ lpTransportName,
+ ((INT *)dwError)[0],
+ ((INT *)dwError)[1],
+ ((INT *)dwError)[2],
+ ((INT *)dwError)[3]);
+
+ break;
+
+ case NO_PDC:
+ wsprintf (lpErrorMessage,
+ L"There are no PDC on domain %s transport %s.\nThe domain master is owned by %s.\n",
+ lpDomainName,
+ lpTransportName,
+ lpMasterName);
+
+ lstrcat (lpErrorMessage, GetError (dwError));
+ break;
+
+ case MASTER_NOT_PDC:
+ wsprintf (lpErrorMessage,
+ L"The master browser %s on domain %s transport %s is not a PDC.\nPDC is owned by %s.\n",
+ lpBrowserName,
+ lpDomainName,
+ lpTransportName,
+ lpMasterName);
+
+ break;
+ case BROWSE_TOO_LONG:
+ wsprintf (lpErrorMessage,
+ L"Browse request to %ws (domain %ws, transport %ws), took %ld milliseconds.\n",
+ lpBrowserName,
+ lpDomainName,
+ lpTransportName,
+ dwError);
+
+ break;
+
+ default:
+ return;
+ }
+
+ if (lpBrowserName != NULL)
+ {
+ lstrcat (lpErrorMessage, ServerInfo(lpBrowserName));
+
+ if ((dwError != 0) && ((nErrorType == MASTER_NO_SERVER_LIST) ||
+ (nErrorType == MASTER_NO_DOMAIN_LIST) ||
+ (nErrorType == MASTER_NO_LOCAL_LIST) ||
+ (nErrorType == BROWSER_NO_SERVER_LIST)||
+ (nErrorType == BROWSER_NO_DOMAIN_LIST)))
+
+ {
+ WCHAR ShareName[UNCLEN+1];
+
+ wcscpy(ShareName, lpBrowserName);
+ wcscat(ShareName, L"\\Ipc$");
+
+ NetUseDel(NULL, ShareName, USE_LOTS_OF_FORCE);
+
+ dwNSGI = NetServerGetInfo (lpBrowserName,
+ 101,
+ (LPBYTE *)&pSV101);
+
+ if (dwNSGI == NERR_Success &&
+ !(pSV101->sv101_type & SV_TYPE_POTENTIAL_BROWSER))
+ {
+ if (dwError == ERROR_BAD_NETPATH && dwNSGI == NERR_Success) {
+ DbgPrint("Sending magic bullet\n");
+ SendMagicBullet();
+ }
+
+ CheckErrorType5(lpBrowserName,
+ lpTransportName,
+ lpDomainName,
+ lpUser);
+ } else {
+#if 0
+
+ if (dwError == ERROR_VC_DISCONNECTED &&
+ dwNSGI == ERROR_VC_DISCONNECTED) {
+ DbgPrint("Sending magic bullet\n");
+ SendMagicBullet();
+ }
+#endif
+
+ }
+
+ if (pSV101 != NULL)
+ {
+ MIDL_user_free (pSV101);
+ }
+ }
+ }
+
+ RecordError (&fError[nErrorType],
+ lpBrowserName,
+ lpTransportName,
+ lpDomainName,
+ lpUser,
+ lpErrorMessage,
+ dwError,
+ dwNSGI,
+ nErrorType);
+}
+
+void RecordError (BOOL * pfError,
+ LPTSTR lpBrowserName,
+ LPTSTR lpTransportName,
+ LPTSTR lpDomainName,
+ LPTSTR lpUser,
+ LPTSTR lpErrorMessage,
+ DWORD dwError,
+ DWORD dwNSGI,
+ INT nErrorType)
+{
+ SYSTEMTIME systime;
+
+ GetLocalTime (&systime);
+
+ if ((nErrorType != BROWSER_NO_SERVER_LIST) &&
+ (nErrorType != BROWSER_NO_DOMAIN_LIST) &&
+ (nErrorType != NO_PDC))
+ {
+ LogError (pLOGFILE, lpErrorMessage);
+ }
+ else
+ {
+ LogWarning (pLOGFILE, lpErrorMessage);
+ }
+
+ //
+ // Notify that the error occurred.
+ //
+
+ if ((nErrorType != BROWSER_NO_SERVER_LIST) &&
+ (nErrorType != BROWSER_NO_DOMAIN_LIST) &&
+ (nErrorType != MASTER_NOT_PDC) &&
+ (nErrorType != WRONG_NUM_BACKUP) &&
+ (nErrorType != NO_PDC)) {
+ NotifyUser (lpUser, lpErrorMessage);
+ }
+
+ if (*pfError) // A known error.
+ {
+ ERRORLIST * pErrorEntry = pErrorList[nErrorType];
+
+ do
+ {
+ if (!lstrcmp (pErrorEntry->lpTransport, lpTransportName) &&
+ !lstrcmp (pErrorEntry->lpDomain, lpDomainName) &&
+ ((lpBrowserName == NULL) ||
+ ((lpBrowserName != NULL) &&
+ (!lstrcmp (pErrorEntry->lpServer, lpBrowserName)))) &&
+ ((nErrorType == WRONG_NUM_BACKUP) ||
+ (nErrorType == STALE_SERVER) ||
+ (nErrorType == STALE_DOMAIN) ||
+ ((pErrorEntry->nVal1 == dwError) &&
+ (pErrorEntry->nVal2 == dwNSGI))))
+ {
+ if ((nErrorType != STALE_SERVER) &&
+ (nErrorType != STALE_DOMAIN))
+ {
+ // record the last time the error occurred.
+ pErrorEntry->wHour = systime.wHour;
+ pErrorEntry->wMinute = systime.wMinute;
+ }
+ else
+ {
+ AddStaleEntry (pErrorEntry,
+ ((DWORD *) dwError)[1],
+ ((DWORD *) dwError)[0],
+ systime.wHour,
+ systime.wMinute);
+ }
+ (pErrorEntry->nCount)++;
+ return;
+ }
+
+ if (pErrorEntry->pNextEntry != NULL)
+ {
+ pErrorEntry = pErrorEntry->pNextEntry;
+ }
+ else
+ {
+ pErrorEntry->pNextEntry = NewEntry (lpBrowserName,
+ lpTransportName,
+ lpDomainName,
+ dwError,
+ dwNSGI,
+ systime.wHour,
+ systime.wMinute,
+ nErrorType);
+ return;
+ }
+ } while (TRUE);
+ }
+ else // Error never occur before.
+ {
+ pErrorList[nErrorType] = NewEntry(lpBrowserName,
+ lpTransportName,
+ lpDomainName,
+ dwError,
+ dwNSGI,
+ systime.wHour,
+ systime.wMinute,
+ nErrorType);
+ if (pErrorList[nErrorType] != NULL)
+ {
+ (*pfError) = TRUE;
+ }
+ }
+}
+
+PERRORLIST NewEntry (LPTSTR lpServer,
+ LPTSTR lpTransport,
+ LPTSTR lpDomain,
+ DWORD dwError,
+ DWORD dwNSGI,
+ WORD wHour,
+ WORD wMinute,
+ INT nErrorType)
+{
+ PERRORLIST pNewEntry;
+
+ // Added for reduce the logfile size, ie not report error caused
+ // by machine crash.
+ if ((dwError == 53) &&
+ (dwNSGI == 53) &&
+ !IsOurMachine (lpServer))
+ {
+ return(NULL);
+ }
+
+ pNewEntry = (PERRORLIST) LocalAlloc (LPTR, sizeof(ERRORLIST));
+ if (pNewEntry == NULL)
+ {
+ fprintf(stdout, "Not enough memory");
+ return(NULL);
+ }
+
+ pNewEntry->lpTransport = (LPTSTR) LocalAlloc (LPTR, (2*lstrlen(lpTransport)+2));
+ pNewEntry->lpDomain = (LPTSTR) LocalAlloc (LPTR, (2*lstrlen(lpDomain)+2));
+ if ((pNewEntry->lpTransport == NULL) ||
+ (pNewEntry->lpDomain == NULL))
+ {
+ fprintf(stdout, "Not enough memory");
+ return(NULL);
+ }
+
+ if (lpServer != NULL)
+ {
+ pNewEntry->lpServer = (LPTSTR) LocalAlloc (LPTR, (2*lstrlen(lpServer)+2));
+ if (pNewEntry->lpServer == NULL)
+ {
+ fprintf(stdout, "Not enough memory");
+ return(NULL);
+ }
+ lstrcpy (pNewEntry->lpServer, lpServer);
+ }
+
+ lstrcpy (pNewEntry->lpTransport, lpTransport);
+ lstrcpy (pNewEntry->lpDomain, lpDomain);
+ pNewEntry->nCount = 1;
+ pNewEntry->wHour = wHour;
+ pNewEntry->wMinute = wMinute;
+ pNewEntry->pNextEntry = NULL;
+
+ if (nErrorType == WRONG_NUM_BACKUP)
+ {
+ pNewEntry->nVal1 = ((INT *)dwError)[0];
+ pNewEntry->nVal2 = ((INT *)dwError)[1];
+ pNewEntry->nVal3 = ((INT *)dwError)[2];
+ pNewEntry->nVal4 = ((INT *)dwError)[3];
+ }
+ else if ((nErrorType == STALE_SERVER)||
+ (nErrorType == STALE_DOMAIN))
+
+ {
+ AddStaleEntry (pNewEntry,
+ ((DWORD *)dwError)[1],
+ ((DWORD *)dwError)[0],
+ wHour,
+ wMinute);
+ }
+ else
+ {
+ pNewEntry->nVal1 = dwError;
+ pNewEntry->nVal2 = dwNSGI;
+ }
+
+ return(pNewEntry);
+}
+
+void AddStaleEntry (PERRORLIST pErrorList,
+ DWORD dwBackupEntry,
+ DWORD dwMasterEntry,
+ WORD wHour,
+ WORD wMinute)
+{
+ PENTRYLIST pNewEntry;
+
+ pNewEntry = (PENTRYLIST) LocalAlloc (LPTR, sizeof(ENTRYLIST));
+ if (pNewEntry == NULL)
+ {
+ fprintf(stdout, "Not enough memory");
+ return;
+ }
+
+ pNewEntry->dwBackupEntry = dwBackupEntry;
+ pNewEntry->dwMasterEntry = dwMasterEntry;
+ pNewEntry->wHour = wHour;
+ pNewEntry->wMinute = wMinute;
+ pNewEntry->pNextEntry = (PENTRYLIST) pErrorList->nVal1;
+
+ *((PENTRYLIST *)(&pErrorList->nVal1)) = pNewEntry;
+}
+
+// Get all the servers in lpBrowserName.
+BOOL MyGetList (LPTSTR lpUser,
+ LPTSTR lpBrowserName,
+ LPTSTR lpTransportName,
+ LPTSTR lpDomainName,
+ LPTSTR lpMasterName,
+ DWORD * pdwEntriesRead,
+ DWORD * pdwTotalEntries,
+ LPVOID * lppBrowserList,
+ BOOL fBrowserNotDomain,
+ DWORD nTimeLimit)
+{
+ DWORD dwVal;
+ INT nErrorType;
+ DWORD dwStartTime;
+ DWORD dwEndTime;
+
+ nErrorType = lstrcmp(lpBrowserName, lpMasterName)?
+ (fBrowserNotDomain? BROWSER_NO_SERVER_LIST : BROWSER_NO_DOMAIN_LIST) :
+ (fBrowserNotDomain? MASTER_NO_SERVER_LIST : MASTER_NO_DOMAIN_LIST);
+
+
+ dwStartTime = GetTickCount();
+
+ dwVal = MyRxNetServerEnum (lpBrowserName,
+ lpTransportName,
+ 101,
+ (LPBYTE *) lppBrowserList,
+ 0xffffffff,
+ pdwEntriesRead,
+ pdwTotalEntries,
+ fBrowserNotDomain? SV_TYPE_ALL : SV_TYPE_DOMAIN_ENUM,
+ lpDomainName,
+ NULL);
+
+ dwEndTime = GetTickCount();
+
+ if (dwVal != NERR_Success) // Failed to return backup browser list.
+ {
+ ReportError (lpUser,
+ lpTransportName,
+ lpDomainName,
+ lpMasterName,
+ lpBrowserName,
+ dwVal,
+ nErrorType);
+
+ return(FALSE);
+ }
+ else // Continue if we got a backup list from the backup browser.
+ {
+ nCountSuccess[nErrorType]++;
+
+ if ((dwEndTime - dwStartTime) > nTimeLimit) {
+
+ dwVal = dwEndTime - dwStartTime;
+
+ //
+ // If this took "too long", log it as an error.
+ //
+
+ ReportError (lpUser,
+ lpTransportName,
+ lpDomainName,
+ lpMasterName,
+ lpBrowserName,
+ dwVal,
+ BROWSE_TOO_LONG);
+
+ if ((dwEndTime - dwStartTime) > 2*nTimeLimit) {
+ SendMagicBullet();
+ }
+
+ } else {
+ nCountSuccess[BROWSE_TOO_LONG] += 1;
+ }
+
+ // Log the number of backups etc.
+ fprintf (pLOGFILE,
+ "There are %lu %s on browser %s\nEntriesRead = %lu. TotalEntries = %lu. Time = %lu milliseconds\n",
+ *pdwTotalEntries,
+ fBrowserNotDomain? "server" : "domain",
+ toansi (lpBrowserName),
+ *pdwEntriesRead,
+ *pdwTotalEntries,
+ dwEndTime - dwStartTime);
+
+ WriteList (pLOGFILE, *lppBrowserList, *pdwEntriesRead);
+ return(TRUE);
+ }
+}
+
+// Check for error type 2: Incorrect server lists returned by
+// backup browsers (and master browsers). This includes stale
+// servers in the list. Same for the domain list.
+void CheckErrorType2 (LPTSTR lpUser,
+ LPTSTR lpTransportName,
+ LPTSTR lpDomainName,
+ LPTSTR lpMasterName,
+ PWSTR * lpList,
+ DWORD dwEntries,
+ INT nTolerancePercent,
+ DWORD nTimeLimit)
+{
+ // Check if the server list returned from backup browser is the same
+ // as that from the master browser.
+
+ CheckList (lpUser,
+ lpTransportName,
+ lpDomainName,
+ lpMasterName,
+ lpList,
+ dwEntries,
+ nTolerancePercent,
+ TRUE,
+ nTimeLimit);
+
+ // Check if the domain list returned from backup browser is the same
+ // as that from the master browser.
+
+ CheckList (lpUser,
+ lpTransportName,
+ lpDomainName,
+ lpMasterName,
+ lpList,
+ dwEntries,
+ nTolerancePercent,
+ FALSE,
+ nTimeLimit);
+}
+
+void CheckList (LPTSTR lpUser,
+ LPTSTR lpTransportName,
+ LPTSTR lpDomainName,
+ LPTSTR lpMasterName,
+ PWSTR * lpList,
+ DWORD dwEntries,
+ INT nTolerancePercent,
+ BOOL fBrowserNotDomain,
+ DWORD nTimeLimit)
+{
+ INT i;
+ INT nTolerance;
+ INT nErrorType = fBrowserNotDomain? STALE_SERVER : STALE_DOMAIN;
+ BOOL fError = FALSE;
+ DWORD dwTotalEntries; // Total Backup Browsers
+ DWORD dwEntriesRead; // Total Backup Browsers Read.
+ DWORD dwBackupTotalEntries; // Total Backup Browsers
+ DWORD dwBackupEntriesRead; // Total Backup Browsers Read.
+ LPVOID lpBrowserList;
+ LPVOID lpBackupBrowserList;
+
+ if (!MyGetList(lpUser,
+ lpMasterName,
+ lpTransportName,
+ lpDomainName,
+ lpMasterName,
+ &dwEntriesRead,
+ &dwTotalEntries,
+ &lpBrowserList,
+ fBrowserNotDomain,
+ nTimeLimit))
+ {
+ if (lpBrowserList != NULL)
+ {
+ MIDL_user_free (lpBrowserList);
+ }
+
+ return;
+ }
+
+ nTolerance = nTolerancePercent*dwEntriesRead/100;
+
+ for (i = 0; i < (INT) dwEntries ; i++) // Enumerate on backups.
+ {
+ if (lstrcmp (lpMasterName, lpList[i])) // When they are different it returns true.
+ {
+ if (MyGetList (lpUser,
+ lpList[i],
+ lpTransportName,
+ lpDomainName,
+ lpMasterName,
+ &dwBackupEntriesRead,
+ &dwBackupTotalEntries,
+ &lpBackupBrowserList,
+ fBrowserNotDomain,
+ nTimeLimit))
+ {
+ // Compare the number of browserlist from the backup with the one
+ // from the master.
+
+ if ((abs(dwBackupEntriesRead - dwEntriesRead) > 10) &&
+ ((dwEntriesRead > dwBackupEntriesRead + nTolerance) ||
+ (dwEntriesRead < dwBackupEntriesRead - nTolerance)))
+ {
+ fError = TRUE;
+ }
+ else
+ {
+ // Compare the servers returned from backups with those from master.
+ // When the list are too different, CompareList returns false.
+ fError = !CompareList (lpBrowserList,
+ lpBackupBrowserList,
+ dwEntriesRead,
+ dwBackupEntriesRead,
+ nTolerance);
+ }
+
+ if (fError) // Type 2 error occured.
+ {
+ DWORD dwEntries[2] = {dwEntriesRead, dwBackupEntriesRead};
+
+ ReportError (lpUser,
+ lpTransportName,
+ lpDomainName,
+ lpMasterName,
+ lpList[i],
+ (DWORD)dwEntries,
+ nErrorType);
+ } // End of if type 2 error occured.
+ else
+ {
+ nCountSuccess[nErrorType]++;
+ }
+
+ } //End of if we get a browser list from the backup browser.
+
+ if (lpBackupBrowserList != NULL)
+ {
+ MIDL_user_free (lpBackupBrowserList);
+ }
+
+ } // End of if when we get a backup browser.
+
+ } // End of for statement.
+
+ MIDL_user_free (lpBrowserList);
+}
+
+// Check for error type 3: Incorrect number of browser servers.
+void CheckErrorType3 (LPTSTR lpUser,
+ LPTSTR lpTransportName,
+ LPTSTR lpDomainName,
+ LPTSTR lpMasterName)
+{
+ INT i;
+ INT nNTMachine = 0;
+ DWORD nBackup = 0;
+ INT nServer;
+ DWORD dwVal;
+ DWORD dwEntriesRead;
+ DWORD dwTotalEntries;
+// TCHAR lpErrorMessage[STRINGLEN];
+ LPVOID lpServerList;
+ PSERVER_INFO_101 pServerInfo101;
+
+ // Get the number of servers.
+ dwVal = MyRxNetServerEnum (lpMasterName,
+ lpTransportName,
+ 101,
+ (LPBYTE *) &lpServerList,
+ 0xffffffff,
+ &dwEntriesRead,
+ &dwTotalEntries,
+ SV_TYPE_LOCAL_LIST_ONLY,
+ lpDomainName,
+ NULL);
+
+ if (dwVal != NERR_Success) {
+ if (lpServerList != NULL) {
+ MIDL_user_free (lpServerList);
+ }
+
+ ReportError (lpUser,
+ lpTransportName,
+ lpDomainName,
+ lpMasterName,
+ lpMasterName,
+ dwVal,
+ MASTER_NO_LOCAL_LIST); //Error type number.
+ return;
+ } else { // Got the server list.
+ DWORD nExpected;
+ nCountSuccess[MASTER_NO_LOCAL_LIST]++;
+
+ nServer = dwEntriesRead;
+
+ pServerInfo101 = lpServerList;
+
+ for (i = 0; i < (INT) dwEntriesRead; i++ ) {
+ if (pServerInfo101[i].sv101_type & SV_TYPE_BACKUP_BROWSER) {
+ nBackup++;
+ }
+
+ if ( (pServerInfo101[i].sv101_type & SV_TYPE_BACKUP_BROWSER) &&
+ !(pServerInfo101[i].sv101_type & SV_TYPE_POTENTIAL_BROWSER)) {
+
+ nNTMachine++;
+ }
+ }
+
+ //Check for error type 3: Incorrect number of browser server.
+
+ if (nNTMachine > 1) {
+ nExpected = nNTMachine;
+ } else {
+ nExpected = (nServer+31)/32;
+ }
+
+ if ((nNTMachine > 1 && (nBackup != nExpected)) ||
+ ((nNTMachine <= 1) && (nBackup < nExpected))) {
+
+ INT nMachine[4] = {nNTMachine, nBackup, nServer, nExpected};
+
+ ReportError(lpUser,
+ lpTransportName,
+ lpDomainName,
+ NULL,
+ NULL,
+ (DWORD) nMachine,
+ WRONG_NUM_BACKUP);
+ } else { // No type 3 error. Write info to the logfile.
+ nCountSuccess[WRONG_NUM_BACKUP]++;
+ }
+
+ MIDL_user_free (lpServerList);
+ }
+}
+
+// Check for error type 4: Master is not a PDC.
+void CheckErrorType4 (LPTSTR lpUser,
+ LPTSTR lpTransportName,
+ LPTSTR lpDomainName,
+ LPTSTR lpMasterName)
+{
+ LPTSTR lpPDCName = NULL;
+ DWORD dwVal;
+
+ dwVal = NetGetDCName (NULL,
+ lpDomainName,
+ (LPBYTE *)&lpPDCName);
+ if (dwVal != NERR_Success)
+ {
+ ReportError (lpUser,
+ lpTransportName,
+ lpDomainName,
+ lpMasterName,
+ lpMasterName,
+ dwVal,
+ NO_PDC);
+ }
+ else
+ {
+ nCountSuccess[NO_PDC]++;
+
+ if (lstrcmp (lpMasterName, lpPDCName))
+ {
+ ReportError (lpUser,
+ lpTransportName,
+ lpDomainName,
+ lpPDCName,
+ lpMasterName,
+ 0,
+ MASTER_NOT_PDC);
+ }
+ else
+ {
+ nCountSuccess[MASTER_NOT_PDC]++;
+ }
+ }
+
+ if (lpPDCName != NULL)
+ {
+ LocalFree(lpPDCName);
+ }
+}
+
+void CheckErrorType5 (LPTSTR lpBrowserName,
+ LPTSTR lpTransportName,
+ LPTSTR lpDomainName,
+ LPTSTR lpUser)
+{
+ PLMDR_TRANSPORT_LIST TransportList = NULL;
+ PLMDR_TRANSPORT_LIST TransportEntry = NULL;
+ NET_API_STATUS Status;
+
+ Status = GetBrowserTransportList(&TransportList);
+ if (Status != NERR_Success)
+ {
+ return;
+ }
+
+ TransportEntry = TransportList;
+
+ while (TransportEntry != NULL)
+ {
+ UNICODE_STRING TransportName;
+ LPBYTE lpBrowserList;
+ ULONG dwEntriesRead, dwTotalEntries;
+
+ TransportName.Buffer = TransportEntry->TransportName;
+ TransportName.Length = (USHORT) TransportEntry->TransportNameLength;
+ TransportName.MaximumLength = (USHORT) TransportEntry->TransportNameLength;
+
+ Status = RxNetServerEnum(lpBrowserName,
+ TransportName.Buffer,
+ 101,
+ &lpBrowserList,
+ 0xffffffff,
+ &dwEntriesRead,
+ &dwTotalEntries,
+ SV_TYPE_ALL,
+ NULL,
+ NULL);
+
+ if (lpBrowserList != NULL)
+ {
+ NetApiBufferFree(lpBrowserList);
+ }
+
+ if (Status == NERR_Success)
+ {
+ break;
+ }
+
+ if (TransportEntry->NextEntryOffset == 0)
+ TransportEntry = NULL;
+ else
+ {
+ TransportEntry = (PLMDR_TRANSPORT_LIST) ((PCHAR) TransportEntry
+ +TransportEntry->NextEntryOffset);
+ }
+ }
+
+ NetApiBufferFree(TransportList);
+
+ if (Status != NERR_Success)
+ {
+ static BOOL fError = FALSE;
+ TCHAR lpErrorMessage[STRINGLEN];
+
+ nCountFail[TRANSPORT_FAILURE]++;
+ wsprintf (lpErrorMessage, L"Browser %ws failed on all transports.\n", lpBrowserName);
+
+ RecordError (&fError,
+ lpBrowserName,
+ lpTransportName,
+ lpDomainName,
+ lpUser,
+ lpErrorMessage,
+ 0,
+ 0,
+ TRANSPORT_FAILURE);
+ }
+ else
+ nCountSuccess[TRANSPORT_FAILURE]++;
+}
+
+LPTSTR ServerInfo (LPTSTR lpServer)
+{
+ PSERVER_INFO_101 psvInfo = NULL;
+ static TCHAR lpTemp[STRINGLEN/2];
+
+ WCHAR ShareName[STRINGLEN/2];
+
+ wcscpy(ShareName, lpServer);
+ wcscat(ShareName, L"\\Ipc$");
+
+ NetUseDel(NULL, ShareName, USE_LOTS_OF_FORCE);
+
+ if (NetServerGetInfo (lpServer,
+ 101,
+ (LPBYTE *)&psvInfo) == NERR_Success)
+ {
+ wsprintf (lpTemp,
+ L"\\\\%ws: version=%ld.%ld type=",
+ psvInfo->sv101_name,
+ psvInfo->sv101_version_major,
+ psvInfo->sv101_version_minor);
+
+ if (psvInfo->sv101_type&SV_TYPE_WORKSTATION)
+ {
+ lstrcat (lpTemp, L"WORKSTATION");
+ }
+
+ if (psvInfo->sv101_type&SV_TYPE_SERVER)
+ {
+ lstrcat (lpTemp, L" | SERVER");
+ }
+
+ if (psvInfo->sv101_type&SV_TYPE_SQLSERVER)
+ {
+ lstrcat (lpTemp, L" | SQLSERVER");
+ }
+
+ if (psvInfo->sv101_type&SV_TYPE_DOMAIN_CTRL)
+ {
+ lstrcat (lpTemp, L" | DOMAIN_CTRL");
+ }
+
+ if (psvInfo->sv101_type&SV_TYPE_DOMAIN_BAKCTRL)
+ {
+ lstrcat (lpTemp, L" | DOMAIN_BAKCTRL");
+ }
+
+ if (psvInfo->sv101_type&SV_TYPE_TIME_SOURCE)
+ {
+ lstrcat (lpTemp, L" | TIME_SOURCE");
+ }
+
+ if (psvInfo->sv101_type&SV_TYPE_AFP)
+ {
+ lstrcat (lpTemp, L" | AFP");
+ }
+
+ if (psvInfo->sv101_type&SV_TYPE_NOVELL)
+ {
+ lstrcat (lpTemp, L" | NOVELL");
+ }
+
+ if (psvInfo->sv101_type&SV_TYPE_DOMAIN_MEMBER)
+ {
+ lstrcat (lpTemp, L" | DOMAIN_MEMEBER");
+ }
+
+ if (psvInfo->sv101_type&SV_TYPE_PRINTQ_SERVER)
+ {
+ lstrcat (lpTemp, L" | PRINTQ_SERVER");
+ }
+
+ if (psvInfo->sv101_type&SV_TYPE_DIALIN_SERVER)
+ {
+ lstrcat (lpTemp, L" | DIALIN_SERVER");
+ }
+
+ if (psvInfo->sv101_type&SV_TYPE_XENIX_SERVER)
+ {
+ lstrcat (lpTemp, L" | XENIX_SERVER");
+ }
+
+ if (psvInfo->sv101_type&SV_TYPE_NT)
+ {
+ lstrcat (lpTemp, L" | NT");
+ }
+
+ if (psvInfo->sv101_type&SV_TYPE_WFW)
+ {
+ lstrcat (lpTemp, L" | WFW");
+ }
+
+ if (psvInfo->sv101_type&SV_TYPE_POTENTIAL_BROWSER)
+ {
+ lstrcat (lpTemp, L" | POTNETIAL_BROWSER");
+ }
+
+ if (psvInfo->sv101_type&SV_TYPE_BACKUP_BROWSER)
+ {
+ lstrcat (lpTemp, L" | BACKUP_BROWSER");
+ }
+
+ if (psvInfo->sv101_type&SV_TYPE_MASTER_BROWSER)
+ {
+ lstrcat (lpTemp, L" | MASTER_BROWSER");
+ }
+
+ if (psvInfo->sv101_type&SV_TYPE_DOMAIN_MASTER)
+ {
+ lstrcat (lpTemp, L" | DOMAIN_MASTER");
+ }
+
+ lstrcat (lpTemp, L"\n");
+ MIDL_user_free (psvInfo);
+
+ return(lpTemp);
+ }
+ else
+ return(L"\0");
+}
+
+BOOL
+ConvertFile(
+ DWORD CtrlType
+ )
+{
+ WriteSummory();
+ MyFreeList();
+ fclose (pLOGFILE);
+
+ CtrlType;
+
+ return FALSE;
+}
+
+void WriteSummory()
+{
+ FILE * pFile;
+ INT i;
+ LPSTR lpText[ERRORTYPENUM];
+
+ lpText[0] = szText0;
+ lpText[1] = szText1;
+ lpText[2] = szText2;
+ lpText[3] = szText3;
+ lpText[4] = szText4;
+ lpText[5] = szText5;
+ lpText[6] = szText6;
+ lpText[7] = szText7;
+ lpText[8] = szText8;
+ lpText[9] = szText9;
+ lpText[10] = szText10;
+ lpText[11] = szText11;
+ lpText[12] = szText12;
+ lpText[13] = szText13;
+ lpText[14] = szText14;
+ lpText[15] = szText15;
+ lpText[16] = szText16;
+ lpText[17] = szText17;
+ lpText[18] = szText18;
+
+ pFile = fopen (szLOGFILE, "w+");
+
+ if (pFile == NULL) {
+ fprintf (stderr, "The browser checker was unable to open the log file.\n");
+ return;
+ }
+
+ fprintf (pFile, "The browser check has run %d times.\n", nTimes);
+
+ for (i=0 ; i < ERRORTYPENUM ; i++)
+ {
+ fprintf (pFile,
+ "%d : %d -- %s\n",
+ nCountFail[i],
+ nCountSuccess[i],
+ lpText[i]);
+
+ PrintEntries(pFile, i, pErrorList[i]);
+
+ fprintf (pFile, "\n");
+ }
+
+ fclose(pFile);
+}
+
+void PrintEntries (FILE * pFile,
+ INT nErrorType,
+ PERRORLIST pErrorList)
+{
+ PERRORLIST pErrorEntry;
+
+ pErrorEntry = pErrorList;
+
+ while (pErrorEntry != NULL)
+ {
+ if ((nErrorType == STALE_SERVER) ||
+ (nErrorType == STALE_DOMAIN))
+ {
+ PENTRYLIST pEntryList;
+ pEntryList = (PENTRYLIST) pErrorEntry->nVal1;
+
+ fprintf (pFile,
+ toansi(L" %ws %ws %ws occurred %d times.\n"),
+ pErrorEntry->lpTransport,
+ pErrorEntry->lpDomain,
+ pErrorEntry->lpServer,
+ pErrorEntry->nCount);
+
+ while (pEntryList != NULL)
+ {
+ fprintf (pFile,
+ " BackupEntries = %lu, MasterEntries = %lu, time = %d:%d\n",
+ pEntryList->dwBackupEntry,
+ pEntryList->dwMasterEntry,
+ pEntryList->wHour,
+ pEntryList->wMinute);
+
+ pEntryList = pEntryList->pNextEntry;
+ }
+ }
+ else
+ {
+ if (pErrorEntry->lpServer != NULL) {
+ fprintf (pFile,
+ toansi(L" %ws %ws %ws occurred %d times. Last time at %d:%d\n"),
+ pErrorEntry->lpTransport,
+ pErrorEntry->lpDomain,
+ pErrorEntry->lpServer,
+ pErrorEntry->nCount,
+ pErrorEntry->wHour,
+ pErrorEntry->wMinute);
+ } else {
+ fprintf (pFile,
+ toansi(L" %ws %ws occurred %d times. Last time at %d:%d\n"),
+ pErrorEntry->lpTransport,
+ pErrorEntry->lpDomain,
+ pErrorEntry->nCount,
+ pErrorEntry->wHour,
+ pErrorEntry->wMinute);
+ }
+
+ if (nErrorType == WRONG_NUM_MASTER) {
+ fprintf (pFile,
+ " %d masters returned.\n",
+ pErrorEntry->nVal1);
+ } else if (nErrorType == BROWSE_TOO_LONG) {
+ fprintf (pFile,
+ " Request took %d milliseconds.\n",
+ pErrorEntry->nVal1);
+ } else if (nErrorType == WRONG_NUM_BACKUP) {
+ fprintf (pFile,
+ " nNTMachine = %d nBackupBrowser = %d nServer = %d, nExpected %d \n",
+ pErrorEntry->nVal1,
+ pErrorEntry->nVal2,
+ pErrorEntry->nVal3,
+ pErrorEntry->nVal4);
+
+ }
+ else
+ {
+ if (pErrorEntry->nVal1 != 0)
+ {
+ fprintf (pFile,
+ " Error %d occurred : %s",
+ pErrorEntry->nVal1,
+ toansi (GetError (pErrorEntry->nVal1)));
+
+ if ((pErrorEntry->nVal1 != 0) &&
+ ((nErrorType == MASTER_NO_SERVER_LIST) ||
+ (nErrorType == MASTER_NO_DOMAIN_LIST) ||
+ (nErrorType == BROWSER_NO_SERVER_LIST) ||
+ (nErrorType == BROWSER_NO_DOMAIN_LIST) ||
+ (nErrorType == MASTER_NO_LOCAL_LIST)))
+ {
+ fprintf (pFile,
+ " NetServerGetInfo returned %d : %s",
+ pErrorEntry->nVal2,
+ toansi (GetError (pErrorEntry->nVal2)));
+ }
+ }
+ }
+ }
+
+ pErrorEntry = pErrorEntry->pNextEntry;
+ }
+}
+
+void MyFreeList ()
+{
+ INT i;
+ PERRORLIST pThisEntry;
+ PERRORLIST pErrorEntry;
+
+ for (i=0 ; i < ERRORTYPENUM ; i++)
+ {
+ pErrorEntry = pErrorList[i];
+
+ while (pErrorEntry != NULL)
+ {
+ pThisEntry = pErrorEntry;
+ pErrorEntry = pThisEntry->pNextEntry;
+
+ if (pThisEntry->lpServer != NULL)
+ {
+ LocalFree(pThisEntry->lpServer);
+ }
+ LocalFree (pThisEntry->lpTransport);
+ LocalFree (pThisEntry->lpDomain);
+
+ if ((i == STALE_SERVER) ||
+ (i == STALE_DOMAIN))
+ {
+ PENTRYLIST pEntryList;
+ PENTRYLIST pFreeEntry;
+
+ pEntryList = (PENTRYLIST)pThisEntry->nVal1;
+
+ while (pEntryList != NULL)
+ {
+ pFreeEntry = pEntryList;
+ pEntryList = pFreeEntry->pNextEntry;
+
+ LocalFree (pFreeEntry);
+ }
+ }
+
+ LocalFree (pThisEntry);
+ }
+ }
+}
+
diff --git a/private/net/svcdlls/browser/bchk/bchk.h b/private/net/svcdlls/browser/bchk/bchk.h
new file mode 100644
index 000000000..813556cff
--- /dev/null
+++ b/private/net/svcdlls/browser/bchk/bchk.h
@@ -0,0 +1,305 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ bchk.h
+
+Abstract:
+
+ Pivate header file for bchk.c and support.c
+
+Author:
+
+ Congpa You (CongpaY) 10-Feb-1993
+
+Revision History:
+
+--*/
+
+#define INTLEN 20
+#define STRINGLEN 1024
+
+#define szBchk L"BCHK ASSERT"
+
+#define szAPPNAME L"bchk"
+#define szFILENAME L"bchk.ini"
+#define szTOLERANCE L"Tolerance"
+#define szTIMELIMIT L"BrowseTimeLimit"
+#define szSLEEPTIME L"SleepTime"
+#define szFILESIZE L"FileSizeLimit"
+#define szOTHERUSERS L"OtherUsers"
+#define szDOMAINS L"Domains"
+
+#define szDefaultTolerance L"5"
+#define szDefaultTimeLimit L"10000" // in milliseconds (10 seconds).
+#define szDefaultSleepTime L"900000" // in millisecond.
+#define szDefaultFileSize L"1000000" // in millisecond.
+#define szDefaultOtherUser NULL
+#define szDefaultDomain L"ntlan ntwins"
+
+#define szBCHKLOG "bchklog"
+#define szLBCHKLOG L"bchklog"
+#define szLBACKUP L"backup"
+#define szLOGFILE "logfile"
+#define szALERTLOG "alertlog"
+
+#define szSeps L" ,\t\n"
+
+#define NO_MASTER_RUNNING 0
+#define INVALID_MASTER 1
+#define NO_BACKUP 2
+#define NO_MASTER_NAME 3
+#define WRONG_NUM_MASTER 4
+#define FAIL_RETURN_MASTER 5
+#define UNKNOWN_MASTER 6
+#define MASTER_NO_SERVER_LIST 7
+#define BROWSER_NO_SERVER_LIST 8
+#define MASTER_NO_DOMAIN_LIST 9
+#define BROWSER_NO_DOMAIN_LIST 10
+#define STALE_SERVER 11
+#define STALE_DOMAIN 12
+#define MASTER_NO_LOCAL_LIST 13
+#define WRONG_NUM_BACKUP 14
+#define NO_PDC 15
+#define MASTER_NOT_PDC 16
+#define TRANSPORT_FAILURE 17
+#define BROWSE_TOO_LONG 18
+
+#define ERRORTYPENUM 19
+
+#define szText0 "No browser master is running. (GetBrowserServerList failed)"
+#define szText1 "GetNetBiosMasterName succeeded, but GetBrowserServerList failed."
+#define szText2 "No backup browsers."
+#define szText3 "GetNetBiosMasterName failed, but GetBrowserServerList succeeded"
+#define szText4 "Incorrect number of browser masters were returned."
+#define szText5 "Browser failed to return its master."
+#define szText6 "Cannot determine the master browser's name."
+#define szText7 "Master browser failed to return the server list."
+#define szText8 "Backup browser failed to return the server list."
+#define szText9 "Master browser failed to return the domain list."
+#define szText10 "Backup browser failed to return the domain list."
+#define szText11 "Browser returns an incorrect number of servers."
+#define szText12 "Browser returns an incorrect number of domains."
+#define szText13 "Master browser failed to return the local list."
+#define szText14 "Incorrect number of browser servers."
+#define szText15 "No PDC on the domain (NetGetDCName failed)."
+#define szText16 "Master browser is not the PDC."
+#define szText17 "Browser failed on all transports while NetServerGetInfo succeeded."
+#define szText18 "Browse request took too long."
+
+#define strncpyf wcsncpy
+#define strlenf wcslen
+#define strtokf wcstok
+
+extern FILE * pLOGFILE;
+
+typedef struct _INITPARAM
+{
+ INT nTolerance;
+ INT nSleepTime;
+ INT nFileSizeLimit;
+ INT nTimeLimit;
+ LPTSTR lpUser;
+ LPTSTR lpDomain;
+} INITPARAM;
+
+typedef struct _ERRORLIST
+{
+ LPTSTR lpServer;
+ LPTSTR lpTransport;
+ LPTSTR lpDomain;
+ INT nCount;
+ ULONG nVal1;
+ ULONG nVal2;
+ INT nVal3;
+ INT nVal4;
+ WORD wHour;
+ WORD wMinute;
+ struct _ERRORLIST * pNextEntry;
+} ERRORLIST, * PERRORLIST;
+
+typedef struct _ENTRYLIST
+{
+ DWORD dwBackupEntry;
+ DWORD dwMasterEntry;
+ WORD wHour;
+ WORD wMinute;
+ struct _ENTRYLIST * pNextEntry;
+} ENTRYLIST, * PENTRYLIST;
+
+BOOL Init (INITPARAM * pInitParam);
+
+void InitAllCount ();
+
+void InitCount ();
+
+void CountTotal();
+
+NET_API_STATUS
+MyRxNetServerEnum (
+ IN LPTSTR UncServerName,
+ IN LPTSTR TransportName,
+ IN DWORD Level,
+ OUT LPBYTE *BufPtr,
+ IN DWORD PrefMaxSize,
+ OUT LPDWORD EntriesRead,
+ OUT LPDWORD TotalEntries,
+ IN DWORD ServerType,
+ IN LPTSTR Domain OPTIONAL,
+ IN OUT LPDWORD Resume_Handle OPTIONAL
+ );
+
+NET_API_STATUS GetMasterName (LPTSTR lpMasterName,
+ LPTSTR lpTransportName,
+ LPTSTR lpDomainName);
+
+BOOL RxGetMasterName (LPTSTR lpUser,
+ LPTSTR lpMasterName,
+ LPTSTR lpTransportName,
+ LPTSTR lpDomainName,
+ PWSTR * BrowserList,
+ DWORD BrowserListLength,
+ DWORD nTimeLimit);
+
+BOOL MyGetList (LPTSTR lpUser,
+ LPTSTR lpBrowserName,
+ LPTSTR lpTransportName,
+ LPTSTR lpDomainName,
+ LPTSTR lpMasterName,
+ DWORD * pdwEntriesRead,
+ DWORD * pdwTotalEntries,
+ LPVOID * lppBrowserList,
+ BOOL fBrowserNotDomain,
+ DWORD nTimeLimit);
+
+void ReportError (LPTSTR lpUser,
+ LPTSTR lpTransportName,
+ LPTSTR lpDomainName,
+ LPTSTR lpMasterName,
+ LPTSTR lpBrowserName,
+ DWORD dwError,
+ INT nErrorType);
+
+void CheckErrorType2 (LPTSTR lpUser,
+ LPTSTR lpTransportName,
+ LPTSTR lpDomainName,
+ LPTSTR lpMasterName,
+ PWSTR * lpList,
+ DWORD dwEntries,
+ INT nTolerancePercent,
+ DWORD nTimeLimit);
+
+void CheckList (LPTSTR lpUser,
+ LPTSTR lpTransportName,
+ LPTSTR lpDomainName,
+ LPTSTR lpMasterName,
+ PWSTR * lpList,
+ DWORD dwEntries,
+ INT nTolerancePercent,
+ BOOL fBrowserNotDomain,
+ DWORD nTimeLimit);
+
+void CheckErrorType3 (LPTSTR lpUser,
+ LPTSTR lpTransportName,
+ LPTSTR lpDomainName,
+ LPTSTR lpMasterName);
+
+void CheckErrorType4 (LPTSTR lpUser,
+ LPTSTR lpTransportName,
+ LPTSTR lpDomainName,
+ LPTSTR lpMasterName);
+
+void CheckErrorType5 (LPTSTR lpBrowserName,
+ LPTSTR lpTransportName,
+ LPTSTR lpDomainName,
+ LPTSTR lpUser);
+
+void NotifyUser (LPTSTR lpUser,
+ LPTSTR lpErrorMessage);
+
+LPTSTR GetError (DWORD dwError);
+
+void MyMessageBox (DWORD dwError);
+
+NET_API_STATUS GetBrowserTransportList(
+ OUT PLMDR_TRANSPORT_LIST *TransportList);
+
+LPSTR toansi (LPTSTR lpUnicode);
+
+BOOL CompareList (LPVOID lpBackupList,
+ LPVOID lpBrowserList,
+ DWORD dwBackup,
+ DWORD dwBrowser,
+ INT nTolerance);
+
+void KillSpace (LPTSTR lpTemp);
+
+LPTSTR ServerInfo(LPTSTR lpServer);
+
+BOOL
+ConvertFile(DWORD Event);
+
+void WriteSummory();
+
+void PrintEntries (FILE * pFile,
+ INT nErrorType,
+ PERRORLIST pErrorList);
+
+PERRORLIST NewEntry (LPTSTR lpServer,
+ LPTSTR lpTransport,
+ LPTSTR lpDomain,
+ DWORD dwError,
+ DWORD dwNSGI,
+ WORD wHour,
+ WORD wMinute,
+ INT nErrorType);
+
+void AddStaleEntry (PERRORLIST pErrorList,
+ DWORD dwBackupEntry,
+ DWORD dwMasterEntry,
+ WORD wHour,
+ WORD wMinute);
+void MyFreeList();
+
+void PrintHeader (FILE * pFile);
+
+void PrintSeparation (FILE * pFile);
+
+void LogError (FILE * pFile,
+ LPTSTR lpErrorMessage);
+
+void LogWarning (FILE * pFile,
+ LPTSTR lpErrorMessage);
+
+void LogMaster (FILE * pFile,
+ LPTSTR lpTransportName,
+ LPTSTR lpDomainName,
+ LPTSTR lpMasterName);
+
+void WriteList (FILE * pFile,
+ LPVOID lpBrowserList,
+ DWORD dwEntriesRead);
+
+void WriteBrowserList (FILE * pFile,
+ PWSTR * BrowserList,
+ ULONG BrowserListLength);
+
+BOOL IsOurMachine (LPTSTR lpServer);
+
+void RecordError (BOOL * pfError,
+ LPTSTR lpBrowserName,
+ LPTSTR lpTransportName,
+ LPTSTR lpDomainName,
+ LPTSTR lpUser,
+ LPTSTR lpErrorMessage,
+ DWORD dwError,
+ DWORD dwNSGI,
+ INT nErrorType);
+
+VOID
+SendMagicBullet(
+ VOID
+ );
+
diff --git a/private/net/svcdlls/browser/bchk/makefile b/private/net/svcdlls/browser/bchk/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/net/svcdlls/browser/bchk/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/net/svcdlls/browser/bchk/sources b/private/net/svcdlls/browser/bchk/sources
new file mode 100644
index 000000000..7bb361f5a
--- /dev/null
+++ b/private/net/svcdlls/browser/bchk/sources
@@ -0,0 +1,53 @@
+!IF 0
+
+Copyright (c) 1989-1992 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+
+Author:
+
+ Steve Wood (stevewo) 12-Apr-1990
+ Congpa You (congpay) 04-Feb-1993
+
+NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl
+
+!ENDIF
+
+MAJORCOMP=windows
+MINORCOMP=bchk
+
+TARGETNAME=bchk
+TARGETPATH=obj
+TARGETTYPE=LIBRARY
+
+INCLUDES=..\..\..;..\..\..\inc;..\..\..\..\inc;..;
+
+
+!IFNDEF DISABLE_NET_UNICODE
+UNICODE=1
+NET_C_DEFINES=-DUNICODE
+!ENDIF
+
+SOURCES=support.c
+
+UMTYPE=console
+UMAPPL=bchk
+UMLIBS=obj\*\bchk.lib \
+ \nt\public\sdk\lib\*\netapi32.lib \
+ ..\common\daytona\obj\*\brcommon.lib \
+ \nt\public\sdk\lib\*\rpcutil.lib \
+ \nt\public\sdk\lib\*\ntdll.lib \
+ \nt\public\sdk\lib\*\user32.lib
+
+
+
diff --git a/private/net/svcdlls/browser/bchk/support.c b/private/net/svcdlls/browser/bchk/support.c
new file mode 100644
index 000000000..092d75a04
--- /dev/null
+++ b/private/net/svcdlls/browser/bchk/support.c
@@ -0,0 +1,449 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ support.c
+
+Abstract:
+
+ routes used by bchk.c
+
+Author:
+
+ Congpa You (CongpaY) 10-Feb-1993
+
+Revision History
+
+--*/
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windows.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <lm.h>
+#include <ntddbrow.h> // for LMDR_TRANSPORT_LIST.
+#include <brcommon.h>
+#include <rap.h>
+#include <rxserver.h> // for MyRxNetServerEnum. In net\inc.
+#include "bchk.h"
+
+// Convert the error number to the corresponding error string.
+LPTSTR GetError (DWORD dwError)
+{
+ DWORD dwFlag = FORMAT_MESSAGE_FROM_SYSTEM;
+ static TCHAR szErrorMessage[STRINGLEN];
+ static HANDLE hSource = NULL;
+
+ if ((dwError >= 2100) && (dwError < 6000))
+ {
+ if (hSource == NULL)
+ {
+ hSource = LoadLibrary(L"netmsg.dll");
+ }
+
+ if (hSource == NULL)
+ {
+ wsprintf (szErrorMessage,
+ L"Unable to load netmsg.dll. Error %d occured.\n",
+ dwError);
+ return(szErrorMessage);
+ }
+
+ dwFlag = FORMAT_MESSAGE_FROM_HMODULE;
+ }
+
+ if (!FormatMessage (dwFlag,
+ hSource,
+ dwError,
+ 0,
+ szErrorMessage,
+ STRINGLEN,
+ NULL))
+ {
+ wsprintf (szErrorMessage,
+ L"An unknown error occured: %d \n",
+ dwError);
+ }
+ return(szErrorMessage);
+}
+
+// Local function. Used for reporting an API error.
+void MyMessageBox (DWORD dwError)
+{
+ MessageBox (NULL, GetError(dwError), szBchk, MB_OK|MB_ICONSTOP);
+ return;
+}
+
+// Copied from ..\client\browstub.c.
+NET_API_STATUS GetBrowserTransportList (OUT PLMDR_TRANSPORT_LIST *TransportList)
+{
+
+ NET_API_STATUS Status;
+ HANDLE BrowserHandle;
+ LMDR_REQUEST_PACKET RequestPacket;
+
+ Status = OpenBrowser(&BrowserHandle);
+
+ if (Status != NERR_Success) {
+ return Status;
+ }
+
+ RequestPacket.Version = LMDR_REQUEST_PACKET_VERSION;
+
+ RequestPacket.Type = EnumerateXports;
+
+ RtlInitUnicodeString(&RequestPacket.TransportName, NULL);
+#ifdef _CAIRO_
+ RtlInitUnicodeString(&RequestPacket.EmulatedDomainName, NULL);
+#endif // _CAIRO_
+
+ Status = DeviceControlGetInfo(
+ BrowserHandle,
+ IOCTL_LMDR_ENUMERATE_TRANSPORTS,
+ &RequestPacket,
+ sizeof(RequestPacket),
+ (PVOID *)TransportList,
+ 0xffffffff,
+ 4096,
+ NULL);
+
+ NtClose(BrowserHandle);
+
+ return Status;
+}
+
+// Convert an unicode string to ansi string.
+LPSTR toansi(LPTSTR lpUnicode)
+{
+ static CHAR lpAnsi[STRINGLEN];
+ BOOL fDummy;
+ INT i;
+
+ i = WideCharToMultiByte (CP_ACP,
+ 0,
+ lpUnicode,
+ lstrlen(lpUnicode),
+ lpAnsi,
+ STRINGLEN,
+ NULL,
+ &fDummy);
+
+ lpAnsi[i] = 0;
+
+ return(lpAnsi);
+}
+
+BOOL CompareList (LPVOID lpBackupList,
+ LPVOID lpBrowserList,
+ DWORD dwBackup,
+ DWORD dwBrowser,
+ INT nTolerance)
+{
+ INT i;
+ INT j;
+ INT nSame = 0;
+ PSERVER_INFO_101 pBackupServerInfo101 = lpBackupList;
+ PSERVER_INFO_101 pBrowserServerInfo101 = lpBrowserList;
+
+ for (i = 0; i< (INT) dwBackup ; i++ )
+ {
+ for (j = 0; j < (INT) dwBrowser; j++ )
+ {
+ if (!lstrcmp (pBackupServerInfo101[i].sv101_name,
+ pBrowserServerInfo101[j].sv101_name))
+ {
+ nSame++;
+ break;
+ }
+ }
+ }
+
+ //
+ // If there are at least 10 browsers different between the two
+ // lists, and there is more than the tollerence different, report an
+ // error.
+ //
+
+ if (abs(dwBackup - nSame) > 10 &&
+ abs(dwBrowser - nSame) > 10 &&
+ ((nSame < (INT) dwBackup - nTolerance) ||
+ (nSame < (INT) dwBrowser - nTolerance)))
+ {
+ return(FALSE);
+ }
+ else
+ return(TRUE);
+}
+
+void KillSpace (LPTSTR lpTemp)
+{
+ INT i = 0;
+ while ( (*(lpTemp+i) != 0) &&
+ (*(lpTemp+i) != ' '))
+ {
+ i++;
+ }
+
+ *(lpTemp+i) = 0;
+}
+
+// Write the browser list got from RxNetServerEnum call to the logfile
+void WriteList (FILE * pFile,
+ LPVOID lpList,
+ DWORD dwEntries)
+{
+ INT i;
+ TCHAR lpTemp[UNLEN+1];
+ PSERVER_INFO_101 pServerInfo101;
+
+ if (dwEntries > 10)
+ {
+ fprintf (pFile, "\n");
+ return;
+ }
+
+
+ fprintf (pFile,
+ "The server list has %lu entries:\n",
+ dwEntries);
+
+ pServerInfo101 = lpList;
+
+ for (i = 0; i < (INT) dwEntries ; i++)
+ {
+ lstrcpy (lpTemp, L"\\\\");
+ lstrcat (lpTemp, pServerInfo101[i].sv101_name);
+ fprintf (pFile, "%s", toansi(lpTemp));
+ fprintf (pFile, "\n");
+ }
+
+ fprintf (pFile, "\n");
+}
+
+// Write the browser list get from GetBrowserServerList call to the logfile.
+void WriteBrowserList (FILE * pFile,
+ PWSTR * BrowserList,
+ ULONG BrowserListLength)
+{
+ INT i;
+
+ fprintf (pFile,
+ "The browser list returned from GetBrowserServerList has %lu entries:\n",
+ BrowserListLength);
+
+ for (i = 0; i < (INT) BrowserListLength ; i++)
+ {
+ fprintf (pFile, "%s", toansi(BrowserList[i]));
+ fprintf (pFile, "\n");
+ }
+
+ fprintf(pFile, "\n");
+}
+
+
+// Send lpErrorMessage to the users in lpUser.
+void NotifyUser (LPTSTR lpUser,
+ LPTSTR lpErrorMessage)
+{
+ INT i;
+ TCHAR lpUserName[UNLEN+1];
+ NET_API_STATUS Status;
+
+ if (lpUser == NULL)
+ {
+ return;
+ }
+ while (*lpUser != 0)
+ {
+ i=0;
+ while ((*(lpUser+i) != 0) &&
+ (*(lpUser+i) != ' '))
+ {
+ i++;
+ }
+
+ strncpyf (lpUserName, lpUser, i);
+ lpUserName[i] = 0;
+
+ lpUser += i;
+
+ // Get rid of the space.
+ while ((*lpUser != 0) &&
+ (*lpUser == ' '))
+ {
+ lpUser++;
+ }
+
+ Status = NetMessageBufferSend (NULL,
+ lpUserName,
+ NULL,
+ (LPBYTE) lpErrorMessage,
+ 2*strlenf(lpErrorMessage)+2);
+
+ if (Status != NERR_Success)
+ {
+ TCHAR lpNotifyError[STRINGLEN];
+ wsprintf (lpNotifyError,
+ L"!!! Unable to send the abover message to %s.\n!!! Error %lu occured: %s\n",
+ lpUserName,
+ Status,
+ GetError(Status));
+
+ fprintf (stdout, toansi(lpNotifyError));
+ }
+ }
+}
+
+// Print the start new run header in the logfile.
+void PrintHeader(FILE * pFile)
+{
+ SYSTEMTIME systime;
+
+ GetLocalTime (&systime);
+
+ // Declare a new setion in logfile.
+ fprintf(pFile, "***************************************\n");
+ fprintf(pFile, "*********** START NEW RUN ***********\n");
+
+ fprintf(pFile,
+ "*********** %d/%d/%d %d:%d ***********\n",
+ systime.wMonth,
+ systime.wDay,
+ systime.wYear,
+ systime.wHour,
+ systime.wMinute);
+
+ fprintf(pFile, "***************************************\n\n");
+}
+
+void PrintSeparation (FILE * pFile)
+{
+ fprintf (pFile,
+ "========================================================================\n\n");
+}
+
+// Write lpErrorMessage to the logfile.
+void LogError (FILE * pFile,
+ LPTSTR lpErrorMessage)
+{
+ fprintf(pFile, "________________________________FAILURE___________________________\n\n");
+ fprintf (pFile, toansi(lpErrorMessage));
+ fprintf(pFile, "__________________________________________________________________\n\n");
+}
+
+
+void LogWarning (FILE * pFile,
+ LPTSTR lpErrorMessage)
+{
+ fprintf(pFile, "________________________________WARNING___________________________\n\n");
+ fprintf (pFile, toansi(lpErrorMessage));
+ fprintf(pFile, "__________________________________________________________________\n\n");
+}
+
+// Write mastername and browser list to the logfile.
+void LogMaster (FILE * pFile,
+ LPTSTR lpTransportName,
+ LPTSTR lpDomainName,
+ LPTSTR lpMasterName)
+{
+ TCHAR lpTempString[STRINGLEN];
+
+ wsprintf (lpTempString,
+ L"The master browser on domain %ws, transport %ws, is %ws.\n\n",
+ lpDomainName,
+ lpTransportName,
+ lpMasterName);
+
+ fprintf (pFile, toansi(lpTempString));
+}
+
+NET_API_STATUS
+MyRxNetServerEnum (
+ IN LPTSTR UncServerName,
+ IN LPTSTR TransportName,
+ IN DWORD Level,
+ OUT LPBYTE *BufPtr,
+ IN DWORD PrefMaxSize,
+ OUT LPDWORD EntriesRead,
+ OUT LPDWORD TotalEntries,
+ IN DWORD ServerType,
+ IN LPTSTR Domain OPTIONAL,
+ IN OUT LPDWORD Resume_Handle OPTIONAL
+ )
+{
+ DWORD dwStartTime;
+ DWORD dwEndTime;
+ NET_API_STATUS dwStatus;
+
+ dwStartTime = GetTickCount();
+
+ dwStatus = RxNetServerEnum ( UncServerName,
+ TransportName,
+ Level,
+ BufPtr,
+ PrefMaxSize,
+ EntriesRead,
+ TotalEntries,
+ ServerType,
+ Domain,
+ Resume_Handle );
+
+ dwEndTime = GetTickCount();
+
+ if (dwStatus != NERR_Success)
+ {
+ fprintf (pLOGFILE, "RxNetServerEnum failed after %d milliseconds on %s.\n",
+ dwEndTime-dwStartTime,
+ toansi (UncServerName));
+ }
+
+ return(dwStatus);
+}
+
+BOOL IsOurMachine (LPTSTR lpServer)
+{
+ return(!lstrcmp (lpServer, L"CRACKERJACK")||
+ !lstrcmp (lpServer, L"BONKERS")||
+ !lstrcmp (lpServer, L"ORVILLE")||
+ !lstrcmp (lpServer, L"RASTAMAN")||
+ !lstrcmp (lpServer, L"KERNEL")||
+ !lstrcmp (lpServer, L"PHOENIX")||
+ !lstrcmp (lpServer, L"NTABROAD")||
+ !lstrcmp (lpServer, L"RAIDERNT")||
+ !lstrcmp (lpServer, L"CLIFFV4"));
+}
+
+
+VOID
+SendMagicBullet(
+ VOID
+ )
+{
+ HANDLE MsHandle;
+ DWORD BytesWritten;
+
+ MsHandle = CreateFile(L"\\\\MagicBullet\\Mailslot\\Foobar",
+ GENERIC_WRITE,
+ 0,
+ NULL,
+ OPEN_ALWAYS,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL
+ );
+
+ if (MsHandle == INVALID_HANDLE_VALUE) {
+ printf("Unable to open mailslot: %ld\n", GetLastError());
+ return;
+ }
+
+ if (!WriteFile(MsHandle, &MsHandle, sizeof(MsHandle), &BytesWritten, NULL)) {
+ printf("Unable to write to mailslot: %ld\n", GetLastError());
+ }
+
+ CloseHandle(MsHandle);
+}
diff --git a/private/net/svcdlls/browser/bowser.acf b/private/net/svcdlls/browser/bowser.acf
new file mode 100644
index 000000000..4608dbf43
--- /dev/null
+++ b/private/net/svcdlls/browser/bowser.acf
@@ -0,0 +1,11 @@
+[ implicit_handle( handle_t browser_bhandle )]
+
+interface browser
+
+{
+
+typedef [allocate(all_nodes)] LPSERVER_INFO_100;
+typedef [allocate(all_nodes)] LPSERVER_INFO_101;
+typedef [allocate(all_nodes)] PBROWSER_EMULATED_DOMAIN;
+
+}
diff --git a/private/net/svcdlls/browser/bowser.idl b/private/net/svcdlls/browser/bowser.idl
new file mode 100644
index 000000000..e917e7afa
--- /dev/null
+++ b/private/net/svcdlls/browser/bowser.idl
@@ -0,0 +1,222 @@
+/*++
+
+Copyright (c) 1990 Microsoft Corporation
+
+Module Name:
+
+ bowser.idl
+
+Abstract:
+
+ Contains the Netr (Net Remote) RPC interface specification for the APIs
+ associated with the Browser service. This consists of the NetServerEnum
+ API
+
+ Also contains the RPC specific data structures for these API.
+
+Author:
+
+ Rita Wong (ritaw) 06-May-1991
+ Larry Osterman (larryo) 23-Mar-1992
+
+Environment:
+
+ User Mode - Win32 - MIDL
+
+Revision History:
+
+--*/
+
+//
+// Interface Attributes
+//
+
+[
+ uuid(6BFFD098-A112-3610-9833-012892020162),
+ version(0.0),
+#ifdef __midl
+ ms_union,
+#endif // __midl
+ pointer_default(unique)
+]
+
+
+//
+// Interface Keyword
+//
+
+interface browser
+
+
+//
+// Interface Body
+//
+
+{
+
+import "imports.idl";
+#include <lmcons.h>
+
+//
+// BUGBUG - take this definition out when midl understands LPWSTR etc
+//
+
+#ifdef UNICODE
+#define LPTSTR wchar_t*
+#endif
+
+//
+// ---------------------------------------------------------------//
+//
+
+
+typedef [handle] LPTSTR BROWSER_IMPERSONATE_HANDLE;
+
+typedef [handle] LPTSTR BROWSER_IDENTIFY_HANDLE;
+
+
+//
+// I_BrowserrServerEnum
+//
+
+typedef struct _SERVER_INFO_100_CONTAINER {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] LPSERVER_INFO_100 Buffer;
+} SERVER_INFO_100_CONTAINER, *PSERVER_INFO_100_CONTAINER,
+*LPSERVER_INFO_100_CONTAINER;
+
+
+typedef struct _SERVER_INFO_101_CONTAINER {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] LPSERVER_INFO_101 Buffer;
+} SERVER_INFO_101_CONTAINER, *PSERVER_INFO_101_CONTAINER,
+*LPSERVER_INFO_101_CONTAINER;
+
+typedef struct _BROWSER_STATISTICS_100_CONTAINER {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] PBROWSER_STATISTICS_100 Buffer;
+} BROWSER_STATISTICS_100_CONTAINER, *PBROWSER_STATISTICS_100_CONTAINER;
+
+typedef struct _BROWSER_STATISTICS_101_CONTAINER {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] PBROWSER_STATISTICS_101 Buffer;
+} BROWSER_STATISTICS_101_CONTAINER, *PBROWSER_STATISTICS_101_CONTAINER;
+
+typedef struct _BROWSER_EMULATED_DOMAIN_CONTAINER {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] PBROWSER_EMULATED_DOMAIN Buffer;
+} BROWSER_EMULATED_DOMAIN_CONTAINER, *PBROWSER_EMULATED_DOMAIN_CONTAINER;
+
+typedef struct _SERVER_ENUM_STRUCT {
+ DWORD Level;
+ [switch_is(Level)] union _SERVER_ENUM_UNION {
+ [case(100)]
+ LPSERVER_INFO_100_CONTAINER Level100;
+ [case(101)]
+ LPSERVER_INFO_101_CONTAINER Level101;
+ [default]
+ ;
+ } ServerInfo;
+}SERVER_ENUM_STRUCT, *PSERVER_ENUM_STRUCT, *LPSERVER_ENUM_STRUCT;
+
+typedef struct _BROWSER_STATISTICS_STRUCT {
+ DWORD Level;
+ [switch_is(Level)] union _BROWSER_STATISTICS_UNION {
+ [case(100)]
+ PBROWSER_STATISTICS_100_CONTAINER Level100;
+ [case(101)]
+ PBROWSER_STATISTICS_101_CONTAINER Level101;
+ [default]
+ ;
+ } Statistics;
+}BROWSER_STATISTICS_STRUCT, *PBROWSER_STATISTICS_STRUCT, *LPBROWSER_STATISTICS_STRUCT;
+
+NET_API_STATUS
+I_BrowserrServerEnum(
+ [in,string,unique] BROWSER_IDENTIFY_HANDLE ServerName,
+ [in,string,unique] LPTSTR TransportName,
+ [in,string,unique] LPTSTR ClientName,
+ [in,out] LPSERVER_ENUM_STRUCT InfoStruct,
+ [in] DWORD PreferedMaximumLength,
+ [out] LPDWORD TotalEntries,
+ [in] DWORD ServerType,
+ [in,string,unique] LPTSTR Domain,
+ [in,out,unique] LPDWORD ResumeHandle
+ );
+
+NET_API_STATUS
+I_BrowserrDebugCall(
+ [in,string,unique] BROWSER_IDENTIFY_HANDLE ServerName,
+ [in] DWORD DebugFunction,
+ [in] DWORD OptionalValue
+ );
+
+NET_API_STATUS
+I_BrowserrQueryOtherDomains(
+ [in,string,unique] BROWSER_IDENTIFY_HANDLE ServerName,
+ [in,out] LPSERVER_ENUM_STRUCT InfoStruct,
+ [out] LPDWORD TotalEntries
+ );
+
+NET_API_STATUS
+I_BrowserrResetNetlogonState(
+ [in,string,unique] BROWSER_IDENTIFY_HANDLE ServerName
+ );
+
+NET_API_STATUS
+I_BrowserrDebugTrace(
+ [in,string,unique] BROWSER_IDENTIFY_HANDLE ServerName,
+ [in, string] LPSTR TraceString
+ );
+
+NET_API_STATUS
+I_BrowserrQueryStatistics (
+ [in, string, unique] BROWSER_IDENTIFY_HANDLE servername OPTIONAL,
+ [out] LPBROWSER_STATISTICS *statistics
+ );
+
+NET_API_STATUS
+I_BrowserrResetStatistics (
+ [in, string, unique] BROWSER_IDENTIFY_HANDLE servername OPTIONAL
+ );
+
+NET_API_STATUS
+NetrBrowserStatisticsClear (
+ [in, string, unique] BROWSER_IDENTIFY_HANDLE servername OPTIONAL
+ );
+
+NET_API_STATUS
+NetrBrowserStatisticsGet (
+ [in, string, unique] BROWSER_IDENTIFY_HANDLE servername OPTIONAL,
+ [in] DWORD Level,
+ [in, out] LPBROWSER_STATISTICS_STRUCT StatisticsStruct
+ );
+
+NET_API_STATUS
+I_BrowserrSetNetlogonState(
+ [in, string, unique] BROWSER_IDENTIFY_HANDLE ServerName OPTIONAL,
+ [in, string] LPTSTR DomainName,
+ [in, string, unique] LPTSTR EmulatedComputerName OPTIONAL,
+ [in] DWORD Role
+ );
+
+NET_API_STATUS
+I_BrowserrQueryEmulatedDomains (
+ [in, string, unique] BROWSER_IDENTIFY_HANDLE ServerName OPTIONAL,
+ [in,out] PBROWSER_EMULATED_DOMAIN_CONTAINER EmulatedDomains
+ );
+
+NET_API_STATUS
+I_BrowserrServerEnumEx(
+ [in,string,unique] BROWSER_IDENTIFY_HANDLE ServerName,
+ [in,string,unique] LPTSTR TransportName,
+ [in,string,unique] LPTSTR ClientName,
+ [in,out] LPSERVER_ENUM_STRUCT InfoStruct,
+ [in] DWORD PreferedMaximumLength,
+ [out] LPDWORD TotalEntries,
+ [in] DWORD ServerType,
+ [in,string,unique] LPTSTR Domain,
+ [in,string,unique] LPTSTR FirstNameToReturn
+ );
+
+}
diff --git a/private/net/svcdlls/browser/bperf/bperf.c b/private/net/svcdlls/browser/bperf/bperf.c
new file mode 100644
index 000000000..995440be2
--- /dev/null
+++ b/private/net/svcdlls/browser/bperf/bperf.c
@@ -0,0 +1,359 @@
+/*++
+
+Copyright (c) 1993 Micorsoft Corporation
+
+Module Name:
+
+ bchk.c
+
+Abstract:
+
+ Browser Monitor main program.
+
+Author:
+
+ Congpa You (CongpaY) 10-Feb-1993
+
+Revision History:
+
+--*/
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <sys\types.h>
+#include <sys\stat.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <windows.h>
+#include <lm.h>
+#include <ntddbrow.h>
+#include <brcommon.h> // svcdll\browser
+#include <rap.h>
+#include <rxserver.h>
+#include <winerror.h> // inc
+#include <rpcutil.h> // for MIDL_user_free. net\inc
+
+
+void PerfDomain ( INT i,
+ TCHAR szDomainName[],
+ DWORD dwServerType);
+
+void PerfTransport (INT i,
+ TCHAR szDomainName[],
+ DWORD dwServerType);
+
+void InitCount ();
+
+void CalcCount (DWORD dwVal,
+ DWORD dwEndTime);
+
+void PrintResult();
+
+NET_API_STATUS GetBrowserTransportList (OUT PLMDR_TRANSPORT_LIST *TransportList);
+
+LPSTR toansi(LPTSTR lpUnicode);
+
+// Global data
+INT nCount[16];
+
+void _CRTAPI1 main (INT argc, CHAR * argv[])
+{
+ INT i;
+ TCHAR szDomainName[DNLEN];
+ DWORD dwServerType;
+
+ // Get value from command line.
+ if (argc < 3)
+ {
+ printf("bperf -domain -n -m\n -n the number of times to run.\n -m server type, optional\n");
+ return;
+ }
+
+ i = MultiByteToWideChar (CP_ACP,
+ 0,
+ argv[1],
+ strlen(argv[1]),
+ szDomainName,
+ DNLEN);
+
+ szDomainName[i] = 0;
+
+ i = atoi(argv[2]);
+
+ if (argc == 4)
+ {
+ dwServerType = atoi(argv[3]);
+
+ if ((dwServerType < 1) || (dwServerType > 0xFFFFFFFF))
+ {
+ printf("wrong value for SV_TYPE.");
+ return;
+ }
+ }
+ else
+ dwServerType = SV_TYPE_ALL;
+
+ PerfDomain (i, szDomainName, dwServerType);
+
+ PerfTransport (i, szDomainName, dwServerType);
+}
+
+
+void PerfDomain ( INT i,
+ TCHAR szDomainName[],
+ DWORD dwServerType)
+{
+ DWORD dwVal; // The value returned from function calls.
+ DWORD dwStartTime;
+ DWORD dwEndTime;
+ DWORD dwEntriesRead;
+ DWORD dwTotalEntries;
+ LPVOID lpBrowserList;
+
+ InitCount ();
+
+ // run NetServerEnum on domain and RxNetServerEnum on transport.
+ while (i-- > 0)
+ {
+ dwStartTime = GetTickCount();
+
+ dwVal = NetServerEnum (NULL,
+ 101,
+ (LPBYTE *) &lpBrowserList,
+ 0xffffffff,
+ &dwEntriesRead,
+ &dwTotalEntries,
+ dwServerType,
+ szDomainName,
+ NULL);
+
+ dwEndTime = GetTickCount() - dwStartTime;
+
+ CalcCount(dwVal, dwEndTime);
+
+ MIDL_user_free (lpBrowserList);
+ }
+
+ PrintResult();
+}
+
+
+
+void PerfTransport (INT i,
+ TCHAR szDomainName[],
+ DWORD dwServerType)
+{
+ INT j;
+ DWORD dwVal;
+ DWORD dwRxVal;
+ DWORD dwStartTime;
+ DWORD dwEndTime;
+ DWORD dwEntriesRead;
+ DWORD dwTotalEntries;
+ LPVOID lpBrowserList;
+ PWSTR * BrowserList = NULL;
+ ULONG BrowserListLength = 0;
+ PLMDR_TRANSPORT_LIST TransportList = NULL;
+ PLMDR_TRANSPORT_LIST TransportEntry = NULL;
+
+ // Find all transports that we have.
+ dwVal = GetBrowserTransportList (&TransportList);
+ if (dwVal != NERR_Success)
+ {
+ if (TransportList != NULL)
+ {
+ MIDL_user_free (TransportList);
+ }
+ printf("GetBrowserTansportList failed. Error %d\n", dwVal);
+ return;
+ }
+
+ TransportEntry = TransportList;
+
+ // Enumerate on the transports.
+ while (TransportEntry != NULL)
+ {
+ UNICODE_STRING TransportName;
+
+ TransportName.Buffer = TransportEntry->TransportName;
+ TransportName.Length = (USHORT) TransportEntry->TransportNameLength;
+ TransportName.MaximumLength = (USHORT) TransportEntry->TransportNameLength;
+
+ InitCount();
+
+ j = i;
+
+ while (j-- > 0)
+ {
+ dwVal = GetBrowserServerList (&TransportName,
+ szDomainName,
+ &BrowserList,
+ &BrowserListLength,
+ TRUE);
+
+ if (dwVal != NERR_Success) // Type 1 error occured.
+ {
+ printf("GetBrowserServerList failed. Error %d\n", dwVal);
+ if (BrowserList != NULL)
+ {
+ MIDL_user_free (BrowserList);
+ }
+ break;
+ }
+
+ dwStartTime = GetTickCount();
+
+ dwRxVal = RxNetServerEnum (BrowserList[0],
+ TransportName.Buffer,
+ 101,
+ (LPBYTE *) &lpBrowserList,
+ 0xffffffff,
+ &dwEntriesRead,
+ &dwTotalEntries,
+ dwServerType,
+ szDomainName,
+ NULL);
+
+ dwEndTime = GetTickCount() - dwStartTime;
+
+ CalcCount (dwRxVal, dwEndTime);
+
+ MIDL_user_free (lpBrowserList);
+ }
+
+ printf("\nTransport = %s\n", toansi(TransportName.Buffer));
+ if (dwVal == NERR_Success)
+ {
+ PrintResult();
+ }
+ else
+ {
+ printf("GetBrowserServerList failed. Error %d\n", dwVal);
+ }
+
+ if (TransportEntry->NextEntryOffset == 0)
+ TransportEntry = NULL;
+ else
+ {
+ TransportEntry = (PLMDR_TRANSPORT_LIST) ((PCHAR) TransportEntry
+ +TransportEntry->NextEntryOffset);
+ }
+
+ } // End of the enumeration of transports.
+
+ // Free memory.
+ MIDL_user_free (TransportList);
+}
+
+
+void InitCount ()
+{
+ INT i;
+ for (i = 0; i < 16; i++)
+ {
+ nCount[i] = 0;
+ }
+}
+
+
+void CalcCount (DWORD dwVal,
+ DWORD dwEndTime)
+{
+ if (dwVal != NERR_Success)
+ {
+ nCount[15]++;
+ }
+ else if (dwEndTime < 1000)
+ {
+ nCount[dwEndTime/100]++;
+ }
+ else if (dwEndTime > 5000)
+ {
+ nCount[14]++;
+ }
+ else
+ {
+ nCount[dwEndTime/1000 + 9]++;
+ }
+}
+
+
+void PrintResult()
+{
+ INT i;
+ printf("< 100ms = %d\n", nCount[0]);
+
+ for (i = 1; i < 10; i++)
+ {
+ printf("%d-%dms = %d\n", i*100, i*100+100, nCount[i]);
+ }
+
+ for (i = 1; i < 5; i++)
+ {
+ printf("%d-%dsec = %d\n", i, i+1, nCount[i+9]);
+ }
+
+ printf("> 5sec = %d\n", nCount[14]);
+ printf("error = %d\n", nCount[15]);
+}
+
+// Copied from ..\client\browstub.c.
+NET_API_STATUS GetBrowserTransportList (OUT PLMDR_TRANSPORT_LIST *TransportList)
+{
+
+ NET_API_STATUS Status;
+ HANDLE BrowserHandle;
+ LMDR_REQUEST_PACKET RequestPacket;
+
+ Status = OpenBrowser(&BrowserHandle);
+
+ if (Status != NERR_Success) {
+ return Status;
+ }
+
+ RequestPacket.Version = LMDR_REQUEST_PACKET_VERSION;
+
+ RequestPacket.Type = EnumerateXports;
+
+ RtlInitUnicodeString(&RequestPacket.TransportName, NULL);
+#ifdef _CAIRO_
+ RtlInitUnicodeString(&RequestPacket.EmulatedDomainName, NULL);
+#endif // _CAIRO_
+
+ Status = DeviceControlGetInfo(
+ BrowserHandle,
+ IOCTL_LMDR_ENUMERATE_TRANSPORTS,
+ &RequestPacket,
+ sizeof(RequestPacket),
+ (PVOID *)TransportList,
+ 0xffffffff,
+ 4096,
+ NULL);
+
+ NtClose(BrowserHandle);
+
+ return Status;
+}
+
+// Convert an unicode string to ansi string.
+LPSTR toansi(LPTSTR lpUnicode)
+{
+ static CHAR lpAnsi[128];
+ BOOL fDummy;
+ INT i;
+
+ i = WideCharToMultiByte (CP_ACP,
+ 0,
+ lpUnicode,
+ lstrlen(lpUnicode),
+ lpAnsi,
+ 128,
+ NULL,
+ &fDummy);
+
+ lpAnsi[i] = 0;
+
+ return(lpAnsi);
+}
diff --git a/private/net/svcdlls/browser/bperf/makefile b/private/net/svcdlls/browser/bperf/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/net/svcdlls/browser/bperf/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/net/svcdlls/browser/bperf/sources b/private/net/svcdlls/browser/bperf/sources
new file mode 100644
index 000000000..173f4c1b9
--- /dev/null
+++ b/private/net/svcdlls/browser/bperf/sources
@@ -0,0 +1,49 @@
+!IF 0
+
+Copyright (c) 1989-1992 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+
+Author:
+
+ Steve Wood (stevewo) 12-Apr-1990
+ Congpa You (congpay) 04-Feb-1993
+
+NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl
+
+!ENDIF
+
+MAJORCOMP=windows
+MINORCOMP=bperf
+
+TARGETNAME=bperf
+TARGETPATH=obj
+TARGETTYPE=LIBRARY
+
+INCLUDES=..\..\..;..\..\..\inc;..\..\..\..\inc;..;
+
+
+!IFNDEF DISABLE_NET_UNICODE
+UNICODE=1
+NET_C_DEFINES=-DUNICODE
+!ENDIF
+
+SOURCES=
+
+UMTYPE=console
+UMAPPL=bperf
+UMLIBS=\nt\public\sdk\lib\*\netapi32.lib \
+ ..\common\daytona\obj\*\brcommon.lib \
+ \nt\public\sdk\lib\*\rpcutil.lib \
+ \nt\public\sdk\lib\*\ntdll.lib \
+
diff --git a/private/net/svcdlls/browser/brcommon.h b/private/net/svcdlls/browser/brcommon.h
new file mode 100644
index 000000000..89cdf2f9c
--- /dev/null
+++ b/private/net/svcdlls/browser/brcommon.h
@@ -0,0 +1,335 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ brcommon.h
+
+Abstract:
+
+ Header for utility routines for the browser service.
+
+Author:
+
+ Larry Osterman (LarryO) 23-Mar-1992
+
+Environment:
+
+ User Mode - Win32
+
+Revision History:
+
+--*/
+
+#ifndef _BRCOMMON_
+#define _BRCOMMON_
+
+#include <winsvc.h>
+#include <svcs.h> // PSVCS_NET_BIOS_RESET
+
+#if DEVL
+//
+// Codes for I_BrowserDebugCall
+//
+
+#define BROWSER_DEBUG_BREAK_POINT 0
+#define BROWSER_DEBUG_DUMP_NETWORKS 1
+#define BROWSER_DEBUG_DUMP_SERVERS 2
+#define BROWSER_DEBUG_ENABLE_BROWSER 3
+#define BROWSER_DEBUG_SET_DEBUG 4
+#define BROWSER_DEBUG_CLEAR_DEBUG 5
+#define BROWSER_DEBUG_TICKLE 6
+#define BROWSER_DEBUG_ELECT 7
+#define BROWSER_DEBUG_GET_MASTER 8
+#define BROWSER_DEBUG_FIND_MASTER 9
+#define BROWSER_DEBUG_GET_BACKUP_LIST 10
+#define BROWSER_DEBUG_ANNOUNCE_MASTER 11
+#define BROWSER_DEBUG_ILLEGAL_DGRAM 12
+#define BROWSER_DEBUG_GET_OTHLIST 13
+#define BROWSER_DEBUG_ADD_MASTERNAME 14
+#define BROWSER_DEBUG_VIEW 15
+#define BROWSER_DEBUG_FORCE_ANNOUNCE 16
+#define BROWSER_DEBUG_LOCAL_BRLIST 17
+#define BROWSER_DEBUG_ANNOUNCE 18
+#define BROWSER_DEBUG_RPCLIST 19
+#define BROWSER_DEBUG_RPCCMP 20
+#define BROWSER_DEBUG_TRUNCATE_LOG 21
+#define BROWSER_DEBUG_STATISTICS 22
+#define BROWSER_DEBUG_BOWSERDEBUG 23
+#define BROWSER_DEBUG_POPULATE_SERVER 24
+#define BROWSER_DEBUG_POPULATE_DOMAIN 25
+#define BROWSER_DEBUG_LIST_WFW 26
+#define BROWSER_DEBUG_STATUS 27
+#define BROWSER_DEBUG_GETPDC 28
+#define BROWSER_DEBUG_ADD_DOMAINNAME 29
+#define BROWSER_DEBUG_GET_WINSSERVER 30
+#define BROWSER_DEBUG_GET_DOMAINLIST 31
+#define BROWSER_DEBUG_GET_NETBIOSNAMES 32
+#define BROWSER_DEBUG_SET_EMULATEDDOMAIN 33
+#define BROWSER_DEBUG_SET_EMULATEDDOMAINENUM 34
+#define BROWSER_DEBUG_ADD_ALTERNATE 35
+
+#ifdef _CAIRO_
+//
+// Debug trace level bits for turning on/off trace statements in the
+// browser service
+//
+
+#define BR_CRITICAL 0x00000001
+#define BR_INIT 0x00000002
+#define BR_UTIL 0x00000020
+#define BR_CONFIG 0x00000040
+#define BR_MAIN 0x00000080
+#define BR_BACKUP 0x00000400
+#define BR_MASTER 0x00000800
+#define BR_DOMAIN 0x00001000
+#define BR_NETWORK 0x00002000
+#define BR_COMMON 0x0000FFFF
+
+#define BR_TIMER 0x00010000
+#define BR_QUEUE 0x00020000
+#define BR_LOCKS 0x00040000
+#define BR_SERVER_ENUM 0x00100000
+
+#define BR_ALL 0xFFFFFFFF
+#else // _CAIRO_
+//
+// Debug trace level bits for turning on/off trace statements in the
+// browser service
+//
+
+#define BROWSER_DEBUG_SERVER_ENUM 0x00000010
+#define BROWSER_DEBUG_UTIL 0x00000020
+#define BROWSER_DEBUG_CONFIG 0x00000040
+#define BROWSER_DEBUG_MAIN 0x00000080
+#define BROWSER_DEBUG_LOGON 0x00000200
+#define BROWSER_DEBUG_BACKUP 0x00000400
+#define BROWSER_DEBUG_MASTER 0x00000800
+#define BROWSER_DEBUG_DOMAIN 0x00001000
+#define BROWSER_DEBUG_TIMER 0x00002000
+#define BROWSER_DEBUG_QUEUE 0x00004000
+#define BROWSER_DEBUG_LOCKS 0x00008000
+#define BROWSER_DEBUG_INIT 0x00010000
+#define BROWSER_DEBUG_ALL 0xFFFFFFFF
+#endif // _CAIRO_
+//#include <\nt\private\ntos\bowser\debug.h>
+
+NET_API_STATUS
+I_BrowserDebugCall (
+ IN LPTSTR servername OPTIONAL,
+ IN DWORD DebugCode,
+ IN DWORD OptionalValue
+ );
+
+#endif
+
+typedef struct _INTERIM_ELEMENT {
+ LIST_ENTRY NextElement;
+ ULONG Periodicity;
+ ULONG TimeLastSeen;
+ ULONG PlatformId;
+ ULONG MajorVersionNumber;
+ ULONG MinorVersionNumber;
+ ULONG Type;
+ TCHAR Name[CNLEN+1];
+ TCHAR Comment[MAXCOMMENTSZ+1];
+} INTERIM_ELEMENT, *PINTERIM_ELEMENT;
+
+struct _INTERIM_SERVER_LIST;
+
+typedef
+VOID
+(*PINTERIM_NEW_CALLBACK)(
+ IN struct _INTERIM_SERVER_LIST *InterimList,
+ IN PINTERIM_ELEMENT Element
+ );
+
+typedef
+VOID
+(*PINTERIM_EXISTING_CALLBACK)(
+ IN struct _INTERIM_SERVER_LIST *InterimList,
+ IN PINTERIM_ELEMENT Element
+ );
+
+
+typedef
+VOID
+(*PINTERIM_DELETE_CALLBACK)(
+ IN struct _INTERIM_SERVER_LIST *InterimList,
+ IN PINTERIM_ELEMENT Element
+ );
+
+typedef
+BOOLEAN
+(*PINTERIM_AGE_CALLBACK)(
+ IN struct _INTERIM_SERVER_LIST *InterimList,
+ IN PINTERIM_ELEMENT Element
+ );
+
+
+typedef struct _INTERIM_SERVER_LIST {
+// RTL_GENERIC_TABLE ServerTable;
+ LIST_ENTRY ServerList;
+ ULONG TotalBytesNeeded;
+ ULONG TotalEntries;
+ ULONG EntriesRead;
+ PINTERIM_NEW_CALLBACK NewElementCallback;
+ PINTERIM_EXISTING_CALLBACK ExistingElementCallback;
+ PINTERIM_DELETE_CALLBACK DeleteElementCallback;
+ PINTERIM_AGE_CALLBACK AgeElementCallback;
+} INTERIM_SERVER_LIST, *PINTERIM_SERVER_LIST;
+
+
+NET_API_STATUS
+DeviceControlGetInfo(
+ IN HANDLE FileHandle,
+ IN ULONG DeviceControlCode,
+ IN PVOID RequestPacket,
+ IN ULONG RequestPacketLength,
+ OUT LPVOID *OutputBuffer,
+ IN ULONG PreferedMaximumLength,
+ IN ULONG BufferHintSize,
+ OUT PULONG Information OPTIONAL
+ );
+
+ NET_API_STATUS
+BrDgReceiverIoControl(
+ IN HANDLE FileHandle,
+ IN ULONG DgReceiverControlCode,
+ IN PLMDR_REQUEST_PACKET Drp,
+ IN ULONG DrpSize,
+ IN PVOID SecondBuffer OPTIONAL,
+ IN ULONG SecondBufferLength,
+ OUT PULONG Information OPTIONAL
+ );
+
+NET_API_STATUS
+OpenBrowser(
+ OUT PHANDLE BrowserHandle
+ );
+
+NET_API_STATUS
+GetBrowserServerList(
+ IN PUNICODE_STRING TransportName,
+ IN LPCWSTR domain,
+ OUT LPWSTR *BrowserList[],
+ OUT PULONG BrowserListLength,
+ IN BOOLEAN ForceRescan
+ );
+
+NET_API_STATUS
+InitializeInterimServerList(
+ IN PINTERIM_SERVER_LIST InterimServerList,
+ IN PINTERIM_NEW_CALLBACK NewCallback,
+ IN PINTERIM_EXISTING_CALLBACK ExistingCallback,
+ IN PINTERIM_DELETE_CALLBACK DeleteElementCallback,
+ IN PINTERIM_AGE_CALLBACK AgeElementCallback
+ );
+
+NET_API_STATUS
+CopyInterimServerList(
+ IN PINTERIM_SERVER_LIST NewInterimServerList,
+ IN PINTERIM_SERVER_LIST OldInterimServerList
+ );
+
+
+
+NET_API_STATUS
+UninitializeInterimServerList(
+ IN PINTERIM_SERVER_LIST InterimServerList
+ );
+
+
+NET_API_STATUS
+InsertElementInterimServerList (
+ IN PINTERIM_SERVER_LIST InterimServerList,
+ IN PINTERIM_ELEMENT InterimElement,
+ IN ULONG Level,
+ IN PBOOLEAN NewElement OPTIONAL,
+ IN PINTERIM_ELEMENT *ActualElement OPTIONAL
+ );
+
+ULONG
+NumberInterimServerListElements(
+ IN PINTERIM_SERVER_LIST InterimServerList
+ );
+
+NET_API_STATUS
+AgeInterimServerList(
+ IN PINTERIM_SERVER_LIST InterimServerList
+ );
+
+
+NET_API_STATUS
+MergeServerList(
+ IN PINTERIM_SERVER_LIST InterimServerList,
+ IN ULONG level,
+ IN PVOID NewServerList,
+ IN ULONG NewEntriesRead,
+ IN ULONG NewTotalEntries
+ );
+
+PINTERIM_ELEMENT
+LookupInterimServerList(
+ IN PINTERIM_SERVER_LIST InterimServerList,
+ IN LPTSTR ServerNameToLookUp
+ );
+
+
+
+NET_API_STATUS
+PackServerList(
+ IN PINTERIM_SERVER_LIST InterimServerList,
+ IN ULONG Level,
+ IN ULONG ServerType,
+ IN ULONG PreferedDataLength,
+ OUT PVOID *bufptr,
+ OUT PULONG entriesread,
+ OUT PULONG totalentries,
+ IN LPCWSTR FirstNameToReturn
+ );
+
+VOID
+PrepareServerListForMerge(
+ IN PVOID ServerInfoList,
+ IN ULONG Level,
+ IN ULONG EntriesInList
+ );
+
+NET_API_STATUS
+CheckForService(
+ IN LPTSTR ServiceName,
+ OUT LPSERVICE_STATUS ServiceStatus OPTIONAL
+ );
+
+
+NET_API_STATUS
+BrGetLanaNumFromNetworkName(
+ IN LPWSTR TransportName,
+ OUT CCHAR *LanaNum
+ );
+
+NET_API_STATUS
+GetNetBiosMasterName(
+ IN LPWSTR NetworkName,
+ IN LPWSTR PrimaryDomain,
+ OUT LPWSTR MasterName,
+ IN PSVCS_NET_BIOS_RESET SvcsNetBiosReset OPTIONAL
+ );
+
+NET_API_STATUS
+SendDatagram(
+ IN HANDLE DgReceiverHandle,
+ IN PUNICODE_STRING Network,
+#ifdef _CAIRO_
+ IN PUNICODE_STRING EmulatedDomainName,
+#endif // _CAIRO_
+ IN PWSTR ResponseName,
+ IN DGRECEIVER_NAME_TYPE NameType,
+ IN PVOID Buffer,
+ IN ULONG BufferLength
+ );
+
+#endif // _BRCOMMON_
+
diff --git a/private/net/svcdlls/browser/brnames.h b/private/net/svcdlls/browser/brnames.h
new file mode 100644
index 000000000..5317fb063
--- /dev/null
+++ b/private/net/svcdlls/browser/brnames.h
@@ -0,0 +1,21 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ wsnames.h
+
+Abstract:
+
+ Private header file which defines the Workstation names.
+
+Author:
+
+ Rita Wong (ritaw) 06-May-1991
+
+Revision History:
+
+--*/
+
+#define BROWSER_INTERFACE_NAME TEXT("browser")
diff --git a/private/net/svcdlls/browser/browtest/browtest.cht b/private/net/svcdlls/browser/browtest/browtest.cht
new file mode 100644
index 000000000..d6ddb7849
--- /dev/null
+++ b/private/net/svcdlls/browser/browtest/browtest.cht
@@ -0,0 +1,120 @@
+ Browser Tester
+ ==============
+
+ The browser tester can be used for either
+ 1. Functional testing of the browser service.
+ 2. Stress testing of the browser service.
+
+ The functional or stress testing is done on a set of machines which are
+parts of 1 or more domains. The names of the set of machines are given through
+an input file (BROWTEST.INP). The machine from which this test is run should
+have administrative priveleges on all the (NT) machines specified in the input
+file.
+===============================================================================
+To Run:
+=======
+ I. Functional Test
+ ------------------
+ Files Needed:
+ a) BROWTEST.EXE - On one machine in the Primary domain.
+ b) BROWTEST.INP - On one machine in the Primary domain.
+ c) SHUTSVC.EXE - On the PDC of all other domains tested.
+
+ Steps:
+ ------
+ 1. Set up the test bed such that there is 1 or more domains.
+
+ +--------------------------------------------------------------+
+ | (PDC)(WINS) (PDC) (BDC) |
+ | D1 D1 D1 D2 D1 |
+ | AS3.5 NT3.5 CHICAGO AS3.5 AS3.5 |
+ | | | | | | |
+ | +------------+---------+--------+---- Router ---+ |
+ | | | | | |
+ | AS3.1 NT3.1 WFW311 AS3.5 |
+ | D1 D1 D1 D3 |
+ | (BDC) (PDC) |
+ | |
+ | D1 - Primary Domain, D2 - Domain2 D3 - Domain3 |
+ +--------------------------------------------------------------+
+ Given above is a generic configuration.
+ The test can be done with or without the router.
+ All of the interop machines need not be there in Domain1.
+ WINS can be anywhere.
+ If the router is present, the BDC on the other side should be a AS3.5.
+
+ 2. Make the BROWTEST.INP based on the configuration of the machines.
+ (Read the sample BROWTEST.INP for more details.)
+
+ 3. Copy ShutSvc.exe to the %SystemRoot% of all the PDC's of domains
+ tested, other than the Primary domain (ie. D2, D3).
+
+ 4. From the machine, from which the test is run, get access permissions
+ to all (NT) machines participating in the test.
+
+ 5. Start the test. BROWTEST without any switches will do.
+ (Wait a short while, to see if the test has access permissions)
+
+ The time the test takes to complete depends on the number of
+ protocols tested and the number of machines participating in
+ the test.
+
+ Results:
+ --------
+ Two output files are created. BROWTEST.LOG and BROWTEST.SUM.
+ BROWTEST.SUM has the summary of the test run. There should not be
+ any ERRORS in the summary. Detailed report on errors can be found in
+ BROWTEST.LOG.
+
+
+
+===============================================================================
+To Run:
+=======
+ II. Stress Test
+ ---------------
+
+ Files Needed:
+ a) BROWTEST.EXE - On one machine in the Primary domain.
+ b) BROWTEST.INP - On one machine in the Primary domain.
+
+ Steps:
+ ------
+ 1. Set up the test bed such that there is 1 or more domains.
+
+ +--------------------------------------------------------------+
+ | (PDC)(WINS) (PDC) (BDC) |
+ | D1 D1 D1 D2 D1 |
+ | AS3.5 NT3.5 CHICAGO AS3.5 AS3.5 |
+ | | | | | | |
+ | +------------+---------+--------+---- Router ---+ |
+ | | | | | |
+ | AS3.1 NT3.1 WFW311 AS3.5 |
+ | D1 D1 D1 D3 |
+ | (BDC) (PDC) |
+ | |
+ | D1 - Primary Domain, D2 - Domain2 D3 - Domain3 |
+ +--------------------------------------------------------------+
+ Given above is a generic configuration.
+ The test can be done with or without the router.
+ All of the interop machines need not be there in Domain1.
+ WINS can be anywhere.
+ If the router is present, the BDC on the other side should be a AS3.5.
+
+ 2. Make the BROWTEST.INP based on the configuration of the machines.
+ (Read the sample BROWTEST.INP for more details.)
+
+ 3. From the machine, from which the test is run, get access permissions
+ to all (NT) machines participating in the test.
+
+ 4. Start the test. BROWTEST /StressT:60 will stress for 60 minutes.
+ (Wait a short while, to see if the test has access permissions)
+
+
+ Results:
+ --------
+ Make sure none of the machines crashes in the test. Generally for
+ crashing a machine it takes 7-8hrs of stressing. So run the test
+ overnight.
+ Check the lines printed against "View Thread:". Make sure the SUCCESS
+ line is not all zero's.
diff --git a/private/net/svcdlls/browser/browtest/dirs b/private/net/svcdlls/browser/browtest/dirs
new file mode 100644
index 000000000..d4f636069
--- /dev/null
+++ b/private/net/svcdlls/browser/browtest/dirs
@@ -0,0 +1,26 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ dirs.
+
+Abstract:
+
+ This file specifies the subdirectories of the current directory that
+ contain component makefiles.
+
+
+Author:
+
+ Steve Wood (stevewo) 17-Apr-1990
+
+NOTE: Commented description of this file is in \nt\bak\bin\dirs.tpl
+
+!ENDIF
+
+
+DIRS=shutsvc \
+ src
+
diff --git a/private/net/svcdlls/browser/browtest/src/browfunc.c b/private/net/svcdlls/browser/browtest/src/browfunc.c
new file mode 100644
index 000000000..9fdd03c87
--- /dev/null
+++ b/private/net/svcdlls/browser/browtest/src/browfunc.c
@@ -0,0 +1,564 @@
+
+#ifndef UNICODE
+#define UNICODE
+#endif
+
+#include "browfunc.h"
+
+CHAR WCtoPrintfBuf[256];
+
+#define SPACES " "
+#define BUFF_SIZE 128
+
+#define ClearNcb( PNCB ) { \
+ RtlZeroMemory( PNCB , sizeof (NCB) ); \
+ RtlCopyMemory( (PNCB)->ncb_name, SPACES, sizeof(SPACES)-1 );\
+ RtlCopyMemory( (PNCB)->ncb_callname, SPACES, sizeof(SPACES)-1 );\
+ }
+
+
+NET_API_STATUS
+ClearNbtNameTableCache(UNICODE_STRING usDevice)
+{
+HANDLE hNbt = (HANDLE)-1;
+CHAR cBuffer;
+CHAR pScope[BUFF_SIZE];
+TCHAR pDeviceName[BUFF_SIZE];
+DWORD Status;
+
+// Status = ReadRegistry((CHAR*)pDeviceName, pScope);
+// if(Status != ERROR_SUCCESS)
+// return (!NERR_Success);
+
+ Status = OpenNbt(usDevice, &hNbt);
+ if(!NT_SUCCESS(Status))
+ return (!NERR_Success);
+
+ Status = DeviceIoCtrl(hNbt, &cBuffer, 1, IOCTL_NETBT_PURGE_CACHE, NULL, 0);
+ if(!NT_SUCCESS(Status))
+ return (!NERR_Success);
+
+ NtClose(hNbt);
+
+return NERR_Success;
+}
+
+
+
+
+NET_API_STATUS
+Elect(
+ IN UNICODE_STRING Transport,
+ IN LPTSTR Domain
+ )
+{
+ REQUEST_ELECTION ElectionRequest;
+ HANDLE BrowserHandle;
+ NET_API_STATUS Status;
+
+ OpenBrowser(&BrowserHandle);
+
+ ElectionRequest.Type = Election;
+
+ ElectionRequest.ElectionRequest.Version = 0;
+ ElectionRequest.ElectionRequest.Criteria = 0;
+ ElectionRequest.ElectionRequest.TimeUp = 0;
+ ElectionRequest.ElectionRequest.MustBeZero = 0;
+ ElectionRequest.ElectionRequest.ServerName[0] = '\0';
+
+ Status = SendDatagram(BrowserHandle, &Transport,
+ Domain,
+ BrowserElection,
+ &ElectionRequest,
+ sizeof(ElectionRequest));
+ CloseHandle(BrowserHandle);
+
+ return Status;
+}
+
+
+VOID
+ForceAnnounce(
+ IN UNICODE_STRING Transport,
+ IN LPTSTR Domain
+ )
+{
+ REQUEST_ANNOUNCE_PACKET RequestAnnounce;
+ HANDLE BrowserHandle;
+ ULONG NameSize = sizeof(RequestAnnounce.RequestAnnouncement.Reply);
+
+
+ OpenBrowser(&BrowserHandle);
+
+ RequestAnnounce.Type = AnnouncementRequest;
+
+ RequestAnnounce.RequestAnnouncement.Flags = 0;
+
+ GetComputerNameA(RequestAnnounce.RequestAnnouncement.Reply, &NameSize);
+
+ SendDatagram(BrowserHandle, &Transport,
+ Domain,
+ BrowserElection,
+ &RequestAnnounce,
+ FIELD_OFFSET(REQUEST_ANNOUNCE_PACKET, RequestAnnouncement.Reply) + NameSize + sizeof(CHAR));
+ CloseHandle(BrowserHandle);
+
+}
+
+
+NET_API_STATUS
+GetBList(
+ IN UNICODE_STRING TransportName,
+ IN TCHAR * Domain,
+ IN BOOLEAN ForceRescan,
+ OUT ULONG * NumBackUps,
+ OUT TCHAR wcBackUpBrowsers[MAXBACKUPS][CNLEN+3]
+ )
+{
+ NET_API_STATUS Status;
+ UNICODE_STRING UTransportName;
+ ANSI_STRING ATransportName;
+ PWSTR *BrowserList;
+ ULONG i;
+
+ Status = GetBrowserServerList(&TransportName, Domain,
+ &BrowserList,
+ NumBackUps,
+ ForceRescan);
+
+ if (Status != NERR_Success)
+ return Status;
+
+ for (i = 0; (i < (*NumBackUps)) && (i < (ULONG)MAXBACKUPS); i ++ ) {
+// printf("Browser: %s\n", UnicodeToPrintfString(BrowserList[i]));
+ lstrcpy(wcBackUpBrowsers[i], BrowserList[i]);
+ }
+
+ NetApiBufferFree(BrowserList);
+
+return Status;
+}
+
+
+NET_API_STATUS
+GetBrowserTransportList(
+ OUT PLMDR_TRANSPORT_LIST *TransportList
+ )
+
+/*++
+
+Routine Description:
+
+ This routine returns the list of transports bound into the browser.
+
+Arguments:
+
+ OUT PLMDR_TRANSPORT_LIST *TransportList - Transport list to return.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+
+{
+
+ NET_API_STATUS Status;
+ HANDLE BrowserHandle;
+ LMDR_REQUEST_PACKET RequestPacket;
+
+ Status = OpenBrowser(&BrowserHandle);
+
+ if (Status != NERR_Success) {
+ return Status;
+ }
+
+ RequestPacket.Version = LMDR_REQUEST_PACKET_VERSION;
+
+ RequestPacket.Type = EnumerateXports;
+
+ RtlInitUnicodeString(&RequestPacket.TransportName, NULL);
+#ifdef _CAIRO_
+ RtlInitUnicodeString(&RequestPacket.EmulatedDomainName, NULL);
+#endif // _CAIRO_
+
+ Status = DeviceControlGetInfo(
+ BrowserHandle,
+ IOCTL_LMDR_ENUMERATE_TRANSPORTS,
+ &RequestPacket,
+ sizeof(RequestPacket),
+ (PVOID *)TransportList,
+ 0xffffffff,
+ 4096,
+ NULL);
+
+ NtClose(BrowserHandle);
+
+ return Status;
+}
+
+
+NET_API_STATUS
+GetNetBiosPdcName(
+ IN LPWSTR NetworkName,
+ IN LPWSTR PrimaryDomain,
+ OUT LPWSTR MasterName
+ )
+{
+ CCHAR LanaNum;
+ NCB AStatNcb;
+ struct {
+ ADAPTER_STATUS AdapterInfo;
+ NAME_BUFFER Names[32];
+ } AdapterStatus;
+ WORD i;
+ CHAR remoteName[CNLEN+1];
+ NET_API_STATUS Status;
+ BOOL UsedDefaultChar;
+
+ Status = BrGetLanaNumFromNetworkName(NetworkName, &LanaNum);
+
+ if (Status != NERR_Success) {
+ return Status;
+ }
+
+ ClearNcb(&AStatNcb);
+
+ AStatNcb.ncb_command = NCBRESET;
+ AStatNcb.ncb_lsn = 0; // Request resources
+ AStatNcb.ncb_lana_num = LanaNum;
+ AStatNcb.ncb_callname[0] = 0; // 16 sessions
+ AStatNcb.ncb_callname[1] = 0; // 16 commands
+ AStatNcb.ncb_callname[2] = 0; // 8 names
+ AStatNcb.ncb_callname[3] = 0; // Don't want the reserved address
+ Netbios( &AStatNcb );
+
+ ClearNcb( &AStatNcb );
+
+ if (WideCharToMultiByte( CP_OEMCP, 0,
+ PrimaryDomain,
+ -1,
+ remoteName,
+ sizeof(remoteName),
+ "?",
+ &UsedDefaultChar) == 0) {
+ return GetLastError();
+ }
+
+ //
+ // Uppercase the remote name.
+ //
+
+ _strupr(remoteName);
+
+ AStatNcb.ncb_command = NCBASTAT;
+
+ RtlCopyMemory( AStatNcb.ncb_callname, remoteName, strlen(remoteName));
+
+ AStatNcb.ncb_callname[15] = PRIMARY_CONTROLLER_SIGNATURE;
+
+ AStatNcb.ncb_lana_num = LanaNum;
+ AStatNcb.ncb_length = sizeof( AdapterStatus );
+ AStatNcb.ncb_buffer = (CHAR *)&AdapterStatus;
+ Netbios( &AStatNcb );
+
+ if ( AStatNcb.ncb_retcode == NRC_GOODRET ) {
+ for ( i=0 ; i < AdapterStatus.AdapterInfo.name_count ; i++ ) {
+ if (AdapterStatus.Names[i].name[NCBNAMSZ-1] == SERVER_SIGNATURE) {
+// LPWSTR SpacePointer;
+ DWORD j;
+
+ if (MultiByteToWideChar(CP_OEMCP,
+ 0,
+ AdapterStatus.Names[i].name,
+ CNLEN,
+ MasterName,
+ CNLEN) == 0) {
+ return(GetLastError());
+ }
+
+ for (j = CNLEN - 1; j ; j -= 1) {
+ if (MasterName[j] != L' ') {
+ MasterName[j+1] = UNICODE_NULL;
+ break;
+ }
+ }
+
+ return NERR_Success;
+ }
+ }
+ } else {
+ return AStatNcb.ncb_retcode;
+ }
+}
+
+
+//
+// map an error number to its error message string. note, uses static,
+// not reentrant.
+//
+CHAR *
+get_error_text(DWORD dwErr)
+{
+ static CHAR text[512] ;
+ WORD err ;
+ WORD msglen ;
+
+ memset(text,0, sizeof(text));
+
+ //
+ // get error message
+ //
+ err = DosGetMessage(NULL,
+ 0,
+ text,
+ sizeof(text),
+ (WORD)dwErr,
+ (dwErr<NERR_BASE)||(dwErr>MAX_LANMAN_MESSAGE_ID) ?
+ TEXT("BASE"):TEXT("NETMSG"),
+ &msglen) ;
+
+ if (err != NERR_Success)
+ {
+ // use number instead. if looks like NTSTATUS then use hex.
+ sprintf(text, (dwErr & 0xC0000000)?"(%lx)":"(%ld)", dwErr) ;
+ }
+
+ return text ;
+}
+
+
+NTSTATUS
+ReadRegistry(
+ OUT PUCHAR pDeviceName,
+ OUT PUCHAR pScope
+ )
+
+/*++
+
+Routine Description:
+
+ This procedure reads the registry to get the name of NBT to bind to.
+ That name is stored in the Linkage/Exports section under the Netbt key.
+
+Arguments:
+
+
+Return Value:
+
+ 0 if successful, -1 otherwise.
+
+--*/
+
+{
+ PWCHAR SubKeyParms=L"system\\currentcontrolset\\services\\netbt\\parameters";
+ PWCHAR SubKeyLinkage=L"system\\currentcontrolset\\services\\netbt\\linkage";
+ HKEY Key;
+ PWCHAR Scope=L"ScopeId";
+ PWCHAR Linkage=L"Export";
+ LONG Type;
+ LONG status;
+ ULONG size;
+
+ size = BUFF_SIZE;
+ status = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
+ SubKeyLinkage,
+ 0,
+ KEY_READ,
+ &Key);
+
+ if (status == ERROR_SUCCESS){
+ // now read the linkage values
+ status = RegQueryValueEx(Key,
+ Linkage,
+ NULL,
+ &Type,
+ pDeviceName,
+ &size);
+
+ if (status != ERROR_SUCCESS) {
+
+ RegCloseKey(Key);
+ return(STATUS_UNSUCCESSFUL);
+ }
+
+ RegCloseKey(Key);
+
+ } else {
+ return(STATUS_UNSUCCESSFUL);
+ }
+
+
+ size = BUFF_SIZE;
+ status = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
+ SubKeyParms,
+ 0,
+ KEY_READ,
+ &Key);
+
+ if (status == ERROR_SUCCESS){
+ // now read the linkage values
+ status = RegQueryValueEx(Key,
+ Scope,
+ NULL,
+ &Type,
+ pScope,
+ &size);
+
+ if (status != ERROR_SUCCESS) {
+ RegCloseKey(Key);
+ return(STATUS_UNSUCCESSFUL);
+ }
+
+ RegCloseKey(Key);
+
+ } else
+ return(STATUS_UNSUCCESSFUL);
+
+ return(STATUS_SUCCESS);
+}
+
+
+NTSTATUS
+OpenNbt(
+ IN UNICODE_STRING usDeviceName,
+ OUT PHANDLE pHandle
+ )
+
+/*++
+
+Routine Description:
+
+ This function opens a stream.
+
+Arguments:
+
+ path - path to the STREAMS driver
+ oflag - currently ignored. In the future, O_NONBLOCK will be
+ relevant.
+ ignored - not used
+
+Return Value:
+
+ An NT handle for the stream, or INVALID_HANDLE_VALUE if unsuccessful.
+
+--*/
+
+{
+ HANDLE StreamHandle;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ IO_STATUS_BLOCK IoStatusBlock;
+ NTSTATUS status;
+
+
+ InitializeObjectAttributes(
+ &ObjectAttributes,
+ &usDeviceName,
+ OBJ_CASE_INSENSITIVE,
+ (HANDLE) NULL,
+ (PSECURITY_DESCRIPTOR) NULL
+ );
+
+ status =
+ NtCreateFile(
+ &StreamHandle,
+ SYNCHRONIZE | FILE_READ_DATA | FILE_WRITE_DATA,
+ &ObjectAttributes,
+ &IoStatusBlock,
+ NULL,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ FILE_OPEN_IF,
+ 0,
+ NULL,
+ 0);
+
+
+ *pHandle = StreamHandle;
+
+ return(status);
+
+} // s_open
+
+//------------------------------------------------------------------------
+NTSTATUS
+DeviceIoCtrl(
+ IN HANDLE fd,
+ IN PVOID ReturnBuffer,
+ IN ULONG BufferSize,
+ IN ULONG Ioctl,
+ IN PVOID pInput,
+ IN ULONG SizeInput
+ )
+
+/*++
+
+Routine Description:
+
+ This procedure performs an ioctl(I_STR) on a stream.
+
+Arguments:
+
+ fd - NT file handle
+ iocp - pointer to a strioctl structure
+
+Return Value:
+
+ 0 if successful, -1 otherwise.
+
+--*/
+
+{
+ NTSTATUS status;
+ int retval;
+ ULONG QueryType;
+ IO_STATUS_BLOCK iosb;
+
+
+ status = NtDeviceIoControlFile(
+ fd, // Handle
+ NULL, // Event
+ NULL, // ApcRoutine
+ NULL, // ApcContext
+ &iosb, // IoStatusBlock
+ Ioctl, // IoControlCode
+ pInput, // InputBuffer
+ SizeInput, // InputBufferSize
+ (PVOID) ReturnBuffer, // OutputBuffer
+ BufferSize); // OutputBufferSize
+
+
+ if (status == STATUS_PENDING)
+ {
+ status = NtWaitForSingleObject(
+ fd, // Handle
+ TRUE, // Alertable
+ NULL); // Timeout
+ if (NT_SUCCESS(status))
+ {
+ status = iosb.Status;
+ }
+ }
+
+ return(status);
+
+}
+
+
+
+PCHAR
+UnicodeToPrintfString(
+ PWCHAR WideChar
+ )
+{
+ UNICODE_STRING UString;
+ ANSI_STRING AString;
+ AString.Buffer = WCtoPrintfBuf;
+ AString.MaximumLength = sizeof(WCtoPrintfBuf);
+ RtlInitUnicodeString(&UString, WideChar);
+ RtlUnicodeStringToOemString(&AString, &UString, FALSE);
+
+ return WCtoPrintfBuf;
+}
+
+
diff --git a/private/net/svcdlls/browser/browtest/src/browfunc.h b/private/net/svcdlls/browser/browtest/src/browfunc.h
new file mode 100644
index 000000000..6138a375a
--- /dev/null
+++ b/private/net/svcdlls/browser/browtest/src/browfunc.h
@@ -0,0 +1,212 @@
+#ifndef _BROWFUNC
+#define _BROWFUNC
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windows.h>
+#include <lm.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <ntddbrow.h>
+#include <hostannc.h>
+#include <lmbrowsr.h>
+#include <brcommon.h>
+#include <netlib.h>
+#include <nb30.h>
+#include <rxserver.h>
+#include <nbtioctl.h>
+
+
+
+#define MAXBACKUPS 5
+
+NET_API_STATUS
+ClearNbtNameTableCache(UNICODE_STRING);
+
+
+NTSTATUS
+DeviceIoCtrl(
+ IN HANDLE fd,
+ IN PVOID ReturnBuffer,
+ IN ULONG BufferSize,
+ IN ULONG Ioctl,
+ IN PVOID pInput,
+ IN ULONG SizeInput
+ );
+
+
+BOOL
+FindAllTransports(UNICODE_STRING *,
+ DWORD *
+ );
+
+VOID
+ListWFW(
+ IN PCHAR Domain
+ );
+
+VOID
+RpcList(
+ IN PCHAR Transport,
+ IN PCHAR ServerOrDomain,
+ IN PCHAR Flags,
+ IN BOOL GoForever
+ );
+
+VOID
+RpcCmp(
+ IN PCHAR Transport,
+ IN PCHAR ServerOrDomain,
+ IN PCHAR Flags,
+ IN BOOL GoForever
+ );
+
+NET_API_STATUS
+GetBrowserTransportList(
+ OUT PLMDR_TRANSPORT_LIST *
+ );
+
+PCHAR
+get_error_text(
+ DWORD dwErr
+ );
+
+VOID
+GetLocalList(
+ IN PCHAR Transport,
+ IN PCHAR Flags
+ );
+
+NET_API_STATUS
+GetNetBiosPdcName(
+ IN LPWSTR NetworkName,
+ IN LPWSTR PrimaryDomain,
+ OUT LPWSTR MasterName
+ );
+
+VOID
+GetOtherdomains(
+ IN PCHAR ServerName
+ );
+
+VOID
+IllegalDatagram(
+ IN PCHAR Transport,
+ IN PCHAR ServerName
+ );
+VOID
+AnnounceMaster(
+ IN PCHAR Transport,
+ IN PCHAR ServerName
+ );
+
+VOID
+Announce(
+ IN PCHAR Transport,
+ IN PCHAR Domain,
+ IN BOOL AsMaster
+ );
+
+NTSTATUS
+OpenNbt(
+ IN UNICODE_STRING,
+ OUT PHANDLE pHandle
+ );
+
+
+VOID
+Populate(
+ IN BOOL PopulateDomain,
+ IN PCHAR Transport,
+ IN PCHAR Domain,
+ IN PCHAR NumberOfMachines,
+ IN PCHAR Frequency
+ );
+
+VOID
+AddMasterName(
+ IN PCHAR Transport,
+ IN PCHAR Domain,
+ IN BOOL Pause
+ );
+
+VOID
+AddDomainName(
+ IN PCHAR Transport,
+ IN PCHAR Domain,
+ IN BOOL Pause
+ );
+
+
+VOID
+Tickle(
+ IN PCHAR Transport,
+ IN PCHAR Domain
+ );
+
+VOID
+ForceAnnounce(
+ IN UNICODE_STRING Transport,
+ IN LPTSTR Domain
+ );
+
+NET_API_STATUS
+GetBList(
+ IN UNICODE_STRING TransportName,
+ IN TCHAR * DomainName,
+ IN BOOLEAN ForceRescan,
+ OUT ULONG * NumBackUps,
+ OUT TCHAR wcBackUpBrowsers[MAXBACKUPS][CNLEN+3]
+ );
+
+VOID
+DumpStatistics(
+ IN ULONG NArgs,
+ IN PCHAR Arg1
+ );
+
+
+NET_API_STATUS
+Elect(
+ IN UNICODE_STRING Transport,
+ IN LPTSTR Domain
+ );
+
+
+NET_API_STATUS
+EnableService(
+ IN LPTSTR ServiceName
+ );
+
+
+VOID
+GetWinsServer(
+ IN PCHAR Transport
+ );
+
+VOID
+GetDomainList(
+ IN PCHAR IpAddress
+ );
+
+NTSTATUS
+ReadRegistry(
+ OUT PUCHAR pDeviceName,
+ OUT PUCHAR pScope
+ );
+
+
+PCHAR
+UnicodeToPrintfString( PWCHAR );
+
+VOID
+View(
+ IN PCHAR Transport,
+ IN PCHAR ServerOrDomain,
+ IN PCHAR Flags,
+ IN PCHAR Domain,
+ IN BOOL GoForever
+ );
+
+#endif
diff --git a/private/net/svcdlls/browser/browtest/src/browglob.h b/private/net/svcdlls/browser/browtest/src/browglob.h
new file mode 100644
index 000000000..4f2e61e9d
--- /dev/null
+++ b/private/net/svcdlls/browser/browtest/src/browglob.h
@@ -0,0 +1,62 @@
+/*
+ * This file contains all the global variables in browtest.
+ */
+
+
+
+//
+// Note that the ordering of these lists are important
+//
+TCHAR *TRANSPORTS[MAXPROTOCOLS] = {L"NwlnkIpx", L"NwlnkNb", L"NetBT", L"Ubnb", L"Nbf"};
+CHAR *PROTOCOLS[MAXPROTOCOLS] = {"IPX", "NBIPX", "TCP", "XNS", "NETBEUI"};
+enum E_PROTOCOLS {IPX, NBIPX, TCP, XNS, NETBEUI};
+
+
+OSPROP OSTYPES[MAXOSTYPES] = {{"DOSLM", 0},
+ {"OS2", 0},
+ {"WFW3.11", 1},
+ {"CHICAGO", 1},
+ {"NT3.1", 2},
+ {"NT3.5", 2},
+ {"AS3.1", 3},
+ {"AS3.5", 4}
+ };
+
+
+MACHINEINFO HeadList1;
+
+// File pointers to the log file and summary file
+FILE *fplog;
+FILE *fpsum;
+
+
+CHAR PrintBuf[1024];
+DOMAININFO LocDomInfo; // PDC of local domain
+LPMACHINEINFO lpDomStart; // Local machine domain start
+LPMACHINEINFO lpLocMachineInfo; // Local machine info
+
+DWORD ERRCOUNT = 0;
+DWORD WRNCOUNT = 0;
+DWORD TESTCOUNT = 0;
+DWORD SUCSCOUNT = 0;
+DWORD OKCOUNT = 0;
+
+TCHAR wcTESTEDDOMAINS[MAXTESTEDDOMAINS][DNLEN+1];
+INT iNumOfTestedDomains = 0;
+
+BOOL bIPXHack = TRUE;
+BOOL bForceAnn = FALSE;
+BOOL bSingleDomTest = TRUE;
+DWORD dwStressTest = 0;
+
+
+COMMAND_TABLE CommandTable[] = {{"IPXHack" , VALUETYPE_BOOL, &bIPXHack},
+ {"ForceAnn", VALUETYPE_BOOL, &bForceAnn},
+ {"SingleD" , VALUETYPE_BOOL, &bSingleDomTest},
+ {"StressT" , VALUETYPE_ULONG, &dwStressTest}};
+
+
+INT CommandTableSz = (sizeof(CommandTable)/sizeof(COMMAND_TABLE));
+
+
+HANDLE ConsoleMutex;
diff --git a/private/net/svcdlls/browser/browtest/src/browmdom.c b/private/net/svcdlls/browser/browtest/src/browmdom.c
new file mode 100644
index 000000000..ab746aeaf
--- /dev/null
+++ b/private/net/svcdlls/browser/browtest/src/browmdom.c
@@ -0,0 +1,1165 @@
+#include "browmdom.h"
+
+extern CHAR PrintBuf[1024];
+extern MACHINEINFO HeadList1;
+
+extern TCHAR wcTESTEDDOMAINS[MAXTESTEDDOMAINS][DNLEN+1];
+extern INT iNumOfTestedDomains;
+extern FILE *fplog;
+extern FILE *fpsum;
+
+extern DWORD ERRCOUNT;
+extern DWORD WRNCOUNT;
+extern DWORD TESTCOUNT;
+extern DWORD SUCSCOUNT;
+extern DWORD OKCOUNT;
+
+extern enum E_PROTOCOLS {IPX, NBIPX, TCP, XNS, NETBEUI};
+
+extern BOOL bIPXHack;
+extern BOOL bForceAnn;
+
+extern LPMACHINEINFO lpDomStart;
+extern DOMAININFO LocDomInfo;
+
+extern HANDLE ConsoleMutex;
+
+VOID
+CheckSrvListOnPrimaryDom(DOMAININFO DomainInfo,
+ XPORTINFO *TestedXportInfo,
+ INT iNumOfTestedXports,
+ TCHAR wcCurrentMasters[MAXPROTOCOLS][CNLEN+1],
+ BOOL bTEST_TCP)
+{
+INT i, index;
+DWORD dwEntriesInList;
+DWORD dwTotalEntries;
+PVOID pvMsServerList;
+NET_API_STATUS Status;
+PSERVER_INFO_101 pServerInfo101;
+
+
+ for(index = 0; index < iNumOfTestedXports; index++){
+
+ //
+ // If TCP/IP is not to be tested then skip
+ //
+ if((TestedXportInfo[index].index == TCP) && !bTEST_TCP)
+ continue;
+
+ //
+ // If no master is available in primary domain, then don't test other PDC.
+ //
+ if(_wcsicmp(wcCurrentMasters[index], L"") == 0)
+ continue;
+
+ if(DomainInfo.lpMInfo->Protocols[TestedXportInfo[index].index]){
+ //
+ // Remote machine has this protocol
+ //
+ sprintf(PrintBuf,"\n\nTEST#: [TS%ld].\n=============", ++TESTCOUNT);
+ PrintString(TOALL, PrintBuf);
+
+ sprintf(PrintBuf,"\n\nTEST: Check if list from %s has the Other domain name. ",
+ UnicodeToPrintfString(wcCurrentMasters[index]));
+ PrintString(TOALL, PrintBuf);
+ sprintf(PrintBuf," Transport %s.\n",
+ UnicodeToPrintfString(TestedXportInfo[index].Transport.Buffer));
+ PrintString(TOALL, PrintBuf);
+
+
+ if((TestedXportInfo[index].index == IPX) && bIPXHack){
+
+ Status = RetrieveList(wcCurrentMasters[index], L"\\Device\\NwLnkNb",
+ &pvMsServerList, &dwEntriesInList, &dwTotalEntries,
+ SV_TYPE_ALTERNATE_XPORT|SV_TYPE_DOMAIN_ENUM, NULL, NULL, FALSE);
+
+ } else {
+ Status = RetrieveList(wcCurrentMasters[index], TestedXportInfo[index].Transport.Buffer,
+ &pvMsServerList, &dwEntriesInList, &dwTotalEntries,
+ SV_TYPE_DOMAIN_ENUM, NULL, NULL, FALSE);
+ }
+
+ if(Status == NERR_Success){
+ pServerInfo101 = (PSERVER_INFO_101)pvMsServerList;
+
+ //
+ // Check whether the other domain name is there in the list
+ // retrieved from the current master of primary domain on this transport.
+ //
+ for(i=0; i< (INT)dwEntriesInList &&
+ _wcsicmp(pServerInfo101[i].sv101_name, DomainInfo.wcDomainName) != 0; i++);
+
+ if(i >= (INT)dwEntriesInList ) {
+ sprintf(PrintBuf,"\n\nERROR[ER%ld]: Other Domain Name not found in the list from %s ",
+ ++ERRCOUNT, UnicodeToPrintfString(wcCurrentMasters[index]));
+ PrintString(TOALL, PrintBuf);
+
+ sprintf(PrintBuf,"on Transport %s.\n",
+ UnicodeToPrintfString(TestedXportInfo[index].Transport.Buffer));
+ PrintString(TOALL, PrintBuf);
+ } else {
+
+ sprintf(PrintBuf,"\nSUCCESS[SC%ld]: Other Domain Name found in the list from %s ",
+ ++SUCSCOUNT, UnicodeToPrintfString(wcCurrentMasters[index]));
+ PrintString(TOALL, PrintBuf);
+
+ sprintf(PrintBuf,"on Transport %s.\n",
+ UnicodeToPrintfString(TestedXportInfo[index].Transport.Buffer));
+ PrintString(TOALL, PrintBuf);
+ }
+
+ } else {
+ sprintf(PrintBuf,"\n\nERROR[ER%ld]: Cannot get the Domain List from %s ",
+ ++ERRCOUNT, UnicodeToPrintfString(wcCurrentMasters[index]));
+ PrintString(TOALL, PrintBuf);
+
+ sprintf(PrintBuf,"on Transport %s.\n",
+ UnicodeToPrintfString(TestedXportInfo[index].Transport.Buffer));
+ PrintString(TOALL, PrintBuf);
+ }
+
+ NetApiBufferFree(pvMsServerList);
+ }
+
+ }
+
+}
+
+
+
+VOID
+CheckSrvListOnOtherDom(DOMAININFO DomainInfo,
+ XPORTINFO *TestedXportInfo,
+ INT iNumOfTestedXports,
+ TCHAR wcCurrentMasters[MAXPROTOCOLS][CNLEN+1],
+ BOOL bTEST_TCP)
+{
+INT i, index;
+DWORD dwEntriesInList;
+DWORD dwTotalEntries;
+PVOID pvMsServerList;
+NET_API_STATUS Status;
+PSERVER_INFO_101 pServerInfo101;
+
+
+ for(index = 0; index < iNumOfTestedXports; index++){
+ //
+ // If TCP/IP is not to be tested then skip
+ //
+ if((TestedXportInfo[index].index == TCP) && !bTEST_TCP)
+ continue;
+
+ //
+ // If no master is available in primary domain, then don't test other PDC.
+ //
+ if(_wcsicmp(wcCurrentMasters[index], L"") == 0)
+ continue;
+
+ if(DomainInfo.lpMInfo->Protocols[TestedXportInfo[index].index]){
+ //
+ // Remote machine has this protocol
+ //
+ sprintf(PrintBuf,"\n\nTEST#: [TS%ld].\n=============", ++TESTCOUNT);
+ PrintString(TOALL, PrintBuf);
+
+ sprintf(PrintBuf,"\n\nTEST: Check if list from %s has the Primary domain name. ",
+ UnicodeToPrintfString(DomainInfo.lpMInfo->wcMachineName));
+ PrintString(TOALL, PrintBuf);
+ sprintf(PrintBuf," Transport %s.\n",
+ UnicodeToPrintfString(TestedXportInfo[index].Transport.Buffer));
+ PrintString(TOALL, PrintBuf);
+
+
+ if((TestedXportInfo[index].index == IPX) && bIPXHack){
+
+ Status = RetrieveList(DomainInfo.lpMInfo->wcMachineName, L"\\Device\\NwLnkNb",
+ &pvMsServerList, &dwEntriesInList, &dwTotalEntries,
+ SV_TYPE_ALTERNATE_XPORT|SV_TYPE_DOMAIN_ENUM, NULL, NULL, FALSE);
+
+ } else {
+ Status = RetrieveList(DomainInfo.lpMInfo->wcMachineName, TestedXportInfo[index].Transport.Buffer,
+ &pvMsServerList, &dwEntriesInList, &dwTotalEntries,
+ SV_TYPE_DOMAIN_ENUM, NULL, NULL, FALSE);
+ }
+
+ if(Status == NERR_Success){
+ pServerInfo101 = (PSERVER_INFO_101)pvMsServerList;
+
+ //
+ // Check whether the primary domain name is there in the list
+ // retrieved from the Other PDC.
+ //
+ for(i=0; i< (INT)dwEntriesInList &&
+ _wcsicmp(pServerInfo101[i].sv101_name, wcTESTEDDOMAINS[0]) != 0; i++);
+
+ if(i >= (INT)dwEntriesInList ) {
+ sprintf(PrintBuf,"\n\nERROR[ER%ld]: Primary Domain Name not found in the list from %s ",
+ ++ERRCOUNT, UnicodeToPrintfString(DomainInfo.lpMInfo->wcMachineName));
+ PrintString(TOALL, PrintBuf);
+
+ sprintf(PrintBuf,"on Transport %s.\n",
+ UnicodeToPrintfString(TestedXportInfo[index].Transport.Buffer));
+ PrintString(TOALL, PrintBuf);
+ } else {
+
+ sprintf(PrintBuf,"\nSUCCESS[SC%ld]: Primary Domain Name found in the list from %s ",
+ ++SUCSCOUNT, UnicodeToPrintfString(DomainInfo.lpMInfo->wcMachineName));
+ PrintString(TOALL, PrintBuf);
+
+ sprintf(PrintBuf,"on Transport %s.\n",
+ UnicodeToPrintfString(TestedXportInfo[index].Transport.Buffer));
+ PrintString(TOALL, PrintBuf);
+ }
+
+ } else {
+ sprintf(PrintBuf,"\n\nERROR[ER%ld]: Cannot get the Domain List from %s ",
+ ++ERRCOUNT, UnicodeToPrintfString(DomainInfo.lpMInfo->wcMachineName));
+ PrintString(TOALL, PrintBuf);
+
+ sprintf(PrintBuf,"on Transport %s.\n",
+ UnicodeToPrintfString(TestedXportInfo[index].Transport.Buffer));
+ PrintString(TOALL, PrintBuf);
+ }
+
+ NetApiBufferFree(pvMsServerList);
+ }
+
+ }
+
+}
+
+
+NET_API_STATUS
+CreateShutService(LPTSTR wcMachineName)
+{
+
+HANDLE schService;
+HANDLE schSCManager;
+
+
+ schSCManager = OpenSCManager(
+ wcMachineName,
+ NULL,
+ SC_MANAGER_ALL_ACCESS);
+
+ if(schSCManager == (HANDLE)NULL){
+ sprintf(PrintBuf,"\n\nCannot Access the service controller on %s. \nError %s\n",
+ UnicodeToPrintfString(wcMachineName), get_error_text(GetLastError()));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+
+ return(!NERR_Success);
+ }
+
+ schService = CreateService( schSCManager,
+ SHUTSVCNAME,
+ SHUTSVCNAME,
+ SERVICE_ALL_ACCESS,
+ SERVICE_WIN32_OWN_PROCESS,
+ SERVICE_DEMAND_START,
+ SERVICE_ERROR_NORMAL,
+ (LPTSTR)SHUTSVCPATH,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL);
+
+ if(schService == (HANDLE)NULL){
+ if(GetLastError() != ERROR_SERVICE_EXISTS){
+ sprintf(PrintBuf,"\n\nCannot Create service on %s. \nError %s\n",
+ UnicodeToPrintfString(wcMachineName), get_error_text(GetLastError()));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+
+ return(!NERR_Success);
+ }
+ }
+
+ CloseHandle(schSCManager);
+ CloseHandle(schService);
+
+return NERR_Success;
+}
+
+
+VOID
+DeleteShutService(LPTSTR wcMachineName)
+{
+
+HANDLE schService;
+HANDLE schSCManager;
+
+
+ schSCManager = OpenSCManager(
+ wcMachineName,
+ NULL,
+ SC_MANAGER_ALL_ACCESS);
+
+ if(schSCManager == (HANDLE)NULL){
+ sprintf(PrintBuf,"\n\nCannot Access the service controller on %s for deletion. \nError %s\n",
+ UnicodeToPrintfString(wcMachineName), get_error_text(GetLastError()));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+
+ return;
+ }
+
+ schService = OpenService( schSCManager,
+ SHUTSVCNAME,
+ SERVICE_ALL_ACCESS);
+
+ if(schService == (HANDLE)NULL){
+ sprintf(PrintBuf,"\n\nCannot Open Shut service on %s for deletion. \nError %s\n",
+ UnicodeToPrintfString(wcMachineName), get_error_text(GetLastError()));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+
+ return;
+ }
+
+ DeleteService(schService);
+
+ CloseHandle(schSCManager);
+ CloseHandle(schService);
+
+ return;
+}
+
+
+VOID
+DoMulDomMulSubNetTests(XPORTINFO *TestedXportInfo,
+ INT iNumOfTestedXports)
+{
+INT i, iDomIndex;
+TCHAR wcOthPDC[CNLEN+1];
+LPMACHINEINFO lpMachineInfo;
+LPMACHINEINFO lpOtherPDC = NULL;
+DOMAININFO OthSubNetDomInfo;
+DOMAININFO SameSubNetDomInfo;
+BOOL bFoundSameSubNetPDC = FALSE;
+BOOL bFoundOthSubNetPDC = FALSE;
+NET_API_STATUS Status;
+
+
+ sprintf(PrintBuf,"\n\n\n================================================================================\n");
+ PrintString(TOALL, PrintBuf);
+
+ sprintf(PrintBuf,"\n\t\tMULTIPLE DOMAIN TESTS!\n\t\t======================\n\n");
+ PrintString(TOALL, PrintBuf);
+
+ //
+ // We first find out whether the second domain is in the same subnet.
+ // If it is in the same subnet, we expect both the domains to see each other
+ // while each browse master on domain 1 is stopped. If they are on two
+ // subnets, TCP/IP needs the PDC to be up, for them to see each other.
+ for(lpMachineInfo=HeadList1.Next; lpMachineInfo;
+ lpMachineInfo=lpMachineInfo->Next){
+
+ //
+ // If the domain is not the same as any of them found
+ //
+ for(i=0; (i < iNumOfTestedDomains) &&
+
+ _wcsicmp(lpMachineInfo->wcDomainName, wcTESTEDDOMAINS[i]) != 0; i++);
+
+ if(i >= iNumOfTestedDomains){
+ //
+ // This domain is not in the group found.
+ //
+ if(iNumOfTestedDomains < MAXTESTEDDOMAINS){
+
+ wcscpy(wcTESTEDDOMAINS[iNumOfTestedDomains++], lpMachineInfo->wcDomainName);
+
+ } else {
+ sprintf(PrintBuf,"\n\nERROR: Only a maximum of %ld Domains can be specified in the input file.\n",
+ MAXTESTEDDOMAINS);
+ PrintString(TOALL, PrintBuf);
+
+ }
+
+ } // if (i< NumOfTestedDomains)
+
+ } // for lpMachineInfo
+
+
+ if(iNumOfTestedDomains <= 1){
+ sprintf(PrintBuf,"\n\nERROR: There is only one domain specified in the input file!\n");
+ PrintString(TOALL, PrintBuf);
+ sprintf(PrintBuf,"\n\nERROR: Multiple domain tests not done!\n");
+ PrintString(TOALL, PrintBuf);
+ return;
+ }
+
+
+ //
+ // Find the PDC's of the domains and see whether any of them
+ // are on the primary subnet.
+ //
+ for(iDomIndex = 1; iDomIndex < iNumOfTestedDomains; iDomIndex++){
+ i = 0;
+ lstrcpy(wcOthPDC, L"");
+ while((i<iNumOfTestedXports) && (Status = GetNetBiosPdcName(TestedXportInfo[i].Transport.Buffer, wcTESTEDDOMAINS[iDomIndex], wcOthPDC)) != NERR_Success)
+ i++;
+ if(Status == NERR_Success){
+ //
+ // If found the PDC;
+ //
+ for(lpMachineInfo=HeadList1.Next; lpMachineInfo &&
+ _wcsicmp(lpMachineInfo->wcMachineName, wcOthPDC) != 0;
+ lpMachineInfo=lpMachineInfo->Next);
+
+ if(lpMachineInfo) {
+ //
+ // Found PDC in the list.
+ //
+ if(lpMachineInfo->iSubnet == SUBNET1){
+ //
+ // If the machine is in the same subnet
+ //
+ if(!bFoundSameSubNetPDC){
+ wcscpy(SameSubNetDomInfo.wcDomainName, wcTESTEDDOMAINS[iDomIndex]);
+ SameSubNetDomInfo.lpMInfo = lpMachineInfo;
+ bFoundSameSubNetPDC = TRUE;
+ }
+
+ } else { // Other subnet
+
+ if(!bFoundOthSubNetPDC){
+ wcscpy(OthSubNetDomInfo.wcDomainName, wcTESTEDDOMAINS[iDomIndex]);
+ OthSubNetDomInfo.lpMInfo = lpMachineInfo;
+ bFoundOthSubNetPDC = TRUE;
+ }
+ }
+
+ } else {
+ //
+ // Could not find PDC in List
+ //
+ sprintf(PrintBuf,"\n\nERROR: The PDC (%s) of domain", UnicodeToPrintfString(wcTESTEDDOMAINS[iDomIndex]));
+ PrintString(TOALL, PrintBuf);
+ sprintf(PrintBuf," %s is not found in the input file!\n", UnicodeToPrintfString(wcOthPDC));
+ PrintString(TOALL, PrintBuf);
+ sprintf(PrintBuf,"\n\nERROR: Multiple domain tests not done!\n");
+ PrintString(TOALL, PrintBuf);
+ return;
+ }
+
+ } // found PDC
+ }
+
+
+
+ if(!bFoundSameSubNetPDC && !bFoundOthSubNetPDC){
+ //
+ // No PDC's in other domains.
+ //
+ sprintf(PrintBuf,"\n\nERROR[ER%ld]:No PDC's in other domains.\n",++ERRCOUNT);
+ PrintString(TOALL, PrintBuf);
+ sprintf(PrintBuf,"\n\nERROR: Multiple domain tests not done!\n");
+ PrintString(TOALL, PrintBuf);
+ return;
+ }
+
+ if(bFoundSameSubNetPDC){
+ //
+ // There is a PDC in the same subnet
+ //
+ // Test all transports
+
+ MulDomSameSubnetTest(SameSubNetDomInfo, TestedXportInfo, iNumOfTestedXports);
+ }
+
+ //
+ // Start the browser back on all machines
+ //
+ sprintf(PrintBuf,"\nStarting all the Browsers back.\n");
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ if(!CheckBrServiceOnMachinesInList())
+ return;
+
+ sprintf(PrintBuf, "\n\nSleeping for %ld msecs.\n", BASESLEEPTIME*2);
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ Sleep(BASESLEEPTIME*2);
+
+
+ if(bFoundOthSubNetPDC){
+ //
+ // There is a PDC on the other subnet
+ //
+
+ MulDomDiffSubnetTest(OthSubNetDomInfo, TestedXportInfo, iNumOfTestedXports);
+ }
+
+ //
+ // Start the browser back on all machines
+ //
+ sprintf(PrintBuf,"\nStarting all the Browsers back.\n");
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ if(!CheckBrServiceOnMachinesInList())
+ return;
+
+ sprintf(PrintBuf,"\n\n--------------------------------------------------------------------------------\n");
+ PrintString(TOALL, PrintBuf);
+
+}
+
+
+VOID
+FindTheCurrentMasters(LPTSTR wcDomainName,
+ XPORTINFO *TestedXportInfo,
+ INT iNumOfTestedXports,
+ TCHAR wcCurrentMasters[MAXPROTOCOLS][CNLEN+1])
+{
+INT index;
+TCHAR wcNewMaster[CNLEN +1];
+LPMACHINEINFO lpMachineInfo;
+NET_API_STATUS Status;
+
+
+ for(index = 0; index < iNumOfTestedXports; index++){
+
+ //
+ // Find the new Master.
+ //
+ lstrcpy(wcNewMaster, L"");
+ if((Status = GetMasterName(TestedXportInfo, iNumOfTestedXports, index, wcDomainName, wcNewMaster)) != NERR_Success){
+ if(MasterAvailable(TestedXportInfo[index], wcDomainName, SUBNET1)){
+ sprintf(PrintBuf,"\nERROR: Unable to find the new master: Domain: %s ",UnicodeToPrintfString(wcDomainName));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ sprintf(PrintBuf," Transport: %s", UnicodeToPrintfString(TestedXportInfo[index].Transport.Buffer));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ sprintf(PrintBuf," Old Master: %s.\n", UnicodeToPrintfString(wcCurrentMasters[index]));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+
+ } else {
+ sprintf(PrintBuf,"\nOK: Unable to find the new master. No servers running Browser service. Domain: %s ", UnicodeToPrintfString(wcDomainName));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ sprintf(PrintBuf," Transport: %s", UnicodeToPrintfString(TestedXportInfo[index].Transport.Buffer));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ sprintf(PrintBuf," Old Master: %s.\n", UnicodeToPrintfString(wcCurrentMasters[index]));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ }
+
+ wcscpy(wcCurrentMasters[index], L"");
+ continue;
+ }
+
+ sprintf(PrintBuf,"\nNew Master in Primary Domain on transport %s: ", UnicodeToPrintfString(TestedXportInfo[index].Transport.Buffer));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ sprintf(PrintBuf,"%s.\n", UnicodeToPrintfString(wcNewMaster));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+
+ wcscpy(wcCurrentMasters[index], wcNewMaster);
+ }
+}
+
+
+
+BOOL
+MulDomDiffSubnetTest(DOMAININFO OthSubNetDomInfo,
+ XPORTINFO *TestedXportInfo,
+ INT iNumOfTestedXports)
+{
+BOOL Found;
+INT i, index;
+TCHAR wcCurrentMasters[MAXPROTOCOLS][CNLEN+1];
+LPMACHINEINFO lpMachineInfo;
+NET_API_STATUS Status;
+BOOL bTEST_TCP = TRUE;
+
+
+
+ for(i = 0; i< MAXPROTOCOLS; i++) wcscpy(wcCurrentMasters[i], L"");
+
+ //
+ // Create the service on the remote computer
+ //
+ sprintf(PrintBuf,"\n\nTEST#: [TS%ld].\n=============", ++TESTCOUNT);
+ PrintString(TOALL, PrintBuf);
+
+ sprintf(PrintBuf,"\n\nTEST: Multiple domain tests on different subnet.\n");
+ PrintString(TOALL, PrintBuf);
+
+
+ Status = CreateShutService(OthSubNetDomInfo.lpMInfo->wcMachineName);
+
+ if(Status == NERR_Success){
+
+ //
+ // Primary PDC is running. Check whether the other domain has
+ // this domain info.
+ //
+ sprintf(PrintBuf,"\n\nCheck if Other domain has Primary domain info, with Primary PDC Browser running.\n");
+ PrintString(TOALL, PrintBuf);
+
+ //
+ // Find Current masters of the primary domain.
+ //
+ FindTheCurrentMasters(wcTESTEDDOMAINS[0], TestedXportInfo, iNumOfTestedXports, wcCurrentMasters);
+
+ //
+ // Find if there are any masters at all.
+ //
+ for(i = 0; (i < iNumOfTestedXports) &&
+ (_wcsicmp(wcCurrentMasters[i], L"") == 0); i++);
+
+ if(i < iNumOfTestedXports){
+ //
+ // There is a master available in the Primary domain.
+
+ // Check primary and other domain for each others info.
+ //
+
+ TestPrimAndOthDoms(OthSubNetDomInfo, TestedXportInfo, iNumOfTestedXports, wcCurrentMasters, bTEST_TCP);
+ }
+
+ //
+ // Without the PDC TCP/IP won't work
+ //
+ bTEST_TCP = FALSE;
+
+ //
+ // If there is only TCP then we are not testing it by shutting down
+ // each machines
+ //
+ if(!((iNumOfTestedXports == 1) && (TestedXportInfo[0].index == TCP))) {
+
+ //
+ // Shutdown each of the local masters and find if the
+ // other PDC has the local domain info.
+ //
+ do{
+ Found = FALSE;
+
+ //
+ // Find whether any of the current masters are NT machines
+ //
+ for(index = 0; ((index < iNumOfTestedXports) && !Found); ){
+
+ if(_wcsicmp(wcCurrentMasters[index], L"") != 0){
+ for(lpMachineInfo = lpDomStart; lpMachineInfo && _wcsicmp(wcCurrentMasters[index],
+ lpMachineInfo->wcMachineName) != 0; lpMachineInfo= lpMachineInfo->Next);
+
+ if(lpMachineInfo)
+ Found = (IsNTMachine(lpMachineInfo) && lpMachineInfo->BrowserServiceStarted);
+ }
+
+ if(!Found)
+ index++;
+ }
+
+ if(Found){
+ //
+ // A master was found, stop it and then "stop and start" the
+ // RDR, Browser on other PDC and check whether, it gets this domain info.
+ //
+ sprintf(PrintBuf,"\n\n--------------------------------------------------------------------------------\n");
+ PrintString(TOALL, PrintBuf);
+ sprintf(PrintBuf,"\nTEST: Stop Browser on Master %s in Primary domain and check if \nthe other domain gets this info.\n",
+ UnicodeToPrintfString(wcCurrentMasters[index]));
+ PrintString(TOALL, PrintBuf);
+
+ if((Status = StopBrowserService(wcCurrentMasters[index])) != NERR_Success){
+ sprintf(PrintBuf,"\nERROR[%ld]:Could not stop browser on %s.\n",
+ ++ERRCOUNT, UnicodeToPrintfString(wcCurrentMasters[index]));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ return FALSE;
+ }
+
+ lpMachineInfo->BrowserServiceStarted = FALSE;
+
+ //
+ // Sleep for sometime
+ //
+ sprintf(PrintBuf, "\nSleeping for %ld msecs\n", BASESLEEPTIME * 2);
+ PrintString(TOSCREENANDLOG, PrintBuf);
+
+ Sleep(BASESLEEPTIME*2);
+
+ //
+ // For each of the tested transports find who is the Master
+ //
+ FindTheCurrentMasters(wcTESTEDDOMAINS[0], TestedXportInfo, iNumOfTestedXports, wcCurrentMasters);
+
+ //
+ // Find if there are any masters at all.
+ //
+ for(i = 0; (i < iNumOfTestedXports) &&
+ (_wcsicmp(wcCurrentMasters[i], L"") == 0); i++);
+
+ if(i < iNumOfTestedXports){
+ //
+ // There is a master available in the Primary domain.
+
+ // Check primary and other domain for each others info.
+ //
+
+ TestPrimAndOthDoms(OthSubNetDomInfo, TestedXportInfo, iNumOfTestedXports, wcCurrentMasters, bTEST_TCP);
+ }
+
+ }
+
+ }while(Found);
+
+ } // If not just TCP alone.
+
+ //
+ // This part is specifically for the TCP/IP protocol.
+ // Start Browser on just the two PDC's and see if the
+ // other PDC has info of the primary domain.
+ //
+ for(index = 0; (index < iNumOfTestedXports) &&
+ TestedXportInfo[index].index != TCP ; index++);
+ //
+ // If TCP is a tested Transport
+ //
+ if(index < iNumOfTestedXports) {
+
+ if(LocDomInfo.lpMInfo->Protocols[TCP] && OthSubNetDomInfo.lpMInfo->Protocols[TCP]){
+ //
+ // Stop Browser on all machines in the primary domain.
+ // Start browser on the primary PDC. Stop and start RDR on the Other PDC.
+ // Then check the info on each others domains.
+
+ sprintf(PrintBuf,"\n\n--------------------------------------------------------------------------------\n");
+ PrintString(TOALL, PrintBuf);
+
+ sprintf(PrintBuf, "\nTEST: Check two isolated domains on TCP/IP across the subnets.\n");
+ PrintString(TOALL, PrintBuf);
+
+
+ for(lpMachineInfo = lpDomStart; lpMachineInfo; lpMachineInfo = lpMachineInfo->Next){
+ if(IsNTMachine(lpMachineInfo) && lpMachineInfo->BrowserServiceStarted){
+ if(StopBrowserService(lpMachineInfo->wcMachineName) == NERR_Success)
+ lpMachineInfo->BrowserServiceStarted = FALSE;
+ else {
+ sprintf(PrintBuf,"\nCould not stop browser on %s.\n",
+ UnicodeToPrintfString(lpMachineInfo->wcMachineName));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ }
+ }
+ }
+
+ if((Status = StartBrowserService(LocDomInfo.lpMInfo->wcMachineName)) != NERR_Success){
+ sprintf(PrintBuf,"\nERROR:Starting Browser on %s: \nError: %s\n", UnicodeToPrintfString(LocDomInfo.lpMInfo->wcMachineName), get_error_text(Status));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ } else {
+ LocDomInfo.lpMInfo->BrowserServiceStarted = TRUE;
+ }
+
+ if((Status = StartBrowserService(OthSubNetDomInfo.lpMInfo->wcMachineName)) != NERR_Success){
+ sprintf(PrintBuf,"\nERROR:Starting Browser on %s. \nError: %s\n", UnicodeToPrintfString(OthSubNetDomInfo.lpMInfo->wcMachineName), get_error_text(Status));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ } else {
+ OthSubNetDomInfo.lpMInfo->BrowserServiceStarted = TRUE;
+ }
+
+ sprintf(PrintBuf, "\nSleeping for %ld msecs.\n", BASESLEEPTIME*2);
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ Sleep(BASESLEEPTIME*2);
+
+ //
+ // Note: Special case!
+ //
+ // Only the master for TCP is found and it is obtained in
+ // index = 0.
+
+ wcscpy(wcCurrentMasters[0], L"");
+ FindTheCurrentMasters(wcTESTEDDOMAINS[0], &TestedXportInfo[index], 1, wcCurrentMasters);
+
+ if(_wcsicmp(wcCurrentMasters[0], L"") != 0){
+ //
+ // There is a master available in the Primary domain.
+
+ // Check primary and other domain for each others info.
+ //
+
+ TestPrimAndOthDoms(OthSubNetDomInfo, &TestedXportInfo[index], 1, wcCurrentMasters, TRUE);
+
+ } else {
+ sprintf(PrintBuf, "\nERROR[ER%ld]: There is no master in Primary Domain.\n", ++ERRCOUNT);
+ PrintString(TOALL, PrintBuf);
+ }
+
+ }
+ }
+
+
+ DeleteShutService(OthSubNetDomInfo.lpMInfo->wcMachineName);
+
+ } else {
+ sprintf(PrintBuf,"\n\nERROR[ER%ld]: Could not Create Shut Service on %s.\n", ++ERRCOUNT,UnicodeToPrintfString(OthSubNetDomInfo.lpMInfo->wcMachineName));
+ PrintString(TOALL, PrintBuf);
+ sprintf(PrintBuf,"\n\nERROR: Multiple domain tests not done on different Subnets!\n");
+ PrintString(TOALL, PrintBuf);
+ return FALSE;
+ }
+
+ sprintf(PrintBuf,"\nSUCCESS[SC%ld]: Multiple domain tests on different subnets.\n", ++SUCSCOUNT);
+ PrintString(TOALL, PrintBuf);
+
+ return TRUE;
+}
+
+
+
+BOOL
+MulDomSameSubnetTest(DOMAININFO SameSubNetDomInfo,
+ XPORTINFO *TestedXportInfo,
+ INT iNumOfTestedXports)
+{
+BOOL Found;
+INT i, index;
+TCHAR wcCurrentMasters[MAXPROTOCOLS][CNLEN+1];
+LPMACHINEINFO lpMachineInfo;
+NET_API_STATUS Status;
+BOOL bTEST_TCP = TRUE;
+
+ for(i = 0; i< MAXPROTOCOLS; i++) wcscpy(wcCurrentMasters[i], L"");
+
+ //
+ // Create the service on the remote computer
+ //
+ sprintf(PrintBuf,"\n\nTEST#: [TS%ld].\n=============", ++TESTCOUNT);
+ PrintString(TOALL, PrintBuf);
+
+ sprintf(PrintBuf,"\n\nTEST: Multiple domain tests on same subnet.\n");
+ PrintString(TOALL, PrintBuf);
+
+
+ Status = CreateShutService(SameSubNetDomInfo.lpMInfo->wcMachineName);
+
+ if(Status == NERR_Success){
+
+ //
+ // Primary PDC is running. Check whether the other domain has
+ // this domain info.
+ //
+ sprintf(PrintBuf,"\n\nCheck if Other domain has Primary domain info, with Primary PDC Browser running.\n");
+ PrintString(TOALL, PrintBuf);
+
+ //
+ // Find Current masters of the primary domain.
+ //
+ FindTheCurrentMasters(wcTESTEDDOMAINS[0], TestedXportInfo, iNumOfTestedXports, wcCurrentMasters);
+
+ //
+ // Find if there are any masters at all.
+ //
+ for(i = 0; (i < iNumOfTestedXports) &&
+ (_wcsicmp(wcCurrentMasters[i], L"") == 0); i++);
+
+ if(i < iNumOfTestedXports){
+ //
+ // There is a master available in the Primary domain.
+
+ // Check primary and other domain for each others info.
+ //
+
+ TestPrimAndOthDoms(SameSubNetDomInfo, TestedXportInfo, iNumOfTestedXports, wcCurrentMasters, bTEST_TCP);
+ }
+
+ //
+ // Shutdown each of the local masters and find if the
+ // other PDC has the local domain info.
+ //
+ do{
+ Found = FALSE;
+
+ //
+ // Find whether any of the current masters are NT machines
+ //
+ for(index = 0; ((index < iNumOfTestedXports) && !Found); ){
+
+ if(_wcsicmp(wcCurrentMasters[index], L"") != 0){
+ for(lpMachineInfo = lpDomStart; lpMachineInfo && _wcsicmp(wcCurrentMasters[index],
+ lpMachineInfo->wcMachineName) != 0; lpMachineInfo= lpMachineInfo->Next);
+
+ if(lpMachineInfo)
+ Found = (IsNTMachine(lpMachineInfo) && lpMachineInfo->BrowserServiceStarted);
+ }
+
+ if(!Found)
+ index++;
+ }
+
+ if(Found){
+ //
+ // A master was found, stop it and then "stop and start" the
+ // RDR, Browser on other PDC and check whether, it gets this domain info.
+ //
+ sprintf(PrintBuf,"\n\n--------------------------------------------------------------------------------\n");
+ PrintString(TOALL, PrintBuf);
+ sprintf(PrintBuf,"\nTEST: Stop Browser on Master %s in Primary domain and check if \nthe other domain gets this info\n",
+ UnicodeToPrintfString(wcCurrentMasters[index]));
+ PrintString(TOALL, PrintBuf);
+
+ if((Status = StopBrowserService(wcCurrentMasters[index])) != NERR_Success){
+ sprintf(PrintBuf,"\nERROR[%ld]:Could not stop browser on %s.\n",
+ ++ERRCOUNT, UnicodeToPrintfString(wcCurrentMasters[index]));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ return FALSE;
+ }
+
+ lpMachineInfo->BrowserServiceStarted = FALSE;
+
+ //
+ // Sleep for sometime
+ //
+ sprintf(PrintBuf, "\nSleeping for %ld msecs\n", BASESLEEPTIME * 2);
+ PrintString(TOSCREENANDLOG, PrintBuf);
+
+ Sleep(BASESLEEPTIME*2);
+
+ //
+ // For each of the tested transports find who is the Master
+ //
+ FindTheCurrentMasters(wcTESTEDDOMAINS[0], TestedXportInfo, iNumOfTestedXports, wcCurrentMasters);
+
+ //
+ // Find if there are any masters at all.
+ //
+ for(i = 0; (i < iNumOfTestedXports) &&
+ (_wcsicmp(wcCurrentMasters[i], L"") == 0); i++);
+
+ if(i < iNumOfTestedXports){
+ //
+ // There is a master available in the Primary domain.
+
+ // Check primary and other domain for each others info.
+ //
+
+ TestPrimAndOthDoms(SameSubNetDomInfo, TestedXportInfo, iNumOfTestedXports, wcCurrentMasters, bTEST_TCP);
+ }
+ }
+
+ }while(Found);
+
+
+ DeleteShutService(SameSubNetDomInfo.lpMInfo->wcMachineName);
+
+ } else {
+ sprintf(PrintBuf,"\n\nERROR[ER%ld]: Could not Create Shut Service on %s.\n", ++ERRCOUNT,UnicodeToPrintfString(SameSubNetDomInfo.lpMInfo->wcMachineName));
+ PrintString(TOALL, PrintBuf);
+ sprintf(PrintBuf,"\n\nERROR: Multiple domain tests not done on Same Subnet!\n");
+ PrintString(TOALL, PrintBuf);
+ return FALSE;
+ }
+
+ sprintf(PrintBuf,"\nSUCCESS[SC%ld]: Multiple domain tests on same subnet.\n", ++SUCSCOUNT);
+ PrintString(TOALL, PrintBuf);
+
+ return TRUE;
+}
+
+
+NET_API_STATUS
+StartShutService(LPTSTR wcMachineName,
+ DWORD dwArgument)
+
+{
+HANDLE schService;
+HANDLE schSCManager;
+DWORD LastError;
+CHAR cArgument[5];
+TCHAR wcArgument[5];
+LPTSTR lpszPtrStr[1];
+SERVICE_STATUS ssServiceStatus;
+
+
+ schSCManager = OpenSCManager(
+ wcMachineName,
+ NULL,
+ SC_MANAGER_ALL_ACCESS);
+
+ if(schSCManager == (HANDLE)NULL){
+ sprintf(PrintBuf,"\n\nCannot Access the service controller on %s for Starting. \nError %s\n",
+ UnicodeToPrintfString(wcMachineName), get_error_text(GetLastError()));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+
+ return (!NERR_Success);
+ }
+
+
+ schService = OpenService(schSCManager, SHUTSVCNAME, SERVICE_ALL_ACCESS);
+ if(schService == (HANDLE)NULL){
+ sprintf(PrintBuf,"\n\nCannot Open Shut service on %s for Starting. \nError %s\n",
+ UnicodeToPrintfString(wcMachineName), get_error_text(GetLastError()));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ CloseHandle(schSCManager);
+ return (!NERR_Success);
+ }
+
+ sprintf(cArgument, "%ld", dwArgument);
+ MultiByteToWideChar(CP_OEMCP, MB_PRECOMPOSED, cArgument, strlen(cArgument)+1, wcArgument, sizeof(wcArgument));
+ lpszPtrStr[0] = wcArgument;
+
+ if(!StartService(schService,
+ 1,
+ lpszPtrStr)){
+
+ LastError = GetLastError();
+ if(!(LastError == NERR_ServiceInstalled || LastError == ERROR_SERVICE_ALREADY_RUNNING)){
+ sprintf(PrintBuf,"\n\nCannot Start Shut service on %s. \nError %s\n",
+ UnicodeToPrintfString(wcMachineName), get_error_text(GetLastError()));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+
+ CloseHandle(schSCManager);
+ CloseHandle(schService);
+ return (!NERR_Success);
+ } else {
+ //
+ // Service is started, stop it and restart it.
+ //
+ if(!ControlService(schService,
+ SERVICE_CONTROL_STOP,
+ &ssServiceStatus)){
+ sprintf(PrintBuf,"\n\nCannot Stop Shut service before starting on %s. \nError %s\n",
+ UnicodeToPrintfString(wcMachineName), get_error_text(GetLastError()));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+
+ CloseHandle(schSCManager);
+ CloseHandle(schService);
+ return (!NERR_Success);
+ }
+
+ //
+ // Wait for the SVCDataBase to be updated
+ //
+ Sleep(15000);
+
+ if(!StartService(schService,
+ 1,
+ lpszPtrStr)){
+
+ sprintf(PrintBuf,"\n\nCannot Start Shut service after Stopping on %s. \nError %s\n",
+ UnicodeToPrintfString(wcMachineName), get_error_text(GetLastError()));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+
+ CloseHandle(schSCManager);
+ CloseHandle(schService);
+ return (!NERR_Success);
+ }
+ }
+ }
+
+ CloseHandle(schSCManager);
+ CloseHandle(schService);
+
+ return (NERR_Success);
+}
+
+
+NET_API_STATUS
+StopShutService(LPTSTR wcMachineName)
+{
+HANDLE schService;
+HANDLE schSCManager;
+SERVICE_STATUS ssServiceStatus;
+
+ schSCManager = OpenSCManager(
+ wcMachineName,
+ NULL,
+ SC_MANAGER_ALL_ACCESS);
+
+ if(schSCManager == (HANDLE)NULL){
+ sprintf(PrintBuf,"\n\nCannot Access the service controller on %s for Stopping. \nError %s\n",
+ UnicodeToPrintfString(wcMachineName), get_error_text(GetLastError()));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+
+ return (!NERR_Success);
+ }
+
+
+ schService = OpenService(schSCManager, SHUTSVCNAME, SERVICE_ALL_ACCESS);
+ if(schService == (HANDLE)NULL){
+ sprintf(PrintBuf,"\n\nCannot Open Shut service on %s for Stopping. \nError %s\n",
+ UnicodeToPrintfString(wcMachineName), get_error_text(GetLastError()));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+
+ return (!NERR_Success);
+ }
+
+ if(!ControlService(schService,
+ SERVICE_CONTROL_STOP,
+ &ssServiceStatus)){
+ if(GetLastError() != ERROR_SERVICE_NOT_ACTIVE){
+ sprintf(PrintBuf,"\n\nCannot Stop Shut service on %s. \nError %s\n",
+ UnicodeToPrintfString(wcMachineName), get_error_text(GetLastError()));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+
+ return (!NERR_Success);
+ }
+ }
+
+
+ CloseHandle(schSCManager);
+ CloseHandle(schService);
+
+ return (NERR_Success);
+}
+
+
+VOID
+TestPrimAndOthDoms(DOMAININFO DomainInfo,
+ XPORTINFO *TestedXportInfo,
+ INT iNumOfTestedXports,
+ TCHAR wcCurrentMasters[MAXPROTOCOLS][CNLEN+1],
+ BOOL bTEST_TCP)
+{
+ sprintf(PrintBuf, "\n\nShut down and restart RDR, BROWSER etc on %s\n", UnicodeToPrintfString(DomainInfo.lpMInfo->wcMachineName));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+
+
+ if(StartShutService(DomainInfo.lpMInfo->wcMachineName, STOPANDSTARTRDR) != NERR_Success){
+ sprintf(PrintBuf,"\n\nERROR[ER%ld]: Could not Start Shut Service on %s.\n",
+ ++ERRCOUNT,UnicodeToPrintfString(DomainInfo.lpMInfo->wcMachineName));
+ PrintString(TOALL, PrintBuf);
+ sprintf(PrintBuf,"\n\nERROR: Multiple domain tests not done on Same Subnet!\n");
+ PrintString(TOALL, PrintBuf);
+ return;
+ }
+
+ //
+ // Sleep for 4 minutes so that the Stopped Server is back.
+ // RDR and browser will come up in 2 minutes, Allow time
+ // for the browser to be updated.
+ //
+ sprintf(PrintBuf, "\n\nSleeping for %ld msecs.\n", BASESLEEPTIME*8);
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ Sleep(BASESLEEPTIME * 8);
+
+ //
+ // Check for the other domain info on current masters of Prim domain.
+ //
+ CheckSrvListOnPrimaryDom(DomainInfo, TestedXportInfo, iNumOfTestedXports, wcCurrentMasters, bTEST_TCP);
+
+ //
+ // Force announce or wait for the primary domain to announce
+ // itself.
+ //
+ if(!bForceAnn) {
+ sprintf(PrintBuf, "\nSleeping for %ld msecs.\n", UPDATESLEEPTIME);
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ Sleep(UPDATESLEEPTIME);
+
+ } else {
+ //
+ // Ask the machines in the domain to announce themselves and
+ // sleep for sometime so the elections and things happen
+ //
+ ForceAnnounce(TestedXportInfo[0].Transport, wcTESTEDDOMAINS[0]);
+
+ sprintf(PrintBuf, "\nForceAnnounce. Sleeping for %ld msecs.\n", BASESLEEPTIME*2);
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ Sleep(BASESLEEPTIME*2);
+ }
+
+
+ //
+ // Check the list on other domain for the primary domain info.
+ //
+ CheckSrvListOnOtherDom(DomainInfo, TestedXportInfo, iNumOfTestedXports, wcCurrentMasters, bTEST_TCP);
+
+
+ StopShutService(DomainInfo.lpMInfo->wcMachineName);
+
+}
diff --git a/private/net/svcdlls/browser/browtest/src/browmdom.h b/private/net/svcdlls/browser/browtest/src/browmdom.h
new file mode 100644
index 000000000..0cd884752
--- /dev/null
+++ b/private/net/svcdlls/browser/browtest/src/browmdom.h
@@ -0,0 +1,24 @@
+#include "browfunc.h"
+#include "browutil.h"
+#include "browtest.h"
+
+
+
+VOID CheckSrvListOnPrimaryDom(DOMAININFO, XPORTINFO *, INT,
+ TCHAR [MAXPROTOCOLS][CNLEN+1], BOOL);
+VOID CheckSrvListOnOtherDom(DOMAININFO, XPORTINFO *, INT,
+ TCHAR [MAXPROTOCOLS][CNLEN+1], BOOL);
+NET_API_STATUS CreateShutService(LPTSTR);
+VOID DeleteShutService(LPTSTR);
+VOID DoMulDomMulSubNetTests(XPORTINFO *, INT);
+VOID FindTheCurrentMasters(LPTSTR, XPORTINFO *,
+ INT, TCHAR [MAXPROTOCOLS][CNLEN+1]);
+
+BOOL MulDomSameSubnetTest(DOMAININFO, XPORTINFO *, INT);
+BOOL MulDomDiffSubnetTest(DOMAININFO, XPORTINFO *, INT);
+NET_API_STATUS StartShutService(LPTSTR, DWORD);
+NET_API_STATUS StopShutService(LPTSTR);
+VOID TestPrimAndOthDoms(DOMAININFO, XPORTINFO *, INT,
+ TCHAR [MAXPROTOCOLS][CNLEN+1], BOOL);
+
+
diff --git a/private/net/svcdlls/browser/browtest/src/browstrs.c b/private/net/svcdlls/browser/browtest/src/browstrs.c
new file mode 100644
index 000000000..93e543de9
--- /dev/null
+++ b/private/net/svcdlls/browser/browtest/src/browstrs.c
@@ -0,0 +1,534 @@
+#include "browstrs.h"
+
+
+extern CHAR PrintBuf[1024];
+extern MACHINEINFO HeadList1;
+
+extern TCHAR wcTESTEDDOMAINS[MAXTESTEDDOMAINS][DNLEN+1];
+extern INT iNumOfTestedDomains;
+extern FILE *fplog;
+extern FILE *fpsum;
+
+extern HANDLE ConsoleMutex;
+extern TCHAR *TRANSPORTS[MAXPROTOCOLS];
+extern enum E_PROTOCOLS {IPX, NBIPX, TCP, XNS, NETBEUI};
+
+extern BOOL bIPXHack;
+
+extern DWORD dwStressTest;
+
+ /*************************************************\
+ * *
+ * Note: Don't use UnicodeToPrintfString() in the *
+ * threads!! *
+ * *
+ \*************************************************/
+
+//
+// This is the main function for the browser stress test.
+// This test creates two threads. One thread stops the browser service on
+// some machine in the list. Other thread finds the master browser, backup
+// browser and try to retrieve lists from it. The main threads sleeps for some
+// time and wakesup and kills the threads. Then it starts back all the browsers.
+//
+BOOL
+StartBrowserStressTests(UNICODE_STRING *TransportNames, INT iNumOfTransports)
+{
+INT i, j;
+DWORD dwThreadId;
+HANDLE hStrStopThread;
+HANDLE hListVwThread;
+THREADDATA ThreadData;
+LPMACHINEINFO lpMachineInfo;
+XPORTINFO *Xports;
+
+
+ srand((unsigned)time(NULL));
+
+ if((Xports = (XPORTINFO *)calloc(iNumOfTransports, sizeof(XPORTINFO))) == NULL){
+ sprintf(PrintBuf,"\n\nCould not allocate space for transports.\n");
+ PrintString(TOALL, PrintBuf);
+ return FALSE;
+ }
+
+ for(i=0; i< iNumOfTransports; i++){
+ Xports[i].Transport = TransportNames[i];
+ }
+
+ //
+ //Find the index of the transport;
+ //
+ for(i=0; i< iNumOfTransports; i++){
+ for(j=0; j < MAXPROTOCOLS
+ && wcsstr(Xports[i].Transport.Buffer, TRANSPORTS[j]) == NULL; j++);
+ if(j >= MAXPROTOCOLS){
+ sprintf(PrintBuf,"\n\nUnknown transport %s!\n",
+ UnicodeToPrintfString(Xports[i].Transport.Buffer));
+ PrintString(TOALL, PrintBuf);
+ return FALSE;
+ }
+ Xports[i].index = j;
+ }
+
+
+ sprintf(PrintBuf,"\n\nTested Transports are:\n");
+ PrintString(TOALL, PrintBuf);
+ for(i=0; i< iNumOfTransports; i++){
+ sprintf(PrintBuf,"Tested Transports[%d] = %-25s Index=%d\n", i+1,
+ UnicodeToPrintfString(Xports[i].Transport.Buffer), Xports[i].index);
+ PrintString(TOALL, PrintBuf);
+ }
+
+
+ //
+ // Check whether the browser service has been started on all NT machines
+ //
+ if(!CheckBrServiceOnMachinesInList())
+ return FALSE;
+
+ //
+ // Sleeping for some time
+ //
+ sprintf(PrintBuf, "\nSleeping for %ld msecs.\n", BASESLEEPTIME*2);
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ Sleep(BASESLEEPTIME*2);
+
+ ThreadData.Xports = Xports;
+ ThreadData.iNumOfTransports = iNumOfTransports;
+
+ //
+ // find all tested domains
+ //
+ for(lpMachineInfo=HeadList1.Next; lpMachineInfo;
+ lpMachineInfo=lpMachineInfo->Next){
+
+ //
+ // If the domain is not the same as any of them found
+ //
+ for(i=0; (i < iNumOfTestedDomains) &&
+ _wcsicmp(lpMachineInfo->wcDomainName, wcTESTEDDOMAINS[i]) != 0; i++);
+
+ if(i >= iNumOfTestedDomains){
+ //
+ // This domain is not in the group found.
+ //
+ if(iNumOfTestedDomains < MAXTESTEDDOMAINS){
+
+ wcscpy(wcTESTEDDOMAINS[iNumOfTestedDomains++], lpMachineInfo->wcDomainName);
+
+ } else {
+ sprintf(PrintBuf,"\n\nERROR: Only a maximum of %ld Domains can be specified in the input file.\n",
+ MAXTESTEDDOMAINS);
+ PrintString(TOALL, PrintBuf);
+ }
+
+ } // if (i< NumOfTestedDomains)
+
+ } // for lpMachineInfo
+
+
+ //
+ // Create the thread that does the starting and stopping of browser.
+ //
+ if((hStrStopThread = CreateThread(NULL,
+ 0,
+ (LPTHREAD_START_ROUTINE)StopStartThread,
+ &ThreadData,
+ 0,
+ &dwThreadId)) == NULL){
+
+ sprintf(PrintBuf,"\n\nERROR: Create Thread! Start Stop Thread.\nError: %s\n"
+ , get_error_text(GetLastError()));
+ PrintString(TOALL, PrintBuf);
+ free(Xports);
+ return FALSE;
+ }
+
+ //
+ // Create the thread that retrieves lists from other machines, forces
+ // elections, find master etc.
+ //
+ if((hListVwThread = CreateThread(NULL,
+ 0,
+ (LPTHREAD_START_ROUTINE)ListViewThread,
+ &ThreadData,
+ 0,
+ &dwThreadId)) == NULL){
+
+ sprintf(PrintBuf,"\n\nERROR: Create Thread! List viewer Thread. \nError: %s\n",
+ get_error_text(GetLastError()));
+ PrintString(TOALL, PrintBuf);
+ free(Xports);
+ return FALSE;
+ }
+
+ //
+ // Sleep for the time specified in command line
+ //
+ dwStartTime = GetTickCount();
+ sprintf(PrintBuf, "\nMain Thread:Sleeping for %ld Minutes.\n", dwStressTest);
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ Sleep(dwStressTest*60*1000);
+
+ //
+ // Terminate Thread for Starting and stopping browser.
+ //
+ if(!TerminateThread(hStrStopThread, ERROR_SUCCESS)){
+ sprintf(PrintBuf,"\n\nERROR: Terminate Thread! Start Stop Thread. \nError: %s\n",
+ get_error_text(GetLastError()));
+ PrintString(TOALL, PrintBuf);
+ free(Xports);
+ return FALSE;
+ }
+
+ //
+ // Terminate Thread for viewing browse list.
+ //
+ if(!TerminateThread(hListVwThread, ERROR_SUCCESS)){
+ sprintf(PrintBuf,"\n\nERROR: Terminate Thread! Start Stop Thread. \nError: %s\n",
+ get_error_text(GetLastError()));
+ PrintString(TOALL, PrintBuf);
+ free(Xports);
+ return FALSE;
+ }
+
+
+ sprintf(PrintBuf, "\nMain Thread: Stress test completed successfully! Time %ld Minutes\n", dwStressTest);
+ PrintString(TOALL, PrintBuf);
+
+ sprintf(PrintBuf, "\nSleeping for %ld msecs.\n", BASESLEEPTIME);
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ Sleep(BASESLEEPTIME);
+
+ //
+ // Start all the browser back.
+ //
+ for(lpMachineInfo = HeadList1.Next; lpMachineInfo; lpMachineInfo = lpMachineInfo->Next)
+ StartBrowserService(lpMachineInfo->wcMachineName);
+
+
+ //
+ // Free the transport Name Buffer
+ //
+ for(i=0; i< iNumOfTransports; i++) free(TransportNames[i].Buffer);
+
+ CloseHandle(hStrStopThread);
+ CloseHandle(hListVwThread);
+ free(Xports);
+ return TRUE;
+}
+
+
+//
+// This routine is executed by a thread which randomly, finnds the MBR, BBR
+// on some transport and retrieve the lists from them. It forces election
+// randomly.
+//
+VOID
+ListViewThread(THREADDATA *ThreadData)
+{
+INT iXportIndex;
+INT iDomIndex;
+INT iNumOfTransports;
+INT iPrintCount = 1;
+DWORD dwEntriesInList;
+DWORD dwTotalEntries;
+DWORD dwCurrentTime;
+PVOID pvServerList;
+TCHAR *pwcDomain;
+STATS stSuccs;
+STATS stFails;
+NET_API_STATUS Status;
+XPORTINFO *Xports;
+
+ memset(&stSuccs, 0, sizeof(STATS));
+ memset(&stFails, 0, sizeof(STATS));
+
+
+ Xports = ThreadData->Xports;
+ iNumOfTransports = ThreadData->iNumOfTransports;
+
+ do{
+ iXportIndex = rand() % iNumOfTransports;
+ iDomIndex = rand() % iNumOfTestedDomains;
+
+ pwcDomain = wcTESTEDDOMAINS[iDomIndex];
+
+
+ //
+ // Find Master and retrieve list from it.
+ //
+ {
+ TCHAR wcMasterName[CNLEN+1];
+
+ //
+ // Find the master on a protocol
+ //
+ if((Status = GetMasterName(Xports, iNumOfTransports,
+ iXportIndex, pwcDomain, wcMasterName)) == NERR_Success){
+
+ stSuccs.dwGM++;
+
+ //
+ // Retrieve the Server list from it.
+ //
+ if((Xports[iXportIndex].index == IPX) && bIPXHack){
+
+ Status = RetrieveList(wcMasterName, L"\\Device\\NwLnkNb",
+ &pvServerList, &dwEntriesInList, &dwTotalEntries,
+ SV_TYPE_ALTERNATE_XPORT, NULL, NULL, FALSE);
+ } else {
+ Status = RetrieveList(wcMasterName, Xports[iXportIndex].Transport.Buffer,
+ &pvServerList, &dwEntriesInList, &dwTotalEntries,
+ SV_TYPE_ALL, NULL, NULL, FALSE);
+ }
+
+ if(Status)
+ stSuccs.dwSrvMs++;
+ else
+ stFails.dwSrvMs++;
+
+ NetApiBufferFree(pvServerList);
+
+ //
+ // Retrieve the domain list
+ //
+ if((Xports[iXportIndex].index == IPX) && bIPXHack){
+
+ Status = RetrieveList(wcMasterName, L"\\Device\\NwLnkNb",
+ &pvServerList, &dwEntriesInList, &dwTotalEntries,
+ SV_TYPE_DOMAIN_ENUM | SV_TYPE_ALTERNATE_XPORT, NULL, NULL, FALSE);
+ } else {
+ Status = RetrieveList(wcMasterName, Xports[iXportIndex].Transport.Buffer,
+ &pvServerList, &dwEntriesInList, &dwTotalEntries,
+ SV_TYPE_DOMAIN_ENUM | SV_TYPE_ALL, NULL, NULL, FALSE);
+ }
+
+ if(Status)
+ stSuccs.dwDomMs++;
+ else
+ stFails.dwDomMs++;
+
+ NetApiBufferFree(pvServerList);
+
+ } else {
+ stFails.dwGM++;
+ }
+
+
+ } // End of Get master
+
+ //
+ // Sleep for some time
+ //
+ Sleep(rand() % BASESLEEPTIME);
+
+
+ //
+ // Find the backUp list
+ //
+ {
+ INT i;
+ TCHAR wcBackUpBrowsers[MAXBACKUPS][CNSLASHLEN+1];
+ ULONG ulNumBackUps;
+
+ if((Status = GetBList(Xports[iXportIndex].Transport, pwcDomain, TRUE,
+ &ulNumBackUps, wcBackUpBrowsers)) == NERR_Success) {
+
+ stSuccs.dwGB++;
+
+ //
+ // Retrieve the server list from one backup browser
+ //
+ i = rand() % ulNumBackUps;
+
+ if((Xports[iXportIndex].index == IPX) && bIPXHack){
+
+ Status = RetrieveList(wcBackUpBrowsers[i], L"\\Device\\NwLnkNb",
+ &pvServerList, &dwEntriesInList, &dwTotalEntries,
+ SV_TYPE_ALTERNATE_XPORT, NULL, NULL, FALSE);
+ } else {
+ Status = RetrieveList(wcBackUpBrowsers[i], Xports[iXportIndex].Transport.Buffer,
+ &pvServerList, &dwEntriesInList, &dwTotalEntries,
+ SV_TYPE_ALL, NULL, NULL, FALSE);
+ }
+
+ if(Status)
+ stSuccs.dwSrvBk++;
+ else
+ stFails.dwSrvBk++;
+
+ NetApiBufferFree(pvServerList);
+
+ //
+ // Retrieve the domain list from another backup browser
+ //
+ i = rand() % ulNumBackUps;
+
+ if((Xports[iXportIndex].index == IPX) && bIPXHack){
+
+ Status = RetrieveList(wcBackUpBrowsers[i], L"\\Device\\NwLnkNb",
+ &pvServerList, &dwEntriesInList, &dwTotalEntries,
+ SV_TYPE_DOMAIN_ENUM | SV_TYPE_ALTERNATE_XPORT, NULL, NULL, FALSE);
+ } else {
+ Status = RetrieveList(wcBackUpBrowsers[i], Xports[iXportIndex].Transport.Buffer,
+ &pvServerList, &dwEntriesInList, &dwTotalEntries,
+ SV_TYPE_DOMAIN_ENUM | SV_TYPE_ALL, NULL, NULL, FALSE);
+ }
+
+ if(Status)
+ stSuccs.dwDomBk++;
+ else
+ stFails.dwDomBk++;
+
+ NetApiBufferFree(pvServerList);
+
+ } else
+ stFails.dwGB++;
+
+ } // Get backUp List
+
+ //
+ // Force an election randomly
+ //
+ if(rand() % 2)
+ Elect(Xports[iXportIndex].Transport, pwcDomain);
+
+
+ //
+ // Sleep for some time
+ //
+ Sleep(rand() % BASESLEEPTIME);
+
+ if((iPrintCount % 5) == 0){
+ dwCurrentTime = GetTickCount();
+ sprintf(PrintBuf, "\nView Thread: Running for Mins:%ld, Secs:%ld", ((dwCurrentTime - dwStartTime)/60000), ((dwCurrentTime - dwStartTime)%60000)/1000);
+ PrintString(TOALL, PrintBuf);
+ sprintf(PrintBuf, "\nView Thread: SUCCESS: GM %ld, GB %ld, SrvMs %ld, DomMs %ld, SrvBk %ld DomBk %ld",
+ stSuccs.dwGM, stSuccs.dwGB, stSuccs.dwSrvMs, stSuccs.dwDomMs,
+ stSuccs.dwSrvBk, stSuccs.dwDomBk);
+ PrintString(TOALL, PrintBuf);
+ sprintf(PrintBuf, "\nView Thread: FAILURE: GM %ld, GB %ld, SrvMs %ld, DomMs %ld, SrvBk %ld DomBk %ld\n",
+ stFails.dwGM, stFails.dwGB, stFails.dwSrvMs, stFails.dwDomMs,
+ stFails.dwSrvBk, stFails.dwDomBk);
+ PrintString(TOALL, PrintBuf);
+
+ iPrintCount = 1;
+
+ } else
+ iPrintCount++;
+
+ }while(1);
+
+}
+
+
+
+//
+// This routine is executed by a thread. It finds the current master on a
+// transport and stops it. It then randomly picks up a stopped browser and
+// starts it.
+//
+
+VOID
+StopStartThread(THREADDATA *ThreadData)
+{
+INT iXportIndex;
+INT iDomIndex;
+INT iNumOfTransports;
+INT iPrintCount = 1;
+DWORD dwStartCount = 0;
+DWORD dwStopCount = 0;
+DWORD dwCurrentTime;
+TCHAR *pwcDomain;
+LPMACHINEINFO lpMachineInfo;
+NET_API_STATUS Status;
+XPORTINFO *Xports;
+
+
+ Xports = ThreadData->Xports;
+ iNumOfTransports = ThreadData->iNumOfTransports;
+
+ do{
+ iXportIndex = rand() % iNumOfTransports;
+ iDomIndex = rand() % iNumOfTestedDomains;
+
+ pwcDomain = wcTESTEDDOMAINS[iDomIndex];
+
+
+ //
+ // Find Master and stop it.
+ //
+ {
+ TCHAR wcMasterName[CNLEN+1];
+
+ //
+ // Find the master on a protocol
+ //
+ if((Status = GetMasterName(Xports, iNumOfTransports,
+ iXportIndex, pwcDomain, wcMasterName)) == NERR_Success){
+
+ for(lpMachineInfo = HeadList1.Next; lpMachineInfo &&
+ (wcscmp(lpMachineInfo->wcMachineName, wcMasterName) != 0);
+ lpMachineInfo = lpMachineInfo->Next);
+
+ if(lpMachineInfo && IsNTMachine(lpMachineInfo)){
+ if(StopBrowserService(wcMasterName) == NERR_Success){
+ lpMachineInfo->BrowserServiceStarted = FALSE;
+ dwStopCount++;
+ Sleep(rand() % BASESLEEPTIME);
+ }
+ }
+ }
+ }
+
+ Sleep(BASESLEEPTIME);
+
+ //
+ // Start a stopped machine randomly
+ //
+ if(rand() % 2){
+ INT iBrStoppedMachines = 0;
+ INT iBrStartMachine;
+
+ for(lpMachineInfo = HeadList1.Next; lpMachineInfo;
+ lpMachineInfo = lpMachineInfo->Next)
+ if(IsNTMachine(lpMachineInfo) && lpMachineInfo->BrowserServiceStarted == FALSE)
+ iBrStoppedMachines++;
+
+ if(iBrStoppedMachines){
+ iBrStartMachine = (rand() % iBrStoppedMachines)+1;
+
+ for(lpMachineInfo = HeadList1.Next; lpMachineInfo;
+ lpMachineInfo = lpMachineInfo->Next)
+ if(IsNTMachine(lpMachineInfo) && lpMachineInfo->BrowserServiceStarted == FALSE){
+ iBrStartMachine--;
+ if(!iBrStartMachine)
+ if(StartBrowserService(lpMachineInfo->wcMachineName) == NERR_Success){
+ lpMachineInfo->BrowserServiceStarted = TRUE;
+ dwStartCount++;
+ Sleep(rand() % BASESLEEPTIME);
+ }
+ }
+ }
+
+ }
+
+ Sleep(BASESLEEPTIME*2);
+
+ if((iPrintCount % 5) == 0){
+ dwCurrentTime = GetTickCount();
+ sprintf(PrintBuf, "\nStartStop Thread: Running for Mins:%ld Secs:%ld", ((dwCurrentTime - dwStartTime)/60000), ((dwCurrentTime - dwStartTime)%60000)/1000);
+ PrintString(TOALL, PrintBuf);
+ sprintf(PrintBuf, "\nStartStop Thread: SUCCESS: Start %ld, Stop %ld\n", dwStartCount, dwStopCount);
+ PrintString(TOALL, PrintBuf);
+ iPrintCount = 1;
+
+ } else
+ iPrintCount++;
+
+
+
+ }while(1);
+
+}
diff --git a/private/net/svcdlls/browser/browtest/src/browstrs.h b/private/net/svcdlls/browser/browtest/src/browstrs.h
new file mode 100644
index 000000000..1e88ca2ab
--- /dev/null
+++ b/private/net/svcdlls/browser/browtest/src/browstrs.h
@@ -0,0 +1,24 @@
+#include "browutil.h"
+#include "browtest.h"
+#include <time.h>
+
+typedef struct _ThreadData{
+ XPORTINFO *Xports;
+ INT iNumOfTransports;
+ }THREADDATA;
+
+typedef struct _Stats{
+ DWORD dwGM;
+ DWORD dwGB;
+ DWORD dwSrvMs;
+ DWORD dwDomMs;
+ DWORD dwSrvBk;
+ DWORD dwDomBk;
+ }STATS;
+
+DWORD dwStartTime;
+
+BOOL StartBrowserStressTests(UNICODE_STRING *, INT);
+VOID ListViewThread(THREADDATA *);
+VOID StopStartThread(THREADDATA *);
+
diff --git a/private/net/svcdlls/browser/browtest/src/browtest.c b/private/net/svcdlls/browser/browtest/src/browtest.c
new file mode 100644
index 000000000..e4d21e6ce
--- /dev/null
+++ b/private/net/svcdlls/browser/browtest/src/browtest.c
@@ -0,0 +1,1947 @@
+/**
+
+Browser Tester
+==============
+
+Author: Anil F. Thomas
+Date: 9/30/94
+
+
+Function: Browser test involves 2 parts.
+ 1. Functional Testing.
+ 2. Stress Testing.
+
+ The browser service is tested on the protocols installed on the
+ current machine, and what are specified to be tested in the input
+ file. The functional test can be broadly classified as the
+ following
+
+ a) Browser test in a domain spanning a single subnet.
+ b) Browser test in a domain spanning 2 subnets.
+ c) Browser test in 2 domains in a subnet.
+ d) Browser test in 2 domains in 2 subnets.
+
+ When multiple subnets are involved the difference is only in TCP/IP
+ transport.
+
+ Test a) involves finding the master browser, backup browsers,
+ retrieving the browse lists from them and comparing with the input
+ file info, forcing elections etc. Browser on each of the masters
+ in the primary domain is shut down one after the other and the
+ lists are checked.
+
+ Test b) involves TCP with a domain spanning 2 subnets. There are
+ 2 masters and the PDC is the domain master.
+
+ Test c) involves checking whether the PDC of second domain gets the
+ information on the first domain and vice versa. Browser on each of
+ the masters in the primary domain is shut down one after the other
+ and the second PDC and the master of the primary domain are tested.
+ Before testing the other PDC, the RDR and Browser on that machine
+ is shutdown and restarted on that machine. This ensures that a new
+ Browse list is created by it.
+
+ Test d) is similar to test c. However for TCP, the PDC has to be
+ up for the information to be obtained on each subnets.
+
+ Since in Daytona, rdr is not bound to IPX, we have to use a hack
+ to find the master browser and retrive browse list on that protocol.
+
+ Note:
+ To run this test
+ a) You need administrative privileges to all the NT machines
+ specified in the input file.
+ b) The current machine should not be multihomed.
+ c) ShutSvc.exe should be in %SystemRoot% of the PDC of other
+ domains.
+
+
+**/
+
+
+#include "browtest.h"
+#include "browglob.h" // Has the global variables
+#include "browmdom.h"
+#include "browutil.h"
+#include "browstrs.h"
+
+void
+_CRTAPI1
+main(int argc, char *argv[])
+{
+UNICODE_STRING TransportNames[MAXTRANSPORTS];
+INT iNumOfTransports;
+
+
+ HeadList1.Next = NULL;
+
+ ConsoleMutex = CreateMutex( NULL, FALSE, NULL );
+ if(ConsoleMutex == NULL ) {
+ printf("\nCould not create a console mutex\n");
+ exit(1);
+ }
+
+ ParseCommandLine(argc, argv);
+
+
+ Initialize(TransportNames, &iNumOfTransports);
+
+
+ if(!dwStressTest){
+ //
+ // Start the browser functional testing
+ //
+ if(!StartBrowserFunctionalTest(TransportNames, iNumOfTransports)){
+ CloseHandle(ConsoleMutex);
+ fclose(fplog);
+ fclose(fpsum);
+ CleanMem();
+ exit(1);
+ }
+
+ sprintf(PrintBuf,"\n\n\n\t\t\tSUMMARY\n\t\t\t=======\n\n");
+ PrintString(TOALL, PrintBuf);
+ sprintf(PrintBuf,"\n\tERRORS : %ld" , ERRCOUNT);
+ PrintString(TOALL, PrintBuf);
+ sprintf(PrintBuf,"\n\tWARNINGS : %ld" , WRNCOUNT);
+ PrintString(TOALL, PrintBuf);
+ sprintf(PrintBuf,"\n\tOK : %ld" , OKCOUNT);
+ PrintString(TOALL, PrintBuf);
+ sprintf(PrintBuf,"\n\tSUCCESS : %ld\n", SUCSCOUNT);
+ PrintString(TOALL, PrintBuf);
+ sprintf(PrintBuf,"\n\n================================================================================\n");
+ PrintString(TOALL, PrintBuf);
+
+ } else {
+
+ //
+ // Start browser stress tests
+ //
+ if(!StartBrowserStressTests(TransportNames, iNumOfTransports)){
+ sprintf(PrintBuf,"\nStress test encountered Errors!\n");
+ PrintString(TOALL, PrintBuf);
+ }
+ }
+
+CloseHandle(ConsoleMutex);
+fclose(fplog);
+fclose(fpsum);
+CleanMem();
+}
+
+
+
+//
+// Add an element to the linked list of machines.
+// The machines in a domain are grouped together.
+// They are arranged in decreasing order of OS type.
+//
+
+VOID
+AddToList(LPMACHINEINFO Head,
+ LPMACHINEINFO lpMachineInfo)
+{
+LPMACHINEINFO lpTmpInfo;
+LPMACHINEINFO lpTmpPrevInfo;
+
+ //
+ // Find the start of this domain group.
+ //
+ for(lpTmpPrevInfo = Head, lpTmpInfo = Head->Next; lpTmpInfo &&
+ _wcsicmp(lpMachineInfo->wcDomainName, lpTmpInfo->wcDomainName) != 0;
+ lpTmpPrevInfo = lpTmpInfo, lpTmpInfo = lpTmpInfo->Next);
+
+ if(!lpTmpInfo){ // Reached end of list
+ lpTmpPrevInfo->Next = lpMachineInfo;
+ lpMachineInfo->Next = NULL;
+ } else {
+ //
+ // Put it in a slot matching its OS type
+ //
+ for(; lpTmpInfo && _wcsicmp(lpMachineInfo->wcDomainName,
+ lpTmpInfo->wcDomainName) == 0
+ && (lpMachineInfo->iOsType < lpTmpInfo->iOsType);
+ lpTmpPrevInfo = lpTmpInfo, lpTmpInfo = lpTmpInfo->Next);
+
+ if(!lpTmpInfo){ // Reached end of list
+ lpTmpPrevInfo->Next = lpMachineInfo;
+ lpMachineInfo->Next = NULL;
+ } else {
+ lpMachineInfo->Next = lpTmpPrevInfo->Next;
+ lpTmpPrevInfo->Next = lpMachineInfo;
+ }
+ }
+}
+
+
+
+//
+// Check access permissions on all the servers by trying to stop
+// browser on them.
+//
+
+BOOL
+CheckAccessPermissionOnAllMachines()
+{
+BOOL ALLOK = TRUE;
+DWORD Status;
+LPMACHINEINFO lpMachineInfo;
+
+
+
+ for(lpMachineInfo = HeadList1.Next; lpMachineInfo; lpMachineInfo = lpMachineInfo->Next){
+ if(IsNTMachine(lpMachineInfo)){
+ if((Status = StopBrowserService(lpMachineInfo->wcMachineName)) != NERR_Success){
+ sprintf(PrintBuf,"\nERROR: Stopping Browser on %s.\nError: %s\n", UnicodeToPrintfString(lpMachineInfo->wcMachineName), get_error_text(Status));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ ALLOK = FALSE;
+ }
+ }
+ }
+
+
+ if(!ALLOK){
+ sprintf(PrintBuf,"\n\nI need administrative privileges to some of the \
+ \nmachines as listed above. Please get the access \
+ \nand restart the test!!!\n\n");
+ PrintString(TOSCREEN, PrintBuf);
+ } else {
+ sprintf(PrintBuf,"\n\nI have the necessary priveleges!!\n");
+ PrintString(TOSCREEN, PrintBuf);
+ Sleep(3000);
+ }
+
+ return ALLOK;
+}
+
+
+
+//
+// Retrieve the browse lists from the master and the backups. The list
+// from the master is compared against the input file data. The list
+// from the backup is compared against the master.
+//
+
+VOID
+CheckBrowseListsOfMasterAndBackUps(XPORTINFO XportInfo,
+ LPTSTR wcDomainName,
+ LPTSTR wcMasterName,
+ TCHAR wcBackUpBrowsers[MAXBACKUPS][CNSLASHLEN+1],
+ ULONG ulNumBackUps,
+ BOOL bSingleSubnet)
+{
+INT i, index;
+DWORD dwEntriesInList;
+DWORD dwTotalEntries;
+DWORD dwEntriesInBackUpList;
+DWORD dwTotalBackUpEntries;
+PVOID pvMsServerList;
+PVOID pvBkServerList;
+PSERVER_INFO_101 pServerInfo101_l1;
+NET_API_STATUS Status;
+
+
+ //
+ // Get the server list from the Master Browser
+ //
+ // For IPX we need a Hack. We can retrieve the List only through
+ // NwLnkNb. If flag 0x20000000 is specified we get the IPX list
+ //
+ if((XportInfo.index == IPX) && bIPXHack){
+
+ Status = RetrieveList(wcMasterName, L"\\Device\\NwLnkNb",
+ &pvMsServerList, &dwEntriesInList, &dwTotalEntries,
+ SV_TYPE_ALTERNATE_XPORT, NULL, NULL, TRUE);
+
+ } else {
+ Status = RetrieveList(wcMasterName, XportInfo.Transport.Buffer,
+ &pvMsServerList, &dwEntriesInList, &dwTotalEntries,
+ SV_TYPE_ALL, NULL, NULL, TRUE);
+ }
+
+ if(Status == NERR_Success){
+ sprintf(PrintBuf, "\nList Retrieved from : %s",
+ UnicodeToPrintfString(wcMasterName));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ sprintf(PrintBuf, "\nThe Server List in domain %s. EntriesInList %ld. \
+ TotalEntires %ld.",
+ UnicodeToPrintfString(wcDomainName),
+ dwEntriesInList, dwTotalEntries);
+ PrintString(TOSCREENANDLOG, PrintBuf);
+
+ pServerInfo101_l1 = pvMsServerList;
+
+ for(i = 0; i< (INT)dwEntriesInList; i++){
+ sprintf(PrintBuf, "\nServer %s Type %ld",
+ UnicodeToPrintfString(pServerInfo101_l1[i].sv101_name),
+ pServerInfo101_l1[i].sv101_type);
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ }
+ }
+ sprintf(PrintBuf, "\n\n");
+ PrintString(TOSCREENANDLOG, PrintBuf);
+
+
+ //
+ // Check if the List is how it should be, by comparing it with the user's
+ // input file.
+ //
+ sprintf(PrintBuf,"\n\nTEST#: [TS%ld]:\n=============", ++TESTCOUNT);
+ PrintString(TOALL, PrintBuf);
+
+ sprintf(PrintBuf,"\nTEST: Check whether Browse List of Master matches User input.\n");
+ PrintString(TOALL, PrintBuf);
+
+ if(dwEntriesInList)
+ CompareListsMasterAndFile(pvMsServerList, dwEntriesInList,
+ wcDomainName, wcMasterName, XportInfo, bSingleSubnet);
+ else {
+ sprintf(PrintBuf,"\nERROR#: [ER%ld]:There are no entries in the list retrieved from Master: %s.\n",
+ ++ERRCOUNT, UnicodeToPrintfString(wcMasterName));
+ PrintString(TOALL, PrintBuf);
+ }
+
+ //
+ // Retrieve the list from each of the BackUp servers
+ //
+ for(index = 0; index < (INT)ulNumBackUps; index++){
+
+ //
+ // If Master browser and Backup browser are not same retrieve the list.
+ // BackUp browser name has "\\" in the beginning.
+ //
+ if(_wcsicmp(wcMasterName, &wcBackUpBrowsers[index][2]) != 0){
+
+ //
+ // For IPX we need a Hack. We can retrieve the List only through
+ // NwLnkNb. If flag 0x20000000 is specified we get the IPX list
+ //
+ if((XportInfo.index == IPX) && bIPXHack){
+
+ Status = RetrieveList(wcMasterName, L"\\Device\\NwLnkNb",
+ &pvBkServerList, &dwEntriesInBackUpList,
+ &dwTotalBackUpEntries, SV_TYPE_ALTERNATE_XPORT,
+ NULL, NULL, TRUE);
+ } else {
+
+ Status = RetrieveList(wcBackUpBrowsers[index],
+ XportInfo.Transport.Buffer, &pvBkServerList,
+ &dwEntriesInBackUpList, &dwTotalBackUpEntries,
+ SV_TYPE_ALL, NULL, NULL, TRUE);
+ }
+ if(Status == NERR_Success){
+ sprintf(PrintBuf, "\n\nList Retrieved from : %s",
+ UnicodeToPrintfString(wcBackUpBrowsers[index]));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ sprintf(PrintBuf, "\nServer List in domain %s. EntriesInList %ld. TotalEntires %ld.",
+ UnicodeToPrintfString(wcDomainName),
+ dwEntriesInList, dwTotalEntries);
+ PrintString(TOSCREENANDLOG, PrintBuf);
+
+ pServerInfo101_l1 = pvBkServerList;
+
+ for(i = 0; i< (INT)dwEntriesInBackUpList; i++){
+ sprintf(PrintBuf, "\nServer %s Type %ld",
+ UnicodeToPrintfString(pServerInfo101_l1[i].sv101_name),
+ pServerInfo101_l1[i].sv101_type);
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ }
+ }
+
+ //
+ // Compare the Master's and BackUp's Lists.
+ //
+ sprintf(PrintBuf,"\n\nTEST#: [TS%ld]:\n=============", ++TESTCOUNT);
+ PrintString(TOALL, PrintBuf);
+
+ sprintf(PrintBuf,"\nTEST: Check whether Browse List of Master And BackUps are same.\n");
+ PrintString(TOALL, PrintBuf);
+
+ if(dwEntriesInList && dwEntriesInBackUpList)
+ CompareListsMasterAndBackUp(pvMsServerList, dwEntriesInList, pvBkServerList, dwEntriesInBackUpList,
+ wcDomainName, wcMasterName, wcBackUpBrowsers[index], XportInfo.Transport.Buffer);
+ else {
+ if(!dwEntriesInBackUpList){
+ sprintf(PrintBuf,"\nERROR#: [ER%ld]:There are no entries in the list retrieved from BackUp: %s.\n",
+ ++ERRCOUNT, UnicodeToPrintfString(wcBackUpBrowsers[index]));
+ PrintString(TOALL, PrintBuf);
+ }
+ }
+
+ sprintf(PrintBuf, "\n\n");
+ PrintString(TOSCREENANDLOG, PrintBuf);
+
+ NetApiBufferFree(pvBkServerList);
+
+ } else { // If Master != BackUp
+
+ sprintf(PrintBuf, "\n\nINFO: BackUp same as Master (%s). Hence not retrieving the list from it.\n\n",
+ UnicodeToPrintfString(wcBackUpBrowsers[index]));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ }
+
+ } // for (index < ulNumBackUps)
+
+
+ NetApiBufferFree(pvMsServerList);
+}
+
+
+
+//
+// Check if browser is started on all machines in the list, if not start
+// the service.
+//
+
+BOOL
+CheckBrServiceOnMachinesInList()
+{
+BOOL ALLOK = TRUE;
+DWORD Status;
+LPMACHINEINFO lpMachineInfo;
+SERVICE_STATUS ssServiceStatus;
+
+ //
+ // Start browser on PDC first and then wait a few seconds
+ //
+ if(LocDomInfo.lpMInfo && (Status = StartBrowserService(LocDomInfo.lpMInfo->wcMachineName)) != NERR_Success){
+ sprintf(PrintBuf,"\nERROR:Starting Browser on PDC %s.\nError: %s\n", UnicodeToPrintfString(LocDomInfo.lpMInfo->wcMachineName), get_error_text(Status));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ ALLOK = FALSE;
+ } else {
+ LocDomInfo.lpMInfo->BrowserServiceStarted = TRUE;
+ }
+
+ sprintf(PrintBuf, "\n\nSleeping for %ld msecs.\n", BASESLEEPTIME*2);
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ Sleep(BASESLEEPTIME*2);
+
+
+ for(lpMachineInfo = HeadList1.Next; lpMachineInfo; lpMachineInfo = lpMachineInfo->Next){
+ if(IsNTMachine(lpMachineInfo)){
+ if((Status = QueryBrowserServiceStatus(lpMachineInfo, &ssServiceStatus)) != NERR_Success){
+ sprintf(PrintBuf,"\nERROR: Query on %s.\nError: %s", UnicodeToPrintfString(lpMachineInfo->wcMachineName), get_error_text(Status));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ ALLOK = FALSE;
+ } else {
+ if((ssServiceStatus.dwCurrentState == SERVICE_STOP_PENDING) ||
+ (ssServiceStatus.dwCurrentState == SERVICE_STOPPED)){
+
+ sprintf(PrintBuf,"\nBrowser service is not started on %s", UnicodeToPrintfString(lpMachineInfo->wcMachineName));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+
+ //
+ // Attempt to start the browser service
+ //
+ if((Status = StartBrowserService(lpMachineInfo->wcMachineName)) != NERR_Success){
+ sprintf(PrintBuf,"\nERROR:Starting Browser on %s.\nError: %s\n", UnicodeToPrintfString(lpMachineInfo->wcMachineName), get_error_text(Status));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ ALLOK = FALSE;
+ } else {
+ lpMachineInfo->BrowserServiceStarted = TRUE;
+ }
+
+ } else
+ lpMachineInfo->BrowserServiceStarted = TRUE;
+ }
+ }
+ }
+
+
+return ALLOK;
+}
+
+
+
+//
+// Cleanup the Linked list.
+//
+
+VOID
+CleanMem()
+{
+LPMACHINEINFO lpMachineInfo = NULL;
+
+ lpMachineInfo = HeadList1.Next;
+ if(lpMachineInfo) HeadList1.Next = lpMachineInfo->Next;
+
+ for(;lpMachineInfo; lpMachineInfo = HeadList1.Next){
+ HeadList1.Next = lpMachineInfo->Next;
+ free(lpMachineInfo);
+ }
+}
+
+
+
+//
+// Check if a machine specified by name or the MachineInfo data, has a
+// particular protocol.
+//
+
+BOOL
+DoesMachineHaveThisTransport(LPTSTR wcServer,
+ XPORTINFO XportInfo,
+ LPMACHINEINFO lpMachineInfo)
+{
+
+ if(lpMachineInfo == NULL){
+ //
+ // Find the server from the lists
+ //
+
+ // Check Lists 1
+ for(lpMachineInfo = HeadList1.Next; lpMachineInfo &&
+ _wcsicmp(wcServer, lpMachineInfo->wcMachineName) != 0;
+ lpMachineInfo = lpMachineInfo->Next);
+
+ if(lpMachineInfo == NULL) {
+ sprintf(PrintBuf, "ERROR[ER%ld]: Cannot find Server %s\n", ++ERRCOUNT, UnicodeToPrintfString(wcServer));
+ PrintString(TOALL, PrintBuf);
+ return FALSE;
+ }
+
+ }
+
+ //
+ // This transport is there on the specified server
+ //
+ if(lpMachineInfo->Protocols[XportInfo.index])
+ return TRUE;
+
+
+ return FALSE;
+}
+
+
+
+//
+// Test of a domain spanning two subnets. This test is for TCP.
+// The rest of the protocols are covered by Dingle domain tests.
+// If two subnets have one master browser each. They should have
+// the information on the other side if the PDC of the domain is up.
+//
+
+VOID
+DomSpanningMulSubNetsTests(XPORTINFO *TestedXportInfo,
+ INT iNumOfTestedXports)
+{
+INT i, index;
+BOOL bOtherMasterAvailable = FALSE;
+TCHAR *wcDomPDC;
+TCHAR wcOtherMaster[CNLEN+1];
+DWORD dwEntriesInList;
+DWORD dwTotalEntries;
+PVOID pvMsServerList;
+PSERVER_INFO_101 pServerInfo101_l1;
+LPMACHINEINFO lpMachineInfo;
+NET_API_STATUS Status;
+
+ //
+ // If we have TCP as a tested transport.
+ //
+ for(index = 0; (index < iNumOfTestedXports) &&
+ TestedXportInfo[index].index != TCP ; index++);
+ //
+ // If TCP is not a tested Transport, just return.
+ //
+ if(index >= iNumOfTestedXports)
+ return;
+
+ //
+ // if PDC of the domain does not have TCP then return
+ //
+ if(!(LocDomInfo.lpMInfo->Protocols[TCP]))
+ return;
+
+ //
+ // if there are no machines of the same domain in the other subnet, with TCP
+ // then return.
+ //
+ for(lpMachineInfo=lpDomStart; lpMachineInfo; lpMachineInfo = lpMachineInfo->Next){
+ if((lpMachineInfo->iSubnet == SUBNET2) && IsNTMachine(lpMachineInfo)
+ && (lpMachineInfo->Protocols[TCP]))
+ bOtherMasterAvailable = TRUE;
+ }
+
+ if(!bOtherMasterAvailable)
+ return;
+
+ //
+ // Now we know that there are machines in the domain that are across
+ // the subnet and running TCP.
+ //
+ sprintf(PrintBuf,"\n\n\n================================================================================\n");
+ PrintString(TOALL, PrintBuf);
+
+ sprintf(PrintBuf,"\n\n\t\tDOMAIN SPANNING MULTIPLE SUBNETS!\n\t\t=================================\n\n");
+ PrintString(TOALL, PrintBuf);
+
+ sprintf(PrintBuf,"\n\nTEST#: [TS%ld].\n=============", ++TESTCOUNT);
+ PrintString(TOALL, PrintBuf);
+
+ wcDomPDC = LocDomInfo.lpMInfo->wcMachineName;
+ sprintf(PrintBuf,"\nTEST: Retrieve the list of master browsers from PDC.\n",
+ UnicodeToPrintfString(wcDomPDC));
+ PrintString(TOALL, PrintBuf);
+
+ //
+ // Retrieve the list of master browsers.
+ //
+ Status = RetrieveList(wcDomPDC, TestedXportInfo[index].Transport.Buffer,
+ &pvMsServerList, &dwEntriesInList, &dwTotalEntries,
+ SV_TYPE_MASTER_BROWSER, NULL, NULL, FALSE);
+ if(Status == NERR_Success){
+ if(dwEntriesInList > 1) {
+ //
+ // There are more than one master, the other one must be in other
+ // subnet.
+ //
+ sprintf(PrintBuf,"\nSUCCESS[SC%ld]: Retrieved the list of masters. Num of Masters = %ld\n", ++SUCSCOUNT, dwEntriesInList);
+ PrintString(TOALL, PrintBuf);
+
+ pServerInfo101_l1 = pvMsServerList;
+
+ for(i = 0; (i < (INT)dwEntriesInList) &&
+ (_wcsicmp(pServerInfo101_l1[i].sv101_name,wcDomPDC) == 0); i++);
+
+ if(i < (INT)dwEntriesInList)
+ wcscpy(wcOtherMaster, pServerInfo101_l1[i].sv101_name);
+ else {
+ sprintf(PrintBuf,"\nERROR[ER%ld]: Could not find a master other than PDC in the list.\n", ++ERRCOUNT);
+ PrintString(TOALL, PrintBuf);
+
+ NetApiBufferFree(pvMsServerList);
+ return;
+ }
+
+ } else {
+ sprintf(PrintBuf,"\nERROR[ER%ld]: Num of masters in the list is less than 2. Num Of Masters = %ld\n", ++ERRCOUNT, dwEntriesInList);
+ PrintString(TOALL, PrintBuf);
+
+ NetApiBufferFree(pvMsServerList);
+ return;
+ }
+ } else {
+ sprintf(PrintBuf,"\nERROR[ER%ld]: Could not retrieve the list of masters from %s.\nError:%s\n",
+ ++ERRCOUNT, UnicodeToPrintfString(wcDomPDC), get_error_text(Status));
+ PrintString(TOALL, PrintBuf);
+
+ NetApiBufferFree(pvMsServerList);
+ return;
+ }
+
+ NetApiBufferFree(pvMsServerList);
+
+
+ //
+ // Now retrieve the list from the other master.
+ //
+ Status = RetrieveList(wcOtherMaster, TestedXportInfo[index].Transport.Buffer,
+ &pvMsServerList, &dwEntriesInList, &dwTotalEntries,
+ SV_TYPE_ALL, NULL, NULL, FALSE);
+ if(Status == NERR_Success){
+ if(dwEntriesInList > 0) {
+ //
+ // Some machines were in the list
+ //
+
+ pServerInfo101_l1 = pvMsServerList;
+
+ for(i = 0; (i < (INT)dwEntriesInList) &&
+ (_wcsicmp(pServerInfo101_l1[i].sv101_name,wcDomPDC) == 0); i++);
+
+ if(i < (INT)dwEntriesInList){
+ sprintf(PrintBuf,"\nSUCCESS[SC%ld]: PDC is found in other masters's List. Master: %s.\n",
+ ++SUCSCOUNT, UnicodeToPrintfString(wcOtherMaster));
+ PrintString(TOALL, PrintBuf);
+ } else {
+ sprintf(PrintBuf,"\nERROR[ER%ld]: Could not find the PDC in other master's List. Master: %s.\n",
+ ++ERRCOUNT, UnicodeToPrintfString(wcOtherMaster));
+ PrintString(TOALL, PrintBuf);
+
+ NetApiBufferFree(pvMsServerList);
+ return;
+ }
+
+ } else {
+ sprintf(PrintBuf,"\nERROR[ER%ld]: No machines are found in the other master's List. Master: %s.\n",
+ ++ERRCOUNT, UnicodeToPrintfString(wcOtherMaster));
+ PrintString(TOALL, PrintBuf);
+
+ NetApiBufferFree(pvMsServerList);
+ return;
+ }
+ } else {
+ sprintf(PrintBuf,"\nERROR[ER%ld]: Could not retrieve the list from other master. Master: %s. \nError: %s\n",
+ ++ERRCOUNT, UnicodeToPrintfString(wcOtherMaster), get_error_text(Status));
+ PrintString(TOALL, PrintBuf);
+
+ NetApiBufferFree(pvMsServerList);
+ return;
+ }
+
+ NetApiBufferFree(pvMsServerList);
+
+
+return;
+}
+
+
+//
+// This is the main routine which does the single domain tests.
+// The test involves, finding the master browser, backup browsers,
+// retrieving the lists from them and comparing it against the input
+// file info, forcing elections. Then the current browse master is
+// shut down and the new master browser is found. This is repeated
+// until all the
+//
+
+BOOL
+DoSingleDomainTests(XPORTINFO *TestedXportInfo,
+ INT iNumOfTestedXports)
+{
+INT i = 0;
+INT index = 0;
+ULONG ulNumBackUps;
+TCHAR *wcLocDomainName;
+TCHAR wcDomPDC[CNLEN+1];
+TCHAR wcMasterName[CNLEN +1];
+TCHAR wcBackUpBrowsers[MAXBACKUPS][CNSLASHLEN+1];
+TCHAR wcCurrentMasters[MAXPROTOCOLS][CNLEN+1];
+NET_API_STATUS Status;
+LPMACHINEINFO lpMachineInfo;
+
+
+ sprintf(PrintBuf,"\n\n================================================================================\n");
+ PrintString(TOALL, PrintBuf);
+
+ sprintf(PrintBuf,"\n\t\tSINGLE DOMAIN TESTS!\n\t\t====================\n\n");
+ PrintString(TOALL, PrintBuf);
+
+ /******************************\
+ * *
+ * Check PDC == Master Browser *
+ * *
+ \******************************/
+
+ //
+ // This part of the test will do single domain tests on the primary domain
+ // (Domain of this server)
+ //
+ wcLocDomainName = lpLocMachineInfo->wcDomainName;
+ wcscpy(wcDomPDC, LocDomInfo.lpMInfo->wcMachineName);
+ //
+ // Find out if the Master is the same as PDC on each transport
+ //
+ for(index=0; index < iNumOfTestedXports; index++){
+
+
+ /***********************************************\
+ * *
+ * Find Master Browser on each Transport *
+ * *
+ \***********************************************/
+
+ sprintf(PrintBuf,"\n--------------------------------------------------------------------------------\n");
+ PrintString(TOALL, PrintBuf);
+ sprintf(PrintBuf,"\nTransport: %s.\n",UnicodeToPrintfString(TestedXportInfo[index].Transport.Buffer));
+ PrintString(TOALL, PrintBuf);
+
+ sprintf(PrintBuf,"\n\nTEST#: [TS%ld].\n=============", ++TESTCOUNT);
+ PrintString(TOALL, PrintBuf);
+
+ sprintf(PrintBuf,"\nTEST: Check whether PDC and Master Browser are same.\n");
+ PrintString(TOALL, PrintBuf);
+
+ lstrcpy(wcMasterName, L"");
+
+ if((Status = GetMasterName(TestedXportInfo, iNumOfTestedXports, index, wcLocDomainName, wcMasterName)) != NERR_Success){
+ sprintf(PrintBuf,"\nERROR[ER%ld]: Unable to find master: Domain: %s ", ++ERRCOUNT, UnicodeToPrintfString(wcLocDomainName));
+ PrintString(TOALL, PrintBuf);
+ sprintf(PrintBuf," Transport: %s", UnicodeToPrintfString(TestedXportInfo[index].Transport.Buffer));
+ PrintString(TOALL, PrintBuf);
+ sprintf(PrintBuf," PDC: %s.\n", UnicodeToPrintfString(wcDomPDC));
+ PrintString(TOALL, PrintBuf);
+
+ }
+
+ lstrcpy(wcCurrentMasters[index], wcMasterName);
+
+ if(lstrcmp(wcDomPDC, wcMasterName) != 0){
+ //
+ // If the PDC doesnot have this transport then it is OK.
+ //
+ if(DoesMachineHaveThisTransport(wcDomPDC, TestedXportInfo[index], NULL)){
+ sprintf(PrintBuf,"\nERROR[ER%ld]: Master Browser != PDC: Domain: %s ", ++ERRCOUNT, UnicodeToPrintfString(wcLocDomainName));
+ PrintString(TOALL, PrintBuf);
+ sprintf(PrintBuf," Transport: %s.", UnicodeToPrintfString(TestedXportInfo[index].Transport.Buffer));
+ PrintString(TOALL, PrintBuf);
+ sprintf(PrintBuf," PDC: %s", UnicodeToPrintfString(wcDomPDC));
+ PrintString(TOALL, PrintBuf);
+ sprintf(PrintBuf," Master: %s.\n", UnicodeToPrintfString(wcMasterName));
+ PrintString(TOALL, PrintBuf);
+ } else {
+ sprintf(PrintBuf,"\nOK[OK%ld]: PDC not Master. (PDC doesnot have Protocol). Domain: %s ", ++OKCOUNT, UnicodeToPrintfString(wcLocDomainName));
+ PrintString(TOALL, PrintBuf);
+ sprintf(PrintBuf," Transport: %s.", UnicodeToPrintfString(TestedXportInfo[index].Transport.Buffer));
+ PrintString(TOALL, PrintBuf);
+ sprintf(PrintBuf," PDC: %s", UnicodeToPrintfString(wcDomPDC));
+ PrintString(TOALL, PrintBuf);
+ sprintf(PrintBuf," Master: %s.\n", UnicodeToPrintfString(wcMasterName));
+ PrintString(TOALL, PrintBuf);
+ }
+
+ } else {
+ sprintf(PrintBuf,"\nSUCCESS[SC%ld]:Master is PDC. Domain: %s ", ++SUCSCOUNT, UnicodeToPrintfString(wcLocDomainName));
+ PrintString(TOALL, PrintBuf);
+ sprintf(PrintBuf," Transport: %s", UnicodeToPrintfString(TestedXportInfo[index].Transport.Buffer));
+ PrintString(TOALL, PrintBuf);
+ sprintf(PrintBuf," PDC: %s", UnicodeToPrintfString(wcDomPDC));
+ PrintString(TOALL, PrintBuf);
+ sprintf(PrintBuf," Master: %s.\n", UnicodeToPrintfString(wcMasterName));
+ PrintString(TOALL, PrintBuf);
+ }
+
+ /******************************\
+ * *
+ * Find BackUp Browsers *
+ * *
+ \******************************/
+
+ sprintf(PrintBuf,"\n\nTEST#: [TS%ld].\n=============", ++TESTCOUNT);
+ PrintString(TOALL, PrintBuf);
+
+ sprintf(PrintBuf,"\nTEST: Retrieve the backUp list for Domain %s.\n", UnicodeToPrintfString(wcLocDomainName));
+ PrintString(TOALL, PrintBuf);
+
+ ulNumBackUps = 0;
+ if((Status = GetBList(TestedXportInfo[index].Transport, wcLocDomainName, TRUE, &ulNumBackUps, wcBackUpBrowsers)) != NERR_Success) {
+ sprintf(PrintBuf, "\nERROR[ER%ld]: Unable to get backup list of Domain %s ", ++ERRCOUNT, UnicodeToPrintfString(wcLocDomainName));
+ PrintString(TOALL, PrintBuf);
+ sprintf(PrintBuf, "on transport %s.\nError: %s", UnicodeToPrintfString(TestedXportInfo[index].Transport.Buffer), get_error_text(Status));
+ PrintString(TOALL, PrintBuf);
+ } else {
+
+ if(ulNumBackUps){
+ sprintf(PrintBuf,"\nSUCCESS[SC%ld]:Retrieved BackUp List for Domain %s.\n", ++SUCSCOUNT, UnicodeToPrintfString(wcLocDomainName));
+ PrintString(TOALL, PrintBuf);
+
+ sprintf(PrintBuf, "\nBackUp Servers of Domain %s ", UnicodeToPrintfString(wcLocDomainName));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ sprintf(PrintBuf, " on Transport %s are:\n", UnicodeToPrintfString(TestedXportInfo[index].Transport.Buffer));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ for(i = 0; i<(INT)ulNumBackUps; i++){
+ sprintf(PrintBuf, "%s \n", UnicodeToPrintfString(wcBackUpBrowsers[i]));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ }
+ } else {
+ sprintf(PrintBuf, "\nWARNING[WR%ld]: No BackUp Browsers in Domain %s ", ++WRNCOUNT, UnicodeToPrintfString(wcLocDomainName));
+ PrintString(TOALL, PrintBuf);
+ sprintf(PrintBuf, " Transport %s.", UnicodeToPrintfString(TestedXportInfo[index].Transport.Buffer));
+ PrintString(TOALL, PrintBuf);
+ } // ulNumBackUps
+ } // GetBList
+
+ //
+ // Check the browser lists of Master and BackUps
+ //
+ if(_wcsicmp(wcMasterName, L"") != 0)
+ CheckBrowseListsOfMasterAndBackUps(TestedXportInfo[index], wcLocDomainName, wcMasterName, wcBackUpBrowsers, ulNumBackUps, TRUE);
+
+
+ //
+ // Force election on the transport and see who wins.
+ //
+ ForceElectionAndFindWhoWins(TestedXportInfo, iNumOfTestedXports, wcLocDomainName, wcCurrentMasters, index);
+
+ } // for (index == each Transport)
+
+ //
+ // Stop the browser and check who has become the new master on each transport.
+ //
+ if(!StopBrowsersAndFindWhoBecomesMaster(wcLocDomainName, wcDomPDC, TestedXportInfo, iNumOfTestedXports, wcCurrentMasters))
+ return FALSE;
+
+/*
+// Register Fake names.
+// Promotion of BDC to PDC
+// Send illegal Election packets
+*/
+
+ //
+ // Start the browser back on all machines
+ //
+ sprintf(PrintBuf,"\nStarting all the Browsers back.\n");
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ if(!CheckBrServiceOnMachinesInList())
+ return FALSE;
+
+ sprintf(PrintBuf, "\n\nSleeping for %ld msecs.\n", BASESLEEPTIME*2);
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ Sleep(BASESLEEPTIME*2);
+
+
+ return TRUE;
+}
+
+
+//
+// Find the transports to which the browser is bound to, on the local machine.
+//
+BOOL
+FindAllTransports(UNICODE_STRING *TransportNames, DWORD *iNumOfTransports)
+{
+INT i;
+DWORD Status;
+PLMDR_TRANSPORT_LIST TransportList, TransportEntry;
+
+ //
+ // Start the browser on the local machine before foinding the transports
+ //
+ sprintf(PrintBuf,"\n\nStarting browser on the local machine.\n");
+ PrintString(TOSCREENANDLOG, PrintBuf);
+
+ StartBrowserService(lpLocMachineInfo->wcMachineName);
+
+ sprintf(PrintBuf, "\n\nSleeping for %ld msecs.\n", BASESLEEPTIME);
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ Sleep(BASESLEEPTIME);
+
+ //
+ // Find the Protocols into which the browser is bound
+ //
+ Status = GetBrowserTransportList(&TransportList);
+
+ if (Status != NERR_Success) {
+ sprintf(PrintBuf,"ERROR:Unable to retrieve transport list.\n Error: %s\n", get_error_text(Status));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ return FALSE;
+ }
+
+ i = 0;
+ TransportEntry = TransportList;
+ while (TransportEntry != NULL) {
+
+ if(i >= MAXTRANSPORTS){
+ sprintf(PrintBuf,"\nOnly a maximum of %d Transports can be handled.\n", MAXTRANSPORTS);
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ NetApiBufferFree(TransportList);
+ return FALSE;
+ }
+ if((TransportNames[i].Buffer = (TCHAR *)calloc(1, sizeof(TCHAR) * (USHORT)TransportEntry->TransportNameLength + 1)) == NULL){
+ sprintf(PrintBuf,"\nError in allocating Buffer for transport.\n");
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ NetApiBufferFree(TransportList);
+ return FALSE;
+ }
+ wcscpy(TransportNames[i].Buffer, TransportEntry->TransportName);
+ TransportNames[i].Length = (USHORT)TransportEntry->TransportNameLength;
+ TransportNames[i].MaximumLength = (USHORT)TransportEntry->TransportNameLength;
+
+ if (TransportEntry->NextEntryOffset == 0) {
+ TransportEntry = NULL;
+ } else {
+ TransportEntry = (PLMDR_TRANSPORT_LIST)((PCHAR)TransportEntry+TransportEntry->NextEntryOffset);
+ }
+ i++;
+ }
+
+ *iNumOfTransports = i;
+
+ NetApiBufferFree(TransportList);
+
+return TRUE;
+}
+
+
+//
+// Force an election on a transport and find who wins.
+//
+VOID
+ForceElectionAndFindWhoWins(XPORTINFO *TestedXportInfo,
+ INT iNumOfTestedXports,
+ LPTSTR wcDomainName,
+ TCHAR wcCurrentMasters[MAXPROTOCOLS][CNLEN+1],
+ INT index)
+{
+TCHAR wcNewMaster[CNLEN +1];
+NET_API_STATUS Status;
+
+ sprintf(PrintBuf,"\n\nTEST#: [TS%ld].\n=============", ++TESTCOUNT);
+ PrintString(TOALL, PrintBuf);
+
+ sprintf(PrintBuf,"\nTEST: Force Election and Find who wins.\n");
+ PrintString(TOALL, PrintBuf);
+
+ if((Status = Elect(TestedXportInfo[index].Transport, wcDomainName)) != NERR_Success){
+ sprintf(PrintBuf,"\nEEROR[ER%ld]: Could not force an election.\n", ++ERRCOUNT);
+ PrintString(TOALL, PrintBuf);
+
+ } else {
+ //
+ // Sleep for sometime
+ //
+ sprintf(PrintBuf, "\nSleeping for %ld msecs.\n", BASESLEEPTIME);
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ Sleep(BASESLEEPTIME);
+
+ //
+ // Find the new Master.
+ //
+ lstrcpy(wcNewMaster, L"");
+ if((Status = GetMasterName(TestedXportInfo, iNumOfTestedXports, index, wcDomainName, wcNewMaster)) != NERR_Success){
+ sprintf(PrintBuf,"\nERROR[ER%ld]: Unable to find the new master: Domain: %s ", ++ERRCOUNT, UnicodeToPrintfString(wcDomainName));
+ PrintString(TOALL, PrintBuf);
+ sprintf(PrintBuf," Transport: %s", UnicodeToPrintfString(TestedXportInfo[index].Transport.Buffer));
+ PrintString(TOALL, PrintBuf);
+ sprintf(PrintBuf," Old Master: %s.\n", UnicodeToPrintfString(wcCurrentMasters[index]));
+ PrintString(TOALL, PrintBuf);
+ wcscpy(wcCurrentMasters[index], L"");
+ return;
+ }
+
+ if(_wcsicmp(wcCurrentMasters[index], wcNewMaster) != 0){
+ sprintf(PrintBuf,"\nERROR[ER%ld]: New master not same as Old Master after election. New master: %s ", ++ERRCOUNT, UnicodeToPrintfString(wcNewMaster));
+ PrintString(TOALL, PrintBuf);
+ sprintf(PrintBuf," Old Master: %s.\n", UnicodeToPrintfString(wcCurrentMasters[index]));
+ PrintString(TOALL, PrintBuf);
+ sprintf(PrintBuf," Transport: %s", UnicodeToPrintfString(TestedXportInfo[index].Transport.Buffer));
+ PrintString(TOALL, PrintBuf);
+
+ wcscpy(wcCurrentMasters[index], wcNewMaster);
+
+ } else {
+ sprintf(PrintBuf,"\nSUCCESS[SC%ld]: New master same as Old Master after election.", ++SUCSCOUNT);
+ PrintString(TOALL, PrintBuf);
+ }
+ }
+
+}
+
+
+//
+// Initialize all variables, check the PDC etc.
+//
+VOID
+Initialize(UNICODE_STRING *TransportNames, INT *iNumOfTransports)
+{
+INT i;
+TCHAR wcLocalComputerName[CNLEN+1];
+TCHAR wcDomPDC[CNLEN+1];
+DWORD dwNameLen;
+LPMACHINEINFO lpMachineInfo;
+NET_API_STATUS Status;
+
+
+ if((fplog = fopen(BROWTESTLOGFILE, "w")) == NULL){
+ printf("\nError opening the output file %s!\n", BROWTESTLOGFILE);
+ CloseHandle(ConsoleMutex);
+ exit(1);
+ }
+
+ if((fpsum = fopen(BROWTESTSUMMARYFILE, "w")) == NULL){
+ printf("\nError opening the output file %s!\n", BROWTESTSUMMARYFILE);
+ fclose(fplog);
+ CloseHandle(ConsoleMutex);
+ exit(1);
+ }
+
+ sprintf(PrintBuf,"Browser Test started.\n\n");
+ PrintString(TOALL, PrintBuf);
+
+ if(!dwStressTest){
+ sprintf(PrintBuf,"Functional test chosen.\n\n");
+ PrintString(TOALL, PrintBuf);
+
+ } else {
+ sprintf(PrintBuf,"Stress test chosen. Time %ld Minutes.\n\n", dwStressTest);
+ PrintString(TOALL, PrintBuf);
+ }
+
+
+ sprintf(PrintBuf,"\t IPXHack = %s\n", (bIPXHack ? "TRUE": "FALSE"));
+ PrintString(TOALL, PrintBuf);
+ sprintf(PrintBuf,"\t Force Announcement = %s\n", (bForceAnn ? "TRUE": "FALSE"));
+ PrintString(TOALL, PrintBuf);
+ sprintf(PrintBuf,"\t Single Domain Tests = %s\n", (bSingleDomTest ? "DONE": "NOT DONE"));
+ PrintString(TOALL, PrintBuf);
+ sprintf(PrintBuf,"\t Stress Test = %s\n", (dwStressTest ? "DONE": "NOT DONE"));
+ PrintString(TOALL, PrintBuf);
+
+
+ //
+ // Get the domain info from the input file
+ //
+ if(!ReadInputFile()){
+ fclose(fplog);
+ fclose(fpsum);
+ CleanMem();
+ CloseHandle(ConsoleMutex);
+ exit(1);
+ }
+
+
+ if(HeadList1.Next == NULL){
+ sprintf(PrintBuf,"\nThere are no machines specified in Subnet 1.\n");
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ fclose(fplog);
+ fclose(fpsum);
+ CleanMem();
+ CloseHandle(ConsoleMutex);
+ exit(1);
+ }
+
+ //
+ // Now we have the information on the domains in 1 or 2 subnets, in the list.
+ //
+ sprintf(PrintBuf,"\nList1:");
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ PrintList(HeadList1);
+
+
+ //
+ // Find the Local Computer Name
+ //
+ dwNameLen = sizeof(wcLocalComputerName);
+ if(!GetComputerName(wcLocalComputerName, &dwNameLen)){
+ sprintf(PrintBuf,"\nError: Unable to get the Local ComputerName.\
+ \nError %s\n", get_error_text(GetLastError()));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ fclose(fplog);
+ fclose(fpsum);
+ CleanMem();
+ CloseHandle(ConsoleMutex);
+ exit(1);
+ }
+
+ //
+ // Find the Local Computer in the List
+ //
+ for(lpLocMachineInfo = HeadList1.Next; lpLocMachineInfo &&
+ _wcsicmp(wcLocalComputerName, lpLocMachineInfo->wcMachineName) != 0;
+ lpLocMachineInfo = lpLocMachineInfo->Next);
+ if(!lpLocMachineInfo){
+ sprintf(PrintBuf,"\nError: Unable to match the Local ComputerName in \
+ \nthe List: %s\n\n", UnicodeToPrintfString(wcLocalComputerName));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ fclose(fplog);
+ fclose(fpsum);
+ CleanMem();
+ CloseHandle(ConsoleMutex);
+ exit(1);
+ }
+
+
+ sprintf(PrintBuf,"\n\nMatched the Local Machine Name: %s\n",
+ UnicodeToPrintfString(lpLocMachineInfo->wcMachineName));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+
+
+ //
+ // Find the domain name in the list. all machines in the domain are close
+ // by.
+ //
+ lpDomStart = HeadList1.Next;
+ while(lpDomStart &&
+ _wcsicmp(lpDomStart->wcDomainName,lpLocMachineInfo->wcDomainName) != 0)
+ lpDomStart = lpDomStart->Next;
+
+ if(!lpDomStart){
+ sprintf(PrintBuf, "\n\nERROR[ER%ld]:Comparing the Lists.Could not find \
+ \n Domain %s in List.\n",++ERRCOUNT,
+ UnicodeToPrintfString(lpLocMachineInfo->wcDomainName));
+ PrintString(TOALL, PrintBuf);
+ fclose(fplog);
+ fclose(fpsum);
+ CleanMem();
+ CloseHandle(ConsoleMutex);
+ exit(1);
+ }
+
+ wcscpy(wcTESTEDDOMAINS[iNumOfTestedDomains++], lpDomStart->wcDomainName);
+
+ sprintf(PrintBuf,"\n\nPlease wait while I check my access permissions on the servers \nby Starting and Stopping Browser on them.........\n");
+ PrintString(TOSCREEN, PrintBuf);
+ Sleep(3000);
+
+ //
+ // Find the Protocols into which the browser is bound
+ //
+ if(!FindAllTransports(TransportNames, iNumOfTransports)){
+ fclose(fplog);
+ fclose(fpsum);
+ CleanMem();
+ CloseHandle(ConsoleMutex);
+ exit(1);
+ }
+
+ //
+ // If this is not stress test, then check whether it is a multihomed
+ // machine.
+ //
+
+ if(!dwStressTest){
+ //
+ // Find if the machine is multihomed
+ //
+ if(LocalMachineIsMultihomed(TransportNames, *iNumOfTransports)){
+ sprintf(PrintBuf,"\nThis machine is multihomed. \
+ \nI cannot test browser from a multihomed Machine.\n");
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ fclose(fplog);
+ fclose(fpsum);
+ CleanMem();
+ CloseHandle(ConsoleMutex);
+ exit(1);
+ }
+ }
+
+ //
+ // Check whether I have administrative privileges on all machines.
+ //
+ if(!CheckAccessPermissionOnAllMachines()){
+ fclose(fplog);
+ fclose(fpsum);
+ CleanMem();
+ CloseHandle(ConsoleMutex);
+ exit(1);
+ }
+
+ //
+ // Wait for the service controller to get updated.
+ //
+ sprintf(PrintBuf, "\n\nSleeping for %ld msecs.\n", BASESLEEPTIME);
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ Sleep(BASESLEEPTIME);
+
+
+ //
+ // Find the PDC of the Primary domain.
+ //
+ i = 0;
+ lstrcpy(wcDomPDC, L"");
+ while((i < (*iNumOfTransports)) && (Status = GetNetBiosPdcName(TransportNames[i].Buffer, lpLocMachineInfo->wcDomainName, wcDomPDC)) != NERR_Success)
+ i++;
+
+ if(Status == NERR_Success){
+
+ sprintf(PrintBuf,"\nPDC of %s is ", UnicodeToPrintfString(lpLocMachineInfo->wcDomainName));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ sprintf(PrintBuf, "%s\n", UnicodeToPrintfString(wcDomPDC));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+
+ for(lpMachineInfo=HeadList1.Next; lpMachineInfo &&
+ _wcsicmp(lpMachineInfo->wcMachineName, wcDomPDC) != 0;
+ lpMachineInfo=lpMachineInfo->Next);
+
+ if(lpMachineInfo) {
+ //
+ // Found PDC in the list.
+ //
+ wcscpy(LocDomInfo.wcDomainName, lpMachineInfo->wcDomainName);
+ LocDomInfo.lpMInfo = lpMachineInfo;
+ } else {
+ sprintf(PrintBuf,"\nERROR[ER%ld]: Unable to find the PDC %s in the input file.\n",
+ ++ERRCOUNT,UnicodeToPrintfString(wcDomPDC));
+ PrintString(TOALL, PrintBuf);
+ fclose(fplog);
+ fclose(fpsum);
+ CleanMem();
+ CloseHandle(ConsoleMutex);
+ exit(1);
+ }
+
+
+ } else {
+ sprintf(PrintBuf,"\nERROR[ER%ld]: Unable to determine the PDC of %s on any transports.\n",
+ ++ERRCOUNT,UnicodeToPrintfString(lpLocMachineInfo->wcDomainName));
+ PrintString(TOALL, PrintBuf);
+ fclose(fplog);
+ fclose(fpsum);
+ CleanMem();
+ CloseHandle(ConsoleMutex);
+ exit(1);
+ }
+
+}
+
+
+
+//
+// This routine has to be changed. Now it looks for multiple
+// entries of Netbt, or Nbf to determine if it is Multihomed.
+//
+BOOL
+LocalMachineIsMultihomed(UNICODE_STRING *TransportNames, INT iNumOfTransports)
+{
+INT i, j;
+
+
+
+ for(i=0; i< MAXPROTOCOLS; i++){
+ BOOL found = FALSE;
+ for(j=0; j<iNumOfTransports; j++) {
+ if(wcsstr(TransportNames[j].Buffer, TRANSPORTS[i])!=NULL){
+ if(!found)
+ found = TRUE;
+ else
+ return TRUE; // Found Multiple entries
+ }
+ }
+ }
+
+ //
+ // If number of transports retrieved is more than the MAXPROTOCOLS
+ // we suspect multihomed (RAS).
+ //
+ if(iNumOfTransports > MAXPROTOCOLS){
+ sprintf(PrintBuf, "\nMore than %ld Transports found. Exiting expecting machine to be Multihomed.\n", MAXPROTOCOLS);
+ PrintString(TOALL, PrintBuf);
+ return TRUE;
+ }
+
+
+return FALSE;
+}
+
+
+//
+// Check if the New master found, is the correct in terms of OS type.
+//
+BOOL
+NewMasterIsCorrect(TCHAR wcCurrentMasters[MAXPROTOCOLS][CNLEN+1],
+ INT iMasterIndex,
+ LPTSTR wcNewMaster,
+ XPORTINFO XportInfo,
+ LPMACHINEINFO lpStoppedMachineInfo)
+{
+INT index;
+LPMACHINEINFO lpMachineInfo, lpNewMasterInfo;
+LPTSTR pLocDomainName;
+
+ //
+ // If Stopped machine is not the same as the current master, then
+ // stopped machine didn't have the protocol. So check whether the
+ // current and New masters are same.
+ //
+ if(_wcsicmp(wcCurrentMasters[iMasterIndex], lpStoppedMachineInfo->wcMachineName) != 0){
+ if(_wcsicmp(wcCurrentMasters[iMasterIndex], wcNewMaster) != 0){
+ sprintf(PrintBuf, "\n\nERROR[ER%ld]:Browser stopped on another machine. However master changed! New master: %s ",++ERRCOUNT, UnicodeToPrintfString(wcNewMaster));
+ PrintString(TOALL, PrintBuf);
+ sprintf(PrintBuf, " Old master: %s.\n", UnicodeToPrintfString(wcCurrentMasters[iMasterIndex]));
+ PrintString(TOALL, PrintBuf);
+ return FALSE;
+ } else
+ return TRUE;
+ }
+
+ pLocDomainName = lpStoppedMachineInfo->wcDomainName;
+
+ //
+ // Find the transport index
+ //
+ index = XportInfo.index;
+
+ //
+ // Find the New Master name in the list.
+ //
+ lpMachineInfo = HeadList1.Next;
+ while(lpMachineInfo && _wcsicmp(lpMachineInfo->wcMachineName,wcNewMaster) != 0)
+ lpMachineInfo = lpMachineInfo->Next;
+
+ if(!lpMachineInfo){
+ sprintf(PrintBuf, "\n\nERROR[ER%ld]:Comparing the Lists.Could not find New master %s in List.\n",++ERRCOUNT, UnicodeToPrintfString(wcNewMaster));
+ PrintString(TOALL, PrintBuf);
+ return FALSE;
+ }
+ lpNewMasterInfo = lpMachineInfo;
+
+ //
+ // The machines are ordered based on there OSTYPES. Check whether browser
+ // service is started on any machine with higher OS Level than the New master.
+ //
+ lpMachineInfo = lpDomStart;
+ while(lpMachineInfo && _wcsicmp(lpMachineInfo->wcMachineName,wcNewMaster) != 0){
+
+ //
+ // For TCP/IP the machines in second subnet is not participating
+ // in the election.
+ //
+ if((index == TCP) && (lpMachineInfo->iSubnet != SUBNET1)){
+ lpMachineInfo = lpMachineInfo->Next;
+ continue;
+ }
+
+
+ if(lpMachineInfo->BrowserServiceStarted){
+ //
+ // If OS Level is greater and this machine has the protocol
+ // then return FALSE.
+ //
+ if((lpMachineInfo->iOsPreference > lpNewMasterInfo->iOsPreference) && lpMachineInfo->Protocols[index]){
+ sprintf(PrintBuf, "\n\nERROR[ER%ld]:Comparing the Lists.Could not find Domain %s in List.\n",++ERRCOUNT, UnicodeToPrintfString(pLocDomainName));
+ PrintString(TOALL, PrintBuf);
+ return FALSE;
+ } else {
+ if((lpMachineInfo->iOsPreference == lpNewMasterInfo->iOsPreference) && lpMachineInfo->Protocols[index]){
+ //
+ // If both have same OStypes then we check the Server Bits to see
+ // if the current master have higher Server Bits.
+ //
+ if(lpMachineInfo->dwServerBits > lpNewMasterInfo->dwServerBits){
+ sprintf(PrintBuf, "\n\nERROR[ER%ld]:Machine %s has higher server bits than New master!\n",++ERRCOUNT, UnicodeToPrintfString(lpMachineInfo->wcMachineName));
+ PrintString(TOALL, PrintBuf);
+ return FALSE;
+ }
+ }
+ }
+ }
+
+ lpMachineInfo = lpMachineInfo->Next;
+ } // while
+
+
+return TRUE;
+}
+
+
+//
+// Parse the command line to extract the options
+//
+VOID
+ParseCommandLine(
+ INT argc,
+ CHAR *argv[]
+ )
+{
+ INT i, j;
+ BOOL bNoMatchFound;
+
+ //
+ // Command arguments as specified as:
+ // [/-]command:value
+ // All commands are treated as case insensitive
+ //
+ for ( i = 1; i < argc; i++ ) {
+
+ if ( argv[i][0] == '/' || argv[i][0] == '-' ) {
+
+ bNoMatchFound = TRUE;
+ for( j = 0; j < CommandTableSz && bNoMatchFound; j++ ) {
+
+ if ( _strnicmp( CommandTable[j].Command, &argv[i][1], strlen(CommandTable[j].Command ) ) == 0 ) {
+ //
+ // Matched a keyword
+ //
+ CHAR *Value = strrchr( &argv[i][1], ':' );
+
+ bNoMatchFound=FALSE;
+ //
+ // Setup for command's with RHS
+ //
+ if ( CommandTable[j].ValueType != VALUETYPE_IGNORE && CommandTable[j].ValueType != VALUETYPE_HELP ) {
+ if ( Value == NULL ) {
+ printf("Error: Incorrectly specified argument: %s.", argv[i] );
+ Usage( argv[0] );
+ ExitProcess(1L);
+ }
+ //
+ // Ensure a RHS has been correctly specified
+ //
+ if ( strlen(Value) <= 1 ) {
+ printf("Error: Must specify a Value for the option \"%s\"", CommandTable[j].Command );
+ Usage( argv[0] );
+ ExitProcess(1L);
+ }
+ Value++;
+ }
+ //
+ // Perform RHS extraction
+ //
+ switch ( CommandTable[j].ValueType ) {
+ case VALUETYPE_IGNORE : break;
+ case VALUETYPE_HELP : Usage( argv[0] ); ExitProcess( 0L );
+ case VALUETYPE_BOOL : *((BOOL *)CommandTable[j].Value) = (atoi( Value ) ? TRUE : FALSE); break;
+ case VALUETYPE_INTEGER: *((INT *)CommandTable[j].Value) = atoi( Value ); break;
+ case VALUETYPE_ULONG : *((ULONG *)CommandTable[j].Value) = atol( Value ); break;
+ case VALUETYPE_STRING : *((CHAR **)CommandTable[j].Value) = (CHAR *)Value; break;
+ }
+ }
+ }
+ if ( bNoMatchFound ) { printf("Error: Unknown argument: %s", argv[i] ); Usage( argv[0] );ExitProcess(1L);}
+ }
+ }
+
+}
+
+
+//
+// Print the linked list.
+//
+VOID
+PrintList(MACHINEINFO Head)
+{
+INT i;
+LPMACHINEINFO lpMachineInfo ;
+
+ sprintf(PrintBuf,"\nDomain Machine Type Subnet Protocols");
+ PrintString(TOALL, PrintBuf);
+ sprintf(PrintBuf,"\n-----------------------------------------------------------");
+ PrintString(TOALL, PrintBuf);
+ for(lpMachineInfo= Head.Next; lpMachineInfo; lpMachineInfo = lpMachineInfo->Next){
+ sprintf(PrintBuf,"\n%-16s ", UnicodeToPrintfString(lpMachineInfo->wcDomainName));
+ PrintString(TOALL, PrintBuf);
+ sprintf(PrintBuf,"%-16s %-9s %d ", UnicodeToPrintfString(lpMachineInfo->wcMachineName),
+ OSTYPES[lpMachineInfo->iOsType].Type, lpMachineInfo->iSubnet);
+ PrintString(TOALL, PrintBuf);
+ for(i=0; i<MAXPROTOCOLS; i++)
+ if(lpMachineInfo->Protocols[i]){
+ sprintf(PrintBuf,"%s ", PROTOCOLS[i]);
+ PrintString(TOALL, PrintBuf);
+ }
+ }
+
+}
+
+
+//
+// Main function doing the browser functional testing.
+//
+BOOL
+StartBrowserFunctionalTest(UNICODE_STRING *TransportNames,
+ INT iNumOfTransports)
+{
+INT i, j;
+INT iNumOfTestedXports = 0;
+XPORTINFO TestedXportInfo[MAXPROTOCOLS];
+
+// for(i=0; i < iNumOfTransports; i++)
+// printf("\nTransport[%d] = %s",i+1, UnicodeToPrintfString(TransportNames[i].Buffer));
+
+
+
+ //
+ // Decide which protocols will be tested. This is done, by inspecting the local
+ // transports and what was requested by the user in the input file. Only the Local
+ // Machine line is inspected
+ //
+
+ for(i = 0; i < MAXPROTOCOLS; i++){
+ if(lpLocMachineInfo->Protocols[i]){
+ for(j = 0; j < iNumOfTransports; j++){
+ if(wcsstr(TransportNames[j].Buffer, TRANSPORTS[i]) != NULL){
+ if((TestedXportInfo[iNumOfTestedXports].Transport.Buffer = (TCHAR *)calloc(1, sizeof(TCHAR) * TransportNames[j].MaximumLength+1)) == NULL){
+ sprintf(PrintBuf,"\nError in allocating Buffer for tested transports.\n");
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ return FALSE;
+ }
+ wcscpy(TestedXportInfo[iNumOfTestedXports].Transport.Buffer, TransportNames[j].Buffer);
+ TestedXportInfo[iNumOfTestedXports].Transport.Length = TransportNames[j].Length;
+ TestedXportInfo[iNumOfTestedXports].Transport.MaximumLength = TransportNames[j].MaximumLength;
+ iNumOfTestedXports++;
+ break;
+ }
+ }
+ }
+ }
+
+ //
+ // Free the transport Name Buffer
+ //
+ for(i=0; i< iNumOfTransports; i++) free(TransportNames[i].Buffer);
+
+ if(!iNumOfTestedXports){
+ sprintf(PrintBuf,"\nThe transports requested to be tested is not currently installed/enabled\
+ \non this computer.\n");
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ return FALSE;
+ }
+
+ //
+ //Find the index of the transport;
+ //
+ for(i=0; i< iNumOfTestedXports; i++){
+ for(j=0; j < MAXPROTOCOLS
+ && wcsstr(TestedXportInfo[i].Transport.Buffer, TRANSPORTS[j]) == NULL; j++);
+ if(j >= MAXPROTOCOLS){
+ sprintf(PrintBuf,"\n\nUnknown transport %s!\n",UnicodeToPrintfString(TestedXportInfo[i].Transport.Buffer) );
+ PrintString(TOALL, PrintBuf);
+ return FALSE;
+ }
+ TestedXportInfo[i].index = j;
+ }
+
+
+ sprintf(PrintBuf,"\n\nTested Transports are:\n");
+ PrintString(TOALL, PrintBuf);
+ for(i=0; i< iNumOfTestedXports; i++){
+ sprintf(PrintBuf,"Tested Transports[%d] = %-25s Index=%d\n", i+1, UnicodeToPrintfString(TestedXportInfo[i].Transport.Buffer), TestedXportInfo[i].index);
+ PrintString(TOALL, PrintBuf);
+ }
+
+
+ //
+ // Check whether the browser service has been started on all NT machines
+ //
+ if(!CheckBrServiceOnMachinesInList())
+ return FALSE;
+
+
+ //
+ // Ask the machines in the domain to announce themselves and
+ // sleep for sometime so the elections and things happen
+ //
+ if(!bForceAnn) {
+ sprintf(PrintBuf, "\nSleeping for %ld msecs.\n", UPDATESLEEPTIME);
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ Sleep(UPDATESLEEPTIME);
+
+ } else {
+
+ //
+ // Ask the machines in the domain to announce themselves and
+ // sleep for sometime so the elections and things happen
+ //
+ ForceAnnounce(TestedXportInfo[0].Transport, lpLocMachineInfo->wcDomainName);
+
+ sprintf(PrintBuf, "\nForceAnnounce. Sleeping for %ld msecs.\n", BASESLEEPTIME*2);
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ Sleep(BASESLEEPTIME*2);
+ }
+
+
+ if(bSingleDomTest){
+ //
+ // Do the single domain tests
+ //
+
+ if(!DoSingleDomainTests(TestedXportInfo, iNumOfTestedXports)){
+ sprintf(PrintBuf,"\n\nERROR: Single domain test failed!\n");
+ PrintString(TOALL, PrintBuf);
+ }
+ }
+
+
+ //
+ // Domain Spanning Multiple domains.
+ //
+ DomSpanningMulSubNetsTests(TestedXportInfo, iNumOfTestedXports);
+
+
+ //
+ // Do the multiple domain tests
+ //
+ DoMulDomMulSubNetTests(TestedXportInfo, iNumOfTestedXports);
+
+ //
+ // Free the tested transport Name Buffer
+ //
+ for(i=0; i< iNumOfTestedXports; i++) free(TestedXportInfo[i].Transport.Buffer);
+
+return TRUE;
+}
+
+
+BOOL
+StopBrowsersAndFindWhoBecomesMaster(LPTSTR wcDomainName,
+ LPTSTR wcDomPDC,
+ XPORTINFO *TestedXportInfo,
+ INT iNumOfTestedXports,
+ TCHAR wcCurrentMasters[MAXPROTOCOLS][CNLEN+1])
+{
+INT index;
+BOOL Found;
+INT iNonMasters = 0;
+LPMACHINEINFO lpMachineInfo;
+
+ sprintf(PrintBuf,"\n\n--------------------------------------------------------------------------------\n");
+ PrintString(TOALL, PrintBuf);
+
+ sprintf(PrintBuf,"\nTEST: Stop Browser on PDC %s and find who becomes the master.\n", UnicodeToPrintfString(wcDomPDC));
+ PrintString(TOALL, PrintBuf);
+
+ if(!StopBrowserOnCurrentMaster(wcDomainName, wcDomPDC, TestedXportInfo, iNumOfTestedXports, wcCurrentMasters))
+ return FALSE;
+
+ //
+ // Go through all the NT machines and stop the browsers on them.
+ //
+ do{
+ Found = FALSE;
+
+ //
+ // Find whether any of the current masters are NT machines
+ //
+ for(index = 0; ((index < iNumOfTestedXports) && !Found); ){
+
+ if(_wcsicmp(wcCurrentMasters[index], L"") != 0){
+ for(lpMachineInfo = lpDomStart; lpMachineInfo && _wcsicmp(wcCurrentMasters[index],
+ lpMachineInfo->wcMachineName) != 0; lpMachineInfo= lpMachineInfo->Next);
+
+ if(lpMachineInfo)
+ Found = (IsNTMachine(lpMachineInfo) && lpMachineInfo->BrowserServiceStarted);
+ }
+
+ if(!Found)
+ index++;
+ }
+
+ if(Found){
+ sprintf(PrintBuf,"\n\n--------------------------------------------------------------------------------\n");
+ PrintString(TOALL, PrintBuf);
+ sprintf(PrintBuf,"\nTEST: Stop Browser on Master %s and find who becomes the master.\n",
+ UnicodeToPrintfString(wcCurrentMasters[index]));
+ PrintString(TOALL, PrintBuf);
+ if(!StopBrowserOnCurrentMaster(wcDomainName, wcCurrentMasters[index], TestedXportInfo, iNumOfTestedXports, wcCurrentMasters))
+ return FALSE;
+ }
+ }while(Found);
+
+ //
+ // See if any of the NT machines in the list didnot become a master browser.
+ // If the machine has only TCP then, we check whther it is in the same
+ // subnet.
+ //
+ sprintf(PrintBuf,"\n\nTEST#: [TS%ld].\n=============", ++TESTCOUNT);
+ PrintString(TOALL, PrintBuf);
+
+ sprintf(PrintBuf,"\nTEST: Check whether any NT machines didnot become a master.\n");
+ PrintString(TOALL, PrintBuf);
+
+
+ for(lpMachineInfo = lpDomStart; lpMachineInfo && _wcsicmp(wcDomainName,
+ lpMachineInfo->wcDomainName) == 0; lpMachineInfo= lpMachineInfo->Next){
+
+ if(IsNTMachine(lpMachineInfo) && lpMachineInfo->BrowserServiceStarted){
+ if(lpMachineInfo->Protocols[IPX] || lpMachineInfo->Protocols[NBIPX] ||
+ lpMachineInfo->Protocols[XNS] ||lpMachineInfo->Protocols[NETBEUI] ||
+ (lpMachineInfo->Protocols[TCP] && lpMachineInfo->iSubnet == SUBNET1)){
+
+ iNonMasters++;
+ sprintf(PrintBuf,"\nERROR: Machine %s didnot become a master!\n", UnicodeToPrintfString(lpMachineInfo->wcMachineName));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ }
+ }
+ }
+
+ if(!iNonMasters){
+ sprintf(PrintBuf,"\nSUCCESS[SC%ld]:All NT machines became masters.\n", ++SUCSCOUNT);
+ PrintString(TOALL, PrintBuf);
+ } else {
+ sprintf(PrintBuf,"\nERROR[ER%ld]: %d machines didnot become a master!\n", ++ERRCOUNT, iNonMasters);
+ PrintString(TOALL, PrintBuf);
+ }
+
+
+return TRUE;
+}
+
+
+BOOL
+StopBrowserOnCurrentMaster (LPTSTR wcDomainName,
+ LPTSTR wcBrStopMachine,
+ XPORTINFO *TestedXportInfo,
+ INT iNumOfTestedXports,
+ TCHAR wcCurrentMasters[MAXPROTOCOLS][CNLEN+1])
+{
+INT i, index;
+TCHAR wcNewMaster[CNLEN +1];
+LPMACHINEINFO lpMachineInfo, lpBrStoppedMachineInfo;
+NET_API_STATUS Status;
+
+ //
+ // Shutdown the Browser on the specified machine and see who becomes the new Master browser
+ //
+ if((Status = StopBrowserService(wcBrStopMachine)) != NERR_Success){
+ sprintf(PrintBuf,"\nERROR[ER%ld]:Could not stop browser on %s.\n", ++ERRCOUNT, UnicodeToPrintfString(wcBrStopMachine));
+ PrintString(TOALL, PrintBuf);
+ return FALSE;
+ }
+
+ for(lpMachineInfo = HeadList1.Next; lpMachineInfo &&
+ (_wcsicmp(lpMachineInfo->wcMachineName, wcBrStopMachine) != 0); lpMachineInfo = lpMachineInfo->Next);
+
+ if(!lpMachineInfo){
+ sprintf(PrintBuf,"\nERROR[ER%ld]:Could not find Stopped machine's name in List1. %s.\n", ++ERRCOUNT, UnicodeToPrintfString(wcBrStopMachine));
+ PrintString(TOALL, PrintBuf);
+ return FALSE;
+ }
+ lpMachineInfo->BrowserServiceStarted = FALSE;
+ lpBrStoppedMachineInfo = lpMachineInfo;
+
+ //
+ // Sleep for sometime
+ //
+ sprintf(PrintBuf, "\nSleeping for %ld msecs\n", BASESLEEPTIME * 2);
+ PrintString(TOSCREENANDLOG, PrintBuf);
+
+ Sleep(BASESLEEPTIME*2);
+
+
+ //
+ // For each of the tested transports find who is the Master
+ //
+ for(index = 0; index < iNumOfTestedXports; index++){
+
+ sprintf(PrintBuf,"\n\nTEST#: [TS%ld].\n=============", ++TESTCOUNT);
+ PrintString(TOALL, PrintBuf);
+
+ sprintf(PrintBuf,"\nTEST: Find new master on Transport %s.\n", UnicodeToPrintfString(TestedXportInfo[index].Transport.Buffer));
+ PrintString(TOALL, PrintBuf);
+
+ //
+ // Find the new Master.
+ //
+ lstrcpy(wcNewMaster, L"");
+ if((Status = GetMasterName(TestedXportInfo, iNumOfTestedXports, index, wcDomainName, wcNewMaster)) != NERR_Success){
+ if(MasterAvailable(TestedXportInfo[index], wcDomainName, SUBNET1)){
+ sprintf(PrintBuf,"\nERROR[ER%ld]: Unable to find the new master: Domain: %s ", ++ERRCOUNT, UnicodeToPrintfString(wcDomainName));
+ PrintString(TOALL, PrintBuf);
+ sprintf(PrintBuf," Transport: %s", UnicodeToPrintfString(TestedXportInfo[index].Transport.Buffer));
+ PrintString(TOALL, PrintBuf);
+ sprintf(PrintBuf," Old Master: %s.\n", UnicodeToPrintfString(wcCurrentMasters[index]));
+ PrintString(TOALL, PrintBuf);
+
+ } else {
+ sprintf(PrintBuf,"\nOK[OK%ld]: Unable to find the new master. No servers running Browser service. Domain: %s ", ++OKCOUNT, UnicodeToPrintfString(wcDomainName));
+ PrintString(TOALL, PrintBuf);
+ sprintf(PrintBuf," Transport: %s", UnicodeToPrintfString(TestedXportInfo[index].Transport.Buffer));
+ PrintString(TOALL, PrintBuf);
+ sprintf(PrintBuf," Old Master: %s.\n", UnicodeToPrintfString(wcCurrentMasters[index]));
+ PrintString(TOALL, PrintBuf);
+ }
+
+ wcscpy(wcCurrentMasters[index], L"");
+ continue;
+ }
+
+ sprintf(PrintBuf,"\nNew Master: %s.\n", UnicodeToPrintfString(wcNewMaster));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+
+ if(NewMasterIsCorrect(wcCurrentMasters, index, wcNewMaster, TestedXportInfo[index], lpBrStoppedMachineInfo)){
+ sprintf(PrintBuf,"\nSUCCESS[SC%ld]: New master is OK.: %s \n", ++SUCSCOUNT, UnicodeToPrintfString(wcNewMaster));
+ PrintString(TOALL, PrintBuf);
+
+ }
+
+ wcscpy(wcCurrentMasters[index], wcNewMaster);
+ }
+
+
+ /********************************\
+ * *
+ * Sleep for 15 minutes and then *
+ * retrieve the lists. *
+ * *
+ \********************************/
+
+ //
+ // Sleep for 15 minutes and retrieve the list from master and backup
+ // to check whether it matches the User input.
+ //
+
+ if(!bForceAnn) {
+ sprintf(PrintBuf, "\nSleeping for %ld msecs.\n", UPDATESLEEPTIME);
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ Sleep(UPDATESLEEPTIME);
+
+ } else {
+
+ //
+ // Ask the machines in the domain to announce themselves and
+ // sleep for sometime so the elections and things happen
+ //
+ ForceAnnounce(TestedXportInfo[0].Transport, wcDomainName);
+
+ sprintf(PrintBuf, "\nForceAnnounce. Sleeping for %ld msecs.\n", BASESLEEPTIME*2);
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ Sleep(BASESLEEPTIME*2);
+ }
+
+ //
+ // Find the backup browsers for each transport
+ //
+ //
+
+ for(index = 0; index < iNumOfTestedXports; index++){
+
+ if(_wcsicmp(wcCurrentMasters[index], L"") != 0){
+ ULONG ulNumBackUps = 0;
+ TCHAR wcBackUpBrowsers[MAXBACKUPS][CNSLASHLEN+1];
+
+ if((Status = GetBList(TestedXportInfo[index].Transport, wcDomainName, TRUE, &ulNumBackUps, wcBackUpBrowsers)) != NERR_Success) {
+ sprintf(PrintBuf, "\nERROR[ER%ld]: Unable to get backup list of Domain %s ", ++ERRCOUNT, UnicodeToPrintfString(wcDomainName));
+ PrintString(TOALL, PrintBuf);
+ sprintf(PrintBuf, "Transport %s.\nError: %s", UnicodeToPrintfString(TestedXportInfo[index].Transport.Buffer), get_error_text(Status));
+ PrintString(TOALL, PrintBuf);
+ } else {
+
+ if(ulNumBackUps){
+ sprintf(PrintBuf, "\nBackUp Servers of Domain %s ", UnicodeToPrintfString(wcDomainName));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ sprintf(PrintBuf, " on Transport %s are:\n", UnicodeToPrintfString(TestedXportInfo[index].Transport.Buffer));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ for(i = 0; i<(INT)ulNumBackUps; i++){
+ sprintf(PrintBuf, "%s \n", UnicodeToPrintfString(wcBackUpBrowsers[i]));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ }
+ } else {
+ sprintf(PrintBuf, "\nWARNING[WR%ld]: No BackUp Browsers in Domain %s ", ++WRNCOUNT, UnicodeToPrintfString(wcDomainName));
+ PrintString(TOALL, PrintBuf);
+ sprintf(PrintBuf, " Transport %s.", UnicodeToPrintfString(TestedXportInfo[index].Transport.Buffer));
+ PrintString(TOALL, PrintBuf);
+ } // ulNumBackUps
+ } // GetBList
+
+ //
+ // Check the browser lists of Master and BackUps
+ //
+
+ if(_wcsicmp(wcCurrentMasters[index], L"") != 0)
+ CheckBrowseListsOfMasterAndBackUps(TestedXportInfo[index], wcDomainName, wcCurrentMasters[index], wcBackUpBrowsers, ulNumBackUps, TRUE);
+
+ } // if(wcCurrentMaster)
+
+ } // for(index)
+
+return TRUE;
+}
+
+
+
+
+VOID
+Usage(CHAR *ProgramName)
+{
+
+ printf("\nUsage: %s [/IPXHack:RHS] [[/ForceAnn:RHS] [/SingleD:RHS]] [/StressT:RHS]\n", ProgramName);
+ printf("\n /ipxhack: This parameter is used to specify");
+ printf("\n whether IPX master browser is found");
+ printf("\n using IPX over NetBIOS. (Daytona)");
+ printf("\n RHS can be 1(TRUE) or 0(FALSE).");
+ printf("\n Default: 1.\n");
+
+ printf("\n /ForceAnn: This parameter is used to specify");
+ printf("\n whether server names are forced to");
+ printf("\n to be announced or the test waits");
+ printf("\n for the update period (15 Minutes).");
+ printf("\n Waiting takes too long, but gives ");
+ printf("\n better results.");
+ printf("\n RHS can be 1(TRUE) or 0(FALSE).");
+ printf("\n Default: 0.\n");
+
+ printf("\n /SingleD: This parameter is used to specify");
+ printf("\n whether Single domain test be done (1)");
+ printf("\n or not done(0).");
+ printf("\n RHS can be 1(DONE) or 0(NOT DONE).");
+ printf("\n Default: 1.\n");
+
+ printf("\n /StressT: This parameter is used to specify");
+ printf("\n whether functional tests(0) or stress");
+ printf("\n tests need be done.");
+ printf("\n RHS can be 0(NOT STRESS) or a value");
+ printf("\n specifying time in Minutes for");
+ printf("\n stressing the browser.");
+ printf("\n Range 0 - 1,000,000 Minutes.");
+ printf("\n Default: 0.\n");
+}
diff --git a/private/net/svcdlls/browser/browtest/src/browtest.h b/private/net/svcdlls/browser/browtest/src/browtest.h
new file mode 100644
index 000000000..a82dfe3db
--- /dev/null
+++ b/private/net/svcdlls/browser/browtest/src/browtest.h
@@ -0,0 +1,148 @@
+#ifndef _BROWTEST
+#define _BROWTEST
+
+#ifndef UNICODE
+#define UNICODE
+#endif
+
+#include "browfunc.h"
+
+#define BROWTESTINFILE "browtest.inp"
+#define BROWTESTLOGFILE "browtest.log"
+#define BROWTESTSUMMARYFILE "browtest.sum"
+#define MAXCHARPERLINE 256
+#define MAXTRANSPORTS 16
+
+#define COMMENTCHAR ';'
+#define NEWLINECHAR '\n'
+#define QUOTECHAR '"'
+#define SPACECHAR ' '
+
+#define COMMASTR ","
+#define NEWLINESTR "\n"
+#define QUOTESTR "\""
+#define SPACETABSTR " \t"
+
+#define SUBNET1 1
+#define SUBNET2 2
+
+#define BROWSER TEXT("BROWSER")
+#define NETLOGON TEXT("NETLOGON")
+#define WORKSTATION TEXT("LanmanWorkstation")
+
+#define CNSLASHLEN CNLEN+2 // ComputerName with "\\"
+
+#define BASESLEEPTIME (40*1000) // 40 sec's
+#define UPDATESLEEPTIME (15*60*1000) // 15 minutes
+
+#define TOSCREEN 0x1UL
+#define TOLOGFILE 0x2UL
+#define TOSUMMARYFILE 0x4UL
+
+#define TOSCREENANDLOG (TOSCREEN | TOLOGFILE)
+#define TOLOGANDSUMMARY (TOLOGFILE | TOSUMMARYFILE)
+#define TOALL (TOSCREEN | TOLOGFILE | TOSUMMARYFILE)
+
+
+#define MAXPROTOCOLS 5
+#define MAXOSTYPES 8
+#define MAXTESTEDDOMAINS 5
+
+typedef struct _OSProp{
+ CHAR *Type;
+ INT iPreference;
+ }OSPROP;
+
+
+typedef struct _MACHINEINFO{
+ TCHAR wcDomainName[DNLEN+1];
+ TCHAR wcMachineName[CNLEN+1];
+ INT iOsType;
+ INT iOsPreference;
+ DWORD dwServerBits;
+ INT iSubnet;
+ INT Protocols[MAXPROTOCOLS];
+ BOOL BrowserServiceStarted;
+ struct _MACHINEINFO *Next;
+ }MACHINEINFO, *LPMACHINEINFO;
+
+//
+// Heads of the lists for the 2 subnets
+//
+
+typedef struct _XportInfo{
+ UNICODE_STRING Transport;
+ INT index;
+ }XPORTINFO;
+
+
+
+typedef struct _DOMAININFO{
+ TCHAR wcDomainName[DNLEN+1];
+ LPMACHINEINFO lpMInfo;
+ }DOMAININFO, *LPDOMAININFO;
+
+
+
+#define VALUETYPE_INTEGER 1
+#define VALUETYPE_ULONG 2
+#define VALUETYPE_STRING 3
+#define VALUETYPE_IGNORE 4
+#define VALUETYPE_HELP 5
+#define VALUETYPE_BOOL 6
+
+typedef struct _COMMANDTABLE {
+ CHAR Command[10];
+ INT ValueType ;
+ VOID *Value ;
+} COMMAND_TABLE;
+
+
+#define SHUTSVCPATH TEXT("%SystemRoot%\\shutsvc.exe")
+#define SHUTSVCNAME TEXT("BrShut_serv")
+
+#define STOPANDSTARTRDR 0
+#define REBOOTMACHINE 1
+
+
+#define NTOSTYPESBASE 4
+#define ASOSTYPESBASE 6
+#define IsNTMachine(a)((a->iOsType >= NTOSTYPESBASE) ? TRUE : FALSE)
+#define IsASMachine(a)((a->iOsType >= ASOSTYPESBASE) ? TRUE : FALSE)
+
+
+#define PrintString(FLAG, PrintStr) { \
+ if(WaitForSingleObject(ConsoleMutex, INFINITE) != WAIT_FAILED) { \
+ if(FLAG & TOSCREEN) printf("%s", PrintStr); \
+ if(FLAG & TOLOGFILE) fprintf(fplog,"%s", PrintStr); \
+ if(FLAG & TOSUMMARYFILE) fprintf(fpsum,"%s", PrintStr); \
+ fflush(NULL); \
+ ReleaseMutex( ConsoleMutex ); \
+ } \
+ }
+
+
+VOID AddToList(LPMACHINEINFO, LPMACHINEINFO);
+BOOL CheckAccessPermissionOnAllMachines();
+VOID CheckBrowseListsOfMasterAndBackUps(XPORTINFO, LPTSTR, LPTSTR, TCHAR [MAXBACKUPS][CNSLASHLEN+1], ULONG, BOOL);
+BOOL CheckBrServiceOnMachinesInList();
+VOID CleanMem();
+BOOL DoesMachineHaveThisTransport(LPTSTR, XPORTINFO, LPMACHINEINFO);
+VOID DomSpanningMulSubNetsTests(XPORTINFO *, INT);
+BOOL DoSingleDomainTests(XPORTINFO *, INT);
+BOOL FindAllTransports(UNICODE_STRING *, DWORD *);
+VOID ForceElectionAndFindWhoWins(XPORTINFO *, INT, LPTSTR, TCHAR [MAXPROTOCOLS][CNLEN+1], INT);
+VOID Initialize(UNICODE_STRING *, INT *);
+BOOL LocalMachineIsMultihomed(UNICODE_STRING *, INT);
+BOOL NewMasterIsCorrect(TCHAR [MAXPROTOCOLS][CNLEN+1], INT, LPTSTR, XPORTINFO, LPMACHINEINFO);
+VOID ParseCommandLine(INT, CHAR **);
+VOID PrintList(MACHINEINFO);
+
+BOOL StartBrowserFunctionalTest(UNICODE_STRING *, INT);
+BOOL StopBrowserOnCurrentMaster(LPTSTR, LPTSTR, XPORTINFO *,
+ INT, TCHAR [MAXPROTOCOLS][CNLEN+1]);
+BOOL StopBrowsersAndFindWhoBecomesMaster(LPTSTR, LPTSTR, XPORTINFO *,
+ INT, TCHAR [MAXPROTOCOLS][CNLEN+1]);
+VOID Usage(CHAR *);
+
+#endif
diff --git a/private/net/svcdlls/browser/browtest/src/browtest.inp b/private/net/svcdlls/browser/browtest/src/browtest.inp
new file mode 100644
index 000000000..8e07ac52d
--- /dev/null
+++ b/private/net/svcdlls/browser/browtest/src/browtest.inp
@@ -0,0 +1,56 @@
+; Browtest.inp
+;
+; This file serves as the input to the browser tester.
+; It gives information on the machines participating in the test.
+;
+; +--------------------------------------------------------------+
+; | (PDC)(WINS) (PDC) (BDC) |
+; | D1 D1 D1 D2 D1 |
+; | AS3.5 NT3.5 CHICAGO AS3.5 AS3.5 |
+; | | | | | | |
+; | +------------+---------+--------+---- Router ---+ |
+; | | | | | |
+; | AS3.1 NT3.1 WFW311 AS3.5 |
+; | D1 D1 D1 D3 |
+; | (BDC) (PDC) |
+; | |
+; | D1 - Primary Domain, D2 - Domain2 D3 - Domain3 |
+; +--------------------------------------------------------------+
+; Given above is a generic configuration.
+; The test can be done with or without the router.
+; All of the interop machines need not be there in Domain1.
+; WINS can be anywhere.
+; If the router is present, the BDC on the other side should be a AS3.5.
+;
+;
+;
+; Format:
+;
+; DomainName MachineName Type Subnet Protocols
+; eg:
+; D™MI¥ "Green 5" AS3.5 1 TCP, IPX, NBIPX
+; DOMAIN1 Red1 NT3.1 2 IPX, NBIPX
+;
+; MachineName: should be specified with "" if there is a space in the name.
+; Type : AS3.5, AS3.1, NT3.5, NT3.1, CHICAGO, WFW3.11, OS2, DOSLM
+; Subnet : 1 for this subnet, 2 for other subnet.
+; Protocols : TCP, IPX, NBIPX, NETBEUI, XNS
+;
+; Note:
+; a) This test will be done only for the protocols that are installed
+; on the server, on which the test is run. More protocols specified will
+; be ignored. For stress test, all installed protocols will be tested.
+; b) Currently, multihomed machines cannot be tested.
+; c) Only Machinenames are expected in Double quotes.
+; d) Protocols should be seperated by commas.
+;
+;DomainName MachineName Type Subnet Protocols
+ DOMŽDOM’’ ANILTH_2 NT3.5 1 NBIPX, IPX, TCP, NETBEUI
+ DOMŽDOM’’ ANILTH_3 AS3.5 1 NBIPX,IPX, NETBEUI, TCP
+; DOMŽDOM’’ ORANGE6 NT3.5 1 NBIPX,IPX, NETBEUI, TCP
+; DOMŽDOM’’ yellow5 wfw3.11 1 IPX, TCP
+; DOMŽDOM’’ yell6 chicago 1 IPX, NETBEUI
+ BROWDOM2 GREEN5 AS3.5 1 NBIPX, IPX, TCP, NETBEUI
+
+
+
diff --git a/private/net/svcdlls/browser/browtest/src/browutil.c b/private/net/svcdlls/browser/browtest/src/browutil.c
new file mode 100644
index 000000000..1f8a62a51
--- /dev/null
+++ b/private/net/svcdlls/browser/browtest/src/browutil.c
@@ -0,0 +1,780 @@
+#include "browutil.h"
+
+
+extern CHAR PrintBuf[1024];
+extern MACHINEINFO HeadList1;
+
+extern TCHAR wcTESTEDDOMAINS[MAXTESTEDDOMAINS][DNLEN+1];
+extern INT iNumOfTestedDomains;
+extern FILE *fplog;
+extern FILE *fpsum;
+
+extern DWORD ERRCOUNT;
+extern DWORD WRNCOUNT;
+extern DWORD TESTCOUNT;
+extern DWORD SUCSCOUNT;
+extern DWORD OKCOUNT;
+
+extern enum E_PROTOCOLS {IPX, NBIPX, TCP, XNS, NETBEUI};
+extern CHAR *PROTOCOLS[MAXPROTOCOLS];
+
+extern BOOL bIPXHack;
+
+extern LPMACHINEINFO lpLocMachineInfo;
+extern LPMACHINEINFO lpDomStart;
+extern DOMAININFO LocDomInfo;
+
+extern OSPROP OSTYPES[MAXOSTYPES];
+
+extern HANDLE ConsoleMutex;
+
+
+//
+// Compare the browse lists of the master and the backups.
+//
+
+VOID
+CompareListsMasterAndBackUp(PVOID pvMsServerList,
+ DWORD dwEntriesInList,
+ PVOID pvBkServerList,
+ DWORD dwEntriesInBackList,
+ LPTSTR wcDomainName,
+ LPTSTR wcMasterName,
+ LPTSTR wcBackUpBrowser,
+ LPTSTR wcTestedTransport)
+{
+INT i, j;
+PSERVER_INFO_101 pServerInfo101_l1;
+PSERVER_INFO_101 pServerInfo101_l2;
+
+ if(dwEntriesInList != dwEntriesInBackList){
+ sprintf(PrintBuf, "\nThe List in Master and BackUp differs by %ld. Master : %s ", abs(dwEntriesInList - dwEntriesInBackList),
+ UnicodeToPrintfString(wcMasterName));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ sprintf(PrintBuf, " BackUp: %s ",UnicodeToPrintfString(wcBackUpBrowser));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ sprintf(PrintBuf, " Transport: %s.",UnicodeToPrintfString(wcTestedTransport));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+
+ sprintf(PrintBuf, "\nWARNING[WR%ld]: The List in Master and BackUp doesnot match. Transport : %s.", ++WRNCOUNT,
+ UnicodeToPrintfString(wcTestedTransport));
+ PrintString(TOALL, PrintBuf);
+
+ } else {
+ sprintf(PrintBuf, "\nSUCCESS[SC%ld]: The List in Master and BackUp Matches. Transport : %s.", ++SUCSCOUNT,
+ UnicodeToPrintfString(wcTestedTransport));
+ PrintString(TOALL, PrintBuf);
+
+ }
+
+ //
+ // Note : Needs to add the rest of the checking
+ //
+
+}
+
+
+
+//
+// Compare the browse lists of the master to the input file.
+//
+
+VOID
+CompareListsMasterAndFile(PVOID pvServerList,
+ DWORD dwEntriesInList,
+ LPTSTR wcDomainName,
+ LPTSTR wcMachineName,
+ XPORTINFO XportInfo,
+ BOOL bSingleSubnet
+ )
+{
+INT i, index;
+LPMACHINEINFO lpMachineInfo;
+DWORD dwNumNotFound = 0;
+DWORD dwNumWrongFind = 0;
+DWORD dwNumMachinesWithProtocol = 0;
+PSERVER_INFO_101 pServerInfo101;
+
+ sprintf(PrintBuf, "\n\nComparing the Lists from %s to the Input File ",
+ UnicodeToPrintfString(wcMachineName));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+
+ sprintf(PrintBuf, "on transport %s.\n",
+ UnicodeToPrintfString(XportInfo.Transport.Buffer));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+
+ index = XportInfo.index;
+ //
+ // Check if all the servers from the input file are there in the list.
+ //
+ pServerInfo101 = pvServerList;
+ lpMachineInfo = lpDomStart;
+
+ while(lpMachineInfo &&
+ _wcsicmp(lpMachineInfo->wcDomainName,wcDomainName) == 0){
+
+ BOOL MachineHasProtocol = FALSE;
+ MachineHasProtocol = DoesMachineHaveThisTransport(lpMachineInfo->wcMachineName, XportInfo, lpMachineInfo);
+ if(MachineHasProtocol)
+ dwNumMachinesWithProtocol++;
+
+ {
+ //
+ // The ServerNames in pServerInfo is compared with the Input file
+ //
+ i = 0;
+ while (i < (INT)dwEntriesInList && (_wcsicmp(lpMachineInfo->wcMachineName, pServerInfo101[i].sv101_name) != 0))
+ i++;
+
+ //
+ // Not Found
+ //
+ if(i >= (INT)dwEntriesInList){
+ if(MachineHasProtocol){
+ //
+ // For TCP only one subnet info is available if browser is
+ // not running on PDC
+ //
+ if(index == TCP){
+ if(!((lpMachineInfo->iSubnet != SUBNET1) && bSingleSubnet)) {
+ sprintf(PrintBuf, "\nMachine not in Retrieved List: %s.",
+ UnicodeToPrintfString(lpMachineInfo->wcMachineName));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ dwNumNotFound++;
+ }
+ }else{
+ sprintf(PrintBuf, "\nMachine not in Retrieved List: %s.",
+ UnicodeToPrintfString(lpMachineInfo->wcMachineName));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ dwNumNotFound++;
+ }
+ }
+ } else {
+ if(!MachineHasProtocol){
+ sprintf(PrintBuf, "\nMachine %s is NOT expected in Retrieved List!",
+ UnicodeToPrintfString(lpMachineInfo->wcMachineName));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ dwNumWrongFind++;
+ }
+ lpMachineInfo->dwServerBits = pServerInfo101[i].sv101_type;
+ }
+ }
+
+ lpMachineInfo = lpMachineInfo->Next;
+ }
+
+ if(dwNumNotFound){
+ sprintf(PrintBuf,"\nWARNING[WR%ld]: %ld Machines (is)are not in Retrieved List on %s Transport.\n", ++WRNCOUNT, dwNumNotFound,
+ UnicodeToPrintfString(XportInfo.Transport.Buffer));
+ PrintString(TOALL, PrintBuf);
+ } else {
+ sprintf(PrintBuf,"\nSUCCESS[SC%ld]: Matched all machines in input file on transport %s.\n", ++SUCSCOUNT,
+ UnicodeToPrintfString(XportInfo.Transport.Buffer));
+ PrintString(TOALL, PrintBuf);
+ }
+
+ if(dwNumWrongFind){
+ sprintf(PrintBuf,"\n\nWARNING[WR%ld]: %ld Machines Wrongly found in Retrieved List on %s Transport.\n", ++WRNCOUNT, dwNumWrongFind,
+ UnicodeToPrintfString(XportInfo.Transport.Buffer));
+ PrintString(TOALL, PrintBuf);
+ }
+
+}
+
+
+ //
+ // Find the Master for a domain for a transport
+ //
+
+NET_API_STATUS
+GetMasterName( XPORTINFO *TestedXportInfo,
+ INT iNumOfTestedXports,
+ INT index,
+ LPTSTR Domain,
+ LPTSTR Master)
+{
+NET_API_STATUS Status;
+
+ //
+ // For IPX we need a hack. GetNetBiosMasterName will fail in Daytona (NT3.5)
+ // and NT3.1. So we find the BackUp on NwlnkIpx , retrieve the List from one
+ // of those through NwLnkNb and see who is the master in the List
+ //
+ if((TestedXportInfo[index].index == IPX) && bIPXHack){
+
+ TCHAR wcBackUpBrowsers[MAXBACKUPS][CNSLASHLEN+1];
+ INT i, ibcount=0;
+ ULONG ulNumBackUps = 0;
+ DWORD dwEntriesInList;
+ DWORD dwTotalEntries;
+ PVOID pvServerList;
+ PSERVER_INFO_101 pServerInfo101_l1;
+
+ //
+ // If the local browse master is not started then we cannot get the backup
+ // list in nwlnkipx so we use nwlnknb.
+ //
+ if(lpLocMachineInfo->BrowserServiceStarted)
+ Status = GetBList(TestedXportInfo[index].Transport, Domain, TRUE, &ulNumBackUps, wcBackUpBrowsers);
+ else {
+ for(i = 0; i < iNumOfTestedXports && TestedXportInfo[i].index != NBIPX; i++);
+ if(i < iNumOfTestedXports)
+ Status = GetBList(TestedXportInfo[i].Transport, Domain, TRUE, &ulNumBackUps, wcBackUpBrowsers);
+ else
+ return(!NERR_Success);
+ }
+
+ if(Status == NERR_Success) {
+
+ if(ulNumBackUps){
+
+ do{
+
+ if((Status = RetrieveList(wcBackUpBrowsers[ibcount], L"\\Device\\NwlnkNb", &pvServerList,
+ &dwEntriesInList, &dwTotalEntries, (SV_TYPE_ALTERNATE_XPORT | SV_TYPE_MASTER_BROWSER), NULL, NULL, FALSE)) == NERR_Success){
+
+ if(dwEntriesInList) {
+ pServerInfo101_l1 = pvServerList;
+ wcscpy(Master, pServerInfo101_l1[0].sv101_name);
+
+ } else {
+
+ Status = (!NERR_Success);
+ }
+
+ }// Retrieve List
+
+ NetApiBufferFree(pvServerList);
+ ibcount++;
+
+ }while((Status != NERR_Success) && (ibcount < (INT)ulNumBackUps));
+
+ } else // If got any BackUp
+ return (!NERR_Success);
+
+ } // If GetBList
+
+ return Status;
+ }
+
+ //
+ // For TCP we need to clear the local cache before making a new call
+ // to GetNetBiosMasterName
+ //
+ if(TestedXportInfo[index].index == TCP)
+// system("NbtStat -R");
+ ClearNbtNameTableCache(TestedXportInfo[index].Transport);
+
+
+ Status = GetNetBiosMasterName(
+ TestedXportInfo[index].Transport.Buffer,
+ Domain,
+ Master,
+ NULL);
+
+
+
+ return Status;
+}
+
+
+//
+// Are there any browsers servers available to be a master.
+//
+BOOL
+MasterAvailable(XPORTINFO XportInfo,
+ LPTSTR wcDomainName,
+ INT iSubnet)
+{
+INT index;
+LPMACHINEINFO lpMachineInfo;
+
+ index = XportInfo.index;
+ //
+ // We check whether the machine has the protocol. If the protocol is TCP we
+ // have to check whether it is in the same subnet.If it has then see if it
+ // is an NT. If it is not then return TRUE because the Low level machine
+ // could become a Master browser. If it is NT then check to see whether
+ // Browser is started on it and if so then
+ //
+ for(lpMachineInfo=lpDomStart; lpMachineInfo && _wcsicmp(lpMachineInfo->wcDomainName,
+ wcDomainName) == 0; lpMachineInfo=lpMachineInfo->Next){
+
+ if(lpMachineInfo->Protocols[index]){
+
+ if((index == TCP) && (lpMachineInfo->iSubnet != iSubnet))
+ continue;
+
+ if(IsNTMachine(lpMachineInfo)){
+ if(lpMachineInfo->BrowserServiceStarted) {
+ sprintf(PrintBuf, "\nMaster Available %s.",UnicodeToPrintfString(lpMachineInfo->wcMachineName));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ return TRUE;
+ }
+ } else { // Not an NT
+ //
+ // For IPX we cannot get the Master since there are no masters
+ // running nwlnknb.
+ //
+ if(bIPXHack && (index == IPX))
+ continue;
+ else{
+ sprintf(PrintBuf, "\nMaster Available %s.",UnicodeToPrintfString(lpMachineInfo->wcMachineName));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ return TRUE;
+ }
+ }
+ }
+ }
+
+return FALSE;
+}
+
+
+BOOL
+ReadInputFile(VOID)
+{
+INT i;
+CHAR cbReadBuf[MAXCHARPERLINE];
+CHAR cbTmpBuf[16];
+PCHAR lpTmpPtr;
+PCHAR lpTokPtr;
+FILE *fpin;
+LPMACHINEINFO lpMachineInfo;
+
+ if((fpin = fopen(BROWTESTINFILE, "r")) == NULL){
+ sprintf(PrintBuf,"\nError opening the input file %s!!\n", BROWTESTINFILE);
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ return FALSE;
+ }
+
+ while(fgets(cbReadBuf, MAXCHARPERLINE, fpin) != NULL){
+ BOOL QUOTEFOUND = FALSE;
+
+ //
+ // Skip lines starting with the COMMENTCHAR sign
+ //
+ for(lpTmpPtr = cbReadBuf; (*lpTmpPtr) == SPACECHAR; lpTmpPtr++);
+ if((*lpTmpPtr) == COMMENTCHAR || (*lpTmpPtr) == NEWLINECHAR){
+ continue;
+ }
+
+
+// printf("%s", cbReadBuf);
+ //
+ // Check if Machine name is given in quotes
+ //
+ for(lpTokPtr = lpTmpPtr; ((*lpTokPtr) != '\0') &&
+ ((*lpTokPtr) != QUOTECHAR); lpTokPtr++);
+
+ if((*lpTokPtr) == QUOTECHAR)
+ QUOTEFOUND = TRUE;
+
+ //
+ // Get the domain name
+ //
+ if((lpTokPtr = strtok(lpTmpPtr, SPACETABSTR)) == NULL){
+ printf("Error in parsing the Domain Name: %s", cbReadBuf);
+ fclose(fpin);
+ return FALSE;
+ }
+
+ //
+ // Allocate the Machine Info Structure
+ //
+ if((lpMachineInfo = (LPMACHINEINFO) calloc(1, sizeof(MACHINEINFO))) == NULL){
+ printf("\nError. Not enough memory for MachineInfo\n");
+ fclose(fpin);
+ return FALSE;
+ }
+ memset(lpMachineInfo, 0, sizeof(MACHINEINFO));
+
+// RemoveTabs(&lpTokPtr);
+ MultiByteToWideChar(CP_OEMCP, MB_PRECOMPOSED, lpTokPtr, strlen(lpTokPtr)+1, lpMachineInfo->wcDomainName, sizeof(lpMachineInfo->wcDomainName));
+// printf("Domain: XXX%sXXX\n", UnicodeToPrintfString(lpMachineInfo->wcDomainName));
+
+ //
+ // Get the machine name
+ //
+
+ if(QUOTEFOUND){
+ //
+ // Machine name given in quotes
+ //
+ if((lpTokPtr = strtok(NULL, QUOTESTR)) == NULL){
+ printf("(1)Error in parsing the Machine Name: %s", cbReadBuf);
+ free(lpMachineInfo);
+ fclose(fpin);
+ return FALSE;
+ }
+
+ if((lpTokPtr = strtok(NULL, QUOTESTR)) == NULL){
+ printf("(2)Error in parsing the Machine Name: %s", cbReadBuf);
+ free(lpMachineInfo);
+ fclose(fpin);
+ return FALSE;
+ }
+
+ MultiByteToWideChar(CP_OEMCP, MB_PRECOMPOSED, lpTokPtr, strlen(lpTokPtr)+1, lpMachineInfo->wcMachineName, sizeof(lpMachineInfo->wcMachineName));
+
+ } else {
+
+ //
+ // Machine name not in quotes
+ //
+ if((lpTokPtr = strtok(NULL, SPACETABSTR)) == NULL){
+ printf("(3)Error in parsing the Machine Name: %s", cbReadBuf);
+ free(lpMachineInfo);
+ fclose(fpin);
+ return FALSE;
+ }
+
+// RemoveTabs(&lpTokPtr);
+ MultiByteToWideChar(CP_OEMCP, MB_PRECOMPOSED, lpTokPtr, strlen(lpTokPtr)+1, lpMachineInfo->wcMachineName, sizeof(lpMachineInfo->wcMachineName));
+ }
+
+// printf("MachineName: XXX%sXXX\n", UnicodeToPrintfString(lpMachineInfo->wcMachineName));
+
+ //
+ // Get the type (eg: AS3.5)
+ //
+ if((lpTokPtr = strtok(NULL, SPACETABSTR)) == NULL){
+ printf("Error in parsing the Type. Machine: %s", UnicodeToPrintfString(lpMachineInfo->wcMachineName));
+ free(lpMachineInfo);
+ fclose(fpin);
+ return FALSE;
+ }
+
+// RemoveTabs(&lpTokPtr);
+ for(i=0; i<MAXOSTYPES && _strnicmp(lpTokPtr, OSTYPES[i].Type, strlen(OSTYPES[i].Type)) != 0; i++);
+ if(i < MAXOSTYPES){
+ lpMachineInfo->iOsType = i;
+ lpMachineInfo->iOsPreference = OSTYPES[i].iPreference;
+
+// printf("ZZZ%sZZZ OSTYPE=%s\n", lpTokPtr, OSTYPES[i]);
+ } else {
+ printf("\nError unknown OsType: %s\n", lpTokPtr);
+ free(lpMachineInfo);
+ fclose(fpin);
+ return FALSE;
+ }
+
+ //
+ // Get the Subnet
+ //
+ if((lpTokPtr = strtok(NULL, SPACETABSTR)) == NULL){
+ printf("Error in parsing the Subnet. Machine: %s", UnicodeToPrintfString(lpMachineInfo->wcMachineName));
+ free(lpMachineInfo);
+ fclose(fpin);
+ return FALSE;
+ }
+
+// RemoveTabs(&lpTokPtr);
+ lpMachineInfo->iSubnet = atoi(lpTokPtr);
+// printf("Subnet: XXX%dXXX\n", atoi(lpTokPtr));
+ if(!(lpMachineInfo->iSubnet == SUBNET1 || lpMachineInfo->iSubnet == SUBNET2)){
+ printf("\nUnknown Subnet specified: %d\n", lpMachineInfo->iSubnet);
+ fclose(fpin);
+ return FALSE;
+ }
+
+ //
+ // Get the list of protocols
+ //
+ if((lpTokPtr = strtok(NULL, NEWLINESTR)) == NULL){
+ printf("Error in parsing the Protocols. Machine: %s", UnicodeToPrintfString(lpMachineInfo->wcMachineName));
+ free(lpMachineInfo);
+ fclose(fpin);
+ return FALSE;
+ }
+
+
+ RemoveTabs(&lpTokPtr);
+ lpTmpPtr = lpTokPtr;
+ if((lpTokPtr = strtok(lpTmpPtr, COMMASTR)) == NULL){
+ printf("Error in parsing the first protocol. Machine: %s", UnicodeToPrintfString(lpMachineInfo->wcMachineName));
+ free(lpMachineInfo);
+ fclose(fpin);
+ return FALSE;
+ }
+ for(lpTmpPtr = lpTokPtr; (*lpTmpPtr != '\0') && (*lpTmpPtr == SPACECHAR); lpTmpPtr++);
+ RemoveTabs(&lpTmpPtr);
+ for(i=0; i<MAXPROTOCOLS && _strnicmp(lpTmpPtr, PROTOCOLS[i], strlen(PROTOCOLS[i])) != 0; i++);
+ if(i < MAXPROTOCOLS){
+ lpMachineInfo->Protocols[i] = TRUE;
+// printf("ZZZ%sZZZ PROTOCOL=%s\n", lpTmpPtr, PROTOCOLS[i]);
+ }
+ else {
+ printf("\n(1)Error unknown Protocol: %s\n", lpTmpPtr);
+ free(lpMachineInfo);
+ fclose(fpin);
+ return FALSE;
+ }
+
+
+ while((lpTokPtr = strtok(NULL, COMMASTR)) != NULL){
+ for(lpTmpPtr = lpTokPtr; (*lpTmpPtr != '\0') && (*lpTmpPtr == SPACECHAR); lpTmpPtr++);
+ RemoveTabs(&lpTmpPtr);
+ for(i=0; i<MAXPROTOCOLS && _strnicmp(lpTmpPtr, PROTOCOLS[i], strlen(PROTOCOLS[i])) != 0; i++);
+ if(i < MAXPROTOCOLS){
+ lpMachineInfo->Protocols[i] = TRUE;
+// printf("ZZZ%sZZZ PROTOCOL=%s\n", lpTmpPtr, PROTOCOLS[i]);
+ } else {
+ printf("\n(2)Error unknown Protocol: %s\n", lpTmpPtr);
+ free(lpMachineInfo);
+ fclose(fpin);
+ return FALSE;
+ }
+ }
+
+ AddToList(&HeadList1, lpMachineInfo);
+
+
+ } // while(fgets)
+
+fclose(fpin);
+return TRUE;
+}
+
+
+VOID
+RemoveTabs(PCHAR *lpTokenStr)
+{
+PCHAR lpTmpPtr;
+PCHAR lpString;
+
+
+ lpString = *lpTokenStr;
+
+ //
+ // Remove beginning tabs
+ //
+ while(*lpString == '\t' && *lpString != '\0')
+ lpString++;
+
+ *lpTokenStr = lpString;
+
+ //
+ // empty string
+ //
+ if (*lpString == '\0')
+ return;
+
+ //
+ // Point to the last character in the string
+ //
+ lpTmpPtr = lpString + strlen(lpString) - 1;
+
+
+ while(*lpTmpPtr == '\t' && lpTmpPtr != lpString){
+ lpTmpPtr--;
+ }
+
+
+ *(++lpTmpPtr) = '\0';
+
+}
+
+
+NET_API_STATUS
+RetrieveList(LPTSTR wcMachineName,
+ LPTSTR wcTestedTransport,
+ LPVOID *ppvList,
+ LPDWORD pdwEntriesInList,
+ LPDWORD pdwTotalEntries,
+ DWORD Flag,
+ LPTSTR wcDomain,
+ LPDWORD pdwHandle,
+ BOOL ErrMsg
+ )
+{
+DWORD dwStartTime;
+DWORD dwEndTime;
+TCHAR wcTmpName[CNSLASHLEN+1];
+NET_API_STATUS Status;
+
+ if(_wcsnicmp(wcMachineName, L"\\\\", 2) != 0) {
+ wcscpy(wcTmpName, L"\\\\");
+ wcscat(wcTmpName, wcMachineName);
+
+ } else {
+ wcscpy(wcTmpName, wcMachineName);
+ }
+
+ dwStartTime = GetTickCount();
+ Status = RxNetServerEnum(wcTmpName,
+ wcTestedTransport,
+ 101,
+ (LPBYTE *)ppvList,
+ 0xffffffff,
+ pdwEntriesInList,
+ pdwTotalEntries,
+ Flag,
+ wcDomain,
+ NULL
+ );
+
+ dwEndTime = GetTickCount();
+
+ if(ErrMsg){
+ if (Status != NERR_Success) {
+ sprintf(PrintBuf,"\nERROR[ER%ld]:Unable to retrieve List from %s ", ++ERRCOUNT, UnicodeToPrintfString(wcTmpName));
+ PrintString(TOALL, PrintBuf);
+ sprintf(PrintBuf,"on transport %s with Flag %lx. \nError: %s (%ld milliseconds)\n", UnicodeToPrintfString(wcTestedTransport), Flag, get_error_text(Status), dwEndTime - dwStartTime);
+ PrintString(TOALL, PrintBuf);
+
+// if (Status != ERROR_MORE_DATA) {
+// exit(1);
+// }
+ } else {
+ sprintf(PrintBuf,"\nINFO:Retrieved List from %s ", UnicodeToPrintfString(wcTmpName));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ sprintf(PrintBuf,"on transport %s with Flag %lx: (%ld milliseconds).", UnicodeToPrintfString(wcTestedTransport), Flag, dwEndTime - dwStartTime);
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ }
+ }
+
+ return Status;
+}
+
+
+NET_API_STATUS
+StartBrowserService(LPTSTR wcMachineName)
+{
+DWORD LastError;
+SC_HANDLE ServiceControllerHandle;
+SC_HANDLE ServiceHandle;
+
+ sprintf(PrintBuf,"\nTrying to start Browser on %s.", UnicodeToPrintfString(wcMachineName));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+
+ ServiceControllerHandle = OpenSCManager(wcMachineName, NULL, SC_MANAGER_ALL_ACCESS);
+ if (ServiceControllerHandle == NULL) {
+ return GetLastError();
+ }
+
+ ServiceHandle = OpenService(ServiceControllerHandle, BROWSER, SERVICE_ALL_ACCESS);
+ if (ServiceHandle == NULL) {
+ CloseServiceHandle(ServiceControllerHandle);
+ return GetLastError();
+ }
+
+
+ if(!StartService(ServiceHandle, 0, NULL)){
+ CloseServiceHandle(ServiceHandle);
+ CloseServiceHandle(ServiceControllerHandle);
+
+ LastError = GetLastError();
+
+ if(LastError != ERROR_SERVICE_ALREADY_RUNNING)
+ return LastError;
+ else {
+ sprintf(PrintBuf,"\nBrowser already running on %s.\n", UnicodeToPrintfString(wcMachineName));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ return NERR_Success;
+ }
+ }
+
+ sprintf(PrintBuf,"\nStarted Browser on %s.\n", UnicodeToPrintfString(wcMachineName));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+
+ CloseServiceHandle(ServiceHandle);
+ CloseServiceHandle(ServiceControllerHandle);
+ return NERR_Success;
+}
+
+
+NET_API_STATUS
+StopBrowserService(LPTSTR wcMachineName)
+{
+DWORD LastError;
+SC_HANDLE ServiceControllerHandle;
+SC_HANDLE ServiceHandle;
+SERVICE_STATUS ssServiceStatus;
+
+ sprintf(PrintBuf,"\nTrying to stop Browser on %s.", UnicodeToPrintfString(wcMachineName));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+
+ ServiceControllerHandle = OpenSCManager(wcMachineName, NULL, SC_MANAGER_ALL_ACCESS);
+ if (ServiceControllerHandle == NULL) {
+ return GetLastError();
+ }
+
+ ServiceHandle = OpenService(ServiceControllerHandle, BROWSER, SERVICE_ALL_ACCESS);
+ if (ServiceHandle == NULL) {
+ CloseServiceHandle(ServiceControllerHandle);
+ return GetLastError();
+ }
+
+
+ if(!ControlService(ServiceHandle, SERVICE_CONTROL_STOP, &ssServiceStatus)){
+ LastError = GetLastError();
+ if(LastError != ERROR_SERVICE_NOT_ACTIVE){
+ CloseServiceHandle(ServiceHandle);
+ CloseServiceHandle(ServiceControllerHandle);
+ return LastError;
+ } else {
+ sprintf(PrintBuf,"\nService not started on %s.\n", UnicodeToPrintfString(wcMachineName));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ CloseServiceHandle(ServiceHandle);
+ CloseServiceHandle(ServiceControllerHandle);
+ return NERR_Success;
+ }
+ }
+
+ if(!((ssServiceStatus.dwCurrentState == SERVICE_STOP_PENDING) ||
+ (ssServiceStatus.dwCurrentState == SERVICE_STOPPED))){
+
+ sprintf(PrintBuf,"\nCould not stop Browser service on %s\n", UnicodeToPrintfString(wcMachineName));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ CloseServiceHandle(ServiceHandle);
+ CloseServiceHandle(ServiceControllerHandle);
+ return (!NERR_Success);
+ }
+
+
+
+ sprintf(PrintBuf,"\nStopped Browser service on %s.\n", UnicodeToPrintfString(wcMachineName));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+
+ CloseServiceHandle(ServiceHandle);
+ CloseServiceHandle(ServiceControllerHandle);
+ return NERR_Success;
+}
+
+
+NET_API_STATUS
+QueryBrowserServiceStatus(LPMACHINEINFO lpMachineInfo, LPSERVICE_STATUS lpssServiceStatus)
+{
+SC_HANDLE ServiceControllerHandle;
+SC_HANDLE ServiceHandle;
+
+ sprintf(PrintBuf,"\n\nCheck whether Browser is started on %s.", UnicodeToPrintfString(lpMachineInfo->wcMachineName));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+
+ ServiceControllerHandle = OpenSCManager(lpMachineInfo->wcMachineName, NULL, SC_MANAGER_ALL_ACCESS);
+ if (ServiceControllerHandle == NULL) {
+ return GetLastError();
+ }
+
+ ServiceHandle = OpenService(ServiceControllerHandle, BROWSER, SERVICE_ALL_ACCESS);
+ if (ServiceHandle == NULL) {
+ CloseServiceHandle(ServiceControllerHandle);
+ return GetLastError();
+ }
+
+
+ if(!QueryServiceStatus(ServiceHandle, lpssServiceStatus)){
+ CloseServiceHandle(ServiceHandle);
+ CloseServiceHandle(ServiceControllerHandle);
+ return GetLastError();
+ }
+
+
+ CloseServiceHandle(ServiceHandle);
+ CloseServiceHandle(ServiceControllerHandle);
+ return NERR_Success;
+}
+
+
+
diff --git a/private/net/svcdlls/browser/browtest/src/browutil.h b/private/net/svcdlls/browser/browtest/src/browutil.h
new file mode 100644
index 000000000..d5043affa
--- /dev/null
+++ b/private/net/svcdlls/browser/browtest/src/browutil.h
@@ -0,0 +1,16 @@
+#include "browfunc.h"
+#include "browtest.h"
+
+
+VOID CompareListsMasterAndBackUp(PVOID, DWORD, PVOID, DWORD, LPTSTR, LPTSTR, LPTSTR, LPTSTR);
+VOID CompareListsMasterAndFile(PVOID, DWORD, LPTSTR, LPTSTR, XPORTINFO, BOOL);
+NET_API_STATUS GetMasterName(XPORTINFO *, INT, INT, LPTSTR, LPTSTR);
+BOOL MasterAvailable(XPORTINFO, LPTSTR, INT);
+BOOL ReadInputFile(VOID);
+VOID RemoveTabs(PCHAR *);
+NET_API_STATUS RetrieveList(LPTSTR, LPTSTR, LPVOID, LPDWORD, LPDWORD, DWORD, LPTSTR, LPDWORD, BOOL);
+NET_API_STATUS StartBrowserService(LPTSTR);
+NET_API_STATUS StopBrowserService(LPTSTR);
+NET_API_STATUS QueryBrowserServiceStatus(LPMACHINEINFO, LPSERVICE_STATUS);
+
+
diff --git a/private/net/svcdlls/browser/browtest/src/makefile b/private/net/svcdlls/browser/browtest/src/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/net/svcdlls/browser/browtest/src/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/net/svcdlls/browser/browtest/src/makefile.inc b/private/net/svcdlls/browser/browtest/src/makefile.inc
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/private/net/svcdlls/browser/browtest/src/makefile.inc
diff --git a/private/net/svcdlls/browser/browtest/src/sources b/private/net/svcdlls/browser/browtest/src/sources
new file mode 100644
index 000000000..ee9388073
--- /dev/null
+++ b/private/net/svcdlls/browser/browtest/src/sources
@@ -0,0 +1,70 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+
+Author:
+
+ Steve Wood (stevewo) 12-Apr-1989
+
+
+Revision History:
+
+!ENDIF
+
+MAJORCOMP = net
+MINORCOMP = browtest
+
+NTPROFILEINPUT=YES
+
+TARGETNAME=browfunc
+TARGETPATH=obj
+TARGETTYPE=LIBRARY
+
+
+INCLUDES=..;..\..;..\..\..\..\inc;$(BASEDIR)\private\inc
+
+
+!IFNDEF DISABLE_NET_UNICODE
+UNICODE=1
+NET_C_DEFINES=-DUNICODE
+!ENDIF
+
+SOURCES=browfunc.c \
+ browutil.c \
+ browmdom.c \
+ browstrs.c
+
+
+
+C_DEFINES=-DRPC_NO_WINDOWS_H
+
+WARNING_LEVEL=-W4
+
+USE_CRTDLL=1
+
+UMTYPE=console
+UMAPPL=browtest
+UMTEST=
+UMLIBS=$(BASEDIR)\public\sdk\lib\*\netapi32.lib \
+ obj\*\browfunc.lib \
+ ..\..\client\daytona\obj\*\bowser.lib \
+ ..\..\common\daytona\obj\*\brcommon.lib\
+ $(BASEDIR)\public\sdk\lib\*\rpcutil.lib \
+ $(BASEDIR)\public\sdk\lib\*\ntdll.lib \
+ $(BASEDIR)\public\sdk\lib\*\netlib.lib \
+ $(BASEDIR)\public\sdk\lib\*\advapi32.lib \
+ $(BASEDIR)\public\sdk\lib\*\user32.lib \
+
+
diff --git a/private/net/svcdlls/browser/bwatch/bwatch.c b/private/net/svcdlls/browser/bwatch/bwatch.c
new file mode 100644
index 000000000..2cd5ebfe5
--- /dev/null
+++ b/private/net/svcdlls/browser/bwatch/bwatch.c
@@ -0,0 +1,704 @@
+/*++
+
+Copyright (c) 1993 Micorsoft Corporation
+
+Module Name:
+
+ bwatch.c
+
+Abstract:
+
+ Browser Monitor main program.
+
+Author:
+ Dan Hinsley (DanHi) 10-Oct-1992
+ Congpa You (CongpaY) 10-Feb-1993
+
+Revision History:
+
+--*/
+#define INCLUDE_SMB_TRANSACTION
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <sys\types.h>
+#include <sys\stat.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <windows.h>
+#include <lm.h>
+#include <ntddbrow.h>
+#include <brcommon.h>
+#include <rpcutil.h>
+#include <nb30.h>
+#include <smbtypes.h>
+#include <smb.h>
+#include <smbgtpt.h>
+#include <hostannc.h>
+#include "bwatch.h"
+
+// main is going to be changed into a function which taks lpTransportName
+// and pDomainName as parameter.
+_cdecl main()
+{
+ INT nFileSizeLimit;
+ INT nIndex = 0;
+ DWORD dwVal;
+ CHAR pDomain[BUFFERLENGTH];
+ FILE * pFile;
+ PLMDR_TRANSPORT_LIST TransportList = NULL;
+ PLMDR_TRANSPORT_LIST TransportEntry = NULL;
+
+ // Tell user the program is running.
+ fprintf(stdout, "BWATCH is started.\n") ;
+
+ //Create a logfile for writing all the errors and other information to it.
+ pFile = fopen (szBWATCH, "w+");
+
+ // Print the start new run header in the logfile.
+ PrintHeader (pFile);
+
+ // Initialize global data.
+ if (!Init())
+ return;
+
+ // Get lpDomain from bwatch.ini.
+ if (!GetDomain (pDomain))
+ return;
+
+ nFileSizeLimit = GetLimit();
+
+ // Find all transports that we have.
+ dwVal = GetBrowserTransportList (&TransportList);
+ if (dwVal != NERR_Success)
+ {
+ if (TransportList != NULL)
+ {
+ MIDL_user_free (TransportList);
+ }
+ ReportError (dwVal);
+ return;
+ }
+
+ TransportEntry = TransportList;
+
+ // Enumerate on the transports.
+ while (TransportEntry != NULL)
+ {
+ CHAR * pDomainName;
+ CHAR pDomainList[BUFFERLENGTH];
+ CCHAR lana_num;
+ NET_API_STATUS Status;
+ PSUPER_NCB psuperncb;
+
+ strcpy (pDomainList, pDomain);
+
+ pDomainName = strtok (pDomainList, szSeps);
+
+ // Initialize psuperncb.
+
+ Status = BrGetLanaNumFromNetworkName (TransportEntry->TransportName, &lana_num);
+
+ if (Status != NERR_Success) {
+ ReportError (Status);
+ MIDL_user_free (TransportList);
+ return;
+ }
+
+ // Reset the adapter. You have to reset before doing anything.
+ if (Reset(FALSE, lana_num))
+ {
+ while (pDomainName != NULL)
+ {
+ psuperncb = (PSUPER_NCB) LocalAlloc (LPTR, sizeof(*psuperncb));
+ if (psuperncb != NULL)
+ {
+ psuperncb->lanaNumber = lana_num;
+
+ // Add DOMAIN(1e).
+ Registe (TransportEntry->TransportName, pDomainName, psuperncb, nIndex);
+
+ // Run Netbios.
+ SubmitRCDg (psuperncb);
+
+ nIndex++;
+ }
+
+ pDomainName = strtok (NULL, szSeps);
+ }
+
+#ifdef INCLUDE_MSBROWSE
+ psuperncb = (PSUPER_NCB) LocalAlloc (LPTR, sizeof(*psuperncb));
+
+ if (psuperncb != NULL)
+ {
+ psuperncb->lanaNumber = lana_num;
+
+ // Add MSBROWSE.
+ RegisteWkgroupName (TransportEntry->TransportName, "MSBROWSE", psuperncb, nIndex);
+
+ // Run Netbios.
+ SubmitRCDg (psuperncb);
+
+ nIndex++;
+ }
+#endif // INCLUDE_BRWOSE
+
+ }
+
+ if (TransportEntry->NextEntryOffset == 0)
+ {
+ TransportEntry = NULL;
+ }
+ else
+ {
+ TransportEntry = (PLMDR_TRANSPORT_LIST) ((PCHAR) TransportEntry
+ + TransportEntry->NextEntryOffset);
+ }
+ }
+
+ // Free the memory of TransportList.
+ MIDL_user_free (TransportList);
+
+ // Tell user the program is running.
+ fprintf(stdout, "BWATCH is now running.\n") ;
+
+ while (TRUE)
+ {
+ ProcessQueue(pFile, nFileSizeLimit);
+ }
+}
+
+// Report if an error occurs.
+void ReportError (DWORD dwError)
+{
+ printf("An error occured: %lu", dwError);
+}
+
+// Initialize the structure.
+BOOL Init()
+{
+ InitializeListHead (&WorkQueueHead);
+ InitializeListHead (&FreeQueueHead);
+
+ InitializeCriticalSection (&CSWorkQueue);
+ InitializeCriticalSection (&CSFreeQueue);
+
+ hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+
+ if (hEvent == NULL)
+ {
+ return(FALSE);
+ }
+ return(TRUE);
+}
+
+// Before we start a new session, we have to reset.
+// ncb.lana_num must be already set.
+
+BOOL Reset (UCHAR Reset,
+ CCHAR LanaNum)
+{
+ NCB ncb;
+ ClearNcb( &ncb );
+ ncb.ncb_command = NCBRESET;
+
+ ncb.ncb_lsn = Reset;
+
+ ncb.ncb_callname[0] = ncb.ncb_callname[1] = ncb.ncb_callname[2] =
+ ncb.ncb_callname[3] = 0;
+
+ ncb.ncb_lana_num = LanaNum;
+
+ Netbios( &ncb );
+ if ( ncb.ncb_retcode != NRC_GOODRET ) {
+ printf( "Reset returned an error %lx\n", ncb.ncb_retcode);
+ return(FALSE);
+ }
+
+ return(TRUE);
+}
+
+// Registe a session.
+void Registe (LPTSTR lpTransportName,
+ CHAR * pDomainName,
+ PSUPER_NCB psuperncb,
+ INT nIndex)
+{
+ // Receive Datagram.
+ printf ("Running Netbios on transport %s, domain %s...\n", toansi(lpTransportName), pDomainName);
+
+ strcpy (psuperncb->pTransportName, toansi(lpTransportName));
+
+ strcpy (psuperncb->pDomainName, pDomainName);
+
+ psuperncb->nIndex = nIndex;
+
+ psuperncb->ncb.ncb_lana_num = psuperncb->lanaNumber;
+
+ // Registe DOMAIN(1e) for hearing Election and AnnouncementRequest packets.
+ AddName(0x1e, (NCB *) psuperncb);
+
+ psuperncb->nameNumber = psuperncb->ncb.ncb_num;
+
+}
+
+// Registe Netbios name.
+VOID AddName(UCHAR Suffix,
+ NCB * pncb)
+{
+ CHAR localName[32];
+
+ strcpy (localName, _strupr(((PSUPER_NCB) pncb)->pDomainName));
+ strcat (localName, SIXTEENSPACES);
+
+ ClearNcb( pncb );
+ pncb->ncb_command = NCBADDGRNAME;
+
+ localName[15] = Suffix;
+ RtlCopyMemory( pncb->ncb_name, localName, 16);
+ pncb->ncb_lana_num = ((PSUPER_NCB) pncb)->lanaNumber;
+ Netbios( pncb );
+ if ( pncb->ncb_retcode != NRC_GOODRET ) {
+ printf( "AddGroupname 0x%x returned an error %lx\n", Suffix,
+ pncb->ncb_retcode);
+ return;
+ }
+}
+
+// Registe a session.
+void RegisteWkgroupName (LPTSTR lpTransportName,
+ CHAR * pDomainName,
+ PSUPER_NCB psuperncb,
+ INT nIndex)
+{
+ CHAR localName[16];
+
+ printf ("Running Netbios on transport %s, domain %s...\n", toansi(lpTransportName), pDomainName);
+
+ strcpy (psuperncb->pTransportName, toansi(lpTransportName));
+
+ strcpy (psuperncb->pDomainName, pDomainName);
+
+ psuperncb->nIndex = nIndex;
+
+ psuperncb->ncb.ncb_lana_num = psuperncb->lanaNumber;
+
+ // Jam in Domain announcement name
+ strcpy (&localName[2], "__MSBROWSE__");
+ localName[0] = 0x01;
+ localName[1] = 0x02;
+ localName[14] = 0x02;
+ localName[15] = 0x01;
+
+ RtlCopyMemory (psuperncb->ncb.ncb_name, localName, 16);
+ psuperncb->ncb.ncb_command = NCBADDGRNAME;
+ psuperncb->ncb.ncb_lana_num = psuperncb->lanaNumber;
+ Netbios ((NCB *) psuperncb);
+
+ if (psuperncb->ncb.ncb_retcode != NRC_GOODRET) {
+ printf("AddGroupname MSBROUSE returned an error %lx\n", psuperncb->ncb.ncb_retcode);
+ }
+
+ psuperncb->nameNumber = psuperncb->ncb.ncb_num;
+
+}
+
+// This is the function which calls Netbios.
+void SubmitRCDg (PSUPER_NCB psuperncb)
+{
+ ClearNcb ((NCB *) psuperncb);
+
+ psuperncb->ncb.ncb_command = NCBDGRECV|ASYNCH;
+ psuperncb->ncb.ncb_lana_num = (UCHAR)psuperncb->lanaNumber;
+ psuperncb->ncb.ncb_num = psuperncb->nameNumber;
+ psuperncb->ncb.ncb_length = BUFFERLENGTH;
+ psuperncb->ncb.ncb_buffer = psuperncb->Buffer;
+ psuperncb->ncb.ncb_post = RCDgPost;
+ Netbios ((NCB *) psuperncb);
+}
+
+// Netbios's call back function.
+void RCDgPost (NCB * pncb)
+{
+ PSUPER_NCB psuperncb = (PSUPER_NCB) pncb;
+
+ if (pncb->ncb_retcode != NRC_GOODRET)
+ {
+ printf ("ReceiveDatagram returned an error %lx\n", pncb->ncb_retcode);
+ if (pncb->ncb_retcode != NRC_INCOMP)
+ {
+ return;
+ }
+ }
+
+ LoadWorkQueue (psuperncb);
+
+ SubmitRCDg(psuperncb);
+}
+
+// This is the function get entries from the queue and print out the content.
+void ProcessQueue(FILE * pFile, INT nFileSizeLimit)
+{
+ PQUEUE_ENTRY pBuffer;
+ struct _stat buf;
+ BOOL fRet;
+
+ if (_fstat (_fileno(pFile), &buf) != -1)
+ {
+ if (buf.st_size > nFileSizeLimit)
+ {
+ fflush (pFile);
+
+ fclose (pFile);
+
+ fRet = MoveFileEx (szLOGFILE, szBACKUP, MOVEFILE_REPLACE_EXISTING);
+ if (!fRet)
+ {
+ pFile = fopen (szBWATCH, "w+");
+ ReportError (GetLastError());
+ }
+ else
+ pFile = fopen (szBWATCH, "w+");
+ }
+ }
+
+ WaitForSingleObject (hEvent, INFINITE);
+
+ while (TRUE)
+ {
+ CHAR DecodedName[20];
+
+ pBuffer = PullBufferFromQueue (&WorkQueueHead, &CSWorkQueue);
+
+ if (pBuffer == NULL)
+ {
+ return;
+ }
+
+ DecodeName (DecodedName, pBuffer->ncb_callname);
+
+ DecodeSmb (pFile, DecodedName, pBuffer);
+
+ PutBufferOnQueue (&FreeQueueHead, pBuffer, &CSFreeQueue);
+ }
+}
+
+// put the buffer returned from Netbios on the queue.
+void LoadWorkQueue (PSUPER_NCB psuperncb)
+{
+ PQUEUE_ENTRY pEntry;
+
+ // Allocate memory for pEntry.
+
+ pEntry = PullBufferFromQueue (&FreeQueueHead, &CSFreeQueue);
+
+ if (pEntry == NULL)
+ {
+ pEntry = (PQUEUE_ENTRY) LocalAlloc (LPTR, sizeof(*pEntry));
+ if (pEntry == NULL)
+ {
+ return;
+ }
+ }
+
+ GetLocalTime (&pEntry->systime);
+
+ strcpy (pEntry->ncb_callname, psuperncb->ncb.ncb_callname);
+ memcpy (pEntry->ncb_buffer, psuperncb->ncb.ncb_buffer, BUFFERLENGTH);
+ strcpy (pEntry->pTransportName, psuperncb->pTransportName);
+ strcpy (pEntry->pDomainName, psuperncb->pDomainName);
+ pEntry->nIndex = psuperncb->nIndex;
+
+ PutBufferOnQueue (&WorkQueueHead, pEntry, &CSWorkQueue);
+ SetEvent (hEvent);
+}
+
+// add a new entry to queue.
+void PutBufferOnQueue (PLIST_ENTRY pQueueHead,
+ PQUEUE_ENTRY pEntry,
+ CRITICAL_SECTION * pCSQueue)
+{
+ EnterCriticalSection (pCSQueue);
+
+ InsertTailList (pQueueHead, &(pEntry->List));
+
+ LeaveCriticalSection (pCSQueue);
+}
+
+// get an entry from queue.
+PQUEUE_ENTRY PullBufferFromQueue(PLIST_ENTRY pQueueHead,
+ CRITICAL_SECTION * pCSQueue)
+{
+ PQUEUE_ENTRY pEntry;
+
+ EnterCriticalSection (pCSQueue);
+
+ if (IsListEmpty (pQueueHead))
+ pEntry = NULL;
+ else
+ pEntry = (PQUEUE_ENTRY) RemoveHeadList (pQueueHead);
+
+ LeaveCriticalSection (pCSQueue);
+
+ return(pEntry);
+}
+
+
+
+// Change Netbios name into readable form: NAME(XX)
+VOID
+DecodeName(
+ LPSTR DecodedName,
+ LPSTR EncodedName
+ )
+{
+
+ CHAR TempString[6];
+ int i;
+
+ //
+ // Find first blank
+ //
+
+ for (i = 0; i < 15 ; i++) {
+ if (EncodedName[i] == ' ') {
+ break;
+ }
+ }
+
+ strncpy(DecodedName, EncodedName, i);
+ DecodedName[i] = '\0';
+ sprintf(TempString, "<%x>", EncodedName[15]);
+ strcat(DecodedName, TempString);
+
+}
+
+// Print out what's in the buffer.
+BOOL
+DecodeSmb(FILE * pFile,
+ LPSTR DecodedName,
+ PQUEUE_ENTRY pEntry)
+{
+ PBYTE Smb;
+ LPSTR MailslotName;
+ PUCHAR pPacketType;
+ PNT_SMB_HEADER pSmbHeader;
+ PREQ_TRANSACTION pSmbTransaction;
+ PBROWSE_ANNOUNCE_PACKET pBrowseAnnouncePacket;
+ PREQUEST_ELECTION pRequestElection;
+ PBECOME_BACKUP pBecomeBackup;
+ PREQUEST_ANNOUNCE_PACKET pRequestAnnouncement;
+
+ if (pEntry->nIndex != nIndex)
+ {
+ fprintf (pFile,
+ "\nTransport: %s, Domain: %s\n",
+ pEntry->pTransportName,
+ pEntry->pDomainName);
+
+ nIndex = pEntry->nIndex;
+ }
+
+ TimeStamp (pFile, &pEntry->systime);
+
+ Smb = pEntry->ncb_buffer;
+ //
+ // Decipher the SMB in the packet
+ //
+
+ pSmbHeader = (PNT_SMB_HEADER) Smb;
+ if (pSmbHeader->Protocol[0] != 0xff &&
+ pSmbHeader->Protocol[1] != 'S' &&
+ pSmbHeader->Protocol[2] != 'M' &&
+ pSmbHeader->Protocol[3] != 'B') {
+ fprintf(pFile, "Not a valid SMB header\n");
+ return(FALSE);
+ }
+ pSmbTransaction = (PREQ_TRANSACTION)
+ (Smb + sizeof(NT_SMB_HEADER));
+
+ MailslotName = (LPSTR) (pSmbTransaction->Buffer +
+ pSmbTransaction->SetupCount + 5);
+
+ if (!strcmp(MailslotName, "\\MAILSLOT\\BROWSE")) {
+ pPacketType = (PUCHAR) Smb +
+ pSmbTransaction->DataOffset;
+ switch (*pPacketType) {
+
+ case AnnouncementRequest:
+ fprintf(pFile, "Announcement request from %s. ", DecodedName);
+ pRequestAnnouncement =
+ (PREQUEST_ANNOUNCE_PACKET) pPacketType;
+ fprintf(pFile, "Reply %s\n",
+ pRequestAnnouncement->RequestAnnouncement.Reply);
+ break;
+
+ case Election:
+ fprintf(pFile, "Election request: %s ", DecodedName);
+ pRequestElection = (PREQUEST_ELECTION) pPacketType;
+ fprintf(pFile, "Version(%d) Criteria(0x%x) ",
+ pRequestElection->ElectionRequest.Version,
+ pRequestElection->ElectionRequest.Criteria);
+ fprintf(pFile, "TimeUp(%d)\n",
+ pRequestElection->ElectionRequest.TimeUp);
+ break;
+
+ case BecomeBackupServer:
+ fprintf(pFile, "BecomeBackupServer from %s ", DecodedName);
+ pBecomeBackup = (PBECOME_BACKUP) pPacketType;
+ fprintf(pFile, "to %s\n", pBecomeBackup->BecomeBackup.BrowserToPromote);
+ break;
+
+ case LocalMasterAnnouncement:
+ case WkGroupAnnouncement:
+ pBrowseAnnouncePacket =
+ (PBROWSE_ANNOUNCE_PACKET) pPacketType;
+ switch (*pPacketType) {
+ case LocalMasterAnnouncement:
+ fprintf(pFile, "LocalMasterAnnouncement from %s. ", DecodedName);
+ break;
+ case WkGroupAnnouncement:
+ fprintf(pFile, "Workgroup Announcement from %s. Domain: %s, Master %s. ", DecodedName, pBrowseAnnouncePacket->BrowseAnnouncement.ServerName, pBrowseAnnouncePacket->BrowseAnnouncement.Comment);
+ break;
+ }
+ fprintf(pFile, "UpdateCount = %d\n",
+ pBrowseAnnouncePacket->BrowseAnnouncement.UpdateCount);
+ break;
+
+ default:
+ fprintf(pFile, "\n**** Packet type %d from %s ****\n",
+ *pPacketType, DecodedName);
+ }
+ }
+ else if (strcmp(MailslotName, "\\MAILSLOT\\NET\\REPL_CLI") &&
+ strcmp(MailslotName, "\\MAILSLOT\\NET\\NTLOGON") &&
+ strcmp(MailslotName, "\\MAILSLOT\\NET\\NETLOGON") &&
+ strcmp(MailslotName, "\\MAILSLOT\\LANMAN")) {
+ fprintf(pFile, "Received an unknown datagram, name = %s\n",
+ MailslotName);
+ }
+
+ return(TRUE);
+}
+
+// Convert an unicode string to ansi string.
+LPSTR toansi(LPTSTR lpUnicode)
+{
+ static CHAR lpAnsi[BUFFERLENGTH];
+ BOOL fDummy;
+ INT i;
+
+ i = WideCharToMultiByte (CP_ACP,
+ 0,
+ lpUnicode,
+ lstrlen(lpUnicode),
+ lpAnsi,
+ BUFFERLENGTH,
+ NULL,
+ &fDummy);
+
+ lpAnsi[i] = 0;
+
+ return(lpAnsi);
+}
+
+// Get Domains from bwatch.ini.
+BOOL GetDomain (CHAR * pDomain)
+{
+ DWORD dwVal;
+
+ // Read all the domains that we want to check from bchk.ini
+ dwVal = GetPrivateProfileStringA (szAPPNAME,
+ szDOMAINS,
+ szDefaultDomain,
+ pDomain,
+ BUFFERLENGTH,
+ szFILENAME);
+
+ if (dwVal >= (BUFFERLENGTH-2)) // The memory assigned to lpDomainList is not big enough.
+ {
+ printf ("ERROR_NOT_ENOUGH_MEMORY");
+ return(FALSE);
+ }
+
+ return(TRUE);
+}
+
+INT GetLimit ()
+{
+ DWORD dwVal;
+ CHAR pTemp[10];
+ INT nFileSizeLimit;
+
+ // Read all the domains that we want to check from bchk.ini
+ dwVal = GetPrivateProfileStringA (szAPPNAME,
+ szFILESIZELIMIT,
+ szDefaultFileSizeLimit,
+ pTemp,
+ 10,
+ szFILENAME);
+
+ if (dwVal >= (BUFFERLENGTH-2)) // The memory assigned to lpDomainList is not big enough.
+ {
+ printf ("ERROR_NOT_ENOUGH_MEMORY");
+ return(atoi(szDefaultFileSizeLimit));
+ }
+
+ return(atoi(pTemp));
+}
+
+// Copied from ..\client\browstub.c.
+NET_API_STATUS GetBrowserTransportList (OUT PLMDR_TRANSPORT_LIST *TransportList)
+{
+
+ NET_API_STATUS Status;
+ HANDLE BrowserHandle;
+ LMDR_REQUEST_PACKET RequestPacket;
+
+ Status = OpenBrowser(&BrowserHandle);
+
+ if (Status != NERR_Success) {
+ return Status;
+ }
+
+ RequestPacket.Version = LMDR_REQUEST_PACKET_VERSION;
+
+ RequestPacket.Type = EnumerateXports;
+
+ RtlInitUnicodeString(&RequestPacket.TransportName, NULL);
+ RtlInitUnicodeString(&RequestPacket.EmulatedDomainName, NULL);
+
+ Status = DeviceControlGetInfo(
+ BrowserHandle,
+ IOCTL_LMDR_ENUMERATE_TRANSPORTS,
+ &RequestPacket,
+ sizeof(RequestPacket),
+ (PVOID *)TransportList,
+ 0xffffffff,
+ 4096,
+ NULL);
+
+ NtClose(BrowserHandle);
+
+ return Status;
+}
+
+// Print out the header line in the log file.
+void PrintHeader (FILE * pFile)
+{
+ fprintf(pFile, "**********************************************\n");
+ fprintf(pFile, "******************* BWATCH *******************\n");
+ fprintf(pFile, "**********************************************\n");
+}
+
+// Print out the time in the log file.
+void TimeStamp (FILE * pFile, SYSTEMTIME * psystime)
+{
+ fprintf (pFile,
+ "%d:%d:%d ",
+ psystime->wHour,
+ psystime->wMinute,
+ psystime->wSecond);
+}
diff --git a/private/net/svcdlls/browser/bwatch/bwatch.h b/private/net/svcdlls/browser/bwatch/bwatch.h
new file mode 100644
index 000000000..833524885
--- /dev/null
+++ b/private/net/svcdlls/browser/bwatch/bwatch.h
@@ -0,0 +1,120 @@
+/*++
+
+Copyright (c) 1993 Micorsoft Corporation
+
+Module Name:
+
+ bwatch.h
+
+Abstract:
+
+ Header file for browser watch program.
+
+Author:
+ Congpa You (CongpaY) 10-Feb-1993
+
+Revision History:
+
+--*/
+
+// Local data.
+#define SIXTEENSPACES " "
+#define BUFFERLENGTH 512
+#define STRINGLEN 30
+#define szBWATCH "bwatchlg"
+#define szLOGFILE L"bwatchlg"
+#define szBACKUP L"bwbackup"
+#define szSeps " ,\t\n"
+
+#define szAPPNAME "bwatch"
+#define szDOMAINS "DOMAINS"
+#define szDefaultDomain "ntlan"
+#define szFILENAME "bwatch.ini"
+#define szFILESIZELIMIT "FileSizeLimit"
+#define szDefaultFileSizeLimit "100000"
+
+// Global data.
+INT nIndex = -1;
+LIST_ENTRY WorkQueueHead;
+LIST_ENTRY FreeQueueHead;
+CRITICAL_SECTION CSWorkQueue;
+CRITICAL_SECTION CSFreeQueue;
+HANDLE hEvent;
+
+// Local data type.
+typedef struct _QUEUE_ENTRY
+{
+ LIST_ENTRY List;
+ UCHAR ncb_callname[NCBNAMSZ];
+ UCHAR ncb_buffer[BUFFERLENGTH];
+ CHAR pTransportName[STRINGLEN];
+ CHAR pDomainName[STRINGLEN];
+ INT nIndex;
+ SYSTEMTIME systime;
+}QUEUE_ENTRY, *PQUEUE_ENTRY;
+
+typedef struct _SUPER_NCB
+{
+ NCB ncb;
+ UCHAR Buffer[BUFFERLENGTH];
+ CCHAR lanaNumber;
+ CHAR pTransportName[STRINGLEN];
+ CHAR pDomainName[STRINGLEN];
+ CHAR nameNumber;
+ INT nIndex;
+}SUPER_NCB, *PSUPER_NCB;
+
+// Local Functions.
+#define ClearNcb( PNCB ) { \
+ RtlZeroMemory( PNCB , sizeof (NCB) ); \
+ RtlCopyMemory( (PNCB)->ncb_name, SIXTEENSPACES, sizeof(SIXTEENSPACES)-1 );\
+ RtlCopyMemory( (PNCB)->ncb_callname, SIXTEENSPACES, sizeof(SIXTEENSPACES)-1 );\
+ }
+
+void ReportError (DWORD dwError);
+
+BOOL GetDomain (CHAR * pDomain);
+
+INT GetLimit ();
+
+BOOL Init();
+
+void Registe (LPTSTR lpTransportName,
+ CHAR * pDomainName,
+ PSUPER_NCB psuperncb,
+ INT nIndex);
+
+void RegisteWkgroupName (LPTSTR lpTransportName,
+ CHAR * pDomainName,
+ PSUPER_NCB psuperncb,
+ INT nIndex);
+
+void SubmitRCDg(PSUPER_NCB psuperncb);
+void RCDgPost(NCB * pncb);
+
+void ProcessQueue(FILE * pFile, INT nFileSizeLimit);
+
+void LoadWorkQueue (PSUPER_NCB psuperncb);
+
+void PutBufferOnQueue(PLIST_ENTRY pQueueHead,
+ PQUEUE_ENTRY pEntry,
+ CRITICAL_SECTION * pCriticalSection);
+
+PQUEUE_ENTRY PullBufferFromQueue(PLIST_ENTRY pQueueHead,
+ CRITICAL_SECTION * pCriticalSection);
+
+BOOL Reset (UCHAR Lsn, CCHAR Lana_Num);
+
+VOID AddName(UCHAR Suffix, NCB * pncb);
+
+VOID DecodeName(LPSTR DecodedName, LPSTR EncodedName);
+BOOL DecodeSmb(FILE * pFile, LPSTR DecodedName, PQUEUE_ENTRY pEntry);
+
+LPSTR toansi(LPTSTR lpUnicode);
+
+NET_API_STATUS GetBrowserTransportList (OUT PLMDR_TRANSPORT_LIST *TransportList);
+
+void PrintHeader (FILE * pFile);
+
+void TimeStamp (FILE * pFile, SYSTEMTIME * psystime);
+
diff --git a/private/net/svcdlls/browser/bwatch/makefile b/private/net/svcdlls/browser/bwatch/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/net/svcdlls/browser/bwatch/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/net/svcdlls/browser/bwatch/sources b/private/net/svcdlls/browser/bwatch/sources
new file mode 100644
index 000000000..93385c832
--- /dev/null
+++ b/private/net/svcdlls/browser/bwatch/sources
@@ -0,0 +1,52 @@
+!IF 0
+
+Copyright (c) 1989-1992 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+
+Author:
+
+ Steve Wood (stevewo) 12-Apr-1990
+ Congpa You (congpay) 04-Feb-1993
+
+NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl
+
+!ENDIF
+
+MAJORCOMP=windows
+MINORCOMP=bwatch
+
+TARGETNAME=bwatch
+TARGETPATH=obj
+TARGETTYPE=LIBRARY
+
+INCLUDES=..\..\..;..\..\..\inc;..\..\..\..\inc;..\..\..\..\..\inc;..;
+
+
+!IFNDEF DISABLE_NET_UNICODE
+UNICODE=1
+NET_C_DEFINES=-DUNICODE
+!ENDIF
+
+SOURCES=
+
+UMTYPE=console
+UMAPPL=bwatch
+UMLIBS=\nt\public\sdk\lib\*\netapi32.lib \
+ ..\common\daytona\obj\*\utils.obj \
+ \nt\public\sdk\lib\*\ntdll.lib \
+ \nt\public\sdk\lib\*\rpcutil.lib
+
+
+
+
diff --git a/private/net/svcdlls/browser/client/brclient.h b/private/net/svcdlls/browser/client/brclient.h
new file mode 100644
index 000000000..45b7c84a7
--- /dev/null
+++ b/private/net/svcdlls/browser/client/brclient.h
@@ -0,0 +1,71 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ brclient.h
+
+Abstract:
+
+ Private header file for the client end of the Browser service
+ modules.
+
+Author:
+
+ Rita Wong (ritaw) 10-May-1991
+
+Revision History:
+
+--*/
+
+#include <nt.h> // DbgPrint prototype
+#include <ntrtl.h> // DbgPrint
+#include <nturtl.h> // Needed by winbase.h
+
+#include <windef.h> // DWORD
+#include <winbase.h> // LocalFree
+
+#include <rpc.h> // DataTypes and runtime APIs
+#include <rpcutil.h> // GENERIC_ENUM_STRUCT
+
+#include <lmcons.h> // NET_API_STATUS
+#include <lmerr.h> // NetError codes
+#include <lmremutl.h> // SUPPORTS_RPC
+
+#include <netlibnt.h> // NetpNtStatusToApiStatus
+#include <netdebug.h> // NetpDbgPrint
+
+#include <bowser.h> // generated by the MIDL complier
+#include <brnames.h> // Service and interface names
+
+//
+// Debug trace level bits for turning on/off trace statements in the client
+// end of the Browser service
+//
+
+//
+// Client stub trace output
+//
+#define BROWSER_DEBUG_CLIENTSTUBS 0x00000001
+
+//
+// Client RPC binding trace output
+//
+#define BROWSER_DEBUG_RPCBIND 0x00000002
+
+//
+// All debug flags on
+//
+#define BROWSER_DEBUG_ALL 0xFFFFFFFF
+
+
+#if DBG
+
+#define STATIC
+
+#else
+
+#define STATIC static
+
+#endif // DBG
diff --git a/private/net/svcdlls/browser/client/browbind.c b/private/net/svcdlls/browser/client/browbind.c
new file mode 100644
index 000000000..98621d121
--- /dev/null
+++ b/private/net/svcdlls/browser/client/browbind.c
@@ -0,0 +1,186 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ wksbind.c
+
+Abstract:
+
+ Routines which use RPC to bind and unbind the client to the Browser
+ service.
+
+Author:
+
+ Rita Wong (ritaw) 14-May-1991
+ Larry Osterman (larryo) 23-Mar-1992
+
+Environment:
+
+ User Mode -Win32
+
+Revision History:
+
+--*/
+
+#include "brclient.h"
+
+
+handle_t
+BROWSER_IMPERSONATE_HANDLE_bind(
+ BROWSER_IMPERSONATE_HANDLE ServerName
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is called from the Browser service client stubs when
+ it is necessary create an RPC binding to the server end with
+ impersonation level of impersonation.
+
+Arguments:
+
+ ServerName - A pointer to a string containing the name of the server
+ to bind with.
+
+Return Value:
+
+ The binding handle is returned to the stub routine. If the bind is
+ unsuccessful, a NULL will be returned.
+
+--*/
+{
+ handle_t BindHandle;
+ RPC_STATUS RpcStatus;
+
+ RpcStatus = NetpBindRpc (
+ ServerName,
+ BROWSER_INTERFACE_NAME,
+ TEXT("Security=Impersonation Dynamic False"),
+ &BindHandle
+ );
+
+ if (RpcStatus != RPC_S_OK) {
+ KdPrint((
+ "BROWSER_IMPERSONATE_HANDLE_bind failed: " FORMAT_NTSTATUS "\n",
+ RpcStatus
+ ));
+ }
+
+ return BindHandle;
+}
+
+
+
+handle_t
+BROWSER_IDENTIFY_HANDLE_bind(
+ BROWSER_IDENTIFY_HANDLE ServerName
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is called from the Browser service client stubs when
+ it is necessary create an RPC binding to the server end with
+ identification level of impersonation.
+
+Arguments:
+
+ ServerName - A pointer to a string containing the name of the server
+ to bind with.
+
+Return Value:
+
+ The binding handle is returned to the stub routine. If the bind is
+ unsuccessful, a NULL will be returned.
+
+--*/
+{
+ handle_t BindHandle;
+ RPC_STATUS RpcStatus;
+
+ RpcStatus = NetpBindRpc (
+ ServerName,
+ BROWSER_INTERFACE_NAME,
+ TEXT("Security=Identification Dynamic False"),
+ &BindHandle
+ );
+
+ if (RpcStatus != RPC_S_OK) {
+ KdPrint((
+ "BROWSER_IDENTIFY_HANDLE_bind failed: " FORMAT_NTSTATUS "\n",
+ RpcStatus
+ ));
+ }
+
+ return BindHandle;
+}
+
+
+
+void
+BROWSER_IMPERSONATE_HANDLE_unbind(
+ BROWSER_IMPERSONATE_HANDLE ServerName,
+ handle_t BindHandle
+ )
+
+/*++
+
+Routine Description:
+
+ This routine calls a common unbind routine that is shared by all services.
+ This routine is called from the Browser service client stubs when it is
+ necessary to unbind from the server end.
+
+Arguments:
+
+ ServerName - This is the name of the server from which to unbind.
+
+ BindingHandle - This is the binding handle that is to be closed.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ UNREFERENCED_PARAMETER(ServerName);
+
+ NetpUnbindRpc(BindHandle);
+}
+
+
+
+void
+BROWSER_IDENTIFY_HANDLE_unbind(
+ BROWSER_IDENTIFY_HANDLE ServerName,
+ handle_t BindHandle
+ )
+
+/*++
+
+Routine Description:
+
+ This routine calls a common unbind routine that is shared by all services.
+ This routine is called from the server service client stubs when it is
+ necessary to unbind from a server.
+
+Arguments:
+
+ ServerName - This is the name of the server from which to unbind.
+
+ BindingHandle - This is the binding handle that is to be closed.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ UNREFERENCED_PARAMETER(ServerName);
+
+ NetpUnbindRpc(BindHandle);
+}
diff --git a/private/net/svcdlls/browser/client/browdeb.c b/private/net/svcdlls/browser/client/browdeb.c
new file mode 100644
index 000000000..b822cfbb1
--- /dev/null
+++ b/private/net/svcdlls/browser/client/browdeb.c
@@ -0,0 +1,4474 @@
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windows.h>
+#include <lm.h>
+#include <ntddbrow.h>
+#include <brcommon.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <hostannc.h>
+#include <lmbrowsr.h>
+#include <nb30.h>
+#include <rap.h>
+#include <rxserver.h>
+#include <srvann.h>
+#include <time.h>
+#include <tstring.h>
+#include <netlib.h>
+#include <icanon.h>
+#include "..\server\brwins.h"
+
+static char ProgramName[MAX_PATH+1] ;
+
+struct {
+ LPSTR SwitchName;
+ LPSTR ShortName;
+ ULONG SwitchValue;
+ LPSTR SwitchInformation;
+ int MinArgc;
+ int MaxArgc;
+ LPSTR Syntax;
+} CommandSwitchList[] = {
+ { "ELECT", "EL", BROWSER_DEBUG_ELECT,
+ "Force election on remote domain",
+#ifdef _CAIRO_
+ 4, 5, "<Transport> <Domain> [<EmulatedDomain>]" },
+#else
+ 4, 4, "<Transport> <Domain>" },
+#endif
+ { "GETBLIST", "GB", BROWSER_DEBUG_GET_BACKUP_LIST,
+ "Get backup list for domain",
+ 3, 5, "<Transport> [[<Domain>] REFRESH]" },
+ { "GETMASTER", "GM", BROWSER_DEBUG_GET_MASTER,
+ "Get remote Master Browser name (using NetBIOS)",
+ 4, 4, "<Transport> <Domain>" },
+ { "GETPDC", "GP", BROWSER_DEBUG_GETPDC,
+ "Get PDC name (using NetBIOS)",
+ 4, 4, "<Transport> <Domain>" },
+ { "LISTWFW", "WFW", BROWSER_DEBUG_LIST_WFW,
+ "List WFW servers that are actually running browser",
+ 3, 3, "<Domain>" },
+ { "STATS", "STS", BROWSER_DEBUG_STATISTICS,
+ "Dump browser statistics",
+ 2, 4, "[\\\\<Computer> [RESET]]" },
+ { "STATUS", "STA", BROWSER_DEBUG_STATUS,
+ "Display status about a domain",
+ 2, 4, "[-V] [<Domain>]" },
+ { "TICKLE", "TIC", BROWSER_DEBUG_TICKLE,
+ "Force remote master to stop",
+#ifdef _CAIRO_
+ 3, 5, "<Transport> [<Domain> | \\\\<Server> [<EmulatedDomain>]]" },
+#else
+ 3, 4, "<Transport> [<Domain> | \\\\<Server>]" },
+#endif
+ { "VIEW", "VW", BROWSER_DEBUG_VIEW,
+ "Remote NetServerEnum to a server or domain on transport",
+#ifndef _PSS_RELEASE
+ 3, 7, "Transport [<Domain>|\\\\<Server> [<Flags>|/DOMAIN [<DomainToQuery> [Forever]]]]" },
+#else
+ 3, 6, "Transport [<Domain>|\\\\<Server> [<Flags>|/DOMAIN [<DomainToQuery>]]]" },
+#endif
+//
+// NOTE: Any Option below and including "BREAK" will not be displayed
+// with _PSS_RELEASE Defined
+//
+ { "BREAK", "BRK", BROWSER_DEBUG_BREAK_POINT,
+ "Break into debugger in browser service",
+ 2, 2, "" },
+ { "RPCLIST", "RPC", BROWSER_DEBUG_RPCLIST,
+ "Retrieve the remote server list using RPC",
+ 3, 6, "<Transport> [<Domain> || \\\\<Server>] [ServerFlags] [GoForever]" },
+ { "MASTERNAME", "MN", BROWSER_DEBUG_ADD_MASTERNAME,
+ "Add the master name on a domain",
+ 4, 5, "<Transport> <Domain> [PAUSE]" },
+ { "WKSTADOM", "WD", BROWSER_DEBUG_ADD_DOMAINNAME,
+ "Add the domain name",
+ 4, 5, "<Transport> <Domain> [PAUSE]" },
+ { "DUMPNET", "DN", BROWSER_DEBUG_DUMP_NETWORKS,
+ "Dump the list of networks",
+ 2, 2, "" },
+ { "ENABLE", "EN", BROWSER_DEBUG_ENABLE_BROWSER,
+ "Enable the browser service",
+ 2, 2, "" },
+ { "DEBUG", "DBG", BROWSER_DEBUG_SET_DEBUG,
+ "Change browser service debug options",
+ 3, 4, "[[+-]DebugFlag|<Value>] [\\\\<Computer>]" },
+ { "FINDMASTER", "FM", BROWSER_DEBUG_FIND_MASTER,
+ "Find master of current domain",
+#ifdef _CAIRO
+ 3, 4, "<Transport> [<EmulatedDomain>]" },
+#else
+ 3, 3, "<Transport>" },
+#endif
+ { "MASTERANNOUNCE", "MA", BROWSER_DEBUG_ANNOUNCE_MASTER,
+ "Send a master announcement with this machine as master",
+#ifdef _CAIRO_
+ 4, 5, "<Transport> <Master> [<EmulatedDomain>]" },
+#else
+ 4, 4, "<Transport> <Master>" },
+#endif
+ { "ILLEGAL", "ILL", BROWSER_DEBUG_ILLEGAL_DGRAM,
+ "Send an illegal datagram to workstation",
+#ifdef _CAIRO_
+ 4, 5, "<Transport> <Computer> [<EmulatedDomain>]" },
+#else
+ 4, 4, "<Transport> <Computer>" },
+#endif
+ { "FORCEANNOUNCE", "FA", BROWSER_DEBUG_FORCE_ANNOUNCE,
+ "Force all browsers in domain to announce to master browser",
+#ifdef _CAIRO_
+ 4, 5, "<Transport> <Domain> [<EmulatedDomain>]" },
+#else
+ 4, 4, "<Transport> <Domain>" },
+#endif
+ { "LOCALLIST", "LL", BROWSER_DEBUG_LOCAL_BRLIST,
+ "Retrieve the local browser list",
+#ifdef _CAIRO_
+ 3, 5, "<Transport> [<ServerFlags>] [<EmulatedDomain>]" },
+#else
+ 3, 4, "<Transport> [<ServerFlags>]" },
+#endif
+ { "ANNOUNCE", "ANN", BROWSER_DEBUG_ANNOUNCE,
+ "Send server announcement w/this machine member of domain",
+#ifdef _CAIRO_
+ 4, 6, "<Transport> <Domain> [<EmulatedDomainName>] [ASMASTER]" },
+#else
+ 4, 5, "<Transport> <Domain> [ASMASTER]" },
+#endif
+ { "RPCCMP", "RC", BROWSER_DEBUG_RPCCMP,
+ "Compare the RPC generated list with the Rx list",
+ 3, 6, "<Transport> [<Domain> || \\\\<Server>] [<ServerFlags>] [GoForever]" },
+ { "TRUNCLOG", "TLG", BROWSER_DEBUG_TRUNCATE_LOG,
+ "Truncate the browser log",
+ 2, 2, "" },
+ { "BOWDEBUG", "SD", BROWSER_DEBUG_BOWSERDEBUG,
+ "Set debug info in the bowser",
+ 3, 4, "TRUNCATE" },
+ { "POPSERVER", "PS", BROWSER_DEBUG_POPULATE_SERVER,
+ "Populate a workgroup with random server names",
+#ifdef _CAIRO_
+ 5, 7, "<Transport> <Domain> <NumberOfMachines> [<EmulatedDomain>] [AnnouncementFrequency]" },
+#else
+ 5, 6, "<Transport> <Domain> <NumberOfMachines> [AnnouncementFrequency (ms)]" },
+#endif
+ { "POPDOMAIN", "PD", BROWSER_DEBUG_POPULATE_DOMAIN,
+ "Populate a workgroup with random domain names",
+#ifdef _CAIRO_
+ 5, 7, "<Transport> <Domain> <NumberOfMachines> [<EmulatedDomain>] [AnnouncementFrequency]" },
+#else
+ 5, 6, "<Transport> <Domain> <NumberOfMachines> [AnnouncementFrequency (ms)]" },
+#endif
+ { "OTHERDOMAIN", "OTH", BROWSER_DEBUG_GET_OTHLIST,
+ "Retrieve list of otherdomains that computer listens to",
+ 3, 3, "<Computer>" },
+ { "GETWINS", "GW", BROWSER_DEBUG_GET_WINSSERVER,
+ "Retrieve the primary and backup WINS server",
+ 3, 3, "<Transport>" },
+ { "GETDOMAIN", "GWD", BROWSER_DEBUG_GET_DOMAINLIST,
+ "Retrieve the domain list from a WINS server",
+ 3, 3, "<Ip Address>" },
+ { "GETNETBIOS", "GN", BROWSER_DEBUG_GET_NETBIOSNAMES,
+ "Get Netbios names for a transport",
+#ifdef _CAIRO_
+ 3, 4, "<Transport> [<EmulatedDomain>]" },
+#else
+ 3, 3, "<Transport>" },
+#endif
+ { "ADDALTCOMP", "AAC", BROWSER_DEBUG_ADD_ALTERNATE,
+ "Add an alternate computer name",
+ 3, 4, "<Transport> <AlternateComptureName>" },
+#ifdef _CAIRO_
+ { "EMULATEDOMAIN", "ED", BROWSER_DEBUG_SET_EMULATEDDOMAIN,
+ "Create/Set/Delete emulated domain",
+ 4, 5, "<EmulatedDomain> PDC|BDC|DELETE [<EmulatedComputerName>]" },
+ { "EMULATEDOMAINENUM", "EDE", BROWSER_DEBUG_SET_EMULATEDDOMAINENUM,
+ "Enumerate emulated domains",
+ 2, 2, "" },
+#endif
+ { NULL, NULL, 0, NULL }
+
+};
+
+
+struct {
+ LPSTR SwitchName;
+ ULONG SwitchValue;
+} DebugSwitchList[] = {
+#ifdef _CAIRO_
+ { "INIT", BR_INIT },
+ { "CRITICAL", BR_CRITICAL },
+ { "ENUM", BR_SERVER_ENUM },
+ { "UTIL", BR_UTIL },
+ { "CONFIG", BR_CONFIG },
+ { "MAIN", BR_MAIN },
+ { "BACKUP", BR_BACKUP },
+ { "MASTER", BR_MASTER },
+ { "DOMAIN", BR_DOMAIN },
+ { "NETWORK", BR_NETWORK },
+ { "TIMER", BR_TIMER },
+ { "QUEUE", BR_QUEUE },
+ { "LOCKS", BR_LOCKS },
+ { "COMMON", BR_COMMON },
+ { "ALL", BR_ALL },
+#else // _CAIRO_
+
+ { "SERVER_ENUM", BROWSER_DEBUG_SERVER_ENUM },
+ { "UTIL", BROWSER_DEBUG_UTIL },
+ { "CONFIG", BROWSER_DEBUG_CONFIG },
+ { "MAIN", BROWSER_DEBUG_MAIN },
+ { "LOGON", BROWSER_DEBUG_LOGON },
+ { "BACKUP", BROWSER_DEBUG_BACKUP },
+ { "MASTER", BROWSER_DEBUG_MASTER },
+ { "DOMAIN", BROWSER_DEBUG_DOMAIN },
+ { "TIMER", BROWSER_DEBUG_TIMER },
+ { "QUEUE", BROWSER_DEBUG_QUEUE },
+ { "LOCKS", BROWSER_DEBUG_LOCKS },
+ { "ALL", BROWSER_DEBUG_ALL },
+#endif // _CAIRO_
+ { NULL, 0 }
+
+};
+
+typedef struct _BIT_NAME {
+ DWORD dwValue ;
+ LPSTR lpString ;
+ LPSTR Comment;
+} BIT_NAME ;
+
+BIT_NAME BitToStringTable[] = {
+ { SV_TYPE_WORKSTATION, "W", "Workstation" },
+ { SV_TYPE_SERVER, "S", "Server" },
+ { SV_TYPE_SQLSERVER, "SQL", "SQLServer" } ,
+ { SV_TYPE_DOMAIN_CTRL, "PDC", "PrimaryDomainController" } ,
+ { SV_TYPE_DOMAIN_BAKCTRL, "BDC", "BackupDomainController" } ,
+ { SV_TYPE_TIME_SOURCE, "TS", "TimeSource" } ,
+ { SV_TYPE_AFP, "AFP", "AFPServer" } ,
+ { SV_TYPE_NOVELL, "NV", "Novell" } ,
+ { SV_TYPE_DOMAIN_MEMBER, "MBC", "MemberServer" } ,
+ { SV_TYPE_PRINTQ_SERVER, "PQ", "PrintServer" } ,
+ { SV_TYPE_DIALIN_SERVER, "DL", "DialinServer" } ,
+ { SV_TYPE_XENIX_SERVER, "XN", "Xenix" } ,
+ { SV_TYPE_NT, "NT", "Windows NT" } ,
+ { SV_TYPE_WFW, "WFW", "WindowsForWorkgroups" } ,
+ { SV_TYPE_SERVER_MFPN, "MFPN", "MS Netware" } ,
+ { SV_TYPE_SERVER_NT, "SS", "StandardServer" } ,
+ { SV_TYPE_POTENTIAL_BROWSER, "PBR", "PotentialBrowser" } ,
+ { SV_TYPE_BACKUP_BROWSER, "BBR", "BackupBrowser" } ,
+ { SV_TYPE_MASTER_BROWSER, "MBR", "MasterBrowser" } ,
+ { SV_TYPE_DOMAIN_MASTER, "DMB", "DomainMasterBrowser" } ,
+ { SV_TYPE_SERVER_OSF, "OSF", "OSFServer" } ,
+ { SV_TYPE_SERVER_VMS, "VMS", "VMSServer" } ,
+ { SV_TYPE_WINDOWS, "W95", "Windows95" } ,
+ { SV_TYPE_DFS, "DFS", "DistributedFileSystem" } ,
+ { 0, "", NULL }
+} ;
+
+#include <..\..\..\..\ntos\bowser\debug.h>
+
+#ifdef notdef
+struct {
+ LPSTR SwitchName;
+ ULONG SwitchValue;
+} BowserSwitchList[] = {
+ { "DOMAIN", DPRT_DOMAIN },
+ { "ANNOUNCE", DPRT_ANNOUNCE },
+ { "TDI", DPRT_TDI },
+ { "FSPDISP", DPRT_FSPDISP },
+ { "BROWSER", DPRT_BROWSER },
+ { "ELECT", DPRT_ELECT },
+ { "CLIENT", DPRT_CLIENT },
+ { "MASTER", DPRT_MASTER },
+ { "SRVENUM", DPRT_SRVENUM },
+ { "NETLOGON", DPRT_NETLOGON },
+ { "FSCTL", DPRT_FSCTL },
+ { "INIT", DPRT_INIT },
+ { "REF", DPRT_REF },
+ { "SCAVTHRD", DPRT_SCAVTHRD },
+ { "TIMER", DPRT_TIMER },
+ { "PACK", DPRT_PACK },
+ { "ALL", 0xffffffff },
+ { NULL, 0 }
+};
+#endif // notdef
+
+//
+// forward declarations
+//
+
+VOID
+BrowserStatus(
+ IN BOOL Verbose,
+ OUT PCHAR Domain OPTIONAL
+ );
+
+ NET_API_STATUS
+GetMasterServerNames(
+ IN PUNICODE_STRING NetworkName,
+#ifdef _CAIRO_
+ IN PUNICODE_STRING EmulatedDomainName,
+#endif
+ OUT LPWSTR *MasterName
+ );
+
+PCHAR
+UnicodeToPrintfString(
+ PWCHAR WideChar
+ );
+PCHAR
+UnicodeToPrintfString2(
+ PWCHAR WideChar
+ );
+
+NET_API_STATUS
+GetLocalBrowseList(
+ IN PUNICODE_STRING Network,
+#ifdef _CAIRO_
+ IN PUNICODE_STRING EmulatedDomainName,
+#endif
+ IN ULONG Level,
+ IN ULONG ServerType,
+ OUT PVOID *ServerList,
+ OUT PULONG EntriesRead,
+ OUT PULONG TotalEntries
+ );
+
+VOID
+View(
+ IN PCHAR Transport,
+ IN PCHAR ServerOrDomain,
+ IN PCHAR Flags,
+ IN PCHAR Domain,
+ IN BOOL GoForever
+ );
+
+VOID
+ListWFW(
+ IN PCHAR Domain
+ );
+
+VOID
+RpcList(
+ IN PCHAR Transport,
+ IN PCHAR ServerOrDomain,
+ IN PCHAR Flags,
+ IN BOOL GoForever
+ );
+
+VOID
+RpcCmp(
+ IN PCHAR Transport,
+ IN PCHAR ServerOrDomain,
+ IN PCHAR Flags,
+ IN BOOL GoForever
+ );
+
+VOID
+GetLocalList(
+ IN PCHAR Transport,
+#ifdef _CAIRO_
+ IN PCHAR FlagsString,
+ IN PCHAR EmulatedDomain
+#else
+ IN PCHAR FlagsString
+#endif
+ );
+
+VOID
+PrintNetbiosNames(
+#ifdef _CAIRO_
+ IN PCHAR Transport,
+ IN PCHAR EmulatedDomain OPTIONAL
+#else
+ IN PCHAR Transport
+#endif
+ );
+
+NET_API_STATUS
+GetNetbiosNames(
+ IN PUNICODE_STRING Network,
+#ifdef _CAIRO_
+ IN PUNICODE_STRING EmulatedDomainName,
+#endif
+ OUT PVOID *NameList,
+ OUT PULONG EntriesRead,
+ OUT PULONG TotalEntries
+ );
+
+NET_API_STATUS
+AddAlternateComputerName(
+ IN PCHAR Transport,
+ IN PCHAR ComputerName
+ );
+
+VOID
+GetOtherdomains(
+ IN PCHAR ServerName
+ );
+
+VOID
+IllegalDatagram(
+ IN PCHAR Transport,
+#ifdef _CAIRO_
+ IN PCHAR ServerName,
+ IN PCHAR EmulatedDomain
+#else
+ IN PCHAR ServerName
+#endif
+ );
+VOID
+AnnounceMaster(
+ IN PCHAR Transport,
+#ifdef _CAIRO_
+ IN PCHAR ServerName,
+ IN PCHAR EmulatedDomain
+#else
+ IN PCHAR ServerName
+#endif
+ );
+
+VOID
+Announce(
+ IN PCHAR Transport,
+ IN PCHAR Domain,
+#ifdef _CAIRO_
+ IN PCHAR EmulatedDomain,
+#endif
+ IN BOOL AsMaster
+ );
+
+VOID
+Populate(
+ IN BOOL PopulateDomains,
+ IN PCHAR Transport,
+ IN PCHAR Domain,
+#ifdef _CAIRO_
+ IN PCHAR EmulatedDomain,
+#endif
+ IN PCHAR NumberOfMachinesString,
+ IN PCHAR PeriodicityString OPTIONAL
+ );
+
+VOID
+AddMasterName(
+ IN PCHAR Transport,
+ IN PCHAR Domain,
+ IN BOOL Pause
+ );
+
+VOID
+AddDomainName(
+ IN PCHAR Transport,
+ IN PCHAR Domain,
+ IN BOOL Pause
+ );
+
+VOID
+GetMaster(
+ IN PCHAR Transport,
+ IN PCHAR Domain
+ );
+
+VOID
+GetPdc(
+ IN PCHAR Transport,
+ IN PCHAR Domain
+ );
+
+VOID
+FindMaster(
+#ifdef _CAIRO_
+ IN PCHAR Transport,
+ IN PCHAR EmulatedDomain
+#else
+ IN PCHAR Transport
+#endif
+ );
+
+VOID
+Elect(
+ IN PCHAR Transport,
+#ifdef _CAIRO_
+ IN PCHAR Domain,
+ IN PCHAR EmulatedDomain
+#else
+ IN PCHAR Domain
+#endif
+ );
+
+VOID
+Tickle(
+ IN PCHAR Transport,
+#ifdef _CAIRO_
+ IN PCHAR Domain,
+ IN PCHAR EmulatedDomain
+#else
+ IN PCHAR Domain
+#endif
+ );
+
+VOID
+ForceAnnounce(
+ IN PCHAR Transport,
+#ifdef _CAIRO_
+ IN PCHAR Domain,
+ IN PCHAR EmulatedDomain
+#else
+ IN PCHAR Domain
+#endif
+ );
+
+VOID
+GetBlist(
+ IN PCHAR TransportName,
+ IN PCHAR DomainName,
+ IN BOOLEAN ForceRescan
+ );
+
+NET_API_STATUS
+EnableService(
+ IN LPTSTR ServiceName
+ );
+
+VOID
+DumpStatistics(
+ IN ULONG NArgs,
+ IN PCHAR Arg1
+ );
+
+VOID
+TruncateBowserLog();
+
+VOID
+CloseBowserLog();
+
+VOID
+OpenBowserLog(PCHAR FileName);
+
+VOID
+SetBowserDebug(PCHAR DebugBits);
+
+VOID
+usage( char *details ) ;
+
+VOID
+help( char *details ) ;
+
+BOOL
+look_for_help(int argc, char **argv) ;
+
+VOID
+qualify_transport(CHAR *old_name, PUNICODE_STRING new_name) ;
+
+VOID
+DisplayServerInfo101(
+ PSERVER_INFO_101 Server,
+ BOOL DomainEnumeration
+ );
+
+DWORD
+display_sv_bits(DWORD dwBits) ;
+
+CHAR *
+get_error_text(DWORD dwErr) ;
+
+VOID
+GetWinsServer(
+ IN PCHAR Transport
+ );
+
+VOID
+GetDomainList(
+ IN PCHAR IpAddress
+ );
+
+#ifdef _CAIRO_
+VOID
+SetEmulatedDomain(
+ IN PCHAR EmulatedDomain,
+ IN PCHAR Role,
+ IN PCHAR EmulatedComputer
+ );
+
+VOID
+EnumEmulatedDomains(
+ );
+#endif
+
+//
+// functions
+//
+
+VOID
+usage(
+ char *details
+ )
+{
+ ULONG i = 0;
+ DWORD LineLength;
+#ifndef _PSS_RELEASE
+ printf("Usage: %s Command [Options]\n", ProgramName);
+#else
+ printf("Usage: %s Command [Options | /HELP]\n", ProgramName);
+#endif
+ printf("Where <Command> is one of:\n\n");
+
+
+#ifndef _PSS_RELEASE
+ while (CommandSwitchList[i].SwitchName != NULL)
+#else
+ while (CommandSwitchList[i].SwitchValue != BROWSER_DEBUG_BREAK_POINT )
+#endif
+ {
+ printf(" %-14.14s(%3.3s) - %s\n",
+ CommandSwitchList[i].SwitchName,
+ CommandSwitchList[i].ShortName,
+ CommandSwitchList[i].SwitchInformation);
+ i += 1;
+ }
+
+
+ if (details)
+ printf(details);
+
+ //
+ // Print the descriptions of server type bits
+ //
+ printf("\nIn server (or domain) list displays, the following flags are used:\n");
+
+ LineLength = 0;
+ i=0;
+ while ( BitToStringTable[i].dwValue != 0 ) {
+ DWORD ItemLength;
+
+ ItemLength = strlen(BitToStringTable[i].lpString) +
+ 1 +
+ strlen(BitToStringTable[i].Comment);
+
+ if ( LineLength + ItemLength >= 77 ) {
+ LineLength = 0;
+ printf(",\n");
+ }
+ if ( LineLength == 0) {
+ printf(" ");
+ LineLength = 5;
+ } else {
+ printf(", ");
+ LineLength += 2;
+ }
+
+ printf( "%s=%s", BitToStringTable[i].lpString, BitToStringTable[i].Comment);
+ LineLength += ItemLength;
+ i++;
+
+ }
+ printf("\n");
+
+
+}
+
+VOID
+CommandUsage(
+ ULONG ControlCode
+ )
+/*++
+
+Routine Description:
+
+ Print the usage description for a single command
+
+Arguments:
+
+ ControlCode - Control code of the command who's usage is to be printed.
+
+Return Value:
+
+ None
+
+--*/
+{
+ ULONG Index;
+ ULONG i;
+
+ //
+ // Look up the command in the list of commands.
+ //
+
+ Index = 0;
+ while (CommandSwitchList[Index].SwitchName != NULL) {
+ if ( ControlCode == CommandSwitchList[Index].SwitchValue ) {
+ break;
+ }
+ Index += 1;
+ }
+
+ if (CommandSwitchList[Index].SwitchName == NULL) {
+ usage("Unknown switch specified");
+ return;
+ }
+
+
+ //
+ // Print command usage.
+ //
+
+ printf( "Usage: %s %s %s\n",
+ ProgramName,
+ CommandSwitchList[Index].SwitchName,
+ CommandSwitchList[Index].Syntax );
+
+ //
+ // Print additional command specific information.
+ //
+ switch (ControlCode) {
+ case BROWSER_DEBUG_VIEW:
+ printf(" %s VIEW <transport>\n"
+ " %s VIEW <transport> <domain>|\\\\<Server> [/DOMAIN]\n"
+ " %s VIEW <transport> <server> /DOMAIN <domain>\n",
+ ProgramName,
+ ProgramName,
+ ProgramName );
+
+ break;
+
+ case BROWSER_DEBUG_SET_DEBUG:
+
+ printf("where DebugFlag is one of the following:\n");
+
+ i = 0;
+ while (DebugSwitchList[i].SwitchName != NULL) {
+ printf("\t%s\n", DebugSwitchList[i].SwitchName);
+ i += 1;
+ }
+ break;
+
+
+ case BROWSER_DEBUG_BOWSERDEBUG:
+ printf(" %s BOWDEBUG CLOSE\n"
+ " %s BOWDEBUG OPEN <FileName>\n"
+ " %s BOWDEBUG DEBUG <Flags>\n",
+ ProgramName,
+ ProgramName,
+ ProgramName );
+
+#ifdef notdef
+ printf("where Flags is one of the following:\n");
+
+ i = 0;
+ while (BowserSwitchList[i].SwitchName != NULL) {
+ printf("\t%s\n", BowserSwitchList[i].SwitchName);
+ i += 1;
+ }
+#endif // notdef
+
+ break;
+ }
+
+ printf( "%s.\n",
+ CommandSwitchList[Index].SwitchInformation );
+
+ help("");
+ exit(4);
+}
+
+VOID
+help(
+ char *details
+ )
+{
+ printf("%s\nType \"%s\" to list all switches.\n", details, ProgramName);
+}
+
+VOID
+qualify_transport(CHAR *old_name, PUNICODE_STRING new_name)
+{
+ int len = strlen(old_name) ;
+ char *devicestring = "\\device\\" ;
+ int devicelen = strlen(devicestring) ;
+ CHAR QualifiedTransport[MAX_PATH] ;
+ ANSI_STRING AString;
+
+ //
+ // Qualify the name.
+ //
+ if (_strnicmp(old_name, devicestring, devicelen) != 0)
+ {
+ strcpy(QualifiedTransport, devicestring) ;
+ strcat(QualifiedTransport, (*old_name == '\\') ? old_name+1 : old_name) ;
+ }
+ else
+ {
+ strcpy(QualifiedTransport, old_name) ;
+ }
+
+
+ //
+ // Convert it to a UNICODE_STRING
+ //
+ RtlInitString(&AString, QualifiedTransport);
+
+ RtlAnsiStringToUnicodeString(new_name, &AString, TRUE);
+}
+
+VOID
+_cdecl
+main (argc, argv)
+ int argc;
+ char *argv[];
+{
+ NET_API_STATUS Status;
+ ULONG ControlCode;
+ ULONG Options = 0;
+ LPTSTR Server = NULL;
+ TCHAR ServerBuffer[CNLEN+1];
+ ULONG i = 0;
+
+ strcpy(ProgramName,argv[0]) ; // cannot overflow, since buffer > MAXPATH
+ _strupr(ProgramName) ;
+
+ if (argc < 2) {
+ usage(NULL);
+ exit(1);
+ }
+
+
+ //
+ // Look up the command in the list of commands.
+ //
+
+ while (CommandSwitchList[i].SwitchName != NULL) {
+ if (!_stricmp(argv[1], CommandSwitchList[i].SwitchName) ||
+ !_stricmp(argv[1], CommandSwitchList[i].ShortName)) {
+ ControlCode = CommandSwitchList[i].SwitchValue;
+ break;
+ }
+
+ i += 1;
+ }
+
+ if (CommandSwitchList[i].SwitchName == NULL) {
+ usage("Unknown switch specified");
+ exit(5);
+ }
+
+ //
+ // If an incorrect number of arguments were supplied,
+ // complain.
+ //
+
+ if ( look_for_help(argc, argv) ||
+ argc < CommandSwitchList[i].MinArgc ||
+ argc > CommandSwitchList[i].MaxArgc ) {
+
+ CommandUsage( ControlCode );
+ exit(4);
+
+ }
+
+ //
+ // Do command specific processing.
+ //
+ switch (ControlCode) {
+ case BROWSER_DEBUG_SET_DEBUG:
+ {
+ ULONG i = 0;
+
+
+ if ((Options = atol(argv[2])) == 0) {
+ PCHAR SwitchText;
+
+ if (argv[2][0] == '+') {
+ SwitchText = &argv[2][1];
+ ControlCode = BROWSER_DEBUG_SET_DEBUG;
+ } else if (argv[2][0] == '-') {
+ SwitchText = &argv[2][1];
+ ControlCode = BROWSER_DEBUG_CLEAR_DEBUG;
+ } else {
+ CommandUsage( ControlCode );
+ exit(4);
+ }
+
+ while (DebugSwitchList[i].SwitchName != NULL) {
+ if (!_stricmp(SwitchText, DebugSwitchList[i].SwitchName)) {
+ Options = DebugSwitchList[i].SwitchValue;
+ break;
+ }
+
+ i += 1;
+ }
+
+ if (DebugSwitchList[i].SwitchName == NULL) {
+ CommandUsage( ControlCode );
+ exit(4);
+ }
+
+ if (argc == 4) {
+ MultiByteToWideChar(CP_ACP, 0, argv[3], strlen(argv[3])+1, ServerBuffer, sizeof(ServerBuffer));
+ Server = ServerBuffer;
+ }
+
+ }
+
+ }
+ break;
+ case BROWSER_DEBUG_BREAK_POINT:
+ case BROWSER_DEBUG_DUMP_NETWORKS:
+ case BROWSER_DEBUG_TRUNCATE_LOG:
+ break;
+
+ case BROWSER_DEBUG_ENABLE_BROWSER:
+ {
+ NET_API_STATUS Status;
+
+ Status = EnableService(TEXT("BROWSER"));
+
+ if (Status != NERR_Success) {
+ printf("Unable to enable browser service - %ld\n", Status);
+ }
+
+ exit(Status);
+ }
+ break;
+ case BROWSER_DEBUG_BOWSERDEBUG:
+ if (argc == 3) {
+ if (_stricmp(argv[2], "TRUNCATE") == 0) {
+ TruncateBowserLog();
+ } else if (_stricmp(argv[2], "CLOSE") == 0) {
+ CloseBowserLog();
+ } else {
+ CommandUsage( BROWSER_DEBUG_BOWSERDEBUG );
+ }
+ } else if (argc == 4) {
+ if (_stricmp(argv[2], "OPEN") == 0) {
+ OpenBowserLog(argv[3]);
+ } else if (_stricmp(argv[2], "DEBUG") == 0) {
+ SetBowserDebug(argv[3]);
+ } else {
+ CommandUsage( BROWSER_DEBUG_BOWSERDEBUG );
+ }
+
+ }
+ exit(0);
+
+ case BROWSER_DEBUG_ELECT:
+#ifdef _CAIRO_
+ Elect(argv[2], argv[3], (argc == 5) ? argv[4] : NULL );
+#else
+ Elect(argv[2], argv[3]);
+#endif
+ exit(0);
+ break;
+ case BROWSER_DEBUG_GET_MASTER:
+ GetMaster(argv[2], argv[3]);
+ exit(0);
+ break;
+ case BROWSER_DEBUG_TICKLE:
+#ifdef _CAIRO_
+ Tickle(argv[2], argv[3], (argc == 5) ? argv[4] : NULL );
+#else
+ Tickle(argv[2], argv[3]);
+#endif
+ exit(0);
+ break;
+ case BROWSER_DEBUG_FORCE_ANNOUNCE:
+#ifdef _CAIRO_
+ ForceAnnounce(argv[2], argv[3], (argc == 5) ? argv[4] : NULL );
+#else
+ ForceAnnounce(argv[2], argv[3]);
+#endif
+ exit(0);
+ break;
+ case BROWSER_DEBUG_GETPDC:
+ GetPdc(argv[2], argv[3]);
+ exit(0);
+ break;
+
+ case BROWSER_DEBUG_ADD_MASTERNAME:
+ AddMasterName(argv[2], argv[3], (argc==5 ? TRUE : FALSE));
+
+ exit(0);
+ break;
+
+ case BROWSER_DEBUG_ADD_DOMAINNAME:
+ AddDomainName(argv[2], argv[3], (argc==5 ? TRUE : FALSE));
+
+ exit(0);
+ break;
+
+ case BROWSER_DEBUG_FIND_MASTER:
+#ifdef _CAIRO_
+ FindMaster(argv[2], (argc==3 ? NULL : argv[3]) );
+#else
+ FindMaster(argv[2]);
+#endif
+ exit(0);
+ break;
+ case BROWSER_DEBUG_GET_BACKUP_LIST:
+ GetBlist(argv[2], (argc == 3 ? NULL : argv[3]), (BOOLEAN)(argc==5? TRUE : FALSE));
+
+ exit(0);
+ break;
+
+ case BROWSER_DEBUG_ANNOUNCE_MASTER:
+#ifdef _CAIRO_
+ AnnounceMaster(argv[2], argv[3], (argc == 5) ? argv[4] : NULL );
+#else
+ AnnounceMaster(argv[2], argv[3]);
+#endif
+
+ exit(0);
+ break;
+
+ case BROWSER_DEBUG_ILLEGAL_DGRAM:
+#ifdef _CAIRO_
+ IllegalDatagram(argv[2], argv[3], (argc == 5) ? argv[4] : NULL );
+#else
+ IllegalDatagram(argv[2], argv[3]);
+#endif
+
+ exit(0);
+ break;
+
+ case BROWSER_DEBUG_GET_OTHLIST:
+ GetOtherdomains(argv[2]);
+
+ exit(0);
+ break;
+
+ case BROWSER_DEBUG_VIEW:
+ View(argv[2],
+ (argc >= 4 ? argv[3] : NULL),
+ (argc >= 5 ? argv[4] : NULL),
+ (argc >= 6 ? argv[5] : NULL),
+#ifndef _PSS_RELEASE
+ (argc == 7 ? TRUE : FALSE));
+#else
+ FALSE);
+#endif
+
+ exit(0);
+ break;
+
+ case BROWSER_DEBUG_LIST_WFW:
+ ListWFW(argv[2]) ;
+ exit(0);
+ break;
+
+ case BROWSER_DEBUG_RPCLIST:
+ RpcList(argv[2], (argc >= 4 ? argv[3] : NULL), (argc >= 5 ? argv[4] : NULL), (argc == 6 ? TRUE : FALSE));
+
+ exit(0);
+ break;
+
+ case BROWSER_DEBUG_RPCCMP:
+ RpcCmp(argv[2], (argc >= 4 ? argv[3] : NULL), (argc >= 5 ? argv[4] : NULL), (argc == 6 ? TRUE : FALSE));
+
+ exit(0);
+ break;
+
+ case BROWSER_DEBUG_LOCAL_BRLIST:
+#ifdef _CAIRO_
+ GetLocalList(argv[2], (argc >= 4 ? argv[3] : NULL), (argc >= 5 ? argv[4] : NULL) );
+#else
+ GetLocalList(argv[2], (argc >= 4 ? argv[3] : NULL) );
+#endif
+
+ exit(0);
+ break;
+
+ case BROWSER_DEBUG_GET_NETBIOSNAMES:
+#ifdef _CAIRO_
+ PrintNetbiosNames(argv[2], (argc >= 4 ? argv[3] : NULL));
+#else
+ PrintNetbiosNames(argv[2]);
+#endif
+
+ exit(0);
+ break;
+
+ case BROWSER_DEBUG_ADD_ALTERNATE:
+ AddAlternateComputerName(argv[2], argv[3]);
+
+ exit(0);
+ break;
+
+ case BROWSER_DEBUG_STATISTICS:
+ DumpStatistics(argc, argv[2]);
+
+ exit(0);
+ break;
+
+ case BROWSER_DEBUG_ANNOUNCE:
+#ifdef _CAIRO_
+ Announce(argv[2], argv[3], (argc >= 5 ? argv[4] : NULL ), (argc >= 6 ? TRUE : FALSE));
+#else
+ Announce(argv[2], argv[3], (argc >= 5 ? TRUE : FALSE));
+#endif
+
+ exit(0);
+ break;
+ case BROWSER_DEBUG_POPULATE_DOMAIN:
+ case BROWSER_DEBUG_POPULATE_SERVER:
+ Populate((ControlCode == BROWSER_DEBUG_POPULATE_DOMAIN ? TRUE : FALSE),
+ argv[2],
+ argv[3],
+#ifdef _CAIRO_
+ (argc >= 6 ? argv[5] : NULL),
+ argv[4],
+ (argc >= 7 ? argv[6] : NULL));
+#else
+ argv[4],
+ (argc >= 6 ? argv[5] : NULL));
+#endif
+
+ exit(0);
+ break;
+
+ case BROWSER_DEBUG_GET_WINSSERVER:
+ GetWinsServer(argv[2]);
+ exit(0);
+ break;
+
+ case BROWSER_DEBUG_GET_DOMAINLIST:
+ GetDomainList(argv[2]);
+ exit(0);
+ break;
+
+ case BROWSER_DEBUG_STATUS:
+ {
+ PCHAR Domain;
+ BOOL Verbose = FALSE;
+
+ if (argc == 4) {
+ if (_stricmp(argv[2], "-v") == 0) {
+ Verbose = TRUE;
+ Domain = argv[3];
+ } else {
+ CommandUsage( ControlCode );
+ exit(4);
+ }
+ } else if (argc == 3) {
+ if (_stricmp(argv[2], "-v") == 0) {
+ Verbose = TRUE;
+ Domain = NULL;
+ } else {
+ Domain = argv[2];
+ }
+ } else {
+ Domain = NULL;
+ }
+
+ BrowserStatus(Verbose, Domain);
+ }
+
+ exit(0);
+ break;
+
+#ifdef _CAIRO_
+ case BROWSER_DEBUG_SET_EMULATEDDOMAIN:
+ SetEmulatedDomain(argv[2], argv[3], (argc >= 5 ? argv[4] : NULL) );
+
+ exit(0);
+ break;
+
+
+ case BROWSER_DEBUG_SET_EMULATEDDOMAINENUM:
+ EnumEmulatedDomains();
+
+ exit(0);
+ break;
+
+#endif
+ }
+
+ Status = I_BrowserDebugCall(Server, ControlCode, Options);
+
+ printf("Api call returned %ld\n", Status);
+
+}
+
+
+NET_API_STATUS
+EnableService(
+ IN LPTSTR ServiceName
+ )
+{
+ SC_HANDLE ServiceControllerHandle;
+ SC_HANDLE ServiceHandle;
+
+ ServiceControllerHandle = OpenSCManager(NULL, NULL, SC_MANAGER_ENUMERATE_SERVICE);
+
+ if (ServiceControllerHandle == NULL) {
+
+ return GetLastError();
+ }
+
+ ServiceHandle = OpenService(ServiceControllerHandle, ServiceName, SERVICE_CHANGE_CONFIG);
+
+ if (ServiceHandle == NULL) {
+
+ CloseServiceHandle(ServiceControllerHandle);
+ return GetLastError();
+ }
+
+ if (!ChangeServiceConfig(ServiceHandle, SERVICE_NO_CHANGE,
+ SERVICE_DEMAND_START,
+ SERVICE_NO_CHANGE,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL)) {
+ CloseServiceHandle(ServiceHandle);
+ CloseServiceHandle(ServiceControllerHandle);
+
+ return GetLastError();
+ }
+
+
+ CloseServiceHandle(ServiceHandle);
+
+ CloseServiceHandle(ServiceControllerHandle);
+
+ return NERR_Success;
+}
+
+
+VOID
+GetBlist(
+ IN PCHAR TransportName,
+ IN PCHAR DomainName,
+ IN BOOLEAN ForceRescan
+ )
+{
+ NET_API_STATUS Status;
+ UNICODE_STRING UTransportName;
+ LPTSTR Domain;
+ PWSTR *BrowserList;
+ ULONG BrowserListLength;
+ ULONG i;
+
+ qualify_transport(TransportName, &UTransportName ) ;
+
+ Domain = NULL;
+
+ if (DomainName != NULL) {
+ UNICODE_STRING UDomainName;
+ ANSI_STRING ADomainName;
+
+ RtlInitString(&ADomainName, DomainName);
+
+ RtlAnsiStringToUnicodeString(&UDomainName, &ADomainName, TRUE);
+
+ Domain = UDomainName.Buffer;
+ }
+
+ Status = GetBrowserServerList(&UTransportName, Domain,
+ &BrowserList,
+ &BrowserListLength,
+ ForceRescan);
+
+ if (Status != NERR_Success) {
+ printf("Unable to get backup list: %s\n", get_error_text(Status));
+ exit(1);
+ }
+
+ for (i = 0; i < BrowserListLength ; i ++ ) {
+ printf("Browser: %s\n", UnicodeToPrintfString(BrowserList[i]));
+ }
+
+}
+
+ NET_API_STATUS
+SendDatagramA(
+ IN PCHAR Transport,
+#ifdef _CAIRO_
+ IN PCHAR EmulatedDomain OPTIONAL,
+#endif
+ IN PCHAR NetbiosName,
+ IN DGRECEIVER_NAME_TYPE NameType,
+ IN PVOID Buffer,
+ IN ULONG BufferSize
+ )
+/*++
+
+Routine Description:
+
+ Send a datagram on the specified transport.
+
+ The arguments are in the OEM character set.
+
+Arguments:
+
+ Transport - Transport to send on (might not be qualified yet.)
+
+#ifdef _CAIRO_
+ EmulatedDomain - Emulated Domain name. NULL implies primary domain.
+
+#endif
+ NetbiosName - Name to send the datagram to. (If Netbios name begins with
+ leading \\, they are removed.)
+
+ NameType - Type of 'Name'
+
+ Buffer - data to send
+
+ BufferSize - Number of byte in 'Buffer'
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+
+{
+ NET_API_STATUS NetStatus;
+
+ UNICODE_STRING TransportName;
+
+#ifdef _CAIRO_
+ UNICODE_STRING EmulatedDomainName;
+ ANSI_STRING AEmulatedDomainName;
+
+#endif
+ UNICODE_STRING UNetbiosName;
+ ANSI_STRING ANetbiosName;
+
+ HANDLE BrowserHandle;
+
+ //
+ // Qualify the transport name and convert it to unicode
+ //
+ qualify_transport(Transport, &TransportName) ;
+
+#ifdef _CAIRO_
+ //
+ // Convert the emulated domain name to unicode
+ //
+ RtlInitString(&AEmulatedDomainName, EmulatedDomain);
+ RtlAnsiStringToUnicodeString(&EmulatedDomainName, &AEmulatedDomainName, TRUE);
+#endif
+
+ //
+ // Convert the destination Netbios name to unicode
+ //
+
+ if (NetbiosName[0] == '\\' && NetbiosName[1] == '\\') {
+ RtlInitString(&ANetbiosName, &NetbiosName[2]);
+ } else {
+ RtlInitString(&ANetbiosName, NetbiosName );
+ }
+ RtlAnsiStringToUnicodeString(&UNetbiosName, &ANetbiosName, TRUE);
+
+
+ //
+ // Send the datagram
+ //
+
+ OpenBrowser(&BrowserHandle);
+
+ NetStatus = SendDatagram( BrowserHandle,
+ &TransportName,
+#ifdef _CAIRO_
+ &EmulatedDomainName,
+#endif
+ UNetbiosName.Buffer,
+ NameType,
+ Buffer,
+ BufferSize );
+
+ CloseHandle(BrowserHandle);
+
+ return NetStatus;
+}
+
+VOID
+Elect(
+ IN PCHAR Transport,
+#ifdef _CAIRO_
+ IN PCHAR Domain,
+ IN PCHAR EmulatedDomain
+#else
+ IN PCHAR Domain
+#endif
+ )
+{
+ REQUEST_ELECTION ElectionRequest;
+
+ ElectionRequest.Type = Election;
+
+ ElectionRequest.ElectionRequest.Version = 0;
+ ElectionRequest.ElectionRequest.Criteria = 0;
+ ElectionRequest.ElectionRequest.TimeUp = 0;
+ ElectionRequest.ElectionRequest.MustBeZero = 0;
+ ElectionRequest.ElectionRequest.ServerName[0] = '\0';
+
+ SendDatagramA( Transport,
+#ifdef _CAIRO_
+ EmulatedDomain,
+#endif
+ Domain,
+ BrowserElection,
+ &ElectionRequest,
+ sizeof(ElectionRequest) );
+
+}
+
+VOID
+Tickle(
+ IN PCHAR Transport,
+#ifdef _CAIRO_
+ IN PCHAR Domain,
+ IN PCHAR EmulatedDomain
+#else
+ IN PCHAR Domain
+#endif
+ )
+{
+ RESET_STATE ResetState;
+
+ ResetState.Type = ResetBrowserState;
+
+ ResetState.ResetStateRequest.Options = RESET_STATE_STOP_MASTER;
+
+ SendDatagramA( Transport,
+#ifdef _CAIRO_
+ EmulatedDomain,
+#endif
+ Domain,
+ ((Domain[0] == '\\' && Domain[1] == '\\') ?
+ ComputerName : MasterBrowser),
+ &ResetState,
+ sizeof(ResetState));
+
+}
+
+VOID
+GetMaster(
+ IN PCHAR Transport,
+ IN PCHAR Domain
+ )
+{
+ NET_API_STATUS Status;
+ UNICODE_STRING TransportName;
+ ANSI_STRING AString;
+ WCHAR MasterName[256];
+ UNICODE_STRING DomainName;
+
+ qualify_transport(Transport, &TransportName ) ;
+
+ RtlInitString(&AString, Domain);
+ RtlAnsiStringToUnicodeString(&DomainName, &AString, TRUE);
+
+ Status = GetNetBiosMasterName(
+ TransportName.Buffer,
+ DomainName.Buffer,
+ MasterName,
+ NULL);
+
+ if (Status != NERR_Success) {
+ printf("Unable to get Master: %s\n", get_error_text(Status));
+ exit(1);
+ }
+
+ printf("Master Browser: %s\n", UnicodeToPrintfString(MasterName));
+
+}
+
+#define SPACES " "
+
+#define ClearNcb( PNCB ) { \
+ RtlZeroMemory( PNCB , sizeof (NCB) ); \
+ RtlCopyMemory( (PNCB)->ncb_name, SPACES, sizeof(SPACES)-1 );\
+ RtlCopyMemory( (PNCB)->ncb_callname, SPACES, sizeof(SPACES)-1 );\
+ }
+
+
+VOID
+AddMasterName(
+ IN PCHAR Transport,
+ IN PCHAR Domain,
+ IN BOOL Pause
+ )
+{
+ NET_API_STATUS Status;
+ UNICODE_STRING TransportName;
+ CCHAR LanaNum;
+ NCB AddNameNcb;
+
+ qualify_transport(Transport, &TransportName) ;
+
+ Status = BrGetLanaNumFromNetworkName(TransportName.Buffer, &LanaNum);
+
+ if (Status != NERR_Success) {
+ printf("Unable to get transport: %lx\n", Status);
+ }
+
+ ClearNcb(&AddNameNcb)
+
+ AddNameNcb.ncb_command = NCBRESET;
+ AddNameNcb.ncb_lsn = 0; // Request resources
+ AddNameNcb.ncb_lana_num = LanaNum;
+ AddNameNcb.ncb_callname[0] = 0; // 16 sessions
+ AddNameNcb.ncb_callname[1] = 0; // 16 commands
+ AddNameNcb.ncb_callname[2] = 0; // 8 names
+ AddNameNcb.ncb_callname[3] = 0; // Don't want the reserved address
+ Netbios( &AddNameNcb );
+
+ ClearNcb( &AddNameNcb );
+
+ //
+ // Uppercase the remote name.
+ //
+
+ _strupr(Domain);
+
+ AddNameNcb.ncb_command = NCBADDNAME;
+
+ RtlCopyMemory( AddNameNcb.ncb_name, Domain, strlen(Domain));
+
+ AddNameNcb.ncb_name[15] = MASTER_BROWSER_SIGNATURE;
+
+ AddNameNcb.ncb_lana_num = LanaNum;
+ AddNameNcb.ncb_length = 0;
+ AddNameNcb.ncb_buffer = NULL;
+ Netbios( &AddNameNcb );
+
+ if ( AddNameNcb.ncb_retcode == NRC_GOODRET ) {
+ printf("Successfully added master name!!!!!\n");
+ } else {
+ printf("Unable to add master name: %lx\n", AddNameNcb.ncb_retcode);
+ }
+
+ if (Pause) {
+ printf("Press any key to continue...");
+ getchar();
+ }
+
+
+}
+
+VOID
+AddDomainName(
+ IN PCHAR Transport,
+ IN PCHAR Domain,
+ IN BOOL Pause
+ )
+{
+ NET_API_STATUS Status;
+ UNICODE_STRING TransportName;
+ CCHAR LanaNum;
+ NCB AddNameNcb;
+
+ qualify_transport(Transport, &TransportName ) ;
+
+ Status = BrGetLanaNumFromNetworkName(TransportName.Buffer, &LanaNum);
+
+ if (Status != NERR_Success) {
+ printf("Unable to get transport: %lx\n", Status);
+ }
+
+ ClearNcb(&AddNameNcb)
+
+ AddNameNcb.ncb_command = NCBRESET;
+ AddNameNcb.ncb_lsn = 0; // Request resources
+ AddNameNcb.ncb_lana_num = LanaNum;
+ AddNameNcb.ncb_callname[0] = 0; // 16 sessions
+ AddNameNcb.ncb_callname[1] = 0; // 16 commands
+ AddNameNcb.ncb_callname[2] = 0; // 8 names
+ AddNameNcb.ncb_callname[3] = 0; // Don't want the reserved address
+ Netbios( &AddNameNcb );
+
+ ClearNcb( &AddNameNcb );
+
+ //
+ // Uppercase the remote name.
+ //
+
+ _strupr(Domain);
+
+ AddNameNcb.ncb_command = NCBADDNAME;
+
+ RtlCopyMemory( AddNameNcb.ncb_name, Domain, strlen(Domain));
+
+ AddNameNcb.ncb_name[15] = PRIMARY_DOMAIN_SIGNATURE;
+
+ AddNameNcb.ncb_lana_num = LanaNum;
+ AddNameNcb.ncb_length = 0;
+ AddNameNcb.ncb_buffer = NULL;
+ Netbios( &AddNameNcb );
+
+ if ( AddNameNcb.ncb_retcode == NRC_GOODRET ) {
+ printf("Successfully added master name!!!!!\n");
+ } else {
+ printf("Unable to add master name: %lx\n", AddNameNcb.ncb_retcode);
+ }
+
+ if (Pause) {
+ printf("Press any key to continue...");
+ getchar();
+ }
+
+
+}
+
+VOID
+FindMaster(
+#ifdef _CAIRO_
+ IN PCHAR Transport,
+ IN PCHAR EmulatedDomain
+#else
+ IN PCHAR Transport
+#endif
+ )
+{
+ NET_API_STATUS Status;
+ UNICODE_STRING TransportName;
+#ifdef _CAIRO_
+ UNICODE_STRING EmulatedDomainName;
+ ANSI_STRING AEmulatedDomainName;
+#endif
+ LPWSTR MasterName;
+
+ qualify_transport(Transport, &TransportName) ;
+
+#ifdef _CAIRO_
+ RtlInitString(&AEmulatedDomainName, EmulatedDomain);
+ RtlAnsiStringToUnicodeString(&EmulatedDomainName, &AEmulatedDomainName, TRUE);
+
+ Status = GetMasterServerNames(&TransportName, &EmulatedDomainName, &MasterName);
+#else
+
+ Status = GetMasterServerNames(&TransportName, &MasterName);
+#endif
+
+ if (Status != NERR_Success) {
+ printf("Unable to get Master: %s\n", get_error_text(Status));
+ exit(1);
+ }
+
+ printf("Master Browser: %s\n", UnicodeToPrintfString(MasterName));
+
+}
+
+
+ NET_API_STATUS
+GetMasterServerNames(
+ IN PUNICODE_STRING NetworkName,
+#ifdef _CAIRO_
+ IN PUNICODE_STRING EmulatedDomainName,
+#endif
+ OUT LPWSTR *MasterName
+ )
+/*++
+
+Routine Description:
+
+ This function is the worker routine called to determine the name of the
+ master browser server for a particular network.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ Status - The status of the operation.
+
+--*/
+{
+ NET_API_STATUS Status;
+ HANDLE BrowserHandle;
+
+ PLMDR_REQUEST_PACKET RequestPacket = NULL;
+
+ RequestPacket = malloc(sizeof(LMDR_REQUEST_PACKET)+MAXIMUM_FILENAME_LENGTH*sizeof(WCHAR));
+
+ if (RequestPacket == NULL) {
+ return(ERROR_NOT_ENOUGH_MEMORY);
+ }
+
+ Status = OpenBrowser(&BrowserHandle);
+
+ if (Status != NERR_Success) {
+ return(Status);
+ }
+
+ RequestPacket->Version = LMDR_REQUEST_PACKET_VERSION;
+
+ RequestPacket->TransportName = *NetworkName;
+#ifdef _CAIRO_
+ RequestPacket->EmulatedDomainName = *EmulatedDomainName;
+#endif
+
+ //
+ // Reference the network while the I/O is pending.
+ //
+
+ Status = BrDgReceiverIoControl(BrowserHandle,
+ IOCTL_LMDR_GET_MASTER_NAME,
+ RequestPacket,
+ sizeof(LMDR_REQUEST_PACKET)+NetworkName->Length,
+ RequestPacket,
+ sizeof(LMDR_REQUEST_PACKET)+MAXIMUM_FILENAME_LENGTH*sizeof(WCHAR),
+ NULL);
+
+ if (Status != NERR_Success) {
+
+ printf("Browser: Unable to determine master for network %s: %ld\n", UnicodeToPrintfString(NetworkName->Buffer), Status);
+
+ free(RequestPacket);
+
+ return(Status);
+ }
+
+ *MasterName = malloc(RequestPacket->Parameters.GetMasterName.MasterNameLength+sizeof(WCHAR));
+
+ RtlCopyMemory(*MasterName, RequestPacket->Parameters.GetMasterName.Name,
+ RequestPacket->Parameters.GetMasterName.MasterNameLength+sizeof(WCHAR));
+
+ free(RequestPacket);
+
+ return Status;
+}
+
+ VOID
+AnnounceMaster(
+ IN PCHAR Transport,
+#ifdef _CAIRO_
+ IN PCHAR ServerName,
+ IN PCHAR EmulatedDomain
+#else
+ IN PCHAR ServerName
+#endif
+ )
+{
+ CHAR Buffer[sizeof(MASTER_ANNOUNCEMENT)+MAX_COMPUTERNAME_LENGTH+1];
+ PMASTER_ANNOUNCEMENT MasterAnnouncementp = (PMASTER_ANNOUNCEMENT)Buffer;
+ ULONG ComputerNameSize = MAX_COMPUTERNAME_LENGTH+1;
+
+
+ //
+ // Get the computer name of this machine and put it in the announcement
+ //
+
+ GetComputerNameA( MasterAnnouncementp->MasterAnnouncement.MasterName,
+ &ComputerNameSize);
+
+
+ //
+ // Send the announcement
+ //
+
+ MasterAnnouncementp->Type = MasterAnnouncement;
+
+ SendDatagramA( Transport,
+#ifdef _CAIRO_
+ EmulatedDomain,
+#endif
+ ServerName,
+ ComputerName,
+ MasterAnnouncementp,
+ FIELD_OFFSET(MASTER_ANNOUNCEMENT, MasterAnnouncement.MasterName) + ComputerNameSize+sizeof(CHAR));
+
+ return;
+}
+
+ VOID
+IllegalDatagram(
+ IN PCHAR Transport,
+#ifdef _CAIRO_
+ IN PCHAR ServerName,
+ IN PCHAR EmulatedDomain
+#else
+ IN PCHAR ServerName
+#endif
+ )
+{
+ REQUEST_ELECTION ElectRequest;
+
+ ElectRequest.Type = Election;
+
+ SendDatagramA( Transport,
+#ifdef _CAIRO_
+ EmulatedDomain,
+#endif
+ ServerName,
+ ComputerName,
+ &ElectRequest,
+ FIELD_OFFSET(REQUEST_ELECTION, ElectionRequest.TimeUp) );
+
+ return;
+}
+
+ VOID
+GetOtherdomains(
+ IN PCHAR ServerName
+ )
+{
+ NET_API_STATUS Status;
+ ANSI_STRING AServerName;
+ UNICODE_STRING UServerName;
+ PVOID Buffer;
+ PSERVER_INFO_100 ServerInfo;
+ ULONG i;
+ ULONG EntriesRead;
+ ULONG TotalEntries;
+
+ RtlInitString(&AServerName, ServerName);
+
+ RtlAnsiStringToUnicodeString(&UServerName, &AServerName, TRUE);
+
+ if ((wcslen(UServerName.Buffer) < 3) ||
+ wcsncmp(UServerName.Buffer, TEXT("\\\\"), 2) != 0 ||
+ I_NetNameValidate(NULL,
+ ((LPWSTR)UServerName.Buffer)+2,
+ NAMETYPE_COMPUTER,
+ 0L))
+ {
+ printf("Unable to query otherdomains: Invalid computer name\n") ;
+ return;
+ }
+
+ Status = I_BrowserQueryOtherDomains(UServerName.Buffer, (LPBYTE *)&Buffer, &EntriesRead, &TotalEntries);
+
+ if (Status != NERR_Success) {
+ printf("Unable to query otherdomains: %s\n", get_error_text(Status));
+ return;
+ }
+
+ printf("Other domains:\n");
+
+ ServerInfo = Buffer;
+
+ for (i = 0 ; i < EntriesRead; i++) {
+ printf(" %s\n", UnicodeToPrintfString(ServerInfo->sv100_name));
+ ServerInfo ++;
+ }
+
+ return;
+}
+
+VOID
+View(
+ IN PCHAR Transport,
+ IN PCHAR ServerOrDomain,
+ IN PCHAR FlagsString,
+ IN PCHAR Domain,
+ IN BOOL GoForever
+ )
+{
+ NET_API_STATUS Status;
+ UNICODE_STRING TransportName;
+ ANSI_STRING AServerName;
+ UNICODE_STRING UServerName;
+ ANSI_STRING ADomainName;
+ UNICODE_STRING UDomainName;
+ ULONG Flags ;
+ PVOID ServerList;
+ PSERVER_INFO_101 Server;
+ ULONG EntriesInList;
+ ULONG TotalEntries;
+ unsigned int i;
+
+ if ((ServerOrDomain && _stricmp(ServerOrDomain,"/domain")==0) ||
+ (Domain && _stricmp(Domain,"/domain")==0) )
+ {
+ CommandUsage( BROWSER_DEBUG_VIEW );
+ exit(4);
+ }
+
+ if (FlagsString)
+ {
+ if (_stricmp(FlagsString,"/domain")==0)
+ Flags = SV_TYPE_DOMAIN_ENUM ;
+ else
+ Flags = strtoul(FlagsString, NULL, 0);
+ }
+ else
+ Flags = SV_TYPE_ALL ;
+
+ qualify_transport(Transport, &TransportName) ;
+
+ RtlInitString(&AServerName, ServerOrDomain);
+
+ RtlAnsiStringToUnicodeString(&UServerName, &AServerName, TRUE);
+
+ if (ARGUMENT_PRESENT(Domain)) {
+ RtlInitString(&ADomainName, Domain);
+
+ RtlAnsiStringToUnicodeString(&UDomainName, &ADomainName, TRUE);
+
+ //
+ // if domain is present, this must be computername
+ //
+ if ((wcslen(UServerName.Buffer) < 3) ||
+ wcsncmp(UServerName.Buffer, TEXT("\\\\"), 2) != 0 ||
+ I_NetNameValidate(NULL,
+ ((LPWSTR)UServerName.Buffer)+2,
+ NAMETYPE_COMPUTER,
+ 0L))
+ {
+ printf("Invalid computer name: %s\n", ServerOrDomain) ;
+ exit(1);
+ }
+
+ }
+
+ if (UServerName.Buffer[0] != L'\\' || UServerName.Buffer[1] != L'\\') {
+ PWSTR *BrowserList;
+ ULONG BrowserListLength;
+
+ Status = GetBrowserServerList(&TransportName, UServerName.Buffer,
+ &BrowserList, &BrowserListLength, FALSE);
+
+ if (Status != NERR_Success) {
+ printf("Unable to get backup list for %s on transport %s: %s\n", UnicodeToPrintfString(UServerName.Buffer), UnicodeToPrintfString2(TransportName.Buffer), get_error_text(Status));
+ exit(1);
+ }
+
+ if (BrowserListLength == 0) {
+ printf("Unable to get backup list for %s", UnicodeToPrintfString(UServerName.Buffer));
+ printf(" on transport %s: %s\n", UnicodeToPrintfString(TransportName.Buffer), get_error_text(Status));
+ exit(1);
+ }
+
+ UServerName.Buffer = *BrowserList;
+
+ }
+
+ printf("Remoting NetServerEnum to %s", UnicodeToPrintfString(UServerName.Buffer));
+ printf(" on transport %s with flags %lx\n", UnicodeToPrintfString(TransportName.Buffer), Flags);
+
+ do {
+
+ DWORD StartTime = GetTickCount();
+ DWORD EndTime;
+
+ Status = RxNetServerEnum(UServerName.Buffer,
+ TransportName.Buffer,
+ 101,
+ (LPBYTE *)&ServerList,
+ 0xffffffff,
+ &EntriesInList,
+ &TotalEntries,
+ Flags,
+ ARGUMENT_PRESENT(Domain) ? UDomainName.Buffer : NULL,
+ NULL
+ );
+
+ EndTime = GetTickCount();
+
+ if (Status != NERR_Success) {
+ printf("Unable to remote API to %s ", UnicodeToPrintfString(UServerName.Buffer));
+ printf("on transport %s: %s (%d milliseconds)\n", UnicodeToPrintfString(TransportName.Buffer), get_error_text(Status), EndTime - StartTime);
+
+ if (Status != ERROR_MORE_DATA) {
+ exit(1);
+ }
+ }
+
+ printf("%ld entries returned. %ld total. %ld milliseconds\n\n", EntriesInList, TotalEntries, EndTime-StartTime);
+
+ if (!GoForever) {
+ Server = ServerList;
+
+ for (i = 0; i < EntriesInList ; i ++ ) {
+ DisplayServerInfo101( &Server[i], Flags==SV_TYPE_DOMAIN_ENUM );
+ printf("\n");
+ }
+ }
+
+ NetApiBufferFree(ServerList);
+
+ } while ( GoForever );
+
+
+ return;
+
+}
+
+VOID
+ListWFW(
+ IN PCHAR Domain
+ )
+{
+ NET_API_STATUS Status;
+ ANSI_STRING ADomainName;
+ UNICODE_STRING UDomainName;
+ PVOID ServerList;
+ PSERVER_INFO_101 Server;
+ ULONG EntriesInList;
+ ULONG TotalEntries;
+ unsigned int i;
+
+ RtlInitString(&ADomainName, Domain);
+ RtlAnsiStringToUnicodeString(&UDomainName, &ADomainName, TRUE);
+
+ printf("Calling NetServerEnum to enumerate WFW servers.\n") ;
+
+ Status = NetServerEnum(NULL,
+ 101,
+ (LPBYTE *)&ServerList,
+ 0xffffffff,
+ &EntriesInList,
+ &TotalEntries,
+ SV_TYPE_WFW,
+ UDomainName.Buffer,
+ NULL) ;
+
+ if (Status != NERR_Success)
+ {
+ printf("Unable to enumerate WFW servers. Error: %s\n",
+ get_error_text(Status));
+ exit(1);
+ }
+
+ printf("%ld WFW servers returned. %ld total.\n",
+ EntriesInList,
+ TotalEntries);
+ if (EntriesInList == 0)
+ printf("There are WFW servers with an active Browser.\n") ;
+ else
+ {
+ printf("The following are running the browser:\n\n") ;
+ Server = ServerList;
+ for (i = 0; i < EntriesInList ; i ++ ) {
+ DWORD ServerType = Server[i].sv101_type ;
+
+ if (!(ServerType & (SV_TYPE_POTENTIAL_BROWSER |
+ SV_TYPE_BACKUP_BROWSER |
+ SV_TYPE_MASTER_BROWSER ))) {
+ continue ;
+ }
+
+ DisplayServerInfo101( &Server[i], FALSE );
+ printf( "\n" );
+
+ }
+ }
+
+ NetApiBufferFree(ServerList);
+
+ return;
+}
+
+
+VOID
+ForceAnnounce(
+ IN PCHAR Transport,
+#ifdef _CAIRO_
+ IN PCHAR Domain,
+ IN PCHAR EmulatedDomain
+#else
+ IN PCHAR Domain
+#endif
+ )
+{
+ REQUEST_ANNOUNCE_PACKET RequestAnnounce;
+ ULONG NameSize = sizeof(RequestAnnounce.RequestAnnouncement.Reply);
+
+ //
+ // Build the request
+ //
+
+ RequestAnnounce.Type = AnnouncementRequest;
+
+ RequestAnnounce.RequestAnnouncement.Flags = 0;
+
+ GetComputerNameA(RequestAnnounce.RequestAnnouncement.Reply, &NameSize);
+
+ //
+ // Send the request
+ //
+
+ SendDatagramA( Transport,
+#ifdef _CAIRO_
+ EmulatedDomain,
+#endif
+ Domain,
+ BrowserElection,
+ &RequestAnnounce,
+ FIELD_OFFSET(REQUEST_ANNOUNCE_PACKET, RequestAnnouncement.Reply) + NameSize + sizeof(CHAR));
+
+}
+
+VOID
+GetLocalList(
+ IN PCHAR Transport,
+#ifdef _CAIRO_
+ IN PCHAR FlagsString,
+ IN PCHAR EmulatedDomain
+#else
+ IN PCHAR FlagsString
+#endif
+ )
+{
+ NET_API_STATUS Status;
+ UNICODE_STRING TransportName;
+#ifdef _CAIRO_
+ ANSI_STRING AEmulatedDomainName;
+ UNICODE_STRING EmulatedDomainName;
+#endif
+ ULONG Flags = (FlagsString == NULL ? SV_TYPE_ALL : strtoul(FlagsString, NULL, 0));
+ PVOID ServerList;
+ PSERVER_INFO_101 Server;
+ ULONG EntriesInList;
+ ULONG TotalEntries;
+ unsigned int i;
+
+ qualify_transport(Transport, &TransportName) ;
+
+#ifdef _CAIRO_
+ RtlInitString(&AEmulatedDomainName, EmulatedDomain );
+ RtlAnsiStringToUnicodeString(&EmulatedDomainName, &AEmulatedDomainName, TRUE);
+
+ printf("Retrieving local browser list on transport %ws\\%s with flags %lx\n", EmulatedDomainName.Buffer, UnicodeToPrintfString(TransportName.Buffer), Flags);
+#else
+
+ printf("Retrieving local browser list on transport %s with flags %lx\n", UnicodeToPrintfString(TransportName.Buffer), Flags);
+#endif
+
+ Status = GetLocalBrowseList (&TransportName,
+#ifdef _CAIRO_
+ &EmulatedDomainName,
+#endif
+ 101,
+ Flags,
+ (LPBYTE *)&ServerList,
+ &EntriesInList,
+ &TotalEntries
+ );
+
+ if (Status != NERR_Success) {
+ printf("Unable to retrieve local list on transport %s: %lx\n", UnicodeToPrintfString(TransportName.Buffer), Status);
+
+ exit(1);
+ }
+
+ Server = ServerList;
+
+ printf("%ld entries returned. %ld total.\n", EntriesInList, TotalEntries);
+
+ for (i = 0; i < EntriesInList ; i ++ ) {
+
+ DisplayServerInfo101( &Server[i], Flags==SV_TYPE_DOMAIN_ENUM );
+
+ if (Flags == SV_TYPE_BACKUP_BROWSER) {
+ PUSHORT BrowserVersion = (PUSHORT)Server[i].sv101_comment - 1;
+ printf(" V:%4.4x", *BrowserVersion);
+ }
+
+ printf("\n") ;
+
+ }
+
+
+
+ return;
+
+}
+
+ NET_API_STATUS
+GetLocalBrowseList(
+ IN PUNICODE_STRING Network,
+#ifdef _CAIRO_
+ IN PUNICODE_STRING EmulatedDomainName,
+#endif
+ IN ULONG Level,
+ IN ULONG ServerType,
+ OUT PVOID *ServerList,
+ OUT PULONG EntriesRead,
+ OUT PULONG TotalEntries
+ )
+{
+ NET_API_STATUS status;
+ PLMDR_REQUEST_PACKET Drp; // Datagram receiver request packet
+ ULONG DrpSize;
+ HANDLE BrowserHandle;
+ LPBYTE Where;
+
+ OpenBrowser(&BrowserHandle);
+
+ //
+ // Allocate the request packet large enough to hold the variable length
+ // domain name.
+ //
+
+ DrpSize = sizeof(LMDR_REQUEST_PACKET) +
+#ifdef _CAIRO_
+ Network->Length + sizeof(WCHAR) +
+ EmulatedDomainName->Length + sizeof(WCHAR);
+#else
+ Network->Length + sizeof(WCHAR);
+#endif
+
+ if ((Drp = malloc(DrpSize)) == NULL) {
+
+ return GetLastError();
+ }
+
+ //
+ // Set up request packet. Output buffer structure is of enumerate
+ // servers type.
+ //
+
+ Drp->Version = LMDR_REQUEST_PACKET_VERSION;
+ Drp->Type = EnumerateServers;
+
+ Drp->Level = Level;
+
+ Drp->Parameters.EnumerateServers.ServerType = ServerType;
+ Drp->Parameters.EnumerateServers.ResumeHandle = 0;
+ Drp->Parameters.EnumerateServers.DomainNameLength = 0;
+ Drp->Parameters.EnumerateServers.DomainName[0] = '\0';
+
+ Where = ((PCHAR)Drp+sizeof(LMDR_REQUEST_PACKET));
+ wcscpy( (LPWSTR)Where, Network->Buffer );
+ RtlInitUnicodeString( &Drp->TransportName, (LPWSTR) Where );
+
+ Where += Drp->TransportName.MaximumLength;
+#ifdef _CAIRO_
+ wcscpy( (LPWSTR)Where, EmulatedDomainName->Buffer );
+ RtlInitUnicodeString( &Drp->EmulatedDomainName, (LPWSTR) Where );
+#ifdef notdef
+ Where += Drp->EmulatedDomainName.MaximumLength;
+#endif // notdef
+#endif
+
+ //
+ // Ask the datagram receiver to enumerate the servers
+ //
+
+ status = DeviceControlGetInfo(
+ BrowserHandle,
+ IOCTL_LMDR_ENUMERATE_SERVERS,
+ Drp,
+ DrpSize,
+ ServerList,
+ 0xffffffff,
+ 4096,
+ NULL
+ );
+
+ *EntriesRead = Drp->Parameters.EnumerateServers.EntriesRead;
+ *TotalEntries = Drp->Parameters.EnumerateServers.TotalEntries;
+
+ (void) free(Drp);
+
+ return status;
+
+}
+
+VOID
+PrintNetbiosNames(
+#ifdef _CAIRO_
+ IN PCHAR Transport,
+ IN PCHAR EmulatedDomain OPTIONAL
+#else
+ IN PCHAR Transport
+#endif
+ )
+/*++
+
+Routine Description:
+
+ Prints the list of Netbios names registered on a particular transport
+
+Arguments:
+
+ Transport - Transport to query
+
+#ifdef _CAIRO_
+ EmulatedDomain - Emulated domain to query
+
+#endif
+Return Value:
+
+ None
+
+--*/
+{
+ NET_API_STATUS Status;
+ UNICODE_STRING TransportName;
+#ifdef _CAIRO_
+ ANSI_STRING AEmulatedDomainName;
+ UNICODE_STRING EmulatedDomainName;
+#endif
+ PVOID NameList;
+ PDGRECEIVE_NAMES Names;
+ ULONG EntriesInList;
+ ULONG TotalEntries;
+ unsigned int i;
+
+ //
+ // Get the netbios names
+ //
+ qualify_transport(Transport, &TransportName ) ;
+
+#ifdef _CAIRO_
+ RtlInitString(&AEmulatedDomainName, EmulatedDomain );
+ RtlAnsiStringToUnicodeString(&EmulatedDomainName, &AEmulatedDomainName, TRUE);
+
+ printf("Retrieving browser Netbios names on transport %ws\\%ws\n", EmulatedDomainName.Buffer, TransportName.Buffer);
+#else
+ printf("Retrieving browser Netbios names on transport %ws\n", TransportName.Buffer);
+#endif
+
+ Status = GetNetbiosNames(&TransportName,
+#ifdef _CAIRO_
+ &EmulatedDomainName,
+#endif
+ &NameList,
+ &EntriesInList,
+ &TotalEntries
+ );
+
+ if (Status != NERR_Success) {
+ printf("Unable to retrieve Netbios names on transport %ws: %lx\n", TransportName.Buffer, Status);
+ exit(1);
+ }
+
+ //
+ // Print the netbios names.
+ //
+
+ Names = NameList;
+
+ printf("%ld entries returned. %ld total.\n", EntriesInList, TotalEntries);
+
+ for (i = 0; i < EntriesInList ; i ++ ) {
+ if ( Names[i].Type == DomainAnnouncement ) {
+ printf("%-16.16s", "__MSBROWSE__" );
+ } else {
+ printf("%-16.16wZ", &Names[i].DGReceiverName );
+ }
+ switch ( Names[i].Type ) {
+ case ComputerName:
+ printf("<00> ComputerName"); break;
+ case AlternateComputerName:
+ printf("<00> AlternateComputerName"); break;
+ case PrimaryDomain:
+ printf("<00> PrimaryDomain"); break;
+ case LogonDomain:
+ printf("<00> LogonDomain"); break;
+ case OtherDomain:
+ printf("<00> OtherDomain"); break;
+ case DomainAnnouncement:
+ printf("DomainAnnouncement"); break;
+ case MasterBrowser:
+ printf("<1D> MasterBrowser"); break;
+ case BrowserElection:
+ printf("<1E> BrowserElection"); break;
+ case BrowserServer:
+ printf("<20> BrowserServer"); break;
+ case DomainName:
+ printf("<1C> DomainName"); break;
+ case PrimaryDomainBrowser:
+ printf("<1B> DomainMasterBrowser"); break;
+ default:
+ printf("<Unknown>"); break;
+ }
+ printf("\n") ;
+
+ }
+
+ return;
+
+}
+
+ NET_API_STATUS
+GetNetbiosNames(
+ IN PUNICODE_STRING Network,
+#ifdef _CAIRO_
+ IN PUNICODE_STRING EmulatedDomainName,
+#endif
+ OUT PVOID *NameList,
+ OUT PULONG EntriesRead,
+ OUT PULONG TotalEntries
+ )
+{
+ NET_API_STATUS status;
+ PLMDR_REQUEST_PACKET Drp; // Datagram receiver request packet
+ ULONG DrpSize;
+ HANDLE BrowserHandle;
+ LPBYTE Where;
+
+ OpenBrowser(&BrowserHandle);
+
+ //
+ // Allocate the request packet large enough to hold the variable length
+ // domain name.
+ //
+
+ DrpSize = sizeof(LMDR_REQUEST_PACKET) +
+#ifdef _CAIRO_
+ Network->Length + sizeof(WCHAR) +
+ EmulatedDomainName->Length + sizeof(WCHAR);
+#else
+ Network->Length + sizeof(WCHAR);
+#endif
+
+ if ((Drp = malloc(DrpSize)) == NULL) {
+ return GetLastError();
+ }
+
+ //
+ // Set up request packet. Output buffer structure is of enumerate
+ // servers type.
+ //
+
+ Drp->Version = LMDR_REQUEST_PACKET_VERSION;
+ Drp->Type = EnumerateNames;
+
+ Drp->Level = 0;
+
+ Drp->Parameters.EnumerateNames.ResumeHandle = 0;
+
+ Where = ((PCHAR)Drp+sizeof(LMDR_REQUEST_PACKET));
+ wcscpy( (LPWSTR)Where, Network->Buffer );
+ RtlInitUnicodeString( &Drp->TransportName, (LPWSTR) Where );
+
+ Where += Drp->TransportName.MaximumLength;
+#ifdef _CAIRO_
+ wcscpy( (LPWSTR)Where, EmulatedDomainName->Buffer );
+ RtlInitUnicodeString( &Drp->EmulatedDomainName, (LPWSTR) Where );
+#ifdef notdef
+ Where += Drp->EmulatedDomainName.MaximumLength;
+#endif // notdef
+#endif
+
+ //
+ // Ask the datagram receiver to enumerate the names
+ //
+
+ status = DeviceControlGetInfo(
+ BrowserHandle,
+ IOCTL_LMDR_ENUMERATE_NAMES,
+ Drp,
+ DrpSize,
+ NameList,
+ 0xffffffff,
+ 4096,
+ NULL );
+
+ *EntriesRead = Drp->Parameters.EnumerateNames.EntriesRead;
+ *TotalEntries = Drp->Parameters.EnumerateNames.TotalEntries;
+
+ (void) free(Drp);
+
+ return status;
+
+}
+
+ NET_API_STATUS
+AddAlternateComputerName(
+ IN PCHAR Transport,
+ IN PCHAR ComputerName
+ )
+/*++
+
+Routine Description:
+
+ This function adds an alternate compture name on the specified transport.
+
+Arguments:
+
+ Transport - Transport to add the computer name on.
+
+ ComputerName - Alternate computer name to add.
+
+Return Value:
+
+ Status - The status of the operation.
+
+--*/
+{
+ NET_API_STATUS Status;
+ HANDLE BrowserHandle;
+ LPBYTE Where;
+
+ PLMDR_REQUEST_PACKET RequestPacket = NULL;
+
+ UNICODE_STRING TransportName;
+ WCHAR UnicodeComputerName[CNLEN+1];
+
+#ifdef _CAIRO_
+ UNICODE_STRING EmulatedDomainName;
+ ANSI_STRING AEmulatedDomainName;
+#endif
+
+ //
+ // Qualify the transport name and convert it to unicode
+ //
+ qualify_transport(Transport, &TransportName) ;
+ NetpCopyStrToWStr( UnicodeComputerName, ComputerName );
+
+ RequestPacket = malloc(sizeof(LMDR_REQUEST_PACKET)+(LM20_CNLEN+1)*sizeof(WCHAR));
+
+ if (RequestPacket == NULL) {
+ return(ERROR_NOT_ENOUGH_MEMORY);
+ }
+
+ Status = OpenBrowser(&BrowserHandle);
+
+ if (Status != NERR_Success) {
+ return(Status);
+ }
+
+ RequestPacket->Version = LMDR_REQUEST_PACKET_VERSION;
+
+ RequestPacket->TransportName = TransportName;
+#ifdef _CAIRO_
+ RequestPacket->EmulatedDomainName = *EmulatedDomainName;
+#endif
+
+ RequestPacket->Parameters.AddDelName.Type = AlternateComputerName;
+ RequestPacket->Parameters.AddDelName.DgReceiverNameLength =
+ wcslen(UnicodeComputerName)*sizeof(WCHAR);
+ wcscpy(RequestPacket->Parameters.AddDelName.Name, UnicodeComputerName);
+ Where = ((LPBYTE)(RequestPacket->Parameters.AddDelName.Name)) +
+ RequestPacket->Parameters.AddDelName.DgReceiverNameLength +
+ sizeof(WCHAR);
+
+ //
+ // Reference the network while the I/O is pending.
+ //
+
+ Status = BrDgReceiverIoControl(BrowserHandle,
+ IOCTL_LMDR_ADD_NAME_DOM,
+ RequestPacket,
+ Where - (LPBYTE)RequestPacket,
+ NULL,
+ 0,
+ NULL);
+
+ if (Status != NERR_Success) {
+
+ printf("Browser: Unable to add name for network %s: %ld\n", UnicodeToPrintfString(TransportName.Buffer), Status);
+
+ free(RequestPacket);
+
+ return(Status);
+ }
+
+ free(RequestPacket);
+
+ return Status;
+}
+
+VOID
+Announce(
+ IN PCHAR Transport,
+ IN PCHAR Domain,
+#ifdef _CAIRO_
+ IN PCHAR EmulatedDomain,
+#endif
+ IN BOOL AsMaster
+ )
+{
+
+ PSERVER_INFO_101 ServerInfo;
+ PWKSTA_INFO_101 WkstaInfo;
+ UNICODE_STRING TransportName;
+ LPBYTE Buffer;
+ ULONG BrowserType;
+ ULONG OriginalBrowserType;
+ WCHAR UDomain[256];
+ WCHAR ServerComment[256];
+ WCHAR ServerName[256];
+ BOOLEAN IsLocalDomain;
+ SERVICE_STATUS ServiceStatus;
+ DWORD VersionMajor;
+ DWORD VersionMinor;
+ BOOL UsedDefaultChar;
+
+ qualify_transport(Transport, &TransportName ) ;
+
+
+ MultiByteToWideChar(CP_ACP, 0, Domain, strlen(Domain)+1, UDomain, sizeof(UDomain));
+
+ NetServerGetInfo(NULL, 101, &Buffer);
+
+ ServerInfo = (PSERVER_INFO_101 )Buffer;
+
+ BrowserType = (ServerInfo->sv101_type & (SV_TYPE_BACKUP_BROWSER | SV_TYPE_POTENTIAL_BROWSER | SV_TYPE_MASTER_BROWSER));
+
+ wcscpy(ServerComment, ServerInfo->sv101_comment);
+
+ wcscpy(ServerName, ServerInfo->sv101_name);
+
+ VersionMajor = ServerInfo->sv101_version_major;
+
+ VersionMinor = ServerInfo->sv101_version_minor;
+
+ NetApiBufferFree(Buffer);
+
+ NetWkstaGetInfo(NULL, 101, &Buffer);
+
+ WkstaInfo = (PWKSTA_INFO_101 )Buffer;
+
+ IsLocalDomain = !_wcsicmp(UDomain, WkstaInfo->wki101_langroup);
+
+ NetApiBufferFree(Buffer);
+
+ OriginalBrowserType = BrowserType;
+
+ if (AsMaster) {
+ BrowserType |= SV_TYPE_MASTER_BROWSER;
+ }
+
+ //
+ // If the browser is running, and this is our local domain, have the
+ // server do the announcing.
+ //
+
+ if (IsLocalDomain &&
+ CheckForService(SERVICE_BROWSER, &ServiceStatus) == NERR_Success ) {
+
+ printf("Toggling local server status bits to %lx and then to %lx\n",
+ BrowserType, OriginalBrowserType);
+
+ I_NetServerSetServiceBits(NULL, TransportName.Buffer, BrowserType, TRUE);
+
+ I_NetServerSetServiceBits(NULL, TransportName.Buffer, OriginalBrowserType, TRUE);
+
+ } else {
+ BROWSE_ANNOUNCE_PACKET BrowseAnnouncement;
+
+ printf("Announcing to domain %s by hand\n", UnicodeToPrintfString(UDomain));
+
+ BrowseAnnouncement.BrowseType = (AsMaster ? LocalMasterAnnouncement : HostAnnouncement);
+
+ BrowseAnnouncement.BrowseAnnouncement.UpdateCount = 0;
+
+ WideCharToMultiByte(CP_OEMCP, 0,
+ ServerName,
+ wcslen(ServerName),
+ BrowseAnnouncement.BrowseAnnouncement.ServerName,
+ LM20_CNLEN+1,
+ "?",
+ &UsedDefaultChar
+ );
+
+ BrowseAnnouncement.BrowseAnnouncement.VersionMajor = (UCHAR)VersionMajor;
+ BrowseAnnouncement.BrowseAnnouncement.VersionMinor = (UCHAR)VersionMinor;
+ BrowseAnnouncement.BrowseAnnouncement.Type = BrowserType;
+
+ WideCharToMultiByte(CP_OEMCP, 0,
+ ServerComment,
+ wcslen(ServerComment),
+ BrowseAnnouncement.BrowseAnnouncement.Comment,
+ LM20_MAXCOMMENTSZ+1,
+ "?",
+ &UsedDefaultChar
+ );
+
+ BrowseAnnouncement.BrowseAnnouncement.CommentPointer = NULL;
+
+
+ //
+ // Send the request
+ //
+
+ SendDatagramA( Transport,
+#ifdef _CAIRO_
+ EmulatedDomain,
+#endif
+ Domain,
+ (AsMaster ? BrowserElection : MasterBrowser),
+ &BrowseAnnouncement,
+ sizeof(BrowseAnnouncement));
+
+ }
+
+ return;
+}
+
+VOID
+RpcList(
+ IN PCHAR Transport,
+ IN PCHAR ServerOrDomain,
+ IN PCHAR FlagsString,
+ IN BOOL GoForever
+ )
+{
+ NET_API_STATUS Status;
+ UNICODE_STRING TransportName;
+ ANSI_STRING AServerName;
+ UNICODE_STRING UServerName;
+ ULONG Flags = (FlagsString == NULL ? SV_TYPE_ALL : strtoul(FlagsString, NULL, 0));
+ PVOID ServerList;
+ PSERVER_INFO_101 Server;
+ ULONG EntriesInList;
+ ULONG TotalEntries;
+ unsigned int i;
+
+ qualify_transport(Transport, &TransportName) ;
+
+ RtlInitString(&AServerName, ServerOrDomain);
+ RtlAnsiStringToUnicodeString(&UServerName, &AServerName, TRUE);
+
+ if (UServerName.Buffer[0] != L'\\' || UServerName.Buffer[1] != L'\\') {
+ PWSTR *BrowserList;
+ ULONG BrowserListLength;
+
+ Status = GetBrowserServerList(&TransportName, UServerName.Buffer,
+ &BrowserList, &BrowserListLength, FALSE);
+
+ if (Status != NERR_Success) {
+ printf("Unable to get backup list for %s", UnicodeToPrintfString(UServerName.Buffer));
+ printf(" on transport %s: %s\n", UnicodeToPrintfString(TransportName.Buffer), get_error_text(Status));
+ exit(1);
+ }
+
+ if (BrowserListLength == 0) {
+ printf("Unable to get backup list for %s", UnicodeToPrintfString(UServerName.Buffer));
+ printf(" on transport %s: %s\n",
+ UnicodeToPrintfString(TransportName.Buffer), get_error_text(Status));
+ exit(1);
+ }
+
+ UServerName.Buffer = *BrowserList;
+
+ }
+
+ printf("Remoting I_BrowserServerEnum to %s", UnicodeToPrintfString(UServerName.Buffer));
+ printf(" on transport %s with flags %lx\n", UnicodeToPrintfString(TransportName.Buffer), Flags);
+
+ do {
+
+ Status = I_BrowserServerEnum(UServerName.Buffer,
+ TransportName.Buffer,
+ NULL,
+ 101,
+ (LPBYTE *)&ServerList,
+ 0xffffffff,
+ &EntriesInList,
+ &TotalEntries,
+ Flags,
+ NULL,
+ NULL
+ );
+
+ if (Status != NERR_Success) {
+
+ printf("Unable to remote API to %s", UnicodeToPrintfString(UServerName.Buffer));
+ printf(" on transport %s: %s\n",UnicodeToPrintfString(TransportName.Buffer), get_error_text(Status));
+ if (Status != ERROR_MORE_DATA) {
+ exit(1);
+ }
+ }
+
+ printf("%ld entries returned. %ld total.\n", EntriesInList, TotalEntries);
+
+ if (!GoForever) {
+ Server = ServerList;
+
+ for (i = 0; i < EntriesInList ; i ++ ) {
+
+ DisplayServerInfo101( &Server[i], Flags==SV_TYPE_DOMAIN_ENUM );
+ printf( "\n" );
+ }
+ }
+
+ NetApiBufferFree(ServerList);
+
+ } while ( GoForever );
+
+
+
+ return;
+
+}
+
+VOID
+RpcCmp(
+ IN PCHAR Transport,
+ IN PCHAR ServerOrDomain,
+ IN PCHAR FlagsString,
+ IN BOOL GoForever
+ )
+{
+ NET_API_STATUS Status;
+ UNICODE_STRING TransportName;
+ ANSI_STRING AServerName;
+ UNICODE_STRING UServerName;
+ ULONG Flags = (FlagsString == NULL ? SV_TYPE_ALL : strtoul(FlagsString, NULL, 0));
+ PVOID RpcServerList;
+ PVOID RxServerList;
+ PSERVER_INFO_101 RpcServer;
+ PSERVER_INFO_101 RxServer;
+ ULONG RpcEntriesInList;
+ ULONG RpcTotalEntries;
+ ULONG RxEntriesInList;
+ ULONG RxTotalEntries;
+ unsigned int i;
+ unsigned int j;
+
+ qualify_transport(Transport, &TransportName) ;
+
+ RtlInitString(&AServerName, ServerOrDomain);
+
+ RtlAnsiStringToUnicodeString(&UServerName, &AServerName, TRUE);
+
+ if (UServerName.Buffer[0] != L'\\' || UServerName.Buffer[1] != L'\\') {
+ PWSTR *BrowserList;
+ ULONG BrowserListLength;
+
+ Status = GetBrowserServerList(&TransportName, UServerName.Buffer,
+ &BrowserList, &BrowserListLength, FALSE);
+
+ if (Status != NERR_Success) {
+ printf("Unable to get backup list for %s on transport %s: %lx\n", UnicodeToPrintfString(UServerName.Buffer), UnicodeToPrintfString2(TransportName.Buffer), Status);
+ exit(1);
+ }
+
+ if (BrowserListLength == 0) {
+ printf("Unable to get backup list for %s on transport %s: %lx\n", UnicodeToPrintfString(UServerName.Buffer), UnicodeToPrintfString2(TransportName.Buffer), Status);
+ exit(1);
+ }
+
+ UServerName.Buffer = *BrowserList;
+
+ }
+
+ printf("Remoting I_BrowserServerEnum to %s on transport %s with flags %lx\n", UnicodeToPrintfString(UServerName.Buffer), UnicodeToPrintfString2(TransportName.Buffer), Flags);
+
+ do {
+
+ Status = I_BrowserServerEnum(UServerName.Buffer,
+ TransportName.Buffer,
+ NULL,
+ 101,
+ (LPBYTE *)&RpcServerList,
+ 0xffffffff,
+ &RpcEntriesInList,
+ &RpcTotalEntries,
+ Flags,
+ NULL,
+ NULL
+ );
+
+ if (Status != NERR_Success) {
+ printf("Unable to remote API to %s on transport %s: %ld\n", UnicodeToPrintfString(UServerName.Buffer), UnicodeToPrintfString2(TransportName.Buffer), Status);
+
+ exit(1);
+ }
+
+ printf("%ld entries returned from RPC. %ld total.\n", RpcEntriesInList, RpcTotalEntries);
+
+ if (RpcEntriesInList != RpcTotalEntries) {
+ printf("EntriesRead != TotalEntries from remoted server enum\n");
+ }
+
+ if (RpcEntriesInList <= 20) {
+ printf("EntriesInList returned %ld from remoted server enum\n", RpcEntriesInList);
+ }
+
+
+ Status = RxNetServerEnum(UServerName.Buffer,
+ TransportName.Buffer,
+ 101,
+ (LPBYTE *)&RxServerList,
+ 0xffffffff,
+ &RxEntriesInList,
+ &RxTotalEntries,
+ Flags,
+ NULL,
+ NULL
+ );
+
+
+ if (Status != NERR_Success) {
+ printf("Unable to remote API to %s on transport %s: %ld\n", UnicodeToPrintfString(UServerName.Buffer), UnicodeToPrintfString2(TransportName.Buffer), Status);
+ exit(1);
+ }
+
+ printf("%ld entries returned from RX. %ld total.\n", RxEntriesInList, RxTotalEntries);
+
+ if (RxEntriesInList != RxTotalEntries) {
+ printf("RxEntriesRead != RxEntriesInList from remoted server enum\n");
+ }
+
+ if (RxEntriesInList <= 20) {
+ printf("RxEntriesInList returned %ld from remoted server enum\n", RxEntriesInList);
+ }
+
+ if (RxEntriesInList != RpcEntriesInList) {
+ printf("RxEntriesRead (%ld) != RpcTotalEntries (%ld) from remoted server enum\n", RxEntriesInList, RpcEntriesInList);
+ }
+
+ RxServer = RxServerList;
+ RpcServer = RpcServerList;
+
+ for (i = 0; i < RxEntriesInList ; i ++ ) {
+
+ for (j = 0; j < RpcEntriesInList ; j++) {
+
+ if (RxServer[i].sv101_name != NULL &&
+ RpcServer[j].sv101_name != NULL) {
+
+ if (!wcscmp(RxServer[i].sv101_name, RpcServer[j].sv101_name)) {
+ RxServer[i].sv101_name = NULL;
+ RpcServer[j].sv101_name = NULL;
+ break;
+ }
+ }
+ }
+ }
+
+ for (i = 0; i < RpcEntriesInList ; i++ ) {
+ if (RpcServer[i].sv101_name != NULL) {
+ printf("Rpc Server not in Rx List: %s\n", UnicodeToPrintfString(RpcServer[i].sv101_name));
+ }
+ }
+
+ for (i = 0; i < RxEntriesInList ; i++ ) {
+ if (RxServer[i].sv101_name != NULL) {
+ printf("Rx Server not in Rpc List: %s\n", UnicodeToPrintfString(RxServer[i].sv101_name));
+ }
+ }
+
+ NetApiBufferFree(RxServerList);
+ NetApiBufferFree(RpcServerList);
+
+ } while ( GoForever );
+
+ return;
+
+}
+
+CHAR * format_dlword(ULONG high, ULONG low, CHAR * buf);
+
+VOID
+revstr_add(CHAR * target, CHAR * source);
+
+VOID
+DumpStatistics(
+ IN ULONG NArgs,
+ IN PCHAR Arg1
+ )
+{
+ PBROWSER_STATISTICS Statistics = NULL;
+ NET_API_STATUS Status;
+ CHAR Buffer[256];
+ WCHAR ServerName[256];
+ LPTSTR Server = NULL;
+ BOOL ResetStatistics = FALSE;
+
+ if (NArgs == 2) {
+ Server = NULL;
+ ResetStatistics = FALSE;
+ } else if (NArgs == 3) {
+ if (*Arg1 == '\\') {
+ MultiByteToWideChar(CP_ACP, 0, Arg1, strlen(Arg1)+1, ServerName, sizeof(ServerName));
+
+ Server = ServerName;
+ ResetStatistics = FALSE;
+ } else {
+ Server = NULL;
+ ResetStatistics = TRUE;
+ }
+ } else if (*Arg1 == '\\') {
+ MultiByteToWideChar(CP_ACP, 0, Arg1, strlen(Arg1)+1, ServerName, sizeof(ServerName));
+ Server = ServerName;
+ ResetStatistics = TRUE;
+ }
+
+ if (ResetStatistics) {
+ Status = I_BrowserResetStatistics(Server);
+
+ if (Status != NERR_Success) {
+ printf("Unable to reset browser statistics: %ld\n", Status);
+ exit(1);
+ }
+ } else {
+ FILETIME LocalFileTime;
+ SYSTEMTIME LocalSystemTime;
+
+ Status = I_BrowserQueryStatistics(Server, &Statistics);
+
+ if (Status != NERR_Success) {
+ printf("Unable to query browser statistics: %ld\n", Status);
+ exit(1);
+ }
+
+ if (!FileTimeToLocalFileTime((LPFILETIME)&Statistics->StatisticsStartTime, &LocalFileTime)) {
+ printf("Unable to convert statistics start time: %ld\n", GetLastError());
+ exit(1);
+ }
+
+ if (!FileTimeToSystemTime(&LocalFileTime, &LocalSystemTime)) {
+ printf("Unable to convert statistics start time to system time: %ld\n", GetLastError());
+ exit(1);
+ }
+
+ printf("Browser statistics since %ld:%ld:%ld.%ld on %ld/%d/%d\n",
+ LocalSystemTime.wHour,
+ LocalSystemTime.wMinute,
+ LocalSystemTime.wSecond,
+ LocalSystemTime.wMilliseconds,
+ LocalSystemTime.wMonth,
+ LocalSystemTime.wDay,
+ LocalSystemTime.wYear);
+
+ printf("NumberOfServerEnumerations:\t\t\t%d\n", Statistics->NumberOfServerEnumerations);
+ printf("NumberOfDomainEnumerations:\t\t\t%d\n", Statistics->NumberOfDomainEnumerations);
+ printf("NumberOfOtherEnumerations:\t\t\t%d\n", Statistics->NumberOfOtherEnumerations);
+ printf("NumberOfMailslotWrites:\t\t\t\t%d\n", Statistics->NumberOfMailslotWrites);
+ printf("NumberOfServerAnnouncements:\t\t\t%s\n", format_dlword(Statistics->NumberOfServerAnnouncements.HighPart, Statistics->NumberOfServerAnnouncements.LowPart, Buffer));
+ printf("NumberOfDomainAnnouncements:\t\t\t%s\n", format_dlword(Statistics->NumberOfDomainAnnouncements.HighPart, Statistics->NumberOfDomainAnnouncements.LowPart, Buffer));
+ printf("NumberOfElectionPackets:\t\t\t%d\n", Statistics->NumberOfElectionPackets);
+ printf("NumberOfGetBrowserServerListRequests:\t\t%d\n", Statistics->NumberOfGetBrowserServerListRequests);
+ printf("NumberOfMissedGetBrowserServerListRequests:\t%d\n", Statistics->NumberOfMissedGetBrowserServerListRequests);
+ printf("NumberOfDroppedServerAnnouncements:\t\t%d\n", Statistics->NumberOfMissedServerAnnouncements);
+ printf("NumberOfDroppedMailslotDatagrams:\t\t%d\n", Statistics->NumberOfMissedMailslotDatagrams);
+// printf("NumberOfFailedMailslotAllocations:\t\t%d\n", Statistics->NumberOfFailedMailslotAllocations);
+ printf("NumberOfFailedMailslotReceives:\t\t\t%d\n", Statistics->NumberOfFailedMailslotReceives);
+// printf("NumberOfFailedMailslotWrites:\t\t\t%d\n", Statistics->NumberOfFailedMailslotWrites);
+// printf("NumberOfFailedMailslotOpens:\t\t\t%d\n", Statistics->NumberOfFailedMailslotOpens);
+// printf("NumberOfFailedServerAnnounceAllocations:\t%d\n", Statistics->NumberOfFailedServerAnnounceAllocations);
+ printf("NumberOfMasterAnnouncements:\t\t\t%d\n", Statistics->NumberOfDuplicateMasterAnnouncements);
+ printf("NumberOfIllegalDatagrams:\t\t\t%s\n", format_dlword(Statistics->NumberOfIllegalDatagrams.HighPart, Statistics->NumberOfIllegalDatagrams.LowPart, Buffer));
+ }
+}
+
+#define DLWBUFSIZE 22 /* buffer big enough to represent a 64-bit unsigned int
+
+
+/*
+ * format_dlword --
+ *
+ * This function takes a 64-bit number and writes its base-10 representation
+ * into a string.
+ *
+ * Much magic occurs within this function, so beware. We do a lot of string-
+ * reversing and addition-by-hand in order to get it to work.
+ *
+ * ENTRY
+ * high - high 32 bits
+ * low - low 32 bits
+ * buf - buffer to put it into
+ *
+ * RETURNS
+ * pointer to buffer if successful
+ */
+
+CHAR * format_dlword(ULONG high, ULONG low, CHAR * buf)
+{
+ CHAR addend[DLWBUFSIZE]; /* REVERSED power of two */
+ CHAR copy[DLWBUFSIZE];
+ int i = 0;
+
+ _ultoa(low, buf, 10); /* the low part is easy */
+ _strrev(buf); /* and reverse it */
+
+ /* set up addend with rep. of 2^32 */
+ _ultoa(0xFFFFFFFF, addend, 10); /* 2^32 -1 */
+ _strrev(addend); /* reversed, and will stay this way */
+ revstr_add(addend, "1"); /* and add one == 2^32 */
+
+ /* addend will contain the reverse-ASCII base-10 rep. of 2^(i+32) */
+
+ /* now, we loop through each digit of the high longword */
+ while (TRUE) {
+ /* if this bit is set, add in its base-10 rep */
+ if (high & 1)
+ revstr_add(buf,addend);
+
+ /* move on to next bit */
+ high >>= 1;
+
+ /* if no more digits in high, bag out */
+ if (!high)
+ break;
+
+ /* we increment i, and double addend */
+ i++;
+ strcpy(copy, addend);
+ revstr_add(addend,copy); /* i.e. add it to itself */
+
+ }
+
+ _strrev(buf);
+ return buf;
+}
+
+
+
+/*
+ * revstr_add --
+ *
+ * This function will add together reversed ASCII representations of
+ * base-10 numbers.
+ *
+ * Examples: "2" + "2" = "4" "9" + "9" = "81"
+ *
+ * This handles arbitrarily large numbers.
+ *
+ * ENTRY
+ *
+ * source - number to add in
+ * target - we add source to this
+ *
+ * EXIT
+ * target - contains sum of entry values of source and target
+ *
+ */
+
+VOID
+revstr_add(CHAR FAR * target, CHAR FAR * source)
+{
+ register CHAR accum;
+ register CHAR target_digit;
+ unsigned int carrybit = 0;
+ unsigned int srcstrlen;
+ unsigned int i;
+
+ srcstrlen = strlen(source);
+
+ for (i = 0; (i < srcstrlen) || carrybit; ++i) {
+
+ /* add in the source digit */
+ accum = (i < srcstrlen) ? (CHAR) (source[i] - '0') : (CHAR) 0;
+
+ /* add in the target digit, or '0' if we hit null term */
+ target_digit = target[i];
+ accum += (target_digit) ? target_digit : '0';
+
+ /* add in the carry bit */
+ accum += (CHAR) carrybit;
+
+ /* do a carry out, if necessary */
+ if (accum > '9') {
+ carrybit = 1;
+ accum -= 10;
+ }
+ else
+ carrybit = 0;
+
+ /* if we're expanding the string, must put in a new null term */
+ if (!target_digit)
+ target[i+1] = '\0';
+
+ /* and write out the digit */
+ target[i] = accum;
+ }
+
+}
+
+VOID
+TruncateBowserLog()
+{
+ LMDR_REQUEST_PACKET RequestPacket;
+ DWORD BytesReturned;
+ HANDLE BrowserHandle;
+
+ RtlZeroMemory(&RequestPacket, sizeof(RequestPacket));
+
+ OpenBrowser(&BrowserHandle);
+
+ RequestPacket.Version = LMDR_REQUEST_PACKET_VERSION;
+
+ RequestPacket.Parameters.Debug.TruncateLog = TRUE;
+
+ if (!DeviceIoControl(BrowserHandle, IOCTL_LMDR_DEBUG_CALL,
+ &RequestPacket, sizeof(RequestPacket),
+ NULL, 0, &BytesReturned, NULL)) {
+ printf("Unable to truncate browser log: %ld\n", GetLastError());
+ }
+
+ CloseHandle(BrowserHandle);
+
+}
+
+VOID
+CloseBowserLog()
+{
+ LMDR_REQUEST_PACKET RequestPacket;
+ DWORD BytesReturned;
+ HANDLE BrowserHandle;
+
+ RtlZeroMemory(&RequestPacket, sizeof(RequestPacket));
+
+ OpenBrowser(&BrowserHandle);
+
+ RequestPacket.Version = LMDR_REQUEST_PACKET_VERSION;
+
+ RequestPacket.Parameters.Debug.CloseLog = TRUE;
+
+ if (!DeviceIoControl(BrowserHandle, IOCTL_LMDR_DEBUG_CALL,
+ &RequestPacket, sizeof(RequestPacket),
+ NULL, 0, &BytesReturned, NULL)) {
+ printf("Unable to close browser log: %ld\n", GetLastError());
+ }
+
+ CloseHandle(BrowserHandle);
+
+}
+
+VOID
+OpenBowserLog(PCHAR FileName)
+{
+
+ CHAR Buffer[sizeof(LMDR_REQUEST_PACKET)+4096];
+ PLMDR_REQUEST_PACKET RequestPacket = (PLMDR_REQUEST_PACKET)Buffer;
+ DWORD BytesReturned;
+ HANDLE BrowserHandle;
+ UNICODE_STRING UString;
+ UNICODE_STRING NtString;
+ ANSI_STRING AString;
+
+ RtlZeroMemory(RequestPacket, sizeof(Buffer));
+
+ OpenBrowser(&BrowserHandle);
+
+ RtlInitString(&AString, FileName);
+
+ UString.Buffer = RequestPacket->Parameters.Debug.TraceFileName;
+ UString.MaximumLength = 4096;
+
+ RtlAnsiStringToUnicodeString(&UString, &AString, TRUE );
+
+ if (!RtlDosPathNameToNtPathName_U( UString.Buffer, &NtString, NULL, NULL )) {
+ printf( "Invalid file name: %ws\n", UString.Buffer );
+ return;
+ }
+
+ RtlCopyMemory( RequestPacket->Parameters.Debug.TraceFileName,
+ NtString.Buffer,
+ NtString.MaximumLength );
+
+ RequestPacket->Version = LMDR_REQUEST_PACKET_VERSION;
+
+ RequestPacket->Parameters.Debug.OpenLog = TRUE;
+
+ if (!DeviceIoControl(BrowserHandle, IOCTL_LMDR_DEBUG_CALL,
+ RequestPacket, sizeof(Buffer),
+ NULL, 0, &BytesReturned, NULL)) {
+ printf("Unable to open browser log: %ld\n", GetLastError());
+ }
+
+ CloseHandle(BrowserHandle);
+
+}
+
+VOID
+SetBowserDebug(PCHAR DebugBits)
+{
+ LMDR_REQUEST_PACKET RequestPacket;
+ DWORD BytesReturned;
+ HANDLE BrowserHandle;
+ char *end;
+
+ RtlZeroMemory(&RequestPacket, sizeof(RequestPacket));
+
+ OpenBrowser(&BrowserHandle);
+
+ RequestPacket.Version = LMDR_REQUEST_PACKET_VERSION;
+
+ RequestPacket.Parameters.Debug.DebugTraceBits = strtoul( DebugBits, &end, 16 );
+
+ if (!DeviceIoControl(BrowserHandle, IOCTL_LMDR_DEBUG_CALL,
+ &RequestPacket, sizeof(RequestPacket),
+ NULL, 0, &BytesReturned, NULL)) {
+ printf("Unable to truncate browser log: %ld\n", GetLastError());
+ }
+
+ CloseHandle(BrowserHandle);
+}
+
+#define NAME_MIN_LENGTH 4
+#define NAME_LENGTH (CNLEN-NAME_MIN_LENGTH)
+
+VOID
+Populate(
+ IN BOOL PopulateDomains,
+ IN PCHAR Transport,
+ IN PCHAR Domain,
+#ifdef _CAIRO_
+ IN PCHAR EmulatedDomain,
+#endif
+ IN PCHAR NumberOfMachinesString,
+ IN PCHAR PeriodicityString OPTIONAL
+ )
+{
+
+ PSERVER_INFO_101 ServerInfo;
+ LPBYTE Buffer;
+ ULONG NumberOfMachines = strtoul(NumberOfMachinesString, NULL, 0);
+ ULONG Periodicity = (PeriodicityString == NULL ? 60000 : strtoul(PeriodicityString, NULL, 0));
+ ULONG ServerType;
+ WCHAR ServerComment[256];
+ WCHAR ComputerName[CNLEN+1];
+ CHAR ServerName[256];
+ DWORD VersionMajor;
+ DWORD VersionMinor;
+ BOOL UsedDefaultChar;
+ ULONG i;
+ BROWSE_ANNOUNCE_PACKET BrowseAnnouncement;
+ static char ServerCharacters[] = {"ABCDEFGHIJKLMNOPQRSTUVWXYZ 1234567890.-_"};
+ DWORD Seed;
+ NET_API_STATUS Status;
+
+
+ if (Periodicity == 0) {
+ Periodicity = 60000;
+ }
+
+ NetServerGetInfo(NULL, 101, &Buffer);
+
+ ServerInfo = (PSERVER_INFO_101 )Buffer;
+
+ ServerType = ServerInfo->sv101_type;
+
+ wcscpy(ServerComment, ServerInfo->sv101_comment);
+
+ wcscpy(ComputerName, ServerInfo->sv101_name);
+
+ VersionMajor = ServerInfo->sv101_version_major;
+
+ VersionMinor = ServerInfo->sv101_version_minor;
+
+ NetApiBufferFree(Buffer);
+
+ if (PopulateDomains) {
+ printf("Populating all domains on transport %s with %ld domains. Periodicity = %ld\n", Transport, NumberOfMachines, Periodicity);
+ } else {
+ printf("Populating workgroup %s on transport %s with %ld servers. Periodicity = %ld\n", Domain, Transport, NumberOfMachines, Periodicity);
+ }
+
+ Seed = time(&Seed);
+
+ for (i = 0 ; i < NumberOfMachines; i += 1) {
+ LONG NL1 = RtlRandom(&Seed) % (NAME_LENGTH-1);
+ LONG NameLength;
+ LONG NL2;
+ LONG j;
+
+ NL2 = NAME_LENGTH/2 - NL1;
+
+ NameLength = NAME_LENGTH/2 + NL2 + NAME_MIN_LENGTH;
+
+ for (j = 0; j < NameLength ; j += 1) {
+ ServerName[j] = ServerCharacters[RtlRandom(&Seed) % (sizeof(ServerCharacters) - 1)];
+ }
+
+ ServerName[j] = '\0';
+
+ //
+ // Build an announcement packet.
+ //
+
+ if (PopulateDomains) {
+ BrowseAnnouncement.BrowseType = WkGroupAnnouncement;
+ } else {
+ BrowseAnnouncement.BrowseType = HostAnnouncement;
+ }
+
+ BrowseAnnouncement.BrowseAnnouncement.UpdateCount = 0;
+
+ BrowseAnnouncement.BrowseAnnouncement.Periodicity = Periodicity;
+
+ strcpy(BrowseAnnouncement.BrowseAnnouncement.ServerName, ServerName);
+
+ BrowseAnnouncement.BrowseAnnouncement.VersionMajor = (UCHAR)VersionMajor;
+ BrowseAnnouncement.BrowseAnnouncement.VersionMinor = (UCHAR)VersionMinor;
+ BrowseAnnouncement.BrowseAnnouncement.Type = (ServerType & ~(SV_TYPE_BACKUP_BROWSER | SV_TYPE_MASTER_BROWSER));
+
+ if (PopulateDomains) {
+ WideCharToMultiByte(CP_OEMCP, 0,
+ ComputerName,
+ wcslen(ComputerName)+1,
+ BrowseAnnouncement.BrowseAnnouncement.Comment,
+ CNLEN+1,
+ "?",
+ &UsedDefaultChar
+ );
+ } else {
+
+ WideCharToMultiByte(CP_OEMCP, 0,
+ ServerComment,
+ wcslen(ServerComment)+1,
+ BrowseAnnouncement.BrowseAnnouncement.Comment,
+ LM20_MAXCOMMENTSZ+1,
+ "?",
+ &UsedDefaultChar
+ );
+ }
+
+ BrowseAnnouncement.BrowseAnnouncement.CommentPointer = NULL;
+
+ //
+ // Send the request
+ //
+
+ Status = SendDatagramA( Transport,
+#ifdef _CAIRO_
+ EmulatedDomain,
+#endif
+ Domain,
+ (PopulateDomains ? DomainAnnouncement : MasterBrowser),
+ &BrowseAnnouncement,
+ sizeof(BrowseAnnouncement));
+
+ if (Status != NERR_Success) {
+ printf("Unable to send datagram: %ld\n", Status);
+ }
+
+ Sleep(50);
+
+ }
+
+
+ return;
+}
+
+NET_API_STATUS
+GetBuildNumber(
+ LPWSTR Server,
+ LPWSTR BuildNumber
+ );
+
+NET_API_STATUS
+GetBrowserTransportList(
+ OUT PLMDR_TRANSPORT_LIST *TransportList
+ );
+
+
+NET_API_STATUS
+GetStatusForTransport(
+ IN BOOL Verbose,
+ IN PUNICODE_STRING Transport,
+ IN PUNICODE_STRING Domain
+ );
+
+
+ VOID
+BrowserStatus(
+ IN BOOL Verbose,
+ IN PCHAR Domain OPTIONAL
+ )
+{
+ NET_API_STATUS Status;
+ UNICODE_STRING DomainName;
+ PVOID Buffer;
+ PWKSTA_INFO_101 WkstaInfo;
+ PLMDR_TRANSPORT_LIST TransportList, TransportEntry;
+
+ if (Domain == NULL) {
+ PWCHAR DomainBuffer = NULL;
+ UNICODE_STRING TDomainName;
+ Status = NetWkstaGetInfo(NULL, 101, (LPBYTE *)&Buffer);
+
+ if (Status != NERR_Success) {
+ printf("Unable to retrieve workstation information: %s\n", get_error_text(Status));
+ exit(Status);
+ }
+ WkstaInfo = (PWKSTA_INFO_101 )Buffer;
+
+ DomainBuffer = malloc((wcslen(WkstaInfo->wki101_langroup)+1)*sizeof(WCHAR));
+
+ DomainName.Buffer = DomainBuffer;
+
+ DomainName.MaximumLength = (wcslen(WkstaInfo->wki101_langroup)+1)*sizeof(WCHAR);
+
+ RtlInitUnicodeString(&TDomainName, WkstaInfo->wki101_langroup);
+
+ RtlCopyUnicodeString(&DomainName, &TDomainName);
+
+ NetApiBufferFree(Buffer);
+ } else {
+ ANSI_STRING AString;
+
+ RtlInitAnsiString(&AString, Domain);
+
+ RtlAnsiStringToUnicodeString(&DomainName, &AString, TRUE);
+ }
+
+ //
+ // We now know the domain to query. Iterate through the transports and
+ // get status for each of them.
+ //
+
+ Status = GetBrowserTransportList(&TransportList);
+
+ if (Status != NERR_Success) {
+ printf("Unable to retrieve transport list: %s\n", get_error_text(Status));
+ exit(Status);
+ }
+
+ TransportEntry = TransportList;
+
+ while (TransportEntry != NULL) {
+ UNICODE_STRING TransportName;
+
+ TransportName.Buffer = TransportEntry->TransportName;
+ TransportName.Length = (USHORT)TransportEntry->TransportNameLength;
+ TransportName.MaximumLength = (USHORT)TransportEntry->TransportNameLength;
+
+ Status = GetStatusForTransport(Verbose, &TransportName, &DomainName);
+
+ if (TransportEntry->NextEntryOffset == 0) {
+ TransportEntry = NULL;
+ } else {
+ TransportEntry = (PLMDR_TRANSPORT_LIST)((PCHAR)TransportEntry+TransportEntry->NextEntryOffset);
+ }
+ }
+
+
+ NetApiBufferFree(TransportList);
+
+ exit(0);
+}
+
+NET_API_STATUS
+GetBrowserTransportList(
+ OUT PLMDR_TRANSPORT_LIST *TransportList
+ )
+
+/*++
+
+Routine Description:
+
+ This routine returns the list of transports bound into the browser.
+
+Arguments:
+
+ OUT PLMDR_TRANSPORT_LIST *TransportList - Transport list to return.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+
+{
+
+ NET_API_STATUS Status;
+ HANDLE BrowserHandle;
+ LMDR_REQUEST_PACKET RequestPacket;
+
+ Status = OpenBrowser(&BrowserHandle);
+
+ if (Status != NERR_Success) {
+ return Status;
+ }
+
+ RequestPacket.Version = LMDR_REQUEST_PACKET_VERSION;
+
+ RequestPacket.Type = EnumerateXports;
+
+ RtlInitUnicodeString(&RequestPacket.TransportName, NULL);
+#ifdef _CAIRO_
+ RtlInitUnicodeString(&RequestPacket.EmulatedDomainName, NULL);
+#endif
+
+ Status = DeviceControlGetInfo(
+ BrowserHandle,
+ IOCTL_LMDR_ENUMERATE_TRANSPORTS,
+ &RequestPacket,
+ sizeof(RequestPacket),
+ (PVOID *)TransportList,
+ 0xffffffff,
+ 4096,
+ NULL);
+
+ NtClose(BrowserHandle);
+
+ return Status;
+}
+
+NET_API_STATUS
+GetStatusForTransport(
+ IN BOOL Verbose,
+ IN PUNICODE_STRING Transport,
+ IN PUNICODE_STRING Domain
+ )
+{
+ WCHAR MasterName[256];
+ WCHAR MasterServerName[256+2];
+ NET_API_STATUS Status;
+ PVOID Buffer;
+ PSERVER_INFO_101 ServerInfo;
+ DWORD EntriesInList;
+ DWORD TotalEntries;
+ DWORD BrowserListLength;
+ PWSTR *BrowserList;
+ DWORD i;
+ DWORD NumberNTASMachines = 0;
+ DWORD NumberOS2DCs = 0;
+ DWORD NumberWfWMachines = 0;
+ DWORD NumberOfNTMachines = 0;
+ DWORD NumberWfWBrowsers = 0;
+ DWORD NumberOfOs2Machines = 0;
+ DWORD NumberOfBrowsers = 0;
+ DWORD NumberOfBackupBrowsers = 0;
+ DWORD NumberOfMasterBrowsers = 0;
+ WCHAR BuildNumber[512];
+
+ printf("\n\nStatus for domain %s on transport %s\n", UnicodeToPrintfString(Domain->Buffer), UnicodeToPrintfString2(Transport->Buffer));
+
+ Status = GetBrowserServerList(Transport, Domain->Buffer, &BrowserList, &BrowserListLength, TRUE);
+
+ if (Status == NERR_Success) {
+
+ printf(" Browsing is active on domain.\n");
+
+ } else {
+ printf(" Browsing is NOT active on domain.\n", Status);
+
+ Status = GetNetBiosMasterName(
+ Transport->Buffer,
+ Domain->Buffer,
+ MasterName,
+ NULL);
+
+ if (Status == NERR_Success) {
+
+ wcscpy(MasterServerName, L"\\\\");
+ wcscat(MasterServerName, MasterName);
+
+ printf(" Master browser name is held by: %s\n", UnicodeToPrintfString(MasterName));
+
+ Status = GetBuildNumber(MasterServerName, BuildNumber);
+
+ if (Status == NERR_Success) {
+ printf(" Master browser is running build %s\n", UnicodeToPrintfString(BuildNumber));
+ } else {
+ PSERVER_INFO_101 pSV101;
+
+ printf(" Unable to determine build of browser master: %d\n", Status);
+
+ Status = NetServerGetInfo(MasterServerName, 101, (LPBYTE *)&pSV101);
+
+ if (Status != NERR_Success) {
+ printf(" Unable to determine server information for browser master: %d\n", Status);
+
+ return Status;
+ }
+
+ printf(" %-16.16s. Version:%2.2d.%2.2d Flags: %lx ", UnicodeToPrintfString(MasterServerName), pSV101->sv101_version_major, pSV101->sv101_version_minor, pSV101->sv101_type);
+
+ if (pSV101->sv101_type & SV_TYPE_WFW) {
+ printf("WFW ");
+ }
+
+ if (pSV101->sv101_type & SV_TYPE_NT) {
+ printf("NT ");
+ }
+
+ if (pSV101->sv101_type & SV_TYPE_POTENTIAL_BROWSER) {
+ printf("POTENTIAL ");
+ }
+
+ if (pSV101->sv101_type & SV_TYPE_BACKUP_BROWSER) {
+ printf("BACKUP ");
+ }
+
+ if (pSV101->sv101_type & SV_TYPE_MASTER_BROWSER) {
+ printf("MASTER ");
+ }
+
+ if (pSV101->sv101_type & SV_TYPE_DOMAIN_CTRL) {
+ printf("CONTROLLER ");
+ }
+
+ if (pSV101->sv101_type & SV_TYPE_DOMAIN_BAKCTRL) {
+ printf("BACKUP CONTROLLER ");
+ }
+
+ if (pSV101->sv101_type & SV_TYPE_SERVER_NT) {
+ printf("SERVER ");
+ }
+
+
+ }
+
+ } else {
+ printf(" Master name cannot be determined from GetAdapterStatus.\n");
+ }
+ return Status;
+ }
+
+ Status = GetNetBiosMasterName(
+ Transport->Buffer,
+ Domain->Buffer,
+ MasterName,
+ NULL);
+
+ if (Status == NERR_Success) {
+
+ wcscpy(MasterServerName, L"\\\\");
+ wcscat(MasterServerName, MasterName);
+
+ printf(" Master browser name is: %s\n", UnicodeToPrintfString(MasterName));
+
+
+ } else {
+ printf(" Master name cannot be determined from GetAdapterStatus. Using %s\n", UnicodeToPrintfString(BrowserList[0]));
+
+ wcscpy(MasterServerName, BrowserList[0]);
+ wcscpy(MasterName, (BrowserList[0])+2);
+ }
+
+ //
+ // Print the build number or whatever else you can find out about the master
+ //
+
+ Status = GetBuildNumber(MasterServerName, BuildNumber);
+
+ if (Status == NERR_Success) {
+ printf(" Master browser is running build %s\n", UnicodeToPrintfString(BuildNumber));
+ } else {
+ PSERVER_INFO_101 pSV101;
+
+ printf(" Unable to determine build of browser master: %d\n", Status);
+
+ Status = NetServerGetInfo(MasterServerName, 101, (LPBYTE *)&pSV101);
+
+ if (Status != NERR_Success) {
+ printf(" Unable to determine server information for browser master: %d\n", Status);
+ }
+
+ if (Status == NERR_Success) {
+
+ printf(" \\\\%-16.16s. Version:%2.2d.%2.2d Flags: %lx ", UnicodeToPrintfString(MasterServerName), pSV101->sv101_version_major, pSV101->sv101_version_minor, pSV101->sv101_type);
+
+ if (pSV101->sv101_type & SV_TYPE_WFW) {
+ printf("WFW ");
+ }
+
+ if (pSV101->sv101_type & SV_TYPE_NT) {
+ printf("NT ");
+ }
+
+ if (pSV101->sv101_type & SV_TYPE_POTENTIAL_BROWSER) {
+ printf("POTENTIAL ");
+ }
+
+ if (pSV101->sv101_type & SV_TYPE_BACKUP_BROWSER) {
+ printf("BACKUP ");
+ }
+
+ if (pSV101->sv101_type & SV_TYPE_MASTER_BROWSER) {
+ printf("MASTER ");
+ }
+
+ if (pSV101->sv101_type & SV_TYPE_DOMAIN_CTRL) {
+ printf("CONTROLLER ");
+ }
+
+ if (pSV101->sv101_type & SV_TYPE_DOMAIN_BAKCTRL) {
+ printf("BACKUP CONTROLLER ");
+ }
+
+ if (pSV101->sv101_type & SV_TYPE_SERVER_NT) {
+ printf("SERVER ");
+ }
+
+ printf("\n");
+ }
+ }
+
+ printf(" %ld backup servers retrieved from master %s\n", BrowserListLength, UnicodeToPrintfString(MasterName));
+
+ for (i = 0; i < BrowserListLength ; i++ ) {
+ printf(" %s\n", UnicodeToPrintfString(BrowserList[i]));
+ }
+
+
+ Status = RxNetServerEnum(MasterServerName,
+ Transport->Buffer,
+ 101,
+ (LPBYTE *)&Buffer,
+ 0xffffffff, // PreferedMaxLength
+ &EntriesInList,
+ &TotalEntries,
+ SV_TYPE_ALL,
+// Domain->Buffer,
+ NULL,
+ NULL
+ );
+ if (Status != NERR_Success) {
+ printf(" Unable to retrieve server list from %s: %ld\n", UnicodeToPrintfString(MasterName), Status);
+ return Status;
+ } else {
+
+ printf(" There are %ld servers in domain %s on transport %s\n", EntriesInList, UnicodeToPrintfString(Domain->Buffer), UnicodeToPrintfString2(Transport->Buffer));
+
+ if (Verbose) {
+ if (EntriesInList != 0) {
+ ServerInfo = (PSERVER_INFO_101)Buffer;
+
+ for (i = 0 ; i < EntriesInList ; i += 1) {
+ if (ServerInfo->sv101_type & (SV_TYPE_DOMAIN_BAKCTRL | SV_TYPE_DOMAIN_CTRL)) {
+
+ if (ServerInfo->sv101_type & SV_TYPE_NT) {
+ NumberNTASMachines += 1;
+ } else {
+ NumberOS2DCs += 1;
+ }
+
+ }
+
+ if (ServerInfo->sv101_type & SV_TYPE_WFW) {
+ NumberWfWMachines += 1;
+
+ if (ServerInfo->sv101_type & (SV_TYPE_BACKUP_BROWSER | SV_TYPE_POTENTIAL_BROWSER | SV_TYPE_MASTER_BROWSER)) {
+ NumberWfWBrowsers += 1;
+ }
+ } else if (ServerInfo->sv101_type & SV_TYPE_NT) {
+ NumberOfNTMachines += 1;
+ } else {
+ NumberOfOs2Machines += 1;
+ }
+
+ if (ServerInfo->sv101_type & (SV_TYPE_BACKUP_BROWSER | SV_TYPE_POTENTIAL_BROWSER | SV_TYPE_MASTER_BROWSER)) {
+ NumberOfBrowsers += 1;
+
+ if (ServerInfo->sv101_type & SV_TYPE_BACKUP_BROWSER) {
+ NumberOfBackupBrowsers += 1;
+ }
+
+ if (ServerInfo->sv101_type & SV_TYPE_MASTER_BROWSER) {
+ NumberOfMasterBrowsers += 1;
+ }
+ }
+
+ ServerInfo += 1;
+ }
+
+ printf(" Number of NT Advanced Servers:\t\t\t%ld\n", NumberNTASMachines);
+ printf(" Number of OS/2 Domain controllers:\t\t%ld\n", NumberOS2DCs);
+ printf(" Number of Windows For Workgroups machines:\t%ld\n", NumberWfWMachines);
+ printf(" Number of Os/2 machines:\t\t\t%ld\n", NumberOfOs2Machines);
+ printf(" Number of NT machines:\t\t\t\t%ld\n", NumberOfNTMachines);
+ printf("\n");
+ printf(" Number of active WfW browsers:\t\t\t%ld\n", NumberWfWBrowsers);
+ printf(" Number of browsers:\t\t\t\t%ld\n", NumberOfBrowsers);
+ printf(" Number of backup browsers:\t\t\t%ld\n", NumberOfBackupBrowsers);
+ printf(" Number of master browsers:\t\t\t%ld\n", NumberOfMasterBrowsers);
+
+ }
+ }
+
+ }
+ Status = RxNetServerEnum(MasterServerName,
+ Transport->Buffer,
+ 101,
+ (LPBYTE *)&Buffer,
+ 0xffffffff, // PreferedMaxLength
+ &EntriesInList,
+ &TotalEntries,
+ SV_TYPE_DOMAIN_ENUM,
+// Domain->Buffer,
+ NULL,
+ NULL
+ );
+ if ( Status == ERROR_MORE_DATA ) {
+ printf(" Only %ld out of %ld domains could be returned\n", EntriesInList, TotalEntries );
+ } else if (Status != NERR_Success) {
+ printf(" Unable to retrieve server list from %s: %ld\n", UnicodeToPrintfString(MasterName), Status);
+ return Status;
+ }
+ printf(" There are %ld domains in domain %s on transport %s\n", EntriesInList, UnicodeToPrintfString(Domain->Buffer), UnicodeToPrintfString2(Transport->Buffer));
+
+ return NERR_Success;
+}
+
+#define BUILD_NUMBER_KEY L"SOFTWARE\\MICROSOFT\\WINDOWS NT\\CURRENTVERSION"
+#define BUILD_NUMBER_BUFFER_LENGTH 80
+
+NET_API_STATUS
+GetBuildNumber(
+ LPWSTR Server,
+ LPWSTR BuildNumber
+ )
+{
+ HKEY RegKey;
+ HKEY RegKeyBuildNumber;
+ DWORD WinStatus;
+ DWORD BuildNumberLength;
+ DWORD KeyType;
+
+ WinStatus = RegConnectRegistry(Server, HKEY_LOCAL_MACHINE,
+ &RegKey);
+ if (WinStatus == RPC_S_SERVER_UNAVAILABLE) {
+// printf("%15ws no longer accessable", Server+2);
+ return(WinStatus);
+ }
+ else if (WinStatus != ERROR_SUCCESS) {
+ printf("Could not connect to registry, error = %d", WinStatus);
+ return(WinStatus);
+ }
+
+ WinStatus = RegOpenKeyEx(RegKey, BUILD_NUMBER_KEY,0, KEY_READ,
+ & RegKeyBuildNumber);
+ if (WinStatus != ERROR_SUCCESS) {
+ printf("Could not open key in registry, error = %d", WinStatus);
+ return(WinStatus);
+ }
+
+ BuildNumberLength = BUILD_NUMBER_BUFFER_LENGTH * sizeof(WCHAR);
+
+ WinStatus = RegQueryValueEx(RegKeyBuildNumber, L"CurrentBuildNumber",
+ (LPDWORD) NULL, & KeyType, (LPBYTE) BuildNumber, & BuildNumberLength);
+
+ if (WinStatus != ERROR_SUCCESS) {
+
+ WinStatus = RegQueryValueEx(RegKeyBuildNumber, L"CurrentBuild",
+ (LPDWORD) NULL, & KeyType, (LPBYTE) BuildNumber, & BuildNumberLength);
+ if (WinStatus != ERROR_SUCCESS) {
+ RegCloseKey(RegKeyBuildNumber);
+ RegCloseKey(RegKey);
+ return WinStatus;
+ }
+ }
+
+ WinStatus = RegCloseKey(RegKeyBuildNumber);
+
+ if (WinStatus != ERROR_SUCCESS) {
+ printf("Could not close registry key, error = %d", WinStatus);
+ }
+
+ WinStatus = RegCloseKey(RegKey);
+
+ if (WinStatus != ERROR_SUCCESS) {
+ printf("Could not close registry connection, error = %d", WinStatus);
+ }
+
+ return(WinStatus);
+}
+ NET_API_STATUS
+GetNetBiosPdcName(
+ IN LPWSTR NetworkName,
+ IN LPWSTR PrimaryDomain,
+ OUT LPWSTR MasterName
+ );
+
+VOID
+GetPdc(
+ IN PCHAR Transport,
+ IN PCHAR Domain
+ )
+{
+ NET_API_STATUS Status;
+ UNICODE_STRING TransportName;
+ ANSI_STRING AString;
+ WCHAR MasterName[256];
+ UNICODE_STRING DomainName;
+
+ qualify_transport(Transport, &TransportName) ;
+
+ RtlInitString(&AString, Domain);
+ RtlAnsiStringToUnicodeString(&DomainName, &AString, TRUE);
+
+ Status = GetNetBiosPdcName(TransportName.Buffer, DomainName.Buffer, MasterName);
+
+ if (Status != NERR_Success) {
+ printf("Unable to get PDC: %s\n", get_error_text(Status));
+ exit(1);
+ }
+
+ printf("PDC: %s\n", UnicodeToPrintfString(MasterName));
+
+}
+ NET_API_STATUS
+GetNetBiosPdcName(
+ IN LPWSTR NetworkName,
+ IN LPWSTR PrimaryDomain,
+ OUT LPWSTR MasterName
+ )
+{
+ CCHAR LanaNum;
+ NCB AStatNcb;
+ struct {
+ ADAPTER_STATUS AdapterInfo;
+ NAME_BUFFER Names[32];
+ } AdapterStatus;
+ WORD i;
+ CHAR remoteName[CNLEN+1];
+ NET_API_STATUS Status;
+ BOOL UsedDefaultChar;
+
+ Status = BrGetLanaNumFromNetworkName(NetworkName, &LanaNum);
+
+ if (Status != NERR_Success) {
+ return Status;
+ }
+
+ ClearNcb(&AStatNcb)
+
+ AStatNcb.ncb_command = NCBRESET;
+ AStatNcb.ncb_lsn = 0; // Request resources
+ AStatNcb.ncb_lana_num = LanaNum;
+ AStatNcb.ncb_callname[0] = 0; // 16 sessions
+ AStatNcb.ncb_callname[1] = 0; // 16 commands
+ AStatNcb.ncb_callname[2] = 0; // 8 names
+ AStatNcb.ncb_callname[3] = 0; // Don't want the reserved address
+ Netbios( &AStatNcb );
+
+ ClearNcb( &AStatNcb );
+
+ if (WideCharToMultiByte( CP_OEMCP, 0,
+ PrimaryDomain,
+ -1,
+ remoteName,
+ sizeof(remoteName),
+ "?",
+ &UsedDefaultChar) == 0) {
+ return GetLastError();
+ }
+
+ //
+ // Uppercase the remote name.
+ //
+
+ _strupr(remoteName);
+
+ AStatNcb.ncb_command = NCBASTAT;
+
+ RtlCopyMemory( AStatNcb.ncb_callname, remoteName, strlen(remoteName));
+
+ AStatNcb.ncb_callname[15] = PRIMARY_CONTROLLER_SIGNATURE;
+
+ AStatNcb.ncb_lana_num = LanaNum;
+ AStatNcb.ncb_length = sizeof( AdapterStatus );
+ AStatNcb.ncb_buffer = (CHAR *)&AdapterStatus;
+ Netbios( &AStatNcb );
+
+ if ( AStatNcb.ncb_retcode == NRC_GOODRET ) {
+ for ( i=0 ; i < AdapterStatus.AdapterInfo.name_count ; i++ ) {
+ if (AdapterStatus.Names[i].name[NCBNAMSZ-1] == SERVER_SIGNATURE) {
+// LPWSTR SpacePointer;
+ DWORD j;
+
+ if (MultiByteToWideChar(CP_OEMCP,
+ 0,
+ AdapterStatus.Names[i].name,
+ CNLEN,
+ MasterName,
+ CNLEN) == 0) {
+ return(GetLastError());
+ }
+
+ for (j = CNLEN - 1; j ; j -= 1) {
+ if (MasterName[j] != L' ') {
+ MasterName[j+1] = UNICODE_NULL;
+ break;
+ }
+ }
+
+ return NERR_Success;
+ }
+ }
+ } else {
+ return AStatNcb.ncb_retcode;
+ }
+}
+
+VOID
+DisplayServerInfo101(
+ PSERVER_INFO_101 Server,
+ BOOL DomainEnumeration
+ )
+{
+ DWORD MajorVersion, MinorVersion ;
+ DWORD CharactersPrinted = 0;
+
+ if ( DomainEnumeration ) {
+ printf( "%-16.16ws", Server->sv101_name);
+ CharactersPrinted += 16;
+ } else {
+ printf( "\\\\%-16.16ws", Server->sv101_name);
+ CharactersPrinted += 18;
+ }
+
+ printf(" %s",
+ (Server->sv101_platform_id == PLATFORM_ID_DOS ? "DOS" :
+ (Server->sv101_platform_id == PLATFORM_ID_OS2 ?
+ ((Server->sv101_type & SV_TYPE_WINDOWS) ? "W95" :
+ ((Server->sv101_type & SV_TYPE_WFW) ? "WFW": "OS2" )) :
+ (Server->sv101_platform_id == PLATFORM_ID_NT ? "NT " :
+ "Unk") ) ) );
+ CharactersPrinted += 5;
+
+ MajorVersion = Server->sv101_version_major ;
+ MinorVersion = Server->sv101_version_minor ;
+ if ((MajorVersion == 1) && (MinorVersion >= 50)) {
+ printf(" %2.2d.%2.2d", MajorVersion+2, MinorVersion-40);
+ } else {
+ printf(" %2.2d.%2.2d", MajorVersion, MinorVersion);
+ }
+ CharactersPrinted += 5;
+
+ CharactersPrinted += display_sv_bits(Server->sv101_type);
+
+ if ( Server->sv101_comment != NULL && wcslen(Server->sv101_comment) > 0 ) {
+ printf( " " );
+ CharactersPrinted ++;
+
+ while ( CharactersPrinted < 48 ) {
+ printf( " " );
+ CharactersPrinted ++;
+ }
+ while ( CharactersPrinted % 4 != 0 ) {
+ printf( " " );
+ CharactersPrinted ++;
+ }
+
+ printf( "%ws", Server->sv101_comment );
+ }
+
+}
+
+//
+// display server bits as defined in BitsToStringTable
+//
+// Returns the number of characters printed.
+
+DWORD
+display_sv_bits(DWORD dwBits)
+{
+ BIT_NAME *lpEntry = BitToStringTable ;
+ BOOL fFirst = TRUE ;
+ DWORD CharactersPrinted = 0;
+
+ printf(" (") ;
+ CharactersPrinted += 2;
+ while (1)
+ {
+ if (lpEntry->dwValue & dwBits)
+ {
+ if (lpEntry != BitToStringTable && !fFirst) {
+ printf(",") ;
+ CharactersPrinted += 1;
+ }
+
+ dwBits &= ~lpEntry->dwValue;
+ printf("%s",lpEntry->lpString) ;
+ CharactersPrinted += strlen(lpEntry->lpString);
+ fFirst = FALSE ;
+ }
+ lpEntry++ ;
+ if ( !(lpEntry->dwValue) ) {
+ dwBits &= ~(SV_TYPE_DOMAIN_ENUM|SV_TYPE_LOCAL_LIST_ONLY);
+ if ( dwBits != 0 ) {
+ if ( !fFirst ) {
+ printf(",") ;
+ CharactersPrinted += 1;
+ }
+ printf( "%8.8X", dwBits );
+ CharactersPrinted += 8;
+ }
+ printf(")") ;
+ CharactersPrinted += 1;
+ break ;
+ }
+ }
+ return CharactersPrinted;
+}
+
+//
+// map an error number to its error message string. note, uses static,
+// not reentrant.
+//
+CHAR *
+get_error_text(DWORD dwErr)
+{
+ static CHAR text[512] ;
+ WORD err ;
+ WORD msglen ;
+
+ memset(text,0, sizeof(text));
+
+ //
+ // get error message
+ //
+ err = DosGetMessage(NULL,
+ 0,
+ text,
+ sizeof(text),
+ (WORD)dwErr,
+ (dwErr<NERR_BASE)||(dwErr>MAX_LANMAN_MESSAGE_ID) ?
+ TEXT("BASE"):TEXT("NETMSG"),
+ &msglen) ;
+
+ if (err != NERR_Success)
+ {
+ // use number instead. if looks like NTSTATUS then use hex.
+ sprintf(text, (dwErr & 0xC0000000)?"(%lx)":"(%ld)", dwErr) ;
+ }
+
+ return text ;
+}
+
+BOOL
+look_for_help(int argc, char **argv)
+{
+ int i ;
+
+ for (i = 2; i < argc; i++)
+ {
+ if (_stricmp(argv[i],"/help") == 0)
+ return TRUE ;
+ if (_stricmp(argv[i],"-help") == 0)
+ return TRUE ;
+ if (strcmp(argv[i],"/?") == 0)
+ return TRUE ;
+ if (strcmp(argv[i],"-?") == 0)
+ return TRUE ;
+ }
+ return FALSE ;
+}
+
+CHAR
+PrintfBuffer[256];
+
+PCHAR
+UnicodeToPrintfString(
+ PWCHAR WideChar
+ )
+{
+ UNICODE_STRING UString;
+ ANSI_STRING AString;
+ AString.Buffer = PrintfBuffer;
+ AString.MaximumLength = sizeof(PrintfBuffer);
+ RtlInitUnicodeString(&UString, WideChar);
+
+ RtlUnicodeStringToOemString(&AString, &UString, FALSE);
+
+ return PrintfBuffer;
+}
+
+
+CHAR
+PrintfBuffer2[256];
+
+PCHAR
+UnicodeToPrintfString2(
+ PWCHAR WideChar
+ )
+{
+ UNICODE_STRING UString;
+ ANSI_STRING AString;
+
+ AString.Buffer = PrintfBuffer2;
+
+ AString.MaximumLength = sizeof(PrintfBuffer2);
+
+ RtlInitUnicodeString(&UString, WideChar);
+
+ RtlUnicodeStringToOemString(&AString, &UString, FALSE);
+
+ return PrintfBuffer2;
+}
+
+ VOID
+GetWinsServer(
+ IN PCHAR Transport
+ )
+{
+ NET_API_STATUS Status;
+ UNICODE_STRING TransportName;
+ LPTSTR PrimaryWinsServerAddress;
+ LPTSTR SecondaryWinsServerAddress;
+
+ qualify_transport(Transport, &TransportName) ;
+
+ Status = BrGetWinsServerName(&TransportName, &PrimaryWinsServerAddress, &SecondaryWinsServerAddress);
+
+ if (Status != NERR_Success) {
+ printf("Unable to query WINS server address: %ld\n", Status);
+ exit(1);
+ }
+
+ printf("Primary Wins server address: %ws\n", PrimaryWinsServerAddress);
+ printf("Secondary Wins server address: %ws\n", SecondaryWinsServerAddress);
+
+ exit(0);
+}
+
+ VOID
+GetDomainList(
+ IN PCHAR IpAddress
+ )
+{
+ NET_API_STATUS Status;
+ PSERVER_INFO_101 WinsServerList;
+ DWORD EntriesInList;
+ DWORD TotalEntriesInList;
+ UNICODE_STRING IpAddressString;
+ ANSI_STRING IpAddressAString;
+ DWORD i;
+
+ RtlInitString(&IpAddressAString, IpAddress);
+
+ RtlAnsiStringToUnicodeString(&IpAddressString, &IpAddressAString, TRUE);
+
+ Status = BrQuerySpecificWinsServer(IpAddressString.Buffer, &WinsServerList, &EntriesInList, &TotalEntriesInList);
+
+ if (Status != NERR_Success) {
+ printf("Unable to query domain list from WINS server: %ld\n", Status);
+ exit(1);
+ }
+
+ PrepareServerListForMerge( WinsServerList, 101, EntriesInList );
+
+ for (i = 0; i < EntriesInList ; i ++ ) {
+ printf("%-16.16s\n", UnicodeToPrintfString(WinsServerList[i].sv101_name));
+ }
+
+ exit(0);
+}
+
+NET_API_STATUS
+BrMapStatus(
+ IN NTSTATUS Status
+ )
+{
+ return RtlNtStatusToDosError(Status);
+}
+
+
+#ifdef _CAIRO_
+VOID
+EnumEmulatedDomains(
+ )
+/*++
+
+Routine Description:
+
+ Enumerate emulated domains.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NET_API_STATUS NetStatus;
+ LPWSTR EmulatedDomainName;
+ LPWSTR EmulatedComputerName;
+ DWORD RoleBits;
+
+ PBROWSER_EMULATED_DOMAIN Domains;
+ DWORD EntriesRead;
+ DWORD i;
+
+ //
+ // Enumerate the emulated domains.
+ //
+
+ NetStatus = I_BrowserQueryEmulatedDomains(
+ NULL,
+ &Domains,
+ &EntriesRead );
+
+ if ( NetStatus != NERR_Success ) {
+ printf( "Can't enumerate EmulatedDomains: %s\n", get_error_text(NetStatus) );
+ return;
+ }
+
+ if ( EntriesRead == 0 ) {
+ printf( "There are no emulated domains\n" );
+ return;
+ }
+
+ //
+ // Print the enumerated information
+ //
+
+ for ( i=0 ; i<EntriesRead; i++ ) {
+ printf( "%-16.16ws %3.3s \\\\%-16.16ws\n",
+ Domains[i].DomainName,
+ (Domains[i].Role & BROWSER_ROLE_PDC) ? "PDC" : "BDC",
+ Domains[i].EmulatedServerName );
+ }
+
+
+}
+
+
+VOID
+SetEmulatedDomain(
+ IN PCHAR EmulatedDomain,
+ IN PCHAR Role,
+ IN PCHAR EmulatedComputer
+ )
+/*++
+
+Routine Description:
+
+ Create and/or set role on emulated domain
+
+Arguments:
+
+ EmulatedDomain - Emulated Domain name.
+
+ Role - Role this machine plays in the domain.
+
+ EmulatedComputerName - Name of this computer in the emulated domain.
+ (Need only be specified when the domain is being created.)
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+
+{
+ NET_API_STATUS NetStatus;
+ LPWSTR EmulatedDomainName;
+ LPWSTR EmulatedComputerName;
+ DWORD RoleBits;
+
+ //
+ // Comvert strings to unicode.
+ //
+
+ EmulatedDomainName = NetpAllocWStrFromStr( EmulatedDomain );
+ if ( EmulatedComputer != NULL ) {
+ EmulatedComputerName = NetpAllocWStrFromStr( EmulatedComputer );
+ } else {
+ EmulatedComputerName = NULL;
+ }
+
+ //
+ // Convert Role to binary
+ //
+
+ if ( _stricmp( Role, "PDC") == 0 ) {
+ RoleBits = BROWSER_ROLE_PDC;
+ } else if ( _stricmp( Role, "BDC") == 0 ) {
+ RoleBits = BROWSER_ROLE_BDC;
+ } else if ( _strnicmp( Role, "DELETE", 3) == 0 ) {
+ RoleBits = 0;
+ } else {
+ printf( "Invalid Role: %s\n\n", Role );
+ CommandUsage(BROWSER_DEBUG_SET_EMULATEDDOMAIN);
+ return;
+ }
+
+ NetStatus = I_BrowserSetNetlogonState(
+ NULL,
+ EmulatedDomainName,
+ EmulatedComputerName,
+ RoleBits );
+
+ if ( NetStatus != NERR_Success ) {
+ printf( "Can't set domain role: %s\n", get_error_text(NetStatus));
+ }
+
+}
+#endif
diff --git a/private/net/svcdlls/browser/client/browdeb.rc b/private/net/svcdlls/browser/client/browdeb.rc
new file mode 100644
index 000000000..57657bae7
--- /dev/null
+++ b/private/net/svcdlls/browser/client/browdeb.rc
@@ -0,0 +1,12 @@
+#include <windows.h>
+#include <ntverp.h>
+
+#define VER_FILETYPE VFT_APP
+#define VER_FILESUBTYPE VFT2_UNKNOWN
+#define VER_FILEDESCRIPTION_STR "Microsoft\256 Browser Debug Utility"
+
+#define VER_INTERNALNAME_STR "browdeb.exe"
+#define VER_ORIGINALFILENAME_STR "browdeb.exe"
+
+#include <common.ver>
+
diff --git a/private/net/svcdlls/browser/client/browstat.c b/private/net/svcdlls/browser/client/browstat.c
new file mode 100644
index 000000000..691736192
--- /dev/null
+++ b/private/net/svcdlls/browser/client/browstat.c
@@ -0,0 +1,3 @@
+#define _PSS_RELEASE
+
+#include "browdeb.c"
diff --git a/private/net/svcdlls/browser/client/browstat.rc b/private/net/svcdlls/browser/client/browstat.rc
new file mode 100644
index 000000000..227d967aa
--- /dev/null
+++ b/private/net/svcdlls/browser/client/browstat.rc
@@ -0,0 +1,12 @@
+#include <windows.h>
+#include <ntverp.h>
+
+#define VER_FILETYPE VFT_APP
+#define VER_FILESUBTYPE VFT2_UNKNOWN
+#define VER_FILEDESCRIPTION_STR "Microsoft\256 Browser Statistics Utility"
+
+#define VER_INTERNALNAME_STR "browstat.exe"
+#define VER_ORIGINALFILENAME_STR "browstat.exe"
+
+#include <common.ver>
+
diff --git a/private/net/svcdlls/browser/client/browstub.c b/private/net/svcdlls/browser/client/browstub.c
new file mode 100644
index 000000000..f879a9ed8
--- /dev/null
+++ b/private/net/svcdlls/browser/client/browstub.c
@@ -0,0 +1,1493 @@
+/*++
+
+Copyright (c) 1991-1992 Microsoft Corporation
+
+Module Name:
+
+ wksstub.c
+
+Abstract:
+
+ Client stubs of the Browser service APIs.
+
+Author:
+
+ Rita Wong (ritaw) 10-May-1991
+ Larry Osterman (LarryO) 23-Mar-1992
+
+Environment:
+
+ User Mode - Win32
+
+Revision History:
+
+ 18-Jun-1991 JohnRo
+ Remote NetUse APIs to downlevel servers.
+ 24-Jul-1991 JohnRo
+ Use NET_REMOTE_TRY_RPC etc macros for NetUse APIs.
+ Moved NetpIsServiceStarted() into NetLib.
+ 25-Jul-1991 JohnRo
+ Quiet DLL stub debug output.
+ 19-Aug-1991 JohnRo
+ Implement downlevel NetWksta APIs. Use NetRpc.h for NetWksta APIs.
+ 07-Nov-1991 JohnRo
+ RAID 4186: assert in RxNetShareAdd and other DLL stub problems.
+ 19-Nov-1991 JohnRo
+ Make sure status is correct for APIs not supported on downlevel.
+ Implement remote NetWkstaUserEnum().
+ 09-Nov-1992 JohnRo
+ Fix NET_API_FUNCTION references.
+ Avoid compiler warnings.
+--*/
+
+#include "brclient.h"
+#undef IF_DEBUG // avoid wsclient.h vs. debuglib.h conflicts.
+#include <debuglib.h> // IF_DEBUG() (needed by netrpc.h).
+#include <lmserver.h>
+#include <lmsvc.h>
+#include <rxuse.h> // RxNetUse APIs.
+#include <rxwksta.h> // RxNetWksta and RxNetWkstaUser APIs.
+#include <rap.h> // Needed by rxserver.h
+#include <rxserver.h> // RxNetServerEnum API.
+#include <netlib.h> // NetpServiceIsStarted() (needed by netrpc.h).
+#include <ntddbrow.h> // Browser definitions
+#include <netrpc.h> // NET_REMOTE macros.
+#include <align.h>
+#include <tstr.h>
+#include <tstring.h> // NetpInitOemString().
+#include <brcommon.h> // Routines common between client & server
+#include <lmapibuf.h> // NetApiBufferFree().
+#include <lmbrowsr.h> // Definition of I_BrowserServerEnum
+#include <icanon.h>
+#include <lmapibuf.h>
+
+//-------------------------------------------------------------------//
+// //
+// Global variables //
+// //
+//-------------------------------------------------------------------//
+
+#define API_SUCCESS(x) ((x) == NERR_Success || (x) == ERROR_MORE_DATA)
+
+
+//-------------------------------------------------------------------//
+// //
+// Global types //
+// //
+//-------------------------------------------------------------------//
+
+
+
+//-------------------------------------------------------------------//
+// //
+// Private routines //
+// //
+//-------------------------------------------------------------------//
+
+
+NET_API_STATUS
+GetBrowserTransportList(
+ OUT PLMDR_TRANSPORT_LIST *TransportList
+ );
+
+NET_API_STATUS
+EnumServersForTransport(
+ IN PUNICODE_STRING TransportName,
+ IN LPCWSTR DomainName OPTIONAL,
+ IN ULONG level,
+ IN ULONG prefmaxlen,
+ IN ULONG servertype,
+ IN LPTSTR CurrentComputerName,
+ OUT PINTERIM_SERVER_LIST InterimServerList,
+ OUT PULONG TotalEntriesOnThisTransport,
+ IN LPCWSTR FirstNameToReturn,
+ IN BOOLEAN WannishTransport,
+ IN BOOLEAN RasTransport,
+ IN BOOLEAN IPXTransport
+ );
+
+ NET_API_STATUS NET_API_FUNCTION
+NetServerEnum(
+ IN LPCWSTR servername OPTIONAL,
+ IN DWORD level,
+ OUT LPBYTE *bufptr,
+ IN DWORD prefmaxlen,
+ OUT LPDWORD entriesread,
+ OUT LPDWORD totalentries,
+ IN DWORD servertype,
+ IN LPCWSTR domain OPTIONAL,
+ IN OUT LPDWORD resume_handle OPTIONAL
+ )
+/*++
+
+Routine Description:
+
+ This is the DLL entrypoint for NetServerEnum.
+
+Arguments:
+
+ servername - Supplies the name of server to execute this function
+
+ level - Supplies the requested level of information.
+
+ bufptr - Returns a pointer to a buffer which contains the
+ requested transport information.
+
+ prefmaxlen - Supplies the number of bytes of information
+ to return in the buffer. If this value is MAXULONG, we will try
+ to return all available information if there is enough memory
+ resource.
+
+ entriesread - Returns the number of entries read into the buffer. This
+ value is returned only if the return code is NERR_Success or
+ ERROR_MORE_DATA.
+
+ totalentries - Returns the total number of entries available. This value
+ is returned only if the return code is NERR_Success or ERROR_MORE_DATA.
+
+ servertype - Supplies the type of server to enumerate.
+
+ domain - Supplies the name of one of the active domains to enumerate the
+ servers from. If NULL, servers from the primary domain, logon domain
+ and other domains are enumerated.
+
+ resume_handle - Supplies and returns the point to continue with enumeration.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NET_API_STATUS NetStatus;
+
+
+ //
+ // NetServerEnum is simply a wrapper to NetServerEnumEx
+ //
+
+ NetStatus = NetServerEnumEx(
+ servername,
+ level,
+ bufptr,
+ prefmaxlen,
+ entriesread,
+ totalentries,
+ servertype,
+ domain,
+ NULL ); // NULL FirstNameToReturn
+
+ if (ARGUMENT_PRESENT(resume_handle)) {
+ *resume_handle = 0;
+ }
+
+ return NetStatus;
+
+}
+
+
+ NET_API_STATUS NET_API_FUNCTION
+NetServerEnumEx(
+ IN LPCWSTR servername OPTIONAL,
+ IN DWORD level,
+ OUT LPBYTE *bufptr,
+ IN DWORD prefmaxlen,
+ OUT LPDWORD entriesread,
+ OUT LPDWORD totalentries,
+ IN DWORD servertype,
+ IN LPCWSTR domain OPTIONAL,
+ IN LPCWSTR FirstNameToReturnArg OPTIONAL
+ )
+/*++
+
+Routine Description:
+
+ This is the DLL entrypoint for NetServerEnum.
+
+Arguments:
+
+ servername - Supplies the name of server to execute this function
+
+ level - Supplies the requested level of information.
+
+ bufptr - Returns a pointer to a buffer which contains the
+ requested transport information.
+
+ prefmaxlen - Supplies the number of bytes of information
+ to return in the buffer. If this value is MAXULONG, we will try
+ to return all available information if there is enough memory
+ resource.
+
+ entriesread - Returns the number of entries read into the buffer. This
+ value is returned only if the return code is NERR_Success or
+ ERROR_MORE_DATA.
+
+ totalentries - Returns the total number of entries available. This value
+ is returned only if the return code is NERR_Success or ERROR_MORE_DATA.
+
+ servertype - Supplies the type of server to enumerate.
+
+ domain - Supplies the name of one of the active domains to enumerate the
+ servers from. If NULL, servers from the primary domain, logon domain
+ and other domains are enumerated.
+
+ FirstNameToReturnArg - Supplies the name of the first domain or server entry to return.
+ The caller can use this parameter to implement a resume handle of sorts by passing
+ the name of the last entry returned on a previous call. (Notice that the specified
+ entry will, also, be returned on this call unless it has since been deleted.)
+ Pass NULL (or a zero length string) to start with the first entry available.
+
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+ ERROR_MORE_DATA - More servers are available to be enumerated.
+
+ It is possible to return ERROR_MORE_DATA and zero entries in the case
+ where the browser server used doesn't support enumerating all the entries
+ it has. (e.g., an NT 3.5x Domain Master Browser that downloaded a domain
+ list from WINS and the WINS list is more than 64Kb long.) The caller
+ should simply ignore the additional data.
+
+ It is possible to fail to return ERROR_MORE_DATA and return a truncated
+ list. (e.g., an NT 3.5x Backup browser or WIN 95 backup browser in the
+ above mentioned domain. Such a backup browser replicates only 64kb
+ of data from the DMB (PDC) then represents that list as the entire list.)
+ The caller should ignore this problem. The site should upgrade its
+ browser servers.
+
+--*/
+{
+ PLMDR_TRANSPORT_LIST TransportList = NULL;
+ PLMDR_TRANSPORT_LIST TransportEntry = NULL;
+ INTERIM_SERVER_LIST InterimServerList;
+ NET_API_STATUS Status;
+ DWORD DomainNameSize = 0;
+ TCHAR DomainName[DNLEN + 1];
+ WCHAR FirstNameToReturn[DNLEN+1];
+ DWORD LocalTotalEntries;
+ BOOLEAN AnyTransportHasMoreData = FALSE;
+
+ //
+ //
+ // The workstation has to be started for the NetServerEnum API to work.
+ //
+ //
+
+ if ((Status = CheckForService(SERVICE_WORKSTATION, NULL)) != NERR_Success) {
+ return Status;
+ }
+
+ //
+ // Canonicalize the input parameters to make later comparisons easier.
+ //
+
+ if (ARGUMENT_PRESENT(domain)) {
+
+ if ( I_NetNameCanonicalize(
+ NULL,
+ (LPWSTR) domain,
+ DomainName,
+ (DNLEN + 1) * sizeof(TCHAR),
+ NAMETYPE_WORKGROUP,
+ LM2X_COMPATIBLE
+ ) != NERR_Success) {
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ DomainNameSize = STRLEN(DomainName) * sizeof(WCHAR);
+
+ domain = DomainName;
+ }
+
+ if (ARGUMENT_PRESENT(FirstNameToReturnArg) && *FirstNameToReturnArg != L'\0') {
+
+ if ( I_NetNameCanonicalize(
+ NULL,
+ (LPWSTR) FirstNameToReturnArg,
+ FirstNameToReturn,
+ sizeof(FirstNameToReturn),
+ NAMETYPE_WORKGROUP,
+ LM2X_COMPATIBLE
+ ) != NERR_Success) {
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ } else {
+ FirstNameToReturn[0] = L'\0';
+ }
+
+ if ((servername != NULL) &&
+ ( *servername != TCHAR_EOS)) {
+
+ //
+ // Call downlevel version of the API
+ //
+
+ Status = RxNetServerEnum(
+ servername,
+ NULL,
+ level,
+ bufptr,
+ prefmaxlen,
+ entriesread,
+ totalentries,
+ servertype,
+ domain,
+ FirstNameToReturn );
+
+ return Status;
+ }
+
+ //
+ // Only levels 100 and 101 are valid
+ //
+
+ if ((level != 100) && (level != 101)) {
+ return ERROR_INVALID_LEVEL;
+ }
+
+ if (servertype != SV_TYPE_ALL) {
+ if (servertype & SV_TYPE_DOMAIN_ENUM) {
+ if (servertype != SV_TYPE_DOMAIN_ENUM) {
+ return ERROR_INVALID_FUNCTION;
+ }
+ }
+ }
+
+
+ //
+ // Initialize the buffer to a known value.
+ //
+
+ *bufptr = NULL;
+
+ *entriesread = 0;
+
+ *totalentries = 0;
+
+ Status = InitializeInterimServerList(&InterimServerList, NULL, NULL, NULL, NULL);
+
+ try {
+ BOOL AnyEnumServersSucceeded = FALSE;
+ LPTSTR MyComputerName = NULL;
+
+ Status = NetpGetComputerName( &MyComputerName);
+
+ if ( Status != NERR_Success ) {
+ goto try_exit;
+ }
+
+ //
+ // Retrieve the list of transports from the browser.
+ //
+
+ Status = GetBrowserTransportList(&TransportList);
+
+ if (Status != NERR_Success) {
+ goto try_exit;
+ }
+
+ TransportEntry = TransportList;
+
+ while (TransportEntry != NULL) {
+ UNICODE_STRING TransportName;
+
+ TransportName.Buffer = TransportEntry->TransportName;
+ TransportName.Length = (USHORT)TransportEntry->TransportNameLength;
+ TransportName.MaximumLength = (USHORT)TransportEntry->TransportNameLength;
+
+ Status = EnumServersForTransport(&TransportName,
+ domain,
+ level,
+ prefmaxlen,
+ servertype,
+ MyComputerName,
+ &InterimServerList,
+ &LocalTotalEntries,
+ FirstNameToReturn,
+ (BOOLEAN)((TransportEntry->Flags & LMDR_TRANSPORT_WANNISH) != 0),
+ (BOOLEAN)((TransportEntry->Flags & LMDR_TRANSPORT_RAS) != 0),
+ (BOOLEAN)((TransportEntry->Flags & LMDR_TRANSPORT_IPX) != 0));
+
+ if (API_SUCCESS(Status)) {
+ if ( Status == ERROR_MORE_DATA ) {
+ AnyTransportHasMoreData = TRUE;
+ }
+ AnyEnumServersSucceeded = TRUE;
+ if ( LocalTotalEntries > *totalentries ) {
+ *totalentries = LocalTotalEntries;
+ }
+ }
+
+ if (TransportEntry->NextEntryOffset == 0) {
+ TransportEntry = NULL;
+ } else {
+ TransportEntry = (PLMDR_TRANSPORT_LIST)((PCHAR)TransportEntry+TransportEntry->NextEntryOffset);
+ }
+
+ }
+
+ if ( MyComputerName != NULL ) {
+ (void) NetApiBufferFree( MyComputerName );
+ }
+
+ if (AnyEnumServersSucceeded) {
+
+ //
+ // Pack the interim server list into its final form.
+ //
+
+ Status = PackServerList(&InterimServerList,
+ level,
+ servertype,
+ prefmaxlen,
+ (PVOID *)bufptr,
+ entriesread,
+ &LocalTotalEntries, // Pack thinks it has ALL the entries
+ NULL ); // The server has already returned us the right entries
+
+ if ( API_SUCCESS( Status ) ) {
+ if ( LocalTotalEntries > *totalentries ) {
+ *totalentries = LocalTotalEntries;
+ }
+ }
+ }
+
+try_exit:NOTHING;
+ } finally {
+ if (TransportList != NULL) {
+ MIDL_user_free(TransportList);
+ }
+
+ UninitializeInterimServerList(&InterimServerList);
+ }
+
+ if ( API_SUCCESS( Status )) {
+
+ //
+ // At this point,
+ // *totalentries is the largest of:
+ // The TotalEntries returned from any transport.
+ // The actual number of entries read.
+ //
+ // Adjust TotalEntries returned for reality.
+ //
+
+ if ( Status == NERR_Success ) {
+ *totalentries = *entriesread;
+ } else {
+ if ( *totalentries <= *entriesread ) {
+ *totalentries = *entriesread + 1;
+ }
+ }
+
+ //
+ // Ensure we return ERROR_MORE_DATA if any transport has more data.
+ //
+
+ if ( AnyTransportHasMoreData ) {
+ Status = ERROR_MORE_DATA;
+ }
+ }
+
+ return Status;
+}
+
+ NET_API_STATUS
+EnumServersForTransport(
+ IN PUNICODE_STRING TransportName,
+ IN LPCWSTR DomainName OPTIONAL,
+ IN ULONG level,
+ IN ULONG prefmaxlen,
+ IN ULONG servertype,
+ IN LPTSTR CurrentComputerName,
+ OUT PINTERIM_SERVER_LIST InterimServerList,
+ OUT PULONG TotalEntriesOnThisTransport,
+ IN LPCWSTR FirstNameToReturn,
+ IN BOOLEAN WannishTransport,
+ IN BOOLEAN RasTransport,
+ IN BOOLEAN IpxTransport
+ )
+{
+ PWSTR *BrowserList = NULL;
+ ULONG BrowserListLength = 0;
+ NET_API_STATUS Status;
+ PVOID ServerList = NULL;
+ ULONG EntriesInList = 0;
+ ULONG ServerIndex = 0;
+
+ //
+ // Skip over IPX transports - we can't contact machines over them anyway.
+ //
+
+ *TotalEntriesOnThisTransport = 0;
+
+ if (IpxTransport) {
+ return NERR_Success;
+ }
+
+ //
+ // Retrieve a new browser list. Do not force a revalidation.
+ //
+
+ Status = GetBrowserServerList(TransportName,
+ DomainName,
+ &BrowserList,
+ &BrowserListLength,
+ FALSE);
+
+ //
+ // If a domain name was specified and we were unable to find the browse
+ // master for the domain and we are running on a wannish transport,
+ // invoke the "double hop" code and allow a local browser server
+ // remote the API to the browse master for that domain (we assume that
+ // this means that the workgroup is on a different subnet of a WAN).
+ //
+
+ if (!API_SUCCESS(Status) &&
+ DomainName != NULL) {
+
+ Status = GetBrowserServerList(TransportName,
+ NULL,
+ &BrowserList,
+ &BrowserListLength,
+ FALSE);
+
+
+ }
+
+
+ //
+ // If we were able to retrieve the list, remote the API. Otherwise
+ // return.
+ //
+
+ if (API_SUCCESS(Status)) {
+
+ do {
+ LPTSTR Transport;
+ LPTSTR ServerName;
+ BOOL AlreadyInTree;
+
+ //
+ // Remote the API to that server.
+ //
+
+ Transport = TransportName->Buffer;
+ ServerName = BrowserList[0];
+ *TotalEntriesOnThisTransport = 0;
+
+ // add 2 to skip double backslash at start of ServerName
+
+ if ( STRICMP(ServerName + 2, CurrentComputerName ) == 0 ) {
+
+ //
+ // If we are going to remote the API to ourselves,
+ // and we are running the browser service, simply
+ // use RPC to get the information we need, don't
+ // bother using the redirector. This allows us to
+ // avoid tying up RPCXLATE thread.
+ //
+
+ Status = I_BrowserServerEnumEx (
+ NULL,
+ Transport,
+ CurrentComputerName,
+ level,
+ (LPBYTE *)&ServerList,
+ prefmaxlen,
+ &EntriesInList,
+ TotalEntriesOnThisTransport,
+ servertype,
+ DomainName,
+ FirstNameToReturn );
+
+ } else {
+
+ Status = RxNetServerEnum(
+ ServerName,
+ Transport,
+ level,
+ (LPBYTE *)&ServerList,
+ prefmaxlen,
+ &EntriesInList,
+ TotalEntriesOnThisTransport,
+ servertype,
+ DomainName,
+ FirstNameToReturn );
+
+
+ }
+
+ if ( !API_SUCCESS(Status)) {
+ NET_API_STATUS GetBListStatus;
+
+ //
+ // If we failed to remote the API for some reason,
+ // we want to regenerate the bowsers list of browser
+ // servers.
+ //
+
+ if (BrowserList != NULL) {
+
+ MIDL_user_free(BrowserList);
+
+ BrowserList = NULL;
+ }
+
+
+ GetBListStatus = GetBrowserServerList(TransportName,
+ DomainName,
+ &BrowserList,
+ &BrowserListLength,
+ TRUE);
+ if (GetBListStatus != NERR_Success) {
+
+ //
+ // If we were unable to reload the list,
+ // try the next transport.
+ //
+
+ break;
+ }
+
+ ServerIndex += 1;
+
+ //
+ // If we've looped more times than we got servers
+ // in the list, we're done.
+ //
+
+ if ( ServerIndex > BrowserListLength ) {
+ break;
+ }
+
+ } else {
+
+ NET_API_STATUS TempStatus;
+ TempStatus = MergeServerList(
+ InterimServerList,
+ level,
+ ServerList,
+ EntriesInList,
+ *TotalEntriesOnThisTransport );
+
+ if ( TempStatus != NERR_Success ) {
+ Status = TempStatus;
+ }
+
+ //
+ // The remote API succeeded.
+ //
+ // Now free up the remaining parts of the list.
+ //
+
+ if (ServerList != NULL) {
+ NetApiBufferFree(ServerList);
+ ServerList = NULL;
+ }
+
+ // We're done regardless of the success or failure of MergeServerList.
+ break;
+
+ }
+
+ } while ( !API_SUCCESS(Status) );
+
+ }
+
+ //
+ // Free up the browser list.
+ //
+
+ if (BrowserList != NULL) {
+ MIDL_user_free(BrowserList);
+ BrowserList = NULL;
+ }
+
+ return Status;
+}
+
+
+NET_API_STATUS
+GetBrowserTransportList(
+ OUT PLMDR_TRANSPORT_LIST *TransportList
+ )
+
+/*++
+
+Routine Description:
+
+ This routine returns the list of transports bound into the browser.
+
+Arguments:
+
+ OUT PLMDR_TRANSPORT_LIST *TransportList - Transport list to return.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+
+{
+
+ NET_API_STATUS Status;
+ HANDLE BrowserHandle;
+ LMDR_REQUEST_PACKET RequestPacket;
+
+ Status = OpenBrowser(&BrowserHandle);
+
+ if (Status != NERR_Success) {
+ return Status;
+ }
+
+ RequestPacket.Version = LMDR_REQUEST_PACKET_VERSION;
+
+ RequestPacket.Type = EnumerateXports;
+
+ RtlInitUnicodeString(&RequestPacket.TransportName, NULL);
+#ifdef _CAIRO_
+ RtlInitUnicodeString(&RequestPacket.EmulatedDomainName, NULL);
+#endif // _CAIRO_
+
+ Status = DeviceControlGetInfo(
+ BrowserHandle,
+ IOCTL_LMDR_ENUMERATE_TRANSPORTS,
+ &RequestPacket,
+ sizeof(RequestPacket),
+ (PVOID *)TransportList,
+ 0xffffffff,
+ 4096,
+ NULL);
+
+ NtClose(BrowserHandle);
+
+ return Status;
+}
+
+ NET_API_STATUS
+I_BrowserServerEnum (
+ IN LPCWSTR servername OPTIONAL,
+ IN LPCWSTR transport OPTIONAL,
+ IN LPCWSTR clientname OPTIONAL,
+ IN DWORD level,
+ OUT LPBYTE *bufptr,
+ IN DWORD prefmaxlen,
+ OUT LPDWORD entriesread,
+ OUT LPDWORD totalentries,
+ IN DWORD servertype,
+ IN LPCWSTR domain OPTIONAL,
+ IN OUT LPDWORD resume_handle OPTIONAL
+ )
+
+/*++
+
+Routine Description:
+
+ This is the DLL entrypoint for NetWkstaSetInfo.
+
+Arguments:
+
+ servername - Supplies the name of server to execute this function
+
+ level - Supplies the level of information.
+
+ buf - Supplies a buffer which contains the information structure of fields
+ to set. The level denotes the structure in this buffer.
+
+ parm_err - Returns the identifier to the invalid parameter in buf if this
+ function returns ERROR_INVALID_PARAMETER.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NET_API_STATUS status;
+ GENERIC_INFO_CONTAINER GenericInfoContainer;
+ GENERIC_ENUM_STRUCT InfoStruct;
+
+ GenericInfoContainer.Buffer = NULL;
+ GenericInfoContainer.EntriesRead = 0;
+
+ InfoStruct.Container = &GenericInfoContainer;
+ InfoStruct.Level = level;
+
+ NET_REMOTE_TRY_RPC
+
+ //
+ // Try RPC (local or remote) version of API.
+ //
+
+ status = I_BrowserrServerEnum(
+ (LPWSTR) servername,
+ (LPWSTR) transport,
+ (LPWSTR) clientname,
+ (LPSERVER_ENUM_STRUCT)&InfoStruct,
+ prefmaxlen,
+ totalentries,
+ servertype,
+ (LPWSTR) domain,
+ resume_handle
+ );
+
+ if (status == NERR_Success || status == ERROR_MORE_DATA) {
+ *bufptr = (LPBYTE) GenericInfoContainer.Buffer;
+ *entriesread = GenericInfoContainer.EntriesRead;
+
+#if 0
+ if (((servertype == SV_TYPE_ALL || servertype == SV_TYPE_DOMAIN_ENUM)) &&
+ (STRICMP(transport, L"\\Device\\Streams\\NBT"))) {
+ if (*entriesread <= 20) {
+ KdPrint(("RPC API Returned EntriesRead == %ld on transport %ws\n", *entriesread, transport));
+ }
+ if (*totalentries <= 20) {
+ KdPrint(("RPC API Returned TotalEntries == %ld on transport %ws\n", *totalentries, transport));
+ }
+ }
+#endif
+ }
+
+ NET_REMOTE_RPC_FAILED("I_BrServerEnum",
+ servername,
+ status,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_BROWSER )
+
+ //
+ // There is no downlevel version of api.
+ //
+ status = ERROR_NOT_SUPPORTED;
+
+ NET_REMOTE_END
+
+#if 0
+ if ((servertype == SV_TYPE_ALL || servertype == SV_TYPE_DOMAIN_ENUM) &&
+ (STRICMP(transport, L"\\Device\\Streams\\NBT"))) {
+ if (*entriesread <= 20) {
+ KdPrint(("Client API Returned EntriesRead == %ld on transport %ws\n", *entriesread, transport));
+ }
+ if (*totalentries <= 20) {
+ KdPrint(("Client API Returned TotalEntries == %ld on transport %ws\n", *totalentries, transport));
+ }
+ }
+#endif
+
+ return status;
+}
+
+ NET_API_STATUS
+I_BrowserServerEnumEx (
+ IN LPCWSTR servername OPTIONAL,
+ IN LPCWSTR transport OPTIONAL,
+ IN LPCWSTR clientname OPTIONAL,
+ IN DWORD level,
+ OUT LPBYTE *bufptr,
+ IN DWORD prefmaxlen,
+ OUT LPDWORD entriesread,
+ OUT LPDWORD totalentries,
+ IN DWORD servertype,
+ IN LPCWSTR domain OPTIONAL,
+ IN LPCWSTR FirstNameToReturn OPTIONAL
+ )
+
+/*++
+
+Routine Description:
+
+ This is the DLL entrypoint for NetWkstaSetInfo.
+
+Arguments:
+
+ servername - Supplies the name of server to execute this function
+
+ level - Supplies the level of information.
+
+ buf - Supplies a buffer which contains the information structure of fields
+ to set. The level denotes the structure in this buffer.
+
+ parm_err - Returns the identifier to the invalid parameter in buf if this
+ function returns ERROR_INVALID_PARAMETER.
+
+ FirstNameToReturn - Supplies the name of the first domain or server entry to return.
+ The caller can use this parameter to implement a resume handle of sorts by passing
+ the name of the last entry returned on a previous call. (Notice that the specified
+ entry will, also, be returned on this call unless it has since been deleted.)
+ Pass NULL to start with the first entry available.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NET_API_STATUS status;
+ GENERIC_INFO_CONTAINER GenericInfoContainer;
+ GENERIC_ENUM_STRUCT InfoStruct;
+
+ GenericInfoContainer.Buffer = NULL;
+ GenericInfoContainer.EntriesRead = 0;
+
+ InfoStruct.Container = &GenericInfoContainer;
+ InfoStruct.Level = level;
+
+ NET_REMOTE_TRY_RPC
+
+ //
+ // Try RPC (local or remote) version of API.
+ //
+
+ status = I_BrowserrServerEnumEx(
+ (LPWSTR) servername,
+ (LPWSTR) transport,
+ (LPWSTR) clientname,
+ (LPSERVER_ENUM_STRUCT)&InfoStruct,
+ prefmaxlen,
+ totalentries,
+ servertype,
+ (LPWSTR) domain,
+ (LPWSTR) FirstNameToReturn );
+
+ if (status == NERR_Success || status == ERROR_MORE_DATA) {
+ *bufptr = (LPBYTE) GenericInfoContainer.Buffer;
+ *entriesread = GenericInfoContainer.EntriesRead;
+
+#if 0
+ if (((servertype == SV_TYPE_ALL || servertype == SV_TYPE_DOMAIN_ENUM)) &&
+ (STRICMP(transport, L"\\Device\\Streams\\NBT"))) {
+ if (*entriesread <= 20) {
+ KdPrint(("RPC API Returned EntriesRead == %ld on transport %ws\n", *entriesread, transport));
+ }
+ if (*totalentries <= 20) {
+ KdPrint(("RPC API Returned TotalEntries == %ld on transport %ws\n", *totalentries, transport));
+ }
+ }
+#endif
+ }
+
+ NET_REMOTE_RPC_FAILED("I_BrServerEnum",
+ servername,
+ status,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_BROWSER )
+
+ //
+ // There is no downlevel version of api.
+ //
+ status = ERROR_NOT_SUPPORTED;
+
+ NET_REMOTE_END
+
+#if 0
+ if ((servertype == SV_TYPE_ALL || servertype == SV_TYPE_DOMAIN_ENUM) &&
+ (STRICMP(transport, L"\\Device\\Streams\\NBT"))) {
+ if (*entriesread <= 20) {
+ KdPrint(("Client API Returned EntriesRead == %ld on transport %ws\n", *entriesread, transport));
+ }
+ if (*totalentries <= 20) {
+ KdPrint(("Client API Returned TotalEntries == %ld on transport %ws\n", *totalentries, transport));
+ }
+ }
+#endif
+
+ return status;
+}
+
+
+ NET_API_STATUS NET_API_FUNCTION
+I_BrowserQueryOtherDomains (
+ IN LPCWSTR servername OPTIONAL,
+ OUT LPBYTE *bufptr,
+ OUT LPDWORD entriesread,
+ OUT LPDWORD totalentries
+ )
+
+/*++
+
+Routine Description:
+
+ This is the DLL entrypoint for NetWkstaSetInfo.
+
+Arguments:
+
+ servername - Supplies the name of server to execute this function
+
+ buf - Supplies a buffer which contains the information structure of fields
+ to set. The level denotes the structure in this buffer.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NET_API_STATUS status;
+ GENERIC_INFO_CONTAINER GenericInfoContainer;
+ GENERIC_ENUM_STRUCT InfoStruct;
+
+ GenericInfoContainer.Buffer = NULL;
+ GenericInfoContainer.EntriesRead = 0;
+
+ InfoStruct.Container = &GenericInfoContainer;
+ InfoStruct.Level = 100;
+
+ NET_REMOTE_TRY_RPC
+
+ //
+ // Try RPC (local or remote) version of API.
+ //
+
+ status = I_BrowserrQueryOtherDomains (
+ (LPWSTR) servername,
+ (LPSERVER_ENUM_STRUCT)&InfoStruct,
+ totalentries
+ );
+
+ if (status == NERR_Success || status == ERROR_MORE_DATA) {
+ *bufptr = (LPBYTE) GenericInfoContainer.Buffer;
+ *entriesread = GenericInfoContainer.EntriesRead;
+ }
+
+ NET_REMOTE_RPC_FAILED("I_BrowserQueryOtherDomains",
+ servername,
+ status,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_BROWSER )
+
+ //
+ // There is no downlevel version of api.
+ //
+ status = ERROR_NOT_SUPPORTED;
+
+ NET_REMOTE_END
+
+ return status;
+}
+ NET_API_STATUS
+I_BrowserResetNetlogonState (
+ IN LPCWSTR servername OPTIONAL
+ )
+
+/*++
+
+Routine Description:
+
+ This is the DLL entrypoint for NetWkstaSetInfo.
+
+Arguments:
+
+ servername - Supplies the name of server to execute this function
+
+ buf - Supplies a buffer which contains the information structure of fields
+ to set. The level denotes the structure in this buffer.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NET_API_STATUS status;
+
+ NET_REMOTE_TRY_RPC
+
+ //
+ // Try RPC (local or remote) version of API.
+ //
+
+ status = I_BrowserrResetNetlogonState (
+ (LPWSTR) servername );
+
+ NET_REMOTE_RPC_FAILED("I_BrowserResetNetlogonState",
+ servername,
+ status,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_BROWSER )
+
+ //
+ // There is no downlevel version of api.
+ //
+ status = ERROR_NOT_SUPPORTED;
+
+ NET_REMOTE_END
+
+ return status;
+}
+
+
+NET_API_STATUS
+I_BrowserDebugCall (
+ IN LPTSTR servername OPTIONAL,
+ IN DWORD DebugCode,
+ IN DWORD Options
+ )
+{
+ NET_API_STATUS status;
+
+ NET_REMOTE_TRY_RPC
+
+ //
+ // Try RPC (local or remote) version of API.
+ //
+
+ status = I_BrowserrDebugCall(
+ servername,
+ DebugCode,
+ Options
+ );
+
+
+ NET_REMOTE_RPC_FAILED("I_BrowserDebugCall",
+ servername,
+ status,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_BROWSER )
+
+ //
+ // There is no downlevel version of api.
+ //
+ status = ERROR_NOT_SUPPORTED;
+
+ NET_REMOTE_END
+
+ return status;
+
+}
+
+NET_API_STATUS
+I_BrowserDebugTrace (
+ IN LPTSTR servername OPTIONAL,
+ IN PCHAR DebugString
+ )
+{
+ NET_API_STATUS status;
+
+ NET_REMOTE_TRY_RPC
+
+ //
+ // Try RPC (local or remote) version of API.
+ //
+
+ status = I_BrowserrDebugTrace(
+ servername,
+ DebugString
+ );
+
+
+ NET_REMOTE_RPC_FAILED("I_BrowserDebugTrace",
+ servername,
+ status,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_BROWSER )
+
+ //
+ // There is no downlevel version of api.
+ //
+ status = ERROR_NOT_SUPPORTED;
+
+ NET_REMOTE_END
+
+ return status;
+
+}
+
+
+NET_API_STATUS
+I_BrowserQueryStatistics (
+ IN LPCWSTR servername OPTIONAL,
+ IN OUT LPBROWSER_STATISTICS *Statistics
+ )
+{
+ NET_API_STATUS status;
+
+ NET_REMOTE_TRY_RPC
+
+ //
+ // Try RPC (local or remote) version of API.
+ //
+
+ status = I_BrowserrQueryStatistics(
+ (LPWSTR) servername,
+ Statistics
+ );
+
+
+ NET_REMOTE_RPC_FAILED("I_BrowserQueryStatistics",
+ servername,
+ status,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_BROWSER )
+
+ //
+ // There is no downlevel version of api.
+ //
+ status = ERROR_NOT_SUPPORTED;
+
+ NET_REMOTE_END
+
+ return status;
+
+}
+
+ NET_API_STATUS
+I_BrowserResetStatistics (
+ IN LPCWSTR servername OPTIONAL
+ )
+{
+ NET_API_STATUS status;
+
+ NET_REMOTE_TRY_RPC
+
+ //
+ // Try RPC (local or remote) version of API.
+ //
+
+ status = I_BrowserrResetStatistics(
+ (LPWSTR) servername );
+
+
+ NET_REMOTE_RPC_FAILED("I_BrowserResetStatistics",
+ servername,
+ status,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_BROWSER )
+
+ //
+ // There is no downlevel version of api.
+ //
+ status = ERROR_NOT_SUPPORTED;
+
+ NET_REMOTE_END
+
+ return status;
+
+}
+
+
+NET_API_STATUS
+NetBrowserStatisticsGet(
+ IN LPTSTR ServerName,
+ IN DWORD Level,
+ OUT LPBYTE* Buffer
+ )
+
+/*++
+
+Routine Description:
+
+ Wrapper for workstation statistics retrieval routine - either calls the
+ client-side RPC function or calls RxNetStatisticsGet to retrieve the
+ statistics from a down-level workstation service
+
+Arguments:
+
+ ServerName - where to remote this function
+ Level - of information required (100, or 101)
+ Buffer - pointer to pointer to returned buffer
+
+Return Value:
+
+ NET_API_STATUS
+ Success - NERR_Success
+ Failure - ERROR_INVALID_LEVEL
+ Level not 0
+ ERROR_INVALID_PARAMETER
+ Unsupported options requested
+ ERROR_NOT_SUPPORTED
+ Service is not SERVER or WORKSTATION
+ ERROR_ACCESS_DENIED
+ Caller doesn't have necessary access rights for request
+
+--*/
+
+{
+ NET_API_STATUS status;
+ GENERIC_INFO_CONTAINER GenericInfoContainer;
+ GENERIC_ENUM_STRUCT InfoStruct;
+
+ //
+ // set the caller's buffer pointer to known value. This will kill the
+ // calling app if it gave us a bad pointer and didn't use try...except
+ //
+
+ *Buffer = NULL;
+
+ //
+ // validate parms
+ //
+
+ if (Level != 100 && Level != 101) {
+ return ERROR_INVALID_LEVEL;
+ }
+
+ GenericInfoContainer.Buffer = NULL;
+ GenericInfoContainer.EntriesRead = 0;
+
+ InfoStruct.Container = &GenericInfoContainer;
+ InfoStruct.Level = Level;
+
+
+ NET_REMOTE_TRY_RPC
+ status = NetrBrowserStatisticsGet(ServerName,
+ Level,
+ (PBROWSER_STATISTICS_STRUCT)&InfoStruct
+ );
+
+ if (status == NERR_Success || status == ERROR_MORE_DATA) {
+ *Buffer = (LPBYTE) GenericInfoContainer.Buffer;
+ }
+
+ NET_REMOTE_RPC_FAILED("NetBrowserStatisticsGet",
+ ServerName,
+ status,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_BROWSER
+ )
+
+ status = ERROR_NOT_SUPPORTED;
+
+ NET_REMOTE_END
+
+ return status;
+}
+
+
+ NET_API_STATUS
+I_BrowserSetNetlogonState(
+ IN LPWSTR ServerName,
+ IN LPWSTR DomainName,
+ IN LPWSTR EmulatedComputerName,
+ IN DWORD Role
+ )
+/*++
+
+Routine Description:
+
+ This is the DLL entrypoint for I_BrowserSetNetlogonState.
+
+Arguments:
+
+ servername - Supplies the name of server to execute this function
+
+ DomainName - name of the domain who's role is to be updated.
+
+ EmulatedComputerName - Name of the computer within DomainName
+
+ Role - Role of the specified domain.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+#ifdef _CAIRO_
+ NET_API_STATUS status;
+
+ NET_REMOTE_TRY_RPC
+
+ //
+ // Try RPC (local or remote) version of API.
+ //
+
+ status = I_BrowserrSetNetlogonState (
+ ServerName,
+ DomainName,
+ EmulatedComputerName,
+ Role );
+
+ NET_REMOTE_RPC_FAILED("I_BrowserSetNetlogonState",
+ ServerName,
+ status,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_BROWSER )
+
+ //
+ // There is no downlevel version of api.
+ //
+ status = ERROR_NOT_SUPPORTED;
+
+ NET_REMOTE_END
+
+ return status;
+#else // _CAIRO_
+ return ERROR_NOT_SUPPORTED;
+#endif // _CAIRO_
+}
+
+ NET_API_STATUS NET_API_FUNCTION
+I_BrowserQueryEmulatedDomains (
+ IN LPTSTR ServerName OPTIONAL,
+ OUT PBROWSER_EMULATED_DOMAIN *EmulatedDomains,
+ OUT LPDWORD EntriesRead
+ )
+
+/*++
+
+Routine Description:
+
+ This is the DLL entrypoint for I_BrowserQueryEmulatedDomains.
+
+Arguments:
+
+ ServerName - Supplies the name of server to execute this function
+
+ EmulatedDomains - Returns a pointer to a an allocated array of emulated domain
+ information.
+
+ EntriesRead - Returns the number of entries in 'EmulatedDomains'
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+#ifdef _CAIRO_
+ NET_API_STATUS NetStatus;
+ BROWSER_EMULATED_DOMAIN_CONTAINER Container;
+
+ // Force RPC to allocate the buffer
+ Container.Buffer = NULL;
+ Container.EntriesRead = 0;
+ *EmulatedDomains = NULL;
+ *EntriesRead = 0;
+
+ NET_REMOTE_TRY_RPC
+
+ //
+ // Try RPC (local or remote) version of API.
+ //
+
+ NetStatus = I_BrowserrQueryEmulatedDomains (
+ ServerName,
+ &Container );
+
+ if ( NetStatus == NERR_Success ) {
+ *EmulatedDomains = (PBROWSER_EMULATED_DOMAIN) Container.Buffer;
+ *EntriesRead = Container.EntriesRead;
+ }
+
+
+ NET_REMOTE_RPC_FAILED("I_BrowserQueryEmulatedDomains",
+ ServerName,
+ NetStatus,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_BROWSER )
+
+ //
+ // There is no downlevel version of api.
+ //
+ NetStatus = ERROR_NOT_SUPPORTED;
+
+ NET_REMOTE_END
+
+ return NetStatus;
+#else // _CAIRO_
+ return ERROR_NOT_SUPPORTED;
+#endif // _CAIRO_
+}
diff --git a/private/net/svcdlls/browser/client/brtsenum.c b/private/net/svcdlls/browser/client/brtsenum.c
new file mode 100644
index 000000000..1347ad301
--- /dev/null
+++ b/private/net/svcdlls/browser/client/brtsenum.c
@@ -0,0 +1,590 @@
+/*++
+
+Copyright (c) 1991-1992 Microsoft Corporation
+
+Module Name:
+
+ wstsend.c
+
+Abstract:
+
+ Test program for the NetServerEnum API. Run this test after
+ starting the Workstation service.
+
+ wstenum [domain]
+
+Author:
+
+ Rita Wong (ritaw) 24-Oct-1991
+
+Revision History:
+
+--*/
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+
+#include <winerror.h>
+#include <windef.h> // Win32 type definitions
+#include <winbase.h> // Win32 base API prototypes
+
+#include <lm.h> // LAN Man definitions
+#include <lmserver.h>
+
+#include <tstring.h>
+#include <netdebug.h> // NetpDbgDisplay routines.
+#include <dlserver.h>
+
+#define FIXED_WIDTH_STRING "%-30ws: "
+#define INDENT " "
+
+VOID
+DisplayServerInfo(
+ IN DWORD Level,
+ IN LPVOID Info
+ );
+
+VOID
+DisplayDword(
+ IN LPTSTR Tag,
+ IN DWORD Value
+ );
+
+VOID
+DisplayBool(
+ IN LPTSTR Tag,
+ IN BOOL Value
+ );
+
+VOID
+TestServerEnum(
+ IN LPTSTR DomainName OPTIONAL,
+ IN DWORD PreferedMaximumLength,
+ IN OUT LPDWORD ResumeHandle OPTIONAL
+ );
+
+
+
+VOID
+_cdecl
+main(
+ int argc,
+ char *argv[]
+ )
+{
+ LPTSTR DomainName = NULL;
+
+ if (argc > 2) {
+ printf("Usage: wstenum [domain].\n");
+ return;
+ }
+
+ if (argc == 2) {
+#ifdef UNICODE
+ OEM_STRING AString;
+ UNICODE_STRING UString;
+
+ NetpInitOemString(&AString, argv[1]);
+ RtlOemStringToUnicodeString(&UString, &AString, TRUE);
+ DomainName = UString.Buffer;
+#else
+ DomainName = argv[1];
+
+#endif
+ }
+
+ //
+ // Enumerate all servers
+ //
+ TestServerEnum(DomainName, MAXULONG, NULL);
+}
+
+
+
+
+
+VOID
+TestServerEnum(
+ IN LPTSTR DomainName OPTIONAL,
+ IN DWORD PreferedMaximumLength,
+ IN OUT LPDWORD ResumeHandle OPTIONAL
+ )
+{
+ DWORD i;
+ NET_API_STATUS status;
+ DWORD EntriesRead,
+ TotalEntries;
+
+ PSERVER_INFO_101 ServerInfo, saveptr;
+
+
+ if (ARGUMENT_PRESENT(ResumeHandle)) {
+ printf("\nInput ResumeHandle=x%08lx\n", *ResumeHandle);
+ }
+
+ status = NetServerEnum(
+ NULL,
+ 101,
+ (LPBYTE *) &ServerInfo,
+ PreferedMaximumLength,
+ &EntriesRead,
+ &TotalEntries,
+ SV_TYPE_ALL,
+ DomainName,
+ ResumeHandle
+ );
+
+ saveptr = ServerInfo;
+
+ if (status != NERR_Success && status != ERROR_MORE_DATA) {
+ printf("NetServerEnum FAILED %lu\n", status);
+ }
+ else {
+ printf("Return code from NetServerEnum %lu\n", status);
+
+ printf("EntriesRead=%lu, TotalEntries=%lu\n",
+ EntriesRead, TotalEntries);
+
+ if (ARGUMENT_PRESENT(ResumeHandle)) {
+ printf("Output ResumeHandle=x%08lx\n", *ResumeHandle);
+ }
+
+ for (i = 0; i < EntriesRead; i++, ServerInfo++) {
+ DisplayServerInfo(101, ServerInfo);
+ }
+
+ //
+ // Free buffer allocated for us.
+ //
+ NetApiBufferFree(saveptr);
+ }
+}
+VOID
+DisplayTag(
+ IN LPTSTR Tag
+ )
+{
+ printf(INDENT FIXED_WIDTH_STRING, Tag);
+
+} // NetpDbgDisplayTag
+VOID
+DisplayString(
+ IN LPTSTR Tag,
+ IN LPTSTR Value
+ )
+{
+ DisplayTag( Tag );
+ if (Value != NULL) {
+ printf(FORMAT_LPTSTR "\n", Value);
+ } else {
+ printf("(none)\n");
+ }
+
+} // NetpDbgDisplayString
+
+VOID
+DisplayDwordHex(
+ IN LPTSTR Tag,
+ IN DWORD Value
+ )
+{
+ DisplayTag( Tag );
+ printf(FORMAT_HEX_DWORD, Value);
+ printf("\n");
+
+} // NetpDbgDisplayDwordHex
+
+DBGSTATIC VOID
+DisplayServerType(
+ IN DWORD Type
+ )
+{
+ // Longest name is "POTENTIAL_BROWSER" (14 chars)
+ TCHAR str[(17+2)*17]; // 17 chars per name, 2 spaces, for 17 names.
+ str[0] = '\0';
+
+ // BUGBUG: Use TEXT() macro somehow below?
+#define DO(name) \
+ if (Type & SV_TYPE_ ## name) { \
+ (void) STRCAT(str, TEXT(#name)); \
+ (void) STRCAT(str, TEXT(" ")); \
+ Type &= ~(SV_TYPE_ ## name); \
+ }
+
+ NetpAssert(Type != 0);
+ DO(WORKSTATION)
+ DO(SERVER)
+ DO(SQLSERVER)
+ DO(DOMAIN_CTRL)
+ DO(DOMAIN_BAKCTRL)
+ DO(TIME_SOURCE)
+ DO(AFP)
+ DO(NOVELL)
+ DO(DOMAIN_MEMBER)
+ DO(PRINTQ_SERVER)
+ DO(DIALIN_SERVER)
+ DO(XENIX_SERVER)
+ DO(NT)
+ DO(POTENTIAL_BROWSER)
+ DO(BACKUP_BROWSER)
+ DO(MASTER_BROWSER)
+ DO(DOMAIN_MASTER)
+
+ DisplayString(TEXT("Server Type"), str);
+ if (Type != 0) {
+ DisplayDwordHex( TEXT("UNEXPECTED TYPE BIT(S)"), Type );
+ }
+
+} // DisplayServerType
+VOID
+DisplayLanManVersion(
+ IN DWORD MajorVersion,
+ IN DWORD MinorVersion
+ )
+{
+ DisplayTag( TEXT("LanMan version") );
+ printf(FORMAT_DWORD "." FORMAT_DWORD "\n",
+ (DWORD) (MajorVersion & (MAJOR_VERSION_MASK)),
+ (DWORD) (MinorVersion) );
+
+} // DisplayLanManVersion
+
+DBGSTATIC VOID
+DisplayDisconnectTime(
+ IN LONG DiscTime
+ )
+{
+ DisplayTag( TEXT("Idle session time (min)") );
+ if (DiscTime == SV_NODISC) {
+ printf("infinite\n" );
+ } else {
+ printf(FORMAT_LONG "\n", DiscTime );
+ }
+} // NetpDbgDisplayDisconnectTime
+
+DBGSTATIC VOID
+DisplayLicenses(
+ IN DWORD MajorVersion,
+ IN DWORD Licenses
+ )
+{
+ // BUGBUG: Eventually, use <srvver.h> stuff to handle unlimited and other
+ // types of licenses. This is indicated in MajorVersion.
+ UNREFERENCED_PARAMETER( MajorVersion );
+
+ DisplayDword( TEXT("Licenses (NOT users)"), Licenses );
+} // NetpDbgDisplayLicenses
+
+VOID
+DisplayPlatformId(
+ IN DWORD Value
+ )
+{
+ LPTSTR String;
+
+ switch (Value) {
+ case PLATFORM_ID_DOS : String = TEXT("DOS"); break;
+ case PLATFORM_ID_OS2 : String = TEXT("OS2"); break;
+ case PLATFORM_ID_NT : String = TEXT("NT"); break;
+ default : String = TEXT("unknown"); break;
+ }
+
+ DisplayString( TEXT("Platform ID"), String );
+}
+VOID
+DisplayBool(
+ IN LPTSTR Tag,
+ IN BOOL Value
+ )
+{
+ DisplayTag( Tag );
+ printf(Value ? "Yes" : "No");
+ printf("\n");
+
+}
+
+VOID
+DisplayDword(
+ IN LPTSTR Tag,
+ IN DWORD Value
+ )
+{
+ DisplayTag( Tag );
+ printf(FORMAT_DWORD, Value);
+ printf("\n");
+
+} // DbgDisplayDword
+VOID
+DisplayServerInfo(
+ IN DWORD Level,
+ IN LPVOID Info
+ )
+{
+ printf("server info (level " FORMAT_DWORD ") at "
+ FORMAT_LPVOID ":\n", Level, (LPVOID) Info);
+ NetpAssert(Info != NULL);
+
+ switch (Level) {
+ case 0 :
+ {
+ LPSERVER_INFO_0 psv0 = Info;
+ DisplayString(TEXT("name"), psv0->sv0_name);
+ }
+ break;
+
+ case 1 :
+ {
+ LPSERVER_INFO_1 psv1 = Info;
+ DisplayString(TEXT("name"), psv1->sv1_name);
+ DisplayLanManVersion(
+ psv1->sv1_version_major,
+ psv1->sv1_version_minor);
+ DisplayServerType( psv1->sv1_type );
+ DisplayString(TEXT("comment"), psv1->sv1_comment);
+ }
+ break;
+
+ case 2 :
+ {
+ LPSERVER_INFO_2 psv2 = Info;
+ DisplayString(TEXT("name"), psv2->sv2_name);
+ DisplayLanManVersion(
+ psv2->sv2_version_major,
+ psv2->sv2_version_minor);
+ DisplayServerType( psv2->sv2_type );
+ DisplayString(TEXT("comment"), psv2->sv2_comment);
+ DisplayDword(TEXT("ulist_mtime"), psv2->sv2_ulist_mtime);
+ DisplayDword(TEXT("glist_mtime"), psv2->sv2_glist_mtime);
+ DisplayDword(TEXT("alist_mtime"), psv2->sv2_alist_mtime);
+ DisplayDword(TEXT("users"), psv2->sv2_users);
+ DisplayDisconnectTime( psv2->sv2_disc);
+ DisplayString(TEXT("alerts"), psv2->sv2_alerts);
+ DisplayDword(TEXT("security"), psv2->sv2_security);
+ DisplayDword(TEXT("auditing"), psv2->sv2_auditing);
+ DisplayDword(TEXT("numadmin"), psv2->sv2_numadmin);
+ DisplayDword(TEXT("lanmask"), psv2->sv2_lanmask);
+ DisplayDword(TEXT("hidden"), psv2->sv2_hidden);
+ DisplayDword(TEXT("announce"), psv2->sv2_announce);
+ DisplayDword(TEXT("anndelta"), psv2->sv2_anndelta);
+ DisplayString(TEXT("guestacct"), psv2->sv2_guestacct);
+ DisplayLicenses(
+ psv2->sv2_version_major,
+ psv2->sv2_licenses );
+ DisplayString(TEXT("userpath"), psv2->sv2_userpath);
+ DisplayDword(TEXT("chdevs"), psv2->sv2_chdevs);
+ DisplayDword(TEXT("chdevq"), psv2->sv2_chdevq);
+ DisplayDword(TEXT("chdevjobs"), psv2->sv2_chdevjobs);
+ DisplayDword(TEXT("connections"), psv2->sv2_connections);
+ DisplayDword(TEXT("shares"), psv2->sv2_shares);
+ DisplayDword(TEXT("openfiles"), psv2->sv2_openfiles);
+ DisplayDword(TEXT("sessopens"), psv2->sv2_sessopens);
+ DisplayDword(TEXT("sessvcs"), psv2->sv2_sessvcs);
+ DisplayDword(TEXT("sessreqs"), psv2->sv2_sessreqs);
+ DisplayDword(TEXT("opensearch"), psv2->sv2_opensearch);
+ DisplayDword(TEXT("activelocks"), psv2->sv2_activelocks);
+ DisplayDword(TEXT("numreqbuf"), psv2->sv2_numreqbuf);
+ DisplayDword(TEXT("sizreqbuf"), psv2->sv2_sizreqbuf);
+ DisplayDword(TEXT("numbigbuf"), psv2->sv2_numbigbuf);
+ DisplayDword(TEXT("numfiletasks"), psv2->sv2_numfiletasks);
+ DisplayDword(TEXT("alertsched"), psv2->sv2_alertsched);
+ DisplayDword(TEXT("erroralert"), psv2->sv2_erroralert);
+ DisplayDword(TEXT("logonalert"), psv2->sv2_logonalert);
+ DisplayDword(TEXT("accessalert"), psv2->sv2_accessalert);
+ DisplayDword(TEXT("diskalert"), psv2->sv2_diskalert);
+ DisplayDword(TEXT("netioalert"), psv2->sv2_netioalert);
+ DisplayDword(TEXT("maxauditsz"), psv2->sv2_maxauditsz);
+ DisplayString(TEXT("srvheuristics"), psv2->sv2_srvheuristics);
+ }
+ break;
+
+ case 3 :
+ {
+ LPSERVER_INFO_3 psv3 = Info;
+ DisplayString(TEXT("name"), psv3->sv3_name);
+ DisplayLanManVersion(
+ psv3->sv3_version_major,
+ psv3->sv3_version_minor);
+ DisplayServerType( psv3->sv3_type );
+ DisplayString(TEXT("comment"), psv3->sv3_comment);
+ DisplayDword(TEXT("ulist_mtime"), psv3->sv3_ulist_mtime);
+ DisplayDword(TEXT("glist_mtime"), psv3->sv3_glist_mtime);
+ DisplayDword(TEXT("alist_mtime"), psv3->sv3_alist_mtime);
+ DisplayDword(TEXT("users"), psv3->sv3_users);
+ DisplayDisconnectTime( psv3->sv3_disc );
+ DisplayString(TEXT("alerts"), psv3->sv3_alerts);
+ DisplayDword(TEXT("security"), psv3->sv3_security);
+ DisplayDword(TEXT("auditing"), psv3->sv3_auditing);
+ DisplayDword(TEXT("numadmin"), psv3->sv3_numadmin);
+ DisplayDword(TEXT("lanmask"), psv3->sv3_lanmask);
+ DisplayDword(TEXT("hidden"), psv3->sv3_hidden);
+ DisplayDword(TEXT("announce"), psv3->sv3_announce);
+ DisplayDword(TEXT("anndelta"), psv3->sv3_anndelta);
+ DisplayString(TEXT("guestacct"), psv3->sv3_guestacct);
+ DisplayLicenses(
+ psv3->sv3_version_major,
+ psv3->sv3_licenses );
+ DisplayString(TEXT("userpath"), psv3->sv3_userpath);
+ DisplayDword(TEXT("chdevs"), psv3->sv3_chdevs);
+ DisplayDword(TEXT("chdevq"), psv3->sv3_chdevq);
+ DisplayDword(TEXT("chdevjobs"), psv3->sv3_chdevjobs);
+ DisplayDword(TEXT("connections"), psv3->sv3_connections);
+ DisplayDword(TEXT("shares"), psv3->sv3_shares);
+ DisplayDword(TEXT("openfiles"), psv3->sv3_openfiles);
+ DisplayDword(TEXT("sessopens"), psv3->sv3_sessopens);
+ DisplayDword(TEXT("sessvcs"), psv3->sv3_sessvcs);
+ DisplayDword(TEXT("sessreqs"), psv3->sv3_sessreqs);
+ DisplayDword(TEXT("opensearch"), psv3->sv3_opensearch);
+ DisplayDword(TEXT("activelocks"), psv3->sv3_activelocks);
+ DisplayDword(TEXT("numreqbuf"), psv3->sv3_numreqbuf);
+ DisplayDword(TEXT("sizreqbuf"), psv3->sv3_sizreqbuf);
+ DisplayDword(TEXT("numbigbuf"), psv3->sv3_numbigbuf);
+ DisplayDword(TEXT("numfiletasks"), psv3->sv3_numfiletasks);
+ DisplayDword(TEXT("alertsched"), psv3->sv3_alertsched);
+ DisplayDword(TEXT("erroralert"), psv3->sv3_erroralert);
+ DisplayDword(TEXT("logonalert"), psv3->sv3_logonalert);
+ DisplayDword(TEXT("accessalert"), psv3->sv3_accessalert);
+ DisplayDword(TEXT("diskalert"), psv3->sv3_diskalert);
+ DisplayDword(TEXT("netioalert"), psv3->sv3_netioalert);
+ DisplayDword(TEXT("maxauditsz"), psv3->sv3_maxauditsz);
+ DisplayString(TEXT("srvheuristics"), psv3->sv3_srvheuristics);
+ DisplayDword(TEXT("auditedevents"), psv3->sv3_auditedevents);
+ DisplayDword(TEXT("autoprofile"), psv3->sv3_autoprofile);
+ DisplayString(TEXT("autopath"), psv3->sv3_autopath);
+ }
+ break;
+
+ case 100 :
+ {
+ LPSERVER_INFO_100 psv100 = Info;
+ DisplayPlatformId( psv100->sv100_platform_id );
+ DisplayString(TEXT("Server Name"), psv100->sv100_name);
+ }
+ break;
+
+ case 101 :
+ {
+ LPSERVER_INFO_101 psv101 = Info;
+ DisplayPlatformId( psv101->sv101_platform_id );
+ DisplayString(TEXT("Server Name"), psv101->sv101_name);
+ DisplayLanManVersion(
+ psv101->sv101_version_major,
+ psv101->sv101_version_minor);
+ DisplayServerType( psv101->sv101_type );
+ DisplayString(TEXT( "Server Comment"), psv101->sv101_comment);
+ }
+ break;
+
+ case 102 :
+ {
+ LPSERVER_INFO_102 psv102 = Info;
+ DisplayPlatformId( psv102->sv102_platform_id );
+ DisplayString(TEXT("Server Name"), psv102->sv102_name);
+ DisplayLanManVersion(
+ psv102->sv102_version_major,
+ psv102->sv102_version_minor );
+ DisplayServerType( psv102->sv102_type );
+ DisplayString(TEXT( "Server Comment"), psv102->sv102_comment );
+ DisplayDword(TEXT( "users"), psv102->sv102_users );
+ DisplayBool(TEXT( "Server hidden"), psv102->sv102_hidden );
+ DisplayDword(TEXT( "announce"), psv102->sv102_announce );
+ DisplayDword(TEXT( "announce delta"), psv102->sv102_anndelta );
+ DisplayLicenses(
+ psv102->sv102_version_major,
+ psv102->sv102_licenses );
+ DisplayString(TEXT( "user path"), psv102->sv102_userpath );
+ }
+ break;
+
+ case 402 :
+ {
+ LPSERVER_INFO_402 psv402 = Info;
+ DisplayDword(TEXT("ulist mtime"), psv402->sv402_ulist_mtime);
+ DisplayDword(TEXT("glist mtime"), psv402->sv402_glist_mtime);
+ DisplayDword(TEXT("alist mtime"), psv402->sv402_alist_mtime);
+ DisplayString(TEXT("alerts"), psv402->sv402_alerts);
+ DisplayDword(TEXT("security"), psv402->sv402_security);
+ DisplayDword(TEXT("numadmin"), psv402->sv402_numadmin);
+ DisplayDwordHex(TEXT("lanmask"), psv402->sv402_lanmask);
+ DisplayString(TEXT("guestacct"), psv402->sv402_guestacct);
+ DisplayDword(TEXT("chdevs"), psv402->sv402_chdevs);
+ DisplayDword(TEXT("chdevq"), psv402->sv402_chdevq);
+ DisplayDword(TEXT("chdevjobs"), psv402->sv402_chdevjobs);
+ DisplayDword(TEXT("connections"), psv402->sv402_connections);
+ DisplayDword(TEXT("shares"), psv402->sv402_shares);
+ DisplayDword(TEXT("openfiles"), psv402->sv402_openfiles);
+ DisplayDword(TEXT("sessopens"), psv402->sv402_sessopens);
+ DisplayDword(TEXT("sessvcs"), psv402->sv402_sessvcs);
+ DisplayDword(TEXT("sessreqs"), psv402->sv402_sessreqs);
+ DisplayDword(TEXT("opensearch"), psv402->sv402_opensearch);
+ DisplayDword(TEXT("activelocks"), psv402->sv402_activelocks);
+ DisplayDword(TEXT("numreqbuf"), psv402->sv402_numreqbuf);
+ DisplayDword(TEXT("sizreqbuf"), psv402->sv402_sizreqbuf);
+ DisplayDword(TEXT("numbigbuf"), psv402->sv402_numbigbuf);
+ DisplayDword(TEXT("numfiletasks"), psv402->sv402_numfiletasks);
+ DisplayDword(TEXT("alertsched"), psv402->sv402_alertsched);
+ DisplayDword(TEXT("erroralert"), psv402->sv402_erroralert);
+ DisplayDword(TEXT("logonalert"), psv402->sv402_logonalert);
+ DisplayDword(TEXT("diskalert"), psv402->sv402_diskalert);
+ DisplayDword(TEXT("accessalert"), psv402->sv402_accessalert);
+ DisplayDword(TEXT("diskalert"), psv402->sv402_diskalert);
+ DisplayDword(TEXT("netioalert"), psv402->sv402_netioalert);
+ DisplayDword(TEXT("maxauditsz"), psv402->sv402_maxauditsz);
+ DisplayString(TEXT("srvheuristics"), psv402->sv402_srvheuristics);
+ }
+ break;
+
+ case 403 :
+ {
+ LPSERVER_INFO_403 psv403 = Info;
+ DisplayDword(TEXT("ulist mtime"), psv403->sv403_ulist_mtime);
+ DisplayDword(TEXT("glist mtime"), psv403->sv403_glist_mtime);
+ DisplayDword(TEXT("alist mtime"), psv403->sv403_alist_mtime);
+ DisplayString(TEXT("alerts"), psv403->sv403_alerts);
+ DisplayDword(TEXT("security"), psv403->sv403_security);
+ DisplayDword(TEXT("numadmin"), psv403->sv403_numadmin);
+ DisplayDwordHex(TEXT("lanmask"), psv403->sv403_lanmask);
+ DisplayString(TEXT("guestacct"), psv403->sv403_guestacct);
+ DisplayDword(TEXT("chdevs"), psv403->sv403_chdevs);
+ DisplayDword(TEXT("chdevq"), psv403->sv403_chdevq);
+ DisplayDword(TEXT("chdevjobs"), psv403->sv403_chdevjobs);
+ DisplayDword(TEXT("connections"), psv403->sv403_connections);
+ DisplayDword(TEXT("shares"), psv403->sv403_shares);
+ DisplayDword(TEXT("openfiles"), psv403->sv403_openfiles);
+ DisplayDword(TEXT("sessopens"), psv403->sv403_sessopens);
+ DisplayDword(TEXT("sessvcs"), psv403->sv403_sessvcs);
+ DisplayDword(TEXT("sessreqs"), psv403->sv403_sessreqs);
+ DisplayDword(TEXT("opensearch"), psv403->sv403_opensearch);
+ DisplayDword(TEXT("activelocks"), psv403->sv403_activelocks);
+ DisplayDword(TEXT("numreqbuf"), psv403->sv403_numreqbuf);
+ DisplayDword(TEXT("sizreqbuf"), psv403->sv403_sizreqbuf);
+ DisplayDword(TEXT("numbigbuf"), psv403->sv403_numbigbuf);
+ DisplayDword(TEXT("numfiletasks"), psv403->sv403_numfiletasks);
+ DisplayDword(TEXT("alertsched"), psv403->sv403_alertsched);
+ DisplayDword(TEXT("erroralert"), psv403->sv403_erroralert);
+ DisplayDword(TEXT("logonalert"), psv403->sv403_logonalert);
+ DisplayDword(TEXT("diskalert"), psv403->sv403_diskalert);
+ DisplayDword(TEXT("accessalert"), psv403->sv403_accessalert);
+ DisplayDword(TEXT("diskalert"), psv403->sv403_diskalert);
+ DisplayDword(TEXT("netioalert"), psv403->sv403_netioalert);
+ DisplayDword(TEXT("maxauditsz"), psv403->sv403_maxauditsz);
+ DisplayString(TEXT("srvheuristics"), psv403->sv403_srvheuristics);
+ DisplayDword(TEXT("auditedevents"), psv403->sv403_auditedevents);
+ DisplayDword(TEXT("autoprofile"), psv403->sv403_autoprofile);
+ DisplayString(TEXT("autopath"), psv403->sv403_autopath);
+ }
+ break;
+
+ // BUGBUG: RpcXlate doesn't need support for info levels 502, 503, 599.
+ // Feel free to add them here if you need them.
+
+ default :
+ NetpAssert(FALSE);
+ }
+
+} // DisplayServerInfo
+
diff --git a/private/net/svcdlls/browser/client/bwatch.c b/private/net/svcdlls/browser/client/bwatch.c
new file mode 100644
index 000000000..4de239a54
--- /dev/null
+++ b/private/net/svcdlls/browser/client/bwatch.c
@@ -0,0 +1,351 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ bwatch.c
+
+Abstract:
+
+ Prints out interesting browser related events for a domain
+
+Author:
+
+ Dan Hinsley (DanHi) 10-Oct-1992
+
+Environment:
+
+ Application mode
+
+Revision History:
+
+--*/
+#define INCLUDE_SMB_TRANSACTION
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windows.h>
+#include <lmcons.h>
+
+#include <nb30.h>
+#include <smbtypes.h>
+#include <smb.h>
+#include <smbgtpt.h>
+#include <hostannc.h>
+#include <stdio.h>
+#include <string.h>
+
+#define SPACES " "
+
+#define ClearNcb( PNCB ) { \
+ RtlZeroMemory( PNCB , sizeof (NCB) ); \
+ RtlCopyMemory( (PNCB)->ncb_name, SPACES, sizeof(SPACES)-1 );\
+ RtlCopyMemory( (PNCB)->ncb_callname, SPACES, sizeof(SPACES)-1 );\
+ }
+
+//
+// Globals
+//
+
+NCB myncb;
+CHAR localName[16];
+ULONG lanNumber=0;
+CHAR DecodedName[20];
+
+//
+// Functions
+//
+
+VOID AddName(UCHAR Suffix);
+VOID Reset(UCHAR Lsn);
+VOID DecodeName(LPSTR DecodedName, LPSTR EncodedName);
+BOOL DecodeSmb(PBYTE Smb);
+
+VOID
+usage (
+ LPSTR CommandName
+ )
+{
+ printf("usage:\n\t%s [-n #] Domain\n", CommandName);
+ printf("\n\t-n:# supply lana number, default 0\n\n");
+}
+
+_cdecl
+main (int argc, char *argv[])
+{
+ CHAR Buffer2[512];
+ int i;
+ UCHAR name_number;
+ USHORT length;
+
+ if ( argc < 2 || argc > 3) {
+ usage (argv[0]);
+ return 1;
+ }
+
+ //
+ // Clear out the name
+ //
+
+ for (i = 0; i < 16 ;i++ ) {
+ localName[i] = ' ';
+ }
+
+ //
+ // parse the switches
+ //
+
+ for (i=1;i<argc ;i++ ) {
+ if (argv[i][0] == '-') {
+ switch (argv[i][1]) {
+ case 'n':
+ if (!NT_SUCCESS(RtlCharToInteger (&argv[i][3], 10, &lanNumber))) {
+ usage (argv[0]);
+ return 1;
+ }
+ break;
+
+ default:
+ usage (argv[0]);
+ return 1;
+ break;
+
+ }
+
+ } else {
+
+ //
+ // not a switch must be a name
+ //
+
+ RtlCopyMemory (localName, argv[i], strlen( argv[i] ));
+ _strupr(localName);
+ }
+ }
+
+ printf("Starting to watch domain %s on lana %d\n", localName, lanNumber);
+
+
+ //
+ // Reset the adapter
+ //
+
+ Reset(FALSE); // Really reset the adapter.
+
+ // AddName for all possible names for this domain
+ AddName(0x1e);
+ AddName(0);
+ AddName(' ');
+
+ // Jam in Domain annoucement name
+ strcpy(&localName[2], "__MSBROWSE__");
+ localName[0] = 0x01;
+ localName[1] = 0x02;
+ localName[14] = 0x02;
+ localName[15] = 0x01;
+ RtlCopyMemory( myncb.ncb_name, localName, 16);
+ myncb.ncb_lana_num = (UCHAR)lanNumber;
+ Netbios( &myncb );
+
+ if ( myncb.ncb_retcode != NRC_GOODRET ) {
+ printf( "AddGroupname MSBROWSE returned an error %lx\n", myncb.ncb_retcode );
+ }
+
+ // Receive Datagram
+ printf( "Listening for Datagrams\n" );
+ while (1) {
+ ClearNcb( &myncb );
+ myncb.ncb_command = NCBDGRECV;
+ myncb.ncb_lana_num = (UCHAR)lanNumber;
+ myncb.ncb_num = 0xff;
+ myncb.ncb_length = sizeof( Buffer2 );
+ myncb.ncb_buffer = Buffer2;
+ Netbios( &myncb );
+ if ( myncb.ncb_retcode != NRC_GOODRET ) {
+ printf( "ReceiveDatagram returned an error %lx\n", myncb.ncb_retcode );
+ if (myncb.ncb_retcode != NRC_INCOMP) {
+ return 1;
+ }
+ }
+
+ //
+ // Find first blank
+ //
+
+ for (i = 0; i < 15 ; i++) {
+ if (myncb.ncb_callname[i] == ' ') {
+ break;
+ }
+ }
+
+ DecodeName(DecodedName, myncb.ncb_callname);
+
+ DecodeSmb(myncb.ncb_buffer);
+ }
+
+ //
+ // Reset the adapter on the way out
+ //
+
+ Reset(TRUE);
+
+ return 0;
+}
+
+VOID
+AddName(
+ UCHAR Suffix
+ )
+{
+ ClearNcb( &myncb );
+ myncb.ncb_command = NCBADDGRNAME;
+ localName[15] = Suffix;
+ RtlCopyMemory( myncb.ncb_name, localName, 16);
+ myncb.ncb_lana_num = (UCHAR)lanNumber;
+ Netbios( &myncb );
+ if ( myncb.ncb_retcode != NRC_GOODRET ) {
+ printf( "AddGroupname 0x%x returned an error %lx\n", Suffix,
+ myncb.ncb_retcode);
+ return;
+ }
+}
+
+VOID
+Reset(
+ UCHAR Reset
+ )
+{
+ ClearNcb( &myncb );
+ myncb.ncb_command = NCBRESET;
+ myncb.ncb_lsn = Reset;
+ myncb.ncb_lana_num = lanNumber;
+ myncb.ncb_callname[0] = myncb.ncb_callname[1] = myncb.ncb_callname[2] =
+ myncb.ncb_callname[3] = 0;
+ Netbios( &myncb );
+ if ( myncb.ncb_retcode != NRC_GOODRET ) {
+ printf( "Reset returned an error %lx\n", myncb.ncb_retcode);
+ return;
+ }
+}
+
+VOID
+DecodeName(
+ LPSTR DecodedName,
+ LPSTR EncodedName
+
+ )
+{
+
+ CHAR TempString[6];
+ int i;
+
+ //
+ // Find first blank
+ //
+
+ for (i = 0; i < 15 ; i++) {
+ if (EncodedName[i] == ' ') {
+ break;
+ }
+ }
+
+ strncpy(DecodedName, EncodedName, i);
+ DecodedName[i] = '\0';
+ sprintf(TempString, "<%x>", EncodedName[15]);
+ strcat(DecodedName, TempString);
+
+}
+
+BOOL
+DecodeSmb(
+ PBYTE Smb
+ )
+{
+ LPSTR MailslotName;
+ PUCHAR pPacketType;
+ PNT_SMB_HEADER pSmbHeader;
+ PREQ_TRANSACTION pSmbTransaction;
+ PBROWSE_ANNOUNCE_PACKET pBrowseAnnouncePacket;
+ PREQUEST_ELECTION pRequestElection;
+ PBECOME_BACKUP pBecomeBackup;
+ PREQUEST_ANNOUNCE_PACKET pRequestAnnouncement;
+
+ //
+ // Decipher the SMB in the packet
+ //
+
+ pSmbHeader = (PNT_SMB_HEADER) Smb;
+ if (pSmbHeader->Protocol[0] != 0xff &&
+ pSmbHeader->Protocol[1] != 'S' &&
+ pSmbHeader->Protocol[2] != 'M' &&
+ pSmbHeader->Protocol[3] != 'B') {
+ printf("Not a valid SMB header\n");
+ return(FALSE);
+ }
+ pSmbTransaction = (PREQ_TRANSACTION)
+ (Smb + sizeof(NT_SMB_HEADER));
+
+ MailslotName = (LPSTR) (pSmbTransaction->Buffer +
+ pSmbTransaction->SetupCount + 5);
+
+ if (!strcmp(MailslotName, "\\MAILSLOT\\BROWSE")) {
+ pPacketType = (PUCHAR) myncb.ncb_buffer +
+ pSmbTransaction->DataOffset;
+ switch (*pPacketType) {
+
+ case AnnouncementRequest:
+ printf("Announcement request from %s. ", DecodedName);
+ pRequestAnnouncement =
+ (PREQUEST_ANNOUNCE_PACKET) pPacketType;
+ printf("Reply %s\n",
+ pRequestAnnouncement->RequestAnnouncement.Reply);
+ break;
+
+ case Election:
+ printf("Election request: %s ", DecodedName);
+ pRequestElection = (PREQUEST_ELECTION) pPacketType;
+ printf("Version(%d) Criteria(0x%x) ",
+ pRequestElection->ElectionRequest.Version,
+ pRequestElection->ElectionRequest.Criteria);
+ printf("TimeUp(%d)\n",
+ pRequestElection->ElectionRequest.TimeUp);
+ break;
+
+ case BecomeBackupServer:
+ printf("BecomeBackupServer from %s ", DecodedName);
+ pBecomeBackup = (PBECOME_BACKUP) pPacketType;
+ printf("to %s\n", pBecomeBackup->BecomeBackup.BrowserToPromote);
+ break;
+
+ case LocalMasterAnnouncement:
+ case WkGroupAnnouncement:
+ pBrowseAnnouncePacket =
+ (PBROWSE_ANNOUNCE_PACKET) pPacketType;
+ switch (*pPacketType) {
+ case LocalMasterAnnouncement:
+ printf("LocalMasterAnnouncement from %s. ", DecodedName);
+ break;
+ case WkGroupAnnouncement:
+ printf("Workgroup Announcement from %s. ", DecodedName);
+ break;
+ }
+ printf("UpdateCount = %d\n",
+ pBrowseAnnouncePacket->BrowseAnnouncement.UpdateCount);
+ break;
+
+ default:
+ printf("\n**** Packet type %d from %s ****\n",
+ *pPacketType, DecodedName);
+ }
+ }
+ else if (strcmp(MailslotName, "\\MAILSLOT\\NET\\REPL_CLI") &&
+ strcmp(MailslotName, "\\MAILSLOT\\NET\\NTLOGON") &&
+ strcmp(MailslotName, "\\MAILSLOT\\NET\\NETLOGON") &&
+ strcmp(MailslotName, "\\MAILSLOT\\LANMAN")) {
+ printf("Received an unknown datagram, name = %s\n",
+ MailslotName);
+ }
+
+ return(TRUE);
+}
diff --git a/private/net/svcdlls/browser/client/daytona/makefile b/private/net/svcdlls/browser/client/daytona/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/net/svcdlls/browser/client/daytona/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/net/svcdlls/browser/client/daytona/makefile.inc b/private/net/svcdlls/browser/client/daytona/makefile.inc
new file mode 100644
index 000000000..6a0c3862d
--- /dev/null
+++ b/private/net/svcdlls/browser/client/daytona/makefile.inc
@@ -0,0 +1,5 @@
+..\browstat.c: ..\browdeb.c
+
+obj\$(TARGET_DIRECTORY)\browdeb.res: ..\browdeb.rc
+
+obj\$(TARGET_DIRECTORY)\browstat.res: ..\browstat.rc
diff --git a/private/net/svcdlls/browser/client/daytona/sources b/private/net/svcdlls/browser/client/daytona/sources
new file mode 100644
index 000000000..38d56543f
--- /dev/null
+++ b/private/net/svcdlls/browser/client/daytona/sources
@@ -0,0 +1,86 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+
+Author:
+
+ Steve Wood (stevewo) 12-Apr-1989
+
+
+Revision History:
+
+!ENDIF
+
+MAJORCOMP = net
+MINORCOMP = browclient
+
+
+
+TARGETNAME=bowser
+TARGETPATH=obj
+TARGETTYPE=LIBRARY
+
+
+INCLUDES=..\..;..\..\common;..\..\..\..\inc;..\..\..\..\..\inc
+
+
+!IFNDEF DISABLE_NET_UNICODE
+UNICODE=1
+NET_C_DEFINES=-DUNICODE
+!ENDIF
+
+SOURCES=..\browstub.c \
+ ..\browbind.c \
+ ..\bowser_c.c
+
+C_DEFINES=-DRPC_NO_WINDOWS_H
+
+MSC_WARNING_LEVEL=/W3 /WX
+
+USE_CRTDLL=1
+
+UMTYPE=console
+UMAPPL=browdeb*browstat
+UMRES=$(@R).res
+UMTEST=browlist
+
+!if "$(CAIRO_PRODUCT)" == "1"
+UMLIBS=$(BASEDIR)\public\sdk\lib\cairo\*\netapi32.lib \
+ ..\..\common\cairo\obj\*\utils.obj \
+ ..\..\common\cairo\obj\*\interim.obj \
+ ..\..\server\cairo\obj\*\brwins.obj \
+! ifndef NTNOPCH
+ ..\..\server\cairo\obj\*\precomp.obj
+! endif
+!else
+UMLIBS=$(BASEDIR)\public\sdk\lib\*\netapi32.lib \
+ ..\..\common\daytona\obj\*\utils.obj \
+ ..\..\common\daytona\obj\*\interim.obj \
+ ..\..\server\daytona\obj\*\brwins.obj \
+! ifndef NTNOPCH
+ ..\..\server\daytona\obj\*\precomp.obj
+! endif
+!endif
+
+UMLIBS = $(UMLIBS) \
+ $(BASEDIR)\public\sdk\lib\*\rpcutil.lib \
+ $(BASEDIR)\public\sdk\lib\*\ntdll.lib \
+ $(BASEDIR)\public\sdk\lib\*\netlib.lib \
+ $(BASEDIR)\public\sdk\lib\*\advapi32.lib
+
+NTTARGETFILE0=..\browdeb.c \
+ obj\*\browdeb.res \
+ obj\*\browstat.res
+
diff --git a/private/net/svcdlls/browser/client/dirs b/private/net/svcdlls/browser/client/dirs
new file mode 100644
index 000000000..945735ae2
--- /dev/null
+++ b/private/net/svcdlls/browser/client/dirs
@@ -0,0 +1,25 @@
+!IF 0
+
+Copyright (c) 1994 Microsoft Corporation
+
+Module Name:
+
+ dirs.
+
+Abstract:
+
+ This file specifies the subdirectories of the current directory that
+ contain component makefiles. This was necessitated by the need to
+ build a and nt rdr.
+
+Author:
+
+ Milan Shah (April 20, 1994)
+
+NOTE: Commented description of this file is in \nt\bak\bin\dirs.tpl
+
+!ENDIF
+
+DIRS=daytona
+
+OPTIONAL_DIRS=
diff --git a/private/net/svcdlls/browser/common/daytona/makefile b/private/net/svcdlls/browser/common/daytona/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/net/svcdlls/browser/common/daytona/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/net/svcdlls/browser/common/daytona/sources b/private/net/svcdlls/browser/common/daytona/sources
new file mode 100644
index 000000000..704119fff
--- /dev/null
+++ b/private/net/svcdlls/browser/common/daytona/sources
@@ -0,0 +1,55 @@
+
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+
+Author:
+
+ Steve Wood (stevewo) 12-Apr-1989
+
+
+Revision History:
+
+!ENDIF
+
+MAJORCOMP = net
+MINORCOMP = browclient
+
+
+NTPROFILEINPUT=YES
+
+TARGETNAME=brcommon
+TARGETPATH=obj
+TARGETTYPE=LIBRARY
+
+
+INCLUDES=..\..;..\..\..\..\inc;..\..\..\..\..\inc
+
+
+NTPROFILEINPUT=1
+
+!IFNDEF DISABLE_NET_UNICODE
+UNICODE=1
+NET_C_DEFINES=-DUNICODE
+!ENDIF
+
+SOURCES=..\interim.c \
+ ..\utils.c
+
+C_DEFINES=-DRPC_NO_WINDOWS_H
+
+MSC_WARNING_LEVEL=/W3 /WX
+
+
diff --git a/private/net/svcdlls/browser/common/dirs b/private/net/svcdlls/browser/common/dirs
new file mode 100644
index 000000000..945735ae2
--- /dev/null
+++ b/private/net/svcdlls/browser/common/dirs
@@ -0,0 +1,25 @@
+!IF 0
+
+Copyright (c) 1994 Microsoft Corporation
+
+Module Name:
+
+ dirs.
+
+Abstract:
+
+ This file specifies the subdirectories of the current directory that
+ contain component makefiles. This was necessitated by the need to
+ build a and nt rdr.
+
+Author:
+
+ Milan Shah (April 20, 1994)
+
+NOTE: Commented description of this file is in \nt\bak\bin\dirs.tpl
+
+!ENDIF
+
+DIRS=daytona
+
+OPTIONAL_DIRS=
diff --git a/private/net/svcdlls/browser/common/interim.c b/private/net/svcdlls/browser/common/interim.c
new file mode 100644
index 000000000..55dc959cc
--- /dev/null
+++ b/private/net/svcdlls/browser/common/interim.c
@@ -0,0 +1,976 @@
+#include <nt.h> // DbgPrint prototype
+#include <ntrtl.h> // DbgPrint
+#include <nturtl.h> // Needed by winbase.h
+
+#include <windef.h> // DWORD
+#include <winbase.h> // LocalFree
+
+#include <rpcutil.h> // GENERIC_ENUM_STRUCT
+
+#include <lmcons.h> // NET_API_STATUS
+#include <lmerr.h> // NetError codes
+#include <lmremutl.h> // SUPPORTS_RPC
+
+#include <bowser.h> // generated by the MIDL complier
+#include <brnames.h> // Service and interface names
+
+#include <netlib.h>
+#include <netdebug.h>
+
+#include <winsvc.h>
+
+#include <lmserver.h>
+#include <tstr.h>
+
+#include <ntddbrow.h>
+#include <brcommon.h> // Routines common between client & server
+
+VOID
+UpdateInterimServerListElement(
+ IN PINTERIM_SERVER_LIST ServerList,
+ IN PINTERIM_ELEMENT Element,
+ IN ULONG Level,
+ IN BOOLEAN NewElement
+ );
+
+PINTERIM_ELEMENT
+AllocateInterimServerListEntry(
+ IN PSERVER_INFO_101 ServerInfo,
+ IN ULONG Level
+ );
+
+ NET_API_STATUS
+MergeServerList(
+ IN OUT PINTERIM_SERVER_LIST InterimServerList,
+ IN ULONG Level,
+ IN PVOID NewServerList,
+ IN ULONG NewEntriesRead,
+ IN ULONG NewTotalEntries
+ )
+/*++
+
+Routine Description:
+
+ This function will merge two server lists. It will reallocate the buffer
+ for the old list if appropriate.
+
+Arguments:
+
+ IN OUT PINTERIM_SERVER_LIST InterimServerList - Supplies an interim server list to merge into.
+
+ IN ULONG Level - Supplies the level of the list (100 or 101). Special
+ level 1010 is really level 101 with the special semantic that no
+ fields from this the NewServerList override existing fields in the
+ InterimServerList.
+
+ IN ULONG NewServerList - Supplies the list to merge into the interim list
+
+ IN ULONG NewEntriesRead - Supplies the entries read in the list.
+
+ IN ULONG NewTotalEntries - Supplies the total entries available in the list.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ ULONG i;
+ ULONG ServerElementSize;
+ PSERVER_INFO_101 ServerInfo = NewServerList;
+ PINTERIM_ELEMENT InterimEntry = NULL;
+ PLIST_ENTRY InterimList;
+ PINTERIM_ELEMENT NewElement = NULL;
+
+
+ if (Level == 100) {
+ ServerElementSize = sizeof(SERVER_INFO_100);
+ } else if ( Level == 101 || Level == 1010 ) {
+ ServerElementSize = sizeof(SERVER_INFO_101);
+ } else {
+ return(ERROR_INVALID_LEVEL);
+ }
+
+ //
+ // Early out if no entries in list.
+ //
+
+ if (NewEntriesRead == 0) {
+ return NERR_Success;
+ }
+
+ PrepareServerListForMerge(NewServerList, Level, NewEntriesRead);
+
+ InterimList = InterimServerList->ServerList.Flink;
+
+ //
+ // Walk the existing structure, packing it into an interim element, and
+ // sticking the element into the interim table.
+ //
+
+ for (i = 0; i < NewEntriesRead; i ++) {
+ BOOLEAN EntryInserted = FALSE;
+
+ //
+ // Walk forward in the interim element and find the appropriate place
+ // to insert this element.
+ //
+
+ while (InterimList != &InterimServerList->ServerList) {
+
+ LONG CompareResult;
+
+ InterimEntry = CONTAINING_RECORD(InterimList, INTERIM_ELEMENT, NextElement);
+
+// KdPrint(("MergeServerList: Compare %ws and %ws\n", NewElement->Name, InterimEntry->Name));
+
+#if DBG
+ //
+ // Make sure that this entry is lexically less than the next
+ // entry.
+ //
+
+ {
+ PLIST_ENTRY NextList = InterimList->Flink;
+ PINTERIM_ELEMENT NextEntry = CONTAINING_RECORD(NextList, INTERIM_ELEMENT, NextElement);
+
+ if (NextList != &InterimServerList->ServerList) {
+ ASSERT (STRCMP(InterimEntry->Name, NextEntry->Name) < 0);
+ }
+
+ //
+ // Now make sure that the input buffer also doesn't contain
+ // duplicate entries.
+ //
+
+ if (i < (NewEntriesRead-1)) {
+ PSERVER_INFO_101 NextServerInfo = (PSERVER_INFO_101)((PCHAR)ServerInfo+ServerElementSize);
+
+ ASSERT (STRCMP(ServerInfo->sv101_name, NextServerInfo->sv101_name) <= 0);
+ }
+
+ }
+#endif
+
+ CompareResult = STRCMP(ServerInfo->sv101_name, InterimEntry->Name);
+
+ if (CompareResult == 0) {
+
+// KdPrint(("MergeServerList: Elements equal - update\n"));
+
+ //
+ // If the new information should override the old information,
+ // copy it on top of the new info.
+ //
+ if ( Level != 1010 ) {
+ InterimEntry->PlatformId = ServerInfo->sv101_platform_id;
+
+ if (Level >= 101) {
+ InterimEntry->MajorVersionNumber = ServerInfo->sv101_version_major;
+
+ InterimEntry->MinorVersionNumber = ServerInfo->sv101_version_minor;
+
+ InterimEntry->Type = ServerInfo->sv101_type;
+
+ InterimServerList->TotalBytesNeeded -= STRLEN(InterimEntry->Comment) * sizeof(TCHAR) + sizeof(TCHAR);
+
+ STRCPY(InterimEntry->Comment, ServerInfo->sv101_comment);
+
+ InterimServerList->TotalBytesNeeded += STRLEN(InterimEntry->Comment) * sizeof(TCHAR) + sizeof(TCHAR);
+
+ }
+ }
+
+ UpdateInterimServerListElement(InterimServerList, InterimEntry, Level, FALSE);
+
+ EntryInserted = TRUE;
+
+ break;
+
+ } else if (CompareResult > 0) {
+
+// KdPrint(("MergeServerList: Elements greater. Skip element\n"));
+
+ InterimList = InterimList->Flink;
+
+ } else {
+
+ NewElement = AllocateInterimServerListEntry(ServerInfo, Level);
+
+ if (NewElement == NULL) {
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+// KdPrint(("MergeServerList: Elements less. Insert at end\n"));
+
+ //
+ // The new entry is < than the previous entry. Insert it
+ // before this entry.
+ //
+
+ InsertTailList(&InterimEntry->NextElement, &NewElement->NextElement);
+
+ //
+ // Skip to the next element in the list.
+ //
+
+ InterimList = &NewElement->NextElement;
+
+ UpdateInterimServerListElement(InterimServerList, NewElement, Level, TRUE);
+
+ EntryInserted = TRUE;
+
+ break;
+ }
+ }
+
+ if (!EntryInserted &&
+ (InterimList == &InterimServerList->ServerList)) {
+
+ NewElement = AllocateInterimServerListEntry(ServerInfo, Level);
+
+ if (NewElement == NULL) {
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+// KdPrint(("MergeServerList: Insert %ws at end of list\n", NewElement->Name));
+
+ InsertTailList(&InterimServerList->ServerList, &NewElement->NextElement);
+
+ InterimList = &NewElement->NextElement;
+
+ UpdateInterimServerListElement(InterimServerList, NewElement, Level, TRUE);
+
+ }
+
+ ServerInfo = (PSERVER_INFO_101)((PCHAR)ServerInfo+ServerElementSize);
+ }
+
+#if 0
+ {
+ PLIST_ENTRY InterimList;
+ ULONG TotalNeededForList = 0;
+
+ for (InterimList = InterimServerList->ServerList.Flink ;
+ InterimList != &InterimServerList->ServerList ;
+ InterimList = InterimList->Flink ) {
+ ULONG ServerElementSize;
+
+ InterimEntry = CONTAINING_RECORD(InterimList, INTERIM_ELEMENT, NextElement);
+
+ if (Level == 100) {
+ ServerElementSize = sizeof(SERVER_INFO_100);
+ } else {
+ ServerElementSize = sizeof(SERVER_INFO_101);
+ }
+
+ ServerElementSize += STRLEN(InterimEntry->Name)*sizeof(TCHAR)+sizeof(TCHAR);
+
+ ServerElementSize += STRLEN(InterimEntry->Comment)*sizeof(TCHAR)+sizeof(TCHAR);
+
+// KdPrint(("MergeInterimServerList: %ws. %ld needed\n", InterimEntry->Name, ServerElementSize));
+
+ TotalNeededForList += ServerElementSize;
+ }
+
+ if (TotalNeededForList != InterimServerList->TotalBytesNeeded) {
+ KdPrint(("UpdateInterimServerList: Wrong number of bytes (%ld) for interim server list. %ld needed\n", InterimServerList->TotalBytesNeeded, TotalNeededForList));
+ }
+ }
+
+#endif
+// KdPrint(("%lx bytes needed to hold server list\n", InterimServerList->TotalBytesNeeded));
+
+ //
+ // Also, we had better have the whole table locally.
+ //
+
+ ASSERT (InterimServerList->EntriesRead == InterimServerList->TotalEntries);
+
+ return(NERR_Success);
+}
+
+ULONG
+_CRTAPI1
+CompareServerInfo(
+ const void * Param1,
+ const void * Param2
+ )
+{
+ const SERVER_INFO_100 * ServerInfo1 = Param1;
+ const SERVER_INFO_100 * ServerInfo2 = Param2;
+
+ return STRCMP(ServerInfo1->sv100_name, ServerInfo2->sv100_name);
+}
+
+VOID
+PrepareServerListForMerge(
+ IN PVOID ServerInfoList,
+ IN ULONG Level,
+ IN ULONG EntriesInList
+ )
+/*++
+
+Routine Description:
+
+ MergeServerList requires that the inputs to the list be in a strictly
+ sorted order. This routine guarantees that this list will be of
+ an "appropriate" form to be merged.
+
+Arguments:
+
+ IN PVOID ServerInfoList - Supplies the list to munge.
+
+ IN ULONG Level - Supplies the level of the list (100 or 101).
+ (Or level 1010 which is identical to level 101.)
+
+ IN ULONG EntriesInList - Supplies the number of entries in the list.
+
+Return Value:
+
+ None.
+
+Note:
+ In 99% of the cases, the list passed in will already be sorted. We want to
+ take the input list and first check to see if it is sorted. If it is,
+ we can return immediately. If it is not, we need to sort the list.
+
+ We don't just unilaterally sort the list, because the input is mostly
+ sorted anyway, and there are no good sorting algorithms that handle mostly
+ sorted inputs. Since we will see unsorted input only rarely (basically,
+ we will only see it from WfW machines), we just take the penalty of a worst
+ case quicksort if the input is unsorted.
+
+--*/
+
+{
+ LONG i;
+ ULONG ServerElementSize;
+ PSERVER_INFO_101 ServerInfo = ServerInfoList;
+ BOOLEAN MisOrderedList = FALSE;
+
+ ASSERT (Level == 100 || Level == 101 || Level == 1010);
+
+ //
+ // Figure out the size of each element.
+ //
+
+ if (Level == 100) {
+ ServerElementSize = sizeof(SERVER_INFO_100);
+ } else {
+ ServerElementSize = sizeof(SERVER_INFO_101);
+ }
+
+ //
+ // Next check to see if the input list is sorted.
+ //
+
+ for (i = 0 ; i < ((LONG)EntriesInList - 1) ; i += 1 ) {
+ PSERVER_INFO_101 NextServerInfo = (PSERVER_INFO_101)((PCHAR)ServerInfo+ServerElementSize);
+
+ if (STRCMP(ServerInfo->sv101_name, NextServerInfo->sv101_name) >= 0) {
+ MisOrderedList = TRUE;
+ break;
+ }
+
+ ServerInfo = NextServerInfo;
+ }
+
+ //
+ // This list is sorted. Return right away, it's fine.
+ //
+
+ if (!MisOrderedList) {
+ return;
+ }
+
+ //
+ // This list isn't sorted. We need to sort it.
+ //
+
+ qsort(ServerInfoList, EntriesInList, ServerElementSize, CompareServerInfo);
+
+
+}
+
+ PINTERIM_ELEMENT
+AllocateInterimServerListEntry(
+ IN PSERVER_INFO_101 ServerInfo,
+ IN ULONG Level
+ )
+{
+ PINTERIM_ELEMENT NewElement;
+
+ NewElement = MIDL_user_allocate(sizeof(INTERIM_ELEMENT));
+
+ if (NewElement == NULL) {
+ return NULL;
+ }
+
+ //
+ // Initialize TimeLastSeen and Periodicity.
+ //
+
+ NewElement->TimeLastSeen = 0;
+
+ NewElement->Periodicity = 0;
+
+ NewElement->PlatformId = ServerInfo->sv101_platform_id;
+
+ ASSERT (STRLEN(ServerInfo->sv101_name) <= CNLEN);
+
+ STRCPY(NewElement->Name, ServerInfo->sv101_name);
+
+ if (Level == 100) {
+ NewElement->MajorVersionNumber = 0;
+ NewElement->MinorVersionNumber = 0;
+ *NewElement->Comment = L'\0';
+ NewElement->Type = SV_TYPE_ALL;
+ } else {
+ NewElement->MajorVersionNumber = ServerInfo->sv101_version_major;
+
+ NewElement->MinorVersionNumber = ServerInfo->sv101_version_minor;
+
+ NewElement->Type = ServerInfo->sv101_type;
+
+ ASSERT (STRLEN(ServerInfo->sv101_comment) <= MAXCOMMENTSZ);
+
+ STRCPY(NewElement->Comment, ServerInfo->sv101_comment);
+
+ }
+
+ return NewElement;
+}
+
+
+VOID
+UpdateInterimServerListElement(
+ IN PINTERIM_SERVER_LIST InterimServerList,
+ IN PINTERIM_ELEMENT InterimElement,
+ IN ULONG Level,
+ IN BOOLEAN NewElement
+ )
+{
+#if 0
+ PINTERIM_ELEMENT InterimEntry;
+ ULONG TotalNeededForList = 0;
+#endif
+
+ //
+ // If this is a new element, update the size of the table to match.
+ //
+
+ if (NewElement) {
+ ULONG ServerElementSize;
+
+ if (Level == 100) {
+ ServerElementSize = sizeof(SERVER_INFO_100);
+ } else {
+ ServerElementSize = sizeof(SERVER_INFO_101);
+ }
+
+ InterimServerList->EntriesRead += 1;
+
+ ServerElementSize += STRLEN(InterimElement->Name)*sizeof(TCHAR)+sizeof(TCHAR);
+
+ if (Level == 100) {
+ ServerElementSize += sizeof(TCHAR);
+ } else {
+ ServerElementSize += STRLEN(InterimElement->Comment)*sizeof(TCHAR)+sizeof(TCHAR);
+ }
+
+ InterimServerList->TotalBytesNeeded += ServerElementSize;
+
+ InterimServerList->TotalEntries += 1;
+
+ if (InterimServerList->NewElementCallback != NULL) {
+ InterimServerList->NewElementCallback(InterimServerList,
+ InterimElement);
+ } else {
+ InterimElement->Periodicity = 0xffffffff;
+ InterimElement->TimeLastSeen = 0xffffffff;
+ }
+
+
+ } else {
+ if (InterimServerList->ExistingElementCallback != NULL) {
+ InterimServerList->ExistingElementCallback(InterimServerList,
+ InterimElement);
+ } else {
+ InterimElement->Periodicity = 0xffffffff;
+ InterimElement->TimeLastSeen = 0xffffffff;
+ }
+
+ }
+
+#if 0
+ {
+ PLIST_ENTRY InterimList;
+ ULONG TotalNeededForList = 0;
+
+ for (InterimList = InterimServerList->ServerList.Flink ;
+ InterimList != &InterimServerList->ServerList ;
+ InterimList = InterimList->Flink ) {
+ ULONG ServerElementSize;
+
+ InterimEntry = CONTAINING_RECORD(InterimList, INTERIM_ELEMENT, NextElement);
+
+ if (Level == 100) {
+ ServerElementSize = sizeof(SERVER_INFO_100);
+ } else {
+ ServerElementSize = sizeof(SERVER_INFO_101);
+ }
+
+ ServerElementSize += STRLEN(InterimEntry->Name)*sizeof(TCHAR)+sizeof(TCHAR);
+
+ ServerElementSize += STRLEN(InterimEntry->Comment)*sizeof(TCHAR)+sizeof(TCHAR);
+
+ TotalNeededForList += ServerElementSize;
+ }
+
+ if (TotalNeededForList != InterimServerList->TotalBytesNeeded) {
+ KdPrint(("UpdateInterimServerList: Wrong number of bytes (%ld) for interim server list. %ld needed\n", InterimServerList->TotalBytesNeeded, TotalNeededForList));
+ }
+ }
+
+#endif
+
+ return;
+
+}
+
+ NET_API_STATUS
+PackServerList(
+ IN PINTERIM_SERVER_LIST InterimServerList,
+ IN ULONG Level,
+ IN ULONG ServerType,
+ IN ULONG PreferedMaximumLength,
+ OUT PVOID *ServerList,
+ OUT PULONG EntriesRead,
+ OUT PULONG TotalEntries,
+ IN LPCWSTR FirstNameToReturn
+ )
+/*++
+
+Routine Description:
+
+ This function will take an interim server list and "pack" it into an array
+ of server_info_xxx structures.
+
+Arguments:
+
+ IN PINTERIM_SERVER_LIST InterimServerList - Supplies an interim server list to merge into.
+
+ IN ULONG Level - Supplies the level of the list (100 or 101).
+
+ IN ULONG ServerType - Supplies the type to filter on the list.
+
+ IN ULONG PreferedMaximumLength - Supplies the prefered size of the list.
+
+ OUT PVOID *ServerList - Where to put the destination list.
+
+ OUT PULONG EntriesEntries - Receives the entries packed in the list.
+
+ OUT PULONG TotalEntries - Receives the total entries available in the list.
+
+ FirstNameToReturn - Supplies the name of the first domain or server entry to return.
+ The caller can use this parameter to implement a resume handle of sorts by passing
+ the name of the last entry returned on a previous call. (Notice that the specified
+ entry will, also, be returned on this call unless it has since been deleted.)
+ Pass NULL to start with the first entry available.
+
+ Passed name must be the canonical form of the name.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ ULONG EntriesPacked = 0;
+ PLIST_ENTRY InterimList;
+ PSERVER_INFO_101 ServerEntry;
+ ULONG EntrySize = 0;
+ LPWSTR BufferEnd;
+ BOOLEAN ReturnWholeList = FALSE;
+ BOOLEAN TrimmingNames;
+ BOOLEAN BufferFull = FALSE;
+
+ if (Level == 100) {
+ EntrySize = sizeof(SERVER_INFO_100);
+ } else if (Level == 101) {
+ EntrySize = sizeof(SERVER_INFO_101);
+ } else {
+ return(ERROR_INVALID_LEVEL);
+ }
+
+ //
+ // Set the entries read based on the information we collected before.
+ //
+
+ *TotalEntries = 0;
+
+ if (PreferedMaximumLength == 0xffffffff) {
+ *ServerList = MIDL_user_allocate(InterimServerList->TotalBytesNeeded);
+
+ BufferEnd = (LPWSTR)((ULONG)(*ServerList)+InterimServerList->TotalBytesNeeded);
+
+ } else {
+ *ServerList = MIDL_user_allocate(PreferedMaximumLength);
+
+ BufferEnd = (LPWSTR)((ULONG)(*ServerList)+PreferedMaximumLength);
+ }
+
+ if (ServerType == SV_TYPE_ALL || ServerType == SV_TYPE_DOMAIN_ENUM) {
+ ReturnWholeList = TRUE;
+ }
+
+ if ( *ServerList == NULL ) {
+ return(ERROR_NOT_ENOUGH_MEMORY);
+
+ }
+
+ TrimmingNames = (FirstNameToReturn != NULL && *FirstNameToReturn != L'\0');
+ ServerEntry = *ServerList;
+
+ for (InterimList = InterimServerList->ServerList.Flink ;
+ InterimList != &InterimServerList->ServerList ;
+ InterimList = InterimList->Flink ) {
+
+ PINTERIM_ELEMENT InterimEntry = CONTAINING_RECORD(InterimList, INTERIM_ELEMENT, NextElement);
+
+#if DBG
+ //
+ // Make sure that this entry is lexically less than the next
+ // entry.
+ //
+
+ {
+ PLIST_ENTRY NextList = InterimList->Flink;
+ PINTERIM_ELEMENT NextEntry = CONTAINING_RECORD(NextList, INTERIM_ELEMENT, NextElement);
+
+ if (NextList != &InterimServerList->ServerList) {
+ ASSERT (STRCMP(InterimEntry->Name, NextEntry->Name) < 0);
+ }
+
+ }
+#endif
+
+ //
+ // Trim the first several names from the list.
+ //
+
+ if ( TrimmingNames ) {
+ if ( STRCMP( InterimEntry->Name, FirstNameToReturn ) < 0 ) {
+ continue;
+ }
+ TrimmingNames = FALSE;
+ }
+
+ //
+ // If the server's type matches the type filter, pack it into the buffer.
+ //
+
+ if (InterimEntry->Type & ServerType) {
+
+ (*TotalEntries) += 1;
+
+ //
+ // If this entry will fit into the buffer, pack it in.
+ //
+ // Please note that we only count an entry if the entire entry
+ // (server and comment) fits in the buffer. This is NOT
+ // strictly Lan Manager compatible.
+ //
+
+ if ( !BufferFull &&
+ ((ULONG)ServerEntry+EntrySize <= (ULONG)BufferEnd)) {
+
+ ServerEntry->sv101_platform_id = InterimEntry->PlatformId;
+
+ ServerEntry->sv101_name = InterimEntry->Name;
+
+ if (NetpPackString(&ServerEntry->sv101_name,
+ (LPBYTE)((PCHAR)ServerEntry)+EntrySize,
+ &BufferEnd)) {
+
+ if (Level == 101) {
+
+ ServerEntry->sv101_version_major = InterimEntry->MajorVersionNumber;;
+
+ ServerEntry->sv101_version_minor = InterimEntry->MinorVersionNumber;;
+
+ ServerEntry->sv101_type = InterimEntry->Type;
+
+ ServerEntry->sv101_comment = InterimEntry->Comment;
+
+ if (NetpPackString(&ServerEntry->sv101_comment,
+ (LPBYTE)((PCHAR)ServerEntry)+EntrySize,
+ &BufferEnd)) {
+ EntriesPacked += 1;
+ } else {
+ BufferFull = TRUE;
+ }
+ } else {
+ EntriesPacked += 1;
+ }
+#if DBG
+ {
+ PSERVER_INFO_101 PreviousServerInfo = (PSERVER_INFO_101)((PCHAR)ServerEntry-EntrySize);
+ if (PreviousServerInfo >= (PSERVER_INFO_101)*ServerList) {
+ ASSERT (STRCMP(ServerEntry->sv101_name, PreviousServerInfo->sv101_name) > 0);
+ }
+
+ }
+#endif
+ } else {
+ BufferFull = TRUE;
+ }
+
+ } else {
+
+ //
+ // If we're returning the entire list and we have exceeded
+ // the amount that fits in the list, we can early out
+ // now.
+ //
+
+ if (ReturnWholeList) {
+
+ *TotalEntries = InterimServerList->TotalEntries;
+
+ break;
+ }
+
+ BufferFull = TRUE;
+ }
+
+ //
+ // Step to the next server entry.
+ //
+
+ ServerEntry = (PSERVER_INFO_101)((PCHAR)ServerEntry+EntrySize);
+ }
+ }
+
+ ASSERT (InterimServerList->EntriesRead >= EntriesPacked);
+
+ *EntriesRead = EntriesPacked;
+
+ if (EntriesPacked != *TotalEntries) {
+ return ERROR_MORE_DATA;
+ } else {
+ return NERR_Success;
+ }
+
+}
+
+
+ NET_API_STATUS
+InitializeInterimServerList(
+ IN PINTERIM_SERVER_LIST InterimServerList,
+ IN PINTERIM_NEW_CALLBACK NewCallback,
+ IN PINTERIM_EXISTING_CALLBACK ExistingCallback,
+ IN PINTERIM_DELETE_CALLBACK DeleteElementCallback,
+ IN PINTERIM_AGE_CALLBACK AgeElementCallback
+ )
+{
+
+ InitializeListHead(&InterimServerList->ServerList);
+
+ InterimServerList->TotalBytesNeeded = 0;
+ InterimServerList->TotalEntries = 0;
+ InterimServerList->EntriesRead = 0;
+
+ InterimServerList->NewElementCallback = NewCallback;
+ InterimServerList->ExistingElementCallback = ExistingCallback;
+ InterimServerList->DeleteElementCallback = DeleteElementCallback;
+ InterimServerList->AgeElementCallback = AgeElementCallback;
+ return(NERR_Success);
+}
+
+ NET_API_STATUS
+UninitializeInterimServerList(
+ IN PINTERIM_SERVER_LIST InterimServerList
+ )
+{
+ PINTERIM_ELEMENT InterimElement;
+
+
+// KdPrint(("BROWSER: Uninitialize Interim Server List %lx\n", InterimServerList));
+
+ //
+ // Enumerate the elements in the table, deleting them as we go.
+ //
+
+ while (!IsListEmpty(&InterimServerList->ServerList)) {
+ PLIST_ENTRY Entry;
+
+ Entry = RemoveHeadList(&InterimServerList->ServerList);
+
+ InterimElement = CONTAINING_RECORD(Entry, INTERIM_ELEMENT, NextElement);
+
+ if (InterimServerList->DeleteElementCallback != NULL) {
+ InterimServerList->DeleteElementCallback(InterimServerList, InterimElement);
+ }
+
+ //
+ // There is one less element in the list.
+ //
+
+ InterimServerList->EntriesRead -= 1;
+
+ InterimServerList->TotalEntries -= 1;
+
+ MIDL_user_free(InterimElement);
+ }
+
+ ASSERT (InterimServerList->EntriesRead == 0);
+
+ return(NERR_Success);
+}
+
+ULONG
+NumberInterimServerListElements(
+ IN PINTERIM_SERVER_LIST InterimServerList
+ )
+{
+ PLIST_ENTRY InterimList;
+ ULONG NumberOfEntries = 0;
+
+ for (InterimList = InterimServerList->ServerList.Flink ;
+ InterimList != &InterimServerList->ServerList ;
+ InterimList = InterimList->Flink ) {
+ NumberOfEntries += 1;
+
+ }
+
+ return NumberOfEntries;
+}
+
+ NET_API_STATUS
+AgeInterimServerList(
+ IN PINTERIM_SERVER_LIST InterimServerList
+ )
+{
+ PLIST_ENTRY InterimList, NextElement;
+ PINTERIM_ELEMENT InterimElement;
+
+ if (InterimServerList->AgeElementCallback != NULL) {
+
+ //
+ // Enumerate the elements in the table, aging them as we go.
+ //
+
+
+ for (InterimList = InterimServerList->ServerList.Flink ;
+ InterimList != &InterimServerList->ServerList ;
+ InterimList = NextElement) {
+ InterimElement = CONTAINING_RECORD(InterimList, INTERIM_ELEMENT, NextElement);
+
+ //
+ // Call into the aging routine and if this entry is too old,
+ // remove it from the interim list.
+ //
+
+ if (InterimServerList->AgeElementCallback(InterimServerList, InterimElement)) {
+ ULONG ElementSize = sizeof(SERVER_INFO_101) + ((STRLEN(InterimElement->Comment) + 1) * sizeof(WCHAR)) + ((STRLEN(InterimElement->Name) + 1) * sizeof(WCHAR));
+
+ ASSERT (ElementSize <= InterimServerList->TotalBytesNeeded);
+
+ NextElement = InterimList->Flink;
+
+ //
+ // Remove this entry from the list.
+ //
+
+ RemoveEntryList(&InterimElement->NextElement);
+
+ if (InterimServerList->DeleteElementCallback != NULL) {
+ InterimServerList->DeleteElementCallback(InterimServerList, InterimElement);
+ }
+
+ //
+ // There is one less element in the list.
+ //
+
+ InterimServerList->EntriesRead -= 1;
+
+ InterimServerList->TotalEntries -= 1;
+
+ //
+ // Since this element isn't in the table any more, we don't
+ // need to allocate memory for it.
+ //
+
+ InterimServerList->TotalBytesNeeded -= ElementSize;
+
+ MIDL_user_free(InterimElement);
+
+ } else {
+ NextElement = InterimList->Flink;
+ }
+ }
+#if 0
+ {
+ PINTERIM_ELEMENT InterimEntry;
+ ULONG TotalNeededForList = 0;
+
+ for (InterimList = InterimServerList->ServerList.Flink ;
+ InterimList != &InterimServerList->ServerList ;
+ InterimList = InterimList->Flink ) {
+ ULONG ServerElementSize;
+
+ InterimEntry = CONTAINING_RECORD(InterimList, INTERIM_ELEMENT, NextElement);
+
+ ServerElementSize = sizeof(SERVER_INFO_101);
+
+ ServerElementSize += STRLEN(InterimEntry->Name)*sizeof(TCHAR)+sizeof(TCHAR);
+
+ ServerElementSize += STRLEN(InterimEntry->Comment)*sizeof(TCHAR)+sizeof(TCHAR);
+
+ TotalNeededForList += ServerElementSize;
+ }
+
+ if (TotalNeededForList != InterimServerList->TotalBytesNeeded) {
+ KdPrint(("AgeInterimServerList: Too few bytes (%ld) for interim server list. %ld needed\n", InterimServerList->TotalBytesNeeded, TotalNeededForList));
+ }
+ }
+#endif
+
+ }
+
+ return(NERR_Success);
+}
+
+ PINTERIM_ELEMENT
+LookupInterimServerList(
+ IN PINTERIM_SERVER_LIST InterimServerList,
+ IN LPWSTR ServerNameToLookUp
+ )
+{
+ PLIST_ENTRY InterimList;
+
+ for (InterimList = InterimServerList->ServerList.Flink ;
+ InterimList != &InterimServerList->ServerList ;
+ InterimList = InterimList->Flink ) {
+
+ PINTERIM_ELEMENT InterimEntry = CONTAINING_RECORD(InterimList, INTERIM_ELEMENT, NextElement);
+ LONG CompareResult;
+
+ if ((CompareResult = STRICMP(InterimEntry->Name, ServerNameToLookUp) == 0)) {
+ return InterimEntry;
+ }
+
+ //
+ // If we went past this guy, return an error.
+ //
+
+ if (CompareResult > 0) {
+ return NULL;
+ }
+
+ }
+
+ return NULL;
+}
+
+
diff --git a/private/net/svcdlls/browser/common/utils.c b/private/net/svcdlls/browser/common/utils.c
new file mode 100644
index 000000000..0383dd5c8
--- /dev/null
+++ b/private/net/svcdlls/browser/common/utils.c
@@ -0,0 +1,969 @@
+/*++
+
+Copyright (c) 1991-1992 Microsoft Corporation
+
+Module Name:
+
+ utils.c
+
+Abstract:
+
+ Utility routines for the browser service.
+
+Author:
+
+ Larry Osterman (LarryO) 23-Mar-1992
+
+Environment:
+
+ User Mode - Win32
+
+Revision History:
+
+--*/
+
+#undef IF_DEBUG // avoid wsclient.h vs. debuglib.h conflicts.
+#include <nt.h> // DbgPrint prototype
+#include <ntrtl.h> // DbgPrint
+#include <nturtl.h> // Needed by winbase.h
+
+#include <windef.h> // DWORD
+#include <winbase.h> // LocalFree
+#include <winreg.h>
+
+#include <rpc.h> // DataTypes and runtime APIs
+#include <rpcutil.h> // GENERIC_ENUM_STRUCT
+
+#include <lmcons.h> // NET_API_STATUS
+#include <lmerr.h> // NetError codes
+#include <lmremutl.h> // SUPPORTS_RPC
+
+#include <netlib.h> // NetpNtStatusToApiStatus
+#include <netlibnt.h> // NetpNtStatusToApiStatus
+#include <netdebug.h>
+
+#include <bowser.h> // generated by the MIDL complier
+#include <brnames.h> // Service and interface names
+
+#include <winsvc.h>
+
+#include <debuglib.h> // IF_DEBUG() (needed by netrpc.h).
+#include <lmserver.h>
+#include <align.h>
+#include <tstr.h>
+
+#include <ntddbrow.h>
+#include <brcommon.h> // Routines common between client & server
+
+#include <nb30.h>
+#include <hostannc.h>
+
+#include <winnls.h>
+
+//
+// Buffer allocation size for enumeration output buffer.
+//
+#define INITIAL_ALLOCATION_SIZE 48*1024 // First attempt size (48K)
+#define FUDGE_FACTOR_SIZE 1024 // Second try TotalBytesNeeded
+ // plus this amount
+
+
+
+
+
+
+
+
+ NET_API_STATUS
+BrDgReceiverIoControl(
+ IN HANDLE FileHandle,
+ IN ULONG DgReceiverControlCode,
+ IN PLMDR_REQUEST_PACKET Drp,
+ IN ULONG DrpSize,
+ IN PVOID SecondBuffer OPTIONAL,
+ IN ULONG SecondBufferLength,
+ OUT PULONG Information OPTIONAL
+ )
+/*++
+
+Routine Description:
+
+Arguments:
+
+ FileHandle - Supplies a handle to the file or device on which the service
+ is being performed.
+
+ DgReceiverControlCode - Supplies the NtDeviceIoControlFile function code
+ given to the datagram receiver.
+
+ Drp - Supplies the datagram receiver request packet.
+
+ DrpSize - Supplies the length of the datagram receiver request packet.
+
+ SecondBuffer - Supplies the second buffer in call to NtDeviceIoControlFile.
+
+ SecondBufferLength - Supplies the length of the second buffer.
+
+ Information - Returns the information field of the I/O status block.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+
+{
+ NTSTATUS ntstatus;
+ IO_STATUS_BLOCK IoStatusBlock;
+ PLMDR_REQUEST_PACKET RealDrp;
+ HANDLE CompletionEvent;
+ LPBYTE Where;
+
+ if (FileHandle == NULL) {
+ return ERROR_NOT_SUPPORTED;
+ }
+
+ //
+ // Allocate a copy of the request packet where we can put the transport and
+ // emulated domain name in the packet itself.
+ //
+#ifdef _CAIRO_
+ RealDrp = MIDL_user_allocate(DrpSize+
+ Drp->TransportName.Length+sizeof(WCHAR)+
+ Drp->EmulatedDomainName.Length+sizeof(WCHAR) );
+#else // _CAIRO_
+ RealDrp = MIDL_user_allocate(DrpSize+Drp->TransportName.MaximumLength);
+#endif // _CAIRO_
+
+ if (RealDrp == NULL) {
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ //
+ // Copy the request packet into the local copy.
+ //
+ RtlCopyMemory(RealDrp, Drp, DrpSize);
+
+ Where = (LPBYTE)RealDrp+DrpSize;
+ if (Drp->TransportName.Length != 0) {
+ RealDrp->TransportName.Buffer = (LPWSTR)Where;
+ RealDrp->TransportName.MaximumLength = Drp->TransportName.Length+sizeof(WCHAR);
+ RtlCopyUnicodeString(&RealDrp->TransportName, &Drp->TransportName);
+ Where += RealDrp->TransportName.MaximumLength;
+ }
+
+#ifdef _CAIRO_
+ if (Drp->EmulatedDomainName.Length != 0) {
+ RealDrp->EmulatedDomainName.Buffer = (LPWSTR)Where;
+ RealDrp->EmulatedDomainName.MaximumLength = Drp->EmulatedDomainName.Length+sizeof(WCHAR);
+ RtlCopyUnicodeString(&RealDrp->EmulatedDomainName, &Drp->EmulatedDomainName);
+ Where += RealDrp->EmulatedDomainName.MaximumLength;
+ }
+#endif // _CAIRO_
+
+
+
+ //
+ // Create a completion event
+ //
+ CompletionEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+
+ if (CompletionEvent == NULL) {
+
+ MIDL_user_free(RealDrp);
+
+ return(GetLastError());
+ }
+
+ //
+ // Send the request to the Datagram Receiver DD.
+ //
+
+ ntstatus = NtDeviceIoControlFile(
+ FileHandle,
+ CompletionEvent,
+ NULL,
+ NULL,
+ &IoStatusBlock,
+ DgReceiverControlCode,
+ RealDrp,
+ Where-(LPBYTE)RealDrp,
+ SecondBuffer,
+ SecondBufferLength
+ );
+
+ if (NT_SUCCESS(ntstatus)) {
+
+ //
+ // If pending was returned, then wait until the request completes.
+ //
+
+ if (ntstatus == STATUS_PENDING) {
+
+ do {
+ ntstatus = WaitForSingleObjectEx(CompletionEvent, 0xffffffff, TRUE);
+ } while ( ntstatus == WAIT_IO_COMPLETION );
+ }
+
+
+ if (NT_SUCCESS(ntstatus)) {
+ ntstatus = IoStatusBlock.Status;
+ }
+ }
+
+ if (ARGUMENT_PRESENT(Information)) {
+ *Information = IoStatusBlock.Information;
+ }
+
+ MIDL_user_free(RealDrp);
+
+ CloseHandle(CompletionEvent);
+
+ return NetpNtStatusToApiStatus(ntstatus);
+}
+
+ NET_API_STATUS
+DeviceControlGetInfo(
+ IN HANDLE FileHandle,
+ IN ULONG DeviceControlCode,
+ IN PVOID RequestPacket,
+ IN ULONG RequestPacketLength,
+ OUT LPVOID *OutputBuffer,
+ IN ULONG PreferedMaximumLength,
+ IN ULONG BufferHintSize,
+ OUT PULONG Information OPTIONAL
+ )
+/*++
+
+Routine Description:
+
+ This function allocates the buffer and fill it with the information
+ that is retrieved from the datagram receiver.
+
+Arguments:
+
+ DeviceDriverType - Supplies the value which indicates whether to call
+ the datagram receiver.
+
+ FileHandle - Supplies a handle to the file or device of which to get
+ information about.
+
+ DeviceControlCode - Supplies the NtFsControlFile or NtIoDeviceControlFile
+ function control code.
+
+ RequestPacket - Supplies a pointer to the device request packet.
+
+ RrequestPacketLength - Supplies the length of the device request packet.
+
+ OutputBuffer - Returns a pointer to the buffer allocated by this routine
+ which contains the use information requested. This pointer is set to
+ NULL if return code is not NERR_Success.
+
+ PreferedMaximumLength - Supplies the number of bytes of information to
+ return in the buffer. If this value is MAXULONG, we will try to
+ return all available information if there is enough memory resource.
+
+ BufferHintSize - Supplies the hint size of the output buffer so that the
+ memory allocated for the initial buffer will most likely be large
+ enough to hold all requested data.
+
+ Information - Returns the information code from the NtFsControlFile or
+ NtIoDeviceControlFile call.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NET_API_STATUS status;
+ NTSTATUS ntstatus;
+ DWORD OutputBufferLength;
+ DWORD TotalBytesNeeded = 1;
+ ULONG OriginalResumeKey;
+ IO_STATUS_BLOCK IoStatusBlock;
+ PLMDR_REQUEST_PACKET Drrp = (PLMDR_REQUEST_PACKET) RequestPacket;
+ HANDLE CompletionEvent;
+
+ OriginalResumeKey = Drrp->Parameters.EnumerateNames.ResumeHandle;
+
+ //
+ // If PreferedMaximumLength is MAXULONG, then we are supposed to get all
+ // the information, regardless of size. Allocate the output buffer of a
+ // reasonable size and try to use it. If this fails, the Redirector FSD
+ // will say how much we need to allocate.
+ //
+ if (PreferedMaximumLength == MAXULONG) {
+ OutputBufferLength = (BufferHintSize) ?
+ BufferHintSize :
+ INITIAL_ALLOCATION_SIZE;
+ }
+ else {
+ OutputBufferLength = PreferedMaximumLength;
+ }
+
+ OutputBufferLength = ROUND_UP_COUNT(OutputBufferLength, ALIGN_WCHAR);
+
+ if ((*OutputBuffer = MIDL_user_allocate(OutputBufferLength)) == NULL) {
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+ RtlZeroMemory((PVOID) *OutputBuffer, OutputBufferLength);
+
+ CompletionEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+
+ if (CompletionEvent == (HANDLE)-1) {
+ MIDL_user_free(*OutputBuffer);
+ *OutputBuffer = NULL;
+ return(GetLastError());
+ }
+
+ Drrp->Parameters.EnumerateServers.EntriesRead = 0;
+
+ //
+ // Make the request of the Datagram Receiver
+ //
+
+ ntstatus = NtDeviceIoControlFile(
+ FileHandle,
+ CompletionEvent,
+ NULL, // APC routine
+ NULL, // APC context
+ &IoStatusBlock,
+ DeviceControlCode,
+ Drrp,
+ RequestPacketLength,
+ *OutputBuffer,
+ OutputBufferLength
+ );
+
+ if (NT_SUCCESS(ntstatus)) {
+
+ //
+ // If pending was returned, then wait until the request completes.
+ //
+
+ if (ntstatus == STATUS_PENDING) {
+ do {
+ ntstatus = WaitForSingleObjectEx(CompletionEvent, 0xffffffff, TRUE);
+ } while ( ntstatus == WAIT_IO_COMPLETION );
+ }
+
+ if (NT_SUCCESS(ntstatus)) {
+ ntstatus = IoStatusBlock.Status;
+ }
+ }
+
+ //
+ // Map NT status to Win error
+ //
+ status = NetpNtStatusToApiStatus(ntstatus);
+
+ if (status == ERROR_MORE_DATA) {
+
+ NetpAssert(
+ FIELD_OFFSET(
+ LMDR_REQUEST_PACKET,
+ Parameters.EnumerateNames.TotalBytesNeeded
+ ) ==
+ FIELD_OFFSET(
+ LMDR_REQUEST_PACKET,
+ Parameters.EnumerateServers.TotalBytesNeeded
+ )
+ );
+
+ NetpAssert(
+ FIELD_OFFSET(
+ LMDR_REQUEST_PACKET,
+ Parameters.GetBrowserServerList.TotalBytesNeeded
+ ) ==
+ FIELD_OFFSET(
+ LMDR_REQUEST_PACKET,
+ Parameters.EnumerateServers.TotalBytesNeeded
+ )
+ );
+
+ TotalBytesNeeded = Drrp->Parameters.EnumerateNames.TotalBytesNeeded;
+ }
+
+ if ((TotalBytesNeeded > OutputBufferLength) &&
+ (PreferedMaximumLength == MAXULONG)) {
+ PLMDR_REQUEST_PACKET Drrp = (PLMDR_REQUEST_PACKET) RequestPacket;
+
+ //
+ // Initial output buffer allocated was too small and we need to return
+ // all data. First free the output buffer before allocating the
+ // required size plus a fudge factor just in case the amount of data
+ // grew.
+ //
+
+ MIDL_user_free(*OutputBuffer);
+
+ OutputBufferLength =
+ ROUND_UP_COUNT((TotalBytesNeeded + FUDGE_FACTOR_SIZE),
+ ALIGN_WCHAR);
+
+ if ((*OutputBuffer = MIDL_user_allocate(OutputBufferLength)) == NULL) {
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+ RtlZeroMemory((PVOID) *OutputBuffer, OutputBufferLength);
+
+
+ NetpAssert(
+ FIELD_OFFSET(
+ LMDR_REQUEST_PACKET,
+ Parameters.EnumerateNames.ResumeHandle
+ ) ==
+ FIELD_OFFSET(
+ LMDR_REQUEST_PACKET,
+ Parameters.EnumerateServers.ResumeHandle
+ )
+ );
+
+ NetpAssert(
+ FIELD_OFFSET(
+ LMDR_REQUEST_PACKET,
+ Parameters.EnumerateNames.ResumeHandle
+ ) ==
+ FIELD_OFFSET(
+ LMDR_REQUEST_PACKET,
+ Parameters.GetBrowserServerList.ResumeHandle
+ )
+ );
+
+ Drrp->Parameters.EnumerateNames.ResumeHandle = OriginalResumeKey;
+ Drrp->Parameters.EnumerateServers.EntriesRead = 0;
+
+ //
+ // Make the request of the Datagram Receiver
+ //
+
+ ntstatus = NtDeviceIoControlFile(
+ FileHandle,
+ CompletionEvent,
+ NULL, // APC routine
+ NULL, // APC context
+ &IoStatusBlock,
+ DeviceControlCode,
+ Drrp,
+ RequestPacketLength,
+ *OutputBuffer,
+ OutputBufferLength
+ );
+
+ if (NT_SUCCESS(ntstatus)) {
+
+ //
+ // If pending was returned, then wait until the request completes.
+ //
+
+ if (ntstatus == STATUS_PENDING) {
+ do {
+ ntstatus = WaitForSingleObjectEx(CompletionEvent, 0xffffffff, TRUE);
+ } while ( ntstatus == WAIT_IO_COMPLETION );
+ }
+
+ if (NT_SUCCESS(ntstatus)) {
+ ntstatus = IoStatusBlock.Status;
+ }
+ }
+
+ status = NetpNtStatusToApiStatus(ntstatus);
+
+ }
+
+
+ //
+ // If not successful in getting any data, or if the caller asked for
+ // all available data with PreferedMaximumLength == MAXULONG and
+ // our buffer overflowed, free the output buffer and set its pointer
+ // to NULL.
+ //
+ if ((status != NERR_Success && status != ERROR_MORE_DATA) ||
+ (TotalBytesNeeded == 0) ||
+ (PreferedMaximumLength == MAXULONG && status == ERROR_MORE_DATA) ||
+ (Drrp->Parameters.EnumerateServers.EntriesRead == 0)) {
+
+ MIDL_user_free(*OutputBuffer);
+ *OutputBuffer = NULL;
+
+ //
+ // PreferedMaximumLength == MAXULONG and buffer overflowed means
+ // we do not have enough memory to satisfy the request.
+ //
+ if (status == ERROR_MORE_DATA) {
+ status = ERROR_NOT_ENOUGH_MEMORY;
+ }
+ }
+
+ CloseHandle(CompletionEvent);
+
+ return status;
+
+ UNREFERENCED_PARAMETER(Information);
+}
+
+ NET_API_STATUS
+GetBrowserServerList(
+ IN PUNICODE_STRING TransportName,
+ IN LPCWSTR Domain,
+ OUT LPWSTR *BrowserList[],
+ OUT PULONG BrowserListLength,
+ IN BOOLEAN ForceRescan
+ )
+/*++
+
+Routine Description:
+
+ This function will return a list of browser servers.
+
+Arguments:
+
+ IN PUNICODE_STRING TransportName - Transport to return list.
+
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+
+
+ NET_API_STATUS Status;
+ HANDLE BrowserHandle;
+ PLMDR_REQUEST_PACKET RequestPacket = NULL;
+
+// DbgPrint("Getting browser server list for transport %wZ\n", TransportName);
+
+ Status = OpenBrowser(&BrowserHandle);
+
+ if (Status != NERR_Success) {
+ return Status;
+ }
+
+ RequestPacket = MIDL_user_allocate(sizeof(LMDR_REQUEST_PACKET)+(DNLEN*sizeof(WCHAR))+TransportName->MaximumLength);
+
+ if (RequestPacket == NULL) {
+ NtClose(BrowserHandle);
+ return(GetLastError());
+ }
+
+ RequestPacket->Version = LMDR_REQUEST_PACKET_VERSION;
+
+ RequestPacket->Level = 0;
+
+ RequestPacket->Parameters.GetBrowserServerList.ForceRescan = ForceRescan;
+
+ if (Domain != NULL) {
+ STRCPY(RequestPacket->Parameters.GetBrowserServerList.DomainName, Domain);
+
+ RequestPacket->Parameters.GetBrowserServerList.DomainNameLength = (USHORT)STRLEN(Domain) * sizeof(TCHAR);
+ } else {
+ RequestPacket->Parameters.GetBrowserServerList.DomainNameLength = 0;
+ RequestPacket->Parameters.GetBrowserServerList.DomainName[0] = L'\0';
+
+ }
+
+ RequestPacket->TransportName.Buffer = (PWSTR)((PCHAR)RequestPacket+sizeof(LMDR_REQUEST_PACKET)+DNLEN*sizeof(WCHAR));
+ RequestPacket->TransportName.MaximumLength = TransportName->MaximumLength;
+
+ RtlCopyUnicodeString(&RequestPacket->TransportName, TransportName);
+#ifdef _CAIRO_
+ RtlInitUnicodeString( &RequestPacket->EmulatedDomainName, NULL );
+#endif // _CAIRO_
+
+ RequestPacket->Parameters.GetBrowserServerList.ResumeHandle = 0;
+
+ Status = DeviceControlGetInfo(
+ BrowserHandle,
+ IOCTL_LMDR_GET_BROWSER_SERVER_LIST,
+ RequestPacket,
+ sizeof(LMDR_REQUEST_PACKET)+
+ (DNLEN*sizeof(WCHAR))+TransportName->MaximumLength,
+ (PVOID *)BrowserList,
+ 0xffffffff,
+ 4096,
+ NULL);
+
+ if (Status == NERR_Success) {
+ *BrowserListLength = RequestPacket->Parameters.GetBrowserServerList.EntriesRead;
+ }
+
+ NtClose(BrowserHandle);
+ MIDL_user_free(RequestPacket);
+
+ return Status;
+}
+ NET_API_STATUS
+OpenBrowser(
+ OUT PHANDLE BrowserHandle
+ )
+/*++
+
+Routine Description:
+
+ This function opens a handle to the bowser device driver.
+
+Arguments:
+
+ OUT PHANDLE BrowserHandle - Returns the handle to the browser.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NTSTATUS ntstatus;
+
+ UNICODE_STRING DeviceName;
+
+ IO_STATUS_BLOCK IoStatusBlock;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+
+
+ //
+ // Open the redirector device.
+ //
+ RtlInitUnicodeString(&DeviceName, DD_BROWSER_DEVICE_NAME_U);
+
+ InitializeObjectAttributes(
+ &ObjectAttributes,
+ &DeviceName,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL
+ );
+
+ ntstatus = NtOpenFile(
+ BrowserHandle,
+ SYNCHRONIZE | GENERIC_READ | GENERIC_WRITE,
+ &ObjectAttributes,
+ &IoStatusBlock,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ FILE_SYNCHRONOUS_IO_NONALERT
+ );
+
+ if (NT_SUCCESS(ntstatus)) {
+ ntstatus = IoStatusBlock.Status;
+ }
+
+ return NetpNtStatusToApiStatus(ntstatus);
+
+}
+
+NET_API_STATUS
+CheckForService(
+ IN LPTSTR ServiceName,
+ OUT LPSERVICE_STATUS ServiceStatus OPTIONAL
+ )
+{
+ SC_HANDLE ServiceControllerHandle;
+ SC_HANDLE ServiceHandle;
+ SERVICE_STATUS Status;
+
+ if (!ARGUMENT_PRESENT(ServiceStatus)) {
+ ServiceStatus = &Status;
+ }
+
+ ServiceControllerHandle = OpenSCManager(NULL, NULL, SC_MANAGER_ENUMERATE_SERVICE);
+
+ if (ServiceControllerHandle == NULL) {
+
+ return GetLastError();
+ }
+
+ ServiceHandle = OpenService(ServiceControllerHandle, ServiceName, SERVICE_QUERY_STATUS);
+
+ if (ServiceHandle == NULL) {
+
+ CloseServiceHandle(ServiceControllerHandle);
+ return GetLastError();
+ }
+
+
+ if (!QueryServiceStatus(ServiceHandle, ServiceStatus)) {
+ CloseServiceHandle(ServiceHandle);
+ CloseServiceHandle(ServiceControllerHandle);
+
+ return GetLastError();
+ }
+
+ if ((ServiceStatus->dwCurrentState != SERVICE_RUNNING) &&
+ (ServiceStatus->dwCurrentState != SERVICE_START_PENDING)) {
+ CloseServiceHandle(ServiceHandle);
+ CloseServiceHandle(ServiceControllerHandle);
+ return(NERR_ServiceNotInstalled);
+ }
+
+ CloseServiceHandle(ServiceHandle);
+
+ CloseServiceHandle(ServiceControllerHandle);
+
+ return NERR_Success;
+}
+
+NET_API_STATUS
+BrGetLanaNumFromNetworkName(
+ IN PWCHAR TransportName,
+ OUT CCHAR *LanaNum
+ )
+/*++
+
+ NOTE: THIS CODE WILL NOT WORK IN THE FUTURE!!!!!!!!!!!!!!
+
+--*/
+
+{
+ HKEY Key;
+ WCHAR BindInformation[MAX_PATH*20];
+ LPWSTR DevicePointer;
+ ULONG BindInfoSize = sizeof(BindInformation);
+ struct {
+ CHAR Enumerated;
+ CHAR LanaNum;
+ } LanaMap[256];
+
+ ULONG LanaMapSize = sizeof(LanaMap);
+ NET_API_STATUS Status;
+ DWORD Type;
+ DWORD LanaIndex;
+
+ RtlZeroMemory(LanaMap, sizeof(LanaMap));
+
+ LanaIndex = 0;
+
+ if (Status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"System\\CurrentControlSet\\Services\\Netbios\\Linkage", 0, KEY_QUERY_VALUE, &Key)) {
+ return Status;
+ }
+
+ if (Status = RegQueryValueEx(Key, L"Bind", 0, &Type, (LPBYTE)BindInformation, &BindInfoSize)) {
+ RegCloseKey(Key);
+ return Status;
+ }
+
+ if (Status = RegQueryValueEx(Key, L"LanaMap", 0, &Type, (LPBYTE)LanaMap, &LanaMapSize)) {
+ RegCloseKey(Key);
+ return Status;
+ }
+
+ DevicePointer = BindInformation;
+
+ while (*DevicePointer != UNICODE_NULL) {
+ if (!_wcsicmp(TransportName, DevicePointer)) {
+
+ if (LanaMap[LanaIndex].Enumerated != 0) {
+ *LanaNum = LanaMap[LanaIndex].LanaNum;
+ Status = NERR_Success;
+ } else {
+ Status = ERROR_FILE_NOT_FOUND;
+ }
+
+ RegCloseKey(Key);
+
+ return Status;
+ }
+
+ LanaIndex += 1;
+
+ DevicePointer += wcslen(DevicePointer)+1;
+ }
+
+ RegCloseKey(Key);
+ return(ERROR_FILE_NOT_FOUND);
+}
+
+// 1234567890123456
+#define SPACES " "
+
+#define ClearNcb( PNCB ) { \
+ RtlZeroMemory( PNCB , sizeof (NCB) ); \
+ RtlCopyMemory( (PNCB)->ncb_name, SPACES, sizeof(SPACES)-1 );\
+ RtlCopyMemory( (PNCB)->ncb_callname, SPACES, sizeof(SPACES)-1 );\
+ }
+
+ NET_API_STATUS
+GetNetBiosMasterName(
+ IN LPWSTR NetworkName,
+ IN LPWSTR PrimaryDomain,
+ OUT LPWSTR MasterName,
+ IN PSVCS_NET_BIOS_RESET SvcsNetBiosReset OPTIONAL
+ )
+{
+ CCHAR LanaNum;
+ NCB AStatNcb;
+#define MAX_NETBIOS_NAMES 256
+ struct {
+ ADAPTER_STATUS AdapterInfo;
+ NAME_BUFFER Names[MAX_NETBIOS_NAMES];
+ } AdapterStatus;
+ WORD i;
+ CHAR remoteName[CNLEN+1];
+ NET_API_STATUS Status;
+ OEM_STRING OemString;
+ UNICODE_STRING UnicodeString;
+
+ Status = BrGetLanaNumFromNetworkName(NetworkName, &LanaNum);
+
+ if (Status != NERR_Success) {
+ return Status;
+ }
+
+ //
+ // If the SvcsNetBiosReset argument is present, then this routine is
+ // being called from the service. In this case it needs to synchronize
+ // its NetBios Reset with the workstation and the messenger.
+ //
+ if (ARGUMENT_PRESENT(SvcsNetBiosReset)) {
+ SvcsNetBiosReset(LanaNum);
+ }
+ else {
+ ClearNcb(&AStatNcb)
+
+ AStatNcb.ncb_command = NCBRESET;
+ AStatNcb.ncb_lsn = 0; // Request resources
+ AStatNcb.ncb_lana_num = LanaNum;
+ AStatNcb.ncb_callname[0] = 0; // 16 sessions
+ AStatNcb.ncb_callname[1] = 0; // 16 commands
+ AStatNcb.ncb_callname[2] = 0; // 8 names
+ AStatNcb.ncb_callname[3] = 0; // Don't want the reserved address
+ Netbios( &AStatNcb );
+ }
+ ClearNcb( &AStatNcb );
+
+ //
+ // Uppercase the remote name.
+ //
+
+ RtlInitUnicodeString(&UnicodeString, PrimaryDomain);
+
+ OemString.Buffer=remoteName;
+
+ OemString.MaximumLength=sizeof(remoteName);
+
+ Status = RtlUpcaseUnicodeStringToOemString(&OemString,
+ &UnicodeString,
+ FALSE);
+
+ if (!NT_SUCCESS(Status)) {
+ return RtlNtStatusToDosError(Status);
+ }
+
+ AStatNcb.ncb_command = NCBASTAT;
+
+ RtlCopyMemory( AStatNcb.ncb_callname, remoteName, strlen(remoteName));
+
+ AStatNcb.ncb_callname[15] = MASTER_BROWSER_SIGNATURE;
+
+ AStatNcb.ncb_lana_num = LanaNum;
+ AStatNcb.ncb_length = sizeof( AdapterStatus );
+ AStatNcb.ncb_buffer = (CHAR *)&AdapterStatus;
+ Netbios( &AStatNcb );
+
+ if ( AStatNcb.ncb_retcode == NRC_GOODRET ||
+ AStatNcb.ncb_retcode == NRC_INCOMP ) {
+ for ( i=0 ; i < min(AdapterStatus.AdapterInfo.name_count, MAX_NETBIOS_NAMES) ; i++ ) {
+ if (AdapterStatus.Names[i].name[NCBNAMSZ-1] == SERVER_SIGNATURE) {
+ DWORD j;
+
+ //
+ // Ignore malformed netbios names.
+ //
+ // Some transports have strange netbios names. For instance,
+ // netbt registers a netbios name where the first 12 bytes
+ // are 0 and the last 4 bytes are the IP address.
+ //
+ for ( j = 0 ; j < CNLEN ; j++ ) {
+ if (AdapterStatus.Names[i].name[j] == '\0') {
+ break;
+ }
+ }
+
+ if ( j != CNLEN ) {
+ continue;
+ }
+
+ //
+ // Convert to unicode
+ //
+
+ if (MultiByteToWideChar(CP_OEMCP,
+ 0,
+ AdapterStatus.Names[i].name,
+ CNLEN,
+ MasterName,
+ CNLEN) == 0) {
+ return(GetLastError());
+ }
+
+ for (j = CNLEN - 1; j ; j -= 1) {
+ if (MasterName[j] != L' ') {
+ MasterName[j+1] = UNICODE_NULL;
+ break;
+ }
+ }
+
+ return NERR_Success;
+ }
+ }
+ } else {
+ return AStatNcb.ncb_retcode;
+ }
+}
+
+ NET_API_STATUS
+SendDatagram(
+ IN HANDLE DgReceiverHandle,
+ IN PUNICODE_STRING Network,
+#ifdef _CAIRO_
+ IN PUNICODE_STRING EmulatedDomainName,
+#endif // _CAIRO_
+ IN PWSTR ResponseName,
+ IN DGRECEIVER_NAME_TYPE NameType,
+ IN PVOID Buffer,
+ IN ULONG BufferLength
+ )
+{
+ NET_API_STATUS Status;
+ UCHAR PacketBuffer[sizeof(LMDR_REQUEST_PACKET)+(LM20_CNLEN+1)*sizeof(WCHAR)];
+ PLMDR_REQUEST_PACKET RequestPacket = (PLMDR_REQUEST_PACKET)PacketBuffer;
+
+
+ RequestPacket->Version = LMDR_REQUEST_PACKET_VERSION;
+
+ RequestPacket->TransportName = *Network;
+#ifdef _CAIRO_
+ RequestPacket->EmulatedDomainName = *EmulatedDomainName;
+#endif // _CAIRO_
+
+ RequestPacket->Type = Datagram;
+
+ RequestPacket->Parameters.SendDatagram.DestinationNameType = NameType;
+
+ RequestPacket->Parameters.SendDatagram.MailslotNameLength = 0;
+
+ //
+ // The domain announcement name is special, so we don't have to specify
+ // a destination name for it.
+ //
+
+ RequestPacket->Parameters.SendDatagram.NameLength = wcslen(ResponseName)*sizeof(WCHAR);
+
+ wcscpy(RequestPacket->Parameters.SendDatagram.Name, ResponseName);
+
+ //
+ // This is a simple IoControl - It just sends the datagram.
+ //
+
+ Status = BrDgReceiverIoControl(DgReceiverHandle,
+ IOCTL_LMDR_WRITE_MAILSLOT,
+ RequestPacket,
+ FIELD_OFFSET(LMDR_REQUEST_PACKET, Parameters.SendDatagram.Name)+
+ RequestPacket->Parameters.SendDatagram.NameLength,
+ Buffer,
+ BufferLength,
+ NULL);
+
+ return Status;
+}
+
diff --git a/private/net/svcdlls/browser/dirs b/private/net/svcdlls/browser/dirs
new file mode 100644
index 000000000..7ae0222c7
--- /dev/null
+++ b/private/net/svcdlls/browser/dirs
@@ -0,0 +1,30 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ dirs.
+
+Abstract:
+
+ This file specifies the subdirectories of the current directory that
+ contain component makefiles.
+
+
+Author:
+
+ Steve Wood (stevewo) 17-Apr-1990
+
+NOTE: Commented description of this file is in \nt\bak\bin\dirs.tpl
+
+!ENDIF
+
+
+DIRS=common \
+ server \
+ client
+
+
+#OPTIONAL_DIRS=dir8 \
+# dir9
diff --git a/private/net/svcdlls/browser/imports.h b/private/net/svcdlls/browser/imports.h
new file mode 100644
index 000000000..1577f8707
--- /dev/null
+++ b/private/net/svcdlls/browser/imports.h
@@ -0,0 +1,46 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ imports.h
+
+Abstract:
+
+ This file allows us to include standard system header files in the
+ .idl file. The main .idl file imports a file called import.idl.
+ This allows the .idl file to use the types defined in these header
+ files. It also causes the following line to be added in the
+ MIDL generated header file:
+
+ #include "imports.h"
+
+ Thus these types are available to the RPC stub routines as well.
+
+Author:
+
+ Dan Lafferty (danl) 07-May-1991
+
+Revision History:
+
+
+--*/
+
+
+#include <windef.h>
+#include <lmcons.h>
+
+#ifdef MIDL_PASS
+#ifdef UNICODE
+#define LPTSTR [string] wchar_t*
+#else
+#define LPTSTR [string] LPTSTR
+#endif
+#define LPSTR [string] LPSTR
+#define BOOL DWORD
+#endif
+
+#include <lmserver.h>
+#include <lmbrowsr.h>
+
diff --git a/private/net/svcdlls/browser/imports.idl b/private/net/svcdlls/browser/imports.idl
new file mode 100644
index 000000000..46fc29d70
--- /dev/null
+++ b/private/net/svcdlls/browser/imports.idl
@@ -0,0 +1,67 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ imports.idl
+
+Abstract:
+
+ This file is useful for creating RPC interfaces that require the use
+ of windef types. The .idl file for the RPC product should contain a
+ line in the interface body that imports this file. For example:
+
+ import "imports.h";
+
+ Doing this causes the MIDL generated header file to contain the
+ following line:
+
+ #include "imports.h"
+
+ If this technique is not used, and instead the .idl file for the RPC
+ product simply contains #include <importsf.h>, then the contents of
+ imports.h will be expanded in the MIDL generated header file. This
+ can lead to duplicate definition problems later when the RPC client
+ or RPC server code needs to include both the MIDL generated header file
+ and a file that is included in imports.h.
+
+Author:
+
+ Dan Lafferty (danl) 20-Mar-1991
+
+Environment:
+
+ User Mode - Win32 - for use with the MIDL compiler
+
+
+Revision History:
+
+ 03-Apr-1991 danl
+ created
+
+--*/
+
+[
+ uuid(12345678-1234-ABCD-EF00-9948756789AB),
+#ifdef __midl
+ ms_union,
+#endif // __midl
+ version(0.0)
+]
+interface imports
+
+{
+#define MIDL_PASS
+#include "imports.h"
+
+//
+// All .idl files need to contain at least one function prototype
+//
+
+DWORD
+Dummy(
+ [in] DWORD DummyParm);
+
+
+}
diff --git a/private/net/svcdlls/browser/makefil0 b/private/net/svcdlls/browser/makefil0
new file mode 100644
index 000000000..c92408bcd
--- /dev/null
+++ b/private/net/svcdlls/browser/makefil0
@@ -0,0 +1,61 @@
+#
+# This is the MIDL compile phase of the build process.
+#
+# The following is where you put the name of your .idl file without
+# the .idl extension:
+#
+
+!INCLUDE $(NTMAKEENV)\makefile.plt
+
+IDL_NAME = bowser
+
+!IFNDEF DISABLE_NET_UNICODE
+UNICODE=1
+NET_C_DEFINES=-DUNICODE
+!ENDIF
+
+SDKINC = $(BASEDIR)\public\sdk\inc
+NETINC = $(BASEDIR)\private\net\inc
+SDKCRTINC = $(BASEDIR)\public\sdk\inc\crt
+PRIVINC = $(BASEDIR)\private\inc
+
+INCS = -I$(SDKINC) -I$(SDKCRTINC) -I$(PRIVINC) -I$(NETINC)
+
+CLIENT_TARGETS = client\$(IDL_NAME)_c.c \
+ .\$(IDL_NAME).h
+
+SERVER_TARGETS = server\$(IDL_NAME)_s.mdl
+
+EXTRN_DEPENDS = $(SDKINC)\lmcons.h \
+ $(SDKINC)\windef.h \
+ $(SDKINC)\lmserver.h \
+ $(SDKINC)\lmbrowsr.h \
+ $(IDL_NAME).acf
+
+CPP = -cpp_cmd "$(MIDL_CPP)" $(MIDL_FLAGS) $(C_DEFINES) $(NET_C_DEFINES)
+
+#
+# Define Products and Dependencies
+#
+
+all: $(CLIENT_TARGETS) $(SERVER_TARGETS) $(EXTRN_DEPENDS)
+!IF "$(BUILDMSG)" != ""
+ @ech ; $(BUILDMSG) ;
+!ENDIF
+
+clean: delsrc all
+
+delsrc:
+ erase $(CLIENT_TARGETS) $(SERVER_TARGETS)
+
+#
+# MIDL COMPILE
+#
+
+$(CLIENT_TARGETS) : .\$(IDL_NAME).idl $(EXTRN_DEPENDS)
+ midl -Oi -server none -oldnames -error allocation -error ref -ms_ext -c_ext $(CPP) .\$(IDL_NAME).idl $(INCS)
+ IF EXIST $(IDL_NAME)_c.c copy $(IDL_NAME)_c.c .\client & del $(IDL_NAME)_c.c
+
+$(SERVER_TARGETS) : .\$(IDL_NAME).idl $(EXTRN_DEPENDS)
+ midl -client none -oldnames -error stub_data -error allocation -error ref -ms_ext -c_ext $(CPP) .\$(IDL_NAME).idl $(INCS)
+ IF EXIST $(IDL_NAME)_s.c copy $(IDL_NAME)_s.c .\server\$(IDL_NAME)_s.mdl & del $(IDL_NAME)_s.c
diff --git a/private/net/svcdlls/browser/server/bowqueue.c b/private/net/svcdlls/browser/server/bowqueue.c
new file mode 100644
index 000000000..b365a8932
--- /dev/null
+++ b/private/net/svcdlls/browser/server/bowqueue.c
@@ -0,0 +1,644 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ bowqueue.c
+
+Abstract:
+
+ This module implements a worker thread and a set of functions for
+ passing work to it.
+
+Author:
+
+ Larry Osterman (LarryO) 13-Jul-1992
+
+
+Revision History:
+
+--*/
+
+#include "precomp.h"
+#pragma hdrstop
+
+//
+// Number of worker threads to create and the usage count array.
+//
+
+
+ULONG BrNumberOfWorkerThreads = 0;
+
+PULONG
+BrWorkerThreadCount = NULL;
+
+PHANDLE
+BrThreadArray = NULL;
+
+PHANDLE
+BrTerminateArray = NULL;
+
+//
+// Synchronization event to guard the WorkQueue list.
+//
+
+HANDLE
+BrWorkerLock = NULL;
+
+#if DBG
+#define LOCK_WORK_QUEUE() \
+ if (WaitForSingleObject(BrWorkerLock, 0xffffffff) == 0xffffffff) { \
+ KdPrint(("BROWSER: Unable to lock work queue %ld\n", GetLastError())); \
+ }
+
+#define UNLOCK_WORK_QUEUE() \
+ if (!SetEvent(BrWorkerLock)) { \
+ KdPrint(("BROWSER: Unable to unlock work queue %ld\n", GetLastError())); \
+ }
+#else
+#define LOCK_WORK_QUEUE() WaitForSingleObject(BrWorkerLock, 0xffffffff);
+#define UNLOCK_WORK_QUEUE() SetEvent(BrWorkerLock);
+#endif
+//
+// Head of singly linked list of work items queued to the worker thread.
+//
+
+LIST_ENTRY
+BrWorkerQueueHead = {0};
+
+//
+// Event that is signal whenever a work item is put in the queue. The
+// worker thread waits on this event.
+//
+
+HANDLE
+BrWorkerSemaphore = NULL;
+
+VOID
+BrTimerRoutine(
+ IN PVOID TimerContext,
+ IN ULONG TImerLowValue,
+ IN LONG TimerHighValue
+ );
+
+NET_API_STATUS
+BrWorkerInitialization(
+ VOID
+ )
+{
+ ULONG Index;
+ ULONG Status = NERR_Success;
+ ULONG ThreadId;
+
+ try {
+ //
+ // Initialize the work queue spinlock, list head, and semaphore.
+ //
+
+ BrWorkerLock = CreateEvent( NULL, FALSE, TRUE, NULL );
+
+ if (BrWorkerLock == NULL) {
+ Status = GetLastError();
+
+ try_return(Status);
+ }
+
+ BrWorkerSemaphore = CreateSemaphore(NULL, 0, 0x7fffffff, NULL);
+
+ if (BrWorkerSemaphore == NULL) {
+ Status = GetLastError();
+
+ try_return(Status);
+ }
+
+ InitializeListHead( &BrWorkerQueueHead );
+
+ BrThreadArray = LocalAlloc(LMEM_ZEROINIT, (NumberOfServicedNetworks+1)*sizeof(HANDLE));
+
+ if (BrThreadArray == NULL) {
+ try_return(Status = ERROR_NOT_ENOUGH_MEMORY);
+ }
+
+
+ BrTerminateArray = LocalAlloc(LMEM_ZEROINIT, (NumberOfServicedNetworks+1)*sizeof(HANDLE));
+
+ if (BrTerminateArray == NULL) {
+ try_return(Status = ERROR_NOT_ENOUGH_MEMORY);
+ }
+
+ BrWorkerThreadCount = (PULONG)LocalAlloc(LMEM_ZEROINIT, (NumberOfServicedNetworks+1)*sizeof(HANDLE)*2);
+
+ if (BrWorkerThreadCount == NULL) {
+ try_return(Status = ERROR_NOT_ENOUGH_MEMORY);
+ }
+
+ //
+ // Create the desired number of worker threads.
+ //
+
+ for (Index = 0; Index < NumberOfServicedNetworks; Index += 1) {
+
+ BrThreadArray[Index] = CreateThread(NULL,
+ 0,
+ (LPTHREAD_START_ROUTINE)BrWorkerThread,
+ (PVOID)Index,
+ 0,
+ &ThreadId
+ );
+
+ if (BrThreadArray[Index] == NULL) {
+ Status = GetLastError();
+ break;
+ }
+
+ //
+ // Set the browser threads to time critical priority.
+ //
+
+ SetThreadPriority(BrThreadArray[Index], THREAD_PRIORITY_ABOVE_NORMAL);
+
+
+ BrTerminateArray[Index] = CreateEvent(NULL, TRUE, FALSE, NULL);
+
+ if (BrTerminateArray[Index] == NULL) {
+ Status = GetLastError();
+ break;
+ }
+
+
+ }
+
+try_exit:NOTHING;
+ } finally {
+
+ if (Status != NERR_Success) {
+ if (BrWorkerSemaphore != NULL) {
+ CloseHandle(BrWorkerSemaphore);
+
+ BrWorkerSemaphore = NULL;
+ }
+
+ for (Index = 0 ; Index < NumberOfServicedNetworks ; Index += 1) {
+ if (BrThreadArray[Index] != 0) {
+ TerminateThread(BrThreadArray[Index], 0);
+ CloseHandle(BrThreadArray[Index]);
+ }
+ }
+
+ for (Index = 0 ; Index < NumberOfServicedNetworks ; Index += 1) {
+ if (BrTerminateArray[Index] != 0) {
+ CloseHandle(BrTerminateArray[Index]);
+ }
+ }
+
+ if (BrThreadArray != NULL) {
+ LocalFree(BrThreadArray);
+ BrThreadArray = NULL;
+ }
+
+ if (BrTerminateArray != NULL) {
+ LocalFree(BrTerminateArray);
+ BrTerminateArray = NULL;
+ }
+
+ if (BrWorkerThreadCount != NULL) {
+ LocalFree(BrWorkerThreadCount);
+ BrWorkerThreadCount = NULL;
+ }
+
+ BrNumberOfWorkerThreads = 0;
+
+ }
+ }
+
+ return Status;
+}
+
+NET_API_STATUS
+BrWorkerTermination(
+ VOID
+ )
+{
+ ULONG Index;
+
+ //
+ // Make sure the terminate now event is in the signalled state to unwind
+ // all our threads.
+ //
+
+ SetEvent( BrGlobalData.TerminateNowEvent );
+
+ for ( Index = 0 ; Index < NumberOfServicedNetworks ; Index += 1 ) {
+ if ( BrThreadArray[Index] != NULL ) {
+
+ WaitForSingleObject( BrTerminateArray[Index], 0xffffffff );
+
+ CloseHandle( BrTerminateArray[Index] );
+
+ CloseHandle( BrThreadArray[Index] );
+ }
+
+ }
+
+ CloseHandle( BrWorkerLock );
+
+ BrWorkerLock = NULL;
+
+ if ( BrWorkerSemaphore != NULL ) {
+ CloseHandle( BrWorkerSemaphore );
+
+ BrWorkerSemaphore = NULL;
+ }
+
+ if (BrThreadArray != NULL) {
+ LocalFree(BrThreadArray);
+
+ BrThreadArray = NULL;
+
+ }
+
+ if (BrTerminateArray != NULL) {
+ LocalFree(BrTerminateArray);
+
+ BrTerminateArray = NULL;
+ }
+
+ if (BrWorkerThreadCount != NULL) {
+ LocalFree(BrWorkerThreadCount);
+
+ BrWorkerThreadCount = NULL;
+ }
+
+ BrNumberOfWorkerThreads = 0;
+
+ return NERR_Success;
+}
+
+VOID
+BrQueueWorkItem(
+ IN PWORKER_ITEM WorkItem
+ )
+
+/*++
+
+Routine Description:
+
+ This function queues a work item to a queue that is processed by
+ a worker thread. This thread runs at low priority, at IRQL 0
+
+Arguments:
+
+ WorkItem - Supplies a pointer to the work item to add the the queue.
+ This structure must be located in NonPagedPool. The work item
+ structure contains a doubly linked list entry, the address of a
+ routine to call and a parameter to pass to that routine. It is
+ the routine's responsibility to reclaim the storage occupied by
+ the WorkItem structure.
+
+Return Value:
+
+ Status value -
+
+--*/
+
+{
+ //
+ // Acquire the worker thread spinlock and insert the work item in the
+ // list and release the worker thread semaphore if the work item is
+ // not already in the list.
+ //
+
+ LOCK_WORK_QUEUE();
+
+ if (WorkItem->Inserted == FALSE) {
+
+ dprintf(QUEUE, ("Inserting work item %lx (%lx)\n",WorkItem, WorkItem->WorkerRoutine));
+
+ InsertTailList( &BrWorkerQueueHead, &WorkItem->List );
+
+ WorkItem->Inserted = TRUE;
+
+ ReleaseSemaphore( BrWorkerSemaphore,
+ 1,
+ NULL
+ );
+ }
+
+ UNLOCK_WORK_QUEUE();
+
+ return;
+}
+
+ VOID
+BrWorkerThread(
+ IN PVOID StartContext
+ )
+
+{
+ NET_API_STATUS NetStatus;
+
+#define WORKER_SIGNALED 0
+#define TERMINATION_SIGNALED 1
+#define REG_CHANGE_SIGNALED 2
+#define NUMBER_OF_EVENTS 3
+ HANDLE WaitList[NUMBER_OF_EVENTS];
+ ULONG WaitCount = 0;
+
+ ULONG Index;
+ PWORKER_ITEM WorkItem;
+ ULONG ThreadIndex = (ULONG)StartContext;
+
+ HKEY RegistryHandle = NULL;
+ HANDLE EventHandle = NULL;
+
+ WaitList[WORKER_SIGNALED] = BrWorkerSemaphore;
+ WaitCount ++;
+ WaitList[TERMINATION_SIGNALED] = BrGlobalData.TerminateNowEvent;
+ WaitCount ++;
+
+ //
+ // Primary thread waits on registry changes, too.
+ //
+ if ( ThreadIndex == 0xFFFFFFFF ) {
+ DWORD RegStatus;
+ NET_API_STATUS NetStatus;
+
+ //
+ // Register for notifications of changes to Parameters
+ //
+ // Failure doesn't affect normal operation of the browser.
+ //
+
+ RegStatus = RegOpenKeyA( HKEY_LOCAL_MACHINE,
+ "System\\CurrentControlSet\\Services\\Browser\\Parameters",
+ &RegistryHandle );
+
+ if ( RegStatus != ERROR_SUCCESS ) {
+ dprintf(QUEUE, ("BrWorkerThead: Can't RegOpenKey %ld\n", RegStatus ));
+ } else {
+
+ EventHandle = CreateEvent(
+ NULL, // No security attributes
+ TRUE, // Automatically reset
+ FALSE, // Initially not signaled
+ NULL ); // No name
+
+ if ( EventHandle == NULL ) {
+ dprintf(QUEUE, ("BrWorkerThead: Can't CreateEvent %ld\n", GetLastError() ));
+ } else {
+ NetStatus = RegNotifyChangeKeyValue(
+ RegistryHandle,
+ FALSE, // Ignore subkeys
+ REG_NOTIFY_CHANGE_LAST_SET, // Notify of value changes
+ EventHandle,
+ TRUE ); // Signal event upon change
+
+ if ( NetStatus != NERR_Success ) {
+ dprintf(QUEUE, ("BrWorkerThead: Can't RegNotifyChangeKeyValue %ld\n", NetStatus ));
+ } else {
+ WaitList[REG_CHANGE_SIGNALED] = EventHandle;
+ WaitCount ++;
+ }
+ }
+ }
+ }
+
+ dprintf(QUEUE, ("Starting new work thread, Context: %lx\n", StartContext));
+
+ //
+ // Set the thread priority to the lowest realtime level.
+ //
+
+ while( TRUE ) {
+ ULONG WaitItem;
+
+ LOCK_WORK_QUEUE();
+
+ //
+ // Wait until something is put in the queue (semaphore is
+ // released), remove the item from the queue, mark it not
+ // inserted, and execute the specified routine.
+ //
+
+ BrNumberOfWorkerThreads += 1;
+
+ UNLOCK_WORK_QUEUE();
+
+ dprintf(QUEUE, ("%lx: waiting\n", StartContext));
+
+ do {
+ WaitItem = WaitForMultipleObjectsEx( WaitCount, WaitList, FALSE, 0xffffffff, TRUE );
+ } while ( WaitItem == WAIT_IO_COMPLETION );
+
+ if (WaitItem == 0xffffffff) {
+ InternalError(("WaitForMultipleObjects in browser queue returned %ld\n", GetLastError()));
+ break;
+ }
+
+ if (WaitItem == TERMINATION_SIGNALED) {
+ break;
+
+ //
+ // If the registry has changed,
+ // process the changes.
+ //
+
+ } else if ( WaitItem == REG_CHANGE_SIGNALED ) {
+
+ //
+ // Setup for future notifications.
+ //
+ NetStatus = RegNotifyChangeKeyValue(
+ RegistryHandle,
+ FALSE, // Ignore subkeys
+ REG_NOTIFY_CHANGE_LAST_SET, // Notify of value changes
+ EventHandle,
+ TRUE ); // Signal event upon change
+
+ if ( NetStatus != NERR_Success ) {
+ dprintf(QUEUE, ("BrWorkerThead: Can't RegNotifyChangeKeyValue %ld\n", NetStatus ));
+ }
+
+
+ NetStatus = BrReadBrowserConfigFields( FALSE );
+
+ if ( NetStatus != NERR_Success) {
+ dprintf(QUEUE, ("BrWorkerThead: Can't BrReadConfigFields %ld\n", NetStatus ));
+ }
+
+ continue;
+
+ }
+
+ dprintf(QUEUE, ("%lx: Waking up\n", StartContext));
+
+ LOCK_WORK_QUEUE();
+
+ Index = BrNumberOfWorkerThreads;
+
+ BrNumberOfWorkerThreads -= 1;
+
+ BrWorkerThreadCount[Index - 1] += 1;
+
+ ASSERT (!IsListEmpty(&BrWorkerQueueHead));
+
+ if (!IsListEmpty(&BrWorkerQueueHead)) {
+ WorkItem = (PWORKER_ITEM)RemoveHeadList( &BrWorkerQueueHead );
+
+ ASSERT (WorkItem->Inserted);
+
+ WorkItem->Inserted = FALSE;
+
+ } else {
+ WorkItem = NULL;
+ }
+
+ UNLOCK_WORK_QUEUE();
+
+ dprintf(QUEUE, ("%lx: Pulling off work item %lx (%lx)\n", StartContext, WorkItem, WorkItem->WorkerRoutine));
+
+ //
+ // Execute the specified routine.
+ //
+
+ if (WorkItem != NULL) {
+ (WorkItem->WorkerRoutine)( WorkItem->Parameter );
+ }
+
+ }
+
+ dprintf(QUEUE, ("%lx: Exiting\n", StartContext));
+
+ //
+ // Clean up after ourselves
+ //
+
+ if ( RegistryHandle != NULL ) {
+ (VOID) RegCloseKey( RegistryHandle );
+ }
+
+ if ( EventHandle != NULL ) {
+ (VOID) CloseHandle( EventHandle );
+ }
+
+ if ( ThreadIndex <= NumberOfServicedNetworks ) {
+ IO_STATUS_BLOCK IoSb;
+
+ //
+ // Cancel any I/O outstanding on this file for this thread.
+ //
+
+ NtCancelIoFile(BrDgReceiverDeviceHandle, &IoSb);
+
+ SetEvent(BrTerminateArray[ThreadIndex]);
+ }
+
+}
+
+ NET_API_STATUS
+BrCreateTimer(
+ IN PBROWSER_TIMER Timer
+ )
+{
+ OBJECT_ATTRIBUTES ObjA;
+ NTSTATUS Status;
+
+ InitializeObjectAttributes(&ObjA, NULL, 0, NULL, NULL);
+
+ Status = NtCreateTimer(&Timer->TimerHandle,
+ TIMER_ALL_ACCESS,
+ &ObjA,
+ NotificationTimer);
+
+ if (!NT_SUCCESS(Status)) {
+ dprintf(TIMER, ("Failed to create timer %lx: %X\n", Timer, Status));
+ return(BrMapStatus(Status));
+ }
+
+ dprintf(TIMER, ("Creating timer %lx: Handle: %lx\n", Timer, Timer->TimerHandle));
+
+ return(NERR_Success);
+}
+
+ NET_API_STATUS
+BrDestroyTimer(
+ IN PBROWSER_TIMER Timer
+ )
+{
+ dprintf(TIMER, ("Destroying timer %lx\n", Timer));
+
+ return BrMapStatus(NtClose(Timer->TimerHandle));
+
+}
+
+ NET_API_STATUS
+BrCancelTimer(
+ IN PBROWSER_TIMER Timer
+ )
+{
+ dprintf(TIMER, ("Canceling timer %lx\n", Timer));
+ return BrMapStatus(NtCancelTimer(Timer->TimerHandle, NULL));
+}
+
+ NET_API_STATUS
+BrSetTimer(
+ IN PBROWSER_TIMER Timer,
+ IN ULONG MillisecondsToExpire,
+ IN PBROWSER_WORKER_ROUTINE WorkerFunction,
+ IN PVOID Context
+ )
+{
+ LARGE_INTEGER TimerDueTime;
+ NTSTATUS NtStatus;
+
+ dprintf(TIMER, ("Setting timer %lx to %ld milliseconds, WorkerFounction %lx, Context: %lx\n", Timer, MillisecondsToExpire, WorkerFunction, Context));
+
+ //
+ // Figure out the timeout.
+ //
+
+ TimerDueTime.QuadPart = Int32x32To64( MillisecondsToExpire, -10000 );
+
+ BrInitializeWorkItem(&Timer->WorkItem, WorkerFunction, Context);
+
+ //
+ // Set the timer to go off when it expires.
+ //
+
+ NtStatus = NtSetTimer(Timer->TimerHandle,
+ &TimerDueTime,
+ BrTimerRoutine,
+ Timer,
+ FALSE,
+ 0,
+ NULL
+ );
+
+ if (!NT_SUCCESS(NtStatus)) {
+#if DBG
+ KdPrint(("Browser: Unable to set browser timer expiration: %X (%lx)\n", NtStatus, Timer));
+ DbgBreakPoint();
+#endif
+
+ return(BrMapStatus(NtStatus));
+ }
+
+ return NERR_Success;
+
+
+}
+
+ VOID
+BrTimerRoutine(
+ IN PVOID TimerContext,
+ IN ULONG TImerLowValue,
+ IN LONG TimerHighValue
+ )
+{
+ PBROWSER_TIMER Timer = TimerContext;
+
+ dprintf(TIMER, ("Timer %lx fired\n", Timer));
+
+ BrQueueWorkItem(&Timer->WorkItem);
+}
diff --git a/private/net/svcdlls/browser/server/bowqueue.h b/private/net/svcdlls/browser/server/bowqueue.h
new file mode 100644
index 000000000..d0290a133
--- /dev/null
+++ b/private/net/svcdlls/browser/server/bowqueue.h
@@ -0,0 +1,101 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ bowqueue.h
+
+Abstract:
+
+ Private header file for the NT Browser service. This file describes
+ the bowser thread queue interfaces.
+
+Author:
+
+ Larry Osterman (larryo) 15-Feb-1991
+
+Revision History:
+
+--*/
+
+#ifndef _BOWQUEUE_
+#define _BOWQUEUE_
+
+
+typedef
+VOID
+(*PBROWSER_WORKER_ROUTINE) (
+ IN PVOID Parameter
+ );
+
+
+typedef struct _WORKER_ITEM {
+ LIST_ENTRY List;
+ PBROWSER_WORKER_ROUTINE WorkerRoutine;
+ PVOID Parameter;
+ BOOLEAN Inserted;
+} WORKER_ITEM, *PWORKER_ITEM;
+
+typedef struct _BROWSER_TIMER {
+ HANDLE TimerHandle;
+ WORKER_ITEM WorkItem;
+} BROWSER_TIMER, *PBROWSER_TIMER;
+
+
+VOID
+BrWorkerThread(
+ IN PVOID Context
+ );
+
+
+VOID
+BrQueueWorkItem(
+ IN PWORKER_ITEM WorkItem
+ );
+
+NET_API_STATUS
+BrWorkerInitialization(
+ VOID
+ );
+
+VOID
+BrWorkerKillThreads(
+ VOID
+ );
+
+NET_API_STATUS
+BrWorkerTermination (
+ VOID
+ );
+
+NET_API_STATUS
+BrSetTimer(
+ IN PBROWSER_TIMER Timer,
+ IN ULONG MilliSecondsToExpire,
+ IN PBROWSER_WORKER_ROUTINE WorkerFunction,
+ IN PVOID Context
+ );
+
+NET_API_STATUS
+BrCancelTimer(
+ IN PBROWSER_TIMER Timer
+ );
+
+NET_API_STATUS
+BrDestroyTimer(
+ IN PBROWSER_TIMER Timer
+ );
+
+NET_API_STATUS
+BrCreateTimer(
+ IN PBROWSER_TIMER Timer
+ );
+
+#define BrInitializeWorkItem(Item, Routine, Context) \
+ (Item)->WorkerRoutine = (Routine); \
+ (Item)->Parameter = (Context); \
+ (Item)->Inserted = FALSE;
+
+
+#endif // ifdef _BOWQUEUE_
diff --git a/private/net/svcdlls/browser/server/bowsvc.rc b/private/net/svcdlls/browser/server/bowsvc.rc
new file mode 100644
index 000000000..ec011c4ee
--- /dev/null
+++ b/private/net/svcdlls/browser/server/bowsvc.rc
@@ -0,0 +1,12 @@
+#include <windows.h>
+
+#include <ntverp.h>
+
+#define VER_FILETYPE VFT_DLL
+#define VER_FILESUBTYPE VFT2_UNKNOWN
+#define VER_FILEDESCRIPTION_STR "Computer Browser Service DLL"
+#define VER_INTERNALNAME_STR "browser.dll"
+#define VER_ORIGINALFILENAME_STR "browser.dll"
+
+#include "common.ver"
+
diff --git a/private/net/svcdlls/browser/server/br.h b/private/net/svcdlls/browser/server/br.h
new file mode 100644
index 000000000..18ca110c3
--- /dev/null
+++ b/private/net/svcdlls/browser/server/br.h
@@ -0,0 +1,137 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ br.h
+
+Abstract:
+
+ Private header file for the NT Browser service included by every
+ module of the Workstation service.
+
+Author:
+
+ Rita Wong (ritaw) 15-Feb-1991
+
+Revision History:
+
+--*/
+
+#ifndef _BR_INCLUDED_
+#define _BR_INCLUDED_
+
+
+#include <nt.h> // NT definitions
+#include <ntrtl.h> // NT runtime library definitions
+#include <nturtl.h>
+
+#include <windef.h> // Win32 type definitions
+#include <winbase.h> // Win32 base API prototypes
+#include <winsvc.h> // Win32 service control APIs
+
+#include <lmcons.h> // LAN Manager common definitions
+#include <lmerr.h> // LAN Manager network error definitions
+#include <lmsname.h> // LAN Manager service names
+#include <lmapibuf.h> // NetApiBufferFree
+#include <lmserver.h>
+
+#include <netlib.h> // LAN Man utility routines
+#include <netlibnt.h> // NetpNtStatusToApiStatus
+#include <netdebug.h> // NetpDbgPrint
+#include <tstring.h> // Transitional string functions
+#include <icanon.h> // I_Net canonicalize functions
+#include <align.h> // ROUND_UP_COUNT macro
+#include <services.h> // LM Extensions of service definitions
+#include <strarray.h>
+
+#include <rpc.h> // DataTypes and runtime APIs
+#include <rpcutil.h> // Prototypes for MIDL user functions
+#include <bowser.h> // Generated by the MIDL complier
+#include <winsvc.h>
+#include <srvann.h>
+#include <lmbrowsr.h>
+
+#include <ntddbrow.h>
+#include <brcommon.h> // Common browser routines.
+#include <rx.h>
+#include <rxserver.h>
+
+#include "brconst.h"
+#include "bowqueue.h"
+#include "browsnet.h"
+#include "browslst.h"
+#include "brutil.h"
+#include "brwan.h"
+#include "brmain.h"
+#include "brdevice.h"
+#include "brconfig.h"
+#include "browsdom.h"
+#include "brbackup.h"
+#include "brmaster.h"
+#include "srvenum.h"
+
+//
+// The following macros are used to establish the semantics needed
+// to do a return from within a try-finally clause. As a rule every
+// try clause must end with a label call try_exit. For example,
+//
+// try {
+// :
+// :
+//
+// try_exit: NOTHING;
+// } finally {
+//
+// :
+// :
+// }
+//
+// Every return statement executed inside of a try clause should use the
+// try_return macro. If the compiler fully supports the try-finally construct
+// then the macro should be
+//
+// #define try_return(S) { return(S); }
+//
+// If the compiler does not support the try-finally construct then the macro
+// should be
+//
+
+#define try_return(S) { S; goto try_exit; }
+
+
+
+
+#if DBG
+
+#define STATIC
+
+#define BR_DEBUG if (TRUE)
+
+#define InternalError(PrintfString) { \
+ NetpDbgPrint PrintfString; \
+ BrowserTrace PrintfString; \
+}
+
+#define dprintf(LEVEL,String) { \
+ if (((BROWSER_DEBUG_ ## LEVEL) == 0) || (BrInfo.BrowserDebug & (BROWSER_DEBUG_ ## LEVEL))) { \
+ BrowserTrace String; \
+ } \
+}
+
+
+#else
+
+#define STATIC static
+
+#define BR_DEBUG if (FALSE)
+
+#define InternalError(PrintfString)
+
+#define dprintf(LEVEL, String) {NOTHING;}
+
+#endif // DBG
+
+
+#endif // ifdef _BR_INCLUDED_
diff --git a/private/net/svcdlls/browser/server/brbackup.h b/private/net/svcdlls/browser/server/brbackup.h
new file mode 100644
index 000000000..a1c6fdca5
--- /dev/null
+++ b/private/net/svcdlls/browser/server/brbackup.h
@@ -0,0 +1,78 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ brmain.h
+
+Abstract:
+
+ Private header file which defines the global data which is used for
+ communication between the service control handler and the
+ rest of the NT Workstation service.
+
+Author:
+
+ Rita Wong (ritaw) 06-May-1991
+
+Revision History:
+
+--*/
+
+#ifndef _BRBACKUP_INCLUDED_
+#define _BRBACKUP_INCLUDED_
+
+NET_API_STATUS
+BecomeBackup(
+ IN PNETWORK Network,
+ IN PVOID Context
+ );
+
+NET_API_STATUS
+BrBecomeBackup(
+ VOID
+ );
+
+NET_API_STATUS
+BrStopBackup (
+ IN PNETWORK Network
+ );
+
+NET_API_STATUS
+BrPostBecomeBackup(
+ VOID
+ );
+
+ NET_API_STATUS
+BrPostBecomeMaster(
+ VOID
+ );
+
+NET_API_STATUS
+BrPostGetMasterAnnouncement (
+ VOID
+ );
+
+NET_API_STATUS
+BrPostWaitForRoleChange (
+ VOID
+ );
+
+NET_API_STATUS
+BrStopMaster(
+ IN PNETWORK Network
+ );
+
+NET_API_STATUS
+StartBackupBrowserTimer(
+ IN PNETWORK Network
+ );
+
+NET_API_STATUS
+BackupBrowserTimerRoutine (
+ IN PVOID TimerContext
+ );
+
+#endif // ifndef _BRBACKUP_INCLUDED_
+
diff --git a/private/net/svcdlls/browser/server/brconfig.c b/private/net/svcdlls/browser/server/brconfig.c
new file mode 100644
index 000000000..27d4961ec
--- /dev/null
+++ b/private/net/svcdlls/browser/server/brconfig.c
@@ -0,0 +1,734 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ brconfig.c
+
+Abstract:
+
+ This module contains the Browser service configuration routines.
+
+Author:
+
+ Rita Wong (ritaw) 22-May-1991
+
+Revision History:
+
+--*/
+
+#include "precomp.h"
+#pragma hdrstop
+
+
+
+//-------------------------------------------------------------------//
+// //
+// Global variables //
+// //
+//-------------------------------------------------------------------//
+
+//
+// Browser configuration information structure which holds the
+// computername, primary domain, browser config buffer, and a resource
+// to serialize access to the whole thing.
+//
+BRCONFIGURATION_INFO BrInfo = {0};
+
+STATIC BR_BROWSER_FIELDS BrFields[] = {
+
+ {WKSTA_KEYWORD_MAINTAINSRVLST, (LPDWORD) &BrInfo.MaintainServerList,
+ 1,(DWORD)-1, 0, TriValueType, 0, NULL},
+
+ {WKSTA_KEYWORD_ISDOMAINMASTER, (LPDWORD) &BrInfo.IsDomainMasterBrowser,
+ 0, 0, 1, BooleanType, 0, NULL},
+
+ {BROWSER_CONFIG_BACKUP_RECOVERY_TIME, &BrInfo.BackupBrowserRecoveryTime,
+ BACKUP_BROWSER_RECOVERY_TIME, 0, 0xffffffff, DWordType, 0, NULL},
+
+ {L"CacheHitLimit", &BrInfo.CacheHitLimit,
+// {BROWSER_CONFIG_CACHE_HIT_LIMIT, &BrInfo.CacheHitLimit,
+ CACHED_BROWSE_RESPONSE_HIT_LIMIT, 0, 0x100, DWordType, 0, NULL },
+
+ {L"CacheResponseSize", &BrInfo.NumberOfCachedResponses,
+// {BROWSER_CONFIG_CACHE_HIT_LIMIT, &BrInfo.CacheHitLimit,
+ CACHED_BROWSE_RESPONSE_LIMIT, 0, MAXULONG, DWordType, 0, NULL },
+
+ {L"QueryDriverFrequency", &BrInfo.DriverQueryFrequency,
+ BROWSER_QUERY_DRIVER_FREQUENCY, 0, 15*60, DWordType, 0, NULL },
+
+ {L"DirectHostBinding", (LPDWORD)&BrInfo.DirectHostBinding,
+ 0, 0, 0, MultiSzType, 0, NULL },
+
+ {L"UnboundBindings", (LPDWORD)&BrInfo.UnboundBindings,
+ 0, 0, 0, MultiSzType, 0, NULL },
+
+ {L"MasterPeriodicity", (LPDWORD)&BrInfo.MasterPeriodicity,
+ MASTER_PERIODICITY, 5*60, 0xffffffff, DWordType, 0, BrChangeMasterPeriodicity },
+
+ {L"BackupPeriodicity", (LPDWORD)&BrInfo.BackupPeriodicity,
+ BACKUP_PERIODICITY, 5*60, 0xffffffff, DWordType, 0, NULL },
+
+
+#if DBG
+ {L"BrowserDebug", (LPDWORD) &BrInfo.BrowserDebug,
+ 0, 0, 0xffffffff,DWordType, 0, NULL},
+ {L"BrowserDebugLimit", (LPDWORD) &BrInfo.BrowserDebugFileLimit,
+ 10000*1024, 0, 0xffffffff,DWordType, 0, NULL},
+#endif
+
+ {NULL, NULL, 0, 0, BooleanType}
+
+ };
+
+
+ULONG
+NumberOfServerEnumerations = {0};
+
+ULONG
+NumberOfDomainEnumerations = {0};
+
+ULONG
+NumberOfOtherEnumerations = {0};
+
+ULONG
+NumberOfMissedGetBrowserListRequests = {0};
+
+CRITICAL_SECTION
+BrowserStatisticsLock = {0};
+
+
+DWORD
+BrInAWorkgroup(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This function determines whether we are a member of a domain, or of
+ a workgroup. First it checks to make sure we're running on a Windows NT
+ system (otherwise we're obviously in a domain) and if so, queries LSA
+ to get the Primary domain SID, if this is NULL, we're in a workgroup.
+
+ If we fail for some random unexpected reason, we'll pretend we're in a
+ domain (it's more restrictive).
+
+Arguments:
+ None
+
+Return Value:
+
+ TRUE - We're in a workgroup
+ FALSE - We're in a domain
+
+--*/
+{
+ NT_PRODUCT_TYPE ProductType;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ LSA_HANDLE Handle;
+ NTSTATUS Status;
+ PPOLICY_PRIMARY_DOMAIN_INFO PolicyPrimaryDomainInfo = NULL;
+ DWORD Result = FALSE;
+
+
+ Status = RtlGetNtProductType(&ProductType);
+
+ if (!NT_SUCCESS(Status)) {
+ return FALSE;
+ }
+
+ if (ProductType == NtProductLanManNt) {
+ return(FALSE);
+ }
+
+ InitializeObjectAttributes(&ObjectAttributes, NULL, 0, 0, NULL);
+
+ Status = LsaOpenPolicy(NULL,
+ &ObjectAttributes,
+ POLICY_VIEW_LOCAL_INFORMATION,
+ &Handle);
+
+ if (!NT_SUCCESS(Status)) {
+ return FALSE;
+ }
+
+ Status = LsaQueryInformationPolicy(Handle, PolicyPrimaryDomainInformation,
+ (PVOID *) &PolicyPrimaryDomainInfo);
+
+ if (NT_SUCCESS(Status)) {
+
+ if (PolicyPrimaryDomainInfo->Sid == NULL) {
+ Result = TRUE;
+ }
+ else {
+ Result = FALSE;
+ }
+ }
+
+ if (PolicyPrimaryDomainInfo) {
+ LsaFreeMemory((PVOID)PolicyPrimaryDomainInfo);
+ }
+ LsaClose(Handle);
+
+ return(Result);
+}
+
+
+NET_API_STATUS
+BrGetBrowserConfiguration(
+ VOID
+ )
+{
+ NET_API_STATUS status;
+ LPTSTR ComputerName;
+ NT_PRODUCT_TYPE NtProductType;
+
+ //
+ // Initialize the resource for serializing access to configuration
+ // information.
+ //
+ RtlInitializeResource(&BrInfo.ConfigResource);
+
+ //
+ // Lock config information structure for write access since we are
+ // initializing the data in the structure.
+ //
+ if (! RtlAcquireResourceExclusive(&BrInfo.ConfigResource, TRUE)) {
+ return NERR_InternalError;
+ }
+
+ //
+ // Set pointer to configuration fields structure
+ //
+ BrInfo.BrConfigFields = BrFields;
+
+ //
+ // Get the configured computer name. NetpGetComputerName allocates
+ // the memory to hold the computername string using LocalAlloc.
+ //
+ if ((status = NetpGetComputerName(
+ &ComputerName
+ )) != NERR_Success) {
+ RtlReleaseResource(&BrInfo.ConfigResource);
+ return status;
+ }
+
+ if ((status = I_NetNameCanonicalize(
+ NULL,
+ ComputerName,
+ BrInfo.BrComputerName,
+ (CNLEN + 1) * sizeof(TCHAR),
+ NAMETYPE_COMPUTER,
+ 0
+ )) != NERR_Success) {
+
+ KdPrint(("[Browser] ComputerName specified is invalid\n"));
+
+ (void) LocalFree(ComputerName);
+ RtlReleaseResource(&BrInfo.ConfigResource);
+ return status;
+ }
+
+ //
+ // Free memory allocated by NetpGetComputerName.
+ //
+ (void) LocalFree(ComputerName);
+
+ BrInfo.BrComputerNameLength = STRLEN(BrInfo.BrComputerName);
+
+ dprintf(CONFIG, ("[Browser] ComputerName %ws, length %lu\n",
+ BrInfo.BrComputerName, BrInfo.BrComputerNameLength));
+
+
+ //
+ // Get the primary domain name from the configuration file
+ //
+
+ {
+ LPTSTR DomainNameT;
+ if ((status = NetpGetDomainName(
+ & DomainNameT
+ )) != NERR_Success) {
+ goto CloseConfigFile;
+ }
+ NetpAssert( DomainNameT != NULL );
+ (void) STRCPY( BrInfo.BrPrimaryDomainName, DomainNameT );
+ BrInfo.BrPrimaryDomainNameLength = STRLEN( DomainNameT );
+ (void) NetApiBufferFree( DomainNameT );
+ }
+
+ if ((status = I_NetNameCanonicalize(
+ NULL,
+ BrInfo.BrPrimaryDomainName,
+ BrInfo.BrPrimaryDomainName,
+ (DNLEN + 1) * sizeof(TCHAR),
+ BrInAWorkgroup() ? NAMETYPE_WORKGROUP : NAMETYPE_DOMAIN,
+ 0
+ )) != NERR_Success) {
+
+ KdPrint(("[Browser] Primary domain name specified is invalid\n"));
+ goto CloseConfigFile;
+ }
+
+ //
+ // Determine our product type.
+ //
+
+ RtlGetNtProductType(&NtProductType);
+
+ BrInfo.IsLanmanNt = (NtProductType == NtProductLanManNt);
+
+ //
+ // Now determine the primary domain controller for our domain.
+ //
+
+ {
+ if (NtProductType == NtProductLanManNt) {
+ LSA_HANDLE LsaHandle;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ NTSTATUS Status;
+ PPOLICY_LSA_SERVER_ROLE_INFO ServerRole;
+
+ InitializeObjectAttributes(&ObjectAttributes, NULL, 0, NULL, NULL);
+
+ Status = LsaOpenPolicy(NULL, &ObjectAttributes,
+ POLICY_VIEW_LOCAL_INFORMATION,
+ &LsaHandle);
+
+ if (!NT_SUCCESS(Status)) {
+
+ RtlReleaseResource(&BrInfo.ConfigResource);
+
+ return(BrMapStatus(Status));
+ }
+
+ Status = LsaQueryInformationPolicy(LsaHandle,
+ PolicyLsaServerRoleInformation,
+ (PVOID)&ServerRole
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ LsaClose(LsaHandle);
+
+ RtlReleaseResource(&BrInfo.ConfigResource);
+
+ return(BrMapStatus(Status));
+ }
+
+ LsaClose(LsaHandle);
+
+ //
+ // If we're running on the primary DC, then set that information
+ // up, otherwise ask the DC for its name.
+ //
+
+ if (ServerRole->LsaServerRole == PolicyServerRolePrimary) {
+ BrInfo.IsPrimaryDomainController = TRUE;
+ } else {
+ BrInfo.IsPrimaryDomainController = FALSE;
+ }
+
+ LsaFreeMemory( ServerRole );
+
+ } else {
+ LSA_HANDLE LsaHandle;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ NTSTATUS Status;
+ PPOLICY_PRIMARY_DOMAIN_INFO PrimaryDomain;
+
+ InitializeObjectAttributes(&ObjectAttributes, NULL, 0, NULL, NULL);
+
+ Status = LsaOpenPolicy(NULL, &ObjectAttributes,
+ POLICY_VIEW_LOCAL_INFORMATION,
+ &LsaHandle);
+
+ if (!NT_SUCCESS(Status)) {
+
+ RtlReleaseResource(&BrInfo.ConfigResource);
+
+ return(BrMapStatus(Status));
+ }
+
+ Status = LsaQueryInformationPolicy(LsaHandle,
+ PolicyPrimaryDomainInformation,
+ (PVOID)&PrimaryDomain
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ LsaClose(LsaHandle);
+
+ RtlReleaseResource(&BrInfo.ConfigResource);
+
+ return(BrMapStatus(Status));
+ }
+
+ LsaClose(LsaHandle);
+
+ if (PrimaryDomain->Sid == NULL) {
+ BrInfo.IsDomainMember = FALSE;
+ } else {
+ BrInfo.IsDomainMember = TRUE;
+ }
+
+ LsaFreeMemory( PrimaryDomain );
+
+ //
+ // We're not NTAS, so we cannot be the domain controller.
+ //
+
+ BrInfo.IsPrimaryDomainController = FALSE;
+
+ }
+ }
+
+ //
+ // Read from the config file the browser configuration fields
+ //
+
+ status = BrReadBrowserConfigFields( TRUE );
+
+ if (status != NERR_Success) {
+ goto CloseConfigFile;
+ }
+
+ //
+ // Leave config file open because we need to read transport names from it.
+ //
+ RtlReleaseResource(&BrInfo.ConfigResource);
+
+ return NERR_Success;
+
+
+CloseConfigFile:
+
+ BrShutdownDgReceiver();
+
+ RtlReleaseResource(&BrInfo.ConfigResource);
+ return status;
+}
+
+#define REPORT_KEYWORD_IGNORED( lptstrKeyword ) \
+ { \
+ LPWSTR SubString[1]; \
+ SubString[0] = lptstrKeyword; \
+ BrLogEvent(EVENT_BROWSER_ILLEGAL_CONFIG, NERR_Success, 1, SubString); \
+ NetpKdPrint(( \
+ "[Browser] *ERROR* Tried to set keyword '" FORMAT_LPTSTR \
+ "' with invalid value.\n" \
+ "This error is ignored.\n", \
+ lptstrKeyword )); \
+ }
+
+
+NET_API_STATUS
+BrReadBrowserConfigFields(
+ IN BOOL InitialCall
+ )
+/*++
+
+Routine Description:
+
+ This function assigns each browser/redir configuration field to the default
+ value if it is not specified in the configuration file or if the value
+ specified in the configuration file is invalid. Otherwise it overrides
+ the default value with the value found in the configuration file.
+
+Arguments:
+
+ InitialCall - True if this call was made during initialization
+
+Return Value:
+
+ None.
+
+--*/
+{
+ NET_API_STATUS status;
+ LPNET_CONFIG_HANDLE BrowserSection;
+ DWORD i;
+
+ LPTSTR KeywordValueBuffer;
+ DWORD KeywordValueStringLength;
+ DWORD KeywordValue;
+ DWORD OldKeywordValue;
+
+ //
+ // Open config file and get handle to the [LanmanBrowser] section
+ //
+
+ if ((status = NetpOpenConfigData(
+ &BrowserSection,
+ NULL, // Local
+ SECT_NT_BROWSER,
+ TRUE // want read-only access
+ )) != NERR_Success) {
+ return status;
+ }
+
+ for (i = 0; BrInfo.BrConfigFields[i].Keyword != NULL; i++) {
+ BOOL ParameterChanged = FALSE;
+
+ //
+ // Skip this parameter if it can't change dynamically and
+ // this isn't the initial call.
+ //
+
+ if ( !InitialCall && BrInfo.BrConfigFields[i].DynamicChangeRoutine == NULL ) {
+ continue;
+ }
+
+ switch (BrInfo.BrConfigFields[i].DataType) {
+
+ case MultiSzType:
+ status = NetpGetConfigTStrArray(
+ BrowserSection,
+ BrInfo.BrConfigFields[i].Keyword,
+ (LPTSTR_ARRAY *)(BrInfo.BrConfigFields[i].FieldPtr));
+ if ((status != NO_ERROR) && (status != NERR_CfgParamNotFound)) {
+ REPORT_KEYWORD_IGNORED( BrInfo.BrConfigFields[i].Keyword );
+ }
+ break;
+
+ case BooleanType:
+
+ status = NetpGetConfigBool(
+ BrowserSection,
+ BrInfo.BrConfigFields[i].Keyword,
+ BrInfo.BrConfigFields[i].Default,
+ (LPBOOL)(BrInfo.BrConfigFields[i].FieldPtr)
+ );
+
+ if ((status != NO_ERROR) && (status != NERR_CfgParamNotFound)) {
+
+ REPORT_KEYWORD_IGNORED( BrInfo.BrConfigFields[i].Keyword );
+
+ }
+
+ break;
+
+ case TriValueType:
+
+ //
+ // Assign default configuration value
+ //
+
+ *(BrInfo.BrConfigFields[i].FieldPtr) = BrInfo.BrConfigFields[i].Default;
+
+ if (NetpGetConfigValue(
+ BrowserSection,
+ BrInfo.BrConfigFields[i].Keyword,
+ &KeywordValueBuffer
+ ) != NERR_Success) {
+ continue;
+ }
+
+ KeywordValueStringLength = STRLEN(KeywordValueBuffer);
+
+ if (STRICMP(KeywordValueBuffer, KEYWORD_YES) == 0) {
+ *(BrInfo.BrConfigFields[i].FieldPtr) = 1;
+ } else if (STRICMP(KeywordValueBuffer, KEYWORD_TRUE) == 0) {
+ *(BrInfo.BrConfigFields[i].FieldPtr) = 1;
+ } else if (STRICMP(KeywordValueBuffer, KEYWORD_NO) == 0) {
+ *(BrInfo.BrConfigFields[i].FieldPtr) = (DWORD) -1;
+ } else if (STRICMP(KeywordValueBuffer, KEYWORD_FALSE) == 0) {
+ *(BrInfo.BrConfigFields[i].FieldPtr) = (DWORD) -1;
+ } else if (STRICMP(KeywordValueBuffer, TEXT("AUTO")) == 0) {
+ *(BrInfo.BrConfigFields[i].FieldPtr) = 0;
+ }
+ else {
+ REPORT_KEYWORD_IGNORED( BrInfo.BrConfigFields[i].Keyword );
+ }
+
+ NetApiBufferFree(KeywordValueBuffer);
+
+ break;
+
+
+ case DWordType:
+
+ OldKeywordValue = *(LPDWORD)BrInfo.BrConfigFields[i].FieldPtr;
+ if (NetpGetConfigDword(
+ BrowserSection,
+ BrInfo.BrConfigFields[i].Keyword,
+ BrInfo.BrConfigFields[i].Default,
+ (LPDWORD)(BrInfo.BrConfigFields[i].FieldPtr)
+ ) != NERR_Success) {
+ continue;
+ }
+
+ KeywordValue = *(LPDWORD)BrInfo.BrConfigFields[i].FieldPtr;
+
+ //
+ // Convert unicode strings back to ANSI so that we can use
+ // isdigit() and atol().
+ //
+
+ if (KeywordValue < BrInfo.BrConfigFields[i].Minimum ||
+ KeywordValue > BrInfo.BrConfigFields[i].Maximum) {
+ KdPrint(("[Browser] %ws value out of range %lu (%lu-%lu)\n",
+ BrInfo.BrConfigFields[i].Keyword, KeywordValue,
+ BrInfo.BrConfigFields[i].Minimum,
+ BrInfo.BrConfigFields[i].Maximum
+ ));
+ KeywordValue =
+ *(LPDWORD)BrInfo.BrConfigFields[i].FieldPtr =
+ BrInfo.BrConfigFields[i].Default;
+ }
+
+ //
+ // Test if the parameter has actually changed
+ //
+
+ if ( OldKeywordValue != KeywordValue ) {
+ ParameterChanged = TRUE;
+ }
+
+ break;
+
+ default:
+ NetpAssert(FALSE);
+
+ }
+
+ //
+ // If this is a dynamic parameter change,
+ // and this isn't the initial call.
+ // notify that this parameter changed.
+ //
+
+ if ( !InitialCall && ParameterChanged ) {
+ BrInfo.BrConfigFields[i].DynamicChangeRoutine();
+ }
+ }
+
+ status = NetpCloseConfigData(BrowserSection);
+
+ if (BrInfo.DirectHostBinding != NULL &&
+ !NetpIsTStrArrayEmpty(BrInfo.DirectHostBinding)) {
+ KdPrint(("Browser: DirectHostBinding length: %ld\n",NetpTStrArrayEntryCount(BrInfo.DirectHostBinding)));
+
+ if (NetpTStrArrayEntryCount(BrInfo.DirectHostBinding) % 2 != 0) {
+ status = ERROR_INVALID_PARAMETER;
+ }
+ }
+
+ return status;
+}
+
+
+ VOID
+BrDeleteConfiguration (
+ DWORD BrInitState
+ )
+{
+
+ if (BrInfo.DirectHostBinding != NULL) {
+ NetApiBufferFree(BrInfo.DirectHostBinding);
+ }
+
+ if (BrInfo.UnboundBindings != NULL) {
+ NetApiBufferFree(BrInfo.UnboundBindings);
+ }
+
+ RtlDeleteResource(&BrInfo.ConfigResource);
+
+ UNREFERENCED_PARAMETER(BrInitState);
+}
+
+#if DBG
+NET_API_STATUS
+BrUpdateDebugInformation(
+ IN LPWSTR SystemKeyName,
+ IN LPWSTR ValueName,
+ IN LPTSTR TransportName,
+ IN LPTSTR ServerName OPTIONAL,
+ IN DWORD ServiceStatus
+ )
+/*++
+
+Routine Description:
+
+ This routine will stick debug information in the registry about the last
+ time the browser retrieved information from the remote server.
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ WCHAR TotalKeyName[MAX_PATH];
+ ULONG Disposition;
+ HKEY Key;
+ ULONG Status;
+ SYSTEMTIME LocalTime;
+ WCHAR LastUpdateTime[100];
+
+ //
+ // Build the key name:
+ //
+ // HKEY_LOCAL_MACHINE:System\CurrentControlSet\Services\Browser\Debug\<Transport>\SystemKeyName
+ //
+
+ wcscpy(TotalKeyName, L"System\\CurrentControlSet\\Services\\Browser\\Debug");
+
+ wcscat(TotalKeyName, TransportName);
+
+ wcscat(TotalKeyName, L"\\");
+
+ wcscat(TotalKeyName, SystemKeyName);
+
+ if ((Status = RegCreateKeyEx(HKEY_LOCAL_MACHINE, TotalKeyName, 0,
+ L"BrowserDebugInformation",
+ REG_OPTION_NON_VOLATILE,
+ KEY_WRITE,
+ NULL,
+ &Key,
+ &Disposition)) != ERROR_SUCCESS) {
+ KdPrint(("[BROWSER] Unable to create key to log debug information: %lx\n", Status));
+ return Status;
+ }
+
+ if (ARGUMENT_PRESENT(ServerName)) {
+ if ((Status = RegSetValueEx(Key, ValueName, 0, REG_SZ, (LPBYTE)ServerName, (wcslen(ServerName)+1) * sizeof(WCHAR))) != ERROR_SUCCESS) {
+ KdPrint(("[BROWSER] Unable to set value of ServerName value to %ws: %lx\n", ServerName, Status));
+ RegCloseKey(Key);
+ return Status;
+ }
+ } else {
+ if ((Status = RegSetValueEx(Key, ValueName, 0, REG_DWORD, (LPBYTE)&ServiceStatus, sizeof(ULONG))) != ERROR_SUCCESS) {
+ KdPrint(("[BROWSER] Unable to set value of ServerName value to %ws: %lx\n", ServerName, Status));
+ RegCloseKey(Key);
+ return Status;
+ }
+ }
+
+
+ GetLocalTime(&LocalTime);
+
+ swprintf(LastUpdateTime, L"%d/%d/%d %d:%d:%d:%d", LocalTime.wDay,
+ LocalTime.wMonth,
+ LocalTime.wYear,
+ LocalTime.wHour,
+ LocalTime.wMinute,
+ LocalTime.wSecond,
+ LocalTime.wMilliseconds);
+
+ if ((Status = RegSetValueEx(Key, L"LastUpdateTime", 0, REG_SZ, (LPBYTE)&LastUpdateTime, (wcslen(LastUpdateTime) + 1)*sizeof(WCHAR))) != ERROR_SUCCESS) {
+ KdPrint(("[BROWSER] Unable to set value of LastUpdateTime value to %s: %lx\n", LastUpdateTime, Status));
+ RegCloseKey(Key);
+ return Status;
+ }
+
+ RegCloseKey(Key);
+}
+
+#endif
diff --git a/private/net/svcdlls/browser/server/brconfig.h b/private/net/svcdlls/browser/server/brconfig.h
new file mode 100644
index 000000000..69729a5a0
--- /dev/null
+++ b/private/net/svcdlls/browser/server/brconfig.h
@@ -0,0 +1,134 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ brconfig.h
+
+Abstract:
+
+ Private header file to be included by Workstation service modules that
+ need to load Workstation configuration information.
+
+Author:
+
+ Rita Wong (ritaw) 22-May-1991
+
+Revision History:
+
+--*/
+
+
+#ifndef _BRCONFIG_INCLUDED_
+#define _BRCONFIG_INCLUDED_
+
+#define BROWSER_CONFIG_VERSION_MAJOR 3
+#define BROWSER_CONFIG_VERSION_MINOR 10
+
+typedef enum _DATATYPE {
+ BooleanType,
+ DWordType,
+ MultiSzType,
+ TriValueType // Yes, No, Auto
+} DATATYPE, *PDATATYPE;
+
+typedef struct _BR_BROWSER_FIELDS {
+ LPTSTR Keyword;
+ LPDWORD FieldPtr;
+ DWORD Default;
+ DWORD Minimum;
+ DWORD Maximum;
+ DATATYPE DataType;
+ DWORD Parmnum;
+ VOID (*DynamicChangeRoutine) ( VOID );
+} BR_BROWSER_FIELDS, *PBR_BROWSER_FIELDS;
+
+//
+// Configuration information. Reading and writing to this global
+// structure requires that the resource be acquired first.
+//
+typedef struct _BRCONFIGURATION_INFO {
+
+ RTL_RESOURCE ConfigResource; // To serialize access to config
+ // fields.
+
+ TCHAR BrComputerName[CNLEN + 1];
+ DWORD BrComputerNameLength;
+ TCHAR BrPrimaryDomainName[DNLEN + 1];
+ DWORD BrPrimaryDomainNameLength;
+ DWORD MaintainServerList; // -1, 0, or 1 (No, Auto, Yes)
+ DWORD BackupBrowserRecoveryTime;
+ DWORD CacheHitLimit; // Browse response Cache hit limit.
+ DWORD NumberOfCachedResponses; // Browse response cache size.
+ DWORD DriverQueryFrequency; // Browser driver query frequency.
+ DWORD MasterPeriodicity; // Master announce frequency (seconds)
+ DWORD BackupPeriodicity; // Backup scavange frequency (seconds)
+ BOOL IsDomainMasterBrowser; // True if domain master browser.
+ BOOL IsLanmanNt; // True if is on LM NT machine
+ BOOL IsPrimaryDomainController;// True if machine is PDC
+ BOOL IsDomainMember; // True if machine is member of domain.
+ LPTSTR_ARRAY DirectHostBinding; // Direct host equivalence map.
+ LPTSTR_ARRAY UnboundBindings; // Redir bindings that aren't bound to browser
+ PBR_BROWSER_FIELDS BrConfigFields;
+#if DBG
+ DWORD BrowserDebug; // If non zero, indicates debug info.
+ DWORD BrowserDebugFileLimit; // File size limit on browser log size.
+#endif
+} BRCONFIGURATION_INFO, *PBRCONFIGURATION_INFO;
+
+extern BRCONFIGURATION_INFO BrInfo;
+
+#define BRBUF BrInfo.BrConfigBuf
+
+extern
+ULONG
+NumberOfServerEnumerations;
+
+extern
+ULONG
+NumberOfDomainEnumerations;
+
+extern
+ULONG
+NumberOfOtherEnumerations;
+
+extern
+ULONG
+NumberOfMissedGetBrowserListRequests;
+
+extern
+CRITICAL_SECTION
+BrowserStatisticsLock;
+
+
+
+NET_API_STATUS
+BrGetBrowserConfiguration(
+ VOID
+ );
+
+VOID
+BrDeleteConfiguration (
+ DWORD BrInitState
+ );
+
+NET_API_STATUS
+BrReadBrowserConfigFields(
+ BOOL InitialCall
+ );
+
+
+#if DEVL
+NET_API_STATUS
+BrUpdateDebugInformation(
+ IN LPWSTR SystemKeyName,
+ IN LPWSTR ValueName,
+ IN LPTSTR TransportName,
+ IN LPTSTR ServerName OPTIONAL,
+ IN DWORD ServiceStatus
+ );
+
+#endif
+
+#endif
diff --git a/private/net/svcdlls/browser/server/brconst.h b/private/net/svcdlls/browser/server/brconst.h
new file mode 100644
index 000000000..4f55ebfb2
--- /dev/null
+++ b/private/net/svcdlls/browser/server/brconst.h
@@ -0,0 +1,98 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ brconst.h
+
+Abstract:
+
+ Private header file which defines assorted mainifest constants for
+ the browser service
+
+Author:
+
+ Rita Wong (ritaw) 06-May-1991
+
+Revision History:
+
+--*/
+
+#ifndef _BRCONST_INCLUDED_
+#define _BRCONST_INCLUDED_
+
+//
+// Age the masters server list cache every MASTER_PERIODICITY times.
+//
+#define MASTER_PERIODICITY 12*60
+
+//
+// Refresh the backup browsers server list every BACKUP_PERIODICITY
+//
+#define BACKUP_PERIODICITY 12*60
+
+//
+// Buffer size used for GetBrowserServerList responses (in bytes).
+//
+
+#define BROWSER_BACKUP_LIST_RESPONSE_SIZE 400
+
+//
+// If we failed to retrieve the server list, retry in BACKUP_ERROR_PERIODICITY
+// seconds
+//
+
+#define BACKUP_ERROR_PERIODICITY 30
+
+//
+// If we failed to retrieve the server (or domain) list BACKUP_ERROR_FAILURE
+// times in a row, stop being a backup browser.
+//
+
+#define BACKUP_ERROR_FAILURE 5
+
+//
+// Once we have stopped being a backup browser, we will not become a backup
+// until at least BACKUP_BROWSER_RECOVERY_TIME milliseconds have elapsed.
+//
+
+#define BACKUP_BROWSER_RECOVERY_TIME 30*60*1000
+
+//
+// If we receive fewer than this # of domains or servers, we treat it as an
+// error.
+//
+
+#define BROWSER_MINIMUM_DOMAIN_NUMBER 1
+#define BROWSER_MINIMUM_SERVER_NUMBER 2
+
+//
+// Wait for this many minutes after each failed promotion before
+// continuing.
+//
+
+#define FAILED_PROMOTION_PERIODICITY 5*60
+
+//
+// Run the master browser timer for 3 times (45 minutes) before
+// tossing the list in the service.
+//
+
+#define MASTER_BROWSER_LAN_TIMER_LIMIT 3
+
+//
+// A browse request has to have a hit count of at least this value before
+// it is retained in the cache.
+//
+
+#define CACHED_BROWSE_RESPONSE_HIT_LIMIT 1
+
+//
+// The maximum number of cache responses we will allow.
+//
+
+#define CACHED_BROWSE_RESPONSE_LIMIT 10
+
+#endif // ifndef _BRCONST_INCLUDED_
+
diff --git a/private/net/svcdlls/browser/server/brdevice.c b/private/net/svcdlls/browser/server/brdevice.c
new file mode 100644
index 000000000..e6e7e37ea
--- /dev/null
+++ b/private/net/svcdlls/browser/server/brdevice.c
@@ -0,0 +1,686 @@
+
+
+/*++
+
+Copyright (c) 1991-1992 Microsoft Corporation
+
+Module Name:
+
+ brdevice.c
+
+Abstract:
+
+ This module contains the support routines for the APIs that call
+ into the browser or the datagram receiver.
+
+Author:
+
+ Rita Wong (ritaw) 20-Feb-1991
+ Larry Osterman (larryo) 23-Mar-1992
+
+Revision History:
+
+--*/
+
+#include "precomp.h"
+#pragma hdrstop
+
+//-------------------------------------------------------------------//
+// //
+// Local Function Prototypes //
+// //
+//-------------------------------------------------------------------//
+
+
+//-------------------------------------------------------------------//
+// //
+// Global variables //
+// //
+//-------------------------------------------------------------------//
+
+//
+// Handle to the Datagram Receiver DD
+//
+HANDLE BrDgReceiverDeviceHandle = NULL;
+
+VOID
+CompleteAsyncBrowserIoControl(
+ IN PVOID ApcContext,
+ IN PIO_STATUS_BLOCK IoStatusBlock,
+ IN ULONG Reserved
+ );
+
+NET_API_STATUS
+BrOpenDgReceiver (
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This routine opens the NT LAN Man Datagram Receiver driver.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NTSTATUS ntstatus;
+
+ UNICODE_STRING DeviceName;
+
+ IO_STATUS_BLOCK IoStatusBlock;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+
+
+ //
+ // Open the redirector device.
+ //
+ RtlInitUnicodeString(&DeviceName, DD_BROWSER_DEVICE_NAME_U);
+
+ InitializeObjectAttributes(
+ &ObjectAttributes,
+ &DeviceName,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL
+ );
+
+ ntstatus = NtOpenFile(
+ &BrDgReceiverDeviceHandle,
+ SYNCHRONIZE,
+ &ObjectAttributes,
+ &IoStatusBlock,
+ 0,
+ 0
+ );
+
+ if (NT_SUCCESS(ntstatus)) {
+ ntstatus = IoStatusBlock.Status;
+ }
+
+ if (! NT_SUCCESS(ntstatus)) {
+ KdPrint(("[Browser] NtOpenFile browser driver failed: 0x%08lx\n",
+ ntstatus));
+ }
+
+ return NetpNtStatusToApiStatus(ntstatus);
+}
+
+
+
+VOID
+BrShutdownDgReceiver(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This routine close the LAN Man Redirector device.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ IO_STATUS_BLOCK IoSb;
+
+ //
+ // Cancel the I/O operations outstanding on the browser.
+ //
+
+ NtCancelIoFile(BrDgReceiverDeviceHandle, &IoSb);
+
+}
+
+
+//
+// Retreive the list of bound transports from the bowser driver.
+//
+
+NET_API_STATUS
+BrGetTransportList(
+ OUT PLMDR_TRANSPORT_LIST *TransportList
+ )
+{
+ NET_API_STATUS Status;
+ LMDR_REQUEST_PACKET RequestPacket;
+
+ //
+ // If we have a previous buffer that was too small, free it up.
+ //
+
+ RequestPacket.Version = LMDR_REQUEST_PACKET_VERSION;
+
+ RequestPacket.Type = EnumerateXports;
+
+ RtlInitUnicodeString(&RequestPacket.TransportName, NULL);
+
+ Status = DeviceControlGetInfo(
+ BrDgReceiverDeviceHandle,
+ IOCTL_LMDR_ENUMERATE_TRANSPORTS,
+ &RequestPacket,
+ sizeof(RequestPacket),
+ (LPVOID *)TransportList,
+ 0xffffffff,
+ 4096,
+ NULL
+ );
+
+ return Status;
+}
+
+ NET_API_STATUS
+BrAnnounceDomain(
+ IN PNETWORK Network,
+ IN ULONG Periodicity
+ )
+{
+ NET_API_STATUS Status;
+ UCHAR AnnounceBuffer[sizeof(BROWSE_ANNOUNCE_PACKET)+LM20_CNLEN+1];
+ PBROWSE_ANNOUNCE_PACKET Announcement = (PBROWSE_ANNOUNCE_PACKET )AnnounceBuffer;
+ CHAR ADomainName[CNLEN+1];
+ ULONG ADomainNameLength;
+ CHAR AMasterName[CNLEN+1];
+ ULONG AMasterNameLength;
+
+ //
+ // We don't announce domains on direct host IPX.
+ //
+
+ if (Network->Flags & NETWORK_IPX) {
+ return NERR_Success;
+ }
+
+ Status = RtlAcquireResourceShared(&BrInfo.ConfigResource, TRUE);
+
+ if (!Status) {
+ KdPrint(("Browser: Unable to lock config database: %lx\n", Status));
+ return Status;
+ }
+
+ Status = RtlUpcaseUnicodeToOemN(ADomainName,
+ sizeof(ADomainName),
+ &ADomainNameLength,
+ BrInfo.BrPrimaryDomainName,
+ BrInfo.BrPrimaryDomainNameLength*sizeof(TCHAR));
+
+ if (!NT_SUCCESS(Status)) {
+ KdPrint(("Browser: Unable to convert primary domain name to OEM\n"));
+ return Status;
+ }
+
+ ADomainName[ADomainNameLength] = '\0';
+
+ Status = RtlUpcaseUnicodeToOemN(AMasterName,
+ sizeof(AMasterName),
+ &AMasterNameLength,
+ BrInfo.BrComputerName,
+ BrInfo.BrComputerNameLength*sizeof(TCHAR));
+
+ if (!NT_SUCCESS(Status)) {
+ KdPrint(("Browser: Unable to convert computer name to OEM\n"));
+ return Status;
+ }
+
+ AMasterName[AMasterNameLength] = '\0';
+
+ RtlReleaseResource(&BrInfo.ConfigResource);
+
+ Announcement->BrowseType = WkGroupAnnouncement;
+
+ Announcement->BrowseAnnouncement.Periodicity = Periodicity;
+
+ Announcement->BrowseAnnouncement.UpdateCount = 0;
+
+ Announcement->BrowseAnnouncement.VersionMajor = BROWSER_CONFIG_VERSION_MAJOR;
+
+ Announcement->BrowseAnnouncement.VersionMinor = BROWSER_CONFIG_VERSION_MINOR;
+
+ Announcement->BrowseAnnouncement.Type = SV_TYPE_DOMAIN_ENUM | SV_TYPE_NT;
+
+ if (BrInfo.IsPrimaryDomainController) {
+ Announcement->BrowseAnnouncement.Type |= SV_TYPE_DOMAIN_CTRL;
+ }
+
+ strcpy(Announcement->BrowseAnnouncement.ServerName, ADomainName);
+
+ strcpy(Announcement->BrowseAnnouncement.Comment, AMasterName);
+
+ Status = SendDatagram(BrDgReceiverDeviceHandle,
+ &Network->NetworkName,
+ BrInfo.BrPrimaryDomainName,
+ DomainAnnouncement,
+ Announcement,
+ FIELD_OFFSET(BROWSE_ANNOUNCE_PACKET, BrowseAnnouncement.Comment)+
+ AMasterNameLength+sizeof(UCHAR)
+ );
+
+ if (Status != NERR_Success) {
+
+ KdPrint(("Browser: Unable to announce domain for network %wZ: %X\n", &Network->NetworkName, Status));
+
+ }
+
+ return Status;
+
+}
+
+
+
+ NET_API_STATUS
+BrUpdateBrowserStatus (
+ IN PNETWORK Network,
+ IN DWORD ServiceStatus
+ )
+{
+ NET_API_STATUS Status;
+ UCHAR PacketBuffer[sizeof(LMDR_REQUEST_PACKET)+(LM20_CNLEN+1)*sizeof(WCHAR)];
+ PLMDR_REQUEST_PACKET RequestPacket = (PLMDR_REQUEST_PACKET)PacketBuffer;
+
+
+ RequestPacket->Version = LMDR_REQUEST_PACKET_VERSION;
+
+ RequestPacket->TransportName = Network->NetworkName;
+
+ RequestPacket->Parameters.UpdateStatus.NewStatus = ServiceStatus;
+
+ RequestPacket->Parameters.UpdateStatus.IsLanmanNt = BrInfo.IsLanmanNt;
+
+ RequestPacket->Parameters.UpdateStatus.IsMemberDomain = BrInfo.IsDomainMember;
+
+ RequestPacket->Parameters.UpdateStatus.IsPrimaryDomainController = BrInfo.IsPrimaryDomainController;
+
+ RequestPacket->Parameters.UpdateStatus.IsDomainMaster = BrInfo.IsDomainMasterBrowser;
+
+ RequestPacket->Parameters.UpdateStatus.MaintainServerList = (BrInfo.MaintainServerList == 1);
+
+ //
+ // Tell the bowser the number of servers in the server table.
+ //
+
+ RequestPacket->Parameters.UpdateStatus.NumberOfServersInTable =
+ NumberInterimServerListElements(&Network->BrowseTable) +
+ NumberInterimServerListElements(&Network->DomainList) +
+ Network->TotalBackupServerListEntries +
+ Network->TotalBackupDomainListEntries;
+
+ //
+ // This is a simple IoControl - It just updates the status.
+ //
+
+ Status = BrDgReceiverIoControl(BrDgReceiverDeviceHandle,
+ IOCTL_LMDR_UPDATE_STATUS,
+ RequestPacket,
+ sizeof(LMDR_REQUEST_PACKET),
+ NULL,
+ 0,
+ NULL);
+
+ return Status;
+}
+
+ NET_API_STATUS
+BrIssueAsyncBrowserIoControl(
+ IN PNETWORK Network,
+ IN ULONG ControlCode,
+ IN PBROWSER_WORKER_ROUTINE CompletionRoutine
+ )
+{
+ ULONG PacketSize;
+ PLMDR_REQUEST_PACKET RequestPacket = NULL;
+ NTSTATUS NtStatus;
+
+ PBROWSERASYNCCONTEXT Context = NULL;
+
+ PacketSize = sizeof(LMDR_REQUEST_PACKET) +
+ MAXIMUM_FILENAME_LENGTH * sizeof(WCHAR) +
+ Network->NetworkName.MaximumLength;
+
+ RequestPacket = MIDL_user_allocate(PacketSize);
+
+ if (RequestPacket == NULL) {
+ return(ERROR_NOT_ENOUGH_MEMORY);
+ }
+
+ Context = MIDL_user_allocate(sizeof(BROWSERASYNCCONTEXT));
+
+ if (Context == NULL) {
+
+ MIDL_user_free(RequestPacket);
+
+ return(ERROR_NOT_ENOUGH_MEMORY);
+
+ }
+
+ RequestPacket->Version = LMDR_REQUEST_PACKET_VERSION;
+
+ //
+ // Set level to FALSE to indicate that find master should not initiate
+ // a findmaster request, simply complete when a new master announces
+ // itself.
+ //
+
+ RequestPacket->Level = 0;
+
+ //
+ // Stick the name of the transport associated with this request at the
+ // end of the request packet.
+ //
+
+ RequestPacket->TransportName.MaximumLength = Network->NetworkName.MaximumLength;
+
+ RequestPacket->TransportName.Buffer = (PWSTR)((PCHAR)RequestPacket+sizeof(LMDR_REQUEST_PACKET)+(MAXIMUM_FILENAME_LENGTH*sizeof(WCHAR)));
+
+ RtlCopyUnicodeString(&RequestPacket->TransportName, &Network->NetworkName);
+
+ BrInitializeWorkItem(&Context->WorkItem, CompletionRoutine, Context);
+
+ Context->Network = Network;
+
+ Context->RequestPacket = RequestPacket;
+
+ NtStatus = NtDeviceIoControlFile(BrDgReceiverDeviceHandle,
+ NULL,
+ CompleteAsyncBrowserIoControl,
+ Context,
+ &Context->IoStatusBlock,
+ ControlCode,
+ RequestPacket,
+ PacketSize,
+ RequestPacket,
+ sizeof(LMDR_REQUEST_PACKET)+MAXIMUM_FILENAME_LENGTH*sizeof(WCHAR)
+ );
+
+ if (!NT_SUCCESS(NtStatus)) {
+
+ KdPrint(("Browser: Unable to issue browser IoControl: %X\n", NtStatus));
+
+ MIDL_user_free(RequestPacket);
+
+ MIDL_user_free(Context);
+
+
+ return(BrMapStatus(NtStatus));
+ }
+
+ return NERR_Success;
+
+}
+
+ VOID
+CompleteAsyncBrowserIoControl(
+ IN PVOID ApcContext,
+ IN PIO_STATUS_BLOCK IoStatusBlock,
+ IN ULONG Reserved
+ )
+{
+
+ PBROWSERASYNCCONTEXT Context = ApcContext;
+
+ //
+ // If this request was canceled, we're stopping the browser, so we
+ // want to clean up our allocated pool. In addition, don't bother
+ // calling into the routine - the threads are gone by now.
+ //
+
+ if (IoStatusBlock->Status == STATUS_CANCELLED) {
+
+ MIDL_user_free(Context->RequestPacket);
+
+ MIDL_user_free(Context);
+
+ return;
+
+ }
+
+ //
+ // Timestamp when this request was completed. This allows us to tell
+ // where a request spent its time.
+ //
+
+ NtQueryPerformanceCounter(&Context->TimeCompleted, NULL);
+
+ BrQueueWorkItem(&Context->WorkItem);
+
+}
+
+ NET_API_STATUS
+BrGetLocalBrowseList(
+ IN PNETWORK Network,
+ IN LPWSTR DomainName OPTIONAL,
+ IN ULONG Level,
+ IN ULONG ServerType,
+ OUT PVOID *ServerList,
+ OUT PULONG EntriesRead,
+ OUT PULONG TotalEntries
+ )
+{
+ NET_API_STATUS status;
+ PLMDR_REQUEST_PACKET Drp; // Datagram receiver request packet
+ ULONG DrpSize;
+
+ //
+ // Allocate the request packet large enough to hold the variable length
+ // domain name.
+ //
+
+ DrpSize = sizeof(LMDR_REQUEST_PACKET) +
+ (ARGUMENT_PRESENT(DomainName) ? (wcslen(DomainName) + 1) * sizeof(WCHAR) : 0) +
+ Network->NetworkName.MaximumLength;
+
+ if ((Drp = MIDL_user_allocate(DrpSize)) == NULL) {
+
+ return GetLastError();
+ }
+
+ //
+ // Set up request packet. Output buffer structure is of enumerate
+ // servers type.
+ //
+
+ Drp->Version = LMDR_REQUEST_PACKET_VERSION;
+ Drp->Type = EnumerateServers;
+
+ Drp->Level = Level;
+
+ Drp->Parameters.EnumerateServers.ServerType = ServerType;
+ Drp->Parameters.EnumerateServers.ResumeHandle = 0;
+
+ Drp->TransportName.Buffer = (PWSTR)((PCHAR)Drp+sizeof(LMDR_REQUEST_PACKET) +
+ (ARGUMENT_PRESENT(DomainName) ? (wcslen(DomainName) + 1) * sizeof(WCHAR) : 0));
+
+ Drp->TransportName.MaximumLength = Network->NetworkName.MaximumLength;
+
+ RtlCopyUnicodeString(&Drp->TransportName, &Network->NetworkName);
+
+ if (ARGUMENT_PRESENT(DomainName)) {
+
+ Drp->Parameters.EnumerateServers.DomainNameLength = wcslen(DomainName)*sizeof(WCHAR);
+ wcscpy(Drp->Parameters.EnumerateServers.DomainName, DomainName);
+
+ } else {
+ Drp->Parameters.EnumerateServers.DomainNameLength = 0;
+ Drp->Parameters.EnumerateServers.DomainName[0] = '\0';
+ }
+
+ //
+ // Ask the datagram receiver to enumerate the servers
+ //
+
+ status = DeviceControlGetInfo(
+ BrDgReceiverDeviceHandle,
+ IOCTL_LMDR_ENUMERATE_SERVERS,
+ Drp,
+ DrpSize,
+ ServerList,
+ 0xffffffff,
+ 4096,
+ NULL
+ );
+
+ *EntriesRead = Drp->Parameters.EnumerateServers.EntriesRead;
+ *TotalEntries = Drp->Parameters.EnumerateServers.TotalEntries;
+
+ (void) MIDL_user_free(Drp);
+
+ return status;
+
+}
+
+NET_API_STATUS
+BrRemoveOtherDomain(
+ IN PNETWORK Network,
+ IN LPTSTR ServerName
+ )
+{
+ NET_API_STATUS Status;
+ UCHAR PacketBuffer[sizeof(LMDR_REQUEST_PACKET)+(LM20_CNLEN+1)*sizeof(WCHAR)];
+ PLMDR_REQUEST_PACKET RequestPacket = (PLMDR_REQUEST_PACKET)PacketBuffer;
+
+
+ RequestPacket->Version = LMDR_REQUEST_PACKET_VERSION;
+
+ RequestPacket->TransportName = Network->NetworkName;
+
+ RequestPacket->Parameters.AddDelName.DgReceiverNameLength = STRLEN(ServerName)*sizeof(TCHAR);
+
+ RequestPacket->Parameters.AddDelName.Type = OtherDomain;
+
+ STRCPY(RequestPacket->Parameters.AddDelName.Name,ServerName);
+
+ //
+ // This is a simple IoControl - It just updates the status.
+ //
+
+ Status = BrDgReceiverIoControl(BrDgReceiverDeviceHandle,
+ IOCTL_LMDR_DELETE_NAME,
+ RequestPacket,
+ sizeof(LMDR_REQUEST_PACKET),
+ NULL,
+ 0,
+ NULL);
+
+ return Status;
+}
+NET_API_STATUS
+BrAddOtherDomain(
+ IN PNETWORK Network,
+ IN LPTSTR ServerName
+ )
+{
+ NET_API_STATUS Status;
+ UCHAR PacketBuffer[sizeof(LMDR_REQUEST_PACKET)+(LM20_CNLEN+1)*sizeof(WCHAR)];
+ PLMDR_REQUEST_PACKET RequestPacket = (PLMDR_REQUEST_PACKET)PacketBuffer;
+
+
+ RequestPacket->Version = LMDR_REQUEST_PACKET_VERSION;
+
+ RequestPacket->TransportName = Network->NetworkName;
+
+ RequestPacket->Parameters.AddDelName.DgReceiverNameLength = STRLEN(ServerName)*sizeof(TCHAR);
+
+ RequestPacket->Parameters.AddDelName.Type = OtherDomain;
+
+ STRCPY(RequestPacket->Parameters.AddDelName.Name,ServerName);
+
+ //
+ // This is a simple IoControl - It just updates the status.
+ //
+
+ Status = BrDgReceiverIoControl(BrDgReceiverDeviceHandle,
+ IOCTL_LMDR_ADD_NAME,
+ RequestPacket,
+ sizeof(LMDR_REQUEST_PACKET),
+ NULL,
+ 0,
+ NULL);
+
+ return Status;
+}
+
+ NET_API_STATUS
+BrBindToTransport(
+ IN LPTSTR TransportName
+ )
+{
+ NET_API_STATUS Status;
+ UCHAR PacketBuffer[sizeof(LMDR_REQUEST_PACKET)+(MAXIMUM_FILENAME_LENGTH+1)*sizeof(WCHAR)];
+ PLMDR_REQUEST_PACKET RequestPacket = (PLMDR_REQUEST_PACKET)PacketBuffer;
+
+
+ RequestPacket->Version = LMDR_REQUEST_PACKET_VERSION;
+
+ RequestPacket->TransportName.Length = 0;
+ RequestPacket->TransportName.MaximumLength = 0;
+
+ RequestPacket->Parameters.Bind.TransportNameLength = STRLEN(TransportName)*sizeof(TCHAR);
+
+ STRCPY(RequestPacket->Parameters.Bind.TransportName, TransportName);
+
+ //
+ // This is a simple IoControl - It just updates the status.
+ //
+
+ Status = BrDgReceiverIoControl(BrDgReceiverDeviceHandle,
+ IOCTL_LMDR_BIND_TO_TRANSPORT,
+ RequestPacket,
+ FIELD_OFFSET(LMDR_REQUEST_PACKET, Parameters.Bind.TransportName) +
+ RequestPacket->Parameters.Bind.TransportNameLength,
+ NULL,
+ 0,
+ NULL);
+
+ return Status;
+}
+ NET_API_STATUS
+BrUnbindFromTransport(
+ IN LPTSTR TransportName
+ )
+{
+ NET_API_STATUS Status;
+ UCHAR PacketBuffer[sizeof(LMDR_REQUEST_PACKET)+(MAXIMUM_FILENAME_LENGTH+1)*sizeof(WCHAR)];
+ PLMDR_REQUEST_PACKET RequestPacket = (PLMDR_REQUEST_PACKET)PacketBuffer;
+
+
+ RequestPacket->Version = LMDR_REQUEST_PACKET_VERSION;
+
+ RequestPacket->TransportName.Length = 0;
+ RequestPacket->TransportName.MaximumLength = 0;
+
+ RequestPacket->Parameters.Unbind.TransportNameLength = STRLEN(TransportName)*sizeof(TCHAR);
+
+ STRCPY(RequestPacket->Parameters.Unbind.TransportName, TransportName);
+
+ KdPrint(("Browser: unbind from IPX transport %ws\n", TransportName));
+
+ //
+ // This is a simple IoControl - It just updates the status.
+ //
+
+ Status = BrDgReceiverIoControl(BrDgReceiverDeviceHandle,
+ IOCTL_LMDR_UNBIND_FROM_TRANSPORT,
+ RequestPacket,
+ FIELD_OFFSET(LMDR_REQUEST_PACKET, Parameters.Bind.TransportName) +
+ RequestPacket->Parameters.Bind.TransportNameLength,
+ NULL,
+ 0,
+ NULL);
+
+ if (Status != NERR_Success) {
+ KdPrint(("Browser: unbind from IPX transport %ws: %ld\n", TransportName, Status));
+ }
+ return Status;
+}
+
diff --git a/private/net/svcdlls/browser/server/brdevice.h b/private/net/svcdlls/browser/server/brdevice.h
new file mode 100644
index 000000000..ea4a110a9
--- /dev/null
+++ b/private/net/svcdlls/browser/server/brdevice.h
@@ -0,0 +1,138 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ brdevice.h
+
+Abstract:
+
+ Private header file to be included by Workstation service modules that
+ need to call into the NT Redirector and the NT Datagram Receiver.
+
+Author:
+
+ Rita Wong (ritaw) 15-Feb-1991
+
+Revision History:
+
+--*/
+
+#ifndef _BRDEVICE_INCLUDED_
+#define _BRDEVICE_INCLUDED_
+
+#include <ntddbrow.h> // Datagram receiver include file
+
+//-------------------------------------------------------------------//
+// //
+// Type definitions //
+// //
+//-------------------------------------------------------------------//
+
+typedef enum _DDTYPE {
+ DatagramReceiver
+} DDTYPE, *PDDTYPE;
+
+typedef struct _BROWSERASYNCCONTEXT {
+ WORKER_ITEM WorkItem;
+
+ PNETWORK Network;
+
+ IO_STATUS_BLOCK IoStatusBlock;
+
+ PLMDR_REQUEST_PACKET RequestPacket;
+
+ //
+ // Timestamp when request was completed.
+ //
+
+ LARGE_INTEGER TimeCompleted;
+
+} BROWSERASYNCCONTEXT, *PBROWSERASYNCCONTEXT;
+
+//-------------------------------------------------------------------//
+// //
+// Function prototypes of support routines found in wsdevice.c //
+// //
+//-------------------------------------------------------------------//
+
+NET_API_STATUS
+BrOpenDgReceiver (
+ VOID
+ );
+
+NET_API_STATUS
+BrAnnounceDomain(
+ IN PNETWORK Network,
+ IN ULONG Periodicty
+ );
+
+NET_API_STATUS
+BrGetTransportList(
+ OUT PLMDR_TRANSPORT_LIST *TransportList
+ );
+
+NET_API_STATUS
+BrIssueAsyncBrowserIoControl(
+ IN PNETWORK Network,
+ IN ULONG ControlCode,
+ IN PBROWSER_WORKER_ROUTINE CompletionRoutine
+ );
+
+NET_API_STATUS
+BrGetLocalBrowseList(
+ IN PNETWORK Network,
+ IN LPWSTR DomainName,
+ IN ULONG Level,
+ IN ULONG ServerType,
+ OUT PVOID *ServerList,
+ OUT PULONG EntriesRead,
+ OUT PULONG TotalEntries
+ );
+
+NET_API_STATUS
+BrUpdateBrowserStatus (
+ IN PNETWORK Network,
+ IN DWORD ServiceStatus
+ );
+
+VOID
+BrShutdownDgReceiver(
+ VOID
+ );
+
+NET_API_STATUS
+BrRemoveOtherDomain(
+ IN PNETWORK Network,
+ IN LPTSTR ServerName
+ );
+
+NET_API_STATUS
+BrAddOtherDomain(
+ IN PNETWORK Network,
+ IN LPTSTR ServerName
+ );
+
+NET_API_STATUS
+BrBindToTransport(
+ IN LPTSTR ServerName
+ );
+
+NET_API_STATUS
+BrUnbindFromTransport(
+ IN LPTSTR ServerName
+ );
+
+//-------------------------------------------------------------------//
+// //
+// Global variables //
+// //
+//-------------------------------------------------------------------//
+
+//
+// Handle to the Datagram Receiver DD
+//
+extern HANDLE BrDgReceiverDeviceHandle;
+
+#endif // ifndef _BRDEVICE_INCLUDED_
diff --git a/private/net/svcdlls/browser/server/brdmmstr.c b/private/net/svcdlls/browser/server/brdmmstr.c
new file mode 100644
index 000000000..94a1b4c40
--- /dev/null
+++ b/private/net/svcdlls/browser/server/brdmmstr.c
@@ -0,0 +1,420 @@
+
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ brdmmstr.c
+
+Abstract:
+
+ This module contains the routines to manage a domain master browser server
+
+Author:
+
+ Rita Wong (ritaw) 20-Feb-1991
+
+Revision History:
+
+--*/
+
+#include "precomp.h"
+#pragma hdrstop
+
+//-------------------------------------------------------------------//
+// //
+// Local structure definitions //
+// //
+//-------------------------------------------------------------------//
+
+//-------------------------------------------------------------------//
+// //
+// Local function prototypes //
+// //
+//-------------------------------------------------------------------//
+
+NET_API_STATUS
+PostGetMasterAnnouncement (
+ PNETWORK Network,
+ PVOID Ctx
+ );
+
+VOID
+BrPostGetMasterAnnouncementWorker(
+ IN PVOID Ctx
+ );
+
+VOID
+GetMasterAnnouncementCompletion (
+ IN PVOID Ctx
+ );
+
+typedef struct _BROWSER_GET_MASTER_ANNOUNCEMENT_CONTEXT {
+ HANDLE EventHandle;
+ NET_API_STATUS NetStatus;
+} BROWSER_GET_MASTER_ANNOUNCEMENT_CONTEXT, *PBROWSER_GET_MASTER_ANNOUNCEMENT_CONTEXT;
+
+NET_API_STATUS
+BrPostGetMasterAnnouncementInWorker(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ Wrapper for BrPostGetMasterAnnouncement. Since BrPostGetMasterAnnouncement
+ starts a pending IO to the browser driver, the thread that calls
+ BrPostGetMasterAnnouncement must remain around forever. This wrapper can
+ be called by any transient thread (e.g., an RPC thread). It simply causes
+ BrPostGetMasterAnnouncement to be called in a worker thread.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ Status of operation.
+
+--*/
+{
+ NET_API_STATUS NetStatus;
+ DWORD WaitStatus;
+
+ WORKER_ITEM WorkItem;
+ BROWSER_GET_MASTER_ANNOUNCEMENT_CONTEXT Context;
+
+ //
+ // Create an event which we use to wait on the worker thread.
+ //
+
+ Context.EventHandle = CreateEvent(
+ NULL, // Event attributes
+ TRUE, // Event must be manually reset
+ FALSE, // Initial state not signalled
+ NULL ); // Event name
+
+ if ( Context.EventHandle == NULL ) {
+ NetStatus = GetLastError();
+ return NetStatus;
+ }
+
+ //
+ // Queue the request to the worker thread.
+ //
+
+ BrInitializeWorkItem( &WorkItem,
+ BrPostGetMasterAnnouncementWorker,
+ &Context );
+
+ BrQueueWorkItem( &WorkItem );
+
+ //
+ // Wait for the worker thread to finish
+ //
+
+ WaitStatus = WaitForSingleObject( Context.EventHandle, INFINITE );
+
+ if ( WaitStatus == WAIT_OBJECT_0 ) {
+ NetStatus = Context.NetStatus;
+ } else {
+ NetStatus = GetLastError();
+ }
+
+ CloseHandle( Context.EventHandle );
+
+ return NetStatus;
+}
+
+ VOID
+BrPostGetMasterAnnouncementWorker(
+ IN PVOID Ctx
+ )
+/*++
+
+Routine Description:
+
+ Worker routine for BrPostGetMasterAnnouncementInWorker.
+
+ This routine executes in the context of a worker thread.
+
+Arguments:
+
+ Context - Context describing the workitem.
+
+Return Value:
+
+ None
+
+--*/
+{
+ PBROWSER_GET_MASTER_ANNOUNCEMENT_CONTEXT Context =
+ (PBROWSER_GET_MASTER_ANNOUNCEMENT_CONTEXT) Ctx;
+
+ //
+ // Create the domain.
+ //
+
+ Context->NetStatus = BrPostGetMasterAnnouncement();
+
+ //
+ // Let the caller know we're done.
+ //
+ SetEvent( Context->EventHandle );
+
+}
+
+ NET_API_STATUS
+BrPostGetMasterAnnouncement (
+ VOID
+ )
+{
+ dprintf(MASTER, ("BrPostGetMasterAnnouncement\n"));
+
+ return BrEnumerateNetworks(PostGetMasterAnnouncement, NULL);
+}
+
+
+ NET_API_STATUS
+PostGetMasterAnnouncement (
+ PNETWORK Network,
+ PVOID Context
+ )
+/*++
+
+Routine Description:
+
+ This function is the worker routine called to actually issue a BecomeBackup
+ FsControl to the bowser driver on all the bound transports. It will
+ complete when the machine becomes a backup browser server.
+
+ Please note that this might never complete.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ Status - The status of the operation.
+
+--*/
+{
+ NET_API_STATUS NetStatus = NERR_Success;
+
+ if (!LOCK_NETWORK(Network)) {
+ return NERR_InternalError;
+ }
+
+ if (Network->Flags & NETWORK_WANNISH) {
+
+ if (!(Network->Flags & NETWORK_GET_MASTER_ANNOUNCE_POSTED)) {
+
+ dprintf(MASTER, ("PostGetMasterAnnouncement on %ws\n", Network->NetworkName.Buffer));
+
+ NetStatus = BrIssueAsyncBrowserIoControl(Network,
+ IOCTL_LMDR_WAIT_FOR_MASTER_ANNOUNCE,
+ GetMasterAnnouncementCompletion
+ );
+
+ if ( NetStatus == NERR_Success ) {
+ Network->Flags |= NETWORK_GET_MASTER_ANNOUNCE_POSTED;
+ }
+ } else {
+ dprintf(MASTER, ("PostGetMasterAnnouncement already posted on %ws\n", Network->NetworkName.Buffer));
+ }
+ }
+
+ UNLOCK_NETWORK(Network);
+ return NetStatus;
+
+ UNREFERENCED_PARAMETER(Context);
+}
+
+
+VOID
+GetMasterAnnouncementCompletion (
+ IN PVOID Ctx
+ )
+/*++
+
+Routine Description:
+
+ This function is the completion routine for a master announcement. It is
+ called whenever a master announcement is received for a particular network.
+
+Arguments:
+
+ Ctx - Context block for request.
+
+Return Value:
+
+ None.
+
+--*/
+
+
+{
+ PVOID ServerList = NULL;
+ ULONG EntriesRead;
+ ULONG TotalEntries;
+ NET_API_STATUS Status = NERR_Success;
+ PBROWSERASYNCCONTEXT Context = Ctx;
+ PLMDR_REQUEST_PACKET MasterAnnouncement = Context->RequestPacket;
+ PNETWORK Network = Context->Network;
+ LPTSTR RemoteMasterName = NULL;
+ BOOLEAN NetLocked = FALSE;
+
+ if (!LOCK_NETWORK(Network)){
+ MIDL_user_free(Context->RequestPacket);
+
+ MIDL_user_free(Context);
+
+ return;
+ }
+
+ NetLocked = TRUE;
+
+ try {
+
+ Network->Flags &= ~NETWORK_GET_MASTER_ANNOUNCE_POSTED;
+
+ //
+ // The request failed for some reason - just return immediately.
+ //
+
+ if (!NT_SUCCESS(Context->IoStatusBlock.Status)) {
+
+ try_return(NOTHING);
+
+ }
+
+ Status = PostGetMasterAnnouncement(Network, NULL);
+
+ if (Status != NERR_Success) {
+ InternalError(("Unable to re-issue GetMasterAnnouncement request: %lx\n", Status));
+
+ try_return(NOTHING);
+
+ }
+
+
+ RemoteMasterName = MIDL_user_allocate(MasterAnnouncement->Parameters.WaitForMasterAnnouncement.MasterNameLength+3*sizeof(TCHAR));
+
+ if (RemoteMasterName == NULL) {
+ try_return(NOTHING);
+ }
+
+ RemoteMasterName[0] = TEXT('\\');
+ RemoteMasterName[1] = TEXT('\\');
+
+ STRNCPY(&RemoteMasterName[2],
+ MasterAnnouncement->Parameters.WaitForMasterAnnouncement.Name,
+ MasterAnnouncement->Parameters.WaitForMasterAnnouncement.MasterNameLength/sizeof(TCHAR));
+
+ RemoteMasterName[(MasterAnnouncement->Parameters.WaitForMasterAnnouncement.MasterNameLength/sizeof(TCHAR))+2] = UNICODE_NULL;
+
+ dprintf(MASTER, ("GetMasterAnnouncement: Got a master browser announcement from %ws\n", RemoteMasterName));
+
+ UNLOCK_NETWORK(Network);
+
+ NetLocked = FALSE;
+
+ //
+ // Remote the api and pull the browse list from the remote server.
+ //
+
+ Status = RxNetServerEnum(RemoteMasterName,
+ Network->NetworkName.Buffer,
+ 101,
+ (LPBYTE *)&ServerList,
+ 0xffffffff,
+ &EntriesRead,
+ &TotalEntries,
+ SV_TYPE_LOCAL_LIST_ONLY,
+ NULL,
+ NULL
+ );
+
+ if ((Status == NERR_Success) || (Status == ERROR_MORE_DATA)) {
+
+ if (!LOCK_NETWORK(Network)) {
+ try_return(NOTHING);
+ }
+
+ NetLocked = TRUE;
+
+ Status = MergeServerList(&Network->BrowseTable,
+ 101,
+ ServerList,
+ EntriesRead,
+ TotalEntries
+ );
+
+ UNLOCK_NETWORK(Network);
+
+ NetLocked = FALSE;
+
+ (void) NetApiBufferFree( ServerList );
+ ServerList = NULL;
+
+ }
+
+ //
+ // Remote the api and pull the browse list from the remote server.
+ //
+
+ Status = RxNetServerEnum(RemoteMasterName,
+ Network->NetworkName.Buffer,
+ 101,
+ (LPBYTE *)&ServerList,
+ 0xffffffff,
+ &EntriesRead,
+ &TotalEntries,
+ SV_TYPE_LOCAL_LIST_ONLY | SV_TYPE_DOMAIN_ENUM,
+ NULL,
+ NULL
+ );
+
+ if ((Status == NERR_Success) || (Status == ERROR_MORE_DATA)) {
+
+ if (!LOCK_NETWORK(Network)) {
+ try_return(NOTHING);
+ }
+
+ NetLocked = TRUE;
+
+ Status = MergeServerList(&Network->DomainList,
+ 101,
+ ServerList,
+ EntriesRead,
+ TotalEntries
+ );
+ }
+
+try_exit:NOTHING;
+ } finally {
+
+ if (NetLocked) {
+ UNLOCK_NETWORK(Network);
+ }
+
+ if (RemoteMasterName != NULL) {
+ MIDL_user_free(RemoteMasterName);
+ }
+
+ MIDL_user_free(Context->RequestPacket);
+
+ MIDL_user_free(Context);
+
+ if ( ServerList != NULL ) {
+ (void) NetApiBufferFree( ServerList );
+ }
+
+ }
+
+ return;
+
+}
diff --git a/private/net/svcdlls/browser/server/brmain.c b/private/net/svcdlls/browser/server/brmain.c
new file mode 100644
index 000000000..836e9a670
--- /dev/null
+++ b/private/net/svcdlls/browser/server/brmain.c
@@ -0,0 +1,1336 @@
+/*++
+
+Copyright (c) 1990-1992 Microsoft Corporation
+
+Module Name:
+
+ brmain.c
+
+Abstract:
+
+ This is the main routine for the NT LAN Manager Browser service.
+
+Author:
+
+ Larry Osterman (LarryO) 3-23-92
+
+Environment:
+
+ User Mode - Win32
+
+Revision History:
+
+--*/
+
+#include "precomp.h"
+#pragma hdrstop
+#if DBG
+#endif
+
+//-------------------------------------------------------------------//
+// //
+// Global variables //
+// //
+//-------------------------------------------------------------------//
+
+BR_GLOBAL_DATA
+BrGlobalData = {0};
+
+ULONG
+BrDefaultRole = {0};
+
+PLMSVCS_GLOBAL_DATA BrLmsvcsGlobalData=NULL;
+
+HANDLE BrDllReferenceHandle=NULL;
+
+//-------------------------------------------------------------------//
+// //
+// Function prototypes //
+// //
+//-------------------------------------------------------------------//
+
+NET_API_STATUS
+BrInitializeBrowser(
+ OUT LPDWORD BrInitState
+ );
+
+NET_API_STATUS
+BrInitializeBrowserService(
+ OUT LPDWORD BrInitState
+ );
+
+VOID
+BrUninitializeBrowser(
+ IN DWORD BrInitState
+ );
+VOID
+BrShutdownBrowser(
+ IN NET_API_STATUS ErrorCode,
+ IN DWORD BrInitState
+ );
+
+VOID
+BrHandleError(
+ IN BR_ERROR_CONDITION FailingCondition,
+ IN NET_API_STATUS Status,
+ IN DWORD BrInitState
+ );
+
+
+VOID
+BrowserControlHandler(
+ IN DWORD Opcode
+ );
+
+
+
+
+VOID
+LMSVCS_ENTRY_POINT( // (BROWSER_main)
+ DWORD NumArgs,
+ LPTSTR *ArgsArray,
+ PLMSVCS_GLOBAL_DATA LmsvcsGlobalData,
+ IN HANDLE SvcRefHandle
+
+ )
+/*++
+
+Routine Description:
+
+ This is the main routine of the Browser Service which registers
+ itself as an RPC server and notifies the Service Controller of the
+ Browser service control entry point.
+
+Arguments:
+
+ NumArgs - Supplies the number of strings specified in ArgsArray.
+
+ ArgsArray - Supplies string arguments that are specified in the
+ StartService API call. This parameter is ignored by the
+ Browser service.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ DWORD BrInitState = 0;
+
+ UNREFERENCED_PARAMETER(NumArgs);
+ UNREFERENCED_PARAMETER(ArgsArray);
+
+ BrDllReferenceHandle = SvcRefHandle;
+
+ //
+ // Save the LMSVCS global data for future use.
+ //
+ BrLmsvcsGlobalData = LmsvcsGlobalData;
+
+ if (BrInitializeBrowserService(&BrInitState) != NERR_Success) {
+ return;
+ }
+
+ //
+ // Process requests in this thread, and wait for termination.
+ //
+
+ //
+ // Set the browser threads to time critical priority.
+ //
+
+ SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL);
+
+
+ BrWorkerThread((PVOID)-1);
+
+ BrShutdownBrowser(
+ NERR_Success,
+ BrInitState
+ );
+
+ return;
+}
+
+
+
+NET_API_STATUS
+BrInitializeBrowserService(
+ OUT LPDWORD BrInitState
+ )
+/*++
+
+Routine Description:
+
+ This function initializes the Browser service.
+
+Arguments:
+
+ BrInitState - Returns a flag to indicate how far we got with initializing
+ the Browser service before an error occured.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NET_API_STATUS Status;
+// HMODULE BrGlobalServerHandle = NULL;
+
+ //
+ // Initialize all the status fields so that subsequent calls to
+ // SetServiceStatus need to only update fields that changed.
+ //
+ BrGlobalData.Status.dwServiceType = SERVICE_WIN32;
+ BrGlobalData.Status.dwCurrentState = SERVICE_START_PENDING;
+ BrGlobalData.Status.dwControlsAccepted = 0;
+ BrGlobalData.Status.dwCheckPoint = 1;
+ BrGlobalData.Status.dwWaitHint = BR_WAIT_HINT_TIME;
+
+ SET_SERVICE_EXITCODE(
+ NO_ERROR,
+ BrGlobalData.Status.dwWin32ExitCode,
+ BrGlobalData.Status.dwServiceSpecificExitCode
+ );
+
+#if DBG
+ BrInitializeTraceLog();
+#endif
+
+ dprintf(INIT, ("Browser starting\n"));
+
+ //
+ // Initialize Browser to receive service requests by registering the
+ // control handler.
+ //
+ if ((BrGlobalData.StatusHandle = RegisterServiceCtrlHandler(
+ SERVICE_BROWSER,
+ BrowserControlHandler
+ )) == (SERVICE_STATUS_HANDLE) NULL) {
+
+ Status = GetLastError();
+ BR_HANDLE_ERROR(BrErrorRegisterControlHandler);
+ return Status;
+ }
+
+ //
+ // Create an event which is used by the service control handler to notify
+ // the Browser service that it is time to terminate.
+ //
+
+ if ((BrGlobalData.TerminateNowEvent =
+ CreateEvent(
+ NULL, // Event attributes
+ TRUE, // Event must be manually reset
+ FALSE,
+ NULL // Initial state not signalled
+ )) == NULL) {
+
+ Status = GetLastError();
+ BR_HANDLE_ERROR(BrErrorCreateTerminateEvent);
+ return Status;
+ }
+ (*BrInitState) |= BR_TERMINATE_EVENT_CREATED;
+
+ //
+ // Notify the Service Controller for the first time that we are alive
+ // and we are start pending
+ //
+
+ if ((Status = BrUpdateStatus()) != NERR_Success) {
+
+ BR_HANDLE_ERROR(BrErrorNotifyServiceController);
+ return Status;
+ }
+
+ if ((Status = BrOpenDgReceiver()) != NERR_Success) {
+ BR_HANDLE_ERROR(BrErrorInitializeNetworks);
+ return Status;
+ }
+
+ dprintf(INIT, ("Devices initialized.\n"));
+ (*BrInitState) |= BR_DEVICES_INITIALIZED;
+
+ //
+ // Initialize NetBios synchronization with the service controller.
+ //
+
+ BrLmsvcsGlobalData->NetBiosOpen();
+ (*BrInitState) |= BR_NETBIOS_INITIALIZED;
+
+ //
+ // Read the configuration information to initialize the browser service.
+ //
+
+ if ((Status = BrInitializeBrowser(BrInitState)) != NERR_Success) {
+
+ BR_HANDLE_ERROR(BrErrorStartBrowser);
+ return Status;
+ }
+
+ dprintf(INIT, ("Browser initialized.\n"));
+ (*BrInitState) |= BR_BROWSER_INITIALIZED;
+
+ //
+ // Service install still pending. Update checkpoint counter and the
+ // status with the Service Controller.
+ //
+ (BrGlobalData.Status.dwCheckPoint)++;
+ (void) BrUpdateStatus();
+
+ //
+ // Initialize the browser service to receive RPC requests
+ //
+ // NOTE: Now all RPC servers in services.exe share the same pipe name.
+ // However, in order to support communication with version 1.0 of WinNt,
+ // it is necessary for the Client Pipe name to remain the same as
+ // it was in version 1.0. Mapping to the new name is performed in
+ // the Named Pipe File System code.
+ //
+ if ((Status = BrLmsvcsGlobalData->StartRpcServer(
+ BrLmsvcsGlobalData->SvcsRpcPipeName,
+ browser_ServerIfHandle
+ )) != NERR_Success) {
+
+ BR_HANDLE_ERROR(BrErrorStartRpcServer);
+ return Status;
+ }
+
+ (*BrInitState) |= BR_RPC_SERVER_STARTED;
+
+ //
+ // Update our announcement bits based on our current role.
+ //
+ // This will force the server to announce itself. It will also update
+ // the browser information in the driver.
+ //
+ //
+
+ if ((Status = BrUpdateAnnouncementBits(BrGlobalData.StatusHandle)) != NERR_Success) {
+ BR_HANDLE_ERROR(BrErrorNotifyServiceController);
+ return Status;
+ }
+
+ dprintf(INIT, ("Network status updated.\n"));
+
+ //
+ // We are done with starting the browser service. Tell Service
+ // Controller our new status.
+ //
+ BrGlobalData.Status.dwCurrentState = SERVICE_RUNNING;
+ BrGlobalData.Status.dwControlsAccepted =
+ (SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN) ;
+
+ BrGlobalData.Status.dwCheckPoint = 0;
+ BrGlobalData.Status.dwWaitHint = 0;
+
+ if ((Status = BrUpdateStatus()) != NERR_Success) {
+
+ BR_HANDLE_ERROR(BrErrorNotifyServiceController);
+ return Status;
+ }
+
+ dprintf(MAIN, ("[Browser] Successful Initialization\n"));
+
+ return NERR_Success;
+}
+
+NET_API_STATUS
+BrInitializeBrowser(
+ OUT LPDWORD BrInitState
+ )
+
+/*++
+
+Routine Description:
+
+ This function shuts down the Browser service.
+
+Arguments:
+
+ ErrorCode - Supplies the error code of the failure
+
+ BrInitState - Supplies a flag to indicate how far we got with initializing
+ the Browser service before an error occured, thus the amount of
+ clean up needed.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ NET_API_STATUS Status;
+ SERVICE_STATUS ServiceStatus;
+
+ //
+ // The browser depends on the following services being started:
+ //
+ // WORKSTATION (to initialize the bowser driver)
+ // SERVER (to receive remote APIs)
+ //
+ // Check to make sure that the services are started.
+ //
+
+ if ((Status = CheckForService(SERVICE_WORKSTATION, &ServiceStatus)) != NERR_Success) {
+ LPWSTR SubStrings[2];
+ CHAR ServiceStatusString[10];
+ WCHAR ServiceStatusStringW[10];
+
+ SubStrings[0] = SERVICE_WORKSTATION;
+
+ _ultoa(ServiceStatus.dwCurrentState, ServiceStatusString, 10);
+
+ mbstowcs(ServiceStatusStringW, ServiceStatusString, 10);
+
+ SubStrings[1] = ServiceStatusStringW;
+
+ BrLogEvent(EVENT_BROWSER_DEPENDANT_SERVICE_FAILED, Status, 2, SubStrings);
+
+ return Status;
+ }
+
+ if ((Status = CheckForService(SERVICE_SERVER, &ServiceStatus)) != NERR_Success) {
+ LPWSTR SubStrings[2];
+ CHAR ServiceStatusString[10];
+ WCHAR ServiceStatusStringW[10];
+
+ SubStrings[0] = SERVICE_SERVER;
+ _ultoa(ServiceStatus.dwCurrentState, ServiceStatusString, 10);
+
+ mbstowcs(ServiceStatusStringW, ServiceStatusString, 10);
+
+ SubStrings[1] = ServiceStatusStringW;
+
+ BrLogEvent(EVENT_BROWSER_DEPENDANT_SERVICE_FAILED, Status, 2, SubStrings);
+
+ return Status;
+ }
+
+ dprintf(INIT, ("Dependant services are running.\n"));
+
+ //
+ // We now know that our dependant services are started.
+ //
+ // Look up our configuration information.
+ //
+
+ if ((Status = BrGetBrowserConfiguration()) != NERR_Success) {
+
+ return Status;
+ }
+
+ dprintf(INIT, ("Configuration read.\n"));
+
+ (*BrInitState) |= BR_CONFIG_INITIALIZED;
+
+ //
+ // Initialize the browser statistics now.
+ //
+
+ NumberOfServerEnumerations = 0;
+
+ NumberOfDomainEnumerations = 0;
+
+ NumberOfOtherEnumerations = 0;
+
+ NumberOfMissedGetBrowserListRequests = 0;
+
+ InitializeCriticalSection(&BrowserStatisticsLock);
+
+ //
+ // MaintainServerList == -1 means No
+ //
+
+ if (BrInfo.MaintainServerList == -1) {
+ KdPrint(("Browser: MaintainServerList value set to NO. Stopping\n"));
+
+ return NERR_BrowserConfiguredToNotRun;
+ }
+
+ //
+ // Initialize the list of serviced networks including the
+ // browse list for each network.
+ //
+
+ if ((Status = BrInitializeNetworks()) != NERR_Success) {
+
+ return Status;
+ }
+
+ dprintf(INIT, ("Networks initialized.\n"));
+
+ (*BrInitState) |= BR_NETWORKS_INITIALIZED;
+
+ //
+ // Service install still pending. Update checkpoint counter and the
+ // status with the Service Controller.
+ //
+
+ (BrGlobalData.Status.dwCheckPoint)++;
+ (void) BrUpdateStatus();
+
+ if ((Status = BrWorkerInitialization()) != NERR_Success) {
+
+ return Status;
+ }
+
+ dprintf(INIT, ("Worker threads created.\n"));
+
+ (*BrInitState) |= BR_THREADS_STARTED;
+
+ //
+ // Service install still pending. Update checkpoint counter and the
+ // status with the Service Controller.
+ //
+
+ (BrGlobalData.Status.dwCheckPoint)++;
+ (void) BrUpdateStatus();
+
+ //
+ // We now know our browser configuration.
+ //
+ //
+ // Perform some tweaks to make sure that we get everything right.
+ //
+
+ Status = RtlAcquireResourceExclusive(&BrInfo.ConfigResource, TRUE);
+
+ if (!Status) {
+ KdPrint(("Browser: Unable to lock config database: %lx\n", Status));
+ return Status;
+ }
+
+ if (BrInfo.DirectHostBinding != NULL) {
+ LPTSTR_ARRAY TStrArray = BrInfo.DirectHostBinding;
+ UNICODE_STRING IpxTransportName;
+ UNICODE_STRING NetbiosTransportName;
+
+ (BrGlobalData.Status.dwCheckPoint)++;
+ (void) BrUpdateStatus();
+
+ while (!NetpIsTStrArrayEmpty(TStrArray)) {
+
+// KdPrint(("BROWSER: Bind to IPX transport %ws\n", TStrArray));
+
+ RtlInitUnicodeString(&IpxTransportName, TStrArray);
+
+ TStrArray = NetpNextTStrArrayEntry(TStrArray);
+
+ ASSERT (!NetpIsTStrArrayEmpty(TStrArray));
+
+ if (!NetpIsTStrArrayEmpty(TStrArray)) {
+ RtlInitUnicodeString(&NetbiosTransportName, TStrArray);
+
+// KdPrint(("BROWSER: Alternate name %ws\n", TStrArray));
+
+ TStrArray = NetpNextTStrArrayEntry(TStrArray);
+
+ //
+ // If the alternate transport doesn't exist,
+ // just ignore the direct host transport.
+ //
+ if ( BrFindNetwork( &NetbiosTransportName ) == NULL) {
+ continue;
+ }
+
+ //
+ // There is a direct host binding on this machine. We want to add
+ // the direct host transport to the browser.
+ //
+
+ Status = BrCreateNetwork(&IpxTransportName, FALSE, FALSE, &NetbiosTransportName);
+
+ if (Status != NERR_Success) {
+ return Status;
+ }
+
+ BrBindToTransport(IpxTransportName.Buffer);
+ }
+ }
+
+ }
+
+ (BrGlobalData.Status.dwCheckPoint)++;
+ (void) BrUpdateStatus();
+
+ //
+ // If we're the PDC, then we're also the domain master.
+ //
+
+ if (BrInfo.IsPrimaryDomainController) {
+ BrInfo.IsDomainMasterBrowser = TRUE;
+ }
+
+ //
+ // if we're a Lan Manager/NT machine, then we need to always be a backup
+ // browser.
+ //
+
+ if (BrInfo.IsLanmanNt) {
+ BrInfo.MaintainServerList = 1;
+ }
+
+ RtlReleaseResource(&BrInfo.ConfigResource);
+
+ //
+ // Post a WaitForRoleChange FsControl on each network the bowser
+ // driver supports. This FsControl will complete when a "tickle"
+ // packet is received on the machine, or when a master browser loses
+ // an election.
+ //
+
+ Status = BrPostWaitForRoleChange();
+
+ Status = RtlAcquireResourceShared(&BrInfo.ConfigResource, TRUE);
+
+ if (BrInfo.IsDomainMasterBrowser) {
+
+ //
+ // If we're a master browser, then preload our domain list with the
+ // master copy from the PDC.
+ //
+
+ BrWanInitialize();
+ dprintf(INIT, ("WAN initialized.\n"));
+ }
+
+ //
+ // This machine's browser has MaintainServerList set to either 0 or 1.
+ //
+
+ //
+ // MaintainServerList == 0 means Auto
+ //
+
+ if (BrInfo.MaintainServerList == 0) {
+
+ //
+ // Post a BecomeBackup FsControl API on each network the bowser
+ // driver supports. This FsControl will complete when the master
+ // for the net wants this client to become a backup server.
+ //
+
+ Status = BrPostBecomeBackup();
+
+ dprintf(INIT, ("Become backup posted.\n"));
+
+ //
+ // Post a BecomeMaster FsControl on each network the bowser driver
+ // supports. This FsControl will complete when this machine becomes
+ // a master browser server.
+ //
+
+ Status = BrPostBecomeMaster();
+
+ dprintf(INIT, ("Become master posted.\n"));
+
+ //
+ // Just for grins, find out who is the master for all the nets. This
+ // will guarantee that someone is the master. Please note that we
+ // ignore any failures on this API.
+ //
+
+ BrGetMasterServerNamesAysnc();
+
+ dprintf(INIT, ("FindMaster for all nets completed.\n"));
+
+ //
+ // MaintainServerList == 1 means Yes
+ //
+
+ } else if (BrInfo.MaintainServerList == 1){
+
+ //
+ // Become a backup server now.
+ //
+
+ Status = BrBecomeBackup();
+
+ dprintf(INIT, ("BecomeBacup on all nets completed.\n"));
+ }
+
+ if (Status != NERR_Success) {
+ RtlReleaseResource(&BrInfo.ConfigResource);
+ return(Status);
+ }
+
+ //
+ // If this machine is running as domain master browser server, post an
+ // FsControl to retreive master browser announcements.
+ //
+
+ if (BrInfo.IsDomainMasterBrowser) {
+ Status = BrPostGetMasterAnnouncement();
+
+ dprintf(INIT, ("MasterAnnouncement posted.\n"));
+
+ }
+
+ if (Status != NERR_Success) {
+ RtlReleaseResource(&BrInfo.ConfigResource);
+ return Status;
+ }
+
+ RtlReleaseResource(&BrInfo.ConfigResource);
+
+ //
+ // If we are on either a domain master, or on a lanman/NT machine,
+ // force an election on all our transports to make sure that we're
+ // the master
+ //
+
+ if (BrInfo.IsDomainMasterBrowser || BrInfo.IsLanmanNt) {
+
+ BrForceElectionOnAllNetworks(EVENT_BROWSER_ELECTION_SENT_LANMAN_NT_STARTED);
+
+ dprintf(INIT, ("Election forced.\n"));
+
+ }
+
+ return Status;
+}
+
+ VOID
+BrUninitializeBrowser(
+ IN DWORD BrInitState
+ )
+/*++
+
+Routine Description:
+
+ This function shuts down the parts of the browser service started by
+ BrInitializeBrowser.
+
+Arguments:
+
+ BrInitState - Supplies a flag to indicate how far we got with initializing
+ the Browser service before an error occured, thus the amount of
+ clean up needed.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ if (BrInitState & BR_CONFIG_INITIALIZED) {
+ BrDeleteConfiguration(BrInitState);
+ }
+
+ DeleteCriticalSection(&BrowserStatisticsLock);
+
+ if (BrInitState & BR_NETWORKS_INITIALIZED) {
+ BrDestroyNetworks(BrInitState);
+ }
+
+}
+
+NET_API_STATUS
+BrElectMasterOnNet(
+ IN PNETWORK Network,
+ IN PVOID Context
+ )
+{
+ DWORD Event = (DWORD)Context;
+ PWSTR SubString[1];
+ REQUEST_ELECTION ElectionRequest;
+
+ if (!LOCK_NETWORK(Network)) {
+ return NERR_InternalError;
+ }
+
+ if (!(Network->Flags & NETWORK_RAS)) {
+
+ //
+ // Indicate we're forcing an election in the event log.
+ //
+
+ SubString[0] = Network->NetworkName.Buffer;
+
+ BrLogEvent(Event, STATUS_SUCCESS, 1, SubString);
+
+ //
+ // Force an election on this net.
+ //
+
+ ElectionRequest.Type = Election;
+
+ ElectionRequest.ElectionRequest.Version = 0;
+ ElectionRequest.ElectionRequest.Criteria = 0;
+ ElectionRequest.ElectionRequest.TimeUp = 0;
+ ElectionRequest.ElectionRequest.MustBeZero = 0;
+ ElectionRequest.ElectionRequest.ServerName[0] = '\0';
+
+ SendDatagram(BrDgReceiverDeviceHandle, &Network->NetworkName,
+ BrInfo.BrPrimaryDomainName,
+ BrowserElection,
+ &ElectionRequest,
+ sizeof(ElectionRequest));
+
+ }
+ UNLOCK_NETWORK(Network);
+
+ return NERR_Success;
+}
+
+ VOID
+BrForceElectionOnAllNetworks(
+ IN DWORD Event
+ )
+{
+ BrEnumerateNetworks(BrElectMasterOnNet, (PVOID)Event);
+}
+
+
+NET_API_STATUS
+BrShutdownBrowserForNet(
+ IN PNETWORK Network,
+ IN PVOID Context
+ )
+{
+ SERVICE_STATUS_HANDLE Handle = (SERVICE_STATUS_HANDLE)Context;
+ NET_API_STATUS Status;
+
+ if (!LOCK_NETWORK(Network)) {
+ return NERR_InternalError;
+ }
+
+ Status = BrUpdateBrowserStatus(Network, 0);
+
+ //
+ // Tell the server that the browser is stopping, so it will announce
+ // that at the browser is not operational.
+ //
+
+ Status = I_NetServerSetServiceBits(NULL, Network->NetworkName.Buffer, 0, TRUE);
+
+ //
+ // Force an election if we are the master for this network - this will
+ // cause someone else to become master.
+ //
+
+ if ( Network->Role & ROLE_MASTER ) {
+ BrElectMasterOnNet(Network, (PVOID)EVENT_BROWSER_ELECTION_SENT_LANMAN_NT_STOPPED);
+ }
+
+ UNLOCK_NETWORK(Network);
+
+ return Status;
+}
+
+ VOID
+BrShutdownBrowser (
+ IN NET_API_STATUS ErrorCode,
+ IN DWORD BrInitState
+ )
+/*++
+
+Routine Description:
+
+ This function shuts down the Browser service.
+
+Arguments:
+
+ ErrorCode - Supplies the error code of the failure
+
+ BrInitState - Supplies a flag to indicate how far we got with initializing
+ the Browser service before an error occured, thus the amount of
+ clean up needed.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ if (BrInitState & BR_RPC_SERVER_STARTED) {
+ //
+ // Stop the RPC server
+ //
+ BrLmsvcsGlobalData->StopRpcServer(browser_ServerIfHandle);
+ }
+
+
+ //
+ // Don't need to ask redirector to unbind from its transports when
+ // cleaning up because the redirector will tear down the bindings when
+ // it stops.
+ //
+
+ if (BrInitState & BR_DEVICES_INITIALIZED) {
+
+ if (BrInitState & BR_NETWORKS_INITIALIZED) {
+ BrEnumerateNetworks(BrShutdownBrowserForNet, (PVOID)BrGlobalData.StatusHandle);
+ }
+
+ //
+ // Shut down the datagram receiver.
+ //
+ // This will cancel all I/O outstanding on the browser for this
+ // handle.
+ //
+
+ BrShutdownDgReceiver();
+ }
+
+ //
+ // Clean up the browser threads.
+ //
+ // This will guarantee that there are no operations outstanding in
+ // the browser when it is shut down.
+ //
+
+ if (BrInitState & BR_THREADS_STARTED) {
+ BrWorkerTermination();
+ }
+
+ if (BrInitState & BR_BROWSER_INITIALIZED) {
+ //
+ // Shut down the browser (including removing networks).
+ //
+ BrUninitializeBrowser(BrInitState);
+ }
+
+ if (BrInitState & BR_PRELOAD_DOMAIN_LIST_READ) {
+ BrWanUninitialize();
+ }
+
+
+ if (BrInitState & BR_TERMINATE_EVENT_CREATED) {
+ //
+ // Close handle to termination event
+ //
+ CloseHandle(BrGlobalData.TerminateNowEvent);
+ }
+
+ if (BrInitState & BR_DEVICES_INITIALIZED) {
+ NtClose(BrDgReceiverDeviceHandle);
+
+ BrDgReceiverDeviceHandle = NULL;
+ }
+
+ //
+ // Tell service controller we are done using NetBios.
+ //
+ if (BrInitState & BR_NETBIOS_INITIALIZED) {
+ BrLmsvcsGlobalData->NetBiosClose();
+ }
+
+ //
+ // We are done with cleaning up. Tell Service Controller that we are
+ // stopped.
+ //
+ BrGlobalData.Status.dwCurrentState = SERVICE_STOPPED;
+ BrGlobalData.Status.dwControlsAccepted = 0;
+
+ SET_SERVICE_EXITCODE(
+ ErrorCode,
+ BrGlobalData.Status.dwWin32ExitCode,
+ BrGlobalData.Status.dwServiceSpecificExitCode
+ );
+
+ BrGlobalData.Status.dwCheckPoint = 0;
+ BrGlobalData.Status.dwWaitHint = 0;
+
+#if DBG
+ BrUninitializeTraceLog();
+#endif
+
+ (void) BrUpdateStatus();
+}
+
+
+VOID
+BrHandleError(
+ IN BR_ERROR_CONDITION FailingCondition,
+ IN NET_API_STATUS Status,
+ IN DWORD BrInitState
+ )
+/*++
+
+Routine Description:
+
+ This function handles a Browser service error condition. If the error
+ condition is fatal, it shuts down the Browser service.
+
+Arguments:
+
+ FailingCondition - Supplies a value which indicates what the failure is.
+
+ Status - Supplies the status code for the failure.
+
+ BrInitState - Supplies a flag to indicate how far we got with initializing
+ the Browser service before an error occured, thus the amount of
+ clean up needed.
+
+Return Value:
+
+ None.
+
+--*/
+{
+
+ switch (FailingCondition) {
+
+ case BrErrorRegisterControlHandler:
+
+ KdPrint(("Browser cannot register control handler "
+ FORMAT_API_STATUS "\n", Status));
+
+ BR_SHUTDOWN_BROWSER(Status);
+
+ break;
+
+ case BrErrorCreateTerminateEvent:
+
+ KdPrint(("[Browser] Cannot create done event "
+ FORMAT_API_STATUS "\n", Status));
+
+ BR_SHUTDOWN_BROWSER(Status);
+
+ break;
+
+ case BrErrorNotifyServiceController:
+
+ KdPrint(("[Browser] SetServiceStatus error "
+ FORMAT_API_STATUS "\n", Status));
+
+ BR_SHUTDOWN_BROWSER(Status);
+
+ break;
+
+ case BrErrorInitLsa:
+
+ KdPrint(("[Browser] LSA initialization error "
+ FORMAT_API_STATUS "\n", Status));
+
+ BR_SHUTDOWN_BROWSER(Status);
+
+ break;
+
+ case BrErrorCheckDependentServices:
+
+ KdPrint(("[Browser] Unable to determine status of dependant services "
+ FORMAT_API_STATUS "\n", Status));
+
+ BR_SHUTDOWN_BROWSER(Status);
+
+ break;
+
+ case BrErrorGetConfiguration:
+
+ KdPrint(("[Browser] Unable to get configuration "
+ FORMAT_API_STATUS "\n", Status));
+
+ BR_SHUTDOWN_BROWSER(Status);
+
+ break;
+
+ case BrErrorInitializeNetworks:
+
+ KdPrint(("[Browser] Unable to initialize networks "
+ FORMAT_API_STATUS "\n", Status));
+
+ BR_SHUTDOWN_BROWSER(Status);
+
+ break;
+
+ case BrErrorStartBrowser:
+
+ KdPrint(("[Browser] Cannot start browser "
+ FORMAT_API_STATUS "\n", Status));
+
+ if (Status == NERR_ServiceInstalled) {
+ BR_SHUTDOWN_BROWSER(NERR_WkstaInconsistentState);
+
+ } else {
+ BR_SHUTDOWN_BROWSER(Status);
+ }
+ break;
+
+ case BrErrorStartRpcServer:
+
+ KdPrint(("[Browser] Cannot start RPC server "
+ FORMAT_API_STATUS "\n", Status));
+
+ BR_SHUTDOWN_BROWSER(Status);
+
+ break;
+
+ case BrErrorCreateApiStructures:
+
+ KdPrint(("[Browser] Error in creating API structures "
+ FORMAT_API_STATUS "\n", Status));
+
+ BR_SHUTDOWN_BROWSER(Status);
+
+ break;
+
+ case BrErrorStartWorkerThreads:
+
+ KdPrint(("[Browser] Error in creating worker threads "
+ FORMAT_API_STATUS "\n", Status));
+
+ BR_SHUTDOWN_BROWSER(Status);
+
+ break;
+
+ default:
+ KdPrint(("[Browser] BrHandleError: unknown error condition %lu\n",
+ FailingCondition));
+
+ NetpAssert(FALSE);
+ BR_SHUTDOWN_BROWSER(Status);
+ break;
+
+ }
+
+}
+
+
+NET_API_STATUS
+BrUpdateStatus(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This function updates the Browser service status with the Service
+ Controller.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NET_API_STATUS status = NERR_Success;
+
+
+ if (BrGlobalData.StatusHandle == (SERVICE_STATUS_HANDLE) NULL) {
+ KdPrint((
+ "[Browser] Cannot call SetServiceStatus, no status handle.\n"
+ ));
+
+ return ERROR_INVALID_HANDLE;
+ }
+
+ if (! SetServiceStatus(BrGlobalData.StatusHandle, &BrGlobalData.Status)) {
+
+ status = GetLastError();
+
+ dprintf(MAIN, ("[Browser] SetServiceStatus error %lu\n", status));
+ }
+
+ return status;
+}
+
+
+
+NET_API_STATUS
+BrUpdateAnnouncementBits(
+ IN SERVICE_STATUS_HANDLE Handle
+ )
+/*++
+
+Routine Description:
+
+ This will update the service announcement bits appropriately depending on
+ the role of the browser server.
+
+Arguments:
+
+ Handle - Supplies a handle for the service controller to allow it to
+ update its information for this service.
+
+Return Value:
+
+ Status - Status of the update..
+
+--*/
+{
+ NET_API_STATUS Status = NERR_Success;
+
+ Status = BrEnumerateNetworks(BrUpdateNetworkAnnouncementBits, NULL);
+
+ return Status;
+}
+
+ ULONG
+BrGetBrowserServiceBits(
+ IN PNETWORK Network
+ )
+{
+ DWORD ServiceBits = 0;
+ if (Network->Role & ROLE_POTENTIAL_BACKUP) {
+ ServiceBits |= SV_TYPE_POTENTIAL_BROWSER;
+ }
+
+ if (Network->Role & ROLE_BACKUP) {
+ ServiceBits |= SV_TYPE_BACKUP_BROWSER;
+ }
+
+ if (Network->Role & ROLE_MASTER) {
+ ServiceBits |= SV_TYPE_MASTER_BROWSER;
+ }
+
+ if (Network->Role & ROLE_DOMAINMASTER) {
+ ServiceBits |= SV_TYPE_DOMAIN_MASTER;
+
+ ASSERT (ServiceBits & SV_TYPE_BACKUP_BROWSER);
+
+ }
+
+ return ServiceBits;
+
+}
+
+ NET_API_STATUS
+BrUpdateNetworkAnnouncementBits(
+ IN PNETWORK Network,
+ IN PVOID Context
+ )
+{
+ NET_API_STATUS Status = NERR_Success;
+ LPTSTR XportName;
+
+ ULONG ServiceBits;
+
+ if (!LOCK_NETWORK(Network)) {
+ return NERR_InternalError;
+ }
+
+ XportName = Network->NetworkName.Buffer;
+
+ try {
+
+ ServiceBits = BrGetBrowserServiceBits(Network);
+
+ //
+ // Have the browser update it's information.
+ //
+
+ //
+ // Don't ever tell the browser to turn off the potential bit - this
+ // has the side effect of turning off the election name.
+ //
+
+ Status = BrUpdateBrowserStatus(Network, ServiceBits | SV_TYPE_POTENTIAL_BROWSER);
+
+ if (Status != NERR_Success) {
+ try_return(Status);
+ }
+
+#if DBG
+ BrUpdateDebugInformation(L"LastServiceStatus", L"LastServiceBits", XportName, NULL, ServiceBits);
+#endif
+
+ if ((Status = I_NetServerSetServiceBits(NULL, XportName, ServiceBits, TRUE)) != NERR_Success) {
+
+ KdPrint(("Unable to set browser service status bits: %ld\n", Status));
+
+ BrLogEvent(EVENT_BROWSER_STATUS_BITS_UPDATE_FAILED, Status, 0, NULL);
+
+ try_return(Status);
+ }
+
+
+try_exit:NOTHING;
+ } finally {
+ UNLOCK_NETWORK(Network);
+
+ }
+
+ return Status;
+}
+
+
+ VOID
+BrowserControlHandler(
+ IN DWORD Opcode
+ )
+/*++
+
+Routine Description:
+
+ This is the service control handler of the Browser service.
+
+Arguments:
+
+ Opcode - Supplies a value which specifies the action for the Browser
+ service to perform.
+
+ Arg - Supplies a value which tells a service specifically what to do
+ for an operation specified by Opcode.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ dprintf(MAIN, ("[Browser] In Control Handler\n"));
+
+ switch (Opcode) {
+
+ case SERVICE_CONTROL_STOP:
+
+ if (BrGlobalData.Status.dwCurrentState != SERVICE_STOP_PENDING) {
+
+ dprintf(MAIN, ("[Browser] Stopping Browser...\n"));
+
+ BrGlobalData.Status.dwCurrentState = SERVICE_STOP_PENDING;
+ BrGlobalData.Status.dwCheckPoint = 1;
+ BrGlobalData.Status.dwWaitHint = BR_WAIT_HINT_TIME;
+
+ if (! SetEvent(BrGlobalData.TerminateNowEvent)) {
+
+ //
+ // Problem with setting event to terminate Browser
+ // service.
+ //
+ KdPrint(("[Browser] Error setting TerminateNowEvent "
+ FORMAT_API_STATUS "\n", GetLastError()));
+ NetpAssert(FALSE);
+ }
+ }
+
+ //
+ // Send the status response.
+ //
+ (void) BrUpdateStatus();
+
+ return;
+
+ case SERVICE_CONTROL_INTERROGATE:
+ break;
+
+ case SERVICE_CONTROL_SHUTDOWN:
+ //
+ // The system is being shutdown. Stop being a browser and
+ // clean up.
+ //
+
+ if (BrGlobalData.Status.dwCurrentState != SERVICE_STOP_PENDING) {
+ BrEnumerateNetworks(BrShutdownBrowserForNet, (PVOID)BrGlobalData.StatusHandle);
+ }
+
+ break;
+
+ default:
+ dprintf(MAIN, ("Unknown Browser opcode " FORMAT_HEX_DWORD
+ "\n", Opcode));
+ }
+
+ //
+ // Send the status response.
+ //
+ (void) BrUpdateStatus();
+}
diff --git a/private/net/svcdlls/browser/server/brmain.h b/private/net/svcdlls/browser/server/brmain.h
new file mode 100644
index 000000000..c7a1447b8
--- /dev/null
+++ b/private/net/svcdlls/browser/server/brmain.h
@@ -0,0 +1,166 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ brmain.h
+
+Abstract:
+
+ Private header file which defines the global data which is used for
+ communication between the service control handler and the
+ rest of the NT Workstation service.
+
+Author:
+
+ Rita Wong (ritaw) 06-May-1991
+
+Revision History:
+
+--*/
+
+#ifndef _BRMAIN_INCLUDED_
+#define _BRMAIN_INCLUDED_
+
+#include <brnames.h> // Service interface names
+
+//
+// Time for the sender of a start or stop request to the Workstation
+// service to wait (in milliseconds) before checking on the
+// Workstation service again to see if it is done.
+//
+#define BR_WAIT_HINT_TIME 45000 // 45 seconds
+
+//
+// Defines to indicate how far we managed to initialize the Browser
+// service before an error is encountered and the extent of clean up needed
+//
+
+#define BR_TERMINATE_EVENT_CREATED 0x00000001
+#define BR_DEVICES_INITIALIZED 0x00000002
+#define BR_RPC_SERVER_STARTED 0x00000004
+#define BR_THREADS_STARTED 0x00000008
+#define BR_NETWORKS_INITIALIZED 0x00000010
+#define BR_BROWSER_INITIALIZED 0x00000020
+#define BR_CONFIG_INITIALIZED 0x00000040
+#define BR_PRELOAD_DOMAIN_LIST_READ 0x00000080
+#define BR_NETBIOS_INITIALIZED 0x00000100
+
+#define BR_BROWSE_LIST_CREATED 0x20000000
+
+#define BR_API_STRUCTURES_CREATED BR_BROWSE_LIST_CREATED
+
+//
+// This macro is called after the redirection of print or comm device
+// has been paused or continued. If either the print or comm device is
+// paused the service is considered paused.
+//
+#define BR_RESET_PAUSE_STATE(BrStatus) { \
+ BrStatus &= ~(SERVICE_PAUSE_STATE); \
+ BrStatus |= (BrStatus & SERVICE_REDIR_PAUSED) ? SERVICE_PAUSED : \
+ SERVICE_ACTIVE; \
+ }
+
+
+
+//
+// Call BrHandleError with the appropriate error condition
+//
+#define BR_HANDLE_ERROR(ErrorCondition) \
+ BrHandleError( \
+ ErrorCondition, \
+ Status, \
+ *BrInitState \
+ );
+
+//
+// Call BrShutdownWorkstation with the exit code
+//
+#define BR_SHUTDOWN_BROWSER(ErrorCode) \
+ BrShutdownBrowser( \
+ ErrorCode, \
+ BrInitState \
+ );
+
+
+//-------------------------------------------------------------------//
+// //
+// Type definitions //
+// //
+//-------------------------------------------------------------------//
+
+typedef enum _BR_ERROR_CONDITION {
+ BrErrorRegisterControlHandler = 0,
+ BrErrorCreateTerminateEvent,
+ BrErrorNotifyServiceController,
+ BrErrorInitLsa,
+ BrErrorStartBrowser,
+ BrErrorGetConfiguration,
+ BrErrorCheckDependentServices,
+ BrErrorInitializeNetworks,
+ BrErrorStartRpcServer,
+ BrErrorInitMessageSend,
+ BrErrorCreateApiStructures,
+ BrErrorStartWorkerThreads,
+ BrErrorInitializeLogon
+} BR_ERROR_CONDITION, *PBR_ERROR_CONDITION;
+
+typedef struct _BR_GLOBAL_DATA {
+
+ //
+ // Workstation service status
+ //
+ SERVICE_STATUS Status;
+
+ //
+ // Service status handle
+ //
+ SERVICE_STATUS_HANDLE StatusHandle;
+
+ //
+ // When the control handler is asked to stop the Workstation service,
+ // it signals this event to notify all threads of the Workstation
+ // service to terminate.
+ //
+ HANDLE TerminateNowEvent;
+
+ HANDLE EventHandle;
+
+} BR_GLOBAL_DATA, *PBR_GLOBAL_DATA;
+
+extern BR_GLOBAL_DATA BrGlobalData;
+
+extern PLMSVCS_GLOBAL_DATA BrLmsvcsGlobalData;
+
+extern
+ULONG
+BrDefaultRole;
+
+ULONG
+BrGetBrowserServiceBits(
+ IN PNETWORK Network
+ );
+
+NET_API_STATUS
+BrUpdateAnnouncementBits(
+ IN SERVICE_STATUS_HANDLE Handle
+ );
+
+NET_API_STATUS
+BrUpdateNetworkAnnouncementBits(
+ IN PNETWORK Network,
+ IN PVOID Context
+ );
+
+NET_API_STATUS
+BrUpdateStatus(
+ VOID
+ );
+
+VOID
+BrForceElectionOnAllNetworks(
+ IN DWORD Event
+ );
+
+#endif // ifndef _BRMAIN_INCLUDED_
diff --git a/private/net/svcdlls/browser/server/brmaster.c b/private/net/svcdlls/browser/server/brmaster.c
new file mode 100644
index 000000000..371cd8f5c
--- /dev/null
+++ b/private/net/svcdlls/browser/server/brmaster.c
@@ -0,0 +1,1541 @@
+/*++
+
+Copyright (c) 1991-1992 Microsoft Corporation
+
+Module Name:
+
+ browser.c
+
+Abstract:
+
+ This module contains the worker routines for the NetWksta APIs
+ implemented in the Workstation service.
+
+Author:
+
+ Rita Wong (ritaw) 20-Feb-1991
+
+Revision History:
+
+--*/
+
+#include "precomp.h"
+#pragma hdrstop
+
+
+//-------------------------------------------------------------------//
+// //
+// Local structure definitions //
+// //
+//-------------------------------------------------------------------//
+
+ULONG
+DomainAnnouncementPeriodicity[] = {1*60*1000, 1*60*1000, 5*60*1000, 5*60*1000, 10*60*1000, 10*60*1000, 15*60*1000};
+
+ULONG
+DomainAnnouncementMax = (sizeof(DomainAnnouncementPeriodicity) / sizeof(ULONG)) - 1;
+
+//-------------------------------------------------------------------//
+// //
+// Local function prototypes //
+// //
+//-------------------------------------------------------------------//
+NET_API_STATUS
+PostBecomeMaster(
+ PNETWORK Network,
+ PVOID Ctx
+ );
+
+VOID
+BecomeMasterCompletion (
+ IN PVOID Ctx
+ );
+
+NET_API_STATUS
+StartMasterBrowserTimer(
+ IN PNETWORK Network
+ );
+
+NET_API_STATUS
+AnnounceMasterToDomainMaster(
+ IN PNETWORK Network,
+ IN LPWSTR ServerName
+ );
+
+//-------------------------------------------------------------------//
+// //
+// Global function prototypes //
+// //
+//-------------------------------------------------------------------//
+ NET_API_STATUS
+BrPostBecomeMaster(
+ VOID
+ )
+{
+ return BrEnumerateNetworks(PostBecomeMaster, NULL);
+}
+
+ NET_API_STATUS
+PostBecomeMaster(
+ PNETWORK Network,
+ PVOID Ctx
+ )
+/*++
+
+Routine Description:
+
+ This function is the worker routine called to actually issue a BecomeMaster
+ FsControl to the bowser driver on all the bound transports. It will
+ complete when the machine becomes a master browser server.
+
+ Please note that this might never complete.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ Status - The status of the operation.
+
+--*/
+{
+ NTSTATUS Status = NERR_Success;
+
+ if (!LOCK_NETWORK(Network)) {
+ return NERR_InternalError;
+ }
+
+ if (!(Network->Flags & NETWORK_BECOME_MASTER_POSTED)) {
+
+ Network->Flags |= NETWORK_BECOME_MASTER_POSTED;
+
+ //
+ // Make certain that we have the browser election name added
+ // before we allow ourselves to become a master. This is a NOP
+ // if we already have the election name added.
+ //
+
+ Status = BrUpdateBrowserStatus(Network, BrGetBrowserServiceBits(Network) | SV_TYPE_POTENTIAL_BROWSER);
+
+ if (Status != NERR_Success) {
+ Network->Flags &= ~NETWORK_BECOME_MASTER_POSTED;
+
+ KdPrint(("Unable to update browser status\n"));
+ UNLOCK_NETWORK(Network);
+ return Status;
+ }
+
+ Status = BrIssueAsyncBrowserIoControl(Network,
+ IOCTL_LMDR_BECOME_MASTER,
+ BecomeMasterCompletion
+ );
+ }
+
+ UNLOCK_NETWORK(Network);
+
+ UNREFERENCED_PARAMETER(Ctx);
+
+ return Status;
+}
+
+NET_API_STATUS
+BrRecoverFromFailedPromotion(
+ IN PVOID Ctx
+ )
+/*++
+
+Routine Description:
+
+ When we attempt to promote a machine to master browser and fail, we will
+ effectively shut down the browser for a period of time. When that period
+ of time expires, we will call BrRecoverFromFailedPromotion to recover
+ from the failure.
+
+ This routine will do one of the following:
+ 1) Force the machine to become a backup browser,
+ or 2) Attempt to discover the name of the master.
+
+Arguments:
+
+ IN PVOID Ctx - The network structure we failed on.
+
+Return Value:
+
+ Status - The status of the operation (usually ignored).
+
+--*/
+
+
+{
+ PNETWORK Network = Ctx;
+ NET_API_STATUS Status;
+ BOOL NetworkLocked = FALSE;
+
+ if (!LOCK_NETWORK(Network)) {
+ return NERR_InternalError;
+ }
+
+ dprintf(MASTER, ("BrRecoverFromFailedPromotion on %ws\n", Network->NetworkName.Buffer));
+
+ NetworkLocked = TRUE;
+
+ try {
+ //
+ // We had better not be the master now.
+ //
+
+ ASSERT (!(Network->Role & ROLE_MASTER));
+
+ //
+ // If we're configured to become a backup by default, then become
+ // a backup now.
+ //
+
+ if (BrInfo.MaintainServerList == 1) {
+
+ dprintf(MASTER, ("BrRecoverFromFailedPromotion. Become backup on %ws\n", Network->NetworkName.Buffer));
+ Status = BecomeBackup(Network, NULL);
+
+ if (Status != NERR_Success) {
+ KdPrint(("Browser: Could not become backup: %lx\n", Status));
+
+ }
+ } else {
+ dprintf(MASTER, ("BrRecoverFromFailedPromotion. FindMaster on %ws\n", Network->NetworkName.Buffer));
+
+ UNLOCK_NETWORK(Network);
+
+ NetworkLocked = FALSE;
+
+ //
+ // Now try to figure out who is the master.
+ //
+
+ Status = GetMasterServerNames(Network);
+
+ //
+ // Ignore the status from this and re-lock the network to
+ // recover cleanly.
+ //
+
+ if (!LOCK_NETWORK(Network)) {
+ return NERR_InternalError;
+ }
+
+ NetworkLocked = TRUE;
+ }
+
+ //
+ // Otherwise, just let sleeping dogs lie.
+ //
+//try_exit:NOTHING;
+ } finally {
+ if (NetworkLocked) {
+ UNLOCK_NETWORK(Network);
+ }
+ }
+
+ return Status;
+}
+
+
+ VOID
+BecomeMasterCompletion (
+ IN PVOID Ctx
+ )
+/*++
+
+Routine Description:
+
+ This function is called by the I/O system when the request to become a
+ master completes.
+
+ Please note that it is possible that the request may complete with an
+ error.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ Status - The status of the operation.
+
+--*/
+{
+ NET_API_STATUS Status;
+ PBROWSERASYNCCONTEXT Context = Ctx;
+ PNETWORK Network = Context->Network;
+ BOOLEAN NetworkLocked = FALSE;
+ ULONG ServiceBits;
+
+ //
+ // Lock the network structure.
+ //
+
+ if (!LOCK_NETWORK(Network)) {
+ MIDL_user_free(Context->RequestPacket);
+
+ MIDL_user_free(Context);
+
+ return;
+
+ }
+
+ NetworkLocked = TRUE;
+
+ try {
+
+ Network->Flags &= ~NETWORK_BECOME_MASTER_POSTED;
+
+ if (!NT_SUCCESS(Context->IoStatusBlock.Status)) {
+ InternalError(("Browser: Failure in BecomeMaster: %X\n", Context->IoStatusBlock.Status));
+
+ try_return(NOTHING);
+
+ }
+
+ dprintf(MASTER, ("BecomeMasterCompletion. Now master on network %ws\n", Network->NetworkName.Buffer));
+
+ //
+ // If we're already a master, ignore this request.
+ //
+
+ if (Network->Role & ROLE_MASTER) {
+ try_return(NOTHING);
+ }
+
+ //
+ // Cancel any outstanding backup timers - we don't download the list
+ // anymore.
+ //
+
+ Status = BrCancelTimer(&Network->BackupBrowserTimer);
+
+ if (!NT_SUCCESS(Status)) {
+ KdPrint(("Browser: Could not stop backup timer: %lx\n", Status));
+ }
+
+ //
+ // Figure out what service bits we should be using when announcing ourselves
+ //
+
+ Network->Role |= ROLE_MASTER;
+
+ ServiceBits = BrGetBrowserServiceBits(Network);
+
+ Status = BrUpdateBrowserStatus(Network, ServiceBits | SV_TYPE_POTENTIAL_BROWSER);
+
+ if (Status != NERR_Success) {
+ dprintf(MASTER, ("Unable to set master announcement bits in browser: %ld\n", Status));
+
+ //
+ // When we're in this state, we can't rely on our being a backup
+ // browser - we may not be able to retrieve a valid list of
+ // browsers from the master.
+ //
+
+ Network->Role &= ~ROLE_BACKUP;
+
+ Network->NumberOfFailedPromotions += 1;
+
+ //
+ // Log every 5 failed promotion attempts, and after having logged 5
+ // promotion events, stop logging them, this means that it's been
+ // 25 times that we've tried to promote, and it's not likely to get
+ // any better. We'll keep on trying, but we won't complain any more.
+ //
+
+ if ((Network->NumberOfFailedPromotions % 5) == 0) {
+ ULONG AStatStatus;
+ LPWSTR SubString[1];
+ WCHAR CurrentMasterName[CNLEN+1];
+
+ if (Network->NumberOfPromotionEventsLogged < 5) {
+
+ AStatStatus = GetNetBiosMasterName(
+ Network->NetworkName.Buffer,
+ BrInfo.BrPrimaryDomainName,
+ CurrentMasterName,
+ BrLmsvcsGlobalData->NetBiosReset
+ );
+
+ if (AStatStatus == NERR_Success) {
+ SubString[0] = CurrentMasterName;
+
+ BrLogEvent(EVENT_BROWSER_MASTER_PROMOTION_FAILED, Status, 1, SubString);
+ } else {
+ BrLogEvent(EVENT_BROWSER_MASTER_PROMOTION_FAILED_NO_MASTER, Status, 0, NULL);
+ }
+
+ Network->NumberOfPromotionEventsLogged += 1;
+
+ if (Network->NumberOfPromotionEventsLogged == 5) {
+ BrLogEvent(EVENT_BROWSER_MASTER_PROMOTION_FAILED_STOPPING, Status, 0, NULL);
+ }
+ }
+ }
+
+ //
+ // We were unable to promote ourselves to master.
+ //
+ // We want to set our role back to browser, and re-issue the become
+ // master request.
+ //
+
+ BrStopMaster(Network);
+
+ BrSetTimer(&Network->MasterBrowserTimer, FAILED_PROMOTION_PERIODICITY*1000, BrRecoverFromFailedPromotion, Network);
+
+ } else {
+
+ //
+ // Initialize the number of times the master timer has run.
+ //
+
+ Network->MasterBrowserTimerCount = 0;
+
+ Status = StartMasterBrowserTimer(Network);
+
+ if (Status != NERR_Success) {
+ InternalError(("Browser: Could not start browser master timer\n"));
+ }
+
+ Network->NumberOfFailedPromotions = 0;
+
+ Network->NumberOfPromotionEventsLogged = 0;
+
+ Network->MasterAnnouncementIndex = 0;
+
+ Status = I_NetServerSetServiceBits(NULL, Network->NetworkName.Buffer, ServiceBits, TRUE);
+
+ if (Status == NERR_Success) {
+
+ //
+ // We successfully became the master.
+ //
+ // Now announce ourselves as the new master for this domain.
+ //
+
+ BrMasterAnnouncement(Network);
+
+ //
+ // Populate the browse list with the information retrieved
+ // while we were a backup browser.
+ //
+
+ if (Network->TotalBackupServerListEntries != 0) {
+ MergeServerList(&Network->BrowseTable,
+ 101,
+ Network->BackupServerList,
+ Network->TotalBackupServerListEntries,
+ Network->TotalBackupServerListEntries
+ );
+ MIDL_user_free(Network->BackupServerList);
+
+ Network->BackupServerList = NULL;
+
+ Network->TotalBackupServerListEntries = 0;
+ }
+
+ if (Network->TotalBackupDomainListEntries != 0) {
+ MergeServerList(&Network->DomainList,
+ 101,
+ Network->BackupDomainList,
+ Network->TotalBackupDomainListEntries,
+ Network->TotalBackupDomainListEntries
+ );
+ MIDL_user_free(Network->BackupDomainList);
+
+ Network->BackupDomainList = NULL;
+
+ Network->TotalBackupDomainListEntries = 0;
+ }
+
+
+
+ //
+ // Unlock the network before calling BrWanMasterInitialize.
+ //
+ UNLOCK_NETWORK(Network);
+ NetworkLocked = FALSE;
+
+
+ //
+ // Run the master browser timer routine to get the entire domains
+ // list of servers.
+ //
+
+ if (Network->Flags & NETWORK_WANNISH) {
+ BrWanMasterInitialize(Network);
+ MasterBrowserTimerRoutine(Network);
+ }
+
+ } else {
+
+ BrLogEvent(EVENT_BROWSER_STATUS_BITS_UPDATE_FAILED, Status, 0, NULL);
+
+ //
+ // Stop being a master browser.
+ //
+
+ BrStopMaster(Network);
+
+ dprintf(MASTER, ("Unable to set master announcement bits to server: %ld\n", Status));
+
+ try_return(NOTHING);
+
+ }
+
+ try_return(NOTHING);
+
+ }
+try_exit:NOTHING;
+ } finally {
+
+ //
+ // Make sure there's a become master oustanding.
+ //
+
+ PostBecomeMaster(Network, NULL);
+
+ if (NetworkLocked) {
+ UNLOCK_NETWORK(Network);
+ }
+
+ MIDL_user_free(Context->RequestPacket);
+
+ MIDL_user_free(Context);
+ }
+
+}
+
+
+
+NET_API_STATUS
+ChangeMasterPeriodicityWorker(
+ PNETWORK Network,
+ PVOID Ctx
+ )
+/*++
+
+Routine Description:
+
+ This function changes the master periodicity for a single network.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ Status - The status of the operation.
+
+--*/
+{
+
+ //
+ // Lock the network
+ //
+
+ if (LOCK_NETWORK(Network)) {
+
+ //
+ // Ensure we're the master.
+ //
+
+ if ( Network->Role & ROLE_MASTER ) {
+ NET_API_STATUS NetStatus;
+
+ //
+ // Cancel the timer to ensure it doesn't go off while we're
+ // processing this change.
+ //
+
+ NetStatus = BrCancelTimer(&Network->MasterBrowserTimer);
+ ASSERT (NetStatus == NERR_Success);
+
+ //
+ // Unlock the network while we execute the timer routine.
+ //
+ UNLOCK_NETWORK( Network );
+
+ //
+ // Call the timer routine immediately.
+ //
+ MasterBrowserTimerRoutine(Network);
+
+ } else {
+ UNLOCK_NETWORK( Network );
+ }
+
+ }
+
+ UNREFERENCED_PARAMETER(Ctx);
+
+ return NERR_Success;
+}
+
+
+
+VOID
+BrChangeMasterPeriodicity (
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This function is called when the master periodicity is changed in the
+ registry.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ (VOID)BrEnumerateNetworks(ChangeMasterPeriodicityWorker, NULL);
+}
+
+ NET_API_STATUS
+StartMasterBrowserTimer(
+ IN PNETWORK Network
+ )
+{
+ NET_API_STATUS Status;
+
+ Status = BrSetTimer( &Network->MasterBrowserTimer,
+ BrInfo.MasterPeriodicity*1000,
+ MasterBrowserTimerRoutine,
+ Network);
+
+ return Status;
+
+}
+
+//
+// Don't worry about this being global. It's only used once during initialization.
+WORKER_ITEM BrowserAsyncNamesWorkItem;
+
+ VOID
+BrGetMasterServerNamesAysnc(
+ VOID
+ )
+{
+ //
+ // Just queue this for later execution.
+ // We're doing this for information purposes only. In the case that
+ // the master can't be found, we don't want to wait for completion.
+ // (e.g., on a machine with multiple transports and the net cable is
+ // pulled)
+ //
+ BrInitializeWorkItem( &BrowserAsyncNamesWorkItem,
+ BrGetMasterServerNamesOnAllNets,
+ NULL );
+
+ BrQueueWorkItem( &BrowserAsyncNamesWorkItem );
+
+ return;
+
+}
+
+ NET_API_STATUS
+BrGetMasterServerNameForNet(
+ IN PNETWORK Network,
+ IN PVOID Context
+ )
+{
+ //
+ // Checkpoint the service controller - this gives us 30 seconds/transport
+ // before the service controller gets unhappy.
+ //
+
+ (BrGlobalData.Status.dwCheckPoint)++;
+ (void) BrUpdateStatus();
+
+ dprintf(INIT, ("FindMaster for net %ws during startup\n", Network->NetworkName.Buffer));
+
+ //
+ // We only call this on startup, so on IPX networks, don't bother to
+ // find out the master.
+ //
+
+ if (!(Network->Flags & NETWORK_IPX)) {
+ GetMasterServerNames(Network);
+ }
+
+ return NERR_Success;
+}
+
+ VOID
+BrGetMasterServerNamesOnAllNets(
+ IN PVOID Context
+ )
+{
+ (VOID) BrEnumerateNetworks(BrGetMasterServerNameForNet, Context);
+ return;
+
+}
+
+ NET_API_STATUS
+GetMasterServerNames(
+ IN PNETWORK Network
+ )
+/*++
+
+Routine Description:
+
+ This function is the worker routine called to determine the name of the
+ master browser server for a particular network.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ Status - The status of the operation.
+
+--*/
+{
+ NET_API_STATUS Status;
+
+ PLMDR_REQUEST_PACKET RequestPacket = NULL;
+
+ PWSTR OldBuffer;
+
+ dprintf(INIT, ("FindMaster on network %ws\n", Network->NetworkName.Buffer));
+
+ //
+ // This request could cause an election. Make sure that if we win
+ // the election that we can handle it.
+ //
+
+ PostBecomeMaster( Network, NULL );
+
+ RequestPacket = MIDL_user_allocate(
+ (UINT) sizeof(LMDR_REQUEST_PACKET)+
+ MAXIMUM_FILENAME_LENGTH*sizeof(WCHAR)
+ );
+
+ if (RequestPacket == NULL) {
+ return(ERROR_NOT_ENOUGH_MEMORY);
+ }
+
+ RequestPacket->Version = LMDR_REQUEST_PACKET_VERSION;
+
+ //
+ // Set level to TRUE to indicate that find master should initiate
+ // a findmaster request.
+ //
+
+ RequestPacket->Level = 1;
+
+ RequestPacket->TransportName = Network->NetworkName;
+
+ //
+ // Reference the network while the I/O is pending.
+ //
+
+ Status = BrDgReceiverIoControl(BrDgReceiverDeviceHandle,
+ IOCTL_LMDR_GET_MASTER_NAME,
+ RequestPacket,
+ sizeof(LMDR_REQUEST_PACKET)+Network->NetworkName.Length,
+ RequestPacket,
+ sizeof(LMDR_REQUEST_PACKET)+MAXIMUM_FILENAME_LENGTH*sizeof(WCHAR),
+ NULL);
+
+ if (Status != NERR_Success) {
+
+ dprintf(INIT, ("FindMaster on network %ws failed: %ld\n", Network->NetworkName.Buffer, Status));
+ MIDL_user_free(RequestPacket);
+
+ return(Status);
+ }
+
+ if (!LOCK_NETWORK(Network)) {
+ MIDL_user_free(RequestPacket);
+
+ return NERR_InternalError;
+ }
+
+ OldBuffer = Network->MasterBrowserName.Buffer;
+
+ Network->MasterBrowserName.Buffer = MIDL_user_allocate(
+ (UINT) RequestPacket->Parameters.GetMasterName.MasterNameLength
+ +sizeof(WCHAR)
+ );
+
+ if (Network->MasterBrowserName.Buffer == NULL) {
+
+ Network->MasterBrowserName.Buffer = OldBuffer;
+
+ UNLOCK_NETWORK(Network);
+
+ MIDL_user_free(RequestPacket);
+
+ return(ERROR_NOT_ENOUGH_MEMORY);
+ }
+
+ Network->MasterBrowserName.MaximumLength = (USHORT)RequestPacket->Parameters.GetMasterName.MasterNameLength+sizeof(WCHAR);
+
+ Network->MasterBrowserName.Length = (USHORT)RequestPacket->Parameters.GetMasterName.MasterNameLength;
+
+ RtlCopyMemory(Network->MasterBrowserName.Buffer, RequestPacket->Parameters.GetMasterName.Name,
+ Network->MasterBrowserName.MaximumLength);
+
+ ASSERT ( NetpIsUncComputerNameValid( Network->MasterBrowserName.Buffer ) );
+
+ dprintf(INIT, ("FindMaster on network %ws succeeded. Master: %ws\n", Network->NetworkName.Buffer, Network->MasterBrowserName.Buffer));
+
+ UNLOCK_NETWORK(Network);
+
+ MIDL_user_free(RequestPacket);
+
+ if (OldBuffer != NULL) {
+ MIDL_user_free(OldBuffer);
+ }
+
+ return Status;
+}
+
+ VOID
+MasterBrowserTimerRoutine (
+ IN PVOID TimerContext
+ )
+{
+ IN PNETWORK Network = TimerContext;
+ NET_API_STATUS Status;
+ PVOID ServerList = NULL;
+ PVOID WinsServerList = NULL;
+ ULONG EntriesInList;
+ ULONG TotalEntriesInList;
+ LPWSTR TransportName;
+ BOOLEAN NetLocked = FALSE;
+ LPWSTR PrimaryDomainController = NULL;
+ LPWSTR PrimaryWinsServerAddress = NULL;
+ LPWSTR SecondaryWinsServerAddress = NULL;
+
+ TransportName = Network->NetworkName.Buffer;
+
+ //
+ // If we're not a master any more, blow away this request.
+ //
+
+ if (!(Network->Role & ROLE_MASTER)) {
+ return;
+ }
+
+ if (!LOCK_NETWORK(Network)) {
+ return;
+ }
+
+ NetLocked = TRUE;
+
+ try {
+
+
+ //
+ // Now that we have the network locked, re-test to see if we are
+ // still the master.
+ //
+
+ if (!(Network->Role & ROLE_MASTER)) {
+ try_return(NOTHING);
+ }
+
+ Network->MasterBrowserTimerCount += 1;
+
+ //
+ // If this is a wannish network, we always want to run the master
+ // timer because we might have information about other subnets
+ // in our list.
+ //
+
+ if (Network->Flags & NETWORK_WANNISH) {
+
+ //
+ // Age out servers and domains from the server list.
+ //
+
+ AgeInterimServerList(&Network->BrowseTable);
+
+ AgeInterimServerList(&Network->DomainList);
+
+ //
+ // If we're not the PDC, then we need to retrieve the list
+ // from the PDC....
+ //
+
+ if (!BrInfo.IsPrimaryDomainController) {
+
+ ASSERT (NetLocked);
+
+ UNLOCK_NETWORK(Network);
+
+ NetLocked = FALSE;
+
+ Status = NetGetDCName(NULL, NULL, (LPBYTE *)&PrimaryDomainController);
+
+ //
+ // If the PDC can be found,
+ // Exchange server lists with it.
+ //
+
+ if (Status == NERR_Success) {
+
+ //
+ // Tell the Domain Master (PDC) that we're a master browser.
+ //
+
+ (VOID) AnnounceMasterToDomainMaster (Network, &PrimaryDomainController[2]);
+
+
+ //
+ // Retrieve the list of all the servers from the PDC.
+ //
+
+ Status = RxNetServerEnum(PrimaryDomainController,
+ TransportName,
+ 101,
+ (LPBYTE *)&ServerList,
+ 0xffffffff,
+ &EntriesInList,
+ &TotalEntriesInList,
+ SV_TYPE_ALL,
+ NULL,
+ NULL
+ );
+
+ if ((Status == NERR_Success) || (Status == ERROR_MORE_DATA)) {
+
+ ASSERT (!NetLocked);
+
+ if (LOCK_NETWORK(Network)) {
+
+ NetLocked = TRUE;
+
+ if (Network->Role & ROLE_MASTER) {
+ (VOID) MergeServerList(&Network->BrowseTable,
+ 101,
+ ServerList,
+ EntriesInList,
+ TotalEntriesInList );
+ }
+ }
+
+ }
+
+ if (ServerList != NULL) {
+ MIDL_user_free(ServerList);
+ ServerList = NULL;
+ }
+
+ if (NetLocked) {
+ UNLOCK_NETWORK(Network);
+ NetLocked = FALSE;
+ }
+
+ //
+ // Retrieve the list of all the domains from the PDC.
+ //
+
+ Status = RxNetServerEnum(PrimaryDomainController,
+ TransportName,
+ 101,
+ (LPBYTE *)&ServerList,
+ 0xffffffff,
+ &EntriesInList,
+ &TotalEntriesInList,
+ SV_TYPE_DOMAIN_ENUM,
+ NULL,
+ NULL
+ );
+
+ if ((Status == NERR_Success) || (Status == ERROR_MORE_DATA)) {
+
+ ASSERT (!NetLocked);
+
+ if (LOCK_NETWORK(Network)) {
+
+ NetLocked = TRUE;
+
+ if (Network->Role & ROLE_MASTER) {
+ (VOID) MergeServerList(&Network->DomainList,
+ 101,
+ ServerList,
+ EntriesInList,
+ TotalEntriesInList );
+ }
+ }
+
+ }
+
+ if (ServerList != NULL) {
+ MIDL_user_free(ServerList);
+ ServerList = NULL;
+ }
+
+
+ //
+ // Unlock the network before calling BrWanMasterInitialize.
+ //
+
+ if (NetLocked) {
+ UNLOCK_NETWORK(Network);
+ NetLocked = FALSE;
+ }
+
+ BrWanMasterInitialize(Network);
+
+ }
+
+
+ //
+ // If we're on the PDC, we need to get the list of servers from
+ // the WINS server.
+ //
+
+ } else {
+
+ //
+ // Ensure a GetMasterAnnouncement request is posted to the bowser.
+ //
+
+ (VOID) PostGetMasterAnnouncement ( Network, NULL );
+
+ //
+ // We want to contact the WINS server now, so we figure out the
+ // IP address of our primary WINS server
+ //
+
+ Status = BrGetWinsServerName(&Network->NetworkName,
+ &PrimaryWinsServerAddress,
+ &SecondaryWinsServerAddress);
+ if (Status == NERR_Success) {
+
+ //
+ // Don't keep the network locked during the WINS query
+ //
+
+ if (NetLocked) {
+ UNLOCK_NETWORK(Network);
+ NetLocked = FALSE;
+ }
+
+ //
+ // This transport supports WINS queries, so query the WINS
+ // server to retrieve the list of domains on this adapter.
+ //
+
+ Status = BrQueryWinsServer(PrimaryWinsServerAddress,
+ SecondaryWinsServerAddress,
+ &WinsServerList,
+ &EntriesInList,
+ &TotalEntriesInList
+ );
+
+ if (Status == NERR_Success) {
+
+ //
+ // Lock the network to merge the server list
+ //
+
+ ASSERT (!NetLocked);
+
+ if (LOCK_NETWORK(Network)) {
+ NetLocked = TRUE;
+
+ if (Network->Role & ROLE_MASTER) {
+
+ //
+ // Merge the list of domains from WINS into the one collected elsewhere
+ //
+ (VOID) MergeServerList(
+ &Network->DomainList,
+ 1010, // Special level to not overide current values
+ WinsServerList,
+ EntriesInList,
+ TotalEntriesInList );
+ }
+ }
+ }
+
+ }
+ }
+
+
+ //
+ // Restart the timer for this domain.
+ //
+ // Wait to restart it until we're almost done with this iteration.
+ // Otherwise, we could end up with two copies of this routine
+ // running.
+ //
+
+ Status = StartMasterBrowserTimer(Network);
+
+ if (Status != NERR_Success) {
+ InternalError(("Browser: Unable to restart browser backup timer: %lx\n", Status));
+ try_return(NOTHING);
+ }
+
+ } else {
+
+ //
+ // If it is a lan-ish transport, and we have run the master
+ // timer for enough times (ie. we've been a master
+ // for "long enough", we can toss the interim server list in the
+ // master, because the bowser driver will have enough data in its
+ // list by now.
+ //
+
+ if (Network->MasterBrowserTimerCount >= MASTER_BROWSER_LAN_TIMER_LIMIT) {
+
+ ASSERT (NetLocked);
+
+ //
+ // Make all the servers and domains in the interim server list
+ // go away - they aren't needed any more for a LAN-ish transport.
+ //
+
+ UninitializeInterimServerList(&Network->BrowseTable);
+
+ ASSERT (Network->BrowseTable.EntriesRead == 0);
+
+ ASSERT (Network->BrowseTable.TotalEntries == 0);
+
+ UninitializeInterimServerList(&Network->DomainList);
+
+ ASSERT (Network->DomainList.EntriesRead == 0);
+
+ ASSERT (Network->DomainList.TotalEntries == 0);
+
+ } else {
+
+ //
+ // Age out servers and domains from the server list.
+ //
+
+ AgeInterimServerList(&Network->BrowseTable);
+
+ AgeInterimServerList(&Network->DomainList);
+
+ //
+ // Restart the timer for this domain.
+ //
+
+ Status = StartMasterBrowserTimer(Network);
+
+ if (Status != NERR_Success) {
+ InternalError(("Browser: Unable to restart browser backup timer: %lx\n", Status));
+ try_return(NOTHING);
+ }
+ }
+
+ }
+try_exit:NOTHING;
+ } finally {
+ if (NetLocked) {
+ UNLOCK_NETWORK(Network);
+ }
+
+ if (PrimaryDomainController != NULL) {
+ NetApiBufferFree(PrimaryDomainController);
+ }
+
+ if (PrimaryWinsServerAddress) {
+ MIDL_user_free(PrimaryWinsServerAddress);
+ }
+
+ if (SecondaryWinsServerAddress) {
+ MIDL_user_free(SecondaryWinsServerAddress);
+ }
+
+ if (WinsServerList) {
+ MIDL_user_free(WinsServerList);
+ }
+ }
+}
+
+
+ VOID
+BrMasterAnnouncement(
+ IN PVOID TimerContext
+ )
+/*++
+
+Routine Description:
+
+ This routine is called to announce the domain on the local sub-net.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None
+
+--*/
+
+{
+ PNETWORK Network = TimerContext;
+ ULONG Periodicity;
+ DWORD ServiceBits;
+ NET_API_STATUS Status;
+
+ if (!LOCK_NETWORK(Network)) {
+ return;
+ }
+
+
+ //
+ // Make absolutely certain that the server thinks that the browser service
+ // bits for this transport are up to date. We do NOT have to force an
+ // announcement, since theoretically, the status didn't change.
+ //
+
+ ServiceBits = BrGetBrowserServiceBits(Network);
+
+ Status = I_NetServerSetServiceBits(NULL, Network->NetworkName.Buffer, ServiceBits, FALSE);
+
+ if (Status != NERR_Success) {
+ BrLogEvent(EVENT_BROWSER_STATUS_BITS_UPDATE_FAILED, Status, 0, NULL);
+ }
+
+ Periodicity = DomainAnnouncementPeriodicity[Network->MasterAnnouncementIndex];
+
+ BrSetTimer(&Network->MasterBrowserAnnouncementTimer, Periodicity, BrMasterAnnouncement, Network);
+
+ if (Network->MasterAnnouncementIndex != DomainAnnouncementMax) {
+ Network->MasterAnnouncementIndex += 1;
+ }
+
+ //
+ // Announce this domain to the world using the current periodicity.
+ //
+
+ BrAnnounceDomain(Network, Periodicity);
+
+ UNLOCK_NETWORK(Network);
+}
+
+
+ NET_API_STATUS
+BrStopMaster(
+ IN PNETWORK Network
+ )
+{
+ NET_API_STATUS Status;
+ ULONG ServiceBits;
+
+ //
+ // This guy is shutting down - set his role to 0 and announce.
+ //
+
+ if (!LOCK_NETWORK(Network)) {
+ return NERR_InternalError;
+ }
+
+ try {
+
+ dprintf(MASTER, ("Stopping being master on network %ws\n", Network->NetworkName.Buffer));
+
+ //
+ // When we stop being a master, we can no longer be considered a
+ // backup either, since backups maintain their server list
+ // differently than the master.
+ //
+
+
+ Network->Role &= ~(ROLE_MASTER | ROLE_BACKUP);
+
+ ServiceBits = BrGetBrowserServiceBits(Network);
+
+ ASSERT ((ServiceBits & SV_TYPE_MASTER_BROWSER) == 0);
+
+ Status = BrUpdateBrowserStatus(Network, ServiceBits | SV_TYPE_POTENTIAL_BROWSER);
+
+ if (Status != NERR_Success) {
+ dprintf(MASTER, ("Unable to clear master announcement bits in browser: %ld\n", Status));
+ try_return(Status);
+ }
+
+ Status = I_NetServerSetServiceBits(NULL, Network->NetworkName.Buffer, ServiceBits, TRUE);
+
+ if (Status != NERR_Success) {
+
+ BrLogEvent(EVENT_BROWSER_STATUS_BITS_UPDATE_FAILED, Status, 0, NULL);
+
+ dprintf(MASTER, ("Unable to clear master announcement bits to server: %ld\n", Status));
+
+ try_return(Status);
+ }
+
+ //
+ // Stop our master related timers.
+ //
+
+ Status = BrCancelTimer(&Network->MasterBrowserAnnouncementTimer);
+
+ ASSERT (Status == NERR_Success);
+
+ Status = BrCancelTimer(&Network->MasterBrowserTimer);
+
+ ASSERT (Status == NERR_Success);
+
+try_exit:NOTHING;
+ } finally {
+ UNLOCK_NETWORK(Network);
+ }
+
+ return Status;
+
+}
+
+ NET_API_STATUS
+AnnounceMasterToDomainMaster(
+ IN PNETWORK Network,
+ IN LPWSTR ServerName
+ )
+{
+ NET_API_STATUS Status;
+ CHAR Buffer[sizeof(MASTER_ANNOUNCEMENT)+CNLEN+1];
+ PMASTER_ANNOUNCEMENT MasterAnnouncementp = (PMASTER_ANNOUNCEMENT)Buffer;
+ OEM_STRING OemComputerName;
+ UNICODE_STRING UnicodeComputerName, ComputerNameU;
+
+ RtlInitUnicodeString(&ComputerNameU, BrInfo.BrComputerName);
+
+ RtlUpcaseUnicodeString(&UnicodeComputerName, &ComputerNameU, TRUE);
+
+ OemComputerName.Buffer = MasterAnnouncementp->MasterAnnouncement.MasterName;
+
+ OemComputerName.MaximumLength = CNLEN+1;
+
+ RtlUnicodeStringToOemString(&OemComputerName, &UnicodeComputerName, FALSE);
+
+ MasterAnnouncementp->Type = MasterAnnouncement;
+
+ Status = SendDatagram(BrDgReceiverDeviceHandle, &Network->NetworkName,
+ ServerName,
+ ComputerName,
+ MasterAnnouncementp,
+ FIELD_OFFSET(MASTER_ANNOUNCEMENT, MasterAnnouncement.MasterName) + OemComputerName.Length+sizeof(CHAR)
+ );
+
+
+ RtlFreeUnicodeString(&UnicodeComputerName);
+
+ return Status;
+}
+
+ NET_API_STATUS NET_API_FUNCTION
+I_BrowserrResetNetlogonState(
+ IN BROWSER_IDENTIFY_HANDLE ServerName
+ )
+
+/*++
+
+Routine Description:
+
+ This routine will reset the bowser's concept of the state of the netlogon
+ service. It is called by the UI when it promotes or demotes a DC.
+
+
+Arguments:
+
+ IN BROWSER_IDENTIFY_HANDLE ServerName - Ignored.
+
+Return Value:
+
+ NET_API_STATUS - The status of this request.
+
+--*/
+
+{
+ LSA_HANDLE LsaHandle;
+ NET_API_STATUS Status = NERR_Success;
+ PPOLICY_LSA_SERVER_ROLE_INFO ServerRole;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+
+ if (! RtlAcquireResourceExclusive(&BrInfo.ConfigResource, TRUE)) {
+ return NERR_InternalError;
+ }
+
+ if (!BrInfo.IsLanmanNt) {
+
+ RtlReleaseResource(&BrInfo.ConfigResource);
+
+ return(NERR_NotPrimary);
+ }
+
+
+ InitializeObjectAttributes(&ObjectAttributes, NULL, 0, NULL, NULL);
+
+ Status = LsaOpenPolicy(NULL, &ObjectAttributes,
+ POLICY_VIEW_LOCAL_INFORMATION,
+ &LsaHandle);
+
+ if (!NT_SUCCESS(Status)) {
+
+ RtlReleaseResource(&BrInfo.ConfigResource);
+
+ return(BrMapStatus(Status));
+ }
+
+ Status = LsaQueryInformationPolicy(LsaHandle,
+ PolicyLsaServerRoleInformation,
+ (PVOID)&ServerRole
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ LsaClose(LsaHandle);
+
+ RtlReleaseResource(&BrInfo.ConfigResource);
+
+ return(BrMapStatus(Status));
+ }
+
+ LsaClose(LsaHandle);
+
+ if (BrInfo.IsPrimaryDomainController) {
+ //
+ // We think we're the primary domain controller. If the
+ // LSA doesn't think we are the PDC, then update our information.
+ //
+
+ if (ServerRole->LsaServerRole != PolicyServerRolePrimary) {
+ BrInfo.IsPrimaryDomainController = FALSE;
+
+ //
+ // We're not a domain master any more, since we're not the PDC.
+ //
+
+ BrInfo.IsDomainMasterBrowser = FALSE;
+
+ }
+
+ } else {
+
+ //
+ // We don't think we're the primary domain controller. If the
+ // LSA thinks we are the PDC, then update our information.
+ //
+
+ if (ServerRole->LsaServerRole == PolicyServerRolePrimary) {
+
+ BrInfo.IsPrimaryDomainController = TRUE;
+
+ BrInfo.IsDomainMasterBrowser = TRUE;
+
+ //
+ // Make sure a GetMasterAnnouncement request is pending.
+ //
+
+ Status = BrPostGetMasterAnnouncementInWorker();
+
+ }
+ }
+
+ RtlReleaseResource(&BrInfo.ConfigResource);
+
+ //
+ // Update this information for all transports now. This will also update
+ // the status for the driver.
+ //
+
+ Status = BrUpdateAnnouncementBits(BrGlobalData.StatusHandle);
+
+ if (Status == NERR_Success) {
+ //
+ // The update worked. Now force an election and let the best server
+ // win.
+ //
+
+ BrForceElectionOnAllNetworks(EVENT_BROWSER_ELECTION_SENT_ROLE_CHANGED);
+ }
+
+ return Status;
+
+}
+
+ NET_API_STATUS NET_API_FUNCTION
+I_BrowserrSetNetlogonState(
+ IN BROWSER_IDENTIFY_HANDLE ServerName,
+ IN LPWSTR DomainName,
+ IN LPWSTR EmulatedComputerName,
+ IN DWORD Role
+ )
+
+/*++
+
+Routine Description:
+
+ This routine will reset the bowser's concept of the state of the netlogon
+ service. It is called by the Netlogon service when it promotes or demotes a DC.
+
+Arguments:
+
+ ServerName - Ignored.
+
+ DomainName - Name of the domain whose role has changed. If the domain name specified
+ isn't the primary domain or an emulated domain, an emulated domain is added.
+
+ EmulatedComputerName - Name of the server within DomainName that's being emulated.
+
+ Role - New role of the machine
+
+Return Value:
+
+ NET_API_STATUS - The status of this request.
+
+--*/
+
+{
+ //
+ // This routine has been superceeded by I_BrowserrSetNetlogonState
+ //
+ return ERROR_NOT_SUPPORTED;
+
+ UNREFERENCED_PARAMETER( ServerName );
+ UNREFERENCED_PARAMETER( DomainName );
+ UNREFERENCED_PARAMETER( EmulatedComputerName );
+ UNREFERENCED_PARAMETER( Role );
+}
+
+ NET_API_STATUS NET_API_FUNCTION
+I_BrowserrQueryEmulatedDomains (
+ IN LPTSTR ServerName OPTIONAL,
+ IN OUT PBROWSER_EMULATED_DOMAIN_CONTAINER EmulatedDomains
+ )
+
+/*++
+
+Routine Description:
+
+ Enumerate the emulated domain list.
+
+Arguments:
+
+ ServerName - Supplies the name of server to execute this function
+
+ EmulatedDomains - Returns a pointer to a an allocated array of emulated domain
+ information.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ return ERROR_NOT_SUPPORTED;
+}
diff --git a/private/net/svcdlls/browser/server/brmaster.h b/private/net/svcdlls/browser/server/brmaster.h
new file mode 100644
index 000000000..3edc501c7
--- /dev/null
+++ b/private/net/svcdlls/browser/server/brmaster.h
@@ -0,0 +1,137 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ brmaster.h
+
+Abstract:
+
+ Private header file which defines the global data which is used for
+ communication between the service control handler and the
+ rest of the NT Workstation service.
+
+Author:
+
+ Rita Wong (ritaw) 06-May-1991
+
+Revision History:
+
+--*/
+
+#ifndef _BRMASTER_INCLUDED_
+#define _BRMASTER_INCLUDED_
+
+ NET_API_STATUS
+BrPostBecomeMaster(
+ VOID
+ );
+
+NET_API_STATUS
+BrPostGetMasterAnnouncementInWorker(
+ VOID
+ );
+
+NET_API_STATUS
+BrPostGetMasterAnnouncement (
+ VOID
+ );
+
+NET_API_STATUS
+PostGetMasterAnnouncement (
+ PNETWORK Network,
+ PVOID Ctx
+ );
+
+NET_API_STATUS
+BrStopMaster(
+ IN PNETWORK Network
+ );
+
+NET_API_STATUS
+PostBecomeMaster(
+ PNETWORK Network,
+ PVOID Ctx
+ );
+
+VOID
+BrGetMasterServerNamesAysnc(
+ VOID
+ );
+
+ VOID
+BrGetMasterServerNamesOnAllNets(
+ IN PVOID Context
+ );
+
+NET_API_STATUS
+GetMasterServerNames(
+ IN PNETWORK Network
+ );
+
+VOID
+BrMasterAnnouncement(
+ IN PVOID Context
+ );
+
+VOID
+MasterBrowserTimerRoutine (
+ IN PVOID TimerContext
+ );
+
+VOID
+BrChangeMasterPeriodicity (
+ VOID
+ );
+
+VOID
+BrBrowseTableInsertRoutine(
+ IN PINTERIM_SERVER_LIST InterimTable,
+ IN PINTERIM_ELEMENT InterimElement
+ );
+
+VOID
+BrBrowseTableDeleteRoutine(
+ IN PINTERIM_SERVER_LIST InterimTable,
+ IN PINTERIM_ELEMENT InterimElement
+ );
+
+VOID
+BrBrowseTableUpdateRoutine(
+ IN PINTERIM_SERVER_LIST InterimTable,
+ IN PINTERIM_ELEMENT InterimElement
+ );
+
+BOOLEAN
+BrBrowseTableAgeRoutine(
+ IN PINTERIM_SERVER_LIST InterimTable,
+ IN PINTERIM_ELEMENT InterimElement
+ );
+
+VOID
+BrDomainTableInsertRoutine(
+ IN PINTERIM_SERVER_LIST InterimTable,
+ IN PINTERIM_ELEMENT InterimElement
+ );
+
+VOID
+BrDomainTableDeleteRoutine(
+ IN PINTERIM_SERVER_LIST InterimTable,
+ IN PINTERIM_ELEMENT InterimElement
+ );
+
+VOID
+BrDomainTableUpdateRoutine(
+ IN PINTERIM_SERVER_LIST InterimTable,
+ IN PINTERIM_ELEMENT InterimElement
+ );
+
+BOOLEAN
+BrDomainTableAgeRoutine(
+ IN PINTERIM_SERVER_LIST InterimTable,
+ IN PINTERIM_ELEMENT InterimElement
+ );
+
+#endif // ifndef _BRBACKUP_INCLUDED_
+
diff --git a/private/net/svcdlls/browser/server/browsdom.c b/private/net/svcdlls/browser/server/browsdom.c
new file mode 100644
index 000000000..55bc767d9
--- /dev/null
+++ b/private/net/svcdlls/browser/server/browsdom.c
@@ -0,0 +1,32 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ browslst.c
+
+Abstract:
+
+ This module contains the worker routines for managing domain lists
+ for the browser service
+
+Author:
+
+ Larry Osterman (larryo) 25-Mar-1992
+
+Revision History:
+
+--*/
+
+#include "precomp.h"
+#pragma hdrstop
+
+//-------------------------------------------------------------------//
+// //
+// Local function prototypes //
+// //
+//-------------------------------------------------------------------//
+
+
+
diff --git a/private/net/svcdlls/browser/server/browsdom.h b/private/net/svcdlls/browser/server/browsdom.h
new file mode 100644
index 000000000..b3df93f43
--- /dev/null
+++ b/private/net/svcdlls/browser/server/browsdom.h
@@ -0,0 +1,46 @@
+
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ browsdom.h
+
+Abstract:
+
+ Private header file to be included by Browser service modules that
+ need to deal with the browser list.
+
+Author:
+
+ Larry Osterman (larryo) 3-Mar-1992
+
+Revision History:
+
+--*/
+
+
+#ifndef _BROWSDOM_INCLUDED_
+#define _BROWSDOM_INCLUDED_
+
+RTL_GENERIC_COMPARE_RESULTS
+BrCompareDomainListEntry(
+ PRTL_GENERIC_TABLE Table,
+ PVOID FirstStruct,
+ PVOID SecondStruct
+ );
+
+ PVOID
+BrAllocateDomainListEntry(
+ PRTL_GENERIC_TABLE Table,
+ CLONG ByteSize
+ );
+
+ PVOID
+BrFreeDomainListEntry(
+ PRTL_GENERIC_TABLE Table,
+ CLONG ByteSize
+ );
+
+#endif // _BROWSDOM_INCLUDED_
diff --git a/private/net/svcdlls/browser/server/browser.c b/private/net/svcdlls/browser/server/browser.c
new file mode 100644
index 000000000..eeeb4c54c
--- /dev/null
+++ b/private/net/svcdlls/browser/server/browser.c
@@ -0,0 +1,1591 @@
+/*++
+
+Copyright (c) 1991-1992 Microsoft Corporation
+
+Module Name:
+
+ browser.c
+
+Abstract:
+
+ This module contains the worker routines for the NetWksta APIs
+ implemented in the Workstation service.
+
+Author:
+
+ Rita Wong (ritaw) 20-Feb-1991
+
+Revision History:
+
+--*/
+
+#include "precomp.h"
+#pragma hdrstop
+
+
+//-------------------------------------------------------------------//
+// //
+// Local structure definitions //
+// //
+//-------------------------------------------------------------------//
+
+//-------------------------------------------------------------------//
+// //
+// Local function prototypes //
+// //
+//-------------------------------------------------------------------//
+
+
+NET_API_STATUS
+BecomeFullBackup(
+ IN PNETWORK Network,
+ IN PVOID Context
+ );
+
+VOID
+CompleteAsyncBrowserIoControl(
+ IN PVOID ApcContext,
+ IN PIO_STATUS_BLOCK IoStatusBlock,
+ IN ULONG Reserved
+ );
+
+NET_API_STATUS
+PostBecomeBackup(
+ PNETWORK Network,
+ PVOID Ctx
+ );
+
+VOID
+BecomeBackupCompletion (
+ IN PVOID Ctx
+ );
+
+
+VOID
+ChangeBrowserRole (
+ IN PVOID Ctx
+ );
+
+NET_API_STATUS
+PostWaitForRoleChange (
+ PNETWORK Network,
+ PVOID Ctx
+ );
+
+NET_API_STATUS
+PostWaitForNewMasterName(
+ PNETWORK Network,
+ PUNICODE_STRING MasterName
+ );
+
+VOID
+NewMasterCompletionRoutine(
+ IN PVOID Ctx
+ );
+
+NET_API_STATUS
+BrRetrieveInterimServerList(
+ IN PNETWORK Network,
+ IN ULONG ServerType
+ );
+
+//-------------------------------------------------------------------//
+// //
+// Global function prototypes //
+// //
+//-------------------------------------------------------------------//
+
+ NET_API_STATUS
+BrBecomeBackup(
+ VOID
+ )
+{
+ return BrEnumerateNetworks(BecomeFullBackup, NULL);
+}
+
+ NET_API_STATUS
+BecomeFullBackup(
+ IN PNETWORK Network,
+ IN PVOID Context
+ )
+/*++
+
+Routine Description:
+
+ This function performs all the operations needed to make a browser server
+ a backup browser server when starting the browser from scratch.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ Status - The status of the operation.
+
+--*/
+{
+ NET_API_STATUS Status;
+
+ //
+ // Checkpoint the service controller - this gives us 30 seconds/transport
+ // before the service controller gets unhappy.
+ //
+
+ (BrGlobalData.Status.dwCheckPoint)++;
+ (void) BrUpdateStatus();
+
+ if (!LOCK_NETWORK(Network)) {
+ return NERR_InternalError;
+ }
+
+ //
+ // Were starting from "net start browser". We need to push in potential
+ // browser before calling BecomeBackup to make the browser add the election
+ // name.
+ //
+
+ Status = BrUpdateBrowserStatus(Network, SV_TYPE_POTENTIAL_BROWSER);
+
+ if (Status != NERR_Success) {
+ KdPrint(("Unable to update browser status\n"));
+ UNLOCK_NETWORK(Network);
+ return Status;
+ }
+
+ //
+ // We want to ignore any failures from becoming a backup browser.
+ //
+ // We do this because we will fail to become a backup on a disconnected
+ // (or connected) RAS link, and if we failed this routine, we would
+ // fail to start at all.
+ //
+ // We will handle failure to become a backup in a "reasonable manner"
+ // inside BecomeBackup.
+ //
+
+ BecomeBackup(Network, Context);
+
+ UNLOCK_NETWORK(Network);
+
+ return NERR_Success;
+
+}
+
+ NET_API_STATUS
+BecomeBackup(
+ IN PNETWORK Network,
+ IN PVOID Context
+ )
+/*++
+
+Routine Description:
+
+ This function performs all the operations needed to make a browser server
+ a backup browser server
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ Status - The status of the operation.
+
+
+NOTE:::: THIS ROUTINE IS CALLED WITH THE NETWORK STRUCTURE LOCKED!!!!!
+
+
+--*/
+{
+ NET_API_STATUS Status = NERR_Success;
+ PUNICODE_STRING MasterName = Context;
+
+ if (Network->TimeStoppedBackup != 0 &&
+ (BrCurrentSystemTime() - Network->TimeStoppedBackup) <= (BrInfo.BackupBrowserRecoveryTime / 1000)) {
+
+ //
+ // We stopped being a backup too recently for us to restart being
+ // a backup again, so just return a generic error.
+ //
+
+ //
+ // Before we return, make sure we're not a backup in the eyes of
+ // the browser.
+ //
+
+ BrStopBackup(Network);
+
+ return ERROR_ACCESS_DENIED;
+
+ }
+
+ //
+ // If we know the name of the master, then we must have become a backup
+ // after being a potential, in which case we already have a
+ // becomemaster request outstanding.
+ //
+
+ if (MasterName == NULL) {
+
+ //
+ // Post a BecomeMaster request for each server. This will complete
+ // when the machine becomes the master browser server (ie. it wins an
+ // election).
+ //
+
+ //
+ // Please note that we only post it if the machine is a backup -
+ // if it's a potential master, then the become master will have
+ // already been posted.
+ //
+
+ Status = PostBecomeMaster(Network, NULL);
+
+ if (Status != NERR_Success) {
+
+ return(Status);
+ }
+
+ //
+ // Find out the name of the master on each network. This will force an
+ // election if necessary. Please note that we must post the BecomeMaster
+ // IoControl first to allow us to handle an election.
+ //
+
+ //
+ // We unlock the network, because this may cause us to become promoted
+ // to a master.
+ //
+
+ dprintf(BACKUP, ("FindMaster called from BecomeBackup on %ws\n", Network->NetworkName.Buffer));
+
+ UNLOCK_NETWORK(Network);
+
+ Status = GetMasterServerNames(Network);
+
+ if (Status != NERR_Success) {
+
+ //
+ // Re-lock the network structure so we will return with the
+ // network locked.
+ //
+
+ if (!LOCK_NETWORK(Network)) {
+ return NERR_InternalError;
+ }
+
+ //
+ // We couldn't find who the master is. Stop being a backup now.
+ //
+
+ BrStopBackup(Network);
+
+ //
+ // If we're a master now, we should return success. We've not
+ // become a backup, but it wasn't an error.
+ //
+ // ERROR_MORE_DATA is the mapping for
+ // STATUS_MORE_PROCESSING_REQUIRED which is returned when this
+ // situation happens.
+ //
+
+ if ((Status == ERROR_MORE_DATA) || (Network->Role & ROLE_MASTER)) {
+ Status = NERR_Success;
+ }
+
+ return(Status);
+ }
+
+ if (!LOCK_NETWORK(Network)) {
+ return NERR_InternalError;
+ }
+
+ //
+ // We managed to become a master. We want to return right away.
+ //
+
+ if (Network->Role & ROLE_MASTER) {
+
+ return NERR_Success;
+ }
+
+ }
+
+#ifdef notdef
+ //
+ // ?? For now, we'll always PostForRoleChange on all transports regardless
+ // of role.
+ // We not only need to do it here. But we need to do it when we become
+ // the master so we can find out when we loose an election.
+ //
+
+
+ //
+ // We're now a backup, we need to issue an API that will complete if the
+ // browse master doesn't like us (and thus forces us to shutdown).
+ //
+ //
+
+ PostWaitForRoleChange ( Network, NULL );
+#endif // notdef
+
+ PostWaitForNewMasterName(Network, &Network->MasterBrowserName );
+
+ //
+ // Unlock the network structure before calling BackupBrowserTimerRoutine.
+ //
+
+ UNLOCK_NETWORK(Network);
+
+ //
+ // Run the timer that causes the browser to download a new browse list
+ // from the master. This will seed our server and domain lists to
+ // guarantee that any clients have a reasonable list. It will also
+ // restart the timer to announce later on.
+ //
+
+ Status = BackupBrowserTimerRoutine(Network);
+
+ if (!LOCK_NETWORK(Network)) {
+ return NERR_InternalError;
+ }
+
+ if (Status == NERR_Success) {
+
+ ASSERT (Network->Role & ROLE_BACKUP);
+
+ //
+ // We're now a backup server, announce ourselves as such.
+ //
+
+ Status = BrUpdateNetworkAnnouncementBits(Network, NULL);
+
+ if (Status != NERR_Success) {
+
+ dprintf(BACKUP, ("Unable to become backup: %ld\n", Status));
+
+ if (Network->Role & ROLE_BACKUP) {
+
+ //
+ // Make sure that we're going to become a potential browser
+ // (we might not if we're an advanced server).
+ //
+
+ Network->Role |= ROLE_POTENTIAL_BACKUP;
+
+ //
+ // We were unable to become a backup.
+ //
+ // We need to back out and become a potential browser now.
+ //
+ //
+
+ BrStopBackup(Network);
+
+ PostBecomeBackup(Network, NULL);
+
+ }
+ }
+
+ return Status;
+
+ }
+
+ return Status;
+}
+
+
+ NET_API_STATUS
+BrBecomePotentialBrowser (
+ IN PVOID TimerContext
+ )
+/*++
+
+Routine Description:
+
+ This routine is called when a machine has stopped being a backup browser.
+
+ It runs after a reasonable timeout period has elapsed, and marks the
+ machine as a potential browser.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ Status - The status of the operation.
+
+
+--*/
+
+{
+ IN PNETWORK Network = TimerContext;
+ NET_API_STATUS Status;
+
+ //
+ // Mark this guy as a potential browser.
+ //
+
+ if (!LOCK_NETWORK(Network)) {
+ return NERR_InternalError;
+ }
+
+ try {
+
+ //
+ // Reset that we've stopped being a backup, since it's been long
+ // enough.
+ //
+
+ Network->TimeStoppedBackup = 0;
+
+ if (BrInfo.MaintainServerList == 0) {
+ Network->Role |= ROLE_POTENTIAL_BACKUP;
+
+ Status = BrUpdateNetworkAnnouncementBits(Network, NULL);
+
+ if (Status != NERR_Success) {
+ dprintf(BACKUP, ("Unable to reset backup announcement bits: %ld\n", Status));
+ try_return(Status);
+ }
+ } else {
+
+ //
+ // If we're configured to be a backup browser, then we want to
+ // become a backup once again.
+ //
+
+ BecomeBackup(Network, NULL);
+ }
+
+
+try_exit:NOTHING;
+ } finally {
+ UNLOCK_NETWORK(Network);
+ }
+
+ return Status;
+}
+
+ NET_API_STATUS
+BrStopBackup (
+ IN PNETWORK Network
+ )
+/*++
+
+Routine Description:
+
+ This routine is called to stop a machine from being a backup browser.
+
+ It is typically called after some form of error has occurred while
+ running as a browser to make sure that we aren't telling anyone that
+ we're a backup browser.
+
+ We are also called when we receive a "reset state" tickle packet.
+
+Arguments:
+
+ Network - The network being shut down.
+
+Return Value:
+
+ Status - The status of the operation.
+
+Note:
+ This routine must be careful about the ROLE_POTENTIAL bit. If the
+ net does not have the potential bit set, it is possible that we might
+ delete the election name in BrUpdateNetworkAnnouncementBits, so we
+ do the work "by hand".
+
+
+--*/
+{
+ NET_API_STATUS Status;
+
+ //
+ // This guy is shutting down - set his role to 0 and announce.
+ //
+
+ if (!LOCK_NETWORK(Network)) {
+ return NERR_InternalError;
+ }
+
+ try {
+
+ dprintf(BACKUP, ("StopBackup on %ws\n", Network->NetworkName.Buffer));
+
+ Network->Role &= ~ROLE_BACKUP;
+
+ Status = BrUpdateBrowserStatus(Network, SV_TYPE_POTENTIAL_BROWSER);
+
+ if (Status != NERR_Success) {
+ dprintf(BACKUP, ("Unable to clear backup announcement bits: %ld\n", Status));
+ try_return(Status);
+ }
+
+ //
+ // Clear ALL the browser bits in the server - we aren't even a
+ // potential browser to the server anymore.
+ //
+
+ Status = I_NetServerSetServiceBits(NULL, Network->NetworkName.Buffer, 0, TRUE);
+
+ if (Status != NERR_Success) {
+ BrLogEvent(EVENT_BROWSER_STATUS_BITS_UPDATE_FAILED, Status, 0, NULL);
+ dprintf(BACKUP, ("Unable to update server status: %ld\n", Status));
+ try_return(Status);
+ }
+
+ Status = BrCancelTimer(&Network->BackupBrowserTimer);
+
+ if (Status != NERR_Success) {
+ dprintf(BACKUP, ("Unable to clear backup browser timer: %ld\n", Status));
+ try_return(Status);
+ }
+
+ if (Network->BackupDomainList != NULL) {
+
+ NetApiBufferFree(Network->BackupDomainList);
+
+ Network->BackupDomainList = NULL;
+
+ Network->TotalBackupDomainListEntries = 0;
+ }
+
+ if (Network->BackupServerList != NULL) {
+ NetApiBufferFree(Network->BackupServerList);
+
+ Network->BackupServerList = NULL;
+
+ Network->TotalBackupServerListEntries = 0;
+ }
+
+ BrDestroyResponseCache(Network);
+
+ //
+ // After our recovery time, we can become a potential browser again.
+ //
+
+ Status = BrSetTimer(&Network->BackupBrowserTimer, BrInfo.BackupBrowserRecoveryTime, BrBecomePotentialBrowser, Network);
+
+ if (Status != NERR_Success) {
+ dprintf(BACKUP, ("Unable to clear backup browser timer: %ld\n", Status));
+ try_return(Status);
+ }
+
+
+try_exit:NOTHING;
+ } finally {
+ //
+ // Remember when we were asked to stop being a backup browser.
+ //
+
+ Network->TimeStoppedBackup = BrCurrentSystemTime();
+
+ UNLOCK_NETWORK(Network);
+ }
+
+ return Status;
+
+}
+
+
+ NET_API_STATUS
+BackupBrowserTimerRoutine (
+ IN PVOID TimerContext
+ )
+{
+ IN PNETWORK Network = TimerContext;
+ NET_API_STATUS Status;
+ PVOID ServerList = NULL;
+ BOOLEAN NetworkLocked = FALSE;
+
+ dprintf(BACKUP, ("BackupBrowserTimerRoutine %ws\n", Network->NetworkName.Buffer));
+
+ if (!LOCK_NETWORK(Network)) {
+ return NERR_InternalError;
+ }
+
+ NetworkLocked = TRUE;
+
+ ASSERT (Network->LockCount == 1);
+
+ ASSERT ( NetpIsUncComputerNameValid( Network->MasterBrowserName.Buffer ) );
+
+ try {
+
+ //
+ // Make sure there's a become master oustanding.
+ //
+
+ PostBecomeMaster(Network, NULL);
+
+ //
+ // We managed to become a master by the time we locked the structure.
+ // We want to return right away.
+ //
+
+ if (Network->Role & ROLE_MASTER) {
+ try_return(Status = NERR_Success);
+ }
+
+ Status = BrRetrieveInterimServerList(Network, SV_TYPE_ALL);
+
+ //
+ // Bail out if we didn't get any new servers.
+ //
+
+ if (Status != NERR_Success && Status != ERROR_MORE_DATA) {
+
+ //
+ // Try again after an appropriate error delay.
+ //
+
+ try_return(Status);
+ }
+
+ //
+ // Now do everything that we did above for the server list for the
+ // list of domains.
+ //
+
+ Status = BrRetrieveInterimServerList(Network, SV_TYPE_DOMAIN_ENUM);
+
+ //
+ // We successfully updated the server and domain lists for this
+ // server. Now age all the cached domain entries out of the cache.
+ //
+
+ if (Status == NERR_Success || Status == ERROR_MORE_DATA) {
+ BrAgeResponseCache(Network);
+ }
+
+ try_return(Status);
+
+try_exit:NOTHING;
+ } finally {
+ NET_API_STATUS NetStatus;
+
+ if (!NetworkLocked) {
+ if (!LOCK_NETWORK(Network)) {
+ return NERR_InternalError;
+ }
+
+ NetworkLocked = TRUE;
+ }
+
+ //
+ // If the API succeeded, Mark that we're a backup and
+ // reset the timer.
+ //
+
+ if (Status == NERR_Success || Status == ERROR_MORE_DATA ) {
+
+ if ((Network->Role & ROLE_BACKUP) == 0) {
+
+ //
+ // If we weren't a backup, we are one now.
+ //
+
+ Network->Role |= ROLE_BACKUP;
+
+ Status = BrUpdateNetworkAnnouncementBits(Network, NULL);
+
+ }
+
+ Network->NumberOfFailedBackupTimers = 0;
+
+ Network->TimeStoppedBackup = 0;
+
+ //
+ // Restart the timer for this domain.
+ //
+
+ NetStatus = BrSetTimer(&Network->BackupBrowserTimer, BrInfo.BackupPeriodicity*1000, BackupBrowserTimerRoutine, Network);
+
+ if (NetStatus != NERR_Success) {
+ InternalError(("Browser: Unable to restart browser backup timer: %lx\n", Status));
+ }
+
+ } else {
+
+ //
+ // We failed to retrieve a backup list, remember the failure and
+ // decide if it's been too many failures. If not, just log
+ // the error, if it has, stop being a backup browser.
+ //
+
+ Network->NumberOfFailedBackupTimers += 1;
+
+ if (Network->NumberOfFailedBackupTimers >= BACKUP_ERROR_FAILURE) {
+ LPWSTR SubStrings[1];
+
+ SubStrings[0] = Network->NetworkName.Buffer;
+
+ //
+ // This guy can't be a backup any more, bail out now.
+ //
+
+ BrLogEvent(EVENT_BROWSER_BACKUP_STOPPED, Status, 1, SubStrings);
+
+ BrStopBackup(Network);
+ } else {
+ //
+ // Restart the timer for this domain.
+ //
+
+ NetStatus = BrSetTimer(&Network->BackupBrowserTimer, BACKUP_ERROR_PERIODICITY*1000, BackupBrowserTimerRoutine, Network);
+
+ if (NetStatus != NERR_Success) {
+ InternalError(("Browser: Unable to restart browser backup timer: %lx\n", Status));
+ }
+
+ }
+
+ }
+
+ if (NetworkLocked) {
+ UNLOCK_NETWORK(Network);
+ }
+ }
+
+ return Status;
+
+}
+
+ NET_API_STATUS
+BrRetrieveInterimServerList(
+ IN PNETWORK Network,
+ IN ULONG ServerType
+ )
+{
+ ULONG EntriesInList;
+ ULONG TotalEntriesInList;
+ ULONG RetryCount = 2;
+ TCHAR ServerName[UNCLEN+1];
+ LPTSTR TransportName;
+ BOOLEAN NetworkLocked = TRUE;
+ NET_API_STATUS Status;
+ PVOID Buffer = NULL;
+ ULONG ModifiedServerType = ServerType;
+ LPTSTR ModifiedTransportName;
+
+ ASSERT (Network->LockCount == 1);
+
+ STRCPY(ServerName, Network->MasterBrowserName.Buffer);
+
+ dprintf(BACKUP, ("BrRetrieveInterimServerList: transport %ws UNC servername is %ws\n",
+ Network->NetworkName.Buffer, ServerName));
+
+ try {
+
+ TransportName = Network->NetworkName.Buffer;
+ ModifiedTransportName = TransportName;
+ //
+ // If this is direct host IPX,
+ // we remote the API over the Netbios IPX transport since
+ // the NT redir doesn't support direct host IPX.
+ //
+
+ if ( (Network->Flags & NETWORK_IPX) &&
+ Network->AlternateNetwork != NULL) {
+
+ //
+ // Use the alternate transport
+ //
+
+ ModifiedTransportName = Network->AlternateNetwork->NetworkName.Buffer;
+
+ //
+ // Tell the server to use it's alternate transport.
+ //
+
+ if ( ServerType == SV_TYPE_ALL ) {
+ ModifiedServerType = SV_TYPE_ALTERNATE_XPORT;
+ } else {
+ ModifiedServerType |= SV_TYPE_ALTERNATE_XPORT;
+ }
+
+ }
+
+ while (RetryCount--) {
+
+ //
+ // If we are promoted to master and fail to become the master,
+ // we will still be marked as being the master in our network
+ // structure, thus we should bail out of the loop in order
+ // to prevent us from looping back on ourselves.
+ //
+
+ if (STRICMP(&ServerName[2], BrInfo.BrComputerName) == 0) {
+
+ if (NetworkLocked) {
+ UNLOCK_NETWORK(Network);
+
+ NetworkLocked = FALSE;
+
+ }
+
+ //
+ // We were unable to find the master. Attempt to find out who
+ // the master is. If there is none, this will force an
+ // election.
+ //
+
+ dprintf(BACKUP, ("FindMaster called from BrRetrieveInterimServerList on %ws\n", Network->NetworkName.Buffer));
+
+ Status = GetMasterServerNames(Network);
+
+ if (Status != NERR_Success) {
+ try_return(Status);
+ }
+
+ ASSERT (!NetworkLocked);
+
+ if (!LOCK_NETWORK(Network)) {
+ try_return(Status = NERR_InternalError);
+ }
+
+ NetworkLocked = TRUE;
+
+ break;
+ }
+
+ //
+ // If we somehow became the master, we don't want to try to
+ // retrieve the list from ourselves either.
+ //
+
+ if (Network->Role & ROLE_MASTER) {
+ try_return(Status = NERR_Success);
+ }
+
+ ASSERT (Network->LockCount == 1);
+
+ if (NetworkLocked) {
+ UNLOCK_NETWORK(Network);
+
+ NetworkLocked = FALSE;
+
+ }
+
+ EntriesInList = 0;
+
+ Status = RxNetServerEnum(ServerName, // Server name
+ ModifiedTransportName, // Transport name
+ 101, // Level
+ (LPBYTE *)&Buffer, // Buffer
+ 0xffffffff, // Prefered Max Length
+ &EntriesInList, // EntriesRead
+ &TotalEntriesInList, // TotalEntries
+ ModifiedServerType, // Server type
+ NULL, // Domain (use default)
+ NULL // Resume key
+ );
+
+ if (Status != NERR_Success && Status != ERROR_MORE_DATA) {
+ LPWSTR SubStrings[2];
+
+ SubStrings[0] = ServerName;
+ SubStrings[1] = TransportName;
+
+ BrLogEvent((ServerType == SV_TYPE_DOMAIN_ENUM ?
+ EVENT_BROWSER_DOMAIN_LIST_FAILED :
+ EVENT_BROWSER_SERVER_LIST_FAILED),
+ Status,
+ 2,
+ SubStrings);
+
+ dprintf(BACKUP, ("Failed to retrieve %s list on %ws from server %ws: %ld\n", (ServerType == SV_TYPE_ALL ? "server" : "domain"), ServerName, TransportName, Status));
+ } else {
+ dprintf(BACKUP, ("Retrieved %s list on %ws from server %ws: E:%ld, T:%ld\n", (ServerType == SV_TYPE_ALL ? "server" : "domain"), ServerName, TransportName, EntriesInList, TotalEntriesInList));
+ }
+
+ //
+ // If we succeeded in retrieving the list, but we only got
+ // a really small number of either servers or domains,
+ // we want to turn this into a failure.
+ //
+
+ if (Status == NERR_Success) {
+ if (((ServerType == SV_TYPE_DOMAIN_ENUM) &&
+ (EntriesInList < BROWSER_MINIMUM_DOMAIN_NUMBER)) ||
+ ((ServerType == SV_TYPE_ALL) &&
+ (EntriesInList < BROWSER_MINIMUM_SERVER_NUMBER))) {
+
+ Status = ERROR_INSUFFICIENT_BUFFER;
+ }
+ }
+
+ if ((Status == NERR_Success) || (Status == ERROR_MORE_DATA)) {
+
+ ASSERT (!NetworkLocked);
+
+ if (!LOCK_NETWORK(Network)) {
+ Status = NERR_InternalError;
+ break;
+ }
+
+ NetworkLocked = TRUE;
+
+ ASSERT (Network->LockCount == 1);
+
+#if DBG
+ BrUpdateDebugInformation((ServerType == SV_TYPE_DOMAIN_ENUM ?
+ L"LastDomainListRead" :
+ L"LastServerListRead"),
+ L"BrowserServerName",
+ TransportName,
+ ServerName,
+ 0);
+#endif
+
+ //
+ // We've retrieved a new list from the browse master, save
+ // the new list away in the "appropriate" spot.
+ //
+
+ //
+ // Of course, we free up the old buffer before we do this..
+ //
+
+ if (ServerType == SV_TYPE_DOMAIN_ENUM) {
+ if (Network->BackupDomainList != NULL) {
+ NetApiBufferFree(Network->BackupDomainList);
+ }
+
+ Network->BackupDomainList = Buffer;
+
+ Network->TotalBackupDomainListEntries = EntriesInList;
+ } else {
+ if (Network->BackupServerList != NULL) {
+ NetApiBufferFree(Network->BackupServerList);
+ }
+
+ Network->BackupServerList = Buffer;
+
+ Network->TotalBackupServerListEntries = EntriesInList;
+ }
+
+ break;
+ } else {
+ NET_API_STATUS GetMasterNameStatus;
+
+ if ((EntriesInList != 0) && (Buffer != NULL)) {
+ NetApiBufferFree(Buffer);
+ Buffer = NULL;
+ }
+
+ dprintf(BACKUP, ("Unable to contact browser server %ws on transport %ws: %lx\n", ServerName, TransportName, Status));
+
+ if (NetworkLocked) {
+
+ //
+ // We were unable to find the master. Attempt to find out who
+ // the master is. If there is none, this will force an
+ // election.
+ //
+
+ ASSERT (Network->LockCount == 1);
+
+ UNLOCK_NETWORK(Network);
+
+ NetworkLocked = FALSE;
+ }
+
+ dprintf(BACKUP, ("FindMaster called from BrRetrieveInterimServerList on %ws for failure\n", Network->NetworkName.Buffer));
+
+ GetMasterNameStatus = GetMasterServerNames(Network);
+
+ //
+ // We were able to find out who the master is.
+ //
+ // Retry and retrieve the server/domain list.
+ //
+
+ if (GetMasterNameStatus == NERR_Success) {
+
+ ASSERT (!NetworkLocked);
+
+ if (!LOCK_NETWORK(Network)) {
+ try_return(Status = NERR_InternalError);
+ }
+
+ NetworkLocked = TRUE;
+
+ ASSERT (Network->LockCount == 1);
+
+ //
+ // We managed to become a master. We want to return right away.
+ //
+
+ if (Network->Role & ROLE_MASTER) {
+
+ try_return(Status = NERR_InternalError);
+ }
+
+ STRCPY (ServerName, Network->MasterBrowserName.Buffer);
+
+ ASSERT ( NetpIsUncComputerNameValid( ServerName ) );
+
+ ASSERT (STRICMP(&ServerName[2], BrInfo.BrComputerName) != 0);
+
+ dprintf(BACKUP, ("New master name for transport %ws is %ws\n", TransportName, ServerName));
+
+ } else {
+ try_return(Status);
+ }
+ }
+ }
+try_exit:NOTHING;
+ } finally {
+ if (!NetworkLocked) {
+ if (!LOCK_NETWORK(Network)) {
+ Status = NERR_InternalError;
+ }
+
+ ASSERT (Network->LockCount == 1);
+
+ }
+ }
+
+ return Status;
+}
+
+
+ NET_API_STATUS
+BrPostBecomeBackup(
+ VOID
+ )
+{
+ return BrEnumerateNetworks(PostBecomeBackup, NULL);
+}
+
+
+ NET_API_STATUS
+PostBecomeBackup(
+ PNETWORK Network,
+ PVOID Ctx
+ )
+/*++
+
+Routine Description:
+
+ This function is the worker routine called to actually issue a BecomeBackup
+ FsControl to the bowser driver on all the bound transports. It will
+ complete when the machine becomes a backup browser server.
+
+ Please note that this might never complete.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ Status - The status of the operation.
+
+--*/
+{
+ NET_API_STATUS Status;
+
+ if (!LOCK_NETWORK(Network)) {
+ return NERR_InternalError;
+ }
+
+ Network->Role |= ROLE_POTENTIAL_BACKUP;
+
+ Status = BrIssueAsyncBrowserIoControl(Network,
+ IOCTL_LMDR_BECOME_BACKUP,
+ BecomeBackupCompletion
+ );
+ UNLOCK_NETWORK(Network);
+
+ return Status;
+
+ UNREFERENCED_PARAMETER(Ctx);
+}
+
+VOID
+BecomeBackupCompletion (
+ IN PVOID Ctx
+ )
+{
+ NET_API_STATUS Status;
+ PBROWSERASYNCCONTEXT Context = Ctx;
+ PNETWORK Network = Context->Network;
+
+ if (NT_SUCCESS(Context->IoStatusBlock.Status)) {
+ PWSTR MasterName = NULL;
+
+ if (!LOCK_NETWORK(Network)) {
+ MIDL_user_free(Context->RequestPacket);
+
+ MIDL_user_free(Context);
+ return;
+ }
+
+ dprintf(BACKUP, ("BROWSER: Become backup completion. We are now a backup server\n"));
+
+ Status = BecomeBackup(Context->Network, NULL);
+
+ UNLOCK_NETWORK(Network);
+
+ }
+
+ MIDL_user_free(Context->RequestPacket);
+
+ MIDL_user_free(Context);
+
+}
+
+ VOID
+BrBrowseTableInsertRoutine(
+ IN PINTERIM_SERVER_LIST InterimTable,
+ IN PINTERIM_ELEMENT InterimElement
+ )
+{
+ //
+ // We need to miss 3 retrievals of the browse list for us to toss the
+ // server.
+ //
+
+ InterimElement->Periodicity = BrInfo.BackupPeriodicity * 3;
+
+ if (InterimElement->TimeLastSeen != 0xffffffff) {
+ InterimElement->TimeLastSeen = BrCurrentSystemTime();
+ }
+}
+
+ VOID
+BrBrowseTableDeleteRoutine(
+ IN PINTERIM_SERVER_LIST InterimTable,
+ IN PINTERIM_ELEMENT InterimElement
+ )
+{
+// KdPrint(("BROWSER: Deleting element for server %ws\n", InterimElement->Name));
+}
+
+ VOID
+BrBrowseTableUpdateRoutine(
+ IN PINTERIM_SERVER_LIST InterimTable,
+ IN PINTERIM_ELEMENT InterimElement
+ )
+{
+ if (InterimElement->TimeLastSeen != 0xffffffff) {
+ InterimElement->TimeLastSeen = BrCurrentSystemTime();
+ }
+}
+
+ BOOLEAN
+BrBrowseTableAgeRoutine(
+ IN PINTERIM_SERVER_LIST InterimTable,
+ IN PINTERIM_ELEMENT InterimElement
+ )
+/*++
+
+Routine Description:
+
+ This routine is called when we are scanning an interim server list trying
+ to age the elements in the list. It returns TRUE if the entry is too
+ old.
+
+Arguments:
+
+ PINTERIM_SERVER_LIST InterimTable - A pointer to the interim server list.
+ PINTERIM_ELEMENT InterimElement - A pointer to the element to check.
+
+Return Value:
+
+ TRUE if the element should be deleted.
+
+--*/
+
+{
+ if (InterimElement->TimeLastSeen == 0xffffffff) {
+ return FALSE;
+ }
+
+ if ((InterimElement->TimeLastSeen + InterimElement->Periodicity) < BrCurrentSystemTime()) {
+// KdPrint(("BROWSER: Aging out element for server %ws\n", InterimElement->Name));
+
+ return TRUE;
+ } else {
+ return FALSE;
+ }
+}
+
+ VOID
+BrDomainTableInsertRoutine(
+ IN PINTERIM_SERVER_LIST InterimTable,
+ IN PINTERIM_ELEMENT InterimElement
+ )
+{
+ InterimElement->Periodicity = BrInfo.BackupPeriodicity * 3;
+ InterimElement->TimeLastSeen = BrCurrentSystemTime();
+
+}
+
+ VOID
+BrDomainTableDeleteRoutine(
+ IN PINTERIM_SERVER_LIST InterimTable,
+ IN PINTERIM_ELEMENT InterimElement
+ )
+{
+// KdPrint(("BROWSER: Deleting element for domain %ws\n", InterimElement->Name));
+}
+
+ VOID
+BrDomainTableUpdateRoutine(
+ IN PINTERIM_SERVER_LIST InterimTable,
+ IN PINTERIM_ELEMENT InterimElement
+ )
+{
+ InterimElement->TimeLastSeen = BrCurrentSystemTime();
+}
+
+ BOOLEAN
+BrDomainTableAgeRoutine(
+ IN PINTERIM_SERVER_LIST InterimTable,
+ IN PINTERIM_ELEMENT InterimElement
+ )
+/*++
+
+Routine Description:
+
+ This routine is called when we are scanning an interim server list trying
+ to age the elements in the list. It returns TRUE if the entry is too
+ old.
+
+Arguments:
+
+ PINTERIM_SERVER_LIST InterimTable - A pointer to the interim server list.
+ PINTERIM_ELEMENT InterimElement - A pointer to the element to check.
+
+Return Value:
+
+ TRUE if the element should be deleted.
+
+--*/
+
+{
+ if ((InterimElement->TimeLastSeen + InterimElement->Periodicity) < BrCurrentSystemTime()) {
+// KdPrint(("BROWSER: Aging out element for domain %ws\n", InterimElement->Name));
+ return TRUE;
+ } else {
+ return FALSE;
+ }
+}
+
+ NET_API_STATUS
+BrPostWaitForRoleChange (
+ VOID
+ )
+{
+ return BrEnumerateNetworks(PostWaitForRoleChange, NULL);
+}
+
+
+ NET_API_STATUS
+PostWaitForRoleChange (
+ PNETWORK Network,
+ PVOID Ctx
+ )
+/*++
+
+Routine Description:
+
+ This function is the worker routine called to actually issue a WaitForRoleChange
+ FsControl to the bowser driver on all the bound transports. It will
+ complete when the machine becomes a backup browser server.
+
+ Please note that this might never complete.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ Status - The status of the operation.
+
+--*/
+{
+ NET_API_STATUS Status;
+
+ if (!LOCK_NETWORK(Network)) {
+ return NERR_InternalError;
+ }
+
+ Status = BrIssueAsyncBrowserIoControl(Network,
+ IOCTL_LMDR_CHANGE_ROLE,
+ ChangeBrowserRole
+ );
+ UNLOCK_NETWORK(Network);
+
+ return Status;
+
+ UNREFERENCED_PARAMETER(Ctx);
+}
+
+VOID
+ChangeBrowserRole (
+ IN PVOID Ctx
+ )
+{
+ PBROWSERASYNCCONTEXT Context = Ctx;
+ PNETWORK Network = Context->Network;
+
+ if (NT_SUCCESS(Context->IoStatusBlock.Status)) {
+ PWSTR MasterName = NULL;
+ PLMDR_REQUEST_PACKET Packet = Context->RequestPacket;
+
+ if (!LOCK_NETWORK(Network)) {
+ MIDL_user_free(Context->RequestPacket);
+
+ MIDL_user_free(Context);
+
+ return;
+ }
+
+ PostWaitForRoleChange(Network, NULL);
+
+ if (Packet->Parameters.ChangeRole.RoleModification & RESET_STATE_CLEAR_ALL) {
+ dprintf(MASTER, ("Reset state request to clear all on %ws\n", Network->NetworkName.Buffer));
+
+ if (Network->Role & ROLE_MASTER) {
+ BrStopMaster(Network);
+ }
+
+ //
+ // Stop being a backup as well.
+ //
+
+ BrStopBackup(Network);
+
+ }
+
+ if ((Network->Role & ROLE_MASTER) &&
+ (Packet->Parameters.ChangeRole.RoleModification & RESET_STATE_STOP_MASTER)) {
+
+ dprintf(MASTER, ("Reset state request to stop master on %ws\n", Network->NetworkName.Buffer));
+
+ BrStopMaster(Network);
+
+ //
+ // If we are configured to be a backup, then become a backup
+ // again.
+ //
+
+ if (BrInfo.MaintainServerList == 1) {
+ BecomeBackup(Network, NULL);
+ }
+ }
+
+ //
+ // Make sure there's a become master oustanding.
+ //
+
+ PostBecomeMaster(Network, NULL);
+
+ UNLOCK_NETWORK(Network);
+ }
+
+ MIDL_user_free(Context->RequestPacket);
+
+ MIDL_user_free(Context);
+
+}
+
+
+NET_API_STATUS
+PostWaitForNewMasterName(
+ PNETWORK Network,
+ PUNICODE_STRING MasterName OPTIONAL
+ )
+{
+ PLMDR_REQUEST_PACKET RequestPacket = NULL;
+ NTSTATUS NtStatus;
+ ULONG PacketSize;
+
+ PBROWSERASYNCCONTEXT Context = NULL;
+
+ PacketSize = sizeof(LMDR_REQUEST_PACKET) +
+ MAXIMUM_FILENAME_LENGTH * sizeof(WCHAR) +
+ Network->NetworkName.MaximumLength;
+
+ RequestPacket = MIDL_user_allocate(PacketSize);
+
+ if (RequestPacket == NULL) {
+ return(ERROR_NOT_ENOUGH_MEMORY);
+ }
+
+ Context = MIDL_user_allocate(sizeof(BROWSERASYNCCONTEXT));
+
+ if (Context == NULL) {
+
+ MIDL_user_free(RequestPacket);
+
+ return(ERROR_NOT_ENOUGH_MEMORY);
+
+ }
+
+ RequestPacket->Version = LMDR_REQUEST_PACKET_VERSION;
+
+ //
+ // Set level to FALSE to indicate that find master should not initiate
+ // a findmaster request, simply complete when a new master announces
+ // itself.
+ //
+
+ RequestPacket->Level = 0;
+
+ //
+ // Stick the name of the transport associated with this request at the
+ // end of the request packet.
+ //
+
+ RequestPacket->TransportName.MaximumLength = Network->NetworkName.MaximumLength;
+
+ RequestPacket->TransportName.Buffer = (PWSTR)((PCHAR)RequestPacket+sizeof(LMDR_REQUEST_PACKET)+(MAXIMUM_FILENAME_LENGTH*sizeof(WCHAR)));
+
+ RtlCopyUnicodeString(&RequestPacket->TransportName, &Network->NetworkName);
+
+ if (ARGUMENT_PRESENT(MasterName)) {
+
+ RequestPacket->Parameters.GetMasterName.MasterNameLength =
+ MasterName->Length-2*sizeof(WCHAR);
+
+ RtlCopyMemory( &RequestPacket->Parameters.GetMasterName.Name,
+ MasterName->Buffer+2,
+ MasterName->Length-2*sizeof(WCHAR));
+
+ } else {
+
+ RequestPacket->Parameters.GetMasterName.MasterNameLength = 0;
+
+ }
+
+ BrInitializeWorkItem(&Context->WorkItem, NewMasterCompletionRoutine, Context);
+
+ Context->Network = Network;
+
+ Context->RequestPacket = RequestPacket;
+
+ NtStatus = NtDeviceIoControlFile(BrDgReceiverDeviceHandle,
+ NULL,
+ CompleteAsyncBrowserIoControl,
+ Context,
+ &Context->IoStatusBlock,
+ IOCTL_LMDR_NEW_MASTER_NAME,
+ RequestPacket,
+ PacketSize,
+ RequestPacket,
+ sizeof(LMDR_REQUEST_PACKET)+MAXIMUM_FILENAME_LENGTH*sizeof(WCHAR)
+ );
+
+ if (!NT_SUCCESS(NtStatus)) {
+
+ dprintf(ALL, ("Browser: Unable to issue browser IoControl: %X\n", NtStatus));
+
+ MIDL_user_free(RequestPacket);
+
+ MIDL_user_free(Context);
+
+
+ return(BrMapStatus(NtStatus));
+ }
+
+ ASSERT ((NtStatus == STATUS_PENDING ) || (NtStatus == STATUS_SUCCESS));
+
+ return NERR_Success;
+
+}
+
+VOID
+NewMasterCompletionRoutine(
+ IN PVOID Ctx
+ )
+{
+ PBROWSERASYNCCONTEXT Context = Ctx;
+ PNETWORK Network = Context->Network;
+
+ dprintf(MASTER, ("WaitForNewMasterCompletion: %ws Got master changed\n",
+ Network->NetworkName.Buffer));
+
+ if (!LOCK_NETWORK(Network)) {
+ MIDL_user_free(Context->RequestPacket);
+
+ MIDL_user_free(Context);
+
+ return;
+ }
+
+ try {
+ UNICODE_STRING NewMasterName;
+
+ //
+ // The request failed for some other reason - just return immediately.
+ //
+
+ if (!NT_SUCCESS(Context->IoStatusBlock.Status)) {
+
+ try_return(NOTHING);
+
+ }
+
+ // Remove new master name & put in transport
+
+ if ( Network->Role & ROLE_MASTER ) {
+
+ try_return(NOTHING);
+
+ }
+
+ NewMasterName.Buffer =
+ Context->RequestPacket->Parameters.GetMasterName.Name;
+
+ NewMasterName.Length = (USHORT)
+ Context->RequestPacket->Parameters.GetMasterName.MasterNameLength;
+
+ dprintf(BACKUP, ("NewMasterCompletionRoutine %ws New:%ws Old %ws\n",
+ Network->NetworkName.Buffer,
+ NewMasterName.Buffer,
+ Network->MasterBrowserName.Buffer));
+
+ MIDL_user_free( Network->MasterBrowserName.Buffer );
+
+ Network->MasterBrowserName.Buffer =
+ MIDL_user_allocate(Context->RequestPacket->Parameters.GetMasterName.MasterNameLength+sizeof(WCHAR));
+
+
+ if (Network->MasterBrowserName.Buffer == NULL) {
+ try_return(NOTHING);
+ }
+
+ Network->MasterBrowserName.MaximumLength = (USHORT)Context->RequestPacket->Parameters.GetMasterName.MasterNameLength+sizeof(WCHAR);
+
+ Network->MasterBrowserName.Length = (USHORT)Context->RequestPacket->Parameters.GetMasterName.MasterNameLength;
+
+ RtlCopyMemory(Network->MasterBrowserName.Buffer, Context->RequestPacket->Parameters.GetMasterName.Name,
+ Network->MasterBrowserName.MaximumLength);
+
+ ASSERT ( NetpIsUncComputerNameValid ( Network->MasterBrowserName.Buffer ) );
+
+ PostWaitForNewMasterName( Network, &Network->MasterBrowserName );
+
+try_exit:NOTHING;
+ } finally {
+
+ MIDL_user_free(Context->RequestPacket);
+
+ MIDL_user_free(Context);
+
+ UNLOCK_NETWORK(Network);
+
+ }
+
+ return;
+}
diff --git a/private/net/svcdlls/browser/server/browslst.c b/private/net/svcdlls/browser/server/browslst.c
new file mode 100644
index 000000000..4adae3814
--- /dev/null
+++ b/private/net/svcdlls/browser/server/browslst.c
@@ -0,0 +1,70 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ browslst.c
+
+Abstract:
+
+ This module contains the worker routines for managing browse lists
+ for the browser service
+
+Author:
+
+ Larry Osterman (larryo) 25-Mar-1992
+
+Revision History:
+
+--*/
+
+#include "precomp.h"
+#pragma hdrstop
+
+//-------------------------------------------------------------------//
+// //
+// Local function prototypes //
+// //
+//-------------------------------------------------------------------//
+
+
+ RTL_GENERIC_COMPARE_RESULTS
+BrCompareBrowseEntry(
+ PRTL_GENERIC_TABLE Table,
+ PVOID FirstStruct,
+ PVOID SecondStruct
+ )
+{
+ PDOMAIN_ENTRY Entry1 = FirstStruct;
+ PDOMAIN_ENTRY Entry2 = SecondStruct;
+
+ LONG CompareResult;
+
+ if ((CompareResult = RtlCompareUnicodeString(Entry1->HostName, Entry2->HostName, TRUE) == 0) {
+ return GenericEqual;
+ } else if (CompareResult < 0) {
+ return GenericLessThan;
+ } else {
+ return GenericGreaterThan;
+ }
+
+}
+
+ PVOID
+BrAllocateBrowseEntry(
+ PRTL_GENERIC_TABLE Table,
+ CLONG ByteSize
+ )
+{
+ return((PVOID) MIDL_user_allocate(LMEM_ZEROINIT, (UINT) ByteSize+sizeof(BROWSE_ENTRY)));
+}
+
+ PVOID
+BrFreeBrowseEntry(
+ PRTL_GENERIC_TABLE Table,
+ CLONG ByteSize
+ )
+{
+ return(MIDL_user_free(ByteSize+sizeof(BROWSE_ENTRY)));
+}
diff --git a/private/net/svcdlls/browser/server/browslst.h b/private/net/svcdlls/browser/server/browslst.h
new file mode 100644
index 000000000..492275d8b
--- /dev/null
+++ b/private/net/svcdlls/browser/server/browslst.h
@@ -0,0 +1,95 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ browselst.h
+
+Abstract:
+
+ Private header file to be included by Browser service modules that
+ need to deal with the browser list.
+
+Author:
+
+ Larry Osterman (larryo) 3-Mar-1992
+
+Revision History:
+
+--*/
+
+
+#ifndef _BROWSELST_INCLUDED_
+#define _BROWSELST_INCLUDED_
+
+
+//
+// The possible roles of this browser server.
+//
+
+
+#define ROLE_POTENTIAL_BACKUP 0x00000001
+#define ROLE_BACKUP 0x00000002
+#define ROLE_MASTER 0x00000004
+#define ROLE_DOMAINMASTER 0x00000008
+
+
+//
+// The HOST_ENTRY structure holds the announcement inside a per-network
+// table.
+//
+
+
+typedef struct _HOST_ENTRY {
+
+ //
+ // The HostName is the name of the server.
+ //
+
+ UNICODE_STRING HostName;
+
+ //
+ // The HostComment is the comment associated with the server
+ //
+
+ UNICODE_STRING HostComment;
+
+ //
+ // Services is a bitmask that indicates the services running on the
+ // server (See LMSERVER.H for details).
+ //
+
+ ULONG Services;
+
+ //
+ // The Periodicity is the frequency that the server announces itself.
+ //
+
+ ULONG Periodicity;
+
+ //
+ // The MajorVersion and MinorVersion number of the software running on
+ // the server.
+ //
+
+ UCHAR MajorVersion;
+ UCHAR MinorVersion;
+
+ //
+ // If this server is a backup server, then this links the backup server
+ // into the network block.
+ //
+
+ LIST_ENTRY BackupChain;
+
+ //
+ // If this server is a domain master, than this links the server into
+ // the network block.
+ //
+
+ LIST_ENTRY DomainMasterChain;
+
+} HOST_ENTRY, *PHOST_ENTRY;
+
+#endif // _BROWSELST_INCLUDED_
diff --git a/private/net/svcdlls/browser/server/browsnet.c b/private/net/svcdlls/browser/server/browsnet.c
new file mode 100644
index 000000000..ba68dda8e
--- /dev/null
+++ b/private/net/svcdlls/browser/server/browsnet.c
@@ -0,0 +1,870 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ browsenet.c
+
+Abstract:
+
+ Code to manage network requests.
+
+Author:
+
+ Larry Osterman (LarryO) 24-Mar-1992
+
+Revision History:
+
+--*/
+
+#include "precomp.h"
+#pragma hdrstop
+
+
+RTL_CRITICAL_SECTION
+NetworkLock = {0};
+
+LIST_ENTRY
+ServicedNetworks = {0};
+
+ULONG
+NumberOfServicedNetworks = 0;
+
+NET_API_STATUS
+BrDestroyNetwork(
+ IN PNETWORK Network,
+ IN PVOID Context
+ );
+
+
+NET_API_STATUS
+BrDumpNetworksWorker(
+ IN PNETWORK Network,
+ IN PVOID Context
+ );
+
+NET_API_STATUS
+BrInitializeNetworks(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ This function will query the NT bowser device driver to determine the
+ list of networks that the bowser is servicing.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ Status of operation.
+
+--*/
+{
+ NET_API_STATUS Status;
+ PLMDR_TRANSPORT_LIST TransportList;
+ PLMDR_TRANSPORT_LIST TransportEntry;
+
+ InitializeListHead(&ServicedNetworks);
+
+ RtlInitializeCriticalSection(&NetworkLock);
+
+ Status = BrGetTransportList(&TransportList);
+
+ TransportEntry = TransportList;
+
+ while (TransportEntry != NULL) {
+
+ UNICODE_STRING TransportName;
+
+ TransportName.Buffer = TransportEntry->TransportName;
+ TransportName.Length = (USHORT)TransportEntry->TransportNameLength;
+ //
+ // We know the bowser sticks in a null at the end, so the max length
+ // is the length + 1.
+ //
+ TransportName.MaximumLength = (USHORT)TransportEntry->TransportNameLength+sizeof(WCHAR);
+
+ Status = BrCreateNetwork(&TransportName,
+ (BOOLEAN)((TransportEntry->Flags & LMDR_TRANSPORT_WANNISH) != 0),
+ (BOOLEAN)((TransportEntry->Flags & LMDR_TRANSPORT_RAS) != 0),
+ NULL);
+
+ if (!NT_SUCCESS(Status)) {
+ MIDL_user_free(TransportList);
+
+ return Status;
+
+ }
+
+ NumberOfServicedNetworks += 1;
+
+ if (TransportEntry->NextEntryOffset == 0) {
+ TransportEntry = NULL;
+ } else {
+ TransportEntry = (PLMDR_TRANSPORT_LIST)((PCHAR)TransportEntry+TransportEntry->NextEntryOffset);
+ }
+
+ }
+
+ MIDL_user_free(TransportList);
+
+ return Status;
+}
+
+NET_API_STATUS
+BrCreateNetwork(
+ PUNICODE_STRING TransportName,
+ IN BOOLEAN Wannish,
+ IN BOOLEAN Ras,
+ IN PUNICODE_STRING AlternateTransportName OPTIONAL
+ )
+/*++
+
+Routine Description:
+
+ This routine allocates memory to hold a network structure, and initializes
+ all of its associated data structures.
+
+Arguments:
+
+ TransportName - The name of the transport to add.
+
+Return Value:
+
+ Status of operation (mostly status of allocations).
+
+--*/
+{
+ NET_API_STATUS Status;
+ PNETWORK Network;
+ BOOLEAN NetworkLockInitialized = FALSE;
+ BOOLEAN MasterFlagsInitialized = FALSE;
+ BOOLEAN BackupBrowserTimerCreated = FALSE;
+ BOOLEAN MasterBrowserTimerCreated =FALSE;
+ BOOLEAN AnnouncementTimerCreated = FALSE;
+ BOOLEAN ResponseCacheLockInitialized = FALSE;
+
+ //
+ // Check to see if the transport already exists.
+ //
+
+ if ((Network = BrFindNetwork(TransportName)) != NULL) {
+
+ return NERR_AlreadyExists;
+ }
+
+ //
+ // If this transport is explicitly on our list of transports to unbind,
+ // simply ignore the transport.
+ //
+
+ if (BrInfo.UnboundBindings != NULL) {
+ LPTSTR_ARRAY TStrArray = BrInfo.UnboundBindings;
+
+ while (!NetpIsTStrArrayEmpty(TStrArray)) {
+ LPWSTR NewTransportName;
+
+#define NAME_PREFIX L"\\Device\\"
+#define NAME_PREFIX_LENGTH 8
+
+ //
+ // The transport name in the registry is only optionally prefixed with \device\
+ //
+
+ if ( _wcsnicmp( NAME_PREFIX, TStrArray, NAME_PREFIX_LENGTH) == 0 ) {
+ NewTransportName = TransportName->Buffer;
+ } else {
+ NewTransportName = TransportName->Buffer + NAME_PREFIX_LENGTH;
+ }
+
+ if ( _wcsicmp( TStrArray, NewTransportName ) == 0 ) {
+ dprintf(INIT, ("Binding is marked as unbound: %s (Silently ignoring)\n", TransportName->Buffer ));
+ return NERR_Success;
+ }
+
+ TStrArray = NetpNextTStrArrayEntry(TStrArray);
+
+ }
+
+ }
+
+
+ //
+ // If this transport isn't bound to the SMB server,
+ // don't create the transport here.
+ // we do announcments through the SMB server.
+ //
+
+ Status = I_NetServerSetServiceBits(NULL, TransportName->Buffer, 0, TRUE);
+
+ if (Status == ERROR_PATH_NOT_FOUND ) {
+ dprintf(INIT, ("SMB Server doesn't have this transport: %s (Silently unbinding)\n", TransportName->Buffer ));
+ return NERR_Success;
+ }
+
+
+ //
+ // Create the transport.
+ //
+
+ try {
+
+ Network = MIDL_user_allocate(sizeof(NETWORK));
+
+ if (Network == NULL) {
+ try_return(Status = ERROR_NOT_ENOUGH_MEMORY);
+ }
+
+ RtlInitializeResource(&Network->Lock);
+
+ NetworkLockInitialized = TRUE;
+
+ Network->LockCount = 0;
+
+ Network->ReferenceCount = 1;
+
+ Network->Role = BrDefaultRole;
+
+ Network->NumberOfFailedBackupTimers = 0;
+
+ Network->NumberOfFailedPromotions = 0;
+
+ Network->NumberOfPromotionEventsLogged = 0;
+
+ Network->LastBackupBrowserReturned = 0;
+
+ Network->LastDomainControllerBrowserReturned = 0;
+
+ Network->TimeStoppedBackup = 0;
+
+ Network->BackupServerList = NULL;
+ Network->BackupDomainList = NULL;
+
+ Network->TotalBackupServerListEntries = 0;
+ Network->TotalBackupDomainListEntries = 0;
+
+ Network->NetworkName.Buffer = MIDL_user_allocate(TransportName->MaximumLength);
+
+ if (Network->NetworkName.Buffer == NULL) {
+ try_return(Status = ERROR_NOT_ENOUGH_MEMORY);
+ }
+
+ Network->NetworkName.MaximumLength = TransportName->MaximumLength;
+
+ RtlCopyUnicodeString(&Network->NetworkName, TransportName);
+
+ Network->Flags = 0;
+
+ if (ARGUMENT_PRESENT(AlternateTransportName)) {
+ PNETWORK AlternateNetwork = BrFindNetwork(AlternateTransportName);
+
+ //
+ // If we didn't find an alternate network, or if that network
+ // already has an alternate network, return an error.
+ //
+
+ if (AlternateNetwork == NULL ||
+ AlternateNetwork->AlternateNetwork != NULL) {
+
+ try_return(Status = NERR_InternalError);
+ }
+
+ Network->Flags |= NETWORK_IPX;
+
+ //
+ // Link the two networks together.
+ //
+
+ Network->AlternateNetwork = AlternateNetwork;
+
+ AlternateNetwork->AlternateNetwork = Network;
+
+ } else {
+ Network->AlternateNetwork = NULL;
+ }
+
+ //
+ // Null terminate the network name buffer.
+ //
+
+ Network->NetworkName.Buffer[Network->NetworkName.Length/sizeof(WCHAR)] = UNICODE_NULL;
+
+ RtlInitUnicodeString(&Network->MasterBrowserName, NULL);
+
+ if (Wannish) {
+ Network->Flags |= NETWORK_WANNISH;
+ }
+
+ if (Ras) {
+ Network->Flags |= NETWORK_RAS;
+ }
+
+ Network->LastBowserServerQueried = 0;
+
+ RtlInitializeCriticalSection(&Network->MasterFlagsLock);
+
+ MasterFlagsInitialized = TRUE;
+
+ Network->MasterFlags = 0;
+
+ InitializeInterimServerList(&Network->BrowseTable,
+ BrBrowseTableInsertRoutine,
+ BrBrowseTableUpdateRoutine,
+ BrBrowseTableDeleteRoutine,
+ BrBrowseTableAgeRoutine);
+
+ Network->LastBowserDomainQueried = 0;
+
+ InitializeInterimServerList(&Network->DomainList,
+ BrDomainTableInsertRoutine,
+ BrDomainTableUpdateRoutine,
+ BrDomainTableDeleteRoutine,
+ BrDomainTableAgeRoutine);
+
+ InitializeListHead(&Network->OtherDomainsList);
+
+ Status = BrCreateTimer(&Network->BackupBrowserTimer);
+
+ if (Status != NERR_Success) {
+
+ try_return(Status);
+ }
+
+ BackupBrowserTimerCreated = TRUE;
+
+ Status = BrCreateTimer(&Network->MasterBrowserTimer);
+
+ if (Status != NERR_Success) {
+ try_return(Status);
+ }
+
+ MasterBrowserTimerCreated = TRUE;
+
+ Status = BrCreateTimer(&Network->MasterBrowserAnnouncementTimer);
+
+ if (Status != NERR_Success) {
+
+ try_return(Status);
+ }
+
+ AnnouncementTimerCreated = TRUE;
+
+ InitializeCriticalSection(&Network->ResponseCacheLock);
+
+ ResponseCacheLockInitialized = TRUE;
+
+ InitializeListHead(&Network->ResponseCache);
+
+ Network->TimeCacheFlushed = 0;
+
+ Network->NumberOfCachedResponses = 0;
+
+ Status = RtlEnterCriticalSection(&NetworkLock);
+
+ if (!NT_SUCCESS(Status)) {
+
+ try_return(Status = BrMapStatus(Status));
+ }
+
+ InsertHeadList(&ServicedNetworks, &Network->NextNet);
+
+ Status = RtlLeaveCriticalSection(&NetworkLock);
+
+ if (!NT_SUCCESS(Status)) {
+ InternalError(("Unable to release browser critical section\n"));
+ }
+
+try_exit:NOTHING;
+ } finally {
+ if (Status != NERR_Success) {
+
+ if (Network != NULL) {
+ if (ResponseCacheLockInitialized) {
+ DeleteCriticalSection(&Network->ResponseCacheLock);
+ }
+
+ if (MasterFlagsInitialized) {
+ RtlDeleteCriticalSection(&Network->MasterFlagsLock);
+ }
+
+ if (NetworkLockInitialized) {
+ RtlDeleteResource(&Network->Lock);
+ }
+
+ if (AnnouncementTimerCreated) {
+ BrDestroyTimer(&Network->MasterBrowserAnnouncementTimer);
+ }
+
+ if (MasterBrowserTimerCreated) {
+ BrDestroyTimer(&Network->MasterBrowserTimer);
+ }
+
+ if (BackupBrowserTimerCreated) {
+ BrDestroyTimer(&Network->BackupBrowserTimer);
+ }
+
+ if (Network->NetworkName.Buffer != NULL) {
+ MIDL_user_free(Network->NetworkName.Buffer);
+ }
+
+ MIDL_user_free(Network);
+
+ }
+
+ }
+ }
+
+ return Status;
+}
+
+ VOID
+BrDestroyNetworks(
+ IN DWORD BrInitState
+ )
+{
+ NET_API_STATUS status;
+
+ RtlEnterCriticalSection(&NetworkLock);
+
+ while (!IsListEmpty(&ServicedNetworks)) {
+ PNETWORK Network = CONTAINING_RECORD(ServicedNetworks.Flink, NETWORK, NextNet);
+
+ //
+ // If this is an IPX transport, we need to manually unbind from the transport.
+ //
+
+ if (Network->Flags & NETWORK_IPX) {
+
+ status = BrUnbindFromTransport(Network->NetworkName.Buffer);
+
+ if (status != NERR_Success) {
+ KdPrint(("Unable to unbind from IPX transport %wS\n", &Network->NetworkName));
+ }
+
+ }
+
+ BrDestroyNetwork(Network, NULL);
+
+ }
+
+ RtlLeaveCriticalSection(&NetworkLock);
+
+ RtlDeleteCriticalSection(&NetworkLock);
+
+ NumberOfServicedNetworks = 0;
+
+}
+
+ NET_API_STATUS
+BrDestroyNetwork(
+ IN PNETWORK Network,
+ IN PVOID Context
+ )
+/*++
+
+Routine Description:
+
+ This routine removes a reference to a network. If the network reference
+ count goes to 0, remove the network.
+
+Arguments:
+
+ Network - The network to remove
+
+Return Value:
+
+ Status of operation (mostly status of allocations).
+
+--*/
+{
+ NTSTATUS Status;
+
+ Status = RtlEnterCriticalSection(&NetworkLock);
+
+ if (!NT_SUCCESS(Status)) {
+ InternalError(("Unable to acquire browser critical section\n"));
+
+ return BrMapStatus(Status);
+ }
+
+ RemoveEntryList(&Network->NextNet);
+
+ Status = RtlLeaveCriticalSection(&NetworkLock);
+
+ if (!NT_SUCCESS(Status)) {
+ InternalError(("Unable to release browser critical section\n"));
+
+ return BrMapStatus(Status);
+ }
+
+ if (Network->Role & ROLE_MASTER) {
+ //
+ // Stop being the master on this network. This means removing
+ // our name from the bowser driver and forcing an election.
+ //
+
+ BrStopMaster(Network);
+ }
+
+ if (Network->Role & (ROLE_POTENTIAL_BACKUP | ROLE_BACKUP)) {
+
+ //
+ // Stop being a backup on this network. This means downgrading our
+ // machine to idle.
+ //
+
+ BrStopBackup(Network);
+ }
+
+ //
+ // Ensure that there are no browser related names active in the browser.
+ //
+
+ BrUpdateBrowserStatus(Network, 0);
+
+ UninitializeInterimServerList(&Network->BrowseTable);
+
+ UninitializeInterimServerList(&Network->DomainList);
+
+ if (Network->BackupServerList != NULL) {
+ MIDL_user_free(Network->BackupServerList);
+ }
+
+ if (Network->BackupDomainList != NULL) {
+ MIDL_user_free(Network->BackupDomainList);
+ }
+
+// RtlDeleteCriticalSection(&Network->Lock);
+ RtlDeleteResource(&Network->Lock);
+
+
+ RtlDeleteCriticalSection(&Network->MasterFlagsLock);
+
+ BrDestroyTimer(&Network->MasterBrowserAnnouncementTimer);
+
+ BrDestroyTimer(&Network->MasterBrowserTimer);
+
+ BrDestroyTimer(&Network->BackupBrowserTimer);
+
+ BrDestroyResponseCache(Network);
+
+ RtlDeleteCriticalSection(&Network->ResponseCacheLock);
+
+ MIDL_user_free(Network->NetworkName.Buffer);
+
+ if (Network->MasterBrowserName.Buffer != NULL) {
+ MIDL_user_free(Network->MasterBrowserName.Buffer);
+ }
+
+ MIDL_user_free(Network);
+
+ return NERR_Success;
+
+ UNREFERENCED_PARAMETER(Context);
+
+}
+
+
+ PNETWORK
+BrFindNetwork(
+ PUNICODE_STRING TransportName
+ )
+/*++
+
+Routine Description:
+
+ This routine will look up a network given a name.
+
+Arguments:
+
+ TransportName - The name of the transport to look up.
+
+Return Value:
+
+ Status of operation (mostly status of allocations).
+
+--*/
+{
+ NTSTATUS Status;
+ PLIST_ENTRY NetEntry;
+
+ Status = RtlEnterCriticalSection(&NetworkLock);
+
+ if (!NT_SUCCESS(Status)) {
+ InternalError(("Unable to acquire browser critical section\n"));
+
+ return NULL;
+ }
+
+ for (NetEntry = ServicedNetworks.Flink ;
+ NetEntry != &ServicedNetworks;
+ NetEntry = NetEntry->Flink ) {
+ PNETWORK Network = CONTAINING_RECORD(NetEntry, NETWORK, NextNet);
+
+ if (RtlEqualUnicodeString(&Network->NetworkName, TransportName, TRUE)) {
+
+ Status = RtlLeaveCriticalSection(&NetworkLock);
+
+ if (!NT_SUCCESS(Status)) {
+ InternalError(("Unable to release browser critical section\n"));
+
+ return NULL;
+ }
+
+ return Network;
+ }
+
+ }
+
+ Status = RtlLeaveCriticalSection(&NetworkLock);
+
+ if (!NT_SUCCESS(Status)) {
+ InternalError(("Unable to release browser critical section\n"));
+
+ return NULL;
+ }
+
+
+ return NULL;
+}
+
+ NET_API_STATUS
+BrEnumerateNetworks(
+ PNET_ENUM_CALLBACK Callback,
+ PVOID Context
+ )
+/*++
+
+Routine Description:
+
+ This routine enumerates all the networks and calls back the specified
+ callback routine with the specified context.
+
+Arguments:
+
+ Callback - The callback routine to call.
+ Context - Context for the routine.
+
+Return Value:
+
+ Status of operation (mostly status of allocations).
+
+--*/
+{
+
+ NTSTATUS Status;
+ PLIST_ENTRY NetEntry;
+
+ Status = RtlEnterCriticalSection(&NetworkLock);
+
+ if (!NT_SUCCESS(Status)) {
+ InternalError(("Unable to acquire browser critical section\n"));
+
+ return Status;
+ }
+
+ for (NetEntry = ServicedNetworks.Flink ;
+ NetEntry != &ServicedNetworks;
+ NetEntry = NetEntry->Flink ) {
+ PNETWORK Network = CONTAINING_RECORD(NetEntry, NETWORK, NextNet);
+
+ Status = RtlLeaveCriticalSection(&NetworkLock);
+
+ if (!NT_SUCCESS(Status)) {
+ InternalError(("Unable to release browser critical section\n"));
+
+ return BrMapStatus(Status);
+ }
+
+ //
+ // Call into the callback routine with this network.
+ //
+
+ Status = (Callback)(Network, Context);
+
+ if (Status != NERR_Success) {
+ return Status;
+ }
+
+ Status = RtlEnterCriticalSection(&NetworkLock);
+
+ if (!NT_SUCCESS(Status)) {
+ InternalError(("Unable to acquire browser critical section\n"));
+
+ return BrMapStatus(Status);
+ }
+ }
+
+ Status = RtlLeaveCriticalSection(&NetworkLock);
+
+ if (!NT_SUCCESS(Status)) {
+ InternalError(("Unable to release browser critical section\n"));
+
+ return Status;
+ }
+
+ return NERR_Success;
+
+}
+
+#if DBG
+
+ BOOL
+BrLockNetwork(
+ IN PNETWORK Network,
+ IN PCHAR FileName,
+ IN ULONG LineNumber
+ )
+{
+ PCHAR File;
+
+ File = strrchr(FileName, '\\');
+
+ if (File == NULL) {
+ File = FileName;
+ }
+
+ dprintf(LOCKS, ("Acquring lock %s:%d on network %ws\n", File, LineNumber, (Network)->NetworkName.Buffer));
+
+ if (!RtlAcquireResourceExclusive(&(Network)->Lock, TRUE)) {
+ dprintf(LOCKS, ("Failed to acquire lock %s:%d on network %ws\n", File, LineNumber, (Network)->NetworkName.Buffer));
+ return FALSE;
+
+ } else {
+
+ InterlockedIncrement( &Network->LockCount );
+
+ dprintf(LOCKS, ("Lock %s:%d on network %ws acquired\n", File, LineNumber, (Network)->NetworkName.Buffer));
+ }
+
+ return TRUE;
+
+}
+
+ BOOL
+BrLockNetworkShared(
+ IN PNETWORK Network,
+ IN PCHAR FileName,
+ IN ULONG LineNumber
+ )
+{
+ PCHAR File;
+
+ File = strrchr(FileName, '\\');
+
+ if (File == NULL) {
+ File = FileName;
+ }
+
+ dprintf(LOCKS, ("Acquring lock %s:%d on network %ws\n", File, LineNumber, (Network)->NetworkName.Buffer));
+
+ if (!RtlAcquireResourceShared(&(Network)->Lock, TRUE)) {
+ dprintf(LOCKS, ("Failed to acquire lock %s:%d on network %ws\n", File, LineNumber, (Network)->NetworkName.Buffer));
+
+ return FALSE;
+
+ } else {
+
+ // Use InterlockedIncrement since we only have a shared lock on the
+ // resource.
+ InterlockedIncrement( &Network->LockCount );
+
+ dprintf(LOCKS, ("Lock %s:%d on network %ws acquired\n", File, LineNumber, (Network)->NetworkName.Buffer));
+ }
+
+ return TRUE;
+
+}
+
+ VOID
+BrUnlockNetwork(
+ IN PNETWORK Network,
+ IN PCHAR FileName,
+ IN ULONG LineNumber
+ )
+{
+ PCHAR File;
+ LONG ReturnValue;
+
+ File = strrchr(FileName, '\\');
+
+ if (File == NULL) {
+ File = FileName;
+ }
+
+
+ dprintf(LOCKS, ("Releasing lock %s:%d on network %ws\n", File, LineNumber, (Network)->NetworkName.Buffer));
+
+ //
+ // Decrement the lock count.
+ //
+
+ ReturnValue = InterlockedDecrement( &Network->LockCount );
+
+ if ( ReturnValue < 0) {
+ dprintf(LOCKS, ("Over released lock %s:%d on network %ws\n", File, LineNumber, (Network)->NetworkName.Buffer));
+ KdPrint(("BROWSER: Over released lock %s:%d on network %ws\n", File, LineNumber, (Network)->NetworkName.Buffer));
+ }
+
+ RtlReleaseResource(&(Network)->Lock);
+
+ return;
+}
+#endif
+
+
+
+#if DBG
+ VOID
+BrDumpNetworks(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This routine will dump the contents of each of the browser network
+ structures.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ BrEnumerateNetworks(BrDumpNetworksWorker, NULL);
+}
+
+
+ NET_API_STATUS
+BrDumpNetworksWorker(
+ IN PNETWORK Network,
+ IN PVOID Context
+ )
+{
+ if (!LOCK_NETWORK(Network)) {
+ return NERR_InternalError;
+ }
+
+ DbgPrint("Network at %lx (%wZ)\n", Network, &Network->NetworkName);
+ DbgPrint(" Reference Count: %lx\n", Network->ReferenceCount);
+ DbgPrint(" Flags: %lx\n", Network->Flags);
+ DbgPrint(" Role: %lx\n", Network->Role);
+ DbgPrint(" Master Browser Name: %wZ (%ws)\n", &Network->MasterBrowserName,
+ Network->MasterBrowserName.Buffer);
+
+ UNLOCK_NETWORK(Network);
+
+ return(NERR_Success);
+}
+#endif
diff --git a/private/net/svcdlls/browser/server/browsnet.h b/private/net/svcdlls/browser/server/browsnet.h
new file mode 100644
index 000000000..e8f6e7d1c
--- /dev/null
+++ b/private/net/svcdlls/browser/server/browsnet.h
@@ -0,0 +1,299 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ browsenet.h
+
+Abstract:
+
+ Private header file to be included by Browser service modules that
+ need to deal with the network specific browser tables.
+
+Author:
+
+ Rita Wong (ritaw) 22-May-1991
+
+Revision History:
+
+--*/
+
+
+#ifndef _BROWSENET_INCLUDED_
+#define _BROWSENET_INCLUDED_
+
+#define NETWORK_BECOME_MASTER_POSTED 0x00000001
+#define NETWORK_GET_MASTER_ANNOUNCE_POSTED 0x00000008
+#define NETWORK_WANNISH 0x80000000
+#define NETWORK_RAS 0x40000000
+#define NETWORK_IPX 0x20000000
+
+#define OTHERDOMAIN_INVALID 0x00000001
+
+#define NETWORK_MASTER_GETBLIST_POSTED 0x00000001
+
+typedef struct _NET_OTHER_DOMAIN {
+ LIST_ENTRY Next;
+ ULONG Flags;
+ WCHAR Name[DNLEN+1];
+} NET_OTHER_DOMAIN, *PNET_OTHER_DOMAIN;
+
+//
+// Network.
+//
+// Almost all of the browser data structures are tied to the "Network"
+// structure.
+//
+// It contains the browse list for the network and information about the
+// domain (including the name of the master, etc).
+//
+
+typedef struct _NETWORK {
+ //
+ // The Lock protects the contents of the network structure, including
+ // the browse list, backup list and domain list.
+ //
+
+ RTL_RESOURCE Lock;
+
+ LONG LockCount;
+
+ ULONG Flags;
+
+ //
+ // The NextNet list links the structure to other networks.
+ //
+
+ LIST_ENTRY NextNet;
+
+ //
+ // The ReferenceCount indicates the number of threads accessing this
+ // network structure.
+ //
+
+ ULONG ReferenceCount;
+
+ //
+ // The NetworkName is the name of the network driver that is used
+ // to access the network. This is used to identify the network
+ // to the bowser driver so it can return the correct network list.
+ //
+
+ UNICODE_STRING NetworkName; // Name of network (\Device\Nbf)
+
+ struct _NETWORK *AlternateNetwork; // Alternate name for network (if IPX).
+
+ //
+ // This is a bitmask indicating the role of this browser server.
+ //
+
+ ULONG Role;
+
+ ULONG MasterAnnouncementIndex;
+
+ ULONG NumberOfFailedBackupTimers;
+
+ ULONG NumberOfFailedPromotions;
+
+ ULONG NumberOfPromotionEventsLogged;
+
+ LONG LastBackupBrowserReturned;
+
+ LONG LastDomainControllerBrowserReturned;
+
+ //
+ // The time we stopped being a backup browser.
+ //
+
+ ULONG TimeStoppedBackup;
+
+ //
+ // The MasterBrowserName contains the name of the master browser server
+ // for this network.
+ //
+
+ UNICODE_STRING MasterBrowserName; // Name of master browser server
+
+ //
+ // Timer used when server is a backup browser server.
+ //
+ // When it expires, the browser downloads a new browser server
+ // list from the master browser server.
+ //
+
+ BROWSER_TIMER BackupBrowserTimer;
+
+ //
+ // Server and domain list for backup browser (and # of entries in each).
+ //
+
+ PSERVER_INFO_101 BackupServerList;
+ DWORD TotalBackupServerListEntries;
+ DWORD BytesToHoldBackupServerList;
+
+ PSERVER_INFO_101 BackupDomainList;
+ DWORD TotalBackupDomainListEntries;
+ DWORD BytesToHoldBackupDomainList;
+
+ //
+ // Lock protecting MasterFlags section of Network structure.
+ //
+
+ RTL_CRITICAL_SECTION MasterFlagsLock;
+
+ ULONG MasterFlags;
+ ULONG MasterBrowserTimerCount; // # of times we've run the master timer.
+
+ //
+ // Master browsers maintain their server list in an "interim server
+ // list", not as raw data from the server.
+ //
+
+ ULONG LastBowserServerQueried;
+ INTERIM_SERVER_LIST BrowseTable; // Browse list for network.
+
+ ULONG LastBowserDomainQueried;
+ INTERIM_SERVER_LIST DomainList; // List of domains active on network
+
+ //
+ // If the browser's role is MasterBrowserServer, then the
+ // DomainMasterBrowserList contains the list of domain master browser
+ // servers.
+ //
+
+ LIST_ENTRY OtherDomainsList; // List of domain master browser.
+
+ //
+ // Timer used when server is a master browser server.
+ //
+ // When it expires, the master browser downloads a new browser
+ // server list from the domain master browser server
+ //
+
+ BROWSER_TIMER MasterBrowserTimer;
+
+ //
+ // Timer used to announce the domain.
+ //
+
+ BROWSER_TIMER MasterBrowserAnnouncementTimer;
+
+ //
+ // List of cached browser responses.
+ //
+
+ CRITICAL_SECTION ResponseCacheLock;
+
+ LIST_ENTRY ResponseCache;
+
+ //
+ // For browse masters, this is the time the cache was last flushed.
+ //
+ // Every <n> seconds, we will age the cache on the master and refresh
+ // the list with the list from the driver.
+ //
+
+ DWORD TimeCacheFlushed;
+
+ DWORD NumberOfCachedResponses;
+
+} NETWORK, *PNETWORK;
+
+#if DBG
+BOOL
+BrLockNetwork(
+ IN PNETWORK Network,
+ IN PCHAR FileName,
+ IN ULONG LineNumber
+ );
+
+BOOL
+BrLockNetworkShared(
+ IN PNETWORK Network,
+ IN PCHAR FileName,
+ IN ULONG LineNumber
+ );
+
+VOID
+BrUnlockNetwork(
+ IN PNETWORK Network,
+ IN PCHAR FileName,
+ IN ULONG LineNumber
+ );
+
+#define LOCK_NETWORK(Network) BrLockNetwork(Network, __FILE__, __LINE__)
+
+#define LOCK_NETWORK_SHARED(Network) BrLockNetworkShared(Network, __FILE__, __LINE__)
+
+#define UNLOCK_NETWORK(Network) BrUnlockNetwork(Network, __FILE__, __LINE__)
+
+#else
+
+#define LOCK_NETWORK(Network) RtlAcquireResourceExclusive(&(Network)->Lock, TRUE)
+
+#define LOCK_NETWORK_SHARED(Network) RtlAcquireResourceShared(&(Network)->Lock, TRUE)
+
+#define UNLOCK_NETWORK(Network) RtlReleaseResource(&(Network)->Lock)
+
+#endif
+
+#define LOCK_NETWORK_MASTER_FLAGS(Network) RtlEnterCriticalSection(&(Network)->MasterFlagsLock)
+
+#define UNLOCK_NETWORK_MASTER_FLAGS(Network) RtlLeaveCriticalSection(&(Network)->MasterFlagsLock)
+
+//
+// The NET_ENUM_CALLBACK is a callback for BrEnumerateNetworks.
+//
+// It defines a routine that takes two parameters, the first is a network
+// structure, the second is a context for that network.
+//
+
+
+typedef
+NET_API_STATUS
+(*PNET_ENUM_CALLBACK)(
+ PNETWORK Network,
+ PVOID Context
+ );
+
+
+NET_API_STATUS
+BrInitializeNetworks(
+ VOID
+ );
+
+VOID
+BrDestroyNetworks(
+ IN ULONG BrInitState
+ );
+
+
+PNETWORK
+BrFindNetwork(
+ PUNICODE_STRING TransportName
+ );
+
+VOID
+BrDumpNetworks(
+ VOID
+ );
+
+NET_API_STATUS
+BrEnumerateNetworks(
+ PNET_ENUM_CALLBACK Callback,
+ PVOID Context
+ );
+
+NET_API_STATUS
+BrCreateNetwork(
+ PUNICODE_STRING TransportName,
+ IN BOOLEAN Wannish,
+ IN BOOLEAN Ras,
+ IN PUNICODE_STRING AlternateTransportName OPTIONAL
+ );
+
+ULONG
+NumberOfServicedNetworks;
+
+#endif // _BROWSENET_INCLUDED_
diff --git a/private/net/svcdlls/browser/server/brsec.h b/private/net/svcdlls/browser/server/brsec.h
new file mode 100644
index 000000000..5fff37dbe
--- /dev/null
+++ b/private/net/svcdlls/browser/server/brsec.h
@@ -0,0 +1,71 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ brsec.h
+
+Abstract:
+
+ Private header file to be included by Workstation service modules that
+ need to enforce security.
+
+Author:
+
+ Rita Wong (ritaw) 19-Feb-1991
+
+Revision History:
+
+--*/
+
+#ifndef _BRSEC_INCLUDED_
+#define _BRSEC_INCLUDED_
+
+#include <secobj.h>
+
+//-------------------------------------------------------------------//
+// //
+// Object specific access masks //
+// //
+//-------------------------------------------------------------------//
+
+//
+// ConfigurationInfo specific access masks
+//
+#define BROWSER_CONFIG_GUEST_INFO_GET 0x0001
+#define BROWSER_CONFIG_USER_INFO_GET 0x0002
+#define BROWSER_CONFIG_ADMIN_INFO_GET 0x0004
+#define BROWSER_CONFIG_INFO_SET 0x0008
+
+#define BROWSER_CONFIG_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | \
+ BROWSER_CONFIG_GUEST_INFO_GET | \
+ BROWSER_CONFIG_USER_INFO_GET | \
+ BROWSER_CONFIG_ADMIN_INFO_GET | \
+ BROWSER_CONFIG_INFO_SET)
+
+
+//
+// Object type names for audit alarm tracking
+//
+#define CONFIG_INFO_OBJECT TEXT("BrowserConfigurationInfo")
+
+//
+// Security descriptors of workstation objects to control user accesses
+// to the workstation configuration information, sending messages, and the
+// logon support functions.
+//
+extern PSECURITY_DESCRIPTOR ConfigurationInfoSd;
+
+//
+// Generic mapping for each workstation object
+//
+extern GENERIC_MAPPING BrConfigInfoMapping;
+
+
+NET_API_STATUS
+BrCreateBrowserObjects(
+ VOID
+ );
+
+#endif // ifndef _WSSEC_INCLUDED_
diff --git a/private/net/svcdlls/browser/server/brutil.c b/private/net/svcdlls/browser/server/brutil.c
new file mode 100644
index 000000000..d4ab2838d
--- /dev/null
+++ b/private/net/svcdlls/browser/server/brutil.c
@@ -0,0 +1,491 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ brutil.c
+
+Abstract:
+
+ This module contains miscellaneous utility routines used by the
+ Browser service.
+
+Author:
+
+ Rita Wong (ritaw) 01-Mar-1991
+
+Revision History:
+
+--*/
+
+#include "precomp.h"
+#pragma hdrstop
+
+//-------------------------------------------------------------------//
+// //
+// Local function prototypes //
+// //
+//-------------------------------------------------------------------//
+
+
+//-------------------------------------------------------------------//
+// //
+// Global variables //
+// //
+//-------------------------------------------------------------------//
+
+
+
+NET_API_STATUS
+BrMapStatus(
+ IN NTSTATUS NtStatus
+ )
+/*++
+
+Routine Description:
+
+ This function takes an NT status code and maps it to the appropriate
+ error code expected from calling a LAN Man API.
+
+Arguments:
+
+ NtStatus - Supplies the NT status.
+
+Return Value:
+
+ Returns the appropriate LAN Man error code for the NT status.
+
+--*/
+{
+ //
+ // A small optimization for the most common case.
+ //
+ if (NT_SUCCESS(NtStatus)) {
+ return NERR_Success;
+ }
+
+ switch (NtStatus) {
+ case STATUS_OBJECT_NAME_COLLISION:
+ return ERROR_ALREADY_ASSIGNED;
+
+ case STATUS_OBJECT_NAME_NOT_FOUND:
+ return NERR_UseNotFound;
+
+ case STATUS_REDIRECTOR_STARTED:
+ return NERR_ServiceInstalled;
+
+ default:
+ return NetpNtStatusToApiStatus(NtStatus);
+ }
+
+}
+
+
+ ULONG
+BrCurrentSystemTime()
+{
+ NTSTATUS Status;
+ SYSTEM_TIMEOFDAY_INFORMATION TODInformation;
+ LARGE_INTEGER CurrentTime;
+ ULONG TimeInSecondsSince1980;
+ ULONG BootTimeInSecondsSince1980;
+
+ Status = NtQuerySystemInformation(SystemTimeOfDayInformation,
+ &TODInformation,
+ sizeof(TODInformation),
+ NULL);
+
+ if (!NT_SUCCESS(Status)) {
+ return(0);
+ }
+
+ Status = NtQuerySystemTime(&CurrentTime);
+
+ if (!NT_SUCCESS(Status)) {
+ return(0);
+ }
+
+ RtlTimeToSecondsSince1980(&CurrentTime, &TimeInSecondsSince1980);
+ RtlTimeToSecondsSince1980(&TODInformation.BootTime, &BootTimeInSecondsSince1980);
+
+ return(TimeInSecondsSince1980 - BootTimeInSecondsSince1980);
+
+}
+
+ VOID
+BrLogEvent(
+ IN ULONG MessageId,
+ IN ULONG ErrorCode,
+ IN ULONG NumberOfSubStrings,
+ IN LPWSTR *SubStrings
+ )
+{
+
+ HANDLE LogHandle;
+
+ PSID UserSid = NULL;
+ DWORD Severity;
+ WORD Type;
+
+
+ LogHandle = RegisterEventSourceW (
+ NULL,
+ SERVICE_BROWSER
+ );
+
+ if (LogHandle == NULL) {
+ KdPrint(("[Browser] RegisterEventSourceW failed %lu\n",
+ GetLastError()));
+ return;
+ }
+
+ //
+ // Log the error code specified
+ //
+
+ Severity = (MessageId & 0xc0000000) >> 30;
+
+ if (Severity == STATUS_SEVERITY_WARNING) {
+ Type = EVENTLOG_WARNING_TYPE;
+ } else if (Severity == STATUS_SEVERITY_SUCCESS) {
+ Type = EVENTLOG_SUCCESS;
+ } else if (Severity == STATUS_SEVERITY_INFORMATIONAL) {
+ Type = EVENTLOG_INFORMATION_TYPE;
+ } else if (Severity == STATUS_SEVERITY_ERROR) {
+ Type = EVENTLOG_ERROR_TYPE;
+ }
+
+ if (ErrorCode == NERR_Success) {
+
+ //
+ // No error codes were specified
+ //
+ (void) ReportEventW(
+ LogHandle,
+ Type,
+ 0, // event category
+ MessageId,
+ UserSid,
+ (WORD)NumberOfSubStrings,
+ 0,
+ SubStrings,
+ (PVOID) NULL
+ );
+
+ }
+ else {
+
+ (void) ReportEventW(
+ LogHandle,
+ Type,
+ 0, // event category
+ MessageId,
+ UserSid,
+ (WORD)NumberOfSubStrings,
+ sizeof(DWORD),
+ SubStrings,
+ (PVOID) &ErrorCode
+ );
+ }
+
+ DeregisterEventSource(LogHandle);
+}
+
+#if DBG
+
+#define TRACE_FILE_SIZE 256
+
+VOID
+BrResetTraceLogFile(
+ VOID
+ );
+
+CRITICAL_SECTION
+BrowserTraceLock = {0};
+
+HANDLE
+BrowserTraceLogHandle = NULL;
+UCHAR LastCharacter = '\n';
+
+DWORD
+BrTraceLogFileSize = 0;
+
+BOOLEAN BrowserTraceInitialized = {0};
+
+VOID
+BrowserTrace(
+ PCHAR FormatString,
+ ...
+ )
+#define LAST_NAMED_ARGUMENT FormatString
+
+{
+ CHAR OutputString[4096];
+ ULONG BytesWritten;
+
+ va_list ParmPtr; // Pointer to stack parms.
+
+ if (!BrowserTraceInitialized) {
+ return;
+ }
+
+ EnterCriticalSection(&BrowserTraceLock);
+
+ try {
+
+ if (BrowserTraceLogHandle == NULL) {
+ //
+ // We've not opened the trace log file yet, so open it.
+ //
+
+ BrOpenTraceLogFile();
+ }
+
+ if (BrowserTraceLogHandle == INVALID_HANDLE_VALUE) {
+ LeaveCriticalSection(&BrowserTraceLock);
+ return;
+ }
+
+ //
+ // Attempt to catch bad trace.
+ //
+
+ for (BytesWritten = 0; BytesWritten < strlen(FormatString) ; BytesWritten += 1) {
+ if (FormatString[BytesWritten] > 0x7f) {
+ DbgBreakPoint();
+ }
+ }
+
+ if (LastCharacter == '\n') {
+ SYSTEMTIME SystemTime;
+
+ GetLocalTime(&SystemTime);
+
+ //
+ // The last character written was a newline character. We should
+ // timestamp this record in the file.
+ //
+
+ sprintf(OutputString, "%2.2d/%2.2d/%4.4d %2.2d:%2.2d:%2.2d.%3.3d: ", SystemTime.wMonth,
+ SystemTime.wDay,
+ SystemTime.wYear,
+ SystemTime.wHour,
+ SystemTime.wMinute,
+ SystemTime.wSecond,
+ SystemTime.wMilliseconds);
+
+ if (!WriteFile(BrowserTraceLogHandle, OutputString, strlen(OutputString), &BytesWritten, NULL)) {
+ KdPrint(("Error writing time to Browser log file: %ld\n", GetLastError()));
+ return;
+ }
+
+ if (BytesWritten != strlen(OutputString)) {
+ KdPrint(("Error writing time to Browser log file: %ld\n", GetLastError()));
+ return;
+ }
+
+ BrTraceLogFileSize += BytesWritten;
+
+ }
+
+ va_start(ParmPtr, LAST_NAMED_ARGUMENT);
+
+ //
+ // Format the parameters to the string.
+ //
+
+ vsprintf(OutputString, FormatString, ParmPtr);
+
+ if (!WriteFile(BrowserTraceLogHandle, OutputString, strlen(OutputString), &BytesWritten, NULL)) {
+ KdPrint(("Error writing to Browser log file: %ld\n", GetLastError()));
+ KdPrint(("%s", OutputString));
+ return;
+ }
+
+ if (BytesWritten != strlen(OutputString)) {
+ KdPrint(("Error writing time to Browser log file: %ld\n", GetLastError()));
+ KdPrint(("%s", OutputString));
+ return;
+ }
+
+ BrTraceLogFileSize += BytesWritten;
+
+ //
+ // Remember the last character output to the log.
+ //
+
+ LastCharacter = OutputString[strlen(OutputString)-1];
+
+ if (BrTraceLogFileSize > BrInfo.BrowserDebugFileLimit) {
+ BrResetTraceLogFile();
+ }
+
+ } finally {
+ LeaveCriticalSection(&BrowserTraceLock);
+ }
+}
+
+
+VOID
+BrInitializeTraceLog()
+{
+
+ InitializeCriticalSection(&BrowserTraceLock);
+ BrowserTraceInitialized = TRUE;
+
+}
+
+VOID
+BrGetTraceLogRoot(
+ IN PWCHAR TraceFile
+ )
+{
+ PSHARE_INFO_502 ShareInfo;
+
+ //
+ // If the DEBUG share exists, put the log file in that directory,
+ // otherwise, use the system root.
+ //
+ // This way, if the browser is running on an NTAS server, we can always
+ // get access to the log file.
+ //
+
+ if (NetShareGetInfo(NULL, L"DEBUG", 502, (PCHAR *)&ShareInfo) != NERR_Success) {
+
+ if (GetSystemDirectory(TraceFile, TRACE_FILE_SIZE*sizeof(WCHAR)) == 0) {
+ KdPrint(("Unable to get system directory: %ld\n", GetLastError()));
+ }
+
+ if (TraceFile[wcslen(TraceFile)] != L'\\') {
+ TraceFile[wcslen(TraceFile)+1] = L'\0';
+ TraceFile[wcslen(TraceFile)] = L'\\';
+ }
+
+ } else {
+ //
+ // Seed the trace file buffer with the local path of the netlogon
+ // share if it exists.
+ //
+
+ wcscpy(TraceFile, ShareInfo->shi502_path);
+
+ TraceFile[wcslen(ShareInfo->shi502_path)] = L'\\';
+ TraceFile[wcslen(ShareInfo->shi502_path)+1] = L'\0';
+
+ NetApiBufferFree(ShareInfo);
+ }
+
+}
+
+VOID
+BrResetTraceLogFile(
+ VOID
+ )
+{
+ WCHAR OldTraceFile[TRACE_FILE_SIZE];
+ WCHAR NewTraceFile[TRACE_FILE_SIZE];
+
+ if (BrowserTraceLogHandle != NULL) {
+ CloseHandle(BrowserTraceLogHandle);
+ }
+
+ BrowserTraceLogHandle = NULL;
+
+ BrGetTraceLogRoot(OldTraceFile);
+
+ wcscpy(NewTraceFile, OldTraceFile);
+
+ wcscat(OldTraceFile, L"Browser.Log");
+
+ wcscat(NewTraceFile, L"Browser.Bak");
+
+ //
+ // Delete the old log
+ //
+
+ DeleteFile(NewTraceFile);
+
+ //
+ // Rename the current log to the new log.
+ //
+
+ MoveFile(OldTraceFile, NewTraceFile);
+
+ BrOpenTraceLogFile();
+
+}
+
+VOID
+BrOpenTraceLogFile(
+ VOID
+ )
+{
+ WCHAR TraceFile[TRACE_FILE_SIZE];
+
+ BrGetTraceLogRoot(TraceFile);
+
+ wcscat(TraceFile, L"Browser.Log");
+
+ BrowserTraceLogHandle = CreateFile(TraceFile,
+ GENERIC_WRITE,
+ FILE_SHARE_READ,
+ NULL,
+ OPEN_ALWAYS,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL);
+
+
+ if (BrowserTraceLogHandle == INVALID_HANDLE_VALUE) {
+ KdPrint(("Error creating trace file %ws: %ld\n", TraceFile, GetLastError()));
+
+ return;
+ }
+
+ BrTraceLogFileSize = SetFilePointer(BrowserTraceLogHandle, 0, NULL, FILE_END);
+
+ if (BrTraceLogFileSize == 0xffffffff) {
+ KdPrint(("Error setting trace file pointer: %ld\n", GetLastError()));
+
+ return;
+ }
+}
+
+VOID
+BrUninitializeTraceLog()
+{
+ DeleteCriticalSection(&BrowserTraceLock);
+
+ if (BrowserTraceLogHandle != NULL) {
+ CloseHandle(BrowserTraceLogHandle);
+ }
+
+ BrowserTraceLogHandle = NULL;
+
+ BrowserTraceInitialized = FALSE;
+
+}
+
+NET_API_STATUS
+BrTruncateLog()
+{
+ if (BrowserTraceLogHandle == NULL) {
+ BrOpenTraceLogFile();
+ }
+
+ if (BrowserTraceLogHandle == INVALID_HANDLE_VALUE) {
+ return ERROR_GEN_FAILURE;
+ }
+
+ if (SetFilePointer(BrowserTraceLogHandle, 0, NULL, FILE_BEGIN) == 0xffffffff) {
+ return GetLastError();
+ }
+
+ if (!SetEndOfFile(BrowserTraceLogHandle)) {
+ return GetLastError();
+ }
+
+ return NO_ERROR;
+}
+
+#endif
diff --git a/private/net/svcdlls/browser/server/brutil.h b/private/net/svcdlls/browser/server/brutil.h
new file mode 100644
index 000000000..d0aa7c85f
--- /dev/null
+++ b/private/net/svcdlls/browser/server/brutil.h
@@ -0,0 +1,104 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ brutil.h
+
+Abstract:
+
+ Private header file for the NT Workstation service included by every module
+ module of the Workstation service.
+
+Author:
+
+ Rita Wong (ritaw) 15-Feb-1991
+
+Revision History:
+
+--*/
+
+#ifndef _BRUTIL_INCLUDED_
+#define _BRUTIL_INCLUDED_
+
+//
+// This include file will be included by tstring.h if Unicode
+// is defined.
+//
+#ifndef UNICODE
+#include <stdlib.h> // Unicode string functions
+#endif
+
+#include "br.h"
+
+
+//
+// An invalid parameter is encountered. Return the value to identify
+// the parameter at fault.
+//
+#define RETURN_INVALID_PARAMETER(ErrorParameter, ParameterId) \
+ if (ARGUMENT_PRESENT(ErrorParameter)) { \
+ *ErrorParameter = ParameterId; \
+ } \
+ return ERROR_INVALID_PARAMETER;
+
+
+
+//-------------------------------------------------------------------//
+// //
+// Type definitions //
+// //
+//-------------------------------------------------------------------//
+
+
+//-------------------------------------------------------------------//
+// //
+// Function prototypes of utility routines found in wsutil.c //
+// //
+//-------------------------------------------------------------------//
+
+NET_API_STATUS
+BrMapStatus(
+ IN NTSTATUS NtStatus
+ );
+
+ULONG
+BrCurrentSystemTime(VOID);
+
+VOID
+BrLogEvent(
+ IN ULONG MessageId,
+ IN ULONG ErrorCode,
+ IN ULONG NumberOfSubStrings,
+ IN LPWSTR *SubStrings
+ );
+
+#if DBG
+VOID
+BrOpenTraceLogFile(
+ VOID
+ );
+
+VOID
+BrowserTrace(
+ PCHAR FormatString,
+ ...
+ );
+VOID
+BrInitializeTraceLog(
+ VOID
+ );
+
+VOID
+BrUninitializeTraceLog(
+ VOID
+ );
+
+NET_API_STATUS
+BrTruncateLog(
+ VOID
+ );
+
+#endif
+#endif // ifndef _WSUTIL_INCLUDED_
diff --git a/private/net/svcdlls/browser/server/brwan.c b/private/net/svcdlls/browser/server/brwan.c
new file mode 100644
index 000000000..086582287
--- /dev/null
+++ b/private/net/svcdlls/browser/server/brwan.c
@@ -0,0 +1,392 @@
+
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ brwan.c
+
+Abstract:
+
+ This module contains WAN support routines used by the
+ Browser service.
+
+Author:
+
+ Larry Osterman (LarryO) 22-Nov-1992
+
+Revision History:
+
+--*/
+
+#include "precomp.h"
+#pragma hdrstop
+
+//-------------------------------------------------------------------//
+// //
+// Local function prototypes //
+// //
+//-------------------------------------------------------------------//
+
+NET_API_STATUS
+BrAddDomainEntry(
+ IN PINTERIM_SERVER_LIST InterimServerList,
+ IN LPTSTR ConfigEntry
+ );
+
+
+//-------------------------------------------------------------------//
+// //
+// Global variables //
+// //
+//-------------------------------------------------------------------//
+
+
+//-------------------------------------------------------------------//
+// //
+// Global routines //
+// //
+//-------------------------------------------------------------------//
+ NET_API_STATUS NET_API_FUNCTION
+I_BrowserrQueryOtherDomains(
+ IN BROWSER_IDENTIFY_HANDLE ServerName,
+ IN OUT LPSERVER_ENUM_STRUCT InfoStruct,
+ OUT LPDWORD TotalEntries
+ )
+
+/*++
+
+Routine Description:
+
+ This routine returns the list of "other domains" configured for this
+ machine. It is only valid on primary domain controllers. If it is called
+ on a machine that is not a PDC, it will return NERR_NotPrimary.
+
+
+Arguments:
+
+ IN BROWSER_IDENTIFY_HANDLE ServerName - Ignored.
+ IN LPSERVER_ENUM_STRUCT InfoStruct - Returns the list of other domains
+ as a SERVER_INFO_100 structure.
+ OUT LPDWORD TotalEntries - Returns the total number of other domains.
+
+Return Value:
+
+ NET_API_STATUS - The status of this request.
+
+--*/
+
+{
+ NET_API_STATUS Status;
+ LMDR_REQUEST_PACKET RequestPacket;
+ PDGRECEIVE_NAMES NameTable;
+ PVOID Buffer;
+ LPTSTR BufferEnd;
+ PSERVER_INFO_100 ServerInfo;
+ ULONG NumberOfOtherDomains;
+ ULONG BufferSizeNeeded;
+ ULONG i;
+
+ if (!BrInfo.IsPrimaryDomainController) {
+ return NERR_NotPrimary;
+ }
+
+ if (InfoStruct->Level != 100) {
+ return(ERROR_INVALID_LEVEL);
+ }
+
+ RequestPacket.Type = EnumerateNames;
+ RequestPacket.Version = LMDR_REQUEST_PACKET_VERSION;
+ RequestPacket.Level = 0;
+ RequestPacket.TransportName.Length = 0;
+ RequestPacket.TransportName.Buffer = NULL;
+ RequestPacket.Parameters.EnumerateNames.ResumeHandle = 0;
+
+ Status = DeviceControlGetInfo(BrDgReceiverDeviceHandle,
+ IOCTL_LMDR_ENUMERATE_NAMES,
+ &RequestPacket,
+ sizeof(RequestPacket),
+ (LPVOID *)&NameTable,
+ 0xffffffff,
+ 0,
+ NULL);
+ if (Status != NERR_Success) {
+ return Status;
+ }
+
+ NumberOfOtherDomains = 0;
+ BufferSizeNeeded = 0;
+
+ for (i = 0;i < RequestPacket.Parameters.EnumerateNames.EntriesRead ; i++) {
+ if (NameTable[i].Type == OtherDomain) {
+ NumberOfOtherDomains += 1;
+ BufferSizeNeeded += sizeof(SERVER_INFO_100)+NameTable[i].DGReceiverName.Length+sizeof(TCHAR);
+ }
+ }
+
+ *TotalEntries = NumberOfOtherDomains;
+
+ Buffer = MIDL_user_allocate(BufferSizeNeeded);
+
+ if (Buffer == NULL) {
+ MIDL_user_free(NameTable);
+ return(ERROR_NOT_ENOUGH_MEMORY);
+ }
+
+ ServerInfo = Buffer;
+ BufferEnd = (LPTSTR)((PCHAR)Buffer+BufferSizeNeeded);
+
+ for (i = 0;i < RequestPacket.Parameters.EnumerateNames.EntriesRead ; i++) {
+ if (NameTable[i].Type == OtherDomain) {
+ WCHAR NameBuffer[DNLEN+1];
+
+ //
+ // The name from the browser is not null terminated, so copy it
+ // to a local buffer and null terminate it.
+ //
+
+ RtlCopyMemory(NameBuffer, NameTable[i].DGReceiverName.Buffer, NameTable[i].DGReceiverName.Length);
+
+ NameBuffer[(NameTable[i].DGReceiverName.Length) / sizeof(TCHAR)] = UNICODE_NULL;
+
+ ServerInfo->sv100_platform_id = PLATFORM_ID_OS2;
+
+ ServerInfo->sv100_name = NameBuffer;
+
+ if (!NetpPackString(&ServerInfo->sv100_name,
+ (LPBYTE)(ServerInfo+1),
+ &BufferEnd)) {
+ MIDL_user_free(NameTable);
+ return(NERR_InternalError);
+ }
+
+ ServerInfo += 1;
+ }
+ }
+
+ MIDL_user_free(NameTable);
+
+ InfoStruct->ServerInfo.Level100->Buffer = Buffer;
+ InfoStruct->ServerInfo.Level100->EntriesRead = NumberOfOtherDomains;
+
+ Status = NERR_Success;
+
+ return Status;
+
+}
+
+
+NET_API_STATUS
+BrWanInitialize(
+ VOID
+ )
+{
+ NET_API_STATUS Status;
+
+ if (BrInfo.IsPrimaryDomainController) {
+
+ //
+ // Post a GetMasterAnnouncement request to the bowser on every appropriate
+ // transport.
+ //
+
+ Status = BrPostGetMasterAnnouncement();
+ }
+
+ return Status;
+}
+
+VOID
+BrWanUninitialize(
+ VOID
+ )
+{
+ return;
+
+}
+
+
+
+ NET_API_STATUS
+BrWanMasterInitialize(
+ IN PNETWORK Network
+ )
+/*++
+
+Routine Description:
+ This routine initializes the wan information for a new master.
+
+--*/
+{
+ LPTSTR PDCName = NULL;
+ LPBYTE Buffer = NULL;
+ PSERVER_INFO_100 ServerInfo;
+ NET_API_STATUS Status;
+ ULONG i;
+ ULONG EntriesRead;
+ ULONG TotalEntries;
+
+ //
+ // If we're not on the PDC, then all our initialization has been done.
+ //
+
+ if (BrInfo.IsPrimaryDomainController) {
+ return NERR_Success;
+ }
+
+ Status = NetGetDCName(NULL, NULL, (LPBYTE *)&PDCName);
+
+ //
+ // It is not an error to not be able to contact the PDC.
+ //
+
+ if (Status != NERR_Success) {
+ return NERR_Success;
+ }
+
+ Status = I_BrowserQueryOtherDomains(PDCName, &Buffer, &EntriesRead, &TotalEntries);
+
+ //
+ // We don't need the PDC name any more.
+ //
+
+ NetApiBufferFree(PDCName);
+
+ PDCName = NULL;
+
+ //
+ // It is also not an error to fail to query the other domains from the PDC.
+ //
+
+ if (Status != NERR_Success) {
+ return NERR_Success;
+ }
+
+ if (!LOCK_NETWORK(Network)) {
+ return NERR_InternalError;
+ }
+
+ try {
+ PLIST_ENTRY Entry;
+ PLIST_ENTRY NextEntry;
+
+ //
+ // Scan the other domains list and turn on the active bit for each
+ // other domain.
+ //
+
+ for (Entry = Network->OtherDomainsList.Flink;
+ Entry != &Network->OtherDomainsList ;
+ Entry = Entry->Flink) {
+ PNET_OTHER_DOMAIN OtherDomain = CONTAINING_RECORD(Entry, NET_OTHER_DOMAIN, Next);
+
+ OtherDomain->Flags |= OTHERDOMAIN_INVALID;
+ }
+
+ ServerInfo = (PSERVER_INFO_100)Buffer;
+
+ for (i = 0; i < EntriesRead; i++ ) {
+
+ //
+ // Add this as an other domain.
+ //
+ for (Entry = Network->OtherDomainsList.Flink;
+ Entry != &Network->OtherDomainsList ;
+ Entry = Entry->Flink) {
+ PNET_OTHER_DOMAIN OtherDomain = CONTAINING_RECORD(Entry, NET_OTHER_DOMAIN, Next);
+
+ //
+ // If this name is in the other domains list, it's not invalid
+ // and we should flag that we've seen the domain name.
+ //
+
+ if (!_wcsicmp(OtherDomain->Name, ServerInfo->sv100_name)) {
+ OtherDomain->Flags &= ~OTHERDOMAIN_INVALID;
+ ServerInfo->sv100_name = NULL;
+ }
+ }
+
+ ServerInfo ++;
+ }
+
+ //
+ // Scan the other domains list and remove any domains that are
+ // still marked as invalid.
+ //
+
+ for (Entry = Network->OtherDomainsList.Flink;
+ Entry != &Network->OtherDomainsList ;
+ Entry = NextEntry) {
+ PNET_OTHER_DOMAIN OtherDomain = CONTAINING_RECORD(Entry, NET_OTHER_DOMAIN, Next);
+
+ if (OtherDomain->Flags & OTHERDOMAIN_INVALID) {
+ NextEntry = Entry->Flink;
+
+ //
+ // Remove this entry from the list.
+ //
+
+ RemoveEntryList(Entry);
+
+ BrRemoveOtherDomain(Network, OtherDomain->Name);
+
+ MIDL_user_free(OtherDomain);
+
+ } else {
+ NextEntry = Entry->Flink;
+ }
+ }
+
+ //
+ // Now scan the domain list from the PDC and add any entries that
+ // weren't there already.
+ //
+
+ ServerInfo = (PSERVER_INFO_100)Buffer;
+
+ for (i = 0; i < EntriesRead; i++ ) {
+
+ if (ServerInfo->sv100_name != NULL) {
+ PNET_OTHER_DOMAIN OtherDomain = MIDL_user_allocate(sizeof(NET_OTHER_DOMAIN));
+
+ if (OtherDomain != NULL) {
+
+ Status = BrAddOtherDomain(Network, ServerInfo->sv100_name);
+
+ //
+ // If we were able to add the other domain, add it to our
+ // internal structure.
+ //
+
+ if (Status == NERR_Success) {
+ wcscpy(OtherDomain->Name, ServerInfo->sv100_name);
+ OtherDomain->Flags = 0;
+ InsertHeadList(&Network->OtherDomainsList, &OtherDomain->Next);
+ } else {
+ LPWSTR SubString[1];
+
+ SubString[0] = ServerInfo->sv100_name;
+
+ BrLogEvent(EVENT_BROWSER_OTHERDOMAIN_ADD_FAILED, Status, 1, SubString);
+ }
+ }
+ }
+
+ ServerInfo ++;
+ }
+
+
+
+
+ } finally {
+ UNLOCK_NETWORK(Network);
+
+ if (Buffer != NULL) {
+ MIDL_user_free(Buffer);
+ }
+
+ }
+ return NERR_Success;
+
+}
diff --git a/private/net/svcdlls/browser/server/brwan.h b/private/net/svcdlls/browser/server/brwan.h
new file mode 100644
index 000000000..416fe79f3
--- /dev/null
+++ b/private/net/svcdlls/browser/server/brwan.h
@@ -0,0 +1,68 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ brwan.h
+
+Abstract:
+
+ This module contains definitions for WAN support routines used by the
+ Browser service.
+
+Author:
+
+ Larry Osterman (LarryO) 22-Nov-1992
+
+Revision History:
+
+--*/
+
+#ifndef _BRWAN_
+#define _BRWAN_
+
+#define BROWSER_CONFIG_FILE_SECTION_SIZE 8192
+#define BROWSER_DOMAINS_CONFIG_FILE_NAME TEXT("DOMAINS.INI")
+#define BROWSER_DOMAINS_CONFIG_FILE_SECTION TEXT("Domains")
+
+
+NET_API_STATUS NET_API_FUNCTION
+I_BrowserrQueryOtherDomains(
+ IN BROWSER_IDENTIFY_HANDLE ServerName,
+ IN OUT LPSERVER_ENUM_STRUCT InfoStruct,
+ OUT LPDWORD TotalEntries
+ );
+
+NET_API_STATUS NET_API_FUNCTION
+I_BrowserrQueryPreConfiguredDomains(
+ IN BROWSER_IDENTIFY_HANDLE ServerName,
+ IN OUT LPSERVER_ENUM_STRUCT InfoStruct,
+ OUT LPDWORD TotalEntries
+ );
+
+NET_API_STATUS
+BrWanInitialize(
+ VOID
+ );
+
+NET_API_STATUS
+BrPostGetMasterAnnouncement (
+ VOID
+ );
+
+NET_API_STATUS
+BrWanMasterInitialize(
+ IN PNETWORK Network
+ );
+
+extern
+INTERIM_SERVER_LIST
+BrPreConfiguredInterimServerList;
+
+VOID
+BrWanUninitialize(
+ VOID
+ );
+
+#endif // _BRWAN_
diff --git a/private/net/svcdlls/browser/server/brwins.c b/private/net/svcdlls/browser/server/brwins.c
new file mode 100644
index 000000000..380931f1c
--- /dev/null
+++ b/private/net/svcdlls/browser/server/brwins.c
@@ -0,0 +1,483 @@
+/*++
+
+Copyright (c) 1991-1992 Microsoft Corporation
+
+Module Name:
+
+ brwins.c
+
+Abstract:
+
+ This module contains the routines to interface with the WINS name server.
+
+Author:
+
+ Larry Osterman
+
+Revision History:
+
+--*/
+
+#include "precomp.h"
+#pragma hdrstop
+
+//
+// Addresses of procedures in winsrpc.dll
+//
+
+DWORD (__RPC_API *BrWinsGetBrowserNames)( PWINSINTF_BIND_DATA_T, PWINSINTF_BROWSER_NAMES_T);
+VOID (__RPC_API *BrWinsFreeMem)(LPVOID);
+CHAR BrWinsScopeId[256];
+
+ NET_API_STATUS
+BrOpenNetwork (
+ IN PUNICODE_STRING NetworkName,
+ OUT PHANDLE NetworkHandle
+ )
+/*++
+
+Routine Description:
+
+ This routine opens the NT LAN Man Datagram Receiver driver.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NTSTATUS ntstatus;
+
+ IO_STATUS_BLOCK IoStatusBlock;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+
+ //
+ // Open the transport device directly.
+ //
+ InitializeObjectAttributes(
+ &ObjectAttributes,
+ NetworkName,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL
+ );
+
+ ntstatus = NtOpenFile(
+ NetworkHandle,
+ SYNCHRONIZE,
+ &ObjectAttributes,
+ &IoStatusBlock,
+ 0,
+ 0
+ );
+
+ if (NT_SUCCESS(ntstatus)) {
+ ntstatus = IoStatusBlock.Status;
+ }
+
+ if (! NT_SUCCESS(ntstatus)) {
+ KdPrint(("[Browser] NtOpenFile network driver failed: 0x%08lx\n",
+ ntstatus));
+ }
+
+ return NetpNtStatusToApiStatus(ntstatus);
+}
+
+ NET_API_STATUS
+BrGetWinsServerName(
+ IN PUNICODE_STRING NetworkName,
+ OUT LPWSTR *PrimaryWinsServerAddress,
+ OUT LPWSTR *SecondaryWinsServerAddress
+ )
+{
+ NET_API_STATUS status;
+ HANDLE netHandle;
+ tWINS_ADDRESSES winsAddresses;
+ DWORD bytesReturned;
+ PCHAR p;
+ DWORD count;
+
+ status = BrOpenNetwork(NetworkName, &netHandle);
+
+ if (status != NERR_Success) {
+ return status;
+ }
+
+ if (!DeviceIoControl(netHandle,
+ IOCTL_NETBT_GET_WINS_ADDR,
+ NULL, 0,
+ &winsAddresses, sizeof(winsAddresses),
+ &bytesReturned, NULL)) {
+ status = GetLastError();
+
+ CloseHandle(netHandle);
+ return status;
+ }
+
+ CloseHandle(netHandle);
+
+ *PrimaryWinsServerAddress = MIDL_user_allocate((3+1+3+1+3+1+3+1) * sizeof(TCHAR));
+
+ if (*PrimaryWinsServerAddress == NULL) {
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ p = (PCHAR)&winsAddresses.PrimaryWinsServer;
+
+ count = swprintf(*PrimaryWinsServerAddress, L"%d.%d.%d.%d", p[3] & 0xff, p[2] & 0xff, p[1] & 0xff, p[0] & 0xff);
+
+ ASSERT (count < 3 + 1 + 3 + 1 + 3 + 1 + 3 + 1);
+
+ *SecondaryWinsServerAddress = MIDL_user_allocate((3+1+3+1+3+1+3+1) * sizeof(TCHAR));
+
+ if (*SecondaryWinsServerAddress == NULL) {
+ MIDL_user_free(*PrimaryWinsServerAddress);
+
+ *PrimaryWinsServerAddress = NULL;
+
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ p = (PCHAR)&winsAddresses.BackupWinsServer;
+
+ count = swprintf(*SecondaryWinsServerAddress, L"%d.%d.%d.%d", p[3] & 0xff, p[2] & 0xff, p[1] & 0xff, p[0] & 0xff);
+
+ ASSERT (count < 3 + 1 + 3 + 1 + 3 + 1 + 3 + 1);
+
+ return NERR_Success;
+}
+
+
+
+
+VOID
+BrWinsGetScopeId(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ This code was stolen from the nbtstat command.
+
+ This procedure save the netbt scope id in the global variable BrWinsScopeId.
+ On any error, a NULL scope ID will be used.
+
+Arguments:
+
+
+Return Value:
+
+ 0 if successful, -1 otherwise.
+
+--*/
+
+{
+ DWORD WinStatus;
+
+ HKEY Key;
+ DWORD BufferSize;
+ DWORD Type;
+
+
+
+ //
+ // Open the registry key containing the scope id.
+ //
+ WinStatus = RegOpenKeyExA(
+ HKEY_LOCAL_MACHINE,
+ "system\\currentcontrolset\\services\\netbt\\parameters",
+ 0,
+ KEY_READ,
+ &Key);
+
+ if ( WinStatus != ERROR_SUCCESS) {
+ *BrWinsScopeId = '\0';
+ return;
+ }
+
+
+ //
+ // Read the scope id value.
+ //
+ BufferSize = sizeof(BrWinsScopeId)-1;
+
+ WinStatus = RegQueryValueExA(
+ Key,
+ "ScopeId",
+ NULL,
+ &Type,
+ (LPBYTE) &BrWinsScopeId[1],
+ &BufferSize );
+
+ (VOID) RegCloseKey( Key );
+
+ if ( WinStatus != ERROR_SUCCESS) {
+ *BrWinsScopeId = '\0';
+ return;
+ }
+
+ //
+ // If there is no scope id (just a zero byte),
+ // just return an empty string.
+ // otherise
+ // return a '.' in front of the scope id.
+ //
+ // This matches what WINS returns from WinsGetBrowserNames.
+ //
+
+ if ( BufferSize == 0 || BrWinsScopeId[1] == '\0' ) {
+ *BrWinsScopeId = '\0';
+ } else {
+ *BrWinsScopeId = '.';
+ }
+
+ return;
+
+}
+
+DWORD
+BrLoadWinsrpcDll(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This routine loads the WinsRpc DLL and locates all the procedures the browser calls
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ Status of the operation
+
+--*/
+{
+ DWORD WinStatus;
+ HANDLE hModule;
+
+ //
+ // If the library is already loaded,
+ // just return.
+ //
+
+ if (BrWinsGetBrowserNames != NULL) {
+ return NERR_Success;
+ }
+
+ //
+ // Load the library.
+ //
+
+ hModule = LoadLibraryA("winsrpc");
+
+ if (NULL == hModule) {
+ WinStatus = GetLastError();
+ return WinStatus;
+ }
+
+ //
+ // Locate all of the procedures needed.
+ //
+
+ BrWinsGetBrowserNames =
+ (DWORD (__RPC_API *)( PWINSINTF_BIND_DATA_T, PWINSINTF_BROWSER_NAMES_T))
+ GetProcAddress( hModule, "WinsGetBrowserNames" );
+
+ if (BrWinsGetBrowserNames == NULL) {
+ WinStatus = GetLastError();
+ FreeLibrary( hModule );
+ return WinStatus;
+ }
+
+
+ BrWinsFreeMem =
+ (VOID (__RPC_API *)(LPVOID))
+ GetProcAddress( hModule, "WinsFreeMem" );
+
+ if (BrWinsFreeMem == NULL) {
+ WinStatus = GetLastError();
+ FreeLibrary( hModule );
+ return WinStatus;
+ }
+
+ //
+ // Initialize BrWinsScopeId
+ //
+
+ BrWinsGetScopeId();
+
+ return NERR_Success;
+}
+
+ NET_API_STATUS
+BrQuerySpecificWinsServer(
+ IN LPWSTR WinsServerAddress,
+ OUT PVOID *WinsServerList,
+ OUT PDWORD EntriesInList,
+ OUT PDWORD TotalEntriesInList
+ )
+{
+ WINSINTF_BIND_DATA_T bindData;
+ NET_API_STATUS status;
+ PVOID winsDomainInformation = NULL;
+ PSERVER_INFO_101 serverInfo;
+ WINSINTF_BROWSER_NAMES_T names;
+ DWORD i,j;
+ LPWSTR serverInfoEnd;
+ DWORD bufferSize;
+
+ //
+ // Load winsrpc.dll
+ //
+
+ status = BrLoadWinsrpcDll();
+
+ if (status != NERR_Success) {
+ return status;
+ }
+
+ //
+ // Get the list of domain names from WINS
+ //
+
+ bindData.fTcpIp = TRUE;
+ bindData.pServerAdd = (LPSTR)WinsServerAddress;
+ names.pInfo = NULL;
+
+ status = (*BrWinsGetBrowserNames)(&bindData, &names);
+
+ if ( status != NERR_Success ) {
+ return status;
+ }
+
+
+ //
+ // Convert the WINS domain list into server list format.
+ //
+ bufferSize = (sizeof(SERVER_INFO_101) + ((CNLEN + 1 + 1) *sizeof(WCHAR))) * names.EntriesRead;
+
+ (*WinsServerList) = winsDomainInformation = MIDL_user_allocate( bufferSize );
+
+ if (winsDomainInformation == NULL) {
+ (*BrWinsFreeMem)(names.pInfo);
+
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ serverInfo = winsDomainInformation;
+ serverInfoEnd = (LPWSTR)((PCHAR)winsDomainInformation + bufferSize);
+
+ *TotalEntriesInList = names.EntriesRead;
+ *EntriesInList = 0;
+
+ for (i = 0; i < names.EntriesRead ; i += 1) {
+ OEM_STRING OemString;
+ UNICODE_STRING UnicodeString;
+ CHAR WinsName[CNLEN+1];
+
+ //
+ // Make up information about this domain.
+ //
+ serverInfo->sv101_platform_id = PLATFORM_ID_NT;
+ serverInfo->sv101_version_major = 0;
+ serverInfo->sv101_version_minor = 0;
+ serverInfo->sv101_type = SV_TYPE_DOMAIN_ENUM | SV_TYPE_NT;
+
+ //
+ // Filter out those entries whose scope id doesn't match ours
+ //
+
+ if ( lstrlenA(names.pInfo[i].pName) >= NETBIOS_NAME_LEN ) {
+ if ( lstrcmpA( &names.pInfo[i].pName[NETBIOS_NAME_LEN], BrWinsScopeId) != 0 ) {
+ continue;
+ }
+ }
+
+
+
+ //
+ // Truncate the 0x1b and spaces from the domain name.
+ //
+ lstrcpynA(WinsName, names.pInfo[i].pName, sizeof(WinsName) );
+ WinsName[CNLEN] = '\0';
+
+ for (j = CNLEN-1 ; j ; j -= 1 ) {
+ if (WinsName[j] != ' ') {
+ WinsName[j+1] = '\0';
+ break;
+ }
+ }
+
+ RtlInitString(&OemString, WinsName);
+
+ status = RtlOemStringToUnicodeString(&UnicodeString, &OemString, TRUE);
+
+ if (!NT_SUCCESS(status)) {
+ //
+ // Ignore bogus entries
+ //
+ continue;
+ }
+
+ serverInfo->sv101_name = UnicodeString.Buffer;
+
+ if (NetpPackString(&serverInfo->sv101_name,
+ (PCHAR )(&serverInfo+1),
+ &serverInfoEnd)) {
+
+ serverInfo->sv101_comment = L"";
+
+ if (NetpPackString(&serverInfo->sv101_comment,
+ (PCHAR )(&serverInfo+1),
+ &serverInfoEnd)) {
+ *EntriesInList += 1;
+ }
+
+ }
+ RtlFreeUnicodeString(&UnicodeString);
+
+ serverInfo += 1;
+
+ }
+
+ (*BrWinsFreeMem)(names.pInfo);
+
+ return NERR_Success;
+}
+
+
+ NET_API_STATUS
+BrQueryWinsServer(
+ IN LPWSTR PrimaryWinsServerAddress,
+ IN LPWSTR SecondaryWinsServerAddress,
+ OUT PVOID WinsServerList,
+ OUT PDWORD EntriesInList,
+ OUT PDWORD TotalEntriesInList
+ )
+{
+ NET_API_STATUS status;
+ status = BrQuerySpecificWinsServer(PrimaryWinsServerAddress,
+ WinsServerList,
+ EntriesInList,
+ TotalEntriesInList);
+
+ if (status == NERR_Success) {
+ return status;
+ }
+
+ status = BrQuerySpecificWinsServer(SecondaryWinsServerAddress,
+ WinsServerList,
+ EntriesInList,
+ TotalEntriesInList);
+
+ return status;
+}
diff --git a/private/net/svcdlls/browser/server/brwins.h b/private/net/svcdlls/browser/server/brwins.h
new file mode 100644
index 000000000..39d95666d
--- /dev/null
+++ b/private/net/svcdlls/browser/server/brwins.h
@@ -0,0 +1,46 @@
+/*++
+
+Copyright (c) 1991-1992 Microsoft Corporation
+
+Module Name:
+
+ brwins.c
+
+Abstract:
+
+ This module contains the routines to interface with the WINS name server.
+
+Author:
+
+ Larry Osterman
+
+Revision History:
+
+--*/
+#ifndef _BRWINS_
+#define _BRWINS_
+
+NET_API_STATUS
+BrGetWinsServerName(
+ IN PUNICODE_STRING Network,
+ OUT LPTSTR *PrimaryWinsServerAddress,
+ OUT LPTSTR *SecondaryWinsServerAddress
+ );
+
+NET_API_STATUS
+BrQueryWinsServer(
+ IN LPTSTR PrimaryWinsServerAddress,
+ IN LPTSTR SecondaryWinsServerAddress,
+ OUT PVOID WinsServerList,
+ OUT PDWORD EntriesInList,
+ OUT PDWORD TotalEntriesInList
+ );
+
+NET_API_STATUS
+BrQuerySpecificWinsServer(
+ IN LPTSTR WinsServerAddress,
+ OUT PVOID *WinsServerList,
+ OUT PDWORD EntriesInList,
+ OUT PDWORD TotalEntriesInList
+ );
+#endif
diff --git a/private/net/svcdlls/browser/server/daytona/browser.def b/private/net/svcdlls/browser/server/daytona/browser.def
new file mode 100644
index 000000000..a1243ec13
--- /dev/null
+++ b/private/net/svcdlls/browser/server/daytona/browser.def
@@ -0,0 +1,7 @@
+NAME browser.dll
+
+DESCRIPTION 'NT LAN Manager Workstation Service'
+
+EXPORTS
+ ServiceEntry
+ I_BrowserServerEnumForXactsrv
diff --git a/private/net/svcdlls/browser/server/daytona/browser.prf b/private/net/svcdlls/browser/server/daytona/browser.prf
new file mode 100644
index 000000000..b65e0b607
--- /dev/null
+++ b/private/net/svcdlls/browser/server/daytona/browser.prf
@@ -0,0 +1,33 @@
+BrQueueWorkItem@4
+BrWorkerThread@4
+BrIssueAsyncBrowserIoControl@12
+CompleteAsyncBrowserIoControl@12
+;GetBrowserServerListRequestCompletion@4
+;PostGetBrowserServerList@8
+;AddBackupToBackupList@12
+
+I_BrowserrServerEnum@36
+I_BrowserServerEnumForXactsrv@48
+BrNetServerEnum@36
+BrRetrieveServerListForMaster@40
+BrRetrieveServerListForBackup@28
+BrLookupAndAllocateCachedEntry@16
+BrAgeResponseCache@4
+I_BrowserrResetStatistics@4
+I_BrowserrQueryStatistics@8
+BrAllocateResponseCacheEntry@16
+BrDestroyCacheEntry@4
+BrDestroyResponseCache@4
+BrFindNetwork@4
+
+MergeServerList@20
+PackServerList@28
+UpdateInterimServerListElement@16
+AllocateInterimServerListEntry@8
+BrGetLocalBrowseList@28
+DeviceControlGetInfo@32
+BrDgReceiverIoControl@28
+BrBrowseTableInsertRoutine@8
+BrBrowseTableUpdateRoutine@8
+BrDomainTableInsertRoutine@8
+BrDomainTableUpdateRoutine@8
diff --git a/private/net/svcdlls/browser/server/daytona/makefile b/private/net/svcdlls/browser/server/daytona/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/net/svcdlls/browser/server/daytona/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/net/svcdlls/browser/server/daytona/makefile.inc b/private/net/svcdlls/browser/server/daytona/makefile.inc
new file mode 100644
index 000000000..bb010490b
--- /dev/null
+++ b/private/net/svcdlls/browser/server/daytona/makefile.inc
@@ -0,0 +1,10 @@
+.SUFFIXES: .mdl
+
+{..\}.mdl.c:
+ type << > $(@B).c
+#include "precomp.h"
+#pragma hdrstop
+<<
+ type $(<R).mdl >> $(@B).c
+
+bowser_s.c: ..\bowser_s.mdl
diff --git a/private/net/svcdlls/browser/server/daytona/sources b/private/net/svcdlls/browser/server/daytona/sources
new file mode 100644
index 000000000..f0d7e2d44
--- /dev/null
+++ b/private/net/svcdlls/browser/server/daytona/sources
@@ -0,0 +1,83 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+
+Author:
+
+ Steve Wood (stevewo) 12-Apr-1990
+
+NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl
+
+!ENDIF
+
+MAJORCOMP=net
+MINORCOMP=browser
+
+TARGETNAME=browser
+TARGETPATH=$(BASEDIR)\public\sdk\lib
+TARGETTYPE=DYNLINK
+
+TARGETLIBS= \
+ ..\..\common\daytona\obj\*\brcommon.lib \
+ $(BASEDIR)\public\sdk\lib\*\kernel32.lib \
+ $(BASEDIR)\public\sdk\lib\*\netapi32.lib \
+ $(BASEDIR)\public\sdk\lib\*\rpcutil.lib \
+ $(BASEDIR)\public\sdk\lib\*\rpcndr.lib \
+ $(BASEDIR)\public\sdk\lib\*\rpcrt4.lib \
+ $(BASEDIR)\public\sdk\lib\*\advapi32.lib \
+ $(BASEDIR)\Public\Sdk\Lib\*\xactsrv.lib \
+ $(BASEDIR)\Public\Sdk\Lib\*\wsock32.lib \
+
+
+INCLUDES=..;..\..;..\..\..\..\inc;..\..\..\..\..\inc;..\..\..\..\api;..\..\..\..\xactsrv
+
+NTPROFILEINPUT=1
+
+!IFNDEF DISABLE_NET_UNICODE
+UNICODE=1
+NET_C_DEFINES=-DUNICODE
+!ENDIF
+
+USE_CRTDLL=1
+
+SOURCES=bowsvc.rc \
+ brmain.c \
+ browser.c \
+ brdevice.c \
+ brutil.c \
+ brconfig.c \
+ brdmmstr.c \
+ brmaster.c \
+ browsnet.c \
+ browsdom.c \
+ brwan.c \
+ brwins.c \
+ bowqueue.c \
+ srvenum.c \
+ bowser_s.c
+
+MSC_WARNING_LEVEL=/W3 /WX
+
+#386_FLAGS=-Gh
+
+UMTYPE=windows
+
+UMLIBS=
+
+PRECOMPILED_INCLUDE=..\precomp.h
+PRECOMPILED_PCH=precomp.pch
+PRECOMPILED_OBJ=precomp.obj
+
+NTTARGETFILE0=bowser_s.c
diff --git a/private/net/svcdlls/browser/server/dirs b/private/net/svcdlls/browser/server/dirs
new file mode 100644
index 000000000..945735ae2
--- /dev/null
+++ b/private/net/svcdlls/browser/server/dirs
@@ -0,0 +1,25 @@
+!IF 0
+
+Copyright (c) 1994 Microsoft Corporation
+
+Module Name:
+
+ dirs.
+
+Abstract:
+
+ This file specifies the subdirectories of the current directory that
+ contain component makefiles. This was necessitated by the need to
+ build a and nt rdr.
+
+Author:
+
+ Milan Shah (April 20, 1994)
+
+NOTE: Commented description of this file is in \nt\bak\bin\dirs.tpl
+
+!ENDIF
+
+DIRS=daytona
+
+OPTIONAL_DIRS=
diff --git a/private/net/svcdlls/browser/server/precomp.h b/private/net/svcdlls/browser/server/precomp.h
new file mode 100644
index 000000000..f5a71b477
--- /dev/null
+++ b/private/net/svcdlls/browser/server/precomp.h
@@ -0,0 +1,38 @@
+#include "br.h"
+#include <string.h>
+#include "bowser.h"
+#include <stdlib.h>
+#include <ctype.h>
+#include "brdevice.h"
+#include "brconfig.h"
+#include "brwins.h"
+#include <lmaccess.h>
+#include <ntlsa.h>
+#include "config.h"
+#include "confname.h"
+#include <winreg.h>
+#include <netevent.h>
+#include "brutil.h"
+#include "hostannc.h"
+#include "tstring.h"
+#include "brsec.h"
+#include "services.h"
+#include "srvann.h"
+#include "names.h"
+#include "browsdom.h"
+#include "netlib.h"
+#include <time.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <lmshare.h>
+#include <lmapibuf.h>
+#include <lmserver.h>
+#include "xstypes.h"
+#include "xsprocsp.h"
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windows.h>
+#include <nb30.h>
+#include <winsintf.h>
+#include <nbtioctl.h>
diff --git a/private/net/svcdlls/browser/server/srvenum.c b/private/net/svcdlls/browser/server/srvenum.c
new file mode 100644
index 000000000..e6d0d6bef
--- /dev/null
+++ b/private/net/svcdlls/browser/server/srvenum.c
@@ -0,0 +1,2263 @@
+/*++
+
+Copyright (c) 1991-1992 Microsoft Corporation
+
+Module Name:
+
+ srvenum.c
+
+Abstract:
+
+ This module contains the worker routine for the NetServerEnum API
+ implemented by the Workstation service.
+
+Author:
+
+ Rita Wong (ritaw) 25-Mar-1991
+
+Revision History:
+
+--*/
+
+#include "precomp.h"
+#pragma hdrstop
+
+NET_API_STATUS NET_API_FUNCTION
+BrNetServerEnum(
+ IN PNETWORK Network OPTIONAL,
+ IN LPCWSTR ClientName OPTIONAL,
+ IN ULONG Level,
+ IN DWORD PreferedMaximumLength,
+ OUT PVOID *Buffer,
+ OUT LPDWORD EntriesRead,
+ OUT LPDWORD TotalEntries,
+ IN DWORD ServerType,
+ IN LPCWSTR Domain,
+ IN LPCWSTR FirstNameToReturn OPTIONAL
+ );
+
+NET_API_STATUS
+BrRetrieveServerListForMaster(
+ IN PNETWORK Network,
+ IN OUT PVOID *Buffer,
+ OUT PDWORD EntriesRead,
+ OUT PDWORD TotalEntries,
+ IN DWORD Level,
+ IN DWORD ServerType,
+ IN DWORD PreferedMaximumLength,
+ IN BOOLEAN LocalListOnly,
+ IN LPTSTR ClientName,
+ IN LPTSTR DomainName,
+ IN LPCWSTR FirstNameToReturn
+ );
+
+NET_API_STATUS
+BrRetrieveServerListForBackup(
+ IN PNETWORK Network,
+ IN OUT PVOID *Buffer,
+ OUT PDWORD EntriesRead,
+ OUT PDWORD TotalEntries,
+ IN DWORD Level,
+ IN DWORD ServerType,
+ IN DWORD PreferedMaximumLength,
+ IN LPCWSTR FirstNameToReturn
+ );
+
+NET_API_STATUS NET_API_FUNCTION
+I_BrowserrServerEnum(
+ IN LPTSTR ServerName OPTIONAL,
+ IN LPTSTR TransportName OPTIONAL,
+ IN LPTSTR ClientName OPTIONAL,
+ IN OUT LPSERVER_ENUM_STRUCT InfoStruct,
+ IN DWORD PreferedMaximumLength,
+ OUT LPDWORD TotalEntries,
+ IN DWORD ServerType,
+ IN LPTSTR Domain,
+ IN OUT LPDWORD ResumeHandle OPTIONAL
+ )
+/*++
+
+Routine Description:
+
+ This function is the NetServerEnum entry point in the Workstation service.
+
+Arguments:
+
+ ServerName - Supplies the name of server to execute this function
+
+ TransportName - Supplies the name of xport on which to enumerate servers
+
+ InfoStruct - This structure supplies the level of information requested,
+ returns a pointer to the buffer allocated by the Workstation service
+ which contains a sequence of information structure of the specified
+ information level, and returns the number of entries read. The buffer
+ pointer is set to NULL if return code is not NERR_Success or
+ ERROR_MORE_DATA, or if EntriesRead returned is 0. The EntriesRead
+ value is only valid if the return code is NERR_Success or
+ ERROR_MORE_DATA.
+
+ PreferedMaximumLength - Supplies the number of bytes of information
+ to return in the buffer. If this value is MAXULONG, we will try
+ to return all available information if there is enough memory
+ resource.
+
+ TotalEntries - Returns the total number of entries available. This value
+ is returned only if the return code is NERR_Success or ERROR_MORE_DATA.
+
+ ServerType - Supplies the type of server to enumerate.
+
+ Domain - Supplies the name of one of the active domains to enumerate the
+ servers from. If NULL, servers from the primary domain, logon domain
+ and other domains are enumerated.
+
+ ResumeHandle - Supplies and returns the point to continue with enumeration.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NET_API_STATUS NetStatus;
+
+ NetStatus = I_BrowserrServerEnumEx(
+ ServerName,
+ TransportName,
+ ClientName,
+ InfoStruct,
+ PreferedMaximumLength,
+ TotalEntries,
+ ServerType,
+ Domain,
+ NULL ); // NULL FirstNameToReturn
+
+ if (ARGUMENT_PRESENT(ResumeHandle)) {
+ *ResumeHandle = 0;
+ }
+
+ return NetStatus;
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+I_BrowserrServerEnumEx(
+ IN LPTSTR ServerName OPTIONAL,
+ IN LPTSTR TransportName OPTIONAL,
+ IN LPTSTR ClientName OPTIONAL,
+ IN OUT LPSERVER_ENUM_STRUCT InfoStruct,
+ IN DWORD PreferedMaximumLength,
+ OUT LPDWORD TotalEntries,
+ IN DWORD ServerType,
+ IN LPTSTR Domain,
+ IN LPTSTR FirstNameToReturnArg
+ )
+/*++
+
+Routine Description:
+
+ This function is the NetServerEnum entry point in the Workstation service.
+
+Arguments:
+
+ ServerName - Supplies the name of server to execute this function
+
+ TransportName - Supplies the name of xport on which to enumerate servers
+
+ InfoStruct - This structure supplies the level of information requested,
+ returns a pointer to the buffer allocated by the Workstation service
+ which contains a sequence of information structure of the specified
+ information level, and returns the number of entries read. The buffer
+ pointer is set to NULL if return code is not NERR_Success or
+ ERROR_MORE_DATA, or if EntriesRead returned is 0. The EntriesRead
+ value is only valid if the return code is NERR_Success or
+ ERROR_MORE_DATA.
+
+ PreferedMaximumLength - Supplies the number of bytes of information
+ to return in the buffer. If this value is MAXULONG, we will try
+ to return all available information if there is enough memory
+ resource.
+
+ TotalEntries - Returns the total number of entries available. This value
+ is returned only if the return code is NERR_Success or ERROR_MORE_DATA.
+
+ ServerType - Supplies the type of server to enumerate.
+
+ Domain - Supplies the name of one of the active domains to enumerate the
+ servers from. If NULL, servers from the primary domain, logon domain
+ and other domains are enumerated.
+
+ FirstNameToReturnArg - Supplies the name of the first domain or server entry to return.
+ The caller can use this parameter to implement a resume handle of sorts by passing
+ the name of the last entry returned on a previous call. (Notice that the specified
+ entry will, also, be returned on this call unless it has since been deleted.)
+ Pass NULL to start with the first entry available.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NET_API_STATUS status;
+ PVOID Buffer = NULL;
+ ULONG EntriesRead;
+ BOOLEAN NetworkLocked = FALSE;
+ PNETWORK Network = NULL;
+ UNICODE_STRING NetworkName;
+ WCHAR FirstNameToReturn[DNLEN+1];
+#if DBG
+ DWORD StartTickCount, EndTickCount;
+#endif
+
+ UNREFERENCED_PARAMETER(ServerName);
+
+#if DBG
+ StartTickCount = GetTickCount();
+#endif
+
+ if (ARGUMENT_PRESENT(TransportName)) {
+ RtlInitUnicodeString(&NetworkName, TransportName);
+
+ Network = BrFindNetwork(&NetworkName);
+
+ if (Network == NULL) {
+ KdPrint(("Network %ws not found.\n", TransportName));
+ return(ERROR_FILE_NOT_FOUND);
+ }
+
+ //
+ // If the caller has asked us to use the alternate transport,
+ // do so.
+ //
+
+ if ((ServerType != SV_TYPE_ALL) &&
+ (ServerType & SV_TYPE_ALTERNATE_XPORT) ) {
+
+ //
+ // If this transport has an alternate network, then actually
+ // query the alternate name, not the real one.
+ //
+
+ if (Network->AlternateNetwork != NULL) {
+ Network = Network->AlternateNetwork;
+ } else {
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ ServerType &= ~SV_TYPE_ALTERNATE_XPORT;
+
+ if (ServerType == 0) {
+ ServerType = SV_TYPE_ALL;
+ }
+
+ }
+
+ if (!LOCK_NETWORK_SHARED(Network)) {
+ return NERR_InternalError;
+ }
+
+ NetworkLocked = TRUE;
+
+ if (!(Network->Role & (ROLE_BACKUP | ROLE_MASTER))) {
+
+ dprintf(SERVER_ENUM, ("Browse request received from %ws, but not backup or master\n", ClientName));
+
+ status = ERROR_REQ_NOT_ACCEP;
+ goto Cleanup;
+ }
+
+ } else {
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ //
+ // Canonicalize the FirstNameToReturn.
+ //
+
+ if (ARGUMENT_PRESENT(FirstNameToReturnArg) && *FirstNameToReturnArg != L'\0') {
+
+ if ( I_NetNameCanonicalize(
+ NULL,
+ (LPWSTR) FirstNameToReturnArg,
+ FirstNameToReturn,
+ sizeof(FirstNameToReturn),
+ NAMETYPE_WORKGROUP,
+ LM2X_COMPATIBLE
+ ) != NERR_Success) {
+ status = ERROR_INVALID_PARAMETER;
+ goto Cleanup;
+ }
+
+ } else {
+ FirstNameToReturn[0] = L'\0';
+ }
+
+ status = BrNetServerEnum(Network,
+ ClientName,
+ InfoStruct->Level,
+ PreferedMaximumLength,
+ &Buffer,
+ &EntriesRead,
+ TotalEntries,
+ ServerType,
+ Domain,
+ FirstNameToReturn );
+
+ //
+ // Return output parameters other than output buffer from request packet.
+ //
+
+ if (status == NERR_Success || status == ERROR_MORE_DATA) {
+
+ if (InfoStruct->Level == 101) {
+ InfoStruct->ServerInfo.Level101->Buffer = (PSERVER_INFO_101) Buffer;
+ InfoStruct->ServerInfo.Level101->EntriesRead = EntriesRead;
+ } else {
+ InfoStruct->ServerInfo.Level100->Buffer = (PSERVER_INFO_100) Buffer;
+ InfoStruct->ServerInfo.Level100->EntriesRead = EntriesRead;
+ }
+
+ }
+
+Cleanup:
+ if (NetworkLocked) {
+ UNLOCK_NETWORK(Network);
+ }
+
+#if DBG
+ EndTickCount = GetTickCount();
+
+ dprintf(SERVER_ENUM, ("Browse request for %ws on %ws took %ld milliseconds\n", ClientName, TransportName, EndTickCount - StartTickCount));
+#endif
+
+ return status;
+
+}
+
+ WORD
+I_BrowserServerEnumForXactsrv(
+ IN LPCWSTR TransportName OPTIONAL,
+ IN LPCWSTR ClientName OPTIONAL,
+
+ IN ULONG Level,
+ IN USHORT ClientLevel,
+
+ IN PVOID ClientBuffer,
+ IN WORD BufferLength,
+ IN DWORD PreferedMaximumLength,
+
+ OUT LPDWORD EntriesFilled,
+ OUT LPDWORD TotalEntries,
+
+ IN DWORD ServerType,
+ IN LPCWSTR Domain,
+ IN LPCWSTR FirstNameToReturnArg OPTIONAL,
+
+ OUT PWORD Converter
+ )
+/*++
+
+Routine Description:
+
+ This function is a private entrypoint for Xactsrv that bypasses RPC
+ entirely.
+
+Arguments:
+
+ TransportName - Supplies the name of xport on which to enumerate servers
+
+ ClientName - Supplies the name of the client that requested the data
+
+ Level - Level of data requested.
+ ClientLevel - Level requested by the client.
+
+ ClientBuffer - Output buffer allocated to hold the buffer.
+ BufferLength - Size of ClientBuffer
+ PreferedMaximumLength - Prefered maximum size of Client buffer if we are
+ going to use the NT form of the buffer
+
+ OUT LPDWORD EntriesFilled - The entries packed into ClientBuffer
+ OUT LPDWORD TotalEntries - The total # of entries available.
+
+ IN DWORD ServerType - Server type mask.
+ IN LPTSTR Domain - Domain to query
+
+ OUT PWORD Converter - Magic constant from Xactsrv that allows the client
+ to convert the response buffer.
+
+Return Value:
+
+ WORD - NERR_Success or reason for failure.
+
+--*/
+{
+ NET_API_STATUS status;
+ BOOLEAN networkLocked = FALSE;
+ PNETWORK network = NULL;
+ UNICODE_STRING networkName;
+ PVOID buffer = NULL;
+ DWORD entriesRead;
+ PCACHED_BROWSE_RESPONSE response = NULL;
+ WCHAR FirstNameToReturn[DNLEN+1];
+
+#if DBG
+ DWORD startTickCount, endTickCount;
+
+ startTickCount = GetTickCount();
+
+#endif
+
+ //
+ // If the browser isn't up and we get one of these calls, fail the API
+ // immediately.
+ //
+
+ if (BrGlobalData.Status.dwCurrentState != SERVICE_RUNNING) {
+ dprintf(SERVER_ENUM, ("Browse request from %ws received, but browser not running\n", ClientName));
+ return NERR_ServiceNotInstalled;
+ }
+
+ if (ARGUMENT_PRESENT(TransportName)) {
+ RtlInitUnicodeString(&networkName, TransportName);
+
+ dprintf(SERVER_ENUM, ("Look up network %ws for %ws\n", TransportName, ClientName));
+ network = BrFindNetwork(&networkName);
+
+ if (network == NULL) {
+ KdPrint(("Network %ws not found.\n", TransportName));
+ return(ERROR_FILE_NOT_FOUND);
+ }
+
+
+ //
+ // If the caller has asked us to use the alternate transport,
+ // do so.
+ //
+
+ if ((ServerType != SV_TYPE_ALL) &&
+ (ServerType & SV_TYPE_ALTERNATE_XPORT) ) {
+
+ //
+ // If this transport has an alternate network, then actually
+ // query the alternate name, not the real one.
+ //
+
+ if (network->AlternateNetwork != NULL) {
+ network = network->AlternateNetwork;
+ } else {
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ ServerType &= ~SV_TYPE_ALTERNATE_XPORT;
+
+ if (ServerType == 0) {
+ ServerType = SV_TYPE_ALL;
+ }
+
+ }
+
+ if (!LOCK_NETWORK_SHARED(network)) {
+ return NERR_InternalError;
+ }
+
+ networkLocked = TRUE;
+
+ dprintf(SERVER_ENUM, ("Network %ws found for %ws\n", TransportName, ClientName));
+
+ if (!(network->Role & (ROLE_BACKUP | ROLE_MASTER))) {
+
+ UNLOCK_NETWORK(network);
+
+ networkLocked = FALSE;
+
+ dprintf(SERVER_ENUM, ("Browse request received from %ws, but not backup or master\n", ClientName));
+
+ return(ERROR_REQ_NOT_ACCEP);
+ }
+
+ } else {
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ //
+ // If we weren't able to find a cached response buffer, or
+ // if the cached response didn't have a buffer allocated for it,
+ // we need to process the browse request.
+ //
+
+ if (network->Role & ROLE_MASTER) {
+
+ //
+ // Check to see if we should flush the cache at this time. If
+ // we should, we need to release the network and re-acquire it
+ // exclusively, because we use the network lock to protect
+ // enumerations from flushes.
+ //
+
+
+ if ((BrCurrentSystemTime() - network->TimeCacheFlushed) > BrInfo.DriverQueryFrequency) {
+
+ UNLOCK_NETWORK(network);
+
+ networkLocked = FALSE;
+
+ if (!LOCK_NETWORK(network)) {
+ return NERR_InternalError;
+ }
+
+ networkLocked = TRUE;
+
+ //
+ // We're running on a master browser, and we found a cached browse
+ // request. See if we can safely use this cached value, or if we
+ // should go to the driver for it.
+ //
+
+ EnterCriticalSection(&network->ResponseCacheLock);
+
+ if ((BrCurrentSystemTime() - network->TimeCacheFlushed) > BrInfo.DriverQueryFrequency) {
+
+ dprintf(SERVER_ENUM, ("Master: Flushing cache for %ws\n", TransportName));
+
+ network->TimeCacheFlushed = BrCurrentSystemTime();
+
+ BrAgeResponseCache( network );
+ }
+
+ LeaveCriticalSection(&network->ResponseCacheLock);
+ }
+ }
+
+ //
+ // Canonicalize the FirstNameToReturn.
+ //
+
+ if (ARGUMENT_PRESENT(FirstNameToReturnArg) && *FirstNameToReturnArg != L'\0') {
+
+ if ( I_NetNameCanonicalize(
+ NULL,
+ (LPWSTR) FirstNameToReturnArg,
+ FirstNameToReturn,
+ sizeof(FirstNameToReturn),
+ NAMETYPE_WORKGROUP,
+ LM2X_COMPATIBLE
+ ) != NERR_Success) {
+ status = ERROR_INVALID_PARAMETER;
+ goto Cleanup;
+ }
+
+ } else {
+ FirstNameToReturn[0] = L'\0';
+ }
+
+ if (!ARGUMENT_PRESENT(Domain) ||
+ !STRICMP(Domain, BrInfo.BrPrimaryDomainName)) {
+
+ dprintf(SERVER_ENUM,("Look up %ws/0x%x/%d/%x.\n", TransportName, ServerType, ClientLevel, BufferLength));
+
+ //
+ // This request is for our primary domain. Look up a cached response
+ // entry. If non is found for this request, allocate a new one and
+ // return it.
+ //
+
+ response = BrLookupAndAllocateCachedEntry(
+ network,
+ ServerType,
+ BufferLength,
+ ClientLevel,
+ FirstNameToReturn
+ );
+
+
+ }
+
+ EnterCriticalSection(&network->ResponseCacheLock);
+ if ((response == NULL)
+
+ ||
+
+ (response->Buffer == NULL)) {
+ LeaveCriticalSection(&network->ResponseCacheLock);
+
+ dprintf(SERVER_ENUM,("Cached entry not found, or hit count too low. Retrieve actual list for %ws on %ws\n", ClientName, TransportName));
+
+ status = BrNetServerEnum(network,
+ ClientName,
+ Level,
+ PreferedMaximumLength,
+ &buffer,
+ &entriesRead,
+ TotalEntries,
+ ServerType,
+ Domain,
+ FirstNameToReturn
+ );
+
+ if (status == NERR_Success || status == ERROR_MORE_DATA) {
+
+ dprintf(SERVER_ENUM,("Convert NT buffer to Xactsrv buffer for %ws on %ws\n", ClientName, TransportName));
+
+ status = XsConvertServerEnumBuffer(
+ buffer,
+ entriesRead,
+ TotalEntries,
+ ClientLevel,
+ ClientBuffer,
+ BufferLength,
+ EntriesFilled,
+ Converter);
+
+
+ if (status == NERR_Success || status == ERROR_MORE_DATA) {
+
+ dprintf(SERVER_ENUM,("Conversion done for %ws on %ws\n", ClientName, TransportName));
+
+ EnterCriticalSection(&network->ResponseCacheLock);
+
+ if ((response != NULL) &&
+
+ (response->Buffer == NULL) &&
+
+ (response->TotalHitCount >= BrInfo.CacheHitLimit)) {
+
+ dprintf(SERVER_ENUM,("Save contents of server list on %ws for 0x%x/%d/%x.\n", TransportName, ServerType, ClientLevel, BufferLength));
+
+ response->Buffer = MIDL_user_allocate(BufferLength);
+
+ if ( response->Buffer != NULL ) {
+
+ //
+ // We successfully allocated the buffer, now copy
+ // our response into the buffer and save away the
+ // other useful stuff about the request.
+ //
+
+ RtlCopyMemory(response->Buffer, ClientBuffer, BufferLength);
+
+ response->EntriesRead = *EntriesFilled;
+ response->TotalEntries = *TotalEntries;
+ response->Converter = *Converter;
+ response->Status = (WORD)status;
+ }
+ }
+
+ LeaveCriticalSection(&network->ResponseCacheLock);
+ }
+ }
+ } else {
+
+ ASSERT (response);
+
+ ASSERT (response->Buffer);
+
+ ASSERT (response->Size == BufferLength);
+
+ dprintf(SERVER_ENUM,("Cache hit. Use contents of server list on %ws for 0x%x/%d/%x.\n", TransportName, ServerType, ClientLevel, BufferLength));
+
+ //
+ // Copy over the cached response from the response cache into the
+ // users response buffer.
+ //
+
+ RtlCopyMemory(ClientBuffer, response->Buffer, BufferLength);
+
+ //
+ // Also copy over the other useful stuff that the client will
+ // want to know about.
+ //
+
+ *EntriesFilled = response->EntriesRead;
+
+ *TotalEntries = response->TotalEntries;
+
+ *Converter = response->Converter;
+
+ status = response->Status;
+ LeaveCriticalSection(&network->ResponseCacheLock);
+
+ }
+
+Cleanup:
+ if (networkLocked) {
+ UNLOCK_NETWORK(network);
+ }
+
+ //
+ // If a buffer was allocated, free it.
+ //
+
+ if (buffer != NULL) {
+
+ MIDL_user_free(buffer);
+ }
+
+#if DBG
+ endTickCount = GetTickCount();
+
+ dprintf(SERVER_ENUM, ("Browse request for %ws on %ws took %ld milliseconds\n", ClientName, TransportName, endTickCount - startTickCount));
+#endif
+
+ return (WORD)status;
+}
+
+
+
+ NET_API_STATUS NET_API_FUNCTION
+BrNetServerEnum(
+ IN PNETWORK Network OPTIONAL,
+ IN LPCWSTR ClientName OPTIONAL,
+ IN ULONG Level,
+ IN DWORD PreferedMaximumLength,
+ OUT PVOID *Buffer,
+ OUT LPDWORD EntriesRead,
+ OUT LPDWORD TotalEntries,
+ IN DWORD ServerType,
+ IN LPCWSTR Domain,
+ IN LPCWSTR FirstNameToReturn OPTIONAL
+ )
+/*++
+
+Routine Description:
+
+ This function is the real worker for the NetServerEnum entry point in the
+ Workstation service.
+
+Arguments:
+
+ ServerName - Supplies the name of server to execute this function
+
+ TransportName - Supplies the name of xport on which to enumerate servers
+
+ InfoStruct - This structure supplies the level of information requested,
+ returns a pointer to the buffer allocated by the Workstation service
+ which contains a sequence of information structure of the specified
+ information level, and returns the number of entries read. The buffer
+ pointer is set to NULL if return code is not NERR_Success or
+ ERROR_MORE_DATA, or if EntriesRead returned is 0. The EntriesRead
+ value is only valid if the return code is NERR_Success or
+ ERROR_MORE_DATA.
+
+ PreferedMaximumLength - Supplies the number of bytes of information
+ to return in the buffer. If this value is MAXULONG, we will try
+ to return all available information if there is enough memory
+ resource.
+
+ TotalEntries - Returns the total number of entries available. This value
+ is returned only if the return code is NERR_Success or ERROR_MORE_DATA.
+
+ ServerType - Supplies the type of server to enumerate.
+
+ Domain - Supplies the name of one of the active domains to enumerate the
+ servers from. If NULL, servers from the primary domain, logon domain
+ and other domains are enumerated.
+
+ FirstNameToReturn - Supplies the name of the first server or domain to return
+ to the caller. If NULL, enumeration begins with the very first entry.
+
+ Passed name must be the canonical form of the name.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NET_API_STATUS status;
+ DWORD DomainNameSize = 0;
+ TCHAR DomainName[DNLEN + 1];
+ BOOLEAN NetworkLocked = TRUE;
+ BOOLEAN LocalListOnly = FALSE;
+ PVOID DoubleHopLocalList = NULL;
+
+ dprintf(SERVER_ENUM, ("Retrieve browse list on %ws for %lx for client %ws\n", Network->NetworkName.Buffer, ServerType, ClientName));
+
+ EnterCriticalSection(&BrowserStatisticsLock);
+
+ if (ServerType == SV_TYPE_DOMAIN_ENUM) {
+ NumberOfDomainEnumerations += 1;
+ } else if (ServerType == SV_TYPE_ALL) {
+ NumberOfServerEnumerations += 1;
+ } else {
+ NumberOfOtherEnumerations += 1;
+ }
+
+ LeaveCriticalSection(&BrowserStatisticsLock);
+
+ //
+ // Only levels 100 and 101 are valid
+ //
+
+ if ((Level != 100) && (Level != 101)) {
+ return ERROR_INVALID_LEVEL;
+ }
+
+ if (ARGUMENT_PRESENT(Domain)) {
+
+ //
+ // NAMETYPE_WORKGROUP allows spaces.
+ //
+
+ if ( I_NetNameCanonicalize(
+ NULL,
+ (LPWSTR) Domain,
+ DomainName,
+ (DNLEN + 1) * sizeof(TCHAR),
+ NAMETYPE_WORKGROUP,
+ 0
+ ) != NERR_Success) {
+ return ERROR_INVALID_DOMAINNAME;
+ }
+
+ DomainNameSize = STRLEN(DomainName) * sizeof(WCHAR);
+ }
+
+ try {
+
+ if (ServerType != SV_TYPE_ALL) {
+
+ //
+ // If the user has specified SV_TYPE_LOCAL_LIST_ONLY, we
+ // will only accept SV_TYPE_DOMAIN_ENUM or 0 as legal bits
+ // otherwise, we return an error.
+ //
+
+ if (ServerType & SV_TYPE_LOCAL_LIST_ONLY) {
+
+ LocalListOnly = TRUE;
+
+// dprintf(SERVER_ENUM, ("Retrieve local list for %ws\n", ClientName));
+
+
+ //
+ // Turn off the LOCAL_LIST_ONLY bit.
+ //
+
+ ServerType &= ~SV_TYPE_LOCAL_LIST_ONLY;
+
+ //
+ // Check the remaining bits. The only two legal values
+ // are SV_TYPE_DOMAIN_ENUM and 0
+ //
+
+ if (ServerType != SV_TYPE_DOMAIN_ENUM) {
+
+ if (ServerType == 0) {
+ ServerType = SV_TYPE_ALL;
+ } else {
+ try_return(status = ERROR_INVALID_FUNCTION);
+ }
+ }
+
+ //
+ // If we aren't a master browser, blow this request
+ // off, since we don't have a local list.
+ //
+
+ if (!(Network->Role & ROLE_MASTER)) {
+ dprintf(SERVER_ENUM, ("Local list request received from %ws, but not master\n", ClientName));
+ try_return(status = ERROR_REQ_NOT_ACCEP);
+ }
+
+ } else if (ServerType & SV_TYPE_DOMAIN_ENUM) {
+ if (ServerType != SV_TYPE_DOMAIN_ENUM) {
+ try_return(status = ERROR_INVALID_FUNCTION);
+ }
+ }
+ }
+
+ if (ARGUMENT_PRESENT(Domain) &&
+ STRICMP(Domain, BrInfo.BrPrimaryDomainName)) {
+ PINTERIM_ELEMENT DomainEntry;
+ LPWSTR MasterName = NULL;
+
+ dprintf(SERVER_ENUM, ("Non local domain %ws - Check for double hop\n", Domain));
+
+ if ( Network->Role & ROLE_MASTER ) {
+ if ( Network->DomainList.EntriesRead != 0 ) {
+
+ DomainEntry = LookupInterimServerList(&Network->DomainList, (LPWSTR) Domain);
+
+ if (DomainEntry != NULL) {
+ MasterName = DomainEntry->Comment;
+ }
+ } else {
+ ULONG i;
+ PSERVER_INFO_101 DomainInfo;
+ DWORD DoubleHopEntriesRead;
+ DWORD DoubleHopTotalEntries;
+
+ status = BrGetLocalBrowseList(Network,
+ NULL,
+ 101,
+ SV_TYPE_DOMAIN_ENUM,
+ &DoubleHopLocalList,
+ &DoubleHopEntriesRead,
+ &DoubleHopTotalEntries);
+
+ for (i = 0 , DomainInfo = DoubleHopLocalList ;
+
+ i < DoubleHopEntriesRead ;
+
+ i += 1) {
+
+ if (!_wcsicmp(Domain, DomainInfo->sv101_name)) {
+ MasterName = DomainInfo->sv101_comment;
+ break;
+ }
+
+ DomainInfo += 1;
+ }
+ }
+
+ } else {
+ ULONG i;
+ PSERVER_INFO_101 DomainInfo;
+
+ //
+ // We're running on a backup browser. We want to find the
+ // name of the master browser by looking in the backup
+ // server list for this network.
+ //
+
+ for (i = 0 , DomainInfo = Network->BackupDomainList ;
+
+ i < Network->TotalBackupDomainListEntries ;
+
+ i += 1) {
+
+ if (!_wcsicmp(Domain, DomainInfo->sv101_name)) {
+ MasterName = DomainInfo->sv101_comment;
+ break;
+ }
+
+ DomainInfo += 1;
+ }
+
+ }
+
+ //
+ // If we couldn't find a master name, bail out right now.
+ //
+
+ if (MasterName == NULL || *MasterName == UNICODE_NULL) {
+ try_return(status = ERROR_NO_BROWSER_SERVERS_FOUND);
+ }
+
+ //
+ // If the master for this domain isn't listed as our
+ // current machine, remote the API to that server.
+ //
+
+ if (STRICMP(MasterName, BrInfo.BrComputerName)) {
+ WCHAR RemoteComputerName[UNLEN+1];
+
+ ASSERT (NetworkLocked);
+
+ UNLOCK_NETWORK(Network);
+
+ NetworkLocked = FALSE;
+
+ //
+ // Build the name of the remote computer.
+ //
+
+ STRCPY(RemoteComputerName, TEXT("\\\\"));
+
+ STRCAT(RemoteComputerName, MasterName);
+
+ dprintf(SERVER_ENUM, ("Double hop to %ws on %ws\n", RemoteComputerName, Network->NetworkName.Buffer));
+ status = RxNetServerEnum(RemoteComputerName,
+ Network->NetworkName.Buffer,
+ Level,
+ (LPBYTE *)Buffer,
+ PreferedMaximumLength,
+ EntriesRead,
+ TotalEntries,
+ ServerType,
+ Domain,
+ FirstNameToReturn );
+ dprintf(SERVER_ENUM, ("Double hop done\n"));
+
+ if (!LOCK_NETWORK_SHARED (Network)) {
+ try_return(status = NERR_InternalError);
+ }
+
+ NetworkLocked = TRUE;
+
+ try_return(status);
+ }
+ }
+
+ ASSERT (NetworkLocked);
+
+ if (!ARGUMENT_PRESENT(Domain)) {
+ STRCPY(DomainName, BrInfo.BrPrimaryDomainName);
+ }
+
+ //
+ // If we are running on the master browser, we want to retrieve the
+ // local list, merge it with our interim server list, and
+ // return that to the user.
+ //
+
+ if (Network->Role & ROLE_MASTER) {
+ status = BrRetrieveServerListForMaster(Network,
+ Buffer,
+ EntriesRead,
+ TotalEntries,
+ Level,
+ ServerType,
+ PreferedMaximumLength,
+ LocalListOnly,
+ (LPWSTR) ClientName,
+ DomainName,
+ FirstNameToReturn );
+
+
+
+ } else {
+
+ status = BrRetrieveServerListForBackup(Network,
+ Buffer,
+ EntriesRead,
+ TotalEntries,
+ Level,
+ ServerType,
+ PreferedMaximumLength,
+ FirstNameToReturn );
+
+
+ }
+
+ try_return(status);
+
+try_exit:NOTHING;
+ } finally {
+
+#if DBG
+ if (status == NERR_Success || status == ERROR_MORE_DATA) {
+ dprintf(SERVER_ENUM, ("Returning Browse list on %ws for %lx with %ld entries for %ws\n", Network->NetworkName.Buffer, ServerType, *EntriesRead, ClientName));
+ } else {
+ dprintf(SERVER_ENUM, ("Failing I_BrowserServerEnum: %ld for client %ws\n", status, ClientName));
+ }
+#endif
+
+ //
+ // If we used the local driver's list to retrieve the list of
+ // domains, free up the buffer.
+ //
+
+ if (DoubleHopLocalList != NULL) {
+ MIDL_user_free(DoubleHopLocalList);
+
+ }
+
+ ASSERT (NetworkLocked);
+ }
+
+ return status;
+}
+
+
+VOID
+TrimServerList(
+ IN DWORD Level,
+ IN OUT LPBYTE *Buffer,
+ IN OUT LPDWORD EntriesRead,
+ IN OUT LPDWORD TotalEntries,
+ IN LPCWSTR FirstNameToReturn
+)
+
+/*++
+
+Routine Description:
+
+ This routine trims any name from Buffer that's less than FirstNameToReturn.
+
+ The routine is implemented by moving the fixed part of the kept entries to the beginning
+ of the allocated buffer. Thay way, the pointer contained in the entries need not be
+ relocated and the original buffer can still be used.
+
+Arguments:
+
+ Level - Level of information in the buffer.
+
+ Buffer - Pointer to address of buffer to trim.
+ Returns null (and deallocates the buffer) if all the entries are trimmed.
+
+ EntriesRead - Pointer to number of entries in the buffer.
+ Returns the number of entries remaining in the buffer after triming.
+
+ TotalEntries - Pointer to number of entries available from server.
+ Returns a number reduced by the number of trimmed entries.
+
+ FirstNameToReturn - Supplies the name of the first server or domain to return
+ to the caller. If NULL, enumeration begins with the very first entry.
+
+ Passed name must be the canonical form of the name.
+
+Return Value:
+
+ Returns the error code for the operation.
+
+--*/
+
+{
+ DWORD EntryCount;
+ DWORD EntryNumber;
+ DWORD FixedSize;
+
+ LPBYTE CurrentEntry;
+
+ //
+ // Early out if there's nothing to do.
+ //
+
+ if ( FirstNameToReturn == NULL || *FirstNameToReturn == L'\0' || *EntriesRead == 0 ) {
+ return;
+ }
+
+
+ //
+ // Compute the size of each entry.
+ //
+
+ switch (Level) {
+ case 100:
+ FixedSize = sizeof(SERVER_INFO_100);
+ break;
+ case 101:
+ FixedSize = sizeof(SERVER_INFO_101);
+ break;
+ default:
+ NetpAssert( FALSE );
+ return;
+
+ }
+
+ //
+ // Finding the first entry to return
+ //
+
+ EntryCount = *EntriesRead;
+
+ for ( EntryNumber=0; EntryNumber< EntryCount; EntryNumber++ ) {
+
+ LPSERVER_INFO_100 ServerEntry =
+ (LPSERVER_INFO_100)( *Buffer + FixedSize * EntryNumber);
+
+ //
+ // Found the first entry to return.
+ //
+
+ if ( STRCMP( ServerEntry->sv100_name, FirstNameToReturn ) >= 0 ) {
+
+ //
+ // If we're returning the whole list,
+ // simply return.
+ //
+
+ if ( ServerEntry == (LPSERVER_INFO_100)(*Buffer) ) {
+ return;
+ }
+
+ //
+ // Copy the remaining entries to the beginning of the buffer.
+ // (Yes, this is an overlapping copy)
+ //
+
+ RtlMoveMemory( *Buffer, ServerEntry, (*EntriesRead) * FixedSize );
+ return;
+
+ }
+
+ //
+ // Account for skipped entries.
+ //
+
+ *EntriesRead -= 1;
+ *TotalEntries -= 1;
+ }
+
+ //
+ // If no entries should be returned,
+ // deallocate the buffer.
+ //
+
+ NetApiBufferFree( *Buffer );
+ *Buffer = NULL;
+
+ ASSERT ( *EntriesRead == 0 );
+
+ return;
+
+} // TrimServerList
+
+
+ NET_API_STATUS
+BrRetrieveServerListForMaster(
+ IN PNETWORK Network,
+ IN OUT PVOID *Buffer,
+ OUT PDWORD EntriesRead,
+ OUT PDWORD TotalEntries,
+ IN DWORD Level,
+ IN DWORD ServerType,
+ IN DWORD PreferedMaximumLength,
+ IN BOOLEAN LocalListOnly,
+ IN LPTSTR ClientName,
+ IN LPTSTR DomainName,
+ IN LPCWSTR FirstNameToReturn
+ )
+{
+ BOOLEAN GetLocalList = FALSE;
+ NET_API_STATUS status;
+ BOOLEAN EarlyOut = FALSE;
+
+ //
+ // Determine if it is appropriate that we should early out after retrieving
+ // the list of servers (or domains) from the bowser.
+ //
+ // We will early out on the following critieria:
+ //
+ // 1) If we are requested to retrieve the local list on a Wannish xport
+ // 2) If the transport isn't a wannish transport, or
+ // 3) If this domain isn't our primary domain (in which case it's an
+ // "otherdomain" request).
+ //
+
+ if (LocalListOnly || STRICMP(DomainName, BrInfo.BrPrimaryDomainName)) {
+ EarlyOut = TRUE;
+ } else if (!(Network->Flags & NETWORK_WANNISH)) {
+
+ //
+ // This isn't a wannish transport. We need to see if we've been
+ // master long enough for the driver to have retrieved a reasonable
+ // list.
+ //
+ // The server and domain table will be emptied when the first master
+ // browser timer is run. This will happen 15 minutes after we
+ // become the master, so we will use our services list for the
+ // first 15 minutes the browser is master.
+ //
+
+ if (ServerType == SV_TYPE_DOMAIN_ENUM) {
+ if (Network->DomainList.EntriesRead == 0) {
+ EarlyOut = TRUE;
+ }
+ } else {
+ if (Network->BrowseTable.EntriesRead == 0) {
+ EarlyOut = TRUE;
+ }
+ }
+
+ }
+
+ if (EarlyOut) {
+
+ GetLocalList = TRUE;
+
+ } else if (ServerType == SV_TYPE_ALL) {
+
+ if ((BrCurrentSystemTime() - Network->LastBowserServerQueried) > BrInfo.DriverQueryFrequency ) {
+
+ GetLocalList = TRUE;
+ }
+
+ } else if (ServerType == SV_TYPE_DOMAIN_ENUM) {
+
+ if ((BrCurrentSystemTime() - Network->LastBowserDomainQueried) > BrInfo.DriverQueryFrequency ) {
+
+ GetLocalList = TRUE;
+ }
+
+ } else {
+
+ GetLocalList = TRUE;
+ }
+
+ if (GetLocalList && !EarlyOut) {
+
+ //
+ // If we're retriving the list from the driver, and can't early out
+ // this request, this means that we will be merging this server list
+ // with an interim server list. This means that we will be modifying
+ // the network structure, and thus that we need to re-lock the network
+ // structure.
+ //
+
+ UNLOCK_NETWORK(Network);
+
+ if (!LOCK_NETWORK(Network)) {
+ return NERR_InternalError;
+ }
+
+ //
+ // Now re-test to see if another thread came in and retrieved the
+ // list from the driver while we had the network unlocked. If so,
+ // we don't want to get the local list any more.
+ //
+ // Otherwise, we want to update the last query time.
+ //
+
+ if (ServerType == SV_TYPE_ALL) {
+
+ if ((BrCurrentSystemTime() - Network->LastBowserServerQueried) > BrInfo.DriverQueryFrequency ) {
+ Network->LastBowserServerQueried = BrCurrentSystemTime();
+ } else {
+ GetLocalList = FALSE;
+ }
+
+ } else if (ServerType == SV_TYPE_DOMAIN_ENUM) {
+
+ if ((BrCurrentSystemTime() - Network->LastBowserDomainQueried) > BrInfo.DriverQueryFrequency ) {
+ Network->LastBowserDomainQueried = BrCurrentSystemTime();
+ } else {
+ GetLocalList = FALSE;
+ }
+
+ } else {
+
+ Network->LastBowserServerQueried = BrCurrentSystemTime();
+
+ }
+
+ }
+
+ //
+ // If we're supposed to retrieve the local server list, retrieve it.
+ //
+
+ if (GetLocalList) {
+ DWORD ServerTypeForLocalList;
+
+ //
+ // Calculate the server type to use when retrieving the list from
+ // the driver.
+ //
+
+ if (LocalListOnly ||
+
+ (ServerType == SV_TYPE_DOMAIN_ENUM) ||
+
+ !(Network->Flags & NETWORK_WANNISH)) {
+
+ //
+ // If we are retrieving the local list, or this is a non-wannish
+ // transport, or we are asking for domains, we want to
+ // keep the callers server type.
+ //
+
+ ServerTypeForLocalList = ServerType;
+
+ } else {
+
+ //
+ // This must be a wannish transport, ask for all servers (we know
+ // that we're not asking for domains, since we checked that
+ // above).
+ //
+ // We ask for all the servers because it's possible that a servers
+ // role has changed.
+ //
+
+ ASSERT (Network->Flags & NETWORK_WANNISH);
+
+ ServerTypeForLocalList = SV_TYPE_ALL;
+ }
+
+ dprintf(SERVER_ENUM, ("Get local browse list for %ws\n", ClientName));
+
+ status = BrGetLocalBrowseList(Network,
+ DomainName,
+ ( EarlyOut ? Level : 101 ),
+ ServerTypeForLocalList,
+ Buffer,
+ EntriesRead,
+ TotalEntries);
+
+// dprintf(SERVER_ENUM, ("List retrieved. %ld entries, %ld total\n", *EntriesRead, *TotalEntries));
+
+
+ //
+ // If we're supposed to early-out this request (or if there was
+ // an error), do so now.
+ //
+
+ if (EarlyOut ||
+ (status != NERR_Success && status != ERROR_MORE_DATA)) {
+
+ //
+ // If we're returning an early out list,
+ // truncate the complete list returned from the kernel.
+ //
+ // This saves us from having to modify the kernel interface and untangle
+ // the code above.
+ //
+
+ if ( status == NERR_Success || status == ERROR_MORE_DATA ) {
+
+ TrimServerList( Level,
+ (LPBYTE *)Buffer,
+ EntriesRead,
+ TotalEntries,
+ FirstNameToReturn );
+
+ }
+
+ dprintf(SERVER_ENUM, ("Early out for %ws with %ld servers. Don't merge server list.\n", ClientName, *EntriesRead));
+
+ return status;
+ }
+
+ if (status == NERR_Success || status == ERROR_MORE_DATA) {
+
+ if (*EntriesRead != 0) {
+
+ //
+ // Merge the local list with the list we got from the
+ // master or from the domain master.
+ //
+
+ dprintf(SERVER_ENUM, ("Merge %d entries in server list %ws for %ws \n", *EntriesRead, Network->NetworkName.Buffer, ClientName));
+
+ status = MergeServerList((ServerType == SV_TYPE_DOMAIN_ENUM ?
+ &Network->DomainList :
+ &Network->BrowseTable),
+ 101,
+ *Buffer,
+ *EntriesRead,
+ *TotalEntries
+ );
+ }
+ }
+ }
+
+ //
+ // We've merged the local list into the appropriate interim table,
+ // now free it up.
+ //
+
+ if (*EntriesRead != 0) {
+ MIDL_user_free(*Buffer);
+ }
+
+ status = PackServerList((ServerType == SV_TYPE_DOMAIN_ENUM ?
+ &Network->DomainList :
+ &Network->BrowseTable),
+ Level,
+ ServerType,
+ PreferedMaximumLength,
+ Buffer,
+ EntriesRead,
+ TotalEntries,
+ FirstNameToReturn
+ );
+ return status;
+}
+
+ NET_API_STATUS
+BrRetrieveServerListForBackup(
+ IN PNETWORK Network,
+ IN OUT PVOID *Buffer,
+ OUT PDWORD EntriesRead,
+ OUT PDWORD TotalEntries,
+ IN DWORD Level,
+ IN DWORD ServerType,
+ IN DWORD PreferedMaximumLength,
+ IN LPCWSTR FirstNameToReturn
+ )
+{
+ PSERVER_INFO_101 ServerList, ClientServerInfo;
+ ULONG EntriesInList;
+ ULONG TotalEntriesInList;
+ ULONG EntrySize;
+ ULONG BufferSize;
+ LPTSTR BufferEnd;
+ BOOLEAN ReturnWholeList = FALSE;
+ BOOLEAN TrimmingNames;
+
+ //
+ // If we are not running as a master, we want to use our stored
+ // server list to figure out what the client gets.
+ //
+
+ if (ServerType == SV_TYPE_DOMAIN_ENUM) {
+
+ ServerList = Network->BackupDomainList;
+
+ TotalEntriesInList = EntriesInList = Network->TotalBackupDomainListEntries;
+
+ ReturnWholeList = TRUE;
+
+ } else {
+ ServerList = Network->BackupServerList;
+
+ TotalEntriesInList = EntriesInList = Network->TotalBackupServerListEntries;
+
+ if (ServerType == SV_TYPE_ALL) {
+ ReturnWholeList = TRUE;
+ }
+ }
+
+ //
+ // Figure out the largest buffer we have to allocate to hold this
+ // server info.
+ //
+
+ if (Level == 101) {
+ if (PreferedMaximumLength == MAXULONG) {
+
+ if (ServerType == SV_TYPE_DOMAIN_ENUM) {
+ BufferSize = (sizeof(SERVER_INFO_101) + (CNLEN+1 + CNLEN+1)*sizeof(TCHAR)) * EntriesInList;
+ } else {
+ BufferSize = (sizeof(SERVER_INFO_101) + (CNLEN+1 + MAXCOMMENTSZ+1)*sizeof(TCHAR)) * EntriesInList;
+ }
+ } else {
+ BufferSize = PreferedMaximumLength;
+ }
+
+ EntrySize = sizeof(SERVER_INFO_101);
+ } else {
+ if (PreferedMaximumLength == MAXULONG) {
+ BufferSize = (sizeof(SERVER_INFO_100) + (CNLEN+1)*sizeof(TCHAR)) * EntriesInList;
+ } else {
+ BufferSize = PreferedMaximumLength;
+ }
+
+ EntrySize = sizeof(SERVER_INFO_100);
+ }
+
+ *Buffer = MIDL_user_allocate(BufferSize);
+
+ if (*Buffer == NULL) {
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ BufferEnd = (LPTSTR)((ULONG)*Buffer+BufferSize);
+
+ ClientServerInfo = *Buffer;
+
+ *TotalEntries = 0;
+
+ *EntriesRead = 0;
+
+ //
+ // While there are still entries to process....
+ //
+
+ TrimmingNames = (FirstNameToReturn != NULL && *FirstNameToReturn != L'\0');
+ while (EntriesInList) {
+
+ EntriesInList -= 1;
+
+ //
+ // If this entry is appropriate to be packed,
+ //
+
+ if ( (ServerList->sv101_type & ServerType) &&
+ (!TrimmingNames ||
+ STRCMP( ServerList->sv101_name, FirstNameToReturn ) >= 0 ) ) {
+
+ TrimmingNames = FALSE;
+
+ //
+ // Indicate one more entry in the list.
+ //
+
+ *TotalEntries += 1;
+
+ //
+ // If we can fit this entry before the buffer end,
+ // pack in the information into the buffer.
+ //
+
+ if ((ULONG)ClientServerInfo+EntrySize <= (ULONG)BufferEnd) {
+
+ //
+ // Copy over the platform ID and computer name.
+ //
+
+ ClientServerInfo->sv101_platform_id = ServerList->sv101_platform_id;
+
+ ClientServerInfo->sv101_name = ServerList->sv101_name;
+
+ if (NetpPackString(&ClientServerInfo->sv101_name,
+ (LPBYTE)((PCHAR)ClientServerInfo)+EntrySize,
+ &BufferEnd)) {
+
+ if (Level == 101) {
+
+ ClientServerInfo->sv101_version_major = ServerList->sv101_version_major;
+
+ ClientServerInfo->sv101_version_minor = ServerList->sv101_version_minor;
+
+ ClientServerInfo->sv101_type = ServerList->sv101_type;
+
+ ClientServerInfo->sv101_comment = ServerList->sv101_comment;
+
+ if (NetpPackString(&ClientServerInfo->sv101_comment,
+ (LPBYTE)((PCHAR)ClientServerInfo)+EntrySize,
+ &BufferEnd)) {
+ *EntriesRead += 1;
+ }
+ } else {
+ *EntriesRead += 1;
+ }
+ }
+
+ ClientServerInfo = (PSERVER_INFO_101)((PCHAR)ClientServerInfo+EntrySize);
+ } else {
+ //
+ // If we're returning the entire list, we can
+ // early out now, since there's no point in continuing.
+ //
+
+ if (ReturnWholeList) {
+
+ *TotalEntries = TotalEntriesInList;
+
+ break;
+ }
+ }
+
+ }
+
+ ServerList += 1;
+ }
+
+ //
+ // If we weren't able to pack all the entries into the list,
+ // return ERROR_MORE_DATA
+ //
+
+ if (*EntriesRead != *TotalEntries) {
+ return ERROR_MORE_DATA;
+ } else {
+ return NERR_Success;
+ }
+
+}
+
+
+
+ NET_API_STATUS
+I_BrowserrResetStatistics (
+ IN LPTSTR servername OPTIONAL
+ )
+{
+ NET_API_STATUS Status = NERR_Success;
+ ULONG BufferSize;
+
+ EnterCriticalSection(&BrowserStatisticsLock);
+
+ NumberOfServerEnumerations = 0;
+
+ NumberOfDomainEnumerations = 0;
+
+ NumberOfOtherEnumerations = 0;
+
+ NumberOfMissedGetBrowserListRequests = 0;
+
+ //
+ // Reset the driver's statistics as well.
+ //
+
+ if (!DeviceIoControl(BrDgReceiverDeviceHandle, IOCTL_LMDR_RESET_STATISTICS, NULL, 0, NULL, 0, &BufferSize, NULL)) {
+
+ //
+ // The API failed, return the error.
+ //
+
+ Status = GetLastError();
+ }
+
+ LeaveCriticalSection(&BrowserStatisticsLock);
+
+ return Status;
+}
+
+ NET_API_STATUS
+I_BrowserrQueryStatistics (
+ IN LPTSTR servername OPTIONAL,
+ OUT LPBROWSER_STATISTICS *Statistics
+ )
+{
+ NET_API_STATUS Status = NERR_Success;
+ BOWSER_STATISTICS BowserStatistics;
+ ULONG BufferSize;
+
+ *Statistics = MIDL_user_allocate(sizeof(BROWSER_STATISTICS));
+
+ if (*Statistics == NULL) {
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ EnterCriticalSection(&BrowserStatisticsLock);
+
+ if (!DeviceIoControl(BrDgReceiverDeviceHandle, IOCTL_LMDR_QUERY_STATISTICS, NULL, 0, &BowserStatistics, sizeof(BowserStatistics), &BufferSize, NULL)) {
+
+ //
+ // The API failed, return the error.
+ //
+
+ Status = GetLastError();
+ } else {
+
+ if (BufferSize != sizeof(BOWSER_STATISTICS)) {
+ Status = ERROR_INSUFFICIENT_BUFFER;
+ } else {
+ (*Statistics)->StatisticsStartTime = BowserStatistics.StartTime;
+
+ (*Statistics)->NumberOfServerAnnouncements = BowserStatistics.NumberOfServerAnnouncements;
+ (*Statistics)->NumberOfDomainAnnouncements = BowserStatistics.NumberOfDomainAnnouncements;
+ (*Statistics)->NumberOfElectionPackets = BowserStatistics.NumberOfElectionPackets;
+ (*Statistics)->NumberOfMailslotWrites = BowserStatistics.NumberOfMailslotWrites;
+ (*Statistics)->NumberOfGetBrowserServerListRequests = BowserStatistics.NumberOfGetBrowserServerListRequests;
+ (*Statistics)->NumberOfMissedServerAnnouncements = BowserStatistics.NumberOfMissedServerAnnouncements;
+ (*Statistics)->NumberOfMissedMailslotDatagrams = BowserStatistics.NumberOfMissedMailslotDatagrams;
+ (*Statistics)->NumberOfMissedGetBrowserServerListRequests = BowserStatistics.NumberOfMissedGetBrowserServerListRequests +
+ NumberOfMissedGetBrowserListRequests;
+ (*Statistics)->NumberOfFailedServerAnnounceAllocations = BowserStatistics.NumberOfFailedServerAnnounceAllocations;
+ (*Statistics)->NumberOfFailedMailslotAllocations = BowserStatistics.NumberOfFailedMailslotAllocations;
+ (*Statistics)->NumberOfFailedMailslotReceives = BowserStatistics.NumberOfFailedMailslotReceives;
+ (*Statistics)->NumberOfFailedMailslotWrites = BowserStatistics.NumberOfFailedMailslotWrites;
+ (*Statistics)->NumberOfFailedMailslotOpens = BowserStatistics.NumberOfFailedMailslotOpens;
+ (*Statistics)->NumberOfDuplicateMasterAnnouncements = BowserStatistics.NumberOfDuplicateMasterAnnouncements;
+ (*Statistics)->NumberOfIllegalDatagrams = BowserStatistics.NumberOfIllegalDatagrams;
+
+ //
+ // Now fill in the local statistics.
+ //
+
+ (*Statistics)->NumberOfServerEnumerations = NumberOfServerEnumerations;
+ (*Statistics)->NumberOfDomainEnumerations = NumberOfDomainEnumerations;
+ (*Statistics)->NumberOfOtherEnumerations = NumberOfOtherEnumerations;
+
+ }
+ }
+
+ LeaveCriticalSection(&BrowserStatisticsLock);
+
+ return Status;
+}
+
+
+
+//
+// Browser request response cache management logic.
+//
+
+
+
+ PCACHED_BROWSE_RESPONSE
+BrLookupAndAllocateCachedEntry(
+ IN PNETWORK Network,
+ IN DWORD ServerType,
+ IN WORD Size,
+ IN DWORD Level,
+ IN LPCWSTR FirstNameToReturn
+ )
+/*++
+
+Routine Description:
+
+ This function will look up (and allocate if appropriate) a cached
+ browse response for this browse.
+
+ Enter with the Network Locked shared or exclusive.
+
+Arguments:
+ IN PNETWORK Network - Network to allocate entry on.
+ IN DWORD ServerType - Server type bits for request.
+ IN WORD Size, - Users buffer size for request.
+ IN WORD Level - Level of request.
+
+ FirstNameToReturn - Supplies the name of the first domain or server entry to return.
+ The caller can use this parameter to implement a resume handle of sorts by passing
+ the name of the last entry returned on a previous call. (Notice that the specified
+ entry will, also, be returned on this call unless it has since been deleted.)
+
+ Passed name must be the canonical form of the name.
+
+ This entry is never NULL. It may be a pointer to an empty string to indicate
+ the enumeration starts at the beginning of the list.
+
+
+Return Value:
+
+ PCACHED_BROWSE_RESPONSE - NULL or a cached response for the request.
+
+--*/
+
+{
+ PLIST_ENTRY entry;
+ PCACHED_BROWSE_RESPONSE response;
+
+ //
+ // If we have more cached responses than we are allowed,
+ // remove the last entry from the list and free it.
+ //
+
+ if (Network->NumberOfCachedResponses > BrInfo.NumberOfCachedResponses) {
+
+ //
+ // We need to release the network and re-acquire it
+ // exclusively, because we use the network lock to protect
+ // enumerations from deletions.
+ //
+
+ UNLOCK_NETWORK(Network);
+
+ if (LOCK_NETWORK(Network)) {
+
+ EnterCriticalSection(&Network->ResponseCacheLock);
+ if (Network->NumberOfCachedResponses > BrInfo.NumberOfCachedResponses) {
+
+ PLIST_ENTRY LastEntry = RemoveTailList(&Network->ResponseCache);
+
+ response = CONTAINING_RECORD(LastEntry, CACHED_BROWSE_RESPONSE, Next);
+
+ Network->NumberOfCachedResponses -= 1;
+
+ response->Next.Flink = NULL;
+ response->Next.Blink = NULL;
+
+ //
+ // Free the last cached entry.
+ //
+
+ BrDestroyCacheEntry( response );
+
+ response = NULL;
+ }
+ LeaveCriticalSection(&Network->ResponseCacheLock);
+ }
+ }
+
+ //
+ // Search the list of responses for this one.
+ //
+
+ EnterCriticalSection(&Network->ResponseCacheLock);
+
+ for (entry = Network->ResponseCache.Flink ;
+ entry != &Network->ResponseCache ;
+ entry = entry->Flink ) {
+
+ response = CONTAINING_RECORD(entry, CACHED_BROWSE_RESPONSE, Next);
+
+ //
+ // If this response cache entry matches the incoming request,
+ // we can increment the hit count for this entry and return it.
+ //
+
+ if (response->Level == Level
+ &&
+ response->ServerType == ServerType
+ &&
+ response->Size == Size
+ &&
+ wcscmp( response->FirstNameToReturn, FirstNameToReturn ) == 0) {
+
+ //
+ // This response exactly matches the request.
+ //
+ // Bump its hit count and move it to the head of the cache.
+ //
+
+ response->HitCount += 1;
+
+ response->TotalHitCount += 1;
+
+ //
+ // Remove this entry from its current location in the list and
+ // move it to the head of the list.
+ //
+
+ RemoveEntryList(&response->Next);
+
+ InsertHeadList(&Network->ResponseCache, &response->Next);
+
+ dprintf(SERVER_ENUM, ("Found cache entry 0x%x/%d/%x H:%d T:%d\n", response->ServerType, response->Level, response->Size, response->HitCount, response->TotalHitCount ));
+
+ LeaveCriticalSection(&Network->ResponseCacheLock);
+
+ return response;
+ }
+ }
+
+ //
+ // We've walked our entire cache and have been unable to find
+ // a response that matches our request.
+ //
+ // Allocate a new response cache entry and hook it into the cache.
+ //
+
+ response = BrAllocateResponseCacheEntry(Network, ServerType, Size, Level, FirstNameToReturn );
+
+ LeaveCriticalSection(&Network->ResponseCacheLock);
+
+ return response;
+
+}
+
+ VOID
+BrAgeResponseCache(
+ IN PNETWORK Network
+ )
+/*++
+
+Routine Description:
+
+ This function will age response cache entries for a network.
+
+ We scan the response cache, and every entry that has a cached response
+ will be tossed. In addition, any entry that has had less than the
+ cache hit limit number of hits since the past scan will also be removed.
+
+Arguments:
+ IN PNETWORK Network - Network to age entries on.
+
+Return Value:
+
+ None.
+
+
+--*/
+{
+ PLIST_ENTRY entry;
+
+ EnterCriticalSection(&Network->ResponseCacheLock);
+
+ try {
+
+ for (entry = Network->ResponseCache.Flink ;
+ entry != &Network->ResponseCache ;
+ entry = entry->Flink ) {
+ PCACHED_BROWSE_RESPONSE response = CONTAINING_RECORD(entry, CACHED_BROWSE_RESPONSE, Next);
+
+ //
+ // If this response didn't have a hit count high enough during
+ // the previous run to justify keeping it around, blow it away.
+ //
+
+ if (response->HitCount < BrInfo.CacheHitLimit) {
+ response->LowHitCount += 1;
+ }
+
+ //
+ // If we have CacheHitLimit iterations of low hits, then
+ // flush the entry from the cache.
+ //
+
+ if (response->LowHitCount > BrInfo.CacheHitLimit) {
+ PLIST_ENTRY nextentry = entry->Blink;
+
+ dprintf(SERVER_ENUM,("Flush cache entry for 0x%x/%d/%x H:%d T:%d\n", response->ServerType, response->Level, response->Size, response->HitCount, response->TotalHitCount ));
+
+ Network->NumberOfCachedResponses -= 1;
+
+ RemoveEntryList(entry);
+
+ BR_DEBUG entry->Flink = NULL;
+ BR_DEBUG entry->Blink = NULL;
+
+ BrDestroyCacheEntry(response);
+
+ entry = nextentry;
+
+ //
+ // Null out the pointer to make sure we don't use it again.
+ //
+
+ BR_DEBUG response = NULL;
+
+ } else {
+ dprintf(SERVER_ENUM, ("Retain cache entry 0x%x/%d/%x H:%d T:%d\n", response->ServerType, response->Level, response->Size, response->HitCount, response->TotalHitCount ));
+
+ //
+ // We ALWAYS blow away the response buffer during an age pass.
+ //
+
+ MIDL_user_free( response->Buffer );
+
+ response->Buffer = NULL;
+
+ //
+ // Reset the hit count for this entry for this pass.
+ //
+
+ response->HitCount = 0;
+ }
+
+ }
+
+ } finally {
+ LeaveCriticalSection(&Network->ResponseCacheLock);
+ }
+}
+
+
+ PCACHED_BROWSE_RESPONSE
+BrAllocateResponseCacheEntry(
+ IN PNETWORK Network,
+ IN DWORD ServerType,
+ IN WORD Size,
+ IN DWORD Level,
+ IN LPCWSTR FirstNameToReturn
+ )
+/*++
+
+Routine Description:
+
+ This function will allocate a new browse response cache entry.
+
+Arguments:
+ IN PNETWORK Network - Network to allocate entry on.
+ IN DWORD ServerType - Server type bits for request.
+ IN WORD Size, - Users buffer size for request.
+ IN WORD Level - Level of request.
+
+ FirstNameToReturn - FirstNameCached
+
+Return Value:
+
+ PCACHED_BROWSE_RESPONSE - NULL or a cached response for the request.
+
+NOTE: This is called with the network response cache locked.
+
+--*/
+
+{
+ PCACHED_BROWSE_RESPONSE response;
+
+
+ response = MIDL_user_allocate( sizeof( CACHED_BROWSE_RESPONSE ) );
+
+ if ( response == NULL ) {
+ return NULL;
+ }
+
+ //
+ // Flag the information for this response.
+ //
+
+ response->ServerType = ServerType;
+ response->Size = Size;
+ response->Level = Level;
+
+ //
+ // Initialize the other fields in the response.
+ //
+
+ response->Buffer = NULL;
+ response->HitCount = 0;
+ response->TotalHitCount = 0;
+ response->LowHitCount = 0;
+ response->Status = NERR_Success;
+ wcscpy( response->FirstNameToReturn, FirstNameToReturn );
+
+ Network->NumberOfCachedResponses += 1;
+
+ //
+ // We hook this response into the tail of the cache. We do this
+ // because we assume that this request won't be used frequently. If
+ // it is, it will move to the head of the cache naturally.
+ //
+
+ InsertTailList(&Network->ResponseCache, &response->Next);
+
+ return response;
+}
+
+ NET_API_STATUS
+BrDestroyCacheEntry(
+ IN PCACHED_BROWSE_RESPONSE CacheEntry
+ )
+/*++
+
+Routine Description:
+
+ This routine destroys an individual response cache entry.
+
+Arguments:
+ IN PCACHED_BROWSE_RESPONSE CacheEntry - Entry to destroy.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success
+
+--*/
+{
+ ASSERT (CacheEntry->Next.Flink == NULL);
+ ASSERT (CacheEntry->Next.Blink == NULL);
+
+ if (CacheEntry->Buffer != NULL) {
+ MIDL_user_free(CacheEntry->Buffer);
+ }
+
+ MIDL_user_free(CacheEntry);
+
+ return NERR_Success;
+}
+
+ NET_API_STATUS
+BrDestroyResponseCache(
+ IN PNETWORK Network
+ )
+/*++
+
+Routine Description:
+
+ This routine destroys the entire response cache for a supplied network.
+
+Arguments:
+ IN PNETWORK Network - Network to allocate entry on.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success
+
+--*/
+
+{
+ while (!IsListEmpty(&Network->ResponseCache)) {
+ PCACHED_BROWSE_RESPONSE cacheEntry;
+ PLIST_ENTRY entry = RemoveHeadList(&Network->ResponseCache);
+
+ BR_DEBUG entry->Flink = NULL;
+ BR_DEBUG entry->Blink = NULL;
+
+ cacheEntry = CONTAINING_RECORD(entry, CACHED_BROWSE_RESPONSE, Next);
+
+ Network->NumberOfCachedResponses -= 1;
+
+ BrDestroyCacheEntry(cacheEntry);
+
+ }
+
+ ASSERT (Network->NumberOfCachedResponses == 0);
+
+ return NERR_Success;
+}
+
+NET_API_STATUS
+NetrBrowserStatisticsGet (
+ IN LPTSTR servername OPTIONAL,
+ IN DWORD Level,
+ IN OUT LPBROWSER_STATISTICS_STRUCT InfoStruct
+ )
+{
+ //
+ // And return success.
+ //
+
+ return(NERR_Success);
+
+}
+
+NET_API_STATUS
+NetrBrowserStatisticsClear (
+ IN LPTSTR servername OPTIONAL
+ )
+{
+ //
+ // And return success.
+ //
+
+ return(NERR_Success);
+
+}
+
+#if DBG
+
+NET_API_STATUS
+I_BrowserrDebugCall (
+ IN LPTSTR servername OPTIONAL,
+ IN DWORD DebugCode,
+ IN DWORD OptionalValue
+ )
+{
+ NET_API_STATUS Status = STATUS_SUCCESS;
+
+ switch (DebugCode) {
+ case BROWSER_DEBUG_BREAK_POINT:
+ DbgBreakPoint();
+ break;
+
+ case BROWSER_DEBUG_DUMP_NETWORKS:
+ BrDumpNetworks();
+ break;
+ case BROWSER_DEBUG_SET_DEBUG:
+#if DBG
+ BrInfo.BrowserDebug |= OptionalValue;
+ DbgPrint("Setting browser trace to %lx\n", BrInfo.BrowserDebug);
+#else
+ DbgPrint("Setting browser debug not supported on free builds\n");
+#endif
+ break;
+
+ case BROWSER_DEBUG_CLEAR_DEBUG:
+#if DBG
+ BrInfo.BrowserDebug &= ~OptionalValue;
+ DbgPrint("Setting browser trace to %lx\n", BrInfo.BrowserDebug);
+#else
+ DbgPrint("Clearing browser debug not supported on free builds\n");
+#endif
+ break;
+ case BROWSER_DEBUG_TRUNCATE_LOG:
+ Status = BrTruncateLog();
+ break;
+
+ default:
+ KdPrint(("Unknown debug callout %lx\n", DebugCode));
+ DbgBreakPoint();
+ break;
+ }
+
+ return Status;
+
+}
+
+NET_API_STATUS
+I_BrowserrDebugTrace (
+ IN LPTSTR servername OPTIONAL,
+ IN LPSTR String
+ )
+{
+ //
+ // Stick the string parameter into the browser log.
+ //
+
+ BrowserTrace("%s", String);
+
+ //
+ // And return success.
+ //
+
+ return(NERR_Success);
+
+}
+#else
+
+NET_API_STATUS
+I_BrowserrDebugCall (
+ IN LPTSTR servername OPTIONAL,
+ IN DWORD DebugCode,
+ IN DWORD OptionalValue
+ )
+{
+ return(ERROR_NOT_SUPPORTED);
+
+}
+NET_API_STATUS
+I_BrowserrDebugTrace (
+ IN LPTSTR servername OPTIONAL,
+ IN LPSTR String
+ )
+{
+ return(ERROR_NOT_SUPPORTED);
+
+}
+#endif
diff --git a/private/net/svcdlls/browser/server/srvenum.h b/private/net/svcdlls/browser/server/srvenum.h
new file mode 100644
index 000000000..2c6192866
--- /dev/null
+++ b/private/net/svcdlls/browser/server/srvenum.h
@@ -0,0 +1,117 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ srvenum.h
+
+Abstract:
+
+ Private header file to be included by Browser service modules that need
+ to know about server enumeration routines (including the browse cache
+ modules).
+
+
+Author:
+
+ Larry Osterman (larryo) 23-Jun-1993
+
+Revision History:
+
+--*/
+
+
+#ifndef _SRVENUM_INCLUDED_
+#define _SRVENUM_INCLUDED_
+
+//
+// Cached browse response.
+//
+// The cached browse request structure is used to hold the response to
+// a NetServerEnum request.
+//
+// If a NetServerEnum request comes in through Xactsrv, the browser will
+// look up to see if there is a cached browse that matches this request,
+// and if there is, it will simply return that request to the caller.
+//
+//
+// In a nutshell, this is how the response cache works:
+//
+// The browser keeps a list of all of the browse requests that come into the
+// browser. This list is keyed by Level, ServerType, and buffer size. The
+// actual chain is protected by a CriticalSection called the
+// ResponseCacheLock. Entries in the list are protected by the global
+// network lock.
+//
+// When a browse request is received from Xactsrv, the browser looks up
+// the request in the response cache, and if it finds a matching response,
+// it increments 2 hit counters. The first hit counter indicates he number
+// of hits the request has seen since the last time the cache was aged.
+// The second indicates the total number of hits over the lifetime of the
+// browser for this response.
+//
+// If the lifetime hit count is over the configurable hit limit, the
+// browser will save a copy of the response buffer associated with the
+// request. Any and all subsequent browse requests will use this buffer
+// for their response instead of converting the response.
+//
+// When a call is made to BrAgeResponseCache, the browser will scan the
+// cache and free up all of the cached responses. It will also delete
+// any responses that have a hit count less than the hit limit.
+//
+
+typedef struct _CACHED_BROWSE_RESPONSE {
+ LIST_ENTRY Next; // Pointer to next request.
+ DWORD HitCount; // Hitcount for this cached request.
+ DWORD TotalHitCount; // Total hit count for this request.
+ DWORD LowHitCount; // Number of passes with a low hit count.
+ DWORD ServerType; // Server type.
+ DWORD Level; // Level of request
+ WORD Size; // Request size
+ WORD Converter; // Converter (used by client to get strings right).
+
+ PVOID Buffer; // Response buffer.
+ DWORD EntriesRead; // Number of entries in cached list
+ DWORD TotalEntries; // Total # of entries available.
+ WORD Status; // Status of request.
+ WCHAR FirstNameToReturn[CNLEN+1]; // Name of first entry in buffer
+} CACHED_BROWSE_RESPONSE, *PCACHED_BROWSE_RESPONSE;
+
+
+
+
+PCACHED_BROWSE_RESPONSE
+BrLookupAndAllocateCachedEntry(
+ IN PNETWORK Network,
+ IN DWORD ServerType,
+ IN WORD Size,
+ IN ULONG Level,
+ IN LPCWSTR FirstNameToReturn
+ );
+
+NET_API_STATUS
+BrDestroyResponseCache(
+ IN PNETWORK Network
+ );
+
+NET_API_STATUS
+BrDestroyCacheEntry(
+ IN PCACHED_BROWSE_RESPONSE CacheEntry
+ );
+
+VOID
+BrAgeResponseCache(
+ IN PNETWORK Network
+ );
+
+PCACHED_BROWSE_RESPONSE
+BrAllocateResponseCacheEntry(
+ IN PNETWORK Network,
+ IN DWORD ServerType,
+ IN WORD Size,
+ IN ULONG Level,
+ IN LPCWSTR FirstNameToReturn
+ );
+
+#endif // _SRVENUM_INCLUDED_
diff --git a/private/net/svcdlls/browser2/bchk/bchk.c b/private/net/svcdlls/browser2/bchk/bchk.c
new file mode 100644
index 000000000..a7444f274
--- /dev/null
+++ b/private/net/svcdlls/browser2/bchk/bchk.c
@@ -0,0 +1,1902 @@
+/*++
+
+Copyright (c) 1993 Micorsoft Corporation
+
+Module Name:
+
+ bchk.c
+
+Abstract:
+
+ Browser Monitor main program.
+
+Author:
+
+ Congpa You (CongpaY) 10-Feb-1993
+
+Revision History:
+
+--*/
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <sys\types.h>
+#include <sys\stat.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <windows.h>
+#include <lm.h>
+#include <ntddbrow.h>
+#include <brcommon.h> // svcdll\browser
+#include <rap.h>
+#include <rxserver.h>
+#include <winerror.h> // inc
+#include <rpcutil.h> // for MIDL_user_free. net\inc
+#include "bchk.h"
+
+// global data for counting.
+INT nCountFail[ERRORTYPENUM];
+INT nCountSuccess[ERRORTYPENUM];
+INT nTimes;
+PERRORLIST pErrorList[ERRORTYPENUM];
+FILE * pLOGFILE;
+
+void _CRTAPI1 main()
+{
+ DWORD dwVal; // The value returned from function calls.
+ PWSTR * BrowserList = NULL; // The browser list returned from Master Browser.
+ ULONG BrowserListLength = 0;
+ INITPARAM InitParam;
+ struct _stat buf;
+ PLMDR_TRANSPORT_LIST TransportList = NULL;
+ PLMDR_TRANSPORT_LIST TransportEntry = NULL;
+
+ // Set trap on ctrl-c.
+ if (!SetConsoleCtrlHandler (ConvertFile, TRUE))
+ {
+ printf("Error %lu occured in SetConsoleCtrlHandler", GetLastError());
+ return;
+ }
+
+ try
+ {
+ // Get nTolerance, nSleepTime, lpUser, lpDomain from bchk.ini.
+ if (!Init (&InitParam))
+ return;
+
+ // Create a logfile for writing all the errors and other information to.
+ pLOGFILE = fopen (szBCHKLOG, "w+");
+
+ // Set all count to 0.
+ InitAllCount();
+
+ do // This runs forever.
+ {
+ fprintf(stdout, "BCHK is running.\n") ;
+
+ // Print the header in the bchklog file.
+ PrintHeader (pLOGFILE);
+
+ // Count the times of the check.
+ nTimes++;
+
+ // Find all transports that we have.
+ dwVal = GetBrowserTransportList (&TransportList);
+ if (dwVal != NERR_Success)
+ {
+ if (TransportList != NULL)
+ {
+ MIDL_user_free (TransportList);
+ }
+ MyMessageBox (dwVal);
+ return;
+ }
+
+ TransportEntry = TransportList;
+
+ // Enumerate on the transports.
+ while (TransportEntry != NULL)
+ {
+ TCHAR lpDomain[STRINGLEN]; // The array of domain names.
+ LPTSTR lpDomainName; // One domain's name.
+ UNICODE_STRING TransportName;
+
+ TransportName.Buffer = TransportEntry->TransportName;
+ TransportName.Length = (USHORT) TransportEntry->TransportNameLength;
+ TransportName.MaximumLength = (USHORT) TransportEntry->TransportNameLength;
+
+ // Enumerate all the Domains that we want. All domains are in one string.
+ // They are separated by space.
+ lstrcpy (lpDomain, InitParam.lpDomain);
+ lpDomainName = strtokf (lpDomain, szSeps);
+
+ while (lpDomainName != NULL)
+ {
+ BOOL fFoundMaster = FALSE; // See if we have master browser's name.
+ TCHAR lpMasterName[UNCLEN+1];
+ {
+ SYSTEMTIME systime;
+
+ GetLocalTime(&systime);
+
+ fprintf(pLOGFILE, "Run %ws %ws started at %2.2d/%2.2d/%4.4d %2.2d:%2.2d:%2.2d:%4.4d\n",
+ lpDomainName,
+ TransportName.Buffer,
+ systime.wMonth,
+ systime.wDay,
+ systime.wYear,
+ systime.wHour,
+ systime.wMinute,
+ systime.wSecond,
+ systime.wMilliseconds);
+
+ }
+
+
+ // Check for error type 1: Browser master is not returning
+ // backup browser list.
+
+ dwVal = GetBrowserServerList (&TransportName,
+ lpDomainName,
+ &BrowserList,
+ &BrowserListLength,
+ TRUE);
+
+ if (dwVal != NERR_Success) // Type 1 error occured.
+ {
+ //
+ // Send a magic bullet on this failure.
+ //
+
+ SendMagicBullet();
+
+ ReportError (InitParam.lpUser,
+ TransportName.Buffer,
+ lpDomainName,
+ lpMasterName,
+ NULL,
+ dwVal,
+ NO_MASTER_RUNNING); //Error type number.
+
+ // Try to get Domain Master's name.
+ if (GetMasterName (lpMasterName,
+ TransportName.Buffer,
+ lpDomainName) == NERR_Success)
+ {
+ ReportError (InitParam.lpUser,
+ TransportName.Buffer,
+ lpDomainName,
+ NULL,
+ lpMasterName,
+ dwVal,
+ INVALID_MASTER); //Error type number.
+ }
+ else
+ {
+ nCountSuccess[INVALID_MASTER]++;
+ }
+ }
+ else if (BrowserListLength == 1) //Type 1 error didnot occur but there is only one browser on the domain.
+ {
+ nCountSuccess[NO_MASTER_RUNNING]++;
+ ReportError (InitParam.lpUser,
+ TransportName.Buffer,
+ lpDomainName,
+ BrowserList[0],
+ NULL,
+ 0,
+ NO_BACKUP);
+ }
+ else // Type 1 error didn't occur and there are backup browsers.
+ {
+ WriteBrowserList (pLOGFILE, BrowserList, BrowserListLength);
+
+ nCountSuccess[NO_MASTER_RUNNING]++;
+ nCountSuccess[NO_BACKUP]++;
+
+ // Try to get Domain Master's name.
+ dwVal = GetMasterName (lpMasterName,
+ TransportName.Buffer,
+ lpDomainName);
+ if (dwVal == NERR_Success)
+ {
+ nCountSuccess[NO_MASTER_NAME]++;
+ fFoundMaster = TRUE;
+ }
+ else // Try other ways to find the master browser's name.
+ {
+ ReportError (InitParam.lpUser,
+ TransportName.Buffer,
+ lpDomainName,
+ NULL,
+ NULL,
+ dwVal,
+ NO_MASTER_NAME); //Error type number.
+
+ fFoundMaster = RxGetMasterName (InitParam.lpUser,
+ lpMasterName,
+ TransportName.Buffer,
+ lpDomainName,
+ BrowserList,
+ BrowserListLength,
+ InitParam.nTimeLimit);
+ }
+
+ if (fFoundMaster) // Report error if we donot have master name
+ {
+ // Log the master browser's name.
+ LogMaster (pLOGFILE,
+ TransportName.Buffer,
+ lpDomainName,
+ lpMasterName);
+
+ // Check for error type 2: Incorrect server lists returned by
+ // backup browsers (and master browsers). This includes stale
+ // servers in the list.
+ CheckErrorType2 (InitParam.lpUser,
+ TransportName.Buffer,
+ lpDomainName,
+ lpMasterName,
+ BrowserList,
+ BrowserListLength,
+ InitParam.nTolerance,
+ InitParam.nTimeLimit);
+
+ // Check for error type 3: Incorrect number of browser servers.
+ CheckErrorType3 (InitParam.lpUser,
+ TransportName.Buffer,
+ lpDomainName,
+ lpMasterName);
+
+ // Check for error type 4: Master is not PDC.
+ CheckErrorType4 (InitParam.lpUser,
+ TransportName.Buffer,
+ lpDomainName,
+ lpMasterName);
+
+ } // End of else when we have the master name.
+
+ } // End of else when type 1 error didnot occur and there are backup browsers.
+
+ if (BrowserList != NULL)
+ {
+ MIDL_user_free(BrowserList);
+ }
+
+ {
+ SYSTEMTIME systime;
+
+ GetLocalTime(&systime);
+
+ fprintf(pLOGFILE, "Run %ws %ws completed at %2.2d/%2.2d/%4.4d %2.2d:%2.2d:%2.2d:%4.4d\n",
+ lpDomainName,
+ TransportName.Buffer,
+ systime.wMonth,
+ systime.wDay,
+ systime.wYear,
+ systime.wHour,
+ systime.wMinute,
+ systime.wSecond,
+ systime.wMilliseconds);
+
+ }
+
+ // print out == between domains.
+
+ PrintSeparation (pLOGFILE);
+
+ lpDomainName = strtokf (NULL, szSeps);
+
+ } // End of the enumeration of domains.
+
+ if (TransportEntry->NextEntryOffset == 0)
+ TransportEntry = NULL;
+ else
+ {
+ TransportEntry = (PLMDR_TRANSPORT_LIST) ((PCHAR) TransportEntry
+ +TransportEntry->NextEntryOffset);
+ }
+
+ } // End of the enumeration of transports.
+
+ // Free memory.
+ MIDL_user_free (TransportList);
+
+ // Write the result to the summory file.
+ WriteSummory();
+
+ // Make sure alert is writen to the disk.
+ fflush (pLOGFILE);
+
+ // Check if the file size is too big. Reopen it if it's so.
+ if (_fstat (_fileno(pLOGFILE), &buf) != -1)
+ {
+ printf("File size = %ld, Limit = %d\n", buf.st_size, InitParam.nFileSizeLimit);
+
+ if (buf.st_size > InitParam.nFileSizeLimit)
+ {
+ fclose (pLOGFILE);
+
+ if (!MoveFileEx (szLBCHKLOG, szLBACKUP, MOVEFILE_REPLACE_EXISTING))
+ {
+ pLOGFILE = fopen (szBCHKLOG, "w+");
+ LogError (pLOGFILE, GetError(GetLastError()));
+ }
+ else
+ pLOGFILE = fopen (szBCHKLOG, "w+");
+ }
+ }
+
+ // Wait for a while before start again.
+ fprintf(stdout, "bchk is sleeping for %d secs\n", InitParam.nSleepTime/1000) ;
+ Sleep (InitParam.nSleepTime);
+
+ } while (TRUE);
+ }
+ finally
+ {
+ ConvertFile(0);
+ LocalFree (InitParam.lpUser);
+ LocalFree (InitParam.lpDomain);
+ }
+ return;
+}
+
+// Get nTolerance, nSleepTime, lpUser, lpDomain from bchk.ini.
+BOOL Init (INITPARAM * pInitParam)
+{
+ DWORD dwVal;
+ DWORD dwComputerNameLength = STRINGLEN;
+ LPTSTR lpUser;
+ LPTSTR lpTemp;
+
+ pInitParam->lpUser = (LPTSTR) LocalAlloc (LPTR, STRINGLEN);
+ pInitParam->lpDomain = (LPTSTR) LocalAlloc (LPTR, STRINGLEN);
+ lpUser = (LPTSTR) LocalAlloc (LPTR, STRINGLEN);
+ lpTemp = (LPTSTR) LocalAlloc (LPTR, INTLEN);
+
+ if ((pInitParam->lpUser == NULL) ||
+ (pInitParam->lpDomain == NULL) ||
+ (lpUser == NULL) ||
+ (lpTemp == NULL))
+ {
+ MyMessageBox (ERROR_NOT_ENOUGH_MEMORY);
+ return(FALSE);
+ }
+
+ // Get the current computer name. We will always notify the current user
+ // when an error occurs. This computer name is for sending message.
+ if (!GetComputerName (pInitParam->lpUser, &dwComputerNameLength))
+ {
+ MyMessageBox (GetLastError());
+ return(FALSE);
+ }
+
+ // Read nTolerance from bchk.ini. We allow backup browser return
+ // nTolerance different entries from those from master browser.
+ GetPrivateProfileString (szAPPNAME,
+ szTOLERANCE,
+ szDefaultTolerance,
+ lpTemp,
+ INTLEN,
+ szFILENAME);
+
+ pInitParam->nTolerance = atoi(toansi(lpTemp));
+
+ // Read nTolerance from bchk.ini. We allow backup browser return
+ // nTolerance different entries from those from master browser.
+ GetPrivateProfileString (szAPPNAME,
+ szTIMELIMIT,
+ szDefaultTimeLimit,
+ lpTemp,
+ INTLEN,
+ szFILENAME);
+
+ pInitParam->nTimeLimit = atoi(toansi(lpTemp));
+
+ // Get the sleep time from bchk.ini.
+ GetPrivateProfileString (szAPPNAME,
+ szSLEEPTIME,
+ szDefaultSleepTime, //millisecond.
+ lpTemp,
+ INTLEN,
+ szFILENAME);
+
+ pInitParam->nSleepTime = atoi(toansi(lpTemp));
+
+ // Get the file size limit from bchk.ini.
+ GetPrivateProfileString (szAPPNAME,
+ szFILESIZE,
+ szDefaultFileSize, //millisecond.
+ lpTemp,
+ INTLEN,
+ szFILENAME);
+
+ pInitParam->nFileSizeLimit = atoi(toansi(lpTemp));
+
+ // Read the people we want to notify when an browser error occurs from bchk.ini.
+ dwVal = GetPrivateProfileString (szAPPNAME,
+ szOTHERUSERS,
+ szDefaultOtherUser,
+ lpUser,
+ STRINGLEN,
+ szFILENAME);
+
+ if (dwVal >= (STRINGLEN-2)) // The memory assigned to lpUser is not big enough.
+ {
+ MyMessageBox (ERROR_NOT_ENOUGH_MEMORY);
+ return(FALSE);
+ }
+ else
+ {
+ lstrcat (pInitParam->lpUser, L" ");
+ lstrcat (pInitParam->lpUser, lpUser);
+ }
+
+ // Read all the domains that we want to check from bchk.ini
+ dwVal = GetPrivateProfileString (szAPPNAME,
+ szDOMAINS,
+ szDefaultDomain,
+ pInitParam->lpDomain,
+ STRINGLEN,
+ szFILENAME);
+
+ if (dwVal >= (STRINGLEN-2)) // The memory assigned to lpDomainList is not big enough.
+ {
+ MyMessageBox (ERROR_NOT_ENOUGH_MEMORY);
+ return(FALSE);
+ }
+
+ //
+ // Make sure that the domain name is upper cased.
+ //
+
+ _wcsupr(pInitParam->lpDomain);
+
+ LocalFree (lpUser);
+ LocalFree (lpTemp);
+
+ return(TRUE);
+}
+
+void InitAllCount ()
+{
+ INT i;
+
+ nTimes = 0;
+
+ for (i = 0; i< ERRORTYPENUM ; i++)
+ {
+ nCountFail[i] = 0;
+ nCountSuccess[i] = 0;
+ }
+}
+
+// Try to get the Domain Master's name using GetNetBiosMasterName.
+NET_API_STATUS GetMasterName (LPTSTR lpMasterName,
+ LPTSTR lpTransportName,
+ LPTSTR lpDomainName)
+{
+ TCHAR lpTempMaster[UNCLEN+1];
+ DWORD dwStartTime;
+ DWORD dwEndTime;
+ NET_API_STATUS dwVal;
+
+ dwStartTime = GetTickCount();
+
+ dwVal = GetNetBiosMasterName (lpTransportName,
+ lpDomainName,
+ lpTempMaster,
+ NULL );
+
+ dwEndTime = GetTickCount();
+
+ if (dwVal == NERR_Success)
+ {
+ // lpTempMaster has lots of space following
+ // the characters. We want to make a null terminated
+ // string, and add \\ in front of master name.
+ lstrcpy (lpMasterName, L"\\\\");
+ KillSpace (lpTempMaster);
+ lstrcat (lpMasterName, lpTempMaster);
+ }
+ else
+ {
+ fprintf (pLOGFILE, "GetNetBiosMasterName failed after %d milliseconds.\n",
+ dwEndTime-dwStartTime);
+
+ wsprintf (lpMasterName, L"unknown");
+ }
+ return(dwVal);
+}
+
+// Use RxNetServerEnum call to get Domain Master's name.
+BOOL RxGetMasterName (LPTSTR lpUser,
+ LPTSTR lpMasterName,
+ LPTSTR lpTransportName,
+ LPTSTR lpDomainName,
+ PWSTR * BrowserList,
+ DWORD BrowserListLength,
+ DWORD nTimeLimit)
+{
+ INT i = 0;
+ DWORD dwVal;
+ DWORD dwTotalEntries; // Total servers returned from Backup Browser.
+ DWORD dwEntriesRead; // Total servers read from Backup Browser.
+ LPVOID lpBrowserList;
+// TCHAR lpErrorMessage[STRINGLEN];
+ PSERVER_INFO_101 pBrowserServerInfo101;
+ DWORD dwStartTime;
+ DWORD dwEndTime;
+
+ for (i = 0 ; i < (INT) BrowserListLength ; i++)
+ {
+ dwStartTime = GetTickCount();
+
+ dwVal = MyRxNetServerEnum (BrowserList[i],
+ lpTransportName,
+ 101,
+ (LPBYTE *) &lpBrowserList,
+ 0xffffffff,
+ &dwEntriesRead,
+ &dwTotalEntries,
+ SV_TYPE_MASTER_BROWSER,
+ lpDomainName,
+ NULL);
+
+ dwEndTime = GetTickCount();
+
+ if (dwVal == NERR_Success) // Get the master browser list.
+ {
+ nCountSuccess[FAIL_RETURN_MASTER]++;
+
+ if ((dwEndTime - dwStartTime) > nTimeLimit) {
+
+ dwVal = dwEndTime - dwStartTime;
+
+ //
+ // If this took "too long", log it as an error.
+ //
+
+ ReportError (lpUser,
+ lpTransportName,
+ lpDomainName,
+ NULL,
+ BrowserList[i],
+ dwVal,
+ BROWSE_TOO_LONG);
+
+ if ((dwEndTime - dwStartTime) > 2*nTimeLimit) {
+ SendMagicBullet();
+ }
+
+
+ } else {
+ nCountSuccess[BROWSE_TOO_LONG] += 1;
+ }
+
+ if (dwEntriesRead != 1) // Wrong master browser list
+ {
+ WriteList (pLOGFILE, lpBrowserList, dwEntriesRead);
+
+ MIDL_user_free (lpBrowserList);
+
+ ReportError (lpUser,
+ lpTransportName,
+ lpDomainName,
+ NULL,
+ BrowserList[i],
+ dwEntriesRead,
+ WRONG_NUM_MASTER); //Error type number.
+
+ }
+ else //Found the master.
+ {
+ nCountSuccess[WRONG_NUM_MASTER]++;
+ nCountSuccess[UNKNOWN_MASTER]++;
+
+ pBrowserServerInfo101 = lpBrowserList;
+ wsprintf(lpMasterName, L"\\\\%s", pBrowserServerInfo101[0].sv101_name);
+ MIDL_user_free (lpBrowserList);
+ return(TRUE);
+ }
+
+ } // End of if when we get the master browser list.
+ else
+ {
+ if (lpBrowserList != NULL)
+ {
+ MIDL_user_free (lpBrowserList);
+ }
+
+ ReportError (NULL,
+ lpTransportName,
+ lpDomainName,
+ NULL,
+ BrowserList[i],
+ dwVal,
+ FAIL_RETURN_MASTER);
+ }
+
+ } // End of the for loop
+
+ ReportError (lpUser,
+ lpTransportName,
+ lpDomainName,
+ NULL,
+ NULL,
+ 0,
+ UNKNOWN_MASTER); //Error type number.
+
+ return(FALSE);
+}
+
+// Generate an error message, write to the logfile and send to all the users.
+void ReportError (LPTSTR lpUser,
+ LPTSTR lpTransportName,
+ LPTSTR lpDomainName,
+ LPTSTR lpMasterName,
+ LPTSTR lpBrowserName,
+ DWORD dwError,
+ INT nErrorType)
+{
+ TCHAR lpErrorMessage[STRINGLEN];
+ DWORD dwNSGI = 0;
+ PSERVER_INFO_101 pSV101 = NULL;
+
+ static BOOL fError[ERRORTYPENUM] = {FALSE, FALSE, FALSE, FALSE, FALSE,
+ FALSE, FALSE, FALSE, FALSE, FALSE,
+ FALSE, FALSE, FALSE, FALSE, FALSE,
+ FALSE, FALSE, FALSE, FALSE};
+
+ nCountFail[nErrorType]++;
+
+ switch (nErrorType)
+ {
+ case NO_MASTER_RUNNING:
+ GetMasterName (lpMasterName,
+ lpTransportName,
+ lpDomainName);
+
+ wsprintf (lpErrorMessage,
+ L"No browser master is running on domain %s, transport %s. \nThe browser master's name is %ws.\n",
+ lpDomainName,
+ lpTransportName,
+ lpMasterName);
+ lstrcat (lpErrorMessage, GetError (dwError));
+
+ break;
+
+ case INVALID_MASTER:
+ wsprintf (lpErrorMessage,
+ L"Browser added a not-running master %s on domain %s transport %s.\n",
+ lpBrowserName,
+ lpDomainName,
+ lpTransportName);
+ break;
+
+ case NO_BACKUP:
+ wsprintf (lpErrorMessage,
+ L"There are no backup browsers on domain %s transport %s.\nThe domain master is owned by %s.\n",
+ lpDomainName,
+ lpTransportName,
+ lpMasterName);
+ break;
+
+ case NO_MASTER_NAME:
+ wsprintf (lpErrorMessage,
+ L"GetNetBiosMasterName failed on domain %s transport %s.\n",
+ lpDomainName,
+ lpTransportName);
+ lstrcat (lpErrorMessage, GetError (dwError));
+ break;
+
+ case WRONG_NUM_MASTER:
+ wsprintf (lpErrorMessage,
+ L"%lu browser masters are returned from %s.\nCannot determine the master browser on domain %s, transport%s through %s.\n",
+ dwError, // = dwEntriesRead
+ lpBrowserName,
+ lpDomainName,
+ lpTransportName,
+ lpBrowserName);
+ break;
+
+ case FAIL_RETURN_MASTER:
+ wsprintf (lpErrorMessage,
+ L"Browser %s on domain %s, transport %s failed to return its master.\n",
+ lpBrowserName,
+ lpDomainName,
+ lpTransportName);
+ lstrcat (lpErrorMessage, GetError (dwError));
+ break;
+
+ case UNKNOWN_MASTER:
+ wsprintf (lpErrorMessage,
+ L"Cannot determine the master browser on domain %s, transport %s.\n",
+ lpDomainName,
+ lpTransportName);
+ break;
+
+ case MASTER_NO_SERVER_LIST:
+ wsprintf (lpErrorMessage,
+ L"The master browser %s on domain %s transport %s failed to return the server list.\n",
+ lpMasterName,
+ lpDomainName,
+ lpTransportName);
+
+ lstrcat (lpErrorMessage, GetError (dwError));
+ break;
+
+ case BROWSER_NO_SERVER_LIST:
+ wsprintf (lpErrorMessage,
+ L"Browser %s on domain %s transport %s failed to return the server list.\nThe domain master is owned by %s.\n",
+ lpBrowserName,
+ lpDomainName,
+ lpTransportName,
+ lpMasterName);
+
+ lstrcat (lpErrorMessage, GetError (dwError));
+ break;
+
+ case MASTER_NO_DOMAIN_LIST:
+ wsprintf (lpErrorMessage,
+ L"The master browser %s on domain %s transport %s failed to return the domain list.\n",
+ lpMasterName,
+ lpDomainName,
+ lpTransportName);
+
+ lstrcat (lpErrorMessage, GetError (dwError));
+ break;
+
+ case BROWSER_NO_DOMAIN_LIST:
+ wsprintf (lpErrorMessage,
+ L"Browser %s on domain %s transport %s failed to return the domain list.\nThe domain master is owned by %s.\n",
+ lpBrowserName,
+ lpDomainName,
+ lpTransportName,
+ lpMasterName);
+
+ lstrcat (lpErrorMessage, GetError (dwError));
+ break;
+
+ case STALE_SERVER:
+ wsprintf (lpErrorMessage,
+ L"Browser %s on domain %s,transport %s returned an incorrect number of servers. \nThe domain master is owned by %s.\n",
+ lpBrowserName,
+ lpDomainName,
+ lpTransportName,
+ lpMasterName);
+ break;
+
+ case STALE_DOMAIN:
+ wsprintf (lpErrorMessage,
+ L"Browser %s on domain %s,transport %s returned an incorrect number of domains. \nThe domain master is owned by %s.\n",
+ lpBrowserName,
+ lpDomainName,
+ lpTransportName,
+ lpMasterName);
+ break;
+
+ case MASTER_NO_LOCAL_LIST:
+ wsprintf (lpErrorMessage,
+ L"The master browser %s on domain %s transport %s failed to return the local list.\n",
+ lpMasterName,
+ lpDomainName,
+ lpTransportName);
+
+ lstrcat (lpErrorMessage, GetError (dwError));
+ break;
+
+ case WRONG_NUM_BACKUP:
+ wsprintf (lpErrorMessage,
+ L"Incorrect number of Backup Browsers on domain %s transport %s \n nNTMachine = %d \n nBackupBrowser = %d \n nServer = %d \n Expected %d backups \n",
+ lpDomainName,
+ lpTransportName,
+ ((INT *)dwError)[0],
+ ((INT *)dwError)[1],
+ ((INT *)dwError)[2],
+ ((INT *)dwError)[3]);
+
+ break;
+
+ case NO_PDC:
+ wsprintf (lpErrorMessage,
+ L"There are no PDC on domain %s transport %s.\nThe domain master is owned by %s.\n",
+ lpDomainName,
+ lpTransportName,
+ lpMasterName);
+
+ lstrcat (lpErrorMessage, GetError (dwError));
+ break;
+
+ case MASTER_NOT_PDC:
+ wsprintf (lpErrorMessage,
+ L"The master browser %s on domain %s transport %s is not a PDC.\nPDC is owned by %s.\n",
+ lpBrowserName,
+ lpDomainName,
+ lpTransportName,
+ lpMasterName);
+
+ break;
+ case BROWSE_TOO_LONG:
+ wsprintf (lpErrorMessage,
+ L"Browse request to %ws (domain %ws, transport %ws), took %ld milliseconds.\n",
+ lpBrowserName,
+ lpDomainName,
+ lpTransportName,
+ dwError);
+
+ break;
+
+ default:
+ return;
+ }
+
+ if (lpBrowserName != NULL)
+ {
+ lstrcat (lpErrorMessage, ServerInfo(lpBrowserName));
+
+ if ((dwError != 0) && ((nErrorType == MASTER_NO_SERVER_LIST) ||
+ (nErrorType == MASTER_NO_DOMAIN_LIST) ||
+ (nErrorType == MASTER_NO_LOCAL_LIST) ||
+ (nErrorType == BROWSER_NO_SERVER_LIST)||
+ (nErrorType == BROWSER_NO_DOMAIN_LIST)))
+
+ {
+ WCHAR ShareName[UNCLEN+1];
+
+ wcscpy(ShareName, lpBrowserName);
+ wcscat(ShareName, L"\\Ipc$");
+
+ NetUseDel(NULL, ShareName, USE_LOTS_OF_FORCE);
+
+ dwNSGI = NetServerGetInfo (lpBrowserName,
+ 101,
+ (LPBYTE *)&pSV101);
+
+ if (dwNSGI == NERR_Success &&
+ !(pSV101->sv101_type & SV_TYPE_POTENTIAL_BROWSER))
+ {
+ if (dwError == ERROR_BAD_NETPATH && dwNSGI == NERR_Success) {
+ DbgPrint("Sending magic bullet\n");
+ SendMagicBullet();
+ }
+
+ CheckErrorType5(lpBrowserName,
+ lpTransportName,
+ lpDomainName,
+ lpUser);
+ } else {
+#if 0
+
+ if (dwError == ERROR_VC_DISCONNECTED &&
+ dwNSGI == ERROR_VC_DISCONNECTED) {
+ DbgPrint("Sending magic bullet\n");
+ SendMagicBullet();
+ }
+#endif
+
+ }
+
+ if (pSV101 != NULL)
+ {
+ MIDL_user_free (pSV101);
+ }
+ }
+ }
+
+ RecordError (&fError[nErrorType],
+ lpBrowserName,
+ lpTransportName,
+ lpDomainName,
+ lpUser,
+ lpErrorMessage,
+ dwError,
+ dwNSGI,
+ nErrorType);
+}
+
+void RecordError (BOOL * pfError,
+ LPTSTR lpBrowserName,
+ LPTSTR lpTransportName,
+ LPTSTR lpDomainName,
+ LPTSTR lpUser,
+ LPTSTR lpErrorMessage,
+ DWORD dwError,
+ DWORD dwNSGI,
+ INT nErrorType)
+{
+ SYSTEMTIME systime;
+
+ GetLocalTime (&systime);
+
+ if ((nErrorType != BROWSER_NO_SERVER_LIST) &&
+ (nErrorType != BROWSER_NO_DOMAIN_LIST) &&
+ (nErrorType != NO_PDC))
+ {
+ LogError (pLOGFILE, lpErrorMessage);
+ }
+ else
+ {
+ LogWarning (pLOGFILE, lpErrorMessage);
+ }
+
+ //
+ // Notify that the error occurred.
+ //
+
+ if ((nErrorType != BROWSER_NO_SERVER_LIST) &&
+ (nErrorType != BROWSER_NO_DOMAIN_LIST) &&
+ (nErrorType != MASTER_NOT_PDC) &&
+ (nErrorType != WRONG_NUM_BACKUP) &&
+ (nErrorType != NO_PDC)) {
+ NotifyUser (lpUser, lpErrorMessage);
+ }
+
+ if (*pfError) // A known error.
+ {
+ ERRORLIST * pErrorEntry = pErrorList[nErrorType];
+
+ do
+ {
+ if (!lstrcmp (pErrorEntry->lpTransport, lpTransportName) &&
+ !lstrcmp (pErrorEntry->lpDomain, lpDomainName) &&
+ ((lpBrowserName == NULL) ||
+ ((lpBrowserName != NULL) &&
+ (!lstrcmp (pErrorEntry->lpServer, lpBrowserName)))) &&
+ ((nErrorType == WRONG_NUM_BACKUP) ||
+ (nErrorType == STALE_SERVER) ||
+ (nErrorType == STALE_DOMAIN) ||
+ ((pErrorEntry->nVal1 == dwError) &&
+ (pErrorEntry->nVal2 == dwNSGI))))
+ {
+ if ((nErrorType != STALE_SERVER) &&
+ (nErrorType != STALE_DOMAIN))
+ {
+ // record the last time the error occurred.
+ pErrorEntry->wHour = systime.wHour;
+ pErrorEntry->wMinute = systime.wMinute;
+ }
+ else
+ {
+ AddStaleEntry (pErrorEntry,
+ ((DWORD *) dwError)[1],
+ ((DWORD *) dwError)[0],
+ systime.wHour,
+ systime.wMinute);
+ }
+ (pErrorEntry->nCount)++;
+ return;
+ }
+
+ if (pErrorEntry->pNextEntry != NULL)
+ {
+ pErrorEntry = pErrorEntry->pNextEntry;
+ }
+ else
+ {
+ pErrorEntry->pNextEntry = NewEntry (lpBrowserName,
+ lpTransportName,
+ lpDomainName,
+ dwError,
+ dwNSGI,
+ systime.wHour,
+ systime.wMinute,
+ nErrorType);
+ return;
+ }
+ } while (TRUE);
+ }
+ else // Error never occur before.
+ {
+ pErrorList[nErrorType] = NewEntry(lpBrowserName,
+ lpTransportName,
+ lpDomainName,
+ dwError,
+ dwNSGI,
+ systime.wHour,
+ systime.wMinute,
+ nErrorType);
+ if (pErrorList[nErrorType] != NULL)
+ {
+ (*pfError) = TRUE;
+ }
+ }
+}
+
+PERRORLIST NewEntry (LPTSTR lpServer,
+ LPTSTR lpTransport,
+ LPTSTR lpDomain,
+ DWORD dwError,
+ DWORD dwNSGI,
+ WORD wHour,
+ WORD wMinute,
+ INT nErrorType)
+{
+ PERRORLIST pNewEntry;
+
+ // Added for reduce the logfile size, ie not report error caused
+ // by machine crash.
+ if ((dwError == 53) &&
+ (dwNSGI == 53) &&
+ !IsOurMachine (lpServer))
+ {
+ return(NULL);
+ }
+
+ pNewEntry = (PERRORLIST) LocalAlloc (LPTR, sizeof(ERRORLIST));
+ if (pNewEntry == NULL)
+ {
+ fprintf(stdout, "Not enough memory");
+ return(NULL);
+ }
+
+ pNewEntry->lpTransport = (LPTSTR) LocalAlloc (LPTR, (2*lstrlen(lpTransport)+2));
+ pNewEntry->lpDomain = (LPTSTR) LocalAlloc (LPTR, (2*lstrlen(lpDomain)+2));
+ if ((pNewEntry->lpTransport == NULL) ||
+ (pNewEntry->lpDomain == NULL))
+ {
+ fprintf(stdout, "Not enough memory");
+ return(NULL);
+ }
+
+ if (lpServer != NULL)
+ {
+ pNewEntry->lpServer = (LPTSTR) LocalAlloc (LPTR, (2*lstrlen(lpServer)+2));
+ if (pNewEntry->lpServer == NULL)
+ {
+ fprintf(stdout, "Not enough memory");
+ return(NULL);
+ }
+ lstrcpy (pNewEntry->lpServer, lpServer);
+ }
+
+ lstrcpy (pNewEntry->lpTransport, lpTransport);
+ lstrcpy (pNewEntry->lpDomain, lpDomain);
+ pNewEntry->nCount = 1;
+ pNewEntry->wHour = wHour;
+ pNewEntry->wMinute = wMinute;
+ pNewEntry->pNextEntry = NULL;
+
+ if (nErrorType == WRONG_NUM_BACKUP)
+ {
+ pNewEntry->nVal1 = ((INT *)dwError)[0];
+ pNewEntry->nVal2 = ((INT *)dwError)[1];
+ pNewEntry->nVal3 = ((INT *)dwError)[2];
+ pNewEntry->nVal4 = ((INT *)dwError)[3];
+ }
+ else if ((nErrorType == STALE_SERVER)||
+ (nErrorType == STALE_DOMAIN))
+
+ {
+ AddStaleEntry (pNewEntry,
+ ((DWORD *)dwError)[1],
+ ((DWORD *)dwError)[0],
+ wHour,
+ wMinute);
+ }
+ else
+ {
+ pNewEntry->nVal1 = dwError;
+ pNewEntry->nVal2 = dwNSGI;
+ }
+
+ return(pNewEntry);
+}
+
+void AddStaleEntry (PERRORLIST pErrorList,
+ DWORD dwBackupEntry,
+ DWORD dwMasterEntry,
+ WORD wHour,
+ WORD wMinute)
+{
+ PENTRYLIST pNewEntry;
+
+ pNewEntry = (PENTRYLIST) LocalAlloc (LPTR, sizeof(ENTRYLIST));
+ if (pNewEntry == NULL)
+ {
+ fprintf(stdout, "Not enough memory");
+ return;
+ }
+
+ pNewEntry->dwBackupEntry = dwBackupEntry;
+ pNewEntry->dwMasterEntry = dwMasterEntry;
+ pNewEntry->wHour = wHour;
+ pNewEntry->wMinute = wMinute;
+ pNewEntry->pNextEntry = (PENTRYLIST) pErrorList->nVal1;
+
+ *((PENTRYLIST *)(&pErrorList->nVal1)) = pNewEntry;
+}
+
+// Get all the servers in lpBrowserName.
+BOOL MyGetList (LPTSTR lpUser,
+ LPTSTR lpBrowserName,
+ LPTSTR lpTransportName,
+ LPTSTR lpDomainName,
+ LPTSTR lpMasterName,
+ DWORD * pdwEntriesRead,
+ DWORD * pdwTotalEntries,
+ LPVOID * lppBrowserList,
+ BOOL fBrowserNotDomain,
+ DWORD nTimeLimit)
+{
+ DWORD dwVal;
+ INT nErrorType;
+ DWORD dwStartTime;
+ DWORD dwEndTime;
+
+ nErrorType = lstrcmp(lpBrowserName, lpMasterName)?
+ (fBrowserNotDomain? BROWSER_NO_SERVER_LIST : BROWSER_NO_DOMAIN_LIST) :
+ (fBrowserNotDomain? MASTER_NO_SERVER_LIST : MASTER_NO_DOMAIN_LIST);
+
+
+ dwStartTime = GetTickCount();
+
+ dwVal = MyRxNetServerEnum (lpBrowserName,
+ lpTransportName,
+ 101,
+ (LPBYTE *) lppBrowserList,
+ 0xffffffff,
+ pdwEntriesRead,
+ pdwTotalEntries,
+ fBrowserNotDomain? SV_TYPE_ALL : SV_TYPE_DOMAIN_ENUM,
+ lpDomainName,
+ NULL);
+
+ dwEndTime = GetTickCount();
+
+ if (dwVal != NERR_Success) // Failed to return backup browser list.
+ {
+ ReportError (lpUser,
+ lpTransportName,
+ lpDomainName,
+ lpMasterName,
+ lpBrowserName,
+ dwVal,
+ nErrorType);
+
+ return(FALSE);
+ }
+ else // Continue if we got a backup list from the backup browser.
+ {
+ nCountSuccess[nErrorType]++;
+
+ if ((dwEndTime - dwStartTime) > nTimeLimit) {
+
+ dwVal = dwEndTime - dwStartTime;
+
+ //
+ // If this took "too long", log it as an error.
+ //
+
+ ReportError (lpUser,
+ lpTransportName,
+ lpDomainName,
+ lpMasterName,
+ lpBrowserName,
+ dwVal,
+ BROWSE_TOO_LONG);
+
+ if ((dwEndTime - dwStartTime) > 2*nTimeLimit) {
+ SendMagicBullet();
+ }
+
+ } else {
+ nCountSuccess[BROWSE_TOO_LONG] += 1;
+ }
+
+ // Log the number of backups etc.
+ fprintf (pLOGFILE,
+ "There are %lu %s on browser %s\nEntriesRead = %lu. TotalEntries = %lu. Time = %lu milliseconds\n",
+ *pdwTotalEntries,
+ fBrowserNotDomain? "server" : "domain",
+ toansi (lpBrowserName),
+ *pdwEntriesRead,
+ *pdwTotalEntries,
+ dwEndTime - dwStartTime);
+
+ WriteList (pLOGFILE, *lppBrowserList, *pdwEntriesRead);
+ return(TRUE);
+ }
+}
+
+// Check for error type 2: Incorrect server lists returned by
+// backup browsers (and master browsers). This includes stale
+// servers in the list. Same for the domain list.
+void CheckErrorType2 (LPTSTR lpUser,
+ LPTSTR lpTransportName,
+ LPTSTR lpDomainName,
+ LPTSTR lpMasterName,
+ PWSTR * lpList,
+ DWORD dwEntries,
+ INT nTolerancePercent,
+ DWORD nTimeLimit)
+{
+ // Check if the server list returned from backup browser is the same
+ // as that from the master browser.
+
+ CheckList (lpUser,
+ lpTransportName,
+ lpDomainName,
+ lpMasterName,
+ lpList,
+ dwEntries,
+ nTolerancePercent,
+ TRUE,
+ nTimeLimit);
+
+ // Check if the domain list returned from backup browser is the same
+ // as that from the master browser.
+
+ CheckList (lpUser,
+ lpTransportName,
+ lpDomainName,
+ lpMasterName,
+ lpList,
+ dwEntries,
+ nTolerancePercent,
+ FALSE,
+ nTimeLimit);
+}
+
+void CheckList (LPTSTR lpUser,
+ LPTSTR lpTransportName,
+ LPTSTR lpDomainName,
+ LPTSTR lpMasterName,
+ PWSTR * lpList,
+ DWORD dwEntries,
+ INT nTolerancePercent,
+ BOOL fBrowserNotDomain,
+ DWORD nTimeLimit)
+{
+ INT i;
+ INT nTolerance;
+ INT nErrorType = fBrowserNotDomain? STALE_SERVER : STALE_DOMAIN;
+ BOOL fError = FALSE;
+ DWORD dwTotalEntries; // Total Backup Browsers
+ DWORD dwEntriesRead; // Total Backup Browsers Read.
+ DWORD dwBackupTotalEntries; // Total Backup Browsers
+ DWORD dwBackupEntriesRead; // Total Backup Browsers Read.
+ LPVOID lpBrowserList;
+ LPVOID lpBackupBrowserList;
+
+ if (!MyGetList(lpUser,
+ lpMasterName,
+ lpTransportName,
+ lpDomainName,
+ lpMasterName,
+ &dwEntriesRead,
+ &dwTotalEntries,
+ &lpBrowserList,
+ fBrowserNotDomain,
+ nTimeLimit))
+ {
+ if (lpBrowserList != NULL)
+ {
+ MIDL_user_free (lpBrowserList);
+ }
+
+ return;
+ }
+
+ nTolerance = nTolerancePercent*dwEntriesRead/100;
+
+ for (i = 0; i < (INT) dwEntries ; i++) // Enumerate on backups.
+ {
+ if (lstrcmp (lpMasterName, lpList[i])) // When they are different it returns true.
+ {
+ if (MyGetList (lpUser,
+ lpList[i],
+ lpTransportName,
+ lpDomainName,
+ lpMasterName,
+ &dwBackupEntriesRead,
+ &dwBackupTotalEntries,
+ &lpBackupBrowserList,
+ fBrowserNotDomain,
+ nTimeLimit))
+ {
+ // Compare the number of browserlist from the backup with the one
+ // from the master.
+
+ if ((abs(dwBackupEntriesRead - dwEntriesRead) > 10) &&
+ ((dwEntriesRead > dwBackupEntriesRead + nTolerance) ||
+ (dwEntriesRead < dwBackupEntriesRead - nTolerance)))
+ {
+ fError = TRUE;
+ }
+ else
+ {
+ // Compare the servers returned from backups with those from master.
+ // When the list are too different, CompareList returns false.
+ fError = !CompareList (lpBrowserList,
+ lpBackupBrowserList,
+ dwEntriesRead,
+ dwBackupEntriesRead,
+ nTolerance);
+ }
+
+ if (fError) // Type 2 error occured.
+ {
+ DWORD dwEntries[2] = {dwEntriesRead, dwBackupEntriesRead};
+
+ ReportError (lpUser,
+ lpTransportName,
+ lpDomainName,
+ lpMasterName,
+ lpList[i],
+ (DWORD)dwEntries,
+ nErrorType);
+ } // End of if type 2 error occured.
+ else
+ {
+ nCountSuccess[nErrorType]++;
+ }
+
+ } //End of if we get a browser list from the backup browser.
+
+ if (lpBackupBrowserList != NULL)
+ {
+ MIDL_user_free (lpBackupBrowserList);
+ }
+
+ } // End of if when we get a backup browser.
+
+ } // End of for statement.
+
+ MIDL_user_free (lpBrowserList);
+}
+
+// Check for error type 3: Incorrect number of browser servers.
+void CheckErrorType3 (LPTSTR lpUser,
+ LPTSTR lpTransportName,
+ LPTSTR lpDomainName,
+ LPTSTR lpMasterName)
+{
+ INT i;
+ INT nNTMachine = 0;
+ DWORD nBackup = 0;
+ INT nServer;
+ DWORD dwVal;
+ DWORD dwEntriesRead;
+ DWORD dwTotalEntries;
+// TCHAR lpErrorMessage[STRINGLEN];
+ LPVOID lpServerList;
+ PSERVER_INFO_101 pServerInfo101;
+
+ // Get the number of servers.
+ dwVal = MyRxNetServerEnum (lpMasterName,
+ lpTransportName,
+ 101,
+ (LPBYTE *) &lpServerList,
+ 0xffffffff,
+ &dwEntriesRead,
+ &dwTotalEntries,
+ SV_TYPE_LOCAL_LIST_ONLY,
+ lpDomainName,
+ NULL);
+
+ if (dwVal != NERR_Success) {
+ if (lpServerList != NULL) {
+ MIDL_user_free (lpServerList);
+ }
+
+ ReportError (lpUser,
+ lpTransportName,
+ lpDomainName,
+ lpMasterName,
+ lpMasterName,
+ dwVal,
+ MASTER_NO_LOCAL_LIST); //Error type number.
+ return;
+ } else { // Got the server list.
+ DWORD nExpected;
+ nCountSuccess[MASTER_NO_LOCAL_LIST]++;
+
+ nServer = dwEntriesRead;
+
+ pServerInfo101 = lpServerList;
+
+ for (i = 0; i < (INT) dwEntriesRead; i++ ) {
+ if (pServerInfo101[i].sv101_type & SV_TYPE_BACKUP_BROWSER) {
+ nBackup++;
+ }
+
+ if ( (pServerInfo101[i].sv101_type & SV_TYPE_BACKUP_BROWSER) &&
+ !(pServerInfo101[i].sv101_type & SV_TYPE_POTENTIAL_BROWSER)) {
+
+ nNTMachine++;
+ }
+ }
+
+ //Check for error type 3: Incorrect number of browser server.
+
+ if (nNTMachine > 1) {
+ nExpected = nNTMachine;
+ } else {
+ nExpected = (nServer+31)/32;
+ }
+
+ if ((nNTMachine > 1 && (nBackup != nExpected)) ||
+ ((nNTMachine <= 1) && (nBackup < nExpected))) {
+
+ INT nMachine[4] = {nNTMachine, nBackup, nServer, nExpected};
+
+ ReportError(lpUser,
+ lpTransportName,
+ lpDomainName,
+ NULL,
+ NULL,
+ (DWORD) nMachine,
+ WRONG_NUM_BACKUP);
+ } else { // No type 3 error. Write info to the logfile.
+ nCountSuccess[WRONG_NUM_BACKUP]++;
+ }
+
+ MIDL_user_free (lpServerList);
+ }
+}
+
+// Check for error type 4: Master is not a PDC.
+void CheckErrorType4 (LPTSTR lpUser,
+ LPTSTR lpTransportName,
+ LPTSTR lpDomainName,
+ LPTSTR lpMasterName)
+{
+ LPTSTR lpPDCName = NULL;
+ DWORD dwVal;
+
+ dwVal = NetGetDCName (NULL,
+ lpDomainName,
+ (LPBYTE *)&lpPDCName);
+ if (dwVal != NERR_Success)
+ {
+ ReportError (lpUser,
+ lpTransportName,
+ lpDomainName,
+ lpMasterName,
+ lpMasterName,
+ dwVal,
+ NO_PDC);
+ }
+ else
+ {
+ nCountSuccess[NO_PDC]++;
+
+ if (lstrcmp (lpMasterName, lpPDCName))
+ {
+ ReportError (lpUser,
+ lpTransportName,
+ lpDomainName,
+ lpPDCName,
+ lpMasterName,
+ 0,
+ MASTER_NOT_PDC);
+ }
+ else
+ {
+ nCountSuccess[MASTER_NOT_PDC]++;
+ }
+ }
+
+ if (lpPDCName != NULL)
+ {
+ LocalFree(lpPDCName);
+ }
+}
+
+void CheckErrorType5 (LPTSTR lpBrowserName,
+ LPTSTR lpTransportName,
+ LPTSTR lpDomainName,
+ LPTSTR lpUser)
+{
+ PLMDR_TRANSPORT_LIST TransportList = NULL;
+ PLMDR_TRANSPORT_LIST TransportEntry = NULL;
+ NET_API_STATUS Status;
+
+ Status = GetBrowserTransportList(&TransportList);
+ if (Status != NERR_Success)
+ {
+ return;
+ }
+
+ TransportEntry = TransportList;
+
+ while (TransportEntry != NULL)
+ {
+ UNICODE_STRING TransportName;
+ LPBYTE lpBrowserList;
+ ULONG dwEntriesRead, dwTotalEntries;
+
+ TransportName.Buffer = TransportEntry->TransportName;
+ TransportName.Length = (USHORT) TransportEntry->TransportNameLength;
+ TransportName.MaximumLength = (USHORT) TransportEntry->TransportNameLength;
+
+ Status = RxNetServerEnum(lpBrowserName,
+ TransportName.Buffer,
+ 101,
+ &lpBrowserList,
+ 0xffffffff,
+ &dwEntriesRead,
+ &dwTotalEntries,
+ SV_TYPE_ALL,
+ NULL,
+ NULL);
+
+ if (lpBrowserList != NULL)
+ {
+ NetApiBufferFree(lpBrowserList);
+ }
+
+ if (Status == NERR_Success)
+ {
+ break;
+ }
+
+ if (TransportEntry->NextEntryOffset == 0)
+ TransportEntry = NULL;
+ else
+ {
+ TransportEntry = (PLMDR_TRANSPORT_LIST) ((PCHAR) TransportEntry
+ +TransportEntry->NextEntryOffset);
+ }
+ }
+
+ NetApiBufferFree(TransportList);
+
+ if (Status != NERR_Success)
+ {
+ static BOOL fError = FALSE;
+ TCHAR lpErrorMessage[STRINGLEN];
+
+ nCountFail[TRANSPORT_FAILURE]++;
+ wsprintf (lpErrorMessage, L"Browser %ws failed on all transports.\n", lpBrowserName);
+
+ RecordError (&fError,
+ lpBrowserName,
+ lpTransportName,
+ lpDomainName,
+ lpUser,
+ lpErrorMessage,
+ 0,
+ 0,
+ TRANSPORT_FAILURE);
+ }
+ else
+ nCountSuccess[TRANSPORT_FAILURE]++;
+}
+
+LPTSTR ServerInfo (LPTSTR lpServer)
+{
+ PSERVER_INFO_101 psvInfo = NULL;
+ static TCHAR lpTemp[STRINGLEN/2];
+
+ WCHAR ShareName[STRINGLEN/2];
+
+ wcscpy(ShareName, lpServer);
+ wcscat(ShareName, L"\\Ipc$");
+
+ NetUseDel(NULL, ShareName, USE_LOTS_OF_FORCE);
+
+ if (NetServerGetInfo (lpServer,
+ 101,
+ (LPBYTE *)&psvInfo) == NERR_Success)
+ {
+ wsprintf (lpTemp,
+ L"\\\\%ws: version=%ld.%ld type=",
+ psvInfo->sv101_name,
+ psvInfo->sv101_version_major,
+ psvInfo->sv101_version_minor);
+
+ if (psvInfo->sv101_type&SV_TYPE_WORKSTATION)
+ {
+ lstrcat (lpTemp, L"WORKSTATION");
+ }
+
+ if (psvInfo->sv101_type&SV_TYPE_SERVER)
+ {
+ lstrcat (lpTemp, L" | SERVER");
+ }
+
+ if (psvInfo->sv101_type&SV_TYPE_SQLSERVER)
+ {
+ lstrcat (lpTemp, L" | SQLSERVER");
+ }
+
+ if (psvInfo->sv101_type&SV_TYPE_DOMAIN_CTRL)
+ {
+ lstrcat (lpTemp, L" | DOMAIN_CTRL");
+ }
+
+ if (psvInfo->sv101_type&SV_TYPE_DOMAIN_BAKCTRL)
+ {
+ lstrcat (lpTemp, L" | DOMAIN_BAKCTRL");
+ }
+
+ if (psvInfo->sv101_type&SV_TYPE_TIME_SOURCE)
+ {
+ lstrcat (lpTemp, L" | TIME_SOURCE");
+ }
+
+ if (psvInfo->sv101_type&SV_TYPE_AFP)
+ {
+ lstrcat (lpTemp, L" | AFP");
+ }
+
+ if (psvInfo->sv101_type&SV_TYPE_NOVELL)
+ {
+ lstrcat (lpTemp, L" | NOVELL");
+ }
+
+ if (psvInfo->sv101_type&SV_TYPE_DOMAIN_MEMBER)
+ {
+ lstrcat (lpTemp, L" | DOMAIN_MEMEBER");
+ }
+
+ if (psvInfo->sv101_type&SV_TYPE_PRINTQ_SERVER)
+ {
+ lstrcat (lpTemp, L" | PRINTQ_SERVER");
+ }
+
+ if (psvInfo->sv101_type&SV_TYPE_DIALIN_SERVER)
+ {
+ lstrcat (lpTemp, L" | DIALIN_SERVER");
+ }
+
+ if (psvInfo->sv101_type&SV_TYPE_XENIX_SERVER)
+ {
+ lstrcat (lpTemp, L" | XENIX_SERVER");
+ }
+
+ if (psvInfo->sv101_type&SV_TYPE_NT)
+ {
+ lstrcat (lpTemp, L" | NT");
+ }
+
+ if (psvInfo->sv101_type&SV_TYPE_WFW)
+ {
+ lstrcat (lpTemp, L" | WFW");
+ }
+
+ if (psvInfo->sv101_type&SV_TYPE_POTENTIAL_BROWSER)
+ {
+ lstrcat (lpTemp, L" | POTNETIAL_BROWSER");
+ }
+
+ if (psvInfo->sv101_type&SV_TYPE_BACKUP_BROWSER)
+ {
+ lstrcat (lpTemp, L" | BACKUP_BROWSER");
+ }
+
+ if (psvInfo->sv101_type&SV_TYPE_MASTER_BROWSER)
+ {
+ lstrcat (lpTemp, L" | MASTER_BROWSER");
+ }
+
+ if (psvInfo->sv101_type&SV_TYPE_DOMAIN_MASTER)
+ {
+ lstrcat (lpTemp, L" | DOMAIN_MASTER");
+ }
+
+ lstrcat (lpTemp, L"\n");
+ MIDL_user_free (psvInfo);
+
+ return(lpTemp);
+ }
+ else
+ return(L"\0");
+}
+
+BOOL
+ConvertFile(
+ DWORD CtrlType
+ )
+{
+ WriteSummory();
+ MyFreeList();
+ fclose (pLOGFILE);
+
+ CtrlType;
+
+ return FALSE;
+}
+
+void WriteSummory()
+{
+ FILE * pFile;
+ INT i;
+ LPSTR lpText[ERRORTYPENUM];
+
+ lpText[0] = szText0;
+ lpText[1] = szText1;
+ lpText[2] = szText2;
+ lpText[3] = szText3;
+ lpText[4] = szText4;
+ lpText[5] = szText5;
+ lpText[6] = szText6;
+ lpText[7] = szText7;
+ lpText[8] = szText8;
+ lpText[9] = szText9;
+ lpText[10] = szText10;
+ lpText[11] = szText11;
+ lpText[12] = szText12;
+ lpText[13] = szText13;
+ lpText[14] = szText14;
+ lpText[15] = szText15;
+ lpText[16] = szText16;
+ lpText[17] = szText17;
+ lpText[18] = szText18;
+
+ pFile = fopen (szLOGFILE, "w+");
+
+ if (pFile == NULL) {
+ fprintf (stderr, "The browser checker was unable to open the log file.\n");
+ return;
+ }
+
+ fprintf (pFile, "The browser check has run %d times.\n", nTimes);
+
+ for (i=0 ; i < ERRORTYPENUM ; i++)
+ {
+ fprintf (pFile,
+ "%d : %d -- %s\n",
+ nCountFail[i],
+ nCountSuccess[i],
+ lpText[i]);
+
+ PrintEntries(pFile, i, pErrorList[i]);
+
+ fprintf (pFile, "\n");
+ }
+
+ fclose(pFile);
+}
+
+void PrintEntries (FILE * pFile,
+ INT nErrorType,
+ PERRORLIST pErrorList)
+{
+ PERRORLIST pErrorEntry;
+
+ pErrorEntry = pErrorList;
+
+ while (pErrorEntry != NULL)
+ {
+ if ((nErrorType == STALE_SERVER) ||
+ (nErrorType == STALE_DOMAIN))
+ {
+ PENTRYLIST pEntryList;
+ pEntryList = (PENTRYLIST) pErrorEntry->nVal1;
+
+ fprintf (pFile,
+ toansi(L" %ws %ws %ws occurred %d times.\n"),
+ pErrorEntry->lpTransport,
+ pErrorEntry->lpDomain,
+ pErrorEntry->lpServer,
+ pErrorEntry->nCount);
+
+ while (pEntryList != NULL)
+ {
+ fprintf (pFile,
+ " BackupEntries = %lu, MasterEntries = %lu, time = %d:%d\n",
+ pEntryList->dwBackupEntry,
+ pEntryList->dwMasterEntry,
+ pEntryList->wHour,
+ pEntryList->wMinute);
+
+ pEntryList = pEntryList->pNextEntry;
+ }
+ }
+ else
+ {
+ if (pErrorEntry->lpServer != NULL) {
+ fprintf (pFile,
+ toansi(L" %ws %ws %ws occurred %d times. Last time at %d:%d\n"),
+ pErrorEntry->lpTransport,
+ pErrorEntry->lpDomain,
+ pErrorEntry->lpServer,
+ pErrorEntry->nCount,
+ pErrorEntry->wHour,
+ pErrorEntry->wMinute);
+ } else {
+ fprintf (pFile,
+ toansi(L" %ws %ws occurred %d times. Last time at %d:%d\n"),
+ pErrorEntry->lpTransport,
+ pErrorEntry->lpDomain,
+ pErrorEntry->nCount,
+ pErrorEntry->wHour,
+ pErrorEntry->wMinute);
+ }
+
+ if (nErrorType == WRONG_NUM_MASTER) {
+ fprintf (pFile,
+ " %d masters returned.\n",
+ pErrorEntry->nVal1);
+ } else if (nErrorType == BROWSE_TOO_LONG) {
+ fprintf (pFile,
+ " Request took %d milliseconds.\n",
+ pErrorEntry->nVal1);
+ } else if (nErrorType == WRONG_NUM_BACKUP) {
+ fprintf (pFile,
+ " nNTMachine = %d nBackupBrowser = %d nServer = %d, nExpected %d \n",
+ pErrorEntry->nVal1,
+ pErrorEntry->nVal2,
+ pErrorEntry->nVal3,
+ pErrorEntry->nVal4);
+
+ }
+ else
+ {
+ if (pErrorEntry->nVal1 != 0)
+ {
+ fprintf (pFile,
+ " Error %d occurred : %s",
+ pErrorEntry->nVal1,
+ toansi (GetError (pErrorEntry->nVal1)));
+
+ if ((pErrorEntry->nVal1 != 0) &&
+ ((nErrorType == MASTER_NO_SERVER_LIST) ||
+ (nErrorType == MASTER_NO_DOMAIN_LIST) ||
+ (nErrorType == BROWSER_NO_SERVER_LIST) ||
+ (nErrorType == BROWSER_NO_DOMAIN_LIST) ||
+ (nErrorType == MASTER_NO_LOCAL_LIST)))
+ {
+ fprintf (pFile,
+ " NetServerGetInfo returned %d : %s",
+ pErrorEntry->nVal2,
+ toansi (GetError (pErrorEntry->nVal2)));
+ }
+ }
+ }
+ }
+
+ pErrorEntry = pErrorEntry->pNextEntry;
+ }
+}
+
+void MyFreeList ()
+{
+ INT i;
+ PERRORLIST pThisEntry;
+ PERRORLIST pErrorEntry;
+
+ for (i=0 ; i < ERRORTYPENUM ; i++)
+ {
+ pErrorEntry = pErrorList[i];
+
+ while (pErrorEntry != NULL)
+ {
+ pThisEntry = pErrorEntry;
+ pErrorEntry = pThisEntry->pNextEntry;
+
+ if (pThisEntry->lpServer != NULL)
+ {
+ LocalFree(pThisEntry->lpServer);
+ }
+ LocalFree (pThisEntry->lpTransport);
+ LocalFree (pThisEntry->lpDomain);
+
+ if ((i == STALE_SERVER) ||
+ (i == STALE_DOMAIN))
+ {
+ PENTRYLIST pEntryList;
+ PENTRYLIST pFreeEntry;
+
+ pEntryList = (PENTRYLIST)pThisEntry->nVal1;
+
+ while (pEntryList != NULL)
+ {
+ pFreeEntry = pEntryList;
+ pEntryList = pFreeEntry->pNextEntry;
+
+ LocalFree (pFreeEntry);
+ }
+ }
+
+ LocalFree (pThisEntry);
+ }
+ }
+}
+
diff --git a/private/net/svcdlls/browser2/bchk/bchk.h b/private/net/svcdlls/browser2/bchk/bchk.h
new file mode 100644
index 000000000..813556cff
--- /dev/null
+++ b/private/net/svcdlls/browser2/bchk/bchk.h
@@ -0,0 +1,305 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ bchk.h
+
+Abstract:
+
+ Pivate header file for bchk.c and support.c
+
+Author:
+
+ Congpa You (CongpaY) 10-Feb-1993
+
+Revision History:
+
+--*/
+
+#define INTLEN 20
+#define STRINGLEN 1024
+
+#define szBchk L"BCHK ASSERT"
+
+#define szAPPNAME L"bchk"
+#define szFILENAME L"bchk.ini"
+#define szTOLERANCE L"Tolerance"
+#define szTIMELIMIT L"BrowseTimeLimit"
+#define szSLEEPTIME L"SleepTime"
+#define szFILESIZE L"FileSizeLimit"
+#define szOTHERUSERS L"OtherUsers"
+#define szDOMAINS L"Domains"
+
+#define szDefaultTolerance L"5"
+#define szDefaultTimeLimit L"10000" // in milliseconds (10 seconds).
+#define szDefaultSleepTime L"900000" // in millisecond.
+#define szDefaultFileSize L"1000000" // in millisecond.
+#define szDefaultOtherUser NULL
+#define szDefaultDomain L"ntlan ntwins"
+
+#define szBCHKLOG "bchklog"
+#define szLBCHKLOG L"bchklog"
+#define szLBACKUP L"backup"
+#define szLOGFILE "logfile"
+#define szALERTLOG "alertlog"
+
+#define szSeps L" ,\t\n"
+
+#define NO_MASTER_RUNNING 0
+#define INVALID_MASTER 1
+#define NO_BACKUP 2
+#define NO_MASTER_NAME 3
+#define WRONG_NUM_MASTER 4
+#define FAIL_RETURN_MASTER 5
+#define UNKNOWN_MASTER 6
+#define MASTER_NO_SERVER_LIST 7
+#define BROWSER_NO_SERVER_LIST 8
+#define MASTER_NO_DOMAIN_LIST 9
+#define BROWSER_NO_DOMAIN_LIST 10
+#define STALE_SERVER 11
+#define STALE_DOMAIN 12
+#define MASTER_NO_LOCAL_LIST 13
+#define WRONG_NUM_BACKUP 14
+#define NO_PDC 15
+#define MASTER_NOT_PDC 16
+#define TRANSPORT_FAILURE 17
+#define BROWSE_TOO_LONG 18
+
+#define ERRORTYPENUM 19
+
+#define szText0 "No browser master is running. (GetBrowserServerList failed)"
+#define szText1 "GetNetBiosMasterName succeeded, but GetBrowserServerList failed."
+#define szText2 "No backup browsers."
+#define szText3 "GetNetBiosMasterName failed, but GetBrowserServerList succeeded"
+#define szText4 "Incorrect number of browser masters were returned."
+#define szText5 "Browser failed to return its master."
+#define szText6 "Cannot determine the master browser's name."
+#define szText7 "Master browser failed to return the server list."
+#define szText8 "Backup browser failed to return the server list."
+#define szText9 "Master browser failed to return the domain list."
+#define szText10 "Backup browser failed to return the domain list."
+#define szText11 "Browser returns an incorrect number of servers."
+#define szText12 "Browser returns an incorrect number of domains."
+#define szText13 "Master browser failed to return the local list."
+#define szText14 "Incorrect number of browser servers."
+#define szText15 "No PDC on the domain (NetGetDCName failed)."
+#define szText16 "Master browser is not the PDC."
+#define szText17 "Browser failed on all transports while NetServerGetInfo succeeded."
+#define szText18 "Browse request took too long."
+
+#define strncpyf wcsncpy
+#define strlenf wcslen
+#define strtokf wcstok
+
+extern FILE * pLOGFILE;
+
+typedef struct _INITPARAM
+{
+ INT nTolerance;
+ INT nSleepTime;
+ INT nFileSizeLimit;
+ INT nTimeLimit;
+ LPTSTR lpUser;
+ LPTSTR lpDomain;
+} INITPARAM;
+
+typedef struct _ERRORLIST
+{
+ LPTSTR lpServer;
+ LPTSTR lpTransport;
+ LPTSTR lpDomain;
+ INT nCount;
+ ULONG nVal1;
+ ULONG nVal2;
+ INT nVal3;
+ INT nVal4;
+ WORD wHour;
+ WORD wMinute;
+ struct _ERRORLIST * pNextEntry;
+} ERRORLIST, * PERRORLIST;
+
+typedef struct _ENTRYLIST
+{
+ DWORD dwBackupEntry;
+ DWORD dwMasterEntry;
+ WORD wHour;
+ WORD wMinute;
+ struct _ENTRYLIST * pNextEntry;
+} ENTRYLIST, * PENTRYLIST;
+
+BOOL Init (INITPARAM * pInitParam);
+
+void InitAllCount ();
+
+void InitCount ();
+
+void CountTotal();
+
+NET_API_STATUS
+MyRxNetServerEnum (
+ IN LPTSTR UncServerName,
+ IN LPTSTR TransportName,
+ IN DWORD Level,
+ OUT LPBYTE *BufPtr,
+ IN DWORD PrefMaxSize,
+ OUT LPDWORD EntriesRead,
+ OUT LPDWORD TotalEntries,
+ IN DWORD ServerType,
+ IN LPTSTR Domain OPTIONAL,
+ IN OUT LPDWORD Resume_Handle OPTIONAL
+ );
+
+NET_API_STATUS GetMasterName (LPTSTR lpMasterName,
+ LPTSTR lpTransportName,
+ LPTSTR lpDomainName);
+
+BOOL RxGetMasterName (LPTSTR lpUser,
+ LPTSTR lpMasterName,
+ LPTSTR lpTransportName,
+ LPTSTR lpDomainName,
+ PWSTR * BrowserList,
+ DWORD BrowserListLength,
+ DWORD nTimeLimit);
+
+BOOL MyGetList (LPTSTR lpUser,
+ LPTSTR lpBrowserName,
+ LPTSTR lpTransportName,
+ LPTSTR lpDomainName,
+ LPTSTR lpMasterName,
+ DWORD * pdwEntriesRead,
+ DWORD * pdwTotalEntries,
+ LPVOID * lppBrowserList,
+ BOOL fBrowserNotDomain,
+ DWORD nTimeLimit);
+
+void ReportError (LPTSTR lpUser,
+ LPTSTR lpTransportName,
+ LPTSTR lpDomainName,
+ LPTSTR lpMasterName,
+ LPTSTR lpBrowserName,
+ DWORD dwError,
+ INT nErrorType);
+
+void CheckErrorType2 (LPTSTR lpUser,
+ LPTSTR lpTransportName,
+ LPTSTR lpDomainName,
+ LPTSTR lpMasterName,
+ PWSTR * lpList,
+ DWORD dwEntries,
+ INT nTolerancePercent,
+ DWORD nTimeLimit);
+
+void CheckList (LPTSTR lpUser,
+ LPTSTR lpTransportName,
+ LPTSTR lpDomainName,
+ LPTSTR lpMasterName,
+ PWSTR * lpList,
+ DWORD dwEntries,
+ INT nTolerancePercent,
+ BOOL fBrowserNotDomain,
+ DWORD nTimeLimit);
+
+void CheckErrorType3 (LPTSTR lpUser,
+ LPTSTR lpTransportName,
+ LPTSTR lpDomainName,
+ LPTSTR lpMasterName);
+
+void CheckErrorType4 (LPTSTR lpUser,
+ LPTSTR lpTransportName,
+ LPTSTR lpDomainName,
+ LPTSTR lpMasterName);
+
+void CheckErrorType5 (LPTSTR lpBrowserName,
+ LPTSTR lpTransportName,
+ LPTSTR lpDomainName,
+ LPTSTR lpUser);
+
+void NotifyUser (LPTSTR lpUser,
+ LPTSTR lpErrorMessage);
+
+LPTSTR GetError (DWORD dwError);
+
+void MyMessageBox (DWORD dwError);
+
+NET_API_STATUS GetBrowserTransportList(
+ OUT PLMDR_TRANSPORT_LIST *TransportList);
+
+LPSTR toansi (LPTSTR lpUnicode);
+
+BOOL CompareList (LPVOID lpBackupList,
+ LPVOID lpBrowserList,
+ DWORD dwBackup,
+ DWORD dwBrowser,
+ INT nTolerance);
+
+void KillSpace (LPTSTR lpTemp);
+
+LPTSTR ServerInfo(LPTSTR lpServer);
+
+BOOL
+ConvertFile(DWORD Event);
+
+void WriteSummory();
+
+void PrintEntries (FILE * pFile,
+ INT nErrorType,
+ PERRORLIST pErrorList);
+
+PERRORLIST NewEntry (LPTSTR lpServer,
+ LPTSTR lpTransport,
+ LPTSTR lpDomain,
+ DWORD dwError,
+ DWORD dwNSGI,
+ WORD wHour,
+ WORD wMinute,
+ INT nErrorType);
+
+void AddStaleEntry (PERRORLIST pErrorList,
+ DWORD dwBackupEntry,
+ DWORD dwMasterEntry,
+ WORD wHour,
+ WORD wMinute);
+void MyFreeList();
+
+void PrintHeader (FILE * pFile);
+
+void PrintSeparation (FILE * pFile);
+
+void LogError (FILE * pFile,
+ LPTSTR lpErrorMessage);
+
+void LogWarning (FILE * pFile,
+ LPTSTR lpErrorMessage);
+
+void LogMaster (FILE * pFile,
+ LPTSTR lpTransportName,
+ LPTSTR lpDomainName,
+ LPTSTR lpMasterName);
+
+void WriteList (FILE * pFile,
+ LPVOID lpBrowserList,
+ DWORD dwEntriesRead);
+
+void WriteBrowserList (FILE * pFile,
+ PWSTR * BrowserList,
+ ULONG BrowserListLength);
+
+BOOL IsOurMachine (LPTSTR lpServer);
+
+void RecordError (BOOL * pfError,
+ LPTSTR lpBrowserName,
+ LPTSTR lpTransportName,
+ LPTSTR lpDomainName,
+ LPTSTR lpUser,
+ LPTSTR lpErrorMessage,
+ DWORD dwError,
+ DWORD dwNSGI,
+ INT nErrorType);
+
+VOID
+SendMagicBullet(
+ VOID
+ );
+
diff --git a/private/net/svcdlls/browser2/bchk/makefile b/private/net/svcdlls/browser2/bchk/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/net/svcdlls/browser2/bchk/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/net/svcdlls/browser2/bchk/sources b/private/net/svcdlls/browser2/bchk/sources
new file mode 100644
index 000000000..20fa74ab5
--- /dev/null
+++ b/private/net/svcdlls/browser2/bchk/sources
@@ -0,0 +1,53 @@
+!IF 0
+
+Copyright (c) 1989-1992 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+
+Author:
+
+ Steve Wood (stevewo) 12-Apr-1990
+ Congpa You (congpay) 04-Feb-1993
+
+NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl
+
+!ENDIF
+
+MAJORCOMP=windows
+MINORCOMP=bchk
+
+TARGETNAME=bchk
+TARGETPATH=obj
+TARGETTYPE=LIBRARY
+
+INCLUDES=..\..\..;..\..\..\inc;..\..\..\..\inc;..;
+
+
+!IFNDEF DISABLE_NET_UNICODE
+UNICODE=1
+NET_C_DEFINES=-DUNICODE
+!ENDIF
+
+SOURCES=support.c
+
+UMTYPE=console
+UMAPPL=bchk
+UMLIBS=obj\*\bchk.lib \
+ \nt\public\sdk\lib\*\netapi32.lib \
+ ..\common\obj\*\brcommon.lib \
+ \nt\public\sdk\lib\*\rpcutil.lib \
+ \nt\public\sdk\lib\*\ntdll.lib \
+ \nt\public\sdk\lib\*\user32.lib
+
+
+
diff --git a/private/net/svcdlls/browser2/bchk/support.c b/private/net/svcdlls/browser2/bchk/support.c
new file mode 100644
index 000000000..ce7616c56
--- /dev/null
+++ b/private/net/svcdlls/browser2/bchk/support.c
@@ -0,0 +1,447 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ support.c
+
+Abstract:
+
+ routes used by bchk.c
+
+Author:
+
+ Congpa You (CongpaY) 10-Feb-1993
+
+Revision History
+
+--*/
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windows.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <lm.h>
+#include <ntddbrow.h> // for LMDR_TRANSPORT_LIST.
+#include <brcommon.h>
+#include <rap.h>
+#include <rxserver.h> // for MyRxNetServerEnum. In net\inc.
+#include "bchk.h"
+
+// Convert the error number to the corresponding error string.
+LPTSTR GetError (DWORD dwError)
+{
+ DWORD dwFlag = FORMAT_MESSAGE_FROM_SYSTEM;
+ static TCHAR szErrorMessage[STRINGLEN];
+ static HANDLE hSource = NULL;
+
+ if ((dwError >= 2100) && (dwError < 6000))
+ {
+ if (hSource == NULL)
+ {
+ hSource = LoadLibrary(L"netmsg.dll");
+ }
+
+ if (hSource == NULL)
+ {
+ wsprintf (szErrorMessage,
+ L"Unable to load netmsg.dll. Error %d occured.\n",
+ dwError);
+ return(szErrorMessage);
+ }
+
+ dwFlag = FORMAT_MESSAGE_FROM_HMODULE;
+ }
+
+ if (!FormatMessage (dwFlag,
+ hSource,
+ dwError,
+ 0,
+ szErrorMessage,
+ STRINGLEN,
+ NULL))
+ {
+ wsprintf (szErrorMessage,
+ L"An unknown error occured: %d \n",
+ dwError);
+ }
+ return(szErrorMessage);
+}
+
+// Local function. Used for reporting an API error.
+void MyMessageBox (DWORD dwError)
+{
+ MessageBox (NULL, GetError(dwError), szBchk, MB_OK|MB_ICONSTOP);
+ return;
+}
+
+// Copied from ..\client\browstub.c.
+NET_API_STATUS GetBrowserTransportList (OUT PLMDR_TRANSPORT_LIST *TransportList)
+{
+
+ NET_API_STATUS Status;
+ HANDLE BrowserHandle;
+ LMDR_REQUEST_PACKET RequestPacket;
+
+ Status = OpenBrowser(&BrowserHandle);
+
+ if (Status != NERR_Success) {
+ return Status;
+ }
+
+ RequestPacket.Version = LMDR_REQUEST_PACKET_VERSION;
+
+ RequestPacket.Type = EnumerateXports;
+
+ RtlInitUnicodeString(&RequestPacket.TransportName, NULL);
+ RtlInitUnicodeString(&RequestPacket.EmulatedDomainName, NULL);
+
+ Status = DeviceControlGetInfo(
+ BrowserHandle,
+ IOCTL_LMDR_ENUMERATE_TRANSPORTS,
+ &RequestPacket,
+ sizeof(RequestPacket),
+ (PVOID *)TransportList,
+ 0xffffffff,
+ 4096,
+ NULL);
+
+ NtClose(BrowserHandle);
+
+ return Status;
+}
+
+// Convert an unicode string to ansi string.
+LPSTR toansi(LPTSTR lpUnicode)
+{
+ static CHAR lpAnsi[STRINGLEN];
+ BOOL fDummy;
+ INT i;
+
+ i = WideCharToMultiByte (CP_ACP,
+ 0,
+ lpUnicode,
+ lstrlen(lpUnicode),
+ lpAnsi,
+ STRINGLEN,
+ NULL,
+ &fDummy);
+
+ lpAnsi[i] = 0;
+
+ return(lpAnsi);
+}
+
+BOOL CompareList (LPVOID lpBackupList,
+ LPVOID lpBrowserList,
+ DWORD dwBackup,
+ DWORD dwBrowser,
+ INT nTolerance)
+{
+ INT i;
+ INT j;
+ INT nSame = 0;
+ PSERVER_INFO_101 pBackupServerInfo101 = lpBackupList;
+ PSERVER_INFO_101 pBrowserServerInfo101 = lpBrowserList;
+
+ for (i = 0; i< (INT) dwBackup ; i++ )
+ {
+ for (j = 0; j < (INT) dwBrowser; j++ )
+ {
+ if (!lstrcmp (pBackupServerInfo101[i].sv101_name,
+ pBrowserServerInfo101[j].sv101_name))
+ {
+ nSame++;
+ break;
+ }
+ }
+ }
+
+ //
+ // If there are at least 10 browsers different between the two
+ // lists, and there is more than the tollerence different, report an
+ // error.
+ //
+
+ if (abs(dwBackup - nSame) > 10 &&
+ abs(dwBrowser - nSame) > 10 &&
+ ((nSame < (INT) dwBackup - nTolerance) ||
+ (nSame < (INT) dwBrowser - nTolerance)))
+ {
+ return(FALSE);
+ }
+ else
+ return(TRUE);
+}
+
+void KillSpace (LPTSTR lpTemp)
+{
+ INT i = 0;
+ while ( (*(lpTemp+i) != 0) &&
+ (*(lpTemp+i) != ' '))
+ {
+ i++;
+ }
+
+ *(lpTemp+i) = 0;
+}
+
+// Write the browser list got from RxNetServerEnum call to the logfile
+void WriteList (FILE * pFile,
+ LPVOID lpList,
+ DWORD dwEntries)
+{
+ INT i;
+ TCHAR lpTemp[UNLEN+1];
+ PSERVER_INFO_101 pServerInfo101;
+
+ if (dwEntries > 10)
+ {
+ fprintf (pFile, "\n");
+ return;
+ }
+
+
+ fprintf (pFile,
+ "The server list has %lu entries:\n",
+ dwEntries);
+
+ pServerInfo101 = lpList;
+
+ for (i = 0; i < (INT) dwEntries ; i++)
+ {
+ lstrcpy (lpTemp, L"\\\\");
+ lstrcat (lpTemp, pServerInfo101[i].sv101_name);
+ fprintf (pFile, "%s", toansi(lpTemp));
+ fprintf (pFile, "\n");
+ }
+
+ fprintf (pFile, "\n");
+}
+
+// Write the browser list get from GetBrowserServerList call to the logfile.
+void WriteBrowserList (FILE * pFile,
+ PWSTR * BrowserList,
+ ULONG BrowserListLength)
+{
+ INT i;
+
+ fprintf (pFile,
+ "The browser list returned from GetBrowserServerList has %lu entries:\n",
+ BrowserListLength);
+
+ for (i = 0; i < (INT) BrowserListLength ; i++)
+ {
+ fprintf (pFile, "%s", toansi(BrowserList[i]));
+ fprintf (pFile, "\n");
+ }
+
+ fprintf(pFile, "\n");
+}
+
+
+// Send lpErrorMessage to the users in lpUser.
+void NotifyUser (LPTSTR lpUser,
+ LPTSTR lpErrorMessage)
+{
+ INT i;
+ TCHAR lpUserName[UNLEN+1];
+ NET_API_STATUS Status;
+
+ if (lpUser == NULL)
+ {
+ return;
+ }
+ while (*lpUser != 0)
+ {
+ i=0;
+ while ((*(lpUser+i) != 0) &&
+ (*(lpUser+i) != ' '))
+ {
+ i++;
+ }
+
+ strncpyf (lpUserName, lpUser, i);
+ lpUserName[i] = 0;
+
+ lpUser += i;
+
+ // Get rid of the space.
+ while ((*lpUser != 0) &&
+ (*lpUser == ' '))
+ {
+ lpUser++;
+ }
+
+ Status = NetMessageBufferSend (NULL,
+ lpUserName,
+ NULL,
+ (LPBYTE) lpErrorMessage,
+ 2*strlenf(lpErrorMessage)+2);
+
+ if (Status != NERR_Success)
+ {
+ TCHAR lpNotifyError[STRINGLEN];
+ wsprintf (lpNotifyError,
+ L"!!! Unable to send the abover message to %s.\n!!! Error %lu occured: %s\n",
+ lpUserName,
+ Status,
+ GetError(Status));
+
+ fprintf (stdout, toansi(lpNotifyError));
+ }
+ }
+}
+
+// Print the start new run header in the logfile.
+void PrintHeader(FILE * pFile)
+{
+ SYSTEMTIME systime;
+
+ GetLocalTime (&systime);
+
+ // Declare a new setion in logfile.
+ fprintf(pFile, "***************************************\n");
+ fprintf(pFile, "*********** START NEW RUN ***********\n");
+
+ fprintf(pFile,
+ "*********** %d/%d/%d %d:%d ***********\n",
+ systime.wMonth,
+ systime.wDay,
+ systime.wYear,
+ systime.wHour,
+ systime.wMinute);
+
+ fprintf(pFile, "***************************************\n\n");
+}
+
+void PrintSeparation (FILE * pFile)
+{
+ fprintf (pFile,
+ "========================================================================\n\n");
+}
+
+// Write lpErrorMessage to the logfile.
+void LogError (FILE * pFile,
+ LPTSTR lpErrorMessage)
+{
+ fprintf(pFile, "________________________________FAILURE___________________________\n\n");
+ fprintf (pFile, toansi(lpErrorMessage));
+ fprintf(pFile, "__________________________________________________________________\n\n");
+}
+
+
+void LogWarning (FILE * pFile,
+ LPTSTR lpErrorMessage)
+{
+ fprintf(pFile, "________________________________WARNING___________________________\n\n");
+ fprintf (pFile, toansi(lpErrorMessage));
+ fprintf(pFile, "__________________________________________________________________\n\n");
+}
+
+// Write mastername and browser list to the logfile.
+void LogMaster (FILE * pFile,
+ LPTSTR lpTransportName,
+ LPTSTR lpDomainName,
+ LPTSTR lpMasterName)
+{
+ TCHAR lpTempString[STRINGLEN];
+
+ wsprintf (lpTempString,
+ L"The master browser on domain %ws, transport %ws, is %ws.\n\n",
+ lpDomainName,
+ lpTransportName,
+ lpMasterName);
+
+ fprintf (pFile, toansi(lpTempString));
+}
+
+NET_API_STATUS
+MyRxNetServerEnum (
+ IN LPTSTR UncServerName,
+ IN LPTSTR TransportName,
+ IN DWORD Level,
+ OUT LPBYTE *BufPtr,
+ IN DWORD PrefMaxSize,
+ OUT LPDWORD EntriesRead,
+ OUT LPDWORD TotalEntries,
+ IN DWORD ServerType,
+ IN LPTSTR Domain OPTIONAL,
+ IN OUT LPDWORD Resume_Handle OPTIONAL
+ )
+{
+ DWORD dwStartTime;
+ DWORD dwEndTime;
+ NET_API_STATUS dwStatus;
+
+ dwStartTime = GetTickCount();
+
+ dwStatus = RxNetServerEnum ( UncServerName,
+ TransportName,
+ Level,
+ BufPtr,
+ PrefMaxSize,
+ EntriesRead,
+ TotalEntries,
+ ServerType,
+ Domain,
+ Resume_Handle );
+
+ dwEndTime = GetTickCount();
+
+ if (dwStatus != NERR_Success)
+ {
+ fprintf (pLOGFILE, "RxNetServerEnum failed after %d milliseconds on %s.\n",
+ dwEndTime-dwStartTime,
+ toansi (UncServerName));
+ }
+
+ return(dwStatus);
+}
+
+BOOL IsOurMachine (LPTSTR lpServer)
+{
+ return(!lstrcmp (lpServer, L"CRACKERJACK")||
+ !lstrcmp (lpServer, L"BONKERS")||
+ !lstrcmp (lpServer, L"ORVILLE")||
+ !lstrcmp (lpServer, L"RASTAMAN")||
+ !lstrcmp (lpServer, L"KERNEL")||
+ !lstrcmp (lpServer, L"PHOENIX")||
+ !lstrcmp (lpServer, L"NTABROAD")||
+ !lstrcmp (lpServer, L"RAIDERNT")||
+ !lstrcmp (lpServer, L"CLIFFV4"));
+}
+
+
+VOID
+SendMagicBullet(
+ VOID
+ )
+{
+ HANDLE MsHandle;
+ DWORD BytesWritten;
+
+ MsHandle = CreateFile(L"\\\\MagicBullet\\Mailslot\\Foobar",
+ GENERIC_WRITE,
+ 0,
+ NULL,
+ OPEN_ALWAYS,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL
+ );
+
+ if (MsHandle == INVALID_HANDLE_VALUE) {
+ printf("Unable to open mailslot: %ld\n", GetLastError());
+ return;
+ }
+
+ if (!WriteFile(MsHandle, &MsHandle, sizeof(MsHandle), &BytesWritten, NULL)) {
+ printf("Unable to write to mailslot: %ld\n", GetLastError());
+ }
+
+ CloseHandle(MsHandle);
+}
diff --git a/private/net/svcdlls/browser2/bowser.acf b/private/net/svcdlls/browser2/bowser.acf
new file mode 100644
index 000000000..4608dbf43
--- /dev/null
+++ b/private/net/svcdlls/browser2/bowser.acf
@@ -0,0 +1,11 @@
+[ implicit_handle( handle_t browser_bhandle )]
+
+interface browser
+
+{
+
+typedef [allocate(all_nodes)] LPSERVER_INFO_100;
+typedef [allocate(all_nodes)] LPSERVER_INFO_101;
+typedef [allocate(all_nodes)] PBROWSER_EMULATED_DOMAIN;
+
+}
diff --git a/private/net/svcdlls/browser2/bowser.idl b/private/net/svcdlls/browser2/bowser.idl
new file mode 100644
index 000000000..e917e7afa
--- /dev/null
+++ b/private/net/svcdlls/browser2/bowser.idl
@@ -0,0 +1,222 @@
+/*++
+
+Copyright (c) 1990 Microsoft Corporation
+
+Module Name:
+
+ bowser.idl
+
+Abstract:
+
+ Contains the Netr (Net Remote) RPC interface specification for the APIs
+ associated with the Browser service. This consists of the NetServerEnum
+ API
+
+ Also contains the RPC specific data structures for these API.
+
+Author:
+
+ Rita Wong (ritaw) 06-May-1991
+ Larry Osterman (larryo) 23-Mar-1992
+
+Environment:
+
+ User Mode - Win32 - MIDL
+
+Revision History:
+
+--*/
+
+//
+// Interface Attributes
+//
+
+[
+ uuid(6BFFD098-A112-3610-9833-012892020162),
+ version(0.0),
+#ifdef __midl
+ ms_union,
+#endif // __midl
+ pointer_default(unique)
+]
+
+
+//
+// Interface Keyword
+//
+
+interface browser
+
+
+//
+// Interface Body
+//
+
+{
+
+import "imports.idl";
+#include <lmcons.h>
+
+//
+// BUGBUG - take this definition out when midl understands LPWSTR etc
+//
+
+#ifdef UNICODE
+#define LPTSTR wchar_t*
+#endif
+
+//
+// ---------------------------------------------------------------//
+//
+
+
+typedef [handle] LPTSTR BROWSER_IMPERSONATE_HANDLE;
+
+typedef [handle] LPTSTR BROWSER_IDENTIFY_HANDLE;
+
+
+//
+// I_BrowserrServerEnum
+//
+
+typedef struct _SERVER_INFO_100_CONTAINER {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] LPSERVER_INFO_100 Buffer;
+} SERVER_INFO_100_CONTAINER, *PSERVER_INFO_100_CONTAINER,
+*LPSERVER_INFO_100_CONTAINER;
+
+
+typedef struct _SERVER_INFO_101_CONTAINER {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] LPSERVER_INFO_101 Buffer;
+} SERVER_INFO_101_CONTAINER, *PSERVER_INFO_101_CONTAINER,
+*LPSERVER_INFO_101_CONTAINER;
+
+typedef struct _BROWSER_STATISTICS_100_CONTAINER {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] PBROWSER_STATISTICS_100 Buffer;
+} BROWSER_STATISTICS_100_CONTAINER, *PBROWSER_STATISTICS_100_CONTAINER;
+
+typedef struct _BROWSER_STATISTICS_101_CONTAINER {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] PBROWSER_STATISTICS_101 Buffer;
+} BROWSER_STATISTICS_101_CONTAINER, *PBROWSER_STATISTICS_101_CONTAINER;
+
+typedef struct _BROWSER_EMULATED_DOMAIN_CONTAINER {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] PBROWSER_EMULATED_DOMAIN Buffer;
+} BROWSER_EMULATED_DOMAIN_CONTAINER, *PBROWSER_EMULATED_DOMAIN_CONTAINER;
+
+typedef struct _SERVER_ENUM_STRUCT {
+ DWORD Level;
+ [switch_is(Level)] union _SERVER_ENUM_UNION {
+ [case(100)]
+ LPSERVER_INFO_100_CONTAINER Level100;
+ [case(101)]
+ LPSERVER_INFO_101_CONTAINER Level101;
+ [default]
+ ;
+ } ServerInfo;
+}SERVER_ENUM_STRUCT, *PSERVER_ENUM_STRUCT, *LPSERVER_ENUM_STRUCT;
+
+typedef struct _BROWSER_STATISTICS_STRUCT {
+ DWORD Level;
+ [switch_is(Level)] union _BROWSER_STATISTICS_UNION {
+ [case(100)]
+ PBROWSER_STATISTICS_100_CONTAINER Level100;
+ [case(101)]
+ PBROWSER_STATISTICS_101_CONTAINER Level101;
+ [default]
+ ;
+ } Statistics;
+}BROWSER_STATISTICS_STRUCT, *PBROWSER_STATISTICS_STRUCT, *LPBROWSER_STATISTICS_STRUCT;
+
+NET_API_STATUS
+I_BrowserrServerEnum(
+ [in,string,unique] BROWSER_IDENTIFY_HANDLE ServerName,
+ [in,string,unique] LPTSTR TransportName,
+ [in,string,unique] LPTSTR ClientName,
+ [in,out] LPSERVER_ENUM_STRUCT InfoStruct,
+ [in] DWORD PreferedMaximumLength,
+ [out] LPDWORD TotalEntries,
+ [in] DWORD ServerType,
+ [in,string,unique] LPTSTR Domain,
+ [in,out,unique] LPDWORD ResumeHandle
+ );
+
+NET_API_STATUS
+I_BrowserrDebugCall(
+ [in,string,unique] BROWSER_IDENTIFY_HANDLE ServerName,
+ [in] DWORD DebugFunction,
+ [in] DWORD OptionalValue
+ );
+
+NET_API_STATUS
+I_BrowserrQueryOtherDomains(
+ [in,string,unique] BROWSER_IDENTIFY_HANDLE ServerName,
+ [in,out] LPSERVER_ENUM_STRUCT InfoStruct,
+ [out] LPDWORD TotalEntries
+ );
+
+NET_API_STATUS
+I_BrowserrResetNetlogonState(
+ [in,string,unique] BROWSER_IDENTIFY_HANDLE ServerName
+ );
+
+NET_API_STATUS
+I_BrowserrDebugTrace(
+ [in,string,unique] BROWSER_IDENTIFY_HANDLE ServerName,
+ [in, string] LPSTR TraceString
+ );
+
+NET_API_STATUS
+I_BrowserrQueryStatistics (
+ [in, string, unique] BROWSER_IDENTIFY_HANDLE servername OPTIONAL,
+ [out] LPBROWSER_STATISTICS *statistics
+ );
+
+NET_API_STATUS
+I_BrowserrResetStatistics (
+ [in, string, unique] BROWSER_IDENTIFY_HANDLE servername OPTIONAL
+ );
+
+NET_API_STATUS
+NetrBrowserStatisticsClear (
+ [in, string, unique] BROWSER_IDENTIFY_HANDLE servername OPTIONAL
+ );
+
+NET_API_STATUS
+NetrBrowserStatisticsGet (
+ [in, string, unique] BROWSER_IDENTIFY_HANDLE servername OPTIONAL,
+ [in] DWORD Level,
+ [in, out] LPBROWSER_STATISTICS_STRUCT StatisticsStruct
+ );
+
+NET_API_STATUS
+I_BrowserrSetNetlogonState(
+ [in, string, unique] BROWSER_IDENTIFY_HANDLE ServerName OPTIONAL,
+ [in, string] LPTSTR DomainName,
+ [in, string, unique] LPTSTR EmulatedComputerName OPTIONAL,
+ [in] DWORD Role
+ );
+
+NET_API_STATUS
+I_BrowserrQueryEmulatedDomains (
+ [in, string, unique] BROWSER_IDENTIFY_HANDLE ServerName OPTIONAL,
+ [in,out] PBROWSER_EMULATED_DOMAIN_CONTAINER EmulatedDomains
+ );
+
+NET_API_STATUS
+I_BrowserrServerEnumEx(
+ [in,string,unique] BROWSER_IDENTIFY_HANDLE ServerName,
+ [in,string,unique] LPTSTR TransportName,
+ [in,string,unique] LPTSTR ClientName,
+ [in,out] LPSERVER_ENUM_STRUCT InfoStruct,
+ [in] DWORD PreferedMaximumLength,
+ [out] LPDWORD TotalEntries,
+ [in] DWORD ServerType,
+ [in,string,unique] LPTSTR Domain,
+ [in,string,unique] LPTSTR FirstNameToReturn
+ );
+
+}
diff --git a/private/net/svcdlls/browser2/bperf/bperf.c b/private/net/svcdlls/browser2/bperf/bperf.c
new file mode 100644
index 000000000..6407de361
--- /dev/null
+++ b/private/net/svcdlls/browser2/bperf/bperf.c
@@ -0,0 +1,357 @@
+/*++
+
+Copyright (c) 1993 Micorsoft Corporation
+
+Module Name:
+
+ bchk.c
+
+Abstract:
+
+ Browser Monitor main program.
+
+Author:
+
+ Congpa You (CongpaY) 10-Feb-1993
+
+Revision History:
+
+--*/
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <sys\types.h>
+#include <sys\stat.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <windows.h>
+#include <lm.h>
+#include <ntddbrow.h>
+#include <brcommon.h> // svcdll\browser
+#include <rap.h>
+#include <rxserver.h>
+#include <winerror.h> // inc
+#include <rpcutil.h> // for MIDL_user_free. net\inc
+
+
+void PerfDomain ( INT i,
+ TCHAR szDomainName[],
+ DWORD dwServerType);
+
+void PerfTransport (INT i,
+ TCHAR szDomainName[],
+ DWORD dwServerType);
+
+void InitCount ();
+
+void CalcCount (DWORD dwVal,
+ DWORD dwEndTime);
+
+void PrintResult();
+
+NET_API_STATUS GetBrowserTransportList (OUT PLMDR_TRANSPORT_LIST *TransportList);
+
+LPSTR toansi(LPTSTR lpUnicode);
+
+// Global data
+INT nCount[16];
+
+void _CRTAPI1 main (INT argc, CHAR * argv[])
+{
+ INT i;
+ TCHAR szDomainName[DNLEN];
+ DWORD dwServerType;
+
+ // Get value from command line.
+ if (argc < 3)
+ {
+ printf("bperf -domain -n -m\n -n the number of times to run.\n -m server type, optional\n");
+ return;
+ }
+
+ i = MultiByteToWideChar (CP_ACP,
+ 0,
+ argv[1],
+ strlen(argv[1]),
+ szDomainName,
+ DNLEN);
+
+ szDomainName[i] = 0;
+
+ i = atoi(argv[2]);
+
+ if (argc == 4)
+ {
+ dwServerType = atoi(argv[3]);
+
+ if ((dwServerType < 1) || (dwServerType > 0xFFFFFFFF))
+ {
+ printf("wrong value for SV_TYPE.");
+ return;
+ }
+ }
+ else
+ dwServerType = SV_TYPE_ALL;
+
+ PerfDomain (i, szDomainName, dwServerType);
+
+ PerfTransport (i, szDomainName, dwServerType);
+}
+
+
+void PerfDomain ( INT i,
+ TCHAR szDomainName[],
+ DWORD dwServerType)
+{
+ DWORD dwVal; // The value returned from function calls.
+ DWORD dwStartTime;
+ DWORD dwEndTime;
+ DWORD dwEntriesRead;
+ DWORD dwTotalEntries;
+ LPVOID lpBrowserList;
+
+ InitCount ();
+
+ // run NetServerEnum on domain and RxNetServerEnum on transport.
+ while (i-- > 0)
+ {
+ dwStartTime = GetTickCount();
+
+ dwVal = NetServerEnum (NULL,
+ 101,
+ (LPBYTE *) &lpBrowserList,
+ 0xffffffff,
+ &dwEntriesRead,
+ &dwTotalEntries,
+ dwServerType,
+ szDomainName,
+ NULL);
+
+ dwEndTime = GetTickCount() - dwStartTime;
+
+ CalcCount(dwVal, dwEndTime);
+
+ MIDL_user_free (lpBrowserList);
+ }
+
+ PrintResult();
+}
+
+
+
+void PerfTransport (INT i,
+ TCHAR szDomainName[],
+ DWORD dwServerType)
+{
+ INT j;
+ DWORD dwVal;
+ DWORD dwRxVal;
+ DWORD dwStartTime;
+ DWORD dwEndTime;
+ DWORD dwEntriesRead;
+ DWORD dwTotalEntries;
+ LPVOID lpBrowserList;
+ PWSTR * BrowserList = NULL;
+ ULONG BrowserListLength = 0;
+ PLMDR_TRANSPORT_LIST TransportList = NULL;
+ PLMDR_TRANSPORT_LIST TransportEntry = NULL;
+
+ // Find all transports that we have.
+ dwVal = GetBrowserTransportList (&TransportList);
+ if (dwVal != NERR_Success)
+ {
+ if (TransportList != NULL)
+ {
+ MIDL_user_free (TransportList);
+ }
+ printf("GetBrowserTansportList failed. Error %d\n", dwVal);
+ return;
+ }
+
+ TransportEntry = TransportList;
+
+ // Enumerate on the transports.
+ while (TransportEntry != NULL)
+ {
+ UNICODE_STRING TransportName;
+
+ TransportName.Buffer = TransportEntry->TransportName;
+ TransportName.Length = (USHORT) TransportEntry->TransportNameLength;
+ TransportName.MaximumLength = (USHORT) TransportEntry->TransportNameLength;
+
+ InitCount();
+
+ j = i;
+
+ while (j-- > 0)
+ {
+ dwVal = GetBrowserServerList (&TransportName,
+ szDomainName,
+ &BrowserList,
+ &BrowserListLength,
+ TRUE);
+
+ if (dwVal != NERR_Success) // Type 1 error occured.
+ {
+ printf("GetBrowserServerList failed. Error %d\n", dwVal);
+ if (BrowserList != NULL)
+ {
+ MIDL_user_free (BrowserList);
+ }
+ break;
+ }
+
+ dwStartTime = GetTickCount();
+
+ dwRxVal = RxNetServerEnum (BrowserList[0],
+ TransportName.Buffer,
+ 101,
+ (LPBYTE *) &lpBrowserList,
+ 0xffffffff,
+ &dwEntriesRead,
+ &dwTotalEntries,
+ dwServerType,
+ szDomainName,
+ NULL);
+
+ dwEndTime = GetTickCount() - dwStartTime;
+
+ CalcCount (dwRxVal, dwEndTime);
+
+ MIDL_user_free (lpBrowserList);
+ }
+
+ printf("\nTransport = %s\n", toansi(TransportName.Buffer));
+ if (dwVal == NERR_Success)
+ {
+ PrintResult();
+ }
+ else
+ {
+ printf("GetBrowserServerList failed. Error %d\n", dwVal);
+ }
+
+ if (TransportEntry->NextEntryOffset == 0)
+ TransportEntry = NULL;
+ else
+ {
+ TransportEntry = (PLMDR_TRANSPORT_LIST) ((PCHAR) TransportEntry
+ +TransportEntry->NextEntryOffset);
+ }
+
+ } // End of the enumeration of transports.
+
+ // Free memory.
+ MIDL_user_free (TransportList);
+}
+
+
+void InitCount ()
+{
+ INT i;
+ for (i = 0; i < 16; i++)
+ {
+ nCount[i] = 0;
+ }
+}
+
+
+void CalcCount (DWORD dwVal,
+ DWORD dwEndTime)
+{
+ if (dwVal != NERR_Success)
+ {
+ nCount[15]++;
+ }
+ else if (dwEndTime < 1000)
+ {
+ nCount[dwEndTime/100]++;
+ }
+ else if (dwEndTime > 5000)
+ {
+ nCount[14]++;
+ }
+ else
+ {
+ nCount[dwEndTime/1000 + 9]++;
+ }
+}
+
+
+void PrintResult()
+{
+ INT i;
+ printf("< 100ms = %d\n", nCount[0]);
+
+ for (i = 1; i < 10; i++)
+ {
+ printf("%d-%dms = %d\n", i*100, i*100+100, nCount[i]);
+ }
+
+ for (i = 1; i < 5; i++)
+ {
+ printf("%d-%dsec = %d\n", i, i+1, nCount[i+9]);
+ }
+
+ printf("> 5sec = %d\n", nCount[14]);
+ printf("error = %d\n", nCount[15]);
+}
+
+// Copied from ..\client\browstub.c.
+NET_API_STATUS GetBrowserTransportList (OUT PLMDR_TRANSPORT_LIST *TransportList)
+{
+
+ NET_API_STATUS Status;
+ HANDLE BrowserHandle;
+ LMDR_REQUEST_PACKET RequestPacket;
+
+ Status = OpenBrowser(&BrowserHandle);
+
+ if (Status != NERR_Success) {
+ return Status;
+ }
+
+ RequestPacket.Version = LMDR_REQUEST_PACKET_VERSION;
+
+ RequestPacket.Type = EnumerateXports;
+
+ RtlInitUnicodeString(&RequestPacket.TransportName, NULL);
+ RtlInitUnicodeString(&RequestPacket.EmulatedDomainName, NULL);
+
+ Status = DeviceControlGetInfo(
+ BrowserHandle,
+ IOCTL_LMDR_ENUMERATE_TRANSPORTS,
+ &RequestPacket,
+ sizeof(RequestPacket),
+ (PVOID *)TransportList,
+ 0xffffffff,
+ 4096,
+ NULL);
+
+ NtClose(BrowserHandle);
+
+ return Status;
+}
+
+// Convert an unicode string to ansi string.
+LPSTR toansi(LPTSTR lpUnicode)
+{
+ static CHAR lpAnsi[128];
+ BOOL fDummy;
+ INT i;
+
+ i = WideCharToMultiByte (CP_ACP,
+ 0,
+ lpUnicode,
+ lstrlen(lpUnicode),
+ lpAnsi,
+ 128,
+ NULL,
+ &fDummy);
+
+ lpAnsi[i] = 0;
+
+ return(lpAnsi);
+}
diff --git a/private/net/svcdlls/browser2/bperf/makefile b/private/net/svcdlls/browser2/bperf/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/net/svcdlls/browser2/bperf/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/net/svcdlls/browser2/bperf/sources b/private/net/svcdlls/browser2/bperf/sources
new file mode 100644
index 000000000..e1f43b3b6
--- /dev/null
+++ b/private/net/svcdlls/browser2/bperf/sources
@@ -0,0 +1,49 @@
+!IF 0
+
+Copyright (c) 1989-1992 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+
+Author:
+
+ Steve Wood (stevewo) 12-Apr-1990
+ Congpa You (congpay) 04-Feb-1993
+
+NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl
+
+!ENDIF
+
+MAJORCOMP=windows
+MINORCOMP=bperf
+
+TARGETNAME=bperf
+TARGETPATH=obj
+TARGETTYPE=LIBRARY
+
+INCLUDES=..\..\..;..\..\..\inc;..\..\..\..\inc;..;
+
+
+!IFNDEF DISABLE_NET_UNICODE
+UNICODE=1
+NET_C_DEFINES=-DUNICODE
+!ENDIF
+
+SOURCES=
+
+UMTYPE=console
+UMAPPL=bperf
+UMLIBS=\nt\public\sdk\lib\*\netapi32.lib \
+ ..\common\obj\*\brcommon.lib \
+ \nt\public\sdk\lib\*\rpcutil.lib \
+ \nt\public\sdk\lib\*\ntdll.lib \
+
diff --git a/private/net/svcdlls/browser2/brcommon.h b/private/net/svcdlls/browser2/brcommon.h
new file mode 100644
index 000000000..701c27c02
--- /dev/null
+++ b/private/net/svcdlls/browser2/brcommon.h
@@ -0,0 +1,312 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ brcommon.h
+
+Abstract:
+
+ Header for utility routines for the browser service.
+
+Author:
+
+ Larry Osterman (LarryO) 23-Mar-1992
+
+Environment:
+
+ User Mode - Win32
+
+Revision History:
+
+--*/
+
+#ifndef _BRCOMMON_
+#define _BRCOMMON_
+
+#include <winsvc.h>
+#include <svcs.h> // PSVCS_NET_BIOS_RESET
+
+#if DEVL
+//
+// Codes for I_BrowserDebugCall
+//
+
+#define BROWSER_DEBUG_BREAK_POINT 0
+#define BROWSER_DEBUG_DUMP_NETWORKS 1
+#define BROWSER_DEBUG_DUMP_SERVERS 2
+#define BROWSER_DEBUG_ENABLE_BROWSER 3
+#define BROWSER_DEBUG_SET_DEBUG 4
+#define BROWSER_DEBUG_CLEAR_DEBUG 5
+#define BROWSER_DEBUG_TICKLE 6
+#define BROWSER_DEBUG_ELECT 7
+#define BROWSER_DEBUG_GET_MASTER 8
+#define BROWSER_DEBUG_FIND_MASTER 9
+#define BROWSER_DEBUG_GET_BACKUP_LIST 10
+#define BROWSER_DEBUG_ANNOUNCE_MASTER 11
+#define BROWSER_DEBUG_ILLEGAL_DGRAM 12
+#define BROWSER_DEBUG_GET_OTHLIST 13
+#define BROWSER_DEBUG_ADD_MASTERNAME 14
+#define BROWSER_DEBUG_VIEW 15
+#define BROWSER_DEBUG_FORCE_ANNOUNCE 16
+#define BROWSER_DEBUG_LOCAL_BRLIST 17
+#define BROWSER_DEBUG_ANNOUNCE 18
+#define BROWSER_DEBUG_RPCLIST 19
+#define BROWSER_DEBUG_RPCCMP 20
+#define BROWSER_DEBUG_TRUNCATE_LOG 21
+#define BROWSER_DEBUG_STATISTICS 22
+#define BROWSER_DEBUG_BOWSERDEBUG 23
+#define BROWSER_DEBUG_POPULATE_SERVER 24
+#define BROWSER_DEBUG_POPULATE_DOMAIN 25
+#define BROWSER_DEBUG_LIST_WFW 26
+#define BROWSER_DEBUG_STATUS 27
+#define BROWSER_DEBUG_GETPDC 28
+#define BROWSER_DEBUG_ADD_DOMAINNAME 29
+#define BROWSER_DEBUG_GET_WINSSERVER 30
+#define BROWSER_DEBUG_GET_DOMAINLIST 31
+#define BROWSER_DEBUG_GET_NETBIOSNAMES 32
+#define BROWSER_DEBUG_SET_EMULATEDDOMAIN 33
+#define BROWSER_DEBUG_SET_EMULATEDDOMAINENUM 34
+#define BROWSER_DEBUG_ADD_ALTERNATE 35
+
+
+//
+// Debug trace level bits for turning on/off trace statements in the
+// browser service
+//
+
+#define BR_CRITICAL 0x00000001
+#define BR_INIT 0x00000002
+#define BR_UTIL 0x00000020
+#define BR_CONFIG 0x00000040
+#define BR_MAIN 0x00000080
+#define BR_BACKUP 0x00000400
+#define BR_MASTER 0x00000800
+#define BR_DOMAIN 0x00001000
+#define BR_NETWORK 0x00002000
+#define BR_COMMON 0x0000FFFF
+
+#define BR_TIMER 0x00010000
+#define BR_QUEUE 0x00020000
+#define BR_LOCKS 0x00040000
+#define BR_SERVER_ENUM 0x00100000
+
+#define BR_ALL 0xFFFFFFFF
+
+NET_API_STATUS
+I_BrowserDebugCall (
+ IN LPTSTR servername OPTIONAL,
+ IN DWORD DebugCode,
+ IN DWORD OptionalValue
+ );
+
+#endif
+
+typedef struct _INTERIM_ELEMENT {
+ LIST_ENTRY NextElement;
+ ULONG Periodicity;
+ ULONG TimeLastSeen;
+ ULONG PlatformId;
+ ULONG MajorVersionNumber;
+ ULONG MinorVersionNumber;
+ ULONG Type;
+ TCHAR Name[CNLEN+1];
+ TCHAR Comment[MAXCOMMENTSZ+1];
+} INTERIM_ELEMENT, *PINTERIM_ELEMENT;
+
+struct _INTERIM_SERVER_LIST;
+
+typedef
+VOID
+(*PINTERIM_NEW_CALLBACK)(
+ IN struct _INTERIM_SERVER_LIST *InterimList,
+ IN PINTERIM_ELEMENT Element
+ );
+
+typedef
+VOID
+(*PINTERIM_EXISTING_CALLBACK)(
+ IN struct _INTERIM_SERVER_LIST *InterimList,
+ IN PINTERIM_ELEMENT Element
+ );
+
+
+typedef
+VOID
+(*PINTERIM_DELETE_CALLBACK)(
+ IN struct _INTERIM_SERVER_LIST *InterimList,
+ IN PINTERIM_ELEMENT Element
+ );
+
+typedef
+BOOLEAN
+(*PINTERIM_AGE_CALLBACK)(
+ IN struct _INTERIM_SERVER_LIST *InterimList,
+ IN PINTERIM_ELEMENT Element
+ );
+
+
+typedef struct _INTERIM_SERVER_LIST {
+// RTL_GENERIC_TABLE ServerTable;
+ LIST_ENTRY ServerList;
+ ULONG TotalBytesNeeded;
+ ULONG TotalEntries;
+ ULONG EntriesRead;
+ PINTERIM_NEW_CALLBACK NewElementCallback;
+ PINTERIM_EXISTING_CALLBACK ExistingElementCallback;
+ PINTERIM_DELETE_CALLBACK DeleteElementCallback;
+ PINTERIM_AGE_CALLBACK AgeElementCallback;
+} INTERIM_SERVER_LIST, *PINTERIM_SERVER_LIST;
+
+
+NET_API_STATUS
+DeviceControlGetInfo(
+ IN HANDLE FileHandle,
+ IN ULONG DeviceControlCode,
+ IN PVOID RequestPacket,
+ IN ULONG RequestPacketLength,
+ OUT LPVOID *OutputBuffer,
+ IN ULONG PreferedMaximumLength,
+ IN ULONG BufferHintSize,
+ OUT PULONG Information OPTIONAL
+ );
+
+ NET_API_STATUS
+BrDgReceiverIoControl(
+ IN HANDLE FileHandle,
+ IN ULONG DgReceiverControlCode,
+ IN PLMDR_REQUEST_PACKET Drp,
+ IN ULONG DrpSize,
+ IN PVOID SecondBuffer OPTIONAL,
+ IN ULONG SecondBufferLength,
+ OUT PULONG Information OPTIONAL
+ );
+
+NET_API_STATUS
+OpenBrowser(
+ OUT PHANDLE BrowserHandle
+ );
+
+NET_API_STATUS
+GetBrowserServerList(
+ IN PUNICODE_STRING TransportName,
+ IN LPCWSTR domain,
+ OUT LPWSTR *BrowserList[],
+ OUT PULONG BrowserListLength,
+ IN BOOLEAN ForceRescan
+ );
+
+NET_API_STATUS
+InitializeInterimServerList(
+ IN PINTERIM_SERVER_LIST InterimServerList,
+ IN PINTERIM_NEW_CALLBACK NewCallback,
+ IN PINTERIM_EXISTING_CALLBACK ExistingCallback,
+ IN PINTERIM_DELETE_CALLBACK DeleteElementCallback,
+ IN PINTERIM_AGE_CALLBACK AgeElementCallback
+ );
+
+NET_API_STATUS
+CopyInterimServerList(
+ IN PINTERIM_SERVER_LIST NewInterimServerList,
+ IN PINTERIM_SERVER_LIST OldInterimServerList
+ );
+
+
+
+NET_API_STATUS
+UninitializeInterimServerList(
+ IN PINTERIM_SERVER_LIST InterimServerList
+ );
+
+
+NET_API_STATUS
+InsertElementInterimServerList (
+ IN PINTERIM_SERVER_LIST InterimServerList,
+ IN PINTERIM_ELEMENT InterimElement,
+ IN ULONG Level,
+ IN PBOOLEAN NewElement OPTIONAL,
+ IN PINTERIM_ELEMENT *ActualElement OPTIONAL
+ );
+
+ULONG
+NumberInterimServerListElements(
+ IN PINTERIM_SERVER_LIST InterimServerList
+ );
+
+NET_API_STATUS
+AgeInterimServerList(
+ IN PINTERIM_SERVER_LIST InterimServerList
+ );
+
+
+NET_API_STATUS
+MergeServerList(
+ IN PINTERIM_SERVER_LIST InterimServerList,
+ IN ULONG level,
+ IN PVOID NewServerList,
+ IN ULONG NewEntriesRead,
+ IN ULONG NewTotalEntries
+ );
+
+PINTERIM_ELEMENT
+LookupInterimServerList(
+ IN PINTERIM_SERVER_LIST InterimServerList,
+ IN LPTSTR ServerNameToLookUp
+ );
+
+
+
+NET_API_STATUS
+PackServerList(
+ IN PINTERIM_SERVER_LIST InterimServerList,
+ IN ULONG Level,
+ IN ULONG ServerType,
+ IN ULONG PreferedDataLength,
+ OUT PVOID *bufptr,
+ OUT PULONG entriesread,
+ OUT PULONG totalentries,
+ IN LPCWSTR FirstNameToReturn
+ );
+
+VOID
+PrepareServerListForMerge(
+ IN PVOID ServerInfoList,
+ IN ULONG Level,
+ IN ULONG EntriesInList
+ );
+
+NET_API_STATUS
+CheckForService(
+ IN LPTSTR ServiceName,
+ OUT LPSERVICE_STATUS ServiceStatus OPTIONAL
+ );
+
+
+NET_API_STATUS
+BrGetLanaNumFromNetworkName(
+ IN LPWSTR TransportName,
+ OUT CCHAR *LanaNum
+ );
+
+NET_API_STATUS
+GetNetBiosMasterName(
+ IN LPWSTR NetworkName,
+ IN LPWSTR PrimaryDomain,
+ OUT LPWSTR MasterName,
+ IN PSVCS_NET_BIOS_RESET SvcsNetBiosReset OPTIONAL
+ );
+
+NET_API_STATUS
+SendDatagram(
+ IN HANDLE DgReceiverHandle,
+ IN PUNICODE_STRING Network,
+ IN PUNICODE_STRING EmulatedDomainName,
+ IN PWSTR ResponseName,
+ IN DGRECEIVER_NAME_TYPE NameType,
+ IN PVOID Buffer,
+ IN ULONG BufferLength
+ );
+
+#endif // _BRCOMMON_
+
diff --git a/private/net/svcdlls/browser2/brnames.h b/private/net/svcdlls/browser2/brnames.h
new file mode 100644
index 000000000..5317fb063
--- /dev/null
+++ b/private/net/svcdlls/browser2/brnames.h
@@ -0,0 +1,21 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ wsnames.h
+
+Abstract:
+
+ Private header file which defines the Workstation names.
+
+Author:
+
+ Rita Wong (ritaw) 06-May-1991
+
+Revision History:
+
+--*/
+
+#define BROWSER_INTERFACE_NAME TEXT("browser")
diff --git a/private/net/svcdlls/browser2/browtest/browfunc.c b/private/net/svcdlls/browser2/browtest/browfunc.c
new file mode 100644
index 000000000..bb1f9d662
--- /dev/null
+++ b/private/net/svcdlls/browser2/browtest/browfunc.c
@@ -0,0 +1,569 @@
+
+#ifndef UNICODE
+#define UNICODE
+#endif
+
+#include "browfunc.h"
+
+CHAR WCtoPrintfBuf[256];
+
+#define SPACES " "
+#define BUFF_SIZE 128
+
+#define ClearNcb( PNCB ) { \
+ RtlZeroMemory( PNCB , sizeof (NCB) ); \
+ RtlCopyMemory( (PNCB)->ncb_name, SPACES, sizeof(SPACES)-1 );\
+ RtlCopyMemory( (PNCB)->ncb_callname, SPACES, sizeof(SPACES)-1 );\
+ }
+
+
+NET_API_STATUS
+ClearNbtNameTableCache(UNICODE_STRING usDevice)
+{
+HANDLE hNbt = (HANDLE)-1;
+CHAR cBuffer;
+CHAR pScope[BUFF_SIZE];
+TCHAR pDeviceName[BUFF_SIZE];
+DWORD Status;
+
+// Status = ReadRegistry((CHAR*)pDeviceName, pScope);
+// if(Status != ERROR_SUCCESS)
+// return (!NERR_Success);
+
+ Status = OpenNbt(usDevice, &hNbt);
+ if(!NT_SUCCESS(Status))
+ return (!NERR_Success);
+
+ Status = DeviceIoCtrl(hNbt, &cBuffer, 1, IOCTL_NETBT_PURGE_CACHE, NULL, 0);
+ if(!NT_SUCCESS(Status))
+ return (!NERR_Success);
+
+ NtClose(hNbt);
+
+return NERR_Success;
+}
+
+
+
+
+NET_API_STATUS
+Elect(
+ IN UNICODE_STRING Transport,
+ IN LPTSTR Domain
+ )
+{
+ REQUEST_ELECTION ElectionRequest;
+ HANDLE BrowserHandle;
+ NET_API_STATUS Status;
+ UNICODE_STRING DomainName;
+
+ OpenBrowser(&BrowserHandle);
+
+ ElectionRequest.Type = Election;
+
+ ElectionRequest.ElectionRequest.Version = 0;
+ ElectionRequest.ElectionRequest.Criteria = 0;
+ ElectionRequest.ElectionRequest.TimeUp = 0;
+ ElectionRequest.ElectionRequest.MustBeZero = 0;
+ ElectionRequest.ElectionRequest.ServerName[0] = '\0';
+ RtlInitUnicodeString( &DomainName, Domain );
+
+ Status = SendDatagram(BrowserHandle,
+ &Transport,
+ &DomainName,
+ Domain,
+ BrowserElection,
+ &ElectionRequest,
+ sizeof(ElectionRequest));
+ CloseHandle(BrowserHandle);
+
+ return Status;
+}
+
+
+VOID
+ForceAnnounce(
+ IN UNICODE_STRING Transport,
+ IN LPTSTR Domain
+ )
+{
+ REQUEST_ANNOUNCE_PACKET RequestAnnounce;
+ HANDLE BrowserHandle;
+ ULONG NameSize = sizeof(RequestAnnounce.RequestAnnouncement.Reply);
+ UNICODE_STRING DomainName;
+
+
+ OpenBrowser(&BrowserHandle);
+
+ RequestAnnounce.Type = AnnouncementRequest;
+
+ RequestAnnounce.RequestAnnouncement.Flags = 0;
+
+ GetComputerNameA(RequestAnnounce.RequestAnnouncement.Reply, &NameSize);
+ RtlInitUnicodeString( &DomainName, Domain );
+
+ SendDatagram(BrowserHandle, &Transport,
+ &DomainName,
+ Domain,
+ BrowserElection,
+ &RequestAnnounce,
+ FIELD_OFFSET(REQUEST_ANNOUNCE_PACKET, RequestAnnouncement.Reply) + NameSize + sizeof(CHAR));
+ CloseHandle(BrowserHandle);
+
+}
+
+
+NET_API_STATUS
+GetBList(
+ IN UNICODE_STRING TransportName,
+ IN TCHAR * Domain,
+ IN BOOLEAN ForceRescan,
+ OUT ULONG * NumBackUps,
+ OUT TCHAR wcBackUpBrowsers[MAXBACKUPS][CNLEN+3]
+ )
+{
+ NET_API_STATUS Status;
+ UNICODE_STRING UTransportName;
+ ANSI_STRING ATransportName;
+ PWSTR *BrowserList;
+ ULONG i;
+
+ Status = GetBrowserServerList(&TransportName, Domain,
+ &BrowserList,
+ NumBackUps,
+ ForceRescan);
+
+ if (Status != NERR_Success)
+ return Status;
+
+ for (i = 0; (i < (*NumBackUps)) && (i < (ULONG)MAXBACKUPS); i ++ ) {
+// printf("Browser: %s\n", UnicodeToPrintfString(BrowserList[i]));
+ lstrcpy(wcBackUpBrowsers[i], BrowserList[i]);
+ }
+
+ NetApiBufferFree(BrowserList);
+
+return Status;
+}
+
+
+NET_API_STATUS
+GetBrowserTransportList(
+ OUT PLMDR_TRANSPORT_LIST *TransportList
+ )
+
+/*++
+
+Routine Description:
+
+ This routine returns the list of transports bound into the browser.
+
+Arguments:
+
+ OUT PLMDR_TRANSPORT_LIST *TransportList - Transport list to return.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+
+{
+
+ NET_API_STATUS Status;
+ HANDLE BrowserHandle;
+ LMDR_REQUEST_PACKET RequestPacket;
+
+ Status = OpenBrowser(&BrowserHandle);
+
+ if (Status != NERR_Success) {
+ return Status;
+ }
+
+ RequestPacket.Version = LMDR_REQUEST_PACKET_VERSION;
+
+ RequestPacket.Type = EnumerateXports;
+
+ RtlInitUnicodeString(&RequestPacket.TransportName, NULL);
+ RtlInitUnicodeString(&RequestPacket.EmulatedDomainName, NULL);
+
+ Status = DeviceControlGetInfo(
+ BrowserHandle,
+ IOCTL_LMDR_ENUMERATE_TRANSPORTS,
+ &RequestPacket,
+ sizeof(RequestPacket),
+ (PVOID *)TransportList,
+ 0xffffffff,
+ 4096,
+ NULL);
+
+ NtClose(BrowserHandle);
+
+ return Status;
+}
+
+
+NET_API_STATUS
+GetNetBiosPdcName(
+ IN LPWSTR NetworkName,
+ IN LPWSTR PrimaryDomain,
+ OUT LPWSTR MasterName
+ )
+{
+ CCHAR LanaNum;
+ NCB AStatNcb;
+ struct {
+ ADAPTER_STATUS AdapterInfo;
+ NAME_BUFFER Names[32];
+ } AdapterStatus;
+ WORD i;
+ CHAR remoteName[CNLEN+1];
+ NET_API_STATUS Status;
+ BOOL UsedDefaultChar;
+
+ Status = BrGetLanaNumFromNetworkName(NetworkName, &LanaNum);
+
+ if (Status != NERR_Success) {
+ return Status;
+ }
+
+ ClearNcb(&AStatNcb);
+
+ AStatNcb.ncb_command = NCBRESET;
+ AStatNcb.ncb_lsn = 0; // Request resources
+ AStatNcb.ncb_lana_num = LanaNum;
+ AStatNcb.ncb_callname[0] = 0; // 16 sessions
+ AStatNcb.ncb_callname[1] = 0; // 16 commands
+ AStatNcb.ncb_callname[2] = 0; // 8 names
+ AStatNcb.ncb_callname[3] = 0; // Don't want the reserved address
+ Netbios( &AStatNcb );
+
+ ClearNcb( &AStatNcb );
+
+ if (WideCharToMultiByte( CP_OEMCP, 0,
+ PrimaryDomain,
+ -1,
+ remoteName,
+ sizeof(remoteName),
+ "?",
+ &UsedDefaultChar) == 0) {
+ return GetLastError();
+ }
+
+ //
+ // Uppercase the remote name.
+ //
+
+ _strupr(remoteName);
+
+ AStatNcb.ncb_command = NCBASTAT;
+
+ RtlCopyMemory( AStatNcb.ncb_callname, remoteName, strlen(remoteName));
+
+ AStatNcb.ncb_callname[15] = PRIMARY_CONTROLLER_SIGNATURE;
+
+ AStatNcb.ncb_lana_num = LanaNum;
+ AStatNcb.ncb_length = sizeof( AdapterStatus );
+ AStatNcb.ncb_buffer = (CHAR *)&AdapterStatus;
+ Netbios( &AStatNcb );
+
+ if ( AStatNcb.ncb_retcode == NRC_GOODRET ) {
+ for ( i=0 ; i < AdapterStatus.AdapterInfo.name_count ; i++ ) {
+ if (AdapterStatus.Names[i].name[NCBNAMSZ-1] == SERVER_SIGNATURE) {
+// LPWSTR SpacePointer;
+ DWORD j;
+
+ if (MultiByteToWideChar(CP_OEMCP,
+ 0,
+ AdapterStatus.Names[i].name,
+ CNLEN,
+ MasterName,
+ CNLEN) == 0) {
+ return(GetLastError());
+ }
+
+ for (j = CNLEN - 1; j ; j -= 1) {
+ if (MasterName[j] != L' ') {
+ MasterName[j+1] = UNICODE_NULL;
+ break;
+ }
+ }
+
+ return NERR_Success;
+ }
+ }
+ } else {
+ return AStatNcb.ncb_retcode;
+ }
+}
+
+
+//
+// map an error number to its error message string. note, uses static,
+// not reentrant.
+//
+CHAR *
+get_error_text(DWORD dwErr)
+{
+ static CHAR text[512] ;
+ WORD err ;
+ WORD msglen ;
+
+ memset(text,0, sizeof(text));
+
+ //
+ // get error message
+ //
+ err = DosGetMessage(NULL,
+ 0,
+ text,
+ sizeof(text),
+ (WORD)dwErr,
+ (dwErr<NERR_BASE)||(dwErr>MAX_LANMAN_MESSAGE_ID) ?
+ TEXT("BASE"):TEXT("NETMSG"),
+ &msglen) ;
+
+ if (err != NERR_Success)
+ {
+ // use number instead. if looks like NTSTATUS then use hex.
+ sprintf(text, (dwErr & 0xC0000000)?"(%lx)":"(%ld)", dwErr) ;
+ }
+
+ return text ;
+}
+
+
+NTSTATUS
+ReadRegistry(
+ OUT PUCHAR pDeviceName,
+ OUT PUCHAR pScope
+ )
+
+/*++
+
+Routine Description:
+
+ This procedure reads the registry to get the name of NBT to bind to.
+ That name is stored in the Linkage/Exports section under the Netbt key.
+
+Arguments:
+
+
+Return Value:
+
+ 0 if successful, -1 otherwise.
+
+--*/
+
+{
+ PWCHAR SubKeyParms=L"system\\currentcontrolset\\services\\netbt\\parameters";
+ PWCHAR SubKeyLinkage=L"system\\currentcontrolset\\services\\netbt\\linkage";
+ HKEY Key;
+ PWCHAR Scope=L"ScopeId";
+ PWCHAR Linkage=L"Export";
+ LONG Type;
+ LONG status;
+ ULONG size;
+
+ size = BUFF_SIZE;
+ status = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
+ SubKeyLinkage,
+ 0,
+ KEY_READ,
+ &Key);
+
+ if (status == ERROR_SUCCESS){
+ // now read the linkage values
+ status = RegQueryValueEx(Key,
+ Linkage,
+ NULL,
+ &Type,
+ pDeviceName,
+ &size);
+
+ if (status != ERROR_SUCCESS) {
+
+ RegCloseKey(Key);
+ return(STATUS_UNSUCCESSFUL);
+ }
+
+ RegCloseKey(Key);
+
+ } else {
+ return(STATUS_UNSUCCESSFUL);
+ }
+
+
+ size = BUFF_SIZE;
+ status = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
+ SubKeyParms,
+ 0,
+ KEY_READ,
+ &Key);
+
+ if (status == ERROR_SUCCESS){
+ // now read the linkage values
+ status = RegQueryValueEx(Key,
+ Scope,
+ NULL,
+ &Type,
+ pScope,
+ &size);
+
+ if (status != ERROR_SUCCESS) {
+ RegCloseKey(Key);
+ return(STATUS_UNSUCCESSFUL);
+ }
+
+ RegCloseKey(Key);
+
+ } else
+ return(STATUS_UNSUCCESSFUL);
+
+ return(STATUS_SUCCESS);
+}
+
+
+NTSTATUS
+OpenNbt(
+ IN UNICODE_STRING usDeviceName,
+ OUT PHANDLE pHandle
+ )
+
+/*++
+
+Routine Description:
+
+ This function opens a stream.
+
+Arguments:
+
+ path - path to the STREAMS driver
+ oflag - currently ignored. In the future, O_NONBLOCK will be
+ relevant.
+ ignored - not used
+
+Return Value:
+
+ An NT handle for the stream, or INVALID_HANDLE_VALUE if unsuccessful.
+
+--*/
+
+{
+ HANDLE StreamHandle;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ IO_STATUS_BLOCK IoStatusBlock;
+ NTSTATUS status;
+
+
+ InitializeObjectAttributes(
+ &ObjectAttributes,
+ &usDeviceName,
+ OBJ_CASE_INSENSITIVE,
+ (HANDLE) NULL,
+ (PSECURITY_DESCRIPTOR) NULL
+ );
+
+ status =
+ NtCreateFile(
+ &StreamHandle,
+ SYNCHRONIZE | FILE_READ_DATA | FILE_WRITE_DATA,
+ &ObjectAttributes,
+ &IoStatusBlock,
+ NULL,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ FILE_OPEN_IF,
+ 0,
+ NULL,
+ 0);
+
+
+ *pHandle = StreamHandle;
+
+ return(status);
+
+} // s_open
+
+//------------------------------------------------------------------------
+NTSTATUS
+DeviceIoCtrl(
+ IN HANDLE fd,
+ IN PVOID ReturnBuffer,
+ IN ULONG BufferSize,
+ IN ULONG Ioctl,
+ IN PVOID pInput,
+ IN ULONG SizeInput
+ )
+
+/*++
+
+Routine Description:
+
+ This procedure performs an ioctl(I_STR) on a stream.
+
+Arguments:
+
+ fd - NT file handle
+ iocp - pointer to a strioctl structure
+
+Return Value:
+
+ 0 if successful, -1 otherwise.
+
+--*/
+
+{
+ NTSTATUS status;
+ int retval;
+ ULONG QueryType;
+ IO_STATUS_BLOCK iosb;
+
+
+ status = NtDeviceIoControlFile(
+ fd, // Handle
+ NULL, // Event
+ NULL, // ApcRoutine
+ NULL, // ApcContext
+ &iosb, // IoStatusBlock
+ Ioctl, // IoControlCode
+ pInput, // InputBuffer
+ SizeInput, // InputBufferSize
+ (PVOID) ReturnBuffer, // OutputBuffer
+ BufferSize); // OutputBufferSize
+
+
+ if (status == STATUS_PENDING)
+ {
+ status = NtWaitForSingleObject(
+ fd, // Handle
+ TRUE, // Alertable
+ NULL); // Timeout
+ if (NT_SUCCESS(status))
+ {
+ status = iosb.Status;
+ }
+ }
+
+ return(status);
+
+}
+
+
+
+PCHAR
+UnicodeToPrintfString(
+ PWCHAR WideChar
+ )
+{
+ UNICODE_STRING UString;
+ ANSI_STRING AString;
+ AString.Buffer = WCtoPrintfBuf;
+ AString.MaximumLength = sizeof(WCtoPrintfBuf);
+ RtlInitUnicodeString(&UString, WideChar);
+ RtlUnicodeStringToOemString(&AString, &UString, FALSE);
+
+ return WCtoPrintfBuf;
+}
+
+
diff --git a/private/net/svcdlls/browser2/browtest/browfunc.h b/private/net/svcdlls/browser2/browtest/browfunc.h
new file mode 100644
index 000000000..6138a375a
--- /dev/null
+++ b/private/net/svcdlls/browser2/browtest/browfunc.h
@@ -0,0 +1,212 @@
+#ifndef _BROWFUNC
+#define _BROWFUNC
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windows.h>
+#include <lm.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <ntddbrow.h>
+#include <hostannc.h>
+#include <lmbrowsr.h>
+#include <brcommon.h>
+#include <netlib.h>
+#include <nb30.h>
+#include <rxserver.h>
+#include <nbtioctl.h>
+
+
+
+#define MAXBACKUPS 5
+
+NET_API_STATUS
+ClearNbtNameTableCache(UNICODE_STRING);
+
+
+NTSTATUS
+DeviceIoCtrl(
+ IN HANDLE fd,
+ IN PVOID ReturnBuffer,
+ IN ULONG BufferSize,
+ IN ULONG Ioctl,
+ IN PVOID pInput,
+ IN ULONG SizeInput
+ );
+
+
+BOOL
+FindAllTransports(UNICODE_STRING *,
+ DWORD *
+ );
+
+VOID
+ListWFW(
+ IN PCHAR Domain
+ );
+
+VOID
+RpcList(
+ IN PCHAR Transport,
+ IN PCHAR ServerOrDomain,
+ IN PCHAR Flags,
+ IN BOOL GoForever
+ );
+
+VOID
+RpcCmp(
+ IN PCHAR Transport,
+ IN PCHAR ServerOrDomain,
+ IN PCHAR Flags,
+ IN BOOL GoForever
+ );
+
+NET_API_STATUS
+GetBrowserTransportList(
+ OUT PLMDR_TRANSPORT_LIST *
+ );
+
+PCHAR
+get_error_text(
+ DWORD dwErr
+ );
+
+VOID
+GetLocalList(
+ IN PCHAR Transport,
+ IN PCHAR Flags
+ );
+
+NET_API_STATUS
+GetNetBiosPdcName(
+ IN LPWSTR NetworkName,
+ IN LPWSTR PrimaryDomain,
+ OUT LPWSTR MasterName
+ );
+
+VOID
+GetOtherdomains(
+ IN PCHAR ServerName
+ );
+
+VOID
+IllegalDatagram(
+ IN PCHAR Transport,
+ IN PCHAR ServerName
+ );
+VOID
+AnnounceMaster(
+ IN PCHAR Transport,
+ IN PCHAR ServerName
+ );
+
+VOID
+Announce(
+ IN PCHAR Transport,
+ IN PCHAR Domain,
+ IN BOOL AsMaster
+ );
+
+NTSTATUS
+OpenNbt(
+ IN UNICODE_STRING,
+ OUT PHANDLE pHandle
+ );
+
+
+VOID
+Populate(
+ IN BOOL PopulateDomain,
+ IN PCHAR Transport,
+ IN PCHAR Domain,
+ IN PCHAR NumberOfMachines,
+ IN PCHAR Frequency
+ );
+
+VOID
+AddMasterName(
+ IN PCHAR Transport,
+ IN PCHAR Domain,
+ IN BOOL Pause
+ );
+
+VOID
+AddDomainName(
+ IN PCHAR Transport,
+ IN PCHAR Domain,
+ IN BOOL Pause
+ );
+
+
+VOID
+Tickle(
+ IN PCHAR Transport,
+ IN PCHAR Domain
+ );
+
+VOID
+ForceAnnounce(
+ IN UNICODE_STRING Transport,
+ IN LPTSTR Domain
+ );
+
+NET_API_STATUS
+GetBList(
+ IN UNICODE_STRING TransportName,
+ IN TCHAR * DomainName,
+ IN BOOLEAN ForceRescan,
+ OUT ULONG * NumBackUps,
+ OUT TCHAR wcBackUpBrowsers[MAXBACKUPS][CNLEN+3]
+ );
+
+VOID
+DumpStatistics(
+ IN ULONG NArgs,
+ IN PCHAR Arg1
+ );
+
+
+NET_API_STATUS
+Elect(
+ IN UNICODE_STRING Transport,
+ IN LPTSTR Domain
+ );
+
+
+NET_API_STATUS
+EnableService(
+ IN LPTSTR ServiceName
+ );
+
+
+VOID
+GetWinsServer(
+ IN PCHAR Transport
+ );
+
+VOID
+GetDomainList(
+ IN PCHAR IpAddress
+ );
+
+NTSTATUS
+ReadRegistry(
+ OUT PUCHAR pDeviceName,
+ OUT PUCHAR pScope
+ );
+
+
+PCHAR
+UnicodeToPrintfString( PWCHAR );
+
+VOID
+View(
+ IN PCHAR Transport,
+ IN PCHAR ServerOrDomain,
+ IN PCHAR Flags,
+ IN PCHAR Domain,
+ IN BOOL GoForever
+ );
+
+#endif
diff --git a/private/net/svcdlls/browser2/browtest/browglob.h b/private/net/svcdlls/browser2/browtest/browglob.h
new file mode 100644
index 000000000..4f2e61e9d
--- /dev/null
+++ b/private/net/svcdlls/browser2/browtest/browglob.h
@@ -0,0 +1,62 @@
+/*
+ * This file contains all the global variables in browtest.
+ */
+
+
+
+//
+// Note that the ordering of these lists are important
+//
+TCHAR *TRANSPORTS[MAXPROTOCOLS] = {L"NwlnkIpx", L"NwlnkNb", L"NetBT", L"Ubnb", L"Nbf"};
+CHAR *PROTOCOLS[MAXPROTOCOLS] = {"IPX", "NBIPX", "TCP", "XNS", "NETBEUI"};
+enum E_PROTOCOLS {IPX, NBIPX, TCP, XNS, NETBEUI};
+
+
+OSPROP OSTYPES[MAXOSTYPES] = {{"DOSLM", 0},
+ {"OS2", 0},
+ {"WFW3.11", 1},
+ {"CHICAGO", 1},
+ {"NT3.1", 2},
+ {"NT3.5", 2},
+ {"AS3.1", 3},
+ {"AS3.5", 4}
+ };
+
+
+MACHINEINFO HeadList1;
+
+// File pointers to the log file and summary file
+FILE *fplog;
+FILE *fpsum;
+
+
+CHAR PrintBuf[1024];
+DOMAININFO LocDomInfo; // PDC of local domain
+LPMACHINEINFO lpDomStart; // Local machine domain start
+LPMACHINEINFO lpLocMachineInfo; // Local machine info
+
+DWORD ERRCOUNT = 0;
+DWORD WRNCOUNT = 0;
+DWORD TESTCOUNT = 0;
+DWORD SUCSCOUNT = 0;
+DWORD OKCOUNT = 0;
+
+TCHAR wcTESTEDDOMAINS[MAXTESTEDDOMAINS][DNLEN+1];
+INT iNumOfTestedDomains = 0;
+
+BOOL bIPXHack = TRUE;
+BOOL bForceAnn = FALSE;
+BOOL bSingleDomTest = TRUE;
+DWORD dwStressTest = 0;
+
+
+COMMAND_TABLE CommandTable[] = {{"IPXHack" , VALUETYPE_BOOL, &bIPXHack},
+ {"ForceAnn", VALUETYPE_BOOL, &bForceAnn},
+ {"SingleD" , VALUETYPE_BOOL, &bSingleDomTest},
+ {"StressT" , VALUETYPE_ULONG, &dwStressTest}};
+
+
+INT CommandTableSz = (sizeof(CommandTable)/sizeof(COMMAND_TABLE));
+
+
+HANDLE ConsoleMutex;
diff --git a/private/net/svcdlls/browser2/browtest/browmdom.c b/private/net/svcdlls/browser2/browtest/browmdom.c
new file mode 100644
index 000000000..ab746aeaf
--- /dev/null
+++ b/private/net/svcdlls/browser2/browtest/browmdom.c
@@ -0,0 +1,1165 @@
+#include "browmdom.h"
+
+extern CHAR PrintBuf[1024];
+extern MACHINEINFO HeadList1;
+
+extern TCHAR wcTESTEDDOMAINS[MAXTESTEDDOMAINS][DNLEN+1];
+extern INT iNumOfTestedDomains;
+extern FILE *fplog;
+extern FILE *fpsum;
+
+extern DWORD ERRCOUNT;
+extern DWORD WRNCOUNT;
+extern DWORD TESTCOUNT;
+extern DWORD SUCSCOUNT;
+extern DWORD OKCOUNT;
+
+extern enum E_PROTOCOLS {IPX, NBIPX, TCP, XNS, NETBEUI};
+
+extern BOOL bIPXHack;
+extern BOOL bForceAnn;
+
+extern LPMACHINEINFO lpDomStart;
+extern DOMAININFO LocDomInfo;
+
+extern HANDLE ConsoleMutex;
+
+VOID
+CheckSrvListOnPrimaryDom(DOMAININFO DomainInfo,
+ XPORTINFO *TestedXportInfo,
+ INT iNumOfTestedXports,
+ TCHAR wcCurrentMasters[MAXPROTOCOLS][CNLEN+1],
+ BOOL bTEST_TCP)
+{
+INT i, index;
+DWORD dwEntriesInList;
+DWORD dwTotalEntries;
+PVOID pvMsServerList;
+NET_API_STATUS Status;
+PSERVER_INFO_101 pServerInfo101;
+
+
+ for(index = 0; index < iNumOfTestedXports; index++){
+
+ //
+ // If TCP/IP is not to be tested then skip
+ //
+ if((TestedXportInfo[index].index == TCP) && !bTEST_TCP)
+ continue;
+
+ //
+ // If no master is available in primary domain, then don't test other PDC.
+ //
+ if(_wcsicmp(wcCurrentMasters[index], L"") == 0)
+ continue;
+
+ if(DomainInfo.lpMInfo->Protocols[TestedXportInfo[index].index]){
+ //
+ // Remote machine has this protocol
+ //
+ sprintf(PrintBuf,"\n\nTEST#: [TS%ld].\n=============", ++TESTCOUNT);
+ PrintString(TOALL, PrintBuf);
+
+ sprintf(PrintBuf,"\n\nTEST: Check if list from %s has the Other domain name. ",
+ UnicodeToPrintfString(wcCurrentMasters[index]));
+ PrintString(TOALL, PrintBuf);
+ sprintf(PrintBuf," Transport %s.\n",
+ UnicodeToPrintfString(TestedXportInfo[index].Transport.Buffer));
+ PrintString(TOALL, PrintBuf);
+
+
+ if((TestedXportInfo[index].index == IPX) && bIPXHack){
+
+ Status = RetrieveList(wcCurrentMasters[index], L"\\Device\\NwLnkNb",
+ &pvMsServerList, &dwEntriesInList, &dwTotalEntries,
+ SV_TYPE_ALTERNATE_XPORT|SV_TYPE_DOMAIN_ENUM, NULL, NULL, FALSE);
+
+ } else {
+ Status = RetrieveList(wcCurrentMasters[index], TestedXportInfo[index].Transport.Buffer,
+ &pvMsServerList, &dwEntriesInList, &dwTotalEntries,
+ SV_TYPE_DOMAIN_ENUM, NULL, NULL, FALSE);
+ }
+
+ if(Status == NERR_Success){
+ pServerInfo101 = (PSERVER_INFO_101)pvMsServerList;
+
+ //
+ // Check whether the other domain name is there in the list
+ // retrieved from the current master of primary domain on this transport.
+ //
+ for(i=0; i< (INT)dwEntriesInList &&
+ _wcsicmp(pServerInfo101[i].sv101_name, DomainInfo.wcDomainName) != 0; i++);
+
+ if(i >= (INT)dwEntriesInList ) {
+ sprintf(PrintBuf,"\n\nERROR[ER%ld]: Other Domain Name not found in the list from %s ",
+ ++ERRCOUNT, UnicodeToPrintfString(wcCurrentMasters[index]));
+ PrintString(TOALL, PrintBuf);
+
+ sprintf(PrintBuf,"on Transport %s.\n",
+ UnicodeToPrintfString(TestedXportInfo[index].Transport.Buffer));
+ PrintString(TOALL, PrintBuf);
+ } else {
+
+ sprintf(PrintBuf,"\nSUCCESS[SC%ld]: Other Domain Name found in the list from %s ",
+ ++SUCSCOUNT, UnicodeToPrintfString(wcCurrentMasters[index]));
+ PrintString(TOALL, PrintBuf);
+
+ sprintf(PrintBuf,"on Transport %s.\n",
+ UnicodeToPrintfString(TestedXportInfo[index].Transport.Buffer));
+ PrintString(TOALL, PrintBuf);
+ }
+
+ } else {
+ sprintf(PrintBuf,"\n\nERROR[ER%ld]: Cannot get the Domain List from %s ",
+ ++ERRCOUNT, UnicodeToPrintfString(wcCurrentMasters[index]));
+ PrintString(TOALL, PrintBuf);
+
+ sprintf(PrintBuf,"on Transport %s.\n",
+ UnicodeToPrintfString(TestedXportInfo[index].Transport.Buffer));
+ PrintString(TOALL, PrintBuf);
+ }
+
+ NetApiBufferFree(pvMsServerList);
+ }
+
+ }
+
+}
+
+
+
+VOID
+CheckSrvListOnOtherDom(DOMAININFO DomainInfo,
+ XPORTINFO *TestedXportInfo,
+ INT iNumOfTestedXports,
+ TCHAR wcCurrentMasters[MAXPROTOCOLS][CNLEN+1],
+ BOOL bTEST_TCP)
+{
+INT i, index;
+DWORD dwEntriesInList;
+DWORD dwTotalEntries;
+PVOID pvMsServerList;
+NET_API_STATUS Status;
+PSERVER_INFO_101 pServerInfo101;
+
+
+ for(index = 0; index < iNumOfTestedXports; index++){
+ //
+ // If TCP/IP is not to be tested then skip
+ //
+ if((TestedXportInfo[index].index == TCP) && !bTEST_TCP)
+ continue;
+
+ //
+ // If no master is available in primary domain, then don't test other PDC.
+ //
+ if(_wcsicmp(wcCurrentMasters[index], L"") == 0)
+ continue;
+
+ if(DomainInfo.lpMInfo->Protocols[TestedXportInfo[index].index]){
+ //
+ // Remote machine has this protocol
+ //
+ sprintf(PrintBuf,"\n\nTEST#: [TS%ld].\n=============", ++TESTCOUNT);
+ PrintString(TOALL, PrintBuf);
+
+ sprintf(PrintBuf,"\n\nTEST: Check if list from %s has the Primary domain name. ",
+ UnicodeToPrintfString(DomainInfo.lpMInfo->wcMachineName));
+ PrintString(TOALL, PrintBuf);
+ sprintf(PrintBuf," Transport %s.\n",
+ UnicodeToPrintfString(TestedXportInfo[index].Transport.Buffer));
+ PrintString(TOALL, PrintBuf);
+
+
+ if((TestedXportInfo[index].index == IPX) && bIPXHack){
+
+ Status = RetrieveList(DomainInfo.lpMInfo->wcMachineName, L"\\Device\\NwLnkNb",
+ &pvMsServerList, &dwEntriesInList, &dwTotalEntries,
+ SV_TYPE_ALTERNATE_XPORT|SV_TYPE_DOMAIN_ENUM, NULL, NULL, FALSE);
+
+ } else {
+ Status = RetrieveList(DomainInfo.lpMInfo->wcMachineName, TestedXportInfo[index].Transport.Buffer,
+ &pvMsServerList, &dwEntriesInList, &dwTotalEntries,
+ SV_TYPE_DOMAIN_ENUM, NULL, NULL, FALSE);
+ }
+
+ if(Status == NERR_Success){
+ pServerInfo101 = (PSERVER_INFO_101)pvMsServerList;
+
+ //
+ // Check whether the primary domain name is there in the list
+ // retrieved from the Other PDC.
+ //
+ for(i=0; i< (INT)dwEntriesInList &&
+ _wcsicmp(pServerInfo101[i].sv101_name, wcTESTEDDOMAINS[0]) != 0; i++);
+
+ if(i >= (INT)dwEntriesInList ) {
+ sprintf(PrintBuf,"\n\nERROR[ER%ld]: Primary Domain Name not found in the list from %s ",
+ ++ERRCOUNT, UnicodeToPrintfString(DomainInfo.lpMInfo->wcMachineName));
+ PrintString(TOALL, PrintBuf);
+
+ sprintf(PrintBuf,"on Transport %s.\n",
+ UnicodeToPrintfString(TestedXportInfo[index].Transport.Buffer));
+ PrintString(TOALL, PrintBuf);
+ } else {
+
+ sprintf(PrintBuf,"\nSUCCESS[SC%ld]: Primary Domain Name found in the list from %s ",
+ ++SUCSCOUNT, UnicodeToPrintfString(DomainInfo.lpMInfo->wcMachineName));
+ PrintString(TOALL, PrintBuf);
+
+ sprintf(PrintBuf,"on Transport %s.\n",
+ UnicodeToPrintfString(TestedXportInfo[index].Transport.Buffer));
+ PrintString(TOALL, PrintBuf);
+ }
+
+ } else {
+ sprintf(PrintBuf,"\n\nERROR[ER%ld]: Cannot get the Domain List from %s ",
+ ++ERRCOUNT, UnicodeToPrintfString(DomainInfo.lpMInfo->wcMachineName));
+ PrintString(TOALL, PrintBuf);
+
+ sprintf(PrintBuf,"on Transport %s.\n",
+ UnicodeToPrintfString(TestedXportInfo[index].Transport.Buffer));
+ PrintString(TOALL, PrintBuf);
+ }
+
+ NetApiBufferFree(pvMsServerList);
+ }
+
+ }
+
+}
+
+
+NET_API_STATUS
+CreateShutService(LPTSTR wcMachineName)
+{
+
+HANDLE schService;
+HANDLE schSCManager;
+
+
+ schSCManager = OpenSCManager(
+ wcMachineName,
+ NULL,
+ SC_MANAGER_ALL_ACCESS);
+
+ if(schSCManager == (HANDLE)NULL){
+ sprintf(PrintBuf,"\n\nCannot Access the service controller on %s. \nError %s\n",
+ UnicodeToPrintfString(wcMachineName), get_error_text(GetLastError()));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+
+ return(!NERR_Success);
+ }
+
+ schService = CreateService( schSCManager,
+ SHUTSVCNAME,
+ SHUTSVCNAME,
+ SERVICE_ALL_ACCESS,
+ SERVICE_WIN32_OWN_PROCESS,
+ SERVICE_DEMAND_START,
+ SERVICE_ERROR_NORMAL,
+ (LPTSTR)SHUTSVCPATH,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL);
+
+ if(schService == (HANDLE)NULL){
+ if(GetLastError() != ERROR_SERVICE_EXISTS){
+ sprintf(PrintBuf,"\n\nCannot Create service on %s. \nError %s\n",
+ UnicodeToPrintfString(wcMachineName), get_error_text(GetLastError()));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+
+ return(!NERR_Success);
+ }
+ }
+
+ CloseHandle(schSCManager);
+ CloseHandle(schService);
+
+return NERR_Success;
+}
+
+
+VOID
+DeleteShutService(LPTSTR wcMachineName)
+{
+
+HANDLE schService;
+HANDLE schSCManager;
+
+
+ schSCManager = OpenSCManager(
+ wcMachineName,
+ NULL,
+ SC_MANAGER_ALL_ACCESS);
+
+ if(schSCManager == (HANDLE)NULL){
+ sprintf(PrintBuf,"\n\nCannot Access the service controller on %s for deletion. \nError %s\n",
+ UnicodeToPrintfString(wcMachineName), get_error_text(GetLastError()));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+
+ return;
+ }
+
+ schService = OpenService( schSCManager,
+ SHUTSVCNAME,
+ SERVICE_ALL_ACCESS);
+
+ if(schService == (HANDLE)NULL){
+ sprintf(PrintBuf,"\n\nCannot Open Shut service on %s for deletion. \nError %s\n",
+ UnicodeToPrintfString(wcMachineName), get_error_text(GetLastError()));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+
+ return;
+ }
+
+ DeleteService(schService);
+
+ CloseHandle(schSCManager);
+ CloseHandle(schService);
+
+ return;
+}
+
+
+VOID
+DoMulDomMulSubNetTests(XPORTINFO *TestedXportInfo,
+ INT iNumOfTestedXports)
+{
+INT i, iDomIndex;
+TCHAR wcOthPDC[CNLEN+1];
+LPMACHINEINFO lpMachineInfo;
+LPMACHINEINFO lpOtherPDC = NULL;
+DOMAININFO OthSubNetDomInfo;
+DOMAININFO SameSubNetDomInfo;
+BOOL bFoundSameSubNetPDC = FALSE;
+BOOL bFoundOthSubNetPDC = FALSE;
+NET_API_STATUS Status;
+
+
+ sprintf(PrintBuf,"\n\n\n================================================================================\n");
+ PrintString(TOALL, PrintBuf);
+
+ sprintf(PrintBuf,"\n\t\tMULTIPLE DOMAIN TESTS!\n\t\t======================\n\n");
+ PrintString(TOALL, PrintBuf);
+
+ //
+ // We first find out whether the second domain is in the same subnet.
+ // If it is in the same subnet, we expect both the domains to see each other
+ // while each browse master on domain 1 is stopped. If they are on two
+ // subnets, TCP/IP needs the PDC to be up, for them to see each other.
+ for(lpMachineInfo=HeadList1.Next; lpMachineInfo;
+ lpMachineInfo=lpMachineInfo->Next){
+
+ //
+ // If the domain is not the same as any of them found
+ //
+ for(i=0; (i < iNumOfTestedDomains) &&
+
+ _wcsicmp(lpMachineInfo->wcDomainName, wcTESTEDDOMAINS[i]) != 0; i++);
+
+ if(i >= iNumOfTestedDomains){
+ //
+ // This domain is not in the group found.
+ //
+ if(iNumOfTestedDomains < MAXTESTEDDOMAINS){
+
+ wcscpy(wcTESTEDDOMAINS[iNumOfTestedDomains++], lpMachineInfo->wcDomainName);
+
+ } else {
+ sprintf(PrintBuf,"\n\nERROR: Only a maximum of %ld Domains can be specified in the input file.\n",
+ MAXTESTEDDOMAINS);
+ PrintString(TOALL, PrintBuf);
+
+ }
+
+ } // if (i< NumOfTestedDomains)
+
+ } // for lpMachineInfo
+
+
+ if(iNumOfTestedDomains <= 1){
+ sprintf(PrintBuf,"\n\nERROR: There is only one domain specified in the input file!\n");
+ PrintString(TOALL, PrintBuf);
+ sprintf(PrintBuf,"\n\nERROR: Multiple domain tests not done!\n");
+ PrintString(TOALL, PrintBuf);
+ return;
+ }
+
+
+ //
+ // Find the PDC's of the domains and see whether any of them
+ // are on the primary subnet.
+ //
+ for(iDomIndex = 1; iDomIndex < iNumOfTestedDomains; iDomIndex++){
+ i = 0;
+ lstrcpy(wcOthPDC, L"");
+ while((i<iNumOfTestedXports) && (Status = GetNetBiosPdcName(TestedXportInfo[i].Transport.Buffer, wcTESTEDDOMAINS[iDomIndex], wcOthPDC)) != NERR_Success)
+ i++;
+ if(Status == NERR_Success){
+ //
+ // If found the PDC;
+ //
+ for(lpMachineInfo=HeadList1.Next; lpMachineInfo &&
+ _wcsicmp(lpMachineInfo->wcMachineName, wcOthPDC) != 0;
+ lpMachineInfo=lpMachineInfo->Next);
+
+ if(lpMachineInfo) {
+ //
+ // Found PDC in the list.
+ //
+ if(lpMachineInfo->iSubnet == SUBNET1){
+ //
+ // If the machine is in the same subnet
+ //
+ if(!bFoundSameSubNetPDC){
+ wcscpy(SameSubNetDomInfo.wcDomainName, wcTESTEDDOMAINS[iDomIndex]);
+ SameSubNetDomInfo.lpMInfo = lpMachineInfo;
+ bFoundSameSubNetPDC = TRUE;
+ }
+
+ } else { // Other subnet
+
+ if(!bFoundOthSubNetPDC){
+ wcscpy(OthSubNetDomInfo.wcDomainName, wcTESTEDDOMAINS[iDomIndex]);
+ OthSubNetDomInfo.lpMInfo = lpMachineInfo;
+ bFoundOthSubNetPDC = TRUE;
+ }
+ }
+
+ } else {
+ //
+ // Could not find PDC in List
+ //
+ sprintf(PrintBuf,"\n\nERROR: The PDC (%s) of domain", UnicodeToPrintfString(wcTESTEDDOMAINS[iDomIndex]));
+ PrintString(TOALL, PrintBuf);
+ sprintf(PrintBuf," %s is not found in the input file!\n", UnicodeToPrintfString(wcOthPDC));
+ PrintString(TOALL, PrintBuf);
+ sprintf(PrintBuf,"\n\nERROR: Multiple domain tests not done!\n");
+ PrintString(TOALL, PrintBuf);
+ return;
+ }
+
+ } // found PDC
+ }
+
+
+
+ if(!bFoundSameSubNetPDC && !bFoundOthSubNetPDC){
+ //
+ // No PDC's in other domains.
+ //
+ sprintf(PrintBuf,"\n\nERROR[ER%ld]:No PDC's in other domains.\n",++ERRCOUNT);
+ PrintString(TOALL, PrintBuf);
+ sprintf(PrintBuf,"\n\nERROR: Multiple domain tests not done!\n");
+ PrintString(TOALL, PrintBuf);
+ return;
+ }
+
+ if(bFoundSameSubNetPDC){
+ //
+ // There is a PDC in the same subnet
+ //
+ // Test all transports
+
+ MulDomSameSubnetTest(SameSubNetDomInfo, TestedXportInfo, iNumOfTestedXports);
+ }
+
+ //
+ // Start the browser back on all machines
+ //
+ sprintf(PrintBuf,"\nStarting all the Browsers back.\n");
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ if(!CheckBrServiceOnMachinesInList())
+ return;
+
+ sprintf(PrintBuf, "\n\nSleeping for %ld msecs.\n", BASESLEEPTIME*2);
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ Sleep(BASESLEEPTIME*2);
+
+
+ if(bFoundOthSubNetPDC){
+ //
+ // There is a PDC on the other subnet
+ //
+
+ MulDomDiffSubnetTest(OthSubNetDomInfo, TestedXportInfo, iNumOfTestedXports);
+ }
+
+ //
+ // Start the browser back on all machines
+ //
+ sprintf(PrintBuf,"\nStarting all the Browsers back.\n");
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ if(!CheckBrServiceOnMachinesInList())
+ return;
+
+ sprintf(PrintBuf,"\n\n--------------------------------------------------------------------------------\n");
+ PrintString(TOALL, PrintBuf);
+
+}
+
+
+VOID
+FindTheCurrentMasters(LPTSTR wcDomainName,
+ XPORTINFO *TestedXportInfo,
+ INT iNumOfTestedXports,
+ TCHAR wcCurrentMasters[MAXPROTOCOLS][CNLEN+1])
+{
+INT index;
+TCHAR wcNewMaster[CNLEN +1];
+LPMACHINEINFO lpMachineInfo;
+NET_API_STATUS Status;
+
+
+ for(index = 0; index < iNumOfTestedXports; index++){
+
+ //
+ // Find the new Master.
+ //
+ lstrcpy(wcNewMaster, L"");
+ if((Status = GetMasterName(TestedXportInfo, iNumOfTestedXports, index, wcDomainName, wcNewMaster)) != NERR_Success){
+ if(MasterAvailable(TestedXportInfo[index], wcDomainName, SUBNET1)){
+ sprintf(PrintBuf,"\nERROR: Unable to find the new master: Domain: %s ",UnicodeToPrintfString(wcDomainName));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ sprintf(PrintBuf," Transport: %s", UnicodeToPrintfString(TestedXportInfo[index].Transport.Buffer));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ sprintf(PrintBuf," Old Master: %s.\n", UnicodeToPrintfString(wcCurrentMasters[index]));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+
+ } else {
+ sprintf(PrintBuf,"\nOK: Unable to find the new master. No servers running Browser service. Domain: %s ", UnicodeToPrintfString(wcDomainName));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ sprintf(PrintBuf," Transport: %s", UnicodeToPrintfString(TestedXportInfo[index].Transport.Buffer));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ sprintf(PrintBuf," Old Master: %s.\n", UnicodeToPrintfString(wcCurrentMasters[index]));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ }
+
+ wcscpy(wcCurrentMasters[index], L"");
+ continue;
+ }
+
+ sprintf(PrintBuf,"\nNew Master in Primary Domain on transport %s: ", UnicodeToPrintfString(TestedXportInfo[index].Transport.Buffer));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ sprintf(PrintBuf,"%s.\n", UnicodeToPrintfString(wcNewMaster));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+
+ wcscpy(wcCurrentMasters[index], wcNewMaster);
+ }
+}
+
+
+
+BOOL
+MulDomDiffSubnetTest(DOMAININFO OthSubNetDomInfo,
+ XPORTINFO *TestedXportInfo,
+ INT iNumOfTestedXports)
+{
+BOOL Found;
+INT i, index;
+TCHAR wcCurrentMasters[MAXPROTOCOLS][CNLEN+1];
+LPMACHINEINFO lpMachineInfo;
+NET_API_STATUS Status;
+BOOL bTEST_TCP = TRUE;
+
+
+
+ for(i = 0; i< MAXPROTOCOLS; i++) wcscpy(wcCurrentMasters[i], L"");
+
+ //
+ // Create the service on the remote computer
+ //
+ sprintf(PrintBuf,"\n\nTEST#: [TS%ld].\n=============", ++TESTCOUNT);
+ PrintString(TOALL, PrintBuf);
+
+ sprintf(PrintBuf,"\n\nTEST: Multiple domain tests on different subnet.\n");
+ PrintString(TOALL, PrintBuf);
+
+
+ Status = CreateShutService(OthSubNetDomInfo.lpMInfo->wcMachineName);
+
+ if(Status == NERR_Success){
+
+ //
+ // Primary PDC is running. Check whether the other domain has
+ // this domain info.
+ //
+ sprintf(PrintBuf,"\n\nCheck if Other domain has Primary domain info, with Primary PDC Browser running.\n");
+ PrintString(TOALL, PrintBuf);
+
+ //
+ // Find Current masters of the primary domain.
+ //
+ FindTheCurrentMasters(wcTESTEDDOMAINS[0], TestedXportInfo, iNumOfTestedXports, wcCurrentMasters);
+
+ //
+ // Find if there are any masters at all.
+ //
+ for(i = 0; (i < iNumOfTestedXports) &&
+ (_wcsicmp(wcCurrentMasters[i], L"") == 0); i++);
+
+ if(i < iNumOfTestedXports){
+ //
+ // There is a master available in the Primary domain.
+
+ // Check primary and other domain for each others info.
+ //
+
+ TestPrimAndOthDoms(OthSubNetDomInfo, TestedXportInfo, iNumOfTestedXports, wcCurrentMasters, bTEST_TCP);
+ }
+
+ //
+ // Without the PDC TCP/IP won't work
+ //
+ bTEST_TCP = FALSE;
+
+ //
+ // If there is only TCP then we are not testing it by shutting down
+ // each machines
+ //
+ if(!((iNumOfTestedXports == 1) && (TestedXportInfo[0].index == TCP))) {
+
+ //
+ // Shutdown each of the local masters and find if the
+ // other PDC has the local domain info.
+ //
+ do{
+ Found = FALSE;
+
+ //
+ // Find whether any of the current masters are NT machines
+ //
+ for(index = 0; ((index < iNumOfTestedXports) && !Found); ){
+
+ if(_wcsicmp(wcCurrentMasters[index], L"") != 0){
+ for(lpMachineInfo = lpDomStart; lpMachineInfo && _wcsicmp(wcCurrentMasters[index],
+ lpMachineInfo->wcMachineName) != 0; lpMachineInfo= lpMachineInfo->Next);
+
+ if(lpMachineInfo)
+ Found = (IsNTMachine(lpMachineInfo) && lpMachineInfo->BrowserServiceStarted);
+ }
+
+ if(!Found)
+ index++;
+ }
+
+ if(Found){
+ //
+ // A master was found, stop it and then "stop and start" the
+ // RDR, Browser on other PDC and check whether, it gets this domain info.
+ //
+ sprintf(PrintBuf,"\n\n--------------------------------------------------------------------------------\n");
+ PrintString(TOALL, PrintBuf);
+ sprintf(PrintBuf,"\nTEST: Stop Browser on Master %s in Primary domain and check if \nthe other domain gets this info.\n",
+ UnicodeToPrintfString(wcCurrentMasters[index]));
+ PrintString(TOALL, PrintBuf);
+
+ if((Status = StopBrowserService(wcCurrentMasters[index])) != NERR_Success){
+ sprintf(PrintBuf,"\nERROR[%ld]:Could not stop browser on %s.\n",
+ ++ERRCOUNT, UnicodeToPrintfString(wcCurrentMasters[index]));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ return FALSE;
+ }
+
+ lpMachineInfo->BrowserServiceStarted = FALSE;
+
+ //
+ // Sleep for sometime
+ //
+ sprintf(PrintBuf, "\nSleeping for %ld msecs\n", BASESLEEPTIME * 2);
+ PrintString(TOSCREENANDLOG, PrintBuf);
+
+ Sleep(BASESLEEPTIME*2);
+
+ //
+ // For each of the tested transports find who is the Master
+ //
+ FindTheCurrentMasters(wcTESTEDDOMAINS[0], TestedXportInfo, iNumOfTestedXports, wcCurrentMasters);
+
+ //
+ // Find if there are any masters at all.
+ //
+ for(i = 0; (i < iNumOfTestedXports) &&
+ (_wcsicmp(wcCurrentMasters[i], L"") == 0); i++);
+
+ if(i < iNumOfTestedXports){
+ //
+ // There is a master available in the Primary domain.
+
+ // Check primary and other domain for each others info.
+ //
+
+ TestPrimAndOthDoms(OthSubNetDomInfo, TestedXportInfo, iNumOfTestedXports, wcCurrentMasters, bTEST_TCP);
+ }
+
+ }
+
+ }while(Found);
+
+ } // If not just TCP alone.
+
+ //
+ // This part is specifically for the TCP/IP protocol.
+ // Start Browser on just the two PDC's and see if the
+ // other PDC has info of the primary domain.
+ //
+ for(index = 0; (index < iNumOfTestedXports) &&
+ TestedXportInfo[index].index != TCP ; index++);
+ //
+ // If TCP is a tested Transport
+ //
+ if(index < iNumOfTestedXports) {
+
+ if(LocDomInfo.lpMInfo->Protocols[TCP] && OthSubNetDomInfo.lpMInfo->Protocols[TCP]){
+ //
+ // Stop Browser on all machines in the primary domain.
+ // Start browser on the primary PDC. Stop and start RDR on the Other PDC.
+ // Then check the info on each others domains.
+
+ sprintf(PrintBuf,"\n\n--------------------------------------------------------------------------------\n");
+ PrintString(TOALL, PrintBuf);
+
+ sprintf(PrintBuf, "\nTEST: Check two isolated domains on TCP/IP across the subnets.\n");
+ PrintString(TOALL, PrintBuf);
+
+
+ for(lpMachineInfo = lpDomStart; lpMachineInfo; lpMachineInfo = lpMachineInfo->Next){
+ if(IsNTMachine(lpMachineInfo) && lpMachineInfo->BrowserServiceStarted){
+ if(StopBrowserService(lpMachineInfo->wcMachineName) == NERR_Success)
+ lpMachineInfo->BrowserServiceStarted = FALSE;
+ else {
+ sprintf(PrintBuf,"\nCould not stop browser on %s.\n",
+ UnicodeToPrintfString(lpMachineInfo->wcMachineName));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ }
+ }
+ }
+
+ if((Status = StartBrowserService(LocDomInfo.lpMInfo->wcMachineName)) != NERR_Success){
+ sprintf(PrintBuf,"\nERROR:Starting Browser on %s: \nError: %s\n", UnicodeToPrintfString(LocDomInfo.lpMInfo->wcMachineName), get_error_text(Status));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ } else {
+ LocDomInfo.lpMInfo->BrowserServiceStarted = TRUE;
+ }
+
+ if((Status = StartBrowserService(OthSubNetDomInfo.lpMInfo->wcMachineName)) != NERR_Success){
+ sprintf(PrintBuf,"\nERROR:Starting Browser on %s. \nError: %s\n", UnicodeToPrintfString(OthSubNetDomInfo.lpMInfo->wcMachineName), get_error_text(Status));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ } else {
+ OthSubNetDomInfo.lpMInfo->BrowserServiceStarted = TRUE;
+ }
+
+ sprintf(PrintBuf, "\nSleeping for %ld msecs.\n", BASESLEEPTIME*2);
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ Sleep(BASESLEEPTIME*2);
+
+ //
+ // Note: Special case!
+ //
+ // Only the master for TCP is found and it is obtained in
+ // index = 0.
+
+ wcscpy(wcCurrentMasters[0], L"");
+ FindTheCurrentMasters(wcTESTEDDOMAINS[0], &TestedXportInfo[index], 1, wcCurrentMasters);
+
+ if(_wcsicmp(wcCurrentMasters[0], L"") != 0){
+ //
+ // There is a master available in the Primary domain.
+
+ // Check primary and other domain for each others info.
+ //
+
+ TestPrimAndOthDoms(OthSubNetDomInfo, &TestedXportInfo[index], 1, wcCurrentMasters, TRUE);
+
+ } else {
+ sprintf(PrintBuf, "\nERROR[ER%ld]: There is no master in Primary Domain.\n", ++ERRCOUNT);
+ PrintString(TOALL, PrintBuf);
+ }
+
+ }
+ }
+
+
+ DeleteShutService(OthSubNetDomInfo.lpMInfo->wcMachineName);
+
+ } else {
+ sprintf(PrintBuf,"\n\nERROR[ER%ld]: Could not Create Shut Service on %s.\n", ++ERRCOUNT,UnicodeToPrintfString(OthSubNetDomInfo.lpMInfo->wcMachineName));
+ PrintString(TOALL, PrintBuf);
+ sprintf(PrintBuf,"\n\nERROR: Multiple domain tests not done on different Subnets!\n");
+ PrintString(TOALL, PrintBuf);
+ return FALSE;
+ }
+
+ sprintf(PrintBuf,"\nSUCCESS[SC%ld]: Multiple domain tests on different subnets.\n", ++SUCSCOUNT);
+ PrintString(TOALL, PrintBuf);
+
+ return TRUE;
+}
+
+
+
+BOOL
+MulDomSameSubnetTest(DOMAININFO SameSubNetDomInfo,
+ XPORTINFO *TestedXportInfo,
+ INT iNumOfTestedXports)
+{
+BOOL Found;
+INT i, index;
+TCHAR wcCurrentMasters[MAXPROTOCOLS][CNLEN+1];
+LPMACHINEINFO lpMachineInfo;
+NET_API_STATUS Status;
+BOOL bTEST_TCP = TRUE;
+
+ for(i = 0; i< MAXPROTOCOLS; i++) wcscpy(wcCurrentMasters[i], L"");
+
+ //
+ // Create the service on the remote computer
+ //
+ sprintf(PrintBuf,"\n\nTEST#: [TS%ld].\n=============", ++TESTCOUNT);
+ PrintString(TOALL, PrintBuf);
+
+ sprintf(PrintBuf,"\n\nTEST: Multiple domain tests on same subnet.\n");
+ PrintString(TOALL, PrintBuf);
+
+
+ Status = CreateShutService(SameSubNetDomInfo.lpMInfo->wcMachineName);
+
+ if(Status == NERR_Success){
+
+ //
+ // Primary PDC is running. Check whether the other domain has
+ // this domain info.
+ //
+ sprintf(PrintBuf,"\n\nCheck if Other domain has Primary domain info, with Primary PDC Browser running.\n");
+ PrintString(TOALL, PrintBuf);
+
+ //
+ // Find Current masters of the primary domain.
+ //
+ FindTheCurrentMasters(wcTESTEDDOMAINS[0], TestedXportInfo, iNumOfTestedXports, wcCurrentMasters);
+
+ //
+ // Find if there are any masters at all.
+ //
+ for(i = 0; (i < iNumOfTestedXports) &&
+ (_wcsicmp(wcCurrentMasters[i], L"") == 0); i++);
+
+ if(i < iNumOfTestedXports){
+ //
+ // There is a master available in the Primary domain.
+
+ // Check primary and other domain for each others info.
+ //
+
+ TestPrimAndOthDoms(SameSubNetDomInfo, TestedXportInfo, iNumOfTestedXports, wcCurrentMasters, bTEST_TCP);
+ }
+
+ //
+ // Shutdown each of the local masters and find if the
+ // other PDC has the local domain info.
+ //
+ do{
+ Found = FALSE;
+
+ //
+ // Find whether any of the current masters are NT machines
+ //
+ for(index = 0; ((index < iNumOfTestedXports) && !Found); ){
+
+ if(_wcsicmp(wcCurrentMasters[index], L"") != 0){
+ for(lpMachineInfo = lpDomStart; lpMachineInfo && _wcsicmp(wcCurrentMasters[index],
+ lpMachineInfo->wcMachineName) != 0; lpMachineInfo= lpMachineInfo->Next);
+
+ if(lpMachineInfo)
+ Found = (IsNTMachine(lpMachineInfo) && lpMachineInfo->BrowserServiceStarted);
+ }
+
+ if(!Found)
+ index++;
+ }
+
+ if(Found){
+ //
+ // A master was found, stop it and then "stop and start" the
+ // RDR, Browser on other PDC and check whether, it gets this domain info.
+ //
+ sprintf(PrintBuf,"\n\n--------------------------------------------------------------------------------\n");
+ PrintString(TOALL, PrintBuf);
+ sprintf(PrintBuf,"\nTEST: Stop Browser on Master %s in Primary domain and check if \nthe other domain gets this info\n",
+ UnicodeToPrintfString(wcCurrentMasters[index]));
+ PrintString(TOALL, PrintBuf);
+
+ if((Status = StopBrowserService(wcCurrentMasters[index])) != NERR_Success){
+ sprintf(PrintBuf,"\nERROR[%ld]:Could not stop browser on %s.\n",
+ ++ERRCOUNT, UnicodeToPrintfString(wcCurrentMasters[index]));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ return FALSE;
+ }
+
+ lpMachineInfo->BrowserServiceStarted = FALSE;
+
+ //
+ // Sleep for sometime
+ //
+ sprintf(PrintBuf, "\nSleeping for %ld msecs\n", BASESLEEPTIME * 2);
+ PrintString(TOSCREENANDLOG, PrintBuf);
+
+ Sleep(BASESLEEPTIME*2);
+
+ //
+ // For each of the tested transports find who is the Master
+ //
+ FindTheCurrentMasters(wcTESTEDDOMAINS[0], TestedXportInfo, iNumOfTestedXports, wcCurrentMasters);
+
+ //
+ // Find if there are any masters at all.
+ //
+ for(i = 0; (i < iNumOfTestedXports) &&
+ (_wcsicmp(wcCurrentMasters[i], L"") == 0); i++);
+
+ if(i < iNumOfTestedXports){
+ //
+ // There is a master available in the Primary domain.
+
+ // Check primary and other domain for each others info.
+ //
+
+ TestPrimAndOthDoms(SameSubNetDomInfo, TestedXportInfo, iNumOfTestedXports, wcCurrentMasters, bTEST_TCP);
+ }
+ }
+
+ }while(Found);
+
+
+ DeleteShutService(SameSubNetDomInfo.lpMInfo->wcMachineName);
+
+ } else {
+ sprintf(PrintBuf,"\n\nERROR[ER%ld]: Could not Create Shut Service on %s.\n", ++ERRCOUNT,UnicodeToPrintfString(SameSubNetDomInfo.lpMInfo->wcMachineName));
+ PrintString(TOALL, PrintBuf);
+ sprintf(PrintBuf,"\n\nERROR: Multiple domain tests not done on Same Subnet!\n");
+ PrintString(TOALL, PrintBuf);
+ return FALSE;
+ }
+
+ sprintf(PrintBuf,"\nSUCCESS[SC%ld]: Multiple domain tests on same subnet.\n", ++SUCSCOUNT);
+ PrintString(TOALL, PrintBuf);
+
+ return TRUE;
+}
+
+
+NET_API_STATUS
+StartShutService(LPTSTR wcMachineName,
+ DWORD dwArgument)
+
+{
+HANDLE schService;
+HANDLE schSCManager;
+DWORD LastError;
+CHAR cArgument[5];
+TCHAR wcArgument[5];
+LPTSTR lpszPtrStr[1];
+SERVICE_STATUS ssServiceStatus;
+
+
+ schSCManager = OpenSCManager(
+ wcMachineName,
+ NULL,
+ SC_MANAGER_ALL_ACCESS);
+
+ if(schSCManager == (HANDLE)NULL){
+ sprintf(PrintBuf,"\n\nCannot Access the service controller on %s for Starting. \nError %s\n",
+ UnicodeToPrintfString(wcMachineName), get_error_text(GetLastError()));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+
+ return (!NERR_Success);
+ }
+
+
+ schService = OpenService(schSCManager, SHUTSVCNAME, SERVICE_ALL_ACCESS);
+ if(schService == (HANDLE)NULL){
+ sprintf(PrintBuf,"\n\nCannot Open Shut service on %s for Starting. \nError %s\n",
+ UnicodeToPrintfString(wcMachineName), get_error_text(GetLastError()));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ CloseHandle(schSCManager);
+ return (!NERR_Success);
+ }
+
+ sprintf(cArgument, "%ld", dwArgument);
+ MultiByteToWideChar(CP_OEMCP, MB_PRECOMPOSED, cArgument, strlen(cArgument)+1, wcArgument, sizeof(wcArgument));
+ lpszPtrStr[0] = wcArgument;
+
+ if(!StartService(schService,
+ 1,
+ lpszPtrStr)){
+
+ LastError = GetLastError();
+ if(!(LastError == NERR_ServiceInstalled || LastError == ERROR_SERVICE_ALREADY_RUNNING)){
+ sprintf(PrintBuf,"\n\nCannot Start Shut service on %s. \nError %s\n",
+ UnicodeToPrintfString(wcMachineName), get_error_text(GetLastError()));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+
+ CloseHandle(schSCManager);
+ CloseHandle(schService);
+ return (!NERR_Success);
+ } else {
+ //
+ // Service is started, stop it and restart it.
+ //
+ if(!ControlService(schService,
+ SERVICE_CONTROL_STOP,
+ &ssServiceStatus)){
+ sprintf(PrintBuf,"\n\nCannot Stop Shut service before starting on %s. \nError %s\n",
+ UnicodeToPrintfString(wcMachineName), get_error_text(GetLastError()));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+
+ CloseHandle(schSCManager);
+ CloseHandle(schService);
+ return (!NERR_Success);
+ }
+
+ //
+ // Wait for the SVCDataBase to be updated
+ //
+ Sleep(15000);
+
+ if(!StartService(schService,
+ 1,
+ lpszPtrStr)){
+
+ sprintf(PrintBuf,"\n\nCannot Start Shut service after Stopping on %s. \nError %s\n",
+ UnicodeToPrintfString(wcMachineName), get_error_text(GetLastError()));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+
+ CloseHandle(schSCManager);
+ CloseHandle(schService);
+ return (!NERR_Success);
+ }
+ }
+ }
+
+ CloseHandle(schSCManager);
+ CloseHandle(schService);
+
+ return (NERR_Success);
+}
+
+
+NET_API_STATUS
+StopShutService(LPTSTR wcMachineName)
+{
+HANDLE schService;
+HANDLE schSCManager;
+SERVICE_STATUS ssServiceStatus;
+
+ schSCManager = OpenSCManager(
+ wcMachineName,
+ NULL,
+ SC_MANAGER_ALL_ACCESS);
+
+ if(schSCManager == (HANDLE)NULL){
+ sprintf(PrintBuf,"\n\nCannot Access the service controller on %s for Stopping. \nError %s\n",
+ UnicodeToPrintfString(wcMachineName), get_error_text(GetLastError()));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+
+ return (!NERR_Success);
+ }
+
+
+ schService = OpenService(schSCManager, SHUTSVCNAME, SERVICE_ALL_ACCESS);
+ if(schService == (HANDLE)NULL){
+ sprintf(PrintBuf,"\n\nCannot Open Shut service on %s for Stopping. \nError %s\n",
+ UnicodeToPrintfString(wcMachineName), get_error_text(GetLastError()));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+
+ return (!NERR_Success);
+ }
+
+ if(!ControlService(schService,
+ SERVICE_CONTROL_STOP,
+ &ssServiceStatus)){
+ if(GetLastError() != ERROR_SERVICE_NOT_ACTIVE){
+ sprintf(PrintBuf,"\n\nCannot Stop Shut service on %s. \nError %s\n",
+ UnicodeToPrintfString(wcMachineName), get_error_text(GetLastError()));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+
+ return (!NERR_Success);
+ }
+ }
+
+
+ CloseHandle(schSCManager);
+ CloseHandle(schService);
+
+ return (NERR_Success);
+}
+
+
+VOID
+TestPrimAndOthDoms(DOMAININFO DomainInfo,
+ XPORTINFO *TestedXportInfo,
+ INT iNumOfTestedXports,
+ TCHAR wcCurrentMasters[MAXPROTOCOLS][CNLEN+1],
+ BOOL bTEST_TCP)
+{
+ sprintf(PrintBuf, "\n\nShut down and restart RDR, BROWSER etc on %s\n", UnicodeToPrintfString(DomainInfo.lpMInfo->wcMachineName));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+
+
+ if(StartShutService(DomainInfo.lpMInfo->wcMachineName, STOPANDSTARTRDR) != NERR_Success){
+ sprintf(PrintBuf,"\n\nERROR[ER%ld]: Could not Start Shut Service on %s.\n",
+ ++ERRCOUNT,UnicodeToPrintfString(DomainInfo.lpMInfo->wcMachineName));
+ PrintString(TOALL, PrintBuf);
+ sprintf(PrintBuf,"\n\nERROR: Multiple domain tests not done on Same Subnet!\n");
+ PrintString(TOALL, PrintBuf);
+ return;
+ }
+
+ //
+ // Sleep for 4 minutes so that the Stopped Server is back.
+ // RDR and browser will come up in 2 minutes, Allow time
+ // for the browser to be updated.
+ //
+ sprintf(PrintBuf, "\n\nSleeping for %ld msecs.\n", BASESLEEPTIME*8);
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ Sleep(BASESLEEPTIME * 8);
+
+ //
+ // Check for the other domain info on current masters of Prim domain.
+ //
+ CheckSrvListOnPrimaryDom(DomainInfo, TestedXportInfo, iNumOfTestedXports, wcCurrentMasters, bTEST_TCP);
+
+ //
+ // Force announce or wait for the primary domain to announce
+ // itself.
+ //
+ if(!bForceAnn) {
+ sprintf(PrintBuf, "\nSleeping for %ld msecs.\n", UPDATESLEEPTIME);
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ Sleep(UPDATESLEEPTIME);
+
+ } else {
+ //
+ // Ask the machines in the domain to announce themselves and
+ // sleep for sometime so the elections and things happen
+ //
+ ForceAnnounce(TestedXportInfo[0].Transport, wcTESTEDDOMAINS[0]);
+
+ sprintf(PrintBuf, "\nForceAnnounce. Sleeping for %ld msecs.\n", BASESLEEPTIME*2);
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ Sleep(BASESLEEPTIME*2);
+ }
+
+
+ //
+ // Check the list on other domain for the primary domain info.
+ //
+ CheckSrvListOnOtherDom(DomainInfo, TestedXportInfo, iNumOfTestedXports, wcCurrentMasters, bTEST_TCP);
+
+
+ StopShutService(DomainInfo.lpMInfo->wcMachineName);
+
+}
diff --git a/private/net/svcdlls/browser2/browtest/browmdom.h b/private/net/svcdlls/browser2/browtest/browmdom.h
new file mode 100644
index 000000000..0cd884752
--- /dev/null
+++ b/private/net/svcdlls/browser2/browtest/browmdom.h
@@ -0,0 +1,24 @@
+#include "browfunc.h"
+#include "browutil.h"
+#include "browtest.h"
+
+
+
+VOID CheckSrvListOnPrimaryDom(DOMAININFO, XPORTINFO *, INT,
+ TCHAR [MAXPROTOCOLS][CNLEN+1], BOOL);
+VOID CheckSrvListOnOtherDom(DOMAININFO, XPORTINFO *, INT,
+ TCHAR [MAXPROTOCOLS][CNLEN+1], BOOL);
+NET_API_STATUS CreateShutService(LPTSTR);
+VOID DeleteShutService(LPTSTR);
+VOID DoMulDomMulSubNetTests(XPORTINFO *, INT);
+VOID FindTheCurrentMasters(LPTSTR, XPORTINFO *,
+ INT, TCHAR [MAXPROTOCOLS][CNLEN+1]);
+
+BOOL MulDomSameSubnetTest(DOMAININFO, XPORTINFO *, INT);
+BOOL MulDomDiffSubnetTest(DOMAININFO, XPORTINFO *, INT);
+NET_API_STATUS StartShutService(LPTSTR, DWORD);
+NET_API_STATUS StopShutService(LPTSTR);
+VOID TestPrimAndOthDoms(DOMAININFO, XPORTINFO *, INT,
+ TCHAR [MAXPROTOCOLS][CNLEN+1], BOOL);
+
+
diff --git a/private/net/svcdlls/browser2/browtest/browstrs.c b/private/net/svcdlls/browser2/browtest/browstrs.c
new file mode 100644
index 000000000..93e543de9
--- /dev/null
+++ b/private/net/svcdlls/browser2/browtest/browstrs.c
@@ -0,0 +1,534 @@
+#include "browstrs.h"
+
+
+extern CHAR PrintBuf[1024];
+extern MACHINEINFO HeadList1;
+
+extern TCHAR wcTESTEDDOMAINS[MAXTESTEDDOMAINS][DNLEN+1];
+extern INT iNumOfTestedDomains;
+extern FILE *fplog;
+extern FILE *fpsum;
+
+extern HANDLE ConsoleMutex;
+extern TCHAR *TRANSPORTS[MAXPROTOCOLS];
+extern enum E_PROTOCOLS {IPX, NBIPX, TCP, XNS, NETBEUI};
+
+extern BOOL bIPXHack;
+
+extern DWORD dwStressTest;
+
+ /*************************************************\
+ * *
+ * Note: Don't use UnicodeToPrintfString() in the *
+ * threads!! *
+ * *
+ \*************************************************/
+
+//
+// This is the main function for the browser stress test.
+// This test creates two threads. One thread stops the browser service on
+// some machine in the list. Other thread finds the master browser, backup
+// browser and try to retrieve lists from it. The main threads sleeps for some
+// time and wakesup and kills the threads. Then it starts back all the browsers.
+//
+BOOL
+StartBrowserStressTests(UNICODE_STRING *TransportNames, INT iNumOfTransports)
+{
+INT i, j;
+DWORD dwThreadId;
+HANDLE hStrStopThread;
+HANDLE hListVwThread;
+THREADDATA ThreadData;
+LPMACHINEINFO lpMachineInfo;
+XPORTINFO *Xports;
+
+
+ srand((unsigned)time(NULL));
+
+ if((Xports = (XPORTINFO *)calloc(iNumOfTransports, sizeof(XPORTINFO))) == NULL){
+ sprintf(PrintBuf,"\n\nCould not allocate space for transports.\n");
+ PrintString(TOALL, PrintBuf);
+ return FALSE;
+ }
+
+ for(i=0; i< iNumOfTransports; i++){
+ Xports[i].Transport = TransportNames[i];
+ }
+
+ //
+ //Find the index of the transport;
+ //
+ for(i=0; i< iNumOfTransports; i++){
+ for(j=0; j < MAXPROTOCOLS
+ && wcsstr(Xports[i].Transport.Buffer, TRANSPORTS[j]) == NULL; j++);
+ if(j >= MAXPROTOCOLS){
+ sprintf(PrintBuf,"\n\nUnknown transport %s!\n",
+ UnicodeToPrintfString(Xports[i].Transport.Buffer));
+ PrintString(TOALL, PrintBuf);
+ return FALSE;
+ }
+ Xports[i].index = j;
+ }
+
+
+ sprintf(PrintBuf,"\n\nTested Transports are:\n");
+ PrintString(TOALL, PrintBuf);
+ for(i=0; i< iNumOfTransports; i++){
+ sprintf(PrintBuf,"Tested Transports[%d] = %-25s Index=%d\n", i+1,
+ UnicodeToPrintfString(Xports[i].Transport.Buffer), Xports[i].index);
+ PrintString(TOALL, PrintBuf);
+ }
+
+
+ //
+ // Check whether the browser service has been started on all NT machines
+ //
+ if(!CheckBrServiceOnMachinesInList())
+ return FALSE;
+
+ //
+ // Sleeping for some time
+ //
+ sprintf(PrintBuf, "\nSleeping for %ld msecs.\n", BASESLEEPTIME*2);
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ Sleep(BASESLEEPTIME*2);
+
+ ThreadData.Xports = Xports;
+ ThreadData.iNumOfTransports = iNumOfTransports;
+
+ //
+ // find all tested domains
+ //
+ for(lpMachineInfo=HeadList1.Next; lpMachineInfo;
+ lpMachineInfo=lpMachineInfo->Next){
+
+ //
+ // If the domain is not the same as any of them found
+ //
+ for(i=0; (i < iNumOfTestedDomains) &&
+ _wcsicmp(lpMachineInfo->wcDomainName, wcTESTEDDOMAINS[i]) != 0; i++);
+
+ if(i >= iNumOfTestedDomains){
+ //
+ // This domain is not in the group found.
+ //
+ if(iNumOfTestedDomains < MAXTESTEDDOMAINS){
+
+ wcscpy(wcTESTEDDOMAINS[iNumOfTestedDomains++], lpMachineInfo->wcDomainName);
+
+ } else {
+ sprintf(PrintBuf,"\n\nERROR: Only a maximum of %ld Domains can be specified in the input file.\n",
+ MAXTESTEDDOMAINS);
+ PrintString(TOALL, PrintBuf);
+ }
+
+ } // if (i< NumOfTestedDomains)
+
+ } // for lpMachineInfo
+
+
+ //
+ // Create the thread that does the starting and stopping of browser.
+ //
+ if((hStrStopThread = CreateThread(NULL,
+ 0,
+ (LPTHREAD_START_ROUTINE)StopStartThread,
+ &ThreadData,
+ 0,
+ &dwThreadId)) == NULL){
+
+ sprintf(PrintBuf,"\n\nERROR: Create Thread! Start Stop Thread.\nError: %s\n"
+ , get_error_text(GetLastError()));
+ PrintString(TOALL, PrintBuf);
+ free(Xports);
+ return FALSE;
+ }
+
+ //
+ // Create the thread that retrieves lists from other machines, forces
+ // elections, find master etc.
+ //
+ if((hListVwThread = CreateThread(NULL,
+ 0,
+ (LPTHREAD_START_ROUTINE)ListViewThread,
+ &ThreadData,
+ 0,
+ &dwThreadId)) == NULL){
+
+ sprintf(PrintBuf,"\n\nERROR: Create Thread! List viewer Thread. \nError: %s\n",
+ get_error_text(GetLastError()));
+ PrintString(TOALL, PrintBuf);
+ free(Xports);
+ return FALSE;
+ }
+
+ //
+ // Sleep for the time specified in command line
+ //
+ dwStartTime = GetTickCount();
+ sprintf(PrintBuf, "\nMain Thread:Sleeping for %ld Minutes.\n", dwStressTest);
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ Sleep(dwStressTest*60*1000);
+
+ //
+ // Terminate Thread for Starting and stopping browser.
+ //
+ if(!TerminateThread(hStrStopThread, ERROR_SUCCESS)){
+ sprintf(PrintBuf,"\n\nERROR: Terminate Thread! Start Stop Thread. \nError: %s\n",
+ get_error_text(GetLastError()));
+ PrintString(TOALL, PrintBuf);
+ free(Xports);
+ return FALSE;
+ }
+
+ //
+ // Terminate Thread for viewing browse list.
+ //
+ if(!TerminateThread(hListVwThread, ERROR_SUCCESS)){
+ sprintf(PrintBuf,"\n\nERROR: Terminate Thread! Start Stop Thread. \nError: %s\n",
+ get_error_text(GetLastError()));
+ PrintString(TOALL, PrintBuf);
+ free(Xports);
+ return FALSE;
+ }
+
+
+ sprintf(PrintBuf, "\nMain Thread: Stress test completed successfully! Time %ld Minutes\n", dwStressTest);
+ PrintString(TOALL, PrintBuf);
+
+ sprintf(PrintBuf, "\nSleeping for %ld msecs.\n", BASESLEEPTIME);
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ Sleep(BASESLEEPTIME);
+
+ //
+ // Start all the browser back.
+ //
+ for(lpMachineInfo = HeadList1.Next; lpMachineInfo; lpMachineInfo = lpMachineInfo->Next)
+ StartBrowserService(lpMachineInfo->wcMachineName);
+
+
+ //
+ // Free the transport Name Buffer
+ //
+ for(i=0; i< iNumOfTransports; i++) free(TransportNames[i].Buffer);
+
+ CloseHandle(hStrStopThread);
+ CloseHandle(hListVwThread);
+ free(Xports);
+ return TRUE;
+}
+
+
+//
+// This routine is executed by a thread which randomly, finnds the MBR, BBR
+// on some transport and retrieve the lists from them. It forces election
+// randomly.
+//
+VOID
+ListViewThread(THREADDATA *ThreadData)
+{
+INT iXportIndex;
+INT iDomIndex;
+INT iNumOfTransports;
+INT iPrintCount = 1;
+DWORD dwEntriesInList;
+DWORD dwTotalEntries;
+DWORD dwCurrentTime;
+PVOID pvServerList;
+TCHAR *pwcDomain;
+STATS stSuccs;
+STATS stFails;
+NET_API_STATUS Status;
+XPORTINFO *Xports;
+
+ memset(&stSuccs, 0, sizeof(STATS));
+ memset(&stFails, 0, sizeof(STATS));
+
+
+ Xports = ThreadData->Xports;
+ iNumOfTransports = ThreadData->iNumOfTransports;
+
+ do{
+ iXportIndex = rand() % iNumOfTransports;
+ iDomIndex = rand() % iNumOfTestedDomains;
+
+ pwcDomain = wcTESTEDDOMAINS[iDomIndex];
+
+
+ //
+ // Find Master and retrieve list from it.
+ //
+ {
+ TCHAR wcMasterName[CNLEN+1];
+
+ //
+ // Find the master on a protocol
+ //
+ if((Status = GetMasterName(Xports, iNumOfTransports,
+ iXportIndex, pwcDomain, wcMasterName)) == NERR_Success){
+
+ stSuccs.dwGM++;
+
+ //
+ // Retrieve the Server list from it.
+ //
+ if((Xports[iXportIndex].index == IPX) && bIPXHack){
+
+ Status = RetrieveList(wcMasterName, L"\\Device\\NwLnkNb",
+ &pvServerList, &dwEntriesInList, &dwTotalEntries,
+ SV_TYPE_ALTERNATE_XPORT, NULL, NULL, FALSE);
+ } else {
+ Status = RetrieveList(wcMasterName, Xports[iXportIndex].Transport.Buffer,
+ &pvServerList, &dwEntriesInList, &dwTotalEntries,
+ SV_TYPE_ALL, NULL, NULL, FALSE);
+ }
+
+ if(Status)
+ stSuccs.dwSrvMs++;
+ else
+ stFails.dwSrvMs++;
+
+ NetApiBufferFree(pvServerList);
+
+ //
+ // Retrieve the domain list
+ //
+ if((Xports[iXportIndex].index == IPX) && bIPXHack){
+
+ Status = RetrieveList(wcMasterName, L"\\Device\\NwLnkNb",
+ &pvServerList, &dwEntriesInList, &dwTotalEntries,
+ SV_TYPE_DOMAIN_ENUM | SV_TYPE_ALTERNATE_XPORT, NULL, NULL, FALSE);
+ } else {
+ Status = RetrieveList(wcMasterName, Xports[iXportIndex].Transport.Buffer,
+ &pvServerList, &dwEntriesInList, &dwTotalEntries,
+ SV_TYPE_DOMAIN_ENUM | SV_TYPE_ALL, NULL, NULL, FALSE);
+ }
+
+ if(Status)
+ stSuccs.dwDomMs++;
+ else
+ stFails.dwDomMs++;
+
+ NetApiBufferFree(pvServerList);
+
+ } else {
+ stFails.dwGM++;
+ }
+
+
+ } // End of Get master
+
+ //
+ // Sleep for some time
+ //
+ Sleep(rand() % BASESLEEPTIME);
+
+
+ //
+ // Find the backUp list
+ //
+ {
+ INT i;
+ TCHAR wcBackUpBrowsers[MAXBACKUPS][CNSLASHLEN+1];
+ ULONG ulNumBackUps;
+
+ if((Status = GetBList(Xports[iXportIndex].Transport, pwcDomain, TRUE,
+ &ulNumBackUps, wcBackUpBrowsers)) == NERR_Success) {
+
+ stSuccs.dwGB++;
+
+ //
+ // Retrieve the server list from one backup browser
+ //
+ i = rand() % ulNumBackUps;
+
+ if((Xports[iXportIndex].index == IPX) && bIPXHack){
+
+ Status = RetrieveList(wcBackUpBrowsers[i], L"\\Device\\NwLnkNb",
+ &pvServerList, &dwEntriesInList, &dwTotalEntries,
+ SV_TYPE_ALTERNATE_XPORT, NULL, NULL, FALSE);
+ } else {
+ Status = RetrieveList(wcBackUpBrowsers[i], Xports[iXportIndex].Transport.Buffer,
+ &pvServerList, &dwEntriesInList, &dwTotalEntries,
+ SV_TYPE_ALL, NULL, NULL, FALSE);
+ }
+
+ if(Status)
+ stSuccs.dwSrvBk++;
+ else
+ stFails.dwSrvBk++;
+
+ NetApiBufferFree(pvServerList);
+
+ //
+ // Retrieve the domain list from another backup browser
+ //
+ i = rand() % ulNumBackUps;
+
+ if((Xports[iXportIndex].index == IPX) && bIPXHack){
+
+ Status = RetrieveList(wcBackUpBrowsers[i], L"\\Device\\NwLnkNb",
+ &pvServerList, &dwEntriesInList, &dwTotalEntries,
+ SV_TYPE_DOMAIN_ENUM | SV_TYPE_ALTERNATE_XPORT, NULL, NULL, FALSE);
+ } else {
+ Status = RetrieveList(wcBackUpBrowsers[i], Xports[iXportIndex].Transport.Buffer,
+ &pvServerList, &dwEntriesInList, &dwTotalEntries,
+ SV_TYPE_DOMAIN_ENUM | SV_TYPE_ALL, NULL, NULL, FALSE);
+ }
+
+ if(Status)
+ stSuccs.dwDomBk++;
+ else
+ stFails.dwDomBk++;
+
+ NetApiBufferFree(pvServerList);
+
+ } else
+ stFails.dwGB++;
+
+ } // Get backUp List
+
+ //
+ // Force an election randomly
+ //
+ if(rand() % 2)
+ Elect(Xports[iXportIndex].Transport, pwcDomain);
+
+
+ //
+ // Sleep for some time
+ //
+ Sleep(rand() % BASESLEEPTIME);
+
+ if((iPrintCount % 5) == 0){
+ dwCurrentTime = GetTickCount();
+ sprintf(PrintBuf, "\nView Thread: Running for Mins:%ld, Secs:%ld", ((dwCurrentTime - dwStartTime)/60000), ((dwCurrentTime - dwStartTime)%60000)/1000);
+ PrintString(TOALL, PrintBuf);
+ sprintf(PrintBuf, "\nView Thread: SUCCESS: GM %ld, GB %ld, SrvMs %ld, DomMs %ld, SrvBk %ld DomBk %ld",
+ stSuccs.dwGM, stSuccs.dwGB, stSuccs.dwSrvMs, stSuccs.dwDomMs,
+ stSuccs.dwSrvBk, stSuccs.dwDomBk);
+ PrintString(TOALL, PrintBuf);
+ sprintf(PrintBuf, "\nView Thread: FAILURE: GM %ld, GB %ld, SrvMs %ld, DomMs %ld, SrvBk %ld DomBk %ld\n",
+ stFails.dwGM, stFails.dwGB, stFails.dwSrvMs, stFails.dwDomMs,
+ stFails.dwSrvBk, stFails.dwDomBk);
+ PrintString(TOALL, PrintBuf);
+
+ iPrintCount = 1;
+
+ } else
+ iPrintCount++;
+
+ }while(1);
+
+}
+
+
+
+//
+// This routine is executed by a thread. It finds the current master on a
+// transport and stops it. It then randomly picks up a stopped browser and
+// starts it.
+//
+
+VOID
+StopStartThread(THREADDATA *ThreadData)
+{
+INT iXportIndex;
+INT iDomIndex;
+INT iNumOfTransports;
+INT iPrintCount = 1;
+DWORD dwStartCount = 0;
+DWORD dwStopCount = 0;
+DWORD dwCurrentTime;
+TCHAR *pwcDomain;
+LPMACHINEINFO lpMachineInfo;
+NET_API_STATUS Status;
+XPORTINFO *Xports;
+
+
+ Xports = ThreadData->Xports;
+ iNumOfTransports = ThreadData->iNumOfTransports;
+
+ do{
+ iXportIndex = rand() % iNumOfTransports;
+ iDomIndex = rand() % iNumOfTestedDomains;
+
+ pwcDomain = wcTESTEDDOMAINS[iDomIndex];
+
+
+ //
+ // Find Master and stop it.
+ //
+ {
+ TCHAR wcMasterName[CNLEN+1];
+
+ //
+ // Find the master on a protocol
+ //
+ if((Status = GetMasterName(Xports, iNumOfTransports,
+ iXportIndex, pwcDomain, wcMasterName)) == NERR_Success){
+
+ for(lpMachineInfo = HeadList1.Next; lpMachineInfo &&
+ (wcscmp(lpMachineInfo->wcMachineName, wcMasterName) != 0);
+ lpMachineInfo = lpMachineInfo->Next);
+
+ if(lpMachineInfo && IsNTMachine(lpMachineInfo)){
+ if(StopBrowserService(wcMasterName) == NERR_Success){
+ lpMachineInfo->BrowserServiceStarted = FALSE;
+ dwStopCount++;
+ Sleep(rand() % BASESLEEPTIME);
+ }
+ }
+ }
+ }
+
+ Sleep(BASESLEEPTIME);
+
+ //
+ // Start a stopped machine randomly
+ //
+ if(rand() % 2){
+ INT iBrStoppedMachines = 0;
+ INT iBrStartMachine;
+
+ for(lpMachineInfo = HeadList1.Next; lpMachineInfo;
+ lpMachineInfo = lpMachineInfo->Next)
+ if(IsNTMachine(lpMachineInfo) && lpMachineInfo->BrowserServiceStarted == FALSE)
+ iBrStoppedMachines++;
+
+ if(iBrStoppedMachines){
+ iBrStartMachine = (rand() % iBrStoppedMachines)+1;
+
+ for(lpMachineInfo = HeadList1.Next; lpMachineInfo;
+ lpMachineInfo = lpMachineInfo->Next)
+ if(IsNTMachine(lpMachineInfo) && lpMachineInfo->BrowserServiceStarted == FALSE){
+ iBrStartMachine--;
+ if(!iBrStartMachine)
+ if(StartBrowserService(lpMachineInfo->wcMachineName) == NERR_Success){
+ lpMachineInfo->BrowserServiceStarted = TRUE;
+ dwStartCount++;
+ Sleep(rand() % BASESLEEPTIME);
+ }
+ }
+ }
+
+ }
+
+ Sleep(BASESLEEPTIME*2);
+
+ if((iPrintCount % 5) == 0){
+ dwCurrentTime = GetTickCount();
+ sprintf(PrintBuf, "\nStartStop Thread: Running for Mins:%ld Secs:%ld", ((dwCurrentTime - dwStartTime)/60000), ((dwCurrentTime - dwStartTime)%60000)/1000);
+ PrintString(TOALL, PrintBuf);
+ sprintf(PrintBuf, "\nStartStop Thread: SUCCESS: Start %ld, Stop %ld\n", dwStartCount, dwStopCount);
+ PrintString(TOALL, PrintBuf);
+ iPrintCount = 1;
+
+ } else
+ iPrintCount++;
+
+
+
+ }while(1);
+
+}
diff --git a/private/net/svcdlls/browser2/browtest/browstrs.h b/private/net/svcdlls/browser2/browtest/browstrs.h
new file mode 100644
index 000000000..1e88ca2ab
--- /dev/null
+++ b/private/net/svcdlls/browser2/browtest/browstrs.h
@@ -0,0 +1,24 @@
+#include "browutil.h"
+#include "browtest.h"
+#include <time.h>
+
+typedef struct _ThreadData{
+ XPORTINFO *Xports;
+ INT iNumOfTransports;
+ }THREADDATA;
+
+typedef struct _Stats{
+ DWORD dwGM;
+ DWORD dwGB;
+ DWORD dwSrvMs;
+ DWORD dwDomMs;
+ DWORD dwSrvBk;
+ DWORD dwDomBk;
+ }STATS;
+
+DWORD dwStartTime;
+
+BOOL StartBrowserStressTests(UNICODE_STRING *, INT);
+VOID ListViewThread(THREADDATA *);
+VOID StopStartThread(THREADDATA *);
+
diff --git a/private/net/svcdlls/browser2/browtest/browtest.c b/private/net/svcdlls/browser2/browtest/browtest.c
new file mode 100644
index 000000000..e4d21e6ce
--- /dev/null
+++ b/private/net/svcdlls/browser2/browtest/browtest.c
@@ -0,0 +1,1947 @@
+/**
+
+Browser Tester
+==============
+
+Author: Anil F. Thomas
+Date: 9/30/94
+
+
+Function: Browser test involves 2 parts.
+ 1. Functional Testing.
+ 2. Stress Testing.
+
+ The browser service is tested on the protocols installed on the
+ current machine, and what are specified to be tested in the input
+ file. The functional test can be broadly classified as the
+ following
+
+ a) Browser test in a domain spanning a single subnet.
+ b) Browser test in a domain spanning 2 subnets.
+ c) Browser test in 2 domains in a subnet.
+ d) Browser test in 2 domains in 2 subnets.
+
+ When multiple subnets are involved the difference is only in TCP/IP
+ transport.
+
+ Test a) involves finding the master browser, backup browsers,
+ retrieving the browse lists from them and comparing with the input
+ file info, forcing elections etc. Browser on each of the masters
+ in the primary domain is shut down one after the other and the
+ lists are checked.
+
+ Test b) involves TCP with a domain spanning 2 subnets. There are
+ 2 masters and the PDC is the domain master.
+
+ Test c) involves checking whether the PDC of second domain gets the
+ information on the first domain and vice versa. Browser on each of
+ the masters in the primary domain is shut down one after the other
+ and the second PDC and the master of the primary domain are tested.
+ Before testing the other PDC, the RDR and Browser on that machine
+ is shutdown and restarted on that machine. This ensures that a new
+ Browse list is created by it.
+
+ Test d) is similar to test c. However for TCP, the PDC has to be
+ up for the information to be obtained on each subnets.
+
+ Since in Daytona, rdr is not bound to IPX, we have to use a hack
+ to find the master browser and retrive browse list on that protocol.
+
+ Note:
+ To run this test
+ a) You need administrative privileges to all the NT machines
+ specified in the input file.
+ b) The current machine should not be multihomed.
+ c) ShutSvc.exe should be in %SystemRoot% of the PDC of other
+ domains.
+
+
+**/
+
+
+#include "browtest.h"
+#include "browglob.h" // Has the global variables
+#include "browmdom.h"
+#include "browutil.h"
+#include "browstrs.h"
+
+void
+_CRTAPI1
+main(int argc, char *argv[])
+{
+UNICODE_STRING TransportNames[MAXTRANSPORTS];
+INT iNumOfTransports;
+
+
+ HeadList1.Next = NULL;
+
+ ConsoleMutex = CreateMutex( NULL, FALSE, NULL );
+ if(ConsoleMutex == NULL ) {
+ printf("\nCould not create a console mutex\n");
+ exit(1);
+ }
+
+ ParseCommandLine(argc, argv);
+
+
+ Initialize(TransportNames, &iNumOfTransports);
+
+
+ if(!dwStressTest){
+ //
+ // Start the browser functional testing
+ //
+ if(!StartBrowserFunctionalTest(TransportNames, iNumOfTransports)){
+ CloseHandle(ConsoleMutex);
+ fclose(fplog);
+ fclose(fpsum);
+ CleanMem();
+ exit(1);
+ }
+
+ sprintf(PrintBuf,"\n\n\n\t\t\tSUMMARY\n\t\t\t=======\n\n");
+ PrintString(TOALL, PrintBuf);
+ sprintf(PrintBuf,"\n\tERRORS : %ld" , ERRCOUNT);
+ PrintString(TOALL, PrintBuf);
+ sprintf(PrintBuf,"\n\tWARNINGS : %ld" , WRNCOUNT);
+ PrintString(TOALL, PrintBuf);
+ sprintf(PrintBuf,"\n\tOK : %ld" , OKCOUNT);
+ PrintString(TOALL, PrintBuf);
+ sprintf(PrintBuf,"\n\tSUCCESS : %ld\n", SUCSCOUNT);
+ PrintString(TOALL, PrintBuf);
+ sprintf(PrintBuf,"\n\n================================================================================\n");
+ PrintString(TOALL, PrintBuf);
+
+ } else {
+
+ //
+ // Start browser stress tests
+ //
+ if(!StartBrowserStressTests(TransportNames, iNumOfTransports)){
+ sprintf(PrintBuf,"\nStress test encountered Errors!\n");
+ PrintString(TOALL, PrintBuf);
+ }
+ }
+
+CloseHandle(ConsoleMutex);
+fclose(fplog);
+fclose(fpsum);
+CleanMem();
+}
+
+
+
+//
+// Add an element to the linked list of machines.
+// The machines in a domain are grouped together.
+// They are arranged in decreasing order of OS type.
+//
+
+VOID
+AddToList(LPMACHINEINFO Head,
+ LPMACHINEINFO lpMachineInfo)
+{
+LPMACHINEINFO lpTmpInfo;
+LPMACHINEINFO lpTmpPrevInfo;
+
+ //
+ // Find the start of this domain group.
+ //
+ for(lpTmpPrevInfo = Head, lpTmpInfo = Head->Next; lpTmpInfo &&
+ _wcsicmp(lpMachineInfo->wcDomainName, lpTmpInfo->wcDomainName) != 0;
+ lpTmpPrevInfo = lpTmpInfo, lpTmpInfo = lpTmpInfo->Next);
+
+ if(!lpTmpInfo){ // Reached end of list
+ lpTmpPrevInfo->Next = lpMachineInfo;
+ lpMachineInfo->Next = NULL;
+ } else {
+ //
+ // Put it in a slot matching its OS type
+ //
+ for(; lpTmpInfo && _wcsicmp(lpMachineInfo->wcDomainName,
+ lpTmpInfo->wcDomainName) == 0
+ && (lpMachineInfo->iOsType < lpTmpInfo->iOsType);
+ lpTmpPrevInfo = lpTmpInfo, lpTmpInfo = lpTmpInfo->Next);
+
+ if(!lpTmpInfo){ // Reached end of list
+ lpTmpPrevInfo->Next = lpMachineInfo;
+ lpMachineInfo->Next = NULL;
+ } else {
+ lpMachineInfo->Next = lpTmpPrevInfo->Next;
+ lpTmpPrevInfo->Next = lpMachineInfo;
+ }
+ }
+}
+
+
+
+//
+// Check access permissions on all the servers by trying to stop
+// browser on them.
+//
+
+BOOL
+CheckAccessPermissionOnAllMachines()
+{
+BOOL ALLOK = TRUE;
+DWORD Status;
+LPMACHINEINFO lpMachineInfo;
+
+
+
+ for(lpMachineInfo = HeadList1.Next; lpMachineInfo; lpMachineInfo = lpMachineInfo->Next){
+ if(IsNTMachine(lpMachineInfo)){
+ if((Status = StopBrowserService(lpMachineInfo->wcMachineName)) != NERR_Success){
+ sprintf(PrintBuf,"\nERROR: Stopping Browser on %s.\nError: %s\n", UnicodeToPrintfString(lpMachineInfo->wcMachineName), get_error_text(Status));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ ALLOK = FALSE;
+ }
+ }
+ }
+
+
+ if(!ALLOK){
+ sprintf(PrintBuf,"\n\nI need administrative privileges to some of the \
+ \nmachines as listed above. Please get the access \
+ \nand restart the test!!!\n\n");
+ PrintString(TOSCREEN, PrintBuf);
+ } else {
+ sprintf(PrintBuf,"\n\nI have the necessary priveleges!!\n");
+ PrintString(TOSCREEN, PrintBuf);
+ Sleep(3000);
+ }
+
+ return ALLOK;
+}
+
+
+
+//
+// Retrieve the browse lists from the master and the backups. The list
+// from the master is compared against the input file data. The list
+// from the backup is compared against the master.
+//
+
+VOID
+CheckBrowseListsOfMasterAndBackUps(XPORTINFO XportInfo,
+ LPTSTR wcDomainName,
+ LPTSTR wcMasterName,
+ TCHAR wcBackUpBrowsers[MAXBACKUPS][CNSLASHLEN+1],
+ ULONG ulNumBackUps,
+ BOOL bSingleSubnet)
+{
+INT i, index;
+DWORD dwEntriesInList;
+DWORD dwTotalEntries;
+DWORD dwEntriesInBackUpList;
+DWORD dwTotalBackUpEntries;
+PVOID pvMsServerList;
+PVOID pvBkServerList;
+PSERVER_INFO_101 pServerInfo101_l1;
+NET_API_STATUS Status;
+
+
+ //
+ // Get the server list from the Master Browser
+ //
+ // For IPX we need a Hack. We can retrieve the List only through
+ // NwLnkNb. If flag 0x20000000 is specified we get the IPX list
+ //
+ if((XportInfo.index == IPX) && bIPXHack){
+
+ Status = RetrieveList(wcMasterName, L"\\Device\\NwLnkNb",
+ &pvMsServerList, &dwEntriesInList, &dwTotalEntries,
+ SV_TYPE_ALTERNATE_XPORT, NULL, NULL, TRUE);
+
+ } else {
+ Status = RetrieveList(wcMasterName, XportInfo.Transport.Buffer,
+ &pvMsServerList, &dwEntriesInList, &dwTotalEntries,
+ SV_TYPE_ALL, NULL, NULL, TRUE);
+ }
+
+ if(Status == NERR_Success){
+ sprintf(PrintBuf, "\nList Retrieved from : %s",
+ UnicodeToPrintfString(wcMasterName));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ sprintf(PrintBuf, "\nThe Server List in domain %s. EntriesInList %ld. \
+ TotalEntires %ld.",
+ UnicodeToPrintfString(wcDomainName),
+ dwEntriesInList, dwTotalEntries);
+ PrintString(TOSCREENANDLOG, PrintBuf);
+
+ pServerInfo101_l1 = pvMsServerList;
+
+ for(i = 0; i< (INT)dwEntriesInList; i++){
+ sprintf(PrintBuf, "\nServer %s Type %ld",
+ UnicodeToPrintfString(pServerInfo101_l1[i].sv101_name),
+ pServerInfo101_l1[i].sv101_type);
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ }
+ }
+ sprintf(PrintBuf, "\n\n");
+ PrintString(TOSCREENANDLOG, PrintBuf);
+
+
+ //
+ // Check if the List is how it should be, by comparing it with the user's
+ // input file.
+ //
+ sprintf(PrintBuf,"\n\nTEST#: [TS%ld]:\n=============", ++TESTCOUNT);
+ PrintString(TOALL, PrintBuf);
+
+ sprintf(PrintBuf,"\nTEST: Check whether Browse List of Master matches User input.\n");
+ PrintString(TOALL, PrintBuf);
+
+ if(dwEntriesInList)
+ CompareListsMasterAndFile(pvMsServerList, dwEntriesInList,
+ wcDomainName, wcMasterName, XportInfo, bSingleSubnet);
+ else {
+ sprintf(PrintBuf,"\nERROR#: [ER%ld]:There are no entries in the list retrieved from Master: %s.\n",
+ ++ERRCOUNT, UnicodeToPrintfString(wcMasterName));
+ PrintString(TOALL, PrintBuf);
+ }
+
+ //
+ // Retrieve the list from each of the BackUp servers
+ //
+ for(index = 0; index < (INT)ulNumBackUps; index++){
+
+ //
+ // If Master browser and Backup browser are not same retrieve the list.
+ // BackUp browser name has "\\" in the beginning.
+ //
+ if(_wcsicmp(wcMasterName, &wcBackUpBrowsers[index][2]) != 0){
+
+ //
+ // For IPX we need a Hack. We can retrieve the List only through
+ // NwLnkNb. If flag 0x20000000 is specified we get the IPX list
+ //
+ if((XportInfo.index == IPX) && bIPXHack){
+
+ Status = RetrieveList(wcMasterName, L"\\Device\\NwLnkNb",
+ &pvBkServerList, &dwEntriesInBackUpList,
+ &dwTotalBackUpEntries, SV_TYPE_ALTERNATE_XPORT,
+ NULL, NULL, TRUE);
+ } else {
+
+ Status = RetrieveList(wcBackUpBrowsers[index],
+ XportInfo.Transport.Buffer, &pvBkServerList,
+ &dwEntriesInBackUpList, &dwTotalBackUpEntries,
+ SV_TYPE_ALL, NULL, NULL, TRUE);
+ }
+ if(Status == NERR_Success){
+ sprintf(PrintBuf, "\n\nList Retrieved from : %s",
+ UnicodeToPrintfString(wcBackUpBrowsers[index]));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ sprintf(PrintBuf, "\nServer List in domain %s. EntriesInList %ld. TotalEntires %ld.",
+ UnicodeToPrintfString(wcDomainName),
+ dwEntriesInList, dwTotalEntries);
+ PrintString(TOSCREENANDLOG, PrintBuf);
+
+ pServerInfo101_l1 = pvBkServerList;
+
+ for(i = 0; i< (INT)dwEntriesInBackUpList; i++){
+ sprintf(PrintBuf, "\nServer %s Type %ld",
+ UnicodeToPrintfString(pServerInfo101_l1[i].sv101_name),
+ pServerInfo101_l1[i].sv101_type);
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ }
+ }
+
+ //
+ // Compare the Master's and BackUp's Lists.
+ //
+ sprintf(PrintBuf,"\n\nTEST#: [TS%ld]:\n=============", ++TESTCOUNT);
+ PrintString(TOALL, PrintBuf);
+
+ sprintf(PrintBuf,"\nTEST: Check whether Browse List of Master And BackUps are same.\n");
+ PrintString(TOALL, PrintBuf);
+
+ if(dwEntriesInList && dwEntriesInBackUpList)
+ CompareListsMasterAndBackUp(pvMsServerList, dwEntriesInList, pvBkServerList, dwEntriesInBackUpList,
+ wcDomainName, wcMasterName, wcBackUpBrowsers[index], XportInfo.Transport.Buffer);
+ else {
+ if(!dwEntriesInBackUpList){
+ sprintf(PrintBuf,"\nERROR#: [ER%ld]:There are no entries in the list retrieved from BackUp: %s.\n",
+ ++ERRCOUNT, UnicodeToPrintfString(wcBackUpBrowsers[index]));
+ PrintString(TOALL, PrintBuf);
+ }
+ }
+
+ sprintf(PrintBuf, "\n\n");
+ PrintString(TOSCREENANDLOG, PrintBuf);
+
+ NetApiBufferFree(pvBkServerList);
+
+ } else { // If Master != BackUp
+
+ sprintf(PrintBuf, "\n\nINFO: BackUp same as Master (%s). Hence not retrieving the list from it.\n\n",
+ UnicodeToPrintfString(wcBackUpBrowsers[index]));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ }
+
+ } // for (index < ulNumBackUps)
+
+
+ NetApiBufferFree(pvMsServerList);
+}
+
+
+
+//
+// Check if browser is started on all machines in the list, if not start
+// the service.
+//
+
+BOOL
+CheckBrServiceOnMachinesInList()
+{
+BOOL ALLOK = TRUE;
+DWORD Status;
+LPMACHINEINFO lpMachineInfo;
+SERVICE_STATUS ssServiceStatus;
+
+ //
+ // Start browser on PDC first and then wait a few seconds
+ //
+ if(LocDomInfo.lpMInfo && (Status = StartBrowserService(LocDomInfo.lpMInfo->wcMachineName)) != NERR_Success){
+ sprintf(PrintBuf,"\nERROR:Starting Browser on PDC %s.\nError: %s\n", UnicodeToPrintfString(LocDomInfo.lpMInfo->wcMachineName), get_error_text(Status));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ ALLOK = FALSE;
+ } else {
+ LocDomInfo.lpMInfo->BrowserServiceStarted = TRUE;
+ }
+
+ sprintf(PrintBuf, "\n\nSleeping for %ld msecs.\n", BASESLEEPTIME*2);
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ Sleep(BASESLEEPTIME*2);
+
+
+ for(lpMachineInfo = HeadList1.Next; lpMachineInfo; lpMachineInfo = lpMachineInfo->Next){
+ if(IsNTMachine(lpMachineInfo)){
+ if((Status = QueryBrowserServiceStatus(lpMachineInfo, &ssServiceStatus)) != NERR_Success){
+ sprintf(PrintBuf,"\nERROR: Query on %s.\nError: %s", UnicodeToPrintfString(lpMachineInfo->wcMachineName), get_error_text(Status));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ ALLOK = FALSE;
+ } else {
+ if((ssServiceStatus.dwCurrentState == SERVICE_STOP_PENDING) ||
+ (ssServiceStatus.dwCurrentState == SERVICE_STOPPED)){
+
+ sprintf(PrintBuf,"\nBrowser service is not started on %s", UnicodeToPrintfString(lpMachineInfo->wcMachineName));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+
+ //
+ // Attempt to start the browser service
+ //
+ if((Status = StartBrowserService(lpMachineInfo->wcMachineName)) != NERR_Success){
+ sprintf(PrintBuf,"\nERROR:Starting Browser on %s.\nError: %s\n", UnicodeToPrintfString(lpMachineInfo->wcMachineName), get_error_text(Status));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ ALLOK = FALSE;
+ } else {
+ lpMachineInfo->BrowserServiceStarted = TRUE;
+ }
+
+ } else
+ lpMachineInfo->BrowserServiceStarted = TRUE;
+ }
+ }
+ }
+
+
+return ALLOK;
+}
+
+
+
+//
+// Cleanup the Linked list.
+//
+
+VOID
+CleanMem()
+{
+LPMACHINEINFO lpMachineInfo = NULL;
+
+ lpMachineInfo = HeadList1.Next;
+ if(lpMachineInfo) HeadList1.Next = lpMachineInfo->Next;
+
+ for(;lpMachineInfo; lpMachineInfo = HeadList1.Next){
+ HeadList1.Next = lpMachineInfo->Next;
+ free(lpMachineInfo);
+ }
+}
+
+
+
+//
+// Check if a machine specified by name or the MachineInfo data, has a
+// particular protocol.
+//
+
+BOOL
+DoesMachineHaveThisTransport(LPTSTR wcServer,
+ XPORTINFO XportInfo,
+ LPMACHINEINFO lpMachineInfo)
+{
+
+ if(lpMachineInfo == NULL){
+ //
+ // Find the server from the lists
+ //
+
+ // Check Lists 1
+ for(lpMachineInfo = HeadList1.Next; lpMachineInfo &&
+ _wcsicmp(wcServer, lpMachineInfo->wcMachineName) != 0;
+ lpMachineInfo = lpMachineInfo->Next);
+
+ if(lpMachineInfo == NULL) {
+ sprintf(PrintBuf, "ERROR[ER%ld]: Cannot find Server %s\n", ++ERRCOUNT, UnicodeToPrintfString(wcServer));
+ PrintString(TOALL, PrintBuf);
+ return FALSE;
+ }
+
+ }
+
+ //
+ // This transport is there on the specified server
+ //
+ if(lpMachineInfo->Protocols[XportInfo.index])
+ return TRUE;
+
+
+ return FALSE;
+}
+
+
+
+//
+// Test of a domain spanning two subnets. This test is for TCP.
+// The rest of the protocols are covered by Dingle domain tests.
+// If two subnets have one master browser each. They should have
+// the information on the other side if the PDC of the domain is up.
+//
+
+VOID
+DomSpanningMulSubNetsTests(XPORTINFO *TestedXportInfo,
+ INT iNumOfTestedXports)
+{
+INT i, index;
+BOOL bOtherMasterAvailable = FALSE;
+TCHAR *wcDomPDC;
+TCHAR wcOtherMaster[CNLEN+1];
+DWORD dwEntriesInList;
+DWORD dwTotalEntries;
+PVOID pvMsServerList;
+PSERVER_INFO_101 pServerInfo101_l1;
+LPMACHINEINFO lpMachineInfo;
+NET_API_STATUS Status;
+
+ //
+ // If we have TCP as a tested transport.
+ //
+ for(index = 0; (index < iNumOfTestedXports) &&
+ TestedXportInfo[index].index != TCP ; index++);
+ //
+ // If TCP is not a tested Transport, just return.
+ //
+ if(index >= iNumOfTestedXports)
+ return;
+
+ //
+ // if PDC of the domain does not have TCP then return
+ //
+ if(!(LocDomInfo.lpMInfo->Protocols[TCP]))
+ return;
+
+ //
+ // if there are no machines of the same domain in the other subnet, with TCP
+ // then return.
+ //
+ for(lpMachineInfo=lpDomStart; lpMachineInfo; lpMachineInfo = lpMachineInfo->Next){
+ if((lpMachineInfo->iSubnet == SUBNET2) && IsNTMachine(lpMachineInfo)
+ && (lpMachineInfo->Protocols[TCP]))
+ bOtherMasterAvailable = TRUE;
+ }
+
+ if(!bOtherMasterAvailable)
+ return;
+
+ //
+ // Now we know that there are machines in the domain that are across
+ // the subnet and running TCP.
+ //
+ sprintf(PrintBuf,"\n\n\n================================================================================\n");
+ PrintString(TOALL, PrintBuf);
+
+ sprintf(PrintBuf,"\n\n\t\tDOMAIN SPANNING MULTIPLE SUBNETS!\n\t\t=================================\n\n");
+ PrintString(TOALL, PrintBuf);
+
+ sprintf(PrintBuf,"\n\nTEST#: [TS%ld].\n=============", ++TESTCOUNT);
+ PrintString(TOALL, PrintBuf);
+
+ wcDomPDC = LocDomInfo.lpMInfo->wcMachineName;
+ sprintf(PrintBuf,"\nTEST: Retrieve the list of master browsers from PDC.\n",
+ UnicodeToPrintfString(wcDomPDC));
+ PrintString(TOALL, PrintBuf);
+
+ //
+ // Retrieve the list of master browsers.
+ //
+ Status = RetrieveList(wcDomPDC, TestedXportInfo[index].Transport.Buffer,
+ &pvMsServerList, &dwEntriesInList, &dwTotalEntries,
+ SV_TYPE_MASTER_BROWSER, NULL, NULL, FALSE);
+ if(Status == NERR_Success){
+ if(dwEntriesInList > 1) {
+ //
+ // There are more than one master, the other one must be in other
+ // subnet.
+ //
+ sprintf(PrintBuf,"\nSUCCESS[SC%ld]: Retrieved the list of masters. Num of Masters = %ld\n", ++SUCSCOUNT, dwEntriesInList);
+ PrintString(TOALL, PrintBuf);
+
+ pServerInfo101_l1 = pvMsServerList;
+
+ for(i = 0; (i < (INT)dwEntriesInList) &&
+ (_wcsicmp(pServerInfo101_l1[i].sv101_name,wcDomPDC) == 0); i++);
+
+ if(i < (INT)dwEntriesInList)
+ wcscpy(wcOtherMaster, pServerInfo101_l1[i].sv101_name);
+ else {
+ sprintf(PrintBuf,"\nERROR[ER%ld]: Could not find a master other than PDC in the list.\n", ++ERRCOUNT);
+ PrintString(TOALL, PrintBuf);
+
+ NetApiBufferFree(pvMsServerList);
+ return;
+ }
+
+ } else {
+ sprintf(PrintBuf,"\nERROR[ER%ld]: Num of masters in the list is less than 2. Num Of Masters = %ld\n", ++ERRCOUNT, dwEntriesInList);
+ PrintString(TOALL, PrintBuf);
+
+ NetApiBufferFree(pvMsServerList);
+ return;
+ }
+ } else {
+ sprintf(PrintBuf,"\nERROR[ER%ld]: Could not retrieve the list of masters from %s.\nError:%s\n",
+ ++ERRCOUNT, UnicodeToPrintfString(wcDomPDC), get_error_text(Status));
+ PrintString(TOALL, PrintBuf);
+
+ NetApiBufferFree(pvMsServerList);
+ return;
+ }
+
+ NetApiBufferFree(pvMsServerList);
+
+
+ //
+ // Now retrieve the list from the other master.
+ //
+ Status = RetrieveList(wcOtherMaster, TestedXportInfo[index].Transport.Buffer,
+ &pvMsServerList, &dwEntriesInList, &dwTotalEntries,
+ SV_TYPE_ALL, NULL, NULL, FALSE);
+ if(Status == NERR_Success){
+ if(dwEntriesInList > 0) {
+ //
+ // Some machines were in the list
+ //
+
+ pServerInfo101_l1 = pvMsServerList;
+
+ for(i = 0; (i < (INT)dwEntriesInList) &&
+ (_wcsicmp(pServerInfo101_l1[i].sv101_name,wcDomPDC) == 0); i++);
+
+ if(i < (INT)dwEntriesInList){
+ sprintf(PrintBuf,"\nSUCCESS[SC%ld]: PDC is found in other masters's List. Master: %s.\n",
+ ++SUCSCOUNT, UnicodeToPrintfString(wcOtherMaster));
+ PrintString(TOALL, PrintBuf);
+ } else {
+ sprintf(PrintBuf,"\nERROR[ER%ld]: Could not find the PDC in other master's List. Master: %s.\n",
+ ++ERRCOUNT, UnicodeToPrintfString(wcOtherMaster));
+ PrintString(TOALL, PrintBuf);
+
+ NetApiBufferFree(pvMsServerList);
+ return;
+ }
+
+ } else {
+ sprintf(PrintBuf,"\nERROR[ER%ld]: No machines are found in the other master's List. Master: %s.\n",
+ ++ERRCOUNT, UnicodeToPrintfString(wcOtherMaster));
+ PrintString(TOALL, PrintBuf);
+
+ NetApiBufferFree(pvMsServerList);
+ return;
+ }
+ } else {
+ sprintf(PrintBuf,"\nERROR[ER%ld]: Could not retrieve the list from other master. Master: %s. \nError: %s\n",
+ ++ERRCOUNT, UnicodeToPrintfString(wcOtherMaster), get_error_text(Status));
+ PrintString(TOALL, PrintBuf);
+
+ NetApiBufferFree(pvMsServerList);
+ return;
+ }
+
+ NetApiBufferFree(pvMsServerList);
+
+
+return;
+}
+
+
+//
+// This is the main routine which does the single domain tests.
+// The test involves, finding the master browser, backup browsers,
+// retrieving the lists from them and comparing it against the input
+// file info, forcing elections. Then the current browse master is
+// shut down and the new master browser is found. This is repeated
+// until all the
+//
+
+BOOL
+DoSingleDomainTests(XPORTINFO *TestedXportInfo,
+ INT iNumOfTestedXports)
+{
+INT i = 0;
+INT index = 0;
+ULONG ulNumBackUps;
+TCHAR *wcLocDomainName;
+TCHAR wcDomPDC[CNLEN+1];
+TCHAR wcMasterName[CNLEN +1];
+TCHAR wcBackUpBrowsers[MAXBACKUPS][CNSLASHLEN+1];
+TCHAR wcCurrentMasters[MAXPROTOCOLS][CNLEN+1];
+NET_API_STATUS Status;
+LPMACHINEINFO lpMachineInfo;
+
+
+ sprintf(PrintBuf,"\n\n================================================================================\n");
+ PrintString(TOALL, PrintBuf);
+
+ sprintf(PrintBuf,"\n\t\tSINGLE DOMAIN TESTS!\n\t\t====================\n\n");
+ PrintString(TOALL, PrintBuf);
+
+ /******************************\
+ * *
+ * Check PDC == Master Browser *
+ * *
+ \******************************/
+
+ //
+ // This part of the test will do single domain tests on the primary domain
+ // (Domain of this server)
+ //
+ wcLocDomainName = lpLocMachineInfo->wcDomainName;
+ wcscpy(wcDomPDC, LocDomInfo.lpMInfo->wcMachineName);
+ //
+ // Find out if the Master is the same as PDC on each transport
+ //
+ for(index=0; index < iNumOfTestedXports; index++){
+
+
+ /***********************************************\
+ * *
+ * Find Master Browser on each Transport *
+ * *
+ \***********************************************/
+
+ sprintf(PrintBuf,"\n--------------------------------------------------------------------------------\n");
+ PrintString(TOALL, PrintBuf);
+ sprintf(PrintBuf,"\nTransport: %s.\n",UnicodeToPrintfString(TestedXportInfo[index].Transport.Buffer));
+ PrintString(TOALL, PrintBuf);
+
+ sprintf(PrintBuf,"\n\nTEST#: [TS%ld].\n=============", ++TESTCOUNT);
+ PrintString(TOALL, PrintBuf);
+
+ sprintf(PrintBuf,"\nTEST: Check whether PDC and Master Browser are same.\n");
+ PrintString(TOALL, PrintBuf);
+
+ lstrcpy(wcMasterName, L"");
+
+ if((Status = GetMasterName(TestedXportInfo, iNumOfTestedXports, index, wcLocDomainName, wcMasterName)) != NERR_Success){
+ sprintf(PrintBuf,"\nERROR[ER%ld]: Unable to find master: Domain: %s ", ++ERRCOUNT, UnicodeToPrintfString(wcLocDomainName));
+ PrintString(TOALL, PrintBuf);
+ sprintf(PrintBuf," Transport: %s", UnicodeToPrintfString(TestedXportInfo[index].Transport.Buffer));
+ PrintString(TOALL, PrintBuf);
+ sprintf(PrintBuf," PDC: %s.\n", UnicodeToPrintfString(wcDomPDC));
+ PrintString(TOALL, PrintBuf);
+
+ }
+
+ lstrcpy(wcCurrentMasters[index], wcMasterName);
+
+ if(lstrcmp(wcDomPDC, wcMasterName) != 0){
+ //
+ // If the PDC doesnot have this transport then it is OK.
+ //
+ if(DoesMachineHaveThisTransport(wcDomPDC, TestedXportInfo[index], NULL)){
+ sprintf(PrintBuf,"\nERROR[ER%ld]: Master Browser != PDC: Domain: %s ", ++ERRCOUNT, UnicodeToPrintfString(wcLocDomainName));
+ PrintString(TOALL, PrintBuf);
+ sprintf(PrintBuf," Transport: %s.", UnicodeToPrintfString(TestedXportInfo[index].Transport.Buffer));
+ PrintString(TOALL, PrintBuf);
+ sprintf(PrintBuf," PDC: %s", UnicodeToPrintfString(wcDomPDC));
+ PrintString(TOALL, PrintBuf);
+ sprintf(PrintBuf," Master: %s.\n", UnicodeToPrintfString(wcMasterName));
+ PrintString(TOALL, PrintBuf);
+ } else {
+ sprintf(PrintBuf,"\nOK[OK%ld]: PDC not Master. (PDC doesnot have Protocol). Domain: %s ", ++OKCOUNT, UnicodeToPrintfString(wcLocDomainName));
+ PrintString(TOALL, PrintBuf);
+ sprintf(PrintBuf," Transport: %s.", UnicodeToPrintfString(TestedXportInfo[index].Transport.Buffer));
+ PrintString(TOALL, PrintBuf);
+ sprintf(PrintBuf," PDC: %s", UnicodeToPrintfString(wcDomPDC));
+ PrintString(TOALL, PrintBuf);
+ sprintf(PrintBuf," Master: %s.\n", UnicodeToPrintfString(wcMasterName));
+ PrintString(TOALL, PrintBuf);
+ }
+
+ } else {
+ sprintf(PrintBuf,"\nSUCCESS[SC%ld]:Master is PDC. Domain: %s ", ++SUCSCOUNT, UnicodeToPrintfString(wcLocDomainName));
+ PrintString(TOALL, PrintBuf);
+ sprintf(PrintBuf," Transport: %s", UnicodeToPrintfString(TestedXportInfo[index].Transport.Buffer));
+ PrintString(TOALL, PrintBuf);
+ sprintf(PrintBuf," PDC: %s", UnicodeToPrintfString(wcDomPDC));
+ PrintString(TOALL, PrintBuf);
+ sprintf(PrintBuf," Master: %s.\n", UnicodeToPrintfString(wcMasterName));
+ PrintString(TOALL, PrintBuf);
+ }
+
+ /******************************\
+ * *
+ * Find BackUp Browsers *
+ * *
+ \******************************/
+
+ sprintf(PrintBuf,"\n\nTEST#: [TS%ld].\n=============", ++TESTCOUNT);
+ PrintString(TOALL, PrintBuf);
+
+ sprintf(PrintBuf,"\nTEST: Retrieve the backUp list for Domain %s.\n", UnicodeToPrintfString(wcLocDomainName));
+ PrintString(TOALL, PrintBuf);
+
+ ulNumBackUps = 0;
+ if((Status = GetBList(TestedXportInfo[index].Transport, wcLocDomainName, TRUE, &ulNumBackUps, wcBackUpBrowsers)) != NERR_Success) {
+ sprintf(PrintBuf, "\nERROR[ER%ld]: Unable to get backup list of Domain %s ", ++ERRCOUNT, UnicodeToPrintfString(wcLocDomainName));
+ PrintString(TOALL, PrintBuf);
+ sprintf(PrintBuf, "on transport %s.\nError: %s", UnicodeToPrintfString(TestedXportInfo[index].Transport.Buffer), get_error_text(Status));
+ PrintString(TOALL, PrintBuf);
+ } else {
+
+ if(ulNumBackUps){
+ sprintf(PrintBuf,"\nSUCCESS[SC%ld]:Retrieved BackUp List for Domain %s.\n", ++SUCSCOUNT, UnicodeToPrintfString(wcLocDomainName));
+ PrintString(TOALL, PrintBuf);
+
+ sprintf(PrintBuf, "\nBackUp Servers of Domain %s ", UnicodeToPrintfString(wcLocDomainName));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ sprintf(PrintBuf, " on Transport %s are:\n", UnicodeToPrintfString(TestedXportInfo[index].Transport.Buffer));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ for(i = 0; i<(INT)ulNumBackUps; i++){
+ sprintf(PrintBuf, "%s \n", UnicodeToPrintfString(wcBackUpBrowsers[i]));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ }
+ } else {
+ sprintf(PrintBuf, "\nWARNING[WR%ld]: No BackUp Browsers in Domain %s ", ++WRNCOUNT, UnicodeToPrintfString(wcLocDomainName));
+ PrintString(TOALL, PrintBuf);
+ sprintf(PrintBuf, " Transport %s.", UnicodeToPrintfString(TestedXportInfo[index].Transport.Buffer));
+ PrintString(TOALL, PrintBuf);
+ } // ulNumBackUps
+ } // GetBList
+
+ //
+ // Check the browser lists of Master and BackUps
+ //
+ if(_wcsicmp(wcMasterName, L"") != 0)
+ CheckBrowseListsOfMasterAndBackUps(TestedXportInfo[index], wcLocDomainName, wcMasterName, wcBackUpBrowsers, ulNumBackUps, TRUE);
+
+
+ //
+ // Force election on the transport and see who wins.
+ //
+ ForceElectionAndFindWhoWins(TestedXportInfo, iNumOfTestedXports, wcLocDomainName, wcCurrentMasters, index);
+
+ } // for (index == each Transport)
+
+ //
+ // Stop the browser and check who has become the new master on each transport.
+ //
+ if(!StopBrowsersAndFindWhoBecomesMaster(wcLocDomainName, wcDomPDC, TestedXportInfo, iNumOfTestedXports, wcCurrentMasters))
+ return FALSE;
+
+/*
+// Register Fake names.
+// Promotion of BDC to PDC
+// Send illegal Election packets
+*/
+
+ //
+ // Start the browser back on all machines
+ //
+ sprintf(PrintBuf,"\nStarting all the Browsers back.\n");
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ if(!CheckBrServiceOnMachinesInList())
+ return FALSE;
+
+ sprintf(PrintBuf, "\n\nSleeping for %ld msecs.\n", BASESLEEPTIME*2);
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ Sleep(BASESLEEPTIME*2);
+
+
+ return TRUE;
+}
+
+
+//
+// Find the transports to which the browser is bound to, on the local machine.
+//
+BOOL
+FindAllTransports(UNICODE_STRING *TransportNames, DWORD *iNumOfTransports)
+{
+INT i;
+DWORD Status;
+PLMDR_TRANSPORT_LIST TransportList, TransportEntry;
+
+ //
+ // Start the browser on the local machine before foinding the transports
+ //
+ sprintf(PrintBuf,"\n\nStarting browser on the local machine.\n");
+ PrintString(TOSCREENANDLOG, PrintBuf);
+
+ StartBrowserService(lpLocMachineInfo->wcMachineName);
+
+ sprintf(PrintBuf, "\n\nSleeping for %ld msecs.\n", BASESLEEPTIME);
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ Sleep(BASESLEEPTIME);
+
+ //
+ // Find the Protocols into which the browser is bound
+ //
+ Status = GetBrowserTransportList(&TransportList);
+
+ if (Status != NERR_Success) {
+ sprintf(PrintBuf,"ERROR:Unable to retrieve transport list.\n Error: %s\n", get_error_text(Status));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ return FALSE;
+ }
+
+ i = 0;
+ TransportEntry = TransportList;
+ while (TransportEntry != NULL) {
+
+ if(i >= MAXTRANSPORTS){
+ sprintf(PrintBuf,"\nOnly a maximum of %d Transports can be handled.\n", MAXTRANSPORTS);
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ NetApiBufferFree(TransportList);
+ return FALSE;
+ }
+ if((TransportNames[i].Buffer = (TCHAR *)calloc(1, sizeof(TCHAR) * (USHORT)TransportEntry->TransportNameLength + 1)) == NULL){
+ sprintf(PrintBuf,"\nError in allocating Buffer for transport.\n");
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ NetApiBufferFree(TransportList);
+ return FALSE;
+ }
+ wcscpy(TransportNames[i].Buffer, TransportEntry->TransportName);
+ TransportNames[i].Length = (USHORT)TransportEntry->TransportNameLength;
+ TransportNames[i].MaximumLength = (USHORT)TransportEntry->TransportNameLength;
+
+ if (TransportEntry->NextEntryOffset == 0) {
+ TransportEntry = NULL;
+ } else {
+ TransportEntry = (PLMDR_TRANSPORT_LIST)((PCHAR)TransportEntry+TransportEntry->NextEntryOffset);
+ }
+ i++;
+ }
+
+ *iNumOfTransports = i;
+
+ NetApiBufferFree(TransportList);
+
+return TRUE;
+}
+
+
+//
+// Force an election on a transport and find who wins.
+//
+VOID
+ForceElectionAndFindWhoWins(XPORTINFO *TestedXportInfo,
+ INT iNumOfTestedXports,
+ LPTSTR wcDomainName,
+ TCHAR wcCurrentMasters[MAXPROTOCOLS][CNLEN+1],
+ INT index)
+{
+TCHAR wcNewMaster[CNLEN +1];
+NET_API_STATUS Status;
+
+ sprintf(PrintBuf,"\n\nTEST#: [TS%ld].\n=============", ++TESTCOUNT);
+ PrintString(TOALL, PrintBuf);
+
+ sprintf(PrintBuf,"\nTEST: Force Election and Find who wins.\n");
+ PrintString(TOALL, PrintBuf);
+
+ if((Status = Elect(TestedXportInfo[index].Transport, wcDomainName)) != NERR_Success){
+ sprintf(PrintBuf,"\nEEROR[ER%ld]: Could not force an election.\n", ++ERRCOUNT);
+ PrintString(TOALL, PrintBuf);
+
+ } else {
+ //
+ // Sleep for sometime
+ //
+ sprintf(PrintBuf, "\nSleeping for %ld msecs.\n", BASESLEEPTIME);
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ Sleep(BASESLEEPTIME);
+
+ //
+ // Find the new Master.
+ //
+ lstrcpy(wcNewMaster, L"");
+ if((Status = GetMasterName(TestedXportInfo, iNumOfTestedXports, index, wcDomainName, wcNewMaster)) != NERR_Success){
+ sprintf(PrintBuf,"\nERROR[ER%ld]: Unable to find the new master: Domain: %s ", ++ERRCOUNT, UnicodeToPrintfString(wcDomainName));
+ PrintString(TOALL, PrintBuf);
+ sprintf(PrintBuf," Transport: %s", UnicodeToPrintfString(TestedXportInfo[index].Transport.Buffer));
+ PrintString(TOALL, PrintBuf);
+ sprintf(PrintBuf," Old Master: %s.\n", UnicodeToPrintfString(wcCurrentMasters[index]));
+ PrintString(TOALL, PrintBuf);
+ wcscpy(wcCurrentMasters[index], L"");
+ return;
+ }
+
+ if(_wcsicmp(wcCurrentMasters[index], wcNewMaster) != 0){
+ sprintf(PrintBuf,"\nERROR[ER%ld]: New master not same as Old Master after election. New master: %s ", ++ERRCOUNT, UnicodeToPrintfString(wcNewMaster));
+ PrintString(TOALL, PrintBuf);
+ sprintf(PrintBuf," Old Master: %s.\n", UnicodeToPrintfString(wcCurrentMasters[index]));
+ PrintString(TOALL, PrintBuf);
+ sprintf(PrintBuf," Transport: %s", UnicodeToPrintfString(TestedXportInfo[index].Transport.Buffer));
+ PrintString(TOALL, PrintBuf);
+
+ wcscpy(wcCurrentMasters[index], wcNewMaster);
+
+ } else {
+ sprintf(PrintBuf,"\nSUCCESS[SC%ld]: New master same as Old Master after election.", ++SUCSCOUNT);
+ PrintString(TOALL, PrintBuf);
+ }
+ }
+
+}
+
+
+//
+// Initialize all variables, check the PDC etc.
+//
+VOID
+Initialize(UNICODE_STRING *TransportNames, INT *iNumOfTransports)
+{
+INT i;
+TCHAR wcLocalComputerName[CNLEN+1];
+TCHAR wcDomPDC[CNLEN+1];
+DWORD dwNameLen;
+LPMACHINEINFO lpMachineInfo;
+NET_API_STATUS Status;
+
+
+ if((fplog = fopen(BROWTESTLOGFILE, "w")) == NULL){
+ printf("\nError opening the output file %s!\n", BROWTESTLOGFILE);
+ CloseHandle(ConsoleMutex);
+ exit(1);
+ }
+
+ if((fpsum = fopen(BROWTESTSUMMARYFILE, "w")) == NULL){
+ printf("\nError opening the output file %s!\n", BROWTESTSUMMARYFILE);
+ fclose(fplog);
+ CloseHandle(ConsoleMutex);
+ exit(1);
+ }
+
+ sprintf(PrintBuf,"Browser Test started.\n\n");
+ PrintString(TOALL, PrintBuf);
+
+ if(!dwStressTest){
+ sprintf(PrintBuf,"Functional test chosen.\n\n");
+ PrintString(TOALL, PrintBuf);
+
+ } else {
+ sprintf(PrintBuf,"Stress test chosen. Time %ld Minutes.\n\n", dwStressTest);
+ PrintString(TOALL, PrintBuf);
+ }
+
+
+ sprintf(PrintBuf,"\t IPXHack = %s\n", (bIPXHack ? "TRUE": "FALSE"));
+ PrintString(TOALL, PrintBuf);
+ sprintf(PrintBuf,"\t Force Announcement = %s\n", (bForceAnn ? "TRUE": "FALSE"));
+ PrintString(TOALL, PrintBuf);
+ sprintf(PrintBuf,"\t Single Domain Tests = %s\n", (bSingleDomTest ? "DONE": "NOT DONE"));
+ PrintString(TOALL, PrintBuf);
+ sprintf(PrintBuf,"\t Stress Test = %s\n", (dwStressTest ? "DONE": "NOT DONE"));
+ PrintString(TOALL, PrintBuf);
+
+
+ //
+ // Get the domain info from the input file
+ //
+ if(!ReadInputFile()){
+ fclose(fplog);
+ fclose(fpsum);
+ CleanMem();
+ CloseHandle(ConsoleMutex);
+ exit(1);
+ }
+
+
+ if(HeadList1.Next == NULL){
+ sprintf(PrintBuf,"\nThere are no machines specified in Subnet 1.\n");
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ fclose(fplog);
+ fclose(fpsum);
+ CleanMem();
+ CloseHandle(ConsoleMutex);
+ exit(1);
+ }
+
+ //
+ // Now we have the information on the domains in 1 or 2 subnets, in the list.
+ //
+ sprintf(PrintBuf,"\nList1:");
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ PrintList(HeadList1);
+
+
+ //
+ // Find the Local Computer Name
+ //
+ dwNameLen = sizeof(wcLocalComputerName);
+ if(!GetComputerName(wcLocalComputerName, &dwNameLen)){
+ sprintf(PrintBuf,"\nError: Unable to get the Local ComputerName.\
+ \nError %s\n", get_error_text(GetLastError()));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ fclose(fplog);
+ fclose(fpsum);
+ CleanMem();
+ CloseHandle(ConsoleMutex);
+ exit(1);
+ }
+
+ //
+ // Find the Local Computer in the List
+ //
+ for(lpLocMachineInfo = HeadList1.Next; lpLocMachineInfo &&
+ _wcsicmp(wcLocalComputerName, lpLocMachineInfo->wcMachineName) != 0;
+ lpLocMachineInfo = lpLocMachineInfo->Next);
+ if(!lpLocMachineInfo){
+ sprintf(PrintBuf,"\nError: Unable to match the Local ComputerName in \
+ \nthe List: %s\n\n", UnicodeToPrintfString(wcLocalComputerName));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ fclose(fplog);
+ fclose(fpsum);
+ CleanMem();
+ CloseHandle(ConsoleMutex);
+ exit(1);
+ }
+
+
+ sprintf(PrintBuf,"\n\nMatched the Local Machine Name: %s\n",
+ UnicodeToPrintfString(lpLocMachineInfo->wcMachineName));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+
+
+ //
+ // Find the domain name in the list. all machines in the domain are close
+ // by.
+ //
+ lpDomStart = HeadList1.Next;
+ while(lpDomStart &&
+ _wcsicmp(lpDomStart->wcDomainName,lpLocMachineInfo->wcDomainName) != 0)
+ lpDomStart = lpDomStart->Next;
+
+ if(!lpDomStart){
+ sprintf(PrintBuf, "\n\nERROR[ER%ld]:Comparing the Lists.Could not find \
+ \n Domain %s in List.\n",++ERRCOUNT,
+ UnicodeToPrintfString(lpLocMachineInfo->wcDomainName));
+ PrintString(TOALL, PrintBuf);
+ fclose(fplog);
+ fclose(fpsum);
+ CleanMem();
+ CloseHandle(ConsoleMutex);
+ exit(1);
+ }
+
+ wcscpy(wcTESTEDDOMAINS[iNumOfTestedDomains++], lpDomStart->wcDomainName);
+
+ sprintf(PrintBuf,"\n\nPlease wait while I check my access permissions on the servers \nby Starting and Stopping Browser on them.........\n");
+ PrintString(TOSCREEN, PrintBuf);
+ Sleep(3000);
+
+ //
+ // Find the Protocols into which the browser is bound
+ //
+ if(!FindAllTransports(TransportNames, iNumOfTransports)){
+ fclose(fplog);
+ fclose(fpsum);
+ CleanMem();
+ CloseHandle(ConsoleMutex);
+ exit(1);
+ }
+
+ //
+ // If this is not stress test, then check whether it is a multihomed
+ // machine.
+ //
+
+ if(!dwStressTest){
+ //
+ // Find if the machine is multihomed
+ //
+ if(LocalMachineIsMultihomed(TransportNames, *iNumOfTransports)){
+ sprintf(PrintBuf,"\nThis machine is multihomed. \
+ \nI cannot test browser from a multihomed Machine.\n");
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ fclose(fplog);
+ fclose(fpsum);
+ CleanMem();
+ CloseHandle(ConsoleMutex);
+ exit(1);
+ }
+ }
+
+ //
+ // Check whether I have administrative privileges on all machines.
+ //
+ if(!CheckAccessPermissionOnAllMachines()){
+ fclose(fplog);
+ fclose(fpsum);
+ CleanMem();
+ CloseHandle(ConsoleMutex);
+ exit(1);
+ }
+
+ //
+ // Wait for the service controller to get updated.
+ //
+ sprintf(PrintBuf, "\n\nSleeping for %ld msecs.\n", BASESLEEPTIME);
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ Sleep(BASESLEEPTIME);
+
+
+ //
+ // Find the PDC of the Primary domain.
+ //
+ i = 0;
+ lstrcpy(wcDomPDC, L"");
+ while((i < (*iNumOfTransports)) && (Status = GetNetBiosPdcName(TransportNames[i].Buffer, lpLocMachineInfo->wcDomainName, wcDomPDC)) != NERR_Success)
+ i++;
+
+ if(Status == NERR_Success){
+
+ sprintf(PrintBuf,"\nPDC of %s is ", UnicodeToPrintfString(lpLocMachineInfo->wcDomainName));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ sprintf(PrintBuf, "%s\n", UnicodeToPrintfString(wcDomPDC));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+
+ for(lpMachineInfo=HeadList1.Next; lpMachineInfo &&
+ _wcsicmp(lpMachineInfo->wcMachineName, wcDomPDC) != 0;
+ lpMachineInfo=lpMachineInfo->Next);
+
+ if(lpMachineInfo) {
+ //
+ // Found PDC in the list.
+ //
+ wcscpy(LocDomInfo.wcDomainName, lpMachineInfo->wcDomainName);
+ LocDomInfo.lpMInfo = lpMachineInfo;
+ } else {
+ sprintf(PrintBuf,"\nERROR[ER%ld]: Unable to find the PDC %s in the input file.\n",
+ ++ERRCOUNT,UnicodeToPrintfString(wcDomPDC));
+ PrintString(TOALL, PrintBuf);
+ fclose(fplog);
+ fclose(fpsum);
+ CleanMem();
+ CloseHandle(ConsoleMutex);
+ exit(1);
+ }
+
+
+ } else {
+ sprintf(PrintBuf,"\nERROR[ER%ld]: Unable to determine the PDC of %s on any transports.\n",
+ ++ERRCOUNT,UnicodeToPrintfString(lpLocMachineInfo->wcDomainName));
+ PrintString(TOALL, PrintBuf);
+ fclose(fplog);
+ fclose(fpsum);
+ CleanMem();
+ CloseHandle(ConsoleMutex);
+ exit(1);
+ }
+
+}
+
+
+
+//
+// This routine has to be changed. Now it looks for multiple
+// entries of Netbt, or Nbf to determine if it is Multihomed.
+//
+BOOL
+LocalMachineIsMultihomed(UNICODE_STRING *TransportNames, INT iNumOfTransports)
+{
+INT i, j;
+
+
+
+ for(i=0; i< MAXPROTOCOLS; i++){
+ BOOL found = FALSE;
+ for(j=0; j<iNumOfTransports; j++) {
+ if(wcsstr(TransportNames[j].Buffer, TRANSPORTS[i])!=NULL){
+ if(!found)
+ found = TRUE;
+ else
+ return TRUE; // Found Multiple entries
+ }
+ }
+ }
+
+ //
+ // If number of transports retrieved is more than the MAXPROTOCOLS
+ // we suspect multihomed (RAS).
+ //
+ if(iNumOfTransports > MAXPROTOCOLS){
+ sprintf(PrintBuf, "\nMore than %ld Transports found. Exiting expecting machine to be Multihomed.\n", MAXPROTOCOLS);
+ PrintString(TOALL, PrintBuf);
+ return TRUE;
+ }
+
+
+return FALSE;
+}
+
+
+//
+// Check if the New master found, is the correct in terms of OS type.
+//
+BOOL
+NewMasterIsCorrect(TCHAR wcCurrentMasters[MAXPROTOCOLS][CNLEN+1],
+ INT iMasterIndex,
+ LPTSTR wcNewMaster,
+ XPORTINFO XportInfo,
+ LPMACHINEINFO lpStoppedMachineInfo)
+{
+INT index;
+LPMACHINEINFO lpMachineInfo, lpNewMasterInfo;
+LPTSTR pLocDomainName;
+
+ //
+ // If Stopped machine is not the same as the current master, then
+ // stopped machine didn't have the protocol. So check whether the
+ // current and New masters are same.
+ //
+ if(_wcsicmp(wcCurrentMasters[iMasterIndex], lpStoppedMachineInfo->wcMachineName) != 0){
+ if(_wcsicmp(wcCurrentMasters[iMasterIndex], wcNewMaster) != 0){
+ sprintf(PrintBuf, "\n\nERROR[ER%ld]:Browser stopped on another machine. However master changed! New master: %s ",++ERRCOUNT, UnicodeToPrintfString(wcNewMaster));
+ PrintString(TOALL, PrintBuf);
+ sprintf(PrintBuf, " Old master: %s.\n", UnicodeToPrintfString(wcCurrentMasters[iMasterIndex]));
+ PrintString(TOALL, PrintBuf);
+ return FALSE;
+ } else
+ return TRUE;
+ }
+
+ pLocDomainName = lpStoppedMachineInfo->wcDomainName;
+
+ //
+ // Find the transport index
+ //
+ index = XportInfo.index;
+
+ //
+ // Find the New Master name in the list.
+ //
+ lpMachineInfo = HeadList1.Next;
+ while(lpMachineInfo && _wcsicmp(lpMachineInfo->wcMachineName,wcNewMaster) != 0)
+ lpMachineInfo = lpMachineInfo->Next;
+
+ if(!lpMachineInfo){
+ sprintf(PrintBuf, "\n\nERROR[ER%ld]:Comparing the Lists.Could not find New master %s in List.\n",++ERRCOUNT, UnicodeToPrintfString(wcNewMaster));
+ PrintString(TOALL, PrintBuf);
+ return FALSE;
+ }
+ lpNewMasterInfo = lpMachineInfo;
+
+ //
+ // The machines are ordered based on there OSTYPES. Check whether browser
+ // service is started on any machine with higher OS Level than the New master.
+ //
+ lpMachineInfo = lpDomStart;
+ while(lpMachineInfo && _wcsicmp(lpMachineInfo->wcMachineName,wcNewMaster) != 0){
+
+ //
+ // For TCP/IP the machines in second subnet is not participating
+ // in the election.
+ //
+ if((index == TCP) && (lpMachineInfo->iSubnet != SUBNET1)){
+ lpMachineInfo = lpMachineInfo->Next;
+ continue;
+ }
+
+
+ if(lpMachineInfo->BrowserServiceStarted){
+ //
+ // If OS Level is greater and this machine has the protocol
+ // then return FALSE.
+ //
+ if((lpMachineInfo->iOsPreference > lpNewMasterInfo->iOsPreference) && lpMachineInfo->Protocols[index]){
+ sprintf(PrintBuf, "\n\nERROR[ER%ld]:Comparing the Lists.Could not find Domain %s in List.\n",++ERRCOUNT, UnicodeToPrintfString(pLocDomainName));
+ PrintString(TOALL, PrintBuf);
+ return FALSE;
+ } else {
+ if((lpMachineInfo->iOsPreference == lpNewMasterInfo->iOsPreference) && lpMachineInfo->Protocols[index]){
+ //
+ // If both have same OStypes then we check the Server Bits to see
+ // if the current master have higher Server Bits.
+ //
+ if(lpMachineInfo->dwServerBits > lpNewMasterInfo->dwServerBits){
+ sprintf(PrintBuf, "\n\nERROR[ER%ld]:Machine %s has higher server bits than New master!\n",++ERRCOUNT, UnicodeToPrintfString(lpMachineInfo->wcMachineName));
+ PrintString(TOALL, PrintBuf);
+ return FALSE;
+ }
+ }
+ }
+ }
+
+ lpMachineInfo = lpMachineInfo->Next;
+ } // while
+
+
+return TRUE;
+}
+
+
+//
+// Parse the command line to extract the options
+//
+VOID
+ParseCommandLine(
+ INT argc,
+ CHAR *argv[]
+ )
+{
+ INT i, j;
+ BOOL bNoMatchFound;
+
+ //
+ // Command arguments as specified as:
+ // [/-]command:value
+ // All commands are treated as case insensitive
+ //
+ for ( i = 1; i < argc; i++ ) {
+
+ if ( argv[i][0] == '/' || argv[i][0] == '-' ) {
+
+ bNoMatchFound = TRUE;
+ for( j = 0; j < CommandTableSz && bNoMatchFound; j++ ) {
+
+ if ( _strnicmp( CommandTable[j].Command, &argv[i][1], strlen(CommandTable[j].Command ) ) == 0 ) {
+ //
+ // Matched a keyword
+ //
+ CHAR *Value = strrchr( &argv[i][1], ':' );
+
+ bNoMatchFound=FALSE;
+ //
+ // Setup for command's with RHS
+ //
+ if ( CommandTable[j].ValueType != VALUETYPE_IGNORE && CommandTable[j].ValueType != VALUETYPE_HELP ) {
+ if ( Value == NULL ) {
+ printf("Error: Incorrectly specified argument: %s.", argv[i] );
+ Usage( argv[0] );
+ ExitProcess(1L);
+ }
+ //
+ // Ensure a RHS has been correctly specified
+ //
+ if ( strlen(Value) <= 1 ) {
+ printf("Error: Must specify a Value for the option \"%s\"", CommandTable[j].Command );
+ Usage( argv[0] );
+ ExitProcess(1L);
+ }
+ Value++;
+ }
+ //
+ // Perform RHS extraction
+ //
+ switch ( CommandTable[j].ValueType ) {
+ case VALUETYPE_IGNORE : break;
+ case VALUETYPE_HELP : Usage( argv[0] ); ExitProcess( 0L );
+ case VALUETYPE_BOOL : *((BOOL *)CommandTable[j].Value) = (atoi( Value ) ? TRUE : FALSE); break;
+ case VALUETYPE_INTEGER: *((INT *)CommandTable[j].Value) = atoi( Value ); break;
+ case VALUETYPE_ULONG : *((ULONG *)CommandTable[j].Value) = atol( Value ); break;
+ case VALUETYPE_STRING : *((CHAR **)CommandTable[j].Value) = (CHAR *)Value; break;
+ }
+ }
+ }
+ if ( bNoMatchFound ) { printf("Error: Unknown argument: %s", argv[i] ); Usage( argv[0] );ExitProcess(1L);}
+ }
+ }
+
+}
+
+
+//
+// Print the linked list.
+//
+VOID
+PrintList(MACHINEINFO Head)
+{
+INT i;
+LPMACHINEINFO lpMachineInfo ;
+
+ sprintf(PrintBuf,"\nDomain Machine Type Subnet Protocols");
+ PrintString(TOALL, PrintBuf);
+ sprintf(PrintBuf,"\n-----------------------------------------------------------");
+ PrintString(TOALL, PrintBuf);
+ for(lpMachineInfo= Head.Next; lpMachineInfo; lpMachineInfo = lpMachineInfo->Next){
+ sprintf(PrintBuf,"\n%-16s ", UnicodeToPrintfString(lpMachineInfo->wcDomainName));
+ PrintString(TOALL, PrintBuf);
+ sprintf(PrintBuf,"%-16s %-9s %d ", UnicodeToPrintfString(lpMachineInfo->wcMachineName),
+ OSTYPES[lpMachineInfo->iOsType].Type, lpMachineInfo->iSubnet);
+ PrintString(TOALL, PrintBuf);
+ for(i=0; i<MAXPROTOCOLS; i++)
+ if(lpMachineInfo->Protocols[i]){
+ sprintf(PrintBuf,"%s ", PROTOCOLS[i]);
+ PrintString(TOALL, PrintBuf);
+ }
+ }
+
+}
+
+
+//
+// Main function doing the browser functional testing.
+//
+BOOL
+StartBrowserFunctionalTest(UNICODE_STRING *TransportNames,
+ INT iNumOfTransports)
+{
+INT i, j;
+INT iNumOfTestedXports = 0;
+XPORTINFO TestedXportInfo[MAXPROTOCOLS];
+
+// for(i=0; i < iNumOfTransports; i++)
+// printf("\nTransport[%d] = %s",i+1, UnicodeToPrintfString(TransportNames[i].Buffer));
+
+
+
+ //
+ // Decide which protocols will be tested. This is done, by inspecting the local
+ // transports and what was requested by the user in the input file. Only the Local
+ // Machine line is inspected
+ //
+
+ for(i = 0; i < MAXPROTOCOLS; i++){
+ if(lpLocMachineInfo->Protocols[i]){
+ for(j = 0; j < iNumOfTransports; j++){
+ if(wcsstr(TransportNames[j].Buffer, TRANSPORTS[i]) != NULL){
+ if((TestedXportInfo[iNumOfTestedXports].Transport.Buffer = (TCHAR *)calloc(1, sizeof(TCHAR) * TransportNames[j].MaximumLength+1)) == NULL){
+ sprintf(PrintBuf,"\nError in allocating Buffer for tested transports.\n");
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ return FALSE;
+ }
+ wcscpy(TestedXportInfo[iNumOfTestedXports].Transport.Buffer, TransportNames[j].Buffer);
+ TestedXportInfo[iNumOfTestedXports].Transport.Length = TransportNames[j].Length;
+ TestedXportInfo[iNumOfTestedXports].Transport.MaximumLength = TransportNames[j].MaximumLength;
+ iNumOfTestedXports++;
+ break;
+ }
+ }
+ }
+ }
+
+ //
+ // Free the transport Name Buffer
+ //
+ for(i=0; i< iNumOfTransports; i++) free(TransportNames[i].Buffer);
+
+ if(!iNumOfTestedXports){
+ sprintf(PrintBuf,"\nThe transports requested to be tested is not currently installed/enabled\
+ \non this computer.\n");
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ return FALSE;
+ }
+
+ //
+ //Find the index of the transport;
+ //
+ for(i=0; i< iNumOfTestedXports; i++){
+ for(j=0; j < MAXPROTOCOLS
+ && wcsstr(TestedXportInfo[i].Transport.Buffer, TRANSPORTS[j]) == NULL; j++);
+ if(j >= MAXPROTOCOLS){
+ sprintf(PrintBuf,"\n\nUnknown transport %s!\n",UnicodeToPrintfString(TestedXportInfo[i].Transport.Buffer) );
+ PrintString(TOALL, PrintBuf);
+ return FALSE;
+ }
+ TestedXportInfo[i].index = j;
+ }
+
+
+ sprintf(PrintBuf,"\n\nTested Transports are:\n");
+ PrintString(TOALL, PrintBuf);
+ for(i=0; i< iNumOfTestedXports; i++){
+ sprintf(PrintBuf,"Tested Transports[%d] = %-25s Index=%d\n", i+1, UnicodeToPrintfString(TestedXportInfo[i].Transport.Buffer), TestedXportInfo[i].index);
+ PrintString(TOALL, PrintBuf);
+ }
+
+
+ //
+ // Check whether the browser service has been started on all NT machines
+ //
+ if(!CheckBrServiceOnMachinesInList())
+ return FALSE;
+
+
+ //
+ // Ask the machines in the domain to announce themselves and
+ // sleep for sometime so the elections and things happen
+ //
+ if(!bForceAnn) {
+ sprintf(PrintBuf, "\nSleeping for %ld msecs.\n", UPDATESLEEPTIME);
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ Sleep(UPDATESLEEPTIME);
+
+ } else {
+
+ //
+ // Ask the machines in the domain to announce themselves and
+ // sleep for sometime so the elections and things happen
+ //
+ ForceAnnounce(TestedXportInfo[0].Transport, lpLocMachineInfo->wcDomainName);
+
+ sprintf(PrintBuf, "\nForceAnnounce. Sleeping for %ld msecs.\n", BASESLEEPTIME*2);
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ Sleep(BASESLEEPTIME*2);
+ }
+
+
+ if(bSingleDomTest){
+ //
+ // Do the single domain tests
+ //
+
+ if(!DoSingleDomainTests(TestedXportInfo, iNumOfTestedXports)){
+ sprintf(PrintBuf,"\n\nERROR: Single domain test failed!\n");
+ PrintString(TOALL, PrintBuf);
+ }
+ }
+
+
+ //
+ // Domain Spanning Multiple domains.
+ //
+ DomSpanningMulSubNetsTests(TestedXportInfo, iNumOfTestedXports);
+
+
+ //
+ // Do the multiple domain tests
+ //
+ DoMulDomMulSubNetTests(TestedXportInfo, iNumOfTestedXports);
+
+ //
+ // Free the tested transport Name Buffer
+ //
+ for(i=0; i< iNumOfTestedXports; i++) free(TestedXportInfo[i].Transport.Buffer);
+
+return TRUE;
+}
+
+
+BOOL
+StopBrowsersAndFindWhoBecomesMaster(LPTSTR wcDomainName,
+ LPTSTR wcDomPDC,
+ XPORTINFO *TestedXportInfo,
+ INT iNumOfTestedXports,
+ TCHAR wcCurrentMasters[MAXPROTOCOLS][CNLEN+1])
+{
+INT index;
+BOOL Found;
+INT iNonMasters = 0;
+LPMACHINEINFO lpMachineInfo;
+
+ sprintf(PrintBuf,"\n\n--------------------------------------------------------------------------------\n");
+ PrintString(TOALL, PrintBuf);
+
+ sprintf(PrintBuf,"\nTEST: Stop Browser on PDC %s and find who becomes the master.\n", UnicodeToPrintfString(wcDomPDC));
+ PrintString(TOALL, PrintBuf);
+
+ if(!StopBrowserOnCurrentMaster(wcDomainName, wcDomPDC, TestedXportInfo, iNumOfTestedXports, wcCurrentMasters))
+ return FALSE;
+
+ //
+ // Go through all the NT machines and stop the browsers on them.
+ //
+ do{
+ Found = FALSE;
+
+ //
+ // Find whether any of the current masters are NT machines
+ //
+ for(index = 0; ((index < iNumOfTestedXports) && !Found); ){
+
+ if(_wcsicmp(wcCurrentMasters[index], L"") != 0){
+ for(lpMachineInfo = lpDomStart; lpMachineInfo && _wcsicmp(wcCurrentMasters[index],
+ lpMachineInfo->wcMachineName) != 0; lpMachineInfo= lpMachineInfo->Next);
+
+ if(lpMachineInfo)
+ Found = (IsNTMachine(lpMachineInfo) && lpMachineInfo->BrowserServiceStarted);
+ }
+
+ if(!Found)
+ index++;
+ }
+
+ if(Found){
+ sprintf(PrintBuf,"\n\n--------------------------------------------------------------------------------\n");
+ PrintString(TOALL, PrintBuf);
+ sprintf(PrintBuf,"\nTEST: Stop Browser on Master %s and find who becomes the master.\n",
+ UnicodeToPrintfString(wcCurrentMasters[index]));
+ PrintString(TOALL, PrintBuf);
+ if(!StopBrowserOnCurrentMaster(wcDomainName, wcCurrentMasters[index], TestedXportInfo, iNumOfTestedXports, wcCurrentMasters))
+ return FALSE;
+ }
+ }while(Found);
+
+ //
+ // See if any of the NT machines in the list didnot become a master browser.
+ // If the machine has only TCP then, we check whther it is in the same
+ // subnet.
+ //
+ sprintf(PrintBuf,"\n\nTEST#: [TS%ld].\n=============", ++TESTCOUNT);
+ PrintString(TOALL, PrintBuf);
+
+ sprintf(PrintBuf,"\nTEST: Check whether any NT machines didnot become a master.\n");
+ PrintString(TOALL, PrintBuf);
+
+
+ for(lpMachineInfo = lpDomStart; lpMachineInfo && _wcsicmp(wcDomainName,
+ lpMachineInfo->wcDomainName) == 0; lpMachineInfo= lpMachineInfo->Next){
+
+ if(IsNTMachine(lpMachineInfo) && lpMachineInfo->BrowserServiceStarted){
+ if(lpMachineInfo->Protocols[IPX] || lpMachineInfo->Protocols[NBIPX] ||
+ lpMachineInfo->Protocols[XNS] ||lpMachineInfo->Protocols[NETBEUI] ||
+ (lpMachineInfo->Protocols[TCP] && lpMachineInfo->iSubnet == SUBNET1)){
+
+ iNonMasters++;
+ sprintf(PrintBuf,"\nERROR: Machine %s didnot become a master!\n", UnicodeToPrintfString(lpMachineInfo->wcMachineName));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ }
+ }
+ }
+
+ if(!iNonMasters){
+ sprintf(PrintBuf,"\nSUCCESS[SC%ld]:All NT machines became masters.\n", ++SUCSCOUNT);
+ PrintString(TOALL, PrintBuf);
+ } else {
+ sprintf(PrintBuf,"\nERROR[ER%ld]: %d machines didnot become a master!\n", ++ERRCOUNT, iNonMasters);
+ PrintString(TOALL, PrintBuf);
+ }
+
+
+return TRUE;
+}
+
+
+BOOL
+StopBrowserOnCurrentMaster (LPTSTR wcDomainName,
+ LPTSTR wcBrStopMachine,
+ XPORTINFO *TestedXportInfo,
+ INT iNumOfTestedXports,
+ TCHAR wcCurrentMasters[MAXPROTOCOLS][CNLEN+1])
+{
+INT i, index;
+TCHAR wcNewMaster[CNLEN +1];
+LPMACHINEINFO lpMachineInfo, lpBrStoppedMachineInfo;
+NET_API_STATUS Status;
+
+ //
+ // Shutdown the Browser on the specified machine and see who becomes the new Master browser
+ //
+ if((Status = StopBrowserService(wcBrStopMachine)) != NERR_Success){
+ sprintf(PrintBuf,"\nERROR[ER%ld]:Could not stop browser on %s.\n", ++ERRCOUNT, UnicodeToPrintfString(wcBrStopMachine));
+ PrintString(TOALL, PrintBuf);
+ return FALSE;
+ }
+
+ for(lpMachineInfo = HeadList1.Next; lpMachineInfo &&
+ (_wcsicmp(lpMachineInfo->wcMachineName, wcBrStopMachine) != 0); lpMachineInfo = lpMachineInfo->Next);
+
+ if(!lpMachineInfo){
+ sprintf(PrintBuf,"\nERROR[ER%ld]:Could not find Stopped machine's name in List1. %s.\n", ++ERRCOUNT, UnicodeToPrintfString(wcBrStopMachine));
+ PrintString(TOALL, PrintBuf);
+ return FALSE;
+ }
+ lpMachineInfo->BrowserServiceStarted = FALSE;
+ lpBrStoppedMachineInfo = lpMachineInfo;
+
+ //
+ // Sleep for sometime
+ //
+ sprintf(PrintBuf, "\nSleeping for %ld msecs\n", BASESLEEPTIME * 2);
+ PrintString(TOSCREENANDLOG, PrintBuf);
+
+ Sleep(BASESLEEPTIME*2);
+
+
+ //
+ // For each of the tested transports find who is the Master
+ //
+ for(index = 0; index < iNumOfTestedXports; index++){
+
+ sprintf(PrintBuf,"\n\nTEST#: [TS%ld].\n=============", ++TESTCOUNT);
+ PrintString(TOALL, PrintBuf);
+
+ sprintf(PrintBuf,"\nTEST: Find new master on Transport %s.\n", UnicodeToPrintfString(TestedXportInfo[index].Transport.Buffer));
+ PrintString(TOALL, PrintBuf);
+
+ //
+ // Find the new Master.
+ //
+ lstrcpy(wcNewMaster, L"");
+ if((Status = GetMasterName(TestedXportInfo, iNumOfTestedXports, index, wcDomainName, wcNewMaster)) != NERR_Success){
+ if(MasterAvailable(TestedXportInfo[index], wcDomainName, SUBNET1)){
+ sprintf(PrintBuf,"\nERROR[ER%ld]: Unable to find the new master: Domain: %s ", ++ERRCOUNT, UnicodeToPrintfString(wcDomainName));
+ PrintString(TOALL, PrintBuf);
+ sprintf(PrintBuf," Transport: %s", UnicodeToPrintfString(TestedXportInfo[index].Transport.Buffer));
+ PrintString(TOALL, PrintBuf);
+ sprintf(PrintBuf," Old Master: %s.\n", UnicodeToPrintfString(wcCurrentMasters[index]));
+ PrintString(TOALL, PrintBuf);
+
+ } else {
+ sprintf(PrintBuf,"\nOK[OK%ld]: Unable to find the new master. No servers running Browser service. Domain: %s ", ++OKCOUNT, UnicodeToPrintfString(wcDomainName));
+ PrintString(TOALL, PrintBuf);
+ sprintf(PrintBuf," Transport: %s", UnicodeToPrintfString(TestedXportInfo[index].Transport.Buffer));
+ PrintString(TOALL, PrintBuf);
+ sprintf(PrintBuf," Old Master: %s.\n", UnicodeToPrintfString(wcCurrentMasters[index]));
+ PrintString(TOALL, PrintBuf);
+ }
+
+ wcscpy(wcCurrentMasters[index], L"");
+ continue;
+ }
+
+ sprintf(PrintBuf,"\nNew Master: %s.\n", UnicodeToPrintfString(wcNewMaster));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+
+ if(NewMasterIsCorrect(wcCurrentMasters, index, wcNewMaster, TestedXportInfo[index], lpBrStoppedMachineInfo)){
+ sprintf(PrintBuf,"\nSUCCESS[SC%ld]: New master is OK.: %s \n", ++SUCSCOUNT, UnicodeToPrintfString(wcNewMaster));
+ PrintString(TOALL, PrintBuf);
+
+ }
+
+ wcscpy(wcCurrentMasters[index], wcNewMaster);
+ }
+
+
+ /********************************\
+ * *
+ * Sleep for 15 minutes and then *
+ * retrieve the lists. *
+ * *
+ \********************************/
+
+ //
+ // Sleep for 15 minutes and retrieve the list from master and backup
+ // to check whether it matches the User input.
+ //
+
+ if(!bForceAnn) {
+ sprintf(PrintBuf, "\nSleeping for %ld msecs.\n", UPDATESLEEPTIME);
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ Sleep(UPDATESLEEPTIME);
+
+ } else {
+
+ //
+ // Ask the machines in the domain to announce themselves and
+ // sleep for sometime so the elections and things happen
+ //
+ ForceAnnounce(TestedXportInfo[0].Transport, wcDomainName);
+
+ sprintf(PrintBuf, "\nForceAnnounce. Sleeping for %ld msecs.\n", BASESLEEPTIME*2);
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ Sleep(BASESLEEPTIME*2);
+ }
+
+ //
+ // Find the backup browsers for each transport
+ //
+ //
+
+ for(index = 0; index < iNumOfTestedXports; index++){
+
+ if(_wcsicmp(wcCurrentMasters[index], L"") != 0){
+ ULONG ulNumBackUps = 0;
+ TCHAR wcBackUpBrowsers[MAXBACKUPS][CNSLASHLEN+1];
+
+ if((Status = GetBList(TestedXportInfo[index].Transport, wcDomainName, TRUE, &ulNumBackUps, wcBackUpBrowsers)) != NERR_Success) {
+ sprintf(PrintBuf, "\nERROR[ER%ld]: Unable to get backup list of Domain %s ", ++ERRCOUNT, UnicodeToPrintfString(wcDomainName));
+ PrintString(TOALL, PrintBuf);
+ sprintf(PrintBuf, "Transport %s.\nError: %s", UnicodeToPrintfString(TestedXportInfo[index].Transport.Buffer), get_error_text(Status));
+ PrintString(TOALL, PrintBuf);
+ } else {
+
+ if(ulNumBackUps){
+ sprintf(PrintBuf, "\nBackUp Servers of Domain %s ", UnicodeToPrintfString(wcDomainName));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ sprintf(PrintBuf, " on Transport %s are:\n", UnicodeToPrintfString(TestedXportInfo[index].Transport.Buffer));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ for(i = 0; i<(INT)ulNumBackUps; i++){
+ sprintf(PrintBuf, "%s \n", UnicodeToPrintfString(wcBackUpBrowsers[i]));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ }
+ } else {
+ sprintf(PrintBuf, "\nWARNING[WR%ld]: No BackUp Browsers in Domain %s ", ++WRNCOUNT, UnicodeToPrintfString(wcDomainName));
+ PrintString(TOALL, PrintBuf);
+ sprintf(PrintBuf, " Transport %s.", UnicodeToPrintfString(TestedXportInfo[index].Transport.Buffer));
+ PrintString(TOALL, PrintBuf);
+ } // ulNumBackUps
+ } // GetBList
+
+ //
+ // Check the browser lists of Master and BackUps
+ //
+
+ if(_wcsicmp(wcCurrentMasters[index], L"") != 0)
+ CheckBrowseListsOfMasterAndBackUps(TestedXportInfo[index], wcDomainName, wcCurrentMasters[index], wcBackUpBrowsers, ulNumBackUps, TRUE);
+
+ } // if(wcCurrentMaster)
+
+ } // for(index)
+
+return TRUE;
+}
+
+
+
+
+VOID
+Usage(CHAR *ProgramName)
+{
+
+ printf("\nUsage: %s [/IPXHack:RHS] [[/ForceAnn:RHS] [/SingleD:RHS]] [/StressT:RHS]\n", ProgramName);
+ printf("\n /ipxhack: This parameter is used to specify");
+ printf("\n whether IPX master browser is found");
+ printf("\n using IPX over NetBIOS. (Daytona)");
+ printf("\n RHS can be 1(TRUE) or 0(FALSE).");
+ printf("\n Default: 1.\n");
+
+ printf("\n /ForceAnn: This parameter is used to specify");
+ printf("\n whether server names are forced to");
+ printf("\n to be announced or the test waits");
+ printf("\n for the update period (15 Minutes).");
+ printf("\n Waiting takes too long, but gives ");
+ printf("\n better results.");
+ printf("\n RHS can be 1(TRUE) or 0(FALSE).");
+ printf("\n Default: 0.\n");
+
+ printf("\n /SingleD: This parameter is used to specify");
+ printf("\n whether Single domain test be done (1)");
+ printf("\n or not done(0).");
+ printf("\n RHS can be 1(DONE) or 0(NOT DONE).");
+ printf("\n Default: 1.\n");
+
+ printf("\n /StressT: This parameter is used to specify");
+ printf("\n whether functional tests(0) or stress");
+ printf("\n tests need be done.");
+ printf("\n RHS can be 0(NOT STRESS) or a value");
+ printf("\n specifying time in Minutes for");
+ printf("\n stressing the browser.");
+ printf("\n Range 0 - 1,000,000 Minutes.");
+ printf("\n Default: 0.\n");
+}
diff --git a/private/net/svcdlls/browser2/browtest/browtest.cht b/private/net/svcdlls/browser2/browtest/browtest.cht
new file mode 100644
index 000000000..d6ddb7849
--- /dev/null
+++ b/private/net/svcdlls/browser2/browtest/browtest.cht
@@ -0,0 +1,120 @@
+ Browser Tester
+ ==============
+
+ The browser tester can be used for either
+ 1. Functional testing of the browser service.
+ 2. Stress testing of the browser service.
+
+ The functional or stress testing is done on a set of machines which are
+parts of 1 or more domains. The names of the set of machines are given through
+an input file (BROWTEST.INP). The machine from which this test is run should
+have administrative priveleges on all the (NT) machines specified in the input
+file.
+===============================================================================
+To Run:
+=======
+ I. Functional Test
+ ------------------
+ Files Needed:
+ a) BROWTEST.EXE - On one machine in the Primary domain.
+ b) BROWTEST.INP - On one machine in the Primary domain.
+ c) SHUTSVC.EXE - On the PDC of all other domains tested.
+
+ Steps:
+ ------
+ 1. Set up the test bed such that there is 1 or more domains.
+
+ +--------------------------------------------------------------+
+ | (PDC)(WINS) (PDC) (BDC) |
+ | D1 D1 D1 D2 D1 |
+ | AS3.5 NT3.5 CHICAGO AS3.5 AS3.5 |
+ | | | | | | |
+ | +------------+---------+--------+---- Router ---+ |
+ | | | | | |
+ | AS3.1 NT3.1 WFW311 AS3.5 |
+ | D1 D1 D1 D3 |
+ | (BDC) (PDC) |
+ | |
+ | D1 - Primary Domain, D2 - Domain2 D3 - Domain3 |
+ +--------------------------------------------------------------+
+ Given above is a generic configuration.
+ The test can be done with or without the router.
+ All of the interop machines need not be there in Domain1.
+ WINS can be anywhere.
+ If the router is present, the BDC on the other side should be a AS3.5.
+
+ 2. Make the BROWTEST.INP based on the configuration of the machines.
+ (Read the sample BROWTEST.INP for more details.)
+
+ 3. Copy ShutSvc.exe to the %SystemRoot% of all the PDC's of domains
+ tested, other than the Primary domain (ie. D2, D3).
+
+ 4. From the machine, from which the test is run, get access permissions
+ to all (NT) machines participating in the test.
+
+ 5. Start the test. BROWTEST without any switches will do.
+ (Wait a short while, to see if the test has access permissions)
+
+ The time the test takes to complete depends on the number of
+ protocols tested and the number of machines participating in
+ the test.
+
+ Results:
+ --------
+ Two output files are created. BROWTEST.LOG and BROWTEST.SUM.
+ BROWTEST.SUM has the summary of the test run. There should not be
+ any ERRORS in the summary. Detailed report on errors can be found in
+ BROWTEST.LOG.
+
+
+
+===============================================================================
+To Run:
+=======
+ II. Stress Test
+ ---------------
+
+ Files Needed:
+ a) BROWTEST.EXE - On one machine in the Primary domain.
+ b) BROWTEST.INP - On one machine in the Primary domain.
+
+ Steps:
+ ------
+ 1. Set up the test bed such that there is 1 or more domains.
+
+ +--------------------------------------------------------------+
+ | (PDC)(WINS) (PDC) (BDC) |
+ | D1 D1 D1 D2 D1 |
+ | AS3.5 NT3.5 CHICAGO AS3.5 AS3.5 |
+ | | | | | | |
+ | +------------+---------+--------+---- Router ---+ |
+ | | | | | |
+ | AS3.1 NT3.1 WFW311 AS3.5 |
+ | D1 D1 D1 D3 |
+ | (BDC) (PDC) |
+ | |
+ | D1 - Primary Domain, D2 - Domain2 D3 - Domain3 |
+ +--------------------------------------------------------------+
+ Given above is a generic configuration.
+ The test can be done with or without the router.
+ All of the interop machines need not be there in Domain1.
+ WINS can be anywhere.
+ If the router is present, the BDC on the other side should be a AS3.5.
+
+ 2. Make the BROWTEST.INP based on the configuration of the machines.
+ (Read the sample BROWTEST.INP for more details.)
+
+ 3. From the machine, from which the test is run, get access permissions
+ to all (NT) machines participating in the test.
+
+ 4. Start the test. BROWTEST /StressT:60 will stress for 60 minutes.
+ (Wait a short while, to see if the test has access permissions)
+
+
+ Results:
+ --------
+ Make sure none of the machines crashes in the test. Generally for
+ crashing a machine it takes 7-8hrs of stressing. So run the test
+ overnight.
+ Check the lines printed against "View Thread:". Make sure the SUCCESS
+ line is not all zero's.
diff --git a/private/net/svcdlls/browser2/browtest/browtest.h b/private/net/svcdlls/browser2/browtest/browtest.h
new file mode 100644
index 000000000..a82dfe3db
--- /dev/null
+++ b/private/net/svcdlls/browser2/browtest/browtest.h
@@ -0,0 +1,148 @@
+#ifndef _BROWTEST
+#define _BROWTEST
+
+#ifndef UNICODE
+#define UNICODE
+#endif
+
+#include "browfunc.h"
+
+#define BROWTESTINFILE "browtest.inp"
+#define BROWTESTLOGFILE "browtest.log"
+#define BROWTESTSUMMARYFILE "browtest.sum"
+#define MAXCHARPERLINE 256
+#define MAXTRANSPORTS 16
+
+#define COMMENTCHAR ';'
+#define NEWLINECHAR '\n'
+#define QUOTECHAR '"'
+#define SPACECHAR ' '
+
+#define COMMASTR ","
+#define NEWLINESTR "\n"
+#define QUOTESTR "\""
+#define SPACETABSTR " \t"
+
+#define SUBNET1 1
+#define SUBNET2 2
+
+#define BROWSER TEXT("BROWSER")
+#define NETLOGON TEXT("NETLOGON")
+#define WORKSTATION TEXT("LanmanWorkstation")
+
+#define CNSLASHLEN CNLEN+2 // ComputerName with "\\"
+
+#define BASESLEEPTIME (40*1000) // 40 sec's
+#define UPDATESLEEPTIME (15*60*1000) // 15 minutes
+
+#define TOSCREEN 0x1UL
+#define TOLOGFILE 0x2UL
+#define TOSUMMARYFILE 0x4UL
+
+#define TOSCREENANDLOG (TOSCREEN | TOLOGFILE)
+#define TOLOGANDSUMMARY (TOLOGFILE | TOSUMMARYFILE)
+#define TOALL (TOSCREEN | TOLOGFILE | TOSUMMARYFILE)
+
+
+#define MAXPROTOCOLS 5
+#define MAXOSTYPES 8
+#define MAXTESTEDDOMAINS 5
+
+typedef struct _OSProp{
+ CHAR *Type;
+ INT iPreference;
+ }OSPROP;
+
+
+typedef struct _MACHINEINFO{
+ TCHAR wcDomainName[DNLEN+1];
+ TCHAR wcMachineName[CNLEN+1];
+ INT iOsType;
+ INT iOsPreference;
+ DWORD dwServerBits;
+ INT iSubnet;
+ INT Protocols[MAXPROTOCOLS];
+ BOOL BrowserServiceStarted;
+ struct _MACHINEINFO *Next;
+ }MACHINEINFO, *LPMACHINEINFO;
+
+//
+// Heads of the lists for the 2 subnets
+//
+
+typedef struct _XportInfo{
+ UNICODE_STRING Transport;
+ INT index;
+ }XPORTINFO;
+
+
+
+typedef struct _DOMAININFO{
+ TCHAR wcDomainName[DNLEN+1];
+ LPMACHINEINFO lpMInfo;
+ }DOMAININFO, *LPDOMAININFO;
+
+
+
+#define VALUETYPE_INTEGER 1
+#define VALUETYPE_ULONG 2
+#define VALUETYPE_STRING 3
+#define VALUETYPE_IGNORE 4
+#define VALUETYPE_HELP 5
+#define VALUETYPE_BOOL 6
+
+typedef struct _COMMANDTABLE {
+ CHAR Command[10];
+ INT ValueType ;
+ VOID *Value ;
+} COMMAND_TABLE;
+
+
+#define SHUTSVCPATH TEXT("%SystemRoot%\\shutsvc.exe")
+#define SHUTSVCNAME TEXT("BrShut_serv")
+
+#define STOPANDSTARTRDR 0
+#define REBOOTMACHINE 1
+
+
+#define NTOSTYPESBASE 4
+#define ASOSTYPESBASE 6
+#define IsNTMachine(a)((a->iOsType >= NTOSTYPESBASE) ? TRUE : FALSE)
+#define IsASMachine(a)((a->iOsType >= ASOSTYPESBASE) ? TRUE : FALSE)
+
+
+#define PrintString(FLAG, PrintStr) { \
+ if(WaitForSingleObject(ConsoleMutex, INFINITE) != WAIT_FAILED) { \
+ if(FLAG & TOSCREEN) printf("%s", PrintStr); \
+ if(FLAG & TOLOGFILE) fprintf(fplog,"%s", PrintStr); \
+ if(FLAG & TOSUMMARYFILE) fprintf(fpsum,"%s", PrintStr); \
+ fflush(NULL); \
+ ReleaseMutex( ConsoleMutex ); \
+ } \
+ }
+
+
+VOID AddToList(LPMACHINEINFO, LPMACHINEINFO);
+BOOL CheckAccessPermissionOnAllMachines();
+VOID CheckBrowseListsOfMasterAndBackUps(XPORTINFO, LPTSTR, LPTSTR, TCHAR [MAXBACKUPS][CNSLASHLEN+1], ULONG, BOOL);
+BOOL CheckBrServiceOnMachinesInList();
+VOID CleanMem();
+BOOL DoesMachineHaveThisTransport(LPTSTR, XPORTINFO, LPMACHINEINFO);
+VOID DomSpanningMulSubNetsTests(XPORTINFO *, INT);
+BOOL DoSingleDomainTests(XPORTINFO *, INT);
+BOOL FindAllTransports(UNICODE_STRING *, DWORD *);
+VOID ForceElectionAndFindWhoWins(XPORTINFO *, INT, LPTSTR, TCHAR [MAXPROTOCOLS][CNLEN+1], INT);
+VOID Initialize(UNICODE_STRING *, INT *);
+BOOL LocalMachineIsMultihomed(UNICODE_STRING *, INT);
+BOOL NewMasterIsCorrect(TCHAR [MAXPROTOCOLS][CNLEN+1], INT, LPTSTR, XPORTINFO, LPMACHINEINFO);
+VOID ParseCommandLine(INT, CHAR **);
+VOID PrintList(MACHINEINFO);
+
+BOOL StartBrowserFunctionalTest(UNICODE_STRING *, INT);
+BOOL StopBrowserOnCurrentMaster(LPTSTR, LPTSTR, XPORTINFO *,
+ INT, TCHAR [MAXPROTOCOLS][CNLEN+1]);
+BOOL StopBrowsersAndFindWhoBecomesMaster(LPTSTR, LPTSTR, XPORTINFO *,
+ INT, TCHAR [MAXPROTOCOLS][CNLEN+1]);
+VOID Usage(CHAR *);
+
+#endif
diff --git a/private/net/svcdlls/browser2/browtest/browtest.inp b/private/net/svcdlls/browser2/browtest/browtest.inp
new file mode 100644
index 000000000..8e07ac52d
--- /dev/null
+++ b/private/net/svcdlls/browser2/browtest/browtest.inp
@@ -0,0 +1,56 @@
+; Browtest.inp
+;
+; This file serves as the input to the browser tester.
+; It gives information on the machines participating in the test.
+;
+; +--------------------------------------------------------------+
+; | (PDC)(WINS) (PDC) (BDC) |
+; | D1 D1 D1 D2 D1 |
+; | AS3.5 NT3.5 CHICAGO AS3.5 AS3.5 |
+; | | | | | | |
+; | +------------+---------+--------+---- Router ---+ |
+; | | | | | |
+; | AS3.1 NT3.1 WFW311 AS3.5 |
+; | D1 D1 D1 D3 |
+; | (BDC) (PDC) |
+; | |
+; | D1 - Primary Domain, D2 - Domain2 D3 - Domain3 |
+; +--------------------------------------------------------------+
+; Given above is a generic configuration.
+; The test can be done with or without the router.
+; All of the interop machines need not be there in Domain1.
+; WINS can be anywhere.
+; If the router is present, the BDC on the other side should be a AS3.5.
+;
+;
+;
+; Format:
+;
+; DomainName MachineName Type Subnet Protocols
+; eg:
+; D™MI¥ "Green 5" AS3.5 1 TCP, IPX, NBIPX
+; DOMAIN1 Red1 NT3.1 2 IPX, NBIPX
+;
+; MachineName: should be specified with "" if there is a space in the name.
+; Type : AS3.5, AS3.1, NT3.5, NT3.1, CHICAGO, WFW3.11, OS2, DOSLM
+; Subnet : 1 for this subnet, 2 for other subnet.
+; Protocols : TCP, IPX, NBIPX, NETBEUI, XNS
+;
+; Note:
+; a) This test will be done only for the protocols that are installed
+; on the server, on which the test is run. More protocols specified will
+; be ignored. For stress test, all installed protocols will be tested.
+; b) Currently, multihomed machines cannot be tested.
+; c) Only Machinenames are expected in Double quotes.
+; d) Protocols should be seperated by commas.
+;
+;DomainName MachineName Type Subnet Protocols
+ DOMŽDOM’’ ANILTH_2 NT3.5 1 NBIPX, IPX, TCP, NETBEUI
+ DOMŽDOM’’ ANILTH_3 AS3.5 1 NBIPX,IPX, NETBEUI, TCP
+; DOMŽDOM’’ ORANGE6 NT3.5 1 NBIPX,IPX, NETBEUI, TCP
+; DOMŽDOM’’ yellow5 wfw3.11 1 IPX, TCP
+; DOMŽDOM’’ yell6 chicago 1 IPX, NETBEUI
+ BROWDOM2 GREEN5 AS3.5 1 NBIPX, IPX, TCP, NETBEUI
+
+
+
diff --git a/private/net/svcdlls/browser2/browtest/browutil.c b/private/net/svcdlls/browser2/browtest/browutil.c
new file mode 100644
index 000000000..1f8a62a51
--- /dev/null
+++ b/private/net/svcdlls/browser2/browtest/browutil.c
@@ -0,0 +1,780 @@
+#include "browutil.h"
+
+
+extern CHAR PrintBuf[1024];
+extern MACHINEINFO HeadList1;
+
+extern TCHAR wcTESTEDDOMAINS[MAXTESTEDDOMAINS][DNLEN+1];
+extern INT iNumOfTestedDomains;
+extern FILE *fplog;
+extern FILE *fpsum;
+
+extern DWORD ERRCOUNT;
+extern DWORD WRNCOUNT;
+extern DWORD TESTCOUNT;
+extern DWORD SUCSCOUNT;
+extern DWORD OKCOUNT;
+
+extern enum E_PROTOCOLS {IPX, NBIPX, TCP, XNS, NETBEUI};
+extern CHAR *PROTOCOLS[MAXPROTOCOLS];
+
+extern BOOL bIPXHack;
+
+extern LPMACHINEINFO lpLocMachineInfo;
+extern LPMACHINEINFO lpDomStart;
+extern DOMAININFO LocDomInfo;
+
+extern OSPROP OSTYPES[MAXOSTYPES];
+
+extern HANDLE ConsoleMutex;
+
+
+//
+// Compare the browse lists of the master and the backups.
+//
+
+VOID
+CompareListsMasterAndBackUp(PVOID pvMsServerList,
+ DWORD dwEntriesInList,
+ PVOID pvBkServerList,
+ DWORD dwEntriesInBackList,
+ LPTSTR wcDomainName,
+ LPTSTR wcMasterName,
+ LPTSTR wcBackUpBrowser,
+ LPTSTR wcTestedTransport)
+{
+INT i, j;
+PSERVER_INFO_101 pServerInfo101_l1;
+PSERVER_INFO_101 pServerInfo101_l2;
+
+ if(dwEntriesInList != dwEntriesInBackList){
+ sprintf(PrintBuf, "\nThe List in Master and BackUp differs by %ld. Master : %s ", abs(dwEntriesInList - dwEntriesInBackList),
+ UnicodeToPrintfString(wcMasterName));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ sprintf(PrintBuf, " BackUp: %s ",UnicodeToPrintfString(wcBackUpBrowser));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ sprintf(PrintBuf, " Transport: %s.",UnicodeToPrintfString(wcTestedTransport));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+
+ sprintf(PrintBuf, "\nWARNING[WR%ld]: The List in Master and BackUp doesnot match. Transport : %s.", ++WRNCOUNT,
+ UnicodeToPrintfString(wcTestedTransport));
+ PrintString(TOALL, PrintBuf);
+
+ } else {
+ sprintf(PrintBuf, "\nSUCCESS[SC%ld]: The List in Master and BackUp Matches. Transport : %s.", ++SUCSCOUNT,
+ UnicodeToPrintfString(wcTestedTransport));
+ PrintString(TOALL, PrintBuf);
+
+ }
+
+ //
+ // Note : Needs to add the rest of the checking
+ //
+
+}
+
+
+
+//
+// Compare the browse lists of the master to the input file.
+//
+
+VOID
+CompareListsMasterAndFile(PVOID pvServerList,
+ DWORD dwEntriesInList,
+ LPTSTR wcDomainName,
+ LPTSTR wcMachineName,
+ XPORTINFO XportInfo,
+ BOOL bSingleSubnet
+ )
+{
+INT i, index;
+LPMACHINEINFO lpMachineInfo;
+DWORD dwNumNotFound = 0;
+DWORD dwNumWrongFind = 0;
+DWORD dwNumMachinesWithProtocol = 0;
+PSERVER_INFO_101 pServerInfo101;
+
+ sprintf(PrintBuf, "\n\nComparing the Lists from %s to the Input File ",
+ UnicodeToPrintfString(wcMachineName));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+
+ sprintf(PrintBuf, "on transport %s.\n",
+ UnicodeToPrintfString(XportInfo.Transport.Buffer));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+
+ index = XportInfo.index;
+ //
+ // Check if all the servers from the input file are there in the list.
+ //
+ pServerInfo101 = pvServerList;
+ lpMachineInfo = lpDomStart;
+
+ while(lpMachineInfo &&
+ _wcsicmp(lpMachineInfo->wcDomainName,wcDomainName) == 0){
+
+ BOOL MachineHasProtocol = FALSE;
+ MachineHasProtocol = DoesMachineHaveThisTransport(lpMachineInfo->wcMachineName, XportInfo, lpMachineInfo);
+ if(MachineHasProtocol)
+ dwNumMachinesWithProtocol++;
+
+ {
+ //
+ // The ServerNames in pServerInfo is compared with the Input file
+ //
+ i = 0;
+ while (i < (INT)dwEntriesInList && (_wcsicmp(lpMachineInfo->wcMachineName, pServerInfo101[i].sv101_name) != 0))
+ i++;
+
+ //
+ // Not Found
+ //
+ if(i >= (INT)dwEntriesInList){
+ if(MachineHasProtocol){
+ //
+ // For TCP only one subnet info is available if browser is
+ // not running on PDC
+ //
+ if(index == TCP){
+ if(!((lpMachineInfo->iSubnet != SUBNET1) && bSingleSubnet)) {
+ sprintf(PrintBuf, "\nMachine not in Retrieved List: %s.",
+ UnicodeToPrintfString(lpMachineInfo->wcMachineName));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ dwNumNotFound++;
+ }
+ }else{
+ sprintf(PrintBuf, "\nMachine not in Retrieved List: %s.",
+ UnicodeToPrintfString(lpMachineInfo->wcMachineName));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ dwNumNotFound++;
+ }
+ }
+ } else {
+ if(!MachineHasProtocol){
+ sprintf(PrintBuf, "\nMachine %s is NOT expected in Retrieved List!",
+ UnicodeToPrintfString(lpMachineInfo->wcMachineName));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ dwNumWrongFind++;
+ }
+ lpMachineInfo->dwServerBits = pServerInfo101[i].sv101_type;
+ }
+ }
+
+ lpMachineInfo = lpMachineInfo->Next;
+ }
+
+ if(dwNumNotFound){
+ sprintf(PrintBuf,"\nWARNING[WR%ld]: %ld Machines (is)are not in Retrieved List on %s Transport.\n", ++WRNCOUNT, dwNumNotFound,
+ UnicodeToPrintfString(XportInfo.Transport.Buffer));
+ PrintString(TOALL, PrintBuf);
+ } else {
+ sprintf(PrintBuf,"\nSUCCESS[SC%ld]: Matched all machines in input file on transport %s.\n", ++SUCSCOUNT,
+ UnicodeToPrintfString(XportInfo.Transport.Buffer));
+ PrintString(TOALL, PrintBuf);
+ }
+
+ if(dwNumWrongFind){
+ sprintf(PrintBuf,"\n\nWARNING[WR%ld]: %ld Machines Wrongly found in Retrieved List on %s Transport.\n", ++WRNCOUNT, dwNumWrongFind,
+ UnicodeToPrintfString(XportInfo.Transport.Buffer));
+ PrintString(TOALL, PrintBuf);
+ }
+
+}
+
+
+ //
+ // Find the Master for a domain for a transport
+ //
+
+NET_API_STATUS
+GetMasterName( XPORTINFO *TestedXportInfo,
+ INT iNumOfTestedXports,
+ INT index,
+ LPTSTR Domain,
+ LPTSTR Master)
+{
+NET_API_STATUS Status;
+
+ //
+ // For IPX we need a hack. GetNetBiosMasterName will fail in Daytona (NT3.5)
+ // and NT3.1. So we find the BackUp on NwlnkIpx , retrieve the List from one
+ // of those through NwLnkNb and see who is the master in the List
+ //
+ if((TestedXportInfo[index].index == IPX) && bIPXHack){
+
+ TCHAR wcBackUpBrowsers[MAXBACKUPS][CNSLASHLEN+1];
+ INT i, ibcount=0;
+ ULONG ulNumBackUps = 0;
+ DWORD dwEntriesInList;
+ DWORD dwTotalEntries;
+ PVOID pvServerList;
+ PSERVER_INFO_101 pServerInfo101_l1;
+
+ //
+ // If the local browse master is not started then we cannot get the backup
+ // list in nwlnkipx so we use nwlnknb.
+ //
+ if(lpLocMachineInfo->BrowserServiceStarted)
+ Status = GetBList(TestedXportInfo[index].Transport, Domain, TRUE, &ulNumBackUps, wcBackUpBrowsers);
+ else {
+ for(i = 0; i < iNumOfTestedXports && TestedXportInfo[i].index != NBIPX; i++);
+ if(i < iNumOfTestedXports)
+ Status = GetBList(TestedXportInfo[i].Transport, Domain, TRUE, &ulNumBackUps, wcBackUpBrowsers);
+ else
+ return(!NERR_Success);
+ }
+
+ if(Status == NERR_Success) {
+
+ if(ulNumBackUps){
+
+ do{
+
+ if((Status = RetrieveList(wcBackUpBrowsers[ibcount], L"\\Device\\NwlnkNb", &pvServerList,
+ &dwEntriesInList, &dwTotalEntries, (SV_TYPE_ALTERNATE_XPORT | SV_TYPE_MASTER_BROWSER), NULL, NULL, FALSE)) == NERR_Success){
+
+ if(dwEntriesInList) {
+ pServerInfo101_l1 = pvServerList;
+ wcscpy(Master, pServerInfo101_l1[0].sv101_name);
+
+ } else {
+
+ Status = (!NERR_Success);
+ }
+
+ }// Retrieve List
+
+ NetApiBufferFree(pvServerList);
+ ibcount++;
+
+ }while((Status != NERR_Success) && (ibcount < (INT)ulNumBackUps));
+
+ } else // If got any BackUp
+ return (!NERR_Success);
+
+ } // If GetBList
+
+ return Status;
+ }
+
+ //
+ // For TCP we need to clear the local cache before making a new call
+ // to GetNetBiosMasterName
+ //
+ if(TestedXportInfo[index].index == TCP)
+// system("NbtStat -R");
+ ClearNbtNameTableCache(TestedXportInfo[index].Transport);
+
+
+ Status = GetNetBiosMasterName(
+ TestedXportInfo[index].Transport.Buffer,
+ Domain,
+ Master,
+ NULL);
+
+
+
+ return Status;
+}
+
+
+//
+// Are there any browsers servers available to be a master.
+//
+BOOL
+MasterAvailable(XPORTINFO XportInfo,
+ LPTSTR wcDomainName,
+ INT iSubnet)
+{
+INT index;
+LPMACHINEINFO lpMachineInfo;
+
+ index = XportInfo.index;
+ //
+ // We check whether the machine has the protocol. If the protocol is TCP we
+ // have to check whether it is in the same subnet.If it has then see if it
+ // is an NT. If it is not then return TRUE because the Low level machine
+ // could become a Master browser. If it is NT then check to see whether
+ // Browser is started on it and if so then
+ //
+ for(lpMachineInfo=lpDomStart; lpMachineInfo && _wcsicmp(lpMachineInfo->wcDomainName,
+ wcDomainName) == 0; lpMachineInfo=lpMachineInfo->Next){
+
+ if(lpMachineInfo->Protocols[index]){
+
+ if((index == TCP) && (lpMachineInfo->iSubnet != iSubnet))
+ continue;
+
+ if(IsNTMachine(lpMachineInfo)){
+ if(lpMachineInfo->BrowserServiceStarted) {
+ sprintf(PrintBuf, "\nMaster Available %s.",UnicodeToPrintfString(lpMachineInfo->wcMachineName));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ return TRUE;
+ }
+ } else { // Not an NT
+ //
+ // For IPX we cannot get the Master since there are no masters
+ // running nwlnknb.
+ //
+ if(bIPXHack && (index == IPX))
+ continue;
+ else{
+ sprintf(PrintBuf, "\nMaster Available %s.",UnicodeToPrintfString(lpMachineInfo->wcMachineName));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ return TRUE;
+ }
+ }
+ }
+ }
+
+return FALSE;
+}
+
+
+BOOL
+ReadInputFile(VOID)
+{
+INT i;
+CHAR cbReadBuf[MAXCHARPERLINE];
+CHAR cbTmpBuf[16];
+PCHAR lpTmpPtr;
+PCHAR lpTokPtr;
+FILE *fpin;
+LPMACHINEINFO lpMachineInfo;
+
+ if((fpin = fopen(BROWTESTINFILE, "r")) == NULL){
+ sprintf(PrintBuf,"\nError opening the input file %s!!\n", BROWTESTINFILE);
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ return FALSE;
+ }
+
+ while(fgets(cbReadBuf, MAXCHARPERLINE, fpin) != NULL){
+ BOOL QUOTEFOUND = FALSE;
+
+ //
+ // Skip lines starting with the COMMENTCHAR sign
+ //
+ for(lpTmpPtr = cbReadBuf; (*lpTmpPtr) == SPACECHAR; lpTmpPtr++);
+ if((*lpTmpPtr) == COMMENTCHAR || (*lpTmpPtr) == NEWLINECHAR){
+ continue;
+ }
+
+
+// printf("%s", cbReadBuf);
+ //
+ // Check if Machine name is given in quotes
+ //
+ for(lpTokPtr = lpTmpPtr; ((*lpTokPtr) != '\0') &&
+ ((*lpTokPtr) != QUOTECHAR); lpTokPtr++);
+
+ if((*lpTokPtr) == QUOTECHAR)
+ QUOTEFOUND = TRUE;
+
+ //
+ // Get the domain name
+ //
+ if((lpTokPtr = strtok(lpTmpPtr, SPACETABSTR)) == NULL){
+ printf("Error in parsing the Domain Name: %s", cbReadBuf);
+ fclose(fpin);
+ return FALSE;
+ }
+
+ //
+ // Allocate the Machine Info Structure
+ //
+ if((lpMachineInfo = (LPMACHINEINFO) calloc(1, sizeof(MACHINEINFO))) == NULL){
+ printf("\nError. Not enough memory for MachineInfo\n");
+ fclose(fpin);
+ return FALSE;
+ }
+ memset(lpMachineInfo, 0, sizeof(MACHINEINFO));
+
+// RemoveTabs(&lpTokPtr);
+ MultiByteToWideChar(CP_OEMCP, MB_PRECOMPOSED, lpTokPtr, strlen(lpTokPtr)+1, lpMachineInfo->wcDomainName, sizeof(lpMachineInfo->wcDomainName));
+// printf("Domain: XXX%sXXX\n", UnicodeToPrintfString(lpMachineInfo->wcDomainName));
+
+ //
+ // Get the machine name
+ //
+
+ if(QUOTEFOUND){
+ //
+ // Machine name given in quotes
+ //
+ if((lpTokPtr = strtok(NULL, QUOTESTR)) == NULL){
+ printf("(1)Error in parsing the Machine Name: %s", cbReadBuf);
+ free(lpMachineInfo);
+ fclose(fpin);
+ return FALSE;
+ }
+
+ if((lpTokPtr = strtok(NULL, QUOTESTR)) == NULL){
+ printf("(2)Error in parsing the Machine Name: %s", cbReadBuf);
+ free(lpMachineInfo);
+ fclose(fpin);
+ return FALSE;
+ }
+
+ MultiByteToWideChar(CP_OEMCP, MB_PRECOMPOSED, lpTokPtr, strlen(lpTokPtr)+1, lpMachineInfo->wcMachineName, sizeof(lpMachineInfo->wcMachineName));
+
+ } else {
+
+ //
+ // Machine name not in quotes
+ //
+ if((lpTokPtr = strtok(NULL, SPACETABSTR)) == NULL){
+ printf("(3)Error in parsing the Machine Name: %s", cbReadBuf);
+ free(lpMachineInfo);
+ fclose(fpin);
+ return FALSE;
+ }
+
+// RemoveTabs(&lpTokPtr);
+ MultiByteToWideChar(CP_OEMCP, MB_PRECOMPOSED, lpTokPtr, strlen(lpTokPtr)+1, lpMachineInfo->wcMachineName, sizeof(lpMachineInfo->wcMachineName));
+ }
+
+// printf("MachineName: XXX%sXXX\n", UnicodeToPrintfString(lpMachineInfo->wcMachineName));
+
+ //
+ // Get the type (eg: AS3.5)
+ //
+ if((lpTokPtr = strtok(NULL, SPACETABSTR)) == NULL){
+ printf("Error in parsing the Type. Machine: %s", UnicodeToPrintfString(lpMachineInfo->wcMachineName));
+ free(lpMachineInfo);
+ fclose(fpin);
+ return FALSE;
+ }
+
+// RemoveTabs(&lpTokPtr);
+ for(i=0; i<MAXOSTYPES && _strnicmp(lpTokPtr, OSTYPES[i].Type, strlen(OSTYPES[i].Type)) != 0; i++);
+ if(i < MAXOSTYPES){
+ lpMachineInfo->iOsType = i;
+ lpMachineInfo->iOsPreference = OSTYPES[i].iPreference;
+
+// printf("ZZZ%sZZZ OSTYPE=%s\n", lpTokPtr, OSTYPES[i]);
+ } else {
+ printf("\nError unknown OsType: %s\n", lpTokPtr);
+ free(lpMachineInfo);
+ fclose(fpin);
+ return FALSE;
+ }
+
+ //
+ // Get the Subnet
+ //
+ if((lpTokPtr = strtok(NULL, SPACETABSTR)) == NULL){
+ printf("Error in parsing the Subnet. Machine: %s", UnicodeToPrintfString(lpMachineInfo->wcMachineName));
+ free(lpMachineInfo);
+ fclose(fpin);
+ return FALSE;
+ }
+
+// RemoveTabs(&lpTokPtr);
+ lpMachineInfo->iSubnet = atoi(lpTokPtr);
+// printf("Subnet: XXX%dXXX\n", atoi(lpTokPtr));
+ if(!(lpMachineInfo->iSubnet == SUBNET1 || lpMachineInfo->iSubnet == SUBNET2)){
+ printf("\nUnknown Subnet specified: %d\n", lpMachineInfo->iSubnet);
+ fclose(fpin);
+ return FALSE;
+ }
+
+ //
+ // Get the list of protocols
+ //
+ if((lpTokPtr = strtok(NULL, NEWLINESTR)) == NULL){
+ printf("Error in parsing the Protocols. Machine: %s", UnicodeToPrintfString(lpMachineInfo->wcMachineName));
+ free(lpMachineInfo);
+ fclose(fpin);
+ return FALSE;
+ }
+
+
+ RemoveTabs(&lpTokPtr);
+ lpTmpPtr = lpTokPtr;
+ if((lpTokPtr = strtok(lpTmpPtr, COMMASTR)) == NULL){
+ printf("Error in parsing the first protocol. Machine: %s", UnicodeToPrintfString(lpMachineInfo->wcMachineName));
+ free(lpMachineInfo);
+ fclose(fpin);
+ return FALSE;
+ }
+ for(lpTmpPtr = lpTokPtr; (*lpTmpPtr != '\0') && (*lpTmpPtr == SPACECHAR); lpTmpPtr++);
+ RemoveTabs(&lpTmpPtr);
+ for(i=0; i<MAXPROTOCOLS && _strnicmp(lpTmpPtr, PROTOCOLS[i], strlen(PROTOCOLS[i])) != 0; i++);
+ if(i < MAXPROTOCOLS){
+ lpMachineInfo->Protocols[i] = TRUE;
+// printf("ZZZ%sZZZ PROTOCOL=%s\n", lpTmpPtr, PROTOCOLS[i]);
+ }
+ else {
+ printf("\n(1)Error unknown Protocol: %s\n", lpTmpPtr);
+ free(lpMachineInfo);
+ fclose(fpin);
+ return FALSE;
+ }
+
+
+ while((lpTokPtr = strtok(NULL, COMMASTR)) != NULL){
+ for(lpTmpPtr = lpTokPtr; (*lpTmpPtr != '\0') && (*lpTmpPtr == SPACECHAR); lpTmpPtr++);
+ RemoveTabs(&lpTmpPtr);
+ for(i=0; i<MAXPROTOCOLS && _strnicmp(lpTmpPtr, PROTOCOLS[i], strlen(PROTOCOLS[i])) != 0; i++);
+ if(i < MAXPROTOCOLS){
+ lpMachineInfo->Protocols[i] = TRUE;
+// printf("ZZZ%sZZZ PROTOCOL=%s\n", lpTmpPtr, PROTOCOLS[i]);
+ } else {
+ printf("\n(2)Error unknown Protocol: %s\n", lpTmpPtr);
+ free(lpMachineInfo);
+ fclose(fpin);
+ return FALSE;
+ }
+ }
+
+ AddToList(&HeadList1, lpMachineInfo);
+
+
+ } // while(fgets)
+
+fclose(fpin);
+return TRUE;
+}
+
+
+VOID
+RemoveTabs(PCHAR *lpTokenStr)
+{
+PCHAR lpTmpPtr;
+PCHAR lpString;
+
+
+ lpString = *lpTokenStr;
+
+ //
+ // Remove beginning tabs
+ //
+ while(*lpString == '\t' && *lpString != '\0')
+ lpString++;
+
+ *lpTokenStr = lpString;
+
+ //
+ // empty string
+ //
+ if (*lpString == '\0')
+ return;
+
+ //
+ // Point to the last character in the string
+ //
+ lpTmpPtr = lpString + strlen(lpString) - 1;
+
+
+ while(*lpTmpPtr == '\t' && lpTmpPtr != lpString){
+ lpTmpPtr--;
+ }
+
+
+ *(++lpTmpPtr) = '\0';
+
+}
+
+
+NET_API_STATUS
+RetrieveList(LPTSTR wcMachineName,
+ LPTSTR wcTestedTransport,
+ LPVOID *ppvList,
+ LPDWORD pdwEntriesInList,
+ LPDWORD pdwTotalEntries,
+ DWORD Flag,
+ LPTSTR wcDomain,
+ LPDWORD pdwHandle,
+ BOOL ErrMsg
+ )
+{
+DWORD dwStartTime;
+DWORD dwEndTime;
+TCHAR wcTmpName[CNSLASHLEN+1];
+NET_API_STATUS Status;
+
+ if(_wcsnicmp(wcMachineName, L"\\\\", 2) != 0) {
+ wcscpy(wcTmpName, L"\\\\");
+ wcscat(wcTmpName, wcMachineName);
+
+ } else {
+ wcscpy(wcTmpName, wcMachineName);
+ }
+
+ dwStartTime = GetTickCount();
+ Status = RxNetServerEnum(wcTmpName,
+ wcTestedTransport,
+ 101,
+ (LPBYTE *)ppvList,
+ 0xffffffff,
+ pdwEntriesInList,
+ pdwTotalEntries,
+ Flag,
+ wcDomain,
+ NULL
+ );
+
+ dwEndTime = GetTickCount();
+
+ if(ErrMsg){
+ if (Status != NERR_Success) {
+ sprintf(PrintBuf,"\nERROR[ER%ld]:Unable to retrieve List from %s ", ++ERRCOUNT, UnicodeToPrintfString(wcTmpName));
+ PrintString(TOALL, PrintBuf);
+ sprintf(PrintBuf,"on transport %s with Flag %lx. \nError: %s (%ld milliseconds)\n", UnicodeToPrintfString(wcTestedTransport), Flag, get_error_text(Status), dwEndTime - dwStartTime);
+ PrintString(TOALL, PrintBuf);
+
+// if (Status != ERROR_MORE_DATA) {
+// exit(1);
+// }
+ } else {
+ sprintf(PrintBuf,"\nINFO:Retrieved List from %s ", UnicodeToPrintfString(wcTmpName));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ sprintf(PrintBuf,"on transport %s with Flag %lx: (%ld milliseconds).", UnicodeToPrintfString(wcTestedTransport), Flag, dwEndTime - dwStartTime);
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ }
+ }
+
+ return Status;
+}
+
+
+NET_API_STATUS
+StartBrowserService(LPTSTR wcMachineName)
+{
+DWORD LastError;
+SC_HANDLE ServiceControllerHandle;
+SC_HANDLE ServiceHandle;
+
+ sprintf(PrintBuf,"\nTrying to start Browser on %s.", UnicodeToPrintfString(wcMachineName));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+
+ ServiceControllerHandle = OpenSCManager(wcMachineName, NULL, SC_MANAGER_ALL_ACCESS);
+ if (ServiceControllerHandle == NULL) {
+ return GetLastError();
+ }
+
+ ServiceHandle = OpenService(ServiceControllerHandle, BROWSER, SERVICE_ALL_ACCESS);
+ if (ServiceHandle == NULL) {
+ CloseServiceHandle(ServiceControllerHandle);
+ return GetLastError();
+ }
+
+
+ if(!StartService(ServiceHandle, 0, NULL)){
+ CloseServiceHandle(ServiceHandle);
+ CloseServiceHandle(ServiceControllerHandle);
+
+ LastError = GetLastError();
+
+ if(LastError != ERROR_SERVICE_ALREADY_RUNNING)
+ return LastError;
+ else {
+ sprintf(PrintBuf,"\nBrowser already running on %s.\n", UnicodeToPrintfString(wcMachineName));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ return NERR_Success;
+ }
+ }
+
+ sprintf(PrintBuf,"\nStarted Browser on %s.\n", UnicodeToPrintfString(wcMachineName));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+
+ CloseServiceHandle(ServiceHandle);
+ CloseServiceHandle(ServiceControllerHandle);
+ return NERR_Success;
+}
+
+
+NET_API_STATUS
+StopBrowserService(LPTSTR wcMachineName)
+{
+DWORD LastError;
+SC_HANDLE ServiceControllerHandle;
+SC_HANDLE ServiceHandle;
+SERVICE_STATUS ssServiceStatus;
+
+ sprintf(PrintBuf,"\nTrying to stop Browser on %s.", UnicodeToPrintfString(wcMachineName));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+
+ ServiceControllerHandle = OpenSCManager(wcMachineName, NULL, SC_MANAGER_ALL_ACCESS);
+ if (ServiceControllerHandle == NULL) {
+ return GetLastError();
+ }
+
+ ServiceHandle = OpenService(ServiceControllerHandle, BROWSER, SERVICE_ALL_ACCESS);
+ if (ServiceHandle == NULL) {
+ CloseServiceHandle(ServiceControllerHandle);
+ return GetLastError();
+ }
+
+
+ if(!ControlService(ServiceHandle, SERVICE_CONTROL_STOP, &ssServiceStatus)){
+ LastError = GetLastError();
+ if(LastError != ERROR_SERVICE_NOT_ACTIVE){
+ CloseServiceHandle(ServiceHandle);
+ CloseServiceHandle(ServiceControllerHandle);
+ return LastError;
+ } else {
+ sprintf(PrintBuf,"\nService not started on %s.\n", UnicodeToPrintfString(wcMachineName));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ CloseServiceHandle(ServiceHandle);
+ CloseServiceHandle(ServiceControllerHandle);
+ return NERR_Success;
+ }
+ }
+
+ if(!((ssServiceStatus.dwCurrentState == SERVICE_STOP_PENDING) ||
+ (ssServiceStatus.dwCurrentState == SERVICE_STOPPED))){
+
+ sprintf(PrintBuf,"\nCould not stop Browser service on %s\n", UnicodeToPrintfString(wcMachineName));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+ CloseServiceHandle(ServiceHandle);
+ CloseServiceHandle(ServiceControllerHandle);
+ return (!NERR_Success);
+ }
+
+
+
+ sprintf(PrintBuf,"\nStopped Browser service on %s.\n", UnicodeToPrintfString(wcMachineName));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+
+ CloseServiceHandle(ServiceHandle);
+ CloseServiceHandle(ServiceControllerHandle);
+ return NERR_Success;
+}
+
+
+NET_API_STATUS
+QueryBrowserServiceStatus(LPMACHINEINFO lpMachineInfo, LPSERVICE_STATUS lpssServiceStatus)
+{
+SC_HANDLE ServiceControllerHandle;
+SC_HANDLE ServiceHandle;
+
+ sprintf(PrintBuf,"\n\nCheck whether Browser is started on %s.", UnicodeToPrintfString(lpMachineInfo->wcMachineName));
+ PrintString(TOSCREENANDLOG, PrintBuf);
+
+ ServiceControllerHandle = OpenSCManager(lpMachineInfo->wcMachineName, NULL, SC_MANAGER_ALL_ACCESS);
+ if (ServiceControllerHandle == NULL) {
+ return GetLastError();
+ }
+
+ ServiceHandle = OpenService(ServiceControllerHandle, BROWSER, SERVICE_ALL_ACCESS);
+ if (ServiceHandle == NULL) {
+ CloseServiceHandle(ServiceControllerHandle);
+ return GetLastError();
+ }
+
+
+ if(!QueryServiceStatus(ServiceHandle, lpssServiceStatus)){
+ CloseServiceHandle(ServiceHandle);
+ CloseServiceHandle(ServiceControllerHandle);
+ return GetLastError();
+ }
+
+
+ CloseServiceHandle(ServiceHandle);
+ CloseServiceHandle(ServiceControllerHandle);
+ return NERR_Success;
+}
+
+
+
diff --git a/private/net/svcdlls/browser2/browtest/browutil.h b/private/net/svcdlls/browser2/browtest/browutil.h
new file mode 100644
index 000000000..d5043affa
--- /dev/null
+++ b/private/net/svcdlls/browser2/browtest/browutil.h
@@ -0,0 +1,16 @@
+#include "browfunc.h"
+#include "browtest.h"
+
+
+VOID CompareListsMasterAndBackUp(PVOID, DWORD, PVOID, DWORD, LPTSTR, LPTSTR, LPTSTR, LPTSTR);
+VOID CompareListsMasterAndFile(PVOID, DWORD, LPTSTR, LPTSTR, XPORTINFO, BOOL);
+NET_API_STATUS GetMasterName(XPORTINFO *, INT, INT, LPTSTR, LPTSTR);
+BOOL MasterAvailable(XPORTINFO, LPTSTR, INT);
+BOOL ReadInputFile(VOID);
+VOID RemoveTabs(PCHAR *);
+NET_API_STATUS RetrieveList(LPTSTR, LPTSTR, LPVOID, LPDWORD, LPDWORD, DWORD, LPTSTR, LPDWORD, BOOL);
+NET_API_STATUS StartBrowserService(LPTSTR);
+NET_API_STATUS StopBrowserService(LPTSTR);
+NET_API_STATUS QueryBrowserServiceStatus(LPMACHINEINFO, LPSERVICE_STATUS);
+
+
diff --git a/private/net/svcdlls/browser2/browtest/makefile b/private/net/svcdlls/browser2/browtest/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/net/svcdlls/browser2/browtest/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/net/svcdlls/browser2/browtest/makefile.inc b/private/net/svcdlls/browser2/browtest/makefile.inc
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/private/net/svcdlls/browser2/browtest/makefile.inc
diff --git a/private/net/svcdlls/browser2/browtest/sources b/private/net/svcdlls/browser2/browtest/sources
new file mode 100644
index 000000000..5aeb50bb5
--- /dev/null
+++ b/private/net/svcdlls/browser2/browtest/sources
@@ -0,0 +1,67 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+
+Author:
+
+ Steve Wood (stevewo) 12-Apr-1989
+
+
+Revision History:
+
+!ENDIF
+
+MAJORCOMP = net
+MINORCOMP = browtest
+
+TARGETNAME=browfunc
+TARGETPATH=obj
+TARGETTYPE=LIBRARY
+
+
+INCLUDES=.;..;..\..\..\inc;$(BASEDIR)\private\inc
+
+
+!IFNDEF DISABLE_NET_UNICODE
+UNICODE=1
+NET_C_DEFINES=-DUNICODE
+!ENDIF
+
+SOURCES=browfunc.c \
+ browutil.c \
+ browmdom.c \
+ browstrs.c
+
+
+
+C_DEFINES=-DRPC_NO_WINDOWS_H
+
+WARNING_LEVEL=-W4
+
+USE_CRTDLL=1
+
+UMTYPE=console
+UMAPPL=browtest
+UMTEST=
+UMLIBS=$(BASEDIR)\public\sdk\lib\*\netapi32.lib \
+ obj\*\browfunc.lib \
+ ..\common\obj\*\brcommon.lib\
+ $(BASEDIR)\public\sdk\lib\*\rpcutil.lib \
+ $(BASEDIR)\public\sdk\lib\*\ntdll.lib \
+ $(BASEDIR)\public\sdk\lib\*\netlib.lib \
+ $(BASEDIR)\public\sdk\lib\*\advapi32.lib \
+ $(BASEDIR)\public\sdk\lib\*\user32.lib \
+
+
diff --git a/private/net/svcdlls/browser2/bwatch/bwatch.c b/private/net/svcdlls/browser2/bwatch/bwatch.c
new file mode 100644
index 000000000..2cd5ebfe5
--- /dev/null
+++ b/private/net/svcdlls/browser2/bwatch/bwatch.c
@@ -0,0 +1,704 @@
+/*++
+
+Copyright (c) 1993 Micorsoft Corporation
+
+Module Name:
+
+ bwatch.c
+
+Abstract:
+
+ Browser Monitor main program.
+
+Author:
+ Dan Hinsley (DanHi) 10-Oct-1992
+ Congpa You (CongpaY) 10-Feb-1993
+
+Revision History:
+
+--*/
+#define INCLUDE_SMB_TRANSACTION
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <sys\types.h>
+#include <sys\stat.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <windows.h>
+#include <lm.h>
+#include <ntddbrow.h>
+#include <brcommon.h>
+#include <rpcutil.h>
+#include <nb30.h>
+#include <smbtypes.h>
+#include <smb.h>
+#include <smbgtpt.h>
+#include <hostannc.h>
+#include "bwatch.h"
+
+// main is going to be changed into a function which taks lpTransportName
+// and pDomainName as parameter.
+_cdecl main()
+{
+ INT nFileSizeLimit;
+ INT nIndex = 0;
+ DWORD dwVal;
+ CHAR pDomain[BUFFERLENGTH];
+ FILE * pFile;
+ PLMDR_TRANSPORT_LIST TransportList = NULL;
+ PLMDR_TRANSPORT_LIST TransportEntry = NULL;
+
+ // Tell user the program is running.
+ fprintf(stdout, "BWATCH is started.\n") ;
+
+ //Create a logfile for writing all the errors and other information to it.
+ pFile = fopen (szBWATCH, "w+");
+
+ // Print the start new run header in the logfile.
+ PrintHeader (pFile);
+
+ // Initialize global data.
+ if (!Init())
+ return;
+
+ // Get lpDomain from bwatch.ini.
+ if (!GetDomain (pDomain))
+ return;
+
+ nFileSizeLimit = GetLimit();
+
+ // Find all transports that we have.
+ dwVal = GetBrowserTransportList (&TransportList);
+ if (dwVal != NERR_Success)
+ {
+ if (TransportList != NULL)
+ {
+ MIDL_user_free (TransportList);
+ }
+ ReportError (dwVal);
+ return;
+ }
+
+ TransportEntry = TransportList;
+
+ // Enumerate on the transports.
+ while (TransportEntry != NULL)
+ {
+ CHAR * pDomainName;
+ CHAR pDomainList[BUFFERLENGTH];
+ CCHAR lana_num;
+ NET_API_STATUS Status;
+ PSUPER_NCB psuperncb;
+
+ strcpy (pDomainList, pDomain);
+
+ pDomainName = strtok (pDomainList, szSeps);
+
+ // Initialize psuperncb.
+
+ Status = BrGetLanaNumFromNetworkName (TransportEntry->TransportName, &lana_num);
+
+ if (Status != NERR_Success) {
+ ReportError (Status);
+ MIDL_user_free (TransportList);
+ return;
+ }
+
+ // Reset the adapter. You have to reset before doing anything.
+ if (Reset(FALSE, lana_num))
+ {
+ while (pDomainName != NULL)
+ {
+ psuperncb = (PSUPER_NCB) LocalAlloc (LPTR, sizeof(*psuperncb));
+ if (psuperncb != NULL)
+ {
+ psuperncb->lanaNumber = lana_num;
+
+ // Add DOMAIN(1e).
+ Registe (TransportEntry->TransportName, pDomainName, psuperncb, nIndex);
+
+ // Run Netbios.
+ SubmitRCDg (psuperncb);
+
+ nIndex++;
+ }
+
+ pDomainName = strtok (NULL, szSeps);
+ }
+
+#ifdef INCLUDE_MSBROWSE
+ psuperncb = (PSUPER_NCB) LocalAlloc (LPTR, sizeof(*psuperncb));
+
+ if (psuperncb != NULL)
+ {
+ psuperncb->lanaNumber = lana_num;
+
+ // Add MSBROWSE.
+ RegisteWkgroupName (TransportEntry->TransportName, "MSBROWSE", psuperncb, nIndex);
+
+ // Run Netbios.
+ SubmitRCDg (psuperncb);
+
+ nIndex++;
+ }
+#endif // INCLUDE_BRWOSE
+
+ }
+
+ if (TransportEntry->NextEntryOffset == 0)
+ {
+ TransportEntry = NULL;
+ }
+ else
+ {
+ TransportEntry = (PLMDR_TRANSPORT_LIST) ((PCHAR) TransportEntry
+ + TransportEntry->NextEntryOffset);
+ }
+ }
+
+ // Free the memory of TransportList.
+ MIDL_user_free (TransportList);
+
+ // Tell user the program is running.
+ fprintf(stdout, "BWATCH is now running.\n") ;
+
+ while (TRUE)
+ {
+ ProcessQueue(pFile, nFileSizeLimit);
+ }
+}
+
+// Report if an error occurs.
+void ReportError (DWORD dwError)
+{
+ printf("An error occured: %lu", dwError);
+}
+
+// Initialize the structure.
+BOOL Init()
+{
+ InitializeListHead (&WorkQueueHead);
+ InitializeListHead (&FreeQueueHead);
+
+ InitializeCriticalSection (&CSWorkQueue);
+ InitializeCriticalSection (&CSFreeQueue);
+
+ hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+
+ if (hEvent == NULL)
+ {
+ return(FALSE);
+ }
+ return(TRUE);
+}
+
+// Before we start a new session, we have to reset.
+// ncb.lana_num must be already set.
+
+BOOL Reset (UCHAR Reset,
+ CCHAR LanaNum)
+{
+ NCB ncb;
+ ClearNcb( &ncb );
+ ncb.ncb_command = NCBRESET;
+
+ ncb.ncb_lsn = Reset;
+
+ ncb.ncb_callname[0] = ncb.ncb_callname[1] = ncb.ncb_callname[2] =
+ ncb.ncb_callname[3] = 0;
+
+ ncb.ncb_lana_num = LanaNum;
+
+ Netbios( &ncb );
+ if ( ncb.ncb_retcode != NRC_GOODRET ) {
+ printf( "Reset returned an error %lx\n", ncb.ncb_retcode);
+ return(FALSE);
+ }
+
+ return(TRUE);
+}
+
+// Registe a session.
+void Registe (LPTSTR lpTransportName,
+ CHAR * pDomainName,
+ PSUPER_NCB psuperncb,
+ INT nIndex)
+{
+ // Receive Datagram.
+ printf ("Running Netbios on transport %s, domain %s...\n", toansi(lpTransportName), pDomainName);
+
+ strcpy (psuperncb->pTransportName, toansi(lpTransportName));
+
+ strcpy (psuperncb->pDomainName, pDomainName);
+
+ psuperncb->nIndex = nIndex;
+
+ psuperncb->ncb.ncb_lana_num = psuperncb->lanaNumber;
+
+ // Registe DOMAIN(1e) for hearing Election and AnnouncementRequest packets.
+ AddName(0x1e, (NCB *) psuperncb);
+
+ psuperncb->nameNumber = psuperncb->ncb.ncb_num;
+
+}
+
+// Registe Netbios name.
+VOID AddName(UCHAR Suffix,
+ NCB * pncb)
+{
+ CHAR localName[32];
+
+ strcpy (localName, _strupr(((PSUPER_NCB) pncb)->pDomainName));
+ strcat (localName, SIXTEENSPACES);
+
+ ClearNcb( pncb );
+ pncb->ncb_command = NCBADDGRNAME;
+
+ localName[15] = Suffix;
+ RtlCopyMemory( pncb->ncb_name, localName, 16);
+ pncb->ncb_lana_num = ((PSUPER_NCB) pncb)->lanaNumber;
+ Netbios( pncb );
+ if ( pncb->ncb_retcode != NRC_GOODRET ) {
+ printf( "AddGroupname 0x%x returned an error %lx\n", Suffix,
+ pncb->ncb_retcode);
+ return;
+ }
+}
+
+// Registe a session.
+void RegisteWkgroupName (LPTSTR lpTransportName,
+ CHAR * pDomainName,
+ PSUPER_NCB psuperncb,
+ INT nIndex)
+{
+ CHAR localName[16];
+
+ printf ("Running Netbios on transport %s, domain %s...\n", toansi(lpTransportName), pDomainName);
+
+ strcpy (psuperncb->pTransportName, toansi(lpTransportName));
+
+ strcpy (psuperncb->pDomainName, pDomainName);
+
+ psuperncb->nIndex = nIndex;
+
+ psuperncb->ncb.ncb_lana_num = psuperncb->lanaNumber;
+
+ // Jam in Domain announcement name
+ strcpy (&localName[2], "__MSBROWSE__");
+ localName[0] = 0x01;
+ localName[1] = 0x02;
+ localName[14] = 0x02;
+ localName[15] = 0x01;
+
+ RtlCopyMemory (psuperncb->ncb.ncb_name, localName, 16);
+ psuperncb->ncb.ncb_command = NCBADDGRNAME;
+ psuperncb->ncb.ncb_lana_num = psuperncb->lanaNumber;
+ Netbios ((NCB *) psuperncb);
+
+ if (psuperncb->ncb.ncb_retcode != NRC_GOODRET) {
+ printf("AddGroupname MSBROUSE returned an error %lx\n", psuperncb->ncb.ncb_retcode);
+ }
+
+ psuperncb->nameNumber = psuperncb->ncb.ncb_num;
+
+}
+
+// This is the function which calls Netbios.
+void SubmitRCDg (PSUPER_NCB psuperncb)
+{
+ ClearNcb ((NCB *) psuperncb);
+
+ psuperncb->ncb.ncb_command = NCBDGRECV|ASYNCH;
+ psuperncb->ncb.ncb_lana_num = (UCHAR)psuperncb->lanaNumber;
+ psuperncb->ncb.ncb_num = psuperncb->nameNumber;
+ psuperncb->ncb.ncb_length = BUFFERLENGTH;
+ psuperncb->ncb.ncb_buffer = psuperncb->Buffer;
+ psuperncb->ncb.ncb_post = RCDgPost;
+ Netbios ((NCB *) psuperncb);
+}
+
+// Netbios's call back function.
+void RCDgPost (NCB * pncb)
+{
+ PSUPER_NCB psuperncb = (PSUPER_NCB) pncb;
+
+ if (pncb->ncb_retcode != NRC_GOODRET)
+ {
+ printf ("ReceiveDatagram returned an error %lx\n", pncb->ncb_retcode);
+ if (pncb->ncb_retcode != NRC_INCOMP)
+ {
+ return;
+ }
+ }
+
+ LoadWorkQueue (psuperncb);
+
+ SubmitRCDg(psuperncb);
+}
+
+// This is the function get entries from the queue and print out the content.
+void ProcessQueue(FILE * pFile, INT nFileSizeLimit)
+{
+ PQUEUE_ENTRY pBuffer;
+ struct _stat buf;
+ BOOL fRet;
+
+ if (_fstat (_fileno(pFile), &buf) != -1)
+ {
+ if (buf.st_size > nFileSizeLimit)
+ {
+ fflush (pFile);
+
+ fclose (pFile);
+
+ fRet = MoveFileEx (szLOGFILE, szBACKUP, MOVEFILE_REPLACE_EXISTING);
+ if (!fRet)
+ {
+ pFile = fopen (szBWATCH, "w+");
+ ReportError (GetLastError());
+ }
+ else
+ pFile = fopen (szBWATCH, "w+");
+ }
+ }
+
+ WaitForSingleObject (hEvent, INFINITE);
+
+ while (TRUE)
+ {
+ CHAR DecodedName[20];
+
+ pBuffer = PullBufferFromQueue (&WorkQueueHead, &CSWorkQueue);
+
+ if (pBuffer == NULL)
+ {
+ return;
+ }
+
+ DecodeName (DecodedName, pBuffer->ncb_callname);
+
+ DecodeSmb (pFile, DecodedName, pBuffer);
+
+ PutBufferOnQueue (&FreeQueueHead, pBuffer, &CSFreeQueue);
+ }
+}
+
+// put the buffer returned from Netbios on the queue.
+void LoadWorkQueue (PSUPER_NCB psuperncb)
+{
+ PQUEUE_ENTRY pEntry;
+
+ // Allocate memory for pEntry.
+
+ pEntry = PullBufferFromQueue (&FreeQueueHead, &CSFreeQueue);
+
+ if (pEntry == NULL)
+ {
+ pEntry = (PQUEUE_ENTRY) LocalAlloc (LPTR, sizeof(*pEntry));
+ if (pEntry == NULL)
+ {
+ return;
+ }
+ }
+
+ GetLocalTime (&pEntry->systime);
+
+ strcpy (pEntry->ncb_callname, psuperncb->ncb.ncb_callname);
+ memcpy (pEntry->ncb_buffer, psuperncb->ncb.ncb_buffer, BUFFERLENGTH);
+ strcpy (pEntry->pTransportName, psuperncb->pTransportName);
+ strcpy (pEntry->pDomainName, psuperncb->pDomainName);
+ pEntry->nIndex = psuperncb->nIndex;
+
+ PutBufferOnQueue (&WorkQueueHead, pEntry, &CSWorkQueue);
+ SetEvent (hEvent);
+}
+
+// add a new entry to queue.
+void PutBufferOnQueue (PLIST_ENTRY pQueueHead,
+ PQUEUE_ENTRY pEntry,
+ CRITICAL_SECTION * pCSQueue)
+{
+ EnterCriticalSection (pCSQueue);
+
+ InsertTailList (pQueueHead, &(pEntry->List));
+
+ LeaveCriticalSection (pCSQueue);
+}
+
+// get an entry from queue.
+PQUEUE_ENTRY PullBufferFromQueue(PLIST_ENTRY pQueueHead,
+ CRITICAL_SECTION * pCSQueue)
+{
+ PQUEUE_ENTRY pEntry;
+
+ EnterCriticalSection (pCSQueue);
+
+ if (IsListEmpty (pQueueHead))
+ pEntry = NULL;
+ else
+ pEntry = (PQUEUE_ENTRY) RemoveHeadList (pQueueHead);
+
+ LeaveCriticalSection (pCSQueue);
+
+ return(pEntry);
+}
+
+
+
+// Change Netbios name into readable form: NAME(XX)
+VOID
+DecodeName(
+ LPSTR DecodedName,
+ LPSTR EncodedName
+ )
+{
+
+ CHAR TempString[6];
+ int i;
+
+ //
+ // Find first blank
+ //
+
+ for (i = 0; i < 15 ; i++) {
+ if (EncodedName[i] == ' ') {
+ break;
+ }
+ }
+
+ strncpy(DecodedName, EncodedName, i);
+ DecodedName[i] = '\0';
+ sprintf(TempString, "<%x>", EncodedName[15]);
+ strcat(DecodedName, TempString);
+
+}
+
+// Print out what's in the buffer.
+BOOL
+DecodeSmb(FILE * pFile,
+ LPSTR DecodedName,
+ PQUEUE_ENTRY pEntry)
+{
+ PBYTE Smb;
+ LPSTR MailslotName;
+ PUCHAR pPacketType;
+ PNT_SMB_HEADER pSmbHeader;
+ PREQ_TRANSACTION pSmbTransaction;
+ PBROWSE_ANNOUNCE_PACKET pBrowseAnnouncePacket;
+ PREQUEST_ELECTION pRequestElection;
+ PBECOME_BACKUP pBecomeBackup;
+ PREQUEST_ANNOUNCE_PACKET pRequestAnnouncement;
+
+ if (pEntry->nIndex != nIndex)
+ {
+ fprintf (pFile,
+ "\nTransport: %s, Domain: %s\n",
+ pEntry->pTransportName,
+ pEntry->pDomainName);
+
+ nIndex = pEntry->nIndex;
+ }
+
+ TimeStamp (pFile, &pEntry->systime);
+
+ Smb = pEntry->ncb_buffer;
+ //
+ // Decipher the SMB in the packet
+ //
+
+ pSmbHeader = (PNT_SMB_HEADER) Smb;
+ if (pSmbHeader->Protocol[0] != 0xff &&
+ pSmbHeader->Protocol[1] != 'S' &&
+ pSmbHeader->Protocol[2] != 'M' &&
+ pSmbHeader->Protocol[3] != 'B') {
+ fprintf(pFile, "Not a valid SMB header\n");
+ return(FALSE);
+ }
+ pSmbTransaction = (PREQ_TRANSACTION)
+ (Smb + sizeof(NT_SMB_HEADER));
+
+ MailslotName = (LPSTR) (pSmbTransaction->Buffer +
+ pSmbTransaction->SetupCount + 5);
+
+ if (!strcmp(MailslotName, "\\MAILSLOT\\BROWSE")) {
+ pPacketType = (PUCHAR) Smb +
+ pSmbTransaction->DataOffset;
+ switch (*pPacketType) {
+
+ case AnnouncementRequest:
+ fprintf(pFile, "Announcement request from %s. ", DecodedName);
+ pRequestAnnouncement =
+ (PREQUEST_ANNOUNCE_PACKET) pPacketType;
+ fprintf(pFile, "Reply %s\n",
+ pRequestAnnouncement->RequestAnnouncement.Reply);
+ break;
+
+ case Election:
+ fprintf(pFile, "Election request: %s ", DecodedName);
+ pRequestElection = (PREQUEST_ELECTION) pPacketType;
+ fprintf(pFile, "Version(%d) Criteria(0x%x) ",
+ pRequestElection->ElectionRequest.Version,
+ pRequestElection->ElectionRequest.Criteria);
+ fprintf(pFile, "TimeUp(%d)\n",
+ pRequestElection->ElectionRequest.TimeUp);
+ break;
+
+ case BecomeBackupServer:
+ fprintf(pFile, "BecomeBackupServer from %s ", DecodedName);
+ pBecomeBackup = (PBECOME_BACKUP) pPacketType;
+ fprintf(pFile, "to %s\n", pBecomeBackup->BecomeBackup.BrowserToPromote);
+ break;
+
+ case LocalMasterAnnouncement:
+ case WkGroupAnnouncement:
+ pBrowseAnnouncePacket =
+ (PBROWSE_ANNOUNCE_PACKET) pPacketType;
+ switch (*pPacketType) {
+ case LocalMasterAnnouncement:
+ fprintf(pFile, "LocalMasterAnnouncement from %s. ", DecodedName);
+ break;
+ case WkGroupAnnouncement:
+ fprintf(pFile, "Workgroup Announcement from %s. Domain: %s, Master %s. ", DecodedName, pBrowseAnnouncePacket->BrowseAnnouncement.ServerName, pBrowseAnnouncePacket->BrowseAnnouncement.Comment);
+ break;
+ }
+ fprintf(pFile, "UpdateCount = %d\n",
+ pBrowseAnnouncePacket->BrowseAnnouncement.UpdateCount);
+ break;
+
+ default:
+ fprintf(pFile, "\n**** Packet type %d from %s ****\n",
+ *pPacketType, DecodedName);
+ }
+ }
+ else if (strcmp(MailslotName, "\\MAILSLOT\\NET\\REPL_CLI") &&
+ strcmp(MailslotName, "\\MAILSLOT\\NET\\NTLOGON") &&
+ strcmp(MailslotName, "\\MAILSLOT\\NET\\NETLOGON") &&
+ strcmp(MailslotName, "\\MAILSLOT\\LANMAN")) {
+ fprintf(pFile, "Received an unknown datagram, name = %s\n",
+ MailslotName);
+ }
+
+ return(TRUE);
+}
+
+// Convert an unicode string to ansi string.
+LPSTR toansi(LPTSTR lpUnicode)
+{
+ static CHAR lpAnsi[BUFFERLENGTH];
+ BOOL fDummy;
+ INT i;
+
+ i = WideCharToMultiByte (CP_ACP,
+ 0,
+ lpUnicode,
+ lstrlen(lpUnicode),
+ lpAnsi,
+ BUFFERLENGTH,
+ NULL,
+ &fDummy);
+
+ lpAnsi[i] = 0;
+
+ return(lpAnsi);
+}
+
+// Get Domains from bwatch.ini.
+BOOL GetDomain (CHAR * pDomain)
+{
+ DWORD dwVal;
+
+ // Read all the domains that we want to check from bchk.ini
+ dwVal = GetPrivateProfileStringA (szAPPNAME,
+ szDOMAINS,
+ szDefaultDomain,
+ pDomain,
+ BUFFERLENGTH,
+ szFILENAME);
+
+ if (dwVal >= (BUFFERLENGTH-2)) // The memory assigned to lpDomainList is not big enough.
+ {
+ printf ("ERROR_NOT_ENOUGH_MEMORY");
+ return(FALSE);
+ }
+
+ return(TRUE);
+}
+
+INT GetLimit ()
+{
+ DWORD dwVal;
+ CHAR pTemp[10];
+ INT nFileSizeLimit;
+
+ // Read all the domains that we want to check from bchk.ini
+ dwVal = GetPrivateProfileStringA (szAPPNAME,
+ szFILESIZELIMIT,
+ szDefaultFileSizeLimit,
+ pTemp,
+ 10,
+ szFILENAME);
+
+ if (dwVal >= (BUFFERLENGTH-2)) // The memory assigned to lpDomainList is not big enough.
+ {
+ printf ("ERROR_NOT_ENOUGH_MEMORY");
+ return(atoi(szDefaultFileSizeLimit));
+ }
+
+ return(atoi(pTemp));
+}
+
+// Copied from ..\client\browstub.c.
+NET_API_STATUS GetBrowserTransportList (OUT PLMDR_TRANSPORT_LIST *TransportList)
+{
+
+ NET_API_STATUS Status;
+ HANDLE BrowserHandle;
+ LMDR_REQUEST_PACKET RequestPacket;
+
+ Status = OpenBrowser(&BrowserHandle);
+
+ if (Status != NERR_Success) {
+ return Status;
+ }
+
+ RequestPacket.Version = LMDR_REQUEST_PACKET_VERSION;
+
+ RequestPacket.Type = EnumerateXports;
+
+ RtlInitUnicodeString(&RequestPacket.TransportName, NULL);
+ RtlInitUnicodeString(&RequestPacket.EmulatedDomainName, NULL);
+
+ Status = DeviceControlGetInfo(
+ BrowserHandle,
+ IOCTL_LMDR_ENUMERATE_TRANSPORTS,
+ &RequestPacket,
+ sizeof(RequestPacket),
+ (PVOID *)TransportList,
+ 0xffffffff,
+ 4096,
+ NULL);
+
+ NtClose(BrowserHandle);
+
+ return Status;
+}
+
+// Print out the header line in the log file.
+void PrintHeader (FILE * pFile)
+{
+ fprintf(pFile, "**********************************************\n");
+ fprintf(pFile, "******************* BWATCH *******************\n");
+ fprintf(pFile, "**********************************************\n");
+}
+
+// Print out the time in the log file.
+void TimeStamp (FILE * pFile, SYSTEMTIME * psystime)
+{
+ fprintf (pFile,
+ "%d:%d:%d ",
+ psystime->wHour,
+ psystime->wMinute,
+ psystime->wSecond);
+}
diff --git a/private/net/svcdlls/browser2/bwatch/bwatch.h b/private/net/svcdlls/browser2/bwatch/bwatch.h
new file mode 100644
index 000000000..833524885
--- /dev/null
+++ b/private/net/svcdlls/browser2/bwatch/bwatch.h
@@ -0,0 +1,120 @@
+/*++
+
+Copyright (c) 1993 Micorsoft Corporation
+
+Module Name:
+
+ bwatch.h
+
+Abstract:
+
+ Header file for browser watch program.
+
+Author:
+ Congpa You (CongpaY) 10-Feb-1993
+
+Revision History:
+
+--*/
+
+// Local data.
+#define SIXTEENSPACES " "
+#define BUFFERLENGTH 512
+#define STRINGLEN 30
+#define szBWATCH "bwatchlg"
+#define szLOGFILE L"bwatchlg"
+#define szBACKUP L"bwbackup"
+#define szSeps " ,\t\n"
+
+#define szAPPNAME "bwatch"
+#define szDOMAINS "DOMAINS"
+#define szDefaultDomain "ntlan"
+#define szFILENAME "bwatch.ini"
+#define szFILESIZELIMIT "FileSizeLimit"
+#define szDefaultFileSizeLimit "100000"
+
+// Global data.
+INT nIndex = -1;
+LIST_ENTRY WorkQueueHead;
+LIST_ENTRY FreeQueueHead;
+CRITICAL_SECTION CSWorkQueue;
+CRITICAL_SECTION CSFreeQueue;
+HANDLE hEvent;
+
+// Local data type.
+typedef struct _QUEUE_ENTRY
+{
+ LIST_ENTRY List;
+ UCHAR ncb_callname[NCBNAMSZ];
+ UCHAR ncb_buffer[BUFFERLENGTH];
+ CHAR pTransportName[STRINGLEN];
+ CHAR pDomainName[STRINGLEN];
+ INT nIndex;
+ SYSTEMTIME systime;
+}QUEUE_ENTRY, *PQUEUE_ENTRY;
+
+typedef struct _SUPER_NCB
+{
+ NCB ncb;
+ UCHAR Buffer[BUFFERLENGTH];
+ CCHAR lanaNumber;
+ CHAR pTransportName[STRINGLEN];
+ CHAR pDomainName[STRINGLEN];
+ CHAR nameNumber;
+ INT nIndex;
+}SUPER_NCB, *PSUPER_NCB;
+
+// Local Functions.
+#define ClearNcb( PNCB ) { \
+ RtlZeroMemory( PNCB , sizeof (NCB) ); \
+ RtlCopyMemory( (PNCB)->ncb_name, SIXTEENSPACES, sizeof(SIXTEENSPACES)-1 );\
+ RtlCopyMemory( (PNCB)->ncb_callname, SIXTEENSPACES, sizeof(SIXTEENSPACES)-1 );\
+ }
+
+void ReportError (DWORD dwError);
+
+BOOL GetDomain (CHAR * pDomain);
+
+INT GetLimit ();
+
+BOOL Init();
+
+void Registe (LPTSTR lpTransportName,
+ CHAR * pDomainName,
+ PSUPER_NCB psuperncb,
+ INT nIndex);
+
+void RegisteWkgroupName (LPTSTR lpTransportName,
+ CHAR * pDomainName,
+ PSUPER_NCB psuperncb,
+ INT nIndex);
+
+void SubmitRCDg(PSUPER_NCB psuperncb);
+void RCDgPost(NCB * pncb);
+
+void ProcessQueue(FILE * pFile, INT nFileSizeLimit);
+
+void LoadWorkQueue (PSUPER_NCB psuperncb);
+
+void PutBufferOnQueue(PLIST_ENTRY pQueueHead,
+ PQUEUE_ENTRY pEntry,
+ CRITICAL_SECTION * pCriticalSection);
+
+PQUEUE_ENTRY PullBufferFromQueue(PLIST_ENTRY pQueueHead,
+ CRITICAL_SECTION * pCriticalSection);
+
+BOOL Reset (UCHAR Lsn, CCHAR Lana_Num);
+
+VOID AddName(UCHAR Suffix, NCB * pncb);
+
+VOID DecodeName(LPSTR DecodedName, LPSTR EncodedName);
+BOOL DecodeSmb(FILE * pFile, LPSTR DecodedName, PQUEUE_ENTRY pEntry);
+
+LPSTR toansi(LPTSTR lpUnicode);
+
+NET_API_STATUS GetBrowserTransportList (OUT PLMDR_TRANSPORT_LIST *TransportList);
+
+void PrintHeader (FILE * pFile);
+
+void TimeStamp (FILE * pFile, SYSTEMTIME * psystime);
+
diff --git a/private/net/svcdlls/browser2/bwatch/makefile b/private/net/svcdlls/browser2/bwatch/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/net/svcdlls/browser2/bwatch/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/net/svcdlls/browser2/bwatch/sources b/private/net/svcdlls/browser2/bwatch/sources
new file mode 100644
index 000000000..b4dcc110d
--- /dev/null
+++ b/private/net/svcdlls/browser2/bwatch/sources
@@ -0,0 +1,52 @@
+!IF 0
+
+Copyright (c) 1989-1992 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+
+Author:
+
+ Steve Wood (stevewo) 12-Apr-1990
+ Congpa You (congpay) 04-Feb-1993
+
+NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl
+
+!ENDIF
+
+MAJORCOMP=windows
+MINORCOMP=bwatch
+
+TARGETNAME=bwatch
+TARGETPATH=obj
+TARGETTYPE=LIBRARY
+
+INCLUDES=..\..\..;..\..\..\inc;..\..\..\..\inc;..\..\..\..\..\inc;..;
+
+
+!IFNDEF DISABLE_NET_UNICODE
+UNICODE=1
+NET_C_DEFINES=-DUNICODE
+!ENDIF
+
+SOURCES=
+
+UMTYPE=console
+UMAPPL=bwatch
+UMLIBS=\nt\public\sdk\lib\*\netapi32.lib \
+ ..\common\obj\*\utils.obj \
+ \nt\public\sdk\lib\*\ntdll.lib \
+ \nt\public\sdk\lib\*\rpcutil.lib
+
+
+
+
diff --git a/private/net/svcdlls/browser2/client/brclient.h b/private/net/svcdlls/browser2/client/brclient.h
new file mode 100644
index 000000000..45b7c84a7
--- /dev/null
+++ b/private/net/svcdlls/browser2/client/brclient.h
@@ -0,0 +1,71 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ brclient.h
+
+Abstract:
+
+ Private header file for the client end of the Browser service
+ modules.
+
+Author:
+
+ Rita Wong (ritaw) 10-May-1991
+
+Revision History:
+
+--*/
+
+#include <nt.h> // DbgPrint prototype
+#include <ntrtl.h> // DbgPrint
+#include <nturtl.h> // Needed by winbase.h
+
+#include <windef.h> // DWORD
+#include <winbase.h> // LocalFree
+
+#include <rpc.h> // DataTypes and runtime APIs
+#include <rpcutil.h> // GENERIC_ENUM_STRUCT
+
+#include <lmcons.h> // NET_API_STATUS
+#include <lmerr.h> // NetError codes
+#include <lmremutl.h> // SUPPORTS_RPC
+
+#include <netlibnt.h> // NetpNtStatusToApiStatus
+#include <netdebug.h> // NetpDbgPrint
+
+#include <bowser.h> // generated by the MIDL complier
+#include <brnames.h> // Service and interface names
+
+//
+// Debug trace level bits for turning on/off trace statements in the client
+// end of the Browser service
+//
+
+//
+// Client stub trace output
+//
+#define BROWSER_DEBUG_CLIENTSTUBS 0x00000001
+
+//
+// Client RPC binding trace output
+//
+#define BROWSER_DEBUG_RPCBIND 0x00000002
+
+//
+// All debug flags on
+//
+#define BROWSER_DEBUG_ALL 0xFFFFFFFF
+
+
+#if DBG
+
+#define STATIC
+
+#else
+
+#define STATIC static
+
+#endif // DBG
diff --git a/private/net/svcdlls/browser2/client/browbind.c b/private/net/svcdlls/browser2/client/browbind.c
new file mode 100644
index 000000000..98621d121
--- /dev/null
+++ b/private/net/svcdlls/browser2/client/browbind.c
@@ -0,0 +1,186 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ wksbind.c
+
+Abstract:
+
+ Routines which use RPC to bind and unbind the client to the Browser
+ service.
+
+Author:
+
+ Rita Wong (ritaw) 14-May-1991
+ Larry Osterman (larryo) 23-Mar-1992
+
+Environment:
+
+ User Mode -Win32
+
+Revision History:
+
+--*/
+
+#include "brclient.h"
+
+
+handle_t
+BROWSER_IMPERSONATE_HANDLE_bind(
+ BROWSER_IMPERSONATE_HANDLE ServerName
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is called from the Browser service client stubs when
+ it is necessary create an RPC binding to the server end with
+ impersonation level of impersonation.
+
+Arguments:
+
+ ServerName - A pointer to a string containing the name of the server
+ to bind with.
+
+Return Value:
+
+ The binding handle is returned to the stub routine. If the bind is
+ unsuccessful, a NULL will be returned.
+
+--*/
+{
+ handle_t BindHandle;
+ RPC_STATUS RpcStatus;
+
+ RpcStatus = NetpBindRpc (
+ ServerName,
+ BROWSER_INTERFACE_NAME,
+ TEXT("Security=Impersonation Dynamic False"),
+ &BindHandle
+ );
+
+ if (RpcStatus != RPC_S_OK) {
+ KdPrint((
+ "BROWSER_IMPERSONATE_HANDLE_bind failed: " FORMAT_NTSTATUS "\n",
+ RpcStatus
+ ));
+ }
+
+ return BindHandle;
+}
+
+
+
+handle_t
+BROWSER_IDENTIFY_HANDLE_bind(
+ BROWSER_IDENTIFY_HANDLE ServerName
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is called from the Browser service client stubs when
+ it is necessary create an RPC binding to the server end with
+ identification level of impersonation.
+
+Arguments:
+
+ ServerName - A pointer to a string containing the name of the server
+ to bind with.
+
+Return Value:
+
+ The binding handle is returned to the stub routine. If the bind is
+ unsuccessful, a NULL will be returned.
+
+--*/
+{
+ handle_t BindHandle;
+ RPC_STATUS RpcStatus;
+
+ RpcStatus = NetpBindRpc (
+ ServerName,
+ BROWSER_INTERFACE_NAME,
+ TEXT("Security=Identification Dynamic False"),
+ &BindHandle
+ );
+
+ if (RpcStatus != RPC_S_OK) {
+ KdPrint((
+ "BROWSER_IDENTIFY_HANDLE_bind failed: " FORMAT_NTSTATUS "\n",
+ RpcStatus
+ ));
+ }
+
+ return BindHandle;
+}
+
+
+
+void
+BROWSER_IMPERSONATE_HANDLE_unbind(
+ BROWSER_IMPERSONATE_HANDLE ServerName,
+ handle_t BindHandle
+ )
+
+/*++
+
+Routine Description:
+
+ This routine calls a common unbind routine that is shared by all services.
+ This routine is called from the Browser service client stubs when it is
+ necessary to unbind from the server end.
+
+Arguments:
+
+ ServerName - This is the name of the server from which to unbind.
+
+ BindingHandle - This is the binding handle that is to be closed.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ UNREFERENCED_PARAMETER(ServerName);
+
+ NetpUnbindRpc(BindHandle);
+}
+
+
+
+void
+BROWSER_IDENTIFY_HANDLE_unbind(
+ BROWSER_IDENTIFY_HANDLE ServerName,
+ handle_t BindHandle
+ )
+
+/*++
+
+Routine Description:
+
+ This routine calls a common unbind routine that is shared by all services.
+ This routine is called from the server service client stubs when it is
+ necessary to unbind from a server.
+
+Arguments:
+
+ ServerName - This is the name of the server from which to unbind.
+
+ BindingHandle - This is the binding handle that is to be closed.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ UNREFERENCED_PARAMETER(ServerName);
+
+ NetpUnbindRpc(BindHandle);
+}
diff --git a/private/net/svcdlls/browser2/client/browdeb.c b/private/net/svcdlls/browser2/client/browdeb.c
new file mode 100644
index 000000000..95c48e5d7
--- /dev/null
+++ b/private/net/svcdlls/browser2/client/browdeb.c
@@ -0,0 +1,4222 @@
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windows.h>
+#include <lm.h>
+#include <ntddbrow.h>
+#include <brcommon.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <hostannc.h>
+#include <lmbrowsr.h>
+#include <nb30.h>
+#include <rap.h>
+#include <rxserver.h>
+#include <srvann.h>
+#include <time.h>
+#include <tstring.h>
+#include <netlib.h>
+#include <icanon.h>
+#include "..\server\brwins.h"
+
+static char ProgramName[MAX_PATH+1] ;
+
+struct {
+ LPSTR SwitchName;
+ LPSTR ShortName;
+ ULONG SwitchValue;
+ LPSTR SwitchInformation;
+ int MinArgc;
+ int MaxArgc;
+ LPSTR Syntax;
+} CommandSwitchList[] = {
+ { "ELECT", "EL", BROWSER_DEBUG_ELECT,
+ "Force election on remote domain",
+ 4, 5, "<Transport> <Domain> [<EmulatedDomain>]" },
+ { "GETBLIST", "GB", BROWSER_DEBUG_GET_BACKUP_LIST,
+ "Get backup list for domain",
+ 3, 5, "<Transport> [[<Domain>] REFRESH]" },
+ { "GETMASTER", "GM", BROWSER_DEBUG_GET_MASTER,
+ "Get remote Master Browser name (using NetBIOS)",
+ 4, 4, "<Transport> <Domain>" },
+ { "GETPDC", "GP", BROWSER_DEBUG_GETPDC,
+ "Get PDC name (using NetBIOS)",
+ 4, 4, "<Transport> <Domain>" },
+ { "LISTWFW", "WFW", BROWSER_DEBUG_LIST_WFW,
+ "List WFW servers that are actually running browser",
+ 3, 3, "<Domain>" },
+ { "STATS", "STS", BROWSER_DEBUG_STATISTICS,
+ "Dump browser statistics",
+ 2, 4, "[\\\\<Computer> [RESET]]" },
+ { "STATUS", "STA", BROWSER_DEBUG_STATUS,
+ "Display status about a domain",
+ 2, 4, "[-V] [<Domain>]" },
+ { "TICKLE", "TIC", BROWSER_DEBUG_TICKLE,
+ "Force remote master to stop",
+ 3, 5, "<Transport> [<Domain> | \\\\<Server> [<EmulatedDomain>]]" },
+ { "VIEW", "VW", BROWSER_DEBUG_VIEW,
+ "Remote NetServerEnum to a server or domain on transport",
+#ifndef _PSS_RELEASE
+ 3, 7, "Transport [<Domain>|\\\\<Server> [<Flags>|/DOMAIN [<DomainToQuery> [Forever]]]]" },
+#else
+ 3, 6, "Transport [<Domain>|\\\\<Server> [<Flags>|/DOMAIN [<DomainToQuery>]]]" },
+#endif
+//
+// NOTE: Any Option below and including "BREAK" will not be displayed
+// with _PSS_RELEASE Defined
+//
+ { "BREAK", "BRK", BROWSER_DEBUG_BREAK_POINT,
+ "Break into debugger in browser service",
+ 2, 2, "" },
+ { "RPCLIST", "RPC", BROWSER_DEBUG_RPCLIST,
+ "Retrieve the remote server list using RPC",
+ 3, 6, "<Transport> [<Domain> || \\\\<Server>] [ServerFlags] [GoForever]" },
+ { "MASTERNAME", "MN", BROWSER_DEBUG_ADD_MASTERNAME,
+ "Add the master name on a domain",
+ 4, 5, "<Transport> <Domain> [PAUSE]" },
+ { "WKSTADOM", "WD", BROWSER_DEBUG_ADD_DOMAINNAME,
+ "Add the domain name",
+ 4, 5, "<Transport> <Domain> [PAUSE]" },
+ { "DUMPNET", "DN", BROWSER_DEBUG_DUMP_NETWORKS,
+ "Dump the list of networks",
+ 2, 2, "" },
+ { "ENABLE", "EN", BROWSER_DEBUG_ENABLE_BROWSER,
+ "Enable the browser service",
+ 2, 2, "" },
+ { "DEBUG", "DBG", BROWSER_DEBUG_SET_DEBUG,
+ "Change browser service debug options",
+ 3, 4, "[[+-]DebugFlag|<Value>] [\\\\<Computer>]" },
+ { "FINDMASTER", "FM", BROWSER_DEBUG_FIND_MASTER,
+ "Find master of current domain",
+ 3, 4, "<Transport> [<EmulatedDomain>]" },
+ { "MASTERANNOUNCE", "MA", BROWSER_DEBUG_ANNOUNCE_MASTER,
+ "Send a master announcement with this machine as master",
+ 4, 5, "<Transport> <Master> [<EmulatedDomain>]" },
+ { "ILLEGAL", "ILL", BROWSER_DEBUG_ILLEGAL_DGRAM,
+ "Send an illegal datagram to workstation",
+ 4, 5, "<Transport> <Computer> [<EmulatedDomain>]" },
+ { "FORCEANNOUNCE", "FA", BROWSER_DEBUG_FORCE_ANNOUNCE,
+ "Force all browsers in domain to announce to master browser",
+ 4, 5, "<Transport> <Domain> [<EmulatedDomain>]" },
+ { "LOCALLIST", "LL", BROWSER_DEBUG_LOCAL_BRLIST,
+ "Retrieve the local browser list",
+ 3, 5, "<Transport> [<ServerFlags>] [<EmulatedDomain>]" },
+ { "ANNOUNCE", "ANN", BROWSER_DEBUG_ANNOUNCE,
+ "Send server announcement w/this machine member of domain",
+ 4, 6, "<Transport> <Domain> [<EmulatedDomainName>] [ASMASTER]" },
+ { "RPCCMP", "RC", BROWSER_DEBUG_RPCCMP,
+ "Compare the RPC generated list with the Rx list",
+ 3, 6, "<Transport> [<Domain> || \\\\<Server>] [<ServerFlags>] [GoForever]" },
+ { "TRUNCLOG", "TLG", BROWSER_DEBUG_TRUNCATE_LOG,
+ "Truncate the browser log",
+ 2, 2, "" },
+ { "BOWDEBUG", "SD", BROWSER_DEBUG_BOWSERDEBUG,
+ "Set debug info in the bowser",
+ 3, 4, "TRUNCATE" },
+ { "POPSERVER", "PS", BROWSER_DEBUG_POPULATE_SERVER,
+ "Populate a workgroup with random server names",
+ 5, 7, "<Transport> <Domain> <NumberOfMachines> [<EmulatedDomain>] [AnnouncementFrequency]" },
+ { "POPDOMAIN", "PD", BROWSER_DEBUG_POPULATE_DOMAIN,
+ "Populate a workgroup with random domain names",
+ 5, 7, "<Transport> <Domain> <NumberOfMachines> [<EmulatedDomain>] [AnnouncementFrequency]" },
+ { "OTHERDOMAIN", "OTH", BROWSER_DEBUG_GET_OTHLIST,
+ "Retrieve list of otherdomains that computer listens to",
+ 3, 3, "<Computer>" },
+ { "GETWINS", "GW", BROWSER_DEBUG_GET_WINSSERVER,
+ "Retrieve the primary and backup WINS server",
+ 3, 3, "<Transport>" },
+ { "GETDOMAIN", "GWD", BROWSER_DEBUG_GET_DOMAINLIST,
+ "Retrieve the domain list from a WINS server",
+ 3, 3, "<Ip Address>" },
+ { "GETNETBIOS", "GN", BROWSER_DEBUG_GET_NETBIOSNAMES,
+ "Get Netbios names for a transport",
+ 3, 4, "<Transport> [<EmulatedDomain>]" },
+ { "ADDALTCOMP", "AAC", BROWSER_DEBUG_ADD_ALTERNATE,
+ "Add an alternate computer name",
+ 4, 5, "<Transport> <AlternateComptureName> [<EmulatedDomain>]" },
+ { "EMULATEDOMAIN", "ED", BROWSER_DEBUG_SET_EMULATEDDOMAIN,
+ "Create/Set/Delete emulated domain",
+ 4, 5, "<EmulatedDomain> PDC|BDC|DELETE [<EmulatedComputerName>]" },
+ { "EMULATEDOMAINENUM", "EDE", BROWSER_DEBUG_SET_EMULATEDDOMAINENUM,
+ "Enumerate emulated domains",
+ 2, 2, "" },
+ { NULL, NULL, 0, NULL }
+
+};
+
+
+struct {
+ LPSTR SwitchName;
+ ULONG SwitchValue;
+} DebugSwitchList[] = {
+ { "INIT", BR_INIT },
+ { "CRITICAL", BR_CRITICAL },
+ { "ENUM", BR_SERVER_ENUM },
+ { "UTIL", BR_UTIL },
+ { "CONFIG", BR_CONFIG },
+ { "MAIN", BR_MAIN },
+ { "BACKUP", BR_BACKUP },
+ { "MASTER", BR_MASTER },
+ { "DOMAIN", BR_DOMAIN },
+ { "NETWORK", BR_NETWORK },
+ { "TIMER", BR_TIMER },
+ { "QUEUE", BR_QUEUE },
+ { "LOCKS", BR_LOCKS },
+ { "COMMON", BR_COMMON },
+ { "ALL", BR_ALL },
+ { NULL, 0 }
+
+};
+
+typedef struct _BIT_NAME {
+ DWORD dwValue ;
+ LPSTR lpString ;
+ LPSTR Comment;
+} BIT_NAME ;
+
+BIT_NAME BitToStringTable[] = {
+ { SV_TYPE_WORKSTATION, "W", "Workstation" },
+ { SV_TYPE_SERVER, "S", "Server" },
+ { SV_TYPE_SQLSERVER, "SQL", "SQLServer" } ,
+ { SV_TYPE_DOMAIN_CTRL, "PDC", "PrimaryDomainController" } ,
+ { SV_TYPE_DOMAIN_BAKCTRL, "BDC", "BackupDomainController" } ,
+ { SV_TYPE_TIME_SOURCE, "TS", "TimeSource" } ,
+ { SV_TYPE_AFP, "AFP", "AFPServer" } ,
+ { SV_TYPE_NOVELL, "NV", "Novell" } ,
+ { SV_TYPE_DOMAIN_MEMBER, "MBC", "MemberServer" } ,
+ { SV_TYPE_PRINTQ_SERVER, "PQ", "PrintServer" } ,
+ { SV_TYPE_DIALIN_SERVER, "DL", "DialinServer" } ,
+ { SV_TYPE_XENIX_SERVER, "XN", "Xenix" } ,
+ { SV_TYPE_NT, "NT", "Windows NT" } ,
+ { SV_TYPE_WFW, "WFW", "WindowsForWorkgroups" } ,
+ { SV_TYPE_SERVER_MFPN, "MFPN", "MS Netware" } ,
+ { SV_TYPE_SERVER_NT, "SS", "StandardServer" } ,
+ { SV_TYPE_POTENTIAL_BROWSER, "PBR", "PotentialBrowser" } ,
+ { SV_TYPE_BACKUP_BROWSER, "BBR", "BackupBrowser" } ,
+ { SV_TYPE_MASTER_BROWSER, "MBR", "MasterBrowser" } ,
+ { SV_TYPE_DOMAIN_MASTER, "DMB", "DomainMasterBrowser" } ,
+ { SV_TYPE_SERVER_OSF, "OSF", "OSFServer" } ,
+ { SV_TYPE_SERVER_VMS, "VMS", "VMSServer" } ,
+ { SV_TYPE_WINDOWS, "W95", "Windows95" } ,
+ { SV_TYPE_DFS, "DFS", "DistributedFileSystem" } ,
+ { 0, "", NULL }
+} ;
+
+#include <..\..\..\..\ntos\bowser\debug.h>
+
+#ifdef notdef
+struct {
+ LPSTR SwitchName;
+ ULONG SwitchValue;
+} BowserSwitchList[] = {
+ { "DOMAIN", DPRT_DOMAIN },
+ { "ANNOUNCE", DPRT_ANNOUNCE },
+ { "TDI", DPRT_TDI },
+ { "FSPDISP", DPRT_FSPDISP },
+ { "BROWSER", DPRT_BROWSER },
+ { "ELECT", DPRT_ELECT },
+ { "CLIENT", DPRT_CLIENT },
+ { "MASTER", DPRT_MASTER },
+ { "SRVENUM", DPRT_SRVENUM },
+ { "NETLOGON", DPRT_NETLOGON },
+ { "FSCTL", DPRT_FSCTL },
+ { "INIT", DPRT_INIT },
+ { "REF", DPRT_REF },
+ { "SCAVTHRD", DPRT_SCAVTHRD },
+ { "TIMER", DPRT_TIMER },
+ { "PACK", DPRT_PACK },
+ { "ALL", 0xffffffff },
+ { NULL, 0 }
+};
+#endif // notdef
+
+//
+// forward declarations
+//
+
+VOID
+BrowserStatus(
+ IN BOOL Verbose,
+ OUT PCHAR Domain OPTIONAL
+ );
+
+ NET_API_STATUS
+GetMasterServerNames(
+ IN PUNICODE_STRING NetworkName,
+ IN PUNICODE_STRING EmulatedDomainName,
+ OUT LPWSTR *MasterName
+ );
+
+PCHAR
+UnicodeToPrintfString(
+ PWCHAR WideChar
+ );
+PCHAR
+UnicodeToPrintfString2(
+ PWCHAR WideChar
+ );
+
+NET_API_STATUS
+GetLocalBrowseList(
+ IN PUNICODE_STRING Network,
+ IN PUNICODE_STRING EmulatedDomainName,
+ IN ULONG Level,
+ IN ULONG ServerType,
+ OUT PVOID *ServerList,
+ OUT PULONG EntriesRead,
+ OUT PULONG TotalEntries
+ );
+
+VOID
+View(
+ IN PCHAR Transport,
+ IN PCHAR ServerOrDomain,
+ IN PCHAR Flags,
+ IN PCHAR Domain,
+ IN BOOL GoForever
+ );
+
+VOID
+ListWFW(
+ IN PCHAR Domain
+ );
+
+VOID
+RpcList(
+ IN PCHAR Transport,
+ IN PCHAR ServerOrDomain,
+ IN PCHAR Flags,
+ IN BOOL GoForever
+ );
+
+VOID
+RpcCmp(
+ IN PCHAR Transport,
+ IN PCHAR ServerOrDomain,
+ IN PCHAR Flags,
+ IN BOOL GoForever
+ );
+
+VOID
+GetLocalList(
+ IN PCHAR Transport,
+ IN PCHAR FlagsString,
+ IN PCHAR EmulatedDomain
+ );
+
+VOID
+PrintNetbiosNames(
+ IN PCHAR Transport,
+ IN PCHAR EmulatedDomain OPTIONAL
+ );
+
+NET_API_STATUS
+GetNetbiosNames(
+ IN PUNICODE_STRING Network,
+ IN PUNICODE_STRING EmulatedDomainName,
+ OUT PVOID *NameList,
+ OUT PULONG EntriesRead,
+ OUT PULONG TotalEntries
+ );
+
+NET_API_STATUS
+AddAlternateComputerName(
+ IN PCHAR Transport,
+ IN PCHAR ComputerName,
+ IN PCHAR EmulatedDomain
+ );
+
+VOID
+GetOtherdomains(
+ IN PCHAR ServerName
+ );
+
+VOID
+IllegalDatagram(
+ IN PCHAR Transport,
+ IN PCHAR ServerName,
+ IN PCHAR EmulatedDomain
+ );
+VOID
+AnnounceMaster(
+ IN PCHAR Transport,
+ IN PCHAR ServerName,
+ IN PCHAR EmulatedDomain
+ );
+
+VOID
+Announce(
+ IN PCHAR Transport,
+ IN PCHAR Domain,
+ IN PCHAR EmulatedDomain,
+ IN BOOL AsMaster
+ );
+
+VOID
+Populate(
+ IN BOOL PopulateDomains,
+ IN PCHAR Transport,
+ IN PCHAR Domain,
+ IN PCHAR EmulatedDomain,
+ IN PCHAR NumberOfMachinesString,
+ IN PCHAR PeriodicityString OPTIONAL
+ );
+
+VOID
+AddMasterName(
+ IN PCHAR Transport,
+ IN PCHAR Domain,
+ IN BOOL Pause
+ );
+
+VOID
+AddDomainName(
+ IN PCHAR Transport,
+ IN PCHAR Domain,
+ IN BOOL Pause
+ );
+
+VOID
+GetMaster(
+ IN PCHAR Transport,
+ IN PCHAR Domain
+ );
+
+VOID
+GetPdc(
+ IN PCHAR Transport,
+ IN PCHAR Domain
+ );
+
+VOID
+FindMaster(
+ IN PCHAR Transport,
+ IN PCHAR EmulatedDomain
+ );
+
+VOID
+Elect(
+ IN PCHAR Transport,
+ IN PCHAR Domain,
+ IN PCHAR EmulatedDomain
+ );
+
+VOID
+Tickle(
+ IN PCHAR Transport,
+ IN PCHAR Domain,
+ IN PCHAR EmulatedDomain
+ );
+
+VOID
+ForceAnnounce(
+ IN PCHAR Transport,
+ IN PCHAR Domain,
+ IN PCHAR EmulatedDomain
+ );
+
+VOID
+GetBlist(
+ IN PCHAR TransportName,
+ IN PCHAR DomainName,
+ IN BOOLEAN ForceRescan
+ );
+
+NET_API_STATUS
+EnableService(
+ IN LPTSTR ServiceName
+ );
+
+VOID
+DumpStatistics(
+ IN ULONG NArgs,
+ IN PCHAR Arg1
+ );
+
+VOID
+TruncateBowserLog();
+
+VOID
+CloseBowserLog();
+
+VOID
+OpenBowserLog(PCHAR FileName);
+
+VOID
+SetBowserDebug(PCHAR DebugBits);
+
+VOID
+usage( char *details ) ;
+
+VOID
+help( char *details ) ;
+
+BOOL
+look_for_help(int argc, char **argv) ;
+
+VOID
+qualify_transport(CHAR *old_name, PUNICODE_STRING new_name) ;
+
+VOID
+DisplayServerInfo101(
+ PSERVER_INFO_101 Server,
+ BOOL DomainEnumeration
+ );
+
+DWORD
+display_sv_bits(DWORD dwBits) ;
+
+CHAR *
+get_error_text(DWORD dwErr) ;
+
+VOID
+GetWinsServer(
+ IN PCHAR Transport
+ );
+
+VOID
+GetDomainList(
+ IN PCHAR IpAddress
+ );
+
+VOID
+SetEmulatedDomain(
+ IN PCHAR EmulatedDomain,
+ IN PCHAR Role,
+ IN PCHAR EmulatedComputer
+ );
+
+VOID
+EnumEmulatedDomains(
+ );
+
+//
+// functions
+//
+
+VOID
+usage(
+ char *details
+ )
+{
+ ULONG i = 0;
+ DWORD LineLength;
+#ifndef _PSS_RELEASE
+ printf("Usage: %s Command [Options]\n", ProgramName);
+#else
+ printf("Usage: %s Command [Options | /HELP]\n", ProgramName);
+#endif
+ printf("Where <Command> is one of:\n\n");
+
+
+#ifndef _PSS_RELEASE
+ while (CommandSwitchList[i].SwitchName != NULL)
+#else
+ while (CommandSwitchList[i].SwitchValue != BROWSER_DEBUG_BREAK_POINT )
+#endif
+ {
+ printf(" %-14.14s(%3.3s) - %s\n",
+ CommandSwitchList[i].SwitchName,
+ CommandSwitchList[i].ShortName,
+ CommandSwitchList[i].SwitchInformation);
+ i += 1;
+ }
+
+
+ if (details)
+ printf(details);
+
+ //
+ // Print the descriptions of server type bits
+ //
+ printf("\nIn server (or domain) list displays, the following flags are used:\n");
+
+ LineLength = 0;
+ i=0;
+ while ( BitToStringTable[i].dwValue != 0 ) {
+ DWORD ItemLength;
+
+ ItemLength = strlen(BitToStringTable[i].lpString) +
+ 1 +
+ strlen(BitToStringTable[i].Comment);
+
+ if ( LineLength + ItemLength >= 77 ) {
+ LineLength = 0;
+ printf(",\n");
+ }
+ if ( LineLength == 0) {
+ printf(" ");
+ LineLength = 5;
+ } else {
+ printf(", ");
+ LineLength += 2;
+ }
+
+ printf( "%s=%s", BitToStringTable[i].lpString, BitToStringTable[i].Comment);
+ LineLength += ItemLength;
+ i++;
+
+ }
+ printf("\n");
+
+
+}
+
+VOID
+CommandUsage(
+ ULONG ControlCode
+ )
+/*++
+
+Routine Description:
+
+ Print the usage description for a single command
+
+Arguments:
+
+ ControlCode - Control code of the command who's usage is to be printed.
+
+Return Value:
+
+ None
+
+--*/
+{
+ ULONG Index;
+ ULONG i;
+
+ //
+ // Look up the command in the list of commands.
+ //
+
+ Index = 0;
+ while (CommandSwitchList[Index].SwitchName != NULL) {
+ if ( ControlCode == CommandSwitchList[Index].SwitchValue ) {
+ break;
+ }
+ Index += 1;
+ }
+
+ if (CommandSwitchList[Index].SwitchName == NULL) {
+ usage("Unknown switch specified");
+ return;
+ }
+
+
+ //
+ // Print command usage.
+ //
+
+ printf( "Usage: %s %s %s\n",
+ ProgramName,
+ CommandSwitchList[Index].SwitchName,
+ CommandSwitchList[Index].Syntax );
+
+ //
+ // Print additional command specific information.
+ //
+ switch (ControlCode) {
+ case BROWSER_DEBUG_VIEW:
+ printf(" %s VIEW <transport>\n"
+ " %s VIEW <transport> <domain>|\\\\<Server> [/DOMAIN]\n"
+ " %s VIEW <transport> <server> /DOMAIN <domain>\n",
+ ProgramName,
+ ProgramName,
+ ProgramName );
+
+ break;
+
+ case BROWSER_DEBUG_SET_DEBUG:
+
+ printf("where DebugFlag is one of the following:\n");
+
+ i = 0;
+ while (DebugSwitchList[i].SwitchName != NULL) {
+ printf("\t%s\n", DebugSwitchList[i].SwitchName);
+ i += 1;
+ }
+ break;
+
+
+ case BROWSER_DEBUG_BOWSERDEBUG:
+ printf(" %s BOWDEBUG CLOSE\n"
+ " %s BOWDEBUG OPEN <FileName>\n"
+ " %s BOWDEBUG DEBUG <Flags>\n",
+ ProgramName,
+ ProgramName,
+ ProgramName );
+
+#ifdef notdef
+ printf("where Flags is one of the following:\n");
+
+ i = 0;
+ while (BowserSwitchList[i].SwitchName != NULL) {
+ printf("\t%s\n", BowserSwitchList[i].SwitchName);
+ i += 1;
+ }
+#endif // notdef
+
+ break;
+ }
+
+ printf( "%s.\n",
+ CommandSwitchList[Index].SwitchInformation );
+
+ help("");
+ exit(4);
+}
+
+VOID
+help(
+ char *details
+ )
+{
+ printf("%s\nType \"%s\" to list all switches.\n", details, ProgramName);
+}
+
+VOID
+qualify_transport(CHAR *old_name, PUNICODE_STRING new_name)
+{
+ int len = strlen(old_name) ;
+ char *devicestring = "\\device\\" ;
+ int devicelen = strlen(devicestring) ;
+ CHAR QualifiedTransport[MAX_PATH] ;
+ ANSI_STRING AString;
+
+ //
+ // Qualify the name.
+ //
+ if (_strnicmp(old_name, devicestring, devicelen) != 0)
+ {
+ strcpy(QualifiedTransport, devicestring) ;
+ strcat(QualifiedTransport, (*old_name == '\\') ? old_name+1 : old_name) ;
+ }
+ else
+ {
+ strcpy(QualifiedTransport, old_name) ;
+ }
+
+
+ //
+ // Convert it to a UNICODE_STRING
+ //
+ RtlInitString(&AString, QualifiedTransport);
+
+ RtlAnsiStringToUnicodeString(new_name, &AString, TRUE);
+}
+
+VOID
+_cdecl
+main (argc, argv)
+ int argc;
+ char *argv[];
+{
+ NET_API_STATUS Status;
+ ULONG ControlCode;
+ ULONG Options = 0;
+ LPTSTR Server = NULL;
+ TCHAR ServerBuffer[CNLEN+1];
+ ULONG i = 0;
+
+ strcpy(ProgramName,argv[0]) ; // cannot overflow, since buffer > MAXPATH
+ _strupr(ProgramName) ;
+
+ if (argc < 2) {
+ usage(NULL);
+ exit(1);
+ }
+
+
+ //
+ // Look up the command in the list of commands.
+ //
+
+ while (CommandSwitchList[i].SwitchName != NULL) {
+ if (!_stricmp(argv[1], CommandSwitchList[i].SwitchName) ||
+ !_stricmp(argv[1], CommandSwitchList[i].ShortName)) {
+ ControlCode = CommandSwitchList[i].SwitchValue;
+ break;
+ }
+
+ i += 1;
+ }
+
+ if (CommandSwitchList[i].SwitchName == NULL) {
+ usage("Unknown switch specified");
+ exit(5);
+ }
+
+ //
+ // If an incorrect number of arguments were supplied,
+ // complain.
+ //
+
+ if ( look_for_help(argc, argv) ||
+ argc < CommandSwitchList[i].MinArgc ||
+ argc > CommandSwitchList[i].MaxArgc ) {
+
+ CommandUsage( ControlCode );
+ exit(4);
+
+ }
+
+ //
+ // Do command specific processing.
+ //
+ switch (ControlCode) {
+ case BROWSER_DEBUG_SET_DEBUG:
+ {
+ ULONG i = 0;
+
+
+ if ((Options = atol(argv[2])) == 0) {
+ PCHAR SwitchText;
+
+ if (argv[2][0] == '+') {
+ SwitchText = &argv[2][1];
+ ControlCode = BROWSER_DEBUG_SET_DEBUG;
+ } else if (argv[2][0] == '-') {
+ SwitchText = &argv[2][1];
+ ControlCode = BROWSER_DEBUG_CLEAR_DEBUG;
+ } else {
+ CommandUsage( ControlCode );
+ exit(4);
+ }
+
+ while (DebugSwitchList[i].SwitchName != NULL) {
+ if (!_stricmp(SwitchText, DebugSwitchList[i].SwitchName)) {
+ Options = DebugSwitchList[i].SwitchValue;
+ break;
+ }
+
+ i += 1;
+ }
+
+ if (DebugSwitchList[i].SwitchName == NULL) {
+ CommandUsage( ControlCode );
+ exit(4);
+ }
+
+ if (argc == 4) {
+ MultiByteToWideChar(CP_ACP, 0, argv[3], strlen(argv[3])+1, ServerBuffer, sizeof(ServerBuffer));
+ Server = ServerBuffer;
+ }
+
+ }
+
+ }
+ break;
+ case BROWSER_DEBUG_BREAK_POINT:
+ case BROWSER_DEBUG_DUMP_NETWORKS:
+ case BROWSER_DEBUG_TRUNCATE_LOG:
+ break;
+
+ case BROWSER_DEBUG_ENABLE_BROWSER:
+ {
+ NET_API_STATUS Status;
+
+ Status = EnableService(TEXT("BROWSER"));
+
+ if (Status != NERR_Success) {
+ printf("Unable to enable browser service - %ld\n", Status);
+ }
+
+ exit(Status);
+ }
+ break;
+ case BROWSER_DEBUG_BOWSERDEBUG:
+ if (argc == 3) {
+ if (_stricmp(argv[2], "TRUNCATE") == 0) {
+ TruncateBowserLog();
+ } else if (_stricmp(argv[2], "CLOSE") == 0) {
+ CloseBowserLog();
+ } else {
+ CommandUsage( BROWSER_DEBUG_BOWSERDEBUG );
+ }
+ } else if (argc == 4) {
+ if (_stricmp(argv[2], "OPEN") == 0) {
+ OpenBowserLog(argv[3]);
+ } else if (_stricmp(argv[2], "DEBUG") == 0) {
+ SetBowserDebug(argv[3]);
+ } else {
+ CommandUsage( BROWSER_DEBUG_BOWSERDEBUG );
+ }
+
+ }
+ exit(0);
+
+ case BROWSER_DEBUG_ELECT:
+ Elect(argv[2], argv[3], (argc == 5) ? argv[4] : NULL );
+ exit(0);
+ break;
+ case BROWSER_DEBUG_GET_MASTER:
+ GetMaster(argv[2], argv[3]);
+ exit(0);
+ break;
+ case BROWSER_DEBUG_TICKLE:
+ Tickle(argv[2], argv[3], (argc == 5) ? argv[4] : NULL );
+ exit(0);
+ break;
+ case BROWSER_DEBUG_FORCE_ANNOUNCE:
+ ForceAnnounce(argv[2], argv[3], (argc == 5) ? argv[4] : NULL );
+ exit(0);
+ break;
+ case BROWSER_DEBUG_GETPDC:
+ GetPdc(argv[2], argv[3]);
+ exit(0);
+ break;
+
+ case BROWSER_DEBUG_ADD_MASTERNAME:
+ AddMasterName(argv[2], argv[3], (argc==5 ? TRUE : FALSE));
+
+ exit(0);
+ break;
+
+ case BROWSER_DEBUG_ADD_DOMAINNAME:
+ AddDomainName(argv[2], argv[3], (argc==5 ? TRUE : FALSE));
+
+ exit(0);
+ break;
+
+ case BROWSER_DEBUG_FIND_MASTER:
+ FindMaster(argv[2], (argc==3 ? NULL : argv[3]) );
+ exit(0);
+ break;
+ case BROWSER_DEBUG_GET_BACKUP_LIST:
+ GetBlist(argv[2], (argc == 3 ? NULL : argv[3]), (BOOLEAN)(argc==5? TRUE : FALSE));
+
+ exit(0);
+ break;
+
+ case BROWSER_DEBUG_ANNOUNCE_MASTER:
+ AnnounceMaster(argv[2], argv[3], (argc == 5) ? argv[4] : NULL );
+
+ exit(0);
+ break;
+
+ case BROWSER_DEBUG_ILLEGAL_DGRAM:
+ IllegalDatagram(argv[2], argv[3], (argc == 5) ? argv[4] : NULL );
+
+ exit(0);
+ break;
+
+ case BROWSER_DEBUG_GET_OTHLIST:
+ GetOtherdomains(argv[2]);
+
+ exit(0);
+ break;
+
+ case BROWSER_DEBUG_VIEW:
+ View(argv[2],
+ (argc >= 4 ? argv[3] : NULL),
+ (argc >= 5 ? argv[4] : NULL),
+ (argc >= 6 ? argv[5] : NULL),
+#ifndef _PSS_RELEASE
+ (argc == 7 ? TRUE : FALSE));
+#else
+ FALSE);
+#endif
+
+ exit(0);
+ break;
+
+ case BROWSER_DEBUG_LIST_WFW:
+ ListWFW(argv[2]) ;
+ exit(0);
+ break;
+
+ case BROWSER_DEBUG_RPCLIST:
+ RpcList(argv[2], (argc >= 4 ? argv[3] : NULL), (argc >= 5 ? argv[4] : NULL), (argc == 6 ? TRUE : FALSE));
+
+ exit(0);
+ break;
+
+ case BROWSER_DEBUG_RPCCMP:
+ RpcCmp(argv[2], (argc >= 4 ? argv[3] : NULL), (argc >= 5 ? argv[4] : NULL), (argc == 6 ? TRUE : FALSE));
+
+ exit(0);
+ break;
+
+ case BROWSER_DEBUG_LOCAL_BRLIST:
+ GetLocalList(argv[2], (argc >= 4 ? argv[3] : NULL), (argc >= 5 ? argv[4] : NULL) );
+
+ exit(0);
+ break;
+
+ case BROWSER_DEBUG_GET_NETBIOSNAMES:
+ PrintNetbiosNames(argv[2], (argc >= 4 ? argv[3] : NULL));
+ exit(0);
+ break;
+
+ case BROWSER_DEBUG_ADD_ALTERNATE:
+ AddAlternateComputerName(argv[2], argv[3], (argc >= 5 ? argv[4] : NULL));
+
+ exit(0);
+ break;
+
+ case BROWSER_DEBUG_STATISTICS:
+ DumpStatistics(argc, argv[2]);
+
+ exit(0);
+ break;
+
+ case BROWSER_DEBUG_ANNOUNCE:
+ Announce(argv[2], argv[3], (argc >= 5 ? argv[4] : NULL ), (argc >= 6 ? TRUE : FALSE));
+
+ exit(0);
+ break;
+ case BROWSER_DEBUG_POPULATE_DOMAIN:
+ case BROWSER_DEBUG_POPULATE_SERVER:
+ Populate((ControlCode == BROWSER_DEBUG_POPULATE_DOMAIN ? TRUE : FALSE),
+ argv[2],
+ argv[3],
+ (argc >= 6 ? argv[5] : NULL),
+ argv[4],
+ (argc >= 7 ? argv[6] : NULL));
+
+ exit(0);
+ break;
+
+ case BROWSER_DEBUG_GET_WINSSERVER:
+ GetWinsServer(argv[2]);
+ exit(0);
+ break;
+
+ case BROWSER_DEBUG_GET_DOMAINLIST:
+ GetDomainList(argv[2]);
+ exit(0);
+ break;
+
+ case BROWSER_DEBUG_STATUS:
+ {
+ PCHAR Domain;
+ BOOL Verbose = FALSE;
+
+ if (argc == 4) {
+ if (_stricmp(argv[2], "-v") == 0) {
+ Verbose = TRUE;
+ Domain = argv[3];
+ } else {
+ CommandUsage( ControlCode );
+ exit(4);
+ }
+ } else if (argc == 3) {
+ if (_stricmp(argv[2], "-v") == 0) {
+ Verbose = TRUE;
+ Domain = NULL;
+ } else {
+ Domain = argv[2];
+ }
+ } else {
+ Domain = NULL;
+ }
+
+ BrowserStatus(Verbose, Domain);
+ }
+
+ exit(0);
+ break;
+
+ case BROWSER_DEBUG_SET_EMULATEDDOMAIN:
+ SetEmulatedDomain(argv[2], argv[3], (argc >= 5 ? argv[4] : NULL) );
+
+ exit(0);
+ break;
+
+
+ case BROWSER_DEBUG_SET_EMULATEDDOMAINENUM:
+ EnumEmulatedDomains();
+
+ exit(0);
+ break;
+
+ }
+
+ Status = I_BrowserDebugCall(Server, ControlCode, Options);
+
+ printf("Api call returned %ld\n", Status);
+
+}
+
+
+NET_API_STATUS
+EnableService(
+ IN LPTSTR ServiceName
+ )
+{
+ SC_HANDLE ServiceControllerHandle;
+ SC_HANDLE ServiceHandle;
+
+ ServiceControllerHandle = OpenSCManager(NULL, NULL, SC_MANAGER_ENUMERATE_SERVICE);
+
+ if (ServiceControllerHandle == NULL) {
+
+ return GetLastError();
+ }
+
+ ServiceHandle = OpenService(ServiceControllerHandle, ServiceName, SERVICE_CHANGE_CONFIG);
+
+ if (ServiceHandle == NULL) {
+
+ CloseServiceHandle(ServiceControllerHandle);
+ return GetLastError();
+ }
+
+ if (!ChangeServiceConfig(ServiceHandle, SERVICE_NO_CHANGE,
+ SERVICE_DEMAND_START,
+ SERVICE_NO_CHANGE,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL)) {
+ CloseServiceHandle(ServiceHandle);
+ CloseServiceHandle(ServiceControllerHandle);
+
+ return GetLastError();
+ }
+
+
+ CloseServiceHandle(ServiceHandle);
+
+ CloseServiceHandle(ServiceControllerHandle);
+
+ return NERR_Success;
+}
+
+
+VOID
+GetBlist(
+ IN PCHAR TransportName,
+ IN PCHAR DomainName,
+ IN BOOLEAN ForceRescan
+ )
+{
+ NET_API_STATUS Status;
+ UNICODE_STRING UTransportName;
+ LPTSTR Domain;
+ PWSTR *BrowserList;
+ ULONG BrowserListLength;
+ ULONG i;
+
+ qualify_transport(TransportName, &UTransportName ) ;
+
+ Domain = NULL;
+
+ if (DomainName != NULL) {
+ UNICODE_STRING UDomainName;
+ ANSI_STRING ADomainName;
+
+ RtlInitString(&ADomainName, DomainName);
+
+ RtlAnsiStringToUnicodeString(&UDomainName, &ADomainName, TRUE);
+
+ Domain = UDomainName.Buffer;
+ }
+
+ Status = GetBrowserServerList(&UTransportName, Domain,
+ &BrowserList,
+ &BrowserListLength,
+ ForceRescan);
+
+ if (Status != NERR_Success) {
+ printf("Unable to get backup list: %s\n", get_error_text(Status));
+ exit(1);
+ }
+
+ for (i = 0; i < BrowserListLength ; i ++ ) {
+ printf("Browser: %s\n", UnicodeToPrintfString(BrowserList[i]));
+ }
+
+}
+
+ NET_API_STATUS
+SendDatagramA(
+ IN PCHAR Transport,
+ IN PCHAR EmulatedDomain OPTIONAL,
+ IN PCHAR NetbiosName,
+ IN DGRECEIVER_NAME_TYPE NameType,
+ IN PVOID Buffer,
+ IN ULONG BufferSize
+ )
+/*++
+
+Routine Description:
+
+ Send a datagram on the specified transport.
+
+ The arguments are in the OEM character set.
+
+Arguments:
+
+ Transport - Transport to send on (might not be qualified yet.)
+
+ EmulatedDomain - Emulated Domain name. NULL implies primary domain.
+
+#endif
+ NetbiosName - Name to send the datagram to. (If Netbios name begins with
+ leading \\, they are removed.)
+
+ NameType - Type of 'Name'
+
+ Buffer - data to send
+
+ BufferSize - Number of byte in 'Buffer'
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+
+{
+ NET_API_STATUS NetStatus;
+
+ UNICODE_STRING TransportName;
+
+ UNICODE_STRING EmulatedDomainName;
+ ANSI_STRING AEmulatedDomainName;
+
+ UNICODE_STRING UNetbiosName;
+ ANSI_STRING ANetbiosName;
+
+ HANDLE BrowserHandle;
+
+ //
+ // Qualify the transport name and convert it to unicode
+ //
+ qualify_transport(Transport, &TransportName) ;
+
+ //
+ // Convert the emulated domain name to unicode
+ //
+ RtlInitString(&AEmulatedDomainName, EmulatedDomain);
+ RtlAnsiStringToUnicodeString(&EmulatedDomainName, &AEmulatedDomainName, TRUE);
+
+ //
+ // Convert the destination Netbios name to unicode
+ //
+
+ if (NetbiosName[0] == '\\' && NetbiosName[1] == '\\') {
+ RtlInitString(&ANetbiosName, &NetbiosName[2]);
+ } else {
+ RtlInitString(&ANetbiosName, NetbiosName );
+ }
+ RtlAnsiStringToUnicodeString(&UNetbiosName, &ANetbiosName, TRUE);
+
+
+ //
+ // Send the datagram
+ //
+
+ OpenBrowser(&BrowserHandle);
+
+ NetStatus = SendDatagram( BrowserHandle,
+ &TransportName,
+ &EmulatedDomainName,
+ UNetbiosName.Buffer,
+ NameType,
+ Buffer,
+ BufferSize );
+
+ CloseHandle(BrowserHandle);
+
+ return NetStatus;
+}
+
+VOID
+Elect(
+ IN PCHAR Transport,
+ IN PCHAR Domain,
+ IN PCHAR EmulatedDomain
+ )
+{
+ REQUEST_ELECTION ElectionRequest;
+
+ ElectionRequest.Type = Election;
+
+ ElectionRequest.ElectionRequest.Version = 0;
+ ElectionRequest.ElectionRequest.Criteria = 0;
+ ElectionRequest.ElectionRequest.TimeUp = 0;
+ ElectionRequest.ElectionRequest.MustBeZero = 0;
+ ElectionRequest.ElectionRequest.ServerName[0] = '\0';
+
+ SendDatagramA( Transport,
+ EmulatedDomain,
+ Domain,
+ BrowserElection,
+ &ElectionRequest,
+ sizeof(ElectionRequest) );
+
+}
+
+VOID
+Tickle(
+ IN PCHAR Transport,
+ IN PCHAR Domain,
+ IN PCHAR EmulatedDomain
+ )
+{
+ RESET_STATE ResetState;
+
+ ResetState.Type = ResetBrowserState;
+
+ ResetState.ResetStateRequest.Options = RESET_STATE_STOP_MASTER;
+
+ SendDatagramA( Transport,
+ EmulatedDomain,
+ Domain,
+ ((Domain[0] == '\\' && Domain[1] == '\\') ?
+ ComputerName : MasterBrowser),
+ &ResetState,
+ sizeof(ResetState));
+
+}
+
+VOID
+GetMaster(
+ IN PCHAR Transport,
+ IN PCHAR Domain
+ )
+{
+ NET_API_STATUS Status;
+ UNICODE_STRING TransportName;
+ ANSI_STRING AString;
+ WCHAR MasterName[256];
+ UNICODE_STRING DomainName;
+
+ qualify_transport(Transport, &TransportName ) ;
+
+ RtlInitString(&AString, Domain);
+ RtlAnsiStringToUnicodeString(&DomainName, &AString, TRUE);
+
+ Status = GetNetBiosMasterName(
+ TransportName.Buffer,
+ DomainName.Buffer,
+ MasterName,
+ NULL);
+
+ if (Status != NERR_Success) {
+ printf("Unable to get Master: %s\n", get_error_text(Status));
+ exit(1);
+ }
+
+ printf("Master Browser: %s\n", UnicodeToPrintfString(MasterName));
+
+}
+
+#define SPACES " "
+
+#define ClearNcb( PNCB ) { \
+ RtlZeroMemory( PNCB , sizeof (NCB) ); \
+ RtlCopyMemory( (PNCB)->ncb_name, SPACES, sizeof(SPACES)-1 );\
+ RtlCopyMemory( (PNCB)->ncb_callname, SPACES, sizeof(SPACES)-1 );\
+ }
+
+
+VOID
+AddMasterName(
+ IN PCHAR Transport,
+ IN PCHAR Domain,
+ IN BOOL Pause
+ )
+{
+ NET_API_STATUS Status;
+ UNICODE_STRING TransportName;
+ CCHAR LanaNum;
+ NCB AddNameNcb;
+
+ qualify_transport(Transport, &TransportName) ;
+
+ Status = BrGetLanaNumFromNetworkName(TransportName.Buffer, &LanaNum);
+
+ if (Status != NERR_Success) {
+ printf("Unable to get transport: %lx\n", Status);
+ }
+
+ ClearNcb(&AddNameNcb)
+
+ AddNameNcb.ncb_command = NCBRESET;
+ AddNameNcb.ncb_lsn = 0; // Request resources
+ AddNameNcb.ncb_lana_num = LanaNum;
+ AddNameNcb.ncb_callname[0] = 0; // 16 sessions
+ AddNameNcb.ncb_callname[1] = 0; // 16 commands
+ AddNameNcb.ncb_callname[2] = 0; // 8 names
+ AddNameNcb.ncb_callname[3] = 0; // Don't want the reserved address
+ Netbios( &AddNameNcb );
+
+ ClearNcb( &AddNameNcb );
+
+ //
+ // Uppercase the remote name.
+ //
+
+ _strupr(Domain);
+
+ AddNameNcb.ncb_command = NCBADDNAME;
+
+ RtlCopyMemory( AddNameNcb.ncb_name, Domain, strlen(Domain));
+
+ AddNameNcb.ncb_name[15] = MASTER_BROWSER_SIGNATURE;
+
+ AddNameNcb.ncb_lana_num = LanaNum;
+ AddNameNcb.ncb_length = 0;
+ AddNameNcb.ncb_buffer = NULL;
+ Netbios( &AddNameNcb );
+
+ if ( AddNameNcb.ncb_retcode == NRC_GOODRET ) {
+ printf("Successfully added master name!!!!!\n");
+ } else {
+ printf("Unable to add master name: %lx\n", AddNameNcb.ncb_retcode);
+ }
+
+ if (Pause) {
+ printf("Press any key to continue...");
+ getchar();
+ }
+
+
+}
+
+VOID
+AddDomainName(
+ IN PCHAR Transport,
+ IN PCHAR Domain,
+ IN BOOL Pause
+ )
+{
+ NET_API_STATUS Status;
+ UNICODE_STRING TransportName;
+ CCHAR LanaNum;
+ NCB AddNameNcb;
+
+ qualify_transport(Transport, &TransportName ) ;
+
+ Status = BrGetLanaNumFromNetworkName(TransportName.Buffer, &LanaNum);
+
+ if (Status != NERR_Success) {
+ printf("Unable to get transport: %lx\n", Status);
+ }
+
+ ClearNcb(&AddNameNcb)
+
+ AddNameNcb.ncb_command = NCBRESET;
+ AddNameNcb.ncb_lsn = 0; // Request resources
+ AddNameNcb.ncb_lana_num = LanaNum;
+ AddNameNcb.ncb_callname[0] = 0; // 16 sessions
+ AddNameNcb.ncb_callname[1] = 0; // 16 commands
+ AddNameNcb.ncb_callname[2] = 0; // 8 names
+ AddNameNcb.ncb_callname[3] = 0; // Don't want the reserved address
+ Netbios( &AddNameNcb );
+
+ ClearNcb( &AddNameNcb );
+
+ //
+ // Uppercase the remote name.
+ //
+
+ _strupr(Domain);
+
+ AddNameNcb.ncb_command = NCBADDNAME;
+
+ RtlCopyMemory( AddNameNcb.ncb_name, Domain, strlen(Domain));
+
+ AddNameNcb.ncb_name[15] = PRIMARY_DOMAIN_SIGNATURE;
+
+ AddNameNcb.ncb_lana_num = LanaNum;
+ AddNameNcb.ncb_length = 0;
+ AddNameNcb.ncb_buffer = NULL;
+ Netbios( &AddNameNcb );
+
+ if ( AddNameNcb.ncb_retcode == NRC_GOODRET ) {
+ printf("Successfully added master name!!!!!\n");
+ } else {
+ printf("Unable to add master name: %lx\n", AddNameNcb.ncb_retcode);
+ }
+
+ if (Pause) {
+ printf("Press any key to continue...");
+ getchar();
+ }
+
+
+}
+
+VOID
+FindMaster(
+ IN PCHAR Transport,
+ IN PCHAR EmulatedDomain
+ )
+{
+ NET_API_STATUS Status;
+ UNICODE_STRING TransportName;
+ UNICODE_STRING EmulatedDomainName;
+ ANSI_STRING AEmulatedDomainName;
+ LPWSTR MasterName;
+
+ qualify_transport(Transport, &TransportName) ;
+
+ RtlInitString(&AEmulatedDomainName, EmulatedDomain);
+ RtlAnsiStringToUnicodeString(&EmulatedDomainName, &AEmulatedDomainName, TRUE);
+
+ Status = GetMasterServerNames(&TransportName, &EmulatedDomainName, &MasterName);
+
+ if (Status != NERR_Success) {
+ printf("Unable to get Master: %s\n", get_error_text(Status));
+ exit(1);
+ }
+
+ printf("Master Browser: %s\n", UnicodeToPrintfString(MasterName));
+
+}
+
+
+ NET_API_STATUS
+GetMasterServerNames(
+ IN PUNICODE_STRING NetworkName,
+ IN PUNICODE_STRING EmulatedDomainName,
+ OUT LPWSTR *MasterName
+ )
+/*++
+
+Routine Description:
+
+ This function is the worker routine called to determine the name of the
+ master browser server for a particular network.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ Status - The status of the operation.
+
+--*/
+{
+ NET_API_STATUS Status;
+ HANDLE BrowserHandle;
+
+ PLMDR_REQUEST_PACKET RequestPacket = NULL;
+
+ RequestPacket = malloc(sizeof(LMDR_REQUEST_PACKET)+MAXIMUM_FILENAME_LENGTH*sizeof(WCHAR));
+
+ if (RequestPacket == NULL) {
+ return(ERROR_NOT_ENOUGH_MEMORY);
+ }
+
+ Status = OpenBrowser(&BrowserHandle);
+
+ if (Status != NERR_Success) {
+ return(Status);
+ }
+
+ RequestPacket->Version = LMDR_REQUEST_PACKET_VERSION_DOM;
+
+ RequestPacket->TransportName = *NetworkName;
+ RequestPacket->EmulatedDomainName = *EmulatedDomainName;
+
+ //
+ // Reference the network while the I/O is pending.
+ //
+
+ Status = BrDgReceiverIoControl(BrowserHandle,
+ IOCTL_LMDR_GET_MASTER_NAME,
+ RequestPacket,
+ sizeof(LMDR_REQUEST_PACKET)+NetworkName->Length,
+ RequestPacket,
+ sizeof(LMDR_REQUEST_PACKET)+MAXIMUM_FILENAME_LENGTH*sizeof(WCHAR),
+ NULL);
+
+ if (Status != NERR_Success) {
+
+ printf("Browser: Unable to determine master for network %s: %ld\n", UnicodeToPrintfString(NetworkName->Buffer), Status);
+
+ free(RequestPacket);
+
+ return(Status);
+ }
+
+ *MasterName = malloc(RequestPacket->Parameters.GetMasterName.MasterNameLength+sizeof(WCHAR));
+
+ RtlCopyMemory(*MasterName, RequestPacket->Parameters.GetMasterName.Name,
+ RequestPacket->Parameters.GetMasterName.MasterNameLength+sizeof(WCHAR));
+
+ free(RequestPacket);
+
+ return Status;
+}
+
+ VOID
+AnnounceMaster(
+ IN PCHAR Transport,
+ IN PCHAR ServerName,
+ IN PCHAR EmulatedDomain
+ )
+{
+ CHAR Buffer[sizeof(MASTER_ANNOUNCEMENT)+MAX_COMPUTERNAME_LENGTH+1];
+ PMASTER_ANNOUNCEMENT MasterAnnouncementp = (PMASTER_ANNOUNCEMENT)Buffer;
+ ULONG ComputerNameSize = MAX_COMPUTERNAME_LENGTH+1;
+
+
+ //
+ // Get the computer name of this machine and put it in the announcement
+ //
+
+ GetComputerNameA( MasterAnnouncementp->MasterAnnouncement.MasterName,
+ &ComputerNameSize);
+
+
+ //
+ // Send the announcement
+ //
+
+ MasterAnnouncementp->Type = MasterAnnouncement;
+
+ SendDatagramA( Transport,
+ EmulatedDomain,
+ ServerName,
+ ComputerName,
+ MasterAnnouncementp,
+ FIELD_OFFSET(MASTER_ANNOUNCEMENT, MasterAnnouncement.MasterName) + ComputerNameSize+sizeof(CHAR));
+
+ return;
+}
+
+ VOID
+IllegalDatagram(
+ IN PCHAR Transport,
+ IN PCHAR ServerName,
+ IN PCHAR EmulatedDomain
+ )
+{
+ REQUEST_ELECTION ElectRequest;
+
+ ElectRequest.Type = Election;
+
+ SendDatagramA( Transport,
+ EmulatedDomain,
+ ServerName,
+ ComputerName,
+ &ElectRequest,
+ FIELD_OFFSET(REQUEST_ELECTION, ElectionRequest.TimeUp) );
+
+ return;
+}
+
+ VOID
+GetOtherdomains(
+ IN PCHAR ServerName
+ )
+{
+ NET_API_STATUS Status;
+ ANSI_STRING AServerName;
+ UNICODE_STRING UServerName;
+ PVOID Buffer;
+ PSERVER_INFO_100 ServerInfo;
+ ULONG i;
+ ULONG EntriesRead;
+ ULONG TotalEntries;
+
+ RtlInitString(&AServerName, ServerName);
+
+ RtlAnsiStringToUnicodeString(&UServerName, &AServerName, TRUE);
+
+ if ((wcslen(UServerName.Buffer) < 3) ||
+ wcsncmp(UServerName.Buffer, TEXT("\\\\"), 2) != 0 ||
+ I_NetNameValidate(NULL,
+ ((LPWSTR)UServerName.Buffer)+2,
+ NAMETYPE_COMPUTER,
+ 0L))
+ {
+ printf("Unable to query otherdomains: Invalid computer name\n") ;
+ return;
+ }
+
+ Status = I_BrowserQueryOtherDomains(UServerName.Buffer, (LPBYTE *)&Buffer, &EntriesRead, &TotalEntries);
+
+ if (Status != NERR_Success) {
+ printf("Unable to query otherdomains: %s\n", get_error_text(Status));
+ return;
+ }
+
+ printf("Other domains:\n");
+
+ ServerInfo = Buffer;
+
+ for (i = 0 ; i < EntriesRead; i++) {
+ printf(" %s\n", UnicodeToPrintfString(ServerInfo->sv100_name));
+ ServerInfo ++;
+ }
+
+ return;
+}
+
+VOID
+View(
+ IN PCHAR Transport,
+ IN PCHAR ServerOrDomain,
+ IN PCHAR FlagsString,
+ IN PCHAR Domain,
+ IN BOOL GoForever
+ )
+{
+ NET_API_STATUS Status;
+ UNICODE_STRING TransportName;
+ ANSI_STRING AServerName;
+ UNICODE_STRING UServerName;
+ ANSI_STRING ADomainName;
+ UNICODE_STRING UDomainName;
+ ULONG Flags ;
+ PVOID ServerList;
+ PSERVER_INFO_101 Server;
+ ULONG EntriesInList;
+ ULONG TotalEntries;
+ unsigned int i;
+
+ if ((ServerOrDomain && _stricmp(ServerOrDomain,"/domain")==0) ||
+ (Domain && _stricmp(Domain,"/domain")==0) )
+ {
+ CommandUsage( BROWSER_DEBUG_VIEW );
+ exit(4);
+ }
+
+ if (FlagsString)
+ {
+ if (_stricmp(FlagsString,"/domain")==0)
+ Flags = SV_TYPE_DOMAIN_ENUM ;
+ else
+ Flags = strtoul(FlagsString, NULL, 0);
+ }
+ else
+ Flags = SV_TYPE_ALL ;
+
+ qualify_transport(Transport, &TransportName) ;
+
+ RtlInitString(&AServerName, ServerOrDomain);
+
+ RtlAnsiStringToUnicodeString(&UServerName, &AServerName, TRUE);
+
+ if (ARGUMENT_PRESENT(Domain)) {
+ RtlInitString(&ADomainName, Domain);
+
+ RtlAnsiStringToUnicodeString(&UDomainName, &ADomainName, TRUE);
+
+ //
+ // if domain is present, this must be computername
+ //
+ if ((wcslen(UServerName.Buffer) < 3) ||
+ wcsncmp(UServerName.Buffer, TEXT("\\\\"), 2) != 0 ||
+ I_NetNameValidate(NULL,
+ ((LPWSTR)UServerName.Buffer)+2,
+ NAMETYPE_COMPUTER,
+ 0L))
+ {
+ printf("Invalid computer name: %s\n", ServerOrDomain) ;
+ exit(1);
+ }
+
+ }
+
+ if (UServerName.Buffer[0] != L'\\' || UServerName.Buffer[1] != L'\\') {
+ PWSTR *BrowserList;
+ ULONG BrowserListLength;
+
+ Status = GetBrowserServerList(&TransportName, UServerName.Buffer,
+ &BrowserList, &BrowserListLength, FALSE);
+
+ if (Status != NERR_Success) {
+ printf("Unable to get backup list for %s on transport %s: %s\n", UnicodeToPrintfString(UServerName.Buffer), UnicodeToPrintfString2(TransportName.Buffer), get_error_text(Status));
+ exit(1);
+ }
+
+ if (BrowserListLength == 0) {
+ printf("Unable to get backup list for %s", UnicodeToPrintfString(UServerName.Buffer));
+ printf(" on transport %s: %s\n", UnicodeToPrintfString(TransportName.Buffer), get_error_text(Status));
+ exit(1);
+ }
+
+ UServerName.Buffer = *BrowserList;
+
+ }
+
+ printf("Remoting NetServerEnum to %s", UnicodeToPrintfString(UServerName.Buffer));
+ printf(" on transport %s with flags %lx\n", UnicodeToPrintfString(TransportName.Buffer), Flags);
+
+ do {
+
+ DWORD StartTime = GetTickCount();
+ DWORD EndTime;
+
+ Status = RxNetServerEnum(UServerName.Buffer,
+ TransportName.Buffer,
+ 101,
+ (LPBYTE *)&ServerList,
+ 0xffffffff,
+ &EntriesInList,
+ &TotalEntries,
+ Flags,
+ ARGUMENT_PRESENT(Domain) ? UDomainName.Buffer : NULL,
+ NULL
+ );
+
+ EndTime = GetTickCount();
+
+ if (Status != NERR_Success) {
+ printf("Unable to remote API to %s ", UnicodeToPrintfString(UServerName.Buffer));
+ printf("on transport %s: %s (%d milliseconds)\n", UnicodeToPrintfString(TransportName.Buffer), get_error_text(Status), EndTime - StartTime);
+
+ if (Status != ERROR_MORE_DATA) {
+ exit(1);
+ }
+ }
+
+ printf("%ld entries returned. %ld total. %ld milliseconds\n\n", EntriesInList, TotalEntries, EndTime-StartTime);
+
+ if (!GoForever) {
+ Server = ServerList;
+
+ for (i = 0; i < EntriesInList ; i ++ ) {
+ DisplayServerInfo101( &Server[i], Flags==SV_TYPE_DOMAIN_ENUM );
+ printf("\n");
+ }
+ }
+
+ NetApiBufferFree(ServerList);
+
+ } while ( GoForever );
+
+
+ return;
+
+}
+
+VOID
+ListWFW(
+ IN PCHAR Domain
+ )
+{
+ NET_API_STATUS Status;
+ ANSI_STRING ADomainName;
+ UNICODE_STRING UDomainName;
+ PVOID ServerList;
+ PSERVER_INFO_101 Server;
+ ULONG EntriesInList;
+ ULONG TotalEntries;
+ unsigned int i;
+
+ RtlInitString(&ADomainName, Domain);
+ RtlAnsiStringToUnicodeString(&UDomainName, &ADomainName, TRUE);
+
+ printf("Calling NetServerEnum to enumerate WFW servers.\n") ;
+
+ Status = NetServerEnum(NULL,
+ 101,
+ (LPBYTE *)&ServerList,
+ 0xffffffff,
+ &EntriesInList,
+ &TotalEntries,
+ SV_TYPE_WFW,
+ UDomainName.Buffer,
+ NULL) ;
+
+ if (Status != NERR_Success)
+ {
+ printf("Unable to enumerate WFW servers. Error: %s\n",
+ get_error_text(Status));
+ exit(1);
+ }
+
+ printf("%ld WFW servers returned. %ld total.\n",
+ EntriesInList,
+ TotalEntries);
+ if (EntriesInList == 0)
+ printf("There are WFW servers with an active Browser.\n") ;
+ else
+ {
+ printf("The following are running the browser:\n\n") ;
+ Server = ServerList;
+ for (i = 0; i < EntriesInList ; i ++ ) {
+ DWORD ServerType = Server[i].sv101_type ;
+
+ if (!(ServerType & (SV_TYPE_POTENTIAL_BROWSER |
+ SV_TYPE_BACKUP_BROWSER |
+ SV_TYPE_MASTER_BROWSER ))) {
+ continue ;
+ }
+
+ DisplayServerInfo101( &Server[i], FALSE );
+ printf( "\n" );
+
+ }
+ }
+
+ NetApiBufferFree(ServerList);
+
+ return;
+}
+
+
+VOID
+ForceAnnounce(
+ IN PCHAR Transport,
+ IN PCHAR Domain,
+ IN PCHAR EmulatedDomain
+ )
+{
+ REQUEST_ANNOUNCE_PACKET RequestAnnounce;
+ ULONG NameSize = sizeof(RequestAnnounce.RequestAnnouncement.Reply);
+
+ //
+ // Build the request
+ //
+
+ RequestAnnounce.Type = AnnouncementRequest;
+
+ RequestAnnounce.RequestAnnouncement.Flags = 0;
+
+ GetComputerNameA(RequestAnnounce.RequestAnnouncement.Reply, &NameSize);
+
+ //
+ // Send the request
+ //
+
+ SendDatagramA( Transport,
+ EmulatedDomain,
+ Domain,
+ BrowserElection,
+ &RequestAnnounce,
+ FIELD_OFFSET(REQUEST_ANNOUNCE_PACKET, RequestAnnouncement.Reply) + NameSize + sizeof(CHAR));
+
+}
+
+VOID
+GetLocalList(
+ IN PCHAR Transport,
+ IN PCHAR FlagsString,
+ IN PCHAR EmulatedDomain
+ )
+{
+ NET_API_STATUS Status;
+ UNICODE_STRING TransportName;
+ ANSI_STRING AEmulatedDomainName;
+ UNICODE_STRING EmulatedDomainName;
+ ULONG Flags = (FlagsString == NULL ? SV_TYPE_ALL : strtoul(FlagsString, NULL, 0));
+ PVOID ServerList;
+ PSERVER_INFO_101 Server;
+ ULONG EntriesInList;
+ ULONG TotalEntries;
+ unsigned int i;
+
+ qualify_transport(Transport, &TransportName) ;
+
+ RtlInitString(&AEmulatedDomainName, EmulatedDomain );
+ RtlAnsiStringToUnicodeString(&EmulatedDomainName, &AEmulatedDomainName, TRUE);
+
+ printf("Retrieving local browser list on transport %ws\\%s with flags %lx\n", EmulatedDomainName.Buffer, UnicodeToPrintfString(TransportName.Buffer), Flags);
+
+ Status = GetLocalBrowseList (&TransportName,
+ &EmulatedDomainName,
+ 101,
+ Flags,
+ (LPBYTE *)&ServerList,
+ &EntriesInList,
+ &TotalEntries
+ );
+
+ if (Status != NERR_Success) {
+ printf("Unable to retrieve local list on transport %s: %lx\n", UnicodeToPrintfString(TransportName.Buffer), Status);
+
+ exit(1);
+ }
+
+ Server = ServerList;
+
+ printf("%ld entries returned. %ld total.\n", EntriesInList, TotalEntries);
+
+ for (i = 0; i < EntriesInList ; i ++ ) {
+
+ DisplayServerInfo101( &Server[i], Flags==SV_TYPE_DOMAIN_ENUM );
+
+ if (Flags == SV_TYPE_BACKUP_BROWSER) {
+ PUSHORT BrowserVersion = (PUSHORT)Server[i].sv101_comment - 1;
+ printf(" V:%4.4x", *BrowserVersion);
+ }
+
+ printf("\n") ;
+
+ }
+
+
+
+ return;
+
+}
+
+ NET_API_STATUS
+GetLocalBrowseList(
+ IN PUNICODE_STRING Network,
+ IN PUNICODE_STRING EmulatedDomainName,
+ IN ULONG Level,
+ IN ULONG ServerType,
+ OUT PVOID *ServerList,
+ OUT PULONG EntriesRead,
+ OUT PULONG TotalEntries
+ )
+{
+ NET_API_STATUS status;
+ PLMDR_REQUEST_PACKET Drp; // Datagram receiver request packet
+ ULONG DrpSize;
+ HANDLE BrowserHandle;
+ LPBYTE Where;
+
+ OpenBrowser(&BrowserHandle);
+
+ //
+ // Allocate the request packet large enough to hold the variable length
+ // domain name.
+ //
+
+ DrpSize = sizeof(LMDR_REQUEST_PACKET) +
+ Network->Length + sizeof(WCHAR) +
+ EmulatedDomainName->Length + sizeof(WCHAR);
+
+ if ((Drp = malloc(DrpSize)) == NULL) {
+
+ return GetLastError();
+ }
+
+ //
+ // Set up request packet. Output buffer structure is of enumerate
+ // servers type.
+ //
+
+ Drp->Version = LMDR_REQUEST_PACKET_VERSION_DOM;
+ Drp->Type = EnumerateServers;
+
+ Drp->Level = Level;
+
+ Drp->Parameters.EnumerateServers.ServerType = ServerType;
+ Drp->Parameters.EnumerateServers.ResumeHandle = 0;
+ Drp->Parameters.EnumerateServers.DomainNameLength = 0;
+ Drp->Parameters.EnumerateServers.DomainName[0] = '\0';
+
+ Where = ((PCHAR)Drp+sizeof(LMDR_REQUEST_PACKET));
+ wcscpy( (LPWSTR)Where, Network->Buffer );
+ RtlInitUnicodeString( &Drp->TransportName, (LPWSTR) Where );
+
+ Where += Drp->TransportName.MaximumLength;
+ wcscpy( (LPWSTR)Where, EmulatedDomainName->Buffer );
+ RtlInitUnicodeString( &Drp->EmulatedDomainName, (LPWSTR) Where );
+#ifdef notdef
+ Where += Drp->EmulatedDomainName.MaximumLength;
+#endif // notdef
+
+ //
+ // Ask the datagram receiver to enumerate the servers
+ //
+
+ status = DeviceControlGetInfo(
+ BrowserHandle,
+ IOCTL_LMDR_ENUMERATE_SERVERS,
+ Drp,
+ DrpSize,
+ ServerList,
+ 0xffffffff,
+ 4096,
+ NULL
+ );
+
+ *EntriesRead = Drp->Parameters.EnumerateServers.EntriesRead;
+ *TotalEntries = Drp->Parameters.EnumerateServers.TotalEntries;
+
+ (void) free(Drp);
+
+ return status;
+
+}
+
+VOID
+PrintNetbiosNames(
+ IN PCHAR Transport,
+ IN PCHAR EmulatedDomain OPTIONAL
+ )
+/*++
+
+Routine Description:
+
+ Prints the list of Netbios names registered on a particular transport
+
+Arguments:
+
+ Transport - Transport to query
+
+ EmulatedDomain - Emulated domain to query
+
+#endif
+Return Value:
+
+ None
+
+--*/
+{
+ NET_API_STATUS Status;
+ UNICODE_STRING TransportName;
+ ANSI_STRING AEmulatedDomainName;
+ UNICODE_STRING EmulatedDomainName;
+ PVOID NameList;
+ PDGRECEIVE_NAMES Names;
+ ULONG EntriesInList;
+ ULONG TotalEntries;
+ unsigned int i;
+
+ //
+ // Get the netbios names
+ //
+ qualify_transport(Transport, &TransportName ) ;
+
+ RtlInitString(&AEmulatedDomainName, EmulatedDomain );
+ RtlAnsiStringToUnicodeString(&EmulatedDomainName, &AEmulatedDomainName, TRUE);
+
+ printf("Retrieving browser Netbios names on transport %ws\\%ws\n", EmulatedDomainName.Buffer, TransportName.Buffer);
+
+ Status = GetNetbiosNames(&TransportName,
+ &EmulatedDomainName,
+ &NameList,
+ &EntriesInList,
+ &TotalEntries
+ );
+
+ if (Status != NERR_Success) {
+ printf("Unable to retrieve Netbios names on transport %ws: %lx\n", TransportName.Buffer, Status);
+ exit(1);
+ }
+
+ //
+ // Print the netbios names.
+ //
+
+ Names = NameList;
+
+ printf("%ld entries returned. %ld total.\n", EntriesInList, TotalEntries);
+
+ for (i = 0; i < EntriesInList ; i ++ ) {
+ if ( Names[i].Type == DomainAnnouncement ) {
+ printf("%-16.16s", "__MSBROWSE__" );
+ } else {
+ printf("%-16.16wZ", &Names[i].DGReceiverName );
+ }
+ switch ( Names[i].Type ) {
+ case ComputerName:
+ printf("<00> ComputerName"); break;
+ case AlternateComputerName:
+ printf("<00> AlternateComputerName"); break;
+ case PrimaryDomain:
+ printf("<00> PrimaryDomain"); break;
+ case LogonDomain:
+ printf("<00> LogonDomain"); break;
+ case OtherDomain:
+ printf("<00> OtherDomain"); break;
+ case DomainAnnouncement:
+ printf("DomainAnnouncement"); break;
+ case MasterBrowser:
+ printf("<1D> MasterBrowser"); break;
+ case BrowserElection:
+ printf("<1E> BrowserElection"); break;
+ case BrowserServer:
+ printf("<20> BrowserServer"); break;
+ case DomainName:
+ printf("<1C> DomainName"); break;
+ case PrimaryDomainBrowser:
+ printf("<1B> DomainMasterBrowser"); break;
+ default:
+ printf("<Unknown>"); break;
+ }
+ printf("\n") ;
+
+ }
+
+ return;
+
+}
+
+ NET_API_STATUS
+GetNetbiosNames(
+ IN PUNICODE_STRING Network,
+ IN PUNICODE_STRING EmulatedDomainName,
+ OUT PVOID *NameList,
+ OUT PULONG EntriesRead,
+ OUT PULONG TotalEntries
+ )
+{
+ NET_API_STATUS status;
+ PLMDR_REQUEST_PACKET Drp; // Datagram receiver request packet
+ ULONG DrpSize;
+ HANDLE BrowserHandle;
+ LPBYTE Where;
+
+ OpenBrowser(&BrowserHandle);
+
+ //
+ // Allocate the request packet large enough to hold the variable length
+ // domain name.
+ //
+
+ DrpSize = sizeof(LMDR_REQUEST_PACKET) +
+ Network->Length + sizeof(WCHAR) +
+ EmulatedDomainName->Length + sizeof(WCHAR);
+
+ if ((Drp = malloc(DrpSize)) == NULL) {
+ return GetLastError();
+ }
+
+ //
+ // Set up request packet. Output buffer structure is of enumerate
+ // servers type.
+ //
+
+ Drp->Version = LMDR_REQUEST_PACKET_VERSION_DOM;
+ Drp->Type = EnumerateNames;
+
+ Drp->Level = 0;
+
+ Drp->Parameters.EnumerateNames.ResumeHandle = 0;
+
+ Where = ((PCHAR)Drp+sizeof(LMDR_REQUEST_PACKET));
+ wcscpy( (LPWSTR)Where, Network->Buffer );
+ RtlInitUnicodeString( &Drp->TransportName, (LPWSTR) Where );
+
+ Where += Drp->TransportName.MaximumLength;
+ wcscpy( (LPWSTR)Where, EmulatedDomainName->Buffer );
+ RtlInitUnicodeString( &Drp->EmulatedDomainName, (LPWSTR) Where );
+#ifdef notdef
+ Where += Drp->EmulatedDomainName.MaximumLength;
+#endif // notdef
+
+ //
+ // Ask the datagram receiver to enumerate the names
+ //
+
+ status = DeviceControlGetInfo(
+ BrowserHandle,
+ IOCTL_LMDR_ENUMERATE_NAMES,
+ Drp,
+ DrpSize,
+ NameList,
+ 0xffffffff,
+ 4096,
+ NULL );
+
+ *EntriesRead = Drp->Parameters.EnumerateNames.EntriesRead;
+ *TotalEntries = Drp->Parameters.EnumerateNames.TotalEntries;
+
+ (void) free(Drp);
+
+ return status;
+
+}
+
+ NET_API_STATUS
+AddAlternateComputerName(
+ IN PCHAR Transport,
+ IN PCHAR ComputerName,
+ IN PCHAR EmulatedDomain
+ )
+/*++
+
+Routine Description:
+
+ This function adds an alternate compture name on the specified transport.
+
+Arguments:
+
+ Transport - Transport to add the computer name on.
+
+ ComputerName - Alternate computer name to add
+
+ EmulatedDomain - Emulated Domain to add computer name on
+
+Return Value:
+
+ Status - The status of the operation.
+
+--*/
+{
+ NET_API_STATUS Status;
+ HANDLE BrowserHandle;
+ LPBYTE Where;
+
+ PLMDR_REQUEST_PACKET RequestPacket = NULL;
+
+ UNICODE_STRING TransportName;
+ WCHAR UnicodeComputerName[CNLEN+1];
+
+ UNICODE_STRING EmulatedDomainName;
+ ANSI_STRING AEmulatedDomainName;
+
+ //
+ // Qualify the transport name and convert it to unicode
+ //
+ qualify_transport(Transport, &TransportName) ;
+ NetpCopyStrToWStr( UnicodeComputerName, ComputerName );
+
+ //
+ // Convert the emulated domain name to unicode
+ //
+ RtlInitString(&AEmulatedDomainName, EmulatedDomain);
+ RtlAnsiStringToUnicodeString(&EmulatedDomainName, &AEmulatedDomainName, TRUE);
+
+ RequestPacket = malloc(sizeof(LMDR_REQUEST_PACKET)+(LM20_CNLEN+1)*sizeof(WCHAR));
+
+ if (RequestPacket == NULL) {
+ return(ERROR_NOT_ENOUGH_MEMORY);
+ }
+
+ Status = OpenBrowser(&BrowserHandle);
+
+ if (Status != NERR_Success) {
+ return(Status);
+ }
+
+ RequestPacket->Version = LMDR_REQUEST_PACKET_VERSION;
+
+ RequestPacket->TransportName = TransportName;
+ RequestPacket->EmulatedDomainName = EmulatedDomainName;
+
+ RequestPacket->Parameters.AddDelName.Type = AlternateComputerName;
+ RequestPacket->Parameters.AddDelName.DgReceiverNameLength =
+ wcslen(UnicodeComputerName)*sizeof(WCHAR);
+ wcscpy(RequestPacket->Parameters.AddDelName.Name, UnicodeComputerName);
+ Where = ((LPBYTE)(RequestPacket->Parameters.AddDelName.Name)) +
+ RequestPacket->Parameters.AddDelName.DgReceiverNameLength +
+ sizeof(WCHAR);
+
+ //
+ // Reference the network while the I/O is pending.
+ //
+
+ Status = BrDgReceiverIoControl(BrowserHandle,
+ IOCTL_LMDR_ADD_NAME_DOM,
+ RequestPacket,
+ Where - (LPBYTE)RequestPacket,
+ NULL,
+ 0,
+ NULL);
+
+ if (Status != NERR_Success) {
+
+ printf("Browser: Unable to add name for network %s: %ld\n", UnicodeToPrintfString(TransportName.Buffer), Status);
+
+ free(RequestPacket);
+
+ return(Status);
+ }
+
+ free(RequestPacket);
+
+ return Status;
+}
+
+VOID
+Announce(
+ IN PCHAR Transport,
+ IN PCHAR Domain,
+ IN PCHAR EmulatedDomain,
+ IN BOOL AsMaster
+ )
+{
+
+ PSERVER_INFO_101 ServerInfo;
+ PWKSTA_INFO_101 WkstaInfo;
+ UNICODE_STRING TransportName;
+ LPBYTE Buffer;
+ ULONG BrowserType;
+ ULONG OriginalBrowserType;
+ WCHAR UDomain[256];
+ WCHAR ServerComment[256];
+ WCHAR ServerName[256];
+ BOOLEAN IsLocalDomain;
+ SERVICE_STATUS ServiceStatus;
+ DWORD VersionMajor;
+ DWORD VersionMinor;
+ BOOL UsedDefaultChar;
+
+ qualify_transport(Transport, &TransportName ) ;
+
+
+ MultiByteToWideChar(CP_ACP, 0, Domain, strlen(Domain)+1, UDomain, sizeof(UDomain));
+
+ NetServerGetInfo(NULL, 101, &Buffer);
+
+ ServerInfo = (PSERVER_INFO_101 )Buffer;
+
+ BrowserType = (ServerInfo->sv101_type & (SV_TYPE_BACKUP_BROWSER | SV_TYPE_POTENTIAL_BROWSER | SV_TYPE_MASTER_BROWSER));
+
+ wcscpy(ServerComment, ServerInfo->sv101_comment);
+
+ wcscpy(ServerName, ServerInfo->sv101_name);
+
+ VersionMajor = ServerInfo->sv101_version_major;
+
+ VersionMinor = ServerInfo->sv101_version_minor;
+
+ NetApiBufferFree(Buffer);
+
+ NetWkstaGetInfo(NULL, 101, &Buffer);
+
+ WkstaInfo = (PWKSTA_INFO_101 )Buffer;
+
+ IsLocalDomain = !_wcsicmp(UDomain, WkstaInfo->wki101_langroup);
+
+ NetApiBufferFree(Buffer);
+
+ OriginalBrowserType = BrowserType;
+
+ if (AsMaster) {
+ BrowserType |= SV_TYPE_MASTER_BROWSER;
+ }
+
+ //
+ // If the browser is running, and this is our local domain, have the
+ // server do the announcing.
+ //
+
+ if (IsLocalDomain &&
+ CheckForService(SERVICE_BROWSER, &ServiceStatus) == NERR_Success ) {
+
+ printf("Toggling local server status bits to %lx and then to %lx\n",
+ BrowserType, OriginalBrowserType);
+
+ I_NetServerSetServiceBits(NULL, TransportName.Buffer, BrowserType, TRUE);
+
+ I_NetServerSetServiceBits(NULL, TransportName.Buffer, OriginalBrowserType, TRUE);
+
+ } else {
+ BROWSE_ANNOUNCE_PACKET BrowseAnnouncement;
+
+ printf("Announcing to domain %s by hand\n", UnicodeToPrintfString(UDomain));
+
+ BrowseAnnouncement.BrowseType = (AsMaster ? LocalMasterAnnouncement : HostAnnouncement);
+
+ BrowseAnnouncement.BrowseAnnouncement.UpdateCount = 0;
+
+ WideCharToMultiByte(CP_OEMCP, 0,
+ ServerName,
+ wcslen(ServerName),
+ BrowseAnnouncement.BrowseAnnouncement.ServerName,
+ LM20_CNLEN+1,
+ "?",
+ &UsedDefaultChar
+ );
+
+ BrowseAnnouncement.BrowseAnnouncement.VersionMajor = (UCHAR)VersionMajor;
+ BrowseAnnouncement.BrowseAnnouncement.VersionMinor = (UCHAR)VersionMinor;
+ BrowseAnnouncement.BrowseAnnouncement.Type = BrowserType;
+
+ WideCharToMultiByte(CP_OEMCP, 0,
+ ServerComment,
+ wcslen(ServerComment),
+ BrowseAnnouncement.BrowseAnnouncement.Comment,
+ LM20_MAXCOMMENTSZ+1,
+ "?",
+ &UsedDefaultChar
+ );
+
+ BrowseAnnouncement.BrowseAnnouncement.CommentPointer = NULL;
+
+
+ //
+ // Send the request
+ //
+
+ SendDatagramA( Transport,
+ EmulatedDomain,
+ Domain,
+ (AsMaster ? BrowserElection : MasterBrowser),
+ &BrowseAnnouncement,
+ sizeof(BrowseAnnouncement));
+
+ }
+
+ return;
+}
+
+VOID
+RpcList(
+ IN PCHAR Transport,
+ IN PCHAR ServerOrDomain,
+ IN PCHAR FlagsString,
+ IN BOOL GoForever
+ )
+{
+ NET_API_STATUS Status;
+ UNICODE_STRING TransportName;
+ ANSI_STRING AServerName;
+ UNICODE_STRING UServerName;
+ ULONG Flags = (FlagsString == NULL ? SV_TYPE_ALL : strtoul(FlagsString, NULL, 0));
+ PVOID ServerList;
+ PSERVER_INFO_101 Server;
+ ULONG EntriesInList;
+ ULONG TotalEntries;
+ unsigned int i;
+
+ qualify_transport(Transport, &TransportName) ;
+
+ RtlInitString(&AServerName, ServerOrDomain);
+ RtlAnsiStringToUnicodeString(&UServerName, &AServerName, TRUE);
+
+ if (UServerName.Buffer[0] != L'\\' || UServerName.Buffer[1] != L'\\') {
+ PWSTR *BrowserList;
+ ULONG BrowserListLength;
+
+ Status = GetBrowserServerList(&TransportName, UServerName.Buffer,
+ &BrowserList, &BrowserListLength, FALSE);
+
+ if (Status != NERR_Success) {
+ printf("Unable to get backup list for %s", UnicodeToPrintfString(UServerName.Buffer));
+ printf(" on transport %s: %s\n", UnicodeToPrintfString(TransportName.Buffer), get_error_text(Status));
+ exit(1);
+ }
+
+ if (BrowserListLength == 0) {
+ printf("Unable to get backup list for %s", UnicodeToPrintfString(UServerName.Buffer));
+ printf(" on transport %s: %s\n",
+ UnicodeToPrintfString(TransportName.Buffer), get_error_text(Status));
+ exit(1);
+ }
+
+ UServerName.Buffer = *BrowserList;
+
+ }
+
+ printf("Remoting I_BrowserServerEnum to %s", UnicodeToPrintfString(UServerName.Buffer));
+ printf(" on transport %s with flags %lx\n", UnicodeToPrintfString(TransportName.Buffer), Flags);
+
+ do {
+
+ Status = I_BrowserServerEnum(UServerName.Buffer,
+ TransportName.Buffer,
+ NULL,
+ 101,
+ (LPBYTE *)&ServerList,
+ 0xffffffff,
+ &EntriesInList,
+ &TotalEntries,
+ Flags,
+ NULL,
+ NULL
+ );
+
+ if (Status != NERR_Success) {
+
+ printf("Unable to remote API to %s", UnicodeToPrintfString(UServerName.Buffer));
+ printf(" on transport %s: %s\n",UnicodeToPrintfString(TransportName.Buffer), get_error_text(Status));
+ if (Status != ERROR_MORE_DATA) {
+ exit(1);
+ }
+ }
+
+ printf("%ld entries returned. %ld total.\n", EntriesInList, TotalEntries);
+
+ if (!GoForever) {
+ Server = ServerList;
+
+ for (i = 0; i < EntriesInList ; i ++ ) {
+
+ DisplayServerInfo101( &Server[i], Flags==SV_TYPE_DOMAIN_ENUM );
+ printf( "\n" );
+ }
+ }
+
+ NetApiBufferFree(ServerList);
+
+ } while ( GoForever );
+
+
+
+ return;
+
+}
+
+VOID
+RpcCmp(
+ IN PCHAR Transport,
+ IN PCHAR ServerOrDomain,
+ IN PCHAR FlagsString,
+ IN BOOL GoForever
+ )
+{
+ NET_API_STATUS Status;
+ UNICODE_STRING TransportName;
+ ANSI_STRING AServerName;
+ UNICODE_STRING UServerName;
+ ULONG Flags = (FlagsString == NULL ? SV_TYPE_ALL : strtoul(FlagsString, NULL, 0));
+ PVOID RpcServerList;
+ PVOID RxServerList;
+ PSERVER_INFO_101 RpcServer;
+ PSERVER_INFO_101 RxServer;
+ ULONG RpcEntriesInList;
+ ULONG RpcTotalEntries;
+ ULONG RxEntriesInList;
+ ULONG RxTotalEntries;
+ unsigned int i;
+ unsigned int j;
+
+ qualify_transport(Transport, &TransportName) ;
+
+ RtlInitString(&AServerName, ServerOrDomain);
+
+ RtlAnsiStringToUnicodeString(&UServerName, &AServerName, TRUE);
+
+ if (UServerName.Buffer[0] != L'\\' || UServerName.Buffer[1] != L'\\') {
+ PWSTR *BrowserList;
+ ULONG BrowserListLength;
+
+ Status = GetBrowserServerList(&TransportName, UServerName.Buffer,
+ &BrowserList, &BrowserListLength, FALSE);
+
+ if (Status != NERR_Success) {
+ printf("Unable to get backup list for %s on transport %s: %lx\n", UnicodeToPrintfString(UServerName.Buffer), UnicodeToPrintfString2(TransportName.Buffer), Status);
+ exit(1);
+ }
+
+ if (BrowserListLength == 0) {
+ printf("Unable to get backup list for %s on transport %s: %lx\n", UnicodeToPrintfString(UServerName.Buffer), UnicodeToPrintfString2(TransportName.Buffer), Status);
+ exit(1);
+ }
+
+ UServerName.Buffer = *BrowserList;
+
+ }
+
+ printf("Remoting I_BrowserServerEnum to %s on transport %s with flags %lx\n", UnicodeToPrintfString(UServerName.Buffer), UnicodeToPrintfString2(TransportName.Buffer), Flags);
+
+ do {
+
+ Status = I_BrowserServerEnum(UServerName.Buffer,
+ TransportName.Buffer,
+ NULL,
+ 101,
+ (LPBYTE *)&RpcServerList,
+ 0xffffffff,
+ &RpcEntriesInList,
+ &RpcTotalEntries,
+ Flags,
+ NULL,
+ NULL
+ );
+
+ if (Status != NERR_Success) {
+ printf("Unable to remote API to %s on transport %s: %ld\n", UnicodeToPrintfString(UServerName.Buffer), UnicodeToPrintfString2(TransportName.Buffer), Status);
+
+ exit(1);
+ }
+
+ printf("%ld entries returned from RPC. %ld total.\n", RpcEntriesInList, RpcTotalEntries);
+
+ if (RpcEntriesInList != RpcTotalEntries) {
+ printf("EntriesRead != TotalEntries from remoted server enum\n");
+ }
+
+ if (RpcEntriesInList <= 20) {
+ printf("EntriesInList returned %ld from remoted server enum\n", RpcEntriesInList);
+ }
+
+
+ Status = RxNetServerEnum(UServerName.Buffer,
+ TransportName.Buffer,
+ 101,
+ (LPBYTE *)&RxServerList,
+ 0xffffffff,
+ &RxEntriesInList,
+ &RxTotalEntries,
+ Flags,
+ NULL,
+ NULL
+ );
+
+
+ if (Status != NERR_Success) {
+ printf("Unable to remote API to %s on transport %s: %ld\n", UnicodeToPrintfString(UServerName.Buffer), UnicodeToPrintfString2(TransportName.Buffer), Status);
+ exit(1);
+ }
+
+ printf("%ld entries returned from RX. %ld total.\n", RxEntriesInList, RxTotalEntries);
+
+ if (RxEntriesInList != RxTotalEntries) {
+ printf("RxEntriesRead != RxEntriesInList from remoted server enum\n");
+ }
+
+ if (RxEntriesInList <= 20) {
+ printf("RxEntriesInList returned %ld from remoted server enum\n", RxEntriesInList);
+ }
+
+ if (RxEntriesInList != RpcEntriesInList) {
+ printf("RxEntriesRead (%ld) != RpcTotalEntries (%ld) from remoted server enum\n", RxEntriesInList, RpcEntriesInList);
+ }
+
+ RxServer = RxServerList;
+ RpcServer = RpcServerList;
+
+ for (i = 0; i < RxEntriesInList ; i ++ ) {
+
+ for (j = 0; j < RpcEntriesInList ; j++) {
+
+ if (RxServer[i].sv101_name != NULL &&
+ RpcServer[j].sv101_name != NULL) {
+
+ if (!wcscmp(RxServer[i].sv101_name, RpcServer[j].sv101_name)) {
+ RxServer[i].sv101_name = NULL;
+ RpcServer[j].sv101_name = NULL;
+ break;
+ }
+ }
+ }
+ }
+
+ for (i = 0; i < RpcEntriesInList ; i++ ) {
+ if (RpcServer[i].sv101_name != NULL) {
+ printf("Rpc Server not in Rx List: %s\n", UnicodeToPrintfString(RpcServer[i].sv101_name));
+ }
+ }
+
+ for (i = 0; i < RxEntriesInList ; i++ ) {
+ if (RxServer[i].sv101_name != NULL) {
+ printf("Rx Server not in Rpc List: %s\n", UnicodeToPrintfString(RxServer[i].sv101_name));
+ }
+ }
+
+ NetApiBufferFree(RxServerList);
+ NetApiBufferFree(RpcServerList);
+
+ } while ( GoForever );
+
+ return;
+
+}
+
+CHAR * format_dlword(ULONG high, ULONG low, CHAR * buf);
+
+VOID
+revstr_add(CHAR * target, CHAR * source);
+
+VOID
+DumpStatistics(
+ IN ULONG NArgs,
+ IN PCHAR Arg1
+ )
+{
+ PBROWSER_STATISTICS Statistics = NULL;
+ NET_API_STATUS Status;
+ CHAR Buffer[256];
+ WCHAR ServerName[256];
+ LPTSTR Server = NULL;
+ BOOL ResetStatistics = FALSE;
+
+ if (NArgs == 2) {
+ Server = NULL;
+ ResetStatistics = FALSE;
+ } else if (NArgs == 3) {
+ if (*Arg1 == '\\') {
+ MultiByteToWideChar(CP_ACP, 0, Arg1, strlen(Arg1)+1, ServerName, sizeof(ServerName));
+
+ Server = ServerName;
+ ResetStatistics = FALSE;
+ } else {
+ Server = NULL;
+ ResetStatistics = TRUE;
+ }
+ } else if (*Arg1 == '\\') {
+ MultiByteToWideChar(CP_ACP, 0, Arg1, strlen(Arg1)+1, ServerName, sizeof(ServerName));
+ Server = ServerName;
+ ResetStatistics = TRUE;
+ }
+
+ if (ResetStatistics) {
+ Status = I_BrowserResetStatistics(Server);
+
+ if (Status != NERR_Success) {
+ printf("Unable to reset browser statistics: %ld\n", Status);
+ exit(1);
+ }
+ } else {
+ FILETIME LocalFileTime;
+ SYSTEMTIME LocalSystemTime;
+
+ Status = I_BrowserQueryStatistics(Server, &Statistics);
+
+ if (Status != NERR_Success) {
+ printf("Unable to query browser statistics: %ld\n", Status);
+ exit(1);
+ }
+
+ if (!FileTimeToLocalFileTime((LPFILETIME)&Statistics->StatisticsStartTime, &LocalFileTime)) {
+ printf("Unable to convert statistics start time: %ld\n", GetLastError());
+ exit(1);
+ }
+
+ if (!FileTimeToSystemTime(&LocalFileTime, &LocalSystemTime)) {
+ printf("Unable to convert statistics start time to system time: %ld\n", GetLastError());
+ exit(1);
+ }
+
+ printf("Browser statistics since %ld:%ld:%ld.%ld on %ld/%d/%d\n",
+ LocalSystemTime.wHour,
+ LocalSystemTime.wMinute,
+ LocalSystemTime.wSecond,
+ LocalSystemTime.wMilliseconds,
+ LocalSystemTime.wMonth,
+ LocalSystemTime.wDay,
+ LocalSystemTime.wYear);
+
+ printf("NumberOfServerEnumerations:\t\t\t%d\n", Statistics->NumberOfServerEnumerations);
+ printf("NumberOfDomainEnumerations:\t\t\t%d\n", Statistics->NumberOfDomainEnumerations);
+ printf("NumberOfOtherEnumerations:\t\t\t%d\n", Statistics->NumberOfOtherEnumerations);
+ printf("NumberOfMailslotWrites:\t\t\t\t%d\n", Statistics->NumberOfMailslotWrites);
+ printf("NumberOfServerAnnouncements:\t\t\t%s\n", format_dlword(Statistics->NumberOfServerAnnouncements.HighPart, Statistics->NumberOfServerAnnouncements.LowPart, Buffer));
+ printf("NumberOfDomainAnnouncements:\t\t\t%s\n", format_dlword(Statistics->NumberOfDomainAnnouncements.HighPart, Statistics->NumberOfDomainAnnouncements.LowPart, Buffer));
+ printf("NumberOfElectionPackets:\t\t\t%d\n", Statistics->NumberOfElectionPackets);
+ printf("NumberOfGetBrowserServerListRequests:\t\t%d\n", Statistics->NumberOfGetBrowserServerListRequests);
+ printf("NumberOfMissedGetBrowserServerListRequests:\t%d\n", Statistics->NumberOfMissedGetBrowserServerListRequests);
+ printf("NumberOfDroppedServerAnnouncements:\t\t%d\n", Statistics->NumberOfMissedServerAnnouncements);
+ printf("NumberOfDroppedMailslotDatagrams:\t\t%d\n", Statistics->NumberOfMissedMailslotDatagrams);
+// printf("NumberOfFailedMailslotAllocations:\t\t%d\n", Statistics->NumberOfFailedMailslotAllocations);
+ printf("NumberOfFailedMailslotReceives:\t\t\t%d\n", Statistics->NumberOfFailedMailslotReceives);
+// printf("NumberOfFailedMailslotWrites:\t\t\t%d\n", Statistics->NumberOfFailedMailslotWrites);
+// printf("NumberOfFailedMailslotOpens:\t\t\t%d\n", Statistics->NumberOfFailedMailslotOpens);
+// printf("NumberOfFailedServerAnnounceAllocations:\t%d\n", Statistics->NumberOfFailedServerAnnounceAllocations);
+ printf("NumberOfMasterAnnouncements:\t\t\t%d\n", Statistics->NumberOfDuplicateMasterAnnouncements);
+ printf("NumberOfIllegalDatagrams:\t\t\t%s\n", format_dlword(Statistics->NumberOfIllegalDatagrams.HighPart, Statistics->NumberOfIllegalDatagrams.LowPart, Buffer));
+ }
+}
+
+#define DLWBUFSIZE 22 /* buffer big enough to represent a 64-bit unsigned int
+
+
+/*
+ * format_dlword --
+ *
+ * This function takes a 64-bit number and writes its base-10 representation
+ * into a string.
+ *
+ * Much magic occurs within this function, so beware. We do a lot of string-
+ * reversing and addition-by-hand in order to get it to work.
+ *
+ * ENTRY
+ * high - high 32 bits
+ * low - low 32 bits
+ * buf - buffer to put it into
+ *
+ * RETURNS
+ * pointer to buffer if successful
+ */
+
+CHAR * format_dlword(ULONG high, ULONG low, CHAR * buf)
+{
+ CHAR addend[DLWBUFSIZE]; /* REVERSED power of two */
+ CHAR copy[DLWBUFSIZE];
+ int i = 0;
+
+ _ultoa(low, buf, 10); /* the low part is easy */
+ _strrev(buf); /* and reverse it */
+
+ /* set up addend with rep. of 2^32 */
+ _ultoa(0xFFFFFFFF, addend, 10); /* 2^32 -1 */
+ _strrev(addend); /* reversed, and will stay this way */
+ revstr_add(addend, "1"); /* and add one == 2^32 */
+
+ /* addend will contain the reverse-ASCII base-10 rep. of 2^(i+32) */
+
+ /* now, we loop through each digit of the high longword */
+ while (TRUE) {
+ /* if this bit is set, add in its base-10 rep */
+ if (high & 1)
+ revstr_add(buf,addend);
+
+ /* move on to next bit */
+ high >>= 1;
+
+ /* if no more digits in high, bag out */
+ if (!high)
+ break;
+
+ /* we increment i, and double addend */
+ i++;
+ strcpy(copy, addend);
+ revstr_add(addend,copy); /* i.e. add it to itself */
+
+ }
+
+ _strrev(buf);
+ return buf;
+}
+
+
+
+/*
+ * revstr_add --
+ *
+ * This function will add together reversed ASCII representations of
+ * base-10 numbers.
+ *
+ * Examples: "2" + "2" = "4" "9" + "9" = "81"
+ *
+ * This handles arbitrarily large numbers.
+ *
+ * ENTRY
+ *
+ * source - number to add in
+ * target - we add source to this
+ *
+ * EXIT
+ * target - contains sum of entry values of source and target
+ *
+ */
+
+VOID
+revstr_add(CHAR FAR * target, CHAR FAR * source)
+{
+ register CHAR accum;
+ register CHAR target_digit;
+ unsigned int carrybit = 0;
+ unsigned int srcstrlen;
+ unsigned int i;
+
+ srcstrlen = strlen(source);
+
+ for (i = 0; (i < srcstrlen) || carrybit; ++i) {
+
+ /* add in the source digit */
+ accum = (i < srcstrlen) ? (CHAR) (source[i] - '0') : (CHAR) 0;
+
+ /* add in the target digit, or '0' if we hit null term */
+ target_digit = target[i];
+ accum += (target_digit) ? target_digit : '0';
+
+ /* add in the carry bit */
+ accum += (CHAR) carrybit;
+
+ /* do a carry out, if necessary */
+ if (accum > '9') {
+ carrybit = 1;
+ accum -= 10;
+ }
+ else
+ carrybit = 0;
+
+ /* if we're expanding the string, must put in a new null term */
+ if (!target_digit)
+ target[i+1] = '\0';
+
+ /* and write out the digit */
+ target[i] = accum;
+ }
+
+}
+
+VOID
+TruncateBowserLog()
+{
+ LMDR_REQUEST_PACKET RequestPacket;
+ DWORD BytesReturned;
+ HANDLE BrowserHandle;
+
+ RtlZeroMemory(&RequestPacket, sizeof(RequestPacket));
+
+ OpenBrowser(&BrowserHandle);
+
+ RequestPacket.Version = LMDR_REQUEST_PACKET_VERSION_DOM;
+
+ RequestPacket.Parameters.Debug.TruncateLog = TRUE;
+
+ if (!DeviceIoControl(BrowserHandle, IOCTL_LMDR_DEBUG_CALL,
+ &RequestPacket, sizeof(RequestPacket),
+ NULL, 0, &BytesReturned, NULL)) {
+ printf("Unable to truncate browser log: %ld\n", GetLastError());
+ }
+
+ CloseHandle(BrowserHandle);
+
+}
+
+VOID
+CloseBowserLog()
+{
+ LMDR_REQUEST_PACKET RequestPacket;
+ DWORD BytesReturned;
+ HANDLE BrowserHandle;
+
+ RtlZeroMemory(&RequestPacket, sizeof(RequestPacket));
+
+ OpenBrowser(&BrowserHandle);
+
+ RequestPacket.Version = LMDR_REQUEST_PACKET_VERSION_DOM;
+
+ RequestPacket.Parameters.Debug.CloseLog = TRUE;
+
+ if (!DeviceIoControl(BrowserHandle, IOCTL_LMDR_DEBUG_CALL,
+ &RequestPacket, sizeof(RequestPacket),
+ NULL, 0, &BytesReturned, NULL)) {
+ printf("Unable to close browser log: %ld\n", GetLastError());
+ }
+
+ CloseHandle(BrowserHandle);
+
+}
+
+VOID
+OpenBowserLog(PCHAR FileName)
+{
+
+ CHAR Buffer[sizeof(LMDR_REQUEST_PACKET)+4096];
+ PLMDR_REQUEST_PACKET RequestPacket = (PLMDR_REQUEST_PACKET)Buffer;
+ DWORD BytesReturned;
+ HANDLE BrowserHandle;
+ UNICODE_STRING UString;
+ UNICODE_STRING NtString;
+ ANSI_STRING AString;
+
+ RtlZeroMemory(RequestPacket, sizeof(Buffer));
+
+ OpenBrowser(&BrowserHandle);
+
+ RtlInitString(&AString, FileName);
+
+ UString.Buffer = RequestPacket->Parameters.Debug.TraceFileName;
+ UString.MaximumLength = 4096;
+
+ RtlAnsiStringToUnicodeString(&UString, &AString, TRUE );
+
+ if (!RtlDosPathNameToNtPathName_U( UString.Buffer, &NtString, NULL, NULL )) {
+ printf( "Invalid file name: %ws\n", UString.Buffer );
+ return;
+ }
+
+ RtlCopyMemory( RequestPacket->Parameters.Debug.TraceFileName,
+ NtString.Buffer,
+ NtString.MaximumLength );
+
+ RequestPacket->Version = LMDR_REQUEST_PACKET_VERSION_DOM;
+
+ RequestPacket->Parameters.Debug.OpenLog = TRUE;
+
+ if (!DeviceIoControl(BrowserHandle, IOCTL_LMDR_DEBUG_CALL,
+ RequestPacket, sizeof(Buffer),
+ NULL, 0, &BytesReturned, NULL)) {
+ printf("Unable to open browser log: %ld\n", GetLastError());
+ }
+
+ CloseHandle(BrowserHandle);
+
+}
+
+VOID
+SetBowserDebug(PCHAR DebugBits)
+{
+ LMDR_REQUEST_PACKET RequestPacket;
+ DWORD BytesReturned;
+ HANDLE BrowserHandle;
+ char *end;
+
+ RtlZeroMemory(&RequestPacket, sizeof(RequestPacket));
+
+ OpenBrowser(&BrowserHandle);
+
+ RequestPacket.Version = LMDR_REQUEST_PACKET_VERSION_DOM;
+
+ RequestPacket.Parameters.Debug.DebugTraceBits = strtoul( DebugBits, &end, 16 );
+
+ if (!DeviceIoControl(BrowserHandle, IOCTL_LMDR_DEBUG_CALL,
+ &RequestPacket, sizeof(RequestPacket),
+ NULL, 0, &BytesReturned, NULL)) {
+ printf("Unable to truncate browser log: %ld\n", GetLastError());
+ }
+
+ CloseHandle(BrowserHandle);
+}
+
+#define NAME_MIN_LENGTH 4
+#define NAME_LENGTH (CNLEN-NAME_MIN_LENGTH)
+
+VOID
+Populate(
+ IN BOOL PopulateDomains,
+ IN PCHAR Transport,
+ IN PCHAR Domain,
+ IN PCHAR EmulatedDomain,
+ IN PCHAR NumberOfMachinesString,
+ IN PCHAR PeriodicityString OPTIONAL
+ )
+{
+
+ PSERVER_INFO_101 ServerInfo;
+ LPBYTE Buffer;
+ ULONG NumberOfMachines = strtoul(NumberOfMachinesString, NULL, 0);
+ ULONG Periodicity = (PeriodicityString == NULL ? 60000 : strtoul(PeriodicityString, NULL, 0));
+ ULONG ServerType;
+ WCHAR ServerComment[256];
+ WCHAR ComputerName[CNLEN+1];
+ CHAR ServerName[256];
+ DWORD VersionMajor;
+ DWORD VersionMinor;
+ BOOL UsedDefaultChar;
+ ULONG i;
+ BROWSE_ANNOUNCE_PACKET BrowseAnnouncement;
+ static char ServerCharacters[] = {"ABCDEFGHIJKLMNOPQRSTUVWXYZ 1234567890.-_"};
+ DWORD Seed;
+ NET_API_STATUS Status;
+
+
+ if (Periodicity == 0) {
+ Periodicity = 60000;
+ }
+
+ NetServerGetInfo(NULL, 101, &Buffer);
+
+ ServerInfo = (PSERVER_INFO_101 )Buffer;
+
+ ServerType = ServerInfo->sv101_type;
+
+ wcscpy(ServerComment, ServerInfo->sv101_comment);
+
+ wcscpy(ComputerName, ServerInfo->sv101_name);
+
+ VersionMajor = ServerInfo->sv101_version_major;
+
+ VersionMinor = ServerInfo->sv101_version_minor;
+
+ NetApiBufferFree(Buffer);
+
+ if (PopulateDomains) {
+ printf("Populating all domains on transport %s with %ld domains. Periodicity = %ld\n", Transport, NumberOfMachines, Periodicity);
+ } else {
+ printf("Populating workgroup %s on transport %s with %ld servers. Periodicity = %ld\n", Domain, Transport, NumberOfMachines, Periodicity);
+ }
+
+ Seed = time(&Seed);
+
+ for (i = 0 ; i < NumberOfMachines; i += 1) {
+ LONG NL1 = RtlRandom(&Seed) % (NAME_LENGTH-1);
+ LONG NameLength;
+ LONG NL2;
+ LONG j;
+
+ NL2 = NAME_LENGTH/2 - NL1;
+
+ NameLength = NAME_LENGTH/2 + NL2 + NAME_MIN_LENGTH;
+
+ for (j = 0; j < NameLength ; j += 1) {
+ ServerName[j] = ServerCharacters[RtlRandom(&Seed) % (sizeof(ServerCharacters) - 1)];
+ }
+
+ ServerName[j] = '\0';
+
+ //
+ // Build an announcement packet.
+ //
+
+ if (PopulateDomains) {
+ BrowseAnnouncement.BrowseType = WkGroupAnnouncement;
+ } else {
+ BrowseAnnouncement.BrowseType = HostAnnouncement;
+ }
+
+ BrowseAnnouncement.BrowseAnnouncement.UpdateCount = 0;
+
+ BrowseAnnouncement.BrowseAnnouncement.Periodicity = Periodicity;
+
+ strcpy(BrowseAnnouncement.BrowseAnnouncement.ServerName, ServerName);
+
+ BrowseAnnouncement.BrowseAnnouncement.VersionMajor = (UCHAR)VersionMajor;
+ BrowseAnnouncement.BrowseAnnouncement.VersionMinor = (UCHAR)VersionMinor;
+ BrowseAnnouncement.BrowseAnnouncement.Type = (ServerType & ~(SV_TYPE_BACKUP_BROWSER | SV_TYPE_MASTER_BROWSER));
+
+ if (PopulateDomains) {
+ WideCharToMultiByte(CP_OEMCP, 0,
+ ComputerName,
+ wcslen(ComputerName)+1,
+ BrowseAnnouncement.BrowseAnnouncement.Comment,
+ CNLEN+1,
+ "?",
+ &UsedDefaultChar
+ );
+ } else {
+
+ WideCharToMultiByte(CP_OEMCP, 0,
+ ServerComment,
+ wcslen(ServerComment)+1,
+ BrowseAnnouncement.BrowseAnnouncement.Comment,
+ LM20_MAXCOMMENTSZ+1,
+ "?",
+ &UsedDefaultChar
+ );
+ }
+
+ BrowseAnnouncement.BrowseAnnouncement.CommentPointer = NULL;
+
+ //
+ // Send the request
+ //
+
+ Status = SendDatagramA( Transport,
+ EmulatedDomain,
+ Domain,
+ (PopulateDomains ? DomainAnnouncement : MasterBrowser),
+ &BrowseAnnouncement,
+ sizeof(BrowseAnnouncement));
+
+ if (Status != NERR_Success) {
+ printf("Unable to send datagram: %ld\n", Status);
+ }
+
+ Sleep(50);
+
+ }
+
+
+ return;
+}
+
+NET_API_STATUS
+GetBuildNumber(
+ LPWSTR Server,
+ LPWSTR BuildNumber
+ );
+
+NET_API_STATUS
+GetBrowserTransportList(
+ OUT PLMDR_TRANSPORT_LIST *TransportList
+ );
+
+
+NET_API_STATUS
+GetStatusForTransport(
+ IN BOOL Verbose,
+ IN PUNICODE_STRING Transport,
+ IN PUNICODE_STRING Domain
+ );
+
+
+ VOID
+BrowserStatus(
+ IN BOOL Verbose,
+ IN PCHAR Domain OPTIONAL
+ )
+{
+ NET_API_STATUS Status;
+ UNICODE_STRING DomainName;
+ PVOID Buffer;
+ PWKSTA_INFO_101 WkstaInfo;
+ PLMDR_TRANSPORT_LIST TransportList, TransportEntry;
+
+ if (Domain == NULL) {
+ PWCHAR DomainBuffer = NULL;
+ UNICODE_STRING TDomainName;
+ Status = NetWkstaGetInfo(NULL, 101, (LPBYTE *)&Buffer);
+
+ if (Status != NERR_Success) {
+ printf("Unable to retrieve workstation information: %s\n", get_error_text(Status));
+ exit(Status);
+ }
+ WkstaInfo = (PWKSTA_INFO_101 )Buffer;
+
+ DomainBuffer = malloc((wcslen(WkstaInfo->wki101_langroup)+1)*sizeof(WCHAR));
+
+ DomainName.Buffer = DomainBuffer;
+
+ DomainName.MaximumLength = (wcslen(WkstaInfo->wki101_langroup)+1)*sizeof(WCHAR);
+
+ RtlInitUnicodeString(&TDomainName, WkstaInfo->wki101_langroup);
+
+ RtlCopyUnicodeString(&DomainName, &TDomainName);
+
+ NetApiBufferFree(Buffer);
+ } else {
+ ANSI_STRING AString;
+
+ RtlInitAnsiString(&AString, Domain);
+
+ RtlAnsiStringToUnicodeString(&DomainName, &AString, TRUE);
+ }
+
+ //
+ // We now know the domain to query. Iterate through the transports and
+ // get status for each of them.
+ //
+
+ Status = GetBrowserTransportList(&TransportList);
+
+ if (Status != NERR_Success) {
+ printf("Unable to retrieve transport list: %s\n", get_error_text(Status));
+ exit(Status);
+ }
+
+ TransportEntry = TransportList;
+
+ while (TransportEntry != NULL) {
+ UNICODE_STRING TransportName;
+
+ TransportName.Buffer = TransportEntry->TransportName;
+ TransportName.Length = (USHORT)TransportEntry->TransportNameLength;
+ TransportName.MaximumLength = (USHORT)TransportEntry->TransportNameLength;
+
+ Status = GetStatusForTransport(Verbose, &TransportName, &DomainName);
+
+ if (TransportEntry->NextEntryOffset == 0) {
+ TransportEntry = NULL;
+ } else {
+ TransportEntry = (PLMDR_TRANSPORT_LIST)((PCHAR)TransportEntry+TransportEntry->NextEntryOffset);
+ }
+ }
+
+
+ NetApiBufferFree(TransportList);
+
+ exit(0);
+}
+
+NET_API_STATUS
+GetBrowserTransportList(
+ OUT PLMDR_TRANSPORT_LIST *TransportList
+ )
+
+/*++
+
+Routine Description:
+
+ This routine returns the list of transports bound into the browser.
+
+Arguments:
+
+ OUT PLMDR_TRANSPORT_LIST *TransportList - Transport list to return.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+
+{
+
+ NET_API_STATUS Status;
+ HANDLE BrowserHandle;
+ LMDR_REQUEST_PACKET RequestPacket;
+
+ Status = OpenBrowser(&BrowserHandle);
+
+ if (Status != NERR_Success) {
+ return Status;
+ }
+
+ RequestPacket.Version = LMDR_REQUEST_PACKET_VERSION_DOM;
+
+ RequestPacket.Type = EnumerateXports;
+
+ RtlInitUnicodeString(&RequestPacket.TransportName, NULL);
+ RtlInitUnicodeString(&RequestPacket.EmulatedDomainName, NULL);
+
+ Status = DeviceControlGetInfo(
+ BrowserHandle,
+ IOCTL_LMDR_ENUMERATE_TRANSPORTS,
+ &RequestPacket,
+ sizeof(RequestPacket),
+ (PVOID *)TransportList,
+ 0xffffffff,
+ 4096,
+ NULL);
+
+ NtClose(BrowserHandle);
+
+ return Status;
+}
+
+NET_API_STATUS
+GetStatusForTransport(
+ IN BOOL Verbose,
+ IN PUNICODE_STRING Transport,
+ IN PUNICODE_STRING Domain
+ )
+{
+ WCHAR MasterName[256];
+ WCHAR MasterServerName[256+2];
+ NET_API_STATUS Status;
+ PVOID Buffer;
+ PSERVER_INFO_101 ServerInfo;
+ DWORD EntriesInList;
+ DWORD TotalEntries;
+ DWORD BrowserListLength;
+ PWSTR *BrowserList;
+ DWORD i;
+ DWORD NumberNTASMachines = 0;
+ DWORD NumberOS2DCs = 0;
+ DWORD NumberWfWMachines = 0;
+ DWORD NumberOfNTMachines = 0;
+ DWORD NumberWfWBrowsers = 0;
+ DWORD NumberOfOs2Machines = 0;
+ DWORD NumberOfBrowsers = 0;
+ DWORD NumberOfBackupBrowsers = 0;
+ DWORD NumberOfMasterBrowsers = 0;
+ WCHAR BuildNumber[512];
+
+ printf("\n\nStatus for domain %s on transport %s\n", UnicodeToPrintfString(Domain->Buffer), UnicodeToPrintfString2(Transport->Buffer));
+
+ Status = GetBrowserServerList(Transport, Domain->Buffer, &BrowserList, &BrowserListLength, TRUE);
+
+ if (Status == NERR_Success) {
+
+ printf(" Browsing is active on domain.\n");
+
+ } else {
+ printf(" Browsing is NOT active on domain.\n", Status);
+
+ Status = GetNetBiosMasterName(
+ Transport->Buffer,
+ Domain->Buffer,
+ MasterName,
+ NULL);
+
+ if (Status == NERR_Success) {
+
+ wcscpy(MasterServerName, L"\\\\");
+ wcscat(MasterServerName, MasterName);
+
+ printf(" Master browser name is held by: %s\n", UnicodeToPrintfString(MasterName));
+
+ Status = GetBuildNumber(MasterServerName, BuildNumber);
+
+ if (Status == NERR_Success) {
+ printf(" Master browser is running build %s\n", UnicodeToPrintfString(BuildNumber));
+ } else {
+ PSERVER_INFO_101 pSV101;
+
+ printf(" Unable to determine build of browser master: %d\n", Status);
+
+ Status = NetServerGetInfo(MasterServerName, 101, (LPBYTE *)&pSV101);
+
+ if (Status != NERR_Success) {
+ printf(" Unable to determine server information for browser master: %d\n", Status);
+
+ return Status;
+ }
+
+ printf(" %-16.16s. Version:%2.2d.%2.2d Flags: %lx ", UnicodeToPrintfString(MasterServerName), pSV101->sv101_version_major, pSV101->sv101_version_minor, pSV101->sv101_type);
+
+ if (pSV101->sv101_type & SV_TYPE_WFW) {
+ printf("WFW ");
+ }
+
+ if (pSV101->sv101_type & SV_TYPE_NT) {
+ printf("NT ");
+ }
+
+ if (pSV101->sv101_type & SV_TYPE_POTENTIAL_BROWSER) {
+ printf("POTENTIAL ");
+ }
+
+ if (pSV101->sv101_type & SV_TYPE_BACKUP_BROWSER) {
+ printf("BACKUP ");
+ }
+
+ if (pSV101->sv101_type & SV_TYPE_MASTER_BROWSER) {
+ printf("MASTER ");
+ }
+
+ if (pSV101->sv101_type & SV_TYPE_DOMAIN_CTRL) {
+ printf("CONTROLLER ");
+ }
+
+ if (pSV101->sv101_type & SV_TYPE_DOMAIN_BAKCTRL) {
+ printf("BACKUP CONTROLLER ");
+ }
+
+ if (pSV101->sv101_type & SV_TYPE_SERVER_NT) {
+ printf("SERVER ");
+ }
+
+
+ }
+
+ } else {
+ printf(" Master name cannot be determined from GetAdapterStatus.\n");
+ }
+ return Status;
+ }
+
+ Status = GetNetBiosMasterName(
+ Transport->Buffer,
+ Domain->Buffer,
+ MasterName,
+ NULL);
+
+ if (Status == NERR_Success) {
+
+ wcscpy(MasterServerName, L"\\\\");
+ wcscat(MasterServerName, MasterName);
+
+ printf(" Master browser name is: %s\n", UnicodeToPrintfString(MasterName));
+
+
+ } else {
+ printf(" Master name cannot be determined from GetAdapterStatus. Using %s\n", UnicodeToPrintfString(BrowserList[0]));
+
+ wcscpy(MasterServerName, BrowserList[0]);
+ wcscpy(MasterName, (BrowserList[0])+2);
+ }
+
+ //
+ // Print the build number or whatever else you can find out about the master
+ //
+
+ Status = GetBuildNumber(MasterServerName, BuildNumber);
+
+ if (Status == NERR_Success) {
+ printf(" Master browser is running build %s\n", UnicodeToPrintfString(BuildNumber));
+ } else {
+ PSERVER_INFO_101 pSV101;
+
+ printf(" Unable to determine build of browser master: %d\n", Status);
+
+ Status = NetServerGetInfo(MasterServerName, 101, (LPBYTE *)&pSV101);
+
+ if (Status != NERR_Success) {
+ printf(" Unable to determine server information for browser master: %d\n", Status);
+ }
+
+ if (Status == NERR_Success) {
+
+ printf(" \\\\%-16.16s. Version:%2.2d.%2.2d Flags: %lx ", UnicodeToPrintfString(MasterServerName), pSV101->sv101_version_major, pSV101->sv101_version_minor, pSV101->sv101_type);
+
+ if (pSV101->sv101_type & SV_TYPE_WFW) {
+ printf("WFW ");
+ }
+
+ if (pSV101->sv101_type & SV_TYPE_NT) {
+ printf("NT ");
+ }
+
+ if (pSV101->sv101_type & SV_TYPE_POTENTIAL_BROWSER) {
+ printf("POTENTIAL ");
+ }
+
+ if (pSV101->sv101_type & SV_TYPE_BACKUP_BROWSER) {
+ printf("BACKUP ");
+ }
+
+ if (pSV101->sv101_type & SV_TYPE_MASTER_BROWSER) {
+ printf("MASTER ");
+ }
+
+ if (pSV101->sv101_type & SV_TYPE_DOMAIN_CTRL) {
+ printf("CONTROLLER ");
+ }
+
+ if (pSV101->sv101_type & SV_TYPE_DOMAIN_BAKCTRL) {
+ printf("BACKUP CONTROLLER ");
+ }
+
+ if (pSV101->sv101_type & SV_TYPE_SERVER_NT) {
+ printf("SERVER ");
+ }
+
+ printf("\n");
+ }
+ }
+
+ printf(" %ld backup servers retrieved from master %s\n", BrowserListLength, UnicodeToPrintfString(MasterName));
+
+ for (i = 0; i < BrowserListLength ; i++ ) {
+ printf(" %s\n", UnicodeToPrintfString(BrowserList[i]));
+ }
+
+
+ Status = RxNetServerEnum(MasterServerName,
+ Transport->Buffer,
+ 101,
+ (LPBYTE *)&Buffer,
+ 0xffffffff, // PreferedMaxLength
+ &EntriesInList,
+ &TotalEntries,
+ SV_TYPE_ALL,
+// Domain->Buffer,
+ NULL,
+ NULL
+ );
+ if (Status != NERR_Success) {
+ printf(" Unable to retrieve server list from %s: %ld\n", UnicodeToPrintfString(MasterName), Status);
+ return Status;
+ } else {
+
+ printf(" There are %ld servers in domain %s on transport %s\n", EntriesInList, UnicodeToPrintfString(Domain->Buffer), UnicodeToPrintfString2(Transport->Buffer));
+
+ if (Verbose) {
+ if (EntriesInList != 0) {
+ ServerInfo = (PSERVER_INFO_101)Buffer;
+
+ for (i = 0 ; i < EntriesInList ; i += 1) {
+ if (ServerInfo->sv101_type & (SV_TYPE_DOMAIN_BAKCTRL | SV_TYPE_DOMAIN_CTRL)) {
+
+ if (ServerInfo->sv101_type & SV_TYPE_NT) {
+ NumberNTASMachines += 1;
+ } else {
+ NumberOS2DCs += 1;
+ }
+
+ }
+
+ if (ServerInfo->sv101_type & SV_TYPE_WFW) {
+ NumberWfWMachines += 1;
+
+ if (ServerInfo->sv101_type & (SV_TYPE_BACKUP_BROWSER | SV_TYPE_POTENTIAL_BROWSER | SV_TYPE_MASTER_BROWSER)) {
+ NumberWfWBrowsers += 1;
+ }
+ } else if (ServerInfo->sv101_type & SV_TYPE_NT) {
+ NumberOfNTMachines += 1;
+ } else {
+ NumberOfOs2Machines += 1;
+ }
+
+ if (ServerInfo->sv101_type & (SV_TYPE_BACKUP_BROWSER | SV_TYPE_POTENTIAL_BROWSER | SV_TYPE_MASTER_BROWSER)) {
+ NumberOfBrowsers += 1;
+
+ if (ServerInfo->sv101_type & SV_TYPE_BACKUP_BROWSER) {
+ NumberOfBackupBrowsers += 1;
+ }
+
+ if (ServerInfo->sv101_type & SV_TYPE_MASTER_BROWSER) {
+ NumberOfMasterBrowsers += 1;
+ }
+ }
+
+ ServerInfo += 1;
+ }
+
+ printf(" Number of NT Advanced Servers:\t\t\t%ld\n", NumberNTASMachines);
+ printf(" Number of OS/2 Domain controllers:\t\t%ld\n", NumberOS2DCs);
+ printf(" Number of Windows For Workgroups machines:\t%ld\n", NumberWfWMachines);
+ printf(" Number of Os/2 machines:\t\t\t%ld\n", NumberOfOs2Machines);
+ printf(" Number of NT machines:\t\t\t\t%ld\n", NumberOfNTMachines);
+ printf("\n");
+ printf(" Number of active WfW browsers:\t\t\t%ld\n", NumberWfWBrowsers);
+ printf(" Number of browsers:\t\t\t\t%ld\n", NumberOfBrowsers);
+ printf(" Number of backup browsers:\t\t\t%ld\n", NumberOfBackupBrowsers);
+ printf(" Number of master browsers:\t\t\t%ld\n", NumberOfMasterBrowsers);
+
+ }
+ }
+
+ }
+ Status = RxNetServerEnum(MasterServerName,
+ Transport->Buffer,
+ 101,
+ (LPBYTE *)&Buffer,
+ 0xffffffff, // PreferedMaxLength
+ &EntriesInList,
+ &TotalEntries,
+ SV_TYPE_DOMAIN_ENUM,
+// Domain->Buffer,
+ NULL,
+ NULL
+ );
+ if ( Status == ERROR_MORE_DATA ) {
+ printf(" Only %ld out of %ld domains could be returned\n", EntriesInList, TotalEntries );
+ } else if (Status != NERR_Success) {
+ printf(" Unable to retrieve server list from %s: %ld\n", UnicodeToPrintfString(MasterName), Status);
+ return Status;
+ }
+ printf(" There are %ld domains in domain %s on transport %s\n", EntriesInList, UnicodeToPrintfString(Domain->Buffer), UnicodeToPrintfString2(Transport->Buffer));
+
+ return NERR_Success;
+}
+
+#define BUILD_NUMBER_KEY L"SOFTWARE\\MICROSOFT\\WINDOWS NT\\CURRENTVERSION"
+#define BUILD_NUMBER_BUFFER_LENGTH 80
+
+NET_API_STATUS
+GetBuildNumber(
+ LPWSTR Server,
+ LPWSTR BuildNumber
+ )
+{
+ HKEY RegKey;
+ HKEY RegKeyBuildNumber;
+ DWORD WinStatus;
+ DWORD BuildNumberLength;
+ DWORD KeyType;
+
+ WinStatus = RegConnectRegistry(Server, HKEY_LOCAL_MACHINE,
+ &RegKey);
+ if (WinStatus == RPC_S_SERVER_UNAVAILABLE) {
+// printf("%15ws no longer accessable", Server+2);
+ return(WinStatus);
+ }
+ else if (WinStatus != ERROR_SUCCESS) {
+ printf("Could not connect to registry, error = %d", WinStatus);
+ return(WinStatus);
+ }
+
+ WinStatus = RegOpenKeyEx(RegKey, BUILD_NUMBER_KEY,0, KEY_READ,
+ & RegKeyBuildNumber);
+ if (WinStatus != ERROR_SUCCESS) {
+ printf("Could not open key in registry, error = %d", WinStatus);
+ return(WinStatus);
+ }
+
+ BuildNumberLength = BUILD_NUMBER_BUFFER_LENGTH * sizeof(WCHAR);
+
+ WinStatus = RegQueryValueEx(RegKeyBuildNumber, L"CurrentBuildNumber",
+ (LPDWORD) NULL, & KeyType, (LPBYTE) BuildNumber, & BuildNumberLength);
+
+ if (WinStatus != ERROR_SUCCESS) {
+
+ WinStatus = RegQueryValueEx(RegKeyBuildNumber, L"CurrentBuild",
+ (LPDWORD) NULL, & KeyType, (LPBYTE) BuildNumber, & BuildNumberLength);
+ if (WinStatus != ERROR_SUCCESS) {
+ RegCloseKey(RegKeyBuildNumber);
+ RegCloseKey(RegKey);
+ return WinStatus;
+ }
+ }
+
+ WinStatus = RegCloseKey(RegKeyBuildNumber);
+
+ if (WinStatus != ERROR_SUCCESS) {
+ printf("Could not close registry key, error = %d", WinStatus);
+ }
+
+ WinStatus = RegCloseKey(RegKey);
+
+ if (WinStatus != ERROR_SUCCESS) {
+ printf("Could not close registry connection, error = %d", WinStatus);
+ }
+
+ return(WinStatus);
+}
+ NET_API_STATUS
+GetNetBiosPdcName(
+ IN LPWSTR NetworkName,
+ IN LPWSTR PrimaryDomain,
+ OUT LPWSTR MasterName
+ );
+
+VOID
+GetPdc(
+ IN PCHAR Transport,
+ IN PCHAR Domain
+ )
+{
+ NET_API_STATUS Status;
+ UNICODE_STRING TransportName;
+ ANSI_STRING AString;
+ WCHAR MasterName[256];
+ UNICODE_STRING DomainName;
+
+ qualify_transport(Transport, &TransportName) ;
+
+ RtlInitString(&AString, Domain);
+ RtlAnsiStringToUnicodeString(&DomainName, &AString, TRUE);
+
+ Status = GetNetBiosPdcName(TransportName.Buffer, DomainName.Buffer, MasterName);
+
+ if (Status != NERR_Success) {
+ printf("Unable to get PDC: %s\n", get_error_text(Status));
+ exit(1);
+ }
+
+ printf("PDC: %s\n", UnicodeToPrintfString(MasterName));
+
+}
+ NET_API_STATUS
+GetNetBiosPdcName(
+ IN LPWSTR NetworkName,
+ IN LPWSTR PrimaryDomain,
+ OUT LPWSTR MasterName
+ )
+{
+ CCHAR LanaNum;
+ NCB AStatNcb;
+ struct {
+ ADAPTER_STATUS AdapterInfo;
+ NAME_BUFFER Names[32];
+ } AdapterStatus;
+ WORD i;
+ CHAR remoteName[CNLEN+1];
+ NET_API_STATUS Status;
+ BOOL UsedDefaultChar;
+
+ Status = BrGetLanaNumFromNetworkName(NetworkName, &LanaNum);
+
+ if (Status != NERR_Success) {
+ return Status;
+ }
+
+ ClearNcb(&AStatNcb)
+
+ AStatNcb.ncb_command = NCBRESET;
+ AStatNcb.ncb_lsn = 0; // Request resources
+ AStatNcb.ncb_lana_num = LanaNum;
+ AStatNcb.ncb_callname[0] = 0; // 16 sessions
+ AStatNcb.ncb_callname[1] = 0; // 16 commands
+ AStatNcb.ncb_callname[2] = 0; // 8 names
+ AStatNcb.ncb_callname[3] = 0; // Don't want the reserved address
+ Netbios( &AStatNcb );
+
+ ClearNcb( &AStatNcb );
+
+ if (WideCharToMultiByte( CP_OEMCP, 0,
+ PrimaryDomain,
+ -1,
+ remoteName,
+ sizeof(remoteName),
+ "?",
+ &UsedDefaultChar) == 0) {
+ return GetLastError();
+ }
+
+ //
+ // Uppercase the remote name.
+ //
+
+ _strupr(remoteName);
+
+ AStatNcb.ncb_command = NCBASTAT;
+
+ RtlCopyMemory( AStatNcb.ncb_callname, remoteName, strlen(remoteName));
+
+ AStatNcb.ncb_callname[15] = PRIMARY_CONTROLLER_SIGNATURE;
+
+ AStatNcb.ncb_lana_num = LanaNum;
+ AStatNcb.ncb_length = sizeof( AdapterStatus );
+ AStatNcb.ncb_buffer = (CHAR *)&AdapterStatus;
+ Netbios( &AStatNcb );
+
+ if ( AStatNcb.ncb_retcode == NRC_GOODRET ) {
+ for ( i=0 ; i < AdapterStatus.AdapterInfo.name_count ; i++ ) {
+ if (AdapterStatus.Names[i].name[NCBNAMSZ-1] == SERVER_SIGNATURE) {
+// LPWSTR SpacePointer;
+ DWORD j;
+
+ if (MultiByteToWideChar(CP_OEMCP,
+ 0,
+ AdapterStatus.Names[i].name,
+ CNLEN,
+ MasterName,
+ CNLEN) == 0) {
+ return(GetLastError());
+ }
+
+ for (j = CNLEN - 1; j ; j -= 1) {
+ if (MasterName[j] != L' ') {
+ MasterName[j+1] = UNICODE_NULL;
+ break;
+ }
+ }
+
+ return NERR_Success;
+ }
+ }
+ } else {
+ return AStatNcb.ncb_retcode;
+ }
+}
+
+VOID
+DisplayServerInfo101(
+ PSERVER_INFO_101 Server,
+ BOOL DomainEnumeration
+ )
+{
+ DWORD MajorVersion, MinorVersion ;
+ DWORD CharactersPrinted = 0;
+
+ if ( DomainEnumeration ) {
+ printf( "%-16.16ws", Server->sv101_name);
+ CharactersPrinted += 16;
+ } else {
+ printf( "\\\\%-16.16ws", Server->sv101_name);
+ CharactersPrinted += 18;
+ }
+
+ printf(" %s",
+ (Server->sv101_platform_id == PLATFORM_ID_DOS ? "DOS" :
+ (Server->sv101_platform_id == PLATFORM_ID_OS2 ?
+ ((Server->sv101_type & SV_TYPE_WINDOWS) ? "W95" :
+ ((Server->sv101_type & SV_TYPE_WFW) ? "WFW": "OS2" )) :
+ (Server->sv101_platform_id == PLATFORM_ID_NT ? "NT " :
+ "Unk") ) ) );
+ CharactersPrinted += 5;
+
+ MajorVersion = Server->sv101_version_major ;
+ MinorVersion = Server->sv101_version_minor ;
+ if ((MajorVersion == 1) && (MinorVersion >= 50)) {
+ printf(" %2.2d.%2.2d", MajorVersion+2, MinorVersion-40);
+ } else {
+ printf(" %2.2d.%2.2d", MajorVersion, MinorVersion);
+ }
+ CharactersPrinted += 5;
+
+ CharactersPrinted += display_sv_bits(Server->sv101_type);
+
+ if ( Server->sv101_comment != NULL && wcslen(Server->sv101_comment) > 0 ) {
+ printf( " " );
+ CharactersPrinted ++;
+
+ while ( CharactersPrinted < 48 ) {
+ printf( " " );
+ CharactersPrinted ++;
+ }
+ while ( CharactersPrinted % 4 != 0 ) {
+ printf( " " );
+ CharactersPrinted ++;
+ }
+
+ printf( "%ws", Server->sv101_comment );
+ }
+
+}
+
+//
+// display server bits as defined in BitsToStringTable
+//
+// Returns the number of characters printed.
+
+DWORD
+display_sv_bits(DWORD dwBits)
+{
+ BIT_NAME *lpEntry = BitToStringTable ;
+ BOOL fFirst = TRUE ;
+ DWORD CharactersPrinted = 0;
+
+ printf(" (") ;
+ CharactersPrinted += 2;
+ while (1)
+ {
+ if (lpEntry->dwValue & dwBits)
+ {
+ if (lpEntry != BitToStringTable && !fFirst) {
+ printf(",") ;
+ CharactersPrinted += 1;
+ }
+
+ dwBits &= ~lpEntry->dwValue;
+ printf("%s",lpEntry->lpString) ;
+ CharactersPrinted += strlen(lpEntry->lpString);
+ fFirst = FALSE ;
+ }
+ lpEntry++ ;
+ if ( !(lpEntry->dwValue) ) {
+ dwBits &= ~(SV_TYPE_DOMAIN_ENUM|SV_TYPE_LOCAL_LIST_ONLY);
+ if ( dwBits != 0 ) {
+ if ( !fFirst ) {
+ printf(",") ;
+ CharactersPrinted += 1;
+ }
+ printf( "%8.8X", dwBits );
+ CharactersPrinted += 8;
+ }
+ printf(")") ;
+ CharactersPrinted += 1;
+ break ;
+ }
+ }
+ return CharactersPrinted;
+}
+
+//
+// map an error number to its error message string. note, uses static,
+// not reentrant.
+//
+CHAR *
+get_error_text(DWORD dwErr)
+{
+ static CHAR text[512] ;
+ WORD err ;
+ WORD msglen ;
+
+ memset(text,0, sizeof(text));
+
+ //
+ // get error message
+ //
+ err = DosGetMessage(NULL,
+ 0,
+ text,
+ sizeof(text),
+ (WORD)dwErr,
+ (dwErr<NERR_BASE)||(dwErr>MAX_LANMAN_MESSAGE_ID) ?
+ TEXT("BASE"):TEXT("NETMSG"),
+ &msglen) ;
+
+ if (err != NERR_Success)
+ {
+ // use number instead. if looks like NTSTATUS then use hex.
+ sprintf(text, (dwErr & 0xC0000000)?"(%lx)":"(%ld)", dwErr) ;
+ }
+
+ return text ;
+}
+
+BOOL
+look_for_help(int argc, char **argv)
+{
+ int i ;
+
+ for (i = 2; i < argc; i++)
+ {
+ if (_stricmp(argv[i],"/help") == 0)
+ return TRUE ;
+ if (_stricmp(argv[i],"-help") == 0)
+ return TRUE ;
+ if (strcmp(argv[i],"/?") == 0)
+ return TRUE ;
+ if (strcmp(argv[i],"-?") == 0)
+ return TRUE ;
+ }
+ return FALSE ;
+}
+
+CHAR
+PrintfBuffer[256];
+
+PCHAR
+UnicodeToPrintfString(
+ PWCHAR WideChar
+ )
+{
+ UNICODE_STRING UString;
+ ANSI_STRING AString;
+ AString.Buffer = PrintfBuffer;
+ AString.MaximumLength = sizeof(PrintfBuffer);
+ RtlInitUnicodeString(&UString, WideChar);
+
+ RtlUnicodeStringToOemString(&AString, &UString, FALSE);
+
+ return PrintfBuffer;
+}
+
+
+CHAR
+PrintfBuffer2[256];
+
+PCHAR
+UnicodeToPrintfString2(
+ PWCHAR WideChar
+ )
+{
+ UNICODE_STRING UString;
+ ANSI_STRING AString;
+
+ AString.Buffer = PrintfBuffer2;
+
+ AString.MaximumLength = sizeof(PrintfBuffer2);
+
+ RtlInitUnicodeString(&UString, WideChar);
+
+ RtlUnicodeStringToOemString(&AString, &UString, FALSE);
+
+ return PrintfBuffer2;
+}
+
+ VOID
+GetWinsServer(
+ IN PCHAR Transport
+ )
+{
+ NET_API_STATUS Status;
+ UNICODE_STRING TransportName;
+ LPTSTR PrimaryWinsServerAddress;
+ LPTSTR SecondaryWinsServerAddress;
+
+ qualify_transport(Transport, &TransportName) ;
+
+ Status = BrGetWinsServerName(&TransportName, &PrimaryWinsServerAddress, &SecondaryWinsServerAddress);
+
+ if (Status != NERR_Success) {
+ printf("Unable to query WINS server address: %ld\n", Status);
+ exit(1);
+ }
+
+ printf("Primary Wins server address: %ws\n", PrimaryWinsServerAddress);
+ printf("Secondary Wins server address: %ws\n", SecondaryWinsServerAddress);
+
+ exit(0);
+}
+
+ VOID
+GetDomainList(
+ IN PCHAR IpAddress
+ )
+{
+ NET_API_STATUS Status;
+ PSERVER_INFO_101 WinsServerList;
+ DWORD EntriesInList;
+ DWORD TotalEntriesInList;
+ UNICODE_STRING IpAddressString;
+ ANSI_STRING IpAddressAString;
+ DWORD i;
+
+ RtlInitString(&IpAddressAString, IpAddress);
+
+ RtlAnsiStringToUnicodeString(&IpAddressString, &IpAddressAString, TRUE);
+
+ Status = BrQuerySpecificWinsServer(IpAddressString.Buffer, &WinsServerList, &EntriesInList, &TotalEntriesInList);
+
+ if (Status != NERR_Success) {
+ printf("Unable to query domain list from WINS server: %ld\n", Status);
+ exit(1);
+ }
+
+ PrepareServerListForMerge( WinsServerList, 101, EntriesInList );
+
+ for (i = 0; i < EntriesInList ; i ++ ) {
+ printf("%-16.16s\n", UnicodeToPrintfString(WinsServerList[i].sv101_name));
+ }
+
+ exit(0);
+}
+
+NET_API_STATUS
+BrMapStatus(
+ IN NTSTATUS Status
+ )
+{
+ return RtlNtStatusToDosError(Status);
+}
+
+
+VOID
+EnumEmulatedDomains(
+ )
+/*++
+
+Routine Description:
+
+ Enumerate emulated domains.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NET_API_STATUS NetStatus;
+ LPWSTR EmulatedDomainName;
+ LPWSTR EmulatedComputerName;
+ DWORD RoleBits;
+
+ PBROWSER_EMULATED_DOMAIN Domains;
+ DWORD EntriesRead;
+ DWORD i;
+
+ //
+ // Enumerate the emulated domains.
+ //
+
+ NetStatus = I_BrowserQueryEmulatedDomains(
+ NULL,
+ &Domains,
+ &EntriesRead );
+
+ if ( NetStatus != NERR_Success ) {
+ printf( "Can't enumerate EmulatedDomains: %s\n", get_error_text(NetStatus) );
+ return;
+ }
+
+ if ( EntriesRead == 0 ) {
+ printf( "There are no emulated domains\n" );
+ return;
+ }
+
+ //
+ // Print the enumerated information
+ //
+
+ for ( i=0 ; i<EntriesRead; i++ ) {
+ printf( "%-16.16ws %3.3s \\\\%-16.16ws\n",
+ Domains[i].DomainName,
+ (Domains[i].Role & BROWSER_ROLE_PDC) ? "PDC" : "BDC",
+ Domains[i].EmulatedServerName );
+ }
+
+
+}
+
+
+VOID
+SetEmulatedDomain(
+ IN PCHAR EmulatedDomain,
+ IN PCHAR Role,
+ IN PCHAR EmulatedComputer
+ )
+/*++
+
+Routine Description:
+
+ Create and/or set role on emulated domain
+
+Arguments:
+
+ EmulatedDomain - Emulated Domain name.
+
+ Role - Role this machine plays in the domain.
+
+ EmulatedComputerName - Name of this computer in the emulated domain.
+ (Need only be specified when the domain is being created.)
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+
+{
+ NET_API_STATUS NetStatus;
+ LPWSTR EmulatedDomainName;
+ LPWSTR EmulatedComputerName;
+ DWORD RoleBits;
+
+ //
+ // Comvert strings to unicode.
+ //
+
+ EmulatedDomainName = NetpAllocWStrFromStr( EmulatedDomain );
+ if ( EmulatedComputer != NULL ) {
+ EmulatedComputerName = NetpAllocWStrFromStr( EmulatedComputer );
+ } else {
+ EmulatedComputerName = NULL;
+ }
+
+ //
+ // Convert Role to binary
+ //
+
+ if ( _stricmp( Role, "PDC") == 0 ) {
+ RoleBits = BROWSER_ROLE_PDC;
+ } else if ( _stricmp( Role, "BDC") == 0 ) {
+ RoleBits = BROWSER_ROLE_BDC;
+ } else if ( _strnicmp( Role, "DELETE", 3) == 0 ) {
+ RoleBits = 0;
+ } else {
+ printf( "Invalid Role: %s\n\n", Role );
+ CommandUsage(BROWSER_DEBUG_SET_EMULATEDDOMAIN);
+ return;
+ }
+
+ NetStatus = I_BrowserSetNetlogonState(
+ NULL,
+ EmulatedDomainName,
+ EmulatedComputerName,
+ RoleBits );
+
+ if ( NetStatus != NERR_Success ) {
+ printf( "Can't set domain role: %s\n", get_error_text(NetStatus));
+ }
+
+}
diff --git a/private/net/svcdlls/browser2/client/browdeb.rc b/private/net/svcdlls/browser2/client/browdeb.rc
new file mode 100644
index 000000000..57657bae7
--- /dev/null
+++ b/private/net/svcdlls/browser2/client/browdeb.rc
@@ -0,0 +1,12 @@
+#include <windows.h>
+#include <ntverp.h>
+
+#define VER_FILETYPE VFT_APP
+#define VER_FILESUBTYPE VFT2_UNKNOWN
+#define VER_FILEDESCRIPTION_STR "Microsoft\256 Browser Debug Utility"
+
+#define VER_INTERNALNAME_STR "browdeb.exe"
+#define VER_ORIGINALFILENAME_STR "browdeb.exe"
+
+#include <common.ver>
+
diff --git a/private/net/svcdlls/browser2/client/browstat.c b/private/net/svcdlls/browser2/client/browstat.c
new file mode 100644
index 000000000..691736192
--- /dev/null
+++ b/private/net/svcdlls/browser2/client/browstat.c
@@ -0,0 +1,3 @@
+#define _PSS_RELEASE
+
+#include "browdeb.c"
diff --git a/private/net/svcdlls/browser2/client/browstat.rc b/private/net/svcdlls/browser2/client/browstat.rc
new file mode 100644
index 000000000..227d967aa
--- /dev/null
+++ b/private/net/svcdlls/browser2/client/browstat.rc
@@ -0,0 +1,12 @@
+#include <windows.h>
+#include <ntverp.h>
+
+#define VER_FILETYPE VFT_APP
+#define VER_FILESUBTYPE VFT2_UNKNOWN
+#define VER_FILEDESCRIPTION_STR "Microsoft\256 Browser Statistics Utility"
+
+#define VER_INTERNALNAME_STR "browstat.exe"
+#define VER_ORIGINALFILENAME_STR "browstat.exe"
+
+#include <common.ver>
+
diff --git a/private/net/svcdlls/browser2/client/browstub.c b/private/net/svcdlls/browser2/client/browstub.c
new file mode 100644
index 000000000..6e740c128
--- /dev/null
+++ b/private/net/svcdlls/browser2/client/browstub.c
@@ -0,0 +1,1483 @@
+/*++
+
+Copyright (c) 1991-1992 Microsoft Corporation
+
+Module Name:
+
+ wksstub.c
+
+Abstract:
+
+ Client stubs of the Browser service APIs.
+
+Author:
+
+ Rita Wong (ritaw) 10-May-1991
+ Larry Osterman (LarryO) 23-Mar-1992
+
+Environment:
+
+ User Mode - Win32
+
+Revision History:
+
+ 18-Jun-1991 JohnRo
+ Remote NetUse APIs to downlevel servers.
+ 24-Jul-1991 JohnRo
+ Use NET_REMOTE_TRY_RPC etc macros for NetUse APIs.
+ Moved NetpIsServiceStarted() into NetLib.
+ 25-Jul-1991 JohnRo
+ Quiet DLL stub debug output.
+ 19-Aug-1991 JohnRo
+ Implement downlevel NetWksta APIs. Use NetRpc.h for NetWksta APIs.
+ 07-Nov-1991 JohnRo
+ RAID 4186: assert in RxNetShareAdd and other DLL stub problems.
+ 19-Nov-1991 JohnRo
+ Make sure status is correct for APIs not supported on downlevel.
+ Implement remote NetWkstaUserEnum().
+ 09-Nov-1992 JohnRo
+ Fix NET_API_FUNCTION references.
+ Avoid compiler warnings.
+--*/
+
+#include "brclient.h"
+#undef IF_DEBUG // avoid wsclient.h vs. debuglib.h conflicts.
+#include <debuglib.h> // IF_DEBUG() (needed by netrpc.h).
+#include <lmserver.h>
+#include <lmsvc.h>
+#include <rxuse.h> // RxNetUse APIs.
+#include <rxwksta.h> // RxNetWksta and RxNetWkstaUser APIs.
+#include <rap.h> // Needed by rxserver.h
+#include <rxserver.h> // RxNetServerEnum API.
+#include <netlib.h> // NetpServiceIsStarted() (needed by netrpc.h).
+#include <ntddbrow.h> // Browser definitions
+#include <netrpc.h> // NET_REMOTE macros.
+#include <align.h>
+#include <tstr.h>
+#include <tstring.h> // NetpInitOemString().
+#include <brcommon.h> // Routines common between client & server
+#include <lmapibuf.h> // NetApiBufferFree().
+#include <lmbrowsr.h> // Definition of I_BrowserServerEnum
+#include <icanon.h>
+#include <lmapibuf.h>
+
+//-------------------------------------------------------------------//
+// //
+// Global variables //
+// //
+//-------------------------------------------------------------------//
+
+#define API_SUCCESS(x) ((x) == NERR_Success || (x) == ERROR_MORE_DATA)
+
+
+//-------------------------------------------------------------------//
+// //
+// Global types //
+// //
+//-------------------------------------------------------------------//
+
+
+
+//-------------------------------------------------------------------//
+// //
+// Private routines //
+// //
+//-------------------------------------------------------------------//
+
+
+NET_API_STATUS
+GetBrowserTransportList(
+ OUT PLMDR_TRANSPORT_LIST *TransportList
+ );
+
+NET_API_STATUS
+EnumServersForTransport(
+ IN PUNICODE_STRING TransportName,
+ IN LPCWSTR DomainName OPTIONAL,
+ IN ULONG level,
+ IN ULONG prefmaxlen,
+ IN ULONG servertype,
+ IN LPTSTR CurrentComputerName,
+ OUT PINTERIM_SERVER_LIST InterimServerList,
+ OUT PULONG TotalEntriesOnThisTransport,
+ IN LPCWSTR FirstNameToReturn,
+ IN BOOLEAN WannishTransport,
+ IN BOOLEAN RasTransport,
+ IN BOOLEAN IPXTransport
+ );
+
+ NET_API_STATUS NET_API_FUNCTION
+NetServerEnum(
+ IN LPCWSTR servername OPTIONAL,
+ IN DWORD level,
+ OUT LPBYTE *bufptr,
+ IN DWORD prefmaxlen,
+ OUT LPDWORD entriesread,
+ OUT LPDWORD totalentries,
+ IN DWORD servertype,
+ IN LPCWSTR domain OPTIONAL,
+ IN OUT LPDWORD resume_handle OPTIONAL
+ )
+/*++
+
+Routine Description:
+
+ This is the DLL entrypoint for NetServerEnum.
+
+Arguments:
+
+ servername - Supplies the name of server to execute this function
+
+ level - Supplies the requested level of information.
+
+ bufptr - Returns a pointer to a buffer which contains the
+ requested transport information.
+
+ prefmaxlen - Supplies the number of bytes of information
+ to return in the buffer. If this value is MAXULONG, we will try
+ to return all available information if there is enough memory
+ resource.
+
+ entriesread - Returns the number of entries read into the buffer. This
+ value is returned only if the return code is NERR_Success or
+ ERROR_MORE_DATA.
+
+ totalentries - Returns the total number of entries available. This value
+ is returned only if the return code is NERR_Success or ERROR_MORE_DATA.
+
+ servertype - Supplies the type of server to enumerate.
+
+ domain - Supplies the name of one of the active domains to enumerate the
+ servers from. If NULL, servers from the primary domain, logon domain
+ and other domains are enumerated.
+
+ resume_handle - Supplies and returns the point to continue with enumeration.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NET_API_STATUS NetStatus;
+
+
+ //
+ // NetServerEnum is simply a wrapper to NetServerEnumEx
+ //
+
+ NetStatus = NetServerEnumEx(
+ servername,
+ level,
+ bufptr,
+ prefmaxlen,
+ entriesread,
+ totalentries,
+ servertype,
+ domain,
+ NULL ); // NULL FirstNameToReturn
+
+ if (ARGUMENT_PRESENT(resume_handle)) {
+ *resume_handle = 0;
+ }
+
+ return NetStatus;
+
+}
+
+
+ NET_API_STATUS NET_API_FUNCTION
+NetServerEnumEx(
+ IN LPCWSTR servername OPTIONAL,
+ IN DWORD level,
+ OUT LPBYTE *bufptr,
+ IN DWORD prefmaxlen,
+ OUT LPDWORD entriesread,
+ OUT LPDWORD totalentries,
+ IN DWORD servertype,
+ IN LPCWSTR domain OPTIONAL,
+ IN LPCWSTR FirstNameToReturnArg OPTIONAL
+ )
+/*++
+
+Routine Description:
+
+ This is the DLL entrypoint for NetServerEnum.
+
+Arguments:
+
+ servername - Supplies the name of server to execute this function
+
+ level - Supplies the requested level of information.
+
+ bufptr - Returns a pointer to a buffer which contains the
+ requested transport information.
+
+ prefmaxlen - Supplies the number of bytes of information
+ to return in the buffer. If this value is MAXULONG, we will try
+ to return all available information if there is enough memory
+ resource.
+
+ entriesread - Returns the number of entries read into the buffer. This
+ value is returned only if the return code is NERR_Success or
+ ERROR_MORE_DATA.
+
+ totalentries - Returns the total number of entries available. This value
+ is returned only if the return code is NERR_Success or ERROR_MORE_DATA.
+
+ servertype - Supplies the type of server to enumerate.
+
+ domain - Supplies the name of one of the active domains to enumerate the
+ servers from. If NULL, servers from the primary domain, logon domain
+ and other domains are enumerated.
+
+ FirstNameToReturnArg - Supplies the name of the first domain or server entry to return.
+ The caller can use this parameter to implement a resume handle of sorts by passing
+ the name of the last entry returned on a previous call. (Notice that the specified
+ entry will, also, be returned on this call unless it has since been deleted.)
+ Pass NULL (or a zero length string) to start with the first entry available.
+
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+ ERROR_MORE_DATA - More servers are available to be enumerated.
+
+ It is possible to return ERROR_MORE_DATA and zero entries in the case
+ where the browser server used doesn't support enumerating all the entries
+ it has. (e.g., an NT 3.5x Domain Master Browser that downloaded a domain
+ list from WINS and the WINS list is more than 64Kb long.) The caller
+ should simply ignore the additional data.
+
+ It is possible to fail to return ERROR_MORE_DATA and return a truncated
+ list. (e.g., an NT 3.5x Backup browser or WIN 95 backup browser in the
+ above mentioned domain. Such a backup browser replicates only 64kb
+ of data from the DMB (PDC) then represents that list as the entire list.)
+ The caller should ignore this problem. The site should upgrade its
+ browser servers.
+
+--*/
+{
+ PLMDR_TRANSPORT_LIST TransportList = NULL;
+ PLMDR_TRANSPORT_LIST TransportEntry = NULL;
+ INTERIM_SERVER_LIST InterimServerList;
+ NET_API_STATUS Status;
+ DWORD DomainNameSize = 0;
+ TCHAR DomainName[DNLEN + 1];
+ WCHAR FirstNameToReturn[DNLEN+1];
+ DWORD LocalTotalEntries;
+ BOOLEAN AnyTransportHasMoreData = FALSE;
+
+ //
+ //
+ // The workstation has to be started for the NetServerEnum API to work.
+ //
+ //
+
+ if ((Status = CheckForService(SERVICE_WORKSTATION, NULL)) != NERR_Success) {
+ return Status;
+ }
+
+ //
+ // Canonicalize the input parameters to make later comparisons easier.
+ //
+
+ if (ARGUMENT_PRESENT(domain)) {
+
+ if ( I_NetNameCanonicalize(
+ NULL,
+ (LPWSTR) domain,
+ DomainName,
+ (DNLEN + 1) * sizeof(TCHAR),
+ NAMETYPE_WORKGROUP,
+ LM2X_COMPATIBLE
+ ) != NERR_Success) {
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ DomainNameSize = STRLEN(DomainName) * sizeof(WCHAR);
+
+ domain = DomainName;
+ }
+
+ if (ARGUMENT_PRESENT(FirstNameToReturnArg) && *FirstNameToReturnArg != L'\0') {
+
+ if ( I_NetNameCanonicalize(
+ NULL,
+ (LPWSTR) FirstNameToReturnArg,
+ FirstNameToReturn,
+ sizeof(FirstNameToReturn),
+ NAMETYPE_WORKGROUP,
+ LM2X_COMPATIBLE
+ ) != NERR_Success) {
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ } else {
+ FirstNameToReturn[0] = L'\0';
+ }
+
+ if ((servername != NULL) &&
+ ( *servername != TCHAR_EOS)) {
+
+ //
+ // Call downlevel version of the API
+ //
+
+ Status = RxNetServerEnum(
+ servername,
+ NULL,
+ level,
+ bufptr,
+ prefmaxlen,
+ entriesread,
+ totalentries,
+ servertype,
+ domain,
+ FirstNameToReturn );
+
+ return Status;
+ }
+
+ //
+ // Only levels 100 and 101 are valid
+ //
+
+ if ((level != 100) && (level != 101)) {
+ return ERROR_INVALID_LEVEL;
+ }
+
+ if (servertype != SV_TYPE_ALL) {
+ if (servertype & SV_TYPE_DOMAIN_ENUM) {
+ if (servertype != SV_TYPE_DOMAIN_ENUM) {
+ return ERROR_INVALID_FUNCTION;
+ }
+ }
+ }
+
+
+ //
+ // Initialize the buffer to a known value.
+ //
+
+ *bufptr = NULL;
+
+ *entriesread = 0;
+
+ *totalentries = 0;
+
+ Status = InitializeInterimServerList(&InterimServerList, NULL, NULL, NULL, NULL);
+
+ try {
+ BOOL AnyEnumServersSucceeded = FALSE;
+ LPTSTR MyComputerName = NULL;
+
+ Status = NetpGetComputerName( &MyComputerName);
+
+ if ( Status != NERR_Success ) {
+ goto try_exit;
+ }
+
+ //
+ // Retrieve the list of transports from the browser.
+ //
+
+ Status = GetBrowserTransportList(&TransportList);
+
+ if (Status != NERR_Success) {
+ goto try_exit;
+ }
+
+ TransportEntry = TransportList;
+
+ while (TransportEntry != NULL) {
+ UNICODE_STRING TransportName;
+
+ TransportName.Buffer = TransportEntry->TransportName;
+ TransportName.Length = (USHORT)TransportEntry->TransportNameLength;
+ TransportName.MaximumLength = (USHORT)TransportEntry->TransportNameLength;
+
+ Status = EnumServersForTransport(&TransportName,
+ domain,
+ level,
+ prefmaxlen,
+ servertype,
+ MyComputerName,
+ &InterimServerList,
+ &LocalTotalEntries,
+ FirstNameToReturn,
+ (BOOLEAN)((TransportEntry->Flags & LMDR_TRANSPORT_WANNISH) != 0),
+ (BOOLEAN)((TransportEntry->Flags & LMDR_TRANSPORT_RAS) != 0),
+ (BOOLEAN)((TransportEntry->Flags & LMDR_TRANSPORT_IPX) != 0));
+
+ if (API_SUCCESS(Status)) {
+ if ( Status == ERROR_MORE_DATA ) {
+ AnyTransportHasMoreData = TRUE;
+ }
+ AnyEnumServersSucceeded = TRUE;
+ if ( LocalTotalEntries > *totalentries ) {
+ *totalentries = LocalTotalEntries;
+ }
+ }
+
+ if (TransportEntry->NextEntryOffset == 0) {
+ TransportEntry = NULL;
+ } else {
+ TransportEntry = (PLMDR_TRANSPORT_LIST)((PCHAR)TransportEntry+TransportEntry->NextEntryOffset);
+ }
+
+ }
+
+ if ( MyComputerName != NULL ) {
+ (void) NetApiBufferFree( MyComputerName );
+ }
+
+ if (AnyEnumServersSucceeded) {
+
+ //
+ // Pack the interim server list into its final form.
+ //
+
+ Status = PackServerList(&InterimServerList,
+ level,
+ servertype,
+ prefmaxlen,
+ (PVOID *)bufptr,
+ entriesread,
+ &LocalTotalEntries, // Pack thinks it has ALL the entries
+ NULL ); // The server has already returned us the right entries
+
+ if ( API_SUCCESS( Status ) ) {
+ if ( LocalTotalEntries > *totalentries ) {
+ *totalentries = LocalTotalEntries;
+ }
+ }
+ }
+
+try_exit:NOTHING;
+ } finally {
+ if (TransportList != NULL) {
+ MIDL_user_free(TransportList);
+ }
+
+ UninitializeInterimServerList(&InterimServerList);
+ }
+
+ if ( API_SUCCESS( Status )) {
+
+ //
+ // At this point,
+ // *totalentries is the largest of:
+ // The TotalEntries returned from any transport.
+ // The actual number of entries read.
+ //
+ // Adjust TotalEntries returned for reality.
+ //
+
+ if ( Status == NERR_Success ) {
+ *totalentries = *entriesread;
+ } else {
+ if ( *totalentries <= *entriesread ) {
+ *totalentries = *entriesread + 1;
+ }
+ }
+
+ //
+ // Ensure we return ERROR_MORE_DATA if any transport has more data.
+ //
+
+ if ( AnyTransportHasMoreData ) {
+ Status = ERROR_MORE_DATA;
+ }
+ }
+
+ return Status;
+}
+
+ NET_API_STATUS
+EnumServersForTransport(
+ IN PUNICODE_STRING TransportName,
+ IN LPCWSTR DomainName OPTIONAL,
+ IN ULONG level,
+ IN ULONG prefmaxlen,
+ IN ULONG servertype,
+ IN LPTSTR CurrentComputerName,
+ OUT PINTERIM_SERVER_LIST InterimServerList,
+ OUT PULONG TotalEntriesOnThisTransport,
+ IN LPCWSTR FirstNameToReturn,
+ IN BOOLEAN WannishTransport,
+ IN BOOLEAN RasTransport,
+ IN BOOLEAN IpxTransport
+ )
+{
+ PWSTR *BrowserList = NULL;
+ ULONG BrowserListLength = 0;
+ NET_API_STATUS Status;
+ PVOID ServerList = NULL;
+ ULONG EntriesInList = 0;
+ ULONG ServerIndex = 0;
+
+ //
+ // Skip over IPX transports - we can't contact machines over them anyway.
+ //
+
+ *TotalEntriesOnThisTransport = 0;
+
+ if (IpxTransport) {
+ return NERR_Success;
+ }
+
+ //
+ // Retrieve a new browser list. Do not force a revalidation.
+ //
+
+ Status = GetBrowserServerList(TransportName,
+ DomainName,
+ &BrowserList,
+ &BrowserListLength,
+ FALSE);
+
+ //
+ // If a domain name was specified and we were unable to find the browse
+ // master for the domain and we are running on a wannish transport,
+ // invoke the "double hop" code and allow a local browser server
+ // remote the API to the browse master for that domain (we assume that
+ // this means that the workgroup is on a different subnet of a WAN).
+ //
+
+ if (!API_SUCCESS(Status) &&
+ DomainName != NULL) {
+
+ Status = GetBrowserServerList(TransportName,
+ NULL,
+ &BrowserList,
+ &BrowserListLength,
+ FALSE);
+
+
+ }
+
+
+ //
+ // If we were able to retrieve the list, remote the API. Otherwise
+ // return.
+ //
+
+ if (API_SUCCESS(Status)) {
+
+ do {
+ LPTSTR Transport;
+ LPTSTR ServerName;
+ BOOL AlreadyInTree;
+
+ //
+ // Remote the API to that server.
+ //
+
+ Transport = TransportName->Buffer;
+ ServerName = BrowserList[0];
+ *TotalEntriesOnThisTransport = 0;
+
+ // add 2 to skip double backslash at start of ServerName
+
+ if ( STRICMP(ServerName + 2, CurrentComputerName ) == 0 ) {
+
+ //
+ // If we are going to remote the API to ourselves,
+ // and we are running the browser service, simply
+ // use RPC to get the information we need, don't
+ // bother using the redirector. This allows us to
+ // avoid tying up RPCXLATE thread.
+ //
+
+ Status = I_BrowserServerEnumEx (
+ NULL,
+ Transport,
+ CurrentComputerName,
+ level,
+ (LPBYTE *)&ServerList,
+ prefmaxlen,
+ &EntriesInList,
+ TotalEntriesOnThisTransport,
+ servertype,
+ DomainName,
+ FirstNameToReturn );
+
+ } else {
+
+ Status = RxNetServerEnum(
+ ServerName,
+ Transport,
+ level,
+ (LPBYTE *)&ServerList,
+ prefmaxlen,
+ &EntriesInList,
+ TotalEntriesOnThisTransport,
+ servertype,
+ DomainName,
+ FirstNameToReturn );
+
+
+ }
+
+ if ( !API_SUCCESS(Status)) {
+ NET_API_STATUS GetBListStatus;
+
+ //
+ // If we failed to remote the API for some reason,
+ // we want to regenerate the bowsers list of browser
+ // servers.
+ //
+
+ if (BrowserList != NULL) {
+
+ MIDL_user_free(BrowserList);
+
+ BrowserList = NULL;
+ }
+
+
+ GetBListStatus = GetBrowserServerList(TransportName,
+ DomainName,
+ &BrowserList,
+ &BrowserListLength,
+ TRUE);
+ if (GetBListStatus != NERR_Success) {
+
+ //
+ // If we were unable to reload the list,
+ // try the next transport.
+ //
+
+ break;
+ }
+
+ ServerIndex += 1;
+
+ //
+ // If we've looped more times than we got servers
+ // in the list, we're done.
+ //
+
+ if ( ServerIndex > BrowserListLength ) {
+ break;
+ }
+
+ } else {
+
+ NET_API_STATUS TempStatus;
+ TempStatus = MergeServerList(
+ InterimServerList,
+ level,
+ ServerList,
+ EntriesInList,
+ *TotalEntriesOnThisTransport );
+
+ if ( TempStatus != NERR_Success ) {
+ Status = TempStatus;
+ }
+
+ //
+ // The remote API succeeded.
+ //
+ // Now free up the remaining parts of the list.
+ //
+
+ if (ServerList != NULL) {
+ NetApiBufferFree(ServerList);
+ ServerList = NULL;
+ }
+
+ // We're done regardless of the success or failure of MergeServerList.
+ break;
+
+ }
+
+ } while ( !API_SUCCESS(Status) );
+
+ }
+
+ //
+ // Free up the browser list.
+ //
+
+ if (BrowserList != NULL) {
+ MIDL_user_free(BrowserList);
+ BrowserList = NULL;
+ }
+
+ return Status;
+}
+
+
+NET_API_STATUS
+GetBrowserTransportList(
+ OUT PLMDR_TRANSPORT_LIST *TransportList
+ )
+
+/*++
+
+Routine Description:
+
+ This routine returns the list of transports bound into the browser.
+
+Arguments:
+
+ OUT PLMDR_TRANSPORT_LIST *TransportList - Transport list to return.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+
+{
+
+ NET_API_STATUS Status;
+ HANDLE BrowserHandle;
+ LMDR_REQUEST_PACKET RequestPacket;
+
+ Status = OpenBrowser(&BrowserHandle);
+
+ if (Status != NERR_Success) {
+ return Status;
+ }
+
+ RequestPacket.Version = LMDR_REQUEST_PACKET_VERSION_DOM;
+
+ RequestPacket.Type = EnumerateXports;
+
+ RtlInitUnicodeString(&RequestPacket.TransportName, NULL);
+ RtlInitUnicodeString(&RequestPacket.EmulatedDomainName, NULL);
+
+ Status = DeviceControlGetInfo(
+ BrowserHandle,
+ IOCTL_LMDR_ENUMERATE_TRANSPORTS,
+ &RequestPacket,
+ sizeof(RequestPacket),
+ (PVOID *)TransportList,
+ 0xffffffff,
+ 4096,
+ NULL);
+
+ NtClose(BrowserHandle);
+
+ return Status;
+}
+
+ NET_API_STATUS
+I_BrowserServerEnum (
+ IN LPCWSTR servername OPTIONAL,
+ IN LPCWSTR transport OPTIONAL,
+ IN LPCWSTR clientname OPTIONAL,
+ IN DWORD level,
+ OUT LPBYTE *bufptr,
+ IN DWORD prefmaxlen,
+ OUT LPDWORD entriesread,
+ OUT LPDWORD totalentries,
+ IN DWORD servertype,
+ IN LPCWSTR domain OPTIONAL,
+ IN OUT LPDWORD resume_handle OPTIONAL
+ )
+
+/*++
+
+Routine Description:
+
+ This is the DLL entrypoint for NetWkstaSetInfo.
+
+Arguments:
+
+ servername - Supplies the name of server to execute this function
+
+ level - Supplies the level of information.
+
+ buf - Supplies a buffer which contains the information structure of fields
+ to set. The level denotes the structure in this buffer.
+
+ parm_err - Returns the identifier to the invalid parameter in buf if this
+ function returns ERROR_INVALID_PARAMETER.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NET_API_STATUS status;
+ GENERIC_INFO_CONTAINER GenericInfoContainer;
+ GENERIC_ENUM_STRUCT InfoStruct;
+
+ GenericInfoContainer.Buffer = NULL;
+ GenericInfoContainer.EntriesRead = 0;
+
+ InfoStruct.Container = &GenericInfoContainer;
+ InfoStruct.Level = level;
+
+ NET_REMOTE_TRY_RPC
+
+ //
+ // Try RPC (local or remote) version of API.
+ //
+
+ status = I_BrowserrServerEnum(
+ (LPWSTR) servername,
+ (LPWSTR) transport,
+ (LPWSTR) clientname,
+ (LPSERVER_ENUM_STRUCT)&InfoStruct,
+ prefmaxlen,
+ totalentries,
+ servertype,
+ (LPWSTR) domain,
+ resume_handle
+ );
+
+ if (status == NERR_Success || status == ERROR_MORE_DATA) {
+ *bufptr = (LPBYTE) GenericInfoContainer.Buffer;
+ *entriesread = GenericInfoContainer.EntriesRead;
+
+#if 0
+ if (((servertype == SV_TYPE_ALL || servertype == SV_TYPE_DOMAIN_ENUM)) &&
+ (STRICMP(transport, L"\\Device\\Streams\\NBT"))) {
+ if (*entriesread <= 20) {
+ KdPrint(("RPC API Returned EntriesRead == %ld on transport %ws\n", *entriesread, transport));
+ }
+ if (*totalentries <= 20) {
+ KdPrint(("RPC API Returned TotalEntries == %ld on transport %ws\n", *totalentries, transport));
+ }
+ }
+#endif
+ }
+
+ NET_REMOTE_RPC_FAILED("I_BrServerEnum",
+ servername,
+ status,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_BROWSER )
+
+ //
+ // There is no downlevel version of api.
+ //
+ status = ERROR_NOT_SUPPORTED;
+
+ NET_REMOTE_END
+
+#if 0
+ if ((servertype == SV_TYPE_ALL || servertype == SV_TYPE_DOMAIN_ENUM) &&
+ (STRICMP(transport, L"\\Device\\Streams\\NBT"))) {
+ if (*entriesread <= 20) {
+ KdPrint(("Client API Returned EntriesRead == %ld on transport %ws\n", *entriesread, transport));
+ }
+ if (*totalentries <= 20) {
+ KdPrint(("Client API Returned TotalEntries == %ld on transport %ws\n", *totalentries, transport));
+ }
+ }
+#endif
+
+ return status;
+}
+
+ NET_API_STATUS
+I_BrowserServerEnumEx (
+ IN LPCWSTR servername OPTIONAL,
+ IN LPCWSTR transport OPTIONAL,
+ IN LPCWSTR clientname OPTIONAL,
+ IN DWORD level,
+ OUT LPBYTE *bufptr,
+ IN DWORD prefmaxlen,
+ OUT LPDWORD entriesread,
+ OUT LPDWORD totalentries,
+ IN DWORD servertype,
+ IN LPCWSTR domain OPTIONAL,
+ IN LPCWSTR FirstNameToReturn OPTIONAL
+ )
+
+/*++
+
+Routine Description:
+
+ This is the DLL entrypoint for NetWkstaSetInfo.
+
+Arguments:
+
+ servername - Supplies the name of server to execute this function
+
+ level - Supplies the level of information.
+
+ buf - Supplies a buffer which contains the information structure of fields
+ to set. The level denotes the structure in this buffer.
+
+ parm_err - Returns the identifier to the invalid parameter in buf if this
+ function returns ERROR_INVALID_PARAMETER.
+
+ FirstNameToReturn - Supplies the name of the first domain or server entry to return.
+ The caller can use this parameter to implement a resume handle of sorts by passing
+ the name of the last entry returned on a previous call. (Notice that the specified
+ entry will, also, be returned on this call unless it has since been deleted.)
+ Pass NULL to start with the first entry available.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NET_API_STATUS status;
+ GENERIC_INFO_CONTAINER GenericInfoContainer;
+ GENERIC_ENUM_STRUCT InfoStruct;
+
+ GenericInfoContainer.Buffer = NULL;
+ GenericInfoContainer.EntriesRead = 0;
+
+ InfoStruct.Container = &GenericInfoContainer;
+ InfoStruct.Level = level;
+
+ NET_REMOTE_TRY_RPC
+
+ //
+ // Try RPC (local or remote) version of API.
+ //
+
+ status = I_BrowserrServerEnumEx(
+ (LPWSTR) servername,
+ (LPWSTR) transport,
+ (LPWSTR) clientname,
+ (LPSERVER_ENUM_STRUCT)&InfoStruct,
+ prefmaxlen,
+ totalentries,
+ servertype,
+ (LPWSTR) domain,
+ (LPWSTR) FirstNameToReturn );
+
+ if (status == NERR_Success || status == ERROR_MORE_DATA) {
+ *bufptr = (LPBYTE) GenericInfoContainer.Buffer;
+ *entriesread = GenericInfoContainer.EntriesRead;
+
+#if 0
+ if (((servertype == SV_TYPE_ALL || servertype == SV_TYPE_DOMAIN_ENUM)) &&
+ (STRICMP(transport, L"\\Device\\Streams\\NBT"))) {
+ if (*entriesread <= 20) {
+ KdPrint(("RPC API Returned EntriesRead == %ld on transport %ws\n", *entriesread, transport));
+ }
+ if (*totalentries <= 20) {
+ KdPrint(("RPC API Returned TotalEntries == %ld on transport %ws\n", *totalentries, transport));
+ }
+ }
+#endif
+ }
+
+ NET_REMOTE_RPC_FAILED("I_BrServerEnum",
+ servername,
+ status,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_BROWSER )
+
+ //
+ // There is no downlevel version of api.
+ //
+ status = ERROR_NOT_SUPPORTED;
+
+ NET_REMOTE_END
+
+#if 0
+ if ((servertype == SV_TYPE_ALL || servertype == SV_TYPE_DOMAIN_ENUM) &&
+ (STRICMP(transport, L"\\Device\\Streams\\NBT"))) {
+ if (*entriesread <= 20) {
+ KdPrint(("Client API Returned EntriesRead == %ld on transport %ws\n", *entriesread, transport));
+ }
+ if (*totalentries <= 20) {
+ KdPrint(("Client API Returned TotalEntries == %ld on transport %ws\n", *totalentries, transport));
+ }
+ }
+#endif
+
+ return status;
+}
+
+
+ NET_API_STATUS NET_API_FUNCTION
+I_BrowserQueryOtherDomains (
+ IN LPCWSTR servername OPTIONAL,
+ OUT LPBYTE *bufptr,
+ OUT LPDWORD entriesread,
+ OUT LPDWORD totalentries
+ )
+
+/*++
+
+Routine Description:
+
+ This is the DLL entrypoint for NetWkstaSetInfo.
+
+Arguments:
+
+ servername - Supplies the name of server to execute this function
+
+ buf - Supplies a buffer which contains the information structure of fields
+ to set. The level denotes the structure in this buffer.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NET_API_STATUS status;
+ GENERIC_INFO_CONTAINER GenericInfoContainer;
+ GENERIC_ENUM_STRUCT InfoStruct;
+
+ GenericInfoContainer.Buffer = NULL;
+ GenericInfoContainer.EntriesRead = 0;
+
+ InfoStruct.Container = &GenericInfoContainer;
+ InfoStruct.Level = 100;
+
+ NET_REMOTE_TRY_RPC
+
+ //
+ // Try RPC (local or remote) version of API.
+ //
+
+ status = I_BrowserrQueryOtherDomains (
+ (LPWSTR) servername,
+ (LPSERVER_ENUM_STRUCT)&InfoStruct,
+ totalentries
+ );
+
+ if (status == NERR_Success || status == ERROR_MORE_DATA) {
+ *bufptr = (LPBYTE) GenericInfoContainer.Buffer;
+ *entriesread = GenericInfoContainer.EntriesRead;
+ }
+
+ NET_REMOTE_RPC_FAILED("I_BrowserQueryOtherDomains",
+ servername,
+ status,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_BROWSER )
+
+ //
+ // There is no downlevel version of api.
+ //
+ status = ERROR_NOT_SUPPORTED;
+
+ NET_REMOTE_END
+
+ return status;
+}
+ NET_API_STATUS
+I_BrowserResetNetlogonState (
+ IN LPCWSTR servername OPTIONAL
+ )
+
+/*++
+
+Routine Description:
+
+ This is the DLL entrypoint for NetWkstaSetInfo.
+
+Arguments:
+
+ servername - Supplies the name of server to execute this function
+
+ buf - Supplies a buffer which contains the information structure of fields
+ to set. The level denotes the structure in this buffer.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NET_API_STATUS status;
+
+ NET_REMOTE_TRY_RPC
+
+ //
+ // Try RPC (local or remote) version of API.
+ //
+
+ status = I_BrowserrResetNetlogonState (
+ (LPWSTR) servername );
+
+ NET_REMOTE_RPC_FAILED("I_BrowserResetNetlogonState",
+ servername,
+ status,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_BROWSER )
+
+ //
+ // There is no downlevel version of api.
+ //
+ status = ERROR_NOT_SUPPORTED;
+
+ NET_REMOTE_END
+
+ return status;
+}
+
+
+NET_API_STATUS
+I_BrowserDebugCall (
+ IN LPTSTR servername OPTIONAL,
+ IN DWORD DebugCode,
+ IN DWORD Options
+ )
+{
+ NET_API_STATUS status;
+
+ NET_REMOTE_TRY_RPC
+
+ //
+ // Try RPC (local or remote) version of API.
+ //
+
+ status = I_BrowserrDebugCall(
+ servername,
+ DebugCode,
+ Options
+ );
+
+
+ NET_REMOTE_RPC_FAILED("I_BrowserDebugCall",
+ servername,
+ status,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_BROWSER )
+
+ //
+ // There is no downlevel version of api.
+ //
+ status = ERROR_NOT_SUPPORTED;
+
+ NET_REMOTE_END
+
+ return status;
+
+}
+
+NET_API_STATUS
+I_BrowserDebugTrace (
+ IN LPTSTR servername OPTIONAL,
+ IN PCHAR DebugString
+ )
+{
+ NET_API_STATUS status;
+
+ NET_REMOTE_TRY_RPC
+
+ //
+ // Try RPC (local or remote) version of API.
+ //
+
+ status = I_BrowserrDebugTrace(
+ servername,
+ DebugString
+ );
+
+
+ NET_REMOTE_RPC_FAILED("I_BrowserDebugTrace",
+ servername,
+ status,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_BROWSER )
+
+ //
+ // There is no downlevel version of api.
+ //
+ status = ERROR_NOT_SUPPORTED;
+
+ NET_REMOTE_END
+
+ return status;
+
+}
+
+
+NET_API_STATUS
+I_BrowserQueryStatistics (
+ IN LPCWSTR servername OPTIONAL,
+ IN OUT LPBROWSER_STATISTICS *Statistics
+ )
+{
+ NET_API_STATUS status;
+
+ NET_REMOTE_TRY_RPC
+
+ //
+ // Try RPC (local or remote) version of API.
+ //
+
+ status = I_BrowserrQueryStatistics(
+ (LPWSTR) servername,
+ Statistics
+ );
+
+
+ NET_REMOTE_RPC_FAILED("I_BrowserQueryStatistics",
+ servername,
+ status,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_BROWSER )
+
+ //
+ // There is no downlevel version of api.
+ //
+ status = ERROR_NOT_SUPPORTED;
+
+ NET_REMOTE_END
+
+ return status;
+
+}
+
+ NET_API_STATUS
+I_BrowserResetStatistics (
+ IN LPCWSTR servername OPTIONAL
+ )
+{
+ NET_API_STATUS status;
+
+ NET_REMOTE_TRY_RPC
+
+ //
+ // Try RPC (local or remote) version of API.
+ //
+
+ status = I_BrowserrResetStatistics(
+ (LPWSTR) servername );
+
+
+ NET_REMOTE_RPC_FAILED("I_BrowserResetStatistics",
+ servername,
+ status,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_BROWSER )
+
+ //
+ // There is no downlevel version of api.
+ //
+ status = ERROR_NOT_SUPPORTED;
+
+ NET_REMOTE_END
+
+ return status;
+
+}
+
+
+NET_API_STATUS
+NetBrowserStatisticsGet(
+ IN LPTSTR ServerName,
+ IN DWORD Level,
+ OUT LPBYTE* Buffer
+ )
+
+/*++
+
+Routine Description:
+
+ Wrapper for workstation statistics retrieval routine - either calls the
+ client-side RPC function or calls RxNetStatisticsGet to retrieve the
+ statistics from a down-level workstation service
+
+Arguments:
+
+ ServerName - where to remote this function
+ Level - of information required (100, or 101)
+ Buffer - pointer to pointer to returned buffer
+
+Return Value:
+
+ NET_API_STATUS
+ Success - NERR_Success
+ Failure - ERROR_INVALID_LEVEL
+ Level not 0
+ ERROR_INVALID_PARAMETER
+ Unsupported options requested
+ ERROR_NOT_SUPPORTED
+ Service is not SERVER or WORKSTATION
+ ERROR_ACCESS_DENIED
+ Caller doesn't have necessary access rights for request
+
+--*/
+
+{
+ NET_API_STATUS status;
+ GENERIC_INFO_CONTAINER GenericInfoContainer;
+ GENERIC_ENUM_STRUCT InfoStruct;
+
+ //
+ // set the caller's buffer pointer to known value. This will kill the
+ // calling app if it gave us a bad pointer and didn't use try...except
+ //
+
+ *Buffer = NULL;
+
+ //
+ // validate parms
+ //
+
+ if (Level != 100 && Level != 101) {
+ return ERROR_INVALID_LEVEL;
+ }
+
+ GenericInfoContainer.Buffer = NULL;
+ GenericInfoContainer.EntriesRead = 0;
+
+ InfoStruct.Container = &GenericInfoContainer;
+ InfoStruct.Level = Level;
+
+
+ NET_REMOTE_TRY_RPC
+ status = NetrBrowserStatisticsGet(ServerName,
+ Level,
+ (PBROWSER_STATISTICS_STRUCT)&InfoStruct
+ );
+
+ if (status == NERR_Success || status == ERROR_MORE_DATA) {
+ *Buffer = (LPBYTE) GenericInfoContainer.Buffer;
+ }
+
+ NET_REMOTE_RPC_FAILED("NetBrowserStatisticsGet",
+ ServerName,
+ status,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_BROWSER
+ )
+
+ status = ERROR_NOT_SUPPORTED;
+
+ NET_REMOTE_END
+
+ return status;
+}
+
+
+ NET_API_STATUS
+I_BrowserSetNetlogonState(
+ IN LPWSTR ServerName,
+ IN LPWSTR DomainName,
+ IN LPWSTR EmulatedComputerName,
+ IN DWORD Role
+ )
+/*++
+
+Routine Description:
+
+ This is the DLL entrypoint for I_BrowserSetNetlogonState.
+
+Arguments:
+
+ servername - Supplies the name of server to execute this function
+
+ DomainName - name of the domain who's role is to be updated.
+
+ EmulatedComputerName - Name of the computer within DomainName
+
+ Role - Role of the specified domain.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NET_API_STATUS status;
+
+ NET_REMOTE_TRY_RPC
+
+ //
+ // Try RPC (local or remote) version of API.
+ //
+
+ status = I_BrowserrSetNetlogonState (
+ ServerName,
+ DomainName,
+ EmulatedComputerName,
+ Role );
+
+ NET_REMOTE_RPC_FAILED("I_BrowserSetNetlogonState",
+ ServerName,
+ status,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_BROWSER )
+
+ //
+ // There is no downlevel version of api.
+ //
+ status = ERROR_NOT_SUPPORTED;
+
+ NET_REMOTE_END
+
+ return status;
+}
+
+ NET_API_STATUS NET_API_FUNCTION
+I_BrowserQueryEmulatedDomains (
+ IN LPTSTR ServerName OPTIONAL,
+ OUT PBROWSER_EMULATED_DOMAIN *EmulatedDomains,
+ OUT LPDWORD EntriesRead
+ )
+
+/*++
+
+Routine Description:
+
+ This is the DLL entrypoint for I_BrowserQueryEmulatedDomains.
+
+Arguments:
+
+ ServerName - Supplies the name of server to execute this function
+
+ EmulatedDomains - Returns a pointer to a an allocated array of emulated domain
+ information.
+
+ EntriesRead - Returns the number of entries in 'EmulatedDomains'
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NET_API_STATUS NetStatus;
+ BROWSER_EMULATED_DOMAIN_CONTAINER Container;
+
+ // Force RPC to allocate the buffer
+ Container.Buffer = NULL;
+ Container.EntriesRead = 0;
+ *EmulatedDomains = NULL;
+ *EntriesRead = 0;
+
+ NET_REMOTE_TRY_RPC
+
+ //
+ // Try RPC (local or remote) version of API.
+ //
+
+ NetStatus = I_BrowserrQueryEmulatedDomains (
+ ServerName,
+ &Container );
+
+ if ( NetStatus == NERR_Success ) {
+ *EmulatedDomains = (PBROWSER_EMULATED_DOMAIN) Container.Buffer;
+ *EntriesRead = Container.EntriesRead;
+ }
+
+
+ NET_REMOTE_RPC_FAILED("I_BrowserQueryEmulatedDomains",
+ ServerName,
+ NetStatus,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_BROWSER )
+
+ //
+ // There is no downlevel version of api.
+ //
+ NetStatus = ERROR_NOT_SUPPORTED;
+
+ NET_REMOTE_END
+
+ return NetStatus;
+}
diff --git a/private/net/svcdlls/browser2/client/brtsenum.c b/private/net/svcdlls/browser2/client/brtsenum.c
new file mode 100644
index 000000000..1347ad301
--- /dev/null
+++ b/private/net/svcdlls/browser2/client/brtsenum.c
@@ -0,0 +1,590 @@
+/*++
+
+Copyright (c) 1991-1992 Microsoft Corporation
+
+Module Name:
+
+ wstsend.c
+
+Abstract:
+
+ Test program for the NetServerEnum API. Run this test after
+ starting the Workstation service.
+
+ wstenum [domain]
+
+Author:
+
+ Rita Wong (ritaw) 24-Oct-1991
+
+Revision History:
+
+--*/
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+
+#include <winerror.h>
+#include <windef.h> // Win32 type definitions
+#include <winbase.h> // Win32 base API prototypes
+
+#include <lm.h> // LAN Man definitions
+#include <lmserver.h>
+
+#include <tstring.h>
+#include <netdebug.h> // NetpDbgDisplay routines.
+#include <dlserver.h>
+
+#define FIXED_WIDTH_STRING "%-30ws: "
+#define INDENT " "
+
+VOID
+DisplayServerInfo(
+ IN DWORD Level,
+ IN LPVOID Info
+ );
+
+VOID
+DisplayDword(
+ IN LPTSTR Tag,
+ IN DWORD Value
+ );
+
+VOID
+DisplayBool(
+ IN LPTSTR Tag,
+ IN BOOL Value
+ );
+
+VOID
+TestServerEnum(
+ IN LPTSTR DomainName OPTIONAL,
+ IN DWORD PreferedMaximumLength,
+ IN OUT LPDWORD ResumeHandle OPTIONAL
+ );
+
+
+
+VOID
+_cdecl
+main(
+ int argc,
+ char *argv[]
+ )
+{
+ LPTSTR DomainName = NULL;
+
+ if (argc > 2) {
+ printf("Usage: wstenum [domain].\n");
+ return;
+ }
+
+ if (argc == 2) {
+#ifdef UNICODE
+ OEM_STRING AString;
+ UNICODE_STRING UString;
+
+ NetpInitOemString(&AString, argv[1]);
+ RtlOemStringToUnicodeString(&UString, &AString, TRUE);
+ DomainName = UString.Buffer;
+#else
+ DomainName = argv[1];
+
+#endif
+ }
+
+ //
+ // Enumerate all servers
+ //
+ TestServerEnum(DomainName, MAXULONG, NULL);
+}
+
+
+
+
+
+VOID
+TestServerEnum(
+ IN LPTSTR DomainName OPTIONAL,
+ IN DWORD PreferedMaximumLength,
+ IN OUT LPDWORD ResumeHandle OPTIONAL
+ )
+{
+ DWORD i;
+ NET_API_STATUS status;
+ DWORD EntriesRead,
+ TotalEntries;
+
+ PSERVER_INFO_101 ServerInfo, saveptr;
+
+
+ if (ARGUMENT_PRESENT(ResumeHandle)) {
+ printf("\nInput ResumeHandle=x%08lx\n", *ResumeHandle);
+ }
+
+ status = NetServerEnum(
+ NULL,
+ 101,
+ (LPBYTE *) &ServerInfo,
+ PreferedMaximumLength,
+ &EntriesRead,
+ &TotalEntries,
+ SV_TYPE_ALL,
+ DomainName,
+ ResumeHandle
+ );
+
+ saveptr = ServerInfo;
+
+ if (status != NERR_Success && status != ERROR_MORE_DATA) {
+ printf("NetServerEnum FAILED %lu\n", status);
+ }
+ else {
+ printf("Return code from NetServerEnum %lu\n", status);
+
+ printf("EntriesRead=%lu, TotalEntries=%lu\n",
+ EntriesRead, TotalEntries);
+
+ if (ARGUMENT_PRESENT(ResumeHandle)) {
+ printf("Output ResumeHandle=x%08lx\n", *ResumeHandle);
+ }
+
+ for (i = 0; i < EntriesRead; i++, ServerInfo++) {
+ DisplayServerInfo(101, ServerInfo);
+ }
+
+ //
+ // Free buffer allocated for us.
+ //
+ NetApiBufferFree(saveptr);
+ }
+}
+VOID
+DisplayTag(
+ IN LPTSTR Tag
+ )
+{
+ printf(INDENT FIXED_WIDTH_STRING, Tag);
+
+} // NetpDbgDisplayTag
+VOID
+DisplayString(
+ IN LPTSTR Tag,
+ IN LPTSTR Value
+ )
+{
+ DisplayTag( Tag );
+ if (Value != NULL) {
+ printf(FORMAT_LPTSTR "\n", Value);
+ } else {
+ printf("(none)\n");
+ }
+
+} // NetpDbgDisplayString
+
+VOID
+DisplayDwordHex(
+ IN LPTSTR Tag,
+ IN DWORD Value
+ )
+{
+ DisplayTag( Tag );
+ printf(FORMAT_HEX_DWORD, Value);
+ printf("\n");
+
+} // NetpDbgDisplayDwordHex
+
+DBGSTATIC VOID
+DisplayServerType(
+ IN DWORD Type
+ )
+{
+ // Longest name is "POTENTIAL_BROWSER" (14 chars)
+ TCHAR str[(17+2)*17]; // 17 chars per name, 2 spaces, for 17 names.
+ str[0] = '\0';
+
+ // BUGBUG: Use TEXT() macro somehow below?
+#define DO(name) \
+ if (Type & SV_TYPE_ ## name) { \
+ (void) STRCAT(str, TEXT(#name)); \
+ (void) STRCAT(str, TEXT(" ")); \
+ Type &= ~(SV_TYPE_ ## name); \
+ }
+
+ NetpAssert(Type != 0);
+ DO(WORKSTATION)
+ DO(SERVER)
+ DO(SQLSERVER)
+ DO(DOMAIN_CTRL)
+ DO(DOMAIN_BAKCTRL)
+ DO(TIME_SOURCE)
+ DO(AFP)
+ DO(NOVELL)
+ DO(DOMAIN_MEMBER)
+ DO(PRINTQ_SERVER)
+ DO(DIALIN_SERVER)
+ DO(XENIX_SERVER)
+ DO(NT)
+ DO(POTENTIAL_BROWSER)
+ DO(BACKUP_BROWSER)
+ DO(MASTER_BROWSER)
+ DO(DOMAIN_MASTER)
+
+ DisplayString(TEXT("Server Type"), str);
+ if (Type != 0) {
+ DisplayDwordHex( TEXT("UNEXPECTED TYPE BIT(S)"), Type );
+ }
+
+} // DisplayServerType
+VOID
+DisplayLanManVersion(
+ IN DWORD MajorVersion,
+ IN DWORD MinorVersion
+ )
+{
+ DisplayTag( TEXT("LanMan version") );
+ printf(FORMAT_DWORD "." FORMAT_DWORD "\n",
+ (DWORD) (MajorVersion & (MAJOR_VERSION_MASK)),
+ (DWORD) (MinorVersion) );
+
+} // DisplayLanManVersion
+
+DBGSTATIC VOID
+DisplayDisconnectTime(
+ IN LONG DiscTime
+ )
+{
+ DisplayTag( TEXT("Idle session time (min)") );
+ if (DiscTime == SV_NODISC) {
+ printf("infinite\n" );
+ } else {
+ printf(FORMAT_LONG "\n", DiscTime );
+ }
+} // NetpDbgDisplayDisconnectTime
+
+DBGSTATIC VOID
+DisplayLicenses(
+ IN DWORD MajorVersion,
+ IN DWORD Licenses
+ )
+{
+ // BUGBUG: Eventually, use <srvver.h> stuff to handle unlimited and other
+ // types of licenses. This is indicated in MajorVersion.
+ UNREFERENCED_PARAMETER( MajorVersion );
+
+ DisplayDword( TEXT("Licenses (NOT users)"), Licenses );
+} // NetpDbgDisplayLicenses
+
+VOID
+DisplayPlatformId(
+ IN DWORD Value
+ )
+{
+ LPTSTR String;
+
+ switch (Value) {
+ case PLATFORM_ID_DOS : String = TEXT("DOS"); break;
+ case PLATFORM_ID_OS2 : String = TEXT("OS2"); break;
+ case PLATFORM_ID_NT : String = TEXT("NT"); break;
+ default : String = TEXT("unknown"); break;
+ }
+
+ DisplayString( TEXT("Platform ID"), String );
+}
+VOID
+DisplayBool(
+ IN LPTSTR Tag,
+ IN BOOL Value
+ )
+{
+ DisplayTag( Tag );
+ printf(Value ? "Yes" : "No");
+ printf("\n");
+
+}
+
+VOID
+DisplayDword(
+ IN LPTSTR Tag,
+ IN DWORD Value
+ )
+{
+ DisplayTag( Tag );
+ printf(FORMAT_DWORD, Value);
+ printf("\n");
+
+} // DbgDisplayDword
+VOID
+DisplayServerInfo(
+ IN DWORD Level,
+ IN LPVOID Info
+ )
+{
+ printf("server info (level " FORMAT_DWORD ") at "
+ FORMAT_LPVOID ":\n", Level, (LPVOID) Info);
+ NetpAssert(Info != NULL);
+
+ switch (Level) {
+ case 0 :
+ {
+ LPSERVER_INFO_0 psv0 = Info;
+ DisplayString(TEXT("name"), psv0->sv0_name);
+ }
+ break;
+
+ case 1 :
+ {
+ LPSERVER_INFO_1 psv1 = Info;
+ DisplayString(TEXT("name"), psv1->sv1_name);
+ DisplayLanManVersion(
+ psv1->sv1_version_major,
+ psv1->sv1_version_minor);
+ DisplayServerType( psv1->sv1_type );
+ DisplayString(TEXT("comment"), psv1->sv1_comment);
+ }
+ break;
+
+ case 2 :
+ {
+ LPSERVER_INFO_2 psv2 = Info;
+ DisplayString(TEXT("name"), psv2->sv2_name);
+ DisplayLanManVersion(
+ psv2->sv2_version_major,
+ psv2->sv2_version_minor);
+ DisplayServerType( psv2->sv2_type );
+ DisplayString(TEXT("comment"), psv2->sv2_comment);
+ DisplayDword(TEXT("ulist_mtime"), psv2->sv2_ulist_mtime);
+ DisplayDword(TEXT("glist_mtime"), psv2->sv2_glist_mtime);
+ DisplayDword(TEXT("alist_mtime"), psv2->sv2_alist_mtime);
+ DisplayDword(TEXT("users"), psv2->sv2_users);
+ DisplayDisconnectTime( psv2->sv2_disc);
+ DisplayString(TEXT("alerts"), psv2->sv2_alerts);
+ DisplayDword(TEXT("security"), psv2->sv2_security);
+ DisplayDword(TEXT("auditing"), psv2->sv2_auditing);
+ DisplayDword(TEXT("numadmin"), psv2->sv2_numadmin);
+ DisplayDword(TEXT("lanmask"), psv2->sv2_lanmask);
+ DisplayDword(TEXT("hidden"), psv2->sv2_hidden);
+ DisplayDword(TEXT("announce"), psv2->sv2_announce);
+ DisplayDword(TEXT("anndelta"), psv2->sv2_anndelta);
+ DisplayString(TEXT("guestacct"), psv2->sv2_guestacct);
+ DisplayLicenses(
+ psv2->sv2_version_major,
+ psv2->sv2_licenses );
+ DisplayString(TEXT("userpath"), psv2->sv2_userpath);
+ DisplayDword(TEXT("chdevs"), psv2->sv2_chdevs);
+ DisplayDword(TEXT("chdevq"), psv2->sv2_chdevq);
+ DisplayDword(TEXT("chdevjobs"), psv2->sv2_chdevjobs);
+ DisplayDword(TEXT("connections"), psv2->sv2_connections);
+ DisplayDword(TEXT("shares"), psv2->sv2_shares);
+ DisplayDword(TEXT("openfiles"), psv2->sv2_openfiles);
+ DisplayDword(TEXT("sessopens"), psv2->sv2_sessopens);
+ DisplayDword(TEXT("sessvcs"), psv2->sv2_sessvcs);
+ DisplayDword(TEXT("sessreqs"), psv2->sv2_sessreqs);
+ DisplayDword(TEXT("opensearch"), psv2->sv2_opensearch);
+ DisplayDword(TEXT("activelocks"), psv2->sv2_activelocks);
+ DisplayDword(TEXT("numreqbuf"), psv2->sv2_numreqbuf);
+ DisplayDword(TEXT("sizreqbuf"), psv2->sv2_sizreqbuf);
+ DisplayDword(TEXT("numbigbuf"), psv2->sv2_numbigbuf);
+ DisplayDword(TEXT("numfiletasks"), psv2->sv2_numfiletasks);
+ DisplayDword(TEXT("alertsched"), psv2->sv2_alertsched);
+ DisplayDword(TEXT("erroralert"), psv2->sv2_erroralert);
+ DisplayDword(TEXT("logonalert"), psv2->sv2_logonalert);
+ DisplayDword(TEXT("accessalert"), psv2->sv2_accessalert);
+ DisplayDword(TEXT("diskalert"), psv2->sv2_diskalert);
+ DisplayDword(TEXT("netioalert"), psv2->sv2_netioalert);
+ DisplayDword(TEXT("maxauditsz"), psv2->sv2_maxauditsz);
+ DisplayString(TEXT("srvheuristics"), psv2->sv2_srvheuristics);
+ }
+ break;
+
+ case 3 :
+ {
+ LPSERVER_INFO_3 psv3 = Info;
+ DisplayString(TEXT("name"), psv3->sv3_name);
+ DisplayLanManVersion(
+ psv3->sv3_version_major,
+ psv3->sv3_version_minor);
+ DisplayServerType( psv3->sv3_type );
+ DisplayString(TEXT("comment"), psv3->sv3_comment);
+ DisplayDword(TEXT("ulist_mtime"), psv3->sv3_ulist_mtime);
+ DisplayDword(TEXT("glist_mtime"), psv3->sv3_glist_mtime);
+ DisplayDword(TEXT("alist_mtime"), psv3->sv3_alist_mtime);
+ DisplayDword(TEXT("users"), psv3->sv3_users);
+ DisplayDisconnectTime( psv3->sv3_disc );
+ DisplayString(TEXT("alerts"), psv3->sv3_alerts);
+ DisplayDword(TEXT("security"), psv3->sv3_security);
+ DisplayDword(TEXT("auditing"), psv3->sv3_auditing);
+ DisplayDword(TEXT("numadmin"), psv3->sv3_numadmin);
+ DisplayDword(TEXT("lanmask"), psv3->sv3_lanmask);
+ DisplayDword(TEXT("hidden"), psv3->sv3_hidden);
+ DisplayDword(TEXT("announce"), psv3->sv3_announce);
+ DisplayDword(TEXT("anndelta"), psv3->sv3_anndelta);
+ DisplayString(TEXT("guestacct"), psv3->sv3_guestacct);
+ DisplayLicenses(
+ psv3->sv3_version_major,
+ psv3->sv3_licenses );
+ DisplayString(TEXT("userpath"), psv3->sv3_userpath);
+ DisplayDword(TEXT("chdevs"), psv3->sv3_chdevs);
+ DisplayDword(TEXT("chdevq"), psv3->sv3_chdevq);
+ DisplayDword(TEXT("chdevjobs"), psv3->sv3_chdevjobs);
+ DisplayDword(TEXT("connections"), psv3->sv3_connections);
+ DisplayDword(TEXT("shares"), psv3->sv3_shares);
+ DisplayDword(TEXT("openfiles"), psv3->sv3_openfiles);
+ DisplayDword(TEXT("sessopens"), psv3->sv3_sessopens);
+ DisplayDword(TEXT("sessvcs"), psv3->sv3_sessvcs);
+ DisplayDword(TEXT("sessreqs"), psv3->sv3_sessreqs);
+ DisplayDword(TEXT("opensearch"), psv3->sv3_opensearch);
+ DisplayDword(TEXT("activelocks"), psv3->sv3_activelocks);
+ DisplayDword(TEXT("numreqbuf"), psv3->sv3_numreqbuf);
+ DisplayDword(TEXT("sizreqbuf"), psv3->sv3_sizreqbuf);
+ DisplayDword(TEXT("numbigbuf"), psv3->sv3_numbigbuf);
+ DisplayDword(TEXT("numfiletasks"), psv3->sv3_numfiletasks);
+ DisplayDword(TEXT("alertsched"), psv3->sv3_alertsched);
+ DisplayDword(TEXT("erroralert"), psv3->sv3_erroralert);
+ DisplayDword(TEXT("logonalert"), psv3->sv3_logonalert);
+ DisplayDword(TEXT("accessalert"), psv3->sv3_accessalert);
+ DisplayDword(TEXT("diskalert"), psv3->sv3_diskalert);
+ DisplayDword(TEXT("netioalert"), psv3->sv3_netioalert);
+ DisplayDword(TEXT("maxauditsz"), psv3->sv3_maxauditsz);
+ DisplayString(TEXT("srvheuristics"), psv3->sv3_srvheuristics);
+ DisplayDword(TEXT("auditedevents"), psv3->sv3_auditedevents);
+ DisplayDword(TEXT("autoprofile"), psv3->sv3_autoprofile);
+ DisplayString(TEXT("autopath"), psv3->sv3_autopath);
+ }
+ break;
+
+ case 100 :
+ {
+ LPSERVER_INFO_100 psv100 = Info;
+ DisplayPlatformId( psv100->sv100_platform_id );
+ DisplayString(TEXT("Server Name"), psv100->sv100_name);
+ }
+ break;
+
+ case 101 :
+ {
+ LPSERVER_INFO_101 psv101 = Info;
+ DisplayPlatformId( psv101->sv101_platform_id );
+ DisplayString(TEXT("Server Name"), psv101->sv101_name);
+ DisplayLanManVersion(
+ psv101->sv101_version_major,
+ psv101->sv101_version_minor);
+ DisplayServerType( psv101->sv101_type );
+ DisplayString(TEXT( "Server Comment"), psv101->sv101_comment);
+ }
+ break;
+
+ case 102 :
+ {
+ LPSERVER_INFO_102 psv102 = Info;
+ DisplayPlatformId( psv102->sv102_platform_id );
+ DisplayString(TEXT("Server Name"), psv102->sv102_name);
+ DisplayLanManVersion(
+ psv102->sv102_version_major,
+ psv102->sv102_version_minor );
+ DisplayServerType( psv102->sv102_type );
+ DisplayString(TEXT( "Server Comment"), psv102->sv102_comment );
+ DisplayDword(TEXT( "users"), psv102->sv102_users );
+ DisplayBool(TEXT( "Server hidden"), psv102->sv102_hidden );
+ DisplayDword(TEXT( "announce"), psv102->sv102_announce );
+ DisplayDword(TEXT( "announce delta"), psv102->sv102_anndelta );
+ DisplayLicenses(
+ psv102->sv102_version_major,
+ psv102->sv102_licenses );
+ DisplayString(TEXT( "user path"), psv102->sv102_userpath );
+ }
+ break;
+
+ case 402 :
+ {
+ LPSERVER_INFO_402 psv402 = Info;
+ DisplayDword(TEXT("ulist mtime"), psv402->sv402_ulist_mtime);
+ DisplayDword(TEXT("glist mtime"), psv402->sv402_glist_mtime);
+ DisplayDword(TEXT("alist mtime"), psv402->sv402_alist_mtime);
+ DisplayString(TEXT("alerts"), psv402->sv402_alerts);
+ DisplayDword(TEXT("security"), psv402->sv402_security);
+ DisplayDword(TEXT("numadmin"), psv402->sv402_numadmin);
+ DisplayDwordHex(TEXT("lanmask"), psv402->sv402_lanmask);
+ DisplayString(TEXT("guestacct"), psv402->sv402_guestacct);
+ DisplayDword(TEXT("chdevs"), psv402->sv402_chdevs);
+ DisplayDword(TEXT("chdevq"), psv402->sv402_chdevq);
+ DisplayDword(TEXT("chdevjobs"), psv402->sv402_chdevjobs);
+ DisplayDword(TEXT("connections"), psv402->sv402_connections);
+ DisplayDword(TEXT("shares"), psv402->sv402_shares);
+ DisplayDword(TEXT("openfiles"), psv402->sv402_openfiles);
+ DisplayDword(TEXT("sessopens"), psv402->sv402_sessopens);
+ DisplayDword(TEXT("sessvcs"), psv402->sv402_sessvcs);
+ DisplayDword(TEXT("sessreqs"), psv402->sv402_sessreqs);
+ DisplayDword(TEXT("opensearch"), psv402->sv402_opensearch);
+ DisplayDword(TEXT("activelocks"), psv402->sv402_activelocks);
+ DisplayDword(TEXT("numreqbuf"), psv402->sv402_numreqbuf);
+ DisplayDword(TEXT("sizreqbuf"), psv402->sv402_sizreqbuf);
+ DisplayDword(TEXT("numbigbuf"), psv402->sv402_numbigbuf);
+ DisplayDword(TEXT("numfiletasks"), psv402->sv402_numfiletasks);
+ DisplayDword(TEXT("alertsched"), psv402->sv402_alertsched);
+ DisplayDword(TEXT("erroralert"), psv402->sv402_erroralert);
+ DisplayDword(TEXT("logonalert"), psv402->sv402_logonalert);
+ DisplayDword(TEXT("diskalert"), psv402->sv402_diskalert);
+ DisplayDword(TEXT("accessalert"), psv402->sv402_accessalert);
+ DisplayDword(TEXT("diskalert"), psv402->sv402_diskalert);
+ DisplayDword(TEXT("netioalert"), psv402->sv402_netioalert);
+ DisplayDword(TEXT("maxauditsz"), psv402->sv402_maxauditsz);
+ DisplayString(TEXT("srvheuristics"), psv402->sv402_srvheuristics);
+ }
+ break;
+
+ case 403 :
+ {
+ LPSERVER_INFO_403 psv403 = Info;
+ DisplayDword(TEXT("ulist mtime"), psv403->sv403_ulist_mtime);
+ DisplayDword(TEXT("glist mtime"), psv403->sv403_glist_mtime);
+ DisplayDword(TEXT("alist mtime"), psv403->sv403_alist_mtime);
+ DisplayString(TEXT("alerts"), psv403->sv403_alerts);
+ DisplayDword(TEXT("security"), psv403->sv403_security);
+ DisplayDword(TEXT("numadmin"), psv403->sv403_numadmin);
+ DisplayDwordHex(TEXT("lanmask"), psv403->sv403_lanmask);
+ DisplayString(TEXT("guestacct"), psv403->sv403_guestacct);
+ DisplayDword(TEXT("chdevs"), psv403->sv403_chdevs);
+ DisplayDword(TEXT("chdevq"), psv403->sv403_chdevq);
+ DisplayDword(TEXT("chdevjobs"), psv403->sv403_chdevjobs);
+ DisplayDword(TEXT("connections"), psv403->sv403_connections);
+ DisplayDword(TEXT("shares"), psv403->sv403_shares);
+ DisplayDword(TEXT("openfiles"), psv403->sv403_openfiles);
+ DisplayDword(TEXT("sessopens"), psv403->sv403_sessopens);
+ DisplayDword(TEXT("sessvcs"), psv403->sv403_sessvcs);
+ DisplayDword(TEXT("sessreqs"), psv403->sv403_sessreqs);
+ DisplayDword(TEXT("opensearch"), psv403->sv403_opensearch);
+ DisplayDword(TEXT("activelocks"), psv403->sv403_activelocks);
+ DisplayDword(TEXT("numreqbuf"), psv403->sv403_numreqbuf);
+ DisplayDword(TEXT("sizreqbuf"), psv403->sv403_sizreqbuf);
+ DisplayDword(TEXT("numbigbuf"), psv403->sv403_numbigbuf);
+ DisplayDword(TEXT("numfiletasks"), psv403->sv403_numfiletasks);
+ DisplayDword(TEXT("alertsched"), psv403->sv403_alertsched);
+ DisplayDword(TEXT("erroralert"), psv403->sv403_erroralert);
+ DisplayDword(TEXT("logonalert"), psv403->sv403_logonalert);
+ DisplayDword(TEXT("diskalert"), psv403->sv403_diskalert);
+ DisplayDword(TEXT("accessalert"), psv403->sv403_accessalert);
+ DisplayDword(TEXT("diskalert"), psv403->sv403_diskalert);
+ DisplayDword(TEXT("netioalert"), psv403->sv403_netioalert);
+ DisplayDword(TEXT("maxauditsz"), psv403->sv403_maxauditsz);
+ DisplayString(TEXT("srvheuristics"), psv403->sv403_srvheuristics);
+ DisplayDword(TEXT("auditedevents"), psv403->sv403_auditedevents);
+ DisplayDword(TEXT("autoprofile"), psv403->sv403_autoprofile);
+ DisplayString(TEXT("autopath"), psv403->sv403_autopath);
+ }
+ break;
+
+ // BUGBUG: RpcXlate doesn't need support for info levels 502, 503, 599.
+ // Feel free to add them here if you need them.
+
+ default :
+ NetpAssert(FALSE);
+ }
+
+} // DisplayServerInfo
+
diff --git a/private/net/svcdlls/browser2/client/makefile b/private/net/svcdlls/browser2/client/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/net/svcdlls/browser2/client/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/net/svcdlls/browser2/client/makefile.inc b/private/net/svcdlls/browser2/client/makefile.inc
new file mode 100644
index 000000000..81d19902b
--- /dev/null
+++ b/private/net/svcdlls/browser2/client/makefile.inc
@@ -0,0 +1,5 @@
+browstat.c: browdeb.c
+
+obj\$(TARGET_DIRECTORY)\browdeb.res: browdeb.rc
+
+obj\$(TARGET_DIRECTORY)\browstat.res: browstat.rc
diff --git a/private/net/svcdlls/browser2/client/sources b/private/net/svcdlls/browser2/client/sources
new file mode 100644
index 000000000..a7f4ebcd7
--- /dev/null
+++ b/private/net/svcdlls/browser2/client/sources
@@ -0,0 +1,78 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+
+Author:
+
+ Steve Wood (stevewo) 12-Apr-1989
+
+
+Revision History:
+
+!ENDIF
+
+DS_BUILD=1
+
+MAJORCOMP = net
+MINORCOMP = browclient
+
+
+
+TARGETNAME=bowser
+TARGETPATH=obj
+TARGETTYPE=LIBRARY
+
+
+INCLUDES=..;..\common;..\..\..\inc;..\..\..\..\inc
+
+
+!IFNDEF DISABLE_NET_UNICODE
+UNICODE=1
+NET_C_DEFINES=-DUNICODE
+!ENDIF
+
+SOURCES=browstub.c \
+ browbind.c \
+ bowser_c.c
+
+C_DEFINES=-DRPC_NO_WINDOWS_H
+
+MSC_WARNING_LEVEL=/W3 /WX
+
+USE_CRTDLL=1
+
+UMTYPE=console
+UMAPPL=browdeb*browstat
+UMRES=$(@R).res
+UMTEST=browlist
+
+UMLIBS=$(BASEDIR)\public\sdk\lib\*\netapi32.lib \
+ ..\common\obj\*\utils.obj \
+ ..\common\obj\*\interim.obj \
+ ..\server\obj\*\brwins.obj \
+! ifndef NTNOPCH
+ ..\server\obj\*\precomp.obj
+! endif
+
+UMLIBS = $(UMLIBS) \
+ $(BASEDIR)\public\sdk\lib\*\rpcutil.lib \
+ $(BASEDIR)\public\sdk\lib\*\ntdll.lib \
+ $(BASEDIR)\public\sdk\lib2\*\netlib.lib \
+ $(BASEDIR)\public\sdk\lib\*\advapi32.lib
+
+NTTARGETFILE0=browdeb.c \
+ obj\*\browdeb.res \
+ obj\*\browstat.res
+
diff --git a/private/net/svcdlls/browser2/common/interim.c b/private/net/svcdlls/browser2/common/interim.c
new file mode 100644
index 000000000..55dc959cc
--- /dev/null
+++ b/private/net/svcdlls/browser2/common/interim.c
@@ -0,0 +1,976 @@
+#include <nt.h> // DbgPrint prototype
+#include <ntrtl.h> // DbgPrint
+#include <nturtl.h> // Needed by winbase.h
+
+#include <windef.h> // DWORD
+#include <winbase.h> // LocalFree
+
+#include <rpcutil.h> // GENERIC_ENUM_STRUCT
+
+#include <lmcons.h> // NET_API_STATUS
+#include <lmerr.h> // NetError codes
+#include <lmremutl.h> // SUPPORTS_RPC
+
+#include <bowser.h> // generated by the MIDL complier
+#include <brnames.h> // Service and interface names
+
+#include <netlib.h>
+#include <netdebug.h>
+
+#include <winsvc.h>
+
+#include <lmserver.h>
+#include <tstr.h>
+
+#include <ntddbrow.h>
+#include <brcommon.h> // Routines common between client & server
+
+VOID
+UpdateInterimServerListElement(
+ IN PINTERIM_SERVER_LIST ServerList,
+ IN PINTERIM_ELEMENT Element,
+ IN ULONG Level,
+ IN BOOLEAN NewElement
+ );
+
+PINTERIM_ELEMENT
+AllocateInterimServerListEntry(
+ IN PSERVER_INFO_101 ServerInfo,
+ IN ULONG Level
+ );
+
+ NET_API_STATUS
+MergeServerList(
+ IN OUT PINTERIM_SERVER_LIST InterimServerList,
+ IN ULONG Level,
+ IN PVOID NewServerList,
+ IN ULONG NewEntriesRead,
+ IN ULONG NewTotalEntries
+ )
+/*++
+
+Routine Description:
+
+ This function will merge two server lists. It will reallocate the buffer
+ for the old list if appropriate.
+
+Arguments:
+
+ IN OUT PINTERIM_SERVER_LIST InterimServerList - Supplies an interim server list to merge into.
+
+ IN ULONG Level - Supplies the level of the list (100 or 101). Special
+ level 1010 is really level 101 with the special semantic that no
+ fields from this the NewServerList override existing fields in the
+ InterimServerList.
+
+ IN ULONG NewServerList - Supplies the list to merge into the interim list
+
+ IN ULONG NewEntriesRead - Supplies the entries read in the list.
+
+ IN ULONG NewTotalEntries - Supplies the total entries available in the list.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ ULONG i;
+ ULONG ServerElementSize;
+ PSERVER_INFO_101 ServerInfo = NewServerList;
+ PINTERIM_ELEMENT InterimEntry = NULL;
+ PLIST_ENTRY InterimList;
+ PINTERIM_ELEMENT NewElement = NULL;
+
+
+ if (Level == 100) {
+ ServerElementSize = sizeof(SERVER_INFO_100);
+ } else if ( Level == 101 || Level == 1010 ) {
+ ServerElementSize = sizeof(SERVER_INFO_101);
+ } else {
+ return(ERROR_INVALID_LEVEL);
+ }
+
+ //
+ // Early out if no entries in list.
+ //
+
+ if (NewEntriesRead == 0) {
+ return NERR_Success;
+ }
+
+ PrepareServerListForMerge(NewServerList, Level, NewEntriesRead);
+
+ InterimList = InterimServerList->ServerList.Flink;
+
+ //
+ // Walk the existing structure, packing it into an interim element, and
+ // sticking the element into the interim table.
+ //
+
+ for (i = 0; i < NewEntriesRead; i ++) {
+ BOOLEAN EntryInserted = FALSE;
+
+ //
+ // Walk forward in the interim element and find the appropriate place
+ // to insert this element.
+ //
+
+ while (InterimList != &InterimServerList->ServerList) {
+
+ LONG CompareResult;
+
+ InterimEntry = CONTAINING_RECORD(InterimList, INTERIM_ELEMENT, NextElement);
+
+// KdPrint(("MergeServerList: Compare %ws and %ws\n", NewElement->Name, InterimEntry->Name));
+
+#if DBG
+ //
+ // Make sure that this entry is lexically less than the next
+ // entry.
+ //
+
+ {
+ PLIST_ENTRY NextList = InterimList->Flink;
+ PINTERIM_ELEMENT NextEntry = CONTAINING_RECORD(NextList, INTERIM_ELEMENT, NextElement);
+
+ if (NextList != &InterimServerList->ServerList) {
+ ASSERT (STRCMP(InterimEntry->Name, NextEntry->Name) < 0);
+ }
+
+ //
+ // Now make sure that the input buffer also doesn't contain
+ // duplicate entries.
+ //
+
+ if (i < (NewEntriesRead-1)) {
+ PSERVER_INFO_101 NextServerInfo = (PSERVER_INFO_101)((PCHAR)ServerInfo+ServerElementSize);
+
+ ASSERT (STRCMP(ServerInfo->sv101_name, NextServerInfo->sv101_name) <= 0);
+ }
+
+ }
+#endif
+
+ CompareResult = STRCMP(ServerInfo->sv101_name, InterimEntry->Name);
+
+ if (CompareResult == 0) {
+
+// KdPrint(("MergeServerList: Elements equal - update\n"));
+
+ //
+ // If the new information should override the old information,
+ // copy it on top of the new info.
+ //
+ if ( Level != 1010 ) {
+ InterimEntry->PlatformId = ServerInfo->sv101_platform_id;
+
+ if (Level >= 101) {
+ InterimEntry->MajorVersionNumber = ServerInfo->sv101_version_major;
+
+ InterimEntry->MinorVersionNumber = ServerInfo->sv101_version_minor;
+
+ InterimEntry->Type = ServerInfo->sv101_type;
+
+ InterimServerList->TotalBytesNeeded -= STRLEN(InterimEntry->Comment) * sizeof(TCHAR) + sizeof(TCHAR);
+
+ STRCPY(InterimEntry->Comment, ServerInfo->sv101_comment);
+
+ InterimServerList->TotalBytesNeeded += STRLEN(InterimEntry->Comment) * sizeof(TCHAR) + sizeof(TCHAR);
+
+ }
+ }
+
+ UpdateInterimServerListElement(InterimServerList, InterimEntry, Level, FALSE);
+
+ EntryInserted = TRUE;
+
+ break;
+
+ } else if (CompareResult > 0) {
+
+// KdPrint(("MergeServerList: Elements greater. Skip element\n"));
+
+ InterimList = InterimList->Flink;
+
+ } else {
+
+ NewElement = AllocateInterimServerListEntry(ServerInfo, Level);
+
+ if (NewElement == NULL) {
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+// KdPrint(("MergeServerList: Elements less. Insert at end\n"));
+
+ //
+ // The new entry is < than the previous entry. Insert it
+ // before this entry.
+ //
+
+ InsertTailList(&InterimEntry->NextElement, &NewElement->NextElement);
+
+ //
+ // Skip to the next element in the list.
+ //
+
+ InterimList = &NewElement->NextElement;
+
+ UpdateInterimServerListElement(InterimServerList, NewElement, Level, TRUE);
+
+ EntryInserted = TRUE;
+
+ break;
+ }
+ }
+
+ if (!EntryInserted &&
+ (InterimList == &InterimServerList->ServerList)) {
+
+ NewElement = AllocateInterimServerListEntry(ServerInfo, Level);
+
+ if (NewElement == NULL) {
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+// KdPrint(("MergeServerList: Insert %ws at end of list\n", NewElement->Name));
+
+ InsertTailList(&InterimServerList->ServerList, &NewElement->NextElement);
+
+ InterimList = &NewElement->NextElement;
+
+ UpdateInterimServerListElement(InterimServerList, NewElement, Level, TRUE);
+
+ }
+
+ ServerInfo = (PSERVER_INFO_101)((PCHAR)ServerInfo+ServerElementSize);
+ }
+
+#if 0
+ {
+ PLIST_ENTRY InterimList;
+ ULONG TotalNeededForList = 0;
+
+ for (InterimList = InterimServerList->ServerList.Flink ;
+ InterimList != &InterimServerList->ServerList ;
+ InterimList = InterimList->Flink ) {
+ ULONG ServerElementSize;
+
+ InterimEntry = CONTAINING_RECORD(InterimList, INTERIM_ELEMENT, NextElement);
+
+ if (Level == 100) {
+ ServerElementSize = sizeof(SERVER_INFO_100);
+ } else {
+ ServerElementSize = sizeof(SERVER_INFO_101);
+ }
+
+ ServerElementSize += STRLEN(InterimEntry->Name)*sizeof(TCHAR)+sizeof(TCHAR);
+
+ ServerElementSize += STRLEN(InterimEntry->Comment)*sizeof(TCHAR)+sizeof(TCHAR);
+
+// KdPrint(("MergeInterimServerList: %ws. %ld needed\n", InterimEntry->Name, ServerElementSize));
+
+ TotalNeededForList += ServerElementSize;
+ }
+
+ if (TotalNeededForList != InterimServerList->TotalBytesNeeded) {
+ KdPrint(("UpdateInterimServerList: Wrong number of bytes (%ld) for interim server list. %ld needed\n", InterimServerList->TotalBytesNeeded, TotalNeededForList));
+ }
+ }
+
+#endif
+// KdPrint(("%lx bytes needed to hold server list\n", InterimServerList->TotalBytesNeeded));
+
+ //
+ // Also, we had better have the whole table locally.
+ //
+
+ ASSERT (InterimServerList->EntriesRead == InterimServerList->TotalEntries);
+
+ return(NERR_Success);
+}
+
+ULONG
+_CRTAPI1
+CompareServerInfo(
+ const void * Param1,
+ const void * Param2
+ )
+{
+ const SERVER_INFO_100 * ServerInfo1 = Param1;
+ const SERVER_INFO_100 * ServerInfo2 = Param2;
+
+ return STRCMP(ServerInfo1->sv100_name, ServerInfo2->sv100_name);
+}
+
+VOID
+PrepareServerListForMerge(
+ IN PVOID ServerInfoList,
+ IN ULONG Level,
+ IN ULONG EntriesInList
+ )
+/*++
+
+Routine Description:
+
+ MergeServerList requires that the inputs to the list be in a strictly
+ sorted order. This routine guarantees that this list will be of
+ an "appropriate" form to be merged.
+
+Arguments:
+
+ IN PVOID ServerInfoList - Supplies the list to munge.
+
+ IN ULONG Level - Supplies the level of the list (100 or 101).
+ (Or level 1010 which is identical to level 101.)
+
+ IN ULONG EntriesInList - Supplies the number of entries in the list.
+
+Return Value:
+
+ None.
+
+Note:
+ In 99% of the cases, the list passed in will already be sorted. We want to
+ take the input list and first check to see if it is sorted. If it is,
+ we can return immediately. If it is not, we need to sort the list.
+
+ We don't just unilaterally sort the list, because the input is mostly
+ sorted anyway, and there are no good sorting algorithms that handle mostly
+ sorted inputs. Since we will see unsorted input only rarely (basically,
+ we will only see it from WfW machines), we just take the penalty of a worst
+ case quicksort if the input is unsorted.
+
+--*/
+
+{
+ LONG i;
+ ULONG ServerElementSize;
+ PSERVER_INFO_101 ServerInfo = ServerInfoList;
+ BOOLEAN MisOrderedList = FALSE;
+
+ ASSERT (Level == 100 || Level == 101 || Level == 1010);
+
+ //
+ // Figure out the size of each element.
+ //
+
+ if (Level == 100) {
+ ServerElementSize = sizeof(SERVER_INFO_100);
+ } else {
+ ServerElementSize = sizeof(SERVER_INFO_101);
+ }
+
+ //
+ // Next check to see if the input list is sorted.
+ //
+
+ for (i = 0 ; i < ((LONG)EntriesInList - 1) ; i += 1 ) {
+ PSERVER_INFO_101 NextServerInfo = (PSERVER_INFO_101)((PCHAR)ServerInfo+ServerElementSize);
+
+ if (STRCMP(ServerInfo->sv101_name, NextServerInfo->sv101_name) >= 0) {
+ MisOrderedList = TRUE;
+ break;
+ }
+
+ ServerInfo = NextServerInfo;
+ }
+
+ //
+ // This list is sorted. Return right away, it's fine.
+ //
+
+ if (!MisOrderedList) {
+ return;
+ }
+
+ //
+ // This list isn't sorted. We need to sort it.
+ //
+
+ qsort(ServerInfoList, EntriesInList, ServerElementSize, CompareServerInfo);
+
+
+}
+
+ PINTERIM_ELEMENT
+AllocateInterimServerListEntry(
+ IN PSERVER_INFO_101 ServerInfo,
+ IN ULONG Level
+ )
+{
+ PINTERIM_ELEMENT NewElement;
+
+ NewElement = MIDL_user_allocate(sizeof(INTERIM_ELEMENT));
+
+ if (NewElement == NULL) {
+ return NULL;
+ }
+
+ //
+ // Initialize TimeLastSeen and Periodicity.
+ //
+
+ NewElement->TimeLastSeen = 0;
+
+ NewElement->Periodicity = 0;
+
+ NewElement->PlatformId = ServerInfo->sv101_platform_id;
+
+ ASSERT (STRLEN(ServerInfo->sv101_name) <= CNLEN);
+
+ STRCPY(NewElement->Name, ServerInfo->sv101_name);
+
+ if (Level == 100) {
+ NewElement->MajorVersionNumber = 0;
+ NewElement->MinorVersionNumber = 0;
+ *NewElement->Comment = L'\0';
+ NewElement->Type = SV_TYPE_ALL;
+ } else {
+ NewElement->MajorVersionNumber = ServerInfo->sv101_version_major;
+
+ NewElement->MinorVersionNumber = ServerInfo->sv101_version_minor;
+
+ NewElement->Type = ServerInfo->sv101_type;
+
+ ASSERT (STRLEN(ServerInfo->sv101_comment) <= MAXCOMMENTSZ);
+
+ STRCPY(NewElement->Comment, ServerInfo->sv101_comment);
+
+ }
+
+ return NewElement;
+}
+
+
+VOID
+UpdateInterimServerListElement(
+ IN PINTERIM_SERVER_LIST InterimServerList,
+ IN PINTERIM_ELEMENT InterimElement,
+ IN ULONG Level,
+ IN BOOLEAN NewElement
+ )
+{
+#if 0
+ PINTERIM_ELEMENT InterimEntry;
+ ULONG TotalNeededForList = 0;
+#endif
+
+ //
+ // If this is a new element, update the size of the table to match.
+ //
+
+ if (NewElement) {
+ ULONG ServerElementSize;
+
+ if (Level == 100) {
+ ServerElementSize = sizeof(SERVER_INFO_100);
+ } else {
+ ServerElementSize = sizeof(SERVER_INFO_101);
+ }
+
+ InterimServerList->EntriesRead += 1;
+
+ ServerElementSize += STRLEN(InterimElement->Name)*sizeof(TCHAR)+sizeof(TCHAR);
+
+ if (Level == 100) {
+ ServerElementSize += sizeof(TCHAR);
+ } else {
+ ServerElementSize += STRLEN(InterimElement->Comment)*sizeof(TCHAR)+sizeof(TCHAR);
+ }
+
+ InterimServerList->TotalBytesNeeded += ServerElementSize;
+
+ InterimServerList->TotalEntries += 1;
+
+ if (InterimServerList->NewElementCallback != NULL) {
+ InterimServerList->NewElementCallback(InterimServerList,
+ InterimElement);
+ } else {
+ InterimElement->Periodicity = 0xffffffff;
+ InterimElement->TimeLastSeen = 0xffffffff;
+ }
+
+
+ } else {
+ if (InterimServerList->ExistingElementCallback != NULL) {
+ InterimServerList->ExistingElementCallback(InterimServerList,
+ InterimElement);
+ } else {
+ InterimElement->Periodicity = 0xffffffff;
+ InterimElement->TimeLastSeen = 0xffffffff;
+ }
+
+ }
+
+#if 0
+ {
+ PLIST_ENTRY InterimList;
+ ULONG TotalNeededForList = 0;
+
+ for (InterimList = InterimServerList->ServerList.Flink ;
+ InterimList != &InterimServerList->ServerList ;
+ InterimList = InterimList->Flink ) {
+ ULONG ServerElementSize;
+
+ InterimEntry = CONTAINING_RECORD(InterimList, INTERIM_ELEMENT, NextElement);
+
+ if (Level == 100) {
+ ServerElementSize = sizeof(SERVER_INFO_100);
+ } else {
+ ServerElementSize = sizeof(SERVER_INFO_101);
+ }
+
+ ServerElementSize += STRLEN(InterimEntry->Name)*sizeof(TCHAR)+sizeof(TCHAR);
+
+ ServerElementSize += STRLEN(InterimEntry->Comment)*sizeof(TCHAR)+sizeof(TCHAR);
+
+ TotalNeededForList += ServerElementSize;
+ }
+
+ if (TotalNeededForList != InterimServerList->TotalBytesNeeded) {
+ KdPrint(("UpdateInterimServerList: Wrong number of bytes (%ld) for interim server list. %ld needed\n", InterimServerList->TotalBytesNeeded, TotalNeededForList));
+ }
+ }
+
+#endif
+
+ return;
+
+}
+
+ NET_API_STATUS
+PackServerList(
+ IN PINTERIM_SERVER_LIST InterimServerList,
+ IN ULONG Level,
+ IN ULONG ServerType,
+ IN ULONG PreferedMaximumLength,
+ OUT PVOID *ServerList,
+ OUT PULONG EntriesRead,
+ OUT PULONG TotalEntries,
+ IN LPCWSTR FirstNameToReturn
+ )
+/*++
+
+Routine Description:
+
+ This function will take an interim server list and "pack" it into an array
+ of server_info_xxx structures.
+
+Arguments:
+
+ IN PINTERIM_SERVER_LIST InterimServerList - Supplies an interim server list to merge into.
+
+ IN ULONG Level - Supplies the level of the list (100 or 101).
+
+ IN ULONG ServerType - Supplies the type to filter on the list.
+
+ IN ULONG PreferedMaximumLength - Supplies the prefered size of the list.
+
+ OUT PVOID *ServerList - Where to put the destination list.
+
+ OUT PULONG EntriesEntries - Receives the entries packed in the list.
+
+ OUT PULONG TotalEntries - Receives the total entries available in the list.
+
+ FirstNameToReturn - Supplies the name of the first domain or server entry to return.
+ The caller can use this parameter to implement a resume handle of sorts by passing
+ the name of the last entry returned on a previous call. (Notice that the specified
+ entry will, also, be returned on this call unless it has since been deleted.)
+ Pass NULL to start with the first entry available.
+
+ Passed name must be the canonical form of the name.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ ULONG EntriesPacked = 0;
+ PLIST_ENTRY InterimList;
+ PSERVER_INFO_101 ServerEntry;
+ ULONG EntrySize = 0;
+ LPWSTR BufferEnd;
+ BOOLEAN ReturnWholeList = FALSE;
+ BOOLEAN TrimmingNames;
+ BOOLEAN BufferFull = FALSE;
+
+ if (Level == 100) {
+ EntrySize = sizeof(SERVER_INFO_100);
+ } else if (Level == 101) {
+ EntrySize = sizeof(SERVER_INFO_101);
+ } else {
+ return(ERROR_INVALID_LEVEL);
+ }
+
+ //
+ // Set the entries read based on the information we collected before.
+ //
+
+ *TotalEntries = 0;
+
+ if (PreferedMaximumLength == 0xffffffff) {
+ *ServerList = MIDL_user_allocate(InterimServerList->TotalBytesNeeded);
+
+ BufferEnd = (LPWSTR)((ULONG)(*ServerList)+InterimServerList->TotalBytesNeeded);
+
+ } else {
+ *ServerList = MIDL_user_allocate(PreferedMaximumLength);
+
+ BufferEnd = (LPWSTR)((ULONG)(*ServerList)+PreferedMaximumLength);
+ }
+
+ if (ServerType == SV_TYPE_ALL || ServerType == SV_TYPE_DOMAIN_ENUM) {
+ ReturnWholeList = TRUE;
+ }
+
+ if ( *ServerList == NULL ) {
+ return(ERROR_NOT_ENOUGH_MEMORY);
+
+ }
+
+ TrimmingNames = (FirstNameToReturn != NULL && *FirstNameToReturn != L'\0');
+ ServerEntry = *ServerList;
+
+ for (InterimList = InterimServerList->ServerList.Flink ;
+ InterimList != &InterimServerList->ServerList ;
+ InterimList = InterimList->Flink ) {
+
+ PINTERIM_ELEMENT InterimEntry = CONTAINING_RECORD(InterimList, INTERIM_ELEMENT, NextElement);
+
+#if DBG
+ //
+ // Make sure that this entry is lexically less than the next
+ // entry.
+ //
+
+ {
+ PLIST_ENTRY NextList = InterimList->Flink;
+ PINTERIM_ELEMENT NextEntry = CONTAINING_RECORD(NextList, INTERIM_ELEMENT, NextElement);
+
+ if (NextList != &InterimServerList->ServerList) {
+ ASSERT (STRCMP(InterimEntry->Name, NextEntry->Name) < 0);
+ }
+
+ }
+#endif
+
+ //
+ // Trim the first several names from the list.
+ //
+
+ if ( TrimmingNames ) {
+ if ( STRCMP( InterimEntry->Name, FirstNameToReturn ) < 0 ) {
+ continue;
+ }
+ TrimmingNames = FALSE;
+ }
+
+ //
+ // If the server's type matches the type filter, pack it into the buffer.
+ //
+
+ if (InterimEntry->Type & ServerType) {
+
+ (*TotalEntries) += 1;
+
+ //
+ // If this entry will fit into the buffer, pack it in.
+ //
+ // Please note that we only count an entry if the entire entry
+ // (server and comment) fits in the buffer. This is NOT
+ // strictly Lan Manager compatible.
+ //
+
+ if ( !BufferFull &&
+ ((ULONG)ServerEntry+EntrySize <= (ULONG)BufferEnd)) {
+
+ ServerEntry->sv101_platform_id = InterimEntry->PlatformId;
+
+ ServerEntry->sv101_name = InterimEntry->Name;
+
+ if (NetpPackString(&ServerEntry->sv101_name,
+ (LPBYTE)((PCHAR)ServerEntry)+EntrySize,
+ &BufferEnd)) {
+
+ if (Level == 101) {
+
+ ServerEntry->sv101_version_major = InterimEntry->MajorVersionNumber;;
+
+ ServerEntry->sv101_version_minor = InterimEntry->MinorVersionNumber;;
+
+ ServerEntry->sv101_type = InterimEntry->Type;
+
+ ServerEntry->sv101_comment = InterimEntry->Comment;
+
+ if (NetpPackString(&ServerEntry->sv101_comment,
+ (LPBYTE)((PCHAR)ServerEntry)+EntrySize,
+ &BufferEnd)) {
+ EntriesPacked += 1;
+ } else {
+ BufferFull = TRUE;
+ }
+ } else {
+ EntriesPacked += 1;
+ }
+#if DBG
+ {
+ PSERVER_INFO_101 PreviousServerInfo = (PSERVER_INFO_101)((PCHAR)ServerEntry-EntrySize);
+ if (PreviousServerInfo >= (PSERVER_INFO_101)*ServerList) {
+ ASSERT (STRCMP(ServerEntry->sv101_name, PreviousServerInfo->sv101_name) > 0);
+ }
+
+ }
+#endif
+ } else {
+ BufferFull = TRUE;
+ }
+
+ } else {
+
+ //
+ // If we're returning the entire list and we have exceeded
+ // the amount that fits in the list, we can early out
+ // now.
+ //
+
+ if (ReturnWholeList) {
+
+ *TotalEntries = InterimServerList->TotalEntries;
+
+ break;
+ }
+
+ BufferFull = TRUE;
+ }
+
+ //
+ // Step to the next server entry.
+ //
+
+ ServerEntry = (PSERVER_INFO_101)((PCHAR)ServerEntry+EntrySize);
+ }
+ }
+
+ ASSERT (InterimServerList->EntriesRead >= EntriesPacked);
+
+ *EntriesRead = EntriesPacked;
+
+ if (EntriesPacked != *TotalEntries) {
+ return ERROR_MORE_DATA;
+ } else {
+ return NERR_Success;
+ }
+
+}
+
+
+ NET_API_STATUS
+InitializeInterimServerList(
+ IN PINTERIM_SERVER_LIST InterimServerList,
+ IN PINTERIM_NEW_CALLBACK NewCallback,
+ IN PINTERIM_EXISTING_CALLBACK ExistingCallback,
+ IN PINTERIM_DELETE_CALLBACK DeleteElementCallback,
+ IN PINTERIM_AGE_CALLBACK AgeElementCallback
+ )
+{
+
+ InitializeListHead(&InterimServerList->ServerList);
+
+ InterimServerList->TotalBytesNeeded = 0;
+ InterimServerList->TotalEntries = 0;
+ InterimServerList->EntriesRead = 0;
+
+ InterimServerList->NewElementCallback = NewCallback;
+ InterimServerList->ExistingElementCallback = ExistingCallback;
+ InterimServerList->DeleteElementCallback = DeleteElementCallback;
+ InterimServerList->AgeElementCallback = AgeElementCallback;
+ return(NERR_Success);
+}
+
+ NET_API_STATUS
+UninitializeInterimServerList(
+ IN PINTERIM_SERVER_LIST InterimServerList
+ )
+{
+ PINTERIM_ELEMENT InterimElement;
+
+
+// KdPrint(("BROWSER: Uninitialize Interim Server List %lx\n", InterimServerList));
+
+ //
+ // Enumerate the elements in the table, deleting them as we go.
+ //
+
+ while (!IsListEmpty(&InterimServerList->ServerList)) {
+ PLIST_ENTRY Entry;
+
+ Entry = RemoveHeadList(&InterimServerList->ServerList);
+
+ InterimElement = CONTAINING_RECORD(Entry, INTERIM_ELEMENT, NextElement);
+
+ if (InterimServerList->DeleteElementCallback != NULL) {
+ InterimServerList->DeleteElementCallback(InterimServerList, InterimElement);
+ }
+
+ //
+ // There is one less element in the list.
+ //
+
+ InterimServerList->EntriesRead -= 1;
+
+ InterimServerList->TotalEntries -= 1;
+
+ MIDL_user_free(InterimElement);
+ }
+
+ ASSERT (InterimServerList->EntriesRead == 0);
+
+ return(NERR_Success);
+}
+
+ULONG
+NumberInterimServerListElements(
+ IN PINTERIM_SERVER_LIST InterimServerList
+ )
+{
+ PLIST_ENTRY InterimList;
+ ULONG NumberOfEntries = 0;
+
+ for (InterimList = InterimServerList->ServerList.Flink ;
+ InterimList != &InterimServerList->ServerList ;
+ InterimList = InterimList->Flink ) {
+ NumberOfEntries += 1;
+
+ }
+
+ return NumberOfEntries;
+}
+
+ NET_API_STATUS
+AgeInterimServerList(
+ IN PINTERIM_SERVER_LIST InterimServerList
+ )
+{
+ PLIST_ENTRY InterimList, NextElement;
+ PINTERIM_ELEMENT InterimElement;
+
+ if (InterimServerList->AgeElementCallback != NULL) {
+
+ //
+ // Enumerate the elements in the table, aging them as we go.
+ //
+
+
+ for (InterimList = InterimServerList->ServerList.Flink ;
+ InterimList != &InterimServerList->ServerList ;
+ InterimList = NextElement) {
+ InterimElement = CONTAINING_RECORD(InterimList, INTERIM_ELEMENT, NextElement);
+
+ //
+ // Call into the aging routine and if this entry is too old,
+ // remove it from the interim list.
+ //
+
+ if (InterimServerList->AgeElementCallback(InterimServerList, InterimElement)) {
+ ULONG ElementSize = sizeof(SERVER_INFO_101) + ((STRLEN(InterimElement->Comment) + 1) * sizeof(WCHAR)) + ((STRLEN(InterimElement->Name) + 1) * sizeof(WCHAR));
+
+ ASSERT (ElementSize <= InterimServerList->TotalBytesNeeded);
+
+ NextElement = InterimList->Flink;
+
+ //
+ // Remove this entry from the list.
+ //
+
+ RemoveEntryList(&InterimElement->NextElement);
+
+ if (InterimServerList->DeleteElementCallback != NULL) {
+ InterimServerList->DeleteElementCallback(InterimServerList, InterimElement);
+ }
+
+ //
+ // There is one less element in the list.
+ //
+
+ InterimServerList->EntriesRead -= 1;
+
+ InterimServerList->TotalEntries -= 1;
+
+ //
+ // Since this element isn't in the table any more, we don't
+ // need to allocate memory for it.
+ //
+
+ InterimServerList->TotalBytesNeeded -= ElementSize;
+
+ MIDL_user_free(InterimElement);
+
+ } else {
+ NextElement = InterimList->Flink;
+ }
+ }
+#if 0
+ {
+ PINTERIM_ELEMENT InterimEntry;
+ ULONG TotalNeededForList = 0;
+
+ for (InterimList = InterimServerList->ServerList.Flink ;
+ InterimList != &InterimServerList->ServerList ;
+ InterimList = InterimList->Flink ) {
+ ULONG ServerElementSize;
+
+ InterimEntry = CONTAINING_RECORD(InterimList, INTERIM_ELEMENT, NextElement);
+
+ ServerElementSize = sizeof(SERVER_INFO_101);
+
+ ServerElementSize += STRLEN(InterimEntry->Name)*sizeof(TCHAR)+sizeof(TCHAR);
+
+ ServerElementSize += STRLEN(InterimEntry->Comment)*sizeof(TCHAR)+sizeof(TCHAR);
+
+ TotalNeededForList += ServerElementSize;
+ }
+
+ if (TotalNeededForList != InterimServerList->TotalBytesNeeded) {
+ KdPrint(("AgeInterimServerList: Too few bytes (%ld) for interim server list. %ld needed\n", InterimServerList->TotalBytesNeeded, TotalNeededForList));
+ }
+ }
+#endif
+
+ }
+
+ return(NERR_Success);
+}
+
+ PINTERIM_ELEMENT
+LookupInterimServerList(
+ IN PINTERIM_SERVER_LIST InterimServerList,
+ IN LPWSTR ServerNameToLookUp
+ )
+{
+ PLIST_ENTRY InterimList;
+
+ for (InterimList = InterimServerList->ServerList.Flink ;
+ InterimList != &InterimServerList->ServerList ;
+ InterimList = InterimList->Flink ) {
+
+ PINTERIM_ELEMENT InterimEntry = CONTAINING_RECORD(InterimList, INTERIM_ELEMENT, NextElement);
+ LONG CompareResult;
+
+ if ((CompareResult = STRICMP(InterimEntry->Name, ServerNameToLookUp) == 0)) {
+ return InterimEntry;
+ }
+
+ //
+ // If we went past this guy, return an error.
+ //
+
+ if (CompareResult > 0) {
+ return NULL;
+ }
+
+ }
+
+ return NULL;
+}
+
+
diff --git a/private/net/svcdlls/browser2/common/makefile b/private/net/svcdlls/browser2/common/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/net/svcdlls/browser2/common/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/net/svcdlls/browser2/common/sources b/private/net/svcdlls/browser2/common/sources
new file mode 100644
index 000000000..8758c1017
--- /dev/null
+++ b/private/net/svcdlls/browser2/common/sources
@@ -0,0 +1,53 @@
+
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+
+Author:
+
+ Steve Wood (stevewo) 12-Apr-1989
+
+
+Revision History:
+
+!ENDIF
+
+MAJORCOMP = net
+MINORCOMP = browclient
+
+
+NTPROFILEINPUT=YES
+
+TARGETNAME=brcommon
+TARGETPATH=obj
+TARGETTYPE=LIBRARY
+
+
+INCLUDES=..;..\..\..\inc;..\..\..\..\inc
+
+
+NTPROFILEINPUT=1
+
+!IFNDEF DISABLE_NET_UNICODE
+UNICODE=1
+NET_C_DEFINES=-DUNICODE
+!ENDIF
+
+SOURCES=interim.c \
+ utils.c
+
+C_DEFINES=-DRPC_NO_WINDOWS_H
+
+MSC_WARNING_LEVEL=/W3 /WX
diff --git a/private/net/svcdlls/browser2/common/utils.c b/private/net/svcdlls/browser2/common/utils.c
new file mode 100644
index 000000000..29bd82b20
--- /dev/null
+++ b/private/net/svcdlls/browser2/common/utils.c
@@ -0,0 +1,957 @@
+/*++
+
+Copyright (c) 1991-1992 Microsoft Corporation
+
+Module Name:
+
+ utils.c
+
+Abstract:
+
+ Utility routines for the browser service.
+
+Author:
+
+ Larry Osterman (LarryO) 23-Mar-1992
+
+Environment:
+
+ User Mode - Win32
+
+Revision History:
+
+--*/
+
+#undef IF_DEBUG // avoid wsclient.h vs. debuglib.h conflicts.
+#include <nt.h> // DbgPrint prototype
+#include <ntrtl.h> // DbgPrint
+#include <nturtl.h> // Needed by winbase.h
+
+#include <windef.h> // DWORD
+#include <winbase.h> // LocalFree
+#include <winreg.h>
+
+#include <rpc.h> // DataTypes and runtime APIs
+#include <rpcutil.h> // GENERIC_ENUM_STRUCT
+
+#include <lmcons.h> // NET_API_STATUS
+#include <lmerr.h> // NetError codes
+#include <lmremutl.h> // SUPPORTS_RPC
+
+#include <netlib.h> // NetpNtStatusToApiStatus
+#include <netlibnt.h> // NetpNtStatusToApiStatus
+#include <netdebug.h>
+
+#include <bowser.h> // generated by the MIDL complier
+#include <brnames.h> // Service and interface names
+
+#include <winsvc.h>
+
+#include <debuglib.h> // IF_DEBUG() (needed by netrpc.h).
+#include <lmserver.h>
+#include <align.h>
+#include <tstr.h>
+
+#include <ntddbrow.h>
+#include <brcommon.h> // Routines common between client & server
+
+#include <nb30.h>
+#include <hostannc.h>
+
+#include <winnls.h>
+
+//
+// Buffer allocation size for enumeration output buffer.
+//
+#define INITIAL_ALLOCATION_SIZE 48*1024 // First attempt size (48K)
+#define FUDGE_FACTOR_SIZE 1024 // Second try TotalBytesNeeded
+ // plus this amount
+
+
+
+
+
+
+
+
+ NET_API_STATUS
+BrDgReceiverIoControl(
+ IN HANDLE FileHandle,
+ IN ULONG DgReceiverControlCode,
+ IN PLMDR_REQUEST_PACKET Drp,
+ IN ULONG DrpSize,
+ IN PVOID SecondBuffer OPTIONAL,
+ IN ULONG SecondBufferLength,
+ OUT PULONG Information OPTIONAL
+ )
+/*++
+
+Routine Description:
+
+Arguments:
+
+ FileHandle - Supplies a handle to the file or device on which the service
+ is being performed.
+
+ DgReceiverControlCode - Supplies the NtDeviceIoControlFile function code
+ given to the datagram receiver.
+
+ Drp - Supplies the datagram receiver request packet.
+
+ DrpSize - Supplies the length of the datagram receiver request packet.
+
+ SecondBuffer - Supplies the second buffer in call to NtDeviceIoControlFile.
+
+ SecondBufferLength - Supplies the length of the second buffer.
+
+ Information - Returns the information field of the I/O status block.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+
+{
+ NTSTATUS ntstatus;
+ IO_STATUS_BLOCK IoStatusBlock;
+ PLMDR_REQUEST_PACKET RealDrp;
+ HANDLE CompletionEvent;
+ LPBYTE Where;
+
+ if (FileHandle == NULL) {
+ return ERROR_NOT_SUPPORTED;
+ }
+
+ //
+ // Allocate a copy of the request packet where we can put the transport and
+ // emulated domain name in the packet itself.
+ //
+ RealDrp = MIDL_user_allocate(DrpSize+
+ Drp->TransportName.Length+sizeof(WCHAR)+
+ Drp->EmulatedDomainName.Length+sizeof(WCHAR) );
+
+ if (RealDrp == NULL) {
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ //
+ // Copy the request packet into the local copy.
+ //
+ RtlCopyMemory(RealDrp, Drp, DrpSize);
+
+ Where = (LPBYTE)RealDrp+DrpSize;
+ if (Drp->TransportName.Length != 0) {
+ RealDrp->TransportName.Buffer = (LPWSTR)Where;
+ RealDrp->TransportName.MaximumLength = Drp->TransportName.Length+sizeof(WCHAR);
+ RtlCopyUnicodeString(&RealDrp->TransportName, &Drp->TransportName);
+ Where += RealDrp->TransportName.MaximumLength;
+ }
+
+ if (Drp->EmulatedDomainName.Length != 0) {
+ RealDrp->EmulatedDomainName.Buffer = (LPWSTR)Where;
+ RealDrp->EmulatedDomainName.MaximumLength = Drp->EmulatedDomainName.Length+sizeof(WCHAR);
+ RtlCopyUnicodeString(&RealDrp->EmulatedDomainName, &Drp->EmulatedDomainName);
+ Where += RealDrp->EmulatedDomainName.MaximumLength;
+ }
+
+
+
+ //
+ // Create a completion event
+ //
+ CompletionEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+
+ if (CompletionEvent == NULL) {
+
+ MIDL_user_free(RealDrp);
+
+ return(GetLastError());
+ }
+
+ //
+ // Send the request to the Datagram Receiver DD.
+ //
+
+ ntstatus = NtDeviceIoControlFile(
+ FileHandle,
+ CompletionEvent,
+ NULL,
+ NULL,
+ &IoStatusBlock,
+ DgReceiverControlCode,
+ RealDrp,
+ Where-(LPBYTE)RealDrp,
+ SecondBuffer,
+ SecondBufferLength
+ );
+
+ if (NT_SUCCESS(ntstatus)) {
+
+ //
+ // If pending was returned, then wait until the request completes.
+ //
+
+ if (ntstatus == STATUS_PENDING) {
+
+ do {
+ ntstatus = WaitForSingleObjectEx(CompletionEvent, 0xffffffff, TRUE);
+ } while ( ntstatus == WAIT_IO_COMPLETION );
+ }
+
+
+ if (NT_SUCCESS(ntstatus)) {
+ ntstatus = IoStatusBlock.Status;
+ }
+ }
+
+ if (ARGUMENT_PRESENT(Information)) {
+ *Information = IoStatusBlock.Information;
+ }
+
+ MIDL_user_free(RealDrp);
+
+ CloseHandle(CompletionEvent);
+
+ return NetpNtStatusToApiStatus(ntstatus);
+}
+
+ NET_API_STATUS
+DeviceControlGetInfo(
+ IN HANDLE FileHandle,
+ IN ULONG DeviceControlCode,
+ IN PVOID RequestPacket,
+ IN ULONG RequestPacketLength,
+ OUT LPVOID *OutputBuffer,
+ IN ULONG PreferedMaximumLength,
+ IN ULONG BufferHintSize,
+ OUT PULONG Information OPTIONAL
+ )
+/*++
+
+Routine Description:
+
+ This function allocates the buffer and fill it with the information
+ that is retrieved from the datagram receiver.
+
+Arguments:
+
+ DeviceDriverType - Supplies the value which indicates whether to call
+ the datagram receiver.
+
+ FileHandle - Supplies a handle to the file or device of which to get
+ information about.
+
+ DeviceControlCode - Supplies the NtFsControlFile or NtIoDeviceControlFile
+ function control code.
+
+ RequestPacket - Supplies a pointer to the device request packet.
+
+ RrequestPacketLength - Supplies the length of the device request packet.
+
+ OutputBuffer - Returns a pointer to the buffer allocated by this routine
+ which contains the use information requested. This pointer is set to
+ NULL if return code is not NERR_Success.
+
+ PreferedMaximumLength - Supplies the number of bytes of information to
+ return in the buffer. If this value is MAXULONG, we will try to
+ return all available information if there is enough memory resource.
+
+ BufferHintSize - Supplies the hint size of the output buffer so that the
+ memory allocated for the initial buffer will most likely be large
+ enough to hold all requested data.
+
+ Information - Returns the information code from the NtFsControlFile or
+ NtIoDeviceControlFile call.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NET_API_STATUS status;
+ NTSTATUS ntstatus;
+ DWORD OutputBufferLength;
+ DWORD TotalBytesNeeded = 1;
+ ULONG OriginalResumeKey;
+ IO_STATUS_BLOCK IoStatusBlock;
+ PLMDR_REQUEST_PACKET Drrp = (PLMDR_REQUEST_PACKET) RequestPacket;
+ HANDLE CompletionEvent;
+
+ OriginalResumeKey = Drrp->Parameters.EnumerateNames.ResumeHandle;
+
+ //
+ // If PreferedMaximumLength is MAXULONG, then we are supposed to get all
+ // the information, regardless of size. Allocate the output buffer of a
+ // reasonable size and try to use it. If this fails, the Redirector FSD
+ // will say how much we need to allocate.
+ //
+ if (PreferedMaximumLength == MAXULONG) {
+ OutputBufferLength = (BufferHintSize) ?
+ BufferHintSize :
+ INITIAL_ALLOCATION_SIZE;
+ }
+ else {
+ OutputBufferLength = PreferedMaximumLength;
+ }
+
+ OutputBufferLength = ROUND_UP_COUNT(OutputBufferLength, ALIGN_WCHAR);
+
+ if ((*OutputBuffer = MIDL_user_allocate(OutputBufferLength)) == NULL) {
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+ RtlZeroMemory((PVOID) *OutputBuffer, OutputBufferLength);
+
+ CompletionEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+
+ if (CompletionEvent == (HANDLE)-1) {
+ MIDL_user_free(*OutputBuffer);
+ *OutputBuffer = NULL;
+ return(GetLastError());
+ }
+
+ Drrp->Parameters.EnumerateServers.EntriesRead = 0;
+
+ //
+ // Make the request of the Datagram Receiver
+ //
+
+ ntstatus = NtDeviceIoControlFile(
+ FileHandle,
+ CompletionEvent,
+ NULL, // APC routine
+ NULL, // APC context
+ &IoStatusBlock,
+ DeviceControlCode,
+ Drrp,
+ RequestPacketLength,
+ *OutputBuffer,
+ OutputBufferLength
+ );
+
+ if (NT_SUCCESS(ntstatus)) {
+
+ //
+ // If pending was returned, then wait until the request completes.
+ //
+
+ if (ntstatus == STATUS_PENDING) {
+ do {
+ ntstatus = WaitForSingleObjectEx(CompletionEvent, 0xffffffff, TRUE);
+ } while ( ntstatus == WAIT_IO_COMPLETION );
+ }
+
+ if (NT_SUCCESS(ntstatus)) {
+ ntstatus = IoStatusBlock.Status;
+ }
+ }
+
+ //
+ // Map NT status to Win error
+ //
+ status = NetpNtStatusToApiStatus(ntstatus);
+
+ if (status == ERROR_MORE_DATA) {
+
+ NetpAssert(
+ FIELD_OFFSET(
+ LMDR_REQUEST_PACKET,
+ Parameters.EnumerateNames.TotalBytesNeeded
+ ) ==
+ FIELD_OFFSET(
+ LMDR_REQUEST_PACKET,
+ Parameters.EnumerateServers.TotalBytesNeeded
+ )
+ );
+
+ NetpAssert(
+ FIELD_OFFSET(
+ LMDR_REQUEST_PACKET,
+ Parameters.GetBrowserServerList.TotalBytesNeeded
+ ) ==
+ FIELD_OFFSET(
+ LMDR_REQUEST_PACKET,
+ Parameters.EnumerateServers.TotalBytesNeeded
+ )
+ );
+
+ TotalBytesNeeded = Drrp->Parameters.EnumerateNames.TotalBytesNeeded;
+ }
+
+ if ((TotalBytesNeeded > OutputBufferLength) &&
+ (PreferedMaximumLength == MAXULONG)) {
+ PLMDR_REQUEST_PACKET Drrp = (PLMDR_REQUEST_PACKET) RequestPacket;
+
+ //
+ // Initial output buffer allocated was too small and we need to return
+ // all data. First free the output buffer before allocating the
+ // required size plus a fudge factor just in case the amount of data
+ // grew.
+ //
+
+ MIDL_user_free(*OutputBuffer);
+
+ OutputBufferLength =
+ ROUND_UP_COUNT((TotalBytesNeeded + FUDGE_FACTOR_SIZE),
+ ALIGN_WCHAR);
+
+ if ((*OutputBuffer = MIDL_user_allocate(OutputBufferLength)) == NULL) {
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+ RtlZeroMemory((PVOID) *OutputBuffer, OutputBufferLength);
+
+
+ NetpAssert(
+ FIELD_OFFSET(
+ LMDR_REQUEST_PACKET,
+ Parameters.EnumerateNames.ResumeHandle
+ ) ==
+ FIELD_OFFSET(
+ LMDR_REQUEST_PACKET,
+ Parameters.EnumerateServers.ResumeHandle
+ )
+ );
+
+ NetpAssert(
+ FIELD_OFFSET(
+ LMDR_REQUEST_PACKET,
+ Parameters.EnumerateNames.ResumeHandle
+ ) ==
+ FIELD_OFFSET(
+ LMDR_REQUEST_PACKET,
+ Parameters.GetBrowserServerList.ResumeHandle
+ )
+ );
+
+ Drrp->Parameters.EnumerateNames.ResumeHandle = OriginalResumeKey;
+ Drrp->Parameters.EnumerateServers.EntriesRead = 0;
+
+ //
+ // Make the request of the Datagram Receiver
+ //
+
+ ntstatus = NtDeviceIoControlFile(
+ FileHandle,
+ CompletionEvent,
+ NULL, // APC routine
+ NULL, // APC context
+ &IoStatusBlock,
+ DeviceControlCode,
+ Drrp,
+ RequestPacketLength,
+ *OutputBuffer,
+ OutputBufferLength
+ );
+
+ if (NT_SUCCESS(ntstatus)) {
+
+ //
+ // If pending was returned, then wait until the request completes.
+ //
+
+ if (ntstatus == STATUS_PENDING) {
+ do {
+ ntstatus = WaitForSingleObjectEx(CompletionEvent, 0xffffffff, TRUE);
+ } while ( ntstatus == WAIT_IO_COMPLETION );
+ }
+
+ if (NT_SUCCESS(ntstatus)) {
+ ntstatus = IoStatusBlock.Status;
+ }
+ }
+
+ status = NetpNtStatusToApiStatus(ntstatus);
+
+ }
+
+
+ //
+ // If not successful in getting any data, or if the caller asked for
+ // all available data with PreferedMaximumLength == MAXULONG and
+ // our buffer overflowed, free the output buffer and set its pointer
+ // to NULL.
+ //
+ if ((status != NERR_Success && status != ERROR_MORE_DATA) ||
+ (TotalBytesNeeded == 0) ||
+ (PreferedMaximumLength == MAXULONG && status == ERROR_MORE_DATA) ||
+ (Drrp->Parameters.EnumerateServers.EntriesRead == 0)) {
+
+ MIDL_user_free(*OutputBuffer);
+ *OutputBuffer = NULL;
+
+ //
+ // PreferedMaximumLength == MAXULONG and buffer overflowed means
+ // we do not have enough memory to satisfy the request.
+ //
+ if (status == ERROR_MORE_DATA) {
+ status = ERROR_NOT_ENOUGH_MEMORY;
+ }
+ }
+
+ CloseHandle(CompletionEvent);
+
+ return status;
+
+ UNREFERENCED_PARAMETER(Information);
+}
+
+ NET_API_STATUS
+GetBrowserServerList(
+ IN PUNICODE_STRING TransportName,
+ IN LPCWSTR Domain,
+ OUT LPWSTR *BrowserList[],
+ OUT PULONG BrowserListLength,
+ IN BOOLEAN ForceRescan
+ )
+/*++
+
+Routine Description:
+
+ This function will return a list of browser servers.
+
+Arguments:
+
+ IN PUNICODE_STRING TransportName - Transport to return list.
+
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+
+
+ NET_API_STATUS Status;
+ HANDLE BrowserHandle;
+ PLMDR_REQUEST_PACKET RequestPacket = NULL;
+
+// DbgPrint("Getting browser server list for transport %wZ\n", TransportName);
+
+ Status = OpenBrowser(&BrowserHandle);
+
+ if (Status != NERR_Success) {
+ return Status;
+ }
+
+ RequestPacket = MIDL_user_allocate(sizeof(LMDR_REQUEST_PACKET)+(DNLEN*sizeof(WCHAR))+TransportName->MaximumLength);
+
+ if (RequestPacket == NULL) {
+ NtClose(BrowserHandle);
+ return(GetLastError());
+ }
+
+ RequestPacket->Version = LMDR_REQUEST_PACKET_VERSION_DOM;
+
+ RequestPacket->Level = 0;
+
+ RequestPacket->Parameters.GetBrowserServerList.ForceRescan = ForceRescan;
+
+ if (Domain != NULL) {
+ STRCPY(RequestPacket->Parameters.GetBrowserServerList.DomainName, Domain);
+
+ RequestPacket->Parameters.GetBrowserServerList.DomainNameLength = (USHORT)STRLEN(Domain) * sizeof(TCHAR);
+ } else {
+ RequestPacket->Parameters.GetBrowserServerList.DomainNameLength = 0;
+ RequestPacket->Parameters.GetBrowserServerList.DomainName[0] = L'\0';
+
+ }
+
+ RequestPacket->TransportName.Buffer = (PWSTR)((PCHAR)RequestPacket+sizeof(LMDR_REQUEST_PACKET)+DNLEN*sizeof(WCHAR));
+ RequestPacket->TransportName.MaximumLength = TransportName->MaximumLength;
+
+ RtlCopyUnicodeString(&RequestPacket->TransportName, TransportName);
+ RtlInitUnicodeString( &RequestPacket->EmulatedDomainName, NULL );
+
+ RequestPacket->Parameters.GetBrowserServerList.ResumeHandle = 0;
+
+ Status = DeviceControlGetInfo(
+ BrowserHandle,
+ IOCTL_LMDR_GET_BROWSER_SERVER_LIST,
+ RequestPacket,
+ sizeof(LMDR_REQUEST_PACKET)+
+ (DNLEN*sizeof(WCHAR))+TransportName->MaximumLength,
+ (PVOID *)BrowserList,
+ 0xffffffff,
+ 4096,
+ NULL);
+
+ if (Status == NERR_Success) {
+ *BrowserListLength = RequestPacket->Parameters.GetBrowserServerList.EntriesRead;
+ }
+
+ NtClose(BrowserHandle);
+ MIDL_user_free(RequestPacket);
+
+ return Status;
+}
+ NET_API_STATUS
+OpenBrowser(
+ OUT PHANDLE BrowserHandle
+ )
+/*++
+
+Routine Description:
+
+ This function opens a handle to the bowser device driver.
+
+Arguments:
+
+ OUT PHANDLE BrowserHandle - Returns the handle to the browser.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NTSTATUS ntstatus;
+
+ UNICODE_STRING DeviceName;
+
+ IO_STATUS_BLOCK IoStatusBlock;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+
+
+ //
+ // Open the redirector device.
+ //
+ RtlInitUnicodeString(&DeviceName, DD_BROWSER_DEVICE_NAME_U);
+
+ InitializeObjectAttributes(
+ &ObjectAttributes,
+ &DeviceName,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL
+ );
+
+ ntstatus = NtOpenFile(
+ BrowserHandle,
+ SYNCHRONIZE | GENERIC_READ | GENERIC_WRITE,
+ &ObjectAttributes,
+ &IoStatusBlock,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ FILE_SYNCHRONOUS_IO_NONALERT
+ );
+
+ if (NT_SUCCESS(ntstatus)) {
+ ntstatus = IoStatusBlock.Status;
+ }
+
+ return NetpNtStatusToApiStatus(ntstatus);
+
+}
+
+NET_API_STATUS
+CheckForService(
+ IN LPTSTR ServiceName,
+ OUT LPSERVICE_STATUS ServiceStatus OPTIONAL
+ )
+{
+ SC_HANDLE ServiceControllerHandle;
+ SC_HANDLE ServiceHandle;
+ SERVICE_STATUS Status;
+
+ if (!ARGUMENT_PRESENT(ServiceStatus)) {
+ ServiceStatus = &Status;
+ }
+
+ ServiceControllerHandle = OpenSCManager(NULL, NULL, SC_MANAGER_ENUMERATE_SERVICE);
+
+ if (ServiceControllerHandle == NULL) {
+
+ return GetLastError();
+ }
+
+ ServiceHandle = OpenService(ServiceControllerHandle, ServiceName, SERVICE_QUERY_STATUS);
+
+ if (ServiceHandle == NULL) {
+
+ CloseServiceHandle(ServiceControllerHandle);
+ return GetLastError();
+ }
+
+
+ if (!QueryServiceStatus(ServiceHandle, ServiceStatus)) {
+ CloseServiceHandle(ServiceHandle);
+ CloseServiceHandle(ServiceControllerHandle);
+
+ return GetLastError();
+ }
+
+ if ((ServiceStatus->dwCurrentState != SERVICE_RUNNING) &&
+ (ServiceStatus->dwCurrentState != SERVICE_START_PENDING)) {
+ CloseServiceHandle(ServiceHandle);
+ CloseServiceHandle(ServiceControllerHandle);
+ return(NERR_ServiceNotInstalled);
+ }
+
+ CloseServiceHandle(ServiceHandle);
+
+ CloseServiceHandle(ServiceControllerHandle);
+
+ return NERR_Success;
+}
+
+NET_API_STATUS
+BrGetLanaNumFromNetworkName(
+ IN PWCHAR TransportName,
+ OUT CCHAR *LanaNum
+ )
+/*++
+
+ NOTE: THIS CODE WILL NOT WORK IN THE FUTURE!!!!!!!!!!!!!!
+
+--*/
+
+{
+ HKEY Key;
+ WCHAR BindInformation[MAX_PATH*20];
+ LPWSTR DevicePointer;
+ ULONG BindInfoSize = sizeof(BindInformation);
+ struct {
+ CHAR Enumerated;
+ CHAR LanaNum;
+ } LanaMap[256];
+
+ ULONG LanaMapSize = sizeof(LanaMap);
+ NET_API_STATUS Status;
+ DWORD Type;
+ DWORD LanaIndex;
+
+ RtlZeroMemory(LanaMap, sizeof(LanaMap));
+
+ LanaIndex = 0;
+
+ if (Status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"System\\CurrentControlSet\\Services\\Netbios\\Linkage", 0, KEY_QUERY_VALUE, &Key)) {
+ return Status;
+ }
+
+ if (Status = RegQueryValueEx(Key, L"Bind", 0, &Type, (LPBYTE)BindInformation, &BindInfoSize)) {
+ RegCloseKey(Key);
+ return Status;
+ }
+
+ if (Status = RegQueryValueEx(Key, L"LanaMap", 0, &Type, (LPBYTE)LanaMap, &LanaMapSize)) {
+ RegCloseKey(Key);
+ return Status;
+ }
+
+ DevicePointer = BindInformation;
+
+ while (*DevicePointer != UNICODE_NULL) {
+ if (!_wcsicmp(TransportName, DevicePointer)) {
+
+ if (LanaMap[LanaIndex].Enumerated != 0) {
+ *LanaNum = LanaMap[LanaIndex].LanaNum;
+ Status = NERR_Success;
+ } else {
+ Status = ERROR_FILE_NOT_FOUND;
+ }
+
+ RegCloseKey(Key);
+
+ return Status;
+ }
+
+ LanaIndex += 1;
+
+ DevicePointer += wcslen(DevicePointer)+1;
+ }
+
+ RegCloseKey(Key);
+ return(ERROR_FILE_NOT_FOUND);
+}
+
+// 1234567890123456
+#define SPACES " "
+
+#define ClearNcb( PNCB ) { \
+ RtlZeroMemory( PNCB , sizeof (NCB) ); \
+ RtlCopyMemory( (PNCB)->ncb_name, SPACES, sizeof(SPACES)-1 );\
+ RtlCopyMemory( (PNCB)->ncb_callname, SPACES, sizeof(SPACES)-1 );\
+ }
+
+ NET_API_STATUS
+GetNetBiosMasterName(
+ IN LPWSTR NetworkName,
+ IN LPWSTR PrimaryDomain,
+ OUT LPWSTR MasterName,
+ IN PSVCS_NET_BIOS_RESET SvcsNetBiosReset OPTIONAL
+ )
+{
+ CCHAR LanaNum;
+ NCB AStatNcb;
+#define MAX_NETBIOS_NAMES 256
+ struct {
+ ADAPTER_STATUS AdapterInfo;
+ NAME_BUFFER Names[MAX_NETBIOS_NAMES];
+ } AdapterStatus;
+ WORD i;
+ CHAR remoteName[CNLEN+1];
+ NET_API_STATUS Status;
+ OEM_STRING OemString;
+ UNICODE_STRING UnicodeString;
+
+ Status = BrGetLanaNumFromNetworkName(NetworkName, &LanaNum);
+
+ if (Status != NERR_Success) {
+ return Status;
+ }
+
+ //
+ // If the SvcsNetBiosReset argument is present, then this routine is
+ // being called from the service. In this case it needs to synchronize
+ // its NetBios Reset with the workstation and the messenger.
+ //
+ if (ARGUMENT_PRESENT(SvcsNetBiosReset)) {
+ SvcsNetBiosReset(LanaNum);
+ }
+ else {
+ ClearNcb(&AStatNcb)
+
+ AStatNcb.ncb_command = NCBRESET;
+ AStatNcb.ncb_lsn = 0; // Request resources
+ AStatNcb.ncb_lana_num = LanaNum;
+ AStatNcb.ncb_callname[0] = 0; // 16 sessions
+ AStatNcb.ncb_callname[1] = 0; // 16 commands
+ AStatNcb.ncb_callname[2] = 0; // 8 names
+ AStatNcb.ncb_callname[3] = 0; // Don't want the reserved address
+ Netbios( &AStatNcb );
+ }
+ ClearNcb( &AStatNcb );
+
+ //
+ // Uppercase the remote name.
+ //
+
+ RtlInitUnicodeString(&UnicodeString, PrimaryDomain);
+
+ OemString.Buffer=remoteName;
+
+ OemString.MaximumLength=sizeof(remoteName);
+
+ Status = RtlUpcaseUnicodeStringToOemString(&OemString,
+ &UnicodeString,
+ FALSE);
+
+ if (!NT_SUCCESS(Status)) {
+ return RtlNtStatusToDosError(Status);
+ }
+
+ AStatNcb.ncb_command = NCBASTAT;
+
+ RtlCopyMemory( AStatNcb.ncb_callname, remoteName, strlen(remoteName));
+
+ AStatNcb.ncb_callname[15] = MASTER_BROWSER_SIGNATURE;
+
+ AStatNcb.ncb_lana_num = LanaNum;
+ AStatNcb.ncb_length = sizeof( AdapterStatus );
+ AStatNcb.ncb_buffer = (CHAR *)&AdapterStatus;
+ Netbios( &AStatNcb );
+
+ if ( AStatNcb.ncb_retcode == NRC_GOODRET ||
+ AStatNcb.ncb_retcode == NRC_INCOMP ) {
+ for ( i=0 ; i < min(AdapterStatus.AdapterInfo.name_count, MAX_NETBIOS_NAMES) ; i++ ) {
+ if (AdapterStatus.Names[i].name[NCBNAMSZ-1] == SERVER_SIGNATURE) {
+ DWORD j;
+
+ //
+ // Ignore malformed netbios names.
+ //
+ // Some transports have strange netbios names. For instance,
+ // netbt registers a netbios name where the first 12 bytes
+ // are 0 and the last 4 bytes are the IP address.
+ //
+ for ( j = 0 ; j < CNLEN ; j++ ) {
+ if (AdapterStatus.Names[i].name[j] == '\0') {
+ break;
+ }
+ }
+
+ if ( j != CNLEN ) {
+ continue;
+ }
+
+ //
+ // Convert to unicode
+ //
+
+ if (MultiByteToWideChar(CP_OEMCP,
+ 0,
+ AdapterStatus.Names[i].name,
+ CNLEN,
+ MasterName,
+ CNLEN) == 0) {
+ return(GetLastError());
+ }
+
+ for (j = CNLEN - 1; j ; j -= 1) {
+ if (MasterName[j] != L' ') {
+ MasterName[j+1] = UNICODE_NULL;
+ break;
+ }
+ }
+
+ return NERR_Success;
+ }
+ }
+ } else {
+ return AStatNcb.ncb_retcode;
+ }
+}
+
+ NET_API_STATUS
+SendDatagram(
+ IN HANDLE DgReceiverHandle,
+ IN PUNICODE_STRING Network,
+ IN PUNICODE_STRING EmulatedDomainName,
+ IN PWSTR ResponseName,
+ IN DGRECEIVER_NAME_TYPE NameType,
+ IN PVOID Buffer,
+ IN ULONG BufferLength
+ )
+{
+ NET_API_STATUS Status;
+ UCHAR PacketBuffer[sizeof(LMDR_REQUEST_PACKET)+(LM20_CNLEN+1)*sizeof(WCHAR)];
+ PLMDR_REQUEST_PACKET RequestPacket = (PLMDR_REQUEST_PACKET)PacketBuffer;
+
+
+ RequestPacket->Version = LMDR_REQUEST_PACKET_VERSION_DOM;
+
+ RequestPacket->TransportName = *Network;
+ RequestPacket->EmulatedDomainName = *EmulatedDomainName;
+
+ RequestPacket->Type = Datagram;
+
+ RequestPacket->Parameters.SendDatagram.DestinationNameType = NameType;
+
+ RequestPacket->Parameters.SendDatagram.MailslotNameLength = 0;
+
+ //
+ // The domain announcement name is special, so we don't have to specify
+ // a destination name for it.
+ //
+
+ RequestPacket->Parameters.SendDatagram.NameLength = wcslen(ResponseName)*sizeof(WCHAR);
+
+ wcscpy(RequestPacket->Parameters.SendDatagram.Name, ResponseName);
+
+ //
+ // This is a simple IoControl - It just sends the datagram.
+ //
+
+ Status = BrDgReceiverIoControl(DgReceiverHandle,
+ IOCTL_LMDR_WRITE_MAILSLOT,
+ RequestPacket,
+ FIELD_OFFSET(LMDR_REQUEST_PACKET, Parameters.SendDatagram.Name)+
+ RequestPacket->Parameters.SendDatagram.NameLength,
+ Buffer,
+ BufferLength,
+ NULL);
+
+ return Status;
+}
+
diff --git a/private/net/svcdlls/browser2/dirs b/private/net/svcdlls/browser2/dirs
new file mode 100644
index 000000000..7ae0222c7
--- /dev/null
+++ b/private/net/svcdlls/browser2/dirs
@@ -0,0 +1,30 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ dirs.
+
+Abstract:
+
+ This file specifies the subdirectories of the current directory that
+ contain component makefiles.
+
+
+Author:
+
+ Steve Wood (stevewo) 17-Apr-1990
+
+NOTE: Commented description of this file is in \nt\bak\bin\dirs.tpl
+
+!ENDIF
+
+
+DIRS=common \
+ server \
+ client
+
+
+#OPTIONAL_DIRS=dir8 \
+# dir9
diff --git a/private/net/svcdlls/browser2/imports.h b/private/net/svcdlls/browser2/imports.h
new file mode 100644
index 000000000..1577f8707
--- /dev/null
+++ b/private/net/svcdlls/browser2/imports.h
@@ -0,0 +1,46 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ imports.h
+
+Abstract:
+
+ This file allows us to include standard system header files in the
+ .idl file. The main .idl file imports a file called import.idl.
+ This allows the .idl file to use the types defined in these header
+ files. It also causes the following line to be added in the
+ MIDL generated header file:
+
+ #include "imports.h"
+
+ Thus these types are available to the RPC stub routines as well.
+
+Author:
+
+ Dan Lafferty (danl) 07-May-1991
+
+Revision History:
+
+
+--*/
+
+
+#include <windef.h>
+#include <lmcons.h>
+
+#ifdef MIDL_PASS
+#ifdef UNICODE
+#define LPTSTR [string] wchar_t*
+#else
+#define LPTSTR [string] LPTSTR
+#endif
+#define LPSTR [string] LPSTR
+#define BOOL DWORD
+#endif
+
+#include <lmserver.h>
+#include <lmbrowsr.h>
+
diff --git a/private/net/svcdlls/browser2/imports.idl b/private/net/svcdlls/browser2/imports.idl
new file mode 100644
index 000000000..46fc29d70
--- /dev/null
+++ b/private/net/svcdlls/browser2/imports.idl
@@ -0,0 +1,67 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ imports.idl
+
+Abstract:
+
+ This file is useful for creating RPC interfaces that require the use
+ of windef types. The .idl file for the RPC product should contain a
+ line in the interface body that imports this file. For example:
+
+ import "imports.h";
+
+ Doing this causes the MIDL generated header file to contain the
+ following line:
+
+ #include "imports.h"
+
+ If this technique is not used, and instead the .idl file for the RPC
+ product simply contains #include <importsf.h>, then the contents of
+ imports.h will be expanded in the MIDL generated header file. This
+ can lead to duplicate definition problems later when the RPC client
+ or RPC server code needs to include both the MIDL generated header file
+ and a file that is included in imports.h.
+
+Author:
+
+ Dan Lafferty (danl) 20-Mar-1991
+
+Environment:
+
+ User Mode - Win32 - for use with the MIDL compiler
+
+
+Revision History:
+
+ 03-Apr-1991 danl
+ created
+
+--*/
+
+[
+ uuid(12345678-1234-ABCD-EF00-9948756789AB),
+#ifdef __midl
+ ms_union,
+#endif // __midl
+ version(0.0)
+]
+interface imports
+
+{
+#define MIDL_PASS
+#include "imports.h"
+
+//
+// All .idl files need to contain at least one function prototype
+//
+
+DWORD
+Dummy(
+ [in] DWORD DummyParm);
+
+
+}
diff --git a/private/net/svcdlls/browser2/makefil0 b/private/net/svcdlls/browser2/makefil0
new file mode 100644
index 000000000..c92408bcd
--- /dev/null
+++ b/private/net/svcdlls/browser2/makefil0
@@ -0,0 +1,61 @@
+#
+# This is the MIDL compile phase of the build process.
+#
+# The following is where you put the name of your .idl file without
+# the .idl extension:
+#
+
+!INCLUDE $(NTMAKEENV)\makefile.plt
+
+IDL_NAME = bowser
+
+!IFNDEF DISABLE_NET_UNICODE
+UNICODE=1
+NET_C_DEFINES=-DUNICODE
+!ENDIF
+
+SDKINC = $(BASEDIR)\public\sdk\inc
+NETINC = $(BASEDIR)\private\net\inc
+SDKCRTINC = $(BASEDIR)\public\sdk\inc\crt
+PRIVINC = $(BASEDIR)\private\inc
+
+INCS = -I$(SDKINC) -I$(SDKCRTINC) -I$(PRIVINC) -I$(NETINC)
+
+CLIENT_TARGETS = client\$(IDL_NAME)_c.c \
+ .\$(IDL_NAME).h
+
+SERVER_TARGETS = server\$(IDL_NAME)_s.mdl
+
+EXTRN_DEPENDS = $(SDKINC)\lmcons.h \
+ $(SDKINC)\windef.h \
+ $(SDKINC)\lmserver.h \
+ $(SDKINC)\lmbrowsr.h \
+ $(IDL_NAME).acf
+
+CPP = -cpp_cmd "$(MIDL_CPP)" $(MIDL_FLAGS) $(C_DEFINES) $(NET_C_DEFINES)
+
+#
+# Define Products and Dependencies
+#
+
+all: $(CLIENT_TARGETS) $(SERVER_TARGETS) $(EXTRN_DEPENDS)
+!IF "$(BUILDMSG)" != ""
+ @ech ; $(BUILDMSG) ;
+!ENDIF
+
+clean: delsrc all
+
+delsrc:
+ erase $(CLIENT_TARGETS) $(SERVER_TARGETS)
+
+#
+# MIDL COMPILE
+#
+
+$(CLIENT_TARGETS) : .\$(IDL_NAME).idl $(EXTRN_DEPENDS)
+ midl -Oi -server none -oldnames -error allocation -error ref -ms_ext -c_ext $(CPP) .\$(IDL_NAME).idl $(INCS)
+ IF EXIST $(IDL_NAME)_c.c copy $(IDL_NAME)_c.c .\client & del $(IDL_NAME)_c.c
+
+$(SERVER_TARGETS) : .\$(IDL_NAME).idl $(EXTRN_DEPENDS)
+ midl -client none -oldnames -error stub_data -error allocation -error ref -ms_ext -c_ext $(CPP) .\$(IDL_NAME).idl $(INCS)
+ IF EXIST $(IDL_NAME)_s.c copy $(IDL_NAME)_s.c .\server\$(IDL_NAME)_s.mdl & del $(IDL_NAME)_s.c
diff --git a/private/net/svcdlls/browser2/server/bowqueue.c b/private/net/svcdlls/browser2/server/bowqueue.c
new file mode 100644
index 000000000..12511c78e
--- /dev/null
+++ b/private/net/svcdlls/browser2/server/bowqueue.c
@@ -0,0 +1,672 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ bowqueue.c
+
+Abstract:
+
+ This module implements a worker thread and a set of functions for
+ passing work to it.
+
+Author:
+
+ Larry Osterman (LarryO) 13-Jul-1992
+
+
+Revision History:
+
+--*/
+
+#include "precomp.h"
+#pragma hdrstop
+
+//
+// Number of worker threads to create and the usage count array.
+//
+
+
+ULONG BrNumberOfWorkerThreads = 0;
+
+ULONG BrNumberOfCreatedWorkerThreads = 0;
+
+PULONG
+BrWorkerThreadCount = NULL;
+
+PHANDLE
+BrThreadArray = NULL;
+
+//
+// CritSect guard the WorkQueue list.
+//
+
+CRITICAL_SECTION BrWorkerCritSect;
+
+#define LOCK_WORK_QUEUE() EnterCriticalSection(&BrWorkerCritSect);
+#define UNLOCK_WORK_QUEUE() LeaveCriticalSection(&BrWorkerCritSect);
+
+//
+// Head of singly linked list of work items queued to the worker thread.
+//
+
+LIST_ENTRY
+BrWorkerQueueHead = {0};
+
+//
+// Event that is signal whenever a work item is put in the queue. The
+// worker thread waits on this event.
+//
+
+HANDLE
+BrWorkerSemaphore = NULL;
+
+VOID
+BrTimerRoutine(
+ IN PVOID TimerContext,
+ IN ULONG TImerLowValue,
+ IN LONG TimerHighValue
+ );
+
+NET_API_STATUS
+BrWorkerInitialization(
+ VOID
+ )
+{
+ ULONG Index;
+ ULONG ThreadId;
+
+ NET_API_STATUS NetStatus;
+ PLMDR_TRANSPORT_LIST TransportList = NULL ;
+ PLMDR_TRANSPORT_LIST TransportEntry;
+
+ //
+ // Perform initialization that allows us to call BrWorkerTermination
+ //
+
+ InitializeCriticalSection( &BrWorkerCritSect );
+ InitializeListHead( &BrWorkerQueueHead );
+
+ //
+ // Get the list of transports from the datagram receiver and count them.
+ // Create one thread per network (and the main thread is a worker thread)
+ //
+ NetStatus = BrGetTransportList(&TransportList);
+
+ if ( NetStatus != NERR_Success ) {
+ goto Cleanup;
+ }
+
+ TransportEntry = TransportList;
+ BrNumberOfCreatedWorkerThreads = 1;
+
+ while (TransportEntry->NextEntryOffset != 0) {
+ BrNumberOfCreatedWorkerThreads ++;
+ TransportEntry = (PLMDR_TRANSPORT_LIST)((PCHAR)TransportEntry+TransportEntry->NextEntryOffset);
+ }
+
+
+ //
+ // Initialize the work queue semaphore.
+ //
+
+ BrWorkerSemaphore = CreateSemaphore(NULL, 0, 0x7fffffff, NULL);
+
+ if (BrWorkerSemaphore == NULL) {
+ NetStatus = GetLastError();
+ goto Cleanup;
+ }
+
+
+ BrThreadArray = LocalAlloc(LMEM_ZEROINIT, (BrNumberOfCreatedWorkerThreads+1)*sizeof(HANDLE));
+
+ if (BrThreadArray == NULL) {
+ NetStatus = ERROR_NOT_ENOUGH_MEMORY;
+ goto Cleanup;
+ }
+
+
+ BrWorkerThreadCount = (PULONG)LocalAlloc(LMEM_ZEROINIT, (BrNumberOfCreatedWorkerThreads+1)*sizeof(HANDLE)*2);
+
+ if (BrWorkerThreadCount == NULL) {
+ NetStatus = ERROR_NOT_ENOUGH_MEMORY;
+ goto Cleanup;
+ }
+
+ //
+ // Create the desired number of worker threads.
+ //
+
+ for (Index = 0; Index < BrNumberOfCreatedWorkerThreads; Index += 1) {
+
+ BrThreadArray[Index] = CreateThread(NULL,
+ 0,
+ (LPTHREAD_START_ROUTINE)BrWorkerThread,
+ (PVOID)Index,
+ 0,
+ &ThreadId
+ );
+
+ if (BrThreadArray[Index] == NULL) {
+ NetStatus = GetLastError();
+ goto Cleanup;
+ }
+
+ //
+ // Set the browser threads to time critical priority.
+ //
+
+ SetThreadPriority(BrThreadArray[Index], THREAD_PRIORITY_ABOVE_NORMAL);
+
+
+ }
+
+ NetStatus = NERR_Success;
+
+ //
+ // Done
+ //
+Cleanup:
+
+ if (NetStatus != NERR_Success) {
+ (VOID) BrWorkerTermination();
+ }
+
+ if ( TransportList != NULL ) {
+ MIDL_user_free(TransportList);
+ }
+
+ return NetStatus;
+}
+
+VOID
+BrWorkerKillThreads(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ Terminate all worker threads.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ ULONG Index;
+
+ //
+ // Make sure the terminate now event is in the signalled state to unwind
+ // all our threads.
+ //
+
+ SetEvent( BrGlobalData.TerminateNowEvent );
+
+ if ( BrThreadArray != NULL ) {
+ for ( Index = 0 ; Index < BrNumberOfCreatedWorkerThreads ; Index += 1 ) {
+ if ( BrThreadArray != NULL && BrThreadArray[Index] != NULL ) {
+
+ WaitForSingleObject( BrThreadArray[Index], 0xffffffff );
+
+ CloseHandle( BrThreadArray[Index] );
+ BrThreadArray[Index] = NULL;
+ }
+
+ }
+ }
+
+ return;
+}
+
+NET_API_STATUS
+BrWorkerTermination(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ Undo initialization of the worker threads.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ Status value -
+
+--*/
+{
+ //
+ // Ensure the threads have been terminated.
+ //
+
+ BrWorkerKillThreads();
+
+ if ( BrWorkerSemaphore != NULL ) {
+ CloseHandle( BrWorkerSemaphore );
+
+ BrWorkerSemaphore = NULL;
+ }
+
+ if (BrThreadArray != NULL) {
+ LocalFree(BrThreadArray);
+
+ BrThreadArray = NULL;
+
+ }
+
+ if (BrWorkerThreadCount != NULL) {
+ LocalFree(BrWorkerThreadCount);
+
+ BrWorkerThreadCount = NULL;
+ }
+
+ BrNumberOfWorkerThreads = 0;
+ BrNumberOfCreatedWorkerThreads = 0;
+
+ DeleteCriticalSection( &BrWorkerCritSect );
+
+ return NERR_Success;
+}
+
+VOID
+BrQueueWorkItem(
+ IN PWORKER_ITEM WorkItem
+ )
+
+/*++
+
+Routine Description:
+
+ This function queues a work item to a queue that is processed by
+ a worker thread. This thread runs at low priority, at IRQL 0
+
+Arguments:
+
+ WorkItem - Supplies a pointer to the work item to add the the queue.
+ This structure must be located in NonPagedPool. The work item
+ structure contains a doubly linked list entry, the address of a
+ routine to call and a parameter to pass to that routine. It is
+ the routine's responsibility to reclaim the storage occupied by
+ the WorkItem structure.
+
+Return Value:
+
+ Status value -
+
+--*/
+
+{
+ //
+ // Acquire the worker thread spinlock and insert the work item in the
+ // list and release the worker thread semaphore if the work item is
+ // not already in the list.
+ //
+
+ LOCK_WORK_QUEUE();
+
+ if (WorkItem->Inserted == FALSE) {
+
+ BrPrint(( BR_QUEUE, "Inserting work item %lx (%lx)\n",WorkItem, WorkItem->WorkerRoutine));
+
+ InsertTailList( &BrWorkerQueueHead, &WorkItem->List );
+
+ WorkItem->Inserted = TRUE;
+
+ ReleaseSemaphore( BrWorkerSemaphore,
+ 1,
+ NULL
+ );
+ }
+
+ UNLOCK_WORK_QUEUE();
+
+ return;
+}
+
+ VOID
+BrWorkerThread(
+ IN PVOID StartContext
+ )
+
+{
+ NET_API_STATUS NetStatus;
+
+#define WORKER_SIGNALED 0
+#define TERMINATION_SIGNALED 1
+#define REG_CHANGE_SIGNALED 2
+#define NUMBER_OF_EVENTS 3
+ HANDLE WaitList[NUMBER_OF_EVENTS];
+ ULONG WaitCount = 0;
+
+ ULONG Index;
+ PWORKER_ITEM WorkItem;
+ ULONG ThreadIndex = (ULONG)StartContext;
+
+ HKEY RegistryHandle = NULL;
+ HANDLE EventHandle = NULL;
+
+ WaitList[WORKER_SIGNALED] = BrWorkerSemaphore;
+ WaitCount ++;
+ WaitList[TERMINATION_SIGNALED] = BrGlobalData.TerminateNowEvent;
+ WaitCount ++;
+
+ //
+ // Primary thread waits on registry changes, too.
+ //
+ if ( ThreadIndex == 0xFFFFFFFF ) {
+ DWORD RegStatus;
+ NET_API_STATUS NetStatus;
+
+ //
+ // Register for notifications of changes to Parameters
+ //
+ // Failure doesn't affect normal operation of the browser.
+ //
+
+ RegStatus = RegOpenKeyA( HKEY_LOCAL_MACHINE,
+ "System\\CurrentControlSet\\Services\\Browser\\Parameters",
+ &RegistryHandle );
+
+ if ( RegStatus != ERROR_SUCCESS ) {
+ BrPrint(( BR_CRITICAL, "BrWorkerThead: Can't RegOpenKey %ld\n", RegStatus ));
+ } else {
+
+ EventHandle = CreateEvent(
+ NULL, // No security attributes
+ TRUE, // Automatically reset
+ FALSE, // Initially not signaled
+ NULL ); // No name
+
+ if ( EventHandle == NULL ) {
+ BrPrint(( BR_CRITICAL, "BrWorkerThead: Can't CreateEvent %ld\n", GetLastError() ));
+ } else {
+ NetStatus = RegNotifyChangeKeyValue(
+ RegistryHandle,
+ FALSE, // Ignore subkeys
+ REG_NOTIFY_CHANGE_LAST_SET, // Notify of value changes
+ EventHandle,
+ TRUE ); // Signal event upon change
+
+ if ( NetStatus != NERR_Success ) {
+ BrPrint(( BR_CRITICAL, "BrWorkerThead: Can't RegNotifyChangeKeyValue %ld\n", NetStatus ));
+ } else {
+ WaitList[REG_CHANGE_SIGNALED] = EventHandle;
+ WaitCount ++;
+ }
+ }
+ }
+ }
+
+ BrPrint(( BR_QUEUE, "Starting new work thread, Context: %lx\n", StartContext));
+
+ //
+ // Set the thread priority to the lowest realtime level.
+ //
+
+ while( TRUE ) {
+ ULONG WaitItem;
+
+ LOCK_WORK_QUEUE();
+
+ //
+ // Wait until something is put in the queue (semaphore is
+ // released), remove the item from the queue, mark it not
+ // inserted, and execute the specified routine.
+ //
+
+ BrNumberOfWorkerThreads += 1;
+
+ UNLOCK_WORK_QUEUE();
+
+ BrPrint(( BR_QUEUE, "%lx: worker thread waiting\n", StartContext));
+
+ do {
+ WaitItem = WaitForMultipleObjectsEx( WaitCount, WaitList, FALSE, 0xffffffff, TRUE );
+ } while ( WaitItem == WAIT_IO_COMPLETION );
+
+ if (WaitItem == 0xffffffff) {
+ BrPrint(( BR_CRITICAL, "WaitForMultipleObjects in browser queue returned %ld\n", GetLastError()));
+ break;
+ }
+
+ if (WaitItem == TERMINATION_SIGNALED) {
+ break;
+
+ //
+ // If the registry has changed,
+ // process the changes.
+ //
+
+ } else if ( WaitItem == REG_CHANGE_SIGNALED ) {
+
+ //
+ // Setup for future notifications.
+ //
+ NetStatus = RegNotifyChangeKeyValue(
+ RegistryHandle,
+ FALSE, // Ignore subkeys
+ REG_NOTIFY_CHANGE_LAST_SET, // Notify of value changes
+ EventHandle,
+ TRUE ); // Signal event upon change
+
+ if ( NetStatus != NERR_Success ) {
+ BrPrint(( BR_CRITICAL, "BrWorkerThead: Can't RegNotifyChangeKeyValue %ld\n", NetStatus ));
+ }
+
+
+ NetStatus = BrReadBrowserConfigFields( FALSE );
+
+ if ( NetStatus != NERR_Success) {
+ BrPrint(( BR_CRITICAL, "BrWorkerThead: Can't BrReadConfigFields %ld\n", NetStatus ));
+ }
+
+ continue;
+
+ }
+
+ BrPrint(( BR_QUEUE, "%lx: Worker thread waking up\n", StartContext));
+
+ LOCK_WORK_QUEUE();
+
+ Index = BrNumberOfWorkerThreads;
+
+ BrNumberOfWorkerThreads -= 1;
+
+ BrWorkerThreadCount[Index - 1] += 1;
+
+ ASSERT (!IsListEmpty(&BrWorkerQueueHead));
+
+ if (!IsListEmpty(&BrWorkerQueueHead)) {
+ WorkItem = (PWORKER_ITEM)RemoveHeadList( &BrWorkerQueueHead );
+
+ ASSERT (WorkItem->Inserted);
+
+ WorkItem->Inserted = FALSE;
+
+ } else {
+ WorkItem = NULL;
+ }
+
+ UNLOCK_WORK_QUEUE();
+
+ BrPrint(( BR_QUEUE, "%lx: Pulling off work item %lx (%lx)\n", StartContext, WorkItem, WorkItem->WorkerRoutine));
+
+ //
+ // Execute the specified routine.
+ //
+
+ if (WorkItem != NULL) {
+ (WorkItem->WorkerRoutine)( WorkItem->Parameter );
+ }
+
+ }
+
+ BrPrint(( BR_QUEUE, "%lx: worker thread exitting\n", StartContext));
+
+ if ( ThreadIndex <= BrNumberOfCreatedWorkerThreads ) {
+ IO_STATUS_BLOCK IoSb;
+
+ //
+ // Cancel any I/O outstanding on this file for this thread.
+ //
+
+ NtCancelIoFile(BrDgReceiverDeviceHandle, &IoSb);
+
+ }
+
+}
+
+ NET_API_STATUS
+BrCreateTimer(
+ IN PBROWSER_TIMER Timer
+ )
+{
+ OBJECT_ATTRIBUTES ObjA;
+ NTSTATUS Status;
+
+ InitializeObjectAttributes(&ObjA, NULL, 0, NULL, NULL);
+
+ Status = NtCreateTimer(&Timer->TimerHandle,
+ TIMER_ALL_ACCESS,
+ &ObjA,
+ NotificationTimer);
+
+ if (!NT_SUCCESS(Status)) {
+ BrPrint(( BR_CRITICAL, "Failed to create timer %lx: %X\n", Timer, Status));
+ return(BrMapStatus(Status));
+ }
+
+ BrPrint(( BR_TIMER, "Creating timer %lx: Handle: %lx\n", Timer, Timer->TimerHandle));
+
+ return(NERR_Success);
+}
+
+ NET_API_STATUS
+BrDestroyTimer(
+ IN PBROWSER_TIMER Timer
+ )
+{
+ HANDLE Handle;
+
+ //
+ // Avoid destroying a timer twice.
+ //
+
+ if ( Timer->TimerHandle == NULL ) {
+ return NERR_Success;
+ }
+
+ // Closing doesn't automatically cancel the timer.
+ (VOID) BrCancelTimer( Timer );
+
+ //
+ // Close the handle and prevent future uses.
+ //
+
+ Handle = Timer->TimerHandle;
+ Timer->TimerHandle = NULL;
+
+ BrPrint(( BR_TIMER, "Destroying timer %lx\n", Timer));
+ return BrMapStatus(NtClose(Handle));
+
+}
+
+ NET_API_STATUS
+BrCancelTimer(
+ IN PBROWSER_TIMER Timer
+ )
+{
+ //
+ // Avoid cancelling a destroyed timer.
+ //
+
+ if ( Timer->TimerHandle == NULL ) {
+ BrPrint(( BR_TIMER, "Canceling destroyed timer %lx\n", Timer));
+ return NERR_Success;
+ }
+
+ BrPrint(( BR_TIMER, "Canceling timer %lx\n", Timer));
+ return BrMapStatus(NtCancelTimer(Timer->TimerHandle, NULL));
+}
+
+ NET_API_STATUS
+BrSetTimer(
+ IN PBROWSER_TIMER Timer,
+ IN ULONG MillisecondsToExpire,
+ IN PBROWSER_WORKER_ROUTINE WorkerFunction,
+ IN PVOID Context
+ )
+{
+ LARGE_INTEGER TimerDueTime;
+ NTSTATUS NtStatus;
+ //
+ // Avoid setting a destroyed timer.
+ //
+
+ if ( Timer->TimerHandle == NULL ) {
+ BrPrint(( BR_TIMER, "Setting a destroyed timer %lx\n", Timer));
+ return NERR_Success;
+ }
+
+ BrPrint(( BR_TIMER, "Setting timer %lx to %ld milliseconds, WorkerFounction %lx, Context: %lx\n", Timer, MillisecondsToExpire, WorkerFunction, Context));
+
+ //
+ // Figure out the timeout.
+ //
+
+ TimerDueTime.QuadPart = Int32x32To64( MillisecondsToExpire, -10000 );
+
+ BrInitializeWorkItem(&Timer->WorkItem, WorkerFunction, Context);
+
+ //
+ // Set the timer to go off when it expires.
+ //
+
+ NtStatus = NtSetTimer(Timer->TimerHandle,
+ &TimerDueTime,
+ BrTimerRoutine,
+ Timer,
+ FALSE,
+ 0,
+ NULL
+ );
+
+ if (!NT_SUCCESS(NtStatus)) {
+#if DBG
+ BrPrint(( BR_CRITICAL, "Unable to set browser timer expiration: %X (%lx)\n", NtStatus, Timer));
+ DbgBreakPoint();
+#endif
+
+ return(BrMapStatus(NtStatus));
+ }
+
+ return NERR_Success;
+
+
+}
+
+ VOID
+BrTimerRoutine(
+ IN PVOID TimerContext,
+ IN ULONG TImerLowValue,
+ IN LONG TimerHighValue
+ )
+{
+ PBROWSER_TIMER Timer = TimerContext;
+
+ BrPrint(( BR_TIMER, "Timer %lx fired\n", Timer));
+
+ BrQueueWorkItem(&Timer->WorkItem);
+}
diff --git a/private/net/svcdlls/browser2/server/bowqueue.h b/private/net/svcdlls/browser2/server/bowqueue.h
new file mode 100644
index 000000000..d0290a133
--- /dev/null
+++ b/private/net/svcdlls/browser2/server/bowqueue.h
@@ -0,0 +1,101 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ bowqueue.h
+
+Abstract:
+
+ Private header file for the NT Browser service. This file describes
+ the bowser thread queue interfaces.
+
+Author:
+
+ Larry Osterman (larryo) 15-Feb-1991
+
+Revision History:
+
+--*/
+
+#ifndef _BOWQUEUE_
+#define _BOWQUEUE_
+
+
+typedef
+VOID
+(*PBROWSER_WORKER_ROUTINE) (
+ IN PVOID Parameter
+ );
+
+
+typedef struct _WORKER_ITEM {
+ LIST_ENTRY List;
+ PBROWSER_WORKER_ROUTINE WorkerRoutine;
+ PVOID Parameter;
+ BOOLEAN Inserted;
+} WORKER_ITEM, *PWORKER_ITEM;
+
+typedef struct _BROWSER_TIMER {
+ HANDLE TimerHandle;
+ WORKER_ITEM WorkItem;
+} BROWSER_TIMER, *PBROWSER_TIMER;
+
+
+VOID
+BrWorkerThread(
+ IN PVOID Context
+ );
+
+
+VOID
+BrQueueWorkItem(
+ IN PWORKER_ITEM WorkItem
+ );
+
+NET_API_STATUS
+BrWorkerInitialization(
+ VOID
+ );
+
+VOID
+BrWorkerKillThreads(
+ VOID
+ );
+
+NET_API_STATUS
+BrWorkerTermination (
+ VOID
+ );
+
+NET_API_STATUS
+BrSetTimer(
+ IN PBROWSER_TIMER Timer,
+ IN ULONG MilliSecondsToExpire,
+ IN PBROWSER_WORKER_ROUTINE WorkerFunction,
+ IN PVOID Context
+ );
+
+NET_API_STATUS
+BrCancelTimer(
+ IN PBROWSER_TIMER Timer
+ );
+
+NET_API_STATUS
+BrDestroyTimer(
+ IN PBROWSER_TIMER Timer
+ );
+
+NET_API_STATUS
+BrCreateTimer(
+ IN PBROWSER_TIMER Timer
+ );
+
+#define BrInitializeWorkItem(Item, Routine, Context) \
+ (Item)->WorkerRoutine = (Routine); \
+ (Item)->Parameter = (Context); \
+ (Item)->Inserted = FALSE;
+
+
+#endif // ifdef _BOWQUEUE_
diff --git a/private/net/svcdlls/browser2/server/bowsvc.rc b/private/net/svcdlls/browser2/server/bowsvc.rc
new file mode 100644
index 000000000..ec011c4ee
--- /dev/null
+++ b/private/net/svcdlls/browser2/server/bowsvc.rc
@@ -0,0 +1,12 @@
+#include <windows.h>
+
+#include <ntverp.h>
+
+#define VER_FILETYPE VFT_DLL
+#define VER_FILESUBTYPE VFT2_UNKNOWN
+#define VER_FILEDESCRIPTION_STR "Computer Browser Service DLL"
+#define VER_INTERNALNAME_STR "browser.dll"
+#define VER_ORIGINALFILENAME_STR "browser.dll"
+
+#include "common.ver"
+
diff --git a/private/net/svcdlls/browser2/server/br.h b/private/net/svcdlls/browser2/server/br.h
new file mode 100644
index 000000000..7c6d539ec
--- /dev/null
+++ b/private/net/svcdlls/browser2/server/br.h
@@ -0,0 +1,119 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ br.h
+
+Abstract:
+
+ Private header file for the NT Browser service included by every
+ module of the Workstation service.
+
+Author:
+
+ Rita Wong (ritaw) 15-Feb-1991
+
+Revision History:
+
+--*/
+
+#ifndef _BR_INCLUDED_
+#define _BR_INCLUDED_
+
+
+#include <nt.h> // NT definitions
+#include <ntrtl.h> // NT runtime library definitions
+#include <nturtl.h>
+
+#include <windef.h> // Win32 type definitions
+#include <winbase.h> // Win32 base API prototypes
+#include <winsvc.h> // Win32 service control APIs
+
+#include <lmcons.h> // LAN Manager common definitions
+#include <lmerr.h> // LAN Manager network error definitions
+#include <lmsname.h> // LAN Manager service names
+#include <lmapibuf.h> // NetApiBufferFree
+#include <lmserver.h>
+
+#include <netlib.h> // LAN Man utility routines
+#include <netlibnt.h> // NetpNtStatusToApiStatus
+#include <netdebug.h> // NetpDbgPrint
+#include <tstring.h> // Transitional string functions
+#include <icanon.h> // I_Net canonicalize functions
+#include <align.h> // ROUND_UP_COUNT macro
+#include <services.h> // LM Extensions of service definitions
+#include <strarray.h>
+
+#include <rpc.h> // DataTypes and runtime APIs
+#include <rpcutil.h> // Prototypes for MIDL user functions
+#include <bowser.h> // Generated by the MIDL complier
+#include <winsvc.h>
+#include <srvann.h>
+#include <lmbrowsr.h>
+
+#include <ntddbrow.h>
+#include <brcommon.h> // Common browser routines.
+#include <rx.h>
+#include <rxserver.h>
+
+#include <brconst.h>
+#include "bowqueue.h"
+#include "brdomain.h"
+#include "browsnet.h"
+#include "browslst.h"
+#include "brutil.h"
+#include "brwan.h"
+#include "brmain.h"
+#include "brdevice.h"
+#include "brconfig.h"
+#include "browsdom.h"
+#include "brbackup.h"
+#include "brmaster.h"
+#include "srvenum.h"
+
+//
+// The following macros are used to establish the semantics needed
+// to do a return from within a try-finally clause. As a rule every
+// try clause must end with a label call try_exit. For example,
+//
+// try {
+// :
+// :
+//
+// try_exit: NOTHING;
+// } finally {
+//
+// :
+// :
+// }
+//
+// Every return statement executed inside of a try clause should use the
+// try_return macro. If the compiler fully supports the try-finally construct
+// then the macro should be
+//
+// #define try_return(S) { return(S); }
+//
+// If the compiler does not support the try-finally construct then the macro
+// should be
+//
+
+#define try_return(S) { S; goto try_exit; }
+
+
+
+
+#if DBG
+
+#define BrPrint(_x_) BrowserTrace _x_
+
+
+#else
+
+#define BrPrint(_x_)
+
+#endif // DBG
+
+
+#endif // ifdef _BR_INCLUDED_
diff --git a/private/net/svcdlls/browser2/server/brbackup.h b/private/net/svcdlls/browser2/server/brbackup.h
new file mode 100644
index 000000000..0ef998122
--- /dev/null
+++ b/private/net/svcdlls/browser2/server/brbackup.h
@@ -0,0 +1,73 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ brmain.h
+
+Abstract:
+
+ Private header file which defines the global data which is used for
+ communication between the service control handler and the
+ rest of the NT Workstation service.
+
+Author:
+
+ Rita Wong (ritaw) 06-May-1991
+
+Revision History:
+
+--*/
+
+#ifndef _BRBACKUP_INCLUDED_
+#define _BRBACKUP_INCLUDED_
+
+NET_API_STATUS
+BecomeBackup(
+ IN PNETWORK Network,
+ IN PVOID Context
+ );
+
+NET_API_STATUS
+BrBecomeBackup(
+ PDOMAIN_INFO DomainInfo
+ );
+
+NET_API_STATUS
+PostBecomeBackup(
+ PNETWORK Network
+ );
+
+NET_API_STATUS
+BrStopBackup (
+ IN PNETWORK Network
+ );
+
+NET_API_STATUS
+PostWaitForRoleChange (
+ PNETWORK Network
+ );
+
+NET_API_STATUS
+BrPostGetMasterAnnouncement (
+ PDOMAIN_INFO DomainInfo
+ );
+
+NET_API_STATUS
+BrStopMaster(
+ IN PNETWORK Network
+ );
+
+NET_API_STATUS
+StartBackupBrowserTimer(
+ IN PNETWORK Network
+ );
+
+NET_API_STATUS
+BackupBrowserTimerRoutine (
+ IN PVOID TimerContext
+ );
+
+#endif // ifndef _BRBACKUP_INCLUDED_
+
diff --git a/private/net/svcdlls/browser2/server/brconfig.c b/private/net/svcdlls/browser2/server/brconfig.c
new file mode 100644
index 000000000..8396b3af3
--- /dev/null
+++ b/private/net/svcdlls/browser2/server/brconfig.c
@@ -0,0 +1,656 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ brconfig.c
+
+Abstract:
+
+ This module contains the Browser service configuration routines.
+
+Author:
+
+ Rita Wong (ritaw) 22-May-1991
+
+Revision History:
+
+--*/
+
+#include "precomp.h"
+#pragma hdrstop
+
+
+
+//-------------------------------------------------------------------//
+// //
+// Global variables //
+// //
+//-------------------------------------------------------------------//
+
+//
+// Browser configuration information structure which holds the
+// computername, primary domain, browser config buffer, and a resource
+// to serialize access to the whole thing.
+//
+BRCONFIGURATION_INFO BrInfo = {0};
+
+BR_BROWSER_FIELDS BrFields[] = {
+
+ {WKSTA_KEYWORD_MAINTAINSRVLST, (LPDWORD) &BrInfo.MaintainServerList,
+ 1,(DWORD)-1, 0, TriValueType, 0, NULL},
+
+ {BROWSER_CONFIG_BACKUP_RECOVERY_TIME, &BrInfo.BackupBrowserRecoveryTime,
+ BACKUP_BROWSER_RECOVERY_TIME, 0, 0xffffffff, DWordType, 0, NULL},
+
+ {L"CacheHitLimit", &BrInfo.CacheHitLimit,
+// {BROWSER_CONFIG_CACHE_HIT_LIMIT, &BrInfo.CacheHitLimit,
+ CACHED_BROWSE_RESPONSE_HIT_LIMIT, 0, 0x100, DWordType, 0, NULL },
+
+ {L"CacheResponseSize", &BrInfo.NumberOfCachedResponses,
+// {BROWSER_CONFIG_CACHE_HIT_LIMIT, &BrInfo.CacheHitLimit,
+ CACHED_BROWSE_RESPONSE_LIMIT, 0, MAXULONG, DWordType, 0, NULL },
+
+ {L"QueryDriverFrequency", &BrInfo.DriverQueryFrequency,
+ BROWSER_QUERY_DRIVER_FREQUENCY, 0, 15*60, DWordType, 0, NULL },
+
+ {L"DirectHostBinding", (LPDWORD)&BrInfo.DirectHostBinding,
+ 0, 0, 0, MultiSzType, 0, NULL },
+
+ {L"UnboundBindings", (LPDWORD)&BrInfo.UnboundBindings,
+ 0, 0, 0, MultiSzType, 0, NULL },
+
+ {L"MasterPeriodicity", (LPDWORD)&BrInfo.MasterPeriodicity,
+ MASTER_PERIODICITY, 5*60, 0xffffffff/1000, DWordType, 0, BrChangeMasterPeriodicity },
+
+ {L"BackupPeriodicity", (LPDWORD)&BrInfo.BackupPeriodicity,
+ BACKUP_PERIODICITY, 5*60, 0xffffffff, DWordType, 0, NULL },
+
+#if DBG
+ {L"BrowserDebug", (LPDWORD) &BrInfo.BrowserDebug,
+ 0, 0, 0xffffffff,DWordType, 0, NULL},
+ {L"BrowserDebugLimit", (LPDWORD) &BrInfo.BrowserDebugFileLimit,
+ 10000*1024, 0, 0xffffffff,DWordType, 0, NULL},
+#endif
+
+ {NULL, NULL, 0, 0, 0, BooleanType, 0, NULL}
+
+ };
+
+
+ULONG
+NumberOfServerEnumerations = {0};
+
+ULONG
+NumberOfDomainEnumerations = {0};
+
+ULONG
+NumberOfOtherEnumerations = {0};
+
+ULONG
+NumberOfMissedGetBrowserListRequests = {0};
+
+CRITICAL_SECTION
+BrowserStatisticsLock = {0};
+
+#ifdef notdef // never called
+
+DWORD
+BrInAWorkgroup(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This function determines whether we are a member of a domain, or of
+ a workgroup. First it checks to make sure we're running on a Windows NT
+ system (otherwise we're obviously in a domain) and if so, queries LSA
+ to get the Primary domain SID, if this is NULL, we're in a workgroup.
+
+ If we fail for some random unexpected reason, we'll pretend we're in a
+ domain (it's more restrictive).
+
+Arguments:
+ None
+
+Return Value:
+
+ TRUE - We're in a workgroup
+ FALSE - We're in a domain
+
+--*/
+{
+ NT_PRODUCT_TYPE ProductType;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ LSA_HANDLE Handle;
+ NTSTATUS Status;
+ PPOLICY_PRIMARY_DOMAIN_INFO PolicyPrimaryDomainInfo = NULL;
+ DWORD Result = FALSE;
+
+
+ Status = RtlGetNtProductType(&ProductType);
+
+ if (!NT_SUCCESS(Status)) {
+ return FALSE;
+ }
+
+ if (ProductType == NtProductLanManNt) {
+ return(FALSE);
+ }
+
+ InitializeObjectAttributes(&ObjectAttributes, NULL, 0, 0, NULL);
+
+ Status = LsaOpenPolicy(NULL,
+ &ObjectAttributes,
+ POLICY_VIEW_LOCAL_INFORMATION,
+ &Handle);
+
+ if (!NT_SUCCESS(Status)) {
+ return FALSE;
+ }
+
+ Status = LsaQueryInformationPolicy(Handle, PolicyPrimaryDomainInformation,
+ (PVOID *) &PolicyPrimaryDomainInfo);
+
+ if (NT_SUCCESS(Status)) {
+
+ if (PolicyPrimaryDomainInfo->Sid == NULL) {
+ Result = TRUE;
+ }
+ else {
+ Result = FALSE;
+ }
+ }
+
+ if (PolicyPrimaryDomainInfo) {
+ LsaFreeMemory((PVOID)PolicyPrimaryDomainInfo);
+ }
+ LsaClose(Handle);
+
+ return(Result);
+}
+#endif // notdef
+
+
+NET_API_STATUS
+BrGetBrowserConfiguration(
+ VOID
+ )
+{
+ NET_API_STATUS status;
+ NT_PRODUCT_TYPE NtProductType;
+
+ //
+ // Initialize the resource for serializing access to configuration
+ // information.
+ //
+ InitializeCriticalSection(&BrInfo.ConfigCritSect);
+
+ //
+ // Lock config information structure for write access since we are
+ // initializing the data in the structure.
+ //
+ EnterCriticalSection( &BrInfo.ConfigCritSect );
+
+ //
+ // Set pointer to configuration fields structure
+ //
+ BrInfo.BrConfigFields = BrFields;
+
+ //
+ // Determine our product type.
+ //
+
+ RtlGetNtProductType(&NtProductType);
+
+ BrInfo.IsLanmanNt = (NtProductType == NtProductLanManNt);
+
+ //
+ // Now determine the primary domain controller for our domain.
+ //
+
+ {
+ if (NtProductType == NtProductLanManNt) {
+ LSA_HANDLE LsaHandle;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ NTSTATUS Status;
+ PPOLICY_LSA_SERVER_ROLE_INFO ServerRole;
+
+ InitializeObjectAttributes(&ObjectAttributes, NULL, 0, NULL, NULL);
+
+ Status = LsaOpenPolicy(NULL, &ObjectAttributes,
+ POLICY_VIEW_LOCAL_INFORMATION,
+ &LsaHandle);
+
+ if (!NT_SUCCESS(Status)) {
+
+ //
+ // The server may be unavailable if we have come up before
+ // the LSA server. In this case, assume we are not
+ // the primary until we are told differently.
+ //
+
+ if (Status != RPC_NT_SERVER_UNAVAILABLE) {
+
+ LeaveCriticalSection(&BrInfo.ConfigCritSect);
+
+ return(BrMapStatus(Status));
+ } else {
+ BrInfo.IsPrimaryDomainController = FALSE;
+ }
+
+ } else {
+
+ //
+ // we opened the LSA, so get our role now.
+ //
+
+
+ Status = LsaQueryInformationPolicy(LsaHandle,
+ PolicyLsaServerRoleInformation,
+ (PVOID)&ServerRole
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ LsaClose(LsaHandle);
+
+ LeaveCriticalSection(&BrInfo.ConfigCritSect);
+
+ return(BrMapStatus(Status));
+ }
+
+ LsaClose(LsaHandle);
+
+ //
+ // If we're running on the primary DC, then set that information
+ // up, otherwise ask the DC for its name.
+ //
+
+ if (ServerRole->LsaServerRole == PolicyServerRolePrimary) {
+ BrInfo.IsPrimaryDomainController = TRUE;
+ } else {
+ BrInfo.IsPrimaryDomainController = FALSE;
+ }
+
+ LsaFreeMemory( ServerRole );
+
+ }
+
+
+ } else {
+
+ //
+ // We're not NTAS, so we cannot be the domain controller.
+ //
+
+ BrInfo.IsPrimaryDomainController = FALSE;
+
+ }
+ }
+
+ //
+ // Read from the config file the browser configuration fields
+ //
+
+ status = BrReadBrowserConfigFields( TRUE );
+
+ if (status != NERR_Success) {
+ goto CloseConfigFile;
+ }
+
+ if (BrInfo.IsLanmanNt) {
+ BrInfo.MaintainServerList = 1;
+ }
+
+ //
+ // Leave config file open because we need to read transport names from it.
+ //
+ LeaveCriticalSection(&BrInfo.ConfigCritSect);
+
+ return NERR_Success;
+
+
+CloseConfigFile:
+
+ BrShutdownDgReceiver();
+
+ LeaveCriticalSection(&BrInfo.ConfigCritSect);
+ return status;
+}
+
+#define REPORT_KEYWORD_IGNORED( lptstrKeyword ) \
+ { \
+ LPWSTR SubString[1]; \
+ SubString[0] = lptstrKeyword; \
+ BrLogEvent(EVENT_BROWSER_ILLEGAL_CONFIG, NERR_Success, 1, SubString); \
+ NetpKdPrint(( \
+ "[Browser] *ERROR* Tried to set keyword '" FORMAT_LPTSTR \
+ "' with invalid value.\n" \
+ "This error is ignored.\n", \
+ lptstrKeyword )); \
+ }
+
+
+NET_API_STATUS
+BrReadBrowserConfigFields(
+ IN BOOL InitialCall
+ )
+/*++
+
+Routine Description:
+
+ This function assigns each browser/redir configuration field to the default
+ value if it is not specified in the configuration file or if the value
+ specified in the configuration file is invalid. Otherwise it overrides
+ the default value with the value found in the configuration file.
+
+Arguments:
+
+
+ InitialCall - True if this call was made during initialization
+
+Return Value:
+
+ None.
+
+--*/
+{
+ NET_API_STATUS status;
+ LPNET_CONFIG_HANDLE BrowserSection;
+ DWORD i;
+
+ LPTSTR KeywordValueBuffer;
+ DWORD KeywordValueStringLength;
+ DWORD KeywordValue;
+ DWORD OldKeywordValue;
+
+ //
+ // Open config file and get handle to the [LanmanBrowser] section
+ //
+
+ if ((status = NetpOpenConfigData(
+ &BrowserSection,
+ NULL, // Local
+ SECT_NT_BROWSER,
+ TRUE // want read-only access
+ )) != NERR_Success) {
+ return status;
+ }
+
+ for (i = 0; BrInfo.BrConfigFields[i].Keyword != NULL; i++) {
+ BOOL ParameterChanged = FALSE;
+
+ //
+ // Skip this parameter if it can't change dynamically and
+ // this isn't the initial call.
+ //
+
+ if ( !InitialCall && BrInfo.BrConfigFields[i].DynamicChangeRoutine == NULL ) {
+ continue;
+ }
+
+ switch (BrInfo.BrConfigFields[i].DataType) {
+
+ case MultiSzType:
+ status = NetpGetConfigTStrArray(
+ BrowserSection,
+ BrInfo.BrConfigFields[i].Keyword,
+ (LPTSTR_ARRAY *)(BrInfo.BrConfigFields[i].FieldPtr));
+ if ((status != NO_ERROR) && (status != NERR_CfgParamNotFound)) {
+ REPORT_KEYWORD_IGNORED( BrInfo.BrConfigFields[i].Keyword );
+ }
+ break;
+
+ case BooleanType:
+
+ status = NetpGetConfigBool(
+ BrowserSection,
+ BrInfo.BrConfigFields[i].Keyword,
+ BrInfo.BrConfigFields[i].Default,
+ (LPBOOL)(BrInfo.BrConfigFields[i].FieldPtr)
+ );
+
+ if ((status != NO_ERROR) && (status != NERR_CfgParamNotFound)) {
+
+ REPORT_KEYWORD_IGNORED( BrInfo.BrConfigFields[i].Keyword );
+
+ }
+
+ break;
+
+ case TriValueType:
+
+ //
+ // Assign default configuration value
+ //
+
+ *(BrInfo.BrConfigFields[i].FieldPtr) = BrInfo.BrConfigFields[i].Default;
+
+ if (NetpGetConfigValue(
+ BrowserSection,
+ BrInfo.BrConfigFields[i].Keyword,
+ &KeywordValueBuffer
+ ) != NERR_Success) {
+ continue;
+ }
+
+ KeywordValueStringLength = STRLEN(KeywordValueBuffer);
+
+ if (STRICMP(KeywordValueBuffer, KEYWORD_YES) == 0) {
+ *(BrInfo.BrConfigFields[i].FieldPtr) = 1;
+ } else if (STRICMP(KeywordValueBuffer, KEYWORD_TRUE) == 0) {
+ *(BrInfo.BrConfigFields[i].FieldPtr) = 1;
+ } else if (STRICMP(KeywordValueBuffer, KEYWORD_NO) == 0) {
+ *(BrInfo.BrConfigFields[i].FieldPtr) = (DWORD) -1;
+ } else if (STRICMP(KeywordValueBuffer, KEYWORD_FALSE) == 0) {
+ *(BrInfo.BrConfigFields[i].FieldPtr) = (DWORD) -1;
+ } else if (STRICMP(KeywordValueBuffer, TEXT("AUTO")) == 0) {
+ *(BrInfo.BrConfigFields[i].FieldPtr) = 0;
+ }
+ else {
+ REPORT_KEYWORD_IGNORED( BrInfo.BrConfigFields[i].Keyword );
+ }
+
+ NetApiBufferFree(KeywordValueBuffer);
+
+ break;
+
+
+ case DWordType:
+
+ OldKeywordValue = *(LPDWORD)BrInfo.BrConfigFields[i].FieldPtr;
+ if (NetpGetConfigDword(
+ BrowserSection,
+ BrInfo.BrConfigFields[i].Keyword,
+ BrInfo.BrConfigFields[i].Default,
+ (LPDWORD)(BrInfo.BrConfigFields[i].FieldPtr)
+ ) != NERR_Success) {
+ continue;
+ }
+
+ KeywordValue = *(LPDWORD)BrInfo.BrConfigFields[i].FieldPtr;
+
+ //
+ // Don't allow too large or small a value.
+ //
+
+ if (KeywordValue < BrInfo.BrConfigFields[i].Minimum) {
+ BrPrint(( BR_CRITICAL, "%ws value out of range %lu (%lu-%lu)\n",
+ BrInfo.BrConfigFields[i].Keyword, KeywordValue,
+ BrInfo.BrConfigFields[i].Minimum,
+ BrInfo.BrConfigFields[i].Maximum
+ ));
+ KeywordValue =
+ *(LPDWORD)BrInfo.BrConfigFields[i].FieldPtr =
+ BrInfo.BrConfigFields[i].Minimum;
+ }
+
+ if (KeywordValue > BrInfo.BrConfigFields[i].Maximum) {
+ BrPrint(( BR_CRITICAL, "%ws value out of range %lu (%lu-%lu)\n",
+ BrInfo.BrConfigFields[i].Keyword, KeywordValue,
+ BrInfo.BrConfigFields[i].Minimum,
+ BrInfo.BrConfigFields[i].Maximum
+ ));
+ KeywordValue =
+ *(LPDWORD)BrInfo.BrConfigFields[i].FieldPtr =
+ BrInfo.BrConfigFields[i].Maximum;
+ }
+
+ //
+ // Test if the parameter has actually changed
+ //
+
+ if ( OldKeywordValue != KeywordValue ) {
+ ParameterChanged = TRUE;
+ }
+
+ break;
+
+ default:
+ NetpAssert(FALSE);
+
+ }
+
+ //
+ // If this is a dynamic parameter change,
+ // and this isn't the initial call.
+ // notify that this parameter changed.
+ //
+
+ if ( !InitialCall && ParameterChanged ) {
+ BrInfo.BrConfigFields[i].DynamicChangeRoutine();
+ }
+ }
+
+ status = NetpCloseConfigData(BrowserSection);
+
+ if (BrInfo.DirectHostBinding != NULL &&
+ !NetpIsTStrArrayEmpty(BrInfo.DirectHostBinding)) {
+ BrPrint(( BR_INIT,"DirectHostBinding length: %ld\n",NetpTStrArrayEntryCount(BrInfo.DirectHostBinding)));
+
+ if (NetpTStrArrayEntryCount(BrInfo.DirectHostBinding) % 2 != 0) {
+ status = ERROR_INVALID_PARAMETER;
+ }
+ }
+
+ return status;
+}
+
+
+ VOID
+BrDeleteConfiguration (
+ DWORD BrInitState
+ )
+{
+
+ if (BrInfo.DirectHostBinding != NULL) {
+ NetApiBufferFree(BrInfo.DirectHostBinding);
+ }
+
+ if (BrInfo.UnboundBindings != NULL) {
+ NetApiBufferFree(BrInfo.UnboundBindings);
+ }
+
+ DeleteCriticalSection(&BrInfo.ConfigCritSect);
+
+ UNREFERENCED_PARAMETER(BrInitState);
+}
+
+#if DBG
+NET_API_STATUS
+BrUpdateDebugInformation(
+ IN LPWSTR SystemKeyName,
+ IN LPWSTR ValueName,
+ IN LPTSTR TransportName,
+ IN LPTSTR ServerName OPTIONAL,
+ IN DWORD ServiceStatus
+ )
+/*++
+
+Routine Description:
+
+ This routine will stick debug information in the registry about the last
+ time the browser retrieved information from the remote server.
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ WCHAR TotalKeyName[MAX_PATH];
+ ULONG Disposition;
+ HKEY Key;
+ ULONG Status;
+ SYSTEMTIME LocalTime;
+ WCHAR LastUpdateTime[100];
+
+ //
+ // Build the key name:
+ //
+ // HKEY_LOCAL_MACHINE:System\CurrentControlSet\Services\Browser\Debug\<Transport>\SystemKeyName
+ //
+
+ wcscpy(TotalKeyName, L"System\\CurrentControlSet\\Services\\Browser\\Debug");
+
+ wcscat(TotalKeyName, TransportName);
+
+ wcscat(TotalKeyName, L"\\");
+
+ wcscat(TotalKeyName, SystemKeyName);
+
+ if ((Status = RegCreateKeyEx(HKEY_LOCAL_MACHINE, TotalKeyName, 0,
+ L"BrowserDebugInformation",
+ REG_OPTION_NON_VOLATILE,
+ KEY_WRITE,
+ NULL,
+ &Key,
+ &Disposition)) != ERROR_SUCCESS) {
+ BrPrint(( BR_CRITICAL,"Unable to create key to log debug information: %lx\n", Status));
+ return Status;
+ }
+
+ if (ARGUMENT_PRESENT(ServerName)) {
+ if ((Status = RegSetValueEx(Key, ValueName, 0, REG_SZ, (LPBYTE)ServerName, (wcslen(ServerName)+1) * sizeof(WCHAR))) != ERROR_SUCCESS) {
+ BrPrint(( BR_CRITICAL,
+ "Unable to set value of ServerName value to %ws: %lx\n",
+ ServerName, Status));
+ RegCloseKey(Key);
+ return Status;
+ }
+ } else {
+ if ((Status = RegSetValueEx(Key, ValueName, 0, REG_DWORD, (LPBYTE)&ServiceStatus, sizeof(ULONG))) != ERROR_SUCCESS) {
+ BrPrint(( BR_CRITICAL,"Unable to set value of ServerName value to %ws: %lx\n", ServerName, Status));
+ RegCloseKey(Key);
+ return Status;
+ }
+ }
+
+
+ GetLocalTime(&LocalTime);
+
+ swprintf(LastUpdateTime, L"%d/%d/%d %d:%d:%d:%d", LocalTime.wDay,
+ LocalTime.wMonth,
+ LocalTime.wYear,
+ LocalTime.wHour,
+ LocalTime.wMinute,
+ LocalTime.wSecond,
+ LocalTime.wMilliseconds);
+
+ if ((Status = RegSetValueEx(Key, L"LastUpdateTime", 0, REG_SZ, (LPBYTE)&LastUpdateTime, (wcslen(LastUpdateTime) + 1)*sizeof(WCHAR))) != ERROR_SUCCESS) {
+ BrPrint(( BR_CRITICAL,"Unable to set value of LastUpdateTime value to %s: %lx\n", LastUpdateTime, Status));
+ RegCloseKey(Key);
+ return Status;
+ }
+
+ RegCloseKey(Key);
+}
+
+#endif
diff --git a/private/net/svcdlls/browser2/server/brconfig.h b/private/net/svcdlls/browser2/server/brconfig.h
new file mode 100644
index 000000000..ca4d4f309
--- /dev/null
+++ b/private/net/svcdlls/browser2/server/brconfig.h
@@ -0,0 +1,128 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ brconfig.h
+
+Abstract:
+
+ Private header file to be included by Workstation service modules that
+ need to load Workstation configuration information.
+
+Author:
+
+ Rita Wong (ritaw) 22-May-1991
+
+Revision History:
+
+--*/
+
+
+#ifndef _BRCONFIG_INCLUDED_
+#define _BRCONFIG_INCLUDED_
+
+#define BROWSER_CONFIG_VERSION_MAJOR 3
+#define BROWSER_CONFIG_VERSION_MINOR 10
+
+typedef enum _DATATYPE {
+ BooleanType,
+ DWordType,
+ MultiSzType,
+ TriValueType // Yes, No, Auto
+} DATATYPE, *PDATATYPE;
+
+typedef struct _BR_BROWSER_FIELDS {
+ LPTSTR Keyword;
+ LPDWORD FieldPtr;
+ DWORD Default;
+ DWORD Minimum;
+ DWORD Maximum;
+ DATATYPE DataType;
+ DWORD Parmnum;
+ VOID (*DynamicChangeRoutine) ( VOID );
+} BR_BROWSER_FIELDS, *PBR_BROWSER_FIELDS;
+
+//
+// Configuration information. Reading and writing to this global
+// structure requires that the resource be acquired first.
+//
+
+typedef struct _BRCONFIGURATION_INFO {
+
+ CRITICAL_SECTION ConfigCritSect; // To serialize access to config fields.
+
+ DWORD MaintainServerList; // -1, 0, or 1 (No, Auto, Yes)
+ DWORD BackupBrowserRecoveryTime;
+ DWORD CacheHitLimit; // Browse response Cache hit limit.
+ DWORD NumberOfCachedResponses; // Browse response cache size.
+ DWORD DriverQueryFrequency; // Browser driver query frequency.
+ DWORD MasterPeriodicity; // Master announce frequency (seconds)
+ DWORD BackupPeriodicity; // Backup scavange frequency (seconds)
+ BOOL IsLanmanNt; // True if is on LM NT machine
+ BOOL IsPrimaryDomainController;// True if machine is PDC of the primary domain
+ LPTSTR_ARRAY DirectHostBinding; // Direct host equivalence map.
+ LPTSTR_ARRAY UnboundBindings; // Redir bindings that aren't bound to browser
+ PBR_BROWSER_FIELDS BrConfigFields;
+#if DBG
+ DWORD BrowserDebug; // If non zero, indicates debug info.
+ DWORD BrowserDebugFileLimit; // File size limit on browser log size.
+#endif
+} BRCONFIGURATION_INFO, *PBRCONFIGURATION_INFO;
+
+extern BRCONFIGURATION_INFO BrInfo;
+
+#define BRBUF BrInfo.BrConfigBuf
+
+extern
+ULONG
+NumberOfServerEnumerations;
+
+extern
+ULONG
+NumberOfDomainEnumerations;
+
+extern
+ULONG
+NumberOfOtherEnumerations;
+
+extern
+ULONG
+NumberOfMissedGetBrowserListRequests;
+
+extern
+CRITICAL_SECTION
+BrowserStatisticsLock;
+
+
+
+NET_API_STATUS
+BrGetBrowserConfiguration(
+ VOID
+ );
+
+VOID
+BrDeleteConfiguration (
+ DWORD BrInitState
+ );
+
+NET_API_STATUS
+BrReadBrowserConfigFields(
+ BOOL InitialCall
+ );
+
+
+#if DEVL
+NET_API_STATUS
+BrUpdateDebugInformation(
+ IN LPWSTR SystemKeyName,
+ IN LPWSTR ValueName,
+ IN LPTSTR TransportName,
+ IN LPTSTR ServerName OPTIONAL,
+ IN DWORD ServiceStatus
+ );
+
+#endif
+
+#endif
diff --git a/private/net/svcdlls/browser2/server/brconst.h b/private/net/svcdlls/browser2/server/brconst.h
new file mode 100644
index 000000000..4f55ebfb2
--- /dev/null
+++ b/private/net/svcdlls/browser2/server/brconst.h
@@ -0,0 +1,98 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ brconst.h
+
+Abstract:
+
+ Private header file which defines assorted mainifest constants for
+ the browser service
+
+Author:
+
+ Rita Wong (ritaw) 06-May-1991
+
+Revision History:
+
+--*/
+
+#ifndef _BRCONST_INCLUDED_
+#define _BRCONST_INCLUDED_
+
+//
+// Age the masters server list cache every MASTER_PERIODICITY times.
+//
+#define MASTER_PERIODICITY 12*60
+
+//
+// Refresh the backup browsers server list every BACKUP_PERIODICITY
+//
+#define BACKUP_PERIODICITY 12*60
+
+//
+// Buffer size used for GetBrowserServerList responses (in bytes).
+//
+
+#define BROWSER_BACKUP_LIST_RESPONSE_SIZE 400
+
+//
+// If we failed to retrieve the server list, retry in BACKUP_ERROR_PERIODICITY
+// seconds
+//
+
+#define BACKUP_ERROR_PERIODICITY 30
+
+//
+// If we failed to retrieve the server (or domain) list BACKUP_ERROR_FAILURE
+// times in a row, stop being a backup browser.
+//
+
+#define BACKUP_ERROR_FAILURE 5
+
+//
+// Once we have stopped being a backup browser, we will not become a backup
+// until at least BACKUP_BROWSER_RECOVERY_TIME milliseconds have elapsed.
+//
+
+#define BACKUP_BROWSER_RECOVERY_TIME 30*60*1000
+
+//
+// If we receive fewer than this # of domains or servers, we treat it as an
+// error.
+//
+
+#define BROWSER_MINIMUM_DOMAIN_NUMBER 1
+#define BROWSER_MINIMUM_SERVER_NUMBER 2
+
+//
+// Wait for this many minutes after each failed promotion before
+// continuing.
+//
+
+#define FAILED_PROMOTION_PERIODICITY 5*60
+
+//
+// Run the master browser timer for 3 times (45 minutes) before
+// tossing the list in the service.
+//
+
+#define MASTER_BROWSER_LAN_TIMER_LIMIT 3
+
+//
+// A browse request has to have a hit count of at least this value before
+// it is retained in the cache.
+//
+
+#define CACHED_BROWSE_RESPONSE_HIT_LIMIT 1
+
+//
+// The maximum number of cache responses we will allow.
+//
+
+#define CACHED_BROWSE_RESPONSE_LIMIT 10
+
+#endif // ifndef _BRCONST_INCLUDED_
+
diff --git a/private/net/svcdlls/browser2/server/brdevice.c b/private/net/svcdlls/browser2/server/brdevice.c
new file mode 100644
index 000000000..caff7451e
--- /dev/null
+++ b/private/net/svcdlls/browser2/server/brdevice.c
@@ -0,0 +1,896 @@
+
+
+/*++
+
+Copyright (c) 1991-1992 Microsoft Corporation
+
+Module Name:
+
+ brdevice.c
+
+Abstract:
+
+ This module contains the support routines for the APIs that call
+ into the browser or the datagram receiver.
+
+Author:
+
+ Rita Wong (ritaw) 20-Feb-1991
+ Larry Osterman (larryo) 23-Mar-1992
+
+Revision History:
+
+--*/
+
+#include "precomp.h"
+#pragma hdrstop
+
+//-------------------------------------------------------------------//
+// //
+// Local Function Prototypes //
+// //
+//-------------------------------------------------------------------//
+
+
+//-------------------------------------------------------------------//
+// //
+// Global variables //
+// //
+//-------------------------------------------------------------------//
+
+//
+// Handle to the Datagram Receiver DD
+//
+HANDLE BrDgReceiverDeviceHandle = NULL;
+
+VOID
+CompleteAsyncBrowserIoControl(
+ IN PVOID ApcContext,
+ IN PIO_STATUS_BLOCK IoStatusBlock,
+ IN ULONG Reserved
+ );
+
+NET_API_STATUS
+BrOpenDgReceiver (
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This routine opens the NT LAN Man Datagram Receiver driver.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NTSTATUS ntstatus;
+
+ UNICODE_STRING DeviceName;
+
+ IO_STATUS_BLOCK IoStatusBlock;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+
+
+ //
+ // Open the redirector device.
+ //
+ RtlInitUnicodeString(&DeviceName, DD_BROWSER_DEVICE_NAME_U);
+
+ InitializeObjectAttributes(
+ &ObjectAttributes,
+ &DeviceName,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL
+ );
+
+ ntstatus = NtOpenFile(
+ &BrDgReceiverDeviceHandle,
+ SYNCHRONIZE,
+ &ObjectAttributes,
+ &IoStatusBlock,
+ 0,
+ 0
+ );
+
+ if (NT_SUCCESS(ntstatus)) {
+ ntstatus = IoStatusBlock.Status;
+ }
+
+ if (! NT_SUCCESS(ntstatus)) {
+ BrPrint(( BR_CRITICAL,"NtOpenFile browser driver failed: 0x%08lx\n",
+ ntstatus));
+ }
+
+ return NetpNtStatusToApiStatus(ntstatus);
+}
+
+
+
+VOID
+BrShutdownDgReceiver(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This routine close the LAN Man Redirector device.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ IO_STATUS_BLOCK IoSb;
+
+ //
+ // Cancel the I/O operations outstanding on the browser.
+ //
+
+ NtCancelIoFile(BrDgReceiverDeviceHandle, &IoSb);
+
+}
+
+
+//
+// Retreive the list of bound transports from the bowser driver.
+//
+
+NET_API_STATUS
+BrGetTransportList(
+ OUT PLMDR_TRANSPORT_LIST *TransportList
+ )
+{
+ NET_API_STATUS Status;
+ LMDR_REQUEST_PACKET RequestPacket;
+
+ //
+ // If we have a previous buffer that was too small, free it up.
+ //
+
+ RequestPacket.Version = LMDR_REQUEST_PACKET_VERSION_DOM;
+
+ RequestPacket.Type = EnumerateXports;
+
+ RtlInitUnicodeString(&RequestPacket.TransportName, NULL);
+ RtlInitUnicodeString(&RequestPacket.EmulatedDomainName, NULL);
+
+ Status = DeviceControlGetInfo(
+ BrDgReceiverDeviceHandle,
+ IOCTL_LMDR_ENUMERATE_TRANSPORTS,
+ &RequestPacket,
+ sizeof(RequestPacket),
+ (LPVOID *)TransportList,
+ 0xffffffff,
+ 4096,
+ NULL
+ );
+
+ return Status;
+}
+
+ NET_API_STATUS
+BrAnnounceDomain(
+ IN PNETWORK Network,
+ IN ULONG Periodicity
+ )
+{
+ NET_API_STATUS Status;
+ UCHAR AnnounceBuffer[sizeof(BROWSE_ANNOUNCE_PACKET)+LM20_CNLEN+1];
+ PBROWSE_ANNOUNCE_PACKET Announcement = (PBROWSE_ANNOUNCE_PACKET )AnnounceBuffer;
+
+ //
+ // We don't announce domains on direct host IPX.
+ //
+
+ if (Network->Flags & NETWORK_IPX) {
+ return NERR_Success;
+ }
+
+ Announcement->BrowseType = WkGroupAnnouncement;
+
+ Announcement->BrowseAnnouncement.Periodicity = Periodicity;
+
+ Announcement->BrowseAnnouncement.UpdateCount = 0;
+
+ Announcement->BrowseAnnouncement.VersionMajor = BROWSER_CONFIG_VERSION_MAJOR;
+
+ Announcement->BrowseAnnouncement.VersionMinor = BROWSER_CONFIG_VERSION_MINOR;
+
+ Announcement->BrowseAnnouncement.Type = SV_TYPE_DOMAIN_ENUM | SV_TYPE_NT;
+
+ if (Network->DomainInfo->IsPrimaryDomainController) {
+ Announcement->BrowseAnnouncement.Type |= SV_TYPE_DOMAIN_CTRL;
+ }
+
+ lstrcpyA(Announcement->BrowseAnnouncement.ServerName, Network->DomainInfo->DomOemDomainName);
+
+ lstrcpyA(Announcement->BrowseAnnouncement.Comment, Network->DomainInfo->DomOemComputerName );
+
+ Status = SendDatagram(BrDgReceiverDeviceHandle,
+ &Network->NetworkName,
+ &Network->DomainInfo->DomUnicodeDomainNameString,
+ Network->DomainInfo->DomUnicodeDomainName,
+ DomainAnnouncement,
+ Announcement,
+ FIELD_OFFSET(BROWSE_ANNOUNCE_PACKET, BrowseAnnouncement.Comment)+
+ Network->DomainInfo->DomOemComputerNameLength+sizeof(UCHAR)
+ );
+
+ if (Status != NERR_Success) {
+
+ BrPrint(( BR_CRITICAL,
+ "%ws: Unable to announce domain for network %wZ: %X\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ &Network->NetworkName,
+ Status));
+
+ }
+
+ return Status;
+
+}
+
+
+
+ NET_API_STATUS
+BrUpdateBrowserStatus (
+ IN PNETWORK Network,
+ IN DWORD ServiceStatus
+ )
+{
+ NET_API_STATUS Status;
+ UCHAR PacketBuffer[sizeof(LMDR_REQUEST_PACKET)+(LM20_CNLEN+1)*sizeof(WCHAR)];
+ PLMDR_REQUEST_PACKET RequestPacket = (PLMDR_REQUEST_PACKET)PacketBuffer;
+
+
+ RequestPacket->Version = LMDR_REQUEST_PACKET_VERSION_DOM;
+
+ RequestPacket->TransportName = Network->NetworkName;
+ RequestPacket->EmulatedDomainName = Network->DomainInfo->DomUnicodeDomainNameString;
+
+ RequestPacket->Parameters.UpdateStatus.NewStatus = ServiceStatus;
+
+ RequestPacket->Parameters.UpdateStatus.IsLanmanNt = BrInfo.IsLanmanNt;
+
+ // RequestPacket->Parameters.UpdateStatus.IsMemberDomain = TRUE; // Not used
+
+ RequestPacket->Parameters.UpdateStatus.IsPrimaryDomainController = Network->DomainInfo->IsPrimaryDomainController;
+
+ RequestPacket->Parameters.UpdateStatus.IsDomainMaster = Network->DomainInfo->IsDomainMasterBrowser;
+
+ RequestPacket->Parameters.UpdateStatus.MaintainServerList = (BrInfo.MaintainServerList == 1);
+
+ //
+ // Tell the bowser the number of servers in the server table.
+ //
+
+ RequestPacket->Parameters.UpdateStatus.NumberOfServersInTable =
+ NumberInterimServerListElements(&Network->BrowseTable) +
+ NumberInterimServerListElements(&Network->DomainList) +
+ Network->TotalBackupServerListEntries +
+ Network->TotalBackupDomainListEntries;
+
+ //
+ // This is a simple IoControl - It just updates the status.
+ //
+
+ Status = BrDgReceiverIoControl(BrDgReceiverDeviceHandle,
+ IOCTL_LMDR_UPDATE_STATUS,
+ RequestPacket,
+ sizeof(LMDR_REQUEST_PACKET),
+ NULL,
+ 0,
+ NULL);
+
+ return Status;
+}
+
+ NET_API_STATUS
+BrIssueAsyncBrowserIoControl(
+ IN PNETWORK Network,
+ IN ULONG ControlCode,
+ IN PBROWSER_WORKER_ROUTINE CompletionRoutine,
+ IN PVOID OptionalParameter
+ )
+/*++
+
+Routine Description:
+
+ Issue an asynchronous Io Control to the browser. Call the specified
+ completion routine when the IO finishes.
+
+Arguments:
+
+ Network - Network the function applies to
+
+ ControlCode - IoControl function code
+
+ CompletionRoutine - Routine to be called when the IO finishes.
+
+ OptionalParameter - Function code specific information
+
+Return Value:
+
+ Status of the operation.
+
+--*/
+
+{
+ ULONG PacketSize;
+ PLMDR_REQUEST_PACKET RequestPacket = NULL;
+ NTSTATUS NtStatus;
+
+ PBROWSERASYNCCONTEXT Context = NULL;
+
+ PacketSize = sizeof(LMDR_REQUEST_PACKET) +
+ MAXIMUM_FILENAME_LENGTH * sizeof(WCHAR) +
+ Network->NetworkName.MaximumLength +
+ Network->DomainInfo->DomUnicodeDomainNameString.Length;
+
+ RequestPacket = MIDL_user_allocate(PacketSize);
+
+ if (RequestPacket == NULL) {
+ return(ERROR_NOT_ENOUGH_MEMORY);
+ }
+
+ Context = MIDL_user_allocate(sizeof(BROWSERASYNCCONTEXT));
+
+ if (Context == NULL) {
+
+ MIDL_user_free(RequestPacket);
+
+ return(ERROR_NOT_ENOUGH_MEMORY);
+
+ }
+
+ RequestPacket->Version = LMDR_REQUEST_PACKET_VERSION_DOM;
+
+ //
+ // Set level to FALSE to indicate that find master should not initiate
+ // a findmaster request, simply complete when a new master announces
+ // itself.
+ //
+
+ RequestPacket->Level = 0;
+
+ //
+ // Stick the name of the transport associated with this request at the
+ // end of the request packet.
+ //
+
+ RequestPacket->TransportName.MaximumLength = Network->NetworkName.MaximumLength;
+
+ RequestPacket->TransportName.Buffer = (PWSTR)((PCHAR)RequestPacket+sizeof(LMDR_REQUEST_PACKET)+(MAXIMUM_FILENAME_LENGTH*sizeof(WCHAR)));
+
+ RtlCopyUnicodeString(&RequestPacket->TransportName, &Network->NetworkName);
+
+ //
+ // Stick the domain name associated with this request at the
+ // end of the request packet.
+ //
+
+ RequestPacket->EmulatedDomainName.MaximumLength = Network->DomainInfo->DomUnicodeDomainNameString.Length;
+ RequestPacket->EmulatedDomainName.Length = 0;
+ RequestPacket->EmulatedDomainName.Buffer = (PWSTR)(((PCHAR)RequestPacket->TransportName.Buffer) + RequestPacket->TransportName.MaximumLength);
+
+ RtlAppendUnicodeToString(&RequestPacket->EmulatedDomainName, Network->DomainInfo->DomUnicodeDomainName );
+
+
+ //
+ // Do opcode dependent initialization of the request packet.
+ //
+
+ switch ( ControlCode ) {
+ case IOCTL_LMDR_NEW_MASTER_NAME:
+ if (ARGUMENT_PRESENT(OptionalParameter)) {
+ LPWSTR MasterName = (LPWSTR) OptionalParameter;
+
+ RequestPacket->Parameters.GetMasterName.MasterNameLength =
+ wcslen(MasterName+2)*sizeof(WCHAR);
+
+ wcscpy( RequestPacket->Parameters.GetMasterName.Name, MasterName+2);
+
+ } else {
+
+ RequestPacket->Parameters.GetMasterName.MasterNameLength = 0;
+
+ }
+ break;
+ }
+
+
+ BrInitializeWorkItem(&Context->WorkItem, CompletionRoutine, Context);
+
+ Context->Network = Network;
+
+ Context->RequestPacket = RequestPacket;
+
+ NtStatus = NtDeviceIoControlFile(BrDgReceiverDeviceHandle,
+ NULL,
+ CompleteAsyncBrowserIoControl,
+ Context,
+ &Context->IoStatusBlock,
+ ControlCode,
+ RequestPacket,
+ PacketSize,
+ RequestPacket,
+ sizeof(LMDR_REQUEST_PACKET)+MAXIMUM_FILENAME_LENGTH*sizeof(WCHAR)
+ );
+
+ if (!NT_SUCCESS(NtStatus)) {
+
+ BrPrint(( BR_CRITICAL,
+ "Unable to issue browser IoControl: %X\n", NtStatus));
+
+ MIDL_user_free(RequestPacket);
+
+ MIDL_user_free(Context);
+
+
+ return(BrMapStatus(NtStatus));
+ }
+
+ return NERR_Success;
+
+}
+
+ VOID
+CompleteAsyncBrowserIoControl(
+ IN PVOID ApcContext,
+ IN PIO_STATUS_BLOCK IoStatusBlock,
+ IN ULONG Reserved
+ )
+{
+
+ PBROWSERASYNCCONTEXT Context = ApcContext;
+
+ //
+ // If this request was canceled, we're stopping the browser, so we
+ // want to clean up our allocated pool. In addition, don't bother
+ // calling into the routine - the threads are gone by now.
+ //
+
+ if (IoStatusBlock->Status == STATUS_CANCELLED) {
+
+ MIDL_user_free(Context->RequestPacket);
+
+ MIDL_user_free(Context);
+
+ return;
+
+ }
+
+ //
+ // Timestamp when this request was completed. This allows us to tell
+ // where a request spent its time.
+ //
+
+ NtQueryPerformanceCounter(&Context->TimeCompleted, NULL);
+
+ BrQueueWorkItem(&Context->WorkItem);
+
+}
+
+ NET_API_STATUS
+BrGetLocalBrowseList(
+ IN PNETWORK Network,
+ IN LPWSTR DomainName OPTIONAL,
+ IN ULONG Level,
+ IN ULONG ServerType,
+ OUT PVOID *ServerList,
+ OUT PULONG EntriesRead,
+ OUT PULONG TotalEntries
+ )
+{
+ NET_API_STATUS status;
+ PLMDR_REQUEST_PACKET Drp; // Datagram receiver request packet
+ ULONG DrpSize;
+ ULONG DomainNameSize;
+
+ //
+ // Allocate the request packet large enough to hold the variable length
+ // domain name.
+ //
+
+ DomainNameSize = ARGUMENT_PRESENT(DomainName) ? (wcslen(DomainName) + 1) * sizeof(WCHAR) : 0;
+
+
+ DrpSize = sizeof(LMDR_REQUEST_PACKET) +
+ DomainNameSize +
+ Network->NetworkName.MaximumLength +
+ Network->DomainInfo->DomUnicodeDomainNameString.Length;
+
+ if ((Drp = MIDL_user_allocate(DrpSize)) == NULL) {
+
+ return GetLastError();
+ }
+
+ //
+ // Set up request packet. Output buffer structure is of enumerate
+ // servers type.
+ //
+
+ Drp->Version = LMDR_REQUEST_PACKET_VERSION_DOM;
+ Drp->Type = EnumerateServers;
+
+ Drp->Level = Level;
+
+ Drp->Parameters.EnumerateServers.ServerType = ServerType;
+ Drp->Parameters.EnumerateServers.ResumeHandle = 0;
+
+ //
+ // Copy the transport name into the buffer.
+ //
+ Drp->TransportName.Buffer = (PWSTR)((PCHAR)Drp+
+ sizeof(LMDR_REQUEST_PACKET) +
+ DomainNameSize);
+
+ Drp->TransportName.MaximumLength = Network->NetworkName.MaximumLength;
+
+ RtlCopyUnicodeString(&Drp->TransportName, &Network->NetworkName);
+
+ //
+ // Copy the enumalated domain name into the buffer.
+ //
+
+ Drp->EmulatedDomainName.MaximumLength = Network->DomainInfo->DomUnicodeDomainNameString.Length;
+ Drp->EmulatedDomainName.Length = 0;
+ Drp->EmulatedDomainName.Buffer = (PWSTR)(((PCHAR)Drp->TransportName.Buffer) + Drp->TransportName.MaximumLength);
+
+ RtlAppendUnicodeToString(&Drp->EmulatedDomainName, Network->DomainInfo->DomUnicodeDomainName );
+
+ //
+ // Copy the queried domain name into the buffer.
+ //
+
+ if (ARGUMENT_PRESENT(DomainName)) {
+
+ Drp->Parameters.EnumerateServers.DomainNameLength = DomainNameSize - sizeof(WCHAR);
+ wcscpy(Drp->Parameters.EnumerateServers.DomainName, DomainName);
+
+ } else {
+ Drp->Parameters.EnumerateServers.DomainNameLength = 0;
+ Drp->Parameters.EnumerateServers.DomainName[0] = '\0';
+ }
+
+ //
+ // Ask the datagram receiver to enumerate the servers
+ //
+
+ status = DeviceControlGetInfo(
+ BrDgReceiverDeviceHandle,
+ IOCTL_LMDR_ENUMERATE_SERVERS,
+ Drp,
+ DrpSize,
+ ServerList,
+ 0xffffffff,
+ 4096,
+ NULL
+ );
+
+ *EntriesRead = Drp->Parameters.EnumerateServers.EntriesRead;
+ *TotalEntries = Drp->Parameters.EnumerateServers.TotalEntries;
+
+ (void) MIDL_user_free(Drp);
+
+ return status;
+
+}
+
+NET_API_STATUS
+BrRemoveOtherDomain(
+ IN PNETWORK Network,
+ IN LPTSTR ServerName
+ )
+{
+ NET_API_STATUS Status;
+ UCHAR PacketBuffer[sizeof(LMDR_REQUEST_PACKET)+(LM20_CNLEN+1)*sizeof(WCHAR)];
+ PLMDR_REQUEST_PACKET RequestPacket = (PLMDR_REQUEST_PACKET)PacketBuffer;
+
+
+ RequestPacket->Version = LMDR_REQUEST_PACKET_VERSION_DOM;
+
+ RequestPacket->TransportName = Network->NetworkName;
+ RequestPacket->EmulatedDomainName = Network->DomainInfo->DomUnicodeDomainNameString;
+
+ RequestPacket->Parameters.AddDelName.DgReceiverNameLength = STRLEN(ServerName)*sizeof(TCHAR);
+
+ RequestPacket->Parameters.AddDelName.Type = OtherDomain;
+
+ STRCPY(RequestPacket->Parameters.AddDelName.Name,ServerName);
+
+ //
+ // This is a simple IoControl - It just updates the status.
+ //
+
+ Status = BrDgReceiverIoControl(BrDgReceiverDeviceHandle,
+ IOCTL_LMDR_DELETE_NAME_DOM,
+ RequestPacket,
+ sizeof(LMDR_REQUEST_PACKET),
+ NULL,
+ 0,
+ NULL);
+
+ return Status;
+}
+
+NET_API_STATUS
+BrAddName(
+ IN PNETWORK Network,
+ IN LPTSTR Name,
+ IN DGRECEIVER_NAME_TYPE NameType
+ )
+/*++
+
+Routine Description:
+
+ Add a single name to a single transport.
+
+Arguments:
+
+ Network - Transport to add the name to
+
+ Name - Name to add
+
+ NameType - Type of the name to add
+
+Return Value:
+
+ None.
+
+--*/
+{
+ NET_API_STATUS Status;
+ UCHAR PacketBuffer[sizeof(LMDR_REQUEST_PACKET)+(LM20_CNLEN+1)*sizeof(WCHAR)];
+ PLMDR_REQUEST_PACKET RequestPacket = (PLMDR_REQUEST_PACKET)PacketBuffer;
+
+
+ RequestPacket->Version = LMDR_REQUEST_PACKET_VERSION_DOM;
+
+ RequestPacket->TransportName = Network->NetworkName;
+ RequestPacket->EmulatedDomainName = Network->DomainInfo->DomUnicodeDomainNameString;
+
+ RequestPacket->Parameters.AddDelName.DgReceiverNameLength = STRLEN(Name)*sizeof(TCHAR);
+
+ RequestPacket->Parameters.AddDelName.Type = NameType;
+
+ STRCPY(RequestPacket->Parameters.AddDelName.Name,Name);
+
+ //
+ // This is a simple IoControl - It just updates the status.
+ //
+
+ Status = BrDgReceiverIoControl(BrDgReceiverDeviceHandle,
+ IOCTL_LMDR_ADD_NAME_DOM,
+ RequestPacket,
+ sizeof(LMDR_REQUEST_PACKET),
+ NULL,
+ 0,
+ NULL);
+
+ return Status;
+}
+
+
+ NET_API_STATUS
+BrQueryOtherDomains(
+ OUT LPSERVER_INFO_100 *ReturnedBuffer,
+ OUT LPDWORD TotalEntries
+ )
+
+/*++
+
+Routine Description:
+
+ This routine returns the list of "other domains" configured for this
+ machine.
+
+Arguments:
+
+ ReturnedBuffer - Returns the list of other domains as a SERVER_INFO_100 structure.
+
+ TotalEntries - Returns the total number of other domains.
+
+Return Value:
+
+ NET_API_STATUS - The status of this request.
+
+--*/
+
+{
+ NET_API_STATUS Status;
+ LMDR_REQUEST_PACKET RequestPacket;
+ PDGRECEIVE_NAMES NameTable;
+ PVOID Buffer;
+ LPTSTR BufferEnd;
+ PSERVER_INFO_100 ServerInfo;
+ ULONG NumberOfOtherDomains;
+ ULONG BufferSizeNeeded;
+ ULONG i;
+
+ RequestPacket.Type = EnumerateNames;
+ RequestPacket.Version = LMDR_REQUEST_PACKET_VERSION_DOM;
+ RequestPacket.Level = 0;
+ RequestPacket.TransportName.Length = 0;
+ RequestPacket.TransportName.Buffer = NULL;
+ RtlInitUnicodeString( &RequestPacket.EmulatedDomainName, NULL );
+ RequestPacket.Parameters.EnumerateNames.ResumeHandle = 0;
+
+ Status = DeviceControlGetInfo(BrDgReceiverDeviceHandle,
+ IOCTL_LMDR_ENUMERATE_NAMES,
+ &RequestPacket,
+ sizeof(RequestPacket),
+ (LPVOID *)&NameTable,
+ 0xffffffff,
+ 0,
+ NULL);
+ if (Status != NERR_Success) {
+ return Status;
+ }
+
+ NumberOfOtherDomains = 0;
+ BufferSizeNeeded = 0;
+
+ for (i = 0;i < RequestPacket.Parameters.EnumerateNames.EntriesRead ; i++) {
+ if (NameTable[i].Type == OtherDomain) {
+ NumberOfOtherDomains += 1;
+ BufferSizeNeeded += sizeof(SERVER_INFO_100)+NameTable[i].DGReceiverName.Length+sizeof(TCHAR);
+ }
+ }
+
+ *TotalEntries = NumberOfOtherDomains;
+
+ Buffer = MIDL_user_allocate(BufferSizeNeeded);
+
+ if (Buffer == NULL) {
+ MIDL_user_free(NameTable);
+ return(ERROR_NOT_ENOUGH_MEMORY);
+ }
+
+ ServerInfo = Buffer;
+ BufferEnd = (LPTSTR)((PCHAR)Buffer+BufferSizeNeeded);
+
+ for (i = 0;i < RequestPacket.Parameters.EnumerateNames.EntriesRead ; i++) {
+ if (NameTable[i].Type == OtherDomain) {
+ WCHAR NameBuffer[DNLEN+1];
+
+ //
+ // The name from the browser is not null terminated, so copy it
+ // to a local buffer and null terminate it.
+ //
+
+ RtlCopyMemory(NameBuffer, NameTable[i].DGReceiverName.Buffer, NameTable[i].DGReceiverName.Length);
+
+ NameBuffer[(NameTable[i].DGReceiverName.Length) / sizeof(TCHAR)] = UNICODE_NULL;
+
+ ServerInfo->sv100_platform_id = PLATFORM_ID_OS2;
+
+ ServerInfo->sv100_name = NameBuffer;
+
+ if (!NetpPackString(&ServerInfo->sv100_name,
+ (LPBYTE)(ServerInfo+1),
+ &BufferEnd)) {
+ MIDL_user_free(NameTable);
+ return(NERR_InternalError);
+ }
+
+ ServerInfo += 1;
+ }
+ }
+
+ MIDL_user_free(NameTable);
+
+ *ReturnedBuffer = (LPSERVER_INFO_100) Buffer;
+
+ Status = NERR_Success;
+
+ return Status;
+
+}
+
+NET_API_STATUS
+BrAddOtherDomain(
+ IN PNETWORK Network,
+ IN LPTSTR ServerName
+ )
+{
+ return BrAddName( Network, ServerName, OtherDomain );
+}
+
+ NET_API_STATUS
+BrBindToTransport(
+ IN LPWSTR TransportName,
+ IN LPWSTR EmulatedDomainName,
+ IN LPWSTR EmulatedComputerName
+ )
+{
+ NET_API_STATUS Status;
+ UCHAR PacketBuffer[sizeof(LMDR_REQUEST_PACKET)+(MAXIMUM_FILENAME_LENGTH+1+CNLEN+1)*sizeof(WCHAR)];
+ PLMDR_REQUEST_PACKET RequestPacket = (PLMDR_REQUEST_PACKET)PacketBuffer;
+
+
+ RequestPacket->Version = LMDR_REQUEST_PACKET_VERSION_DOM;
+ RequestPacket->Level = TRUE; // EmulatedComputerName follows transport name
+
+ RequestPacket->TransportName.Length = 0;
+ RequestPacket->TransportName.MaximumLength = 0;
+ RtlInitUnicodeString( &RequestPacket->EmulatedDomainName, EmulatedDomainName );
+
+ RequestPacket->Parameters.Bind.TransportNameLength = STRLEN(TransportName)*sizeof(TCHAR);
+
+ STRCPY(RequestPacket->Parameters.Bind.TransportName, TransportName);
+ STRCAT(RequestPacket->Parameters.Bind.TransportName, EmulatedComputerName );
+
+ //
+ // This is a simple IoControl - It just updates the status.
+ //
+
+ Status = BrDgReceiverIoControl(BrDgReceiverDeviceHandle,
+ IOCTL_LMDR_BIND_TO_TRANSPORT_DOM,
+ RequestPacket,
+ FIELD_OFFSET(LMDR_REQUEST_PACKET, Parameters.Bind.TransportName) +
+ RequestPacket->Parameters.Bind.TransportNameLength +
+ wcslen(EmulatedComputerName) * sizeof(WCHAR) + sizeof(WCHAR),
+ NULL,
+ 0,
+ NULL);
+
+ return Status;
+}
+
+ NET_API_STATUS
+BrUnbindFromTransport(
+ IN LPWSTR TransportName,
+ IN LPWSTR EmulatedDomainName
+ )
+{
+ NET_API_STATUS Status;
+ UCHAR PacketBuffer[sizeof(LMDR_REQUEST_PACKET)+(MAXIMUM_FILENAME_LENGTH+1)*sizeof(WCHAR)];
+ PLMDR_REQUEST_PACKET RequestPacket = (PLMDR_REQUEST_PACKET)PacketBuffer;
+
+
+ RequestPacket->Version = LMDR_REQUEST_PACKET_VERSION_DOM;
+
+ RequestPacket->TransportName.Length = 0;
+ RequestPacket->TransportName.MaximumLength = 0;
+ RtlInitUnicodeString( &RequestPacket->EmulatedDomainName, EmulatedDomainName );
+
+ RequestPacket->Parameters.Unbind.TransportNameLength = STRLEN(TransportName)*sizeof(TCHAR);
+
+ STRCPY(RequestPacket->Parameters.Unbind.TransportName, TransportName);
+
+ BrPrint(( BR_CRITICAL, "unbind from IPX transport %ws\n", TransportName));
+
+ //
+ // This is a simple IoControl - It just updates the status.
+ //
+
+ Status = BrDgReceiverIoControl(BrDgReceiverDeviceHandle,
+ IOCTL_LMDR_UNBIND_FROM_TRANSPORT_DOM,
+ RequestPacket,
+ FIELD_OFFSET(LMDR_REQUEST_PACKET, Parameters.Bind.TransportName) +
+ RequestPacket->Parameters.Bind.TransportNameLength,
+ NULL,
+ 0,
+ NULL);
+
+ if (Status != NERR_Success) {
+ BrPrint(( BR_CRITICAL, "unbind from IPX transport %ws: %ld\n", TransportName, Status));
+ }
+ return Status;
+}
+
diff --git a/private/net/svcdlls/browser2/server/brdevice.h b/private/net/svcdlls/browser2/server/brdevice.h
new file mode 100644
index 000000000..5f3f737ab
--- /dev/null
+++ b/private/net/svcdlls/browser2/server/brdevice.h
@@ -0,0 +1,148 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ brdevice.h
+
+Abstract:
+
+ Private header file to be included by Workstation service modules that
+ need to call into the NT Redirector and the NT Datagram Receiver.
+
+Author:
+
+ Rita Wong (ritaw) 15-Feb-1991
+
+Revision History:
+
+--*/
+
+#ifndef _BRDEVICE_INCLUDED_
+#define _BRDEVICE_INCLUDED_
+
+#include <ntddbrow.h> // Datagram receiver include file
+
+//-------------------------------------------------------------------//
+// //
+// Type definitions //
+// //
+//-------------------------------------------------------------------//
+
+typedef enum _DDTYPE {
+ DatagramReceiver
+} DDTYPE, *PDDTYPE;
+
+typedef struct _BROWSERASYNCCONTEXT {
+ WORKER_ITEM WorkItem;
+
+ PNETWORK Network;
+
+ IO_STATUS_BLOCK IoStatusBlock;
+
+ PLMDR_REQUEST_PACKET RequestPacket;
+
+ //
+ // Timestamp when request was completed.
+ //
+
+ LARGE_INTEGER TimeCompleted;
+
+} BROWSERASYNCCONTEXT, *PBROWSERASYNCCONTEXT;
+
+//-------------------------------------------------------------------//
+// //
+// Function prototypes of support routines found in wsdevice.c //
+// //
+//-------------------------------------------------------------------//
+
+NET_API_STATUS
+BrOpenDgReceiver (
+ VOID
+ );
+
+NET_API_STATUS
+BrAnnounceDomain(
+ IN PNETWORK Network,
+ IN ULONG Periodicty
+ );
+
+NET_API_STATUS
+BrGetTransportList(
+ OUT PLMDR_TRANSPORT_LIST *TransportList
+ );
+
+NET_API_STATUS
+BrIssueAsyncBrowserIoControl(
+ IN PNETWORK Network,
+ IN ULONG ControlCode,
+ IN PBROWSER_WORKER_ROUTINE CompletionRoutine,
+ IN PVOID OptionalParamter
+ );
+
+NET_API_STATUS
+BrGetLocalBrowseList(
+ IN PNETWORK Network,
+ IN LPWSTR DomainName,
+ IN ULONG Level,
+ IN ULONG ServerType,
+ OUT PVOID *ServerList,
+ OUT PULONG EntriesRead,
+ OUT PULONG TotalEntries
+ );
+
+NET_API_STATUS
+BrUpdateBrowserStatus (
+ IN PNETWORK Network,
+ IN DWORD ServiceStatus
+ );
+
+VOID
+BrShutdownDgReceiver(
+ VOID
+ );
+
+NET_API_STATUS
+BrRemoveOtherDomain(
+ IN PNETWORK Network,
+ IN LPTSTR ServerName
+ );
+
+NET_API_STATUS
+BrQueryOtherDomains(
+ OUT LPSERVER_INFO_100 *ReturnedBuffer,
+ OUT LPDWORD TotalEntries
+ );
+
+NET_API_STATUS
+BrAddOtherDomain(
+ IN PNETWORK Network,
+ IN LPTSTR ServerName
+ );
+
+NET_API_STATUS
+BrBindToTransport(
+ IN LPWSTR TransportName,
+ IN LPWSTR EmulatedDomainName,
+ IN LPWSTR EmulatedComputerName
+ );
+
+NET_API_STATUS
+BrUnbindFromTransport(
+ IN LPWSTR TransportName,
+ IN LPWSTR EmulatedDomainName
+ );
+
+//-------------------------------------------------------------------//
+// //
+// Global variables //
+// //
+//-------------------------------------------------------------------//
+
+//
+// Handle to the Datagram Receiver DD
+//
+extern HANDLE BrDgReceiverDeviceHandle;
+
+#endif // ifndef _BRDEVICE_INCLUDED_
diff --git a/private/net/svcdlls/browser2/server/brdmmstr.c b/private/net/svcdlls/browser2/server/brdmmstr.c
new file mode 100644
index 000000000..d9e0d73d3
--- /dev/null
+++ b/private/net/svcdlls/browser2/server/brdmmstr.c
@@ -0,0 +1,456 @@
+
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ brdmmstr.c
+
+Abstract:
+
+ This module contains the routines to manage a domain master browser server
+
+Author:
+
+ Rita Wong (ritaw) 20-Feb-1991
+
+Revision History:
+
+--*/
+
+#include "precomp.h"
+#pragma hdrstop
+
+//-------------------------------------------------------------------//
+// //
+// Local structure definitions //
+// //
+//-------------------------------------------------------------------//
+
+//-------------------------------------------------------------------//
+// //
+// Local function prototypes //
+// //
+//-------------------------------------------------------------------//
+
+
+VOID
+BrPostGetMasterAnnouncementWorker(
+ IN PVOID Ctx
+ );
+
+VOID
+GetMasterAnnouncementCompletion (
+ IN PVOID Ctx
+ );
+
+typedef struct _BROWSER_GET_MASTER_ANNOUNCEMENT_CONTEXT {
+ PDOMAIN_INFO DomainInfo;
+ HANDLE EventHandle;
+ NET_API_STATUS NetStatus;
+} BROWSER_GET_MASTER_ANNOUNCEMENT_CONTEXT, *PBROWSER_GET_MASTER_ANNOUNCEMENT_CONTEXT;
+
+NET_API_STATUS
+BrPostGetMasterAnnouncementInWorker(
+ PDOMAIN_INFO DomainInfo
+ )
+
+/*++
+
+Routine Description:
+
+ Wrapper for BrPostGetMasterAnnouncement. Since BrPostGetMasterAnnouncement
+ starts a pending IO to the browser driver, the thread that calls
+ BrPostGetMasterAnnouncement must remain around forever. This wrapper can
+ be called by any transient thread (e.g., an RPC thread). It simply causes
+ BrPostGetMasterAnnouncement to be called in a worker thread.
+
+Arguments:
+
+ DomainInfo - Domain the get master announcement is to be posted for
+
+Return Value:
+
+ Status of operation.
+
+--*/
+{
+ NET_API_STATUS NetStatus;
+ DWORD WaitStatus;
+
+ WORKER_ITEM WorkItem;
+ BROWSER_GET_MASTER_ANNOUNCEMENT_CONTEXT Context;
+
+ //
+ // Copy the parameters into the context.
+ //
+
+ Context.DomainInfo = DomainInfo;
+
+ //
+ // Create an event which we use to wait on the worker thread.
+ //
+
+ Context.EventHandle = CreateEvent(
+ NULL, // Event attributes
+ TRUE, // Event must be manually reset
+ FALSE, // Initial state not signalled
+ NULL ); // Event name
+
+ if ( Context.EventHandle == NULL ) {
+ NetStatus = GetLastError();
+ return NetStatus;
+ }
+
+ //
+ // Queue the request to the worker thread.
+ //
+
+ BrInitializeWorkItem( &WorkItem,
+ BrPostGetMasterAnnouncementWorker,
+ &Context );
+
+ BrQueueWorkItem( &WorkItem );
+
+ //
+ // Wait for the worker thread to finish
+ //
+
+ WaitStatus = WaitForSingleObject( Context.EventHandle, INFINITE );
+
+ if ( WaitStatus == WAIT_OBJECT_0 ) {
+ NetStatus = Context.NetStatus;
+ } else {
+ NetStatus = GetLastError();
+ }
+
+ CloseHandle( Context.EventHandle );
+
+ return NetStatus;
+}
+
+ VOID
+BrPostGetMasterAnnouncementWorker(
+ IN PVOID Ctx
+ )
+/*++
+
+Routine Description:
+
+ Worker routine for BrPostGetMasterAnnouncementInWorker.
+
+ This routine executes in the context of a worker thread.
+
+Arguments:
+
+ Context - Context describing the workitem.
+
+Return Value:
+
+ None
+
+--*/
+{
+ PBROWSER_GET_MASTER_ANNOUNCEMENT_CONTEXT Context =
+ (PBROWSER_GET_MASTER_ANNOUNCEMENT_CONTEXT) Ctx;
+
+ //
+ // Create the domain.
+ //
+
+ Context->NetStatus = BrPostGetMasterAnnouncement( Context->DomainInfo );
+
+ //
+ // Let the caller know we're done.
+ //
+ SetEvent( Context->EventHandle );
+
+}
+
+ NET_API_STATUS
+BrPostGetMasterAnnouncement (
+ PDOMAIN_INFO DomainInfo
+ )
+/*++
+
+Routine Description:
+
+ Ensure the GetMasterAnnouncement request is posted for all networks on
+ the specified domain.
+
+Arguments:
+
+ DomainInfo - Domain this operation is to apply to
+
+Return Value:
+
+ Status - The status of the operation.
+
+--*/
+{
+ BrPrint(( BR_MASTER, "%ws: BrPostGetMasterAnnouncement\n", DomainInfo->DomUnicodeDomainName ));
+
+ return BrEnumerateNetworksForDomain(DomainInfo, PostGetMasterAnnouncement, NULL);
+}
+
+
+ NET_API_STATUS
+PostGetMasterAnnouncement (
+ PNETWORK Network,
+ PVOID Context
+ )
+/*++
+
+Routine Description:
+
+ Ensure the GetMasterAnnouncement request is posted for a particular network.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ Status - The status of the operation.
+
+--*/
+{
+ NET_API_STATUS NetStatus = NERR_Success;
+
+ if (!LOCK_NETWORK(Network)) {
+ return NERR_InternalError;
+ }
+
+ if ( Network->DomainInfo->IsDomainMasterBrowser &&
+ (Network->Flags & NETWORK_WANNISH)) {
+
+ if (!(Network->Flags & NETWORK_GET_MASTER_ANNOUNCE_POSTED)) {
+
+ BrPrint(( BR_MASTER,
+ "%ws: PostGetMasterAnnouncement on %ws\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ Network->NetworkName.Buffer));
+
+ NetStatus = BrIssueAsyncBrowserIoControl(Network,
+ IOCTL_LMDR_WAIT_FOR_MASTER_ANNOUNCE,
+ GetMasterAnnouncementCompletion,
+ NULL
+ );
+
+ if ( NetStatus == NERR_Success ) {
+ Network->Flags |= NETWORK_GET_MASTER_ANNOUNCE_POSTED;
+ }
+ } else {
+ BrPrint(( BR_MASTER,
+ "%ws: PostGetMasterAnnouncement already posted on %ws\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ Network->NetworkName.Buffer));
+ }
+ }
+
+ UNLOCK_NETWORK(Network);
+ return NetStatus;
+
+ UNREFERENCED_PARAMETER(Context);
+}
+
+
+VOID
+GetMasterAnnouncementCompletion (
+ IN PVOID Ctx
+ )
+/*++
+
+Routine Description:
+
+ This function is the completion routine for a master announcement. It is
+ called whenever a master announcement is received for a particular network.
+
+Arguments:
+
+ Ctx - Context block for request.
+
+Return Value:
+
+ None.
+
+--*/
+
+
+{
+ PVOID ServerList = NULL;
+ ULONG EntriesRead;
+ ULONG TotalEntries;
+ NET_API_STATUS Status = NERR_Success;
+ PBROWSERASYNCCONTEXT Context = Ctx;
+ PLMDR_REQUEST_PACKET MasterAnnouncement = Context->RequestPacket;
+ PNETWORK Network = Context->Network;
+ LPTSTR RemoteMasterName = NULL;
+ BOOLEAN NetLocked = FALSE;
+ BOOLEAN NetReferenced = FALSE;
+
+
+ try {
+ //
+ // Ensure the network wasn't deleted from under us.
+ //
+ if ( BrReferenceNetwork( Network ) == NULL ) {
+ try_return(NOTHING);
+ }
+ NetReferenced = TRUE;
+
+ if (!LOCK_NETWORK(Network)){
+ try_return(NOTHING);
+ }
+ NetLocked = TRUE;
+
+ Network->Flags &= ~NETWORK_GET_MASTER_ANNOUNCE_POSTED;
+
+ //
+ // The request failed for some reason - just return immediately.
+ //
+
+ if (!NT_SUCCESS(Context->IoStatusBlock.Status)) {
+ try_return(NOTHING);
+ }
+
+ Status = PostGetMasterAnnouncement(Network, NULL);
+
+ if (Status != NERR_Success) {
+ BrPrint(( BR_CRITICAL,
+ "%ws: %ws: Unable to re-issue GetMasterAnnouncement request: %lx\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ Network->NetworkName.Buffer,
+ Status));
+
+ try_return(NOTHING);
+
+ }
+
+
+ RemoteMasterName = MIDL_user_allocate(MasterAnnouncement->Parameters.WaitForMasterAnnouncement.MasterNameLength+3*sizeof(TCHAR));
+
+ if (RemoteMasterName == NULL) {
+ try_return(NOTHING);
+ }
+
+ RemoteMasterName[0] = TEXT('\\');
+ RemoteMasterName[1] = TEXT('\\');
+
+ STRNCPY(&RemoteMasterName[2],
+ MasterAnnouncement->Parameters.WaitForMasterAnnouncement.Name,
+ MasterAnnouncement->Parameters.WaitForMasterAnnouncement.MasterNameLength/sizeof(TCHAR));
+
+ RemoteMasterName[(MasterAnnouncement->Parameters.WaitForMasterAnnouncement.MasterNameLength/sizeof(TCHAR))+2] = UNICODE_NULL;
+
+ BrPrint(( BR_MASTER,
+ "%ws: %ws: GetMasterAnnouncement: Got a master browser announcement from %ws\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ Network->NetworkName.Buffer,
+ RemoteMasterName));
+
+ UNLOCK_NETWORK(Network);
+
+ NetLocked = FALSE;
+
+ //
+ // Remote the api and pull the browse list from the remote server.
+ //
+
+ Status = RxNetServerEnum(RemoteMasterName,
+ Network->NetworkName.Buffer,
+ 101,
+ (LPBYTE *)&ServerList,
+ 0xffffffff,
+ &EntriesRead,
+ &TotalEntries,
+ SV_TYPE_LOCAL_LIST_ONLY,
+ NULL,
+ NULL
+ );
+
+ if ((Status == NERR_Success) || (Status == ERROR_MORE_DATA)) {
+
+ if (!LOCK_NETWORK(Network)) {
+ try_return(NOTHING);
+ }
+
+ NetLocked = TRUE;
+
+ Status = MergeServerList(&Network->BrowseTable,
+ 101,
+ ServerList,
+ EntriesRead,
+ TotalEntries
+ );
+
+ UNLOCK_NETWORK(Network);
+
+ NetLocked = FALSE;
+
+ (void) NetApiBufferFree( ServerList );
+ ServerList = NULL;
+
+ }
+
+ //
+ // Remote the api and pull the browse list from the remote server.
+ //
+
+ Status = RxNetServerEnum(RemoteMasterName,
+ Network->NetworkName.Buffer,
+ 101,
+ (LPBYTE *)&ServerList,
+ 0xffffffff,
+ &EntriesRead,
+ &TotalEntries,
+ SV_TYPE_LOCAL_LIST_ONLY | SV_TYPE_DOMAIN_ENUM,
+ NULL,
+ NULL
+ );
+
+ if ((Status == NERR_Success) || (Status == ERROR_MORE_DATA)) {
+
+ if (!LOCK_NETWORK(Network)) {
+ try_return(NOTHING);
+ }
+
+ NetLocked = TRUE;
+
+ Status = MergeServerList(&Network->DomainList,
+ 101,
+ ServerList,
+ EntriesRead,
+ TotalEntries
+ );
+ }
+
+try_exit:NOTHING;
+ } finally {
+
+ if (NetLocked) {
+ UNLOCK_NETWORK(Network);
+ }
+
+ if ( NetReferenced ) {
+ BrDereferenceNetwork( Network );
+ }
+
+ if (RemoteMasterName != NULL) {
+ MIDL_user_free(RemoteMasterName);
+ }
+
+ MIDL_user_free(Context->RequestPacket);
+
+ MIDL_user_free(Context);
+
+ if ( ServerList != NULL ) {
+ (void) NetApiBufferFree( ServerList );
+ }
+
+ }
+
+ return;
+
+}
diff --git a/private/net/svcdlls/browser2/server/brdomain.c b/private/net/svcdlls/browser2/server/brdomain.c
new file mode 100644
index 000000000..185b290fd
--- /dev/null
+++ b/private/net/svcdlls/browser2/server/brdomain.c
@@ -0,0 +1,790 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ brdomain.c
+
+Abstract:
+
+ Code to manage primary and emulated networks.
+
+Author:
+
+ Cliff Van Dyke (CliffV) 11-Jan-1995
+
+Revision History:
+
+--*/
+
+#include "precomp.h"
+#pragma hdrstop
+
+//
+// Module specific globals
+//
+
+// Serialized by NetworkCritSect
+LIST_ENTRY ServicedDomains = {0};
+
+//
+// Local procedure forwards.
+//
+
+NET_API_STATUS
+BrCreateDomain(
+ LPWSTR DomainName,
+ LPWSTR ComputerName,
+ BOOLEAN IsEmulatedDomain,
+ BOOLEAN IsPrimaryDomainController
+ );
+
+VOID
+BrCreateDomainWorker(
+ IN PVOID Ctx
+ );
+
+
+
+NET_API_STATUS
+BrInitializeDomains(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ Initialize brdomain.c and create the primary domain.
+
+Arguments:
+
+ None
+
+Return Value:
+
+ Status of operation.
+
+--*/
+{
+ NET_API_STATUS NetStatus;
+ LPWSTR ComputerName = NULL;
+ LPWSTR DomainName = NULL;
+
+ //
+ // Initialize globals
+ //
+
+ InitializeListHead(&ServicedDomains);
+
+ //
+ // Initialize actual domain of this machine.
+ //
+ // Get the configured computer name. NetpGetComputerName allocates
+ // the memory to hold the computername string using LocalAlloc.
+ //
+
+ NetStatus = NetpGetComputerName( &ComputerName );
+
+ if ( NetStatus != NERR_Success ) {
+ goto Cleanup;
+ }
+
+ NetStatus = NetpGetDomainName( &DomainName );
+
+ if ( NetStatus != NERR_Success ) {
+ goto Cleanup;
+ }
+
+ NetStatus = BrCreateDomain( DomainName,
+ ComputerName,
+ FALSE,
+ (BOOLEAN) BrInfo.IsPrimaryDomainController );
+
+ if ( NetStatus != NERR_Success ) {
+ goto Cleanup;
+ }
+
+
+ NetStatus = NERR_Success;
+
+ //
+ // Free locally used resources
+ //
+Cleanup:
+ if ( ComputerName != NULL ) {
+ (VOID)LocalFree( ComputerName );
+ }
+ if ( DomainName != NULL ) {
+ (VOID)LocalFree( DomainName );
+ }
+
+ return NetStatus;
+}
+
+
+NET_API_STATUS
+BrCreateDomain(
+ LPWSTR DomainName,
+ LPWSTR ComputerName,
+ BOOLEAN IsEmulatedDomain,
+ BOOLEAN IsPrimaryDomainController
+ )
+
+/*++
+
+Routine Description:
+
+ Create a new domain to browse on.
+
+Arguments:
+
+ DomainName - Name of the domain to browse on
+
+ ComputerName - Name of this computer in the specified domain.
+
+ IsEmulatedDomain - TRUE iff this domain is an emulated domain of this machine.
+
+ IsPrimaryDomainControler - TRUE iff this machine is the PDC of this domain.
+
+Return Value:
+
+ Status of operation.
+
+--*/
+{
+ NTSTATUS Status;
+ NET_API_STATUS NetStatus;
+
+ BOOLEAN CanCallBrDeleteDomain = FALSE;
+
+ PDOMAIN_INFO DomainInfo = NULL;
+ ULONG AComputerNameLength;
+
+ BrPrint(( BR_DOMAIN, "%ws: Added new domain and computer: %ws\n",
+ DomainName,
+ ComputerName ));
+
+ //
+ // Allocate a structure describing the new domain.
+ //
+
+ DomainInfo = LocalAlloc( LMEM_ZEROINIT, sizeof(DOMAIN_INFO) );
+
+ if ( DomainInfo == NULL ) {
+ NetStatus = GetLastError();
+ goto Cleanup;
+ }
+
+ //
+ // Create an interim reference count for this domain.
+ //
+
+ DomainInfo->ReferenceCount = 1;
+
+ DomainInfo->IsEmulatedDomain = IsEmulatedDomain;
+ DomainInfo->IsPrimaryDomainController = IsPrimaryDomainController;
+
+
+ //
+ // Copy the computer name into the structure.
+ //
+
+ NetStatus = I_NetNameCanonicalize(
+ NULL,
+ ComputerName,
+ DomainInfo->DomUnicodeComputerName,
+ sizeof(DomainInfo->DomUnicodeComputerName),
+ NAMETYPE_COMPUTER,
+ 0 );
+
+
+ if ( NetStatus != NERR_Success ) {
+ BrPrint(( BR_CRITICAL,
+ "ComputerName " FORMAT_LPWSTR " is invalid\n",
+ ComputerName ));
+ goto Cleanup;
+ }
+
+ DomainInfo->DomUnicodeComputerNameLength = wcslen(DomainInfo->DomUnicodeComputerName);
+
+ Status = RtlUpcaseUnicodeToOemN( DomainInfo->DomOemComputerName,
+ sizeof(DomainInfo->DomOemComputerName),
+ &DomainInfo->DomOemComputerNameLength,
+ DomainInfo->DomUnicodeComputerName,
+ DomainInfo->DomUnicodeComputerNameLength*sizeof(WCHAR));
+
+ if (!NT_SUCCESS(Status)) {
+ BrPrint(( BR_CRITICAL, "Unable to convert computer name to OEM %ws %lx\n", ComputerName, Status ));
+ NetStatus = ERROR_NOT_ENOUGH_MEMORY;
+ goto Cleanup;
+ }
+
+ DomainInfo->DomOemComputerName[DomainInfo->DomOemComputerNameLength] = '\0';
+
+
+ //
+ // Copy the domain name into the structure
+ //
+
+ NetStatus = I_NetNameCanonicalize(
+ NULL,
+ DomainName,
+ DomainInfo->DomUnicodeDomainName,
+ sizeof(DomainInfo->DomUnicodeDomainName),
+ NAMETYPE_DOMAIN,
+ 0 );
+
+
+ if ( NetStatus != NERR_Success ) {
+ BrPrint(( BR_CRITICAL, "%ws: DomainName is invalid\n", DomainName ));
+ goto Cleanup;
+ }
+
+ RtlInitUnicodeString( &DomainInfo->DomUnicodeDomainNameString,
+ DomainInfo->DomUnicodeDomainName );
+
+ Status = RtlUpcaseUnicodeToOemN( DomainInfo->DomOemDomainName,
+ sizeof(DomainInfo->DomOemDomainName),
+ &DomainInfo->DomOemDomainNameLength,
+ DomainInfo->DomUnicodeDomainNameString.Buffer,
+ DomainInfo->DomUnicodeDomainNameString.Length);
+
+ if (!NT_SUCCESS(Status)) {
+ BrPrint(( BR_CRITICAL, "%ws: Unable to convert Domain name to OEM\n", DomainName ));
+ NetStatus = ERROR_NOT_ENOUGH_MEMORY;
+ goto Cleanup;
+ }
+
+ DomainInfo->DomOemDomainName[DomainInfo->DomOemDomainNameLength] = '\0';
+
+ //
+ // Link the domain into the list of domains
+ // (And mark that any future cleanup can be done by calling BrDeleteDomain)
+
+ EnterCriticalSection(&NetworkCritSect);
+ InsertTailList(&ServicedDomains, &DomainInfo->Next);
+ LeaveCriticalSection(&NetworkCritSect);
+ CanCallBrDeleteDomain = TRUE;
+
+ //
+ // Create the various networks for this domain.
+ //
+
+ NetStatus = BrCreateNetworks( DomainInfo );
+
+ if ( NetStatus != NERR_Success ) {
+ goto Cleanup;
+ }
+ //
+ // If we're the PDC, then we're also the domain master.
+ //
+
+ if (DomainInfo->IsPrimaryDomainController) {
+ DomainInfo->IsDomainMasterBrowser = TRUE;
+ }
+
+
+ //
+ // If this machine is running as domain master browser server, post an
+ // FsControl to retreive master browser announcements.
+ //
+
+ if (DomainInfo->IsDomainMasterBrowser) {
+ NetStatus = BrPostGetMasterAnnouncement( DomainInfo );
+
+ BrPrint(( BR_INIT, "%ws: MasterAnnouncement posted.\n", DomainName));
+
+ if (NetStatus != NERR_Success) {
+ goto Cleanup;
+ }
+
+ }
+
+ //
+ // If we are on either a domain master, or on a lanman/NT machine,
+ // force an election on all our transports to make sure that we're
+ // the master
+ //
+
+ if (DomainInfo->IsDomainMasterBrowser || BrInfo.IsLanmanNt) {
+
+ BrForceElectionOnAllNetworks( DomainInfo, EVENT_BROWSER_ELECTION_SENT_LANMAN_NT_STARTED);
+
+ BrPrint(( BR_INIT, "%ws: Election forced on startup.\n", DomainName));
+
+ }
+
+ //
+ // This machine's browser has MaintainServerList set to either 0 or 1.
+ //
+
+ //
+ // If MaintainServerList = Auto,
+ // then asynchronously get the master server name for each network
+ // to ensure someone is the master.
+ //
+ // Ignore failures since this is just priming the domain.
+ //
+
+ EnterCriticalSection(&BrInfo.ConfigCritSect);
+ if (BrInfo.MaintainServerList == 0) {
+
+ BrGetMasterServerNamesAysnc( DomainInfo );
+
+ BrPrint(( BR_INIT, "%ws: FindMaster for all nets completed.\n", DomainName ));
+
+ //
+ // if we're a Lan Manager/NT machine, then we need to always be a backup
+ // browser.
+ //
+
+ //
+ // MaintainServerList == 1 means Yes
+ //
+
+ } else if (BrInfo.MaintainServerList == 1){
+
+ //
+ // Become a backup server now.
+ //
+
+ Status = BrBecomeBackup( DomainInfo );
+
+ if (Status != NERR_Success) {
+ goto Cleanup;
+ }
+
+ BrPrint(( BR_INIT, "%ws: BecomeBackup on all nets completed.\n", DomainName ));
+ }
+ LeaveCriticalSection(&BrInfo.ConfigCritSect);
+
+ Status = NERR_Success;
+
+
+ //
+ // Free Locally used resources
+ //
+Cleanup:
+
+ if (NetStatus != NERR_Success) {
+
+ if (DomainInfo != NULL) {
+
+ //
+ // If we've initialized to the point where we can call
+ // we can call BrDeleteDomain, do so.
+ //
+
+ if ( CanCallBrDeleteDomain ) {
+ (VOID) BrDeleteDomain( DomainInfo );
+
+ //
+ // Otherwise, just delete what we've created.
+ //
+ } else {
+
+ (VOID) LocalFree(DomainInfo);
+ }
+
+ }
+
+ }
+
+ return NetStatus;
+}
+
+typedef struct _BROWSER_CREATE_DOMAIN_CONTEXT {
+ LPWSTR DomainName;
+ LPWSTR ComputerName;
+ BOOLEAN IsEmulatedDomain;
+ BOOLEAN IsPrimaryDomainController;
+ HANDLE EventHandle;
+ NET_API_STATUS NetStatus;
+} BROWSER_CREATE_DOMAIN_CONTEXT, *PBROWSER_CREATE_DOMAIN_CONTEXT;
+
+NET_API_STATUS
+BrCreateDomainInWorker(
+ LPWSTR DomainName,
+ LPWSTR ComputerName,
+ BOOLEAN IsEmulatedDomain,
+ BOOLEAN IsPrimaryDomainController
+ )
+
+/*++
+
+Routine Description:
+
+ Wrapper for BrCreateDomain. Since BrCreateDomain starts several pending
+ IO's to the browser driver, the thread that calls BrCreateDomain must
+ remain around forever. This wrapper can be called by any transient thread
+ (e.g., an RPC thread). It simply causes BrCreateDomain to be called in a
+ worker thread.
+
+Arguments:
+
+ DomainName - Name of the domain to browse on
+
+ ComputerName - Name of this computer in the specified domain.
+
+ IsEmulatedDomain - TRUE iff this domain is an emulated domain of this machine.
+
+ IsPrimaryDomainControler - TRUE iff this machine is the PDC of this domain.
+
+Return Value:
+
+ Status of operation.
+
+--*/
+{
+ NET_API_STATUS NetStatus;
+ DWORD WaitStatus;
+
+ WORKER_ITEM WorkItem;
+ BROWSER_CREATE_DOMAIN_CONTEXT Context;
+
+ //
+ // Copy our arguments into a context block for the worker thread
+ //
+
+ Context.DomainName = DomainName;
+ Context.ComputerName = ComputerName;
+ Context.IsEmulatedDomain = IsEmulatedDomain;
+ Context.IsPrimaryDomainController = IsPrimaryDomainController;
+
+ //
+ // Create an event which we use to wait on the worker thread.
+ //
+
+ Context.EventHandle = CreateEvent(
+ NULL, // Event attributes
+ TRUE, // Event must be manually reset
+ FALSE, // Initial state not signalled
+ NULL ); // Event name
+
+ if ( Context.EventHandle == NULL ) {
+ NetStatus = GetLastError();
+ return NetStatus;
+ }
+
+ //
+ // Queue the request to the worker thread.
+ //
+
+ BrInitializeWorkItem( &WorkItem,
+ BrCreateDomainWorker,
+ &Context );
+
+ BrQueueWorkItem( &WorkItem );
+
+ //
+ // Wait for the worker thread to finish
+ //
+
+ WaitStatus = WaitForSingleObject( Context.EventHandle, INFINITE );
+
+ if ( WaitStatus == WAIT_OBJECT_0 ) {
+ NetStatus = Context.NetStatus;
+ } else {
+ NetStatus = GetLastError();
+ }
+
+ CloseHandle( Context.EventHandle );
+
+ return NetStatus;
+}
+
+ VOID
+BrCreateDomainWorker(
+ IN PVOID Ctx
+ )
+/*++
+
+Routine Description:
+
+ Worker routine for BrCreateDomainInWorker.
+
+ This routine executes in the context of a worker thread.
+
+Arguments:
+
+ Context - Context containing the workitem and the description of the
+ domain to create.
+
+Return Value:
+
+ None
+
+--*/
+{
+ PBROWSER_CREATE_DOMAIN_CONTEXT Context = (PBROWSER_CREATE_DOMAIN_CONTEXT) Ctx;
+
+ //
+ // Create the domain.
+ //
+
+ Context->NetStatus = BrCreateDomain(
+ Context->DomainName,
+ Context->ComputerName,
+ Context->IsEmulatedDomain,
+ Context->IsPrimaryDomainController );
+
+ //
+ // Let the caller know we're done.
+ //
+ SetEvent( Context->EventHandle );
+
+}
+
+PDOMAIN_INFO
+BrFindDomain(
+ LPWSTR DomainName,
+ BOOLEAN DefaultToPrimary
+ )
+/*++
+
+Routine Description:
+
+ This routine will look up a domain given a name.
+
+Arguments:
+
+ DomainName - The name of the domain to look up.
+
+ DefaultToPrimary - Return the primary domain if DomainName is NULL or
+ can't be found.
+
+Return Value:
+
+ NULL - No such domain exists
+
+ A pointer to the domain found. The found domain should be dereferenced
+ using BrDereferenceDomain.
+
+--*/
+{
+ NTSTATUS Status;
+ PLIST_ENTRY DomainEntry;
+
+ PDOMAIN_INFO DomainInfo = NULL;
+
+ CHAR OemDomainName[DNLEN+1];
+ DWORD OemDomainNameLength;
+
+ EnterCriticalSection(&NetworkCritSect);
+
+
+ //
+ // If domain was specified,
+ // try to return primary domain.
+ //
+
+ if ( DomainName != NULL ) {
+
+
+
+ //
+ // Convert the domain name to OEM for faster comparison
+ //
+ Status = RtlUpcaseUnicodeToOemN( OemDomainName,
+ sizeof(OemDomainName),
+ &OemDomainNameLength,
+ DomainName,
+ wcslen(DomainName)*sizeof(WCHAR));
+
+ if (!NT_SUCCESS(Status)) {
+ BrPrint(( BR_CRITICAL, "%ws: Unable to convert Domain name to OEM\n", DomainName ));
+ DomainInfo = NULL;
+ goto Cleanup;
+ }
+
+
+ //
+ // Loop trying to find this domain name.
+ //
+
+ for (DomainEntry = ServicedDomains.Flink ;
+ DomainEntry != &ServicedDomains;
+ DomainEntry = DomainEntry->Flink ) {
+
+ DomainInfo = CONTAINING_RECORD(DomainEntry, DOMAIN_INFO, Next);
+
+ if ( DomainInfo->DomOemDomainNameLength == OemDomainNameLength &&
+ RtlCompareMemory( DomainInfo->DomOemDomainName,
+ OemDomainName,
+ OemDomainNameLength ) == OemDomainNameLength ) {
+ break;
+ }
+
+ DomainInfo = NULL;
+
+ }
+ }
+
+ //
+ // If we're to default to the primary domain,
+ // do so.
+ //
+
+ if ( DefaultToPrimary && DomainInfo == NULL ) {
+ if ( !IsListEmpty( &ServicedDomains ) ) {
+ DomainInfo = CONTAINING_RECORD(ServicedDomains.Flink, DOMAIN_INFO, Next);
+ }
+ }
+
+ //
+ // Reference the domain.
+ //
+
+ if ( DomainInfo != NULL ) {
+ DomainInfo->ReferenceCount ++;
+ }
+
+Cleanup:
+ LeaveCriticalSection(&NetworkCritSect);
+
+ return DomainInfo;
+}
+
+
+VOID
+BrDereferenceDomain(
+ IN PDOMAIN_INFO DomainInfo
+ )
+/*++
+
+Routine Description:
+
+ Decrement the reference count on a domain.
+
+ If the reference count goes to 0, remove the domain.
+
+ On entry, the global NetworkCritSect may not be locked
+
+Arguments:
+
+ DomainInfo - The domain to dereference
+
+Return Value:
+
+ None
+
+--*/
+{
+ NTSTATUS Status;
+ ULONG ReferenceCount;
+
+ //
+ // Decrement the reference count
+ //
+
+ EnterCriticalSection(&NetworkCritSect);
+ ReferenceCount = -- DomainInfo->ReferenceCount;
+ LeaveCriticalSection(&NetworkCritSect);
+
+ if ( ReferenceCount != 0 ) {
+ return;
+ }
+
+
+
+ //
+ // Free the Domain Info structure.
+ //
+ (VOID) LocalFree( DomainInfo );
+
+}
+
+VOID
+BrDeleteDomain(
+ IN PDOMAIN_INFO DomainInfo
+ )
+/*++
+
+Routine Description:
+
+ Force a domain to be deleted.
+
+Arguments:
+
+ DomainInfo - The domain to delete
+
+Return Value:
+
+ None
+
+--*/
+{
+ //
+ // Delete each of the networks for this domain.
+ //
+
+ BrEnumerateNetworksForDomain(DomainInfo, BrDeleteNetwork, NULL );
+
+ //
+ // Delink the domain from the global list and remove the final reference.
+ //
+
+ EnterCriticalSection(&NetworkCritSect);
+ RemoveEntryList(&DomainInfo->Next);
+ LeaveCriticalSection(&NetworkCritSect);
+
+ BrDereferenceDomain( DomainInfo );
+
+}
+
+VOID
+BrUninitializeDomains(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ Delete all of the domains.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None
+
+--*/
+{
+ //
+ // Loop through the domains deleting each of them
+ //
+
+ EnterCriticalSection(&NetworkCritSect);
+
+ while (!IsListEmpty(&ServicedDomains)) {
+
+ PDOMAIN_INFO DomainInfo = CONTAINING_RECORD(ServicedDomains.Flink, DOMAIN_INFO, Next);
+
+ DomainInfo->ReferenceCount ++;
+
+ LeaveCriticalSection(&NetworkCritSect);
+
+ //
+ // Clean up the domain.
+ //
+
+ BrDeleteDomain( DomainInfo );
+
+ //
+ // Actually delete the delinked structure by removing the last reference
+ //
+
+ ASSERT( DomainInfo->ReferenceCount == 1 );
+ BrDereferenceDomain( DomainInfo );
+
+
+ EnterCriticalSection(&NetworkCritSect);
+
+ }
+ LeaveCriticalSection(&NetworkCritSect);
+
+}
diff --git a/private/net/svcdlls/browser2/server/brdomain.h b/private/net/svcdlls/browser2/server/brdomain.h
new file mode 100644
index 000000000..f7ce8db55
--- /dev/null
+++ b/private/net/svcdlls/browser2/server/brdomain.h
@@ -0,0 +1,112 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ brdomain.h
+
+Abstract:
+
+ Header file for code to manage primary and emulated networks.
+
+Author:
+
+ Cliff Van Dyke (CliffV) 13-Jan-1995
+
+Revision History:
+
+--*/
+
+//
+// Description of a single domain.
+//
+
+typedef struct _DOMAIN_INFO {
+
+ //
+ // Link to next domain in 'ServicedDomains'
+ // (Serialized by NetworkCritSect)
+ //
+
+ LIST_ENTRY Next;
+
+ //
+ // Name of the domain being handled
+ //
+
+ UNICODE_STRING DomUnicodeDomainNameString;
+ WCHAR DomUnicodeDomainName[DNLEN+1];
+
+ CHAR DomOemDomainName[DNLEN+1];
+ DWORD DomOemDomainNameLength;
+
+ //
+ // Computer name of this computer in this domain.
+ //
+ WCHAR DomUnicodeComputerName[CNLEN+1];
+ DWORD DomUnicodeComputerNameLength;
+
+ CHAR DomOemComputerName[CNLEN+1];
+ DWORD DomOemComputerNameLength;
+
+ //
+ // Number of outstanding pointers to the domain structure.
+ // (Serialized by NetworkCritSect)
+ //
+
+ DWORD ReferenceCount;
+
+ //
+ // Misc flags.
+ //
+
+ BOOLEAN IsDomainMasterBrowser; // True if domain master browser.
+ BOOLEAN IsPrimaryDomainController; // True if machine is PDC of this domain.
+ BOOLEAN IsEmulatedDomain; // True if this is an emulated domain
+
+} DOMAIN_INFO, *PDOMAIN_INFO;
+
+//
+// List of all domains. The primary domain is at the front of the list.
+//
+extern LIST_ENTRY ServicedDomains;
+
+
+//
+// brdomain.c procedure forwards.
+//
+
+NET_API_STATUS
+BrInitializeDomains(
+ VOID
+ );
+
+NET_API_STATUS
+BrCreateDomainInWorker(
+ LPWSTR DomainName,
+ LPWSTR ComputerName,
+ BOOLEAN IsEmulatedDomain,
+ BOOLEAN IsPrimaryDomainController
+ );
+
+PDOMAIN_INFO
+BrFindDomain(
+ LPWSTR DomainName,
+ BOOLEAN DefaultToPrimary
+ );
+
+VOID
+BrDereferenceDomain(
+ IN PDOMAIN_INFO DomainInfo
+ );
+
+VOID
+BrDeleteDomain(
+ IN PDOMAIN_INFO DomainInfo
+ );
+
+VOID
+BrUninitializeDomains(
+ VOID
+ );
diff --git a/private/net/svcdlls/browser2/server/brmain.c b/private/net/svcdlls/browser2/server/brmain.c
new file mode 100644
index 000000000..14a14a3da
--- /dev/null
+++ b/private/net/svcdlls/browser2/server/brmain.c
@@ -0,0 +1,1243 @@
+/*++
+
+Copyright (c) 1990-1992 Microsoft Corporation
+
+Module Name:
+
+ brmain.c
+
+Abstract:
+
+ This is the main routine for the NT LAN Manager Browser service.
+
+Author:
+
+ Larry Osterman (LarryO) 3-23-92
+
+Environment:
+
+ User Mode - Win32
+
+Revision History:
+
+--*/
+
+#include "precomp.h"
+#pragma hdrstop
+#if DBG
+#endif
+
+//-------------------------------------------------------------------//
+// //
+// Global variables //
+// //
+//-------------------------------------------------------------------//
+
+BR_GLOBAL_DATA
+BrGlobalData = {0};
+
+ULONG
+BrDefaultRole = {0};
+
+PLMSVCS_GLOBAL_DATA BrLmsvcsGlobalData=NULL;
+
+HANDLE BrDllReferenceHandle=NULL;
+
+//-------------------------------------------------------------------//
+// //
+// Function prototypes //
+// //
+//-------------------------------------------------------------------//
+
+NET_API_STATUS
+BrInitializeBrowser(
+ OUT LPDWORD BrInitState
+ );
+
+NET_API_STATUS
+BrInitializeBrowserService(
+ OUT LPDWORD BrInitState
+ );
+
+VOID
+BrUninitializeBrowser(
+ IN DWORD BrInitState
+ );
+VOID
+BrShutdownBrowser(
+ IN NET_API_STATUS ErrorCode,
+ IN DWORD BrInitState
+ );
+
+VOID
+BrHandleError(
+ IN BR_ERROR_CONDITION FailingCondition,
+ IN NET_API_STATUS Status,
+ IN DWORD BrInitState
+ );
+
+
+VOID
+BrowserControlHandler(
+ IN DWORD Opcode
+ );
+
+
+
+
+VOID
+LMSVCS_ENTRY_POINT( // (BROWSER_main)
+ DWORD NumArgs,
+ LPTSTR *ArgsArray,
+ PLMSVCS_GLOBAL_DATA LmsvcsGlobalData,
+ IN HANDLE SvcRefHandle
+
+ )
+/*++
+
+Routine Description:
+
+ This is the main routine of the Browser Service which registers
+ itself as an RPC server and notifies the Service Controller of the
+ Browser service control entry point.
+
+Arguments:
+
+ NumArgs - Supplies the number of strings specified in ArgsArray.
+
+ ArgsArray - Supplies string arguments that are specified in the
+ StartService API call. This parameter is ignored by the
+ Browser service.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ DWORD BrInitState = 0;
+
+ UNREFERENCED_PARAMETER(NumArgs);
+ UNREFERENCED_PARAMETER(ArgsArray);
+
+ BrDllReferenceHandle = SvcRefHandle;
+
+ //
+ // Save the LMSVCS global data for future use.
+ //
+ BrLmsvcsGlobalData = LmsvcsGlobalData;
+
+ if (BrInitializeBrowserService(&BrInitState) != NERR_Success) {
+ return;
+ }
+
+ //
+ // Process requests in this thread, and wait for termination.
+ //
+
+ //
+ // Set the browser threads to time critical priority.
+ //
+
+ SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL);
+
+
+ BrWorkerThread((PVOID)-1);
+
+ BrShutdownBrowser(
+ NERR_Success,
+ BrInitState
+ );
+
+ return;
+}
+
+
+
+NET_API_STATUS
+BrInitializeBrowserService(
+ OUT LPDWORD BrInitState
+ )
+/*++
+
+Routine Description:
+
+ This function initializes the Browser service.
+
+Arguments:
+
+ BrInitState - Returns a flag to indicate how far we got with initializing
+ the Browser service before an error occured.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NET_API_STATUS Status;
+// HMODULE BrGlobalServerHandle = NULL;
+
+ //
+ // Initialize all the status fields so that subsequent calls to
+ // SetServiceStatus need to only update fields that changed.
+ //
+ BrGlobalData.Status.dwServiceType = SERVICE_WIN32;
+ BrGlobalData.Status.dwCurrentState = SERVICE_START_PENDING;
+ BrGlobalData.Status.dwControlsAccepted = 0;
+ BrGlobalData.Status.dwCheckPoint = 0;
+ BrGlobalData.Status.dwWaitHint = BR_WAIT_HINT_TIME;
+
+ SET_SERVICE_EXITCODE(
+ NO_ERROR,
+ BrGlobalData.Status.dwWin32ExitCode,
+ BrGlobalData.Status.dwServiceSpecificExitCode
+ );
+
+#if DBG
+ BrInitializeTraceLog();
+#endif
+
+ BrPrint(( BR_INIT, "Browser starting\n"));
+
+ //
+ // Initialize Browser to receive service requests by registering the
+ // control handler.
+ //
+ if ((BrGlobalData.StatusHandle = RegisterServiceCtrlHandler(
+ SERVICE_BROWSER,
+ BrowserControlHandler
+ )) == (SERVICE_STATUS_HANDLE) NULL) {
+
+ Status = GetLastError();
+ BR_HANDLE_ERROR(BrErrorRegisterControlHandler);
+ return Status;
+ }
+
+ //
+ // Create an event which is used by the service control handler to notify
+ // the Browser service that it is time to terminate.
+ //
+
+ if ((BrGlobalData.TerminateNowEvent =
+ CreateEvent(
+ NULL, // Event attributes
+ TRUE, // Event must be manually reset
+ FALSE,
+ NULL // Initial state not signalled
+ )) == NULL) {
+
+ Status = GetLastError();
+ BR_HANDLE_ERROR(BrErrorCreateTerminateEvent);
+ return Status;
+ }
+ (*BrInitState) |= BR_TERMINATE_EVENT_CREATED;
+
+ //
+ // Notify the Service Controller for the first time that we are alive
+ // and we are start pending
+ //
+
+ if ((Status = BrGiveInstallHints( SERVICE_START_PENDING )) != NERR_Success) {
+ BR_HANDLE_ERROR(BrErrorNotifyServiceController);
+ return Status;
+ }
+
+ //
+ // Open a handle to the driver.
+ //
+ if ((Status = BrOpenDgReceiver()) != NERR_Success) {
+ BR_HANDLE_ERROR(BrErrorInitializeNetworks);
+ return Status;
+ }
+
+ BrPrint(( BR_INIT, "Devices initialized.\n"));
+ (*BrInitState) |= BR_DEVICES_INITIALIZED;
+
+ //
+ // Initialize NetBios synchronization with the service controller.
+ //
+
+ BrLmsvcsGlobalData->NetBiosOpen();
+ (*BrInitState) |= BR_NETBIOS_INITIALIZED;
+
+ //
+ // Read the configuration information to initialize the browser service.
+ //
+
+ if ((Status = BrInitializeBrowser(BrInitState)) != NERR_Success) {
+
+ BR_HANDLE_ERROR(BrErrorStartBrowser);
+ return Status;
+ }
+
+ BrPrint(( BR_INIT, "Browser initialized.\n"));
+ (*BrInitState) |= BR_BROWSER_INITIALIZED;
+
+ //
+ // Service install still pending.
+ //
+ (void) BrGiveInstallHints( SERVICE_START_PENDING );
+
+
+ //
+ // Initialize the browser service to receive RPC requests
+ //
+ // NOTE: Now all RPC servers in services.exe share the same pipe name.
+ // However, in order to support communication with version 1.0 of WinNt,
+ // it is necessary for the Client Pipe name to remain the same as
+ // it was in version 1.0. Mapping to the new name is performed in
+ // the Named Pipe File System code.
+ //
+ if ((Status = BrLmsvcsGlobalData->StartRpcServer(
+ BrLmsvcsGlobalData->SvcsRpcPipeName,
+ browser_ServerIfHandle
+ )) != NERR_Success) {
+
+ BR_HANDLE_ERROR(BrErrorStartRpcServer);
+ return Status;
+ }
+
+ (*BrInitState) |= BR_RPC_SERVER_STARTED;
+
+ //
+ // Update our announcement bits based on our current role.
+ //
+ // This will force the server to announce itself. It will also update
+ // the browser information in the driver.
+ //
+ //
+
+ if ((Status = BrUpdateAnnouncementBits( NULL, BrGlobalData.StatusHandle)) != NERR_Success) {
+ BR_HANDLE_ERROR(BrErrorNotifyServiceController);
+ return Status;
+ }
+
+ BrPrint(( BR_INIT, "Network status updated.\n"));
+
+ //
+ // We are done with starting the browser service. Tell Service
+ // Controller our new status.
+ //
+
+
+ if ((Status = BrGiveInstallHints( SERVICE_RUNNING )) != NERR_Success) {
+ BR_HANDLE_ERROR(BrErrorNotifyServiceController);
+ return Status;
+ }
+
+ BrPrint(( BR_MAIN, "Successful Initialization\n"));
+
+ return NERR_Success;
+}
+
+NET_API_STATUS
+BrInitializeBrowser(
+ OUT LPDWORD BrInitState
+ )
+
+/*++
+
+Routine Description:
+
+ This function shuts down the Browser service.
+
+Arguments:
+
+ ErrorCode - Supplies the error code of the failure
+
+ BrInitState - Supplies a flag to indicate how far we got with initializing
+ the Browser service before an error occured, thus the amount of
+ clean up needed.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ NET_API_STATUS NetStatus;
+ SERVICE_STATUS ServiceStatus;
+
+ //
+ // The browser depends on the following services being started:
+ //
+ // WORKSTATION (to initialize the bowser driver)
+ // SERVER (to receive remote APIs)
+ //
+ // Check to make sure that the services are started.
+ //
+
+ if ((NetStatus = CheckForService(SERVICE_WORKSTATION, &ServiceStatus)) != NERR_Success) {
+ LPWSTR SubStrings[2];
+ CHAR ServiceStatusString[10];
+ WCHAR ServiceStatusStringW[10];
+
+ SubStrings[0] = SERVICE_WORKSTATION;
+
+ _ultoa(ServiceStatus.dwCurrentState, ServiceStatusString, 10);
+
+ mbstowcs(ServiceStatusStringW, ServiceStatusString, 10);
+
+ SubStrings[1] = ServiceStatusStringW;
+
+ BrLogEvent(EVENT_BROWSER_DEPENDANT_SERVICE_FAILED, NetStatus, 2, SubStrings);
+
+ goto Cleanup;
+ }
+
+ if ((NetStatus = CheckForService(SERVICE_SERVER, &ServiceStatus)) != NERR_Success) {
+ LPWSTR SubStrings[2];
+ CHAR ServiceStatusString[10];
+ WCHAR ServiceStatusStringW[10];
+
+ SubStrings[0] = SERVICE_SERVER;
+ _ultoa(ServiceStatus.dwCurrentState, ServiceStatusString, 10);
+
+ mbstowcs(ServiceStatusStringW, ServiceStatusString, 10);
+
+ SubStrings[1] = ServiceStatusStringW;
+
+ BrLogEvent(EVENT_BROWSER_DEPENDANT_SERVICE_FAILED, NetStatus, 2, SubStrings);
+
+ goto Cleanup;
+ }
+
+ BrPrint(( BR_INIT, "Dependant services are running.\n"));
+
+ //
+ // We now know that our dependant services are started.
+ //
+ // Look up our configuration information.
+ //
+
+ if ((NetStatus = BrGetBrowserConfiguration()) != NERR_Success) {
+ goto Cleanup;
+ }
+
+ BrPrint(( BR_INIT, "Configuration read.\n"));
+
+ (*BrInitState) |= BR_CONFIG_INITIALIZED;
+
+ //
+ // Initialize the browser statistics now.
+ //
+
+ NumberOfServerEnumerations = 0;
+
+ NumberOfDomainEnumerations = 0;
+
+ NumberOfOtherEnumerations = 0;
+
+ NumberOfMissedGetBrowserListRequests = 0;
+
+ InitializeCriticalSection(&BrowserStatisticsLock);
+
+ //
+ // MaintainServerList == -1 means No
+ //
+
+ if (BrInfo.MaintainServerList == -1) {
+ BrPrint(( BR_CRITICAL, "MaintainServerList value set to NO. Stopping\n"));
+
+ NetStatus = NERR_BrowserConfiguredToNotRun;
+ goto Cleanup;
+ }
+
+
+ //
+ // Initialize the worker threads.
+ //
+
+ (void) BrGiveInstallHints( SERVICE_START_PENDING );
+ if ((NetStatus = BrWorkerInitialization()) != NERR_Success) {
+ goto Cleanup;
+ }
+
+ BrPrint(( BR_INIT, "Worker threads created.\n"));
+
+ (*BrInitState) |= BR_THREADS_STARTED;
+
+ //
+ // Initialize the networks module
+ //
+
+ (void) BrGiveInstallHints( SERVICE_START_PENDING );
+ BrInitializeNetworks();
+ (*BrInitState) |= BR_NETWORKS_INITIALIZED;
+
+
+ //
+ // Initialize the Domains module (and create networks for primary domain)
+ //
+
+ (void) BrGiveInstallHints( SERVICE_START_PENDING );
+ NetStatus = BrInitializeDomains();
+ if ( NetStatus != NERR_Success ) {
+ goto Cleanup;
+ }
+ (*BrInitState) |= BR_DOMAINS_INITIALIZED;
+
+
+
+
+ NetStatus = NERR_Success;
+
+ //
+ // Free Locally used resources
+ //
+Cleanup:
+ return NetStatus;
+}
+
+ VOID
+BrUninitializeBrowser(
+ IN DWORD BrInitState
+ )
+/*++
+
+Routine Description:
+
+ This function shuts down the parts of the browser service started by
+ BrInitializeBrowser.
+
+Arguments:
+
+ BrInitState - Supplies a flag to indicate how far we got with initializing
+ the Browser service before an error occured, thus the amount of
+ clean up needed.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ if (BrInitState & BR_CONFIG_INITIALIZED) {
+ BrDeleteConfiguration(BrInitState);
+ }
+
+ if (BrInitState & BR_DOMAINS_INITIALIZED) {
+ BrUninitializeDomains();
+ }
+
+ if (BrInitState & BR_NETWORKS_INITIALIZED) {
+ BrUninitializeNetworks(BrInitState);
+ }
+
+ DeleteCriticalSection(&BrowserStatisticsLock);
+
+}
+
+NET_API_STATUS
+BrElectMasterOnNet(
+ IN PNETWORK Network,
+ IN PVOID Context
+ )
+{
+ DWORD Event = (DWORD)Context;
+ PWSTR SubString[1];
+ REQUEST_ELECTION ElectionRequest;
+
+ if (!LOCK_NETWORK(Network)) {
+ return NERR_InternalError;
+ }
+
+ if (!(Network->Flags & NETWORK_RAS)) {
+
+ //
+ // Indicate we're forcing an election in the event log.
+ //
+
+ SubString[0] = Network->NetworkName.Buffer;
+
+ BrLogEvent(Event, STATUS_SUCCESS, 1, SubString);
+
+ //
+ // Force an election on this net.
+ //
+
+ ElectionRequest.Type = Election;
+
+ ElectionRequest.ElectionRequest.Version = 0;
+ ElectionRequest.ElectionRequest.Criteria = 0;
+ ElectionRequest.ElectionRequest.TimeUp = 0;
+ ElectionRequest.ElectionRequest.MustBeZero = 0;
+ ElectionRequest.ElectionRequest.ServerName[0] = '\0';
+
+ SendDatagram( BrDgReceiverDeviceHandle,
+ &Network->NetworkName,
+ &Network->DomainInfo->DomUnicodeDomainNameString,
+ Network->DomainInfo->DomUnicodeDomainName,
+ BrowserElection,
+ &ElectionRequest,
+ sizeof(ElectionRequest));
+
+ }
+ UNLOCK_NETWORK(Network);
+
+ return NERR_Success;
+}
+
+ VOID
+BrForceElectionOnAllNetworks(
+ IN PDOMAIN_INFO DomainInfo,
+ IN DWORD Event
+ )
+/*++
+
+Routine Description:
+
+ For an election on all networks for a specified domain.
+
+Arguments:
+
+ DomainInfo - Specifies the domain to force the election on
+
+ Event - Specifies the reason the election is being forced
+
+Return Value:
+
+ None.
+
+--*/
+{
+ BrEnumerateNetworksForDomain(DomainInfo, BrElectMasterOnNet, (PVOID)Event);
+}
+
+
+NET_API_STATUS
+BrShutdownBrowserForNet(
+ IN PNETWORK Network,
+ IN PVOID Context
+ )
+{
+ SERVICE_STATUS_HANDLE Handle = (SERVICE_STATUS_HANDLE)Context;
+ NET_API_STATUS NetStatus;
+
+ if (!LOCK_NETWORK(Network)) {
+ return NERR_InternalError;
+ }
+
+ NetStatus = BrUpdateBrowserStatus(Network, 0);
+
+ if ( NetStatus != NERR_Success ) {
+ BrPrint(( BR_CRITICAL,
+ "%ws: %ws: BrShutdownBrowserForNet: Cannot BrUpdateBrowserStatus %ld\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ Network->NetworkName.Buffer,
+ NetStatus ));
+ }
+
+ //
+ // Tell the server that the browser is stopping, so it will announce
+ // that at the browser is not operational.
+ //
+
+ NetStatus = I_NetServerSetServiceBitsEx(
+ NULL,
+ Network->DomainInfo->DomUnicodeComputerName,
+ Network->NetworkName.Buffer,
+ BROWSER_SERVICE_BITS_OF_INTEREST,
+ 0,
+ TRUE);
+
+ if ( NetStatus != NERR_Success ) {
+ BrPrint(( BR_CRITICAL,
+ "%ws: %ws: BrShutdownBrowserForNet: Cannot I_NetServerSetServiceBitsEx %ld\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ Network->NetworkName.Buffer,
+ NetStatus ));
+ }
+
+ //
+ // Force an election if we are the master for this network - this will
+ // cause someone else to become master.
+ //
+
+ if ( Network->Role & ROLE_MASTER ) {
+ BrElectMasterOnNet(Network, (PVOID)EVENT_BROWSER_ELECTION_SENT_LANMAN_NT_STOPPED);
+ }
+
+ UNLOCK_NETWORK(Network);
+
+ //
+ // Continue with next network regardless of success or failure of this one.
+ //
+ return NERR_Success;
+}
+
+ VOID
+BrShutdownBrowser (
+ IN NET_API_STATUS ErrorCode,
+ IN DWORD BrInitState
+ )
+/*++
+
+Routine Description:
+
+ This function shuts down the Browser service.
+
+Arguments:
+
+ ErrorCode - Supplies the error code of the failure
+
+ BrInitState - Supplies a flag to indicate how far we got with initializing
+ the Browser service before an error occured, thus the amount of
+ clean up needed.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ if (BrInitState & BR_RPC_SERVER_STARTED) {
+ //
+ // Stop the RPC server
+ //
+ BrLmsvcsGlobalData->StopRpcServer(browser_ServerIfHandle);
+ }
+
+
+ //
+ // Don't need to ask redirector to unbind from its transports when
+ // cleaning up because the redirector will tear down the bindings when
+ // it stops.
+ //
+
+ if (BrInitState & BR_DEVICES_INITIALIZED) {
+
+ if (BrInitState & BR_NETWORKS_INITIALIZED) {
+ BrEnumerateNetworks(BrShutdownBrowserForNet, (PVOID)BrGlobalData.StatusHandle);
+ }
+
+ //
+ // Shut down the datagram receiver.
+ //
+ // This will cancel all I/O outstanding on the browser for this
+ // handle.
+ //
+
+ BrShutdownDgReceiver();
+ }
+
+ //
+ // Clean up the browser threads.
+ //
+ // This will guarantee that there are no operations outstanding in
+ // the browser when it is shut down.
+ //
+
+ if (BrInitState & BR_THREADS_STARTED) {
+ BrWorkerKillThreads();
+ }
+
+ if (BrInitState & BR_BROWSER_INITIALIZED) {
+ //
+ // Shut down the browser (including removing networks).
+ //
+ BrUninitializeBrowser(BrInitState);
+ }
+
+ //
+ // Now that we're sure no one will try to use the worker threads,
+ // Unitialize the subsystem.
+ //
+
+ if (BrInitState & BR_THREADS_STARTED) {
+ BrWorkerTermination();
+ }
+
+ if (BrInitState & BR_PRELOAD_DOMAIN_LIST_READ) {
+ BrWanUninitialize();
+ }
+
+
+ if (BrInitState & BR_TERMINATE_EVENT_CREATED) {
+ //
+ // Close handle to termination event
+ //
+ CloseHandle(BrGlobalData.TerminateNowEvent);
+ }
+
+ if (BrInitState & BR_DEVICES_INITIALIZED) {
+ NtClose(BrDgReceiverDeviceHandle);
+
+ BrDgReceiverDeviceHandle = NULL;
+ }
+
+ //
+ // Tell service controller we are done using NetBios.
+ //
+ if (BrInitState & BR_NETBIOS_INITIALIZED) {
+ BrLmsvcsGlobalData->NetBiosClose();
+ }
+
+#if DBG
+ BrUninitializeTraceLog();
+#endif
+
+ //
+ // We are done with cleaning up. Tell Service Controller that we are
+ // stopped.
+ //
+
+ SET_SERVICE_EXITCODE(
+ ErrorCode,
+ BrGlobalData.Status.dwWin32ExitCode,
+ BrGlobalData.Status.dwServiceSpecificExitCode
+ );
+
+ (void) BrGiveInstallHints( SERVICE_STOPPED );
+}
+
+
+VOID
+BrHandleError(
+ IN BR_ERROR_CONDITION FailingCondition,
+ IN NET_API_STATUS Status,
+ IN DWORD BrInitState
+ )
+/*++
+
+Routine Description:
+
+ This function handles a Browser service error condition. If the error
+ condition is fatal, it shuts down the Browser service.
+
+Arguments:
+
+ FailingCondition - Supplies a value which indicates what the failure is.
+
+ Status - Supplies the status code for the failure.
+
+ BrInitState - Supplies a flag to indicate how far we got with initializing
+ the Browser service before an error occured, thus the amount of
+ clean up needed.
+
+Return Value:
+
+ None.
+
+--*/
+{
+
+ switch (FailingCondition) {
+
+ case BrErrorRegisterControlHandler:
+
+ BrPrint(( BR_CRITICAL, "Cannot register control handler "
+ FORMAT_API_STATUS "\n", Status));
+
+ BR_SHUTDOWN_BROWSER(Status);
+
+ break;
+
+ case BrErrorCreateTerminateEvent:
+
+ BrPrint(( BR_CRITICAL, "Cannot create done event "
+ FORMAT_API_STATUS "\n", Status));
+
+ BR_SHUTDOWN_BROWSER(Status);
+
+ break;
+
+ case BrErrorNotifyServiceController:
+
+ BrPrint(( BR_CRITICAL, "SetServiceStatus error "
+ FORMAT_API_STATUS "\n", Status));
+
+ BR_SHUTDOWN_BROWSER(Status);
+
+ break;
+
+ case BrErrorInitLsa:
+
+ BrPrint(( BR_CRITICAL, "LSA initialization error "
+ FORMAT_API_STATUS "\n", Status));
+
+ BR_SHUTDOWN_BROWSER(Status);
+
+ break;
+
+ case BrErrorCheckDependentServices:
+
+ BrPrint(( BR_CRITICAL, "Unable to determine status of dependant services "
+ FORMAT_API_STATUS "\n", Status));
+
+ BR_SHUTDOWN_BROWSER(Status);
+
+ break;
+
+ case BrErrorGetConfiguration:
+
+ BrPrint(( BR_CRITICAL, "Unable to get configuration "
+ FORMAT_API_STATUS "\n", Status));
+
+ BR_SHUTDOWN_BROWSER(Status);
+
+ break;
+
+ case BrErrorInitializeNetworks:
+
+ BrPrint(( BR_CRITICAL, "Unable to initialize networks "
+ FORMAT_API_STATUS "\n", Status));
+
+ BR_SHUTDOWN_BROWSER(Status);
+
+ break;
+
+ case BrErrorStartBrowser:
+
+ BrPrint(( BR_CRITICAL, "Cannot start browser "
+ FORMAT_API_STATUS "\n", Status));
+
+ if (Status == NERR_ServiceInstalled) {
+ BR_SHUTDOWN_BROWSER(NERR_WkstaInconsistentState);
+
+ } else {
+ BR_SHUTDOWN_BROWSER(Status);
+ }
+ break;
+
+ case BrErrorStartRpcServer:
+
+ BrPrint(( BR_CRITICAL, "Cannot start RPC server "
+ FORMAT_API_STATUS "\n", Status));
+
+ BR_SHUTDOWN_BROWSER(Status);
+
+ break;
+
+ case BrErrorCreateApiStructures:
+
+ BrPrint(( BR_CRITICAL, "Error in creating API structures "
+ FORMAT_API_STATUS "\n", Status));
+
+ BR_SHUTDOWN_BROWSER(Status);
+
+ break;
+
+ case BrErrorStartWorkerThreads:
+
+ BrPrint(( BR_CRITICAL, "Error in creating worker threads "
+ FORMAT_API_STATUS "\n", Status));
+
+ BR_SHUTDOWN_BROWSER(Status);
+
+ break;
+
+ default:
+ BrPrint(( BR_CRITICAL, "BrHandleError: unknown error condition %lu\n",
+ FailingCondition));
+
+ NetpAssert(FALSE);
+ BR_SHUTDOWN_BROWSER(Status);
+ break;
+
+ }
+
+}
+
+
+NET_API_STATUS
+BrGiveInstallHints(
+ DWORD NewState
+ )
+/*++
+
+Routine Description:
+
+ This function updates the Browser service status with the Service
+ Controller.
+
+Arguments:
+
+ NewState - State to tell the service contoller
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NET_API_STATUS status = NERR_Success;
+
+ //
+ // If we're not starting,
+ // we don't need this install hint.
+ //
+
+ if ( BrGlobalData.Status.dwCurrentState != SERVICE_START_PENDING &&
+ NewState == SERVICE_START_PENDING ) {
+ return NERR_Success;
+ }
+
+
+ //
+ // Update our state for the service controller.
+ //
+
+ BrGlobalData.Status.dwCurrentState = NewState;
+ switch ( NewState ) {
+ case SERVICE_RUNNING:
+ BrGlobalData.Status.dwControlsAccepted =
+ (SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN) ;
+ BrGlobalData.Status.dwCheckPoint = 0;
+ BrGlobalData.Status.dwWaitHint = 0;
+ break;
+
+ case SERVICE_START_PENDING:
+ BrGlobalData.Status.dwCheckPoint++;
+ break;
+
+ case SERVICE_STOPPED:
+ BrGlobalData.Status.dwCurrentState = SERVICE_STOPPED;
+ BrGlobalData.Status.dwControlsAccepted = 0;
+ BrGlobalData.Status.dwCheckPoint = 0;
+ BrGlobalData.Status.dwWaitHint = 0;
+ break;
+
+ case SERVICE_STOP_PENDING:
+ BrGlobalData.Status.dwCurrentState = SERVICE_STOP_PENDING;
+ BrGlobalData.Status.dwCheckPoint = 1;
+ BrGlobalData.Status.dwWaitHint = BR_WAIT_HINT_TIME;
+ break;
+ }
+
+
+ //
+ // Tell the service controller our current state.
+ //
+
+ if (BrGlobalData.StatusHandle == (SERVICE_STATUS_HANDLE) NULL) {
+ BrPrint(( BR_CRITICAL,
+ "Cannot call SetServiceStatus, no status handle.\n"
+ ));
+
+ return ERROR_INVALID_HANDLE;
+ }
+
+ if (! SetServiceStatus(BrGlobalData.StatusHandle, &BrGlobalData.Status)) {
+
+ status = GetLastError();
+
+ BrPrint(( BR_CRITICAL, "SetServiceStatus error %lu\n", status));
+ }
+
+ return status;
+}
+
+
+
+NET_API_STATUS
+BrUpdateAnnouncementBits(
+ IN PDOMAIN_INFO DomainInfo OPTIONAL,
+ IN SERVICE_STATUS_HANDLE Handle
+ )
+/*++
+
+Routine Description:
+
+ This will update the service announcement bits appropriately depending on
+ the role of the browser server.
+
+Arguments:
+
+ DomainInfo - Domain the announcement is to be made for
+ NULL implies the primary domain.
+
+ Handle - Supplies a handle for the service controller to allow it to
+ update its information for this service.
+
+Return Value:
+
+ Status - Status of the update..
+
+--*/
+{
+ NET_API_STATUS Status = NERR_Success;
+
+ Status = BrEnumerateNetworksForDomain(DomainInfo, BrUpdateNetworkAnnouncementBits, NULL);
+
+ return Status;
+}
+
+ ULONG
+BrGetBrowserServiceBits(
+ IN PNETWORK Network
+ )
+{
+ DWORD ServiceBits = 0;
+ if (Network->Role & ROLE_POTENTIAL_BACKUP) {
+ ServiceBits |= SV_TYPE_POTENTIAL_BROWSER;
+ }
+
+ if (Network->Role & ROLE_BACKUP) {
+ ServiceBits |= SV_TYPE_BACKUP_BROWSER;
+ }
+
+ if (Network->Role & ROLE_MASTER) {
+ ServiceBits |= SV_TYPE_MASTER_BROWSER;
+ }
+
+ if (Network->Role & ROLE_DOMAINMASTER) {
+ ServiceBits |= SV_TYPE_DOMAIN_MASTER;
+
+ ASSERT (ServiceBits & SV_TYPE_BACKUP_BROWSER);
+
+ }
+
+ return ServiceBits;
+
+}
+
+ NET_API_STATUS
+BrUpdateNetworkAnnouncementBits(
+ IN PNETWORK Network,
+ IN PVOID Context
+ )
+{
+ NET_API_STATUS Status = NERR_Success;
+
+ ULONG ServiceBits;
+
+ if (!LOCK_NETWORK(Network)) {
+ return NERR_InternalError;
+ }
+
+ try {
+
+ ServiceBits = BrGetBrowserServiceBits(Network);
+
+ //
+ // Have the browser update it's information.
+ //
+
+ //
+ // Don't ever tell the browser to turn off the potential bit - this
+ // has the side effect of turning off the election name.
+ //
+
+ Status = BrUpdateBrowserStatus(Network, ServiceBits | SV_TYPE_POTENTIAL_BROWSER);
+
+ if (Status != NERR_Success) {
+ try_return(Status);
+ }
+
+#if DBG
+ BrUpdateDebugInformation(L"LastServiceStatus", L"LastServiceBits", Network->NetworkName.Buffer, NULL, ServiceBits);
+#endif
+
+ Status = I_NetServerSetServiceBitsEx(
+ NULL,
+ Network->DomainInfo->DomUnicodeComputerName,
+ Network->NetworkName.Buffer,
+ BROWSER_SERVICE_BITS_OF_INTEREST,
+ ServiceBits,
+ TRUE);
+
+ if ( Status != NERR_Success) {
+
+ BrPrint(( BR_CRITICAL,
+ "%ws: %ws: BrUpdateNetworkAnnouncementBits: Cannot I_NetServerSetServiceBitsEx %ld\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ Network->NetworkName.Buffer,
+ Status ));
+
+ BrLogEvent(EVENT_BROWSER_STATUS_BITS_UPDATE_FAILED, Status, 0, NULL);
+
+ try_return(Status);
+ }
+
+
+try_exit:NOTHING;
+ } finally {
+ UNLOCK_NETWORK(Network);
+
+ }
+
+ return Status;
+}
+
+
+ VOID
+BrowserControlHandler(
+ IN DWORD Opcode
+ )
+/*++
+
+Routine Description:
+
+ This is the service control handler of the Browser service.
+
+Arguments:
+
+ Opcode - Supplies a value which specifies the action for the Browser
+ service to perform.
+
+ Arg - Supplies a value which tells a service specifically what to do
+ for an operation specified by Opcode.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ BrPrint(( BR_MAIN, "In Control Handler\n"));
+
+ switch (Opcode) {
+
+ case SERVICE_CONTROL_STOP:
+
+ if (BrGlobalData.Status.dwCurrentState != SERVICE_STOP_PENDING) {
+
+ BrPrint(( BR_MAIN, "Stopping Browser...\n"));
+
+
+ if (! SetEvent(BrGlobalData.TerminateNowEvent)) {
+
+ //
+ // Problem with setting event to terminate Browser
+ // service.
+ //
+ BrPrint(( BR_CRITICAL, "Error setting TerminateNowEvent "
+ FORMAT_API_STATUS "\n", GetLastError()));
+ NetpAssert(FALSE);
+ }
+ }
+
+ //
+ // Send the status response.
+ //
+ (void) BrGiveInstallHints( SERVICE_STOP_PENDING );
+
+ return;
+
+ case SERVICE_CONTROL_INTERROGATE:
+ break;
+
+ case SERVICE_CONTROL_SHUTDOWN:
+ //
+ // The system is being shutdown. Stop being a browser and
+ // clean up.
+ //
+
+ if (BrGlobalData.Status.dwCurrentState != SERVICE_STOP_PENDING) {
+ BrEnumerateNetworks(BrShutdownBrowserForNet, (PVOID)BrGlobalData.StatusHandle);
+ }
+
+ break;
+
+ default:
+ BrPrint(( BR_CRITICAL, "Unknown Browser opcode " FORMAT_HEX_DWORD
+ "\n", Opcode));
+ }
+
+ //
+ // Send the status response.
+ //
+ (void) BrGiveInstallHints( SERVICE_START_PENDING );
+}
diff --git a/private/net/svcdlls/browser2/server/brmain.h b/private/net/svcdlls/browser2/server/brmain.h
new file mode 100644
index 000000000..3cd8461d1
--- /dev/null
+++ b/private/net/svcdlls/browser2/server/brmain.h
@@ -0,0 +1,175 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ brmain.h
+
+Abstract:
+
+ Private header file which defines the global data which is used for
+ communication between the service control handler and the
+ rest of the NT Workstation service.
+
+Author:
+
+ Rita Wong (ritaw) 06-May-1991
+
+Revision History:
+
+--*/
+
+#ifndef _BRMAIN_INCLUDED_
+#define _BRMAIN_INCLUDED_
+
+#include <brnames.h> // Service interface names
+
+//
+// Time for the sender of a start or stop request to the Workstation
+// service to wait (in milliseconds) before checking on the
+// Workstation service again to see if it is done.
+//
+#define BR_WAIT_HINT_TIME 45000 // 45 seconds
+
+//
+// Defines to indicate how far we managed to initialize the Browser
+// service before an error is encountered and the extent of clean up needed
+//
+
+#define BR_TERMINATE_EVENT_CREATED 0x00000001
+#define BR_DEVICES_INITIALIZED 0x00000002
+#define BR_RPC_SERVER_STARTED 0x00000004
+#define BR_THREADS_STARTED 0x00000008
+#define BR_NETWORKS_INITIALIZED 0x00000010
+#define BR_BROWSER_INITIALIZED 0x00000020
+#define BR_CONFIG_INITIALIZED 0x00000040
+#define BR_PRELOAD_DOMAIN_LIST_READ 0x00000080
+#define BR_NETBIOS_INITIALIZED 0x00000100
+#define BR_DOMAINS_INITIALIZED 0x00000200
+
+#define BR_BROWSE_LIST_CREATED 0x20000000
+
+#define BR_API_STRUCTURES_CREATED BR_BROWSE_LIST_CREATED
+
+//
+// This macro is called after the redirection of print or comm device
+// has been paused or continued. If either the print or comm device is
+// paused the service is considered paused.
+//
+#define BR_RESET_PAUSE_STATE(BrStatus) { \
+ BrStatus &= ~(SERVICE_PAUSE_STATE); \
+ BrStatus |= (BrStatus & SERVICE_REDIR_PAUSED) ? SERVICE_PAUSED : \
+ SERVICE_ACTIVE; \
+ }
+
+
+
+//
+// Call BrHandleError with the appropriate error condition
+//
+#define BR_HANDLE_ERROR(ErrorCondition) \
+ BrHandleError( \
+ ErrorCondition, \
+ Status, \
+ *BrInitState \
+ );
+
+//
+// Call BrShutdownWorkstation with the exit code
+//
+#define BR_SHUTDOWN_BROWSER(ErrorCode) \
+ BrShutdownBrowser( \
+ ErrorCode, \
+ BrInitState \
+ );
+
+
+//-------------------------------------------------------------------//
+// //
+// Type definitions //
+// //
+//-------------------------------------------------------------------//
+
+typedef enum _BR_ERROR_CONDITION {
+ BrErrorRegisterControlHandler = 0,
+ BrErrorCreateTerminateEvent,
+ BrErrorNotifyServiceController,
+ BrErrorInitLsa,
+ BrErrorStartBrowser,
+ BrErrorGetConfiguration,
+ BrErrorCheckDependentServices,
+ BrErrorInitializeNetworks,
+ BrErrorStartRpcServer,
+ BrErrorInitMessageSend,
+ BrErrorCreateApiStructures,
+ BrErrorStartWorkerThreads,
+ BrErrorInitializeLogon
+} BR_ERROR_CONDITION, *PBR_ERROR_CONDITION;
+
+typedef struct _BR_GLOBAL_DATA {
+
+ //
+ // Workstation service status
+ //
+ SERVICE_STATUS Status;
+
+ //
+ // Service status handle
+ //
+ SERVICE_STATUS_HANDLE StatusHandle;
+
+ //
+ // When the control handler is asked to stop the Workstation service,
+ // it signals this event to notify all threads of the Workstation
+ // service to terminate.
+ //
+ HANDLE TerminateNowEvent;
+
+ HANDLE EventHandle;
+
+} BR_GLOBAL_DATA, *PBR_GLOBAL_DATA;
+
+extern BR_GLOBAL_DATA BrGlobalData;
+
+extern PLMSVCS_GLOBAL_DATA BrLmsvcsGlobalData;
+
+extern
+ULONG
+BrDefaultRole;
+
+#define BROWSER_SERVICE_BITS_OF_INTEREST \
+ ( SV_TYPE_POTENTIAL_BROWSER | \
+ SV_TYPE_BACKUP_BROWSER | \
+ SV_TYPE_MASTER_BROWSER | \
+ SV_TYPE_DOMAIN_MASTER )
+
+ULONG
+BrGetBrowserServiceBits(
+ IN PNETWORK Network
+ );
+
+NET_API_STATUS
+BrUpdateAnnouncementBits(
+ IN PDOMAIN_INFO DomainInfo,
+ IN SERVICE_STATUS_HANDLE Handle
+ );
+
+NET_API_STATUS
+BrUpdateNetworkAnnouncementBits(
+ IN PNETWORK Network,
+ IN PVOID Context
+ );
+
+NET_API_STATUS
+BrGiveInstallHints(
+ DWORD NewState
+ );
+
+VOID
+BrForceElectionOnAllNetworks(
+ IN PDOMAIN_INFO DomainInfo,
+ IN DWORD Event
+ );
+
+#endif // ifndef _BRMAIN_INCLUDED_
diff --git a/private/net/svcdlls/browser2/server/brmaster.c b/private/net/svcdlls/browser2/server/brmaster.c
new file mode 100644
index 000000000..3a522bc98
--- /dev/null
+++ b/private/net/svcdlls/browser2/server/brmaster.c
@@ -0,0 +1,1864 @@
+/*++
+
+Copyright (c) 1991-1992 Microsoft Corporation
+
+Module Name:
+
+ browser.c
+
+Abstract:
+
+ This module contains the worker routines for the NetWksta APIs
+ implemented in the Workstation service.
+
+Author:
+
+ Rita Wong (ritaw) 20-Feb-1991
+
+Revision History:
+
+--*/
+
+#include "precomp.h"
+#pragma hdrstop
+
+
+//-------------------------------------------------------------------//
+// //
+// Local structure definitions //
+// //
+//-------------------------------------------------------------------//
+
+ULONG
+DomainAnnouncementPeriodicity[] = {1*60*1000, 1*60*1000, 5*60*1000, 5*60*1000, 10*60*1000, 10*60*1000, 15*60*1000};
+
+ULONG
+DomainAnnouncementMax = (sizeof(DomainAnnouncementPeriodicity) / sizeof(ULONG)) - 1;
+
+//-------------------------------------------------------------------//
+// //
+// Local function prototypes //
+// //
+//-------------------------------------------------------------------//
+
+VOID
+BrGetMasterServerNamesOnAllNets(
+ IN PVOID Context
+ );
+
+NET_API_STATUS
+BrGetMasterServerNameForNet(
+ IN PNETWORK Network,
+ IN PVOID Context
+ );
+
+
+VOID
+BecomeMasterCompletion (
+ IN PVOID Ctx
+ );
+
+NET_API_STATUS
+StartMasterBrowserTimer(
+ IN PNETWORK Network
+ );
+
+NET_API_STATUS
+AnnounceMasterToDomainMaster(
+ IN PNETWORK Network,
+ IN LPWSTR ServerName
+ );
+
+//-------------------------------------------------------------------//
+// //
+// Global function prototypes //
+// //
+//-------------------------------------------------------------------//
+
+ NET_API_STATUS
+PostBecomeMaster(
+ PNETWORK Network
+ )
+/*++
+
+Routine Description:
+
+ This function is the worker routine called to actually issue a BecomeMaster
+ FsControl to the bowser driver on all the bound transports. It will
+ complete when the machine becomes a master browser server.
+
+ Please note that this might never complete.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ Status - The status of the operation.
+
+--*/
+{
+ NTSTATUS Status = NERR_Success;
+
+ if (!LOCK_NETWORK(Network)) {
+ return NERR_InternalError;
+ }
+
+ if (!(Network->Flags & NETWORK_BECOME_MASTER_POSTED)) {
+
+ Network->Flags |= NETWORK_BECOME_MASTER_POSTED;
+
+ //
+ // Make certain that we have the browser election name added
+ // before we allow ourselves to become a master. This is a NOP
+ // if we already have the election name added.
+ //
+
+ Status = BrUpdateBrowserStatus(Network, BrGetBrowserServiceBits(Network) | SV_TYPE_POTENTIAL_BROWSER);
+
+ if (Status != NERR_Success) {
+ Network->Flags &= ~NETWORK_BECOME_MASTER_POSTED;
+
+ BrPrint(( BR_CRITICAL,
+ "%ws: %ws: Unable to update browser status: %ld\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ Network->NetworkName.Buffer,
+ Status ));
+ UNLOCK_NETWORK(Network);
+ return Status;
+ }
+
+ Status = BrIssueAsyncBrowserIoControl(Network,
+ IOCTL_LMDR_BECOME_MASTER,
+ BecomeMasterCompletion,
+ NULL );
+ }
+
+ UNLOCK_NETWORK(Network);
+
+ return Status;
+}
+
+NET_API_STATUS
+BrRecoverFromFailedPromotion(
+ IN PVOID Ctx
+ )
+/*++
+
+Routine Description:
+
+ When we attempt to promote a machine to master browser and fail, we will
+ effectively shut down the browser for a period of time. When that period
+ of time expires, we will call BrRecoverFromFailedPromotion to recover
+ from the failure.
+
+ This routine will do one of the following:
+ 1) Force the machine to become a backup browser,
+ or 2) Attempt to discover the name of the master.
+
+Arguments:
+
+ IN PVOID Ctx - The network structure we failed on.
+
+Return Value:
+
+ Status - The status of the operation (usually ignored).
+
+--*/
+
+
+{
+ PNETWORK Network = Ctx;
+ NET_API_STATUS Status;
+ BOOL NetworkLocked = FALSE;
+
+ //
+ // Prevent the network from being deleted while we're in this timer routine.
+ //
+ if ( BrReferenceNetwork( Network ) == NULL ) {
+ return NERR_InternalError;
+ }
+
+ try {
+
+ if (!LOCK_NETWORK(Network)) {
+ try_return( Status = NERR_InternalError);
+ }
+
+ BrPrint(( BR_MASTER,
+ "%ws: %ws: BrRecoverFromFailedPromotion.\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ Network->NetworkName.Buffer ));
+
+ NetworkLocked = TRUE;
+ //
+ // We had better not be the master now.
+ //
+
+ ASSERT (!(Network->Role & ROLE_MASTER));
+
+ //
+ // If we're configured to become a backup by default, then become
+ // a backup now.
+ //
+
+ if (BrInfo.MaintainServerList == 1) {
+
+ BrPrint(( BR_MASTER,
+ "%ws: %ws: BrRecoverFromFailedPromotion. Become backup.\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ Network->NetworkName.Buffer ));
+ Status = BecomeBackup(Network, NULL);
+
+ if (Status != NERR_Success) {
+ BrPrint(( BR_CRITICAL,
+ "%ws: %ws: Could not become backup: %lx\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ Network->NetworkName.Buffer,
+ Status));
+
+ }
+ } else {
+ BrPrint(( BR_MASTER,
+ "%ws: %ws: BrRecoverFromFailedPromotion. FindMaster.\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ Network->NetworkName.Buffer));
+
+ UNLOCK_NETWORK(Network);
+
+ NetworkLocked = FALSE;
+
+ //
+ // Now try to figure out who is the master.
+ //
+
+ Status = GetMasterServerNames(Network);
+
+ //
+ // Ignore the status from this and re-lock the network to
+ // recover cleanly.
+ //
+
+ if (!LOCK_NETWORK(Network)) {
+ try_return( Status = NERR_InternalError);
+ }
+
+ NetworkLocked = TRUE;
+
+ }
+
+ //
+ // Otherwise, just let sleeping dogs lie.
+ //
+try_exit:NOTHING;
+ } finally {
+ if (NetworkLocked) {
+ UNLOCK_NETWORK(Network);
+ }
+
+ BrDereferenceNetwork( Network );
+ }
+
+ return Status;
+}
+
+
+ VOID
+BecomeMasterCompletion (
+ IN PVOID Ctx
+ )
+/*++
+
+Routine Description:
+
+ This function is called by the I/O system when the request to become a
+ master completes.
+
+ Please note that it is possible that the request may complete with an
+ error.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ Status - The status of the operation.
+
+--*/
+{
+ NET_API_STATUS Status;
+ PBROWSERASYNCCONTEXT Context = Ctx;
+ PNETWORK Network = Context->Network;
+ BOOLEAN NetworkLocked = FALSE;
+ BOOLEAN NetReferenced = FALSE;
+ ULONG ServiceBits;
+
+
+ try {
+ //
+ // Ensure the network wasn't deleted from under us.
+ //
+ if ( BrReferenceNetwork( Network ) == NULL ) {
+ try_return(NOTHING);
+ }
+ NetReferenced = TRUE;
+
+ //
+ // Lock the network structure.
+ //
+
+ if (!LOCK_NETWORK(Network)) {
+ try_return(NOTHING);
+ }
+ NetworkLocked = TRUE;
+
+ Network->Flags &= ~NETWORK_BECOME_MASTER_POSTED;
+
+ if (!NT_SUCCESS(Context->IoStatusBlock.Status)) {
+ BrPrint(( BR_CRITICAL,
+ "%ws: %ws: Failure in BecomeMaster: %X\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ Network->NetworkName.Buffer,
+ Context->IoStatusBlock.Status));
+
+ try_return(NOTHING);
+
+ }
+
+ BrPrint(( BR_MASTER,
+ "%ws: %ws: BecomeMasterCompletion. Now master on network %ws\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ Network->NetworkName.Buffer));
+
+ //
+ // If we're already a master, ignore this request.
+ //
+
+ if (Network->Role & ROLE_MASTER) {
+ try_return(NOTHING);
+ }
+
+ //
+ // Cancel any outstanding backup timers - we don't download the list
+ // anymore.
+ //
+
+ Status = BrCancelTimer(&Network->BackupBrowserTimer);
+
+ if (!NT_SUCCESS(Status)) {
+ BrPrint(( BR_CRITICAL,
+ "%ws: %ws: Could not stop backup timer: %lx\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ Network->NetworkName.Buffer,
+ Status));
+ }
+
+ //
+ // Figure out what service bits we should be using when announcing ourselves
+ //
+
+ Network->Role |= ROLE_MASTER;
+
+ ServiceBits = BrGetBrowserServiceBits(Network);
+
+ Status = BrUpdateBrowserStatus(Network, ServiceBits | SV_TYPE_POTENTIAL_BROWSER);
+
+ if (Status != NERR_Success) {
+ BrPrint(( BR_MASTER,
+ "%ws: %ws: Unable to set master announcement bits in browser: %ld\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ Network->NetworkName.Buffer,
+ Status));
+
+ //
+ // When we're in this state, we can't rely on our being a backup
+ // browser - we may not be able to retrieve a valid list of
+ // browsers from the master.
+ //
+
+ Network->Role &= ~ROLE_BACKUP;
+
+ Network->NumberOfFailedPromotions += 1;
+
+ //
+ // Log every 5 failed promotion attempts, and after having logged 5
+ // promotion events, stop logging them, this means that it's been
+ // 25 times that we've tried to promote, and it's not likely to get
+ // any better. We'll keep on trying, but we won't complain any more.
+ //
+
+ if ((Network->NumberOfFailedPromotions % 5) == 0) {
+ ULONG AStatStatus;
+ LPWSTR SubString[1];
+ WCHAR CurrentMasterName[CNLEN+1];
+
+ if (Network->NumberOfPromotionEventsLogged < 5) {
+
+ AStatStatus = GetNetBiosMasterName(
+ Network->NetworkName.Buffer,
+ Network->DomainInfo->DomUnicodeDomainName,
+ CurrentMasterName,
+ BrLmsvcsGlobalData->NetBiosReset
+ );
+
+ if (AStatStatus == NERR_Success) {
+ SubString[0] = CurrentMasterName;
+
+ BrLogEvent(EVENT_BROWSER_MASTER_PROMOTION_FAILED, Status, 1, SubString);
+ } else {
+ BrLogEvent(EVENT_BROWSER_MASTER_PROMOTION_FAILED_NO_MASTER, Status, 0, NULL);
+ }
+
+ Network->NumberOfPromotionEventsLogged += 1;
+
+ if (Network->NumberOfPromotionEventsLogged == 5) {
+ BrLogEvent(EVENT_BROWSER_MASTER_PROMOTION_FAILED_STOPPING, Status, 0, NULL);
+ }
+ }
+ }
+
+ //
+ // We were unable to promote ourselves to master.
+ //
+ // We want to set our role back to browser, and re-issue the become
+ // master request.
+ //
+
+ BrStopMaster(Network);
+
+ BrSetTimer(&Network->MasterBrowserTimer, FAILED_PROMOTION_PERIODICITY*1000, BrRecoverFromFailedPromotion, Network);
+
+ } else {
+
+ //
+ // Initialize the number of times the master timer has run.
+ //
+
+ Network->MasterBrowserTimerCount = 0;
+
+ Status = StartMasterBrowserTimer(Network);
+
+ if (Status != NERR_Success) {
+ BrPrint(( BR_CRITICAL,
+ "%ws: %ws: Could not start browser master timer: %ld\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ Network->NetworkName.Buffer,
+ Status ));
+ }
+
+ Network->NumberOfFailedPromotions = 0;
+
+ Network->NumberOfPromotionEventsLogged = 0;
+
+ Network->MasterAnnouncementIndex = 0;
+
+ Status = I_NetServerSetServiceBitsEx(
+ NULL,
+ Network->DomainInfo->DomUnicodeComputerName,
+ Network->NetworkName.Buffer,
+ BROWSER_SERVICE_BITS_OF_INTEREST,
+ ServiceBits,
+ TRUE);
+
+
+ if (Status == NERR_Success) {
+
+ //
+ // We successfully became the master.
+ //
+ // Now announce ourselves as the new master for this domain.
+ //
+
+ BrMasterAnnouncement(Network);
+
+ //
+ // Populate the browse list with the information retrieved
+ // while we were a backup browser.
+ //
+
+ if (Network->TotalBackupServerListEntries != 0) {
+ MergeServerList(&Network->BrowseTable,
+ 101,
+ Network->BackupServerList,
+ Network->TotalBackupServerListEntries,
+ Network->TotalBackupServerListEntries
+ );
+ MIDL_user_free(Network->BackupServerList);
+
+ Network->BackupServerList = NULL;
+
+ Network->TotalBackupServerListEntries = 0;
+ }
+
+ if (Network->TotalBackupDomainListEntries != 0) {
+ MergeServerList(&Network->DomainList,
+ 101,
+ Network->BackupDomainList,
+ Network->TotalBackupDomainListEntries,
+ Network->TotalBackupDomainListEntries
+ );
+ MIDL_user_free(Network->BackupDomainList);
+
+ Network->BackupDomainList = NULL;
+
+ Network->TotalBackupDomainListEntries = 0;
+ }
+
+
+
+ //
+ // Unlock the network before calling BrWanMasterInitialize.
+ //
+ UNLOCK_NETWORK(Network);
+ NetworkLocked = FALSE;
+
+
+ //
+ // Run the master browser timer routine to get the entire domains
+ // list of servers.
+ //
+
+ if (Network->Flags & NETWORK_WANNISH) {
+ BrWanMasterInitialize(Network);
+ MasterBrowserTimerRoutine(Network);
+ }
+
+ } else {
+
+ BrLogEvent(EVENT_BROWSER_STATUS_BITS_UPDATE_FAILED, Status, 0, NULL);
+
+ //
+ // Stop being a master browser.
+ //
+
+ BrStopMaster(Network);
+
+ BrPrint(( BR_MASTER,
+ "%ws: %ws: Unable to set master announcement bits to server: %ld\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ Network->NetworkName.Buffer,
+ Status));
+
+ try_return(NOTHING);
+
+ }
+
+ try_return(NOTHING);
+
+ }
+try_exit:NOTHING;
+ } finally {
+
+ //
+ // Make sure there's a become master oustanding.
+ //
+
+ PostBecomeMaster(Network);
+
+ if (NetworkLocked) {
+ UNLOCK_NETWORK(Network);
+ }
+
+ if ( NetReferenced ) {
+ BrDereferenceNetwork( Network );
+ }
+
+ MIDL_user_free(Context->RequestPacket);
+
+ MIDL_user_free(Context);
+ }
+
+}
+
+
+
+
+NET_API_STATUS
+ChangeMasterPeriodicityWorker(
+ PNETWORK Network,
+ PVOID Ctx
+ )
+/*++
+
+Routine Description:
+
+ This function changes the master periodicity for a single network.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ Status - The status of the operation.
+
+--*/
+{
+
+ //
+ // Lock the network
+ //
+
+ if (LOCK_NETWORK(Network)) {
+
+ //
+ // Ensure we're the master.
+ //
+
+ if ( Network->Role & ROLE_MASTER ) {
+ NET_API_STATUS NetStatus;
+
+ //
+ // Cancel the timer to ensure it doesn't go off while we're
+ // processing this change.
+ //
+
+ NetStatus = BrCancelTimer(&Network->MasterBrowserTimer);
+ ASSERT (NetStatus == NERR_Success);
+
+ //
+ // Unlock the network while we execute the timer routine.
+ //
+ UNLOCK_NETWORK( Network );
+
+ //
+ // Call the timer routine immediately.
+ //
+ MasterBrowserTimerRoutine(Network);
+
+ } else {
+ UNLOCK_NETWORK( Network );
+ }
+
+ }
+
+ UNREFERENCED_PARAMETER(Ctx);
+
+ return NERR_Success;
+}
+
+
+
+VOID
+BrChangeMasterPeriodicity (
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This function is called when the master periodicity is changed in the
+ registry.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ (VOID)BrEnumerateNetworks(ChangeMasterPeriodicityWorker, NULL);
+}
+
+ NET_API_STATUS
+StartMasterBrowserTimer(
+ IN PNETWORK Network
+ )
+{
+ NET_API_STATUS Status;
+
+ Status = BrSetTimer( &Network->MasterBrowserTimer,
+ BrInfo.MasterPeriodicity*1000,
+ MasterBrowserTimerRoutine,
+ Network);
+
+ return Status;
+
+}
+
+
+typedef struct _BROWSER_GETNAMES_CONTEXT {
+ WORKER_ITEM WorkItem;
+
+ PDOMAIN_INFO DomainInfo;
+
+} BROWSER_GETNAMES_CONTEXT, *PBROWSER_GETNAMES_CONTEXT;
+
+ VOID
+BrGetMasterServerNamesAysnc(
+ PDOMAIN_INFO DomainInfo
+ )
+/*++
+
+Routine Description:
+
+ Queue a workitem to asynchronously get the master browser names for
+ all transports of a domain.
+
+Arguments:
+
+ DomainInfo - Identifies the domain to query.
+
+Return Value:
+
+ None
+
+--*/
+{
+ PBROWSER_GETNAMES_CONTEXT Context;
+
+ //
+ // Allocate context for this async call.
+ //
+
+ Context = LocalAlloc( 0, sizeof(BROWSER_GETNAMES_CONTEXT) );
+
+ if ( Context == NULL ) {
+ return;
+ }
+
+ //
+ // Just queue this for later execution.
+ // We're doing this for information purposes only. In the case that
+ // the master can't be found, we don't want to wait for completion.
+ // (e.g., on a machine with multiple transports and the net cable is
+ // pulled)
+ //
+
+ Context->DomainInfo = DomainInfo;
+
+ BrInitializeWorkItem( &Context->WorkItem,
+ BrGetMasterServerNamesOnAllNets,
+ Context );
+
+ BrQueueWorkItem( &Context->WorkItem );
+
+ return;
+
+}
+
+ VOID
+BrGetMasterServerNamesOnAllNets(
+ IN PVOID Context
+ )
+/*++
+
+Routine Description:
+
+ Worker routine to asynchronously get the master browser names for
+ all transports of a domain.
+
+Arguments:
+
+ Context - Context containing the workitem and the description of the
+ domain to query.
+
+Return Value:
+
+ None
+
+--*/
+{
+ (VOID) BrEnumerateNetworksForDomain(
+ ((PBROWSER_GETNAMES_CONTEXT)Context)->DomainInfo,
+ BrGetMasterServerNameForNet,
+ NULL );
+
+ (VOID) LocalFree( Context );
+ return;
+
+}
+
+ NET_API_STATUS
+BrGetMasterServerNameForNet(
+ IN PNETWORK Network,
+ IN PVOID Context
+ )
+/*++
+
+Routine Description:
+
+ Routine to get the master browser name for a particular network.
+
+Arguments:
+
+ Context - Not used.
+
+Return Value:
+
+ Status of the operation.
+
+--*/
+{
+ BrPrint(( BR_INIT,
+ "%ws: %ws: FindMaster during startup\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ Network->NetworkName.Buffer));
+
+ //
+ // We only call this on startup, so on IPX networks, don't bother to
+ // find out the master.
+ //
+
+ if (!(Network->Flags & NETWORK_IPX)) {
+ GetMasterServerNames(Network);
+ }
+
+ return NERR_Success;
+}
+
+ NET_API_STATUS
+GetMasterServerNames(
+ IN PNETWORK Network
+ )
+/*++
+
+Routine Description:
+
+ This function is the worker routine called to determine the name of the
+ master browser server for a particular network.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ Status - The status of the operation.
+
+--*/
+{
+ NET_API_STATUS Status;
+
+ PLMDR_REQUEST_PACKET RequestPacket = NULL;
+
+ BrPrint(( BR_INIT,
+ "%ws: %ws: FindMaster started\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ Network->NetworkName.Buffer));
+
+ //
+ // This request could cause an election. Make sure that if we win
+ // the election that we can handle it.
+ //
+
+ PostBecomeMaster( Network);
+
+ RequestPacket = MIDL_user_allocate(
+ (UINT) sizeof(LMDR_REQUEST_PACKET)+
+ MAXIMUM_FILENAME_LENGTH*sizeof(WCHAR)
+ );
+
+ if (RequestPacket == NULL) {
+ return(ERROR_NOT_ENOUGH_MEMORY);
+ }
+
+ RequestPacket->Version = LMDR_REQUEST_PACKET_VERSION_DOM;
+
+ //
+ // Set level to TRUE to indicate that find master should initiate
+ // a findmaster request.
+ //
+
+ RequestPacket->Level = 1;
+
+ RequestPacket->TransportName = Network->NetworkName;
+ RequestPacket->EmulatedDomainName = Network->DomainInfo->DomUnicodeDomainNameString;
+
+ //
+ // Reference the network while the I/O is pending.
+ //
+
+ Status = BrDgReceiverIoControl(BrDgReceiverDeviceHandle,
+ IOCTL_LMDR_GET_MASTER_NAME,
+ RequestPacket,
+ sizeof(LMDR_REQUEST_PACKET)+Network->NetworkName.Length,
+ RequestPacket,
+ sizeof(LMDR_REQUEST_PACKET)+MAXIMUM_FILENAME_LENGTH*sizeof(WCHAR),
+ NULL);
+
+ if (Status != NERR_Success) {
+
+ BrPrint(( BR_INIT,
+ "%ws: %ws: FindMaster failed: %ld\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ Network->NetworkName.Buffer,
+ Status));
+ MIDL_user_free(RequestPacket);
+
+ return(Status);
+ }
+
+ if (!LOCK_NETWORK(Network)) {
+ MIDL_user_free(RequestPacket);
+
+ return NERR_InternalError;
+ }
+
+
+ //
+ // Copy the master browser name into the network structure
+ //
+
+ wcsncpy( Network->UncMasterBrowserName,
+ RequestPacket->Parameters.GetMasterName.Name,
+ UNCLEN+1 );
+
+ Network->UncMasterBrowserName[UNCLEN] = L'\0';
+
+ ASSERT ( NetpIsUncComputerNameValid( Network->UncMasterBrowserName ) );
+
+ BrPrint(( BR_INIT, "%ws: %ws: FindMaster succeeded. Master: %ws\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ Network->NetworkName.Buffer,
+ Network->UncMasterBrowserName));
+
+ UNLOCK_NETWORK(Network);
+
+ MIDL_user_free(RequestPacket);
+
+ return Status;
+}
+
+ VOID
+MasterBrowserTimerRoutine (
+ IN PVOID TimerContext
+ )
+{
+ IN PNETWORK Network = TimerContext;
+ NET_API_STATUS Status;
+ PVOID ServerList = NULL;
+ PVOID WinsServerList = NULL;
+ ULONG EntriesInList;
+ ULONG TotalEntriesInList;
+ LPWSTR TransportName;
+ BOOLEAN NetLocked = FALSE;
+ LPWSTR PrimaryDomainController = NULL;
+ LPWSTR PrimaryWinsServerAddress = NULL;
+ LPWSTR SecondaryWinsServerAddress = NULL;
+
+ //
+ // Prevent the network from being deleted while we're in this timer routine.
+ //
+ if ( BrReferenceNetwork( Network ) == NULL ) {
+ return;
+ }
+
+ try {
+
+ //
+ // If we're not a master any more, blow away this request.
+ //
+
+ if (!(Network->Role & ROLE_MASTER)) {
+ try_return(NOTHING);
+ }
+
+ if (!LOCK_NETWORK(Network)) {
+ try_return(NOTHING);
+ }
+
+ NetLocked = TRUE;
+
+ TransportName = Network->NetworkName.Buffer;
+
+
+ //
+ // Now that we have the network locked, re-test to see if we are
+ // still the master.
+ //
+
+ if (!(Network->Role & ROLE_MASTER)) {
+ try_return(NOTHING);
+ }
+
+ Network->MasterBrowserTimerCount += 1;
+
+ //
+ // If this is a wannish network, we always want to run the master
+ // timer because we might have information about other subnets
+ // in our list.
+ //
+
+ if (Network->Flags & NETWORK_WANNISH) {
+
+ //
+ // Age out servers and domains from the server list.
+ //
+
+ AgeInterimServerList(&Network->BrowseTable);
+
+ AgeInterimServerList(&Network->DomainList);
+
+ //
+ // If we're not the PDC, then we need to retrieve the list
+ // from the PDC....
+ //
+
+ if (!Network->DomainInfo->IsPrimaryDomainController) {
+
+ ASSERT (NetLocked);
+
+ UNLOCK_NETWORK(Network);
+
+ NetLocked = FALSE;
+
+ Status = NetGetDCName(NULL, NULL, (LPBYTE *)&PrimaryDomainController);
+
+ //
+ // If the PDC can be found,
+ // Exchange server lists with it.
+ //
+
+ if (Status == NERR_Success) {
+
+ //
+ // Tell the Domain Master (PDC) that we're a master browser.
+ //
+
+ (VOID) AnnounceMasterToDomainMaster (Network, &PrimaryDomainController[2]);
+
+
+ //
+ // Retrieve the list of all the servers from the PDC.
+ //
+
+ Status = RxNetServerEnum(PrimaryDomainController,
+ TransportName,
+ 101,
+ (LPBYTE *)&ServerList,
+ 0xffffffff,
+ &EntriesInList,
+ &TotalEntriesInList,
+ SV_TYPE_ALL,
+ NULL,
+ NULL
+ );
+
+ if ((Status == NERR_Success) || (Status == ERROR_MORE_DATA)) {
+
+ ASSERT (!NetLocked);
+
+ if (LOCK_NETWORK(Network)) {
+
+ NetLocked = TRUE;
+
+ if (Network->Role & ROLE_MASTER) {
+ (VOID) MergeServerList(&Network->BrowseTable,
+ 101,
+ ServerList,
+ EntriesInList,
+ TotalEntriesInList );
+ }
+ }
+
+ }
+
+ if (ServerList != NULL) {
+ MIDL_user_free(ServerList);
+ ServerList = NULL;
+ }
+
+ if (NetLocked) {
+ UNLOCK_NETWORK(Network);
+ NetLocked = FALSE;
+ }
+
+ //
+ // Retrieve the list of all the domains from the PDC.
+ //
+
+ Status = RxNetServerEnum(PrimaryDomainController,
+ TransportName,
+ 101,
+ (LPBYTE *)&ServerList,
+ 0xffffffff,
+ &EntriesInList,
+ &TotalEntriesInList,
+ SV_TYPE_DOMAIN_ENUM,
+ NULL,
+ NULL
+ );
+
+ if ((Status == NERR_Success) || (Status == ERROR_MORE_DATA)) {
+
+ ASSERT (!NetLocked);
+
+ if (LOCK_NETWORK(Network)) {
+
+ NetLocked = TRUE;
+
+ if (Network->Role & ROLE_MASTER) {
+ (VOID) MergeServerList(&Network->DomainList,
+ 101,
+ ServerList,
+ EntriesInList,
+ TotalEntriesInList );
+ }
+ }
+
+ }
+
+ if (ServerList != NULL) {
+ MIDL_user_free(ServerList);
+ ServerList = NULL;
+ }
+
+
+ //
+ // Unlock the network before calling BrWanMasterInitialize.
+ //
+
+ if (NetLocked) {
+ UNLOCK_NETWORK(Network);
+ NetLocked = FALSE;
+ }
+
+ BrWanMasterInitialize(Network);
+
+ }
+
+
+ //
+ // If we're on the PDC, we need to get the list of servers from
+ // the WINS server.
+ //
+
+ } else {
+ //
+ // Ensure a GetMasterAnnouncement request is posted to the bowser.
+ //
+
+ (VOID) PostGetMasterAnnouncement ( Network, NULL );
+
+ //
+ // We want to contact the WINS server now, so we figure out the
+ // IP address of our primary WINS server
+ //
+
+ Status = BrGetWinsServerName(&Network->NetworkName,
+ &PrimaryWinsServerAddress,
+ &SecondaryWinsServerAddress);
+ if (Status == NERR_Success) {
+
+ //
+ // Don't keep the network locked during the WINS query
+ //
+
+ if (NetLocked) {
+ UNLOCK_NETWORK(Network);
+ NetLocked = FALSE;
+ }
+
+ //
+ // This transport supports WINS queries, so query the WINS
+ // server to retrieve the list of domains on this adapter.
+ //
+
+ Status = BrQueryWinsServer(PrimaryWinsServerAddress,
+ SecondaryWinsServerAddress,
+ &WinsServerList,
+ &EntriesInList,
+ &TotalEntriesInList
+ );
+
+ if (Status == NERR_Success) {
+
+ //
+ // Lock the network to merge the server list
+ //
+
+ ASSERT (!NetLocked);
+
+ if (LOCK_NETWORK(Network)) {
+ NetLocked = TRUE;
+
+ if (Network->Role & ROLE_MASTER) {
+
+ //
+ // Merge the list of domains from WINS into the one collected elsewhere
+ //
+ (VOID) MergeServerList(
+ &Network->DomainList,
+ 1010, // Special level to not overide current values
+ WinsServerList,
+ EntriesInList,
+ TotalEntriesInList );
+ }
+ }
+ }
+
+ }
+ }
+
+
+ //
+ // Restart the timer for this domain.
+ //
+ // Wait to restart it until we're almost done with this iteration.
+ // Otherwise, we could end up with two copies of this routine
+ // running.
+ //
+
+ Status = StartMasterBrowserTimer(Network);
+
+ if (Status != NERR_Success) {
+ BrPrint(( BR_CRITICAL,
+ "%ws: %ws: Unable to restart browser backup timer: %lx\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ Network->NetworkName.Buffer,
+ Status));
+ try_return(NOTHING);
+ }
+
+ } else {
+
+ //
+ // If it is a lan-ish transport, and we have run the master
+ // timer for enough times (ie. we've been a master
+ // for "long enough", we can toss the interim server list in the
+ // master, because the bowser driver will have enough data in its
+ // list by now.
+ //
+
+ if (Network->MasterBrowserTimerCount >= MASTER_BROWSER_LAN_TIMER_LIMIT) {
+
+ ASSERT (NetLocked);
+
+ //
+ // Make all the servers and domains in the interim server list
+ // go away - they aren't needed any more for a LAN-ish transport.
+ //
+
+ UninitializeInterimServerList(&Network->BrowseTable);
+
+ ASSERT (Network->BrowseTable.EntriesRead == 0);
+
+ ASSERT (Network->BrowseTable.TotalEntries == 0);
+
+ UninitializeInterimServerList(&Network->DomainList);
+
+ ASSERT (Network->DomainList.EntriesRead == 0);
+
+ ASSERT (Network->DomainList.TotalEntries == 0);
+
+ } else {
+
+ //
+ // Age out servers and domains from the server list.
+ //
+
+ AgeInterimServerList(&Network->BrowseTable);
+
+ AgeInterimServerList(&Network->DomainList);
+
+ //
+ // Restart the timer for this domain.
+ //
+
+ Status = StartMasterBrowserTimer(Network);
+
+ if (Status != NERR_Success) {
+ BrPrint(( BR_CRITICAL,
+ "%ws: %ws: Unable to restart browser backup timer: %lx\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ Network->NetworkName.Buffer,
+ Status));
+ try_return(NOTHING);
+ }
+ }
+
+ }
+try_exit:NOTHING;
+ } finally {
+
+ if (PrimaryDomainController != NULL) {
+ NetApiBufferFree(PrimaryDomainController);
+ }
+
+ if (PrimaryWinsServerAddress) {
+ MIDL_user_free(PrimaryWinsServerAddress);
+ }
+
+ if (SecondaryWinsServerAddress) {
+ MIDL_user_free(SecondaryWinsServerAddress);
+ }
+
+ if (WinsServerList) {
+ MIDL_user_free(WinsServerList);
+ }
+
+ if (NetLocked) {
+ UNLOCK_NETWORK(Network);
+ }
+
+ BrDereferenceNetwork( Network );
+ }
+}
+
+
+ VOID
+BrMasterAnnouncement(
+ IN PVOID TimerContext
+ )
+/*++
+
+Routine Description:
+
+ This routine is called to announce the domain on the local sub-net.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None
+
+--*/
+
+{
+ PNETWORK Network = TimerContext;
+ ULONG Periodicity;
+ DWORD ServiceBits;
+ NET_API_STATUS Status;
+
+ //
+ // Prevent the network from being deleted while we're in this timer routine.
+ //
+ if ( BrReferenceNetwork( Network ) == NULL ) {
+ return;
+ }
+
+ if (!LOCK_NETWORK(Network)) {
+ BrDereferenceNetwork( Network );
+ return;
+ }
+
+
+ //
+ // Make absolutely certain that the server thinks that the browser service
+ // bits for this transport are up to date. We do NOT have to force an
+ // announcement, since theoretically, the status didn't change.
+ //
+
+ ServiceBits = BrGetBrowserServiceBits(Network);
+
+ Status = I_NetServerSetServiceBitsEx(
+ NULL,
+ Network->DomainInfo->DomUnicodeComputerName,
+ Network->NetworkName.Buffer,
+ BROWSER_SERVICE_BITS_OF_INTEREST,
+ ServiceBits,
+ FALSE);
+
+
+ if (Status != NERR_Success) {
+
+ BrPrint(( BR_CRITICAL,
+ "%ws: %ws: BrMasterAnnouncement: Cannot I_NetServerSetServiceBitsEx %ld\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ Network->NetworkName.Buffer,
+ Status ));
+
+ BrLogEvent(EVENT_BROWSER_STATUS_BITS_UPDATE_FAILED, Status, 0, NULL);
+ }
+
+ Periodicity = DomainAnnouncementPeriodicity[Network->MasterAnnouncementIndex];
+
+ BrSetTimer(&Network->MasterBrowserAnnouncementTimer, Periodicity, BrMasterAnnouncement, Network);
+
+ if (Network->MasterAnnouncementIndex != DomainAnnouncementMax) {
+ Network->MasterAnnouncementIndex += 1;
+ }
+
+ //
+ // Announce this domain to the world using the current periodicity.
+ //
+
+ BrAnnounceDomain(Network, Periodicity);
+
+ UNLOCK_NETWORK(Network);
+ BrDereferenceNetwork( Network );
+}
+
+
+ NET_API_STATUS
+BrStopMaster(
+ IN PNETWORK Network
+ )
+{
+ NET_API_STATUS Status;
+ ULONG ServiceBits;
+
+ //
+ // This guy is shutting down - set his role to 0 and announce.
+ //
+
+ if (!LOCK_NETWORK(Network)) {
+ return NERR_InternalError;
+ }
+
+ try {
+
+ BrPrint(( BR_MASTER,
+ "%ws: %ws: Stopping being master.\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ Network->NetworkName.Buffer));
+
+ //
+ // When we stop being a master, we can no longer be considered a
+ // backup either, since backups maintain their server list
+ // differently than the master.
+ //
+
+
+ Network->Role &= ~(ROLE_MASTER | ROLE_BACKUP);
+
+ ServiceBits = BrGetBrowserServiceBits(Network);
+
+ ASSERT ((ServiceBits & SV_TYPE_MASTER_BROWSER) == 0);
+
+ Status = BrUpdateBrowserStatus(Network, ServiceBits | SV_TYPE_POTENTIAL_BROWSER);
+
+ if (Status != NERR_Success) {
+ BrPrint(( BR_MASTER,
+ "%ws: %ws: Unable to clear master announcement bits in browser: %ld\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ Network->NetworkName.Buffer,
+ Status));
+ try_return(Status);
+ }
+
+ Status = I_NetServerSetServiceBitsEx(
+ NULL,
+ Network->DomainInfo->DomUnicodeComputerName,
+ Network->NetworkName.Buffer,
+ BROWSER_SERVICE_BITS_OF_INTEREST,
+ ServiceBits,
+ TRUE );
+
+ if (Status != NERR_Success) {
+
+ BrLogEvent(EVENT_BROWSER_STATUS_BITS_UPDATE_FAILED, Status, 0, NULL);
+
+ BrPrint(( BR_MASTER,
+ "%ws: %ws: Unable to clear master announcement bits to server: %ld\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ Network->NetworkName.Buffer,
+ Status));
+
+ try_return(Status);
+ }
+
+ //
+ // Stop our master related timers.
+ //
+
+ Status = BrCancelTimer(&Network->MasterBrowserAnnouncementTimer);
+
+ ASSERT (Status == NERR_Success);
+
+ Status = BrCancelTimer(&Network->MasterBrowserTimer);
+
+ ASSERT (Status == NERR_Success);
+
+try_exit:NOTHING;
+ } finally {
+ UNLOCK_NETWORK(Network);
+ }
+
+ return Status;
+
+}
+
+ NET_API_STATUS
+AnnounceMasterToDomainMaster(
+ IN PNETWORK Network,
+ IN LPWSTR ServerName
+ )
+{
+ NET_API_STATUS Status;
+ UNICODE_STRING EmulatedDomainName;
+ CHAR Buffer[sizeof(MASTER_ANNOUNCEMENT)+CNLEN+1];
+ PMASTER_ANNOUNCEMENT MasterAnnouncementp = (PMASTER_ANNOUNCEMENT)Buffer;
+
+ lstrcpyA( MasterAnnouncementp->MasterAnnouncement.MasterName,
+ Network->DomainInfo->DomOemComputerName );
+
+ MasterAnnouncementp->Type = MasterAnnouncement;
+
+ Status = SendDatagram( BrDgReceiverDeviceHandle,
+ &Network->NetworkName,
+ &Network->DomainInfo->DomUnicodeDomainNameString,
+ ServerName,
+ ComputerName,
+ MasterAnnouncementp,
+ FIELD_OFFSET(MASTER_ANNOUNCEMENT, MasterAnnouncement.MasterName) + Network->DomainInfo->DomOemComputerNameLength+sizeof(CHAR)
+ );
+
+ return Status;
+}
+
+ NET_API_STATUS NET_API_FUNCTION
+I_BrowserrResetNetlogonState(
+ IN BROWSER_IDENTIFY_HANDLE ServerName
+ )
+
+/*++
+
+Routine Description:
+
+ This routine will reset the bowser's concept of the state of the netlogon
+ service. It is called by the UI when it promotes or demotes a DC.
+
+
+Arguments:
+
+ IN BROWSER_IDENTIFY_HANDLE ServerName - Ignored.
+
+Return Value:
+
+ NET_API_STATUS - The status of this request.
+
+--*/
+
+{
+ //
+ // This routine has been superceeded by I_BrowserrSetNetlogonState
+ //
+ return ERROR_NOT_SUPPORTED;
+
+ UNREFERENCED_PARAMETER( ServerName );
+}
+
+
+ NET_API_STATUS NET_API_FUNCTION
+I_BrowserrSetNetlogonState(
+ IN BROWSER_IDENTIFY_HANDLE ServerName,
+ IN LPWSTR DomainName,
+ IN LPWSTR EmulatedComputerName,
+ IN DWORD Role
+ )
+
+/*++
+
+Routine Description:
+
+ This routine will reset the bowser's concept of the state of the netlogon
+ service. It is called by the Netlogon service when it promotes or demotes a DC.
+
+Arguments:
+
+ ServerName - Ignored.
+
+ DomainName - Name of the domain whose role has changed. If the domain name specified
+ isn't the primary domain or an emulated domain, an emulated domain is added.
+
+ EmulatedComputerName - Name of the server within DomainName that's being emulated.
+
+ Role - New role of the machine.
+ Zero implies emulated domain is to be deleted.
+
+Return Value:
+
+ NET_API_STATUS - The status of this request.
+
+--*/
+
+{
+ NET_API_STATUS NetStatus = NERR_Success;
+ PDOMAIN_INFO DomainInfo = NULL;
+ BOOLEAN ConfigCritSectLocked = FALSE;
+ BOOLEAN RoleChanged = FALSE;
+
+ if (!BrInfo.IsLanmanNt) {
+ NetStatus = NERR_NotPrimary;
+ goto Cleanup;
+ }
+
+ //
+ // See if we're handling the specified domain.
+ //
+
+ DomainInfo = BrFindDomain( DomainName, FALSE );
+
+ if ( DomainInfo == NULL ) {
+
+ //
+ // Try to create the domain.
+ //
+
+ if ( EmulatedComputerName == NULL || Role == 0 ) {
+ NetStatus = ERROR_NO_SUCH_DOMAIN;
+ goto Cleanup;
+ }
+
+ NetStatus = BrCreateDomainInWorker(
+ DomainName,
+ EmulatedComputerName,
+ TRUE,
+ (BOOLEAN)((Role & BROWSER_ROLE_PDC) != 0));
+
+ if ( NetStatus != NERR_Success ) {
+ goto Cleanup;
+ }
+
+ //
+ // Find the newly created domain
+ //
+ DomainInfo = BrFindDomain( DomainName, FALSE );
+
+ if ( DomainInfo == NULL ) {
+ NetStatus = ERROR_NO_SUCH_DOMAIN;
+ goto Cleanup;
+ }
+ }
+
+ //
+ // Set the role to PDC.
+ //
+
+ EnterCriticalSection(&BrInfo.ConfigCritSect);
+ ConfigCritSectLocked = TRUE;
+
+ if ( Role & BROWSER_ROLE_PDC ) {
+
+ //
+ // We don't think we're the PDC. Update our information.
+ //
+ if ( !DomainInfo->IsPrimaryDomainController) {
+ DomainInfo->IsPrimaryDomainController = TRUE;
+ RoleChanged = TRUE;
+
+ DomainInfo->IsDomainMasterBrowser = TRUE;
+
+ //
+ // Make sure a GetMasterAnnouncement request is pending.
+ //
+
+ (VOID) BrPostGetMasterAnnouncementInWorker( DomainInfo );
+ }
+
+
+ //
+ // Set the role to BDC.
+ //
+
+ } else if ( Role & BROWSER_ROLE_BDC ) {
+
+ //
+ // We think we're the PDC. Update our information.
+ //
+
+ if ( DomainInfo->IsPrimaryDomainController) {
+ DomainInfo->IsPrimaryDomainController = FALSE;
+ RoleChanged = TRUE;
+
+ //
+ // We're not a domain master any more, since we're not the PDC.
+ //
+
+ DomainInfo->IsDomainMasterBrowser = FALSE;
+ }
+
+ //
+ // Delete the Emulated domain.
+ //
+
+ } else if ( Role == 0 ) {
+
+ //
+ // Don't allow the primary domain to be deleted.
+ //
+
+ if ( !DomainInfo->IsEmulatedDomain ) {
+ NetStatus = ERROR_NO_SUCH_DOMAIN;
+ goto Cleanup;
+ }
+
+ BrDeleteDomain( DomainInfo );
+
+ //
+ // All other combinations are invalid
+ //
+ } else {
+ NetStatus = ERROR_INVALID_PARAMETER;
+ goto Cleanup;
+ }
+
+ LeaveCriticalSection(&BrInfo.ConfigCritSect);
+ ConfigCritSectLocked = FALSE;
+
+
+ //
+ // Handle changing the role.
+ //
+
+ if ( RoleChanged ) {
+ //
+ // Update this information for all transports now. This will also update
+ // the status for the driver.
+ //
+
+ NetStatus = BrUpdateAnnouncementBits( DomainInfo, BrGlobalData.StatusHandle);
+
+ if (NetStatus == NERR_Success) {
+ //
+ // The update worked. Now force an election and let the best server
+ // win.
+ //
+
+ BrForceElectionOnAllNetworks(DomainInfo, EVENT_BROWSER_ELECTION_SENT_ROLE_CHANGED);
+ }
+ }
+
+
+ //
+ // Free locally used resources
+ //
+Cleanup:
+
+ if ( ConfigCritSectLocked ) {
+ LeaveCriticalSection(&BrInfo.ConfigCritSect);
+ }
+
+ if ( DomainInfo != NULL ) {
+ BrDereferenceDomain( DomainInfo );
+ }
+ return NetStatus;
+
+}
+
+
+ NET_API_STATUS NET_API_FUNCTION
+I_BrowserrQueryEmulatedDomains (
+ IN LPTSTR ServerName OPTIONAL,
+ IN OUT PBROWSER_EMULATED_DOMAIN_CONTAINER EmulatedDomains
+ )
+
+/*++
+
+Routine Description:
+
+ Enumerate the emulated domain list.
+
+Arguments:
+
+ ServerName - Supplies the name of server to execute this function
+
+ EmulatedDomains - Returns a pointer to a an allocated array of emulated domain
+ information.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NET_API_STATUS NetStatus;
+
+ PBROWSER_EMULATED_DOMAIN Domains = NULL;
+ PLIST_ENTRY DomainEntry;
+ PDOMAIN_INFO DomainInfo;
+ DWORD BufferSize;
+ DWORD Index;
+ LPBYTE Where;
+ DWORD EntryCount;
+
+ //
+ // Initialization
+ //
+
+ EnterCriticalSection(&NetworkCritSect);
+ EmulatedDomains->EntriesRead = 0;
+ EmulatedDomains->Buffer = NULL;
+
+
+
+ //
+ // Loop through the list of emulated domains determining the size of the
+ // return buffer.
+ //
+
+ BufferSize = 0;
+ EntryCount = 0;
+
+ for (DomainEntry = ServicedDomains.Flink ;
+ DomainEntry != &ServicedDomains;
+ DomainEntry = DomainEntry->Flink ) {
+
+ DomainInfo = CONTAINING_RECORD(DomainEntry, DOMAIN_INFO, Next);
+
+ if ( DomainInfo->IsEmulatedDomain ) {
+ BufferSize += sizeof(BROWSER_EMULATED_DOMAIN) +
+ DomainInfo->DomUnicodeDomainNameString.Length + sizeof(WCHAR) +
+ DomainInfo->DomUnicodeComputerNameLength * sizeof(WCHAR) + sizeof(WCHAR);
+ EntryCount ++;
+ }
+
+ }
+
+ //
+ // Allocate the return buffer.
+ //
+
+ Domains = MIDL_user_allocate( BufferSize );
+
+ if ( Domains == NULL ) {
+ NetStatus = ERROR_NOT_ENOUGH_MEMORY;
+ goto Cleanup;
+ }
+
+
+ //
+ // Copy the information into the buffer
+ //
+
+ Index = 0;
+ Where = (LPBYTE)(Domains+EntryCount);
+
+ for (DomainEntry = ServicedDomains.Flink ;
+ DomainEntry != &ServicedDomains;
+ DomainEntry = DomainEntry->Flink ) {
+
+ DomainInfo = CONTAINING_RECORD(DomainEntry, DOMAIN_INFO, Next);
+
+ if ( DomainInfo->IsEmulatedDomain ) {
+
+ Domains[Index].DomainName = (LPWSTR)Where;
+ wcscpy( (LPWSTR) Where, DomainInfo->DomUnicodeDomainNameString.Buffer );
+ Where += DomainInfo->DomUnicodeDomainNameString.Length + sizeof(WCHAR);
+
+ Domains[Index].EmulatedServerName = (LPWSTR)Where;
+ wcscpy( (LPWSTR) Where, DomainInfo->DomUnicodeComputerName );
+ Where += DomainInfo->DomUnicodeComputerNameLength * sizeof(WCHAR) + sizeof(WCHAR);
+
+ Domains[Index].Role = DomainInfo->IsPrimaryDomainController ?
+ BROWSER_ROLE_PDC :
+ BROWSER_ROLE_BDC;
+
+ Index ++;
+ }
+
+ }
+
+ //
+ // Success
+ //
+
+ EmulatedDomains->Buffer = (PVOID) Domains;
+ EmulatedDomains->EntriesRead = EntryCount;
+ NetStatus = NERR_Success;
+
+
+Cleanup:
+ LeaveCriticalSection(&NetworkCritSect);
+
+ return NetStatus;
+}
diff --git a/private/net/svcdlls/browser2/server/brmaster.h b/private/net/svcdlls/browser2/server/brmaster.h
new file mode 100644
index 000000000..7117e273c
--- /dev/null
+++ b/private/net/svcdlls/browser2/server/brmaster.h
@@ -0,0 +1,121 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ brmaster.h
+
+Abstract:
+
+ Private header file which defines the global data which is used for
+ communication between the service control handler and the
+ rest of the NT Workstation service.
+
+Author:
+
+ Rita Wong (ritaw) 06-May-1991
+
+Revision History:
+
+--*/
+
+#ifndef _BRMASTER_INCLUDED_
+#define _BRMASTER_INCLUDED_
+
+ NET_API_STATUS
+PostBecomeMaster(
+ PNETWORK Network
+ );
+
+NET_API_STATUS
+BrPostGetMasterAnnouncementInWorker(
+ PDOMAIN_INFO DomainInfo
+ );
+
+NET_API_STATUS
+PostGetMasterAnnouncement (
+ PNETWORK Network,
+ PVOID Ctx
+ );
+
+NET_API_STATUS
+BrStopMaster(
+ IN PNETWORK Network
+ );
+
+VOID
+BrGetMasterServerNamesAysnc(
+ PDOMAIN_INFO DomainInfo
+ );
+
+NET_API_STATUS
+GetMasterServerNames(
+ IN PNETWORK Network
+ );
+
+VOID
+BrMasterAnnouncement(
+ IN PVOID Context
+ );
+
+VOID
+MasterBrowserTimerRoutine (
+ IN PVOID TimerContext
+ );
+
+VOID
+BrChangeMasterPeriodicity (
+ VOID
+ );
+
+VOID
+BrBrowseTableInsertRoutine(
+ IN PINTERIM_SERVER_LIST InterimTable,
+ IN PINTERIM_ELEMENT InterimElement
+ );
+
+VOID
+BrBrowseTableDeleteRoutine(
+ IN PINTERIM_SERVER_LIST InterimTable,
+ IN PINTERIM_ELEMENT InterimElement
+ );
+
+VOID
+BrBrowseTableUpdateRoutine(
+ IN PINTERIM_SERVER_LIST InterimTable,
+ IN PINTERIM_ELEMENT InterimElement
+ );
+
+BOOLEAN
+BrBrowseTableAgeRoutine(
+ IN PINTERIM_SERVER_LIST InterimTable,
+ IN PINTERIM_ELEMENT InterimElement
+ );
+
+VOID
+BrDomainTableInsertRoutine(
+ IN PINTERIM_SERVER_LIST InterimTable,
+ IN PINTERIM_ELEMENT InterimElement
+ );
+
+VOID
+BrDomainTableDeleteRoutine(
+ IN PINTERIM_SERVER_LIST InterimTable,
+ IN PINTERIM_ELEMENT InterimElement
+ );
+
+VOID
+BrDomainTableUpdateRoutine(
+ IN PINTERIM_SERVER_LIST InterimTable,
+ IN PINTERIM_ELEMENT InterimElement
+ );
+
+BOOLEAN
+BrDomainTableAgeRoutine(
+ IN PINTERIM_SERVER_LIST InterimTable,
+ IN PINTERIM_ELEMENT InterimElement
+ );
+
+#endif // ifndef _BRBACKUP_INCLUDED_
+
diff --git a/private/net/svcdlls/browser2/server/browsdom.c b/private/net/svcdlls/browser2/server/browsdom.c
new file mode 100644
index 000000000..55bc767d9
--- /dev/null
+++ b/private/net/svcdlls/browser2/server/browsdom.c
@@ -0,0 +1,32 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ browslst.c
+
+Abstract:
+
+ This module contains the worker routines for managing domain lists
+ for the browser service
+
+Author:
+
+ Larry Osterman (larryo) 25-Mar-1992
+
+Revision History:
+
+--*/
+
+#include "precomp.h"
+#pragma hdrstop
+
+//-------------------------------------------------------------------//
+// //
+// Local function prototypes //
+// //
+//-------------------------------------------------------------------//
+
+
+
diff --git a/private/net/svcdlls/browser2/server/browsdom.h b/private/net/svcdlls/browser2/server/browsdom.h
new file mode 100644
index 000000000..b3df93f43
--- /dev/null
+++ b/private/net/svcdlls/browser2/server/browsdom.h
@@ -0,0 +1,46 @@
+
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ browsdom.h
+
+Abstract:
+
+ Private header file to be included by Browser service modules that
+ need to deal with the browser list.
+
+Author:
+
+ Larry Osterman (larryo) 3-Mar-1992
+
+Revision History:
+
+--*/
+
+
+#ifndef _BROWSDOM_INCLUDED_
+#define _BROWSDOM_INCLUDED_
+
+RTL_GENERIC_COMPARE_RESULTS
+BrCompareDomainListEntry(
+ PRTL_GENERIC_TABLE Table,
+ PVOID FirstStruct,
+ PVOID SecondStruct
+ );
+
+ PVOID
+BrAllocateDomainListEntry(
+ PRTL_GENERIC_TABLE Table,
+ CLONG ByteSize
+ );
+
+ PVOID
+BrFreeDomainListEntry(
+ PRTL_GENERIC_TABLE Table,
+ CLONG ByteSize
+ );
+
+#endif // _BROWSDOM_INCLUDED_
diff --git a/private/net/svcdlls/browser2/server/browser.c b/private/net/svcdlls/browser2/server/browser.c
new file mode 100644
index 000000000..18ae245e1
--- /dev/null
+++ b/private/net/svcdlls/browser2/server/browser.c
@@ -0,0 +1,1633 @@
+/*++
+
+Copyright (c) 1991-1992 Microsoft Corporation
+
+Module Name:
+
+ browser.c
+
+Abstract:
+
+ This module contains the worker routines for the NetWksta APIs
+ implemented in the Workstation service.
+
+Author:
+
+ Rita Wong (ritaw) 20-Feb-1991
+
+Revision History:
+
+--*/
+
+#include "precomp.h"
+#pragma hdrstop
+
+
+//-------------------------------------------------------------------//
+// //
+// Local structure definitions //
+// //
+//-------------------------------------------------------------------//
+
+//-------------------------------------------------------------------//
+// //
+// Local function prototypes //
+// //
+//-------------------------------------------------------------------//
+
+
+NET_API_STATUS
+BecomeFullBackup(
+ IN PNETWORK Network,
+ IN PVOID Context
+ );
+
+VOID
+CompleteAsyncBrowserIoControl(
+ IN PVOID ApcContext,
+ IN PIO_STATUS_BLOCK IoStatusBlock,
+ IN ULONG Reserved
+ );
+
+VOID
+BecomeBackupCompletion (
+ IN PVOID Ctx
+ );
+
+
+VOID
+ChangeBrowserRole (
+ IN PVOID Ctx
+ );
+
+NET_API_STATUS
+PostWaitForNewMasterName(
+ PNETWORK Network,
+ LPWSTR MasterName OPTIONAL
+ );
+
+VOID
+NewMasterCompletionRoutine(
+ IN PVOID Ctx
+ );
+
+NET_API_STATUS
+BrRetrieveInterimServerList(
+ IN PNETWORK Network,
+ IN ULONG ServerType
+ );
+
+//-------------------------------------------------------------------//
+// //
+// Global function prototypes //
+// //
+//-------------------------------------------------------------------//
+
+ NET_API_STATUS
+BrBecomeBackup(
+ PDOMAIN_INFO DomainInfo
+ )
+/*++
+
+Routine Description:
+
+ Force this machine to become a backup browser on all networks for the
+ specified domain.
+
+Arguments:
+
+ DomainInfo - Specifies the domain to become the backup browser for.
+
+Return Value:
+
+ The status of the operation.
+
+--*/
+{
+ return BrEnumerateNetworksForDomain( DomainInfo, BecomeFullBackup, NULL);
+}
+
+ NET_API_STATUS
+BecomeFullBackup(
+ IN PNETWORK Network,
+ IN PVOID Context
+ )
+/*++
+
+Routine Description:
+
+ This function performs all the operations needed to make a browser server
+ a backup browser server when starting the browser from scratch.
+
+Arguments:
+
+ Network - Network to become backup browser for
+
+ Context - Not used.
+
+Return Value:
+
+ Status - The status of the operation.
+
+--*/
+{
+ NET_API_STATUS Status;
+
+ //
+ // Checkpoint the service controller - this gives us 30 seconds/transport
+ // before the service controller gets unhappy.
+ //
+
+ (void) BrGiveInstallHints( SERVICE_START_PENDING );
+
+ if (!LOCK_NETWORK(Network)) {
+ return NERR_InternalError;
+ }
+
+ //
+ // Were starting from "net start browser". We need to push in potential
+ // browser before calling BecomeBackup to make the browser add the election
+ // name.
+ //
+
+ Status = BrUpdateBrowserStatus(Network, SV_TYPE_POTENTIAL_BROWSER);
+
+ if (Status != NERR_Success) {
+ BrPrint(( BR_CRITICAL,
+ "%ws: %ws: Unable to update browser status: %ld\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ Network->NetworkName.Buffer,
+ Status ));
+ UNLOCK_NETWORK(Network);
+ return Status;
+ }
+
+ //
+ // We want to ignore any failures from becoming a backup browser.
+ //
+ // We do this because we will fail to become a backup on a disconnected
+ // (or connected) RAS link, and if we failed this routine, we would
+ // fail to start at all.
+ //
+ // We will handle failure to become a backup in a "reasonable manner"
+ // inside BecomeBackup.
+ //
+
+ BecomeBackup(Network, Context);
+
+ UNLOCK_NETWORK(Network);
+
+ return NERR_Success;
+
+}
+
+ NET_API_STATUS
+BecomeBackup(
+ IN PNETWORK Network,
+ IN PVOID Context
+ )
+/*++
+
+Routine Description:
+
+ This function performs all the operations needed to make a browser server
+ a backup browser server
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ Status - The status of the operation.
+
+
+NOTE:::: THIS ROUTINE IS CALLED WITH THE NETWORK STRUCTURE LOCKED!!!!!
+
+
+--*/
+{
+ NET_API_STATUS Status = NERR_Success;
+ PUNICODE_STRING MasterName = Context;
+
+ BrPrint(( BR_BACKUP,
+ "%ws: %ws: BecomeBackup called\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ Network->NetworkName.Buffer));
+
+ if (Network->TimeStoppedBackup != 0 &&
+ (BrCurrentSystemTime() - Network->TimeStoppedBackup) <= (BrInfo.BackupBrowserRecoveryTime / 1000)) {
+
+ //
+ // We stopped being a backup too recently for us to restart being
+ // a backup again, so just return a generic error.
+ //
+
+ //
+ // Before we return, make sure we're not a backup in the eyes of
+ // the browser.
+ //
+
+ BrPrint(( BR_BACKUP,
+ "%ws: %ws: can't BecomeBackup since we were backup recently.\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ Network->NetworkName.Buffer));
+ BrStopBackup(Network);
+
+ return ERROR_ACCESS_DENIED;
+
+ }
+
+ //
+ // If we know the name of the master, then we must have become a backup
+ // after being a potential, in which case we already have a
+ // becomemaster request outstanding.
+ //
+
+ if (MasterName == NULL) {
+
+ //
+ // Post a BecomeMaster request for each server. This will complete
+ // when the machine becomes the master browser server (ie. it wins an
+ // election).
+ //
+
+ //
+ // Please note that we only post it if the machine is a backup -
+ // if it's a potential master, then the become master will have
+ // already been posted.
+ //
+
+ Status = PostBecomeMaster(Network);
+
+ if (Status != NERR_Success) {
+
+ return(Status);
+ }
+
+ //
+ // Find out the name of the master on each network. This will force an
+ // election if necessary. Please note that we must post the BecomeMaster
+ // IoControl first to allow us to handle an election.
+ //
+
+ //
+ // We unlock the network, because this may cause us to become promoted
+ // to a master.
+ //
+
+ BrPrint(( BR_BACKUP,
+ "%ws: %ws: FindMaster called from BecomeBackup\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ Network->NetworkName.Buffer));
+
+ UNLOCK_NETWORK(Network);
+
+ Status = GetMasterServerNames(Network);
+
+ if (Status != NERR_Success) {
+
+ //
+ // Re-lock the network structure so we will return with the
+ // network locked.
+ //
+
+ if (!LOCK_NETWORK(Network)) {
+ return NERR_InternalError;
+ }
+
+ //
+ // We couldn't find who the master is. Stop being a backup now.
+ //
+
+ BrPrint(( BR_BACKUP,
+ "%ws: %ws: can't BecomeBackup since we can't find master.\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ Network->NetworkName.Buffer));
+
+ BrStopBackup(Network);
+
+ //
+ // If we're a master now, we should return success. We've not
+ // become a backup, but it wasn't an error.
+ //
+ // ERROR_MORE_DATA is the mapping for
+ // STATUS_MORE_PROCESSING_REQUIRED which is returned when this
+ // situation happens.
+ //
+
+ if ((Status == ERROR_MORE_DATA) || (Network->Role & ROLE_MASTER)) {
+ Status = NERR_Success;
+ }
+
+ return(Status);
+ }
+
+ if (!LOCK_NETWORK(Network)) {
+ return NERR_InternalError;
+ }
+
+ //
+ // We managed to become a master. We want to return right away.
+ //
+
+ if (Network->Role & ROLE_MASTER) {
+
+ return NERR_Success;
+ }
+
+ }
+
+#ifdef notdef
+ //
+ // ?? For now, we'll always PostForRoleChange on all transports regardless
+ // of role.
+ // We not only need to do it here. But we need to do it when we become
+ // the master so we can find out when we loose an election.
+ //
+
+
+ //
+ // We're now a backup, we need to issue an API that will complete if the
+ // browse master doesn't like us (and thus forces us to shutdown).
+ //
+ //
+
+ PostWaitForRoleChange ( Network );
+#endif // notdef
+
+ PostWaitForNewMasterName(Network, Network->UncMasterBrowserName );
+
+ //
+ // Unlock the network structure before calling BackupBrowserTimerRoutine.
+ //
+
+ UNLOCK_NETWORK(Network);
+
+ //
+ // Run the timer that causes the browser to download a new browse list
+ // from the master. This will seed our server and domain lists to
+ // guarantee that any clients have a reasonable list. It will also
+ // restart the timer to announce later on.
+ //
+
+ Status = BackupBrowserTimerRoutine(Network);
+
+ if (!LOCK_NETWORK(Network)) {
+ return NERR_InternalError;
+ }
+
+ if (Status == NERR_Success) {
+
+ ASSERT (Network->Role & ROLE_BACKUP);
+
+ //
+ // We're now a backup server, announce ourselves as such.
+ //
+
+ Status = BrUpdateNetworkAnnouncementBits(Network, NULL);
+
+ if (Status != NERR_Success) {
+
+ BrPrint(( BR_CRITICAL,
+ "%ws: %ws: Unable to become backup: %ld\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ Network->NetworkName.Buffer,
+ Status));
+
+ if (Network->Role & ROLE_BACKUP) {
+
+ //
+ // Make sure that we're going to become a potential browser
+ // (we might not if we're an advanced server).
+ //
+
+ Network->Role |= ROLE_POTENTIAL_BACKUP;
+
+ //
+ // We were unable to become a backup.
+ //
+ // We need to back out and become a potential browser now.
+ //
+ //
+
+ BrPrint(( BR_BACKUP,
+ "%ws: %ws: can't BecomeBackup since we can't update announce bits.\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ Network->NetworkName.Buffer));
+ BrStopBackup(Network);
+
+ PostBecomeBackup(Network);
+
+ }
+ }
+
+ return Status;
+
+ }
+
+ return Status;
+}
+
+
+ NET_API_STATUS
+BrBecomePotentialBrowser (
+ IN PVOID TimerContext
+ )
+/*++
+
+Routine Description:
+
+ This routine is called when a machine has stopped being a backup browser.
+
+ It runs after a reasonable timeout period has elapsed, and marks the
+ machine as a potential browser.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ Status - The status of the operation.
+
+
+--*/
+
+{
+ IN PNETWORK Network = TimerContext;
+ NET_API_STATUS Status;
+
+ //
+ // Prevent the network from being deleted while we're in this timer routine.
+ //
+ if ( BrReferenceNetwork( Network ) == NULL ) {
+ return NERR_InternalError;
+ }
+
+
+ //
+ // Mark this guy as a potential browser.
+ //
+
+ try {
+
+ if (!LOCK_NETWORK(Network)) {
+ try_return(Status = NERR_InternalError );
+ }
+
+ BrPrint(( BR_BACKUP,
+ "%ws: %ws: BrBecomePotentialBrowser called\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ Network->NetworkName.Buffer));
+
+ //
+ // Reset that we've stopped being a backup, since it's been long
+ // enough.
+ //
+
+ Network->TimeStoppedBackup = 0;
+
+ if (BrInfo.MaintainServerList == 0) {
+ Network->Role |= ROLE_POTENTIAL_BACKUP;
+
+ Status = BrUpdateNetworkAnnouncementBits(Network, NULL);
+
+ if (Status != NERR_Success) {
+ BrPrint(( BR_BACKUP,
+ "%ws: %ws: Unable to reset backup announcement bits: %ld\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ Network->NetworkName.Buffer,
+ Status));
+ try_return(Status);
+ }
+ } else {
+
+ //
+ // If we're configured to be a backup browser, then we want to
+ // become a backup once again.
+ //
+
+ BecomeBackup(Network, NULL);
+ }
+
+
+try_exit:NOTHING;
+ } finally {
+ UNLOCK_NETWORK(Network);
+ BrDereferenceNetwork( Network );
+ }
+
+ return Status;
+}
+
+ NET_API_STATUS
+BrStopBackup (
+ IN PNETWORK Network
+ )
+/*++
+
+Routine Description:
+
+ This routine is called to stop a machine from being a backup browser.
+
+ It is typically called after some form of error has occurred while
+ running as a browser to make sure that we aren't telling anyone that
+ we're a backup browser.
+
+ We are also called when we receive a "reset state" tickle packet.
+
+Arguments:
+
+ Network - The network being shut down.
+
+Return Value:
+
+ Status - The status of the operation.
+
+Note:
+ This routine must be careful about the ROLE_POTENTIAL bit. If the
+ net does not have the potential bit set, it is possible that we might
+ delete the election name in BrUpdateNetworkAnnouncementBits, so we
+ do the work "by hand".
+
+
+--*/
+{
+ NET_API_STATUS Status;
+
+ //
+ // This guy is shutting down - set his role to 0 and announce.
+ //
+
+ if (!LOCK_NETWORK(Network)) {
+ return NERR_InternalError;
+ }
+
+ try {
+
+ BrPrint(( BR_BACKUP,
+ "%ws: %ws: BrStopBackup called\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ Network->NetworkName.Buffer));
+
+ Network->Role &= ~ROLE_BACKUP;
+
+ Status = BrUpdateBrowserStatus(Network, SV_TYPE_POTENTIAL_BROWSER);
+
+ if (Status != NERR_Success) {
+ BrPrint(( BR_CRITICAL,
+ "%ws: %ws: Unable to clear backup announcement bits: %ld\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ Network->NetworkName.Buffer,
+ Status));
+ try_return(Status);
+ }
+
+ //
+ // Clear ALL the browser bits in the server - we aren't even a
+ // potential browser to the server anymore.
+ //
+
+ Status = I_NetServerSetServiceBitsEx(
+ NULL,
+ Network->DomainInfo->DomUnicodeComputerName,
+ Network->NetworkName.Buffer,
+ BROWSER_SERVICE_BITS_OF_INTEREST,
+ 0,
+ TRUE );
+
+ if (Status != NERR_Success) {
+ BrLogEvent(EVENT_BROWSER_STATUS_BITS_UPDATE_FAILED, Status, 0, NULL);
+ BrPrint(( BR_CRITICAL,
+ "%ws: %ws: Unable to update server status: %ld\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ Network->NetworkName.Buffer,
+ Status));
+ try_return(Status);
+ }
+
+ Status = BrCancelTimer(&Network->BackupBrowserTimer);
+
+ if (Status != NERR_Success) {
+ BrPrint(( BR_CRITICAL,
+ "%ws: %ws: Unable to clear backup browser timer: %ld\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ Network->NetworkName.Buffer,
+ Status));
+ try_return(Status);
+ }
+
+ if (Network->BackupDomainList != NULL) {
+
+ NetApiBufferFree(Network->BackupDomainList);
+
+ Network->BackupDomainList = NULL;
+
+ Network->TotalBackupDomainListEntries = 0;
+ }
+
+ if (Network->BackupServerList != NULL) {
+ NetApiBufferFree(Network->BackupServerList);
+
+ Network->BackupServerList = NULL;
+
+ Network->TotalBackupServerListEntries = 0;
+ }
+
+ BrDestroyResponseCache(Network);
+
+ //
+ // After our recovery time, we can become a potential browser again.
+ //
+
+ Status = BrSetTimer(&Network->BackupBrowserTimer, BrInfo.BackupBrowserRecoveryTime, BrBecomePotentialBrowser, Network);
+
+ if (Status != NERR_Success) {
+ BrPrint(( BR_CRITICAL,
+ "%ws: %ws: Unable to clear backup browser timer: %ld\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ Network->NetworkName.Buffer,
+ Status));
+ try_return(Status);
+ }
+
+
+try_exit:NOTHING;
+ } finally {
+ //
+ // Remember when we were asked to stop being a backup browser.
+ //
+
+ Network->TimeStoppedBackup = BrCurrentSystemTime();
+
+ UNLOCK_NETWORK(Network);
+ }
+
+ return Status;
+
+}
+
+
+ NET_API_STATUS
+BackupBrowserTimerRoutine (
+ IN PVOID TimerContext
+ )
+{
+ IN PNETWORK Network = TimerContext;
+ NET_API_STATUS Status;
+ PVOID ServerList = NULL;
+ BOOLEAN NetworkLocked = FALSE;
+
+ //
+ // Prevent the network from being deleted while we're in this timer routine.
+ //
+ if ( BrReferenceNetwork( Network ) == NULL ) {
+ return NERR_InternalError;
+ }
+
+ try {
+
+ if (!LOCK_NETWORK(Network)) {
+ try_return(Status = NERR_InternalError );
+ }
+
+ NetworkLocked = TRUE;
+
+ ASSERT (Network->LockCount == 1);
+
+ ASSERT ( NetpIsUncComputerNameValid( Network->UncMasterBrowserName ) );
+
+ BrPrint(( BR_BACKUP,
+ "%ws: %ws: BackupBrowserTimerRoutine called\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ Network->NetworkName.Buffer));
+
+ //
+ // Make sure there's a become master oustanding.
+ //
+
+ PostBecomeMaster(Network);
+
+ //
+ // We managed to become a master by the time we locked the structure.
+ // We want to return right away.
+ //
+
+ if (Network->Role & ROLE_MASTER) {
+ try_return(Status = NERR_Success);
+ }
+
+ Status = BrRetrieveInterimServerList(Network, SV_TYPE_ALL);
+
+ //
+ // Bail out if we didn't get any new servers.
+ //
+
+ if (Status != NERR_Success && Status != ERROR_MORE_DATA) {
+
+ //
+ // Try again after an appropriate error delay.
+ //
+
+ try_return(Status);
+ }
+
+ //
+ // Now do everything that we did above for the server list for the
+ // list of domains.
+ //
+
+ Status = BrRetrieveInterimServerList(Network, SV_TYPE_DOMAIN_ENUM);
+
+ //
+ // We successfully updated the server and domain lists for this
+ // server. Now age all the cached domain entries out of the cache.
+ //
+
+ if (Status == NERR_Success || Status == ERROR_MORE_DATA) {
+ BrAgeResponseCache(Network);
+ }
+
+ try_return(Status);
+
+try_exit:NOTHING;
+ } finally {
+ NET_API_STATUS NetStatus;
+
+ if (!NetworkLocked) {
+ if (!LOCK_NETWORK(Network)) {
+ return NERR_InternalError;
+ }
+
+ NetworkLocked = TRUE;
+ }
+
+ //
+ // If the API succeeded, Mark that we're a backup and
+ // reset the timer.
+ //
+
+ if (Status == NERR_Success || Status == ERROR_MORE_DATA ) {
+
+ if ((Network->Role & ROLE_BACKUP) == 0) {
+
+ //
+ // If we weren't a backup, we are one now.
+ //
+
+ Network->Role |= ROLE_BACKUP;
+
+ Status = BrUpdateNetworkAnnouncementBits(Network, NULL);
+
+ }
+
+ Network->NumberOfFailedBackupTimers = 0;
+
+ Network->TimeStoppedBackup = 0;
+
+ //
+ // Restart the timer for this domain.
+ //
+
+ NetStatus = BrSetTimer(&Network->BackupBrowserTimer, BrInfo.BackupPeriodicity*1000, BackupBrowserTimerRoutine, Network);
+
+ if (NetStatus != NERR_Success) {
+ BrPrint(( BR_CRITICAL,
+ "%ws: %ws: Unable to restart browser backup timer: %lx\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ Network->NetworkName.Buffer,
+ Status));
+ }
+
+ } else {
+
+ //
+ // We failed to retrieve a backup list, remember the failure and
+ // decide if it's been too many failures. If not, just log
+ // the error, if it has, stop being a backup browser.
+ //
+
+ Network->NumberOfFailedBackupTimers += 1;
+
+ if (Network->NumberOfFailedBackupTimers >= BACKUP_ERROR_FAILURE) {
+ LPWSTR SubStrings[1];
+
+ SubStrings[0] = Network->NetworkName.Buffer;
+
+ //
+ // This guy can't be a backup any more, bail out now.
+ //
+
+ BrLogEvent(EVENT_BROWSER_BACKUP_STOPPED, Status, 1, SubStrings);
+
+ BrPrint(( BR_BACKUP,
+ "%ws: %ws: BackupBrowserTimerRoutine retrieve backup list so stop being backup.\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ Network->NetworkName.Buffer));
+ BrStopBackup(Network);
+ } else {
+ //
+ // Restart the timer for this domain.
+ //
+
+ NetStatus = BrSetTimer(&Network->BackupBrowserTimer, BACKUP_ERROR_PERIODICITY*1000, BackupBrowserTimerRoutine, Network);
+
+ if (NetStatus != NERR_Success) {
+ BrPrint(( BR_CRITICAL,
+ "%ws: %ws: Unable to restart browser backup timer: %lx\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ Network->NetworkName.Buffer,
+ Status));
+ }
+
+ }
+
+ }
+
+ if (NetworkLocked) {
+ UNLOCK_NETWORK(Network);
+ }
+
+ BrDereferenceNetwork( Network );
+ }
+
+ return Status;
+
+}
+
+ NET_API_STATUS
+BrRetrieveInterimServerList(
+ IN PNETWORK Network,
+ IN ULONG ServerType
+ )
+{
+ ULONG EntriesInList;
+ ULONG TotalEntriesInList;
+ ULONG RetryCount = 2;
+ TCHAR ServerName[UNCLEN+1];
+ LPTSTR TransportName;
+ BOOLEAN NetworkLocked = TRUE;
+ NET_API_STATUS Status;
+ PVOID Buffer = NULL;
+ ULONG ModifiedServerType = ServerType;
+ LPTSTR ModifiedTransportName;
+
+ ASSERT (Network->LockCount == 1);
+
+ wcscpy(ServerName, Network->UncMasterBrowserName );
+
+ BrPrint(( BR_BACKUP,
+ "%ws: %ws: BrRetrieveInterimServerList: UNC servername is %ws\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ Network->NetworkName.Buffer,
+ ServerName));
+
+ try {
+
+ TransportName = Network->NetworkName.Buffer;
+ ModifiedTransportName = TransportName;
+ //
+ // If this is direct host IPX,
+ // we remote the API over the Netbios IPX transport since
+ // the NT redir doesn't support direct host IPX.
+ //
+
+ if ( (Network->Flags & NETWORK_IPX) &&
+ Network->AlternateNetwork != NULL) {
+
+ //
+ // Use the alternate transport
+ //
+
+ ModifiedTransportName = Network->AlternateNetwork->NetworkName.Buffer;
+
+ //
+ // Tell the server to use it's alternate transport.
+ //
+
+ if ( ServerType == SV_TYPE_ALL ) {
+ ModifiedServerType = SV_TYPE_ALTERNATE_XPORT;
+ } else {
+ ModifiedServerType |= SV_TYPE_ALTERNATE_XPORT;
+ }
+
+ }
+
+ while (RetryCount--) {
+
+ //
+ // If we are promoted to master and fail to become the master,
+ // we will still be marked as being the master in our network
+ // structure, thus we should bail out of the loop in order
+ // to prevent us from looping back on ourselves.
+ //
+
+ if (STRICMP(&ServerName[2], Network->DomainInfo->DomUnicodeComputerName) == 0) {
+
+ if (NetworkLocked) {
+ UNLOCK_NETWORK(Network);
+
+ NetworkLocked = FALSE;
+
+ }
+
+ //
+ // We were unable to find the master. Attempt to find out who
+ // the master is. If there is none, this will force an
+ // election.
+ //
+
+ BrPrint(( BR_BACKUP,
+ "%ws: %ws: FindMaster called from BrRetrieveInterimServerList\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ Network->NetworkName.Buffer));
+
+ Status = GetMasterServerNames(Network);
+
+ if (Status != NERR_Success) {
+ try_return(Status);
+ }
+
+ ASSERT (!NetworkLocked);
+
+ if (!LOCK_NETWORK(Network)) {
+ try_return(Status = NERR_InternalError);
+ }
+
+ NetworkLocked = TRUE;
+
+ break;
+ }
+
+ //
+ // If we somehow became the master, we don't want to try to
+ // retrieve the list from ourselves either.
+ //
+
+ if (Network->Role & ROLE_MASTER) {
+ try_return(Status = NERR_Success);
+ }
+
+ ASSERT (Network->LockCount == 1);
+
+ if (NetworkLocked) {
+ UNLOCK_NETWORK(Network);
+
+ NetworkLocked = FALSE;
+
+ }
+
+ EntriesInList = 0;
+
+ Status = RxNetServerEnum(ServerName, // Server name
+ ModifiedTransportName, // Transport name
+ 101, // Level
+ (LPBYTE *)&Buffer, // Buffer
+ 0xffffffff, // Prefered Max Length
+ &EntriesInList, // EntriesRead
+ &TotalEntriesInList, // TotalEntries
+ ModifiedServerType, // Server type
+ NULL, // Domain (use default)
+ NULL // Resume key
+ );
+
+ if (Status != NERR_Success && Status != ERROR_MORE_DATA) {
+ LPWSTR SubStrings[2];
+
+ SubStrings[0] = ServerName;
+ SubStrings[1] = TransportName;
+
+ BrLogEvent((ServerType == SV_TYPE_DOMAIN_ENUM ?
+ EVENT_BROWSER_DOMAIN_LIST_FAILED :
+ EVENT_BROWSER_SERVER_LIST_FAILED),
+ Status,
+ 2,
+ SubStrings);
+
+ BrPrint(( BR_BACKUP,
+ "%ws: %ws: Failed to retrieve %s list from server %ws: %ld\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ TransportName,
+ (ServerType == SV_TYPE_ALL ? "server" : "domain"),
+ ServerName,
+ Status));
+ } else {
+ BrPrint(( BR_BACKUP,
+ "%ws: %ws: Retrieved %s list from server %ws: E:%ld, T:%ld\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ TransportName,
+ (ServerType == SV_TYPE_ALL ? "server" : "domain"),
+ ServerName,
+ EntriesInList,
+ TotalEntriesInList));
+ }
+
+ //
+ // If we succeeded in retrieving the list, but we only got
+ // a really small number of either servers or domains,
+ // we want to turn this into a failure.
+ //
+
+ if (Status == NERR_Success) {
+ if (((ServerType == SV_TYPE_DOMAIN_ENUM) &&
+ (EntriesInList < BROWSER_MINIMUM_DOMAIN_NUMBER)) ||
+ ((ServerType == SV_TYPE_ALL) &&
+ (EntriesInList < BROWSER_MINIMUM_SERVER_NUMBER))) {
+
+ Status = ERROR_INSUFFICIENT_BUFFER;
+ }
+ }
+
+ if ((Status == NERR_Success) || (Status == ERROR_MORE_DATA)) {
+
+ ASSERT (!NetworkLocked);
+
+ if (!LOCK_NETWORK(Network)) {
+ Status = NERR_InternalError;
+ break;
+ }
+
+ NetworkLocked = TRUE;
+
+ ASSERT (Network->LockCount == 1);
+
+#if DBG
+ BrUpdateDebugInformation((ServerType == SV_TYPE_DOMAIN_ENUM ?
+ L"LastDomainListRead" :
+ L"LastServerListRead"),
+ L"BrowserServerName",
+ TransportName,
+ ServerName,
+ 0);
+#endif
+
+ //
+ // We've retrieved a new list from the browse master, save
+ // the new list away in the "appropriate" spot.
+ //
+
+ //
+ // Of course, we free up the old buffer before we do this..
+ //
+
+ if (ServerType == SV_TYPE_DOMAIN_ENUM) {
+ if (Network->BackupDomainList != NULL) {
+ NetApiBufferFree(Network->BackupDomainList);
+ }
+
+ Network->BackupDomainList = Buffer;
+
+ Network->TotalBackupDomainListEntries = EntriesInList;
+ } else {
+ if (Network->BackupServerList != NULL) {
+ NetApiBufferFree(Network->BackupServerList);
+ }
+
+ Network->BackupServerList = Buffer;
+
+ Network->TotalBackupServerListEntries = EntriesInList;
+ }
+
+ break;
+ } else {
+ NET_API_STATUS GetMasterNameStatus;
+
+ if ((EntriesInList != 0) && (Buffer != NULL)) {
+ NetApiBufferFree(Buffer);
+ Buffer = NULL;
+ }
+
+ BrPrint(( BR_BACKUP,
+ "%ws: %ws: Unable to contact browser server %ws: %lx\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ TransportName,
+ ServerName,
+ Status));
+
+ if (NetworkLocked) {
+
+ //
+ // We were unable to find the master. Attempt to find out who
+ // the master is. If there is none, this will force an
+ // election.
+ //
+
+ ASSERT (Network->LockCount == 1);
+
+ UNLOCK_NETWORK(Network);
+
+ NetworkLocked = FALSE;
+ }
+
+ BrPrint(( BR_BACKUP,
+ "%ws: %ws: FindMaster called from BrRetrieveInterimServerList for failure\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ Network->NetworkName.Buffer));
+
+ GetMasterNameStatus = GetMasterServerNames(Network);
+
+ //
+ // We were able to find out who the master is.
+ //
+ // Retry and retrieve the server/domain list.
+ //
+
+ if (GetMasterNameStatus == NERR_Success) {
+
+ ASSERT (!NetworkLocked);
+
+ if (!LOCK_NETWORK(Network)) {
+ try_return(Status = NERR_InternalError);
+ }
+
+ NetworkLocked = TRUE;
+
+ ASSERT (Network->LockCount == 1);
+
+ //
+ // We managed to become a master. We want to return right away.
+ //
+
+ if (Network->Role & ROLE_MASTER) {
+
+ try_return(Status = NERR_InternalError);
+ }
+
+ wcscpy(ServerName, Network->UncMasterBrowserName );
+
+ ASSERT ( NetpIsUncComputerNameValid( ServerName ) );
+
+ ASSERT (STRICMP(&ServerName[2], Network->DomainInfo->DomUnicodeComputerName) != 0);
+
+ BrPrint(( BR_BACKUP,
+ "%ws: %ws: New master name is %ws\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ Network->NetworkName.Buffer,
+ ServerName));
+
+ } else {
+ try_return(Status);
+ }
+ }
+ }
+try_exit:NOTHING;
+ } finally {
+ if (!NetworkLocked) {
+ if (!LOCK_NETWORK(Network)) {
+ Status = NERR_InternalError;
+ }
+
+ ASSERT (Network->LockCount == 1);
+
+ }
+ }
+
+ return Status;
+}
+
+
+ NET_API_STATUS
+PostBecomeBackup(
+ PNETWORK Network
+ )
+/*++
+
+Routine Description:
+
+ This function is the worker routine called to actually issue a BecomeBackup
+ FsControl to the bowser driver on all the bound transports. It will
+ complete when the machine becomes a backup browser server.
+
+ Please note that this might never complete.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ Status - The status of the operation.
+
+--*/
+{
+ NET_API_STATUS Status;
+
+ if (!LOCK_NETWORK(Network)) {
+ return NERR_InternalError;
+ }
+
+ Network->Role |= ROLE_POTENTIAL_BACKUP;
+
+ Status = BrIssueAsyncBrowserIoControl(Network,
+ IOCTL_LMDR_BECOME_BACKUP,
+ BecomeBackupCompletion,
+ NULL );
+ UNLOCK_NETWORK(Network);
+
+ return Status;
+}
+
+VOID
+BecomeBackupCompletion (
+ IN PVOID Ctx
+ )
+{
+ NET_API_STATUS Status;
+ PBROWSERASYNCCONTEXT Context = Ctx;
+ PNETWORK Network = Context->Network;
+
+ if (NT_SUCCESS(Context->IoStatusBlock.Status)) {
+
+ //
+ // Ensure the network wasn't deleted from under us.
+ //
+ if ( BrReferenceNetwork( Network ) != NULL ) {
+
+ if (LOCK_NETWORK(Network)) {
+
+ BrPrint(( BR_BACKUP,
+ "%ws: %ws: BecomeBackupCompletion. We are now a backup server\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ Network->NetworkName.Buffer ));
+
+ Status = BecomeBackup(Context->Network, NULL);
+
+ UNLOCK_NETWORK(Network);
+ }
+
+ BrDereferenceNetwork( Network );
+ }
+
+ }
+
+ MIDL_user_free(Context->RequestPacket);
+
+ MIDL_user_free(Context);
+
+}
+
+ VOID
+BrBrowseTableInsertRoutine(
+ IN PINTERIM_SERVER_LIST InterimTable,
+ IN PINTERIM_ELEMENT InterimElement
+ )
+{
+ //
+ // We need to miss 3 retrievals of the browse list for us to toss the
+ // server.
+ //
+
+ InterimElement->Periodicity = BrInfo.BackupPeriodicity * 3;
+
+ if (InterimElement->TimeLastSeen != 0xffffffff) {
+ InterimElement->TimeLastSeen = BrCurrentSystemTime();
+ }
+}
+
+ VOID
+BrBrowseTableDeleteRoutine(
+ IN PINTERIM_SERVER_LIST InterimTable,
+ IN PINTERIM_ELEMENT InterimElement
+ )
+{
+// BrPrint(( BR_CRITICAL, "Deleting element for server %ws\n", InterimElement->Name));
+}
+
+ VOID
+BrBrowseTableUpdateRoutine(
+ IN PINTERIM_SERVER_LIST InterimTable,
+ IN PINTERIM_ELEMENT InterimElement
+ )
+{
+ if (InterimElement->TimeLastSeen != 0xffffffff) {
+ InterimElement->TimeLastSeen = BrCurrentSystemTime();
+ }
+}
+
+ BOOLEAN
+BrBrowseTableAgeRoutine(
+ IN PINTERIM_SERVER_LIST InterimTable,
+ IN PINTERIM_ELEMENT InterimElement
+ )
+/*++
+
+Routine Description:
+
+ This routine is called when we are scanning an interim server list trying
+ to age the elements in the list. It returns TRUE if the entry is too
+ old.
+
+Arguments:
+
+ PINTERIM_SERVER_LIST InterimTable - A pointer to the interim server list.
+ PINTERIM_ELEMENT InterimElement - A pointer to the element to check.
+
+Return Value:
+
+ TRUE if the element should be deleted.
+
+--*/
+
+{
+ if (InterimElement->TimeLastSeen == 0xffffffff) {
+ return FALSE;
+ }
+
+ if ((InterimElement->TimeLastSeen + InterimElement->Periodicity) < BrCurrentSystemTime()) {
+// BrPrint(( BR_CRITICAL, "Aging out element for server %ws\n", InterimElement->Name));
+
+ return TRUE;
+ } else {
+ return FALSE;
+ }
+}
+
+ VOID
+BrDomainTableInsertRoutine(
+ IN PINTERIM_SERVER_LIST InterimTable,
+ IN PINTERIM_ELEMENT InterimElement
+ )
+{
+ InterimElement->Periodicity = BrInfo.BackupPeriodicity * 3;
+ InterimElement->TimeLastSeen = BrCurrentSystemTime();
+
+}
+
+ VOID
+BrDomainTableDeleteRoutine(
+ IN PINTERIM_SERVER_LIST InterimTable,
+ IN PINTERIM_ELEMENT InterimElement
+ )
+{
+// BrPrint(( BR_CRITICAL, "Deleting element for domain %ws\n", InterimElement->Name));
+}
+
+ VOID
+BrDomainTableUpdateRoutine(
+ IN PINTERIM_SERVER_LIST InterimTable,
+ IN PINTERIM_ELEMENT InterimElement
+ )
+{
+ InterimElement->TimeLastSeen = BrCurrentSystemTime();
+}
+
+ BOOLEAN
+BrDomainTableAgeRoutine(
+ IN PINTERIM_SERVER_LIST InterimTable,
+ IN PINTERIM_ELEMENT InterimElement
+ )
+/*++
+
+Routine Description:
+
+ This routine is called when we are scanning an interim server list trying
+ to age the elements in the list. It returns TRUE if the entry is too
+ old.
+
+Arguments:
+
+ PINTERIM_SERVER_LIST InterimTable - A pointer to the interim server list.
+ PINTERIM_ELEMENT InterimElement - A pointer to the element to check.
+
+Return Value:
+
+ TRUE if the element should be deleted.
+
+--*/
+
+{
+ if ((InterimElement->TimeLastSeen + InterimElement->Periodicity) < BrCurrentSystemTime()) {
+// BrPrint(( BR_CRITICAL, "Aging out element for domain %ws\n", InterimElement->Name));
+ return TRUE;
+ } else {
+ return FALSE;
+ }
+}
+
+
+ NET_API_STATUS
+PostWaitForRoleChange (
+ PNETWORK Network
+ )
+/*++
+
+Routine Description:
+
+ This function is the worker routine called to actually issue a WaitForRoleChange
+ FsControl to the bowser driver on all the bound transports. It will
+ complete when the machine becomes a backup browser server.
+
+ Please note that this might never complete.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ Status - The status of the operation.
+
+--*/
+{
+ NET_API_STATUS Status;
+
+ if (!LOCK_NETWORK(Network)) {
+ return NERR_InternalError;
+ }
+
+ Status = BrIssueAsyncBrowserIoControl(Network,
+ IOCTL_LMDR_CHANGE_ROLE,
+ ChangeBrowserRole,
+ NULL );
+ UNLOCK_NETWORK(Network);
+
+ return Status;
+}
+
+VOID
+ChangeBrowserRole (
+ IN PVOID Ctx
+ )
+{
+ PBROWSERASYNCCONTEXT Context = Ctx;
+ PNETWORK Network = Context->Network;
+
+ if (NT_SUCCESS(Context->IoStatusBlock.Status)) {
+ PWSTR MasterName = NULL;
+ PLMDR_REQUEST_PACKET Packet = Context->RequestPacket;
+
+ //
+ // Ensure the network wasn't deleted from under us.
+ //
+ if ( BrReferenceNetwork( Network ) != NULL ) {
+
+ if (LOCK_NETWORK(Network)) {
+
+ PostWaitForRoleChange(Network);
+
+ if (Packet->Parameters.ChangeRole.RoleModification & RESET_STATE_CLEAR_ALL) {
+ BrPrint(( BR_MASTER,
+ "%ws: %ws: Reset state request to clear all\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ Network->NetworkName.Buffer ));
+
+ if (Network->Role & ROLE_MASTER) {
+ BrStopMaster(Network);
+ }
+
+ //
+ // Stop being a backup as well.
+ //
+
+ BrStopBackup(Network);
+
+ }
+
+ if ((Network->Role & ROLE_MASTER) &&
+ (Packet->Parameters.ChangeRole.RoleModification & RESET_STATE_STOP_MASTER)) {
+
+ BrPrint(( BR_MASTER,
+ "%ws: %ws: Reset state request to stop master\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ Network->NetworkName.Buffer ));
+
+ BrStopMaster(Network);
+
+ //
+ // If we are configured to be a backup, then become a backup
+ // again.
+ //
+
+ if (BrInfo.MaintainServerList == 1) {
+ BecomeBackup(Network, NULL);
+ }
+ }
+
+ //
+ // Make sure there's a become master oustanding.
+ //
+
+ PostBecomeMaster(Network);
+
+ UNLOCK_NETWORK(Network);
+
+ }
+
+ BrDereferenceNetwork( Network );
+ }
+ }
+
+ MIDL_user_free(Context->RequestPacket);
+
+ MIDL_user_free(Context);
+
+}
+
+
+NET_API_STATUS
+PostWaitForNewMasterName(
+ PNETWORK Network,
+ LPWSTR MasterName OPTIONAL
+ )
+{
+
+ return BrIssueAsyncBrowserIoControl(
+ Network,
+ IOCTL_LMDR_NEW_MASTER_NAME,
+ NewMasterCompletionRoutine,
+ MasterName );
+
+
+}
+
+VOID
+NewMasterCompletionRoutine(
+ IN PVOID Ctx
+ )
+{
+ PBROWSERASYNCCONTEXT Context = Ctx;
+ PNETWORK Network = Context->Network;
+ BOOLEAN NetLocked = FALSE;
+ BOOLEAN NetReferenced = FALSE;
+
+
+ try {
+ UNICODE_STRING NewMasterName;
+
+ //
+ // Ensure the network wasn't deleted from under us.
+ //
+ if ( BrReferenceNetwork( Network ) == NULL ) {
+ try_return(NOTHING);
+ }
+ NetReferenced = TRUE;
+
+ BrPrint(( BR_MASTER,
+ "%ws: %ws: NewMasterCompletionRoutine: Got master changed\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ Network->NetworkName.Buffer ));
+
+ if (!LOCK_NETWORK(Network)){
+ try_return(NOTHING);
+ }
+ NetLocked = TRUE;
+
+ //
+ // The request failed for some other reason - just return immediately.
+ //
+
+ if (!NT_SUCCESS(Context->IoStatusBlock.Status)) {
+
+ try_return(NOTHING);
+
+ }
+
+ // Remove new master name & put in transport
+
+ if ( Network->Role & ROLE_MASTER ) {
+
+ try_return(NOTHING);
+
+ }
+
+ BrPrint(( BR_BACKUP,
+ "%ws: %ws: NewMasterCompletionRoutin: New:%ws Old %ws\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ Network->NetworkName.Buffer,
+ Context->RequestPacket->Parameters.GetMasterName.Name,
+ Network->UncMasterBrowserName ));
+
+ //
+ // Copy the master browser name into the network structure
+ //
+
+ wcsncpy( Network->UncMasterBrowserName,
+ Context->RequestPacket->Parameters.GetMasterName.Name,
+ UNCLEN+1 );
+
+ Network->UncMasterBrowserName[UNCLEN] = L'\0';
+
+ ASSERT ( NetpIsUncComputerNameValid ( Network->UncMasterBrowserName ) );
+
+ PostWaitForNewMasterName( Network, Network->UncMasterBrowserName );
+
+try_exit:NOTHING;
+ } finally {
+
+ if (NetLocked) {
+ UNLOCK_NETWORK(Network);
+ }
+
+ if ( NetReferenced ) {
+ BrDereferenceNetwork( Network );
+ }
+
+ MIDL_user_free(Context->RequestPacket);
+
+ MIDL_user_free(Context);
+
+ }
+
+ return;
+}
diff --git a/private/net/svcdlls/browser2/server/browser.def b/private/net/svcdlls/browser2/server/browser.def
new file mode 100644
index 000000000..a1243ec13
--- /dev/null
+++ b/private/net/svcdlls/browser2/server/browser.def
@@ -0,0 +1,7 @@
+NAME browser.dll
+
+DESCRIPTION 'NT LAN Manager Workstation Service'
+
+EXPORTS
+ ServiceEntry
+ I_BrowserServerEnumForXactsrv
diff --git a/private/net/svcdlls/browser2/server/browser.prf b/private/net/svcdlls/browser2/server/browser.prf
new file mode 100644
index 000000000..6e93b73dc
--- /dev/null
+++ b/private/net/svcdlls/browser2/server/browser.prf
@@ -0,0 +1,33 @@
+BrQueueWorkItem@4
+BrWorkerThread@4
+BrIssueAsyncBrowserIoControl@16
+CompleteAsyncBrowserIoControl@12
+;GetBrowserServerListRequestCompletion@4
+;PostGetBrowserServerList@8
+;AddBackupToBackupList@12
+
+I_BrowserrServerEnum@36
+I_BrowserServerEnumForXactsrv@48
+BrNetServerEnum@36
+BrRetrieveServerListForMaster@40
+BrRetrieveServerListForBackup@28
+BrLookupAndAllocateCachedEntry@16
+BrAgeResponseCache@4
+I_BrowserrResetStatistics@4
+I_BrowserrQueryStatistics@8
+BrAllocateResponseCacheEntry@16
+BrDestroyCacheEntry@4
+BrDestroyResponseCache@4
+BrFindNetwork@8
+
+MergeServerList@20
+PackServerList@28
+UpdateInterimServerListElement@16
+AllocateInterimServerListEntry@8
+BrGetLocalBrowseList@28
+DeviceControlGetInfo@32
+BrDgReceiverIoControl@28
+BrBrowseTableInsertRoutine@8
+BrBrowseTableUpdateRoutine@8
+BrDomainTableInsertRoutine@8
+BrDomainTableUpdateRoutine@8
diff --git a/private/net/svcdlls/browser2/server/browslst.c b/private/net/svcdlls/browser2/server/browslst.c
new file mode 100644
index 000000000..4adae3814
--- /dev/null
+++ b/private/net/svcdlls/browser2/server/browslst.c
@@ -0,0 +1,70 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ browslst.c
+
+Abstract:
+
+ This module contains the worker routines for managing browse lists
+ for the browser service
+
+Author:
+
+ Larry Osterman (larryo) 25-Mar-1992
+
+Revision History:
+
+--*/
+
+#include "precomp.h"
+#pragma hdrstop
+
+//-------------------------------------------------------------------//
+// //
+// Local function prototypes //
+// //
+//-------------------------------------------------------------------//
+
+
+ RTL_GENERIC_COMPARE_RESULTS
+BrCompareBrowseEntry(
+ PRTL_GENERIC_TABLE Table,
+ PVOID FirstStruct,
+ PVOID SecondStruct
+ )
+{
+ PDOMAIN_ENTRY Entry1 = FirstStruct;
+ PDOMAIN_ENTRY Entry2 = SecondStruct;
+
+ LONG CompareResult;
+
+ if ((CompareResult = RtlCompareUnicodeString(Entry1->HostName, Entry2->HostName, TRUE) == 0) {
+ return GenericEqual;
+ } else if (CompareResult < 0) {
+ return GenericLessThan;
+ } else {
+ return GenericGreaterThan;
+ }
+
+}
+
+ PVOID
+BrAllocateBrowseEntry(
+ PRTL_GENERIC_TABLE Table,
+ CLONG ByteSize
+ )
+{
+ return((PVOID) MIDL_user_allocate(LMEM_ZEROINIT, (UINT) ByteSize+sizeof(BROWSE_ENTRY)));
+}
+
+ PVOID
+BrFreeBrowseEntry(
+ PRTL_GENERIC_TABLE Table,
+ CLONG ByteSize
+ )
+{
+ return(MIDL_user_free(ByteSize+sizeof(BROWSE_ENTRY)));
+}
diff --git a/private/net/svcdlls/browser2/server/browslst.h b/private/net/svcdlls/browser2/server/browslst.h
new file mode 100644
index 000000000..492275d8b
--- /dev/null
+++ b/private/net/svcdlls/browser2/server/browslst.h
@@ -0,0 +1,95 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ browselst.h
+
+Abstract:
+
+ Private header file to be included by Browser service modules that
+ need to deal with the browser list.
+
+Author:
+
+ Larry Osterman (larryo) 3-Mar-1992
+
+Revision History:
+
+--*/
+
+
+#ifndef _BROWSELST_INCLUDED_
+#define _BROWSELST_INCLUDED_
+
+
+//
+// The possible roles of this browser server.
+//
+
+
+#define ROLE_POTENTIAL_BACKUP 0x00000001
+#define ROLE_BACKUP 0x00000002
+#define ROLE_MASTER 0x00000004
+#define ROLE_DOMAINMASTER 0x00000008
+
+
+//
+// The HOST_ENTRY structure holds the announcement inside a per-network
+// table.
+//
+
+
+typedef struct _HOST_ENTRY {
+
+ //
+ // The HostName is the name of the server.
+ //
+
+ UNICODE_STRING HostName;
+
+ //
+ // The HostComment is the comment associated with the server
+ //
+
+ UNICODE_STRING HostComment;
+
+ //
+ // Services is a bitmask that indicates the services running on the
+ // server (See LMSERVER.H for details).
+ //
+
+ ULONG Services;
+
+ //
+ // The Periodicity is the frequency that the server announces itself.
+ //
+
+ ULONG Periodicity;
+
+ //
+ // The MajorVersion and MinorVersion number of the software running on
+ // the server.
+ //
+
+ UCHAR MajorVersion;
+ UCHAR MinorVersion;
+
+ //
+ // If this server is a backup server, then this links the backup server
+ // into the network block.
+ //
+
+ LIST_ENTRY BackupChain;
+
+ //
+ // If this server is a domain master, than this links the server into
+ // the network block.
+ //
+
+ LIST_ENTRY DomainMasterChain;
+
+} HOST_ENTRY, *PHOST_ENTRY;
+
+#endif // _BROWSELST_INCLUDED_
diff --git a/private/net/svcdlls/browser2/server/browsnet.c b/private/net/svcdlls/browser2/server/browsnet.c
new file mode 100644
index 000000000..57e8768d8
--- /dev/null
+++ b/private/net/svcdlls/browser2/server/browsnet.c
@@ -0,0 +1,1349 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ browsenet.c
+
+Abstract:
+
+ Code to manage network requests.
+
+Author:
+
+ Larry Osterman (LarryO) 24-Mar-1992
+
+Revision History:
+
+--*/
+
+#include "precomp.h"
+#pragma hdrstop
+
+
+CRITICAL_SECTION NetworkCritSect = {0};
+
+LIST_ENTRY
+ServicedNetworks = {0};
+
+ULONG
+NumberOfServicedNetworks = 0;
+
+
+NET_API_STATUS
+BrDumpNetworksWorker(
+ IN PNETWORK Network,
+ IN PVOID Context
+ );
+
+
+
+VOID
+BrInitializeNetworks(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ Initialization for this source file.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ Status of operation.
+
+--*/
+{
+ InitializeListHead(&ServicedNetworks);
+ InitializeCriticalSection(&NetworkCritSect);
+
+ return;
+}
+
+
+NET_API_STATUS
+BrCreateNetworks(
+ PDOMAIN_INFO DomainInfo
+ )
+
+/*++
+
+Routine Description:
+
+ Create all of the networks for a particular domain.
+
+Arguments:
+
+ DomainInfo - Specifies the domain being browsed.
+
+Return Value:
+
+ Status of operation.
+
+--*/
+{
+ NET_API_STATUS NetStatus;
+ PLMDR_TRANSPORT_LIST TransportList = NULL ;
+ PLMDR_TRANSPORT_LIST TransportEntry;
+ BOOLEAN ConfigCritSectLocked = FALSE;
+
+ BrPrint(( BR_NETWORK, "%ws: Creating networks for domain\n", DomainInfo->DomUnicodeDomainName ));
+
+ //
+ // Get the list of transports from the datagram receiver.
+ //
+ NetStatus = BrGetTransportList(&TransportList);
+
+ if ( NetStatus != NERR_Success ) {
+ goto Cleanup;
+ }
+
+ //
+ // Create a Network for each of the transports.
+ //
+ TransportEntry = TransportList;
+
+ while (TransportEntry != NULL) {
+
+ //
+ // Don't do the Direct Host IPX transport here (do it below)
+ //
+
+ if ( (TransportEntry->Flags & LMDR_TRANSPORT_IPX) == 0 ) {
+ UNICODE_STRING TransportName;
+
+ TransportName.Buffer = TransportEntry->TransportName;
+ TransportName.Length = (USHORT)TransportEntry->TransportNameLength;
+ //
+ // We know the bowser sticks in a null at the end, so the max length
+ // is the length + 1.
+ //
+ TransportName.MaximumLength = (USHORT)TransportEntry->TransportNameLength+sizeof(WCHAR);
+
+ NetStatus = BrCreateNetwork(
+ &TransportName,
+ (BOOLEAN)((TransportEntry->Flags & LMDR_TRANSPORT_WANNISH) != 0),
+ (BOOLEAN)((TransportEntry->Flags & LMDR_TRANSPORT_RAS) != 0),
+ NULL,
+ DomainInfo );
+
+ if ( NetStatus != NERR_Success ) {
+ goto Cleanup;
+ }
+ }
+
+ if (TransportEntry->NextEntryOffset == 0) {
+ TransportEntry = NULL;
+ } else {
+ TransportEntry = (PLMDR_TRANSPORT_LIST)((PCHAR)TransportEntry+TransportEntry->NextEntryOffset);
+ }
+
+ }
+
+ //
+ // Handle DirectHost IPX transport
+ //
+
+ EnterCriticalSection(&BrInfo.ConfigCritSect);
+ ConfigCritSectLocked = TRUE;
+
+ if (BrInfo.DirectHostBinding != NULL ) {
+ LPTSTR_ARRAY TStrArray = BrInfo.DirectHostBinding;
+ UNICODE_STRING IpxTransportName;
+ UNICODE_STRING NetbiosTransportName;
+
+ while (!NetpIsTStrArrayEmpty(TStrArray)) {
+
+ RtlInitUnicodeString(&IpxTransportName, TStrArray);
+
+ TStrArray = NetpNextTStrArrayEntry(TStrArray);
+
+ ASSERT (!NetpIsTStrArrayEmpty(TStrArray));
+
+ if (!NetpIsTStrArrayEmpty(TStrArray)) {
+ PNETWORK Network;
+ RtlInitUnicodeString(&NetbiosTransportName, TStrArray);
+
+// BrPrint(( BR_CRITICAL, "Alternate name %ws\n", TStrArray));
+
+ TStrArray = NetpNextTStrArrayEntry(TStrArray);
+
+ //
+ // If the alternate transport doesn't exist,
+ // just ignore the direct host transport.
+ //
+
+ Network = BrFindNetwork( DomainInfo, &NetbiosTransportName );
+
+ if (Network == NULL) {
+ continue;
+ } else {
+ BrDereferenceNetwork( Network );
+ }
+
+ //
+ // There is a direct host binding on this machine. We want to add
+ // the direct host transport to the browser.
+ //
+
+ NetStatus = BrCreateNetwork(
+ &IpxTransportName,
+ FALSE,
+ FALSE,
+ &NetbiosTransportName,
+ DomainInfo );
+
+ if (NetStatus != NERR_Success) {
+ goto Cleanup;
+ }
+
+ }
+ }
+
+ }
+
+ NetStatus = NERR_Success;
+
+Cleanup:
+
+ if ( ConfigCritSectLocked ) {
+ LeaveCriticalSection(&BrInfo.ConfigCritSect);
+ }
+ if ( TransportList != NULL ) {
+ MIDL_user_free(TransportList);
+ }
+
+ return NetStatus;
+}
+
+NET_API_STATUS
+BrCreateNetwork(
+ IN PUNICODE_STRING TransportName,
+ IN BOOLEAN Wannish,
+ IN BOOLEAN Ras,
+ IN PUNICODE_STRING AlternateTransportName OPTIONAL,
+ IN PDOMAIN_INFO DomainInfo
+ )
+/*++
+
+Routine Description:
+
+ This routine allocates memory to hold a network structure, and initializes
+ all of its associated data structures.
+
+Arguments:
+
+ TransportName - The name of the transport to add.
+
+ Wannish - TRUE if there should be a separate master browser on each
+ subnet.
+
+ Ras - TRUE if this network is a RAS transport.
+
+ AlternateTransportName - If specified, this is the name of an alternate
+ transport similar to the one being created.
+
+ DomainInfo - Specifies the domain being browsed.
+
+
+Return Value:
+
+ Status of operation (mostly status of allocations).
+
+--*/
+{
+ NET_API_STATUS NetStatus;
+ PNETWORK Network;
+ BOOLEAN NetworkLockInitialized = FALSE;
+ BOOLEAN ResponseCacheLockInitialized = FALSE;
+ BOOLEAN CanCallBrDestroyNetwork = FALSE;
+
+ BOOLEAN ConfigCritSectLocked = FALSE;
+
+ BrPrint(( BR_NETWORK,
+ "%ws: %ws: Creating network.\n",
+ DomainInfo->DomUnicodeDomainName,
+ TransportName->Buffer ));
+
+ //
+ // Check to see if the transport already exists.
+ //
+
+ if ((Network = BrFindNetwork( DomainInfo, TransportName)) != NULL) {
+ BrDereferenceNetwork( Network );
+ return NERR_AlreadyExists;
+ }
+
+ //
+ // If this transport is explicitly on our list of transports to unbind,
+ // simply ignore the transport.
+ //
+
+ if (BrInfo.UnboundBindings != NULL) {
+ LPTSTR_ARRAY TStrArray = BrInfo.UnboundBindings;
+
+ while (!NetpIsTStrArrayEmpty(TStrArray)) {
+ LPWSTR NewTransportName;
+
+#define NAME_PREFIX L"\\Device\\"
+#define NAME_PREFIX_LENGTH 8
+
+ //
+ // The transport name in the registry is only optionally prefixed with \device\
+ //
+
+ if ( _wcsnicmp( NAME_PREFIX, TStrArray, NAME_PREFIX_LENGTH) == 0 ) {
+ NewTransportName = TransportName->Buffer;
+ } else {
+ NewTransportName = TransportName->Buffer + NAME_PREFIX_LENGTH;
+ }
+
+ if ( _wcsicmp( TStrArray, NewTransportName ) == 0 ) {
+ BrPrint(( BR_INIT, "Binding is marked as unbound: %s (Silently ignoring)\n", TransportName->Buffer ));
+ return NERR_Success;
+ }
+
+ TStrArray = NetpNextTStrArrayEntry(TStrArray);
+
+ }
+
+ }
+
+
+ //
+ // If this transport isn't bound to the SMB server,
+ // don't create the transport here.
+ // we do announcments through the SMB server.
+ //
+
+ NetStatus = I_NetServerSetServiceBitsEx(
+ NULL,
+ DomainInfo->DomUnicodeComputerName,
+ TransportName->Buffer,
+ BROWSER_SERVICE_BITS_OF_INTEREST,
+ 0,
+ TRUE );
+
+ if (NetStatus == ERROR_PATH_NOT_FOUND ) {
+ BrPrint(( BR_INIT, "SMB Server doesn't have this transport: %s (Silently unbinding)\n", TransportName->Buffer ));
+ return NERR_Success;
+ }
+
+
+ //
+ // Create the transport.
+ //
+
+ try {
+
+ //
+ // Allocate the NETWORK structure.
+ //
+
+ Network = MIDL_user_allocate(sizeof(NETWORK));
+
+ if (Network == NULL) {
+ try_return(NetStatus = ERROR_NOT_ENOUGH_MEMORY);
+ }
+
+ RtlZeroMemory( Network, sizeof(NETWORK) );
+
+
+
+ //
+ // Initialize those fields that must be initialized before we can call
+ // BrDeleteNetwork (on failure).
+ //
+
+ RtlInitializeResource(&Network->Lock);
+
+ NetworkLockInitialized = TRUE;
+ Network->Role = BrDefaultRole;
+
+ // One for being in ServiceNetworks. One for this routine's reference.
+ Network->ReferenceCount = 2;
+
+
+ Network->NetworkName.Buffer = MIDL_user_allocate(TransportName->MaximumLength);
+
+ if (Network->NetworkName.Buffer == NULL) {
+ try_return(NetStatus = ERROR_NOT_ENOUGH_MEMORY);
+ }
+
+ Network->NetworkName.MaximumLength = TransportName->MaximumLength;
+ RtlCopyUnicodeString(&Network->NetworkName, TransportName);
+ Network->NetworkName.Buffer[Network->NetworkName.Length/sizeof(WCHAR)] = UNICODE_NULL;
+
+
+ RtlZeroMemory( Network->UncMasterBrowserName, sizeof( Network->UncMasterBrowserName ));
+
+ if (Wannish) {
+ Network->Flags |= NETWORK_WANNISH;
+ }
+
+ if (Ras) {
+ Network->Flags |= NETWORK_RAS;
+ }
+
+ InitializeInterimServerList(&Network->BrowseTable,
+ BrBrowseTableInsertRoutine,
+ BrBrowseTableUpdateRoutine,
+ BrBrowseTableDeleteRoutine,
+ BrBrowseTableAgeRoutine);
+
+ Network->LastBowserDomainQueried = 0;
+
+ InitializeInterimServerList(&Network->DomainList,
+ BrDomainTableInsertRoutine,
+ BrDomainTableUpdateRoutine,
+ BrDomainTableDeleteRoutine,
+ BrDomainTableAgeRoutine);
+
+ InitializeListHead(&Network->OtherDomainsList);
+
+ InitializeCriticalSection(&Network->ResponseCacheLock);
+ ResponseCacheLockInitialized = TRUE;
+
+ InitializeListHead(&Network->ResponseCache);
+
+ Network->TimeCacheFlushed = 0;
+
+ Network->NumberOfCachedResponses = 0;
+
+ EnterCriticalSection(&NetworkCritSect);
+ Network->DomainInfo = DomainInfo;
+ DomainInfo->ReferenceCount ++;
+ InsertHeadList(&ServicedNetworks, &Network->NextNet);
+ NumberOfServicedNetworks += 1;
+ LeaveCriticalSection(&NetworkCritSect);
+
+
+ //
+ // Mark that we can now call BrDeleteNetwork upon failure.
+ //
+ // Continue initializing the network.
+ //
+
+ CanCallBrDestroyNetwork = TRUE;
+
+ NetStatus = BrCreateTimer(&Network->BackupBrowserTimer);
+
+ if (NetStatus != NERR_Success) {
+ try_return(NetStatus);
+ }
+
+ NetStatus = BrCreateTimer(&Network->MasterBrowserTimer);
+
+ if (NetStatus != NERR_Success) {
+ try_return(NetStatus);
+ }
+
+ NetStatus = BrCreateTimer(&Network->MasterBrowserAnnouncementTimer);
+
+ if (NetStatus != NERR_Success) {
+ try_return(NetStatus);
+ }
+
+
+ //
+ // Handle the alternate transport.
+ //
+
+ if (ARGUMENT_PRESENT(AlternateTransportName)) {
+ PNETWORK AlternateNetwork = BrFindNetwork( DomainInfo, AlternateTransportName);
+
+ //
+ // If we didn't find an alternate network, or if that network
+ // already has an alternate network, return an error.
+ //
+
+ if ( AlternateNetwork == NULL ) {
+ BrPrint(( BR_CRITICAL,
+ "%ws: %ws: Creating network. Can't find alternate net %ws\n",
+ DomainInfo->DomUnicodeDomainName,
+ TransportName->Buffer,
+ AlternateTransportName ));
+ try_return(NetStatus = NERR_InternalError);
+ }
+
+ BrDereferenceNetwork( AlternateNetwork );
+ if (AlternateNetwork->AlternateNetwork != NULL) {
+ try_return(NetStatus = NERR_InternalError);
+ }
+
+ Network->Flags |= NETWORK_IPX;
+
+ //
+ // Link the two networks together.
+ //
+
+ Network->AlternateNetwork = AlternateNetwork;
+
+ AlternateNetwork->AlternateNetwork = Network;
+
+ } else {
+ Network->AlternateNetwork = NULL;
+ }
+
+ //
+ // Since the Rdr doesn't support this transport,
+ // we actually have to bind ourselves.
+ //
+ // Bind for Direct host IPX and for emulated domains.
+ //
+
+ if ( (Network->Flags & NETWORK_IPX) || DomainInfo->IsEmulatedDomain ) {
+ NetStatus = BrBindToTransport( TransportName->Buffer,
+ DomainInfo->DomUnicodeDomainName,
+ DomainInfo->DomUnicodeComputerName );
+
+ if ( NetStatus != NERR_Success ) {
+ BrPrint(( BR_CRITICAL,
+ "%ws: %ws: Creating network. Can't bind to transport\n",
+ DomainInfo->DomUnicodeDomainName,
+ TransportName->Buffer ));
+ try_return( NetStatus );
+ }
+
+
+ Network->Flags |= NETWORK_BOUND;
+
+ }
+
+ //
+ // Post a WaitForRoleChange FsControl on each network the bowser
+ // driver supports. This FsControl will complete when a "tickle"
+ // packet is received on the machine, or when a master browser loses
+ // an election.
+ //
+
+ NetStatus = PostWaitForRoleChange(Network);
+
+ if (NetStatus != NERR_Success) {
+ BrPrint(( BR_CRITICAL,
+ "%ws: %ws: Creating network. Can't post wait for role change: %ld\n",
+ DomainInfo->DomUnicodeDomainName,
+ TransportName->Buffer,
+ NetStatus ));
+ try_return(NetStatus);
+ }
+
+ EnterCriticalSection(&BrInfo.ConfigCritSect);
+ ConfigCritSectLocked = TRUE;
+
+ //
+ // If MaintainServerList says to automatically determine mastership,
+ // post queryies to the driver.
+ //
+
+ if (BrInfo.MaintainServerList == 0) {
+
+ //
+ // Post a BecomeBackup FsControl API on each network the bowser
+ // driver supports. This FsControl will complete when the master
+ // for the net wants this client to become a backup server.
+ //
+
+ NetStatus = PostBecomeBackup( Network );
+
+ if (NetStatus != NERR_Success) {
+ BrPrint(( BR_CRITICAL,
+ "%ws: %ws: Creating network. Can't post become backup.\n",
+ DomainInfo->DomUnicodeDomainName,
+ TransportName->Buffer,
+ NetStatus ));
+ try_return(NetStatus);
+ }
+
+ //
+ // Post a BecomeMaster FsControl on each network the bowser driver
+ // supports. This FsControl will complete when this machine becomes
+ // a master browser server.
+ //
+
+ NetStatus = PostBecomeMaster( Network );
+
+ if (NetStatus != NERR_Success) {
+ BrPrint(( BR_CRITICAL,
+ "%ws: %ws: Creating network. Can't post become master.\n",
+ DomainInfo->DomUnicodeDomainName,
+ TransportName->Buffer,
+ NetStatus ));
+ try_return(NetStatus);
+ }
+
+ }
+
+
+
+ NetStatus = NERR_Success;
+
+try_exit:NOTHING;
+ } finally {
+
+ if ( AbnormalTermination() ) {
+ NetStatus = NERR_InternalError;
+ }
+
+ if ( ConfigCritSectLocked ) {
+ LeaveCriticalSection(&BrInfo.ConfigCritSect);
+ ConfigCritSectLocked = FALSE;
+ }
+
+ if (NetStatus != NERR_Success) {
+
+ if (Network != NULL) {
+
+ //
+ // If we've initialized to the point where we can call
+ // we can call BrDeleteNetwork, do so.
+ //
+
+ if ( CanCallBrDestroyNetwork ) {
+ (VOID) BrDeleteNetwork( Network, NULL );
+
+ //
+ // Otherwise, just delete what we've created.
+ //
+ } else {
+
+ if (ResponseCacheLockInitialized) {
+ DeleteCriticalSection(&Network->ResponseCacheLock);
+ }
+
+ if (NetworkLockInitialized) {
+ RtlDeleteResource(&Network->Lock);
+ }
+
+ if (Network->NetworkName.Buffer != NULL) {
+ MIDL_user_free(Network->NetworkName.Buffer);
+ }
+
+ MIDL_user_free(Network);
+ }
+
+ }
+
+ //
+ // We're done creating the network.
+ // Remove this routines reference to it.
+ //
+
+ } else {
+ BrDereferenceNetwork( Network );
+ }
+
+ }
+ return NetStatus;
+}
+
+ VOID
+BrUninitializeNetworks(
+ IN DWORD BrInitState
+ )
+{
+ DeleteCriticalSection(&NetworkCritSect);
+
+ NumberOfServicedNetworks = 0;
+
+}
+
+ PNETWORK
+BrReferenceNetwork(
+ PNETWORK PotentialNetwork
+ )
+/*++
+
+Routine Description:
+
+ This routine will look up a network given a potential pointer to the network.
+
+ This routine is useful if a caller has a pointer to a network but
+ hasn't incremented the reference count. For instance,
+ BrIssueAsyncBrowserIoControl calls the async completion routines like that.
+
+Arguments:
+
+ PotentialNetwork - Pointer to the network structure to be verified.
+
+Return Value:
+
+ NULL - No such network exists
+
+ A pointer to the network found. The found network should be dereferenced
+ using BrDereferenceNetwork.
+
+--*/
+{
+ NTSTATUS Status;
+ PLIST_ENTRY NetEntry;
+
+ EnterCriticalSection(&NetworkCritSect);
+
+ for (NetEntry = ServicedNetworks.Flink ;
+ NetEntry != &ServicedNetworks;
+ NetEntry = NetEntry->Flink ) {
+ PNETWORK Network = CONTAINING_RECORD(NetEntry, NETWORK, NextNet);
+
+ if ( PotentialNetwork == Network ) {
+
+ Network->ReferenceCount ++;
+ BrPrint(( BR_LOCKS,
+ "%ws: %ws: reference network: %ld\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ Network->NetworkName.Buffer,
+ Network->ReferenceCount ));
+ LeaveCriticalSection(&NetworkCritSect);
+
+ return Network;
+ }
+
+ }
+
+ LeaveCriticalSection(&NetworkCritSect);
+
+ return NULL;
+}
+
+
+VOID
+BrDereferenceNetwork(
+ IN PNETWORK Network
+ )
+/*++
+
+Routine Description:
+
+ This routine decrements the reference to a network. If the network reference
+ count goes to 0, remove the network.
+
+ On entry, the global NetworkCritSect crit sect may not be locked
+
+Arguments:
+
+ Network - The network to dereference
+
+Return Value:
+
+ None
+
+--*/
+{
+ NTSTATUS Status;
+ ULONG ReferenceCount;
+
+ EnterCriticalSection(&NetworkCritSect);
+ ReferenceCount = -- Network->ReferenceCount;
+ LeaveCriticalSection(&NetworkCritSect);
+ BrPrint(( BR_LOCKS,
+ "%ws: %ws: Dereference network: %ld\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ Network->NetworkName.Buffer,
+ ReferenceCount ));
+
+ if ( ReferenceCount != 0 ) {
+ return;
+ }
+
+
+ //
+ // Stop being the master on this network. This means removing
+ // our name from the bowser driver and forcing an election.
+ //
+ if (Network->Role & ROLE_MASTER) {
+ BrStopMaster(Network);
+ }
+
+
+ //
+ // Stop being a backup on this network. This means downgrading our
+ // machine to idle.
+ //
+ if (Network->Role & (ROLE_POTENTIAL_BACKUP | ROLE_BACKUP)) {
+ BrStopBackup(Network);
+ }
+
+ //
+ // Ensure that there are no browser related names active in the browser.
+ //
+
+ BrUpdateBrowserStatus(Network, 0);
+
+ //
+ // If this service did the bind, do the unbind.
+ //
+ // True for IPX and for emulated domains.
+ //
+
+ if (Network->Flags & NETWORK_BOUND) {
+ NET_API_STATUS NetStatus;
+
+ NetStatus = BrUnbindFromTransport( Network->NetworkName.Buffer,
+ Network->DomainInfo->DomUnicodeDomainName );
+
+ if (NetStatus != NERR_Success) {
+ BrPrint(( BR_CRITICAL,
+ "%ws: %ws: Unable to unbind from IPX transport\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ Network->NetworkName.Buffer ));
+ }
+
+ }
+
+ UninitializeInterimServerList(&Network->BrowseTable);
+
+ UninitializeInterimServerList(&Network->DomainList);
+
+ if (Network->BackupServerList != NULL) {
+ MIDL_user_free(Network->BackupServerList);
+ }
+
+ if (Network->BackupDomainList != NULL) {
+ MIDL_user_free(Network->BackupDomainList);
+ }
+
+ if (Network->NetworkName.Buffer != NULL) {
+ MIDL_user_free(Network->NetworkName.Buffer);
+ }
+
+ RtlDeleteResource(&Network->Lock);
+
+
+ BrDestroyResponseCache(Network);
+
+ DeleteCriticalSection(&Network->ResponseCacheLock);
+
+ BrDereferenceDomain( Network->DomainInfo );
+
+ MIDL_user_free(Network);
+
+ return;
+
+}
+
+ NET_API_STATUS
+BrDeleteNetwork(
+ IN PNETWORK Network,
+ IN PVOID Context
+ )
+/*++
+
+Routine Description:
+
+ This routine prevents any new references to the network. It then removes
+ the global reference to the Network allowing it to be deleted.
+
+ Finally, it sleeps until the only reference left is the one held by the caller.
+ This ensures the Network will be deleted when the caller Dereferences the
+ network.
+
+Arguments:
+
+ Network - The network to remove
+ The caller must have a reference to the Network.
+
+ Context - not used.
+
+Return Value:
+
+ NERR_Success - always
+
+--*/
+{
+ BrPrint(( BR_NETWORK,
+ "%ws: %ws: Delete network\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ Network->NetworkName.Buffer ));
+ //
+ // Remove this network from the list to prevent any new references.
+ //
+
+ EnterCriticalSection(&NetworkCritSect);
+ RemoveEntryList(&Network->NextNet);
+ NumberOfServicedNetworks -= 1;
+ LeaveCriticalSection(&NetworkCritSect);
+
+ //
+ // Prevent new references by the timer routines
+ //
+
+ BrDestroyTimer(&Network->MasterBrowserAnnouncementTimer);
+
+ BrDestroyTimer(&Network->MasterBrowserTimer);
+
+ BrDestroyTimer(&Network->BackupBrowserTimer);
+
+ //
+ // Decrement the global reference due to being is 'ServicedNetworks'
+ //
+
+ BrDereferenceNetwork( Network );
+
+
+ //
+ // Loop until the caller has the last reference.
+ //
+
+ EnterCriticalSection(&NetworkCritSect);
+ while ( Network->ReferenceCount != 1 ) {
+ LeaveCriticalSection(&NetworkCritSect);
+ Sleep(1000);
+ EnterCriticalSection(&NetworkCritSect);
+ }
+ LeaveCriticalSection(&NetworkCritSect);
+
+ UNREFERENCED_PARAMETER(Context);
+
+ return NERR_Success;
+
+}
+
+
+ PNETWORK
+BrFindNetwork(
+ PDOMAIN_INFO DomainInfo,
+ PUNICODE_STRING TransportName
+ )
+/*++
+
+Routine Description:
+
+ This routine will look up a network given a name.
+
+Arguments:
+
+ DomainInfo - Specifies the domain this network is specific to
+
+ TransportName - The name of the transport to look up.
+
+Return Value:
+
+ NULL - No such network exists
+
+ A pointer to the network found. The found network should be dereferenced
+ using BrDereferenceNetwork.
+
+--*/
+{
+ NTSTATUS Status;
+ PLIST_ENTRY NetEntry;
+
+ EnterCriticalSection(&NetworkCritSect);
+
+ for (NetEntry = ServicedNetworks.Flink ;
+ NetEntry != &ServicedNetworks;
+ NetEntry = NetEntry->Flink ) {
+ PNETWORK Network = CONTAINING_RECORD(NetEntry, NETWORK, NextNet);
+
+ if ( Network->DomainInfo == DomainInfo &&
+ RtlEqualUnicodeString(&Network->NetworkName, TransportName, TRUE)) {
+
+ Network->ReferenceCount ++;
+ BrPrint(( BR_LOCKS,
+ "%ws: %ws: find network: %ld\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ Network->NetworkName.Buffer,
+ Network->ReferenceCount ));
+ LeaveCriticalSection(&NetworkCritSect);
+
+ return Network;
+ }
+
+ }
+
+ LeaveCriticalSection(&NetworkCritSect);
+
+ return NULL;
+}
+
+ NET_API_STATUS
+BrEnumerateNetworks(
+ PNET_ENUM_CALLBACK Callback,
+ PVOID Context
+ )
+/*++
+
+Routine Description:
+
+ This routine enumerates all the networks and calls back the specified
+ callback routine with the specified context.
+
+Arguments:
+
+ Callback - The callback routine to call.
+ Context - Context for the routine.
+
+Return Value:
+
+ Status of operation (mostly status of allocations).
+
+--*/
+{
+ NET_API_STATUS NetStatus = NERR_Success;
+ PLIST_ENTRY NetEntry;
+ PNETWORK Network;
+ PNETWORK NetworkToDereference = NULL;
+
+ EnterCriticalSection(&NetworkCritSect);
+
+
+ for (NetEntry = ServicedNetworks.Flink ;
+ NetEntry != &ServicedNetworks;
+ NetEntry = NetEntry->Flink ) {
+
+ //
+ // Reference the next network in the list
+ //
+
+ Network = CONTAINING_RECORD(NetEntry, NETWORK, NextNet);
+ Network->ReferenceCount ++;
+ BrPrint(( BR_LOCKS,
+ "%ws: %ws: enumerate network: %ld\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ Network->NetworkName.Buffer,
+ Network->ReferenceCount ));
+
+ LeaveCriticalSection(&NetworkCritSect);
+
+ //
+ // Dereference any network previously referenced.
+ //
+ if ( NetworkToDereference != NULL) {
+ BrDereferenceNetwork( NetworkToDereference );
+ NetworkToDereference = NULL;
+ }
+
+
+ //
+ // Call into the callback routine with this network.
+ //
+
+ NetStatus = (Callback)(Network, Context);
+
+ EnterCriticalSection(&NetworkCritSect);
+
+ NetworkToDereference = Network;
+
+ if (NetStatus != NERR_Success) {
+ break;
+ }
+
+ }
+
+ LeaveCriticalSection(&NetworkCritSect);
+
+ //
+ // Dereference the last network
+ //
+ if ( NetworkToDereference != NULL) {
+ BrDereferenceNetwork( NetworkToDereference );
+ }
+
+ return NetStatus;
+
+}
+
+ NET_API_STATUS
+BrEnumerateNetworksForDomain(
+ PDOMAIN_INFO DomainInfo OPTIONAL,
+ PNET_ENUM_CALLBACK Callback,
+ PVOID Context
+ )
+/*++
+
+Routine Description:
+
+ This routine enumerates all the networks for a specified domain
+ and calls back the specified callback routine with the specified context.
+
+Arguments:
+
+ DomainInfo - Specifies the Domain to limit the enumeration to.
+ NULL implies the primary domain.
+
+ Callback - The callback routine to call.
+
+ Context - Context for the routine.
+
+Return Value:
+
+ Status of operation (mostly status of allocations).
+
+--*/
+{
+
+ NTSTATUS NetStatus = NERR_Success;
+ PLIST_ENTRY NetEntry;
+ PNETWORK Network;
+ PNETWORK NetworkToDereference = NULL;
+
+ //
+ // Default to the primary domain.
+ //
+ if ( DomainInfo == NULL && !IsListEmpty( &ServicedDomains ) ) {
+ DomainInfo = CONTAINING_RECORD(ServicedDomains.Flink, DOMAIN_INFO, Next);
+ }
+
+ EnterCriticalSection(&NetworkCritSect);
+
+
+ for (NetEntry = ServicedNetworks.Flink ;
+ NetEntry != &ServicedNetworks;
+ NetEntry = NetEntry->Flink ) {
+
+
+ //
+ // If the entry isn't for the specified domain,
+ // skip it.
+ //
+ Network = CONTAINING_RECORD(NetEntry, NETWORK, NextNet);
+ if ( Network->DomainInfo != DomainInfo ) {
+ continue;
+ }
+
+ //
+ // Reference the next network in the list
+ //
+ Network->ReferenceCount ++;
+ BrPrint(( BR_LOCKS,
+ "%ws: %ws: enumerate network for domain: %ld\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ Network->NetworkName.Buffer,
+ Network->ReferenceCount ));
+
+ LeaveCriticalSection(&NetworkCritSect);
+
+ //
+ // Dereference any network previously referenced.
+ //
+ if ( NetworkToDereference != NULL) {
+ BrDereferenceNetwork( NetworkToDereference );
+ NetworkToDereference = NULL;
+ }
+
+
+ //
+ // Call into the callback routine with this network.
+ //
+
+ NetStatus = (Callback)(Network, Context);
+
+ EnterCriticalSection(&NetworkCritSect);
+
+ NetworkToDereference = Network;
+
+ if (NetStatus != NERR_Success) {
+ break;
+ }
+
+ }
+
+ LeaveCriticalSection(&NetworkCritSect);
+
+ //
+ // Dereference the last network
+ //
+ if ( NetworkToDereference != NULL) {
+ BrDereferenceNetwork( NetworkToDereference );
+ }
+
+ return NERR_Success;
+
+}
+
+#if DBG
+
+ BOOL
+BrLockNetwork(
+ IN PNETWORK Network,
+ IN PCHAR FileName,
+ IN ULONG LineNumber
+ )
+{
+ PCHAR File;
+
+ File = strrchr(FileName, '\\');
+
+ if (File == NULL) {
+ File = FileName;
+ }
+
+ BrPrint(( BR_LOCKS,
+ "%ws: %ws: Acquiring network lock %s:%d\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ Network->NetworkName.Buffer,
+ File,
+ LineNumber ));
+
+ if (!RtlAcquireResourceExclusive(&(Network)->Lock, TRUE)) {
+ BrPrint(( BR_CRITICAL,
+ "%ws: %ws: Failed to acquire network %s:%d\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ Network->NetworkName.Buffer,
+ File, LineNumber ));
+ return FALSE;
+
+ } else {
+
+ InterlockedIncrement( &Network->LockCount );
+
+ BrPrint(( BR_LOCKS,
+ "%ws: %ws: network lock %s:%d acquired\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ Network->NetworkName.Buffer,
+ File, LineNumber ));
+ }
+
+ return TRUE;
+
+}
+
+ BOOL
+BrLockNetworkShared(
+ IN PNETWORK Network,
+ IN PCHAR FileName,
+ IN ULONG LineNumber
+ )
+{
+ PCHAR File;
+
+ File = strrchr(FileName, '\\');
+
+ if (File == NULL) {
+ File = FileName;
+ }
+
+ BrPrint(( BR_LOCKS,
+ "%ws: %ws: Acquiring network lock %s:%d\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ Network->NetworkName.Buffer,
+ File, LineNumber ));
+
+ if (!RtlAcquireResourceShared(&(Network)->Lock, TRUE)) {
+ BrPrint(( BR_CRITICAL,
+ "%ws: %ws: failed to acquire network lock %s:%d\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ Network->NetworkName.Buffer,
+ File, LineNumber ));
+
+ return FALSE;
+
+ } else {
+
+ // Use InterlockedIncrement since we only have a shared lock on the
+ // resource.
+ InterlockedIncrement( &Network->LockCount );
+
+ BrPrint(( BR_LOCKS,
+ "%ws: %ws: Network lock %s:%d acquired\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ Network->NetworkName.Buffer,
+ File, LineNumber ));
+ }
+
+ return TRUE;
+
+}
+
+ VOID
+BrUnlockNetwork(
+ IN PNETWORK Network,
+ IN PCHAR FileName,
+ IN ULONG LineNumber
+ )
+{
+ PCHAR File;
+ LONG ReturnValue;
+
+ File = strrchr(FileName, '\\');
+
+ if (File == NULL) {
+ File = FileName;
+ }
+
+
+ BrPrint(( BR_LOCKS,
+ "%ws: %ws: Releasing network lock %s:%d\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ Network->NetworkName.Buffer,
+ File, LineNumber ));
+
+ //
+ // Decrement the lock count.
+ //
+
+ ReturnValue = InterlockedDecrement( &Network->LockCount );
+
+ if ( ReturnValue < 0) {
+ BrPrint(( BR_CRITICAL,
+ "%ws: %ws: Over released network lock %s:%d\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ Network->NetworkName.Buffer,
+ File, LineNumber ));
+ }
+
+ RtlReleaseResource(&(Network)->Lock);
+
+ return;
+}
+#endif
+
+
+
+#if DBG
+ VOID
+BrDumpNetworks(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This routine will dump the contents of each of the browser network
+ structures.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ BrEnumerateNetworks(BrDumpNetworksWorker, NULL);
+}
+
+
+ NET_API_STATUS
+BrDumpNetworksWorker(
+ IN PNETWORK Network,
+ IN PVOID Context
+ )
+{
+ if (!LOCK_NETWORK(Network)) {
+ return NERR_InternalError;
+ }
+
+ BrPrint(( BR_CRITICAL,
+ "%ws: %ws: Network at %lx\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ Network->NetworkName.Buffer,
+ Network ));
+ BrPrint(( BR_CRITICAL, " Reference Count: %lx\n", Network->ReferenceCount));
+ BrPrint(( BR_CRITICAL, " Flags: %lx\n", Network->Flags));
+ BrPrint(( BR_CRITICAL, " Role: %lx\n", Network->Role));
+ BrPrint(( BR_CRITICAL, " Master Browser Name: %ws\n", Network->UncMasterBrowserName ));
+
+ UNLOCK_NETWORK(Network);
+
+ return(NERR_Success);
+}
+#endif
diff --git a/private/net/svcdlls/browser2/server/browsnet.h b/private/net/svcdlls/browser2/server/browsnet.h
new file mode 100644
index 000000000..9b6356a3b
--- /dev/null
+++ b/private/net/svcdlls/browser2/server/browsnet.h
@@ -0,0 +1,330 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ browsenet.h
+
+Abstract:
+
+ Private header file to be included by Browser service modules that
+ need to deal with the network specific browser tables.
+
+Author:
+
+ Rita Wong (ritaw) 22-May-1991
+
+Revision History:
+
+--*/
+
+
+#ifndef _BROWSENET_INCLUDED_
+#define _BROWSENET_INCLUDED_
+
+#define NETWORK_BECOME_MASTER_POSTED 0x00000001
+#define NETWORK_GET_MASTER_ANNOUNCE_POSTED 0x00000008
+#define NETWORK_WANNISH 0x80000000
+#define NETWORK_RAS 0x40000000
+#define NETWORK_IPX 0x20000000
+#define NETWORK_BOUND 0x10000000
+
+#define OTHERDOMAIN_INVALID 0x00000001
+
+#define NETWORK_MASTER_GETBLIST_POSTED 0x00000001
+
+typedef struct _NET_OTHER_DOMAIN {
+ LIST_ENTRY Next;
+ ULONG Flags;
+ WCHAR Name[DNLEN+1];
+} NET_OTHER_DOMAIN, *PNET_OTHER_DOMAIN;
+
+//
+// Network.
+//
+// Almost all of the browser data structures are tied to the "Network"
+// structure.
+//
+// It contains the browse list for the network and information about the
+// domain (including the name of the master, etc).
+//
+
+typedef struct _NETWORK {
+ //
+ // The Lock protects the contents of the network structure, including
+ // the browse list, backup list and domain list.
+ //
+
+ RTL_RESOURCE Lock;
+
+ LONG LockCount;
+
+ ULONG Flags;
+
+ //
+ // The NextNet list links the structure to other networks.
+ //
+
+ LIST_ENTRY NextNet;
+
+ //
+ // Domain this network is specific to
+ //
+
+ PDOMAIN_INFO DomainInfo;
+
+ //
+ // The ReferenceCount indicates the number of threads accessing this
+ // network structure.
+ //
+
+ ULONG ReferenceCount;
+
+ //
+ // The NetworkName is the name of the network driver that is used
+ // to access the network. This is used to identify the network
+ // to the bowser driver so it can return the correct network list.
+ //
+
+ UNICODE_STRING NetworkName; // Name of network (\Device\Nbf)
+
+ struct _NETWORK *AlternateNetwork; // Alternate name for network (if IPX).
+
+ //
+ // This is a bitmask indicating the role of this browser server.
+ //
+
+ ULONG Role;
+
+ ULONG MasterAnnouncementIndex;
+
+ ULONG NumberOfFailedBackupTimers;
+
+ ULONG NumberOfFailedPromotions;
+
+ ULONG NumberOfPromotionEventsLogged;
+
+ LONG LastBackupBrowserReturned;
+
+ LONG LastDomainControllerBrowserReturned;
+
+ //
+ // The time we stopped being a backup browser.
+ //
+
+ ULONG TimeStoppedBackup;
+
+ //
+ // The UncMasterBrowserName contains the name of the master browser server
+ // for this network.
+ //
+
+ WCHAR UncMasterBrowserName[UNCLEN+1]; // Name of master browser server
+
+ //
+ // Timer used when server is a backup browser server.
+ //
+ // When it expires, the browser downloads a new browser server
+ // list from the master browser server.
+ //
+
+ BROWSER_TIMER BackupBrowserTimer;
+
+ //
+ // Server and domain list for backup browser (and # of entries in each).
+ //
+
+ PSERVER_INFO_101 BackupServerList;
+ DWORD TotalBackupServerListEntries;
+ DWORD BytesToHoldBackupServerList;
+
+ PSERVER_INFO_101 BackupDomainList;
+ DWORD TotalBackupDomainListEntries;
+ DWORD BytesToHoldBackupDomainList;
+
+ //
+ // Lock protecting MasterFlags section of Network structure.
+ //
+
+ ULONG MasterFlags;
+ ULONG MasterBrowserTimerCount; // # of times we've run the master timer.
+
+ //
+ // Master browsers maintain their server list in an "interim server
+ // list", not as raw data from the server.
+ //
+
+ ULONG LastBowserServerQueried;
+ INTERIM_SERVER_LIST BrowseTable; // Browse list for network.
+
+ ULONG LastBowserDomainQueried;
+ INTERIM_SERVER_LIST DomainList; // List of domains active on network
+
+ //
+ // If the browser's role is MasterBrowserServer, then the
+ // DomainMasterBrowserList contains the list of domain master browser
+ // servers.
+ //
+
+ LIST_ENTRY OtherDomainsList; // List of domain master browser.
+
+ //
+ // Timer used when server is a master browser server.
+ //
+ // When it expires, the master browser downloads a new browser
+ // server list from the domain master browser server
+ //
+
+ BROWSER_TIMER MasterBrowserTimer;
+
+ //
+ // Timer used to announce the domain.
+ //
+
+ BROWSER_TIMER MasterBrowserAnnouncementTimer;
+
+ //
+ // List of cached browser responses.
+ //
+
+ CRITICAL_SECTION ResponseCacheLock;
+
+ LIST_ENTRY ResponseCache;
+
+ //
+ // For browse masters, this is the time the cache was last flushed.
+ //
+ // Every <n> seconds, we will age the cache on the master and refresh
+ // the list with the list from the driver.
+ //
+
+ DWORD TimeCacheFlushed;
+
+ DWORD NumberOfCachedResponses;
+
+} NETWORK, *PNETWORK;
+
+#if DBG
+BOOL
+BrLockNetwork(
+ IN PNETWORK Network,
+ IN PCHAR FileName,
+ IN ULONG LineNumber
+ );
+
+BOOL
+BrLockNetworkShared(
+ IN PNETWORK Network,
+ IN PCHAR FileName,
+ IN ULONG LineNumber
+ );
+
+VOID
+BrUnlockNetwork(
+ IN PNETWORK Network,
+ IN PCHAR FileName,
+ IN ULONG LineNumber
+ );
+
+#define LOCK_NETWORK(Network) BrLockNetwork(Network, __FILE__, __LINE__)
+
+#define LOCK_NETWORK_SHARED(Network) BrLockNetworkShared(Network, __FILE__, __LINE__)
+
+#define UNLOCK_NETWORK(Network) BrUnlockNetwork(Network, __FILE__, __LINE__)
+
+#else
+
+#define LOCK_NETWORK(Network) RtlAcquireResourceExclusive(&(Network)->Lock, TRUE)
+
+#define LOCK_NETWORK_SHARED(Network) RtlAcquireResourceShared(&(Network)->Lock, TRUE)
+
+#define UNLOCK_NETWORK(Network) RtlReleaseResource(&(Network)->Lock)
+
+#endif
+
+//
+// The NET_ENUM_CALLBACK is a callback for BrEnumerateNetworks.
+//
+// It defines a routine that takes two parameters, the first is a network
+// structure, the second is a context for that network.
+//
+
+
+typedef
+NET_API_STATUS
+(*PNET_ENUM_CALLBACK)(
+ PNETWORK Network,
+ PVOID Context
+ );
+
+
+VOID
+BrInitializeNetworks(
+ VOID
+ );
+
+VOID
+BrUninitializeNetworks(
+ IN ULONG BrInitState
+ );
+
+PNETWORK
+BrReferenceNetwork(
+ PNETWORK PotentialNetwork
+ );
+
+VOID
+BrDereferenceNetwork(
+ IN PNETWORK Network
+ );
+
+PNETWORK
+BrFindNetwork(
+ PDOMAIN_INFO DomainInfo,
+ PUNICODE_STRING TransportName
+ );
+
+VOID
+BrDumpNetworks(
+ VOID
+ );
+
+NET_API_STATUS
+BrEnumerateNetworks(
+ PNET_ENUM_CALLBACK Callback,
+ PVOID Context
+ );
+
+NET_API_STATUS
+BrEnumerateNetworksForDomain(
+ PDOMAIN_INFO DomainInfo,
+ PNET_ENUM_CALLBACK Callback,
+ PVOID Context
+ );
+
+NET_API_STATUS
+BrCreateNetworks(
+ PDOMAIN_INFO DomainInfo
+ );
+
+NET_API_STATUS
+BrCreateNetwork(
+ PUNICODE_STRING TransportName,
+ IN BOOLEAN Wannish,
+ IN BOOLEAN Ras,
+ IN PUNICODE_STRING AlternateTransportName OPTIONAL,
+ IN PDOMAIN_INFO DomainInfo
+ );
+
+NET_API_STATUS
+BrDeleteNetwork(
+ IN PNETWORK Network,
+ IN PVOID Context
+ );
+
+extern ULONG NumberOfServicedNetworks;
+
+extern CRITICAL_SECTION NetworkCritSect;
+
+#endif // _BROWSENET_INCLUDED_
diff --git a/private/net/svcdlls/browser2/server/brsec.h b/private/net/svcdlls/browser2/server/brsec.h
new file mode 100644
index 000000000..5fff37dbe
--- /dev/null
+++ b/private/net/svcdlls/browser2/server/brsec.h
@@ -0,0 +1,71 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ brsec.h
+
+Abstract:
+
+ Private header file to be included by Workstation service modules that
+ need to enforce security.
+
+Author:
+
+ Rita Wong (ritaw) 19-Feb-1991
+
+Revision History:
+
+--*/
+
+#ifndef _BRSEC_INCLUDED_
+#define _BRSEC_INCLUDED_
+
+#include <secobj.h>
+
+//-------------------------------------------------------------------//
+// //
+// Object specific access masks //
+// //
+//-------------------------------------------------------------------//
+
+//
+// ConfigurationInfo specific access masks
+//
+#define BROWSER_CONFIG_GUEST_INFO_GET 0x0001
+#define BROWSER_CONFIG_USER_INFO_GET 0x0002
+#define BROWSER_CONFIG_ADMIN_INFO_GET 0x0004
+#define BROWSER_CONFIG_INFO_SET 0x0008
+
+#define BROWSER_CONFIG_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | \
+ BROWSER_CONFIG_GUEST_INFO_GET | \
+ BROWSER_CONFIG_USER_INFO_GET | \
+ BROWSER_CONFIG_ADMIN_INFO_GET | \
+ BROWSER_CONFIG_INFO_SET)
+
+
+//
+// Object type names for audit alarm tracking
+//
+#define CONFIG_INFO_OBJECT TEXT("BrowserConfigurationInfo")
+
+//
+// Security descriptors of workstation objects to control user accesses
+// to the workstation configuration information, sending messages, and the
+// logon support functions.
+//
+extern PSECURITY_DESCRIPTOR ConfigurationInfoSd;
+
+//
+// Generic mapping for each workstation object
+//
+extern GENERIC_MAPPING BrConfigInfoMapping;
+
+
+NET_API_STATUS
+BrCreateBrowserObjects(
+ VOID
+ );
+
+#endif // ifndef _WSSEC_INCLUDED_
diff --git a/private/net/svcdlls/browser2/server/brutil.c b/private/net/svcdlls/browser2/server/brutil.c
new file mode 100644
index 000000000..a9a5e92db
--- /dev/null
+++ b/private/net/svcdlls/browser2/server/brutil.c
@@ -0,0 +1,546 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ brutil.c
+
+Abstract:
+
+ This module contains miscellaneous utility routines used by the
+ Browser service.
+
+Author:
+
+ Rita Wong (ritaw) 01-Mar-1991
+
+Revision History:
+
+--*/
+
+#include "precomp.h"
+#pragma hdrstop
+
+//-------------------------------------------------------------------//
+// //
+// Local function prototypes //
+// //
+//-------------------------------------------------------------------//
+
+
+//-------------------------------------------------------------------//
+// //
+// Global variables //
+// //
+//-------------------------------------------------------------------//
+
+
+
+NET_API_STATUS
+BrMapStatus(
+ IN NTSTATUS NtStatus
+ )
+/*++
+
+Routine Description:
+
+ This function takes an NT status code and maps it to the appropriate
+ error code expected from calling a LAN Man API.
+
+Arguments:
+
+ NtStatus - Supplies the NT status.
+
+Return Value:
+
+ Returns the appropriate LAN Man error code for the NT status.
+
+--*/
+{
+ //
+ // A small optimization for the most common case.
+ //
+ if (NT_SUCCESS(NtStatus)) {
+ return NERR_Success;
+ }
+
+ switch (NtStatus) {
+ case STATUS_OBJECT_NAME_COLLISION:
+ return ERROR_ALREADY_ASSIGNED;
+
+ case STATUS_OBJECT_NAME_NOT_FOUND:
+ return NERR_UseNotFound;
+
+ case STATUS_REDIRECTOR_STARTED:
+ return NERR_ServiceInstalled;
+
+ default:
+ return NetpNtStatusToApiStatus(NtStatus);
+ }
+
+}
+
+
+ ULONG
+BrCurrentSystemTime()
+{
+ NTSTATUS Status;
+ SYSTEM_TIMEOFDAY_INFORMATION TODInformation;
+ LARGE_INTEGER CurrentTime;
+ ULONG TimeInSecondsSince1980;
+ ULONG BootTimeInSecondsSince1980;
+
+ Status = NtQuerySystemInformation(SystemTimeOfDayInformation,
+ &TODInformation,
+ sizeof(TODInformation),
+ NULL);
+
+ if (!NT_SUCCESS(Status)) {
+ return(0);
+ }
+
+ Status = NtQuerySystemTime(&CurrentTime);
+
+ if (!NT_SUCCESS(Status)) {
+ return(0);
+ }
+
+ RtlTimeToSecondsSince1980(&CurrentTime, &TimeInSecondsSince1980);
+ RtlTimeToSecondsSince1980(&TODInformation.BootTime, &BootTimeInSecondsSince1980);
+
+ return(TimeInSecondsSince1980 - BootTimeInSecondsSince1980);
+
+}
+
+ VOID
+BrLogEvent(
+ IN ULONG MessageId,
+ IN ULONG ErrorCode,
+ IN ULONG NumberOfSubStrings,
+ IN LPWSTR *SubStrings
+ )
+{
+
+ HANDLE LogHandle;
+
+ PSID UserSid = NULL;
+ DWORD Severity;
+ WORD Type;
+
+
+ LogHandle = RegisterEventSourceW (
+ NULL,
+ SERVICE_BROWSER
+ );
+
+ if (LogHandle == NULL) {
+ BrPrint(( BR_CRITICAL, "RegisterEventSourceW failed %lu\n",
+ GetLastError()));
+ return;
+ }
+
+ //
+ // Log the error code specified
+ //
+
+ Severity = (MessageId & 0xc0000000) >> 30;
+
+ if (Severity == STATUS_SEVERITY_WARNING) {
+ Type = EVENTLOG_WARNING_TYPE;
+ } else if (Severity == STATUS_SEVERITY_SUCCESS) {
+ Type = EVENTLOG_SUCCESS;
+ } else if (Severity == STATUS_SEVERITY_INFORMATIONAL) {
+ Type = EVENTLOG_INFORMATION_TYPE;
+ } else if (Severity == STATUS_SEVERITY_ERROR) {
+ Type = EVENTLOG_ERROR_TYPE;
+ }
+
+ if (ErrorCode == NERR_Success) {
+
+ //
+ // No error codes were specified
+ //
+ (void) ReportEventW(
+ LogHandle,
+ Type,
+ 0, // event category
+ MessageId,
+ UserSid,
+ (WORD)NumberOfSubStrings,
+ 0,
+ SubStrings,
+ (PVOID) NULL
+ );
+
+ }
+ else {
+
+ (void) ReportEventW(
+ LogHandle,
+ Type,
+ 0, // event category
+ MessageId,
+ UserSid,
+ (WORD)NumberOfSubStrings,
+ sizeof(DWORD),
+ SubStrings,
+ (PVOID) &ErrorCode
+ );
+ }
+
+ DeregisterEventSource(LogHandle);
+}
+
+#if DBG
+
+#define TRACE_FILE_SIZE 256
+
+VOID
+BrResetTraceLogFile(
+ VOID
+ );
+
+CRITICAL_SECTION
+BrowserTraceLock = {0};
+
+HANDLE
+BrowserTraceLogHandle = NULL;
+
+DWORD
+BrTraceLogFileSize = 0;
+
+BOOLEAN BrowserTraceInitialized = {0};
+
+VOID
+BrowserTrace(
+ ULONG DebugFlag,
+ PCHAR FormatString,
+ ...
+ )
+#define LAST_NAMED_ARGUMENT FormatString
+
+{
+ CHAR OutputString[4096];
+ ULONG length;
+ ULONG BytesWritten;
+ static BeginningOfLine = TRUE;
+
+ va_list ParmPtr; // Pointer to stack parms.
+
+ if (!BrowserTraceInitialized) {
+ return;
+ }
+
+ //
+ // If we aren't debugging this functionality, just return.
+ //
+ if ( DebugFlag != 0 && (BrInfo.BrowserDebug & DebugFlag) == 0 ) {
+ return;
+ }
+
+ EnterCriticalSection(&BrowserTraceLock);
+ length = 0;
+
+ try {
+
+ if (BrowserTraceLogHandle == NULL) {
+ //
+ // We've not opened the trace log file yet, so open it.
+ //
+
+ BrOpenTraceLogFile();
+ }
+
+ if (BrowserTraceLogHandle == INVALID_HANDLE_VALUE) {
+ LeaveCriticalSection(&BrowserTraceLock);
+ return;
+ }
+
+ //
+ // Attempt to catch bad trace.
+ //
+
+ for (BytesWritten = 0; BytesWritten < strlen(FormatString) ; BytesWritten += 1) {
+ if (FormatString[BytesWritten] > 0x7f) {
+ DbgBreakPoint();
+ }
+ }
+
+
+ //
+ // Handle the beginning of a new line.
+ //
+ //
+
+ if ( BeginningOfLine ) {
+ SYSTEMTIME SystemTime;
+
+ //
+ // Put the timestamp at the begining of the line.
+ //
+ GetLocalTime( &SystemTime );
+ length += (ULONG) sprintf( &OutputString[length],
+ "%02u/%02u %02u:%02u:%02u ",
+ SystemTime.wMonth,
+ SystemTime.wDay,
+ SystemTime.wHour,
+ SystemTime.wMinute,
+ SystemTime.wSecond );
+
+
+ //
+ // Indicate the type of message on the line
+ //
+ {
+ char *Text;
+
+ switch (DebugFlag) {
+ case BR_CRITICAL:
+ Text = "[CRITICAL]"; break;
+ case BR_INIT:
+ Text = "[INIT] "; break;
+ case BR_SERVER_ENUM:
+ Text = "[ENUM] "; break;
+ case BR_UTIL:
+ Text = "[UTIL] "; break;
+ case BR_CONFIG:
+ Text = "[CONFIG] "; break;
+ case BR_MAIN:
+ Text = "[MAIN] "; break;
+ case BR_BACKUP:
+ Text = "[BACKUP] "; break;
+ case BR_MASTER:
+ Text = "[MASTER] "; break;
+ case BR_DOMAIN:
+ Text = "[DOMAIN] "; break;
+ case BR_NETWORK:
+ Text = "[NETWORK]"; break;
+ case BR_TIMER:
+ Text = "[TIMER]"; break;
+ case BR_QUEUE:
+ Text = "[QUEUE]"; break;
+ case BR_LOCKS:
+ Text = "[LOCKS]"; break;
+ default:
+ Text = "[UNKNOWN]"; break;
+ }
+ length += (ULONG) sprintf( &OutputString[length], "%s ", Text );
+ }
+ }
+
+ //
+ // Put a the information requested by the caller onto the line
+ //
+
+ va_start(ParmPtr, FormatString);
+
+ length += (ULONG) vsprintf(&OutputString[length], FormatString, ParmPtr);
+ BeginningOfLine = (length > 0 && OutputString[length-1] == '\n' );
+ if ( BeginningOfLine ) {
+ OutputString[length-1] = '\r';
+ OutputString[length] = '\n';
+ OutputString[length+1] = '\0';
+ length++;
+ }
+
+ va_end(ParmPtr);
+
+ ASSERT(length <= sizeof(OutputString));
+
+
+ //
+ // Actually write the bytes.
+ //
+
+ if (!WriteFile(BrowserTraceLogHandle, OutputString, length, &BytesWritten, NULL)) {
+ KdPrint(("Error writing to Browser log file: %ld\n", GetLastError()));
+ KdPrint(("%s", OutputString));
+ return;
+ }
+
+ if (BytesWritten != length) {
+ KdPrint(("Error writing time to Browser log file: %ld\n", GetLastError()));
+ KdPrint(("%s", OutputString));
+ return;
+ }
+
+ //
+ // If the file has grown too large,
+ // truncate it.
+ //
+
+ BrTraceLogFileSize += BytesWritten;
+
+ if (BrTraceLogFileSize > BrInfo.BrowserDebugFileLimit) {
+ BrResetTraceLogFile();
+ }
+
+ } finally {
+ LeaveCriticalSection(&BrowserTraceLock);
+ }
+}
+
+
+VOID
+BrInitializeTraceLog()
+{
+
+ InitializeCriticalSection(&BrowserTraceLock);
+ BrowserTraceInitialized = TRUE;
+
+}
+
+VOID
+BrGetTraceLogRoot(
+ IN PWCHAR TraceFile
+ )
+{
+ PSHARE_INFO_502 ShareInfo;
+
+ //
+ // If the DEBUG share exists, put the log file in that directory,
+ // otherwise, use the system root.
+ //
+ // This way, if the browser is running on an NTAS server, we can always
+ // get access to the log file.
+ //
+
+ if (NetShareGetInfo(NULL, L"DEBUG", 502, (PCHAR *)&ShareInfo) != NERR_Success) {
+
+ if (GetSystemDirectory(TraceFile, TRACE_FILE_SIZE*sizeof(WCHAR)) == 0) {
+ KdPrint(("Unable to get system directory: %ld\n", GetLastError()));
+ }
+
+ if (TraceFile[wcslen(TraceFile)] != L'\\') {
+ TraceFile[wcslen(TraceFile)+1] = L'\0';
+ TraceFile[wcslen(TraceFile)] = L'\\';
+ }
+
+ } else {
+ //
+ // Seed the trace file buffer with the local path of the netlogon
+ // share if it exists.
+ //
+
+ wcscpy(TraceFile, ShareInfo->shi502_path);
+
+ TraceFile[wcslen(ShareInfo->shi502_path)] = L'\\';
+ TraceFile[wcslen(ShareInfo->shi502_path)+1] = L'\0';
+
+ NetApiBufferFree(ShareInfo);
+ }
+
+}
+
+VOID
+BrResetTraceLogFile(
+ VOID
+ )
+{
+ WCHAR OldTraceFile[TRACE_FILE_SIZE];
+ WCHAR NewTraceFile[TRACE_FILE_SIZE];
+
+ if (BrowserTraceLogHandle != NULL) {
+ CloseHandle(BrowserTraceLogHandle);
+ }
+
+ BrowserTraceLogHandle = NULL;
+
+ BrGetTraceLogRoot(OldTraceFile);
+
+ wcscpy(NewTraceFile, OldTraceFile);
+
+ wcscat(OldTraceFile, L"Browser.Log");
+
+ wcscat(NewTraceFile, L"Browser.Bak");
+
+ //
+ // Delete the old log
+ //
+
+ DeleteFile(NewTraceFile);
+
+ //
+ // Rename the current log to the new log.
+ //
+
+ MoveFile(OldTraceFile, NewTraceFile);
+
+ BrOpenTraceLogFile();
+
+}
+
+VOID
+BrOpenTraceLogFile(
+ VOID
+ )
+{
+ WCHAR TraceFile[TRACE_FILE_SIZE];
+
+ BrGetTraceLogRoot(TraceFile);
+
+ wcscat(TraceFile, L"Browser.Log");
+
+ BrowserTraceLogHandle = CreateFile(TraceFile,
+ GENERIC_WRITE,
+ FILE_SHARE_READ,
+ NULL,
+ OPEN_ALWAYS,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL);
+
+
+ if (BrowserTraceLogHandle == INVALID_HANDLE_VALUE) {
+ KdPrint(("Error creating trace file %ws: %ld\n", TraceFile, GetLastError()));
+
+ return;
+ }
+
+ BrTraceLogFileSize = SetFilePointer(BrowserTraceLogHandle, 0, NULL, FILE_END);
+
+ if (BrTraceLogFileSize == 0xffffffff) {
+ KdPrint(("Error setting trace file pointer: %ld\n", GetLastError()));
+
+ return;
+ }
+}
+
+VOID
+BrUninitializeTraceLog()
+{
+ DeleteCriticalSection(&BrowserTraceLock);
+
+ if (BrowserTraceLogHandle != NULL) {
+ CloseHandle(BrowserTraceLogHandle);
+ }
+
+ BrowserTraceLogHandle = NULL;
+
+ BrowserTraceInitialized = FALSE;
+
+}
+
+NET_API_STATUS
+BrTruncateLog()
+{
+ if (BrowserTraceLogHandle == NULL) {
+ BrOpenTraceLogFile();
+ }
+
+ if (BrowserTraceLogHandle == INVALID_HANDLE_VALUE) {
+ return ERROR_GEN_FAILURE;
+ }
+
+ if (SetFilePointer(BrowserTraceLogHandle, 0, NULL, FILE_BEGIN) == 0xffffffff) {
+ return GetLastError();
+ }
+
+ if (!SetEndOfFile(BrowserTraceLogHandle)) {
+ return GetLastError();
+ }
+
+ return NO_ERROR;
+}
+
+#endif
diff --git a/private/net/svcdlls/browser2/server/brutil.h b/private/net/svcdlls/browser2/server/brutil.h
new file mode 100644
index 000000000..a6c1e7f41
--- /dev/null
+++ b/private/net/svcdlls/browser2/server/brutil.h
@@ -0,0 +1,106 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ brutil.h
+
+Abstract:
+
+ Private header file for the NT Workstation service included by every module
+ module of the Workstation service.
+
+Author:
+
+ Rita Wong (ritaw) 15-Feb-1991
+
+Revision History:
+
+--*/
+
+#ifndef _BRUTIL_INCLUDED_
+#define _BRUTIL_INCLUDED_
+
+//
+// This include file will be included by tstring.h if Unicode
+// is defined.
+//
+#ifndef UNICODE
+#include <stdlib.h> // Unicode string functions
+#endif
+
+#include "br.h"
+
+
+//
+// An invalid parameter is encountered. Return the value to identify
+// the parameter at fault.
+//
+#define RETURN_INVALID_PARAMETER(ErrorParameter, ParameterId) \
+ if (ARGUMENT_PRESENT(ErrorParameter)) { \
+ *ErrorParameter = ParameterId; \
+ } \
+ return ERROR_INVALID_PARAMETER;
+
+
+
+//-------------------------------------------------------------------//
+// //
+// Type definitions //
+// //
+//-------------------------------------------------------------------//
+
+
+//-------------------------------------------------------------------//
+// //
+// Function prototypes of utility routines found in wsutil.c //
+// //
+//-------------------------------------------------------------------//
+
+NET_API_STATUS
+BrMapStatus(
+ IN NTSTATUS NtStatus
+ );
+
+ULONG
+BrCurrentSystemTime(VOID);
+
+VOID
+BrLogEvent(
+ IN ULONG MessageId,
+ IN ULONG ErrorCode,
+ IN ULONG NumberOfSubStrings,
+ IN LPWSTR *SubStrings
+ );
+
+#if DBG
+VOID
+BrOpenTraceLogFile(
+ VOID
+ );
+
+VOID
+BrowserTrace(
+ ULONG DebugFlag,
+ PCHAR FormatString,
+ ...
+ );
+
+VOID
+BrInitializeTraceLog(
+ VOID
+ );
+
+VOID
+BrUninitializeTraceLog(
+ VOID
+ );
+
+NET_API_STATUS
+BrTruncateLog(
+ VOID
+ );
+
+#endif
+#endif // ifndef _WSUTIL_INCLUDED_
diff --git a/private/net/svcdlls/browser2/server/brwan.c b/private/net/svcdlls/browser2/server/brwan.c
new file mode 100644
index 000000000..57fad8644
--- /dev/null
+++ b/private/net/svcdlls/browser2/server/brwan.c
@@ -0,0 +1,303 @@
+
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ brwan.c
+
+Abstract:
+
+ This module contains WAN support routines used by the
+ Browser service.
+
+Author:
+
+ Larry Osterman (LarryO) 22-Nov-1992
+
+Revision History:
+
+--*/
+
+#include "precomp.h"
+#pragma hdrstop
+
+//-------------------------------------------------------------------//
+// //
+// Local function prototypes //
+// //
+//-------------------------------------------------------------------//
+
+NET_API_STATUS
+BrAddDomainEntry(
+ IN PINTERIM_SERVER_LIST InterimServerList,
+ IN LPTSTR ConfigEntry
+ );
+
+
+//-------------------------------------------------------------------//
+// //
+// Global variables //
+// //
+//-------------------------------------------------------------------//
+
+
+//-------------------------------------------------------------------//
+// //
+// Global routines //
+// //
+//-------------------------------------------------------------------//
+ NET_API_STATUS NET_API_FUNCTION
+I_BrowserrQueryOtherDomains(
+ IN BROWSER_IDENTIFY_HANDLE ServerName,
+ IN OUT LPSERVER_ENUM_STRUCT InfoStruct,
+ OUT LPDWORD TotalEntries
+ )
+
+/*++
+
+Routine Description:
+
+ This routine returns the list of "other domains" configured for this
+ machine. It is only valid on primary domain controllers. If it is called
+ on a machine that is not a PDC, it will return NERR_NotPrimary.
+
+
+Arguments:
+
+ IN BROWSER_IDENTIFY_HANDLE ServerName - Ignored.
+ IN LPSERVER_ENUM_STRUCT InfoStruct - Returns the list of other domains
+ as a SERVER_INFO_100 structure.
+ OUT LPDWORD TotalEntries - Returns the total number of other domains.
+
+Return Value:
+
+ NET_API_STATUS - The status of this request.
+
+--*/
+
+{
+ NET_API_STATUS Status;
+ PSERVER_INFO_100 ServerInfo;
+ ULONG NumberOfOtherDomains;
+
+ if (!BrInfo.IsPrimaryDomainController) {
+ return NERR_NotPrimary;
+ }
+
+ if (InfoStruct->Level != 100) {
+ return(ERROR_INVALID_LEVEL);
+ }
+
+ //
+ // Use the worker routine to do the actual work.
+ //
+
+ Status = BrQueryOtherDomains( &ServerInfo, &NumberOfOtherDomains );
+
+ if ( Status == NERR_Success ) {
+ *TotalEntries = NumberOfOtherDomains;
+
+ InfoStruct->ServerInfo.Level100->Buffer = ServerInfo;
+ InfoStruct->ServerInfo.Level100->EntriesRead = NumberOfOtherDomains;
+ }
+
+ return Status;
+
+}
+
+
+VOID
+BrWanUninitialize(
+ VOID
+ )
+{
+ return;
+
+}
+
+
+
+ NET_API_STATUS
+BrWanMasterInitialize(
+ IN PNETWORK Network
+ )
+/*++
+
+Routine Description:
+ This routine initializes the wan information for a new master.
+
+--*/
+{
+ LPTSTR PDCName = NULL;
+ LPBYTE Buffer = NULL;
+ PSERVER_INFO_100 ServerInfo;
+ NET_API_STATUS Status;
+ ULONG i;
+ ULONG EntriesRead;
+ ULONG TotalEntries;
+
+ //
+ // If we're not on the PDC, then all our initialization has been done.
+ //
+
+ if (Network->DomainInfo->IsPrimaryDomainController) {
+ return NERR_Success;
+ }
+
+ Status = NetGetDCName(NULL, NULL, (LPBYTE *)&PDCName);
+
+ //
+ // It is not an error to not be able to contact the PDC.
+ //
+
+ if (Status != NERR_Success) {
+ return NERR_Success;
+ }
+
+ Status = I_BrowserQueryOtherDomains(PDCName, &Buffer, &EntriesRead, &TotalEntries);
+
+ //
+ // We don't need the PDC name any more.
+ //
+
+ NetApiBufferFree(PDCName);
+
+ PDCName = NULL;
+
+ //
+ // It is also not an error to fail to query the other domains from the PDC.
+ //
+
+ if (Status != NERR_Success) {
+ return NERR_Success;
+ }
+
+ if (!LOCK_NETWORK(Network)) {
+ return NERR_InternalError;
+ }
+
+ try {
+ PLIST_ENTRY Entry;
+ PLIST_ENTRY NextEntry;
+
+ //
+ // Scan the other domains list and turn on the active bit for each
+ // other domain.
+ //
+
+ for (Entry = Network->OtherDomainsList.Flink;
+ Entry != &Network->OtherDomainsList ;
+ Entry = Entry->Flink) {
+ PNET_OTHER_DOMAIN OtherDomain = CONTAINING_RECORD(Entry, NET_OTHER_DOMAIN, Next);
+
+ OtherDomain->Flags |= OTHERDOMAIN_INVALID;
+ }
+
+ ServerInfo = (PSERVER_INFO_100)Buffer;
+
+ for (i = 0; i < EntriesRead; i++ ) {
+
+ //
+ // Add this as an other domain.
+ //
+ for (Entry = Network->OtherDomainsList.Flink;
+ Entry != &Network->OtherDomainsList ;
+ Entry = Entry->Flink) {
+ PNET_OTHER_DOMAIN OtherDomain = CONTAINING_RECORD(Entry, NET_OTHER_DOMAIN, Next);
+
+ //
+ // If this name is in the other domains list, it's not invalid
+ // and we should flag that we've seen the domain name.
+ //
+
+ if (!_wcsicmp(OtherDomain->Name, ServerInfo->sv100_name)) {
+ OtherDomain->Flags &= ~OTHERDOMAIN_INVALID;
+ ServerInfo->sv100_name = NULL;
+ }
+ }
+
+ ServerInfo ++;
+ }
+
+ //
+ // Scan the other domains list and remove any domains that are
+ // still marked as invalid.
+ //
+
+ for (Entry = Network->OtherDomainsList.Flink;
+ Entry != &Network->OtherDomainsList ;
+ Entry = NextEntry) {
+ PNET_OTHER_DOMAIN OtherDomain = CONTAINING_RECORD(Entry, NET_OTHER_DOMAIN, Next);
+
+ if (OtherDomain->Flags & OTHERDOMAIN_INVALID) {
+ NextEntry = Entry->Flink;
+
+ //
+ // Remove this entry from the list.
+ //
+
+ RemoveEntryList(Entry);
+
+ BrRemoveOtherDomain(Network, OtherDomain->Name);
+
+ MIDL_user_free(OtherDomain);
+
+ } else {
+ NextEntry = Entry->Flink;
+ }
+ }
+
+ //
+ // Now scan the domain list from the PDC and add any entries that
+ // weren't there already.
+ //
+
+ ServerInfo = (PSERVER_INFO_100)Buffer;
+
+ for (i = 0; i < EntriesRead; i++ ) {
+
+ if (ServerInfo->sv100_name != NULL) {
+ PNET_OTHER_DOMAIN OtherDomain = MIDL_user_allocate(sizeof(NET_OTHER_DOMAIN));
+
+ if (OtherDomain != NULL) {
+
+ Status = BrAddOtherDomain(Network, ServerInfo->sv100_name);
+
+ //
+ // If we were able to add the other domain, add it to our
+ // internal structure.
+ //
+
+ if (Status == NERR_Success) {
+ wcscpy(OtherDomain->Name, ServerInfo->sv100_name);
+ OtherDomain->Flags = 0;
+ InsertHeadList(&Network->OtherDomainsList, &OtherDomain->Next);
+ } else {
+ LPWSTR SubString[1];
+
+ SubString[0] = ServerInfo->sv100_name;
+
+ BrLogEvent(EVENT_BROWSER_OTHERDOMAIN_ADD_FAILED, Status, 1, SubString);
+ }
+ }
+ }
+
+ ServerInfo ++;
+ }
+
+
+
+
+ } finally {
+ UNLOCK_NETWORK(Network);
+
+ if (Buffer != NULL) {
+ MIDL_user_free(Buffer);
+ }
+
+ }
+ return NERR_Success;
+
+}
diff --git a/private/net/svcdlls/browser2/server/brwan.h b/private/net/svcdlls/browser2/server/brwan.h
new file mode 100644
index 000000000..44736f9a9
--- /dev/null
+++ b/private/net/svcdlls/browser2/server/brwan.h
@@ -0,0 +1,58 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ brwan.h
+
+Abstract:
+
+ This module contains definitions for WAN support routines used by the
+ Browser service.
+
+Author:
+
+ Larry Osterman (LarryO) 22-Nov-1992
+
+Revision History:
+
+--*/
+
+#ifndef _BRWAN_
+#define _BRWAN_
+
+#define BROWSER_CONFIG_FILE_SECTION_SIZE 8192
+#define BROWSER_DOMAINS_CONFIG_FILE_NAME TEXT("DOMAINS.INI")
+#define BROWSER_DOMAINS_CONFIG_FILE_SECTION TEXT("Domains")
+
+
+NET_API_STATUS NET_API_FUNCTION
+I_BrowserrQueryOtherDomains(
+ IN BROWSER_IDENTIFY_HANDLE ServerName,
+ IN OUT LPSERVER_ENUM_STRUCT InfoStruct,
+ OUT LPDWORD TotalEntries
+ );
+
+NET_API_STATUS NET_API_FUNCTION
+I_BrowserrQueryPreConfiguredDomains(
+ IN BROWSER_IDENTIFY_HANDLE ServerName,
+ IN OUT LPSERVER_ENUM_STRUCT InfoStruct,
+ OUT LPDWORD TotalEntries
+ );
+
+NET_API_STATUS
+BrWanMasterInitialize(
+ IN PNETWORK Network
+ );
+
+extern
+INTERIM_SERVER_LIST
+BrPreConfiguredInterimServerList;
+
+VOID
+BrWanUninitialize(
+ VOID
+ );
+
+#endif // _BRWAN_
diff --git a/private/net/svcdlls/browser2/server/brwins.c b/private/net/svcdlls/browser2/server/brwins.c
new file mode 100644
index 000000000..ed98d8135
--- /dev/null
+++ b/private/net/svcdlls/browser2/server/brwins.c
@@ -0,0 +1,484 @@
+/*++
+
+Copyright (c) 1991-1992 Microsoft Corporation
+
+Module Name:
+
+ brwins.c
+
+Abstract:
+
+ This module contains the routines to interface with the WINS name server.
+
+Author:
+
+ Larry Osterman
+
+Revision History:
+
+--*/
+
+#include "precomp.h"
+#pragma hdrstop
+
+//
+// Addresses of procedures in winsrpc.dll
+//
+
+DWORD (__RPC_API *BrWinsGetBrowserNames)( PWINSINTF_BIND_DATA_T, PWINSINTF_BROWSER_NAMES_T);
+VOID (__RPC_API *BrWinsFreeMem)(LPVOID);
+CHAR BrWinsScopeId[256];
+
+ NET_API_STATUS
+BrOpenNetwork (
+ IN PUNICODE_STRING NetworkName,
+ OUT PHANDLE NetworkHandle
+ )
+/*++
+
+Routine Description:
+
+ This routine opens the NT LAN Man Datagram Receiver driver.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NTSTATUS ntstatus;
+
+ IO_STATUS_BLOCK IoStatusBlock;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+
+ //
+ // Open the transport device directly.
+ //
+ InitializeObjectAttributes(
+ &ObjectAttributes,
+ NetworkName,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL
+ );
+
+ ntstatus = NtOpenFile(
+ NetworkHandle,
+ SYNCHRONIZE,
+ &ObjectAttributes,
+ &IoStatusBlock,
+ 0,
+ 0
+ );
+
+ if (NT_SUCCESS(ntstatus)) {
+ ntstatus = IoStatusBlock.Status;
+ }
+
+ if (! NT_SUCCESS(ntstatus)) {
+ KdPrint(("NtOpenFile network driver failed: 0x%08lx\n",
+ ntstatus));
+ }
+
+ return NetpNtStatusToApiStatus(ntstatus);
+}
+
+ NET_API_STATUS
+BrGetWinsServerName(
+ IN PUNICODE_STRING NetworkName,
+ OUT LPWSTR *PrimaryWinsServerAddress,
+ OUT LPWSTR *SecondaryWinsServerAddress
+ )
+{
+ NET_API_STATUS status;
+ HANDLE netHandle;
+ tWINS_ADDRESSES winsAddresses;
+ DWORD bytesReturned;
+ PCHAR p;
+ DWORD count;
+
+ status = BrOpenNetwork(NetworkName, &netHandle);
+
+ if (status != NERR_Success) {
+ return status;
+ }
+
+ if (!DeviceIoControl(netHandle,
+ IOCTL_NETBT_GET_WINS_ADDR,
+ NULL, 0,
+ &winsAddresses, sizeof(winsAddresses),
+ &bytesReturned, NULL)) {
+ status = GetLastError();
+
+ CloseHandle(netHandle);
+ return status;
+ }
+
+ CloseHandle(netHandle);
+
+ *PrimaryWinsServerAddress = MIDL_user_allocate((3+1+3+1+3+1+3+1) * sizeof(TCHAR));
+
+ if (*PrimaryWinsServerAddress == NULL) {
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ p = (PCHAR)&winsAddresses.PrimaryWinsServer;
+
+ count = swprintf(*PrimaryWinsServerAddress, L"%d.%d.%d.%d", p[3] & 0xff, p[2] & 0xff, p[1] & 0xff, p[0] & 0xff);
+
+ ASSERT (count < 3 + 1 + 3 + 1 + 3 + 1 + 3 + 1);
+
+ *SecondaryWinsServerAddress = MIDL_user_allocate((3+1+3+1+3+1+3+1) * sizeof(TCHAR));
+
+ if (*SecondaryWinsServerAddress == NULL) {
+ MIDL_user_free(*PrimaryWinsServerAddress);
+
+ *PrimaryWinsServerAddress = NULL;
+
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ p = (PCHAR)&winsAddresses.BackupWinsServer;
+
+ count = swprintf(*SecondaryWinsServerAddress, L"%d.%d.%d.%d", p[3] & 0xff, p[2] & 0xff, p[1] & 0xff, p[0] & 0xff);
+
+ ASSERT (count < 3 + 1 + 3 + 1 + 3 + 1 + 3 + 1);
+
+ return NERR_Success;
+}
+
+
+
+
+VOID
+BrWinsGetScopeId(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ This code was stolen from the nbtstat command.
+
+ This procedure save the netbt scope id in the global variable BrWinsScopeId.
+ On any error, a NULL scope ID will be used.
+
+Arguments:
+
+
+Return Value:
+
+ 0 if successful, -1 otherwise.
+
+--*/
+
+{
+ DWORD WinStatus;
+
+ HKEY Key;
+ DWORD BufferSize;
+ DWORD Type;
+
+
+
+ //
+ // Open the registry key containing the scope id.
+ //
+ WinStatus = RegOpenKeyExA(
+ HKEY_LOCAL_MACHINE,
+ "system\\currentcontrolset\\services\\netbt\\parameters",
+ 0,
+ KEY_READ,
+ &Key);
+
+ if ( WinStatus != ERROR_SUCCESS) {
+ *BrWinsScopeId = '\0';
+ return;
+ }
+
+
+ //
+ // Read the scope id value.
+ //
+ BufferSize = sizeof(BrWinsScopeId)-1;
+
+ WinStatus = RegQueryValueExA(
+ Key,
+ "ScopeId",
+ NULL,
+ &Type,
+ (LPBYTE) &BrWinsScopeId[1],
+ &BufferSize );
+
+ (VOID) RegCloseKey( Key );
+
+ if ( WinStatus != ERROR_SUCCESS) {
+ *BrWinsScopeId = '\0';
+ return;
+ }
+
+ //
+ // If there is no scope id (just a zero byte),
+ // just return an empty string.
+ // otherise
+ // return a '.' in front of the scope id.
+ //
+ // This matches what WINS returns from WinsGetBrowserNames.
+ //
+
+ if ( BufferSize == 0 || BrWinsScopeId[1] == '\0' ) {
+ *BrWinsScopeId = '\0';
+ } else {
+ *BrWinsScopeId = '.';
+ }
+
+ return;
+
+}
+
+DWORD
+BrLoadWinsrpcDll(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This routine loads the WinsRpc DLL and locates all the procedures the browser calls
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ Status of the operation
+
+--*/
+{
+ DWORD WinStatus;
+ HANDLE hModule;
+
+ //
+ // If the library is already loaded,
+ // just return.
+ //
+
+ if (BrWinsGetBrowserNames != NULL) {
+ return NERR_Success;
+ }
+
+ //
+ // Load the library.
+ //
+
+ hModule = LoadLibraryA("winsrpc");
+
+ if (NULL == hModule) {
+ WinStatus = GetLastError();
+ return WinStatus;
+ }
+
+ //
+ // Locate all of the procedures needed.
+ //
+
+ BrWinsGetBrowserNames =
+ (DWORD (__RPC_API *)( PWINSINTF_BIND_DATA_T, PWINSINTF_BROWSER_NAMES_T))
+ GetProcAddress( hModule, "WinsGetBrowserNames" );
+
+ if (BrWinsGetBrowserNames == NULL) {
+ WinStatus = GetLastError();
+ FreeLibrary( hModule );
+ return WinStatus;
+ }
+
+
+ BrWinsFreeMem =
+ (VOID (__RPC_API *)(LPVOID))
+ GetProcAddress( hModule, "WinsFreeMem" );
+
+ if (BrWinsFreeMem == NULL) {
+ WinStatus = GetLastError();
+ FreeLibrary( hModule );
+ return WinStatus;
+ }
+
+ //
+ // Initialize BrWinsScopeId
+ //
+
+ BrWinsGetScopeId();
+
+ return NERR_Success;
+}
+
+ NET_API_STATUS
+BrQuerySpecificWinsServer(
+ IN LPWSTR WinsServerAddress,
+ OUT PVOID *WinsServerList,
+ OUT PDWORD EntriesInList,
+ OUT PDWORD TotalEntriesInList
+ )
+{
+ WINSINTF_BIND_DATA_T bindData;
+ NET_API_STATUS status;
+ PVOID winsDomainInformation = NULL;
+ PSERVER_INFO_101 serverInfo;
+ WINSINTF_BROWSER_NAMES_T names;
+ DWORD i,j;
+ LPWSTR serverInfoEnd;
+ DWORD bufferSize;
+
+ //
+ // Load winsrpc.dll
+ //
+
+ status = BrLoadWinsrpcDll();
+
+ if (status != NERR_Success) {
+ return status;
+ }
+
+ //
+ // Get the list of domain names from WINS
+ //
+
+ bindData.fTcpIp = TRUE;
+ bindData.pServerAdd = (LPSTR)WinsServerAddress;
+ names.pInfo = NULL;
+
+ status = (*BrWinsGetBrowserNames)(&bindData, &names);
+
+ if ( status != NERR_Success ) {
+ return status;
+ }
+
+
+ //
+ // Convert the WINS domain list into server list format.
+ //
+ bufferSize = (sizeof(SERVER_INFO_101) + ((CNLEN + 1 + 1) *sizeof(WCHAR))) * names.EntriesRead;
+
+ (*WinsServerList) = winsDomainInformation = MIDL_user_allocate( bufferSize );
+
+ if (winsDomainInformation == NULL) {
+ (*BrWinsFreeMem)(names.pInfo);
+
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ serverInfo = winsDomainInformation;
+ serverInfoEnd = (LPWSTR)((PCHAR)winsDomainInformation + bufferSize);
+
+ *TotalEntriesInList = names.EntriesRead;
+ *EntriesInList = 0;
+
+ for (i = 0; i < names.EntriesRead ; i += 1) {
+ OEM_STRING OemString;
+ UNICODE_STRING UnicodeString;
+ CHAR WinsName[CNLEN+1];
+
+ //
+ // Make up information about this domain.
+ //
+ serverInfo->sv101_platform_id = PLATFORM_ID_NT;
+ serverInfo->sv101_version_major = 0;
+ serverInfo->sv101_version_minor = 0;
+ serverInfo->sv101_type = SV_TYPE_DOMAIN_ENUM | SV_TYPE_NT;
+
+ //
+ // Filter out those entries whose scope id doesn't match ours
+ //
+
+ if ( lstrlenA(names.pInfo[i].pName) >= NETBIOS_NAME_LEN ) {
+ if ( lstrcmpA( &names.pInfo[i].pName[NETBIOS_NAME_LEN], BrWinsScopeId) != 0 ) {
+ continue;
+ }
+ }
+
+
+
+ //
+ // Truncate the 0x1b and spaces from the domain name.
+ //
+ lstrcpynA(WinsName, names.pInfo[i].pName, sizeof(WinsName) );
+ WinsName[CNLEN] = '\0';
+
+ for (j = CNLEN-1 ; j ; j -= 1 ) {
+ if (WinsName[j] != ' ') {
+ WinsName[j+1] = '\0';
+ break;
+ }
+ }
+
+ RtlInitString(&OemString, WinsName);
+
+ status = RtlOemStringToUnicodeString(&UnicodeString, &OemString, TRUE);
+
+ if (!NT_SUCCESS(status)) {
+
+ //
+ // Ignore bogus entries
+ //
+ continue;
+ }
+
+ serverInfo->sv101_name = UnicodeString.Buffer;
+
+ if (NetpPackString(&serverInfo->sv101_name,
+ (PCHAR )(&serverInfo+1),
+ &serverInfoEnd)) {
+
+ serverInfo->sv101_comment = L"";
+
+ if (NetpPackString(&serverInfo->sv101_comment,
+ (PCHAR )(&serverInfo+1),
+ &serverInfoEnd)) {
+ *EntriesInList += 1;
+ }
+
+ }
+ RtlFreeUnicodeString(&UnicodeString);
+
+ serverInfo += 1;
+
+ }
+
+ (*BrWinsFreeMem)(names.pInfo);
+
+ return NERR_Success;
+}
+
+
+ NET_API_STATUS
+BrQueryWinsServer(
+ IN LPWSTR PrimaryWinsServerAddress,
+ IN LPWSTR SecondaryWinsServerAddress,
+ OUT PVOID WinsServerList,
+ OUT PDWORD EntriesInList,
+ OUT PDWORD TotalEntriesInList
+ )
+{
+ NET_API_STATUS status;
+ status = BrQuerySpecificWinsServer(PrimaryWinsServerAddress,
+ WinsServerList,
+ EntriesInList,
+ TotalEntriesInList);
+
+ if (status == NERR_Success) {
+ return status;
+ }
+
+ status = BrQuerySpecificWinsServer(SecondaryWinsServerAddress,
+ WinsServerList,
+ EntriesInList,
+ TotalEntriesInList);
+
+ return status;
+}
diff --git a/private/net/svcdlls/browser2/server/brwins.h b/private/net/svcdlls/browser2/server/brwins.h
new file mode 100644
index 000000000..39d95666d
--- /dev/null
+++ b/private/net/svcdlls/browser2/server/brwins.h
@@ -0,0 +1,46 @@
+/*++
+
+Copyright (c) 1991-1992 Microsoft Corporation
+
+Module Name:
+
+ brwins.c
+
+Abstract:
+
+ This module contains the routines to interface with the WINS name server.
+
+Author:
+
+ Larry Osterman
+
+Revision History:
+
+--*/
+#ifndef _BRWINS_
+#define _BRWINS_
+
+NET_API_STATUS
+BrGetWinsServerName(
+ IN PUNICODE_STRING Network,
+ OUT LPTSTR *PrimaryWinsServerAddress,
+ OUT LPTSTR *SecondaryWinsServerAddress
+ );
+
+NET_API_STATUS
+BrQueryWinsServer(
+ IN LPTSTR PrimaryWinsServerAddress,
+ IN LPTSTR SecondaryWinsServerAddress,
+ OUT PVOID WinsServerList,
+ OUT PDWORD EntriesInList,
+ OUT PDWORD TotalEntriesInList
+ );
+
+NET_API_STATUS
+BrQuerySpecificWinsServer(
+ IN LPTSTR WinsServerAddress,
+ OUT PVOID *WinsServerList,
+ OUT PDWORD EntriesInList,
+ OUT PDWORD TotalEntriesInList
+ );
+#endif
diff --git a/private/net/svcdlls/browser2/server/makefile b/private/net/svcdlls/browser2/server/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/net/svcdlls/browser2/server/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/net/svcdlls/browser2/server/makefile.inc b/private/net/svcdlls/browser2/server/makefile.inc
new file mode 100644
index 000000000..38ed50291
--- /dev/null
+++ b/private/net/svcdlls/browser2/server/makefile.inc
@@ -0,0 +1,10 @@
+.SUFFIXES: .mdl
+
+.mdl.c:
+ type << > $(@B).c
+#include "precomp.h"
+#pragma hdrstop
+<<
+ type $(<R).mdl >> $(@B).c
+
+bowser_s.c: bowser_s.mdl
diff --git a/private/net/svcdlls/browser2/server/precomp.h b/private/net/svcdlls/browser2/server/precomp.h
new file mode 100644
index 000000000..f5a71b477
--- /dev/null
+++ b/private/net/svcdlls/browser2/server/precomp.h
@@ -0,0 +1,38 @@
+#include "br.h"
+#include <string.h>
+#include "bowser.h"
+#include <stdlib.h>
+#include <ctype.h>
+#include "brdevice.h"
+#include "brconfig.h"
+#include "brwins.h"
+#include <lmaccess.h>
+#include <ntlsa.h>
+#include "config.h"
+#include "confname.h"
+#include <winreg.h>
+#include <netevent.h>
+#include "brutil.h"
+#include "hostannc.h"
+#include "tstring.h"
+#include "brsec.h"
+#include "services.h"
+#include "srvann.h"
+#include "names.h"
+#include "browsdom.h"
+#include "netlib.h"
+#include <time.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <lmshare.h>
+#include <lmapibuf.h>
+#include <lmserver.h>
+#include "xstypes.h"
+#include "xsprocsp.h"
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windows.h>
+#include <nb30.h>
+#include <winsintf.h>
+#include <nbtioctl.h>
diff --git a/private/net/svcdlls/browser2/server/sources b/private/net/svcdlls/browser2/server/sources
new file mode 100644
index 000000000..ea6f6f255
--- /dev/null
+++ b/private/net/svcdlls/browser2/server/sources
@@ -0,0 +1,85 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+
+Author:
+
+ Steve Wood (stevewo) 12-Apr-1990
+
+NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl
+
+!ENDIF
+
+DS_BUILD=1
+
+MAJORCOMP=net
+MINORCOMP=browser
+
+TARGETNAME=browser
+TARGETPATH=$(BASEDIR)\public\sdk\lib2
+TARGETTYPE=DYNLINK
+
+TARGETLIBS= \
+ ..\common\obj\*\brcommon.lib \
+ $(BASEDIR)\public\sdk\lib\*\kernel32.lib \
+ $(BASEDIR)\public\sdk\lib\*\netapi32.lib \
+ $(BASEDIR)\public\sdk\lib\*\rpcutil.lib \
+ $(BASEDIR)\public\sdk\lib\*\rpcndr.lib \
+ $(BASEDIR)\public\sdk\lib\*\rpcrt4.lib \
+ $(BASEDIR)\public\sdk\lib\*\advapi32.lib \
+ $(BASEDIR)\Public\Sdk\Lib\*\xactsrv.lib \
+ $(BASEDIR)\Public\Sdk\Lib\*\wsock32.lib \
+
+
+
+INCLUDES=.;..;..\..\..\inc;..\..\..\..\inc;..\..\..\api;..\..\..\xactsrv
+
+NTPROFILEINPUT=1
+
+!IFNDEF DISABLE_NET_UNICODE
+UNICODE=1
+NET_C_DEFINES=-DUNICODE
+!ENDIF
+
+USE_CRTDLL=1
+
+SOURCES= bowsvc.rc \
+ brmain.c \
+ browser.c \
+ brdevice.c \
+ brdomain.c \
+ brutil.c \
+ brconfig.c \
+ brdmmstr.c \
+ brmaster.c \
+ browsnet.c \
+ browsdom.c \
+ brwan.c \
+ brwins.c \
+ bowqueue.c \
+ srvenum.c \
+ bowser_s.c
+
+MSC_WARNING_LEVEL=/W3 /WX
+
+UMTYPE=windows
+
+UMLIBS=
+
+PRECOMPILED_INCLUDE=precomp.h
+PRECOMPILED_PCH=precomp.pch
+PRECOMPILED_OBJ=precomp.obj
+
+NTTARGETFILE0=bowser_s.c
diff --git a/private/net/svcdlls/browser2/server/srvenum.c b/private/net/svcdlls/browser2/server/srvenum.c
new file mode 100644
index 000000000..bb0bca957
--- /dev/null
+++ b/private/net/svcdlls/browser2/server/srvenum.c
@@ -0,0 +1,2422 @@
+/*++
+
+Copyright (c) 1991-1992 Microsoft Corporation
+
+Module Name:
+
+ srvenum.c
+
+Abstract:
+
+ This module contains the worker routine for the NetServerEnum API
+ implemented by the Workstation service.
+
+Author:
+
+ Rita Wong (ritaw) 25-Mar-1991
+
+Revision History:
+
+--*/
+
+#include "precomp.h"
+#pragma hdrstop
+
+NET_API_STATUS NET_API_FUNCTION
+BrNetServerEnum(
+ IN PNETWORK Network OPTIONAL,
+ IN LPCWSTR ClientName OPTIONAL,
+ IN ULONG Level,
+ IN DWORD PreferedMaximumLength,
+ OUT PVOID *Buffer,
+ OUT LPDWORD EntriesRead,
+ OUT LPDWORD TotalEntries,
+ IN DWORD ServerType,
+ IN LPCWSTR Domain,
+ IN LPCWSTR FirstNameToReturn OPTIONAL
+ );
+
+NET_API_STATUS
+BrRetrieveServerListForMaster(
+ IN PNETWORK Network,
+ IN OUT PVOID *Buffer,
+ OUT PDWORD EntriesRead,
+ OUT PDWORD TotalEntries,
+ IN DWORD Level,
+ IN DWORD ServerType,
+ IN DWORD PreferedMaximumLength,
+ IN BOOLEAN LocalListOnly,
+ IN LPTSTR ClientName,
+ IN LPTSTR DomainName,
+ IN LPCWSTR FirstNameToReturn
+ );
+
+NET_API_STATUS
+BrRetrieveServerListForBackup(
+ IN PNETWORK Network,
+ IN OUT PVOID *Buffer,
+ OUT PDWORD EntriesRead,
+ OUT PDWORD TotalEntries,
+ IN DWORD Level,
+ IN DWORD ServerType,
+ IN DWORD PreferedMaximumLength,
+ IN LPCWSTR FirstNameToReturn
+ );
+
+NET_API_STATUS NET_API_FUNCTION
+I_BrowserrServerEnum(
+ IN LPTSTR ServerName OPTIONAL,
+ IN LPTSTR TransportName OPTIONAL,
+ IN LPTSTR ClientName OPTIONAL,
+ IN OUT LPSERVER_ENUM_STRUCT InfoStruct,
+ IN DWORD PreferedMaximumLength,
+ OUT LPDWORD TotalEntries,
+ IN DWORD ServerType,
+ IN LPTSTR Domain,
+ IN OUT LPDWORD ResumeHandle OPTIONAL
+ )
+/*++
+
+Routine Description:
+
+ This function is the NetServerEnum entry point in the Workstation service.
+
+Arguments:
+
+ ServerName - Supplies the name of server to execute this function
+
+ TransportName - Supplies the name of xport on which to enumerate servers
+
+ InfoStruct - This structure supplies the level of information requested,
+ returns a pointer to the buffer allocated by the Workstation service
+ which contains a sequence of information structure of the specified
+ information level, and returns the number of entries read. The buffer
+ pointer is set to NULL if return code is not NERR_Success or
+ ERROR_MORE_DATA, or if EntriesRead returned is 0. The EntriesRead
+ value is only valid if the return code is NERR_Success or
+ ERROR_MORE_DATA.
+
+ PreferedMaximumLength - Supplies the number of bytes of information
+ to return in the buffer. If this value is MAXULONG, we will try
+ to return all available information if there is enough memory
+ resource.
+
+ TotalEntries - Returns the total number of entries available. This value
+ is returned only if the return code is NERR_Success or ERROR_MORE_DATA.
+
+ ServerType - Supplies the type of server to enumerate.
+
+ Domain - Supplies the name of one of the active domains to enumerate the
+ servers from. If NULL, servers from the primary domain, logon domain
+ and other domains are enumerated.
+
+ ResumeHandle - Supplies and returns the point to continue with enumeration.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NET_API_STATUS NetStatus;
+
+ NetStatus = I_BrowserrServerEnumEx(
+ ServerName,
+ TransportName,
+ ClientName,
+ InfoStruct,
+ PreferedMaximumLength,
+ TotalEntries,
+ ServerType,
+ Domain,
+ NULL ); // NULL FirstNameToReturn
+
+ if (ARGUMENT_PRESENT(ResumeHandle)) {
+ *ResumeHandle = 0;
+ }
+
+ return NetStatus;
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+I_BrowserrServerEnumEx(
+ IN LPTSTR ServerName OPTIONAL,
+ IN LPTSTR TransportName OPTIONAL,
+ IN LPTSTR ClientName OPTIONAL,
+ IN OUT LPSERVER_ENUM_STRUCT InfoStruct,
+ IN DWORD PreferedMaximumLength,
+ OUT LPDWORD TotalEntries,
+ IN DWORD ServerType,
+ IN LPTSTR Domain,
+ IN LPTSTR FirstNameToReturnArg
+ )
+/*++
+
+Routine Description:
+
+ This function is the NetServerEnum entry point in the Workstation service.
+
+Arguments:
+
+ ServerName - Supplies the name of server to execute this function
+
+ TransportName - Supplies the name of xport on which to enumerate servers
+
+ InfoStruct - This structure supplies the level of information requested,
+ returns a pointer to the buffer allocated by the Workstation service
+ which contains a sequence of information structure of the specified
+ information level, and returns the number of entries read. The buffer
+ pointer is set to NULL if return code is not NERR_Success or
+ ERROR_MORE_DATA, or if EntriesRead returned is 0. The EntriesRead
+ value is only valid if the return code is NERR_Success or
+ ERROR_MORE_DATA.
+
+ PreferedMaximumLength - Supplies the number of bytes of information
+ to return in the buffer. If this value is MAXULONG, we will try
+ to return all available information if there is enough memory
+ resource.
+
+ TotalEntries - Returns the total number of entries available. This value
+ is returned only if the return code is NERR_Success or ERROR_MORE_DATA.
+
+ ServerType - Supplies the type of server to enumerate.
+
+ Domain - Supplies the name of one of the active domains to enumerate the
+ servers from. If NULL, servers from the primary domain, logon domain
+ and other domains are enumerated.
+
+ FirstNameToReturnArg - Supplies the name of the first domain or server entry to return.
+ The caller can use this parameter to implement a resume handle of sorts by passing
+ the name of the last entry returned on a previous call. (Notice that the specified
+ entry will, also, be returned on this call unless it has since been deleted.)
+ Pass NULL to start with the first entry available.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NET_API_STATUS status;
+ PVOID Buffer = NULL;
+ ULONG EntriesRead;
+ BOOLEAN NetworkLocked = FALSE;
+ PNETWORK Network = NULL;
+ UNICODE_STRING NetworkName;
+ WCHAR FirstNameToReturn[DNLEN+1];
+ PDOMAIN_INFO DomainInfo = NULL;
+#if DBG
+ DWORD StartTickCount, EndTickCount;
+#endif
+
+ UNREFERENCED_PARAMETER(ServerName);
+
+#if DBG
+ StartTickCount = GetTickCount();
+#endif
+
+ if (!ARGUMENT_PRESENT(TransportName)) {
+ status = ERROR_INVALID_PARAMETER;
+ goto Cleanup;
+ }
+
+ //
+ // Find the requested domain.
+ //
+
+ DomainInfo = BrFindDomain( Domain, TRUE );
+
+ if ( DomainInfo == NULL) {
+ status = ERROR_NO_SUCH_DOMAIN;
+ goto Cleanup;
+ }
+
+ //
+ // Find the requested network
+ //
+
+ RtlInitUnicodeString(&NetworkName, TransportName);
+
+ Network = BrFindNetwork( DomainInfo, &NetworkName);
+
+ if (Network == NULL) {
+ BrPrint(( BR_CRITICAL,
+ "%ws: %ws: BrowserrServerEnum: Network not found.\n",
+ Domain,
+ TransportName));
+ status = ERROR_FILE_NOT_FOUND;
+ goto Cleanup;
+ }
+
+ //
+ // If the caller has asked us to use the alternate transport,
+ // do so.
+ //
+
+ if ((ServerType != SV_TYPE_ALL) &&
+ (ServerType & SV_TYPE_ALTERNATE_XPORT) ) {
+
+ //
+ // If this transport has an alternate network, then actually
+ // query the alternate name, not the real one.
+ //
+
+ if (Network->AlternateNetwork != NULL) {
+ Network = Network->AlternateNetwork;
+ } else {
+ status = ERROR_INVALID_PARAMETER;
+ goto Cleanup;
+ }
+
+ ServerType &= ~SV_TYPE_ALTERNATE_XPORT;
+
+ if (ServerType == 0) {
+ ServerType = SV_TYPE_ALL;
+ }
+
+ }
+
+ if (!LOCK_NETWORK_SHARED(Network)) {
+ status = NERR_InternalError;
+ goto Cleanup;
+ }
+
+ NetworkLocked = TRUE;
+
+ if (!(Network->Role & (ROLE_BACKUP | ROLE_MASTER))) {
+ BrPrint(( BR_CRITICAL,
+ "%ws: %ws: Browse request received from %ws, but not backup or master\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ Network->NetworkName.Buffer,
+ ClientName));
+
+ status = ERROR_REQ_NOT_ACCEP;
+ goto Cleanup;
+ }
+
+ //
+ // Canonicalize the FirstNameToReturn.
+ //
+
+ if (ARGUMENT_PRESENT(FirstNameToReturnArg) && *FirstNameToReturnArg != L'\0') {
+
+ if ( I_NetNameCanonicalize(
+ NULL,
+ (LPWSTR) FirstNameToReturnArg,
+ FirstNameToReturn,
+ sizeof(FirstNameToReturn),
+ NAMETYPE_WORKGROUP,
+ LM2X_COMPATIBLE
+ ) != NERR_Success) {
+ status = ERROR_INVALID_PARAMETER;
+ goto Cleanup;
+ }
+
+ } else {
+ FirstNameToReturn[0] = L'\0';
+ }
+
+ status = BrNetServerEnum(Network,
+ ClientName,
+ InfoStruct->Level,
+ PreferedMaximumLength,
+ &Buffer,
+ &EntriesRead,
+ TotalEntries,
+ ServerType,
+ Domain,
+ FirstNameToReturn );
+
+ //
+ // Return output parameters other than output buffer from request packet.
+ //
+
+ if (status == NERR_Success || status == ERROR_MORE_DATA) {
+
+ if (InfoStruct->Level == 101) {
+ InfoStruct->ServerInfo.Level101->Buffer = (PSERVER_INFO_101) Buffer;
+ InfoStruct->ServerInfo.Level101->EntriesRead = EntriesRead;
+ } else {
+ InfoStruct->ServerInfo.Level100->Buffer = (PSERVER_INFO_100) Buffer;
+ InfoStruct->ServerInfo.Level100->EntriesRead = EntriesRead;
+ }
+
+ }
+
+Cleanup:
+ if (NetworkLocked) {
+ UNLOCK_NETWORK(Network);
+ }
+
+ if ( Network != NULL ) {
+
+#if DBG
+ EndTickCount = GetTickCount();
+
+ BrPrint(( BR_SERVER_ENUM,
+ "%ws: %ws: Browse request for %ws took %ld milliseconds\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ Network->NetworkName.Buffer,
+ ClientName,
+ EndTickCount - StartTickCount));
+#endif
+ BrDereferenceNetwork( Network );
+ }
+
+ if ( DomainInfo != NULL ) {
+ BrDereferenceDomain( DomainInfo );
+ }
+
+ return status;
+
+}
+
+ WORD
+I_BrowserServerEnumForXactsrv(
+ IN LPCWSTR TransportName OPTIONAL,
+ IN LPCWSTR ClientName OPTIONAL,
+
+ IN ULONG Level,
+ IN USHORT ClientLevel,
+
+ IN PVOID ClientBuffer,
+ IN WORD BufferLength,
+ IN DWORD PreferedMaximumLength,
+
+ OUT LPDWORD EntriesFilled,
+ OUT LPDWORD TotalEntries,
+
+ IN DWORD ServerType,
+ IN LPCWSTR Domain,
+ IN LPCWSTR FirstNameToReturnArg OPTIONAL,
+
+ OUT PWORD Converter
+ )
+/*++
+
+Routine Description:
+
+ This function is a private entrypoint for Xactsrv that bypasses RPC
+ entirely.
+
+Arguments:
+
+ TransportName - Supplies the name of xport on which to enumerate servers
+
+ ClientName - Supplies the name of the client that requested the data
+
+ Level - Level of data requested.
+ ClientLevel - Level requested by the client.
+
+ ClientBuffer - Output buffer allocated to hold the buffer.
+ BufferLength - Size of ClientBuffer
+ PreferedMaximumLength - Prefered maximum size of Client buffer if we are
+ going to use the NT form of the buffer
+
+ OUT LPDWORD EntriesFilled - The entries packed into ClientBuffer
+ OUT LPDWORD TotalEntries - The total # of entries available.
+
+ IN DWORD ServerType - Server type mask.
+ IN LPTSTR Domain - Domain to query
+
+ OUT PWORD Converter - Magic constant from Xactsrv that allows the client
+ to convert the response buffer.
+
+Return Value:
+
+ WORD - NERR_Success or reason for failure.
+
+--*/
+{
+ NET_API_STATUS status;
+ BOOLEAN networkLocked = FALSE;
+ PNETWORK network = NULL;
+ UNICODE_STRING networkName;
+ PDOMAIN_INFO DomainInfo = NULL;
+ PVOID buffer = NULL;
+ DWORD entriesRead;
+ PCACHED_BROWSE_RESPONSE response = NULL;
+ WCHAR FirstNameToReturn[DNLEN+1];
+
+#if DBG
+ DWORD startTickCount, endTickCount;
+
+ startTickCount = GetTickCount();
+
+#endif
+
+ //
+ // If the browser isn't up and we get one of these calls, fail the API
+ // immediately.
+ //
+
+ if (BrGlobalData.Status.dwCurrentState != SERVICE_RUNNING) {
+ BrPrint(( BR_CRITICAL,
+ "Browse request from %ws received, but browser not running\n", ClientName));
+ status = NERR_ServiceNotInstalled;
+ goto Cleanup;
+ }
+
+ if (!ARGUMENT_PRESENT(TransportName)) {
+ status = ERROR_INVALID_PARAMETER;
+ goto Cleanup;
+ }
+
+ //
+ // Find the requested domain.
+ //
+
+ DomainInfo = BrFindDomain( (LPWSTR) Domain, TRUE );
+
+ if ( DomainInfo == NULL) {
+ status = ERROR_NO_SUCH_DOMAIN;
+ goto Cleanup;
+ }
+
+ //
+ // Look up the transport.
+ //
+ RtlInitUnicodeString(&networkName, TransportName);
+
+ BrPrint(( BR_SERVER_ENUM,
+ "%ws: %ws: NetServerEnum: Look up network for %ws\n",
+ Domain,
+ TransportName,
+ ClientName));
+ network = BrFindNetwork( DomainInfo, &networkName);
+
+ if (network == NULL) {
+ BrPrint(( BR_CRITICAL,
+ "%ws: %ws: Network not found.\n",
+ Domain,
+ TransportName));
+ status = ERROR_FILE_NOT_FOUND;
+ goto Cleanup;
+ }
+
+
+ //
+ // If the caller has asked us to use the alternate transport,
+ // do so.
+ //
+
+ if ((ServerType != SV_TYPE_ALL) &&
+ (ServerType & SV_TYPE_ALTERNATE_XPORT) ) {
+
+ //
+ // If this transport has an alternate network, then actually
+ // query the alternate name, not the real one.
+ //
+
+ if (network->AlternateNetwork != NULL) {
+ PNETWORK TempNetwork = network;
+ network = network->AlternateNetwork;
+ if ( !BrReferenceNetwork( network )) {
+ network = TempNetwork;
+ status = ERROR_INVALID_PARAMETER;
+ goto Cleanup;
+ }
+ BrDereferenceNetwork( TempNetwork );
+ } else {
+ status = ERROR_INVALID_PARAMETER;
+ goto Cleanup;
+ }
+
+ ServerType &= ~SV_TYPE_ALTERNATE_XPORT;
+
+ if (ServerType == 0) {
+ ServerType = SV_TYPE_ALL;
+ }
+
+ }
+
+ if (!LOCK_NETWORK_SHARED(network)) {
+ status = NERR_InternalError;
+ goto Cleanup;
+ }
+
+ networkLocked = TRUE;
+
+ BrPrint(( BR_SERVER_ENUM,
+ "%ws: %ws: Network found for %ws\n",
+ network->DomainInfo->DomUnicodeDomainName,
+ network->NetworkName.Buffer,
+ ClientName));
+
+ if (!(network->Role & (ROLE_BACKUP | ROLE_MASTER))) {
+
+ BrPrint(( BR_CRITICAL,
+ "%ws: %ws: Browse request received from %ws, but not backup or master\n",
+ network->DomainInfo->DomUnicodeDomainName,
+ network->NetworkName.Buffer,
+ ClientName));
+
+ status = ERROR_REQ_NOT_ACCEP;
+ goto Cleanup;
+ }
+
+ //
+ // If we weren't able to find a cached response buffer, or
+ // if the cached response didn't have a buffer allocated for it,
+ // we need to process the browse request.
+ //
+
+ if (network->Role & ROLE_MASTER) {
+
+ //
+ // Check to see if we should flush the cache at this time. If
+ // we should, we need to release the network and re-acquire it
+ // exclusively, because we use the network lock to protect
+ // enumerations from flushes.
+ //
+
+
+ if ((BrCurrentSystemTime() - network->TimeCacheFlushed) > BrInfo.DriverQueryFrequency) {
+
+ UNLOCK_NETWORK(network);
+
+ networkLocked = FALSE;
+
+ if (!LOCK_NETWORK(network)) {
+ status = NERR_InternalError;
+ goto Cleanup;
+ }
+
+ networkLocked = TRUE;
+
+ //
+ // We're running on a master browser, and we found a cached browse
+ // request. See if we can safely use this cached value, or if we
+ // should go to the driver for it.
+ //
+
+ EnterCriticalSection(&network->ResponseCacheLock);
+
+ if ((BrCurrentSystemTime() - network->TimeCacheFlushed) > BrInfo.DriverQueryFrequency) {
+
+ BrPrint(( BR_SERVER_ENUM,
+ "%ws: %ws: Flushing cache\n",
+ network->DomainInfo->DomUnicodeDomainName,
+ network->NetworkName.Buffer ));
+
+ network->TimeCacheFlushed = BrCurrentSystemTime();
+
+ BrAgeResponseCache( network );
+ }
+
+ LeaveCriticalSection(&network->ResponseCacheLock);
+ }
+ }
+
+ //
+ // Canonicalize the FirstNameToReturn.
+ //
+
+ if (ARGUMENT_PRESENT(FirstNameToReturnArg) && *FirstNameToReturnArg != L'\0') {
+
+ if ( I_NetNameCanonicalize(
+ NULL,
+ (LPWSTR) FirstNameToReturnArg,
+ FirstNameToReturn,
+ sizeof(FirstNameToReturn),
+ NAMETYPE_WORKGROUP,
+ LM2X_COMPATIBLE
+ ) != NERR_Success) {
+ status = ERROR_INVALID_PARAMETER;
+ goto Cleanup;
+ }
+
+ } else {
+ FirstNameToReturn[0] = L'\0';
+ }
+
+ if (!ARGUMENT_PRESENT(Domain) ||
+ !STRICMP(Domain, network->DomainInfo->DomUnicodeDomainName)) {
+
+ BrPrint(( BR_SERVER_ENUM,
+ "%ws: %ws: Look up 0x%x/%d/%x.\n",
+ Domain, TransportName, ServerType, ClientLevel, BufferLength));
+
+ //
+ // This request is for our primary domain. Look up a cached response
+ // entry. If non is found for this request, allocate a new one and
+ // return it.
+ //
+
+ response = BrLookupAndAllocateCachedEntry(
+ network,
+ ServerType,
+ BufferLength,
+ ClientLevel,
+ FirstNameToReturn
+ );
+
+
+ }
+
+ EnterCriticalSection(&network->ResponseCacheLock);
+ if ((response == NULL)
+
+ ||
+
+ (response->Buffer == NULL)) {
+ LeaveCriticalSection(&network->ResponseCacheLock);
+
+ BrPrint(( BR_SERVER_ENUM,
+ "%ws: %ws: Cached entry not found, or hit count too low. Retrieve actual list for %ws\n",
+ Domain, TransportName, ClientName));
+
+ status = BrNetServerEnum(network,
+ ClientName,
+ Level,
+ PreferedMaximumLength,
+ &buffer,
+ &entriesRead,
+ TotalEntries,
+ ServerType,
+ Domain,
+ FirstNameToReturn
+ );
+
+ if (status == NERR_Success || status == ERROR_MORE_DATA) {
+
+ BrPrint(( BR_SERVER_ENUM,
+ "%ws: %ws: Convert NT buffer to Xactsrv buffer for %ws\n",
+ Domain,
+ TransportName,
+ ClientName ));
+
+ status = XsConvertServerEnumBuffer(
+ buffer,
+ entriesRead,
+ TotalEntries,
+ ClientLevel,
+ ClientBuffer,
+ BufferLength,
+ EntriesFilled,
+ Converter);
+
+
+ if (status == NERR_Success || status == ERROR_MORE_DATA) {
+
+ BrPrint(( BR_SERVER_ENUM,
+ "%ws: %ws: Conversion done for %ws\n",
+ Domain,
+ TransportName,
+ ClientName ));
+
+ EnterCriticalSection(&network->ResponseCacheLock);
+
+ if ((response != NULL) &&
+
+ (response->Buffer == NULL) &&
+
+ (response->TotalHitCount >= BrInfo.CacheHitLimit)) {
+
+ BrPrint(( BR_SERVER_ENUM,
+ "%ws: %ws: Save contents of server list for 0x%x/%d/%x.\n",
+ Domain,
+ TransportName,
+ ServerType, ClientLevel, BufferLength));
+
+ response->Buffer = MIDL_user_allocate(BufferLength);
+
+ if ( response->Buffer != NULL ) {
+
+ //
+ // We successfully allocated the buffer, now copy
+ // our response into the buffer and save away the
+ // other useful stuff about the request.
+ //
+
+ RtlCopyMemory(response->Buffer, ClientBuffer, BufferLength);
+
+ response->EntriesRead = *EntriesFilled;
+ response->TotalEntries = *TotalEntries;
+ response->Converter = *Converter;
+ response->Status = (WORD)status;
+ }
+ }
+
+ LeaveCriticalSection(&network->ResponseCacheLock);
+ }
+ }
+ } else {
+
+ ASSERT (response);
+
+ ASSERT (response->Buffer);
+
+ ASSERT (response->Size == BufferLength);
+
+ BrPrint(( BR_SERVER_ENUM,
+ "Cache hit. Use contents of server list for 0x%x/%d/%x.\n",
+ Domain,
+ TransportName,
+ ServerType, ClientLevel, BufferLength));
+
+ //
+ // Copy over the cached response from the response cache into the
+ // users response buffer.
+ //
+
+ RtlCopyMemory(ClientBuffer, response->Buffer, BufferLength);
+
+ //
+ // Also copy over the other useful stuff that the client will
+ // want to know about.
+ //
+
+ *EntriesFilled = response->EntriesRead;
+
+ *TotalEntries = response->TotalEntries;
+
+ *Converter = response->Converter;
+
+ status = response->Status;
+ LeaveCriticalSection(&network->ResponseCacheLock);
+
+ }
+
+Cleanup:
+ if (networkLocked) {
+ UNLOCK_NETWORK(network);
+ }
+
+ //
+ // If a buffer was allocated, free it.
+ //
+
+ if (buffer != NULL) {
+ MIDL_user_free(buffer);
+ }
+
+
+ if ( network != NULL ) {
+#if DBG
+ endTickCount = GetTickCount();
+
+ BrPrint(( BR_SERVER_ENUM,
+ "%ws: %ws: Browse request for %ws took %ld milliseconds\n",
+ network->DomainInfo->DomUnicodeDomainName,
+ network->NetworkName.Buffer,
+ ClientName,
+ endTickCount - startTickCount));
+#endif
+ BrDereferenceNetwork( network );
+ }
+
+ if ( DomainInfo != NULL ) {
+ BrDereferenceDomain( DomainInfo );
+ }
+
+ return (WORD)status;
+}
+
+
+
+ NET_API_STATUS NET_API_FUNCTION
+BrNetServerEnum(
+ IN PNETWORK Network OPTIONAL,
+ IN LPCWSTR ClientName OPTIONAL,
+ IN ULONG Level,
+ IN DWORD PreferedMaximumLength,
+ OUT PVOID *Buffer,
+ OUT LPDWORD EntriesRead,
+ OUT LPDWORD TotalEntries,
+ IN DWORD ServerType,
+ IN LPCWSTR Domain,
+ IN LPCWSTR FirstNameToReturn OPTIONAL
+ )
+/*++
+
+Routine Description:
+
+ This function is the real worker for the NetServerEnum entry point in the
+ Workstation service.
+
+Arguments:
+
+ ServerName - Supplies the name of server to execute this function
+
+ TransportName - Supplies the name of xport on which to enumerate servers
+
+ InfoStruct - This structure supplies the level of information requested,
+ returns a pointer to the buffer allocated by the Workstation service
+ which contains a sequence of information structure of the specified
+ information level, and returns the number of entries read. The buffer
+ pointer is set to NULL if return code is not NERR_Success or
+ ERROR_MORE_DATA, or if EntriesRead returned is 0. The EntriesRead
+ value is only valid if the return code is NERR_Success or
+ ERROR_MORE_DATA.
+
+ PreferedMaximumLength - Supplies the number of bytes of information
+ to return in the buffer. If this value is MAXULONG, we will try
+ to return all available information if there is enough memory
+ resource.
+
+ TotalEntries - Returns the total number of entries available. This value
+ is returned only if the return code is NERR_Success or ERROR_MORE_DATA.
+
+ ServerType - Supplies the type of server to enumerate.
+
+ Domain - Supplies the name of one of the active domains to enumerate the
+ servers from. If NULL, servers from the primary domain, logon domain
+ and other domains are enumerated.
+
+ FirstNameToReturn - Supplies the name of the first server or domain to return
+ to the caller. If NULL, enumeration begins with the very first entry.
+
+ Passed name must be the canonical form of the name.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NET_API_STATUS status;
+ DWORD DomainNameSize = 0;
+ TCHAR DomainName[DNLEN + 1];
+ BOOLEAN NetworkLocked = TRUE;
+ BOOLEAN LocalListOnly = FALSE;
+ PVOID DoubleHopLocalList = NULL;
+
+ BrPrint(( BR_SERVER_ENUM,
+ "%ws: %ws: Retrieve browse list for %lx for client %ws\n",
+ Domain,
+ Network->NetworkName.Buffer,
+ ServerType,
+ ClientName));
+
+ EnterCriticalSection(&BrowserStatisticsLock);
+
+ if (ServerType == SV_TYPE_DOMAIN_ENUM) {
+ NumberOfDomainEnumerations += 1;
+ } else if (ServerType == SV_TYPE_ALL) {
+ NumberOfServerEnumerations += 1;
+ } else {
+ NumberOfOtherEnumerations += 1;
+ }
+
+ LeaveCriticalSection(&BrowserStatisticsLock);
+
+ //
+ // Only levels 100 and 101 are valid
+ //
+
+ if ((Level != 100) && (Level != 101)) {
+ return ERROR_INVALID_LEVEL;
+ }
+
+ if (ARGUMENT_PRESENT(Domain)) {
+
+ //
+ // NAMETYPE_WORKGROUP allows spaces.
+ //
+
+ if ( I_NetNameCanonicalize(
+ NULL,
+ (LPWSTR) Domain,
+ DomainName,
+ (DNLEN + 1) * sizeof(TCHAR),
+ NAMETYPE_WORKGROUP,
+ 0
+ ) != NERR_Success) {
+ return ERROR_INVALID_DOMAINNAME;
+ }
+
+ DomainNameSize = STRLEN(DomainName) * sizeof(WCHAR);
+ }
+
+ try {
+
+ if (ServerType != SV_TYPE_ALL) {
+
+ //
+ // If the user has specified SV_TYPE_LOCAL_LIST_ONLY, we
+ // will only accept SV_TYPE_DOMAIN_ENUM or 0 as legal bits
+ // otherwise, we return an error.
+ //
+
+ if (ServerType & SV_TYPE_LOCAL_LIST_ONLY) {
+
+ LocalListOnly = TRUE;
+
+// BrPrint(( BR_SERVER_ENUM, "Retrieve local list for %ws\n", ClientName));
+
+
+ //
+ // Turn off the LOCAL_LIST_ONLY bit.
+ //
+
+ ServerType &= ~SV_TYPE_LOCAL_LIST_ONLY;
+
+ //
+ // Check the remaining bits. The only two legal values
+ // are SV_TYPE_DOMAIN_ENUM and 0
+ //
+
+ if (ServerType != SV_TYPE_DOMAIN_ENUM) {
+
+ if (ServerType == 0) {
+ ServerType = SV_TYPE_ALL;
+ } else {
+ try_return(status = ERROR_INVALID_FUNCTION);
+ }
+ }
+
+ //
+ // If we aren't a master browser, blow this request
+ // off, since we don't have a local list.
+ //
+
+ if (!(Network->Role & ROLE_MASTER)) {
+ BrPrint(( BR_CRITICAL,
+ "%ws: %ws: Local list request received from %ws, but not master\n",
+ Domain,
+ Network->NetworkName.Buffer,
+ ClientName));
+ try_return(status = ERROR_REQ_NOT_ACCEP);
+ }
+
+ } else if (ServerType & SV_TYPE_DOMAIN_ENUM) {
+ if (ServerType != SV_TYPE_DOMAIN_ENUM) {
+ try_return(status = ERROR_INVALID_FUNCTION);
+ }
+ }
+ }
+
+ if (ARGUMENT_PRESENT(Domain) &&
+ STRICMP(Domain, Network->DomainInfo->DomUnicodeDomainName)) {
+ PINTERIM_ELEMENT DomainEntry;
+ LPWSTR MasterName = NULL;
+
+ BrPrint(( BR_SERVER_ENUM,
+ "Non local domain %ws - Check for double hop\n",
+ Domain));
+
+ if ( Network->Role & ROLE_MASTER ) {
+ if ( Network->DomainList.EntriesRead != 0 ) {
+
+ DomainEntry = LookupInterimServerList(&Network->DomainList, (LPWSTR) Domain);
+
+ if (DomainEntry != NULL) {
+ MasterName = DomainEntry->Comment;
+ }
+ } else {
+ ULONG i;
+ PSERVER_INFO_101 DomainInfo;
+ DWORD DoubleHopEntriesRead;
+ DWORD DoubleHopTotalEntries;
+
+ status = BrGetLocalBrowseList(Network,
+ NULL,
+ 101,
+ SV_TYPE_DOMAIN_ENUM,
+ &DoubleHopLocalList,
+ &DoubleHopEntriesRead,
+ &DoubleHopTotalEntries);
+
+ for (i = 0 , DomainInfo = DoubleHopLocalList ;
+
+ i < DoubleHopEntriesRead ;
+
+ i += 1) {
+
+ if (!_wcsicmp(Domain, DomainInfo->sv101_name)) {
+ MasterName = DomainInfo->sv101_comment;
+ break;
+ }
+
+ DomainInfo += 1;
+ }
+ }
+
+ } else {
+ ULONG i;
+ PSERVER_INFO_101 DomainInfo;
+
+ //
+ // We're running on a backup browser. We want to find the
+ // name of the master browser by looking in the backup
+ // server list for this network.
+ //
+
+ for (i = 0 , DomainInfo = Network->BackupDomainList ;
+
+ i < Network->TotalBackupDomainListEntries ;
+
+ i += 1) {
+
+ if (!_wcsicmp(Domain, DomainInfo->sv101_name)) {
+ MasterName = DomainInfo->sv101_comment;
+ break;
+ }
+
+ DomainInfo += 1;
+ }
+
+ }
+
+ //
+ // If we couldn't find a master name, bail out right now.
+ //
+
+ if (MasterName == NULL || *MasterName == UNICODE_NULL) {
+ try_return(status = ERROR_NO_BROWSER_SERVERS_FOUND);
+ }
+
+ //
+ // If the master for this domain isn't listed as our
+ // current machine, remote the API to that server.
+ //
+
+ if (STRICMP(MasterName, Network->DomainInfo->DomUnicodeComputerName)) {
+ WCHAR RemoteComputerName[UNLEN+1];
+
+ ASSERT (NetworkLocked);
+
+ UNLOCK_NETWORK(Network);
+
+ NetworkLocked = FALSE;
+
+ //
+ // Build the name of the remote computer.
+ //
+
+ STRCPY(RemoteComputerName, TEXT("\\\\"));
+
+ STRCAT(RemoteComputerName, MasterName);
+
+ BrPrint(( BR_SERVER_ENUM,
+ "Double hop to %ws on %ws\n",
+ RemoteComputerName,
+ Network->NetworkName.Buffer));
+
+ status = RxNetServerEnum(RemoteComputerName,
+ Network->NetworkName.Buffer,
+ Level,
+ (LPBYTE *)Buffer,
+ PreferedMaximumLength,
+ EntriesRead,
+ TotalEntries,
+ ServerType,
+ Domain,
+ FirstNameToReturn );
+ BrPrint(( BR_SERVER_ENUM, "Double hop done\n"));
+
+ if (!LOCK_NETWORK_SHARED (Network)) {
+ try_return(status = NERR_InternalError);
+ }
+
+ NetworkLocked = TRUE;
+
+ try_return(status);
+ }
+ }
+
+ ASSERT (NetworkLocked);
+
+ if (!ARGUMENT_PRESENT(Domain)) {
+ STRCPY(DomainName, Network->DomainInfo->DomUnicodeDomainName);
+ }
+
+ //
+ // If we are running on the master browser, we want to retrieve the
+ // local list, merge it with our interim server list, and
+ // return that to the user.
+ //
+
+ if (Network->Role & ROLE_MASTER) {
+ status = BrRetrieveServerListForMaster(Network,
+ Buffer,
+ EntriesRead,
+ TotalEntries,
+ Level,
+ ServerType,
+ PreferedMaximumLength,
+ LocalListOnly,
+ (LPWSTR) ClientName,
+ DomainName,
+ FirstNameToReturn );
+
+
+
+ } else {
+
+ status = BrRetrieveServerListForBackup(Network,
+ Buffer,
+ EntriesRead,
+ TotalEntries,
+ Level,
+ ServerType,
+ PreferedMaximumLength,
+ FirstNameToReturn );
+
+
+ }
+
+ try_return(status);
+
+try_exit:NOTHING;
+ } finally {
+
+#if DBG
+ if (status == NERR_Success || status == ERROR_MORE_DATA) {
+ BrPrint(( BR_SERVER_ENUM,
+ "%ws: %ws: Returning Browse list for %lx with %ld entries for %ws\n",
+ Domain,
+ Network->NetworkName.Buffer,
+ ServerType,
+ *EntriesRead,
+ ClientName));
+ } else {
+ BrPrint(( BR_CRITICAL,
+ "%ws: %ws: Failing I_BrowserServerEnum: %ld for client %ws\n",
+ Domain,
+ Network->NetworkName.Buffer,
+ status, ClientName));
+ }
+#endif
+
+ //
+ // If we used the local driver's list to retrieve the list of
+ // domains, free up the buffer.
+ //
+
+ if (DoubleHopLocalList != NULL) {
+ MIDL_user_free(DoubleHopLocalList);
+
+ }
+
+ ASSERT (NetworkLocked);
+ }
+
+ return status;
+}
+
+
+VOID
+TrimServerList(
+ IN DWORD Level,
+ IN OUT LPBYTE *Buffer,
+ IN OUT LPDWORD EntriesRead,
+ IN OUT LPDWORD TotalEntries,
+ IN LPCWSTR FirstNameToReturn
+)
+
+/*++
+
+Routine Description:
+
+ This routine trims any name from Buffer that's less than FirstNameToReturn.
+
+ The routine is implemented by moving the fixed part of the kept entries to the beginning
+ of the allocated buffer. Thay way, the pointer contained in the entries need not be
+ relocated and the original buffer can still be used.
+
+Arguments:
+
+ Level - Level of information in the buffer.
+
+ Buffer - Pointer to address of buffer to trim.
+ Returns null (and deallocates the buffer) if all the entries are trimmed.
+
+ EntriesRead - Pointer to number of entries in the buffer.
+ Returns the number of entries remaining in the buffer after triming.
+
+ TotalEntries - Pointer to number of entries available from server.
+ Returns a number reduced by the number of trimmed entries.
+
+ FirstNameToReturn - Supplies the name of the first server or domain to return
+ to the caller. If NULL, enumeration begins with the very first entry.
+
+ Passed name must be the canonical form of the name.
+
+Return Value:
+
+ Returns the error code for the operation.
+
+--*/
+
+{
+ DWORD EntryCount;
+ DWORD EntryNumber;
+ DWORD FixedSize;
+
+ LPBYTE CurrentEntry;
+
+ //
+ // Early out if there's nothing to do.
+ //
+
+ if ( FirstNameToReturn == NULL || *FirstNameToReturn == L'\0' || *EntriesRead == 0 ) {
+ return;
+ }
+
+
+ //
+ // Compute the size of each entry.
+ //
+
+ switch (Level) {
+ case 100:
+ FixedSize = sizeof(SERVER_INFO_100);
+ break;
+ case 101:
+ FixedSize = sizeof(SERVER_INFO_101);
+ break;
+ default:
+ NetpAssert( FALSE );
+ return;
+
+ }
+
+ //
+ // Finding the first entry to return
+ //
+
+ EntryCount = *EntriesRead;
+
+ for ( EntryNumber=0; EntryNumber< EntryCount; EntryNumber++ ) {
+
+ LPSERVER_INFO_100 ServerEntry =
+ (LPSERVER_INFO_100)( *Buffer + FixedSize * EntryNumber);
+
+ //
+ // Found the first entry to return.
+ //
+
+ if ( STRCMP( ServerEntry->sv100_name, FirstNameToReturn ) >= 0 ) {
+
+ //
+ // If we're returning the whole list,
+ // simply return.
+ //
+
+ if ( ServerEntry == (LPSERVER_INFO_100)(*Buffer) ) {
+ return;
+ }
+
+ //
+ // Copy the remaining entries to the beginning of the buffer.
+ // (Yes, this is an overlapping copy)
+ //
+
+ RtlMoveMemory( *Buffer, ServerEntry, (*EntriesRead) * FixedSize );
+ return;
+
+ }
+
+ //
+ // Account for skipped entries.
+ //
+
+ *EntriesRead -= 1;
+ *TotalEntries -= 1;
+ }
+
+ //
+ // If no entries should be returned,
+ // deallocate the buffer.
+ //
+
+ NetApiBufferFree( *Buffer );
+ *Buffer = NULL;
+
+ ASSERT ( *EntriesRead == 0 );
+
+ return;
+
+} // TrimServerList
+
+
+ NET_API_STATUS
+BrRetrieveServerListForMaster(
+ IN PNETWORK Network,
+ IN OUT PVOID *Buffer,
+ OUT PDWORD EntriesRead,
+ OUT PDWORD TotalEntries,
+ IN DWORD Level,
+ IN DWORD ServerType,
+ IN DWORD PreferedMaximumLength,
+ IN BOOLEAN LocalListOnly,
+ IN LPTSTR ClientName,
+ IN LPTSTR DomainName,
+ IN LPCWSTR FirstNameToReturn
+ )
+{
+ BOOLEAN GetLocalList = FALSE;
+ NET_API_STATUS status;
+ BOOLEAN EarlyOut = FALSE;
+
+ //
+ // Determine if it is appropriate that we should early out after retrieving
+ // the list of servers (or domains) from the bowser.
+ //
+ // We will early out on the following critieria:
+ //
+ // 1) If we are requested to retrieve the local list on a Wannish xport
+ // 2) If the transport isn't a wannish transport, or
+ // 3) If this domain isn't our primary domain (in which case it's an
+ // "otherdomain" request).
+ //
+
+ if (LocalListOnly || STRICMP(DomainName, Network->DomainInfo->DomUnicodeDomainName)) {
+ EarlyOut = TRUE;
+ } else if (!(Network->Flags & NETWORK_WANNISH)) {
+
+ //
+ // This isn't a wannish transport. We need to see if we've been
+ // master long enough for the driver to have retrieved a reasonable
+ // list.
+ //
+ // The server and domain table will be emptied when the first master
+ // browser timer is run. This will happen 15 minutes after we
+ // become the master, so we will use our services list for the
+ // first 15 minutes the browser is master.
+ //
+
+ if (ServerType == SV_TYPE_DOMAIN_ENUM) {
+ if (Network->DomainList.EntriesRead == 0) {
+ EarlyOut = TRUE;
+ }
+ } else {
+ if (Network->BrowseTable.EntriesRead == 0) {
+ EarlyOut = TRUE;
+ }
+ }
+
+ }
+
+ if (EarlyOut) {
+
+ GetLocalList = TRUE;
+
+ } else if (ServerType == SV_TYPE_ALL) {
+
+ if ((BrCurrentSystemTime() - Network->LastBowserServerQueried) > BrInfo.DriverQueryFrequency ) {
+
+ GetLocalList = TRUE;
+ }
+
+ } else if (ServerType == SV_TYPE_DOMAIN_ENUM) {
+
+ if ((BrCurrentSystemTime() - Network->LastBowserDomainQueried) > BrInfo.DriverQueryFrequency ) {
+
+ GetLocalList = TRUE;
+ }
+
+ } else {
+
+ GetLocalList = TRUE;
+ }
+
+ if (GetLocalList && !EarlyOut) {
+
+ //
+ // If we're retriving the list from the driver, and can't early out
+ // this request, this means that we will be merging this server list
+ // with an interim server list. This means that we will be modifying
+ // the network structure, and thus that we need to re-lock the network
+ // structure.
+ //
+
+ UNLOCK_NETWORK(Network);
+
+ if (!LOCK_NETWORK(Network)) {
+ return NERR_InternalError;
+ }
+
+ //
+ // Now re-test to see if another thread came in and retrieved the
+ // list from the driver while we had the network unlocked. If so,
+ // we don't want to get the local list any more.
+ //
+ // Otherwise, we want to update the last query time.
+ //
+
+ if (ServerType == SV_TYPE_ALL) {
+
+ if ((BrCurrentSystemTime() - Network->LastBowserServerQueried) > BrInfo.DriverQueryFrequency ) {
+ Network->LastBowserServerQueried = BrCurrentSystemTime();
+ } else {
+ GetLocalList = FALSE;
+ }
+
+ } else if (ServerType == SV_TYPE_DOMAIN_ENUM) {
+
+ if ((BrCurrentSystemTime() - Network->LastBowserDomainQueried) > BrInfo.DriverQueryFrequency ) {
+ Network->LastBowserDomainQueried = BrCurrentSystemTime();
+ } else {
+ GetLocalList = FALSE;
+ }
+
+ } else {
+
+ Network->LastBowserServerQueried = BrCurrentSystemTime();
+
+ }
+
+ }
+
+ //
+ // If we're supposed to retrieve the local server list, retrieve it.
+ //
+
+ if (GetLocalList) {
+ DWORD ServerTypeForLocalList;
+
+ //
+ // Calculate the server type to use when retrieving the list from
+ // the driver.
+ //
+
+ if (LocalListOnly ||
+
+ (ServerType == SV_TYPE_DOMAIN_ENUM) ||
+
+ !(Network->Flags & NETWORK_WANNISH)) {
+
+ //
+ // If we are retrieving the local list, or this is a non-wannish
+ // transport, or we are asking for domains, we want to
+ // keep the callers server type.
+ //
+
+ ServerTypeForLocalList = ServerType;
+
+ } else {
+
+ //
+ // This must be a wannish transport, ask for all servers (we know
+ // that we're not asking for domains, since we checked that
+ // above).
+ //
+ // We ask for all the servers because it's possible that a servers
+ // role has changed.
+ //
+
+ ASSERT (Network->Flags & NETWORK_WANNISH);
+
+ ServerTypeForLocalList = SV_TYPE_ALL;
+ }
+
+ BrPrint(( BR_SERVER_ENUM,
+ "%ws: %ws: Get local browse list for %ws\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ Network->NetworkName.Buffer,
+ ClientName));
+
+ status = BrGetLocalBrowseList(
+ Network,
+ DomainName,
+ ( EarlyOut ? Level : 101 ),
+ ServerTypeForLocalList,
+ Buffer,
+ EntriesRead,
+ TotalEntries);
+
+// BrPrint(( BR_SERVER_ENUM, "List retrieved. %ld entries, %ld total\n", *EntriesRead, *TotalEntries));
+
+
+ //
+ // If we're supposed to early-out this request (or if there was
+ // an error), do so now.
+ //
+
+ if (EarlyOut ||
+ (status != NERR_Success && status != ERROR_MORE_DATA)) {
+
+ //
+ // If we're returning an early out list,
+ // truncate the complete list returned from the kernel.
+ //
+ // This saves us from having to modify the kernel interface and untangle
+ // the code above.
+ //
+
+ if ( status == NERR_Success || status == ERROR_MORE_DATA ) {
+
+ TrimServerList( Level,
+ (LPBYTE *)Buffer,
+ EntriesRead,
+ TotalEntries,
+ FirstNameToReturn );
+
+ }
+
+ BrPrint(( BR_SERVER_ENUM, "Early out for %ws with %ld servers. Don't merge server list.\n", ClientName, *EntriesRead));
+
+ return status;
+ }
+
+ if (status == NERR_Success || status == ERROR_MORE_DATA) {
+
+ if (*EntriesRead != 0) {
+
+ //
+ // Merge the local list with the list we got from the
+ // master or from the domain master.
+ //
+
+ BrPrint(( BR_SERVER_ENUM,
+ "%ws: %ws: Merge %d entries in server list for %ws \n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ Network->NetworkName.Buffer,
+ *EntriesRead,
+ ClientName));
+
+ status = MergeServerList((ServerType == SV_TYPE_DOMAIN_ENUM ?
+ &Network->DomainList :
+ &Network->BrowseTable),
+ 101,
+ *Buffer,
+ *EntriesRead,
+ *TotalEntries
+ );
+ }
+ }
+ }
+
+ //
+ // We've merged the local list into the appropriate interim table,
+ // now free it up.
+ //
+
+ if (*EntriesRead != 0) {
+ MIDL_user_free(*Buffer);
+ }
+
+ status = PackServerList((ServerType == SV_TYPE_DOMAIN_ENUM ?
+ &Network->DomainList :
+ &Network->BrowseTable),
+ Level,
+ ServerType,
+ PreferedMaximumLength,
+ Buffer,
+ EntriesRead,
+ TotalEntries,
+ FirstNameToReturn
+ );
+ return status;
+}
+
+ NET_API_STATUS
+BrRetrieveServerListForBackup(
+ IN PNETWORK Network,
+ IN OUT PVOID *Buffer,
+ OUT PDWORD EntriesRead,
+ OUT PDWORD TotalEntries,
+ IN DWORD Level,
+ IN DWORD ServerType,
+ IN DWORD PreferedMaximumLength,
+ IN LPCWSTR FirstNameToReturn
+ )
+{
+ PSERVER_INFO_101 ServerList, ClientServerInfo;
+ ULONG EntriesInList;
+ ULONG TotalEntriesInList;
+ ULONG EntrySize;
+ ULONG BufferSize;
+ LPTSTR BufferEnd;
+ BOOLEAN ReturnWholeList = FALSE;
+ BOOLEAN TrimmingNames;
+
+ //
+ // If we are not running as a master, we want to use our stored
+ // server list to figure out what the client gets.
+ //
+
+ if (ServerType == SV_TYPE_DOMAIN_ENUM) {
+
+ ServerList = Network->BackupDomainList;
+
+ TotalEntriesInList = EntriesInList = Network->TotalBackupDomainListEntries;
+
+ ReturnWholeList = TRUE;
+
+ } else {
+ ServerList = Network->BackupServerList;
+
+ TotalEntriesInList = EntriesInList = Network->TotalBackupServerListEntries;
+
+ if (ServerType == SV_TYPE_ALL) {
+ ReturnWholeList = TRUE;
+ }
+ }
+
+ //
+ // Figure out the largest buffer we have to allocate to hold this
+ // server info.
+ //
+
+ if (Level == 101) {
+ if (PreferedMaximumLength == MAXULONG) {
+
+ if (ServerType == SV_TYPE_DOMAIN_ENUM) {
+ BufferSize = (sizeof(SERVER_INFO_101) + (CNLEN+1 + CNLEN+1)*sizeof(TCHAR)) * EntriesInList;
+ } else {
+ BufferSize = (sizeof(SERVER_INFO_101) + (CNLEN+1 + MAXCOMMENTSZ+1)*sizeof(TCHAR)) * EntriesInList;
+ }
+ } else {
+ BufferSize = PreferedMaximumLength;
+ }
+
+ EntrySize = sizeof(SERVER_INFO_101);
+ } else {
+ if (PreferedMaximumLength == MAXULONG) {
+ BufferSize = (sizeof(SERVER_INFO_100) + (CNLEN+1)*sizeof(TCHAR)) * EntriesInList;
+ } else {
+ BufferSize = PreferedMaximumLength;
+ }
+
+ EntrySize = sizeof(SERVER_INFO_100);
+ }
+
+ *Buffer = MIDL_user_allocate(BufferSize);
+
+ if (*Buffer == NULL) {
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ BufferEnd = (LPTSTR)((ULONG)*Buffer+BufferSize);
+
+ ClientServerInfo = *Buffer;
+
+ *TotalEntries = 0;
+
+ *EntriesRead = 0;
+
+ //
+ // While there are still entries to process....
+ //
+
+ TrimmingNames = (FirstNameToReturn != NULL && *FirstNameToReturn != L'\0');
+ while (EntriesInList) {
+
+ EntriesInList -= 1;
+
+ //
+ // If this entry is appropriate to be packed,
+ //
+
+ if ( (ServerList->sv101_type & ServerType) &&
+ (!TrimmingNames ||
+ STRCMP( ServerList->sv101_name, FirstNameToReturn ) >= 0 ) ) {
+
+ TrimmingNames = FALSE;
+
+ //
+ // Indicate one more entry in the list.
+ //
+
+ *TotalEntries += 1;
+
+ //
+ // If we can fit this entry before the buffer end,
+ // pack in the information into the buffer.
+ //
+
+ if ((ULONG)ClientServerInfo+EntrySize <= (ULONG)BufferEnd) {
+
+ //
+ // Copy over the platform ID and computer name.
+ //
+
+ ClientServerInfo->sv101_platform_id = ServerList->sv101_platform_id;
+
+ ClientServerInfo->sv101_name = ServerList->sv101_name;
+
+ if (NetpPackString(&ClientServerInfo->sv101_name,
+ (LPBYTE)((PCHAR)ClientServerInfo)+EntrySize,
+ &BufferEnd)) {
+
+ if (Level == 101) {
+
+ ClientServerInfo->sv101_version_major = ServerList->sv101_version_major;
+
+ ClientServerInfo->sv101_version_minor = ServerList->sv101_version_minor;
+
+ ClientServerInfo->sv101_type = ServerList->sv101_type;
+
+ ClientServerInfo->sv101_comment = ServerList->sv101_comment;
+
+ if (NetpPackString(&ClientServerInfo->sv101_comment,
+ (LPBYTE)((PCHAR)ClientServerInfo)+EntrySize,
+ &BufferEnd)) {
+ *EntriesRead += 1;
+ }
+ } else {
+ *EntriesRead += 1;
+ }
+ }
+
+ ClientServerInfo = (PSERVER_INFO_101)((PCHAR)ClientServerInfo+EntrySize);
+ } else {
+ //
+ // If we're returning the entire list, we can
+ // early out now, since there's no point in continuing.
+ //
+
+ if (ReturnWholeList) {
+
+ *TotalEntries = TotalEntriesInList;
+
+ break;
+ }
+ }
+
+ }
+
+ ServerList += 1;
+ }
+
+ //
+ // If we weren't able to pack all the entries into the list,
+ // return ERROR_MORE_DATA
+ //
+
+ if (*EntriesRead != *TotalEntries) {
+ return ERROR_MORE_DATA;
+ } else {
+ return NERR_Success;
+ }
+
+}
+
+
+
+ NET_API_STATUS
+I_BrowserrResetStatistics (
+ IN LPTSTR servername OPTIONAL
+ )
+{
+ NET_API_STATUS Status = NERR_Success;
+ ULONG BufferSize;
+
+ EnterCriticalSection(&BrowserStatisticsLock);
+
+ NumberOfServerEnumerations = 0;
+
+ NumberOfDomainEnumerations = 0;
+
+ NumberOfOtherEnumerations = 0;
+
+ NumberOfMissedGetBrowserListRequests = 0;
+
+ //
+ // Reset the driver's statistics as well.
+ //
+
+ if (!DeviceIoControl(BrDgReceiverDeviceHandle, IOCTL_LMDR_RESET_STATISTICS, NULL, 0, NULL, 0, &BufferSize, NULL)) {
+
+ //
+ // The API failed, return the error.
+ //
+
+ Status = GetLastError();
+ }
+
+ LeaveCriticalSection(&BrowserStatisticsLock);
+
+ return Status;
+}
+
+ NET_API_STATUS
+I_BrowserrQueryStatistics (
+ IN LPTSTR servername OPTIONAL,
+ OUT LPBROWSER_STATISTICS *Statistics
+ )
+{
+ NET_API_STATUS Status = NERR_Success;
+ BOWSER_STATISTICS BowserStatistics;
+ ULONG BufferSize;
+
+ *Statistics = MIDL_user_allocate(sizeof(BROWSER_STATISTICS));
+
+ if (*Statistics == NULL) {
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ EnterCriticalSection(&BrowserStatisticsLock);
+
+ if (!DeviceIoControl(BrDgReceiverDeviceHandle, IOCTL_LMDR_QUERY_STATISTICS, NULL, 0, &BowserStatistics, sizeof(BowserStatistics), &BufferSize, NULL)) {
+
+ //
+ // The API failed, return the error.
+ //
+
+ Status = GetLastError();
+ } else {
+
+ if (BufferSize != sizeof(BOWSER_STATISTICS)) {
+ Status = ERROR_INSUFFICIENT_BUFFER;
+ } else {
+ (*Statistics)->StatisticsStartTime = BowserStatistics.StartTime;
+
+ (*Statistics)->NumberOfServerAnnouncements = BowserStatistics.NumberOfServerAnnouncements;
+ (*Statistics)->NumberOfDomainAnnouncements = BowserStatistics.NumberOfDomainAnnouncements;
+ (*Statistics)->NumberOfElectionPackets = BowserStatistics.NumberOfElectionPackets;
+ (*Statistics)->NumberOfMailslotWrites = BowserStatistics.NumberOfMailslotWrites;
+ (*Statistics)->NumberOfGetBrowserServerListRequests = BowserStatistics.NumberOfGetBrowserServerListRequests;
+ (*Statistics)->NumberOfMissedServerAnnouncements = BowserStatistics.NumberOfMissedServerAnnouncements;
+ (*Statistics)->NumberOfMissedMailslotDatagrams = BowserStatistics.NumberOfMissedMailslotDatagrams;
+ (*Statistics)->NumberOfMissedGetBrowserServerListRequests = BowserStatistics.NumberOfMissedGetBrowserServerListRequests +
+ NumberOfMissedGetBrowserListRequests;
+ (*Statistics)->NumberOfFailedServerAnnounceAllocations = BowserStatistics.NumberOfFailedServerAnnounceAllocations;
+ (*Statistics)->NumberOfFailedMailslotAllocations = BowserStatistics.NumberOfFailedMailslotAllocations;
+ (*Statistics)->NumberOfFailedMailslotReceives = BowserStatistics.NumberOfFailedMailslotReceives;
+ (*Statistics)->NumberOfFailedMailslotWrites = BowserStatistics.NumberOfFailedMailslotWrites;
+ (*Statistics)->NumberOfFailedMailslotOpens = BowserStatistics.NumberOfFailedMailslotOpens;
+ (*Statistics)->NumberOfDuplicateMasterAnnouncements = BowserStatistics.NumberOfDuplicateMasterAnnouncements;
+ (*Statistics)->NumberOfIllegalDatagrams = BowserStatistics.NumberOfIllegalDatagrams;
+
+ //
+ // Now fill in the local statistics.
+ //
+
+ (*Statistics)->NumberOfServerEnumerations = NumberOfServerEnumerations;
+ (*Statistics)->NumberOfDomainEnumerations = NumberOfDomainEnumerations;
+ (*Statistics)->NumberOfOtherEnumerations = NumberOfOtherEnumerations;
+
+ }
+ }
+
+ LeaveCriticalSection(&BrowserStatisticsLock);
+
+ return Status;
+}
+
+
+
+//
+// Browser request response cache management logic.
+//
+
+
+
+ PCACHED_BROWSE_RESPONSE
+BrLookupAndAllocateCachedEntry(
+ IN PNETWORK Network,
+ IN DWORD ServerType,
+ IN WORD Size,
+ IN DWORD Level,
+ IN LPCWSTR FirstNameToReturn
+ )
+/*++
+
+Routine Description:
+
+ This function will look up (and allocate if appropriate) a cached
+ browse response for this browse.
+
+ Enter with the Network Locked shared or exclusive.
+
+Arguments:
+ IN PNETWORK Network - Network to allocate entry on.
+ IN DWORD ServerType - Server type bits for request.
+ IN WORD Size, - Users buffer size for request.
+ IN WORD Level - Level of request.
+
+ FirstNameToReturn - Supplies the name of the first domain or server entry to return.
+ The caller can use this parameter to implement a resume handle of sorts by passing
+ the name of the last entry returned on a previous call. (Notice that the specified
+ entry will, also, be returned on this call unless it has since been deleted.)
+
+ Passed name must be the canonical form of the name.
+
+ This entry is never NULL. It may be a pointer to an empty string to indicate
+ the enumeration starts at the beginning of the list.
+
+
+Return Value:
+
+ PCACHED_BROWSE_RESPONSE - NULL or a cached response for the request.
+
+--*/
+
+{
+ PLIST_ENTRY entry;
+ PCACHED_BROWSE_RESPONSE response;
+
+ //
+ // If we have more cached responses than we are allowed,
+ // remove the last entry from the list and free it.
+ //
+
+ if (Network->NumberOfCachedResponses > BrInfo.NumberOfCachedResponses) {
+
+ //
+ // We need to release the network and re-acquire it
+ // exclusively, because we use the network lock to protect
+ // enumerations from deletions.
+ //
+
+ UNLOCK_NETWORK(Network);
+
+ if (LOCK_NETWORK(Network)) {
+
+ EnterCriticalSection(&Network->ResponseCacheLock);
+ if (Network->NumberOfCachedResponses > BrInfo.NumberOfCachedResponses) {
+
+ PLIST_ENTRY LastEntry = RemoveTailList(&Network->ResponseCache);
+
+ response = CONTAINING_RECORD(LastEntry, CACHED_BROWSE_RESPONSE, Next);
+
+ Network->NumberOfCachedResponses -= 1;
+
+ response->Next.Flink = NULL;
+ response->Next.Blink = NULL;
+
+ //
+ // Free the last cached entry.
+ //
+
+ BrDestroyCacheEntry( response );
+
+ response = NULL;
+ }
+ LeaveCriticalSection(&Network->ResponseCacheLock);
+ }
+ }
+
+ //
+ // Search the list of responses for this one.
+ //
+
+ EnterCriticalSection(&Network->ResponseCacheLock);
+
+ for (entry = Network->ResponseCache.Flink ;
+ entry != &Network->ResponseCache ;
+ entry = entry->Flink ) {
+
+ response = CONTAINING_RECORD(entry, CACHED_BROWSE_RESPONSE, Next);
+
+ //
+ // If this response cache entry matches the incoming request,
+ // we can increment the hit count for this entry and return it.
+ //
+
+ if (response->Level == Level
+ &&
+ response->ServerType == ServerType
+ &&
+ response->Size == Size
+ &&
+ wcscmp( response->FirstNameToReturn, FirstNameToReturn ) == 0) {
+
+ //
+ // This response exactly matches the request.
+ //
+ // Bump its hit count and move it to the head of the cache.
+ //
+
+ response->HitCount += 1;
+
+ response->TotalHitCount += 1;
+
+ //
+ // Remove this entry from its current location in the list and
+ // move it to the head of the list.
+ //
+
+ RemoveEntryList(&response->Next);
+
+ InsertHeadList(&Network->ResponseCache, &response->Next);
+
+ BrPrint(( BR_SERVER_ENUM,
+ "%ws: %ws: Found cache entry 0x%x/%d/%x H:%d T:%d\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ Network->NetworkName.Buffer,
+ response->ServerType,
+ response->Level,
+ response->Size,
+ response->HitCount,
+ response->TotalHitCount ));
+
+ LeaveCriticalSection(&Network->ResponseCacheLock);
+
+ return response;
+ }
+ }
+
+ //
+ // We've walked our entire cache and have been unable to find
+ // a response that matches our request.
+ //
+ // Allocate a new response cache entry and hook it into the cache.
+ //
+
+ response = BrAllocateResponseCacheEntry(Network, ServerType, Size, Level, FirstNameToReturn );
+
+ LeaveCriticalSection(&Network->ResponseCacheLock);
+
+ return response;
+
+}
+
+ VOID
+BrAgeResponseCache(
+ IN PNETWORK Network
+ )
+/*++
+
+Routine Description:
+
+ This function will age response cache entries for a network.
+
+ We scan the response cache, and every entry that has a cached response
+ will be tossed. In addition, any entry that has had less than the
+ cache hit limit number of hits since the past scan will also be removed.
+
+Arguments:
+ IN PNETWORK Network - Network to age entries on.
+
+Return Value:
+
+ None.
+
+
+--*/
+{
+ PLIST_ENTRY entry;
+
+ EnterCriticalSection(&Network->ResponseCacheLock);
+
+ try {
+
+ for (entry = Network->ResponseCache.Flink ;
+ entry != &Network->ResponseCache ;
+ entry = entry->Flink ) {
+ PCACHED_BROWSE_RESPONSE response = CONTAINING_RECORD(entry, CACHED_BROWSE_RESPONSE, Next);
+
+ //
+ // If this response didn't have a hit count high enough during
+ // the previous run to justify keeping it around, blow it away.
+ //
+
+ if (response->HitCount < BrInfo.CacheHitLimit) {
+ response->LowHitCount += 1;
+ }
+
+ //
+ // If we have CacheHitLimit iterations of low hits, then
+ // flush the entry from the cache.
+ //
+
+ if (response->LowHitCount > BrInfo.CacheHitLimit) {
+ PLIST_ENTRY nextentry = entry->Blink;
+
+ BrPrint(( BR_SERVER_ENUM,
+ "%ws: %ws: Flush cache entry for 0x%x/%d/%x H:%d T:%d\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ Network->NetworkName.Buffer,
+ response->ServerType,
+ response->Level,
+ response->Size,
+ response->HitCount,
+ response->TotalHitCount ));
+
+ Network->NumberOfCachedResponses -= 1;
+
+ RemoveEntryList(entry);
+
+ entry->Flink = NULL;
+ entry->Blink = NULL;
+
+ BrDestroyCacheEntry(response);
+
+ entry = nextentry;
+
+ //
+ // Null out the pointer to make sure we don't use it again.
+ //
+
+ response = NULL;
+
+ } else {
+ BrPrint(( BR_SERVER_ENUM,
+ "%ws: %ws: Retain cache entry 0x%x/%d/%x H:%d T:%d\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ Network->NetworkName.Buffer,
+ response->ServerType, response->Level, response->Size, response->HitCount, response->TotalHitCount ));
+
+ //
+ // We ALWAYS blow away the response buffer during an age pass.
+ //
+
+ MIDL_user_free( response->Buffer );
+
+ response->Buffer = NULL;
+
+ //
+ // Reset the hit count for this entry for this pass.
+ //
+
+ response->HitCount = 0;
+ }
+
+ }
+
+ } finally {
+ LeaveCriticalSection(&Network->ResponseCacheLock);
+ }
+}
+
+
+ PCACHED_BROWSE_RESPONSE
+BrAllocateResponseCacheEntry(
+ IN PNETWORK Network,
+ IN DWORD ServerType,
+ IN WORD Size,
+ IN DWORD Level,
+ IN LPCWSTR FirstNameToReturn
+ )
+/*++
+
+Routine Description:
+
+ This function will allocate a new browse response cache entry.
+
+Arguments:
+ IN PNETWORK Network - Network to allocate entry on.
+ IN DWORD ServerType - Server type bits for request.
+ IN WORD Size, - Users buffer size for request.
+ IN WORD Level - Level of request.
+
+ FirstNameToReturn - FirstNameCached
+
+Return Value:
+
+ PCACHED_BROWSE_RESPONSE - NULL or a cached response for the request.
+
+NOTE: This is called with the network response cache locked.
+
+--*/
+
+{
+ PCACHED_BROWSE_RESPONSE response;
+
+ response = MIDL_user_allocate( sizeof( CACHED_BROWSE_RESPONSE ) );
+
+ if ( response == NULL ) {
+ return NULL;
+ }
+
+ //
+ // Flag the information for this response.
+ //
+
+ response->ServerType = ServerType;
+ response->Size = Size;
+ response->Level = Level;
+
+ //
+ // Initialize the other fields in the response.
+ //
+
+ response->Buffer = NULL;
+ response->HitCount = 0;
+ response->TotalHitCount = 0;
+ response->LowHitCount = 0;
+ response->Status = NERR_Success;
+ wcscpy( response->FirstNameToReturn, FirstNameToReturn );
+
+ Network->NumberOfCachedResponses += 1;
+
+ //
+ // We hook this response into the tail of the cache. We do this
+ // because we assume that this request won't be used frequently. If
+ // it is, it will move to the head of the cache naturally.
+ //
+
+ InsertTailList(&Network->ResponseCache, &response->Next);
+
+ return response;
+}
+
+ NET_API_STATUS
+BrDestroyCacheEntry(
+ IN PCACHED_BROWSE_RESPONSE CacheEntry
+ )
+/*++
+
+Routine Description:
+
+ This routine destroys an individual response cache entry.
+
+Arguments:
+ IN PCACHED_BROWSE_RESPONSE CacheEntry - Entry to destroy.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success
+
+--*/
+{
+ ASSERT (CacheEntry->Next.Flink == NULL);
+ ASSERT (CacheEntry->Next.Blink == NULL);
+
+ if (CacheEntry->Buffer != NULL) {
+ MIDL_user_free(CacheEntry->Buffer);
+ }
+
+ MIDL_user_free(CacheEntry);
+
+ return NERR_Success;
+}
+
+ NET_API_STATUS
+BrDestroyResponseCache(
+ IN PNETWORK Network
+ )
+/*++
+
+Routine Description:
+
+ This routine destroys the entire response cache for a supplied network.
+
+Arguments:
+ IN PNETWORK Network - Network to allocate entry on.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success
+
+--*/
+
+{
+ while (!IsListEmpty(&Network->ResponseCache)) {
+ PCACHED_BROWSE_RESPONSE cacheEntry;
+ PLIST_ENTRY entry = RemoveHeadList(&Network->ResponseCache);
+
+ entry->Flink = NULL;
+ entry->Blink = NULL;
+
+ cacheEntry = CONTAINING_RECORD(entry, CACHED_BROWSE_RESPONSE, Next);
+
+ Network->NumberOfCachedResponses -= 1;
+
+ BrDestroyCacheEntry(cacheEntry);
+
+ }
+
+ ASSERT (Network->NumberOfCachedResponses == 0);
+
+ return NERR_Success;
+}
+
+NET_API_STATUS
+NetrBrowserStatisticsGet (
+ IN LPTSTR servername OPTIONAL,
+ IN DWORD Level,
+ IN OUT LPBROWSER_STATISTICS_STRUCT InfoStruct
+ )
+{
+ //
+ // And return success.
+ //
+
+ return(NERR_Success);
+
+}
+
+NET_API_STATUS
+NetrBrowserStatisticsClear (
+ IN LPTSTR servername OPTIONAL
+ )
+{
+ //
+ // And return success.
+ //
+
+ return(NERR_Success);
+
+}
+
+#if DBG
+
+NET_API_STATUS
+I_BrowserrDebugCall (
+ IN LPTSTR servername OPTIONAL,
+ IN DWORD DebugCode,
+ IN DWORD OptionalValue
+ )
+{
+ NET_API_STATUS Status = STATUS_SUCCESS;
+
+ switch (DebugCode) {
+ case BROWSER_DEBUG_BREAK_POINT:
+ DbgBreakPoint();
+ break;
+
+ case BROWSER_DEBUG_DUMP_NETWORKS:
+ BrDumpNetworks();
+ break;
+ case BROWSER_DEBUG_SET_DEBUG:
+ BrInfo.BrowserDebug |= OptionalValue;
+ BrPrint(( BR_INIT, "Setting browser trace to %lx\n", BrInfo.BrowserDebug));
+ break;
+
+ case BROWSER_DEBUG_CLEAR_DEBUG:
+ BrInfo.BrowserDebug &= ~OptionalValue;
+ BrPrint(( BR_INIT, "Setting browser trace to %lx\n", BrInfo.BrowserDebug));
+ break;
+ case BROWSER_DEBUG_TRUNCATE_LOG:
+ Status = BrTruncateLog();
+ break;
+
+ default:
+ BrPrint(( BR_CRITICAL, "Unknown debug callout %lx\n", DebugCode));
+ DbgBreakPoint();
+ break;
+ }
+
+ return Status;
+
+}
+
+NET_API_STATUS
+I_BrowserrDebugTrace (
+ IN LPTSTR servername OPTIONAL,
+ IN LPSTR String
+ )
+{
+ //
+ // Stick the string parameter into the browser log.
+ //
+
+ BrowserTrace( BR_UTIL, "%s", String);
+
+ //
+ // And return success.
+ //
+
+ return(NERR_Success);
+
+}
+#else
+
+NET_API_STATUS
+I_BrowserrDebugCall (
+ IN LPTSTR servername OPTIONAL,
+ IN DWORD DebugCode,
+ IN DWORD OptionalValue
+ )
+{
+ return(ERROR_NOT_SUPPORTED);
+
+}
+NET_API_STATUS
+I_BrowserrDebugTrace (
+ IN LPTSTR servername OPTIONAL,
+ IN LPSTR String
+ )
+{
+ return(ERROR_NOT_SUPPORTED);
+
+}
+#endif
diff --git a/private/net/svcdlls/browser2/server/srvenum.h b/private/net/svcdlls/browser2/server/srvenum.h
new file mode 100644
index 000000000..2c6192866
--- /dev/null
+++ b/private/net/svcdlls/browser2/server/srvenum.h
@@ -0,0 +1,117 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ srvenum.h
+
+Abstract:
+
+ Private header file to be included by Browser service modules that need
+ to know about server enumeration routines (including the browse cache
+ modules).
+
+
+Author:
+
+ Larry Osterman (larryo) 23-Jun-1993
+
+Revision History:
+
+--*/
+
+
+#ifndef _SRVENUM_INCLUDED_
+#define _SRVENUM_INCLUDED_
+
+//
+// Cached browse response.
+//
+// The cached browse request structure is used to hold the response to
+// a NetServerEnum request.
+//
+// If a NetServerEnum request comes in through Xactsrv, the browser will
+// look up to see if there is a cached browse that matches this request,
+// and if there is, it will simply return that request to the caller.
+//
+//
+// In a nutshell, this is how the response cache works:
+//
+// The browser keeps a list of all of the browse requests that come into the
+// browser. This list is keyed by Level, ServerType, and buffer size. The
+// actual chain is protected by a CriticalSection called the
+// ResponseCacheLock. Entries in the list are protected by the global
+// network lock.
+//
+// When a browse request is received from Xactsrv, the browser looks up
+// the request in the response cache, and if it finds a matching response,
+// it increments 2 hit counters. The first hit counter indicates he number
+// of hits the request has seen since the last time the cache was aged.
+// The second indicates the total number of hits over the lifetime of the
+// browser for this response.
+//
+// If the lifetime hit count is over the configurable hit limit, the
+// browser will save a copy of the response buffer associated with the
+// request. Any and all subsequent browse requests will use this buffer
+// for their response instead of converting the response.
+//
+// When a call is made to BrAgeResponseCache, the browser will scan the
+// cache and free up all of the cached responses. It will also delete
+// any responses that have a hit count less than the hit limit.
+//
+
+typedef struct _CACHED_BROWSE_RESPONSE {
+ LIST_ENTRY Next; // Pointer to next request.
+ DWORD HitCount; // Hitcount for this cached request.
+ DWORD TotalHitCount; // Total hit count for this request.
+ DWORD LowHitCount; // Number of passes with a low hit count.
+ DWORD ServerType; // Server type.
+ DWORD Level; // Level of request
+ WORD Size; // Request size
+ WORD Converter; // Converter (used by client to get strings right).
+
+ PVOID Buffer; // Response buffer.
+ DWORD EntriesRead; // Number of entries in cached list
+ DWORD TotalEntries; // Total # of entries available.
+ WORD Status; // Status of request.
+ WCHAR FirstNameToReturn[CNLEN+1]; // Name of first entry in buffer
+} CACHED_BROWSE_RESPONSE, *PCACHED_BROWSE_RESPONSE;
+
+
+
+
+PCACHED_BROWSE_RESPONSE
+BrLookupAndAllocateCachedEntry(
+ IN PNETWORK Network,
+ IN DWORD ServerType,
+ IN WORD Size,
+ IN ULONG Level,
+ IN LPCWSTR FirstNameToReturn
+ );
+
+NET_API_STATUS
+BrDestroyResponseCache(
+ IN PNETWORK Network
+ );
+
+NET_API_STATUS
+BrDestroyCacheEntry(
+ IN PCACHED_BROWSE_RESPONSE CacheEntry
+ );
+
+VOID
+BrAgeResponseCache(
+ IN PNETWORK Network
+ );
+
+PCACHED_BROWSE_RESPONSE
+BrAllocateResponseCacheEntry(
+ IN PNETWORK Network,
+ IN DWORD ServerType,
+ IN WORD Size,
+ IN ULONG Level,
+ IN LPCWSTR FirstNameToReturn
+ );
+
+#endif // _SRVENUM_INCLUDED_
diff --git a/private/net/svcdlls/dfs/client/dfsstub.c b/private/net/svcdlls/dfs/client/dfsstub.c
new file mode 100644
index 000000000..dc4290baa
--- /dev/null
+++ b/private/net/svcdlls/dfs/client/dfsstub.c
@@ -0,0 +1,1194 @@
+//+----------------------------------------------------------------------------
+//
+// Copyright (C) 1995, Microsoft Corporation
+//
+// File: dfsstub.c
+//
+// Contents: Stub file for the NetDfsXXX APIs. The stubs turn around and
+// call the NetrDfsXXX APIs on the appropriate server.
+//
+// Classes:
+//
+// Functions: NetDfsEnum
+//
+// History: 01-10-96 Milans created
+//
+//-----------------------------------------------------------------------------
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windows.h>
+#include <stdlib.h>
+#include <lm.h>
+#include <lmdfs.h>
+#include <dfsp.h>
+#include <netdfs.h>
+#include "domain.h"
+
+#define IS_UNC_PATH(wsz, cw) \
+ ((cw) > 2 && (wsz)[0] == L'\\' && (wsz)[1] == L'\\')
+
+#define IS_VALID_PREFIX(wsz, cw) \
+ ((cw) > 1 && (wsz)[0] == L'\\' && (wsz)[1] != L'\\')
+
+#define IS_VALID_DFS_PATH(wsz, cw) \
+ ((cw) > 0 && (wsz)[0] != L'\\')
+
+#define IS_VALID_STRING(wsz) \
+ ((wsz) != NULL && (wsz)[0] != UNICODE_NULL)
+
+NET_API_STATUS
+DfspGetDfsNameFromEntryPath(
+ LPWSTR wszEntryPath,
+ DWORD cwEntryPath,
+ LPWSTR *ppwszDfsName);
+
+NET_API_STATUS
+DfspBindRpc(
+ IN LPWSTR DfsName,
+ OUT RPC_BINDING_HANDLE *BindingHandle);
+
+VOID
+DfspFreeBinding(
+ RPC_BINDING_HANDLE BindingHandle);
+
+NET_API_STATUS
+DfspVerifyBinding();
+
+//
+// The APIs are all single-threaded - only 1 can be outstanding at a time in
+// any one process. The following critical section is used to gate the calls.
+// The critical section is initialized at DLL Load time.
+//
+
+CRITICAL_SECTION NetDfsApiCriticalSection;
+
+#define ENTER_NETDFS_API EnterCriticalSection( &NetDfsApiCriticalSection );
+
+#define LEAVE_NETDFS_API LeaveCriticalSection( &NetDfsApiCriticalSection );
+
+
+//+----------------------------------------------------------------------------
+//
+// Function: NetDfsAdd
+//
+// Synopsis: Creates a new volume, adds a replica to an existing volume,
+// or creates a link to another Dfs.
+//
+// Arguments: [DfsEntryPath] -- Name of volume/link to create/add replica
+// to.
+// [ServerName] -- Name of server hosting the storage, or for
+// link, name of Dfs root.
+// [ShareName] -- Name of share hosting the storage.
+// [Flags] -- Describes what is being added.
+//
+// Returns: [NERR_Success] -- Successfully completed operation.
+//
+// [ERROR_INVALID_PARAMETER] -- DfsEntryPath and/or ServerName
+// and/or ShareName and/or Flags are incorrect.
+//
+// [ERROR_INVALID_NAME] -- Unable to locate server or domain.
+//
+// [ERROR_DCNotFound] -- Unable to locate DC for DfsName.
+//
+// [ERROR_NOT_ENOUGH_MEMORY] -- Out of memory condition.
+//
+// [NERR_DfsNoSuchVolume] -- DfsEntryPath does not correspond to a
+// existing Dfs volume.
+//
+// [NERR_DfsVolumeAlreadyExists] -- DFS_ADD_VOLUME was specified
+// and a volume with DfsEntryPath already exists.
+//
+// [NERR_DfsInternalCorruption] -- Corruption of Dfs data
+// encountered at the server.
+//
+//-----------------------------------------------------------------------------
+
+NET_API_STATUS
+NetDfsAdd(
+ IN LPWSTR DfsEntryPath,
+ IN LPWSTR ServerName,
+ IN LPWSTR ShareName,
+ IN LPWSTR Comment,
+ IN DWORD Flags)
+{
+ NET_API_STATUS dwErr;
+ DWORD cwDfsEntryPath;
+ LPWSTR pwszDfsName;
+
+ //
+ // Validate the string arguments so RPC won't complain...
+ //
+
+ if (!IS_VALID_STRING(ServerName) ||
+ !IS_VALID_STRING(ShareName)) {
+ return( ERROR_INVALID_PARAMETER );
+ }
+
+ cwDfsEntryPath = wcslen(DfsEntryPath);
+
+ if (!IS_UNC_PATH(DfsEntryPath, cwDfsEntryPath) &&
+ !IS_VALID_PREFIX(DfsEntryPath, cwDfsEntryPath) &&
+ !IS_VALID_DFS_PATH(DfsEntryPath, cwDfsEntryPath)) {
+ return( ERROR_INVALID_PARAMETER );
+ }
+
+ ENTER_NETDFS_API
+
+ dwErr = DfspGetDfsNameFromEntryPath(
+ DfsEntryPath,
+ cwDfsEntryPath,
+ &pwszDfsName);
+
+ if (dwErr == NERR_Success) {
+
+ dwErr = DfspBindRpc(pwszDfsName, &netdfs_bhandle);
+
+ if (dwErr == NERR_Success) {
+
+ RpcTryExcept {
+
+ dwErr = NetrDfsAdd(
+ DfsEntryPath,
+ ServerName,
+ ShareName,
+ Comment,
+ Flags);
+
+ } RpcExcept(1) {
+
+ dwErr = RpcExceptionCode();
+
+ } RpcEndExcept;
+
+ DfspFreeBinding( netdfs_bhandle );
+
+ }
+
+ free( pwszDfsName );
+
+ }
+
+ LEAVE_NETDFS_API
+
+ return( dwErr );
+
+}
+
+
+//+----------------------------------------------------------------------------
+//
+// Function: NetDfsRemove
+//
+// Synopsis: Deletes a Dfs volume, removes a replica from an existing
+// volume, or removes a link to another Dfs.
+//
+// Arguments: [DfsEntryPath] -- Name of volume/link to remove.
+// [ServerName] -- Name of server hosting the storage. Must be
+// NULL if removing Link.
+// [ShareName] -- Name of share hosting the storage. Must be
+// NULL if removing Link.
+//
+// Returns: [NERR_Success] -- Successfully completed operation.
+//
+// [ERROR_INVALID_PARAMETER] -- DfsEntryPath is incorrect.
+//
+// [ERROR_INVALID_NAME] -- Unable to locate server or domain.
+//
+// [ERROR_DCNotFound] -- Unable to locate DC for DfsName.
+//
+// [ERROR_NOT_ENOUGH_MEMORY] -- Out of memory condition.
+//
+// [NERR_DfsNoSuchVolume] -- DfsEntryPath does not correspond to
+// a valid entry path.
+//
+// [NERR_DfsNotALeafVolume] -- Unable to delete the volume
+// because it is not a leaf volume.
+//
+// [NERR_DfsInternalCorruption] -- Corruption of Dfs data
+// encountered at the server.
+//
+//-----------------------------------------------------------------------------
+
+NET_API_STATUS
+NetDfsRemove(
+ IN LPWSTR DfsEntryPath,
+ IN LPWSTR ServerName,
+ IN LPWSTR ShareName)
+{
+ NET_API_STATUS dwErr;
+ DWORD cwDfsEntryPath;
+ LPWSTR pwszDfsName;
+
+ //
+ // Validate the string arguments so RPC won't complain...
+ //
+
+ cwDfsEntryPath = wcslen(DfsEntryPath);
+
+ if (!IS_UNC_PATH(DfsEntryPath, cwDfsEntryPath) &&
+ !IS_VALID_PREFIX(DfsEntryPath, cwDfsEntryPath) &&
+ !IS_VALID_DFS_PATH(DfsEntryPath, cwDfsEntryPath)) {
+ return( ERROR_INVALID_PARAMETER );
+ }
+
+ ENTER_NETDFS_API
+
+ dwErr = DfspGetDfsNameFromEntryPath(
+ DfsEntryPath,
+ cwDfsEntryPath,
+ &pwszDfsName);
+
+ if (dwErr == NERR_Success) {
+
+ dwErr = DfspBindRpc(pwszDfsName, &netdfs_bhandle);
+
+ if (dwErr == NERR_Success) {
+
+ RpcTryExcept {
+
+ dwErr = NetrDfsRemove(
+ DfsEntryPath,
+ ServerName,
+ ShareName);
+
+ } RpcExcept(1) {
+
+ dwErr = RpcExceptionCode();
+
+ } RpcEndExcept;
+
+ DfspFreeBinding( netdfs_bhandle );
+
+ }
+
+ free( pwszDfsName );
+
+ }
+
+ LEAVE_NETDFS_API
+
+ return( dwErr );
+
+}
+
+
+//+----------------------------------------------------------------------------
+//
+// Function: NetDfsSetInfo
+//
+// Synopsis: Sets the comment or state of a Dfs volume or Replica.
+//
+// Arguments: [DfsEntryPath] -- Path to the volume. Implicityly indicates
+// which server or domain to connect to.
+// [ServerName] -- Optional. If specified, only the state of
+// the server supporting this volume is modified.
+// [ShareName] -- Optional. If specified, only the state of
+// this share on the specified server is modified.
+// [Level] -- Must be 100 or 101
+// [Buffer] -- Pointer to DFS_INFO_100 or DFS_INFO_101
+//
+// Returns: [NERR_Success] -- If successfully set info.
+//
+// [ERROR_INVALID_LEVEL] -- Level is not 100 or 101
+//
+// [ERROR_INVALID_PARAMETER] -- Either DfsEntryPath is NULL,
+// or ShareName is specified but ServerName is not, or
+// Buffer is NULL.
+//
+// [ERROR_INVALID_NAME] -- Unable to locate server or domain.
+//
+// [ERROR_DCNotFound] -- Unable to locate DC for domain.
+//
+// [ERROR_NOT_ENOUGH_MEMORY] -- Out of memory condition.
+//
+// [NERR_DfsNoSuchVolume] -- No volume matches DfsEntryPath.
+//
+// [NERR_DfsNoSuchShare] -- The indicated ServerName/ShareName do
+// not support this Dfs volume.
+//
+// [NERR_DfsInternalCorruption] -- Corruption of Dfs data
+// encountered at the server.
+//
+//-----------------------------------------------------------------------------
+
+NET_API_STATUS NET_API_FUNCTION
+NetDfsSetInfo(
+ IN LPWSTR DfsEntryPath,
+ IN LPWSTR ServerName OPTIONAL,
+ IN LPWSTR ShareName OPTIONAL,
+ IN DWORD Level,
+ IN LPBYTE Buffer)
+{
+ NET_API_STATUS dwErr;
+ LPWSTR pwszDfsName;
+ DWORD cwDfsEntryPath;
+ DFS_INFO_STRUCT DfsInfo;
+
+ //
+ // Some elementary parameter checking to make sure we can proceed
+ // reasonably...
+ //
+
+ if (Level != 100 && Level != 101) {
+ return( ERROR_INVALID_LEVEL );
+ }
+
+ cwDfsEntryPath = wcslen(DfsEntryPath);
+
+ if (!IS_UNC_PATH(DfsEntryPath, cwDfsEntryPath) &&
+ !IS_VALID_PREFIX(DfsEntryPath, cwDfsEntryPath) &&
+ !IS_VALID_DFS_PATH(DfsEntryPath, cwDfsEntryPath)) {
+ return( ERROR_INVALID_PARAMETER );
+ }
+
+ if (!IS_VALID_STRING(ServerName) && IS_VALID_STRING(ShareName)) {
+ return( ERROR_INVALID_PARAMETER );
+ }
+
+ ENTER_NETDFS_API
+
+ dwErr = DfspGetDfsNameFromEntryPath(
+ DfsEntryPath,
+ cwDfsEntryPath,
+ &pwszDfsName);
+
+ if (dwErr == NERR_Success) {
+
+ //
+ // By now, we should have a valid pwszDfsName. Lets try to bind to it,
+ // and call the server.
+ //
+
+ dwErr = DfspBindRpc( pwszDfsName, &netdfs_bhandle );
+
+ if (dwErr == NERR_Success) {
+
+ RpcTryExcept {
+
+ DfsInfo.DfsInfo100 = (LPDFS_INFO_100) Buffer;
+
+ dwErr = NetrDfsSetInfo(
+ DfsEntryPath,
+ ServerName,
+ ShareName,
+ Level,
+ &DfsInfo);
+
+ } RpcExcept( 1 ) {
+
+ dwErr = RpcExceptionCode();
+
+ } RpcEndExcept;
+
+ DfspFreeBinding( netdfs_bhandle );
+
+ }
+
+ free( pwszDfsName );
+
+ }
+
+ LEAVE_NETDFS_API
+
+ return( dwErr );
+
+}
+
+
+//+----------------------------------------------------------------------------
+//
+// Function: NetDfsGetInfo
+//
+// Synopsis: Retrieves information about a particular Dfs volume.
+//
+// Arguments: [DfsEntryPath] -- Path to the volume. Implicitly indicates
+// which server or domain to connect to.
+// [ServerName] -- Optional. If specified, indicates the
+// server supporting DfsEntryPath.
+// [ShareName] -- Optional. If specified, indicates the share
+// on ServerName for which info is desired.
+// [Level] -- Indicates the level of info required.
+// [Buffer] -- On successful return, will contain the buffer
+// containing the required Info. This buffer should be
+// freed using NetApiBufferFree.
+//
+// Returns: [NERR_Success] -- Info successfully returned.
+//
+// [ERROR_INVALID_LEVEL] -- Level is not 1,2,3,100, or 101
+//
+// [ERROR_INVALID_PARAMETER] -- Either DfsEntryPath is NULL,
+// or ShareName is specified but ServerName is NULL.
+//
+// [ERROR_INVALID_NAME] -- Unable to locate server or domain.
+//
+// [ERROR_DCNotFound] -- Unable to locate DC for domain.
+//
+// [ERROR_NOT_ENOUGH_MEMORY] -- Out of memory condition.
+//
+// [NERR_DfsNoSuchVolume] -- No volume matches DfsEntryPath.
+//
+// [NERR_DfsInternalCorruption] -- Corruption of Dfs data
+// encountered at the server.
+//
+//-----------------------------------------------------------------------------
+
+NET_API_STATUS NET_API_FUNCTION
+NetDfsGetInfo(
+ IN LPWSTR DfsEntryPath,
+ IN LPWSTR ServerName OPTIONAL,
+ IN LPWSTR ShareName OPTIONAL,
+ IN DWORD Level,
+ OUT LPBYTE* Buffer)
+{
+ NET_API_STATUS dwErr;
+ LPWSTR pwszDfsName;
+ DWORD cwDfsEntryPath;
+ DFS_INFO_STRUCT DfsInfo;
+
+ //
+ // Some elementary parameter checking to make sure we can proceed
+ // reasonably...
+ //
+
+ if (!(Level >= 1 && Level <= 3) && !(Level >= 100 && Level <= 101)) {
+ return( ERROR_INVALID_LEVEL );
+ }
+
+ cwDfsEntryPath = wcslen(DfsEntryPath);
+
+ if (!IS_UNC_PATH(DfsEntryPath, cwDfsEntryPath) &&
+ !IS_VALID_PREFIX(DfsEntryPath, cwDfsEntryPath) &&
+ !IS_VALID_DFS_PATH(DfsEntryPath, cwDfsEntryPath)) {
+ return( ERROR_INVALID_PARAMETER );
+ }
+
+ if (!IS_VALID_STRING(ServerName) && IS_VALID_STRING(ShareName)) {
+ return( ERROR_INVALID_PARAMETER );
+ }
+
+ ENTER_NETDFS_API
+
+ dwErr = DfspGetDfsNameFromEntryPath(
+ DfsEntryPath,
+ cwDfsEntryPath,
+ &pwszDfsName);
+
+ if (dwErr == NERR_Success) {
+
+ //
+ // By now, we should have a valid pwszDfsName. Lets try to bind to it,
+ // and call the server.
+ //
+
+ dwErr = DfspBindRpc( pwszDfsName, &netdfs_bhandle );
+
+ if (dwErr == NERR_Success) {
+
+ RpcTryExcept {
+
+ DfsInfo.DfsInfo1 = NULL;
+
+ dwErr = NetrDfsGetInfo(
+ DfsEntryPath,
+ ServerName,
+ ShareName,
+ Level,
+ &DfsInfo);
+
+ if (dwErr == NERR_Success) {
+
+ *Buffer = (LPBYTE) DfsInfo.DfsInfo1;
+
+ }
+
+ } RpcExcept( 1 ) {
+
+ dwErr = RpcExceptionCode();
+
+ } RpcEndExcept;
+
+ DfspFreeBinding( netdfs_bhandle );
+
+ }
+
+ free( pwszDfsName );
+
+ }
+
+ LEAVE_NETDFS_API
+
+ return( dwErr );
+
+}
+
+
+//+----------------------------------------------------------------------------
+//
+// Function
+//
+// Synopsis: Enumerates the Dfs volumes.
+//
+// Arguments: [DfsName] -- Name of server or domain whose Dfs is being
+// enumerated. A leading \\ is optional.
+// [Level] -- Indicates the level of info needed back. Valid
+// Levels are 1,2, and 3.
+// [PrefMaxLen] -- Preferred maximum length of return buffer.
+// [Buffer] -- On successful return, contains an array of
+// DFS_INFO_X. This buffer should be freed with a call
+// to NetApiBufferFree.
+// [EntriesRead] -- On successful return, contains the number
+// of entries read (and therefore, size of the array in
+// Buffer).
+// [ResumeHandle] -- Must be 0 on first call. On subsequent calls
+// the value returned by the immediately preceding call.
+//
+// Returns: [NERR_Success] -- Enum data successfully returned.
+//
+// [ERROR_INVALID_LEVEL] -- The Level specified in invalid.
+//
+// [ERROR_INVALID_NAME] -- Unable to locate server or domain.
+//
+// [ERROR_DCNotFound] -- Unable to locate DC for DfsName.
+//
+// [ERROR_NOT_ENOUGH_MEMORY] -- Out of memory condition.
+//
+// [ERROR_NO_MORE_ITEMS] -- No more volumes to be enumerated.
+//
+// [NERR_DfsInternalCorruption] -- Corruption of Dfs data
+// encountered at the server.
+//
+//-----------------------------------------------------------------------------
+
+NET_API_STATUS NET_API_FUNCTION
+NetDfsEnum(
+ IN LPWSTR DfsName,
+ IN DWORD Level,
+ IN DWORD PrefMaxLen,
+ OUT LPBYTE* Buffer,
+ OUT LPDWORD EntriesRead,
+ IN OUT LPDWORD ResumeHandle)
+{
+ NET_API_STATUS dwErr;
+ DFS_INFO_ENUM_STRUCT DfsEnum;
+ DFS_INFO_3_CONTAINER DfsInfo3Container;
+
+ //
+ // Check the Level Parameter first, or RPC won't know how to marshal the
+ // arguments.
+ //
+
+ if (Level != 1 && Level != 2 && Level != 3) {
+ return( ERROR_INVALID_LEVEL );
+ }
+
+ ENTER_NETDFS_API
+
+ dwErr = DfspBindRpc( DfsName, &netdfs_bhandle );
+
+ if (dwErr == NERR_Success) {
+
+ DfsInfo3Container.EntriesRead = 0;
+ DfsInfo3Container.Buffer = NULL;
+
+ DfsEnum.Level = Level;
+ DfsEnum.DfsInfoContainer.DfsInfo3Container = &DfsInfo3Container;
+
+ RpcTryExcept {
+
+ dwErr = NetrDfsEnum(
+ Level,
+ PrefMaxLen,
+ &DfsEnum,
+ ResumeHandle);
+
+ if (dwErr == NERR_Success) {
+
+ *EntriesRead =DfsInfo3Container.EntriesRead;
+
+ *Buffer = (LPBYTE) DfsInfo3Container.Buffer;
+
+ }
+
+ } RpcExcept( 1 ) {
+
+ dwErr = RpcExceptionCode();
+
+ } RpcEndExcept;
+
+ DfspFreeBinding( netdfs_bhandle );
+
+ }
+
+ LEAVE_NETDFS_API
+
+ return( dwErr );
+
+}
+
+
+//+----------------------------------------------------------------------------
+//
+// Function: NetDfsMove
+//
+// Synopsis: Moves a dfs volume to a new place in the Dfs hierarchy.
+//
+// Arguments: [DfsEntryPath] -- Current path to the volume.
+// [NewDfsEntryPath] -- Desired new path to the volume.
+//
+// Returns: [NERR_Success] -- Info successfully returned.
+//
+// [ERROR_INVALID_PARAMETER] -- Either DfsEntryPath or
+// NewDfsEntryPath are not valid.
+//
+// [ERROR_INVALID_NAME] -- Unable to locate server or domain.
+//
+// [ERROR_DCNotFound] -- Unable to locate DC for domain.
+//
+// [ERROR_NOT_ENOUGH_MEMORY] -- Out of memory condition.
+//
+// [NERR_DfsNoSuchVolume] -- No volume matches DfsEntryPath.
+//
+// [NERR_DfsInternalCorruption] -- Corruption of Dfs data
+// encountered at the server.
+//
+//-----------------------------------------------------------------------------
+
+NET_API_STATUS
+NetDfsMove(
+ IN LPWSTR DfsEntryPath,
+ IN LPWSTR NewDfsEntryPath)
+{
+ NET_API_STATUS dwErr;
+ DWORD cwEntryPath;
+ LPWSTR pwszDfsName;
+
+ //
+ // Validate the input arguments...
+ //
+
+ cwEntryPath = wcslen( NewDfsEntryPath );
+
+ if (!IS_UNC_PATH(NewDfsEntryPath, cwEntryPath) &&
+ !IS_VALID_PREFIX(NewDfsEntryPath, cwEntryPath) &&
+ !IS_VALID_DFS_PATH(NewDfsEntryPath, cwEntryPath)) {
+ return( ERROR_INVALID_PARAMETER );
+ }
+
+ cwEntryPath = wcslen( DfsEntryPath );
+
+ if (!IS_UNC_PATH(DfsEntryPath, cwEntryPath) &&
+ !IS_VALID_PREFIX(DfsEntryPath, cwEntryPath) &&
+ !IS_VALID_DFS_PATH(DfsEntryPath, cwEntryPath)) {
+ return( ERROR_INVALID_PARAMETER );
+ }
+
+ ENTER_NETDFS_API
+
+ dwErr = DfspGetDfsNameFromEntryPath(
+ DfsEntryPath,
+ cwEntryPath,
+ &pwszDfsName);
+
+ if (dwErr == NERR_Success) {
+
+ //
+ // By now, we should have a valid pwszDfsName. Lets try to bind to it,
+ // and call the server.
+ //
+
+ dwErr = DfspBindRpc( pwszDfsName, &netdfs_bhandle );
+
+ if (dwErr == NERR_Success) {
+
+ RpcTryExcept {
+
+ dwErr = NetrDfsMove( DfsEntryPath, NewDfsEntryPath );
+
+ } RpcExcept(1) {
+
+ dwErr = RpcExceptionCode();
+
+ } RpcEndExcept;
+
+ DfspFreeBinding( netdfs_bhandle );
+
+ }
+
+ free( pwszDfsName );
+
+ }
+
+ LEAVE_NETDFS_API
+
+ return( dwErr );
+
+}
+
+
+//+----------------------------------------------------------------------------
+//
+// Function: NetDfsRename
+//
+// Synopsis: Renames a path that is along a Dfs Volume Entry Path
+//
+// Arguments: [Path] -- Current path.
+// [NewPath] -- Desired new path.
+//
+// Returns: [NERR_Success] -- Info successfully returned.
+//
+// [ERROR_INVALID_PARAMETER] -- Either DfsEntryPath or
+// NewDfsEntryPath are not valid.
+//
+// [ERROR_INVALID_NAME] -- Unable to locate server or domain.
+//
+// [ERROR_DCNotFound] -- Unable to locate DC for domain.
+//
+// [ERROR_NOT_ENOUGH_MEMORY] -- Out of memory condition.
+//
+// [NERR_DfsNoSuchVolume] -- No volume matches DfsEntryPath.
+//
+// [NERR_DfsInternalCorruption] -- Corruption of Dfs data
+// encountered at the server.
+//
+//-----------------------------------------------------------------------------
+
+NET_API_STATUS
+NetDfsRename(
+ IN LPWSTR Path,
+ IN LPWSTR NewPath)
+{
+ NET_API_STATUS dwErr;
+ DWORD cwPath;
+ LPWSTR pwszDfsName;
+
+ //
+ // Validate the input arguments...
+ //
+
+ cwPath = wcslen( NewPath );
+
+ if (!IS_UNC_PATH(NewPath, cwPath) &&
+ !IS_VALID_PREFIX(NewPath, cwPath) &&
+ !IS_VALID_DFS_PATH(NewPath, cwPath)) {
+ return( ERROR_INVALID_PARAMETER );
+ }
+
+ cwPath = wcslen( Path );
+
+ if (!IS_UNC_PATH(Path, cwPath) &&
+ !IS_VALID_PREFIX(Path, cwPath) &&
+ !IS_VALID_DFS_PATH(Path, cwPath)) {
+ return( ERROR_INVALID_PARAMETER );
+ }
+
+ ENTER_NETDFS_API
+
+ dwErr = DfspGetDfsNameFromEntryPath(
+ Path,
+ cwPath,
+ &pwszDfsName);
+
+ if (dwErr == NERR_Success) {
+
+ //
+ // By now, we should have a valid pwszDfsName. Lets try to bind to it,
+ // and call the server.
+ //
+
+ dwErr = DfspBindRpc( pwszDfsName, &netdfs_bhandle );
+
+ if (dwErr == NERR_Success) {
+
+ RpcTryExcept {
+
+ dwErr = NetrDfsRename( Path, NewPath );
+
+ } RpcExcept(1) {
+
+ dwErr = RpcExceptionCode();
+
+ } RpcEndExcept;
+
+ DfspFreeBinding( netdfs_bhandle );
+
+ }
+
+ free( pwszDfsName );
+
+ }
+
+ LEAVE_NETDFS_API
+
+ return( dwErr );
+
+}
+
+
+//+----------------------------------------------------------------------------
+//
+// Function: NetDfsManagerGetConfigInfo
+//
+// Synopsis: Given a DfsEntryPath and Guid of a local volume, this api
+// remotes to the root server of the entry path and retrieves
+// the config info from it.
+//
+// Arguments: [wszServer] -- Name of local machine
+// [wszLocalVolumeEntryPath] -- Entry Path of local volume.
+// [guidLocalVolume] -- Guid of local volume.
+// [ppDfsmRelationInfo] -- On successful return, contains pointer
+// to config info at the root server. Free using
+// NetApiBufferFree.
+//
+// Returns: [NERR_Success] -- Info returned successfully.
+//
+// [ERROR_INVALID_PARAMETER] -- wszLocalVolumeEntryPath is
+// invalid.
+//
+// [ERROR_INVALID_NAME] -- Unable to parse out server/domain name
+// from wszLocalVolumeEntryPath
+//
+// [ERROR_DCNotFound] -- Unable to locate a DC for domain
+//
+// [ERROR_NOT_ENOUGH_MEMORY] -- Out of memory condition
+//
+// [NERR_DfsNoSuchVolume] -- The root server did not recognize
+// a volume with this guid/entrypath
+//
+// [NERR_DfsNoSuchServer] -- wszServer is not a valid server for
+// wszLocalVolumeEntryPath
+//
+//-----------------------------------------------------------------------------
+
+NET_API_STATUS
+NetDfsManagerGetConfigInfo(
+ LPWSTR wszServer,
+ LPWSTR wszLocalVolumeEntryPath,
+ GUID guidLocalVolume,
+ LPDFSM_RELATION_INFO *ppDfsmRelationInfo)
+{
+ NET_API_STATUS dwErr;
+ LPWSTR pwszDfsName;
+ DWORD cwDfsEntryPath;
+
+ //
+ // Some elementary parameter checking to make sure we can proceed
+ // reasonably...
+ //
+
+ cwDfsEntryPath = wcslen(wszLocalVolumeEntryPath);
+
+ if (!IS_UNC_PATH(wszLocalVolumeEntryPath, cwDfsEntryPath) &&
+ !IS_VALID_PREFIX(wszLocalVolumeEntryPath, cwDfsEntryPath) &&
+ !IS_VALID_DFS_PATH(wszLocalVolumeEntryPath, cwDfsEntryPath)) {
+ return( ERROR_INVALID_PARAMETER );
+ }
+
+
+ ENTER_NETDFS_API
+
+ dwErr = DfspGetDfsNameFromEntryPath(
+ wszLocalVolumeEntryPath,
+ cwDfsEntryPath,
+ &pwszDfsName);
+
+ if (dwErr == NERR_Success) {
+
+ //
+ // By now, we should have a valid pwszDfsName. Lets try to bind to it,
+ // and call the server.
+ //
+
+ dwErr = DfspBindRpc( pwszDfsName, &netdfs_bhandle );
+
+ if (dwErr == NERR_Success) {
+
+ RpcTryExcept {
+
+ *ppDfsmRelationInfo = NULL;
+
+ dwErr = NetrDfsManagerGetConfigInfo(
+ wszServer,
+ wszLocalVolumeEntryPath,
+ guidLocalVolume,
+ ppDfsmRelationInfo);
+
+ } RpcExcept( 1 ) {
+
+ dwErr = RpcExceptionCode();
+
+ } RpcEndExcept;
+
+ DfspFreeBinding( netdfs_bhandle );
+
+ }
+
+ free( pwszDfsName );
+
+ }
+
+ LEAVE_NETDFS_API
+
+ return( dwErr );
+
+}
+
+
+//+----------------------------------------------------------------------------
+//
+// Function: DfspGetDfsNameFromEntryPath
+//
+// Synopsis: Given a DfsEntryPath, this routine returns the name of the
+// Dfs Root.
+//
+// Arguments: [wszEntryPath] -- Pointer to EntryPath to parse.
+//
+// [cwEntryPath] -- Length in WCHAR of wszEntryPath.
+//
+// [ppwszDfsName] -- Name of Dfs root is returned here. Memory
+// is allocated using malloc; caller resposible for
+// freeing it.
+//
+// Returns: [NERR_Success] -- Successfully parsed out Dfs Root.
+//
+// [ERROR_INVALID_NAME] -- Unable to parse wszEntryPath.
+//
+// [ERROR_NOT_ENOUGH_MEMORY] -- Unable to allocate memory for
+// ppwszDfsName.
+//
+//-----------------------------------------------------------------------------
+
+NET_API_STATUS
+DfspGetDfsNameFromEntryPath(
+ LPWSTR wszEntryPath,
+ DWORD cwEntryPath,
+ LPWSTR *ppwszDfsName)
+{
+ LPWSTR pwszDfsName, pwszFirst, pwszLast;
+ DWORD cwDfsName;
+
+ if (IS_UNC_PATH(wszEntryPath, cwEntryPath)) {
+
+ pwszFirst = &wszEntryPath[2];
+
+ } else if (IS_VALID_PREFIX(wszEntryPath, cwEntryPath)) {
+
+ pwszFirst = &wszEntryPath[1];
+
+ } else if (IS_VALID_DFS_PATH(wszEntryPath, cwEntryPath)) {
+
+ pwszFirst = &wszEntryPath[0];
+
+ } else {
+
+ return( ERROR_INVALID_NAME );
+
+ }
+
+ for (cwDfsName = 0, pwszLast = pwszFirst;
+ *pwszLast != UNICODE_NULL && *pwszLast != L'\\';
+ pwszLast++, cwDfsName++) {
+ ;
+ }
+
+ ++cwDfsName;
+
+ pwszDfsName = malloc( cwDfsName * sizeof(WCHAR) );
+
+ if (pwszDfsName != NULL) {
+
+ pwszDfsName[ cwDfsName - 1 ] = 0;
+
+ for (cwDfsName--; cwDfsName > 0; cwDfsName--) {
+
+ pwszDfsName[ cwDfsName - 1 ] = pwszFirst[ cwDfsName - 1 ];
+
+ }
+
+ *ppwszDfsName = pwszDfsName;
+
+ return( NERR_Success );
+
+ } else {
+
+ return( ERROR_NOT_ENOUGH_MEMORY );
+
+ }
+
+}
+
+
+//+----------------------------------------------------------------------------
+//
+// Function: DfspBindRpc
+//
+// Synopsis: Given a server or domain name, this API will bind to the
+// appropriate Dfs Manager service.
+//
+// Arguments: [DfsName] -- Name of domain or server. Leading \\ is optional
+//
+// [BindingHandle] -- On successful return, the binding handle
+// is returned here.
+//
+// Returns: [NERR_Success] -- Binding handle successfull returned.
+//
+// [RPC_S_SERVER_NOT_AVAILABLE] -- Unable to bind to NetDfs
+// interface on the named server or domain.
+//
+// [ERROR_INVALID_NAME] -- Unable to parse DfsName as a valid
+// server or domain name.
+//
+// [ERROR_DCNotFound] -- Unable to locate DC for DfsName.
+//
+// [ERROR_NOT_ENOUGH_MEMORY] -- Out of memory condition.
+//
+//-----------------------------------------------------------------------------
+
+NET_API_STATUS
+DfspBindRpc(
+ IN LPWSTR DfsName,
+ OUT RPC_BINDING_HANDLE *BindingHandle)
+{
+ LPWSTR wszProtocolSeq = L"ncacn_np";
+ LPWSTR wszEndPoint = L"\\pipe\\netdfs";
+ LPWSTR pwszRpcBindingString = NULL;
+ LPWSTR pwszDCName = NULL;
+ NET_API_STATUS dwErr;
+
+ //
+ // First, see if this is a domain name.
+ //
+
+ dwErr = I_NetDfsIsThisADomainName( DfsName );
+
+ if (dwErr == ERROR_SUCCESS) {
+
+ //
+ // Its a domain name. Get the PDC for the domain and try to bind to
+ // it.
+ //
+
+ dwErr = NetGetDCName( NULL, DfsName, (LPBYTE *) &pwszDCName );
+
+ } else {
+
+ //
+ // Lets see if this is a machine-based Dfs
+ //
+
+ pwszDCName = DfsName;
+
+ dwErr = ERROR_SUCCESS;
+
+ }
+
+ if (dwErr == ERROR_SUCCESS) {
+
+ dwErr = RpcStringBindingCompose(
+ NULL, // Object UUID
+ wszProtocolSeq, // Protocol Sequence
+ pwszDCName, // Network Address
+ wszEndPoint, // RPC Endpoint
+ NULL, // RPC Options
+ &pwszRpcBindingString); // Returned binding string
+
+ if (dwErr == RPC_S_OK) {
+
+ dwErr = RpcBindingFromStringBinding(
+ pwszRpcBindingString,
+ BindingHandle);
+
+ if (dwErr == RPC_S_OK) {
+
+ dwErr = DfspVerifyBinding();
+
+ } else {
+
+ dwErr = ERROR_INVALID_NAME;
+
+ }
+
+ }
+
+ }
+
+ if (pwszRpcBindingString != NULL) {
+
+ RpcStringFree( &pwszRpcBindingString );
+
+ }
+
+ if (pwszDCName != DfsName) {
+
+ NetApiBufferFree( pwszDCName );
+
+ }
+
+ if (dwErr == RPC_S_OUT_OF_MEMORY) {
+ dwErr = ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ return( dwErr );
+
+}
+
+//+----------------------------------------------------------------------------
+//
+// Function: DfspFreeBinding
+//
+// Synopsis: Frees a binding created by DfspBindRpc
+//
+// Arguments: [BindingHandle] -- The handle to free.
+//
+// Returns: Nothing
+//
+//-----------------------------------------------------------------------------
+
+VOID
+DfspFreeBinding(
+ RPC_BINDING_HANDLE BindingHandle)
+{
+ DWORD dwErr;
+
+ dwErr = RpcBindingFree( BindingHandle );
+
+}
+
+//+----------------------------------------------------------------------------
+//
+// Function: DfspVerifyBinding
+//
+// Synopsis: Verifies that the binding can be used by doing a
+// NetrDfsManagerGetVersion call on the binding.
+//
+// Arguments: None
+//
+// Returns: [NERR_Success] -- Server connnected to.
+//
+// [RPC_S_SERVER_UNAVAILABLE] -- The server is not available.
+//
+// Other RPC error from calling the remote server.
+//
+//-----------------------------------------------------------------------------
+
+NET_API_STATUS
+DfspVerifyBinding()
+{
+ NET_API_STATUS status = NERR_Success;
+ DWORD Version;
+
+ RpcTryExcept {
+
+ Version = NetrDfsManagerGetVersion();
+
+ } RpcExcept(1) {
+
+ status = RpcExceptionCode();
+
+ } RpcEndExcept;
+
+ return( status );
+
+}
+
diff --git a/private/net/svcdlls/dfs/client/domain.c b/private/net/svcdlls/dfs/client/domain.c
new file mode 100644
index 000000000..7abbe7019
--- /dev/null
+++ b/private/net/svcdlls/dfs/client/domain.c
@@ -0,0 +1,551 @@
+//+----------------------------------------------------------------------------
+//
+// Copyright (C) 1996, Microsoft Corporation
+//
+// File: domain.c
+//
+// Contents: Code to figure out domain dfs addresses
+//
+// Classes: None
+//
+// Functions: I_NetDfsIsThisADomainName
+//
+// DfspInitDomainList
+// DfspInitDomainListFromRegistry
+// DfspInitDomainListFromLSA
+// DfspInsertLsaDomainList
+//
+// History: Feb 7, 1996 Milans created
+//
+//-----------------------------------------------------------------------------
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <ntlsa.h> // LsaEnumerateTrustedDomains
+#include <windows.h>
+
+#include <lm.h> // NetWkstaGetInfo
+#include <netdfs.h>
+#include "domain.h"
+
+//
+// Global structure describing list of trusted domains
+//
+
+static struct {
+ ULONG cDomains;
+ UNICODE_STRING ustrThisDomain;
+ PUNICODE_STRING rgustrDomains;
+ ULONG cbNameBuffer;
+ PWSTR wszNameBuffer;
+} DfsDomainInfo;
+
+static BOOL DomainListInited = FALSE;
+
+//
+// Key and value name for trusted domain list in the registry. These are only
+// available on non-DC machines.
+//
+
+#define REG_KEY_TRUSTED_DOMAINS \
+ L"SYSTEM\\CurrentControlSet\\Services\\NetLogon\\Parameters"
+
+#define REG_VALUE_TRUSTED_DOMAINS \
+ L"TrustedDomainList"
+
+
+//
+// Private functions
+//
+
+VOID
+DfspInitDomainList();
+
+DWORD
+DfspInitDomainListFromRegistry(
+ IN HKEY hkey);
+
+DWORD
+DfspInitDomainListFromLSA();
+
+NTSTATUS
+DfspInsertLsaDomainList(
+ PLSA_TRUST_INFORMATION rglsaDomainList,
+ ULONG cDomains);
+
+
+//+----------------------------------------------------------------------------
+//
+// Function: I_NetDfsIsThisADomainName
+//
+// Synopsis: Runs down the list of domains in DfsDomainInfo and tries
+// to match the given name with one of the entries in the list.
+//
+// Arguments: [wszName] -- Name to find in DfsDomainInfo
+//
+// Returns: [ERROR_SUCCESS] -- Name is indeed a domain name.
+//
+// [ERROR_FILE_NOT_FOUND] -- Name is not in the DfsDomainInfo
+// list.
+//
+//-----------------------------------------------------------------------------
+
+DWORD
+I_NetDfsIsThisADomainName(
+ LPWSTR wszName)
+{
+ USHORT cbLength, i;
+ BOOLEAN fFound = FALSE;
+
+ if (!DomainListInited)
+ DfspInitDomainList();
+
+ if (!DomainListInited)
+ return( ERROR_FILE_NOT_FOUND );
+
+ cbLength = wcslen( wszName ) * sizeof(WCHAR);
+
+ if (cbLength == DfsDomainInfo.ustrThisDomain.Length) {
+
+ fFound = (_wcsnicmp(
+ wszName,
+ DfsDomainInfo.ustrThisDomain.Buffer,
+ DfsDomainInfo.ustrThisDomain.Length) == 0);
+
+ }
+
+ for (i = 0; i < DfsDomainInfo.cDomains && !fFound; i++) {
+
+ if (cbLength == DfsDomainInfo.rgustrDomains[i].Length) {
+
+ fFound = (_wcsnicmp(
+ wszName,
+ DfsDomainInfo.rgustrDomains[i].Buffer,
+ cbLength) == 0);
+
+ }
+
+ }
+
+ return( fFound ? ERROR_SUCCESS : ERROR_FILE_NOT_FOUND );
+
+}
+
+
+//+----------------------------------------------------------------------------
+//
+// Function: DfspInitDomainList
+//
+// Synopsis: Initializes the list of trusted domains so that their Dfs's
+// may be accessed.
+//
+// Arguments: None
+//
+// Returns: Nothing.
+//
+//-----------------------------------------------------------------------------
+
+VOID
+DfspInitDomainList()
+{
+ PWKSTA_INFO_100 wki100;
+ DWORD dwErr;
+ HKEY hkey;
+
+ ZeroMemory( &DfsDomainInfo, sizeof(DfsDomainInfo) );
+
+ //
+ // Get our own domain name
+ //
+
+ dwErr = NetWkstaGetInfo( NULL, 100, (LPBYTE *) &wki100 );
+
+ if (dwErr == ERROR_SUCCESS) {
+
+ if (wki100->wki100_langroup != NULL) {
+
+ DfsDomainInfo.ustrThisDomain.Length =
+ wcslen(wki100->wki100_langroup) * sizeof(WCHAR);
+
+ DfsDomainInfo.ustrThisDomain.MaximumLength =
+ DfsDomainInfo.ustrThisDomain.Length + sizeof(UNICODE_NULL);
+
+ DfsDomainInfo.ustrThisDomain.Buffer = (LPWSTR)
+ MIDL_user_allocate( DfsDomainInfo.ustrThisDomain.MaximumLength );
+
+ if (DfsDomainInfo.ustrThisDomain.Buffer != NULL) {
+
+ wcscpy(
+ DfsDomainInfo.ustrThisDomain.Buffer,
+ wki100->wki100_langroup);
+
+ }
+
+ }
+
+ NetApiBufferFree( wki100 );
+
+ }
+
+ //
+ // Next try to open the trusted domain list in the registry...
+ //
+
+ if (dwErr == ERROR_SUCCESS) {
+
+ dwErr = RegOpenKey( HKEY_LOCAL_MACHINE, REG_KEY_TRUSTED_DOMAINS, &hkey);
+
+ if (dwErr == ERROR_SUCCESS) {
+
+ dwErr = DfspInitDomainListFromRegistry( hkey );
+
+ RegCloseKey( hkey );
+
+ }
+
+ //
+ // If either the key was not found, or DfspInitDomainListFromRegistry
+ // returned ERROR_FILE_NOT_FOUND, try to initialize the list from the
+ // LSA.
+ //
+
+ if (dwErr == ERROR_FILE_NOT_FOUND) {
+
+ dwErr = DfspInitDomainListFromLSA();
+
+ }
+
+ }
+
+ if (dwErr == ERROR_SUCCESS) {
+
+ DomainListInited = TRUE;
+
+ } else {
+
+ if (DfsDomainInfo.ustrThisDomain.Buffer != NULL)
+ MIDL_user_free( DfsDomainInfo.ustrThisDomain.Buffer );
+
+ if (DfsDomainInfo.rgustrDomains != NULL)
+ MIDL_user_free( DfsDomainInfo.rgustrDomains );
+
+ if (DfsDomainInfo.wszNameBuffer != NULL)
+ MIDL_user_free( DfsDomainInfo.wszNameBuffer );
+
+ }
+
+}
+
+
+//+----------------------------------------------------------------------------
+//
+// Function: DfspInitDomainListFromRegistry
+//
+// Synopsis: Reads the trusted domain list from the registry.
+//
+// Arguments: [hkey] -- Handle to the key that contains the
+// TrustedDomainList value.
+//
+// Returns: [ERROR_SUCCESS] -- Successfully read in the list
+//
+// [ERROR_NOT_ENOUGH_MEMORY] -- Unable to allocate memory for
+// list.
+//
+// [ERROR_FILE_NOT_FOUND] -- Unable to find the
+// TrustedDomainList value in the registry.
+//
+//-----------------------------------------------------------------------------
+
+DWORD
+DfspInitDomainListFromRegistry(
+ IN HKEY hkey)
+{
+ DWORD dwErr, dwType, cbSize;
+ PBYTE pBuffer = NULL;
+
+ cbSize = 1024;
+
+ do {
+
+ if (pBuffer)
+ MIDL_user_free( pBuffer );
+
+ pBuffer = (PBYTE) MIDL_user_allocate( cbSize );
+
+ if (pBuffer != NULL) {
+
+ dwErr = RegQueryValueEx(
+ hkey,
+ REG_VALUE_TRUSTED_DOMAINS,
+ NULL,
+ &dwType,
+ pBuffer,
+ &cbSize);
+
+ } else {
+
+ dwErr = ERROR_NOT_ENOUGH_MEMORY;
+
+ }
+
+ } while ( dwErr == ERROR_MORE_DATA );
+
+ if (dwErr == ERROR_SUCCESS) {
+
+ //
+ // Good, we have the data. Now, we simply have to put them in the
+ // rgustrDomains list. The returned buffer contains a list of
+ // NULL terminated domain names, with the last one doubly NULL
+ // terminated.
+ //
+
+ PWCHAR pwch = (PWCHAR) pBuffer;
+
+ while (*pwch != UNICODE_NULL) {
+
+ if (*(pwch+1) == UNICODE_NULL) {
+
+ DfsDomainInfo.cDomains++;
+
+ pwch++;
+
+ }
+
+ pwch++;
+
+ }
+
+ DfsDomainInfo.rgustrDomains = (PUNICODE_STRING) MIDL_user_allocate(
+ DfsDomainInfo.cDomains *
+ sizeof(UNICODE_STRING));
+
+ if (DfsDomainInfo.rgustrDomains != NULL) {
+
+ ULONG i;
+
+ DfsDomainInfo.wszNameBuffer = (LPWSTR) pBuffer;
+
+ DfsDomainInfo.cbNameBuffer = cbSize;
+
+ for (i = 0, pwch = (PWCHAR) pBuffer;
+ i < DfsDomainInfo.cDomains;
+ i++) {
+
+ RtlInitUnicodeString(
+ &DfsDomainInfo.rgustrDomains[i],
+ pwch);
+
+ pwch += (DfsDomainInfo.rgustrDomains[i].Length +
+ sizeof(UNICODE_NULL)) / sizeof(WCHAR);
+
+ }
+
+ } else {
+
+ dwErr = ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ }
+
+ if ((dwErr != ERROR_SUCCESS) && (pBuffer != NULL))
+ MIDL_user_free( pBuffer );
+
+ return( dwErr );
+
+}
+
+
+//+----------------------------------------------------------------------------
+//
+// Function: DfspInitDomainListFromLSA
+//
+// Synopsis: Retrieves the list of trusted domains from the LSA.
+//
+// Arguments: None
+//
+// Returns: [ERROR_SUCCESS] -- Successfully inited list.
+//
+// [ERROR_NOT_ENOUGH_MEMORY] -- Out of memory condition
+//
+//-----------------------------------------------------------------------------
+
+DWORD
+DfspInitDomainListFromLSA()
+{
+ DWORD dwErr;
+ NTSTATUS status;
+ OBJECT_ATTRIBUTES oa;
+ LSA_HANDLE hlsa;
+ LSA_ENUMERATION_HANDLE hEnum = (LSA_ENUMERATION_HANDLE) NULL;
+ PLSA_TRUST_INFORMATION rglsaDomainInfo = NULL;
+
+ ZeroMemory( &oa, sizeof(OBJECT_ATTRIBUTES) );
+
+ status = LsaOpenPolicy(
+ NULL, // SystemName
+ &oa, // LSA Object Attributes
+ POLICY_VIEW_LOCAL_INFORMATION, // Desired Access
+ &hlsa);
+
+ if (NT_SUCCESS(status)) {
+
+ do {
+
+ ULONG cEnum;
+
+ status = LsaEnumerateTrustedDomains(
+ hlsa,
+ &hEnum,
+ (PVOID) &rglsaDomainInfo,
+ LSA_MAXIMUM_ENUMERATION_LENGTH,
+ &cEnum);
+
+ if (NT_SUCCESS(status)) {
+
+ status = DfspInsertLsaDomainList(
+ rglsaDomainInfo,
+ cEnum);
+
+ LsaFreeReturnBuffer( rglsaDomainInfo );
+
+ }
+
+ } while ( status == STATUS_SUCCESS );
+
+ }
+
+ switch (status) {
+ case STATUS_SUCCESS:
+ case STATUS_NO_MORE_ENTRIES:
+ dwErr = ERROR_SUCCESS;
+ break;
+
+ case STATUS_INSUFFICIENT_RESOURCES:
+ dwErr = ERROR_NOT_ENOUGH_MEMORY;
+ break;
+
+ default:
+ dwErr = ERROR_UNEXP_NET_ERR;
+ break;
+
+ }
+
+ return( dwErr );
+
+}
+
+
+//+----------------------------------------------------------------------------
+//
+// Function: DfspInsertLsaDomainList
+//
+// Synopsis: Helper function to insert a part of the trusted domain list
+// into the DfsDomainInfo.
+//
+// Arguments: [rglsaDomainList] -- Array of LSA_TRUST_INFORMATIONs.
+// [cDomains] -- Number of elements in rglsaDomainList
+//
+// Returns: [STATUS_SUCCESS] -- Successfully appended domain list to
+// DfsDomainInfo.
+//
+// [STATUS_INSUFFICIENT_RESOURCES] -- Unable to allocate memory
+// for new list.
+//
+//-----------------------------------------------------------------------------
+
+NTSTATUS
+DfspInsertLsaDomainList(
+ PLSA_TRUST_INFORMATION rglsaDomainList,
+ ULONG cDomains)
+{
+ PUNICODE_STRING rgustrDomains = NULL;
+ PWSTR wszNameBuffer = NULL, pwch;
+ ULONG cTotalDomains, cbNameBuffer, i, j;
+
+ cTotalDomains = DfsDomainInfo.cDomains + cDomains;
+
+ cbNameBuffer = DfsDomainInfo.cbNameBuffer;
+
+ for (i = 0; i < cDomains; i++) {
+
+ cbNameBuffer += rglsaDomainList[i].Name.Length + sizeof(UNICODE_NULL);
+
+ }
+
+ wszNameBuffer = (PWSTR) MIDL_user_allocate( cbNameBuffer );
+
+ if (wszNameBuffer == NULL) {
+
+ return( STATUS_INSUFFICIENT_RESOURCES );
+
+ }
+
+ rgustrDomains = (PUNICODE_STRING) MIDL_user_allocate(
+ cTotalDomains * sizeof(UNICODE_STRING));
+
+ if (rgustrDomains == NULL) {
+
+ MIDL_user_free( wszNameBuffer );
+
+ return( STATUS_INSUFFICIENT_RESOURCES );
+
+ }
+
+ //
+ // Copy over the existing DfsDomainInfo
+ //
+
+ if (DfsDomainInfo.cDomains != 0) {
+
+ CopyMemory(
+ wszNameBuffer,
+ DfsDomainInfo.wszNameBuffer,
+ DfsDomainInfo.cbNameBuffer);
+
+ CopyMemory(
+ rgustrDomains,
+ DfsDomainInfo.rgustrDomains,
+ DfsDomainInfo.cDomains * sizeof(UNICODE_STRING));
+
+ }
+
+ pwch = (PWSTR) (((PCHAR) wszNameBuffer) + DfsDomainInfo.cbNameBuffer);
+
+ for (j = 0, i = DfsDomainInfo.cDomains; j < cDomains; j++, i++) {
+
+ CopyMemory(
+ pwch,
+ rglsaDomainList[j].Name.Buffer,
+ rglsaDomainList[j].Name.Length);
+
+ pwch[ rglsaDomainList[j].Name.Length / sizeof(WCHAR) ] = UNICODE_NULL;
+
+ RtlInitUnicodeString(
+ &rgustrDomains[i],
+ pwch);
+
+ pwch += (rglsaDomainList[j].Name.Length / sizeof(WCHAR) + 1);
+
+ }
+
+ if (DfsDomainInfo.cDomains != 0) {
+
+ MIDL_user_free( DfsDomainInfo.rgustrDomains );
+
+ MIDL_user_free( DfsDomainInfo.wszNameBuffer );
+
+ }
+
+ DfsDomainInfo.cDomains = cTotalDomains;
+
+ DfsDomainInfo.rgustrDomains = rgustrDomains;
+
+ DfsDomainInfo.wszNameBuffer = wszNameBuffer;
+
+ DfsDomainInfo.cbNameBuffer = cbNameBuffer;
+
+ return( STATUS_SUCCESS );
+
+}
+
diff --git a/private/net/svcdlls/dfs/client/domain.h b/private/net/svcdlls/dfs/client/domain.h
new file mode 100644
index 000000000..3203d744a
--- /dev/null
+++ b/private/net/svcdlls/dfs/client/domain.h
@@ -0,0 +1,24 @@
+//+----------------------------------------------------------------------------
+//
+// Copyright (C) 1996, Microsoft Corporation
+//
+// File: domain.h
+//
+// Contents: Code to figure out domain dfs addresses
+//
+// Classes: None
+//
+// Functions: I_NetDfsIsThisADomainName
+//
+// History: Feb 7, 1996 Milans created
+//
+//-----------------------------------------------------------------------------
+
+#ifndef _DFS_DOMAIN_
+#define _DFS_DOMAIN
+
+DWORD
+I_NetDfsIsThisADomainName(
+ LPWSTR wszDomain);
+
+#endif // _DFS_DOMAIN_
diff --git a/private/net/svcdlls/dfs/client/makefile b/private/net/svcdlls/dfs/client/makefile
new file mode 100644
index 000000000..2311fac39
--- /dev/null
+++ b/private/net/svcdlls/dfs/client/makefile
@@ -0,0 +1,15 @@
+############################################################################
+#
+# Copyright (C) 1992, Microsoft Corporation.
+#
+# All rights reserved.
+#
+############################################################################
+
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
+
diff --git a/private/net/svcdlls/dfs/client/sources b/private/net/svcdlls/dfs/client/sources
new file mode 100644
index 000000000..e24b87012
--- /dev/null
+++ b/private/net/svcdlls/dfs/client/sources
@@ -0,0 +1,42 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+
+Author:
+
+ Milan Shah (milans) 10-Jan-95
+
+!ENDIF
+
+MAJORCOMP=NETDFS
+MINORCOMP=client
+
+TARGETNAME=netdfscl
+TARGETPATH=..\lib
+TARGETTYPE=LIBRARY
+
+INCLUDES=..;..\..\..\inc
+
+USE_CRTDLL=1
+
+C_DEFINES=-DUNICODE
+
+SOURCES= netdfs_c.c \
+ dfsstub.c \
+ domain.c
+
+
+
+
diff --git a/private/net/svcdlls/dfs/dfscli.acf b/private/net/svcdlls/dfs/dfscli.acf
new file mode 100644
index 000000000..7b14447f3
--- /dev/null
+++ b/private/net/svcdlls/dfs/dfscli.acf
@@ -0,0 +1,10 @@
+[ implicit_handle( handle_t netdfs_bhandle ) ]
+
+interface netdfs
+
+{
+typedef [allocate(all_nodes)] LPDFS_INFO_1;
+typedef [allocate(all_nodes)] LPDFS_INFO_2;
+typedef [allocate(all_nodes)] LPDFS_INFO_3;
+typedef [allocate(all_nodes)] LPDFSM_RELATION_INFO;
+}
diff --git a/private/net/svcdlls/dfs/dfssrv.acf b/private/net/svcdlls/dfs/dfssrv.acf
new file mode 100644
index 000000000..0c6341b4f
--- /dev/null
+++ b/private/net/svcdlls/dfs/dfssrv.acf
@@ -0,0 +1,7 @@
+[ implicit_handle( handle_t netdfs_bhandle ) ]
+
+interface netdfs
+
+{
+typedef [allocate(all_nodes)] LPDFSM_RELATION_INFO;
+}
diff --git a/private/net/svcdlls/dfs/dirs b/private/net/svcdlls/dfs/dirs
new file mode 100644
index 000000000..de4919d24
--- /dev/null
+++ b/private/net/svcdlls/dfs/dirs
@@ -0,0 +1,29 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ dirs.
+
+Abstract:
+
+ This file specifies the subdirectories of the current directory that
+ contain component makefiles.
+
+
+Author:
+
+ Steve Wood (stevewo) 17-Apr-1990
+
+NOTE: Commented description of this file is in \nt\bak\bin\dirs.tpl
+
+!ENDIF
+
+
+DIRS=server \
+ client \
+ utest
+
+
+
diff --git a/private/net/svcdlls/dfs/import.h b/private/net/svcdlls/dfs/import.h
new file mode 100644
index 000000000..baa292ffa
--- /dev/null
+++ b/private/net/svcdlls/dfs/import.h
@@ -0,0 +1,38 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ import.h
+
+Abstract:
+
+ This file allows us to include standard system header files in the
+ .idl file. The main .idl file imports a file called import.idl.
+ This allows the .idl file to use the types defined in these header
+ files. It also causes the following line to be added in the
+ MIDL generated header file:
+
+ #include "import.h"
+
+ Thus these types are available to the RPC stub routines as well.
+
+Author:
+
+ Dan Lafferty (danl) 07-May-1991
+
+Revision History:
+
+
+--*/
+
+#include <windef.h>
+#include <lmcons.h>
+
+#ifdef MIDL_PASS
+#define LPWSTR [string] LPWSTR
+#endif
+
+#include <lmdfs.h>
+
diff --git a/private/net/svcdlls/dfs/import.idl b/private/net/svcdlls/dfs/import.idl
new file mode 100644
index 000000000..c032eb6fe
--- /dev/null
+++ b/private/net/svcdlls/dfs/import.idl
@@ -0,0 +1,58 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ import.idl
+
+Abstract:
+
+ This file is useful for creating RPC interfaces that require the use
+ of windef types. The .idl file for the RPC product should contain a
+ line in the interface body that imports this file. For example:
+
+ import "import.h";
+
+ Doing this causes the MIDL generated header file to contain the
+ following line:
+
+ #include "import.h"
+
+ If this technique is not used, and instead the .idl file for the RPC
+ product simply contains #include <import.h>, then the contents of
+ import.h will be expanded in the MIDL generated header file. This
+ can lead to duplicate definition problems later when the RPC client
+ or RPC server code needs to include both the MIDL generated header file
+ and a file that is included in import.h.
+
+Author:
+
+ Dan Lafferty (danl) 20-Mar-1991
+
+Environment:
+
+ User Mode - Win32 - for use with the MIDL compiler
+
+
+Revision History:
+
+ 03-Apr-1991 danl
+ created
+
+--*/
+
+[
+ uuid(12345678-1234-ABCD-EF00-9948756789AB),
+#ifdef __midl
+ ms_union,
+#endif // __midl
+ version(1.0)
+]
+interface imports
+
+{
+#define MIDL_PASS
+#include "import.h"
+
+}
diff --git a/private/net/svcdlls/dfs/makefil0 b/private/net/svcdlls/dfs/makefil0
new file mode 100644
index 000000000..a7ecb98d6
--- /dev/null
+++ b/private/net/svcdlls/dfs/makefil0
@@ -0,0 +1,58 @@
+#
+# this is the midl compile phase of the build process.
+#
+# The following is where you put the name of your .idl file without
+# the .idl extension:
+#
+
+!INCLUDE $(NTMAKEENV)\makefile.plt
+
+IDL_NAME = netdfs
+CLIENT_ACF = dfscli.acf
+SERVER_ACF = dfssrv.acf
+
+!IFNDEF DISABLE_NET_UNICODE
+UNICODE=1
+NET_C_DEFINES=-DUNICODE
+!ENDIF
+
+SDKINC = $(BASEDIR)\public\sdk\inc
+SDKCRTINC = $(BASEDIR)\public\sdk\inc\crt
+PRIVATEINC = $(BASEDIR)\private\inc
+INCS = -I$(SDKINC) -I$(PRIVATEINC) -I$(SDKCRTINC) -I..\..\inc
+
+CPP = -cpp_cmd "$(MIDL_CPP)" $(MIDL_FLAGS) $(C_DEFINES) $(NET_C_DEFINES)
+
+CLIENT_TARGETS = client\$(IDL_NAME)_c.c \
+ .\$(IDL_NAME).h
+
+SERVER_TARGETS = server\$(IDL_NAME)_s.c
+
+EXTRN_DEPENDS = $(BASEDIR)\public\sdk\inc\lmdfs.h \
+ import.h \
+ import.idl \
+ $(CLIENT_ACF) \
+ $(SERVER_ACF)
+
+#
+# Define Products and Dependencies
+#
+
+all: $(CLIENT_TARGETS) $(SERVER_TARGETS) $(EXTRN_DEPENDS)
+
+clean: delete_source all
+
+delete_source:
+ erase $(CLIENT_TARGETS) $(SERVER_TARGETS)
+
+#
+# MIDL COMPILE
+#
+
+$(CLIENT_TARGETS) : $(IDL_NAME).idl $(EXTRN_DEPENDS)
+ midl -Oi -server none -acf $(CLIENT_ACF) -oldnames -error allocation -error ref -ms_ext -c_ext $(CPP) $(IDL_NAME).idl $(INCS)
+ IF EXIST $(IDL_NAME)_c.c copy $(IDL_NAME)_c.c .\client & del $(IDL_NAME)_c.c
+
+$(SERVER_TARGETS) : $(IDL_NAME).idl $(EXTRN_DEPENDS)
+ midl -client none -acf $(SERVER_ACF) -oldnames -error stub_data -error allocation -error ref -ms_ext -c_ext $(CPP) $(IDL_NAME).idl $(INCS)
+ IF EXIST $(IDL_NAME)_s.c copy $(IDL_NAME)_s.c .\server & del $(IDL_NAME)_s.c
diff --git a/private/net/svcdlls/dfs/netdfs.idl b/private/net/svcdlls/dfs/netdfs.idl
new file mode 100644
index 000000000..9e14baf5f
--- /dev/null
+++ b/private/net/svcdlls/dfs/netdfs.idl
@@ -0,0 +1,173 @@
+/*++
+
+Copyright (c) 1996 Microsoft Corporation
+
+Module Name:
+
+ NETDFS.IDL
+
+Abstract:
+
+ Contains the Netr (Net Remote) RPC interface specification for the
+ API associated with the Dfs Manager Service. This includes the following
+ APIs:
+
+ NetDfsEnum
+
+ Also contains the RPC specific data structures for these API.
+
+Author:
+
+ Milan Shah (milans) 08-Jan-1996
+
+Environment:
+
+ User Mode - Win32 - MIDL
+
+Revision History:
+
+ 08-Jan-1996 Milans Created.
+
+--*/
+
+
+//
+// Interface Attributes
+//
+
+[
+ uuid(4fc742e0-4a10-11cf-8273-00aa004ae673),
+ version(3.0),
+#ifdef __midl
+ ms_union,
+#endif // __midl
+ pointer_default(unique)
+]
+
+
+interface netdfs
+
+{
+
+import "import.idl";
+#include <lmcons.h>
+
+//
+// Data structures used by the public Dfs interface
+//
+
+typedef struct _DFS_INFO_1_CONTAINER {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] LPDFS_INFO_1 Buffer;
+} DFS_INFO_1_CONTAINER, *LPDFS_INFO_1_CONTAINER;
+
+typedef struct _DFS_INFO_2_CONTAINER {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] LPDFS_INFO_2 Buffer;
+} DFS_INFO_2_CONTAINER, *LPDFS_INFO_2_CONTAINER;
+
+typedef struct _DFS_INFO_3_CONTAINER {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] LPDFS_INFO_3 Buffer;
+} DFS_INFO_3_CONTAINER, *LPDFS_INFO_3_CONTAINER;
+
+typedef struct _DFS_INFO_ENUM_STRUCT {
+ DWORD Level;
+ [switch_is(Level)] union {
+ [case(1)]
+ LPDFS_INFO_1_CONTAINER DfsInfo1Container;
+ [case(2)]
+ LPDFS_INFO_2_CONTAINER DfsInfo2Container;
+ [case(3)]
+ LPDFS_INFO_3_CONTAINER DfsInfo3Container;
+ } DfsInfoContainer;
+} DFS_INFO_ENUM_STRUCT, *LPDFS_INFO_ENUM_STRUCT;
+
+typedef [switch_type(unsigned long)] union _DFS_INFO_STRUCT {
+ [case(1)]
+ LPDFS_INFO_1 DfsInfo1;
+ [case(2)]
+ LPDFS_INFO_2 DfsInfo2;
+ [case(3)]
+ LPDFS_INFO_3 DfsInfo3;
+ [case(100)]
+ LPDFS_INFO_100 DfsInfo100;
+ [case(101)]
+ LPDFS_INFO_101 DfsInfo101;
+ [default]
+ ;
+} DFS_INFO_STRUCT, *LPDFS_INFO_STRUCT;
+
+//
+// Data structures are used by the private Dfs Manager interface
+//
+
+typedef struct _DFSM_ENTRY_ID {
+ GUID idSubordinate;
+ [string, unique] LPWSTR wszSubordinate;
+} DFSM_ENTRY_ID, *LPDFSM_ENTRY_ID;
+
+typedef struct _DFSM_RELATION_INFO {
+ DWORD cSubordinates;
+ [size_is(cSubordinates)] DFSM_ENTRY_ID eid[];
+} DFSM_RELATION_INFO, *LPDFSM_RELATION_INFO;
+
+//
+// The public Dfs interface methods
+//
+
+DWORD NetrDfsManagerGetVersion();
+
+NET_API_STATUS NetrDfsAdd(
+ [in,string] LPWSTR DfsEntryPath,
+ [in,string] LPWSTR ServerName,
+ [in,unique,string] LPWSTR ShareName,
+ [in,unique,string] LPWSTR Comment,
+ [in] DWORD Flags);
+
+NET_API_STATUS NetrDfsRemove(
+ [in,string] LPWSTR DfsEntryPath,
+ [in,unique,string] LPWSTR ServerName,
+ [in,unique,string] LPWSTR ShareName);
+
+NET_API_STATUS NetrDfsSetInfo(
+ [in,string] LPWSTR DfsEntryPath,
+ [in,unique,string] LPWSTR ServerName,
+ [in,unique,string] LPWSTR ShareName,
+ [in] DWORD Level,
+ [in,switch_is(Level)] LPDFS_INFO_STRUCT DfsInfo);
+
+NET_API_STATUS NetrDfsGetInfo(
+ [in,string] LPWSTR DfsEntryPath,
+ [in,unique,string] LPWSTR ServerName,
+ [in,unique,string] LPWSTR ShareName,
+ [in] DWORD Level,
+ [out,switch_is(Level)] LPDFS_INFO_STRUCT DfsInfo);
+
+NET_API_STATUS NetrDfsEnum(
+ [in] DWORD Level,
+ [in] DWORD PrefMaxLen,
+ [in,out,unique] LPDFS_INFO_ENUM_STRUCT DfsEnum,
+ [in,out,unique] LPDWORD ResumeHandle);
+
+NET_API_STATUS NetrDfsMove(
+ [in,string] LPWSTR DfsEntryPath,
+ [in,string] LPWSTR NewDfsEntryPath);
+
+NET_API_STATUS NetrDfsRename(
+ [in,string] LPWSTR Path,
+ [in,string] LPWSTR NewPath);
+
+//
+// The private Dfs Manager methods
+//
+
+NET_API_STATUS NetrDfsManagerGetConfigInfo(
+ [in, string] LPWSTR wszServer,
+ [in, string] LPWSTR wszLocalVolumeEntryPath,
+ [in] GUID idLocalVolume,
+ [in, out, unique] LPDFSM_RELATION_INFO *ppRelationInfo);
+
+}
+
+
diff --git a/private/net/svcdlls/dfs/server/makefile b/private/net/svcdlls/dfs/server/makefile
new file mode 100644
index 000000000..2311fac39
--- /dev/null
+++ b/private/net/svcdlls/dfs/server/makefile
@@ -0,0 +1,15 @@
+############################################################################
+#
+# Copyright (C) 1992, Microsoft Corporation.
+#
+# All rights reserved.
+#
+############################################################################
+
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
+
diff --git a/private/net/svcdlls/dfs/server/sources b/private/net/svcdlls/dfs/server/sources
new file mode 100644
index 000000000..3f0565db9
--- /dev/null
+++ b/private/net/svcdlls/dfs/server/sources
@@ -0,0 +1,40 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+
+Author:
+
+ Milan Shah (milans) 10-Jan-95
+
+!ENDIF
+
+MAJORCOMP=NETDFS
+MINORCOMP=server
+
+TARGETNAME=netdfssv
+TARGETPATH=..\lib
+TARGETTYPE=LIBRARY
+
+INCLUDES=..
+
+USE_CRTDLL=1
+
+C_DEFINES=-DUNICODE
+
+SOURCES= netdfs_s.c
+
+
+
+
diff --git a/private/net/svcdlls/dfs/utest/makefile b/private/net/svcdlls/dfs/utest/makefile
new file mode 100644
index 000000000..124a39ca1
--- /dev/null
+++ b/private/net/svcdlls/dfs/utest/makefile
@@ -0,0 +1,28 @@
+############################################################################
+#
+# Copyright (C) 1992, Microsoft Corporation.
+#
+# All rights reserved.
+#
+############################################################################
+
+!if "$(NTMAKEENV)" != ""
+
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
+
+!else
+
+
+default: all
+
+!include filelist.mk
+!include $(COMMON)\src\win40.mk
+!include depend.mk
+
+
+!endif
diff --git a/private/net/svcdlls/dfs/utest/netdfs.c b/private/net/svcdlls/dfs/utest/netdfs.c
new file mode 100644
index 000000000..1b32f30e3
--- /dev/null
+++ b/private/net/svcdlls/dfs/utest/netdfs.c
@@ -0,0 +1,904 @@
+//+----------------------------------------------------------------------------
+//
+// Copyright (C) 1992, Microsoft Corporation
+//
+// File: netdfs.c
+//
+// Contents: Code to test the NetDfsXXX APIs
+//
+// Classes:
+//
+// Functions:
+//
+// History: 10 Jan 95 Milans Created.
+//
+//-----------------------------------------------------------------------------
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <windows.h>
+#include <lm.h>
+#include <lmdfs.h>
+
+// BUGBUG!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+NET_API_STATUS NET_API_FUNCTION
+NetDfsGetPathInfo(
+ IN LPWSTR Path, // Win32 path
+ IN DWORD Level, // Level of information requested
+ OUT LPBYTE* Buffer // API allocates and returns buffer with requested info
+ )
+{
+ return ERROR_INVALID_FUNCTION;
+}
+
+typedef ULONG (*API_TEST_FUNC)(
+ LPWSTR wszDfsName,
+ LPWSTR wszDfsEntryPath,
+ LPWSTR wszNewEntryPath,
+ LPWSTR wszServerName,
+ LPWSTR wszShareName,
+ LPWSTR wszComment,
+ DWORD Level,
+ DWORD Flag,
+ DWORD State,
+ DWORD PrefMaxLen,
+ DWORD ResumeHandle);
+
+ULONG DfsAdd(
+ LPWSTR wszDfsName,
+ LPWSTR wszDfsEntryPath,
+ LPWSTR wszNewEntryPath,
+ LPWSTR wszServerName,
+ LPWSTR wszShareName,
+ LPWSTR wszComment,
+ DWORD Level,
+ DWORD Flag,
+ DWORD State,
+ DWORD PrefMaxLen,
+ DWORD ResumeHandle);
+
+ULONG DfsRemove(
+ LPWSTR wszDfsName,
+ LPWSTR wszDfsEntryPath,
+ LPWSTR wszNewEntryPath,
+ LPWSTR wszServerName,
+ LPWSTR wszShareName,
+ LPWSTR wszComment,
+ DWORD Level,
+ DWORD Flag,
+ DWORD State,
+ DWORD PrefMaxLen,
+ DWORD ResumeHandle);
+
+ULONG DfsSetInfo(
+ LPWSTR wszDfsName,
+ LPWSTR wszDfsEntryPath,
+ LPWSTR wszNewEntryPath,
+ LPWSTR wszServerName,
+ LPWSTR wszShareName,
+ LPWSTR wszComment,
+ DWORD Level,
+ DWORD Flag,
+ DWORD State,
+ DWORD PrefMaxLen,
+ DWORD ResumeHandle);
+
+
+ULONG DfsGetInfo(
+ LPWSTR wszDfsName,
+ LPWSTR wszDfsEntryPath,
+ LPWSTR wszNewEntryPath,
+ LPWSTR wszServerName,
+ LPWSTR wszShareName,
+ LPWSTR wszComment,
+ DWORD Level,
+ DWORD Flag,
+ DWORD State,
+ DWORD PrefMaxLen,
+ DWORD ResumeHandle);
+
+ULONG DfsGetPathInfo(
+ LPWSTR wszDfsName,
+ LPWSTR wszDfsEntryPath,
+ LPWSTR wszNewEntryPath,
+ LPWSTR wszServerName,
+ LPWSTR wszShareName,
+ LPWSTR wszComment,
+ DWORD Level,
+ DWORD Flag,
+ DWORD State,
+ DWORD PrefMaxLen,
+ DWORD ResumeHandle);
+
+ULONG DfsEnum(
+ LPWSTR wszDfsName,
+ LPWSTR wszDfsEntryPath,
+ LPWSTR wszNewEntryPath,
+ LPWSTR wszServerName,
+ LPWSTR wszShareName,
+ LPWSTR wszComment,
+ DWORD Level,
+ DWORD Flag,
+ DWORD State,
+ DWORD PrefMaxLen,
+ DWORD ResumeHandle);
+
+ULONG DfsMove(
+ LPWSTR wszDfsName,
+ LPWSTR wszDfsEntryPath,
+ LPWSTR wszNewEntryPath,
+ LPWSTR wszServerName,
+ LPWSTR wszShareName,
+ LPWSTR wszComment,
+ DWORD Level,
+ DWORD Flag,
+ DWORD State,
+ DWORD PrefMaxLen,
+ DWORD ResumeHandle);
+
+ULONG DfsRename(
+ LPWSTR wszDfsName,
+ LPWSTR wszDfsEntryPath,
+ LPWSTR wszNewEntryPath,
+ LPWSTR wszServerName,
+ LPWSTR wszShareName,
+ LPWSTR wszComment,
+ DWORD Level,
+ DWORD Flag,
+ DWORD State,
+ DWORD PrefMaxLen,
+ DWORD ResumeHandle);
+
+const struct {
+ LPWSTR wszApiName;
+ LPWSTR wszApiComment;
+ API_TEST_FUNC fnApiTest;
+} rgApiInfo[] = {
+ { L"NetDfsAdd", L"(flags: -e -v -r [-c] -f)", DfsAdd },
+ { L"NetDfsRemove", L"(flags: -e -v -r)", DfsRemove },
+ { L"NetDfsSetInfo", L"(flags: -e [-v] [-r] -l -s [1 = offline, 2 = online]) (levels 100, 101)", DfsSetInfo },
+ { L"NetDfsGetInfo", L"(flags: -e [-v] [-r] -l) (levels 100, 101)", DfsGetInfo },
+ { L"NetDfsGetPathInfo", L"(flags: -e -l) (levels 200, 201, 202)", DfsGetPathInfo },
+ { L"NetDfsEnum", L"(flags: -d -l) (levels 1, 2, 3)", DfsEnum },
+ // Some aliases for convenience...
+ { L"Add", L"", DfsAdd },
+ { L"Remove", L"", DfsRemove },
+ { L"SetInfo", L"", DfsSetInfo },
+ { L"GetInfo", L"", DfsGetInfo},
+ { L"GetPathInfo", L"", DfsGetPathInfo },
+ { L"Enum", L"", DfsEnum}
+};
+
+VOID Usage();
+
+BOOLEAN fQuiet = FALSE;
+
+VOID
+errmsg( char *text, ULONG code )
+{
+ int i;
+ char msg[ 100 ];
+
+ i = FormatMessageA( FORMAT_MESSAGE_FROM_SYSTEM | sizeof( msg ),
+ NULL,
+ code,
+ 0,
+ msg,
+ sizeof(msg),
+ NULL );
+
+ if( i )
+ fprintf( stderr, "%s: %s\n", text, msg );
+ else
+ fprintf( stderr, "%s: error 0x%X (%u)\n", text, code, code );
+}
+
+
+//+----------------------------------------------------------------------------
+//
+// Function: main
+//
+// Synopsis:
+//
+// Arguments:
+//
+// Returns:
+//
+//-----------------------------------------------------------------------------
+
+ULONG __cdecl
+main(
+ int argc,
+ char *argv[])
+{
+ WCHAR wszApiName[32];
+ WCHAR wszDfsName[32];
+ WCHAR wszDfsEntryPath[MAX_PATH];
+ WCHAR wszNewEntryPath[MAX_PATH];
+ WCHAR wszServerName[80];
+ WCHAR wszShareName[NNLEN+1];
+ WCHAR wszComment[80];
+ DWORD Level;
+ DWORD Flag;
+ DWORD State;
+ DWORD PrefMaxLen;
+ DWORD ResumeHandle;
+
+ int i;
+ char *curtok;
+ BOOL fCmdError = FALSE;
+
+ //
+ // Initialize vars to defaults...
+ //
+
+ wszApiName[0] = UNICODE_NULL;
+ wszDfsName[0] = UNICODE_NULL;
+ wszDfsEntryPath[0] = UNICODE_NULL;
+ wszNewEntryPath[0] = UNICODE_NULL;
+ wszServerName[0] = UNICODE_NULL;
+ wszShareName[0] = UNICODE_NULL;
+ wszComment[0] = UNICODE_NULL;
+
+ Level = 1;
+ Flag = 0;
+ State = 0;
+ PrefMaxLen = (DWORD) ~0;
+ ResumeHandle = 0;
+
+ //
+ // Parse out the args...
+ //
+
+ for (i = 1; i < argc && !fCmdError; i++) {
+
+ curtok = argv[i];
+
+ if (curtok[0] == '-' || curtok[0] == '\\') {
+
+ #define NEXT_STRING_ARG(wszDest) \
+ i++; \
+ if (i < argc) { \
+ mbstowcs(wszDest, argv[i], strlen(argv[i]) + 1); \
+ } else { \
+ fCmdError = TRUE; \
+ }
+
+ #define NEXT_DWORD_ARG(k) \
+ i++; \
+ if (i < argc) { \
+ k = (DWORD) atoi(argv[i]); \
+ } else { \
+ fCmdError = TRUE; \
+ }
+
+
+ switch (curtok[1]) {
+ case 'a':
+ NEXT_STRING_ARG(wszApiName);
+ break;
+
+ case 'c':
+ NEXT_STRING_ARG(wszComment);
+ break;
+
+ case 'd':
+ NEXT_STRING_ARG(wszDfsName);
+ break;
+
+ case 'q':
+ fQuiet = 1 - fQuiet;
+ break;
+
+ case 'e':
+ NEXT_STRING_ARG(wszDfsEntryPath);
+ break;
+
+ case 'f':
+ NEXT_DWORD_ARG(Flag);
+ break;
+
+ case 'h':
+ NEXT_DWORD_ARG(ResumeHandle);
+ break;
+
+ case 'l':
+ NEXT_DWORD_ARG(Level);
+ break;
+
+ case 'n':
+ NEXT_STRING_ARG(wszNewEntryPath);
+ break;
+
+ case 'm':
+ NEXT_DWORD_ARG(PrefMaxLen);
+ break;
+
+ case 'r':
+ NEXT_STRING_ARG(wszShareName);
+ break;
+
+ case 's':
+ NEXT_DWORD_ARG(State);
+ break;
+
+ case 'v':
+ NEXT_STRING_ARG(wszServerName);
+ break;
+
+ default:
+ fCmdError = TRUE;
+ break;
+ }
+
+ } else {
+
+ fCmdError = TRUE;
+
+ }
+
+ }
+
+ if (wszApiName[0] == UNICODE_NULL) {
+
+ fCmdError = TRUE;
+
+ }
+
+ if (!fCmdError) {
+
+ fCmdError = TRUE; // ...because we haven't
+ // yet found a valid API name
+
+ for (i = 0;
+ i < sizeof(rgApiInfo) / sizeof(rgApiInfo[0]) && fCmdError;
+ i++) {
+
+ if (_wcsicmp( wszApiName, rgApiInfo[i].wszApiName ) == 0) {
+
+ return rgApiInfo[i].fnApiTest(
+ wszDfsName,
+ wszDfsEntryPath,
+ wszNewEntryPath,
+ wszServerName,
+ wszShareName,
+ wszComment,
+ Level,
+ Flag,
+ State,
+ PrefMaxLen,
+ ResumeHandle);
+ }
+
+ }
+
+ }
+
+ if (fCmdError) {
+ Usage();
+ }
+
+ return 1;
+
+}
+
+//+----------------------------------------------------------------------------
+//
+// Function: DfsAdd
+//
+// Synopsis: Test function for NetDfsAdd
+//
+// Arguments:
+//
+// Returns:
+//
+//-----------------------------------------------------------------------------
+
+ULONG DfsAdd(
+ LPWSTR wszDfsName,
+ LPWSTR wszDfsEntryPath,
+ LPWSTR wszNewEntryPath,
+ LPWSTR wszServerName,
+ LPWSTR wszShareName,
+ LPWSTR wszComment,
+ DWORD Level,
+ DWORD Flag,
+ DWORD State,
+ DWORD PrefMaxLen,
+ DWORD ResumeHandle)
+{
+ NET_API_STATUS status;
+
+ if( fQuiet == FALSE ) {
+ printf("Calling NetDfsAdd...\n");
+ printf("\tDfsEntryPath: [%ws]\n"
+ "\tServerName: [%ws]\n"
+ "\tShareName: [%ws]\n"
+ "\tComment: [%ws]\n"
+ "\tFlag: %d\n",
+ wszDfsEntryPath, wszServerName, wszShareName, wszComment, Flag);
+ }
+
+ status = NetDfsAdd(
+ wszDfsEntryPath,
+ wszServerName,
+ wszShareName,
+ wszComment,
+ Flag);
+
+ if (status == NERR_Success) {
+ if( fQuiet == FALSE ) printf("NetDfsAdd succeeded!\n");
+ } else {
+ errmsg("NetDfsAdd", status);
+ }
+
+ return status;
+}
+
+//+----------------------------------------------------------------------------
+//
+// Function: DfsRemove
+//
+// Synopsis: Test function for NetDfsRemove
+//
+// Arguments:
+//
+// Returns:
+//
+//-----------------------------------------------------------------------------
+
+ULONG DfsRemove(
+ LPWSTR wszDfsName,
+ LPWSTR wszDfsEntryPath,
+ LPWSTR wszNewEntryPath,
+ LPWSTR wszServerName,
+ LPWSTR wszShareName,
+ LPWSTR wszComment,
+ DWORD Level,
+ DWORD Flag,
+ DWORD State,
+ DWORD PrefMaxLen,
+ DWORD ResumeHandle)
+{
+ NET_API_STATUS status;
+
+ if( fQuiet == FALSE ) {
+ printf("Calling NetDfsRemove...\n");
+ printf("\tDfsEntryPath: [%ws]\n"
+ "\tServerName: [%ws]\n"
+ "\tShareName: [%ws]\n",
+ wszDfsEntryPath, wszServerName, wszShareName);
+ }
+
+ status = NetDfsRemove(
+ wszDfsEntryPath,
+ wszServerName,
+ wszShareName);
+
+ if (status == NERR_Success) {
+ if( fQuiet == FALSE )printf("NetDfsRemove succeeded!\n");
+ } else {
+ errmsg("NetDfsRemove", status);
+ }
+
+ return status;
+}
+
+//+----------------------------------------------------------------------------
+//
+// Function: DfsSetInfo
+//
+// Synopsis: Test function for NetDfsSetInfo
+//
+// Arguments:
+//
+// Returns:
+//
+//-----------------------------------------------------------------------------
+
+ULONG DfsSetInfo(
+ LPWSTR wszDfsName,
+ LPWSTR wszDfsEntryPath,
+ LPWSTR wszNewEntryPath,
+ LPWSTR wszServerName,
+ LPWSTR wszShareName,
+ LPWSTR wszComment,
+ DWORD Level,
+ DWORD Flag,
+ DWORD State,
+ DWORD PrefMaxLen,
+ DWORD ResumeHandle)
+{
+ NET_API_STATUS status;
+ DFS_INFO_100 DfsInfo;
+ LPDFS_INFO_100 pDfsInfo0 = (LPDFS_INFO_100) &DfsInfo;
+ LPDFS_INFO_101 pDfsInfo1 = (LPDFS_INFO_101) &DfsInfo;
+
+ if( fQuiet == FALSE ) {
+ printf("Calling NetDfsSetInfo...\n");
+ printf("\tDfsEntryPath: [%ws]\n"
+ "\tServer: [%ws]\n"
+ "\tShare: [%ws]\n"
+ "\tLevel: %d\n",
+ wszDfsEntryPath, wszServerName, wszShareName, Level);
+ }
+ if (Level == 100) {
+ if( fQuiet == FALSE) printf("\tComment: [%ws]\n", wszComment);
+ pDfsInfo0->Comment = wszComment;
+ } else if (Level == 101) {
+ if( fQuiet == FALSE) printf("\tState: %d\n", State);
+ pDfsInfo1->State = State;
+ } else {
+ printf("\tUnrecognized Level, passing garbage for Buffer\n");
+ }
+
+ status = NetDfsSetInfo(
+ wszDfsEntryPath,
+ wszServerName,
+ wszShareName,
+ Level,
+ (LPBYTE) &DfsInfo);
+
+ if (status == NERR_Success) {
+ if( fQuiet == FALSE )printf("NetDfsSetInfo succeeded!\n");
+ } else {
+ errmsg("NetDfsSetInfo", status);
+ }
+ return status;
+}
+
+//+----------------------------------------------------------------------------
+//
+// Function: DfsGetInfo
+//
+// Synopsis: Test function for NetDfsGetInfo
+//
+// Arguments:
+//
+// Returns: Nothing
+//
+//-----------------------------------------------------------------------------
+
+ULONG DfsGetInfo(
+ LPWSTR wszDfsName,
+ LPWSTR wszDfsEntryPath,
+ LPWSTR wszNewEntryPath,
+ LPWSTR wszServerName,
+ LPWSTR wszShareName,
+ LPWSTR wszComment,
+ DWORD Level,
+ DWORD Flag,
+ DWORD State,
+ DWORD PrefMaxLen,
+ DWORD ResumeHandle)
+{
+ NET_API_STATUS status;
+ LPBYTE Buffer;
+ DWORD i;
+
+ if( fQuiet == FALSE ) {
+ printf("Calling NetDfsGetInfo...\n");
+ printf("\tDfsEntryPath: [%ws]\n"
+ "\tServerName: [%ws]\n"
+ "\tShareName: [%ws]\n"
+ "\tLevel: %d\n\n",
+ wszDfsEntryPath, wszServerName, wszShareName, Level);
+ }
+
+ status = NetDfsGetInfo(
+ wszDfsEntryPath,
+ wszServerName,
+ wszShareName,
+ Level,
+ &Buffer);
+
+ if (status == NERR_Success) {
+
+ LPDFS_INFO_3 pDfsInfoX = (LPDFS_INFO_3) Buffer;
+ LPDFS_INFO_100 pDfsInfo100 = (LPDFS_INFO_100) Buffer;
+ LPDFS_INFO_101 pDfsInfo101 = (LPDFS_INFO_101) Buffer;
+
+ if( fQuiet == FALSE )printf("NetDfsGetInfo Succeeded!\n");
+
+ switch (Level) {
+ case 1:
+ case 2:
+ case 3:
+ printf("EntryPath: [%ws]\n", pDfsInfoX->EntryPath);
+ if (Level == 1)
+ break;
+ printf("Comment: [%ws]\n", pDfsInfoX->Comment);
+ printf("State: %d\n", pDfsInfoX->State);
+ printf("NumberOfStorages: %d\n", pDfsInfoX->NumberOfStorages);
+ if (Level == 2)
+ break;
+ for (i = 0; i < pDfsInfoX->NumberOfStorages; i++) {
+ printf(" %d. \\\\%ws\\%ws -- State %d\n",
+ i, pDfsInfoX->Storage[i].ServerName,
+ pDfsInfoX->Storage[i].ShareName,
+ pDfsInfoX->Storage[i].State);
+ }
+ break;
+
+ case 100:
+ printf("Comment: [%ws]\n", pDfsInfo100->Comment);
+ break;
+
+ case 101:
+ printf("State: %d\n", pDfsInfo101->State);
+ break;
+
+ default:
+ printf("Unrecognized Level %d\n", Level);
+ break;
+
+ }
+
+ NetApiBufferFree( Buffer );
+
+ } else {
+
+ errmsg("NetDfsGetInfo", status);
+
+ }
+
+ return status;
+
+}
+
+//+----------------------------------------------------------------------------
+//
+// Function: DfsGetPathInfo
+//
+// Synopsis: Test function for NetDfsGetPathInfo
+//
+// Arguments:
+//
+// Returns: Nothing
+//
+//-----------------------------------------------------------------------------
+
+ULONG DfsGetPathInfo(
+ LPWSTR wszDfsName,
+ LPWSTR wszDfsEntryPath,
+ LPWSTR wszNewEntryPath,
+ LPWSTR wszServerName,
+ LPWSTR wszShareName,
+ LPWSTR wszComment,
+ DWORD Level,
+ DWORD Flag,
+ DWORD State,
+ DWORD PrefMaxLen,
+ DWORD ResumeHandle)
+{
+ NET_API_STATUS status;
+ LPBYTE Buffer;
+ DWORD i;
+
+ printf("BUGUBG: NetDfsGetPathInfo unimplemented\n");
+ return NO_ERROR;
+
+ printf("Calling NetDfsGetPathInfo...\n");
+ printf("\tDfsEntryPath: [%ws]\n"
+ "\tLevel: %d\n\n",
+ wszDfsEntryPath, Level);
+
+ status = NetDfsGetPathInfo(
+ wszDfsEntryPath,
+ Level,
+ &Buffer);
+
+ if (status == NERR_Success) {
+
+ LPDFS_INFO_200 pDfsInfo200 = (LPDFS_INFO_200) Buffer;
+ LPDFS_INFO_201 pDfsInfo201 = (LPDFS_INFO_201) Buffer;
+ LPDFS_INFO_202 pDfsInfo202 = (LPDFS_INFO_202) Buffer;
+
+ printf("NetDfsGetPathInfo Succeeded!\n");
+
+ do // for break
+ {
+ printf("PathType: [");
+ switch (pDfsInfo200->PathType)
+ {
+ case DFS_PATH_NONDFS: printf("non-Dfs"); break;
+ case DFS_PATH_REDIRECTED: printf("redirected"); break;
+ case DFS_PATH_LOCAL: printf("local"); break;
+ case DFS_PATH_UNC_IN_DFS: printf("UNC in Dfs"); break;
+ case DFS_PATH_UNIVERSAL: printf("Universal"); break;
+ default: printf("UNKNOWN"); break;
+ }
+ printf("]\n");
+
+ if (Level == 200)
+ break;
+
+ if (pDfsInfo201->PathType != DFS_PATH_NONDFS)
+ {
+ printf("DfsUniversalPath: [%ws]\n", pDfsInfo201->DfsUniversalPath);
+ }
+
+ if (Level == 201)
+ break;
+
+ if (pDfsInfo202->PathType != DFS_PATH_NONDFS)
+ {
+ printf("EntryPathLen: [%d]\n", pDfsInfo202->EntryPathLen);
+ }
+ } while (0);
+
+ NetApiBufferFree( Buffer );
+
+ } else {
+
+ errmsg("NetDfsGetPathInfo", status);
+
+ }
+
+ return status;
+
+}
+
+//+----------------------------------------------------------------------------
+//
+// Function: DfsEnum
+//
+// Synopsis: Test function for NetDfsEnum
+//
+// Arguments:
+//
+// Returns: Nothing
+//
+//-----------------------------------------------------------------------------
+
+ULONG DfsEnum(
+ LPWSTR wszDfsName,
+ LPWSTR wszDfsEntryPath,
+ LPWSTR wszNewEntryPath,
+ LPWSTR wszServerName,
+ LPWSTR wszShareName,
+ LPWSTR wszComment,
+ DWORD Level,
+ DWORD Flag,
+ DWORD State,
+ DWORD PrefMaxLen,
+ DWORD ResumeHandle)
+{
+ NET_API_STATUS status;
+ LPBYTE Buffer;
+ DWORD EntriesRead;
+ PDFS_INFO_3 pDfsInfo, pNextDfsInfo;
+
+ if( fQuiet == FALSE ) {
+ printf("Calling NetDfsEnum...\n");
+ printf("\tDfsName: [%ws]\n"
+ "\tLevel: %d\n"
+ "\t PrefMaxLen: %d\n"
+ "\tResumeHandle: %d\n\n",
+ wszDfsName, Level, PrefMaxLen, ResumeHandle);
+ }
+
+ status = NetDfsEnum(
+ wszDfsName,
+ Level,
+ PrefMaxLen,
+ &Buffer,
+ &EntriesRead,
+ &ResumeHandle);
+
+ if (status == NERR_Success) {
+
+ DWORD k, j;
+ LPDFS_STORAGE_INFO lpStorage;
+
+ if( fQuiet == FALSE )printf("NetDfsEnum succeeded!\n");
+ printf("\tEntries Read = %d\n", EntriesRead);
+ printf("\tResume Handle = %d\n", ResumeHandle);
+
+ printf("\tReturned Entries:\n");
+
+ pDfsInfo = (LPDFS_INFO_3) Buffer;
+
+ for (k = 0; k < EntriesRead; k++) {
+
+ switch (Level) {
+ case 1:
+ case 2:
+ case 3:
+ printf("\t % 3d. [%ws]\n", k, pDfsInfo->EntryPath);
+
+ if (Level == 1) {
+
+ pDfsInfo = (LPDFS_INFO_3)
+ ((LPBYTE) pDfsInfo + sizeof(DFS_INFO_1));
+
+ break;
+ }
+
+ printf("\t [%ws]\n", pDfsInfo->Comment);
+
+ printf("\t State:%d Storages:%d\n",
+ pDfsInfo->State,
+ pDfsInfo->NumberOfStorages);
+
+ if (Level == 2) {
+
+ pDfsInfo = (LPDFS_INFO_3)
+ ((LPBYTE) pDfsInfo + sizeof(DFS_INFO_2));
+
+ break;
+
+ }
+
+ lpStorage = pDfsInfo->Storage;
+
+ for (j = 0; j < pDfsInfo->NumberOfStorages; j++) {
+
+ printf("\t \\\\%ws\\%ws\n",
+ lpStorage[j].ServerName,
+ lpStorage[j].ShareName);
+
+ }
+
+ pDfsInfo = (LPDFS_INFO_3)
+ ((LPBYTE) pDfsInfo + sizeof(DFS_INFO_3));
+
+ break;
+
+ default:
+ printf("Unrecognized Level %d\n", Level);
+ break;
+
+ } // end switch
+
+ } // end for
+
+ NetApiBufferFree( Buffer );
+
+ } else {
+
+ errmsg("NetDfsEnum", status);
+
+ }
+
+ return status;
+
+}
+
+//+----------------------------------------------------------------------------
+//
+// Function: Usage
+//
+// Synopsis: Prints out usage command line for this program.
+//
+// Arguments: None
+//
+// Returns: Nothing
+//
+//-----------------------------------------------------------------------------
+
+VOID Usage()
+{
+ ULONG i;
+
+ printf("Usage: netdfs -a <api name> [options]\n");
+ printf("Options:\n");
+ printf("\t-c Comment -- Comment to pass to API\n");
+ printf("\t-d DfsName -- Name of Dfs Root\n");
+ printf("\t-e EntryPath -- Name of Dfs Volume\n");
+ printf("\t-f Flag -- Flag to pass to NetDfsAdd, 1 = add volume, 2 = add dfs link\n");
+ printf("\t-h ResumeHandle -- ResumeHandle or State value to pass to API\n");
+ printf("\t-l Level -- Level of info requested\n");
+ printf("\t-m PrefMaxLen -- Preferred Maximum Length of return buffer\n");
+ printf("\t-n NewEntryPath -- NewEntryPath to pass to API\n");
+ printf("\t-r ShareName -- ShareName to pass to API\n");
+ printf("\t-s State -- State to pass to NetDfsSetInfo for Level 101\n");
+ printf("\t-v ServerName -- ServerName to pass to API\n");
+ printf("\t-q -- quiet mode\n" );
+ printf("\nValid API names:\n" );
+
+ for (i = 0; i < sizeof(rgApiInfo) / sizeof(rgApiInfo[0]) ; i++ ) {
+ printf( "\t%ws %ws\n", rgApiInfo[i].wszApiName, rgApiInfo[i].wszApiComment );
+ }
+
+}
diff --git a/private/net/svcdlls/dfs/utest/netdfs.rc b/private/net/svcdlls/dfs/utest/netdfs.rc
new file mode 100644
index 000000000..025b34be9
--- /dev/null
+++ b/private/net/svcdlls/dfs/utest/netdfs.rc
@@ -0,0 +1,10 @@
+#include <windows.h>
+#include <ntverp.h>
+
+#define VER_FILETYPE VFT_APP
+#define VER_FILESUBTYPE VFT2_UNKNOWN
+#define VER_FILEDESCRIPTION_STR "Windows NT Distributed File System Utility"
+#define VER_INTERNALNAME_STR "netdfs.exe"
+#define VER_ORIGINALFILENAME_STR "netdfs.exe"
+
+#include "common.ver"
diff --git a/private/net/svcdlls/dfs/utest/sources b/private/net/svcdlls/dfs/utest/sources
new file mode 100644
index 000000000..1e2526a0c
--- /dev/null
+++ b/private/net/svcdlls/dfs/utest/sources
@@ -0,0 +1,47 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+
+Author:
+
+ Milan Shah (milans) 10-Jan-95
+
+NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl
+
+!ENDIF
+
+MAJORCOMP=netdfs
+MINORCOMP=test
+
+TARGETNAME=netdfs
+TARGETPATH=obj
+TARGETTYPE=PROGRAM
+
+C_DEFINES=-DUNICODE -D_UNICODE
+
+INCLUDES=.; \
+ ..;
+
+TARGETLIBS=$(BASEDIR)\public\sdk\lib\*\ntdll.lib \
+ $(BASEDIR)\public\sdk\lib\*\netapi32.lib \
+ $(BASEDIR)\public\sdk\lib\*\rpcrt4.lib \
+ $(BASEDIR)\public\sdk\lib\*\rpcndr.lib \
+ $(BASEDIR)\public\sdk\lib\*\rpcutil.lib
+
+USE_CRTDLL=1
+
+SOURCES= netdfs.c netdfs.rc
+
+UMTYPE=console
diff --git a/private/net/svcdlls/dirs b/private/net/svcdlls/dirs
new file mode 100644
index 000000000..c1e4bff07
--- /dev/null
+++ b/private/net/svcdlls/dirs
@@ -0,0 +1,58 @@
+!IF 0
+
+Copyright (c) 1989-92 Microsoft Corporation
+
+Module Name:
+
+ dirs.
+
+Abstract:
+
+ This file specifies the subdirectories of the current directory that
+ contain component makefiles.
+
+
+Author:
+
+ Steve Wood (stevewo) 17-Apr-1990
+
+NOTE: Commented description of this file is in \nt\public\oak\bin\dirs.tpl
+
+!ENDIF
+
+#
+# This macro is defined by the developer. It is a list of all subdirectories
+# that build required components. Each subdirectory should be on a separate
+# line using the line continuation character. This will minimize merge
+# conflicts if two developers adding source files to the same component.
+# The order of the directories is the order that they will be built when
+# doing a build.
+#
+
+DIRS= \
+ srvsvc \
+ wkssvc \
+ alrsvc \
+ msgsvc \
+ Repl \
+ at \
+ rpl \
+ browser \
+ logonsrv\
+ ntlmssp \
+ upssvc \
+ nwsap \
+ lls \
+ dfs \
+ fpnw
+
+#
+# This macro is defined by the developer. It is a list of all subdirectories
+# that build optional components. Each subdirectory should be on a separate
+# line using the line continuation character. This will minimize merge
+# conflicts if two developers adding source files to the same component.
+# The order of the directories is the order that they will be built when
+# doing a build.
+#
+
+OPTIONAL_DIRS= browser2
diff --git a/private/net/svcdlls/fpnw/client/encrypt.c b/private/net/svcdlls/fpnw/client/encrypt.c
new file mode 100644
index 000000000..eefa57237
--- /dev/null
+++ b/private/net/svcdlls/fpnw/client/encrypt.c
@@ -0,0 +1,314 @@
+
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ encrypt.c
+
+Abstract:
+
+ This module implements the routines for the NetWare
+ redirector to mangle an objectid, challenge key and
+ password such that a NetWare server will accept the
+ password as valid.
+
+ This program uses information published in Byte Magazine.
+
+Author:
+
+ Colin Watson [ColinW] 15-Mar-1993
+ Andy Herron [AndyHe]
+
+Revision History:
+
+--*/
+
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windows.h>
+#include <nwsutil.h>
+#include <usrprop.h>
+#include <crypt.h>
+#include <fpnwcomm.h>
+
+UCHAR Table[] =
+{0x7,0x8,0x0,0x8,0x6,0x4,0xE,0x4,0x5,0xC,0x1,0x7,0xB,0xF,0xA,0x8,
+ 0xF,0x8,0xC,0xC,0x9,0x4,0x1,0xE,0x4,0x6,0x2,0x4,0x0,0xA,0xB,0x9,
+ 0x2,0xF,0xB,0x1,0xD,0x2,0x1,0x9,0x5,0xE,0x7,0x0,0x0,0x2,0x6,0x6,
+ 0x0,0x7,0x3,0x8,0x2,0x9,0x3,0xF,0x7,0xF,0xC,0xF,0x6,0x4,0xA,0x0,
+ 0x2,0x3,0xA,0xB,0xD,0x8,0x3,0xA,0x1,0x7,0xC,0xF,0x1,0x8,0x9,0xD,
+ 0x9,0x1,0x9,0x4,0xE,0x4,0xC,0x5,0x5,0xC,0x8,0xB,0x2,0x3,0x9,0xE,
+ 0x7,0x7,0x6,0x9,0xE,0xF,0xC,0x8,0xD,0x1,0xA,0x6,0xE,0xD,0x0,0x7,
+ 0x7,0xA,0x0,0x1,0xF,0x5,0x4,0xB,0x7,0xB,0xE,0xC,0x9,0x5,0xD,0x1,
+ 0xB,0xD,0x1,0x3,0x5,0xD,0xE,0x6,0x3,0x0,0xB,0xB,0xF,0x3,0x6,0x4,
+ 0x9,0xD,0xA,0x3,0x1,0x4,0x9,0x4,0x8,0x3,0xB,0xE,0x5,0x0,0x5,0x2,
+ 0xC,0xB,0xD,0x5,0xD,0x5,0xD,0x2,0xD,0x9,0xA,0xC,0xA,0x0,0xB,0x3,
+ 0x5,0x3,0x6,0x9,0x5,0x1,0xE,0xE,0x0,0xE,0x8,0x2,0xD,0x2,0x2,0x0,
+ 0x4,0xF,0x8,0x5,0x9,0x6,0x8,0x6,0xB,0xA,0xB,0xF,0x0,0x7,0x2,0x8,
+ 0xC,0x7,0x3,0xA,0x1,0x4,0x2,0x5,0xF,0x7,0xA,0xC,0xE,0x5,0x9,0x3,
+ 0xE,0x7,0x1,0x2,0xE,0x1,0xF,0x4,0xA,0x6,0xC,0x6,0xF,0x4,0x3,0x0,
+ 0xC,0x0,0x3,0x6,0xF,0x8,0x7,0xB,0x2,0xD,0xC,0x6,0xA,0xA,0x8,0xD};
+
+UCHAR Keys[32] =
+{0x48,0x93,0x46,0x67,0x98,0x3D,0xE6,0x8D,
+ 0xB7,0x10,0x7A,0x26,0x5A,0xB9,0xB1,0x35,
+ 0x6B,0x0F,0xD5,0x70,0xAE,0xFB,0xAD,0x11,
+ 0xF4,0x47,0xDC,0xA7,0xEC,0xCF,0x50,0xC0};
+
+#define XorArray( DEST, SRC ) { \
+ PULONG D = (PULONG)DEST; \
+ PULONG S = (PULONG)SRC; \
+ int i; \
+ for ( i = 0; i <= 7 ; i++ ) { \
+ D[i] ^= S[i]; \
+ } \
+}
+
+int
+Scramble(
+ int iSeed,
+ UCHAR achBuffer[32]
+ );
+
+VOID
+Shuffle(
+ UCHAR *achObjectId,
+ UCHAR *szUpperPassword,
+ int iPasswordLen,
+ UCHAR *achOutputBuffer
+ )
+
+/*++
+
+Routine Description:
+
+ This routine shuffles around the object ID with the password
+
+Arguments:
+
+ IN achObjectId - Supplies the 4 byte user's bindery object id
+
+ IN szUpperPassword - Supplies the user's uppercased password on the
+ first call to process the password. On the second and third calls
+ this parameter contains the OutputBuffer from the first call
+
+ IN iPasswordLen - length of uppercased password
+
+ OUT achOutputBuffer - Returns the 8 byte sub-calculation
+
+Return Value:
+
+ none.
+
+--*/
+
+{
+ int iTempIndex;
+ int iOutputIndex;
+ UCHAR achTemp[32];
+
+ //
+ // Truncate all trailing zeros from the password.
+ //
+
+ while (iPasswordLen > 0 && szUpperPassword[iPasswordLen-1] == 0 ) {
+ iPasswordLen--;
+ }
+
+ //
+ // Initialize the achTemp buffer. Initialization consists of taking
+ // the password and dividing it up into chunks of 32. Any bytes left
+ // over are the remainder and do not go into the initialization.
+ //
+ // achTemp[0] = szUpperPassword[0] ^ szUpperPassword[32] ^ szUpper...
+ // achTemp[1] = szUpperPassword[1] ^ szUpperPassword[33] ^ szUpper...
+ // etc.
+ //
+
+ if ( iPasswordLen > 32) {
+
+ // At least one chunk of 32. Set the buffer to the first chunk.
+
+ RtlCopyMemory( achTemp, szUpperPassword, 32 );
+
+ szUpperPassword +=32; // Remove the first chunk
+ iPasswordLen -=32;
+
+ while ( iPasswordLen >= 32 ) {
+ //
+ // Xor this chunk with the characters already loaded into
+ // achTemp.
+ //
+
+ XorArray( achTemp, szUpperPassword);
+
+ szUpperPassword +=32; // Remove this chunk
+ iPasswordLen -=32;
+ }
+
+ } else {
+
+ // No chunks of 32 so set the buffer to zero's
+
+ RtlZeroMemory( achTemp, sizeof(achTemp));
+
+ }
+
+ //
+ // achTemp is now initialized. Load the remainder into achTemp.
+ // The remainder is repeated to fill achTemp.
+ //
+ // The corresponding character from Keys is taken to seperate
+ // each repitition.
+ //
+ // As an example, take the remainder "ABCDEFG". The remainder is expanded
+ // to "ABCDEFGwABCDEFGxABCDEFGyABCDEFGz" where w is Keys[7],
+ // x is Keys[15], y is Keys[23] and z is Keys[31].
+ //
+ //
+
+ if (iPasswordLen > 0) {
+ int iPasswordOffset = 0;
+ for (iTempIndex = 0; iTempIndex < 32; iTempIndex++) {
+
+ if (iPasswordLen == iPasswordOffset) {
+ iPasswordOffset = 0;
+ achTemp[iTempIndex] ^= Keys[iTempIndex];
+ } else {
+ achTemp[iTempIndex] ^= szUpperPassword[iPasswordOffset++];
+ }
+ }
+ }
+
+ //
+ // achTemp has been loaded with the users password packed into 32
+ // bytes. Now take the objectid that came from the server and use
+ // that to munge every byte in achTemp.
+ //
+
+ for (iTempIndex = 0; iTempIndex < 32; iTempIndex++)
+ achTemp[iTempIndex] ^= achObjectId[ iTempIndex & 3];
+
+ Scramble( Scramble( 0, achTemp ), achTemp );
+
+ //
+ // Finally take pairs of bytes in achTemp and return the two
+ // nibbles obtained from Table. The pairs of bytes used
+ // are achTemp[n] and achTemp[n+16].
+ //
+
+ for (iOutputIndex = 0; iOutputIndex < 16; iOutputIndex++) {
+
+ achOutputBuffer[iOutputIndex] =
+ Table[achTemp[iOutputIndex << 1]] |
+ (Table[achTemp[(iOutputIndex << 1) + 1]] << 4);
+ }
+
+ return;
+}
+
+int
+Scramble(
+ int iSeed,
+ UCHAR achBuffer[32]
+ )
+
+/*++
+
+Routine Description:
+
+ This routine scrambles around the contents of the buffer. Each buffer
+ position is updated to include the contents of at least two character
+ positions plus an EncryptKey value. The buffer is processed left to right
+ and so if a character position chooses to merge with a buffer position
+ to its left then this buffer position will include bits derived from at
+ least 3 bytes of the original buffer contents.
+
+Arguments:
+
+ IN iSeed
+ IN OUT achBuffer[32]
+
+Return Value:
+
+ none.
+
+--*/
+
+{
+ int iBufferIndex;
+
+ for (iBufferIndex = 0; iBufferIndex < 32; iBufferIndex++) {
+ achBuffer[iBufferIndex] =
+ (UCHAR)(
+ ((UCHAR)(achBuffer[iBufferIndex] + iSeed)) ^
+ ((UCHAR)( achBuffer[(iBufferIndex+iSeed) & 31] -
+ Keys[iBufferIndex] )));
+
+ iSeed += achBuffer[iBufferIndex];
+ }
+ return iSeed;
+}
+
+NTSTATUS
+ReturnNetwareForm(
+ const char * pszSecretValue,
+ DWORD dwUserId,
+ const WCHAR * pchNWPassword,
+ UCHAR * pchEncryptedNWPassword
+ )
+
+/*++
+
+Routine Description:
+
+ This routine takes the ObjectId and encrypts it with the user
+ supplied password to develop a credential for the intermediate form.
+
+Arguments:
+ DWORD dwUserId - Supplies the 4 byte user's object id
+ const WCHAR * pchNWPassword - Supplies the user's password
+
+ UCHAR * pchEncryptedNWPassword - 16 characters where the result goes.
+
+Return Value:
+
+ none.
+
+--*/
+
+{
+ DWORD dwStatus;
+ DWORD chObjectId = SWAP_OBJECT_ID (dwUserId);
+ UNICODE_STRING uniNWPassword;
+ OEM_STRING oemNWPassword;
+
+ //
+ // shuffle actually uses 32 bytes, not just 16. It only returns 16 though.
+ //
+
+ UCHAR pszShuffledNWPassword[NT_OWF_PASSWORD_LENGTH * 2];
+
+ uniNWPassword.Buffer = (WCHAR *) pchNWPassword;
+ uniNWPassword.Length = lstrlenW (pchNWPassword)*sizeof(WCHAR);
+ uniNWPassword.MaximumLength = uniNWPassword.Length;
+
+ if ((dwStatus = RtlUpcaseUnicodeStringToOemString (&oemNWPassword,
+ &uniNWPassword,
+ TRUE)) == STATUS_SUCCESS)
+ {
+ Shuffle((UCHAR *) &chObjectId, oemNWPassword.Buffer, oemNWPassword.Length, pszShuffledNWPassword);
+
+ // Encrypt with LSA secret.
+ dwStatus = RtlEncryptNtOwfPwdWithUserKey(
+ (PNT_OWF_PASSWORD) pszShuffledNWPassword,
+ (PUSER_SESSION_KEY) pszSecretValue,
+ (PENCRYPTED_NT_OWF_PASSWORD) pchEncryptedNWPassword);
+ }
+
+ return (dwStatus);
+}
diff --git a/private/net/svcdlls/fpnw/client/fpnwclnt.def b/private/net/svcdlls/fpnw/client/fpnwclnt.def
new file mode 100644
index 000000000..551897318
--- /dev/null
+++ b/private/net/svcdlls/fpnw/client/fpnwclnt.def
@@ -0,0 +1,56 @@
+LIBRARY FPNWCLNT
+
+DESCRIPTION 'File and Print for Netware Client DLL'
+
+EXPORTS
+
+ NwApiBufferFree
+ NwServerGetInfo
+ NwServerSetInfo
+ NwVolumeAdd
+ NwVolumeDel
+ NwVolumeEnum
+ NwVolumeGetInfo
+ NwVolumeSetInfo
+ NwConnectionEnum
+ NwConnectionDel
+ NwVolumeConnEnum
+ NwFileEnum
+ NwFileClose
+ NwMessageBufferSend
+ NwSetDefaultQueue
+ NwAddPServer
+ NwRemovePServer
+ FpnwApiBufferFree
+ FpnwServerGetInfo
+ FpnwServerSetInfo
+ FpnwVolumeAdd
+ FpnwVolumeDel
+ FpnwVolumeEnum
+ FpnwVolumeGetInfo
+ FpnwVolumeSetInfo
+ FpnwConnectionEnum
+ FpnwConnectionDel
+ FpnwVolumeConnEnum
+ FpnwFileEnum
+ FpnwFileClose
+ FpnwMessageBufferSend
+ FpnwSetDefaultQueue
+ FpnwAddPServer
+ FpnwRemovePServer
+ SetUserProperty
+ SetUserPropertyWithLength
+ QueryUserProperty
+ QueryUserPropertyWithLength
+ IsNetWareInstalled
+ ReturnNetwareForm
+ GetNcpSecretKey
+ GetRemoteNcpSecretKey
+ Shuffle
+ Msv1_0SubAuthenticationRoutine
+ Msv1_0SubAuthenticationRoutine2
+ MapRidToObjectId
+ SwapObjectId
+ PasswordChangeNotify
+ DeltaNotify
+ InitializeChangeNotify
diff --git a/private/net/svcdlls/fpnw/client/fpnwclnt.rc b/private/net/svcdlls/fpnw/client/fpnwclnt.rc
new file mode 100644
index 000000000..574877690
--- /dev/null
+++ b/private/net/svcdlls/fpnw/client/fpnwclnt.rc
@@ -0,0 +1,10 @@
+#include <windows.h>
+
+#include <ntverp.h>
+
+#define VER_FILETYPE VFT_DLL
+#define VER_FILESUBTYPE VFT2_UNKNOWN
+#define VER_FILEDESCRIPTION_STR "FPNW Client DLL"
+#define VER_INTERNALNAME_STR "fpnwclnt.dll"
+
+#include "common.ver"
diff --git a/private/net/svcdlls/fpnw/client/logon.c b/private/net/svcdlls/fpnw/client/logon.c
new file mode 100644
index 000000000..ca5204fba
--- /dev/null
+++ b/private/net/svcdlls/fpnw/client/logon.c
@@ -0,0 +1,1131 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+
+Module Name:
+
+ logon.c
+
+Abstract:
+
+ This module contains the routines called by MSV1_0 authentication package.
+
+Author:
+
+ Yi-Hsin Sung (yihsins)
+ Andy Herron (andyhe) 06-Jun-1994 Added support for MSV1_0 subauthority
+
+Revision History:
+
+ Andy Herron (andyhe) 15-Aug-1994 Pulled out (older) unused MSV1_0
+ subauthority routines.
+--*/
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <ntsam.h>
+#include <windows.h>
+#include <ntmsv1_0.h>
+#include <crypt.h>
+#include <usrprop.h>
+#include <samrpc.h>
+#include <samisrv.h>
+#include <ntlsa.h>
+#include <lsarpc.h>
+#include <lsaisrv.h>
+#include <lmcons.h>
+#include <logonmsv.h>
+
+#define RESPONSE_SIZE 8
+#define WKSTA_ADDRESS_SIZE 20
+#define NET_ADDRESS_SIZE 8
+#define NODE_ADDRESS_SIZE 12
+
+#define MSV1_0_PASSTHRU 0x01
+#define MSV1_0_GUEST_LOGON 0x02
+
+#ifndef LOGON_SUBAUTH_SESSION_KEY
+#define LOGON_SUBAUTH_SESSION_KEY 0x40
+#endif
+
+ULONG
+MapRidToObjectId(
+ DWORD dwRid,
+ LPWSTR pszUserName,
+ BOOL fNTAS,
+ BOOL fBuiltin );
+
+//
+// These are never closed once they're opened. This is similar to how
+// msv1_0 does it. Since there's no callback at shutdown time, we have no
+// way of knowing when to close them.
+//
+
+HANDLE SamDomainHandle = NULL;
+SAMPR_HANDLE SamConnectHandle = NULL;
+LSA_HANDLE LsaPolicyHandle = NULL;
+
+//
+// This is where we store out LSA Secret
+//
+
+BOOLEAN GotSecret = FALSE;
+UCHAR LsaSecretBuffer[USER_SESSION_KEY_LENGTH + 1];
+
+//
+// forward declare
+//
+
+BOOLEAN
+MNSWorkstationValidate (
+ IN PUNICODE_STRING Workstation,
+ IN PUNICODE_STRING UserParameters
+ );
+
+BOOL
+GetPasswordExpired(
+ IN LARGE_INTEGER PasswordLastSet,
+ IN LARGE_INTEGER MaxPasswordAge
+ );
+
+NTSTATUS
+QueryDomainPasswordInfo (
+ PSAMPR_DOMAIN_INFO_BUFFER *DomainInfo
+ );
+
+VOID
+Shuffle(
+ UCHAR *achObjectId,
+ UCHAR *szUpperPassword,
+ int iPasswordLen,
+ UCHAR *achOutputBuffer
+ );
+
+NTSTATUS GetNcpSecretKey( CHAR *pchNWSecretKey );
+
+
+NTSTATUS
+Msv1_0SubAuthenticationRoutine2 (
+ IN NETLOGON_LOGON_INFO_CLASS LogonLevel,
+ IN PVOID LogonInformation,
+ IN ULONG Flags,
+ IN PUSER_ALL_INFORMATION UserAll,
+ OUT PULONG WhichFields,
+ OUT PULONG UserFlags,
+ OUT PBOOLEAN Authoritative,
+ OUT PLARGE_INTEGER LogoffTime,
+ OUT PLARGE_INTEGER KickoffTime,
+ OUT PUSER_SESSION_KEY UserSessionKey OPTIONAL
+)
+/*++
+
+Routine Description:
+
+ The subauthentication routine does cient/server specific authentication
+ of a user. The credentials of the user are passed in addition to all the
+ information from SAM defining the user. This routine decides whether to
+ let the user logon.
+
+
+Arguments:
+
+ LogonLevel -- Specifies the level of information given in
+ LogonInformation.
+
+ LogonInformation -- Specifies the description for the user
+ logging on. The LogonDomainName field should be ignored.
+
+ Flags - Flags describing the circumstances of the logon.
+
+ MSV1_0_PASSTHRU -- This is a PassThru authenication. (i.e., the
+ user isn't connecting to this machine.)
+ MSV1_0_GUEST_LOGON -- This is a retry of the logon using the GUEST
+ user account.
+
+ UserAll -- The description of the user as returned from SAM.
+
+ WhichFields -- Returns which fields from UserAllInfo are to be written
+ back to SAM. The fields will only be written if MSV returns success
+ to it's caller. Only the following bits are valid.
+
+ USER_ALL_PARAMETERS - Write UserAllInfo->Parameters back to SAM. If
+ the size of the buffer is changed, Msv1_0SubAuthenticationRoutine
+ must delete the old buffer using MIDL_user_free() and reallocate the
+ buffer using MIDL_user_allocate().
+
+ UserFlags -- Returns UserFlags to be returned from LsaLogonUser in the
+ LogonProfile. The following bits are currently defined:
+
+
+ LOGON_GUEST -- This was a guest logon
+ LOGON_NOENCRYPTION -- The caller didn't specify encrypted credentials
+ LOGON_GRACE_LOGON -- The caller's password has expired but logon
+ was allowed during a grace period following the expiration.
+ LOGON_SUBAUTH_SESSION_KEY - a session key was returned from this
+ logon
+
+ SubAuthentication packages should restrict themselves to returning
+ bits in the high order byte of UserFlags. However, this convention
+ isn't enforced giving the SubAuthentication package more flexibility.
+
+ Authoritative -- Returns whether the status returned is an
+ authoritative status which should be returned to the original
+ caller. If not, this logon request may be tried again on another
+ domain controller. This parameter is returned regardless of the
+ status code.
+
+ LogoffTime - Receives the time at which the user should logoff the
+ system. This time is specified as a GMT relative NT system time.
+
+ KickoffTime - Receives the time at which the user should be kicked
+ off the system. This time is specified as a GMT relative NT system
+ time. Specify, a full scale positive number if the user isn't to
+ be kicked off.
+
+ UserSessionKey - If non-null, recives a session key for this logon
+ session.
+
+
+Return Value:
+
+ STATUS_SUCCESS: if there was no error.
+
+ STATUS_NO_SUCH_USER: The specified user has no account.
+ STATUS_WRONG_PASSWORD: The password was invalid.
+
+ STATUS_INVALID_INFO_CLASS: LogonLevel is invalid.
+ STATUS_ACCOUNT_LOCKED_OUT: The account is locked out
+ STATUS_ACCOUNT_DISABLED: The account is disabled
+ STATUS_ACCOUNT_EXPIRED: The account has expired.
+ STATUS_PASSWORD_MUST_CHANGE: Account is marked as Password must change
+ on next logon.
+ STATUS_PASSWORD_EXPIRED: The Password is expired.
+ STATUS_INVALID_LOGON_HOURS - The user is not authorized to logon at
+ this time.
+ STATUS_INVALID_WORKSTATION - The user is not authorized to logon to
+ the specified workstation.
+
+--*/
+{
+ NTSTATUS status;
+ ULONG UserAccountControl;
+ LARGE_INTEGER LogonTime;
+ WCHAR PropertyFlag;
+ NT_OWF_PASSWORD DecryptedPassword;
+ UCHAR Response[RESPONSE_SIZE];
+ UNICODE_STRING EncryptedPassword;
+ UNICODE_STRING PasswordDateSet;
+ UNICODE_STRING GraceLoginRemaining;
+ SAMPR_HANDLE UserHandle;
+ LARGE_INTEGER pwSetTime;
+ PSAMPR_DOMAIN_INFO_BUFFER DomainInfo;
+ PSAMPR_USER_INFO_BUFFER userControlInfo;
+ LPWSTR pNewUserParams;
+ int index;
+ UCHAR achK[32];
+ PNETLOGON_NETWORK_INFO LogonNetworkInfo;
+ PCHAR challenge;
+ BOOLEAN authoritative = TRUE; // important default!
+ ULONG userFlags = 0; // important default!
+ ULONG whichFields = 0; // important default!
+ LARGE_INTEGER logoffTime;
+ LARGE_INTEGER kickoffTime;
+
+ pNewUserParams = NULL;
+ DomainInfo = NULL;
+ GraceLoginRemaining.Buffer = NULL;
+ PasswordDateSet.Buffer = NULL;
+ EncryptedPassword.Buffer = NULL;
+ userControlInfo = NULL;
+
+ logoffTime.HighPart = 0x7FFFFFFF; // default to no kickoff and
+ logoffTime.LowPart = 0xFFFFFFFF; // no forced logoff
+ kickoffTime.HighPart = 0x7FFFFFFF;
+ kickoffTime.LowPart = 0xFFFFFFFF;
+
+ (VOID) NtQuerySystemTime( &LogonTime );
+
+ //
+ // Check whether the SubAuthentication package supports this type
+ // of logon.
+ //
+
+ if ( LogonLevel != NetlogonNetworkInformation ) {
+
+ //
+ // This SubAuthentication package only supports network logons.
+ //
+
+ status = STATUS_INVALID_INFO_CLASS;
+ goto CleanUp;
+ }
+
+ //
+ // This SubAuthentication package doesn't support access via machine
+ // accounts.
+ //
+
+ UserAccountControl = USER_NORMAL_ACCOUNT;
+
+ //
+ // Local user (Temp Duplicate) accounts are only used on the machine
+ // being directly logged onto.
+ // (Nor are interactive or service logons allowed to them.)
+ //
+
+ if ( (Flags & MSV1_0_PASSTHRU) == 0 ) {
+ UserAccountControl |= USER_TEMP_DUPLICATE_ACCOUNT;
+ }
+
+ LogonNetworkInfo = (PNETLOGON_NETWORK_INFO) LogonInformation;
+
+ //
+ // If the account type isn't allowed,
+ // Treat this as though the User Account doesn't exist.
+ //
+ // This SubAuthentication package doesn't allow guest logons.
+ //
+
+ if ( ( (UserAccountControl & UserAll->UserAccountControl) == 0 ) ||
+ ( Flags & MSV1_0_GUEST_LOGON ) ) {
+
+ authoritative = FALSE;
+ status = STATUS_NO_SUCH_USER;
+ goto CleanUp;
+ }
+
+ //
+ // Ensure the account isn't locked out.
+ //
+
+ if ( UserAll->UserId != DOMAIN_USER_RID_ADMIN &&
+ (UserAll->UserAccountControl & USER_ACCOUNT_AUTO_LOCKED) ) {
+
+ //
+ // Since the UI strongly encourages admins to disable user
+ // accounts rather than delete them. Treat disabled acccount as
+ // non-authoritative allowing the search to continue for other
+ // accounts by the same name.
+ //
+ if ( UserAll->UserAccountControl & USER_ACCOUNT_DISABLED ) {
+ authoritative = FALSE;
+ }
+ status = STATUS_ACCOUNT_LOCKED_OUT;
+ goto CleanUp;
+ }
+
+ //
+ // Get the encrypted password from the user parms field
+ //
+
+ status = QueryUserPropertyWithLength( &UserAll->Parameters,
+ NWPASSWORD,
+ &PropertyFlag,
+ &EncryptedPassword );
+
+ if ( !NT_SUCCESS( status )) {
+
+ goto CleanUp;
+ }
+
+ //
+ // If the user does not have a netware password, fail the login
+ //
+
+ if ( EncryptedPassword.Length == 0 ) {
+
+ status = STATUS_NO_SUCH_USER;
+ goto CleanUp;
+ }
+
+ //
+ // Read our LSA secret if we haven't already
+ //
+
+ if (! GotSecret) {
+
+ status = GetNcpSecretKey( &LsaSecretBuffer[0] );
+
+ if (! NT_SUCCESS(status)) {
+
+ goto CleanUp;
+ }
+
+ GotSecret = TRUE;
+ }
+
+ //
+ // Decrypt the password with NetwareLsaSecret to get the one way form
+ //
+
+ status = RtlDecryptNtOwfPwdWithUserKey(
+ (PENCRYPTED_NT_OWF_PASSWORD) EncryptedPassword.Buffer,
+ (PUSER_SESSION_KEY) &LsaSecretBuffer[0],
+ &DecryptedPassword );
+
+ if ( !NT_SUCCESS( status )) {
+
+ goto CleanUp;
+ }
+
+ //
+ // Get the response to challenge. We do this by finishing off the
+ // password encryption algorithm here.
+ //
+
+ challenge = (PCHAR) &(LogonNetworkInfo->LmChallenge);
+
+ Shuffle( challenge, (UCHAR *) &(DecryptedPassword.data), 16, &achK[0] );
+ Shuffle( challenge+4, (UCHAR *) &(DecryptedPassword.data), 16, &achK[16] );
+
+ for (index = 0; index < 16; index++) {
+ achK[index] ^= achK[31-index];
+ }
+
+ for (index = 0; index < RESPONSE_SIZE; index++) {
+ Response[index] = achK[index] ^ achK[15-index];
+ }
+
+ if ( memcmp( Response,
+ (PCHAR) (LogonNetworkInfo->LmChallengeResponse.Buffer),
+ RESPONSE_SIZE ) != 0 ) {
+
+ //
+ // Since the UI strongly encourages admins to disable user
+ // accounts rather than delete them. Treat disabled acccount as
+ // non-authoritative allowing the search to continue for other
+ // accounts by the same name.
+ //
+
+ if ( UserAll->UserAccountControl & USER_ACCOUNT_DISABLED ) {
+ authoritative = FALSE;
+ }
+
+ //
+ // if the user tried to use a NULL password, don't note this as
+ // a bad password attempt since LOGON.EXE does this by default.
+ // Instead, map it to STATUS_LOGON_FAILURE.
+ //
+
+ {
+ UCHAR pszShuffledNWPassword[NT_OWF_PASSWORD_LENGTH * 2];
+ DWORD chObjectId;
+ NT_PRODUCT_TYPE ProductType;
+ DWORD dwUserId;
+
+ //
+ // first we calculate what the user's Object ID is...
+ //
+
+ RtlGetNtProductType( &ProductType );
+ dwUserId = MapRidToObjectId(
+ UserAll->UserId,
+ UserAll->UserName.Buffer,
+ ProductType == NtProductLanManNt,
+ FALSE );
+ chObjectId = SWAP_OBJECT_ID (dwUserId);
+
+ //
+ // then we calculate the user's password residue with a null
+ // password
+ //
+
+ RtlZeroMemory( &pszShuffledNWPassword, NT_OWF_PASSWORD_LENGTH * 2 );
+
+ Shuffle( (UCHAR *) &chObjectId, NULL, 0, pszShuffledNWPassword );
+
+ //
+ // we then finish off the encryption as we did above for the
+ // password in the user's record.
+ //
+
+ challenge = (PCHAR) &(LogonNetworkInfo->LmChallenge);
+
+ Shuffle( challenge, pszShuffledNWPassword, 16, &achK[0] );
+ Shuffle( challenge+4, pszShuffledNWPassword, 16, &achK[16] );
+
+ for (index = 0; index < 16; index++) {
+ achK[index] ^= achK[31-index];
+ }
+
+ for (index = 0; index < RESPONSE_SIZE; index++) {
+ Response[index] = achK[index] ^ achK[15-index];
+ }
+
+ //
+ // now if the password that the user sent in matches the encrypted
+ // form of the null password, we exit with a generic return code
+ // that won't cause the user's record to be updated. This will
+ // also cause LSA to not wait for 3 seconds to return the error
+ // (which is a good thing in this case).
+ //
+
+ if ( memcmp( Response,
+ (PCHAR) (LogonNetworkInfo->LmChallengeResponse.Buffer),
+ RESPONSE_SIZE ) == 0 ) {
+
+ status = STATUS_LOGON_FAILURE;
+
+ } else {
+
+ status = STATUS_WRONG_PASSWORD;
+ }
+ }
+ goto CleanUp;
+ }
+
+ //
+ // Prevent rest of things from effecting the Administrator user
+ //
+
+ if (UserAll->UserId == DOMAIN_USER_RID_ADMIN) {
+
+ status = STATUS_SUCCESS;
+ goto CleanUp;
+ }
+
+ //
+ // Check if the account is disabled.
+ //
+
+ if ( UserAll->UserAccountControl & USER_ACCOUNT_DISABLED ) {
+
+ //
+ // Since the UI strongly encourages admins to disable user
+ // accounts rather than delete them. Treat disabled acccount as
+ // non-authoritative allowing the search to continue for other
+ // accounts by the same name.
+ //
+
+ authoritative = FALSE;
+ status = STATUS_ACCOUNT_DISABLED;
+ goto CleanUp;
+ }
+
+ //
+ // Check if the account has expired.
+ //
+
+ if (UserAll->AccountExpires.QuadPart > 0 &&
+ LogonTime.QuadPart >= UserAll->AccountExpires.QuadPart ) {
+
+ status = STATUS_ACCOUNT_EXPIRED;
+ goto CleanUp;
+ }
+
+ status = QueryDomainPasswordInfo( &DomainInfo );
+
+ if ( !NT_SUCCESS( status )) {
+
+ goto CleanUp;
+ }
+
+ //
+ // Response is correct. So, check if the password has expired or not
+ //
+
+ if (! (UserAll->UserAccountControl & USER_DONT_EXPIRE_PASSWORD)) {
+
+ status = QueryUserPropertyWithLength( &UserAll->Parameters,
+ NWTIMEPASSWORDSET,
+ &PropertyFlag,
+ &PasswordDateSet );
+ if ( !NT_SUCCESS( status ) ||
+ PasswordDateSet.Length < sizeof(LARGE_INTEGER) ) {
+
+ // date last password was set was not present.... hmmm.
+ // we won't update anything here but let someone know all
+ // is not kosher by making this a grace login.
+
+ userFlags = LOGON_GRACE_LOGON;
+
+ } else {
+
+ pwSetTime = *((PLARGE_INTEGER)(PasswordDateSet.Buffer));
+
+ if ( (pwSetTime.HighPart == 0xFFFF &&
+ pwSetTime.LowPart == 0xFFFF ) ||
+ GetPasswordExpired( pwSetTime,
+ DomainInfo->Password.MaxPasswordAge )) {
+
+ //
+ // Password has expired, so check if grace login is still allowed
+ //
+
+ userFlags = LOGON_GRACE_LOGON;
+
+ //
+ // if this is a password validate rather than an
+ // actual login, don't update/check grace logins.
+ //
+
+ if ( LogonNetworkInfo->Identity.Workstation.Length > 0 ) {
+
+ status = QueryUserPropertyWithLength( &UserAll->Parameters,
+ GRACELOGINREMAINING,
+ &PropertyFlag,
+ &GraceLoginRemaining );
+
+ if ( ! NT_SUCCESS( status ) ) {
+
+ //
+ // The grace login value cannot be determined.
+ //
+
+ goto CleanUp;
+
+ } else if ( ( GraceLoginRemaining.Length != 0 ) &&
+ ( *(GraceLoginRemaining.Buffer) > 0 ) ) {
+
+ //
+ // Password has expired and grace login is available.
+ // So, return success and decrease the grace login remaining
+ // in the user parms field.
+ //
+
+ BOOL fUpdate;
+
+ (*(GraceLoginRemaining.Buffer))--;
+
+ status = SetUserProperty( UserAll->Parameters.Buffer,
+ GRACELOGINREMAINING,
+ GraceLoginRemaining,
+ USER_PROPERTY_TYPE_ITEM,
+ &pNewUserParams,
+ &fUpdate );
+
+ if ( NT_SUCCESS( status) &&
+ fUpdate ) {
+
+ //
+ // if we actually updated the parms, mark as such.
+ //
+
+ whichFields = USER_ALL_PARAMETERS;
+
+ //
+ // The length of the parameters didn't grow... we
+ // know that because we started with a value and
+ // ended with the same value - 1 ( same length )
+ //
+
+ memcpy( UserAll->Parameters.Buffer,
+ pNewUserParams,
+ UserAll->Parameters.Length );
+ }
+ status = STATUS_SUCCESS;
+
+ } else {
+
+ status = STATUS_PASSWORD_EXPIRED;
+ goto CleanUp;
+ }
+ }
+ }
+ }
+ }
+
+ //
+ // To validate the user's logon hours, we must have a handle to the user.
+ // We'll open the user here.
+ //
+
+ UserHandle = NULL;
+
+ status = SamrOpenUser( SamDomainHandle,
+ USER_READ_ACCOUNT,
+ UserAll->UserId,
+ &UserHandle );
+
+ if ( !NT_SUCCESS(status) ) {
+
+ KdPrint(( "FPNWCLNT: Cannot SamrOpenUser %lX\n", status));
+ goto CleanUp;
+ }
+
+ //
+ // Validate the user's logon hours.
+ //
+
+ status = SamIAccountRestrictions( UserHandle,
+ NULL, // workstation id
+ NULL, // workstation list
+ &UserAll->LogonHours,
+ &logoffTime,
+ &kickoffTime
+ );
+ SamrCloseHandle( &UserHandle );
+
+ if ( ! NT_SUCCESS( status )) {
+ goto CleanUp;
+ }
+
+ //
+ // Validate if the user can logon from this workstation.
+ // (Supply subauthentication package specific code here.)
+
+ if ( ! MNSWorkstationValidate( &LogonNetworkInfo->Identity.Workstation,
+ &UserAll->Parameters ) ) {
+
+ status = STATUS_INVALID_WORKSTATION;
+ goto CleanUp;
+ }
+
+ //
+ // The user is valid. CleanUp up before returning.
+ //
+
+CleanUp:
+
+ //
+ // If we succeeded, create a session key. The session key is created
+ // by taking the decrypted password (a hash of the object id and
+ // cleartext password) and adding the index of each byte to each byte
+ // modulo 255, and using that to create a new challenge response from
+ // the old challenge response.
+ //
+
+ if (NT_SUCCESS(status) && (UserSessionKey != NULL)) {
+ UCHAR ChallengeResponse[NT_CHALLENGE_LENGTH];
+ PUCHAR Password = (PUCHAR) &DecryptedPassword.data;
+ PUCHAR SessionKey = (PUCHAR) UserSessionKey;
+
+ ASSERT(RESPONSE_SIZE >= NT_CHALLENGE_LENGTH);
+
+ RtlZeroMemory( UserSessionKey, sizeof(*UserSessionKey) );
+
+ RtlCopyMemory(
+ ChallengeResponse,
+ Response,
+ NT_CHALLENGE_LENGTH );
+
+ //
+ // Create the new password
+ //
+
+ for (index = 0; index < sizeof(DecryptedPassword) ; index++ ) {
+ Password[index] = Password[index] + (UCHAR) index;
+ }
+
+ //
+ // Use it to make a normal challenge response using the old challenge
+ // response
+ //
+
+ Shuffle( ChallengeResponse, (UCHAR *) &(DecryptedPassword.data), 16, &achK[0] );
+ Shuffle( ChallengeResponse+4, (UCHAR *) &(DecryptedPassword.data), 16, &achK[16] );
+
+ for (index = 0; index < 16; index++) {
+ achK[index] ^= achK[31-index];
+ }
+
+ for (index = 0; index < RESPONSE_SIZE; index++) {
+ SessionKey[index] = achK[index] ^ achK[15-index];
+ }
+ userFlags |= LOGON_SUBAUTH_SESSION_KEY;
+
+ }
+
+ if (DomainInfo != NULL) {
+ SamIFree_SAMPR_DOMAIN_INFO_BUFFER( DomainInfo, DomainPasswordInformation );
+ }
+ if (EncryptedPassword.Buffer == NULL) {
+ LocalFree( EncryptedPassword.Buffer );
+ }
+ if (PasswordDateSet.Buffer != NULL) {
+ LocalFree( PasswordDateSet.Buffer );
+ }
+ if (GraceLoginRemaining.Buffer != NULL) {
+ LocalFree( GraceLoginRemaining.Buffer );
+ }
+ if (pNewUserParams != NULL) {
+ LocalFree( pNewUserParams );
+ }
+
+ *Authoritative = authoritative;
+ *UserFlags = userFlags;
+ *WhichFields = whichFields;
+
+ LogoffTime->HighPart = logoffTime.HighPart;
+ LogoffTime->LowPart = logoffTime.LowPart;
+ KickoffTime->HighPart = kickoffTime.HighPart;
+ KickoffTime->LowPart = kickoffTime.LowPart;
+
+ return status;
+
+} // Msv1_0SubAuthenticationRoutine
+
+
+
+NTSTATUS
+Msv1_0SubAuthenticationRoutine (
+ IN NETLOGON_LOGON_INFO_CLASS LogonLevel,
+ IN PVOID LogonInformation,
+ IN ULONG Flags,
+ IN PUSER_ALL_INFORMATION UserAll,
+ OUT PULONG WhichFields,
+ OUT PULONG UserFlags,
+ OUT PBOOLEAN Authoritative,
+ OUT PLARGE_INTEGER LogoffTime,
+ OUT PLARGE_INTEGER KickoffTime
+)
+/*++
+
+Routine Description:
+
+ Compatibility wrapper for Msv1_0SubAuthenticationRoutine2.
+
+
+Arguments:
+
+ Same as Msv1_0SubAuthenticationRoutine2
+
+Return Value:
+
+ Same as Msv1_0SubAuthenticationRoutine2
+
+--*/
+{
+ return(Msv1_0SubAuthenticationRoutine2(
+ LogonLevel,
+ LogonInformation,
+ Flags,
+ UserAll,
+ WhichFields,
+ UserFlags,
+ Authoritative,
+ LogoffTime,
+ KickoffTime,
+ NULL // session key
+ ) );
+}
+
+BOOLEAN
+MNSWorkstationValidate (
+ IN PUNICODE_STRING Workstation,
+ IN PUNICODE_STRING UserParameters
+)
+{
+ NTSTATUS status;
+ WCHAR PropertyFlag;
+ UNICODE_STRING LogonWorkstations;
+ INT cbRequired;
+ INT cb;
+ LPWSTR pszTmp;
+
+ if ( Workstation->Length < (NET_ADDRESS_SIZE * sizeof(WCHAR)) ) {
+
+ //
+ // Zero is used when simply verifying a password.
+ //
+ // We also check that the length is enough so we dont
+ // blow up later. If for some reason a bad string is
+ // supplied, we pass it. This should never happen. Not a
+ // security hole as the user has no control over the string.
+ //
+
+ return(TRUE);
+ }
+
+ status = QueryUserPropertyWithLength( UserParameters,
+ NWLOGONFROM,
+ &PropertyFlag,
+ &LogonWorkstations );
+
+ if ( !NT_SUCCESS( status) || LogonWorkstations.Length == 0 ) {
+ return TRUE;
+ }
+
+ cbRequired = (LogonWorkstations.Length + 1) * sizeof(WCHAR);
+ pszTmp = LocalAlloc( LMEM_ZEROINIT, cbRequired);
+
+ if ( pszTmp == NULL ) {
+
+ //
+ // Not enough memory to allocate the buffer. Just
+ // let the user logon.
+ //
+
+ LocalFree( LogonWorkstations.Buffer );
+ return TRUE;
+ }
+
+ cb = MultiByteToWideChar( CP_ACP,
+ MB_PRECOMPOSED,
+ (const CHAR *) LogonWorkstations.Buffer,
+ LogonWorkstations.Length,
+ pszTmp,
+ cbRequired );
+
+ LocalFree( LogonWorkstations.Buffer ); // Don't need it any more
+
+ if ( cb > 1 )
+ {
+ USHORT TotalEntries = LogonWorkstations.Length/WKSTA_ADDRESS_SIZE;
+ WCHAR *pszEntry = pszTmp;
+ WCHAR *pszWksta = Workstation->Buffer ;
+
+ _wcsupr(pszEntry) ;
+ _wcsupr(pszWksta) ;
+
+ while ( TotalEntries > 0 )
+ {
+
+ //
+ // if net # is not wildcard, check for match
+ //
+ if (wcsncmp(L"FFFFFFFF", pszEntry, NET_ADDRESS_SIZE)!=0)
+ {
+ if (wcsncmp(pszWksta, pszEntry, NET_ADDRESS_SIZE)!=0)
+ {
+ //
+ // if no match, goto next entry
+ //
+ pszEntry += WKSTA_ADDRESS_SIZE;
+ TotalEntries--;
+ continue ;
+ }
+ }
+
+ //
+ // from above, net number passes. check node number.
+ // again, look for wildcard first.
+ //
+ if (wcsncmp(L"FFFFFFFFFFFF", pszEntry+NET_ADDRESS_SIZE,
+ NODE_ADDRESS_SIZE)!=0)
+ {
+ if (wcsncmp(pszEntry+NET_ADDRESS_SIZE,
+ pszWksta+NET_ADDRESS_SIZE,
+ NODE_ADDRESS_SIZE)!=0)
+ {
+ //
+ // if no match, goto next entry
+ //
+ pszEntry += WKSTA_ADDRESS_SIZE;
+ TotalEntries--;
+ continue ;
+ }
+ }
+
+ //
+ // found a match. return it.
+ //
+ LocalFree( pszTmp );
+ return TRUE;
+ }
+ } else {
+
+ //
+ // MultiByteToWideChar failed or empty string (ie. 1 char).
+ // Just let the user logon
+ //
+ LocalFree( pszTmp );
+ return TRUE;
+ }
+
+ LocalFree( pszTmp );
+ return FALSE;
+}
+
+BOOL
+GetPasswordExpired(
+ IN LARGE_INTEGER PasswordLastSet,
+ IN LARGE_INTEGER MaxPasswordAge
+ )
+
+/*++
+
+Routine Description:
+
+ This routine returns true if the password is expired, false otherwise.
+
+Arguments:
+
+ PasswordLastSet - Time when the password was last set for this user.
+
+ MaxPasswordAge - Maximum password age for any password in the domain.
+
+Return Value:
+
+ Returns true if password is expired. False if not expired.
+
+--*/
+{
+ LARGE_INTEGER PasswordMustChange;
+ NTSTATUS status;
+ BOOLEAN rc;
+ LARGE_INTEGER TimeNow;
+
+ //
+ // Compute the expiration time as the time the password was
+ // last set plus the maximum age.
+ //
+
+ if (PasswordLastSet.QuadPart < 0 ||
+ MaxPasswordAge.QuadPart > 0 ) {
+
+ rc = TRUE; // default for invalid times is that it is expired.
+
+ } else {
+
+ try {
+
+ PasswordMustChange.QuadPart = PasswordLastSet.QuadPart -
+ MaxPasswordAge.QuadPart;
+ //
+ // Limit the resultant time to the maximum valid absolute time
+ //
+
+ if ( PasswordMustChange.QuadPart < 0 ) {
+
+ rc = FALSE;
+
+ } else {
+
+ status = NtQuerySystemTime( &TimeNow );
+ if (NT_SUCCESS(status)) {
+
+ if ( RtlLargeIntegerGreaterThanOrEqualTo( TimeNow,
+ PasswordMustChange ) ) {
+ rc = TRUE;
+
+ } else {
+
+ rc = FALSE;
+ }
+ } else {
+ rc = FALSE; // won't fail if NtQuerySystemTime failed.
+ }
+ }
+
+ } except(EXCEPTION_EXECUTE_HANDLER) {
+
+ rc = TRUE;
+ }
+ }
+
+ return rc;
+}
+
+NTSTATUS
+QueryDomainPasswordInfo (
+ PSAMPR_DOMAIN_INFO_BUFFER *DomainInfo
+ )
+/*++
+
+ This routine opens a handle to sam so that we can get the max password
+ age.
+
+--*/
+{
+ NTSTATUS status;
+ OBJECT_ATTRIBUTES PolicyObjectAttributes;
+ PLSAPR_POLICY_INFORMATION PolicyAccountDomainInfo = NULL;
+
+ //
+ // if we don't yet have a domain handle, open domain handle so that
+ // we can query the domain's password expiration time.
+ //
+
+ if (SamDomainHandle == NULL) {
+
+ //
+ // Determine the DomainName and DomainId of the Account Database
+ //
+
+ if (LsaPolicyHandle == NULL) {
+
+ InitializeObjectAttributes( &PolicyObjectAttributes,
+ NULL, // Name
+ 0, // Attributes
+ NULL, // Root
+ NULL ); // Security Descriptor
+
+ status = LsaIOpenPolicyTrusted(&LsaPolicyHandle);
+
+ if ( !NT_SUCCESS(status) ) {
+
+ LsaPolicyHandle = NULL;
+ KdPrint(( "FPNWCLNT: Cannot LsaIOpenPolicyTrusted 0x%x\n", status));
+ goto CleanUp;
+ }
+ }
+
+ status = LsarQueryInformationPolicy( LsaPolicyHandle,
+ PolicyAccountDomainInformation,
+ &PolicyAccountDomainInfo );
+
+ if ( !NT_SUCCESS(status) ) {
+
+ KdPrint(( "FPNWCLNT: Cannot LsarQueryInformationPolicy 0x%x\n", status));
+ goto CleanUp;
+ }
+
+ if ( PolicyAccountDomainInfo->PolicyAccountDomainInfo.DomainSid == NULL ) {
+
+ status = STATUS_NO_SUCH_DOMAIN;
+
+ KdPrint(( "FPNWCLNT: Domain Sid is null 0x%x\n", status));
+ goto CleanUp;
+ }
+
+ //
+ // Open our connection with SAM
+ //
+
+ if (SamConnectHandle == NULL) {
+
+ status = SamIConnect( NULL, // No server name
+ &SamConnectHandle,
+ SAM_SERVER_CONNECT,
+ (BOOLEAN) TRUE ); // Indicate we are privileged
+
+ if ( !NT_SUCCESS(status) ) {
+
+ SamConnectHandle = NULL;
+
+ KdPrint(( "FPNWCLNT: Cannot SamIConnect 0x%x\n", status));
+ goto CleanUp;
+ }
+ }
+
+ //
+ // Open the domain.
+ //
+
+ status = SamrOpenDomain( SamConnectHandle,
+ DOMAIN_READ_OTHER_PARAMETERS,
+ (RPC_SID *) PolicyAccountDomainInfo->PolicyAccountDomainInfo.DomainSid,
+ &SamDomainHandle );
+
+ if ( !NT_SUCCESS(status) ) {
+
+ SamDomainHandle = NULL;
+ KdPrint(( "FPNWCLNT: Cannot SamrOpenDomain 0x%x\n", status));
+ goto CleanUp;
+ }
+ }
+
+ status = SamrQueryInformationDomain( SamDomainHandle,
+ DomainPasswordInformation,
+ DomainInfo );
+ if ( !NT_SUCCESS(status) ) {
+
+ KdPrint(( "FPNWCLNT: Cannot SamrQueryInformationDomain %lX\n", status));
+ goto CleanUp;
+ }
+
+CleanUp:
+
+ if (PolicyAccountDomainInfo != NULL) {
+ LsaIFree_LSAPR_POLICY_INFORMATION( PolicyAccountDomainInformation,
+ PolicyAccountDomainInfo );
+ }
+ return(status);
+
+} // QueryDomainPasswordInfo
+
+// logon.c eof.
+
diff --git a/private/net/svcdlls/fpnw/client/makefile b/private/net/svcdlls/fpnw/client/makefile
new file mode 100644
index 000000000..f0db8e4a7
--- /dev/null
+++ b/private/net/svcdlls/fpnw/client/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS LINE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/net/svcdlls/fpnw/client/makefile.inc b/private/net/svcdlls/fpnw/client/makefile.inc
new file mode 100644
index 000000000..8b1378917
--- /dev/null
+++ b/private/net/svcdlls/fpnw/client/makefile.inc
@@ -0,0 +1 @@
+
diff --git a/private/net/svcdlls/fpnw/client/ncpbind.c b/private/net/svcdlls/fpnw/client/ncpbind.c
new file mode 100644
index 000000000..2f04a134c
--- /dev/null
+++ b/private/net/svcdlls/fpnw/client/ncpbind.c
@@ -0,0 +1,117 @@
+/*++
+
+Copyright (c) 1990 Microsoft Corporation
+
+Module Name:
+
+ ncpbind.c
+
+Abstract:
+
+ Contains the RPC bind and un-bind routines for the NcpServer
+ Service.
+
+Author:
+
+ Dan Lafferty (danl) 01-Mar-1991
+
+Environment:
+
+ User Mode -Win32
+
+Revision History:
+
+ 01-Mar-1991 danl
+ created
+ 07-Jun-1991 JohnRo
+ Allowed debug output of failures.
+ 15-Nov-1993 Yi-Hsin
+ Modify for NcpServer.
+
+--*/
+
+//
+// INCLUDES
+//
+#include <nt.h> // DbgPrint prototype
+#include <rpc.h> // DataTypes and runtime APIs
+#include <ncpsvc.h> // generated by the MIDL complier
+#include <ntrpcp.h> // Rpc utils
+#include <srvnames.h> // SERVER_INTERFACE_NAME
+
+
+
+handle_t
+NCPSVC_HANDLE_bind (
+ NCPSVC_HANDLE ServerName
+ )
+
+/*++
+
+Routine Description:
+ This routine calls a common bind routine that is shared by all services.
+ This routine is called from the ncpserver service client stubs when
+ it is necessary to bind to a server.
+
+Arguments:
+
+ ServerName - A pointer to a string containing the name of the server
+ to bind with.
+
+Return Value:
+
+ The binding handle is returned to the stub routine. If the
+ binding is unsuccessful, a NULL will be returned.
+
+--*/
+{
+ handle_t bindingHandle;
+ RPC_STATUS status;
+
+ status = RpcpBindRpc( ServerName,
+ SERVER_INTERFACE_NAME,
+ // TEXT("Security=Impersonation Static True"),
+ TEXT("Security=Impersonation Dynamic False"),
+ &bindingHandle );
+
+#if DBG
+ if ( status != RPC_S_OK )
+ KdPrint(("NCPSVC_HANDLE_bind: RpcpBindRpc failed status=%lC\n",status));
+#endif
+
+ return( bindingHandle );
+}
+
+
+
+void
+NCPSVC_HANDLE_unbind (
+ NCPSVC_HANDLE ServerName,
+ handle_t BindingHandle
+ )
+/*++
+
+Routine Description:
+
+ This routine calls a common unbind routine that is shared by
+ all services.
+ This routine is called from the ncpserver service client stubs when
+ it is necessary to unbind to a server.
+
+
+Arguments:
+
+ ServerName - This is the name of the server from which to unbind.
+
+ BindingHandle - This is the binding handle that is to be closed.
+
+Return Value:
+
+ none.
+
+--*/
+{
+ UNREFERENCED_PARAMETER( ServerName ); // This parameter is not used
+
+ RpcpUnbindRpc ( BindingHandle );
+}
diff --git a/private/net/svcdlls/fpnw/client/ncpstub.c b/private/net/svcdlls/fpnw/client/ncpstub.c
new file mode 100644
index 000000000..90a30d086
--- /dev/null
+++ b/private/net/svcdlls/fpnw/client/ncpstub.c
@@ -0,0 +1,1435 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ ncpstub.c
+
+Abstract:
+
+ Contains NCP Server APIs.
+
+Author:
+
+ Yi-Hsin Sung (yihsins) 11-Sept-1993
+ Andy Herron (andyhe)
+
+Revision History:
+
+--*/
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <rpc.h>
+#include <ncpsvc.h>
+#include <nwstruct.h>
+
+DWORD NwpMapRpcError(
+ IN DWORD RpcError
+);
+
+
+DWORD
+FpnwApiBufferFree(
+ IN LPVOID pBuffer
+)
+/*++
+
+Routine Description:
+
+ This API frees the memory allocated by all enumeration and getinfo APIs.
+
+Arguments:
+
+ pBuffer - A pointer to an API information buffer previously returned
+ on an API call.
+
+Return Value:
+
+ Error.
+
+--*/
+{
+ if ( pBuffer == NULL )
+ return NO_ERROR;
+
+ MIDL_user_free( pBuffer );
+ return NO_ERROR;
+}
+
+DWORD
+NwApiBufferFree(
+ IN LPVOID pBuffer
+)
+{ return(FpnwApiBufferFree( pBuffer ));
+}
+
+
+
+DWORD
+FpnwServerGetInfo(
+ IN LPWSTR pServerName OPTIONAL,
+ IN DWORD dwLevel,
+ OUT LPBYTE *ppServerInfo
+)
+/*++
+
+Routine Description:
+
+ This API returns the information about the given server.
+
+Arguments:
+
+ pServerName - A pointer to a UNICODE string containing the name of the
+ remote server on which the function is to execute. A NULL pointer
+ or string specifies the local machine.
+
+ dwLevel - Reserved for the level of the server structure requested, use
+ 1 for now.
+
+ ppServerInfo - Place to store a pointer pointing to the returned
+ NWSERVERINFO structure.
+
+Return Value:
+
+ Error.
+
+--*/
+{
+ DWORD err;
+
+ if ( dwLevel != 1 )
+ return ERROR_INVALID_LEVEL;
+
+ if ( ppServerInfo == NULL )
+ return ERROR_INVALID_PARAMETER;
+
+ RpcTryExcept
+ {
+ err = NwrServerGetInfo( pServerName,
+ dwLevel,
+ (PFPNWSERVERINFO *) ppServerInfo );
+ }
+ RpcExcept(1)
+ {
+ err = NwpMapRpcError( RpcExceptionCode() );
+ }
+ RpcEndExcept
+
+ return err;
+
+}
+DWORD
+NwServerGetInfo(
+ IN LPWSTR pServerName OPTIONAL,
+ IN DWORD dwLevel,
+ OUT PNWSERVERINFO *ppServerInfo
+)
+{ return(FpnwServerGetInfo( pServerName,
+ dwLevel,
+ (LPBYTE *) ppServerInfo));
+}
+
+
+
+DWORD
+FpnwServerSetInfo(
+ IN LPWSTR pServerName OPTIONAL,
+ IN DWORD dwLevel,
+ IN LPBYTE pServerInfo
+)
+/*++
+
+Routine Description:
+
+ This API sets the information about the given server.
+
+Arguments:
+
+ pServerName - A pointer to a UNICODE string containing the name of the
+ remote server on which the function is to execute. A NULL pointer
+ or string specifies the local machine.
+
+ dwLevel - Reserved for the level of the server structure contained in
+ pServerInfo, use 1 for now.
+
+ pServerInfo - Points to a NWSERVERINFO structure which contains the server
+ properties to set to.
+
+Return Value:
+
+ Error.
+
+--*/
+{
+ DWORD err;
+
+ if ( dwLevel != 1 )
+ return ERROR_INVALID_LEVEL;
+
+ if ( pServerInfo == NULL )
+ return ERROR_INVALID_PARAMETER;
+
+ RpcTryExcept
+ {
+ err = NwrServerSetInfo( pServerName,
+ dwLevel,
+ (PNWSERVERINFO) pServerInfo );
+ }
+ RpcExcept(1)
+ {
+ err = NwpMapRpcError( RpcExceptionCode() );
+ }
+ RpcEndExcept
+
+ return err;
+}
+DWORD
+NwServerSetInfo(
+ IN LPWSTR pServerName OPTIONAL,
+ IN DWORD dwLevel,
+ IN PNWSERVERINFO pServerInfo
+)
+{ return( FpnwServerSetInfo( pServerName,
+ dwLevel,
+ (LPBYTE) pServerInfo ));
+}
+
+
+
+DWORD
+FpnwVolumeAdd(
+ IN LPWSTR pServerName OPTIONAL,
+ IN DWORD dwLevel,
+ IN LPBYTE pVolumeInfo
+)
+/*++
+
+Routine Description:
+
+ This API adds a volume to the given server.
+
+Arguments:
+
+ pServerName - A pointer to a UNICODE string containing the name of the
+ remote server on which the function is to execute. A NULL pointer
+ or string specifies the local machine.
+
+ dwLevel - Reserved for the level of the volume structure contained in
+ pVolumeInfo, use 1 & 2 for now.
+
+ pVolumeInfo - Points to a NWVOLUMEINFO structure which contains the
+ information about the volume to be added, i.e. the volume name, path,
+ type, user limit and description. dwCurrentUses will be ignored.
+
+Return Value:
+
+ Error.
+
+--*/
+{
+ DWORD err;
+ ULONG SDLength = 0;
+ ULONG oldSDLength;
+ PSECURITY_DESCRIPTOR fileSecurityDescriptor = NULL;
+ PSECURITY_DESCRIPTOR oldFileSecurityDescriptor = NULL;
+ PFPNWVOLUMEINFO_2 volInfo = (PFPNWVOLUMEINFO_2) pVolumeInfo;
+
+ if ( dwLevel != 1 && dwLevel != 2 )
+ return ERROR_INVALID_LEVEL;
+
+ if ( pVolumeInfo == NULL )
+ return ERROR_INVALID_PARAMETER;
+
+ RpcTryExcept
+ {
+ if ( dwLevel == 2 ) {
+
+ //
+ // Save this. We need to restore this later.
+ //
+
+ oldFileSecurityDescriptor = volInfo->FileSecurityDescriptor;
+ oldSDLength = volInfo->dwFileSecurityDescriptorLength;
+
+ if ( oldFileSecurityDescriptor != NULL ) {
+
+ if ( !RtlValidSecurityDescriptor( oldFileSecurityDescriptor ) ) {
+
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ //
+ // Make a self relative security descriptor for use in the
+ // RPC call..
+ //
+
+ err = RtlMakeSelfRelativeSD(
+ oldFileSecurityDescriptor,
+ NULL,
+ &SDLength
+ );
+
+ if (err != STATUS_BUFFER_TOO_SMALL) {
+
+ return(ERROR_INVALID_PARAMETER);
+
+ } else {
+
+ fileSecurityDescriptor = MIDL_user_allocate( SDLength );
+
+ if ( fileSecurityDescriptor == NULL) {
+
+ return ERROR_NOT_ENOUGH_MEMORY;
+
+ } else {
+
+ //
+ // make an appropriate self-relative security descriptor
+ //
+
+ err = RtlMakeSelfRelativeSD(
+ oldFileSecurityDescriptor,
+ (PSECURITY_DESCRIPTOR) fileSecurityDescriptor,
+ &SDLength
+ );
+
+ if ( !NT_SUCCESS(err) ) {
+ MIDL_user_free( fileSecurityDescriptor );
+ return(ERROR_INVALID_PARAMETER);
+ }
+
+ volInfo->FileSecurityDescriptor = fileSecurityDescriptor;
+ volInfo->dwFileSecurityDescriptorLength = SDLength;
+ }
+ }
+
+ } else {
+
+ volInfo->dwFileSecurityDescriptorLength = 0;
+ }
+ }
+
+ err = NwrVolumeAdd( pServerName,
+ dwLevel,
+ (LPVOLUME_INFO) pVolumeInfo );
+
+ if ( fileSecurityDescriptor != NULL ) {
+
+ //
+ // restore old values
+ //
+
+ volInfo->dwFileSecurityDescriptorLength = oldSDLength;
+ volInfo->FileSecurityDescriptor = oldFileSecurityDescriptor;
+ MIDL_user_free( fileSecurityDescriptor );
+ }
+
+ }
+ RpcExcept(1)
+ {
+ err = NwpMapRpcError( RpcExceptionCode() );
+ }
+ RpcEndExcept
+
+ return err;
+}
+DWORD
+NwVolumeAdd(
+ IN LPWSTR pServerName OPTIONAL,
+ IN DWORD dwLevel,
+ IN PNWVOLUMEINFO pVolumeInfo
+)
+{ return( FpnwVolumeAdd( pServerName, dwLevel, (LPBYTE) pVolumeInfo ));
+}
+
+
+
+DWORD
+FpnwVolumeDel(
+ IN LPWSTR pServerName OPTIONAL,
+ IN LPWSTR pVolumeName
+)
+/*++
+
+Routine Description:
+
+ This API deletes a volume from the given server.
+
+Arguments:
+
+ pServerName - A pointer to a UNICODE string containing the name of the
+ remote server on which the function is to execute. A NULL pointer
+ or string specifies the local machine.
+
+ pVolumeName - Specifies teh volume name that is to be deleted.
+
+Return Value:
+
+ Error.
+
+--*/
+{
+ DWORD err;
+
+ if ( (pVolumeName == NULL) || (pVolumeName[0] == 0 ))
+ return ERROR_INVALID_PARAMETER;
+
+ RpcTryExcept
+ {
+ err = NwrVolumeDel( pServerName,
+ pVolumeName );
+ }
+ RpcExcept(1)
+ {
+ err = NwpMapRpcError( RpcExceptionCode() );
+ }
+ RpcEndExcept
+
+ return err;
+}
+DWORD
+NwVolumeDel(
+ IN LPWSTR pServerName OPTIONAL,
+ IN LPWSTR pVolumeName
+)
+{ return( FpnwVolumeDel( pServerName, pVolumeName ));
+}
+
+
+
+DWORD
+FpnwVolumeEnum(
+ IN LPWSTR pServerName OPTIONAL,
+ IN DWORD dwLevel,
+ OUT LPBYTE *ppVolumeInfo,
+ OUT PDWORD pEntriesRead,
+ IN OUT PDWORD resumeHandle OPTIONAL
+)
+/*++
+
+Routine Description:
+
+ This enumerates all volumes on the given server.
+
+Arguments:
+
+ pServerName - A pointer to a UNICODE string containing the name of the
+ remote server on which the function is to execute. A NULL pointer
+ or string specifies the local machine.
+
+ dwLevel - Reserved for the level of the volume structure contained in
+ *ppVolumeInfo, use 1 for now.
+
+ ppVolumeInfo - On return, this will point to an array of NWVOLUMEINFO
+ structures, one for each volume on the server.
+
+ pEntriesRead - On return, this will specify the number of volumes returned
+
+ resumeHandle - On return, a resume handle is stored in the DWORD pointed
+ to by resumeHandle, and is used to continue an existing server search.
+ The handle should be zero on the first call and left unchanged for
+ subsequent calls. If the resumeHandle is NULL, then no resume
+ handle is stored.
+
+Return Value:
+
+ Error.
+
+--*/
+{
+ DWORD err;
+
+ FPNWVOLUMEINFO_CONTAINER NwVolumeInfoContainer;
+
+ if ( dwLevel != 1 )
+ return ERROR_INVALID_LEVEL;
+
+ if ( ppVolumeInfo == NULL || pEntriesRead == NULL )
+ return ERROR_INVALID_PARAMETER;
+
+ NwVolumeInfoContainer.Buffer = NULL;
+
+ RpcTryExcept
+ {
+ err = NwrVolumeEnum( pServerName,
+ dwLevel,
+ &NwVolumeInfoContainer,
+ resumeHandle );
+
+ *ppVolumeInfo = (LPBYTE) NwVolumeInfoContainer.Buffer;
+
+ if ( NwVolumeInfoContainer.Buffer != NULL ) {
+
+ *pEntriesRead = NwVolumeInfoContainer.EntriesRead;
+
+ } else {
+
+ *pEntriesRead = 0;
+ }
+ }
+ RpcExcept(1)
+ {
+ err = NwpMapRpcError( RpcExceptionCode() );
+ }
+ RpcEndExcept
+
+ return err;
+}
+DWORD
+NwVolumeEnum(
+ IN LPWSTR pServerName OPTIONAL,
+ IN DWORD dwLevel,
+ OUT PNWVOLUMEINFO *ppVolumeInfo,
+ OUT PDWORD pEntriesRead,
+ IN OUT PDWORD resumeHandle OPTIONAL
+)
+{ return(FpnwVolumeEnum( pServerName,
+ dwLevel,
+ (LPBYTE *)ppVolumeInfo,
+ pEntriesRead,
+ resumeHandle ));
+}
+
+
+
+DWORD
+FpnwVolumeGetInfo(
+ IN LPWSTR pServerName OPTIONAL,
+ IN LPWSTR pVolumeName,
+ IN DWORD dwLevel,
+ OUT LPBYTE *ppVolumeInfo
+)
+/*++
+
+Routine Description:
+
+ This querys the information about the given volume on the given server.
+
+Arguments:
+
+ pServerName - A pointer to a UNICODE string containing the name of the
+ remote server on which the function is to execute. A NULL pointer
+ or string specifies the local machine.
+
+ pVolumeName - A pointer to a UNICODE string containing the name of the
+ volume we want to get information on.
+
+ dwLevel - Reserved for the level of the volume structure contained in
+ *ppVolumeInfo, use 1 for now.
+
+ ppVolumeInfo - On return, this will point to a NWVOLUMEINFO structure
+ which contains information on the given volume on the given server.
+
+Return Value:
+
+ Error.
+
+--*/
+{
+ DWORD err;
+
+ if ( dwLevel != 1 && dwLevel != 2 ) {
+ return ERROR_INVALID_LEVEL;
+ }
+
+ if ((pVolumeName == NULL) ||
+ (pVolumeName[0] == 0 ) ||
+ (ppVolumeInfo == NULL) ) {
+
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ *ppVolumeInfo = NULL ;
+
+ RpcTryExcept
+ {
+ err = NwrVolumeGetInfo( pServerName,
+ pVolumeName,
+ dwLevel,
+ (LPVOLUME_INFO *) ppVolumeInfo );
+ }
+ RpcExcept(1)
+ {
+ err = NwpMapRpcError( RpcExceptionCode() );
+ }
+ RpcEndExcept
+
+ return err;
+}
+DWORD
+NwVolumeGetInfo(
+ IN LPWSTR pServerName OPTIONAL,
+ IN LPWSTR pVolumeName,
+ IN DWORD dwLevel,
+ OUT PNWVOLUMEINFO *ppVolumeInfo
+)
+{ return(FpnwVolumeGetInfo( pServerName,
+ pVolumeName,
+ dwLevel,
+ (LPBYTE *)ppVolumeInfo ));
+}
+
+
+
+DWORD
+FpnwVolumeSetInfo(
+ IN LPWSTR pServerName OPTIONAL,
+ IN LPWSTR pVolumeName,
+ IN DWORD dwLevel,
+ IN LPBYTE pVolumeInfo
+)
+/*++
+
+Routine Description:
+
+ This sets the information about the given volume on the given server.
+
+Arguments:
+
+ pServerName - A pointer to a UNICODE string containing the name of the
+ remote server on which the function is to execute. A NULL pointer
+ or string specifies the local machine.
+
+ pVolumeName - A pointer to a UNICODE string containing the name of the
+ volume we want to set information on.
+
+ dwLevel - Reserved for the level of the volume structure contained in
+ pVolumeInfo, use 1 for now.
+
+ pVolumeInfo - Points to a NWVOLUMEINFO structure which contains
+ information on the given volume to set to. Only dwMaxUses can be
+ set. All the other fields in this structure will be ignored.
+
+Return Value:
+
+ Error.
+
+--*/
+{
+ DWORD err;
+ ULONG SDLength = 0;
+ ULONG oldSDLength;
+ PFPNWVOLUMEINFO_2 volInfo = (PFPNWVOLUMEINFO_2) pVolumeInfo;
+
+ PSECURITY_DESCRIPTOR fileSecurityDescriptor = NULL;
+ PSECURITY_DESCRIPTOR oldFileSecurityDescriptor = NULL;
+
+ if ( dwLevel != 1 && dwLevel != 2 )
+ return ERROR_INVALID_LEVEL;
+
+ if ( ((pVolumeName == NULL) ||
+ ( pVolumeName[0] == 0 )) ||
+ ( pVolumeInfo == NULL )
+ ) {
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ RpcTryExcept
+ {
+ if ( dwLevel == 2 ) {
+
+ //
+ // Save this. We need to restore this later.
+ //
+
+ oldFileSecurityDescriptor = volInfo->FileSecurityDescriptor;
+ oldSDLength = volInfo->dwFileSecurityDescriptorLength;
+
+ if ( oldFileSecurityDescriptor != NULL ) {
+
+ if ( !RtlValidSecurityDescriptor( oldFileSecurityDescriptor ) ) {
+
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ //
+ // Make a self relative security descriptor for use in the
+ // RPC call..
+ //
+
+ err = RtlMakeSelfRelativeSD(
+ oldFileSecurityDescriptor,
+ NULL,
+ &SDLength
+ );
+
+ if (err != STATUS_BUFFER_TOO_SMALL) {
+
+ return(ERROR_INVALID_PARAMETER);
+
+ } else {
+
+ fileSecurityDescriptor = MIDL_user_allocate( SDLength );
+
+ if ( fileSecurityDescriptor == NULL) {
+
+ return ERROR_NOT_ENOUGH_MEMORY;
+
+ } else {
+
+ //
+ // make an appropriate self-relative security descriptor
+ //
+
+ err = RtlMakeSelfRelativeSD(
+ oldFileSecurityDescriptor,
+ (PSECURITY_DESCRIPTOR) fileSecurityDescriptor,
+ &SDLength
+ );
+
+ if ( !NT_SUCCESS(err) ) {
+ MIDL_user_free( fileSecurityDescriptor );
+ return(ERROR_INVALID_PARAMETER);
+ }
+
+ volInfo->FileSecurityDescriptor = fileSecurityDescriptor;
+ volInfo->dwFileSecurityDescriptorLength = SDLength;
+ }
+ }
+
+ } else {
+
+ volInfo->dwFileSecurityDescriptorLength = 0;
+ }
+ }
+
+ err = NwrVolumeSetInfo( pServerName,
+ pVolumeName,
+ dwLevel,
+ (LPVOLUME_INFO) pVolumeInfo );
+
+ if ( fileSecurityDescriptor != NULL ) {
+
+ //
+ // restore old values
+ //
+
+ volInfo->dwFileSecurityDescriptorLength = oldSDLength;
+ volInfo->FileSecurityDescriptor = oldFileSecurityDescriptor;
+ MIDL_user_free( fileSecurityDescriptor );
+ }
+
+ }
+ RpcExcept(1)
+ {
+ err = NwpMapRpcError( RpcExceptionCode() );
+ }
+ RpcEndExcept
+
+ return err;
+}
+DWORD
+NwVolumeSetInfo(
+ IN LPWSTR pServerName OPTIONAL,
+ IN LPWSTR pVolumeName,
+ IN DWORD dwLevel,
+ IN PNWVOLUMEINFO pVolumeInfo
+)
+{ return( FpnwVolumeSetInfo( pServerName,
+ pVolumeName,
+ dwLevel,
+ (LPBYTE) pVolumeInfo ));
+}
+
+
+
+DWORD
+FpnwConnectionEnum(
+ IN LPWSTR pServerName OPTIONAL,
+ IN DWORD dwLevel,
+ OUT LPBYTE *ppConnectionInfo,
+ OUT PDWORD pEntriesRead,
+ IN OUT PDWORD resumeHandle OPTIONAL
+)
+/*++
+
+Routine Description:
+
+ This enumerates all connections on the given server.
+
+Arguments:
+
+ pServerName - A pointer to a UNICODE string containing the name of the
+ remote server on which the function is to execute. A NULL pointer
+ or string specifies the local machine.
+
+ dwLevel - Reserved for the level of the volume structure contained in
+ *ppConnectionInfo, use 1 for now.
+
+ ppConnectionInfo - On return, this will point to an array of
+ NWCONNECTIONINFO structures, one for each volume on the server.
+
+ pEntriesRead - On return, this will specify the number of current
+ connecitons.
+
+ resumeHandle - On return, a resume handle is stored in the DWORD pointed
+ to by resumeHandle, and is used to continue an existing server search.
+ The handle should be zero on the first call and left unchanged for
+ subsequent calls. If the resumeHandle is NULL, then no resume
+ handle is stored.
+
+Return Value:
+
+ Error.
+
+--*/
+{
+ DWORD err;
+
+ FPNWCONNECTIONINFO_CONTAINER NwConnectionInfoContainer;
+
+ if ( dwLevel != 1 )
+ return ERROR_INVALID_LEVEL;
+
+ if (( ppConnectionInfo == NULL ) || ( pEntriesRead == NULL ))
+ return ERROR_INVALID_PARAMETER;
+
+ NwConnectionInfoContainer.Buffer = NULL;
+
+ RpcTryExcept
+ {
+ err = NwrConnectionEnum( pServerName,
+ dwLevel,
+ &NwConnectionInfoContainer,
+ resumeHandle );
+
+ *ppConnectionInfo = (LPBYTE) NwConnectionInfoContainer.Buffer;
+
+ if ( NwConnectionInfoContainer.Buffer != NULL ) {
+
+ *pEntriesRead = NwConnectionInfoContainer.EntriesRead;
+
+ } else {
+
+ *pEntriesRead = 0;
+ }
+ }
+ RpcExcept(1)
+ {
+ err = NwpMapRpcError( RpcExceptionCode() );
+ }
+ RpcEndExcept
+
+ return err;
+}
+DWORD
+NwConnectionEnum(
+ IN LPWSTR pServerName OPTIONAL,
+ IN DWORD dwLevel,
+ OUT PNWCONNECTIONINFO *ppConnectionInfo,
+ OUT PDWORD pEntriesRead,
+ IN OUT PDWORD resumeHandle OPTIONAL
+)
+{ return(FpnwConnectionEnum( pServerName,
+ dwLevel,
+ (LPBYTE *) ppConnectionInfo,
+ pEntriesRead,
+ resumeHandle ));
+}
+
+
+
+DWORD
+FpnwConnectionDel(
+ IN LPWSTR pServerName OPTIONAL,
+ IN DWORD dwConnectionId
+)
+/*++
+
+Routine Description:
+
+ This delete the connection with the given connection id on the given server.
+
+Arguments:
+
+ pServerName - A pointer to a UNICODE string containing the name of the
+ remote server on which the function is to execute. A NULL pointer
+ or string specifies the local machine.
+
+ dwConnectionId - The identification number of the connection to tear down.
+
+Return Value:
+
+ Error.
+
+--*/
+{
+ DWORD err;
+
+ RpcTryExcept
+ {
+ err = NwrConnectionDel( pServerName,
+ dwConnectionId );
+ }
+ RpcExcept(1)
+ {
+ err = NwpMapRpcError( RpcExceptionCode() );
+ }
+ RpcEndExcept
+
+ return err;
+}
+DWORD
+NwConnectionDel(
+ IN LPWSTR pServerName OPTIONAL,
+ IN DWORD dwConnectionId
+)
+{ return( FpnwConnectionDel( pServerName, dwConnectionId ));
+}
+
+
+
+DWORD
+FpnwVolumeConnEnum(
+ IN LPWSTR pServerName OPTIONAL,
+ IN DWORD dwLevel,
+ IN LPWSTR pVolumeName OPTIONAL,
+ IN DWORD dwConnectionId,
+ OUT LPBYTE *ppVolumeConnInfo,
+ OUT PDWORD pEntriesRead,
+ IN OUT PDWORD resumeHandle OPTIONAL
+)
+/*++
+
+Routine Description:
+
+ This enumerates all connections to a volume or list all volumes used by
+ a particular connection on the given server.
+
+Arguments:
+
+ pServerName - A pointer to a UNICODE string containing the name of the
+ remote server on which the function is to execute. A NULL pointer
+ or string specifies the local machine.
+
+ dwLevel - Reserved for the level of the volume structure contained in
+ *ppVolumeConnInfo, use 1 for now.
+
+ pVolumeName - Specifies the volume name which we want to get all opened
+ resources. This must be NULL if dwConnectionId is not 0.
+
+ dwConnectionId - Specifies the connection id on which we want to get all
+ opened resources. This must be 0 if pVolumeName is not NULL.
+
+ ppVolumeConnInfo - On return, this will point to an array of
+ NWVOLUMECONNINFO structures.
+
+ pEntriesRead - On return, this will specify the number of NWVOLUMECONNINFO
+ returned.
+
+ resumeHandle - On return, a resume handle is stored in the DWORD pointed
+ to by resumeHandle, and is used to continue an existing server search.
+ The handle should be zero on the first call and left unchanged for
+ subsequent calls. If the resumeHandle is NULL, then no resume
+ handle is stored.
+
+Return Value:
+
+ Error.
+
+--*/
+{
+ DWORD err;
+
+ FPNWVOLUMECONNINFO_CONTAINER NwVolumeConnInfoContainer;
+
+ if ( dwLevel != 1 )
+ return ERROR_INVALID_LEVEL;
+
+ if ( ( dwConnectionId == 0 )
+ && (( pVolumeName == NULL ) || ( *pVolumeName == 0 ))
+ )
+ {
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ if ( ( dwConnectionId != 0 )
+ && (( pVolumeName != NULL) && ( *pVolumeName != 0 ))
+ )
+ {
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ if (( ppVolumeConnInfo == NULL ) || ( pEntriesRead == NULL ))
+ return ERROR_INVALID_PARAMETER;
+
+ NwVolumeConnInfoContainer.Buffer = NULL;
+
+ RpcTryExcept
+ {
+ err = NwrVolumeConnEnum( pServerName,
+ dwLevel,
+ pVolumeName,
+ dwConnectionId,
+ &NwVolumeConnInfoContainer,
+ resumeHandle );
+
+ *ppVolumeConnInfo = (LPBYTE) NwVolumeConnInfoContainer.Buffer;
+
+ if ( NwVolumeConnInfoContainer.Buffer != NULL ) {
+
+ *pEntriesRead = NwVolumeConnInfoContainer.EntriesRead;
+
+ } else {
+
+ *pEntriesRead = 0;
+ }
+ }
+ RpcExcept(1)
+ {
+ err = NwpMapRpcError( RpcExceptionCode() );
+ }
+ RpcEndExcept
+
+ return err;
+}
+DWORD
+NwVolumeConnEnum(
+ IN LPWSTR pServerName OPTIONAL,
+ IN DWORD dwLevel,
+ IN LPWSTR pVolumeName OPTIONAL,
+ IN DWORD dwConnectionId,
+ OUT PNWVOLUMECONNINFO *ppVolumeConnInfo,
+ OUT PDWORD pEntriesRead,
+ IN OUT PDWORD resumeHandle OPTIONAL
+)
+{ return( FpnwVolumeConnEnum( pServerName,
+ dwLevel,
+ pVolumeName,
+ dwConnectionId,
+ (LPBYTE *) ppVolumeConnInfo,
+ pEntriesRead,
+ resumeHandle ));
+}
+
+
+
+DWORD
+FpnwFileEnum(
+ IN LPWSTR pServerName OPTIONAL,
+ IN DWORD dwLevel,
+ IN LPWSTR pPathName OPTIONAL,
+ OUT LPBYTE *ppFileInfo,
+ OUT PDWORD pEntriesRead,
+ IN OUT PDWORD resumeHandle OPTIONAL
+)
+/*++
+
+Routine Description:
+
+ This enumerates files opened on the server.
+
+Arguments:
+
+ pServerName - A pointer to a UNICODE string containing the name of the
+ remote server on which the function is to execute. A NULL pointer
+ or string specifies the local machine.
+
+ dwLevel - Reserved for the level of the volume structure contained in
+ *ppFileInfo, use 1 for now.
+
+ pPathName - If this is not NULL, this means that we want to filter
+ on the path. We only want entries with this path, i.e., all users that
+ currently opened the file. If this is NULL, then all files that are
+ opened are returned along with the user information.
+
+ ppFileInfo - On return, this will point to an array of NWFILEINFO structures
+
+ pEntriesRead - On return, this will specify the number of NWFILEINFO
+ returned.
+
+ resumeHandle - On return, a resume handle is stored in the DWORD pointed
+ to by resumeHandle, and is used to continue an existing server search.
+ The handle should be zero on the first call and left unchanged for
+ subsequent calls. If the resumeHandle is NULL, then no resume
+ handle is stored.
+
+Return Value:
+
+ Error.
+
+--*/
+{
+ DWORD err;
+ FPNWFILEINFO_CONTAINER NwFileInfoContainer;
+
+ if ( dwLevel != 1 )
+ return ERROR_INVALID_LEVEL;
+
+ if (( ppFileInfo == NULL ) || ( pEntriesRead == NULL ))
+ return ERROR_INVALID_PARAMETER;
+
+ NwFileInfoContainer.Buffer = NULL;
+
+ RpcTryExcept
+ {
+ err = NwrFileEnum( pServerName,
+ dwLevel,
+ pPathName,
+ &NwFileInfoContainer,
+ resumeHandle );
+
+ *ppFileInfo = (LPBYTE) NwFileInfoContainer.Buffer;
+
+ if ( NwFileInfoContainer.Buffer != NULL ) {
+
+ *pEntriesRead = NwFileInfoContainer.EntriesRead;
+
+ } else {
+
+ *pEntriesRead = 0;
+ }
+ }
+ RpcExcept(1)
+ {
+ err = NwpMapRpcError( RpcExceptionCode() );
+ }
+ RpcEndExcept
+
+ return err;
+}
+DWORD
+NwFileEnum(
+ IN LPWSTR pServerName OPTIONAL,
+ IN DWORD dwLevel,
+ IN LPWSTR pPathName OPTIONAL,
+ OUT PNWFILEINFO *ppFileInfo,
+ OUT PDWORD pEntriesRead,
+ IN OUT PDWORD resumeHandle OPTIONAL
+)
+{ return(FpnwFileEnum( pServerName,
+ dwLevel,
+ pPathName,
+ (LPBYTE *) ppFileInfo,
+ pEntriesRead,
+ resumeHandle ));
+}
+
+
+
+DWORD
+FpnwFileClose(
+ IN LPWSTR pServerName OPTIONAL,
+ IN DWORD dwFileId
+)
+/*++
+
+Routine Description:
+
+ This closes the file with the given identification number.
+
+Arguments:
+
+ pServerName - A pointer to a UNICODE string containing the name of the
+ remote server on which the function is to execute. A NULL pointer
+ or string specifies the local machine.
+
+ dwFileId - The identification number of the file to close.
+
+Return Value:
+
+ Error.
+
+--*/
+{
+ DWORD err;
+
+ RpcTryExcept
+ {
+ err = NwrFileClose( pServerName,
+ dwFileId );
+ }
+ RpcExcept(1)
+ {
+ err = NwpMapRpcError( RpcExceptionCode() );
+ }
+ RpcEndExcept
+
+ return err;
+}
+DWORD
+NwFileClose(
+ IN LPWSTR pServerName OPTIONAL,
+ IN DWORD dwFileId
+)
+{ return(FpnwFileClose( pServerName, dwFileId ));
+}
+
+
+
+DWORD
+FpnwMessageBufferSend(
+ IN LPWSTR pServerName OPTIONAL,
+ IN DWORD dwConnectionId,
+ IN DWORD fConsoleBroadcast,
+ IN LPBYTE pbBuffer,
+ IN DWORD cbBuffer
+)
+/*++
+
+Routine Description:
+
+ This sends the message to the given connection id.
+
+Arguments:
+
+ pServerName - A pointer to a UNICODE string containing the name of the
+ remote server on which the function is to execute. A NULL pointer
+ or string specifies the local machine.
+
+ dwConnectionId - The id of the connection to send message to.
+
+ fConsoleBroadcast - If this is TRUE, that means use console broadcast. If
+ FALSE, use user broadcast.
+
+ pbBuffer - Points to the message buffer to be sent.
+
+ cbBuffer - The size of the pbBuffer in bytes.
+
+Return Value:
+
+ Error.
+
+--*/
+{
+ DWORD err;
+
+ if (( pbBuffer == NULL ) || ( cbBuffer == 0 ))
+ return ERROR_INVALID_PARAMETER;
+
+ RpcTryExcept
+ {
+ err = NwrMessageBufferSend( pServerName,
+ dwConnectionId,
+ fConsoleBroadcast,
+ pbBuffer,
+ cbBuffer );
+ }
+ RpcExcept(1)
+ {
+ err = NwpMapRpcError( RpcExceptionCode() );
+ }
+ RpcEndExcept
+
+ return err;
+}
+DWORD
+NwMessageBufferSend(
+ IN LPWSTR pServerName OPTIONAL,
+ IN DWORD dwConnectionId,
+ IN DWORD fConsoleBroadcast,
+ IN LPBYTE pbBuffer,
+ IN DWORD cbBuffer
+)
+{ return( FpnwMessageBufferSend( pServerName,
+ dwConnectionId,
+ fConsoleBroadcast,
+ pbBuffer,
+ cbBuffer ));
+}
+
+
+
+DWORD
+FpnwSetDefaultQueue(
+ IN LPWSTR pServerName OPTIONAL,
+ IN LPWSTR pQueueName
+)
+/*++
+
+Routine Description:
+
+ This sets the default queue on the server.
+
+Arguments:
+
+ pServerName - A pointer to a UNICODE string containing the name of the
+ remote server on which the function is to execute. A NULL pointer
+ or string specifies the local machine.
+
+ pQueueName - The name of the queue that will become the default
+
+Return Value:
+
+ Error.
+
+--*/
+{
+ DWORD err;
+
+ if (( pQueueName == NULL ) || ( *pQueueName == 0 ))
+ return ERROR_INVALID_PARAMETER;
+
+ RpcTryExcept
+ {
+ err = NwrSetDefaultQueue( pServerName,
+ pQueueName );
+ }
+ RpcExcept(1)
+ {
+ err = NwpMapRpcError( RpcExceptionCode() );
+ }
+ RpcEndExcept
+
+ return err;
+}
+DWORD
+NwSetDefaultQueue(
+ IN LPWSTR pServerName OPTIONAL,
+ IN LPWSTR pQueueName
+)
+{ return(FpnwSetDefaultQueue( pServerName, pQueueName ));
+}
+
+
+
+DWORD
+FpnwAddPServer(
+ IN LPWSTR pServerName OPTIONAL,
+ IN LPWSTR pPServerName
+)
+/*++
+
+Routine Description:
+
+ This adds a pserver.
+
+Arguments:
+
+ pServerName - A pointer to a UNICODE string containing the name of the
+ remote server on which the function is to execute. A NULL pointer
+ or string specifies the local machine.
+
+ pPServerName - The name of the PServer.
+
+Return Value:
+
+ Error.
+
+--*/
+{
+ DWORD err;
+
+ if (( pPServerName == NULL ) || ( *pPServerName == 0 ))
+ return ERROR_INVALID_PARAMETER;
+
+ RpcTryExcept
+ {
+ err = NwrAddPServer( pServerName,
+ pPServerName );
+ }
+ RpcExcept(1)
+ {
+ err = NwpMapRpcError( RpcExceptionCode() );
+ }
+ RpcEndExcept
+
+ return err;
+}
+DWORD
+NwAddPServer(
+ IN LPWSTR pServerName OPTIONAL,
+ IN LPWSTR pPServerName
+)
+{ return( FpnwAddPServer( pServerName, pPServerName ));
+}
+
+
+
+DWORD
+FpnwRemovePServer(
+ IN LPWSTR pServerName OPTIONAL,
+ IN LPWSTR pPServerName
+)
+/*++
+
+Routine Description:
+
+ This removes a pserver.
+
+Arguments:
+
+ pServerName - A pointer to a UNICODE string containing the name of the
+ remote server on which the function is to execute. A NULL pointer
+ or string specifies the local machine.
+
+ pPServerName - The name of the PServer.
+
+Return Value:
+
+ Error.
+
+--*/
+{
+ DWORD err;
+
+ if (( pPServerName == NULL ) || ( *pPServerName == 0 ))
+ return ERROR_INVALID_PARAMETER;
+
+ RpcTryExcept
+ {
+ err = NwrRemovePServer( pServerName,
+ pPServerName );
+ }
+ RpcExcept(1)
+ {
+ err = NwpMapRpcError( RpcExceptionCode() );
+ }
+ RpcEndExcept
+
+ return err;
+}
+DWORD
+NwRemovePServer(
+ IN LPWSTR pServerName OPTIONAL,
+ IN LPWSTR pPServerName
+)
+{ return( FpnwRemovePServer( pServerName, pPServerName ));
+}
+
+
+DWORD NwpMapRpcError(
+ IN DWORD RpcError
+)
+/*++
+
+Routine Description:
+
+ This routine maps the RPC error into a more meaningful windows
+ error for the caller.
+
+Arguments:
+
+ RpcError - Supplies the exception error raised by RPC
+
+Return Value:
+
+ Returns the mapped error.
+
+--*/
+{
+
+ switch (RpcError)
+ {
+ case RPC_S_INVALID_BINDING:
+ case RPC_X_SS_IN_NULL_CONTEXT:
+ case RPC_X_SS_CONTEXT_DAMAGED:
+ case RPC_X_SS_HANDLES_MISMATCH:
+ case ERROR_INVALID_HANDLE:
+ return ERROR_INVALID_HANDLE;
+
+ case RPC_X_NULL_REF_POINTER:
+ return ERROR_INVALID_PARAMETER;
+
+ case STATUS_ACCESS_VIOLATION:
+ return ERROR_INVALID_ADDRESS;
+
+ default:
+ return RpcError;
+ }
+}
+
+// ncpstub.c eof.
+
diff --git a/private/net/svcdlls/fpnw/client/notify.c b/private/net/svcdlls/fpnw/client/notify.c
new file mode 100644
index 000000000..2e2ff3537
--- /dev/null
+++ b/private/net/svcdlls/fpnw/client/notify.c
@@ -0,0 +1,543 @@
+/*++
+
+Copyright (c) 1987-1994 Microsoft Corporation
+
+Module Name:
+
+ notify.c
+
+Abstract:
+
+ Sample SubAuthentication Package.
+
+Author:
+
+ Yi-Hsin Sung (yihsins) 27-Feb-1995
+
+Revisions:
+
+
+Environment:
+
+ User mode only.
+ Contains NT-specific code.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+--*/
+
+
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <ntsam.h>
+#include <ntlsa.h>
+
+#include <windef.h>
+#include <winbase.h>
+#include <winuser.h>
+
+#include <lmaccess.h>
+#include <lmapibuf.h>
+
+#include <nwsutil.h>
+#include <usrprop.h>
+
+#define SW_DLL_NAME L"swclnt.dll"
+#define PASSWORD_PROC_NAME "SwPasswordChangeNotify"
+#define NOTIFY_PROC_NAME "SwDeltaChangeNotify"
+#define NO_GRACE_LOGIN_LIMIT 0xFF
+
+typedef DWORD (*PWPROC)( LPWSTR pUserName,
+ ULONG RelativeId,
+ LPWSTR pPassword );
+
+DWORD
+GetNCPLSASecret(
+ VOID
+);
+
+BOOL fTriedToGetSW = FALSE;
+BOOL fTriedToGetNCP = FALSE;
+HINSTANCE hinstSW = NULL;
+PWPROC ProcPasswordChange = NULL;
+PSAM_DELTA_NOTIFICATION_ROUTINE ProcDeltaChange = NULL;
+BOOL fGotSecret = FALSE;
+char szNWSecretValue[NCP_LSA_SECRET_LENGTH] = "";
+
+
+
+NTSTATUS
+PasswordChangeNotify(
+ PUNICODE_STRING UserName,
+ ULONG RelativeId,
+ PUNICODE_STRING Password
+ )
+{
+ DWORD err = NO_ERROR;
+ PUSER_INFO_2 pUserInfo2 = NULL;
+ LPWSTR pszUser = NULL;
+ LPWSTR pszPassword = NULL;
+
+ //
+ // If password is NULL, we can't get the cleartext password. Hence,
+ // ignore this notification. Same for UserName.
+ //
+ if ( (Password == NULL) || (Password->Buffer == NULL) )
+ return STATUS_SUCCESS;
+
+ if ( (UserName == NULL) || (UserName->Buffer == NULL) )
+ return STATUS_SUCCESS;
+
+ //
+ // if neither DSMN nor FPNW are installed, blow out of here as there's
+ // nothing to do.
+ //
+
+ if ( ( fTriedToGetSW && hinstSW == NULL ) &&
+ ( fTriedToGetNCP && fGotSecret == FALSE) )
+ {
+ return STATUS_SUCCESS;
+ }
+
+ //
+ // Make sure user name and password are null terminated
+ //
+ pszUser = LocalAlloc( LMEM_ZEROINIT, UserName->Length + sizeof(WCHAR));
+
+ if ( pszUser == NULL )
+ return STATUS_NO_MEMORY;
+
+ pszPassword = LocalAlloc( LMEM_ZEROINIT, Password->Length + sizeof(WCHAR));
+
+ if ( pszPassword == NULL )
+ {
+ LocalFree( pszUser );
+ return STATUS_NO_MEMORY;
+ }
+
+ memcpy( pszUser, UserName->Buffer, UserName->Length );
+ memcpy( pszPassword, Password->Buffer, Password->Length );
+ CharUpper( pszPassword );
+
+ //
+ // First, try to change the small world password if it is installed.
+ //
+ if ( !fTriedToGetSW )
+ {
+ hinstSW = LoadLibrary( SW_DLL_NAME );
+ fTriedToGetSW = TRUE;
+ }
+
+ if (( hinstSW != NULL ) && ( ProcPasswordChange == NULL ))
+ {
+ ProcPasswordChange = (PWPROC) GetProcAddress( hinstSW,
+ PASSWORD_PROC_NAME );
+ }
+
+ if ( ProcPasswordChange != NULL )
+ {
+ err = (ProcPasswordChange)( pszUser, RelativeId, pszPassword );
+ }
+
+#if DBG
+ if ( err )
+ {
+ KdPrint(("[FPNWCLNT] SwPasswordChangeNotify of user %ws changing returns %d.\n", pszUser, err ));
+ }
+#endif
+
+ //
+ // we require that the PDC be rebooted after either DSMN or FPNW is
+ // installed anywhere in the domain for the first server... if we
+ // decide we shouldn't require a reboot, change the code such that
+ // it looks for the LSA secret everytime, not just first time through.
+ //
+
+ if ( !fTriedToGetNCP ) {
+
+ fTriedToGetNCP = TRUE;
+
+ //
+ // Get the LSA secret used to encrypt the password
+ //
+ err = GetNCPLSASecret();
+ }
+
+ if ( !fGotSecret ) {
+
+ goto CleanUp;
+ }
+
+ //
+ // Next, change the netware password residue in the user parms field
+ //
+ err = NetUserGetInfo( NULL,
+ pszUser,
+ 2,
+ (LPBYTE *) &pUserInfo2 );
+
+ if ( !err )
+ {
+ WCHAR PropertyFlag;
+ UNICODE_STRING PropertyValue;
+
+ err = RtlNtStatusToDosError(
+ QueryUserProperty( pUserInfo2->usri2_parms,
+ NWPASSWORD,
+ &PropertyFlag,
+ &PropertyValue ));
+
+
+ if ( !err && PropertyValue.Length != 0 )
+ {
+ //
+ // This is a netware-enabled user, we need to store
+ // the new password residue into the user parms
+ //
+
+ NT_PRODUCT_TYPE ProductType;
+ WCHAR szEncryptedNWPassword[NWENCRYPTEDPASSWORDLENGTH];
+ DWORD dwUserId;
+ WORD wGraceLoginRemaining;
+ WORD wGraceLoginAllowed;
+
+ LocalFree( PropertyValue.Buffer );
+
+ //
+ // Get the grace login allowed and remaining value
+ //
+ err = RtlNtStatusToDosError(
+ QueryUserProperty( pUserInfo2->usri2_parms,
+ GRACELOGINREMAINING,
+ &PropertyFlag,
+ &PropertyValue ));
+
+ if ( !err && ( PropertyValue.Length != 0 ))
+ {
+ wGraceLoginRemaining = (WORD) *(PropertyValue.Buffer);
+ LocalFree( PropertyValue.Buffer );
+
+ if ( wGraceLoginRemaining != NO_GRACE_LOGIN_LIMIT )
+ {
+ // If the grace login remaining is not unlimited,
+ // then we need to reset grace login remaining to
+ // the value in grace login allowed. Hence, read the
+ // grace login allowed value.
+
+ err = RtlNtStatusToDosError(
+ QueryUserProperty( pUserInfo2->usri2_parms,
+ GRACELOGINALLOWED,
+ &PropertyFlag,
+ &PropertyValue ));
+
+ if ( !err && ( PropertyValue.Length != 0 ))
+ {
+ wGraceLoginAllowed = (WORD) *(PropertyValue.Buffer);
+ LocalFree( PropertyValue.Buffer );
+ }
+
+ }
+ }
+
+
+ if ( !err )
+ {
+ RtlGetNtProductType( &ProductType );
+
+
+ dwUserId = MapRidToObjectId(
+ RelativeId,
+ pszUser,
+ ProductType == NtProductLanManNt,
+ FALSE );
+
+ err = RtlNtStatusToDosError(
+ ReturnNetwareForm(
+ szNWSecretValue,
+ dwUserId,
+ pszPassword,
+ (UCHAR *) szEncryptedNWPassword ));
+ }
+
+ if ( !err )
+ {
+ LPWSTR pNewUserParms = NULL;
+ BOOL fUpdate;
+ UNICODE_STRING uPropertyValue;
+
+ uPropertyValue.Buffer = szEncryptedNWPassword;
+ uPropertyValue.Length = uPropertyValue.MaximumLength
+ = sizeof( szEncryptedNWPassword );
+
+ err = RtlNtStatusToDosError(
+ SetUserProperty(
+ pUserInfo2->usri2_parms,
+ NWPASSWORD,
+ uPropertyValue,
+ PropertyFlag,
+ &pNewUserParms,
+ &fUpdate ));
+
+ if ( !err && fUpdate )
+ {
+ USER_INFO_1013 userInfo1013;
+ LPWSTR pNewUserParms2 = NULL;
+ LPWSTR pNewUserParms3 = NULL;
+ LARGE_INTEGER currentTime;
+
+ //
+ // Since we're resetting the user's password, let's
+ // also clear the flag saying the password has
+ // expired. We do this by putting the current
+ // time into the NWPasswordSet.
+ //
+
+ NtQuerySystemTime (&currentTime);
+
+ uPropertyValue.Buffer = (PWCHAR) &currentTime;
+ uPropertyValue.Length = sizeof (LARGE_INTEGER);
+ uPropertyValue.MaximumLength = sizeof (LARGE_INTEGER);
+
+ SetUserProperty( pNewUserParms,
+ NWTIMEPASSWORDSET,
+ uPropertyValue,
+ (SHORT) 0, // not a set
+ &pNewUserParms2,
+ &fUpdate );
+
+ if (pNewUserParms2 != NULL) {
+ userInfo1013.usri1013_parms = pNewUserParms2;
+ } else {
+ userInfo1013.usri1013_parms = pNewUserParms;
+ }
+
+ if ( wGraceLoginRemaining != NO_GRACE_LOGIN_LIMIT )
+ {
+ // If the grace login remaining is not unlimited,
+ // then we need to reset grace login remaining to
+ // the value in grace login allowed.
+
+ uPropertyValue.Buffer = (PWCHAR) &wGraceLoginAllowed;
+ uPropertyValue.Length = uPropertyValue.MaximumLength
+ = sizeof(wGraceLoginAllowed);
+
+ SetUserProperty( userInfo1013.usri1013_parms,
+ GRACELOGINREMAINING,
+ uPropertyValue,
+ (SHORT) 0, // not a set
+ &pNewUserParms3,
+ &fUpdate );
+
+ if (pNewUserParms3 != NULL)
+ userInfo1013.usri1013_parms = pNewUserParms3;
+ }
+
+ err = NetUserSetInfo( NULL,
+ pszUser,
+ USER_PARMS_INFOLEVEL,
+ (LPBYTE) &userInfo1013,
+ NULL );
+
+ if (pNewUserParms2 != NULL)
+ LocalFree( pNewUserParms2 );
+
+ if (pNewUserParms3 != NULL)
+ LocalFree( pNewUserParms3 );
+ }
+
+ if ( pNewUserParms != NULL )
+ LocalFree( pNewUserParms );
+ }
+ }
+
+ NetApiBufferFree( pUserInfo2 );
+ }
+
+#if DBG
+ if ( err )
+ {
+ KdPrint(("[FPNWCLNT] Password of user %ws changing returns %d.\n",
+ pszUser, err ));
+ }
+#endif
+
+CleanUp:
+
+ LocalFree( pszUser );
+
+ // Need to clear all memory that contains password
+ memset( pszPassword, 0, Password->Length + sizeof( WCHAR ));
+ LocalFree( pszPassword );
+
+ return STATUS_SUCCESS;
+}
+
+
+BOOLEAN
+InitializeChangeNotify (
+ VOID
+ )
+{
+ DWORD err = NO_ERROR;
+
+ //
+ // First, check to see if small world is installed.
+ //
+ if ( !fTriedToGetSW )
+ {
+ hinstSW = LoadLibrary( SW_DLL_NAME );
+ fTriedToGetSW = TRUE;
+ }
+
+ if (( hinstSW != NULL )) {
+
+ return TRUE;
+ }
+
+ if ( !fTriedToGetNCP ) {
+
+ fTriedToGetNCP = TRUE;
+
+ //
+ // Get the LSA secret used to encrypt the password
+ //
+ err = GetNCPLSASecret();
+ }
+
+ return fGotSecret;
+}
+
+
+
+DWORD
+GetNCPLSASecret(
+ VOID
+)
+{
+ DWORD err;
+ LSA_HANDLE hlsaPolicy;
+ OBJECT_ATTRIBUTES oa;
+ SECURITY_QUALITY_OF_SERVICE sqos;
+ LSA_HANDLE hlsaSecret;
+ UNICODE_STRING uSecretName;
+ UNICODE_STRING *puSecretValue;
+ LARGE_INTEGER lintCurrentSetTime, lintOldSetTime;
+
+ sqos.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
+ sqos.ImpersonationLevel = SecurityImpersonation;
+ sqos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
+ sqos.EffectiveOnly = FALSE;
+
+ //
+ // Set up the object attributes prior to opening the LSA.
+ //
+
+ InitializeObjectAttributes( &oa, NULL, 0L, NULL, NULL );
+
+ //
+ // The InitializeObjectAttributes macro presently store NULL for
+ // the psqos field, so we must manually copy that
+ // structure for now.
+ //
+
+ oa.SecurityQualityOfService = &sqos;
+
+
+ err = RtlNtStatusToDosError( LsaOpenPolicy( NULL,
+ &oa,
+ GENERIC_EXECUTE,
+ &hlsaPolicy ));
+
+ if ( !err )
+ {
+ RtlInitUnicodeString( &uSecretName, NCP_LSA_SECRET_KEY );
+ err = RtlNtStatusToDosError( LsaOpenSecret( hlsaPolicy,
+ &uSecretName,
+ GENERIC_READ,
+ &hlsaSecret ));
+
+ if ( !err )
+ {
+ err = RtlNtStatusToDosError(
+ LsaQuerySecret( hlsaSecret,
+ &puSecretValue,
+ &lintCurrentSetTime,
+ NULL,
+ &lintOldSetTime ));
+
+ if ( !err )
+ {
+ memcpy( szNWSecretValue,
+ puSecretValue->Buffer,
+ NCP_LSA_SECRET_LENGTH );
+
+ fGotSecret = TRUE;
+
+ (VOID) LsaFreeMemory( puSecretValue );
+ }
+
+ (VOID) LsaClose( hlsaSecret );
+
+ }
+
+ (VOID) LsaClose( hlsaPolicy );
+ }
+
+ return err;
+
+}
+
+
+
+NTSTATUS
+DeltaNotify(
+ IN PSID DomainSid,
+ IN SECURITY_DB_DELTA_TYPE DeltaType,
+ IN SECURITY_DB_OBJECT_TYPE ObjectType,
+ IN ULONG ObjectRid,
+ IN PUNICODE_STRING ObjectName OPTIONAL,
+ IN PLARGE_INTEGER ModifiedCount,
+ IN PSAM_DELTA_DATA DeltaData OPTIONAL
+ )
+{
+ NTSTATUS err = NO_ERROR;
+
+ //
+ // Try to notify small world of SAM changes if it is installed.
+ //
+
+ if ( !fTriedToGetSW )
+ {
+ hinstSW = LoadLibrary( SW_DLL_NAME );
+ fTriedToGetSW = TRUE;
+ }
+
+ if ( ( hinstSW != NULL ) && ( ProcDeltaChange == NULL ))
+ {
+ ProcDeltaChange = (PSAM_DELTA_NOTIFICATION_ROUTINE)
+ GetProcAddress( hinstSW, NOTIFY_PROC_NAME );
+ }
+
+ if ( ProcDeltaChange != NULL )
+ {
+ err = (ProcDeltaChange)( DomainSid,
+ DeltaType,
+ ObjectType,
+ ObjectRid,
+ ObjectName,
+ ModifiedCount,
+ DeltaData );
+ }
+
+#if DBG
+ if ( err )
+ {
+ KdPrint(("[FPNWCLNT] SwDeltaChangeNotify of type %d on rid 0x%x returns %d.\n", DeltaType, ObjectRid, err ));
+ }
+#endif
+
+ return(STATUS_SUCCESS);
+}
diff --git a/private/net/svcdlls/fpnw/client/nwsutil.c b/private/net/svcdlls/fpnw/client/nwsutil.c
new file mode 100644
index 000000000..5349cd8d3
--- /dev/null
+++ b/private/net/svcdlls/fpnw/client/nwsutil.c
@@ -0,0 +1,213 @@
+/*++
+
+Copyright (c) 1993-1993 Microsoft Corporation
+
+Module Name:
+
+ nwsutil.c
+
+Abstract:
+
+ This module implements IsNetWareInstalled()
+
+Author:
+
+ Congpa You (CongpaY) 02-Dec-1993 Crested
+
+Revision History:
+
+--*/
+
+#include "nt.h"
+#include "ntrtl.h"
+#include "nturtl.h"
+
+#include "windef.h"
+#include "winerror.h"
+#include "winbase.h"
+
+#include "ntlsa.h"
+#include "nwsutil.h"
+#include "crypt.h"
+
+#include <fpnwcomm.h>
+#include <usrprop.h>
+
+NTSTATUS
+GetRemoteNcpSecretKey (
+ PUNICODE_STRING SystemName,
+ CHAR *pchNWSecretKey
+ )
+{
+ //
+ // this function returns the FPNW LSA Secret for the specified domain
+ //
+
+ NTSTATUS ntstatus;
+ OBJECT_ATTRIBUTES ObjAttributes;
+ LSA_HANDLE PolicyHandle = NULL;
+ LSA_HANDLE SecretHandle = NULL;
+ UNICODE_STRING SecretNameString;
+ PUNICODE_STRING punicodeCurrentValue;
+ PUNICODE_STRING punicodeOldValue;
+
+ InitializeObjectAttributes( &ObjAttributes,
+ NULL,
+ 0L,
+ NULL,
+ NULL );
+
+ ntstatus = LsaOpenPolicy( SystemName,
+ &ObjAttributes,
+ POLICY_CREATE_SECRET,
+ &PolicyHandle );
+
+ if ( !NT_SUCCESS( ntstatus ))
+ {
+ return( ntstatus );
+ }
+
+ RtlInitUnicodeString( &SecretNameString, NCP_LSA_SECRET_KEY );
+
+ ntstatus = LsaOpenSecret( PolicyHandle,
+ &SecretNameString,
+ SECRET_QUERY_VALUE,
+ &SecretHandle );
+
+ if ( !NT_SUCCESS( ntstatus ))
+ {
+ LsaClose( PolicyHandle );
+ return( ntstatus );
+ }
+
+ //
+ // Do not need the policy handle anymore.
+ //
+
+ LsaClose( PolicyHandle );
+
+ ntstatus = LsaQuerySecret( SecretHandle,
+ &punicodeCurrentValue,
+ NULL,
+ &punicodeOldValue,
+ NULL );
+
+ //
+ // Do not need the secret handle anymore.
+ //
+
+ LsaClose( SecretHandle );
+
+ if ( NT_SUCCESS(ntstatus) && ( punicodeCurrentValue->Buffer != NULL))
+ {
+ memcpy( pchNWSecretKey,
+ punicodeCurrentValue->Buffer,
+ min(punicodeCurrentValue->Length, USER_SESSION_KEY_LENGTH));
+ }
+
+ LsaFreeMemory( punicodeCurrentValue );
+ LsaFreeMemory( punicodeOldValue );
+
+ return( ntstatus );
+}
+
+NTSTATUS
+GetNcpSecretKey (
+ CHAR *pchNWSecretKey
+ )
+{
+ //
+ // simply return the LSA Secret for the local domain
+ //
+
+ return GetRemoteNcpSecretKey( NULL, pchNWSecretKey );
+}
+
+BOOL IsNetWareInstalled( VOID )
+{
+ CHAR pszNWSecretKey[USER_SESSION_KEY_LENGTH];
+
+ return( !NT_SUCCESS( GetNcpSecretKey (pszNWSecretKey))
+ ? FALSE
+ : (pszNWSecretKey[0] != 0));
+}
+
+NTSTATUS InstallNetWare( LPWSTR lpNcpSecretKey )
+{
+ NTSTATUS ntstatus;
+ OBJECT_ATTRIBUTES ObjAttributes;
+ LSA_HANDLE PolicyHandle;
+ LSA_HANDLE SecretHandle;
+ UNICODE_STRING SecretNameString;
+ UNICODE_STRING unicodeCurrentValue;
+ UNICODE_STRING unicodeOldValue;
+
+ InitializeObjectAttributes( &ObjAttributes,
+ NULL,
+ 0L,
+ NULL,
+ NULL);
+
+ ntstatus = LsaOpenPolicy( NULL,
+ &ObjAttributes,
+ POLICY_CREATE_SECRET,
+ &PolicyHandle );
+
+ if ( !NT_SUCCESS( ntstatus ))
+ {
+ return( ntstatus );
+ }
+
+ RtlInitUnicodeString( &SecretNameString, NCP_LSA_SECRET_KEY );
+
+ ntstatus = LsaCreateSecret( PolicyHandle,
+ &SecretNameString,
+ SECRET_SET_VALUE | DELETE,
+ &SecretHandle );
+
+ if ( ntstatus == STATUS_OBJECT_NAME_COLLISION )
+ {
+ ntstatus = LsaOpenSecret( PolicyHandle,
+ &SecretNameString,
+ SECRET_SET_VALUE,
+ &SecretHandle );
+ }
+
+ if ( NT_SUCCESS( ntstatus ))
+ {
+ RtlInitUnicodeString( &unicodeOldValue, NULL );
+ RtlInitUnicodeString( &unicodeCurrentValue, lpNcpSecretKey );
+
+ ntstatus = LsaSetSecret( SecretHandle,
+ &unicodeCurrentValue,
+ &unicodeOldValue );
+
+ LsaClose( SecretHandle );
+ }
+
+ LsaClose( PolicyHandle );
+
+ return( ntstatus );
+}
+
+ULONG
+MapRidToObjectId(
+ DWORD dwRid,
+ LPWSTR pszUserName,
+ BOOL fNTAS,
+ BOOL fBuiltin )
+{
+ (void) fBuiltin ; // unused for now.
+
+ if (pszUserName && (lstrcmpi(pszUserName, SUPERVISOR_NAME_STRING)==0))
+ return SUPERVISOR_USERID ;
+
+ return ( fNTAS ? (dwRid | 0x10000000) : dwRid ) ;
+}
+
+
+ULONG SwapObjectId( ULONG ulObjectId )
+{
+ return (MAKELONG(HIWORD(ulObjectId),SWAPWORD(LOWORD(ulObjectId)))) ;
+}
+
diff --git a/private/net/svcdlls/fpnw/client/sources b/private/net/svcdlls/fpnw/client/sources
new file mode 100644
index 000000000..e6a237f47
--- /dev/null
+++ b/private/net/svcdlls/fpnw/client/sources
@@ -0,0 +1,69 @@
+!IF 0
+
+Copyright (c) 1989-93 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+
+Author:
+
+ Rita Wong (ritaw) 14-Feb-1993
+
+
+Revision History:
+
+!ENDIF
+
+MAJORCOMP=fpnw
+MINORCOMP=service
+
+TARGETNAME=fpnwclnt
+TARGETPATH=\nt\public\sdk\lib
+TARGETTYPE=DYNLINK
+DLLBASE = 0x69700000
+
+TARGETLIBS= \
+ \nt\public\sdk\lib\*\kernel32.lib \
+ \nt\public\sdk\lib\*\samsrv.lib \
+ \nt\public\sdk\lib\*\lsasrv.lib \
+ \nt\public\sdk\lib\*\netapi32.lib \
+ \nt\public\sdk\lib\*\rpcrt4.lib \
+ \nt\public\sdk\lib\*\advapi32.lib \
+ \nt\public\sdk\lib\*\rpcutil.lib \
+ \nt\public\sdk\lib\*\user32.lib
+
+INCLUDES=.;..\inc;..\..\..\inc;$(BASEDIR)\private\inc
+
+!IFDEF NTDBGFILES
+BINPLACE_FLAGS=-r $(_NTTREE) -p $(BASEDIR)\private\net\svcdlls\fpnw\nwsplace.txt -s $(_NTTREE)\Symbols
+!ELSE
+BINPLACE_FLAGS=-r $(_NTTREE) -p $(BASEDIR)\private\net\svcdlls\fpnw\nwsplace.txt
+!ENDIF
+
+UNICODE=1
+
+SOURCES= \
+ encrypt.c \
+ logon.c \
+ ncpbind.c \
+ ncpstub.c \
+ ncpsvc_c.c \
+ notify.c \
+ nwsutil.c \
+ usrprop.c \
+ fpnwclnt.rc
+
+C_DEFINES=-DRPC_NO_WINDOWS_H -DUNICODE
+
+#386_WARNING_LEVEL=-W4
+
+NTTARGETFILE0=fpnwclnt.rc
diff --git a/private/net/svcdlls/fpnw/client/usrprop.c b/private/net/svcdlls/fpnw/client/usrprop.c
new file mode 100644
index 000000000..151aee47c
--- /dev/null
+++ b/private/net/svcdlls/fpnw/client/usrprop.c
@@ -0,0 +1,831 @@
+/*++
+
+Copyright (c) 1993-1993 Microsoft Corporation
+
+Module Name:
+
+ usrprop.c
+
+Abstract:
+
+ This module implements QueryUserProperty() and SetUserProperty()
+ which read and write NetWare Properties to the UserParms field.
+
+Author:
+
+ Andy Herron (andyhe) 24-May-1993
+ Congpa You (CongpaY) 28-Oct-1993 Seperated SetUserProperty() and
+ QueryUserProperty() out from usrprop.c
+ in ncpsrv\svcdlls\ncpsvc\libbind,
+ modified the code and fixed a few
+ existing problems.
+
+Revision History:
+
+--*/
+
+#include "nt.h"
+#include "ntrtl.h"
+#include "nturtl.h"
+#include "ntioapi.h"
+#include "windef.h"
+#include "winbase.h"
+#include "stdio.h"
+#include "stdlib.h"
+#include "winuser.h"
+
+#include <fpnwcomm.h>
+#include <usrprop.h>
+
+//
+// All internal (opaque) structures are listed here since no one else
+// needs to reference them.
+//
+
+//
+// The user's Parameter field is mapped out to a structure that contains
+// the backlevel 48 WCHARs for Mac/Ras compatibility plus a new structure
+// that is basically an array of chars that make up a property name plus
+// a property value.
+//
+
+//
+// This is the structure for an individual property. Note that there are
+// no null terminators in this.
+//
+typedef struct _USER_PROPERTY {
+ WCHAR PropertyLength; // length of property name
+ WCHAR ValueLength; // length of property value
+ WCHAR PropertyFlag; // type of property (1 = set, 2 = item)
+ WCHAR Property[1]; // start of property name, followed by value
+} USER_PROPERTY, *PUSER_PROPERTY;
+
+//
+// This is the structure that maps the beginning of the user's Parameters
+// field. It is only separate so that we can do a sizeof() without including
+// the first property, which may or may not be there.
+//
+
+typedef struct _USER_PROPERTIES_HDR {
+ WCHAR BacklevelParms[48]; // RAS & Mac data stored here.
+ WCHAR PropertySignature; // signature that we can look for.
+ WCHAR PropertyCount; // number of properties present.
+} USER_PROPERTIES_HDR, *PUSER_PROPERTIES_HDR;
+
+//
+// This structure maps out the whole of the user's Parameters field when
+// the user properties structure is present and at least one property is
+// defined.
+//
+
+typedef struct _USER_PROPERTIES {
+ USER_PROPERTIES_HDR Header;
+ USER_PROPERTY FirstProperty;
+} USER_PROPERTIES, *PUSER_PROPERTIES;
+
+//
+// forward references
+//
+
+NTSTATUS
+UserPropertyAllocBlock (
+ IN PUNICODE_STRING Existing,
+ IN ULONG DesiredLength,
+ IN OUT PUNICODE_STRING New
+ );
+
+BOOL
+FindUserProperty (
+ PUSER_PROPERTIES UserProperties,
+ LPWSTR Property,
+ PUSER_PROPERTY *pUserProperty,
+ USHORT *pCount
+ );
+
+VOID
+RemoveUserProperty (
+ UNICODE_STRING *puniUserParms,
+ PUSER_PROPERTY UserProperty,
+ USHORT Count,
+ BOOL *Update
+ );
+
+NTSTATUS
+SetUserProperty (
+ IN LPWSTR UserParms,
+ IN LPWSTR Property,
+ IN UNICODE_STRING PropertyValue,
+ IN WCHAR PropertyFlag,
+ OUT LPWSTR *pNewUserParms, // memory has to be freed afer use.
+ OUT BOOL *Update
+ )
+/*
+ This function sets a property field in the user's Parameters field.
+*/
+{
+ NTSTATUS status;
+ UNICODE_STRING uniUserParms;
+ UNICODE_STRING uniNewUserParms;
+ USHORT Count = 0;
+ USHORT PropertyLength;
+ USHORT ValueLength;
+ PUSER_PROPERTIES UserProperties;
+ PUSER_PROPERTY UserProperty;
+ LPWSTR PropertyValueString = NULL;
+ USHORT oldUserParmsLength;
+ INT i;
+ UCHAR *pchValue = NULL;
+
+ // Check if parameters are correct.
+ if (Property == NULL)
+ {
+ return( STATUS_INVALID_PARAMETER );
+ }
+
+ // Initialize output variables.
+ *Update = FALSE;
+ *pNewUserParms = NULL;
+
+ try {
+
+ oldUserParmsLength = (lstrlenW(UserParms) + 1) * sizeof(WCHAR);
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ //
+ // if we can't get the length of the current UserParameters, we loose
+ // the whole thing.
+ //
+
+ UserParms = NULL;
+ }
+
+ // Convert UserParms to unicode string.
+ uniUserParms.Buffer = UserParms;
+ uniUserParms.Length = UserParms ? oldUserParmsLength : 0;
+ uniUserParms.MaximumLength = uniUserParms.Length;
+
+ /** Get the length of the property name **/
+
+ PropertyLength = lstrlenW( Property ) * sizeof(WCHAR);
+
+ /** Get the length of the property value **/
+ ValueLength = PropertyValue.Length;
+
+ if (ValueLength != 0)
+ {
+ // Convert property value to asci string so that
+ // if property value is 0, it can be stored correctly.
+
+ PropertyValueString = (LPWSTR) LocalAlloc (LPTR, (ValueLength+1)*sizeof (WCHAR));
+ if (!PropertyValueString)
+ {
+ return(STATUS_NO_MEMORY) ;
+ }
+
+ pchValue = (UCHAR *) PropertyValue.Buffer;
+
+ // BUGBUG. Since wsprint converts 0x00 to 20 30 (20 is
+ // space and 30 is 0), sscanf converts 20 30 to 0. If the
+ // value is uncode string, this convertsion would not
+ // convert back to original value. So if we want to store
+ // some value in the UserParms, we have to pass in ansi
+ // string.
+
+ for (i = 0; i < ValueLength; i++)
+ {
+ wsprintfA ((PCHAR)(PropertyValueString+i), "%02x", *(pchValue+i));
+ }
+
+ *(PropertyValueString+ValueLength) = 0;
+ ValueLength = ValueLength * sizeof (WCHAR);
+ }
+
+ //
+ // check that user has valid property structure , if not, create one
+ //
+
+ if (UserParms != NULL)
+ {
+ Count = oldUserParmsLength;
+ }
+
+ if (Count < sizeof( USER_PROPERTIES))
+ {
+ Count = sizeof( USER_PROPERTIES_HDR ) + sizeof(WCHAR);
+ }
+
+ if (ValueLength > 0)
+ {
+ Count += sizeof( USER_PROPERTY ) + PropertyLength + ValueLength;
+ }
+
+ if (Count > 0x7FFF)
+ {
+ // can't be bigger than 32K of user parms.
+ return (STATUS_BUFFER_OVERFLOW);
+ }
+
+ try {
+
+ status = UserPropertyAllocBlock( &uniUserParms,
+ Count,
+ &uniNewUserParms );
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ //
+ // if we can't copy the current UserParameters, we loose the whole thing.
+ //
+
+ UserParms = NULL;
+ uniUserParms.Buffer = UserParms;
+ uniUserParms.Length = 0;
+ uniUserParms.MaximumLength = uniUserParms.Length;
+
+ Count = sizeof( USER_PROPERTIES_HDR ) + sizeof(WCHAR);
+ if (ValueLength > 0) {
+ Count += sizeof( USER_PROPERTY ) + PropertyLength + ValueLength;
+ }
+
+ status = UserPropertyAllocBlock( &uniUserParms,
+ Count,
+ &uniNewUserParms );
+ }
+
+ if ( !NT_SUCCESS(status) ) {
+ return status;
+ }
+
+ // Make the output pNewUserParms point to uniNewUserPams's buffer
+ // which is the new UserParms string.
+
+ *pNewUserParms = uniNewUserParms.Buffer;
+
+ UserProperties = (PUSER_PROPERTIES) uniNewUserParms.Buffer;
+
+ try {
+
+ if (FindUserProperty (UserProperties,
+ Property,
+ &UserProperty,
+ &Count))
+ {
+ RemoveUserProperty ( &uniNewUserParms,
+ UserProperty,
+ Count,
+ Update);
+ }
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ //
+ // we have corrupted user parms here... get rid of them.
+ //
+
+ *Update = TRUE;
+
+ if (*pNewUserParms != NULL) {
+ LocalFree( *pNewUserParms );
+ }
+ *pNewUserParms = NULL;
+ status = STATUS_INVALID_PARAMETER;
+ }
+
+ if ( !NT_SUCCESS(status) ) {
+ return status;
+ }
+
+ //
+ // If the new value of the property is not null, add it.
+ //
+
+ if (ValueLength > 0) {
+
+ try {
+
+ // find the end of the parameters list
+
+ UserProperty = &(UserProperties->FirstProperty);
+
+ for (Count = 1; Count <= UserProperties->Header.PropertyCount; Count++)
+ {
+ UserProperty = (PUSER_PROPERTY)
+ ((LPSTR)((LPSTR) UserProperty +
+ sizeof(USER_PROPERTY) + // length of entry
+ UserProperty->PropertyLength +
+ UserProperty->ValueLength -
+ sizeof(WCHAR))); // for Property[0]
+ }
+
+ //
+ // append it to the end and update length of string
+ //
+
+ UserProperty->PropertyFlag = (PropertyFlag & NCP_SET) ?
+ USER_PROPERTY_TYPE_SET :
+ USER_PROPERTY_TYPE_ITEM;
+
+ UserProperty->PropertyLength = PropertyLength;
+ UserProperty->ValueLength = ValueLength;
+
+ RtlCopyMemory( &(UserProperty->Property[0]),
+ Property,
+ PropertyLength );
+
+ RtlCopyMemory( &(UserProperty->Property[PropertyLength / sizeof(WCHAR)]),
+ PropertyValueString,
+ ValueLength );
+
+ uniNewUserParms.Length +=
+ sizeof(USER_PROPERTY) + // length of entry
+ PropertyLength + // length of property name string
+ ValueLength - // length of value string
+ sizeof(WCHAR); // account for WCHAR Property[1]
+
+ UserProperties->Header.PropertyCount++;
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ //
+ // we have corrupted user parms here... get rid of them.
+ //
+
+ if (*pNewUserParms != NULL) {
+ LocalFree( *pNewUserParms );
+ }
+ *pNewUserParms = NULL;
+ status = STATUS_INVALID_PARAMETER;
+ }
+ *Update = TRUE;
+ }
+
+ // UserParms is already null terminated. We don't need to set the
+ // end of UserParms to be NULL since we zero init the buffer already.
+
+ return( status );
+}
+
+NTSTATUS
+SetUserPropertyWithLength (
+ IN PUNICODE_STRING UserParms,
+ IN LPWSTR Property,
+ IN UNICODE_STRING PropertyValue,
+ IN WCHAR PropertyFlag,
+ OUT LPWSTR *pNewUserParms, // memory has to be freed afer use.
+ OUT BOOL *Update
+ )
+/*
+ This function sets a property field in the user's Parameters field.
+*/
+{
+ NTSTATUS status;
+ UNICODE_STRING newUserParms;
+ ULONG length;
+ PWCHAR ptr;
+
+ length = UserParms->Length;
+
+ if (UserParms->MaximumLength < length + sizeof(WCHAR)) {
+
+ //
+ // have to realloc
+ //
+
+ newUserParms.Buffer = UserParms->Buffer;
+ newUserParms.Length =
+ newUserParms.MaximumLength =
+ (USHORT) ( length + sizeof(WCHAR) );
+
+ newUserParms.Buffer = LocalAlloc (LPTR, newUserParms.Length);
+
+ if (newUserParms.Buffer == NULL) {
+
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ RtlCopyMemory( newUserParms.Buffer,
+ UserParms->Buffer,
+ length );
+
+ } else {
+
+ newUserParms.Buffer = UserParms->Buffer;
+ newUserParms.Length = (USHORT) length;
+ newUserParms.MaximumLength = UserParms->MaximumLength;
+ }
+
+ //
+ // Slap in null terminator
+ //
+
+ ptr = newUserParms.Buffer + ( length / sizeof(WCHAR) );
+ *ptr = L'\0';
+
+ status = SetUserProperty( newUserParms.Buffer,
+ Property,
+ PropertyValue,
+ PropertyFlag,
+ pNewUserParms,
+ Update );
+
+ if (newUserParms.Buffer != UserParms->Buffer) {
+
+ LocalFree( newUserParms.Buffer );
+ }
+
+ return(status);
+}
+
+#define MAPHEXTODIGIT(x) ( x >= '0' && x <= '9' ? (x-'0') : \
+ x >= 'A' && x <= 'F' ? (x-'A'+10) : \
+ x >= 'a' && x <= 'f' ? (x-'a'+10) : 0 )
+
+
+NTSTATUS
+QueryUserProperty (
+ IN LPWSTR UserParms,
+ IN LPWSTR PropertyName,
+ OUT PWCHAR PropertyFlag,
+ OUT PUNICODE_STRING PropertyValue
+ )
+/*
+ This routine returns a user definable property value as it is stored
+ in the user's Parameters field. Note that the RAS/MAC fields are
+ stripped before we start processing user properties.
+*/
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ USHORT PropertyNameLength;
+ USHORT Count;
+ PUSER_PROPERTY UserProperty;
+ WCHAR *Value;
+ UINT i;
+ CHAR *PropertyValueString = NULL;
+ CHAR *pchValue;
+
+ // Set PropertyValue->Length to 0 initially. If the property is not found
+ // it will still be 0 on exit.
+
+ PropertyValue->Length = 0;
+ PropertyValue->Buffer = NULL;
+
+ try {
+
+ PropertyNameLength = lstrlenW(PropertyName) * sizeof(WCHAR);
+
+ // Check if UserParms have the right structure.
+
+ if (FindUserProperty ((PUSER_PROPERTIES) UserParms,
+ PropertyName,
+ &UserProperty,
+ &Count) ) {
+
+ Value = (LPWSTR)(LPSTR)((LPSTR) &(UserProperty->Property[0]) +
+ PropertyNameLength);
+
+ //
+ // Found the requested property
+ //
+
+ //
+ // Copy the property flag.
+ //
+
+ if (PropertyFlag) {
+ *PropertyFlag = UserProperty->PropertyFlag;
+ }
+
+ // Allocate memory for PropertyValue->Buffer
+
+ PropertyValueString = LocalAlloc ( LPTR, UserProperty->ValueLength+1);
+ PropertyValue->Buffer = LocalAlloc ( LPTR, UserProperty->ValueLength/sizeof(WCHAR));
+
+ //
+ // Make sure the property value length is valid.
+ //
+ if ((PropertyValue->Buffer == NULL) || (PropertyValueString == NULL)) {
+
+ status = STATUS_INSUFFICIENT_RESOURCES;
+
+ if (PropertyValue->Buffer != NULL) {
+ LocalFree( PropertyValue->Buffer );
+ PropertyValue->Buffer = NULL;
+ }
+
+ } else {
+
+ //
+ // Copy the property value to the buffer.
+ //
+
+ RtlCopyMemory( PropertyValueString,
+ Value,
+ UserProperty->ValueLength );
+
+ pchValue = (CHAR *) PropertyValue->Buffer;
+
+ // Convert from value unicode string to value.
+ for (i = 0; i < UserProperty->ValueLength/sizeof(WCHAR) ; i++)
+ {
+ // sscanf will trash memory.
+ // sscanf( PropertyValueString+2*i, "%2x", pchValue+i);
+
+ pchValue[i] = MAPHEXTODIGIT( PropertyValueString[2*i]) * 16 +
+ MAPHEXTODIGIT( PropertyValueString[2*i+1]);
+ }
+
+ PropertyValue->Length = UserProperty->ValueLength/sizeof(WCHAR);
+ PropertyValue->MaximumLength = UserProperty->ValueLength/sizeof(WCHAR);
+ }
+ }
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ //
+ // we have corrupted user parms here... can't return a decent value
+ //
+
+ if (PropertyValue->Buffer != NULL) {
+ LocalFree( PropertyValue->Buffer );
+ PropertyValue->Buffer = NULL;
+ }
+
+ PropertyValue->Length = 0;
+ status = STATUS_INVALID_PARAMETER;
+ }
+
+ if ( PropertyValueString != NULL ) {
+ LocalFree( PropertyValueString);
+ }
+
+ return status;
+}
+
+NTSTATUS
+QueryUserPropertyWithLength (
+ IN PUNICODE_STRING UserParms,
+ IN LPWSTR PropertyName,
+ OUT PWCHAR PropertyFlag,
+ OUT PUNICODE_STRING PropertyValue
+ )
+/*
+ This routine returns a user definable property value as it is stored
+ in the user's Parameters field. Note that the RAS/MAC fields are
+ stripped before we start processing user properties.
+*/
+{
+ NTSTATUS status;
+ UNICODE_STRING newUserParms;
+ ULONG length;
+ PWCHAR ptr;
+
+ length = UserParms->Length;
+
+ if (UserParms->MaximumLength < length + sizeof(WCHAR)) {
+
+ //
+ // have to realloc
+ //
+
+ newUserParms.Buffer = UserParms->Buffer;
+ newUserParms.Length =
+ newUserParms.MaximumLength =
+ (USHORT) (length + sizeof(WCHAR) );
+
+ newUserParms.Buffer = LocalAlloc (LPTR, newUserParms.Length);
+
+ if (newUserParms.Buffer == NULL) {
+
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ RtlCopyMemory( newUserParms.Buffer,
+ UserParms->Buffer,
+ length );
+
+ } else {
+
+ newUserParms.Buffer = UserParms->Buffer;
+ newUserParms.Length = (USHORT) length;
+ newUserParms.MaximumLength = UserParms->MaximumLength;
+ }
+
+ //
+ // Slap in null terminator
+ //
+
+ ptr = newUserParms.Buffer + ( length / sizeof(WCHAR) );
+ *ptr = L'\0';
+
+ status = QueryUserProperty( newUserParms.Buffer,
+ PropertyName,
+ PropertyFlag,
+ PropertyValue );
+
+ if (newUserParms.Buffer != UserParms->Buffer) {
+
+ LocalFree( newUserParms.Buffer );
+ }
+
+ return(status);
+}
+
+// Common routine used by QueryUserProperty() and SetUserProperty().
+
+BOOL
+FindUserProperty (
+ PUSER_PROPERTIES UserProperties,
+ LPWSTR Property,
+ PUSER_PROPERTY *pUserProperty,
+ USHORT *pCount
+ )
+{
+ BOOL fFound = FALSE;
+ USHORT PropertyLength;
+
+ //
+ // Check if user has valid property structure attached,
+ // pointed to by UserProperties.
+ //
+
+ if ( ( UserProperties != NULL )
+ && ( lstrlenW( (LPWSTR) UserProperties) * sizeof(WCHAR) >
+ sizeof(UserProperties->Header.BacklevelParms))
+ && ( UserProperties->Header.PropertySignature == USER_PROPERTY_SIGNATURE)
+ )
+ {
+ //
+ // user has valid property structure.
+ //
+
+ *pUserProperty = &(UserProperties->FirstProperty);
+
+ PropertyLength = lstrlenW( Property ) * sizeof(WCHAR);
+
+ for ( *pCount = 1; *pCount <= UserProperties->Header.PropertyCount;
+ (*pCount)++ )
+ {
+ if ( ( PropertyLength == (*pUserProperty)->PropertyLength )
+ && ( RtlCompareMemory( &((*pUserProperty)->Property[0]),
+ Property,
+ PropertyLength ) == PropertyLength )
+ )
+ {
+ fFound = TRUE;
+ break;
+ }
+
+ *pUserProperty = (PUSER_PROPERTY)
+ ((LPSTR) (*pUserProperty)
+ + sizeof( USER_PROPERTY )
+ + (*pUserProperty)->PropertyLength
+ + (*pUserProperty)->ValueLength
+ - sizeof(WCHAR)); // for Property[0]
+ }
+ }
+
+ return( fFound );
+}
+
+
+// Remove a property field from the User Parms.
+
+VOID
+RemoveUserProperty (
+ UNICODE_STRING *puniUserParms,
+ PUSER_PROPERTY UserProperty,
+ USHORT Count,
+ BOOL *Update
+ )
+{
+ PUSER_PROPERTIES UserProperties;
+ PUSER_PROPERTY NextProperty;
+ USHORT OldParmLength;
+
+ UserProperties = (PUSER_PROPERTIES) puniUserParms->Buffer;
+
+ OldParmLength = sizeof( USER_PROPERTY ) +
+ UserProperty->PropertyLength +
+ UserProperty->ValueLength -
+ sizeof(WCHAR); // for Property[0]
+
+
+ NextProperty = (PUSER_PROPERTY)(LPSTR)((LPSTR) UserProperty + OldParmLength);
+
+ //
+ // if we're not on the last one, copy the remaining buffer up
+ //
+
+ if (Count < UserProperties->Header.PropertyCount) {
+
+ RtlMoveMemory( UserProperty,
+ NextProperty,
+ puniUserParms->Length -
+ ((LPSTR) NextProperty -
+ (LPSTR) puniUserParms->Buffer ));
+ }
+
+ //
+ // Now reduce the length of the buffer by the amount we pulled out
+ //
+
+ puniUserParms->Length -= OldParmLength;
+
+ UserProperties->Header.PropertyCount--;
+
+ *Update = TRUE;
+}
+
+
+NTSTATUS
+UserPropertyAllocBlock (
+ IN PUNICODE_STRING Existing,
+ IN ULONG DesiredLength,
+ IN OUT PUNICODE_STRING New
+ )
+/*
+ This allocates a larger block for user's parameters and copies the old
+ block in.
+*/
+{
+ PUSER_PROPERTIES UserProperties;
+ CLONG Count;
+ WCHAR *pNewBuff;
+
+
+ //
+ // We will allocate a new buffer to store the new parameters
+ // and copy the existing parameters into it.
+ //
+
+ New->Buffer = LocalAlloc (LPTR, DesiredLength);
+
+ if ( New->Buffer == NULL)
+ {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ New->MaximumLength = (USHORT) DesiredLength;
+
+ if (Existing != NULL)
+ {
+
+ New->Length = Existing->Length;
+
+ RtlCopyMemory( New->Buffer,
+ Existing->Buffer,
+ Existing->Length );
+ }
+ else
+ {
+ New->Length = 0;
+ }
+
+ //
+ // Ensure that we don't have any nulls in our string.
+ //
+
+ for ( Count = 0;
+ Count < New->Length / sizeof(WCHAR);
+ Count++ )
+ {
+ if (*(New->Buffer + Count) == L'\0')
+ {
+ New->Length = (USHORT) Count * sizeof(WCHAR);
+ break;
+ }
+ }
+
+ //
+ // now pad it out with spaces until reached Mac+Ras reserved length
+ //
+
+ pNewBuff = (WCHAR *) New->Buffer + ( New->Length / sizeof(WCHAR) );
+
+ while ( New->Length < sizeof(UserProperties->Header.BacklevelParms))
+ {
+ *( pNewBuff++ ) = L' ';
+ New->Length += sizeof(WCHAR);
+ }
+
+ //
+ // If the signature isn't there, stick it in and set prop count to 0
+ //
+
+ UserProperties = (PUSER_PROPERTIES) New->Buffer;
+
+ if (New->Length < sizeof(USER_PROPERTIES_HDR) ||
+ UserProperties->Header.PropertySignature != USER_PROPERTY_SIGNATURE)
+ {
+
+ UserProperties->Header.PropertySignature = USER_PROPERTY_SIGNATURE;
+ UserProperties->Header.PropertyCount = 0;
+
+ New->Length = sizeof(USER_PROPERTIES_HDR);
+ }
+
+ return STATUS_SUCCESS;
+}
+
+// usrprop.c eof.
+
+
diff --git a/private/net/svcdlls/fpnw/dirs b/private/net/svcdlls/fpnw/dirs
new file mode 100644
index 000000000..a26ad3aa6
--- /dev/null
+++ b/private/net/svcdlls/fpnw/dirs
@@ -0,0 +1,24 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ dirs.
+
+Abstract:
+
+ This file specifies the subdirectories of the current directory that
+ contain component makefiles.
+
+
+Author:
+
+ Steve Wood (stevewo) 17-Apr-1990
+
+NOTE: Commented description of this file is in \nt\bak\bin\dirs.tpl
+
+!ENDIF
+
+
+DIRS= client
diff --git a/private/net/svcdlls/fpnw/imports.idl b/private/net/svcdlls/fpnw/imports.idl
new file mode 100644
index 000000000..df9f49940
--- /dev/null
+++ b/private/net/svcdlls/fpnw/imports.idl
@@ -0,0 +1,57 @@
+/*++
+
+Copyright (c) 1991-1993 Microsoft Corporation
+
+Module Name:
+
+ imports.idl
+
+Abstract:
+
+ This file is useful for creating RPC interfaces that require the use
+ of windef types. The .idl file for the RPC product should contain a
+ line in the interface body that imports this file. For example:
+
+ import "imports.h";
+
+ Doing this causes the MIDL generated header file to contain the
+ following line:
+
+ #include "imports.h"
+
+ If this technique is not used, and instead the .idl file for the RPC
+ product simply contains #include <importsf.h>, then the contents of
+ imports.h will be expanded in the MIDL generated header file. This
+ can lead to duplicate definition problems later when the RPC client
+ or RPC server code needs to include both the MIDL generated header file
+ and a file that is included in imports.h.
+
+Author:
+
+ Dan Lafferty (danl) 20-Mar-1991
+ Rita Wong (ritaw) 14-Feb-1993
+ Yi-Hsin Sung (yihsins) 14-Nov-1993
+
+Environment:
+
+ User Mode - Win32 - for use with the MIDL compiler
+
+
+Revision History:
+
+--*/
+
+[
+#ifdef __midl
+ ms_union,
+#endif // __midl
+ version(1.0)
+]
+
+interface imports
+
+{
+#define MIDL_PASS
+#include "imports.h"
+
+}
diff --git a/private/net/svcdlls/fpnw/inc/fpnwapi.h b/private/net/svcdlls/fpnw/inc/fpnwapi.h
new file mode 100644
index 000000000..058c806fb
--- /dev/null
+++ b/private/net/svcdlls/fpnw/inc/fpnwapi.h
@@ -0,0 +1,347 @@
+/****************************************************************************
+* *
+* fpnwapi.h -- FPNW procedure declarations, constant definitions and macros *
+* *
+* Copyright (c) 1994-1995, Microsoft Corp. All rights reserved. *
+* *
+****************************************************************************/
+
+#ifndef _FPNWAPI_H_
+#define _FPNWAPI_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+//
+// Volume flags returned by VolumeGetInfo
+//
+
+#define FPNWVOL_TYPE_DISKTREE 0
+#define FPNWVOL_TYPE_CDROM 104
+#define FPNWVOL_TYPE_REMOVABLE 105
+
+//
+// Permissions flags returned in structure FPNWFILEINFO
+//
+
+#define FPNWFILE_PERM_NONE 0
+#define FPNWFILE_PERM_READ 0x01
+#define FPNWFILE_PERM_WRITE 0x02
+#define FPNWFILE_PERM_CREATE 0x04
+#define FPNWFILE_PERM_EXEC 0x08
+#define FPNWFILE_PERM_DELETE 0x10
+#define FPNWFILE_PERM_ATRIB 0x20
+#define FPNWFILE_PERM_PERM 0x40
+
+typedef BYTE FPNWSERVERADDR[12]; // Network address, first 4 bytes is
+ // the network number, and bytes
+ // 5-10 is the physical node
+ // address. The last two bytes are
+ // reserved.
+
+//
+// This is the level 1 structure for FpnwServerGetInfo & FpnwServerSetInfo.
+//
+
+typedef struct _FPNWServerInfo
+{
+ LPWSTR lpServerName; // Name of the server
+ DWORD dwNetwareMajorVersion; // Netware compatible major version num
+ DWORD dwNetwareMinorVersion; // Netware compatible minor version num
+ DWORD dwOSRevision; // OS revision number
+ DWORD dwMaxConnections; // Maximum number of connections
+ // supported
+ DWORD dwVolumes; // The current number of volumes on the
+ // server
+ DWORD dwLoggedOnUsers; // Number of current users logged on
+ DWORD dwConnectedWorkstations;// Number of workstations connected
+ DWORD dwOpenFiles; // Number of open files
+ DWORD dwFileLocks; // Number of file locks
+ FPNWSERVERADDR NetworkAddress; // Address consisting of network
+ // number (first 4 bytes) and the
+ // physical node address(bytes 5-10)
+ BOOL fEnableLogin; // TRUE if users are allowed to logged
+ // on, FALSE otherwise.
+ LPWSTR lpDescription; // Description of the server
+ LPWSTR lpHomeDirectory; // Path of the home directory
+
+} FPNWSERVERINFO, *PFPNWSERVERINFO;
+
+
+//
+// This is the level 1 structure for FpnwVolumeAdd, FpnwVolumeDel, FpnwVolumeEnum,
+// FpnwVolumeGetInfo, & FpnwVolumeSetInfo.
+//
+
+typedef struct _FPNWVolumeInfo
+{
+ LPWSTR lpVolumeName; // Name of the volume
+ DWORD dwType; // Specifics of the volume. FPNWVOL_TYPE_???
+ DWORD dwMaxUses; // Maximum number of connections that are
+ // allowed to the volume
+ DWORD dwCurrentUses; // Current number of connections to the volume
+ LPWSTR lpPath; // Path of the volume
+
+} FPNWVOLUMEINFO, *PFPNWVOLUMEINFO;
+
+
+//
+// This is the level 2 structure for FpnwVolumeAdd, FpnwVolumeDel, FpnwVolumeEnum,
+// FpnwVolumeGetInfo, & FpnwVolumeSetInfo.
+// Note that this is not supported on the FPNW beta.
+//
+
+typedef struct _FPNWVolumeInfo_2
+{
+ LPWSTR lpVolumeName; // Name of the volume
+ DWORD dwType; // Specifics of the volume. FPNWVOL_TYPE_???
+ DWORD dwMaxUses; // Maximum number of connections that are
+ // allowed to the volume
+ DWORD dwCurrentUses; // Current number of connections to the volume
+ LPWSTR lpPath; // Path of the volume
+
+ DWORD dwFileSecurityDescriptorLength; // reserved, this is calculated
+ PSECURITY_DESCRIPTOR FileSecurityDescriptor;
+
+} FPNWVOLUMEINFO_2, *PFPNWVOLUMEINFO_2;
+
+
+//
+// This is the level 1 structure for FpnwConnectionEnum.
+//
+
+typedef struct _FPNWConnectionInfo
+{
+ DWORD dwConnectionId; // Identification number for this connection
+ FPNWSERVERADDR WkstaAddress; // The workstation address which established
+ // the conn.
+ DWORD dwAddressType; // Address type: IP, IPX ...
+ LPWSTR lpUserName; // The name of the user which established
+ // the conn.
+ DWORD dwOpens; // Number of resources opened during this conn.
+ DWORD dwLogonTime; // Time this connection has been active
+ BOOL fLoggedOn; // TRUE if the user is logged on,FALSE otherwise
+ DWORD dwForcedLogoffTime; // Time left before forcing logoff
+ BOOL fAdministrator; // TRUE if the user is an administrator,
+ // FALSE otherwise
+
+} FPNWCONNECTIONINFO, *PFPNWCONNECTIONINFO;
+
+
+//
+// This is the level 1 structure for FpnwVolumeConnEnum.
+//
+
+typedef struct _FPNWVolumeConnectionInfo
+{
+ USHORT nDriveLetter; // Driver letter mapped to the volume by user
+ DWORD dwConnectionId; // Identification number for this connection
+ DWORD dwConnType; // The type of connection: FPNWVOL_TYPE_DISK,
+ // FPNWVOL_TYPE_PRINTER
+ DWORD dwOpens; // The number of open files on this connection.
+ DWORD dwTime; // Time this connection is active (or connected)
+ LPWSTR lpUserName; // The user who established the connection
+ LPWSTR lpConnectName; // The workstation address OR volume name based
+ // on the qualifier to FpnwConnectionEnum
+
+} FPNWVOLUMECONNINFO, *PFPNWVOLUMECONNINFO;
+
+
+//
+// This is the level 1 structure for FpnwFileEnum.
+//
+
+typedef struct _FPNWFileInfo
+{
+ DWORD dwFileId; // File identification number
+ LPWSTR lpPathName; // Full path name of this file
+ LPWSTR lpVolumeName; // Volume name this file is on
+ DWORD dwPermissions; // Permission mask: FPNWFILE_PERM_READ,
+ // FPNWFILE_PERM_WRITE,
+ // FPNWFILE_PERM_CREATE...
+ DWORD dwLocks; // Number of locks on this file
+ LPWSTR lpUserName; // The name of the user that established the
+ // connection and opened the file
+ FPNWSERVERADDR WkstaAddress; // The workstation address which opened the file
+ DWORD dwAddressType; // Address type: IP, IPX
+
+} FPNWFILEINFO, *PFPNWFILEINFO;
+
+
+//
+// Below are the APIs available to manipulate FPNW servers, volumes, etc.
+//
+
+//
+// The FpnwApiBufferFree should be called for any buffer returned by the
+// other APIs.
+//
+
+DWORD
+FpnwApiBufferFree(
+ IN LPVOID pBuffer
+);
+
+//
+// For Level 1, an FPNWSERVERINFO structure is returned in *ppServerInfo.
+//
+
+DWORD
+FpnwServerGetInfo(
+ IN LPWSTR pServerName OPTIONAL,
+ IN DWORD dwLevel,
+ OUT LPBYTE *ppServerInfo
+);
+
+
+//
+// The following fields are modified by a call to FpnwServerSetInfo :
+//
+// LPWSTR lpDescription; // Description of the server
+// BOOL fEnableLogin; // TRUE if users are allowed to logged
+// LPWSTR lpHomeDirectory; // Path of the home directory
+//
+// All other fields in FPNWSERVERINFO structure are ignored. Also note
+// that lpHomeDirectory and lpDescription require a restart for the server
+// to pick up the changes.
+//
+
+//
+// For Level 1, an FPNWSERVERINFO structure should be passed as pServerInfo.
+//
+
+DWORD
+FpnwServerSetInfo(
+ IN LPWSTR pServerName OPTIONAL,
+ IN DWORD dwLevel,
+ IN LPBYTE pServerInfo
+);
+
+
+//
+// For FpnwVolumeAdd, FpnwVolumeEnum, FpnwVolumeSetInfo, and
+// FpnwVolumeGetInfo, the following holds:
+// Level 1 -> an FPNWVOLUMEINFO structure should be passed as pVolumeInfo.
+// Level 2 -> an FPNWVOLUMEINFO_2 structure should be passed as pVolumeInfo.
+//
+
+DWORD
+FpnwVolumeAdd(
+ IN LPWSTR pServerName OPTIONAL,
+ IN DWORD dwLevel,
+ IN LPBYTE pVolumeInfo
+);
+
+DWORD
+FpnwVolumeDel(
+ IN LPWSTR pServerName OPTIONAL,
+ IN LPWSTR pVolumeName
+);
+
+DWORD
+FpnwVolumeEnum(
+ IN LPWSTR pServerName OPTIONAL,
+ IN DWORD dwLevel,
+ OUT LPBYTE *ppVolumeInfo,
+ OUT PDWORD pEntriesRead,
+ IN OUT PDWORD resumeHandle OPTIONAL
+);
+
+DWORD
+FpnwVolumeGetInfo(
+ IN LPWSTR pServerName OPTIONAL,
+ IN LPWSTR pVolumeName,
+ IN DWORD dwLevel,
+ OUT LPBYTE *ppVolumeInfo
+);
+
+
+//
+// The following fields are modified by a call to FpnwVolumeSetInfo :
+//
+// DWORD dwMaxUses; // Maximum number of connections that are
+// PSECURITY_DESCRIPTOR FileSecurityDescriptor;
+//
+// All other fields in FPNWVOLUMEINFO structure are ignored. You may send
+// in a pointer to an FPNWVOLUMEINFO_2 structure instead of FPNWVOLUMEINFO.
+//
+
+DWORD
+FpnwVolumeSetInfo(
+ IN LPWSTR pServerName OPTIONAL,
+ IN LPWSTR pVolumeName,
+ IN DWORD dwLevel,
+ IN LPBYTE pVolumeInfo
+);
+
+//
+// For Level 1, an FPNWCONNECTIONINFO structure is returned in *ppConnectionInfo.
+//
+
+DWORD
+FpnwConnectionEnum(
+ IN LPWSTR pServerName OPTIONAL,
+ IN DWORD dwLevel,
+ OUT LPBYTE *ppConnectionInfo,
+ OUT PDWORD pEntriesRead,
+ IN OUT PDWORD resumeHandle OPTIONAL
+);
+
+DWORD FpnwConnectionDel(
+ IN LPWSTR pServerName OPTIONAL,
+ IN DWORD dwConnectionId
+);
+
+
+//
+// For Level 1, an PFPNWVOLUMECONNINFO structure is returned in *ppVolumeConnInfo.
+//
+
+DWORD
+FpnwVolumeConnEnum(
+ IN LPWSTR pServerName OPTIONAL,
+ IN DWORD dwLevel,
+ IN LPWSTR pVolumeName,
+ IN DWORD dwConnectionId,
+ OUT LPBYTE *ppVolumeConnInfo,
+ OUT PDWORD pEntriesRead,
+ IN OUT PDWORD resumeHandle OPTIONAL
+);
+
+
+//
+// For Level 1, an PFPNWFILEINFO structure is returned in *ppFileInfo.
+//
+
+DWORD
+FpnwFileEnum(
+ IN LPWSTR pServerName OPTIONAL,
+ IN DWORD dwLevel,
+ IN LPWSTR pPathName OPTIONAL,
+ OUT LPBYTE *ppFileInfo,
+ OUT PDWORD pEntriesRead,
+ IN OUT PDWORD resumeHandle OPTIONAL
+);
+
+DWORD
+FpnwFileClose(
+ IN LPWSTR pServerName OPTIONAL,
+ IN DWORD nFileId
+);
+
+
+DWORD FpnwMessageBufferSend(
+ IN LPWSTR pServerName OPTIONAL,
+ IN DWORD dwConnectionId,
+ IN DWORD fConsoleBroadcast,
+ IN LPBYTE pbBuffer,
+ IN DWORD cbBuffer
+);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif
+
diff --git a/private/net/svcdlls/fpnw/inc/imports.h b/private/net/svcdlls/fpnw/inc/imports.h
new file mode 100644
index 000000000..426a2763e
--- /dev/null
+++ b/private/net/svcdlls/fpnw/inc/imports.h
@@ -0,0 +1,9 @@
+#include <windef.h>
+
+#ifdef MIDL_PASS
+#define LPWSTR [string] wchar_t *
+#define BOOL DWORD
+#endif
+
+#include "nwstruct.h"
+
diff --git a/private/net/svcdlls/fpnw/inc/nwprint.h b/private/net/svcdlls/fpnw/inc/nwprint.h
new file mode 100644
index 000000000..ee2d80c08
--- /dev/null
+++ b/private/net/svcdlls/fpnw/inc/nwprint.h
@@ -0,0 +1,157 @@
+/*++
+
+Copyright (c) 1993-1995, Microsoft Corp. All rights reserved.
+
+Module Name:
+
+ nw\svcdlls\ncpsvc\proc\nwprint.h
+
+Abstract:
+
+ Include file for the NCP print processor.
+
+Author:
+
+ Tommy Evans (vtommye) 02-16-1993
+
+Revision History:
+
+--*/
+
+/** Data types we support **/
+
+#define PRINTPROCESSOR_TYPE_RAW 0
+#define PRINTPROCESSOR_TYPE_RAW_FF 1
+#define PRINTPROCESSOR_TYPE_RAW_FF_AUTO 2
+#define PRINTPROCESSOR_TYPE_JOURNAL 3
+#define PRINTPROCESSOR_TYPE_TEXT 4
+#define PRINTPROCESSOR_TYPE_NT_TEXT 5
+#define PRINTPROCESSOR_TYPE_NUM 6 /* What is this? */
+
+/** This is so we can compile JOURNAL.C **/
+
+extern BOOL GdiPlayJournal(HDC, LPWSTR, DWORD, DWORD, INT);
+
+extern HANDLE NCPXsPortHandle;
+
+#define IDS_PSERVER_PORT 400
+
+/** Structure used to track jobs **/
+
+typedef struct _PRINTPROCESSORDATA {
+ DWORD signature;
+ DWORD cb;
+ struct _PRINTPROCESSORDATA *pNext;
+ DWORD fsStatus;
+ DWORD uDatatype;
+ DWORD JobId;
+ DWORD Copies; /* Number of copies to print */
+ DWORD TabSize; /* Tab expansion size */
+ ULONG QueueId; /* Object id of the queue */
+ HANDLE semPaused; /* Semaphore for job pausing */
+ HANDLE hPrinter;
+ HANDLE hLPCPort;
+ HDC hDC;
+ LPWSTR pPortName; /* Text string for printer port */
+ LPWSTR pPrinterName; /* Text string for printer name */
+ LPWSTR pDocument;
+ LPWSTR pOutputFile;
+ LPWSTR pDatatype; /* Text string for datatype */
+ LPWSTR pParameters; /* Parameters string for job */
+ USHORT NcpJobNumber; /* NetWare job number for this job */
+ BOOL PServerPortFlag; /* Flag if on a PServer port */
+ BOOL PServerAttachedFlag; /* Flag if PServer attached to q */
+} PRINTPROCESSORDATA, *PPRINTPROCESSORDATA;
+
+#define PRINTPROCESSORDATA_SIGNATURE 0x5051 /* 'QP' is the signature value */
+
+/* Define flags for fsStatus field */
+
+#define PRINTPROCESSOR_ABORTED 0x0001
+#define PRINTPROCESSOR_PAUSED 0x0002
+#define PRINTPROCESSOR_CLOSED 0x0004
+
+#define PRINTPROCESSOR_RESERVED 0xFFF8
+
+/** Flags used for the GetKey routing **/
+
+#define VALUE_STRING 0x01
+#define VALUE_ULONG 0x02
+
+/** Buffer sizes we'll use **/
+
+#define READ_BUFFER_SIZE 4096
+#define BASE_PRINTER_BUFFER_SIZE 2048
+
+PPRINTPROCESSORDATA
+ValidateHandle(
+ HANDLE hPrintProcessor
+);
+
+/**
+ Debugging stuff.
+**/
+
+#define DBG_NONE 0x00000000
+#define DBG_INFO 0x00000001
+#define DBG_WARNING 0x00000002
+#define DBG_ERROR 0x00000004
+#define DBG_TRACE 0x00000008
+
+#if DBG
+
+/* Quick fix:
+ *
+ * Ensure DbgPrint and DbgBreakPoint are prototyped,
+ * so that we're not screwed by STDCALL.
+ * This should be replaced by OutputDebugString
+ */
+ULONG
+DbgPrint(
+ PCH Format,
+ ...
+ );
+
+VOID
+DbgBreakPoint(
+ VOID
+ );
+
+
+#define GLOBAL_DEBUG_FLAGS Debug
+
+extern DWORD GLOBAL_DEBUG_FLAGS;
+
+/* These flags are not used as arguments to the DBGMSG macro.
+ * You have to set the high word of the global variable to cause it to break.
+ * It is ignored if used with DBGMSG.
+ * (Here mainly for explanatory purposes.)
+ */
+#define DBG_BREAK_ON_WARNING ( DBG_WARNING << 16 )
+#define DBG_BREAK_ON_ERROR ( DBG_ERROR << 16 )
+
+/* Double braces are needed for this one, e.g.:
+ *
+ * DBGMSG( DBG_ERROR, ( "Error code %d", Error ) );
+ *
+ * This is because we can't use variable parameter lists in macros.
+ * The statement gets pre-processed to a semi-colon in non-debug mode.
+ *
+ * Set the global variable GLOBAL_DEBUG_FLAGS via the debugger.
+ * Setting the flag in the low word causes that level to be printed;
+ * setting the high word causes a break into the debugger.
+ * E.g. setting it to 0x00040006 will print out all warning and error
+ * messages, and break on errors.
+ */
+#define DBGMSG( Level, MsgAndArgs ) \
+{ \
+ if( ( Level & 0xFFFF ) & GLOBAL_DEBUG_FLAGS ) \
+ DbgPrint MsgAndArgs; \
+ if( ( Level << 16 ) & GLOBAL_DEBUG_FLAGS ) \
+ DbgBreakPoint(); \
+}
+
+#else
+#define DBGMSG
+#endif
+
diff --git a/private/net/svcdlls/fpnw/inc/nwstruct.h b/private/net/svcdlls/fpnw/inc/nwstruct.h
new file mode 100644
index 000000000..4c0b0c0e3
--- /dev/null
+++ b/private/net/svcdlls/fpnw/inc/nwstruct.h
@@ -0,0 +1,183 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ nwstruct.h
+
+Abstract:
+
+ Contains data structures used by NCP Server APIs.
+
+Author:
+
+ Yi-Hsin Sung (yihsins) 11-Sept-1993
+
+Revision History:
+
+--*/
+
+#ifndef _NWSTRUCT_H_
+#define _NWSTRUCT_H_
+
+#include <fpnwapi.h>
+
+//
+// Volume types : disk or printer
+//
+
+#define NWVOL_TYPE_DISKTREE FPNWVOL_TYPE_DISKTREE
+#define NWVOL_TYPE_CDROM FPNWVOL_TYPE_CDROM
+#define NWVOL_TYPE_REMOVABLE FPNWVOL_TYPE_REMOVABLE
+
+#define NWVOL_MAX_USES_UNLIMITED ((ULONG)-1)
+
+//
+// Permissions flags returned in structure FPNWFILEINFO
+//
+
+#define NWFILE_PERM_NONE FPNWFILE_PERM_NONE
+#define NWFILE_PERM_READ FPNWFILE_PERM_READ
+#define NWFILE_PERM_WRITE FPNWFILE_PERM_WRITE
+#define NWFILE_PERM_CREATE FPNWFILE_PERM_CREATE
+#define NWFILE_PERM_EXEC FPNWFILE_PERM_EXEC
+#define NWFILE_PERM_DELETE FPNWFILE_PERM_DELETE
+#define NWFILE_PERM_ATRIB FPNWFILE_PERM_ATRIB
+#define NWFILE_PERM_PERM FPNWFILE_PERM_PERM
+
+#define NWSERVERADDR FPNWSERVERADDR
+
+typedef FPNWSERVERINFO NWSERVERINFO, *PNWSERVERINFO;
+typedef FPNWVOLUMEINFO NWVOLUMEINFO, *PNWVOLUMEINFO;
+typedef FPNWVOLUMEINFO_2 NWVOLUMEINFO_2, *PNWVOLUMEINFO_2;
+typedef FPNWCONNECTIONINFO NWCONNECTIONINFO, *PNWCONNECTIONINFO;
+typedef FPNWVOLUMECONNINFO NWVOLUMECONNINFO, *PNWVOLUMECONNINFO;
+typedef FPNWFILEINFO NWFILEINFO, *PNWFILEINFO;
+
+//
+// Below are the APIs available to manipulate FPNW servers, volumes, etc.
+//
+
+DWORD
+NwApiBufferFree(
+ IN LPVOID pBuffer
+);
+
+DWORD
+NwServerGetInfo(
+ IN LPWSTR pServerName OPTIONAL,
+ IN DWORD dwLevel,
+ OUT PNWSERVERINFO *ppServerInfo
+);
+
+DWORD
+NwServerSetInfo(
+ IN LPWSTR pServerName OPTIONAL,
+ IN DWORD dwLevel,
+ IN PNWSERVERINFO pServerInfo
+);
+
+DWORD
+NwVolumeAdd(
+ IN LPWSTR pServerName OPTIONAL,
+ IN DWORD dwLevel,
+ IN PNWVOLUMEINFO pVolumeInfo
+);
+
+DWORD
+NwVolumeDel(
+ IN LPWSTR pServerName OPTIONAL,
+ IN LPWSTR pVolumeName
+);
+
+DWORD
+NwVolumeEnum(
+ IN LPWSTR pServerName OPTIONAL,
+ IN DWORD dwLevel,
+ OUT PNWVOLUMEINFO *ppVolumeInfo,
+ OUT PDWORD pEntriesRead,
+ IN OUT PDWORD resumeHandle OPTIONAL
+);
+
+DWORD
+NwVolumeGetInfo(
+ IN LPWSTR pServerName OPTIONAL,
+ IN LPWSTR pVolumeName,
+ IN DWORD dwLevel,
+ OUT PNWVOLUMEINFO *ppVolumeInfo
+);
+
+DWORD
+NwVolumeSetInfo(
+ IN LPWSTR pServerName OPTIONAL,
+ IN LPWSTR pVolumeName,
+ IN DWORD dwLevel,
+ IN PNWVOLUMEINFO pVolumeInfo
+);
+
+DWORD
+NwConnectionEnum(
+ IN LPWSTR pServerName OPTIONAL,
+ IN DWORD dwLevel,
+ OUT PNWCONNECTIONINFO *ppConnectionInfo,
+ OUT PDWORD pEntriesRead,
+ IN OUT PDWORD resumeHandle OPTIONAL
+);
+
+DWORD NwConnectionDel(
+ IN LPWSTR pServerName OPTIONAL,
+ IN DWORD dwConnectionId
+);
+
+DWORD
+NwVolumeConnEnum(
+ IN LPWSTR pServerName OPTIONAL,
+ IN DWORD dwLevel,
+ IN LPWSTR pVolumeName,
+ IN DWORD dwConnectionId,
+ OUT PNWVOLUMECONNINFO *ppVolumeConnInfo,
+ OUT PDWORD pEntriesRead,
+ IN OUT PDWORD resumeHandle OPTIONAL
+);
+
+DWORD
+NwFileEnum(
+ IN LPWSTR pServerName OPTIONAL,
+ IN DWORD dwLevel,
+ IN LPWSTR pPathName OPTIONAL,
+ OUT PNWFILEINFO *ppFileInfo,
+ OUT PDWORD pEntriesRead,
+ IN OUT PDWORD resumeHandle OPTIONAL
+);
+
+DWORD
+NwFileClose(
+ IN LPWSTR pServerName OPTIONAL,
+ IN DWORD nFileId
+);
+
+DWORD NwMessageBufferSend(
+ IN LPWSTR pServerName OPTIONAL,
+ IN DWORD dwConnectionId,
+ IN DWORD fConsoleBroadcast,
+ IN LPBYTE pbBuffer,
+ IN DWORD cbBuffer
+);
+
+DWORD NwSetDefaultQueue(
+ IN LPWSTR pServerName OPTIONAL,
+ IN LPWSTR pQueueName
+);
+
+DWORD NwAddPServer(
+ IN LPWSTR pServerName OPTIONAL,
+ IN LPWSTR pPServerName
+);
+
+DWORD NwRemovePServer(
+ IN LPWSTR pServerName OPTIONAL,
+ IN LPWSTR pPServerName
+);
+
+#endif
diff --git a/private/net/svcdlls/fpnw/inc/nwsutil.h b/private/net/svcdlls/fpnw/inc/nwsutil.h
new file mode 100644
index 000000000..be78f60b9
--- /dev/null
+++ b/private/net/svcdlls/fpnw/inc/nwsutil.h
@@ -0,0 +1,64 @@
+/*++
+
+Copyright (c) 1993-1995, Microsoft Corp. All rights reserved.
+
+Module Name:
+
+ nwsutil.h
+
+Abstract:
+
+ This is the public include file for some of the functions used by
+ User Manager and Server Manager.
+
+Author:
+
+ Congpa You 02-Dec-1993 Created.
+
+Revision History:
+
+--*/
+
+#ifndef _NWSUTIL_H_
+#define _NWSUTIL_H_
+
+#include <crypt.h>
+#include <fpnwname.h>
+
+
+/** Function Prototypes **/
+
+NTSTATUS GetNcpSecretKey( CHAR *pchNWSecretKey );
+
+NTSTATUS
+GetRemoteNcpSecretKey (
+ PUNICODE_STRING SystemName,
+ CHAR *pchNWSecretKey
+ );
+
+BOOL IsNetWareInstalled( VOID );
+
+ULONG
+MapRidToObjectId(
+ DWORD dwRid,
+ LPWSTR pszUserName,
+ BOOL fNTAS,
+ BOOL fBuiltin
+ );
+
+ULONG
+SwapObjectId(
+ ULONG ulObjectId
+ ) ;
+
+NTSTATUS InstallNetWare( LPWSTR lpNcpSecretKey );
+
+VOID
+Shuffle(
+ UCHAR *achObjectId,
+ UCHAR *szUpperPassword,
+ int iPasswordLen,
+ UCHAR *achOutputBuffer
+ );
+
+#endif // _NWSUTIL_H_
diff --git a/private/net/svcdlls/fpnw/inc/srvnames.h b/private/net/svcdlls/fpnw/inc/srvnames.h
new file mode 100644
index 000000000..031c5517c
--- /dev/null
+++ b/private/net/svcdlls/fpnw/inc/srvnames.h
@@ -0,0 +1,21 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ srvnames.h
+
+Abstract:
+
+ Private header file which defines the Server Service names.
+
+Author:
+
+ Dan Lafferty (danl) 07-Jan-1993
+
+Revision History:
+
+--*/
+
+#define SERVER_INTERFACE_NAME TEXT("FPNW")
diff --git a/private/net/svcdlls/fpnw/inc/usrprop.h b/private/net/svcdlls/fpnw/inc/usrprop.h
new file mode 100644
index 000000000..58e4220f1
--- /dev/null
+++ b/private/net/svcdlls/fpnw/inc/usrprop.h
@@ -0,0 +1,70 @@
+/*++
+
+Copyright (c) 1993-1995, Microsoft Corp. All rights reserved.
+
+Module Name:
+
+ usrprop.h
+
+Abstract:
+
+ This is the public include file for some of the functions used by
+ User Manager and Server Manager.
+
+Author:
+
+ Congpa You 02-Dec-1993 Created.
+
+Revision History:
+
+--*/
+
+#ifndef _USRPROP_H_
+#define _USRPROP_H_
+
+#include <fpnwcomm.h>
+
+
+//Encryption function
+NTSTATUS ReturnNetwareForm (const char * pszSecretValue,
+ DWORD dwUserId,
+ const WCHAR * pchNWPassword,
+ UCHAR * pchEncryptedNWPassword);
+
+NTSTATUS
+SetUserProperty (
+ IN LPWSTR UserParms,
+ IN LPWSTR Property,
+ IN UNICODE_STRING PropertyValue,
+ IN WCHAR PropertyFlag,
+ OUT LPWSTR * pNewUserParms, // memory has to be freed afer use.
+ OUT BOOL * Update
+ );
+
+NTSTATUS
+SetUserPropertyWithLength (
+ IN PUNICODE_STRING UserParms,
+ IN LPWSTR Property,
+ IN UNICODE_STRING PropertyValue,
+ IN WCHAR PropertyFlag,
+ OUT LPWSTR * pNewUserParms, // memory has to be freed afer use.
+ OUT BOOL * Update
+ );
+
+NTSTATUS
+QueryUserProperty (
+ IN LPWSTR UserParms,
+ IN LPWSTR Property,
+ OUT PWCHAR PropertyFlag,
+ OUT PUNICODE_STRING PropertyValue
+ );
+
+NTSTATUS
+QueryUserPropertyWithLength (
+ IN PUNICODE_STRING UserParms,
+ IN LPWSTR Property,
+ OUT PWCHAR PropertyFlag,
+ OUT PUNICODE_STRING PropertyValue
+ );
+
+#endif // _USRPROP_H_
diff --git a/private/net/svcdlls/fpnw/makefil0 b/private/net/svcdlls/fpnw/makefil0
new file mode 100644
index 000000000..b83d813b6
--- /dev/null
+++ b/private/net/svcdlls/fpnw/makefil0
@@ -0,0 +1,72 @@
+#
+# This is the MIDL compile phase of the build process.
+#
+
+# The following is where you put the name of your .idl file without
+# the .idl extension:
+
+IDL_NAME = ncpsvc
+IMPORT = imports
+
+#
+#
+
+!INCLUDE $(NTMAKEENV)\makefile.plt
+
+UNICODE=1
+
+SDKINC = \nt\public\sdk\inc
+SDKCRTINC = \nt\public\sdk\inc\crt
+PRIVINC = ..\..\..\inc
+NWINC = .\inc
+
+INCS = -I$(SDKINC) -I$(SDKCRTINC) -I$(PRIVINC) -I$(NWINC) -I.\inc
+
+
+CLIENT_TARGETS = client\$(IDL_NAME)_c.c \
+ inc\$(IDL_NAME).h
+
+SERVER_TARGETS = server\$(IDL_NAME)_s.c
+
+
+EXTRN_DEPENDS = $(SDKINC)\winbase.h \
+ $(SDKINC)\windef.h \
+ inc\imports.h \
+ $(NWINC)\nwstruct.h \
+ $(IDL_NAME).acf
+
+# CPP = -cpp_cmd "$(MIDL_CPP)" -cpp_opt "-nologo -E $(MIDL_FLAGS) $(INCS) $(C_DEFINES) $(NET_C_DEFINES)"
+
+CPP = -cpp_cmd "$(MIDL_CPP)" $(MIDL_FLAGS) $(C_DEFINES) $(NET_C_DEFINES)
+
+#
+# Define Products and Dependencies
+#
+
+all: $(CLIENT_TARGETS) $(SERVER_TARGETS) $(EXTRN_DEPENDS)
+!IF "$(BUILDMSG)" != ""
+ @ech ; $(BUILDMSG) ;
+!ENDIF
+
+clean: delete_source all
+
+delete_source:
+ erase $(CLIENT_TARGETS) $(SERVER_TARGETS)
+
+
+
+#
+# MIDL COMPILE
+#
+
+
+$(CLIENT_TARGETS) : $(IDL_NAME).idl $(EXTRN_DEPENDS)
+ midl -Oi -server none -oldnames -error allocation -error ref -ms_ext -c_ext $(CPP) $(IDL_NAME).idl $(INCS)
+ IF EXIST $(IDL_NAME)_c.c copy $(IDL_NAME)_c.c .\client & del $(IDL_NAME)_c.c
+ IF EXIST $(IDL_NAME).h copy $(IDL_NAME).h .\inc & del $(IDL_NAME).h
+
+$(SERVER_TARGETS) : $(IDL_NAME).idl $(EXTRN_DEPENDS)
+ midl -client none -oldnames -error stub_data -error allocation -error ref -ms_ext -c_ext $(CPP) $(IDL_NAME).idl $(INCS)
+ IF EXIST $(IDL_NAME)_s.c copy $(IDL_NAME)_s.c .\server & del $(IDL_NAME)_s.c
+ IF EXIST $(IDL_NAME).h del $(IDL_NAME).h
+
diff --git a/private/net/svcdlls/fpnw/ncpsvc.acf b/private/net/svcdlls/fpnw/ncpsvc.acf
new file mode 100644
index 000000000..4d232a6c3
--- /dev/null
+++ b/private/net/svcdlls/fpnw/ncpsvc.acf
@@ -0,0 +1,20 @@
+[ implicit_handle( handle_t ncpsvc_handle )]
+
+interface ncpsvc
+
+{
+typedef [allocate(all_nodes)] PNWSERVERINFO;
+typedef [allocate(all_nodes)] PNWVOLUMEINFO;
+typedef [allocate(all_nodes)] PNWCONNECTIONINFO;
+typedef [allocate(all_nodes)] PNWVOLUMECONNINFO;
+typedef [allocate(all_nodes)] PNWFILEINFO;
+typedef [allocate(all_nodes)] PFPNWSERVERINFO;
+typedef [allocate(all_nodes)] PFPNWVOLUMEINFO;
+typedef [allocate(all_nodes)] PFPNWCONNECTIONINFO;
+typedef [allocate(all_nodes)] PFPNWVOLUMECONNINFO;
+typedef [allocate(all_nodes)] PFPNWFILEINFO;
+typedef [allocate(all_nodes)] PFPNWVOLUMEINFO_2;
+typedef [allocate(all_nodes)] PFPNWVOLUMEINFO_2_I;
+typedef [allocate(all_nodes)] LPVOLUME_INFO;
+}
+ \ No newline at end of file
diff --git a/private/net/svcdlls/fpnw/ncpsvc.idl b/private/net/svcdlls/fpnw/ncpsvc.idl
new file mode 100644
index 000000000..bce792969
--- /dev/null
+++ b/private/net/svcdlls/fpnw/ncpsvc.idl
@@ -0,0 +1,242 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ ncpsvc.idl
+
+Abstract:
+
+ This is the IDL file that describes the RPC interface for the
+ NCP server internal APIs.
+
+Author:
+
+ Yi-Hsin Sung 11-Sept-1993
+
+Environment:
+
+ User Mode -Win32
+
+--*/
+
+//
+// Interface Attributes
+//
+
+[
+ uuid(E67AB081-9844-3521-9D32-834F038001C1),
+ version(1.0),
+#ifdef __midl
+ ms_union,
+#endif // __midl
+ pointer_default(unique)
+]
+
+//
+// Interface Keyword
+//
+
+interface ncpsvc
+
+//
+// Interface Body
+//
+
+{
+
+import "imports.idl";
+
+//
+// Handle type
+//
+
+typedef [handle] wchar_t * NCPSVC_HANDLE;
+
+//
+// Data type
+//
+typedef struct _FPNWVOLUMEINFO_CONTAINER {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] PFPNWVOLUMEINFO Buffer;
+} FPNWVOLUMEINFO_CONTAINER, *PFPNWVOLUMEINFO_CONTAINER;
+typedef struct _NWVOLUMEINFO_CONTAINER {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] PNWVOLUMEINFO Buffer;
+} NWVOLUMEINFO_CONTAINER, *PNWVOLUMEINFO_CONTAINER;
+
+typedef struct _FPNWCONNECTIONINFO_CONTAINER {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] PFPNWCONNECTIONINFO Buffer;
+} FPNWCONNECTIONINFO_CONTAINER, *PFPNWCONNECTIONINFO_CONTAINER;
+typedef struct _NWCONNECTIONINFO_CONTAINER {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] PNWCONNECTIONINFO Buffer;
+} NWCONNECTIONINFO_CONTAINER, *PNWCONNECTIONINFO_CONTAINER;
+
+typedef struct _FPNWVOLUMECONNINFO_CONTAINER {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] PFPNWVOLUMECONNINFO Buffer;
+} FPNWVOLUMECONNINFO_CONTAINER, *PFPNWVOLUMECONNINFO_CONTAINER;
+typedef struct _NWVOLUMECONNINFO_CONTAINER {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] PNWVOLUMECONNINFO Buffer;
+} NWVOLUMECONNINFO_CONTAINER, *PNWVOLUMECONNINFO_CONTAINER;
+
+typedef struct _FPNWFILEINFO_CONTAINER {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] PFPNWFILEINFO Buffer;
+} FPNWFILEINFO_CONTAINER, *PFPNWFILEINFO_CONTAINER;
+typedef struct _NWFILEINFO_CONTAINER {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] PNWFILEINFO Buffer;
+} NWFILEINFO_CONTAINER, *PNWFILEINFO_CONTAINER;
+
+//
+// Internal 2 structure used for passing and/or returning
+// self-relative security descriptors.
+//
+
+typedef struct _FPNWVolumeInfo_2_I
+{
+ [string] LPWSTR lpVolumeName;
+ DWORD dwType;
+ DWORD dwMaxUses;
+ DWORD dwCurrentUses;
+ [string] LPWSTR lpPath;
+ DWORD dwFileSecurityDescriptorLength;
+ [size_is(dwFileSecurityDescriptorLength)] PUCHAR FileSecurityDescriptor;
+} FPNWVOLUMEINFO_2_I, *PFPNWVOLUMEINFO_2_I;
+
+typedef [switch_type(unsigned long)] union _VOLUME_INFO { // for Get & Set Info
+ [case(1)]
+ FPNWVOLUMEINFO VolumeInfo1;
+ [case(2)]
+ FPNWVOLUMEINFO_2_I VolumeInfo2;
+ [default]
+ ;
+} VOLUME_INFO, *PVOLUME_INFO, *LPVOLUME_INFO;
+
+//
+// Function prototype
+//
+
+DWORD
+NwrServerGetInfo(
+ [in, string, unique] NCPSVC_HANDLE pServerName,
+ [in] DWORD dwLevel,
+ [out] PFPNWSERVERINFO *ppServerInfo
+);
+
+DWORD
+NwrServerSetInfo(
+ [in, string, unique] NCPSVC_HANDLE pServerName,
+ [in] DWORD dwLevel,
+ [in] PFPNWSERVERINFO pServerInfo
+);
+
+DWORD
+NwrVolumeAdd(
+ [in, string, unique] NCPSVC_HANDLE pServerName,
+ [in] DWORD dwLevel,
+ [in, switch_is(dwLevel)] LPVOLUME_INFO pVolumeInfo
+);
+
+DWORD
+NwrVolumeDel(
+ [in, string, unique] NCPSVC_HANDLE pServerName,
+ [in, string] LPWSTR pVolumeName
+);
+
+DWORD
+NwrVolumeEnum(
+ [in, string, unique] NCPSVC_HANDLE pServerName,
+ [in] DWORD dwLevel,
+ [out] PFPNWVOLUMEINFO_CONTAINER pVolumeInfoContainer,
+ [in, out, unique] PDWORD resumeHandle
+);
+
+DWORD
+NwrVolumeGetInfo(
+ [in, string, unique] NCPSVC_HANDLE pServerName,
+ [in, string] LPWSTR pVolumeName,
+ [in] DWORD dwLevel,
+ [out, switch_is(dwLevel)] LPVOLUME_INFO *ppVolumeInfo
+);
+
+DWORD
+NwrVolumeSetInfo(
+ [in, string, unique] NCPSVC_HANDLE pServerName,
+ [in, string] LPWSTR pVolumeName,
+ [in] DWORD dwLevel,
+ [in, switch_is(dwLevel)] LPVOLUME_INFO pVolumeInfo
+);
+
+DWORD
+NwrConnectionEnum(
+ [in, string, unique] NCPSVC_HANDLE pServerName,
+ [in] DWORD dwLevel,
+ [out] PFPNWCONNECTIONINFO_CONTAINER pConnectionInfoContainer,
+ [in, out, unique] PDWORD resumeHandle
+);
+
+DWORD
+NwrConnectionDel(
+ [in, string, unique] NCPSVC_HANDLE pServerName,
+ [in] DWORD dwConnectionId
+);
+
+DWORD
+NwrVolumeConnEnum(
+ [in, string, unique] NCPSVC_HANDLE pServerName,
+ [in] DWORD dwLevel,
+ [in, string, unique] LPWSTR pVolumeName,
+ [in] DWORD dwConnectionId,
+ [out] PFPNWVOLUMECONNINFO_CONTAINER pVolumeConnInfoContainer,
+ [in, out, unique] PDWORD resumeHandle
+);
+
+DWORD
+NwrFileEnum(
+ [in, string, unique] NCPSVC_HANDLE pServerName,
+ [in] DWORD dwLevel,
+ [in, string, unique] LPWSTR pPathName,
+ [out] PFPNWFILEINFO_CONTAINER pFileInfoContainer,
+ [in, out, unique] PDWORD resumeHandle
+);
+
+DWORD
+NwrFileClose(
+ [in, string, unique] NCPSVC_HANDLE pServerName,
+ [in] DWORD dwFileId
+);
+
+DWORD
+NwrMessageBufferSend(
+ [in, string, unique] NCPSVC_HANDLE pServerName,
+ [in] DWORD dwConnectionId,
+ [in] DWORD fConsoleBroadcast,
+ [in, size_is(cbBuffer)] LPBYTE pbBuffer,
+ [in] DWORD cbBuffer
+);
+
+DWORD
+NwrSetDefaultQueue(
+ [in, string, unique] NCPSVC_HANDLE pServerName,
+ [in, string] LPWSTR pQueueName
+);
+
+DWORD
+NwrAddPServer(
+ [in, string, unique] NCPSVC_HANDLE pServerName,
+ [in, string] LPWSTR pPServerName
+);
+
+DWORD
+NwrRemovePServer(
+ [in, string, unique] NCPSVC_HANDLE pServerName,
+ [in, string] LPWSTR pPServerName
+);
+
+}
diff --git a/private/net/svcdlls/fpnw/nwsplace.txt b/private/net/svcdlls/fpnw/nwsplace.txt
new file mode 100644
index 000000000..dcc80232f
--- /dev/null
+++ b/private/net/svcdlls/fpnw/nwsplace.txt
@@ -0,0 +1 @@
+fpnwclnt.dll windows
diff --git a/private/net/svcdlls/lls/ccfapi32/ccfapi.cpp b/private/net/svcdlls/lls/ccfapi32/ccfapi.cpp
new file mode 100644
index 000000000..c0104f534
--- /dev/null
+++ b/private/net/svcdlls/lls/ccfapi32/ccfapi.cpp
@@ -0,0 +1,354 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ ccfapi.cpp
+
+Abstract:
+
+ Implementation of CCcfApiApp, the MFC application object for CCFAPI32.DLL.
+
+Author:
+
+ Jeff Parham (jeffparh) 13-Dec-1995
+
+Revision History:
+
+--*/
+
+#include "stdafx.h"
+#include <lmerr.h>
+
+#include "ccfapi.h"
+#include "source.h"
+#include "imagelst.h"
+#include "remdlg.h"
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+
+CCcfApiApp theApp; // The one and only CCcfApiApp object
+
+
+BEGIN_MESSAGE_MAP(CCcfApiApp, CWinApp)
+ //{{AFX_MSG_MAP(CCcfApiApp)
+ // NOTE - the ClassWizard will add and remove mapping macros here.
+ // DO NOT EDIT what you see in these blocks of generated code!
+ //}}AFX_MSG_MAP
+END_MESSAGE_MAP()
+
+
+CCcfApiApp::CCcfApiApp()
+
+/*++
+
+Routine Description:
+
+ Constructor for CCF API application.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ AFX_MANAGE_STATE( AfxGetStaticModuleState() );
+
+ m_LastError = 0;
+ m_LastLlsError = 0;
+
+ LPTSTR pszHelpFileName = m_strHelpFileName.GetBuffer( MAX_PATH );
+
+ if ( NULL != pszHelpFileName )
+ {
+ BOOL ok = GetSystemDirectory( pszHelpFileName, MAX_PATH );
+ m_strHelpFileName.ReleaseBuffer();
+
+ if ( ok )
+ {
+ m_strHelpFileName += TEXT( "\\" );
+ }
+
+ m_strHelpFileName += TEXT( "ccfapi.hlp" );
+ }
+}
+
+
+void CCcfApiApp::DisplayLastError()
+
+/*++
+
+Routine Description:
+
+ Displays a message corresponding to the last error encountered.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ CString strLastError;
+ CString strErrorCaption;
+
+ strLastError = GetLastErrorString();
+
+ AfxMessageBox( strLastError, MB_ICONSTOP | MB_OK );
+}
+
+
+CString CCcfApiApp::GetLastErrorString()
+
+/*++
+
+Routine Description:
+
+ Retrieves string for last error.
+
+ (Routine stolen from winsadmn...).
+
+ (And that routine stolen from LlsMgr...).
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ CString.
+
+--*/
+
+{
+ CString strLastError;
+ DWORD nId = m_LastError;
+ const int cchLastErrorSize = 512;
+ LPTSTR pszLastError;
+ DWORD cchLastError;
+
+ if (((long)nId == RPC_S_CALL_FAILED) ||
+ ((long)nId == RPC_NT_SS_CONTEXT_MISMATCH))
+ {
+ strLastError.LoadString(IDS_ERROR_DROPPED_LINK);
+ }
+ else if (((long)nId == RPC_S_SERVER_UNAVAILABLE) ||
+ ((long)nId == RPC_NT_SERVER_UNAVAILABLE))
+ {
+ strLastError.LoadString(IDS_ERROR_NO_RPC_SERVER);
+ }
+ else if ((long)nId == STATUS_INVALID_LEVEL)
+ {
+ strLastError.LoadString(IDS_ERROR_DOWNLEVEL_SERVER);
+ }
+ else if (((long)nId == ERROR_ACCESS_DENIED) ||
+ ((long)nId == STATUS_ACCESS_DENIED))
+ {
+ strLastError.LoadString(IDS_ERROR_ACCESS_DENIED);
+ }
+ else if ((long)nId == STATUS_ACCOUNT_EXPIRED)
+ {
+ strLastError.LoadString(IDS_ERROR_CERTIFICATE_EXPIRED);
+ }
+ else
+ {
+ HINSTANCE hinstDll = NULL;
+
+ if ((nId >= NERR_BASE) && (nId <= MAX_NERR))
+ {
+ hinstDll = ::LoadLibrary( _T( "netmsg.dll" ) );
+ }
+ else if (nId >= 0x4000000)
+ {
+ hinstDll = ::LoadLibrary( _T( "ntdll.dll" ) );
+ }
+
+ cchLastError = 0;
+ pszLastError = strLastError.GetBuffer( cchLastErrorSize );
+
+ if ( NULL != pszLastError )
+ {
+ DWORD dwFlags = FORMAT_MESSAGE_IGNORE_INSERTS
+ | FORMAT_MESSAGE_MAX_WIDTH_MASK
+ | ( hinstDll ? FORMAT_MESSAGE_FROM_HMODULE
+ : FORMAT_MESSAGE_FROM_SYSTEM );
+
+ cchLastError = ::FormatMessage( dwFlags,
+ hinstDll,
+ nId,
+ 0,
+ pszLastError,
+ cchLastErrorSize,
+ NULL );
+
+ strLastError.ReleaseBuffer();
+ }
+
+ if ( hinstDll )
+ {
+ ::FreeLibrary( hinstDll );
+ }
+
+ if ( 0 == cchLastError )
+ {
+ strLastError.LoadString( IDS_ERROR_UNSUCCESSFUL );
+ }
+ }
+
+ return strLastError;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// CCF API //
+///////////////
+
+DWORD CCcfApiApp::CertificateEnter( HWND hWndParent, LPCSTR pszServerName, LPCSTR pszProductName, LPCSTR pszVendor, DWORD dwFlags, LPCSTR pszSourceToUse )
+
+/*++
+
+Routine Description:
+
+ Display a dialog allowing the user to enter a license certificate
+ into the system.
+
+Arguments:
+
+ hWndParent (HWND)
+ HWND to the client's main window, for use as the parent window to any
+ opened dialogs. May be NULL.
+ pszServerName (LPCSTR)
+ Name of the server for which licenses are to be installed. Note that
+ this may not be the same as the server on which licenses are actually
+ installed, as, for example, per seat licenses are always installed on
+ the enterprise server. A NULL value indicates the local server.
+ pszProductName (LPCSTR)
+ Product for which licenses are to be installed. A NULL value indicates
+ that the user should be allowed to choose.
+ pszVendor (LPCSTR)
+ Name of the vendor of the product. This value should be NULL if
+ pszProductName is NULL, and should be non-NULL if pszProductName is
+ non-NULL.
+ dwFlags (DWORD)
+ A bitfield containing one or more of the following:
+ CCF_ENTER_FLAG_PER_SEAT_ONLY
+ Allow the user to enter only per seat licenses. Not valid in
+ combination with CCF_ENTER_FLAG_PER_SERVER_ONLY.
+ CCF_ENTER_FLAG_PER_SERVER_ONLY
+ Allow the user to enter only per server licenses. Not valid in
+ combination with CCF_ENTER_FLAG_PER_SEAT_ONLY.
+ pszSourceToUse (LPCSTR)
+ Name of the secure certificate source to use to install the certificate,
+ e.g., "Paper". A NULL value indicates that the user should be allowed
+ to choose.
+
+Return Value:
+
+ ERROR_SUCCESS (A certificate was successfully entered into the system.)
+ ERROR_CANCELLED (The user cancelled without installing a certificate.)
+ other Win error
+
+--*/
+
+{
+ CCertSourceSelectDlg srcDlg( CWnd::FromHandle( hWndParent ) );
+ LPCSTR pszNetServerName = NULL;
+ CHAR szNetServerName[ 3 + MAX_COMPUTERNAME_LENGTH ] = "\\\\";
+
+ // make sure server name, if specified, is in the form \\server
+ if ( NULL != pszServerName )
+ {
+ if ( ( pszServerName[0] != '\\' ) || ( pszServerName[1] != '\\' ) )
+ {
+ // is not prefixed with backslashes
+ lstrcpynA( szNetServerName + 2, pszServerName, sizeof( szNetServerName ) - 3 );
+ pszNetServerName = szNetServerName;
+ }
+ else
+ {
+ // is prefixed with backslashes
+ pszNetServerName = pszServerName;
+ }
+ }
+
+ return srcDlg.CertificateEnter( hWndParent, pszNetServerName, pszProductName, pszVendor, dwFlags, pszSourceToUse );
+}
+
+
+DWORD CCcfApiApp::CertificateRemove( HWND hWndParent, LPCSTR pszServerName, LPCSTR pszProductName, LPCSTR pszVendor, DWORD dwFlags, LPCSTR pszSourceToUse )
+
+/*++
+
+Routine Description:
+
+ Display a dialog allowing the user to remove one or more license
+ certificates from the system.
+
+Arguments:
+
+ hWndParent (HWND)
+ HWND to the client's main window, for use as the parent window to any
+ opened dialogs. May be NULL.
+ pszServerName (LPCSTR)
+ Name of the server on which licenses are to be removed. A NULL value
+ indicates the local server.
+ pszProductName (LPCSTR)
+ Product for which licenses are to be removed. A NULL value indicates
+ that the user should be allowed to remove licenses from any product.
+ pszVendor (LPCSTR)
+ Name of the vendor of the product. This value should be NULL if
+ pszProductName is NULL, and should be non-NULL if pszProductName is
+ non-NULL.
+ dwFlags (DWORD)
+ Certificate removal options. As of this writing, no flags are
+ supported.
+ pszSourceToUse (LPCSTR)
+ Name of the secure certificate source by which licenses are to be
+ removed, e.g., "Paper". A NULL value indicates that the user should
+ be allowed to remove licenses that were installed with any source.
+
+Return Value:
+
+ ERROR_SUCCESS
+ Win error
+
+--*/
+
+{
+ CCertRemoveSelectDlg remDlg( CWnd::FromHandle( hWndParent ) );
+ LPCSTR pszNetServerName = NULL;
+ CHAR szNetServerName[ 3 + MAX_COMPUTERNAME_LENGTH ] = "\\\\";
+
+ // make sure server name, if specified, is in the form \\server
+ if ( NULL != pszServerName )
+ {
+ if ( ( pszServerName[0] != '\\' ) || ( pszServerName[1] != '\\' ) )
+ {
+ // is not prefixed with backslashes
+ lstrcpynA( szNetServerName + 2, pszServerName, sizeof( szNetServerName ) - 3 );
+ pszNetServerName = szNetServerName;
+ }
+ else
+ {
+ // is prefixed with backslashes
+ pszNetServerName = pszServerName;
+ }
+ }
+
+ return remDlg.CertificateRemove( pszNetServerName, pszProductName, pszVendor, dwFlags, pszSourceToUse );
+}
+
diff --git a/private/net/svcdlls/lls/ccfapi32/ccfapi.h b/private/net/svcdlls/lls/ccfapi32/ccfapi.h
new file mode 100644
index 000000000..2bc28aa9f
--- /dev/null
+++ b/private/net/svcdlls/lls/ccfapi32/ccfapi.h
@@ -0,0 +1,110 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ ccfapi.cpp
+
+Abstract:
+
+ Prototype of CCcfApiApp, the MFC application object for CCFAPI32.DLL.
+
+Author:
+
+ Jeff Parham (jeffparh) 13-Dec-1995
+
+Revision History:
+
+--*/
+
+#ifndef __AFXWIN_H__
+ #error include 'stdafx.h' before including this file for PCH
+#endif
+
+#include "resource.h" // main symbols
+
+class CCcfApiApp : public CWinApp
+{
+public:
+ // constructor
+ CCcfApiApp();
+
+ // error API
+ void SetLastError( DWORD dwLastError );
+ DWORD GetLastError();
+
+ void SetLastLlsError( NTSTATUS nt );
+ DWORD GetLastLlsError();
+ BOOL IsConnectionDropped();
+
+ CString GetLastErrorString();
+ void DisplayLastError();
+
+ // help API
+ LPCTSTR GetHelpFileName();
+
+ // CCF API
+ DWORD CertificateEnter( HWND hWndParent, LPCSTR pszServerName, LPCSTR pszProductName, LPCSTR pszVendor, DWORD dwFlags, LPCSTR pszSourceToUse );
+ DWORD CertificateRemove( HWND hWndParent, LPCSTR pszServerName, LPCSTR pszProductName, LPCSTR pszVendor, DWORD dwFlags, LPCSTR pszSourceToUse );
+
+// Overrides
+ // ClassWizard generated virtual function overrides
+ //{{AFX_VIRTUAL(CCcfApiApp)
+ //}}AFX_VIRTUAL
+
+ //{{AFX_MSG(CCcfApiApp)
+ // NOTE - the ClassWizard will add and remove member functions here.
+ // DO NOT EDIT what you see in these blocks of generated code !
+ //}}AFX_MSG
+ DECLARE_MESSAGE_MAP()
+
+private:
+ DWORD m_LastError;
+ NTSTATUS m_LastLlsError;
+ CString m_strHelpFileName;
+};
+
+// return the name of the CCF UI help file
+inline LPCTSTR CCcfApiApp::GetHelpFileName()
+ { return m_strHelpFileName; }
+
+// set last general error
+inline void CCcfApiApp::SetLastError( DWORD dwLastError )
+ { m_LastError = dwLastError; }
+
+// get last general error
+inline DWORD CCcfApiApp::GetLastError()
+ { return m_LastError; }
+
+// set last license server API error
+inline void CCcfApiApp::SetLastLlsError( NTSTATUS nt )
+ { m_LastLlsError = nt; m_LastError = (DWORD) nt; }
+
+// get last license server API error
+inline DWORD CCcfApiApp::GetLastLlsError()
+ { return m_LastLlsError; }
+
+// did the last license server call fail because of a lack of connectivity?
+inline BOOL CCcfApiApp::IsConnectionDropped()
+ { return ( (m_LastLlsError == STATUS_INVALID_HANDLE) ||
+ (m_LastLlsError == RPC_NT_SERVER_UNAVAILABLE) ||
+ (m_LastLlsError == RPC_NT_SS_CONTEXT_MISMATCH) ||
+ (m_LastLlsError == RPC_S_SERVER_UNAVAILABLE) ||
+ (m_LastLlsError == RPC_S_CALL_FAILED) ); }
+
+/////////////////////////////////////////////////////////////////////////////
+
+// maximum amount of data to request at a time from license server
+#define LLS_PREFERRED_LENGTH ((DWORD)-1L)
+
+extern CCcfApiApp theApp;
+
+extern "C"
+{
+ DWORD APIENTRY NoCertificateEnter( HWND hWnd, LPCSTR pszServerName, LPCSTR pszProductName, LPCSTR pszVendor, DWORD dwFlags );
+ DWORD APIENTRY PaperCertificateEnter( HWND hWnd, LPCSTR pszServerName, LPCSTR pszProductName, LPCSTR pszVendor, DWORD dwFlags );
+
+ DWORD APIENTRY NoCertificateRemove( HWND hWnd, LPCSTR pszServerName, DWORD dwFlags, DWORD dwLicenseLevel, LPVOID pvLicenseInfo );
+ DWORD APIENTRY PaperCertificateRemove( HWND hWnd, LPCSTR pszServerName, DWORD dwFlags, DWORD dwLicenseLevel, LPVOID pvLicenseInfo );
+}
diff --git a/private/net/svcdlls/lls/ccfapi32/ccfapi32.def b/private/net/svcdlls/lls/ccfapi32/ccfapi32.def
new file mode 100644
index 000000000..5396cf438
--- /dev/null
+++ b/private/net/svcdlls/lls/ccfapi32/ccfapi32.def
@@ -0,0 +1,13 @@
+; ccfapi32.def : Declares the module parameters for the DLL.
+
+LIBRARY "CCFAPI32"
+DESCRIPTION 'CCFAPI32 License Certificate API'
+
+EXPORTS
+ ; Explicit exports can go here
+ CCFCertificateEnterUI
+ CCFCertificateRemoveUI
+ PaperCertificateEnter
+ PaperCertificateRemove
+ NoCertificateEnter
+ NoCertificateRemove
diff --git a/private/net/svcdlls/lls/ccfapi32/ccfapi32.rc b/private/net/svcdlls/lls/ccfapi32/ccfapi32.rc
new file mode 100644
index 000000000..c9cdd44d7
--- /dev/null
+++ b/private/net/svcdlls/lls/ccfapi32/ccfapi32.rc
@@ -0,0 +1,346 @@
+//Microsoft Developer Studio generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "afxres.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.S.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+#endif //_WIN32
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "#include ""afxres.h""\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "#define _AFX_NO_SPLITTER_RESOURCES\r\n"
+ "#define _AFX_NO_OLE_RESOURCES\r\n"
+ "#define _AFX_NO_TRACKER_RESOURCES\r\n"
+ "#define _AFX_NO_PROPERTY_RESOURCES\r\n"
+ "\r\n"
+ "#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)\r\n"
+ "#ifdef _WIN32\r\n"
+ "LANGUAGE 9, 1\r\n"
+ "#pragma code_page(1252)\r\n"
+ "#endif\r\n"
+ "#include ""res\\ccfapi32.rc2"" // non-Microsoft Visual C++ edited resources\r\n"
+ "#include ""afxres.rc"" // Standard components\r\n"
+ "#endif\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+IDD_CERT_SOURCE_PAPER DIALOG DISCARDABLE 0, 0, 275, 289
+STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
+CAPTION "License Certificate Entry"
+FONT 8, "MS Shell Dlg"
+BEGIN
+ COMBOBOX IDC_PRODUCT_NAME,90,40,113,65,CBS_DROPDOWN | CBS_SORT |
+ WS_VSCROLL | WS_TABSTOP
+ EDITTEXT IDC_VENDOR,90,59,113,12,ES_AUTOHSCROLL
+ EDITTEXT IDC_SERIAL_NUMBER,90,76,113,12,ES_AUTOHSCROLL
+ EDITTEXT IDC_KEY_CODE,90,93,113,12,ES_AUTOHSCROLL
+ EDITTEXT IDC_ACTIVATION_CODE,90,111,113,12,ES_AUTOHSCROLL
+ EDITTEXT IDC_COMMENT,90,128,113,12,ES_AUTOHSCROLL
+ CONTROL "All licenses",IDC_ALL_LICENSES,"Button",
+ BS_AUTORADIOBUTTON | WS_GROUP,47,186,51,10
+ CONTROL "Only",IDC_SOME_LICENSES,"Button",BS_AUTORADIOBUTTON,47,
+ 199,30,10
+ EDITTEXT IDC_NUM_LICENSES,78,197,40,14,ES_AUTOHSCROLL | WS_GROUP
+ CONTROL "Spin1",IDC_SPIN_LICENSES,"msctls_updown32",
+ UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS |
+ UDS_NOTHOUSANDS,118,197,10,14
+ CONTROL "Per Seat",IDC_PER_SEAT,"Button",BS_AUTORADIOBUTTON |
+ WS_GROUP,47,252,43,10
+ CONTROL "Per Server",IDC_PER_SERVER,"Button",BS_AUTORADIOBUTTON,
+ 47,264,49,10
+ DEFPUSHBUTTON "OK",IDOK,217,10,50,14,WS_GROUP
+ PUSHBUTTON "Cancel",IDCANCEL,217,28,50,14
+ PUSHBUTTON "Help",IDC_MY_HELP,217,46,50,14
+ GROUPBOX "Enter the following information",IDC_STATIC,8,5,201,144
+ LTEXT "Product Name:",IDC_STATIC,16,44,52,8
+ LTEXT "Vendor:",IDC_STATIC,16,61,52,8
+ LTEXT "Serial #:",IDC_STATIC,16,78,52,8
+ LTEXT "Key Code:",IDC_STATIC,16,95,52,8
+ ICON IDI_LICENSE,IDC_STATIC,16,19,21,20
+ LTEXT "Please enter the following information exactly as it appears on your certificate:",
+ IDC_STATIC,42,17,160,18
+ LTEXT "Activation Code:",IDC_STATIC,15,113,58,8
+ LTEXT "Comment:",IDC_STATIC,15,130,58,8
+ GROUPBOX "License quantity",IDC_STATIC,8,152,201,66
+ LTEXT "license(s)",IDC_STATIC,133,200,30,8
+ ICON IDI_LICENSE,IDC_STATIC,16,167,21,20
+ LTEXT "How many licenses from this certificate would you like to install on this machine?",
+ IDC_STATIC,42,165,160,18
+ GROUPBOX "License mode",IDC_STATIC,8,221,201,60
+ ICON IDI_LICENSE,IDC_STATIC,15,234,21,20
+ LTEXT "For which license mode would you like to install these licenses?",
+ IDC_STATIC,41,232,160,18
+END
+
+IDD_CERT_SOURCE_SELECT DIALOG DISCARDABLE 0, 0, 242, 79
+STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
+CAPTION "Select Certificate Source"
+FONT 8, "MS Shell Dlg"
+BEGIN
+ DEFPUSHBUTTON "OK",IDOK,188,10,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,188,28,50,14
+ GROUPBOX "Enter the following information",IDC_STATIC,6,6,176,60
+ ICON IDI_LICENSE,IDC_STATIC,14,20,18,20
+ LTEXT "Please select a method for entering your License Certificate.",
+ IDC_STATIC,41,19,137,19
+ COMBOBOX IDC_CERT_SOURCE,41,43,109,32,CBS_DROPDOWNLIST | CBS_SORT |
+ WS_VSCROLL | WS_TABSTOP
+ PUSHBUTTON "Help",IDC_MY_HELP,188,46,50,14
+END
+
+IDD_NEW_LICENSE DIALOG DISCARDABLE 0, 0, 259, 111
+STYLE DS_MODALFRAME | DS_3DLOOK | WS_POPUP | WS_VISIBLE | WS_CAPTION |
+ WS_SYSMENU
+CAPTION "New Client Access License"
+FONT 8, "MS Shell Dlg"
+BEGIN
+ LTEXT "Choose the server product, quantity, and license mode for this license.",
+ IDC_STATIC,10,10,155,20
+ LTEXT "&Product:",IDC_STATIC,10,35,30,10
+ COMBOBOX IDC_NEW_LICENSE_PRODUCT,69,33,115,60,CBS_DROPDOWNLIST |
+ CBS_SORT | WS_VSCROLL | WS_TABSTOP
+ LTEXT "&Quantity:",IDC_STATIC,10,56,30,10
+ EDITTEXT IDC_NEW_LICENSE_QUANTITY,69,54,40,12,ES_AUTOHSCROLL
+ CONTROL "Generic1",IDC_NEW_LICENSE_SPIN,"msctls_updown32",
+ UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS,109,55,7,
+ 11
+ LTEXT "For License &Mode:",IDC_STATIC,10,70,50,16
+ CONTROL "Per Seat",IDC_PER_SEAT,"Button",BS_AUTORADIOBUTTON |
+ WS_GROUP,69,73,43,10
+ CONTROL "Per Server",IDC_PER_SERVER,"Button",BS_AUTORADIOBUTTON,
+ 121,73,49,10
+ LTEXT "&Comment:",IDC_STATIC,10,92,35,10
+ EDITTEXT IDC_NEW_LICENSE_COMMENT,69,90,115,12,ES_AUTOHSCROLL |
+ WS_GROUP
+ DEFPUSHBUTTON "OK",IDOK,201,10,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,201,30,50,14
+ PUSHBUTTON "&Help",IDC_MY_HELP,201,50,50,14
+END
+
+IDD_PER_SEAT_LICENSING DIALOG DISCARDABLE 0, 0, 261, 153
+STYLE DS_MODALFRAME | DS_3DLOOK | WS_POPUP | WS_VISIBLE | WS_CAPTION |
+ WS_SYSMENU
+CAPTION "Per Seat Licensing"
+FONT 8, "MS Shell Dlg"
+BEGIN
+ CONTROL "&I agree that:",IDC_PER_SEAT_AGREE,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,45,100,140,10
+ ICON IDI_MY_WARNING,IDC_STATIC,10,10,18,20
+ GROUPBOX "Licensing",IDC_STATIC,35,10,165,130
+ LTEXT "If you have not purchased a Client Access License for every computer that will access %1, you must purchase them prior to using %1.",
+ IDC_PER_SEAT_STATIC_CLIENTS,45,25,145,40
+ LTEXT "For complete terms and conditions governing the use of this product, see the license agreements, which can be found under Help.",
+ IDC_STATIC,45,70,145,25
+ LTEXT "I have read and agree to be bound by the license agreements for this product.",
+ IDC_STATIC,45,115,145,20
+ DEFPUSHBUTTON "OK",IDOK,205,15,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,205,35,50,14
+ PUSHBUTTON "&Help",ID_HELP,205,55,50,14
+END
+
+IDD_PER_SERVER_LICENSING DIALOG DISCARDABLE 0, 0, 266, 153
+STYLE DS_MODALFRAME | DS_3DLOOK | WS_POPUP | WS_VISIBLE | WS_CAPTION |
+ WS_SYSMENU
+CAPTION "Per Server Licensing"
+FONT 8, "MS Shell Dlg"
+BEGIN
+ CONTROL "&I agree that:",IDC_PER_SERVER_AGREE,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,45,105,55,10
+ LTEXT "I have read and agree to be bound by the license agreements for this product.",
+ IDC_STATIC,45,120,145,20
+ ICON IDI_MY_WARNING,IDC_STATIC,10,10,18,20
+ GROUPBOX "Licensing",IDC_STATIC,35,10,165,135
+ LTEXT "If you have not purchased %1 Client Access Licenses, you must purchase the required licenses prior to using %2 on this computer.",
+ IDC_PER_SERVER_STATIC_CLIENTS,45,25,145,35
+ LTEXT "For complete terms and conditions governing the use of this product, see the license agreements, which can be found under Help.",
+ IDC_STATIC,45,65,145,35
+ DEFPUSHBUTTON "OK",IDOK,205,15,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,205,35,50,14
+ PUSHBUTTON "&Help",ID_HELP,205,55,50,14
+END
+
+IDD_CERT_REMOVE_SELECT DIALOG DISCARDABLE 0, 0, 376, 215
+STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Select Certificate to Remove Licenses"
+FONT 8, "MS Shell Dlg"
+BEGIN
+ CONTROL "List1",IDC_CERTIFICATE_LIST,"SysListView32",LVS_REPORT |
+ LVS_SINGLESEL | LVS_SHAREIMAGELISTS | WS_BORDER |
+ WS_TABSTOP,7,16,362,100
+ EDITTEXT IDC_NUM_LICENSES,110,124,40,14,ES_AUTOHSCROLL
+ CONTROL "Spin1",IDC_SPIN_LICENSES,"msctls_updown32",
+ UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS,150,124,
+ 10,14
+ DEFPUSHBUTTON "Remove",IDOK,145,194,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,203,194,50,14
+ PUSHBUTTON "Help",IDC_MY_HELP,319,194,50,14
+ LTEXT "Number of Licenses to remove:",IDC_STATIC,7,126,98,8
+ LTEXT "Removing licenses allows them to be legally reinstalled on another machine.",
+ IDC_STATIC,7,148,362,19
+ LTEXT "Installed Certificates:",IDC_STATIC,7,7,66,8
+ PUSHBUTTON "Refresh List",IDC_REFRESH,261,194,50,14
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Icon
+//
+
+// Icon with lowest ID value placed first to ensure application icon
+// remains consistent on all systems.
+IDI_LICENSE ICON PRELOAD DISCARDABLE "res\\license.ico"
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Bitmap
+//
+
+IDB_SMALL_ICONS BITMAP DISCARDABLE "res\\smicons.bmp"
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// String Table
+//
+
+STRINGTABLE DISCARDABLE
+BEGIN
+ IDS_ERROR_DROPPED_LINK "Communication with the license server has been interrupted. License certificate entry cannot be performed at this time."
+ IDS_ERROR_NO_RPC_SERVER "The License Logging Service is not running on the target machine, or the target machine is not accessible."
+ IDS_ERROR_UNSUCCESSFUL "The operation failed to complete successfully. "
+ IDS_ERROR_DOWNLEVEL_SERVER
+ "The License Logging Server on the target machine does not support license certificates. You must use the Windows NT 3.51-compatible license source to enter licenses for this server."
+ IDS_BAD_ACTIVATION_CODE "The certificate data or activation code is incorrect. Please ensure that the certificate data entered is identical to that on your certificate."
+ IDS_NO_PRODUCT_SEND_TO_ENTERPRISE
+ "This product is not currently registered on the target server. Please install the product before entering this license certificate."
+ IDS_PER_SEAT_SEND_TO_ENTERPRISE
+ "This certificate will be used in the Per Seat licensing mode. Therefore, it will instead be installed on the enterprise server.\n\nIf you wish to abort installation of this certificate, click Cancel."
+ IDS_PER_SEAT_LICENSING_1
+ "If you have not purchased a Client Access License for every computer that will access %1, you must purchase them prior to using %1."
+ IDS_NO_CERTIFICATE_SOURCE_NAME "No Certificate"
+ IDS_ERROR_ACCESS_DENIED "You do not have the appropriate access privileges to complete this operation."
+ IDS_PER_SERVER_LICENSING_1
+ "If you have not purchased %1 Client Access Licenses, you must purchase the required licenses prior to using %2 on this computer."
+ IDS_PER_SERVER_APP_NOT_INSTALLED
+ "The application is not installed on the target machine.\n\nPer server licenses may be installed only on machines on which the application which uses those licenses has already been installed."
+END
+
+STRINGTABLE DISCARDABLE
+BEGIN
+ IDS_PER_SEAT_CHOSEN_SEND_TO_ENTERPRISE
+ "You have chosen to install licenses in the Per Seat licensing mode. Therefore, they will instead be installed on the enterprise server.\n\nIf you wish to abort installation of these licenses, click Cancel."
+ IDS_NOT_ENOUGH_LICENSES_ON_CERTIFICATE
+ "There are not enough licenses on this certificate to install the number you requested."
+ IDS_INVALID_NUM_LICENSES
+ "The number of licenses you have selected to install from this certificate is invalid. Please enter a number between 1 and 4095."
+ IDS_ERROR_CERTIFICATE_EXPIRED
+ "This certificate has expired. The licenses it contains are no longer valid."
+ IDS_SERIAL_NUMBER "Serial Number"
+ IDS_PRODUCT_NAME "Product"
+ IDS_QUANTITY "Quantity"
+ IDS_SOURCE "Source"
+ IDS_SOURCE_NONE "None"
+ IDS_REMOVE_CERTIFICATE_CONFIRM
+ "Are you sure you wish to remove %1 licenses from %2?"
+ IDS_CERT_SOURCE_NOT_AVAILABLE
+ "The source with which this certificate was installed is not available on the local machine.\n\nUse License Manager on the target server to remove this certificate."
+ IDS_PAPER_REMOVE_COMMENT "Licenses removed by administrator."
+ IDS_NO_REMOVE_COMMENT "Licenses removed by administrator."
+ IDS_LICENSE_MODE "License Mode"
+END
+
+STRINGTABLE DISCARDABLE
+BEGIN
+ IDS_LICENSE_MODE_EITHER "Either"
+ IDS_LICENSE_MODE_PER_SEAT "Per Seat"
+ IDS_LICENSE_MODE_PER_SERVER "Per Server"
+ IDS_LICENSE_MODE_UNKNOWN "Unknown"
+ IDS_LOCAL_LICENSES_ALREADY_INSTALLED
+ "Licenses from this certificate have already been added to the target machine. Adding the requested number of licenses would exceed the installable license capacity of the certificate."
+ IDS_NET_CERTIFICATE_TARGET_ENTRY "%1\t(%2 licenses)"
+ IDS_NET_LICENSES_ALREADY_INSTALLED_ON
+ "Licenses from this certificate have already been installed on the following machines:\n\n%1\n\nAdding the requested number of licenses would exceed the installable license capacity of the certificate."
+ IDS_NET_LICENSES_ALREADY_INSTALLED
+ "Licenses from this certificate have already been installed on your network. Adding the requested number of licenses would exceed the installable license capacity of the certificate."
+ IDS_ENTERPRISE_SERVER_BACKLEVEL_CANT_ADD_CERT
+ "The enterprise server does not support this license certificate format.\nYou must upgrade the Windows NT installation on the enterprise server or use this product in the Per Server licensing mode."
+ IDS_REMOVE_INVALID_NUM_LICENSES
+ "The number of licenses to remove must be between 1 and the number of licenses currently installed."
+ IDS_LICENSE_MODE_NOT_ALLOWED
+ "This certificate does not support the selected license mode."
+ IDS_NO_PRODUCT_CERTIFICATE_SOURCES
+ "No certificate source is installed with which licenses for this product could be added."
+END
+
+STRINGTABLE DISCARDABLE
+BEGIN
+ AFX_IDS_APP_TITLE "License System"
+END
+
+#endif // English (U.S.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+#define _AFX_NO_SPLITTER_RESOURCES
+#define _AFX_NO_OLE_RESOURCES
+#define _AFX_NO_TRACKER_RESOURCES
+#define _AFX_NO_PROPERTY_RESOURCES
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE 9, 1
+#pragma code_page(1252)
+#endif
+#include "res\ccfapi32.rc2" // non-Microsoft Visual C++ edited resources
+#include "afxres.rc" // Standard components
+#endif
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
diff --git a/private/net/svcdlls/lls/ccfapi32/exports.cpp b/private/net/svcdlls/lls/ccfapi32/exports.cpp
new file mode 100644
index 000000000..58caff51b
--- /dev/null
+++ b/private/net/svcdlls/lls/ccfapi32/exports.cpp
@@ -0,0 +1,382 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ exports.cpp
+
+Abstract:
+
+ Provides APIs to enter and remove license certificates from the system.
+ The clientele consists of LICCPA.CPL (the licensing control panel applet)
+ and LLSMGR.EXE (License Manager), and it may also be used directly by setup
+ programs.
+
+Author:
+
+ Jeff Parham (jeffparh) 13-Dec-1995
+
+Revision History:
+
+--*/
+
+#include "stdafx.h"
+#include "ccfapi.h"
+#include "paper.h"
+#include "nlicdlg.h"
+
+
+///////////////////////////////////////////////////////////////////////////////
+// CCF API //
+///////////////
+
+DWORD APIENTRY CCFCertificateEnterUI( HWND hWndParent, LPCSTR pszServerName, LPCSTR pszProductName, LPCSTR pszVendor, DWORD dwFlags, LPCSTR pszSourceToUse )
+
+/*++
+
+Routine Description:
+
+ Display a dialog allowing the user to enter a license certificate
+ into the system.
+
+Arguments:
+
+ hWndParent (HWND)
+ HWND to the client's main window, for use as the parent window to any
+ opened dialogs. May be NULL.
+ pszServerName (LPCSTR)
+ Name of the server for which licenses are to be installed. Note that
+ this may not be the same as the server on which licenses are actually
+ installed, as, for example, per seat licenses are always installed on
+ the enterprise server. A NULL value indicates the local server.
+ pszProductName (LPCSTR)
+ Product for which licenses are to be installed. A NULL value indicates
+ that the user should be allowed to choose.
+ pszVendor (LPCSTR)
+ Name of the vendor of the product. This value should be NULL if
+ pszProductName is NULL, and should be non-NULL if pszProductName is
+ non-NULL.
+ dwFlags (DWORD)
+ A bitfield containing one or more of the following:
+ CCF_ENTER_FLAG_PER_SEAT_ONLY
+ Allow the user to enter only per seat licenses. Not valid in
+ combination with CCF_ENTER_FLAG_PER_SERVER_ONLY.
+ CCF_ENTER_FLAG_PER_SERVER_ONLY
+ Allow the user to enter only per server licenses. Not valid in
+ combination with CCF_ENTER_FLAG_PER_SEAT_ONLY.
+ pszSourceToUse (LPCSTR)
+ Name of the secure certificate source to use to install the certificate,
+ e.g., "Paper". A NULL value indicates that the user should be allowed
+ to choose.
+
+Return Value:
+
+ ERROR_SUCCESS (A certificate was successfully entered into the system.)
+ ERROR_CANCELLED (The user cancelled without installing a certificate.)
+ other Win error
+
+--*/
+
+{
+ AFX_MANAGE_STATE( AfxGetStaticModuleState() );
+
+ DWORD dwError;
+
+ dwError = theApp.CertificateEnter( hWndParent, pszServerName, pszProductName, pszVendor, dwFlags, pszSourceToUse );
+
+ return dwError;
+}
+
+
+//////////////////////////////////////////////////////////////////////////////
+DWORD APIENTRY CCFCertificateRemoveUI( HWND hWndParent, LPCSTR pszServerName, LPCSTR pszProductName, LPCSTR pszVendor, DWORD dwFlags, LPCSTR pszSourceToUse )
+
+/*++
+
+Routine Description:
+
+ Display a dialog allowing the user to remove one or more license
+ certificates from the system.
+
+Arguments:
+
+ hWndParent (HWND)
+ HWND to the client's main window, for use as the parent window to any
+ opened dialogs. May be NULL.
+ pszServerName (LPCSTR)
+ Name of the server on which licenses are to be removed. A NULL value
+ indicates the local server.
+ pszProductName (LPCSTR)
+ Product for which licenses are to be removed. A NULL value indicates
+ that the user should be allowed to remove licenses from any product.
+ pszVendor (LPCSTR)
+ Name of the vendor of the product. This value should be NULL if
+ pszProductName is NULL, and should be non-NULL if pszProductName is
+ non-NULL.
+ dwFlags (DWORD)
+ Certificate removal options. As of this writing, no flags are
+ supported.
+ pszSourceToUse (LPCSTR)
+ Name of the secure certificate source by which licenses are to be
+ removed, e.g., "Paper". A NULL value indicates that the user should
+ be allowed to remove licenses that were installed with any source.
+
+Return Value:
+
+ ERROR_SUCCESS
+ Win error
+
+--*/
+
+{
+ AFX_MANAGE_STATE( AfxGetStaticModuleState() );
+
+ DWORD dwError;
+
+ dwError = theApp.CertificateRemove( hWndParent, pszServerName, pszProductName, pszVendor, dwFlags, pszSourceToUse );
+
+ return dwError;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Certificate Source -- No Certificate //
+////////////////////////////////////////////
+
+DWORD APIENTRY NoCertificateEnter( HWND hWnd,
+ LPCSTR pszServerName,
+ LPCSTR pszProductName,
+ LPCSTR pszVendor,
+ DWORD dwFlags )
+
+/*++
+
+Routine Description:
+
+ Display a dialog allowing the user to enter a license certificate
+ into the system with no certificate (3.51-style).
+
+Arguments:
+
+ hWndParent (HWND)
+ HWND to the client's main window, for use as the parent window to any
+ opened dialogs. May be NULL.
+ pszServerName (LPCSTR)
+ Name of the server for which licenses are to be installed. Note that
+ this may not be the same as the server on which licenses are actually
+ installed, as, for example, per seat licenses are always installed on
+ the enterprise server. A NULL value indicates the local server.
+ pszProductName (LPCSTR)
+ Product for which licenses are to be installed. A NULL value indicates
+ that the user should be allowed to choose.
+ pszVendor (LPCSTR)
+ Name of the vendor of the product. This value should be NULL if
+ pszProductName is NULL, and should be non-NULL if pszProductName is
+ non-NULL.
+ dwFlags (DWORD)
+ A bitfield containing one or more of the following:
+ CCF_ENTER_FLAG_PER_SEAT_ONLY
+ Allow the user to enter only per seat licenses. Not valid in
+ combination with CCF_ENTER_FLAG_PER_SERVER_ONLY.
+ CCF_ENTER_FLAG_PER_SERVER_ONLY
+ Allow the user to enter only per server licenses. Not valid in
+ combination with CCF_ENTER_FLAG_PER_SEAT_ONLY.
+
+Return Value:
+
+ ERROR_SUCCESS (A certificate was successfully entered into the system.)
+ ERROR_CANCELLED (The user cancelled without installing a certificate.)
+ other Win error
+
+--*/
+
+{
+ AFX_MANAGE_STATE( AfxGetStaticModuleState() );
+
+ CNewLicenseDialog dlg( CWnd::FromHandle( hWnd ) );
+ return dlg.CertificateEnter( pszServerName, pszProductName, pszVendor, dwFlags );
+}
+
+
+//////////////////////////////////////////////////////////////////////////////
+DWORD APIENTRY NoCertificateRemove( HWND hWnd,
+ LPCSTR pszServerName,
+ DWORD dwFlags,
+ DWORD dwLicenseLevel,
+ LPVOID pvLicenseInfo )
+
+/*++
+
+Routine Description:
+
+ Remove licenses previously installed via 3.51 or NoCertificateEnter().
+
+Arguments:
+
+ hWndParent (HWND)
+ HWND to the client's main window, for use as the parent window to any
+ opened dialogs. May be NULL.
+ pszServerName (LPCSTR)
+ Name of the server on which licenses are to be removed. A NULL value
+ indicates the local server.
+ dwFlags (DWORD)
+ Certificate removal options. As of this writing, no flags are
+ supported.
+ dwLicenseLevel (DWORD)
+ Level of the LLS_LICENSE_INFO_X structure pointed to by pvLicenseInfo.
+ pvLicenseInfo (LPVOID)
+ Points to a LLS_LICENSE_INFO_X (where X is determined by dwLicenseLevel)
+ describing the licenses to be removed.
+
+Return Value:
+
+ ERROR_SUCCESS
+ Win error
+
+--*/
+
+{
+ AFX_MANAGE_STATE( AfxGetStaticModuleState() );
+
+ DWORD dwError;
+
+ if ( 1 != dwLicenseLevel )
+ {
+ dwError = ERROR_INVALID_LEVEL;
+ }
+ else
+ {
+ CNewLicenseDialog dlg( CWnd::FromHandle( hWnd ) );
+ dwError = dlg.CertificateRemove( pszServerName, dwFlags, (PLLS_LICENSE_INFO_1) pvLicenseInfo );
+ }
+
+ return dwError;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Certificate Source -- Paper Certificate //
+///////////////////////////////////////////////
+
+DWORD APIENTRY PaperCertificateEnter( HWND hWnd,
+ LPCSTR pszServerName,
+ LPCSTR pszProductName,
+ LPCSTR pszVendor,
+ DWORD dwFlags )
+
+/*++
+
+Routine Description:
+
+ Display a dialog allowing the user to enter a license certificate
+ into the system with a paper certificate.
+
+Arguments:
+
+ hWndParent (HWND)
+ HWND to the client's main window, for use as the parent window to any
+ opened dialogs. May be NULL.
+ pszServerName (LPCSTR)
+ Name of the server for which licenses are to be installed. Note that
+ this may not be the same as the server on which licenses are actually
+ installed, as, for example, per seat licenses are always installed on
+ the enterprise server. A NULL value indicates the local server.
+ pszProductName (LPCSTR)
+ Product for which licenses are to be installed. A NULL value indicates
+ that the user should be allowed to choose.
+ pszVendor (LPCSTR)
+ Name of the vendor of the product. This value should be NULL if
+ pszProductName is NULL, and should be non-NULL if pszProductName is
+ non-NULL.
+ dwFlags (DWORD)
+ A bitfield containing one or more of the following:
+ CCF_ENTER_FLAG_PER_SEAT_ONLY
+ Allow the user to enter only per seat licenses. Not valid in
+ combination with CCF_ENTER_FLAG_PER_SERVER_ONLY.
+ CCF_ENTER_FLAG_PER_SERVER_ONLY
+ Allow the user to enter only per server licenses. Not valid in
+ combination with CCF_ENTER_FLAG_PER_SEAT_ONLY.
+
+Return Value:
+
+ ERROR_SUCCESS (A certificate was successfully entered into the system.)
+ ERROR_CANCELLED (The user cancelled without installing a certificate.)
+ other Win error
+
+--*/
+
+{
+ AFX_MANAGE_STATE( AfxGetStaticModuleState() );
+
+ DWORD dwError;
+
+ if ( !!pszProductName != !!pszVendor )
+ {
+ // they must both be NULL or both have values
+ dwError = ERROR_INVALID_PARAMETER;
+ }
+ else
+ {
+ CPaperSourceDlg dlg( CWnd::FromHandle( hWnd ) );
+ dwError = dlg.CertificateEnter( pszServerName, pszProductName, pszVendor, dwFlags );
+ }
+
+ return dwError;
+}
+
+
+//////////////////////////////////////////////////////////////////////////////
+DWORD APIENTRY PaperCertificateRemove( HWND hWnd,
+ LPCSTR pszServerName,
+ DWORD dwFlags,
+ DWORD dwLicenseLevel,
+ LPVOID pvLicenseInfo )
+
+/*++
+
+Routine Description:
+
+ Remove licenses previously installed via PaperCertificateEnter().
+
+Arguments:
+
+ hWndParent (HWND)
+ HWND to the client's main window, for use as the parent window to any
+ opened dialogs. May be NULL.
+ pszServerName (LPCSTR)
+ Name of the server on which licenses are to be removed. A NULL value
+ indicates the local server.
+ dwFlags (DWORD)
+ Certificate removal options. As of this writing, no flags are
+ supported.
+ dwLicenseLevel (DWORD)
+ Level of the LLS_LICENSE_INFO_X structure pointed to by pvLicenseInfo.
+ pvLicenseInfo (LPVOID)
+ Points to a LLS_LICENSE_INFO_X (where X is determined by dwLicenseLevel)
+ describing the licenses to be removed.
+
+Return Value:
+
+ ERROR_SUCCESS
+ Win error
+
+--*/
+
+{
+ AFX_MANAGE_STATE( AfxGetStaticModuleState() );
+
+ DWORD dwError;
+
+ if ( 1 != dwLicenseLevel )
+ {
+ dwError = ERROR_INVALID_LEVEL;
+ }
+ else
+ {
+ CPaperSourceDlg dlg( CWnd::FromHandle( hWnd ) );
+ dwError = dlg.CertificateRemove( pszServerName, dwFlags, (PLLS_LICENSE_INFO_1) pvLicenseInfo );
+ }
+
+ return dwError;
+}
diff --git a/private/net/svcdlls/lls/ccfapi32/imagelst.h b/private/net/svcdlls/lls/ccfapi32/imagelst.h
new file mode 100644
index 000000000..65313e4e2
--- /dev/null
+++ b/private/net/svcdlls/lls/ccfapi32/imagelst.h
@@ -0,0 +1,38 @@
+/*++
+
+Copyright (c) 1994-95 Microsoft Corporation
+
+Module Name:
+
+ imagelst.h
+
+Abstract:
+
+ Constants for shared image list.
+
+Author:
+
+ Don Ryan (donryan) 12-Feb-1995
+
+Environment:
+
+ User Mode - Win32
+
+Revision History:
+
+ Jeff Parham (jeffparh) 12-Nov-1995
+ Copied from LLSMGR and modified for CCFApi bitmaps.
+
+--*/
+
+#ifndef _IMAGELST_H_
+#define _IMAGELST_H_
+
+#define BMPI_RGB_BKGND (RGB(0,255,0)) // green mask...
+
+#define BMPI_CERTIFICATE 0
+
+#define BMPI_SMALL_SIZE 18 // one bit border...
+#define BMPI_LARGE_SIZE 34 // one bit border...
+
+#endif // _IMAGELST_H_
diff --git a/private/net/svcdlls/lls/ccfapi32/licobj.cpp b/private/net/svcdlls/lls/ccfapi32/licobj.cpp
new file mode 100644
index 000000000..120f6f73a
--- /dev/null
+++ b/private/net/svcdlls/lls/ccfapi32/licobj.cpp
@@ -0,0 +1,308 @@
+/*++
+
+Copyright (c) 1994-95 Microsoft Corporation
+
+Module Name:
+
+ licobj.cpp
+
+Abstract:
+
+ License object implementation.
+
+Author:
+
+ Don Ryan (donryan) 04-Jan-1995
+
+Environment:
+
+ User Mode - Win32
+
+Revision History:
+
+ Jeff Parham (jeffparh) 12-Nov-1995
+ Copied from LLSMGR, converted to handle level 1 licenses,
+ removed OLE support.
+
+--*/
+
+#include "stdafx.h"
+#include "ccfapi.h"
+#include "licobj.h"
+
+#ifdef _DEBUG
+#undef THIS_FILE
+static char BASED_CODE THIS_FILE[] = __FILE__;
+#endif
+
+IMPLEMENT_DYNCREATE(CLicense, CObject)
+
+CLicense::CLicense( LPCTSTR pProduct /* = NULL */,
+ LPCTSTR pVendor /* = NULL */,
+ LPCTSTR pAdmin /* = NULL */,
+ DWORD dwPurchaseDate /* = 0 */,
+ long lQuantity /* = 0 */,
+ LPCTSTR pDescription /* = NULL */,
+ DWORD dwAllowedModes /* = LLS_LICENSE_MODE_ALLOW_PER_SEAT */,
+ DWORD dwCertificateID /* = 0 */,
+ LPCTSTR pSource /* = TEXT("None") */,
+ DWORD dwExpirationDate /* = 0 */,
+ DWORD dwMaxQuantity /* = 0 */,
+ LPDWORD pdwSecrets /* = NULL */ )
+
+/*++
+
+Routine Description:
+
+ Constructor for CLicense object.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ ASSERT(pProduct && *pProduct);
+
+ m_strAdmin = pAdmin;
+ m_strVendor = pVendor;
+ m_strProduct = pProduct;
+ m_strDescription = pDescription;
+ m_strSource = pSource;
+ m_lQuantity = lQuantity;
+ m_dwAllowedModes = dwAllowedModes;
+ m_dwCertificateID = dwCertificateID;
+ m_dwPurchaseDate = dwPurchaseDate;
+ m_dwExpirationDate = dwExpirationDate;
+ m_dwMaxQuantity = dwMaxQuantity;
+
+ if ( NULL == pdwSecrets )
+ {
+ ZeroMemory( m_adwSecrets, sizeof( m_adwSecrets ) );
+ }
+ else
+ {
+ memcpy( m_adwSecrets, pdwSecrets, sizeof( m_adwSecrets ) );
+ }
+
+ m_strSourceDisplayName = TEXT("");
+ m_strAllowedModes = TEXT("");
+}
+
+
+CString CLicense::GetSourceDisplayName()
+
+/*++
+
+Routine Description:
+
+ Retrieve the display name for the certificate source that was used to
+ install these licenses. Note that if the source that was used is not
+ installed locally, the display name is not retrievable, and the source
+ name will be returned instead.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ CString.
+
+--*/
+
+{
+ if ( m_strSourceDisplayName.IsEmpty() )
+ {
+ if ( !m_strSource.CompareNoCase( TEXT( "None" ) ) )
+ {
+ m_strSourceDisplayName.LoadString( IDS_SOURCE_NONE );
+ }
+ else
+ {
+ LONG lError;
+ CString strKeyName = TEXT( "Software\\LSAPI\\Microsoft\\CertificateSources\\" )
+ + m_strSource;
+ HKEY hKeySource;
+
+ lError = RegOpenKeyEx( HKEY_LOCAL_MACHINE, strKeyName, 0, KEY_READ, &hKeySource );
+
+ if ( ERROR_SUCCESS == lError )
+ {
+ const DWORD cchSourceDisplayName = 80;
+ DWORD cbSourceDisplayName = sizeof( TCHAR ) * cchSourceDisplayName;
+ LPTSTR pszSourceDisplayName;
+ DWORD dwType;
+
+ pszSourceDisplayName = m_strSourceDisplayName.GetBuffer( cchSourceDisplayName );
+
+ if ( NULL != pszSourceDisplayName )
+ {
+ lError = RegQueryValueEx( hKeySource, REG_VALUE_NAME, NULL, &dwType, (LPBYTE) pszSourceDisplayName, &cbSourceDisplayName );
+
+ m_strSourceDisplayName.ReleaseBuffer();
+ }
+
+ RegCloseKey( hKeySource );
+ }
+
+ if ( ( ERROR_SUCCESS != lError ) || m_strSourceDisplayName.IsEmpty() )
+ {
+ m_strSourceDisplayName = m_strSource;
+ }
+ }
+ }
+
+ return m_strSourceDisplayName;
+}
+
+
+DWORD CLicense::CreateLicenseInfo( PLLS_LICENSE_INFO_1 pLicInfo1 )
+
+/*++
+
+Routine Description:
+
+ Create a LLS_LICENSE_INFO_1 structure corresponding to this object.
+
+Arguments:
+
+ pLicInfo1 (PLLS_LICENSE_INFO_1)
+ On return, holds the created structure.
+
+Return Values:
+
+ ERROR_SUCCESS or ERROR_NOT_ENOUGH_MEMORY.
+
+--*/
+
+{
+ DWORD dwError;
+
+ pLicInfo1->Product = (LPTSTR) LocalAlloc( LMEM_FIXED, sizeof( TCHAR ) * ( 1 + m_strProduct.GetLength() ) );
+ pLicInfo1->Vendor = (LPTSTR) LocalAlloc( LMEM_FIXED, sizeof( TCHAR ) * ( 1 + m_strVendor.GetLength() ) );
+ pLicInfo1->Admin = (LPTSTR) LocalAlloc( LMEM_FIXED, sizeof( TCHAR ) * ( 1 + m_strAdmin.GetLength() ) );
+ pLicInfo1->Comment = (LPTSTR) LocalAlloc( LMEM_FIXED, sizeof( TCHAR ) * ( 1 + m_strDescription.GetLength() ) );
+ pLicInfo1->Source = (LPTSTR) LocalAlloc( LMEM_FIXED, sizeof( TCHAR ) * ( 1 + m_strSource.GetLength() ) );
+
+ if ( ( NULL == pLicInfo1->Product )
+ || ( NULL == pLicInfo1->Vendor )
+ || ( NULL == pLicInfo1->Admin )
+ || ( NULL == pLicInfo1->Comment )
+ || ( NULL == pLicInfo1->Source ) )
+ {
+ dwError = ERROR_NOT_ENOUGH_MEMORY;
+ }
+ else
+ {
+ lstrcpy( pLicInfo1->Product, m_strProduct );
+ lstrcpy( pLicInfo1->Vendor, m_strVendor );
+ lstrcpy( pLicInfo1->Admin, m_strAdmin );
+ lstrcpy( pLicInfo1->Comment, m_strDescription );
+ lstrcpy( pLicInfo1->Source, m_strSource );
+
+ pLicInfo1->Quantity = m_lQuantity;
+ pLicInfo1->MaxQuantity = m_dwMaxQuantity;
+ pLicInfo1->Date = m_dwPurchaseDate;
+ pLicInfo1->AllowedModes = m_dwAllowedModes;
+ pLicInfo1->CertificateID = m_dwCertificateID;
+ pLicInfo1->ExpirationDate = m_dwExpirationDate;
+ memcpy( pLicInfo1->Secrets, m_adwSecrets, sizeof( m_adwSecrets ) );
+
+ dwError = ERROR_SUCCESS;
+ }
+
+ if ( ERROR_SUCCESS != dwError )
+ {
+ if ( NULL != pLicInfo1->Product ) LocalFree( pLicInfo1->Product );
+ if ( NULL != pLicInfo1->Vendor ) LocalFree( pLicInfo1->Vendor );
+ if ( NULL != pLicInfo1->Admin ) LocalFree( pLicInfo1->Admin );
+ if ( NULL != pLicInfo1->Comment ) LocalFree( pLicInfo1->Comment );
+ if ( NULL != pLicInfo1->Source ) LocalFree( pLicInfo1->Source );
+
+ ZeroMemory( pLicInfo1, sizeof( *pLicInfo1 ) );
+ }
+
+ return dwError;
+}
+
+
+void CLicense::DestroyLicenseInfo( PLLS_LICENSE_INFO_1 pLicInfo1 )
+
+/*++
+
+Routine Description:
+
+ Frees a license structure previously created by CreateLicenseInfo().
+
+Arguments:
+
+ pLicInfo1 (PLLS_LICENSE_INFO_1)
+ The structure previously created by CreateLicenseInfo().
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ if ( NULL != pLicInfo1->Product ) LocalFree( pLicInfo1->Product );
+ if ( NULL != pLicInfo1->Vendor ) LocalFree( pLicInfo1->Vendor );
+ if ( NULL != pLicInfo1->Admin ) LocalFree( pLicInfo1->Admin );
+ if ( NULL != pLicInfo1->Comment ) LocalFree( pLicInfo1->Comment );
+ if ( NULL != pLicInfo1->Source ) LocalFree( pLicInfo1->Source );
+}
+
+
+CString CLicense::GetAllowedModesString()
+
+/*++
+
+Routine Description:
+
+ Get a string corresponding to the license mode(s) for which this license
+ was installed.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ CString.
+
+--*/
+
+{
+ if ( m_strAllowedModes.IsEmpty() )
+ {
+ UINT uStringID;
+
+ switch ( m_dwAllowedModes & ( LLS_LICENSE_MODE_ALLOW_PER_SEAT | LLS_LICENSE_MODE_ALLOW_PER_SERVER ) )
+ {
+ case ( LLS_LICENSE_MODE_ALLOW_PER_SEAT | LLS_LICENSE_MODE_ALLOW_PER_SERVER ):
+ uStringID = IDS_LICENSE_MODE_EITHER;
+ break;
+ case LLS_LICENSE_MODE_ALLOW_PER_SEAT:
+ uStringID = IDS_LICENSE_MODE_PER_SEAT;
+ break;
+ case LLS_LICENSE_MODE_ALLOW_PER_SERVER:
+ uStringID = IDS_LICENSE_MODE_PER_SERVER;
+ break;
+ default:
+ uStringID = IDS_LICENSE_MODE_UNKNOWN;
+ break;
+ }
+
+ m_strAllowedModes.LoadString( uStringID );
+ }
+
+ return m_strAllowedModes;
+}
+
diff --git a/private/net/svcdlls/lls/ccfapi32/licobj.h b/private/net/svcdlls/lls/ccfapi32/licobj.h
new file mode 100644
index 000000000..ce62aa4d0
--- /dev/null
+++ b/private/net/svcdlls/lls/ccfapi32/licobj.h
@@ -0,0 +1,76 @@
+/*++
+
+Copyright (c) 1994-95 Microsoft Corporation
+
+Module Name:
+
+ licobj.h
+
+Abstract:
+
+ License object implementation.
+
+Author:
+
+ Don Ryan (donryan) 04-Jan-1995
+
+Environment:
+
+ User Mode - Win32
+
+Revision History:
+
+ Jeff Parham (jeffparh) 12-Nov-1995
+ Copied from LLSMGR, converted to handle level 1 licenses,
+ removed OLE support.
+
+--*/
+
+#ifndef _LICOBJ_H_
+#define _LICOBJ_H_
+
+class CLicense : public CObject
+{
+ DECLARE_DYNCREATE(CLicense)
+
+public:
+ CString m_strAdmin;
+ CString m_strProduct;
+ CString m_strVendor;
+ CString m_strDescription;
+ CString m_strSource;
+ long m_lQuantity;
+ DWORD m_dwAllowedModes;
+ DWORD m_dwCertificateID;
+ DWORD m_dwPurchaseDate;
+ DWORD m_dwExpirationDate;
+ DWORD m_dwMaxQuantity;
+ DWORD m_adwSecrets[ LLS_NUM_SECRETS ];
+
+ // cache for derived values
+ CString m_strSourceDisplayName;
+ CString m_strAllowedModes;
+
+public:
+ CLicense( LPCTSTR pProduct = NULL,
+ LPCTSTR pVendor = NULL,
+ LPCTSTR pAdmin = NULL,
+ DWORD dwPurchaseDate = 0,
+ long lQuantity = 0,
+ LPCTSTR pDescription = NULL,
+ DWORD dwAllowedModes = LLS_LICENSE_MODE_ALLOW_PER_SEAT,
+ DWORD dwCertificateID = 0,
+ LPCTSTR pSource = TEXT("None"),
+ DWORD dwExpirationDate = 0,
+ DWORD dwMaxQuantity = 0,
+ LPDWORD pdwSecrets = NULL );
+
+ CString GetSourceDisplayName();
+ CString GetAllowedModesString();
+
+ DWORD CreateLicenseInfo( PLLS_LICENSE_INFO_1 pLicInfo );
+ void DestroyLicenseInfo( PLLS_LICENSE_INFO_1 pLicInfo );
+
+};
+
+#endif // _LICOBJ_H_
diff --git a/private/net/svcdlls/lls/ccfapi32/makefile b/private/net/svcdlls/lls/ccfapi32/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/net/svcdlls/lls/ccfapi32/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/net/svcdlls/lls/ccfapi32/md4.h b/private/net/svcdlls/lls/ccfapi32/md4.h
new file mode 100644
index 000000000..0f1905dc2
--- /dev/null
+++ b/private/net/svcdlls/lls/ccfapi32/md4.h
@@ -0,0 +1,120 @@
+#ifndef _MD4_H_
+#define _MD4_H_
+
+/* MD4.H - header file for MD4C.C
+ */
+
+/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
+ rights reserved.
+
+ License to copy and use this software is granted provided that it
+ is identified as the "RSA Data Security, Inc. MD4 Message-Digest
+ Algorithm" in all material mentioning or referencing this software
+ or this function.
+
+ License is also granted to make and use derivative works provided
+ that such works are identified as "derived from the RSA Data
+ Security, Inc. MD4 Message-Digest Algorithm" in all material
+ mentioning or referencing the derived work.
+
+ RSA Data Security, Inc. makes no representations concerning either
+ the merchantability of this software or the suitability of this
+ software for any particular purpose. It is provided "as is"
+ without express or implied warranty of any kind.
+
+ These notices must be retained in any copies of any part of this
+ documentation and/or software.
+ */
+
+/* This code differs from the MD4 implementation contained in Internet
+ RFC-1320 in the following respects:
+
+ 1. "global.h" is no longer needed.
+
+ 2. PROTO_LIST was removed from the function prototypes.
+
+ 3. Comments on the use of the main calls added to aid developers.
+ */
+
+/* ---------------------------------------------------------------------
+ * The procedure for using the following function calls to compute a
+ * digest is as follows:
+ *
+ * MD4_CTX context;
+ * // create a storage context that persistes between calls.
+ *
+ * MD4_Init (&context);
+ * // initialize context's initial digest and byte-count
+ *
+ * MD4Update (&context, inputString, inputLength);
+ * // input first or only block of data to be digested.
+ *
+ * MD4Update (&context, inputString, inputLength);
+ * // input subsequent blocks or last block of data to be digested.
+ *
+ * MD4Final (digest, &context);
+ * // compute and return final 16-byte digest
+ *
+ * --------------------------------------------------------------------- */
+
+typedef unsigned long UINT4;
+
+/* POINTER defines a generic pointer type */
+typedef unsigned char *POINTER;
+
+/* MD4 context. */
+typedef struct {
+ UINT4 state[4]; /* state (ABCD) */
+ UINT4 count[2]; /* number of bits, modulo 2^64 (lsb first) */
+ unsigned char buffer[64]; /* input buffer */
+} MD4_CTX;
+
+/* ---------------------------------------------------------------------
+ * This function initializes the context for the message digest.
+ * It must be called as the first step and before processing input data.
+ * --------------------------------------------------------------------- */
+
+void MD4Init ( MD4_CTX *context ) ; /* context */
+
+/* ---------------------------------------------------------------------
+ * This function accepts input data of length "inputLen" and digests it.
+ * All data to be digested is input via this function and no other.
+ * The running byte count and any fragment of undigested data is stored
+ * in the context for retention between calls.
+ * --------------------------------------------------------------------- */
+
+void MD4Update ( MD4_CTX *context, /* context */
+ POINTER input, /* input block */
+ unsigned int inputLen ) ; /* length of input block */
+
+/* ---------------------------------------------------------------------
+ * This function accepts not data, but finishes up the digest and
+ * returns the 16 byte resulting message digest. Finishing up includes
+ * taking any undigested fragment stored in the context, padding the
+ * message, appending the length and then digesting the resulting string.
+ * --------------------------------------------------------------------- */
+
+void MD4Final ( unsigned char *digest, /* 16-byte message digest */
+ MD4_CTX *context ) ; /* context */
+
+/* ---------------------------------------------------------------------
+
+ The MD4 test suite results, contained in appendix A.5 of RFC-1320,
+ are as listed below. They are printed by the "mddriver.c" test
+ driver contained in appendix A.4 of RFC-1320.
+
+ MD4 test suite:
+ MD4 ("") = 31d6cfe0d16ae931b73c59d7e0c089c0
+ MD4 ("a") = bde52cb31de33e46245e05fbdbd6fb24
+ MD4 ("abc") = a448017aaf21d8525fc10ae87aa6729d
+ MD4 ("message digest") = d9130a8164549fe818874806e1c7014b
+ MD4 ("abcdefghijklmnopqrstuvwxyz") = d79e1c308aa5bbcdeea8ed63df412da9
+ MD4 ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789")
+ = 043f8582f241db351ce627e153e7f0e4
+ MD4 ("123456789012345678901234567890123456789012345678901234567890123
+ 45678901234567890") = e33b4ddc9c38f2199c3e7b164fcc0536
+
+ * --------------------------------------------------------------------- */
+
+#endif
+
diff --git a/private/net/svcdlls/lls/ccfapi32/md4c.cpp b/private/net/svcdlls/lls/ccfapi32/md4c.cpp
new file mode 100644
index 000000000..db1e8f56c
--- /dev/null
+++ b/private/net/svcdlls/lls/ccfapi32/md4c.cpp
@@ -0,0 +1,291 @@
+/* MD4C.C - RSA Data Security, Inc., MD4 message-digest algorithm
+ */
+
+/* Copyright (C) 1990-2, RSA Data Security, Inc. All rights reserved.
+
+ License to copy and use this software is granted provided that it
+ is identified as the "RSA Data Security, Inc. MD4 Message-Digest
+ Algorithm" in all material mentioning or referencing this software
+ or this function.
+
+ License is also granted to make and use derivative works provided
+ that such works are identified as "derived from the RSA Data
+ Security, Inc. MD4 Message-Digest Algorithm" in all material
+ mentioning or referencing the derived work.
+
+ RSA Data Security, Inc. makes no representations concerning either
+ the merchantability of this software or the suitability of this
+ software for any particular purpose. It is provided "as is"
+ without express or implied warranty of any kind.
+
+ These notices must be retained in any copies of any part of this
+ documentation and/or software.
+ */
+
+/* This code differs from the MD4 implementation contained in Internet
+ RFC-1320 in the following respects:
+
+ 1. Faster boolean computations for the "F" and "G" functions, as
+ suggested by Richard Schoeppel, rcs@cs.arizona.edu, have been included.
+ The original code contained in the RFC is retained as comments.
+
+ 2. PROTO_LIST was removed from the function prototypes.
+ */
+
+#include "stdafx.h"
+#include "md4.h"
+
+/* Constants for MD4Transform routine.
+ */
+#define S11 3
+#define S12 7
+#define S13 11
+#define S14 19
+#define S21 3
+#define S22 5
+#define S23 9
+#define S24 13
+#define S31 3
+#define S32 9
+#define S33 11
+#define S34 15
+
+static void MD4Transform ( UINT4 *state, unsigned char *block ) ;
+static void Encode ( unsigned char *output,
+ UINT4 *input, unsigned int len ) ;
+static void Decode ( UINT4 *output,
+ unsigned char *input, unsigned int len ) ;
+
+
+static unsigned char PADDING[64] = {
+ 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+/* F, G and H are basic MD4 functions.
+ Faster methods suggested by Richard Schoeppel, rcs@cs.arizona.edu
+ */
+
+/* #define F(x, y, z) (((x) & (y)) | ((~x) & (z))) original code */
+#define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z)))) /* faster method */
+/* #define G(x, y, z) (((x) & (y)) | ((x) & (z)) | ((y) & (z))) original code */
+#define G(x, y, z) (((x) & (y)) | ((z) & ((x) | (y)))) /* faster method */
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+
+/* ROTATE_LEFT rotates x left n bits.
+ */
+#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))
+
+/* FF, GG and HH are transformations for rounds 1, 2 and 3 */
+/* Rotation is separate from addition to prevent recomputation */
+
+#define FF(a, b, c, d, x, s) { \
+ (a) += F ((b), (c), (d)) + (x); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ }
+#define GG(a, b, c, d, x, s) { \
+ (a) += G ((b), (c), (d)) + (x) + (UINT4)0x5a827999; \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ }
+#define HH(a, b, c, d, x, s) { \
+ (a) += H ((b), (c), (d)) + (x) + (UINT4)0x6ed9eba1; \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ }
+
+/* MD4 initialization. Begins an MD4 operation, writing a new context.
+ */
+
+void MD4Init ( MD4_CTX *context ) /* context */
+
+{
+ context->count[0] = context->count[1] = 0;
+
+ /* Load magic initialization constants.
+ */
+ context->state[0] = 0x67452301;
+ context->state[1] = 0xefcdab89;
+ context->state[2] = 0x98badcfe;
+ context->state[3] = 0x10325476;
+}
+
+/* MD4 block update operation. Continues an MD4 message-digest
+ operation, processing another message block, and updating the
+ context.
+ */
+
+void MD4Update ( MD4_CTX *context, /* context */
+ POINTER input, /* input block */
+ unsigned int inputLen ) /* length of input block */
+
+{
+ unsigned int i, index, partLen;
+
+ /* Compute number of bytes mod 64 */
+ index = (unsigned int)((context->count[0] >> 3) & 0x3F);
+ /* Update number of bits */
+ if ((context->count[0] += ((UINT4)inputLen << 3))
+ < ((UINT4)inputLen << 3))
+ context->count[1]++;
+ context->count[1] += ((UINT4)inputLen >> 29);
+
+ partLen = 64 - index;
+
+ /* Transform as many times as possible.
+ */
+ if (inputLen >= partLen) {
+ memcpy((POINTER)&context->buffer[index], (POINTER)input, partLen);
+ MD4Transform (context->state, context->buffer);
+
+ for (i = partLen; i + 63 < inputLen; i += 64)
+ MD4Transform (context->state, &input[i]);
+
+ index = 0;
+ }
+ else
+ i = 0;
+
+ /* Buffer remaining input */
+ memcpy((POINTER)&context->buffer[index], (POINTER)&input[i], inputLen-i);
+}
+
+/* MD4 finalization. Ends an MD4 message-digest operation, writing the
+ the message digest and zeroizing the context.
+ */
+
+void MD4Final ( unsigned char *digest, /* 16-byte message digest */
+ MD4_CTX *context ) /* context */
+
+{
+ unsigned char bits[8];
+ unsigned int index, padLen;
+
+ /* Save number of bits */
+ Encode (bits, context->count, 8);
+
+ /* Pad out to 56 mod 64.
+ */
+ index = (unsigned int)((context->count[0] >> 3) & 0x3f);
+ padLen = (index < 56) ? (56 - index) : (120 - index);
+ MD4Update (context, PADDING, padLen);
+
+ /* Append length (before padding) */
+ MD4Update (context, bits, 8);
+ /* Store state in digest */
+ Encode (digest, context->state, 16);
+
+ /* Zeroize sensitive information.
+ */
+ memset ((POINTER)context, 0, sizeof (*context));
+
+}
+
+/* MD4 basic transformation. Transforms state based on block.
+ */
+
+static void MD4Transform ( UINT4 *state, /* 16-byte state */
+ unsigned char *block ) /* 64-byte block */
+
+{
+ UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16];
+
+ Decode (x, block, 64);
+
+ /* Round 1 */
+ FF (a, b, c, d, x[ 0], S11); /* 1 */
+ FF (d, a, b, c, x[ 1], S12); /* 2 */
+ FF (c, d, a, b, x[ 2], S13); /* 3 */
+ FF (b, c, d, a, x[ 3], S14); /* 4 */
+ FF (a, b, c, d, x[ 4], S11); /* 5 */
+ FF (d, a, b, c, x[ 5], S12); /* 6 */
+ FF (c, d, a, b, x[ 6], S13); /* 7 */
+ FF (b, c, d, a, x[ 7], S14); /* 8 */
+ FF (a, b, c, d, x[ 8], S11); /* 9 */
+ FF (d, a, b, c, x[ 9], S12); /* 10 */
+ FF (c, d, a, b, x[10], S13); /* 11 */
+ FF (b, c, d, a, x[11], S14); /* 12 */
+ FF (a, b, c, d, x[12], S11); /* 13 */
+ FF (d, a, b, c, x[13], S12); /* 14 */
+ FF (c, d, a, b, x[14], S13); /* 15 */
+ FF (b, c, d, a, x[15], S14); /* 16 */
+
+ /* Round 2 */
+ GG (a, b, c, d, x[ 0], S21); /* 17 */
+ GG (d, a, b, c, x[ 4], S22); /* 18 */
+ GG (c, d, a, b, x[ 8], S23); /* 19 */
+ GG (b, c, d, a, x[12], S24); /* 20 */
+ GG (a, b, c, d, x[ 1], S21); /* 21 */
+ GG (d, a, b, c, x[ 5], S22); /* 22 */
+ GG (c, d, a, b, x[ 9], S23); /* 23 */
+ GG (b, c, d, a, x[13], S24); /* 24 */
+ GG (a, b, c, d, x[ 2], S21); /* 25 */
+ GG (d, a, b, c, x[ 6], S22); /* 26 */
+ GG (c, d, a, b, x[10], S23); /* 27 */
+ GG (b, c, d, a, x[14], S24); /* 28 */
+ GG (a, b, c, d, x[ 3], S21); /* 29 */
+ GG (d, a, b, c, x[ 7], S22); /* 30 */
+ GG (c, d, a, b, x[11], S23); /* 31 */
+ GG (b, c, d, a, x[15], S24); /* 32 */
+
+ /* Round 3 */
+ HH (a, b, c, d, x[ 0], S31); /* 33 */
+ HH (d, a, b, c, x[ 8], S32); /* 34 */
+ HH (c, d, a, b, x[ 4], S33); /* 35 */
+ HH (b, c, d, a, x[12], S34); /* 36 */
+ HH (a, b, c, d, x[ 2], S31); /* 37 */
+ HH (d, a, b, c, x[10], S32); /* 38 */
+ HH (c, d, a, b, x[ 6], S33); /* 39 */
+ HH (b, c, d, a, x[14], S34); /* 40 */
+ HH (a, b, c, d, x[ 1], S31); /* 41 */
+ HH (d, a, b, c, x[ 9], S32); /* 42 */
+ HH (c, d, a, b, x[ 5], S33); /* 43 */
+ HH (b, c, d, a, x[13], S34); /* 44 */
+ HH (a, b, c, d, x[ 3], S31); /* 45 */
+ HH (d, a, b, c, x[11], S32); /* 46 */
+ HH (c, d, a, b, x[ 7], S33); /* 47 */
+ HH (b, c, d, a, x[15], S34); /* 48 */
+
+ state[0] += a;
+ state[1] += b;
+ state[2] += c;
+ state[3] += d;
+
+ /* Zeroize sensitive information.
+ */
+ memset ((POINTER)x, 0, sizeof (x));
+}
+
+/* Encodes input (UINT4) into output (unsigned char). Assumes len is
+ a multiple of 4.
+ */
+
+static void Encode ( unsigned char *output,
+ UINT4 *input,
+ unsigned int len )
+
+{
+ unsigned int i, j;
+
+ for (i = 0, j = 0; j < len; i++, j += 4) {
+ output[j] = (unsigned char)(input[i] & 0xff);
+ output[j+1] = (unsigned char)((input[i] >> 8) & 0xff);
+ output[j+2] = (unsigned char)((input[i] >> 16) & 0xff);
+ output[j+3] = (unsigned char)((input[i] >> 24) & 0xff);
+ }
+}
+
+/* Decodes input (unsigned char) into output (UINT4). Assumes len is
+ a multiple of 4.
+ */
+
+static void Decode ( UINT4 *output,
+ unsigned char *input,
+ unsigned int len )
+
+{
+ unsigned int i, j;
+
+ for (i = 0, j = 0; j < len; i++, j += 4)
+ output[i] = ((UINT4)input[j]) | (((UINT4)input[j+1]) << 8) |
+ (((UINT4)input[j+2]) << 16) | (((UINT4)input[j+3]) << 24);
+}
diff --git a/private/net/svcdlls/lls/ccfapi32/nlicdlg.cpp b/private/net/svcdlls/lls/ccfapi32/nlicdlg.cpp
new file mode 100644
index 000000000..5d98de7b1
--- /dev/null
+++ b/private/net/svcdlls/lls/ccfapi32/nlicdlg.cpp
@@ -0,0 +1,1386 @@
+/*++
+
+Copyright (c) 1994-95 Microsoft Corporation
+
+Module Name:
+
+ nlicdlg.cpp
+
+Abstract:
+
+ 3.51-style license dialog implementation.
+
+Author:
+
+ Don Ryan (donryan) 02-Feb-1995
+
+Environment:
+
+ User Mode - Win32
+
+Revision History:
+
+ Jeff Parham (jeffparh) 14-Dec-1995
+ Moved over from LLSMGR, added ability to purchase per server licenses,
+ added license removal functionality.
+
+--*/
+
+#include "stdafx.h"
+#include "ccfapi.h"
+#include "nlicdlg.h"
+#include "pseatdlg.h"
+#include "psrvdlg.h"
+
+#ifdef _DEBUG
+#undef THIS_FILE
+static char BASED_CODE THIS_FILE[] = __FILE__;
+#endif
+
+BEGIN_MESSAGE_MAP(CNewLicenseDialog, CDialog)
+ //{{AFX_MSG_MAP(CNewLicenseDialog)
+ ON_NOTIFY(UDN_DELTAPOS, IDC_NEW_LICENSE_SPIN, OnDeltaPosSpin)
+ ON_EN_UPDATE(IDC_NEW_LICENSE_QUANTITY, OnUpdateQuantity)
+ ON_BN_CLICKED(IDC_MY_HELP, OnHelp)
+ ON_BN_CLICKED(IDC_PER_SEAT, OnPerSeat)
+ ON_BN_CLICKED(IDC_PER_SERVER, OnPerServer)
+ ON_WM_DESTROY()
+ //}}AFX_MSG_MAP
+END_MESSAGE_MAP()
+
+
+CNewLicenseDialog::CNewLicenseDialog(CWnd* pParent /*=NULL*/)
+ : CDialog(CNewLicenseDialog::IDD, pParent)
+
+/*++
+
+Routine Description:
+
+ Constructor for dialog.
+
+Arguments:
+
+ pParent - owner window.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ //{{AFX_DATA_INIT(CNewLicenseDialog)
+ m_strComment = _T("");
+ m_nLicenses = 0;
+ m_nLicensesMin = 0;
+ m_strProduct = _T("");
+ m_nLicenseMode = -1;
+ //}}AFX_DATA_INIT
+
+ m_strServerName = _T("");
+ m_strProduct = _T("");
+ m_dwEnterFlags = 0;
+ m_nLicenseMode = 0; // per seat
+
+ m_bAreCtrlsInitialized = FALSE;
+
+ m_hLls = NULL;
+ m_hEnterpriseLls = NULL;
+}
+
+CNewLicenseDialog::~CNewLicenseDialog()
+{
+ if ( NULL != m_hLls )
+ {
+ LlsClose( m_hLls );
+ }
+
+ if ( NULL != m_hEnterpriseLls )
+ {
+ LlsClose( m_hEnterpriseLls );
+ }
+}
+
+void CNewLicenseDialog::DoDataExchange(CDataExchange* pDX)
+
+/*++
+
+Routine Description:
+
+ Called by framework to exchange dialog data.
+
+Arguments:
+
+ pDX - data exchange object.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ CDialog::DoDataExchange(pDX);
+ //{{AFX_DATA_MAP(CNewLicenseDialog)
+ DDX_Control(pDX, IDC_NEW_LICENSE_COMMENT, m_comEdit);
+ DDX_Control(pDX, IDC_NEW_LICENSE_QUANTITY, m_licEdit);
+ DDX_Control(pDX, IDC_NEW_LICENSE_SPIN, m_spinCtrl);
+ DDX_Control(pDX, IDC_NEW_LICENSE_PRODUCT, m_productList);
+ DDX_Text(pDX, IDC_NEW_LICENSE_COMMENT, m_strComment);
+ DDX_Text(pDX, IDC_NEW_LICENSE_QUANTITY, m_nLicenses);
+ DDV_MinMaxLong(pDX, m_nLicenses, m_nLicensesMin, 999999);
+ DDX_CBString(pDX, IDC_NEW_LICENSE_PRODUCT, m_strProduct);
+ DDX_Radio(pDX, IDC_PER_SEAT, m_nLicenseMode);
+ //}}AFX_DATA_MAP
+}
+
+
+void CNewLicenseDialog::InitCtrls()
+
+/*++
+
+Routine Description:
+
+ Initializes dialog controls.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ m_licEdit.SetFocus();
+ m_licEdit.SetSel(0,-1);
+ m_licEdit.LimitText(6);
+
+ m_comEdit.LimitText(256);
+
+ m_spinCtrl.SetRange(0, UD_MAXVAL);
+
+ // if license mode set by application, don't let user change it
+ if ( m_dwEnterFlags & ( CCF_ENTER_FLAG_PER_SEAT_ONLY | CCF_ENTER_FLAG_PER_SERVER_ONLY ) )
+ {
+ if ( m_dwEnterFlags & CCF_ENTER_FLAG_PER_SEAT_ONLY )
+ {
+ m_nLicenseMode = 0;
+ OnPerSeat();
+ }
+ else
+ {
+ m_nLicenseMode = 1;
+ OnPerServer();
+ }
+
+ GetDlgItem( IDC_PER_SERVER )->EnableWindow( FALSE );
+ GetDlgItem( IDC_PER_SEAT )->EnableWindow( FALSE );
+ UpdateData( FALSE );
+ }
+
+ m_bAreCtrlsInitialized = TRUE;
+}
+
+
+void CNewLicenseDialog::AbortDialogIfNecessary()
+
+/*++
+
+Routine Description:
+
+ Displays status and aborts if connection lost.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ theApp.DisplayLastError();
+
+ if ( theApp.IsConnectionDropped() )
+ {
+ AbortDialog(); // bail...
+ }
+}
+
+
+void CNewLicenseDialog::AbortDialog()
+
+/*++
+
+Routine Description:
+
+ Aborts dialog.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ EndDialog(IDABORT);
+}
+
+
+BOOL CNewLicenseDialog::OnInitDialog()
+
+/*++
+
+Routine Description:
+
+ Message handler for WM_INITDIALOG.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ Returns false if focus set manually.
+
+--*/
+
+{
+ CDialog::OnInitDialog();
+
+ if (!m_bAreCtrlsInitialized)
+ {
+ InitCtrls();
+
+ if (!RefreshCtrls())
+ {
+ AbortDialogIfNecessary(); // display error...
+ }
+ }
+
+ return TRUE;
+}
+
+
+void CNewLicenseDialog::OnOK()
+
+/*++
+
+Routine Description:
+
+ Creates a new license for product.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ if ( ConnectServer() )
+ {
+ if (!IsQuantityValid())
+ return;
+
+ if (m_strProduct.IsEmpty())
+ return;
+
+ if ( m_nLicenseMode )
+ {
+ CPerServerLicensingDialog psLicDlg;
+ psLicDlg.m_strProduct = m_strProduct;
+ psLicDlg.m_strLicenses.Format( TEXT( "%u" ), m_nLicenses );
+
+ if ( psLicDlg.DoModal() != IDOK )
+ return;
+ }
+ else
+ {
+ CPerSeatLicensingDialog psLicDlg;
+ psLicDlg.m_strProduct = m_strProduct;
+
+ if ( psLicDlg.DoModal() != IDOK )
+ return;
+ }
+
+ NTSTATUS NtStatus = AddLicense();
+
+ if ( STATUS_SUCCESS == NtStatus )
+ {
+ EndDialog(IDOK);
+ }
+ else if ( ( ERROR_CANCELLED != NtStatus ) && ( STATUS_CANCELLED != NtStatus ) )
+ {
+ AbortDialogIfNecessary(); // display error...
+ }
+ }
+}
+
+
+BOOL CNewLicenseDialog::RefreshCtrls()
+
+/*++
+
+Routine Description:
+
+ Refreshs list of products available.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ Returns true if controls refreshed.
+
+--*/
+
+{
+ int iProductInCB = CB_ERR;
+
+ BeginWaitCursor(); // hourglass...
+
+ if ( !m_strProduct.IsEmpty() )
+ {
+ iProductInCB = m_productList.AddString(m_strProduct);
+ }
+ else if ( ConnectServer() )
+ {
+ GetProductList();
+ }
+
+ m_productList.SetCurSel((iProductInCB == CB_ERR) ? 0 : iProductInCB);
+
+ EndWaitCursor(); // hourglass...
+
+ return TRUE;
+}
+
+
+void CNewLicenseDialog::OnDeltaPosSpin(NMHDR* pNMHDR, LRESULT* pResult)
+
+/*++
+
+Routine Description:
+
+ Notification handler for UDN_DELTAPOS.
+
+Arguments:
+
+ pNMHDR - notification header.
+ pResult - return code.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ UpdateData(TRUE); // get data
+
+ m_nLicenses += ((NM_UPDOWN*)pNMHDR)->iDelta;
+
+ if (m_nLicenses < 0)
+ {
+ m_nLicenses = 0;
+
+ ::MessageBeep(MB_OK);
+ }
+ else if (m_nLicenses > 999999)
+ {
+ m_nLicenses = 999999;
+
+ ::MessageBeep(MB_OK);
+ }
+
+ UpdateData(FALSE); // set data
+
+ *pResult = 1; // handle ourselves...
+}
+
+
+void CNewLicenseDialog::OnUpdateQuantity()
+
+/*++
+
+Routine Description:
+
+ Message handler for EN_UPDATE.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ long nLicensesOld = m_nLicenses;
+
+ if (!IsQuantityValid())
+ {
+ m_nLicenses = nLicensesOld;
+
+ UpdateData(FALSE);
+
+ m_licEdit.SetFocus();
+ m_licEdit.SetSel(0,-1);
+
+ ::MessageBeep(MB_OK);
+ }
+}
+
+
+BOOL CNewLicenseDialog::IsQuantityValid()
+
+/*++
+
+Routine Description:
+
+ Wrapper around UpdateData(TRUE).
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ VT_BOOL.
+
+--*/
+
+{
+ BOOL bIsValid;
+
+ m_nLicensesMin = 1; // raise minimum...
+
+ bIsValid = UpdateData(TRUE);
+
+ m_nLicensesMin = 0; // reset minimum...
+
+ return bIsValid;
+}
+
+
+BOOL CNewLicenseDialog::ConnectServer()
+
+/*++
+
+Routine Description:
+
+ Establish a connection to the license service on the target server.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ BOOL.
+
+--*/
+
+{
+ if ( NULL == m_hLls )
+ {
+ LPTSTR pszServerName;
+
+ if ( m_strServerName.IsEmpty() )
+ {
+ pszServerName = NULL;
+ }
+ else
+ {
+ pszServerName = m_strServerName.GetBuffer( 0 );
+ }
+
+ NTSTATUS nt = ConnectTo( FALSE, pszServerName, &m_hLls );
+
+ if ( NULL != pszServerName )
+ {
+ m_strServerName.ReleaseBuffer();
+ }
+ }
+
+ if ( NULL == m_hLls )
+ {
+ theApp.DisplayLastError();
+ EndDialog( IDABORT );
+ }
+
+ return ( NULL != m_hLls );
+}
+
+
+BOOL CNewLicenseDialog::ConnectEnterprise()
+
+/*++
+
+Routine Description:
+
+ Establish a connection to the license service on the enterprise server
+ of the target server.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ BOOL.
+
+--*/
+
+{
+ if ( NULL == m_hEnterpriseLls )
+ {
+ LPTSTR pszServerName;
+
+ if ( m_strServerName.IsEmpty() )
+ {
+ pszServerName = NULL;
+ }
+ else
+ {
+ pszServerName = m_strServerName.GetBuffer( 0 );
+ }
+
+ NTSTATUS nt = ConnectTo( !( m_dwEnterFlags & CCF_ENTER_FLAG_SERVER_IS_ES ), pszServerName, &m_hEnterpriseLls );
+
+ if ( NULL != pszServerName )
+ {
+ m_strServerName.ReleaseBuffer();
+ }
+ }
+
+ if ( NULL == m_hEnterpriseLls )
+ {
+ theApp.DisplayLastError();
+
+ // not being able to connect to the enterprise
+ // is not a fatal error
+ // EndDialog( IDABORT );
+ }
+
+ return ( NULL != m_hEnterpriseLls );
+}
+
+
+NTSTATUS CNewLicenseDialog::ConnectTo( BOOL bUseEnterprise, LPTSTR pszServerName, PLLS_HANDLE phLls )
+
+/*++
+
+Routine Description:
+
+ Establish a connection to the license service on the given server or that
+ on the given server's enterprise server.
+
+Arguments:
+
+ bUseEnterprise (BOOL)
+ If TRUE, connect to the enterprise server of the target server, not to
+ the target server itself.
+ pszServerName (LPTSTR)
+ The target server. A NULL value indicates the local server.
+ phLls (PLLS_HANDLE)
+ On return, holds the handle to the standard LLS RPC.
+
+Return Values:
+
+ STATUS_SUCCESS or NT status code.
+
+--*/
+
+{
+ NTSTATUS nt;
+
+ if ( !bUseEnterprise )
+ {
+ nt = ::LlsConnect( pszServerName, phLls );
+ }
+ else
+ {
+ PLLS_CONNECT_INFO_0 pConnect = NULL;
+
+ nt = ::LlsConnectEnterprise( pszServerName, phLls, 0, (LPBYTE *) &pConnect );
+
+ if ( STATUS_SUCCESS == nt )
+ {
+ ::LlsFreeMemory( pConnect );
+ }
+ }
+
+ if ( STATUS_SUCCESS != nt )
+ {
+ *phLls = NULL;
+ }
+
+ theApp.SetLastLlsError( nt );
+
+ return nt;
+}
+
+
+void CNewLicenseDialog::GetProductList()
+
+/*++
+
+Routine Description:
+
+ Fill the product list box with the unsecure product names from the
+ target server.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ if ( ConnectServer() )
+ {
+ // get list of products from license server, inserting into listbox
+ m_productList.ResetContent();
+
+ DWORD dwResumeHandle = 0;
+ DWORD dwTotalEntries;
+ DWORD dwEntriesRead;
+ NTSTATUS nt;
+
+ do
+ {
+ LPBYTE pReturnBuffer = NULL;
+ BOOL bListProduct;
+
+ nt = ::LlsProductEnum( m_hLls,
+ 0,
+ &pReturnBuffer,
+ 0x4000,
+ &dwEntriesRead,
+ &dwTotalEntries,
+ &dwResumeHandle );
+ theApp.SetLastLlsError( nt );
+
+ if ( ( STATUS_SUCCESS == nt ) || ( STATUS_MORE_ENTRIES == nt ) )
+ {
+ LLS_PRODUCT_INFO_0 * pProductInfo = (LLS_PRODUCT_INFO_0 *) pReturnBuffer;
+
+ for ( DWORD i=0; i < dwEntriesRead; i++ )
+ {
+ if ( !LlsCapabilityIsSupported( m_hLls, LLS_CAPABILITY_SECURE_CERTIFICATES ) )
+ {
+ // 3.51-level server; all products are unsecure, so add all
+ bListProduct = TRUE;
+ }
+ else
+ {
+ // only list this product if it's unsecure
+ BOOL bIsSecure;
+
+ bListProduct = ( STATUS_SUCCESS != ::LlsProductSecurityGet( m_hLls, pProductInfo[i].Product, &bIsSecure ) )
+ || ( !bIsSecure );
+ }
+
+ if ( bListProduct )
+ {
+ m_productList.AddString( pProductInfo[i].Product );
+ }
+
+ ::LlsFreeMemory( pProductInfo[i].Product );
+ }
+
+ ::LlsFreeMemory( pProductInfo );
+ }
+
+ } while ( STATUS_MORE_ENTRIES == nt );
+
+ if ( STATUS_SUCCESS != nt )
+ {
+ // still connected?
+ AbortDialogIfNecessary();
+ }
+
+ // restore previous edit selection
+ UpdateData( FALSE );
+ }
+}
+
+
+NTSTATUS CNewLicenseDialog::AddLicense()
+
+/*++
+
+Routine Description:
+
+ Add the license described in the dialog.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ STATUS_SUCCESS
+ ERROR_CANCELLED
+ ERROR_NOT_ENOUGH_MEMORY
+ NT status code
+ Win error
+
+--*/
+
+{
+ NTSTATUS nt;
+
+ if ( !ConnectServer() )
+ {
+ nt = ERROR_CANCELLED;
+ // don't set last error
+ // (preserve that set by the failed connect attempt)
+ }
+ else
+ {
+ LPTSTR pszProductName = m_strProduct.GetBuffer(0);
+ LPTSTR pszServerName = m_strServerName.GetBuffer(0);
+ LPTSTR pszComment = m_strComment.GetBuffer(0);
+
+ if ( ( NULL == pszProductName ) || ( NULL == pszServerName ) || ( NULL == pszComment ) )
+ {
+ nt = ERROR_NOT_ENOUGH_MEMORY;
+ theApp.SetLastError( nt );
+ }
+ else
+ {
+ LLS_HANDLE hLls = m_hLls;
+
+ if ( ( 0 != m_nLicenseMode )
+ || ( m_dwEnterFlags & CCF_ENTER_FLAG_SERVER_IS_ES ) )
+ {
+ // per server mode, or per seat installed on ES; target server correct
+ nt = STATUS_SUCCESS;
+ }
+ else
+ {
+ // per seat mode; make sure we're installing on the enterprise server
+ PLLS_CONNECT_INFO_0 pConnectInfo = NULL;
+
+ BeginWaitCursor();
+
+ nt = ::LlsEnterpriseServerFind( pszServerName, 0, (LPBYTE *) &pConnectInfo );
+ theApp.SetLastLlsError( nt );
+
+ EndWaitCursor();
+
+ if ( STATUS_SUCCESS == nt )
+ {
+ if ( lstrcmpi( pszServerName, pConnectInfo->EnterpriseServer ) )
+ {
+ // not the enterprise server; make sure that per seat
+ // licenses are being sent to the right place (i.e., the
+ // enterprise server)
+ int nResponse;
+
+ nResponse = AfxMessageBox( IDS_PER_SEAT_CHOSEN_SEND_TO_ENTERPRISE, MB_ICONINFORMATION | MB_OKCANCEL, 0 );
+
+ if ( IDOK == nResponse )
+ {
+ if ( !ConnectEnterprise() )
+ {
+ nt = ERROR_CANCELLED;
+ // don't set last error
+ // (preserve that set by the failed connect attempt)
+ }
+ else
+ {
+ hLls = m_hEnterpriseLls;
+ }
+ }
+ else
+ {
+ nt = ERROR_CANCELLED;
+ theApp.SetLastError( nt );
+ }
+ }
+ }
+
+ // free memory allocated for us by Lls
+ LlsFreeMemory( pConnectInfo );
+ }
+
+ if ( STATUS_SUCCESS == nt )
+ {
+ // we've determined the real target server
+
+ // get name of user entering the certificate
+ TCHAR szUserName[ 64 ];
+ DWORD cchUserName;
+ BOOL ok;
+
+ cchUserName = sizeof( szUserName ) / sizeof( *szUserName );
+ ok = GetUserName( szUserName, &cchUserName );
+
+ if ( !ok )
+ {
+ nt = GetLastError();
+ theApp.SetLastError( nt );
+ }
+ else
+ {
+ // enter certificate into system
+ if ( 0 == m_nLicenseMode )
+ {
+ // add 3.51 style per seat license
+ LLS_LICENSE_INFO_0 lic;
+
+ ZeroMemory( &lic, sizeof( lic ) );
+
+ lic.Product = pszProductName;
+ lic.Comment = pszComment;
+ lic.Admin = szUserName;
+ lic.Quantity = m_nLicenses;
+ lic.Date = 0;
+
+ BeginWaitCursor();
+
+ nt = ::LlsLicenseAdd( hLls, 0, (LPBYTE) &lic );
+ theApp.SetLastLlsError( nt );
+
+ EndWaitCursor();
+ }
+ else
+ {
+ // add 3.51 style per server license (blek)
+ HKEY hKeyLocalMachine;
+
+ nt = RegConnectRegistry( pszServerName, HKEY_LOCAL_MACHINE, &hKeyLocalMachine );
+
+ if ( ERROR_SUCCESS != nt )
+ {
+ theApp.SetLastError( nt );
+ }
+ else
+ {
+ HKEY hKeyLicenseInfo;
+
+ nt = RegOpenKeyEx( hKeyLocalMachine, TEXT( "SYSTEM\\CurrentControlSet\\Services\\LicenseInfo" ), 0, KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS | KEY_SET_VALUE, &hKeyLicenseInfo );
+
+ if ( ERROR_SUCCESS != nt )
+ {
+ theApp.SetLastError( nt );
+ }
+ else
+ {
+ BOOL bFoundKey = FALSE;
+ DWORD iSubKey = 0;
+
+ // okay, now we have to find the product corresponding to this display name (ickie)
+ do
+ {
+ TCHAR szKeyName[ 128 ];
+ DWORD cchKeyName = sizeof( szKeyName ) / sizeof( *szKeyName );
+
+ nt = RegEnumKeyEx( hKeyLicenseInfo, iSubKey++, szKeyName, &cchKeyName, NULL, NULL, NULL, NULL );
+
+ if ( ERROR_SUCCESS == nt )
+ {
+ HKEY hKeyProduct;
+
+ nt = RegOpenKeyEx( hKeyLicenseInfo, szKeyName, 0, KEY_QUERY_VALUE | KEY_SET_VALUE, &hKeyProduct );
+
+ if ( ERROR_SUCCESS == nt )
+ {
+ DWORD dwType;
+ TCHAR szDisplayName[ 128 ];
+ DWORD cbDisplayName = sizeof( szDisplayName );
+
+ nt = RegQueryValueEx( hKeyProduct, TEXT( "DisplayName" ), NULL, &dwType, (LPBYTE) szDisplayName, &cbDisplayName );
+
+ if ( ( ERROR_SUCCESS == nt )
+ && ( REG_SZ == dwType )
+ && !lstrcmpi( szDisplayName, pszProductName ) )
+ {
+ // YES! we found the product key
+ // now add the concurrent licenses
+ bFoundKey = TRUE;
+
+ DWORD dwConcurrentLimit;
+ DWORD cbConcurrentLimit = sizeof( dwConcurrentLimit );
+
+ nt = RegQueryValueEx( hKeyProduct, TEXT( "ConcurrentLimit" ), NULL, &dwType, (LPBYTE) &dwConcurrentLimit, &cbConcurrentLimit );
+
+ if ( ( ERROR_FILE_NOT_FOUND == nt ) || ( ERROR_PATH_NOT_FOUND == nt ) )
+ {
+ // okay if the value doesn't exist
+ dwConcurrentLimit = 0;
+ nt = ERROR_SUCCESS;
+ }
+
+ if ( ERROR_SUCCESS == nt )
+ {
+ if ( (LONG)dwConcurrentLimit + (LONG)m_nLicenses > 0 )
+ {
+ dwConcurrentLimit += m_nLicenses;
+
+ nt = RegSetValueEx( hKeyProduct, TEXT( "ConcurrentLimit" ), 0, REG_DWORD, (LPBYTE) &dwConcurrentLimit, sizeof( dwConcurrentLimit ) );
+ }
+ }
+ }
+
+ RegCloseKey( hKeyProduct );
+ }
+
+ // even if an error occurred while trying to find the right product key,
+ // we should continue to search the rest
+ if ( !bFoundKey )
+ {
+ nt = ERROR_SUCCESS;
+ }
+ }
+ } while ( !bFoundKey && ( ERROR_SUCCESS == nt ) );
+
+ if ( ERROR_NO_MORE_ITEMS == nt )
+ {
+ // trying to install per server licenses for this box, but
+ // the application isn't installed locally
+ AfxMessageBox( IDS_PER_SERVER_APP_NOT_INSTALLED, MB_ICONSTOP | MB_OK, 0 );
+
+ nt = ERROR_CANCELLED;
+ }
+ else if ( ERROR_SUCCESS != nt )
+ {
+ theApp.SetLastError( nt );
+ }
+
+ RegCloseKey( hKeyLicenseInfo );
+ }
+
+ RegCloseKey( hKeyLocalMachine );
+ }
+ }
+ }
+ }
+ }
+
+ if ( NULL != pszProductName ) m_strProduct.ReleaseBuffer();
+ if ( NULL != pszServerName ) m_strServerName.ReleaseBuffer();
+ if ( NULL != pszComment ) m_strComment.ReleaseBuffer();
+ }
+
+ return nt;
+}
+
+
+void CNewLicenseDialog::OnHelp()
+
+/*++
+
+Routine Description:
+
+ Handler for help button click.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ WinHelp( IDD, HELP_CONTEXT );
+}
+
+
+void CNewLicenseDialog::WinHelp(DWORD dwData, UINT nCmd)
+
+/*++
+
+Routine Description:
+
+ Call WinHelp for this dialog.
+
+Arguments:
+
+ dwData (DWORD)
+ nCmd (UINT)
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ BOOL ok = ::WinHelp( m_hWnd, theApp.GetHelpFileName(), nCmd, dwData );
+ ASSERT( ok );
+}
+
+
+void CNewLicenseDialog::OnDestroy()
+
+/*++
+
+Routine Description:
+
+ Handler for WM_DESTROY.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ ::WinHelp( m_hWnd, theApp.GetHelpFileName(), HELP_QUIT, 0 );
+
+ CDialog::OnDestroy();
+}
+
+
+void CNewLicenseDialog::OnPerSeat()
+
+/*++
+
+Routine Description:
+
+ Handler for per seat radio button selection.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ GetDlgItem( IDC_NEW_LICENSE_COMMENT )->EnableWindow( TRUE );
+}
+
+
+void CNewLicenseDialog::OnPerServer()
+
+/*++
+
+Routine Description:
+
+ Handler for per server radio button selection.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ GetDlgItem( IDC_NEW_LICENSE_COMMENT )->EnableWindow( FALSE );
+}
+
+
+DWORD CNewLicenseDialog::CertificateEnter( LPCSTR pszServerName, LPCSTR pszProductName, LPCSTR pszVendor, DWORD dwFlags )
+
+/*++
+
+Routine Description:
+
+ Display a dialog allowing the user to enter a license certificate
+ into the system with no certificate (3.51-style).
+
+Arguments:
+
+ pszServerName (LPCSTR)
+ Name of the server for which licenses are to be installed. Note that
+ this may not be the same as the server on which licenses are actually
+ installed, as, for example, per seat licenses are always installed on
+ the enterprise server. A NULL value indicates the local server.
+ pszProductName (LPCSTR)
+ Product for which licenses are to be installed. A NULL value indicates
+ that the user should be allowed to choose.
+ pszVendor (LPCSTR)
+ Name of the vendor of the product. This value should be NULL if
+ pszProductName is NULL, and should be non-NULL if pszProductName is
+ non-NULL.
+ dwFlags (DWORD)
+ A bitfield containing one or more of the following:
+ CCF_ENTER_FLAG_PER_SEAT_ONLY
+ Allow the user to enter only per seat licenses. Not valid in
+ combination with CCF_ENTER_FLAG_PER_SERVER_ONLY.
+ CCF_ENTER_FLAG_PER_SERVER_ONLY
+ Allow the user to enter only per server licenses. Not valid in
+ combination with CCF_ENTER_FLAG_PER_SEAT_ONLY.
+
+Return Value:
+
+ ERROR_SUCCESS (A certificate was successfully entered into the system.)
+ ERROR_CANCELLED (The user cancelled without installing a certificate.)
+ other Win error
+
+--*/
+
+{
+ DWORD dwError;
+
+ m_strServerName = pszServerName ? pszServerName : "";
+ m_strProduct = pszProductName ? pszProductName : "";
+ // pszVendor is not used
+ m_dwEnterFlags = dwFlags;
+
+ if ( IDOK == DoModal() )
+ {
+ dwError = ERROR_SUCCESS;
+ }
+ else
+ {
+ dwError = ERROR_CANCELLED;
+ }
+
+ return dwError;
+}
+
+
+DWORD CNewLicenseDialog::CertificateRemove( LPCSTR pszServerName, DWORD dwFlags, PLLS_LICENSE_INFO_1 pLicenseInfo )
+
+/*++
+
+Routine Description:
+
+ Remove licenses previously installed via 3.51 or CertificateEnter().
+
+Arguments:
+
+ pszServerName (LPCSTR)
+ Name of the server on which licenses are to be removed. A NULL value
+ indicates the local server.
+ dwFlags (DWORD)
+ Certificate removal options. As of this writing, no flags are
+ supported.
+ dwLicenseLevel (DWORD)
+ Level of the LLS_LICENSE_INFO_X structure pointed to by pvLicenseInfo.
+ pvLicenseInfo (LPVOID)
+ Points to a LLS_LICENSE_INFO_X (where X is determined by dwLicenseLevel)
+ describing the licenses to be removed.
+
+Return Value:
+
+ ERROR_SUCCESS
+ Win error
+
+--*/
+
+{
+ DWORD dwError;
+
+ m_strServerName = pszServerName ? pszServerName : "";
+ m_strProduct = pLicenseInfo->Product;
+
+ if ( !ConnectServer() )
+ {
+ dwError = theApp.GetLastError();
+ // error message already displayed
+ }
+ else
+ {
+ if ( LLS_LICENSE_MODE_ALLOW_PER_SERVER & pLicenseInfo->AllowedModes )
+ {
+ // remove per server licenses
+ HKEY hKeyLocalMachine;
+
+ LPTSTR pszUniServerName = m_strServerName.GetBuffer(0);
+
+ if ( NULL == pszUniServerName )
+ {
+ dwError = ERROR_NOT_ENOUGH_MEMORY;
+ }
+ else
+ {
+ dwError = RegConnectRegistry( pszUniServerName, HKEY_LOCAL_MACHINE, &hKeyLocalMachine );
+
+ if ( ERROR_SUCCESS != dwError )
+ {
+ theApp.SetLastError( dwError );
+ }
+ else
+ {
+ HKEY hKeyLicenseInfo;
+
+ dwError = RegOpenKeyEx( hKeyLocalMachine, TEXT( "SYSTEM\\CurrentControlSet\\Services\\LicenseInfo" ), 0, KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS | KEY_SET_VALUE, &hKeyLicenseInfo );
+
+ if ( ERROR_SUCCESS != dwError )
+ {
+ theApp.SetLastError( dwError );
+ }
+ else
+ {
+ BOOL bFoundKey = FALSE;
+ DWORD iSubKey = 0;
+
+ // okay, now we have to find the product corresponding to this display name (ickie)
+ do
+ {
+ TCHAR szKeyName[ 128 ];
+ DWORD cchKeyName = sizeof( szKeyName ) / sizeof( *szKeyName );
+
+ dwError = RegEnumKeyEx( hKeyLicenseInfo, iSubKey++, szKeyName, &cchKeyName, NULL, NULL, NULL, NULL );
+
+ if ( ERROR_SUCCESS == dwError )
+ {
+ HKEY hKeyProduct;
+
+ dwError = RegOpenKeyEx( hKeyLicenseInfo, szKeyName, 0, KEY_QUERY_VALUE | KEY_SET_VALUE, &hKeyProduct );
+
+ if ( ERROR_SUCCESS == dwError )
+ {
+ DWORD dwType;
+ TCHAR szDisplayName[ 128 ];
+ DWORD cbDisplayName = sizeof( szDisplayName );
+
+ dwError = RegQueryValueEx( hKeyProduct, TEXT( "DisplayName" ), NULL, &dwType, (LPBYTE) szDisplayName, &cbDisplayName );
+
+ if ( ( ERROR_SUCCESS == dwError )
+ && ( REG_SZ == dwType )
+ && !lstrcmpi( szDisplayName, m_strProduct ) )
+ {
+ // YES! we found the product key
+ // now subtract the concurrent licenses
+ bFoundKey = TRUE;
+
+ DWORD dwConcurrentLimit;
+ DWORD cbConcurrentLimit = sizeof( dwConcurrentLimit );
+
+ dwError = RegQueryValueEx( hKeyProduct, TEXT( "ConcurrentLimit" ), NULL, &dwType, (LPBYTE) &dwConcurrentLimit, &cbConcurrentLimit );
+
+ if ( ( ERROR_SUCCESS == dwError ) && ( REG_DWORD == dwType ) )
+ {
+ if ( (LONG)dwConcurrentLimit + (LONG)m_nLicenses > 0 )
+ {
+ if ( pLicenseInfo->Quantity > (LONG)dwConcurrentLimit )
+ {
+ dwConcurrentLimit = 0;
+ }
+ else
+ {
+ dwConcurrentLimit -= pLicenseInfo->Quantity;
+ }
+
+ dwError = RegSetValueEx( hKeyProduct, TEXT( "ConcurrentLimit" ), 0, REG_DWORD, (LPBYTE) &dwConcurrentLimit, sizeof( dwConcurrentLimit ) );
+ }
+ }
+ }
+
+ RegCloseKey( hKeyProduct );
+ }
+
+ // even if an error occurred while trying to find the right product key,
+ // we should continue to search the rest
+ if ( !bFoundKey )
+ {
+ dwError = ERROR_SUCCESS;
+ }
+ }
+ } while ( !bFoundKey && ( ERROR_SUCCESS == dwError ) );
+
+ if ( ERROR_SUCCESS != dwError )
+ {
+ theApp.SetLastError( dwError );
+ }
+
+ RegCloseKey( hKeyLicenseInfo );
+ }
+
+ RegCloseKey( hKeyLocalMachine );
+ }
+ }
+ }
+ else
+ {
+ // remove per seat licenses
+ CString strComment;
+ strComment.LoadString( IDS_NO_REMOVE_COMMENT );
+
+ LPTSTR pszUniProductName = m_strProduct.GetBuffer(0);
+ LPTSTR pszUniComment = strComment.GetBuffer(0);
+
+ if ( ( NULL == pszUniProductName ) || ( NULL == pszUniComment ) )
+ {
+ dwError = ERROR_NOT_ENOUGH_MEMORY;
+ }
+ else
+ {
+ TCHAR szUserName[ 256 ];
+ DWORD cchUserName = sizeof( szUserName ) / sizeof( *szUserName );
+
+ BOOL ok = GetUserName( szUserName, &cchUserName );
+
+ if ( !ok )
+ {
+ dwError = GetLastError();
+ }
+ else
+ {
+ NTSTATUS nt;
+ LLS_LICENSE_INFO_0 lic;
+
+ ZeroMemory( &lic, sizeof( lic ) );
+
+ lic.Product = pszUniProductName;
+ lic.Comment = pszUniComment;
+ lic.Admin = szUserName;
+ lic.Quantity = -pLicenseInfo->Quantity;
+ lic.Date = 0;
+
+ BeginWaitCursor();
+
+ nt = ::LlsLicenseAdd( m_hLls, 0, (LPBYTE) &lic );
+ theApp.SetLastLlsError( nt );
+
+ EndWaitCursor();
+
+ dwError = (DWORD) nt;
+ }
+ }
+
+ if ( NULL != pszUniProductName ) m_strProduct.ReleaseBuffer();
+ if ( NULL != pszUniComment ) strComment.ReleaseBuffer();
+ }
+
+ if ( ( ERROR_SUCCESS != dwError ) && ( ERROR_CANCELLED != dwError ) )
+ {
+ theApp.SetLastError( dwError );
+ theApp.DisplayLastError();
+ }
+ }
+
+ return dwError;
+}
diff --git a/private/net/svcdlls/lls/ccfapi32/nlicdlg.h b/private/net/svcdlls/lls/ccfapi32/nlicdlg.h
new file mode 100644
index 000000000..e45efafda
--- /dev/null
+++ b/private/net/svcdlls/lls/ccfapi32/nlicdlg.h
@@ -0,0 +1,102 @@
+/*++
+
+Copyright (c) 1994-95 Microsoft Corporation
+
+Module Name:
+
+ nlicdlg.h
+
+Abstract:
+
+ New license dialog implementation.
+
+Author:
+
+ Don Ryan (donryan) 02-Feb-1995
+
+Environment:
+
+ User Mode - Win32
+
+Revision History:
+
+ Jeff Parham (jeffparh) 14-Dec-1995
+ Moved over from LLSMGR, added ability to purchase per server licenses,
+ added license removal functionality.
+
+--*/
+
+#ifndef _NLICDLG_H_
+#define _NLICDLG_H_
+
+class CNewLicenseDialog : public CDialog
+{
+private:
+ BOOL m_bAreCtrlsInitialized;
+
+public:
+ CString m_strServerName;
+
+ LLS_HANDLE m_hLls;
+ LLS_HANDLE m_hEnterpriseLls;
+
+ DWORD m_dwEnterFlags;
+
+ //{{AFX_DATA(CNewLicenseDialog)
+ enum { IDD = IDD_NEW_LICENSE };
+ CEdit m_comEdit;
+ CEdit m_licEdit;
+ CSpinButtonCtrl m_spinCtrl;
+ CComboBox m_productList;
+ CString m_strComment;
+ long m_nLicenses;
+ long m_nLicensesMin;
+ CString m_strProduct;
+ int m_nLicenseMode;
+ //}}AFX_DATA
+
+public:
+ CNewLicenseDialog(CWnd* pParent = NULL);
+ ~CNewLicenseDialog();
+
+ // CCF API
+ DWORD CertificateEnter( LPCSTR pszServerName, LPCSTR pszProductName, LPCSTR pszVendor, DWORD dwFlags );
+ DWORD CertificateRemove( LPCSTR pszServerName, DWORD dwFlags, PLLS_LICENSE_INFO_1 pLicenseInfo );
+
+ NTSTATUS ConnectTo( BOOL bUseEnterprise, LPTSTR pszServerName, PLLS_HANDLE phLls );
+ BOOL ConnectServer();
+ BOOL ConnectEnterprise();
+
+ void GetProductList();
+ NTSTATUS AddLicense();
+
+ void AbortDialogIfNecessary();
+ void AbortDialog();
+
+ void InitCtrls();
+ BOOL RefreshCtrls();
+
+ BOOL IsQuantityValid();
+
+ //{{AFX_VIRTUAL(CNewLicenseDialog)
+ public:
+ virtual void WinHelp(DWORD dwData, UINT nCmd = HELP_CONTEXT);
+ protected:
+ virtual void DoDataExchange(CDataExchange* pDX);
+ //}}AFX_VIRTUAL
+
+protected:
+ //{{AFX_MSG(CNewLicenseDialog)
+ virtual void OnOK();
+ virtual BOOL OnInitDialog();
+ afx_msg void OnDestroy();
+ afx_msg void OnDeltaPosSpin(NMHDR* pNMHDR, LRESULT* pResult);
+ afx_msg void OnUpdateQuantity();
+ afx_msg void OnHelp();
+ afx_msg void OnPerSeat();
+ afx_msg void OnPerServer();
+ //}}AFX_MSG
+ DECLARE_MESSAGE_MAP()
+};
+
+#endif // _NLICDLG_H_
diff --git a/private/net/svcdlls/lls/ccfapi32/paper.cpp b/private/net/svcdlls/lls/ccfapi32/paper.cpp
new file mode 100644
index 000000000..919db7d6c
--- /dev/null
+++ b/private/net/svcdlls/lls/ccfapi32/paper.cpp
@@ -0,0 +1,1389 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ paper.cpp
+
+Abstract:
+
+ Paper certificate dialog implementation.
+
+Author:
+
+ Jeff Parham (jeffparh) 13-Dec-1995
+
+Revision History:
+
+--*/
+
+
+#include "stdafx.h"
+#include <stdlib.h>
+
+#include "resource.h"
+#include "ccfapi.h"
+#include "paper.h"
+#include "md4.h"
+
+#ifdef _DEBUG
+#undef THIS_FILE
+static char BASED_CODE THIS_FILE[] = __FILE__;
+#endif
+
+
+static void MD4UpdateDword( MD4_CTX * pCtx, DWORD dwValue );
+
+
+CPaperSourceDlg::CPaperSourceDlg(CWnd* pParent /*=NULL*/)
+ : CDialog(CPaperSourceDlg::IDD, pParent)
+
+/*++
+
+Routine Description:
+
+ Constructor for dialog.
+
+Arguments:
+
+ pParent - owner window.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ //{{AFX_DATA_INIT(CPaperSourceDlg)
+ m_strActivationCode = _T("");
+ m_strKeyCode = _T("");
+ m_strSerialNumber = _T("");
+ m_strVendor = _T("");
+ m_strProductName = _T("");
+ m_strComment = _T("");
+ m_nDontInstallAllLicenses = -1;
+ m_nLicenses = 0;
+ m_nLicenseMode = -1;
+ //}}AFX_DATA_INIT
+
+ m_bProductListRetrieved = FALSE;
+ m_hLls = NULL;
+ m_hEnterpriseLls = NULL;
+ m_dwEnterFlags = 0;
+
+ m_nLicenses = 1;
+ m_nDontInstallAllLicenses = 0;
+
+ m_strServerName = _T("");
+}
+
+
+CPaperSourceDlg::~CPaperSourceDlg()
+
+/*++
+
+Routine Description:
+
+ Destructor for dialog.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ if ( NULL != m_hLls )
+ {
+ LlsClose( m_hLls );
+ }
+
+ if ( NULL != m_hEnterpriseLls )
+ {
+ LlsClose( m_hEnterpriseLls );
+ }
+}
+
+
+void CPaperSourceDlg::DoDataExchange(CDataExchange* pDX)
+
+/*++
+
+Routine Description:
+
+ Called by framework to exchange dialog data.
+
+Arguments:
+
+ pDX - data exchange object.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ CDialog::DoDataExchange(pDX);
+ //{{AFX_DATA_MAP(CPaperSourceDlg)
+ DDX_Control(pDX, IDC_SPIN_LICENSES, m_spinLicenses);
+ DDX_Control(pDX, IDC_PRODUCT_NAME, m_cboxProductName);
+ DDX_Text(pDX, IDC_ACTIVATION_CODE, m_strActivationCode);
+ DDX_Text(pDX, IDC_KEY_CODE, m_strKeyCode);
+ DDX_Text(pDX, IDC_SERIAL_NUMBER, m_strSerialNumber);
+ DDX_Text(pDX, IDC_VENDOR, m_strVendor);
+ DDX_CBString(pDX, IDC_PRODUCT_NAME, m_strProductName);
+ DDX_Text(pDX, IDC_COMMENT, m_strComment);
+ DDX_Radio(pDX, IDC_ALL_LICENSES, m_nDontInstallAllLicenses);
+ DDX_Text(pDX, IDC_NUM_LICENSES, m_nLicenses);
+ DDX_Radio(pDX, IDC_PER_SEAT, m_nLicenseMode);
+ //}}AFX_DATA_MAP
+}
+
+
+BEGIN_MESSAGE_MAP(CPaperSourceDlg, CDialog)
+ //{{AFX_MSG_MAP(CPaperSourceDlg)
+ ON_EN_UPDATE(IDC_ACTIVATION_CODE, OnUpdateActivationCode)
+ ON_EN_UPDATE(IDC_KEY_CODE, OnUpdateKeyCode)
+ ON_EN_UPDATE(IDC_VENDOR, OnUpdateVendor)
+ ON_EN_UPDATE(IDC_SERIAL_NUMBER, OnUpdateSerialNumber)
+ ON_CBN_EDITUPDATE(IDC_PRODUCT_NAME, OnUpdateProductName)
+ ON_CBN_DROPDOWN(IDC_PRODUCT_NAME, OnDropDownProductName)
+ ON_BN_CLICKED(IDC_MY_HELP, OnHelp)
+ ON_WM_DESTROY()
+ ON_BN_CLICKED(IDC_ALL_LICENSES, OnAllLicenses)
+ ON_BN_CLICKED(IDC_SOME_LICENSES, OnSomeLicenses)
+ ON_NOTIFY(UDN_DELTAPOS, IDC_SPIN_LICENSES, OnDeltaPosSpinLicenses)
+ //}}AFX_MSG_MAP
+END_MESSAGE_MAP()
+
+
+void CPaperSourceDlg::OnUpdateActivationCode()
+
+/*++
+
+Routine Description:
+
+ Message handler for EN_UPDATE of activation code.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ EnableOrDisableOK();
+}
+
+
+void CPaperSourceDlg::OnUpdateKeyCode()
+
+/*++
+
+Routine Description:
+
+ Message handler for EN_UPDATE of key code.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ EnableOrDisableOK();
+}
+
+
+void CPaperSourceDlg::OnUpdateProductName()
+
+/*++
+
+Routine Description:
+
+ Message handler for EN_UPDATE of product name.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ EnableOrDisableOK();
+}
+
+
+void CPaperSourceDlg::OnUpdateSerialNumber()
+
+/*++
+
+Routine Description:
+
+ Message handler for EN_UPDATE of serial number.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ EnableOrDisableOK();
+}
+
+
+void CPaperSourceDlg::OnUpdateVendor()
+
+/*++
+
+Routine Description:
+
+ Message handler for EN_UPDATE of vendor.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ EnableOrDisableOK();
+}
+
+
+void CPaperSourceDlg::EnableOrDisableOK()
+
+/*++
+
+Routine Description:
+
+ Enable or diable OK button depending upon whether all necessary dialog data
+ has been supplied by the user.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ BOOL bEnableOK;
+
+ UpdateData( TRUE );
+
+ bEnableOK = !m_strActivationCode.IsEmpty()
+ && !m_strKeyCode.IsEmpty()
+ && !m_strProductName.IsEmpty()
+ && !m_strSerialNumber.IsEmpty()
+ && !m_strVendor.IsEmpty()
+ && ( ( 0 == m_nLicenseMode )
+ || ( 1 == m_nLicenseMode ) );
+
+ GetDlgItem( IDOK )->EnableWindow( bEnableOK );
+}
+
+
+BOOL CPaperSourceDlg::OnInitDialog()
+
+/*++
+
+Routine Description:
+
+ Handler for WM_INITDIALOG.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ Returns false if focus set manually.
+
+--*/
+
+{
+ CDialog::OnInitDialog();
+
+ EnableOrDisableOK();
+
+ if ( m_nDontInstallAllLicenses )
+ {
+ OnSomeLicenses();
+ }
+ else
+ {
+ OnAllLicenses();
+ }
+
+ m_spinLicenses.SetRange( 1, MAX_NUM_LICENSES );
+
+ // ghost out items that were passed to us from the application
+ if ( !m_strProductName.IsEmpty() )
+ GetDlgItem( IDC_PRODUCT_NAME )->EnableWindow( FALSE );
+ if ( !m_strVendor.IsEmpty() )
+ GetDlgItem( IDC_VENDOR )->EnableWindow( FALSE );
+
+ // if license mode set by application, don't let user change it
+ if ( m_dwEnterFlags & ( CCF_ENTER_FLAG_PER_SEAT_ONLY | CCF_ENTER_FLAG_PER_SERVER_ONLY ) )
+ {
+ m_nLicenseMode = ( m_dwEnterFlags & CCF_ENTER_FLAG_PER_SEAT_ONLY ) ? 0 : 1;
+ GetDlgItem( IDC_PER_SERVER )->EnableWindow( FALSE );
+ GetDlgItem( IDC_PER_SEAT )->EnableWindow( FALSE );
+ UpdateData( FALSE );
+ }
+
+ return TRUE;
+}
+
+
+void CPaperSourceDlg::OnOK()
+
+/*++
+
+Routine Description:
+
+ Creates a new license for product.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS nt;
+
+ if( UpdateData( TRUE ) )
+ {
+ // verify activation code
+ DWORD nActivationCode = wcstoul( m_strActivationCode, NULL, 16 );
+
+ if ( nActivationCode != ComputeActivationCode() )
+ {
+ AfxMessageBox( IDS_BAD_ACTIVATION_CODE, MB_ICONEXCLAMATION | MB_OK, 0 );
+ }
+ else if ( ( m_nLicenses < 1 ) || ( m_nLicenses > MAX_NUM_LICENSES ) )
+ {
+ AfxMessageBox( IDS_INVALID_NUM_LICENSES, MB_ICONEXCLAMATION | MB_OK, 0 );
+
+ GetDlgItem( IDC_NUM_LICENSES )->SetActiveWindow();
+ }
+ else
+ {
+ DWORD dwKeyCode = KEY_CODE_MASK ^ wcstoul( m_strKeyCode, NULL, 10 );
+
+ if ( m_nDontInstallAllLicenses
+ && ( (DWORD)m_nLicenses > KeyCodeToNumLicenses( dwKeyCode ) ) )
+ {
+ // can't install more licenses than are in the certificate
+ AfxMessageBox( IDS_NOT_ENOUGH_LICENSES_ON_CERTIFICATE, MB_ICONEXCLAMATION | MB_OK, 0 );
+
+ GetDlgItem( IDC_NUM_LICENSES )->SetActiveWindow();
+ }
+ else if ( !( ( 1 << m_nLicenseMode ) & KeyCodeToModesAllowed( dwKeyCode ) ) )
+ {
+ // can't install certificate in a mode that's not allowed by the key code
+ AfxMessageBox( IDS_LICENSE_MODE_NOT_ALLOWED, MB_ICONEXCLAMATION | MB_OK, 0 );
+
+ GetDlgItem( IDC_PER_SEAT )->SetActiveWindow();
+ }
+ else
+ {
+ nt = AddLicense();
+
+ if ( STATUS_SUCCESS == nt )
+ {
+ CDialog::OnOK();
+ }
+ else if ( ( ERROR_CANCELLED != nt ) && ( STATUS_CANCELLED != nt ) )
+ {
+ AbortDialogIfNecessary();
+ }
+ }
+ }
+ }
+}
+
+
+void CPaperSourceDlg::GetProductList()
+
+/*++
+
+Routine Description:
+
+ Retrieves the list of installed product from the license server.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ if ( ConnectServer() )
+ {
+ // save edit selection
+ UpdateData( TRUE );
+
+ // get list of products from license server, inserting into listbox
+ m_cboxProductName.ResetContent();
+
+ DWORD dwResumeHandle = 0;
+ DWORD dwTotalEntries;
+ DWORD dwEntriesRead;
+ NTSTATUS nt;
+
+ do
+ {
+ LPBYTE pReturnBuffer = NULL;
+
+ nt = ::LlsProductEnum( m_hLls,
+ 0,
+ &pReturnBuffer,
+ LLS_PREFERRED_LENGTH,
+ &dwEntriesRead,
+ &dwTotalEntries,
+ &dwResumeHandle );
+ theApp.SetLastLlsError( nt );
+
+ if ( ( STATUS_SUCCESS == nt ) || ( STATUS_MORE_ENTRIES == nt ) )
+ {
+ LLS_PRODUCT_INFO_0 * pProductInfo = (LLS_PRODUCT_INFO_0 *) pReturnBuffer;
+
+ for ( DWORD i=0; i < dwEntriesRead; i++ )
+ {
+ m_cboxProductName.AddString( pProductInfo[i].Product );
+
+ ::LlsFreeMemory( pProductInfo->Product );
+ }
+
+ ::LlsFreeMemory( pProductInfo );
+ }
+
+ } while ( STATUS_MORE_ENTRIES == nt );
+
+ if ( STATUS_SUCCESS != nt )
+ {
+ // still connected?
+ AbortDialogIfNecessary();
+ }
+
+ // restore previous edit selection
+ UpdateData( FALSE );
+ }
+}
+
+
+void CPaperSourceDlg::OnDropDownProductName()
+
+/*++
+
+Routine Description:
+
+ Handler for CBN_DROPDOWN of product name combo box.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ if ( !m_bProductListRetrieved )
+ {
+ BeginWaitCursor();
+ GetProductList();
+ EndWaitCursor();
+
+ m_bProductListRetrieved = TRUE;
+ }
+}
+
+
+BOOL CPaperSourceDlg::ConnectServer()
+
+/*++
+
+Routine Description:
+
+ Establish a connection to the license service on the target server.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ BOOL.
+
+--*/
+
+{
+ if ( NULL == m_hLls )
+ {
+ ConnectTo( FALSE, m_strServerName, &m_hLls );
+
+ if ( !LlsCapabilityIsSupported( m_hLls, LLS_CAPABILITY_SECURE_CERTIFICATES ) )
+ {
+ // we connected to the machine, but it doesn't support secure certificates
+ // we should not get here under normal circumstances, since the select
+ // source dialog should not allow the user to choose the paper source
+ // under such circumstances
+ LlsClose( m_hLls );
+ m_hLls = NULL;
+
+ theApp.SetLastLlsError( STATUS_INVALID_LEVEL );
+ }
+
+ if ( NULL == m_hLls )
+ {
+ theApp.DisplayLastError();
+ EndDialog( IDABORT );
+ }
+ }
+
+ return ( NULL != m_hLls );
+}
+
+
+BOOL CPaperSourceDlg::ConnectEnterprise()
+
+/*++
+
+Routine Description:
+
+ Establish a connection to the license service on the enterprise server
+ of the target server.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ BOOL.
+
+--*/
+
+{
+ if ( NULL == m_hEnterpriseLls )
+ {
+ ConnectTo( !( m_dwEnterFlags & CCF_ENTER_FLAG_SERVER_IS_ES ), m_strServerName, &m_hEnterpriseLls );
+
+ if ( NULL == m_hEnterpriseLls )
+ {
+ theApp.DisplayLastError();
+ }
+ }
+
+ return ( NULL != m_hEnterpriseLls );
+}
+
+
+NTSTATUS CPaperSourceDlg::ConnectTo( BOOL bUseEnterprise, CString strServerName, PLLS_HANDLE phLls )
+
+/*++
+
+Routine Description:
+
+ Establish a connection to the license service on the given server or that
+ on the given server's enterprise server.
+
+Arguments:
+
+ bUseEnterprise (BOOL)
+ If TRUE, connect to the enterprise server of the target server, not to
+ the target server itself.
+ pszServerName (CString)
+ The target server. An empty value indicates the local server.
+ phLls (PLLS_HANDLE)
+ On return, holds the handle to the standard LLS RPC.
+
+Return Values:
+
+ STATUS_SUCCESS or NT status code.
+
+--*/
+
+{
+ NTSTATUS nt = STATUS_SUCCESS;
+ LPTSTR pszServerName = NULL;
+
+ if ( !strServerName.IsEmpty() )
+ {
+ pszServerName = strServerName.GetBuffer(0);
+
+ if ( NULL == pszServerName )
+ {
+ nt = ERROR_NOT_ENOUGH_MEMORY;
+ }
+ }
+
+ if ( STATUS_SUCCESS == nt )
+ {
+ if ( !bUseEnterprise )
+ {
+ nt = ::LlsConnect( pszServerName, phLls );
+ }
+ else
+ {
+ PLLS_CONNECT_INFO_0 pConnect = NULL;
+
+ nt = ::LlsConnectEnterprise( pszServerName, phLls, 0, (LPBYTE *) &pConnect );
+
+ if ( STATUS_SUCCESS == nt )
+ {
+ ::LlsFreeMemory( pConnect );
+ }
+ }
+
+ theApp.SetLastLlsError( nt );
+ }
+
+ if ( STATUS_SUCCESS != nt )
+ {
+ *phLls = NULL;
+ }
+
+ return nt;
+}
+
+
+void CPaperSourceDlg::OnHelp()
+
+/*++
+
+Routine Description:
+
+ Handler for help button click.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ WinHelp( IDD, HELP_CONTEXT );
+}
+
+
+void CPaperSourceDlg::WinHelp(DWORD dwData, UINT nCmd)
+
+/*++
+
+Routine Description:
+
+ Call WinHelp for this dialog.
+
+Arguments:
+
+ dwData (DWORD)
+ nCmd (UINT)
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ BOOL ok = ::WinHelp( m_hWnd, theApp.GetHelpFileName(), nCmd, dwData );
+ ASSERT( ok );
+}
+
+
+void CPaperSourceDlg::OnDestroy()
+
+/*++
+
+Routine Description:
+
+ Handler for WM_DESTROY.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ ::WinHelp( m_hWnd, theApp.GetHelpFileName(), HELP_QUIT, 0 );
+
+ CDialog::OnDestroy();
+}
+
+
+void CPaperSourceDlg::AbortDialogIfNecessary()
+
+/*++
+
+Routine Description:
+
+ Displays status and aborts if connection lost.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ theApp.DisplayLastError();
+
+ if ( theApp.IsConnectionDropped() )
+ {
+ EndDialog( IDABORT );
+ }
+}
+
+
+DWORD CPaperSourceDlg::ComputeActivationCode()
+
+/*++
+
+Routine Description:
+
+ Return the computed activation code corresponding to the entered
+ certificate.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ DWORD.
+
+--*/
+
+{
+ MD4_CTX ctx;
+ DWORD dw;
+ UCHAR digest[ 16 ];
+ DWORD adwCodeSeg[ sizeof( digest ) / sizeof( DWORD ) ];
+ int nCodeSeg;
+ int nCodeSegByte;
+ DWORD dwActivationCode ;
+ CHAR szAnsiName[ 128 ];
+
+ MD4Init( &ctx );
+
+ ZeroMemory( szAnsiName, sizeof( szAnsiName ) );
+ wcstombs( szAnsiName, m_strProductName, sizeof( szAnsiName ) - 1 );
+ MD4Update( &ctx, (LPBYTE)szAnsiName, strlen( szAnsiName ) );
+
+ ZeroMemory( szAnsiName, sizeof( szAnsiName ) );
+ wcstombs( szAnsiName, m_strVendor, sizeof( szAnsiName ) - 1 );
+ MD4Update( &ctx, (LPBYTE)szAnsiName, strlen( szAnsiName ) );
+
+ MD4UpdateDword( &ctx, 14721776 );
+ MD4UpdateDword( &ctx, wcstoul( m_strSerialNumber, NULL, 10 ) );
+ MD4UpdateDword( &ctx, 19721995 );
+ MD4UpdateDword( &ctx, KEY_CODE_MASK ^ wcstoul( m_strKeyCode, NULL, 10 ) );
+
+ MD4Final( digest, &ctx );
+
+ // convert digest into platform-independent array of DWORDs
+ for ( nCodeSeg=0; nCodeSeg < sizeof( adwCodeSeg ) / sizeof( *adwCodeSeg ); nCodeSeg++ )
+ {
+ adwCodeSeg[ nCodeSeg ] = 0;
+
+ for ( nCodeSegByte=0; nCodeSegByte < sizeof( *adwCodeSeg ); nCodeSegByte++ )
+ {
+ adwCodeSeg[ nCodeSeg ] <<= 8;
+ adwCodeSeg[ nCodeSeg ] |= digest[ nCodeSeg * sizeof( *adwCodeSeg ) + nCodeSegByte ];
+ }
+ }
+
+ dwActivationCode = ( adwCodeSeg[ 0 ] + adwCodeSeg[ 1 ] ) ^ ( adwCodeSeg[ 2 ] - adwCodeSeg[ 3 ] );
+
+ return dwActivationCode;
+}
+
+
+NTSTATUS CPaperSourceDlg::AddLicense()
+
+/*++
+
+Routine Description:
+
+ Enter a new license into the system.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ STATUS_SUCCESS
+ ERROR_NOT_ENOUGH_MEMORY
+ ERROR_CANCELLED
+ NT status code
+ Win error
+
+--*/
+
+{
+ NTSTATUS nt;
+
+ if ( !ConnectServer() )
+ {
+ nt = ERROR_CANCELLED;
+ }
+ else
+ {
+ LPTSTR pszProductName = m_strProductName.GetBuffer(0);
+ LPTSTR pszServerName = m_strServerName.GetBuffer(0);
+ LPTSTR pszComment = m_strComment.GetBuffer(0);
+ LPTSTR pszVendor = m_strVendor.GetBuffer(0);
+
+ if ( ( NULL == pszProductName ) || ( NULL == pszServerName ) || ( NULL == pszComment ) || ( NULL == pszVendor ) )
+ {
+ nt = ERROR_NOT_ENOUGH_MEMORY;
+ }
+ else
+ {
+ // handles for LLS on machine to receive licenses
+ // (if per seat, these will be changed to correspond to the enterprise server)
+ LLS_HANDLE hLls = NULL;
+
+ if ( 0 == m_nLicenseMode )
+ {
+ // per seat mode; install on enterprise server
+ BeginWaitCursor();
+ BOOL ok = ConnectEnterprise();
+ EndWaitCursor();
+
+ if ( !ok )
+ {
+ // can't connect to enterprise server
+ nt = ERROR_CANCELLED;
+ }
+ else if ( !LlsCapabilityIsSupported( m_hEnterpriseLls, LLS_CAPABILITY_SECURE_CERTIFICATES ) )
+ {
+ // enterprise server doesn't support secure certificates
+ AfxMessageBox( IDS_ENTERPRISE_SERVER_BACKLEVEL_CANT_ADD_CERT, MB_ICONSTOP | MB_OK, 0 );
+ nt = ERROR_CANCELLED;
+ }
+ else
+ {
+ hLls = m_hEnterpriseLls;
+ nt = STATUS_SUCCESS;
+ }
+ }
+ else
+ {
+ // per server mode; install on target server
+ hLls = m_hLls;
+ nt = STATUS_SUCCESS;
+ }
+
+ if ( STATUS_SUCCESS == nt )
+ {
+ TCHAR szUserName[ 64 ];
+ DWORD cchUserName;
+ BOOL ok;
+
+ cchUserName = sizeof( szUserName ) / sizeof( *szUserName );
+ ok = GetUserName( szUserName, &cchUserName );
+
+ if ( !ok )
+ {
+ nt = GetLastError();
+ }
+ else
+ {
+ // enter certificate into system
+ DWORD nKeyCode = KEY_CODE_MASK ^ wcstoul( m_strKeyCode, NULL, 10 );
+
+ // --------- fill in certificate info ---------
+ LLS_LICENSE_INFO_1 lic;
+
+ ZeroMemory( &lic, sizeof( lic ) );
+
+ lic.Product = pszProductName;
+ lic.Vendor = pszVendor;
+ lic.Comment = pszComment;
+ lic.Admin = szUserName;
+ lic.Quantity = m_nDontInstallAllLicenses ? m_nLicenses : KeyCodeToNumLicenses( nKeyCode );
+ lic.Date = 0;
+ lic.AllowedModes = 1 << m_nLicenseMode;
+ lic.CertificateID = wcstoul( m_strSerialNumber, NULL, 10 );
+ lic.Source = TEXT("Paper");
+ lic.ExpirationDate = KeyCodeToExpirationDate( nKeyCode );
+ lic.MaxQuantity = KeyCodeToNumLicenses( nKeyCode );
+
+ BeginWaitCursor();
+
+ nt = ::LlsLicenseAdd( hLls, 1, (LPBYTE) &lic );
+ theApp.SetLastLlsError( nt );
+
+ EndWaitCursor();
+
+ if ( ( STATUS_OBJECT_NAME_EXISTS == nt )
+ || ( STATUS_ALREADY_COMMITTED == nt ) )
+ {
+ LLS_HANDLE hLlsForTargets = NULL;
+
+ // too many licenses of this certificate
+ if ( STATUS_OBJECT_NAME_EXISTS == nt )
+ {
+ // denied by target's local database
+ hLlsForTargets = hLls;
+ }
+ else if ( ConnectEnterprise() )
+ {
+ // denied by target's enterprise server; we're connected!
+ hLlsForTargets = m_hEnterpriseLls;
+ }
+
+ if ( NULL == hLlsForTargets )
+ {
+ // denied by enterprise server, and can't connect to it (?!)
+ AfxMessageBox( IDS_NET_LICENSES_ALREADY_INSTALLED, MB_ICONSTOP | MB_OK, 0 );
+ }
+ else
+ {
+ // too many licenses of this certificate exist in the enterprise
+ LPBYTE ReturnBuffer = NULL;
+ DWORD dwNumTargets = 0;
+
+ // get list of machines on which licenses from this certificate have been installed
+ nt = ::LlsCertificateClaimEnum( hLlsForTargets, 1, (LPBYTE) &lic, 0, &ReturnBuffer, &dwNumTargets );
+
+ if ( ( STATUS_SUCCESS == nt ) && ( dwNumTargets > 0 ) )
+ {
+ PLLS_CERTIFICATE_CLAIM_INFO_0 pTarget = (PLLS_CERTIFICATE_CLAIM_INFO_0) ReturnBuffer;
+ CString strLicenses;
+ CString strTarget;
+ CString strTargetList;
+
+ while ( dwNumTargets-- )
+ {
+ strLicenses.Format( TEXT("%d"), pTarget->Quantity );
+ AfxFormatString2( strTarget, IDS_NET_CERTIFICATE_TARGET_ENTRY, pTarget->ServerName, strLicenses );
+
+ strTargetList = strTargetList.IsEmpty() ? strTarget : ( TEXT("\n") + strTarget );
+
+ pTarget++;
+ }
+
+ CString strMessage;
+
+ AfxFormatString1( strMessage, IDS_NET_LICENSES_ALREADY_INSTALLED_ON, strTargetList );
+ AfxMessageBox( strMessage, MB_ICONSTOP | MB_OK );
+ }
+ else
+ {
+ AfxMessageBox( IDS_NET_LICENSES_ALREADY_INSTALLED, MB_ICONSTOP | MB_OK, 0 );
+ }
+
+ if ( STATUS_SUCCESS == nt )
+ {
+ ::LlsFreeMemory( ReturnBuffer );
+ }
+ }
+
+ nt = ERROR_CANCELLED;
+ }
+ }
+ }
+ }
+
+ // don't set if !ConnectServer() -- otherwise we'll clobber the LLS error
+ theApp.SetLastError( nt );
+
+ if ( NULL != pszProductName )
+ m_strProductName.ReleaseBuffer();
+ if ( NULL != pszServerName )
+ m_strServerName.ReleaseBuffer();
+ if ( NULL != pszComment )
+ m_strComment.ReleaseBuffer();
+ if ( NULL != pszVendor )
+ m_strVendor.ReleaseBuffer();
+ }
+
+ return nt;
+}
+
+
+DWORD CPaperSourceDlg::KeyCodeToExpirationDate( DWORD dwKeyCode )
+
+/*++
+
+Routine Description:
+
+ Derive the license expiration date from the key code.
+
+Arguments:
+
+ dwKeyCode (DWORD)
+
+Return Values:
+
+ DWORD.
+
+--*/
+
+{
+ DWORD dwExpirationDate = 0;
+ USHORT usWinDate = (USHORT)( ( dwKeyCode >> 12 ) & 0x0000FFFF );
+
+ if ( 0 != usWinDate )
+ {
+ TIME_FIELDS tf;
+ LARGE_INTEGER li;
+
+ ZeroMemory( &tf, sizeof( tf ) );
+
+ tf.Year = 1980 + ( usWinDate & 0x7F );
+ tf.Month = ( ( usWinDate >> 7 ) & 0x0F );
+ tf.Day = ( usWinDate >> 11 );
+ tf.Hour = 23;
+ tf.Minute = 59;
+ tf.Second = 59;
+
+ BOOL ok;
+
+ ok = RtlTimeFieldsToTime( &tf, &li );
+ ASSERT( ok );
+
+ if ( ok )
+ {
+ ok = RtlTimeToSecondsSince1980( &li, &dwExpirationDate );
+ ASSERT( ok );
+ }
+ }
+
+ return dwExpirationDate;
+}
+
+
+void CPaperSourceDlg::OnAllLicenses()
+
+/*++
+
+Routine Description:
+
+ Handler for BN_CLICKED of "install all licenses".
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ GetDlgItem( IDC_NUM_LICENSES )->EnableWindow( FALSE );
+ GetDlgItem( IDC_SPIN_LICENSES )->EnableWindow( FALSE );
+}
+
+
+void CPaperSourceDlg::OnSomeLicenses()
+
+/*++
+
+Routine Description:
+
+ Handler for BN_CLICKED of "install only x licenses".
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ GetDlgItem( IDC_NUM_LICENSES )->EnableWindow( TRUE );
+ GetDlgItem( IDC_SPIN_LICENSES )->EnableWindow( TRUE );
+}
+
+
+void CPaperSourceDlg::OnDeltaPosSpinLicenses(NMHDR* pNMHDR, LRESULT* pResult)
+
+/*++
+
+Routine Description:
+
+ Handler for UDN_DELTAPOS of number of licenses.
+
+Arguments:
+
+ pNMHDR (NMHDR*)
+ pResult (LRESULT*)
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ if ( UpdateData(TRUE) ) // get data
+ {
+ m_nLicenses += ((NM_UPDOWN*)pNMHDR)->iDelta;
+
+ if (m_nLicenses < 1)
+ {
+ m_nLicenses = 1;
+
+ ::MessageBeep(MB_OK);
+ }
+ else if (m_nLicenses > MAX_NUM_LICENSES )
+ {
+ m_nLicenses = MAX_NUM_LICENSES;
+
+ ::MessageBeep(MB_OK);
+ }
+
+ UpdateData(FALSE); // set data
+ }
+
+ *pResult = 1; // handle ourselves...
+}
+
+
+DWORD CPaperSourceDlg::CertificateEnter( LPCSTR pszServerName, LPCSTR pszProductName, LPCSTR pszVendor, DWORD dwFlags )
+
+/*++
+
+Routine Description:
+
+ Display a dialog allowing the user to enter a license certificate
+ into the system with a paper certificate.
+
+Arguments:
+
+ pszServerName (LPCSTR)
+ Name of the server for which licenses are to be installed. Note that
+ this may not be the same as the server on which licenses are actually
+ installed, as, for example, per seat licenses are always installed on
+ the enterprise server. A NULL value indicates the local server.
+ pszProductName (LPCSTR)
+ Product for which licenses are to be installed. A NULL value indicates
+ that the user should be allowed to choose.
+ pszVendor (LPCSTR)
+ Name of the vendor of the product. This value should be NULL if
+ pszProductName is NULL, and should be non-NULL if pszProductName is
+ non-NULL.
+ dwFlags (DWORD)
+ A bitfield containing one or more of the following:
+ CCF_ENTER_FLAG_PER_SEAT_ONLY
+ Allow the user to enter only per seat licenses. Not valid in
+ combination with CCF_ENTER_FLAG_PER_SERVER_ONLY.
+ CCF_ENTER_FLAG_PER_SERVER_ONLY
+ Allow the user to enter only per server licenses. Not valid in
+ combination with CCF_ENTER_FLAG_PER_SEAT_ONLY.
+
+Return Value:
+
+ ERROR_SUCCESS (A certificate was successfully entered into the system.)
+ ERROR_CANCELLED (The user cancelled without installing a certificate.)
+ other Win error
+
+--*/
+
+{
+ DWORD dwError;
+
+ m_strServerName = pszServerName ? pszServerName : "";
+ m_strProductName = pszProductName ? pszProductName : "";
+ m_strVendor = pszVendor ? pszVendor : "";
+ m_dwEnterFlags = dwFlags;
+
+ if ( IDOK == DoModal() )
+ {
+ dwError = ERROR_SUCCESS;
+ }
+ else
+ {
+ dwError = ERROR_CANCELLED;
+ }
+
+ return dwError;
+}
+
+
+DWORD CPaperSourceDlg::CertificateRemove( LPCSTR pszServerName, DWORD dwFlags, PLLS_LICENSE_INFO_1 pLicenseInfo )
+
+/*++
+
+Routine Description:
+
+ Remove licenses previously installed via PaperCertificateEnter().
+
+Arguments:
+
+ hWndParent (HWND)
+ HWND to the client's main window, for use as the parent window to any
+ opened dialogs. May be NULL.
+ pszServerName (LPCSTR)
+ Name of the server on which licenses are to be removed. A NULL value
+ indicates the local server.
+ dwFlags (DWORD)
+ Certificate removal options. As of this writing, no flags are
+ supported.
+ dwLicenseLevel (DWORD)
+ Level of the LLS_LICENSE_INFO_X structure pointed to by pvLicenseInfo.
+ pvLicenseInfo (LPVOID)
+ Points to a LLS_LICENSE_INFO_X (where X is determined by dwLicenseLevel)
+ describing the licenses to be removed.
+
+Return Value:
+
+ ERROR_SUCCESS
+ Win error
+
+--*/
+
+{
+ DWORD dwError;
+
+ // dwFlags unused
+
+ m_strServerName = pszServerName ? pszServerName : "";
+ m_strProductName = pLicenseInfo->Product;
+
+ if ( !ConnectServer() )
+ {
+ dwError = theApp.GetLastError();
+ // error message already displayed
+ }
+ else
+ {
+ CString strComment;
+ strComment.LoadString( IDS_PAPER_REMOVE_COMMENT );
+
+ LPTSTR pszComment = strComment.GetBuffer(0);
+
+ if ( NULL == pszComment )
+ {
+ dwError = ERROR_NOT_ENOUGH_MEMORY;
+ }
+ else
+ {
+ TCHAR szUserName[ 256 ];
+ DWORD cchUserName = sizeof( szUserName ) / sizeof( *szUserName );
+
+ BOOL ok = GetUserName( szUserName, &cchUserName );
+
+ if ( !ok )
+ {
+ dwError = GetLastError();
+ }
+ else
+ {
+ NTSTATUS nt;
+ LLS_LICENSE_INFO_1 lic;
+
+ memcpy( &lic, pLicenseInfo, sizeof( lic ) );
+ lic.Admin = szUserName;
+ lic.Comment = pszComment;
+ lic.Date = 0;
+ lic.Quantity = -pLicenseInfo->Quantity;
+
+ BeginWaitCursor();
+
+ nt = ::LlsLicenseAdd( m_hLls, 1, (LPBYTE) &lic );
+ theApp.SetLastLlsError( nt );
+
+ EndWaitCursor();
+
+ dwError = (DWORD) nt;
+ }
+ }
+
+ strComment.ReleaseBuffer();
+
+ if ( ( ERROR_SUCCESS != dwError ) && ( ERROR_CANCELLED != dwError ) )
+ {
+ theApp.SetLastError( dwError );
+ theApp.DisplayLastError();
+ }
+ }
+
+ return dwError;
+}
+
+// MD4Update on a DWORD that is platform-independent
+static void MD4UpdateDword( MD4_CTX * pCtx, DWORD dwValue )
+{
+ BYTE b;
+
+ b = (BYTE) ( ( dwValue & 0xFF000000 ) >> 24 );
+ MD4Update( pCtx, &b, 1 );
+
+ b = (BYTE) ( ( dwValue & 0x00FF0000 ) >> 16 );
+ MD4Update( pCtx, &b, 1 );
+
+ b = (BYTE) ( ( dwValue & 0x0000FF00 ) >> 8 );
+ MD4Update( pCtx, &b, 1 );
+
+ b = (BYTE) ( ( dwValue & 0x000000FF ) );
+ MD4Update( pCtx, &b, 1 );
+}
diff --git a/private/net/svcdlls/lls/ccfapi32/paper.h b/private/net/svcdlls/lls/ccfapi32/paper.h
new file mode 100644
index 000000000..4f234a22e
--- /dev/null
+++ b/private/net/svcdlls/lls/ccfapi32/paper.h
@@ -0,0 +1,145 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ paper.h
+
+Abstract:
+
+ Paper certificate dialog prototype.
+
+Author:
+
+ Jeff Parham (jeffparh) 13-Dec-1995
+
+Revision History:
+
+--*/
+
+
+// constrained by number of bits allowed for it in KeyCode (see below)
+#define MAX_NUM_LICENSES ( 4095 )
+
+// XOR mask for key code value; this is so that the high bits that are
+// usually set for the key code need not be entered -- they only need
+// be entered if the high bits are _not_ set
+#define KEY_CODE_MASK ( 0xF0000000 )
+
+
+class CPaperSourceDlg : public CDialog
+{
+// Construction
+public:
+ CPaperSourceDlg(CWnd* pParent = NULL); // standard constructor
+ ~CPaperSourceDlg();
+
+ DWORD CertificateEnter( LPCSTR pszServerName, LPCSTR pszProductName, LPCSTR pszVendor, DWORD dwFlags );
+ DWORD CertificateRemove( LPCSTR pszServerName, DWORD dwFlags, PLLS_LICENSE_INFO_1 pLicenseInfo );
+
+ BOOL ConnectServer();
+ BOOL ConnectEnterprise();
+ NTSTATUS ConnectTo( BOOL bUseEnterprise, CString strServerName, PLLS_HANDLE phLls );
+ void AbortDialogIfNecessary();
+
+ void GetProductList();
+
+ DWORD ComputeActivationCode();
+ NTSTATUS AddLicense();
+
+ BOOL m_bProductListRetrieved;
+ DWORD m_dwEnterFlags;
+
+ CString m_strServerName;
+
+ LLS_HANDLE m_hLls;
+ LLS_HANDLE m_hEnterpriseLls;
+
+ // KeyCode format:
+ //
+ // 31....................................0
+ // | 2 | 2 | 16 | 12 |
+ // A B C D
+ //
+ // ModesAllowed = A
+ // Bit field: bit 0 = allow per seat
+ // bit 1 = allow per server
+ //
+ // FlipsAllowed = B
+ // Bit field: bit 0 = allow flip from per seat
+ // bit 1 = allow flip from per server
+ // (THIS IS CURRENTLY UNIMPLEMENTED.)
+ //
+ // ExpirationDate = C
+ // Bit field: bits 0-6 = years since 1980
+ // bits 7-10 = month (1-12)
+ // bits 11-15 = day (1-31)
+ //
+ // NumLicenses = D
+
+ DWORD KeyCodeToNumLicenses( DWORD dwKeyCode );
+ DWORD KeyCodeToFlipsAllowed( DWORD dwKeyCode );
+ DWORD KeyCodeToModesAllowed( DWORD dwKeyCode );
+ DWORD KeyCodeToExpirationDate( DWORD dwKeyCode );
+
+// Dialog Data
+ //{{AFX_DATA(CPaperSourceDlg)
+ enum { IDD = IDD_CERT_SOURCE_PAPER };
+ CSpinButtonCtrl m_spinLicenses;
+ CComboBox m_cboxProductName;
+ CString m_strActivationCode;
+ CString m_strKeyCode;
+ CString m_strSerialNumber;
+ CString m_strVendor;
+ CString m_strProductName;
+ CString m_strComment;
+ int m_nDontInstallAllLicenses;
+ int m_nLicenses;
+ int m_nLicenseMode;
+ //}}AFX_DATA
+
+// Overrides
+ // ClassWizard generated virtual function overrides
+ //{{AFX_VIRTUAL(CPaperSourceDlg)
+ public:
+ virtual void WinHelp(DWORD dwData, UINT nCmd = HELP_CONTEXT);
+ protected:
+ virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
+ //}}AFX_VIRTUAL
+
+// Implementation
+protected:
+
+ void EnableOrDisableOK();
+
+ // Generated message map functions
+ //{{AFX_MSG(CPaperSourceDlg)
+ afx_msg void OnUpdateActivationCode();
+ afx_msg void OnUpdateKeyCode();
+ afx_msg void OnUpdateVendor();
+ virtual BOOL OnInitDialog();
+ virtual void OnOK();
+ afx_msg void OnUpdateSerialNumber();
+ afx_msg void OnUpdateProductName();
+ afx_msg void OnDropDownProductName();
+ afx_msg void OnHelp();
+ afx_msg BOOL OnHelpInfo(HELPINFO* pHelpInfo);
+ afx_msg void OnDestroy();
+ afx_msg void OnAllLicenses();
+ afx_msg void OnSomeLicenses();
+ afx_msg void OnDeltaPosSpinLicenses(NMHDR* pNMHDR, LRESULT* pResult);
+ //}}AFX_MSG
+ DECLARE_MESSAGE_MAP()
+};
+
+///////////////////////////////////
+
+inline DWORD CPaperSourceDlg::KeyCodeToNumLicenses( DWORD dwKeyCode )
+ { return ( dwKeyCode & 0x00000FFF ); }
+
+inline DWORD CPaperSourceDlg::KeyCodeToModesAllowed( DWORD dwKeyCode )
+ { return ( dwKeyCode >> 30 ); }
+
+inline DWORD CPaperSourceDlg::KeyCodeToFlipsAllowed( DWORD dwKeyCode )
+ { return ( ( dwKeyCode >> 28 ) & 0x3 ); }
diff --git a/private/net/svcdlls/lls/ccfapi32/pseatdlg.cpp b/private/net/svcdlls/lls/ccfapi32/pseatdlg.cpp
new file mode 100644
index 000000000..0e9db97c3
--- /dev/null
+++ b/private/net/svcdlls/lls/ccfapi32/pseatdlg.cpp
@@ -0,0 +1,154 @@
+/*++
+
+Copyright (c) 1994-95 Microsoft Corporation
+
+Module Name:
+
+ pseatdlg.cpp
+
+Abstract:
+
+ Per seat confirmation dialog.
+
+Author:
+
+ Don Ryan (donryan) 28-Feb-1995
+
+Environment:
+
+ User Mode - Win32
+
+Revision History:
+
+ Jeff Parham (jeffparh) 15-Dec-1995
+ Robbed from LLSMGR.
+
+--*/
+
+#include "stdafx.h"
+#include "ccfapi.h"
+#include "pseatdlg.h"
+
+#ifdef _DEBUG
+#undef THIS_FILE
+static char BASED_CODE THIS_FILE[] = __FILE__;
+#endif
+
+BEGIN_MESSAGE_MAP(CPerSeatLicensingDialog, CDialog)
+ //{{AFX_MSG_MAP(CPerSeatLicensingDialog)
+ ON_BN_CLICKED(IDC_PER_SEAT_AGREE, OnAgree)
+ //}}AFX_MSG_MAP
+END_MESSAGE_MAP()
+
+
+CPerSeatLicensingDialog::CPerSeatLicensingDialog(CWnd* pParent /*=NULL*/)
+ : CDialog(CPerSeatLicensingDialog::IDD, pParent)
+
+/*++
+
+Routine Description:
+
+ Constructor for dialog.
+
+Arguments:
+
+ pParent - owner window.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ //{{AFX_DATA_INIT(CPerSeatLicensingDialog)
+ m_strStaticClients = _T("");
+ //}}AFX_DATA_INIT
+
+ m_strProduct = _T("");
+}
+
+
+void CPerSeatLicensingDialog::DoDataExchange(CDataExchange* pDX)
+
+/*++
+
+Routine Description:
+
+ Called by framework to exchange dialog data.
+
+Arguments:
+
+ pDX - data exchange object.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ CDialog::DoDataExchange(pDX);
+ //{{AFX_DATA_MAP(CPerSeatLicensingDialog)
+ DDX_Control(pDX, IDC_PER_SEAT_AGREE, m_agreeBtn);
+ DDX_Control(pDX, IDOK, m_okBtn);
+ DDX_Text(pDX, IDC_PER_SEAT_STATIC_CLIENTS, m_strStaticClients);
+ //}}AFX_DATA_MAP
+}
+
+
+BOOL CPerSeatLicensingDialog::OnInitDialog()
+
+/*++
+
+Routine Description:
+
+ Message handler for WM_INITDIALOG.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ Returns false if focus set manually.
+
+--*/
+
+{
+ AfxFormatString1(
+ m_strStaticClients,
+ IDS_PER_SEAT_LICENSING_1,
+ m_strProduct
+ );
+
+ CDialog::OnInitDialog();
+
+ m_agreeBtn.SetCheck(0);
+ m_okBtn.EnableWindow(FALSE);
+
+ return TRUE;
+}
+
+
+void CPerSeatLicensingDialog::OnAgree()
+
+/*++
+
+Routine Description:
+
+ Toggle okay button.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ m_okBtn.EnableWindow(!m_okBtn.IsWindowEnabled());
+}
diff --git a/private/net/svcdlls/lls/ccfapi32/pseatdlg.h b/private/net/svcdlls/lls/ccfapi32/pseatdlg.h
new file mode 100644
index 000000000..705c66df8
--- /dev/null
+++ b/private/net/svcdlls/lls/ccfapi32/pseatdlg.h
@@ -0,0 +1,62 @@
+/*++
+
+Copyright (c) 1994-95 Microsoft Corporation
+
+Module Name:
+
+ pseatdlg.h
+
+Abstract:
+
+ Per seat confirmation dialog.
+
+Author:
+
+ Don Ryan (donryan) 28-Feb-1995
+
+Environment:
+
+ User Mode - Win32
+
+Revision History:
+
+ Jeff Parham (jeffparh) 15-Dec-1995
+ Robbed from LLSMGR.
+
+--*/
+
+#ifndef _PSEATDLG_H_
+#define _PSEATDLG_H_
+
+class CPerSeatLicensingDialog : public CDialog
+{
+public:
+ CString m_strProduct;
+
+public:
+ CPerSeatLicensingDialog(CWnd* pParent = NULL);
+
+ //{{AFX_DATA(CPerSeatLicensingDialog)
+ enum { IDD = IDD_PER_SEAT_LICENSING };
+ CButton m_agreeBtn;
+ CButton m_okBtn;
+ CString m_strStaticClients;
+ //}}AFX_DATA
+
+ //{{AFX_VIRTUAL(CPerSeatLicensingDialog)
+ protected:
+ virtual void DoDataExchange(CDataExchange* pDX);
+ //}}AFX_VIRTUAL
+
+protected:
+ //{{AFX_MSG(CPerSeatLicensingDialog)
+ virtual BOOL OnInitDialog();
+ afx_msg void OnAgree();
+ //}}AFX_MSG
+ DECLARE_MESSAGE_MAP()
+};
+
+#endif // _PSEATDLG_H_
+
+
+
diff --git a/private/net/svcdlls/lls/ccfapi32/psrvdlg.cpp b/private/net/svcdlls/lls/ccfapi32/psrvdlg.cpp
new file mode 100644
index 000000000..636ce7640
--- /dev/null
+++ b/private/net/svcdlls/lls/ccfapi32/psrvdlg.cpp
@@ -0,0 +1,152 @@
+/*++
+
+Copyright (c) 1994-95 Microsoft Corporation
+
+Module Name:
+
+ psrvdlg.cpp
+
+Abstract:
+
+ Per server confirmation dialog.
+
+Author:
+
+ Don Ryan (donryan) 28-Feb-1995
+
+Environment:
+
+ User Mode - Win32
+
+Revision History:
+
+ Jeff Parham (jeffparh) 15-Dec-1995
+ Robbed from LLSMGR.
+
+--*/
+
+#include "stdafx.h"
+#include "ccfapi.h"
+#include "psrvdlg.h"
+
+#ifdef _DEBUG
+#undef THIS_FILE
+static char BASED_CODE THIS_FILE[] = __FILE__;
+#endif
+
+BEGIN_MESSAGE_MAP(CPerServerLicensingDialog, CDialog)
+ //{{AFX_MSG_MAP(CPerServerLicensingDialog)
+ ON_BN_CLICKED(IDC_PER_SERVER_AGREE, OnAgree)
+ //}}AFX_MSG_MAP
+END_MESSAGE_MAP()
+
+CPerServerLicensingDialog::CPerServerLicensingDialog(CWnd* pParent /*=NULL*/)
+ : CDialog(CPerServerLicensingDialog::IDD, pParent)
+
+/*++
+
+Routine Description:
+
+ Constructor for dialog.
+
+Arguments:
+
+ pParent - owner window.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ //{{AFX_DATA_INIT(CPerServerLicensingDialog)
+ m_strStaticClients = _T("");
+ //}}AFX_DATA_INIT
+}
+
+
+void CPerServerLicensingDialog::DoDataExchange(CDataExchange* pDX)
+
+/*++
+
+Routine Description:
+
+ Called by framework to exchange dialog data.
+
+Arguments:
+
+ pDX - data exchange object.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ CDialog::DoDataExchange(pDX);
+ //{{AFX_DATA_MAP(CPerServerLicensingDialog)
+ DDX_Control(pDX, IDC_PER_SERVER_AGREE, m_agreeBtn);
+ DDX_Control(pDX, IDOK, m_okBtn);
+ DDX_Text(pDX, IDC_PER_SERVER_STATIC_CLIENTS, m_strStaticClients);
+ //}}AFX_DATA_MAP
+}
+
+
+BOOL CPerServerLicensingDialog::OnInitDialog()
+
+/*++
+
+Routine Description:
+
+ Message handler for WM_INITDIALOG.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ Returns false if focus set manually.
+
+--*/
+
+{
+ AfxFormatString2(
+ m_strStaticClients,
+ IDS_PER_SERVER_LICENSING_1,
+ m_strLicenses,
+ m_strProduct
+ );
+
+ CDialog::OnInitDialog();
+
+ m_agreeBtn.SetCheck(0);
+ m_okBtn.EnableWindow(FALSE);
+
+ return TRUE;
+}
+
+
+void CPerServerLicensingDialog::OnAgree()
+
+/*++
+
+Routine Description:
+
+ Toggle okay button.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ m_okBtn.EnableWindow(!m_okBtn.IsWindowEnabled());
+}
diff --git a/private/net/svcdlls/lls/ccfapi32/psrvdlg.h b/private/net/svcdlls/lls/ccfapi32/psrvdlg.h
new file mode 100644
index 000000000..cc0a26553
--- /dev/null
+++ b/private/net/svcdlls/lls/ccfapi32/psrvdlg.h
@@ -0,0 +1,63 @@
+/*++
+
+Copyright (c) 1994-95 Microsoft Corporation
+
+Module Name:
+
+ psrvdlg.h
+
+Abstract:
+
+ Per server confirmation dialog.
+
+Author:
+
+ Don Ryan (donryan) 28-Feb-1995
+
+Environment:
+
+ User Mode - Win32
+
+Revision History:
+
+ Jeff Parham (jeffparh) 15-Dec-1995
+ Robbed from LLSMGR.
+
+--*/
+
+#ifndef _PSRVDLG_H_
+#define _PSRVDLG_H_
+
+class CPerServerLicensingDialog : public CDialog
+{
+public:
+ CString m_strProduct;
+ CString m_strLicenses;
+
+public:
+ CPerServerLicensingDialog(CWnd* pParent = NULL);
+
+ //{{AFX_DATA(CPerServerLicensingDialog)
+ enum { IDD = IDD_PER_SERVER_LICENSING };
+ CButton m_agreeBtn;
+ CButton m_okBtn;
+ CString m_strStaticClients;
+ //}}AFX_DATA
+
+ //{{AFX_VIRTUAL(CPerServerLicensingDialog)
+ protected:
+ virtual void DoDataExchange(CDataExchange* pDX);
+ //}}AFX_VIRTUAL
+
+protected:
+ //{{AFX_MSG(CPerServerLicensingDialog)
+ virtual BOOL OnInitDialog();
+ afx_msg void OnAgree();
+ //}}AFX_MSG
+ DECLARE_MESSAGE_MAP()
+};
+
+#endif // _PSRVDLG_H_
+
+
+
diff --git a/private/net/svcdlls/lls/ccfapi32/remdlg.cpp b/private/net/svcdlls/lls/ccfapi32/remdlg.cpp
new file mode 100644
index 000000000..2d9af28c8
--- /dev/null
+++ b/private/net/svcdlls/lls/ccfapi32/remdlg.cpp
@@ -0,0 +1,1364 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ remdlg.cpp
+
+Abstract:
+
+ Remove licenses dialog implementation.
+
+Author:
+
+ Jeff Parham (jeffparh) 13-Dec-1995
+
+Revision History:
+
+--*/
+
+
+#include "stdafx.h"
+#include "ccfapi.h"
+#include "remdlg.h"
+#include "utils.h"
+#include "licobj.h"
+#include "imagelst.h"
+#include "nlicdlg.h"
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+// describes the list view layout
+static LV_COLUMN_INFO g_removeColumnInfo =
+{
+ 0, 1, LVID_REMOVE_TOTAL_COLUMNS,
+
+ {{LVID_REMOVE_SERIAL_NUMBER, IDS_SERIAL_NUMBER, LVCX_REMOVE_SERIAL_NUMBER },
+ {LVID_REMOVE_PRODUCT_NAME, IDS_PRODUCT_NAME, LVCX_REMOVE_PRODUCT_NAME },
+ {LVID_REMOVE_LICENSE_MODE, IDS_LICENSE_MODE, LVCX_REMOVE_LICENSE_MODE },
+ {LVID_REMOVE_NUM_LICENSES, IDS_QUANTITY, LVCX_REMOVE_NUM_LICENSES },
+ {LVID_REMOVE_SOURCE, IDS_SOURCE, LVCX_REMOVE_SOURCE }},
+};
+
+
+CCertRemoveSelectDlg::CCertRemoveSelectDlg(CWnd* pParent /*=NULL*/)
+ : CDialog(CCertRemoveSelectDlg::IDD, pParent)
+
+/*++
+
+Routine Description:
+
+ Constructor for dialog.
+
+Arguments:
+
+ pParent - owner window.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ //{{AFX_DATA_INIT(CCertRemoveSelectDlg)
+ m_nLicenses = 0;
+ //}}AFX_DATA_INIT
+
+ m_hLls = NULL;
+ m_bLicensesRefreshed = FALSE;
+ m_dwRemoveFlags = 0;
+}
+
+
+CCertRemoveSelectDlg::~CCertRemoveSelectDlg()
+
+/*++
+
+Routine Description:
+
+ Destructor for dialog.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ if ( NULL != m_hLls )
+ {
+ LlsClose( m_hLls );
+ }
+}
+
+
+void CCertRemoveSelectDlg::DoDataExchange(CDataExchange* pDX)
+
+/*++
+
+Routine Description:
+
+ Called by framework to exchange dialog data.
+
+Arguments:
+
+ pDX - data exchange object.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ CDialog::DoDataExchange(pDX);
+ //{{AFX_DATA_MAP(CCertRemoveSelectDlg)
+ DDX_Control(pDX, IDC_SPIN_LICENSES, m_spinLicenses);
+ DDX_Control(pDX, IDC_CERTIFICATE_LIST, m_listCertificates);
+ DDX_Text(pDX, IDC_NUM_LICENSES, m_nLicenses);
+ //}}AFX_DATA_MAP
+}
+
+
+BEGIN_MESSAGE_MAP(CCertRemoveSelectDlg, CDialog)
+ //{{AFX_MSG_MAP(CCertRemoveSelectDlg)
+ ON_BN_CLICKED(IDC_MY_HELP, OnHelp)
+ ON_NOTIFY(LVN_COLUMNCLICK, IDC_CERTIFICATE_LIST, OnColumnClickCertificateList)
+ ON_NOTIFY(LVN_GETDISPINFO, IDC_CERTIFICATE_LIST, OnGetDispInfoCertificateList)
+ ON_NOTIFY(UDN_DELTAPOS, IDC_SPIN_LICENSES, OnDeltaPosSpinLicenses)
+ ON_NOTIFY(NM_DBLCLK, IDC_CERTIFICATE_LIST, OnDblClkCertificateList)
+ ON_NOTIFY(NM_RETURN, IDC_CERTIFICATE_LIST, OnReturnCertificateList)
+ ON_WM_DESTROY()
+ ON_NOTIFY(NM_CLICK, IDC_CERTIFICATE_LIST, OnClickCertificateList)
+ ON_NOTIFY(LVN_KEYDOWN, IDC_CERTIFICATE_LIST, OnKeyDownCertificateList)
+ ON_BN_CLICKED(IDC_REFRESH, OnRefresh)
+ //}}AFX_MSG_MAP
+END_MESSAGE_MAP()
+
+
+BOOL CCertRemoveSelectDlg::OnInitDialog()
+
+/*++
+
+Routine Description:
+
+ Handler for WM_INITDIALOG.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ Returns false if focus set manually.
+
+--*/
+
+{
+ CDialog::OnInitDialog();
+
+ LoadImages();
+
+ m_listCertificates.SetImageList( &m_smallImages, LVSIL_SMALL );
+
+ ::LvInitColumns( &m_listCertificates, &g_removeColumnInfo );
+
+ RefreshLicenses();
+ RefreshCertificateList();
+ UpdateSpinControlRange();
+
+ return TRUE; // return TRUE unless you set the focus to a control
+ // EXCEPTION: OCX Property Pages should return FALSE
+}
+
+
+void CCertRemoveSelectDlg::OnOK()
+
+/*++
+
+Routine Description:
+
+ Handler for BN_CLICKED of OK.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ RemoveSelectedCertificate();
+}
+
+
+void CCertRemoveSelectDlg::OnHelp()
+
+/*++
+
+Routine Description:
+
+ Handler for help button click.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ WinHelp( IDD, HELP_CONTEXT );
+}
+
+
+void CCertRemoveSelectDlg::WinHelp(DWORD dwData, UINT nCmd)
+
+/*++
+
+Routine Description:
+
+ Call WinHelp for this dialog.
+
+Arguments:
+
+ dwData (DWORD)
+ nCmd (UINT)
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ BOOL ok = ::WinHelp( m_hWnd, theApp.GetHelpFileName(), nCmd, dwData );
+ ASSERT( ok );
+}
+
+
+void CCertRemoveSelectDlg::OnDestroy()
+
+/*++
+
+Routine Description:
+
+ Handler for WM_DESTROY.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ ResetLicenses();
+
+ ::WinHelp( m_hWnd, theApp.GetHelpFileName(), HELP_QUIT, 0 );
+
+ CDialog::OnDestroy();
+}
+
+
+void CCertRemoveSelectDlg::OnColumnClickCertificateList(NMHDR* pNMHDR, LRESULT* pResult)
+
+/*++
+
+Routine Description:
+
+ Handler for LVN_COLUMNCLICK of certificate list view.
+
+Arguments:
+
+ pNMHDR (NMHDR*)
+ pResult (LRESULT*)
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ g_removeColumnInfo.bSortOrder = GetKeyState(VK_CONTROL) < 0;
+ g_removeColumnInfo.nSortedItem = ((NM_LISTVIEW*)pNMHDR)->iSubItem;
+
+ m_listCertificates.SortItems( CompareLicenses, 0 ); // use column info
+
+ *pResult = 0;
+}
+
+
+void CCertRemoveSelectDlg::OnGetDispInfoCertificateList(NMHDR* pNMHDR, LRESULT* pResult)
+
+/*++
+
+Routine Description:
+
+ Handler for LVN_GETDISPINFO of certificate list view.
+
+Arguments:
+
+ pNMHDR (NMHDR*)
+ pResult (LRESULT*)
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ LV_ITEM* plvItem = &((LV_DISPINFO*)pNMHDR)->item;
+ ASSERT(plvItem);
+
+ CLicense* pLicense = (CLicense*)plvItem->lParam;
+ VALIDATE_OBJECT(pLicense, CLicense);
+
+ switch (plvItem->iSubItem)
+ {
+ case LVID_REMOVE_SERIAL_NUMBER:
+ plvItem->iImage = BMPI_CERTIFICATE;
+ {
+ CString strSerialNumber;
+
+ strSerialNumber.Format( TEXT("%ld"), (LONG) ( pLicense->m_dwCertificateID ) );
+ lstrcpyn( plvItem->pszText, strSerialNumber, plvItem->cchTextMax );
+ }
+ break;
+
+ case LVID_REMOVE_PRODUCT_NAME:
+ lstrcpyn( plvItem->pszText, pLicense->m_strProduct, plvItem->cchTextMax );
+ break;
+
+ case LVID_REMOVE_LICENSE_MODE:
+ lstrcpyn( plvItem->pszText, pLicense->GetAllowedModesString(), plvItem->cchTextMax );
+ break;
+
+ case LVID_REMOVE_NUM_LICENSES:
+ {
+ CString strLicenses;
+
+ strLicenses.Format( TEXT("%ld"), (LONG) ( pLicense->m_lQuantity ) );
+ lstrcpyn( plvItem->pszText, strLicenses, plvItem->cchTextMax );
+ }
+ break;
+
+ case LVID_REMOVE_SOURCE:
+ lstrcpyn( plvItem->pszText, pLicense->GetSourceDisplayName(), plvItem->cchTextMax );
+ break;
+
+ default:
+ ASSERT( FALSE );
+ break;
+ }
+
+ *pResult = 0;
+}
+
+
+void CCertRemoveSelectDlg::OnDeltaPosSpinLicenses(NMHDR* pNMHDR, LRESULT* pResult)
+
+/*++
+
+Routine Description:
+
+ Handler for UDN_DELTAPOS of number of licenses.
+
+Arguments:
+
+ pNMHDR (NMHDR*)
+ pResult (LRESULT*)
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ if ( UpdateData(TRUE) ) // get data
+ {
+ m_nLicenses += ((NM_UPDOWN*)pNMHDR)->iDelta;
+
+ int nLow;
+ int nHigh;
+
+ m_spinLicenses.GetRange( nLow, nHigh );
+
+ if (m_nLicenses < nLow)
+ {
+ m_nLicenses = nLow;
+
+ ::MessageBeep(MB_OK);
+ }
+ else if (m_nLicenses > nHigh )
+ {
+ m_nLicenses = nHigh;
+
+ ::MessageBeep(MB_OK);
+ }
+
+ UpdateData(FALSE); // set data
+ }
+
+ *pResult = 1; // handle ourselves...
+}
+
+
+void CCertRemoveSelectDlg::OnDblClkCertificateList(NMHDR* pNMHDR, LRESULT* pResult)
+
+/*++
+
+Routine Description:
+
+ Handler for NM_DBLCLK of certificate list view.
+
+Arguments:
+
+ pNMHDR (NMHDR*)
+ pResult (LRESULT*)
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ RemoveSelectedCertificate();
+ *pResult = 0;
+}
+
+
+void CCertRemoveSelectDlg::OnReturnCertificateList(NMHDR* pNMHDR, LRESULT* pResult)
+
+/*++
+
+Routine Description:
+
+ Handler for NM_RETURN of certificate list view.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ RemoveSelectedCertificate();
+ *pResult = 0;
+}
+
+
+void CCertRemoveSelectDlg::ResetLicenses()
+
+/*++
+
+Routine Description:
+
+ Remove all licenses from internal list.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ CLicense* pLicense;
+ int iLicense = m_licenseArray.GetSize();
+
+ while (iLicense--)
+ {
+ if (pLicense = (CLicense*)m_licenseArray[iLicense])
+ {
+ ASSERT(pLicense->IsKindOf(RUNTIME_CLASS(CLicense)));
+ delete pLicense;
+ }
+ }
+
+ m_licenseArray.RemoveAll();
+ m_listCertificates.DeleteAllItems();
+
+ m_bLicensesRefreshed = FALSE;
+}
+
+
+BOOL CCertRemoveSelectDlg::RefreshLicenses()
+
+/*++
+
+Routine Description:
+
+ Refresh internal license list with data from license server.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ BOOL.
+
+--*/
+
+{
+ ResetLicenses();
+
+ if ( ConnectServer() )
+ {
+ NTSTATUS NtStatus;
+ DWORD ResumeHandle = 0L;
+
+ int iLicense = 0;
+
+ do
+ {
+ DWORD EntriesRead;
+ DWORD TotalEntries;
+ LPBYTE ReturnBuffer = NULL;
+ DWORD Level = LlsCapabilityIsSupported( m_hLls, LLS_CAPABILITY_SECURE_CERTIFICATES ) ? 1 : 0;
+
+ BeginWaitCursor();
+ NtStatus = ::LlsLicenseEnum( m_hLls,
+ Level,
+ &ReturnBuffer,
+ LLS_PREFERRED_LENGTH,
+ &EntriesRead,
+ &TotalEntries,
+ &ResumeHandle );
+ EndWaitCursor();
+
+ if ( ( STATUS_SUCCESS == NtStatus )
+ || ( STATUS_MORE_ENTRIES == NtStatus ) )
+ {
+ CLicense* pLicense;
+ PLLS_LICENSE_INFO_0 pLicenseInfo0;
+ PLLS_LICENSE_INFO_1 pLicenseInfo1;
+
+ pLicenseInfo0 = (PLLS_LICENSE_INFO_0)ReturnBuffer;
+ pLicenseInfo1 = (PLLS_LICENSE_INFO_1)ReturnBuffer;
+
+ while (EntriesRead--)
+ {
+ if ( ( m_strProductName.IsEmpty() || !m_strProductName.CompareNoCase( Level ? pLicenseInfo1->Product : pLicenseInfo0->Product ) )
+ && ( m_strSourceToUse.IsEmpty() || !m_strSourceToUse.CompareNoCase( Level ? pLicenseInfo1->Source : TEXT("None") ) ) )
+ {
+ // we want to list this license
+
+ // have we seen this certificate yet?
+ for ( int i=0; i < m_licenseArray.GetSize(); i++ )
+ {
+ pLicense = (CLicense*) m_licenseArray[ i ];
+
+ VALIDATE_OBJECT( pLicense, CLicense );
+
+ if ( ( ( 1 == Level )
+ && ( pLicense->m_dwCertificateID == pLicenseInfo1->CertificateID )
+ && ( pLicense->m_dwAllowedModes == pLicenseInfo1->AllowedModes )
+ && ( pLicense->m_dwMaxQuantity == pLicenseInfo1->MaxQuantity )
+ && ( !pLicense->m_strSource.CompareNoCase( pLicenseInfo1->Source ) )
+ && ( !pLicense->m_strProduct.CompareNoCase( pLicenseInfo1->Product ) )
+ && ( !memcmp( pLicense->m_adwSecrets,
+ pLicenseInfo1->Secrets,
+ sizeof( pLicense->m_adwSecrets ) ) ) )
+ || ( ( 0 == Level )
+ && ( !pLicense->m_strProduct.CompareNoCase( pLicenseInfo0->Product ) ) ) )
+ {
+ // we've seen this certificate before; update the tally
+ pLicense->m_lQuantity += ( Level ? pLicenseInfo1->Quantity : pLicenseInfo0->Quantity );
+ break;
+ }
+ }
+
+ if ( i >= m_licenseArray.GetSize() )
+ {
+ // we haven't seen this certificate yet; create a new license for it
+ if ( 1 == Level )
+ {
+ pLicense = new CLicense( pLicenseInfo1->Product,
+ pLicenseInfo1->Vendor,
+ pLicenseInfo1->Admin,
+ pLicenseInfo1->Date,
+ pLicenseInfo1->Quantity,
+ pLicenseInfo1->Comment,
+ pLicenseInfo1->AllowedModes,
+ pLicenseInfo1->CertificateID,
+ pLicenseInfo1->Source,
+ pLicenseInfo1->ExpirationDate,
+ pLicenseInfo1->MaxQuantity,
+ pLicenseInfo1->Secrets );
+
+ ::LlsFreeMemory( pLicenseInfo1->Product );
+ ::LlsFreeMemory( pLicenseInfo1->Admin );
+ ::LlsFreeMemory( pLicenseInfo1->Comment );
+ ::LlsFreeMemory( pLicenseInfo1->Source );
+ }
+ else
+ {
+ ASSERT( 0 == Level );
+
+ pLicense = new CLicense( pLicenseInfo0->Product,
+ TEXT( "Microsoft" ),
+ pLicenseInfo0->Admin,
+ pLicenseInfo0->Date,
+ pLicenseInfo0->Quantity,
+ pLicenseInfo0->Comment );
+
+ ::LlsFreeMemory( pLicenseInfo0->Product );
+ ::LlsFreeMemory( pLicenseInfo0->Admin );
+ ::LlsFreeMemory( pLicenseInfo0->Comment );
+ }
+
+ if ( NULL == pLicense )
+ {
+ NtStatus = ERROR_OUTOFMEMORY;
+ break;
+ }
+
+ m_licenseArray.Add( pLicense );
+ }
+ }
+
+ pLicenseInfo1++;
+ pLicenseInfo0++;
+ }
+
+ ::LlsFreeMemory(ReturnBuffer);
+ }
+
+ } while ( STATUS_MORE_ENTRIES == NtStatus );
+
+ theApp.SetLastLlsError( NtStatus ); // called api
+
+ if ( STATUS_SUCCESS == NtStatus )
+ {
+ // add per server entries
+ LPTSTR pszServerName = m_strServerName.GetBuffer(0);
+
+ if ( NULL != pszServerName )
+ {
+ BeginWaitCursor();
+
+ HKEY hKeyLocalMachine;
+
+ NtStatus = RegConnectRegistry( pszServerName, HKEY_LOCAL_MACHINE, &hKeyLocalMachine );
+
+ if ( ERROR_SUCCESS != NtStatus )
+ {
+ theApp.SetLastError( NtStatus );
+ }
+ else
+ {
+ HKEY hKeyLicenseInfo;
+
+ NtStatus = RegOpenKeyEx( hKeyLocalMachine, TEXT( "SYSTEM\\CurrentControlSet\\Services\\LicenseInfo" ), 0, KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS | KEY_SET_VALUE, &hKeyLicenseInfo );
+
+ if ( ERROR_SUCCESS != NtStatus )
+ {
+ theApp.SetLastError( NtStatus );
+ }
+ else
+ {
+ NTSTATUS ntEnum;
+ BOOL bFoundKey = FALSE;
+ DWORD iSubKey = 0;
+
+ // if the service is 3.51-style per server, add it to the list
+ do
+ {
+ TCHAR szKeyName[ 128 ];
+ DWORD cchKeyName = sizeof( szKeyName ) / sizeof( *szKeyName );
+
+ ntEnum = RegEnumKeyEx( hKeyLicenseInfo, iSubKey++, szKeyName, &cchKeyName, NULL, NULL, NULL, NULL );
+
+ if ( ERROR_SUCCESS == ntEnum )
+ {
+ HKEY hKeyProduct;
+
+ NtStatus = RegOpenKeyEx( hKeyLicenseInfo, szKeyName, 0, KEY_QUERY_VALUE | KEY_SET_VALUE, &hKeyProduct );
+
+ if ( ERROR_SUCCESS == NtStatus )
+ {
+ DWORD dwType;
+ TCHAR szDisplayName[ 128 ];
+ DWORD cbDisplayName = sizeof( szDisplayName );
+
+ NtStatus = RegQueryValueEx( hKeyProduct, TEXT( "DisplayName" ), NULL, &dwType, (LPBYTE) szDisplayName, &cbDisplayName );
+
+ if ( ERROR_SUCCESS == NtStatus )
+ {
+ // is this product secure?
+ BOOL bIsSecure = FALSE;
+
+ if ( LlsCapabilityIsSupported( m_hLls, LLS_CAPABILITY_SECURE_CERTIFICATES ) )
+ {
+ NtStatus = ::LlsProductSecurityGet( m_hLls, szDisplayName, &bIsSecure );
+ theApp.SetLastLlsError( NtStatus );
+
+ if ( STATUS_SUCCESS != NtStatus )
+ {
+ bIsSecure = FALSE;
+ }
+ }
+
+ if ( !bIsSecure )
+ {
+#ifdef REMOVE_CONCURRENT_ONLY_IF_PER_SERVER_MODE
+ // not secure; is it in per server mode?
+ DWORD dwMode;
+ DWORD cbMode = sizeof( dwMode );
+
+ NtStatus = RegQueryValueEx( hKeyProduct, TEXT( "Mode" ), NULL, &dwType, (LPBYTE) &dwMode, &cbMode );
+
+ if ( ( ERROR_SUCCESS == NtStatus ) && dwMode )
+ {
+ // per server mode; add to list
+#endif
+ DWORD dwConcurrentLimit;
+ DWORD cbConcurrentLimit = sizeof( dwConcurrentLimit );
+
+ NtStatus = RegQueryValueEx( hKeyProduct, TEXT( "ConcurrentLimit" ), NULL, &dwType, (LPBYTE) &dwConcurrentLimit, &cbConcurrentLimit );
+
+ if ( ( ERROR_SUCCESS == NtStatus )
+ && ( 0 < dwConcurrentLimit )
+ && ( m_strProductName.IsEmpty() || !m_strProductName.CompareNoCase( szDisplayName ) )
+ && ( m_strSourceToUse.IsEmpty() || !m_strSourceToUse.CompareNoCase( TEXT("None") ) ) )
+ {
+ CLicense * pLicense = new CLicense( szDisplayName,
+ TEXT(""),
+ TEXT(""),
+ 0,
+ dwConcurrentLimit,
+ TEXT(""),
+ LLS_LICENSE_MODE_ALLOW_PER_SERVER );
+
+ if ( NULL != pLicense )
+ {
+ m_licenseArray.Add( pLicense );
+ }
+ }
+ }
+#ifdef REMOVE_CONCURRENT_ONLY_IF_PER_SERVER_MODE
+ }
+#endif
+ }
+
+ RegCloseKey( hKeyProduct );
+ }
+ }
+ } while ( ERROR_SUCCESS == ntEnum );
+
+ RegCloseKey( hKeyLicenseInfo );
+ }
+
+ RegCloseKey( hKeyLocalMachine );
+ }
+
+ m_strServerName.ReleaseBuffer();
+ }
+
+ EndWaitCursor();
+
+ m_bLicensesRefreshed = TRUE;
+
+ // remove any entries from the list that aren't removable
+ for ( int i=0; i < m_licenseArray.GetSize(); )
+ {
+ CLicense* pLicense = (CLicense*) m_licenseArray[ i ];
+
+ VALIDATE_OBJECT( pLicense, CLicense );
+
+ if ( pLicense->m_lQuantity <= 0 )
+ {
+ delete pLicense;
+ m_licenseArray.RemoveAt( i );
+ }
+ else
+ {
+ i++;
+ }
+ }
+ }
+ else
+ {
+ theApp.DisplayLastError();
+ ResetLicenses();
+ }
+ }
+
+ return m_bLicensesRefreshed;
+}
+
+
+BOOL CCertRemoveSelectDlg::RefreshCertificateList()
+
+/*++
+
+Routine Description:
+
+ Refresh certificate list view from internal license list.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ BOOL.
+
+--*/
+
+{
+ BeginWaitCursor();
+
+ BOOL ok = ::LvRefreshObArray( &m_listCertificates, &g_removeColumnInfo, &m_licenseArray );
+
+ EndWaitCursor();
+
+ return ok;
+}
+
+
+int CALLBACK CompareLicenses(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
+
+/*++
+
+Routine Description:
+
+ Notification handler for LVM_SORTITEMS.
+
+Arguments:
+
+ lParam1 - object to sort.
+ lParam2 - object to sort.
+ lParamSort - sort criteria.
+
+Return Values:
+
+ Same as lstrcmp.
+
+--*/
+
+{
+ CLicense * pLic1 = (CLicense *) lParam1;
+ CLicense * pLic2 = (CLicense *) lParam2;
+
+ VALIDATE_OBJECT( pLic1, CLicense );
+ VALIDATE_OBJECT( pLic2, CLicense );
+
+ int iResult;
+
+ switch (g_removeColumnInfo.nSortedItem)
+ {
+ case LVID_REMOVE_SERIAL_NUMBER:
+ iResult = pLic1->m_dwCertificateID - pLic2->m_dwCertificateID;
+ break;
+
+ case LVID_REMOVE_PRODUCT_NAME:
+ iResult = pLic1->m_strProduct.CompareNoCase( pLic2->m_strProduct );
+ break;
+
+ case LVID_REMOVE_NUM_LICENSES:
+ iResult = pLic1->m_lQuantity - pLic2->m_lQuantity;
+ break;
+
+ case LVID_REMOVE_SOURCE:
+ iResult = pLic1->GetSourceDisplayName().CompareNoCase( pLic2->GetSourceDisplayName() );
+ break;
+
+ default:
+ iResult = 0;
+ break;
+ }
+
+ return g_removeColumnInfo.bSortOrder ? -iResult : iResult;
+}
+
+
+void CCertRemoveSelectDlg::UpdateSpinControlRange()
+
+/*++
+
+Routine Description:
+
+ Update range of spin control for number of licenses.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ CLicense * pLicense;
+
+ UpdateData( TRUE );
+
+ if ( pLicense = (CLicense*)::LvGetSelObj( &m_listCertificates ) )
+ {
+ m_spinLicenses.SetRange( 1, pLicense->m_lQuantity );
+ m_nLicenses = pLicense->m_lQuantity;
+ GetDlgItem( IDOK )->EnableWindow( TRUE );
+ }
+ else
+ {
+ m_spinLicenses.SetRange( 0, 0 );
+ m_nLicenses = 0;
+ GetDlgItem( IDOK )->EnableWindow( FALSE );
+ }
+
+ UpdateData( FALSE );
+}
+
+
+DWORD CCertRemoveSelectDlg::RemoveSelectedCertificate()
+
+/*++
+
+Routine Description:
+
+ Remove the given number of licenses from the selected certificate.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ ERROR_SUCCESS
+ NT status code
+ Win error
+
+--*/
+
+{
+ BOOL bDisplayError = TRUE;
+ NTSTATUS nt = STATUS_SUCCESS;
+ CLicense * pLicense;
+
+ UpdateData( TRUE );
+
+ if ( !( pLicense = (CLicense*)::LvGetSelObj( &m_listCertificates ) ) )
+ {
+ // no certificate selected
+ bDisplayError = FALSE;
+ }
+ else if ( ( m_nLicenses < 1 ) || ( m_nLicenses > pLicense->m_lQuantity ) )
+ {
+ // invalid number of licenses to remove
+ AfxMessageBox( IDS_REMOVE_INVALID_NUM_LICENSES, MB_ICONEXCLAMATION | MB_OK, 0 );
+ nt = ERROR_CANCELLED;
+ bDisplayError = FALSE;
+ }
+ else
+ {
+ CString strLicenses;
+ CString strConfirm;
+
+ strLicenses.Format( TEXT("%d"), m_nLicenses );
+ AfxFormatString2( strConfirm, IDS_REMOVE_CERTIFICATE_CONFIRM, strLicenses, pLicense->m_strProduct );
+
+ int nResponse = AfxMessageBox( strConfirm, MB_ICONQUESTION | MB_YESNO | MB_DEFBUTTON2 );
+
+ if ( IDYES != nResponse )
+ {
+ nt = ERROR_CANCELLED;
+ bDisplayError = FALSE;
+ }
+ else
+ {
+ // delete certificate
+ LPSTR pszAscServerName = (LPSTR) LocalAlloc( LMEM_FIXED, 1 + m_strServerName.GetLength() );
+ LPSTR pszAscProductName = (LPSTR) LocalAlloc( LMEM_FIXED, 1 + pLicense->m_strProduct.GetLength() );
+ LPSTR pszAscVendor = (LPSTR) LocalAlloc( LMEM_FIXED, 1 + m_strVendor.GetLength() );
+
+ if ( ( NULL == pszAscServerName ) || ( NULL == pszAscProductName ) || ( NULL == pszAscVendor ) )
+ {
+ nt = ERROR_NOT_ENOUGH_MEMORY;
+ }
+ else
+ {
+ wsprintfA( pszAscServerName, "%ls", (LPCWSTR) m_strServerName );
+ wsprintfA( pszAscProductName, "%ls", (LPCWSTR) pLicense->m_strProduct );
+ wsprintfA( pszAscVendor, "%ls", (LPCWSTR) m_strVendor );
+
+ LLS_LICENSE_INFO_1 lic;
+
+ nt = pLicense->CreateLicenseInfo( &lic );
+
+ if ( STATUS_SUCCESS == nt )
+ {
+ // only remove as many licenses as requested
+ lic.Quantity = m_nLicenses;
+
+ if ( !pLicense->m_strSource.CompareNoCase( TEXT( "None" ) ) )
+ {
+ nt = NoCertificateRemove( m_hWnd, pszAscServerName, m_dwRemoveFlags, 1, &lic );
+ bDisplayError = FALSE;
+ }
+ else
+ {
+ // get certificate source DLL path
+ CString strKeyName = TEXT( "Software\\LSAPI\\Microsoft\\CertificateSources\\" )
+ + pLicense->m_strSource;
+ HKEY hKeySource;
+
+ nt = RegOpenKeyEx( HKEY_LOCAL_MACHINE, strKeyName, 0, KEY_READ, &hKeySource );
+
+ if ( ( ERROR_PATH_NOT_FOUND == nt ) || ( ERROR_FILE_NOT_FOUND == nt ) )
+ {
+ AfxMessageBox( IDS_CERT_SOURCE_NOT_AVAILABLE, MB_ICONSTOP | MB_OK, 0 );
+ nt = ERROR_CANCELLED;
+ bDisplayError = FALSE;
+ }
+ else if ( ERROR_SUCCESS == nt )
+ {
+ TCHAR szImagePath[ 1 + _MAX_PATH ];
+ DWORD cbImagePath = sizeof( szImagePath );
+ DWORD dwType;
+
+ nt = RegQueryValueEx( hKeySource, TEXT( "ImagePath" ), NULL, &dwType, (LPBYTE) szImagePath, &cbImagePath );
+
+ if ( ERROR_SUCCESS == nt )
+ {
+ TCHAR szExpandedImagePath[ 1 + _MAX_PATH ];
+
+ BOOL ok = ExpandEnvironmentStrings( szImagePath, szExpandedImagePath, sizeof( szExpandedImagePath ) / sizeof( *szExpandedImagePath ) );
+
+ if ( !ok )
+ {
+ nt = GetLastError();
+ }
+ else
+ {
+ // load certificate source DLL
+ HINSTANCE hDll = ::LoadLibrary( szExpandedImagePath );
+
+ if ( NULL == hDll )
+ {
+ nt = GetLastError();
+ }
+ else
+ {
+ // get certificate remove function
+ CHAR szExportName[ 256 ];
+ PCCF_REMOVE_API pRemoveFn;
+
+ wsprintfA( szExportName, "%lsCertificateRemove", (LPCWSTR) pLicense->m_strSource );
+ pRemoveFn = (PCCF_REMOVE_API) GetProcAddress( hDll, szExportName );
+
+ if ( NULL == pRemoveFn )
+ {
+ nt = GetLastError();
+ }
+ else
+ {
+ // remove certificate
+ nt = (*pRemoveFn)( m_hWnd, pszAscServerName, m_dwRemoveFlags, 1, &lic );
+ bDisplayError = FALSE;
+ }
+
+ ::FreeLibrary( hDll );
+ }
+ }
+ }
+
+ RegCloseKey( hKeySource );
+ }
+ }
+
+ pLicense->DestroyLicenseInfo( &lic );
+ }
+ }
+
+ if ( NULL != pszAscServerName ) LocalFree( pszAscServerName );
+ if ( NULL != pszAscProductName ) LocalFree( pszAscProductName );
+ if ( NULL != pszAscVendor ) LocalFree( pszAscVendor );
+
+ RefreshLicenses();
+ RefreshCertificateList();
+ UpdateSpinControlRange();
+ }
+ }
+
+ if ( bDisplayError && ( ERROR_SUCCESS != nt ) )
+ {
+ theApp.SetLastError( nt );
+ theApp.DisplayLastError();
+ }
+
+ return nt;
+}
+
+
+BOOL CCertRemoveSelectDlg::ConnectServer()
+
+/*++
+
+Routine Description:
+
+ Establish a connection to the license service on the target server.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ BOOL.
+
+--*/
+
+{
+ if ( NULL == m_hLls )
+ {
+ LPTSTR pszServerName;
+
+ if ( m_strServerName.IsEmpty() )
+ {
+ pszServerName = NULL;
+ }
+ else
+ {
+ pszServerName = m_strServerName.GetBuffer( 0 );
+ }
+
+ ConnectTo( pszServerName, &m_hLls );
+
+ if ( NULL != pszServerName )
+ {
+ m_strServerName.ReleaseBuffer();
+ }
+ }
+
+ if ( NULL == m_hLls )
+ {
+ theApp.DisplayLastError();
+
+ if ( ( NULL != m_hWnd ) && IsWindow( m_hWnd ) )
+ {
+ EndDialog( IDABORT );
+ }
+ }
+
+ return ( NULL != m_hLls );
+}
+
+
+NTSTATUS CCertRemoveSelectDlg::ConnectTo( LPTSTR pszServerName, PLLS_HANDLE phLls )
+
+/*++
+
+Routine Description:
+
+ Establish a connection to the license service on the given server.
+
+Arguments:
+
+ pszServerName (CString)
+ The target server. An empty value indicates the local server.
+ phLls (PLLS_HANDLE)
+ On return, holds the handle to the standard LLS RPC.
+
+Return Values:
+
+ STATUS_SUCCESS or NT status code.
+
+--*/
+
+{
+ NTSTATUS nt;
+
+ nt = ::LlsConnect( pszServerName, phLls );
+ theApp.SetLastLlsError( nt );
+
+ if ( STATUS_SUCCESS != nt )
+ {
+ *phLls = NULL;
+ }
+
+ return nt;
+}
+
+
+void CCertRemoveSelectDlg::OnClickCertificateList(NMHDR* pNMHDR, LRESULT* pResult)
+
+/*++
+
+Routine Description:
+
+ Handler for NM_CLICK of certificate list view.
+
+Arguments:
+
+ pNMHDR (NMHDR*)
+ pResult (LRESULT*)
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ UpdateSpinControlRange();
+ *pResult = 1; // not handled...
+}
+
+void CCertRemoveSelectDlg::OnKeyDownCertificateList(NMHDR* pNMHDR, LRESULT* pResult)
+
+/*++
+
+Routine Description:
+
+ Handler for LVN_KEYDOWN of certificate list view.
+
+Arguments:
+
+ pNMHDR (NMHDR*)
+ pResult (LRESULT*)
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ UpdateSpinControlRange();
+ *pResult = 1; // not handled...
+}
+
+
+BOOL CCertRemoveSelectDlg::LoadImages()
+
+/*++
+
+Routine Description:
+
+ Load icons for the list view.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ BOOL.
+
+--*/
+
+{
+ BOOL bImagesLoaded = m_smallImages.Create( IDB_SMALL_ICONS, BMPI_SMALL_SIZE, 0, BMPI_RGB_BKGND );
+ ASSERT( bImagesLoaded );
+
+ return bImagesLoaded;
+}
+
+
+void CCertRemoveSelectDlg::OnRefresh()
+
+/*++
+
+Routine Description:
+
+ Handler for BN_CLICK of refresh button.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ RefreshLicenses();
+ RefreshCertificateList();
+ UpdateSpinControlRange();
+}
+
+
+DWORD CCertRemoveSelectDlg::CertificateRemove( LPCSTR pszServerName, LPCSTR pszProductName, LPCSTR pszVendor, DWORD dwFlags, LPCSTR pszSourceToUse )
+
+/*++
+
+Routine Description:
+
+ Display a dialog allowing the user to remove one or more license
+ certificates from the system.
+
+Arguments:
+
+ pszServerName (LPCSTR)
+ Name of the server on which licenses are to be removed. A NULL value
+ indicates the local server.
+ pszProductName (LPCSTR)
+ Product for which licenses are to be removed. A NULL value indicates
+ that the user should be allowed to remove licenses from any product.
+ pszVendor (LPCSTR)
+ Name of the vendor of the product. This value should be NULL if
+ pszProductName is NULL, and should be non-NULL if pszProductName is
+ non-NULL.
+ dwFlags (DWORD)
+ Certificate removal options. As of this writing, no flags are
+ supported.
+ pszSourceToUse (LPCSTR)
+ Name of the secure certificate source by which licenses are to be
+ removed, e.g., "Paper". A NULL value indicates that the user should
+ be allowed to remove licenses that were installed with any source.
+
+Return Value:
+
+ ERROR_SUCCESS
+ Win error
+
+--*/
+
+{
+ m_strServerName = pszServerName ? pszServerName : "";
+ m_strProductName = pszProductName ? pszProductName : "";
+ m_strVendor = pszVendor ? pszVendor : "";
+ m_dwRemoveFlags = dwFlags;
+ m_strSourceToUse = pszSourceToUse ? pszSourceToUse : "";
+
+ DoModal();
+
+ return ERROR_SUCCESS;
+}
diff --git a/private/net/svcdlls/lls/ccfapi32/remdlg.h b/private/net/svcdlls/lls/ccfapi32/remdlg.h
new file mode 100644
index 000000000..58295c979
--- /dev/null
+++ b/private/net/svcdlls/lls/ccfapi32/remdlg.h
@@ -0,0 +1,105 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ paper.cpp
+
+Abstract:
+
+ Remove licenses dialog prototype.
+
+Author:
+
+ Jeff Parham (jeffparh) 13-Dec-1995
+
+Revision History:
+
+--*/
+
+
+class CCertRemoveSelectDlg : public CDialog
+{
+public:
+ CCertRemoveSelectDlg(CWnd* pParent = NULL); // standard constructor
+ ~CCertRemoveSelectDlg();
+
+ void UpdateSpinControlRange();
+ BOOL LoadImages();
+
+// Dialog Data
+ //{{AFX_DATA(CCertRemoveSelectDlg)
+ enum { IDD = IDD_CERT_REMOVE_SELECT };
+ CSpinButtonCtrl m_spinLicenses;
+ CListCtrl m_listCertificates;
+ int m_nLicenses;
+ //}}AFX_DATA
+
+ CObArray m_licenseArray;
+ LLS_HANDLE m_hLls;
+ BOOL m_bLicensesRefreshed;
+ CString m_strSourceToUse;
+ CString m_strProductName;
+ CString m_strServerName;
+ CString m_strVendor;
+ CImageList m_smallImages;
+ DWORD m_dwRemoveFlags;
+
+// Overrides
+ // ClassWizard generated virtual function overrides
+ //{{AFX_VIRTUAL(CCertRemoveSelectDlg)
+ public:
+ virtual void WinHelp(DWORD dwData, UINT nCmd = HELP_CONTEXT);
+ protected:
+ virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
+ //}}AFX_VIRTUAL
+
+public:
+ DWORD CertificateRemove( LPCSTR pszServerName, LPCSTR pszProductName, LPCSTR pszVendor, DWORD dwFlags, LPCSTR pszSourceToUse );
+
+ void ResetLicenses();
+ BOOL RefreshLicenses();
+ BOOL RefreshCertificateList();
+ DWORD RemoveSelectedCertificate();
+
+ BOOL ConnectServer();
+ NTSTATUS ConnectTo( LPTSTR pszServerName, PLLS_HANDLE phLls );
+
+// Implementation
+protected:
+
+ // Generated message map functions
+ //{{AFX_MSG(CCertRemoveSelectDlg)
+ afx_msg void OnHelp();
+ afx_msg void OnColumnClickCertificateList(NMHDR* pNMHDR, LRESULT* pResult);
+ afx_msg void OnGetDispInfoCertificateList(NMHDR* pNMHDR, LRESULT* pResult);
+ afx_msg void OnDeltaPosSpinLicenses(NMHDR* pNMHDR, LRESULT* pResult);
+ virtual void OnOK();
+ virtual BOOL OnInitDialog();
+ afx_msg void OnDblClkCertificateList(NMHDR* pNMHDR, LRESULT* pResult);
+ afx_msg void OnReturnCertificateList(NMHDR* pNMHDR, LRESULT* pResult);
+ afx_msg void OnDestroy();
+ afx_msg void OnClickCertificateList(NMHDR* pNMHDR, LRESULT* pResult);
+ afx_msg void OnKeyDownCertificateList(NMHDR* pNMHDR, LRESULT* pResult);
+ afx_msg void OnRefresh();
+ //}}AFX_MSG
+ DECLARE_MESSAGE_MAP()
+};
+
+#define LVID_REMOVE_SERIAL_NUMBER 0
+#define LVID_REMOVE_PRODUCT_NAME 1
+#define LVID_REMOVE_LICENSE_MODE 2
+#define LVID_REMOVE_NUM_LICENSES 3
+#define LVID_REMOVE_SOURCE 4
+
+#define LVID_REMOVE_TOTAL_COLUMNS 5
+
+#define LVCX_REMOVE_SERIAL_NUMBER 20
+#define LVCX_REMOVE_PRODUCT_NAME 35
+#define LVCX_REMOVE_LICENSE_MODE 16
+#define LVCX_REMOVE_NUM_LICENSES 10
+#define LVCX_REMOVE_SOURCE -1
+
+int CALLBACK CompareLicenses(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort);
+
diff --git a/private/net/svcdlls/lls/ccfapi32/res/ccfapi32.rc2 b/private/net/svcdlls/lls/ccfapi32/res/ccfapi32.rc2
new file mode 100644
index 000000000..15a197155
--- /dev/null
+++ b/private/net/svcdlls/lls/ccfapi32/res/ccfapi32.rc2
@@ -0,0 +1,24 @@
+//
+// CCFAPI32.RC2 - resources Microsoft Visual C++ does not edit directly
+//
+
+#ifdef APSTUDIO_INVOKED
+ #error this file is not editable by Microsoft Visual C++
+#endif //APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+// Add manually edited resources here...
+
+#include <winver.h>
+#include <ntverp.h>
+
+#define VER_FILETYPE VFT_APP
+#define VER_FILESUBTYPE VFT2_UNKNOWN
+#define VER_FILEDESCRIPTION_STR "License Certificate API"
+#define VER_INTERNALNAME_STR "CCFAPI32.DLL"
+#define VER_ORIGINALFILENAME_STR "CCFAPI32.DLL"
+
+#include "common.ver"
+
+/////////////////////////////////////////////////////////////////////////////
diff --git a/private/net/svcdlls/lls/ccfapi32/res/license.ico b/private/net/svcdlls/lls/ccfapi32/res/license.ico
new file mode 100644
index 000000000..2570c2c25
--- /dev/null
+++ b/private/net/svcdlls/lls/ccfapi32/res/license.ico
Binary files differ
diff --git a/private/net/svcdlls/lls/ccfapi32/res/smicons.bmp b/private/net/svcdlls/lls/ccfapi32/res/smicons.bmp
new file mode 100644
index 000000000..08fd32c5c
--- /dev/null
+++ b/private/net/svcdlls/lls/ccfapi32/res/smicons.bmp
Binary files differ
diff --git a/private/net/svcdlls/lls/ccfapi32/res/warning.ico b/private/net/svcdlls/lls/ccfapi32/res/warning.ico
new file mode 100644
index 000000000..e8e1f6fba
--- /dev/null
+++ b/private/net/svcdlls/lls/ccfapi32/res/warning.ico
Binary files differ
diff --git a/private/net/svcdlls/lls/ccfapi32/resource.h b/private/net/svcdlls/lls/ccfapi32/resource.h
new file mode 100644
index 000000000..9ce11dde3
--- /dev/null
+++ b/private/net/svcdlls/lls/ccfapi32/resource.h
@@ -0,0 +1,105 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Developer Studio generated include file.
+// Used by CCFApi.rc
+//
+
+// special case error messages
+#define IDS_ERROR_DROPPED_LINK 2000
+#define IDS_ERROR_NO_RPC_SERVER 2001
+#define IDS_ERROR_UNSUCCESSFUL 2002
+#define IDS_ERROR_DOWNLEVEL_SERVER 2003
+#define IDS_ERROR_ACCESS_DENIED 2004
+#define IDS_ERROR_CERTIFICATE_EXPIRED 2005
+
+// dialog messages
+#define IDS_BAD_ACTIVATION_CODE 2100
+#define IDS_NO_PRODUCT_SEND_TO_ENTERPRISE 2101
+#define IDS_PER_SEAT_SEND_TO_ENTERPRISE 2102
+#define IDS_PER_SERVER_APP_NOT_INSTALLED 2103
+#define IDS_PER_SEAT_CHOSEN_SEND_TO_ENTERPRISE 2104
+#define IDS_NOT_ENOUGH_LICENSES_ON_CERTIFICATE 2105
+#define IDS_INVALID_NUM_LICENSES 2106
+#define IDS_REMOVE_CERTIFICATE_CONFIRM 2107
+#define IDS_CERT_SOURCE_NOT_AVAILABLE 2108
+#define IDS_ENTERPRISE_SERVER_BACKLEVEL_CANT_ADD_CERT 2109
+#define IDS_LOCAL_LICENSES_ALREADY_INSTALLED 2110
+#define IDS_NET_CERTIFICATE_TARGET_ENTRY 2111
+#define IDS_NET_LICENSES_ALREADY_INSTALLED_ON 2112
+#define IDS_NET_LICENSES_ALREADY_INSTALLED 2113
+#define IDS_PER_SEAT_LICENSING_1 2114
+#define IDS_PER_SERVER_LICENSING_1 2115
+#define IDS_REMOVE_INVALID_NUM_LICENSES 2116
+#define IDS_LICENSE_MODE_NOT_ALLOWED 2117
+#define IDS_NO_PRODUCT_CERTIFICATE_SOURCES 2118
+
+// certificate removal comments
+#define IDS_NO_REMOVE_COMMENT 2200
+#define IDS_PAPER_REMOVE_COMMENT 2201
+
+// dialog data values
+#define IDS_NO_CERTIFICATE_SOURCE_NAME 2300
+#define IDS_SOURCE_NONE 2301
+#define IDS_LICENSE_MODE_EITHER 2302
+#define IDS_LICENSE_MODE_PER_SEAT 2303
+#define IDS_LICENSE_MODE_PER_SERVER 2304
+#define IDS_LICENSE_MODE_UNKNOWN 2305
+
+// dialog data headers
+#define IDS_SERIAL_NUMBER 2400
+#define IDS_PRODUCT_NAME 2401
+#define IDS_QUANTITY 2402
+#define IDS_SOURCE 2403
+#define IDS_LICENSE_MODE 2404
+
+// icons
+#define IDI_LICENSE 2500
+#define IDI_PHONE 2501
+#define IDI_MY_WARNING 2502
+
+// dialogs
+#define IDD_CERT_SOURCE_SELECT 2600
+#define IDD_CERT_SOURCE_PAPER 2601
+#define IDD_CERT_REMOVE_SELECT 2602
+#define IDD_NEW_LICENSE 2603
+#define IDD_PER_SERVER_LICENSING 2604
+#define IDD_PER_SEAT_LICENSING 2605
+
+// bitmaps
+#define IDB_SMALL_ICONS 2700
+
+// dialog controls
+#define IDC_CERT_SOURCE 2800
+#define IDC_PRODUCT_NAME 2801
+#define IDC_PER_SEAT 2802
+#define IDC_VENDOR 2803
+#define IDC_PER_SERVER 2804
+#define IDC_SERIAL_NUMBER 2805
+#define IDC_KEY_CODE 2806
+#define IDC_ALL_LICENSES 2807
+#define IDC_ACTIVATION_CODE 2808
+#define IDC_COMMENT 2809
+#define IDC_MY_HELP 2810
+#define IDC_SOME_LICENSES 2811
+#define IDC_SPIN_LICENSES 2812
+#define IDC_NUM_LICENSES 2813
+#define IDC_CERTIFICATE_LIST 2814
+#define IDC_NEW_LICENSE_PRODUCT 2815
+#define IDC_NEW_LICENSE_COMMENT 2816
+#define IDC_NEW_LICENSE_QUANTITY 2817
+#define IDC_NEW_LICENSE_SPIN 2818
+#define IDC_PER_SERVER_STATIC_CLIENTS 2819
+#define IDC_PER_SERVER_AGREE 2820
+#define IDC_PER_SEAT_AGREE 2821
+#define IDC_PER_SEAT_STATIC_CLIENTS 2822
+#define IDC_REFRESH 2823
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 133
+#define _APS_NEXT_COMMAND_VALUE 32771
+#define _APS_NEXT_CONTROL_VALUE 1017
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/private/net/svcdlls/lls/ccfapi32/source.cpp b/private/net/svcdlls/lls/ccfapi32/source.cpp
new file mode 100644
index 000000000..9fa4a12f5
--- /dev/null
+++ b/private/net/svcdlls/lls/ccfapi32/source.cpp
@@ -0,0 +1,706 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ source.cpp
+
+Abstract:
+
+ Select certificate source dialog implementation.
+
+Author:
+
+ Jeff Parham (jeffparh) 13-Dec-1995
+
+Revision History:
+
+--*/
+
+
+#include "stdafx.h"
+#include "ccfapi.h"
+#include "source.h"
+#include "paper.h"
+#include "nlicdlg.h"
+
+#ifdef _DEBUG
+#undef THIS_FILE
+static char BASED_CODE THIS_FILE[] = __FILE__;
+#endif
+
+
+// 3.51-style
+static CString l_strOldEntryName;
+static const DWORD l_dwOldEntryIndex = (DWORD) (-1L);
+
+
+CCertSourceSelectDlg::CCertSourceSelectDlg(CWnd* pParent /*=NULL*/)
+ : CDialog(CCertSourceSelectDlg::IDD, pParent)
+
+/*++
+
+Routine Description:
+
+ Constructor for dialog.
+
+Arguments:
+
+ pParent - owner window.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ //{{AFX_DATA_INIT(CCertSourceSelectDlg)
+ m_strSource = _T("");
+ //}}AFX_DATA_INIT
+
+ m_dwEnterFlags = 0;
+ m_pszProductName = NULL;
+ m_pszServerName = NULL;
+ m_pszVendor = NULL;
+
+ l_strOldEntryName.LoadString( IDS_NO_CERTIFICATE_SOURCE_NAME );
+
+ m_hLls = NULL;
+}
+
+
+CCertSourceSelectDlg::~CCertSourceSelectDlg()
+
+/*++
+
+Routine Description:
+
+ Destructor for dialog.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ if ( NULL != m_hLls )
+ {
+ LlsClose( m_hLls );
+ }
+}
+
+
+void CCertSourceSelectDlg::DoDataExchange(CDataExchange* pDX)
+
+/*++
+
+Routine Description:
+
+ Called by framework to exchange dialog data.
+
+Arguments:
+
+ pDX - data exchange object.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ CDialog::DoDataExchange(pDX);
+ //{{AFX_DATA_MAP(CCertSourceSelectDlg)
+ DDX_Control(pDX, IDC_CERT_SOURCE, m_cboxSource);
+ DDX_CBString(pDX, IDC_CERT_SOURCE, m_strSource);
+ //}}AFX_DATA_MAP
+}
+
+
+BEGIN_MESSAGE_MAP(CCertSourceSelectDlg, CDialog)
+ //{{AFX_MSG_MAP(CCertSourceSelectDlg)
+ ON_BN_CLICKED(IDC_MY_HELP, OnHelp)
+ ON_WM_DESTROY()
+ //}}AFX_MSG_MAP
+END_MESSAGE_MAP()
+
+
+BOOL CCertSourceSelectDlg::OnInitDialog()
+
+/*++
+
+Routine Description:
+
+ Handler for WM_INITDIALOG.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ Returns false if focus set manually.
+
+--*/
+
+{
+ CDialog::OnInitDialog();
+
+ GetSourceList();
+
+ m_cboxSource.SetCurSel( 0 );
+
+ return TRUE;
+}
+
+
+void CCertSourceSelectDlg::OnOK()
+
+/*++
+
+Routine Description:
+
+ Handler for BN_CLICKED of OK.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ if ( NULL != GetParent() )
+ GetParent()->EnableWindow();
+
+ ShowWindow( FALSE );
+
+ if ( ERROR_SUCCESS == CallCertificateSource( m_cboxSource.GetItemData( m_cboxSource.GetCurSel() ) ) )
+ CDialog::OnOK();
+ else
+ ShowWindow( TRUE );
+}
+
+
+void CCertSourceSelectDlg::OnHelp()
+
+/*++
+
+Routine Description:
+
+ Handler for help button click.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ WinHelp( IDD, HELP_CONTEXT );
+}
+
+
+void CCertSourceSelectDlg::WinHelp(DWORD dwData, UINT nCmd)
+
+/*++
+
+Routine Description:
+
+ Call WinHelp for this dialog.
+
+Arguments:
+
+ dwData (DWORD)
+ nCmd (UINT)
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ BOOL ok = ::WinHelp( m_hWnd, theApp.GetHelpFileName(), nCmd, dwData );
+ ASSERT( ok );
+}
+
+void CCertSourceSelectDlg::OnDestroy()
+
+/*++
+
+Routine Description:
+
+ Handler for WM_DESTROY.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ WinHelp( 0, HELP_QUIT );
+
+ CDialog::OnDestroy();
+}
+
+
+void CCertSourceSelectDlg::GetSourceList()
+
+/*++
+
+Routine Description:
+
+ Insert list of valid certificate sources into list box.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ BOOL ok = TRUE;
+ int nCboxIndex;
+
+ if ( NULL == m_pszProductName )
+ {
+ // otherwise we know that the product is secure, otherwise it would have
+ // been handed to the unsecure product entry dialog already, and we
+ // wouldn't offer to let the user use the unsecure entry dialog
+
+ // add standard non-secure certificate source to possible choices
+ nCboxIndex = m_cboxSource.AddString( l_strOldEntryName );
+
+ ok = ( 0 <= nCboxIndex )
+ && ( CB_ERR != m_cboxSource.SetItemData( nCboxIndex, l_dwOldEntryIndex ) );
+ }
+
+ if ( ok
+ && ConnectServer()
+ && LlsCapabilityIsSupported( m_hLls, LLS_CAPABILITY_SECURE_CERTIFICATES ) )
+ {
+ // secure certificates supported on the target server (post-3.51 license server)
+
+ // add secure certificate sources to source list
+ for ( int nSourceIndex=0; ok && ( nSourceIndex < m_cslSourceList.GetNumSources() ); nSourceIndex++ )
+ {
+ nCboxIndex = m_cboxSource.AddString( m_cslSourceList.GetSourceDisplayName( nSourceIndex ) );
+
+ if ( nCboxIndex < 0 )
+ {
+ // couldn't add string to combo box
+ ok = FALSE;
+ }
+ else
+ {
+ // string added; associate index of source with it
+ ok = ( CB_ERR != m_cboxSource.SetItemData( nCboxIndex, nSourceIndex ) );
+ }
+ }
+ }
+
+ if ( !ok )
+ {
+ theApp.SetLastError( ERROR_NOT_ENOUGH_MEMORY );
+ theApp.DisplayLastError();
+ EndDialog( IDABORT );
+ }
+ else if ( m_cboxSource.GetCount() == 0 )
+ {
+ AfxMessageBox( IDS_NO_PRODUCT_CERTIFICATE_SOURCES, MB_OK | MB_ICONSTOP, 0 );
+ EndDialog( IDABORT );
+ }
+}
+
+
+DWORD CCertSourceSelectDlg::CallCertificateSource( int nIndex )
+
+/*++
+
+Routine Description:
+
+ Call the certificate source with the specified index into the source list.
+
+Arguments:
+
+ nIndex (int)
+
+Return Values:
+
+ ERROR_SUCCESS
+ ERROR_SERVICE_NOT_FOUND
+ Win error
+
+--*/
+
+{
+ DWORD dwError = ERROR_SERVICE_NOT_FOUND;
+
+ if ( l_dwOldEntryIndex == nIndex )
+ {
+ dwError = NoCertificateEnter( m_hWnd, m_pszServerName, m_pszProductName, m_pszVendor, m_dwEnterFlags );
+ }
+ else
+ {
+ HMODULE hDll;
+
+ hDll = ::LoadLibrary( m_cslSourceList.GetSourceImagePath( nIndex ) );
+
+ if ( NULL == hDll )
+ {
+ dwError = GetLastError();
+ theApp.SetLastError( dwError );
+ theApp.DisplayLastError();
+ }
+ else
+ {
+ CHAR szExportName[ 256 ];
+ PCCF_ENTER_API pfn;
+
+ wsprintfA( szExportName, "%lsCertificateEnter", m_cslSourceList.GetSourceName( nIndex ) );
+
+ pfn = (PCCF_ENTER_API) GetProcAddress( hDll, szExportName );
+
+ if ( NULL == pfn )
+ {
+ dwError = GetLastError();
+ theApp.SetLastError( dwError );
+ theApp.DisplayLastError();
+ }
+ else
+ {
+ dwError = (*pfn)( m_hWnd, m_pszServerName, m_pszProductName, m_pszVendor, m_dwEnterFlags );
+ }
+
+ ::FreeLibrary( hDll );
+ }
+ }
+
+ return dwError;
+}
+
+
+void CCertSourceSelectDlg::AbortDialogIfNecessary()
+
+/*++
+
+Routine Description:
+
+ Display error message and abort dialog if connection lost.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ theApp.DisplayLastError();
+
+ if ( theApp.IsConnectionDropped() )
+ {
+ EndDialog( IDABORT );
+ }
+}
+
+
+BOOL CCertSourceSelectDlg::ConnectServer()
+
+/*++
+
+Routine Description:
+
+ Establish a connection to the license service on the target server.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ BOOL.
+
+--*/
+
+{
+ NTSTATUS nt;
+
+ if ( NULL == m_hLls )
+ {
+ LPTSTR pszUniServerName = NULL;
+
+ if ( NULL == m_pszServerName )
+ {
+ pszUniServerName = NULL;
+ nt = STATUS_SUCCESS;
+ }
+ else
+ {
+ pszUniServerName = (LPTSTR) LocalAlloc( LMEM_FIXED, sizeof( TCHAR ) * ( 1 + strlen( m_pszServerName ) ) );
+
+ if ( NULL == pszUniServerName )
+ {
+ nt = ERROR_NOT_ENOUGH_MEMORY;
+ theApp.SetLastError( (DWORD) nt );
+ }
+ else
+ {
+ wsprintf( pszUniServerName, TEXT( "%hs" ), m_pszServerName );
+ nt = STATUS_SUCCESS;
+ }
+ }
+
+ if ( STATUS_SUCCESS == nt )
+ {
+ nt = ConnectTo( pszUniServerName, &m_hLls );
+ }
+
+ if ( NULL != pszUniServerName )
+ {
+ LocalFree( pszUniServerName );
+ }
+ }
+
+ if ( NULL == m_hLls )
+ {
+ theApp.DisplayLastError();
+
+ if ( ( NULL != m_hWnd ) && IsWindow( m_hWnd ) )
+ {
+ EndDialog( IDABORT );
+ }
+ }
+
+ return ( NULL != m_hLls );
+}
+
+
+NTSTATUS CCertSourceSelectDlg::ConnectTo( LPTSTR pszServerName, PLLS_HANDLE phLls )
+
+/*++
+
+Routine Description:
+
+ Establish a connection to the license service on the given server.
+
+Arguments:
+
+ pszServerName (CString)
+ The target server. An empty value indicates the local server.
+ phLls (PLLS_HANDLE)
+ On return, holds the handle to the standard LLS RPC.
+
+Return Values:
+
+ STATUS_SUCCESS or NT status code.
+
+--*/
+
+{
+ NTSTATUS nt;
+
+ nt = ::LlsConnect( pszServerName, phLls );
+ theApp.SetLastLlsError( nt );
+
+ if ( STATUS_SUCCESS != nt )
+ {
+ *phLls = NULL;
+ }
+
+ return nt;
+}
+
+
+DWORD CCertSourceSelectDlg::CertificateEnter( HWND hWndParent, LPCSTR pszServerName, LPCSTR pszProductName, LPCSTR pszVendor, DWORD dwFlags, LPCSTR pszSourceToUse )
+
+/*++
+
+Routine Description:
+
+ Display a dialog allowing the user to enter a license certificate
+ into the system.
+
+Arguments:
+
+ pszServerName (LPCSTR)
+ Name of the server for which licenses are to be installed. Note that
+ this may not be the same as the server on which licenses are actually
+ installed, as, for example, per seat licenses are always installed on
+ the enterprise server. A NULL value indicates the local server.
+ pszProductName (LPCSTR)
+ Product for which licenses are to be installed. A NULL value indicates
+ that the user should be allowed to choose.
+ pszVendor (LPCSTR)
+ Name of the vendor of the product. This value should be NULL if
+ pszProductName is NULL, and should be non-NULL if pszProductName is
+ non-NULL.
+ dwFlags (DWORD)
+ A bitfield containing one or more of the following:
+ CCF_ENTER_FLAG_PER_SEAT_ONLY
+ Allow the user to enter only per seat licenses. Not valid in
+ combination with CCF_ENTER_FLAG_PER_SERVER_ONLY.
+ CCF_ENTER_FLAG_PER_SERVER_ONLY
+ Allow the user to enter only per server licenses. Not valid in
+ combination with CCF_ENTER_FLAG_PER_SEAT_ONLY.
+ pszSourceToUse (LPCSTR)
+ Name of the secure certificate source to use to install the certificate,
+ e.g., "Paper". A NULL value indicates that the user should be allowed
+ to choose.
+
+Return Value:
+
+ ERROR_SUCCESS (A certificate was successfully entered into the system.)
+ ERROR_CANCELLED (The user cancelled without installing a certificate.)
+ other Win error
+
+--*/
+
+{
+ DWORD dwError;
+
+ m_pszServerName = pszServerName;
+ m_pszProductName = pszProductName;
+ m_pszVendor = pszVendor;
+ m_dwEnterFlags = dwFlags;
+
+ if ( pszSourceToUse != NULL )
+ {
+ CString strSourceToUse = pszSourceToUse;
+ int nSrcIndex;
+
+ for ( nSrcIndex = 0; nSrcIndex < m_cslSourceList.GetNumSources(); nSrcIndex++ )
+ {
+ if ( !strSourceToUse.CompareNoCase( m_cslSourceList.GetSourceDisplayName( nSrcIndex ) ) )
+ {
+ // use this certificate source
+ break;
+ }
+ }
+
+ if ( m_cslSourceList.GetNumSources() == nSrcIndex )
+ {
+ // requested certificate source is not available
+ dwError = ERROR_SERVICE_NOT_FOUND;
+ }
+ else
+ {
+ // don't display dialog, just use the indicated source
+ dwError = CallCertificateSource( nSrcIndex );
+ }
+ }
+ else if ( pszProductName != NULL )
+ {
+ // find out if this is a secure product
+ if ( !ConnectServer() )
+ {
+ dwError = theApp.GetLastError();
+ }
+ else
+ {
+ BOOL bProductIsSecure;
+
+ if ( !LlsCapabilityIsSupported( m_hLls, LLS_CAPABILITY_SECURE_CERTIFICATES ) )
+ {
+ // no extended RPC, so all products on this server must be unsecure
+ bProductIsSecure = FALSE;
+ dwError = ERROR_SUCCESS;
+ }
+ else
+ {
+ LPTSTR pszUniProductName;
+
+ pszUniProductName = (LPTSTR) LocalAlloc( LMEM_FIXED, sizeof( TCHAR ) * ( 1 + strlen( pszProductName ) ) );
+
+ if ( NULL == pszUniProductName )
+ {
+ dwError = ERROR_NOT_ENOUGH_MEMORY;
+ theApp.SetLastError( dwError );
+ theApp.DisplayLastError();
+ }
+ else
+ {
+ dwError = ERROR_SUCCESS;
+
+ wsprintf( pszUniProductName, TEXT( "%hs" ), pszProductName );
+
+ BOOL bIsSecure;
+
+ bProductIsSecure = ( STATUS_SUCCESS == ::LlsProductSecurityGet( m_hLls, pszUniProductName, &bIsSecure ) )
+ && bIsSecure;
+
+ LocalFree( pszUniProductName );
+ }
+ }
+
+ if ( ERROR_SUCCESS == dwError )
+ {
+ if ( !bProductIsSecure )
+ {
+ // unsecure product; no need to select source
+ dwError = NoCertificateEnter( hWndParent, pszServerName, pszProductName, pszVendor, dwFlags );
+ }
+ else if ( 1 == m_cslSourceList.GetNumSources() )
+ {
+ // product is secure and there is only one source to choose from; use it!
+ dwError = CallCertificateSource( 0 );
+ }
+ else if ( IDOK == DoModal() )
+ {
+ dwError = ERROR_SUCCESS;
+ }
+ else
+ {
+ dwError = ERROR_CANCELLED;
+ }
+ }
+ }
+ }
+ else if ( !ConnectServer() )
+ {
+ dwError = theApp.GetLastError();
+ }
+ else if ( !LlsCapabilityIsSupported( m_hLls, LLS_CAPABILITY_SECURE_CERTIFICATES )
+ || !m_cslSourceList.GetNumSources() )
+ {
+ // secure certificates not supported or no sources available; use unsecure source
+ dwError = NoCertificateEnter( hWndParent, pszServerName, pszProductName, pszVendor, dwFlags );
+ }
+ else if ( IDOK == DoModal() )
+ {
+ dwError = ERROR_SUCCESS;
+ }
+ else
+ {
+ dwError = ERROR_CANCELLED;
+ }
+
+ return dwError;
+}
diff --git a/private/net/svcdlls/lls/ccfapi32/source.h b/private/net/svcdlls/lls/ccfapi32/source.h
new file mode 100644
index 000000000..ae54e1035
--- /dev/null
+++ b/private/net/svcdlls/lls/ccfapi32/source.h
@@ -0,0 +1,83 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ source.h
+
+Abstract:
+
+ Select certificate source dialog prototype.
+
+Author:
+
+ Jeff Parham (jeffparh) 13-Dec-1995
+
+Revision History:
+
+--*/
+
+
+#include "afxwin.h"
+#include "srclist.h"
+#include "resource.h"
+
+/////////////////////////////////////////////////////////////////////////////
+// CCertSourceSelect dialog
+
+class CCertSourceSelectDlg : public CDialog
+{
+public:
+ CCertSourceSelectDlg(CWnd* pParent = NULL);
+ ~CCertSourceSelectDlg();
+
+ DWORD CertificateEnter( HWND hWndParent, LPCSTR pszServerName, LPCSTR pszProductName, LPCSTR pszVendor, DWORD dwFlags, LPCSTR pszSourceToUse );
+
+ void AbortDialogIfNecessary();
+
+ DWORD CallCertificateSource( int nIndex );
+
+ BOOL ConnectServer();
+ NTSTATUS ConnectTo( LPTSTR pszServerName, PLLS_HANDLE phLls );
+
+ void GetSourceList();
+
+ LLS_HANDLE m_hLls;
+
+ LPCSTR m_pszServerName;
+ LPCSTR m_pszProductName;
+ LPCSTR m_pszVendor;
+ DWORD m_dwEnterFlags;
+
+ CCertSourceList m_cslSourceList;
+
+// Dialog Data
+ //{{AFX_DATA(CCertSourceSelectDlg)
+ enum { IDD = IDD_CERT_SOURCE_SELECT };
+ CComboBox m_cboxSource;
+ CString m_strSource;
+ //}}AFX_DATA
+
+// Overrides
+ // ClassWizard generated virtual function overrides
+ //{{AFX_VIRTUAL(CCertSourceSelectDlg)
+ public:
+ virtual void WinHelp(DWORD dwData, UINT nCmd = HELP_CONTEXT);
+ protected:
+ virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
+ //}}AFX_VIRTUAL
+
+// Implementation
+protected:
+
+ // Generated message map functions
+ //{{AFX_MSG(CCertSourceSelectDlg)
+ virtual BOOL OnInitDialog();
+ virtual void OnOK();
+ afx_msg void OnHelp();
+ afx_msg void OnDestroy();
+ afx_msg BOOL OnHelpInfo(HELPINFO* pHelpInfo);
+ //}}AFX_MSG
+ DECLARE_MESSAGE_MAP()
+};
diff --git a/private/net/svcdlls/lls/ccfapi32/sources b/private/net/svcdlls/lls/ccfapi32/sources
new file mode 100644
index 000000000..7d0ca9d63
--- /dev/null
+++ b/private/net/svcdlls/lls/ccfapi32/sources
@@ -0,0 +1,51 @@
+MAJORCOMP=lls
+MINORCOMP=ccfapi32
+
+TARGETNAME=ccfapi32
+TARGETPATH=$(BASEDIR)\public\sdk\lib
+TARGETTYPE=DYNLINK
+
+USE_MFCUNICODE=1
+
+PRECOMPILED_INCLUDE=stdafx.h
+PRECOMPILED_CXX=1
+
+C_DEFINES=-DUNICODE -D_UNICODE
+
+DLLENTRY=_DllMainCRTStartup
+
+TARGETLIBS= \
+ $(BASEDIR)\public\sdk\lib\*\llsrpc.lib \
+ $(BASEDIR)\public\sdk\lib\*\netapi32.lib \
+ $(BASEDIR)\public\sdk\lib\*\kernel32.lib \
+ $(BASEDIR)\public\sdk\lib\*\advapi32.lib \
+ $(BASEDIR)\public\sdk\lib\*\uuid.lib \
+ $(BASEDIR)\public\sdk\lib\*\ole32.lib \
+ $(BASEDIR)\public\sdk\lib\*\oleaut32.lib \
+ $(BASEDIR)\public\sdk\lib\*\comdlg32.lib \
+ $(BASEDIR)\public\sdk\lib\*\comctl32.lib \
+ $(BASEDIR)\public\sdk\lib\*\winspool.lib \
+ $(BASEDIR)\public\sdk\lib\*\shell32.lib \
+ $(BASEDIR)\public\sdk\lib\*\user32.lib \
+ $(BASEDIR)\public\sdk\lib\*\gdi32.lib \
+ $(BASEDIR)\public\sdk\lib\*\ntdll.lib
+
+INCLUDES= \
+ $(BASEDIR)\private\inc; \
+ $(BASEDIR)\private\net\inc; \
+ $(BASEDIR)\private\net\svcdlls\lls\inc
+
+SOURCES= \
+ ccfapi.cpp \
+ ccfapi32.rc \
+ exports.cpp \
+ licobj.cpp \
+ md4c.cpp \
+ nlicdlg.cpp \
+ paper.cpp \
+ pseatdlg.cpp \
+ psrvdlg.cpp \
+ remdlg.cpp \
+ source.cpp \
+ srclist.cpp \
+ utils.cpp
diff --git a/private/net/svcdlls/lls/ccfapi32/srclist.cpp b/private/net/svcdlls/lls/ccfapi32/srclist.cpp
new file mode 100644
index 000000000..9160c8591
--- /dev/null
+++ b/private/net/svcdlls/lls/ccfapi32/srclist.cpp
@@ -0,0 +1,393 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ srclist.cpp
+
+Abstract:
+
+ Certificate source list object implementation.
+
+Author:
+
+ Jeff Parham (jeffparh) 15-Dec-1995
+
+Revision History:
+
+--*/
+
+
+#include "stdafx.h"
+#include "srclist.h"
+
+// key name under which the individual source key names may be found
+#define KEY_CERT_SOURCE_LIST "Software\\LSAPI\\Microsoft\\CertificateSources"
+
+// value name for the path to the certificate source DLL (REG_EXPAND_SZ)
+#define VALUE_CERT_SOURCE_PATH "ImagePath"
+
+// value name for the display name of the certificate source
+#define VALUE_CERT_DISPLAY_NAME "DisplayName"
+
+
+CCertSourceList::CCertSourceList()
+
+/*++
+
+Routine Description:
+
+ Constructor for object.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ m_dwNumSources = 0;
+ m_ppcsiSourceList = NULL;
+
+ RefreshSources();
+}
+
+
+CCertSourceList::~CCertSourceList()
+
+/*++
+
+Routine Description:
+
+ Destructor for dialog.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ RemoveSources();
+}
+
+
+BOOL CCertSourceList::RefreshSources()
+
+/*++
+
+Routine Description:
+
+ Refresh source list from configuration stored in the registry.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ BOOL.
+
+--*/
+
+{
+ LONG lError;
+ LONG lEnumError;
+ HKEY hKeyCertSourceList;
+ int iSubKey;
+ HKEY hKeyCertSource;
+ DWORD cb;
+ BOOL ok;
+ PCERT_SOURCE_INFO pcsiSourceInfo;
+ DWORD cch;
+ TCHAR szExpImagePath[ _MAX_PATH ];
+
+ RemoveSources();
+
+ lError = RegOpenKeyEx( HKEY_LOCAL_MACHINE, TEXT( KEY_CERT_SOURCE_LIST ), 0, KEY_READ, &hKeyCertSourceList );
+
+ if ( ERROR_SUCCESS == lError )
+ {
+ iSubKey = 0;
+
+ do
+ {
+ ok = FALSE;
+
+ pcsiSourceInfo = (PCERT_SOURCE_INFO) LocalAlloc( LPTR, sizeof( *pcsiSourceInfo ) );
+
+ if ( NULL != pcsiSourceInfo )
+ {
+ // determine next certificate source
+ cch = sizeof( pcsiSourceInfo->szName ) / sizeof( pcsiSourceInfo->szName[0] );
+ lEnumError = RegEnumKeyEx( hKeyCertSourceList, iSubKey, pcsiSourceInfo->szName, &cch, NULL, NULL, NULL, NULL );
+ iSubKey++;
+
+ if ( ERROR_SUCCESS == lError )
+ {
+ // open certificate source's key
+ lError = RegOpenKeyEx( hKeyCertSourceList, pcsiSourceInfo->szName, 0, KEY_READ, &hKeyCertSource );
+
+ if ( ERROR_SUCCESS == lError )
+ {
+ // certificate source key opened; get its REG_EXPAND_SZ image path
+ cb = sizeof( szExpImagePath );
+ lError = RegQueryValueEx( hKeyCertSource, TEXT( VALUE_CERT_SOURCE_PATH ), NULL, NULL, (LPBYTE) szExpImagePath, &cb );
+
+ if ( ERROR_SUCCESS == lError )
+ {
+ // translate environment variables in path
+ cch = ExpandEnvironmentStrings( szExpImagePath, pcsiSourceInfo->szImagePath, sizeof( pcsiSourceInfo->szImagePath ) / sizeof( pcsiSourceInfo->szImagePath[0] ) );
+
+ if ( ( 0 != cch ) && ( cch < sizeof( pcsiSourceInfo->szImagePath ) / sizeof( pcsiSourceInfo->szImagePath[0] ) ) )
+ {
+ // get display name
+ cb = sizeof( pcsiSourceInfo->szDisplayName );
+ lError = RegQueryValueEx( hKeyCertSource, TEXT( VALUE_CERT_DISPLAY_NAME ), NULL, NULL, (LPBYTE) pcsiSourceInfo->szDisplayName, &cb );
+
+ if ( ERROR_SUCCESS != lError )
+ {
+ // default display name is the key name
+ lstrcpy( pcsiSourceInfo->szDisplayName, pcsiSourceInfo->szName );
+ }
+
+ // add the certificate source to our list
+ AddSource( pcsiSourceInfo );
+
+ ok = TRUE;
+ }
+ }
+
+ RegCloseKey( hKeyCertSource );
+ }
+ }
+
+ if ( !ok )
+ {
+ // an error occurred before saving our pointer; don't leak!
+ LocalFree( pcsiSourceInfo );
+ }
+ }
+
+ } while ( ( NULL != pcsiSourceInfo ) && ( ERROR_SUCCESS == lEnumError ) );
+
+ RegCloseKey( hKeyCertSourceList );
+ }
+
+ // 'salright
+ return TRUE;
+}
+
+
+BOOL CCertSourceList::RemoveSources()
+
+/*++
+
+Routine Description:
+
+ Free internal certificate source list.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ BOOL.
+
+--*/
+
+{
+ if ( NULL != m_ppcsiSourceList )
+ {
+ for ( DWORD i=0; i < m_dwNumSources; i++ )
+ {
+ LocalFree( m_ppcsiSourceList[i] );
+ }
+
+ LocalFree( m_ppcsiSourceList );
+ }
+
+ m_ppcsiSourceList = NULL;
+ m_dwNumSources = 0;
+
+ return TRUE;
+}
+
+
+LPCTSTR CCertSourceList::GetSourceName( int nIndex )
+
+/*++
+
+Routine Description:
+
+ Get the name (e.g., "Paper") of the source at the given index.
+
+Arguments:
+
+ nIndex (int)
+
+Return Values:
+
+ LPCTSTR.
+
+--*/
+
+{
+ LPTSTR pszName;
+
+ if ( ( nIndex < 0 ) || ( nIndex >= (int) m_dwNumSources ) )
+ {
+ pszName = NULL;
+ }
+ else
+ {
+ pszName = m_ppcsiSourceList[ nIndex ]->szName;
+ }
+
+ return pszName;
+}
+
+
+LPCTSTR CCertSourceList::GetSourceDisplayName( int nIndex )
+
+/*++
+
+Routine Description:
+
+ Get the display name (e.g., "Paper Certificate") of the source
+ at the given index.
+
+Arguments:
+
+ nIndex (int)
+
+Return Values:
+
+ LPCTSTR.
+
+--*/
+
+{
+ LPTSTR pszDisplayName;
+
+ if ( ( nIndex < 0 ) || ( nIndex >= (int) m_dwNumSources ) )
+ {
+ pszDisplayName = NULL;
+ }
+ else
+ {
+ pszDisplayName = m_ppcsiSourceList[ nIndex ]->szDisplayName;
+ }
+
+ return pszDisplayName;
+}
+
+
+LPCTSTR CCertSourceList::GetSourceImagePath( int nIndex )
+
+/*++
+
+Routine Description:
+
+ Get the image path name (e.g., "C:\WINNT35\SYSTEM32\CCFAPI32.DLL") of
+ the source at the given index.
+
+Arguments:
+
+ nIndex (int)
+
+Return Values:
+
+ LPCTSTR.
+
+--*/
+
+{
+ LPTSTR pszImagePath;
+
+ if ( ( nIndex < 0 ) || ( nIndex >= (int) m_dwNumSources ) )
+ {
+ pszImagePath = NULL;
+ }
+ else
+ {
+ pszImagePath = m_ppcsiSourceList[ nIndex ]->szImagePath;
+ }
+
+ return pszImagePath;
+}
+
+
+int CCertSourceList::GetNumSources()
+
+/*++
+
+Routine Description:
+
+ Get the number of certificate sources available.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ int.
+
+--*/
+
+{
+ return m_dwNumSources;
+}
+
+
+BOOL CCertSourceList::AddSource( PCERT_SOURCE_INFO pcsiNewSource )
+
+/*++
+
+Routine Description:
+
+ Add a source to the internal list.
+
+Arguments:
+
+ pcsiNewSource (PCERT_SOURCE_INFO)
+
+Return Values:
+
+ BOOL.
+
+--*/
+
+{
+ if ( 0 == m_dwNumSources )
+ {
+ m_ppcsiSourceList = (PCERT_SOURCE_INFO *) LocalAlloc( LMEM_FIXED, sizeof( pcsiNewSource ) );
+ }
+ else
+ {
+ m_ppcsiSourceList = (PCERT_SOURCE_INFO *) LocalReAlloc( m_ppcsiSourceList, ( 1 + m_dwNumSources ) * sizeof( pcsiNewSource ), 0 );
+ }
+
+ if ( NULL != m_ppcsiSourceList )
+ {
+ m_ppcsiSourceList[ m_dwNumSources ] = pcsiNewSource;
+ m_dwNumSources++;
+ }
+ else
+ {
+ m_dwNumSources = 0;
+ }
+
+ return ( NULL != m_ppcsiSourceList );
+}
diff --git a/private/net/svcdlls/lls/ccfapi32/srclist.h b/private/net/svcdlls/lls/ccfapi32/srclist.h
new file mode 100644
index 000000000..19d146a36
--- /dev/null
+++ b/private/net/svcdlls/lls/ccfapi32/srclist.h
@@ -0,0 +1,47 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ srclist.h
+
+Abstract:
+
+ Certificate source list object prototype.
+
+Author:
+
+ Jeff Parham (jeffparh) 15-Dec-1995
+
+Revision History:
+
+--*/
+
+
+typedef struct _CERT_SOURCE_INFO
+{
+ TCHAR szName[ 64 ];
+ TCHAR szDisplayName[ 64 ];
+ TCHAR szImagePath[ _MAX_PATH ];
+} CERT_SOURCE_INFO, *PCERT_SOURCE_INFO;
+
+class CCertSourceList
+{
+public:
+ CCertSourceList();
+ ~CCertSourceList();
+
+ BOOL RefreshSources();
+ LPCTSTR GetSourceName( int nIndex );
+ LPCTSTR GetSourceDisplayName( int nIndex );
+ LPCTSTR GetSourceImagePath( int nIndex );
+ int GetNumSources();
+
+private:
+ BOOL RemoveSources();
+ BOOL AddSource( PCERT_SOURCE_INFO pcsiNewSource );
+
+ PCERT_SOURCE_INFO * m_ppcsiSourceList;
+ DWORD m_dwNumSources;
+};
diff --git a/private/net/svcdlls/lls/ccfapi32/stdafx.h b/private/net/svcdlls/lls/ccfapi32/stdafx.h
new file mode 100644
index 000000000..cfc26a5de
--- /dev/null
+++ b/private/net/svcdlls/lls/ccfapi32/stdafx.h
@@ -0,0 +1,46 @@
+// stdafx.h : include file for standard system include files,
+// or project specific include files that are used frequently, but
+// are changed infrequently
+//
+
+// #define VC_EXTRALEAN // Exclude rarely-used stuff from Windows headers
+
+// #define _AFX_NO_OLE_SUPPORT
+// #define _AFX_NO_DB_SUPPORT
+#define _AFX_NO_DAO_SUPPORT
+// #define _AFX_NO_AFXCMN_SUPPORT
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#ifdef ASSERT
+# undef ASSERT
+#endif
+
+#include <afxwin.h> // MFC core and standard components
+#include <afxext.h> // MFC extensions
+
+#include <llsapi.h>
+
+#ifndef _AFX_NO_OLE_SUPPORT
+#include <afxole.h> // MFC OLE classes
+#include <afxodlgs.h> // MFC OLE dialog classes
+#include <afxdisp.h> // MFC OLE automation classes
+#endif // _AFX_NO_OLE_SUPPORT
+
+
+#ifndef _AFX_NO_DB_SUPPORT
+#include <afxdb.h> // MFC ODBC database classes
+#endif // _AFX_NO_DB_SUPPORT
+
+#ifndef _AFX_NO_DAO_SUPPORT
+#include <afxdao.h> // MFC DAO database classes
+#endif // _AFX_NO_DAO_SUPPORT
+
+#ifndef _AFX_NO_AFXCMN_SUPPORT
+#include <afxcmn.h> // MFC support for Windows 95 Common Controls
+#endif // _AFX_NO_AFXCMN_SUPPORT
+
+
+
+
diff --git a/private/net/svcdlls/lls/ccfapi32/utils.cpp b/private/net/svcdlls/lls/ccfapi32/utils.cpp
new file mode 100644
index 000000000..ffdec9ba0
--- /dev/null
+++ b/private/net/svcdlls/lls/ccfapi32/utils.cpp
@@ -0,0 +1,713 @@
+/*++
+
+Copyright (c) 1994-95 Microsoft Corporation
+
+Module Name:
+
+ utils.cpp
+
+Abstract:
+
+ Utiltities.
+
+Author:
+
+ Don Ryan (donryan) 04-Jan-1995
+
+Environment:
+
+ User Mode - Win32
+
+Revision History:
+
+ Jeff Parham (jeffparh) 12-Nov-1995
+ Copied from LLSMGR, stripped Tv (Tree view) functions,
+ removed OLE support
+
+--*/
+
+#include "stdafx.h"
+#include "ccfapi.h"
+#include "utils.h"
+
+#define _AFX_NO_OLE_SUPPORT
+
+//
+// List view utilities
+//
+
+void LvInitColumns(CListCtrl* pListCtrl, PLV_COLUMN_INFO plvColumnInfo)
+
+/*++
+
+Routine Description:
+
+ Initializes list view columns.
+
+Arguments:
+
+ pListCtrl - list control.
+ plvColumnInfo - column information.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ ASSERT(plvColumnInfo);
+ VALIDATE_OBJECT(pListCtrl, CListCtrl);
+
+ int nStringId;
+ CString strText;
+ LV_COLUMN lvColumn;
+
+ int nColumns = plvColumnInfo->nColumns;
+ PLV_COLUMN_ENTRY plvColumnEntry = plvColumnInfo->lvColumnEntry;
+
+ lvColumn.mask = LVCF_FMT|
+ LVCF_TEXT|
+ LVCF_SUBITEM;
+
+ lvColumn.fmt = LVCFMT_LEFT;
+
+ pListCtrl->SetRedraw(FALSE); // turn off drawing...
+
+ while (nColumns--)
+ {
+ lvColumn.iSubItem = plvColumnEntry->iSubItem;
+
+ if (nStringId = plvColumnEntry->nStringId)
+ {
+ strText.LoadString(nStringId);
+ }
+ else
+ {
+ strText = _T("");
+ }
+
+ lvColumn.pszText = strText.GetBuffer(0);
+
+ int nColumnInserted = pListCtrl->InsertColumn( lvColumn.iSubItem, &lvColumn );
+ ASSERT( -1 != nColumnInserted );
+
+ plvColumnEntry++;
+
+ strText.ReleaseBuffer();
+ }
+
+ SetDefaultFont(pListCtrl);
+
+ LvResizeColumns(pListCtrl, plvColumnInfo);
+
+ pListCtrl->SetRedraw(TRUE); // turn on drawing...
+}
+
+
+void LvResizeColumns(CListCtrl* pListCtrl, PLV_COLUMN_INFO plvColumnInfo)
+
+/*++
+
+Routine Description:
+
+ Resizes list view columns.
+
+Arguments:
+
+ pListCtrl - list control.
+ plvColumnInfo - column information.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ ASSERT(plvColumnInfo);
+ VALIDATE_OBJECT(pListCtrl, CListCtrl);
+
+ int nColumnWidth;
+ int nRelativeWidth;
+ int nEntireWidthSoFar = 0;
+ int nColumns = plvColumnInfo->nColumns;
+ PLV_COLUMN_ENTRY plvColumnEntry = plvColumnInfo->lvColumnEntry;
+
+ CRect clientRect;
+ pListCtrl->GetClientRect(clientRect);
+
+ pListCtrl->SetRedraw(FALSE); // turn off drawing...
+
+ while ((nRelativeWidth = plvColumnEntry->nRelativeWidth) != -1)
+ {
+ nColumnWidth = (nRelativeWidth * clientRect.Width()) / 100;
+ pListCtrl->SetColumnWidth(plvColumnEntry->iSubItem, nColumnWidth);
+ nEntireWidthSoFar += nColumnWidth;
+ plvColumnEntry++;
+ }
+
+ nColumnWidth = clientRect.Width() - nEntireWidthSoFar;
+ pListCtrl->SetColumnWidth(plvColumnEntry->iSubItem, nColumnWidth);
+
+ pListCtrl->SetRedraw(TRUE); // turn on drawing...
+}
+
+
+void LvChangeFormat(CListCtrl* pListCtrl, UINT nFormatId)
+
+/*++
+
+Routine Description:
+
+ Changes window style of list view.
+
+Arguments:
+
+ pListCtrl - list control.
+ nFormatId - format specification.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ VALIDATE_OBJECT(pListCtrl, CListCtrl);
+
+ DWORD dwStyle = ::GetWindowLong(pListCtrl->GetSafeHwnd(), GWL_STYLE);
+
+ pListCtrl->BeginWaitCursor();
+ pListCtrl->SetRedraw(FALSE); // turn off drawing...
+
+ if ((dwStyle & LVS_TYPEMASK) != nFormatId)
+ {
+ ::SetWindowLong(
+ pListCtrl->GetSafeHwnd(),
+ GWL_STYLE,
+ (dwStyle & ~LVS_TYPEMASK) | nFormatId
+ );
+ }
+
+ pListCtrl->SetRedraw(TRUE); // turn on drawing...
+ pListCtrl->EndWaitCursor();
+}
+
+
+LPVOID LvGetSelObj(CListCtrl* pListCtrl)
+
+/*++
+
+Routine Description:
+
+ Retrieves the object selected (assumes one) from list view.
+
+Arguments:
+
+ pListCtrl - list control.
+
+Return Values:
+
+ Same as LvGetNextObj.
+
+--*/
+
+{
+ int iItem = -1;
+ return LvGetNextObj(pListCtrl, &iItem);
+}
+
+
+LPVOID LvGetNextObj(CListCtrl* pListCtrl, LPINT piItem, int nType)
+
+/*++
+
+Routine Description:
+
+ Retrieves the next object selected from list view.
+
+Arguments:
+
+ pListCtrl - list control.
+ piItem - starting index (updated).
+ nType - specifies search criteria.
+
+Return Values:
+
+ Returns object pointer or null.
+
+--*/
+
+{
+ ASSERT(piItem);
+ VALIDATE_OBJECT(pListCtrl, CListCtrl);
+
+ LV_ITEM lvItem;
+
+ if ((lvItem.iItem = pListCtrl->GetNextItem(*piItem, nType)) != -1)
+ {
+ lvItem.mask = LVIF_PARAM;
+ lvItem.iSubItem = 0;
+
+ if (pListCtrl->GetItem(&lvItem))
+ {
+ *piItem = lvItem.iItem;
+ return (LPVOID)lvItem.lParam;
+ }
+ }
+
+ return NULL;
+}
+
+
+BOOL LvInsertObArray(CListCtrl* pListCtrl, PLV_COLUMN_INFO plvColumnInfo, CObArray* pObArray)
+
+/*++
+
+Routine Description:
+
+ Insert object array into list view.
+ Note list view must be unsorted and support LVN_GETDISPINFO.
+
+Arguments:
+
+ pListCtrl - list control.
+ plvColumnInfo - column info.
+ pObArray - object array.
+
+Return Values:
+
+ VT_BOOL.
+
+--*/
+
+{
+ VALIDATE_OBJECT(pObArray, CObArray);
+ VALIDATE_OBJECT(pListCtrl, CListCtrl);
+
+ ASSERT(plvColumnInfo);
+ ASSERT(pListCtrl->GetItemCount() == 0);
+
+ BOOL bItemsInserted = FALSE;
+
+ LV_ITEM lvItem;
+
+ lvItem.mask = LVIF_TEXT|
+ LVIF_PARAM|
+ LVIF_IMAGE;
+
+ lvItem.pszText = LPSTR_TEXTCALLBACK;
+ lvItem.cchTextMax = LPSTR_TEXTCALLBACK_MAX;
+ lvItem.iImage = I_IMAGECALLBACK;
+ lvItem.iSubItem = 0;
+
+ int iItem;
+ int iSubItem;
+
+ int nItems = pObArray->GetSize();
+ ASSERT(nItems != -1); // iItem is -1 if error...
+
+ pListCtrl->SetRedraw(FALSE); // turn off drawing...
+
+ pListCtrl->SetItemCount(nItems);
+
+ CObject* pObject = NULL;
+
+ for (iItem = 0; (-1 != iItem) && (iItem < nItems) && (pObject = pObArray->GetAt(iItem)); iItem++)
+ {
+ VALIDATE_OBJECT(pObject, CObject);
+
+ lvItem.iItem = iItem;
+ lvItem.lParam = (LPARAM)(LPVOID)pObject;
+
+ iItem = pListCtrl->InsertItem(&lvItem);
+ ASSERT((iItem == lvItem.iItem) || (iItem == -1));
+
+ if ( -1 != iItem )
+ {
+ for (iSubItem = 1; iSubItem < plvColumnInfo->nColumns; iSubItem++)
+ {
+ BOOL ok = pListCtrl->SetItemText(iItem, iSubItem, LPSTR_TEXTCALLBACK);
+ ASSERT( ok );
+ }
+ }
+ }
+
+ if (iItem == nItems)
+ {
+ bItemsInserted = TRUE;
+ VERIFY(pListCtrl->SetItemState(
+ 0,
+ LVIS_FOCUSED|
+ LVIS_SELECTED,
+ LVIS_FOCUSED|
+ LVIS_SELECTED
+ ));
+ }
+ else
+ {
+ theApp.SetLastError(ERROR_OUTOFMEMORY);
+ VERIFY(pListCtrl->DeleteAllItems());
+ }
+
+ LvResizeColumns(pListCtrl, plvColumnInfo);
+
+ pListCtrl->SetRedraw(TRUE); // turn on drawing...
+
+ return bItemsInserted;
+}
+
+
+BOOL
+LvRefreshObArray(
+ CListCtrl* pListCtrl,
+ PLV_COLUMN_INFO plvColumnInfo,
+ CObArray* pObArray
+ )
+
+/*++
+
+Routine Description:
+
+ Refresh object array in list view.
+
+Arguments:
+
+ pListCtrl - list control.
+ plvColumnInfo - column info.
+ pObArray - object array.
+
+Return Values:
+
+ VT_BOOL.
+
+--*/
+
+{
+ ASSERT(plvColumnInfo);
+ VALIDATE_OBJECT(pObArray, CObArray);
+ VALIDATE_OBJECT(pListCtrl, CListCtrl);
+
+ long nObjects = pObArray->GetSize();
+ long nObjectsInList = pListCtrl->GetItemCount();
+
+ if (!nObjects)
+ {
+ LvReleaseObArray(pListCtrl);
+ return TRUE;
+ }
+ else if (!nObjectsInList)
+ {
+ return LvInsertObArray(
+ pListCtrl,
+ plvColumnInfo,
+ pObArray
+ );
+ }
+
+ CObject* pObject;
+
+ int iObject = 0;
+ int iObjectInList = 0;
+
+ LV_ITEM lvItem;
+
+ pListCtrl->SetRedraw(FALSE); // turn off drawing...
+
+ while (nObjectsInList--)
+ {
+ lvItem.mask = LVIF_PARAM;
+ lvItem.iItem = iObjectInList;
+ lvItem.iSubItem = 0;
+
+ VERIFY(pListCtrl->GetItem(&lvItem));
+
+ pObject = (CObject*)lvItem.lParam;
+ VALIDATE_OBJECT(pObject, CObject);
+
+ if (iObject < nObjects)
+ {
+ pObject = pObArray->GetAt(iObject++);
+ VALIDATE_OBJECT(pObject, CObject);
+
+ lvItem.mask = LVIF_TEXT|LVIF_PARAM;
+ lvItem.pszText = LPSTR_TEXTCALLBACK;
+ lvItem.cchTextMax = LPSTR_TEXTCALLBACK_MAX;
+ lvItem.lParam = (LPARAM)(LPVOID)pObject;
+
+ VERIFY(pListCtrl->SetItem(&lvItem)); // overwrite...
+
+ iObjectInList++; // increment count...
+ }
+ else
+ {
+ VERIFY(pListCtrl->DeleteItem(iObjectInList));
+ }
+ }
+
+ lvItem.mask = LVIF_TEXT|
+ LVIF_PARAM|
+ LVIF_IMAGE;
+
+ lvItem.pszText = LPSTR_TEXTCALLBACK;
+ lvItem.cchTextMax = LPSTR_TEXTCALLBACK_MAX;
+ lvItem.iImage = I_IMAGECALLBACK;
+ lvItem.iSubItem = 0;
+
+ int iItem;
+ int iSubItem;
+
+ while (iObject < nObjects)
+ {
+ lvItem.iItem = iObject;
+
+ pObject = pObArray->GetAt(iObject++);
+ VALIDATE_OBJECT(pObject, CObject);
+
+ lvItem.lParam = (LPARAM)(LPVOID)pObject;
+
+ iItem = pListCtrl->InsertItem(&lvItem);
+ ASSERT((iItem == lvItem.iItem) && (iItem != -1));
+
+ for (iSubItem = 1; iSubItem < plvColumnInfo->nColumns; iSubItem++)
+ {
+ VERIFY(pListCtrl->SetItemText(iItem, iSubItem, LPSTR_TEXTCALLBACK));
+ }
+ }
+
+ LvResizeColumns(pListCtrl, plvColumnInfo);
+
+ pListCtrl->SetRedraw(TRUE); // turn on drawing...
+
+ return TRUE;
+}
+
+
+void LvReleaseObArray(CListCtrl* pListCtrl)
+
+/*++
+
+Routine Description:
+
+ Release objects inserted into list view.
+
+Arguments:
+
+ pListCtrl - list control.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ VALIDATE_OBJECT(pListCtrl, CListCtrl);
+
+ LV_ITEM lvItem;
+
+ CObject* pObject;
+
+ lvItem.mask = LVIF_PARAM;
+ lvItem.iItem = 0;
+ lvItem.iSubItem = 0;
+
+ int nObjectsInList = pListCtrl->GetItemCount();
+
+ pListCtrl->BeginWaitCursor();
+ pListCtrl->SetRedraw(FALSE); // turn off drawing...
+
+ while (nObjectsInList--)
+ {
+ VERIFY(pListCtrl->GetItem(&lvItem));
+
+ pObject = (CObject*)lvItem.lParam;
+ VALIDATE_OBJECT(pObject, CObject);
+
+ VERIFY(pListCtrl->DeleteItem(lvItem.iItem));
+ }
+
+ pListCtrl->SetRedraw(TRUE); // turn on drawing...
+ pListCtrl->EndWaitCursor();
+}
+
+
+void LvReleaseSelObjs(CListCtrl* pListCtrl)
+
+/*++
+
+Routine Description:
+
+ Release selected objects in list view.
+
+Arguments:
+
+ pListCtrl - list control.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ VALIDATE_OBJECT(pListCtrl, CListCtrl);
+
+ LV_ITEM lvItem;
+
+ lvItem.mask = LVIF_PARAM;
+ lvItem.iSubItem = 0;
+
+ CObject* pObject;
+
+ pListCtrl->SetRedraw(FALSE); // turn off drawing...
+
+ int iItem = pListCtrl->GetNextItem(-1, LVNI_ALL|LVNI_SELECTED);
+
+ while (iItem != -1)
+ {
+ lvItem.iItem = iItem;
+
+ VERIFY(pListCtrl->GetItem(&lvItem));
+
+ pObject = (CObject*)lvItem.lParam;
+ VALIDATE_OBJECT(pObject, CObject);
+
+ iItem = pListCtrl->GetNextItem(lvItem.iItem, LVNI_ALL|LVNI_SELECTED);
+
+ VERIFY(pListCtrl->DeleteItem(lvItem.iItem));
+ }
+
+ LvSelObjIfNecessary(pListCtrl);
+
+ pListCtrl->SetRedraw(TRUE); // turn on drawing...
+}
+
+
+void LvSelObjIfNecessary(CListCtrl* pListCtrl, BOOL bSetFocus)
+
+/*++
+
+Routine Description:
+
+ Ensure that object selected.
+
+Arguments:
+
+ pListCtrl - list control.
+ bSetFocus - true if focus to be set focus as well.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ VALIDATE_OBJECT(pListCtrl, CListCtrl);
+
+ if (!IsItemSelectedInList(pListCtrl) && pListCtrl->GetItemCount())
+ {
+ pListCtrl->SendMessage(WM_KEYDOWN, VK_RIGHT); // HACKHACK...
+
+ int iItem = pListCtrl->GetNextItem(-1, LVNI_FOCUSED|LVNI_ALL);
+ int nState = bSetFocus ? (LVIS_SELECTED|LVIS_FOCUSED) : LVIS_SELECTED;
+
+ VERIFY(pListCtrl->SetItemState((iItem == -1) ? 0 : iItem, nState, nState));
+ }
+}
+
+
+#ifdef _DEBUG
+
+void LvDumpObArray(CListCtrl* pListCtrl)
+
+/*++
+
+Routine Description:
+
+ Release objects inserted into list view.
+
+Arguments:
+
+ pListCtrl - list control.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ VALIDATE_OBJECT(pListCtrl, CListCtrl);
+
+ LV_ITEM lvItem;
+
+ CString strDump;
+ CObject* pObject;
+
+ lvItem.mask = LVIF_STATE|LVIF_PARAM;
+ lvItem.stateMask = (DWORD)-1;
+ lvItem.iSubItem = 0;
+
+ int nObjectsInList = pListCtrl->GetItemCount();
+
+ pListCtrl->SetRedraw(FALSE); // turn off drawing...
+
+ while (nObjectsInList--)
+ {
+ lvItem.iItem = nObjectsInList;
+
+ VERIFY(pListCtrl->GetItem(&lvItem));
+
+ pObject = (CObject*)lvItem.lParam;
+ VALIDATE_OBJECT(pObject, CObject);
+
+ strDump.Format(_T("iItem %d"), lvItem.iItem);
+ strDump += (lvItem.state & LVIS_CUT) ? _T(" LVIS_CUT ") : _T("");
+ strDump += (lvItem.state & LVIS_FOCUSED) ? _T(" LVIS_FOCUSED ") : _T("");
+ strDump += (lvItem.state & LVIS_SELECTED) ? _T(" LVIS_SELECTED ") : _T("");
+ strDump += _T("\r\n");
+
+ afxDump << strDump;
+ }
+
+ pListCtrl->SetRedraw(TRUE); // turn on drawing...
+}
+
+#endif
+
+
+void SetDefaultFont(CWnd* pWnd)
+
+/*++
+
+Routine Description:
+
+ Set default font.
+
+Arguments:
+
+ pWnd - window to change font.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ VALIDATE_OBJECT(pWnd, CWnd);
+
+ HFONT hFont;
+ LOGFONT lFont;
+
+ memset(&lFont, 0, sizeof(LOGFONT)); // initialize
+
+ lFont.lfHeight = 13;
+ lFont.lfWeight = 200; // non-bold
+
+ hFont = ::CreateFontIndirect(&lFont);
+ pWnd->SetFont(CFont::FromHandle(hFont));
+}
diff --git a/private/net/svcdlls/lls/ccfapi32/utils.h b/private/net/svcdlls/lls/ccfapi32/utils.h
new file mode 100644
index 000000000..09f42b879
--- /dev/null
+++ b/private/net/svcdlls/lls/ccfapi32/utils.h
@@ -0,0 +1,93 @@
+/*++
+
+Copyright (c) 1994-95 Microsoft Corporation
+
+Module Name:
+
+ utils.h
+
+Abstract:
+
+ Utilities.
+
+Author:
+
+ Don Ryan (donryan) 04-Jan-1995
+
+Environment:
+
+ User Mode - Win32
+
+Revision History:
+
+ Jeff Parham (jeffparh) 12-Nov-1995
+ Copied from LLSMGR, stripped Tv (Tree view) functions,
+ removed OLE support
+
+--*/
+
+#ifndef _UTILS_H_
+#define _UTILS_H_
+
+#define LPSTR_TEXTCALLBACK_MAX 260
+
+//
+// List view utilities
+//
+
+#define LVID_SEPARATOR 0
+#define LVID_UNSORTED_LIST -1
+
+typedef struct _LV_COLUMN_ENTRY {
+
+ int iSubItem; // column index
+ int nStringId; // header string id
+ int nRelativeWidth; // header width
+
+} LV_COLUMN_ENTRY, *PLV_COLUMN_ENTRY;
+
+#pragma warning(disable:4200)
+typedef struct _LV_COLUMN_INFO {
+
+ BOOL bSortOrder; // sort order (ascending false)
+ int nSortedItem; // column sorted (default none)
+
+ int nColumns;
+ LV_COLUMN_ENTRY lvColumnEntry[];
+
+} LV_COLUMN_INFO, *PLV_COLUMN_INFO;
+#pragma warning(default:4200)
+
+void LvInitColumns(CListCtrl* pListCtrl, PLV_COLUMN_INFO plvColumnInfo);
+void LvResizeColumns(CListCtrl* pListCtrl, PLV_COLUMN_INFO plvColumnInfo);
+void LvChangeFormat(CListCtrl* pListCtrl, UINT nFormatId);
+
+LPVOID LvGetSelObj(CListCtrl* pListCtrl);
+LPVOID LvGetNextObj(CListCtrl* pListCtrl, LPINT piItem, int nType = LVNI_ALL|LVNI_SELECTED);
+void LvSelObjIfNecessary(CListCtrl* pListCtrl, BOOL bSetFocus = FALSE);
+
+BOOL LvInsertObArray(CListCtrl* pListCtrl, PLV_COLUMN_INFO plvColumnInfo, CObArray* pObArray);
+BOOL LvRefreshObArray(CListCtrl* pListCtrl, PLV_COLUMN_INFO plvColumnInfo, CObArray* pObArray);
+void LvReleaseObArray(CListCtrl* pListCtrl);
+void LvReleaseSelObjs(CListCtrl* pListCtrl);
+
+#ifdef _DEBUG
+void LvDumpObArray(CListCtrl* pListCtrl);
+#endif
+
+#define IsItemSelectedInList(plv) (::LvGetSelObj((CListCtrl*)(plv)) != NULL)
+
+//
+// Other stuff...
+//
+
+void SetDefaultFont(CWnd* pWnd);
+
+#ifdef _DEBUG
+#define VALIDATE_OBJECT(pOb, ObClass) \
+ { ASSERT_VALID((pOb)); ASSERT((pOb)->IsKindOf(RUNTIME_CLASS(ObClass))); }
+#else
+#define VALIDATE_OBJECT(pOb, ObClass)
+#endif
+
+#endif // _UTILS_H_
diff --git a/private/net/svcdlls/lls/client/llsevent.mc b/private/net/svcdlls/lls/client/llsevent.mc
new file mode 100644
index 000000000..7ba61c628
--- /dev/null
+++ b/private/net/svcdlls/lls/client/llsevent.mc
@@ -0,0 +1,176 @@
+;//=============================================================================
+;// Microsoft (R) License Logging Service (tm). Copyright (C) 1991-1995.
+;//
+;// MODULE: llsevent.mc
+;//
+;// Modification History
+;//
+;// arth 10-Mar-1995 Created
+;// jeffparh 05-Nov-1995 Added replication events.
+;// jeffparh 16-Nov-1995 Added certificate database events.
+;//=============================================================================
+
+;//
+;#ifndef _LLSEVENT_
+;#define _LLSEVENT_
+;//
+
+MessageIdTypedef=DWORD
+
+SeverityNames=(Success=0x0:STATUS_SEVERITY_SUCCESS
+ Informational=0x1:STATUS_SEVERITY_INFORMATIONAL
+ Warning=0x2:STATUS_SEVERITY_WARNING
+ Error=0x3:STATUS_SEVERITY_ERROR
+ )
+
+
+;
+;/////////////////////////////////////////////////////////////////////////
+;//
+;// LLS Events messages 1-100 are informational
+;//
+;/////////////////////////////////////////////////////////////////////////
+;
+
+MessageId=1 Severity=Informational Facility=Application SymbolicName=LLS_EVENT_STARTED
+Language=English
+The License Logging Service has started successfully.
+.
+
+MessageId=+1 Severity=Informational Facility=Application SymbolicName=LLS_EVENT_STOPPED
+Language=English
+The License Logging Service has stopped successfully.
+.
+
+MessageId=+1 Severity=Informational Facility=Application SymbolicName=LLS_EVENT_REPL_BACKOFF
+Language=English
+Server %1 has requested that license database replication to it be delayed.
+.
+
+MessageId=+1 Severity=Informational Facility=Application SymbolicName=LLS_EVENT_REPL_START
+Language=English
+License database replication to server %1 has started.
+.
+
+MessageId=+1 Severity=Informational Facility=Application SymbolicName=LLS_EVENT_REPL_END
+Language=English
+License database replication to server %1 has completed successfully.
+.
+
+;/////////////////////////////////////////////////////////////////////////
+;//
+;// LLS messages 200+ are warnings and errors
+;//
+
+MessageId=200 Severity=Warning Facility=Application SymbolicName=LLS_EVENT_NO_MEMORY
+Language=English
+The License Logging Service was unable to allocate memory.
+.
+
+MessageId=+1 Severity=Error Facility=Application SymbolicName=LLS_EVENT_USER_NO_LICENSE
+Language=English
+No license was available for user %1 using product %2.
+.
+
+MessageId=+1 Severity=Error Facility=Application SymbolicName=LLS_EVENT_PRODUCT_NO_LICENSE
+Language=English
+The product %1 is out of licenses. Use License Manager from the Administrative Tools folder for more information on which users are out of compliance and how many licenses should be purchased.
+.
+
+MessageId=+1 Severity=Error Facility=Application SymbolicName=LLS_EVENT_SAVE_USER
+Language=English
+The user data could not be saved.
+.
+
+MessageId=+1 Severity=Error Facility=Application SymbolicName=LLS_EVENT_SAVE_MAPPING
+Language=English
+The license group data could not be saved.
+.
+
+MessageId=+1 Severity=Error Facility=Application SymbolicName=LLS_EVENT_SAVE_LICENSE
+Language=English
+The purchased license data could not be saved.
+.
+
+MessageId=+1 Severity=Error Facility=Application SymbolicName=LLS_EVENT_SAVE_PRODUCT
+Language=English
+The product data could not be saved.
+.
+
+MessageId=+1 Severity=Error Facility=Application SymbolicName=LLS_EVENT_SAVE_SERVER
+Language=English
+The replicated server data could not be saved.
+.
+
+MessageId=+1 Severity=Error Facility=Application SymbolicName=LLS_EVENT_LOAD_USER
+Language=English
+The saved user data could not be restored.
+.
+
+MessageId=+1 Severity=Error Facility=Application SymbolicName=LLS_EVENT_LOAD_MAPPING
+Language=English
+The saved license group data could not be restored.
+.
+
+MessageId=+1 Severity=Error Facility=Application SymbolicName=LLS_EVENT_LOAD_LICENSE
+Language=English
+The saved purchased license data could not be restored.
+.
+
+MessageId=+1 Severity=Error Facility=Application SymbolicName=LLS_EVENT_LOAD_PRODUCT
+Language=English
+The saved product data could not be restored.
+.
+
+MessageId=+1 Severity=Error Facility=Application SymbolicName=LLS_EVENT_LOAD_SERVER
+Language=English
+The saved replicated server data could not be restored.
+.
+
+MessageId=+1 Severity=Warning Facility=Application SymbolicName=LLS_EVENT_REPL_NO_CONNECTION
+Language=English
+Replication of license information failed because the License Logging Service on server %1 could not be contacted.
+.
+
+MessageId=+1 Severity=Warning Facility=Application SymbolicName=LLS_EVENT_REPL_REQUEST_FAILED
+Language=English
+The License Logging Service encountered an error while initiating replication to server %1.
+.
+
+MessageId=+1 Severity=Warning Facility=Application SymbolicName=LLS_EVENT_REPL_FAILED
+Language=English
+License database replication to server %1 was unsuccessful.
+.
+
+MessageId=+1 Severity=Error Facility=Application SymbolicName=LLS_EVENT_CERT_VIOLATION_SERVER_ENTRY
+Language=English
+%1\t(%2 licenses)
+.
+
+MessageId=+1 Severity=Error Facility=Application SymbolicName=LLS_EVENT_CERT_VIOLATION
+Language=English
+The license certificate for product %1 with serial number %2 is in violation. There are currently %3 licenses installed from this certificate, while only %4 are allowed by the license agreement. The servers with this certificate installed are as follows:
+
+%5
+
+Use License Manager to remove licenses in order to comply with the license agreement.
+.
+
+MessageId=+1 Severity=Error Facility=Application SymbolicName=LLS_EVENT_SAVE_CERT_DB
+Language=English
+The certificate database could not be saved.
+.
+
+MessageId=+1 Severity=Error Facility=Application SymbolicName=LLS_EVENT_LOAD_CERT_DB
+Language=English
+The certificate database could not be restored.
+.
+
+MessageId=+1 Severity=Warning Facility=Application SymbolicName=LLS_EVENT_REPL_DOWNLEVEL_TARGET
+Language=English
+License database replication cannot be performed to server %1 because the version of Windows NT installed there does not support the License Logging Service.
+.
+
+;
+;#endif // _LLSEVENT.H_
+;
diff --git a/private/net/svcdlls/lls/client/llsrpc.c b/private/net/svcdlls/lls/client/llsrpc.c
new file mode 100644
index 000000000..da941db5d
--- /dev/null
+++ b/private/net/svcdlls/lls/client/llsrpc.c
@@ -0,0 +1,6600 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ llsrpc.c
+
+Abstract:
+
+ Client side RPC wrappers for License Logging Service.
+
+Author:
+
+ Arthur Hanson (arth) 30-Jan-1995
+
+Revision History:
+
+ Jeff Parham (jeffparh) 04-Dec-1995
+ o Forced include of LLS API prototypes, exposing an incorrect prototype
+ in LLSAPI.H.
+ o Fixed case where an LSA access denied was interpreted as implying the
+ server had no DC, rather than properly bubbling the access denied back
+ to the caller (of LlsConnectEnterprise()). This plugs a security hole
+ wherein a non-admin user with the ability to read the System registry
+ key would be allowed to administer domain licenses through
+ License Manager. (Bug #11441.)
+ o Added functions to support extended LLSRPC API.
+ o Removed replication dependency on no further LlsConnect()'s being made
+ until replication was completed.
+ o Installed lock around llsrpc_handle global binding variable. Required
+ addition of DllMain() function.
+ o Added LLSRPC capabilities detection. Upon connection, the client
+ requests the server's capabilities (an RPC call which itself will fail
+ when connected to a 3.51 server). The capabilities set is an
+ arbitrary bit field, but individual bits are normally defined to
+ indicate that a specific feature has been implemented at the server.
+ o Added szServerName filed to LOCAL_HANDLE to remember the name of the
+ machine to which we're connected.
+
+--*/
+
+#include <nt.h>
+#include <ntlsa.h>
+#include <ntsam.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windows.h>
+#include <lm.h>
+
+#include "debug.h"
+
+#include "llsapi.h"
+#include "llsrpc_c.h"
+
+// #define API_TRACE
+
+typedef struct _GENERIC_INFO_CONTAINER {
+ DWORD EntriesRead;
+ LPBYTE Buffer;
+} GENERIC_INFO_CONTAINER, *PGENERIC_INFO_CONTAINER, *LPGENERIC_INFO_CONTAINER ;
+
+typedef struct _GENERIC_ENUM_STRUCT {
+ DWORD Level;
+ PGENERIC_INFO_CONTAINER Container;
+} GENERIC_ENUM_STRUCT, *PGENERIC_ENUM_STRUCT, *LPGENERIC_ENUM_STRUCT ;
+
+
+typedef struct _LOCAL_HANDLE {
+ TCHAR szServerName[ 3 + MAX_COMPUTERNAME_LENGTH ];
+ LPTSTR pszStringBinding;
+ handle_t llsrpc_handle;
+ LLS_HANDLE Handle;
+ BYTE Capabilities[ ( LLS_CAPABILITY_MAX + 7 ) / 8 ];
+} LOCAL_HANDLE, *PLOCAL_HANDLE;
+
+
+LPTSTR pszStringBinding = NULL;
+
+
+RTL_CRITICAL_SECTION g_RpcHandleLock;
+
+
+/////////////////////////////////////////////////////////////////////////
+BOOL APIENTRY DllMain( HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved )
+
+/*++
+
+Routine Description:
+
+ Standard DLL entry point.
+
+Arguments:
+
+ hInstance (HINSTANCE)
+ dwReason (DWORD)
+ lpReserved (LPVOID)
+
+Return Value:
+
+ TRUE if successful.
+
+--*/
+
+{
+ NTSTATUS nt = STATUS_SUCCESS;
+
+ switch (dwReason)
+ {
+ case DLL_PROCESS_ATTACH:
+ nt = RtlInitializeCriticalSection( &g_RpcHandleLock );
+ break;
+
+ case DLL_PROCESS_DETACH:
+ nt = RtlDeleteCriticalSection( &g_RpcHandleLock );
+ break;
+
+ case DLL_THREAD_ATTACH:
+ break;
+
+ case DLL_THREAD_DETACH:
+ break;
+
+ default:
+ break;
+ }
+
+ return NT_SUCCESS( nt );
+}
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTDomainGet(
+ LPTSTR ServerName,
+ LPTSTR Domain
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ static TCHAR Serv[MAX_COMPUTERNAME_LENGTH + 3];
+ UNICODE_STRING us;
+ NTSTATUS ret;
+ OBJECT_ATTRIBUTES oa;
+ ACCESS_MASK am;
+ SECURITY_QUALITY_OF_SERVICE qos;
+ LSA_HANDLE hLSA;
+ PPOLICY_PRIMARY_DOMAIN_INFO pvBuffer;
+
+ lstrcpy(Domain, TEXT(""));
+
+ // only need read access
+ am = POLICY_READ | POLICY_VIEW_LOCAL_INFORMATION;
+
+ // set up quality of service
+ qos.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
+ qos.ImpersonationLevel = SecurityImpersonation;
+ qos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
+ qos.EffectiveOnly = FALSE;
+
+ // Macro sets everything except security field
+ InitializeObjectAttributes( &oa, NULL, 0L, NULL, NULL );
+ oa.SecurityQualityOfService = &qos;
+
+ if ( (ServerName == NULL) || (ServerName[0] == TEXT('\0')) )
+ ret = LsaOpenPolicy(NULL, &oa, am, &hLSA);
+ else {
+ if (ServerName[0] == TEXT('\\'))
+ lstrcpy(Serv, ServerName);
+ else
+ wsprintf(Serv, TEXT("\\\\%s"), ServerName);
+
+ // Set up unicode string structure
+ us.Length = lstrlen(Serv) * sizeof(TCHAR);
+ us.MaximumLength = us.Length + sizeof(TCHAR);
+ us.Buffer = Serv;
+
+ ret = LsaOpenPolicy(&us, &oa, am, &hLSA);
+ }
+
+ if (!ret) {
+ ret = LsaQueryInformationPolicy(hLSA, PolicyPrimaryDomainInformation, (PVOID *) &pvBuffer);
+ LsaClose(hLSA);
+ if ((!ret) && (pvBuffer != NULL) && (pvBuffer->Sid != NULL)) {
+ lstrcpy(Domain, pvBuffer->Name.Buffer);
+ LsaFreeMemory((PVOID) pvBuffer);
+ } else
+ if (!ret)
+ ret = STATUS_UNSUCCESSFUL;
+ }
+
+ return ret;
+
+} // NTDomainGet
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+EnterpriseServerGet(
+ LPTSTR ServerName,
+ LPTSTR pEnterpriseServer
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ HKEY hKey = NULL;
+ HKEY hKey2 = NULL;
+ BOOL Enterprise = FALSE;
+ DWORD dwType, dwSize;
+ TCHAR RegKeyText[512];
+ NTSTATUS Status;
+ DWORD UseEnterprise;
+ TCHAR EnterpriseServer[MAX_COMPUTERNAME_LENGTH + 1];
+ LPTSTR pName = ServerName;
+
+ Status = RegConnectRegistry(ServerName, HKEY_LOCAL_MACHINE, &hKey);
+ if (Status == ERROR_SUCCESS) {
+ //
+ // Create registry key-name we are looking for
+ //
+ lstrcpy(RegKeyText, TEXT("System\\CurrentControlSet\\Services\\LicenseService\\Parameters"));
+
+ if ((Status = RegOpenKeyEx(hKey, RegKeyText, 0, KEY_READ, &hKey2)) == ERROR_SUCCESS) {
+ dwSize = sizeof(UseEnterprise);
+ Status = RegQueryValueEx(hKey2, TEXT("UseEnterprise"), NULL, &dwType, (LPBYTE) &UseEnterprise, &dwSize);
+
+ if ((Status == ERROR_SUCCESS) && (UseEnterprise == 1)) {
+ dwSize = sizeof(EnterpriseServer);
+ Status = RegQueryValueEx(hKey2, TEXT("EnterpriseServer"), NULL, &dwType, (LPBYTE) &EnterpriseServer, &dwSize);
+
+ if (Status == ERROR_SUCCESS)
+ pName = EnterpriseServer;
+
+ }
+
+ RegCloseKey(hKey2);
+ }
+
+ RegCloseKey(hKey);
+ }
+
+ if (*pName != TEXT('\\')) {
+ lstrcpy(pEnterpriseServer, TEXT("\\\\"));
+ lstrcat(pEnterpriseServer, pName);
+ } else
+ lstrcpy(pEnterpriseServer, pName);
+
+ return STATUS_SUCCESS;
+} // EnterpriseServerGet
+
+
+/////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsEnterpriseServerFindW(
+ LPTSTR Focus,
+ DWORD Level,
+ LPBYTE *BufPtr
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ LPTSTR pFocus;
+ BOOL Domain = TRUE;
+ LPTSTR pszUuid = NULL;
+ LPTSTR pszProtocolSequence = NULL;
+ LPTSTR pszNetworkAddress = NULL;
+ LPTSTR pszEndpoint = NULL;
+ LPTSTR pszOptions = NULL;
+ TCHAR EnterpriseServer[MAX_COMPUTERNAME_LENGTH + 3];
+ TCHAR pDomain[MAX_COMPUTERNAME_LENGTH + 1];
+ NET_API_STATUS uRet;
+ LPBYTE pbBuffer = NULL;
+ PLLS_CONNECT_INFO_0 pConnectInfo;
+ ULONG Size;
+
+ if (Level != 0)
+ return STATUS_INVALID_LEVEL;
+
+ lstrcpy(pDomain, TEXT(""));
+ lstrcpy(EnterpriseServer, TEXT(""));
+
+ //
+ // Figure out if doing domain or server
+ //
+ pFocus = Focus;
+ if (pFocus !=NULL)
+ while ((*pFocus != TEXT('\0')) && (*pFocus == TEXT('\\'))) {
+ Domain = FALSE;
+ pFocus++;
+ }
+
+ if (!Domain) {
+ //
+ // If we are given a server, find the domain it is in
+ //
+ Status = NTDomainGet((LPWSTR) Focus, pDomain);
+
+ //
+ // If we got a domain find the DC of it, else find DC of server
+ //
+ if (Status == STATUS_SUCCESS) {
+ Domain = TRUE;
+ uRet = NetGetDCName(NULL, pDomain, &pbBuffer);
+ } else if ( STATUS_ACCESS_DENIED == Status ) {
+ goto LlsEnterpriseServerFindWExit;
+ } else {
+ Domain = FALSE;
+ uRet = 1;
+ }
+
+ } else {
+ //
+ // Get the DC name of wherever we are going
+ //
+ if ((pFocus == NULL) || (*pFocus == TEXT('\0')))
+ uRet = NetGetDCName(NULL, NULL, &pbBuffer);
+ else
+ uRet = NetGetDCName(NULL, pFocus, &pbBuffer);
+ }
+
+ if (uRet) {
+ //
+ // If we focus on a server and can't find a domain then look for an
+ // enterprise server. This is the case if the focus server is a
+ // standalone system.
+ //
+ if (Domain == FALSE) {
+ Status = EnterpriseServerGet((LPTSTR) Focus, EnterpriseServer);
+ goto LlsEnterpriseServerFindWExit;
+ }
+
+ return STATUS_NO_SUCH_DOMAIN;
+ }
+
+ //
+ // pbBuffer contains the DC name - if we don't have the domain yet, then
+ // get that.
+ //
+ Status = NTDomainGet((LPWSTR) pbBuffer, pDomain);
+ if (Status != STATUS_SUCCESS) {
+ NetApiBufferFree(pbBuffer);
+ return Status;
+ }
+
+ //
+ // Go to PDC and figure out if they are replicating anywhere, if so go
+ // to that system.
+ //
+ Status = EnterpriseServerGet((LPTSTR) pbBuffer, EnterpriseServer);
+
+ if (pbBuffer != NULL)
+ NetApiBufferFree(pbBuffer);
+
+LlsEnterpriseServerFindWExit:
+ if (Status != STATUS_SUCCESS)
+ return Status;
+
+ Size = sizeof(LLS_CONNECT_INFO_0);
+ Size += ((lstrlen(pDomain) + 1) * sizeof(TCHAR));
+ Size += ((lstrlen(EnterpriseServer) + 1) * sizeof(TCHAR));
+
+ pConnectInfo = (PLLS_CONNECT_INFO_0) MIDL_user_allocate(Size);
+ if (pConnectInfo == NULL)
+ return STATUS_NO_MEMORY;
+
+ pConnectInfo->Domain = (LPTSTR) (((PBYTE) pConnectInfo) + sizeof(LLS_CONNECT_INFO_0));
+ pConnectInfo->EnterpriseServer = (LPTSTR) &pConnectInfo->Domain[lstrlen(pDomain) + 1];
+
+ lstrcpy(pConnectInfo->Domain, pDomain);
+ lstrcpy(pConnectInfo->EnterpriseServer, EnterpriseServer);
+ *BufPtr = (LPBYTE) pConnectInfo;
+ return Status;
+
+} // LlsEnterpriseServerFindW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsEnterpriseServerFindA(
+ LPSTR Focus,
+ DWORD Level,
+ LPBYTE *BufPtr
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsEnterpriseServerFindA\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+} // LlsEnterpriseServerFindA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsConnectW(
+ LPTSTR Server,
+ LLS_HANDLE* Handle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ RPC_STATUS Status;
+ LPTSTR pszUuid = NULL;
+ LPTSTR pszProtocolSequence = NULL;
+ LPTSTR pszNetworkAddress = NULL;
+ LPTSTR pszEndpoint = NULL;
+ LPTSTR pszOptions = NULL;
+ TCHAR pComputer[MAX_COMPUTERNAME_LENGTH + 1];
+ ULONG Size;
+ PLOCAL_HANDLE pLocalHandle;
+ handle_t prev_llsrpc_handle;
+
+#ifdef API_TRACE
+ if (Server == NULL)
+ dprintf(TEXT("LLSRPC.DLL: LlsConnectW: <NULL>\n"));
+ else
+ dprintf(TEXT("LLSRPC.DLL: LlsConnectW: %s\n"), Server);
+#endif
+
+ if ((Server != NULL) && ( (Server[0] != TEXT('\\')) || (Server[1] != TEXT('\\')) ) )
+ return STATUS_INVALID_PARAMETER;
+
+ if (Handle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ *Handle = NULL;
+ Size = sizeof(pComputer);
+ GetComputerName(pComputer, &Size);
+
+ if ((Server == NULL) || (*Server == TEXT('\0'))) {
+ pszProtocolSequence = TEXT("ncalrpc");
+ pszEndpoint = TEXT(LLS_LPC_ENDPOINT);
+ pszNetworkAddress = NULL;
+ } else {
+ pszProtocolSequence = TEXT("ncacn_np");
+ pszEndpoint = TEXT(LLS_NP_ENDPOINT);
+ pszNetworkAddress = Server;
+ }
+
+ pLocalHandle = MIDL_user_allocate(sizeof(LOCAL_HANDLE));
+ if (pLocalHandle == NULL)
+ return STATUS_NO_MEMORY;
+
+ pLocalHandle->pszStringBinding = NULL;
+ pLocalHandle->llsrpc_handle = NULL;
+ pLocalHandle->Handle = NULL;
+
+ ZeroMemory( pLocalHandle->szServerName, sizeof( pLocalHandle->szServerName ) );
+ if ( NULL != Server )
+ {
+ lstrcpyn( pLocalHandle->szServerName, Server, sizeof( pLocalHandle->szServerName ) / sizeof( *pLocalHandle->szServerName ) - 1 );
+ }
+ else
+ {
+ wsprintf( pLocalHandle->szServerName, TEXT( "\\\\%s" ), pComputer );
+ }
+
+ // Compose a string binding
+ Status = RpcStringBindingComposeW(pszUuid,
+ pszProtocolSequence,
+ pszNetworkAddress,
+ pszEndpoint,
+ pszOptions,
+ &pLocalHandle->pszStringBinding);
+ if(Status) {
+#if DBG
+ dprintf(TEXT("LLSRPC RpcStringBindingComposeW Failed: 0x%lX\n"), Status);
+#endif
+ return I_RpcMapWin32Status(Status);
+ }
+
+ RtlEnterCriticalSection( &g_RpcHandleLock );
+ prev_llsrpc_handle = llsrpc_handle;
+
+ // Bind using the created string binding...
+ Status = RpcBindingFromStringBindingW(pLocalHandle->pszStringBinding, &llsrpc_handle);
+ if(Status) {
+#if DBG
+ dprintf(TEXT("LLSRPC RpcBindingFromStringBindingW Failed: 0x%lX\n"), Status);
+#endif
+ Status = I_RpcMapWin32Status(Status);
+ }
+
+ if ( NT_SUCCESS( Status ) )
+ {
+ pLocalHandle->llsrpc_handle = llsrpc_handle;
+
+ try {
+ LlsrConnect(&pLocalHandle->Handle, pComputer);
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("LLSRPC ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ if ( NT_SUCCESS( Status ) )
+ {
+ // get server capabilities
+ try {
+ LlsrCapabilityGet( pLocalHandle->Handle, sizeof( pLocalHandle->Capabilities ), pLocalHandle->Capabilities );
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+
+ if ( RPC_NT_PROCNUM_OUT_OF_RANGE == Status )
+ {
+ // 'salright; API doesn't exist at target server (it's running 3.51)
+ ZeroMemory( pLocalHandle->Capabilities, sizeof( pLocalHandle->Capabilities ) );
+ Status = STATUS_SUCCESS;
+ }
+ else
+ {
+#if DBG
+ dprintf(TEXT("LLSRPC ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+ }
+
+ if ( !NT_SUCCESS( Status ) )
+ {
+ LlsClose( pLocalHandle );
+ }
+ else
+ {
+ *Handle = (LLS_HANDLE) pLocalHandle;
+ }
+ }
+ }
+
+ llsrpc_handle = prev_llsrpc_handle;
+
+ RtlLeaveCriticalSection( &g_RpcHandleLock );
+
+ return Status;
+} // LlsConnectW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsConnectA(
+ LPSTR Server,
+ LLS_HANDLE* Handle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsConnectA\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+} // LlsConnectA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsConnectEnterpriseW(
+ LPTSTR Focus,
+ LLS_HANDLE* Handle,
+ DWORD Level,
+ LPBYTE *BufPtr
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ PLLS_CONNECT_INFO_0 pConnectInfo;
+
+ Status = LlsEnterpriseServerFindW(Focus, Level, BufPtr);
+
+ if (Status)
+ return Status;
+
+ pConnectInfo = (PLLS_CONNECT_INFO_0) *BufPtr;
+ Status = LlsConnectW(pConnectInfo->EnterpriseServer, Handle);
+
+ return Status;
+
+} // LlsConnectEnterpriseW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsConnectEnterpriseA(
+ LPSTR Focus,
+ LLS_HANDLE* Handle,
+ DWORD Level,
+ LPBYTE *BufPtr
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsConnectEnterpriseA\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+} // LlsConnectEnterpriseA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsClose(
+ LLS_HANDLE Handle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ RPC_STATUS Status;
+ NTSTATUS NtStatus = STATUS_SUCCESS;
+ PLOCAL_HANDLE pLocalHandle;
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ try {
+ NtStatus = LlsrClose(pLocalHandle->Handle);
+ }
+ except (TRUE) {
+ NtStatus = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), NtStatus);
+#endif
+ }
+
+ try {
+ Status = RpcStringFree(&pLocalHandle->pszStringBinding);
+ if (Status ) {
+ NtStatus = I_RpcMapWin32Status(Status);
+#if DBG
+ dprintf(TEXT("LLSRPC.DLL: LlsClose - RpcStringFree returned: 0x%lX\n"), NtStatus);
+#endif
+ }
+
+ Status = RpcBindingFree(&pLocalHandle->llsrpc_handle);
+ if (Status ) {
+ NtStatus = I_RpcMapWin32Status(Status);
+#if DBG
+ dprintf(TEXT("LLSRPC.DLL: LlsClose - RpcBindingFree returned: 0x%lX\n"), NtStatus);
+#endif
+ }
+ }
+ except (TRUE) {
+ NtStatus = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), NtStatus);
+#endif
+ }
+
+ MIDL_user_free(pLocalHandle);
+ return NtStatus;
+
+} // LlsClose
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsFreeMemory(
+ PVOID BufPtr
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ MIDL_user_free( BufPtr );
+ return STATUS_SUCCESS;
+} // LlsFreeMemory
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsLicenseEnumW(
+ LLS_HANDLE Handle,
+ DWORD Level,
+ LPBYTE* bufptr,
+ DWORD PrefMaxLen,
+ LPDWORD EntriesRead,
+ LPDWORD TotalEntries,
+ LPDWORD ResumeHandle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ GENERIC_INFO_CONTAINER GenericInfoContainer;
+ GENERIC_ENUM_STRUCT InfoStruct;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsLicenseEnumW\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ GenericInfoContainer.Buffer = NULL;
+ GenericInfoContainer.EntriesRead = 0;
+
+ InfoStruct.Container = &GenericInfoContainer;
+ InfoStruct.Level = Level;
+
+ try {
+ Status = LlsrLicenseEnumW(
+ pLocalHandle->Handle,
+ (PLLS_LICENSE_ENUM_STRUCTW) &InfoStruct,
+ PrefMaxLen,
+ TotalEntries,
+ ResumeHandle
+ );
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ if ((Status == STATUS_SUCCESS) || (Status == STATUS_MORE_ENTRIES)) {
+ *bufptr = (LPBYTE) GenericInfoContainer.Buffer;
+ *EntriesRead = GenericInfoContainer.EntriesRead;
+ }
+
+
+ return Status;
+} // LlsLicenseEnumW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsLicenseEnumA(
+ LLS_HANDLE Handle,
+ DWORD Level,
+ LPBYTE* bufptr,
+ DWORD PrefMaxLen,
+ LPDWORD EntriesRead,
+ LPDWORD TotalEntries,
+ LPDWORD ResumeHandle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ GENERIC_INFO_CONTAINER GenericInfoContainer;
+ GENERIC_ENUM_STRUCT InfoStruct;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsLicenseEnumA\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ GenericInfoContainer.Buffer = NULL;
+ GenericInfoContainer.EntriesRead = 0;
+
+ InfoStruct.Container = &GenericInfoContainer;
+ InfoStruct.Level = Level;
+
+ try {
+ Status = LlsrLicenseEnumA(
+ pLocalHandle->Handle,
+ (PLLS_LICENSE_ENUM_STRUCTA) &InfoStruct,
+ PrefMaxLen,
+ TotalEntries,
+ ResumeHandle
+ );
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ if ((Status == STATUS_SUCCESS) || (Status == STATUS_MORE_ENTRIES)) {
+ *bufptr = (LPBYTE) GenericInfoContainer.Buffer;
+ *EntriesRead = GenericInfoContainer.EntriesRead;
+ }
+
+
+ return Status;
+
+} // LlsLicenseEnumA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsLicenseAddW(
+ IN LLS_HANDLE Handle,
+ IN DWORD Level, // Level 0 supported
+ IN LPBYTE bufptr
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsLicenseAddW\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ try {
+ Status = LlsrLicenseAddW(pLocalHandle->Handle, Level, (PVOID) bufptr);
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ return Status;
+} // LlsLicenseAddW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsLicenseAddA(
+ IN LLS_HANDLE Handle,
+ IN DWORD Level, // Level 0 supported
+ IN LPBYTE bufptr
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsLicenseAddA\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ try {
+ Status = LlsrLicenseAddA(pLocalHandle->Handle, Level, (PVOID) bufptr);
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ return Status;
+} // LlsLicenseAddA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsProductEnumW(
+ LLS_HANDLE Handle,
+ DWORD Level,
+ LPBYTE* bufptr,
+ DWORD PrefMaxLen,
+ LPDWORD EntriesRead,
+ LPDWORD TotalEntries,
+ LPDWORD ResumeHandle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ GENERIC_INFO_CONTAINER GenericInfoContainer;
+ GENERIC_ENUM_STRUCT InfoStruct;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsProductEnumW\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ GenericInfoContainer.Buffer = NULL;
+ GenericInfoContainer.EntriesRead = 0;
+
+ InfoStruct.Container = &GenericInfoContainer;
+ InfoStruct.Level = Level;
+
+ try {
+ Status = LlsrProductEnumW(
+ pLocalHandle->Handle,
+ (PLLS_PRODUCT_ENUM_STRUCTW) &InfoStruct,
+ PrefMaxLen,
+ TotalEntries,
+ ResumeHandle
+ );
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ if ((Status == STATUS_SUCCESS) || (Status == STATUS_MORE_ENTRIES)) {
+ *bufptr = (LPBYTE) GenericInfoContainer.Buffer;
+ *EntriesRead = GenericInfoContainer.EntriesRead;
+ }
+
+
+ return Status;
+
+} // LlsProductEnumW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsProductEnumA(
+ LLS_HANDLE Handle,
+ DWORD Level,
+ LPBYTE* bufptr,
+ DWORD PrefMaxLen,
+ LPDWORD EntriesRead,
+ LPDWORD TotalEntries,
+ LPDWORD ResumeHandle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ GENERIC_INFO_CONTAINER GenericInfoContainer;
+ GENERIC_ENUM_STRUCT InfoStruct;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsProductEnumA\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ GenericInfoContainer.Buffer = NULL;
+ GenericInfoContainer.EntriesRead = 0;
+
+ InfoStruct.Container = &GenericInfoContainer;
+ InfoStruct.Level = Level;
+
+ try {
+ Status = LlsrProductEnumA(
+ pLocalHandle->Handle,
+ (PLLS_PRODUCT_ENUM_STRUCTA) &InfoStruct,
+ PrefMaxLen,
+ TotalEntries,
+ ResumeHandle
+ );
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ if ((Status == STATUS_SUCCESS) || (Status == STATUS_MORE_ENTRIES)) {
+ *bufptr = (LPBYTE) GenericInfoContainer.Buffer;
+ *EntriesRead = GenericInfoContainer.EntriesRead;
+ }
+
+
+ return Status;
+} // LlsProductEnumA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+LlsProductAddW(
+ IN LLS_REPL_HANDLE Handle,
+ IN LPWSTR ProductFamily,
+ IN LPWSTR Product,
+ IN LPWSTR Version
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsProductAddW\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ try {
+ Status = LlsrProductAddW(pLocalHandle->Handle, ProductFamily, Product, Version);
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ return Status;
+} // LlsProductAddW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+LlsProductAddA(
+ IN LLS_REPL_HANDLE Handle,
+ IN LPSTR ProductFamily,
+ IN LPSTR Product,
+ IN LPSTR Version
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsProductAddA\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ try {
+ Status = LlsrProductAddA(pLocalHandle->Handle, ProductFamily, Product, Version);
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ return Status;
+} // LlsProductAddA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsProductUserEnumW(
+ LLS_HANDLE Handle,
+ LPTSTR Product,
+ DWORD Level,
+ LPBYTE* bufptr,
+ DWORD PrefMaxLen,
+ LPDWORD EntriesRead,
+ LPDWORD TotalEntries,
+ LPDWORD ResumeHandle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ GENERIC_INFO_CONTAINER GenericInfoContainer;
+ GENERIC_ENUM_STRUCT InfoStruct;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsProductUserEnumW\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ GenericInfoContainer.Buffer = NULL;
+ GenericInfoContainer.EntriesRead = 0;
+
+ InfoStruct.Container = &GenericInfoContainer;
+ InfoStruct.Level = Level;
+
+ try {
+ Status = LlsrProductUserEnumW(
+ pLocalHandle->Handle,
+ Product,
+ (PLLS_PRODUCT_USER_ENUM_STRUCTW) &InfoStruct,
+ PrefMaxLen,
+ TotalEntries,
+ ResumeHandle
+ );
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ if ((Status == STATUS_SUCCESS) || (Status == STATUS_MORE_ENTRIES)) {
+ *bufptr = (LPBYTE) GenericInfoContainer.Buffer;
+ *EntriesRead = GenericInfoContainer.EntriesRead;
+ }
+
+
+ return Status;
+} // LlsProductUserEnumW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsProductUserEnumA(
+ LLS_HANDLE Handle,
+ LPSTR Product,
+ DWORD Level,
+ LPBYTE* bufptr,
+ DWORD PrefMaxLen,
+ LPDWORD EntriesRead,
+ LPDWORD TotalEntries,
+ LPDWORD ResumeHandle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ GENERIC_INFO_CONTAINER GenericInfoContainer;
+ GENERIC_ENUM_STRUCT InfoStruct;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsProductUserEnumA\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ GenericInfoContainer.Buffer = NULL;
+ GenericInfoContainer.EntriesRead = 0;
+
+ InfoStruct.Container = &GenericInfoContainer;
+ InfoStruct.Level = Level;
+
+ try {
+ Status = LlsrProductUserEnumA(
+ pLocalHandle->Handle,
+ Product,
+ (PLLS_PRODUCT_USER_ENUM_STRUCTA) &InfoStruct,
+ PrefMaxLen,
+ TotalEntries,
+ ResumeHandle
+ );
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ if ((Status == STATUS_SUCCESS) || (Status == STATUS_MORE_ENTRIES)) {
+ *bufptr = (LPBYTE) GenericInfoContainer.Buffer;
+ *EntriesRead = GenericInfoContainer.EntriesRead;
+ }
+
+
+ return Status;
+} // LlsProductUserEnumA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsProductServerEnumW(
+ LLS_HANDLE Handle,
+ LPTSTR Product,
+ DWORD Level,
+ LPBYTE* bufptr,
+ DWORD PrefMaxLen,
+ LPDWORD EntriesRead,
+ LPDWORD TotalEntries,
+ LPDWORD ResumeHandle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ GENERIC_INFO_CONTAINER GenericInfoContainer;
+ GENERIC_ENUM_STRUCT InfoStruct;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsProductServerEnumW\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ GenericInfoContainer.Buffer = NULL;
+ GenericInfoContainer.EntriesRead = 0;
+
+ InfoStruct.Container = &GenericInfoContainer;
+ InfoStruct.Level = Level;
+
+ try {
+ Status = LlsrProductServerEnumW(
+ pLocalHandle->Handle,
+ Product,
+ (PLLS_SERVER_PRODUCT_ENUM_STRUCTW) &InfoStruct,
+ PrefMaxLen,
+ TotalEntries,
+ ResumeHandle
+ );
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ if ((Status == STATUS_SUCCESS) || (Status == STATUS_MORE_ENTRIES)) {
+ *bufptr = (LPBYTE) GenericInfoContainer.Buffer;
+ *EntriesRead = GenericInfoContainer.EntriesRead;
+ }
+
+
+ return Status;
+} // LlsProductServerEnumW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsProductServerEnumA(
+ LLS_HANDLE Handle,
+ LPSTR Product,
+ DWORD Level,
+ LPBYTE* bufptr,
+ DWORD PrefMaxLen,
+ LPDWORD EntriesRead,
+ LPDWORD TotalEntries,
+ LPDWORD ResumeHandle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ GENERIC_INFO_CONTAINER GenericInfoContainer;
+ GENERIC_ENUM_STRUCT InfoStruct;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsProductServerEnumA\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ GenericInfoContainer.Buffer = NULL;
+ GenericInfoContainer.EntriesRead = 0;
+
+ InfoStruct.Container = &GenericInfoContainer;
+ InfoStruct.Level = Level;
+
+ try {
+ Status = LlsrProductServerEnumA(
+ pLocalHandle->Handle,
+ Product,
+ (PLLS_SERVER_PRODUCT_ENUM_STRUCTA) &InfoStruct,
+ PrefMaxLen,
+ TotalEntries,
+ ResumeHandle
+ );
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ if ((Status == STATUS_SUCCESS) || (Status == STATUS_MORE_ENTRIES)) {
+ *bufptr = (LPBYTE) GenericInfoContainer.Buffer;
+ *EntriesRead = GenericInfoContainer.EntriesRead;
+ }
+
+
+ return Status;
+} // LlsProductServerEnumA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsProductLicenseEnumW(
+ LLS_HANDLE Handle,
+ LPTSTR Product,
+ DWORD Level,
+ LPBYTE* bufptr,
+ DWORD PrefMaxLen,
+ LPDWORD EntriesRead,
+ LPDWORD TotalEntries,
+ LPDWORD ResumeHandle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ GENERIC_INFO_CONTAINER GenericInfoContainer;
+ GENERIC_ENUM_STRUCT InfoStruct;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsProductLicenseEnumW\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ GenericInfoContainer.Buffer = NULL;
+ GenericInfoContainer.EntriesRead = 0;
+
+ InfoStruct.Container = &GenericInfoContainer;
+ InfoStruct.Level = Level;
+
+ try {
+ Status = LlsrProductLicenseEnumW(
+ pLocalHandle->Handle,
+ Product,
+ (PLLS_PRODUCT_LICENSE_ENUM_STRUCTW) &InfoStruct,
+ PrefMaxLen,
+ TotalEntries,
+ ResumeHandle
+ );
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ if ((Status == STATUS_SUCCESS) || (Status == STATUS_MORE_ENTRIES)) {
+ *bufptr = (LPBYTE) GenericInfoContainer.Buffer;
+ *EntriesRead = GenericInfoContainer.EntriesRead;
+ }
+
+
+ return Status;
+} // LlsProductLicenseEnumW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsProductLicenseEnumA(
+ LLS_HANDLE Handle,
+ LPSTR Product,
+ DWORD Level,
+ LPBYTE* bufptr,
+ DWORD PrefMaxLen,
+ LPDWORD EntriesRead,
+ LPDWORD TotalEntries,
+ LPDWORD ResumeHandle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ GENERIC_INFO_CONTAINER GenericInfoContainer;
+ GENERIC_ENUM_STRUCT InfoStruct;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsProductLicenseEnumA\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ GenericInfoContainer.Buffer = NULL;
+ GenericInfoContainer.EntriesRead = 0;
+
+ InfoStruct.Container = &GenericInfoContainer;
+ InfoStruct.Level = Level;
+
+ try {
+ Status = LlsrProductLicenseEnumA(
+ pLocalHandle->Handle,
+ Product,
+ (PLLS_PRODUCT_LICENSE_ENUM_STRUCTA) &InfoStruct,
+ PrefMaxLen,
+ TotalEntries,
+ ResumeHandle
+ );
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ if ((Status == STATUS_SUCCESS) || (Status == STATUS_MORE_ENTRIES)) {
+ *bufptr = (LPBYTE) GenericInfoContainer.Buffer;
+ *EntriesRead = GenericInfoContainer.EntriesRead;
+ }
+
+
+ return Status;
+} // LlsProductLicenseEnumA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsUserEnumW(
+ LLS_HANDLE Handle,
+ DWORD Level,
+ LPBYTE* bufptr,
+ DWORD PrefMaxLen,
+ LPDWORD EntriesRead,
+ LPDWORD TotalEntries,
+ LPDWORD ResumeHandle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ GENERIC_INFO_CONTAINER GenericInfoContainer;
+ GENERIC_ENUM_STRUCT InfoStruct;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsUserEnumW\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ GenericInfoContainer.Buffer = NULL;
+ GenericInfoContainer.EntriesRead = 0;
+
+ InfoStruct.Container = &GenericInfoContainer;
+ InfoStruct.Level = Level;
+
+ try {
+ Status = LlsrUserEnumW(
+ pLocalHandle->Handle,
+ (PLLS_USER_ENUM_STRUCTW) &InfoStruct,
+ PrefMaxLen,
+ TotalEntries,
+ ResumeHandle
+ );
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ if ((Status == STATUS_SUCCESS) || (Status == STATUS_MORE_ENTRIES)) {
+ *bufptr = (LPBYTE) GenericInfoContainer.Buffer;
+ *EntriesRead = GenericInfoContainer.EntriesRead;
+ }
+
+
+ return Status;
+} // LlsUserEnumW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsUserEnumA(
+ LLS_HANDLE Handle,
+ DWORD Level,
+ LPBYTE* bufptr,
+ DWORD PrefMaxLen,
+ LPDWORD EntriesRead,
+ LPDWORD TotalEntries,
+ LPDWORD ResumeHandle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ GENERIC_INFO_CONTAINER GenericInfoContainer;
+ GENERIC_ENUM_STRUCT InfoStruct;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsUserEnumA\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ GenericInfoContainer.Buffer = NULL;
+ GenericInfoContainer.EntriesRead = 0;
+
+ InfoStruct.Container = &GenericInfoContainer;
+ InfoStruct.Level = Level;
+
+ try {
+ Status = LlsrUserEnumA(
+ pLocalHandle->Handle,
+ (PLLS_USER_ENUM_STRUCTA) &InfoStruct,
+ PrefMaxLen,
+ TotalEntries,
+ ResumeHandle
+ );
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ if ((Status == STATUS_SUCCESS) || (Status == STATUS_MORE_ENTRIES)) {
+ *bufptr = (LPBYTE) GenericInfoContainer.Buffer;
+ *EntriesRead = GenericInfoContainer.EntriesRead;
+ }
+
+
+ return Status;
+} // LlsUserEnumA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsUserInfoGetW(
+ IN LLS_HANDLE Handle,
+ IN LPWSTR User,
+ IN DWORD Level,
+ OUT LPBYTE* bufptr
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsUserInfoGetW\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if ((pLocalHandle == NULL) || (bufptr == NULL))
+ return STATUS_INVALID_PARAMETER;
+
+ *bufptr = NULL;
+
+ try {
+ Status = LlsrUserInfoGetW(pLocalHandle->Handle, User, Level, (PLLS_USER_INFOW *) bufptr);
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ return Status;
+} // LlsUserInfoGetW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsUserInfoGetA(
+ IN LLS_HANDLE Handle,
+ IN LPSTR User,
+ IN DWORD Level,
+ OUT LPBYTE* bufptr
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsUserInfoGetA\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if ((pLocalHandle == NULL) || (bufptr == NULL))
+ return STATUS_INVALID_PARAMETER;
+
+ *bufptr = NULL;
+
+ try {
+ Status = LlsrUserInfoGetA(pLocalHandle->Handle, User, Level, (PVOID) bufptr);
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ return Status;
+} // LlsUserInfoGetA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsUserInfoSetW(
+ IN LLS_HANDLE Handle,
+ IN LPWSTR User,
+ IN DWORD Level,
+ IN LPBYTE bufptr // Level 1 supported
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsUserInfoSetW\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ try {
+ Status = LlsrUserInfoSetW(pLocalHandle->Handle, User, Level, (PLLS_USER_INFOW) bufptr);
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ return Status;
+} // LlsUserInfoSetW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsUserInfoSetA(
+ IN LLS_HANDLE Handle,
+ IN LPSTR User,
+ IN DWORD Level,
+ IN LPBYTE bufptr
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsUserInfoSetA\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ try {
+ Status = LlsrUserInfoSetA(pLocalHandle->Handle, User, Level, (PVOID) bufptr);
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ return Status;
+} // LlsUserInfoSetA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsUserDeleteW(
+ IN LLS_HANDLE Handle,
+ IN LPWSTR User
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsUserDeleteW\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ try {
+ Status = LlsrUserDeleteW(pLocalHandle->Handle, User);
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ return Status;
+} // LlsUserDeleteW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsUserDeleteA(
+ IN LLS_HANDLE Handle,
+ IN LPSTR User
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsUserDeleteA\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ try {
+ Status = LlsrUserDeleteA(pLocalHandle->Handle, User);
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ return Status;
+} // LlsUserDeleteA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsUserProductEnumW(
+ LLS_HANDLE Handle,
+ LPTSTR User,
+ DWORD Level,
+ LPBYTE* bufptr,
+ DWORD PrefMaxLen,
+ LPDWORD EntriesRead,
+ LPDWORD TotalEntries,
+ LPDWORD ResumeHandle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ GENERIC_INFO_CONTAINER GenericInfoContainer;
+ GENERIC_ENUM_STRUCT InfoStruct;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsUserProductEnumW\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ GenericInfoContainer.Buffer = NULL;
+ GenericInfoContainer.EntriesRead = 0;
+
+ InfoStruct.Container = &GenericInfoContainer;
+ InfoStruct.Level = Level;
+
+ try {
+ Status = LlsrUserProductEnumW(
+ pLocalHandle->Handle,
+ User,
+ (PLLS_USER_PRODUCT_ENUM_STRUCTW) &InfoStruct,
+ PrefMaxLen,
+ TotalEntries,
+ ResumeHandle
+ );
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ if ((Status == STATUS_SUCCESS) || (Status == STATUS_MORE_ENTRIES)) {
+ *bufptr = (LPBYTE) GenericInfoContainer.Buffer;
+ *EntriesRead = GenericInfoContainer.EntriesRead;
+ }
+
+
+ return Status;
+} // LlsUserProductEnumW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsUserProductEnumA(
+ LLS_HANDLE Handle,
+ LPSTR User,
+ DWORD Level,
+ LPBYTE* bufptr,
+ DWORD PrefMaxLen,
+ LPDWORD EntriesRead,
+ LPDWORD TotalEntries,
+ LPDWORD ResumeHandle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ GENERIC_INFO_CONTAINER GenericInfoContainer;
+ GENERIC_ENUM_STRUCT InfoStruct;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsUserProductEnumA\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ GenericInfoContainer.Buffer = NULL;
+ GenericInfoContainer.EntriesRead = 0;
+
+ InfoStruct.Container = &GenericInfoContainer;
+ InfoStruct.Level = Level;
+
+ try {
+ Status = LlsrUserProductEnumA(
+ pLocalHandle->Handle,
+ User,
+ (PLLS_USER_PRODUCT_ENUM_STRUCTA) &InfoStruct,
+ PrefMaxLen,
+ TotalEntries,
+ ResumeHandle
+ );
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ if ((Status == STATUS_SUCCESS) || (Status == STATUS_MORE_ENTRIES)) {
+ *bufptr = (LPBYTE) GenericInfoContainer.Buffer;
+ *EntriesRead = GenericInfoContainer.EntriesRead;
+ }
+
+
+ return Status;
+} // LlsUserProductEnumA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsUserProductDeleteW(
+ IN LLS_HANDLE Handle,
+ IN LPWSTR User,
+ IN LPWSTR Product
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsUserProductDeleteW\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ try {
+ Status = LlsrUserProductDeleteW(pLocalHandle->Handle, User, Product);
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ return Status;
+} // LlsUserProductDeleteW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsUserProductDeleteA(
+ IN LLS_HANDLE Handle,
+ IN LPSTR User,
+ IN LPSTR Product
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsUserProductDeleteA\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ try {
+ Status = LlsrUserProductDeleteA(pLocalHandle->Handle, User, Product);
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ return Status;
+} // LlsUserProductDeleteA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsGroupEnumW(
+ LLS_HANDLE Handle,
+ DWORD Level,
+ LPBYTE* bufptr,
+ DWORD PrefMaxLen,
+ LPDWORD EntriesRead,
+ LPDWORD TotalEntries,
+ LPDWORD ResumeHandle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ GENERIC_INFO_CONTAINER GenericInfoContainer;
+ GENERIC_ENUM_STRUCT InfoStruct;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsGroupEnumW\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ GenericInfoContainer.Buffer = NULL;
+ GenericInfoContainer.EntriesRead = 0;
+
+ InfoStruct.Container = &GenericInfoContainer;
+ InfoStruct.Level = Level;
+
+ try {
+ Status = LlsrMappingEnumW(
+ pLocalHandle->Handle,
+ (PLLS_MAPPING_ENUM_STRUCTW) &InfoStruct,
+ PrefMaxLen,
+ TotalEntries,
+ ResumeHandle
+ );
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ if ((Status == STATUS_SUCCESS) || (Status == STATUS_MORE_ENTRIES)) {
+ *bufptr = (LPBYTE) GenericInfoContainer.Buffer;
+ *EntriesRead = GenericInfoContainer.EntriesRead;
+ }
+
+
+ return Status;
+} // LlsGroupEnumW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsGroupEnumA(
+ LLS_HANDLE Handle,
+ DWORD Level,
+ LPBYTE* bufptr,
+ DWORD PrefMaxLen,
+ LPDWORD EntriesRead,
+ LPDWORD TotalEntries,
+ LPDWORD ResumeHandle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ GENERIC_INFO_CONTAINER GenericInfoContainer;
+ GENERIC_ENUM_STRUCT InfoStruct;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsGroupEnumA\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ GenericInfoContainer.Buffer = NULL;
+ GenericInfoContainer.EntriesRead = 0;
+
+ InfoStruct.Container = &GenericInfoContainer;
+ InfoStruct.Level = Level;
+
+ try {
+ Status = LlsrMappingEnumA(
+ pLocalHandle->Handle,
+ (PLLS_MAPPING_ENUM_STRUCTA) &InfoStruct,
+ PrefMaxLen,
+ TotalEntries,
+ ResumeHandle
+ );
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ if ((Status == STATUS_SUCCESS) || (Status == STATUS_MORE_ENTRIES)) {
+ *bufptr = (LPBYTE) GenericInfoContainer.Buffer;
+ *EntriesRead = GenericInfoContainer.EntriesRead;
+ }
+
+
+ return Status;
+} // LlsGroupEnumA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsGroupInfoGetW(
+ IN LLS_HANDLE Handle,
+ IN LPWSTR Group,
+ IN DWORD Level,
+ OUT LPBYTE* bufptr
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsGroupInfoGetW\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if ((pLocalHandle == NULL) || (bufptr == NULL))
+ return STATUS_INVALID_PARAMETER;
+
+ *bufptr = NULL;
+
+ try {
+ Status = LlsrMappingInfoGetW(pLocalHandle->Handle, Group, Level, (PVOID) bufptr);
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ return Status;
+} // LlsGroupInfoGetW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsGroupInfoGetA(
+ IN LLS_HANDLE Handle,
+ IN LPSTR Group,
+ IN DWORD Level,
+ OUT LPBYTE* bufptr
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsGroupInfoGetA\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if ((pLocalHandle == NULL) || (bufptr == NULL))
+ return STATUS_INVALID_PARAMETER;
+
+ *bufptr = NULL;
+
+ try {
+ Status = LlsrMappingInfoGetA(pLocalHandle->Handle, Group, Level, (PVOID) bufptr);
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ return Status;
+} // LlsGroupInfoGetA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsGroupInfoSetW(
+ IN LLS_HANDLE Handle,
+ IN LPWSTR Group,
+ IN DWORD Level,
+ IN LPBYTE bufptr
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsGroupInfoSetW\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ try {
+ Status = LlsrMappingInfoSetW(pLocalHandle->Handle, Group, Level, (PVOID) bufptr);
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ return Status;
+} // LlsGroupInfoSetW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsGroupInfoSetA(
+ IN LLS_HANDLE Handle,
+ IN LPSTR Group,
+ IN DWORD Level,
+ IN LPBYTE bufptr
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsGroupInfoSetA\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ try {
+ Status = LlsrMappingInfoSetA(pLocalHandle->Handle, Group, Level, (PVOID) bufptr);
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ return Status;
+} // LlsGroupInfoSetA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsGroupUserEnumW(
+ LLS_HANDLE Handle,
+ LPTSTR Group,
+ DWORD Level,
+ LPBYTE* bufptr,
+ DWORD PrefMaxLen,
+ LPDWORD EntriesRead,
+ LPDWORD TotalEntries,
+ LPDWORD ResumeHandle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ GENERIC_INFO_CONTAINER GenericInfoContainer;
+ GENERIC_ENUM_STRUCT InfoStruct;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsGroupUserEnumW\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ GenericInfoContainer.Buffer = NULL;
+ GenericInfoContainer.EntriesRead = 0;
+
+ InfoStruct.Container = &GenericInfoContainer;
+ InfoStruct.Level = Level;
+
+ try {
+ Status = LlsrMappingUserEnumW(
+ pLocalHandle->Handle,
+ Group,
+ (PLLS_USER_ENUM_STRUCTW) &InfoStruct,
+ PrefMaxLen,
+ TotalEntries,
+ ResumeHandle
+ );
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ if ((Status == STATUS_SUCCESS) || (Status == STATUS_MORE_ENTRIES)) {
+ *bufptr = (LPBYTE) GenericInfoContainer.Buffer;
+ *EntriesRead = GenericInfoContainer.EntriesRead;
+ }
+
+
+ return Status;
+} // LlsGroupUserEnumW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsGroupUserEnumA(
+ LLS_HANDLE Handle,
+ LPSTR Group,
+ DWORD Level,
+ LPBYTE* bufptr,
+ DWORD PrefMaxLen,
+ LPDWORD EntriesRead,
+ LPDWORD TotalEntries,
+ LPDWORD ResumeHandle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ GENERIC_INFO_CONTAINER GenericInfoContainer;
+ GENERIC_ENUM_STRUCT InfoStruct;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsGroupUserEnumA\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ GenericInfoContainer.Buffer = NULL;
+ GenericInfoContainer.EntriesRead = 0;
+
+ InfoStruct.Container = &GenericInfoContainer;
+ InfoStruct.Level = Level;
+
+ try {
+ Status = LlsrMappingUserEnumA(
+ pLocalHandle->Handle,
+ Group,
+ (PLLS_USER_ENUM_STRUCTA) &InfoStruct,
+ PrefMaxLen,
+ TotalEntries,
+ ResumeHandle
+ );
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ if ((Status == STATUS_SUCCESS) || (Status == STATUS_MORE_ENTRIES)) {
+ *bufptr = (LPBYTE) GenericInfoContainer.Buffer;
+ *EntriesRead = GenericInfoContainer.EntriesRead;
+ }
+
+
+ return Status;
+} // LlsGroupUserEnumA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsGroupUserAddW(
+ IN LLS_HANDLE Handle,
+ IN LPWSTR Group,
+ IN LPWSTR User
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsGroupUserAddW\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ try {
+ Status = LlsrMappingUserAddW(pLocalHandle->Handle, Group, User);
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ return Status;
+} // LlsGroupUserAddW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsGroupUserAddA(
+ IN LLS_HANDLE Handle,
+ IN LPSTR Group,
+ IN LPSTR User
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsGroupUserAddA\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ try {
+ Status = LlsrMappingUserAddA(pLocalHandle->Handle, Group, User);
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ return Status;
+} // LlsGroupUserAddA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsGroupUserDeleteW(
+ IN LLS_HANDLE Handle,
+ IN LPWSTR Group,
+ IN LPWSTR User
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsGroupUserDeleteW\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ try {
+ Status = LlsrMappingUserDeleteW(pLocalHandle->Handle, Group, User);
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ return Status;
+} // LlsGroupUserDeleteW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsGroupUserDeleteA(
+ IN LLS_HANDLE Handle,
+ IN LPSTR Group,
+ IN LPSTR User
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsGroupUserDeleteA\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ try {
+ Status = LlsrMappingUserDeleteA(pLocalHandle->Handle, Group, User);
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ return Status;
+} // LlsGroupUserDeleteA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsGroupAddW(
+ IN LLS_HANDLE Handle,
+ IN DWORD Level,
+ IN LPBYTE bufptr
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsGroupAddW\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ try {
+ Status = LlsrMappingAddW(pLocalHandle->Handle, Level, (PVOID) bufptr);
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ return Status;
+} // LlsGroupAddW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsGroupAddA(
+ IN LLS_HANDLE Handle,
+ IN DWORD Level,
+ IN LPBYTE bufptr
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsGroupAddA\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ try {
+ Status = LlsrMappingAddA(pLocalHandle->Handle, Level, (PVOID) bufptr);
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ return Status;
+} // LlsGroupAddA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsGroupDeleteW(
+ IN LLS_HANDLE Handle,
+ IN LPWSTR Group
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsGroupDeleteW\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ try {
+ Status = LlsrMappingDeleteW(pLocalHandle->Handle, Group);
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ return Status;
+} // LlsGroupDeleteW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsGroupDeleteA(
+ IN LLS_HANDLE Handle,
+ IN LPSTR Group
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: GroupDeleteA\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ try {
+ Status = LlsrMappingDeleteA(pLocalHandle->Handle, Group);
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ return Status;
+} // LlsGroupDeleteA
+
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsServerEnumW(
+ LLS_HANDLE Handle,
+ LPWSTR Server,
+ DWORD Level,
+ LPBYTE* bufptr,
+ DWORD PrefMaxLen,
+ LPDWORD EntriesRead,
+ LPDWORD TotalEntries,
+ LPDWORD ResumeHandle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ GENERIC_INFO_CONTAINER GenericInfoContainer;
+ GENERIC_ENUM_STRUCT InfoStruct;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsServerEnumW\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ GenericInfoContainer.Buffer = NULL;
+ GenericInfoContainer.EntriesRead = 0;
+
+ InfoStruct.Container = &GenericInfoContainer;
+ InfoStruct.Level = Level;
+
+ try {
+ Status = LlsrServerEnumW(
+ pLocalHandle->Handle,
+ Server,
+ (PLLS_SERVER_ENUM_STRUCTW) &InfoStruct,
+ PrefMaxLen,
+ TotalEntries,
+ ResumeHandle
+ );
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ if ((Status == STATUS_SUCCESS) || (Status == STATUS_MORE_ENTRIES)) {
+ *bufptr = (LPBYTE) GenericInfoContainer.Buffer;
+ *EntriesRead = GenericInfoContainer.EntriesRead;
+ }
+
+
+ return Status;
+} // LlsServerEnumW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsServerEnumA(
+ LLS_HANDLE Handle,
+ LPSTR Server,
+ DWORD Level,
+ LPBYTE* bufptr,
+ DWORD PrefMaxLen,
+ LPDWORD EntriesRead,
+ LPDWORD TotalEntries,
+ LPDWORD ResumeHandle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ GENERIC_INFO_CONTAINER GenericInfoContainer;
+ GENERIC_ENUM_STRUCT InfoStruct;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsServerEnumA\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ GenericInfoContainer.Buffer = NULL;
+ GenericInfoContainer.EntriesRead = 0;
+
+ InfoStruct.Container = &GenericInfoContainer;
+ InfoStruct.Level = Level;
+
+ try {
+ Status = LlsrServerEnumA(
+ pLocalHandle->Handle,
+ Server,
+ (PLLS_SERVER_ENUM_STRUCTA) &InfoStruct,
+ PrefMaxLen,
+ TotalEntries,
+ ResumeHandle
+ );
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ if ((Status == STATUS_SUCCESS) || (Status == STATUS_MORE_ENTRIES)) {
+ *bufptr = (LPBYTE) GenericInfoContainer.Buffer;
+ *EntriesRead = GenericInfoContainer.EntriesRead;
+ }
+
+
+ return Status;
+} // LlsServerEnumA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsServerProductEnumW(
+ LLS_HANDLE Handle,
+ LPWSTR Server,
+ DWORD Level,
+ LPBYTE* bufptr,
+ DWORD PrefMaxLen,
+ LPDWORD EntriesRead,
+ LPDWORD TotalEntries,
+ LPDWORD ResumeHandle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ GENERIC_INFO_CONTAINER GenericInfoContainer;
+ GENERIC_ENUM_STRUCT InfoStruct;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsServerProductEnumW\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ GenericInfoContainer.Buffer = NULL;
+ GenericInfoContainer.EntriesRead = 0;
+
+ InfoStruct.Container = &GenericInfoContainer;
+ InfoStruct.Level = Level;
+
+ try {
+ Status = LlsrServerProductEnumW(
+ pLocalHandle->Handle,
+ Server,
+ (PLLS_SERVER_PRODUCT_ENUM_STRUCTW) &InfoStruct,
+ PrefMaxLen,
+ TotalEntries,
+ ResumeHandle
+ );
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ if ((Status == STATUS_SUCCESS) || (Status == STATUS_MORE_ENTRIES)) {
+ *bufptr = (LPBYTE) GenericInfoContainer.Buffer;
+ *EntriesRead = GenericInfoContainer.EntriesRead;
+ }
+
+
+ return Status;
+} // LlsServerProductEnumW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsServerProductEnumA(
+ LLS_HANDLE Handle,
+ LPSTR Server,
+ DWORD Level,
+ LPBYTE* bufptr,
+ DWORD PrefMaxLen,
+ LPDWORD EntriesRead,
+ LPDWORD TotalEntries,
+ LPDWORD ResumeHandle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ GENERIC_INFO_CONTAINER GenericInfoContainer;
+ GENERIC_ENUM_STRUCT InfoStruct;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsServerProductEnumA\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ GenericInfoContainer.Buffer = NULL;
+ GenericInfoContainer.EntriesRead = 0;
+
+ InfoStruct.Container = &GenericInfoContainer;
+ InfoStruct.Level = Level;
+
+ try {
+ Status = LlsrServerProductEnumA(
+ pLocalHandle->Handle,
+ Server,
+ (PLLS_SERVER_PRODUCT_ENUM_STRUCTA) &InfoStruct,
+ PrefMaxLen,
+ TotalEntries,
+ ResumeHandle
+ );
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ if ((Status == STATUS_SUCCESS) || (Status == STATUS_MORE_ENTRIES)) {
+ *bufptr = (LPBYTE) GenericInfoContainer.Buffer;
+ *EntriesRead = GenericInfoContainer.EntriesRead;
+ }
+
+
+ return Status;
+} // LlsServerProductEnumA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsLocalProductEnumW(
+ LLS_HANDLE Handle,
+ DWORD Level,
+ LPBYTE* bufptr,
+ DWORD PrefMaxLen,
+ LPDWORD EntriesRead,
+ LPDWORD TotalEntries,
+ LPDWORD ResumeHandle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ GENERIC_INFO_CONTAINER GenericInfoContainer;
+ GENERIC_ENUM_STRUCT InfoStruct;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsLocalProductEnumW\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ GenericInfoContainer.Buffer = NULL;
+ GenericInfoContainer.EntriesRead = 0;
+
+ InfoStruct.Container = &GenericInfoContainer;
+ InfoStruct.Level = Level;
+
+ try {
+ Status = LlsrLocalProductEnumW(
+ pLocalHandle->Handle,
+ (PLLS_SERVER_PRODUCT_ENUM_STRUCTW) &InfoStruct,
+ PrefMaxLen,
+ TotalEntries,
+ ResumeHandle
+ );
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ if ((Status == STATUS_SUCCESS) || (Status == STATUS_MORE_ENTRIES)) {
+ *bufptr = (LPBYTE) GenericInfoContainer.Buffer;
+ *EntriesRead = GenericInfoContainer.EntriesRead;
+ }
+
+
+ return Status;
+} // LlsLocalProductEnumW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsLocalProductEnumA(
+ LLS_HANDLE Handle,
+ DWORD Level,
+ LPBYTE* bufptr,
+ DWORD PrefMaxLen,
+ LPDWORD EntriesRead,
+ LPDWORD TotalEntries,
+ LPDWORD ResumeHandle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ GENERIC_INFO_CONTAINER GenericInfoContainer;
+ GENERIC_ENUM_STRUCT InfoStruct;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsLocalProductEnumA\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ GenericInfoContainer.Buffer = NULL;
+ GenericInfoContainer.EntriesRead = 0;
+
+ InfoStruct.Container = &GenericInfoContainer;
+ InfoStruct.Level = Level;
+
+ try {
+ Status = LlsrLocalProductEnumA(
+ pLocalHandle->Handle,
+ (PLLS_SERVER_PRODUCT_ENUM_STRUCTA) &InfoStruct,
+ PrefMaxLen,
+ TotalEntries,
+ ResumeHandle
+ );
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ if ((Status == STATUS_SUCCESS) || (Status == STATUS_MORE_ENTRIES)) {
+ *bufptr = (LPBYTE) GenericInfoContainer.Buffer;
+ *EntriesRead = GenericInfoContainer.EntriesRead;
+ }
+
+
+ return Status;
+} // LlsLocalProductEnumA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsLocalProductInfoGetW(
+ IN LLS_HANDLE Handle,
+ IN LPWSTR Product,
+ IN DWORD Level,
+ OUT LPBYTE* bufptr
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsLocalProductInfoGetW\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if ((pLocalHandle == NULL) || (bufptr == NULL))
+ return STATUS_INVALID_PARAMETER;
+
+ *bufptr = NULL;
+
+ try {
+ Status = LlsrLocalProductInfoGetW(pLocalHandle->Handle, Product, Level, (PVOID) bufptr);
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ return Status;
+} // LlsLocalProductInfoGetW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsLocalProductInfoGetA(
+ IN LLS_HANDLE Handle,
+ IN LPSTR Product,
+ IN DWORD Level,
+ OUT LPBYTE* bufptr
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsLocalProductInfoGetA\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if ((pLocalHandle == NULL) || (bufptr == NULL))
+ return STATUS_INVALID_PARAMETER;
+
+ *bufptr = NULL;
+
+ try {
+ Status = LlsrLocalProductInfoGetA(pLocalHandle->Handle, Product, Level, (PVOID) bufptr);
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ return Status;
+} // LlsLocalProductInfoGetA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsLocalProductInfoSetW(
+ IN LLS_HANDLE Handle,
+ IN LPWSTR Product,
+ IN DWORD Level,
+ IN LPBYTE bufptr
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsLocalProductInfoSetW\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ try {
+ Status = LlsrLocalProductInfoSetW(pLocalHandle->Handle, Product, Level, (PVOID) bufptr);
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ return Status;
+} // LlsLocalProductInfoSetW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsLocalProductInfoSetA(
+ IN LLS_HANDLE Handle,
+ IN LPSTR Product,
+ IN DWORD Level,
+ IN LPBYTE bufptr
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsLocalProductInfoSetA\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ try {
+ Status = LlsrLocalProductInfoSetA(pLocalHandle->Handle, Product, Level, (PVOID) bufptr);
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ return Status;
+} // LlsLocalProductInfoSetA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsServiceInfoGetW(
+ IN LLS_HANDLE Handle,
+ IN DWORD Level,
+ OUT LPBYTE* bufptr
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsServiceInfoGetW\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if ((pLocalHandle == NULL) || (bufptr == NULL))
+ return STATUS_INVALID_PARAMETER;
+
+ if ( LlsCapabilityIsSupported( Handle, LLS_CAPABILITY_SERVICE_INFO_GETW ) )
+ {
+ *bufptr = NULL;
+
+ try {
+ Status = LlsrServiceInfoGetW(pLocalHandle->Handle, Level, (PVOID) bufptr);
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+ }
+ else if ( 0 != Level )
+ {
+ Status = STATUS_INVALID_LEVEL;
+ }
+ else
+ {
+ // the target server will blow up if we make the RPC call
+ // in 3.51, the IDL file for the returned structure was incorrect, causing
+ // the ReplicateTo and EnterpriseServer buffers at the server to be freed
+
+ // instead, get this info from the target machine's registry
+
+ PLLS_SERVICE_INFO_0W pServiceInfo;
+
+ pServiceInfo = MIDL_user_allocate( sizeof( *pServiceInfo ) );
+
+ if ( NULL == pServiceInfo )
+ {
+ Status = STATUS_NO_MEMORY;
+ }
+ else
+ {
+ DWORD cbServerName = sizeof( WCHAR ) * ( 3 + MAX_COMPUTERNAME_LENGTH );
+
+ ZeroMemory( pServiceInfo, sizeof( *pServiceInfo ) );
+ pServiceInfo->Version = 5; // we know it's a 3.51 box
+ pServiceInfo->TimeStarted = 0; // don't know, but 3.51 fills in 0 anyway
+ pServiceInfo->Mode = LLS_MODE_ENTERPRISE_SERVER; // we know it's a 3.51 box
+ pServiceInfo->ReplicateTo = MIDL_user_allocate( cbServerName );
+ pServiceInfo->EnterpriseServer = MIDL_user_allocate( cbServerName );
+
+ if ( ( NULL == pServiceInfo->ReplicateTo ) || ( NULL == pServiceInfo->EnterpriseServer ) )
+ {
+ Status = STATUS_NO_MEMORY;
+ }
+ else
+ {
+ HKEY hKeyLocalMachine;
+ LONG lError;
+
+ // get parameters from registry
+ lError = RegConnectRegistry( pLocalHandle->szServerName + 2, HKEY_LOCAL_MACHINE, &hKeyLocalMachine );
+
+ if ( ERROR_SUCCESS == lError )
+ {
+ HKEY hKeyParameters;
+
+ lError = RegOpenKeyEx( hKeyLocalMachine, TEXT( "System\\CurrentControlSet\\Services\\LicenseService\\Parameters" ), 0, KEY_READ, &hKeyParameters );
+
+ if ( ERROR_SUCCESS == lError )
+ {
+ DWORD cbData;
+
+ // these parameters all default to 0
+ // (they were initialized to 0 via ZeroMemory(), above)
+
+ cbData = sizeof( pServiceInfo->ReplicationTime );
+ lError = RegQueryValueEx( hKeyParameters, TEXT( "ReplicationTime" ), NULL, NULL, (LPBYTE) &pServiceInfo->ReplicationTime, &cbData );
+
+ cbData = sizeof( pServiceInfo->ReplicationType );
+ lError = RegQueryValueEx( hKeyParameters, TEXT( "ReplicationType" ), NULL, NULL, (LPBYTE) &pServiceInfo->ReplicationType, &cbData );
+
+ cbData = sizeof( pServiceInfo->UseEnterprise );
+ lError = RegQueryValueEx( hKeyParameters, TEXT( "UseEnterprise" ), NULL, NULL, (LPBYTE) &pServiceInfo->UseEnterprise, &cbData );
+
+ RegCloseKey( hKeyParameters );
+
+ lError = ERROR_SUCCESS;
+ }
+
+ RegCloseKey( hKeyLocalMachine );
+ }
+
+ switch ( lError )
+ {
+ case ERROR_SUCCESS:
+ Status = STATUS_SUCCESS;
+ break;
+ case ERROR_ACCESS_DENIED:
+ Status = STATUS_ACCESS_DENIED;
+ break;
+ default:
+ Status = STATUS_UNSUCCESSFUL;
+ break;
+ }
+
+ if ( STATUS_SUCCESS == Status )
+ {
+ // parameters retrieved from registry; only remaining parameters
+ // to be filled in are EnterpriseServer and ReplicateTo
+ TCHAR szDomain[ 1 + MAX_COMPUTERNAME_LENGTH ];
+
+ // retrieve the enterprise server
+ EnterpriseServerGet( pLocalHandle->szServerName, pServiceInfo->EnterpriseServer );
+
+ // derive ReplicateTo
+ Status = NTDomainGet( pLocalHandle->szServerName, szDomain );
+
+ if ( STATUS_ACCESS_DENIED != Status )
+ {
+ if ( STATUS_SUCCESS == Status )
+ {
+ NET_API_STATUS netStatus;
+ LPWSTR pszDCName;
+
+ netStatus = NetGetDCName( NULL, szDomain, (LPBYTE *) &pszDCName );
+
+ if ( NERR_Success == netStatus )
+ {
+ if ( !lstrcmpi( pszDCName, pLocalHandle->szServerName ) )
+ {
+ // server is primary domain controller;
+ // it replicates to its enterprise server (if any)
+ lstrcpy( pServiceInfo->ReplicateTo, pServiceInfo->EnterpriseServer );
+ }
+ else
+ {
+ // server is domain member; it replicates to the DC
+ lstrcpy( pServiceInfo->ReplicateTo, pszDCName );
+ }
+
+ NetApiBufferFree( pszDCName );
+ }
+ else
+ {
+ // server had domain but domain has no DC?
+ Status = STATUS_NO_SUCH_DOMAIN;
+ }
+ }
+ else
+ {
+ // server is not in a domain;
+ // it replicates to its enterprise server (if any)
+ lstrcpy( pServiceInfo->ReplicateTo, pServiceInfo->EnterpriseServer );
+ Status = STATUS_SUCCESS;
+ }
+ }
+ }
+ }
+ }
+
+ if ( STATUS_SUCCESS != Status )
+ {
+ if ( NULL != pServiceInfo )
+ {
+ if ( NULL != pServiceInfo->ReplicateTo )
+ {
+ MIDL_user_free( pServiceInfo->ReplicateTo );
+ }
+ if ( NULL != pServiceInfo->EnterpriseServer )
+ {
+ MIDL_user_free( pServiceInfo->EnterpriseServer );
+ }
+
+ MIDL_user_free( pServiceInfo );
+ }
+ }
+ else
+ {
+ if ( !lstrcmpi( pLocalHandle->szServerName, pServiceInfo->ReplicateTo ) )
+ {
+ *pServiceInfo->ReplicateTo = TEXT( '\0' );
+ }
+ if ( !lstrcmpi( pLocalHandle->szServerName, pServiceInfo->EnterpriseServer ) )
+ {
+ *pServiceInfo->EnterpriseServer = TEXT( '\0' );
+ }
+
+ *bufptr = (LPBYTE) pServiceInfo;
+ }
+ }
+
+ return Status;
+} // LlsServiceInfoGetW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsServiceInfoGetA(
+ IN LLS_HANDLE Handle,
+ IN DWORD Level,
+ OUT LPBYTE* bufptr
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsServiceInfoGetA\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if ((pLocalHandle == NULL) || (bufptr == NULL))
+ return STATUS_INVALID_PARAMETER;
+
+ *bufptr = NULL;
+
+ try {
+ Status = LlsrServiceInfoGetA(pLocalHandle->Handle, Level, (PVOID) bufptr);
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ return Status;
+} // LlsServiceInfoGetA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsServiceInfoSetW(
+ IN LLS_HANDLE Handle,
+ IN DWORD Level,
+ IN LPBYTE bufptr
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsServiceInfoSetW\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ try {
+ Status = LlsrServiceInfoSetW(pLocalHandle->Handle, Level, (PVOID) bufptr);
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ if ( ( STATUS_NOT_SUPPORTED == Status ) && ( 0 == Level ) )
+ {
+ // RPC API is not supported; use the registry instead
+ HKEY hKeyLocalMachine;
+ HKEY hKeyParameters;
+ LONG lError;
+ PLLS_SERVICE_INFO_0W pServiceInfo = (PLLS_SERVICE_INFO_0W) bufptr;
+ LPWSTR pszEnterpriseServer;
+
+ pszEnterpriseServer = pServiceInfo->EnterpriseServer;
+
+ // strip leading backslashes from EnterpriseServer
+ if ( !wcsncmp( pszEnterpriseServer, L"\\\\", 2 ) )
+ {
+ pszEnterpriseServer += 2;
+ }
+
+ lError = RegConnectRegistry( pLocalHandle->szServerName + 2, HKEY_LOCAL_MACHINE, &hKeyLocalMachine );
+
+ if ( ERROR_SUCCESS == lError )
+ {
+ lError = RegOpenKeyEx( hKeyLocalMachine, TEXT( "System\\CurrentControlSet\\Services\\LicenseService\\Parameters" ), 0, KEY_WRITE, &hKeyParameters );
+
+ if ( ERROR_SUCCESS == lError )
+ {
+ lError = RegSetValueExW( hKeyParameters, L"EnterpriseServer", 0, REG_SZ, (LPBYTE) pszEnterpriseServer, sizeof( *pszEnterpriseServer ) * ( 1 + lstrlenW( pszEnterpriseServer ) ) );
+
+ if ( ERROR_SUCCESS == lError )
+ {
+ lError = RegSetValueEx( hKeyParameters, TEXT( "ReplicationTime" ), 0, REG_DWORD, (LPBYTE) &pServiceInfo->ReplicationTime, sizeof( pServiceInfo->ReplicationTime ) );
+
+ if ( ERROR_SUCCESS == lError )
+ {
+ lError = RegSetValueEx( hKeyParameters, TEXT( "ReplicationType" ), 0, REG_DWORD, (LPBYTE) &pServiceInfo->ReplicationType, sizeof( pServiceInfo->ReplicationType ) );
+
+ if ( ERROR_SUCCESS == lError )
+ {
+ lError = RegSetValueEx( hKeyParameters, TEXT( "UseEnterprise" ), 0, REG_DWORD, (LPBYTE) &pServiceInfo->UseEnterprise, sizeof( pServiceInfo->UseEnterprise ) );
+
+ if ( ERROR_SUCCESS == lError )
+ {
+ Status = STATUS_SUCCESS;
+ }
+ }
+ }
+ }
+
+ RegCloseKey( hKeyParameters );
+ }
+
+ RegCloseKey( hKeyLocalMachine );
+ }
+ }
+
+ return Status;
+} // LlsServiceInfoSetW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsServiceInfoSetA(
+ IN LLS_HANDLE Handle,
+ IN DWORD Level,
+ IN LPBYTE bufptr
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsServiceInfoSetA\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ try {
+ Status = LlsrServiceInfoSetA(pLocalHandle->Handle, Level, (PVOID) bufptr);
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ return Status;
+} // LlsServiceInfoSetA
+
+
+
+/////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+LlsReplConnectW(
+ LPTSTR Server,
+ LLS_REPL_HANDLE* Handle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ RPC_STATUS Status;
+ LPTSTR pszUuid = NULL;
+ LPTSTR pszProtocolSequence = NULL;
+ LPTSTR pszNetworkAddress = NULL;
+ LPTSTR pszEndpoint = NULL;
+ LPTSTR pszOptions = NULL;
+ TCHAR pComputer[MAX_COMPUTERNAME_LENGTH + 1];
+ ULONG Size;
+
+#ifdef API_TRACE
+ if (Server == NULL)
+ dprintf(TEXT("LLSRPC.DLL: LlsReplConnectW: <NULL>\n"));
+ else
+ dprintf(TEXT("LLSRPC.DLL: LlsReplConnectW: %s\n"), Server);
+#endif
+
+ if ((Server == NULL) || (*Server != TEXT('\\')) || (lstrlen(Server) < 3))
+ return STATUS_INVALID_PARAMETER;
+
+
+ Size = sizeof(pComputer);
+ GetComputerName(pComputer, &Size);
+
+ pszProtocolSequence = TEXT("ncacn_np");
+ pszEndpoint = TEXT(LLS_NP_ENDPOINT);
+ pszNetworkAddress = Server;
+
+ // Compose a string binding
+ Status = RpcStringBindingComposeW(pszUuid,
+ pszProtocolSequence,
+ pszNetworkAddress,
+ pszEndpoint,
+ pszOptions,
+ &pszStringBinding);
+ if(Status) {
+#if DBG
+ dprintf(TEXT("LLSRPC RpcStringBindingComposeW Failed: 0x%lX\n"), Status);
+#endif
+ return I_RpcMapWin32Status(Status);
+ }
+
+ RtlEnterCriticalSection( &g_RpcHandleLock );
+
+ // Bind using the created string binding...
+ Status = RpcBindingFromStringBindingW(pszStringBinding, &llsrpc_handle);
+ if(Status) {
+#if DBG
+ dprintf(TEXT("LLSRPC RpcBindingFromStringBindingW Failed: 0x%lX\n"), Status);
+#endif
+ RtlLeaveCriticalSection( &g_RpcHandleLock );
+ return I_RpcMapWin32Status(Status);
+ }
+
+ try {
+ LlsrReplConnect(Handle, pComputer);
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ RtlLeaveCriticalSection( &g_RpcHandleLock );
+
+ return I_RpcMapWin32Status(Status);
+
+} // LlsReplConnectW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsReplClose(
+ PLLS_REPL_HANDLE Handle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ RPC_STATUS Status;
+ NTSTATUS NtStatus = STATUS_SUCCESS;
+
+ try {
+ NtStatus = LlsrReplClose(Handle);
+ }
+ except (TRUE) {
+ NtStatus = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), NtStatus);
+#endif
+ }
+
+ try {
+ Status = RpcStringFree(&pszStringBinding);
+ if (Status ) {
+ NtStatus = I_RpcMapWin32Status(Status);
+#if DBG
+ dprintf(TEXT("LLSRPC.DLL: LlsClose - RpcStringFree returned: 0x%lX\n"), NtStatus);
+#endif
+ }
+
+ Status = RpcBindingFree(&llsrpc_handle);
+ if (Status ) {
+ NtStatus = I_RpcMapWin32Status(Status);
+#if DBG
+ dprintf(TEXT("LLSRPC.DLL: LlsClose - RpcBindingFree returned: 0x%lX\n"), NtStatus);
+#endif
+ }
+ }
+ except (TRUE) {
+ NtStatus = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), NtStatus);
+#endif
+ }
+
+ return NtStatus;
+
+} // LlsClose
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+LlsReplicationRequestW(
+ IN LLS_REPL_HANDLE Handle,
+ IN DWORD Version,
+ IN OUT PREPL_REQUEST Request
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+
+ try {
+ Status = LlsrReplicationRequestW(Handle, Version, Request);
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ return Status;
+} // LlsReplicationRequestW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+LlsReplicationServerAddW(
+ IN LLS_REPL_HANDLE Handle,
+ IN ULONG NumRecords,
+ IN PREPL_SERVER_RECORD Servers
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+
+ try {
+ Status = LlsrReplicationServerAddW(Handle, NumRecords, Servers);
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ return Status;
+} // LlsReplicationServerAddW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+LlsReplicationServerServiceAddW(
+ IN LLS_REPL_HANDLE Handle,
+ IN ULONG NumRecords,
+ IN PREPL_SERVER_SERVICE_RECORD ServerServices
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+
+ try {
+ Status = LlsrReplicationServerServiceAddW(Handle, NumRecords, ServerServices);
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ return Status;
+} // LlsReplicationServerServiceAddW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+LlsReplicationServiceAddW(
+ IN LLS_REPL_HANDLE Handle,
+ IN ULONG NumRecords,
+ IN PREPL_SERVICE_RECORD Services
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+
+ try {
+ Status = LlsrReplicationServiceAddW(Handle, NumRecords, Services);
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ return Status;
+} // LlsReplicationServiceAddW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+LlsReplicationUserAddW(
+ IN LLS_REPL_HANDLE Handle,
+ IN ULONG NumRecords,
+ IN PREPL_USER_RECORD_0 Users
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+
+ try {
+ Status = LlsrReplicationUserAddW(Handle, NumRecords, Users);
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ return Status;
+} // LlsReplicationUserAddW
+
+/////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////
+
+NTSTATUS
+NTAPI
+LlsProductSecurityGetW(
+ LLS_HANDLE Handle,
+ LPWSTR Product,
+ LPBOOL pIsSecure
+ )
+
+/*++
+
+Routine Description:
+
+ Retrieve the "security" of a product. A product is deemed secure iff
+ it requires a secure certificate. In such a case, licenses for the
+ product may not be entered via the Honesty ("enter the number of
+ licenses you purchased") method.
+
+Arguments:
+
+ Handle (LLS_HANDLE)
+ An open LLS handle to the target license server.
+ Product (LPWSTR)
+ The name of the product ("DisplayName") for which to receive the
+ security.
+ pIsSecure (LPBOOL)
+ On return, and if successful, indicates whether the product is
+ secure.
+
+Return Value:
+
+ STATUS_SUCCESS or NTSTATUS error code.
+
+--*/
+
+{
+ NTSTATUS Status;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsProductSecurityGetW\n"));
+#endif
+
+ if ( !LlsCapabilityIsSupported( Handle, LLS_CAPABILITY_SECURE_CERTIFICATES ) )
+ {
+ return STATUS_NOT_SUPPORTED;
+ }
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ try {
+ Status = LlsrProductSecurityGetW( pLocalHandle->Handle, Product, pIsSecure );
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ return Status;
+} // LlsProductSecurityGetW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsProductSecurityGetA(
+ LLS_HANDLE Handle,
+ LPSTR Product,
+ LPBOOL pIsSecure
+ )
+
+/*++
+
+Routine Description:
+
+ Retrieve the "security" of a product. A product is deemed secure iff
+ it requires a secure certificate. In such a case, licenses for the
+ product may not be entered via the Honesty ("enter the number of
+ licenses you purchased") method.
+
+ NOTE: Not yet implemented. Use LlsProductSecurityGetW().
+
+Arguments:
+
+ Handle (LLS_HANDLE)
+ An open LLS handle to the target license server.
+ Product (LPSTR)
+ The name of the product ("DisplayName") for which to receive the
+ security.
+ pIsSecure (LPBOOL)
+ On return, and if successful, indicates whether the product is
+ secure.
+
+Return Value:
+
+ STATUS_NOT_SUPPORTED.
+
+--*/
+
+{
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsProductSecurityGetA\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+} // LlsProductSecurityGetA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsProductSecuritySetW(
+ LLS_HANDLE Handle,
+ LPWSTR Product
+ )
+
+/*++
+
+Routine Description:
+
+ Flags the given product as secure. A product is deemed secure iff
+ it requires a secure certificate. In such a case, licenses for the
+ product may not be entered via the Honesty ("enter the number of
+ licenses you purchased") method.
+
+ This designation is not reversible and is propagated up the
+ replication tree.
+
+Arguments:
+
+ Handle (LLS_HANDLE)
+ An open LLS handle to the target license server.
+ Product (LPWSTR)
+ The name of the product ("DisplayName") for which to activate
+ security.
+
+Return Value:
+
+ STATUS_SUCCESS or NTSTATUS error code.
+
+--*/
+
+{
+ NTSTATUS Status;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsProductSecuritySetW\n"));
+#endif
+
+ if ( !LlsCapabilityIsSupported( Handle, LLS_CAPABILITY_SECURE_CERTIFICATES ) )
+ {
+ return STATUS_NOT_SUPPORTED;
+ }
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ try {
+ Status = LlsrProductSecuritySetW( pLocalHandle->Handle, Product );
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ return Status;
+} // LlsProductSecuritySetW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsProductSecuritySetA(
+ LLS_HANDLE Handle,
+ LPSTR Product
+ )
+
+/*++
+
+Routine Description:
+
+ Flags the given product as secure. A product is deemed secure iff
+ it requires a secure certificate. In such a case, licenses for the
+ product may not be entered via the Honesty ("enter the number of
+ licenses you purchased") method.
+
+ This designation is not reversible and is propagated up the
+ replication tree.
+
+ NOTE: Not yet implemented. Use LlsProductSecuritySetW().
+
+Arguments:
+
+ Handle (LLS_HANDLE)
+ An open LLS handle to the target license server.
+ Product (LPSTR)
+ The name of the product ("DisplayName") for which to activate
+ security.
+
+Return Value:
+
+ STATUS_NOT_SUPPORTED.
+
+--*/
+
+{
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsProductSecuritySetA\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+} // LlsProductSecuritySetA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsProductLicensesGetW(
+ LLS_HANDLE Handle,
+ LPWSTR Product,
+ DWORD Mode,
+ LPDWORD pQuantity )
+
+/*++
+
+Routine Description:
+
+ Returns the number of licenses installed on the target machine for
+ use in the given mode.
+
+Arguments:
+
+ Handle (LLS_HANDLE)
+ An open LLS handle to the target license server.
+ Product (LPWSTR)
+ The name of the product for which to tally licenses.
+ Mode (DWORD)
+ Licensing mode for which to tally the licenses.
+ pQuantity (LPDWORD)
+ On return (and if successful), holds the total number of licenses
+ for use by the given product in the given license mode.
+
+Return Value:
+
+ STATUS_SUCCESS or NTSTATUS error code.
+
+--*/
+
+{
+ NTSTATUS Status;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsProductLicensesGetW\n"));
+#endif
+
+ if ( !LlsCapabilityIsSupported( Handle, LLS_CAPABILITY_SECURE_CERTIFICATES ) )
+ {
+ return STATUS_NOT_SUPPORTED;
+ }
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ try {
+ Status = LlsrProductLicensesGetW( pLocalHandle->Handle, Product, Mode, pQuantity );
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ return Status;
+} // LlsProductLicensesGetW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsProductLicensesGetA(
+ LLS_HANDLE Handle,
+ LPSTR Product,
+ DWORD Mode,
+ LPDWORD pQuantity )
+
+/*++
+
+Routine Description:
+
+ Returns the number of licenses installed on the target machine for
+ use in the given mode.
+
+ NOTE: Not yet implemented. Use LlsProductLicensesGetW().
+
+Arguments:
+
+ Handle (LLS_HANDLE)
+ An open LLS handle to the target license server.
+ Product (LPSTR)
+ The name of the product for which to tally licenses.
+ Mode (DWORD)
+ Licensing mode for which to tally the licenses.
+ pQuantity (LPDWORD)
+ On return (and if successful), holds the total number of licenses
+ for use by the given product in the given license mode.
+
+Return Value:
+
+ STATUS_NOT_SUPPORTED.
+
+--*/
+
+{
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsProductLicensesGetA\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+} // LlsProductLicensesGetA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsCertificateClaimEnumW(
+ LLS_HANDLE Handle,
+ DWORD LicenseLevel,
+ LPBYTE pLicenseInfo,
+ DWORD TargetLevel,
+ LPBYTE * ppTargets,
+ LPDWORD pNumTargets )
+
+/*++
+
+Routine Description:
+
+ Enumerates the servers on which a given secure certificate is installed.
+ This function is normally invoked when an attempt to add licenses from
+ a certificate is denied.
+
+Arguments:
+
+ Handle (LLS_HANDLE)
+ An open LLS handle to the target license server.
+ LicenseLevel (DWORD)
+ The level of the license structure pointed to by pLicenseInfo.
+ pLicenseInfo (LPBYTE)
+ Points to a LLS_LICENSE_INFO_X structure, where X is LicenseLevel.
+ This license structure describes a license for which the certificate
+ targets are requested.
+ TargetLevel (DWORD)
+ The level of the target structure desired.
+ ppTargets (LPBYTE *)
+ On return (and if successful), holds a PLLS_EX_CERTIFICATE_CLAIM_X,
+ where X is TargetLevel. This array of structures describes the
+ location of all installations of licenses from the given certificate.
+ pNumTargets (LPDWORD)
+ On return (and if successful), holds the number of structures pointed
+ to by ppTargets.
+
+Return Value:
+
+ STATUS_SUCCESS or NTSTATUS error code.
+
+--*/
+
+{
+ NTSTATUS Status;
+ GENERIC_INFO_CONTAINER GenericInfoContainer;
+ GENERIC_ENUM_STRUCT InfoStruct;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsCertificateClaimEnumW\n"));
+#endif
+
+ if ( !LlsCapabilityIsSupported( Handle, LLS_CAPABILITY_SECURE_CERTIFICATES ) )
+ {
+ return STATUS_NOT_SUPPORTED;
+ }
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ GenericInfoContainer.Buffer = NULL;
+ GenericInfoContainer.EntriesRead = 0;
+
+ InfoStruct.Container = &GenericInfoContainer;
+ InfoStruct.Level = TargetLevel;
+
+ try
+ {
+ Status = LlsrCertificateClaimEnumW(
+ pLocalHandle->Handle,
+ LicenseLevel,
+ (LPVOID) pLicenseInfo,
+ (PLLS_CERTIFICATE_CLAIM_ENUM_STRUCTW) &InfoStruct );
+ }
+ except (TRUE)
+ {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ if (Status == STATUS_SUCCESS)
+ {
+ *ppTargets = (LPBYTE) GenericInfoContainer.Buffer;
+ *pNumTargets = GenericInfoContainer.EntriesRead;
+ }
+
+ return Status;
+} // LlsCertificateClaimEnumW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsCertificateClaimEnumA(
+ LLS_HANDLE Handle,
+ DWORD LicenseLevel,
+ LPBYTE pLicenseInfo,
+ DWORD TargetLevel,
+ LPBYTE * ppTargets,
+ LPDWORD pNumTargets )
+
+/*++
+
+Routine Description:
+
+ Enumerates the servers on which a given secure certificate is installed.
+ This function is normally invoked when an attempt to add licenses from
+ a certificate is denied.
+
+ NOTE: Not yet implemented. Use LlsCertificateClaimEnumW().
+
+Arguments:
+
+ Handle (LLS_HANDLE)
+ An open LLS handle to the target license server.
+ LicenseLevel (DWORD)
+ The level of the license structure pointed to by pLicenseInfo.
+ pLicenseInfo (LPBYTE)
+ Points to a LLS_LICENSE_INFO_X structure, where X is LicenseLevel.
+ This license structure describes a license for which the certificate
+ targets are requested.
+ TargetLevel (DWORD)
+ The level of the target structure desired.
+ ppTargets (LPBYTE *)
+ On return (and if successful), holds a PLLS_EX_CERTIFICATE_CLAIM_X,
+ where X is TargetLevel. This array of structures describes the
+ location of all installations of licenses from the given certificate.
+ pNumTargets (LPDWORD)
+ On return (and if successful), holds the number of structures pointed
+ to by ppTargets.
+
+Return Value:
+
+ STATUS_NOT_SUPPORTED.
+
+--*/
+
+{
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsCertificateClaimEnumA\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+} // LlsCertificateClaimEnumA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsCertificateClaimAddCheckW(
+ LLS_HANDLE Handle,
+ DWORD LicenseLevel,
+ LPBYTE pLicenseInfo,
+ LPBOOL pbMayInstall )
+
+/*++
+
+Routine Description:
+
+ Verify that no more licenses from a given certificate are installed in
+ a licensing enterprise than are allowed by the certificate.
+
+Arguments:
+
+ Handle (LLS_HANDLE)
+ An open LLS handle to the target license server.
+ LicenseLevel (DWORD)
+ The level of the license structure pointed to by pLicenseInfo.
+ pLicenseInfo (LPBYTE)
+ Points to a LLS_LICENSE_INFO_X structure, where X is LicenseLevel.
+ This license structure describes the license wished to add.
+ pbMayInstall (LPBOOL)
+ On return (and if successful), indicates whether the certificate
+ may be legally installed.
+
+Return Value:
+
+ STATUS_SUCCESS or NTSTATUS error code.
+
+--*/
+
+{
+ NTSTATUS Status;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsCertificateClaimAddCheckW\n"));
+#endif
+
+ if ( !LlsCapabilityIsSupported( Handle, LLS_CAPABILITY_SECURE_CERTIFICATES ) )
+ {
+ return STATUS_NOT_SUPPORTED;
+ }
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ try {
+ Status = LlsrCertificateClaimAddCheckW( pLocalHandle->Handle, LicenseLevel, (LPVOID) pLicenseInfo, pbMayInstall );
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ return Status;
+} // LlsCertificateClaimAddCheckW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsCertificateClaimAddCheckA(
+ IN LLS_HANDLE Handle,
+ IN DWORD LicenseLevel,
+ IN LPBYTE pLicenseInfo,
+ OUT LPBOOL pbMayInstall )
+
+/*++
+
+Routine Description:
+
+ Verify that no more licenses from a given certificate are installed in
+ a licensing enterprise than are allowed by the certificate.
+
+ NOTE: Not yet implemented. Use LlsCertificateClaimAddCheckW().
+
+Arguments:
+
+ Handle (LLS_HANDLE)
+ An open LLS handle to the target license server.
+ LicenseLevel (DWORD)
+ The level of the license structure pointed to by pLicenseInfo.
+ pLicenseInfo (LPBYTE)
+ Points to a LLS_LICENSE_INFO_X structure, where X is LicenseLevel.
+ This license structure describes the license wished to add.
+ pbMayInstall (LPBOOL)
+ On return (and if successful), indicates whether the certificate
+ may be legally installed.
+
+Return Value:
+
+ STATUS_NOT_SUPPORTED.
+
+--*/
+
+{
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsCertificateClaimAddCheckA\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+} // LlsCertificateClaimAddCheckA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsCertificateClaimAddW(
+ LLS_HANDLE Handle,
+ LPWSTR ServerName,
+ DWORD LicenseLevel,
+ LPBYTE pLicenseInfo )
+
+/*++
+
+Routine Description:
+
+ Declare a number of licenses from a given certificate as being installed
+ on the target machine.
+
+Arguments:
+
+ Handle (LLS_HANDLE)
+ An open LLS handle to the target license server.
+ ServerName (LPWSTR)
+ Name of the server on which the licenses are installed.
+ LicenseLevel (DWORD)
+ The level of the license structure pointed to by pLicenseInfo.
+ pLicenseInfo (LPBYTE)
+ Points to a LLS_LICENSE_INFO_X structure, where X is LicenseLevel.
+ This license structure describes the license added.
+
+Return Value:
+
+ STATUS_SUCCESS or NTSTATUS error code.
+
+--*/
+
+{
+ NTSTATUS Status;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsCertificateClaimAddW\n"));
+#endif
+
+ if ( !LlsCapabilityIsSupported( Handle, LLS_CAPABILITY_SECURE_CERTIFICATES ) )
+ {
+ return STATUS_NOT_SUPPORTED;
+ }
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ try {
+ Status = LlsrCertificateClaimAddW( pLocalHandle->Handle, ServerName, LicenseLevel, (LPVOID) pLicenseInfo );
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ return Status;
+} // LlsCertificateClaimAddW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsCertificateClaimAddA(
+ LLS_HANDLE Handle,
+ LPSTR ServerName,
+ DWORD LicenseLevel,
+ LPBYTE pLicenseInfo )
+
+/*++
+
+Routine Description:
+
+ Declare a number of licenses from a given certificate as being installed
+ on the target machine.
+
+ NOTE: Not yet implemented. Use LlsCertificateClaimAddW().
+
+Arguments:
+
+ Handle (LLS_HANDLE)
+ An open LLS handle to the target license server.
+ ServerName (LPWSTR)
+ Name of the server on which the licenses are installed.
+ LicenseLevel (DWORD)
+ The level of the license structure pointed to by pLicenseInfo.
+ pLicenseInfo (LPBYTE)
+ Points to a LLS_LICENSE_INFO_X structure, where X is LicenseLevel.
+ This license structure describes the license added.
+
+Return Value:
+
+ STATUS_NOT_SUPPORTED.
+
+--*/
+
+{
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsCertificateClaimAddA\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+} // LlsCertificateClaimAddA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsReplicationCertDbAddW(
+ LLS_REPL_HANDLE Handle,
+ DWORD Level,
+ LPVOID Certificates )
+
+/*++
+
+Routine Description:
+
+ Called as an optional part of replication, this function replicates
+ the contents of the remote certificate database.
+
+Arguments:
+
+ Handle (LLS_REPL_HANDLE)
+ An open replication handle to the target server.
+ Level (DWORD)
+ Level of replication information sent.
+ Certificates (LPVOID)
+ Replicated certificate information.
+
+Return Value:
+
+ STATUS_SUCCESS or NTSTATUS error code.
+
+--*/
+
+{
+ NTSTATUS Status;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsReplicationCertDbAddW\n"));
+#endif
+
+ try {
+ Status = LlsrReplicationCertDbAddW( Handle, Level, Certificates );
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ return Status;
+} // LlsReplicationCertDbAddW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsReplicationProductSecurityAddW(
+ LLS_REPL_HANDLE Handle,
+ DWORD Level,
+ LPVOID SecureProducts )
+
+/*++
+
+Routine Description:
+
+ Called as an optional part of replication, this function replicates
+ the list of products which require secure certificates.
+
+Arguments:
+
+ Handle (LLS_REPL_HANDLE)
+ An open replication handle to the target server.
+ Level (DWORD)
+ Level of the product security information.
+ SecureProducts (LPVOID)
+ Replicated secure product information.
+
+Return Value:
+
+ STATUS_SUCCESS or NTSTATUS error code.
+
+--*/
+
+{
+ NTSTATUS Status;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsReplicationProductSecurityAddW\n"));
+#endif
+
+ try {
+ Status = LlsrReplicationProductSecurityAddW( Handle, Level, SecureProducts );
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ return Status;
+} // LlsReplicationProductSecurityAddW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsReplicationUserAddExW(
+ LLS_REPL_HANDLE Handle,
+ DWORD Level,
+ LPVOID Users )
+
+/*++
+
+Routine Description:
+
+ Replacement for LlsReplicationUserAddW(). (This function, unlike its
+ counterpart, supports structure levels.) This function replicates
+ the user list.
+
+Arguments:
+
+ Handle (LLS_REPL_HANDLE)
+ An open replication handle to the target server.
+ Level (DWORD)
+ Level of the user information.
+ Users (LPVOID)
+ Replicated user information.
+
+Return Value:
+
+ STATUS_SUCCESS or NTSTATUS error code.
+
+--*/
+
+{
+ NTSTATUS Status;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsReplicationUserAddExW\n"));
+#endif
+
+ if ( (0 != Level) && (1 != Level) )
+ return STATUS_INVALID_LEVEL;
+
+ try {
+ Status = LlsrReplicationUserAddExW( Handle, Level, Users );
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ return Status;
+} // LlsReplicationUserAddExW
+
+
+/////////////////////////////////////////////////////////////////////////
+BOOL
+NTAPI
+LlsCapabilityIsSupported(
+ LLS_HANDLE Handle,
+ DWORD Capability )
+
+/*++
+
+Routine Description:
+
+ Determine whether the target license server supports an arbitrary
+ function.
+
+Arguments:
+
+ Handle (LLS_HANDLE)
+ An open LLS handle to the target license server.
+ Capability (DWORD)
+ The capability number to check for, 0 <= Capability < LLS_CAPABILITY_MAX.
+
+Return Value:
+
+ TRUE (supports the capability) or FALSE (does not).
+
+--*/
+
+{
+ BOOL bIsSupported = FALSE;
+ PLOCAL_HANDLE pLocalHandle;
+ DWORD dwCapByte;
+ DWORD dwCapBit;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsCapabilityIsSupported\n"));
+#endif
+
+ if ( ( NULL != Handle ) && ( Capability < LLS_CAPABILITY_MAX ) )
+ {
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+
+ dwCapByte = Capability / 8;
+ dwCapBit = Capability - 8 * dwCapByte;
+
+ if ( 1 & ( pLocalHandle->Capabilities[ dwCapByte ] >> dwCapBit ) )
+ {
+ bIsSupported = TRUE;
+ }
+ }
+
+ return bIsSupported;
+} // LlsCapabilityIsSupported
+
+
+NTSTATUS
+NTAPI
+LlsLocalServiceEnumW(
+ LLS_HANDLE Handle,
+ DWORD Level,
+ LPBYTE* bufptr,
+ DWORD PrefMaxLen,
+ LPDWORD EntriesRead,
+ LPDWORD TotalEntries,
+ LPDWORD ResumeHandle )
+{
+ NTSTATUS Status;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsLocalServiceEnumW\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+
+ if (pLocalHandle == NULL)
+ {
+ Status = STATUS_INVALID_PARAMETER;
+ }
+ else if ( LlsCapabilityIsSupported( Handle, LLS_CAPABILITY_LOCAL_SERVICE_API ) )
+ {
+ GENERIC_INFO_CONTAINER GenericInfoContainer;
+ GENERIC_ENUM_STRUCT InfoStruct;
+
+ GenericInfoContainer.Buffer = NULL;
+ GenericInfoContainer.EntriesRead = 0;
+
+ InfoStruct.Container = &GenericInfoContainer;
+ InfoStruct.Level = Level;
+
+ try
+ {
+ Status = LlsrLocalServiceEnumW(
+ pLocalHandle->Handle,
+ (PLLS_LOCAL_SERVICE_ENUM_STRUCTW) &InfoStruct,
+ PrefMaxLen,
+ TotalEntries,
+ ResumeHandle
+ );
+ }
+ except (TRUE)
+ {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ if ((Status == STATUS_SUCCESS) || (Status == STATUS_MORE_ENTRIES))
+ {
+ *bufptr = (LPBYTE) GenericInfoContainer.Buffer;
+ *EntriesRead = GenericInfoContainer.EntriesRead;
+ }
+ }
+ else if ( 0 != Level )
+ {
+ Status = STATUS_INVALID_LEVEL;
+ }
+ else
+ {
+ PLLS_LOCAL_SERVICE_INFO_0 pLocalServices = NULL;
+ DWORD cEntriesRead = 0;
+ LONG lError;
+ HKEY hKeyLocalMachine;
+
+ lError = RegConnectRegistry( pLocalHandle->szServerName, HKEY_LOCAL_MACHINE, &hKeyLocalMachine );
+
+ if ( ERROR_SUCCESS == lError )
+ {
+ HKEY hKeyLicenseInfo;
+
+ lError = RegOpenKeyEx( hKeyLocalMachine, REG_KEY_LICENSE, 0, KEY_READ, &hKeyLicenseInfo );
+
+ if ( ERROR_SUCCESS == lError )
+ {
+ const DWORD cbBufferSize = 0x4000;
+
+ // fudge; we ignore MaxPrefLen and allocate a 16k buffer
+ // this is because when we restart an enumeration, we don't know how
+ // many items we have left (we could do it, but it'd be slow)
+ // this is only for 3.51 boxes, anyway, and this buffer will hold
+ // about 500 local service entries (plenty!)
+ // this also keeps us from having to keep the registry key open
+ // across function calls
+
+ pLocalServices = MIDL_user_allocate( cbBufferSize );
+
+ if ( NULL == pLocalServices )
+ {
+ lError = ERROR_OUTOFMEMORY;
+ }
+ else
+ {
+ DWORD iSubKey;
+ TCHAR szKeyName[ 128 ];
+
+ // read all the services installed on this machine
+ for ( iSubKey=0, cEntriesRead=0;
+ ( ERROR_SUCCESS == lError ) && ( ( cEntriesRead + 1 ) * sizeof( *pLocalServices ) < cbBufferSize );
+ iSubKey++ )
+ {
+ lError = RegEnumKey( hKeyLicenseInfo, iSubKey, szKeyName, sizeof( szKeyName ) / sizeof( *szKeyName ) );
+
+ if ( ERROR_SUCCESS == lError )
+ {
+ HKEY hKeyService;
+
+ lError = RegOpenKeyEx( hKeyLicenseInfo, szKeyName, 0, KEY_READ, &hKeyService );
+
+ if ( ERROR_SUCCESS == lError )
+ {
+ DWORD cbData;
+
+ cbData = sizeof( pLocalServices[ cEntriesRead ].Mode );
+ lError = RegQueryValueEx( hKeyService, REG_VALUE_MODE, NULL, NULL, (LPBYTE) &pLocalServices[ cEntriesRead ].Mode, &cbData );
+
+ if ( ERROR_SUCCESS == lError )
+ {
+ cbData = sizeof( pLocalServices[ cEntriesRead ].FlipAllow );
+ lError = RegQueryValueEx( hKeyService, REG_VALUE_FLIP, NULL, NULL, (LPBYTE) &pLocalServices[ cEntriesRead ].FlipAllow, &cbData );
+
+ if ( ERROR_SUCCESS == lError )
+ {
+ cbData = sizeof( pLocalServices[ cEntriesRead ].ConcurrentLimit );
+ lError = RegQueryValueEx( hKeyService, REG_VALUE_LIMIT, NULL, NULL, (LPBYTE) &pLocalServices[ cEntriesRead ].ConcurrentLimit, &cbData );
+
+ if ( ERROR_SUCCESS == lError )
+ {
+ DWORD cbKeyName;
+ DWORD cbDisplayName;
+ DWORD cbFamilyDisplayName;
+
+ cbData = sizeof( pLocalServices[ cEntriesRead ].HighMark );
+ lError = RegQueryValueEx( hKeyService, REG_VALUE_HIGHMARK, NULL, NULL, (LPBYTE) &pLocalServices[ cEntriesRead ].HighMark, &cbData );
+
+ if ( ERROR_SUCCESS != lError )
+ {
+ pLocalServices[ cEntriesRead ].HighMark = 0;
+ lError = ERROR_SUCCESS;
+ }
+
+ if ( ( ERROR_SUCCESS == RegQueryValueEx( hKeyService, REG_VALUE_NAME, NULL, NULL, NULL, &cbDisplayName ) )
+ && ( ERROR_SUCCESS == RegQueryValueEx( hKeyService, REG_VALUE_FAMILY, NULL, NULL, NULL, &cbFamilyDisplayName ) ) )
+ {
+ cbKeyName = sizeof( *szKeyName ) * ( 1 + lstrlen( szKeyName ) );
+
+ pLocalServices[ cEntriesRead ].KeyName = MIDL_user_allocate( cbKeyName );
+
+ if ( NULL == pLocalServices[ cEntriesRead ].KeyName )
+ {
+ lError = ERROR_OUTOFMEMORY;
+ }
+ else
+ {
+ lstrcpy( pLocalServices[ cEntriesRead ].KeyName, szKeyName );
+
+ pLocalServices[ cEntriesRead ].DisplayName = MIDL_user_allocate( cbDisplayName );
+
+ if ( NULL == pLocalServices[ cEntriesRead ].DisplayName )
+ {
+ lError = ERROR_OUTOFMEMORY;
+ }
+ else
+ {
+ lError = RegQueryValueEx( hKeyService, REG_VALUE_NAME, NULL, NULL, (LPBYTE) pLocalServices[ cEntriesRead ].DisplayName, &cbDisplayName );
+
+ if ( ERROR_SUCCESS == lError )
+ {
+ pLocalServices[ cEntriesRead ].FamilyDisplayName = MIDL_user_allocate( cbFamilyDisplayName );
+
+ if ( NULL == pLocalServices[ cEntriesRead ].FamilyDisplayName )
+ {
+ lError = ERROR_OUTOFMEMORY;
+ }
+ else
+ {
+ lError = RegQueryValueEx( hKeyService, REG_VALUE_FAMILY, NULL, NULL, (LPBYTE) pLocalServices[ cEntriesRead ].FamilyDisplayName, &cbFamilyDisplayName );
+
+ if ( ERROR_SUCCESS != lError )
+ {
+ MIDL_user_free( pLocalServices[ cEntriesRead ].FamilyDisplayName );
+ }
+ }
+ }
+
+ if ( ERROR_SUCCESS != lError )
+ {
+ MIDL_user_free( pLocalServices[ cEntriesRead ].DisplayName );
+ }
+ }
+
+ if ( ERROR_SUCCESS != lError )
+ {
+ MIDL_user_free( pLocalServices[ cEntriesRead ].KeyName );
+ }
+ else
+ {
+ // all data for this service was retrieved!
+ cEntriesRead++;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ RegCloseKey( hKeyService );
+ }
+
+ if ( ERROR_OUTOFMEMORY != lError )
+ {
+ // continue enumeration...
+ lError = ERROR_SUCCESS;
+ }
+ }
+ }
+ }
+
+ RegCloseKey( hKeyLicenseInfo );
+ }
+
+ RegCloseKey( hKeyLocalMachine );
+ }
+
+ switch ( lError )
+ {
+ case ERROR_SUCCESS:
+ case ERROR_NO_MORE_ITEMS:
+ Status = STATUS_SUCCESS;
+ break;
+ case ERROR_ACCESS_DENIED:
+ Status = STATUS_ACCESS_DENIED;
+ break;
+ case ERROR_OUTOFMEMORY:
+ Status = STATUS_NO_MEMORY;
+ break;
+ case ERROR_FILE_NOT_FOUND:
+ case ERROR_PATH_NOT_FOUND:
+ Status = STATUS_NOT_FOUND;
+ break;
+ default:
+ Status = STATUS_UNSUCCESSFUL;
+ break;
+ }
+
+ if ( STATUS_SUCCESS != Status )
+ {
+ // free all of our allocations
+ DWORD i;
+
+ for ( i=0; i < cEntriesRead; i++ )
+ {
+ MIDL_user_free( pLocalServices[ i ].KeyName );
+ MIDL_user_free( pLocalServices[ i ].DisplayName );
+ MIDL_user_free( pLocalServices[ i ].FamilyDisplayName );
+ }
+
+ MIDL_user_free( pLocalServices );
+ }
+ else
+ {
+ // success! return the array of services.
+ *bufptr = (LPBYTE) pLocalServices;
+ *EntriesRead = cEntriesRead;
+ *TotalEntries = cEntriesRead;
+ *ResumeHandle = 0;
+ }
+ }
+
+ return Status;
+}
+
+
+NTSTATUS
+NTAPI
+LlsLocalServiceEnumA(
+ LLS_HANDLE Handle,
+ DWORD Level,
+ LPBYTE* bufptr,
+ DWORD PrefMaxLen,
+ LPDWORD EntriesRead,
+ LPDWORD TotalEntries,
+ LPDWORD ResumeHandle )
+{
+ NTSTATUS Status;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsLocalServiceEnumA\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+
+ if (pLocalHandle == NULL)
+ {
+ Status = STATUS_INVALID_PARAMETER;
+ }
+ else if ( LlsCapabilityIsSupported( Handle, LLS_CAPABILITY_LOCAL_SERVICE_API ) )
+ {
+ GENERIC_INFO_CONTAINER GenericInfoContainer;
+ GENERIC_ENUM_STRUCT InfoStruct;
+
+ GenericInfoContainer.Buffer = NULL;
+ GenericInfoContainer.EntriesRead = 0;
+
+ InfoStruct.Container = &GenericInfoContainer;
+ InfoStruct.Level = Level;
+
+ try {
+ Status = LlsrLocalServiceEnumA(
+ pLocalHandle->Handle,
+ (PLLS_LOCAL_SERVICE_ENUM_STRUCTA) &InfoStruct,
+ PrefMaxLen,
+ TotalEntries,
+ ResumeHandle
+ );
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ if ((Status == STATUS_SUCCESS) || (Status == STATUS_MORE_ENTRIES)) {
+ *bufptr = (LPBYTE) GenericInfoContainer.Buffer;
+ *EntriesRead = GenericInfoContainer.EntriesRead;
+ }
+ }
+ else
+ {
+ Status = STATUS_NOT_SUPPORTED;
+ }
+
+ return Status;
+}
+
+
+NTSTATUS
+NTAPI
+LlsLocalServiceAddW(
+ LLS_HANDLE Handle,
+ DWORD Level,
+ LPBYTE bufptr )
+{
+ NTSTATUS Status;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsLocalServiceAddW\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+
+ if (pLocalHandle == NULL)
+ {
+ Status = STATUS_INVALID_PARAMETER;
+ }
+ else if ( LlsCapabilityIsSupported( Handle, LLS_CAPABILITY_LOCAL_SERVICE_API ) )
+ {
+ try
+ {
+ Status = LlsrLocalServiceAddW( pLocalHandle->Handle, Level, (PLLS_LOCAL_SERVICE_INFOW) bufptr );
+ }
+ except (TRUE)
+ {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+ }
+ else if ( 0 != Level )
+ {
+ Status = STATUS_INVALID_LEVEL;
+ }
+ else if ( ( NULL == ((PLLS_LOCAL_SERVICE_INFO_0W) bufptr)->KeyName )
+ || ( NULL == ((PLLS_LOCAL_SERVICE_INFO_0W) bufptr)->DisplayName )
+ || ( NULL == ((PLLS_LOCAL_SERVICE_INFO_0W) bufptr)->FamilyDisplayName ) )
+ {
+ Status = STATUS_INVALID_PARAMETER;
+ }
+ else
+ {
+ PLLS_LOCAL_SERVICE_INFO_0W LocalServiceInfo;
+ LONG lError;
+ HKEY hKeyLocalMachine;
+
+ LocalServiceInfo = (PLLS_LOCAL_SERVICE_INFO_0W) bufptr;
+
+ lError = RegConnectRegistry( pLocalHandle->szServerName, HKEY_LOCAL_MACHINE, &hKeyLocalMachine );
+
+ if ( ERROR_SUCCESS == lError )
+ {
+ HKEY hKeyLicenseInfo;
+
+ lError = RegOpenKeyEx( hKeyLocalMachine, TEXT( "System\\CurrentControlSet\\Services\\LicenseInfo" ), 0, KEY_WRITE, &hKeyLicenseInfo );
+
+ if ( ERROR_SUCCESS == lError )
+ {
+ HKEY hKeyService;
+ DWORD dwDisposition;
+
+ // create key
+ lError = RegCreateKeyEx( hKeyLicenseInfo, LocalServiceInfo->KeyName, 0, NULL, 0, KEY_WRITE, NULL, &hKeyService, &dwDisposition );
+
+ if ( ERROR_SUCCESS == lError )
+ {
+ // set DisplayName
+ lError = RegSetValueEx( hKeyService, TEXT( "DisplayName" ), 0, REG_SZ, (LPBYTE) LocalServiceInfo->DisplayName, sizeof( *LocalServiceInfo->DisplayName ) * ( 1 + lstrlen( LocalServiceInfo->DisplayName ) ) );
+
+ if ( ERROR_SUCCESS == lError )
+ {
+ // set FamilyDisplayName
+ lError = RegSetValueEx( hKeyService, TEXT( "FamilyDisplayName" ), 0, REG_SZ, (LPBYTE) LocalServiceInfo->FamilyDisplayName, sizeof( *LocalServiceInfo->FamilyDisplayName ) * ( 1 + lstrlen( LocalServiceInfo->FamilyDisplayName ) ) );
+ }
+
+ RegCloseKey( hKeyService );
+ }
+
+ RegCloseKey( hKeyLicenseInfo );
+ }
+
+ RegCloseKey( hKeyLocalMachine );
+ }
+
+ switch ( lError )
+ {
+ case ERROR_SUCCESS:
+ Status = STATUS_SUCCESS;
+ break;
+ case ERROR_FILE_NOT_FOUND:
+ case ERROR_PATH_NOT_FOUND:
+ Status = STATUS_OBJECT_NAME_NOT_FOUND;
+ break;
+ case ERROR_ACCESS_DENIED:
+ Status = STATUS_ACCESS_DENIED;
+ break;
+ default:
+ Status = STATUS_UNSUCCESSFUL;
+ break;
+ }
+
+ if ( STATUS_SUCCESS == Status )
+ {
+ // set remaining items
+ Status = LlsLocalServiceInfoSetW( Handle, LocalServiceInfo->KeyName, Level, bufptr );
+ }
+ }
+
+ return Status;
+}
+
+
+NTSTATUS
+NTAPI
+LlsLocalServiceAddA(
+ LLS_HANDLE Handle,
+ DWORD Level,
+ LPBYTE bufptr )
+{
+ NTSTATUS Status;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsLocalServiceAddA\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+
+ if (pLocalHandle == NULL)
+ {
+ Status = STATUS_INVALID_PARAMETER;
+ }
+ else if ( LlsCapabilityIsSupported( Handle, LLS_CAPABILITY_LOCAL_SERVICE_API ) )
+ {
+ try {
+ Status = LlsrLocalServiceAddA( pLocalHandle->Handle, Level, (PLLS_LOCAL_SERVICE_INFOA) bufptr );
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+ }
+ else
+ {
+ Status = STATUS_NOT_SUPPORTED;
+ }
+
+ return Status;
+}
+
+
+NTSTATUS
+NTAPI
+LlsLocalServiceInfoSetW(
+ LLS_HANDLE Handle,
+ LPWSTR KeyName,
+ DWORD Level,
+ LPBYTE bufptr )
+{
+ NTSTATUS Status;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsLocalServiceInfoSetW\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+
+ if (pLocalHandle == NULL)
+ {
+ Status = STATUS_INVALID_PARAMETER;
+ }
+ else if ( LlsCapabilityIsSupported( Handle, LLS_CAPABILITY_LOCAL_SERVICE_API ) )
+ {
+ try
+ {
+ Status = LlsrLocalServiceInfoSetW( pLocalHandle->Handle, KeyName, Level, (PLLS_LOCAL_SERVICE_INFOW) bufptr );
+ }
+ except (TRUE)
+ {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+ }
+ else if ( 0 != Level )
+ {
+ Status = STATUS_INVALID_LEVEL;
+ }
+ else if ( NULL == KeyName )
+ {
+ Status = STATUS_INVALID_PARAMETER;
+ }
+ else
+ {
+ LONG lError;
+ HKEY hKeyLocalMachine;
+
+ lError = RegConnectRegistry( pLocalHandle->szServerName, HKEY_LOCAL_MACHINE, &hKeyLocalMachine );
+
+ if ( ERROR_SUCCESS == lError )
+ {
+ HKEY hKeyLicenseInfo;
+
+ lError = RegOpenKeyEx( hKeyLocalMachine, REG_KEY_LICENSE, 0, KEY_WRITE, &hKeyLicenseInfo );
+
+ if ( ERROR_SUCCESS == lError )
+ {
+ HKEY hKeyService;
+ PLLS_LOCAL_SERVICE_INFO_0W LocalServiceInfo;
+
+ LocalServiceInfo = (PLLS_LOCAL_SERVICE_INFO_0W) bufptr;
+
+ lError = RegOpenKeyEx( hKeyLicenseInfo, KeyName, 0, KEY_WRITE, &hKeyService );
+
+ if ( ERROR_SUCCESS == lError )
+ {
+ // set Mode
+ lError = RegSetValueEx( hKeyService, REG_VALUE_MODE, 0, REG_DWORD, (LPBYTE) &LocalServiceInfo->Mode, sizeof( LocalServiceInfo->Mode ) );
+
+ if ( ERROR_SUCCESS == lError )
+ {
+ // set FlipAllow
+ lError = RegSetValueEx( hKeyService, REG_VALUE_FLIP, 0, REG_DWORD, (LPBYTE) &LocalServiceInfo->FlipAllow, sizeof( LocalServiceInfo->FlipAllow ) );
+
+ if ( ERROR_SUCCESS == lError )
+ {
+ // set ConcurrentLimit
+ lError = RegSetValueEx( hKeyService, REG_VALUE_LIMIT, 0, REG_DWORD, (LPBYTE) &LocalServiceInfo->ConcurrentLimit, sizeof( LocalServiceInfo->ConcurrentLimit ) );
+ }
+ }
+
+ RegCloseKey( hKeyService );
+ }
+
+ RegCloseKey( hKeyLicenseInfo );
+ }
+
+ RegCloseKey( hKeyLocalMachine );
+ }
+
+ switch ( lError )
+ {
+ case ERROR_SUCCESS:
+ Status = STATUS_SUCCESS;
+ break;
+ case ERROR_FILE_NOT_FOUND:
+ case ERROR_PATH_NOT_FOUND:
+ Status = STATUS_OBJECT_NAME_NOT_FOUND;
+ break;
+ case ERROR_ACCESS_DENIED:
+ Status = STATUS_ACCESS_DENIED;
+ break;
+ default:
+ Status = STATUS_UNSUCCESSFUL;
+ break;
+ }
+ }
+
+ return Status;
+}
+
+
+NTSTATUS
+NTAPI
+LlsLocalServiceInfoSetA(
+ LLS_HANDLE Handle,
+ LPSTR KeyName,
+ DWORD Level,
+ LPBYTE bufptr )
+{
+ NTSTATUS Status;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsLocalServiceInfoSetA\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+
+ if (pLocalHandle == NULL)
+ {
+ Status = STATUS_INVALID_PARAMETER;
+ }
+ else if ( LlsCapabilityIsSupported( Handle, LLS_CAPABILITY_LOCAL_SERVICE_API ) )
+ {
+ try {
+ Status = LlsrLocalServiceInfoSetA( pLocalHandle->Handle, KeyName, Level, (PLLS_LOCAL_SERVICE_INFOA) bufptr );
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+ }
+ else
+ {
+ Status = STATUS_NOT_SUPPORTED;
+ }
+
+ return Status;
+}
+
+
+NTSTATUS
+NTAPI
+LlsLocalServiceInfoGetW(
+ LLS_HANDLE Handle,
+ LPWSTR KeyName,
+ DWORD Level,
+ LPBYTE * pbufptr )
+{
+ NTSTATUS Status;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsLocalServiceInfoGetW\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+
+ if (pLocalHandle == NULL)
+ {
+ Status = STATUS_INVALID_PARAMETER;
+ }
+ else if ( LlsCapabilityIsSupported( Handle, LLS_CAPABILITY_LOCAL_SERVICE_API ) )
+ {
+ try
+ {
+ Status = LlsrLocalServiceInfoGetW( pLocalHandle->Handle, KeyName, Level, (PLLS_LOCAL_SERVICE_INFOW *) pbufptr );
+ }
+ except (TRUE)
+ {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+ }
+ else if ( 0 != Level )
+ {
+ Status = STATUS_INVALID_LEVEL;
+ }
+ else if ( NULL == KeyName )
+ {
+ Status = STATUS_INVALID_PARAMETER;
+ }
+ else
+ {
+ PLLS_LOCAL_SERVICE_INFO_0W pLocalService = NULL;
+ LONG lError;
+ HKEY hKeyLocalMachine;
+
+ lError = RegConnectRegistry( pLocalHandle->szServerName, HKEY_LOCAL_MACHINE, &hKeyLocalMachine );
+
+ if ( ERROR_SUCCESS == lError )
+ {
+ HKEY hKeyLicenseInfo;
+
+ lError = RegOpenKeyEx( hKeyLocalMachine, REG_KEY_LICENSE, 0, KEY_READ, &hKeyLicenseInfo );
+
+ if ( ERROR_SUCCESS == lError )
+ {
+ HKEY hKeyService;
+
+ lError = RegOpenKeyEx( hKeyLicenseInfo, KeyName, 0, KEY_READ, &hKeyService );
+
+ if ( ERROR_SUCCESS == lError )
+ {
+ pLocalService = MIDL_user_allocate( sizeof( *pLocalService ) );
+
+ if ( NULL == pLocalService )
+ {
+ lError = ERROR_OUTOFMEMORY;
+ }
+ else
+ {
+ DWORD cbData;
+
+ cbData = sizeof( pLocalService->Mode );
+ lError = RegQueryValueEx( hKeyService, REG_VALUE_MODE, NULL, NULL, (LPBYTE) &pLocalService->Mode, &cbData );
+
+ if ( ERROR_SUCCESS == lError )
+ {
+ cbData = sizeof( pLocalService->FlipAllow );
+ lError = RegQueryValueEx( hKeyService, REG_VALUE_FLIP, NULL, NULL, (LPBYTE) &pLocalService->FlipAllow, &cbData );
+
+ if ( ERROR_SUCCESS == lError )
+ {
+ cbData = sizeof( pLocalService->ConcurrentLimit );
+ lError = RegQueryValueEx( hKeyService, REG_VALUE_LIMIT, NULL, NULL, (LPBYTE) &pLocalService->ConcurrentLimit, &cbData );
+
+ if ( ERROR_SUCCESS == lError )
+ {
+ DWORD cbKeyName;
+ DWORD cbDisplayName;
+ DWORD cbFamilyDisplayName;
+
+ cbData = sizeof( pLocalService->HighMark );
+ lError = RegQueryValueEx( hKeyService, REG_VALUE_HIGHMARK, NULL, NULL, (LPBYTE) &pLocalService->HighMark, &cbData );
+
+ if ( ERROR_SUCCESS != lError )
+ {
+ pLocalService->HighMark = 0;
+ lError = ERROR_SUCCESS;
+ }
+
+ if ( ( ERROR_SUCCESS == RegQueryValueEx( hKeyService, REG_VALUE_NAME, NULL, NULL, NULL, &cbDisplayName ) )
+ && ( ERROR_SUCCESS == RegQueryValueEx( hKeyService, REG_VALUE_FAMILY, NULL, NULL, NULL, &cbFamilyDisplayName ) ) )
+ {
+ cbKeyName = sizeof( *KeyName ) * ( 1 + lstrlen( KeyName ) );
+
+ pLocalService->KeyName = MIDL_user_allocate( cbKeyName );
+
+ if ( NULL == pLocalService->KeyName )
+ {
+ lError = ERROR_OUTOFMEMORY;
+ }
+ else
+ {
+ lstrcpy( pLocalService->KeyName, KeyName );
+
+ pLocalService->DisplayName = MIDL_user_allocate( cbDisplayName );
+
+ if ( NULL == pLocalService->DisplayName )
+ {
+ lError = ERROR_OUTOFMEMORY;
+ }
+ else
+ {
+ lError = RegQueryValueEx( hKeyService, REG_VALUE_NAME, NULL, NULL, (LPBYTE) pLocalService->DisplayName, &cbDisplayName );
+
+ if ( ERROR_SUCCESS == lError )
+ {
+ pLocalService->FamilyDisplayName = MIDL_user_allocate( cbFamilyDisplayName );
+
+ if ( NULL == pLocalService->FamilyDisplayName )
+ {
+ lError = ERROR_OUTOFMEMORY;
+ }
+ else
+ {
+ lError = RegQueryValueEx( hKeyService, REG_VALUE_FAMILY, NULL, NULL, (LPBYTE) pLocalService->FamilyDisplayName, &cbFamilyDisplayName );
+
+ if ( ERROR_SUCCESS != lError )
+ {
+ MIDL_user_free( pLocalService->FamilyDisplayName );
+ }
+ }
+ }
+
+ if ( ERROR_SUCCESS != lError )
+ {
+ MIDL_user_free( pLocalService->DisplayName );
+ }
+ }
+
+ if ( ERROR_SUCCESS != lError )
+ {
+ MIDL_user_free( pLocalService->KeyName );
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if ( ERROR_SUCCESS != lError )
+ {
+ MIDL_user_free( pLocalService );
+ }
+ }
+
+ RegCloseKey( hKeyService );
+ }
+
+ RegCloseKey( hKeyLicenseInfo );
+ }
+
+ RegCloseKey( hKeyLocalMachine );
+ }
+
+ switch ( lError )
+ {
+ case ERROR_SUCCESS:
+ Status = STATUS_SUCCESS;
+ break;
+ case ERROR_FILE_NOT_FOUND:
+ case ERROR_PATH_NOT_FOUND:
+ Status = STATUS_OBJECT_NAME_NOT_FOUND;
+ break;
+ case ERROR_ACCESS_DENIED:
+ Status = STATUS_ACCESS_DENIED;
+ break;
+ default:
+ Status = STATUS_UNSUCCESSFUL;
+ break;
+ }
+
+ if ( STATUS_SUCCESS == Status )
+ {
+ *pbufptr = (LPBYTE) pLocalService;
+ }
+ }
+
+ return Status;
+}
+
+
+NTSTATUS
+NTAPI
+LlsLocalServiceInfoGetA(
+ LLS_HANDLE Handle,
+ DWORD Level,
+ LPSTR KeyName,
+ LPBYTE * pbufptr )
+{
+ NTSTATUS Status;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsLocalServiceInfoGetA\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+
+ if (pLocalHandle == NULL)
+ {
+ Status = STATUS_INVALID_PARAMETER;
+ }
+ else if ( LlsCapabilityIsSupported( Handle, LLS_CAPABILITY_LOCAL_SERVICE_API ) )
+ {
+ try {
+ Status = LlsrLocalServiceInfoGetA( pLocalHandle->Handle, KeyName, Level, (PLLS_LOCAL_SERVICE_INFOA *) pbufptr );
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+ }
+ else
+ {
+ Status = STATUS_NOT_SUPPORTED;
+ }
+
+ return Status;
+}
+
+
diff --git a/private/net/svcdlls/lls/client/llsrpc.def b/private/net/svcdlls/lls/client/llsrpc.def
new file mode 100644
index 000000000..2e4228478
--- /dev/null
+++ b/private/net/svcdlls/lls/client/llsrpc.def
@@ -0,0 +1,106 @@
+LIBRARY LLSRPC
+
+DESCRIPTION 'Licence Logging Service RPC API'
+
+CODE LOADONCALL MOVEABLE DISCARDABLE
+DATA PRELOAD MOVEABLE SINGLE
+
+HEAPSIZE 1024
+
+EXPORTS
+ LlsConnectW
+ LlsConnectA
+ LlsConnectEnterpriseW
+ LlsConnectEnterpriseA
+ LlsEnterpriseServerFindW
+ LlsEnterpriseServerFindA
+ LlsClose
+ LlsFreeMemory
+ LlsLicenseEnumW
+ LlsLicenseEnumA
+ LlsLicenseAddW
+ LlsLicenseAddA
+ LlsProductEnumW
+ LlsProductEnumA
+ LlsProductAddW
+ LlsProductAddA
+ LlsProductUserEnumW
+ LlsProductUserEnumA
+ LlsProductServerEnumW
+ LlsProductServerEnumA
+ LlsProductLicenseEnumW
+ LlsProductLicenseEnumA
+ LlsUserEnumW
+ LlsUserEnumA
+ LlsUserInfoGetW
+ LlsUserInfoGetA
+ LlsUserInfoSetW
+ LlsUserInfoSetA
+ LlsUserDeleteW
+ LlsUserDeleteA
+ LlsUserProductEnumW
+ LlsUserProductEnumA
+ LlsUserProductDeleteW
+ LlsUserProductDeleteA
+ LlsGroupEnumW
+ LlsGroupEnumA
+ LlsGroupInfoGetW
+ LlsGroupInfoGetA
+ LlsGroupInfoSetW
+ LlsGroupInfoSetA
+ LlsGroupUserEnumW
+ LlsGroupUserEnumA
+ LlsGroupUserAddW
+ LlsGroupUserAddA
+ LlsGroupUserDeleteW
+ LlsGroupUserDeleteA
+ LlsGroupAddW
+ LlsGroupAddA
+ LlsGroupDeleteW
+ LlsGroupDeleteA
+ LlsServerEnumW
+ LlsServerEnumA
+ LlsServerProductEnumW
+ LlsServerProductEnumA
+ LlsLocalProductEnumW
+ LlsLocalProductEnumA
+ LlsLocalProductInfoGetW
+ LlsLocalProductInfoGetA
+ LlsLocalProductInfoSetW
+ LlsLocalProductInfoSetA
+ LlsServiceInfoGetW
+ LlsServiceInfoGetA
+ LlsServiceInfoSetW
+ LlsServiceInfoSetA
+ LlsReplConnectW
+ LlsReplClose
+ LlsReplicationRequestW
+ LlsReplicationServerAddW
+ LlsReplicationServerServiceAddW
+ LlsReplicationServiceAddW
+ LlsReplicationUserAddW
+
+ LlsProductSecurityGetW
+ LlsProductSecurityGetA
+ LlsProductSecuritySetW
+ LlsProductSecuritySetA
+ LlsProductLicensesGetW
+ LlsProductLicensesGetA
+ LlsCertificateClaimEnumA
+ LlsCertificateClaimEnumW
+ LlsCertificateClaimAddCheckA
+ LlsCertificateClaimAddCheckW
+ LlsCertificateClaimAddA
+ LlsCertificateClaimAddW
+ LlsReplicationCertDbAddW
+ LlsReplicationProductSecurityAddW
+ LlsReplicationUserAddExW
+ LlsCapabilityIsSupported
+ LlsLocalServiceEnumW
+ LlsLocalServiceEnumA
+ LlsLocalServiceAddW
+ LlsLocalServiceAddA
+ LlsLocalServiceInfoSetW
+ LlsLocalServiceInfoSetA
+ LlsLocalServiceInfoGetW
+ LlsLocalServiceInfoGetA
diff --git a/private/net/svcdlls/lls/client/llsrpc.rc b/private/net/svcdlls/lls/client/llsrpc.rc
new file mode 100644
index 000000000..d33c24461
--- /dev/null
+++ b/private/net/svcdlls/lls/client/llsrpc.rc
@@ -0,0 +1,30 @@
+//=============================================================================
+// Microsoft (R) License Logging Service (tm). Copyright (C) 1991-1995.
+//
+// MODULE: llsrpc.rc
+//
+// Modification History
+//
+// arth Mar, 10 1995 Created
+//=============================================================================
+
+#include "windows.h"
+
+
+// include NT version resources
+#define VER_LEGALCOPYRIGHT_YEARS "1981-1995"
+#include "ntver.rc"
+
+#define IDS_BACKOFFICE 1500
+
+
+STRINGTABLE DISCARDABLE
+{
+ IDS_BACKOFFICE "Microsoft BackOffice"
+}
+
+
+// Event-logging support
+
+LANGUAGE 0x9,0x1
+1 11 MSG00001.bin
diff --git a/private/net/svcdlls/lls/client/makefile b/private/net/svcdlls/lls/client/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/net/svcdlls/lls/client/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/net/svcdlls/lls/client/makefile.inc b/private/net/svcdlls/lls/client/makefile.inc
new file mode 100644
index 000000000..0f9608a94
--- /dev/null
+++ b/private/net/svcdlls/lls/client/makefile.inc
@@ -0,0 +1,8 @@
+
+!IFNDEF MC
+MC=mc
+!ENDIF
+
+llsevent.h msg00001.bin llsevent.rc : llsevent.mc
+ $(MC) llsevent.mc
+ IF EXIST llsevent.h copy llsevent.h ..\inc
diff --git a/private/net/svcdlls/lls/client/ntver.rc b/private/net/svcdlls/lls/client/ntver.rc
new file mode 100644
index 000000000..913d1e4d3
--- /dev/null
+++ b/private/net/svcdlls/lls/client/ntver.rc
@@ -0,0 +1,53 @@
+/*
+** Template for version resources. Place this in your .rc file,
+** editing the values for VER_FILETYPE, VER_FILESUBTYPE,
+** VER_FILEDESCRIPTION_STR and VER_INTERNALNAME_STR as needed.
+** See winver.h for possible values.
+**
+** Ntverp.h defines several global values that don't need to be
+** changed except for official releases such as betas, sdk updates, etc.
+**
+** Common.ver has the actual version resource structure that all these
+** #defines eventually initialize.
+*/
+
+/* #include <windows.h> needed if this will be the .rc file */
+
+#define VER_LEGALCOPYRIGHT_YEARS "1981-1995"
+#include <ntverp.h>
+
+/*-----------------------------------------------*/
+/* the following lines are specific to this file */
+/*-----------------------------------------------*/
+
+/* VER_FILETYPE, VER_FILESUBTYPE, VER_FILEDESCRIPTION_STR
+ * and VER_INTERNALNAME_STR must be defined before including COMMON.VER
+ * The strings don't need a '\0', since common.ver has them.
+ */
+#define VER_FILETYPE VFT_DLL
+/* possible values: VFT_UNKNOWN
+ VFT_APP
+ VFT_DLL
+ VFT_DRV
+ VFT_FONT
+ VFT_VXD
+ VFT_STATIC_LIB
+*/
+#define VER_FILESUBTYPE VFT2_UNKNOWN
+/* possible values VFT2_UNKNOWN
+ VFT2_DRV_PRINTER
+ VFT2_DRV_KEYBOARD
+ VFT2_DRV_LANGUAGE
+ VFT2_DRV_DISPLAY
+ VFT2_DRV_MOUSE
+ VFT2_DRV_NETWORK
+ VFT2_DRV_SYSTEM
+ VFT2_DRV_INSTALLABLE
+ VFT2_DRV_SOUND
+ VFT2_DRV_COMM
+*/
+#define VER_FILEDESCRIPTION_STR "License Logging Service RPC Interface"
+#define VER_INTERNALNAME_STR "LLSRPC.DLL"
+#define VER_ORIGINALFILENAME_STR "LLSRPC.DLL"
+
+#include "common.ver"
diff --git a/private/net/svcdlls/lls/client/sources b/private/net/svcdlls/lls/client/sources
new file mode 100644
index 000000000..775fd58df
--- /dev/null
+++ b/private/net/svcdlls/lls/client/sources
@@ -0,0 +1,68 @@
+!IF 0
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+
+Author:
+
+ Steve Wood (stevewo) 12-Apr-1990
+
+NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl
+
+!ENDIF
+
+MAJORCOMP=lls
+MINORCOMP=client
+
+TARGETNAME=llsrpc
+TARGETPATH=$(BASEDIR)\public\sdk\lib
+TARGETTYPE=DYNLINK
+
+#DLLENTRY=DllEntryPoint
+DLLENTRY=DllMain
+
+USE_CRTDLL=1
+
+#DLLBASE=0x7F000000
+SDKINC=$(BASEDIR)\public\sdk\inc
+PRIVINC=$(BASEDIR)\private\inc
+
+TARGETLIBS= \
+ ..\common\obj\*\llscomm.lib \
+ $(BASEDIR)\public\sdk\lib\*\rpcutil.lib \
+ $(BASEDIR)\public\sdk\lib\*\rpcrt4.lib \
+ $(BASEDIR)\Public\sdk\Lib\*\advapi32.lib \
+ $(BASEDIR)\public\sdk\lib\*\kernel32.lib \
+ $(BASEDIR)\public\sdk\lib\*\netapi32.lib \
+ $(BASEDIR)\public\sdk\lib\*\user32.lib
+
+INCLUDES=$(PRIVINC);$(SDKINC);..\inc
+
+SOURCES= \
+ llsrpc_c.c \
+ llsrpc.c \
+ llsrpc.rc
+
+C_DEFINES=-DINCL_32 -DNT -DWIN32 -DRPC_NO_WINDOWS_H -DUNICODE -D_UNICODE
+UMTYPE=windows
+UMLIBS= \
+ ..\common\obj\*\llscomm.lib \
+ $(BASEDIR)\public\sdk\lib\*\rpcutil.lib \
+ $(BASEDIR)\public\sdk\lib\*\rpcrt4.lib \
+ $(BASEDIR)\Public\Sdk\Lib\*\kernel32.lib \
+ $(BASEDIR)\Public\Sdk\Lib\*\advapi32.lib \
+ $(BASEDIR)\public\sdk\lib\*\netapi32.lib \
+ $(BASEDIR)\public\sdk\lib\*\user32.lib
+
+NTTARGETFILE0=llsevent.h
diff --git a/private/net/svcdlls/lls/common/debug.c b/private/net/svcdlls/lls/common/debug.c
new file mode 100644
index 000000000..c524bffde
--- /dev/null
+++ b/private/net/svcdlls/lls/common/debug.c
@@ -0,0 +1,19 @@
+
+#include <windows.h>
+#include <windowsx.h>
+#include <io.h>
+#include <malloc.h>
+#include <string.h>
+
+#if DBG
+void __cdecl dprintf(LPTSTR szFormat, ...) {
+ static TCHAR tmpStr[1024];
+ va_list marker;
+
+ va_start(marker, szFormat);
+ wvsprintf(tmpStr, szFormat, marker);
+ OutputDebugString(tmpStr);
+ va_end(marker);
+
+} // dprintf
+#endif
diff --git a/private/net/svcdlls/lls/common/makefile b/private/net/svcdlls/lls/common/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/net/svcdlls/lls/common/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/net/svcdlls/lls/common/sources b/private/net/svcdlls/lls/common/sources
new file mode 100644
index 000000000..edb3e0412
--- /dev/null
+++ b/private/net/svcdlls/lls/common/sources
@@ -0,0 +1,44 @@
+
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+
+Author:
+
+ Steve Wood (stevewo) 12-Apr-1990
+
+NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl
+
+!ENDIF
+
+MAJORCOMP=lls
+MINORCOMP=common
+
+TARGETNAME=llscomm
+TARGETPATH=obj
+TARGETTYPE=LIBRARY
+
+USE_CRTDLL=1
+
+INCLUDES=..\inc;$(_NTROOT)\public\sdk\inc;$(_NTROOT)\private\inc
+
+C_DEFINES=-DRPC_NO_WINDOWS_H -DUNICODE -D_UNICODE
+
+
+
+SOURCES= \
+ debug.c
+
+UMTYPE=windows
diff --git a/private/net/svcdlls/lls/dirs b/private/net/svcdlls/lls/dirs
new file mode 100644
index 000000000..f4ee31a11
--- /dev/null
+++ b/private/net/svcdlls/lls/dirs
@@ -0,0 +1 @@
+DIRS=common client server ccfapi32 ntlsapi test
diff --git a/private/net/svcdlls/lls/inc/debug.h b/private/net/svcdlls/lls/inc/debug.h
new file mode 100644
index 000000000..fa67d7307
--- /dev/null
+++ b/private/net/svcdlls/lls/inc/debug.h
@@ -0,0 +1,40 @@
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define TRACE_FUNCTION_TRACE 0x0001
+#define TRACE_WARNINGS 0x0002
+#define TRACE_PACK 0x0004
+#define TRACE_LICENSE_REQUEST 0x0008
+#define TRACE_LICENSE_FREE 0x0010
+#define TRACE_REGISTRY 0x0020
+#define TRACE_REPLICATION 0x0040
+#define TRACE_LPC 0x0080
+#define TRACE_RPC 0x0100
+#define TRACE_INIT 0x0200
+#define TRACE_DATABASE 0x0400
+
+#define SERVICE_TABLE_NUM 1
+#define USER_TABLE_NUM 2
+#define SID_TABLE_NUM 3
+#define LICENSE_TABLE_NUM 4
+#define ADD_CACHE_TABLE_NUM 5
+#define MASTER_SERVICE_TABLE_NUM 6
+#define SERVICE_FAMILY_TABLE_NUM 7
+#define MAPPING_TABLE_NUM 8
+#define SERVER_TABLE_NUM 9
+#define SECURE_PRODUCT_TABLE_NUM 10
+#define CERTIFICATE_TABLE_NUM 11
+
+#if DBG
+void __cdecl dprintf(LPTSTR szFormat, ...);
+
+extern DWORD TraceFlags;
+
+#else
+#define dprintf
+#endif
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/private/net/svcdlls/lls/inc/llsapi.h b/private/net/svcdlls/lls/inc/llsapi.h
new file mode 100644
index 000000000..b5ed01125
--- /dev/null
+++ b/private/net/svcdlls/lls/inc/llsapi.h
@@ -0,0 +1,1441 @@
+/*++
+
+Copyright (c) 1994-95 Microsoft Corporation
+
+Module Name:
+
+ llsapi.h
+
+Abstract:
+
+ License logging server's RPC API's.
+
+Author:
+
+ Arthur Hanson (arth) 21-Mar-1995
+
+Environment:
+
+ User Mode - Win32
+
+Revision History:
+
+ Jeff Parham (jeffparh) 04-Dec-1995
+ o Added type definitions, macros, and prototypes for extended RPC APIs
+ and license certificate APIs (available only post-3.51).
+ o Corrected prototypes for LlsServerEnumW(), LlsServerEnumA(),
+ LlsLocalProductInfoGetW(), and LlsLocalProductInfoGetA().
+
+--*/
+
+#ifndef _LLSAPI_H
+#define _LLSAPI_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#define LLS_FLAG_LICENSED 0x0001
+#define LLS_FLAG_UPDATE 0x0002
+#define LLS_FLAG_SUITE_USE 0x0004
+#define LLS_FLAG_SUITE_AUTO 0x0008
+
+#define LLS_FLAG_PRODUCT_PERSEAT 0x0010
+#define LLS_FLAG_PRODUCT_SWITCH 0x0020
+
+#define LLS_FLAG_DELETED 0x1000
+
+
+typedef PVOID LLS_HANDLE, *PLLS_HANDLE;
+typedef PVOID LLS_REPL_HANDLE, *PLLS_REPL_HANDLE;
+
+#define LLS_NUM_SECRETS ( 4 )
+
+typedef struct _LLS_LICENSE_INFO_0 {
+ LPTSTR Product;
+ LONG Quantity;
+ DWORD Date;
+ LPTSTR Admin;
+ LPTSTR Comment;
+} LLS_LICENSE_INFO_0, *PLLS_LICENSE_INFO_0;
+
+typedef struct _LLS_LICENSE_INFO_1 {
+ LPTSTR Product;
+ LPTSTR Vendor;
+ LONG Quantity;
+ DWORD MaxQuantity;
+ DWORD Date;
+ LPTSTR Admin;
+ LPTSTR Comment;
+ DWORD AllowedModes;
+ DWORD CertificateID;
+ LPTSTR Source;
+ DWORD ExpirationDate;
+ DWORD Secrets[ LLS_NUM_SECRETS ];
+} LLS_LICENSE_INFO_1, *PLLS_LICENSE_INFO_1;
+
+typedef struct _LLS_PRODUCT_INFO_0 {
+ LPTSTR Product;
+} LLS_PRODUCT_INFO_0, *PLLS_PRODUCT_INFO_0;
+
+typedef struct _LLS_PRODUCT_INFO_1 {
+ LPTSTR Product;
+ ULONG Purchased;
+ ULONG InUse;
+ ULONG ConcurrentTotal;
+ ULONG HighMark;
+} LLS_PRODUCT_INFO_1, *PLLS_PRODUCT_INFO_1;
+
+typedef struct _LLS_PRODUCT_USER_INFO_0 {
+ LPTSTR User;
+} LLS_PRODUCT_USER_INFO_0, *PLLS_PRODUCT_USER_INFO_0;
+
+typedef struct _LLS_PRODUCT_USER_INFO_1 {
+ LPTSTR User;
+ DWORD Flags;
+ DWORD LastUsed;
+ ULONG UsageCount;
+} LLS_PRODUCT_USER_INFO_1, *PLLS_PRODUCT_USER_INFO_1;
+
+
+typedef struct _LLS_PRODUCT_LICENSE_INFO_0 {
+ LONG Quantity;
+ DWORD Date;
+ LPTSTR Admin;
+ LPTSTR Comment;
+} LLS_PRODUCT_LICENSE_INFO_0, *PLLS_PRODUCT_LICENSE_INFO_0;
+
+typedef struct _LLS_PRODUCT_LICENSE_INFO_1 {
+ LONG Quantity;
+ DWORD MaxQuantity;
+ DWORD Date;
+ LPTSTR Admin;
+ LPTSTR Comment;
+ DWORD AllowedModes;
+ DWORD CertificateID;
+ LPTSTR Source;
+ DWORD ExpirationDate;
+ DWORD Secrets[ LLS_NUM_SECRETS ];
+} LLS_PRODUCT_LICENSE_INFO_1, *PLLS_PRODUCT_LICENSE_INFO_1;
+
+typedef struct _LLS_USER_INFO_0 {
+ LPTSTR Name;
+} LLS_USER_INFO_0, *PLLS_USER_INFO_0;
+
+typedef struct _LLS_USER_INFO_1 {
+ LPTSTR Name;
+ DWORD Flags;
+ LPTSTR Group;
+ ULONG Licensed;
+ ULONG UnLicensed;
+} LLS_USER_INFO_1, *PLLS_USER_INFO_1;
+
+typedef struct _LLS_USER_INFO_2 {
+ LPTSTR Name;
+ DWORD Flags;
+ LPTSTR Group;
+ ULONG Licensed;
+ ULONG UnLicensed;
+ LPTSTR Products;
+} LLS_USER_INFO_2, *PLLS_USER_INFO_2;
+
+typedef struct _LLS_USER_PRODUCT_INFO_0 {
+ LPTSTR Product;
+} LLS_USER_PRODUCT_INFO_0, *PLLS_USER_PRODUCT_INFO_0;
+
+typedef struct _LLS_USER_PRODUCT_INFO_1 {
+ LPTSTR Product;
+ DWORD Flags;
+ DWORD LastUsed;
+ ULONG UsageCount;
+} LLS_USER_PRODUCT_INFO_1, *PLLS_USER_PRODUCT_INFO_1;
+
+typedef struct _LLS_GROUP_INFO_0 {
+ LPTSTR Name;
+} LLS_GROUP_INFO_0, *PLLS_GROUP_INFO_0;
+
+typedef struct _LLS_GROUP_INFO_1 {
+ LPTSTR Name;
+ LPTSTR Comment;
+ ULONG Licenses;
+} LLS_GROUP_INFO_1, *PLLS_GROUP_INFO_1;
+
+
+#define LLS_REPLICATION_TYPE_DELTA 0
+#define LLS_REPLICATION_TYPE_TIME 1
+
+#define LLS_MODE_LICENSE_SERVER 0
+#define LLS_MODE_PDC 1
+#define LLS_MODE_ENTERPRISE_SERVER 2
+
+typedef struct _LLS_SERVICE_INFO_0 {
+ DWORD Version;
+ DWORD TimeStarted;
+ DWORD Mode;
+ LPTSTR ReplicateTo;
+ LPTSTR EnterpriseServer;
+ DWORD ReplicationType;
+ DWORD ReplicationTime;
+ DWORD UseEnterprise;
+ DWORD LastReplicated;
+} LLS_SERVICE_INFO_0, *PLLS_SERVICE_INFO_0;
+
+typedef struct _LLS_CONNECT_INFO_0 {
+ LPTSTR Domain;
+ LPTSTR EnterpriseServer;
+} LLS_CONNECT_INFO_0, *PLLS_CONNECT_INFO_0;
+
+
+typedef struct _LLS_SERVER_PRODUCT_INFO_0 {
+ LPTSTR Name;
+} LLS_SERVER_PRODUCT_INFO_0, *PLLS_SERVER_PRODUCT_INFO_0;
+
+typedef struct _LLS_SERVER_PRODUCT_INFO_1 {
+ LPTSTR Name;
+ DWORD Flags;
+ ULONG MaxUses;
+ ULONG MaxSetUses;
+ ULONG HighMark;
+} LLS_SERVER_PRODUCT_INFO_1, *PLLS_SERVER_PRODUCT_INFO_1;
+
+
+typedef struct _LLS_SERVER_INFO_0 {
+ LPTSTR Name;
+} LLS_SERVER_INFO_0, *PLLS_SERVER_INFO_0;
+
+typedef struct _LLS_CERTIFICATE_CLAIM_INFO_0
+{
+ TCHAR ServerName[ 1 + MAX_COMPUTERNAME_LENGTH ];
+ LONG Quantity;
+} LLS_CERTIFICATE_CLAIM_INFO_0, *PLLS_CERTIFICATE_CLAIM_INFO_0;
+
+typedef struct _LLS_LOCAL_SERVICE_INFO_0
+{
+ LPTSTR KeyName;
+ LPTSTR DisplayName;
+ LPTSTR FamilyDisplayName;
+ DWORD Mode;
+ DWORD FlipAllow;
+ DWORD ConcurrentLimit;
+ DWORD HighMark;
+} LLS_LOCAL_SERVICE_INFO_0, *PLLS_LOCAL_SERVICE_INFO_0;
+
+#define LLS_LICENSE_MODE_PER_SEAT ( 0 )
+#define LLS_LICENSE_MODE_PER_SERVER ( 1 )
+
+#define LLS_LICENSE_MODE_ALLOW_PER_SEAT ( 1 )
+#define LLS_LICENSE_MODE_ALLOW_PER_SERVER ( 2 )
+
+#define LLS_LICENSE_FLIP_ALLOW_PER_SEAT ( 1 )
+#define LLS_LICENSE_FLIP_ALLOW_PER_SERVER ( 2 )
+
+
+// capability flags; query with LlsCapabilityIsSupported
+#define LLS_CAPABILITY_SECURE_CERTIFICATES ( 0 )
+#define LLS_CAPABILITY_REPLICATE_CERT_DB ( 1 )
+#define LLS_CAPABILITY_REPLICATE_PRODUCT_SECURITY ( 2 )
+#define LLS_CAPABILITY_REPLICATE_USERS_EX ( 3 )
+#define LLS_CAPABILITY_SERVICE_INFO_GETW ( 4 )
+#define LLS_CAPABILITY_LOCAL_SERVICE_API ( 5 )
+#define LLS_CAPABILITY_MAX ( 32 )
+
+
+#ifndef NO_LLS_APIS
+//
+// Connection control API's
+//
+
+NTSTATUS
+NTAPI
+LlsConnectW(
+ IN LPWSTR Server,
+ OUT PLLS_HANDLE Handle
+ );
+
+NTSTATUS
+NTAPI
+LlsConnectA(
+ IN LPSTR Server,
+ OUT PLLS_HANDLE Handle
+ );
+#ifdef UNICODE
+# define LlsConnect LlsConnectW
+#else
+# define LlsConnect LlsConnectA
+#endif
+
+typedef NTSTATUS (NTAPI *PLLS_CONNECT_W)( LPWSTR, PLLS_HANDLE );
+typedef NTSTATUS (NTAPI *PLLS_CONNECT_A)( LPSTR, PLLS_HANDLE );
+
+NTSTATUS
+NTAPI
+LlsConnectEnterpriseW(
+ IN LPWSTR Focus,
+ OUT PLLS_HANDLE Handle,
+ IN DWORD Level,
+ OUT LPBYTE *bufptr
+ );
+
+NTSTATUS
+NTAPI
+LlsConnectEnterpriseA(
+ IN LPSTR Focus,
+ OUT PLLS_HANDLE Handle,
+ IN DWORD Level,
+ OUT LPBYTE *bufptr
+ );
+#ifdef UNICODE
+#define LlsConnectEnterprise LlsConnectEnterpriseW
+#else
+#define LlsConnectEnterprise LlsConnectEnterpriseA
+#endif
+
+typedef NTSTATUS (NTAPI *PLLS_CONNECT_ENTERPRISE_W)( LPWSTR, PLLS_HANDLE, DWORD, LPBYTE * );
+typedef NTSTATUS (NTAPI *PLLS_CONNECT_ENTERPRISE_A)( LPSTR, PLLS_HANDLE, DWORD, LPBYTE * );
+
+NTSTATUS
+NTAPI
+LlsClose(
+ IN LLS_HANDLE Handle
+ );
+
+typedef NTSTATUS (NTAPI *PLLS_CLOSE)( LLS_HANDLE );
+
+NTSTATUS
+NTAPI
+LlsFreeMemory(
+ IN PVOID bufptr
+ );
+
+typedef NTSTATUS (NTAPI *PLLS_FREE_MEMORY)( PVOID );
+
+NTSTATUS
+NTAPI
+LlsEnterpriseServerFindW(
+ IN LPWSTR Focus,
+ IN DWORD Level,
+ OUT LPBYTE *bufptr
+ );
+
+NTSTATUS
+NTAPI
+LlsEnterpriseServerFindA(
+ IN LPSTR Focus,
+ IN DWORD Level,
+ OUT LPBYTE *bufptr
+ );
+#ifdef UNICODE
+#define LlsEnterpriseServerFind LlsEnterpriseServerFindW
+#else
+#define LlsEnterpriseServerFind LlsEnterpriseServerFindA
+#endif
+
+//
+// License control API's
+//
+
+// Enum purchase history of licenses for all products.
+NTSTATUS
+NTAPI
+LlsLicenseEnumW(
+ IN LLS_HANDLE Handle,
+ IN DWORD Level, // Level 0 supported
+ OUT LPBYTE* bufptr,
+ IN DWORD prefmaxlen,
+ OUT LPDWORD EntriesRead,
+ OUT LPDWORD TotalEntries,
+ IN OUT LPDWORD ResumeHandle
+ );
+
+NTSTATUS
+NTAPI
+LlsLicenseEnumA(
+ IN LLS_HANDLE Handle,
+ IN DWORD Level, // Level 0 supported
+ OUT LPBYTE* bufptr,
+ IN DWORD prefmaxlen,
+ OUT LPDWORD EntriesRead,
+ OUT LPDWORD TotalEntries,
+ IN OUT LPDWORD ResumeHandle
+ );
+#ifdef UNICODE
+#define LlsLicenseEnum LlsLicenseEnumW
+#else
+#define LlsLicenseEnum LlsLicenseEnumA
+#endif
+
+// Add purchase of license for a product.
+NTSTATUS
+NTAPI
+LlsLicenseAddW(
+ IN LLS_HANDLE Handle,
+ IN DWORD Level, // Level 0 supported
+ IN LPBYTE bufptr
+ );
+
+NTSTATUS
+NTAPI
+LlsLicenseAddA(
+ IN LLS_HANDLE Handle,
+ IN DWORD Level, // Level 0 supported
+ IN LPBYTE bufptr
+ );
+#ifdef UNICODE
+#define LlsLicenseAdd LlsLicenseAddW
+#else
+#define LlsLicenseAdd LlsLicenseAddA
+#endif
+
+//
+// Product control API's
+//
+// Product is SQL, BackOffice, Exchange, Etc. (Even though BackOffice isn't
+// a product - we count it like one to keep things simplistic.
+//
+
+// Enum all products with purchase and InUse info.
+NTSTATUS
+NTAPI
+LlsProductEnumW(
+ IN LLS_HANDLE Handle,
+ IN DWORD Level, // Levels 0,1 supported
+ OUT LPBYTE* bufptr,
+ IN DWORD prefmaxlen,
+ OUT LPDWORD EntriesRead,
+ OUT LPDWORD TotalEntries,
+ IN OUT LPDWORD ResumeHandle
+ );
+
+NTSTATUS
+NTAPI
+LlsProductEnumA(
+ IN LLS_HANDLE Handle,
+ IN DWORD Level, // Levels 0,1 supported
+ OUT LPBYTE* bufptr,
+ IN DWORD prefmaxlen,
+ OUT LPDWORD EntriesRead,
+ OUT LPDWORD TotalEntries,
+ IN OUT LPDWORD ResumeHandle
+ );
+#ifdef UNICODE
+#define LlsProductEnum LlsProductEnumW
+#else
+#define LlsProductEnum LlsProductEnumA
+#endif
+
+// Add purchase of license for a product.
+NTSTATUS
+NTAPI
+LlsProductAddW(
+ IN LLS_HANDLE Handle,
+ IN LPWSTR ProductFamily,
+ IN LPWSTR Product,
+ IN LPWSTR Version
+ );
+
+NTSTATUS
+NTAPI
+LlsProductAddA(
+ IN LLS_HANDLE Handle,
+ IN LPSTR ProductFamily,
+ IN LPSTR Product,
+ IN LPSTR Version
+ );
+#ifdef UNICODE
+#define LlsProductAdd LlsProductAddW
+#else
+#define LlsProductAdd LlsProductAddA
+#endif
+
+// For a particular product enum all users.
+NTSTATUS
+NTAPI
+LlsProductUserEnumW(
+ IN LLS_HANDLE Handle,
+ IN LPWSTR Product,
+ IN DWORD Level, // Levels 0,1 supported
+ OUT LPBYTE* bufptr,
+ IN DWORD prefmaxlen,
+ OUT LPDWORD EntriesRead,
+ OUT LPDWORD TotalEntries,
+ IN OUT LPDWORD ResumeHandle
+ );
+
+NTSTATUS
+NTAPI
+LlsProductUserEnumA(
+ IN LLS_HANDLE Handle,
+ IN LPSTR Product,
+ IN DWORD Level, // Levels 0,1 supported
+ OUT LPBYTE* bufptr,
+ IN DWORD prefmaxlen,
+ OUT LPDWORD EntriesRead,
+ OUT LPDWORD TotalEntries,
+ IN OUT LPDWORD ResumeHandle
+ );
+#ifdef UNICODE
+#define LlsProductUserEnum LlsProductUserEnumW
+#else
+#define LlsProductUserEnum LlsProductUserEnumA
+#endif
+
+// For a particular product enum all license purchases.
+NTSTATUS
+NTAPI
+LlsProductLicenseEnumW(
+ IN LLS_HANDLE Handle,
+ IN LPWSTR Product,
+ IN DWORD Level, // Level 0 supported
+ OUT LPBYTE* bufptr,
+ IN DWORD prefmaxlen,
+ OUT LPDWORD EntriesRead,
+ OUT LPDWORD TotalEntries,
+ IN OUT LPDWORD ResumeHandle
+ );
+
+NTSTATUS
+NTAPI
+LlsProductLicenseEnumA(
+ IN LLS_HANDLE Handle,
+ IN LPSTR Product,
+ IN DWORD Level, // Level 0 supported
+ OUT LPBYTE* bufptr,
+ IN DWORD prefmaxlen,
+ OUT LPDWORD EntriesRead,
+ OUT LPDWORD TotalEntries,
+ IN OUT LPDWORD ResumeHandle
+ );
+#ifdef UNICODE
+#define LlsProductLicenseEnum LlsProductLicenseEnumW
+#else
+#define LlsProductLicenseEnum LlsProductLicenseEnumA
+#endif
+
+
+// For given product enum all servers with concurrent limits
+NTSTATUS
+NTAPI
+LlsProductServerEnumW(
+ IN LLS_HANDLE Handle,
+ IN LPWSTR Product,
+ IN DWORD Level, // Levels 0,1 supported
+ OUT LPBYTE* bufptr,
+ IN DWORD prefmaxlen,
+ OUT LPDWORD EntriesRead,
+ OUT LPDWORD TotalEntries,
+ IN OUT LPDWORD ResumeHandle
+ );
+
+NTSTATUS
+NTAPI
+LlsProductServerEnumA(
+ IN LLS_HANDLE Handle,
+ IN LPSTR Product,
+ IN DWORD Level, // Levels 0,1 supported
+ OUT LPBYTE* bufptr,
+ IN DWORD prefmaxlen,
+ OUT LPDWORD EntriesRead,
+ OUT LPDWORD TotalEntries,
+ IN OUT LPDWORD ResumeHandle
+ );
+#ifdef UNICODE
+#define LlsProductServerEnum LlsProductServerEnumW
+#else
+#define LlsProductServerEnum LlsProductServerEnumA
+#endif
+//
+// User control API's
+// A user can be a mapped user or a normal user
+//
+
+// Enums all users
+NTSTATUS
+NTAPI
+LlsUserEnumW(
+ IN LLS_HANDLE Handle,
+ IN DWORD Level, // Levels 0,1 supported
+ OUT LPBYTE* bufptr,
+ IN DWORD prefmaxlen,
+ OUT LPDWORD EntriesRead,
+ OUT LPDWORD TotalEntries,
+ IN OUT LPDWORD ResumeHandle
+ );
+
+NTSTATUS
+NTAPI
+LlsUserEnumA(
+ IN LLS_HANDLE Handle,
+ IN DWORD Level, // Levels 0,1 supported
+ OUT LPBYTE* bufptr,
+ IN DWORD prefmaxlen,
+ OUT LPDWORD EntriesRead,
+ OUT LPDWORD TotalEntries,
+ IN OUT LPDWORD ResumeHandle
+ );
+#ifdef UNICODE
+#define LlsUserEnum LlsUserEnumW
+#else
+#define LlsUserEnum LlsUserEnumA
+#endif
+
+// Info is Group and whether to force back-office license
+NTSTATUS
+NTAPI
+LlsUserInfoGetW(
+ IN LLS_HANDLE Handle,
+ IN LPWSTR User,
+ IN DWORD Level, // Level 1 supported
+ OUT LPBYTE* bufptr
+ );
+
+NTSTATUS
+NTAPI
+LlsUserInfoGetA(
+ IN LLS_HANDLE Handle,
+ IN LPSTR User,
+ IN DWORD Level, // Level 1 supported
+ OUT LPBYTE* bufptr
+ );
+#ifdef UNICODE
+#define LlsUserInfoGet LlsUserInfoGetW
+#else
+#define LlsUserInfoGet LlsUserInfoGetA
+#endif
+
+NTSTATUS
+NTAPI
+LlsUserInfoSetW(
+ IN LLS_HANDLE Handle,
+ IN LPWSTR User,
+ IN DWORD Level,
+ IN LPBYTE bufptr // Level 1 supported
+ );
+
+NTSTATUS
+NTAPI
+LlsUserInfoSetA(
+ IN LLS_HANDLE Handle,
+ IN LPSTR User,
+ IN DWORD Level,
+ IN LPBYTE bufptr // Level 1 supported
+ );
+#ifdef UNICODE
+#define LlsUserInfoSet LlsUserInfoSetW
+#else
+#define LlsUserInfoSet LlsUserInfoSetA
+#endif
+
+NTSTATUS
+NTAPI
+LlsUserDeleteW(
+ IN LLS_HANDLE Handle,
+ IN LPWSTR User
+ );
+
+NTSTATUS
+NTAPI
+LlsUserDeleteA(
+ IN LLS_HANDLE Handle,
+ IN LPSTR User
+ );
+#ifdef UNICODE
+#define LlsUserDelete LlsUserDeleteW
+#else
+#define LlsUserDelete LlsUserDeleteA
+#endif
+
+// For a given user enums all license useages
+NTSTATUS
+NTAPI
+LlsUserProductEnumW(
+ IN LLS_HANDLE Handle,
+ IN LPWSTR User,
+ IN DWORD Level, // Levels 0,1 supported
+ OUT LPBYTE* bufptr,
+ IN DWORD prefmaxlen,
+ OUT LPDWORD EntriesRead,
+ OUT LPDWORD TotalEntries,
+ IN OUT LPDWORD ResumeHandle
+ );
+
+NTSTATUS
+NTAPI
+LlsUserProductEnumA(
+ IN LLS_HANDLE Handle,
+ IN LPSTR User,
+ IN DWORD Level, // Levels 0,1 supported
+ OUT LPBYTE* bufptr,
+ IN DWORD prefmaxlen,
+ OUT LPDWORD EntriesRead,
+ OUT LPDWORD TotalEntries,
+ IN OUT LPDWORD ResumeHandle
+ );
+#ifdef UNICODE
+#define LlsUserProductEnum LlsUserProductEnumW
+#else
+#define LlsUserProductEnum LlsUserProductEnumA
+#endif
+
+// For a given user deletes a license useage
+NTSTATUS
+NTAPI
+LlsUserProductDeleteW(
+ IN LLS_HANDLE Handle,
+ IN LPWSTR User,
+ IN LPWSTR Product
+ );
+
+NTSTATUS
+NTAPI
+LlsUserProductDeleteA(
+ IN LLS_HANDLE Handle,
+ IN LPSTR User,
+ IN LPSTR Product
+ );
+#ifdef UNICODE
+#define LlsUserProductDelete LlsUserProductDeleteW
+#else
+#define LlsUserProductDelete LlsUserProductDeleteA
+#endif
+
+//
+// Group control API's
+//
+
+// Enums all user Groups
+NTSTATUS
+NTAPI
+LlsGroupEnumW(
+ IN LLS_HANDLE Handle,
+ IN DWORD Level, // Levels 0,1 supported
+ OUT LPBYTE* bufptr,
+ IN DWORD prefmaxlen,
+ OUT LPDWORD EntriesRead,
+ OUT LPDWORD TotalEntries,
+ IN OUT LPDWORD ResumeHandle
+ );
+
+NTSTATUS
+NTAPI
+LlsGroupEnumA(
+ IN LLS_HANDLE Handle,
+ IN DWORD Level, // Levels 0,1 supported
+ OUT LPBYTE* bufptr,
+ IN DWORD prefmaxlen,
+ OUT LPDWORD EntriesRead,
+ OUT LPDWORD TotalEntries,
+ IN OUT LPDWORD ResumeHandle
+ );
+#ifdef UNICODE
+#define LlsGroupEnum LlsGroupEnumW
+#else
+#define LlsGroupEnum LlsGroupEnumA
+#endif
+
+// For given Group gets info, info is name, comment and # licenses used
+NTSTATUS
+NTAPI
+LlsGroupInfoGetW(
+ IN LLS_HANDLE Handle,
+ IN LPWSTR Group,
+ IN DWORD Level, // Level 1 supported
+ OUT LPBYTE* bufptr
+ );
+
+NTSTATUS
+NTAPI
+LlsGroupInfoGetA(
+ IN LLS_HANDLE Handle,
+ IN LPSTR Group,
+ IN DWORD Level, // Level 1 supported
+ OUT LPBYTE* bufptr
+ );
+#ifdef UNICODE
+#define LlsGroupInfoGet LlsGroupInfoGetW
+#else
+#define LlsGroupInfoGet LlsGroupInfoGetA
+#endif
+
+NTSTATUS
+NTAPI
+LlsGroupInfoSetW(
+ IN LLS_HANDLE Handle,
+ IN LPWSTR Group,
+ IN DWORD Level, // Level 1 supported
+ IN LPBYTE bufptr
+ );
+
+NTSTATUS
+NTAPI
+LlsGroupInfoSetA(
+ IN LLS_HANDLE Handle,
+ IN LPSTR Group,
+ IN DWORD Level, // Level 1 supported
+ IN LPBYTE bufptr
+ );
+#ifdef UNICODE
+#define LlsGroupInfoSet LlsGroupInfoSetW
+#else
+#define LlsGroupInfoSet LlsGroupInfoSetA
+#endif
+
+// For given Group enum all users
+NTSTATUS
+NTAPI
+LlsGroupUserEnumW(
+ IN LLS_HANDLE Handle,
+ IN LPWSTR Group,
+ IN DWORD Level, // Levels 0,1 supported
+ OUT LPBYTE* bufptr,
+ IN DWORD prefmaxlen,
+ OUT LPDWORD EntriesRead,
+ OUT LPDWORD TotalEntries,
+ IN OUT LPDWORD ResumeHandle
+ );
+
+NTSTATUS
+NTAPI
+LlsGroupUserEnumA(
+ IN LLS_HANDLE Handle,
+ IN LPSTR Group,
+ IN DWORD Level, // Levels 0,1 supported
+ OUT LPBYTE* bufptr,
+ IN DWORD prefmaxlen,
+ OUT LPDWORD EntriesRead,
+ OUT LPDWORD TotalEntries,
+ IN OUT LPDWORD ResumeHandle
+ );
+#ifdef UNICODE
+#define LlsGroupUserEnum LlsGroupUserEnumW
+#else
+#define LlsGroupUserEnum LlsGroupUserEnumA
+#endif
+
+// Add user to given Group
+NTSTATUS
+NTAPI
+LlsGroupUserAddW(
+ IN LLS_HANDLE Handle,
+ IN LPWSTR Group,
+ IN LPWSTR User
+ );
+
+NTSTATUS
+NTAPI
+LlsGroupUserAddA(
+ IN LLS_HANDLE Handle,
+ IN LPSTR Group,
+ IN LPSTR User
+ );
+#ifdef UNICODE
+#define LlsGroupUserAdd LlsGroupUserAddW
+#else
+#define LlsGroupUserAdd LlsGroupUserAddA
+#endif
+
+// Delete user from given Group
+NTSTATUS
+NTAPI
+LlsGroupUserDeleteW(
+ IN LLS_HANDLE Handle,
+ IN LPWSTR Group,
+ IN LPWSTR User
+ );
+
+NTSTATUS
+NTAPI
+LlsGroupUserDeleteA(
+ IN LLS_HANDLE Handle,
+ IN LPSTR Group,
+ IN LPSTR User
+ );
+#ifdef UNICODE
+#define LlsGroupUserDelete LlsGroupUserDeleteW
+#else
+#define LlsGroupUserDelete LlsGroupUserDeleteA
+#endif
+
+// Add a given Group
+NTSTATUS
+NTAPI
+LlsGroupAddW(
+ IN LLS_HANDLE Handle,
+ IN DWORD Level, // Level 1 supported
+ IN LPBYTE bufptr
+ );
+
+NTSTATUS
+NTAPI
+LlsGroupAddA(
+ IN LLS_HANDLE Handle,
+ IN DWORD Level, // Level 1 supported
+ IN LPBYTE bufptr
+ );
+#ifdef UNICODE
+#define LlsGroupAdd LlsGroupAddW
+#else
+#define LlsGroupAdd LlsGroupAddA
+#endif
+
+NTSTATUS
+NTAPI
+LlsGroupDeleteW(
+ IN LLS_HANDLE Handle,
+ IN LPWSTR Group
+ );
+
+NTSTATUS
+NTAPI
+LlsGroupDeleteA(
+ IN LLS_HANDLE Handle,
+ IN LPSTR Group
+ );
+#ifdef UNICODE
+#define LlsGroupDelete LlsGroupDeleteW
+#else
+#define LlsGroupDelete LlsGroupDeleteA
+#endif
+
+
+//
+// Service control API's
+//
+
+NTSTATUS
+NTAPI
+LlsServiceInfoGetW(
+ IN LLS_HANDLE Handle,
+ IN DWORD Level,
+ OUT LPBYTE* bufptr
+ );
+
+NTSTATUS
+NTAPI
+LlsServiceInfoGetA(
+ IN LLS_HANDLE Handle,
+ IN DWORD Level,
+ OUT LPBYTE* bufptr
+ );
+#ifdef UNICODE
+#define LlsServiceInfoGet LlsServiceInfoGetW
+#else
+#define LlsServiceInfoGet LlsServiceInfoGetA
+#endif
+
+NTSTATUS
+NTAPI
+LlsServiceInfoSetW(
+ IN LLS_HANDLE Handle,
+ IN DWORD Level,
+ IN LPBYTE bufptr
+ );
+
+NTSTATUS
+NTAPI
+LlsServiceInfoSetA(
+ IN LLS_HANDLE Handle,
+ IN DWORD Level,
+ IN LPBYTE bufptr
+ );
+#ifdef UNICODE
+#define LlsServiceInfoSet LlsServiceInfoSetW
+#else
+#define LlsServiceInfoSet LlsServiceInfoSetA
+#endif
+
+
+//
+// Server Table Stuff (Replicated Server / Product Tree)
+//
+NTSTATUS
+NTAPI
+LlsServerEnumW(
+ IN LLS_HANDLE Handle,
+ IN LPWSTR Server,
+ IN DWORD Level, // Levels 0,1 supported
+ OUT LPBYTE* bufptr,
+ IN DWORD prefmaxlen,
+ OUT LPDWORD EntriesRead,
+ OUT LPDWORD TotalEntries,
+ IN OUT LPDWORD ResumeHandle
+ );
+
+NTSTATUS
+NTAPI
+LlsServerEnumA(
+ IN LLS_HANDLE Handle,
+ IN LPSTR Server,
+ IN DWORD Level, // Levels 0,1 supported
+ OUT LPBYTE* bufptr,
+ IN DWORD prefmaxlen,
+ OUT LPDWORD EntriesRead,
+ OUT LPDWORD TotalEntries,
+ IN OUT LPDWORD ResumeHandle
+ );
+#ifdef UNICODE
+#define LlsServerEnum LlsServerEnumW
+#else
+#define LlsServerEnum LlsServerEnumA
+#endif
+
+
+NTSTATUS
+NTAPI
+LlsServerProductEnumW(
+ IN LLS_HANDLE Handle,
+ IN LPWSTR Server,
+ IN DWORD Level, // Levels 0,1 supported
+ OUT LPBYTE* bufptr,
+ IN DWORD prefmaxlen,
+ OUT LPDWORD EntriesRead,
+ OUT LPDWORD TotalEntries,
+ IN OUT LPDWORD ResumeHandle
+ );
+
+NTSTATUS
+NTAPI
+LlsServerProductEnumA(
+ IN LLS_HANDLE Handle,
+ IN LPSTR Server,
+ IN DWORD Level, // Levels 0,1 supported
+ OUT LPBYTE* bufptr,
+ IN DWORD prefmaxlen,
+ OUT LPDWORD EntriesRead,
+ OUT LPDWORD TotalEntries,
+ IN OUT LPDWORD ResumeHandle
+ );
+#ifdef UNICODE
+#define LlsServerUserEnum LlsServerUserEnumW
+#else
+#define LlsServerUserEnum LlsServerUserEnumA
+#endif
+
+
+//
+// Concurrent (Per-Server) mode API's (these will interact with the registry
+// on the remote system).
+//
+NTSTATUS
+NTAPI
+LlsLocalProductEnumW(
+ IN LLS_HANDLE Handle,
+ IN DWORD Level, // Levels 0,1 supported
+ OUT LPBYTE* bufptr,
+ IN DWORD prefmaxlen,
+ OUT LPDWORD EntriesRead,
+ OUT LPDWORD TotalEntries,
+ IN OUT LPDWORD ResumeHandle
+ );
+
+NTSTATUS
+NTAPI
+LlsLocalProductEnumA(
+ IN LLS_HANDLE Handle,
+ IN DWORD Level, // Levels 0,1 supported
+ OUT LPBYTE* bufptr,
+ IN DWORD prefmaxlen,
+ OUT LPDWORD EntriesRead,
+ OUT LPDWORD TotalEntries,
+ IN OUT LPDWORD ResumeHandle
+ );
+#ifdef UNICODE
+#define LlsLocalProductEnum LlsLocalProductEnumW
+#else
+#define LlsLocalProductEnum LlsLocalProductEnumA
+#endif
+
+
+NTSTATUS
+NTAPI
+LlsLocalProductInfoGetW(
+ IN LLS_HANDLE Handle,
+ IN LPWSTR Product,
+ IN DWORD Level,
+ OUT LPBYTE* bufptr
+ );
+
+NTSTATUS
+NTAPI
+LlsLocalProductInfoGetA(
+ IN LLS_HANDLE Handle,
+ IN LPSTR Product,
+ IN DWORD Level,
+ OUT LPBYTE* bufptr
+ );
+#ifdef UNICODE
+#define LlsLocalProductInfoGet LlsLocalProductInfoGetW
+#else
+#define LlsLocalProductInfoGet LlsLocalProductInfoGetA
+#endif
+
+
+NTSTATUS
+NTAPI
+LlsLocalProductInfoSetW(
+ IN LLS_HANDLE Handle,
+ IN LPWSTR Product,
+ IN DWORD Level,
+ IN LPBYTE bufptr
+ );
+
+NTSTATUS
+NTAPI
+LlsLocalProductInfoSetA(
+ IN LLS_HANDLE Handle,
+ IN LPSTR Product,
+ IN DWORD Level,
+ IN LPBYTE bufptr
+ );
+#ifdef UNICODE
+#define LlsLocalProductInfoSet LlsLocalProductInfoSetW
+#else
+#define LlsLocalProductInfoSet LlsLocalProductInfoSetA
+#endif
+
+
+//////////////////////////////////////////////////////////////////////////////
+// LLS EXTENDED API //
+////////////////////////
+
+BOOL
+NTAPI
+LlsCapabilityIsSupported(
+ LLS_HANDLE Handle,
+ DWORD Capability );
+
+typedef BOOL (NTAPI *PLLS_CAPABILITY_IS_SUPPORTED)( LLS_HANDLE, DWORD );
+
+NTSTATUS
+NTAPI
+LlsProductSecurityGetW(
+ IN LLS_HANDLE Handle,
+ IN LPWSTR Product,
+ OUT LPBOOL pSecurity
+ );
+
+NTSTATUS
+NTAPI
+LlsProductSecurityGetA(
+ IN LLS_HANDLE Handle,
+ IN LPSTR Product,
+ OUT LPBOOL pSecurity
+ );
+
+typedef NTSTATUS (NTAPI *PLLS_PRODUCT_SECURITY_GET_W)( LLS_HANDLE, LPWSTR, LPBOOL );
+typedef NTSTATUS (NTAPI *PLLS_PRODUCT_SECURITY_GET_A)( LLS_HANDLE, LPSTR, LPBOOL );
+
+#ifdef UNICODE
+# define LlsProductSecurityGet LlsProductSecurityGetW
+#else
+# define LlsProductSecurityGet LlsProductSecurityGetA
+#endif
+
+NTSTATUS
+NTAPI
+LlsProductSecuritySetW(
+ IN LLS_HANDLE Handle,
+ IN LPWSTR Product
+ );
+
+NTSTATUS
+NTAPI
+LlsProductSecuritySetA(
+ IN LLS_HANDLE Handle,
+ IN LPSTR Product
+ );
+
+typedef NTSTATUS (NTAPI *PLLS_PRODUCT_SECURITY_SET_W)( LLS_HANDLE, LPWSTR );
+typedef NTSTATUS (NTAPI *PLLS_PRODUCT_SECURITY_SET_A)( LLS_HANDLE, LPSTR );
+
+#ifdef UNICODE
+# define LlsProductSecuritySet LlsProductSecuritySetW
+#else
+# define LlsProductSecuritySet LlsProductSecuritySetA
+#endif
+
+NTSTATUS
+NTAPI
+LlsProductLicensesGetW(
+ IN LLS_HANDLE Handle,
+ IN LPWSTR DisplayName,
+ IN DWORD Mode,
+ OUT LPDWORD pQuantity );
+
+NTSTATUS
+NTAPI
+LlsProductLicensesGetA(
+ IN LLS_HANDLE Handle,
+ IN LPSTR DisplayName,
+ IN DWORD Mode,
+ OUT LPDWORD pQuantity );
+
+typedef NTSTATUS (NTAPI *PLLS_PRODUCT_LICENSES_GET_W)( LLS_HANDLE, LPWSTR, DWORD, LPDWORD );
+typedef NTSTATUS (NTAPI *PLLS_PRODUCT_LICENSES_GET_A)( LLS_HANDLE, LPSTR, DWORD, LPDWORD );
+
+#ifdef UNICODE
+# define LlsProductLicensesGet LlsProductLicensesGetW
+#else
+# define LlsProductLicensesGet LlsProductLicensesGetA
+#endif
+
+NTSTATUS
+NTAPI
+LlsCertificateClaimEnumW(
+ IN LLS_HANDLE Handle,
+ IN DWORD LicenseLevel,
+ IN LPBYTE pLicenseInfo,
+ IN DWORD TargetLevel,
+ OUT LPBYTE * ppTargets,
+ OUT LPDWORD pNumTargets );
+
+NTSTATUS
+NTAPI
+LlsCertificateClaimEnumA(
+ IN LLS_HANDLE Handle,
+ IN DWORD LicenseLevel,
+ IN LPBYTE pLicenseInfo,
+ IN DWORD TargetLevel,
+ OUT LPBYTE * ppTargets,
+ OUT LPDWORD pNumTargets );
+
+typedef NTSTATUS (NTAPI *PLLS_CERTIFICATE_CLAIM_ENUM_W)( LLS_HANDLE, DWORD, LPBYTE, DWORD, LPBYTE *, LPDWORD );
+typedef NTSTATUS (NTAPI *PLLS_CERTIFICATE_CLAIM_ENUM_A)( LLS_HANDLE, DWORD, LPBYTE, DWORD, LPBYTE *, LPDWORD );
+
+#ifdef UNICODE
+# define LlsCertificateClaimEnum LlsCertificateClaimEnumW
+#else
+# define LlsCertificateClaimEnum LlsCertificateClaimEnumA
+#endif
+
+NTSTATUS
+NTAPI
+LlsCertificateClaimAddCheckW(
+ IN LLS_HANDLE Handle,
+ IN DWORD LicenseLevel,
+ IN LPBYTE pLicenseInfo,
+ OUT LPBOOL pMayInstall );
+
+NTSTATUS
+NTAPI
+LlsCertificateClaimAddCheckA(
+ IN LLS_HANDLE Handle,
+ IN DWORD LicenseLevel,
+ IN LPBYTE pLicenseInfo,
+ OUT LPBOOL pMayInstall );
+
+typedef NTSTATUS (NTAPI *PLLS_CERTIFICATE_CLAIM_ADD_CHECK_W)( LLS_HANDLE, DWORD, LPBYTE, LPBOOL );
+typedef NTSTATUS (NTAPI *PLLS_CERTIFICATE_CLAIM_ADD_CHECK_A)( LLS_HANDLE, DWORD, LPBYTE, LPBOOL );
+
+#ifdef UNICODE
+# define LlsCertificateClaimAddCheck LlsCertificateClaimAddCheckW
+#else
+# define LlsCertificateClaimAddCheck LlsCertificateClaimAddCheckA
+#endif
+
+NTSTATUS
+NTAPI
+LlsCertificateClaimAddW(
+ IN LLS_HANDLE Handle,
+ IN LPWSTR ServerName,
+ IN DWORD LicenseLevel,
+ IN LPBYTE pLicenseInfo );
+
+NTSTATUS
+NTAPI
+LlsCertificateClaimAddA(
+ IN LLS_HANDLE Handle,
+ IN LPSTR ServerName,
+ IN DWORD LicenseLevel,
+ IN LPBYTE pLicenseInfo );
+
+typedef NTSTATUS (NTAPI *PLLS_CERTIFICATE_CLAIM_ADD_W)( LLS_HANDLE, LPWSTR, DWORD, LPBYTE );
+typedef NTSTATUS (NTAPI *PLLS_CERTIFICATE_CLAIM_ADD_A)( LLS_HANDLE, LPSTR, DWORD, LPBYTE );
+
+#ifdef UNICODE
+# define LlsCertificateClaimAdd LlsCertificateClaimAddW
+#else
+# define LlsCertificateClaimAdd LlsCertificateClaimAddA
+#endif
+
+
+NTSTATUS
+NTAPI
+LlsReplicationCertDbAddW(
+ LLS_REPL_HANDLE ReplHandle,
+ DWORD Level,
+ LPVOID Certificates );
+
+typedef NTSTATUS (NTAPI *PLLS_REPLICATION_CERT_DB_ADD_W)( LLS_REPL_HANDLE, DWORD, LPVOID );
+
+
+NTSTATUS
+NTAPI
+LlsReplicationProductSecurityAddW(
+ LLS_REPL_HANDLE ReplHandle,
+ DWORD Level,
+ LPVOID SecureProducts );
+
+typedef NTSTATUS (NTAPI *PLLS_REPLICATION_PRODUCT_SECURITY_ADD_W)( LLS_REPL_HANDLE, DWORD, LPVOID );
+
+
+NTSTATUS
+NTAPI
+LlsReplicationUserAddExW(
+ LLS_REPL_HANDLE ReplHandle,
+ DWORD Level,
+ LPVOID Users );
+
+typedef NTSTATUS (NTAPI *PLLS_REPLICATION_USER_ADD_EX_W)( LLS_REPL_HANDLE, DWORD, LPVOID );
+
+
+NTSTATUS
+NTAPI
+LlsLocalServiceEnumW(
+ LLS_HANDLE Handle,
+ DWORD Level,
+ LPBYTE* bufptr,
+ DWORD PrefMaxLen,
+ LPDWORD EntriesRead,
+ LPDWORD TotalEntries,
+ LPDWORD ResumeHandle );
+
+NTSTATUS
+NTAPI
+LlsLocalServiceEnumA(
+ LLS_HANDLE Handle,
+ DWORD Level,
+ LPBYTE* bufptr,
+ DWORD PrefMaxLen,
+ LPDWORD EntriesRead,
+ LPDWORD TotalEntries,
+ LPDWORD ResumeHandle );
+
+#ifdef UNICODE
+# define LlsLocalServiceEnum LlsLocalServiceEnumW
+#else
+# define LlsLocalServiceEnum LlsLocalServiceEnumA
+#endif
+
+NTSTATUS
+NTAPI
+LlsLocalServiceAddW(
+ LLS_HANDLE Handle,
+ DWORD Level,
+ LPBYTE bufptr );
+
+NTSTATUS
+NTAPI
+LlsLocalServiceAddA(
+ LLS_HANDLE Handle,
+ DWORD Level,
+ LPBYTE bufptr );
+
+#ifdef UNICODE
+# define LlsLocalServiceAdd LlsLocalServiceAddW
+#else
+# define LlsLocalServiceAdd LlsLocalServiceAddA
+#endif
+
+NTSTATUS
+NTAPI
+LlsLocalServiceInfoSetW(
+ LLS_HANDLE Handle,
+ LPWSTR KeyName,
+ DWORD Level,
+ LPBYTE bufptr );
+
+NTSTATUS
+NTAPI
+LlsLocalServiceInfoSetA(
+ LLS_HANDLE Handle,
+ LPSTR KeyName,
+ DWORD Level,
+ LPBYTE bufptr );
+
+#ifdef UNICODE
+# define LlsLocalServiceInfoSet LlsLocalServiceInfoSetW
+#else
+# define LlsLocalServiceInfoSet LlsLocalServiceInfoSetA
+#endif
+
+NTSTATUS
+NTAPI
+LlsLocalServiceInfoGetW(
+ LLS_HANDLE Handle,
+ LPWSTR KeyName,
+ DWORD Level,
+ LPBYTE * pbufptr );
+
+NTSTATUS
+NTAPI
+LlsLocalServiceInfoGetA(
+ LLS_HANDLE Handle,
+ DWORD Level,
+ LPSTR KeyName,
+ LPBYTE * pbufptr );
+
+#ifdef UNICODE
+# define LlsLocalServiceInfoGet LlsLocalServiceInfoGetW
+#else
+# define LlsLocalServiceInfoGet LlsLocalServiceInfoGetA
+#endif
+
+
+//////////////////////////////////////////////////////////////////////////////
+// CCF API //
+///////////////
+
+#define CCF_ENTER_FLAG_PER_SEAT_ONLY ( 1 )
+#define CCF_ENTER_FLAG_PER_SERVER_ONLY ( 2 )
+#define CCF_ENTER_FLAG_SERVER_IS_ES ( 4 )
+
+// prototype for certificate source enter API
+typedef DWORD (APIENTRY *PCCF_ENTER_API)( HWND hWndParent,
+ LPCSTR pszServerName,
+ LPCSTR pszProductName,
+ LPCSTR pszVendor,
+ DWORD dwFlags );
+
+DWORD APIENTRY CCFCertificateEnterUI( HWND hWndParent,
+ LPCSTR pszServerName,
+ LPCSTR pszProductName,
+ LPCSTR pszVendor,
+ DWORD dwFlags,
+ LPCSTR pszSourceToUse );
+
+// prototype for certificate source remove API
+typedef DWORD (APIENTRY *PCCF_REMOVE_API)( HWND hWndParent,
+ LPCSTR pszServerName,
+ DWORD dwFlags,
+ DWORD dwLicenseLevel,
+ LPVOID lpvLicenseInfo );
+
+DWORD APIENTRY CCFCertificateRemoveUI( HWND hWndParent,
+ LPCSTR pszServerName,
+ LPCSTR pszProductName,
+ LPCSTR pszVendor,
+ DWORD dwFlags,
+ LPCSTR pszSourceToUse );
+
+#endif
+
+//
+// Registry values
+//
+
+#define REG_KEY_LICENSE TEXT("SYSTEM\\CurrentControlSet\\Services\\LicenseInfo")
+#define REG_KEY_CONFIG TEXT("SYSTEM\\CurrentControlSet\\Services\\LicenseService\\Parameters")
+
+#define REG_VALUE_NAME TEXT("DisplayName")
+#define REG_VALUE_FAMILY TEXT("FamilyDisplayName")
+#define REG_VALUE_MODE TEXT("Mode")
+#define REG_VALUE_FLIP TEXT("FlipAllow")
+#define REG_VALUE_LIMIT TEXT("ConcurrentLimit")
+#define REG_VALUE_HIGHMARK TEXT("LocalKey")
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/private/net/svcdlls/lls/inc/llsconst.h b/private/net/svcdlls/lls/inc/llsconst.h
new file mode 100644
index 000000000..eb65778f0
--- /dev/null
+++ b/private/net/svcdlls/lls/inc/llsconst.h
@@ -0,0 +1,14 @@
+
+#ifndef NT_LS_USER_NAME
+#define NT_LS_USER_NAME 0
+#endif
+
+#ifndef NT_LS_USER_SID
+#define NT_LS_USER_SID 1
+#endif
+
+#define MAX_PRODUCT_NAME_LENGTH 35
+#define MAX_VERSION_LENGTH 15
+#define MAX_USER_NAME_LENGTH 37
+#define MAX_DATA_LENGTH 80
+#define MAX_DATA_LENGTHW 40
diff --git a/private/net/svcdlls/lls/inc/llsimp.h b/private/net/svcdlls/lls/inc/llsimp.h
new file mode 100644
index 000000000..13dfcb17f
--- /dev/null
+++ b/private/net/svcdlls/lls/inc/llsimp.h
@@ -0,0 +1,24 @@
+/*++
+
+Copyright (c) 1994 Microsoft Corporation
+
+Module Name:
+
+ llsimp.h
+
+Abstract:
+
+ Private Includes
+
+Author:
+
+ Arthur Hanson (arth) Jan 20-1994
+
+Environment:
+
+Revision History:
+
+--*/
+
+#include <nt.h>
+#include <windef.h>
diff --git a/private/net/svcdlls/lls/inc/lpcstub.h b/private/net/svcdlls/lls/inc/lpcstub.h
new file mode 100644
index 000000000..bcdf3e8ca
--- /dev/null
+++ b/private/net/svcdlls/lls/inc/lpcstub.h
@@ -0,0 +1,38 @@
+//
+//
+//
+
+#ifndef _LLSLPCSTUB_H
+#define _LLSLPCSTUB_H
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+NTSTATUS LLSInitLPC();
+NTSTATUS LLSCloseLPC();
+NTSTATUS LLSLicenseRequest ( IN LPWSTR ProductName, IN LPWSTR Version, IN ULONG DataType,
+ IN BOOLEAN IsAdmin, IN PVOID Data, OUT PULONG LicenseHandle );
+NTSTATUS LLSLicenseFree ( IN ULONG LicenseHandle );
+
+
+#ifdef DEBUG
+
+NTSTATUS LLSDbg_TableDump ( ULONG Table );
+NTSTATUS LLSDbg_TableInfoDump ( IN ULONG Table, IN ULONG DataType, IN PVOID Data );
+NTSTATUS LLSDbg_TableFlush ( ULONG Table );
+NTSTATUS LLSDbg_TraceSet ( ULONG Level );
+NTSTATUS LLSDbg_ConfigDump ( );
+NTSTATUS LLSDbg_ReplicationForce ( );
+NTSTATUS LLSDbg_ReplicationDeny ( );
+NTSTATUS LLSDbg_RegistryUpdateForce ( );
+NTSTATUS LLSDbg_LicenseCheckForce ( );
+
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/private/net/svcdlls/lls/inc/rpcutil.h b/private/net/svcdlls/lls/inc/rpcutil.h
new file mode 100644
index 000000000..345cfe1a3
--- /dev/null
+++ b/private/net/svcdlls/lls/inc/rpcutil.h
@@ -0,0 +1,76 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ rpcutil.h
+
+Abstract:
+
+ This file contains prototypes for the bind and unbind functions that
+ all lls functions will call. It also includes the allocate
+ and free routines used by the MIDL generated RPC stubs.
+
+Author:
+
+ Arthur Hanson (arth) Jan 30, 1994
+
+[Environment:]
+
+ User Mode - Win32
+
+Revision History:
+
+--*/
+
+#ifndef _RPCUTIL_
+#define _RPCUTIL_
+
+#ifndef RPC_NO_WINDOWS_H // Don't let rpc.h include windows.h
+#define RPC_NO_WINDOWS_H
+#endif // RPC_NO_WINDOWS_H
+
+#include <rpc.h>
+
+//
+// The following typedefs are created for use in the Enum entry point
+// routines. These structures are meant to mirror the level specific
+// info containers that are specified in the .idl file for the Enum API
+// function. Using these structures to set up for the API call allows
+// the entry point routine to avoid using any bulky level-specific logic
+// to set-up or return from the RPC stub call.
+//
+
+typedef struct _GENERIC_INFO_CONTAINER {
+ DWORD EntriesRead;
+ LPBYTE Buffer;
+} GENERIC_INFO_CONTAINER, *PGENERIC_INFO_CONTAINER, *LPGENERIC_INFO_CONTAINER ;
+
+typedef struct _GENERIC_ENUM_STRUCT {
+ DWORD Level;
+ PGENERIC_INFO_CONTAINER Container;
+} GENERIC_ENUM_STRUCT, *PGENERIC_ENUM_STRUCT, *LPGENERIC_ENUM_STRUCT ;
+
+
+
+//
+// DEFINES
+//
+
+//
+// Function Prototypes
+//
+
+void *
+MIDL_user_allocate(
+ IN ULONG NumBytes
+ );
+
+void
+MIDL_user_free(
+ IN PVOID MemPointer
+ );
+
+
+#endif // _RPCUTIL_
diff --git a/private/net/svcdlls/lls/llscli.acf b/private/net/svcdlls/lls/llscli.acf
new file mode 100644
index 000000000..708b5f4ac
--- /dev/null
+++ b/private/net/svcdlls/lls/llscli.acf
@@ -0,0 +1,46 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ llscli.acf
+
+Abstract:
+
+ License Logging Service CLIENT rpc stub attribute configuration file.
+
+ This file contains the attribute configuration information necessary
+ for generating the client stubs for remotable LLS functions.
+
+ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ !! !!
+ !! This .acf file is USED ONLY WHEN GENERATING LLS CLIENT STUBS. !!
+ !! !!
+ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+
+ Use llssrv.acf when generating server stubs.
+
+
+Author:
+
+ Arthur Hanson (arth) Jan 30, 1995
+
+Environment:
+
+ User Mode
+
+Revision History:
+
+--*/
+
+[
+ implicit_handle(handle_t llsrpc_handle)
+]
+
+interface llsrpc
+
+{
+
+}
diff --git a/private/net/svcdlls/lls/llsdbg.idl b/private/net/svcdlls/lls/llsdbg.idl
new file mode 100644
index 000000000..3d71d07d3
--- /dev/null
+++ b/private/net/svcdlls/lls/llsdbg.idl
@@ -0,0 +1,113 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ llsdbg.idl
+
+Abstract:
+
+
+Author:
+
+ Arthur Hanson (arth) Jan 20-1994
+
+Environment:
+
+ User Mode
+
+Revision History:
+
+--*/
+
+[
+ uuid(F40E17F0-520F-11CE-A897-08002B2E9C6D),
+ version(0.0),
+#ifdef __midl
+ ms_union,
+#endif // __midl
+ pointer_default(unique)
+]
+
+interface llsdbgrpc
+
+{
+
+//
+// Import a dummy interface containing #includes for public .h files. This
+// trick is necessary so that midl will only generate marshalling routines
+// for subtypes that are relevant to the parameters specified on the RPC
+// interface. midl also ingores function prototypes contained therein.
+//
+
+import "llsimp.idl" ;
+
+//
+// Emit these constants into the generated file.
+//
+cpp_quote("#define LLS_LPC_ENDPOINT \"llslpc\"")
+//
+// Note: Must use quad backslash to emit two backslashes into #define
+// which when compiled will boil down to single backslash
+//
+cpp_quote("#define LLS_NP_ENDPOINT \"\\\\pipe\\\\llsrpc\"")
+
+
+typedef [string] LPWSTR PNAMEW;
+typedef [string] LPSTR PNAMEA;
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+//
+// Debugging API's
+//
+NTSTATUS
+LlsrDbgTableDump(
+ [in] DWORD Table
+ );
+
+NTSTATUS
+LlsrDbgTableInfoDump(
+ [in] DWORD Table,
+ [in, string] LPWSTR Item
+ );
+
+NTSTATUS
+LlsrDbgTableFlush(
+ [in] DWORD Table
+ );
+
+NTSTATUS
+LlsrDbgTraceSet(
+ [in] DWORD Flags
+ );
+
+NTSTATUS
+LlsrDbgConfigDump(
+ );
+
+NTSTATUS
+LlsrDbgReplicationForce(
+ );
+
+NTSTATUS
+LlsrDbgReplicationDeny(
+ );
+
+NTSTATUS
+LlsrDbgRegistryUpdateForce(
+ );
+
+NTSTATUS
+LlsrDbgDatabaseFlush(
+ );
+
+
+}
diff --git a/private/net/svcdlls/lls/llsimp.idl b/private/net/svcdlls/lls/llsimp.idl
new file mode 100644
index 000000000..4a4af637d
--- /dev/null
+++ b/private/net/svcdlls/lls/llsimp.idl
@@ -0,0 +1,47 @@
+/*++
+
+Copyright (c) 1991-1995 Microsoft Corporation
+
+Module Name:
+
+ llsimp.idl (taken from lsaimp.idl)
+
+Abstract:
+
+ Temporary dummy IDL interface for ntos2.h.
+
+ This file contains a dummy RPC Interface Definition Language file for
+ ntos2.h. This allows the file ntos2.h to be presented to the RPC compiler
+ as an included file within an imported interface. This temporary measure
+ is necessary so that definitions and function prototypes within ntos2.h
+ and its descendants are presentable to midl in such a way that:
+
+ (a) Types are not generated in the output .h file generated by midl
+ (b) Function prototypes therein are not treated as belonging to the
+ IDL interface being compiled.
+
+Author: Scott Birrell (ScottBi) April 23, 1991
+
+Environment: User Mode
+
+Revision History:
+
+--*/
+[
+ uuid(6D5F5960-41FB-11CE-A894-08002B2E9C6D),
+ version(0.0),
+#ifdef __midl
+ ms_union,
+#endif // __midl
+ endpoint("mscn_np:[\pipe\llsrpc]")
+]
+
+interface lsaimp
+
+{
+
+#define MIDL_PASS "llsimp.idl"
+#include <llsimp.h>
+#include <ntlsapi.h>
+void LlsImpDummy();
+}
diff --git a/private/net/svcdlls/lls/llsrpc.idl b/private/net/svcdlls/lls/llsrpc.idl
new file mode 100644
index 000000000..d54c74ebd
--- /dev/null
+++ b/private/net/svcdlls/lls/llsrpc.idl
@@ -0,0 +1,2041 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ llsrpc.idl
+
+Abstract:
+
+ License Logging Service RPC Interface Definition File
+
+ This file contains the RPC Interface Definition Language file for
+ the LLS.
+
+Author:
+
+ Arthur Hanson (arth) Jan 20-1994
+
+Environment:
+
+ User Mode
+
+Revision History:
+
+ Jeff Parham (jeffparh) 06-Dec-1995
+ o Added LLS_LICENSE_INFO_1 and LLS_PRODUCT_LICENSE_INFO_1 support.
+ o Added new API's for SUR.
+ o Plugged memory leak at the server caused by LlsConnect() and
+ LlsReplConnect() being defined as taking PNAMEW parameters (which
+ differ from LPWSTR's in the sense that they're not automatically
+ freed at the server), even though they neither stored nor freed
+ the passed pointers.
+
+--*/
+
+[
+ uuid(342CFD40-3C6C-11CE-A893-08002B2E9C6D),
+ version(0.0),
+#ifdef __midl
+ ms_union,
+#endif // __midl
+ pointer_default(unique)
+]
+
+interface llsrpc
+
+{
+
+//
+// Import a dummy interface containing #includes for public .h files. This
+// trick is necessary so that midl will only generate marshalling routines
+// for subtypes that are relevant to the parameters specified on the RPC
+// interface. midl also ingores function prototypes contained therein.
+//
+
+import "llsimp.idl" ;
+
+//
+// Emit these constants into the generated file.
+//
+cpp_quote("#define LLS_LPC_ENDPOINT \"llslpc\"")
+//
+// Note: Must use quad backslash to emit two backslashes into #define
+// which when compiled will boil down to single backslash
+//
+cpp_quote("#define LLS_NP_ENDPOINT \"\\\\pipe\\\\llsrpc\"")
+
+//
+// LLS RPC Context Handle
+//
+
+typedef [context_handle] PVOID LLS_HANDLE;
+typedef [context_handle] PVOID LLS_REPL_HANDLE;
+
+typedef [ref] LLS_HANDLE * PLLS_HANDLE;
+typedef [ref] LLS_REPL_HANDLE * PLLS_REPL_HANDLE;
+
+// these are not freed at the server
+typedef [string] LPWSTR PNAMEW;
+typedef [string] LPSTR PNAMEA;
+
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+
+///////////////////////////////////////////////////////////////////////////////
+// License Info
+///////////////////////////////////////////////////////////////////////////////
+//
+// Unicode
+//
+typedef struct _LLS_LICENSE_INFO_0W {
+ PNAMEW Product;
+ LONG Quantity;
+ DWORD Date;
+ PNAMEW Admin;
+ PNAMEW Comment;
+} LLS_LICENSE_INFO_0W, *PLLS_LICENSE_INFO_0W;
+
+typedef struct _LLS_LICENSE_INFO_1W {
+ PNAMEW Product;
+ PNAMEW Vendor;
+ LONG Quantity;
+ DWORD MaxQuantity;
+ DWORD Date;
+ PNAMEW Admin;
+ PNAMEW Comment;
+ DWORD AllowedModes;
+ DWORD CertificateID;
+ PNAMEW Source;
+ DWORD ExpirationDate;
+ DWORD Secrets[ 4 ];
+} LLS_LICENSE_INFO_1W, *PLLS_LICENSE_INFO_1W;
+
+typedef [switch_type(DWORD)] union {
+ [case(0)] LLS_LICENSE_INFO_0W LicenseInfo0;
+ [case(1)] LLS_LICENSE_INFO_1W LicenseInfo1;
+} LLS_LICENSE_INFOW, *PLLS_LICENSE_INFOW;
+
+typedef struct _LLS_LICENSE_INFO_0_CONTAINERW {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] PLLS_LICENSE_INFO_0W Buffer;
+} LLS_LICENSE_INFO_0_CONTAINERW, *PLLS_LICENSE_INFO_0_CONTAINERW;
+
+typedef struct _LLS_LICENSE_INFO_1_CONTAINERW {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] PLLS_LICENSE_INFO_1W Buffer;
+} LLS_LICENSE_INFO_1_CONTAINERW, *PLLS_LICENSE_INFO_1_CONTAINERW;
+
+typedef struct _LLS_LICENSE_ENUM_STRUCTW {
+ DWORD Level;
+ [switch_is(Level)] union _LLS_LICENSE_ENUM_UNIONW {
+ [case(0)]
+ PLLS_LICENSE_INFO_0_CONTAINERW Level0;
+ [case(1)]
+ PLLS_LICENSE_INFO_1_CONTAINERW Level1;
+ [default]
+ ;
+ } LlsLicenseInfo;
+} LLS_LICENSE_ENUM_STRUCTW, *PLLS_LICENSE_ENUM_STRUCTW;
+
+
+//
+// ANSI
+//
+typedef struct _LLS_LICENSE_INFO_0A {
+ PNAMEA Product;
+ LONG Quantity;
+ DWORD Date;
+ PNAMEA Admin;
+ PNAMEA Comment;
+} LLS_LICENSE_INFO_0A, *PLLS_LICENSE_INFO_0A;
+
+typedef struct _LLS_LICENSE_INFO_1A
+{
+ PNAMEA Product;
+ PNAMEA Vendor;
+ LONG Quantity;
+ DWORD MaxQuantity;
+ DWORD Date;
+ PNAMEA Admin;
+ PNAMEA Comment;
+ DWORD AllowedModes;
+ DWORD CertificateID;
+ PNAMEA Source;
+ DWORD ExpirationDate;
+ DWORD Secrets[ 4 ];
+} LLS_LICENSE_INFO_1A, *PLLS_LICENSE_INFO_1A;
+
+typedef [switch_type(DWORD)] union {
+ [case(0)] LLS_LICENSE_INFO_0A LicenseInfo0;
+ [case(1)] LLS_LICENSE_INFO_1A LicenseInfo1;
+} LLS_LICENSE_INFOA, *PLLS_LICENSE_INFOA;
+
+typedef struct _LLS_LICENSE_INFO_0_CONTAINERA {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] PLLS_LICENSE_INFO_0A Buffer;
+} LLS_LICENSE_INFO_0_CONTAINERA, *PLLS_LICENSE_INFO_0_CONTAINERA;
+
+typedef struct _LLS_LICENSE_INFO_1_CONTAINERA {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] PLLS_LICENSE_INFO_1A Buffer;
+} LLS_LICENSE_INFO_1_CONTAINERA, *PLLS_LICENSE_INFO_1_CONTAINERA;
+
+typedef struct _LLS_LICENSE_ENUM_STRUCTA {
+ DWORD Level;
+ [switch_is(Level)] union _LLS_LICENSE_ENUM_UNIONA {
+ [case(0)]
+ PLLS_LICENSE_INFO_0_CONTAINERA Level0;
+ [case(1)]
+ PLLS_LICENSE_INFO_1_CONTAINERA Level1;
+ [default]
+ ;
+ } LlsLicenseInfo;
+} LLS_LICENSE_ENUM_STRUCTA, *PLLS_LICENSE_ENUM_STRUCTA;
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Product Info
+///////////////////////////////////////////////////////////////////////////////
+//
+// Unicode
+//
+typedef struct _LLS_PRODUCT_INFO_0W {
+ PNAMEW Product;
+} LLS_PRODUCT_INFO_0W, *PLLS_PRODUCT_INFO_0W;
+
+typedef struct _LLS_PRODUCT_INFO_1W {
+ PNAMEW Product;
+ ULONG Purchased;
+ ULONG InUse;
+ ULONG TotalConcurrent;
+ ULONG HighMark;
+} LLS_PRODUCT_INFO_1W, *PLLS_PRODUCT_INFO_1W;
+
+typedef [switch_type(DWORD)] union {
+ [case(0)] LLS_PRODUCT_INFO_0W ProductInfo0;
+ [case(1)] LLS_PRODUCT_INFO_1W ProductInfo1;
+} LLS_PRODUCT_INFOW, *PLLS_PRODUCT_INFOW;
+
+typedef struct _LLS_PRODUCT_INFO_0_CONTAINERW {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] PLLS_PRODUCT_INFO_0W Buffer;
+} LLS_PRODUCT_INFO_0_CONTAINERW, *PLLS_PRODUCT_INFO_0_CONTAINERW;
+
+typedef struct _LLS_PRODUCT_INFO_1_CONTAINERW {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] PLLS_PRODUCT_INFO_1W Buffer;
+} LLS_PRODUCT_INFO_1_CONTAINERW, *PLLS_PRODUCT_INFO_1_CONTAINERW;
+
+typedef struct _LLS_PRODUCT_ENUM_STRUCTW {
+ DWORD Level;
+ [switch_is(Level)] union _LLS_PRODUCT_ENUM_UNIONW {
+ [case(0)]
+ PLLS_PRODUCT_INFO_0_CONTAINERW Level0;
+ [case(1)]
+ PLLS_PRODUCT_INFO_1_CONTAINERW Level1;
+ [default]
+ ;
+ } LlsProductInfo;
+} LLS_PRODUCT_ENUM_STRUCTW, *PLLS_PRODUCT_ENUM_STRUCTW;
+
+
+//
+// ANSI
+//
+typedef struct _LLS_PRODUCT_INFO_0A {
+ PNAMEA Product;
+} LLS_PRODUCT_INFO_0A, *PLLS_PRODUCT_INFO_0A;
+
+typedef struct _LLS_PRODUCT_INFO_1A {
+ PNAMEA Product;
+ ULONG Purchased;
+ ULONG InUse;
+ ULONG TotalConcurrent;
+ ULONG HighMark;
+} LLS_PRODUCT_INFO_1A, *PLLS_PRODUCT_INFO_1A;
+
+typedef [switch_type(DWORD)] union {
+ [case(0)] LLS_PRODUCT_INFO_0A ProductInfo0;
+ [case(1)] LLS_PRODUCT_INFO_1A ProductInfo1;
+} LLS_PRODUCT_INFOA, *PLLS_PRODUCT_INFOA;
+
+typedef struct _LLS_PRODUCT_INFO_0_CONTAINERA {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] PLLS_PRODUCT_INFO_0A Buffer;
+} LLS_PRODUCT_INFO_0_CONTAINERA, *PLLS_PRODUCT_INFO_0_CONTAINERA;
+
+typedef struct _LLS_PRODUCT_INFO_1_CONTAINERA {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] PLLS_PRODUCT_INFO_1A Buffer;
+} LLS_PRODUCT_INFO_1_CONTAINERA, *PLLS_PRODUCT_INFO_1_CONTAINERA;
+
+typedef struct _LLS_PRODUCT_ENUM_STRUCTA {
+ DWORD Level;
+ [switch_is(Level)] union _LLS_PRODUCT_ENUM_UNIONA {
+ [case(0)]
+ PLLS_PRODUCT_INFO_0_CONTAINERW Level0;
+ [case(1)]
+ PLLS_PRODUCT_INFO_1_CONTAINERW Level1;
+ [default]
+ ;
+ } LlsProductInfo;
+} LLS_PRODUCT_ENUM_STRUCTA, *PLLS_PRODUCT_ENUM_STRUCTA;
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Product User Info
+///////////////////////////////////////////////////////////////////////////////
+//
+// Unicode
+//
+typedef struct _LLS_PRODUCT_USER_INFO_0W {
+ PNAMEW User;
+} LLS_PRODUCT_USER_INFO_0W, *PLLS_PRODUCT_USER_INFO_0W;
+
+typedef struct _LLS_PRODUCT_USER_INFO_1W {
+ PNAMEW User;
+ DWORD Flags;
+ DWORD LastUsed;
+ ULONG UsageCount;
+} LLS_PRODUCT_USER_INFO_1W, *PLLS_PRODUCT_USER_INFO_1W;
+
+typedef [switch_type(DWORD)] union {
+ [case(0)] LLS_PRODUCT_USER_INFO_0W ProductUserInfo0;
+ [case(1)] LLS_PRODUCT_USER_INFO_1W ProductUserInfo1;
+} LLS_PRODUCT_USER_INFOW, *PLLS_PRODUCT_USER_INFOW;
+
+typedef struct _LLS_PRODUCT_USER_INFO_0_CONTAINERW {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] PLLS_PRODUCT_USER_INFO_0W Buffer;
+} LLS_PRODUCT_USER_INFO_0_CONTAINERW, *PLLS_PRODUCT_USER_INFO_0_CONTAINERW;
+
+typedef struct _LLS_PRODUCT_USER_INFO_1_CONTAINERW {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] PLLS_PRODUCT_USER_INFO_1W Buffer;
+} LLS_PRODUCT_USER_INFO_1_CONTAINERW, *PLLS_PRODUCT_USER_INFO_1_CONTAINERW;
+
+typedef struct _LLS_PRODUCT_USER_ENUM_STRUCTW {
+ DWORD Level;
+ [switch_is(Level)] union _LLS_PRODUCT_USER_ENUM_UNIONW {
+ [case(0)]
+ PLLS_PRODUCT_USER_INFO_0_CONTAINERW Level0;
+ [case(1)]
+ PLLS_PRODUCT_USER_INFO_1_CONTAINERW Level1;
+ [default]
+ ;
+ } LlsProductUserInfo;
+} LLS_PRODUCT_USER_ENUM_STRUCTW, *PLLS_PRODUCT_USER_ENUM_STRUCTW;
+
+
+//
+// ANSI
+//
+typedef struct _LLS_PRODUCT_USER_INFO_0A {
+ PNAMEA User;
+} LLS_PRODUCT_USER_INFO_0A, *PLLS_PRODUCT_USER_INFO_0A;
+
+typedef struct _LLS_PRODUCT_USER_INFO_1A {
+ PNAMEA User;
+ DWORD Flags;
+ DWORD LastUsed;
+ ULONG UsageCount;
+} LLS_PRODUCT_USER_INFO_1A, *PLLS_PRODUCT_USER_INFO_1A;
+
+typedef [switch_type(DWORD)] union {
+ [case(0)] LLS_PRODUCT_USER_INFO_0A ProductUserInfo0;
+ [case(1)] LLS_PRODUCT_USER_INFO_1A ProductUserInfo1;
+} LLS_PRODUCT_USER_INFOA, *PLLS_PRODUCT_USER_INFOA;
+
+typedef struct _LLS_PRODUCT_USER_INFO_0_CONTAINERA {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] PLLS_PRODUCT_USER_INFO_0A Buffer;
+} LLS_PRODUCT_USER_INFO_0_CONTAINERA, *PLLS_PRODUCT_USER_INFO_0_CONTAINERA;
+
+typedef struct _LLS_PRODUCT_USER_INFO_1_CONTAINERA {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] PLLS_PRODUCT_USER_INFO_1A Buffer;
+} LLS_PRODUCT_USER_INFO_1_CONTAINERA, *PLLS_PRODUCT_USER_INFO_1_CONTAINERA;
+
+typedef struct _LLS_PRODUCT_USER_ENUM_STRUCTA {
+ DWORD Level;
+ [switch_is(Level)] union _LLS_PRODUCT_USER_ENUM_UNIONA {
+ [case(0)]
+ PLLS_PRODUCT_USER_INFO_0_CONTAINERA Level0;
+ [case(1)]
+ PLLS_PRODUCT_USER_INFO_1_CONTAINERA Level1;
+ [default]
+ ;
+ } LlsProductUserInfo;
+} LLS_PRODUCT_USER_ENUM_STRUCTA, *PLLS_PRODUCT_USER_ENUM_STRUCTA;
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Product License Info
+///////////////////////////////////////////////////////////////////////////////
+//
+// Unicode
+//
+typedef struct _LLS_PRODUCT_LICENSE_INFO_0W {
+ LONG Quantity;
+ DWORD Date;
+ PNAMEW Admin;
+ PNAMEW Comment;
+} LLS_PRODUCT_LICENSE_INFO_0W, *PLLS_PRODUCT_LICENSE_INFO_0W;
+
+typedef struct _LLS_PRODUCT_LICENSE_INFO_1W {
+ LONG Quantity;
+ DWORD MaxQuantity;
+ DWORD Date;
+ PNAMEW Admin;
+ PNAMEW Comment;
+ DWORD AllowedModes;
+ DWORD CertificateID;
+ PNAMEW Source;
+ DWORD ExpirationDate;
+ DWORD Secrets[ 4 ];
+} LLS_PRODUCT_LICENSE_INFO_1W, *PLLS_PRODUCT_LICENSE_INFO_1W;
+
+typedef [switch_type(DWORD)] union {
+ [case(0)] LLS_PRODUCT_LICENSE_INFO_0W ProductLicenseInfo0;
+ [case(1)] LLS_PRODUCT_LICENSE_INFO_1W ProductLicenseInfo1;
+} LLS_PRODUCT_LICENSE_INFOW, *PLLS_PRODUCT_LICNESE_INFOW;
+
+typedef struct _LLS_PRODUCT_LICENSE_INFO_0_CONTAINERW {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] PLLS_PRODUCT_LICENSE_INFO_0W Buffer;
+} LLS_PRODUCT_LICENSE_INFO_0_CONTAINERW, *PLLS_PRODUCT_LICENSE_INFO_0_CONTAINERW;
+
+typedef struct _LLS_PRODUCT_LICENSE_INFO_1_CONTAINERW {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] PLLS_PRODUCT_LICENSE_INFO_1W Buffer;
+} LLS_PRODUCT_LICENSE_INFO_1_CONTAINERW, *PLLS_PRODUCT_LICENSE_INFO_1_CONTAINERW;
+
+typedef struct _LLS_PRODUCT_LICENSE_ENUM_STRUCTW {
+ DWORD Level;
+ [switch_is(Level)] union _LLS_PRODUCT_LICENSE_ENUM_UNIONW {
+ [case(0)]
+ PLLS_PRODUCT_LICENSE_INFO_0_CONTAINERW Level0;
+ [case(1)]
+ PLLS_PRODUCT_LICENSE_INFO_1_CONTAINERW Level1;
+ [default]
+ ;
+ } LlsProductLicenseInfo;
+} LLS_PRODUCT_LICENSE_ENUM_STRUCTW, *PLLS_PRODUCT_LICENSE_ENUM_STRUCTW;
+
+
+//
+// ANSI
+//
+typedef struct _LLS_PRODUCT_LICENSE_INFO_0A {
+ LONG Quantity;
+ DWORD Date;
+ PNAMEA Admin;
+ PNAMEA Comment;
+} LLS_PRODUCT_LICENSE_INFO_0A, *PLLS_PRODUCT_LICENSE_INFO_0A;
+
+typedef struct _LLS_PRODUCT_LICENSE_INFO_1A {
+ LONG Quantity;
+ DWORD MaxQuantity;
+ DWORD Date;
+ PNAMEA Admin;
+ PNAMEA Comment;
+ DWORD AllowedModes;
+ DWORD CertificateID;
+ PNAMEA Source;
+ DWORD ExpirationDate;
+ DWORD Secrets[ 4 ];
+} LLS_PRODUCT_LICENSE_INFO_1A, *PLLS_PRODUCT_LICENSE_INFO_1A;
+
+typedef [switch_type(DWORD)] union {
+ [case(0)] LLS_PRODUCT_LICENSE_INFO_0A ProductLicenseInfo0;
+ [case(1)] LLS_PRODUCT_LICENSE_INFO_1A ProductLicenseInfo1;
+} LLS_PRODUCT_LICENSE_INFOA, *PLLS_PRODUCT_LICENSE_INFOA;
+
+typedef struct _LLS_PRODUCT_LICENSE_INFO_0_CONTAINERA {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] PLLS_PRODUCT_LICENSE_INFO_0A Buffer;
+} LLS_PRODUCT_LICENSE_INFO_0_CONTAINERA, *PLLS_PRODUCT_LICENSE_INFO_0_CONTAINERA;
+
+typedef struct _LLS_PRODUCT_LICENSE_INFO_1_CONTAINERA {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] PLLS_PRODUCT_LICENSE_INFO_1A Buffer;
+} LLS_PRODUCT_LICENSE_INFO_1_CONTAINERA, *PLLS_PRODUCT_LICENSE_INFO_1_CONTAINERA;
+
+typedef struct _LLS_PRODUCT_LICENSE_ENUM_STRUCTA {
+ DWORD Level;
+ [switch_is(Level)] union _LLS_PRODUCT_LICENSE_ENUM_UNIONA {
+ [case(0)]
+ PLLS_PRODUCT_LICENSE_INFO_0_CONTAINERA Level0;
+ [case(1)]
+ PLLS_PRODUCT_LICENSE_INFO_1_CONTAINERA Level1;
+ [default]
+ ;
+ } LlsProductLicenseInfo;
+} LLS_PRODUCT_LICENSE_ENUM_STRUCTA, *PLLS_PRODUCT_LICENSE_ENUM_STRUCTA;
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Server Product Info
+///////////////////////////////////////////////////////////////////////////////
+//
+// Unicode
+//
+typedef struct _LLS_SERVER_PRODUCT_INFO_0W {
+ PNAMEW Name;
+} LLS_SERVER_PRODUCT_INFO_0W, *PLLS_SERVER_PRODUCT_INFO_0W;
+
+typedef struct _LLS_SERVER_PRODUCT_INFO_1W {
+ PNAMEW Name;
+ DWORD Flags;
+ ULONG MaxUses;
+ ULONG MaxSetUses;
+ ULONG HighMark;
+} LLS_SERVER_PRODUCT_INFO_1W, *PLLS_SERVER_PRODUCT_INFO_1W;
+
+typedef [switch_type(DWORD)] union {
+ [case(0)] LLS_SERVER_PRODUCT_INFO_0W ServerProductInfo0;
+ [case(1)] LLS_SERVER_PRODUCT_INFO_1W ServerProductInfo1;
+} LLS_SERVER_PRODUCT_INFOW, *PLLS_SERVER_PRODUCT_INFOW;
+
+typedef struct _LLS_SERVER_PRODUCT_INFO_0_CONTAINERW {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] PLLS_SERVER_PRODUCT_INFO_0W Buffer;
+} LLS_SERVER_PRODUCT_INFO_0_CONTAINERW, *PLLS_SERVER_PRODUCT_INFO_0_CONTAINERW;
+
+typedef struct _LLS_SERVER_PRODUCT_INFO_1_CONTAINERW {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] PLLS_SERVER_PRODUCT_INFO_1W Buffer;
+} LLS_SERVER_PRODUCT_INFO_1_CONTAINERW, *PLLS_SERVER_PRODUCT_INFO_1_CONTAINERW;
+
+typedef struct _LLS_SERVER_PRODUCT_ENUM_STRUCTW {
+ DWORD Level;
+ [switch_is(Level)] union _LLS_SERVER_PRODUCT_ENUM_UNIONW {
+ [case(0)]
+ PLLS_SERVER_PRODUCT_INFO_0_CONTAINERW Level0;
+ [case(1)]
+ PLLS_SERVER_PRODUCT_INFO_1_CONTAINERW Level1;
+ [default]
+ ;
+ } LlsServerProductInfo;
+} LLS_SERVER_PRODUCT_ENUM_STRUCTW, *PLLS_SERVER_PRODUCT_ENUM_STRUCTW;
+
+
+//
+// ANSI
+//
+typedef struct _LLS_SERVER_PRODUCT_INFO_0A {
+ PNAMEA Name;
+} LLS_SERVER_PRODUCT_INFO_0A, *PLLS_SERVER_PRODUCT_INFO_0A;
+
+typedef struct _LLS_SERVER_PRODUCT_INFO_1A {
+ PNAMEA Name;
+ DWORD Flags;
+ ULONG MaxUses;
+ ULONG MaxSetUses;
+ ULONG HighMark;
+} LLS_SERVER_PRODUCT_INFO_1A, *PLLS_SERVER_PRODUCT_INFO_1A;
+
+typedef [switch_type(DWORD)] union {
+ [case(0)] LLS_SERVER_PRODUCT_INFO_0A ServerProductInfo0;
+ [case(1)] LLS_SERVER_PRODUCT_INFO_1A ServerProductInfo1;
+} LLS_SERVER_PRODUCT_INFOA, *PLLS_SERVER_PRODUCT_INFOA;
+
+typedef struct _LLS_SERVER_PRODUCT_INFO_0_CONTAINERA {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] PLLS_SERVER_PRODUCT_INFO_0A Buffer;
+} LLS_SERVER_PRODUCT_INFO_0_CONTAINERA, *PLLS_SERVER_PRODUCT_INFO_0_CONTAINERA;
+
+typedef struct _LLS_SERVER_PRODUCT_INFO_1_CONTAINERA {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] PLLS_SERVER_PRODUCT_INFO_1A Buffer;
+} LLS_SERVER_PRODUCT_INFO_1_CONTAINERA, *PLLS_SERVER_PRODUCT_INFO_1_CONTAINERA;
+
+typedef struct _LLS_SERVER_PRODUCT_ENUM_STRUCTA {
+ DWORD Level;
+ [switch_is(Level)] union _LLS_SERVER_PRODUCT_ENUM_UNIONA {
+ [case(0)]
+ PLLS_SERVER_PRODUCT_INFO_0_CONTAINERA Level0;
+ [case(1)]
+ PLLS_SERVER_PRODUCT_INFO_1_CONTAINERA Level1;
+ [default]
+ ;
+ } LlsServerProductInfo;
+} LLS_SERVER_PRODUCT_ENUM_STRUCTA, *PLLS_SERVER_PRODUCT_ENUM_STRUCTA;
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// User Info
+///////////////////////////////////////////////////////////////////////////////
+//
+// Unicode
+//
+typedef struct _LLS_USER_INFO_0W {
+ PNAMEW Name;
+} LLS_USER_INFO_0W, *PLLS_USER_INFO_0W;
+
+typedef struct _LLS_USER_INFO_1W {
+ PNAMEW Name;
+ DWORD Flags;
+ PNAMEW Mapping;
+ ULONG Licensed;
+ ULONG UnLicensed;
+} LLS_USER_INFO_1W, *PLLS_USER_INFO_1W;
+
+typedef struct _LLS_USER_INFO_2W {
+ PNAMEW Name;
+ DWORD Flags;
+ PNAMEW Mapping;
+ ULONG Licensed;
+ ULONG UnLicensed;
+ [string, unique] LPWSTR Products;
+} LLS_USER_INFO_2W, *PLLS_USER_INFO_2W;
+
+typedef [switch_type(DWORD)] union {
+ [case(0)] LLS_USER_INFO_0W UserInfo0;
+ [case(1)] LLS_USER_INFO_1W UserInfo1;
+ [case(2)] LLS_USER_INFO_2W UserInfo2;
+} LLS_USER_INFOW, *PLLS_USER_INFOW;
+
+typedef struct _LLS_USER_INFO_0_CONTAINERW {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] PLLS_USER_INFO_0W Buffer;
+} LLS_USER_INFO_0_CONTAINERW, *PLLS_USER_INFO_0_CONTAINERW;
+
+typedef struct _LLS_USER_INFO_1_CONTAINERW {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] PLLS_USER_INFO_1W Buffer;
+} LLS_USER_INFO_1_CONTAINERW, *PLLS_USER_INFO_1_CONTAINERW;
+
+typedef struct _LLS_USER_INFO_2_CONTAINERW {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] PLLS_USER_INFO_2W Buffer;
+} LLS_USER_INFO_2_CONTAINERW, *PLLS_USER_INFO_2_CONTAINERW;
+
+typedef struct _LLS_USER_ENUM_STRUCTW {
+ DWORD Level;
+ [switch_is(Level)] union _LLS_USER_ENUM_UNIONW {
+ [case(0)]
+ PLLS_USER_INFO_0_CONTAINERW Level0;
+ [case(1)]
+ PLLS_USER_INFO_1_CONTAINERW Level1;
+ [case(2)]
+ PLLS_USER_INFO_2_CONTAINERW Level2;
+ [default]
+ ;
+ } LlsUserInfo;
+} LLS_USER_ENUM_STRUCTW, *PLLS_USER_ENUM_STRUCTW;
+
+
+//
+// ANSI
+//
+typedef struct _LLS_USER_INFO_0A {
+ PNAMEA Name;
+} LLS_USER_INFO_0A, *PLLS_USER_INFO_0A;
+
+typedef struct _LLS_USER_INFO_1A {
+ PNAMEA Name;
+ DWORD Flags;
+ PNAMEA Mapping;
+ ULONG Licensed;
+ ULONG UnLicensed;
+} LLS_USER_INFO_1A, *PLLS_USER_INFO_1A;
+
+typedef struct _LLS_USER_INFO_2A {
+ PNAMEA Name;
+ DWORD Flags;
+ PNAMEA Mapping;
+ ULONG Licensed;
+ ULONG UnLicensed;
+ [string, unique] LPSTR Products;
+} LLS_USER_INFO_2A, *PLLS_USER_INFO_2A;
+
+typedef [switch_type(DWORD)] union {
+ [case(0)] LLS_USER_INFO_0A UserInfo0;
+ [case(1)] LLS_USER_INFO_1A UserInfo1;
+ [case(2)] LLS_USER_INFO_2A UserInfo2;
+} LLS_USER_INFOA, *PLLS_USER_INFOA;
+
+typedef struct _LLS_USER_INFO_0_CONTAINERA {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] PLLS_USER_INFO_0A Buffer;
+} LLS_USER_INFO_0_CONTAINERA, *PLLS_USER_INFO_0_CONTAINERA;
+
+typedef struct _LLS_USER_INFO_1_CONTAINERA {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] PLLS_USER_INFO_1A Buffer;
+} LLS_USER_INFO_1_CONTAINERA, *PLLS_USER_INFO_1_CONTAINERA;
+
+typedef struct _LLS_USER_INFO_2_CONTAINERA {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] PLLS_USER_INFO_2A Buffer;
+} LLS_USER_INFO_2_CONTAINERA, *PLLS_USER_INFO_2_CONTAINERA;
+
+typedef struct _LLS_USER_ENUM_STRUCTA {
+ DWORD Level;
+ [switch_is(Level)] union _LLS_USER_ENUM_UNIONA {
+ [case(0)]
+ PLLS_USER_INFO_0_CONTAINERA Level0;
+ [case(1)]
+ PLLS_USER_INFO_1_CONTAINERA Level1;
+ [case(2)]
+ PLLS_USER_INFO_2_CONTAINERA Level2;
+ [default]
+ ;
+ } LlsUserInfo;
+} LLS_USER_ENUM_STRUCTA, *PLLS_USER_ENUM_STRUCTA;
+
+
+///////////////////////////////////////////////////////////////////////////////
+// User Product info
+///////////////////////////////////////////////////////////////////////////////
+//
+// Unicode
+//
+typedef struct _LLS_USER_PRODUCT_INFO_0W {
+ PNAMEW Product;
+} LLS_USER_PRODUCT_INFO_0W, *PLLS_USER_PRODUCT_INFO_0W;
+
+typedef struct _LLS_USER_PRODUCT_INFO_1W {
+ PNAMEW Product;
+ DWORD Flags;
+ DWORD LastUsed;
+ ULONG UsageCount;
+} LLS_USER_PRODUCT_INFO_1W, *PLLS_USER_PRODUCT_INFO_1W;
+
+typedef [switch_type(DWORD)] union {
+ [case(0)] LLS_USER_PRODUCT_INFO_0W UserProduct0;
+ [case(1)] LLS_USER_PRODUCT_INFO_1W UserProduct1;
+} LLS_USER_PRODUCT_INFOW, *PLLS_USER_PRODUCT_INFOW;
+
+typedef struct _LLS_USER_PRODUCT_INFO_0_CONTAINERW {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] PLLS_USER_PRODUCT_INFO_0W Buffer;
+} LLS_USER_PRODUCT_INFO_0_CONTAINERW, *PLLS_USER_PRODUCT_INFO_0_CONTAINERW;
+
+typedef struct _LLS_USER_PRODUCT_INFO_1_CONTAINERW {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] PLLS_USER_PRODUCT_INFO_1W Buffer;
+} LLS_USER_PRODUCT_INFO_1_CONTAINERW, *PLLS_USER_PRODUCT_INFO_1_CONTAINERW;
+
+typedef struct _LLS_USER_PRODUCT_ENUM_STRUCTW {
+ DWORD Level;
+ [switch_is(Level)] union _LLS_USER_PRODUCT_ENUM_UNIONW {
+ [case(0)]
+ PLLS_USER_PRODUCT_INFO_0_CONTAINERW Level0;
+ [case(1)]
+ PLLS_USER_PRODUCT_INFO_1_CONTAINERW Level1;
+ [default]
+ ;
+ } LlsUserProductInfo;
+} LLS_USER_PRODUCT_ENUM_STRUCTW, *PLLS_USER_PRODUCT_ENUM_STRUCTW;
+
+
+//
+// ANSI
+//
+typedef struct _LLS_USER_PRODUCT_INFO_0A {
+ PNAMEA Product;
+} LLS_USER_PRODUCT_INFO_0A, *PLLS_USER_PRODUCT_INFO_0A;
+
+typedef struct _LLS_USER_PRODUCT_INFO_1A {
+ PNAMEA Product;
+ DWORD Flags;
+ DWORD LastUsed;
+ ULONG UsageCount;
+} LLS_USER_PRODUCT_INFO_1A, *PLLS_USER_PRODUCT_INFO_1A;
+
+typedef [switch_type(DWORD)] union {
+ [case(0)] LLS_USER_PRODUCT_INFO_0A UserProduct0;
+ [case(1)] LLS_USER_PRODUCT_INFO_1A UserProduct1;
+} LLS_USER_PRODUCT_INFOA, *PLLS_USER_PRODUCT_INFOA;
+
+typedef struct _LLS_USER_PRODUCT_INFO_0_CONTAINERA {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] PLLS_USER_PRODUCT_INFO_0A Buffer;
+} LLS_USER_PRODUCT_INFO_0_CONTAINERA, *PLLS_USER_PRODUCT_INFO_0_CONTAINERA;
+
+typedef struct _LLS_USER_PRODUCT_INFO_1_CONTAINERA {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] PLLS_USER_PRODUCT_INFO_1A Buffer;
+} LLS_USER_PRODUCT_INFO_1_CONTAINERA, *PLLS_USER_PRODUCT_INFO_1_CONTAINERA;
+
+typedef struct _LLS_USER_PRODUCT_ENUM_STRUCTA {
+ DWORD Level;
+ [switch_is(Level)] union _LLS_USER_PRODUCT_ENUM_UNIONA {
+ [case(0)]
+ PLLS_USER_PRODUCT_INFO_0_CONTAINERA Level0;
+ [case(1)]
+ PLLS_USER_PRODUCT_INFO_1_CONTAINERA Level1;
+ [default]
+ ;
+ } LlsUserProductInfo;
+} LLS_USER_PRODUCT_ENUM_STRUCTA, *PLLS_USER_PRODUCT_ENUM_STRUCTA;
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Mapping Info
+///////////////////////////////////////////////////////////////////////////////
+//
+// Unicode
+//
+typedef struct _LLS_MAPPING_INFO_0W {
+ PNAMEW Name;
+} LLS_MAPPING_INFO_0W, *PLLS_MAPPING_INFO_0W;
+
+typedef struct _LLS_MAPPING_INFO_1W {
+ PNAMEW Name;
+ PNAMEW Comment;
+ ULONG Licenses;
+} LLS_MAPPING_INFO_1W, *PLLS_MAPPING_INFO_1W;
+
+typedef [switch_type(DWORD)] union {
+ [case(0)] LLS_MAPPING_INFO_0W MappingInfo0;
+ [case(1)] LLS_MAPPING_INFO_1W MappingInfo1;
+} LLS_MAPPING_INFOW, *PLLS_MAPPING_INFOW;
+
+typedef struct _LLS_MAPPING_INFO_0_CONTAINERW {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] PLLS_MAPPING_INFO_0W Buffer;
+} LLS_MAPPING_INFO_0_CONTAINERW, *PLLS_MAPPING_INFO_0_CONTAINERW;
+
+typedef struct _LLS_MAPPING_INFO_1_CONTAINERW {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] PLLS_MAPPING_INFO_1W Buffer;
+} LLS_MAPPING_INFO_1_CONTAINERW, *PLLS_MAPPING_INFO_1_CONTAINERW;
+
+typedef struct _LLS_MAPPING_ENUM_STRUCTW {
+ DWORD Level;
+ [switch_is(Level)] union _LLS_MAPPING_ENUM_UNIONW {
+ [case(0)]
+ PLLS_MAPPING_INFO_0_CONTAINERW Level0;
+ [case(1)]
+ PLLS_MAPPING_INFO_1_CONTAINERW Level1;
+ [default]
+ ;
+ } LlsMappingInfo;
+} LLS_MAPPING_ENUM_STRUCTW, *PLLS_MAPPING_ENUM_STRUCTW;
+
+
+//
+// ANSI
+//
+typedef struct _LLS_MAPPING_INFO_0A {
+ PNAMEA Name;
+} LLS_MAPPING_INFO_0A, *PLLS_MAPPING_INFO_0A;
+
+typedef struct _LLS_MAPPING_INFO_1A {
+ PNAMEA Name;
+ PNAMEA Comment;
+ ULONG Licenses;
+} LLS_MAPPING_INFO_1A, *PLLS_MAPPING_INFO_1A;
+
+typedef [switch_type(DWORD)] union {
+ [case(0)] LLS_MAPPING_INFO_0A MappingInfo0;
+ [case(1)] LLS_MAPPING_INFO_1A MappingInfo1;
+} LLS_MAPPING_INFOA, *PLLS_MAPPING_INFOA;
+
+typedef struct _LLS_MAPPING_INFO_0_CONTAINERA {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] PLLS_MAPPING_INFO_0A Buffer;
+} LLS_MAPPING_INFO_0_CONTAINERA, *PLLS_MAPPING_INFO_0_CONTAINERA;
+
+typedef struct _LLS_MAPPING_INFO_1_CONTAINERA {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] PLLS_MAPPING_INFO_1A Buffer;
+} LLS_MAPPING_INFO_1_CONTAINERA, *PLLS_MAPPING_INFO_1_CONTAINERA;
+
+typedef struct _LLS_MAPPING_ENUM_STRUCTA {
+ DWORD Level;
+ [switch_is(Level)] union _LLS_MAPPING_ENUM_UNIONA {
+ [case(0)]
+ PLLS_MAPPING_INFO_0_CONTAINERA Level0;
+ [case(1)]
+ PLLS_MAPPING_INFO_1_CONTAINERA Level1;
+ [default]
+ ;
+ } LlsMappingInfo;
+} LLS_MAPPING_ENUM_STRUCTA, *PLLS_MAPPING_ENUM_STRUCTA;
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Service Info
+///////////////////////////////////////////////////////////////////////////////
+//
+// Unicode
+//
+typedef struct _LLS_SERVICE_INFO_0W {
+ DWORD Version;
+ DWORD TimeStarted;
+ DWORD Mode;
+ PNAMEW ReplicateTo;
+ PNAMEW EnterpriseServer;
+ DWORD ReplicationType;
+ DWORD ReplicationTime;
+ DWORD UseEnterprise;
+ DWORD LastReplicated;
+} LLS_SERVICE_INFO_0W, *PLLS_SERVICE_INFO_0W;
+
+typedef [switch_type(DWORD)] union {
+ [case(0)] LLS_SERVICE_INFO_0W ServiceInfo0;
+} LLS_SERVICE_INFOW, *PLLS_SERVICE_INFOW;
+
+
+//
+// ANSI
+//
+typedef struct _LLS_SERVICE_INFO_0A {
+ DWORD Version;
+ DWORD TimeStarted;
+ DWORD Mode;
+ PNAMEA ReplicateTo;
+ PNAMEA EnterpriseServer;
+ DWORD ReplicationType;
+ DWORD ReplicationTime;
+ DWORD UseEnterprise;
+ DWORD LastReplicated;
+} LLS_SERVICE_INFO_0A, *PLLS_SERVICE_INFO_0A;
+
+typedef [switch_type(DWORD)] union {
+ [case(0)] LLS_SERVICE_INFO_0A ServiceInfo0;
+} LLS_SERVICE_INFOA, *PLLS_SERVICE_INFOA;
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Server Info
+///////////////////////////////////////////////////////////////////////////////
+//
+// Unicode
+//
+typedef struct _LLS_SERVER_INFO_0W {
+ PNAMEW Name;
+} LLS_SERVER_INFO_0W, *PLLS_SERVER_INFO_0W;
+
+typedef [switch_type(DWORD)] union {
+ [case(0)] LLS_SERVER_INFO_0W ServerInfo0;
+} LLS_SERVER_INFOW, *PLLS_SERVER_INFOW;
+
+typedef struct _LLS_SERVER_INFO_0_CONTAINERW {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] PLLS_SERVER_INFO_0W Buffer;
+} LLS_SERVER_INFO_0_CONTAINERW, *PLLS_SERVER_INFO_0_CONTAINERW;
+
+typedef struct _LLS_SERVER_ENUM_STRUCTW {
+ DWORD Level;
+ [switch_is(Level)] union _LLS_SERVER_ENUM_UNIONW {
+ [case(0)]
+ PLLS_SERVER_INFO_0_CONTAINERW Level0;
+ [default]
+ ;
+ } LlsServerInfo;
+} LLS_SERVER_ENUM_STRUCTW, *PLLS_SERVER_ENUM_STRUCTW;
+
+
+//
+// ANSI
+//
+typedef struct _LLS_SERVER_INFO_0A {
+ PNAMEA Name;
+} LLS_SERVER_INFO_0A, *PLLS_SERVER_INFO_0A;
+
+typedef [switch_type(DWORD)] union {
+ [case(0)] LLS_SERVER_INFO_0A ServerInfo0;
+} LLS_SERVER_INFOA, *PLLS_SERVER_INFOA;
+
+typedef struct _LLS_SERVER_INFO_0_CONTAINERA {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] PLLS_SERVER_INFO_0A Buffer;
+} LLS_SERVER_INFO_0_CONTAINERA, *PLLS_SERVER_INFO_0_CONTAINERA;
+
+typedef struct _LLS_SERVER_ENUM_STRUCTA {
+ DWORD Level;
+ [switch_is(Level)] union _LLS_SERVER_ENUM_UNIONA {
+ [case(0)]
+ PLLS_SERVER_INFO_0_CONTAINERA Level0;
+ [default]
+ ;
+ } LlsServerInfo;
+} LLS_SERVER_ENUM_STRUCTA, *PLLS_SERVER_ENUM_STRUCTA;
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Replication Info
+///////////////////////////////////////////////////////////////////////////////
+//
+// Unicode
+//
+typedef struct _REPL_REQUEST {
+ DWORD Version;
+ WCHAR EnterpriseServer[18];
+ DWORD EnterpriseServerDate;
+ DWORD LastReplicated;
+ DWORD CurrentTime;
+ ULONG NumberServices;
+ ULONG NumberUsers;
+ ULONG ReplSize;
+ ULONG Backoff;
+} REPL_REQUEST, *PREPL_REQUEST;
+
+typedef struct _REPL_SERVER_SERVICE_RECORD {
+ ULONG Server;
+ DWORD Flags;
+ ULONG Service;
+ ULONG MaxSessionCount;
+ ULONG MaxSetSessionCount;
+ ULONG HighMark;
+} REPL_SERVER_SERVICE_RECORD, *PREPL_SERVER_SERVICE_RECORD;
+
+typedef struct _REPL_SERVER_RECORD {
+ ULONG Index;
+ PNAMEW Name;
+ ULONG MasterServer;
+} REPL_SERVER_RECORD, *PREPL_SERVER_RECORD;
+
+typedef struct _REPL_SERVICE_RECORD {
+ ULONG Index;
+ PNAMEW Name;
+ DWORD Version;
+ PNAMEW FamilyName;
+} REPL_SERVICE_RECORD, *PREPL_SERVICE_RECORD;
+
+typedef struct _REPL_USER_NAME_RECORD {
+ PNAMEW Name;
+} REPL_USER_NAME_RECORD, *PREPL_USER_NAME_RECORD;
+
+typedef [unique] PREPL_SERVER_RECORD REPL_SERVERS;
+typedef [unique] PREPL_SERVER_SERVICE_RECORD REPL_SERVER_SERVICES;
+typedef [unique] PREPL_SERVICE_RECORD REPL_SERVICES;
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Certificate Target Info
+///////////////////////////////////////////////////////////////////////////////
+//
+// Unicode
+//
+typedef struct _LLS_CERTIFICATE_CLAIM_INFO_0W
+{
+ WCHAR ServerName[ 16 ]; // 1 + MAX_COMPUERNAME_LENGTH
+ LONG Quantity;
+} LLS_CERTIFICATE_CLAIM_INFO_0W, *PLLS_CERTIFICATE_CLAIM_INFO_0W;
+
+typedef [switch_type(DWORD)] union
+{
+ [case(0)] LLS_CERTIFICATE_CLAIM_INFO_0W ClaimInfo0;
+} LLS_CERTIFICATE_CLAIM_INFO_W, *PLLS_CERTIFICATE_CLAIM_INFO_W;
+
+typedef struct _LLS_CERTIFICATE_CLAIM_INFO_0_CONTAINERW
+{
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] PLLS_CERTIFICATE_CLAIM_INFO_0W Buffer;
+} LLS_CERTIFICATE_CLAIM_INFO_0_CONTAINERW, *PLLS_CERTIFICATE_CLAIM_INFO_0_CONTAINERW;
+
+typedef struct _LLS_CERTIFICATE_CLAIM_ENUM_STRUCTW
+{
+ DWORD Level;
+ [switch_is(Level)] union _LLS_CERTIFICATE_CLAIM_ENUM_UNIONW
+ {
+ [case(0)]
+ PLLS_CERTIFICATE_CLAIM_INFO_0_CONTAINERW Level0;
+ [default]
+ ;
+ } LlsCertificateClaimInfo;
+} LLS_CERTIFICATE_CLAIM_ENUM_STRUCTW, *PLLS_CERTIFICATE_CLAIM_ENUM_STRUCTW;
+
+//
+// ANSI
+//
+typedef struct _LLS_CERTIFICATE_CLAIM_INFO_0A
+{
+ CHAR ServerName[ 16 ]; // 1 + MAX_COMPUERNAME_LENGTH
+ LONG Quantity;
+} LLS_CERTIFICATE_CLAIM_INFO_0A, *PLLS_CERTIFICATE_CLAIM_INFO_0A;
+
+typedef [switch_type(DWORD)] union
+{
+ [case(0)] LLS_CERTIFICATE_CLAIM_INFO_0A ClaimInfo0;
+} LLS_CERTIFICATE_CLAIM_INFO_A, *PLLS_CERTIFICATE_CLAIM_INFO_A;
+
+typedef struct _LLS_CERTIFICATE_CLAIM_INFO_0_CONTAINERA
+{
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] PLLS_CERTIFICATE_CLAIM_INFO_0A Buffer;
+} LLS_CERTIFICATE_CLAIM_INFO_0_CONTAINERA, *PLLS_CERTIFICATE_CLAIM_INFO_0_CONTAINERA;
+
+typedef struct _LLS_CERTIFICATE_CLAIM_ENUM_STRUCTA
+{
+ DWORD Level;
+ [switch_is(Level)] union _LLS_CERTIFICATE_CLAIM_ENUM_UNIONA
+ {
+ [case(0)]
+ PLLS_CERTIFICATE_CLAIM_INFO_0_CONTAINERA Level0;
+ [default]
+ ;
+ } LlsCertificateClaimInfo;
+} LLS_CERTIFICATE_CLAIM_ENUM_STRUCTA, *PLLS_CERTIFICATE_CLAIM_ENUM_STRUCTA;
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Certificate Replication Info
+///////////////////////////////////////////////////////////////////////////////
+//
+// Unicode
+//
+typedef struct _REPL_CERT_DB_CERTIFICATE_CLAIM_0
+{
+ WCHAR ServerName[ 16 ]; // 1 + MAX_COMPUTERNAME_LENGTH
+ DWORD ReplicationDate;
+ LONG Quantity;
+} REPL_CERT_DB_CERTIFICATE_CLAIM_0, *PREPL_CERT_DB_CERTIFICATE_CLAIM_0;
+
+typedef struct _REPL_CERT_DB_CERTIFICATE_CLAIM_CONTAINER_0
+{
+ DWORD NumClaims;
+ [size_is(NumClaims)] PREPL_CERT_DB_CERTIFICATE_CLAIM_0 Claims;
+} REPL_CERT_DB_CERTIFICATE_CLAIM_CONTAINER_0, *PREPL_CERT_DB_CERTIFICATE_CLAIM_CONTAINER_0;
+
+typedef [switch_type(DWORD)] union
+{
+ [case(0)] REPL_CERT_DB_CERTIFICATE_CLAIM_CONTAINER_0 Level0;
+} REPL_CERT_DB_CERTIFICATE_CLAIM_CONTAINER, *PREPL_CERT_DB_CERTIFICATE_CLAIM_CONTAINER;
+
+typedef struct _REPL_CERT_DB_CERTIFICATE_HEADER_0
+{
+ DWORD CertificateID;
+ DWORD AllowedModes;
+ DWORD MaxQuantity;
+ DWORD ExpirationDate;
+
+ DWORD NumClaims;
+} REPL_CERT_DB_CERTIFICATE_HEADER_0, *PREPL_CERT_DB_CERTIFICATE_HEADER_0;
+
+typedef struct _REPL_CERT_DB_CERTIFICATE_HEADER_CONTAINER_0
+{
+ DWORD NumHeaders;
+ [size_is(NumHeaders)] PREPL_CERT_DB_CERTIFICATE_HEADER_0 Headers;
+} REPL_CERT_DB_CERTIFICATE_HEADER_CONTAINER_0, *PREPL_CERT_DB_CERTIFICATE_HEADER_CONTAINER_0;
+
+typedef [switch_type(DWORD)] union
+{
+ [case(0)] REPL_CERT_DB_CERTIFICATE_HEADER_CONTAINER_0 Level0;
+} REPL_CERT_DB_CERTIFICATE_HEADER_CONTAINER, *PREPL_CERT_DB_CERTIFICATE_HEADER_CONTAINER;
+
+typedef struct _REPL_CERTIFICATE_DB_0
+{
+ DWORD HeaderLevel;
+ [switch_is(HeaderLevel)]
+ REPL_CERT_DB_CERTIFICATE_HEADER_CONTAINER HeaderContainer;
+
+ DWORD ClaimLevel;
+ [switch_is(ClaimLevel)]
+ REPL_CERT_DB_CERTIFICATE_CLAIM_CONTAINER ClaimContainer;
+
+ DWORD StringSize;
+ [size_is(StringSize)] WCHAR * Strings;
+} REPL_CERTIFICATE_DB_0, *PREPL_CERTIFICATE_DB_0;
+
+typedef [switch_type(DWORD)] union
+{
+ [case(0)] REPL_CERTIFICATE_DB_0 Level0;
+} REPL_CERTIFICATE_DB, *PREPL_CERTIFICATE_DB;
+
+typedef [unique] PREPL_CERTIFICATE_DB REPL_CERTIFICATES;
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Product Security Replication Info
+///////////////////////////////////////////////////////////////////////////////
+//
+// Unicode
+//
+typedef struct _REPL_PRODUCT_SECURITY_0
+{
+ DWORD StringSize;
+ [size_is(StringSize)] WCHAR * Strings;
+} REPL_PRODUCT_SECURITY_0, *PREPL_PRODUCT_SECURITY_0;
+
+typedef [switch_type(DWORD)] union
+{
+ [case(0)] REPL_PRODUCT_SECURITY_0 Level0;
+} REPL_PRODUCT_SECURITY, *PREPL_PRODUCT_SECURITY;
+
+typedef [unique] PREPL_PRODUCT_SECURITY REPL_SECURE_PRODUCTS;
+
+
+///////////////////////////////////////////////////////////////////////////////
+// User Replication Info
+///////////////////////////////////////////////////////////////////////////////
+//
+// Unicode
+//
+typedef struct _REPL_USER_RECORD_0
+{
+ PNAMEW Name;
+ ULONG Service;
+ ULONG AccessCount;
+ DWORD LastAccess;
+} REPL_USER_RECORD_0, *PREPL_USER_RECORD_0;
+
+typedef struct _REPL_USER_RECORD_CONTAINER_0
+{
+ DWORD NumUsers;
+ [size_is(NumUsers)] PREPL_USER_RECORD_0 Users;
+} REPL_USER_RECORD_CONTAINER_0, *PREPL_USER_RECORD_CONTAINER_0;
+
+typedef struct _REPL_USER_RECORD_1
+{
+ PNAMEW Name;
+ ULONG Service;
+ ULONG AccessCount;
+ DWORD LastAccess;
+ DWORD Flags;
+} REPL_USER_RECORD_1, *PREPL_USER_RECORD_1;
+
+typedef struct _REPL_USER_RECORD_CONTAINER_1
+{
+ DWORD NumUsers;
+ [size_is(NumUsers)] PREPL_USER_RECORD_1 Users;
+} REPL_USER_RECORD_CONTAINER_1, *PREPL_USER_RECORD_CONTAINER_1;
+
+typedef [switch_type(DWORD)] union
+{
+ [case(0)] REPL_USER_RECORD_CONTAINER_0 Level0;
+ [case(1)] REPL_USER_RECORD_CONTAINER_1 Level1;
+} REPL_USER_RECORD_CONTAINER, *PREPL_USER_RECORD_CONTAINER;
+
+typedef [switch_type(DWORD)] union
+{
+ [case(0)] PREPL_USER_RECORD_0 Level0;
+ [case(1)] PREPL_USER_RECORD_1 Level1;
+} PREPL_USER_RECORD;
+
+typedef [unique] PREPL_USER_RECORD_CONTAINER REPL_USERS;
+typedef [unique] PREPL_USER_RECORD_0 REPL_USERS_0;
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Local Service Info
+///////////////////////////////////////////////////////////////////////////////
+//
+// Unicode
+//
+typedef struct _LLS_LOCAL_SERVICE_INFO_0W
+{
+ PNAMEW KeyName;
+ PNAMEW DisplayName;
+ PNAMEW FamilyDisplayName;
+ DWORD Mode;
+ DWORD FlipAllow;
+ DWORD ConcurrentLimit;
+ DWORD HighMark;
+} LLS_LOCAL_SERVICE_INFO_0W, *PLLS_LOCAL_SERVICE_INFO_0W;
+
+typedef [switch_type(DWORD)] union
+{
+ [case(0)] LLS_LOCAL_SERVICE_INFO_0W LocalServiceInfo0;
+} LLS_LOCAL_SERVICE_INFOW, *PLLS_LOCAL_SERVICE_INFOW;
+
+typedef struct _LLS_LOCAL_SERVICE_INFO_0_CONTAINERW
+{
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] PLLS_LOCAL_SERVICE_INFO_0W Buffer;
+} LLS_LOCAL_SERVICE_INFO_0_CONTAINERW, *PLLS_LOCAL_SERVICE_INFO_0_CONTAINERW;
+
+typedef struct _LLS_LOCAL_SERVICE_ENUM_STRUCTW
+{
+ DWORD Level;
+ [switch_is(Level)] union _LLS_LOCAL_SERVICE_ENUM_UNIONW
+ {
+ [case(0)]
+ PLLS_LOCAL_SERVICE_INFO_0_CONTAINERW Level0;
+ [default]
+ ;
+ } LlsLocalServiceInfo;
+} LLS_LOCAL_SERVICE_ENUM_STRUCTW, *PLLS_LOCAL_SERVICE_ENUM_STRUCTW;
+
+//
+// ANSI
+//
+typedef struct _LLS_LOCAL_SERVICE_INFO_0A
+{
+ PNAMEA KeyName;
+ PNAMEA DisplayName;
+ PNAMEA FamilyDisplayName;
+ DWORD Mode;
+ DWORD FlipAllow;
+ DWORD ConcurrentLimit;
+ DWORD HighMark;
+} LLS_LOCAL_SERVICE_INFO_0A, *PLLS_LOCAL_SERVICE_INFO_0A;
+
+typedef [switch_type(DWORD)] union
+{
+ [case(0)] LLS_LOCAL_SERVICE_INFO_0A LocalServiceInfo0;
+} LLS_LOCAL_SERVICE_INFOA, *PLLS_LOCAL_SERVICE_INFOA;
+
+typedef struct _LLS_LOCAL_SERVICE_INFO_0_CONTAINERA
+{
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] PLLS_LOCAL_SERVICE_INFO_0A Buffer;
+} LLS_LOCAL_SERVICE_INFO_0_CONTAINERA, *PLLS_LOCAL_SERVICE_INFO_0_CONTAINERA;
+
+typedef struct _LLS_LOCAL_SERVICE_ENUM_STRUCTA
+{
+ DWORD Level;
+ [switch_is(Level)] union _LLS_LOCAL_SERVICE_ENUM_UNIONA
+ {
+ [case(0)]
+ PLLS_LOCAL_SERVICE_INFO_0_CONTAINERA Level0;
+ [default]
+ ;
+ } LlsLocalServiceInfo;
+} LLS_LOCAL_SERVICE_ENUM_STRUCTA, *PLLS_LOCAL_SERVICE_ENUM_STRUCTA;
+
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// Connect / Disconnect API's
+//
+NTSTATUS
+LlsrConnect(
+ [out] PLLS_HANDLE Handle,
+ [in, string] LPWSTR Name
+ );
+
+NTSTATUS
+LlsrClose(
+ [in] LLS_HANDLE Handle
+ );
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// License control API's
+//
+NTSTATUS
+LlsrLicenseEnumW(
+ [in] LLS_HANDLE Handle,
+ [in, out] PLLS_LICENSE_ENUM_STRUCTW LicenseInfo,
+ [in] DWORD PrefMaxLen,
+ [out] LPDWORD TotalEntries,
+ [in, out, unique] LPDWORD ResumeHandle
+ );
+
+NTSTATUS
+LlsrLicenseEnumA(
+ [in] LLS_HANDLE Handle,
+ [in, out] PLLS_LICENSE_ENUM_STRUCTA LicenseInfo,
+ [in] DWORD PrefMaxLen,
+ [out] LPDWORD TotalEntries,
+ [in, out, unique] LPDWORD ResumeHandle
+ );
+
+// Add purchase of license for a product.
+NTSTATUS
+LlsrLicenseAddW(
+ [in] LLS_HANDLE Handle,
+ [in] DWORD Level,
+ [in, switch_is(Level)] PLLS_LICENSE_INFOW BufPtr
+ );
+
+NTSTATUS
+LlsrLicenseAddA(
+ [in] LLS_HANDLE Handle,
+ [in] DWORD Level,
+ [in, switch_is(Level)] PLLS_LICENSE_INFOA BufPtr
+ );
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// Product control API's
+//
+NTSTATUS
+LlsrProductEnumW(
+ [in] LLS_HANDLE Handle,
+ [in, out] PLLS_PRODUCT_ENUM_STRUCTW ProductInfo,
+ [in] DWORD PrefMaxLen,
+ [out] LPDWORD TotalEntries,
+ [in, out, unique] LPDWORD ResumeHandle
+ );
+
+NTSTATUS
+LlsrProductEnumA(
+ [in] LLS_HANDLE Handle,
+ [in, out] PLLS_PRODUCT_ENUM_STRUCTA ProductInfo,
+ [in] DWORD PrefMaxLen,
+ [out] LPDWORD TotalEntries,
+ [in, out, unique] LPDWORD ResumeHandle
+ );
+
+NTSTATUS
+LlsrProductAddW(
+ [in] LLS_HANDLE Handle,
+ [in, string] LPWSTR ProductFamily,
+ [in, string] LPWSTR Product,
+ [in, string] LPWSTR Version
+ );
+
+NTSTATUS
+LlsrProductAddA(
+ [in] LLS_HANDLE Handle,
+ [in, string] LPSTR ProductFamily,
+ [in, string] LPSTR Product,
+ [in, string] LPSTR Version
+ );
+
+NTSTATUS
+LlsrProductUserEnumW(
+ [in] LLS_HANDLE Handle,
+ [in, string] LPWSTR Product,
+ [in, out] PLLS_PRODUCT_USER_ENUM_STRUCTW ProductUserInfo,
+ [in] DWORD PrefMaxLen,
+ [out] LPDWORD TotalEntries,
+ [in, out, unique] LPDWORD ResumeHandle
+ );
+
+NTSTATUS
+LlsrProductUserEnumA(
+ [in] LLS_HANDLE Handle,
+ [in, string] LPSTR Product,
+ [in, out] PLLS_PRODUCT_USER_ENUM_STRUCTA ProductUserInfo,
+ [in] DWORD PrefMaxLen,
+ [out] LPDWORD TotalEntries,
+ [in, out, unique] LPDWORD ResumeHandle
+ );
+
+NTSTATUS
+LlsrProductServerEnumW(
+ [in] LLS_HANDLE Handle,
+ [in, string] LPWSTR Product,
+ [in, out] PLLS_SERVER_PRODUCT_ENUM_STRUCTW ProductServerInfo,
+ [in] DWORD PrefMaxLen,
+ [out] LPDWORD TotalEntries,
+ [in, out, unique] LPDWORD ResumeHandle
+ );
+
+NTSTATUS
+LlsrProductServerEnumA(
+ [in] LLS_HANDLE Handle,
+ [in, string] LPSTR Product,
+ [in, out] PLLS_SERVER_PRODUCT_ENUM_STRUCTA ProductServerInfo,
+ [in] DWORD PrefMaxLen,
+ [out] LPDWORD TotalEntries,
+ [in, out, unique] LPDWORD ResumeHandle
+ );
+
+
+NTSTATUS
+LlsrProductLicenseEnumW(
+ [in] LLS_HANDLE Handle,
+ [in, string] LPWSTR Product,
+ [in, out] PLLS_PRODUCT_LICENSE_ENUM_STRUCTW ProductLicenseInfo,
+ [in] DWORD PrefMaxLen,
+ [out] LPDWORD TotalEntries,
+ [in, out, unique] LPDWORD ResumeHandle
+ );
+
+NTSTATUS
+LlsrProductLicenseEnumA(
+ [in] LLS_HANDLE Handle,
+ [in, string] LPSTR Product,
+ [in, out] PLLS_PRODUCT_LICENSE_ENUM_STRUCTA ProductLicenseInfo,
+ [in] DWORD PrefMaxLen,
+ [out] LPDWORD TotalEntries,
+ [in, out, unique] LPDWORD ResumeHandle
+ );
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// User control API's
+//
+NTSTATUS
+LlsrUserEnumW(
+ [in] LLS_HANDLE Handle,
+ [in, out] PLLS_USER_ENUM_STRUCTW UserInfo,
+ [in] DWORD PrefMaxLen,
+ [out] LPDWORD TotalEntries,
+ [in, out, unique] LPDWORD ResumeHandle
+ );
+
+NTSTATUS
+LlsrUserEnumA(
+ [in] LLS_HANDLE Handle,
+ [in, out] PLLS_USER_ENUM_STRUCTA UserInfo,
+ [in] DWORD PrefMaxLen,
+ [out] LPDWORD TotalEntries,
+ [in, out, unique] LPDWORD ResumeHandle
+ );
+
+NTSTATUS
+LlsrUserInfoGetW(
+ [in] LLS_HANDLE Handle,
+ [in, string] LPWSTR User,
+ [in] DWORD Level,
+ [out, switch_is(Level)] PLLS_USER_INFOW *BufPtr
+ );
+
+NTSTATUS
+LlsrUserInfoGetA(
+ [in] LLS_HANDLE Handle,
+ [in, string] LPSTR User,
+ [in] DWORD Level,
+ [out, switch_is(Level)] PLLS_USER_INFOA *BufPtr
+ );
+
+NTSTATUS
+LlsrUserInfoSetW(
+ [in] LLS_HANDLE Handle,
+ [in, string] LPWSTR User,
+ [in] DWORD Level,
+ [in, switch_is(Level)] PLLS_USER_INFOW BufPtr
+ );
+
+NTSTATUS
+LlsrUserInfoSetA(
+ [in] LLS_HANDLE Handle,
+ [in, string] LPSTR User,
+ [in] DWORD Level,
+ [in, switch_is(Level)] PLLS_USER_INFOA BufPtr
+ );
+
+NTSTATUS
+LlsrUserDeleteW(
+ [in] LLS_HANDLE Handle,
+ [in, string] LPWSTR User
+ );
+
+NTSTATUS
+LlsrUserDeleteA(
+ [in] LLS_HANDLE Handle,
+ [in, string] LPSTR User
+ );
+
+NTSTATUS
+LlsrUserProductEnumW(
+ [in] LLS_HANDLE Handle,
+ [in, string] LPWSTR User,
+ [in, out] PLLS_USER_PRODUCT_ENUM_STRUCTW UserProductInfo,
+ [in] DWORD PrefMaxLen,
+ [out] LPDWORD TotalEntries,
+ [in, out, unique] LPDWORD ResumeHandle
+ );
+
+NTSTATUS
+LlsrUserProductEnumA(
+ [in] LLS_HANDLE Handle,
+ [in, string] LPSTR User,
+ [in, out] PLLS_USER_PRODUCT_ENUM_STRUCTA UserProductInfo,
+ [in] DWORD PrefMaxLen,
+ [out] LPDWORD TotalEntries,
+ [in, out, unique] LPDWORD ResumeHandle
+ );
+
+NTSTATUS
+LlsrUserProductDeleteW(
+ [in] LLS_HANDLE Handle,
+ [in, string] LPWSTR User,
+ [in, string] LPWSTR Product
+ );
+
+NTSTATUS
+LlsrUserProductDeleteA(
+ [in] LLS_HANDLE Handle,
+ [in, string] LPSTR User,
+ [in] LPSTR Product
+ );
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// Mapping control API's
+//
+NTSTATUS
+LlsrMappingEnumW(
+ [in] LLS_HANDLE Handle,
+ [in, out] PLLS_MAPPING_ENUM_STRUCTW MappingInfo,
+ [in] DWORD PrefMaxLen,
+ [out] LPDWORD TotalEntries,
+ [in, out, unique] LPDWORD ResumeHandle
+ );
+
+NTSTATUS
+LlsrMappingEnumA(
+ [in] LLS_HANDLE Handle,
+ [in, out] PLLS_MAPPING_ENUM_STRUCTA MappingInfo,
+ [in] DWORD PrefMaxLen,
+ [out] LPDWORD TotalEntries,
+ [in, out, unique] LPDWORD ResumeHandle
+ );
+
+NTSTATUS
+LlsrMappingInfoGetW(
+ [in] LLS_HANDLE Handle,
+ [in, string] LPWSTR Mapping,
+ [in] DWORD Level,
+ [out, switch_is(Level)] PLLS_MAPPING_INFOW *BufPtr
+ );
+
+NTSTATUS
+LlsrMappingInfoGetA(
+ [in] LLS_HANDLE Handle,
+ [in, string] LPSTR Mapping,
+ [in] DWORD Level,
+ [out, switch_is(Level)] PLLS_MAPPING_INFOA *BufPtr
+ );
+
+NTSTATUS
+LlsrMappingInfoSetW(
+ [in] LLS_HANDLE Handle,
+ [in, string] LPWSTR Mapping,
+ [in] DWORD Level,
+ [in, switch_is(Level)] PLLS_MAPPING_INFOW BufPtr
+ );
+
+NTSTATUS
+LlsrMappingInfoSetA(
+ [in] LLS_HANDLE Handle,
+ [in, string] LPSTR Mapping,
+ [in] DWORD Level,
+ [in, switch_is(Level)] PLLS_MAPPING_INFOA BufPtr
+ );
+
+NTSTATUS
+LlsrMappingUserEnumW(
+ [in] LLS_HANDLE Handle,
+ [in, string] LPWSTR Mapping,
+ [in, out] PLLS_USER_ENUM_STRUCTW MappingUserInfo,
+ [in] DWORD PrefMaxLen,
+ [out] LPDWORD TotalEntries,
+ [in, out, unique] LPDWORD ResumeHandle
+ );
+
+NTSTATUS
+LlsrMappingUserEnumA(
+ [in] LLS_HANDLE Handle,
+ [in, string] LPSTR Mapping,
+ [in, out] PLLS_USER_ENUM_STRUCTA MappingUserInfo,
+ [in] DWORD PrefMaxLen,
+ [out] LPDWORD TotalEntries,
+ [in, out, unique] LPDWORD ResumeHandle
+ );
+
+NTSTATUS
+LlsrMappingUserAddW(
+ [in] LLS_HANDLE Handle,
+ [in, string] LPWSTR Mapping,
+ [in, string] LPWSTR User
+ );
+
+NTSTATUS
+LlsrMappingUserAddA(
+ [in] LLS_HANDLE Handle,
+ [in, string] LPSTR Mapping,
+ [in, string] LPSTR User
+ );
+
+NTSTATUS
+LlsrMappingUserDeleteW(
+ [in] LLS_HANDLE Handle,
+ [in, string] LPWSTR Mapping,
+ [in, string] LPWSTR User
+ );
+
+NTSTATUS
+LlsrMappingUserDeleteA(
+ [in] LLS_HANDLE Handle,
+ [in, string] LPSTR Mapping,
+ [in, string] LPSTR User
+ );
+
+NTSTATUS
+LlsrMappingAddW(
+ [in] LLS_HANDLE Handle,
+ [in] DWORD Level,
+ [in, switch_is(Level)] PLLS_MAPPING_INFOW BufPtr
+ );
+
+NTSTATUS
+LlsrMappingAddA(
+ [in] LLS_HANDLE Handle,
+ [in] DWORD Level,
+ [in, switch_is(Level)] PLLS_MAPPING_INFOA BufPtr
+ );
+
+NTSTATUS
+LlsrMappingDeleteW(
+ [in] LLS_HANDLE Handle,
+ [in, string] LPWSTR Mapping
+ );
+
+NTSTATUS
+LlsrMappingDeleteA(
+ [in] LLS_HANDLE Handle,
+ [in, string] LPSTR Mapping
+ );
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// Server Product API's
+//
+NTSTATUS
+LlsrServerEnumW(
+ [in] LLS_HANDLE Handle,
+ [in, string] LPWSTR Server,
+ [in, out] PLLS_SERVER_ENUM_STRUCTW ServerInfo,
+ [in] DWORD PrefMaxLen,
+ [out] LPDWORD TotalEntries,
+ [in, out, unique] LPDWORD ResumeHandle
+ );
+
+NTSTATUS
+LlsrServerEnumA(
+ [in] LLS_HANDLE Handle,
+ [in, string] LPSTR Server,
+ [in, out] PLLS_SERVER_ENUM_STRUCTA ServerInfo,
+ [in] DWORD PrefMaxLen,
+ [out] LPDWORD TotalEntries,
+ [in, out, unique] LPDWORD ResumeHandle
+ );
+
+NTSTATUS
+LlsrServerProductEnumW(
+ [in] LLS_HANDLE Handle,
+ [in, string] LPWSTR Server,
+ [in, out] PLLS_SERVER_PRODUCT_ENUM_STRUCTW ServerProductInfo,
+ [in] DWORD PrefMaxLen,
+ [out] LPDWORD TotalEntries,
+ [in, out, unique] LPDWORD ResumeHandle
+ );
+
+NTSTATUS
+LlsrServerProductEnumA(
+ [in] LLS_HANDLE Handle,
+ [in, string] LPSTR Server,
+ [in, out] PLLS_SERVER_PRODUCT_ENUM_STRUCTA ServerProductInfo,
+ [in] DWORD PrefMaxLen,
+ [out] LPDWORD TotalEntries,
+ [in, out, unique] LPDWORD ResumeHandle
+ );
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// Registry Wrapper API's
+//
+NTSTATUS
+LlsrLocalProductEnumW(
+ [in] LLS_HANDLE Handle,
+ [in, out] PLLS_SERVER_PRODUCT_ENUM_STRUCTW ServerProductInfo,
+ [in] DWORD PrefMaxLen,
+ [out] LPDWORD TotalEntries,
+ [in, out, unique] LPDWORD ResumeHandle
+ );
+
+NTSTATUS
+LlsrLocalProductEnumA(
+ [in] LLS_HANDLE Handle,
+ [in, out] PLLS_SERVER_PRODUCT_ENUM_STRUCTA ServerProductInfo,
+ [in] DWORD PrefMaxLen,
+ [out] LPDWORD TotalEntries,
+ [in, out, unique] LPDWORD ResumeHandle
+ );
+
+NTSTATUS
+LlsrLocalProductInfoGetW(
+ [in] LLS_HANDLE Handle,
+ [in, string] LPWSTR Product,
+ [in] DWORD Level,
+ [out, switch_is(Level)] PLLS_SERVER_PRODUCT_INFOW *BufPtr
+ );
+
+NTSTATUS
+LlsrLocalProductInfoGetA(
+ [in] LLS_HANDLE Handle,
+ [in, string] LPSTR Product,
+ [in] DWORD Level,
+ [out, switch_is(Level)] PLLS_SERVER_PRODUCT_INFOA *BufPtr
+ );
+
+NTSTATUS
+LlsrLocalProductInfoSetW(
+ [in] LLS_HANDLE Handle,
+ [in, string] LPWSTR Product,
+ [in] DWORD Level,
+ [in, switch_is(Level)] PLLS_SERVER_PRODUCT_INFOW BufPtr
+ );
+
+NTSTATUS
+LlsrLocalProductInfoSetA(
+ [in] LLS_HANDLE Handle,
+ [in, string] LPSTR Product,
+ [in] DWORD Level,
+ [in, switch_is(Level)] PLLS_SERVER_PRODUCT_INFOA BufPtr
+ );
+
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// Service control API's
+//
+NTSTATUS
+LlsrServiceInfoGetW(
+ [in] LLS_HANDLE Handle,
+ [in] DWORD Level,
+ [out, switch_is(Level)] PLLS_SERVICE_INFOW *BufPtr
+ );
+
+NTSTATUS
+LlsrServiceInfoGetA(
+ [in] LLS_HANDLE Handle,
+ [in] DWORD Level,
+ [out, switch_is(Level)] PLLS_SERVICE_INFOA *BufPtr
+ );
+
+NTSTATUS
+LlsrServiceInfoSetW(
+ [in] LLS_HANDLE Handle,
+ [in] DWORD Level,
+ [in, switch_is(Level)] PLLS_SERVICE_INFOW BufPtr
+ );
+
+NTSTATUS
+LlsrServiceInfoSetA(
+ [in] LLS_HANDLE Handle,
+ [in] DWORD Level,
+ [in, switch_is(Level)] PLLS_SERVICE_INFOA BufPtr
+ );
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// Replication API's
+//
+NTSTATUS
+LlsrReplConnect(
+ [out] PLLS_REPL_HANDLE Handle,
+ [in, string] LPWSTR Name
+ );
+
+NTSTATUS
+LlsrReplClose(
+ [in, out] LLS_REPL_HANDLE *Handle
+ );
+
+NTSTATUS
+LlsrReplicationRequestW(
+ [in] LLS_REPL_HANDLE Handle,
+ [in] DWORD Version,
+ [in, out] PREPL_REQUEST Request
+ );
+
+NTSTATUS
+LlsrReplicationServerAddW(
+ [in] LLS_REPL_HANDLE Handle,
+ [in] ULONG NumRecords,
+ [in, size_is(NumRecords)] REPL_SERVERS Servers
+ );
+
+NTSTATUS
+LlsrReplicationServerServiceAddW(
+ [in] LLS_REPL_HANDLE Handle,
+ [in] ULONG NumRecords,
+ [in, size_is(NumRecords)] REPL_SERVER_SERVICES ServerServices
+ );
+
+NTSTATUS
+LlsrReplicationServiceAddW(
+ [in] LLS_REPL_HANDLE Handle,
+ [in] ULONG NumRecords,
+ [in, size_is(NumRecords)] REPL_SERVICES Services
+ );
+
+NTSTATUS
+LlsrReplicationUserAddW(
+ [in] LLS_REPL_HANDLE Handle,
+ [in] ULONG NumRecords,
+ [in, size_is(NumRecords)] REPL_USERS_0 Users
+ );
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// LLS Extended API's (added for SUR)
+//
+NTSTATUS
+LlsrProductSecurityGetW(
+ [in] LLS_HANDLE Handle,
+ [in, string] LPWSTR Product,
+ [out] LPBOOL pIsSecure
+ );
+
+NTSTATUS
+LlsrProductSecurityGetA(
+ [in] LLS_HANDLE Handle,
+ [in, string] LPSTR Product,
+ [out] LPBOOL pIsSecure
+ );
+
+NTSTATUS
+LlsrProductSecuritySetW(
+ [in] LLS_HANDLE Handle,
+ [in, string] LPWSTR Product
+ );
+
+NTSTATUS
+LlsrProductSecuritySetA(
+ [in] LLS_HANDLE Handle,
+ [in, string] LPSTR Product
+ );
+
+NTSTATUS
+LlsrProductLicensesGetA(
+ [in] LLS_HANDLE Handle,
+ [in,string] LPSTR DisplayName,
+ [in] DWORD Mode,
+ [out] LPDWORD pQuantity );
+
+NTSTATUS
+LlsrProductLicensesGetW(
+ [in] LLS_HANDLE Handle,
+ [in, string] LPWSTR DisplayName,
+ [in] DWORD Mode,
+ [out] LPDWORD pQuantity );
+
+NTSTATUS
+LlsrCertificateClaimEnumA(
+ [in] LLS_HANDLE Handle,
+ [in] DWORD LicenseLevel,
+ [in, switch_is(LicenseLevel)] PLLS_LICENSE_INFOA LicensePtr,
+ [in,out] PLLS_CERTIFICATE_CLAIM_ENUM_STRUCTA ClaimInfo );
+
+NTSTATUS
+LlsrCertificateClaimEnumW(
+ [in] LLS_HANDLE Handle,
+ [in] DWORD LicenseLevel,
+ [in, switch_is(LicenseLevel)] PLLS_LICENSE_INFOW LicensePtr,
+ [in,out] PLLS_CERTIFICATE_CLAIM_ENUM_STRUCTW ClaimInfo );
+
+NTSTATUS
+LlsrCertificateClaimAddCheckA(
+ [in] LLS_HANDLE Handle,
+ [in] DWORD Level,
+ [in, switch_is(Level)] PLLS_LICENSE_INFOA LicensePtr,
+ [out] LPBOOL pbMayInstall );
+
+NTSTATUS
+LlsrCertificateClaimAddCheckW(
+ [in] LLS_HANDLE Handle,
+ [in] DWORD Level,
+ [in, switch_is(Level)] PLLS_LICENSE_INFOW LicensePtr,
+ [out] LPBOOL pbMayInstall );
+
+NTSTATUS
+LlsrCertificateClaimAddA(
+ [in] LLS_HANDLE Handle,
+ [in, string] LPSTR ServerName,
+ [in] DWORD Level,
+ [in, switch_is(Level)] PLLS_LICENSE_INFOA LicensePtr );
+
+NTSTATUS
+LlsrCertificateClaimAddW(
+ [in] LLS_HANDLE Handle,
+ [in, string] LPWSTR ServerName,
+ [in] DWORD Level,
+ [in, switch_is(Level)] PLLS_LICENSE_INFOW LicensePtr );
+
+NTSTATUS
+LlsrReplicationCertDbAddW(
+ [in] LLS_REPL_HANDLE Handle,
+ [in] DWORD Level,
+ [in, switch_is(Level)] REPL_CERTIFICATES Certificates );
+
+NTSTATUS
+LlsrReplicationProductSecurityAddW(
+ [in] LLS_REPL_HANDLE Handle,
+ [in] DWORD Level,
+ [in, switch_is(Level)] REPL_SECURE_PRODUCTS SecureProducts );
+
+NTSTATUS
+LlsrReplicationUserAddExW(
+ [in] LLS_REPL_HANDLE Handle,
+ [in] DWORD Level,
+ [in, switch_is(Level)] REPL_USERS Users );
+
+NTSTATUS
+LlsrCapabilityGet(
+ [in] LLS_HANDLE Handle,
+ [in] DWORD cbCapabilities,
+ [out, size_is(cbCapabilities)] LPBYTE pbCapabilities );
+
+NTSTATUS
+LlsrLocalServiceEnumW(
+ [in] LLS_HANDLE Handle,
+ [in, out] PLLS_LOCAL_SERVICE_ENUM_STRUCTW LocalServiceInfo,
+ [in] DWORD PrefMaxLen,
+ [out] LPDWORD TotalEntries,
+ [in, out, unique] LPDWORD ResumeHandle
+ );
+
+NTSTATUS
+LlsrLocalServiceEnumA(
+ [in] LLS_HANDLE Handle,
+ [in, out] PLLS_LOCAL_SERVICE_ENUM_STRUCTA LocalServiceInfo,
+ [in] DWORD PrefMaxLen,
+ [out] LPDWORD TotalEntries,
+ [in, out, unique] LPDWORD ResumeHandle
+ );
+
+NTSTATUS
+LlsrLocalServiceAddW(
+ [in] LLS_HANDLE Handle,
+ [in] DWORD Level,
+ [in, switch_is(Level)] PLLS_LOCAL_SERVICE_INFOW LocalServiceInfo
+ );
+
+NTSTATUS
+LlsrLocalServiceAddA(
+ [in] LLS_HANDLE Handle,
+ [in] DWORD Level,
+ [in, switch_is(Level)] PLLS_LOCAL_SERVICE_INFOA LocalServiceInfo
+ );
+
+NTSTATUS
+LlsrLocalServiceInfoSetW(
+ [in] LLS_HANDLE Handle,
+ [in, string] LPWSTR KeyName,
+ [in] DWORD Level,
+ [in, switch_is(Level)] PLLS_LOCAL_SERVICE_INFOW LocalServiceInfo
+ );
+
+NTSTATUS
+LlsrLocalServiceInfoSetA(
+ [in] LLS_HANDLE Handle,
+ [in, string] LPSTR KeyName,
+ [in] DWORD Level,
+ [in, switch_is(Level)] PLLS_LOCAL_SERVICE_INFOA LocalServiceInfo
+ );
+
+NTSTATUS
+LlsrLocalServiceInfoGetW(
+ [in] LLS_HANDLE Handle,
+ [in, string] LPWSTR KeyName,
+ [in] DWORD Level,
+ [out, switch_is(Level)] PLLS_LOCAL_SERVICE_INFOW * LocalServiceInfo
+ );
+
+NTSTATUS
+LlsrLocalServiceInfoGetA(
+ [in] LLS_HANDLE Handle,
+ [in, string] LPSTR KeyName,
+ [in] DWORD Level,
+ [out, switch_is(Level)] PLLS_LOCAL_SERVICE_INFOA * LocalServiceInfo
+ );
+}
diff --git a/private/net/svcdlls/lls/llssrv.acf b/private/net/svcdlls/lls/llssrv.acf
new file mode 100644
index 000000000..b812f5d91
--- /dev/null
+++ b/private/net/svcdlls/lls/llssrv.acf
@@ -0,0 +1,57 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ Llssrv.acf
+
+Abstract:
+
+ License Logging Service SERVER rpc stub attribute configuration file.
+
+ This file contains the attribute configuration information necessary
+ for generating the server stubs for remotable LLS functions.
+
+ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ !! !!
+ !! This .acf file is USED ONLY WHEN GENERATING LLS SERVER STUBS. !!
+ !! !!
+ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+
+ Use Llscli.acf when generating client stubs.
+
+
+Author:
+
+ Arthur Hanson (arth) Jan 30, 1995
+
+Environment:
+
+ User Mode
+
+Revision History:
+
+--*/
+
+[
+ implicit_handle(handle_t llsrpc_handle)
+]
+
+interface llsrpc
+
+{
+
+typedef [allocate(dont_free)] PNAMEW;
+typedef [allocate(dont_free)] PNAMEA;
+
+typedef [allocate(dont_free)] REPL_SERVERS;
+typedef [allocate(dont_free)] REPL_SERVER_SERVICES;
+typedef [allocate(dont_free)] REPL_SERVICES;
+typedef [allocate(dont_free)] REPL_USERS_0;
+typedef [allocate(dont_free)] REPL_CERTIFICATES;
+typedef [allocate(dont_free)] REPL_SECURE_PRODUCTS;
+typedef [allocate(dont_free)] REPL_USERS;
+
+}
diff --git a/private/net/svcdlls/lls/lsapi.idl b/private/net/svcdlls/lls/lsapi.idl
new file mode 100644
index 000000000..4853e1173
--- /dev/null
+++ b/private/net/svcdlls/lls/lsapi.idl
@@ -0,0 +1,88 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ lsapi.idl
+
+Abstract:
+
+
+Author:
+
+ Arthur Hanson (arth) Jan 20-1994
+
+Environment:
+
+ User Mode
+
+Revision History:
+
+--*/
+
+[
+ uuid(57674CD0-5200-11CE-A897-08002B2E9C6D),
+ version(0.0),
+#ifdef __midl
+ ms_union,
+#endif // __midl
+ pointer_default(unique)
+]
+
+interface lsapirpc
+
+{
+
+//
+// Import a dummy interface containing #includes for public .h files. This
+// trick is necessary so that midl will only generate marshalling routines
+// for subtypes that are relevant to the parameters specified on the RPC
+// interface. midl also ingores function prototypes contained therein.
+//
+
+import "llsimp.idl" ;
+
+//
+// Emit these constants into the generated file.
+//
+cpp_quote("#define LLS_LPC_ENDPOINT \"llslpc\"")
+//
+// Note: Must use quad backslash to emit two backslashes into #define
+// which when compiled will boil down to single backslash
+//
+cpp_quote("#define LLS_NP_ENDPOINT \"\\\\pipe\\\\llsrpc\"")
+
+
+typedef [string] LPWSTR PNAMEW;
+typedef [string] LPSTR PNAMEA;
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+//
+// Licensing API's
+//
+NTSTATUS
+LlsrLicenseRequestW(
+ [out] LPDWORD LicenseHandle,
+ [in, string] LPWSTR Product,
+ [in] ULONG VersionIndex,
+ [in] BOOLEAN IsAdmin,
+ [in] ULONG DataType,
+ [in] ULONG DataSize,
+ [in, size_is(DataSize)] PBYTE Data
+ );
+
+NTSTATUS
+LlsrLicenseFree(
+ [in] DWORD LicenseHandle
+ );
+
+
+}
diff --git a/private/net/svcdlls/lls/lsapicli.acf b/private/net/svcdlls/lls/lsapicli.acf
new file mode 100644
index 000000000..ae57f41c0
--- /dev/null
+++ b/private/net/svcdlls/lls/lsapicli.acf
@@ -0,0 +1,46 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ lsapicli.acf
+
+Abstract:
+
+ License Logging Service CLIENT rpc stub attribute configuration file.
+
+ This file contains the attribute configuration information necessary
+ for generating the client stubs for remotable LLS functions.
+
+ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ !! !!
+ !! This .acf file is USED ONLY WHEN GENERATING LLS CLIENT STUBS. !!
+ !! !!
+ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+
+ Use llssrv.acf when generating server stubs.
+
+
+Author:
+
+ Arthur Hanson (arth) Jan 30, 1995
+
+Environment:
+
+ User Mode
+
+Revision History:
+
+--*/
+
+[
+ implicit_handle(handle_t lsapirpc_handle)
+]
+
+interface lsapirpc
+
+{
+
+}
diff --git a/private/net/svcdlls/lls/lsapisrv.acf b/private/net/svcdlls/lls/lsapisrv.acf
new file mode 100644
index 000000000..06d6a1ae2
--- /dev/null
+++ b/private/net/svcdlls/lls/lsapisrv.acf
@@ -0,0 +1,49 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ Lsapicli.acf
+
+Abstract:
+
+ License Logging Service SERVER rpc stub attribute configuration file.
+
+ This file contains the attribute configuration information necessary
+ for generating the server stubs for remotable LLS functions.
+
+ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ !! !!
+ !! This .acf file is USED ONLY WHEN GENERATING LLS SERVER STUBS. !!
+ !! !!
+ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+
+ Use Llscli.acf when generating client stubs.
+
+
+Author:
+
+ Arthur Hanson (arth) Jan 30, 1995
+
+Environment:
+
+ User Mode
+
+Revision History:
+
+--*/
+
+[
+ implicit_handle(handle_t lsapirpc_handle)
+]
+
+interface lsapirpc
+
+{
+
+typedef [allocate(dont_free)] PNAMEW;
+typedef [allocate(dont_free)] PNAMEA;
+
+}
diff --git a/private/net/svcdlls/lls/lsdbgcli.acf b/private/net/svcdlls/lls/lsdbgcli.acf
new file mode 100644
index 000000000..95d505a7f
--- /dev/null
+++ b/private/net/svcdlls/lls/lsdbgcli.acf
@@ -0,0 +1,46 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ lsdbgcli.acf
+
+Abstract:
+
+ License Logging Service CLIENT rpc stub attribute configuration file.
+
+ This file contains the attribute configuration information necessary
+ for generating the client stubs for remotable LLS functions.
+
+ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ !! !!
+ !! This .acf file is USED ONLY WHEN GENERATING LLS CLIENT STUBS. !!
+ !! !!
+ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+
+ Use llssrv.acf when generating server stubs.
+
+
+Author:
+
+ Arthur Hanson (arth) Jan 30, 1995
+
+Environment:
+
+ User Mode
+
+Revision History:
+
+--*/
+
+[
+ implicit_handle(handle_t llsdbgrpc_handle)
+]
+
+interface llsdbgrpc
+
+{
+
+}
diff --git a/private/net/svcdlls/lls/lsdbgsrv.acf b/private/net/svcdlls/lls/lsdbgsrv.acf
new file mode 100644
index 000000000..4c3d4120d
--- /dev/null
+++ b/private/net/svcdlls/lls/lsdbgsrv.acf
@@ -0,0 +1,49 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ Llsdbgsrv.acf
+
+Abstract:
+
+ License Logging Service SERVER rpc stub attribute configuration file.
+
+ This file contains the attribute configuration information necessary
+ for generating the server stubs for remotable LLS functions.
+
+ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ !! !!
+ !! This .acf file is USED ONLY WHEN GENERATING LLS SERVER STUBS. !!
+ !! !!
+ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+
+ Use Llscli.acf when generating client stubs.
+
+
+Author:
+
+ Arthur Hanson (arth) Jan 30, 1995
+
+Environment:
+
+ User Mode
+
+Revision History:
+
+--*/
+
+[
+ implicit_handle(handle_t llsdbgrpc_handle)
+]
+
+interface llsdbgrpc
+
+{
+
+typedef [allocate(dont_free)] PNAMEW;
+typedef [allocate(dont_free)] PNAMEA;
+
+}
diff --git a/private/net/svcdlls/lls/makefil0 b/private/net/svcdlls/lls/makefil0
new file mode 100644
index 000000000..589468402
--- /dev/null
+++ b/private/net/svcdlls/lls/makefil0
@@ -0,0 +1,151 @@
+#
+# This is the MIDL compile phase of the build process.
+#
+# The following is where you put the name of your .idl file without
+# the .idl extension:
+
+!INCLUDE $(NTMAKEENV)\makefile.plt
+
+#####################################################################
+#
+# For the UI RPC functions
+#
+IDL_NAME = llsrpc
+CLIENT_ACF = llscli.acf
+SERVER_ACF = llssrv.acf
+
+CLIENT_INC_FILE = $(IDL_NAME)_c.h
+SERVER_INC_FILE = $(IDL_NAME)_s.h
+
+#####################################################################
+#
+# For the NtLSApi functions
+#
+LSAPI_IDL_NAME = lsapi
+LSAPI_CLIENT_ACF = lsapicli.acf
+LSAPI_SERVER_ACF = lsapisrv.acf
+
+LSAPI_CLIENT_INC_FILE = $(LSAPI_IDL_NAME)_c.h
+LSAPI_SERVER_INC_FILE = $(LSAPI_IDL_NAME)_s.h
+
+#####################################################################
+#
+# For the Debugging RPC functions
+#
+DEBUG_IDL_NAME = llsdbg
+DEBUG_CLIENT_ACF = lsdbgcli.acf
+DEBUG_SERVER_ACF = lsdbgsrv.acf
+
+DEBUG_CLIENT_INC_FILE = $(DEBUG_IDL_NAME)_c.h
+DEBUG_SERVER_INC_FILE = $(DEBUG_IDL_NAME)_s.h
+
+#####################################################################
+#
+# Common INC files
+#
+SDKINC = $(BASEDIR)\public\sdk\inc
+SDKCRTINC = $(BASEDIR)\public\sdk\inc\crt
+LLSINC = .\inc
+
+INCS = -I$(SDKINC) -I$(SDKCRTINC) -I$(LLSINC)
+
+EXTRN_DEPENDS =
+
+CLIENT_FLAGS = -Oi -oldnames -error allocation -error ref -c_ext -ms_ext $(MIDL_FLAGS) -acf $(CLIENT_ACF) -header $(CLIENT_INC_FILE)
+SERVER_FLAGS = -oldnames -error allocation -error ref -c_ext -ms_ext $(MIDL_FLAGS) -acf $(SERVER_ACF) -header $(SERVER_INC_FILE)
+
+LSAPI_CLIENT_FLAGS = -Oi -oldnames -error allocation -error ref -c_ext -ms_ext $(MIDL_FLAGS) -acf $(LSAPI_CLIENT_ACF) -header $(LSAPI_CLIENT_INC_FILE)
+LSAPI_SERVER_FLAGS = -oldnames -error allocation -error ref -c_ext -ms_ext $(MIDL_FLAGS) -acf $(LSAPI_SERVER_ACF) -header $(LSAPI_SERVER_INC_FILE)
+
+DEBUG_CLIENT_FLAGS = -Oi -oldnames -error allocation -error ref -c_ext -ms_ext $(MIDL_FLAGS) -acf $(DEBUG_CLIENT_ACF) -header $(DEBUG_CLIENT_INC_FILE)
+DEBUG_SERVER_FLAGS = -oldnames -error allocation -error ref -c_ext -ms_ext $(MIDL_FLAGS) -acf $(DEBUG_SERVER_ACF) -header $(DEBUG_SERVER_INC_FILE)
+
+CPP = -cpp_cmd "$(MIDL_CPP)"
+
+#
+# Separate client and server targets. Note that the .h file produced
+# when MIDL is run with the client .acf file attached differs from the
+# .h file produced when MIDL is run with the server .acf file attached.
+#
+
+CLIENT_TARGETS = client\$(IDL_NAME)_c.c \
+ client\$(CLIENT_INC_FILE)
+
+SERVER_TARGETS = server\$(IDL_NAME)_s.c \
+ server\$(SERVER_INC_FILE)
+
+LSAPI_CLIENT_TARGETS = \
+ ntlsapi\$(LSAPI_IDL_NAME)_c.c \
+ ntlsapi\$(LSAPI_CLIENT_INC_FILE)
+
+LSAPI_SERVER_TARGETS = \
+ server\$(LSAPI_IDL_NAME)_s.c \
+ server\$(LSAPI_SERVER_INC_FILE)
+
+DEBUG_CLIENT_TARGETS = \
+ test\common\$(DEBUG_IDL_NAME)_c.c \
+ test\common\$(DEBUG_CLIENT_INC_FILE)
+
+DEBUG_SERVER_TARGETS = \
+ server\$(DEBUG_IDL_NAME)_s.c \
+ server\$(DEBUG_SERVER_INC_FILE)
+
+TARGETS = $(CLIENT_TARGETS) \
+ $(SERVER_TARGETS) \
+ $(LSAPI_CLIENT_TARGETS) \
+ $(LSAPI_SERVER_TARGETS) \
+ $(DEBUG_CLIENT_TARGETS) \
+ $(DEBUG_SERVER_TARGETS)
+
+
+CLIENT_EXTRN_DEPENDS = $(CLIENT_ACF) $(LSAPI_CLIENT_ACF) $(DEBUG_CLIENT_ACF)
+SERVER_EXTRN_DEPENDS = $(SERVER_ACF) $(LSAPI_SERVER_ACF) $(DEBUG_SERVER_ACF)
+EXTRN_DEPENDS = $(CLIENT_EXTRN_DEPENDS)
+
+#
+# Define Products and Dependencies
+#
+
+all: $(CLIENT_TARGETS) $(SERVER_TARGETS) $(LSAPI_CLIENT_TARGETS) $(LSAPI_SERVER_TARGETS) $(DEBUG_CLIENT_TARGETS) $(DEBUG_SERVER_TARGETS)
+!IF "$(BUILDMSG)" != ""
+ @ech ; $(BUILDMSG) ;
+!ENDIF
+
+clean: delete_source all
+
+delete_source:
+ -erase $(TARGETS)
+
+#
+# MIDL COMPILE
+#
+
+$(CLIENT_TARGETS) : $(IDL_NAME).idl $(CLIENT_EXTRN_DEPENDS)
+ midl $(CPP) $(CLIENT_FLAGS) $(IDL_NAME).idl $(INCS)
+ IF EXIST $(IDL_NAME)_c.c copy $(IDL_NAME)_c.c .\client & del $(IDL_NAME)_c.c
+ IF EXIST $(CLIENT_INC_FILE) copy $(CLIENT_INC_FILE) .\client & del $(CLIENT_INC_FILE)
+
+$(SERVER_TARGETS) : $(IDL_NAME).idl $(SERVER_EXTRN_DEPENDS)
+ midl $(CPP) $(SERVER_FLAGS) $(IDL_NAME).idl $(INCS)
+ IF EXIST $(IDL_NAME)_s.c copy $(IDL_NAME)_s.c .\server & del $(IDL_NAME)_s.c
+ IF EXIST $(SERVER_INC_FILE) copy $(SERVER_INC_FILE) .\server & del $(SERVER_INC_FILE)
+
+$(LSAPI_CLIENT_TARGETS) : $(LSAPI_IDL_NAME).idl $(CLIENT_EXTRN_DEPENDS)
+ midl $(CPP) $(LSAPI_CLIENT_FLAGS) $(LSAPI_IDL_NAME).idl $(INCS)
+ IF EXIST $(LSAPI_IDL_NAME)_c.c copy $(LSAPI_IDL_NAME)_c.c .\ntlsapi & del $(LSAPI_IDL_NAME)_c.c
+ IF EXIST $(LSAPI_CLIENT_INC_FILE) copy $(LSAPI_CLIENT_INC_FILE) .\ntlsapi & del $(LSAPI_CLIENT_INC_FILE)
+
+$(LSAPI_SERVER_TARGETS) : $(LSAPI_IDL_NAME).idl $(SERVER_EXTRN_DEPENDS)
+ midl $(CPP) $(LSAPI_SERVER_FLAGS) $(LSAPI_IDL_NAME).idl $(INCS)
+ IF EXIST $(LSAPI_IDL_NAME)_s.c copy $(LSAPI_IDL_NAME)_s.c .\server & del $(LSAPI_IDL_NAME)_s.c
+ IF EXIST $(LSAPI_SERVER_INC_FILE) copy $(LSAPI_SERVER_INC_FILE) .\server & del $(LSAPI_SERVER_INC_FILE)
+
+$(DEBUG_CLIENT_TARGETS) : $(DEBUG_IDL_NAME).idl $(CLIENT_EXTRN_DEPENDS)
+ midl $(CPP) $(DEBUG_CLIENT_FLAGS) $(DEBUG_IDL_NAME).idl $(INCS)
+ IF EXIST $(DEBUG_IDL_NAME)_c.c copy $(DEBUG_IDL_NAME)_c.c .\test\common & del $(DEBUG_IDL_NAME)_c.c
+ IF EXIST $(DEBUG_CLIENT_INC_FILE) copy $(DEBUG_CLIENT_INC_FILE) .\test\common & del $(DEBUG_CLIENT_INC_FILE)
+
+$(DEBUG_SERVER_TARGETS) : $(DEBUG_IDL_NAME).idl $(SERVER_EXTRN_DEPENDS)
+ midl $(CPP) $(DEBUG_SERVER_FLAGS) $(DEBUG_IDL_NAME).idl $(INCS)
+ IF EXIST $(DEBUG_IDL_NAME)_s.c copy $(DEBUG_IDL_NAME)_s.c .\server & del $(DEBUG_IDL_NAME)_s.c
+ IF EXIST $(DEBUG_SERVER_INC_FILE) copy $(DEBUG_SERVER_INC_FILE) .\server & del $(DEBUG_SERVER_INC_FILE)
diff --git a/private/net/svcdlls/lls/ntlsapi/main.c b/private/net/svcdlls/lls/ntlsapi/main.c
new file mode 100644
index 000000000..87699f7e4
--- /dev/null
+++ b/private/net/svcdlls/lls/ntlsapi/main.c
@@ -0,0 +1,57 @@
+/*++ BUILD Version: 0001 // Increment this if a change has global effects
+
+Copyright (c) 1994 Microsoft Corporation
+
+Module Name:
+
+ main.c
+
+Created:
+
+ 20-Apr-1994
+
+Revision History:
+
+--*/
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+
+#include <windows.h>
+#include <malloc.h>
+
+#include <lpcstub.h>
+
+//
+// DLL Startup code
+//
+BOOL WINAPI DllMain(
+ HANDLE hDll,
+ DWORD dwReason,
+ LPVOID lpReserved)
+{
+ switch(dwReason)
+ {
+ case DLL_PROCESS_ATTACH:
+ LLSInitLPC();
+ break;
+
+ case DLL_PROCESS_DETACH:
+ LLSCloseLPC();
+ break;
+
+ case DLL_THREAD_ATTACH:
+ break;
+
+ case DLL_THREAD_DETACH:
+ break;
+
+ default:
+ break;
+
+ } // end switch()
+
+ return TRUE;
+
+} // DllMain
diff --git a/private/net/svcdlls/lls/ntlsapi/makefile b/private/net/svcdlls/lls/ntlsapi/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/net/svcdlls/lls/ntlsapi/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/net/svcdlls/lls/ntlsapi/ntlsapi.c b/private/net/svcdlls/lls/ntlsapi/ntlsapi.c
new file mode 100644
index 000000000..2f0ae38f1
--- /dev/null
+++ b/private/net/svcdlls/lls/ntlsapi/ntlsapi.c
@@ -0,0 +1,1150 @@
+/*++ BUILD Version: 0001 // Increment this if a change has global effects
+
+Copyright (c) 1994 Microsoft Corporation
+
+Module Name:
+
+ lsapi.c
+
+Created:
+
+ 20-Apr-1994
+
+Revision History:
+
+ 01-Nov-1994 arth Changed from LS API set to simpler request only
+ API.
+--*/
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windows.h>
+
+#include <ntlsapi.h>
+#include <llsconst.h>
+#include <debug.h>
+#include <stdlib.h>
+
+#include <lpcstub.h>
+
+// #define API_TRACE 1
+// #define TIME_TRACE 1
+
+
+#ifdef TIME_TRACE
+ DWORD TimeDelta;
+#endif
+
+
+//
+// SID is linear in memory (check if this is safe assumption). Can use
+// RtlCopySID then pass linear buffer - use RtlLengthSid for size.
+//
+
+/*++
+ NtLicenseRequest()
+ Request licensing resources needed to allow software to be
+ used.
+
+ Format
+
+ Status = NtLicenseRequest(
+ [in] LS_STR *ProductName,
+ [in] LS_STR *Version,
+ [in] NT_LS_DATA *NtData)
+ );
+
+ Arguments
+
+ ProductName The name of the product requesting licensing
+ resources. This string may not be null and must
+ be unique (in the first 32 characters) within the
+ PublisherName domain.
+
+ Version The version number of this product. This string
+ must be unique (in the first 12 characters) within
+ the ProductName domain, and cannot be NULL
+
+ NtData The username and/or SID identifying the person using the
+ license.
+
+ NOTE: The arguments ProductName, and Version may not be
+ NULL.
+
+
+ Description
+
+ This function is used by the application to request licensing
+ resources to allow the identified product to execute. If a
+ valid license is found, the challenge response is computed and
+ LS_SUCCESS is returned.
+
+--*/
+
+LS_STATUS_CODE LS_API_ENTRY NtLicenseRequestA(
+ LPSTR ProductName,
+ LPSTR Version,
+ LS_HANDLE FAR *LicenseHandle,
+ NT_LS_DATA *NtData)
+{
+ WCHAR uProductName[MAX_PRODUCT_NAME_LENGTH + 1];
+ WCHAR uVersion[MAX_VERSION_LENGTH + 1];
+ WCHAR uUserName[MAX_USER_NAME_LENGTH + 1];
+ void *tmpData = NULL;
+ LS_STATUS_CODE ret = LS_SUCCESS;
+
+#ifdef API_TRACE
+ dprintf(TEXT("NtLicenseRequestA!!!\r\n"));
+
+#endif
+
+ // Convert parms to Unicode and call Unicode function
+
+ // First make sure we have correct data
+ if ( (ProductName != NULL) && (Version != NULL) && (NtData != NULL) && (NtData->Data != NULL)) {
+ if (lstrlenA(ProductName) > MAX_PRODUCT_NAME_LENGTH) {
+#ifdef API_TRACE
+ dprintf(TEXT(" Error: ProductName too long\r\n"));
+#endif
+ lstrcpy(uProductName, TEXT(""));
+ } else
+ MultiByteToWideChar(CP_ACP, 0, ProductName, -1, uProductName, MAX_PRODUCT_NAME_LENGTH + 1);
+
+ if (lstrlenA(Version) > MAX_VERSION_LENGTH) {
+#ifdef API_TRACE
+ dprintf(TEXT(" Error: Version too long\r\n"));
+#endif
+ lstrcpy(uVersion, TEXT(""));
+ } else
+ MultiByteToWideChar(CP_ACP, 0, Version, -1, uVersion, MAX_VERSION_LENGTH + 1);
+
+ if (NtData->DataType == NT_LS_USER_NAME) {
+ if (lstrlenA((LPSTR) NtData->Data) > MAX_USER_NAME_LENGTH) {
+#ifdef API_TRACE
+ dprintf(TEXT(" Error: UserName too long\r\n"));
+#endif
+ lstrcpy(uUserName, TEXT(""));
+ } else
+ MultiByteToWideChar(CP_ACP, 0, NtData->Data, -1, uUserName, MAX_USER_NAME_LENGTH + 1);
+
+ // Have UserName convert to wide char format, but need to point
+ // Data structure to it...
+ tmpData = (void *) NtData->Data;
+ NtData->Data = (void *) uUserName;
+ ret = NtLicenseRequestW(uProductName, uVersion, LicenseHandle, NtData);
+
+ // Nothing needs to be converted back to ANSI on return, just return
+ // data structure to the way it was
+ NtData->Data = tmpData;
+ return ret;
+ } else {
+ // Gave SID so no Unicode conversion needed on name
+ ret = NtLicenseRequestW(uProductName, uVersion, LicenseHandle, NtData);
+ return ret;
+ }
+
+ }
+#ifdef API_TRACE
+ else
+ dprintf(TEXT(" LLS Error: <NULL> Parms passed in!\r\n"));
+#endif
+
+ // If NULL parms or such then just return a dummy handle for right now
+ if ( LicenseHandle != NULL )
+ *LicenseHandle = 0xffffffff;
+
+ return(LS_SUCCESS);
+} // NtLicenseRequestA
+
+
+LS_STATUS_CODE LS_API_ENTRY NtLicenseRequestW(
+ LPWSTR ProductName,
+ LPWSTR Version,
+ LS_HANDLE FAR *LicenseHandle,
+ NT_LS_DATA *NtData)
+{
+ LPWSTR dVersion = Version;
+ LS_STATUS_CODE Status;
+#ifdef API_TRACE
+ UNICODE_STRING UString;
+ NTSTATUS NtStatus;
+#endif
+
+ //
+ // Check parms before calling down
+ //
+ if ((ProductName == NULL) || (NtData == NULL) || (NtData->DataType > NT_LS_USER_SID)) {
+
+#ifdef API_TRACE
+ dprintf(TEXT("NtLicenseRequestW: <Bad Parms>\r\n"));
+#endif
+
+ if (LicenseHandle != NULL)
+ *LicenseHandle = 0xffffffffL;
+
+ return(LS_SUCCESS);
+ }
+
+ //
+ // LsaLogonUser passes in NULL version because it doesn't know what version
+ // the calling app is. So just use a blank field for this. There must
+ // be something in the version field or it messes up the lower level
+ // algorithms, so just enter a blank.
+ //
+ if ((Version == NULL) || (*Version == TEXT('\0')))
+ dVersion = TEXT("");
+
+#ifdef API_TRACE
+ if (NtData->DataType == NT_LS_USER_SID) {
+ NtStatus = RtlConvertSidToUnicodeString(&UString, (PSID) NtData->Data, TRUE);
+
+ if (NtStatus != STATUS_SUCCESS)
+ dprintf(TEXT("NtLicenseRequestW RtlConvertSidToUnicodeString: 0x%lx\n"), NtStatus);
+ else {
+ if (NtData->IsAdmin)
+ dprintf(TEXT("NtLicenseRequestW: %s, %s, <ADMIN>, SID: %s\n"), ProductName, dVersion, UString.Buffer);
+ else
+ dprintf(TEXT("NtLicenseRequestW: %s, %s, SID: %s\n"), ProductName, dVersion, UString.Buffer);
+
+ RtlFreeUnicodeString(&UString);
+ }
+ } else {
+
+ if (NtData->IsAdmin)
+ dprintf(TEXT("NtLicenseRequestW: %s, %s, <ADMIN>, %s\n"), ProductName, dVersion, NtData->Data);
+ else
+ dprintf(TEXT("NtLicenseRequestW: %s, %s, %s\n"), ProductName, dVersion, NtData->Data);
+
+ }
+
+#endif
+
+#ifdef TIME_TRACE
+ TimeDelta = GetTickCount();
+#endif
+
+ // make the LPC call and marshal the parms.
+ Status = (LS_STATUS_CODE) LLSLicenseRequest( ProductName,
+ dVersion,
+ NtData->DataType,
+ (BOOLEAN) NtData->IsAdmin,
+ NtData->Data,
+ LicenseHandle
+ );
+
+#ifdef TIME_TRACE
+ TimeDelta = GetTickCount() - TimeDelta;
+ dprintf(TEXT("NtLicenseRequest LPC Call Time: %ldms\n"), TimeDelta);
+#endif
+
+ return(Status);
+} // NtLicenseRequestW
+
+
+/*
+ NtLSFreeHandle ( )
+
+ Frees all licensing handle context.
+
+ Format
+
+ void NtLSFreeHandle ( [in] LS_HANDLE LicenseHandle);
+
+ Arguments
+
+ LicenseHandle Handle identifying the license context. This
+ argument must be a handle that was created with
+ NtLSRequest() or NtLicenseRequest().
+
+ Description
+
+ (NOTE: The handle is no longer valid.)
+
+*/
+
+LS_STATUS_CODE LS_API_ENTRY NtLSFreeHandle(
+ LS_HANDLE LicenseHandle )
+{
+
+#ifdef API_TRACE
+ dprintf(TEXT("NtLSFreeHandle: %ld\r\n"), LicenseHandle);
+#endif
+
+ //
+ // If we get an invalid License Handle (or a dummy 0xFFFFFFFF one)
+ // then don't even bother calling through LPC as it is a waste
+ // of time.
+ //
+ if (LicenseHandle == (ULONG) 0xFFFFFFFFL)
+ return( LS_SUCCESS );
+
+ //
+ // Make the LPC call
+ //
+ LLSLicenseFree( LicenseHandle );
+
+ return( LS_SUCCESS );
+} // NtLSFreeHandle
+
+
+// **************************************************************************
+// OLD API's Don't Use
+// **************************************************************************
+
+/*
+ LSRequest()
+ Request licensing resources needed to allow software to be
+ used.
+
+ Format
+
+ Status = LSRequest( [in] LicenseSystem, [in] PublisherName,
+ [in] ProductName,
+ [in] Version, [in] TotUnitsReserved, [in]
+ LogComment,
+ [in/out] Challenge, [out] TotUnitsGranted, [out]
+ hLicenseHandle );
+
+ LS_STR * LicenseSystem;
+
+ LS_STR * PublisherName;
+
+ LS_STR * ProductName;
+
+ LS_STR * Version;
+
+ LS_ULONG TotUnitsReserved;
+
+ LS_STR * LogComment;
+
+ LS_CHALLENGE *Challenge;
+
+ LS_ULONG * TotUnitsGranted;
+
+ LS_HANDLE * hLicenseHandle;
+
+ LS_STATUS_CODEStatus;
+
+ Arguments
+
+ LicenseSystem Pointer to a string which uniquely identifies
+ the particular license system. This may be
+ obtained through the LSEnumProviders() API.
+ Normally, the constant LS_ANY is specified to
+ indicate a match against all installed license
+ systems (indicates that all license providers
+ should be searched for a license match).
+
+ PublisherName The name of the publisher (manufacturer) of
+ this product. This string may not be null and
+ must be unique in the first 32 characters. It is
+ recommended that a company name and trademark be
+ used.
+
+ ProductName The name of the product requesting licensing
+ resources. This string may not be null and must
+ be unique (in the first 32 characters) within the
+ PublisherName domain.
+
+ Version The version number of this product. This string
+ must be unique (in the first 12 characters) within
+ the ProductName domain, and cannot be NULL
+
+ NOTE: The arguments PublisherName, ProductName,
+ and Version may not be NULL, or may not be
+ LS_ANY.
+
+ TotUnitsReserved Specifies the number of units required to run
+ the application. The software publisher may
+ choose to specify this policy attribute within the
+ application. The recommended value of
+ LS_DEFAULT_UNITS allows the licensing system to
+ determine the proper value using information
+ provided by the license system or license itself.
+ The license system verifies that the requested
+ number of units exist and may reserve those units,
+ but no units are actually consumed at this time.
+ The number of units available is returned in
+ TotUnitsGranted.
+
+ LogComment An optional string indicating a comment to be
+ associated with the request and logged (if logging
+ is enabled and supported) by the underlying
+ licensing system. The underlying license system
+ may choose to log the comment even if an error is
+ returned (i.e., logged with the error), but this
+ is not guaranteed. If a string is not specified,
+ the value must be LS_NULL.
+
+ Challenge Pointer to a challenge structure. The challenge
+ response will also be returned in this structure.
+ Refer to Challenge Mechanism on page 25 for more
+ information.
+
+ TotUnitsGrantedA pointer to an LS_ULONG in which the total
+ number of units granted is returned. The following
+ table describes the TotUnitsGranted return value,
+ given the TotUnitsReserved input value, and the
+ Status returned:
+
+ LS_INSUFFICIENT_U
+ TotUnitsReserv LS_SUCCES NITS Other
+ ed S errors
+ LS_DEFAULT_UNI (A) (B) (E)
+ TS
+ Other (C) (D) (E)
+ (specific
+ count)
+ (A) The default umber of units commensurate
+ with the license granted.(B) The maximum
+ number of units available to the requesting
+ software. This may be less than the normal
+ default.
+ (C) The number of units used to grant the
+ request. Note that this value may be greater
+ than or equal to the actual units requested
+ (i.e., the policy may allow only in increments
+ of 5 units, thus a request of 7 units would
+ result in 10 units being granted).
+ (D) The maximum number of units available to
+ the requesting software. This may be more or
+ less than the units requested.
+ (E) Zero is returned.
+
+ LicenseHandle Pointer to a LS_HANDLE in which a handle to the
+ license context is to be returned.
+
+ Status Detailed error code that can be directly processed
+ by the caller, or that can be converted into a
+ localized message string by the LSGetMessage()
+ function.
+
+ Description
+
+ This function is used by the application to request licensing
+ resources to allow the identified product to execute. If a
+ valid license is found, the challenge response is computed and
+ LS_SUCCESS is returned. At minimum, the PublisherName,
+ ProductName, and Version strings are used to identify matching
+ license(s). Note that an underlying license system service
+ provider may ascertain additional information for the license
+ request (e.g., the current username, machine name, etc.).
+
+ A valid license handle is always returned by this function
+ whether valid license resources are granted or not. This
+ handle must always be released with LSFreeHandle() when the
+ application has completed execution.
+
+ If license resources were granted, it must call LSRelease() to
+ free the license resource, prior to calling LSFreeHandle().
+
+ A challenge response is NOT returned unless the license
+ request completed successfully (i.e., a status code of
+ LS_SUCCESS is returned).
+
+ If the number of units requested is greater than the number of
+ units available, then the license request is not granted. Upon
+ successful completion, the value returned in TotUnitsReserved
+ indicates the number of units granted. This is greater than or
+ equal to the number of units requested unless LS_DEFAULT_UNITS
+ was specified. In the case of failure, the value returned in
+ TotUnitsGranted is zero.
+
+*/
+
+LS_STATUS_CODE LS_API_ENTRY NtLSRequest(
+ LS_STR FAR *LicenseSystem,
+ LS_STR FAR *PublisherName,
+ LS_STR FAR *ProductName,
+ LS_STR FAR *Version,
+ LS_ULONG TotUnitsReserved,
+ LS_STR FAR *LogComment,
+ LS_CHALLENGE FAR *Challenge,
+ LS_ULONG FAR *TotUnitsGranted,
+ LS_HANDLE FAR *LicenseHandle,
+ NT_LS_DATA FAR *NtData)
+{
+ NT_LS_DATA tmpNtData;
+ WCHAR uProductName[MAX_PRODUCT_NAME_LENGTH + 1];
+ WCHAR uVersion[MAX_VERSION_LENGTH + 1];
+ WCHAR uUserName[MAX_USER_NAME_LENGTH + 1];
+ LS_STATUS_CODE ret = LS_SUCCESS;
+
+#ifdef API_TRACE
+ dprintf(TEXT("NtLSRequest:\r\n"));
+
+ if (ProductName == NULL)
+ dprintf(TEXT(" Product Name: <NULL>\r\n"));
+
+ if (Version == NULL)
+ dprintf(TEXT(" Version: <NULL>\r\n"));
+
+ if (LicenseHandle == NULL)
+ dprintf(TEXT(" LicenseHandle: <NULL>\r\n"));
+
+ if (NtData != NULL) {
+ if (NtData->Data == NULL)
+ dprintf(TEXT(" NtData->Data: <NULL>\r\n"));
+ } else
+ dprintf(TEXT("NtData: <NULL>\r\n"));
+
+ dprintf(TEXT("\r\n"));
+
+#endif
+
+ // Do some fudging to follow old API spec...
+ if ( TotUnitsGranted != NULL )
+ *TotUnitsGranted = TotUnitsReserved;
+
+ // Need to do a couple things:
+ // 1. Convert used parms to Unicode
+ // 2. Set up new NtData structure (extra IsAdmin field)
+ // 3. Make call to new NtLicenseRequest API and use it's return code.
+ //
+ // Note: No conversion back to ANSI needed upon return from API
+ //
+
+ // First make sure we have correct data
+ if ( (ProductName != NULL) && (Version != NULL) && (NtData != NULL) && (NtData->Data != NULL)) {
+
+ // 1. Convert parms to Unicode
+ if (lstrlenA(ProductName) > MAX_PRODUCT_NAME_LENGTH) {
+#ifdef API_TRACE
+ dprintf(TEXT(" Error: ProductName too long\r\n"));
+#endif
+ MultiByteToWideChar(CP_ACP, 0, ProductName, MAX_PRODUCT_NAME_LENGTH, uProductName, MAX_PRODUCT_NAME_LENGTH + 1);
+ uProductName[MAX_PRODUCT_NAME_LENGTH] = TEXT('\0');
+ } else
+ MultiByteToWideChar(CP_ACP, 0, ProductName, -1, uProductName, MAX_PRODUCT_NAME_LENGTH + 1);
+
+ if (lstrlenA(Version) > MAX_VERSION_LENGTH) {
+#ifdef API_TRACE
+ dprintf(TEXT(" Error: Version too long\r\n"));
+#endif
+ MultiByteToWideChar(CP_ACP, 0, Version, MAX_VERSION_LENGTH, uVersion, MAX_VERSION_LENGTH + 1);
+ uVersion[MAX_VERSION_LENGTH] = TEXT('\0');
+ } else
+ MultiByteToWideChar(CP_ACP, 0, Version, -1, uVersion, MAX_VERSION_LENGTH + 1);
+
+ // 2. Set up new NtData structure
+ tmpNtData.DataType = NtData->DataType;
+
+ // just use FALSE for IsAdmin as none of the old Apps need it.
+ tmpNtData.IsAdmin = FALSE;
+
+ if (NtData->DataType == NT_LS_USER_NAME) {
+ if (lstrlenA((LPSTR) NtData->Data) > MAX_USER_NAME_LENGTH) {
+#ifdef API_TRACE
+ dprintf(TEXT(" Error: UserName too long\r\n"));
+#endif
+ MultiByteToWideChar(CP_ACP, 0, NtData->Data, MAX_USER_NAME_LENGTH, uUserName, MAX_USER_NAME_LENGTH + 1);
+ uUserName[MAX_USER_NAME_LENGTH] = TEXT('\0');
+ } else {
+ MultiByteToWideChar(CP_ACP, 0, NtData->Data, -1, uUserName, MAX_USER_NAME_LENGTH + 1);
+ }
+
+ tmpNtData.Data = (void *) uUserName;
+
+ // Have UserName convert to wide char format, but need to point
+ // Data structure to it...
+ ret = NtLicenseRequestW(uProductName, uVersion, LicenseHandle, &tmpNtData);
+
+ // Nothing needs to be converted back to ANSI on return, just return
+ return ret;
+ } else {
+ // Gave SID so no Unicode convesion needed on name
+ tmpNtData.Data = NtData->Data;
+ ret = NtLicenseRequestW(uProductName, uVersion, LicenseHandle, &tmpNtData);
+ return ret;
+ }
+
+ }
+
+ // If NULL parms or such then just return a dummy handle for right now
+ if ( LicenseHandle != NULL )
+ *LicenseHandle = 0xffffffffL;
+
+ return(LS_SUCCESS);
+} // NtLSRequest
+
+/*
+ LSRelease()
+ Release licensing resources associated with the specified
+ context.
+
+ Format
+
+ Status = LSRelease( [in] LicenseHandle, [in] TotUnitsConsumed,
+ [in] LogComment );
+
+ LS_HANDLE LicenseHandle;
+
+ LS_ULONG TotUnitsConsumed;
+
+ LS_STR * LogComment;
+
+ LS_STATUS_CODEStatus;
+
+ Arguments
+
+ LicenseHandle Handle identifying the license context. This
+ argument must be a handle that was created with
+ LSRequest().
+
+ TotUnitsConsumedThe TOTAL number of units consumed in this
+ handle context since the initial LSRequest() call.
+ The software publisher may choose to specify this
+ policy attribute within the application. A value
+ of LS_DEFAULT_UNITS indicates that the licensing
+ system should determine the appropriate value
+ using its own licensing policy mechanisms.
+
+ LogComment An optional string indicating a comment to be
+ associated with the request and logged (if logging
+ is enabled and supported) by the underlying
+ licensing system. The underlying license system
+ may choose to log the comment even if an error is
+ returned (i.e., logged with the error), but this
+ is not guaranteed. If a string is not specified,
+ the value must be LS_NULL.
+
+ Status Detailed error code that can be directly processed
+ by the caller, or that can be converted into a
+ localized message string by the LSGetMessage()
+ function.
+
+ Description
+
+ This function is used to release licensing resources
+ associated with the license context identified by
+ LicenseHandle. If a consumptive style licensing policy is in
+ effect, and if the software publisher chooses to implement
+ such license policy in the application, then the license units
+ to be consumed may be passed as part of this call.
+
+ NOTE: The license handle context is NOT freed. See
+ LSFreeHandle().
+
+*/
+
+LS_STATUS_CODE LS_API_ENTRY NtLSRelease(
+ LS_HANDLE LicenseHandle,
+ LS_ULONG TotUnitsConsumed,
+ LS_STR FAR *LogComment)
+{
+ return(LS_SUCCESS);
+}
+
+/*
+ LSUpdate()
+ Update the synchronization between licensed software and the
+ licensing system.
+
+ Format
+
+ Status = LSUpdate( [in] LicenseHandle, [in] TotUnitsConsumed,
+ [in] TotUnitsReserved,
+ [in] LogComment, [in/out] Challenge, [out]
+ TotUnitsGranted );
+
+ LS_HANDLE LicenseHandle;
+
+ LS_ULONG TotUnitsConsumed;
+
+ LS_ULONG TotUnitsReserved;
+
+ LS_STR * LogComment;
+
+ LS_CHALLENGE *Challenge;
+
+ LS_ULONG * TotUnitsGranted;
+
+ LS_STATUS_CODEStatus;
+
+ Arguments
+
+ LicenseHandle Handle identifying the license context. This
+ argument must be a handle that was created with
+ LSRequest().
+
+ TotUnitsConsumedThe TOTAL number of units consumed so far in
+ this handle context. The software publisher may
+ choose to specify this policy attribute within the
+ application. A value of LS_DEFAULT_UNITS
+ indicates that the licensing system should
+ determine the appropriate value using its own
+ licensing policy mechanisms. If an error is
+ returned, then no units are consumed.
+
+ TotUnitsReserved Specifies the total number of units to be
+ reserved. If no additional units are required
+ since the initial LSRequest() or last LSUpdate(),
+ then this parameter should be the current total
+ (as returned in TotUnitsGranted). The total
+ reserved is inclusive of units consumed. That is,
+ if an application requests 100 units be reserved,
+ then consumes 20 units, there are still 100 units
+ reserved (but only 80 available for consumption).
+
+ If additional units are required, the application
+ must calculate a new total for TotUnitsReserved.
+ LS_DEFAULT_UNITS may be specified, but this will
+ not allocate any additional units.
+
+ The license system verifies that the requested
+ number of units exist and may reserve those units,
+ but these units are not consumed at this time.
+ This value may be smaller than the original
+ request to indicate that fewer units are needed
+ than originally anticipated.
+
+ LogComment An optional string indicating a comment to be
+ associated with the request and logged (if logging
+ is enabled and supported) by the underlying
+ licensing system. The underlying license system
+ may choose to log the comment even if an error is
+ returned (i.e., logged with the error), but this
+ is not guaranteed. If a string is not specified,
+ the value must be LS_NULL.
+
+ Challenge Pointer to a challenge structure. The challenge
+ response will also be returned in this structure.
+ Refer to Challenge Mechanism on page 25 for more
+ information.
+
+ TotUnitsGrantedA pointer to an LS_ULONG in which the total
+ number of units granted since the initial license
+ request is returned. The following table describes
+ the TotUnitsGranted return value, given the
+ TotUnitsReserved input value, and the Status
+ returned:
+
+ LS_INSUFFICIENT_U
+ TotUnitsReserv LS_SUCCES NITS Other
+ ed S errors
+ LS_DEFAULT_UNI (A) (B) (E)
+ TS
+ Other (C) (D) (E)
+ (specific
+ count)
+ (A) The default umber of units commensurate
+ with the license granted. (B) The maximum
+ number of units available to the requesting
+ software. This may be less than the normal
+ default.
+ (C) The number of units used to grant the
+ request. Note that this value may differ from
+ the actual units requested (i.e., the policy
+ may allow only in increments of 5 units, thus a
+ request of 7 units would result in 10 units
+ being granted).
+ (D) The maximum number of units available to
+ the requesting software. This may be more or
+ less than the units requested.
+ (E) Zero is returned.
+
+ Status Detailed error code that can be directly processed
+ by the caller, or that can be converted into a
+ localized message string by the LSGetMessage()
+ function.
+
+ Description
+
+ The client application periodically issues this call to re-
+ verify that the current license is still valid. The LSQuery()
+ API may be used to determine the proper interval for the
+ current licensing context. A guideline of once an hour may be
+ appropriate, with a minimum interval of 15 minutes. Consult
+ your licensing system vendor for more information.
+
+ If the number of new units requested (in TotUnitsReserved) is
+ greater than the number available, then the update request
+ fails with an LS_INSUFFICIENT_UNITS error. Upon successful
+ completion, the value returned in TotUnitsGranted indicates
+ the current total of units granted.
+
+ If the TotUnitsConsumed exceeds the number of units reserved,
+ then the error LS_INSUFFICIENT_UNITS is returned. The
+ remaining units are consumed.
+
+ A challenge response is NOT returned if an error is returned.
+
+ The LSUpdate() call verifies that the licensing system context
+ has not changed from that expected by the licensed software.
+ In this way the LSUpdate() call can:
+
+ 1.Determine if the licensing system can verify that the
+ licensing resources granted to the specified handle are
+ still reserved for this application by the licensing system.
+ Note that in distributed license system, an error here might
+ indicate a temporary network interruption, among other
+ things.
+
+ 2.Determine when the licensing system has released the
+ licensing resources that had been granted to the specified
+ handle, indicating the software requiring that grant no
+ longer has authorization to execute normally.
+
+ Application Software should be prepared to handle vendor
+ specific error conditions, should they arise. However, a best
+ effort will be used by license systems to map error conditions
+ to the common error set.
+
+ The LSUpdate() call may indicate if that the current licensing
+ context has expired (for example, in the case of a time-
+ restricted license policy). In such a case, the warning status
+ LS_LICENSE_EXPIRED is returned. If any error is returned, a
+ call to LSRelease() is still required.
+*/
+
+LS_STATUS_CODE LS_API_ENTRY NtLSUpdate(
+ LS_HANDLE LicenseHandle,
+ LS_ULONG TotUnitsConsumed,
+ LS_ULONG TotUnitsReserved,
+ LS_STR FAR *LogComment,
+ LS_CHALLENGE FAR *Challenge,
+ LS_ULONG FAR *TotUnitsGranted)
+{
+ // set the return buffer to NULL
+ if ( TotUnitsGranted != NULL )
+ *TotUnitsGranted = TotUnitsReserved;
+
+ return(LS_SUCCESS);
+}
+
+/*
+ LSGetMessage()
+ Return the message associated with a License Service API
+ status code.
+
+ Format
+
+ Status = LSGetMessage( [in] LicenseHandle, [in] Value, [out]
+ Buffer, [in] BufferSize );
+
+ LS_HANDLE LicenseHandle;
+
+ LS_STATUS_CODEValue;
+
+ LS_STR * Buffer;
+
+ LS_ULONG BufferSize;
+
+ LS_STATUS_CODEStatus;
+
+ Arguments
+
+ LicenseHandle Handle identifying the license context. This
+ argument must be a handle that was created with
+ LSRequest().
+
+ Value Any status code returned by a License Service API
+ function.
+
+ Buffer Pointer to a buffer in which a localized error
+ message string is to be placed.
+
+ BufferSize Maximum size of the string that may be returned in
+ Buffer.
+
+ Status Resulting status of LSGetMessage() call.
+
+ Description
+
+ For a given error, this function returns an error code and a string
+ describing the error, and a suggested action to be taken in
+ response to the specific error. If the value of Value is
+ LS_USE_LAST, then the last error associated with the supplied
+ licensing handle, and its associated data, is returned. Otherwise,
+ the supplied error code is used.
+
+ Possible status codes returned by LSGetMessage() include:
+ LS_SUCCESS, LS_NO_MSG_TEXT, LS_UNKNOWN_STATUS, and
+ LS_BUFFER_TOO_SMALL.
+
+*/
+
+LS_STATUS_CODE LS_API_ENTRY NtLSGetMessage(
+ LS_HANDLE LicenseHandle,
+ LS_STATUS_CODE Value,
+ LS_STR FAR *Buffer,
+ LS_ULONG BufferSize)
+{
+ return(LS_TEXT_UNAVAILABLE);
+}
+
+/*
+ LSQuery()
+ Return information about the license system context associated
+ with the specified handle.
+
+ Format
+
+ Status = LSQuery( [in] LicenseHandle, [in] Information, [out]
+ InfoBuffer, [in] BufferSize,
+ [out] ActualBufferSize);
+
+ LS_HANDLE LicenseHandle;
+
+ LS_ULONG Information;
+
+ LS_VOID * InfoBuffer;
+
+ LS_ULONG BufferSize;
+
+ LS_ULONG * ActualBufferSize;
+
+ LS_STATUS_CODEStatus;
+
+ Arguments
+
+ LicenseHandle Handle identifying the license context. This
+ argument must be a handle that was created with
+ LSRequest().
+
+ Information Index which identifies the information to be
+ returned.
+
+ InfoBuffer Points to a buffer in which the resulting
+ information is to be placed.
+
+ BufferSize Maximum size of the buffer pointed to by
+ InfoBuffer.
+
+ ActualBufferSize On entry, points to a LS_ULONG whose value on
+ exit indicates the actual count of characters
+ returned in the buffer (not including the trailing
+ NULL byte).
+
+ Status Detailed error code that can be directly processed
+ by the caller, or which can be converted into a
+ localized message string by the LSGetMessage
+ function.
+
+ Description
+
+ This function is used to obtain information about the license
+ obtained from the LSRequest() call. For example, an application may
+ determine the license type (demo, concurrent, personal, etc.); time
+ restrictions; etc.
+
+ The buffer should be large enough to accommodate the expected data.
+ If the buffer is too small, then the status code
+ LS_BUFFER_TOO_SMALL is returned and only BufferSize bytes of data
+ are returned.
+
+ The following Information constants are defined:
+
+ Information Valu Meaning
+ Constant e
+ LS_INFO_NONE 0 Reserved.
+ LS_INFO_SYSTEM 1 Return the unique identification
+ of the license system supplying
+ the current license context.
+ This is returned as a null-
+ terminated string.
+
+ This value is the same as an
+ appropriate call to
+ LSEnumProviders() provides.
+
+ LS_INFO_DATA 2 Return the block of
+ miscellaneous application data
+ contained on the license. This
+ data is completely vendor-
+ defined. The amount of space
+ allocated for such data will
+ vary from license system to
+ license system, or may not be
+ available at all.
+
+ The first ULONG in the data
+ buffer indicates the size (in
+ bytes) of the actual data which
+ follows:
+
+ +------------------------------
+ --+
+ | ULONG
+ |
+ | (count of bytes that follow)
+ |
+ +------------------------------
+ --+
+ | Vendor data bytes from license
+ |
+ |
+ |
+ +------------------------------
+ --+
+
+ LS_UPDATE_PERIO 3 Return the recommended interval
+ D (in minutes) at which LSUpdate()
+ should be called.
+
+ +------------------------------
+ --+
+ | ULONG
+ |
+ | Recommended Interval
+ |
+ | (in minutes)
+ |
+ +------------------------------
+ --+
+ | ULONG
+ |
+ | Recommended Minutes until
+ |
+ | next LSUpdate()call
+ |
+ +------------------------------
+ --+
+
+ If a value of 0xFFFFFFFF is
+ returned for the recommended
+ interval, then no recommendation
+ is being made.
+
+ LS_LICENSE_CONT 4 Return a value which uniquely
+ EXT identifies the licensing context
+ within the specific license
+ service provider identified by
+ the LicenseHandle.
+
+ +------------------------------
+ --+
+ | ULONG
+ |
+ | Count of Bytes that follow
+ |
+ +------------------------------
+ --+
+ | BYTES
+ |
+ ...
+ |
+ |
+ +------------------------------
+ --+
+
+ The contents of the bytes
+ returned is license system
+ specific. In circumstances where
+ license system specific
+ functionality is being used,
+ this sequence of bytes may be
+ used to identify the current
+ license context.
+*/
+
+LS_STATUS_CODE LS_API_ENTRY NtLSQuery(
+ LS_HANDLE LicenseHandle,
+ LS_ULONG Information,
+ LS_VOID FAR *InfoBuffer,
+ LS_ULONG BufferSize,
+ LS_ULONG FAR *ActualBufferSize)
+{
+ switch ( Information )
+ {
+ case LS_INFO_DATA:
+ case LS_LICENSE_CONTEXT:
+ // set the return buffer to NULL
+ if ( InfoBuffer != NULL )
+ *((LS_ULONG *)InfoBuffer) = 0;
+
+ if ( ActualBufferSize != NULL )
+ *ActualBufferSize = sizeof( LS_ULONG );
+
+ break;
+ case LS_UPDATE_PERIOD:
+ if (( InfoBuffer != NULL ) && ( BufferSize >= sizeof(LS_ULONG)*2 ))
+ {
+ // set the return balue to no recommendation
+ LS_ULONG * uLong = (LS_ULONG*)InfoBuffer;
+ *uLong = 0xffffffff;
+ uLong++;
+ *uLong = 0xffffffff;
+ *ActualBufferSize = sizeof(LS_ULONG) * 2;
+ }
+ break;
+ case LS_INFO_NONE:
+ case LS_INFO_SYSTEM:
+ default:
+ // set return buffer to NULL
+ if ( InfoBuffer != NULL )
+ strcpy( InfoBuffer, (LS_STR*)"");
+
+ if ( ActualBufferSize != NULL )
+ *ActualBufferSize = 0;
+
+ break;
+ }
+ return(LS_SUCCESS);
+}
+
+/*
+ LSEnumProviders()
+ This call is used to enumerate the installed license system
+ service providers.
+
+ Format
+
+ Status = LSEnumProviders( [in] Index, [out] Buffer);
+
+ LS_ULONG Index
+
+ LS_STR * Buffer
+
+ LS_STATUS_CODEStatus;
+
+ Arguments
+
+ Index Index of the service provider. The first provider
+ has an index of zero, the second has an index of
+ one, etc. This index should be incremented by the
+ caller for each successive call to
+ LSEnumProviders() until the status LS_BAD_INDEX is
+ returned.
+
+ Buffer Points to a buffer in which the unique null-
+ terminated string identifying the license system
+ service provider is to be placed. The buffer
+ pointed to by Buffer must be at least 255 bytes
+ long. The value of LS_ANY indicates that the
+ current index is not in use, but is not the last
+ index to obtain.
+
+ Status Detailed error code that can be directly processed
+ by the caller, or which can be converted into a
+ localized message string by the LSGetMessage()
+ function.
+
+ Description
+
+ For each installed provider, a unique string is returned. The
+ unique null-terminated string typically identifies the vendor,
+ product, and version of the license system. This value is the same
+ as an appropriate call to LSQuery(). An Error of LS_BAD_INDEX is
+ returned when the value of Index is higher than the number of
+ providers currently installed. In a networked environment, the
+ version returned is that of the client, not the server.
+
+ An application may enumerate the installed license system service
+ providers by calling LSEnumProviders() successively. The Index is
+ passed in and should be incremented by the caller for each call
+ until the status LS_BAD_INDEX is returned.
+
+*/
+
+LS_STATUS_CODE LS_API_ENTRY NtLSEnumProviders(
+ LS_ULONG Index,
+ LS_STR FAR *Buffer)
+{
+ // set return buffer to NULL
+ if ( Buffer != NULL )
+ strcpy( Buffer, (LS_STR*)"" );
+
+ return(LS_SUCCESS);
+}
diff --git a/private/net/svcdlls/lls/ntlsapi/ntlsapi.def b/private/net/svcdlls/lls/ntlsapi/ntlsapi.def
new file mode 100644
index 000000000..ffc93c667
--- /dev/null
+++ b/private/net/svcdlls/lls/ntlsapi/ntlsapi.def
@@ -0,0 +1,19 @@
+LIBRARY ntlsapi
+
+DESCRIPTION 'Licence System 32 bits API'
+
+CODE LOADONCALL MOVEABLE DISCARDABLE
+DATA PRELOAD MOVEABLE SINGLE
+
+HEAPSIZE 1024
+
+EXPORTS
+ NtLSRequest
+ NtLSRelease
+ NtLSFreeHandle
+ NtLSUpdate
+ NtLSGetMessage
+ NtLSQuery
+ NtLSEnumProviders
+ NtLicenseRequestA
+ NtLicenseRequestW
diff --git a/private/net/svcdlls/lls/ntlsapi/ntlsapi.rc b/private/net/svcdlls/lls/ntlsapi/ntlsapi.rc
new file mode 100644
index 000000000..e9a3d07bf
--- /dev/null
+++ b/private/net/svcdlls/lls/ntlsapi/ntlsapi.rc
@@ -0,0 +1,12 @@
+#include <windows.h>
+#include <ntverp.h>
+
+#define VER_FILETYPE VFT_DLL
+#define VER_FILESUBTYPE VFT2_UNKNOWN
+#define VER_FILEDESCRIPTION_STR "Microsoft\256 License Server Interface DLL"
+
+#define VER_INTERNALNAME_STR "ntlsapi.dll"
+#define VER_ORIGINALFILENAME_STR "ntlsapi.dll"
+
+#include <common.ver>
+
diff --git a/private/net/svcdlls/lls/ntlsapi/rpcstub.c b/private/net/svcdlls/lls/ntlsapi/rpcstub.c
new file mode 100644
index 000000000..11614c448
--- /dev/null
+++ b/private/net/svcdlls/lls/ntlsapi/rpcstub.c
@@ -0,0 +1,399 @@
+/*++
+
+Copyright (c) 1994 Microsoft Corporation
+
+Module Name:
+
+ rpcstub.c
+
+Abstract:
+
+ License Logging Service client stubs.
+
+Author:
+
+ Arthur Hanson (arth) 06-Dec-1994
+
+Environment: User mode only.
+
+Revision History:
+
+--*/
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windows.h>
+
+#include <string.h>
+#include <zwapi.h>
+#include <llsconst.h>
+
+#include <debug.h>
+#include "lsapi_c.h"
+
+
+// #define API_TRACE 1
+
+BOOLEAN LLSUp = FALSE;
+
+#define MAX_EXPECTED_SID_LENGTH 72
+
+LPTSTR pszStringBinding = NULL;
+RTL_CRITICAL_SECTION LPCInitLock;
+
+static HANDLE LpcPortHandle = NULL;
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+LLSReInitLPC( )
+
+/*++
+
+Routine Description:
+
+ This service connects to the LLS server and initializes the LPC port.
+
+Arguments:
+
+Return Value:
+
+ STATUS_SUCCESS - The call completed successfully.
+
+--*/
+
+{
+ RPC_STATUS Status = STATUS_SUCCESS;
+ LPTSTR pszUuid = NULL;
+ LPTSTR pszProtocolSequence = NULL;
+ LPTSTR pszNetworkAddress = NULL;
+ LPTSTR pszEndpoint = NULL;
+ LPTSTR pszOptions = NULL;
+
+ pszProtocolSequence = TEXT("ncalrpc");
+ pszEndpoint = TEXT(LLS_LPC_ENDPOINT);
+ pszNetworkAddress = NULL;
+
+ if (LLSUp) {
+ LLSUp = FALSE;
+
+ if (pszStringBinding != NULL) {
+ Status = RpcStringFree(&pszStringBinding);
+ pszStringBinding = NULL;
+ }
+
+ if (Status == STATUS_SUCCESS) {
+
+ if (lsapirpc_handle != NULL) {
+ Status = RpcBindingFree(&lsapirpc_handle);
+ }
+
+ lsapirpc_handle = NULL;
+ }
+
+ }
+
+ try {
+ // Compose a string binding
+ Status = RpcStringBindingComposeW(pszUuid,
+ pszProtocolSequence,
+ pszNetworkAddress,
+ pszEndpoint,
+ pszOptions,
+ &pszStringBinding);
+ }
+ except (TRUE) {
+ Status = RpcExceptionCode();
+ }
+
+ if(Status) {
+#if DBG
+ dprintf(TEXT("NTLSAPI RpcStringBindingComposeW Failed: 0x%lX\n"), Status);
+#endif
+ return I_RpcMapWin32Status(Status);
+ }
+
+ // Bind using the created string binding...
+ try {
+ Status = RpcBindingFromStringBindingW(pszStringBinding, &lsapirpc_handle);
+ }
+ except (TRUE) {
+ Status = RpcExceptionCode();
+ }
+
+ if(Status) {
+#if DBG
+ dprintf(TEXT("NTLSAPI RpcBindingFromStringBindingW Failed: 0x%lX\n"), Status);
+#endif
+ lsapirpc_handle = NULL;
+
+ return I_RpcMapWin32Status(Status);
+ }
+
+ LLSUp = TRUE;
+
+ return I_RpcMapWin32Status(Status);
+
+} // LLSReInitLPC
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+LLSInitLPC( )
+
+/*++
+
+Routine Description:
+
+ This service connects to the LLS server and initializes the LPC port.
+
+Arguments:
+
+Return Value:
+
+ STATUS_SUCCESS - The call completed successfully.
+
+--*/
+
+{
+ NTSTATUS Status;
+
+ RtlInitializeCriticalSection(&LPCInitLock);
+
+ lsapirpc_handle = NULL;
+ Status = LLSReInitLPC();
+ return Status;
+
+} // LLSInitLPC
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+LLSCloseLPC( )
+
+/*++
+
+Routine Description:
+
+ This closes the LPC port connection to the service.
+
+Arguments:
+
+Return Value:
+
+ STATUS_SUCCESS - The call completed successfully.
+
+--*/
+
+{
+ RPC_STATUS Status = STATUS_SUCCESS;
+
+ RtlEnterCriticalSection(&LPCInitLock);
+ LLSUp = FALSE;
+
+ if (pszStringBinding != NULL) {
+ Status = RpcStringFree(&pszStringBinding);
+ pszStringBinding = NULL;
+ }
+
+ if (Status == STATUS_SUCCESS) {
+
+ if (lsapirpc_handle != NULL) {
+ Status = RpcBindingFree(&lsapirpc_handle);
+ }
+
+ lsapirpc_handle = NULL;
+ }
+
+ RtlLeaveCriticalSection(&LPCInitLock);
+ return Status;
+
+} // LLSCloseLPC
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+LLSLicenseRequest (
+ IN LPWSTR ProductName,
+ IN LPWSTR Version,
+ IN ULONG DataType,
+ IN BOOLEAN IsAdmin,
+ IN PVOID Data,
+ OUT PULONG LicenseHandle
+ )
+
+/*++
+
+Arguments:
+
+ ProductName -
+
+ Version -
+
+ DataType -
+
+ IsAdmin -
+
+ Data -
+
+ LicenseHandle -
+
+Return Status:
+
+ STATUS_SUCCESS - Indicates the service completed successfully.
+
+
+Routine Description:
+
+
+--*/
+
+{
+ WCHAR ProductID[MAX_PRODUCT_NAME_LENGTH + MAX_VERSION_LENGTH + 2];
+ NTSTATUS Status = STATUS_SUCCESS;
+ BOOL Close = FALSE;
+ ULONG VersionIndex;
+ ULONG Size = 0;
+ ULONG i;
+
+ //
+ // Get this out of the way in-case anything goes wrong
+ //
+ *LicenseHandle = (ULONG) 0xFFFFFFFFL;
+
+ //
+ // If LicenseService isn't running (no LPC port) then just return
+ // dummy info - and let the user on.
+ //
+ RtlEnterCriticalSection(&LPCInitLock);
+ if (!LLSUp)
+ Status = LLSReInitLPC();
+ RtlLeaveCriticalSection(&LPCInitLock);
+
+ if (!NT_SUCCESS(Status))
+ return STATUS_SUCCESS;
+
+ if (((i = lstrlen(ProductName)) > MAX_PRODUCT_NAME_LENGTH) || (lstrlen(Version) > MAX_VERSION_LENGTH))
+ return STATUS_SUCCESS;
+
+ //
+ // Create productID - product name + version string.
+ //
+ lstrcpy(ProductID, ProductName);
+ lstrcat(ProductID, TEXT(" "));
+ lstrcat(ProductID, Version);
+
+ VersionIndex = i;
+
+ //
+ // Based on DataType figure out if we are doing a name or a SID
+ // and copy the data appropriatly
+ //
+ if (DataType == NT_LS_USER_NAME) {
+ Size = lstrlen((LPWSTR) Data);
+ if (Size > MAX_USER_NAME_LENGTH)
+ return STATUS_SUCCESS;
+
+ Size = (Size + 1) * sizeof(TCHAR);
+ }
+
+ if (DataType == NT_LS_USER_SID) {
+ //
+ // Friggin SID, so need to copy it manually.
+ // WARNING: This makes it dependent on the structure of the
+ // SID!!!
+ //
+ Size = RtlLengthSid( (PSID) Data);
+
+ if (Size > MAX_EXPECTED_SID_LENGTH)
+ return STATUS_SUCCESS;
+ }
+
+ //
+ // Call the Server.
+ //
+ try {
+ Status = LlsrLicenseRequestW(
+ LicenseHandle,
+ ProductID,
+ VersionIndex,
+ IsAdmin,
+ DataType,
+ Size,
+ (PBYTE) Data );
+ }
+ except (TRUE) {
+#if DBG
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+ if (Status != RPC_NT_SERVER_UNAVAILABLE) {
+ dprintf(TEXT("ERROR NTLSAPI.DLL: RPC Exception: 0x%lX\n"), Status);
+// ASSERT(FALSE);
+ }
+#endif
+
+ Status = STATUS_SUCCESS;
+ }
+
+ if (Close)
+ LLSCloseLPC();
+
+ return Status;
+
+} // LLSLicenseRequest
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+LLSLicenseFree (
+ IN ULONG LicenseHandle
+ )
+
+/*++
+
+Arguments:
+
+ LicenseHandle -
+
+Return Status:
+
+ STATUS_SUCCESS - Indicates the service completed successfully.
+
+
+--*/
+
+{
+ BOOL Close = FALSE;
+ NTSTATUS Status;
+
+ //
+ // If LicenseService isn't running (no LPC port) then just return
+ // dummy info - and let the user on.
+ //
+ if (!LLSUp)
+ return STATUS_SUCCESS;
+
+
+ //
+ // Call the Server.
+ //
+ try {
+ Status = LlsrLicenseFree( (DWORD) LicenseHandle );
+ }
+ except (TRUE) {
+#if DBG
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+ if (Status != RPC_NT_SERVER_UNAVAILABLE) {
+ dprintf(TEXT("ERROR NTLSAPI.DLL: RPC Exception: 0x%lX\n"), Status);
+// ASSERT(FALSE);
+ }
+#endif
+
+ Status = STATUS_SUCCESS;
+ }
+
+ if (Close)
+ LLSCloseLPC();
+
+ return Status;
+} // LLSLicenseFree
diff --git a/private/net/svcdlls/lls/ntlsapi/sources b/private/net/svcdlls/lls/ntlsapi/sources
new file mode 100644
index 000000000..21ce312f3
--- /dev/null
+++ b/private/net/svcdlls/lls/ntlsapi/sources
@@ -0,0 +1,66 @@
+
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+
+Author:
+
+ Steve Wood (stevewo) 12-Apr-1990
+
+NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl
+
+!ENDIF
+
+MAJORCOMP=ntlsapi
+MINORCOMP=
+
+TARGETNAME=ntlsapi
+TARGETPATH=$(BASEDIR)\public\sdk\lib
+TARGETTYPE=DYNLINK
+
+DLLENTRY=DllMain
+
+SDKINC=$(BASEDIR)\public\sdk\inc
+PRIVINC=$(BASEDIR)\private\inc
+WINSINC=$(BASEDIR)\private\net\sockets\wins\server\server\inc
+WINSMSGINC=$(BASEDIR)\private\net\sockets\wins\server\server\msg
+TARGETLIBS= \
+ ..\common\obj\*\llscomm.lib \
+ $(BASEDIR)\public\sdk\lib\*\rpcutil.lib \
+ $(BASEDIR)\public\sdk\lib\*\rpcrt4.lib \
+ $(BASEDIR)\Public\sdk\Lib\*\advapi32.lib \
+ $(BASEDIR)\public\sdk\lib\*\kernel32.lib \
+ $(BASEDIR)\public\sdk\lib\*\user32.lib
+
+INCLUDES=$(PRIVINC);$(WINSINC);$(WINSMSGINC);$(SDKINC);..\inc
+
+USE_CRTDLL=1
+
+SOURCES= \
+ ntlsapi.c \
+ rpcstub.c \
+ lsapi_c.c \
+ main.c \
+ ntlsapi.rc
+
+C_DEFINES=-DINCL_32 -DNT -DWIN32 -DRPC_NO_WINDOWS_H -DUNICODE -D_UNICODE
+UMTYPE=console
+UMLIBS= \
+ ..\common\obj\*\llscomm.lib \
+ $(BASEDIR)\public\sdk\lib\*\rpcutil.lib \
+ $(BASEDIR)\public\sdk\lib\*\rpcrt4.lib \
+ $(BASEDIR)\Public\Sdk\Lib\*\kernel32.lib \
+ $(BASEDIR)\Public\Sdk\Lib\*\advapi32.lib \
+ $(BASEDIR)\public\sdk\lib\*\user32.lib
diff --git a/private/net/svcdlls/lls/server/certdb.c b/private/net/svcdlls/lls/server/certdb.c
new file mode 100644
index 000000000..e543ad203
--- /dev/null
+++ b/private/net/svcdlls/lls/server/certdb.c
@@ -0,0 +1,1511 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ certdb.c
+
+Abstract:
+
+ License Logging Service certificate database implementation. This database
+ tracks license certificates to help ensure that no more licenses from a
+ single certificate are installe don the license enterprise than are allowed
+ by the certificate's license agreement.
+
+ The certificate database at the top level is an unsorted array of
+ certificate headers. There is exactly one header per unique certificate.
+ A unique certificate is identified by a combination of product name,
+ certificate ID, certificate capacity (max. licenses legally installable),
+ and expiration date.
+
+ Each header has an attached array of certificate claims. There is exactly
+ one claim per machine that (a) replicates to this machine, directly or
+ indirectly, and (b) has licenses from this certificate installed. Each
+ claim contains the server name to which it corresponds, the number of
+ licenses installed on it, and the date this information was replicated.
+ If a claim is not updated after LLS_CERT_DB_REPLICATION_DATE_DELTA_MAX
+ seconds (3 days as of this writing), the claim is considered forfeit and
+ is erased.
+
+Author:
+
+ Jeff Parham (jeffparh) 08-Dec-1995
+
+Revision History:
+
+--*/
+
+
+#include <stdlib.h>
+#include <limits.h>
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windows.h>
+#include <rpc.h>
+#include <rpcndr.h>
+
+#include "debug.h"
+#include "llsutil.h"
+#include "llssrv.h"
+#include "llsapi.h"
+#include "llsevent.h"
+#include "llsrpc_s.h"
+#include "certdb.h"
+#include "purchase.h"
+#include "registry.h"
+
+
+RTL_RESOURCE CertDbHeaderListLock;
+PLLS_CERT_DB_CERTIFICATE_HEADER CertDbHeaderList = NULL;
+DWORD CertDbHeaderListSize = 0;
+HANDLE CertDbFile = NULL;
+
+
+///////////////////////////////////////////////////////////////////////////////
+NTSTATUS CertDbClaimEnter( LPTSTR pszServerName,
+ PLLS_LICENSE_INFO_1 pLicense,
+ BOOL bIsTotal,
+ DWORD ReplicationDate )
+
+/*++
+
+Routine Description:
+
+ Enter a claim into the database.
+
+Arguments:
+
+ pszServerName (LPTSTR)
+ The server for which to enter this claim. A value of NULL indicates the
+ local server.
+ pLicense (PLLS_LICENSE_INFO_1)
+ License information to enter into the database.
+ bIsTotal (BOOL)
+ If TRUE, indicates that this license information represents the total
+ licenses installed on the machine and should therefore replace the
+ current claim (if any). Otherwise, indicates this license information
+ should be added to the current claim (if any).
+ ReplicationDate (DWORD)
+ Indicates the date which this information was last replicated. A value
+ of 0 will be replaced with the current system time.
+
+Return Value:
+
+ STATUS_SUCCESS
+ STATUS_INVALID_PARAMETER
+ STATUS_INVALID_COMPUTER_NAME
+ STATUS_NO_MEMORY
+
+--*/
+
+{
+ NTSTATUS nt;
+
+ if ( ( NULL == pLicense ) || ( 0 == pLicense->CertificateID ) )
+ {
+ ASSERT( FALSE );
+ nt = STATUS_INVALID_PARAMETER;
+ }
+ else
+ {
+ TCHAR szComputerName[ 1 + MAX_COMPUTERNAME_LENGTH ];
+
+ if ( NULL == pszServerName )
+ {
+ // use local server name
+ DWORD cchComputerName = sizeof( szComputerName ) / sizeof( *szComputerName );
+ BOOL ok;
+
+ ok = GetComputerName( szComputerName, &cchComputerName );
+ ASSERT( ok );
+
+ if ( ok )
+ {
+ pszServerName = szComputerName;
+ }
+ }
+ else
+ {
+ // remove leading backslashes (if any) from server name
+ while ( TEXT('\\') == *pszServerName )
+ {
+ pszServerName++;
+ }
+ }
+
+ if ( ( NULL == pszServerName ) || !*pszServerName || ( lstrlen( pszServerName ) > MAX_COMPUTERNAME_LENGTH ) )
+ {
+ ASSERT( FALSE );
+ nt = STATUS_INVALID_COMPUTER_NAME;
+ }
+ else
+ {
+ PLLS_CERT_DB_CERTIFICATE_HEADER pHeader;
+
+ RtlAcquireResourceExclusive( &CertDbHeaderListLock, TRUE );
+
+ // is the certificate in the db?
+ pHeader = CertDbHeaderFind( pLicense );
+
+ if ( NULL == pHeader )
+ {
+ // certificate not yet in db; add it
+ pHeader = CertDbHeaderAdd( pLicense );
+ }
+
+ if ( NULL == pHeader )
+ {
+ // could not find or add header
+ ASSERT( FALSE );
+ nt = STATUS_NO_MEMORY;
+ }
+ else
+ {
+ // now have header; is this claim already filed?
+ int iClaim;
+
+ iClaim = CertDbClaimFind( pHeader, pszServerName );
+
+ if ( iClaim < 0 )
+ {
+ // claim does not yet exist; add it
+ if ( NULL == pHeader->Claims )
+ {
+ pHeader->Claims = LocalAlloc( LPTR, ( 1 + pHeader->NumClaims ) * sizeof( LLS_CERT_DB_CERTIFICATE_CLAIM ) );
+ }
+ else
+ {
+ pHeader->Claims = LocalReAlloc( pHeader->Claims, ( 1 + pHeader->NumClaims ) * sizeof( LLS_CERT_DB_CERTIFICATE_CLAIM ), LHND );
+ }
+
+ if ( NULL == pHeader->Claims )
+ {
+ // memory allocation failed
+ pHeader->NumClaims = 0;
+ }
+ else
+ {
+ // claim list expanded; save server name
+ iClaim = pHeader->NumClaims;
+ lstrcpy( pHeader->Claims[ iClaim ].ServerName, pszServerName );
+ pHeader->Claims[ iClaim ].Quantity = 0;
+
+ pHeader->NumClaims++;
+ }
+ }
+
+ if ( iClaim < 0 )
+ {
+ // could not find or add claim to header
+ ASSERT( FALSE );
+ nt = STATUS_NO_MEMORY;
+ }
+ else
+ {
+ // claim found or added; update info
+ ASSERT( !lstrcmpi( pszServerName, pHeader->Claims[ iClaim ].ServerName ) );
+ pHeader->Claims[ iClaim ].ReplicationDate = ReplicationDate ? ReplicationDate : DateSystemGet();
+
+ if ( bIsTotal )
+ {
+ // the given value is the new total
+ pHeader->Claims[ iClaim ].Quantity = pLicense->Quantity;
+ nt = STATUS_SUCCESS;
+ }
+ else if ( pHeader->Claims[ iClaim ].Quantity + pLicense->Quantity >= 0 )
+ {
+ // the given value is added to the current sum to make the total
+ pHeader->Claims[ iClaim ].Quantity += pLicense->Quantity;
+ nt = STATUS_SUCCESS;
+ }
+ else
+ {
+ // overflow
+ nt = STATUS_INVALID_PARAMETER;
+ }
+ }
+ }
+
+ RtlReleaseResource( &CertDbHeaderListLock );
+
+ if ( STATUS_SUCCESS == nt )
+ {
+ // any product that has licenses with non-0 certificate IDs
+ // must be secure; this code is here such that when certificates
+ // are replicated, the "product is secure" info is replicated, too
+ // this will also help recover from the case where someone deletes
+ // the registry key that lists all secure products
+ ServiceSecuritySet( pLicense->Product );
+ }
+ }
+ }
+
+ return nt;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+BOOL CertDbClaimApprove( PLLS_LICENSE_INFO_1 pLicense )
+
+/*++
+
+Routine Description:
+
+ Check to see if adding the given licenses is legal. This call is typically
+ made before adding a license into the system to verify that doing so does
+ not violate the certificate's license agreement.
+
+Arguments:
+
+ pLicense (PLLS_LICENSE_INFO_1)
+ License information for which approval is sought.
+
+Return Value:
+
+ TRUE (approved) or FALSE (rejected).
+
+--*/
+
+{
+ BOOL bOkToAdd = TRUE;
+ PLLS_CERT_DB_CERTIFICATE_HEADER pHeader;
+ TCHAR szComputerName[ 1 + MAX_COMPUTERNAME_LENGTH ];
+ DWORD cchComputerName = sizeof( szComputerName ) / sizeof( *szComputerName );
+ BOOL ok;
+
+ if ( ( pLicense->Quantity > 0 ) && ( (DWORD)pLicense->Quantity > pLicense->MaxQuantity ) )
+ {
+ // certificate add request exceeds its capacity all by itself!
+ bOkToAdd = FALSE;
+ }
+ else
+ {
+ ok = GetComputerName( szComputerName, &cchComputerName );
+ ASSERT( ok );
+
+ if ( !ok )
+ {
+ // deletions will fail...
+ *szComputerName = TEXT( '\0' );
+ }
+
+ // do we have a record of this certificate?
+ RtlAcquireResourceShared( &CertDbHeaderListLock, TRUE );
+
+ pHeader = CertDbHeaderFind( pLicense );
+
+ if ( NULL == pHeader )
+ {
+ // don't have any record of this certificate; ok to add if Quantity > 0
+ bOkToAdd = ( pLicense->Quantity > 0 );
+ }
+ else
+ {
+ LONG lTotalQuantity = 0;
+ int iClaim;
+
+ // we have seen this certificate; are there enough licenses available?
+ for ( iClaim=0; (DWORD)iClaim < pHeader->NumClaims; iClaim++ )
+ {
+ // for license remove requests, tally only local licenses
+ // for license add requests, tally all licenses
+ if ( ( ( pLicense->Quantity > 0 )
+ || ( !lstrcmpi( pHeader->Claims[ iClaim ].ServerName, szComputerName ) ) )
+ && ( lTotalQuantity + pHeader->Claims[ iClaim ].Quantity >= 0 ) )
+ {
+ // add to tally
+ lTotalQuantity += pHeader->Claims[ iClaim ].Quantity;
+ }
+ }
+
+ if ( lTotalQuantity + pLicense->Quantity < 0 )
+ {
+ // overflow or underflow
+ bOkToAdd = FALSE;
+ }
+ else if ( (DWORD)(lTotalQuantity + pLicense->Quantity) > pHeader->MaxQuantity )
+ {
+ // exceeds certificate capacity
+ bOkToAdd = FALSE;
+ }
+ else
+ {
+ // okay by me
+ bOkToAdd = TRUE;
+ }
+ }
+
+ RtlReleaseResource( &CertDbHeaderListLock );
+ }
+
+ return bOkToAdd;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+PLLS_CERT_DB_CERTIFICATE_HEADER CertDbHeaderFind( PLLS_LICENSE_INFO_1 pLicense )
+
+/*++
+
+Routine Description:
+
+ Find a certificate header in the database.
+
+Arguments:
+
+ pLicense (PLLS_LICENSE_INFO_1)
+ License information for which to find the appropriate header.
+
+Return Value:
+
+ A pointer to the found header, or NULL if not found.
+
+--*/
+
+{
+ // assumes db is already locked for shared or exclusive access
+
+ PLLS_CERT_DB_CERTIFICATE_HEADER pHeader = NULL;
+ int iHeader;
+
+ for ( iHeader=0; ( NULL == pHeader ) && ( (DWORD)iHeader < CertDbHeaderListSize ); iHeader++ )
+ {
+ if ( ( CertDbHeaderList[ iHeader ].CertificateID == pLicense->CertificateID )
+ && ( CertDbHeaderList[ iHeader ].MaxQuantity == pLicense->MaxQuantity )
+ && ( CertDbHeaderList[ iHeader ].ExpirationDate == pLicense->ExpirationDate )
+ && ( !lstrcmpi( CertDbHeaderList[ iHeader ].Product, pLicense->Product ) ) )
+ {
+ // header found!
+ pHeader = &CertDbHeaderList[ iHeader ];
+ }
+ }
+
+ return pHeader;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+PLLS_CERT_DB_CERTIFICATE_HEADER CertDbHeaderAdd( PLLS_LICENSE_INFO_1 pLicense )
+
+/*++
+
+Routine Description:
+
+ Add a certificate header to the database.
+
+Arguments:
+
+ pLicense (PLLS_LICENSE_INFO_1)
+ License information for which to add the header.
+
+Return Value:
+
+ A pointer to the added header, or NULL if memory could not be allocated.
+
+--*/
+
+{
+ // assumes caller has made sure the header does not already exist
+ // assumes db is locked for exclusive access
+
+ PLLS_CERT_DB_CERTIFICATE_HEADER pHeader;
+
+ if ( CertDbHeaderListSize )
+ {
+ CertDbHeaderList = LocalReAlloc( CertDbHeaderList, ( 1 + CertDbHeaderListSize ) * sizeof( LLS_CERT_DB_CERTIFICATE_HEADER ), LHND );
+ }
+ else
+ {
+ CertDbHeaderList = LocalAlloc( LPTR, ( 1 + CertDbHeaderListSize ) * sizeof( LLS_CERT_DB_CERTIFICATE_HEADER ) );
+ }
+
+ if ( NULL == CertDbHeaderList )
+ {
+ // memory allocation failed; bye-bye database!
+ ASSERT( FALSE );
+ CertDbHeaderListSize = 0;
+ pHeader = NULL;
+ }
+ else
+ {
+ // allocate space for product name
+ CertDbHeaderList[ CertDbHeaderListSize ].Product = LocalAlloc( LPTR, sizeof( TCHAR ) * ( 1 + lstrlen( pLicense->Product ) ) );
+
+ if ( NULL == CertDbHeaderList[ CertDbHeaderListSize ].Product )
+ {
+ // memory allocation failed
+ ASSERT( FALSE );
+ pHeader = NULL;
+ }
+ else
+ {
+ // success!
+ pHeader = &CertDbHeaderList[ CertDbHeaderListSize ];
+ CertDbHeaderListSize++;
+
+ lstrcpy( pHeader->Product, pLicense->Product );
+ pHeader->CertificateID = pLicense->CertificateID;
+ pHeader->MaxQuantity = pLicense->MaxQuantity;
+ pHeader->ExpirationDate = pLicense->ExpirationDate;
+ }
+ }
+
+ return pHeader;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+int CertDbClaimFind( PLLS_CERT_DB_CERTIFICATE_HEADER pHeader, LPTSTR pszServerName )
+
+/*++
+
+Routine Description:
+
+ Find a certificate claim for a specific server in the claim list.
+
+Arguments:
+
+ pHeader (PLLS_CERT_DB_CERTIFICATE_HEADER)
+ Header containing the claim list to search.
+ pszServerName (LPTSTR)
+ Name of the server for which the claim is sought.
+
+Return Value:
+
+ The index of the found claim, or -1 if not found.
+
+--*/
+
+{
+ // assumes db is already locked for shared or exclusive access
+
+ int iClaim;
+
+ for ( iClaim=0; (DWORD)iClaim < pHeader->NumClaims; iClaim++ )
+ {
+ if ( !lstrcmpi( pHeader->Claims[ iClaim ].ServerName, pszServerName ) )
+ {
+ break;
+ }
+ }
+
+ if ( (DWORD)iClaim >= pHeader->NumClaims )
+ {
+ iClaim = -1;
+ }
+
+ return iClaim;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+void CertDbPrune()
+
+/*++
+
+Routine Description:
+
+ Remove entries in the database which have expired. Entries expire if they
+ have not been re-replicated in LLS_CERT_DB_REPLICATION_DATE_DELTA_MAX
+ seconds (3 days as of this writing).
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ int iHeader;
+ int iClaim;
+ DWORD CurrentDate;
+ DWORD MinimumDate;
+ TCHAR szComputerName[ 1 + MAX_COMPUTERNAME_LENGTH ] = TEXT("");
+ DWORD cchComputerName = sizeof( szComputerName ) / sizeof( *szComputerName );
+ BOOL ok;
+
+ ok = GetComputerName( szComputerName, &cchComputerName );
+ ASSERT( ok );
+
+ if ( ok )
+ {
+ RtlAcquireResourceExclusive( &CertDbHeaderListLock, TRUE );
+
+ CurrentDate = DateSystemGet();
+ MinimumDate = CurrentDate - LLS_CERT_DB_REPLICATION_DATE_DELTA_MAX;
+
+ for ( iHeader=0; (DWORD)iHeader < CertDbHeaderListSize; iHeader++ )
+ {
+ for ( iClaim=0; (DWORD)iClaim < CertDbHeaderList[ iHeader ].NumClaims; )
+ {
+ // Note that we prune entries made in the future, too, to avoid having an incorrect date
+ // forcing us to keep an entry forever.
+ //
+ // For this application, it's better to keep fewer entries rather than more, as the
+ // fewer entries we have, the less restrictive the system is.
+ //
+ // Don't prune local entries.
+
+ if ( ( ( CertDbHeaderList[ iHeader ].Claims[ iClaim ].ReplicationDate < MinimumDate )
+ || ( CertDbHeaderList[ iHeader ].Claims[ iClaim ].ReplicationDate > CurrentDate ) )
+ && lstrcmpi( szComputerName, CertDbHeaderList[ iHeader ].Claims[ iClaim ].ServerName ) )
+ {
+ // remove claim
+ MoveMemory( &CertDbHeaderList[ iHeader ].Claims[ iClaim ],
+ &CertDbHeaderList[ iHeader ].Claims[ iClaim+1 ],
+ CertDbHeaderList[ iHeader ].NumClaims - ( iClaim + 1 ) );
+
+ CertDbHeaderList[ iHeader ].NumClaims--;
+ }
+ else
+ {
+ // keep this claim
+ iClaim++;
+ }
+ }
+ }
+
+ RtlReleaseResource( &CertDbHeaderListLock );
+ }
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+void CertDbRemoveLocalClaims()
+
+/*++
+
+Routine Description:
+
+ Remove entries in the database corresponding to the local server.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ int iHeader;
+ int iClaim;
+ TCHAR szComputerName[ 1 + MAX_COMPUTERNAME_LENGTH ] = TEXT("");
+ DWORD cchComputerName = sizeof( szComputerName ) / sizeof( *szComputerName );
+ BOOL ok;
+
+ ok = GetComputerName( szComputerName, &cchComputerName );
+ ASSERT( ok );
+
+ if ( ok )
+ {
+ RtlAcquireResourceExclusive( &CertDbHeaderListLock, TRUE );
+
+ for ( iHeader=0; (DWORD)iHeader < CertDbHeaderListSize; iHeader++ )
+ {
+ for ( iClaim=0; (DWORD)iClaim < CertDbHeaderList[ iHeader ].NumClaims; )
+ {
+ if ( !lstrcmpi( szComputerName, CertDbHeaderList[ iHeader ].Claims[ iClaim ].ServerName ) )
+ {
+ // remove claim
+ MoveMemory( &CertDbHeaderList[ iHeader ].Claims[ iClaim ],
+ &CertDbHeaderList[ iHeader ].Claims[ iClaim+1 ],
+ CertDbHeaderList[ iHeader ].NumClaims - ( iClaim + 1 ) );
+
+ CertDbHeaderList[ iHeader ].NumClaims--;
+ }
+ else
+ {
+ // keep this claim
+ iClaim++;
+ }
+ }
+ }
+
+ RtlReleaseResource( &CertDbHeaderListLock );
+ }
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+void CertDbLogViolations()
+
+/*++
+
+Routine Description:
+
+ Log violations of certificate license agreements to the event log of the
+ local server.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ int iHeader;
+ int iClaim;
+ HANDLE hEventLog;
+ DWORD dwTotalQuantity;
+ HINSTANCE hDll;
+ DWORD cch;
+ LPTSTR pszViolationServerEntryFormat;
+ LPTSTR pszViolationFormat;
+ LPTSTR pszViolationServerEntryList;
+ LPTSTR pszNextViolationServerEntry;
+ TCHAR szNumLicenses[ 20 ];
+ TCHAR szMaxLicenses[ 20 ];
+ TCHAR szCertificateID[ 20 ];
+ LPTSTR apszSubstStrings[ 4 ];
+ DWORD cbViolationServerList;
+ LPTSTR pszViolationServerList;
+
+ // get rid of out-dated entries
+ CertDbPrune();
+
+ hDll = LoadLibrary( TEXT( "LLSRPC.DLL" ) );
+ ASSERT( NULL != hDll );
+
+ if ( NULL != hDll )
+ {
+ // format for part of logged message that lists server and #licenses
+ cch = FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER
+ | FORMAT_MESSAGE_IGNORE_INSERTS
+ | FORMAT_MESSAGE_FROM_HMODULE,
+ hDll,
+ LLS_EVENT_CERT_VIOLATION_SERVER_ENTRY,
+ GetSystemDefaultLangID(),
+ (LPVOID) &pszViolationServerEntryFormat,
+ 0,
+ NULL );
+ ASSERT( 0 != cch );
+
+ if ( 0 != cch )
+ {
+ hEventLog = RegisterEventSource( NULL, TEXT("LicenseService") );
+
+ if ( NULL != hEventLog )
+ {
+ RtlAcquireResourceShared( &CertDbHeaderListLock, TRUE );
+
+ for ( iHeader=0; (DWORD)iHeader < CertDbHeaderListSize; iHeader++ )
+ {
+ dwTotalQuantity = 0;
+
+ // tally the number of licenses claimed against this certificate
+ for ( iClaim=0; (DWORD)iClaim < CertDbHeaderList[ iHeader ].NumClaims; iClaim++ )
+ {
+ if ( dwTotalQuantity + (DWORD)CertDbHeaderList[ iHeader ].Claims[ iClaim ].Quantity < dwTotalQuantity )
+ {
+ // overflow!
+ dwTotalQuantity = ULONG_MAX;
+ break;
+ }
+ else
+ {
+ // add to tally
+ dwTotalQuantity += CertDbHeaderList[ iHeader ].Claims[ iClaim ].Quantity;
+ }
+ }
+
+ if ( dwTotalQuantity > CertDbHeaderList[ iHeader ].MaxQuantity )
+ {
+ // this certificate is in violation
+
+ // create message we're going to log
+ cbViolationServerList = CertDbHeaderList[ iHeader ].NumClaims
+ * sizeof( TCHAR )
+ * ( lstrlen( pszViolationServerEntryFormat )
+ + 20
+ + MAX_COMPUTERNAME_LENGTH );
+ pszViolationServerList = LocalAlloc( LPTR, cbViolationServerList );
+ ASSERT( NULL != pszViolationServerList );
+
+ if ( NULL != pszViolationServerList )
+ {
+ // create an entry for each server in violation, stringing them
+ // together in pszViolationServerList
+ pszNextViolationServerEntry = pszViolationServerList;
+
+ for ( iClaim=0; (DWORD)iClaim < CertDbHeaderList[ iHeader ].NumClaims; iClaim++ )
+ {
+ _ltow( CertDbHeaderList[ iHeader ].Claims[ iClaim ].Quantity, szNumLicenses, 10 );
+
+ apszSubstStrings[ 0 ] = CertDbHeaderList[ iHeader ].Claims[ iClaim ].ServerName;
+ apszSubstStrings[ 1 ] = szNumLicenses;
+
+ cch = FormatMessage( FORMAT_MESSAGE_FROM_STRING
+ | FORMAT_MESSAGE_ARGUMENT_ARRAY,
+ pszViolationServerEntryFormat,
+ 0,
+ 0,
+ pszNextViolationServerEntry,
+ cbViolationServerList - ( pszNextViolationServerEntry - pszViolationServerList ),
+ (LPVOID) apszSubstStrings );
+ ASSERT( 0 != cch );
+
+ pszNextViolationServerEntry += lstrlen( pszNextViolationServerEntry );
+ }
+
+ _ultow( CertDbHeaderList[ iHeader ].CertificateID, szCertificateID, 10 );
+ _ultow( dwTotalQuantity, szNumLicenses, 10 );
+ _ultow( CertDbHeaderList[ iHeader ].MaxQuantity, szMaxLicenses, 10 );
+
+ apszSubstStrings[ 0 ] = CertDbHeaderList[ iHeader ].Product;
+ apszSubstStrings[ 1 ] = szCertificateID;
+ apszSubstStrings[ 2 ] = szNumLicenses;
+ apszSubstStrings[ 3 ] = szMaxLicenses;
+ apszSubstStrings[ 4 ] = pszViolationServerList;
+
+ // log the violation
+ if ( NULL != hEventLog )
+ {
+ ReportEvent( hEventLog,
+ EVENTLOG_ERROR_TYPE,
+ 0,
+ LLS_EVENT_CERT_VIOLATION,
+ NULL,
+ 5,
+ 0,
+ apszSubstStrings,
+ NULL );
+ }
+
+ LocalFree( pszViolationServerList );
+ }
+ }
+ }
+
+ RtlReleaseResource( &CertDbHeaderListLock );
+ LocalFree( pszViolationServerEntryFormat );
+
+ DeregisterEventSource( hEventLog );
+ }
+ }
+
+ FreeLibrary( hDll );
+ }
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+NTSTATUS CertDbPack( LPDWORD pcchProductStrings,
+ LPTSTR * ppchProductStrings,
+ LPDWORD pdwNumHeaders,
+ PREPL_CERT_DB_CERTIFICATE_HEADER_0 * ppHeaders,
+ LPDWORD pdwNumClaims,
+ PREPL_CERT_DB_CERTIFICATE_CLAIM_0 * ppClaims )
+
+/*++
+
+Routine Description:
+
+ Pack the certificate database into manageable chunks that can be saved or
+ replicated.
+
+Arguments:
+
+ pcchProductStrings (LPDWORD)
+ On return, holds the size (in characters) of the buffer pointed to by
+ *ppchProductStrings.
+ ppchProductStrings (LPTSTR *)
+ On return, points to the buffer containing the product strings component
+ of the database.
+ pdwNumHeaders (LPDWORD)
+ On return, holds the number of certificate headers in the array pointed
+ to by *ppHeaders.
+ ppHeaders (PREPL_CERT_DB_CERTIFICATE_HEADER_0 *)
+ On return, holds a pointer to the certificate header array component of
+ the database.
+ pdwNumClaims (LPDWORD)
+ On return, holds the number of certificate claims in the array pointed
+ to by *ppHeaders.
+ ppClaims (PREPL_CERT_DB_CERTIFICATE_CLAIM_0 *)
+ On return, holds a pointer to the certificate claim array component of
+ the database.
+
+Return Value:
+
+ STATUS_SUCCESS or STATUS_NO_MEMORY.
+
+--*/
+
+{
+ NTSTATUS nt = STATUS_SUCCESS;
+
+ DWORD cchProductStrings = 0;
+ LPTSTR pchProductStrings = NULL;
+ DWORD dwNumHeaders = 0;
+ PREPL_CERT_DB_CERTIFICATE_HEADER_0 pHeaders = NULL;
+ DWORD dwNumClaims = 0;
+ PREPL_CERT_DB_CERTIFICATE_CLAIM_0 pClaims = NULL;
+
+ LPTSTR pchNextProductString;
+ int iHeader;
+ int iClaim;
+
+ CertDbPrune();
+ CertDbUpdateLocalClaims();
+
+ RtlAcquireResourceExclusive( &CertDbHeaderListLock, TRUE );
+
+ if ( 0 != CertDbHeaderListSize )
+ {
+ // how big are all of our strings put together?
+ // hom many certificate claims are there?
+ for ( iHeader=0; (DWORD)iHeader < CertDbHeaderListSize; iHeader++ )
+ {
+ cchProductStrings += 1 + lstrlen( CertDbHeaderList[ iHeader ].Product );
+ dwNumClaims += CertDbHeaderList[ iHeader ].NumClaims;
+ }
+ dwNumHeaders = CertDbHeaderListSize;
+
+ pchProductStrings = LocalAlloc( LMEM_FIXED, cchProductStrings * sizeof( TCHAR ) );
+ pHeaders = LocalAlloc( LMEM_FIXED, dwNumHeaders * sizeof( REPL_CERT_DB_CERTIFICATE_HEADER_0 ) );
+ pClaims = LocalAlloc( LMEM_FIXED, dwNumClaims * sizeof( REPL_CERT_DB_CERTIFICATE_CLAIM_0 ) );
+
+ if ( ( NULL == pchProductStrings ) || ( NULL == pHeaders ) || ( NULL == pClaims ) )
+ {
+ ASSERT( FALSE );
+ nt = STATUS_NO_MEMORY;
+ }
+ else
+ {
+ // pack the product strings
+ pchNextProductString = pchProductStrings;
+
+ for ( iHeader=0; (DWORD)iHeader < CertDbHeaderListSize; iHeader++ )
+ {
+ lstrcpy( pchNextProductString, CertDbHeaderList[ iHeader ].Product );
+ pchNextProductString += 1 + lstrlen( pchNextProductString );
+ }
+
+ // now pack away the rest of our structures
+ iClaim = 0;
+ for ( iHeader=0; (DWORD)iHeader < CertDbHeaderListSize; iHeader++ )
+ {
+ pHeaders[ iHeader ].CertificateID = CertDbHeaderList[ iHeader ].CertificateID;
+ pHeaders[ iHeader ].MaxQuantity = CertDbHeaderList[ iHeader ].MaxQuantity;
+ pHeaders[ iHeader ].ExpirationDate = CertDbHeaderList[ iHeader ].ExpirationDate;
+ pHeaders[ iHeader ].NumClaims = CertDbHeaderList[ iHeader ].NumClaims;
+
+ if ( CertDbHeaderList[ iHeader ].NumClaims )
+ {
+ memcpy( &pClaims[ iClaim ],
+ CertDbHeaderList[ iHeader ].Claims,
+ CertDbHeaderList[ iHeader ].NumClaims * sizeof( LLS_CERT_DB_CERTIFICATE_CLAIM ) );
+
+ iClaim += CertDbHeaderList[ iHeader ].NumClaims;
+ }
+ }
+
+ // all done!
+ nt = STATUS_SUCCESS;
+ }
+ }
+
+ if ( STATUS_SUCCESS == nt )
+ {
+ *pcchProductStrings = cchProductStrings;
+ *ppchProductStrings = pchProductStrings;
+
+ *pdwNumHeaders = dwNumHeaders;
+ *ppHeaders = pHeaders;
+
+ *pdwNumClaims = dwNumClaims;
+ *ppClaims = pClaims;
+ }
+ else
+ {
+ if ( NULL != pchProductStrings ) LocalFree( pchProductStrings );
+ if ( NULL != pHeaders ) LocalFree( pHeaders );
+ if ( NULL != pClaims ) LocalFree( pClaims );
+ }
+
+ RtlReleaseResource( &CertDbHeaderListLock );
+
+ return nt;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+NTSTATUS CertDbUnpack( DWORD cchProductStrings,
+ LPTSTR pchProductStrings,
+ DWORD dwNumHeaders,
+ PREPL_CERT_DB_CERTIFICATE_HEADER_0 pHeaders,
+ DWORD dwNumClaims,
+ PREPL_CERT_DB_CERTIFICATE_CLAIM_0 pClaims,
+ BOOL bReplicated )
+
+/*++
+
+Routine Description:
+
+ Pack the certificate database into manageable chunks that can be saved or
+ replicated.
+
+Arguments:
+
+ cchProductStrings (DWORD)
+ The size (in characters) of the buffer pointed to by pchProductStrings.
+ pchProductStrings (LPTSTR)
+ The buffer containing the product strings component of the database.
+ dwNumHeaders (DWORD)
+ The number of certificate headers in the array pointed to by pHeaders.
+ pHeaders (PREPL_CERT_DB_CERTIFICATE_HEADER_0)
+ The certificate header array component of the database.
+ dwNumClaims (DWORD)
+ The number of certificate claims in the array pointed to by pHeaders.
+ pClaims (PREPL_CERT_DB_CERTIFICATE_CLAIM_0)
+ The certificate claim array component of the database.
+ bReplicated (BOOL)
+ Indicates whether this information was replicated. This is used to
+ determine the time at which this information will expire.
+
+Return Value:
+
+ STATUS_SUCCESS or NTSTATUS error code.
+
+--*/
+
+{
+ NTSTATUS nt = STATUS_SUCCESS;
+ LPTSTR pchNextProductString;
+ LPBYTE pb;
+ int iHeader;
+ int iClaim;
+ int iClaimBase;
+ LLS_LICENSE_INFO_1 lic;
+ TCHAR szComputerName[ 1 + MAX_COMPUTERNAME_LENGTH ];
+ DWORD cchComputerName = sizeof( szComputerName ) / sizeof( *szComputerName );
+ BOOL ok;
+
+ ok = GetComputerName( szComputerName, &cchComputerName );
+ ASSERT( ok );
+
+ if ( !ok )
+ {
+ // in this case, we'll just add in the local entries, too
+ // under normal circumstances (i.e., as long as the cert db isn't corrupt),
+ // this is harmless and is preferrable to failing to unpack
+ *szComputerName = TEXT( '\0' );
+ }
+
+ RtlAcquireResourceExclusive( &CertDbHeaderListLock, TRUE );
+
+ pchNextProductString = pchProductStrings;
+
+ // these fields are irrelevant!
+ lic.Date = 0;
+ lic.Admin = NULL;
+ lic.Comment = NULL;
+ lic.Vendor = NULL;
+ lic.Source = NULL;
+ lic.AllowedModes = 0;
+
+ iClaimBase = 0;
+ for ( iHeader=0; (DWORD)iHeader < dwNumHeaders; iHeader++ )
+ {
+ if ( 0 != pHeaders[ iHeader ].NumClaims )
+ {
+ // certificate-specific fields
+ lic.Product = pchNextProductString;
+ lic.CertificateID = pHeaders[ iHeader ].CertificateID;
+ lic.MaxQuantity = pHeaders[ iHeader ].MaxQuantity;
+ lic.ExpirationDate = pHeaders[ iHeader ].ExpirationDate;
+
+ for ( iClaim=0; (DWORD)iClaim < pHeaders[ iHeader ].NumClaims; iClaim++ )
+ {
+ if ( lstrcmpi( szComputerName, pClaims[ iClaimBase + iClaim ].ServerName ) )
+ {
+ // not the local server
+
+ // claim-specific field
+ lic.Quantity = pClaims[ iClaimBase + iClaim ].Quantity;
+
+ nt = CertDbClaimEnter( pClaims[ iClaimBase + iClaim ].ServerName, &lic, TRUE, bReplicated ? 0 : pClaims[ iClaimBase + iClaim ].ReplicationDate );
+ ASSERT( STATUS_SUCCESS == nt );
+
+ // even if we encounter an error, go ahead and unpack the rest of the records
+ }
+ }
+
+ iClaimBase += pHeaders[ iHeader ].NumClaims;
+ }
+
+ pchNextProductString += 1 + lstrlen( pchNextProductString );
+ }
+
+ RtlReleaseResource( &CertDbHeaderListLock );
+
+ return nt;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+NTSTATUS CertDbSave()
+
+/*++
+
+Routine Description:
+
+ Save the certificate database.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ STATUS_SUCCESS, Windows error, or NTSTATUS error code.
+
+--*/
+
+{
+ NTSTATUS nt;
+ LLS_CERT_DB_FILE_HEADER FileHeader;
+ DWORD cchProductStrings = 0;
+ LPTSTR pchProductStrings = NULL;
+ DWORD dwNumHeaders = 0;
+ PREPL_CERT_DB_CERTIFICATE_HEADER_0 pHeaders = NULL;
+ DWORD dwNumClaims = 0;
+ PREPL_CERT_DB_CERTIFICATE_CLAIM_0 pClaims = NULL;
+ DWORD dwBytesWritten;
+ BOOL ok;
+
+ nt = CertDbPack( &cchProductStrings, &pchProductStrings, &dwNumHeaders, &pHeaders, &dwNumClaims, &pClaims );
+
+ if ( STATUS_SUCCESS == nt )
+ {
+ if ( dwNumHeaders )
+ {
+ nt = EBlock( pchProductStrings, cchProductStrings * sizeof( TCHAR ) );
+
+ if ( STATUS_SUCCESS == nt )
+ {
+ nt = EBlock( pHeaders, sizeof( REPL_CERT_DB_CERTIFICATE_HEADER_0 ) * dwNumHeaders );
+
+ if ( STATUS_SUCCESS == nt )
+ {
+ nt = EBlock( pClaims, sizeof( REPL_CERT_DB_CERTIFICATE_CLAIM_0 ) * dwNumClaims );
+
+ if ( STATUS_SUCCESS == nt )
+ {
+ if ( NULL != CertDbFile )
+ {
+ CloseHandle( CertDbFile );
+ }
+
+ CertDbFile = LlsFileInit( CertDbFileName, LLS_CERT_DB_FILE_VERSION, sizeof( LLS_CERT_DB_FILE_HEADER ) );
+
+ if ( NULL == CertDbFile )
+ {
+ nt = GetLastError();
+ }
+ else
+ {
+ FileHeader.NumCertificates = dwNumHeaders;
+ FileHeader.ProductStringSize = cchProductStrings;
+ FileHeader.NumClaims = dwNumClaims;
+
+ ok = WriteFile( CertDbFile, &FileHeader, sizeof( FileHeader ), &dwBytesWritten, NULL );
+
+ if ( ok )
+ {
+ ok = WriteFile( CertDbFile, pchProductStrings, FileHeader.ProductStringSize * sizeof( TCHAR ), &dwBytesWritten, NULL );
+
+ if ( ok )
+ {
+ ok = WriteFile( CertDbFile, pHeaders, sizeof( REPL_CERT_DB_CERTIFICATE_HEADER_0 ) * FileHeader.NumCertificates, &dwBytesWritten, NULL );
+
+ if ( ok )
+ {
+ ok = WriteFile( CertDbFile, pClaims, sizeof( REPL_CERT_DB_CERTIFICATE_CLAIM_0 ) * FileHeader.NumClaims, &dwBytesWritten, NULL );
+ }
+ }
+ }
+
+ if ( !ok )
+ {
+ nt = GetLastError();
+ }
+ }
+ }
+ }
+ }
+
+ LocalFree( pchProductStrings );
+ LocalFree( pHeaders );
+ LocalFree( pClaims );
+ }
+ }
+
+ if ( STATUS_SUCCESS != nt )
+ {
+ LogEvent( LLS_EVENT_SAVE_CERT_DB, 0, NULL, nt );
+ }
+
+ return nt;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+NTSTATUS CertDbLoad()
+
+/*++
+
+Routine Description:
+
+ Load the certificate database.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ STATUS_SUCCESS, Windows error, or NTSTATUS error code.
+
+--*/
+
+{
+ NTSTATUS nt = STATUS_SUCCESS;
+ DWORD dwVersion;
+ DWORD dwDataSize;
+ LLS_CERT_DB_FILE_HEADER FileHeader;
+ LPTSTR pchProductStrings = NULL;
+ PREPL_CERT_DB_CERTIFICATE_HEADER_0 pHeaders = NULL;
+ PREPL_CERT_DB_CERTIFICATE_CLAIM_0 pClaims = NULL;
+ DWORD dwBytesRead;
+ BOOL ok;
+
+ if ( NULL != CertDbFile )
+ {
+ CloseHandle( CertDbFile );
+ CertDbFile = NULL;
+ }
+
+ if ( FileExists( CertDbFileName ) )
+ {
+ CertDbFile = LlsFileCheck( CertDbFileName, &dwVersion, &dwDataSize );
+
+ if ( NULL == CertDbFile )
+ {
+ nt = GetLastError();
+ }
+ else if ( ( LLS_CERT_DB_FILE_VERSION != dwVersion )
+ || ( sizeof( FileHeader ) != dwDataSize ) )
+ {
+ nt = STATUS_FILE_INVALID;
+ }
+ else
+ {
+ ok = ReadFile( CertDbFile, &FileHeader, sizeof( FileHeader ), &dwBytesRead, NULL );
+
+ if ( !ok )
+ {
+ nt = GetLastError();
+ }
+ else if ( FileHeader.NumCertificates )
+ {
+ pchProductStrings = LocalAlloc( LMEM_FIXED, sizeof( TCHAR ) * FileHeader.ProductStringSize );
+ pHeaders = LocalAlloc( LMEM_FIXED, sizeof( REPL_CERT_DB_CERTIFICATE_HEADER_0 ) * FileHeader.NumCertificates );
+ pClaims = LocalAlloc( LMEM_FIXED, sizeof( REPL_CERT_DB_CERTIFICATE_CLAIM_0 ) * FileHeader.NumClaims );
+
+ if ( ( NULL == pchProductStrings ) || ( NULL == pHeaders ) || ( NULL == pClaims ) )
+ {
+ ASSERT( FALSE );
+ nt = STATUS_NO_MEMORY;
+ }
+ else
+ {
+ ok = ReadFile( CertDbFile, pchProductStrings, FileHeader.ProductStringSize * sizeof( TCHAR ), &dwBytesRead, NULL );
+
+ if ( ok )
+ {
+ ok = ReadFile( CertDbFile, pHeaders, sizeof( REPL_CERT_DB_CERTIFICATE_HEADER_0 ) * FileHeader.NumCertificates, &dwBytesRead, NULL );
+
+ if ( ok )
+ {
+ ok = ReadFile( CertDbFile, pClaims, sizeof( REPL_CERT_DB_CERTIFICATE_CLAIM_0 ) * FileHeader.NumClaims, &dwBytesRead, NULL );
+ }
+ }
+
+ if ( !ok )
+ {
+ nt = GetLastError();
+ }
+ else
+ {
+ nt = DeBlock( pchProductStrings, sizeof( TCHAR ) * FileHeader.ProductStringSize );
+
+ if ( STATUS_SUCCESS == nt )
+ {
+ nt = DeBlock( pHeaders, sizeof( REPL_CERT_DB_CERTIFICATE_HEADER_0 ) * FileHeader.NumCertificates );
+
+ if ( STATUS_SUCCESS == nt )
+ {
+ nt = DeBlock( pClaims, sizeof( REPL_CERT_DB_CERTIFICATE_CLAIM_0 ) * FileHeader.NumClaims );
+
+ if ( STATUS_SUCCESS == nt )
+ {
+ nt = CertDbUnpack( FileHeader.ProductStringSize,
+ pchProductStrings,
+ FileHeader.NumCertificates,
+ pHeaders,
+ FileHeader.NumClaims,
+ pClaims,
+ FALSE );
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if ( NULL != pchProductStrings ) LocalFree( pchProductStrings );
+ if ( NULL != pHeaders ) LocalFree( pHeaders );
+ if ( NULL != pClaims ) LocalFree( pClaims );
+
+ if ( STATUS_SUCCESS != nt )
+ {
+ LogEvent( LLS_EVENT_LOAD_CERT_DB, 0, NULL, nt );
+ }
+ else
+ {
+ CertDbPrune();
+ }
+
+ return nt;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+NTSTATUS CertDbInit()
+
+/*++
+
+Routine Description:
+
+ Initialize the certificate database.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ STATUS_SUCCESS.
+
+--*/
+
+{
+ RtlInitializeResource( &CertDbHeaderListLock );
+
+ CertDbFile = NULL;
+
+ CertDbHeaderList = NULL;
+ CertDbHeaderListSize = 0;
+
+ return STATUS_SUCCESS;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+void CertDbUpdateLocalClaims()
+
+/*++
+
+Routine Description:
+
+ Synchronize the certificate database with the purchase history.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ DWORD dwPurchaseNdx;
+ LLS_LICENSE_INFO_1 lic;
+ PLICENSE_PURCHASE_RECORD pPurchase;
+
+ RtlAcquireResourceExclusive( &LicenseListLock, TRUE );
+ RtlAcquireResourceExclusive( &CertDbHeaderListLock, TRUE );
+
+ // first dump all current entries for the local server
+ CertDbRemoveLocalClaims();
+
+ // these fields are irrelevant!
+ lic.Date = 0;
+ lic.Admin = NULL;
+ lic.Comment = NULL;
+ lic.Source = NULL;
+ lic.Vendor = NULL;
+ lic.AllowedModes = 0;
+
+ // add in all secure purchases
+ for ( dwPurchaseNdx = 0; dwPurchaseNdx < PurchaseListSize; dwPurchaseNdx++ )
+ {
+ pPurchase = &PurchaseList[ dwPurchaseNdx ];
+
+ if ( 0 != pPurchase->CertificateID )
+ {
+ lic.Product = ( pPurchase->AllowedModes & LLS_LICENSE_MODE_ALLOW_PER_SEAT )
+ ? pPurchase->Service->ServiceName
+ : pPurchase->PerServerService->ServiceName;
+
+ lic.CertificateID = pPurchase->CertificateID;
+ lic.MaxQuantity = pPurchase->MaxQuantity;
+ lic.ExpirationDate = pPurchase->ExpirationDate;
+ lic.Quantity = pPurchase->NumberLicenses;
+
+ CertDbClaimEnter( NULL, &lic, FALSE, 0 );
+ }
+ }
+
+ RtlReleaseResource( &CertDbHeaderListLock );
+ RtlReleaseResource( &LicenseListLock );
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+NTSTATUS CertDbClaimsGet( PLLS_LICENSE_INFO_1 pLicense,
+ LPDWORD pdwNumClaims,
+ PLLS_CERTIFICATE_CLAIM_INFO_0 * ppTargets )
+
+/*++
+
+Routine Description:
+
+ Retrieve a list of all servers with licenses installed from a given
+ certificate and the number of licenses installed on each.
+
+Arguments:
+
+ pLicense (PLLS_LICENSE_INFO_1)
+ License describing the certificate for which the claims are sought.
+ pdwNumClaims (LPDWORD)
+ On return, holds the number of claims in the array pointed to by
+ *ppTargets.
+ ppTargets (PLLS_CERTIFICATE_CLAIM_INFO_0 *)
+ On return, holds an array describing all claims made on this
+ certificate.
+
+Return Value:
+
+ STATUS_SUCCESS
+ STATUS_NOT_FOUND
+ STATUS_NO_MEMORY
+
+--*/
+
+{
+ NTSTATUS nt;
+ PLLS_CERT_DB_CERTIFICATE_HEADER pHeader;
+ int iClaim;
+
+ RtlAcquireResourceShared( &CertDbHeaderListLock, TRUE );
+
+ // is the certificate in the db?
+ pHeader = CertDbHeaderFind( pLicense );
+
+ if ( NULL == pHeader )
+ {
+ // not here!
+ nt = STATUS_NOT_FOUND;
+ }
+ else
+ {
+ *ppTargets = MIDL_user_allocate( pHeader->NumClaims * sizeof( LLS_CERTIFICATE_CLAIM_INFO_0 ) );
+
+ if ( NULL == *ppTargets )
+ {
+ nt = STATUS_NO_MEMORY;
+ }
+ else
+ {
+ *pdwNumClaims = pHeader->NumClaims;
+
+ for ( iClaim=0; (DWORD)iClaim < pHeader->NumClaims; iClaim++ )
+ {
+ lstrcpy( (*ppTargets)[ iClaim ].ServerName, pHeader->Claims[ iClaim ].ServerName );
+ (*ppTargets)[ iClaim ].Quantity = pHeader->Claims[ iClaim ].Quantity;
+ }
+
+ nt = STATUS_SUCCESS;
+ }
+ }
+
+ RtlReleaseResource( &CertDbHeaderListLock );
+
+ return nt;
+}
+
+
+#if DBG
+/////////////////////////////////////////////////////////////////////////
+void CertDbDebugDump()
+
+/*++
+
+Routine Description:
+
+ Dump contents of certificate database to debug console.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ int iHeader;
+ int iClaim;
+
+ RtlAcquireResourceShared( &CertDbHeaderListLock, TRUE );
+
+ for ( iHeader=0; (DWORD)iHeader < CertDbHeaderListSize; iHeader++ )
+ {
+ dprintf( TEXT("\n(%3d) Product : %s\n"), iHeader, CertDbHeaderList[ iHeader ].Product );
+ dprintf( TEXT(" CertificateID : %d\n"), CertDbHeaderList[ iHeader ].CertificateID );
+ dprintf( TEXT(" MaxQuantity : %d\n"), CertDbHeaderList[ iHeader ].MaxQuantity );
+ dprintf( TEXT(" ExpirationDate : %s\n"), TimeToString( CertDbHeaderList[ iHeader ].ExpirationDate ) );
+
+ for ( iClaim=0; (DWORD)iClaim < CertDbHeaderList[ iHeader ].NumClaims; iClaim++ )
+ {
+ dprintf( TEXT("\n (%3d) ServerName : %s\n"), iClaim, CertDbHeaderList[ iHeader ].Claims[ iClaim ].ServerName );
+ dprintf( TEXT(" ReplicationDate : %s\n"), TimeToString( CertDbHeaderList[ iHeader ].Claims[ iClaim ].ReplicationDate ) );
+ dprintf( TEXT(" Quantity : %d\n"), CertDbHeaderList[ iHeader ].Claims[ iClaim ].Quantity );
+ }
+ }
+
+ RtlReleaseResource( &CertDbHeaderListLock );
+
+} // CertDbDebugDump
+
+#endif
diff --git a/private/net/svcdlls/lls/server/certdb.h b/private/net/svcdlls/lls/server/certdb.h
new file mode 100644
index 000000000..9dd169c41
--- /dev/null
+++ b/private/net/svcdlls/lls/server/certdb.h
@@ -0,0 +1,129 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ certdb.h
+
+Abstract:
+
+
+Author:
+
+ Jeff Parham (jeffparh) 16-Nov-1995
+
+Revision History:
+
+--*/
+
+#ifndef _CERTDB_H_
+#define _CERTDB_H_
+
+// maximum time (in seconds) allowed to pass between certificate replications
+// before we remove the apparently no longer used data. this is so that, for
+// example, if a machine goes down (taking its licenses with it), the licenses
+// it had registered won't forever keep the user from reinstalling them.
+#define LLS_CERT_DB_REPLICATION_DATE_DELTA_MAX ( 60 * 60 * 72 )
+
+#define LLS_CERT_DB_FILE_VERSION ( 0x0201 )
+
+typedef struct _LLS_CERT_DB_FILE_HEADER
+{
+ ULONG ProductStringSize;
+ ULONG NumCertificates;
+ ULONG NumClaims;
+} LLS_CERT_DB_FILE_HEADER, *PLLS_CERT_DB_FILE_HEADER;
+
+typedef struct _LLS_CERT_DB_CERTIFICATE_CLAIM
+{
+ TCHAR ServerName[ 1 + MAX_COMPUTERNAME_LENGTH ];
+ DWORD ReplicationDate;
+ LONG Quantity;
+} LLS_CERT_DB_CERTIFICATE_CLAIM, *PLLS_CERT_DB_CERTIFICATE_CLAIM;
+
+typedef struct _LLS_CERT_DB_CERTIFICATE_HEADER
+{
+ LPTSTR Product;
+ DWORD CertificateID;
+ DWORD MaxQuantity;
+ DWORD ExpirationDate;
+
+ DWORD NumClaims;
+ PLLS_CERT_DB_CERTIFICATE_CLAIM Claims;
+} LLS_CERT_DB_CERTIFICATE_HEADER, *PLLS_CERT_DB_CERTIFICATE_HEADER;
+
+
+extern RTL_RESOURCE CertDbHeaderListLock;
+extern PLLS_CERT_DB_CERTIFICATE_HEADER CertDbHeaderList;
+extern DWORD CertDbHeaderListSize;
+extern HANDLE CertDbFile;
+
+
+NTSTATUS
+CertDbInit();
+
+NTSTATUS
+CertDbLoad();
+
+NTSTATUS
+CertDbSave();
+
+void
+CertDbLogViolations();
+
+void
+CertDbPrune();
+
+void
+CertDbRemoveLocalClaims();
+
+void
+CertDbUpdateLocalClaims();
+
+NTSTATUS
+CertDbClaimEnter( LPTSTR pszServerName,
+ PLLS_LICENSE_INFO_1 pLicense,
+ BOOL bIsTotal,
+ DWORD ReplicationDate );
+
+BOOL
+CertDbClaimApprove( PLLS_LICENSE_INFO_1 pLicense );
+
+PLLS_CERT_DB_CERTIFICATE_HEADER
+CertDbHeaderFind( PLLS_LICENSE_INFO_1 pLicense );
+
+PLLS_CERT_DB_CERTIFICATE_HEADER
+CertDbHeaderAdd( PLLS_LICENSE_INFO_1 pLicense );
+
+int
+CertDbClaimFind( PLLS_CERT_DB_CERTIFICATE_HEADER pHeader,
+ LPTSTR pszServerName );
+
+NTSTATUS
+CertDbPack( LPDWORD pcchProductStrings,
+ LPTSTR * ppchProductStrings,
+ LPDWORD pdwNumHeaders,
+ PREPL_CERT_DB_CERTIFICATE_HEADER_0 * ppHeaders,
+ LPDWORD pdwNumClaims,
+ PREPL_CERT_DB_CERTIFICATE_CLAIM_0 * ppClaims );
+
+NTSTATUS
+CertDbUnpack( DWORD cchProductStrings,
+ LPTSTR pchProductStrings,
+ DWORD dwNumHeaders,
+ PREPL_CERT_DB_CERTIFICATE_HEADER_0 pHeaders,
+ DWORD dwNumClaims,
+ PREPL_CERT_DB_CERTIFICATE_CLAIM_0 pClaims,
+ BOOL bReplicated );
+
+NTSTATUS
+CertDbClaimsGet( PLLS_LICENSE_INFO_1 pLicense,
+ LPDWORD pdwNumClaims,
+ PLLS_CERTIFICATE_CLAIM_INFO_0 * ppTargets );
+
+#if DBG
+void CertDbDebugDump();
+#endif
+
+#endif
diff --git a/private/net/svcdlls/lls/server/llssrv.c b/private/net/svcdlls/lls/server/llssrv.c
new file mode 100644
index 000000000..9af40a16a
--- /dev/null
+++ b/private/net/svcdlls/lls/server/llssrv.c
@@ -0,0 +1,1087 @@
+/*++
+
+Copyright (c) 1994 Microsoft Corporation
+
+Module Name:
+
+ Service.c
+
+Abstract:
+
+ Main routine to setup the exception handlers and initialize everything
+ to listen to LPC and RPC port requests.
+
+Author:
+
+ Arthur Hanson (arth) Dec 07, 1994
+
+Environment:
+
+Revision History:
+
+ Jeff Parham (jeffparh) 05-Dec-1995
+ o Added certificate database support.
+ o Expanded file load time (the update limit sent to the service
+ controller) to account for certificate database loading.
+ o Reordered initialization such that the license purchase subsystem
+ is initialized before the service subsystem. (The service
+ subsystem now uses the license subsystem.)
+ o Increased internal version number.
+
+--*/
+
+#include <nt.h>
+#include <ntlsa.h>
+#include <ntsam.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+
+#include <windows.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <process.h>
+#include <tchar.h>
+
+#include <lm.h>
+#include <alertmsg.h>
+
+#include "llsapi.h"
+#include "debug.h"
+#include "llsutil.h"
+#include "llssrv.h"
+#include "service.h"
+#include "registry.h"
+#include "mapping.h"
+#include "msvctbl.h"
+#include "svctbl.h"
+#include "perseat.h"
+#include "purchase.h"
+#include "server.h"
+#include "repl.h"
+#include "scaven.h"
+#include "llsrpc_s.h"
+#include "certdb.h"
+
+
+VOID LLSRpcInit();
+BOOLEAN LLSpLPCInitialize ( VOID );
+
+
+#define INTERNAL_VERSION 0x0006
+
+#define DEFAULT_LICENSE_CHECK_TIME 24
+#define DEFAULT_REPLICATION_TIME 12 * 60 * 60
+
+CONFIG_RECORD ConfigInfo;
+RTL_CRITICAL_SECTION ConfigInfoLock;
+
+
+VOID LoadAll ( );
+
+#if DBG
+DWORD TraceFlags = 0;
+#endif
+
+//
+// this event is signalled when the service should end
+//
+HANDLE hServerStopEvent = NULL;
+TCHAR MyDomain[MAX_COMPUTERNAME_LENGTH + 2];
+ULONG MyDomainSize;
+
+BOOL IsMaster = FALSE;
+
+//
+// Files
+//
+TCHAR MappingFileName[MAX_PATH + 1];
+TCHAR UserFileName[MAX_PATH + 1];
+TCHAR LicenseFileName[MAX_PATH + 1];
+TCHAR CertDbFileName[MAX_PATH + 1];
+
+
+extern SERVICE_STATUS_HANDLE sshStatusHandle;
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTDomainGet(
+ LPTSTR ServerName,
+ LPTSTR Domain
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ static TCHAR Serv[MAX_COMPUTERNAME_LENGTH + 3];
+ UNICODE_STRING us;
+ NTSTATUS ret;
+ OBJECT_ATTRIBUTES oa;
+ ACCESS_MASK am;
+ SECURITY_QUALITY_OF_SERVICE qos;
+ LSA_HANDLE hLSA;
+ PPOLICY_PRIMARY_DOMAIN_INFO pvBuffer;
+
+ lstrcpy(Domain, TEXT(""));
+
+ // only need read access
+ am = POLICY_READ | POLICY_VIEW_LOCAL_INFORMATION;
+
+ // set up quality of service
+ qos.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
+ qos.ImpersonationLevel = SecurityImpersonation;
+ qos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
+ qos.EffectiveOnly = FALSE;
+
+ // Macro sets everything except security field
+ InitializeObjectAttributes( &oa, NULL, 0L, NULL, NULL );
+ oa.SecurityQualityOfService = &qos;
+
+ if ( (ServerName == NULL) || (ServerName[0] == TEXT('\0')) )
+ ret = LsaOpenPolicy(NULL, &oa, am, &hLSA);
+ else {
+ if (ServerName[0] == TEXT('\\'))
+ lstrcpy(Serv, ServerName);
+ else
+ wsprintf(Serv, TEXT("\\\\%s"), ServerName);
+
+ // Set up unicode string structure
+ us.Length = lstrlen(Serv) * sizeof(TCHAR);
+ us.MaximumLength = us.Length + sizeof(TCHAR);
+ us.Buffer = Serv;
+
+ ret = LsaOpenPolicy(&us, &oa, am, &hLSA);
+ }
+
+ if (!ret) {
+ ret = LsaQueryInformationPolicy(hLSA, PolicyPrimaryDomainInformation, (PVOID *) &pvBuffer);
+ LsaClose(hLSA);
+ if ((!ret) && (pvBuffer != NULL)) {
+ lstrcpy(Domain, pvBuffer->Name.Buffer);
+ LsaFreeMemory((PVOID) pvBuffer);
+ }
+ }
+
+ return ret;
+
+} // NTDomainGet
+
+
+/////////////////////////////////////////////////////////////////////////
+BOOL
+NTIsPDC(
+ LPTSTR ServerName
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ static TCHAR Serv[MAX_COMPUTERNAME_LENGTH + 3];
+ UNICODE_STRING us;
+ NTSTATUS ret;
+ OBJECT_ATTRIBUTES oa;
+ ACCESS_MASK am;
+ SECURITY_QUALITY_OF_SERVICE qos;
+ LSA_HANDLE hLSA;
+ PPOLICY_LSA_SERVER_ROLE_INFO pvBuffer;
+ BOOL IsPDC = FALSE;
+
+ if (ServerName[0] == TEXT('\\'))
+ lstrcpy(Serv, ServerName);
+ else
+ wsprintf(Serv, TEXT("\\\\%s"), ServerName);
+
+ // Set up unicode string structure
+ us.Length = lstrlen(Serv) * sizeof(TCHAR);
+ us.MaximumLength = us.Length + sizeof(TCHAR);
+ us.Buffer = Serv;
+
+ // only need read access
+ am = POLICY_READ | POLICY_VIEW_LOCAL_INFORMATION;
+
+ // set up quality of service
+ qos.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
+ qos.ImpersonationLevel = SecurityImpersonation;
+ qos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
+ qos.EffectiveOnly = FALSE;
+
+ // Macro sets everything except security field
+ InitializeObjectAttributes( &oa, NULL, 0L, NULL, NULL );
+ oa.SecurityQualityOfService = &qos;
+
+ ret = LsaOpenPolicy(&us, &oa, am, &hLSA);
+
+ if (!ret) {
+ ret = LsaQueryInformationPolicy(hLSA, PolicyLsaServerRoleInformation, (PVOID *) &pvBuffer);
+ LsaClose(hLSA);
+ if ((!ret) && (pvBuffer != NULL)) {
+ if (pvBuffer->LsaServerRole == PolicyServerRolePrimary)
+ IsPDC = TRUE;
+
+ LsaFreeMemory((PVOID) pvBuffer);
+ }
+ }
+
+ return IsPDC;
+
+} // NTIsPDC
+
+
+/////////////////////////////////////////////////////////////////////////
+DWORD
+LlsTimeGet(
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ Seconds since midnight.
+
+--*/
+
+{
+ DWORD Seconds;
+ SYSTEMTIME SysTime;
+
+ GetLocalTime(&SysTime);
+
+ Seconds = (SysTime.wHour * 24 * 60) + (SysTime.wMinute * 60) + (SysTime.wSecond);
+ return Seconds;
+
+} // LlsTimeGet
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+ConfigInfoRegistryUpdate( )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ DWORD ReplicationType, ReplicationTime;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: ConfigInfoRegistryUpdate\n"));
+#endif
+
+ RtlEnterCriticalSection(&ConfigInfoLock);
+
+ //
+ // Update values from Registry
+ //
+ ReplicationTime = ConfigInfo.ReplicationTime;
+ ReplicationType = ConfigInfo.ReplicationType;
+ ConfigInfoRegistryInit( &ConfigInfo.UseEnterprise, ConfigInfo.EnterpriseServer,
+ &ConfigInfo.ReplicationType, &ConfigInfo.ReplicationTime,
+ &ConfigInfo.LogLevel );
+
+ if ( (ConfigInfo.ReplicationTime == 0) && (LLS_REPLICATION_TYPE_TIME != ConfigInfo.ReplicationType) )
+ ConfigInfo.ReplicationTime = DEFAULT_REPLICATION_TIME;
+
+ //
+ // Adjust replication time if it has changed
+ //
+ if ((ReplicationTime != ConfigInfo.ReplicationTime) || (ReplicationType != ConfigInfo.ReplicationType))
+ ReplicationTimeSet();
+
+ RtlLeaveCriticalSection(&ConfigInfoLock);
+
+} // ConfigInfoRegistryUpdate
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+ConfigInfoUpdate( )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ BOOL InDomain = FALSE;
+ BOOL IsPDC = FALSE;
+ USHORT cbTotalAvail, cbBuffer;
+ LPBYTE pbBuffer;
+ NET_API_STATUS uRet;
+ PSERVER_INFO_101 pServer1;
+ DWORD ReplicationType, ReplicationTime;
+ TCHAR pDomain[MAX_COMPUTERNAME_LENGTH + 1];
+ NT_PRODUCT_TYPE NtType;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: ConfigInfoUpdate\n"));
+#endif
+ //
+ // Try to get a domain
+ //
+ lstrcpy(pDomain, TEXT(""));
+ if ( !NTDomainGet(NULL, pDomain) ) {
+ InDomain = TRUE;
+
+ //
+ // If we aren't a BDC/PDC then count us as a member
+ //
+ NtType = NtProductLanManNt;
+ RtlGetNtProductType(&NtType);
+ if (NtType != NtProductLanManNt)
+ IsPDC = FALSE;
+ else {
+ //
+ // Let's check if we are a PDC...
+ //
+ IsPDC = NTIsPDC(ConfigInfo.ComputerName);
+ }
+
+ } else {
+ IsPDC = TRUE;
+ InDomain = FALSE;
+ }
+
+ RtlEnterCriticalSection(&ConfigInfoLock);
+
+ ConfigInfo.IsMaster = TRUE;
+ ConfigInfo.Replicate = FALSE;
+
+ //
+ // If we are in a domain, and not the PDC then we replicate to the PDC
+ //
+ if (!IsPDC && InDomain) {
+ //
+ // Get the PDC of the domain
+ //
+ uRet = NetGetDCName(NULL, pDomain, &pbBuffer);
+ if (uRet == 0) {
+ lstrcpy(ConfigInfo.ReplicateTo, (LPWSTR) pbBuffer);
+ NetApiBufferFree(pbBuffer);
+ ConfigInfo.IsMaster = FALSE;
+ ConfigInfo.Replicate = TRUE;
+ } else {
+ InDomain = FALSE;
+ memset(ConfigInfo.ReplicateTo, 0, sizeof(ConfigInfo.ReplicateTo));
+#if DBG
+ dprintf(TEXT("LLS: (WARNING) NetGetDCName: 0x%lX\n"), uRet);
+#endif
+ }
+ }
+
+ //
+ // Update values from Registry
+ //
+ ReplicationTime = ConfigInfo.ReplicationTime;
+ ReplicationType = ConfigInfo.ReplicationType;
+ ConfigInfoRegistryInit( &ConfigInfo.UseEnterprise, ConfigInfo.EnterpriseServer,
+ &ConfigInfo.ReplicationType, &ConfigInfo.ReplicationTime,
+ &ConfigInfo.LogLevel );
+
+ //
+ // Have all registy init'd values - now need to figure out who to
+ // replicate to.
+ //
+ // If we are not in a domain or are a PDC then we can go to the
+ // Enterprise Server.
+ //
+ if (IsPDC || !InDomain) {
+ if (ConfigInfo.UseEnterprise) {
+ ConfigInfo.IsMaster = FALSE;
+ ConfigInfo.Replicate = TRUE;
+
+ //
+ // Make sure we have an enterprise server to go to
+ //
+ if ( ConfigInfo.EnterpriseServer[0] == TEXT('\0') ) {
+ ConfigInfo.UseEnterprise = FALSE;
+ ConfigInfo.IsMaster = TRUE;
+ ConfigInfo.Replicate = FALSE;
+ } else {
+ //
+ // Base ReplicateTo on enterprise server name
+ //
+ if (ConfigInfo.EnterpriseServer[0] != TEXT('\\'))
+ lstrcpy(ConfigInfo.ReplicateTo, TEXT("\\\\"));
+ else
+ lstrcpy(ConfigInfo.ReplicateTo, TEXT(""));
+
+ lstrcat(ConfigInfo.ReplicateTo, ConfigInfo.EnterpriseServer);
+ }
+ } else
+ ConfigInfo.IsMaster = TRUE;
+ } else
+ ConfigInfo.UseEnterprise = FALSE;
+
+ if (ConfigInfo.IsMaster == FALSE) {
+ if ( (ConfigInfo.ReplicateTo == NULL) || (lstrlen(ConfigInfo.ReplicateTo) == 0) ||
+ ( (*ConfigInfo.ReplicateTo == TEXT('\\')) && (lstrlen(ConfigInfo.ReplicateTo) < 3) )) {
+ ConfigInfo.IsMaster = TRUE;
+ ConfigInfo.Replicate = FALSE;
+ }
+ }
+
+ //
+ // Adjust replication time if it has changed
+ //
+ if ((ReplicationTime != ConfigInfo.ReplicationTime) || (ReplicationType != ConfigInfo.ReplicationType))
+ ReplicationTimeSet();
+
+ IsMaster = ConfigInfo.IsMaster;
+ RtlLeaveCriticalSection(&ConfigInfoLock);
+
+} // ConfigInfoUpdate
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+ConfigInfoInit( )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ DWORD Size;
+ TCHAR DataPath[MAX_PATH + 1];
+
+ //
+ // First zero init the memory
+ //
+ memset(&ConfigInfo, 0, sizeof(CONFIG_RECORD));
+
+ ConfigInfo.ComputerName = LocalAlloc(LPTR, (MAX_COMPUTERNAME_LENGTH + 1) * sizeof(TCHAR));
+ ConfigInfo.ReplicateTo = LocalAlloc(LPTR, (MAX_COMPUTERNAME_LENGTH + 3) * sizeof(TCHAR));
+ ConfigInfo.EnterpriseServer = LocalAlloc(LPTR, (MAX_COMPUTERNAME_LENGTH + 3) * sizeof(TCHAR));
+ ConfigInfo.SystemDir = LocalAlloc(LPTR, (MAX_PATH + 1) * sizeof(TCHAR));
+
+ if ((ConfigInfo.ComputerName == NULL) || (ConfigInfo.ReplicateTo == NULL) || (ConfigInfo.EnterpriseServer == NULL) || (ConfigInfo.SystemDir == NULL) ) {
+ ASSERT(FALSE);
+ }
+
+ ConfigInfo.Version = INTERNAL_VERSION;
+ GetLocalTime(&ConfigInfo.Started);
+
+ //
+ // LastReplicated is just for display, LlsReplTime is what is used to
+ // Calculate it.
+ GetLocalTime(&ConfigInfo.LastReplicated);
+ ConfigInfo.LastReplicatedSeconds = DateSystemGet();
+
+ GetSystemDirectory(ConfigInfo.SystemDir, MAX_PATH);
+ lstrcat(ConfigInfo.SystemDir, TEXT("\\"));
+
+ ConfigInfo.IsMaster = TRUE;
+
+ ConfigInfo.Replicate = FALSE;
+ ConfigInfo.IsReplicating = FALSE;
+
+ ConfigInfo.ReplicationType = REPLICATE_DELTA;
+ ConfigInfo.ReplicationTime = DEFAULT_REPLICATION_TIME;
+
+ Size = MAX_COMPUTERNAME_LENGTH + 1;
+ GetComputerName(ConfigInfo.ComputerName, &Size);
+ NTDomainGet( ConfigInfo.ComputerName, MyDomain);
+ lstrcat(MyDomain, TEXT("\\"));
+ MyDomainSize = (lstrlen(MyDomain) + 1) * sizeof(TCHAR);
+
+ RtlInitializeCriticalSection(&ConfigInfoLock);
+
+ ConfigInfoUpdate();
+
+ //
+ // Create File paths
+ //
+ lstrcpy(MappingFileName, ConfigInfo.SystemDir);
+ lstrcat(MappingFileName, TEXT(LLS_FILE_SUBDIR));
+ lstrcat(MappingFileName, TEXT("\\"));
+ lstrcat(MappingFileName, TEXT(MAP_FILE_NAME));
+
+ lstrcpy(UserFileName, ConfigInfo.SystemDir);
+ lstrcat(UserFileName, TEXT(LLS_FILE_SUBDIR));
+ lstrcat(UserFileName, TEXT("\\"));
+ lstrcat(UserFileName, TEXT(USER_FILE_NAME));
+
+ lstrcpy(CertDbFileName, ConfigInfo.SystemDir);
+ lstrcat(CertDbFileName, TEXT(LLS_FILE_SUBDIR));
+ lstrcat(CertDbFileName, TEXT("\\"));
+ lstrcat(CertDbFileName, TEXT(CERT_DB_FILE_NAME));
+
+ lstrcpy(LicenseFileName, ConfigInfo.SystemDir);
+ lstrcat(LicenseFileName, TEXT(LICENSE_FILE_NAME));
+
+ //
+ // Make sure our directory is there.
+ //
+ lstrcpy(DataPath, ConfigInfo.SystemDir);
+ lstrcat(DataPath, TEXT(LLS_FILE_SUBDIR));
+ CreateDirectory(DataPath, NULL);
+
+} // ConfigInfoInit
+
+
+/////////////////////////////////////////////////////////////////////////
+DWORD WINAPI
+LLSTopLevelExceptionHandler(
+ struct _EXCEPTION_POINTERS *ExceptionInfo
+ )
+
+/*++
+
+Routine Description:
+
+ The Top Level exception filter for LLSMain.exe.
+
+ This ensures the entire process will be cleaned up if any of
+ the threads fail. Since LLSMain.exe is a distributed application,
+ it is better to fail the entire process than allow random threads
+ to continue executing.
+
+Arguments:
+
+ ExceptionInfo - Identifies the exception that occurred.
+
+
+Return Values:
+
+ EXCEPTION_EXECUTE_HANDLER - Terminate the process.
+
+ EXCEPTION_CONTINUE_SEARCH - Continue processing as though this filter
+ was never called.
+
+
+--*/
+{
+ HANDLE hModule;
+
+
+ //
+ // Raise an alert
+ //
+
+ hModule = LoadLibraryA("netapi32");
+
+ if ( hModule != NULL ) {
+ NET_API_STATUS (NET_API_FUNCTION *NetAlertRaiseExFunction)
+ (LPTSTR, LPVOID, DWORD, LPTSTR);
+
+
+ NetAlertRaiseExFunction =
+ (NET_API_STATUS (NET_API_FUNCTION *) (LPTSTR, LPVOID, DWORD, LPTSTR))
+ GetProcAddress(hModule, "NetAlertRaiseEx");
+
+ if ( NetAlertRaiseExFunction != NULL ) {
+ NTSTATUS Status;
+ UNICODE_STRING Strings;
+
+ char message[ALERTSZ + sizeof(ADMIN_OTHER_INFO)];
+ PADMIN_OTHER_INFO admin = (PADMIN_OTHER_INFO) message;
+
+ //
+ // Build the variable data
+ //
+
+ admin->alrtad_errcode = ALERT_UnhandledException;
+ admin->alrtad_numstrings = 0;
+
+ Strings.Buffer = (LPWSTR) ALERT_VAR_DATA(admin);
+ Strings.Length = 0;
+ Strings.MaximumLength = ALERTSZ;
+
+ Status = RtlIntegerToUnicodeString(
+ (ULONG)ExceptionInfo->ExceptionRecord->ExceptionCode,
+ 16,
+ &Strings );
+
+ if ( NT_SUCCESS(Status) ) {
+ if ( Strings.Length + sizeof(WCHAR) >= Strings.MaximumLength) {
+ Status = STATUS_BUFFER_TOO_SMALL;
+ } else {
+ admin->alrtad_numstrings++;
+ *(Strings.Buffer+(Strings.Length/sizeof(WCHAR))) = L'\0';
+ Strings.Length += sizeof(WCHAR);
+
+ Status = RtlAppendUnicodeToString( &Strings, L"LLS" );
+ }
+
+ }
+
+ if ( NT_SUCCESS(Status) ) {
+ if ( Strings.Length + sizeof(WCHAR) >= Strings.MaximumLength) {
+ Status = STATUS_BUFFER_TOO_SMALL;
+ } else {
+ admin->alrtad_numstrings++;
+ *(Strings.Buffer+(Strings.Length/sizeof(WCHAR))) = L'\0';
+ Strings.Buffer += (Strings.Length/sizeof(WCHAR)) + 1;
+ Strings.MaximumLength -= Strings.Length + sizeof(WCHAR);
+ Strings.Length = 0;
+
+ Status = RtlIntegerToUnicodeString(
+ (ULONG)ExceptionInfo->ExceptionRecord->ExceptionAddress,
+ 16,
+ &Strings );
+ }
+
+ }
+
+ if ( NT_SUCCESS(Status) ) {
+ if ( Strings.Length + sizeof(WCHAR) >= Strings.MaximumLength) {
+ Status = STATUS_BUFFER_TOO_SMALL;
+ } else {
+ admin->alrtad_numstrings++;
+ *(Strings.Buffer+(Strings.Length/sizeof(WCHAR))) = L'\0';
+ Strings.Buffer += (Strings.Length/sizeof(WCHAR)) + 1;
+
+ (VOID) (*NetAlertRaiseExFunction)(
+ ALERT_ADMIN_EVENT,
+ message,
+ (DWORD)((PCHAR)Strings.Buffer -
+ (PCHAR)message),
+ L"LLS" );
+ }
+
+ }
+
+
+ }
+
+ (VOID) FreeLibrary( hModule );
+ }
+
+
+ //
+ // Just continue processing the exception.
+ //
+
+ return EXCEPTION_CONTINUE_SEARCH;
+
+} // LLSTopLevelExceptionHandler
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+ServiceStart (
+ DWORD dwArgc,
+ LPTSTR *lpszArgv
+ )
+/*++
+
+Routine Description:
+
+ The code that starts everything, is really the main().
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ None.
+
+--*/
+{
+ DWORD dwWait;
+ NTSTATUS Status = STATUS_SUCCESS;
+ BOOLEAN EnableAlignmentFaults = TRUE;
+ KPRIORITY BasePriority;
+
+ ///////////////////////////////////////////////////
+ //
+ // Service initialization
+ //
+
+ //
+ // Report the status to the service control manager.
+ //
+ if (!ReportStatusToSCMgr( SERVICE_START_PENDING, NO_ERROR, 3000)) // wait hint
+ goto Cleanup;
+
+ //
+ // Create the event object. The control handler function signals
+ // this event when it receives the "stop" control code.
+ //
+ hServerStopEvent = CreateEvent(
+ NULL, // no security attributes
+ TRUE, // manual reset event
+ FALSE, // not-signalled
+ NULL); // no name
+
+ if ( hServerStopEvent == NULL)
+ goto Cleanup;
+
+ //
+ // Report the status to the service control manager.
+ //
+ if (!ReportStatusToSCMgr( SERVICE_START_PENDING, NO_ERROR, 3000)) // wait hint
+ goto Cleanup;
+
+
+ //
+ // Define a top-level exception handler for the entire process.
+ //
+
+ (VOID) SetErrorMode( SEM_FAILCRITICALERRORS );
+
+ (VOID) SetUnhandledExceptionFilter( &LLSTopLevelExceptionHandler );
+
+ //
+ // Turn on alignment fault fixups. This is necessary because
+ // several structures stored in the registry have qword aligned
+ // fields. They are nicely aligned in our structures, but they
+ // end up being forced out of alignment when being stored because
+ // the registry api require data to be passed following a wierd
+ // length header.
+ //
+
+ Status = NtSetInformationProcess(
+ NtCurrentProcess(),
+ ProcessEnableAlignmentFaultFixup,
+ (PVOID) &EnableAlignmentFaults,
+ sizeof(BOOLEAN)
+ );
+ ASSERT(NT_SUCCESS(Status));
+
+ //
+ // Report the status to the service control manager.
+ //
+ if (!ReportStatusToSCMgr( SERVICE_START_PENDING, NO_ERROR, 3000)) // wait hint
+ goto Cleanup;
+
+ //
+ // Run the LLS in the foreground.
+ //
+ // Several processes which depend on the LLS (like the lanman server)
+ // run in the foreground. If we don't run in the foreground, they'll
+ // starve waiting for us.
+ //
+
+ BasePriority = FOREGROUND_BASE_PRIORITY;
+
+ Status = NtSetInformationProcess(
+ NtCurrentProcess(),
+ ProcessBasePriority,
+ &BasePriority,
+ sizeof(BasePriority)
+ );
+
+ //
+ // Report the status to the service control manager.
+ //
+ if (!ReportStatusToSCMgr( SERVICE_START_PENDING, NO_ERROR, 3000)) // wait hint
+ goto Cleanup;
+
+ // Initialize the Registry values...
+ RegistryInit();
+
+ //
+ // Report the status to the service control manager.
+ //
+ if (!ReportStatusToSCMgr( SERVICE_START_PENDING, NO_ERROR, 3000)) // wait hint
+ goto Cleanup;
+
+ // Initialize the Registry values...
+ ConfigInfoInit();
+
+ //
+ // Report the status to the service control manager.
+ //
+ if (!ReportStatusToSCMgr( SERVICE_START_PENDING, NO_ERROR, 3000)) // wait hint
+ goto Cleanup;
+
+ // Initialize the Service Table
+ LicenseListInit();
+
+ //
+ // Report the status to the service control manager.
+ //
+ if (!ReportStatusToSCMgr( SERVICE_START_PENDING, NO_ERROR, 3000)) // wait hint
+ goto Cleanup;
+
+ // Initialize the Service Table
+ MasterServiceListInit();
+
+ //
+ // Report the status to the service control manager.
+ //
+ if (!ReportStatusToSCMgr( SERVICE_START_PENDING, NO_ERROR, 3000)) // wait hint
+ goto Cleanup;
+
+ // Initialize the Service Table
+ LocalServiceListInit();
+
+ //
+ // Report the status to the service control manager.
+ //
+ if (!ReportStatusToSCMgr( SERVICE_START_PENDING, NO_ERROR, 3000)) // wait hint
+ goto Cleanup;
+
+ // Initialize the Service Table
+ ServiceListInit();
+
+ //
+ // Report the status to the service control manager.
+ //
+ if (!ReportStatusToSCMgr( SERVICE_START_PENDING, NO_ERROR, 3000)) // wait hint
+ goto Cleanup;
+
+ // Initialize the Service Table
+ MappingListInit();
+
+ //
+ // Report the status to the service control manager.
+ //
+ if (!ReportStatusToSCMgr( SERVICE_START_PENDING, NO_ERROR, 3000)) // wait hint
+ goto Cleanup;
+
+ // Initialize the Per-Seat Table
+ UserListInit();
+
+ //
+ // Report the status to the service control manager.
+ //
+ if (!ReportStatusToSCMgr( SERVICE_START_PENDING, NO_ERROR, 3000)) // wait hint
+ goto Cleanup;
+
+ // Initialize the Service Table
+ ServerListInit();
+
+ //
+ // Report the status to the service control manager.
+ //
+ if (!ReportStatusToSCMgr( SERVICE_START_PENDING, NO_ERROR, 3000)) // wait hint
+ goto Cleanup;
+
+ // Initialize the Certificate Database
+ CertDbInit();
+
+ //
+ // Report the status to the service control manager - need a bit longer
+ // to read in all the data files.
+ //
+ if (!ReportStatusToSCMgr( SERVICE_START_PENDING, NO_ERROR, 15000)) // wait hint
+ goto Cleanup;
+
+ // Load data files
+ LoadAll();
+
+ //
+ // Report the status to the service control manager.
+ //
+ if (!ReportStatusToSCMgr( SERVICE_START_PENDING, NO_ERROR, 3000)) // wait hint
+ goto Cleanup;
+
+ // Initialize RPC Stuff...
+ LLSRpcInit();
+
+ //
+ // Report the status to the service control manager.
+ //
+ if (!ReportStatusToSCMgr( SERVICE_START_PENDING, NO_ERROR, 3000)) // wait hint
+ goto Cleanup;
+
+ // Initialize Replication...
+ ReplicationInit();
+
+ //
+ // Report the status to the service control manager.
+ //
+ if (!ReportStatusToSCMgr( SERVICE_START_PENDING, NO_ERROR, 3000)) // wait hint
+ goto Cleanup;
+
+ // Initialize scavenger thread...
+ ScavengerInit();
+
+ //
+ // Report the status to the service control manager.
+ //
+ if (!ReportStatusToSCMgr( SERVICE_START_PENDING, NO_ERROR, 3000)) // wait hint
+ goto Cleanup;
+
+ // Initialize RegistryMonitor thread...
+ RegistryStartMonitor();
+
+ //
+ // End of initialization
+ //
+ ////////////////////////////////////////////////////////
+
+ //
+ // Tell SCM we are up and running!
+ //
+ if (!ReportStatusToSCMgr( SERVICE_RUNNING, NO_ERROR, 0)) // wait hint
+ goto Cleanup;
+
+ ////////////////////////////////////////////////////////
+ //
+ // Service is now running, perform work until shutdown
+ //
+ dwWait = WaitForSingleObject(hServerStopEvent, INFINITE);
+
+Cleanup:
+
+ if (hServerStopEvent)
+ CloseHandle(hServerStopEvent);
+
+ if (sshStatusHandle)
+ ReportStatusToSCMgr( SERVICE_STOPPED, NO_ERROR, 0);
+
+} // ServiceStart
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID ServiceStop()
+/*++
+
+Routine Description:
+
+ Stops the service.
+
+ If a ServiceStop procedure is going to take longer than 3 seconds to
+ execute, it should spawn a thread to execute the stop code, and return.
+ Otherwise, the ServiceControlManager will believe that the service has
+ stopped responding.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ None.
+
+--*/
+{
+ if ( hServerStopEvent )
+ SetEvent(hServerStopEvent);
+} // ServiceStop
+
+
+#if DBG
+/////////////////////////////////////////////////////////////////////////
+VOID
+ConfigInfoDebugDump( )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ RtlEnterCriticalSection(&ConfigInfoLock);
+
+ dprintf(TEXT("License Logging Service - Version: 0x%lX\n"), ConfigInfo.Version);
+ dprintf(TEXT(" Started: %u-%u-%u @ %u:%u:%u\n"),
+ (UINT) ConfigInfo.Started.wDay,
+ (UINT) ConfigInfo.Started.wMonth,
+ (UINT) ConfigInfo.Started.wYear,
+ (UINT) ConfigInfo.Started.wHour,
+ (UINT) ConfigInfo.Started.wMinute,
+ (UINT) ConfigInfo.Started.wSecond );
+
+ dprintf(TEXT(" Replication\n"));
+ dprintf(TEXT(" +--------------+\n"));
+ if (ConfigInfo.IsMaster)
+ dprintf(TEXT(" Master Server\n"));
+ else
+ dprintf(TEXT(" NOT Master Server\n"));
+
+ if (ConfigInfo.Replicate)
+ dprintf(TEXT(" Replicates\n"));
+ else
+ dprintf(TEXT(" Does not Replicate\n"));
+
+ if (ConfigInfo.IsReplicating)
+ dprintf(TEXT(" Currently Replicating\n"));
+ else
+ dprintf(TEXT(" NOT Currently Replicating\n"));
+
+ dprintf(TEXT(" Replicates To: %s\n"), ConfigInfo.ReplicateTo);
+ dprintf(TEXT(" Enterprise Server: %s\n"), ConfigInfo.EnterpriseServer);
+
+ if (ConfigInfo.ReplicationType == REPLICATE_DELTA)
+ dprintf(TEXT(" Replicate Every: %lu Seconds\n"), ConfigInfo.ReplicationTime );
+ else
+ dprintf(TEXT(" Replicate @: %lu\n"), ConfigInfo.ReplicationTime );
+
+ dprintf(TEXT("\n Last Replicated: %u-%u-%u @ %u:%u:%u\n\n"),
+ (UINT) ConfigInfo.LastReplicated.wDay,
+ (UINT) ConfigInfo.LastReplicated.wMonth,
+ (UINT) ConfigInfo.LastReplicated.wYear,
+ (UINT) ConfigInfo.LastReplicated.wHour,
+ (UINT) ConfigInfo.LastReplicated.wMinute,
+ (UINT) ConfigInfo.LastReplicated.wSecond );
+
+ dprintf(TEXT(" Number Servers Currently Replicating: %lu\n"), ConfigInfo.NumReplicating);
+
+ dprintf(TEXT(" Current Backoff Time Delta: %lu\n"), ConfigInfo.BackoffTime);
+
+ dprintf(TEXT(" Current Replication Speed: %lu\n"), ConfigInfo.ReplicationSpeed);
+
+ RtlLeaveCriticalSection(&ConfigInfoLock);
+
+} // ConfigInfoDebugDump
+#endif
diff --git a/private/net/svcdlls/lls/server/llssrv.h b/private/net/svcdlls/lls/server/llssrv.h
new file mode 100644
index 000000000..1601a4f0e
--- /dev/null
+++ b/private/net/svcdlls/lls/server/llssrv.h
@@ -0,0 +1,109 @@
+/*++
+
+Copyright (c) 1994 Microsoft Corporation
+
+Module Name:
+
+ LlsSrv.h
+
+Abstract:
+
+
+Author:
+
+ Arthur Hanson (arth) Dec 07, 1994
+
+Environment:
+
+Revision History:
+
+ Jeff Parham (jeffparh) 05-Dec-1995
+ o Added certificate database support.
+
+--*/
+
+#ifndef _LLS_LLSSRV_H
+#define _LLS_LLSSRV_H
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define MAP_FILE_NAME "LlsMap.LLS"
+#define USER_FILE_NAME "LlsUser.LLS"
+#define LICENSE_FILE_NAME "CPL.CFG"
+#define CERT_DB_FILE_NAME "LlsCert.LLS"
+
+#define LLS_FILE_SUBDIR "LLS"
+
+
+#define REPLICATE_DELTA 0
+#define REPLICATE_AT 1
+
+#define MAX_USERNAME_LENGTH 256
+#define MAX_DOMAINNAME_LENGTH MAX_COMPUTERNAME_LENGTH
+
+
+/////////////////////////////////////////////////////////////////////////
+typedef struct _CONFIG_RECORD {
+ SYSTEMTIME Started;
+ DWORD Version;
+ LPTSTR SystemDir;
+
+ //
+ // Replication Info
+ //
+ LPTSTR ComputerName;
+ LPTSTR ReplicateTo;
+ LPTSTR EnterpriseServer;
+ DWORD EnterpriseServerDate;
+ DWORD LogLevel;
+
+ // When to replicate
+ ULONG ReplicationType;
+ ULONG ReplicationTime;
+ DWORD UseEnterprise;
+
+ DWORD LastReplicatedSeconds;
+ DWORD NextReplication;
+ SYSTEMTIME LastReplicated;
+
+ ULONG NumReplicating; // Number of machines currently replicating here
+ ULONG BackoffTime;
+ ULONG ReplicationSpeed;
+
+ BOOL IsMaster; // TRUE if is a Master Server (top of repl tree).
+ BOOL Replicate; // Whether this server replicates
+ BOOL IsReplicating; // TRUE if currently replicating
+} CONFIG_RECORD, *PCONFIG_RECORD;
+
+extern CONFIG_RECORD ConfigInfo;
+extern RTL_CRITICAL_SECTION ConfigInfoLock;
+
+extern TCHAR MyDomain[];
+extern ULONG MyDomainSize;
+
+extern BOOL IsMaster;
+
+extern TCHAR MappingFileName[];
+extern TCHAR UserFileName[];
+extern TCHAR LicenseFileName[];
+extern TCHAR CertDbFileName[];
+
+
+DWORD LlsTimeGet();
+VOID ConfigInfoUpdate();
+VOID ConfigInfoRegistryUpdate( );
+
+/////////////////////////////////////////////////////////////////////////
+#if DBG
+
+VOID ConfigInfoDebugDump();
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/private/net/svcdlls/lls/server/llssrv.rc b/private/net/svcdlls/lls/server/llssrv.rc
new file mode 100644
index 000000000..8fd373a89
--- /dev/null
+++ b/private/net/svcdlls/lls/server/llssrv.rc
@@ -0,0 +1,12 @@
+#include <windows.h>
+#include <ntverp.h>
+
+#define VER_FILETYPE VFT_APP
+#define VER_FILESUBTYPE VFT2_UNKNOWN
+#define VER_FILEDESCRIPTION_STR "Microsoft\256 License Server"
+
+#define VER_INTERNALNAME_STR "llssrv.exe"
+#define VER_ORIGINALFILENAME_STR "llssrv.exe"
+
+#include <common.ver>
+
diff --git a/private/net/svcdlls/lls/server/llsutil.c b/private/net/svcdlls/lls/server/llsutil.c
new file mode 100644
index 000000000..6b52e6791
--- /dev/null
+++ b/private/net/svcdlls/lls/server/llsutil.c
@@ -0,0 +1,982 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ LlsUtil.c
+
+Abstract:
+
+
+Author:
+
+ Arthur Hanson (arth) Dec 07, 1994
+
+Environment:
+
+Revision History:
+
+ Jeff Parham (jeffparh) 12-Jan-1996
+ o Added WinNtBuildNumberGet() to ascertain the Windows NT build number
+ running on a given machine.
+ o Enhanced output of TimeToString().
+
+--*/
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <ntlsa.h>
+
+#include <windows.h>
+#include <stdlib.h>
+#include <crypt.h>
+#include <wchar.h>
+
+#include "debug.h"
+#include "llssrv.h"
+
+
+char HeaderString[] = "License Logging System Data File\x01A";
+#define HEADER_SIZE 34
+
+typedef struct _LLS_FILE_HEADER {
+ char Header[HEADER_SIZE];
+ DWORD Version;
+ DWORD DataSize;
+} LLS_FILE_HEADER, *PLLS_FILE_HEADER;
+
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+EBlock(
+ PVOID Data,
+ ULONG DataSize
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ DATA_KEY PublicKey;
+ CRYPT_BUFFER CryptBuffer;
+
+ //
+ // Init our public key
+ //
+ PublicKey.Length = 4;
+ PublicKey.MaximumLength = 4;
+ PublicKey.Buffer = LocalAlloc(LPTR, 4);
+
+ if (PublicKey.Buffer != NULL) {
+ ((char *) (PublicKey.Buffer))[0] = '7';
+ ((char *) (PublicKey.Buffer))[1] = '7';
+ ((char *) (PublicKey.Buffer))[2] = '7';
+ ((char *) (PublicKey.Buffer))[3] = '7';
+
+ CryptBuffer.Length = DataSize;
+ CryptBuffer.MaximumLength = DataSize;
+ CryptBuffer.Buffer = (PVOID) Data;
+ Status = RtlEncryptData2(&CryptBuffer, &PublicKey);
+
+ LocalFree(PublicKey.Buffer);
+ } else
+ Status = STATUS_NO_MEMORY;
+
+ return Status;
+} // EBlock
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+DeBlock(
+ PVOID Data,
+ ULONG DataSize
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ DATA_KEY PublicKey;
+ CRYPT_BUFFER CryptBuffer;
+
+ //
+ // Init our public key
+ //
+ PublicKey.Length = 4;
+ PublicKey.MaximumLength = 4;
+ PublicKey.Buffer = LocalAlloc(LPTR, 4);
+ if (PublicKey.Buffer != NULL) {
+ ((char *) (PublicKey.Buffer))[0] = '7';
+ ((char *) (PublicKey.Buffer))[1] = '7';
+ ((char *) (PublicKey.Buffer))[2] = '7';
+ ((char *) (PublicKey.Buffer))[3] = '7';
+
+ CryptBuffer.Length = DataSize;
+ CryptBuffer.MaximumLength = DataSize;
+ CryptBuffer.Buffer = (PVOID) Data;
+ Status = RtlDecryptData2(&CryptBuffer, &PublicKey);
+
+ LocalFree(PublicKey.Buffer);
+ } else
+ Status = STATUS_NO_MEMORY;
+
+ return Status;
+} // DeBlock
+
+
+/////////////////////////////////////////////////////////////////////////
+BOOL
+FileExists(
+ LPTSTR FileName
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ return (BOOL) RtlDoesFileExists_U(FileName);
+
+} // FileExists
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+lsplitpath(
+ const TCHAR *path,
+ TCHAR *drive,
+ TCHAR *dir,
+ TCHAR *fname,
+ TCHAR *ext
+ )
+
+/*++
+
+Routine Description:
+ Splits a path name into its individual components
+
+ Took the _splitpath and _makepath routines and converted them to
+ be NT (long file name) and Unicode friendly.
+
+Arguments:
+ Entry:
+ path - pointer to path name to be parsed
+ drive - pointer to buffer for drive component, if any
+ dir - pointer to buffer for subdirectory component, if any
+ fname - pointer to buffer for file base name component, if any
+ ext - pointer to buffer for file name extension component, if any
+
+ Exit:
+ drive - pointer to drive string. Includes ':' if a drive was given.
+ dir - pointer to subdirectory string. Includes leading and
+ trailing '/' or '\', if any.
+ fname - pointer to file base name
+ ext - pointer to file extension, if any. Includes leading '.'.
+
+Return Value:
+
+
+--*/
+
+{
+ TCHAR *p;
+ TCHAR *last_slash = NULL, *dot = NULL;
+ unsigned len;
+
+ // init these so we don't exit with bogus values
+ drive[0] = TEXT('\0');
+ dir[0] = TEXT('\0');
+ fname[0] = TEXT('\0');
+ ext[0] = TEXT('\0');
+
+ if (path[0] == TEXT('\0'))
+ return;
+
+ /*+---------------------------------------------------------------------+
+ | Assume that the path argument has the following form, where any or |
+ | all of the components may be missing. |
+ | |
+ | <drive><dir><fname><ext> |
+ | |
+ | drive: |
+ | 0 to MAX_DRIVE-1 characters, the last of which, if any, is a |
+ | ':' or a '\' in the case of a UNC path. |
+ | dir: |
+ | 0 to _MAX_DIR-1 characters in the form of an absolute path |
+ | (leading '/' or '\') or relative path, the last of which, if |
+ | any, must be a '/' or '\'. E.g - |
+ | |
+ | absolute path: |
+ | \top\next\last\ ; or |
+ | /top/next/last/ |
+ | relative path: |
+ | top\next\last\ ; or |
+ | top/next/last/ |
+ | Mixed use of '/' and '\' within a path is also tolerated |
+ | fname: |
+ | 0 to _MAX_FNAME-1 characters not including the '.' character |
+ | ext: |
+ | 0 to _MAX_EXT-1 characters where, if any, the first must be a |
+ | '.' |
+ +---------------------------------------------------------------------+*/
+
+ // extract drive letter and :, if any
+ if ( path[0] && (path[1] == TEXT(':')) ) {
+ if (drive) {
+ drive[0] = path[0];
+ drive[1] = path[1];
+ drive[2] = TEXT('\0');
+ }
+ path += 2;
+ }
+
+ // if no drive then check for UNC pathname
+ if (drive[0] == TEXT('\0'))
+ if ((path[0] == TEXT('\\')) && (path[1] == TEXT('\\'))) {
+ // got a UNC path so put server-sharename into drive
+ drive[0] = path[0];
+ drive[1] = path[1];
+ path += 2;
+
+ p = &drive[2];
+ while ((*path != TEXT('\0')) && (*path != TEXT('\\')))
+ *p++ = *path++;
+
+ if (*path == TEXT('\0'))
+ return;
+
+ // now sitting at the share - copy this as well (copy slash first)
+ *p++ = *path++;
+ while ((*path != TEXT('\0')) && (*path != TEXT('\\')))
+ *p++ = *path++;
+
+ // tack on terminating NULL
+ *p = TEXT('\0');
+ }
+
+ /*+---------------------------------------------------------------------+
+ | extract path string, if any. Path now points to the first character|
+ | of the path, if any, or the filename or extension, if no path was |
+ | specified. Scan ahead for the last occurence, if any, of a '/' or |
+ | '\' path separator character. If none is found, there is no path. |
+ | We will also note the last '.' character found, if any, to aid in |
+ | handling the extension. |
+ +---------------------------------------------------------------------+*/
+
+ for (last_slash = NULL, p = (TCHAR *)path; *p; p++) {
+ if (*p == TEXT('/') || *p == TEXT('\\'))
+ // point to one beyond for later copy
+ last_slash = p + 1;
+ else if (*p == TEXT('.'))
+ dot = p;
+ }
+
+ if (last_slash) {
+
+ // found a path - copy up through last_slash or max. characters allowed,
+ // whichever is smaller
+ if (dir) {
+ len = __min((last_slash - path), (_MAX_DIR - 1));
+ lstrcpyn(dir, path, len + 1);
+ dir[len] = TEXT('\0');
+ }
+ path = last_slash;
+ }
+
+ /*+---------------------------------------------------------------------+
+ | extract file name and extension, if any. Path now points to the |
+ | first character of the file name, if any, or the extension if no |
+ | file name was given. Dot points to the '.' beginning the extension,|
+ | if any. |
+ +---------------------------------------------------------------------+*/
+
+ if (dot && (dot >= path)) {
+ // found the marker for an extension - copy the file name up to the
+ // '.'.
+ if (fname) {
+ len = __min((dot - path), (_MAX_FNAME - 1));
+ lstrcpyn(fname, path, len + 1);
+ *(fname + len) = TEXT('\0');
+ }
+
+ // now we can get the extension - remember that p still points to the
+ // terminating nul character of path.
+ if (ext) {
+ len = __min((p - dot), (_MAX_EXT - 1));
+ lstrcpyn(ext, dot, len + 1);
+ ext[len] = TEXT('\0');
+ }
+ }
+ else {
+ // found no extension, give empty extension and copy rest of string
+ // into fname.
+ if (fname) {
+ len = __min((p - path), (_MAX_FNAME - 1));
+ lstrcpyn(fname, path, len + 1);
+ fname[len] = TEXT('\0');
+ }
+ if (ext) {
+ *ext = TEXT('\0');
+ }
+ }
+
+} // lsplitpath
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+lmakepath(
+ TCHAR *path,
+ const TCHAR *drive,
+ const TCHAR *dir,
+ const TCHAR *fname,
+ const TCHAR *ext
+ )
+
+/*++
+
+Routine Description:
+ Create a path name from its individual components.
+
+Arguments:
+ Entry:
+ char *path - pointer to buffer for constructed path
+ char *drive - pointer to drive component, may or may not contain
+ trailing ':'
+ char *dir - pointer to subdirectory component, may or may not include
+ leading and/or trailing '/' or '\' characters
+ char *fname - pointer to file base name component
+ char *ext - pointer to extension component, may or may not contain
+ a leading '.'.
+
+ Exit:
+ path - pointer to constructed path name
+
+Return Value:
+
+
+--*/
+
+{
+ const TCHAR *p;
+
+ /*+---------------------------------------------------------------------+
+ | we assume that the arguments are in the following form (although we |
+ | do not diagnose invalid arguments or illegal filenames (such as |
+ | names longer than 8.3 or with illegal characters in them) |
+ | |
+ | drive: |
+ | A or A: |
+ | dir: |
+ | \top\next\last\ ; or |
+ | /top/next/last/ ; or |
+ | |
+ | either of the above forms with either/both the leading and |
+ | trailing / or \ removed. Mixed use of '/' and '\' is also |
+ | tolerated |
+ | fname: |
+ | any valid file name |
+ | ext: |
+ | any valid extension (none if empty or null ) |
+ +---------------------------------------------------------------------+*/
+
+ // copy drive
+ if (drive && *drive)
+ while (*drive)
+ *path++ = *drive++;
+
+ // copy dir
+ if ((p = dir) && *p) {
+ do {
+ *path++ = *p++;
+ }
+ while (*p);
+ if ((*(p-1) != TEXT('/')) && (*(p-1) != TEXT('\\'))) {
+ *path++ = TEXT('\\');
+ }
+ }
+
+ // copy fname
+ if (p = fname) {
+ while (*p) {
+ *path++ = *p++;
+ }
+ }
+
+ // copy ext, including 0-terminator - check to see if a '.' needs to be
+ // inserted.
+ if (p = ext) {
+ if (*p && *p != TEXT('.')) {
+ *path++ = TEXT('.');
+ }
+ while (*path++ = *p++)
+ ;
+ }
+ else {
+ // better add the 0-terminator
+ *path = TEXT('\0');
+ }
+
+} // lmakepath
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+FileBackupCreate(
+ LPTSTR Path
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ HANDLE hFile = NULL;
+ DWORD dwFileNumber = 0;
+ TCHAR Drive[_MAX_DRIVE + 1];
+ TCHAR Dir[_MAX_DIR + 1];
+ TCHAR FileName[_MAX_FNAME + 1];
+ TCHAR Ext[_MAX_EXT + 1];
+ TCHAR NewExt[_MAX_EXT + 1];
+ TCHAR NewPath[MAX_PATH + 1];
+
+ //
+ // Make sure file exists
+ //
+ if (!FileExists(FileName))
+ return;
+
+ //
+ // Split name into constituent parts...
+ //
+ lsplitpath(Path, Drive, Dir, FileName, Ext);
+
+ // Find next backup number...
+ // Files are backed up as .xxx where xxx is a number in the form .001,
+ // the first backup is stored as .001, second as .002, etc...
+ do {
+ //
+ // Create new file name with backup extension
+ //
+ dwFileNumber++;
+ wsprintf(NewExt, TEXT("%03u"), dwFileNumber);
+ lmakepath(NewPath, Drive, Dir, FileName, NewExt);
+
+ } while ( FileExists(NewPath) );
+
+ MoveFile( Path, NewPath );
+
+} // FileBackupCreate
+
+
+/////////////////////////////////////////////////////////////////////////
+HANDLE
+LlsFileInit(
+ LPTSTR FileName,
+ DWORD Version,
+ DWORD DataSize
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ HANDLE hFile = NULL;
+ LLS_FILE_HEADER Header;
+ DWORD BytesWritten;
+
+#ifdef DEBUG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_DATABASE))
+ dprintf(TEXT("LLS TRACE: LlsFileInit\n"));
+#endif
+
+ if (FileName == NULL)
+ return NULL;
+
+ strcpy(Header.Header, HeaderString);
+ Header.Version = Version;
+ Header.DataSize = DataSize;
+
+ SetFileAttributes(FileName, FILE_ATTRIBUTE_NORMAL);
+ hFile = CreateFile(FileName, GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (hFile != INVALID_HANDLE_VALUE) {
+ if (!WriteFile(hFile, &Header, sizeof(LLS_FILE_HEADER), &BytesWritten, NULL)) {
+ CloseHandle(hFile);
+ hFile = NULL;
+ }
+ }
+
+ return hFile;
+} // LlsFileInit
+
+
+/////////////////////////////////////////////////////////////////////////
+HANDLE
+LlsFileCheck(
+ LPTSTR FileName,
+ LPDWORD Version,
+ LPDWORD DataSize
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ BOOL FileOK = FALSE;
+ HANDLE hFile = NULL;
+ LLS_FILE_HEADER Header;
+ DWORD FileSize;
+ DWORD BytesRead;
+
+#ifdef DEBUG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_DATABASE))
+ dprintf(TEXT("LLS TRACE: LlsFileCheck\n"));
+#endif
+
+ if (FileName == NULL)
+ return NULL;
+
+ //
+ // We are assuming the file exists
+ //
+ SetFileAttributes(FileName, FILE_ATTRIBUTE_NORMAL);
+ hFile = CreateFile(FileName, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (hFile != INVALID_HANDLE_VALUE) {
+ FileSize = GetFileSize(hFile, NULL);
+
+ //
+ // Make sure there is enough data there to read
+ //
+ if (FileSize > (sizeof(LLS_FILE_HEADER) + 1)) {
+ if (ReadFile(hFile, &Header, sizeof(LLS_FILE_HEADER), &BytesRead, NULL)) {
+ if ( !_strcmpi(Header.Header, HeaderString) ) {
+ //
+ // Data checks out - so return datalength
+ //
+ *Version = Header.Version;
+ *DataSize = Header.DataSize;
+ FileOK = TRUE;
+ }
+ }
+ }
+
+ //
+ // If we opened the file and something was wrong - close it.
+ //
+ if (!FileOK) {
+ CloseHandle(hFile);
+ hFile = NULL;
+ }
+ }
+
+ return hFile;
+
+} // LlsFileCheck
+
+
+/////////////////////////////////////////////////////////////////////////
+DWORD
+DateSystemGet(
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ Seconds since midnight.
+
+--*/
+
+{
+ DWORD Seconds;
+ LARGE_INTEGER SysTime;
+
+ NtQuerySystemTime(&SysTime);
+ RtlTimeToSecondsSince1980(&SysTime, &Seconds);
+ return Seconds;
+
+} // DateSystemGet
+
+
+/////////////////////////////////////////////////////////////////////////
+DWORD
+DateLocalGet(
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ Seconds since midnight.
+
+--*/
+
+{
+ DWORD Seconds;
+ LARGE_INTEGER SysTime, LocalTime;
+
+ NtQuerySystemTime(&SysTime);
+ RtlSystemTimeToLocalTime(&SysTime, &LocalTime);
+ RtlTimeToSecondsSince1980(&LocalTime, &Seconds);
+ return Seconds;
+
+} // DateLocalGet
+
+
+/////////////////////////////////////////////////////////////////////////
+DWORD
+InAWorkgroup(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This function determines whether we are a member of a domain, or of
+ a workgroup. First it checks to make sure we're running on a Windows NT
+ system (otherwise we're obviously in a domain) and if so, queries LSA
+ to get the Primary domain SID, if this is NULL, we're in a workgroup.
+
+ If we fail for some random unexpected reason, we'll pretend we're in a
+ workgroup (it's more restrictive).
+
+Arguments:
+ None
+
+Return Value:
+
+ TRUE - We're in a workgroup
+ FALSE - We're in a domain
+
+--*/
+{
+ NT_PRODUCT_TYPE ProductType;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ LSA_HANDLE Handle;
+ NTSTATUS Status;
+ PPOLICY_PRIMARY_DOMAIN_INFO PolicyPrimaryDomainInfo = NULL;
+ DWORD Result = FALSE;
+
+
+ Status = RtlGetNtProductType(&ProductType);
+
+ if (!NT_SUCCESS(Status)) {
+#if DBG
+ dprintf(TEXT("ERROR LLS Could not get Product type\n"));
+#endif
+ return TRUE;
+ }
+
+ if (ProductType == NtProductLanManNt) {
+ return(FALSE);
+ }
+
+ InitializeObjectAttributes(&ObjectAttributes, NULL, 0, 0, NULL);
+
+ Status = LsaOpenPolicy(NULL,
+ &ObjectAttributes,
+ POLICY_VIEW_LOCAL_INFORMATION,
+ &Handle);
+
+ if (!NT_SUCCESS(Status)) {
+#if DBG
+ dprintf(TEXT("ERROR LLS: Could not open LSA Policy Database\n"));
+#endif
+ return TRUE;
+ }
+
+ Status = LsaQueryInformationPolicy(Handle, PolicyPrimaryDomainInformation,
+ (PVOID *) &PolicyPrimaryDomainInfo);
+
+ if (NT_SUCCESS(Status)) {
+
+ if (PolicyPrimaryDomainInfo->Sid == NULL) {
+ Result = TRUE;
+ }
+ else {
+ Result = FALSE;
+ }
+ }
+
+ if (PolicyPrimaryDomainInfo) {
+ LsaFreeMemory((PVOID)PolicyPrimaryDomainInfo);
+ }
+
+ LsaClose(Handle);
+
+ return(Result);
+} // InAWorkgroup
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+LogEvent(
+ DWORD MessageId,
+ DWORD NumberOfSubStrings,
+ LPWSTR *SubStrings,
+ DWORD ErrorCode
+ )
+{
+
+ HANDLE LogHandle;
+ WORD wEventType;
+
+ LogHandle = RegisterEventSourceW (
+ NULL,
+ TEXT("LicenseService")
+ );
+
+ if (LogHandle == NULL) {
+#if DBG
+ dprintf(TEXT("LLS RegisterEventSourceW failed %lu\n"), GetLastError());
+#endif
+ return;
+ }
+
+ switch ( MessageId >> 30 )
+ {
+ case STATUS_SEVERITY_INFORMATIONAL:
+ case STATUS_SEVERITY_SUCCESS:
+ wEventType = EVENTLOG_INFORMATION_TYPE;
+ break;
+ case STATUS_SEVERITY_WARNING:
+ wEventType = EVENTLOG_WARNING_TYPE;
+ break;
+ case STATUS_SEVERITY_ERROR:
+ default:
+ wEventType = EVENTLOG_ERROR_TYPE;
+ break;
+ }
+
+ if (ErrorCode == ERROR_SUCCESS) {
+
+ //
+ // No error codes were specified
+ //
+ (void) ReportEventW(
+ LogHandle,
+ wEventType,
+ 0, // event category
+ MessageId,
+ NULL,
+ (WORD)NumberOfSubStrings,
+ 0,
+ SubStrings,
+ (PVOID) NULL
+ );
+
+ }
+ else {
+
+ //
+ // Log the error code specified
+ //
+ (void) ReportEventW(
+ LogHandle,
+ wEventType,
+ 0, // event category
+ MessageId,
+ NULL,
+ (WORD)NumberOfSubStrings,
+ sizeof(DWORD),
+ SubStrings,
+ (PVOID) &ErrorCode
+ );
+ }
+
+ DeregisterEventSource(LogHandle);
+} // LogEvent
+
+
+/////////////////////////////////////////////////////////////////////////
+DWORD WinNtBuildNumberGet( LPTSTR pszServerName, LPDWORD pdwBuildNumber )
+
+/*++
+
+Routine Description:
+
+ Retrieve the build number of Windows NT running on a given machine.
+
+Arguments:
+
+ pszServerName (LPTSTR)
+ Name of the server to check.
+ pdwBuildNumber (LPDWORD)
+ On return, holds the build number of the server (e.g., 1057 for the
+ release version of Windows NT 3.51).
+
+Return Value:
+
+ ERROR_SUCCESS or Win error code.
+
+--*/
+
+{
+ LONG lError;
+ HKEY hKeyLocalMachine;
+
+ lError = RegConnectRegistry( pszServerName, HKEY_LOCAL_MACHINE, &hKeyLocalMachine );
+
+ if ( ERROR_SUCCESS != lError )
+ {
+#if DBG
+ dprintf( TEXT("WinNtBuildNumberGet(): Could not connect to remote registry, error %ld.\n"), lError );
+#endif
+ }
+ else
+ {
+ HKEY hKeyCurrentVersion;
+
+ lError = RegOpenKeyEx( hKeyLocalMachine,
+ TEXT( "Software\\Microsoft\\Windows NT\\CurrentVersion" ),
+ 0,
+ KEY_READ,
+ &hKeyCurrentVersion );
+
+ if ( ERROR_SUCCESS != lError )
+ {
+#if DBG
+ dprintf( TEXT("WinNtBuildNumberGet(): Could not open key, error %ld.\n"), lError );
+#endif
+ }
+ else
+ {
+ DWORD dwType;
+ TCHAR szWinNtBuildNumber[ 16 ];
+ DWORD cbWinNtBuildNumber = sizeof( szWinNtBuildNumber );
+
+ lError = RegQueryValueEx( hKeyCurrentVersion,
+ TEXT( "CurrentBuildNumber" ),
+ NULL,
+ &dwType,
+ (LPBYTE) &szWinNtBuildNumber,
+ &cbWinNtBuildNumber );
+
+ if ( ERROR_SUCCESS != lError )
+ {
+#if DBG
+ dprintf( TEXT("WinNtBuildNumberGet(): Could not query value, error %ld.\n"), lError );
+#endif
+ }
+ else
+ {
+ *pdwBuildNumber = (DWORD) _wtol( szWinNtBuildNumber );
+ }
+
+ RegCloseKey( hKeyCurrentVersion );
+ }
+
+ RegCloseKey( hKeyLocalMachine );
+ }
+
+ return (DWORD) lError;
+}
+
+
+#if DBG
+
+/////////////////////////////////////////////////////////////////////////
+LPTSTR
+TimeToString(
+ ULONG Seconds
+ )
+{
+ TIME_FIELDS tf;
+ LARGE_INTEGER Time, LTime;
+ static TCHAR TimeString[100];
+
+ if ( 0 == Seconds )
+ {
+ lstrcpy(TimeString, TEXT("None"));
+ }
+ else
+ {
+ RtlSecondsSince1980ToTime(Seconds, &Time);
+ RtlSystemTimeToLocalTime(&Time, &LTime);
+ RtlTimeToTimeFields(&LTime, &tf);
+
+ wsprintf(TimeString, TEXT("%02hd/%02hd/%04hd @ %02hd:%02hd:%02hd"), tf.Month, tf.Day, tf.Year, tf.Hour, tf.Minute, tf.Second);
+ }
+
+ return TimeString;
+
+} // TimeToString
+
+#endif
+
diff --git a/private/net/svcdlls/lls/server/llsutil.h b/private/net/svcdlls/lls/server/llsutil.h
new file mode 100644
index 000000000..ae4feb24a
--- /dev/null
+++ b/private/net/svcdlls/lls/server/llsutil.h
@@ -0,0 +1,63 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ LlsUtil.h
+
+Abstract:
+
+
+Author:
+
+ Arthur Hanson (arth) Dec 07, 1994
+
+Environment:
+
+Revision History:
+
+ Jeff Parham (jeffparh) 12-Jan-1996
+ o Added WinNtBuildNumberGet() to ascertain the Windows NT build number
+ running on a given machine.
+
+--*/
+
+
+#ifndef _LLS_LLSUTIL_H
+#define _LLS_LLSUTIL_H
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+NTSTATUS EBlock( PVOID Data, ULONG DataSize );
+NTSTATUS DeBlock( PVOID Data, ULONG DataSize );
+
+BOOL FileExists( LPTSTR FileName );
+VOID lsplitpath( const TCHAR *path, TCHAR *drive, TCHAR *dir, TCHAR *fname, TCHAR *ext );
+VOID lmakepath( TCHAR *path, const TCHAR *drive, const TCHAR *dir, const TCHAR *fname, const TCHAR *ext );
+VOID FileBackupCreate( LPTSTR Path );
+HANDLE LlsFileInit( LPTSTR FileName, DWORD Version, DWORD DataSize );
+HANDLE LlsFileCheck( LPTSTR FileName, LPDWORD Version, LPDWORD DataSize );
+
+DWORD DateSystemGet( );
+DWORD DateLocalGet( );
+DWORD InAWorkgroup( VOID );
+VOID LogEvent( DWORD MessageId, DWORD NumberOfSubStrings, LPWSTR *SubStrings, DWORD ErrorCode );
+
+DWORD WinNtBuildNumberGet( LPTSTR pszServerName, LPDWORD pdwBuildNumber );
+
+#if DBG
+
+LPTSTR TimeToString( ULONG Seconds );
+
+#endif
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/private/net/svcdlls/lls/server/makefile b/private/net/svcdlls/lls/server/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/net/svcdlls/lls/server/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/net/svcdlls/lls/server/makefile.inc b/private/net/svcdlls/lls/server/makefile.inc
new file mode 100644
index 000000000..fc603fc9b
--- /dev/null
+++ b/private/net/svcdlls/lls/server/makefile.inc
@@ -0,0 +1 @@
+obj\$(TARGET_DIRECTORY)\llssrv.res: $(SOURCES_PATH)llssrv.rc
diff --git a/private/net/svcdlls/lls/server/mapping.c b/private/net/svcdlls/lls/server/mapping.c
new file mode 100644
index 000000000..fe3dc4645
--- /dev/null
+++ b/private/net/svcdlls/lls/server/mapping.c
@@ -0,0 +1,756 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ Mapping.c
+
+Abstract:
+
+
+Author:
+
+ Arthur Hanson (arth) Dec 07, 1994
+
+Environment:
+
+Revision History:
+
+--*/
+
+#include <stdlib.h>
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+
+#include <windows.h>
+
+#include "debug.h"
+#include "llsutil.h"
+#include "llssrv.h"
+#include "mapping.h"
+#include "msvctbl.h"
+#include "svctbl.h"
+#include "perseat.h"
+
+#define NO_LLS_APIS
+#include "llsapi.h"
+
+
+ULONG MappingListSize = 0;
+PMAPPING_RECORD *MappingList = NULL;
+RTL_RESOURCE MappingListLock;
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+MappingListInit()
+
+/*++
+
+Routine Description:
+
+
+ The table is linear so a binary search can be used on the table. We
+ assume that adding new Mappings is a relatively rare occurance, since
+ we need to sort it each time.
+
+ The Mapping table is guarded by a read and write semaphore. Multiple
+ reads can occur, but a write blocks everything.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PMAPPING_RECORD Mapping;
+
+ RtlInitializeResource(&MappingListLock);
+
+
+} // MappingListInit
+
+
+/////////////////////////////////////////////////////////////////////////
+int __cdecl MappingListCompare(const void *arg1, const void *arg2) {
+ PMAPPING_RECORD Svc1, Svc2;
+
+ Svc1 = (PMAPPING_RECORD) *((PMAPPING_RECORD *) arg1);
+ Svc2 = (PMAPPING_RECORD) *((PMAPPING_RECORD *) arg2);
+
+ return lstrcmpi( Svc1->Name, Svc2->Name);
+
+} // MappingListCompare
+
+
+/////////////////////////////////////////////////////////////////////////
+int __cdecl MappingUserListCompare(const void *arg1, const void *arg2) {
+ LPTSTR User1, User2;
+
+ User1 = (LPTSTR) *((LPTSTR *) arg1);
+ User2 = (LPTSTR) *((LPTSTR *) arg2);
+
+ return lstrcmpi( User1, User2);
+
+} // MappingUserListCompare
+
+
+/////////////////////////////////////////////////////////////////////////
+PMAPPING_RECORD
+MappingListFind(
+ LPTSTR MappingName
+ )
+
+/*++
+
+Routine Description:
+
+ Internal routine to actually do binary search on MappingList, this
+ does not do any locking as we expect the wrapper routine to do this.
+ The search is a simple binary search.
+
+Arguments:
+
+ MappingName -
+
+Return Value:
+
+ Pointer to found Mapping table entry or NULL if not found.
+
+--*/
+
+{
+ LONG begin = 0;
+ LONG end = (LONG) MappingListSize - 1;
+ LONG cur;
+ int match;
+ PMAPPING_RECORD Mapping;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: MappingListFind\n"));
+#endif
+
+ if ((MappingName == NULL) || (MappingListSize == 0))
+ return NULL;
+
+ while (end >= begin) {
+ // go halfway in-between
+ cur = (begin + end) / 2;
+ Mapping = MappingList[cur];
+
+ // compare the two result into match
+ match = lstrcmpi(MappingName, Mapping->Name);
+
+ if (match < 0)
+ // move new begin
+ end = cur - 1;
+ else
+ begin = cur + 1;
+
+ if (match == 0)
+ return Mapping;
+ }
+
+ return NULL;
+
+} // MappingListFind
+
+
+/////////////////////////////////////////////////////////////////////////
+LPTSTR
+MappingUserListFind(
+ LPTSTR User,
+ ULONG NumEntries,
+ LPTSTR *Users
+ )
+
+/*++
+
+Routine Description:
+
+ Internal routine to actually do binary search on MasterServiceList, this
+ does not do any locking as we expect the wrapper routine to do this.
+ The search is a simple binary search.
+
+Arguments:
+
+ ServiceName -
+
+Return Value:
+
+ Pointer to found service table entry or NULL if not found.
+
+--*/
+
+{
+ LONG begin = 0;
+ LONG end;
+ LONG cur;
+ int match;
+ LPTSTR pUser;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: MappingUserListFind\n"));
+#endif
+
+ if (NumEntries == 0)
+ return NULL;
+
+ end = (LONG) NumEntries - 1;
+
+ while (end >= begin) {
+ // go halfway in-between
+ cur = (begin + end) / 2;
+ pUser = Users[cur];
+
+ // compare the two result into match
+ match = lstrcmpi(User, pUser);
+
+ if (match < 0)
+ // move new begin
+ end = cur - 1;
+ else
+ begin = cur + 1;
+
+ if (match == 0)
+ return pUser;
+ }
+
+ return NULL;
+
+} // MappingUserListFind
+
+
+/////////////////////////////////////////////////////////////////////////
+PMAPPING_RECORD
+MappingListUserFind( LPTSTR User )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ ULONG i = 0;
+ PMAPPING_RECORD pMap = NULL;
+
+ //
+ // Need to scan list so get read access.
+ //
+ RtlAcquireResourceShared(&MappingListLock, TRUE);
+
+ if (MappingList == NULL)
+ goto MappingListUserFindExit;
+
+ while ((i < MappingListSize) && (pMap == NULL)) {
+ if (MappingUserListFind(User, MappingList[i]->NumMembers, MappingList[i]->Members ) != NULL)
+ pMap = MappingList[i];
+ i++;
+ }
+
+MappingListUserFindExit:
+ RtlReleaseResource(&MappingListLock);
+
+ return pMap;
+} // MappingListUserFind
+
+
+/////////////////////////////////////////////////////////////////////////
+PMAPPING_RECORD
+MappingListAdd(
+ LPTSTR MappingName,
+ LPTSTR Comment,
+ ULONG Licenses
+ )
+
+/*++
+
+Routine Description:
+
+ Adds a Mapping to the Mapping table.
+
+Arguments:
+
+ MappingName -
+
+Return Value:
+
+ Pointer to added Mapping table entry, or NULL if failed.
+
+--*/
+
+{
+ PMAPPING_RECORD NewMapping;
+ LPTSTR NewMappingName;
+ LPTSTR NewComment;
+ PMAPPING_RECORD CurrentRecord = NULL;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: MappingListAdd\n"));
+#endif
+
+ //
+ // We do a double check here to see if another thread just got done
+ // adding the Mapping, between when we checked last and actually got
+ // the write lock.
+ //
+ CurrentRecord = MappingListFind(MappingName);
+ if (CurrentRecord != NULL) {
+ return CurrentRecord;
+ }
+
+ //
+ // Allocate space for table (zero init it).
+ //
+ if (MappingList == NULL)
+ MappingList = (PMAPPING_RECORD *) LocalAlloc(LPTR, sizeof(PMAPPING_RECORD));
+ else
+ MappingList = (PMAPPING_RECORD *) LocalReAlloc(MappingList, sizeof(PMAPPING_RECORD) * (MappingListSize + 1), LHND);
+
+ //
+ // Make sure we could allocate Mapping table
+ //
+ if (MappingList == NULL) {
+ MappingListSize = 0;
+ return NULL;
+ }
+
+ NewMapping = LocalAlloc(LPTR, sizeof(MAPPING_RECORD));
+ if (NewMapping == NULL)
+ return NULL;
+
+ MappingList[MappingListSize] = NewMapping;
+
+ NewMappingName = (LPTSTR) LocalAlloc(LPTR, (lstrlen(MappingName) + 1) * sizeof(TCHAR));
+ if (NewMappingName == NULL) {
+ LocalFree(NewMapping);
+ return NULL;
+ }
+
+ // now copy it over...
+ NewMapping->Name = NewMappingName;
+ lstrcpy(NewMappingName, MappingName);
+
+ //
+ // Allocate space for Comment
+ //
+ NewComment = (LPTSTR) LocalAlloc(LPTR, (lstrlen(Comment) + 1) * sizeof(TCHAR));
+ if (NewComment == NULL) {
+ LocalFree(NewMapping);
+ LocalFree(NewMappingName);
+ return NULL;
+ }
+
+ // now copy it over...
+ NewMapping->Comment = NewComment;
+ lstrcpy(NewComment, Comment);
+
+ NewMapping->NumMembers = 0;
+ NewMapping->Members = NULL;
+ NewMapping->Licenses = Licenses;
+ NewMapping->LicenseListSize = 0;
+ NewMapping->LicenseList = NULL;
+ NewMapping->Flags = (LLS_FLAG_LICENSED | LLS_FLAG_SUITE_AUTO);
+
+ MappingListSize++;
+
+ // Have added the entry - now need to sort it in order of the Mapping names
+ qsort((void *) MappingList, (size_t) MappingListSize, sizeof(PMAPPING_RECORD), MappingListCompare);
+
+ return NewMapping;
+
+} // MappingListAdd
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+MappingListDelete(
+ LPTSTR MappingName
+ )
+
+/*++
+
+Routine Description:
+
+Arguments:
+
+ MappingName -
+
+Return Value:
+
+
+--*/
+
+{
+ PMAPPING_RECORD Mapping;
+ ULONG i;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: MappingListDelete\n"));
+#endif
+
+ //
+ // Get mapping record based on name given
+ //
+ Mapping = MappingListFind(MappingName);
+ if (Mapping == NULL)
+ return STATUS_OBJECT_NAME_NOT_FOUND;
+
+ //
+ // Make sure there are no members to the mapping
+ //
+ if (Mapping->NumMembers != 0)
+ return STATUS_MEMBER_IN_GROUP;
+
+ //
+ // Check if this is the last Mapping
+ //
+ if (MappingListSize == 1) {
+ LocalFree(Mapping->Name);
+ LocalFree(Mapping->Comment);
+ LocalFree(Mapping);
+ LocalFree(MappingList);
+ MappingListSize = 0;
+ MappingList = NULL;
+ return STATUS_SUCCESS;
+ }
+
+ //
+ // Not the last mapping so find it in the list
+ //
+ i = 0;
+ while ((i < MappingListSize) && (lstrcmpi(MappingList[i]->Name, MappingName)))
+ i++;
+
+ //
+ // Now move everything below it up.
+ //
+ i++;
+ while (i < MappingListSize) {
+ memcpy(&MappingList[i-1], &MappingList[i], sizeof(PMAPPING_RECORD));
+ i++;
+ }
+
+ MappingList = (PMAPPING_RECORD *) LocalReAlloc(MappingList, sizeof(PMAPPING_RECORD) * (MappingListSize - 1), LHND);
+
+ //
+ // Make sure we could allocate Mapping table
+ //
+ ASSERT(MappingList != NULL);
+ if (MappingList == NULL)
+ MappingListSize = 0;
+ else
+ MappingListSize--;
+
+ //
+ // Now free up the record
+ //
+ LocalFree(Mapping->Name);
+ LocalFree(Mapping->Comment);
+ LocalFree(Mapping);
+
+ return STATUS_SUCCESS;
+
+} // MappingListDelete
+
+
+/////////////////////////////////////////////////////////////////////////
+PMAPPING_RECORD
+MappingUserListAdd(
+ LPTSTR MappingName,
+ LPTSTR User
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+ MappingName -
+
+Return Value:
+
+ Pointer to added Mapping table entry, or NULL if failed.
+
+--*/
+
+{
+ PMAPPING_RECORD Mapping;
+ LPTSTR NewName;
+ LPTSTR pUser;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: MappingUserListAdd\n"));
+#endif
+
+ //
+ // Get mapping record based on name given
+ //
+ Mapping = MappingListFind(MappingName);
+ if (Mapping == NULL)
+ return NULL;
+
+ //
+ // We do a double check here to see if another thread just got done
+ // adding the Mapping, between when we checked last and actually got
+ // the write lock.
+ //
+ pUser = MappingUserListFind(User, Mapping->NumMembers, Mapping->Members);
+
+ if (pUser != NULL) {
+ return Mapping;
+ }
+
+ //
+ // Allocate space for table (zero init it).
+ //
+ if (Mapping->Members == NULL)
+ Mapping->Members = (LPTSTR *) LocalAlloc(LPTR, sizeof(LPTSTR));
+ else
+ Mapping->Members = (LPTSTR *) LocalReAlloc(Mapping->Members, sizeof(LPTSTR) * (Mapping->NumMembers + 1), LHND);
+
+ //
+ // Make sure we could allocate Mapping table
+ //
+ if (Mapping->Members == NULL) {
+ Mapping->NumMembers = 0;
+ return NULL;
+ }
+
+ NewName = (LPTSTR) LocalAlloc(LPTR, (lstrlen(User) + 1) * sizeof(TCHAR));
+ if (NewName == NULL)
+ return NULL;
+
+ // now copy it over...
+ Mapping->Members[Mapping->NumMembers] = NewName;
+ lstrcpy(NewName, User);
+
+ Mapping->NumMembers++;
+
+ // Have added the entry - now need to sort it in order of the Mapping names
+ qsort((void *) Mapping->Members, (size_t) Mapping->NumMembers, sizeof(LPTSTR), MappingUserListCompare);
+
+ return Mapping;
+
+} // MappingUserListAdd
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+MappingUserListDelete(
+ LPTSTR MappingName,
+ LPTSTR User
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+ MappingName -
+
+Return Value:
+
+ Pointer to added Mapping table entry, or NULL if failed.
+
+--*/
+
+{
+ PMAPPING_RECORD Mapping;
+ LPTSTR pUser;
+ ULONG i;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: MappingUserListDelete\n"));
+#endif
+
+ //
+ // Get mapping record based on name given
+ //
+ Mapping = MappingListFind(MappingName);
+ if (Mapping == NULL)
+ return STATUS_OBJECT_NAME_NOT_FOUND;
+
+ //
+ // Find the given user
+ //
+ pUser = MappingUserListFind(User, Mapping->NumMembers, Mapping->Members);
+ if (pUser == NULL)
+ return STATUS_OBJECT_NAME_NOT_FOUND;
+
+ //
+ // Check if this is the last user
+ //
+ if (Mapping->NumMembers == 1) {
+ LocalFree(pUser);
+ LocalFree(Mapping->Members);
+ Mapping->Members = NULL;
+ Mapping->NumMembers = 0;
+ return STATUS_SUCCESS;
+ }
+
+ //
+ // Not the last member so find it in the list
+ //
+ i = 0;
+ while ((i < Mapping->NumMembers) && (lstrcmpi(Mapping->Members[i], User)))
+ i++;
+
+ //
+ // Now move everything below it up.
+ //
+ i++;
+ while (i < Mapping->NumMembers) {
+ memcpy(&Mapping->Members[i-1], &Mapping->Members[i], sizeof(LPTSTR));
+ i++;
+ }
+
+ Mapping->Members = (LPTSTR *) LocalReAlloc(Mapping->Members, sizeof(LPTSTR) * (Mapping->NumMembers - 1), LHND);
+
+ //
+ // Make sure we could allocate Mapping table
+ //
+ ASSERT(Mapping->Members != NULL);
+ if (Mapping->Members == NULL)
+ Mapping->NumMembers = 0;
+ else
+ Mapping->NumMembers--;
+
+ LocalFree(pUser);
+ return STATUS_SUCCESS;
+
+} // MappingUserListDelete
+
+
+#if DBG
+/////////////////////////////////////////////////////////////////////////
+VOID
+MappingListDebugDump( )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ ULONG i = 0;
+
+ //
+ // Need to scan list so get read access.
+ //
+ RtlAcquireResourceShared(&MappingListLock, TRUE);
+
+ dprintf(TEXT("Mapping Table, # Entries: %lu\n"), MappingListSize);
+ if (MappingList == NULL)
+ goto MappingListDebugDumpExit;
+
+ for (i = 0; i < MappingListSize; i++) {
+ dprintf(TEXT(" Name: %s Flags: 0x%4lX LT: %2lu Lic: %4lu # Mem: %4lu Comment: %s\n"),
+ MappingList[i]->Name, MappingList[i]->Flags, MappingList[i]->LicenseListSize, MappingList[i]->Licenses, MappingList[i]->NumMembers, MappingList[i]->Comment);
+ }
+
+MappingListDebugDumpExit:
+ RtlReleaseResource(&MappingListLock);
+
+ return;
+} // MappingListDebugDump
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+MappingListDebugInfoDump( PVOID Data )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ ULONG i;
+ PMAPPING_RECORD Mapping = NULL;
+
+ RtlAcquireResourceShared(&MappingListLock, TRUE);
+ dprintf(TEXT("Mapping Table, # Entries: %lu\n"), MappingListSize);
+
+ if (Data == NULL)
+ goto MappingListDebugInfoDumpExit;
+
+ if (MappingList == NULL)
+ goto MappingListDebugInfoDumpExit;
+
+ if (lstrlen((LPWSTR) Data) > 0) {
+ Mapping = MappingListFind((LPTSTR) Data);
+
+ if (Mapping != NULL) {
+ dprintf(TEXT(" Name: %s Flags: 0x%4lX LT: %2lu Lic: %4lu # Mem: %4lu Comment: %s\n"),
+ Mapping->Name, Mapping->Flags, Mapping->LicenseListSize, Mapping->Licenses, Mapping->NumMembers, Mapping->Comment);
+
+ if (Mapping->NumMembers != 0)
+ dprintf(TEXT("\nMembers\n"));
+
+ for (i = 0; i < Mapping->NumMembers; i++)
+ dprintf(TEXT(" %s\n"), Mapping->Members[i]);
+
+ if (Mapping->LicenseListSize != 0)
+ dprintf(TEXT("\nLicenseTable\n"));
+
+ for (i = 0; i < Mapping->LicenseListSize; i++)
+ dprintf( TEXT(" Flags: 0x%4lX Ref: %2lu LN: %2lu Svc: %s\n"),
+ Mapping->LicenseList[i]->Flags,
+ Mapping->LicenseList[i]->RefCount,
+ Mapping->LicenseList[i]->LicensesNeeded,
+ Mapping->LicenseList[i]->Service->Name );
+
+ } else
+ dprintf(TEXT("Mapping not found: %s\n"), (LPTSTR) Data);
+ }
+
+MappingListDebugInfoDumpExit:
+ RtlReleaseResource(&MappingListLock);
+
+} // MappingListDebugInfoDump
+
+#endif
+
+
diff --git a/private/net/svcdlls/lls/server/mapping.h b/private/net/svcdlls/lls/server/mapping.h
new file mode 100644
index 000000000..29fb4289b
--- /dev/null
+++ b/private/net/svcdlls/lls/server/mapping.h
@@ -0,0 +1,70 @@
+/*++
+
+Copyright (c) 1994 Microsoft Corporation
+
+Module Name:
+
+ Mapping.h
+
+Abstract:
+
+
+Author:
+
+ Arthur Hanson (arth) Dec 07, 1994
+
+Environment:
+
+Revision History:
+
+--*/
+
+
+#ifndef _LLS_MAPPING_H
+#define _LLS_MAPPING_H
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct _USER_LICENSE_RECORD;
+
+typedef struct _MAPPING_RECORD {
+ LPTSTR Name;
+ DWORD Flags;
+ LPTSTR Comment;
+ ULONG Licenses;
+ ULONG NumMembers;
+ LPTSTR *Members;
+
+ ULONG LicenseListSize;
+ struct _USER_LICENSE_RECORD **LicenseList;
+} MAPPING_RECORD, *PMAPPING_RECORD;
+
+
+VOID MappingListInit();
+PMAPPING_RECORD MappingListFind( LPTSTR MappingName );
+LPTSTR MappingUserListFind( LPTSTR User, ULONG NumEntries, LPTSTR *Users );
+PMAPPING_RECORD MappingListAdd( LPTSTR MappingName, LPTSTR Comment, ULONG Licenses );
+NTSTATUS MappingListDelete( LPTSTR MappingName );
+PMAPPING_RECORD MappingUserListAdd( LPTSTR MappingName, LPTSTR User );
+PMAPPING_RECORD MappingListUserFind( LPTSTR User );
+NTSTATUS MappingUserListDelete( LPTSTR MappingName, LPTSTR User );
+
+extern ULONG MappingListSize;
+extern PMAPPING_RECORD *MappingList;
+extern RTL_RESOURCE MappingListLock;
+
+#if DBG
+
+VOID MappingListDebugDump();
+VOID MappingListDebugInfoDump( PVOID Data );
+
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/private/net/svcdlls/lls/server/msvctbl.c b/private/net/svcdlls/lls/server/msvctbl.c
new file mode 100644
index 000000000..44e5a8124
--- /dev/null
+++ b/private/net/svcdlls/lls/server/msvctbl.c
@@ -0,0 +1,712 @@
+/*++
+
+Copyright (c) 1994 Microsoft Corporation
+
+Module Name:
+
+ msvctbl.c
+
+Abstract:
+
+ Master Service Table routines. Handles all access to the master service
+ table kept for per-seat information.
+
+ =========================== DATA STRUCTURES =============================
+
+ MasterServiceTable (PMASTER_SERVICE_RECORD *)
+ MasterServiceList (PMASTER_SERVICE_RECORD *)
+ Each of these points to an array of pointers to dynamically allocated
+ MASTER_SERVICE_RECORDs. There is exactly one MASTER_SERVICE_RECORD
+ for each (product, version) pairing; e.g., (SQL 4.0, SNA 2.0,
+ SNA 2.1). The MasterServiceTable is never re-ordered, so a valid
+ index into this table is guaranteed to always dereference to the same
+ (product, version). The MasterServiceList contains the same data
+ sorted lexicographically by product name (and therefore the data
+ pointed to by a specific index may change over time as new
+ (product, version) pairs are added to the table). Each table
+ contains MasterServiceListSize entries.
+
+ RootServiceList (PMASTER_SERVICE_ROOT *)
+ This points to an array of pointers to dynamically allocated
+ MASTER_SERVICE_ROOTs. There is exactly one MASTER_SERVICE_ROOT
+ for each product family. Each MASTER_SERVICE_ROOT contains a
+ pointer to an array of indices into the MasterServiceTable
+ corresponding to all the products in the family, sorted by
+ ascending version number. The RootServiceList itself is
+ sorted lexicographically by ascending family name. It contains
+ RootServiceListSize entries.
+
+Author:
+
+ Arthur Hanson (arth) 07-Dec-1994
+
+Revision History:
+
+ Jeff Parham (jeffparh) 05-Dec-1995
+ o Added a few comments.
+ o Added parameter to LicenseServiceListFind().
+ o Fixed benign bug in memory allocation:
+ sizeof(PULONG) -> sizeof(ULONG).
+
+--*/
+
+#include <stdlib.h>
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windows.h>
+
+#include "llsapi.h"
+#include "debug.h"
+#include "llssrv.h"
+#include "registry.h"
+#include "ntlsapi.h"
+#include "mapping.h"
+#include "msvctbl.h"
+#include "svctbl.h"
+#include "purchase.h"
+#include "perseat.h"
+
+
+/////////////////////////////////////////////////////////////////////////
+//
+// Master Service Table - Keeps list of products (SQL, SNA, etc.) with a
+// sub-list for each version of the product.
+//
+
+/////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////
+
+#define DEFAULT_SERVICE_TABLE_ENTRIES 10
+
+ULONG RootServiceListSize = 0;
+PMASTER_SERVICE_ROOT *RootServiceList = NULL;
+
+ULONG MasterServiceListSize = 0;
+PMASTER_SERVICE_RECORD *MasterServiceList = NULL;
+PMASTER_SERVICE_RECORD *MasterServiceTable = NULL;
+
+TCHAR BackOfficeStr[100];
+PMASTER_SERVICE_RECORD BackOffice;
+
+
+RTL_RESOURCE MasterServiceListLock;
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+MasterServiceListInit()
+
+/*++
+
+Routine Description:
+
+ Creates the Master Service table, used for tracking the services and
+ session count. This will pull the initial services from the registry.
+
+ The table is linear so a binary search can be used on the table, so
+ some extra records are initialized so that each time we add a new
+ service we don't have to do a realloc. We also assume that adding
+ new services is a relatively rare occurance, since we need to sort
+ it each time.
+
+ The service table is guarded by a read and write semaphore. Multiple
+ reads can occur, but a write blocks everything.
+
+ The service table has two default entries for FilePrint and REMOTE_ACCESS.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ int nLen;
+ HANDLE LlsDLLHandle = NULL;
+
+ RtlInitializeResource(&MasterServiceListLock);
+
+ memset(BackOfficeStr, 0, sizeof(BackOffice));
+ BackOffice = NULL;
+ LlsDLLHandle = LoadLibrary(TEXT("LLSRPC.DLL"));
+ ASSERT(LlsDLLHandle != NULL);
+
+ if (LlsDLLHandle != NULL) {
+ nLen = LoadString(LlsDLLHandle, IDS_BACKOFFICE, BackOfficeStr, sizeof(BackOfficeStr));
+
+ if (nLen != 0) {
+ BackOffice = MasterServiceListAdd( BackOfficeStr, BackOfficeStr, 0 );
+ } else {
+#if DBG
+ dprintf(TEXT("LLS ERROR: Could not load BackOffice string\n"));
+#endif
+ }
+
+ }
+
+} // MasterServiceListInit
+
+
+/////////////////////////////////////////////////////////////////////////
+// used by qsort to sort MasterServiceList by product name
+int __cdecl MasterServiceListCompare(const void *arg1, const void *arg2) {
+ PMASTER_SERVICE_RECORD Svc1, Svc2;
+
+ Svc1 = (PMASTER_SERVICE_RECORD) *((PMASTER_SERVICE_RECORD *) arg1);
+ Svc2 = (PMASTER_SERVICE_RECORD) *((PMASTER_SERVICE_RECORD *) arg2);
+
+ return lstrcmpi( Svc1->Name, Svc2->Name );
+
+} // MasterServiceListCompare
+
+
+/////////////////////////////////////////////////////////////////////////
+// used by qsort to sort the Services array of indices pointed to by the
+// MASTER_SERVICE_ROOT structure by product version number
+int __cdecl MServiceRecordCompare(const void *arg1, const void *arg2) {
+ PMASTER_SERVICE_RECORD Svc1, Svc2;
+
+ Svc1 = (PMASTER_SERVICE_RECORD) MasterServiceTable[*((PULONG) arg1)];
+ Svc2 = (PMASTER_SERVICE_RECORD) MasterServiceTable[*((PULONG) arg2)];
+
+ return (int) Svc1->Version - Svc2->Version;
+
+} // MServiceRecordCompare
+
+
+/////////////////////////////////////////////////////////////////////////
+// used by qsort to sort the RootServiceList array of product families
+// by family name
+int __cdecl MServiceRootCompare(const void *arg1, const void *arg2) {
+ PMASTER_SERVICE_ROOT Svc1, Svc2;
+
+ Svc1 = (PMASTER_SERVICE_ROOT) *((PMASTER_SERVICE_ROOT *) arg1);
+ Svc2 = (PMASTER_SERVICE_ROOT) *((PMASTER_SERVICE_ROOT *) arg2);
+
+ return lstrcmpi( Svc1->Name, Svc2->Name );
+
+} // MServiceRootCompare
+
+
+/////////////////////////////////////////////////////////////////////////
+PMASTER_SERVICE_ROOT
+MServiceRootFind(
+ LPTSTR ServiceName
+ )
+
+/*++
+
+Routine Description:
+
+ Internal routine to actually do binary search on MasterServiceList, this
+ does not do any locking as we expect the wrapper routine to do this.
+ The search is a simple binary search.
+
+Arguments:
+
+ ServiceName -
+
+Return Value:
+
+ Pointer to found service table entry or NULL if not found.
+
+--*/
+
+{
+ LONG begin = 0;
+ LONG end = (LONG) RootServiceListSize - 1;
+ LONG cur;
+ int match;
+ PMASTER_SERVICE_ROOT ServiceRoot;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: MServiceRootFind\n"));
+#endif
+
+ if ((RootServiceListSize == 0) || (ServiceName == NULL))
+ return NULL;
+
+ while (end >= begin) {
+ // go halfway in-between
+ cur = (begin + end) / 2;
+ ServiceRoot = RootServiceList[cur];
+
+ // compare the two result into match
+ match = lstrcmpi(ServiceName, ServiceRoot->Name);
+
+ if (match < 0)
+ // move new begin
+ end = cur - 1;
+ else
+ begin = cur + 1;
+
+ if (match == 0)
+ return ServiceRoot;
+ }
+
+ return NULL;
+
+} // MServiceRootFind
+
+
+/////////////////////////////////////////////////////////////////////////
+PMASTER_SERVICE_RECORD
+MasterServiceListFind(
+ LPTSTR Name
+ )
+
+/*++
+
+Routine Description:
+
+ Internal routine to actually do binary search on MasterServiceList, this
+ does not do any locking as we expect the wrapper routine to do this.
+ The search is a simple binary search.
+
+Arguments:
+
+ ServiceName -
+
+Return Value:
+
+ Pointer to found service table entry or NULL if not found.
+
+--*/
+
+{
+ LONG begin = 0;
+ LONG end;
+ LONG cur;
+ int match;
+ PMASTER_SERVICE_RECORD Service;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: MasterServiceListFind\n"));
+#endif
+
+ if ((Name == NULL) || (MasterServiceListSize == 0))
+ return NULL;
+
+ end = (LONG) MasterServiceListSize - 1;
+
+ while (end >= begin) {
+ // go halfway in-between
+ cur = (begin + end) / 2;
+ Service = MasterServiceList[cur];
+
+ // compare the two result into match
+ match = lstrcmpi(Name, Service->Name);
+
+ if (match < 0)
+ // move new begin
+ end = cur - 1;
+ else
+ begin = cur + 1;
+
+ if (match == 0)
+ return Service;
+ }
+
+ return NULL;
+
+} // MasterServiceListFind
+
+
+/////////////////////////////////////////////////////////////////////////
+PMASTER_SERVICE_RECORD
+MasterServiceListAdd(
+ LPTSTR FamilyName,
+ LPTSTR Name,
+ DWORD Version
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+ ServiceName -
+
+Return Value:
+
+ Pointer to added service table entry, or NULL if failed.
+
+--*/
+
+{
+ ULONG i;
+ ULONG SessionLimit = 0;
+ BOOL PerSeatLicensing = FALSE;
+ LPTSTR NewServiceName, pDisplayName;
+ PMASTER_SERVICE_RECORD Service = NULL;
+ PMASTER_SERVICE_ROOT ServiceRoot = NULL;
+ PULONG ServiceList;
+ PLICENSE_SERVICE_RECORD pLicense;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: MasterServiceListAdd\n"));
+#endif
+
+ //
+ // Mask off low word of version - as it doesn't matter to licensing
+ //
+ Version &= 0xFFFF0000;
+
+ Service = MasterServiceListFind(Name);
+ if (Service != NULL)
+ return Service;
+
+ //
+ // Try to find a root node for that family of products
+ //
+ ServiceRoot = MServiceRootFind(FamilyName);
+
+ if (ServiceRoot == NULL) {
+ //
+ // No root record - so create a new one
+ //
+ if (RootServiceList == NULL)
+ RootServiceList = (PMASTER_SERVICE_ROOT *) LocalAlloc(LPTR, sizeof(PMASTER_SERVICE_ROOT));
+ else
+ RootServiceList = (PMASTER_SERVICE_ROOT *) LocalReAlloc(RootServiceList, sizeof(PMASTER_SERVICE_ROOT) * (RootServiceListSize + 1), LHND);
+
+ //
+ // Make sure we could allocate service table
+ //
+ if (RootServiceList == NULL) {
+ ASSERT(FALSE);
+ RootServiceListSize = 0;
+ return NULL;
+ }
+
+ //
+ // Allocate space for Root.
+ //
+ ServiceRoot = (PMASTER_SERVICE_ROOT) LocalAlloc(LPTR, sizeof(MASTER_SERVICE_ROOT));
+ if (ServiceRoot == NULL) {
+ ASSERT(FALSE);
+ return NULL;
+ }
+
+ RootServiceList[RootServiceListSize] = ServiceRoot;
+
+ NewServiceName = (LPTSTR) LocalAlloc(LPTR, (lstrlen(FamilyName) + 1) * sizeof(TCHAR));
+ if (NewServiceName == NULL) {
+ ASSERT(FALSE);
+ LocalFree(ServiceRoot);
+ return NULL;
+ }
+
+ // now copy it over...
+ ServiceRoot->Name = NewServiceName;
+ lstrcpy(NewServiceName, FamilyName);
+
+ //
+ // Initialize stuff for list of various versions of this product
+ //
+ ServiceRoot->ServiceTableSize = 0;
+ ServiceRoot->Services = NULL;
+ ServiceRoot->Flags = 0;
+
+ RtlInitializeResource(&ServiceRoot->ServiceLock);
+
+ RootServiceListSize++;
+
+ // Have added the entry - now need to sort it in order of the service names
+ qsort((void *) RootServiceList, (size_t) RootServiceListSize, sizeof(PMASTER_SERVICE_ROOT), MServiceRootCompare);
+
+ }
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Whether added or found, ServiceRoot points to the Root Node entry.
+ // Now double check to see if another thread just got done adding the
+ // actual service before we got the write lock.
+ //
+ RtlAcquireResourceShared(&ServiceRoot->ServiceLock, TRUE);
+ Service = MasterServiceListFind(Name);
+
+ if (Service == NULL) {
+ //
+ // No Service Record - so create a new one
+ //
+ RtlConvertSharedToExclusive(&ServiceRoot->ServiceLock);
+ ServiceList = ServiceRoot->Services;
+ if (ServiceList == NULL)
+ ServiceList = (PULONG) LocalAlloc(LPTR, sizeof(ULONG));
+ else
+ ServiceList = (PULONG) LocalReAlloc(ServiceList, sizeof(ULONG) * (ServiceRoot->ServiceTableSize + 1), LHND);
+
+ if (MasterServiceList == NULL) {
+ MasterServiceList = (PMASTER_SERVICE_RECORD *) LocalAlloc(LPTR, sizeof(PMASTER_SERVICE_RECORD));
+ MasterServiceTable = (PMASTER_SERVICE_RECORD *) LocalAlloc(LPTR, sizeof(PMASTER_SERVICE_RECORD));
+ } else {
+ MasterServiceList = (PMASTER_SERVICE_RECORD *) LocalReAlloc(MasterServiceList, sizeof(PMASTER_SERVICE_RECORD) * (MasterServiceListSize + 1), LHND);
+ MasterServiceTable = (PMASTER_SERVICE_RECORD *) LocalReAlloc(MasterServiceTable, sizeof(PMASTER_SERVICE_RECORD) * (MasterServiceListSize + 1), LHND);
+ }
+
+ ServiceRoot->Services = ServiceList;
+
+ //
+ // Make sure we could allocate service table
+ //
+ if ((ServiceList == NULL) || (MasterServiceList == NULL) || (MasterServiceTable == NULL)) {
+ ASSERT(FALSE);
+ ServiceRoot->ServiceTableSize = 0;
+ MasterServiceListSize = 0;
+ MasterServiceList = NULL;
+ MasterServiceTable = NULL;
+ RtlReleaseResource(&ServiceRoot->ServiceLock);
+ return NULL;
+ }
+
+ //
+ // Allocate space for saving off Service Record.
+ //
+ Service = (PMASTER_SERVICE_RECORD) LocalAlloc(LPTR, sizeof(MASTER_SERVICE_RECORD));
+ if (Service == NULL) {
+ ASSERT(FALSE);
+ RtlReleaseResource(&ServiceRoot->ServiceLock);
+ return NULL;
+ }
+
+ ServiceList[ServiceRoot->ServiceTableSize] = MasterServiceListSize;
+ MasterServiceList[MasterServiceListSize] = Service;
+ MasterServiceTable[MasterServiceListSize] = Service;
+
+ //
+ // ...DisplayName
+ //
+ NewServiceName = (LPTSTR) LocalAlloc(LPTR, (lstrlen(Name) + 1) * sizeof(TCHAR));
+ if (NewServiceName == NULL) {
+ ASSERT(FALSE);
+ LocalFree(Service);
+ RtlReleaseResource(&ServiceRoot->ServiceLock);
+ return NULL;
+ }
+
+ // now copy it over...
+ Service->Name = NewServiceName;
+ lstrcpy(NewServiceName, Name);
+
+ //
+ // Init rest of values.
+ //
+ Service->Version= Version;
+ Service->LicensesUsed = 0;
+ Service->LicensesClaimed = 0;
+ Service->next = 0;
+ Service->Index = MasterServiceListSize;
+ Service->Family = ServiceRoot;
+
+ pLicense = LicenseServiceListFind(Service->Name, FALSE);
+ if (pLicense == NULL)
+ Service->Licenses = 0;
+ else
+ Service->Licenses = pLicense->NumberLicenses;
+
+ //
+ // Init next pointer
+ //
+ i = 0;
+ while ((i < ServiceRoot->ServiceTableSize) && (MasterServiceTable[ServiceRoot->Services[i]]->Version < Version))
+ i++;
+
+ if (i > 0) {
+ Service->next = MasterServiceTable[ServiceRoot->Services[i - 1]]->next;
+ MasterServiceTable[ServiceRoot->Services[i - 1]]->next = Service->Index + 1;
+ }
+
+ ServiceRoot->ServiceTableSize++;
+ MasterServiceListSize++;
+
+ // Have added the entry - now need to sort it in order of the versions
+ qsort((void *) ServiceList, (size_t) ServiceRoot->ServiceTableSize, sizeof(PMASTER_SERVICE_RECORD), MServiceRecordCompare);
+
+ // And sort the list the UI uses (sorted by service name)
+ qsort((void *) MasterServiceList, (size_t) MasterServiceListSize, sizeof(PMASTER_SERVICE_RECORD), MasterServiceListCompare);
+ }
+
+ RtlReleaseResource(&ServiceRoot->ServiceLock);
+ return Service;
+
+} // MasterServiceListAdd
+
+
+#if DBG
+
+/////////////////////////////////////////////////////////////////////////
+//
+// DEBUG INFORMATION DUMP ROUTINES
+//
+/////////////////////////////////////////////////////////////////////////
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+MasterServiceRootDebugDump( )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ ULONG i = 0;
+
+ //
+ // Need to scan list so get read access.
+ //
+ RtlAcquireResourceShared(&MasterServiceListLock, TRUE);
+
+ dprintf(TEXT("Service Family Table, # Entries: %lu\n"), RootServiceListSize);
+ if (RootServiceList == NULL)
+ goto MasterServiceRootDebugDumpExit;
+
+ for (i = 0; i < RootServiceListSize; i++) {
+ dprintf(TEXT("%3lu) Services: %3lu Svc: %s [%s]\n"),
+ i + 1, RootServiceList[i]->ServiceTableSize, RootServiceList[i]->Name, RootServiceList[i]->Name);
+ }
+
+MasterServiceRootDebugDumpExit:
+ RtlReleaseResource(&MasterServiceListLock);
+
+ return;
+} // MasterServiceRootDebugDump
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+MasterServiceRootDebugInfoDump( PVOID Data )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ ULONG i = 0;
+
+ //
+ // Need to scan list so get read access.
+ //
+ RtlAcquireResourceShared(&MasterServiceListLock, TRUE);
+
+ dprintf(TEXT("Service Family Table, # Entries: %lu\n"), RootServiceListSize);
+ if (RootServiceList == NULL)
+ goto MasterServiceRootDebugDumpExit;
+
+ for (i = 0; i < RootServiceListSize; i++) {
+ dprintf(TEXT("%3lu) Services: %3lu Svc: %s [%s]\n"),
+ i + 1, RootServiceList[i]->ServiceTableSize, RootServiceList[i]->Name, RootServiceList[i]->Name);
+ }
+
+MasterServiceRootDebugDumpExit:
+ RtlReleaseResource(&MasterServiceListLock);
+
+ return;
+} // MasterServiceRootDebugInfoDump
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+MasterServiceListDebugDump( )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ ULONG i = 0;
+
+ //
+ // Need to scan list so get read access.
+ //
+ RtlAcquireResourceShared(&MasterServiceListLock, TRUE);
+
+ dprintf(TEXT("Master Service Table, # Entries: %lu\n"), MasterServiceListSize);
+ if (MasterServiceList == NULL)
+ goto MasterServiceListDebugDumpExit;
+
+ for (i = 0; i < MasterServiceListSize; i++) {
+ dprintf(TEXT("%3lu) [%3lu] LU: %4lu LP: %4lu LC: %4lu MS: %4lu HM: %4lu Next: %3lu Svc: %s %lX\n"),
+ i + 1, MasterServiceList[i]->Index,
+ MasterServiceList[i]->LicensesUsed, MasterServiceList[i]->Licenses, MasterServiceList[i]->LicensesClaimed,
+ MasterServiceList[i]->MaxSessionCount, MasterServiceList[i]->HighMark,
+ MasterServiceList[i]->next, MasterServiceList[i]->Name, MasterServiceList[i]->Version);
+ }
+
+MasterServiceListDebugDumpExit:
+ RtlReleaseResource(&MasterServiceListLock);
+
+ return;
+} // MasterServiceListDebugDump
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+MasterServiceListDebugInfoDump( PVOID Data )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ PMASTER_SERVICE_RECORD CurrentRecord = NULL;
+
+ dprintf(TEXT("Master Service Table, # Entries: %lu\n"), RootServiceListSize);
+
+ if (lstrlen((LPWSTR) Data) > 0) {
+// CurrentRecord = MasterServiceListFind((LPWSTR) Data);
+ if (CurrentRecord != NULL) {
+ }
+ }
+
+} // MasterServiceListDebugInfoDump
+
+#endif
diff --git a/private/net/svcdlls/lls/server/msvctbl.h b/private/net/svcdlls/lls/server/msvctbl.h
new file mode 100644
index 000000000..84c19a3c0
--- /dev/null
+++ b/private/net/svcdlls/lls/server/msvctbl.h
@@ -0,0 +1,151 @@
+/*++
+
+Copyright (c) 1994 Microsoft Corporation
+
+Module Name:
+
+ msvctbl.h
+
+Abstract:
+
+ See msvctbl.c
+
+Author:
+
+ Arthur Hanson (arth) Dec 07, 1994
+
+Environment:
+
+Revision History:
+
+ Jeff Parham (jeffparh) 05-Dec-1995
+ o Added comments.
+
+--*/
+
+
+#ifndef _LLS_MSVCTBL_H
+#define _LLS_MSVCTBL_H
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#define IDS_BACKOFFICE 1500
+
+/////////////////////////////////////////////////////////////////////////
+//
+// The master service record is for license usage tracking. We have
+// A master ROOT record for a family of products (say SQL Server) and
+// a sub linked-list of each of the specific versions in order of the
+// version number. When we do license checking we can move on up the tree
+// to higher level of licenses.
+//
+// There is also a mapping table kept for each of the ROOT records. This
+// tracks if the mapping license count has already been used.
+//
+struct _MASTER_SERVICE_ROOT;
+
+typedef struct _MASTER_SERVICE_RECORD
+{
+ ULONG Index; // index at which
+ // a pointer to this
+ // structure may be
+ // found in the
+ // MasterServiceTable
+
+ LPTSTR Name; // product name
+
+ DWORD Version; // version of the
+ // product;
+ // major.minor ->
+ // (major << 16)
+ // | minor, e.g.,
+ // 5.2 -> 0x50002
+
+ struct _MASTER_SERVICE_ROOT * Family; // pointer to the
+ // product family,
+ // e.g., "SNA 2.1"
+ // -> "SNA"
+
+ ULONG Licenses;
+ ULONG LicensesUsed;
+ ULONG LicensesClaimed;
+
+ ULONG MaxSessionCount;
+ ULONG HighMark;
+
+ ULONG next; // index at which
+ // a pointer to the
+ // next ascending
+ // version of this
+ // product may be
+ // found in the
+ // MasterServiceTable
+ // NOTE: index is
+ // 1-based, so if
+ // next == 0 there
+ // are no more, and
+ // if non-zero then
+ // the next version
+ // is at index next-1
+
+} MASTER_SERVICE_RECORD, *PMASTER_SERVICE_RECORD;
+
+typedef struct _MASTER_SERVICE_ROOT
+{
+ LPTSTR Name; // name of this product family
+
+ DWORD Flags;
+
+ RTL_RESOURCE ServiceLock; // lock for changes to the
+ // Services array (below)
+
+ ULONG ServiceTableSize; // number of entries in Services
+ // array (below)
+
+ ULONG * Services; // array of indices into the
+ // MasterServiceTable of the various
+ // (product,version) pairs
+ // belonging to this family;
+ // sorted in order of ascending
+ // version
+} MASTER_SERVICE_ROOT, *PMASTER_SERVICE_ROOT;
+
+extern ULONG RootServiceListSize;
+extern PMASTER_SERVICE_ROOT *RootServiceList;
+
+extern ULONG MasterServiceListSize;
+extern PMASTER_SERVICE_RECORD *MasterServiceList;
+extern PMASTER_SERVICE_RECORD *MasterServiceTable;
+
+extern RTL_RESOURCE MasterServiceListLock;
+
+extern TCHAR BackOfficeStr[];
+extern PMASTER_SERVICE_RECORD BackOffice;
+
+
+VOID MasterServiceListInit();
+PMASTER_SERVICE_RECORD MServiceRecordFind( DWORD Version, ULONG NumServiceEntries, PULONG ServiceList );
+PMASTER_SERVICE_ROOT MServiceRootFind( LPTSTR ServiceName );
+PMASTER_SERVICE_RECORD MasterServiceListFind( LPTSTR DisplayName );
+PMASTER_SERVICE_RECORD MasterServiceListAdd( LPTSTR FamilyName, LPTSTR Name, DWORD Version );
+
+#if DBG
+
+VOID MasterServiceRootDebugDump();
+VOID MasterServiceRootDebugInfoDump( PVOID Data );
+VOID MasterServiceListDebugDump();
+VOID MasterServiceListDebugInfoDump( PVOID Data );
+
+#endif
+
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/private/net/svcdlls/lls/server/pack.c b/private/net/svcdlls/lls/server/pack.c
new file mode 100644
index 000000000..4c6d2b01f
--- /dev/null
+++ b/private/net/svcdlls/lls/server/pack.c
@@ -0,0 +1,4306 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ pack.c
+
+Abstract:
+
+
+Author:
+
+ Arthur Hanson (arth) 06-Jan-1995
+
+Revision History:
+
+ Jeff Parham (jeffparh) 05-Dec-1995
+ o Added new fields to purchase record to support secure certificates.
+ o Unified per server purchase model with per seat purchase model for
+ secure certificates; per server model still done in the traditional
+ manner for non-secure certificates (for backwards compatibility).
+ o Removed assertion on LicenseAdd() failure. LicenseAdd() may
+ legitimately fail under certain circumstances.
+ o Fixed bug wherein a memory allocation failure in the LLS routines
+ would result in a corrupt data file (which would AV the server when
+ it was thereafter read). (Bug #14072.)
+ o Added SaveAll() function analogous to LoadAll().
+ o Added support for extended user data packing/unpacking. This was
+ done to save the SUITE_USE flag across restarts of the service.
+ o Removed user table parameters from unpack routines that didn't use
+ them.
+ o Fixed ServerServiceListUnpack() to subtract out old values only when
+ they were previously added to the MasterServiceTable. This fixes
+ problems with the MaxSessionCount and HighMark tallies getting skewed.
+
+--*/
+
+#include <stdlib.h>
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windows.h>
+
+#include "llsapi.h"
+#include "debug.h"
+#include "llsutil.h"
+#include "llssrv.h"
+#include "mapping.h"
+#include "msvctbl.h"
+#include "svctbl.h"
+#include "perseat.h"
+#include "purchase.h"
+#include "server.h"
+
+#include "llsrpc_s.h"
+#include "lsapi_s.h"
+#include "llsdbg_s.h"
+#include "repl.h"
+#include "pack.h"
+#include "llsevent.h"
+#include "certdb.h"
+
+
+int __cdecl MServiceRecordCompare(const void *arg1, const void *arg2);
+
+static HANDLE PurchaseFile;
+
+/////////////////////////////////////////////////////////////////////////
+// License List
+//
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+LicenseListUnpackOld (
+ ULONG LicenseServiceTableSize,
+ PPACK_LICENSE_SERVICE_RECORD LicenseServices,
+
+ ULONG LicenseTableSize,
+ PPACK_LICENSE_PURCHASE_RECORD_0 Licenses
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+Return Value:
+
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ ULONG i;
+ PPACK_LICENSE_PURCHASE_RECORD_0 pLicense;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LicenseListUnpackOld: Service[%lu] License[%lu]\n"), LicenseServiceTableSize, LicenseTableSize);
+#endif
+
+ //
+ // Walk services table, adding any new services to our local table.
+ // Fix up the index pointers to match our local services.
+ //
+ RtlAcquireResourceExclusive(&LicenseListLock, TRUE);
+
+ for (i = 0; i < LicenseTableSize; i++) {
+ pLicense = &Licenses[i];
+
+ if (pLicense->Service < LicenseServiceTableSize)
+ Status = LicenseAdd(LicenseServices[pLicense->Service].ServiceName, TEXT("Microsoft"), pLicense->NumberLicenses, 0, pLicense->Admin, pLicense->Comment, pLicense->Date, LLS_LICENSE_MODE_ALLOW_PER_SEAT, 0, TEXT("None"), 0, NULL );
+ else {
+ ASSERT(FALSE);
+ }
+
+ if (Status != STATUS_SUCCESS) {
+#ifdef DBG
+ dprintf(TEXT("LicenseAdd failed: 0x%lX\n"), Status);
+#endif
+ // ASSERT(FALSE);
+ }
+
+ }
+
+ RtlReleaseResource(&LicenseListLock);
+
+} // LicenseListUnpackOld
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+LicenseListStringsUnpackOld (
+ ULONG LicenseServiceTableSize,
+ PPACK_LICENSE_SERVICE_RECORD LicenseServices,
+
+ ULONG LicenseServiceStringSize,
+ LPTSTR LicenseServiceStrings,
+
+ ULONG LicenseTableSize,
+ PPACK_LICENSE_PURCHASE_RECORD_0 Licenses,
+
+ ULONG LicenseStringSize,
+ LPTSTR LicenseStrings
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+Return Value:
+
+
+--*/
+
+{
+ ULONG i;
+ PPACK_LICENSE_SERVICE_RECORD pSvc;
+ PPACK_LICENSE_PURCHASE_RECORD_0 pLicense;
+ TCHAR *pStr;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LicenseListStringsUnpack\n"));
+#endif
+
+ //
+ // First do license service strings
+ //
+ pStr = LicenseServiceStrings;
+ for (i = 0; i < LicenseServiceTableSize; i++) {
+ pSvc = &LicenseServices[i];
+
+ pSvc->ServiceName = pStr;
+
+ //
+ // Move to end of current string
+ //
+ while (*pStr != TEXT('\0'))
+ pStr++;
+
+ // now go past ending NULL
+ pStr++;
+ }
+
+ //
+ // Now do license purchase strings
+ //
+ pStr = LicenseStrings;
+ for (i = 0; i < LicenseTableSize; i++) {
+ pLicense = &Licenses[i];
+
+ pLicense->Admin = pStr;
+
+ //
+ // Move to end of current string
+ //
+ while (*pStr != TEXT('\0'))
+ pStr++;
+
+ // now go past ending NULL
+ pStr++;
+
+ pLicense->Comment = pStr;
+
+ //
+ // Move to end of current string
+ //
+ while (*pStr != TEXT('\0'))
+ pStr++;
+
+ // now go past ending NULL
+ pStr++;
+ }
+
+} // LicenseListStringsUnpackOld
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+LicenseListLoadOld()
+
+/*++
+
+Routine Description:
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ BOOL ret;
+ DWORD Version, DataSize;
+ NTSTATUS Status = STATUS_SUCCESS;
+ HANDLE hFile = NULL;
+
+ ULONG LicenseServiceTableSize;
+ PPACK_LICENSE_SERVICE_RECORD LicenseServices = NULL;
+
+ ULONG LicenseServiceStringSize;
+ LPTSTR LicenseServiceStrings = NULL;
+
+ ULONG LicenseTableSize;
+ PPACK_LICENSE_PURCHASE_RECORD_0 Licenses = NULL;
+
+ ULONG LicenseStringSize;
+ LPTSTR LicenseStrings = NULL;
+
+ LICENSE_FILE_HEADER_0 FileHeader;
+ DWORD BytesRead;
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_DATABASE))
+ dprintf(TEXT("LLS TRACE: LicenseListLoad\n"));
+#endif
+
+ //
+ // Check if we already have file open
+ //
+ if (PurchaseFile != NULL) {
+ CloseHandle(PurchaseFile);
+ PurchaseFile = NULL;
+ }
+
+ //
+ // If nothing to load then get-out
+ //
+ if (!FileExists(LicenseFileName))
+ goto LicenseListLoadExit;
+
+ //
+ // Check the init header
+ //
+ Version = DataSize = 0;
+ PurchaseFile = LlsFileCheck(LicenseFileName, &Version, &DataSize );
+ if (PurchaseFile == NULL) {
+ Status = GetLastError();
+ goto LicenseListLoadExit;
+ }
+
+ if ((Version != LICENSE_FILE_VERSION_0) || (DataSize != sizeof(LICENSE_FILE_HEADER_0))) {
+ Status = STATUS_FILE_INVALID;
+ goto LicenseListLoadExit;
+ }
+
+ //
+ // The init header checks out, so load the license header and data blocks
+ //
+ hFile = PurchaseFile;
+ ret = ReadFile(hFile, &FileHeader, sizeof(LICENSE_FILE_HEADER_0), &BytesRead, NULL);
+
+ LicenseServiceTableSize = 0;
+ LicenseServiceStringSize = 0;
+ LicenseTableSize = 0;
+ LicenseStringSize = 0;
+
+ if (ret) {
+ //
+ // Run through and allocate space to read data blocks into
+ //
+ if (FileHeader.LicenseServiceTableSize != 0) {
+ LicenseServiceTableSize = FileHeader.LicenseServiceTableSize / sizeof(PACK_LICENSE_SERVICE_RECORD);
+ LicenseServices = MIDL_user_allocate(FileHeader.LicenseServiceTableSize);
+
+ if ( LicenseServices == NULL ) {
+ Status = STATUS_NO_MEMORY;
+ goto LicenseListLoadExit;
+ }
+ }
+
+ if (FileHeader.LicenseServiceStringSize != 0) {
+ LicenseServiceStringSize = FileHeader.LicenseServiceStringSize / sizeof(TCHAR);
+ LicenseServiceStrings = MIDL_user_allocate(FileHeader.LicenseServiceStringSize);
+
+ if ( LicenseServiceStrings == NULL ) {
+ Status = STATUS_NO_MEMORY;
+ goto LicenseListLoadExit;
+ }
+ }
+
+ if (FileHeader.LicenseTableSize != 0) {
+ LicenseTableSize = FileHeader.LicenseTableSize / sizeof(PACK_LICENSE_PURCHASE_RECORD);
+ Licenses = MIDL_user_allocate(FileHeader.LicenseTableSize);
+
+ if ( Licenses == NULL ) {
+ Status = STATUS_NO_MEMORY;
+ goto LicenseListLoadExit;
+ }
+ }
+
+ if (FileHeader.LicenseStringSize != 0) {
+ LicenseStringSize = FileHeader.LicenseStringSize / sizeof(TCHAR);
+ LicenseStrings = MIDL_user_allocate(FileHeader.LicenseStringSize);
+
+ if ( LicenseStrings == NULL ) {
+ Status = STATUS_NO_MEMORY;
+ goto LicenseListLoadExit;
+ }
+ }
+
+ }
+
+ if (ret && (FileHeader.LicenseServiceTableSize != 0) )
+ ret = ReadFile(hFile, LicenseServices, FileHeader.LicenseServiceTableSize, &BytesRead, NULL);
+
+ if (ret && (FileHeader.LicenseServiceStringSize != 0) )
+ ret = ReadFile(hFile, LicenseServiceStrings, FileHeader.LicenseServiceStringSize, &BytesRead, NULL);
+
+ if (ret && (FileHeader.LicenseTableSize != 0) )
+ ret = ReadFile(hFile, Licenses, FileHeader.LicenseTableSize, &BytesRead, NULL);
+
+ if (ret && (FileHeader.LicenseStringSize != 0) )
+ ret = ReadFile(hFile, LicenseStrings, FileHeader.LicenseStringSize, &BytesRead, NULL);
+
+ if (!ret) {
+ Status = GetLastError();
+ goto LicenseListLoadExit;
+ }
+
+ //
+ // Decrypt the data
+ //
+ Status = DeBlock(LicenseServices, FileHeader.LicenseServiceTableSize);
+
+ if (Status == STATUS_SUCCESS)
+ Status = DeBlock(LicenseServiceStrings, FileHeader.LicenseServiceStringSize);
+
+ if (Status == STATUS_SUCCESS)
+ Status = DeBlock(Licenses, FileHeader.LicenseTableSize);
+
+ if (Status == STATUS_SUCCESS)
+ Status = DeBlock(LicenseStrings, FileHeader.LicenseStringSize);
+
+ if (Status != STATUS_SUCCESS)
+ goto LicenseListLoadExit;
+
+
+ //
+ // Unpack the string data
+ //
+ LicenseListStringsUnpackOld( LicenseServiceTableSize, LicenseServices,
+ LicenseServiceStringSize, LicenseServiceStrings,
+ LicenseTableSize, Licenses,
+ LicenseStringSize, LicenseStrings );
+
+ //
+ // Unpack the license data
+ //
+ LicenseListUnpackOld( LicenseServiceTableSize, LicenseServices, LicenseTableSize, Licenses );
+
+LicenseListLoadExit:
+
+ // Note: Don't close the License Purchase File (keep it locked).
+
+ //
+ // Run through our tables and clean them up
+ //
+ if (LicenseServices != NULL)
+ MIDL_user_free(LicenseServices);
+
+ if (LicenseServiceStrings != NULL)
+ MIDL_user_free(LicenseServiceStrings);
+
+ if (Licenses != NULL)
+ MIDL_user_free(Licenses);
+
+ if (LicenseStrings != NULL)
+ MIDL_user_free(LicenseStrings);
+
+ //
+ // If there was an error log it.
+ //
+ if (Status != STATUS_SUCCESS)
+ LogEvent(LLS_EVENT_LOAD_LICENSE, 0, NULL, Status);
+
+} // LicenseListLoadOld
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+LicenseListPack (
+ ULONG *pLicenseServiceTableSize,
+ PPACK_LICENSE_SERVICE_RECORD *pLicenseServices,
+
+ ULONG *pLicenseTableSize,
+ PPACK_LICENSE_PURCHASE_RECORD *pLicenses,
+
+ ULONG *pPerServerLicenseServiceTableSize,
+ PPACK_LICENSE_SERVICE_RECORD *pPerServerLicenseServices
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+Return Value:
+
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ PPACK_LICENSE_SERVICE_RECORD LicenseServices = NULL;
+ PPACK_LICENSE_PURCHASE_RECORD Licenses = NULL;
+ ULONG i;
+ ULONG TotalRecords = 0;
+ PLICENSE_SERVICE_RECORD pLicenseService;
+ PLICENSE_PURCHASE_RECORD pLicense;
+ PPACK_LICENSE_SERVICE_RECORD PerServerLicenseServices = NULL;
+ PLICENSE_SERVICE_RECORD pPerServerLicenseService;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: LicenseListPack\n"));
+#endif
+
+ ASSERT(pLicenseServices != NULL);
+ ASSERT(pLicenseServiceTableSize != NULL);
+
+ *pLicenseServices = NULL;
+ *pLicenseServiceTableSize = 0;
+
+ ASSERT(pLicenses != NULL);
+ ASSERT(pLicenseTableSize != NULL);
+
+ *pLicenses = NULL;
+ *pLicenseTableSize = 0;
+
+ ASSERT(pPerServerLicenseServices != NULL);
+ ASSERT(pPerServerLicenseServiceTableSize != NULL);
+
+ *pPerServerLicenseServices = NULL;
+ *pPerServerLicenseServiceTableSize = 0;
+
+ //////////////////////////////////////////////////////////////////
+ //
+ // Do License Service Table First
+ //
+ TotalRecords = LicenseServiceListSize;
+
+ //
+ // Make sure there is anything to replicate
+ //
+ if (TotalRecords > 0) {
+ //
+ // Create our buffer to hold all of the garbage
+ //
+ LicenseServices = MIDL_user_allocate(TotalRecords * sizeof(PACK_LICENSE_SERVICE_RECORD));
+ if (LicenseServices == NULL) {
+ ASSERT(FALSE);
+ return STATUS_NO_MEMORY;
+ }
+
+ //
+ // Fill in the buffer - walk the License Services tree
+ //
+ for (i = 0; i < LicenseServiceListSize; i++) {
+ pLicenseService = LicenseServiceList[i];
+
+ //
+ // Make index match table in it's current state
+ //
+ pLicenseService->Index = i;
+
+ LicenseServices[i].ServiceName = pLicenseService->ServiceName;
+ LicenseServices[i].NumberLicenses = pLicenseService->NumberLicenses;
+ }
+ }
+
+ *pLicenseServices = LicenseServices;
+ *pLicenseServiceTableSize = TotalRecords;
+
+ //////////////////////////////////////////////////////////////////
+ //
+ // Now Do Per Server License Service Table
+ //
+ TotalRecords = PerServerLicenseServiceListSize;
+
+ //
+ // Make sure there is anything to replicate
+ //
+ if (TotalRecords > 0)
+ {
+ //
+ // Create our buffer to hold all of the garbage
+ //
+ PerServerLicenseServices = MIDL_user_allocate(TotalRecords * sizeof(PACK_LICENSE_SERVICE_RECORD));
+ if (PerServerLicenseServices == NULL)
+ {
+ ASSERT(FALSE);
+
+ //
+ // Clean up already alloc'd information
+ //
+ if (LicenseServices != NULL)
+ MIDL_user_free(LicenseServices);
+
+ *pLicenseServices = NULL;
+ *pLicenseServiceTableSize = 0;
+
+ return STATUS_NO_MEMORY;
+ }
+
+ //
+ // Fill in the buffer - walk the Per Server License Services tree
+ //
+ for (i = 0; i < PerServerLicenseServiceListSize; i++)
+ {
+ pPerServerLicenseService = PerServerLicenseServiceList[i];
+
+ //
+ // Make index match table in it's current state
+ //
+ pPerServerLicenseService->Index = i;
+
+ PerServerLicenseServices[i].ServiceName = pPerServerLicenseService->ServiceName;
+ PerServerLicenseServices[i].NumberLicenses = pPerServerLicenseService->NumberLicenses;
+ }
+ }
+
+ *pPerServerLicenseServices = PerServerLicenseServices;
+ *pPerServerLicenseServiceTableSize = TotalRecords;
+
+ //////////////////////////////////////////////////////////////////
+ //
+ // Now Do License Purchase Records
+ //
+ TotalRecords = PurchaseListSize;
+
+ //
+ // Make sure there is anything to replicate
+ //
+ if (TotalRecords > 0) {
+ //
+ // Create our buffer to hold all of the garbage
+ //
+ Licenses = MIDL_user_allocate(TotalRecords * sizeof(PACK_LICENSE_PURCHASE_RECORD));
+ if (Licenses == NULL) {
+ ASSERT(FALSE);
+
+ //
+ // Clean up already alloc'd information
+ //
+ if (LicenseServices != NULL)
+ MIDL_user_free(LicenseServices);
+ if (PerServerLicenseServices != NULL)
+ MIDL_user_free(PerServerLicenseServices);
+
+ *pLicenseServices = NULL;
+ *pLicenseServiceTableSize = 0;
+ *pPerServerLicenseServices = NULL;
+ *pPerServerLicenseServiceTableSize = 0;
+
+ return STATUS_NO_MEMORY;
+ }
+
+ //
+ // Fill in the buffer - walk the License Purchase tree
+ //
+ for (i = 0; i < PurchaseListSize; i++) {
+ pLicense = &PurchaseList[i];
+
+ //
+ // License Service table index is fixed-up to what we need
+ //
+ Licenses[i].Service = ( pLicense->AllowedModes & 1 ) ? pLicense->Service->Index
+ : 0xFFFFFFFF;
+ Licenses[i].NumberLicenses = pLicense->NumberLicenses;
+ Licenses[i].Date = pLicense->Date;
+ Licenses[i].Admin = pLicense->Admin;
+ Licenses[i].Comment = pLicense->Comment;
+
+ Licenses[i].PerServerService = ( pLicense->AllowedModes & 2 ) ? pLicense->PerServerService->Index
+ : 0xFFFFFFFF;
+ Licenses[i].AllowedModes = pLicense->AllowedModes;
+ Licenses[i].CertificateID = pLicense->CertificateID;
+ Licenses[i].Source = pLicense->Source;
+ Licenses[i].ExpirationDate = pLicense->ExpirationDate;
+ Licenses[i].MaxQuantity = pLicense->MaxQuantity;
+ Licenses[i].Vendor = pLicense->Vendor;
+ memcpy( Licenses[i].Secrets, pLicense->Secrets, LLS_NUM_SECRETS * sizeof( *pLicense->Secrets ) );
+ }
+ }
+
+ *pLicenses = Licenses;
+ *pLicenseTableSize = TotalRecords;
+ return Status;
+} // LicenseListPack
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+LicenseListUnpack (
+ ULONG LicenseServiceTableSize,
+ PPACK_LICENSE_SERVICE_RECORD LicenseServices,
+
+ ULONG LicenseTableSize,
+ PPACK_LICENSE_PURCHASE_RECORD Licenses,
+
+ ULONG PerServerLicenseServiceTableSize,
+ PPACK_LICENSE_SERVICE_RECORD PerServerLicenseServices
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+Return Value:
+
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ ULONG i;
+ PPACK_LICENSE_PURCHASE_RECORD pLicense;
+ LPTSTR ServiceName;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LicenseListUnpack: Service[%lu] PerServerService[%lu] License[%lu]\n"), LicenseServiceTableSize, PerServerLicenseServiceTableSize, LicenseTableSize);
+#endif
+
+ //
+ // Walk services table, adding any new services to our local table.
+ // Fix up the index pointers to match our local services.
+ //
+ RtlAcquireResourceExclusive(&LicenseListLock, TRUE);
+
+ for (i = 0; i < LicenseTableSize; i++)
+ {
+ pLicense = &Licenses[i];
+
+ ServiceName = NULL;
+
+ if ( pLicense->AllowedModes & LLS_LICENSE_MODE_ALLOW_PER_SERVER )
+ {
+ if ( pLicense->PerServerService < PerServerLicenseServiceTableSize )
+ {
+ ServiceName = PerServerLicenseServices[ pLicense->PerServerService ].ServiceName;
+ }
+ else
+ {
+ ASSERT( FALSE );
+ }
+ }
+
+ if ( ( NULL == ServiceName ) && ( pLicense->AllowedModes & LLS_LICENSE_MODE_ALLOW_PER_SEAT ) )
+ {
+ if ( pLicense->Service < LicenseServiceTableSize )
+ {
+ ServiceName = LicenseServices[ pLicense->Service ].ServiceName;
+ }
+ else
+ {
+ ASSERT( FALSE );
+ }
+ }
+
+ if ( NULL == ServiceName )
+ {
+ ASSERT( FALSE );
+ }
+ else
+ {
+ Status = LicenseAdd( ServiceName, pLicense->Vendor, pLicense->NumberLicenses, pLicense->MaxQuantity, pLicense->Admin, pLicense->Comment, pLicense->Date, pLicense->AllowedModes, pLicense->CertificateID, pLicense->Source, pLicense->ExpirationDate, pLicense->Secrets );
+
+ if (Status != STATUS_SUCCESS)
+ {
+#ifdef DBG
+ dprintf(TEXT("LicenseAdd failed: 0x%lX\n"), Status);
+#endif
+ // ASSERT(FALSE);
+ }
+ }
+ }
+
+ RtlReleaseResource(&LicenseListLock);
+
+} // LicenseListUnpack
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+LicenseListStringsPack (
+ ULONG LicenseServiceTableSize,
+ PPACK_LICENSE_SERVICE_RECORD LicenseServices,
+
+ ULONG *pLicenseServiceStringSize,
+ LPTSTR *pLicenseServiceStrings,
+
+ ULONG LicenseTableSize,
+ PPACK_LICENSE_PURCHASE_RECORD Licenses,
+
+ ULONG *pLicenseStringSize,
+ LPTSTR *pLicenseStrings,
+
+ ULONG PerServerLicenseServiceTableSize,
+ PPACK_LICENSE_SERVICE_RECORD PerServerLicenseServices,
+
+ ULONG *pPerServerLicenseServiceStringSize,
+ LPTSTR *pPerServerLicenseServiceStrings
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+Return Value:
+
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ ULONG i;
+ ULONG StringSize;
+ PPACK_LICENSE_SERVICE_RECORD pSvc;
+ PPACK_LICENSE_PURCHASE_RECORD pLicense;
+ LPTSTR LicenseServiceStrings = NULL;
+ LPTSTR LicenseStrings = NULL;
+ TCHAR *pStr;
+ LPTSTR PerServerLicenseServiceStrings = NULL;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LicenseListStringsPack\n"));
+#endif
+
+ ASSERT(pLicenseServiceStrings != NULL);
+ ASSERT(pLicenseServiceStringSize != NULL);
+
+ *pLicenseServiceStrings = NULL;
+ *pLicenseServiceStringSize = 0;
+
+ ASSERT(pLicenseStrings != NULL);
+ ASSERT(pLicenseStringSize != NULL);
+
+ *pLicenseStrings = NULL;
+ *pLicenseStringSize = 0;
+
+ ASSERT(pPerServerLicenseServiceStrings != NULL);
+ ASSERT(pPerServerLicenseServiceStringSize != NULL);
+
+ *pPerServerLicenseServiceStrings = NULL;
+ *pPerServerLicenseServiceStringSize = 0;
+
+ //////////////////////////////////////////////////////////////////
+ //
+ // Do License Service Strings
+ //
+
+ //
+ // First walk the list adding up string sizes - to calculate our buff size
+ //
+ StringSize = 0;
+ for (i = 0; i < LicenseServiceTableSize; i++) {
+ pSvc = &LicenseServices[i];
+
+ StringSize = StringSize + lstrlen(pSvc->ServiceName) + 1;
+ }
+
+ //
+ // Make sure there is anything to replicate
+ //
+ if (StringSize > 0) {
+ //
+ // Create our buffer to hold all of the garbage
+ //
+ LicenseServiceStrings = MIDL_user_allocate(StringSize * sizeof(TCHAR));
+ if (LicenseServiceStrings == NULL) {
+ ASSERT(FALSE);
+ return STATUS_NO_MEMORY;
+ }
+
+ //
+ // Fill in the buffer
+ //
+ pStr = LicenseServiceStrings;
+ for (i = 0; i < LicenseServiceTableSize; i++) {
+ pSvc = &LicenseServices[i];
+
+ lstrcpy(pStr, pSvc->ServiceName);
+ pStr = &pStr[lstrlen(pSvc->ServiceName) + 1];
+ }
+ }
+
+ *pLicenseServiceStrings = LicenseServiceStrings;
+ *pLicenseServiceStringSize = StringSize;
+
+ //////////////////////////////////////////////////////////////////
+ //
+ // Do Per Server License Service Strings
+ //
+
+ //
+ // First walk the list adding up string sizes - to calculate our buff size
+ //
+ StringSize = 0;
+ for (i = 0; i < PerServerLicenseServiceTableSize; i++) {
+ pSvc = &PerServerLicenseServices[i];
+
+ StringSize = StringSize + lstrlen(pSvc->ServiceName) + 1;
+ }
+
+ //
+ // Make sure there is anything to replicate
+ //
+ if (StringSize > 0) {
+ //
+ // Create our buffer to hold all of the garbage
+ //
+ PerServerLicenseServiceStrings = MIDL_user_allocate(StringSize * sizeof(TCHAR));
+ if (PerServerLicenseServiceStrings == NULL)
+ {
+ ASSERT(FALSE);
+
+ //
+ // Clean up already alloc'd information
+ //
+ if (LicenseServiceStrings != NULL)
+ MIDL_user_free(LicenseServiceStrings);
+
+ *pLicenseServiceStrings = NULL;
+ *pLicenseServiceStringSize = 0;
+
+ return STATUS_NO_MEMORY;
+ }
+
+ //
+ // Fill in the buffer
+ //
+ pStr = PerServerLicenseServiceStrings;
+ for (i = 0; i < PerServerLicenseServiceTableSize; i++)
+ {
+ pSvc = &PerServerLicenseServices[i];
+
+ lstrcpy(pStr, pSvc->ServiceName);
+ pStr = &pStr[lstrlen(pSvc->ServiceName) + 1];
+ }
+ }
+
+ *pPerServerLicenseServiceStrings = PerServerLicenseServiceStrings;
+ *pPerServerLicenseServiceStringSize = StringSize;
+
+ //////////////////////////////////////////////////////////////////
+ //
+ // Now Do License Purchase Strings
+ //
+
+ //
+ // First walk the list adding up string sizes - to calculate our buff size
+ //
+ StringSize = 0;
+ for (i = 0; i < LicenseTableSize; i++) {
+ pLicense = &Licenses[i];
+
+ StringSize = StringSize + lstrlen(pLicense->Vendor) + 1;
+ StringSize = StringSize + lstrlen(pLicense->Admin) + 1;
+ StringSize = StringSize + lstrlen(pLicense->Comment) + 1;
+ StringSize = StringSize + lstrlen(pLicense->Source) + 1;
+ }
+
+ //
+ // Make sure there is anything to replicate
+ //
+ if (StringSize > 0) {
+ //
+ // Create our buffer to hold all of the garbage
+ //
+ LicenseStrings = MIDL_user_allocate(StringSize * sizeof(TCHAR));
+ if (LicenseStrings == NULL) {
+ ASSERT(FALSE);
+
+ //
+ // Clean up already alloc'd information
+ //
+ if (LicenseServiceStrings != NULL)
+ MIDL_user_free(LicenseServiceStrings);
+ if (PerServerLicenseServiceStrings != NULL)
+ MIDL_user_free(PerServerLicenseServiceStrings);
+
+ *pLicenseServiceStrings = NULL;
+ *pLicenseServiceStringSize = 0;
+ *pPerServerLicenseServiceStrings = NULL;
+ *pPerServerLicenseServiceStringSize = 0;
+
+ return STATUS_NO_MEMORY;
+ }
+
+ //
+ // Fill in the buffer
+ //
+ pStr = LicenseStrings;
+ for (i = 0; i < LicenseTableSize; i++) {
+ pLicense = &Licenses[i];
+
+ lstrcpy(pStr, pLicense->Vendor);
+ pStr = &pStr[lstrlen(pLicense->Vendor) + 1];
+
+ lstrcpy(pStr, pLicense->Admin);
+ pStr = &pStr[lstrlen(pLicense->Admin) + 1];
+
+ lstrcpy(pStr, pLicense->Comment);
+ pStr = &pStr[lstrlen(pLicense->Comment) + 1];
+
+ lstrcpy(pStr, pLicense->Source);
+ pStr = &pStr[lstrlen(pLicense->Source) + 1];
+ }
+ }
+
+ *pLicenseStrings = LicenseStrings;
+ *pLicenseStringSize = StringSize;
+
+ return Status;
+} // LicenseListStringsPack
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+LicenseListStringsUnpack (
+ ULONG LicenseServiceTableSize,
+ PPACK_LICENSE_SERVICE_RECORD LicenseServices,
+
+ ULONG LicenseServiceStringSize,
+ LPTSTR LicenseServiceStrings,
+
+ ULONG LicenseTableSize,
+ PPACK_LICENSE_PURCHASE_RECORD Licenses,
+
+ ULONG LicenseStringSize,
+ LPTSTR LicenseStrings,
+
+ ULONG PerServerLicenseServiceTableSize,
+ PPACK_LICENSE_SERVICE_RECORD PerServerLicenseServices,
+
+ ULONG PerServerLicenseServiceStringSize,
+ LPTSTR PerServerLicenseServiceStrings
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+Return Value:
+
+
+--*/
+
+{
+ ULONG i;
+ PPACK_LICENSE_SERVICE_RECORD pSvc;
+ PPACK_LICENSE_PURCHASE_RECORD pLicense;
+ TCHAR *pStr;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LicenseListStringsUnpack\n"));
+#endif
+
+ //
+ // First do per seat license service strings
+ //
+ pStr = LicenseServiceStrings;
+ for (i = 0; i < LicenseServiceTableSize; i++) {
+ pSvc = &LicenseServices[i];
+
+ pSvc->ServiceName = pStr;
+
+ //
+ // Move to end of current string
+ //
+ while (*pStr != TEXT('\0'))
+ pStr++;
+
+ // now go past ending NULL
+ pStr++;
+ }
+
+ //
+ // Then do per server license service strings
+ //
+ pStr = PerServerLicenseServiceStrings;
+ for (i = 0; i < PerServerLicenseServiceTableSize; i++) {
+ pSvc = &PerServerLicenseServices[i];
+
+ pSvc->ServiceName = pStr;
+
+ //
+ // Move to end of current string
+ //
+ while (*pStr != TEXT('\0'))
+ pStr++;
+
+ // now go past ending NULL
+ pStr++;
+ }
+
+ //
+ // Now do license purchase strings
+ //
+ pStr = LicenseStrings;
+ for (i = 0; i < LicenseTableSize; i++) {
+ pLicense = &Licenses[i];
+
+ pLicense->Vendor = pStr;
+
+ //
+ // Move to end of current string
+ //
+ while (*pStr != TEXT('\0'))
+ pStr++;
+
+ // now go past ending NULL
+ pStr++;
+
+ pLicense->Admin = pStr;
+
+ //
+ // Move to end of current string
+ //
+ while (*pStr != TEXT('\0'))
+ pStr++;
+
+ // now go past ending NULL
+ pStr++;
+
+ pLicense->Comment = pStr;
+
+ //
+ // Move to end of current string
+ //
+ while (*pStr != TEXT('\0'))
+ pStr++;
+
+ // now go past ending NULL
+ pStr++;
+
+ pLicense->Source = pStr;
+
+ //
+ // Move to end of current string
+ //
+ while (*pStr != TEXT('\0'))
+ pStr++;
+
+ // now go past ending NULL
+ pStr++;
+ }
+
+} // LicenseListStringsUnpack
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+LicenseListLoad()
+
+/*++
+
+Routine Description:
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ BOOL ret;
+ DWORD Version, DataSize;
+ NTSTATUS Status = STATUS_SUCCESS;
+ HANDLE hFile = NULL;
+
+ ULONG LicenseServiceTableSize;
+ PPACK_LICENSE_SERVICE_RECORD LicenseServices = NULL;
+
+ ULONG LicenseServiceStringSize;
+ LPTSTR LicenseServiceStrings = NULL;
+
+ ULONG PerServerLicenseServiceTableSize;
+ PPACK_LICENSE_SERVICE_RECORD PerServerLicenseServices = NULL;
+
+ ULONG PerServerLicenseServiceStringSize;
+ LPTSTR PerServerLicenseServiceStrings = NULL;
+
+ ULONG LicenseTableSize;
+ PPACK_LICENSE_PURCHASE_RECORD Licenses = NULL;
+
+ ULONG LicenseStringSize;
+ LPTSTR LicenseStrings = NULL;
+
+ LICENSE_FILE_HEADER FileHeader;
+ DWORD BytesRead;
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_DATABASE))
+ dprintf(TEXT("LLS TRACE: LicenseListLoad\n"));
+#endif
+
+ //
+ // Check if we already have file open
+ //
+ if (PurchaseFile != NULL) {
+ CloseHandle(PurchaseFile);
+ PurchaseFile = NULL;
+ }
+
+ //
+ // If nothing to load then get-out
+ //
+ if (!FileExists(LicenseFileName))
+ goto LicenseListLoadExit;
+
+ //
+ // Check the init header
+ //
+ Version = DataSize = 0;
+ PurchaseFile = LlsFileCheck(LicenseFileName, &Version, &DataSize );
+ if (PurchaseFile == NULL) {
+ Status = GetLastError();
+ goto LicenseListLoadExit;
+ }
+
+ if ( ( Version == LICENSE_FILE_VERSION_0 ) && ( DataSize == sizeof(LICENSE_FILE_HEADER_0) ) ) {
+ CloseHandle(PurchaseFile);
+ PurchaseFile = NULL;
+
+ LicenseListLoadOld();
+ return;
+ }
+
+ if ( ( Version != LICENSE_FILE_VERSION ) || ( DataSize != sizeof(LICENSE_FILE_HEADER) ) ) {
+ Status = STATUS_FILE_INVALID;
+ goto LicenseListLoadExit;
+ }
+
+ //
+ // The init header checks out, so load the license header and data blocks
+ //
+ hFile = PurchaseFile;
+ ret = ReadFile(hFile, &FileHeader, sizeof(LICENSE_FILE_HEADER), &BytesRead, NULL);
+
+ LicenseServiceTableSize = 0;
+ LicenseServiceStringSize = 0;
+ LicenseTableSize = 0;
+ LicenseStringSize = 0;
+
+ if (ret) {
+ //
+ // Run through and allocate space to read data blocks into
+ //
+ if (FileHeader.LicenseServiceTableSize != 0) {
+ LicenseServiceTableSize = FileHeader.LicenseServiceTableSize / sizeof(PACK_LICENSE_SERVICE_RECORD);
+ LicenseServices = MIDL_user_allocate(FileHeader.LicenseServiceTableSize);
+
+ if ( LicenseServices == NULL ) {
+ Status = STATUS_NO_MEMORY;
+ goto LicenseListLoadExit;
+ }
+ }
+
+ if (FileHeader.LicenseServiceStringSize != 0) {
+ LicenseServiceStringSize = FileHeader.LicenseServiceStringSize / sizeof(TCHAR);
+ LicenseServiceStrings = MIDL_user_allocate(FileHeader.LicenseServiceStringSize);
+
+ if ( LicenseServiceStrings == NULL ) {
+ Status = STATUS_NO_MEMORY;
+ goto LicenseListLoadExit;
+ }
+ }
+
+ if (FileHeader.LicenseServiceTableSize != 0) {
+ PerServerLicenseServiceTableSize = FileHeader.PerServerLicenseServiceTableSize / sizeof(PACK_LICENSE_SERVICE_RECORD);
+ PerServerLicenseServices = MIDL_user_allocate(FileHeader.PerServerLicenseServiceTableSize);
+
+ if ( PerServerLicenseServices == NULL ) {
+ Status = STATUS_NO_MEMORY;
+ goto LicenseListLoadExit;
+ }
+ }
+
+ if (FileHeader.PerServerLicenseServiceStringSize != 0) {
+ PerServerLicenseServiceStringSize = FileHeader.PerServerLicenseServiceStringSize / sizeof(TCHAR);
+ PerServerLicenseServiceStrings = MIDL_user_allocate(FileHeader.PerServerLicenseServiceStringSize);
+
+ if ( PerServerLicenseServiceStrings == NULL ) {
+ Status = STATUS_NO_MEMORY;
+ goto LicenseListLoadExit;
+ }
+ }
+
+ if (FileHeader.LicenseTableSize != 0) {
+ LicenseTableSize = FileHeader.LicenseTableSize / sizeof(PACK_LICENSE_PURCHASE_RECORD);
+ Licenses = MIDL_user_allocate(FileHeader.LicenseTableSize);
+
+ if ( Licenses == NULL ) {
+ Status = STATUS_NO_MEMORY;
+ goto LicenseListLoadExit;
+ }
+ }
+
+ if (FileHeader.LicenseStringSize != 0) {
+ LicenseStringSize = FileHeader.LicenseStringSize / sizeof(TCHAR);
+ LicenseStrings = MIDL_user_allocate(FileHeader.LicenseStringSize);
+
+ if ( LicenseStrings == NULL ) {
+ Status = STATUS_NO_MEMORY;
+ goto LicenseListLoadExit;
+ }
+ }
+
+ }
+
+ if (ret && (FileHeader.LicenseServiceTableSize != 0) )
+ ret = ReadFile(hFile, LicenseServices, FileHeader.LicenseServiceTableSize, &BytesRead, NULL);
+
+ if (ret && (FileHeader.LicenseServiceStringSize != 0) )
+ ret = ReadFile(hFile, LicenseServiceStrings, FileHeader.LicenseServiceStringSize, &BytesRead, NULL);
+
+ if (ret && (FileHeader.PerServerLicenseServiceTableSize != 0) )
+ ret = ReadFile(hFile, PerServerLicenseServices, FileHeader.PerServerLicenseServiceTableSize, &BytesRead, NULL);
+
+ if (ret && (FileHeader.PerServerLicenseServiceStringSize != 0) )
+ ret = ReadFile(hFile, PerServerLicenseServiceStrings, FileHeader.PerServerLicenseServiceStringSize, &BytesRead, NULL);
+
+ if (ret && (FileHeader.LicenseTableSize != 0) )
+ ret = ReadFile(hFile, Licenses, FileHeader.LicenseTableSize, &BytesRead, NULL);
+
+ if (ret && (FileHeader.LicenseStringSize != 0) )
+ ret = ReadFile(hFile, LicenseStrings, FileHeader.LicenseStringSize, &BytesRead, NULL);
+
+ if (!ret) {
+ Status = GetLastError();
+ goto LicenseListLoadExit;
+ }
+
+ //
+ // Decrypt the data
+ //
+ Status = DeBlock(LicenseServices, FileHeader.LicenseServiceTableSize);
+
+ if (Status == STATUS_SUCCESS)
+ Status = DeBlock(LicenseServiceStrings, FileHeader.LicenseServiceStringSize);
+
+ if (Status == STATUS_SUCCESS)
+ Status = DeBlock(PerServerLicenseServices, FileHeader.PerServerLicenseServiceTableSize);
+
+ if (Status == STATUS_SUCCESS)
+ Status = DeBlock(PerServerLicenseServiceStrings, FileHeader.PerServerLicenseServiceStringSize);
+
+ if (Status == STATUS_SUCCESS)
+ Status = DeBlock(Licenses, FileHeader.LicenseTableSize);
+
+ if (Status == STATUS_SUCCESS)
+ Status = DeBlock(LicenseStrings, FileHeader.LicenseStringSize);
+
+ if (Status != STATUS_SUCCESS)
+ goto LicenseListLoadExit;
+
+
+ //
+ // Unpack the string data
+ //
+ LicenseListStringsUnpack( LicenseServiceTableSize, LicenseServices,
+ LicenseServiceStringSize, LicenseServiceStrings,
+ LicenseTableSize, Licenses,
+ LicenseStringSize, LicenseStrings,
+ PerServerLicenseServiceTableSize, PerServerLicenseServices,
+ PerServerLicenseServiceStringSize, PerServerLicenseServiceStrings
+ );
+
+ //
+ // Unpack the license data
+ //
+ LicenseListUnpack( LicenseServiceTableSize, LicenseServices, LicenseTableSize, Licenses, PerServerLicenseServiceTableSize, PerServerLicenseServices );
+
+LicenseListLoadExit:
+
+ // Note: Don't close the License Purchase File (keep it locked).
+
+ //
+ // Run through our tables and clean them up
+ //
+ if (LicenseServices != NULL)
+ MIDL_user_free(LicenseServices);
+
+ if (LicenseServiceStrings != NULL)
+ MIDL_user_free(LicenseServiceStrings);
+
+ if (PerServerLicenseServices != NULL)
+ MIDL_user_free(PerServerLicenseServices);
+
+ if (PerServerLicenseServiceStrings != NULL)
+ MIDL_user_free(PerServerLicenseServiceStrings);
+
+ if (Licenses != NULL)
+ MIDL_user_free(Licenses);
+
+ if (LicenseStrings != NULL)
+ MIDL_user_free(LicenseStrings);
+
+ //
+ // If there was an error log it.
+ //
+ if (Status != STATUS_SUCCESS)
+ LogEvent(LLS_EVENT_LOAD_LICENSE, 0, NULL, Status);
+
+} // LicenseListLoad
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+LicenseListSave()
+
+/*++
+
+Routine Description:
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ BOOL ret = TRUE;
+ NTSTATUS Status = STATUS_SUCCESS;
+ HANDLE hFile = NULL;
+
+ ULONG LicenseServiceTableSize;
+ PPACK_LICENSE_SERVICE_RECORD LicenseServices = NULL;
+
+ ULONG LicenseServiceStringSize;
+ LPTSTR LicenseServiceStrings = NULL;
+
+ ULONG PerServerLicenseServiceTableSize;
+ PPACK_LICENSE_SERVICE_RECORD PerServerLicenseServices = NULL;
+
+ ULONG PerServerLicenseServiceStringSize;
+ LPTSTR PerServerLicenseServiceStrings = NULL;
+
+ ULONG LicenseTableSize;
+ PPACK_LICENSE_PURCHASE_RECORD Licenses = NULL;
+
+ ULONG LicenseStringSize;
+ LPTSTR LicenseStrings = NULL;
+
+ LICENSE_FILE_HEADER FileHeader;
+ DWORD BytesWritten;
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_DATABASE))
+ dprintf(TEXT("LLS TRACE: LicenseListSave\n"));
+#endif
+
+ //
+ // Check if we already have file open
+ //
+ if (PurchaseFile != NULL) {
+ CloseHandle(PurchaseFile);
+ PurchaseFile = NULL;
+ }
+
+ RtlAcquireResourceExclusive(&LicenseListLock, TRUE);
+
+ //
+ // If nothing to save then get-out
+ //
+ if ( (LicenseServiceListSize == 0) && (PerServerLicenseServiceListSize == 0) )
+ goto LicenseListSaveExit;
+
+ //
+ // Pack the license data
+ //
+ Status = LicenseListPack( &LicenseServiceTableSize, &LicenseServices, &LicenseTableSize, &Licenses, &PerServerLicenseServiceTableSize, &PerServerLicenseServices );
+ if (Status != STATUS_SUCCESS)
+ goto LicenseListSaveExit;
+
+ //
+ // Now pack the String data
+ //
+ Status = LicenseListStringsPack( LicenseServiceTableSize, LicenseServices,
+ &LicenseServiceStringSize, &LicenseServiceStrings,
+ LicenseTableSize, Licenses,
+ &LicenseStringSize, &LicenseStrings,
+ PerServerLicenseServiceTableSize, PerServerLicenseServices,
+ &PerServerLicenseServiceStringSize, &PerServerLicenseServiceStrings );
+
+ if (Status != STATUS_SUCCESS)
+ goto LicenseListSaveExit;
+
+ //
+ // Fill out the file header - sizes are byte sizes
+ //
+ FileHeader.LicenseServiceTableSize = LicenseServiceTableSize * sizeof(PACK_LICENSE_SERVICE_RECORD);
+ FileHeader.LicenseServiceStringSize = LicenseServiceStringSize * sizeof(TCHAR);
+ FileHeader.PerServerLicenseServiceTableSize = PerServerLicenseServiceTableSize * sizeof(PACK_LICENSE_SERVICE_RECORD);
+ FileHeader.PerServerLicenseServiceStringSize = PerServerLicenseServiceStringSize * sizeof(TCHAR);
+ FileHeader.LicenseTableSize = LicenseTableSize * sizeof(PACK_LICENSE_PURCHASE_RECORD);
+ FileHeader.LicenseStringSize = LicenseStringSize * sizeof(TCHAR);
+
+ //
+ // Encrypt the data before saving it out.
+ //
+ Status = EBlock(LicenseServices, FileHeader.LicenseServiceTableSize);
+
+ if (Status == STATUS_SUCCESS)
+ Status = EBlock(LicenseServiceStrings, FileHeader.LicenseServiceStringSize);
+
+ if (Status == STATUS_SUCCESS)
+ Status = EBlock(PerServerLicenseServices, FileHeader.PerServerLicenseServiceTableSize);
+
+ if (Status == STATUS_SUCCESS)
+ Status = EBlock(PerServerLicenseServiceStrings, FileHeader.PerServerLicenseServiceStringSize);
+
+ if (Status == STATUS_SUCCESS)
+ Status = EBlock(Licenses, FileHeader.LicenseTableSize);
+
+ if (Status == STATUS_SUCCESS)
+ Status = EBlock(LicenseStrings, FileHeader.LicenseStringSize);
+
+ if (Status != STATUS_SUCCESS)
+ goto LicenseListSaveExit;
+
+ //
+ // Save out the header record
+ //
+ PurchaseFile = LlsFileInit(LicenseFileName, LICENSE_FILE_VERSION, sizeof(LICENSE_FILE_HEADER) );
+ if (PurchaseFile == NULL) {
+ Status = GetLastError();
+ goto LicenseListSaveExit;
+ }
+
+ //
+ // Now write out all the data blocks
+ //
+ hFile = PurchaseFile;
+
+ ret = WriteFile(hFile, &FileHeader, sizeof(LICENSE_FILE_HEADER), &BytesWritten, NULL);
+
+ if (ret && (LicenseServices != NULL) && (FileHeader.LicenseServiceTableSize != 0))
+ ret = WriteFile(hFile, LicenseServices, FileHeader.LicenseServiceTableSize, &BytesWritten, NULL);
+
+ if (ret && (LicenseServiceStrings != NULL) && (FileHeader.LicenseServiceStringSize != 0))
+ ret = WriteFile(hFile, LicenseServiceStrings, FileHeader.LicenseServiceStringSize, &BytesWritten, NULL);
+
+ if (ret && (PerServerLicenseServices != NULL) && (FileHeader.PerServerLicenseServiceTableSize != 0))
+ ret = WriteFile(hFile, PerServerLicenseServices, FileHeader.PerServerLicenseServiceTableSize, &BytesWritten, NULL);
+
+ if (ret && (PerServerLicenseServiceStrings != NULL) && (FileHeader.PerServerLicenseServiceStringSize != 0))
+ ret = WriteFile(hFile, PerServerLicenseServiceStrings, FileHeader.PerServerLicenseServiceStringSize, &BytesWritten, NULL);
+
+ if (ret && (Licenses != NULL) && (FileHeader.LicenseTableSize != 0))
+ ret = WriteFile(hFile, Licenses, FileHeader.LicenseTableSize, &BytesWritten, NULL);
+
+ if (ret && (LicenseStrings != NULL) && (FileHeader.LicenseStringSize != 0))
+ ret = WriteFile(hFile, LicenseStrings, FileHeader.LicenseStringSize, &BytesWritten, NULL);
+
+ if (!ret)
+ Status = GetLastError();
+
+LicenseListSaveExit:
+ RtlReleaseResource(&LicenseListLock);
+
+ // Note: Don't close the License Purchase File (keep it locked).
+ if (hFile != NULL)
+ FlushFileBuffers(hFile);
+
+ //
+ // Run through our tables and clean them up
+ //
+ if (LicenseServices != NULL)
+ MIDL_user_free(LicenseServices);
+
+ if (LicenseServiceStrings != NULL)
+ MIDL_user_free(LicenseServiceStrings);
+
+ if (PerServerLicenseServices != NULL)
+ MIDL_user_free(PerServerLicenseServices);
+
+ if (PerServerLicenseServiceStrings != NULL)
+ MIDL_user_free(PerServerLicenseServiceStrings);
+
+ if (Licenses != NULL)
+ MIDL_user_free(Licenses);
+
+ if (LicenseStrings != NULL)
+ MIDL_user_free(LicenseStrings);
+
+ //
+ // If there was an error log it.
+ //
+ if (Status != STATUS_SUCCESS)
+ LogEvent(LLS_EVENT_SAVE_LICENSE, 0, NULL, Status);
+
+ return Status;
+} // LicenseListSave
+
+
+
+/////////////////////////////////////////////////////////////////////////
+// Mapping List
+//
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+MappingListPack (
+ ULONG *pMappingUserTableSize,
+ PPACK_MAPPING_USER_RECORD *pMappingUsers,
+
+ ULONG *pMappingTableSize,
+ PPACK_MAPPING_RECORD *pMappings
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+Return Value:
+
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ PPACK_MAPPING_USER_RECORD MappingUsers = NULL;
+ PPACK_MAPPING_RECORD Mappings = NULL;
+ ULONG i, j, k;
+ ULONG TotalRecords = 0;
+ PMAPPING_RECORD pMapping;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: MappingListPack\n"));
+#endif
+
+ ASSERT(pMappingUsers != NULL);
+ ASSERT(pMappingUserTableSize != NULL);
+
+ *pMappingUsers = NULL;
+ *pMappingUserTableSize = 0;
+
+ ASSERT(pMappings != NULL);
+ ASSERT(pMappingTableSize != NULL);
+
+ *pMappings = NULL;
+ *pMappingTableSize = 0;
+
+ //////////////////////////////////////////////////////////////////
+ //
+ // Do Mapping User Table First
+ //
+ TotalRecords = 0;
+
+ //
+ // Make sure there is anything to replicate
+ //
+ for (i = 0; i < MappingListSize; i++)
+ TotalRecords += MappingList[i]->NumMembers;
+
+ if (TotalRecords > 0) {
+ //
+ // Create our buffer to hold all of the garbage
+ //
+ MappingUsers = MIDL_user_allocate(TotalRecords * sizeof(PACK_MAPPING_USER_RECORD));
+ if (MappingUsers == NULL) {
+ ASSERT(FALSE);
+ return STATUS_NO_MEMORY;
+ }
+
+ //
+ // Fill in the buffer - walk the Mapping tree
+ //
+ k = 0;
+ for (i = 0; i < MappingListSize; i++) {
+ pMapping = MappingList[i];
+
+ for (j = 0; j < pMapping->NumMembers; j++) {
+ MappingUsers[k].Mapping = i;
+ MappingUsers[k].Name = pMapping->Members[j];
+ k++;
+ }
+ }
+ }
+
+ *pMappingUsers = MappingUsers;
+ *pMappingUserTableSize = TotalRecords;
+
+ //////////////////////////////////////////////////////////////////
+ //
+ // Now Do Mapping Records
+ //
+ TotalRecords = MappingListSize;
+
+ //
+ // Make sure there is anything to replicate
+ //
+ if (TotalRecords > 0) {
+ //
+ // Create our buffer to hold all of the garbage
+ //
+ Mappings = MIDL_user_allocate(TotalRecords * sizeof(PACK_MAPPING_RECORD));
+ if (Mappings == NULL) {
+ ASSERT(FALSE);
+
+ //
+ // Clean up already alloc'd information
+ //
+ if (MappingUsers != NULL)
+ MIDL_user_free(MappingUsers);
+
+ *pMappingUsers = NULL;
+ *pMappingUserTableSize = 0;
+
+ return STATUS_NO_MEMORY;
+ }
+
+ //
+ // Fill in the buffer - walk the License Purchase tree
+ //
+ for (i = 0; i < MappingListSize; i++) {
+ pMapping = MappingList[i];
+
+ Mappings[i].Name = pMapping->Name;
+ Mappings[i].Comment = pMapping->Comment;
+ Mappings[i].Licenses = pMapping->Licenses;
+ }
+ }
+
+ *pMappings = Mappings;
+ *pMappingTableSize = TotalRecords;
+ return Status;
+} // MappingListPack
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+MappingListUnpack (
+ ULONG MappingUserTableSize,
+ PPACK_MAPPING_USER_RECORD MappingUsers,
+
+ ULONG MappingTableSize,
+ PPACK_MAPPING_RECORD Mappings
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+Return Value:
+
+
+--*/
+
+{
+ NTSTATUS Status;
+ ULONG i;
+ PPACK_MAPPING_USER_RECORD pUsr;
+ PPACK_MAPPING_RECORD pMapping;
+ PMAPPING_RECORD pMap;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("MappingListUnpack: Mappings[%lu] TotalUsers[%lu]\n"), MappingTableSize, MappingUserTableSize);
+#endif
+
+ RtlAcquireResourceExclusive(&MappingListLock, TRUE);
+
+ //
+ // Add the Mappings first
+ //
+ for (i = 0; i < MappingTableSize; i++) {
+ pMapping = &Mappings[i];
+
+ pMap = MappingListAdd(pMapping->Name, pMapping->Comment, pMapping->Licenses);
+
+ ASSERT(pMap != NULL);
+ }
+
+ //
+ // Now add the users to the mappings...
+ //
+ for (i = 0; i < MappingUserTableSize; i++) {
+ pUsr = &MappingUsers[i];
+
+ pMap = NULL;
+ if (pUsr->Mapping < MappingTableSize)
+ pMap = MappingUserListAdd(Mappings[pUsr->Mapping].Name, pUsr->Name);
+
+#if DBG
+ if (pMap == NULL) {
+ dprintf(TEXT("pMap: 0x%lX pUsr->Mapping: %lu MappingTableSize: %lu\n"), pMap, pUsr->Mapping, MappingTableSize);
+ ASSERT(FALSE);
+ }
+#endif
+ }
+
+ RtlReleaseResource(&MappingListLock);
+
+} // MappingListUnpack
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+MappingListStringsPack (
+ ULONG MappingUserTableSize,
+ PPACK_MAPPING_USER_RECORD MappingUsers,
+
+ ULONG *pMappingUserStringSize,
+ LPTSTR *pMappingUserStrings,
+
+ ULONG MappingTableSize,
+ PPACK_MAPPING_RECORD Mappings,
+
+ ULONG *pMappingStringSize,
+ LPTSTR *pMappingStrings
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+Return Value:
+
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ ULONG i;
+ ULONG StringSize;
+ PPACK_MAPPING_USER_RECORD pUsr;
+ PPACK_MAPPING_RECORD pMapping;
+ LPTSTR MappingUserStrings = NULL;
+ LPTSTR MappingStrings = NULL;
+ TCHAR *pStr;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("MappingListStringsPack\n"));
+#endif
+
+ ASSERT(pMappingUserStrings != NULL);
+ ASSERT(pMappingUserStringSize != NULL);
+
+ *pMappingUserStrings = NULL;
+ *pMappingUserStringSize = 0;
+
+ ASSERT(pMappingStrings != NULL);
+ ASSERT(pMappingStringSize != NULL);
+
+ *pMappingStrings = NULL;
+ *pMappingStringSize = 0;
+
+ //////////////////////////////////////////////////////////////////
+ //
+ // Do Mapping User Strings
+ //
+
+ //
+ // First walk the list adding up string sizes - to calculate our buff size
+ //
+ StringSize = 0;
+ for (i = 0; i < MappingUserTableSize; i++) {
+ pUsr = &MappingUsers[i];
+
+ StringSize = StringSize + lstrlen(pUsr->Name) + 1;
+ }
+
+ //
+ // Make sure there is anything to replicate
+ //
+ if (StringSize > 0) {
+ //
+ // Create our buffer to hold all of the garbage
+ //
+ MappingUserStrings = MIDL_user_allocate(StringSize * sizeof(TCHAR));
+ if (MappingUserStrings == NULL) {
+ ASSERT(FALSE);
+ return STATUS_NO_MEMORY;
+ }
+
+ //
+ // Fill in the buffer
+ //
+ pStr = MappingUserStrings;
+ for (i = 0; i < MappingUserTableSize; i++) {
+ pUsr = &MappingUsers[i];
+
+ lstrcpy(pStr, pUsr->Name);
+ pStr = &pStr[lstrlen(pUsr->Name) + 1];
+ }
+ }
+
+ *pMappingUserStrings = MappingUserStrings;
+ *pMappingUserStringSize = StringSize;
+
+ //////////////////////////////////////////////////////////////////
+ //
+ // Now Do Mapping Strings
+ //
+
+ //
+ // First walk the list adding up string sizes - to calculate our buff size
+ //
+ StringSize = 0;
+ for (i = 0; i < MappingTableSize; i++) {
+ pMapping = &Mappings[i];
+
+ StringSize = StringSize + lstrlen(pMapping->Name) + 1;
+ StringSize = StringSize + lstrlen(pMapping->Comment) + 1;
+ }
+
+ //
+ // Make sure there is anything to replicate
+ //
+ if (StringSize > 0) {
+ //
+ // Create our buffer to hold all of the garbage
+ //
+ MappingStrings = MIDL_user_allocate(StringSize * sizeof(TCHAR));
+ if (MappingStrings == NULL) {
+ ASSERT(FALSE);
+
+ //
+ // Clean up already alloc'd information
+ //
+ if (MappingUserStrings != NULL)
+ MIDL_user_free(MappingUserStrings);
+
+ *pMappingUserStrings = NULL;
+ *pMappingUserStringSize = 0;
+
+ return STATUS_NO_MEMORY;
+ }
+
+ //
+ // Fill in the buffer
+ //
+ pStr = MappingStrings;
+ for (i = 0; i < MappingTableSize; i++) {
+ pMapping = &Mappings[i];
+
+ lstrcpy(pStr, pMapping->Name);
+ pStr = &pStr[lstrlen(pMapping->Name) + 1];
+
+ lstrcpy(pStr, pMapping->Comment);
+ pStr = &pStr[lstrlen(pMapping->Comment) + 1];
+ }
+ }
+
+ *pMappingStrings = MappingStrings;
+ *pMappingStringSize = StringSize;
+
+ return Status;
+} // MappingListStringsPack
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+MappingListStringsUnpack (
+ ULONG MappingUserTableSize,
+ PPACK_MAPPING_USER_RECORD MappingUsers,
+
+ ULONG MappingUserStringSize,
+ LPTSTR MappingUserStrings,
+
+ ULONG MappingTableSize,
+ PPACK_MAPPING_RECORD Mappings,
+
+ ULONG MappingStringSize,
+ LPTSTR MappingStrings
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+Return Value:
+
+
+--*/
+
+{
+ ULONG i;
+ PPACK_MAPPING_USER_RECORD pUsr;
+ PPACK_MAPPING_RECORD pMapping;
+ TCHAR *pStr;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("MappingListStringsUnpack\n"));
+#endif
+
+ //
+ // First do license service strings
+ //
+ pStr = MappingUserStrings;
+ for (i = 0; i < MappingUserTableSize; i++) {
+ pUsr = &MappingUsers[i];
+
+ pUsr->Name = pStr;
+
+ //
+ // Move to end of current string
+ //
+ while (*pStr != TEXT('\0'))
+ pStr++;
+
+ // now go past ending NULL
+ pStr++;
+ }
+
+ //
+ // Now do license purchase strings
+ //
+ pStr = MappingStrings;
+ for (i = 0; i < MappingTableSize; i++) {
+ pMapping = &Mappings[i];
+
+ pMapping->Name = pStr;
+
+ //
+ // Move to end of current string
+ //
+ while (*pStr != TEXT('\0'))
+ pStr++;
+
+ // now go past ending NULL
+ pStr++;
+
+ pMapping->Comment = pStr;
+
+ //
+ // Move to end of current string
+ //
+ while (*pStr != TEXT('\0'))
+ pStr++;
+
+ // now go past ending NULL
+ pStr++;
+ }
+
+} // MappingListStringsUnpack
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+MappingListLoad()
+
+/*++
+
+Routine Description:
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ BOOL ret;
+ DWORD Version, DataSize;
+ NTSTATUS Status = STATUS_SUCCESS;
+ HANDLE hFile = NULL;
+
+ ULONG MappingUserTableSize;
+ PPACK_MAPPING_USER_RECORD MappingUsers = NULL;
+
+ ULONG MappingUserStringSize;
+ LPTSTR MappingUserStrings = NULL;
+
+ ULONG MappingTableSize;
+ PPACK_MAPPING_RECORD Mappings = NULL;
+
+ ULONG MappingStringSize;
+ LPTSTR MappingStrings = NULL;
+
+ MAPPING_FILE_HEADER FileHeader;
+ DWORD BytesRead;
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_DATABASE))
+ dprintf(TEXT("LLS TRACE: MappingListLoad\n"));
+#endif
+
+ //
+ // If nothing to load then get-out
+ //
+ if (!FileExists(MappingFileName))
+ goto MappingListLoadExit;
+
+ //
+ // Check the init header
+ //
+ Version = DataSize = 0;
+ hFile = LlsFileCheck(MappingFileName, &Version, &DataSize );
+ if (hFile == NULL) {
+ Status = GetLastError();
+ goto MappingListLoadExit;
+ }
+
+ if ((Version != MAPPING_FILE_VERSION) || (DataSize != sizeof(MAPPING_FILE_HEADER))) {
+ Status = STATUS_FILE_INVALID;
+ goto MappingListLoadExit;
+ }
+
+ //
+ // The init header checks out, so load the license header and data blocks
+ //
+ ret = ReadFile(hFile, &FileHeader, sizeof(MAPPING_FILE_HEADER), &BytesRead, NULL);
+
+ MappingUserTableSize = 0;
+ MappingUserStringSize = 0;
+ MappingTableSize = 0;
+ MappingStringSize = 0;
+
+ if (ret) {
+ //
+ // Run through and allocate space to read data blocks into
+ //
+ if (FileHeader.MappingUserTableSize != 0) {
+ MappingUserTableSize = FileHeader.MappingUserTableSize / sizeof(PACK_MAPPING_USER_RECORD);
+ MappingUsers = MIDL_user_allocate(FileHeader.MappingUserTableSize);
+
+ if ( MappingUsers == NULL ) {
+ Status = STATUS_NO_MEMORY;
+ goto MappingListLoadExit;
+ }
+ }
+
+ if (FileHeader.MappingUserStringSize != 0) {
+ MappingUserStringSize = FileHeader.MappingUserStringSize / sizeof(TCHAR);
+ MappingUserStrings = MIDL_user_allocate(FileHeader.MappingUserStringSize);
+
+ if ( MappingUserStrings == NULL ) {
+ Status = STATUS_NO_MEMORY;
+ goto MappingListLoadExit;
+ }
+ }
+
+ if (FileHeader.MappingTableSize != 0) {
+ MappingTableSize = FileHeader.MappingTableSize / sizeof(PACK_MAPPING_RECORD);
+ Mappings = MIDL_user_allocate(FileHeader.MappingTableSize);
+
+ if ( Mappings == NULL ) {
+ Status = STATUS_NO_MEMORY;
+ goto MappingListLoadExit;
+ }
+ }
+
+ if (FileHeader.MappingStringSize != 0) {
+ MappingStringSize = FileHeader.MappingStringSize / sizeof(TCHAR);
+ MappingStrings = MIDL_user_allocate(FileHeader.MappingStringSize);
+
+ if ( MappingStrings == NULL ) {
+ Status = STATUS_NO_MEMORY;
+ goto MappingListLoadExit;
+ }
+ }
+
+ }
+
+ if (ret && (FileHeader.MappingUserTableSize != 0) )
+ ret = ReadFile(hFile, MappingUsers, FileHeader.MappingUserTableSize, &BytesRead, NULL);
+
+ if (ret && (FileHeader.MappingUserStringSize != 0) )
+ ret = ReadFile(hFile, MappingUserStrings, FileHeader.MappingUserStringSize, &BytesRead, NULL);
+
+ if (ret && (FileHeader.MappingTableSize != 0) )
+ ret = ReadFile(hFile, Mappings, FileHeader.MappingTableSize, &BytesRead, NULL);
+
+ if (ret && (FileHeader.MappingStringSize != 0) )
+ ret = ReadFile(hFile, MappingStrings, FileHeader.MappingStringSize, &BytesRead, NULL);
+
+ if (!ret) {
+ Status = GetLastError();
+ goto MappingListLoadExit;
+ }
+
+ //
+ // Decrypt the data
+ //
+ Status = DeBlock(MappingUsers, FileHeader.MappingUserTableSize);
+
+ if (Status == STATUS_SUCCESS)
+ Status = DeBlock(MappingUserStrings, FileHeader.MappingUserStringSize);
+
+ if (Status == STATUS_SUCCESS)
+ Status = DeBlock(Mappings, FileHeader.MappingTableSize);
+
+ if (Status == STATUS_SUCCESS)
+ Status = DeBlock(MappingStrings, FileHeader.MappingStringSize);
+
+ if (Status != STATUS_SUCCESS)
+ goto MappingListLoadExit;
+
+
+ //
+ // Unpack the string data
+ //
+ MappingListStringsUnpack( MappingUserTableSize, MappingUsers,
+ MappingUserStringSize, MappingUserStrings,
+ MappingTableSize, Mappings,
+ MappingStringSize, MappingStrings );
+
+ //
+ // Unpack the data
+ //
+ MappingListUnpack( MappingUserTableSize, MappingUsers, MappingTableSize, Mappings );
+
+MappingListLoadExit:
+
+ if (hFile != NULL)
+ CloseHandle(hFile);
+
+ //
+ // Run through our tables and clean them up
+ //
+ if (MappingUsers != NULL)
+ MIDL_user_free(MappingUsers);
+
+ if (MappingUserStrings != NULL)
+ MIDL_user_free(MappingUserStrings);
+
+ if (Mappings != NULL)
+ MIDL_user_free(Mappings);
+
+ if (MappingStrings != NULL)
+ MIDL_user_free(MappingStrings);
+
+ //
+ // If there was an error log it.
+ //
+ if (Status != STATUS_SUCCESS)
+ LogEvent(LLS_EVENT_LOAD_MAPPING, 0, NULL, Status);
+
+} // MappingListLoad
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+MappingListSave()
+
+/*++
+
+Routine Description:
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ BOOL ret = TRUE;
+ NTSTATUS Status = STATUS_SUCCESS;
+ HANDLE hFile = NULL;
+
+ ULONG MappingUserTableSize;
+ PPACK_MAPPING_USER_RECORD MappingUsers = NULL;
+
+ ULONG MappingUserStringSize;
+ LPTSTR MappingUserStrings = NULL;
+
+ ULONG MappingTableSize;
+ PPACK_MAPPING_RECORD Mappings = NULL;
+
+ ULONG MappingStringSize;
+ LPTSTR MappingStrings = NULL;
+
+ MAPPING_FILE_HEADER FileHeader;
+ DWORD BytesWritten;
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_DATABASE))
+ dprintf(TEXT("LLS TRACE: MappingListSave\n"));
+#endif
+
+ RtlAcquireResourceExclusive(&MappingListLock, TRUE);
+
+ //
+ // If nothing to save then get-out
+ //
+ if (MappingListSize == 0)
+ goto MappingListSaveExit;
+
+ //
+ // Pack the data
+ //
+ Status = MappingListPack( &MappingUserTableSize, &MappingUsers, &MappingTableSize, &Mappings );
+ if (Status != STATUS_SUCCESS)
+ goto MappingListSaveExit;
+
+ //
+ // Now pack the String data
+ //
+ Status = MappingListStringsPack( MappingUserTableSize, MappingUsers,
+ &MappingUserStringSize, &MappingUserStrings,
+ MappingTableSize, Mappings,
+ &MappingStringSize, &MappingStrings );
+
+ if (Status != STATUS_SUCCESS)
+ goto MappingListSaveExit;
+
+ //
+ // Fill out the file header - sizes are byte sizes
+ //
+ FileHeader.MappingUserTableSize = MappingUserTableSize * sizeof(PACK_MAPPING_USER_RECORD);
+ FileHeader.MappingUserStringSize = MappingUserStringSize * sizeof(TCHAR);
+ FileHeader.MappingTableSize = MappingTableSize * sizeof(PACK_MAPPING_RECORD);
+ FileHeader.MappingStringSize = MappingStringSize * sizeof(TCHAR);
+
+ //
+ // Encrypt the data before saving it out.
+ //
+ Status = EBlock(MappingUsers, FileHeader.MappingUserTableSize);
+
+ if (Status == STATUS_SUCCESS)
+ Status = EBlock(MappingUserStrings, FileHeader.MappingUserStringSize);
+
+ if (Status == STATUS_SUCCESS)
+ Status = EBlock(Mappings, FileHeader.MappingTableSize);
+
+ if (Status == STATUS_SUCCESS)
+ Status = EBlock(MappingStrings, FileHeader.MappingStringSize);
+
+ if (Status != STATUS_SUCCESS)
+ goto MappingListSaveExit;
+
+ //
+ // Save out the header record
+ //
+ hFile = LlsFileInit(MappingFileName, MAPPING_FILE_VERSION, sizeof(MAPPING_FILE_HEADER) );
+ if (hFile == NULL) {
+ Status = GetLastError();
+ goto MappingListSaveExit;
+ }
+
+ //
+ // Now write out all the data blocks
+ //
+ ret = WriteFile(hFile, &FileHeader, sizeof(MAPPING_FILE_HEADER), &BytesWritten, NULL);
+
+ if (ret && (MappingUsers != NULL) && (FileHeader.MappingUserTableSize != 0))
+ ret = WriteFile(hFile, MappingUsers, FileHeader.MappingUserTableSize, &BytesWritten, NULL);
+
+ if (ret && (MappingUserStrings != NULL) && (FileHeader.MappingUserStringSize != 0))
+ ret = WriteFile(hFile, MappingUserStrings, FileHeader.MappingUserStringSize, &BytesWritten, NULL);
+
+ if (ret && (Mappings != NULL) && (FileHeader.MappingTableSize != 0))
+ ret = WriteFile(hFile, Mappings, FileHeader.MappingTableSize, &BytesWritten, NULL);
+
+ if (ret && (MappingStrings != NULL) && (FileHeader.MappingStringSize != 0))
+ ret = WriteFile(hFile, MappingStrings, FileHeader.MappingStringSize, &BytesWritten, NULL);
+
+ if (!ret)
+ Status = GetLastError();
+
+MappingListSaveExit:
+ RtlReleaseResource(&MappingListLock);
+
+ if (hFile != NULL)
+ CloseHandle(hFile);
+
+ //
+ // Run through our tables and clean them up
+ //
+ if (MappingUsers != NULL)
+ MIDL_user_free(MappingUsers);
+
+ if (MappingUserStrings != NULL)
+ MIDL_user_free(MappingUserStrings);
+
+ if (Mappings != NULL)
+ MIDL_user_free(Mappings);
+
+ if (MappingStrings != NULL)
+ MIDL_user_free(MappingStrings);
+
+ //
+ // If there was an error log it.
+ //
+ if (Status != STATUS_SUCCESS)
+ LogEvent(LLS_EVENT_SAVE_MAPPING, 0, NULL, Status);
+
+ return Status;
+} // MappingListSave
+
+
+
+/////////////////////////////////////////////////////////////////////////
+// User List
+//
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+UserListPack (
+ DWORD LastReplicated,
+ ULONG UserLevel,
+ ULONG *pUserTableSize,
+ LPVOID *pUsers
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+Return Value:
+
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ LPVOID Users = NULL;
+ ULONG i, j, k;
+ ULONG TotalRecords = 0;
+ PUSER_RECORD pUser;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: UserListPack\n"));
+#endif
+
+ ASSERT(pUsers != NULL);
+ ASSERT(pUserTableSize != NULL);
+
+ *pUsers = NULL;
+ *pUserTableSize = 0;
+
+ //
+ // Now walk our tree and figure out how many records we must send.
+ //
+ i = 0;
+ TotalRecords = 0;
+ while (i < UserListNumEntries) {
+ pUser = RtlGetElementGenericTable(&UserList, i);
+
+ if (pUser != NULL) {
+ //
+ // Walk each service under each user
+ //
+ RtlEnterCriticalSection(&pUser->ServiceTableLock);
+
+ for (j = 0; j < pUser->ServiceTableSize; j++)
+ if ( (pUser->Services[j].AccessCount > 0) || (pUser->Services[j].LastAccess > LastReplicated) )
+ TotalRecords++;
+
+ RtlLeaveCriticalSection(&pUser->ServiceTableLock);
+ }
+
+ i++;
+ }
+
+#if DBG
+ if (TraceFlags & TRACE_REPLICATION)
+ dprintf(TEXT(" LLS Packing %lu User Records\n"), TotalRecords);
+#endif
+
+ //
+ // Make sure there is anything to replicate
+ //
+ if (TotalRecords > 0) {
+ //
+ // Create our buffer to hold all of the garbage
+ //
+ Users = MIDL_user_allocate(TotalRecords * ( UserLevel ? sizeof(REPL_USER_RECORD_1)
+ : sizeof(REPL_USER_RECORD_0) ) );
+ if (Users == NULL) {
+ ASSERT(FALSE);
+ return STATUS_NO_MEMORY;
+ }
+
+ //
+ // Fill in the buffer - walk the user tree
+ //
+ i = 0;
+ j = 0;
+ while ((i < UserListNumEntries) && (j < TotalRecords)) {
+ pUser = RtlGetElementGenericTable(&UserList, i);
+
+ if (pUser != NULL) {
+ //
+ // Walk each service under each user
+ //
+ k = 0;
+ RtlEnterCriticalSection(&pUser->ServiceTableLock);
+ while (k < pUser->ServiceTableSize) {
+ if ( (pUser->Services[k].AccessCount > 0) || (pUser->Services[k].LastAccess > LastReplicated) ) {
+ if ( 0 == UserLevel )
+ {
+ ((PREPL_USER_RECORD_0)Users)[j].Name = pUser->UserID;
+ ((PREPL_USER_RECORD_0)Users)[j].Service = pUser->Services[k].Service->Index;
+ ((PREPL_USER_RECORD_0)Users)[j].AccessCount = pUser->Services[k].AccessCount;
+ ((PREPL_USER_RECORD_0)Users)[j].LastAccess = pUser->Services[k].LastAccess;
+ }
+ else
+ {
+ ((PREPL_USER_RECORD_1)Users)[j].Name = pUser->UserID;
+ ((PREPL_USER_RECORD_1)Users)[j].Service = pUser->Services[k].Service->Index;
+ ((PREPL_USER_RECORD_1)Users)[j].AccessCount = pUser->Services[k].AccessCount;
+ ((PREPL_USER_RECORD_1)Users)[j].LastAccess = pUser->Services[k].LastAccess;
+ ((PREPL_USER_RECORD_1)Users)[j].Flags = pUser->Flags;
+ }
+
+ //
+ // Reset access count so we don't increment forever
+ //
+ if (LastReplicated != 0)
+ pUser->Services[k].AccessCount = 0;
+
+ j++;
+ }
+
+ k++;
+ }
+ RtlLeaveCriticalSection(&pUser->ServiceTableLock);
+ }
+
+ i++;
+ }
+ } // User Records
+
+#if DBG
+ if (TraceFlags & TRACE_REPLICATION)
+ dprintf(TEXT("UserListPack: [%lu]\n"), TotalRecords);
+#endif
+ *pUsers = Users;
+ *pUserTableSize = TotalRecords;
+ return Status;
+} // UserListPack
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+UserListUnpack (
+ ULONG ServiceTableSize,
+ PREPL_SERVICE_RECORD Services,
+
+ ULONG ServerTableSize,
+ PREPL_SERVER_RECORD Servers,
+
+ ULONG ServerServiceTableSize,
+ PREPL_SERVER_SERVICE_RECORD ServerServices,
+
+ ULONG UserLevel,
+ ULONG UserTableSize,
+ LPVOID Users
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+Return Value:
+
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ ULONG i;
+ PUSER_RECORD pUser;
+ PREPL_USER_RECORD_0 pReplUser0 = NULL;
+ PREPL_USER_RECORD_1 pReplUser1 = NULL;
+ PADD_CACHE pAdd = NULL;
+ PADD_CACHE tAdd = NULL;
+ PADD_CACHE lAdd = NULL;
+ ULONG CacheSize = 0;
+ ULONG DataLength;
+ LPTSTR NewName;
+
+#if DBG
+ if (TraceFlags & (TRACE_REPLICATION | TRACE_FUNCTION_TRACE))
+ dprintf(TEXT("UserListUnpack: [%lu]\n"), UserTableSize);
+#endif
+ //
+ // Walk User table. First fixup service pointers to our local service
+ // table. Next create a big add cache list to dump onto our add-cache
+ // queue.
+ //
+ for (i = 0; i < UserTableSize; i++) {
+ //
+ // Update Index
+ //
+ if ( 0 == UserLevel )
+ {
+ pReplUser0 = &( (PREPL_USER_RECORD_0) Users)[i];
+ pReplUser0->Service = Services[pReplUser0->Service].Index;
+ }
+ else
+ {
+ pReplUser1 = &( (PREPL_USER_RECORD_1) Users)[i];
+ pReplUser1->Service = Services[pReplUser1->Service].Index;
+ }
+
+ //
+ // Now create Add Cache object
+ //
+ tAdd = LocalAlloc(LPTR, sizeof(ADD_CACHE));
+ if (tAdd != NULL) {
+ if ( 0 == UserLevel )
+ {
+ DataLength = (lstrlen(pReplUser0->Name) + 1) * sizeof(TCHAR);
+ }
+ else
+ {
+ DataLength = (lstrlen(pReplUser1->Name) + 1) * sizeof(TCHAR);
+ }
+
+ NewName = LocalAlloc( LPTR, DataLength);
+
+ if (NewName == NULL) {
+ LocalFree(pAdd);
+ ASSERT(FALSE);
+ } else {
+ tAdd->Data = NewName;
+ tAdd->DataType = DATA_TYPE_USERNAME;
+ tAdd->DataLength = DataLength;
+
+ if ( 0 == UserLevel )
+ {
+ lstrcpy( NewName, pReplUser0->Name );
+ tAdd->AccessCount = pReplUser0->AccessCount;
+ tAdd->LastAccess = pReplUser0->LastAccess;
+ tAdd->Flags = LLS_FLAG_SUITE_AUTO;
+
+ RtlAcquireResourceShared(&MasterServiceListLock, TRUE);
+ tAdd->Service = MasterServiceTable[pReplUser0->Service];
+ RtlReleaseResource(&MasterServiceListLock);
+ }
+ else
+ {
+ lstrcpy( NewName, pReplUser1->Name );
+ tAdd->AccessCount = pReplUser1->AccessCount;
+ tAdd->LastAccess = pReplUser1->LastAccess;
+ tAdd->Flags = pReplUser1->Flags & ( LLS_FLAG_SUITE_USE | LLS_FLAG_SUITE_AUTO );
+
+ RtlAcquireResourceShared(&MasterServiceListLock, TRUE);
+ tAdd->Service = MasterServiceTable[pReplUser1->Service];
+ RtlReleaseResource(&MasterServiceListLock);
+ }
+
+ //
+ // Now add it to our cache
+ //
+ tAdd->prev = pAdd;
+ pAdd = tAdd;
+
+ //
+ // Keep track of first on (bottom on stack) so we can append
+ // it onto the real add cache.
+ //
+ if (lAdd == NULL)
+ lAdd = pAdd;
+
+ CacheSize++;
+ }
+ } else
+ ASSERT(FALSE);
+ }
+
+ //
+ // Now that we've walked through all the users - update the actual
+ // Add Cache.
+ //
+ if (pAdd != NULL) {
+ RtlEnterCriticalSection(&AddCacheLock);
+ lAdd->prev = AddCache;
+ AddCache = pAdd;
+ AddCacheSize += CacheSize;
+ RtlLeaveCriticalSection(&AddCacheLock);
+
+ //
+ // Now must signal the event so we can pull off the new record.
+ //
+ Status = NtSetEvent( LLSAddCacheEvent, NULL );
+ ASSERT(NT_SUCCESS(Status));
+
+ }
+
+} // UserListUnpack
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+UserListStringsPack (
+ ULONG UserLevel,
+
+ ULONG UserTableSize,
+ LPVOID Users,
+
+ ULONG *pUserStringSize,
+ LPTSTR *pUserStrings
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+Return Value:
+
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ ULONG i;
+ ULONG StringSize;
+ LPTSTR UserStrings = NULL;
+ TCHAR *pStr;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("UserListStringsPack\n"));
+#endif
+
+ ASSERT(pUserStrings != NULL);
+ ASSERT(pUserStringSize != NULL);
+
+ *pUserStrings = NULL;
+ *pUserStringSize = 0;
+
+ //
+ // First walk the list adding up string sizes - to calculate our buff size
+ //
+ StringSize = 0;
+ for (i = 0; i < UserTableSize; i++) {
+ if ( 0 == UserLevel )
+ {
+ StringSize += 1 + lstrlen( ((PREPL_USER_RECORD_0) Users)[i].Name );
+ }
+ else
+ {
+ StringSize += 1 + lstrlen( ((PREPL_USER_RECORD_1) Users)[i].Name );
+ }
+ }
+
+ //
+ // Make sure there is anything to replicate
+ //
+ if (StringSize > 0) {
+ //
+ // Create our buffer to hold all of the garbage
+ //
+ UserStrings = MIDL_user_allocate(StringSize * sizeof(TCHAR));
+ if (UserStrings == NULL) {
+ ASSERT(FALSE);
+ return STATUS_NO_MEMORY;
+ }
+
+ //
+ // Fill in the buffer
+ //
+ pStr = UserStrings;
+ for (i = 0; i < UserTableSize; i++) {
+ if ( 0 == UserLevel )
+ {
+ lstrcpy( pStr, ((PREPL_USER_RECORD_0) Users)[i].Name );
+ }
+ else
+ {
+ lstrcpy( pStr, ((PREPL_USER_RECORD_1) Users)[i].Name );
+ }
+
+ pStr += 1 + lstrlen( pStr );
+ }
+ }
+
+ *pUserStrings = UserStrings;
+ *pUserStringSize = StringSize;
+
+ return Status;
+} // UserListStringsPack
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+UserListStringsUnpack (
+ ULONG UserLevel,
+
+ ULONG UserTableSize,
+ LPVOID Users,
+
+ ULONG UserStringSize,
+ LPTSTR UserStrings
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+Return Value:
+
+
+--*/
+
+{
+ ULONG i;
+ TCHAR *pStr;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("UserListStringsUnpack\n"));
+#endif
+
+ pStr = UserStrings;
+ for (i = 0; i < UserTableSize; i++) {
+ if ( 0 == UserLevel )
+ {
+ ((PREPL_USER_RECORD_0) Users)[i].Name = pStr;
+ }
+ else
+ {
+ ((PREPL_USER_RECORD_1) Users)[i].Name = pStr;
+ }
+
+ //
+ // Move to end of current string
+ //
+ while (*pStr != TEXT('\0'))
+ pStr++;
+
+ // now go past ending NULL
+ pStr++;
+ }
+
+} // UserListStringsUnpack
+
+
+
+/////////////////////////////////////////////////////////////////////////
+// Service List
+//
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+ServiceListPack (
+ ULONG *pServiceTableSize,
+ PREPL_SERVICE_RECORD *pServices
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+Return Value:
+
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ PREPL_SERVICE_RECORD Services = NULL;
+ ULONG i;
+ ULONG TotalRecords = 0;
+ PMASTER_SERVICE_RECORD pService;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: ServiceListPack\n"));
+#endif
+
+ ASSERT(pServices != NULL);
+ ASSERT(pServiceTableSize != NULL);
+ *pServices = NULL;
+ *pServiceTableSize = 0;
+
+ TotalRecords = MasterServiceListSize;
+
+ //
+ // Make sure there is anything to replicate
+ //
+ if (TotalRecords > 0) {
+ //
+ // Create our buffer to hold all of the garbage
+ //
+ Services = MIDL_user_allocate(TotalRecords * sizeof(REPL_SERVICE_RECORD));
+ if (Services == NULL) {
+ ASSERT(FALSE);
+ return STATUS_NO_MEMORY;
+ }
+
+ //
+ // Fill in the buffer - walk the user tree
+ //
+ for (i = 0; i < MasterServiceListSize; i++) {
+ pService = MasterServiceTable[i];
+
+ Services[i].Name = pService->Name;
+ Services[i].FamilyName = pService->Family->Name;
+ Services[i].Version = pService->Version;
+ Services[i].Index = pService->Index;
+ }
+ }
+
+#if DBG
+ if (TraceFlags & TRACE_REPLICATION)
+ dprintf(TEXT("ServiceListPack: [%lu]\n"), TotalRecords);
+#endif
+ *pServices = Services;
+ *pServiceTableSize = TotalRecords;
+ return Status;
+} // ServiceListPack
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+ServiceListUnpack (
+ ULONG ServiceTableSize,
+ PREPL_SERVICE_RECORD Services,
+
+ ULONG ServerTableSize,
+ PREPL_SERVER_RECORD Servers,
+
+ ULONG ServerServiceTableSize,
+ PREPL_SERVER_SERVICE_RECORD ServerServices
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+Return Value:
+
+
+--*/
+
+{
+ ULONG i, j;
+ PMASTER_SERVICE_RECORD pService;
+ PREPL_SERVICE_RECORD pSvc;
+
+#if DBG
+ if (TraceFlags & (TRACE_REPLICATION | TRACE_FUNCTION_TRACE))
+ dprintf(TEXT("ServiceListUnpack: [%lu]\n"), ServiceTableSize);
+#endif
+ //
+ // Walk services table, adding any new services to our local table.
+ // Fix up the index pointers to match our local services.
+ //
+ RtlAcquireResourceExclusive(&MasterServiceListLock, TRUE);
+
+ for (i = 0; i < ServiceTableSize; i++) {
+ pSvc = &Services[i];
+ pService = MasterServiceListAdd(pSvc->FamilyName, pSvc->Name, pSvc->Version );
+
+ if (pService != NULL) {
+ pSvc->Index = pService->Index;
+
+ //
+ // In case this got added from the local service list table and we
+ // didn't have a version # yet.
+ //
+ if ( (pService->Version == 0) && (pSvc->Version != 0) ) {
+ PMASTER_SERVICE_ROOT ServiceRoot = NULL;
+
+ //
+ // Fixup next pointer chain
+ //
+ ServiceRoot = pService->Family;
+ j = 0;
+ while ((j < ServiceRoot->ServiceTableSize) && (MasterServiceTable[ServiceRoot->Services[j]]->Version < pSvc->Version))
+ j++;
+
+ pService->next = 0;
+ pService->Version = pSvc->Version;
+ if (j > 0) {
+ if (MasterServiceTable[ServiceRoot->Services[j - 1]]->next == pService->Index + 1)
+ pService->next = 0;
+ else
+ pService->next = MasterServiceTable[ServiceRoot->Services[j - 1]]->next;
+
+ if (MasterServiceTable[ServiceRoot->Services[j - 1]] != pService)
+ MasterServiceTable[ServiceRoot->Services[j - 1]]->next = pService->Index + 1;
+
+ }
+
+ // Resort it in order of the versions
+ qsort((void *) ServiceRoot->Services, (size_t) ServiceRoot->ServiceTableSize, sizeof(PMASTER_SERVICE_RECORD), MServiceRecordCompare);
+ }
+
+ } else {
+ ASSERT(FALSE);
+ pSvc->Index = 0;
+ }
+
+ }
+
+ RtlReleaseResource(&MasterServiceListLock);
+
+} // ServiceListUnpack
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+ServiceListStringsPack (
+ ULONG ServiceTableSize,
+ PREPL_SERVICE_RECORD Services,
+
+ ULONG *pServiceStringSize,
+ LPTSTR *pServiceStrings
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+Return Value:
+
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ ULONG i;
+ ULONG StringSize;
+ PREPL_SERVICE_RECORD pService;
+ LPTSTR ServiceStrings = NULL;
+ TCHAR *pStr;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("ServiceListStringsPack\n"));
+#endif
+
+ ASSERT(pServiceStrings != NULL);
+ ASSERT(pServiceStringSize != NULL);
+
+ *pServiceStrings = NULL;
+ *pServiceStringSize = 0;
+
+ //
+ // First walk the list adding up string sizes - to calculate our buff size
+ //
+ StringSize = 0;
+ for (i = 0; i < ServiceTableSize; i++) {
+ pService = &Services[i];
+
+ StringSize = StringSize + lstrlen(pService->Name) + 1;
+ StringSize = StringSize + lstrlen(pService->FamilyName) + 1;
+ }
+
+ //
+ // Make sure there is anything to replicate
+ //
+ if (StringSize > 0) {
+ //
+ // Create our buffer to hold all of the garbage
+ //
+ ServiceStrings = MIDL_user_allocate(StringSize * sizeof(TCHAR));
+ if (ServiceStrings == NULL) {
+ ASSERT(FALSE);
+ return STATUS_NO_MEMORY;
+ }
+
+ //
+ // Fill in the buffer
+ //
+ pStr = ServiceStrings;
+ for (i = 0; i < ServiceTableSize; i++) {
+ pService = &Services[i];
+
+ lstrcpy(pStr, pService->Name);
+ pStr = &pStr[lstrlen(pService->Name) + 1];
+
+ lstrcpy(pStr, pService->FamilyName);
+ pStr = &pStr[lstrlen(pService->FamilyName) + 1];
+ }
+ }
+
+ *pServiceStrings = ServiceStrings;
+ *pServiceStringSize = StringSize;
+
+ return Status;
+} // ServiceListStringsPack
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+ServiceListStringsUnpack (
+ ULONG ServiceTableSize,
+ PREPL_SERVICE_RECORD Services,
+
+ ULONG ServiceStringSize,
+ LPTSTR ServiceStrings
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+Return Value:
+
+
+--*/
+
+{
+ ULONG i;
+ PREPL_SERVICE_RECORD pService;
+ TCHAR *pStr;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("ServiceListStringsUnpack\n"));
+#endif
+
+ pStr = ServiceStrings;
+ for (i = 0; i < ServiceTableSize; i++) {
+ pService = &Services[i];
+
+ pService->Name = pStr;
+
+ //
+ // Move to end of current string
+ //
+ while (*pStr != TEXT('\0'))
+ pStr++;
+
+ // now go past ending NULL
+ pStr++;
+
+ pService->FamilyName = pStr;
+
+ //
+ // Move to end of current string
+ //
+ while (*pStr != TEXT('\0'))
+ pStr++;
+
+ // now go past ending NULL
+ pStr++;
+ }
+
+} // ServiceListStringsUnpack
+
+
+
+/////////////////////////////////////////////////////////////////////////
+// Server List
+//
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+ServerListPack (
+ ULONG *pServerTableSize,
+ PREPL_SERVER_RECORD *pServers
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+Return Value:
+
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ PREPL_SERVER_RECORD Servers = NULL;
+ ULONG i;
+ ULONG TotalRecords = 0;
+ PSERVER_RECORD pServer;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: ServerListPack\n"));
+#endif
+
+ ASSERT(pServers != NULL);
+ ASSERT(pServerTableSize != NULL);
+
+ *pServers = NULL;
+ *pServerTableSize = 0;
+
+ TotalRecords = ServerListSize;
+
+ //
+ // Make sure there is anything to replicate
+ //
+ if (TotalRecords > 0) {
+ //
+ // Create our buffer to hold all of the garbage
+ //
+ Servers = MIDL_user_allocate(TotalRecords * sizeof(REPL_SERVER_RECORD));
+ if (Servers == NULL) {
+ ASSERT(FALSE);
+ return STATUS_NO_MEMORY;
+ }
+
+ //
+ // Fill in the buffer - walk the user tree
+ //
+ for (i = 0; i < ServerListSize; i++) {
+ pServer = ServerTable[i];
+
+ Servers[i].Name = pServer->Name;
+ Servers[i].MasterServer = pServer->MasterServer;
+ Servers[i].Index = pServer->Index;
+ }
+ }
+
+#if DBG
+ if (TraceFlags & TRACE_REPLICATION)
+ dprintf(TEXT("ServerListPack: [%lu]\n"), TotalRecords);
+#endif
+ *pServers = Servers;;
+ *pServerTableSize = TotalRecords;
+ return Status;
+} // ServerListPack
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+ServerListUnpack (
+ ULONG ServiceTableSize,
+ PREPL_SERVICE_RECORD Services,
+
+ ULONG ServerTableSize,
+ PREPL_SERVER_RECORD Servers,
+
+ ULONG ServerServiceTableSize,
+ PREPL_SERVER_SERVICE_RECORD ServerServices
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+Return Value:
+
+
+--*/
+
+{
+ ULONG i;
+ PSERVER_RECORD pServer;
+ PREPL_SERVER_RECORD pSrv;
+ TCHAR ComputerName[MAX_COMPUTERNAME_LENGTH + 1];
+
+#if DBG
+ if (TraceFlags & (TRACE_REPLICATION | TRACE_FUNCTION_TRACE))
+ dprintf(TEXT("ServerListUnpack: [%lu]\n"), ServerTableSize);
+#endif
+
+ //
+ // Walk server table, adding any new servers to our local table.
+ // Fix up the index pointers to match our local table and re-fix
+ // Service table pointers.
+ //
+ RtlEnterCriticalSection(&ConfigInfoLock);
+ lstrcpy(ComputerName, ConfigInfo.ComputerName);
+ RtlLeaveCriticalSection(&ConfigInfoLock);
+
+ RtlAcquireResourceExclusive(&ServerListLock, TRUE);
+
+ for (i = 0; i < ServerTableSize; i++) {
+ pSrv = &Servers[i];
+
+ if (pSrv->MasterServer != 0)
+ pServer = ServerListAdd(pSrv->Name, Servers[pSrv->MasterServer - 1].Name);
+ else
+ pServer = ServerListAdd(pSrv->Name, ComputerName);
+
+ if (pServer != NULL)
+ pSrv->Index = pServer->Index;
+ else {
+ ASSERT(FALSE);
+ pSrv->Index = 0;
+ }
+ }
+
+ RtlReleaseResource(&ServerListLock);
+
+} // ServerListUnpack
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+ServerServiceListPack (
+ ULONG *pServerServiceTableSize,
+ PREPL_SERVER_SERVICE_RECORD *pServerServices
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+Return Value:
+
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ PREPL_SERVER_SERVICE_RECORD ServerServices = NULL;
+ ULONG i, j, k;
+ ULONG TotalRecords = 0;
+ PSERVER_RECORD pServer;
+ PSERVER_SERVICE_RECORD pServerService;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: ServerServiceListPack\n"));
+#endif
+
+ ASSERT(pServerServices != NULL);
+ ASSERT(pServerServiceTableSize != NULL);
+
+ *pServerServices = NULL;
+ *pServerServiceTableSize = 0;
+
+ //
+ // Walk the ServerList and find all ServiceRecords
+ for (i = 0; i < ServerListSize; i++)
+ TotalRecords += ServerTable[i]->ServiceTableSize;
+
+ //
+ // Make sure there is anything to replicate
+ //
+ if (TotalRecords > 0) {
+ //
+ // Create our buffer to hold all of the garbage
+ //
+ ServerServices = MIDL_user_allocate(TotalRecords * sizeof(REPL_SERVER_SERVICE_RECORD));
+ if (ServerServices == NULL) {
+ ASSERT(FALSE);
+ return STATUS_NO_MEMORY;
+ }
+
+ //
+ // Fill in the buffer - walk the user tree
+ //
+ k = 0;
+ for (i = 0; i < ServerListSize; i++) {
+ pServer = ServerTable[i];
+
+ for (j = 0; j < pServer->ServiceTableSize; j++) {
+ ServerServices[k].Server = pServer->Index;
+ ServerServices[k].Service = pServer->Services[j]->Service;
+ ServerServices[k].MaxSessionCount = pServer->Services[j]->MaxSessionCount;
+ ServerServices[k].MaxSetSessionCount = pServer->Services[j]->MaxSetSessionCount;
+ ServerServices[k].HighMark = pServer->Services[j]->HighMark;
+ k++;
+ }
+ }
+ }
+
+#if DBG
+ if (TraceFlags & TRACE_REPLICATION)
+ dprintf(TEXT("ServerServiceListPack: [%lu]\n"), TotalRecords);
+#endif
+ *pServerServices = ServerServices;
+ *pServerServiceTableSize = TotalRecords;
+ return Status;
+} // ServerServiceListPack
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+ServerServiceListUnpack (
+ ULONG ServiceTableSize,
+ PREPL_SERVICE_RECORD Services,
+
+ ULONG ServerTableSize,
+ PREPL_SERVER_RECORD Servers,
+
+ ULONG ServerServiceTableSize,
+ PREPL_SERVER_SERVICE_RECORD ServerServices
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+Return Value:
+
+
+--*/
+
+{
+ ULONG i;
+ PSERVER_RECORD pServer;
+ PREPL_SERVER_SERVICE_RECORD pSrv;
+ PSERVER_SERVICE_RECORD pService;
+ PMASTER_SERVICE_RECORD pMasterService;
+
+#if DBG
+ if (TraceFlags & (TRACE_REPLICATION | TRACE_FUNCTION_TRACE))
+ dprintf(TEXT("ServerServiceListUnpack: [%lu]\n"), ServerServiceTableSize);
+#endif
+ //
+ // Walk server table, adding any new servers to our local table.
+ // Fix up the index pointers to match our local table and re-fix
+ // Service table pointers.
+ //
+
+ RtlAcquireResourceExclusive(&ServerListLock, TRUE);
+ RtlAcquireResourceShared(&MasterServiceListLock, TRUE);
+
+ for (i = 0; i < ServerServiceTableSize; i++) {
+ pSrv = &ServerServices[i];
+ pServer = ServerListFind(Servers[pSrv->Server - 1].Name);
+ ASSERT(pServer != NULL);
+
+ if (pServer != NULL) {
+ BOOL bReplaceValues;
+
+ pService = ServerServiceListFind(Services[pSrv->Service].Name, pServer->ServiceTableSize, pServer->Services);
+ bReplaceValues = ( NULL != pService );
+
+ pService = ServerServiceListAdd(Services[pSrv->Service].Name,
+ Services[pSrv->Service].Index,
+ &pServer->ServiceTableSize,
+ &pServer->Services);
+
+ ASSERT(pService != NULL);
+
+ //
+ // Remove any old info
+ //
+ pMasterService = MasterServiceTable[Services[pSrv->Service].Index];
+ if ( bReplaceValues )
+ {
+ pMasterService->MaxSessionCount -= pService->MaxSessionCount;
+ pMasterService->HighMark -= pService->HighMark;
+ }
+
+ //
+ // Now update new info
+ //
+ pService->MaxSessionCount = pSrv->MaxSessionCount;
+ pService->HighMark = pSrv->HighMark;
+ pMasterService->MaxSessionCount += pService->MaxSessionCount;
+ pMasterService->HighMark += pService->HighMark;
+ }
+
+ }
+
+ RtlReleaseResource(&MasterServiceListLock);
+ RtlReleaseResource(&ServerListLock);
+
+} // ServerServiceListUnpack
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+ServerListStringsPack (
+ ULONG ServerTableSize,
+ PREPL_SERVER_RECORD Servers,
+
+ ULONG *pServerStringSize,
+ LPTSTR *pServerStrings
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+Return Value:
+
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ ULONG i;
+ ULONG StringSize;
+ PREPL_SERVER_RECORD pServer;
+ LPTSTR ServerStrings = NULL;
+ TCHAR *pStr;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("ServerListStringsPack\n"));
+#endif
+
+ ASSERT(pServerStrings != NULL);
+ ASSERT(pServerStringSize != NULL);
+
+ *pServerStrings = NULL;
+ *pServerStringSize = 0;
+
+ //
+ // First walk the list adding up string sizes - to calculate our buff size
+ //
+ StringSize = 0;
+ for (i = 0; i < ServerTableSize; i++) {
+ pServer = &Servers[i];
+
+ StringSize = StringSize + lstrlen(pServer->Name) + 1;
+ }
+
+ //
+ // Make sure there is anything to replicate
+ //
+ if (StringSize > 0) {
+ //
+ // Create our buffer to hold all of the garbage
+ //
+ ServerStrings = MIDL_user_allocate(StringSize * sizeof(TCHAR));
+ if (ServerStrings == NULL) {
+ ASSERT(FALSE);
+ return STATUS_NO_MEMORY;
+ }
+
+ //
+ // Fill in the buffer
+ //
+ pStr = ServerStrings;
+ for (i = 0; i < ServerTableSize; i++) {
+ pServer = &Servers[i];
+
+ lstrcpy(pStr, pServer->Name);
+ pStr = &pStr[lstrlen(pServer->Name) + 1];
+ }
+ }
+
+ *pServerStrings = ServerStrings;
+ *pServerStringSize = StringSize;
+
+ return Status;
+} // ServerListStringsPack
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+ServerListStringsUnpack (
+ ULONG ServerTableSize,
+ PREPL_SERVER_RECORD Servers,
+
+ ULONG ServerStringSize,
+ LPTSTR ServerStrings
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+Return Value:
+
+
+--*/
+
+{
+ ULONG i;
+ PREPL_SERVER_RECORD pServer;
+ TCHAR *pStr;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("ServerListStringsUnpack\n"));
+#endif
+
+ //
+ // First do license service strings
+ //
+ pStr = ServerStrings;
+ for (i = 0; i < ServerTableSize; i++) {
+ pServer = &Servers[i];
+
+ pServer->Name = pStr;
+
+ //
+ // Move to end of current string
+ //
+ while (*pStr != TEXT('\0'))
+ pStr++;
+
+ // now go past ending NULL
+ pStr++;
+ }
+
+} // ServerListStringsUnpack
+
+
+
+/////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+PackAll (
+ DWORD LastReplicated,
+
+ ULONG *pServiceTableSize,
+ PREPL_SERVICE_RECORD *pServices,
+
+ ULONG *pServerTableSize,
+ PREPL_SERVER_RECORD *pServers,
+
+ ULONG *pServerServiceTableSize,
+ PREPL_SERVER_SERVICE_RECORD *pServerServices,
+
+ ULONG UserLevel,
+ ULONG *pUserTableSize,
+ LPVOID *pUsers
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+Return Value:
+
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: PackAll\n"));
+#endif
+
+ //
+ // We need to grab all the locks here so that a service isn't snuck in
+ // behind our backs - since these tables interact with each other.
+ //
+ RtlAcquireResourceExclusive(&UserListAddEnumLock, TRUE);
+ RtlAcquireResourceShared(&UserListLock, TRUE);
+ RtlAcquireResourceShared(&MasterServiceListLock, TRUE);
+ RtlAcquireResourceShared(&ServerListLock, TRUE);
+
+ Status = ServiceListPack(pServiceTableSize, pServices);
+ if (Status != STATUS_SUCCESS)
+ goto PackAllExit;
+
+ Status = ServerListPack(pServerTableSize, pServers);
+ if (Status != STATUS_SUCCESS)
+ goto PackAllExit;
+
+ Status = ServerServiceListPack(pServerServiceTableSize, pServerServices);
+ if (Status != STATUS_SUCCESS)
+ goto PackAllExit;
+
+ Status = UserListPack(LastReplicated, UserLevel, pUserTableSize, pUsers);
+ if (Status != STATUS_SUCCESS)
+ goto PackAllExit;
+
+ //
+ // Now update our last used time
+ //
+ LastUsedTime = DateSystemGet() + 1;
+
+PackAllExit:
+ RtlReleaseResource(&ServerListLock);
+ RtlReleaseResource(&MasterServiceListLock);
+ RtlReleaseResource(&UserListAddEnumLock);
+ RtlReleaseResource(&UserListLock);
+
+ return Status;
+} // PackAll
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+UnpackAll (
+ ULONG ServiceTableSize,
+ PREPL_SERVICE_RECORD Services,
+
+ ULONG ServerTableSize,
+ PREPL_SERVER_RECORD Servers,
+
+ ULONG ServerServiceTableSize,
+ PREPL_SERVER_SERVICE_RECORD ServerServices,
+
+ ULONG UserLevel,
+ ULONG UserTableSize,
+ LPVOID Users
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+Return Value:
+
+
+--*/
+
+{
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: UnpackAll\n"));
+#endif
+
+ ServiceListUnpack(ServiceTableSize, Services, ServerTableSize, Servers, ServerServiceTableSize, ServerServices);
+ ServerListUnpack(ServiceTableSize, Services, ServerTableSize, Servers, ServerServiceTableSize, ServerServices);
+ ServerServiceListUnpack(ServiceTableSize, Services, ServerTableSize, Servers, ServerServiceTableSize, ServerServices);
+ UserListUnpack(ServiceTableSize, Services, ServerTableSize, Servers, ServerServiceTableSize, ServerServices, UserLevel, UserTableSize, Users);
+} // UnpackAll
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+LLSDataLoad()
+
+/*++
+
+Routine Description:
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ BOOL ret;
+ DWORD Version, DataSize;
+ NTSTATUS Status = STATUS_SUCCESS;
+ HANDLE hFile = NULL;
+
+ ULONG ServiceTableSize = 0;
+ PREPL_SERVICE_RECORD Services = NULL;
+
+ ULONG ServiceStringSize;
+ LPTSTR ServiceStrings = NULL;
+
+ ULONG ServerServiceTableSize = 0;
+ PREPL_SERVER_SERVICE_RECORD ServerServices = NULL;
+
+ ULONG ServerTableSize = 0;
+ PREPL_SERVER_RECORD Servers = NULL;
+
+ ULONG ServerStringSize;
+ LPTSTR ServerStrings = NULL;
+
+ ULONG UserTableSize = 0;
+ LPVOID Users = NULL;
+
+ ULONG UserStringSize;
+ LPTSTR UserStrings = NULL;
+
+ LLS_DATA_FILE_HEADER FileHeader;
+ DWORD BytesRead;
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_DATABASE))
+ dprintf(TEXT("LLS TRACE: LLSDataLoad\n"));
+#endif
+
+ //
+ // If nothing to load then get-out
+ //
+ if (!FileExists(UserFileName))
+ goto LLSDataLoadExit;
+
+ //
+ // Check the init header
+ //
+ Version = DataSize = 0;
+ hFile = LlsFileCheck(UserFileName, &Version, &DataSize );
+ if (hFile == NULL) {
+ Status = GetLastError();
+ goto LLSDataLoadExit;
+ }
+
+ if ( ( ( Version != USER_FILE_VERSION_0 )
+ || ( DataSize != sizeof(LLS_DATA_FILE_HEADER_0) ) )
+ && ( ( Version != USER_FILE_VERSION )
+ || ( DataSize != sizeof(LLS_DATA_FILE_HEADER) ) ) )
+ {
+ Status = STATUS_FILE_INVALID;
+ goto LLSDataLoadExit;
+ }
+
+ //
+ // The init header checks out, so load the license header and data blocks
+ //
+ if ( USER_FILE_VERSION_0 == Version )
+ {
+ // 3.51 data file
+ LLS_DATA_FILE_HEADER_0 FileHeader0;
+
+ ret = ReadFile(hFile, &FileHeader0, sizeof(LLS_DATA_FILE_HEADER_0), &BytesRead, NULL);
+
+ if ( ret )
+ {
+ FileHeader.ServiceLevel = 0;
+ FileHeader.ServiceTableSize = FileHeader0.ServiceTableSize;
+ FileHeader.ServiceStringSize = FileHeader0.ServiceStringSize;
+ FileHeader.ServerLevel = 0;
+ FileHeader.ServerTableSize = FileHeader0.ServerTableSize;
+ FileHeader.ServerStringSize = FileHeader0.ServerStringSize;
+ FileHeader.ServerServiceLevel = 0;
+ FileHeader.ServerServiceTableSize = FileHeader0.ServerServiceTableSize;
+ FileHeader.UserLevel = 0;
+ FileHeader.UserTableSize = FileHeader0.UserTableSize;
+ FileHeader.UserStringSize = FileHeader0.UserStringSize;
+ }
+ }
+ else
+ {
+ ret = ReadFile(hFile, &FileHeader, sizeof(LLS_DATA_FILE_HEADER), &BytesRead, NULL);
+ }
+
+ if ( ret )
+ {
+ // header read okay; ensure data type levels are okay
+ if ( ( 0 != FileHeader.ServiceLevel )
+ || ( 0 != FileHeader.ServerLevel )
+ || ( 0 != FileHeader.ServerServiceLevel )
+ || ( ( 0 != FileHeader.UserLevel )
+ && ( 1 != FileHeader.UserLevel ) ) )
+ {
+ Status = STATUS_FILE_INVALID;
+ goto LLSDataLoadExit;
+ }
+ }
+
+ ServiceTableSize = 0;
+ ServiceStringSize = 0;
+ ServerServiceTableSize = 0;
+ ServerTableSize = 0;
+ ServerStringSize = 0;
+ UserTableSize = 0;
+ UserStringSize = 0;
+
+ if (ret) {
+ //
+ // Run through and allocate space to read data blocks into
+ //
+ if (FileHeader.ServiceTableSize != 0) {
+ ServiceTableSize = FileHeader.ServiceTableSize / sizeof(REPL_SERVICE_RECORD);
+ Services = MIDL_user_allocate(FileHeader.ServiceTableSize);
+
+ if ( Services == NULL ) {
+ Status = STATUS_NO_MEMORY;
+ goto LLSDataLoadExit;
+ }
+ }
+
+ if (FileHeader.ServiceStringSize != 0) {
+ ServiceStringSize = FileHeader.ServiceStringSize / sizeof(TCHAR);
+ ServiceStrings = MIDL_user_allocate(FileHeader.ServiceStringSize);
+
+ if ( ServiceStrings == NULL ) {
+ Status = STATUS_NO_MEMORY;
+ goto LLSDataLoadExit;
+ }
+ }
+
+ if (FileHeader.ServerTableSize != 0) {
+ ServerTableSize = FileHeader.ServerTableSize / sizeof(REPL_SERVER_RECORD);
+ Servers = MIDL_user_allocate(FileHeader.ServerTableSize);
+
+ if ( Servers == NULL ) {
+ Status = STATUS_NO_MEMORY;
+ goto LLSDataLoadExit;
+ }
+ }
+
+ if (FileHeader.ServerStringSize != 0) {
+ ServerStringSize = FileHeader.ServerStringSize / sizeof(TCHAR);
+ ServerStrings = MIDL_user_allocate(FileHeader.ServerStringSize);
+
+ if ( ServerStrings == NULL ) {
+ Status = STATUS_NO_MEMORY;
+ goto LLSDataLoadExit;
+ }
+ }
+
+ if (FileHeader.ServerServiceTableSize != 0) {
+ ServerServiceTableSize = FileHeader.ServerServiceTableSize / sizeof(REPL_SERVER_SERVICE_RECORD);
+ ServerServices = MIDL_user_allocate(FileHeader.ServerServiceTableSize);
+
+ if ( ServerServices == NULL ) {
+ Status = STATUS_NO_MEMORY;
+ goto LLSDataLoadExit;
+ }
+ }
+
+ if (FileHeader.UserTableSize != 0) {
+ UserTableSize = FileHeader.UserTableSize / ( FileHeader.UserLevel ? sizeof(REPL_USER_RECORD_1)
+ : sizeof(REPL_USER_RECORD_0) );
+ Users = MIDL_user_allocate(FileHeader.UserTableSize);
+
+ if ( Users == NULL ) {
+ Status = STATUS_NO_MEMORY;
+ goto LLSDataLoadExit;
+ }
+ }
+
+ if (FileHeader.UserStringSize != 0) {
+ UserStringSize = FileHeader.UserStringSize / sizeof(TCHAR);
+ UserStrings = MIDL_user_allocate(FileHeader.UserStringSize);
+
+ if ( UserStrings == NULL ) {
+ Status = STATUS_NO_MEMORY;
+ goto LLSDataLoadExit;
+ }
+ }
+
+ }
+
+ if (ret && (FileHeader.ServiceTableSize != 0) )
+ ret = ReadFile(hFile, Services, FileHeader.ServiceTableSize, &BytesRead, NULL);
+
+ if (ret && (FileHeader.ServiceStringSize != 0) )
+ ret = ReadFile(hFile, ServiceStrings, FileHeader.ServiceStringSize, &BytesRead, NULL);
+
+ if (ret && (FileHeader.ServerTableSize != 0) )
+ ret = ReadFile(hFile, Servers, FileHeader.ServerTableSize, &BytesRead, NULL);
+
+ if (ret && (FileHeader.ServerStringSize != 0) )
+ ret = ReadFile(hFile, ServerStrings, FileHeader.ServerStringSize, &BytesRead, NULL);
+
+ if (ret && (FileHeader.ServerServiceTableSize != 0) )
+ ret = ReadFile(hFile, ServerServices, FileHeader.ServerServiceTableSize, &BytesRead, NULL);
+
+ if (ret && (FileHeader.UserTableSize != 0) )
+ ret = ReadFile(hFile, Users, FileHeader.UserTableSize, &BytesRead, NULL);
+
+ if (ret && (FileHeader.UserStringSize != 0) )
+ ret = ReadFile(hFile, UserStrings, FileHeader.UserStringSize, &BytesRead, NULL);
+
+ if (!ret) {
+ Status = GetLastError();
+ goto LLSDataLoadExit;
+ }
+
+ //
+ // Decrypt the data
+ //
+ Status = DeBlock(Services, FileHeader.ServiceTableSize);
+
+ if (Status == STATUS_SUCCESS)
+ Status = DeBlock(ServiceStrings, FileHeader.ServiceStringSize);
+
+ if (Status == STATUS_SUCCESS)
+ Status = DeBlock(Servers, FileHeader.ServerTableSize);
+
+ if (Status == STATUS_SUCCESS)
+ Status = DeBlock(ServerStrings, FileHeader.ServerStringSize);
+
+ if (Status == STATUS_SUCCESS)
+ Status = DeBlock(ServerServices, FileHeader.ServerServiceTableSize);
+
+ if (Status == STATUS_SUCCESS)
+ Status = DeBlock(Users, FileHeader.UserTableSize);
+
+ if (Status == STATUS_SUCCESS)
+ Status = DeBlock(UserStrings, FileHeader.UserStringSize);
+
+ if (Status != STATUS_SUCCESS)
+ goto LLSDataLoadExit;
+
+
+ //
+ // Unpack the string data
+ //
+ ServiceListStringsUnpack( ServiceTableSize, Services, ServiceStringSize, ServiceStrings );
+ ServerListStringsUnpack( ServerTableSize, Servers, ServerStringSize, ServerStrings );
+ UserListStringsUnpack( FileHeader.UserLevel, UserTableSize, Users, UserStringSize, UserStrings );
+
+ //
+ // Unpack the data
+ //
+ UnpackAll ( ServiceTableSize, Services, ServerTableSize, Servers,
+ ServerServiceTableSize, ServerServices,
+ FileHeader.UserLevel, UserTableSize, Users );
+
+LLSDataLoadExit:
+
+ if (hFile != NULL)
+ CloseHandle(hFile);
+
+ //
+ // Run through our tables and clean them up
+ //
+ if (Services != NULL)
+ MIDL_user_free(Services);
+
+ if (ServiceStrings != NULL)
+ MIDL_user_free(ServiceStrings);
+
+ if (Servers != NULL)
+ MIDL_user_free(Servers);
+
+ if (ServerStrings != NULL)
+ MIDL_user_free(ServerStrings);
+
+ if (ServerServices != NULL)
+ MIDL_user_free(ServerServices);
+
+ if (Users != NULL)
+ MIDL_user_free(Users);
+
+ if (UserStrings != NULL)
+ MIDL_user_free(UserStrings);
+
+ //
+ // If there was an error log it.
+ //
+ if (Status != STATUS_SUCCESS)
+ LogEvent(LLS_EVENT_LOAD_USER, 0, NULL, Status);
+
+} // LLSDataLoad
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+LLSDataSave()
+
+/*++
+
+Routine Description:
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ BOOL ret = TRUE;
+ NTSTATUS Status = STATUS_SUCCESS;
+ HANDLE hFile = NULL;
+
+ ULONG ServiceTableSize = 0;
+ PREPL_SERVICE_RECORD Services = NULL;
+
+ ULONG ServiceStringSize;
+ LPTSTR ServiceStrings = NULL;
+
+ ULONG ServerServiceTableSize = 0;
+ PREPL_SERVER_SERVICE_RECORD ServerServices = NULL;
+
+ ULONG ServerTableSize = 0;
+ PREPL_SERVER_RECORD Servers = NULL;
+
+ ULONG ServerStringSize;
+ LPTSTR ServerStrings = NULL;
+
+ ULONG UserTableSize = 0;
+ PREPL_USER_RECORD_1 Users = NULL;
+
+ ULONG UserStringSize;
+ LPTSTR UserStrings = NULL;
+
+ LLS_DATA_FILE_HEADER FileHeader;
+ DWORD BytesWritten;
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_DATABASE))
+ dprintf(TEXT("LLS TRACE: LLSDataSave\n"));
+#endif
+
+ //
+ // Pack the data
+ //
+ Status = PackAll ( 0,
+ &ServiceTableSize, &Services,
+ &ServerTableSize, &Servers,
+ &ServerServiceTableSize, &ServerServices,
+ 1, &UserTableSize, &Users );
+ if (Status != STATUS_SUCCESS)
+ goto LLSDataSaveExit;
+
+ //
+ // Now pack the String data
+ //
+ Status = ServiceListStringsPack( ServiceTableSize, Services, &ServiceStringSize, &ServiceStrings );
+ if (Status != STATUS_SUCCESS)
+ goto LLSDataSaveExit;
+
+ Status = ServerListStringsPack( ServerTableSize, Servers, &ServerStringSize, &ServerStrings );
+ if (Status != STATUS_SUCCESS)
+ goto LLSDataSaveExit;
+
+ Status = UserListStringsPack( 1, UserTableSize, Users, &UserStringSize, &UserStrings );
+ if (Status != STATUS_SUCCESS)
+ goto LLSDataSaveExit;
+
+ //
+ // Fill out the file header - sizes are byte sizes
+ //
+ FileHeader.ServiceTableSize = ServiceTableSize * sizeof(REPL_SERVICE_RECORD);
+ FileHeader.ServiceStringSize = ServiceStringSize * sizeof(TCHAR);
+ FileHeader.ServerTableSize = ServerTableSize * sizeof(REPL_SERVER_RECORD);
+ FileHeader.ServerStringSize = ServerStringSize * sizeof(TCHAR);
+ FileHeader.ServerServiceTableSize = ServerServiceTableSize * sizeof(REPL_SERVER_SERVICE_RECORD);
+ FileHeader.UserTableSize = UserTableSize * sizeof(REPL_USER_RECORD_1);
+ FileHeader.UserStringSize = UserStringSize * sizeof(TCHAR);
+
+ FileHeader.ServiceLevel = 0;
+ FileHeader.ServerLevel = 0;
+ FileHeader.ServerServiceLevel = 0;
+ FileHeader.UserLevel = 1;
+
+ //
+ // Encrypt the data before saving it out.
+ //
+ Status = EBlock(Services, FileHeader.ServiceTableSize);
+
+ if (Status == STATUS_SUCCESS)
+ Status = EBlock(ServiceStrings, FileHeader.ServiceStringSize);
+
+ if (Status == STATUS_SUCCESS)
+ Status = EBlock(Servers, FileHeader.ServerTableSize);
+
+ if (Status == STATUS_SUCCESS)
+ Status = EBlock(ServerStrings, FileHeader.ServerStringSize);
+
+ if (Status == STATUS_SUCCESS)
+ Status = EBlock(ServerServices, FileHeader.ServerServiceTableSize);
+
+ if (Status == STATUS_SUCCESS)
+ Status = EBlock(Users, FileHeader.UserTableSize);
+
+ if (Status == STATUS_SUCCESS)
+ Status = EBlock(UserStrings, FileHeader.UserStringSize);
+
+ if (Status != STATUS_SUCCESS)
+ goto LLSDataSaveExit;
+
+ //
+ // Save out the header record
+ //
+ hFile = LlsFileInit(UserFileName, USER_FILE_VERSION, sizeof(LLS_DATA_FILE_HEADER) );
+ if (hFile == NULL) {
+ Status = GetLastError();
+ goto LLSDataSaveExit;
+ }
+
+ //
+ // Now write out all the data blocks
+ //
+ ret = WriteFile(hFile, &FileHeader, sizeof(LLS_DATA_FILE_HEADER), &BytesWritten, NULL);
+
+ if (ret && (Services != NULL) && (FileHeader.ServiceTableSize != 0) )
+ ret = WriteFile(hFile, Services, FileHeader.ServiceTableSize, &BytesWritten, NULL);
+
+ if (ret && (ServiceStrings != NULL) && (FileHeader.ServiceStringSize != 0) )
+ ret = WriteFile(hFile, ServiceStrings, FileHeader.ServiceStringSize, &BytesWritten, NULL);
+
+ if (ret && (Servers != NULL) && (FileHeader.ServerTableSize != 0) )
+ ret = WriteFile(hFile, Servers, FileHeader.ServerTableSize, &BytesWritten, NULL);
+
+ if (ret && (ServerStrings != NULL) && (FileHeader.ServerStringSize != 0) )
+ ret = WriteFile(hFile, ServerStrings, FileHeader.ServerStringSize, &BytesWritten, NULL);
+
+ if (ret && (ServerServices != NULL) && (FileHeader.ServerServiceTableSize != 0) )
+ ret = WriteFile(hFile, ServerServices, FileHeader.ServerServiceTableSize, &BytesWritten, NULL);
+
+ if (ret && (Users != NULL) && (FileHeader.UserTableSize != 0) )
+ ret = WriteFile(hFile, Users, FileHeader.UserTableSize, &BytesWritten, NULL);
+
+ if (ret && (UserStrings != NULL) && (FileHeader.UserStringSize != 0) )
+ ret = WriteFile(hFile, UserStrings, FileHeader.UserStringSize, &BytesWritten, NULL);
+
+ if (!ret)
+ Status = GetLastError();
+
+LLSDataSaveExit:
+
+ if (hFile != NULL)
+ CloseHandle(hFile);
+
+ //
+ // Run through our tables and clean them up
+ //
+ if (Services != NULL)
+ MIDL_user_free(Services);
+
+ if (ServiceStrings != NULL)
+ MIDL_user_free(ServiceStrings);
+
+ if (Servers != NULL)
+ MIDL_user_free(Servers);
+
+ if (ServerStrings != NULL)
+ MIDL_user_free(ServerStrings);
+
+ if (ServerServices != NULL)
+ MIDL_user_free(ServerServices);
+
+ if (Users != NULL)
+ MIDL_user_free(Users);
+
+ if (UserStrings != NULL)
+ MIDL_user_free(UserStrings);
+
+ //
+ // If there was an error log it.
+ //
+ if (Status != STATUS_SUCCESS)
+ LogEvent(LLS_EVENT_SAVE_USER, 0, NULL, Status);
+
+ return Status;
+} // LLSDataSave
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+LoadAll ( )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+Return Value:
+
+
+--*/
+
+{
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_DATABASE))
+ dprintf(TEXT("LLS TRACE: LoadAll\n"));
+#endif
+
+ PurchaseFile = NULL;
+ LicenseListLoad();
+
+ MappingListLoad();
+ LLSDataLoad();
+
+ CertDbLoad();
+
+} // LoadAll
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+SaveAll ( )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+Return Value:
+
+
+--*/
+
+{
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_DATABASE))
+ dprintf(TEXT("LLS TRACE: SaveAll\n"));
+#endif
+
+ LicenseListSave();
+ MappingListSave();
+ LLSDataSave();
+ CertDbSave();
+
+} // SaveAll
diff --git a/private/net/svcdlls/lls/server/pack.h b/private/net/svcdlls/lls/server/pack.h
new file mode 100644
index 000000000..7e6feadeb
--- /dev/null
+++ b/private/net/svcdlls/lls/server/pack.h
@@ -0,0 +1,195 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ Pack.h
+
+Abstract:
+
+
+Author:
+
+ Arthur Hanson (arth) Dec 07, 1994
+
+Environment:
+
+Revision History:
+
+ Jeff Parham (jeffparh) 05-Dec-1995
+ o Added new fields to purchase record to support secure certificates.
+ o Unified per server purchase model with per seat purchase model for
+ secure certificates; per server model still done in the traditional
+ manner for non-secure certificates (for backwards compatibility).
+ o Added SaveAll() function analogous to LoadAll().
+ o Added support for extended user data packing/unpacking. This was
+ done to save the SUITE_USE flag across restarts of the service.
+ o Removed user table parameters from unpack routines that didn't use
+ them.
+
+--*/
+
+#ifndef _LLS_PACK_H
+#define _LLS_PACK_H
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/////////////////////////////////////////////////////////////////////
+//
+// Save / Load Mapping
+//
+#define MAPPING_FILE_VERSION 0x0100
+
+typedef struct _PACK_MAPPING_RECORD {
+ LPTSTR Name;
+ LPTSTR Comment;
+ ULONG Licenses;
+} PACK_MAPPING_RECORD, *PPACK_MAPPING_RECORD;
+
+typedef struct _PACK_MAPPING_USER_RECORD {
+ ULONG Mapping;
+ LPTSTR Name;
+} PACK_MAPPING_USER_RECORD, *PPACK_MAPPING_USER_RECORD;
+
+typedef struct _MAPPING_FILE_HEADER {
+ ULONG MappingUserTableSize;
+ ULONG MappingUserStringSize;
+ ULONG MappingTableSize;
+ ULONG MappingStringSize;
+} MAPPING_FILE_HEADER, *PMAPPING_FILE_HEADER;
+
+
+/////////////////////////////////////////////////////////////////////
+//
+// Save / Load License
+//
+
+///////////////// OLD (3.51) FORMAT ////////////////////
+#define LICENSE_FILE_VERSION_0 0x0100
+
+typedef struct _PACK_LICENSE_PURCHASE_RECORD_0 {
+ ULONG Service;
+ LONG NumberLicenses;
+ DWORD Date;
+ LPTSTR Admin;
+ LPTSTR Comment;
+} PACK_LICENSE_PURCHASE_RECORD_0, *PPACK_LICENSE_PURCHASE_RECORD_0;
+
+typedef struct _LICENSE_FILE_HEADER_0 {
+ ULONG LicenseServiceTableSize;
+ ULONG LicenseServiceStringSize;
+ ULONG LicenseTableSize;
+ ULONG LicenseStringSize;
+} LICENSE_FILE_HEADER_0, *PLICENSE_FILE_HEADER_0;
+
+///////////////// NEW FORMAT ////////////////////
+#define LICENSE_FILE_VERSION 0x0201
+
+typedef struct _PACK_LICENSE_SERVICE_RECORD {
+ LPTSTR ServiceName;
+ LONG NumberLicenses;
+} PACK_LICENSE_SERVICE_RECORD, *PPACK_LICENSE_SERVICE_RECORD;
+
+typedef struct _PACK_LICENSE_PURCHASE_RECORD {
+ ULONG Service;
+ LONG NumberLicenses;
+ DWORD Date;
+ LPTSTR Admin;
+ LPTSTR Comment;
+
+ // new for SUR: (see description in purchase.h)
+ ULONG PerServerService;
+ DWORD AllowedModes;
+ DWORD CertificateID;
+ LPTSTR Source;
+ DWORD ExpirationDate;
+ DWORD MaxQuantity;
+ LPTSTR Vendor;
+ DWORD Secrets[ LLS_NUM_SECRETS ];
+} PACK_LICENSE_PURCHASE_RECORD, *PPACK_LICENSE_PURCHASE_RECORD;
+
+typedef struct _LICENSE_FILE_HEADER {
+ ULONG LicenseServiceTableSize;
+ ULONG LicenseServiceStringSize;
+
+ ULONG LicenseTableSize;
+ ULONG LicenseStringSize;
+
+ // new for SUR:
+ ULONG PerServerLicenseServiceTableSize;
+ ULONG PerServerLicenseServiceStringSize;
+
+} LICENSE_FILE_HEADER, *PLICENSE_FILE_HEADER;
+
+
+/////////////////////////////////////////////////////////////////////
+//
+// Save / Load LLS Data
+//
+
+///////////////// OLD (3.51) FORMAT ////////////////////
+#define USER_FILE_VERSION_0 0x0100
+
+typedef struct _LLS_DATA_FILE_HEADER_0 {
+ ULONG ServiceTableSize;
+ ULONG ServiceStringSize;
+ ULONG ServerTableSize;
+ ULONG ServerStringSize;
+ ULONG ServerServiceTableSize;
+ ULONG UserTableSize;
+ ULONG UserStringSize;
+} LLS_DATA_FILE_HEADER_0, *PLLS_DATA_FILE_HEADER_0;
+
+///////////////// NEW FORMAT ////////////////////
+#define USER_FILE_VERSION 0x0200
+
+typedef struct _LLS_DATA_FILE_HEADER {
+ ULONG ServiceLevel;
+ ULONG ServiceTableSize;
+ ULONG ServiceStringSize;
+
+ ULONG ServerLevel;
+ ULONG ServerTableSize;
+ ULONG ServerStringSize;
+
+ ULONG ServerServiceLevel;
+ ULONG ServerServiceTableSize;
+
+ ULONG UserLevel;
+ ULONG UserTableSize;
+ ULONG UserStringSize;
+} LLS_DATA_FILE_HEADER, *PLLS_DATA_FILE_HEADER;
+
+
+
+VOID LicenseListLoad();
+NTSTATUS LicenseListSave();
+VOID MappingListLoad();
+NTSTATUS MappingListSave();
+VOID LLSDataLoad();
+NTSTATUS LLSDataSave();
+
+VOID LoadAll ( );
+VOID SaveAll ( );
+
+NTSTATUS ServiceListPack ( ULONG *pServiceTableSize, PREPL_SERVICE_RECORD *pServices );
+VOID ServiceListUnpack ( ULONG ServiceTableSize, PREPL_SERVICE_RECORD Services, ULONG ServerTableSize, PREPL_SERVER_RECORD Servers, ULONG ServerServiceTableSize, PREPL_SERVER_SERVICE_RECORD ServerServices );
+NTSTATUS ServerListPack ( ULONG *pServerTableSize, PREPL_SERVER_RECORD *pServers );
+VOID ServerListUnpack ( ULONG ServiceTableSize, PREPL_SERVICE_RECORD Services, ULONG ServerTableSize, PREPL_SERVER_RECORD Servers, ULONG ServerServiceTableSize, PREPL_SERVER_SERVICE_RECORD ServerServices );
+NTSTATUS ServerServiceListPack ( ULONG *pServerServiceTableSize, PREPL_SERVER_SERVICE_RECORD *pServerServices );
+VOID ServerServiceListUnpack ( ULONG ServiceTableSize, PREPL_SERVICE_RECORD Services, ULONG ServerTableSize, PREPL_SERVER_RECORD Servers, ULONG ServerServiceTableSize, PREPL_SERVER_SERVICE_RECORD ServerServices );
+NTSTATUS UserListPack ( DWORD LastReplicated, ULONG UserLevel, ULONG *pUserTableSize, LPVOID *pUsers );
+VOID UserListUnpack ( ULONG ServiceTableSize, PREPL_SERVICE_RECORD Services, ULONG ServerTableSize, PREPL_SERVER_RECORD Servers, ULONG ServerServiceTableSize, PREPL_SERVER_SERVICE_RECORD ServerServices, ULONG UserLevel, ULONG UserTableSize, LPVOID Users );
+NTSTATUS PackAll ( DWORD LastReplicated, ULONG *pServiceTableSize, PREPL_SERVICE_RECORD *pServices, ULONG *pServerTableSize, PREPL_SERVER_RECORD *pServers, ULONG *pServerServiceTableSize, PREPL_SERVER_SERVICE_RECORD *pServerServices, ULONG UserLevel, ULONG *pUserTableSize, LPVOID *pUsers );
+VOID UnpackAll ( ULONG ServiceTableSize, PREPL_SERVICE_RECORD Services, ULONG ServerTableSize, PREPL_SERVER_RECORD Servers, ULONG ServerServiceTableSize, PREPL_SERVER_SERVICE_RECORD ServerServices, ULONG UserLevel, ULONG UserTableSize, LPVOID Users );
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/private/net/svcdlls/lls/server/perseat.c b/private/net/svcdlls/lls/server/perseat.c
new file mode 100644
index 000000000..3bb7c4333
--- /dev/null
+++ b/private/net/svcdlls/lls/server/perseat.c
@@ -0,0 +1,3351 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ perseat.c
+
+Abstract:
+
+ Routines to handle per-seat licensing. Handles the in-memory cache
+ of useage via the Rtl Generic table functions (these are a generic
+ splay tree package).
+
+ There can be up to three tables kept. The first table is a username
+ table and is the main table. The second table is for SID's, which will
+ be converted into usernames when replicated.
+
+ The SID and username trees are handled in this module as they are used
+ by all modes of the server.
+
+Author:
+
+ Arthur Hanson (arth) 03-Jan-1995
+
+Revision History:
+
+ Jeff Parham (jeffparh) 12-Jan-1996
+ o Fixed possible infinite loop in UserListLicenseDelete().
+ o In FamilyLicenseUpdate(), now rescans for BackOffice upgrades
+ regardless of whether the family being updated was BackOffice.
+ This fixes a problem wherein a freed BackOffice license was
+ not being assigned to a user that needed it. (Bug #3299.)
+ o Added support for maintaining the SUITE_USE flag when adding
+ users to the AddCache.
+
+--*/
+
+#include <stdlib.h>
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windows.h>
+
+#include "debug.h"
+#include "llsutil.h"
+#include "llssrv.h"
+#include "mapping.h"
+#include "msvctbl.h"
+#include "svctbl.h"
+#include "perseat.h"
+#include "llsevent.h"
+
+#define NO_LLS_APIS
+#include "llsapi.h"
+
+//
+// At what # of product do we switch to BackOffice
+//
+#define BACKOFFICE_SWITCH 3
+
+/////////////////////////////////////////////////////////////////////////
+//
+// Actual User and SID Lists, and their access locks
+//
+ULONG UserListNumEntries = 0;
+ULONG SidListNumEntries = 0;
+RTL_GENERIC_TABLE UserList;
+RTL_GENERIC_TABLE SidList;
+
+RTL_RESOURCE UserListLock;
+RTL_RESOURCE SidListLock;
+
+/////////////////////////////////////////////////////////////////////////
+//
+// The enum processes for replication and UI can take awhile to go through
+// all the records, while doing this they need a shared lock on the file.
+// However, if we request an exclusive access during this time the pending
+// exclusive access will block other shared access's. Therefore we get
+// this lock first before attempting either an add or enum.
+//
+// An add will block enums, but neither of these function are as time
+// critical as updating normal user records.
+//
+RTL_RESOURCE UserListAddEnumLock;
+RTL_RESOURCE SidListAddEnumLock;
+
+/////////////////////////////////////////////////////////////////////////
+//
+// The AddCache itself, a critical section to protect access to it and an
+// event to signal the server when there are items on it that need to be
+// processed.
+//
+PADD_CACHE AddCache = NULL;
+ULONG AddCacheSize = 0;
+RTL_CRITICAL_SECTION AddCacheLock;
+HANDLE LLSAddCacheEvent;
+
+DWORD LastUsedTime = 0;
+BOOL UsersDeleted = FALSE;
+
+
+RTL_CRITICAL_SECTION GenTableLock;
+
+/////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////
+//
+// The License List is a linear list of all the licenses the object is
+// using.
+//
+// The license list is kept as part of each user and mapping record, if
+// the user is mapped then the mapping should contain the license list.
+// The structure is a sorted array of pointers to License Records, and
+// access is controled by the ServiceTableLock.
+//
+// The license is identified by the Service Family Name (the license list
+// is sorted on this).
+//
+
+/////////////////////////////////////////////////////////////////////////
+int __cdecl
+LicenseListCompare(const void *arg1, const void *arg2) {
+ PUSER_LICENSE_RECORD pLic1, pLic2;
+
+ pLic1 = (PUSER_LICENSE_RECORD) *((PUSER_LICENSE_RECORD *) arg1);
+ pLic2 = (PUSER_LICENSE_RECORD) *((PUSER_LICENSE_RECORD *) arg2);
+
+ return lstrcmpi( pLic1->Family->Name, pLic2->Family->Name );
+
+} // LicenseListCompare
+
+
+/////////////////////////////////////////////////////////////////////////
+PUSER_LICENSE_RECORD
+LicenseListFind(
+ LPTSTR Name,
+ PUSER_LICENSE_RECORD *pLicenseList,
+ ULONG NumTableEntries
+ )
+
+/*++
+
+Routine Description:
+
+ Find the license in a license list for the given Family of products.
+
+Arguments:
+
+ Name - Name of product family to find license for.
+
+ pLicenseList - Size of the license list to search.
+
+ NumTableEntries - Pointer to the license List to search.
+
+Return Value:
+
+ Pointer to the found License Record, or NULL if not found.
+
+--*/
+
+{
+ LONG begin = 0;
+ LONG end = (LONG) NumTableEntries - 1;
+ LONG cur;
+ int match;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: LicenseListFind\n"));
+#endif
+
+ if ((Name == NULL) || (pLicenseList == NULL) || (NumTableEntries == 0))
+ return NULL;
+
+ while (end >= begin) {
+ // go halfway in-between
+ cur = (begin + end) / 2;
+
+ // compare the two result into match
+ match = lstrcmpi(Name, pLicenseList[cur]->Family->Name);
+
+ if (match < 0)
+ // move new begin
+ end = cur - 1;
+ else
+ begin = cur + 1;
+
+ if (match == 0)
+ return pLicenseList[cur];
+ }
+
+ return NULL;
+
+} // LicenseListFind
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+LicenseListDelete(
+ PMASTER_SERVICE_ROOT Family,
+ PUSER_LICENSE_RECORD **pLicenses,
+ PULONG pLicenseListSize
+ )
+
+/*++
+
+Routine Description:
+
+ Delete the given license from the license list.
+
+Arguments:
+
+ Family -
+
+ pLicenses -
+
+ pLicenseListSize -
+
+Return Value:
+
+ STATUS_SUCCESS if successful, else error code.
+
+--*/
+
+{
+ PUSER_LICENSE_RECORD *LicenseList;
+ ULONG LicenseListSize;
+ PUSER_LICENSE_RECORD LicenseRec;
+ ULONG i;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: LicenseListDelete\n"));
+#endif
+
+ if ( (pLicenses == NULL) || (pLicenseListSize == NULL) )
+ return STATUS_OBJECT_NAME_NOT_FOUND;
+
+ LicenseListSize = *pLicenseListSize;
+ LicenseList = *pLicenses;
+
+ //
+ // Get record based on name given
+ //
+ LicenseRec = LicenseListFind(Family->Name, LicenseList, LicenseListSize);
+ if (LicenseRec == NULL)
+ return STATUS_OBJECT_NAME_NOT_FOUND;
+
+ //
+ // Check if this is the last user
+ //
+ if (LicenseListSize == 1) {
+ LocalFree(LicenseList);
+ *pLicenseListSize = 0;
+ *pLicenses = NULL;
+ return STATUS_SUCCESS;
+ }
+
+ //
+ // Not the last so find it in the list
+ //
+ i = 0;
+ while ( (i < LicenseListSize) && (LicenseList[i]->Family != Family) )
+ i++;
+
+ //
+ // Now move everything below it up.
+ //
+ i++;
+ while (i < LicenseListSize) {
+ LicenseList[i-1] = LicenseList[i];
+ i++;
+ }
+
+ LicenseList = (PUSER_LICENSE_RECORD *) LocalReAlloc(LicenseList, sizeof(PUSER_LICENSE_RECORD) * (LicenseListSize - 1), LHND);
+
+ //
+ // Make sure we could allocate table
+ //
+ ASSERT(LicenseList != NULL);
+ if (LicenseList == NULL)
+ LicenseListSize = 0;
+ else
+ LicenseListSize--;
+
+ LocalFree(LicenseRec);
+ *pLicenses = LicenseList;
+ *pLicenseListSize = LicenseListSize;
+
+ return STATUS_SUCCESS;
+
+} // LicenseListDelete
+
+
+/////////////////////////////////////////////////////////////////////////
+PUSER_LICENSE_RECORD
+LicenseListAdd(
+ PMASTER_SERVICE_ROOT Family,
+ PUSER_LICENSE_RECORD **pLicenses,
+ PULONG pLicenseListSize
+ )
+
+/*++
+
+Routine Description:
+
+ Adds an empty license record to the license list. Sets the license
+ family, but not any of the other info.
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ PUSER_LICENSE_RECORD *LicenseList;
+ ULONG LicenseListSize;
+ PUSER_LICENSE_RECORD LicenseRec;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: LicenseListAdd\n"));
+#endif
+
+ if ((Family == NULL) || (pLicenses == NULL) || (pLicenseListSize == NULL) )
+ return NULL;
+
+ LicenseList = *pLicenses;
+ LicenseListSize = *pLicenseListSize;
+
+ //
+ // We do a double check here to see if another thread just got done
+ // adding the Mapping, between when we checked last and actually got
+ // the write lock.
+ //
+ LicenseRec = LicenseListFind(Family->Name, LicenseList, LicenseListSize );
+
+ if (LicenseRec != NULL) {
+ return LicenseRec;
+ }
+
+ //
+ // Allocate space for table (zero init it).
+ //
+ if (LicenseList == NULL)
+ LicenseList = (PUSER_LICENSE_RECORD *) LocalAlloc(LPTR, sizeof(PUSER_LICENSE_RECORD));
+ else
+ LicenseList = (PUSER_LICENSE_RECORD *) LocalReAlloc(LicenseList, sizeof(PUSER_LICENSE_RECORD) * (LicenseListSize + 1), LHND);
+
+ //
+ // Make sure we could allocate Mapping table
+ //
+ if (LicenseList == NULL) {
+ pLicenses = NULL;
+ pLicenseListSize = 0;
+ return NULL;
+ }
+
+ LicenseRec = (PUSER_LICENSE_RECORD) LocalAlloc(LPTR, sizeof(USER_LICENSE_RECORD));
+ if (LicenseRec == NULL)
+ return NULL;
+
+ // now copy it over...
+ LicenseList[LicenseListSize] = LicenseRec;
+ LicenseRec->Family = Family;
+ LicenseRec->Flags = LLS_FLAG_LICENSED;
+ LicenseRec->RefCount = 0;
+ LicenseRec->Service = NULL;
+ LicenseRec->LicensesNeeded = 0;
+
+ LicenseListSize++;
+
+ // Have added the entry - now need to sort it in order of the names
+ qsort((void *) LicenseList, (size_t) LicenseListSize, sizeof(PUSER_LICENSE_RECORD), LicenseListCompare);
+
+ *pLicenses = LicenseList;
+ *pLicenseListSize = LicenseListSize;
+ return LicenseRec;
+
+} // LicenseListAdd
+
+
+/////////////////////////////////////////////////////////////////////////
+// These routines are specific to the license list in the user and
+// mapping records.
+/////////////////////////////////////////////////////////////////////////
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+UserLicenseListFree (
+ PUSER_RECORD pUser
+ )
+
+/*++
+
+Routine Description:
+
+ Walks the license list deleting all entries and freeing up any claimed
+ licenses from the service table. This only cleans up the licenses
+ in a user record (not a mapping) so the # licenses is always 1.
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ ULONG i;
+ BOOL ReScan = FALSE;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: UserLicenseListFree\n"));
+#endif
+
+ //
+ // Walk license table and free all licenses
+ //
+ for (i = 0; i < pUser->LicenseListSize; i++) {
+ pUser->LicenseList[i]->Service->LicensesUsed -= 1;
+ pUser->LicenseList[i]->Service->LicensesClaimed -= (1 - pUser->LicenseList[i]->LicensesNeeded);
+ pUser->LicenseList[i]->Service->Family->Flags |= LLS_FLAG_UPDATE;
+ ReScan = TRUE;
+ LocalFree(pUser->LicenseList[i]);
+ }
+
+ //
+ // Free related entries in user list
+ //
+ if (pUser->LicenseList != NULL)
+ LocalFree(pUser->LicenseList);
+
+ pUser->LicenseList = NULL;
+ pUser->LicenseListSize = 0;
+ pUser->LicensedProducts = 0;
+
+ //
+ // Get rid of pointers in services table
+ //
+ for (i = 0; i < pUser->ServiceTableSize; i++)
+ pUser->Services[i].License = NULL;
+
+ //
+ // Check if we freed up licenses and need to re-scan the user-table
+ //
+ if (ReScan) {
+ //
+ // Set to licensed so scan doesn't assign to ourself
+ //
+ pUser->Flags |= LLS_FLAG_LICENSED;
+
+ for (i = 0; i < RootServiceListSize; i++) {
+ if (RootServiceList[i]->Flags & LLS_FLAG_UPDATE) {
+ RootServiceList[i]->Flags &= ~LLS_FLAG_UPDATE;
+ FamilyLicenseUpdate( RootServiceList[i] );
+ }
+ }
+
+ if (pUser->ServiceTableSize > 0)
+ pUser->Flags &= ~LLS_FLAG_LICENSED;
+ }
+} // UserLicenseListFree
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+MappingLicenseListFree (
+ PMAPPING_RECORD pMap
+ )
+
+/*++
+
+Routine Description:
+
+ Walks the license list in a mapping freeing up any claimed licenses.
+ Like UserLicenseListFree, but for a mapping.
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ ULONG i;
+ BOOL ReScan = FALSE;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: MappingLicenseListFree\n"));
+#endif
+
+ //
+ // Walk license table and free all licenses
+ //
+ for (i = 0; i < pMap->LicenseListSize; i++) {
+ pMap->LicenseList[i]->Service->LicensesUsed -= pMap->Licenses;
+ pMap->LicenseList[i]->Service->LicensesClaimed -= (pMap->Licenses - pMap->LicenseList[i]->LicensesNeeded);
+ pMap->LicenseList[i]->Service->Family->Flags |= LLS_FLAG_UPDATE;
+ ReScan = TRUE;
+ LocalFree(pMap->LicenseList[i]);
+ }
+
+ //
+ // Free related entries in mapping list
+ //
+ if (pMap->LicenseList != NULL)
+ LocalFree(pMap->LicenseList);
+
+ pMap->LicenseList = NULL;
+ pMap->LicenseListSize = 0;
+
+ //
+ // Check if we freed up licenses and need to re-scan the user-table
+ //
+ if (ReScan)
+ for (i = 0; i < RootServiceListSize; i++) {
+ if (RootServiceList[i]->Flags & LLS_FLAG_UPDATE) {
+ RootServiceList[i]->Flags &= ~LLS_FLAG_UPDATE;
+ FamilyLicenseUpdate( RootServiceList[i] );
+ }
+ }
+
+} // MappingLicenseListFree
+
+
+
+/////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////
+//
+// The service table is a linear array of records pointed to by the
+// user record. Each entry contains a pointer into the service table
+// identifying the service, some statistical useage information and a
+// pointer into the license table identifying the license used by the
+// service.
+//
+
+/////////////////////////////////////////////////////////////////////////
+int __cdecl
+SvcListCompare(
+ const void *arg1,
+ const void *arg2
+ )
+{
+ PSVC_RECORD pSvc1, pSvc2;
+
+ pSvc1 = (PSVC_RECORD) arg1;
+ pSvc2 = (PSVC_RECORD) arg2;
+
+ return lstrcmpi( pSvc1->Service->Name, pSvc2->Service->Name );
+
+} // SvcListCompare
+
+
+/////////////////////////////////////////////////////////////////////////
+PSVC_RECORD
+SvcListFind(
+ LPTSTR DisplayName,
+ PSVC_RECORD ServiceList,
+ ULONG NumTableEntries
+ )
+
+/*++
+
+Routine Description:
+
+ Internal routine to actually do binary search on Service List in user
+ record. This is a binary search, however since the string pointers are
+ from the service table and therefore the pointers are fixed, we only
+ need to compare the pointers, not the strings themselves to find a
+ match.
+
+Arguments:
+
+ ServiceName -
+
+Return Value:
+
+ Pointer to found service table entry or NULL if not found.
+
+--*/
+
+{
+ LONG begin = 0;
+ LONG end = (LONG) NumTableEntries - 1;
+ LONG cur;
+ int match;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: SvcListFind\n"));
+#endif
+ if ((DisplayName == NULL) || (ServiceList == NULL) || (NumTableEntries == 0))
+ return NULL;
+
+ while (end >= begin) {
+ // go halfway in-between
+ cur = (begin + end) / 2;
+
+ // compare the two result into match
+ match = lstrcmpi(DisplayName, ServiceList[cur].Service->Name);
+
+ if (match < 0)
+ // move new begin
+ end = cur - 1;
+ else
+ begin = cur + 1;
+
+ if (match == 0)
+ return &ServiceList[cur];
+ }
+
+ return NULL;
+
+} // SvcListFind
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+SvcListDelete(
+ LPTSTR UserName,
+ LPTSTR ServiceName
+)
+
+/*++
+
+Routine Description:
+
+ Deletes a service record from the service table.
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ PUSER_RECORD pUserRec;
+ PSVC_RECORD pService;
+ PSVC_RECORD SvcTable = NULL;
+ PUSER_LICENSE_RECORD License = NULL;
+ ULONG NumLicenses = 1;
+ ULONG i;
+ BOOL ReScan = FALSE;
+ PMASTER_SERVICE_ROOT Family = NULL;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: SvcListDelete\n"));
+#endif
+
+ pUserRec = UserListFind(UserName);
+ if (pUserRec == NULL)
+ return STATUS_OBJECT_NAME_NOT_FOUND;
+
+ RtlEnterCriticalSection(&pUserRec->ServiceTableLock);
+ pService = SvcListFind( ServiceName, pUserRec->Services, pUserRec->ServiceTableSize );
+
+ //
+ // If we couldn't find it then get out.
+ //
+ if (pService == NULL) {
+ RtlLeaveCriticalSection(&pUserRec->ServiceTableLock);
+ return STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ Family = pService->Service->Family;
+
+ //
+ // If we are a mapping then we may use more then one license
+ //
+ if (pUserRec->Mapping != NULL)
+ NumLicenses = pUserRec->Mapping->Licenses;
+
+ License = pService->License;
+
+ if (License != NULL) {
+ License->RefCount--;
+
+ //
+ // If this is the last service that uses this license then we need
+ // to get rid of it.
+ //
+ if (License->RefCount == 0) {
+ License->Service->LicensesUsed -= NumLicenses;
+ NumLicenses -= License->LicensesNeeded;
+ License->Service->LicensesClaimed -= NumLicenses;
+
+ //
+ // Do we need to delete it from the mapping or user license table?
+ //
+ if (pUserRec->Mapping != NULL) {
+ if ((License->Service == BackOffice) && (pUserRec->Mapping->Flags & LLS_FLAG_SUITE_AUTO))
+ pUserRec->Mapping->Flags &= ~LLS_FLAG_SUITE_USE;
+
+ LicenseListDelete(License->Service->Family, &pUserRec->Mapping->LicenseList, &pUserRec->Mapping->LicenseListSize );
+
+ } else {
+ if ((License->Service == BackOffice) && (pUserRec->Flags & LLS_FLAG_SUITE_AUTO))
+ pUserRec->Flags &= ~LLS_FLAG_SUITE_USE;
+
+ LicenseListDelete(License->Service->Family, &pUserRec->LicenseList, &pUserRec->LicenseListSize );
+ }
+
+ //
+ // Freed a license so need to scan and adjust counts
+ //
+ ReScan = TRUE;
+ }
+ }
+
+ if (pService->Flags & LLS_FLAG_LICENSED)
+ pUserRec->LicensedProducts--;
+ else {
+ //
+ // This was an unlicensed product - see if this makes the user
+ // licensed
+ //
+ if (pUserRec->LicensedProducts == (pUserRec->ServiceTableSize - 1))
+ pUserRec->Flags |= LLS_FLAG_LICENSED;
+ }
+
+ //
+ // First check if this is the only entry in the table
+ //
+ if (pUserRec->ServiceTableSize == 1) {
+ LocalFree(pUserRec->Services);
+ pUserRec->Services = NULL;
+ goto SvcListDeleteExit;
+ }
+
+ //
+ // Find this record linearly in the table.
+ //
+ i = 0;
+ while ((i < pUserRec->ServiceTableSize) && (lstrcmpi(pUserRec->Services[i].Service->Name, ServiceName)))
+ i++;
+
+ //
+ // Now move everything below it up.
+ //
+ i++;
+ while (i < pUserRec->ServiceTableSize) {
+ memcpy(&pUserRec->Services[i-1], &pUserRec->Services[i], sizeof(SVC_RECORD));
+ i++;
+ }
+
+ SvcTable = (PSVC_RECORD) LocalReAlloc( pUserRec->Services, sizeof(SVC_RECORD) * (pUserRec->ServiceTableSize - 1), LHND);
+
+ ASSERT(SvcTable != NULL);
+ if (SvcTable == NULL) {
+ pUserRec->Services = NULL;
+ pUserRec->ServiceTableSize = 0;
+ RtlLeaveCriticalSection(&pUserRec->ServiceTableLock);
+ return STATUS_SUCCESS;
+ }
+
+ pUserRec->Services = SvcTable;
+
+SvcListDeleteExit:
+ pUserRec->ServiceTableSize--;
+
+ if (pUserRec->ServiceTableSize == 0)
+ pUserRec->Services = NULL;
+
+ RtlLeaveCriticalSection(&pUserRec->ServiceTableLock);
+
+ if (ReScan)
+ FamilyLicenseUpdate ( Family );
+
+ return STATUS_SUCCESS;
+
+} // SvcListDelete
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+SvcListLicenseFree(
+ PUSER_RECORD pUser
+)
+
+/*++
+
+Routine Description:
+
+
+ Walk the services table and free up any licenses they are using. If the
+ licenses are then no longer needed (refCount == 0) then the license is
+ deleted.
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ ULONG i;
+ ULONG NumLicenses = 1;
+ PUSER_LICENSE_RECORD License = NULL;
+ BOOL ReScan = FALSE;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: SvcListLicenseFree\n"));
+#endif
+
+ //
+ // If we are a mapping then we may use more then one license
+ //
+ for (i = 0; i < pUser->ServiceTableSize; i++) {
+
+ if (pUser->Mapping != NULL)
+ NumLicenses = pUser->Mapping->Licenses;
+ else
+ NumLicenses = 1;
+
+ License = pUser->Services[i].License;
+
+ if (License != NULL) {
+ License->RefCount--;
+
+ //
+ // If this is the last service that uses this license then we need
+ // to get rid of it.
+ //
+ if (License->RefCount == 0) {
+ if ( (pUser->Mapping != NULL) && (License->Service == BackOffice) && (pUser->Mapping->Flags & LLS_FLAG_SUITE_AUTO) )
+ pUser->Mapping->Flags &= ~LLS_FLAG_SUITE_USE;
+
+ License->Service->LicensesUsed -= NumLicenses;
+ NumLicenses -= License->LicensesNeeded;
+
+ if (License->Service->LicensesClaimed > 0) {
+ //
+ // Freed a license so need to scan and adjust counts
+ //
+ License->Service->Family->Flags |= LLS_FLAG_UPDATE;
+ ReScan = TRUE;
+ }
+
+ License->Service->LicensesClaimed -= NumLicenses;
+
+ if (pUser->Mapping != NULL)
+ LicenseListDelete(License->Service->Family, &pUser->Mapping->LicenseList, &pUser->Mapping->LicenseListSize );
+ else
+ LicenseListDelete(License->Service->Family, &pUser->LicenseList, &pUser->LicenseListSize );
+
+ }
+ }
+
+ pUser->Services[i].License = NULL;
+ }
+
+ pUser->LicensedProducts = 0;
+
+ //
+ // Check if we freed up licenses and need to re-scan the user-table
+ //
+ if (ReScan) {
+ //
+ // Flag license so rescan won't worry about this user
+ //
+ pUser->Flags |= LLS_FLAG_LICENSED;
+
+ for (i = 0; i < RootServiceListSize; i++) {
+ if (RootServiceList[i]->Flags & LLS_FLAG_UPDATE) {
+ RootServiceList[i]->Flags &= ~LLS_FLAG_UPDATE;
+ FamilyLicenseUpdate( RootServiceList[i] );
+ }
+ }
+ }
+
+} // SvcListLicenseFree
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+SvcListLicenseUpdate(
+ PUSER_RECORD pUser
+)
+
+/*++
+
+Routine Description:
+
+ Walk the services table and assign the appropriate license to each
+ service. This is the opposite of the SvcListLicenseFree Routine.
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ ULONG i;
+ ULONG Claimed = 0;
+ PUSER_LICENSE_RECORD License = NULL;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: SvcListLicenseUpdate\n"));
+#endif
+
+ //
+ // Check if user is set to use BackOffice
+ if ( pUser->Flags & LLS_FLAG_SUITE_USE ) {
+ //
+ // Go grab a backoffice license to fulfill the suite useage
+ //
+ License = LicenseListAdd(BackOffice->Family, &pUser->LicenseList, &pUser->LicenseListSize);
+
+ ASSERT(License != NULL);
+ if (License != NULL) {
+ License->Service = BackOffice;
+ RtlAcquireResourceExclusive(&MasterServiceListLock, TRUE);
+
+ // Can only claim a # of licenses that we have
+ if ( BackOffice->LicensesClaimed < BackOffice->Licenses) {
+ Claimed = BackOffice->Licenses - BackOffice->LicensesClaimed;
+
+ if (Claimed > 1)
+ Claimed = 1;
+
+ }
+
+ //
+ // Adjust license counts
+ //
+ BackOffice->LicensesUsed += 1;
+ BackOffice->LicensesClaimed += Claimed;
+ License->LicensesNeeded = 1 - Claimed;
+
+ //
+ // Figure out if we are licensed or not.
+ //
+ if (License->LicensesNeeded > 0) {
+ //
+ // Not licensed
+ //
+ License->Flags &= ~LLS_FLAG_LICENSED;
+ pUser->Flags &= ~LLS_FLAG_LICENSED;
+
+ for (i = 0; i < pUser->ServiceTableSize; i++) {
+ pUser->Services[i].Flags &= ~LLS_FLAG_LICENSED;
+ pUser->Services[i].License = License;
+ License->RefCount++;
+ }
+ } else {
+ //
+ // Licensed
+ //
+ License->Flags |= LLS_FLAG_LICENSED;
+ pUser->Flags |= LLS_FLAG_LICENSED;
+
+ for (i = 0; i < pUser->ServiceTableSize; i++) {
+ pUser->LicensedProducts++;
+ pUser->Services[i].Flags |= LLS_FLAG_LICENSED;
+ pUser->Services[i].License = License;
+ License->RefCount++;
+ }
+ }
+
+ RtlReleaseResource(&MasterServiceListLock);
+ }
+
+ } else {
+ BOOL Licensed = TRUE;
+
+ //
+ // Loop through all the services and make sure they are all
+ // licensed.
+ //
+ for (i = 0; i < pUser->ServiceTableSize; i++) {
+ SvcLicenseUpdate(pUser, &pUser->Services[i]);
+
+ if ( pUser->Services[i].Flags & LLS_FLAG_LICENSED )
+ pUser->LicensedProducts++;
+ else
+ Licensed = FALSE;
+ }
+
+ if (Licensed)
+ pUser->Flags |= LLS_FLAG_LICENSED;
+ else
+ pUser->Flags &= ~LLS_FLAG_LICENSED;
+ }
+
+} // SvcListLicenseUpdate
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+SvcLicenseUpdate(
+ PUSER_RECORD pUser,
+ PSVC_RECORD Svc
+)
+
+/*++
+
+Routine Description:
+
+ For a given service record for a user check and update license compliance.
+ Is a single service record version of SvcListLicenseUpdate.
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ ULONG NumLicenses = 1;
+ PUSER_LICENSE_RECORD License = NULL;
+ BOOL UseMapping = FALSE;
+ PMASTER_SERVICE_RECORD LicenseService = NULL;
+ PMASTER_SERVICE_RECORD Service;
+ BOOL ReScan = FALSE;
+ DWORD Flags = 0;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: SvcLicenseUpdate\n"));
+#endif
+
+ if ((pUser == NULL) || (Svc == NULL))
+ return;
+
+ Flags = pUser->Flags;
+
+ //
+ // If we are a mapping then we may use more then one license
+ //
+ if (pUser->Mapping != NULL) {
+ NumLicenses = pUser->Mapping->Licenses;
+ UseMapping = TRUE;
+ Flags = pUser->Mapping->Flags;
+ }
+
+ //
+ // Try to find a license record in the license list of the user or mapping
+ // to use. If we are using BackOffice then look for BackOffice license
+ // instead of the service license.
+ if (Flags & LLS_FLAG_SUITE_USE) {
+ Service = BackOffice;
+
+ if (UseMapping)
+ License = LicenseListFind(BackOfficeStr, pUser->Mapping->LicenseList, pUser->Mapping->LicenseListSize);
+ else
+ License = LicenseListFind(BackOfficeStr, pUser->LicenseList, pUser->LicenseListSize);
+
+ } else {
+ //
+ // Not BackOffice - so look for normal service license
+ //
+ Service = Svc->Service;
+ ASSERT(Service != NULL);
+
+ //
+ // Try to find a license for this family of products
+ //
+ if (UseMapping)
+ License = LicenseListFind(Service->Family->Name, pUser->Mapping->LicenseList, pUser->Mapping->LicenseListSize);
+ else
+ License = LicenseListFind(Service->Family->Name, pUser->LicenseList, pUser->LicenseListSize);
+ }
+
+ //
+ // Check if we couldn't find a license. If we didn't find it then we need
+ // to create a new license for this.
+ //
+ if (License == NULL) {
+ ULONG LicenseListSize;
+ PUSER_LICENSE_RECORD *LicenseList;
+
+ //
+ // The license list to use depends if we are part of a mapping or not.
+ //
+ if (UseMapping) {
+ LicenseListSize = pUser->Mapping->LicenseListSize;
+ LicenseList = pUser->Mapping->LicenseList;
+ } else {
+ LicenseListSize = pUser->LicenseListSize;
+ LicenseList = pUser->LicenseList;
+ }
+
+ //
+ // Check if we need to add a license for BackOffice or just the service
+ // itself.
+ //
+ if (Flags & LLS_FLAG_SUITE_USE)
+ License = LicenseListAdd(BackOffice->Family, &LicenseList, &LicenseListSize);
+ else
+ License = LicenseListAdd(Service->Family, &LicenseList, &LicenseListSize);
+
+ //
+ // Now update the couters in the parent record
+ //
+ if (UseMapping) {
+ pUser->Mapping->LicenseListSize = LicenseListSize;
+ pUser->Mapping->LicenseList = LicenseList;
+ } else {
+ pUser->LicenseListSize = LicenseListSize;
+ pUser->LicenseList = LicenseList;
+ }
+
+ if (License != NULL)
+ License->LicensesNeeded = NumLicenses;
+ }
+
+ //
+ // We have either found an old license or added a new one, either way
+ // License points to it.
+ //
+ if (License != NULL) {
+ RtlAcquireResourceExclusive(&MasterServiceListLock, TRUE);
+
+ //
+ // if we have a license for this family already and the product
+ // version >= current then we are okay, else need to get new license
+ //
+ if ( (License->Service != NULL) && (License->Service->Version >= Service->Version) ) {
+ LicenseService = License->Service;
+ } else {
+ //
+ // we have an old license for this family, but the version
+ // isn't adequate, so we need to try and get a new license.
+ // Walk the family tree looking for the licenses we
+ // need.
+ //
+ LicenseService = Service;
+ while ((LicenseService != NULL) && ( (LicenseService->LicensesUsed + NumLicenses) > LicenseService->Licenses) )
+ if (LicenseService->next > 0)
+ LicenseService = MasterServiceTable[LicenseService->next - 1];
+ else
+ LicenseService = NULL;
+
+ //
+ // if we couldn't find a license just use the old
+ // service.
+ if (LicenseService == NULL)
+ LicenseService = Service;
+ else {
+ //
+ // Need to clean up old stuff
+ //
+ if (License->Service != NULL) {
+ //
+ // If we actually free up any licenses then mark that we need
+ // to rescan to allocate these freed licenses.
+ //
+ if ((NumLicenses - License->LicensesNeeded) > 0)
+ ReScan = TRUE;
+
+ License->Service->LicensesUsed -= NumLicenses;
+ License->Service->LicensesClaimed -= (NumLicenses - License->LicensesNeeded);
+ License->LicensesNeeded = NumLicenses;
+ License->Service = NULL;
+ }
+ }
+ }
+
+ if (LicenseService != NULL) {
+ ULONG Claimed = 0;
+
+ //
+ // If we switched services then adjust LicensesUsed
+ //
+ if (License->Service != LicenseService) {
+ LicenseService->LicensesUsed += NumLicenses;
+
+ if (License->Service != NULL) {
+ License->Service->LicensesUsed -= NumLicenses;
+ }
+ }
+
+ // Can only claim a # of licenses that we have
+ if ( LicenseService->LicensesClaimed < LicenseService->Licenses) {
+ Claimed = LicenseService->Licenses - LicenseService->LicensesClaimed;
+
+ if (Claimed > License->LicensesNeeded)
+ Claimed = License->LicensesNeeded;
+
+ }
+
+ LicenseService->LicensesClaimed += Claimed;
+ License->Service = LicenseService;
+ License->LicensesNeeded -= Claimed;
+
+ if (License->LicensesNeeded != 0)
+ License->Flags &= ~LLS_FLAG_LICENSED;
+ else
+ License->Flags |= LLS_FLAG_LICENSED;
+ }
+
+ RtlReleaseResource(&MasterServiceListLock);
+
+ if (License->Flags & LLS_FLAG_LICENSED)
+ Svc->Flags |= LLS_FLAG_LICENSED;
+ else
+ Svc->Flags &= ~LLS_FLAG_LICENSED;
+
+ } else
+ Svc->Flags &= ~LLS_FLAG_LICENSED;
+
+ if (Svc->License != License)
+ License->RefCount++;
+
+ Svc->License = License;
+ if (ReScan)
+ FamilyLicenseUpdate ( Service->Family );
+
+} // SvcLicenseUpdate
+
+
+
+/////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////
+//
+// Misc licensing Routines.
+//
+// BackOffice and Mappings have a special affect on LicenseUseage and so
+// there are a couple miscelanous routines to handle them.
+//
+// There are also two special cases that cause us to re-walk our lists to
+// fixup the licenses:
+//
+// 1. Sometimes when we add licenses we free up some we already had claimed.
+// Ex: Users of a LicenseGroup used 5 SQL 4.0 licenses but could only
+// claim 2 (there weren't enough). Later we add 5 SQL 5.0 licenses,
+// since we can use these to get into license compliance we free the
+// 2 previously claimed licenses and take the 5 SQL 5.0 licenses. Now
+// we need to re-walk the user table to try and apply the 2 freed
+// licenses.
+//
+// If we switch a user to BackOffice then it will also free up licenses
+// causing us to re-walk the table.
+//
+// 2. If we ever apply new licenses to a user in a mapping then we need
+// to re-walk all the other users in the mapping and update their
+// license compliance.
+//
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+MappingLicenseUpdate (
+ PMAPPING_RECORD Mapping,
+ BOOL ReSynch
+ )
+
+/*++
+
+Routine Description:
+
+ Go through all user records for a given mapping and recalc license
+ compliance.
+
+Arguments:
+
+ Mapping - the Mapping to recalc licenses for.
+
+ ReSync - If true all previous licenses are destroyed before the licenses
+ are checked, else only services that currently don't have a
+ license assignment are touched.
+
+Return Value:
+
+
+--*/
+
+{
+ ULONG i, j;
+ PUSER_LICENSE_RECORD License = NULL;
+ PUSER_RECORD pUser;
+ PSVC_RECORD SvcTable = NULL;
+ BOOL BackOfficeCheck = FALSE;
+ ULONG Claimed;
+ BOOL Licensed = TRUE;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: MappingLicenseUpdate\n"));
+#endif
+
+ //
+ // Run through all the users in the mapping - adjust their licenses
+ // based on the licenses the mapping has...
+ //
+ RtlAcquireResourceExclusive(&MappingListLock, TRUE);
+ for (i = 0; i < Mapping->LicenseListSize; i++)
+ if (!(Mapping->LicenseList[i]->Flags & LLS_FLAG_LICENSED))
+ Licensed = FALSE;
+
+ if (Licensed)
+ Mapping->Flags |= LLS_FLAG_LICENSED;
+ else
+ Mapping->Flags &= ~LLS_FLAG_LICENSED;
+
+ //
+ // If we want to resynch then blow away all old references
+ //
+ if (ReSynch)
+ for (i = 0; i < Mapping->LicenseListSize; i++)
+ Mapping->LicenseList[i]->RefCount = 0;
+
+ //
+ // Special handling if the Mapping uses BackOffice
+ //
+ if (Mapping->Flags & LLS_FLAG_SUITE_USE) {
+ License = LicenseListFind(BackOfficeStr, Mapping->LicenseList, Mapping->LicenseListSize);
+
+ //
+ // If there isn't one (can happen if all users were deleted from
+ // the mapping with BackOffice flag set). Then update everything.
+ //
+ if (License == NULL) {
+ License = LicenseListAdd(BackOffice->Family, &Mapping->LicenseList, &Mapping->LicenseListSize);
+
+ ASSERT(License != NULL);
+ if (License != NULL) {
+ License->Service = BackOffice;
+
+ // Can only claim a # of licenses that we have
+ if ( BackOffice->LicensesClaimed < BackOffice->Licenses) {
+ Claimed = BackOffice->Licenses - BackOffice->LicensesClaimed;
+
+ if (Claimed > Mapping->Licenses)
+ Claimed = Mapping->Licenses;
+
+ }
+
+ BackOffice->LicensesUsed += Mapping->Licenses;
+ BackOffice->LicensesClaimed += Claimed;
+ License->LicensesNeeded = Mapping->Licenses - Claimed;
+
+ Mapping->Flags |= LLS_FLAG_LICENSED;
+ if (License->LicensesNeeded > 0) {
+ License->Flags &= ~LLS_FLAG_LICENSED;
+ Mapping->Flags &= ~LLS_FLAG_LICENSED;
+ }
+ }
+ }
+ }
+
+ //
+ // Run through all the members in the Mapping and update their license
+ // Compliance.
+ //
+ for (i = 0; i < Mapping->NumMembers; i++) {
+ pUser = UserListFind(Mapping->Members[i]);
+
+ if ( (pUser != NULL) && (pUser->Mapping == Mapping) ) {
+ RtlEnterCriticalSection(&pUser->ServiceTableLock);
+ SvcTable = pUser->Services;
+ pUser->LicensedProducts = 0;
+
+ if (Mapping->Flags & LLS_FLAG_SUITE_USE) {
+ if (Mapping->Flags & LLS_FLAG_LICENSED) {
+ pUser->Flags |= LLS_FLAG_LICENSED;
+ pUser->LicensedProducts = pUser->ServiceTableSize;
+ } else
+ pUser->Flags &= ~LLS_FLAG_LICENSED;
+
+ //
+ // All Services and users are flagged as per BackOffice
+ //
+ for (j = 0; j < pUser->ServiceTableSize; j++) {
+ if (ReSynch)
+ SvcTable[j].License = NULL;
+
+ if (SvcTable[j].License == NULL) {
+ SvcTable[j].License = License;
+ License->RefCount++;
+ }
+
+ if (Mapping->Flags & LLS_FLAG_LICENSED) {
+ SvcTable[j].Flags |= LLS_FLAG_LICENSED;
+ } else
+ SvcTable[j].Flags &= ~LLS_FLAG_LICENSED;
+ }
+ } else {
+ BOOL Licensed = TRUE;
+
+ //
+ // Fixup all the service records
+ //
+ for (j = 0; j < pUser->ServiceTableSize; j++) {
+ if (ReSynch)
+ SvcTable[j].License = NULL;
+
+ if (SvcTable[j].License == NULL) {
+ SvcLicenseUpdate(pUser, &SvcTable[j]);
+ BackOfficeCheck = TRUE;
+ }
+ }
+
+ //
+ // Now run through the services again and see if this user is
+ // actually licenses for all the products.
+ //
+ pUser->LicensedProducts = 0;
+ for (j = 0; j < pUser->ServiceTableSize; j++)
+ if ( (SvcTable[j].License != NULL) && (SvcTable[j].License->Flags & LLS_FLAG_LICENSED) ) {
+ SvcTable[j].Flags |= LLS_FLAG_LICENSED;
+ pUser->LicensedProducts++;
+ } else {
+ SvcTable[j].Flags &= ~LLS_FLAG_LICENSED;
+ Licensed = FALSE;
+ }
+
+ if (Licensed)
+ pUser->Flags |= LLS_FLAG_LICENSED;
+ else
+ pUser->Flags &= ~LLS_FLAG_LICENSED;
+ }
+
+ RtlLeaveCriticalSection(&pUser->ServiceTableLock);
+ }
+
+ }
+ RtlReleaseResource(&MappingListLock);
+
+ //
+ // Check if we need to re-check for BackOffice
+ //
+ if (BackOfficeCheck && (pUser != NULL))
+ UserBackOfficeCheck( pUser );
+
+} // MappingLicenseUpdate
+
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+UserMappingAdd (
+ PMAPPING_RECORD Mapping,
+ PUSER_RECORD pUser
+ )
+
+/*++
+
+Routine Description:
+
+ Takes care of re-adjusting the licenses when we add a user to a mapping.
+ We need to free up any old licenses they have and point them to use
+ the licenses the mapping has.
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ ULONG i, j;
+ PUSER_LICENSE_RECORD License = NULL;
+ PSVC_RECORD SvcTable = NULL;
+ BOOL ReScan = FALSE;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: UserMappingAdd\n"));
+#endif
+
+ if ( (pUser == NULL) || (Mapping == NULL) )
+ return;
+
+ //
+ // Run though and clean up all old licenses
+ //
+ RtlEnterCriticalSection(&pUser->ServiceTableLock);
+ SvcListLicenseFree(pUser);
+ UserLicenseListFree(pUser);
+ RtlLeaveCriticalSection(&pUser->ServiceTableLock);
+
+ pUser->Mapping = Mapping;
+ MappingLicenseUpdate(Mapping, FALSE);
+
+} // UserMappingAdd
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+FamilyLicenseUpdate (
+ PMASTER_SERVICE_ROOT Family
+ )
+
+/*++
+
+Routine Description:
+
+ Used when licenses are freed-up or added to a given family of products.
+ Goes through the user list looking for out-of-license conditions for the
+ given family of products and distributes the new licenses.
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ ULONG NumLicenses = 1;
+ PUSER_LICENSE_RECORD License = NULL;
+ PMASTER_SERVICE_RECORD LicenseService = NULL;
+ ULONG i, j;
+ PUSER_RECORD pUser;
+ BOOL UseMapping = FALSE;
+ BOOL ReScan = TRUE;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: FamilyLicenseUpdate\n"));
+#endif
+
+ RtlAcquireResourceShared(&UserListLock, TRUE);
+
+ while (ReScan) {
+ //
+ // Walk user list in order of entry - adding any licenses
+ //
+ ReScan = FALSE;
+ i = 0;
+ while (i < UserListNumEntries) {
+ pUser = RtlGetElementGenericTable(&UserList, i);
+
+ if (pUser != NULL) {
+ //
+ // only worry about un-licensed users
+ //
+ if ( !(pUser->Flags & LLS_FLAG_LICENSED ) ) {
+ //
+ // Find the License?
+ //
+ RtlEnterCriticalSection(&pUser->ServiceTableLock);
+ if (pUser->Mapping != NULL) {
+ License = LicenseListFind(Family->Name, pUser->Mapping->LicenseList, pUser->Mapping->LicenseListSize);
+ NumLicenses = pUser->Mapping->Licenses;
+ } else {
+ License = LicenseListFind(Family->Name, pUser->LicenseList, pUser->LicenseListSize);
+ NumLicenses = 1;
+ }
+
+ //
+ // Make sure we need any extra licenses for this product
+ //
+ if ( (License != NULL) && (License->LicensesNeeded > 0) ) {
+ //
+ // We have found a user using this family of products in need
+ // of more licenses...
+ //
+ LicenseService = License->Service;
+
+ if (pUser->Mapping != NULL)
+ pUser->Mapping->Flags |= LLS_FLAG_UPDATE;
+
+ //
+ // Check if we can satisfy licenses using currently
+ // assigned service
+ //
+ if ((LicenseService->Licenses - LicenseService->LicensesClaimed) >= License->LicensesNeeded) {
+ LicenseService->LicensesClaimed += License->LicensesNeeded;
+ License->LicensesNeeded = 0;
+ } else {
+ //
+ // See if any other service will satisfy it...
+ //
+ while ((LicenseService != NULL) && ((LicenseService->Licenses - LicenseService->LicensesClaimed) < NumLicenses ) )
+ if (LicenseService->next > 0)
+ LicenseService = MasterServiceTable[LicenseService->next - 1];
+ else
+ LicenseService = NULL;
+
+ //
+ // check if we found a service to satisfy licensing needs
+ //
+ if (LicenseService != NULL) {
+ //
+ // Free up any stuff - since we are freeing licenses
+ // we need to re-scan.
+ //
+ ReScan = TRUE;
+
+ License->Service->LicensesUsed -= NumLicenses;
+ License->Service->LicensesClaimed -= (NumLicenses - License->LicensesNeeded);
+
+ //
+ // Now do new stuff
+ //
+ License->Service = LicenseService;
+ License->Service->LicensesUsed += NumLicenses;
+ License->Service->LicensesClaimed += NumLicenses;
+ License->LicensesNeeded = 0;
+ } else {
+ //
+ // Eat any unclaimed licenses
+ //
+ LicenseService = License->Service;
+ if (LicenseService->Licenses > LicenseService->LicensesClaimed) {
+ License->LicensesNeeded -= (LicenseService->Licenses - LicenseService->LicensesClaimed);
+ LicenseService->LicensesClaimed = LicenseService->Licenses;
+ }
+ }
+ }
+
+ //
+ // Check if we got into license
+ //
+ if (License->LicensesNeeded == 0) {
+ BOOL Licensed = TRUE;
+
+ License->Flags |= LLS_FLAG_LICENSED;
+
+ //
+ // this license is fulfilled so scan product list and
+ // adjust flags on any product using this license.
+ //
+ for (j = 0; j < pUser->ServiceTableSize; j++) {
+ if (pUser->Services[j].License == License) {
+ pUser->Services[j].Flags |= LLS_FLAG_LICENSED;
+ } else
+ if (!(pUser->Services[j].Flags & LLS_FLAG_LICENSED))
+ Licensed = FALSE;
+ }
+
+ //
+ // Recalc how many products are licensed
+ //
+ pUser->LicensedProducts = 0;
+ for (j = 0; j < pUser->ServiceTableSize; j++) {
+ if (pUser->Services[j].Flags & LLS_FLAG_LICENSED)
+ pUser->LicensedProducts++;
+ }
+
+ if (Licensed)
+ pUser->Flags |= LLS_FLAG_LICENSED;
+ }
+ }
+
+ RtlLeaveCriticalSection(&pUser->ServiceTableLock);
+ }
+ }
+
+ i++;
+ }
+ }
+
+ //
+ // If this license is for BackOffice, we have applied any licenses to
+ // anything set to use BackOffice. If there are still licenses left
+ // then see if any users should be auto-switched to BackOffice.
+ //
+ // if (Family == BackOffice->Family) {
+ i = 0;
+ while ( (BackOffice->LicensesClaimed < BackOffice->Licenses) && (i < UserListNumEntries) ) {
+ pUser = RtlGetElementGenericTable(&UserList, i);
+
+ if (pUser != NULL)
+ UserBackOfficeCheck(pUser);
+
+ i++;
+ }
+ // }
+
+ RtlReleaseResource(&UserListLock);
+
+ //
+ // Run through mapping and re-adjust any that need it.
+ //
+ for (i = 0; i < MappingListSize; i++) {
+ if (MappingList[i]->Flags & LLS_FLAG_UPDATE) {
+ MappingList[i]->Flags &= ~LLS_FLAG_UPDATE;
+ MappingLicenseUpdate( MappingList[i], FALSE );
+ }
+ }
+
+} // FamilyLicenseUpdate
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+UserListLicenseDelete(
+ PMASTER_SERVICE_RECORD Service,
+ LONG Quantity
+)
+
+/*++
+
+Routine Description:
+
+ This is used when licenses are deleted. It must walk the user-list in
+ the reverse order they were entered (since licenses are applied in a
+ FIFO manner they are removed in a LIFO manner) and delete the required
+ number of licenses from those consumed.
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ LONG Licenses;
+ ULONG i, j;
+ PUSER_RECORD pUser;
+ PSVC_RECORD pService;
+ ULONG NumLicenses = 1;
+ PUSER_LICENSE_RECORD License = NULL;
+ BOOL UseMapping = FALSE;
+ LONG Claimed;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: UserListLicenseDelete\n"));
+#endif
+
+ RtlAcquireResourceShared(&UserListLock, TRUE);
+
+ Licenses = 0 - Quantity;
+
+ //
+ // Walk user list in opposite order of entry - removing licenses
+ //
+ i = UserListNumEntries - 1;
+ while (((LONG)i >= 0) && (Licenses > 0)) {
+ pUser = RtlGetElementGenericTable(&UserList, i);
+
+ if (pUser != NULL) {
+ NumLicenses = 1;
+ UseMapping = FALSE;
+
+ //
+ // If we are a mapping then we may use more then one license
+ //
+ if (pUser->Mapping != NULL) {
+ NumLicenses = pUser->Mapping->Licenses;
+ UseMapping = TRUE;
+ }
+
+ //
+ // Try to find a license for this family of products
+ //
+ if (UseMapping)
+ License = LicenseListFind(Service->Family->Name, pUser->Mapping->LicenseList, pUser->Mapping->LicenseListSize);
+ else
+ License = LicenseListFind(Service->Family->Name, pUser->LicenseList, pUser->LicenseListSize);
+
+ if (License != NULL) {
+ //
+ // Check if same as product we adjusted
+ //
+ if (License->Service == Service) {
+ //
+ // Can only release how many we took
+ //
+ Claimed = NumLicenses - License->LicensesNeeded;
+ if (Claimed > 0) {
+ if (Claimed > Licenses) {
+ License->LicensesNeeded += Licenses;
+ License->Service->LicensesClaimed -= Licenses;
+ Licenses = 0;
+ } else {
+ License->LicensesNeeded = NumLicenses;
+ License->Service->LicensesClaimed -= Claimed;
+ Licenses -= Claimed;
+ }
+
+ License->Flags &= ~LLS_FLAG_LICENSED;
+
+ //
+ // Flag any mapping that we need to recalc uses in the
+ // mapping
+ //
+ if (UseMapping)
+ pUser->Mapping->Flags |= LLS_FLAG_UPDATE;
+
+ //
+ // Scan product list and adjust flags on any
+ // product using this license.
+ //
+ RtlEnterCriticalSection(&pUser->ServiceTableLock);
+ for (j = 0; j < pUser->ServiceTableSize; j++)
+ if (pUser->Services[j].License == License)
+ pUser->Services[j].Flags &= ~LLS_FLAG_LICENSED;
+
+ //
+ // Recalc how many products are licensed
+ //
+ pUser->LicensedProducts = 0;
+ for (j = 0; j < pUser->ServiceTableSize; j++) {
+ if (pUser->Services[j].Flags & LLS_FLAG_LICENSED)
+ pUser->LicensedProducts++;
+ }
+
+ RtlLeaveCriticalSection(&pUser->ServiceTableLock);
+ pUser->Flags &= ~LLS_FLAG_LICENSED;
+ }
+ }
+ }
+ }
+
+ i--;
+ }
+
+ RtlReleaseResource(&UserListLock);
+
+ //
+ // Run through mapping and re-adjust any that need it.
+ //
+ for (i = 0; i < MappingListSize; i++) {
+ if (MappingList[i]->Flags & LLS_FLAG_UPDATE) {
+ MappingList[i]->Flags &= ~LLS_FLAG_UPDATE;
+ MappingLicenseUpdate( MappingList[i], FALSE );
+ }
+ }
+
+} // UserListLicenseDelete
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+UserBackOfficeCheck (
+ PUSER_RECORD pUser
+ )
+
+/*++
+
+Routine Description:
+
+ Checks if the user should switch to BackOffice, and if so - does so. If
+ we switch to BackOffice then we need to free up any old licenes the
+ user may be using and claim a BackOffice License.
+
+ Note: We will only switch to BackOffice if there are enough BackOffice
+ licenses available to satisfy our needs.
+
+Arguments:
+
+Return Value:
+
+
+--*/
+
+{
+ DWORD Flags;
+ ULONG i;
+ ULONG LicenseListSize;
+ ULONG NumLicenses = 1;
+ PSVC_RECORD SvcTable = NULL;
+ PUSER_LICENSE_RECORD *LicenseList = NULL;
+ PUSER_LICENSE_RECORD License = NULL;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: UserBackOfficeCheck\n"));
+#endif
+
+ RtlEnterCriticalSection(&pUser->ServiceTableLock);
+ if (pUser->Mapping != NULL) {
+ Flags = pUser->Mapping->Flags;
+ LicenseListSize = pUser->Mapping->LicenseListSize;
+ LicenseList = pUser->Mapping->LicenseList;
+ NumLicenses = pUser->Mapping->Licenses;
+ } else {
+ Flags = pUser->Flags;
+ LicenseListSize = pUser->LicenseListSize;
+ LicenseList = pUser->LicenseList;
+ }
+
+ //
+ // If we are already using BackOffice - get out
+ //
+ if (Flags & LLS_FLAG_SUITE_USE) {
+ RtlLeaveCriticalSection(&pUser->ServiceTableLock);
+ return;
+ }
+
+ if ( Flags & LLS_FLAG_SUITE_AUTO )
+ //
+ // if we aren't licensed, or the # services == auto switch threshold
+ // then switch to using BackOffice
+ //
+ if ((!(Flags & LLS_FLAG_LICENSED)) || ((LicenseListSize + 1) >= BACKOFFICE_SWITCH) ) {
+ //
+ // Make sure we have licenses for this
+ //
+ RtlAcquireResourceExclusive(&MasterServiceListLock, TRUE);
+ if ( BackOffice->Licenses >= (NumLicenses + BackOffice->LicensesClaimed) ) {
+ //
+ // Free up the old licenses - temporarily claim the BackOffice
+ // licenses so somebody else won't.
+ //
+ BackOffice->LicensesClaimed += NumLicenses;
+ UserLicenseListFree(pUser);
+ BackOffice->LicensesClaimed -= NumLicenses;
+
+ //
+ // UserLicenseListFree might have assigned us a license in
+ // the rescan if we are part of a mapping so check this.
+ //
+ if (pUser->Mapping != NULL)
+ Flags = pUser->Mapping->Flags;
+ else
+ Flags = pUser->Flags;
+
+ //
+ // If we are already using BackOffice - get out
+ //
+ if (Flags & LLS_FLAG_SUITE_USE) {
+ RtlLeaveCriticalSection(&pUser->ServiceTableLock);
+ RtlReleaseResource(&MasterServiceListLock);
+ return;
+ }
+
+ //
+ // And if part of a mapping free those up
+ //
+ if (pUser->Mapping != NULL)
+ MappingLicenseListFree(pUser->Mapping);
+
+ //
+ // Now add the BackOffice License
+ //
+ if (pUser->Mapping != NULL) {
+ pUser->Mapping->LicenseList = NULL;
+ pUser->Mapping->LicenseListSize = 0;
+
+ License = LicenseListAdd(BackOffice->Family, &pUser->Mapping->LicenseList, &pUser->Mapping->LicenseListSize);
+
+ LicenseList = pUser->Mapping->LicenseList;
+ LicenseListSize = pUser->Mapping->LicenseListSize;
+ } else {
+ pUser->LicenseList = NULL;
+ pUser->LicenseListSize = 0;
+
+ License = LicenseListAdd(BackOffice->Family, &pUser->LicenseList, &pUser->LicenseListSize);
+
+ LicenseList = pUser->LicenseList;
+ LicenseListSize = pUser->LicenseListSize;
+ }
+
+ ASSERT(License != NULL);
+ if (License != NULL)
+ License->Service = BackOffice;
+
+ //
+ // if mapping adjust mapping records then go through all users and
+ // adjust them
+ //
+ if (pUser->Mapping != NULL) {
+ pUser->Mapping->Flags |= LLS_FLAG_SUITE_USE;
+ pUser->Mapping->Flags |= LLS_FLAG_LICENSED;
+
+ BackOffice->LicensesUsed += NumLicenses;
+ BackOffice->LicensesClaimed += NumLicenses;
+
+ RtlLeaveCriticalSection(&pUser->ServiceTableLock);
+ RtlReleaseResource(&MasterServiceListLock);
+
+ MappingLicenseUpdate(pUser->Mapping, TRUE);
+ return;
+ } else {
+ pUser->Flags |= LLS_FLAG_SUITE_USE;
+ pUser->Flags |= LLS_FLAG_LICENSED;
+
+ pUser->LicensedProducts = pUser->ServiceTableSize;
+ BackOffice->LicensesUsed += NumLicenses;
+ BackOffice->LicensesClaimed += NumLicenses;
+
+ //
+ // Run through products & licenses adjusting licenses
+ //
+ SvcTable = pUser->Services;
+ for (i = 0; i < pUser->ServiceTableSize; i++) {
+ SvcTable[i].Flags |= LLS_FLAG_LICENSED;
+ SvcTable[i].License = License;
+ SvcTable[i].License->RefCount++;
+ }
+
+ }
+
+ }
+
+ RtlReleaseResource(&MasterServiceListLock);
+
+ }
+
+ RtlLeaveCriticalSection(&pUser->ServiceTableLock);
+
+} // UserBackOfficeCheck
+
+
+/////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////
+//
+// Utility routines for managing the user and SID lists - used mostly
+// by the splay table routines.
+
+/////////////////////////////////////////////////////////////////////////
+RTL_GENERIC_COMPARE_RESULTS
+SidListCompare (
+ struct _RTL_GENERIC_TABLE *Table,
+ PVOID FirstStruct,
+ PVOID SecondStruct
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+ Table -
+
+ FirstStruct -
+
+ SecondStruct -
+
+Return Value:
+
+
+--*/
+
+{
+ PUSER_RECORD UseRec1, UseRec2;
+ int ret;
+
+ if ((FirstStruct == NULL) || (SecondStruct == NULL))
+ return GenericEqual;
+
+ UseRec1 = (PUSER_RECORD) FirstStruct;
+ UseRec2 = (PUSER_RECORD) SecondStruct;
+
+ if (UseRec1->IDSize == UseRec2->IDSize) {
+ ret = memcmp((PVOID) UseRec1->UserID, (PVOID) UseRec2->UserID, UseRec1->IDSize);
+ if (ret < 0)
+ return GenericLessThan;
+ else if (ret > 0)
+ return GenericGreaterThan;
+ else
+ return GenericEqual;
+ } else
+ //
+ // Not same size, so just compare length
+ //
+ if (UseRec1->IDSize > UseRec2->IDSize)
+ return GenericGreaterThan;
+ else
+ return GenericLessThan;
+
+} // SidListCompare
+
+
+/////////////////////////////////////////////////////////////////////////
+RTL_GENERIC_COMPARE_RESULTS
+UserListCompare (
+ struct _RTL_GENERIC_TABLE *Table,
+ PVOID FirstStruct,
+ PVOID SecondStruct
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+ Table -
+
+ FirstStruct -
+
+ SecondStruct -
+
+Return Value:
+
+
+--*/
+
+{
+ PUSER_RECORD UseRec1, UseRec2;
+ int ret;
+
+ if ((FirstStruct == NULL) || (SecondStruct == NULL))
+ return GenericEqual;
+
+ UseRec1 = (PUSER_RECORD) FirstStruct;
+ UseRec2 = (PUSER_RECORD) SecondStruct;
+
+ ret = lstrcmpi((LPTSTR) UseRec1->UserID, (LPTSTR) UseRec2->UserID);
+
+ if (ret < 0)
+ return GenericLessThan;
+ else if (ret > 0)
+ return GenericGreaterThan;
+ else
+ return GenericEqual;
+
+} // UserListCompare
+
+
+/////////////////////////////////////////////////////////////////////////
+PVOID
+UserListAlloc (
+ struct _RTL_GENERIC_TABLE *Table,
+ CLONG ByteSize
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+ Table -
+
+ ByteSize -
+
+Return Value:
+
+
+--*/
+
+{
+ return (PVOID) LocalAlloc(LPTR, ByteSize);
+
+} // UserListAlloc
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+UserListFree (
+ struct _RTL_GENERIC_TABLE *Table,
+ PVOID Buffer
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+ Table -
+
+ Buffer -
+
+Return Value:
+
+
+--*/
+
+{
+ PUSER_RECORD UserRec;
+
+ if (Buffer == NULL)
+ return;
+
+ UserRec = (PUSER_RECORD) Buffer;
+ LocalFree(UserRec->UserID);
+ LocalFree(UserRec);
+
+} // UserListFree
+
+
+/////////////////////////////////////////////////////////////////////////
+PUSER_RECORD
+UserListFind(
+ LPTSTR UserName
+)
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ USER_RECORD UserRec;
+ PUSER_RECORD pUserRec;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: UserListFind\n"));
+#endif
+
+ UserRec.UserID = (PVOID) UserName;
+
+ RtlEnterCriticalSection(&GenTableLock);
+ pUserRec = (PUSER_RECORD) RtlLookupElementGenericTable(&UserList, &UserRec);
+ RtlLeaveCriticalSection(&GenTableLock);
+
+ return pUserRec;
+
+} // UserListFind
+
+
+
+/////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+UserListAdd(
+ PMASTER_SERVICE_RECORD Service,
+ ULONG DataType,
+ ULONG DataLength,
+ PVOID Data,
+ ULONG AccessCount,
+ DWORD LastAccess,
+ DWORD FlagsParam
+)
+
+/*++
+
+Routine Description:
+
+ Routine called by the Add cache routine to update the user and/or SID
+ lists with the new service information.
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ USER_RECORD UserRec;
+ PUSER_RECORD pUserRec;
+ BOOLEAN Added;
+ PSVC_RECORD pService;
+ PSVC_RECORD SvcTable = NULL;
+ PRTL_GENERIC_TABLE pTable = NULL;
+ PRTL_RESOURCE pLock = NULL;
+ PRTL_RESOURCE pAddEnumLock = NULL;
+ BOOL SIDSwitch = FALSE;
+ BOOL UserLock = FALSE;
+ BOOL UserEnumLock = FALSE;
+ PMAPPING_RECORD pMap = NULL;
+ ULONG ProductLicenses, ProductLicensesUsed, i;
+ BOOL SwitchToBackOffice = FALSE;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: UserListAdd\n"));
+#endif
+
+ // only 2 bits are used
+ ASSERT( FlagsParam == ( FlagsParam & ( LLS_FLAG_SUITE_USE | LLS_FLAG_SUITE_AUTO ) ) );
+
+ //
+ // Set up lock and table pointers based on if data is SID or username...
+ //
+ UserRec.UserID = Data;
+ if (DataType == DATA_TYPE_USERNAME) {
+ pTable = &UserList;
+ pLock = &UserListLock;
+ pAddEnumLock = &UserListAddEnumLock;
+ } else if (DataType == DATA_TYPE_SID) {
+ pTable = &SidList;
+ pLock = &SidListLock;
+ pAddEnumLock = &SidListAddEnumLock;
+ }
+
+ if (pTable == NULL)
+ return;
+
+ //
+ // The generic table package copies the record so the record is
+ // temporary, but since we store the string as a pointer the pointer is
+ // copied but the actual memory that is pointed to is kept around
+ // permenantly.
+ //
+ // We have already allocated memory for the Data
+ //
+ UserRec.UserID = Data;
+ UserRec.IDSize = DataLength;
+
+ UserRec.Flags = FlagsParam;
+ UserRec.LicensedProducts = 0;
+ UserRec.LastReplicated = 0;
+ UserRec.ServiceTableSize = 0;
+ UserRec.Services = NULL;
+ UserRec.Mapping = NULL;
+ UserRec.LicenseListSize = 0;
+ UserRec.LicenseList = NULL;
+
+ //
+ // Assume that the user is licensed - we will blast it if they aren't
+ // down below.
+ //
+ UserRec.Flags |= LLS_FLAG_LICENSED;
+
+ //
+ // Need to update list so get exclusive access. First get Add/Enum lock
+ // so we don't block reads if doing an enum.
+ //
+ RtlAcquireResourceExclusive(pAddEnumLock, TRUE);
+ RtlAcquireResourceExclusive(pLock, TRUE);
+
+ pUserRec = (PUSER_RECORD) RtlInsertElementGenericTable(pTable, (PVOID) &UserRec, sizeof(USER_RECORD), &Added);
+
+ if (pUserRec == NULL) {
+ ASSERT(FALSE);
+ LocalFree(UserRec.UserID);
+ RtlReleaseResource(pLock);
+ RtlReleaseResource(pAddEnumLock);
+ return;
+ }
+
+ pUserRec->Flags &= ~LLS_FLAG_DELETED;
+
+ // if auto suite is ever turned off, it's gone for good
+ if ( ! ( FlagsParam & LLS_FLAG_SUITE_AUTO ) )
+ {
+ // set suite use to be that specified in the function parameters
+ pUserRec->Flags &= ~LLS_FLAG_SUITE_AUTO;
+ pUserRec->Flags |= ( FlagsParam & LLS_FLAG_SUITE_USE );
+ }
+
+ //
+ // If for some reason the record is already there then we need to
+ // clean-up the name we allocated.
+ //
+ if (Added == FALSE) {
+ LocalFree(UserRec.UserID);
+
+ //
+ // If this is a SID then check the SID record to find the corresponding
+ // USER_RECORD (it better be there) and update that instead.. Note: We
+ // kludge this by storing the pointer to the user table in the
+ // LastReplicated field.
+ //
+ if ((DataType == DATA_TYPE_SID) && (pUserRec->LastReplicated != 0)) {
+ //
+ // Switch data as approp.
+ //
+ SIDSwitch = TRUE;
+ }
+ } else {
+ //
+ // Do this here so when we release to READ access another thread
+ // won't AV when trying to get access to it.
+ //
+ RtlInitializeCriticalSection(&pUserRec->ServiceTableLock);
+
+ if (DataType == DATA_TYPE_USERNAME) {
+ pMap = MappingListUserFind(UserRec.UserID);
+ pUserRec->Mapping = pMap;
+ UserListNumEntries++;
+ } else
+ SidListNumEntries++;
+
+ }
+
+ //
+ // If this is a SID, and we haven't gotten an appropriate user-rec
+ // then try to de-reference this and get the appropriate user-rec.
+ //
+ if ((DataType == DATA_TYPE_SID) && (pUserRec->LastReplicated == 0)) {
+ TCHAR UserName[MAX_USERNAME_LENGTH + 1];
+ TCHAR DomainName[MAX_DOMAINNAME_LENGTH + 1];
+ TCHAR FullName[MAX_USERNAME_LENGTH + MAX_DOMAINNAME_LENGTH + 2];
+ SID_NAME_USE snu;
+ PUSER_RECORD pUserRec2;
+ DWORD unSize, dnSize;
+
+ unSize = sizeof(UserName);
+ dnSize = sizeof(DomainName);
+ if (LookupAccountSid(NULL, (PSID) Data, UserName, &unSize, DomainName, &dnSize, &snu)) {
+ //
+ // Okay, de-referenced the SID, so go get the user-rec, but pre-pend
+ // domain first...
+ //
+ lstrcpy(FullName, DomainName);
+ lstrcat(FullName, TEXT("\\"));
+ lstrcat(FullName, UserName);
+ UserRec.UserID = FullName;
+ UserRec.IDSize = (lstrlen(FullName) + 1) * sizeof(TCHAR);
+
+ //
+ // Get locks, we will try shared first.
+ //
+ RtlAcquireResourceShared(&UserListLock, TRUE);
+ UserLock = TRUE;
+ SIDSwitch = TRUE;
+
+ RtlEnterCriticalSection(&GenTableLock);
+ pUserRec2 = (PUSER_RECORD) RtlLookupElementGenericTable(&UserList, &UserRec);
+ RtlLeaveCriticalSection(&GenTableLock);
+ if (pUserRec2 != NULL) {
+ //
+ // Tarnation! we found it - so use it.
+ //
+ pUserRec->LastReplicated = (ULONG) pUserRec2;
+ } else {
+ //
+ // Dang it all... It ain't in the dern table, so we're gonna
+ // put it there. First need to alloc perm storage for UserID
+ //
+ UserRec.UserID = LocalAlloc(LPTR, UserRec.IDSize);
+ if (UserRec.UserID != NULL) {
+ lstrcpy((LPTSTR) UserRec.UserID, FullName);
+
+ //
+ // Need to update list so get exclusive access. First get
+ // Add/Enum lock so we don't block reads if doing an enum.
+ //
+ RtlAcquireResourceExclusive(&UserListAddEnumLock, TRUE);
+ UserEnumLock = TRUE;
+ RtlConvertSharedToExclusive(&UserListLock);
+ pUserRec2 = (PUSER_RECORD) RtlInsertElementGenericTable(&UserList, (PVOID) &UserRec, sizeof(USER_RECORD), &Added);
+ }
+
+ //
+ // If we couldn't insert it then seomthing is wrong, clean up
+ // and exit.
+ //
+ if (pUserRec2 == NULL) {
+ ASSERT(FALSE);
+
+ if (UserRec.UserID != NULL)
+ LocalFree(UserRec.UserID);
+
+ RtlReleaseResource(pLock);
+ RtlReleaseResource(pAddEnumLock);
+
+ RtlReleaseResource(&UserListLock);
+ RtlReleaseResource(&UserListAddEnumLock);
+ return;
+ }
+
+ //
+ // Update SID USER_REC pointer (LastReplicated) and then finally
+ // free up the SID lock
+ //
+ pUserRec->LastReplicated = (ULONG) pUserRec2;
+
+ if (Added == TRUE) {
+ //
+ // Do this here so when we release to READ access another
+ // thread won't AV when trying to get access to it.
+ //
+ RtlInitializeCriticalSection(&pUserRec2->ServiceTableLock);
+ pMap = MappingListUserFind(UserRec.UserID);
+ pUserRec2->Mapping = pMap;
+ UserListNumEntries++;
+ }
+
+ RtlConvertExclusiveToShared(&UserListLock);
+
+ }
+
+ //
+ // We have found or added a USER_REC for the SID which pUserRec2
+ // points to. Now we need to switch locks and tables.
+ //
+ }
+ }
+
+ //
+ // If we need to munge from SID to User tables, then do so...
+ //
+ if (SIDSwitch) {
+ //
+ // Switch data as approp.
+ //
+ pUserRec = (PUSER_RECORD) pUserRec->LastReplicated;
+ DataType = DATA_TYPE_USERNAME;
+
+ //
+ // Release locks on SID Table
+ //
+ RtlReleaseResource(pLock);
+ RtlReleaseResource(pAddEnumLock);
+
+ //
+ // Now switch locks to User Table
+ //
+ pTable = &UserList;
+ pLock = &UserListLock;
+ pAddEnumLock = &UserListAddEnumLock;
+
+ if (UserEnumLock)
+ RtlReleaseResource(pAddEnumLock);
+
+ if (!UserLock)
+ RtlAcquireResourceShared(pLock, TRUE);
+ } else {
+ //
+ // No longer need exclusive access
+ //
+ RtlConvertExclusiveToShared(pLock);
+
+ //
+ // Also - no longer need Add/Enum Lock
+ //
+ RtlReleaseResource(pAddEnumLock);
+ }
+
+ //
+ // At this point we have either found the old record, or added a new
+ // one. In either case pUserRec points to the correct record.
+ //
+ if (pUserRec != NULL) {
+ //
+ // Check Service table to make sure our service is already there
+ //
+ RtlEnterCriticalSection(&pUserRec->ServiceTableLock);
+ pService = SvcListFind( Service->Name, pUserRec->Services, pUserRec->ServiceTableSize );
+
+ if (pService != NULL) {
+ //
+ // Found entry in service table so just increment count
+ //
+ if (pService->AccessCount + AccessCount < MAX_ACCESS_COUNT)
+ pService->AccessCount += AccessCount;
+ else
+ pService->AccessCount = MAX_ACCESS_COUNT;
+
+ pService->LastAccess = LastAccess;
+ } else {
+ //
+ // Need to add more entries to service table (or create it...)
+ //
+ if (pUserRec->Services == NULL)
+ SvcTable = (PSVC_RECORD) LocalAlloc( LPTR, sizeof(SVC_RECORD));
+ else
+ SvcTable = (PSVC_RECORD) LocalReAlloc( pUserRec->Services, sizeof(SVC_RECORD) * (pUserRec->ServiceTableSize + 1), LHND);
+
+ pUserRec->Services = SvcTable;
+ ASSERT(SvcTable != NULL);
+ if (SvcTable != NULL) {
+ DWORD Flags;
+
+ if (pUserRec->Mapping != NULL)
+ Flags = pUserRec->Mapping->Flags;
+ else
+ Flags = pUserRec->Flags;
+
+ SvcTable[pUserRec->ServiceTableSize].Service = Service;
+ SvcTable[pUserRec->ServiceTableSize].LastAccess = LastAccess;
+
+ //
+ // Update AccessCount field, but make sure we don't roll over
+ //
+ if (AccessCount < MAX_ACCESS_COUNT)
+ SvcTable[pUserRec->ServiceTableSize].AccessCount = AccessCount;
+ else
+ SvcTable[pUserRec->ServiceTableSize].AccessCount = MAX_ACCESS_COUNT;
+
+ SvcTable[pUserRec->ServiceTableSize].Flags = LLS_FLAG_LICENSED;
+
+ //
+ // Now update the actual license info
+ //
+ SvcLicenseUpdate(pUserRec, &SvcTable[pUserRec->ServiceTableSize]);
+ pUserRec->ServiceTableSize += 1;
+
+ if (SvcTable[pUserRec->ServiceTableSize - 1].Flags & LLS_FLAG_LICENSED)
+ pUserRec->LicensedProducts++;
+
+ //
+ // If the added product isn't licensed then update user flag
+ //
+ if (IsMaster && !(SvcTable[pUserRec->ServiceTableSize - 1].Flags & LLS_FLAG_LICENSED) )
+ pUserRec->Flags &= ~LLS_FLAG_LICENSED;
+
+ // Now that it is all setup - sort the table (so search will work)
+ qsort((void *) pUserRec->Services, (size_t) pUserRec->ServiceTableSize, sizeof(SVC_RECORD), SvcListCompare);
+
+ UserBackOfficeCheck ( pUserRec );
+ }
+
+ }
+ RtlLeaveCriticalSection(&pUserRec->ServiceTableLock);
+
+ }
+
+ RtlReleaseResource(pLock);
+} // UserListAdd
+
+
+/////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////
+//
+// The AddCache routines are a queue of User Identifiers (Username or
+// SID's) and the service being accessed. Records are dequeued by a
+// background thread and handed off to the UserListAdd function.
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+AddCacheManager (
+ IN PVOID ThreadParameter
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+ ThreadParameter - Not used.
+
+
+Return Value:
+
+ This thread never exits.
+
+--*/
+
+{
+ NTSTATUS Status;
+ PADD_CACHE pAdd;
+
+ //
+ // Loop forever waiting to be given the opportunity to serve the
+ // greater good.
+ //
+ for ( ; ; ) {
+ //
+ // Wait to be notified that there is work to be done
+ //
+ Status = NtWaitForSingleObject( LLSAddCacheEvent, TRUE, NULL );
+
+ //
+ // Take an item from the add cache.
+ //
+ RtlEnterCriticalSection(&AddCacheLock);
+ while (AddCache != NULL) {
+ pAdd = AddCache;
+ AddCache = AddCache->prev;
+ AddCacheSize--;
+
+ RtlLeaveCriticalSection(&AddCacheLock);
+
+ if (pAdd != NULL) {
+ UserListAdd(pAdd->Service, pAdd->DataType, pAdd->DataLength, pAdd->Data, pAdd->AccessCount, pAdd->LastAccess, pAdd->Flags);
+ LocalFree(pAdd);
+ }
+
+ Sleep(0);
+ //
+ // Need to re-enter critical section to check in the while loop
+ RtlEnterCriticalSection(&AddCacheLock);
+ }
+
+ RtlLeaveCriticalSection(&AddCacheLock);
+ }
+
+} // AddCacheManager
+
+
+/////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+UserListInit()
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ NTSTATUS Status;
+ DWORD Ignore;
+ HANDLE Thread;
+
+ //
+ // Initialize the generic table.
+ //
+ RtlInitializeGenericTable ( &UserList,
+ UserListCompare,
+ UserListAlloc,
+ UserListFree,
+ (PVOID) TEXT("LLS Table") );
+
+ RtlInitializeCriticalSection(&GenTableLock);
+ RtlInitializeResource(&UserListLock);
+ RtlInitializeResource(&UserListAddEnumLock);
+
+ //
+ // Initialize the SID table.
+ //
+ RtlInitializeGenericTable ( &SidList,
+ SidListCompare,
+ UserListAlloc,
+ UserListFree,
+ (PVOID) TEXT("LLS SID Table") );
+
+ RtlInitializeResource(&SidListLock);
+ RtlInitializeResource(&SidListAddEnumLock);
+
+ //
+ // Now our add cache
+ //
+ RtlInitializeCriticalSection(&AddCacheLock);
+
+ //
+ // Create the Add Cache Management event
+ //
+ Status = NtCreateEvent(
+ &LLSAddCacheEvent,
+ EVENT_QUERY_STATE | EVENT_MODIFY_STATE | SYNCHRONIZE,
+ NULL,
+ SynchronizationEvent,
+ FALSE
+ );
+
+ ASSERT(NT_SUCCESS(Status));
+
+ //
+ // Create the Add Cache management thread
+ //
+ Thread = CreateThread(
+ NULL,
+ 0L,
+ (LPTHREAD_START_ROUTINE) AddCacheManager,
+ 0L,
+ 0L,
+ &Ignore
+ );
+
+
+ LastUsedTime = DateSystemGet();
+
+} // UserListInit
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+UserListUpdate(
+ ULONG DataType,
+ PVOID Data,
+ PSERVICE_RECORD Service
+)
+
+/*++
+
+Routine Description:
+
+ Actual start of the perseat license code. Given a SID or UserName find
+ the record in the appropriate table and check the given service. If the
+ service is already there then the info is updated, if it isn't there then
+ the record is put onto the add cache queue for background processing.
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ USER_RECORD UserRec;
+ PUSER_RECORD pUserRec;
+ BOOLEAN Added;
+ ULONG DataLength;
+ PSVC_RECORD pService;
+ PSVC_RECORD SvcTable = NULL;
+ PRTL_GENERIC_TABLE pTable = NULL;
+ PRTL_RESOURCE pLock = NULL;
+ PADD_CACHE pAdd = NULL;
+ NTSTATUS NtStatus;
+ BOOL ToAddCache = FALSE;
+ BOOL FullName = TRUE;
+ LPTSTR pName;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: UserListUpdate\n"));
+#endif
+
+ //
+ // Setup table and lock pointers based if Data is UserName or SID
+ //
+ UserRec.UserID = Data;
+ if (DataType == DATA_TYPE_USERNAME) {
+ pTable = &UserList;
+ pLock = &UserListLock;
+ DataLength = (lstrlen((LPWSTR) Data) + 1) * sizeof(TCHAR);
+ } else if (DataType == DATA_TYPE_SID) {
+ pTable = &SidList;
+ pLock = &SidListLock;
+ DataLength = RtlLengthSid((PSID) Data);
+ }
+
+ if (pTable == NULL)
+ return;
+
+ //
+ // Searching so don't need exclusive access
+ //
+ RtlAcquireResourceShared(pLock, TRUE);
+
+ RtlEnterCriticalSection(&GenTableLock);
+ pUserRec = (PUSER_RECORD) RtlLookupElementGenericTable(pTable, &UserRec);
+ RtlLeaveCriticalSection(&GenTableLock);
+ if (pUserRec == NULL)
+ ToAddCache = TRUE;
+ else {
+ //
+ // pUserRec now points to the record we must update.
+ //
+ // Check Service table to make sure our service is already there
+ //
+ pUserRec->Flags &= ~LLS_FLAG_DELETED;
+ RtlEnterCriticalSection(&pUserRec->ServiceTableLock);
+ pService = SvcListFind( Service->DisplayName, pUserRec->Services, pUserRec->ServiceTableSize );
+
+ if (pService == NULL)
+ ToAddCache = TRUE;
+ else {
+ //
+ // Found entry in service table so just increment count
+ //
+ pService->AccessCount += 1;
+ pService->LastAccess = LastUsedTime;
+ }
+ RtlLeaveCriticalSection(&pUserRec->ServiceTableLock);
+
+ }
+
+ RtlReleaseResource(pLock);
+
+ if (ToAddCache) {
+ //
+ // Couldn't find the specific user/service, so put it on the Add Cache.
+ // First alloc memory for the name and Add Cache record.
+ //
+ pAdd = LocalAlloc(LPTR, sizeof(ADD_CACHE));
+ if (pAdd == NULL) {
+ ASSERT(FALSE);
+ return;
+ }
+
+ if (DataType == DATA_TYPE_USERNAME) {
+ FullName = FALSE;
+ pName = (LPTSTR) Data;
+
+ //
+ // Make sure first char isn't backslash, if not then look for
+ // backslash as domain-name. If first char is backslash then get
+ // rid of it.
+ //
+ if (*pName != TEXT('\\'))
+ while ((*pName != TEXT('\0')) && !FullName) {
+ if (*pName == TEXT('\\'))
+ FullName = TRUE;
+
+ pName++;
+ }
+ else
+ ((LPTSTR) Data)++;
+
+ }
+
+ //
+ // If we don't have a fully qualified Domain\Username, then tack the
+ // Domain name onto the name.
+ //
+ if (!FullName) {
+ UserRec.UserID = LocalAlloc( LPTR, DataLength + MyDomainSize);
+
+ if (UserRec.UserID == NULL) {
+ ASSERT(FALSE);
+ LocalFree(pAdd);
+ return;
+ }
+
+ pAdd->Data = UserRec.UserID;
+
+ lstrcpy((LPTSTR) pAdd->Data, MyDomain);
+ lstrcat((LPTSTR) pAdd->Data, (LPTSTR) Data);
+ pAdd->DataLength = DataLength + MyDomainSize;
+
+ } else {
+ UserRec.UserID = LocalAlloc( LPTR, DataLength);
+
+ if (UserRec.UserID == NULL) {
+ ASSERT(FALSE);
+ LocalFree(pAdd);
+ return;
+ }
+
+ pAdd->Data = UserRec.UserID;
+ memcpy(pAdd->Data, Data, DataLength);
+ pAdd->DataLength = DataLength;
+ }
+
+ //
+ // copy over all the data fields into the newly created Add Cache
+ // record.
+ //
+ pAdd->DataType = DataType;
+ pAdd->Service = Service->MasterService;
+ pAdd->AccessCount = 1;
+ pAdd->LastAccess = LastUsedTime;
+ pAdd->Flags = LLS_FLAG_SUITE_AUTO;
+
+ //
+ // Now update the actual Add Cache
+ //
+ RtlEnterCriticalSection(&AddCacheLock);
+ pAdd->prev = AddCache;
+ AddCache = pAdd;
+ AddCacheSize++;
+ RtlLeaveCriticalSection(&AddCacheLock);
+
+ //
+ // Now must signal the event so we can pull off the new record.
+ //
+ NtStatus = NtSetEvent( LLSAddCacheEvent, NULL );
+ ASSERT(NT_SUCCESS(NtStatus));
+ }
+
+} // UserListUpdate
+
+
+#if DBG
+/////////////////////////////////////////////////////////////////////////
+VOID
+AddCacheDebugDump ( )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ NTSTATUS Status;
+ PADD_CACHE pAdd;
+ UNICODE_STRING UString;
+ ULONG i = 0;
+
+ RtlEnterCriticalSection(&AddCacheLock);
+
+ dprintf(TEXT("Add Cache Dump. Record Size: %4lu # Entries: %lu\n"), sizeof(ADD_CACHE), AddCacheSize);
+ pAdd = AddCache;
+
+ while (pAdd != NULL) {
+ if (pAdd->DataType == DATA_TYPE_USERNAME)
+ dprintf(TEXT("%4lu) Svc: %s User: [%2lu] %s\n"),
+ ++i,
+ pAdd->Service,
+ pAdd->DataLength,
+ pAdd->Data);
+ else if (pAdd->DataType == DATA_TYPE_SID) {
+ Status = RtlConvertSidToUnicodeString(&UString, (PSID) pAdd->Data, TRUE);
+
+ dprintf(TEXT("%4lu) Svc: %s User: [%2lu] %s\n"),
+ ++i,
+ pAdd->Service,
+ pAdd->DataLength,
+ UString.Buffer);
+
+ RtlFreeUnicodeString(&UString);
+ }
+
+ pAdd = pAdd->prev;
+ }
+
+ RtlLeaveCriticalSection(&AddCacheLock);
+
+} // AddCacheDebugDump
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+UserListDebugDump( )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ ULONG i = 0;
+ PUSER_RECORD UserRec = NULL;
+ PVOID RestartKey = NULL;
+
+ RtlAcquireResourceShared(&UserListLock, TRUE);
+
+ dprintf(TEXT("User List Dump. Record Size: %4lu # Entries: %lu\n"), sizeof(USER_RECORD), UserListNumEntries);
+ UserRec = (PUSER_RECORD) RtlEnumerateGenericTableWithoutSplaying(&UserList, (VOID **) &RestartKey);
+
+ while (UserRec != NULL) {
+ //
+ // Dump info for found user-rec
+ //
+ if (UserRec->Mapping != NULL)
+ dprintf(TEXT("%4lu) Repl: %s LT: %2lu Svc: %2lu Flags: 0x%4lX Map: %s User: [%2lu] %s\n"),
+ ++i,
+ TimeToString(UserRec->LastReplicated),
+ UserRec->LicenseListSize,
+ UserRec->ServiceTableSize,
+ UserRec->Flags,
+ UserRec->Mapping->Name,
+ UserRec->IDSize,
+ (LPTSTR) UserRec->UserID );
+ else
+ dprintf(TEXT("%4lu) Repl: %s LT: %2lu Svc: %2lu Flags: 0x%4lX User: [%2lu] %s\n"),
+ ++i,
+ TimeToString(UserRec->LastReplicated),
+ UserRec->LicenseListSize,
+ UserRec->ServiceTableSize,
+ UserRec->Flags,
+ UserRec->IDSize,
+ (LPTSTR) UserRec->UserID );
+
+ // Get next record
+ UserRec = (PUSER_RECORD) RtlEnumerateGenericTableWithoutSplaying(&UserList, (VOID **) &RestartKey);
+ }
+
+ RtlReleaseResource(&UserListLock);
+} // UserListDebugDump
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+UserListDebugInfoDump(
+ PVOID Data
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ USER_RECORD UserRec;
+ PUSER_RECORD pUserRec;
+ PSVC_RECORD SvcTable = NULL;
+ ULONG i;
+
+ RtlAcquireResourceShared(&UserListLock, TRUE);
+ dprintf(TEXT("User List Info. Record Size: %4lu # Entries: %lu\n"), sizeof(USER_RECORD), UserListNumEntries);
+
+ //
+ // Only dump user if one was specified.
+ //
+ if (lstrlen((LPWSTR) Data) > 0) {
+ UserRec.UserID = Data;
+
+ RtlEnterCriticalSection(&GenTableLock);
+ pUserRec = (PUSER_RECORD) RtlLookupElementGenericTable(&UserList, &UserRec);
+ RtlLeaveCriticalSection(&GenTableLock);
+
+ if (pUserRec != NULL) {
+ //
+ // Dump info for found user-rec
+ //
+ if (pUserRec->Mapping != NULL)
+ dprintf(TEXT(" Repl: %s LT: %2lu Svc: %2lu Flags: 0x%4lX Map: %s User: [%2lu] %s\n"),
+ TimeToString(pUserRec->LastReplicated),
+ pUserRec->LicenseListSize,
+ pUserRec->ServiceTableSize,
+ pUserRec->Flags,
+ pUserRec->Mapping->Name,
+ pUserRec->IDSize,
+ (LPTSTR) pUserRec->UserID );
+ else
+ dprintf(TEXT(" Repl: %s LT: %2lu Svc: %2lu Flags: 0x%4lX User: [%2lu] %s\n"),
+ TimeToString(pUserRec->LastReplicated),
+ pUserRec->LicenseListSize,
+ pUserRec->ServiceTableSize,
+ pUserRec->Flags,
+ pUserRec->IDSize,
+ (LPTSTR) pUserRec->UserID );
+
+ //
+ // Now do the service table - but get critical section first.
+ //
+ RtlEnterCriticalSection(&pUserRec->ServiceTableLock);
+ SvcTable = pUserRec->Services;
+
+ if (pUserRec->ServiceTableSize != 0)
+ dprintf(TEXT("\nServiceTable\n"));
+
+ for (i = 0; i < pUserRec->ServiceTableSize; i++)
+ dprintf( TEXT(" AC: %4lu LA: %s Flags: 0x%4lX Svc: %s\n"),
+ SvcTable[i].AccessCount,
+ TimeToString(SvcTable[i].LastAccess),
+ SvcTable[i].Flags,
+ SvcTable[i].Service->Name );
+
+ if (pUserRec->LicenseListSize != 0)
+ dprintf(TEXT("\nLicenseTable\n"));
+
+ for (i = 0; i < pUserRec->LicenseListSize; i++)
+ dprintf( TEXT(" Flags: 0x%4lX Ref: %2lu LN: %2lu Svc: %s\n"),
+ pUserRec->LicenseList[i]->Flags,
+ pUserRec->LicenseList[i]->RefCount,
+ pUserRec->LicenseList[i]->LicensesNeeded,
+ pUserRec->LicenseList[i]->Service->Name );
+
+ RtlLeaveCriticalSection(&pUserRec->ServiceTableLock);
+
+ } else
+ dprintf(TEXT("User Not Found: %s\n"), (LPWSTR) Data);
+ }
+
+ RtlReleaseResource(&UserListLock);
+
+} // UserListDebugInfoDump
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+UserListDebugFlush( )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ USER_RECORD UserRec;
+
+ //
+ // Searching so don't need exclusive access
+ //
+ RtlAcquireResourceExclusive(&UserListLock, TRUE);
+
+ RtlEnterCriticalSection(&GenTableLock);
+ RtlLookupElementGenericTable(&UserList, &UserRec);
+ RtlLeaveCriticalSection(&GenTableLock);
+
+ RtlReleaseResource(&UserListLock);
+} // UserListDebugFlush
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+SidListDebugDump( )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ ULONG i = 0;
+ PUSER_RECORD UserRec = NULL;
+ UNICODE_STRING UString;
+ NTSTATUS NtStatus;
+ PVOID RestartKey = NULL;
+
+ RtlAcquireResourceShared(&SidListLock, TRUE);
+
+ dprintf(TEXT("SID List Dump. Record Size: %4lu # Entries: %lu\n"), sizeof(USER_RECORD), SidListNumEntries);
+ UserRec = (PUSER_RECORD) RtlEnumerateGenericTableWithoutSplaying(&SidList, (VOID **) &RestartKey);
+
+ while (UserRec != NULL) {
+ //
+ // Dump info for found user-rec
+ //
+ NtStatus = RtlConvertSidToUnicodeString(&UString, (PSID) UserRec->UserID, TRUE);
+ dprintf(TEXT("%4lu) User-Rec: 0x%lX Svc: %2lu User: [%2lu] %s\n"),
+ ++i,
+ UserRec->LastReplicated,
+ UserRec->ServiceTableSize,
+ UserRec->IDSize,
+ UString.Buffer );
+
+ RtlFreeUnicodeString(&UString);
+
+ // Get next record
+ UserRec = (PUSER_RECORD) RtlEnumerateGenericTableWithoutSplaying(&SidList, (VOID **) &RestartKey);
+ }
+
+ RtlReleaseResource(&SidListLock);
+} // SidListDebugDump
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+SidListDebugInfoDump(
+ PVOID Data
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ USER_RECORD UserRec;
+ PUSER_RECORD pUserRec;
+ PSVC_RECORD SvcTable = NULL;
+ ULONG i;
+
+ RtlAcquireResourceShared(&SidListLock, TRUE);
+ dprintf(TEXT("SID List Info. Record Size: %4lu # Entries: %lu\n"), sizeof(USER_RECORD), SidListNumEntries);
+
+ //
+ // Only dump user if one was specified.
+ //
+ if (lstrlen((LPWSTR) Data) > 0) {
+ UserRec.UserID = Data;
+
+ RtlEnterCriticalSection(&GenTableLock);
+ pUserRec = (PUSER_RECORD) RtlLookupElementGenericTable(&SidList, &UserRec);
+ RtlLeaveCriticalSection(&GenTableLock);
+
+ if (pUserRec != NULL) {
+ //
+ // Dump info for found user-rec
+ //
+ dprintf(TEXT(" User-Rec: 0x%lX Svc: %2lu User: [%2lu] %s\n"),
+ pUserRec->LastReplicated,
+ pUserRec->ServiceTableSize,
+ pUserRec->IDSize,
+ (LPTSTR) pUserRec->UserID );
+
+ // No Service Table for SID's
+ } else
+ dprintf(TEXT("SID Not Found: %s\n"), (LPWSTR) Data);
+ }
+
+ RtlReleaseResource(&SidListLock);
+
+} // SidListDebugInfoDump
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+SidListDebugFlush( )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ USER_RECORD UserRec;
+
+ //
+ // Searching so don't need exclusive access
+ //
+ RtlAcquireResourceExclusive(&SidListLock, TRUE);
+
+ RtlReleaseResource(&SidListLock);
+} // SidListDebugFlush
+
+
+#endif
diff --git a/private/net/svcdlls/lls/server/perseat.h b/private/net/svcdlls/lls/server/perseat.h
new file mode 100644
index 000000000..00257f1c6
--- /dev/null
+++ b/private/net/svcdlls/lls/server/perseat.h
@@ -0,0 +1,235 @@
+/*++
+
+Copyright (c) 1994 Microsoft Corporation
+
+Module Name:
+
+ PerSeat.h
+
+Abstract:
+
+
+Author:
+
+ Arthur Hanson (arth) Dec 07, 1994
+
+Environment:
+
+Revision History:
+
+ Jeff Parham (jeffparh) 12-Jan-1996
+ o Added support for maintaining the SUITE_USE flag when adding
+ users to the AddCache.
+ o Exported function prototype for MappingLicenseListFree().
+
+--*/
+
+#ifndef _LLS_PERSEAT_H
+#define _LLS_PERSEAT_H
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define DATA_TYPE_USERNAME 0
+#define DATA_TYPE_SID 1
+
+#define MAX_ACCESS_COUNT 0xFFFFFFF
+
+
+/////////////////////////////////////////////////////////////////////////
+//
+// Add cache is here as add records need exclusive access to the user table.
+// Since we may have alot of read requests comming in at once, we don't want
+// to hold up our reply waiting for the exclusive access to be granted, so
+// we just dump it onto the add cache (queue) and continue on.
+//
+// This is even more important with outgoing replication going on as we can
+// have shared access lock on the table for awhile.
+//
+// Incomming replication just bundles up the data and sticks it on the Add
+// Cache to be processed like normal requests.
+//
+struct _ADD_CACHE;
+
+typedef struct _ADD_CACHE {
+ struct _ADD_CACHE *prev;
+ ULONG DataType;
+ ULONG DataLength;
+ PVOID Data;
+ PMASTER_SERVICE_RECORD Service;
+ ULONG AccessCount;
+ DWORD LastAccess;
+ DWORD Flags;
+} ADD_CACHE, *PADD_CACHE;
+
+
+/////////////////////////////////////////////////////////////////////////
+//
+// These records are for storing the actual user-useage information.
+//
+typedef struct _USER_LICENSE_RECORD {
+ DWORD Flags;
+ PMASTER_SERVICE_ROOT Family;
+ ULONG RefCount;
+
+ //
+ // Version of product License applies to
+ PMASTER_SERVICE_RECORD Service;
+ ULONG LicensesNeeded;
+} USER_LICENSE_RECORD, *PUSER_LICENSE_RECORD;
+
+typedef struct _SVC_RECORD {
+ //
+ // Actual service this is for
+ //
+ PMASTER_SERVICE_RECORD Service;
+
+ //
+ // What license we took - The product may be SQL 3.0, but in determining
+ // the license we might have grabbed a SQL 4.0 license...
+ //
+ PUSER_LICENSE_RECORD License;
+
+ ULONG AccessCount;
+ DWORD LastAccess;
+ ULONG Suite;
+ DWORD Flags;
+} SVC_RECORD, *PSVC_RECORD;
+
+typedef struct _USER_RECORD {
+ ULONG IDSize;
+ PVOID UserID;
+
+ //
+ // Pointer to mapping to use.
+ //
+ PMAPPING_RECORD Mapping;
+
+ //
+ // Flags is mostly used right now for marking records to be deleted and
+ // if backoffice has been set.
+ //
+ DWORD Flags;
+
+ //
+ // How many products are licensed vs unlicensed
+ //
+ ULONG LicensedProducts;
+
+ //
+ // Date when last replicated. Note: For SID records this is a pointer
+ // into the USER_RECORD for the appropriate user.
+ //
+ ULONG LastReplicated;
+
+ //
+ // Keep only a critical section lock, and not RTL_RESOURCE (for read/write
+ // locks). All updates of existing services (most common case by far) will
+ // be very quick, so exclusive access isn't that bad. RTL_RESOURCE also
+ // would make our table size increase dramatically and add too much extra
+ // processing.
+ //
+ RTL_CRITICAL_SECTION ServiceTableLock;
+
+ //
+ // Service table is a linear buffer, we use the service number to access
+ // into this buffer.
+ //
+ ULONG ServiceTableSize;
+
+ // Stuff per service - linear buffer...
+ PSVC_RECORD Services;
+
+ //
+ // Licenses the user is using
+ //
+ ULONG LicenseListSize;
+ PUSER_LICENSE_RECORD *LicenseList;
+} USER_RECORD, *PUSER_RECORD;
+
+
+/////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////
+
+extern ULONG UserListNumEntries;
+extern ULONG SidListNumEntries;
+extern RTL_GENERIC_TABLE UserList;
+extern RTL_GENERIC_TABLE SidList;
+
+extern RTL_RESOURCE UserListLock;
+extern RTL_RESOURCE SidListLock;
+
+//
+// The enum processes for replication and UI can take awhile to go through
+// all the records, while doing this they need a shared lock on the file.
+// However, if we request an exclusive access during this time the pending
+// exclusive access will block other shared access's. Therefore we get
+// this lock first before attempting either an add or enum.
+//
+// An add will block enums, but neither of these function are as time
+// critical as updating normal user records.
+//
+extern RTL_RESOURCE UserListAddEnumLock;
+extern RTL_RESOURCE SidListAddEnumLock;
+
+//
+// The AddCache itself, a critical section to protect access to it and an
+// event to signal the server when there are items on it that need to be
+// processed.
+//
+extern PADD_CACHE AddCache;
+extern ULONG AddCacheSize;
+extern RTL_CRITICAL_SECTION AddCacheLock;
+extern HANDLE LLSAddCacheEvent;
+
+extern DWORD LastUsedTime;
+extern BOOL UsersDeleted;
+
+
+/////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////
+
+PSVC_RECORD SvcListFind( LPTSTR DisplayName, PSVC_RECORD ServiceList, ULONG NumTableEntries );
+NTSTATUS SvcListDelete( LPTSTR UserName, LPTSTR ServiceName );
+VOID SvcListLicenseFree( PUSER_RECORD pUser );
+VOID SvcListLicenseUpdate( PUSER_RECORD pUser );
+
+VOID UserListInit();
+VOID UserListUpdate( ULONG DataType, PVOID Data, PSERVICE_RECORD Service );
+PUSER_RECORD UserListFind( LPTSTR UserName );
+
+VOID UserBackOfficeCheck( PUSER_RECORD pUser );
+
+VOID UserListLicenseDelete( PMASTER_SERVICE_RECORD Service, LONG Quantity );
+VOID UserLicenseListFree ( PUSER_RECORD pUser );
+
+VOID UserMappingAdd ( PMAPPING_RECORD Mapping, PUSER_RECORD pUser );
+VOID FamilyLicenseUpdate ( PMASTER_SERVICE_ROOT Family );
+VOID SvcLicenseUpdate( PUSER_RECORD pUser, PSVC_RECORD Svc );
+
+VOID MappingLicenseListFree ( PMAPPING_RECORD Mapping );
+VOID MappingLicenseUpdate ( PMAPPING_RECORD Mapping, BOOL ReSynch );
+
+
+/////////////////////////////////////////////////////////////////////////
+#if DBG
+
+VOID AddCacheDebugDump( );
+VOID UserListDebugDump( );
+VOID UserListDebugInfoDump( PVOID Data );
+VOID UserListDebugFlush( );
+VOID SidListDebugDump( );
+VOID SidListDebugInfoDump( PVOID Data );
+VOID SidListDebugFlush( );
+
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/private/net/svcdlls/lls/server/purchase.c b/private/net/svcdlls/lls/server/purchase.c
new file mode 100644
index 000000000..17720ac82
--- /dev/null
+++ b/private/net/svcdlls/lls/server/purchase.c
@@ -0,0 +1,865 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ purchase.c
+
+Abstract:
+
+
+Author:
+
+ Arthur Hanson (arth) 03-Jan-1995
+
+Revision History:
+
+ Jeff Parham (jeffparh) 05-Dec-1995
+ o Added support for uniting per seat and per server purchase models.
+ o Added extra parameters and code to support secure certificates and
+ certificate database.
+
+--*/
+
+#include <stdlib.h>
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windows.h>
+
+#include <debug.h>
+#include "llsapi.h"
+#include "llsutil.h"
+#include "llssrv.h"
+#include "mapping.h"
+#include "msvctbl.h"
+#include "purchase.h"
+#include "svctbl.h"
+#include "perseat.h"
+#include "registry.h"
+#include "llsrpc_s.h"
+#include "certdb.h"
+#include "server.h"
+
+
+ULONG LicenseServiceListSize = 0;
+PLICENSE_SERVICE_RECORD *LicenseServiceList = NULL;
+
+ULONG PerServerLicenseServiceListSize = 0;
+PLICENSE_SERVICE_RECORD *PerServerLicenseServiceList = NULL;
+
+PLICENSE_PURCHASE_RECORD PurchaseList = NULL;
+ULONG PurchaseListSize = 0;
+
+RTL_RESOURCE LicenseListLock;
+
+
+static
+NTSTATUS
+LicenseAdd_UpdateQuantity(
+ LPTSTR ServiceName,
+ LONG Quantity,
+ BOOL UsePerServerList,
+ PLICENSE_SERVICE_RECORD * ppService,
+ BOOL * pChangeLicense,
+ LONG * pNewLicenses,
+ PMASTER_SERVICE_RECORD * pmService
+ );
+
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+LicenseListInit()
+
+/*++
+
+Routine Description:
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ RtlInitializeResource(&LicenseListLock);
+
+} // LicenseListInit
+
+
+/////////////////////////////////////////////////////////////////////////
+int __cdecl LicenseServiceListCompare(const void *arg1, const void *arg2) {
+ PLICENSE_SERVICE_RECORD Svc1, Svc2;
+
+ Svc1 = (PLICENSE_SERVICE_RECORD) *((PLICENSE_SERVICE_RECORD *) arg1);
+ Svc2 = (PLICENSE_SERVICE_RECORD) *((PLICENSE_SERVICE_RECORD *) arg2);
+
+ return lstrcmpi( Svc1->ServiceName, Svc2->ServiceName);
+
+} // LicenseServiceListCompare
+
+
+/////////////////////////////////////////////////////////////////////////
+PLICENSE_SERVICE_RECORD
+LicenseServiceListFind(
+ LPTSTR ServiceName,
+ BOOL UsePerServerList
+ )
+
+/*++
+
+Routine Description:
+
+Arguments:
+
+ ServiceName -
+
+ (JeffParh 95-10-31)
+ UsePerServerList - Determines whether the license record is searched for
+ in the per seat list (as in 3.51) or in the per server list (new for
+ SUR). The license purchase models are now unified, so there is now
+ a purchase history for both per seat and per server licenses.
+
+Return Value:
+
+ Pointer to found service table entry or NULL if not found.
+
+--*/
+
+{
+ LONG begin = 0;
+ LONG end = (LONG) LicenseServiceListSize - 1;
+ LONG cur;
+ int match;
+ PLICENSE_SERVICE_RECORD Service;
+ PLICENSE_SERVICE_RECORD * ServiceList;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: LicenseServiceListFind\n"));
+#endif
+ if (ServiceName == NULL)
+ return NULL;
+
+ if ( UsePerServerList )
+ {
+ end = PerServerLicenseServiceListSize - 1;
+ ServiceList = PerServerLicenseServiceList;
+ }
+ else
+ {
+ end = LicenseServiceListSize - 1;
+ ServiceList = LicenseServiceList;
+ }
+
+ while (end >= begin) {
+ // go halfway in-between
+ cur = (begin + end) / 2;
+ Service = ServiceList[cur];
+
+ // compare the two result into match
+ match = lstrcmpi(ServiceName, Service->ServiceName);
+
+ if (match < 0)
+ // move new begin
+ end = cur - 1;
+ else
+ begin = cur + 1;
+
+ if (match == 0)
+ return Service;
+ }
+
+ return NULL;
+
+} // LicenseServiceListFind
+
+
+/////////////////////////////////////////////////////////////////////////
+PLICENSE_SERVICE_RECORD
+LicenseServiceListAdd(
+ LPTSTR ServiceName,
+ BOOL UsePerServerList
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+ ServiceName -
+
+ (JeffParh 95-10-31)
+ UsePerServerList - Determines whether the license record is added to
+ the per seat list (as in 3.51) or the per server list (new for
+ SUR). The license purchase models are now unified, so there is now
+ a purchase history for both per seat and per server licenses.
+
+Return Value:
+
+ Pointer to added service table entry, or NULL if failed.
+
+--*/
+
+{
+ PLICENSE_SERVICE_RECORD Service;
+ LPTSTR NewServiceName;
+ PLICENSE_SERVICE_RECORD ** pServiceList;
+ LPDWORD pServiceListSize;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: LicenseServiceListAdd\n"));
+#endif
+ //
+ // We do a double check here to see if another thread just got done
+ // adding the service, between when we checked last and actually got
+ // the write lock.
+ //
+ Service = LicenseServiceListFind(ServiceName, UsePerServerList);
+ if (Service != NULL) {
+ return Service;
+ }
+
+ if ( UsePerServerList )
+ {
+ pServiceList = &PerServerLicenseServiceList;
+ pServiceListSize = &PerServerLicenseServiceListSize;
+ }
+ else
+ {
+ pServiceList = &LicenseServiceList;
+ pServiceListSize = &LicenseServiceListSize;
+ }
+
+ //
+ // Allocate space for table (zero init it).
+ //
+ if (*pServiceList == NULL)
+ *pServiceList = (PLICENSE_SERVICE_RECORD *) LocalAlloc(LPTR, sizeof(PLICENSE_SERVICE_RECORD) * (*pServiceListSize + 1));
+ else
+ *pServiceList = (PLICENSE_SERVICE_RECORD *) LocalReAlloc(*pServiceList, sizeof(PLICENSE_SERVICE_RECORD) * (*pServiceListSize + 1), LHND);
+
+ //
+ // Make sure we could allocate service table
+ //
+ if (*pServiceList == NULL) {
+ ASSERT(FALSE);
+ *pServiceListSize = 0;
+ return NULL;
+ }
+
+ Service = (PLICENSE_SERVICE_RECORD) LocalAlloc(LPTR, sizeof(LICENSE_SERVICE_RECORD));
+ if (Service == NULL) {
+ ASSERT(FALSE);
+ return NULL;
+ }
+
+ //
+ // Create space for saving off the name.
+ //
+ NewServiceName = (LPTSTR) LocalAlloc(LPTR, (lstrlen(ServiceName) + 1) * sizeof(TCHAR));
+ if (NewServiceName == NULL) {
+ ASSERT(FALSE);
+ return NULL;
+ }
+
+ // now copy it over...
+ Service->ServiceName = NewServiceName;
+ lstrcpy(NewServiceName, ServiceName);
+
+ (*pServiceList)[*pServiceListSize] = Service;
+ Service->NumberLicenses = 0;
+ Service->Index = *pServiceListSize;
+ (*pServiceListSize)++;
+
+ // Have added the entry - now need to sort it in order of the service names
+ qsort((void *) *pServiceList, (size_t) *pServiceListSize, sizeof(PLICENSE_SERVICE_RECORD), LicenseServiceListCompare);
+
+ return Service;
+
+} // LicenseServiceListAdd
+
+
+/////////////////////////////////////////////////////////////////////////
+ULONG
+ProductLicensesGet(
+ LPTSTR ServiceName,
+ BOOL UsePerServerList
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+ ServiceName -
+
+ (JeffParh 95-10-31)
+ UsePerServerList - Determines whether the number of licenses is retirved
+ from the per seat list (as in 3.51) or the per server list (new for
+ SUR). The license purchase models are now unified, so there is now
+ a purchase history for both per seat and per server licenses.
+
+Return Value:
+
+
+--*/
+
+{
+ PLICENSE_SERVICE_RECORD Service;
+ ULONG NumLicenses = 0;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: ProductLicenseGet\n"));
+#endif
+
+ //
+ // Try to find the service.
+ //
+ RtlAcquireResourceShared(&LicenseListLock, TRUE);
+ Service = LicenseServiceListFind(ServiceName, UsePerServerList);
+ if (Service != NULL)
+ NumLicenses = Service->NumberLicenses;
+ RtlReleaseResource(&LicenseListLock);
+
+ return NumLicenses;
+} // ProductLicensesGet
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+LicenseAdd(
+ LPTSTR ServiceName,
+ LPTSTR Vendor,
+ LONG Quantity,
+ DWORD MaxQuantity,
+ LPTSTR Admin,
+ LPTSTR Comment,
+ DWORD Date,
+ DWORD AllowedModes,
+ DWORD CertificateID,
+ LPTSTR Source,
+ DWORD ExpirationDate,
+ LPDWORD Secrets
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ static DWORD NullSecrets[ LLS_NUM_SECRETS ] = { 0, 0, 0, 0 };
+
+ BOOL ChangeLicense = FALSE;
+ PLICENSE_SERVICE_RECORD Service = NULL;
+ PLICENSE_PURCHASE_RECORD PurchaseRecord;
+ LONG NewLicenses = 0;
+ NTSTATUS Status;
+ BOOL PerServerChangeLicense = FALSE;
+ PLICENSE_SERVICE_RECORD PerServerService = NULL;
+ LONG PerServerNewLicenses = 0;
+ LPTSTR NewName;
+ PMASTER_SERVICE_RECORD mService;
+ LLS_LICENSE_INFO_1 lic;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: LicenseAdd\n"));
+#endif
+
+ if ( ( 0 == CertificateID ) && ( ServiceIsSecure( ServiceName ) ) )
+ {
+ return STATUS_ACCESS_DENIED;
+ }
+
+ if ( ( 0 != ExpirationDate ) && ( ExpirationDate < DateSystemGet() ) )
+ {
+ // certificate has expired
+ return STATUS_ACCOUNT_EXPIRED;
+ }
+
+ if ( ( NULL == ServiceName )
+ || ( NULL == Vendor )
+ || ( 0 == Quantity )
+ || ( ( 0 != CertificateID ) && ( 0 == MaxQuantity ) )
+ || ( NULL == Admin )
+ || ( NULL == Comment )
+ || ( 0 == ( AllowedModes & ( LLS_LICENSE_MODE_ALLOW_PER_SEAT | LLS_LICENSE_MODE_ALLOW_PER_SERVER ) ) )
+ || ( NULL == Source ) )
+ {
+ // invalid parameter
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ if ( NULL == Secrets )
+ {
+ Secrets = NullSecrets;
+ }
+
+ RtlAcquireResourceExclusive(&LicenseListLock, TRUE);
+
+ if ( 0 != CertificateID )
+ {
+ lic.Product = ServiceName;
+ lic.Vendor = Vendor;
+ lic.Quantity = Quantity;
+ lic.MaxQuantity = MaxQuantity;
+ lic.Admin = Admin;
+ lic.Comment = Comment;
+ lic.Date = Date;
+ lic.AllowedModes = AllowedModes;
+ lic.CertificateID = CertificateID;
+ lic.Source = Source;
+ lic.ExpirationDate = ExpirationDate;
+ memcpy( lic.Secrets, Secrets, LLS_NUM_SECRETS * sizeof( *Secrets ) );
+
+ if ( !CertDbClaimApprove( &lic ) )
+ {
+ // no way, hoser!
+ RtlReleaseResource( &LicenseListLock );
+ return STATUS_OBJECT_NAME_EXISTS;
+ }
+ }
+
+ // update totals for per seat / per server mode licenses
+
+ Status = STATUS_SUCCESS;
+
+ if ( AllowedModes & 1 )
+ {
+ // per seat allowed; add to per seat license tally
+ Status = LicenseAdd_UpdateQuantity( ServiceName,
+ Quantity,
+ FALSE,
+ &Service,
+ &ChangeLicense,
+ &NewLicenses,
+ &mService );
+ }
+
+ if ( ( STATUS_SUCCESS == Status ) && ( AllowedModes & 2 ) )
+ {
+ // per server allowed; add to per server license tally
+ Status = LicenseAdd_UpdateQuantity( ServiceName,
+ Quantity,
+ TRUE,
+ &PerServerService,
+ &PerServerChangeLicense,
+ &PerServerNewLicenses,
+ &mService );
+ }
+
+ if ( STATUS_SUCCESS != Status )
+ {
+ RtlReleaseResource( &LicenseListLock );
+ return Status;
+ }
+
+ //
+ // Service now points to the service table entry - now update purchase
+ // History.
+ //
+ if (PurchaseList == NULL)
+ PurchaseList = (PLICENSE_PURCHASE_RECORD) LocalAlloc(LPTR, sizeof(LICENSE_PURCHASE_RECORD) * (PurchaseListSize + 1));
+ else
+ PurchaseList = (PLICENSE_PURCHASE_RECORD) LocalReAlloc(PurchaseList, sizeof(LICENSE_PURCHASE_RECORD) * (PurchaseListSize + 1), LHND);
+
+ //
+ // Make sure we could allocate service table
+ //
+ if (PurchaseList == NULL)
+ {
+ ASSERT(FALSE);
+ PurchaseListSize = 0;
+ RtlReleaseResource(&LicenseListLock);
+ return STATUS_NO_MEMORY;
+ }
+
+ PurchaseRecord = &PurchaseList[PurchaseListSize];
+
+ //
+ // Create space for saving off the Admin Name
+ //
+ NewName = (LPTSTR) LocalAlloc(LPTR, (lstrlen(Admin) + 1) * sizeof(TCHAR));
+ if (NewName == NULL)
+ {
+ ASSERT(FALSE);
+ RtlReleaseResource(&LicenseListLock);
+ return STATUS_NO_MEMORY;
+ }
+
+ // now copy it over...
+ PurchaseRecord->Admin = NewName;
+ lstrcpy(NewName, Admin);
+
+ //
+ // Create space for saving off the Comment
+ //
+ NewName = (LPTSTR) LocalAlloc(LPTR, (lstrlen(Comment) + 1) * sizeof(TCHAR));
+ if (NewName == NULL)
+ {
+ ASSERT(FALSE);
+ RtlReleaseResource(&LicenseListLock);
+ return STATUS_NO_MEMORY;
+ }
+
+ // now copy it over...
+ PurchaseRecord->Comment = NewName;
+ lstrcpy(NewName, Comment);
+
+ //
+ // Create space for saving off the Source
+ //
+ NewName = (LPTSTR) LocalAlloc(LPTR, (lstrlen(Source) + 1) * sizeof(TCHAR));
+ if (NewName == NULL)
+ {
+ ASSERT(FALSE);
+ RtlReleaseResource(&LicenseListLock);
+ return STATUS_NO_MEMORY;
+ }
+
+ // now copy it over...
+ PurchaseRecord->Source = NewName;
+ lstrcpy(NewName, Source);
+
+ //
+ // Create space for saving off the Vendor
+ //
+ NewName = (LPTSTR) LocalAlloc(LPTR, (lstrlen(Vendor) + 1) * sizeof(TCHAR));
+ if (NewName == NULL)
+ {
+ ASSERT(FALSE);
+ RtlReleaseResource(&LicenseListLock);
+ return STATUS_NO_MEMORY;
+ }
+
+ // now copy it over...
+ PurchaseRecord->Vendor = NewName;
+ lstrcpy(NewName, Vendor);
+
+ //
+ // Update the rest of the stuff.
+ //
+ PurchaseRecord->NumberLicenses = Quantity;
+ PurchaseRecord->MaxQuantity = MaxQuantity;
+ PurchaseRecord->Service = Service;
+ PurchaseRecord->PerServerService = PerServerService;
+ PurchaseRecord->AllowedModes = AllowedModes;
+ PurchaseRecord->CertificateID = CertificateID;
+ PurchaseRecord->ExpirationDate = ExpirationDate;
+ memcpy( PurchaseRecord->Secrets, Secrets, LLS_NUM_SECRETS * sizeof( *Secrets ) );
+
+ if (Date == 0)
+ PurchaseRecord->Date = DateSystemGet();
+ else
+ PurchaseRecord->Date = Date;
+
+ PurchaseListSize++;
+
+ RtlReleaseResource(&LicenseListLock);
+
+ if ( 0 != CertificateID )
+ {
+ // these should still be set from above
+ ASSERT( lic.Product == ServiceName );
+ ASSERT( lic.Vendor == Vendor );
+ ASSERT( lic.Quantity == Quantity );
+ ASSERT( lic.MaxQuantity == MaxQuantity );
+ ASSERT( lic.Admin == Admin );
+ ASSERT( lic.Comment == Comment );
+ ASSERT( lic.Date == Date );
+ ASSERT( lic.AllowedModes == AllowedModes );
+ ASSERT( lic.CertificateID == CertificateID );
+ ASSERT( lic.Source == Source );
+ ASSERT( lic.ExpirationDate == ExpirationDate );
+ ASSERT( 0 == memcmp( PurchaseRecord->Secrets, Secrets, LLS_NUM_SECRETS * sizeof( *Secrets ) ) );
+
+ CertDbClaimEnter( NULL, &lic, FALSE, 0 );
+ }
+
+ //
+ // Now check if we need to re-scan the user list and update licenses
+ //
+ if (ChangeLicense)
+ {
+ if ( NewLicenses < 0 )
+ UserListLicenseDelete( mService, NewLicenses );
+
+ if ( NewLicenses > 0 )
+ FamilyLicenseUpdate ( mService->Family );
+ }
+
+ if ( AllowedModes & 2 )
+ {
+ // per server licenses modified
+ LocalServiceListConcurrentLimitSet();
+ LocalServerServiceListUpdate();
+ ServiceListResynch();
+ }
+
+ return STATUS_SUCCESS;
+
+} // LicenseAdd
+
+
+/////////////////////////////////////////////////////////////////////////
+static
+NTSTATUS
+LicenseAdd_UpdateQuantity(
+ LPTSTR ServiceName,
+ LONG Quantity,
+ BOOL UsePerServerList,
+ PLICENSE_SERVICE_RECORD * ppService,
+ BOOL * pChangeLicense,
+ LONG * pNewLicenses,
+ PMASTER_SERVICE_RECORD * pmService
+ )
+{
+ BOOL ChangeLicense = FALSE;
+ PLICENSE_SERVICE_RECORD Service;
+ PLICENSE_PURCHASE_RECORD PurchaseRecord;
+ PMASTER_SERVICE_RECORD mService;
+ LONG NewLicenses = 0;
+
+ Service = LicenseServiceListFind( ServiceName, UsePerServerList );
+
+ //
+ // If we didn't find it we will need to add it.
+ //
+ if (Service == NULL)
+ {
+ if (Quantity < 0)
+ {
+#if DBG
+ dprintf(TEXT("Releasing Licenses from Non-existant product!\n"));
+#endif
+ // ASSERT(FALSE);
+ return STATUS_UNSUCCESSFUL;
+ }
+ else
+ {
+ Service = LicenseServiceListAdd(ServiceName, UsePerServerList);
+ }
+ }
+
+ //
+ // Check to make sure we found or added it. The only way we can fail is
+ // if we couldn't alloc memory for it.
+ //
+ if (Service == NULL)
+ {
+ ASSERT(FALSE);
+ return STATUS_NO_MEMORY;
+ }
+
+ if (((LONG) Service->NumberLicenses + Quantity) < 0)
+ {
+ return STATUS_UNSUCCESSFUL;
+ }
+
+ //
+ // Update license count in service record
+ //
+ Service->NumberLicenses += Quantity;
+
+ //
+ // Now in master Service Record
+ //
+ RtlAcquireResourceShared(&MasterServiceListLock, TRUE);
+ mService = MasterServiceListFind(ServiceName);
+
+ if (mService != NULL)
+ {
+ //
+ // if we were out of license and added more, then we need to update
+ // the user list.
+ //
+ if ( ( Quantity > 0 )
+ && ( (mService->LicensesUsed > mService->Licenses) || (mService == BackOffice) ) )
+ {
+ ChangeLicense = TRUE;
+ }
+
+ //
+ // Can only add a number of licenses up to licensed amount
+ //
+ if ( ChangeLicense )
+ {
+ // Get current unlicensed delta
+ NewLicenses = mService->LicensesUsed - mService->Licenses;
+
+ if ((NewLicenses <= 0) || (NewLicenses > Quantity))
+ {
+ NewLicenses = Quantity;
+ }
+ }
+
+ if ( UsePerServerList )
+ {
+ // this will be done by LicenseAdd() in LocalServerServiceListUpdate()
+ // mService->MaxSessionCount += Quantity;
+ }
+ else
+ {
+ mService->Licenses += Quantity;
+ }
+
+ //
+ // if we we subtracted licenses and are out of licenses, then we
+ // need to scan the user list.
+ //
+ if (Quantity < 0)
+ {
+ if (mService->LicensesUsed > mService->Licenses)
+ {
+ ChangeLicense = TRUE;
+
+ //
+ // Only remove # of licenses past limit
+ //
+ NewLicenses = mService->Licenses - mService->LicensesUsed;
+ if (NewLicenses < Quantity)
+ {
+ NewLicenses = Quantity;
+ }
+ }
+ }
+ }
+
+ RtlReleaseResource(&MasterServiceListLock);
+
+ *ppService = Service;
+ *pChangeLicense = ChangeLicense;
+ *pNewLicenses = NewLicenses;
+ *pmService = mService;
+
+ return STATUS_SUCCESS;
+}
+
+#if DBG
+/////////////////////////////////////////////////////////////////////////
+VOID
+LicenseListDebugDump( )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ ULONG i = 0;
+ ULONG j = 0;
+
+ //
+ // Need to scan list so get read access.
+ //
+ RtlAcquireResourceShared(&LicenseListLock, TRUE);
+
+ //
+ // Dump License Service List first
+ //
+ dprintf(TEXT("Per Seat License Service Table, # Entries: %lu\n"), LicenseServiceListSize);
+ if (LicenseServiceList != NULL)
+ {
+ for (i = 0; i < LicenseServiceListSize; i++)
+ dprintf(TEXT(" %2lu) Tot Licenses: %lu Product: %s\n"), i, LicenseServiceList[i]->NumberLicenses, LicenseServiceList[i]->ServiceName);
+ }
+
+ dprintf(TEXT("\nPer Server License Service Table, # Entries: %lu\n"), PerServerLicenseServiceListSize);
+ if (PerServerLicenseServiceList != NULL)
+ {
+ for (i = 0; i < PerServerLicenseServiceListSize; i++)
+ dprintf(TEXT(" %2lu) Tot Licenses: %lu Product: %s\n"), i, PerServerLicenseServiceList[i]->NumberLicenses, PerServerLicenseServiceList[i]->ServiceName);
+ }
+
+ //
+ // Now do purchase history
+ //
+ dprintf(TEXT("\nPurchase History, # Entries: %lu\n"), PurchaseListSize);
+ if (PurchaseList != NULL)
+ {
+ for (i = 0; i < PurchaseListSize; i++)
+ {
+ TCHAR szExpirationDate[ 40 ];
+
+ lstrcpy( szExpirationDate, TimeToString( PurchaseList[i].ExpirationDate ) );
+
+ dprintf( TEXT(" %3lu) Product : %s\n" )
+ TEXT( " Vendor : %s\n" )
+ TEXT( " Allowed Modes :%s%s\n" )
+ TEXT( " Licenses : %d\n" )
+ TEXT( " Max Licenses : %lu\n" )
+ TEXT( " Date Entered : %s\n" )
+ TEXT( " Date Expires : %s\n" )
+ TEXT( " Certificate ID : %lu\n" )
+ TEXT( " Secrets :" ),
+ i,
+ ( PurchaseList[i].AllowedModes & LLS_LICENSE_MODE_ALLOW_PER_SEAT )
+ ? PurchaseList[i].Service->ServiceName
+ : PurchaseList[i].PerServerService->ServiceName,
+ PurchaseList[i].Vendor,
+ ( PurchaseList[i].AllowedModes & LLS_LICENSE_MODE_ALLOW_PER_SEAT )
+ ? TEXT(" PERSEAT")
+ : TEXT(""),
+ ( PurchaseList[i].AllowedModes & LLS_LICENSE_MODE_ALLOW_PER_SERVER )
+ ? TEXT(" PERSERVER")
+ : TEXT(""),
+ PurchaseList[i].NumberLicenses,
+ PurchaseList[i].MaxQuantity,
+ TimeToString( PurchaseList[i].Date ),
+ szExpirationDate,
+ PurchaseList[i].CertificateID
+ );
+
+ for ( j=0; j < LLS_NUM_SECRETS; j++ )
+ {
+ dprintf( TEXT( " %08X" ), PurchaseList[i].Secrets[j] );
+ }
+
+ dprintf( TEXT( "\n" )
+ TEXT( " Source : %s\n" )
+ TEXT( " Admin : %s\n" )
+ TEXT( " Comment : %s\n\n" ),
+ PurchaseList[i].Source,
+ PurchaseList[i].Admin,
+ PurchaseList[i].Comment
+ );
+ }
+ }
+
+LicenseListDebugDumpExit:
+ RtlReleaseResource(&LicenseListLock);
+
+ return;
+} // LicenseListDebugDump
+
+#endif
diff --git a/private/net/svcdlls/lls/server/purchase.h b/private/net/svcdlls/lls/server/purchase.h
new file mode 100644
index 000000000..1e18b4879
--- /dev/null
+++ b/private/net/svcdlls/lls/server/purchase.h
@@ -0,0 +1,114 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ Purchase.h
+
+Abstract:
+
+
+Author:
+
+ Arthur Hanson (arth) Dec 07, 1994
+
+Environment:
+
+Revision History:
+
+ Jeff Parham (jeffparh) 05-Dec-1995
+ o Added support for uniting per seat and per server purchase models.
+ o Added extra parameters and code to support secure certificates and
+ certificate database.
+
+--*/
+
+#ifndef _LLS_PURCHASE_H
+#define _LLS_PURCHASE_H
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+typedef struct _LICENSE_SERVICE_RECORD {
+ LPTSTR ServiceName;
+ ULONG Index;
+ LONG NumberLicenses;
+} LICENSE_SERVICE_RECORD, *PLICENSE_SERVICE_RECORD;
+
+
+typedef struct _LICENSE_PURCHASE_RECORD {
+ PLICENSE_SERVICE_RECORD Service;
+ LONG NumberLicenses;
+ DWORD Date;
+ LPTSTR Admin;
+ LPTSTR Comment;
+
+ // added for SUR:
+ PLICENSE_SERVICE_RECORD PerServerService; // points to per server
+ // license tally for this
+ // service
+
+ DWORD AllowedModes; // bit field: 1, allowed
+ // to be used in per seat
+ // mode; 2, per server
+
+ DWORD CertificateID; // identifies the secure
+ // certificate from which
+ // these licenses came, or
+ // 0 if unsecure
+
+ LPTSTR Source; // source of the certificate
+ // currently supported
+ // values are "None" and
+ // "Paper"
+
+ DWORD ExpirationDate; // time at which this
+ // certificate expires
+
+ DWORD MaxQuantity; // the largest number of licenses
+ // that can be installed from this
+ // certificate
+
+ LPTSTR Vendor; // vendor of the product, e.g.,
+ // "Microsoft"
+
+ DWORD Secrets[ LLS_NUM_SECRETS ]; // secrets for LSAPI
+ // challenge mechanism
+
+} LICENSE_PURCHASE_RECORD, *PLICENSE_PURCHASE_RECORD;
+
+
+
+extern ULONG LicenseServiceListSize;
+extern PLICENSE_SERVICE_RECORD *LicenseServiceList;
+
+extern ULONG PerServerLicenseServiceListSize;
+extern PLICENSE_SERVICE_RECORD *PerServerLicenseServiceList;
+
+extern PLICENSE_PURCHASE_RECORD PurchaseList;
+extern ULONG PurchaseListSize;
+
+extern RTL_RESOURCE LicenseListLock;
+
+
+VOID LicenseListInit();
+PLICENSE_SERVICE_RECORD LicenseServiceListFind( LPTSTR ServiceName, BOOL UsePerServerList );
+PLICENSE_SERVICE_RECORD LicenseServiceListAdd( LPTSTR ServiceName, BOOL UsePerServerList );
+ULONG ProductLicensesGet( LPTSTR ServiceName, BOOL UsePerServerList );
+NTSTATUS LicenseAdd( LPTSTR ServiceName, LPTSTR Vendor, LONG Quantity, DWORD MaxQuantity, LPTSTR Admin, LPTSTR Comment, DWORD Date, DWORD AllowedModes, DWORD CertificateID, LPTSTR Source, DWORD ExpirationDate, LPDWORD Secrets );
+
+
+#if DBG
+VOID LicenseListDebugDump( );
+#endif
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/private/net/svcdlls/lls/server/registry.c b/private/net/svcdlls/lls/server/registry.c
new file mode 100644
index 000000000..622019b26
--- /dev/null
+++ b/private/net/svcdlls/lls/server/registry.c
@@ -0,0 +1,1707 @@
+/*++
+
+Copyright (c) 1994 Microsoft Corporation
+
+Module Name:
+
+ registry.c
+
+Abstract:
+
+ Registry reading routines for License Server. Can Scan the registry
+ for all License Service entries, or for a specific service.
+
+Author:
+
+ Arthur Hanson (arth) 07-Dec-1994
+
+Revision History:
+
+ Jeff Parham (jeffparh) 05-Dec-1995
+ o Removed unnecessary RegConnect() to local server.
+ o Added secure service list. This list tracks the products that
+ require "secure" license certificates for all licenses; i.e., the
+ products that do not accept the 3.51 Honesty method of "enter the
+ number of license you purchased."
+ o Added routine to update the concurrent limit value in the registry
+ to accurately reflect the connection limit of secure products.
+
+--*/
+
+#include <stdlib.h>
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windows.h>
+#include <rpc.h>
+#include <rpcndr.h>
+
+#include "llsapi.h"
+#include "debug.h"
+#include "llssrv.h"
+#include "registry.h"
+#include "ntlsapi.h"
+#include "mapping.h"
+#include "msvctbl.h"
+#include "svctbl.h"
+#include "purchase.h"
+#include "perseat.h"
+#include "server.h"
+#include "llsutil.h"
+
+// #define API_TRACE 1
+
+#define NUM_MAPPING_ENTRIES 2
+LPTSTR NameMappingTable[] = {
+ TEXT("SQL"),
+ TEXT("SNA")
+}; // NameMappingTable
+
+
+LPTSTR NameMappingTable2[] = {
+ TEXT("Microsoft SQL Server"),
+ TEXT("Microsoft SNA Server")
+}; // NameMappingTable2
+
+
+ULONG NumFilePrintEntries = 0;
+LPTSTR *FilePrintTable = NULL;
+
+#define KEY_NAME_SIZE 512
+
+HANDLE LLSRegistryEvent;
+
+
+ULONG LocalServiceListSize = 0;
+PLOCAL_SERVICE_RECORD *LocalServiceList = NULL;
+
+RTL_RESOURCE LocalServiceListLock;
+
+ULONG SecureServiceListSize = 0;
+LPTSTR * SecureServiceList = NULL;
+ULONG SecureServiceBufferSize = 0; // in bytes!
+TCHAR * SecureServiceBuffer = NULL;
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+ConfigInfoRegistryInit(
+ DWORD * pUseEnterprise,
+ LPTSTR pEnterpriseServer,
+ DWORD * pReplicationType,
+ DWORD * pReplicationTime,
+ DWORD * pLogLevel
+ )
+{
+ HKEY hKey2 = NULL;
+ DWORD dwType, dwSize;
+ static BOOL ReportedError = FALSE;
+ static TCHAR RegKeyText[512];
+ LONG Status;
+ BOOL ret = FALSE;
+ DWORD UseEnterprise, ReplicationType, ReplicationTime;
+ static TCHAR EnterpriseServer[MAX_COMPUTERNAME_LENGTH + 3];
+ DWORD LogLevel;
+
+ lstrcpy(EnterpriseServer, TEXT(""));
+ UseEnterprise = ReplicationType = ReplicationTime = LogLevel = 0;
+
+ //
+ // Create registry key-name we are looking for
+ //
+ lstrcpy(RegKeyText, TEXT("System\\CurrentControlSet\\Services\\LicenseService\\Parameters"));
+
+ if ((Status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, RegKeyText, 0, KEY_READ, &hKey2)) == ERROR_SUCCESS)
+ {
+ dwSize = sizeof(UseEnterprise);
+ Status = RegQueryValueEx(hKey2, TEXT("UseEnterprise"), NULL, &dwType, (LPBYTE) &UseEnterprise, &dwSize);
+ if (Status == ERROR_SUCCESS)
+ *pUseEnterprise = UseEnterprise;
+ else
+ UseEnterprise = 0;
+
+
+ if (Status == ERROR_SUCCESS)
+ {
+ dwSize = sizeof(EnterpriseServer);
+ Status = RegQueryValueEx(hKey2, TEXT("EnterpriseServer"), NULL, &dwType, (LPBYTE) &EnterpriseServer, &dwSize);
+
+ if (Status == ERROR_SUCCESS)
+ {
+ lstrcpy(pEnterpriseServer, EnterpriseServer);
+ }
+ else
+ {
+ lstrcpy(pEnterpriseServer, TEXT(""));
+
+ if (!ReportedError)
+ {
+ ReportedError = TRUE;
+#ifdef DEBUG
+ dprintf(TEXT("LLS: (WARNING) No registry parm for EnterpriseServer\n"));
+#endif
+ }
+ }
+
+ }
+ else
+ {
+ if (!ReportedError)
+ {
+ ReportedError = TRUE;
+#ifdef DEBUG
+ dprintf(TEXT("LLS: (WARNING) No registry parm for UseEnterprise\n"));
+#endif
+ }
+ }
+
+ dwSize = sizeof(ReplicationType);
+ Status = RegQueryValueEx(hKey2, TEXT("ReplicationType"), NULL, &dwType, (LPBYTE) &ReplicationType, &dwSize);
+
+ if (Status == ERROR_SUCCESS)
+ {
+ dwSize = sizeof(ReplicationTime);
+ Status = RegQueryValueEx(hKey2, TEXT("ReplicationTime"), NULL, &dwType, (LPBYTE) &ReplicationTime, &dwSize);
+
+ if (Status == ERROR_SUCCESS)
+ {
+ *pReplicationType = ReplicationType;
+ *pReplicationTime = ReplicationTime;
+ }
+ else
+ {
+ if (!ReportedError)
+ {
+ ReportedError = TRUE;
+#ifdef DEBUG
+ dprintf(TEXT("LLS: (WARNING) No registry parm for ReplicationTime\n"));
+#endif
+ }
+ }
+
+ }
+ else
+ {
+ if (!ReportedError)
+ {
+ ReportedError = TRUE;
+#ifdef DEBUG
+ dprintf(TEXT("LLS: (WARNING) No registry parm for ReplicationType\n"));
+#endif
+ }
+ }
+
+ // LogLevel (REG_DWORD): determines how much info is dumped to the EventLog.
+ // Higher values imply more logging. Default: 0.
+ dwSize = sizeof( LogLevel );
+ Status = RegQueryValueEx( hKey2, TEXT("LogLevel"), NULL, &dwType, (LPBYTE) &LogLevel, &dwSize);
+ if ( ERROR_SUCCESS == Status )
+ *pLogLevel = LogLevel;
+ else
+ *pLogLevel = 0;
+
+ // ProductData (REG_BINARY): an encrypted buffer of concatenated service names
+ // that determine which services need to have secure certificates
+ // for license entry
+ Status = RegQueryValueEx( hKey2, TEXT("ProductData"), NULL, &dwType, NULL, &dwSize );
+ if ( ERROR_SUCCESS == Status )
+ {
+ TCHAR * NewSecureServiceBuffer = NULL;
+ LPTSTR * NewSecureServiceList = NULL;
+ ULONG NewSecureServiceListSize = 0;
+ ULONG NewSecureServiceBufferSize;
+
+ NewSecureServiceBufferSize = dwSize;
+ NewSecureServiceBuffer = LocalAlloc( LMEM_FIXED, NewSecureServiceBufferSize );
+
+ if ( NULL != NewSecureServiceBuffer )
+ {
+ Status = RegQueryValueEx( hKey2, TEXT("ProductData"), NULL, &dwType, (LPBYTE) NewSecureServiceBuffer, &dwSize);
+
+ if ( ERROR_SUCCESS == Status )
+ {
+ Status = DeBlock( NewSecureServiceBuffer, dwSize );
+
+ if ( ( STATUS_SUCCESS == Status )
+ && ( ( NULL == SecureServiceBuffer )
+ || ( memcmp( NewSecureServiceBuffer, SecureServiceBuffer, dwSize ) ) ) )
+ {
+ // process changes in secure product list
+ DWORD i;
+ DWORD ProductNdx;
+
+ NewSecureServiceListSize = 0;
+
+ // count number of product names contained in the buffer
+ for ( i=0; ( i < dwSize ) && ( NewSecureServiceBuffer[i] != TEXT( '\0' ) ); i++ )
+ {
+ // skip to beginning of next product name
+ for ( ; ( i < dwSize ) && ( NewSecureServiceBuffer[i] != TEXT( '\0' ) ); i++ );
+ i++;
+
+ if ( i * sizeof( TCHAR) < dwSize )
+ {
+ // properly null-terminated product name
+ NewSecureServiceListSize++;
+ }
+ }
+
+ if ( 0 != NewSecureServiceListSize )
+ {
+ NewSecureServiceList = LocalAlloc( LMEM_FIXED, sizeof( LPTSTR ) * NewSecureServiceListSize );
+
+ if ( NULL != NewSecureServiceList )
+ {
+ for ( i = ProductNdx = 0; ProductNdx < NewSecureServiceListSize; ProductNdx++ )
+ {
+ NewSecureServiceList[ ProductNdx ] = &NewSecureServiceBuffer[i];
+
+ // skip to beginning of next product name
+ for ( ; NewSecureServiceBuffer[i] != TEXT( '\0' ); i++ );
+ i++;
+ }
+
+ // new secure product list read successfully; use it
+ if ( NULL != SecureServiceBuffer )
+ {
+ LocalFree( SecureServiceBuffer );
+ }
+ if ( NULL != SecureServiceList )
+ {
+ LocalFree( SecureServiceList );
+ }
+
+ SecureServiceBuffer = NewSecureServiceBuffer;
+ SecureServiceList = NewSecureServiceList;
+ SecureServiceListSize = NewSecureServiceListSize;
+ SecureServiceBufferSize = NewSecureServiceBufferSize;
+ }
+ }
+ }
+ }
+ }
+
+ // free buffers if we aren't using them anymore
+ if ( ( NULL != NewSecureServiceList )
+ && ( SecureServiceList != NewSecureServiceList ) )
+ {
+ LocalFree( NewSecureServiceList );
+ }
+
+ if ( ( NULL != NewSecureServiceBuffer )
+ && ( SecureServiceBuffer != NewSecureServiceBuffer ) )
+ {
+ LocalFree( NewSecureServiceBuffer );
+ }
+ }
+
+ RegCloseKey(hKey2);
+ }
+
+} // ConfigInfoRegistryInit
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+FilePrintTableInit(
+ )
+
+/*++
+
+Routine Description:
+
+ Builds up the FilePrint mapping table by enumerating the keys in the
+ registry init'd by the various install programs.
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ HKEY hKey2;
+ static TCHAR RegKeyText[512];
+ static TCHAR KeyText[KEY_NAME_SIZE], ClassText[KEY_NAME_SIZE];
+ LONG Status;
+ DWORD index = 0;
+ DWORD KeySize, ClassSize, NumKeys, NumValue, MaxKey, MaxClass, MaxValue, MaxValueData, MaxSD;
+ FILETIME LastWrite;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: FilePrintTableInit\n"));
+#endif
+ //
+ // Create registry key-name we are looking for
+ //
+ lstrcpy(RegKeyText, TEXT("System\\CurrentControlSet\\Services\\LicenseService\\FilePrint"));
+
+ if ((Status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, RegKeyText, 0, KEY_READ, &hKey2)) == ERROR_SUCCESS) {
+ //
+ // Find out how many sub-keys there are to intialize our table size.
+ // The table can still grow dynamically, this just makes having to
+ // realloc it a rare occurance.
+ //
+ ClassSize = KEY_NAME_SIZE;
+ Status = RegQueryInfoKey(hKey2, ClassText, &ClassSize, NULL,
+ &NumKeys, &MaxKey, &MaxClass, &NumValue,
+ &MaxValue, &MaxValueData, &MaxSD, &LastWrite);
+
+ if (Status == ERROR_SUCCESS) {
+ FilePrintTable = (LPTSTR *) LocalAlloc(LPTR, sizeof(LPTSTR) * NumKeys);
+
+ while ((Status == ERROR_SUCCESS) && (FilePrintTable != NULL)) {
+ //
+ // Double check in-case we need to expand the table.
+ //
+ if (index > NumKeys) {
+ NumKeys++;
+ FilePrintTable = (LPTSTR *) LocalReAlloc(FilePrintTable, sizeof(LPTSTR) * NumKeys, LHND);
+ }
+
+ if (FilePrintTable != NULL) {
+ //
+ // Now read in the key name and add it to the table
+ //
+ KeySize = KEY_NAME_SIZE;
+ Status = RegEnumKeyEx(hKey2, index, KeyText, &KeySize, NULL, NULL, NULL, &LastWrite);
+ if (Status == ERROR_SUCCESS) {
+ //
+ // Allocate space in our table and copy the key
+ //
+ FilePrintTable[index] = (LPTSTR) LocalAlloc(LPTR, (KeySize + 1) * sizeof(TCHAR));
+
+ if (FilePrintTable[index] != NULL) {
+ lstrcpy(FilePrintTable[index], KeyText);
+ index++;
+ } else
+ Status = (LONG) GetLastError();
+
+ }
+
+ }
+ }
+ }
+#ifdef DEBUG
+ else {
+ dprintf(TEXT("LLS FilePrintTable Error: 0x%lx\n"), Status);
+ }
+#endif
+
+ RegCloseKey( hKey2 );
+ }
+
+ if (FilePrintTable != NULL)
+ NumFilePrintEntries = index;
+ else
+ NumFilePrintEntries = 0;
+
+} // FilePrintTableInit
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+RegistryMonitor (
+ IN PVOID ThreadParameter
+ )
+
+/*++
+
+Routine Description:
+
+ Watches for any changes in the Licensing Keys, and if any updates our
+ internal information.
+
+Arguments:
+
+ ThreadParameter - Indicates how many active threads there currently
+ are.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ LONG Status = 0;
+ HKEY hKey2 = NULL;
+ NTSTATUS NtStatus = STATUS_SUCCESS;
+ static TCHAR RegKeyText[KEY_NAME_SIZE];
+
+ //
+ // Create registry key-name we are looking for
+ //
+ lstrcpy(RegKeyText, TEXT("System\\CurrentControlSet\\Services"));
+
+ //
+ // Registry is already open so just find the correct keys.
+ //
+ if ((Status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, RegKeyText, 0, KEY_ALL_ACCESS, &hKey2)) != ERROR_SUCCESS) {
+#if DBG
+ dprintf(TEXT("LLS RegistryMonitor - RegOpenKeyEx failed: 0x%lX\n"), Status);
+#endif
+ return (NTSTATUS) Status;
+ }
+
+ //
+ // Loop forever
+ //
+ for ( ; ; ) {
+
+ Status = RegNotifyChangeKeyValue(hKey2, TRUE, REG_NOTIFY_CHANGE_LAST_SET, LLSRegistryEvent, TRUE);
+ if (Status != ERROR_SUCCESS) {
+#if DBG
+ dprintf(TEXT("LLS RegNotifyChangeKeyValue Failed: %lu\n"), Status);
+#endif
+ }
+
+ NtStatus = NtWaitForSingleObject( LLSRegistryEvent, TRUE, NULL );
+
+ //
+ // Re-synch the lists
+ //
+ LocalServiceListUpdate();
+ LocalServerServiceListUpdate();
+ ServiceListResynch();
+ ConfigInfoRegistryUpdate();
+ LocalServiceListConcurrentLimitSet();
+
+ if (NtStatus != STATUS_SUCCESS) {
+#if DBG
+ dprintf(TEXT("LLS Registry Event Notification Failed: %lu\n"), NtStatus);
+#endif
+ //
+ // If we failed - sleep for 2 minutes before looping
+ //
+ Sleep(120000L);
+ }
+ }
+
+ return NtStatus;
+
+} // RegistryMonitor
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+RegistryInit( )
+
+/*++
+
+Routine Description:
+
+ Looks in registry for given service and sets values accordingly.
+
+Arguments:
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ static TCHAR RegKeyText[512];
+ BOOL ret = FALSE;
+ DWORD Mode, ConcurrentLimit;
+
+ Mode = 0;
+ ConcurrentLimit = 0;
+
+ //
+ // Create a key to tell us about any changes in the registry
+ //
+ Status = NtCreateEvent(
+ &LLSRegistryEvent,
+ EVENT_QUERY_STATE | EVENT_MODIFY_STATE | SYNCHRONIZE,
+ NULL,
+ SynchronizationEvent,
+ FALSE
+ );
+
+ ASSERT(NT_SUCCESS(Status));
+
+ //
+ // Create the FilePrint table
+ //
+ FilePrintTableInit();
+
+} // RegistryInit
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+RegistryStartMonitor( )
+
+/*++
+
+Routine Description:
+
+ Looks in registry for given service and sets values accordingly.
+
+Arguments:
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ HANDLE Thread;
+ DWORD Ignore;
+
+ //
+ // Now dispatch a thread to watch for any registry changes
+ //
+ Thread = CreateThread(
+ NULL,
+ 0L,
+ (LPTHREAD_START_ROUTINE) RegistryMonitor,
+ 0L,
+ 0L,
+ &Ignore
+ );
+
+
+} // RegistryStartMonitor
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+RegistryInitValues(
+ LPTSTR ServiceName,
+ BOOL *PerSeatLicensing,
+ ULONG *SessionLimit
+ )
+
+/*++
+
+Routine Description:
+
+ Looks in registry for given service and sets values accordingly.
+
+Arguments:
+
+ Service Name -
+
+ PerSeatLicensing -
+
+ SessionLimit -
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ HKEY hKey2 = NULL;
+ DWORD dwType, dwSize;
+ static TCHAR RegKeyText[512];
+ LONG Status;
+ DWORD Mode, ConcurrentLimit;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: RegistryInitValues\n"));
+#endif
+
+#ifdef SPECIAL_USER_LIMIT
+ *PerSeatLicensing = FALSE;
+ *SessionLimit = SPECIAL_USER_LIMIT;
+#else // #ifdef SPECIAL_USER_LIMIT
+ Mode = 0;
+ ConcurrentLimit = 0;
+
+ //
+ // Create registry key-name we are looking for
+ //
+ lstrcpy(RegKeyText, TEXT("System\\CurrentControlSet\\Services\\LicenseInfo\\"));
+ lstrcat(RegKeyText, ServiceName);
+
+ if ((Status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, RegKeyText, 0, KEY_READ, &hKey2)) == ERROR_SUCCESS) {
+ //
+ // First Get Mode
+ //
+ dwSize = sizeof(Mode);
+ Status = RegQueryValueEx(hKey2, TEXT("Mode"), NULL, &dwType, (LPBYTE) &Mode, &dwSize);
+
+#if DBG
+ if ((TraceFlags & TRACE_REGISTRY) && (Status == ERROR_SUCCESS))
+ dprintf(TEXT("Found Reg-Key for [%s] Mode: %ld\n"), ServiceName, Mode);
+#endif
+ //
+ // Now Concurrent Limit
+ //
+ dwSize = sizeof(ConcurrentLimit);
+ Status = RegQueryValueEx(hKey2, TEXT("ConcurrentLimit"), NULL, &dwType, (LPBYTE) &ConcurrentLimit, &dwSize);
+
+#if DBG
+ if ((TraceFlags & TRACE_REGISTRY) && (Status == ERROR_SUCCESS))
+ dprintf(TEXT("Found Reg-Key for [%s] ConcurrentLimit: %ld\n"), ServiceName, ConcurrentLimit);
+#endif
+ RegCloseKey(hKey2);
+
+ }
+
+
+ if (Mode == 0) {
+ *PerSeatLicensing = TRUE;
+ *SessionLimit = 0;
+ } else {
+ *PerSeatLicensing = FALSE;
+ *SessionLimit = ConcurrentLimit;
+ }
+#endif // #else // #ifdef SPECIAL_USER_LIMIT
+} // RegistryInitValues
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+RegistryDisplayNameGet(
+ LPTSTR ServiceName,
+ LPTSTR DefaultName,
+ LPTSTR *pDisplayName
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+ Service Name -
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ HKEY hKey2 = NULL;
+ DWORD dwType, dwSize;
+ static TCHAR RegKeyText[512];
+ static TCHAR DisplayName[512];
+ LONG Status;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: RegistryDisplayNameGet\n"));
+#endif
+
+ lstrcpy(DisplayName, DefaultName);
+
+ //
+ // Create registry key-name we are looking for
+ //
+ lstrcpy(RegKeyText, TEXT("System\\CurrentControlSet\\Services\\LicenseInfo\\"));
+ lstrcat(RegKeyText, ServiceName);
+
+ if ((Status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, RegKeyText, 0, KEY_READ, &hKey2)) == ERROR_SUCCESS) {
+ dwSize = sizeof(DisplayName);
+ Status = RegQueryValueEx(hKey2, TEXT("DisplayName"), NULL, &dwType, (LPBYTE) DisplayName, &dwSize);
+
+# if DBG
+ if ((TraceFlags & TRACE_REGISTRY) && (Status == ERROR_SUCCESS))
+ dprintf(TEXT("Found Reg-Key for [%s] DisplayName: %s\n"), ServiceName, DisplayName);
+# endif
+ RegCloseKey(hKey2);
+
+ }
+
+ *pDisplayName = LocalAlloc(LPTR, (lstrlen(DisplayName) + 1) * sizeof(TCHAR));
+ if (*pDisplayName != NULL)
+ lstrcpy(*pDisplayName, DisplayName);
+
+} // RegistryDisplayNameGet
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+RegistryFamilyDisplayNameGet(
+ LPTSTR ServiceName,
+ LPTSTR DefaultName,
+ LPTSTR *pDisplayName
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+ Service Name -
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ HKEY hKey2 = NULL;
+ DWORD dwType, dwSize;
+ static TCHAR RegKeyText[512];
+ static TCHAR DisplayName[MAX_PATH + 1];
+ LONG Status;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: RegistryFamilyDisplayNameGet\n"));
+#endif
+
+ lstrcpy(DisplayName, DefaultName);
+
+ //
+ // Create registry key-name we are looking for
+ //
+ lstrcpy(RegKeyText, TEXT("System\\CurrentControlSet\\Services\\LicenseInfo\\"));
+ lstrcat(RegKeyText, ServiceName);
+
+ if ((Status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, RegKeyText, 0, KEY_READ, &hKey2)) == ERROR_SUCCESS) {
+ dwSize = sizeof(DisplayName);
+ Status = RegQueryValueEx(hKey2, TEXT("FamilyDisplayName"), NULL, &dwType, (LPBYTE) DisplayName, &dwSize);
+
+# if DBG
+ if ((TraceFlags & TRACE_REGISTRY) && (Status == ERROR_SUCCESS))
+ dprintf(TEXT("Found Reg-Key for [%s] FamilyDisplayName: %s\n"), ServiceName, DisplayName);
+# endif
+ RegCloseKey(hKey2);
+
+ }
+
+ *pDisplayName = LocalAlloc(LPTR, (lstrlen(DisplayName) + 1) * sizeof(TCHAR));
+ if (*pDisplayName != NULL)
+ lstrcpy(*pDisplayName, DisplayName);
+} // RegistryFamilyDisplayNameGet
+
+
+/////////////////////////////////////////////////////////////////////////
+LPTSTR
+ServiceFindInTable(
+ LPTSTR ServiceName,
+ LPTSTR Table[],
+ ULONG TableSize,
+ ULONG *TableIndex
+ )
+
+/*++
+
+Routine Description:
+
+ Does search of table to find matching service name.
+
+Arguments:
+
+ Service Name -
+
+ Table -
+
+ TableSize -
+
+ TableIndex -
+
+Return Value:
+
+ Pointer to found service or NULL if not found.
+
+--*/
+
+{
+ ULONG i = 0;
+ BOOL Found = FALSE;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: ServiceFindInTable\n"));
+#endif
+ while ((i < TableSize) && (!Found)) {
+ Found = !lstrcmpi(ServiceName, Table[i]);
+ i++;
+ }
+
+ if (Found) {
+ i--;
+ *TableIndex = i;
+ return Table[i];
+ } else
+ return NULL;
+
+} // ServiceFindInTable
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+RegistryInitService(
+ LPTSTR ServiceName,
+ BOOL *PerSeatLicensing,
+ ULONG *SessionLimit
+ )
+
+/*++
+
+Routine Description:
+
+ Gets init values for a given service from the registry. If not found
+ then just returns default values.
+
+Arguments:
+
+ ServiceName -
+
+ PerSeatLicensing -
+
+ SessionLimit -
+
+Return Value:
+
+--*/
+
+{
+ //
+ // These are the default values
+ //
+ ULONG TableEntry;
+ LPTSTR SvcName = NULL;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: RegistryInitService\n"));
+#endif
+ *PerSeatLicensing = FALSE;
+ *SessionLimit = 0;
+
+ //
+ // Check if it is a file/print service - if so don't worry about rest
+ // of registry entries.
+ //
+ if (ServiceFindInTable(ServiceName, FilePrintTable, NumFilePrintEntries, &TableEntry)) {
+ return;
+ }
+
+ //
+ // Not FilePrint - see if we need to map the name.
+ //
+ SvcName = ServiceFindInTable(ServiceName, NameMappingTable2, NUM_MAPPING_ENTRIES, &TableEntry);
+
+ // if it wasn't found, use original ServiceName
+ if (SvcName == NULL)
+ SvcName = ServiceName;
+
+ RegistryInitValues(SvcName, PerSeatLicensing, SessionLimit);
+
+#if DBG
+ if (TraceFlags & TRACE_REGISTRY)
+ if (*PerSeatLicensing)
+ dprintf(TEXT("LLS - Registry Init: PerSeat: Y Svc: %s\n"), SvcName);
+ else
+ dprintf(TEXT("LLS - Registry Init: PerSeat: N Svc: %s\n"), SvcName);
+#endif
+
+} // RegistryInitService
+
+
+
+/////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////
+
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+LocalServiceListInit()
+
+/*++
+
+Routine Description:
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ RtlInitializeResource(&LocalServiceListLock);
+
+ //
+ // Now scan the registry and add all the services
+ //
+ LocalServiceListUpdate();
+} // LocalServiceListInit
+
+
+/////////////////////////////////////////////////////////////////////////
+int __cdecl LocalServiceListCompare(const void *arg1, const void *arg2) {
+ PLOCAL_SERVICE_RECORD Svc1, Svc2;
+
+ Svc1 = (PLOCAL_SERVICE_RECORD) *((PLOCAL_SERVICE_RECORD *) arg1);
+ Svc2 = (PLOCAL_SERVICE_RECORD) *((PLOCAL_SERVICE_RECORD *) arg2);
+
+ return lstrcmpi( Svc1->Name, Svc2->Name );
+
+} // LocalServiceListCompare
+
+
+/////////////////////////////////////////////////////////////////////////
+PLOCAL_SERVICE_RECORD
+LocalServiceListFind(
+ LPTSTR Name
+ )
+
+/*++
+
+Routine Description:
+
+ Internal routine to actually do binary search on LocalServiceList, this
+ does not do any locking as we expect the wrapper routine to do this.
+ The search is a simple binary search.
+
+Arguments:
+
+ ServiceName -
+
+Return Value:
+
+ Pointer to found server table entry or NULL if not found.
+
+--*/
+
+{
+ LONG begin = 0;
+ LONG end = (LONG) LocalServiceListSize - 1;
+ LONG cur;
+ int match;
+ PLOCAL_SERVICE_RECORD Service;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: LocalServiceListFind\n"));
+#endif
+
+ if ((LocalServiceListSize == 0) || (Name == NULL))
+ return NULL;
+
+ while (end >= begin) {
+ // go halfway in-between
+ cur = (begin + end) / 2;
+ Service = LocalServiceList[cur];
+
+ // compare the two result into match
+ match = lstrcmpi(Name, Service->Name);
+
+ if (match < 0)
+ // move new begin
+ end = cur - 1;
+ else
+ begin = cur + 1;
+
+ if (match == 0)
+ return Service;
+ }
+
+ return NULL;
+
+} // LocalServiceListFind
+
+
+/////////////////////////////////////////////////////////////////////////
+PLOCAL_SERVICE_RECORD
+LocalServiceListAdd(
+ LPTSTR Name,
+ LPTSTR DisplayName,
+ LPTSTR FamilyDisplayName,
+ DWORD ConcurrentLimit,
+ DWORD FlipAllow,
+ DWORD Mode,
+ DWORD HighMark
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+ ServiceName -
+
+Return Value:
+
+ Pointer to added service table entry, or NULL if failed.
+
+--*/
+
+{
+ LPTSTR NewName;
+ PLOCAL_SERVICE_RECORD Service;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: LocalServiceListAdd\n"));
+#endif
+
+ if ((Name == NULL) || (*Name == TEXT('\0'))) {
+#if DBG
+ dprintf(TEXT("Error LLS: LocalServiceListAdd Bad Parms\n"));
+#endif
+ ASSERT(FALSE);
+ return NULL;
+ }
+
+ //
+ // Try to find the name
+ //
+ Service = LocalServiceListFind(Name);
+ if (Service != NULL) {
+ Service->ConcurrentLimit = ConcurrentLimit;
+ Service->FlipAllow = FlipAllow;
+ Service->Mode = Mode;
+ return Service;
+ }
+
+ //
+ // No record - so create a new one
+ //
+ if (LocalServiceList == NULL) {
+ LocalServiceList = (PLOCAL_SERVICE_RECORD *) LocalAlloc(LPTR, sizeof(PLOCAL_SERVICE_RECORD));
+ } else {
+ LocalServiceList = (PLOCAL_SERVICE_RECORD *) LocalReAlloc(LocalServiceList, sizeof(PLOCAL_SERVICE_RECORD) * (LocalServiceListSize + 1), LHND);
+ }
+
+ //
+ // Make sure we could allocate server table
+ //
+ if (LocalServiceList == NULL) {
+ ASSERT(FALSE);
+ LocalServiceListSize = 0;
+ return NULL;
+ }
+
+ //
+ // Allocate space for Record.
+ //
+ Service = (PLOCAL_SERVICE_RECORD) LocalAlloc(LPTR, sizeof(LOCAL_SERVICE_RECORD));
+ if (Service == NULL) {
+ ASSERT(FALSE);
+ return NULL;
+ }
+
+ LocalServiceList[LocalServiceListSize] = Service;
+
+ //
+ // Name
+ //
+ NewName = (LPTSTR) LocalAlloc(LPTR, (lstrlen(Name) + 1) * sizeof(TCHAR));
+ if (NewName == NULL) {
+ ASSERT(FALSE);
+ LocalFree(Service);
+ return NULL;
+ }
+
+ // now copy it over...
+ Service->Name = NewName;
+ lstrcpy(NewName, Name);
+
+ //
+ // DisplayName
+ //
+ NewName = (LPTSTR) LocalAlloc(LPTR, (lstrlen(DisplayName) + 1) * sizeof(TCHAR));
+ if (NewName == NULL) {
+ ASSERT(FALSE);
+ LocalFree(Service->Name);
+ LocalFree(Service);
+ return NULL;
+ }
+
+ // now copy it over...
+ Service->DisplayName = NewName;
+ lstrcpy(NewName, DisplayName);
+
+ //
+ // FamilyDisplayName
+ //
+ NewName = (LPTSTR) LocalAlloc(LPTR, (lstrlen(FamilyDisplayName) + 1) * sizeof(TCHAR));
+ if (NewName == NULL) {
+ ASSERT(FALSE);
+ LocalFree(Service->Name);
+ LocalFree(Service->DisplayName);
+ LocalFree(Service);
+ return NULL;
+ }
+
+ // now copy it over...
+ Service->FamilyDisplayName = NewName;
+ lstrcpy(NewName, FamilyDisplayName);
+
+ //
+ // Initialize other stuff
+ //
+ Service->ConcurrentLimit = ConcurrentLimit;
+ Service->FlipAllow = FlipAllow;
+ Service->Mode = Mode;
+ Service->HighMark = HighMark;
+
+ LocalServiceListSize++;
+
+ // Have added the entry - now need to sort it in order of the service names
+ qsort((void *) LocalServiceList, (size_t) LocalServiceListSize, sizeof(PLOCAL_SERVICE_RECORD), LocalServiceListCompare);
+
+ return Service;
+
+} // LocalServiceListAdd
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+LocalServiceListUpdate( )
+
+/*++
+
+Routine Description:
+
+ Looks in registry for given service and sets values accordingly.
+
+Arguments:
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ HKEY hKey2 = NULL;
+ HKEY hKey3 = NULL;
+ static TCHAR KeyName[MAX_PATH + 1];
+ static TCHAR DisplayName[MAX_PATH + 1];
+ static TCHAR FamilyDisplayName[MAX_PATH + 1];
+ LONG EnumStatus;
+ LONG Status;
+ DWORD iSubKey = 0;
+ DWORD dwType, dwSize;
+ DWORD ConcurrentLimit, FlipAllow, Mode, HighMark;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: LocalServiceListUpdate\n"));
+#endif
+
+ RtlAcquireResourceExclusive(&LocalServiceListLock, TRUE);
+
+ EnumStatus = RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("System\\CurrentControlSet\\Services\\LicenseInfo"), 0, KEY_READ, &hKey2);
+
+ while (EnumStatus == ERROR_SUCCESS) {
+ EnumStatus = RegEnumKey(hKey2, iSubKey, KeyName, MAX_PATH + 1);
+ iSubKey++;
+
+ if (EnumStatus == ERROR_SUCCESS) {
+ if ((Status = RegOpenKeyEx(hKey2, KeyName, 0, KEY_READ, &hKey3)) == ERROR_SUCCESS) {
+ dwSize = sizeof(DisplayName);
+ Status = RegQueryValueEx(hKey3, TEXT("DisplayName"), NULL, &dwType, (LPBYTE) DisplayName, &dwSize);
+
+ dwSize = sizeof(FamilyDisplayName);
+ if (Status == ERROR_SUCCESS) {
+ Status = RegQueryValueEx(hKey3, TEXT("FamilyDisplayName"), NULL, &dwType, (LPBYTE) FamilyDisplayName, &dwSize);
+
+ if (Status != ERROR_SUCCESS) {
+ lstrcpy(FamilyDisplayName, DisplayName);
+ Status = ERROR_SUCCESS;
+ }
+ }
+
+ dwSize = sizeof(Mode);
+ if (Status == ERROR_SUCCESS)
+ Status = RegQueryValueEx(hKey3, TEXT("Mode"), NULL, &dwType, (LPBYTE) &Mode, &dwSize);
+
+ dwSize = sizeof(FlipAllow);
+ if (Status == ERROR_SUCCESS)
+ Status = RegQueryValueEx(hKey3, TEXT("FlipAllow"), NULL, &dwType, (LPBYTE) &FlipAllow, &dwSize);
+
+ dwSize = sizeof(ConcurrentLimit);
+ if (Status == ERROR_SUCCESS)
+ if (Mode == 0)
+ ConcurrentLimit = 0;
+ else
+ Status = RegQueryValueEx(hKey3, TEXT("ConcurrentLimit"), NULL, &dwType, (LPBYTE) &ConcurrentLimit, &dwSize);
+
+ dwSize = sizeof(HighMark);
+ if (Status == ERROR_SUCCESS) {
+ Status = RegQueryValueEx(hKey3, TEXT("LocalKey"), NULL, &dwType, (LPBYTE) &HighMark, &dwSize);
+ if (Status != ERROR_SUCCESS) {
+ Status = ERROR_SUCCESS;
+ HighMark = 0;
+ }
+ }
+
+ //
+ // If we read in everything then add to our table
+ //
+ if (Status == ERROR_SUCCESS)
+ LocalServiceListAdd(KeyName, DisplayName, FamilyDisplayName, ConcurrentLimit, FlipAllow, Mode, HighMark);
+
+ RegCloseKey(hKey3);
+ }
+ }
+ }
+
+ RegCloseKey(hKey2);
+
+ RtlReleaseResource(&LocalServiceListLock);
+} // LocalServiceListUpdate
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+LocalServiceListHighMarkSet( )
+
+/*++
+
+Routine Description:
+
+Arguments:
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ HKEY hKey2 = NULL;
+ static TCHAR RegKeyText[512];
+ LONG Status;
+ ULONG i, j;
+ PSERVICE_RECORD Service;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: LocalServiceListHighMarkSet\n"));
+#endif
+
+ RtlAcquireResourceExclusive(&LocalServiceListLock, TRUE);
+
+ for (i = 0; i < LocalServiceListSize; i++) {
+ RtlAcquireResourceShared(&ServiceListLock, TRUE);
+ j = 0;
+ Service = NULL;
+
+ while ( (j < ServiceListSize) && (Service == NULL) ) {
+ if (!lstrcmpi(LocalServiceList[i]->DisplayName, ServiceList[j]->DisplayName) )
+ Service = ServiceList[j];
+
+ j++;
+ }
+
+ RtlReleaseResource(&ServiceListLock);
+
+ if (Service != NULL) {
+ //
+ // Create registry key-name we are looking for
+ //
+ lstrcpy(RegKeyText, TEXT("System\\CurrentControlSet\\Services\\LicenseInfo\\"));
+ lstrcat(RegKeyText, LocalServiceList[i]->Name);
+
+ Status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, RegKeyText, 0, KEY_WRITE, &hKey2);
+
+ if (Status == ERROR_SUCCESS)
+ {
+ Status = RegSetValueEx(hKey2, TEXT("LocalKey"), 0, REG_DWORD, (LPBYTE) &Service->HighMark, sizeof(Service->HighMark));
+ RegCloseKey( hKey2 );
+ }
+ }
+ }
+
+ RtlReleaseResource(&LocalServiceListLock);
+} // LocalServiceListHighMarkSet
+
+
+///////////////////////////////////////////////////////////////////////////////
+VOID
+LocalServiceListConcurrentLimitSet( )
+
+/*++
+
+Routine Description:
+
+ Write concurrent limit to the registry for all secure services.
+
+ Modified from LocalServiceListHighMarkSet() implementation.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ HKEY hKey2 = NULL;
+ TCHAR RegKeyText[512];
+ LONG Status;
+ ULONG i, j;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: LocalServiceListConcurrentLimitSet\n"));
+#endif
+
+ RtlAcquireResourceExclusive(&LocalServiceListLock, TRUE);
+
+ for (i = 0; i < LocalServiceListSize; i++)
+ {
+ //
+ // Create registry key-name we are looking for
+ //
+ lstrcpy(RegKeyText, TEXT("System\\CurrentControlSet\\Services\\LicenseInfo\\"));
+ lstrcat(RegKeyText, LocalServiceList[i]->Name);
+
+ Status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, RegKeyText, 0, KEY_READ, &hKey2);
+
+ if (Status == ERROR_SUCCESS)
+ {
+ DWORD dwConcurrentLimit;
+ DWORD cbConcurrentLimit = sizeof( dwConcurrentLimit );
+ DWORD dwType;
+
+ // don't write unless we have to (to avoid triggering the registry monitor thread)
+ Status = RegQueryValueEx(hKey2, TEXT("ConcurrentLimit"), NULL, &dwType, (LPBYTE) &dwConcurrentLimit, &cbConcurrentLimit );
+
+ if ( ServiceIsSecure( LocalServiceList[i]->DisplayName ) )
+ {
+ LocalServiceList[i]->ConcurrentLimit = LocalServiceList[i]->Mode ? ProductLicensesGet( LocalServiceList[i]->DisplayName, TRUE ) : 0;
+
+ // secure product
+ if ( ( ERROR_SUCCESS != Status )
+ || ( REG_DWORD != dwType )
+ || ( dwConcurrentLimit != LocalServiceList[i]->ConcurrentLimit ) )
+ {
+ RegCloseKey( hKey2 );
+ Status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, RegKeyText, 0, KEY_WRITE, &hKey2);
+
+ ASSERT( ERROR_SUCCESS == Status );
+ if ( ERROR_SUCCESS == Status )
+ {
+ Status = RegSetValueEx(hKey2, TEXT("ConcurrentLimit"), 0, REG_DWORD, (LPBYTE) &LocalServiceList[i]->ConcurrentLimit, sizeof( LocalServiceList[i]->ConcurrentLimit ) );
+ }
+ }
+ }
+
+ RegCloseKey( hKey2 );
+ }
+ }
+
+ RtlReleaseResource(&LocalServiceListLock);
+} // LocalServiceListConcurrentLimitSet
+
+
+///////////////////////////////////////////////////////////////////////////////
+BOOL ServiceIsSecure( LPTSTR ServiceName )
+
+/*++
+
+Routine Description:
+
+ Determine whether a given service disallows 3.51 Honesty-style
+ license purchases.
+
+Arguments:
+
+ ServiceName (LPTSTR)
+ Service to check.
+
+Return Value:
+
+ TRUE if service requires secure certificate,
+ FALSE if it accepts 3.51 Honesty-style license purchases.
+
+--*/
+
+{
+ BOOL IsSecure = FALSE;
+
+ if ( NULL != SecureServiceList )
+ {
+ DWORD i;
+
+ RtlEnterCriticalSection( &ConfigInfoLock );
+
+ for ( i=0; i < SecureServiceListSize; i++ )
+ {
+ if ( !lstrcmpi( SecureServiceList[i], ServiceName ) )
+ {
+ IsSecure = TRUE;
+ break;
+ }
+ }
+
+ RtlLeaveCriticalSection( &ConfigInfoLock );
+ }
+
+ return IsSecure;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+NTSTATUS ServiceSecuritySet( LPTSTR ServiceName )
+
+/*++
+
+Routine Description:
+
+ Add a given service to the secure service list.
+
+Arguments:
+
+ ServiceName (LPTSTR)
+ Service to add.
+
+Return Value:
+
+ STATUS_SUCCESS or Win error or NTSTATUS error code.
+
+--*/
+
+{
+ NTSTATUS nt;
+ DWORD i;
+ BOOL bChangedValue = FALSE;
+
+ RtlEnterCriticalSection( &ConfigInfoLock );
+
+ for ( i=0; i < SecureServiceListSize; i++ )
+ {
+ if ( !lstrcmpi( SecureServiceList[i], ServiceName ) )
+ {
+ // product already registered as secure
+ break;
+ }
+ }
+
+ if ( i < SecureServiceListSize )
+ {
+ // product already registered as secure
+ nt = STATUS_SUCCESS;
+ }
+ else
+ {
+ TCHAR * NewSecureServiceBuffer;
+ ULONG NewSecureServiceBufferSize;
+
+ NewSecureServiceBufferSize = ( SecureServiceBufferSize ? SecureServiceBufferSize : sizeof( TCHAR ) ) + sizeof( TCHAR ) * ( 1 + lstrlen( ServiceName ) );
+ NewSecureServiceBuffer = LocalAlloc( LPTR, NewSecureServiceBufferSize );
+
+ if ( NULL == NewSecureServiceBuffer )
+ {
+ nt = STATUS_NO_MEMORY;
+ ASSERT( FALSE );
+ }
+ else
+ {
+ if ( NULL != SecureServiceBuffer )
+ {
+ // copy over current secure service strings
+ memcpy( NewSecureServiceBuffer, SecureServiceBuffer, SecureServiceBufferSize - sizeof( TCHAR ) );
+
+ // add new secure service (don't forget last string is followed by 2 nulls)
+ memcpy( (LPBYTE) NewSecureServiceBuffer + SecureServiceBufferSize - sizeof( TCHAR ), ServiceName, NewSecureServiceBufferSize - SecureServiceBufferSize - sizeof( TCHAR ) );
+ }
+ else
+ {
+ // add new secure service (don't forget last string is followed by 2 nulls)
+ memcpy( NewSecureServiceBuffer, ServiceName, NewSecureServiceBufferSize - sizeof( TCHAR ) );
+ }
+
+ ASSERT( 0 == *( (LPBYTE) NewSecureServiceBuffer + NewSecureServiceBufferSize - 2 * sizeof( TCHAR ) ) );
+ ASSERT( 0 == *( (LPBYTE) NewSecureServiceBuffer + NewSecureServiceBufferSize - sizeof( TCHAR ) ) );
+
+ // encrypt buffer
+ nt = EBlock( NewSecureServiceBuffer, NewSecureServiceBufferSize );
+ ASSERT( STATUS_SUCCESS == nt );
+
+ if ( STATUS_SUCCESS == nt )
+ {
+ HKEY hKeyParameters;
+
+ // save new list to registry
+ nt = RegOpenKeyEx( HKEY_LOCAL_MACHINE, TEXT("System\\CurrentControlSet\\Services\\LicenseService\\Parameters"), 0, KEY_WRITE, &hKeyParameters );
+ ASSERT( STATUS_SUCCESS == nt );
+
+ if ( STATUS_SUCCESS == nt )
+ {
+ nt = RegSetValueEx( hKeyParameters, TEXT( "ProductData" ), 0, REG_BINARY, (LPBYTE) NewSecureServiceBuffer, NewSecureServiceBufferSize );
+ ASSERT( STATUS_SUCCESS == nt );
+
+ if ( STATUS_SUCCESS == nt )
+ {
+ bChangedValue = TRUE;
+ }
+ }
+
+ RegCloseKey( hKeyParameters );
+ }
+
+ LocalFree( NewSecureServiceBuffer );
+ }
+ }
+
+ RtlLeaveCriticalSection( &ConfigInfoLock );
+
+ if ( ( STATUS_SUCCESS == nt ) && bChangedValue )
+ {
+ // key updated, now update internal copy
+ ConfigInfoRegistryUpdate();
+ }
+
+ return nt;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+NTSTATUS ProductSecurityPack( LPDWORD pcchProductSecurityStrings, WCHAR ** ppchProductSecurityStrings )
+
+/*++
+
+Routine Description:
+
+ Pack the secure service list into a contiguous buffer for transmission.
+
+ NOTE: If the routine succeeds, the caller must later MIDL_user_free() the
+ buffer at *ppchProductSecurityStrings.
+
+Arguments:
+
+ pcchProductSecurityStrings (LPDWORD)
+ On return, holds the size (in characters) of the buffer pointed to
+ by *ppchProductSecurityStrings.
+ ppchProductSecurityStrings (WCHAR **)
+ On return, holds the address of the buffer allocated to hold the names
+ of the secure products.
+
+Return Value:
+
+ STATUS_SUCCESS or STATUS_NO_MEMORY.
+
+--*/
+
+{
+ NTSTATUS nt;
+
+ RtlEnterCriticalSection( &ConfigInfoLock );
+
+ *ppchProductSecurityStrings = MIDL_user_allocate( SecureServiceBufferSize );
+
+ if ( NULL == *ppchProductSecurityStrings )
+ {
+ nt = STATUS_NO_MEMORY;
+ ASSERT( FALSE );
+ }
+ else
+ {
+ memcpy( *ppchProductSecurityStrings, SecureServiceBuffer, SecureServiceBufferSize );
+ *pcchProductSecurityStrings = SecureServiceBufferSize / sizeof( TCHAR );
+
+ nt = STATUS_SUCCESS;
+ }
+
+ RtlLeaveCriticalSection( &ConfigInfoLock );
+
+ return nt;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+NTSTATUS ProductSecurityUnpack( DWORD cchProductSecurityStrings, WCHAR * pchProductSecurityStrings )
+
+/*++
+
+Routine Description:
+
+ Unpack a secure service list packed by ProductSecurityPack(). The products
+ contained in the pack are added to the current secure product list.
+
+Arguments:
+
+ cchProductSecurityStrings (DWORD)
+ The size (in characters) of the buffer pointed to by pchProductSecurityStrings.
+ pchProductSecurityStrings (WCHAR *)
+ The address of the buffer allocated to hold the names of the secure products.
+
+Return Value:
+
+ STATUS_SUCCESS.
+
+--*/
+
+{
+ DWORD i;
+
+ for ( i=0;
+ ( i < cchProductSecurityStrings )
+ && ( TEXT('\0') != pchProductSecurityStrings[i] );
+ i += 1 + lstrlen( &pchProductSecurityStrings[i] ) )
+ {
+ ServiceSecuritySet( &pchProductSecurityStrings[i] );
+ }
+
+ return STATUS_SUCCESS;
+}
+
+#if DBG
+///////////////////////////////////////////////////////////////////////////////
+void ProductSecurityListDebugDump()
+
+/*++
+
+Routine Description:
+
+ Dump contents of product security list to debug console.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ if ( NULL == SecureServiceList )
+ {
+ dprintf( TEXT( "No secure products.\n" ) );
+ }
+ else
+ {
+ DWORD i;
+
+ RtlEnterCriticalSection( &ConfigInfoLock );
+
+ for ( i=0; i < SecureServiceListSize; i++ )
+ {
+ dprintf( TEXT( "(%3ld) %s\n" ), (long)i, SecureServiceList[i] );
+ }
+
+ RtlLeaveCriticalSection( &ConfigInfoLock );
+ }
+}
+#endif
diff --git a/private/net/svcdlls/lls/server/registry.h b/private/net/svcdlls/lls/server/registry.h
new file mode 100644
index 000000000..445bd0730
--- /dev/null
+++ b/private/net/svcdlls/lls/server/registry.h
@@ -0,0 +1,83 @@
+/*++
+
+Copyright (c) 1994 Microsoft Corporation
+
+Module Name:
+
+ Registry.h
+
+Abstract:
+
+
+Author:
+
+ Arthur Hanson (arth) Dec 07, 1994
+
+Environment:
+
+Revision History:
+
+ Jeff Parham (jeffparh) 05-Dec-1995
+ o Added secure service list. This list tracks the products that
+ require "secure" license certificates for all licenses; i.e., the
+ products that do not accept the 3.51 Honesty method of "enter the
+ number of licenses you purchased."
+ o Added routine to update the concurrent limit value in the registry
+ to accurately reflect the connection limit of secure products.
+
+--*/
+
+#ifndef _LLS_REGISTRY_H
+#define _LLS_REGISTRY_H
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+typedef struct _LOCAL_SERVICE_RECORD {
+ LPTSTR Name;
+ LPTSTR DisplayName;
+ LPTSTR FamilyDisplayName;
+ DWORD ConcurrentLimit;
+ DWORD FlipAllow;
+ DWORD Mode;
+ ULONG HighMark;
+} LOCAL_SERVICE_RECORD, *PLOCAL_SERVICE_RECORD;
+
+extern ULONG LocalServiceListSize;
+extern PLOCAL_SERVICE_RECORD *LocalServiceList;
+extern RTL_RESOURCE LocalServiceListLock;
+
+
+VOID RegistryInit( );
+VOID RegistryStartMonitor( );
+VOID ConfigInfoRegistryInit( DWORD *pUseEnterprise, LPTSTR pEnterpriseServer, DWORD *pReplicationType, DWORD *pReplicationTime, DWORD *pLogLevel );
+VOID RegistryInitValues( LPTSTR ServiceName, BOOL *PerSeatLicensing, ULONG *SessionLimit );
+VOID RegistryDisplayNameGet( LPTSTR ServiceName, LPTSTR DefaultName, LPTSTR *pDisplayName );
+VOID RegistryFamilyDisplayNameGet( LPTSTR ServiceName, LPTSTR DefaultName, LPTSTR *pDisplayName );
+VOID RegistryInitService( LPTSTR ServiceName, BOOL *PerSeatLicensing, ULONG *SessionLimit );
+LPTSTR ServiceFindInTable( LPTSTR ServiceName, LPTSTR Table[], ULONG TableSize, ULONG *TableIndex );
+
+VOID LocalServiceListInit();
+PLOCAL_SERVICE_RECORD LocalServiceListFind( LPTSTR Name );
+PLOCAL_SERVICE_RECORD LocalServiceListAdd( LPTSTR Name, LPTSTR DisplayName, LPTSTR FamilyDisplayName, DWORD ConcurrentLimit, DWORD FlipAllow, DWORD Mode, DWORD SessLimit );
+VOID LocalServiceListUpdate( );
+VOID LocalServiceListHighMarkSet( );
+VOID LocalServiceListConcurrentLimitSet( );
+
+BOOL ServiceIsSecure( LPTSTR ServiceName );
+NTSTATUS ServiceSecuritySet( LPTSTR ServiceName );
+NTSTATUS ProductSecurityUnpack( DWORD cchProductSecurityStrings, WCHAR * pchProductSecurityStrings );
+NTSTATUS ProductSecurityPack( LPDWORD pcchProductSecurityStrings, WCHAR ** ppchProductSecurityStrings );
+
+#if DBG
+void ProductSecurityListDebugDump();
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/private/net/svcdlls/lls/server/repl.c b/private/net/svcdlls/lls/server/repl.c
new file mode 100644
index 000000000..725c25174
--- /dev/null
+++ b/private/net/svcdlls/lls/server/repl.c
@@ -0,0 +1,641 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ repl.c
+
+Abstract:
+
+Author:
+
+ Arthur Hanson (arth) 06-Jan-1995
+
+Revision History:
+
+ Jeff Parham (jeffparh) 05-Dec-1995
+ o Added replication of certificate database and secure service list.
+ o Log failure to connect during replication only if the target server
+ is running a build in which license server should be available (i.e.,
+ 1057 (3.51) or greater). If the target server does not support
+ license server, log a message to that effect only once.
+
+--*/
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windows.h>
+
+#include "debug.h"
+#include "llsutil.h"
+#include <llsapi.h>
+#include "llssrv.h"
+#include "mapping.h"
+#include "msvctbl.h"
+#include "svctbl.h"
+#include "perseat.h"
+#include "server.h"
+#include "repl.h"
+#include "llsrpc_s.h"
+#include "pack.h"
+#include "llsevent.h"
+#include "certdb.h"
+#include "registry.h"
+
+HANDLE ReplicationEvent;
+
+HANDLE LlsRPCHandle = NULL;
+FARPROC pLlsReplConnect;
+FARPROC pLlsReplClose;
+FARPROC pLlsFreeMemory;
+
+FARPROC pLlsReplicationRequestW = NULL;
+FARPROC pLlsReplicationServerAddW = NULL;
+FARPROC pLlsReplicationServerServiceAddW = NULL;
+FARPROC pLlsReplicationServiceAddW = NULL;
+FARPROC pLlsReplicationUserAddW = NULL;
+
+PLLS_CAPABILITY_IS_SUPPORTED pLlsCapabilityIsSupported = NULL;
+PLLS_REPLICATION_CERT_DB_ADD_W pLlsReplicationCertDbAddW = NULL;
+PLLS_REPLICATION_PRODUCT_SECURITY_ADD_W pLlsReplicationProductSecurityAddW = NULL;
+PLLS_REPLICATION_USER_ADD_EX_W pLlsReplicationUserAddExW = NULL;
+PLLS_CONNECT_W pLlsConnectW = NULL;
+PLLS_CLOSE pLlsClose = NULL;
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+ReplicationInit ( )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+--*/
+
+{
+ DWORD Ignore;
+ HANDLE Thread;
+ NTSTATUS Status;
+ DWORD Time;
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_REPLICATION))
+ dprintf(TEXT("LLS TRACE: ReplicationInit\n"));
+#endif
+
+ //
+ // Open up our RPC DLL and init our function references.
+ //
+ LlsRPCHandle = LoadLibrary(TEXT("LLSRPC.DLL"));
+ ASSERT(LlsRPCHandle != NULL);
+
+ if (LlsRPCHandle != NULL) {
+ pLlsReplConnect = GetProcAddress(LlsRPCHandle, ("LlsReplConnectW"));
+ pLlsReplClose = GetProcAddress(LlsRPCHandle, ("LlsReplClose"));
+ pLlsFreeMemory = GetProcAddress(LlsRPCHandle, ("LlsFreeMemory"));
+ pLlsReplicationRequestW = GetProcAddress(LlsRPCHandle, ("LlsReplicationRequestW"));
+ pLlsReplicationServerAddW = GetProcAddress(LlsRPCHandle, ("LlsReplicationServerAddW"));
+ pLlsReplicationServerServiceAddW = GetProcAddress(LlsRPCHandle, ("LlsReplicationServerServiceAddW"));
+ pLlsReplicationServiceAddW = GetProcAddress(LlsRPCHandle, ("LlsReplicationServiceAddW"));
+ pLlsReplicationUserAddW = GetProcAddress(LlsRPCHandle, ("LlsReplicationUserAddW"));
+ pLlsReplicationCertDbAddW = (PLLS_REPLICATION_CERT_DB_ADD_W) GetProcAddress(LlsRPCHandle, ("LlsReplicationCertDbAddW"));
+ pLlsReplicationProductSecurityAddW = (PLLS_REPLICATION_PRODUCT_SECURITY_ADD_W) GetProcAddress(LlsRPCHandle, ("LlsReplicationProductSecurityAddW"));
+ pLlsReplicationUserAddExW = (PLLS_REPLICATION_USER_ADD_EX_W) GetProcAddress(LlsRPCHandle, ("LlsReplicationUserAddExW"));
+ pLlsCapabilityIsSupported = (PLLS_CAPABILITY_IS_SUPPORTED) GetProcAddress(LlsRPCHandle, ("LlsCapabilityIsSupported"));
+ pLlsConnectW = (PLLS_CONNECT_W) GetProcAddress(LlsRPCHandle, ("LlsConnectW"));
+ pLlsClose = (PLLS_CLOSE) GetProcAddress(LlsRPCHandle, ("LlsClose"));
+
+ ASSERT (pLlsReplConnect != NULL);
+ ASSERT (pLlsReplClose != NULL);
+ ASSERT (pLlsFreeMemory != NULL);
+ ASSERT (pLlsReplicationRequestW != NULL);
+ ASSERT (pLlsReplicationServerAddW != NULL);
+ ASSERT (pLlsReplicationServerServiceAddW != NULL);
+ ASSERT (pLlsReplicationServiceAddW != NULL);
+ ASSERT (pLlsReplicationUserAddW != NULL);
+ ASSERT (pLlsReplicationCertDbAddW != NULL);
+ ASSERT (pLlsReplicationProductSecurityAddW != NULL);
+ ASSERT (pLlsReplicationUserAddExW != NULL);
+ ASSERT (pLlsCapabilityIsSupported != NULL);
+ ASSERT (pLlsConnectW != NULL);
+ ASSERT (pLlsClose != NULL);
+
+ if ((pLlsReplConnect != NULL) && (pLlsReplClose != NULL) &&
+ (pLlsFreeMemory != NULL) && (pLlsReplicationRequestW != NULL) &&
+ (pLlsReplicationServerAddW != NULL) && (pLlsReplicationServiceAddW != NULL) &&
+ (pLlsReplicationServerServiceAddW != NULL) && (pLlsReplicationUserAddW != NULL) &&
+ (pLlsReplicationCertDbAddW != NULL) && (pLlsReplicationProductSecurityAddW != NULL) &&
+ (pLlsReplicationUserAddExW != NULL) && (pLlsCapabilityIsSupported != NULL) &&
+ (pLlsConnectW != NULL) && (pLlsClose != NULL)
+ ) {
+
+ //
+ // Create the Replication Management event
+ //
+ Status = NtCreateEvent(
+ &ReplicationEvent,
+ EVENT_QUERY_STATE | EVENT_MODIFY_STATE | SYNCHRONIZE,
+ NULL,
+ SynchronizationEvent,
+ FALSE
+ );
+
+ ASSERT(NT_SUCCESS(Status));
+
+ //
+ // Fire off the thread to watch for replication.
+ //
+ Thread = CreateThread(
+ NULL,
+ 0L,
+ (LPTHREAD_START_ROUTINE) ReplicationManager,
+ 0L,
+ 0L,
+ &Ignore
+ );
+
+ }
+ }
+
+ return STATUS_SUCCESS;
+} // ReplicationInit
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+ReplicationDo (
+ LLS_HANDLE LlsHandle,
+ LLS_REPL_HANDLE ReplHandle,
+ DWORD LastReplicated
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ NTSTATUS Status;
+ PREPL_SERVICE_RECORD Services = NULL;
+ ULONG ServicesTotalRecords = 0;
+ PREPL_SERVER_RECORD Servers = NULL;
+ ULONG ServersTotalRecords = 0;
+ PREPL_SERVER_SERVICE_RECORD ServerServices = NULL;
+ ULONG ServerServicesTotalRecords = 0;
+
+ REPL_CERTIFICATE_DB_0 CertificateDB;
+ REPL_PRODUCT_SECURITY_0 ProductSecurity;
+
+ DWORD UserLevel = 0;
+ REPL_USER_RECORD_CONTAINER UserDB;
+
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_REPLICATION))
+ dprintf(TEXT("LLS TRACE: ReplicationDo\n"));
+#endif
+
+ //
+ // Pack all of our data into linear / self-relative buffers so we
+ // can send them over.
+ //
+ ZeroMemory( &UserDB, sizeof( UserDB ) );
+ ZeroMemory( &CertificateDB, sizeof( CertificateDB ) );
+ ZeroMemory( &ProductSecurity, sizeof( ProductSecurity ) );
+
+ if ( (*pLlsCapabilityIsSupported)( LlsHandle, LLS_CAPABILITY_REPLICATE_USERS_EX ) )
+ {
+ UserLevel = 1;
+ Status = PackAll( LastReplicated, &ServicesTotalRecords, &Services, &ServersTotalRecords, &Servers, &ServerServicesTotalRecords, &ServerServices, 1, &UserDB.Level1.NumUsers, &UserDB.Level1.Users );
+ if (Status != STATUS_SUCCESS)
+ goto ReplicationDoExit;
+ }
+ else
+ {
+ UserLevel = 0;
+ Status = PackAll( LastReplicated, &ServicesTotalRecords, &Services, &ServersTotalRecords, &Servers, &ServerServicesTotalRecords, &ServerServices, 0, &UserDB.Level0.NumUsers, &UserDB.Level0.Users );
+ if (Status != STATUS_SUCCESS)
+ goto ReplicationDoExit;
+ }
+
+ if ( (*pLlsCapabilityIsSupported)( LlsHandle, LLS_CAPABILITY_REPLICATE_CERT_DB ) )
+ {
+ Status = CertDbPack( &CertificateDB.StringSize, &CertificateDB.Strings, &CertificateDB.HeaderContainer.Level0.NumHeaders, &CertificateDB.HeaderContainer.Level0.Headers, &CertificateDB.ClaimContainer.Level0.NumClaims, &CertificateDB.ClaimContainer.Level0.Claims );
+ if (Status != STATUS_SUCCESS)
+ goto ReplicationDoExit;
+ }
+
+ if ( (*pLlsCapabilityIsSupported)( LlsHandle, LLS_CAPABILITY_REPLICATE_PRODUCT_SECURITY ) )
+ {
+ Status = ProductSecurityPack( &ProductSecurity.StringSize, &ProductSecurity.Strings );
+ if (Status != STATUS_SUCCESS)
+ goto ReplicationDoExit;
+ }
+
+ //
+ // Transmit...
+ //
+
+ Status = (*pLlsReplicationServiceAddW) ( ReplHandle, ServicesTotalRecords, Services );
+ if (Status != STATUS_SUCCESS)
+ goto ReplicationDoExit;
+
+ Status = (*pLlsReplicationServerAddW) ( ReplHandle, ServersTotalRecords, Servers );
+ if (Status != STATUS_SUCCESS)
+ goto ReplicationDoExit;
+
+ Status = (*pLlsReplicationServerServiceAddW) ( ReplHandle, ServerServicesTotalRecords, ServerServices );
+ if (Status != STATUS_SUCCESS)
+ goto ReplicationDoExit;
+
+ if ( (*pLlsCapabilityIsSupported)( LlsHandle, LLS_CAPABILITY_REPLICATE_USERS_EX ) )
+ {
+ Status = (*pLlsReplicationUserAddExW)( ReplHandle, UserLevel, &UserDB );
+ if (Status != STATUS_SUCCESS)
+ goto ReplicationDoExit;
+ }
+ else
+ {
+ Status = (*pLlsReplicationUserAddW) ( ReplHandle, UserDB.Level0.NumUsers, UserDB.Level0.Users );
+ if (Status != STATUS_SUCCESS)
+ goto ReplicationDoExit;
+ }
+
+ if ( (*pLlsCapabilityIsSupported)( LlsHandle, LLS_CAPABILITY_REPLICATE_CERT_DB ) )
+ {
+ Status = (*pLlsReplicationCertDbAddW)( ReplHandle, 0, &CertificateDB );
+ if (Status != STATUS_SUCCESS)
+ goto ReplicationDoExit;
+ }
+
+ if ( (*pLlsCapabilityIsSupported)( LlsHandle, LLS_CAPABILITY_REPLICATE_PRODUCT_SECURITY ) )
+ {
+ Status = (*pLlsReplicationProductSecurityAddW)( ReplHandle, 0, &ProductSecurity );
+ }
+
+ReplicationDoExit:
+ if (Status != STATUS_SUCCESS) {
+#if DBG
+ dprintf(TEXT("LLS Replication ABORT: 0x%lX\n"), Status);
+#endif
+ }
+
+ if (Services != NULL)
+ MIDL_user_free(Services);
+
+ if (Servers != NULL)
+ MIDL_user_free(Servers);
+
+ if ( 0 == UserLevel )
+ {
+ if (UserDB.Level0.Users != NULL)
+ MIDL_user_free(UserDB.Level0.Users);
+ }
+ else
+ {
+ if (UserDB.Level1.Users != NULL)
+ MIDL_user_free(UserDB.Level1.Users);
+ }
+
+ if (CertificateDB.Strings != NULL)
+ MIDL_user_free(CertificateDB.Strings);
+
+ if (CertificateDB.HeaderContainer.Level0.Headers != NULL)
+ MIDL_user_free(CertificateDB.HeaderContainer.Level0.Headers);
+
+ if (CertificateDB.ClaimContainer.Level0.Claims != NULL)
+ MIDL_user_free(CertificateDB.ClaimContainer.Level0.Claims);
+
+ if (ProductSecurity.Strings != NULL)
+ MIDL_user_free(ProductSecurity.Strings);
+
+#if DBG
+ if (TraceFlags & TRACE_REPLICATION)
+ dprintf(TEXT(" LLS Replication Finished\n"));
+#endif
+
+ return Status;
+
+} // ReplicationDo
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+ReplicationManager (
+ IN PVOID ThreadParameter
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+ ThreadParameter - Not used.
+
+
+Return Value:
+
+ This thread never exits.
+
+--*/
+
+{
+ BOOL DoReplication = FALSE;
+ NTSTATUS Status;
+ LLS_REPL_HANDLE ReplHandle = NULL;
+ LLS_HANDLE LlsHandle = NULL;
+ PLLS_CONNECT_INFO_0 pConnectInfo;
+ PREPL_REQUEST pReplInfo;
+ TCHAR ReplicateTo[MAX_COMPUTERNAME_LENGTH + 3];
+ DWORD LastReplicated;
+ LPTSTR pReplicateTo = ReplicateTo;
+ TCHAR LastFailedConnectionDownlevelReplicateTo[MAX_COMPUTERNAME_LENGTH + 3] = TEXT("");
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_REPLICATION))
+ dprintf(TEXT("LLS TRACE: ReplicationManager\n"));
+#endif
+
+ //
+ // Loop forever waiting to be given the opportunity to serve the
+ // greater good.
+ //
+ for ( ; ; ) {
+ //
+ // Wait to be notified that there is work to be done
+ //
+ Status = NtWaitForSingleObject( ReplicationEvent, TRUE, NULL );
+
+ //
+ // So they said, go replicate my son... Yeah, but first we must ask
+ // the master for permission.
+ //
+
+ //
+ // Construct our repl record
+ //
+ pReplInfo = MIDL_user_allocate(sizeof(REPL_REQUEST));
+ ASSERT(pReplInfo != NULL);
+ if (pReplInfo != NULL) {
+ RtlEnterCriticalSection(&ConfigInfoLock);
+ lstrcpy(ReplicateTo, ConfigInfo.ReplicateTo);
+
+ pReplInfo->EnterpriseServerDate = 0;
+ lstrcpy(pReplInfo->EnterpriseServer, ConfigInfo.EnterpriseServer);
+ pReplInfo->EnterpriseServerDate = ConfigInfo.EnterpriseServerDate;
+
+ pReplInfo->LastReplicated = ConfigInfo.LastReplicatedSeconds;
+ pReplInfo->CurrentTime = LastUsedTime;
+ pReplInfo->NumberServices = 0;
+ pReplInfo->NumberUsers = 0;
+
+ pReplInfo->ReplSize = MAX_REPL_SIZE;
+
+ pReplInfo->Backoff = 0;
+ RtlLeaveCriticalSection(&ConfigInfoLock);
+
+#if DBG
+ if (TraceFlags & TRACE_REPLICATION)
+ dprintf(TEXT("LLS Starting Replication to: %s @ %s\n"), ReplicateTo, TimeToString(pReplInfo->CurrentTime));
+#endif
+
+ Status = (*pLlsReplConnect) ( ReplicateTo, &ReplHandle, 0, (LPBYTE *) &pConnectInfo );
+
+ if ( STATUS_SUCCESS != Status )
+ {
+#if DBG
+ dprintf(TEXT("LLS Error: LlsReplConnect failed: 0x%lX\n"), Status);
+#endif
+ ReplHandle = NULL;
+ }
+ else
+ {
+ Status = (*pLlsConnectW)( ReplicateTo, &LlsHandle );
+
+ if ( STATUS_SUCCESS != Status )
+ {
+#if DBG
+ dprintf(TEXT("LLS Error: LlsConnectW failed: 0x%lX\n"), Status);
+#endif
+ LlsHandle = NULL;
+ }
+ }
+
+ if (Status != STATUS_SUCCESS)
+ {
+ DWORD dwWinError;
+ DWORD dwBuildNumber;
+
+ dwWinError = WinNtBuildNumberGet( ReplicateTo, &dwBuildNumber );
+
+ if ( ( ERROR_SUCCESS == dwWinError ) && ( dwBuildNumber < 1057L ) )
+ {
+ // the ReplicateTo machine does not support the license service
+ if ( lstrcmpi( ReplicateTo, LastFailedConnectionDownlevelReplicateTo ) )
+ {
+ lstrcpy( LastFailedConnectionDownlevelReplicateTo, ReplicateTo );
+
+ LogEvent( LLS_EVENT_REPL_DOWNLEVEL_TARGET, 1, &pReplicateTo, Status );
+ }
+ }
+ else
+ {
+ // the ReplicateTo machine should be running the license service
+ *LastFailedConnectionDownlevelReplicateTo = TEXT( '\0' );
+
+ LogEvent( LLS_EVENT_REPL_NO_CONNECTION, 1, &pReplicateTo, Status );
+ }
+ }
+ else
+ {
+ *LastFailedConnectionDownlevelReplicateTo = TEXT( '\0' );
+
+ Status = (*pLlsReplicationRequestW) ( ReplHandle, REPL_VERSION, pReplInfo );
+
+ if (Status != STATUS_SUCCESS)
+ {
+ LogEvent( LLS_EVENT_REPL_REQUEST_FAILED, 1, &pReplicateTo, Status );
+ }
+ else
+ {
+ RtlEnterCriticalSection(&ConfigInfoLock);
+ lstrcpy(ConfigInfo.EnterpriseServer, pReplInfo->EnterpriseServer);
+ ConfigInfo.EnterpriseServerDate = pReplInfo->EnterpriseServerDate;
+ ConfigInfo.IsReplicating = TRUE;
+ LastReplicated = pReplInfo->LastReplicated;
+ RtlLeaveCriticalSection(&ConfigInfoLock);
+
+ //
+ // And lo, thou may proceed...
+ //
+ if (pReplInfo->Backoff == 0)
+ {
+ if ( ConfigInfo.LogLevel )
+ {
+ LogEvent( LLS_EVENT_REPL_START, 1, &pReplicateTo, ERROR_SUCCESS );
+ }
+
+ Status = ReplicationDo( LlsHandle, ReplHandle, LastReplicated );
+
+ if ( STATUS_SUCCESS != Status )
+ {
+ LogEvent( LLS_EVENT_REPL_FAILED, 1, &pReplicateTo, Status );
+ }
+ else if ( ConfigInfo.LogLevel )
+ {
+ LogEvent( LLS_EVENT_REPL_END, 1, &pReplicateTo, ERROR_SUCCESS );
+ }
+
+ RtlEnterCriticalSection(&ConfigInfoLock);
+
+ //
+ // Need to update when next we should replicate
+ //
+ ConfigInfo.LastReplicatedSeconds = DateSystemGet();
+ GetLocalTime(&ConfigInfo.LastReplicated);
+ ReplicationTimeSet();
+ }
+ else
+ {
+ LogEvent( LLS_EVENT_REPL_BACKOFF, 1, &pReplicateTo, ERROR_SUCCESS );
+ RtlEnterCriticalSection(&ConfigInfoLock);
+ }
+
+ ConfigInfo.IsReplicating = FALSE;
+ RtlLeaveCriticalSection(&ConfigInfoLock);
+ }
+ }
+
+ //
+ // Disconnect from Master Server
+ //
+ if ( NULL != LlsHandle )
+ {
+ (*pLlsClose)( LlsHandle );
+ LlsHandle = NULL;
+ }
+
+ if ( NULL != ReplHandle )
+ {
+ Status = (*pLlsReplClose) ( &ReplHandle );
+ try
+ {
+ RpcSmDestroyClientContext( &ReplHandle );
+ }
+ except (TRUE)
+ {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSSRV.EXE (Repl): RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ ReplHandle = NULL;
+ }
+
+ MIDL_user_free( pReplInfo );
+ }
+ }
+
+} // ReplicationManager
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+ReplicationTimeSet ( )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+--*/
+
+{
+ DWORD CurrTime, ReplTime, Time;
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_REPLICATION))
+ dprintf(TEXT("LLS TRACE: ReplicationTimeSet\n"));
+#endif
+
+ ReplTime = Time = 0;
+
+ //
+ // Figure out what new time to set it to
+ //
+ if (!ConfigInfo.Replicate)
+ return;
+
+ //
+ // If REPLICATE_DELTA it is easy as we just take the delta and apply it to
+ // the last replication time. Otherwise we have to convert the time from
+ // midnight.
+ //
+
+ //
+ // Figure out how long since we last replicated
+ //
+ ReplTime = ConfigInfo.ReplicationTime;
+
+ if (ConfigInfo.ReplicationType == REPLICATE_DELTA) {
+ Time = DateSystemGet() - ConfigInfo.LastReplicatedSeconds;
+
+ //
+ // If we have already gone past when we should replicate then schedule
+ // one real soon now (10 minutes).
+ //
+ if (Time > ReplTime)
+ Time = 10 * 60;
+ else
+ Time = ReplTime - Time;
+
+ Time += DateLocalGet();
+ } else {
+ //
+ // Need to adjust time to midnight - do this by MOD of seconds
+ // per day.
+ //
+ CurrTime = DateLocalGet();
+ Time = CurrTime - ((CurrTime / (60 * 60 * 24)) * (60 * 60 * 24));
+ CurrTime = CurrTime - Time;
+
+ //
+ // Time = seconds past midnight.
+ // CurrTime = Todays @ 12:00 AM
+ // Figure out if we are past the replication time, if so schedule it
+ // for tomorrow, else today.
+ //
+ if (Time > ReplTime)
+ Time = CurrTime + (60 * 60 * 24) + ReplTime;
+ else
+ Time = CurrTime + ReplTime;
+
+ }
+
+ ConfigInfo.NextReplication = Time;
+
+} // ReplicationTimeSet
diff --git a/private/net/svcdlls/lls/server/repl.h b/private/net/svcdlls/lls/server/repl.h
new file mode 100644
index 000000000..4cfa4f37f
--- /dev/null
+++ b/private/net/svcdlls/lls/server/repl.h
@@ -0,0 +1,47 @@
+/*++
+
+Copyright (c) 1994 Microsoft Corporation
+
+Module Name:
+
+ Repl.h
+
+Abstract:
+
+
+Author:
+
+ Arthur Hanson (arth) Dec 07, 1994
+
+Environment:
+
+Revision History:
+
+--*/
+
+#ifndef _LLS_REPL_H
+#define _LLS_REPL_H
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+//
+// Maximum number of records we will send over to the server at once.
+//
+#define MAX_REPL_SIZE 25
+#define REPL_VERSION 0x0102
+
+extern HANDLE ReplicationEvent;
+
+
+NTSTATUS ReplicationInit();
+VOID ReplicationManager ( IN PVOID ThreadParameter );
+VOID ReplicationTimeSet ( );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/private/net/svcdlls/lls/server/rpc.c b/private/net/svcdlls/lls/server/rpc.c
new file mode 100644
index 000000000..dc3d8eb14
--- /dev/null
+++ b/private/net/svcdlls/lls/server/rpc.c
@@ -0,0 +1,6121 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ rpc.c
+
+Abstract:
+
+
+Author:
+
+ Arthur Hanson (arth) 06-Jan-1995
+
+Revision History:
+
+ Jeff Parham (jeffparh) 05-Dec-1995
+ o Added replication of certificate database and secure service list.
+ o Added Llsr API to support secure certificates.
+ o Added LLS_LICENSE_INFO_1 support to LlsrLicenseEnumW() and
+ LlsrLicenseAddW().
+ o Added LLS_PRODUCT_LICENSE_INFO_1 support to LlsrProductLicenseEnumW().
+ o Added save of all data files after receiving replicated data.
+
+--*/
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windows.h>
+
+#include "llsapi.h"
+#include "debug.h"
+#include "llssrv.h"
+#include "mapping.h"
+#include "msvctbl.h"
+#include "svctbl.h"
+#include "perseat.h"
+#include "purchase.h"
+#include "server.h"
+
+#include "llsrpc_s.h"
+#include "lsapi_s.h"
+#include "llsdbg_s.h"
+#include "repl.h"
+#include "pack.h"
+#include "registry.h"
+#include "certdb.h"
+
+
+//
+// BUGBUG: maxpreflength does not currently work for the enum calls as it
+// doesn't take into account the string fields in the records.
+//
+
+
+typedef struct {
+ TCHAR Name[MAX_COMPUTERNAME_LENGTH + 1];
+} CLIENT_CONTEXT_TYPE, *PCLIENT_CONTEXT_TYPE;
+
+typedef struct {
+ TCHAR Name[MAX_COMPUTERNAME_LENGTH + 1];
+ DWORD ReplicationStart;
+
+ BOOL Active;
+ BOOL Replicated;
+
+ BOOL ServicesSent;
+ ULONG ServiceTableSize;
+ PREPL_SERVICE_RECORD Services;
+
+ BOOL ServersSent;
+ ULONG ServerTableSize;
+ PREPL_SERVER_RECORD Servers;
+
+ BOOL ServerServicesSent;
+ ULONG ServerServiceTableSize;
+ PREPL_SERVER_SERVICE_RECORD ServerServices;
+
+ BOOL UsersSent;
+ ULONG UserLevel;
+ ULONG UserTableSize;
+ LPVOID Users;
+
+ BOOL CertDbSent;
+ ULONG CertDbProductStringSize;
+ WCHAR * CertDbProductStrings;
+ ULONG CertDbNumHeaders;
+ PREPL_CERT_DB_CERTIFICATE_HEADER_0 CertDbHeaders;
+ ULONG CertDbNumClaims;
+ PREPL_CERT_DB_CERTIFICATE_CLAIM_0 CertDbClaims;
+
+ BOOL ProductSecuritySent;
+ ULONG ProductSecurityStringSize;
+ WCHAR * ProductSecurityStrings;
+
+} REPL_CONTEXT_TYPE, *PREPL_CONTEXT_TYPE;
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+LLSRpcListen (
+ IN PVOID ThreadParameter
+ )
+
+/*++
+
+Routine Description:
+
+Arguments:
+
+ ThreadParameter - Indicates how many active threads there currently
+ are.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ RPC_STATUS Status;
+
+ Status = RpcServerListen(1, 40, 0);
+ if (Status) {
+#if DBG
+ dprintf(TEXT("RpcServerListen Failed (0x%lx)\n"), Status);
+#endif
+ }
+
+ return Status;
+
+} // LLSRpcListen
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+LLSRpcInit()
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ RPC_STATUS Status;
+ DWORD Ignore;
+ PSECURITY_DESCRIPTOR pSD;
+
+ //
+ // Initialize a security descriptor.
+ //
+ pSD = (PSECURITY_DESCRIPTOR) LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH);
+ if (pSD == NULL) {
+#if DBG
+ dprintf(TEXT("LLS Error: RPC Security Descriptor Alloc Failed\n"));
+#endif
+ ASSERT(FALSE);
+ return;
+ }
+
+ if (!InitializeSecurityDescriptor(pSD, SECURITY_DESCRIPTOR_REVISION)) {
+#if DBG
+ dprintf(TEXT("LLS Error: RPC Init Security Descriptor Failed\n"));
+#endif
+ if(pSD != NULL)
+ LocalFree((HLOCAL) pSD);
+
+ ASSERT(FALSE);
+ return;
+ }
+
+ //
+ // Add a NULL disc. ACL to the security descriptor.
+ //
+ if (!SetSecurityDescriptorDacl(pSD, TRUE, (PACL) NULL, FALSE)) {
+#if DBG
+ dprintf(TEXT("LLS Error: RPC SetSecurityDescriptorDacl\n"));
+#endif
+ if(pSD != NULL)
+ LocalFree((HLOCAL) pSD);
+
+ ASSERT(FALSE);
+ return;
+ }
+
+ //
+ // Setup for LPC calls..
+ //
+ Status = RpcServerUseProtseqEp(TEXT("ncalrpc"), RPC_C_PROTSEQ_MAX_REQS_DEFAULT, TEXT(LLS_LPC_ENDPOINT), pSD);
+ if (Status) {
+#if DBG
+ dprintf(TEXT("RpcServerUseProtseq ncalrpc Failed (0x%lx)\n"), Status);
+#endif
+ if(pSD != NULL)
+ LocalFree((HLOCAL) pSD);
+
+ ASSERT(FALSE);
+ return;
+ }
+
+ // Named pipes as well
+ Status = RpcServerUseProtseqEp(TEXT("ncacn_np"), RPC_C_PROTSEQ_MAX_REQS_DEFAULT, TEXT(LLS_NP_ENDPOINT), pSD);
+ if (Status) {
+#if DBG
+ dprintf(TEXT("RpcServerUseProtseq ncacn_np Failed (0x%lx)\n"), Status);
+#endif
+ if(pSD != NULL)
+ LocalFree((HLOCAL) pSD);
+
+ ASSERT(FALSE);
+ return;
+ }
+
+ if(pSD != NULL)
+ LocalFree((HLOCAL) pSD);
+
+ // register the interface for the UI RPC's
+ Status = RpcServerRegisterIf(llsrpc_ServerIfHandle, NULL, NULL);
+ if (Status) {
+#if DBG
+ dprintf(TEXT("RpcServerRegisterIf Failed (0x%lx)\n"), Status);
+#endif
+ return;
+ }
+
+ // Now the interface for the Licensing RPC's
+ Status = RpcServerRegisterIf(lsapirpc_ServerIfHandle, NULL, NULL);
+ if (Status) {
+#if DBG
+ dprintf(TEXT("RpcServerRegisterIf2 Failed (0x%lx)\n"), Status);
+#endif
+ return;
+ }
+
+#if DBG
+ //
+ // ... and if DEBUG then the debugging interface
+ //
+ Status = RpcServerRegisterIf(llsdbgrpc_ServerIfHandle, NULL, NULL);
+ if (Status) {
+#if DBG
+ dprintf(TEXT("RpcServerRegisterIf (debug) Failed (0x%lx)\n"), Status);
+#endif
+ return;
+ }
+#endif
+
+ //
+ // Create thread to listen for requests.
+ //
+ CreateThread(
+ NULL,
+ 0L,
+ (LPTHREAD_START_ROUTINE) LLSRpcListen,
+ 0L,
+ 0L,
+ &Ignore
+ );
+
+} // LLSRpcInit
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+/////////////////////////////////////////////////////////////////////////
+VOID __RPC_USER LLS_HANDLE_rundown(
+ LLS_HANDLE Handle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ CLIENT_CONTEXT_TYPE *pClient;
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LLS_HANDLE_rundown\n"));
+#endif
+
+ pClient = (CLIENT_CONTEXT_TYPE *) Handle;
+
+ MIDL_user_free(pClient);
+
+} // LLS_HANDLE_rundown
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrConnect(
+ PLLS_HANDLE Handle,
+ LPTSTR Name
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ CLIENT_CONTEXT_TYPE *pClient;
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsConnect: %s\n"), Name);
+#endif
+
+ pClient = (CLIENT_CONTEXT_TYPE *) midl_user_allocate(sizeof(CLIENT_CONTEXT_TYPE));
+
+ if (Name != NULL)
+ lstrcpy(pClient->Name, Name);
+ else
+ lstrcpy(pClient->Name, TEXT(""));
+
+ *Handle = pClient;
+
+ return STATUS_SUCCESS;
+} // LlsrConnect
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrClose(
+ LLS_HANDLE Handle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ CLIENT_CONTEXT_TYPE *pClient;
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsClose\n"));
+#endif
+
+ pClient = (CLIENT_CONTEXT_TYPE *) Handle;
+
+ return STATUS_SUCCESS;
+} // LlsrClose
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrLicenseEnumW(
+ LLS_HANDLE Handle,
+ PLLS_LICENSE_ENUM_STRUCTW pLicenseInfo,
+ DWORD pPrefMaxLen,
+ LPDWORD pTotalEntries,
+ LPDWORD pResumeHandle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ DWORD Level;
+ PVOID BufPtr = NULL;
+ ULONG BufSize = 0;
+ ULONG EntriesRead = 0;
+ ULONG TotalEntries = 0;
+ ULONG i = 0;
+ ULONG j = 0;
+ DWORD RecordSize;
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsLicenseEnumW\n"));
+#endif
+
+ Level = pLicenseInfo->Level;
+
+ if ( 0 == Level )
+ {
+ RecordSize = sizeof( LLS_LICENSE_INFO_0W );
+ }
+ else if ( 1 == Level )
+ {
+ RecordSize = sizeof( LLS_LICENSE_INFO_1W );
+ }
+ else
+ {
+ return STATUS_INVALID_LEVEL;
+ }
+ //
+ // Need to scan list so get read access.
+ //
+ RtlAcquireResourceShared(&LicenseListLock, TRUE);
+
+ //
+ // Calculate how many records will fit into PrefMaxLen buffer.
+ //
+ i = *pResumeHandle;
+ while ( ( i < PurchaseListSize ) && ( BufSize < pPrefMaxLen ) )
+ {
+ if ( ( Level > 0 )
+ || ( PurchaseList[i].AllowedModes & LLS_LICENSE_MODE_ALLOW_PER_SEAT ) )
+ {
+ BufSize += RecordSize;
+ EntriesRead++;
+ }
+
+ i++;
+ }
+
+ TotalEntries = EntriesRead;
+
+ //
+ // If we overflowed the buffer then back up one record.
+ //
+ if (BufSize > pPrefMaxLen)
+ {
+ BufSize -= RecordSize;
+ EntriesRead--;
+ }
+
+ //
+ // Now walk to the end of the list to see how many more records are still
+ // available.
+ //
+ while ( i < PurchaseListSize )
+ {
+ if ( ( Level > 0 )
+ || ( PurchaseList[i].AllowedModes & LLS_LICENSE_MODE_ALLOW_PER_SEAT ) )
+ {
+ TotalEntries++;
+ }
+
+ i++;
+ }
+
+ if (TotalEntries > EntriesRead)
+ Status = STATUS_MORE_ENTRIES;
+
+ //
+ // Reset Enum to correct place.
+ //
+ i = *pResumeHandle;
+
+ //
+ // We now know how many records will fit into the buffer, so allocate space
+ // and fix up pointers so we can copy the information.
+ //
+ BufPtr = MIDL_user_allocate(BufSize);
+ if (BufPtr == NULL) {
+ Status = STATUS_NO_MEMORY;
+ goto LlsrLicenseEnumWExit;
+ }
+
+ RtlZeroMemory((PVOID) BufPtr, BufSize);
+
+ //
+ // Buffers are all setup, so loop through records and copy the data.
+ //
+ while ((j < EntriesRead) && (i < PurchaseListSize))
+ {
+ if ( ( Level > 0 )
+ || ( PurchaseList[i].AllowedModes & LLS_LICENSE_MODE_ALLOW_PER_SEAT ) )
+ {
+ if ( 0 == Level )
+ {
+ ((PLLS_LICENSE_INFO_0W) BufPtr)[j].Product = PurchaseList[i].Service->ServiceName;
+ ((PLLS_LICENSE_INFO_0W) BufPtr)[j].Quantity = PurchaseList[i].NumberLicenses;
+ ((PLLS_LICENSE_INFO_0W) BufPtr)[j].Date = PurchaseList[i].Date;
+ ((PLLS_LICENSE_INFO_0W) BufPtr)[j].Admin = PurchaseList[i].Admin;
+ ((PLLS_LICENSE_INFO_0W) BufPtr)[j].Comment = PurchaseList[i].Comment;
+ }
+ else
+ {
+ ((PLLS_LICENSE_INFO_1W) BufPtr)[j].Product = ( PurchaseList[i].AllowedModes & LLS_LICENSE_MODE_ALLOW_PER_SEAT )
+ ? PurchaseList[i].Service->ServiceName
+ : PurchaseList[i].PerServerService->ServiceName;
+ ((PLLS_LICENSE_INFO_1W) BufPtr)[j].Vendor = PurchaseList[i].Vendor;
+ ((PLLS_LICENSE_INFO_1W) BufPtr)[j].Quantity = PurchaseList[i].NumberLicenses;
+ ((PLLS_LICENSE_INFO_1W) BufPtr)[j].MaxQuantity = PurchaseList[i].MaxQuantity;
+ ((PLLS_LICENSE_INFO_1W) BufPtr)[j].Date = PurchaseList[i].Date;
+ ((PLLS_LICENSE_INFO_1W) BufPtr)[j].Admin = PurchaseList[i].Admin;
+ ((PLLS_LICENSE_INFO_1W) BufPtr)[j].Comment = PurchaseList[i].Comment;
+ ((PLLS_LICENSE_INFO_1W) BufPtr)[j].AllowedModes = PurchaseList[i].AllowedModes;
+ ((PLLS_LICENSE_INFO_1W) BufPtr)[j].CertificateID = PurchaseList[i].CertificateID;
+ ((PLLS_LICENSE_INFO_1W) BufPtr)[j].Source = PurchaseList[i].Source;
+ ((PLLS_LICENSE_INFO_1W) BufPtr)[j].ExpirationDate = PurchaseList[i].ExpirationDate;
+ memcpy( ((PLLS_LICENSE_INFO_1W) BufPtr)[j].Secrets, PurchaseList[i].Secrets, LLS_NUM_SECRETS * sizeof( *PurchaseList[i].Secrets ) );
+ }
+
+ j++;
+ }
+
+ i++;
+ }
+
+LlsrLicenseEnumWExit:
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT(" TotalEntries: %lu EntriesRead: %lu ResumeHandle: 0x%lX\n"), TotalEntries, EntriesRead, i);
+#endif
+ *pTotalEntries = TotalEntries;
+
+ *pResumeHandle = (ULONG) i;
+
+ if ( 0 == Level )
+ {
+ pLicenseInfo->LlsLicenseInfo.Level0->EntriesRead = EntriesRead;
+ pLicenseInfo->LlsLicenseInfo.Level0->Buffer = (PLLS_LICENSE_INFO_0W) BufPtr;
+ }
+ else
+ {
+ pLicenseInfo->LlsLicenseInfo.Level1->EntriesRead = EntriesRead;
+ pLicenseInfo->LlsLicenseInfo.Level1->Buffer = (PLLS_LICENSE_INFO_1W) BufPtr;
+ }
+
+ RtlReleaseResource(&LicenseListLock);
+ return Status;
+} // LlsrLicenseEnumW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrLicenseEnumA(
+ LLS_HANDLE Handle,
+ PLLS_LICENSE_ENUM_STRUCTA LicenseInfo,
+ DWORD PrefMaxLen,
+ LPDWORD TotalEntries,
+ LPDWORD ResumeHandle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsLicenseEnumA\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+} // LlsrLicenseEnumA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrLicenseAddW(
+ LLS_HANDLE Handle,
+ DWORD Level,
+ PLLS_LICENSE_INFOW BufPtr
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ NTSTATUS Status;
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsLicenseAddW\n"));
+#endif
+
+ if ( 0 == Level )
+ {
+ if ( ( NULL == BufPtr )
+ || ( NULL == BufPtr->LicenseInfo0.Product )
+ || ( NULL == BufPtr->LicenseInfo0.Admin )
+ || ( NULL == BufPtr->LicenseInfo0.Comment ) )
+ {
+ Status = STATUS_INVALID_PARAMETER;
+ }
+ else
+ {
+ Status = LicenseAdd( BufPtr->LicenseInfo0.Product,
+ TEXT("Microsoft"),
+ BufPtr->LicenseInfo0.Quantity,
+ 0,
+ BufPtr->LicenseInfo0.Admin,
+ BufPtr->LicenseInfo0.Comment,
+ 0,
+ LLS_LICENSE_MODE_ALLOW_PER_SEAT,
+ 0,
+ TEXT("None"),
+ 0,
+ NULL );
+ }
+ }
+ else if ( 1 == Level )
+ {
+ if ( ( NULL == BufPtr )
+ || ( NULL == BufPtr->LicenseInfo1.Product )
+ || ( NULL == BufPtr->LicenseInfo1.Admin )
+ || ( NULL == BufPtr->LicenseInfo1.Comment )
+ || ( 0 == BufPtr->LicenseInfo1.Quantity )
+ || ( 0 == ( BufPtr->LicenseInfo1.AllowedModes
+ & ( LLS_LICENSE_MODE_ALLOW_PER_SERVER
+ | LLS_LICENSE_MODE_ALLOW_PER_SEAT ) ) ) )
+ {
+ Status = STATUS_INVALID_PARAMETER;
+ }
+ else
+ {
+ // check to see if this certificate is already maxed out in the enterprise
+ BOOL bIsMaster = TRUE;
+ BOOL bMayInstall = TRUE;
+ HINSTANCE hDll = NULL;
+ PLLS_CONNECT_ENTERPRISE_W pLlsConnectEnterpriseW = NULL;
+ PLLS_CLOSE pLlsClose = NULL;
+ PLLS_CAPABILITY_IS_SUPPORTED pLlsCapabilityIsSupported = NULL;
+ PLLS_CERTIFICATE_CLAIM_ADD_CHECK_W pLlsCertificateClaimAddCheckW = NULL;
+ PLLS_CERTIFICATE_CLAIM_ADD_W pLlsCertificateClaimAddW = NULL;
+ PLLS_FREE_MEMORY pLlsFreeMemory = NULL;
+ LLS_HANDLE hEnterpriseLls = NULL;
+ TCHAR szComputerName[ 1 + MAX_COMPUTERNAME_LENGTH ];
+
+ RtlEnterCriticalSection( &ConfigInfoLock );
+ bIsMaster = ConfigInfo.IsMaster;
+ lstrcpy( szComputerName, ConfigInfo.ComputerName );
+ RtlLeaveCriticalSection( &ConfigInfoLock );
+
+ if( !bIsMaster && ( 0 != BufPtr->LicenseInfo1.CertificateID ) )
+ {
+ // ask enterprise server if we can install this certfificate
+ hDll = LoadLibraryA( "LLSRPC.DLL" );
+
+ if ( NULL == hDll )
+ {
+ // LLSRPC.DLL should be available!
+ ASSERT( FALSE );
+ }
+ else
+ {
+ pLlsConnectEnterpriseW = (PLLS_CONNECT_ENTERPRISE_W ) GetProcAddress( hDll, "LlsConnectEnterpriseW" );
+ pLlsClose = (PLLS_CLOSE ) GetProcAddress( hDll, "LlsClose" );
+ pLlsCapabilityIsSupported = (PLLS_CAPABILITY_IS_SUPPORTED ) GetProcAddress( hDll, "LlsCapabilityIsSupported" );
+ pLlsCertificateClaimAddCheckW = (PLLS_CERTIFICATE_CLAIM_ADD_CHECK_W ) GetProcAddress( hDll, "LlsCertificateClaimAddCheckW" );
+ pLlsCertificateClaimAddW = (PLLS_CERTIFICATE_CLAIM_ADD_W ) GetProcAddress( hDll, "LlsCertificateClaimAddW" );
+ pLlsFreeMemory = (PLLS_FREE_MEMORY ) GetProcAddress( hDll, "LlsFreeMemory" );
+
+ if ( ( NULL == pLlsConnectEnterpriseW )
+ || ( NULL == pLlsClose )
+ || ( NULL == pLlsCapabilityIsSupported )
+ || ( NULL == pLlsCertificateClaimAddCheckW )
+ || ( NULL == pLlsCertificateClaimAddW )
+ || ( NULL == pLlsFreeMemory ) )
+ {
+ // All of these functions should be exported!
+ ASSERT( FALSE );
+ }
+ else
+ {
+ PLLS_CONNECT_INFO_0 pConnectInfo;
+
+ Status = (*pLlsConnectEnterpriseW)( NULL, &hEnterpriseLls, 0, (LPBYTE *)&pConnectInfo );
+
+ if ( STATUS_SUCCESS == Status )
+ {
+ (*pLlsFreeMemory)( pConnectInfo );
+
+ if ( (*pLlsCapabilityIsSupported)( hEnterpriseLls, LLS_CAPABILITY_SECURE_CERTIFICATES ) )
+ {
+ Status = (*pLlsCertificateClaimAddCheckW)( hEnterpriseLls, Level, (LPBYTE) BufPtr, &bMayInstall );
+
+ if ( STATUS_SUCCESS != Status )
+ {
+ bMayInstall = TRUE;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if ( !bMayInstall )
+ {
+ // denied!
+ Status = STATUS_ALREADY_COMMITTED;
+ }
+ else
+ {
+ // approved! (or an error occurred trying to get approval...)
+ Status = LicenseAdd( BufPtr->LicenseInfo1.Product,
+ BufPtr->LicenseInfo1.Vendor,
+ BufPtr->LicenseInfo1.Quantity,
+ BufPtr->LicenseInfo1.MaxQuantity,
+ BufPtr->LicenseInfo1.Admin,
+ BufPtr->LicenseInfo1.Comment,
+ 0,
+ BufPtr->LicenseInfo1.AllowedModes,
+ BufPtr->LicenseInfo1.CertificateID,
+ BufPtr->LicenseInfo1.Source,
+ BufPtr->LicenseInfo1.ExpirationDate,
+ BufPtr->LicenseInfo1.Secrets );
+
+ if ( ( STATUS_SUCCESS == Status )
+ && ( NULL != hEnterpriseLls )
+ && ( (*pLlsCapabilityIsSupported)( hEnterpriseLls, LLS_CAPABILITY_SECURE_CERTIFICATES ) ) )
+ {
+ // certificate successfully installed on this machine; register it
+ (*pLlsCertificateClaimAddW)( hEnterpriseLls, szComputerName, Level, (LPBYTE) BufPtr );
+ }
+ }
+
+ if ( NULL != hEnterpriseLls )
+ {
+ (*pLlsClose)( hEnterpriseLls );
+ }
+
+ if ( NULL != hDll )
+ {
+ FreeLibrary( hDll );
+ }
+ }
+ }
+ else
+ {
+ Status = STATUS_INVALID_LEVEL;
+ }
+
+
+ if ( STATUS_SUCCESS == Status )
+ {
+ Status = LicenseListSave();
+ }
+
+ return Status;
+} // LlsrLicenseAddW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrLicenseAddA(
+ LLS_HANDLE Handle,
+ DWORD Level,
+ PLLS_LICENSE_INFOA BufPtr
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsLicenseAddA\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+} // LlsrLicenseAddA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrProductEnumW(
+ LLS_HANDLE Handle,
+ PLLS_PRODUCT_ENUM_STRUCTW pProductInfo,
+ DWORD pPrefMaxLen,
+ LPDWORD pTotalEntries,
+ LPDWORD pResumeHandle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ DWORD Level;
+ ULONG RecSize;
+ PVOID BufPtr = NULL;
+ ULONG BufSize = 0;
+ ULONG EntriesRead = 0;
+ ULONG TotalEntries = 0;
+ ULONG i = 0;
+ ULONG j = 0;
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsProductEnumW\n"));
+#endif
+
+ //
+ // Need to scan list so get read access.
+ //
+ RtlAcquireResourceShared(&MasterServiceListLock, TRUE);
+
+ //
+ // Get size of each record based on info level. Only 0 and 1 supported.
+ //
+ Level = pProductInfo->Level;
+ if (Level == 0)
+ RecSize = sizeof(LLS_PRODUCT_INFO_0W);
+ else if (Level == 1)
+ RecSize = sizeof(LLS_PRODUCT_INFO_1W);
+ else {
+ Status = STATUS_INVALID_LEVEL;
+ goto LlsrProductEnumWExit;
+ }
+
+ //
+ // Calculate how many records will fit into PrefMaxLen buffer. This is
+ // the record size * # records + space for the string data. If MAX_ULONG
+ // is passed in then we return all records.
+ //
+ i = *pResumeHandle;
+ while ((i < MasterServiceListSize) && (BufSize < pPrefMaxLen)) {
+ BufSize += RecSize;
+ EntriesRead++;
+
+ i++;
+ }
+
+ TotalEntries = EntriesRead;
+
+ //
+ // If we overflowed the buffer then back up one record.
+ //
+ if (BufSize > pPrefMaxLen) {
+ BufSize -= RecSize;
+ EntriesRead--;
+ }
+
+ if (i < MasterServiceListSize)
+ Status = STATUS_MORE_ENTRIES;
+
+ //
+ // Now walk to the end of the list to see how many more records are still
+ // available.
+ //
+ TotalEntries += (MasterServiceListSize - i);
+
+ //
+ // Reset Enum to correct place.
+ //
+ i = *pResumeHandle;
+
+ //
+ // We now know how many records will fit into the buffer, so allocate space
+ // and fix up pointers so we can copy the information.
+ //
+ BufPtr = MIDL_user_allocate(BufSize);
+ if (BufPtr == NULL) {
+ Status = STATUS_NO_MEMORY;
+ goto LlsrProductEnumWExit;
+ }
+
+ RtlZeroMemory((PVOID) BufPtr, BufSize);
+
+ //
+ // Buffers are all setup, so loop through records and copy the data.
+ //
+ while ((j < EntriesRead) && (i < MasterServiceListSize)) {
+ if (Level == 0)
+ ((PLLS_PRODUCT_INFO_0) BufPtr)[j].Product = MasterServiceList[i]->Name;
+ else {
+ ((PLLS_PRODUCT_INFO_1) BufPtr)[j].Product = MasterServiceList[i]->Name;
+ ((PLLS_PRODUCT_INFO_1) BufPtr)[j].Purchased = MasterServiceList[i]->Licenses;
+ ((PLLS_PRODUCT_INFO_1) BufPtr)[j].InUse = MasterServiceList[i]->LicensesUsed;
+ ((PLLS_PRODUCT_INFO_1) BufPtr)[j].ConcurrentTotal = MasterServiceList[i]->MaxSessionCount;
+ ((PLLS_PRODUCT_INFO_1) BufPtr)[j].HighMark = MasterServiceList[i]->HighMark;
+ }
+
+ i++; j++;
+ }
+
+LlsrProductEnumWExit:
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT(" TotalEntries: %lu EntriesRead: %lu ResumeHandle: 0x%lX\n"), TotalEntries, EntriesRead, i);
+#endif
+ *pTotalEntries = TotalEntries;
+
+ *pResumeHandle = (ULONG) i;
+ if (Level == 0) {
+ pProductInfo->LlsProductInfo.Level0->EntriesRead = EntriesRead;
+ pProductInfo->LlsProductInfo.Level0->Buffer = (PLLS_PRODUCT_INFO_0W) BufPtr;
+ } else {
+ pProductInfo->LlsProductInfo.Level1->EntriesRead = EntriesRead;
+ pProductInfo->LlsProductInfo.Level1->Buffer = (PLLS_PRODUCT_INFO_1W) BufPtr;
+ }
+
+ RtlReleaseResource(&MasterServiceListLock);
+ return Status;
+
+} // LlsrProductEnumW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrProductEnumA(
+ LLS_HANDLE Handle,
+ PLLS_PRODUCT_ENUM_STRUCTA ProductInfo,
+ DWORD PrefMaxLen,
+ LPDWORD TotalEntries,
+ LPDWORD ResumeHandle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsProductEnumA\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+} // LlsrProductEnumA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrProductAddW(
+ LLS_HANDLE Handle,
+ LPWSTR ProductFamily,
+ LPWSTR Product,
+ LPWSTR lpVersion
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+{
+ PMASTER_SERVICE_RECORD Service;
+ DWORD Version;
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsProductAddW\n"));
+#endif
+
+ if ((ProductFamily == NULL) || (Product == NULL) || (lpVersion == NULL))
+ return STATUS_INVALID_PARAMETER;
+
+ Version = VersionToDWORD(lpVersion);
+ Service = MasterServiceListAdd(ProductFamily, Product, Version);
+
+ if (Service == NULL)
+ return STATUS_NO_MEMORY;
+
+ return STATUS_SUCCESS;
+} // LlsrProductAddW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrProductAddA(
+ LLS_HANDLE Handle,
+ IN LPSTR ProductFamily,
+ IN LPSTR Product,
+ IN LPSTR Version
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+{
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsProductAddA\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+} // LlsrProductAddA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrProductUserEnumW(
+ LLS_HANDLE Handle,
+ LPWSTR Product,
+ PLLS_PRODUCT_USER_ENUM_STRUCTW pProductUserInfo,
+ DWORD pPrefMaxLen,
+ LPDWORD pTotalEntries,
+ LPDWORD pResumeHandle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ DWORD Level;
+ ULONG RecSize;
+ PVOID BufPtr = NULL;
+ ULONG BufSize = 0;
+ ULONG EntriesRead = 0;
+ ULONG TotalEntries = 0;
+ ULONG i = 0;
+ PUSER_RECORD UserRec = NULL;
+ PVOID RestartKey = NULL;
+ PSVC_RECORD pService;
+ DWORD Flags;
+ ULONG j, AccessCount;
+ DWORD LastAccess;
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsProductUserEnumW\n"));
+#endif
+
+ if (Product == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ //
+ // Need AddEnum lock, but just shared UserListLock (as we just read
+ // the data).
+ //
+ RtlAcquireResourceExclusive(&UserListAddEnumLock, TRUE);
+ RtlAcquireResourceShared(&UserListLock, TRUE);
+
+ //
+ // Reset Enum to correct place.
+ //
+ RestartKey = (PVOID) *pResumeHandle;
+ UserRec = (PUSER_RECORD) RtlEnumerateGenericTableWithoutSplaying(&UserList, (VOID **) &RestartKey);
+
+ //
+ // Get size of each record based on info level. Only 0 and 1 supported.
+ //
+ Level = pProductUserInfo->Level;
+ if (Level == 0)
+ RecSize = sizeof(LLS_PRODUCT_USER_INFO_0);
+ else if (Level == 1)
+ RecSize = sizeof(LLS_PRODUCT_USER_INFO_1);
+ else {
+ Status = STATUS_INVALID_LEVEL;
+ goto LlsrProductUserEnumWExit;
+ }
+
+ //
+ // Calculate how many records will fit into PrefMaxLen buffer. This is
+ // the record size * # records + space for the string data. If MAX_ULONG
+ // is passed in then we return all records.
+ //
+ if (lstrcmpi(Product, BackOfficeStr))
+ while ((UserRec != NULL) && (BufSize < pPrefMaxLen)) {
+ if ( !(UserRec->Flags & LLS_FLAG_DELETED) ) {
+ RtlEnterCriticalSection(&UserRec->ServiceTableLock);
+ pService = SvcListFind( Product, UserRec->Services, UserRec->ServiceTableSize );
+ RtlLeaveCriticalSection(&UserRec->ServiceTableLock);
+
+ if (pService != NULL) {
+ BufSize += RecSize;
+ EntriesRead++;
+ }
+ }
+
+ // Get next record
+ UserRec = (PUSER_RECORD) RtlEnumerateGenericTableWithoutSplaying(&UserList, (VOID **) &RestartKey);
+ }
+ else
+ while ((UserRec != NULL) && (BufSize < pPrefMaxLen)) {
+ if (UserRec->Mapping != NULL)
+ Flags = UserRec->Mapping->Flags;
+ else
+ Flags = UserRec->Flags;
+
+ if (!(UserRec->Flags & LLS_FLAG_DELETED))
+ if (Flags & LLS_FLAG_SUITE_USE) {
+ BufSize += RecSize;
+ EntriesRead++;
+ }
+
+ // Get next record
+ UserRec = (PUSER_RECORD) RtlEnumerateGenericTableWithoutSplaying(&UserList, (VOID **) &RestartKey);
+ }
+
+ TotalEntries = EntriesRead;
+
+ //
+ // If we overflowed the buffer then back up one record.
+ //
+ if (BufSize > pPrefMaxLen) {
+ BufSize -= RecSize;
+ EntriesRead--;
+ }
+
+ if (UserRec != NULL)
+ Status = STATUS_MORE_ENTRIES;
+
+ //
+ // Now walk to the end of the list to see how many more records are still
+ // available.
+ //
+ while (UserRec != NULL) {
+ TotalEntries++;
+
+ UserRec = (PUSER_RECORD) RtlEnumerateGenericTableWithoutSplaying(&UserList, (VOID **) &RestartKey);
+ }
+
+ //
+ // Reset Enum to correct place.
+ //
+ RestartKey = (PVOID) *pResumeHandle;
+ UserRec = (PUSER_RECORD) RtlEnumerateGenericTableWithoutSplaying(&UserList, (VOID **) &RestartKey);
+
+ //
+ // We now know how many records will fit into the buffer, so allocate space
+ // and fix up pointers so we can copy the information.
+ //
+ BufPtr = MIDL_user_allocate(BufSize);
+ if (BufPtr == NULL) {
+ Status = STATUS_NO_MEMORY;
+ goto LlsrProductUserEnumWExit;
+ }
+
+ RtlZeroMemory((PVOID) BufPtr, BufSize);
+
+ //
+ // Buffers are all setup, so loop through records and copy the data.
+ //
+ if (lstrcmpi(Product, BackOfficeStr))
+ while ((i < EntriesRead) && (UserRec != NULL)) {
+ if (!(UserRec->Flags & LLS_FLAG_DELETED)) {
+ RtlEnterCriticalSection(&UserRec->ServiceTableLock);
+ pService = SvcListFind( Product, UserRec->Services, UserRec->ServiceTableSize );
+ if (pService != NULL) {
+
+ if (Level == 0)
+ ((PLLS_PRODUCT_USER_INFO_0) BufPtr)[i].User = (LPTSTR) UserRec->UserID;
+ else {
+ ((PLLS_PRODUCT_USER_INFO_1) BufPtr)[i].User = (LPTSTR) UserRec->UserID;
+ ((PLLS_PRODUCT_USER_INFO_1) BufPtr)[i].Flags = pService->Flags;
+ ((PLLS_PRODUCT_USER_INFO_1) BufPtr)[i].LastUsed = pService->LastAccess;
+ ((PLLS_PRODUCT_USER_INFO_1) BufPtr)[i].UsageCount = pService->AccessCount;
+ }
+
+ i++;
+ }
+
+ RtlLeaveCriticalSection(&UserRec->ServiceTableLock);
+ }
+
+ UserRec = (PUSER_RECORD) RtlEnumerateGenericTableWithoutSplaying(&UserList, (VOID **) &RestartKey);
+ }
+ else
+ while ((i < EntriesRead) && (UserRec != NULL)) {
+ if (!(UserRec->Flags & LLS_FLAG_DELETED)) {
+ if (UserRec->Mapping != NULL)
+ Flags = UserRec->Mapping->Flags;
+ else
+ Flags = UserRec->Flags;
+
+ if (!(UserRec->Flags & LLS_FLAG_DELETED))
+ if (Flags & LLS_FLAG_SUITE_USE) {
+ AccessCount = 0;
+ LastAccess = 0;
+
+ RtlEnterCriticalSection(&UserRec->ServiceTableLock);
+ for (j = 0; j < UserRec->ServiceTableSize; j++) {
+ if (UserRec->Services[j].LastAccess > LastAccess)
+ LastAccess = UserRec->Services[j].LastAccess;
+
+ if (UserRec->Services[j].AccessCount > AccessCount)
+ AccessCount = UserRec->Services[j].AccessCount;
+ }
+
+ RtlLeaveCriticalSection(&UserRec->ServiceTableLock);
+ if (Level == 0)
+ ((PLLS_PRODUCT_USER_INFO_0) BufPtr)[i].User = (LPTSTR) UserRec->UserID;
+ else {
+ ((PLLS_PRODUCT_USER_INFO_1) BufPtr)[i].User = (LPTSTR) UserRec->UserID;
+ ((PLLS_PRODUCT_USER_INFO_1) BufPtr)[i].Flags = UserRec->Flags;
+ ((PLLS_PRODUCT_USER_INFO_1) BufPtr)[i].LastUsed = LastAccess;
+ ((PLLS_PRODUCT_USER_INFO_1) BufPtr)[i].UsageCount = AccessCount;
+ }
+
+ i++;
+ }
+
+ }
+
+ UserRec = (PUSER_RECORD) RtlEnumerateGenericTableWithoutSplaying(&UserList, (VOID **) &RestartKey);
+ }
+
+LlsrProductUserEnumWExit:
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT(" TotalEntries: %lu EntriesRead: %lu ResumeHandle: 0x%lX\n"), TotalEntries, EntriesRead, RestartKey);
+#endif
+ *pTotalEntries = TotalEntries;
+ *pResumeHandle = (ULONG) RestartKey;
+ pProductUserInfo->LlsProductUserInfo.Level0->EntriesRead = EntriesRead;
+ pProductUserInfo->LlsProductUserInfo.Level0->Buffer = (PLLS_PRODUCT_USER_INFO_0W) BufPtr;
+
+ RtlReleaseResource(&UserListAddEnumLock);
+ RtlReleaseResource(&UserListLock);
+ return Status;
+} // LlsrProductUserEnumW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrProductUserEnumA(
+ LLS_HANDLE Handle,
+ LPSTR Product,
+ PLLS_PRODUCT_USER_ENUM_STRUCTA ProductUserInfo,
+ DWORD PrefMaxLen,
+ LPDWORD TotalEntries,
+ LPDWORD ResumeHandle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsProductUserEnumA\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+} // LlsrProductUserEnumA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrProductServerEnumW(
+ LLS_HANDLE Handle,
+ LPTSTR Product,
+ PLLS_SERVER_PRODUCT_ENUM_STRUCTW pProductServerInfo,
+ DWORD pPrefMaxLen,
+ LPDWORD pTotalEntries,
+ LPDWORD pResumeHandle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ DWORD Level;
+ ULONG RecSize;
+ PVOID BufPtr = NULL;
+ ULONG BufSize = 0;
+ ULONG EntriesRead = 0;
+ ULONG TotalEntries = 0;
+ ULONG i = 0;
+ ULONG j;
+ ULONG RestartKey = 0;
+ PSERVER_SERVICE_RECORD pSvc;
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsProductServerEnumW\n"));
+#endif
+
+ if (Product == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ //
+ // Reset Enum to correct place.
+ //
+ RestartKey = (ULONG) *pResumeHandle;
+
+ //
+ // Get size of each record based on info level. Only 0 and 1 supported.
+ //
+ Level = pProductServerInfo->Level;
+ RtlAcquireResourceShared(&ServerListLock, TRUE);
+ if (Level == 0)
+ RecSize = sizeof(LLS_SERVER_PRODUCT_INFO_0);
+ else if (Level == 1)
+ RecSize = sizeof(LLS_SERVER_PRODUCT_INFO_1);
+ else {
+ Status = STATUS_INVALID_LEVEL;
+ goto LlsrProductServerEnumWExit;
+ }
+
+
+ //
+ // Calculate how many records will fit into PrefMaxLen buffer. This is
+ // the record size * # records + space for the string data. If MAX_ULONG
+ // is passed in then we return all records.
+ //
+ for (i = RestartKey; i < ServerListSize; i++) {
+ pSvc = ServerServiceListFind( Product, ServerList[i]->ServiceTableSize, ServerList[i]->Services );
+
+ if (pSvc != NULL) {
+ BufSize += RecSize;
+ EntriesRead++;
+ }
+
+ }
+
+ TotalEntries = EntriesRead;
+
+ //
+ // We now know how many records will fit into the buffer, so allocate space
+ // and fix up pointers so we can copy the information.
+ //
+ BufPtr = MIDL_user_allocate(BufSize);
+ if (BufPtr == NULL) {
+ Status = STATUS_NO_MEMORY;
+ goto LlsrProductServerEnumWExit;
+ }
+
+ RtlZeroMemory((PVOID) BufPtr, BufSize);
+
+ //
+ // Buffers are all setup, so loop through records and copy the data.
+ //
+ j = 0;
+ for (i = RestartKey; i < ServerListSize; i++) {
+ pSvc = ServerServiceListFind( Product, ServerList[i]->ServiceTableSize, ServerList[i]->Services );
+
+ if (pSvc != NULL) {
+
+ if (Level == 0)
+ ((PLLS_SERVER_PRODUCT_INFO_0) BufPtr)[j].Name = ServerList[i]->Name;
+ else {
+ ((PLLS_SERVER_PRODUCT_INFO_1) BufPtr)[j].Name = ServerList[i]->Name;
+ ((PLLS_SERVER_PRODUCT_INFO_1) BufPtr)[j].Flags = pSvc->Flags;
+ ((PLLS_SERVER_PRODUCT_INFO_1) BufPtr)[j].MaxUses = pSvc->MaxSessionCount;
+ ((PLLS_SERVER_PRODUCT_INFO_1) BufPtr)[j].MaxSetUses = pSvc->MaxSetSessionCount;
+ ((PLLS_SERVER_PRODUCT_INFO_1) BufPtr)[j].HighMark = pSvc->HighMark;
+ }
+
+ j++;
+ }
+
+ }
+
+LlsrProductServerEnumWExit:
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT(" TotalEntries: %lu EntriesRead: %lu ResumeHandle: 0x%lX\n"), TotalEntries, EntriesRead, RestartKey);
+#endif
+ *pTotalEntries = TotalEntries;
+ *pResumeHandle = (ULONG) RestartKey;
+ pProductServerInfo->LlsServerProductInfo.Level0->EntriesRead = EntriesRead;
+ pProductServerInfo->LlsServerProductInfo.Level0->Buffer = (PLLS_SERVER_PRODUCT_INFO_0W) BufPtr;
+
+ RtlReleaseResource(&ServerListLock);
+ return Status;
+} // LlsrProductServerEnumW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrProductServerEnumA(
+ LLS_HANDLE Handle,
+ LPSTR Product,
+ PLLS_SERVER_PRODUCT_ENUM_STRUCTA ProductServerInfo,
+ DWORD PrefMaxLen,
+ LPDWORD TotalEntries,
+ LPDWORD ResumeHandle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsProductServerEnumA\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+} // LlsrProductServerEnumA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrProductLicenseEnumW(
+ LLS_HANDLE Handle,
+ LPWSTR Product,
+ PLLS_PRODUCT_LICENSE_ENUM_STRUCTW pProductLicenseInfo,
+ DWORD pPrefMaxLen,
+ LPDWORD pTotalEntries,
+ LPDWORD pResumeHandle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ DWORD Level;
+ PVOID BufPtr = NULL;
+ ULONG BufSize = 0;
+ ULONG EntriesRead = 0;
+ ULONG TotalEntries = 0;
+ ULONG i = 0;
+ ULONG j = 0;
+ DWORD RecordSize;
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsProductLicenseEnumW\n"));
+#endif
+
+ Level = pProductLicenseInfo->Level;
+
+ if ( 0 == Level )
+ {
+ RecordSize = sizeof( LLS_PRODUCT_LICENSE_INFO_0W );
+ }
+ else if ( 1 == Level )
+ {
+ RecordSize = sizeof( LLS_PRODUCT_LICENSE_INFO_1W );
+ }
+ else
+ {
+ return STATUS_INVALID_LEVEL;
+ }
+
+ //
+ // Need to scan list so get read access.
+ //
+ RtlAcquireResourceShared(&LicenseListLock, TRUE);
+
+ //
+ // Calculate how many records will fit into PrefMaxLen buffer.
+ //
+ i = *pResumeHandle;
+ while ( ( i < PurchaseListSize ) && ( BufSize < pPrefMaxLen ) )
+ {
+ // level 0 enums return only per seat licenses for backwards compatibility
+ if ( ( ( ( PurchaseList[i].AllowedModes & LLS_LICENSE_MODE_ALLOW_PER_SEAT )
+ && !lstrcmpi( PurchaseList[i].Service->ServiceName, Product ) )
+ || ( ( PurchaseList[i].AllowedModes & LLS_LICENSE_MODE_ALLOW_PER_SERVER )
+ && !lstrcmpi( PurchaseList[i].PerServerService->ServiceName, Product ) ) )
+ && ( ( Level > 0 )
+ || ( PurchaseList[i].AllowedModes & LLS_LICENSE_MODE_ALLOW_PER_SEAT ) ) )
+ {
+ BufSize += RecordSize;
+ EntriesRead++;
+ }
+
+ i++;
+ }
+
+ TotalEntries = EntriesRead;
+
+ //
+ // If we overflowed the buffer then back up one record.
+ //
+ if (BufSize > pPrefMaxLen)
+ {
+ BufSize -= RecordSize;
+ EntriesRead--;
+ }
+
+ //
+ // Now walk to the end of the list to see how many more records are still
+ // available.
+ //
+ while ( i < PurchaseListSize )
+ {
+ if ( ( ( ( PurchaseList[i].AllowedModes & LLS_LICENSE_MODE_ALLOW_PER_SEAT )
+ && !lstrcmpi( PurchaseList[i].Service->ServiceName, Product ) )
+ || ( ( PurchaseList[i].AllowedModes & LLS_LICENSE_MODE_ALLOW_PER_SERVER )
+ && !lstrcmpi( PurchaseList[i].PerServerService->ServiceName, Product ) ) )
+ && ( ( Level > 0 )
+ || ( PurchaseList[i].AllowedModes & LLS_LICENSE_MODE_ALLOW_PER_SEAT ) ) )
+ {
+ TotalEntries++;
+ }
+
+ i++;
+ }
+
+ if (TotalEntries > EntriesRead)
+ Status = STATUS_MORE_ENTRIES;
+
+ //
+ // Reset Enum to correct place.
+ //
+ i = *pResumeHandle;
+
+ //
+ // We now know how many records will fit into the buffer, so allocate space
+ // and fix up pointers so we can copy the information.
+ //
+ BufPtr = MIDL_user_allocate(BufSize);
+ if (BufPtr == NULL) {
+ Status = STATUS_NO_MEMORY;
+ goto LlsrLicenseEnumWExit;
+ }
+
+ RtlZeroMemory((PVOID) BufPtr, BufSize);
+
+ //
+ // Buffers are all setup, so loop through records and copy the data.
+ //
+ while ((j < EntriesRead) && (i < PurchaseListSize))
+ {
+ if ( ( ( ( PurchaseList[i].AllowedModes & LLS_LICENSE_MODE_ALLOW_PER_SEAT )
+ && !lstrcmpi( PurchaseList[i].Service->ServiceName, Product ) )
+ || ( ( PurchaseList[i].AllowedModes & LLS_LICENSE_MODE_ALLOW_PER_SERVER )
+ && !lstrcmpi( PurchaseList[i].PerServerService->ServiceName, Product ) ) )
+ && ( ( Level > 0 )
+ || ( PurchaseList[i].AllowedModes & LLS_LICENSE_MODE_ALLOW_PER_SEAT ) ) )
+ {
+ if ( 0 == Level )
+ {
+ ((PLLS_PRODUCT_LICENSE_INFO_0W) BufPtr)[j].Quantity = PurchaseList[i].NumberLicenses;
+ ((PLLS_PRODUCT_LICENSE_INFO_0W) BufPtr)[j].Date = PurchaseList[i].Date;
+ ((PLLS_PRODUCT_LICENSE_INFO_0W) BufPtr)[j].Admin = PurchaseList[i].Admin;
+ ((PLLS_PRODUCT_LICENSE_INFO_0W) BufPtr)[j].Comment = PurchaseList[i].Comment;
+ }
+ else
+ {
+ ((PLLS_PRODUCT_LICENSE_INFO_1W) BufPtr)[j].Quantity = PurchaseList[i].NumberLicenses;
+ ((PLLS_PRODUCT_LICENSE_INFO_1W) BufPtr)[j].MaxQuantity = PurchaseList[i].MaxQuantity;
+ ((PLLS_PRODUCT_LICENSE_INFO_1W) BufPtr)[j].Date = PurchaseList[i].Date;
+ ((PLLS_PRODUCT_LICENSE_INFO_1W) BufPtr)[j].Admin = PurchaseList[i].Admin;
+ ((PLLS_PRODUCT_LICENSE_INFO_1W) BufPtr)[j].Comment = PurchaseList[i].Comment;
+ ((PLLS_PRODUCT_LICENSE_INFO_1W) BufPtr)[j].AllowedModes = PurchaseList[i].AllowedModes;
+ ((PLLS_PRODUCT_LICENSE_INFO_1W) BufPtr)[j].CertificateID = PurchaseList[i].CertificateID;
+ ((PLLS_PRODUCT_LICENSE_INFO_1W) BufPtr)[j].Source = PurchaseList[i].Source;
+ ((PLLS_PRODUCT_LICENSE_INFO_1W) BufPtr)[j].ExpirationDate = PurchaseList[i].ExpirationDate;
+ memcpy( ((PLLS_PRODUCT_LICENSE_INFO_1W) BufPtr)[j].Secrets, PurchaseList[i].Secrets, LLS_NUM_SECRETS * sizeof( *PurchaseList[i].Secrets ) );
+ }
+
+ j++;
+ }
+
+ i++;
+ }
+
+LlsrLicenseEnumWExit:
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT(" TotalEntries: %lu EntriesRead: %lu ResumeHandle: 0x%lX\n"), TotalEntries, EntriesRead, i);
+#endif
+ *pTotalEntries = TotalEntries;
+
+ *pResumeHandle = (ULONG) i;
+
+ if ( 0 == Level )
+ {
+ pProductLicenseInfo->LlsProductLicenseInfo.Level0->EntriesRead = EntriesRead;
+ pProductLicenseInfo->LlsProductLicenseInfo.Level0->Buffer = (PLLS_PRODUCT_LICENSE_INFO_0W) BufPtr;
+ }
+ else
+ {
+ pProductLicenseInfo->LlsProductLicenseInfo.Level1->EntriesRead = EntriesRead;
+ pProductLicenseInfo->LlsProductLicenseInfo.Level1->Buffer = (PLLS_PRODUCT_LICENSE_INFO_1W) BufPtr;
+ }
+
+ RtlReleaseResource(&LicenseListLock);
+ return Status;
+
+} // LlsrProductLicenseEnumW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrProductLicenseEnumA(
+ LLS_HANDLE Handle,
+ LPSTR Product,
+ PLLS_PRODUCT_LICENSE_ENUM_STRUCTA ProductLicenseInfo,
+ DWORD PrefMaxLen,
+ LPDWORD TotalEntries,
+ LPDWORD ResumeHandle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsProductLicenseEnumA\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+} // LlsrProductLicenseEnumA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrUserEnumW(
+ LLS_HANDLE Handle,
+ PLLS_USER_ENUM_STRUCTW pUserInfo,
+ DWORD pPrefMaxLen,
+ LPDWORD pTotalEntries,
+ LPDWORD pResumeHandle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+ pPrefMaxLen - Supplies the number of bytes of information to return
+ in the buffer. If this value is MAXULONG, all available information
+ will be returned.
+
+ pTotalEntries - Returns the total number of entries available. This value
+ is only valid if the return code is STATUS_SUCCESS or STATUS_MORE_ENTRIES.
+
+ pResumeHandle - Supplies a handle to resume the enumeration from where it
+ left off the last time through. Returns the resume handle if return
+ code is STATUS_MORE_ENTRIES.
+
+Return Value:
+
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ DWORD Level;
+ ULONG RecSize;
+ PVOID BufPtr = NULL;
+ ULONG BufSize = 0;
+ ULONG EntriesRead = 0;
+ ULONG TotalEntries = 0;
+ ULONG i = 0;
+ ULONG j;
+ PUSER_RECORD UserRec = NULL;
+ PVOID RestartKey = NULL;
+ ULONG StrSize;
+ LPTSTR ProductString = NULL;
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsUserEnumW\n"));
+#endif
+
+ //
+ // Need AddEnum lock, but just shared UserListLock (as we just read
+ // the data).
+ //
+ RtlAcquireResourceExclusive(&UserListAddEnumLock, TRUE);
+ RtlAcquireResourceShared(&UserListLock, TRUE);
+
+ //
+ // Reset Enum to correct place.
+ //
+ RestartKey = (PVOID) *pResumeHandle;
+ UserRec = (PUSER_RECORD) RtlEnumerateGenericTableWithoutSplaying(&UserList, (VOID **) &RestartKey);
+
+ //
+ // Get size of each record based on info level. Only 0 and 1 supported.
+ //
+ Level = pUserInfo->Level;
+ if (Level == 0)
+ RecSize = sizeof(LLS_USER_INFO_0);
+ else if (Level == 1)
+ RecSize = sizeof(LLS_USER_INFO_1);
+ else if (Level == 2)
+ RecSize = sizeof(LLS_USER_INFO_2);
+ else {
+ Status = STATUS_INVALID_LEVEL;
+ goto LlsrUserEnumWExit;
+ }
+
+ //
+ // Calculate how many records will fit into PrefMaxLen buffer. This is
+ // the record size * # records + space for the string data. If MAX_ULONG
+ // is passed in then we return all records.
+ //
+ while ((UserRec != NULL) && (BufSize < pPrefMaxLen)) {
+ if (!(UserRec->Flags & LLS_FLAG_DELETED)) {
+ BufSize += RecSize;
+ EntriesRead++;
+ }
+
+ // Get next record
+ UserRec = (PUSER_RECORD) RtlEnumerateGenericTableWithoutSplaying(&UserList, (VOID **) &RestartKey);
+ }
+
+ TotalEntries = EntriesRead;
+
+ //
+ // If we overflowed the buffer then back up one record.
+ //
+ if (BufSize > pPrefMaxLen) {
+ BufSize -= RecSize;
+ EntriesRead--;
+ }
+
+ if (UserRec != NULL)
+ Status = STATUS_MORE_ENTRIES;
+
+ //
+ // Now walk to the end of the list to see how many more records are still
+ // available.
+ //
+ while (UserRec != NULL) {
+ TotalEntries++;
+
+ UserRec = (PUSER_RECORD) RtlEnumerateGenericTableWithoutSplaying(&UserList, (VOID **) &RestartKey);
+ }
+
+ //
+ // Reset Enum to correct place.
+ //
+ RestartKey = (PVOID) *pResumeHandle;
+ UserRec = (PUSER_RECORD) RtlEnumerateGenericTableWithoutSplaying(&UserList, (VOID **) &RestartKey);
+
+ //
+ // We now know how many records will fit into the buffer, so allocate space
+ // and fix up pointers so we can copy the information.
+ //
+ BufPtr = MIDL_user_allocate(BufSize);
+ if (BufPtr == NULL) {
+ Status = STATUS_NO_MEMORY;
+ goto LlsrUserEnumWExit;
+ }
+
+ RtlZeroMemory((PVOID) BufPtr, BufSize);
+
+ //
+ // Buffers are all setup, so loop through records and copy the data.
+ //
+ while ((i < EntriesRead) && (UserRec != NULL)) {
+ if (!(UserRec->Flags & LLS_FLAG_DELETED)) {
+
+ if (Level == 0)
+ ((PLLS_USER_INFO_0) BufPtr)[i].Name = (LPTSTR) UserRec->UserID;
+ else if (Level == 1) {
+ ((PLLS_USER_INFO_1) BufPtr)[i].Name = (LPTSTR) UserRec->UserID;
+
+ if (UserRec->Mapping != NULL)
+ ((PLLS_USER_INFO_1) BufPtr)[i].Group = UserRec->Mapping->Name;
+ else
+ ((PLLS_USER_INFO_1) BufPtr)[i].Group = NULL;
+
+ ((PLLS_USER_INFO_1) BufPtr)[i].Licensed = UserRec->LicensedProducts;
+ ((PLLS_USER_INFO_1) BufPtr)[i].UnLicensed = UserRec->ServiceTableSize - UserRec->LicensedProducts;
+
+ ((PLLS_USER_INFO_1) BufPtr)[i].Flags = UserRec->Flags;
+ } else {
+ ((PLLS_USER_INFO_2) BufPtr)[i].Name = (LPTSTR) UserRec->UserID;
+
+ if (UserRec->Mapping != NULL)
+ ((PLLS_USER_INFO_2) BufPtr)[i].Group = UserRec->Mapping->Name;
+ else
+ ((PLLS_USER_INFO_2) BufPtr)[i].Group = NULL;
+
+ ((PLLS_USER_INFO_2) BufPtr)[i].Licensed = UserRec->LicensedProducts;
+ ((PLLS_USER_INFO_2) BufPtr)[i].UnLicensed = UserRec->ServiceTableSize - UserRec->LicensedProducts;
+
+ ((PLLS_USER_INFO_2) BufPtr)[i].Flags = UserRec->Flags;
+
+ //
+ // Walk product table and build up product string
+ //
+ RtlEnterCriticalSection(&UserRec->ServiceTableLock);
+ StrSize = 0;
+
+ for (j = 0; j < UserRec->ServiceTableSize; j++)
+ StrSize += ((lstrlen(UserRec->Services[j].Service->Name) + 2) * sizeof(TCHAR));
+
+ if (StrSize != 0) {
+ ProductString = MIDL_user_allocate(StrSize);
+ if (ProductString != NULL) {
+ lstrcpy(ProductString, TEXT(""));
+
+ for (j = 0; j < UserRec->ServiceTableSize; j++) {
+ if (j != 0)
+ lstrcat(ProductString, TEXT(", "));
+
+ lstrcat(ProductString, UserRec->Services[j].Service->Name);
+ }
+
+ ((PLLS_USER_INFO_2) BufPtr)[i].Products = ProductString;
+ }
+ }
+
+ if ((StrSize == 0) || (ProductString == NULL)) {
+ ProductString = MIDL_user_allocate(2 * sizeof(TCHAR));
+ if (ProductString != NULL) {
+ lstrcpy(ProductString, TEXT(""));
+ ((PLLS_USER_INFO_2) BufPtr)[i].Products = ProductString;
+ }
+ }
+
+ RtlLeaveCriticalSection(&UserRec->ServiceTableLock);
+ }
+
+ i++;
+ }
+
+ UserRec = (PUSER_RECORD) RtlEnumerateGenericTableWithoutSplaying(&UserList, (VOID **) &RestartKey);
+ }
+
+LlsrUserEnumWExit:
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT(" TotalEntries: %lu EntriesRead: %lu ResumeHandle: 0x%lX\n"), TotalEntries, EntriesRead, RestartKey);
+#endif
+
+ *pTotalEntries = TotalEntries;
+ *pResumeHandle = (ULONG) RestartKey;
+ pUserInfo->LlsUserInfo.Level0->EntriesRead = EntriesRead;
+ pUserInfo->LlsUserInfo.Level0->Buffer = (PLLS_USER_INFO_0W) BufPtr;
+
+ RtlReleaseResource(&UserListAddEnumLock);
+ RtlReleaseResource(&UserListLock);
+ return Status;
+} // LlsrUserEnumW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrUserEnumA(
+ LLS_HANDLE Handle,
+ PLLS_USER_ENUM_STRUCTA UserInfo,
+ DWORD PrefMaxLen,
+ LPDWORD TotalEntries,
+ LPDWORD ResumeHandle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsUserEnumA\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+} // LlsrUserEnumA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrUserInfoGetW(
+ LLS_HANDLE Handle,
+ LPWSTR User,
+ DWORD Level,
+ PLLS_USER_INFOW *BufPtr
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ PUSER_RECORD UserRec = NULL;
+ PLLS_USER_INFOW pUser = NULL;
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsUserInfoGetW\n"));
+#endif
+
+ if (Level != 1)
+ return STATUS_INVALID_LEVEL;
+
+ if ((User == NULL) || (BufPtr == NULL))
+ return STATUS_INVALID_PARAMETER;
+
+ RtlAcquireResourceShared(&UserListLock, TRUE);
+
+ UserRec = UserListFind(User);
+
+ if (UserRec != NULL) {
+ pUser = MIDL_user_allocate(sizeof(LLS_USER_INFOW));
+ if (pUser != NULL) {
+ pUser->UserInfo1.Name = (LPTSTR) UserRec->UserID;
+
+ if (UserRec->Mapping != NULL) {
+ pUser->UserInfo1.Mapping = UserRec->Mapping->Name;
+ pUser->UserInfo1.Licensed = UserRec->Mapping->Licenses;
+ pUser->UserInfo1.UnLicensed = 0;
+ } else {
+ pUser->UserInfo1.Mapping = NULL;
+ pUser->UserInfo1.Licensed = 1;
+ pUser->UserInfo1.UnLicensed = 0;
+ }
+
+ pUser->UserInfo1.Flags = UserRec->Flags;
+ }
+ }
+
+ RtlReleaseResource(&UserListLock);
+
+ if (UserRec == NULL)
+ return STATUS_OBJECT_NAME_NOT_FOUND;
+
+ if (pUser == NULL)
+ return STATUS_NO_MEMORY;
+
+ *BufPtr = (PLLS_USER_INFOW) pUser;
+ return STATUS_SUCCESS;
+} // LlsrUserInfoGetW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrUserInfoGetA(
+ LLS_HANDLE Handle,
+ LPSTR User,
+ DWORD Level,
+ PLLS_USER_INFOA *BufPtr
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsUserInfoGetA\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+} // LlsrUserInfoGetA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrUserInfoSetW(
+ LLS_HANDLE Handle,
+ LPWSTR User,
+ DWORD Level,
+ PLLS_USER_INFOW BufPtr
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ NTSTATUS Status;
+ PUSER_RECORD UserRec = NULL;
+ PLLS_USER_INFO_1 pUser;
+ PMAPPING_RECORD pMap;
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsUserInfoSetW\n"));
+#endif
+
+ if (Level != 1)
+ return STATUS_INVALID_LEVEL;
+
+ if ((User == NULL) || (BufPtr == NULL))
+ return STATUS_INVALID_PARAMETER;
+
+ RtlAcquireResourceShared(&UserListLock, TRUE);
+
+ UserRec = UserListFind(User);
+
+ if (UserRec != NULL) {
+ pUser = (PLLS_USER_INFO_1) BufPtr;
+
+ //
+ // If in a mapping can't change SUITE_USE, since it is based on the
+ // License Group
+ //
+ if (UserRec->Mapping != NULL) {
+ RtlReleaseResource(&UserListLock);
+ return STATUS_MEMBER_IN_GROUP;
+ }
+
+ RtlConvertSharedToExclusive(&UserListLock);
+
+ //
+ // Reset SUITE_USE and turn off SUITE_AUTO
+ //
+ pUser->Flags &= LLS_FLAG_SUITE_USE;
+ UserRec->Flags &= ~LLS_FLAG_SUITE_USE;
+ UserRec->Flags |= pUser->Flags;
+ UserRec->Flags &= ~LLS_FLAG_SUITE_AUTO;
+
+ //
+ // Run though and clean up all old licenses
+ //
+ UserLicenseListFree( UserRec );
+
+ //
+ // Now assign new ones
+ //
+ SvcListLicenseUpdate( UserRec );
+
+ }
+
+ RtlReleaseResource(&UserListLock);
+
+ if (UserRec == NULL)
+ Status = STATUS_OBJECT_NAME_NOT_FOUND;
+ else
+ Status = LLSDataSave();
+
+ return Status;
+} // LlsrUserInfoSetW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrUserInfoSetA(
+ LLS_HANDLE Handle,
+ LPSTR User,
+ DWORD Level,
+ PLLS_USER_INFOA BufPtr
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsUserInfoSetA\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+} // LlsrUserInfoSetA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrUserDeleteW(
+ LLS_HANDLE Handle,
+ LPTSTR User
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ NTSTATUS Status;
+ PUSER_RECORD UserRec = NULL;
+ PLLS_USER_INFO_1 pUser;
+ PMAPPING_RECORD pMap;
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ if (User != NULL)
+ dprintf(TEXT("LLS TRACE: LlsUserDeleteW: %s\n"), User);
+ else
+ dprintf(TEXT("LLS TRACE: LlsUserDeleteW: <NULL>\n"));
+#endif
+
+ if (User == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ RtlAcquireResourceShared(&UserListLock, TRUE);
+
+ UserRec = UserListFind(User);
+
+ if (UserRec != NULL) {
+ RtlConvertSharedToExclusive(&UserListLock);
+
+ UserRec->Flags |= LLS_FLAG_DELETED;
+ UsersDeleted = TRUE;
+ SvcListLicenseFree(UserRec);
+ UserLicenseListFree(UserRec);
+
+ if (UserRec->Services != NULL)
+ LocalFree(UserRec->Services);
+
+ UserRec->Services = NULL;
+ UserRec->ServiceTableSize = 0;
+ }
+
+ RtlReleaseResource(&UserListLock);
+
+ if (UserRec == NULL)
+ Status = STATUS_OBJECT_NAME_NOT_FOUND;
+ else
+ Status = LLSDataSave();
+
+ return Status;
+
+} // LlsrUserDeleteW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrUserDeleteA(
+ LLS_HANDLE Handle,
+ LPSTR User
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsUserDeleteA\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+} // LlsrUserDeleteA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrUserProductEnumW(
+ LLS_HANDLE Handle,
+ LPWSTR pUser,
+ PLLS_USER_PRODUCT_ENUM_STRUCTW pUserProductInfo,
+ DWORD pPrefMaxLen,
+ LPDWORD pTotalEntries,
+ LPDWORD pResumeHandle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ DWORD Level;
+ ULONG RecSize;
+ PVOID BufPtr = NULL;
+ ULONG BufSize = 0;
+ ULONG EntriesRead = 0;
+ ULONG TotalEntries = 0;
+ ULONG i = 0;
+ ULONG j = 0;
+ PUSER_RECORD UserRec = NULL;
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsUserProductEnumW\n"));
+#endif
+
+ if ( pUser == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ //
+ // Get size of each record based on info level. Only 0 and 1 supported.
+ //
+ Level = pUserProductInfo->Level;
+ if (Level == 0)
+ RecSize = sizeof(LLS_USER_PRODUCT_INFO_0);
+ else if (Level == 1)
+ RecSize = sizeof(LLS_USER_PRODUCT_INFO_1);
+ else {
+ return STATUS_INVALID_LEVEL;
+ }
+
+ //
+ // Need to find the user-rec
+ //
+ RtlAcquireResourceShared(&UserListLock, TRUE);
+
+ //
+ // Reset Enum to correct place.
+ //
+ UserRec = UserListFind(pUser);
+ if (UserRec == NULL) {
+ Status = STATUS_OBJECT_NAME_NOT_FOUND;
+ goto LlsrUserProductEnumWExit;
+ }
+
+ i = (ULONG) *pResumeHandle;
+ RtlEnterCriticalSection(&UserRec->ServiceTableLock);
+
+ //
+ // Calculate how many records will fit into PrefMaxLen buffer. This is
+ // the record size * # records + space for the string data. If MAX_ULONG
+ // is passed in then we return all records.
+ //
+ while ((i < UserRec->ServiceTableSize) && (BufSize < pPrefMaxLen)) {
+ BufSize += RecSize;
+ EntriesRead++;
+ i++;
+ }
+
+ TotalEntries = EntriesRead;
+
+ //
+ // If we overflowed the buffer then back up one record.
+ //
+ if (BufSize > pPrefMaxLen) {
+ BufSize -= RecSize;
+ EntriesRead--;
+ }
+
+ if (i < UserRec->ServiceTableSize)
+ Status = STATUS_MORE_ENTRIES;
+
+ //
+ // Now walk to the end of the list to see how many more records are still
+ // available.
+ //
+ TotalEntries += (UserRec->ServiceTableSize - i);
+
+ //
+ // We now know how many records will fit into the buffer, so allocate space
+ // and fix up pointers so we can copy the information.
+ //
+ BufPtr = MIDL_user_allocate(BufSize);
+ if (BufPtr == NULL) {
+ Status = STATUS_NO_MEMORY;
+ RtlLeaveCriticalSection(&UserRec->ServiceTableLock);
+ goto LlsrUserProductEnumWExit;
+ }
+
+ RtlZeroMemory((PVOID) BufPtr, BufSize);
+
+ //
+ // Buffers are all setup, so loop through records and copy the data.
+ //
+ j = 0;
+ i = (ULONG) *pResumeHandle;
+ while ((j < EntriesRead) && (i < UserRec->ServiceTableSize)) {
+
+ if (Level == 0)
+ ((PLLS_USER_PRODUCT_INFO_0) BufPtr)[j].Product = UserRec->Services[i].Service->Name;
+ else {
+ ((PLLS_USER_PRODUCT_INFO_1) BufPtr)[j].Product = UserRec->Services[i].Service->Name;
+ ((PLLS_USER_PRODUCT_INFO_1) BufPtr)[j].Flags = UserRec->Services[i].Flags;
+ ((PLLS_USER_PRODUCT_INFO_1) BufPtr)[j].LastUsed = UserRec->Services[i].LastAccess;
+ ((PLLS_USER_PRODUCT_INFO_1) BufPtr)[j].UsageCount = UserRec->Services[i].AccessCount;
+ }
+
+ i++; j++;
+ }
+
+ RtlLeaveCriticalSection(&UserRec->ServiceTableLock);
+
+LlsrUserProductEnumWExit:
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT(" TotalEntries: %lu EntriesRead: %lu ResumeHandle: 0x%lX\n"), TotalEntries, EntriesRead, i);
+#endif
+ *pTotalEntries = TotalEntries;
+ *pResumeHandle = (ULONG) i;
+ pUserProductInfo->LlsUserProductInfo.Level0->EntriesRead = EntriesRead;
+ pUserProductInfo->LlsUserProductInfo.Level0->Buffer = (PLLS_USER_PRODUCT_INFO_0W) BufPtr;
+
+ RtlReleaseResource(&UserListLock);
+ return Status;
+} // LlsrUserProductEnumW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrUserProductEnumA(
+ LLS_HANDLE Handle,
+ LPSTR User,
+ PLLS_USER_PRODUCT_ENUM_STRUCTA UserProductInfo,
+ DWORD PrefMaxLen,
+ LPDWORD TotalEntries,
+ LPDWORD ResumeHandle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsUserProductEnumA\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+} // LlsrUserProductEnumA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrUserProductDeleteW(
+ LLS_HANDLE Handle,
+ LPWSTR User,
+ LPWSTR Product
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ NTSTATUS Status;
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsUserProductDeleteW\n"));
+#endif
+
+ if ((User == NULL) || (Product == NULL))
+ return STATUS_INVALID_PARAMETER;
+
+ RtlAcquireResourceShared(&UserListLock, TRUE);
+ Status = SvcListDelete(User, Product);
+ RtlReleaseResource(&UserListLock);
+
+ if ( STATUS_SUCCESS == Status )
+ {
+ // save modified data
+ Status = LLSDataSave();
+ }
+
+ return Status;
+} // LlsrUserProductDeleteW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrUserProductDeleteA(
+ LLS_HANDLE Handle,
+ LPSTR User,
+ LPSTR Product
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsUserProductDeleteA\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+} // LlsrUserProductDeleteA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrMappingEnumW(
+ LLS_HANDLE Handle,
+ PLLS_MAPPING_ENUM_STRUCTW pMappingInfo,
+ DWORD pPrefMaxLen,
+ LPDWORD pTotalEntries,
+ LPDWORD pResumeHandle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ DWORD Level;
+ ULONG RecSize;
+ PVOID BufPtr = NULL;
+ ULONG BufSize = 0;
+ ULONG EntriesRead = 0;
+ ULONG TotalEntries = 0;
+ ULONG i = 0;
+ ULONG j = 0;
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsMappingEnumW\n"));
+#endif
+
+ //
+ // Need to scan list so get read access.
+ //
+ RtlAcquireResourceShared(&MappingListLock, TRUE);
+
+ //
+ // Get size of each record based on info level. Only 0 and 1 supported.
+ //
+ Level = pMappingInfo->Level;
+ if (Level == 0)
+ RecSize = sizeof(LLS_MAPPING_INFO_0W);
+ else if (Level == 1)
+ RecSize = sizeof(LLS_MAPPING_INFO_1W);
+ else {
+ Status = STATUS_INVALID_LEVEL;
+ goto LlsrMappingEnumWExit;
+ }
+
+ //
+ // Calculate how many records will fit into PrefMaxLen buffer. This is
+ // the record size * # records + space for the string data. If MAX_ULONG
+ // is passed in then we return all records.
+ //
+ i = *pResumeHandle;
+ while ((i < MappingListSize) && (BufSize < pPrefMaxLen)) {
+ BufSize += RecSize;
+ EntriesRead++;
+
+ i++;
+ }
+
+ TotalEntries = EntriesRead;
+
+ //
+ // If we overflowed the buffer then back up one record.
+ //
+ if (BufSize > pPrefMaxLen) {
+ BufSize -= RecSize;
+ EntriesRead--;
+ }
+
+ if (i < MappingListSize)
+ Status = STATUS_MORE_ENTRIES;
+
+ //
+ // Now walk to the end of the list to see how many more records are still
+ // available.
+ //
+ TotalEntries += (MappingListSize - i);
+
+ //
+ // Reset Enum to correct place.
+ //
+ i = *pResumeHandle;
+
+ //
+ // We now know how many records will fit into the buffer, so allocate space
+ // and fix up pointers so we can copy the information.
+ //
+ BufPtr = MIDL_user_allocate(BufSize);
+ if (BufPtr == NULL) {
+ Status = STATUS_NO_MEMORY;
+ goto LlsrMappingEnumWExit;
+ }
+
+ RtlZeroMemory((PVOID) BufPtr, BufSize);
+
+ //
+ // Buffers are all setup, so loop through records and copy the data.
+ //
+ j = 0;
+ while ((j < EntriesRead) && (i < MappingListSize)) {
+ if (Level == 0)
+ ((PLLS_GROUP_INFO_0) BufPtr)[j].Name = MappingList[i]->Name;
+ else {
+ ((PLLS_GROUP_INFO_1) BufPtr)[j].Name = MappingList[i]->Name;
+ ((PLLS_GROUP_INFO_1) BufPtr)[j].Comment = MappingList[i]->Comment;
+ ((PLLS_GROUP_INFO_1) BufPtr)[j].Licenses = MappingList[i]->Licenses;
+ }
+
+ i++; j++;
+ }
+
+LlsrMappingEnumWExit:
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT(" TotalEntries: %lu EntriesRead: %lu ResumeHandle: 0x%lX\n"), TotalEntries, EntriesRead, i);
+#endif
+ *pTotalEntries = TotalEntries;
+
+ *pResumeHandle = (ULONG) i;
+ if (Level == 0) {
+ pMappingInfo->LlsMappingInfo.Level0->EntriesRead = EntriesRead;
+ pMappingInfo->LlsMappingInfo.Level0->Buffer = (PLLS_MAPPING_INFO_0W) BufPtr;
+ } else {
+ pMappingInfo->LlsMappingInfo.Level1->EntriesRead = EntriesRead;
+ pMappingInfo->LlsMappingInfo.Level1->Buffer = (PLLS_MAPPING_INFO_1W) BufPtr;
+ }
+
+ RtlReleaseResource(&MappingListLock);
+ return Status;
+
+} // LlsrMappingEnumW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrMappingEnumA(
+ LLS_HANDLE Handle,
+ PLLS_MAPPING_ENUM_STRUCTA MappingInfo,
+ DWORD PrefMaxLen,
+ LPDWORD TotalEntries,
+ LPDWORD ResumeHandle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsMappingEnumA\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+} // LlsrMappingEnumA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrMappingInfoGetW(
+ LLS_HANDLE Handle,
+ LPWSTR Mapping,
+ DWORD Level,
+ PLLS_MAPPING_INFOW *BufPtr
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ PMAPPING_RECORD pMapping = NULL;
+ PLLS_GROUP_INFO_1 pMap = NULL;
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsMappingInfoGetW\n"));
+#endif
+
+ if (Level != 1)
+ return STATUS_INVALID_LEVEL;
+
+ if ((Mapping == NULL) || (BufPtr == NULL))
+ return STATUS_INVALID_PARAMETER;
+
+ RtlAcquireResourceShared(&MappingListLock, TRUE);
+
+ pMapping = MappingListFind(Mapping);
+
+ if (pMapping != NULL) {
+ pMap = MIDL_user_allocate(sizeof(LLS_GROUP_INFO_1));
+ if (pMap != NULL) {
+ pMap->Name = pMapping->Name;
+ pMap->Comment = pMapping->Comment;
+ pMap->Licenses = pMapping->Licenses;
+ }
+ }
+
+ RtlReleaseResource(&MappingListLock);
+
+ if (pMapping == NULL)
+ return STATUS_OBJECT_NAME_NOT_FOUND;
+
+ if (pMap == NULL)
+ return STATUS_NO_MEMORY;
+
+ *BufPtr = (PLLS_MAPPING_INFOW) pMap;
+ return STATUS_SUCCESS;
+
+} // LlsrMappingInfoGetW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrMappingInfoGetA(
+ LLS_HANDLE Handle,
+ LPSTR Mapping,
+ DWORD Level,
+ PLLS_MAPPING_INFOA *BufPtr
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsMappingInfoGetA\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+} // LlsrMappingInfoGetA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrMappingInfoSetW(
+ LLS_HANDLE Handle,
+ LPWSTR Mapping,
+ DWORD Level,
+ PLLS_MAPPING_INFOW BufPtr
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ NTSTATUS Status;
+ PMAPPING_RECORD pMapping = NULL;
+ PLLS_GROUP_INFO_1 pMap;
+ LPTSTR NewComment;
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsMappingInfoSetW\n"));
+#endif
+
+ if (Level != 1)
+ return STATUS_INVALID_LEVEL;
+
+ if ((Mapping == NULL) || (BufPtr == NULL))
+ return STATUS_INVALID_PARAMETER;
+
+ RtlAcquireResourceExclusive(&MappingListLock, TRUE);
+
+ pMapping = MappingListFind(Mapping);
+
+ if (pMapping != NULL) {
+ pMap = (PLLS_GROUP_INFO_1) BufPtr;
+
+ //
+ // Check if comment has changed
+ //
+ if (pMap->Comment != NULL)
+ if (lstrcmp(pMap->Comment, pMapping->Comment)) {
+ NewComment = (LPTSTR) LocalAlloc(LPTR, (lstrlen(pMap->Comment) + 1) * sizeof(TCHAR));
+ if (NewComment != NULL) {
+ LocalFree(pMapping->Comment);
+ pMapping->Comment = NewComment;
+ lstrcpy(pMapping->Comment, pMap->Comment);
+ }
+ }
+
+ if ( pMapping->Licenses != pMap->Licenses )
+ {
+ MappingLicenseListFree( pMapping );
+ pMapping->Licenses = pMap->Licenses;
+ MappingLicenseUpdate( pMapping, TRUE );
+ }
+ }
+
+ RtlReleaseResource(&MappingListLock);
+
+ if (pMapping == NULL)
+ Status = STATUS_OBJECT_NAME_NOT_FOUND;
+ else
+ Status = MappingListSave();
+
+ return Status;
+
+} // LlsrMappingInfoSetW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrMappingInfoSetA(
+ LLS_HANDLE Handle,
+ LPSTR Mapping,
+ DWORD Level,
+ PLLS_MAPPING_INFOA BufPtr
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsMappingInfoSetA\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+} // LlsrMappingInfoSetA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrMappingUserEnumW(
+ LLS_HANDLE Handle,
+ LPWSTR Mapping,
+ PLLS_USER_ENUM_STRUCTW pMappingUserInfo,
+ DWORD pPrefMaxLen,
+ LPDWORD pTotalEntries,
+ LPDWORD pResumeHandle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ PMAPPING_RECORD pMapping;
+ DWORD Level;
+ PVOID BufPtr = NULL;
+ ULONG BufSize = 0;
+ ULONG EntriesRead = 0;
+ ULONG TotalEntries = 0;
+ ULONG i = 0;
+ ULONG j = 0;
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsMappingUserEnumW\n"));
+#endif
+
+ Level = pMappingUserInfo->Level;
+ if (Level != 0)
+ return STATUS_INVALID_LEVEL;
+
+ //
+ // Need to scan list so get read access.
+ //
+ RtlAcquireResourceShared(&MappingListLock, TRUE);
+
+ pMapping = MappingListFind(Mapping);
+ if (pMapping == NULL) {
+ Status = STATUS_OBJECT_NAME_NOT_FOUND;
+ goto LlsrMappingUserEnumWExit;
+ }
+
+ //
+ // Calculate how many records will fit into PrefMaxLen buffer. This is
+ // the record size * # records + space for the string data. If MAX_ULONG
+ // is passed in then we return all records.
+ //
+ i = *pResumeHandle;
+ while ((i < pMapping->NumMembers) && (BufSize < pPrefMaxLen)) {
+ BufSize += sizeof(LLS_USER_INFO_0);
+ EntriesRead++;
+
+ i++;
+ }
+
+ TotalEntries = EntriesRead;
+
+ //
+ // If we overflowed the buffer then back up one record.
+ //
+ if (BufSize > pPrefMaxLen) {
+ BufSize -= sizeof(LLS_USER_INFO_0);
+ EntriesRead--;
+ }
+
+ if (i < pMapping->NumMembers)
+ Status = STATUS_MORE_ENTRIES;
+
+ //
+ // Now walk to the end of the list to see how many more records are still
+ // available.
+ //
+ TotalEntries += (pMapping->NumMembers - i);
+
+ //
+ // Reset Enum to correct place.
+ //
+ i = *pResumeHandle;
+
+ //
+ // We now know how many records will fit into the buffer, so allocate space
+ // and fix up pointers so we can copy the information.
+ //
+ BufPtr = MIDL_user_allocate(BufSize);
+ if (BufPtr == NULL) {
+ Status = STATUS_NO_MEMORY;
+ goto LlsrMappingUserEnumWExit;
+ }
+
+ RtlZeroMemory((PVOID) BufPtr, BufSize);
+
+ //
+ // Buffers are all setup, so loop through records and copy the data.
+ //
+ while ((j < EntriesRead) && (i < pMapping->NumMembers)) {
+ ((PLLS_USER_INFO_0) BufPtr)[j].Name = pMapping->Members[i];
+ i++; j++;
+ }
+
+LlsrMappingUserEnumWExit:
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT(" TotalEntries: %lu EntriesRead: %lu ResumeHandle: 0x%lX\n"), TotalEntries, EntriesRead, i);
+#endif
+ *pTotalEntries = TotalEntries;
+
+ *pResumeHandle = (ULONG) i;
+ pMappingUserInfo->LlsUserInfo.Level0->EntriesRead = EntriesRead;
+ pMappingUserInfo->LlsUserInfo.Level0->Buffer = (PLLS_USER_INFO_0W) BufPtr;
+
+ RtlReleaseResource(&MappingListLock);
+ return Status;
+
+} // LlsrMappingUserEnumW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrMappingUserEnumA(
+ LLS_HANDLE Handle,
+ LPSTR Mapping,
+ PLLS_USER_ENUM_STRUCTA MappingUserInfo,
+ DWORD PrefMaxLen,
+ LPDWORD TotalEntries,
+ LPDWORD ResumeHandle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsMappingUserEnumA\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+} // LlsrMappingUserEnumA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrMappingUserAddW(
+ LLS_HANDLE Handle,
+ LPWSTR Mapping,
+ LPWSTR User
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ PUSER_RECORD pUserRec;
+ PMAPPING_RECORD pMap;
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsMappingUserAddW\n"));
+#endif
+
+ if ((Mapping == NULL) || (User == NULL))
+ return STATUS_INVALID_PARAMETER;
+
+ RtlAcquireResourceExclusive(&MappingListLock, TRUE);
+ pMap = MappingUserListAdd( Mapping, User );
+
+ if (pMap == NULL)
+ Status = STATUS_OBJECT_NAME_NOT_FOUND;
+ else {
+ RtlAcquireResourceShared(&UserListLock, TRUE);
+ pUserRec = UserListFind(User);
+
+ if (pUserRec != NULL)
+ UserMappingAdd(pMap, pUserRec);
+
+ RtlReleaseResource(&UserListLock);
+ }
+
+ RtlReleaseResource(&MappingListLock);
+
+ if (Status == STATUS_SUCCESS)
+ {
+ Status = MappingListSave();
+
+ if (Status == STATUS_SUCCESS)
+ Status = LLSDataSave();
+ }
+
+ return Status;
+
+} // LlsrMappingUserAddW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrMappingUserAddA(
+ LLS_HANDLE Handle,
+ LPSTR Mapping,
+ LPSTR User
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsMappingUserAddA\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+} // LlsrMappingUserAddA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrMappingUserDeleteW(
+ LLS_HANDLE Handle,
+ LPWSTR Mapping,
+ LPWSTR User
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ NTSTATUS Status;
+ PUSER_RECORD pUser;
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsMappingUserDeleteW\n"));
+#endif
+
+ if ((Mapping == NULL) || (User == NULL))
+ return STATUS_INVALID_PARAMETER;
+
+ RtlAcquireResourceExclusive(&MappingListLock, TRUE);
+ Status = MappingUserListDelete(Mapping, User);
+ RtlReleaseResource(&MappingListLock);
+
+ pUser = UserListFind( User );
+
+ if (pUser != NULL) {
+ //
+ // if auto switch to BackOffice then turn BackOffice off
+ //
+ if (pUser->Flags & LLS_FLAG_SUITE_AUTO)
+ pUser->Flags &= ~ LLS_FLAG_SUITE_USE;
+
+ //
+ // Free up any licenses used by the user
+ //
+ SvcListLicenseFree( pUser );
+ pUser->Mapping = NULL;
+
+ //
+ // And claim any needed new-ones
+ //
+ SvcListLicenseUpdate( pUser );
+ }
+
+ if (Status == STATUS_SUCCESS)
+ {
+ Status = MappingListSave();
+
+ if (Status == STATUS_SUCCESS)
+ Status = LLSDataSave();
+ }
+
+ return Status;
+} // LlsrMappingUserDeleteW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrMappingUserDeleteA(
+ LLS_HANDLE Handle,
+ LPSTR Mapping,
+ LPSTR User
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsMappingUserDeleteA\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+} // LlsrMappingUserDeleteA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrMappingAddW(
+ LLS_HANDLE Handle,
+ DWORD Level,
+ PLLS_MAPPING_INFOW BufPtr
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ PMAPPING_RECORD pMap;
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsMappingAddW\n"));
+#endif
+
+ if (Level != 1)
+ return STATUS_INVALID_LEVEL;
+
+ if ((BufPtr == NULL) ||
+ (BufPtr->MappingInfo1.Name == NULL) ||
+ (BufPtr->MappingInfo1.Comment == NULL))
+ return STATUS_INVALID_PARAMETER;
+
+ RtlAcquireResourceExclusive(&MappingListLock, TRUE);
+
+ pMap = MappingListAdd(BufPtr->MappingInfo1.Name,
+ BufPtr->MappingInfo1.Comment,
+ BufPtr->MappingInfo1.Licenses);
+
+ RtlReleaseResource(&MappingListLock);
+ if (pMap == NULL)
+ Status = STATUS_NO_MEMORY;
+
+ if (Status == STATUS_SUCCESS)
+ Status = MappingListSave();
+
+ return Status;
+} // LlsrMappingAddW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrMappingAddA(
+ LLS_HANDLE Handle,
+ DWORD Level,
+ PLLS_MAPPING_INFOA BufPtr
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsMappingAddA\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+} // LlsrMappingAddA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrMappingDeleteW(
+ LLS_HANDLE Handle,
+ LPWSTR Mapping
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ NTSTATUS Status;
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsMappingDeleteW\n"));
+#endif
+
+ if (Mapping == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ RtlAcquireResourceExclusive(&MappingListLock, TRUE);
+ Status = MappingListDelete(Mapping);
+ RtlReleaseResource(&MappingListLock);
+
+ if (Status == STATUS_SUCCESS)
+ Status = MappingListSave();
+
+ return Status;
+} // LlsrMappingDeleteW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrMappingDeleteA(
+ LLS_HANDLE Handle,
+ LPSTR Mapping
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsMappingDeleteA\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+} // LlsrMappingDeleteA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrServerEnumW(
+ LLS_HANDLE Handle,
+ LPWSTR Server,
+ PLLS_SERVER_ENUM_STRUCTW pServerProductInfo,
+ DWORD pPrefMaxLen,
+ LPDWORD pTotalEntries,
+ LPDWORD pResumeHandle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsServerEnumW\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+} // LlsrServerEnumW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrServerEnumA(
+ LLS_HANDLE Handle,
+ LPSTR Server,
+ PLLS_SERVER_ENUM_STRUCTA pServerProductInfo,
+ DWORD PrefMaxLen,
+ LPDWORD TotalEntries,
+ LPDWORD ResumeHandle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsServerEnumA\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+} // LlsrServerEnumA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrServerProductEnumW(
+ LLS_HANDLE Handle,
+ LPWSTR Server,
+ PLLS_SERVER_PRODUCT_ENUM_STRUCTW pServerProductInfo,
+ DWORD pPrefMaxLen,
+ LPDWORD pTotalEntries,
+ LPDWORD pResumeHandle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsServerProductEnumW\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+} // LlsrServerProductEnumW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrServerProductEnumA(
+ LLS_HANDLE Handle,
+ LPSTR Server,
+ PLLS_SERVER_PRODUCT_ENUM_STRUCTA pServerProductInfo,
+ DWORD PrefMaxLen,
+ LPDWORD TotalEntries,
+ LPDWORD ResumeHandle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsServerProductEnumA\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+} // LlsrServerProductEnumA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrLocalProductEnumW(
+ LLS_HANDLE Handle,
+ PLLS_SERVER_PRODUCT_ENUM_STRUCTW pServerProductInfo,
+ DWORD pPrefMaxLen,
+ LPDWORD pTotalEntries,
+ LPDWORD pResumeHandle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsLocalProductEnumW\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+} // LlsrLocalProductEnumW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrLocalProductEnumA(
+ LLS_HANDLE Handle,
+ PLLS_SERVER_PRODUCT_ENUM_STRUCTA pServerProductInfo,
+ DWORD PrefMaxLen,
+ LPDWORD TotalEntries,
+ LPDWORD ResumeHandle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsLocalProductEnumA\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+} // LlsrLocalProductEnumA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrLocalProductInfoGetW(
+ LLS_HANDLE Handle,
+ LPWSTR Product,
+ DWORD Level,
+ PLLS_SERVER_PRODUCT_INFOW *BufPtr
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsLocalProductInfoGetW\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+} // LlsrLocalProductInfoGetW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrLocalProductInfoGetA(
+ LLS_HANDLE Handle,
+ LPSTR Product,
+ DWORD Level,
+ PLLS_SERVER_PRODUCT_INFOA *BufPtr
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsLocalProductInfoGetA\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+} // LlsrLocalProductInfoGetA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrLocalProductInfoSetW(
+ LLS_HANDLE Handle,
+ LPWSTR Product,
+ DWORD Level,
+ PLLS_SERVER_PRODUCT_INFOW BufPtr
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsLocalProductInfoSetW\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+} // LlsrLocalProductInfoSetW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrLocalProductInfoSetA(
+ LLS_HANDLE Handle,
+ LPSTR Product,
+ DWORD Level,
+ PLLS_SERVER_PRODUCT_INFOA BufPtr
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsLocalProductInfoSetA\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+} // LlsrLocalProductInfoSetA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrServiceInfoGetW(
+ LLS_HANDLE Handle,
+ DWORD Level,
+ PLLS_SERVICE_INFOW *BufPtr
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ PLLS_SERVICE_INFO_0 pInfo;
+ FILETIME ftTimeStartedLocal;
+ LARGE_INTEGER llTimeStartedLocal;
+ LARGE_INTEGER llTimeStartedSystem;
+
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsServiceInfoGetW\n"));
+#endif
+
+ if (Level != 0)
+ return STATUS_INVALID_LEVEL;
+
+ if (BufPtr == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ pInfo = (PLLS_SERVICE_INFO_0) MIDL_user_allocate(sizeof(LLS_SERVICE_INFO_0));
+ if (pInfo == NULL)
+ return STATUS_NO_MEMORY;
+
+ // make sure the config info is up-to-date
+ ConfigInfoRegistryUpdate();
+
+ RtlEnterCriticalSection(&ConfigInfoLock);
+
+ pInfo->Version = ConfigInfo.Version;
+ pInfo->Mode = LLS_MODE_ENTERPRISE_SERVER;
+ pInfo->ReplicateTo = ConfigInfo.ReplicateTo;
+ pInfo->EnterpriseServer = ConfigInfo.EnterpriseServer;
+ pInfo->ReplicationType = ConfigInfo.ReplicationType;
+ pInfo->ReplicationTime = ConfigInfo.ReplicationTime;
+ pInfo->LastReplicated = ConfigInfo.LastReplicatedSeconds;
+ pInfo->UseEnterprise = ConfigInfo.UseEnterprise;
+
+ SystemTimeToFileTime( &ConfigInfo.Started, &ftTimeStartedLocal );
+
+ RtlLeaveCriticalSection(&ConfigInfoLock);
+
+ // convert time started (a local SYSTEMTIME) to a system time DWORD in seconds since 1980
+ llTimeStartedLocal.u.LowPart = ftTimeStartedLocal.dwLowDateTime;
+ llTimeStartedLocal.u.HighPart = ftTimeStartedLocal.dwHighDateTime;
+
+ RtlLocalTimeToSystemTime( &llTimeStartedLocal, &llTimeStartedSystem );
+ RtlTimeToSecondsSince1980( &llTimeStartedSystem, &pInfo->TimeStarted );
+
+ *BufPtr = (PLLS_SERVICE_INFOW) pInfo;
+
+ return STATUS_SUCCESS;
+
+} // LlsrServiceInfoGetW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrServiceInfoGetA(
+ LLS_HANDLE Handle,
+ DWORD Level,
+ PLLS_SERVICE_INFOA *BufPtr
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+{
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsServiceInfoGetA\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+} // LlsrServiceInfoGetA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrServiceInfoSetW(
+ LLS_HANDLE Handle,
+ DWORD Level,
+ PLLS_SERVICE_INFOW BufPtr
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+{
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsServiceInfoSetW\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+} // LlsrServiceInfoSetW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrServiceInfoSetA(
+ LLS_HANDLE Handle,
+ DWORD Level,
+ PLLS_SERVICE_INFOA BufPtr
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+{
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsServiceInfoSetA\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+} // LlsrServiceInfoSetA
+
+
+/////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////
+// Replication Functions
+
+/////////////////////////////////////////////////////////////////////////
+VOID __RPC_USER LLS_REPL_HANDLE_rundown(
+ LLS_REPL_HANDLE Handle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ REPL_CONTEXT_TYPE *pClient;
+ LLS_REPL_HANDLE xHandle;
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LLS_REPL_HANDLE_rundown\n"));
+#endif
+
+ if (Handle == NULL)
+ return;
+
+ pClient = (REPL_CONTEXT_TYPE *) Handle;
+
+ if (pClient != NULL)
+ if (pClient->Active) {
+ xHandle = Handle;
+ LlsrReplClose(&xHandle);
+ }
+
+ MIDL_user_free(pClient);
+
+} // LLS_REPL_HANDLE_rundown
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrReplConnect(
+ PLLS_REPL_HANDLE Handle,
+ LPTSTR Name
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ REPL_CONTEXT_TYPE *pClient;
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsReplConnect: %s\n"), Name);
+#endif
+
+ pClient = (REPL_CONTEXT_TYPE *) midl_user_allocate(sizeof(REPL_CONTEXT_TYPE));
+ ASSERT(pClient != NULL);
+
+ if (Name != NULL)
+ lstrcpy(pClient->Name, Name);
+ else
+ lstrcpy(pClient->Name, TEXT(""));
+
+ pClient->Active = TRUE;
+ pClient->Replicated = FALSE;
+
+ pClient->ServicesSent = FALSE;
+ pClient->ServiceTableSize = 0;
+ pClient->Services = NULL;
+
+ pClient->ServersSent = FALSE;
+ pClient->ServerTableSize = 0;
+ pClient->Servers = NULL;
+
+ pClient->ServerServicesSent = FALSE;
+ pClient->ServerServiceTableSize = 0;
+ pClient->ServerServices = NULL;
+
+ pClient->UsersSent = FALSE;
+ pClient->UserLevel = 0;
+ pClient->UserTableSize = 0;
+ pClient->Users = NULL;
+
+ pClient->CertDbSent = FALSE;
+ pClient->CertDbProductStringSize = 0;
+ pClient->CertDbProductStrings = NULL;
+ pClient->CertDbNumHeaders = 0;
+ pClient->CertDbHeaders = NULL;
+ pClient->CertDbNumClaims = 0;
+ pClient->CertDbClaims = NULL;
+
+ pClient->ProductSecuritySent = FALSE;
+ pClient->ProductSecurityStringSize = 0;
+ pClient->ProductSecurityStrings = NULL;
+
+ *Handle = pClient;
+
+ return STATUS_SUCCESS;
+} // LlsrReplConnect
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrReplClose(
+ LLS_REPL_HANDLE *pHandle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ NTSTATUS Status;
+ BOOL Replicated = TRUE;
+ LLS_REPL_HANDLE Handle = NULL;
+ TCHAR ComputerName[MAX_COMPUTERNAME_LENGTH + 1];
+ REPL_CONTEXT_TYPE *pClient;
+ PSERVER_RECORD Server;
+ ULONG i;
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsReplClose\n"));
+#endif
+
+ ASSERT (pHandle != NULL);
+
+ Handle = *pHandle;
+ pClient = (REPL_CONTEXT_TYPE *) Handle;
+ pClient->Active = FALSE;
+
+ //
+ // Check to see if we have all the information from the client - if we do
+ // then munge this information into our internal tables.
+ //
+ if (pClient->ServersSent && pClient->UsersSent && pClient->ServicesSent && pClient->ServerServicesSent) {
+#if DBG
+ if (TraceFlags & TRACE_RPC)
+ dprintf(TEXT("LLS Replication - Munging Data\n"));
+#endif
+
+ UnpackAll (
+ pClient->ServiceTableSize,
+ pClient->Services,
+ pClient->ServerTableSize,
+ pClient->Servers,
+ pClient->ServerServiceTableSize,
+ pClient->ServerServices,
+ pClient->UserLevel,
+ pClient->UserTableSize,
+ pClient->Users
+ );
+
+ if ( pClient->CertDbSent )
+ {
+ CertDbUnpack(
+ pClient->CertDbProductStringSize,
+ pClient->CertDbProductStrings,
+ pClient->CertDbNumHeaders,
+ pClient->CertDbHeaders,
+ pClient->CertDbNumClaims,
+ pClient->CertDbClaims,
+ TRUE );
+ }
+
+ if ( pClient->ProductSecuritySent )
+ {
+ ProductSecurityUnpack(
+ pClient->ProductSecurityStringSize,
+ pClient->ProductSecurityStrings );
+ }
+ } else
+ Replicated = FALSE;
+
+ //////////////////////////////////////////////////////////////////
+ //
+ // Replication Finished - clean up the context data.
+ //
+#if DBG
+ if (TraceFlags & TRACE_RPC)
+ dprintf(TEXT("LLS Replication - Munging Finished\n"));
+#endif
+
+ if (pClient->Servers != NULL) {
+ for (i = 0; i < pClient->ServerTableSize; i++)
+ MIDL_user_free(pClient->Servers[i].Name);
+
+ MIDL_user_free(pClient->Servers);
+ }
+
+ if (pClient->Services != NULL) {
+ for (i = 0; i < pClient->ServiceTableSize; i++) {
+ MIDL_user_free(pClient->Services[i].Name);
+ MIDL_user_free(pClient->Services[i].FamilyName);
+ }
+
+ MIDL_user_free(pClient->Services);
+ }
+
+ if (pClient->ServerServices != NULL)
+ MIDL_user_free(pClient->ServerServices);
+
+ if (pClient->Users != NULL) {
+ for (i = 0; i < pClient->UserTableSize; i++)
+ {
+ if ( 0 == pClient->UserLevel )
+ {
+ MIDL_user_free( ((PREPL_USER_RECORD_0) (pClient->Users))[i].Name );
+ }
+ else
+ {
+ MIDL_user_free( ((PREPL_USER_RECORD_1) (pClient->Users))[i].Name );
+ }
+ }
+
+ MIDL_user_free(pClient->Users);
+ }
+
+ if (pClient->CertDbProductStrings != NULL)
+ {
+ MIDL_user_free(pClient->CertDbProductStrings);
+ }
+
+ if (pClient->CertDbHeaders != NULL)
+ {
+ MIDL_user_free(pClient->CertDbHeaders);
+ }
+
+ if (pClient->CertDbClaims != NULL)
+ {
+ MIDL_user_free(pClient->CertDbClaims);
+ }
+
+ if (pClient->ProductSecurityStrings != NULL)
+ {
+ MIDL_user_free(pClient->ProductSecurityStrings);
+ }
+
+ if (pClient->Replicated) {
+ if (Replicated) {
+ RtlAcquireResourceShared(&ServerListLock, TRUE);
+ Server = ServerListFind(pClient->Name);
+ RtlReleaseResource(&ServerListLock);
+
+ ASSERT(Server != NULL);
+ if (Server != NULL)
+ Server->LastReplicated = pClient->ReplicationStart;
+ }
+
+ RtlEnterCriticalSection(&ConfigInfoLock);
+ i = --ConfigInfo.NumReplicating;
+ RtlLeaveCriticalSection(&ConfigInfoLock);
+
+ if ( !i )
+ {
+ // no one's replicating; save all our data files
+ SaveAll();
+ }
+ }
+
+ return STATUS_SUCCESS;
+} // LlsrReplClose
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+LlsrReplicationRequestW(
+ LLS_HANDLE Handle,
+ DWORD Version,
+ PREPL_REQUEST pRequest
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+{
+ TCHAR ComputerName[MAX_COMPUTERNAME_LENGTH + 1];
+ REPL_CONTEXT_TYPE *pClient;
+ PSERVER_RECORD Server;
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC | TRACE_REPLICATION))
+ dprintf(TEXT("LLS TRACE: LlsReplicationRequestW: %s\n"), ((PCLIENT_CONTEXT_TYPE) Handle)->Name);
+#endif
+
+ if (Version != REPL_VERSION) {
+ return STATUS_INVALID_LEVEL;
+ }
+
+ if (pRequest == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ //
+ // Check Enterprise server date from client to see if we need to update
+ // ours. Also, send back new one for the client.
+ //
+ RtlEnterCriticalSection(&ConfigInfoLock);
+ lstrcpy(ComputerName, ConfigInfo.ComputerName);
+
+ if (ConfigInfo.EnterpriseServerDate < pRequest->EnterpriseServerDate) {
+ if (lstrlen(pRequest->EnterpriseServer) != 0) {
+ lstrcpy(ConfigInfo.EnterpriseServer, pRequest->EnterpriseServer);
+ ConfigInfo.EnterpriseServerDate = pRequest->EnterpriseServerDate;
+ }
+ }
+
+ lstrcpy(pRequest->EnterpriseServer, ConfigInfo.EnterpriseServer);
+ pRequest->EnterpriseServerDate = ConfigInfo.EnterpriseServerDate;
+
+ //
+ // Increment Repl Count
+ //
+ ConfigInfo.NumReplicating++;
+ RtlLeaveCriticalSection(&ConfigInfoLock);
+
+ //
+ // Find this server in our server list (add it if not there)
+ //
+ pClient = (REPL_CONTEXT_TYPE *) Handle;
+ pClient->Replicated = TRUE;
+ RtlAcquireResourceExclusive(&ServerListLock, TRUE);
+ Server = ServerListAdd(pClient->Name, ComputerName);
+ RtlReleaseResource(&ServerListLock);
+
+ if (Server == NULL) {
+ ASSERT(FALSE);
+ return STATUS_NO_MEMORY;
+ }
+
+ pClient->ReplicationStart = pRequest->CurrentTime;
+ pRequest->LastReplicated = Server->LastReplicated;
+ return STATUS_SUCCESS;
+} // LlsrReplicationRequestW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+LlsrReplicationServerAddW(
+ LLS_HANDLE Handle,
+ ULONG NumRecords,
+ PREPL_SERVER_RECORD Servers
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+{
+ REPL_CONTEXT_TYPE *pClient;
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsReplicationServerAddW\n"));
+#endif
+
+ pClient = (REPL_CONTEXT_TYPE *) Handle;
+
+ pClient->ServersSent = TRUE;
+ pClient->ServerTableSize = NumRecords;
+ pClient->Servers = Servers;
+
+ return STATUS_SUCCESS;
+} // LlsrReplicationServerAddW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+LlsrReplicationServerServiceAddW(
+ LLS_HANDLE Handle,
+ ULONG NumRecords,
+ PREPL_SERVER_SERVICE_RECORD ServerServices
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+{
+ REPL_CONTEXT_TYPE *pClient;
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsReplicationServerServiceAddW\n"));
+#endif
+
+ pClient = (REPL_CONTEXT_TYPE *) Handle;
+
+ pClient->ServerServicesSent = TRUE;
+ pClient->ServerServiceTableSize = NumRecords;
+ pClient->ServerServices = ServerServices;
+
+ return STATUS_SUCCESS;
+} // LlsrReplicationServerServiceAddW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+LlsrReplicationServiceAddW(
+ LLS_HANDLE Handle,
+ ULONG NumRecords,
+ PREPL_SERVICE_RECORD Services
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+{
+ REPL_CONTEXT_TYPE *pClient;
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsReplicationServiceAddW\n"));
+#endif
+
+ pClient = (REPL_CONTEXT_TYPE *) Handle;
+
+ pClient->ServicesSent = TRUE;
+ pClient->ServiceTableSize = NumRecords;
+ pClient->Services = Services;
+
+ return STATUS_SUCCESS;
+} // LlsrReplicationServiceAddW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+LlsrReplicationUserAddW(
+ LLS_HANDLE Handle,
+ ULONG NumRecords,
+ PREPL_USER_RECORD_0 Users
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+{
+ REPL_CONTEXT_TYPE *pClient;
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsReplicationUserAddW\n"));
+#endif
+
+ pClient = (REPL_CONTEXT_TYPE *) Handle;
+
+ pClient->UsersSent = TRUE;
+ pClient->UserLevel = 0;
+ pClient->UserTableSize = NumRecords;
+ pClient->Users = Users;
+
+ return STATUS_SUCCESS;
+} // LlsrReplicationUserAddW
+
+
+
+/////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////
+// Licensing Functions
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+LlsrLicenseRequestW(
+ LPDWORD LicenseHandle,
+ LPWSTR ProductID,
+ ULONG VersionIndex,
+ BOOLEAN IsAdmin,
+ ULONG DataType,
+ ULONG DataSize,
+ PBYTE Data
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+{
+ NTSTATUS Status;
+ ULONG Handle = 0xFFFFFFFFL;
+
+#if DBG
+ if ( TraceFlags & (TRACE_FUNCTION_TRACE) )
+ dprintf(TEXT("LLS TRACE: LlsrLicenseRequestW\n"));
+#endif
+
+ Status = DispatchRequestLicense(DataType, Data, ProductID, VersionIndex, IsAdmin, &Handle);
+ *LicenseHandle = Handle;
+
+ return Status;
+} // LlsrLicenseRequestW
+
+
+NTSTATUS
+LlsrLicenseFree(
+ DWORD LicenseHandle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+{
+#if DBG
+ if ( TraceFlags & (TRACE_FUNCTION_TRACE) )
+ dprintf(TEXT("LLS TRACE: LlsrLicenseFree\n"));
+#endif
+
+
+ DispatchFreeLicense(LicenseHandle);
+ return STATUS_SUCCESS;
+} // LlsrLicenseFree
+
+
+
+
+/////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////
+
+
+#if DBG
+
+/////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////
+// Debugging API's
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+LlsrDbgTableDump(
+ DWORD Table
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+{
+ //
+ // FreeHandle is actually TableID
+ //
+ switch(Table) {
+ case SERVICE_TABLE_NUM:
+ ServiceListDebugDump();
+ break;
+
+ case USER_TABLE_NUM:
+ UserListDebugDump();
+ break;
+
+ case SID_TABLE_NUM:
+ SidListDebugDump();
+ break;
+
+ case LICENSE_TABLE_NUM:
+ LicenseListDebugDump();
+ break;
+
+ case ADD_CACHE_TABLE_NUM:
+ AddCacheDebugDump();
+ break;
+
+ case MASTER_SERVICE_TABLE_NUM:
+ MasterServiceListDebugDump();
+ break;
+
+ case SERVICE_FAMILY_TABLE_NUM:
+ MasterServiceRootDebugDump();
+ break;
+
+ case MAPPING_TABLE_NUM:
+ MappingListDebugDump();
+ break;
+
+ case SERVER_TABLE_NUM:
+ ServerListDebugDump();
+ break;
+
+ case SECURE_PRODUCT_TABLE_NUM:
+ ProductSecurityListDebugDump();
+ break;
+
+ case CERTIFICATE_TABLE_NUM:
+ CertDbDebugDump();
+ break;
+ }
+
+ return STATUS_SUCCESS;
+} // LlsrDbgTableDump
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+LlsrDbgTableInfoDump(
+ DWORD Table,
+ LPTSTR Item
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+{
+ switch(Table) {
+ case SERVICE_TABLE_NUM:
+ ServiceListDebugInfoDump((PVOID) Item);
+ break;
+
+ case USER_TABLE_NUM:
+ UserListDebugInfoDump((PVOID) Item);
+ break;
+
+// case SID_TABLE_NUM:
+// SidListDebugInfoDump((PVOID) Item);
+// break;
+
+// case LICENSE_TABLE_NUM:
+// LicenseListInfoDebugDump((PVOID) Item);
+// break;
+
+// case ADD_CACHE_TABLE_NUM:
+// AddCacheDebugDump((PVOID) Item);
+// break;
+
+ case MASTER_SERVICE_TABLE_NUM:
+ MasterServiceListDebugInfoDump((PVOID) Item);
+ break;
+
+ case SERVICE_FAMILY_TABLE_NUM:
+ MasterServiceRootDebugInfoDump((PVOID) Item);
+ break;
+
+ case MAPPING_TABLE_NUM:
+ MappingListDebugInfoDump((PVOID) Item);
+ break;
+
+ case SERVER_TABLE_NUM:
+ ServerListDebugInfoDump((PVOID) Item);
+ break;
+ }
+
+ return STATUS_SUCCESS;
+} // LlsrDbgTableInfoDump
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+LlsrDbgTableFlush(
+ DWORD Table
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+{
+ return STATUS_SUCCESS;
+} // LlsrDbgTableFlush
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+LlsrDbgTraceSet(
+ DWORD Flags
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+{
+ TraceFlags = Flags;
+ return STATUS_SUCCESS;
+} // LlsrDbgTraceSet
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+LlsrDbgConfigDump(
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+{
+ ConfigInfoDebugDump();
+ return STATUS_SUCCESS;
+} // LlsrDbgConfigDump
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+LlsrDbgReplicationForce(
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+{
+ NtSetEvent( ReplicationEvent, NULL );
+ return STATUS_SUCCESS;
+} // LlsrDbgReplicationForce
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+LlsrDbgReplicationDeny(
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+{
+ return STATUS_SUCCESS;
+} // LlsrDbgReplicationDeny
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+LlsrDbgRegistryUpdateForce(
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+{
+ ServiceListResynch();
+ return STATUS_SUCCESS;
+} // LlsrDbgRegistryUpdateForce
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+LlsrDbgDatabaseFlush(
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+{
+ LLSDataSave();
+ return STATUS_SUCCESS;
+} // LlsrDbgDatabaseFlush
+
+
+
+#endif // #if DBG
+
+
+/////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////
+// Extended RPC
+
+NTSTATUS LlsrProductSecurityGetA(
+ LLS_HANDLE Handle,
+ LPSTR Product,
+ LPBOOL pIsSecure
+ )
+
+/*++
+
+Routine Description:
+
+ Retrieve the "security" of a product. A product is deemed secure iff
+ it requires a secure certificate. In such a case, licenses for the
+ product may not be entered via the Honesty ("enter the number of
+ licenses you purchased") method.
+
+ NOTE: Not yet implemented. Use LlsrProductSecurityGetW().
+
+Arguments:
+
+ Handle (LLS_HANDLE)
+ An open LLS handle.
+ Product (LPSTR)
+ The name of the product ("DisplayName") for which to receive the
+ security.
+ pIsSecure (LPBOOL)
+ On return, and if successful, indicates whether the product is
+ secure.
+
+Return Value:
+
+ STATUS_NOT_SUPPORTED.
+
+--*/
+
+{
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsrProductSecurityGetA\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+} // LlsrProductSecurityGetA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrProductSecurityGetW(
+ LLS_HANDLE Handle,
+ LPWSTR DisplayName,
+ LPBOOL pIsSecure
+ )
+
+/*++
+
+Routine Description:
+
+ Retrieve the "security" of a product. A product is deemed secure iff
+ it requires a secure certificate. In such a case, licenses for the
+ product may not be entered via the Honesty ("enter the number of
+ licenses you purchased") method.
+
+Arguments:
+
+ Handle (LLS_HANDLE)
+ An open LLS handle.
+ Product (LPWSTR)
+ The name of the product ("DisplayName") for which to receive the
+ security.
+ pIsSecure (LPBOOL)
+ On return, and if successful, indicates whether the product is
+ secure.
+
+Return Value:
+
+ STATUS_SUCCESS or NTSTATUS error code.
+
+--*/
+
+{
+ DWORD i;
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsrProductSecurityGetW\n"));
+#endif
+
+ RtlAcquireResourceShared( &LocalServiceListLock, TRUE );
+
+ *pIsSecure = ServiceIsSecure( DisplayName );
+
+ RtlReleaseResource( &LocalServiceListLock );
+
+ return STATUS_SUCCESS;
+} // LlsrProductSecurityGetW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrProductSecuritySetA(
+ LLS_HANDLE Handle,
+ LPSTR Product
+ )
+
+/*++
+
+Routine Description:
+
+ Flags the given product as secure. A product is deemed secure iff
+ it requires a secure certificate. In such a case, licenses for the
+ product may not be entered via the Honesty ("enter the number of
+ licenses you purchased") method.
+
+ This designation is not reversible and is propagated up the
+ replication tree.
+
+ NOTE: Not yet implemented. Use LlsrProductSecuritySetW().
+
+Arguments:
+
+ Handle (LLS_HANDLE)
+ An open LLS handle.
+ Product (LPSTR)
+ The name of the product ("DisplayName") for which to activate
+ security.
+
+Return Value:
+
+ STATUS_NOT_SUPPORTED.
+
+--*/
+
+{
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsrProductSecuritySetA\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+} // LlsrProductSecuritySetA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrProductSecuritySetW(
+ LLS_HANDLE Handle,
+ LPWSTR DisplayName
+ )
+
+/*++
+
+Routine Description:
+
+ Flags the given product as secure. A product is deemed secure iff
+ it requires a secure certificate. In such a case, licenses for the
+ product may not be entered via the Honesty ("enter the number of
+ licenses you purchased") method.
+
+ This designation is not reversible and is propagated up the
+ replication tree.
+
+Arguments:
+
+ Handle (LLS_HANDLE)
+ An open LLS handle.
+ Product (LPWSTR)
+ The name of the product ("DisplayName") for which to activate
+ security.
+
+Return Value:
+
+ STATUS_SUCCESS or NTSTATUS error code.
+
+--*/
+
+{
+ NTSTATUS nt;
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsrProductSecuritySetW\n"));
+#endif
+
+ nt = ServiceSecuritySet( DisplayName );
+
+ return nt;
+} // LlsrProductSecuritySetW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+LlsrProductLicensesGetA(
+ LLS_HANDLE Handle,
+ LPSTR DisplayName,
+ DWORD Mode,
+ LPDWORD pQuantity )
+
+/*++
+
+Routine Description:
+
+ Returns the number of licenses installed for use in the given mode.
+
+ NOTE: Not yet implemented. Use LlsrProductLicensesGetW().
+
+Arguments:
+
+ Handle (LLS_HANDLE)
+ An open LLS handle.
+ Product (LPSTR)
+ The name of the product for which to tally licenses.
+ Mode (DWORD)
+ Mode for which to tally licenses.
+ pQuantity (LPDWORD)
+ On return (and if successful), holds the total number of licenses
+ for use by the given product in the given license mode.
+
+Return Value:
+
+ STATUS_NOT_SUPPORTED.
+
+--*/
+
+{
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsProductLicensesGetA\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+} // LlsProductLicensesGetA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+LlsrProductLicensesGetW(
+ LLS_HANDLE Handle,
+ LPWSTR DisplayName,
+ DWORD Mode,
+ LPDWORD pQuantity )
+
+/*++
+
+Routine Description:
+
+ Returns the number of licenses installed for use in the given mode.
+
+Arguments:
+
+ Handle (LLS_HANDLE)
+ An open LLS handle.
+ Product (LPWSTR)
+ The name of the product for which to tally licenses.
+ Mode (DWORD)
+ Mode for which to tally licenses.
+ pQuantity (LPDWORD)
+ On return (and if successful), holds the total number of licenses
+ for use by the given product in the given license mode.
+
+Return Value:
+
+ STATUS_SUCCESS or NTSTATUS error code.
+
+--*/
+
+{
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsProductLicensesGetW\n"));
+#endif
+
+ *pQuantity = 0;
+
+ if ( !Mode || ServiceIsSecure( DisplayName ) )
+ {
+ // get limit from purchase list
+ *pQuantity = ProductLicensesGet( DisplayName, Mode );
+ }
+ else
+ {
+ DWORD i;
+
+ LocalServiceListUpdate();
+ LocalServerServiceListUpdate();
+ ServiceListResynch();
+
+ RtlAcquireResourceShared( &LocalServiceListLock, TRUE );
+
+ // get limit from concurrent limit setting from the registry
+ for ( i=0; i < LocalServiceListSize; i++ )
+ {
+ if ( !lstrcmpi( LocalServiceList[i]->DisplayName, DisplayName ) )
+ {
+ // get concurrent limit straight from the registry, not from LocalServiceList!
+ // (if the mode is set to per seat, the per server licenses in the
+ // LocalServiceList will always be 0!)
+ TCHAR szKeyName[ 512 ];
+ HKEY hKeyService;
+ DWORD dwSize;
+ DWORD dwType;
+
+ wsprintf( szKeyName, TEXT("System\\CurrentControlSet\\Services\\LicenseInfo\\%s"), LocalServiceList[i]->Name );
+
+ // if error encountered, return STATUS_SUCCESS with *pQuantity = 0
+ if ( STATUS_SUCCESS == RegOpenKeyEx( HKEY_LOCAL_MACHINE, szKeyName, 0, KEY_READ, &hKeyService ) )
+ {
+ dwSize = sizeof( *pQuantity );
+ RegQueryValueEx( hKeyService, TEXT("ConcurrentLimit"), NULL, &dwType, (LPBYTE) pQuantity, &dwSize );
+
+ RegCloseKey( hKeyService );
+ }
+
+ break;
+ }
+ }
+
+ RtlReleaseResource( &LocalServiceListLock );
+ }
+
+ return STATUS_SUCCESS;
+} // LlsProductLicensesGetW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+LlsrCertificateClaimEnumA(
+ LLS_HANDLE Handle,
+ DWORD LicenseLevel,
+ PLLS_LICENSE_INFOA LicensePtr,
+ PLLS_CERTIFICATE_CLAIM_ENUM_STRUCTA TargetInfo )
+
+/*++
+
+Routine Description:
+
+ Enumerates the servers on which a given secure certificate is installed.
+ This function is normally invoked when an attempt to add licenses from
+ a certificate is denied.
+
+ NOTE: Not yet implemented. Use LlsrCertificateClaimEnumW().
+
+Arguments:
+
+ Handle (LLS_HANDLE)
+ An open LLS handle.
+ LicenseLevel (DWORD)
+ The level of the license structure pointed to by pLicenseInfo.
+ LicensePtr (PLLS_LICENSE_INFOA)
+ Describes a license for which the certificate targets are requested.
+ TargetInfo (PLLS_CERTIFICATE_CLAIM_ENUM_STRUCTA)
+ Container in which to return the target information.
+
+Return Value:
+
+ STATUS_NOT_SUPPORTED.
+
+--*/
+
+{
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsrCertificateClaimEnumA\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+} // LlsrCertificateClaimEnumA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+LlsrCertificateClaimEnumW(
+ LLS_HANDLE Handle,
+ DWORD LicenseLevel,
+ PLLS_LICENSE_INFOW LicensePtr,
+ PLLS_CERTIFICATE_CLAIM_ENUM_STRUCTW TargetInfo )
+
+/*++
+
+Routine Description:
+
+ Enumerates the servers on which a given secure certificate is installed.
+ This function is normally invoked when an attempt to add licenses from
+ a certificate is denied.
+
+Arguments:
+
+ Handle (LLS_HANDLE)
+ An open LLS handle.
+ LicenseLevel (DWORD)
+ The level of the license structure pointed to by pLicenseInfo.
+ LicensePtr (PLLS_LICENSE_INFOW)
+ Describes a license for which the certificate targets are requested.
+ TargetInfo (PLLS_CERTIFICATE_CLAIM_ENUM_STRUCTA)
+ Container in which to return the target information.
+
+Return Value:
+
+ STATUS_SUCCESS or NTSTATUS error code.
+
+--*/
+
+{
+ NTSTATUS nt;
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsrCertificateClaimEnumW\n"));
+#endif
+
+ if ( ( 1 != LicenseLevel ) || ( 0 != TargetInfo->Level ) )
+ {
+ nt = STATUS_INVALID_LEVEL;
+ }
+ else
+ {
+ nt = CertDbClaimsGet( (PLLS_LICENSE_INFO_1) &LicensePtr->LicenseInfo1,
+ &TargetInfo->LlsCertificateClaimInfo.Level0->EntriesRead,
+ (PLLS_CERTIFICATE_CLAIM_INFO_0 *) &TargetInfo->LlsCertificateClaimInfo.Level0->Buffer );
+
+ if ( STATUS_SUCCESS != nt )
+ {
+ TargetInfo->LlsCertificateClaimInfo.Level0->EntriesRead = 0;
+ TargetInfo->LlsCertificateClaimInfo.Level0->Buffer = NULL;
+ }
+ }
+
+ return nt;
+} // LlsrCertificateClaimEnumW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+LlsrCertificateClaimAddCheckA(
+ LLS_HANDLE Handle,
+ DWORD LicenseLevel,
+ PLLS_LICENSE_INFOA LicensePtr,
+ LPBOOL pbMayInstall )
+
+/*++
+
+Routine Description:
+
+ Verify that no more licenses from a given certificate are installed in
+ a licensing enterprise than are allowed by the certificate.
+
+ NOTE: Not yet implemented. Use LlsrCertificateClaimAddCheckW().
+
+Arguments:
+
+ Handle (LLS_HANDLE)
+ An open LLS handle.
+ LicenseLevel (DWORD)
+ The level of the license structure pointed to by pLicenseInfo.
+ LicensePtr (PLLS_LICENSE_INFOA)
+ Describes a license for which permission is requested.
+ pbMayInstall (LPBOOL)
+ On return (and if successful), indicates whether the certificate
+ may be legally installed.
+
+Return Value:
+
+ STATUS_NOT_SUPPORTED.
+
+--*/
+
+{
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsrCertificateClaimAddCheckA\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+} // LlsrCertificateClaimAddCheckA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+LlsrCertificateClaimAddCheckW(
+ LLS_HANDLE Handle,
+ DWORD LicenseLevel,
+ PLLS_LICENSE_INFOW LicensePtr,
+ LPBOOL pbMayInstall )
+
+/*++
+
+Routine Description:
+
+ Verify that no more licenses from a given certificate are installed in
+ a licensing enterprise than are allowed by the certificate.
+
+Arguments:
+
+ Handle (LLS_HANDLE)
+ An open LLS handle.
+ LicenseLevel (DWORD)
+ The level of the license structure pointed to by pLicenseInfo.
+ LicensePtr (PLLS_LICENSE_INFOW)
+ Describes a license for which permission is requested.
+ pbMayInstall (LPBOOL)
+ On return (and if successful), indicates whether the certificate
+ may be legally installed.
+
+Return Value:
+
+ STATUS_SUCCESS or NTSTATUS error code.
+
+--*/
+
+{
+ NTSTATUS nt;
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsrCertificateClaimAddCheckW\n"));
+#endif
+
+ if ( 1 != LicenseLevel )
+ {
+ nt = STATUS_INVALID_LEVEL;
+ }
+ else
+ {
+ *pbMayInstall = CertDbClaimApprove( (PLLS_LICENSE_INFO_1) &LicensePtr->LicenseInfo1 );
+ nt = STATUS_SUCCESS;
+ }
+
+ return nt;
+} // LlsrCertificateClaimAddCheckW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+LlsrCertificateClaimAddA(
+ LLS_HANDLE Handle,
+ LPSTR ServerName,
+ DWORD LicenseLevel,
+ PLLS_LICENSE_INFOA LicensePtr )
+
+/*++
+
+Routine Description:
+
+ Declare a number of licenses from a given certificate as being installed
+ on the target machine.
+
+ NOTE: Not yet implemented. Use LlsCertificateClaimAddW().
+
+Arguments:
+
+ Handle (LLS_HANDLE)
+ An open LLS handle.
+ ServerName (LPWSTR)
+ Name of the server on which the licenses are installed.
+ LicenseLevel (DWORD)
+ The level of the license structure pointed to by pLicenseInfo.
+ LicensePtr (PLLS_LICENSE_INFOA)
+ Describes the installed license.
+
+Return Value:
+
+ STATUS_NOT_SUPPORTED.
+
+--*/
+
+{
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsrCertificateClaimAddA\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+} // LlsrCertificateClaimAddA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+LlsrCertificateClaimAddW(
+ LLS_HANDLE Handle,
+ LPWSTR ServerName,
+ DWORD LicenseLevel,
+ PLLS_LICENSE_INFOW LicensePtr )
+
+/*++
+
+Routine Description:
+
+ Declare a number of licenses from a given certificate as being installed
+ on the target machine.
+
+Arguments:
+
+ Handle (LLS_HANDLE)
+ An open LLS handle to the target license server.
+ ServerName (LPWSTR)
+ Name of the server on which the licenses are installed.
+ LicenseLevel (DWORD)
+ The level of the license structure pointed to by pLicenseInfo.
+ LicensePtr (PLLS_LICENSE_INFOW)
+ Describes the installed license.
+
+Return Value:
+
+ STATUS_SUCCESS or NTSTATUS error code.
+
+--*/
+
+{
+ NTSTATUS nt;
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsrCertificateClaimAddW\n"));
+#endif
+
+ if ( 1 != LicenseLevel )
+ {
+ nt = STATUS_INVALID_LEVEL;
+ }
+ else
+ {
+ nt = CertDbClaimEnter( ServerName, (PLLS_LICENSE_INFO_1) &LicensePtr->LicenseInfo1, FALSE, 0 );
+
+ if ( STATUS_SUCCESS == nt )
+ {
+ nt = CertDbSave();
+ }
+ }
+
+ return nt;
+} // LlsrCertificateClaimAddW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+LlsrReplicationCertDbAddW(
+ LLS_REPL_HANDLE Handle,
+ DWORD Level,
+ REPL_CERTIFICATES Certificates )
+
+/*++
+
+Routine Description:
+
+ Called as an optional part of replication, this function receives
+ the contents of the remote certificate database.
+
+Arguments:
+
+ Handle (LLS_REPL_HANDLE)
+ An open replication handle.
+ Level (DWORD)
+ Level of replicated certificate information.
+ Certificates (REPL_CERTIFICATES)
+ Replicated certificate information.
+
+Return Value:
+
+ STATUS_SUCCESS or STATUS_INVALID_LEVEL.
+
+--*/
+
+{
+ NTSTATUS nt;
+ REPL_CONTEXT_TYPE * pClient;
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsrReplicationCertDbAddW\n"));
+#endif
+
+ if ( ( 0 != Level )
+ || ( ( NULL != Certificates )
+ && ( ( 0 != Certificates->Level0.ClaimLevel )
+ || ( 0 != Certificates->Level0.HeaderLevel ) ) ) )
+ {
+ nt = STATUS_INVALID_LEVEL;
+ }
+ else
+ {
+ pClient = (REPL_CONTEXT_TYPE *) Handle;
+
+ pClient->CertDbSent = TRUE;
+
+ if ( NULL != Certificates )
+ {
+ pClient->CertDbProductStringSize = Certificates->Level0.StringSize;
+ pClient->CertDbProductStrings = Certificates->Level0.Strings;
+ pClient->CertDbNumHeaders = Certificates->Level0.HeaderContainer.Level0.NumHeaders;
+ pClient->CertDbHeaders = Certificates->Level0.HeaderContainer.Level0.Headers;
+ pClient->CertDbNumClaims = Certificates->Level0.ClaimContainer.Level0.NumClaims;
+ pClient->CertDbClaims = Certificates->Level0.ClaimContainer.Level0.Claims;
+
+ // free container only
+ MIDL_user_free( Certificates );
+ }
+
+ nt = STATUS_SUCCESS;
+ }
+
+ return nt;
+} // LlsrReplicationCertDbAddW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+LlsrReplicationProductSecurityAddW(
+ LLS_REPL_HANDLE Handle,
+ DWORD Level,
+ REPL_SECURE_PRODUCTS SecureProducts )
+
+/*++
+
+Routine Description:
+
+ Called as an optional part of replication, this function receives
+ the list of products which require secure certificates.
+
+Arguments:
+
+ Handle (LLS_REPL_HANDLE)
+ An open replication handle.
+ Level (DWORD)
+ Level of replicated secure product information.
+ SecureProducts (REPL_SECURE_PRODUCTS)
+ Replicated secure product information.
+
+Return Value:
+
+ STATUS_SUCCESS or STATUS_INVALID_LEVEL.
+
+--*/
+
+{
+ NTSTATUS nt;
+ REPL_CONTEXT_TYPE * pClient;
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsrReplicationProductSecurityAddW\n"));
+#endif
+
+ if ( 0 != Level )
+ {
+ nt = STATUS_INVALID_LEVEL;
+ }
+ else
+ {
+ pClient = (REPL_CONTEXT_TYPE *) Handle;
+
+ pClient->ProductSecuritySent = TRUE;
+
+ if ( NULL != SecureProducts )
+ {
+ pClient->ProductSecurityStringSize = SecureProducts->Level0.StringSize;
+ pClient->ProductSecurityStrings = SecureProducts->Level0.Strings;
+ }
+
+ nt = STATUS_SUCCESS;
+ }
+
+ return nt;
+} // LlsrReplicationProductSecurityAddW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+LlsrReplicationUserAddExW(
+ LLS_REPL_HANDLE Handle,
+ DWORD Level,
+ REPL_USERS Users )
+
+/*++
+
+Routine Description:
+
+ Replacement for LlsrReplicationUserAddW(). (This function, unlike its
+ counterpart, supports structure levels.) This function replicates the
+ user list.
+
+Arguments:
+
+ Handle (LLS_REPL_HANDLE)
+ An open replication handle.
+ Level (DWORD)
+ Level of replicated user information.
+ Users (REPL_USERS)
+ Replicated user information.
+
+Return Value:
+
+ STATUS_SUCCESS or STATUS_INVALID_LEVEL.
+
+--*/
+
+{
+ NTSTATUS nt;
+ REPL_CONTEXT_TYPE * pClient;
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsrReplicationUserAddExW\n"));
+#endif
+
+ if ( ( 0 != Level ) && ( 1 != Level ) )
+ {
+ nt = STATUS_INVALID_LEVEL;
+ }
+ else
+ {
+ pClient = (REPL_CONTEXT_TYPE *) Handle;
+
+ pClient->UsersSent = TRUE;
+ pClient->UserLevel = Level;
+
+ if ( NULL != Users )
+ {
+ if ( 0 == Level )
+ {
+ pClient->UserTableSize = Users->Level0.NumUsers;
+ pClient->Users = Users->Level0.Users;
+ }
+ else
+ {
+ pClient->UserTableSize = Users->Level1.NumUsers;
+ pClient->Users = Users->Level1.Users;
+ }
+
+ // free container only
+ MIDL_user_free( Users );
+ }
+
+ nt = STATUS_SUCCESS;
+ }
+
+ return nt;
+} // LlsrReplicationUserAddExW
+
+
+NTSTATUS
+LlsrCapabilityGet(
+ LLS_HANDLE Handle,
+ DWORD cbCapabilities,
+ LPBYTE pbCapabilities )
+{
+ static DWORD adwCapabilitiesSupported[] =
+ {
+ LLS_CAPABILITY_SECURE_CERTIFICATES,
+ LLS_CAPABILITY_REPLICATE_CERT_DB,
+ LLS_CAPABILITY_REPLICATE_PRODUCT_SECURITY,
+ LLS_CAPABILITY_REPLICATE_USERS_EX,
+ LLS_CAPABILITY_SERVICE_INFO_GETW,
+ LLS_CAPABILITY_LOCAL_SERVICE_API,
+ (DWORD) -1L
+ };
+
+ DWORD i;
+ DWORD dwCapByte;
+ DWORD dwCapBit;
+
+ ZeroMemory( pbCapabilities, cbCapabilities );
+
+ for ( i=0; (DWORD) -1L != adwCapabilitiesSupported[ i ]; i++ )
+ {
+ dwCapByte = adwCapabilitiesSupported[ i ] / 8;
+ dwCapBit = adwCapabilitiesSupported[ i ] - 8 * dwCapByte;
+
+ if ( dwCapByte < cbCapabilities )
+ {
+ pbCapabilities[ dwCapByte ] |= ( 1 << dwCapBit );
+ }
+ }
+
+ return STATUS_SUCCESS;
+}
+
+
+NTSTATUS
+LlsrLocalServiceEnumW(
+ LLS_HANDLE Handle,
+ PLLS_LOCAL_SERVICE_ENUM_STRUCTW LocalServiceInfo,
+ DWORD PrefMaxLen,
+ LPDWORD pTotalEntries,
+ LPDWORD pResumeHandle )
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ PVOID BufPtr = NULL;
+ ULONG BufSize = 0;
+ ULONG EntriesRead = 0;
+ ULONG TotalEntries = 0;
+ ULONG i = 0;
+ ULONG j = 0;
+ const DWORD RecordSize = sizeof( LLS_LOCAL_SERVICE_INFO_0W );
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsrLocalServiceEnumW\n"));
+#endif
+
+ if ( 0 != LocalServiceInfo->Level )
+ {
+ return STATUS_INVALID_LEVEL;
+ }
+
+ // Need to scan list so get read access.
+ RtlAcquireResourceShared(&LocalServiceListLock, TRUE);
+
+ // Calculate how many records will fit into PrefMaxLen buffer.
+ i = *pResumeHandle;
+ while ( ( i < LocalServiceListSize ) && ( BufSize < PrefMaxLen ) )
+ {
+ BufSize += RecordSize;
+ EntriesRead++;
+ i++;
+ }
+
+ TotalEntries = EntriesRead;
+
+ // If we overflowed the buffer then back up one record.
+ if (BufSize > PrefMaxLen)
+ {
+ BufSize -= RecordSize;
+ EntriesRead--;
+ }
+
+ // Now walk to the end of the list to see how many more records are still
+ // available.
+ TotalEntries += LocalServiceListSize - i;
+
+ if (TotalEntries > EntriesRead)
+ Status = STATUS_MORE_ENTRIES;
+
+ // Reset Enum to correct place.
+ i = *pResumeHandle;
+
+ // We now know how many records will fit into the buffer, so allocate space
+ // and fix up pointers so we can copy the information.
+ BufPtr = MIDL_user_allocate(BufSize);
+ if (BufPtr == NULL)
+ {
+ Status = STATUS_NO_MEMORY;
+ goto LlsrLocalServiceEnumWExit;
+ }
+
+ RtlZeroMemory((PVOID) BufPtr, BufSize);
+
+ // Buffers are all setup, so loop through records and copy the data.
+ while ((j < EntriesRead) && (i < LocalServiceListSize))
+ {
+ ((PLLS_LOCAL_SERVICE_INFO_0W) BufPtr)[j].KeyName = LocalServiceList[i]->Name;
+ ((PLLS_LOCAL_SERVICE_INFO_0W) BufPtr)[j].DisplayName = LocalServiceList[i]->DisplayName;
+ ((PLLS_LOCAL_SERVICE_INFO_0W) BufPtr)[j].FamilyDisplayName = LocalServiceList[i]->FamilyDisplayName;
+ ((PLLS_LOCAL_SERVICE_INFO_0W) BufPtr)[j].Mode = LocalServiceList[i]->Mode;
+ ((PLLS_LOCAL_SERVICE_INFO_0W) BufPtr)[j].FlipAllow = LocalServiceList[i]->FlipAllow;
+ ((PLLS_LOCAL_SERVICE_INFO_0W) BufPtr)[j].ConcurrentLimit = LocalServiceList[i]->ConcurrentLimit;
+ ((PLLS_LOCAL_SERVICE_INFO_0W) BufPtr)[j].HighMark = LocalServiceList[i]->HighMark;
+
+ j++;
+ i++;
+ }
+
+LlsrLocalServiceEnumWExit:
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT(" TotalEntries: %lu EntriesRead: %lu ResumeHandle: 0x%lX\n"), TotalEntries, EntriesRead, i);
+#endif
+ *pTotalEntries = TotalEntries;
+
+ *pResumeHandle = (ULONG) i;
+
+ LocalServiceInfo->LlsLocalServiceInfo.Level0->EntriesRead = EntriesRead;
+ LocalServiceInfo->LlsLocalServiceInfo.Level0->Buffer = (PLLS_LOCAL_SERVICE_INFO_0W) BufPtr;
+
+ RtlReleaseResource(&LocalServiceListLock);
+ return Status;
+}
+
+
+NTSTATUS
+LlsrLocalServiceEnumA(
+ LLS_HANDLE Handle,
+ PLLS_LOCAL_SERVICE_ENUM_STRUCTA LocalServiceInfo,
+ DWORD PrefMaxLen,
+ LPDWORD TotalEntries,
+ LPDWORD ResumeHandle )
+{
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsLocalServiceEnumA\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+}
+
+
+NTSTATUS
+LlsrLocalServiceAddW(
+ LLS_HANDLE Handle,
+ DWORD Level,
+ PLLS_LOCAL_SERVICE_INFOW LocalServiceInfo )
+{
+ NTSTATUS Status;
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsrLocalServiceAddW\n"));
+#endif
+
+ if ( 0 != Level )
+ {
+ Status = STATUS_INVALID_LEVEL;
+ }
+ else if ( ( NULL == LocalServiceInfo->LocalServiceInfo0.KeyName )
+ || ( NULL == LocalServiceInfo->LocalServiceInfo0.DisplayName )
+ || ( NULL == LocalServiceInfo->LocalServiceInfo0.FamilyDisplayName ) )
+ {
+ Status = STATUS_INVALID_PARAMETER;
+ }
+ else
+ {
+ LONG lError;
+ HKEY hKeyLicenseInfo;
+ HKEY hKeyService;
+ DWORD dwDisposition;
+
+ lError = RegOpenKeyEx( HKEY_LOCAL_MACHINE, REG_KEY_LICENSE, 0, KEY_WRITE, &hKeyLicenseInfo );
+
+ if ( ERROR_SUCCESS == lError )
+ {
+ // create key
+ lError = RegCreateKeyEx( hKeyLicenseInfo, LocalServiceInfo->LocalServiceInfo0.KeyName, 0, NULL, 0, KEY_WRITE, NULL, &hKeyService, &dwDisposition );
+
+ if ( ERROR_SUCCESS == lError )
+ {
+ // set DisplayName
+ lError = RegSetValueEx( hKeyService,
+ REG_VALUE_NAME,
+ 0,
+ REG_SZ,
+ (LPBYTE) LocalServiceInfo->LocalServiceInfo0.DisplayName,
+ ( sizeof( *LocalServiceInfo->LocalServiceInfo0.DisplayName )
+ * ( 1 + lstrlen( LocalServiceInfo->LocalServiceInfo0.DisplayName ) ) ) );
+
+ if ( ERROR_SUCCESS == lError )
+ {
+ // set FamilyDisplayName
+ lError = RegSetValueEx( hKeyService,
+ REG_VALUE_FAMILY,
+ 0,
+ REG_SZ,
+ (LPBYTE) LocalServiceInfo->LocalServiceInfo0.FamilyDisplayName,
+ ( sizeof( *LocalServiceInfo->LocalServiceInfo0.FamilyDisplayName )
+ * ( 1 + lstrlen( LocalServiceInfo->LocalServiceInfo0.FamilyDisplayName ) ) ) );
+ }
+
+ RegCloseKey( hKeyService );
+ }
+
+ RegCloseKey( hKeyLicenseInfo );
+ }
+
+ switch ( lError )
+ {
+ case ERROR_SUCCESS:
+ Status = STATUS_SUCCESS;
+ break;
+ case ERROR_FILE_NOT_FOUND:
+ case ERROR_PATH_NOT_FOUND:
+ Status = STATUS_OBJECT_NAME_NOT_FOUND;
+ break;
+ default:
+ Status = STATUS_UNSUCCESSFUL;
+ break;
+ }
+
+ if ( STATUS_SUCCESS == Status )
+ {
+ // set remaining items and update LocalServiceList
+ Status = LlsrLocalServiceInfoSetW( Handle, LocalServiceInfo->LocalServiceInfo0.KeyName, Level, LocalServiceInfo );
+ }
+ }
+
+ return Status;
+}
+
+
+NTSTATUS
+LlsrLocalServiceAddA(
+ LLS_HANDLE Handle,
+ DWORD Level,
+ PLLS_LOCAL_SERVICE_INFOA LocalServiceInfo )
+{
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsLocalServiceAddA\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+}
+
+
+NTSTATUS
+LlsrLocalServiceInfoSetW(
+ LLS_HANDLE Handle,
+ LPWSTR KeyName,
+ DWORD Level,
+ PLLS_LOCAL_SERVICE_INFOW LocalServiceInfo )
+{
+ NTSTATUS Status;
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsLocalServiceInfoSetW\n"));
+#endif
+
+ if ( 0 != Level )
+ {
+ Status = STATUS_INVALID_LEVEL;
+ }
+ else if ( NULL == KeyName )
+ {
+ Status = STATUS_INVALID_PARAMETER;
+ }
+ else
+ {
+ LONG lError;
+ HKEY hKeyLicenseInfo;
+ HKEY hKeyService;
+
+ lError = RegOpenKeyEx( HKEY_LOCAL_MACHINE, REG_KEY_LICENSE, 0, KEY_WRITE, &hKeyLicenseInfo );
+
+ if ( ERROR_SUCCESS == lError )
+ {
+ lError = RegOpenKeyEx( hKeyLicenseInfo, KeyName, 0, KEY_WRITE, &hKeyService );
+
+ if ( ERROR_SUCCESS == lError )
+ {
+ // set Mode
+ lError = RegSetValueEx( hKeyService, REG_VALUE_MODE, 0, REG_DWORD, (LPBYTE) &LocalServiceInfo->LocalServiceInfo0.Mode, sizeof( LocalServiceInfo->LocalServiceInfo0.Mode ) );
+
+ if ( ERROR_SUCCESS == lError )
+ {
+ // set FlipAllow
+ lError = RegSetValueEx( hKeyService, REG_VALUE_FLIP, 0, REG_DWORD, (LPBYTE) &LocalServiceInfo->LocalServiceInfo0.FlipAllow, sizeof( LocalServiceInfo->LocalServiceInfo0.FlipAllow ) );
+
+ if ( ERROR_SUCCESS == lError )
+ {
+ // set ConcurrentLimit
+ lError = RegSetValueEx( hKeyService, REG_VALUE_LIMIT, 0, REG_DWORD, (LPBYTE) &LocalServiceInfo->LocalServiceInfo0.ConcurrentLimit, sizeof( LocalServiceInfo->LocalServiceInfo0.ConcurrentLimit ) );
+ }
+ }
+
+ RegCloseKey( hKeyService );
+ }
+
+ RegCloseKey( hKeyLicenseInfo );
+ }
+
+ switch ( lError )
+ {
+ case ERROR_SUCCESS:
+ Status = STATUS_SUCCESS;
+ break;
+ case ERROR_FILE_NOT_FOUND:
+ case ERROR_PATH_NOT_FOUND:
+ Status = STATUS_OBJECT_NAME_NOT_FOUND;
+ break;
+ default:
+ Status = STATUS_UNSUCCESSFUL;
+ break;
+ }
+
+ if ( STATUS_SUCCESS == Status )
+ {
+ LocalServiceListUpdate();
+ LocalServerServiceListUpdate();
+ ServiceListResynch();
+ }
+ }
+
+ return Status;
+}
+
+
+NTSTATUS
+LlsrLocalServiceInfoSetA(
+ LLS_HANDLE Handle,
+ LPSTR KeyName,
+ DWORD Level,
+ PLLS_LOCAL_SERVICE_INFOA LocalServiceInfo )
+{
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsLocalServiceInfoSetA\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+}
+
+
+NTSTATUS
+LlsrLocalServiceInfoGetW(
+ LLS_HANDLE Handle,
+ LPWSTR KeyName,
+ DWORD Level,
+ PLLS_LOCAL_SERVICE_INFOW * pLocalServiceInfo )
+{
+ NTSTATUS Status;
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsrLocalServiceInfoGetW\n"));
+#endif
+
+ if ( 0 != Level )
+ {
+ Status = STATUS_INVALID_LEVEL;
+ }
+ else if ( NULL == KeyName )
+ {
+ Status = STATUS_INVALID_PARAMETER;
+ }
+ else
+ {
+ PLOCAL_SERVICE_RECORD pRecord;
+
+ RtlAcquireResourceShared(&LocalServiceListLock, TRUE);
+
+ pRecord = LocalServiceListFind( KeyName );
+
+ if ( NULL == pRecord )
+ {
+ Status = STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+ else
+ {
+ *pLocalServiceInfo = MIDL_user_allocate( sizeof( **pLocalServiceInfo ) );
+
+ if ( NULL == *pLocalServiceInfo )
+ {
+ Status = STATUS_NO_MEMORY;
+ }
+ else
+ {
+ (*pLocalServiceInfo)->LocalServiceInfo0.KeyName = pRecord->Name;
+ (*pLocalServiceInfo)->LocalServiceInfo0.DisplayName = pRecord->DisplayName;
+ (*pLocalServiceInfo)->LocalServiceInfo0.FamilyDisplayName = pRecord->FamilyDisplayName;
+ (*pLocalServiceInfo)->LocalServiceInfo0.Mode = pRecord->Mode;
+ (*pLocalServiceInfo)->LocalServiceInfo0.FlipAllow = pRecord->FlipAllow;
+ (*pLocalServiceInfo)->LocalServiceInfo0.ConcurrentLimit = pRecord->ConcurrentLimit;
+ (*pLocalServiceInfo)->LocalServiceInfo0.HighMark = pRecord->HighMark;
+
+ Status = STATUS_SUCCESS;
+ }
+ }
+
+ RtlReleaseResource(&LocalServiceListLock);
+ }
+
+ return Status;
+}
+
+
+NTSTATUS
+LlsrLocalServiceInfoGetA(
+ LLS_HANDLE Handle,
+ LPSTR KeyName,
+ DWORD Level,
+ PLLS_LOCAL_SERVICE_INFOA * pLocalServiceInfo )
+{
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsLocalServiceInfoGetA\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+}
+
+
diff --git a/private/net/svcdlls/lls/server/scaven.c b/private/net/svcdlls/lls/server/scaven.c
new file mode 100644
index 000000000..4c7e7c3b7
--- /dev/null
+++ b/private/net/svcdlls/lls/server/scaven.c
@@ -0,0 +1,198 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ scaven.c
+
+Abstract:
+
+Author:
+
+ Arthur Hanson (arth) 06-Jan-1995
+
+Revision History:
+
+ Jeff Parham (jeffparh) 05-Dec-1995
+ o Added periodic logging of certificate agreement violations.
+
+--*/
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windows.h>
+
+#include "llsapi.h"
+#include "debug.h"
+#include "llsutil.h"
+#include "llssrv.h"
+#include "registry.h"
+#include "ntlsapi.h"
+#include "mapping.h"
+#include "msvctbl.h"
+#include "svctbl.h"
+#include "purchase.h"
+#include "perseat.h"
+#include "server.h"
+#include "repl.h"
+#include "llsevent.h"
+#include "llsrpc_s.h"
+#include "certdb.h"
+
+NTSTATUS LLSDataSave();
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+ScavengerThread (
+ IN PVOID ThreadParameter
+ )
+
+/*++
+
+Routine Description:
+
+Arguments:
+
+ ThreadParameter - Indicates how many active threads there currently
+ are.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ ULONG i;
+ ULONG Count = 0;
+
+ //
+ // Just wait around forver waiting to service things.
+ //
+ while (TRUE) {
+ //
+ // Wait 15 minutes before checking things out.
+ //
+ Sleep(900000L);
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: ScavengerThread waking up\n"));
+#endif
+ //
+ // Update HighMark for local table
+ //
+ LocalServerServiceListHighMarkUpdate();
+
+ //
+ // Hmm, lets check replication...
+ //
+ ConfigInfoUpdate();
+ RtlEnterCriticalSection(&ConfigInfoLock);
+ if (ConfigInfo.Replicate) {
+ //
+ // If we are past replication time then do it
+ //
+ if (DateLocalGet() > ConfigInfo.NextReplication)
+ NtSetEvent( ReplicationEvent, NULL );
+
+ }
+ RtlLeaveCriticalSection(&ConfigInfoLock);
+
+ //
+ // Now update our last used time
+ //
+ RtlAcquireResourceExclusive(&UserListAddEnumLock, TRUE);
+ RtlAcquireResourceExclusive(&UserListLock, TRUE);
+ LastUsedTime = DateSystemGet();
+ RtlReleaseResource(&UserListLock);
+ RtlReleaseResource(&UserListAddEnumLock);
+
+ //
+ // Check stuff every 6 hours (4 * 15 minutes)
+ //
+ Count++;
+ if (Count > (6 * 4)) {
+ // Reset counter
+ Count = 0;
+
+ //
+ // Save out the data
+ //
+ LLSDataSave();
+
+ //
+ // Save HighMark to registry
+ //
+ LocalServiceListHighMarkSet();
+
+ if (IsMaster) {
+ //
+ // Check for license compliance
+ //
+ RtlAcquireResourceShared(&MasterServiceListLock, TRUE);
+
+ for (i = 0; i < MasterServiceListSize; i++) {
+ if (MasterServiceList[i]->LicensesUsed > MasterServiceList[i]->Licenses) {
+ LPWSTR SubString[1];
+
+ //
+ // Notify the system
+ //
+ SubString[0] = (LPWSTR) MasterServiceList[i]->Name;
+
+ LogEvent(LLS_EVENT_PRODUCT_NO_LICENSE, 1, SubString, ERROR_SUCCESS);
+ }
+ }
+
+ RtlReleaseResource(&MasterServiceListLock);
+
+ // log certificate violations
+ CertDbLogViolations();
+ }
+ }
+ }
+
+} // ScavengerThread
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+ScavengerInit( )
+
+/*++
+
+Routine Description:
+
+ Looks in registry for given service and sets values accordingly.
+
+Arguments:
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ HANDLE Thread;
+ DWORD Ignore;
+
+ //
+ // Just dispatch our scavenger thread
+ //
+ Thread = CreateThread(
+ NULL,
+ 0L,
+ (LPTHREAD_START_ROUTINE) ScavengerThread,
+ 0L,
+ 0L,
+ &Ignore
+ );
+
+} // ScavengerInit
+
+
diff --git a/private/net/svcdlls/lls/server/scaven.h b/private/net/svcdlls/lls/server/scaven.h
new file mode 100644
index 000000000..a6a0c14b7
--- /dev/null
+++ b/private/net/svcdlls/lls/server/scaven.h
@@ -0,0 +1,38 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ Scaven.h
+
+Abstract:
+
+
+Author:
+
+ Arthur Hanson (arth) Dec 07, 1994
+
+Environment:
+
+Revision History:
+
+--*/
+
+#ifndef _LLS_SCAVEN_H
+#define _LLS_SCAVEN_H
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+VOID ScavengerInit( );
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/private/net/svcdlls/lls/server/server.c b/private/net/svcdlls/lls/server/server.c
new file mode 100644
index 000000000..459a6e984
--- /dev/null
+++ b/private/net/svcdlls/lls/server/server.c
@@ -0,0 +1,754 @@
+/*++
+
+Copyright (c) 1994 Microsoft Corporation
+
+Module Name:
+
+ server.c
+
+Abstract:
+
+
+Author:
+
+ Arthur Hanson (arth) 07-Dec-1994
+
+Revision History:
+
+--*/
+
+#include <stdlib.h>
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windows.h>
+
+#include "llsapi.h"
+#include "debug.h"
+#include "llsutil.h"
+#include "llssrv.h"
+#include "registry.h"
+#include "ntlsapi.h"
+#include "mapping.h"
+#include "msvctbl.h"
+#include "svctbl.h"
+#include "purchase.h"
+#include "perseat.h"
+#include "server.h"
+
+#define NO_LLS_APIS
+#include "llsapi.h"
+
+
+/////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////
+
+ULONG ServerListSize = 0;
+PSERVER_RECORD *ServerList = NULL;
+PSERVER_RECORD *ServerTable = NULL;
+
+RTL_RESOURCE ServerListLock;
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+ServerListInit()
+
+/*++
+
+Routine Description:
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ RtlInitializeResource(&ServerListLock);
+
+ //
+ // Add ourself as the first server (master server)
+ //
+ RtlEnterCriticalSection(&ConfigInfoLock);
+ ServerListAdd( ConfigInfo.ComputerName, NULL);
+ RtlLeaveCriticalSection(&ConfigInfoLock);
+ LocalServerServiceListUpdate();
+
+} // ServerListInit
+
+
+/////////////////////////////////////////////////////////////////////////
+int __cdecl ServerListCompare(const void *arg1, const void *arg2) {
+ PSERVER_RECORD Svc1, Svc2;
+
+ Svc1 = (PSERVER_RECORD) *((PSERVER_RECORD *) arg1);
+ Svc2 = (PSERVER_RECORD) *((PSERVER_RECORD *) arg2);
+
+ return lstrcmpi( Svc1->Name, Svc2->Name );
+
+} // ServerListCompare
+
+
+/////////////////////////////////////////////////////////////////////////
+int __cdecl ServerServiceListCompare(const void *arg1, const void *arg2) {
+ PSERVER_SERVICE_RECORD Svc1, Svc2;
+
+ Svc1 = (PSERVER_SERVICE_RECORD) *((PSERVER_SERVICE_RECORD *) arg1);
+ Svc2 = (PSERVER_SERVICE_RECORD) *((PSERVER_SERVICE_RECORD *) arg2);
+
+ return lstrcmpi( MasterServiceTable[Svc1->Service]->Name, MasterServiceTable[Svc2->Service]->Name );
+
+} // ServerServiceListCompare
+
+
+/////////////////////////////////////////////////////////////////////////
+PSERVER_SERVICE_RECORD
+ServerServiceListFind(
+ LPTSTR Name,
+ ULONG ServiceTableSize,
+ PSERVER_SERVICE_RECORD *ServiceList
+ )
+
+/*++
+
+Routine Description:
+
+ Internal routine to actually do binary search on ServerServiceList, this
+ does not do any locking as we expect the wrapper routine to do this.
+ The search is a simple binary search.
+
+Arguments:
+
+
+Return Value:
+
+ Pointer to found service table entry or NULL if not found.
+
+--*/
+
+{
+ LONG begin = 0;
+ LONG end;
+ LONG cur;
+ int match;
+ PMASTER_SERVICE_RECORD Service;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: ServerServiceListFind\n"));
+#endif
+
+ if (ServiceTableSize == 0)
+ return NULL;
+
+ end = (LONG) ServiceTableSize - 1;
+
+ while (end >= begin) {
+ // go halfway in-between
+ cur = (begin + end) / 2;
+ Service = MasterServiceTable[ServiceList[cur]->Service];
+
+ // compare the two result into match
+ match = lstrcmpi(Name, Service->Name);
+
+ if (match < 0)
+ // move new begin
+ end = cur - 1;
+ else
+ begin = cur + 1;
+
+ if (match == 0)
+ return ServiceList[cur];
+ }
+
+ return NULL;
+
+} // ServerServiceListFind
+
+
+/////////////////////////////////////////////////////////////////////////
+PSERVER_RECORD
+ServerListFind(
+ LPTSTR Name
+ )
+
+/*++
+
+Routine Description:
+
+ Internal routine to actually do binary search on ServerList, this
+ does not do any locking as we expect the wrapper routine to do this.
+ The search is a simple binary search.
+
+Arguments:
+
+ ServiceName -
+
+Return Value:
+
+ Pointer to found server table entry or NULL if not found.
+
+--*/
+
+{
+ LONG begin = 0;
+ LONG end = (LONG) ServerListSize - 1;
+ LONG cur;
+ int match;
+ PSERVER_RECORD Server;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: ServerListFind\n"));
+#endif
+
+ if ((ServerListSize == 0) || (Name == NULL))
+ return NULL;
+
+ while (end >= begin) {
+ // go halfway in-between
+ cur = (begin + end) / 2;
+ Server = ServerList[cur];
+
+ // compare the two result into match
+ match = lstrcmpi(Name, Server->Name);
+
+ if (match < 0)
+ // move new begin
+ end = cur - 1;
+ else
+ begin = cur + 1;
+
+ if (match == 0)
+ return Server;
+ }
+
+ return NULL;
+
+} // ServerListFind
+
+
+/////////////////////////////////////////////////////////////////////////
+PSERVER_SERVICE_RECORD
+ServerServiceListAdd(
+ LPTSTR Name,
+ ULONG ServiceIndex,
+ PULONG pServiceTableSize,
+ PSERVER_SERVICE_RECORD **pServiceList
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+ ServiceName -
+
+Return Value:
+
+ Pointer to added service table entry, or NULL if failed.
+
+--*/
+
+{
+ LPTSTR NewName;
+ PSERVER_SERVICE_RECORD Service;
+ PSERVER_SERVICE_RECORD *ServiceList;
+ ULONG ServiceListSize;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: ServerServiceListAdd\n"));
+#endif
+
+ if ((Name == NULL) || (*Name == TEXT('\0')) || (pServiceTableSize == NULL) || (pServiceList == NULL)) {
+#if DBG
+ dprintf(TEXT("Error LLS: ServerServiceListAdd Bad Parms\n"));
+#endif
+ ASSERT(FALSE);
+ return NULL;
+ }
+
+ ServiceListSize = *pServiceTableSize;
+ ServiceList = *pServiceList;
+
+ //
+ // Try to find the name
+ //
+ Service = ServerServiceListFind(Name, ServiceListSize, ServiceList);
+ if (Service != NULL) {
+ Service->Service = ServiceIndex;
+ return Service;
+ }
+
+ //
+ // No record - so create a new one
+ //
+ if (ServiceList == NULL) {
+ ServiceList = (PSERVER_SERVICE_RECORD *) LocalAlloc(LPTR, sizeof(PSERVER_SERVICE_RECORD));
+ } else {
+ ServiceList = (PSERVER_SERVICE_RECORD *) LocalReAlloc(ServiceList, sizeof(PSERVER_SERVICE_RECORD) * (ServiceListSize + 1), LHND);
+ }
+
+ //
+ // Make sure we could allocate server table
+ //
+ if (ServiceList == NULL) {
+ ServiceListSize = 0;
+ ASSERT(FALSE);
+ goto ServerServiceListAddExit;
+ }
+
+ //
+ // Allocate space for Record.
+ //
+ Service = (PSERVER_SERVICE_RECORD) LocalAlloc(LPTR, sizeof(SERVER_SERVICE_RECORD));
+ if (Service == NULL) {
+ ASSERT(FALSE);
+ return NULL;
+ }
+
+ ServiceList[ServiceListSize] = Service;
+
+ //
+ // Initialize other stuff
+ //
+ Service->Service = ServiceIndex;
+ Service->MaxSessionCount = 0;
+ Service->MaxSetSessionCount = 0;
+ Service->HighMark = 0;
+ Service->Flags = 0;
+
+ ServiceListSize++;
+
+ // Have added the entry - now need to sort it in order of the service names
+ qsort((void *) ServiceList, (size_t) ServiceListSize, sizeof(PSERVER_SERVICE_RECORD), ServerServiceListCompare);
+
+ServerServiceListAddExit:
+ *pServiceTableSize = ServiceListSize;
+ *pServiceList = ServiceList;
+ return Service;
+
+} // ServerServiceListAdd
+
+
+/////////////////////////////////////////////////////////////////////////
+PSERVER_RECORD
+ServerListAdd(
+ LPTSTR Name,
+ LPTSTR Master
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+ ServiceName -
+
+Return Value:
+
+ Pointer to added service table entry, or NULL if failed.
+
+--*/
+
+{
+ LPTSTR NewName;
+ PSERVER_RECORD Server;
+ PSERVER_RECORD pMaster;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: ServerListAdd\n"));
+#endif
+
+ if ((Name == NULL) || (*Name == TEXT('\0'))) {
+#if DBG
+ dprintf(TEXT("Error LLS: ServerListAdd Bad Parms\n"));
+#endif
+ ASSERT(FALSE);
+ return NULL;
+ }
+
+ //
+ // Try to find the name
+ //
+ Server = ServerListFind(Name);
+ if (Server != NULL) {
+ return Server;
+ }
+
+ //
+ // No record - so create a new one
+ //
+ if (ServerList == NULL) {
+ ServerList = (PSERVER_RECORD *) LocalAlloc(LPTR, sizeof(PSERVER_RECORD));
+ ServerTable = (PSERVER_RECORD *) LocalAlloc(LPTR, sizeof(PSERVER_RECORD));
+ } else {
+ ServerList = (PSERVER_RECORD *) LocalReAlloc(ServerList, sizeof(PSERVER_RECORD) * (ServerListSize + 1), LHND);
+ ServerTable = (PSERVER_RECORD *) LocalReAlloc(ServerTable, sizeof(PSERVER_RECORD) * (ServerListSize + 1), LHND);
+ }
+
+ //
+ // Make sure we could allocate server table
+ //
+ if ((ServerList == NULL) || (ServerTable == NULL)) {
+ ASSERT(FALSE);
+ ServerList = NULL;
+ ServerTable = NULL;
+ ServerListSize = 0;
+ return NULL;
+ }
+
+ //
+ // Allocate space for Record.
+ //
+ Server = (PSERVER_RECORD) LocalAlloc(LPTR, sizeof(SERVER_RECORD));
+ if (Server == NULL) {
+ ASSERT(FALSE);
+ return NULL;
+ }
+
+ ServerList[ServerListSize] = Server;
+ ServerTable[ServerListSize] = Server;
+
+ NewName = (LPTSTR) LocalAlloc(LPTR, (lstrlen(Name) + 1) * sizeof(TCHAR));
+ if (NewName == NULL) {
+ ASSERT(FALSE);
+ LocalFree(Server);
+ return NULL;
+ }
+
+ // now copy it over...
+ Server->Name = NewName;
+ lstrcpy(NewName, Name);
+
+ //
+ // Initialize other stuff
+ //
+ Server->Index = ServerListSize + 1;
+ Server->LastReplicated = 0;
+ Server->IsReplicating = FALSE;
+
+ //
+ // Fixup slave/master chain
+ //
+ Server->MasterServer = 0;
+ Server->NextServer = 0;
+ if (Master != NULL) {
+ pMaster = ServerListFind(Master);
+
+ if (pMaster != NULL) {
+ Server->MasterServer = pMaster->Index;
+ Server->NextServer = pMaster->SlaveServer;
+ pMaster->SlaveServer = Server->Index;
+ } else {
+ ASSERT(FALSE);
+ }
+ }
+
+ Server->SlaveServer = 0;
+
+ Server->ServiceTableSize = 0;
+ Server->Services = NULL;
+
+ ServerListSize++;
+
+ // Have added the entry - now need to sort it in order of the service names
+ qsort((void *) ServerList, (size_t) ServerListSize, sizeof(PSERVER_RECORD), ServerListCompare);
+
+ return Server;
+
+} // ServerListAdd
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+LocalServerServiceListUpdate(
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ PSERVER_RECORD Server;
+ PMASTER_SERVICE_RECORD Service;
+ PSERVER_SERVICE_RECORD ServerService;
+ ULONG i, Index;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: LocalServerServiceListUpdate\n"));
+#endif
+
+ //
+ // Find our local server in the Server table
+ //
+ RtlEnterCriticalSection(&ConfigInfoLock);
+ Server = ServerListFind( ConfigInfo.ComputerName );
+ RtlLeaveCriticalSection(&ConfigInfoLock);
+
+ ASSERT(Server != NULL);
+ if (Server == NULL)
+ return;
+
+ RtlAcquireResourceShared(&LocalServiceListLock, TRUE);
+ RtlAcquireResourceShared(&MasterServiceListLock, TRUE);
+
+ for (i = 0; i < LocalServiceListSize; i++) {
+ Service = MasterServiceListFind(LocalServiceList[i]->DisplayName);
+ if (Service == NULL) {
+ RtlConvertSharedToExclusive(&MasterServiceListLock);
+ Service = MasterServiceListAdd(LocalServiceList[i]->FamilyDisplayName, LocalServiceList[i]->DisplayName, 0);
+ RtlConvertExclusiveToShared(&MasterServiceListLock);
+ }
+
+ if (Service != NULL) {
+ ServerService = ServerServiceListAdd( Service->Name, Service->Index, &Server->ServiceTableSize, &Server->Services );
+
+ ASSERT(ServerService != NULL);
+ if (ServerService != NULL) {
+ //
+ // Update high mark if needed
+ //
+ if ( LocalServiceList[i]->HighMark > ServerService->HighMark )
+ {
+ ServerService->HighMark = LocalServiceList[i]->HighMark;
+ }
+
+ //
+ // Subtract any old licenses we might have
+ //
+ Service->MaxSessionCount -= ServerService->MaxSessionCount;
+
+ //
+ // Now update to current Licenses
+ //
+ ServerService->MaxSessionCount = LocalServiceList[i]->ConcurrentLimit;
+ if (LocalServiceList[i]->ConcurrentLimit > ServerService->MaxSetSessionCount)
+ ServerService->MaxSetSessionCount = LocalServiceList[i]->ConcurrentLimit;
+
+ Service->MaxSessionCount += ServerService->MaxSessionCount;
+ ServerService->Flags &= ~LLS_FLAG_PRODUCT_PERSEAT;
+
+ if (LocalServiceList[i]->Mode == 0)
+ ServerService->Flags |= LLS_FLAG_PRODUCT_PERSEAT;
+
+ }
+
+ }
+
+ }
+
+ RtlReleaseResource(&MasterServiceListLock);
+ RtlReleaseResource(&LocalServiceListLock);
+
+} // LocalServerServiceListUpdate
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+LocalServerServiceListHighMarkUpdate(
+ )
+
+/*++
+
+Routine Description:
+
+ We've got to do this separatly because it locks the Service Table
+ and it needs to be done in reverse. I.E. We need to run through
+ the Service Table to get the display names and then look it up in
+ the ServerServicesList instead of running through the
+ ServerServicesList.
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ PSERVER_RECORD Server;
+ PSERVER_SERVICE_RECORD ServerService;
+ PMASTER_SERVICE_RECORD Service;
+ ULONG i;
+
+ //
+ // Find our local server in the Server table
+ //
+ RtlEnterCriticalSection(&ConfigInfoLock);
+ Server = ServerListFind( ConfigInfo.ComputerName );
+ RtlLeaveCriticalSection(&ConfigInfoLock);
+
+ ASSERT(Server != NULL);
+ if (Server == NULL)
+ return;
+
+ RtlAcquireResourceShared(&MasterServiceListLock, TRUE);
+ RtlAcquireResourceShared(&ServiceListLock, TRUE);
+
+ for (i = 0; i < ServiceListSize; i++) {
+
+ ServerService = ServerServiceListFind( ServiceList[i]->DisplayName, Server->ServiceTableSize, Server->Services );
+
+ if (ServerService != NULL) {
+ Service = MasterServiceListFind(ServiceList[i]->DisplayName);
+ ASSERT(Service != NULL);
+
+ if (Service != NULL) {
+ //
+ // Subtract any old info we might have
+ //
+ if (Service->HighMark != 0)
+ {
+ Service->HighMark -= ServerService->HighMark;
+ }
+
+ //
+ // Now update to current Licenses
+ //
+ ServerService->HighMark = ServiceList[i]->HighMark;
+ Service->HighMark += ServerService->HighMark;
+ }
+ }
+
+ }
+
+ RtlReleaseResource(&ServiceListLock);
+ RtlReleaseResource(&MasterServiceListLock);
+
+} // LocalServerServiceListHighMarkUpdate
+
+
+
+#if DBG
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+ServerListDebugDump( )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ ULONG i = 0;
+
+ //
+ // Need to scan list so get read access.
+ //
+ RtlAcquireResourceShared(&ServerListLock, TRUE);
+
+ dprintf(TEXT("Server Table, # Entries: %lu\n"), ServerListSize);
+ if (ServerList == NULL)
+ goto ServerListDebugDumpExit;
+
+ for (i = 0; i < ServerListSize; i++) {
+ dprintf(TEXT("%3lu) [%3lu] LR: %s #Svc: %4lu M: %3lu S: %3lu N: %3lu Server: %s\n"),
+ i + 1, ServerList[i]->Index, TimeToString(ServerList[i]->LastReplicated), ServerList[i]->ServiceTableSize,
+ ServerList[i]->MasterServer, ServerList[i]->SlaveServer, ServerList[i]->NextServer, ServerList[i]->Name);
+ }
+
+ServerListDebugDumpExit:
+ RtlReleaseResource(&ServerListLock);
+
+ return;
+} // ServerListDebugDump
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+ServerListDebugInfoDump( PVOID Data )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ ULONG i = 0;
+ PSERVER_RECORD Server = NULL;
+
+ //
+ // Need to scan list so get read access.
+ //
+ RtlAcquireResourceShared(&ServerListLock, TRUE);
+
+ dprintf(TEXT("Server Table, # Entries: %lu\n"), ServerListSize);
+ if (ServerList == NULL)
+ goto ServerListDebugInfoDumpExit;
+
+ if (Data == NULL)
+ goto ServerListDebugInfoDumpExit;
+
+ Server = ServerListFind( (LPTSTR) Data );
+ if (Server == NULL) {
+ dprintf(TEXT("Server not found: %s\n"), (LPTSTR) Data );
+ goto ServerListDebugInfoDumpExit;
+ }
+
+ //
+ // Show server
+ //
+ dprintf(TEXT("[%3lu] LR: %s #Svc: %4lu M: %3lu S: %3lu N: %3lu Server: %s\n"),
+ Server->Index, TimeToString(Server->LastReplicated), Server->ServiceTableSize,
+ Server->MasterServer, Server->SlaveServer, Server->NextServer, Server->Name);
+
+ //
+ // Now all the services for this server
+ //
+ RtlAcquireResourceShared(&MasterServiceListLock, TRUE);
+ for (i = 0; i < Server->ServiceTableSize; i++) {
+ dprintf(TEXT(" %3lu) Flags: 0x%4lX MS: %3lu HM: %3lu SHM: %3lu Service: %s\n"),
+ i + 1, Server->Services[i]->Flags, Server->Services[i]->MaxSessionCount, Server->Services[i]->HighMark,
+ Server->Services[i]->MaxSetSessionCount, MasterServiceTable[Server->Services[i]->Service]->Name);
+
+ }
+ RtlReleaseResource(&MasterServiceListLock);
+
+ServerListDebugInfoDumpExit:
+ RtlReleaseResource(&ServerListLock);
+
+ return;
+} // ServerListDebugInfoDump
+
+#endif
diff --git a/private/net/svcdlls/lls/server/server.h b/private/net/svcdlls/lls/server/server.h
new file mode 100644
index 000000000..15dafe202
--- /dev/null
+++ b/private/net/svcdlls/lls/server/server.h
@@ -0,0 +1,84 @@
+/*++
+
+Copyright (c) 1994 Microsoft Corporation
+
+Module Name:
+
+ Server.h
+
+Abstract:
+
+
+Author:
+
+ Arthur Hanson (arth) Dec 07, 1994
+
+Environment:
+
+Revision History:
+
+--*/
+
+
+#ifndef _LLS_SERVERTBL_H
+#define _LLS_SERVERTBL_H
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct _SERVER_SERVICE_RECORD {
+ ULONG Service;
+ DWORD Flags;
+
+ ULONG MaxSessionCount; // Max # simultaneous sessions
+ ULONG MaxSetSessionCount; // Max # simultaneous sessions ever set
+ ULONG HighMark; // Max # simultaneous sessions ever attempted
+} SERVER_SERVICE_RECORD, *PSERVER_SERVICE_RECORD;
+
+
+typedef struct _SERVER_RECORD {
+ ULONG Index;
+ LPTSTR Name;
+
+ DWORD LastReplicated;
+ BOOL IsReplicating;
+
+ ULONG MasterServer;
+ ULONG SlaveServer;
+ ULONG NextServer;
+ ULONG ServiceTableSize;
+ PSERVER_SERVICE_RECORD *Services;
+} SERVER_RECORD, *PSERVER_RECORD;
+
+
+extern ULONG ServerListSize;
+extern PSERVER_RECORD *ServerList;
+extern PSERVER_RECORD *ServerTable;
+
+extern RTL_RESOURCE ServerListLock;
+
+
+VOID ServerListInit();
+PSERVER_RECORD ServerListFind( LPTSTR Name );
+PSERVER_RECORD ServerListAdd( LPTSTR Name, LPTSTR Master );
+
+PSERVER_SERVICE_RECORD ServerServiceListFind( LPTSTR Name, ULONG ServiceTableSize, PSERVER_SERVICE_RECORD *ServiceList );
+PSERVER_SERVICE_RECORD ServerServiceListAdd( LPTSTR Name, ULONG ServiceIndex, PULONG pServiceTableSize, PSERVER_SERVICE_RECORD **pServiceList );
+VOID LocalServerServiceListUpdate();
+VOID LocalServerServiceListHighMarkUpdate();
+
+
+#if DBG
+
+VOID ServerListDebugDump( );
+VOID ServerListDebugInfoDump( PVOID Data );
+#endif
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/private/net/svcdlls/lls/server/service.c b/private/net/svcdlls/lls/server/service.c
new file mode 100644
index 000000000..53c4050e1
--- /dev/null
+++ b/private/net/svcdlls/lls/server/service.c
@@ -0,0 +1,625 @@
+/*++
+
+Copyright (c) 1994 Microsoft Corporation
+
+Module Name:
+
+ Service.c
+
+Abstract:
+
+ License Logging Service - Common routines for all service.
+
+Author:
+
+ Arthur Hanson (arth) Dec 07, 1994
+
+Environment:
+
+Revision History:
+
+--*/
+
+#include <windows.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <process.h>
+#include <tchar.h>
+#include <shellapi.h>
+
+#include "service.h"
+
+
+
+// internal variables
+SERVICE_STATUS ssStatus; // current status of the service
+SERVICE_STATUS_HANDLE sshStatusHandle;
+DWORD dwErr = 0;
+BOOL bDebug = FALSE;
+TCHAR szErr[256];
+
+// internal function prototypes
+VOID WINAPI ServiceCtrl(DWORD dwCtrlCode);
+VOID WINAPI ServiceMain(DWORD dwArgc, LPTSTR *lpszArgv);
+VOID CmdInstallService();
+VOID CmdRemoveService();
+VOID CmdDebugService(int argc, char **argv);
+BOOL WINAPI ControlHandler ( DWORD dwCtrlType );
+LPTSTR GetLastErrorText( LPTSTR lpszBuf, DWORD dwSize );
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID _CRTAPI1
+main(
+ int argc,
+ char **argv
+ )
+/*++
+
+Routine Description:
+
+ Main routine to setup the exception handlers and initialize everything
+ before spawning threads to listen to LPC and RPC port requests.
+
+ main() either performs the command line task, or calls
+ StartServiceCtrlDispatcher to register the main service thread. When the
+ this call returns, the service has stopped, so exit.
+
+Arguments:
+
+ argc - number of command line arguments
+ argv - array of command line arguments
+
+Return Values:
+
+ None.
+
+--*/
+{
+ SERVICE_TABLE_ENTRY dispatchTable[] = {
+ { TEXT(SZSERVICENAME), (LPSERVICE_MAIN_FUNCTION) ServiceMain },
+ { NULL, NULL }
+ };
+
+ if ( (argc > 1) && ((*argv[1] == '-') || (*argv[1] == '/')) ) {
+ if ( _stricmp( "install", argv[1]+1 ) == 0 ) {
+ CmdInstallService();
+ } else if ( _stricmp( "remove", argv[1]+1 ) == 0 ) {
+ CmdRemoveService();
+ } else if ( _stricmp( "debug", argv[1]+1 ) == 0 ) {
+ bDebug = TRUE;
+ CmdDebugService(argc, argv);
+ } else {
+ goto dispatch;
+ }
+
+ exit(0);
+ }
+
+ // if it doesn't match any of the above parameters
+ // the service control manager may be starting the service
+ // so we must call StartServiceCtrlDispatcher
+ dispatch:
+#ifdef DEBUG
+ // this is just to be friendly
+ printf( "%s -install to install the service\n", SZAPPNAME );
+ printf( "%s -remove to remove the service\n", SZAPPNAME );
+ printf( "%s -debug <params> to run as a console app for debugging\n", SZAPPNAME );
+ printf( "\nStartServiceCtrlDispatcher being called.\n" );
+ printf( "This may take several seconds. Please wait.\n" );
+#endif
+
+ if (!StartServiceCtrlDispatcher(dispatchTable))
+ MessageLogWrite(TEXT("StartServiceCtrlDispatcher failed."));
+
+} // main
+
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID WINAPI
+ServiceMain(
+ DWORD dwArgc,
+ LPTSTR *lpszArgv
+ )
+/*++
+
+Routine Description:
+
+ Performs the service initialization and then calls the ServiceStart()
+ routine to perform majority of the work.
+
+Arguments:
+
+ dwArgc - number of command line arguments
+ lpszArgv - array of command line arguments
+
+Return Values:
+
+ None.
+
+--*/
+{
+
+ // register our service control handler:
+ //
+ sshStatusHandle = RegisterServiceCtrlHandler( TEXT(SZSERVICENAME), ServiceCtrl);
+
+ if (!sshStatusHandle)
+ goto cleanup;
+
+ // SERVICE_STATUS members that don't change in example
+ //
+ ssStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
+ ssStatus.dwServiceSpecificExitCode = 0;
+
+
+ // report the status to the service control manager.
+ //
+ if (!ReportStatusToSCMgr(
+ SERVICE_START_PENDING, // service state
+ NO_ERROR, // exit code
+ 3000)) // wait hint
+ goto cleanup;
+
+
+ ServiceStart( dwArgc, lpszArgv );
+
+cleanup:
+
+ // try to report the stopped status to the service control manager.
+ //
+ if (sshStatusHandle)
+ (VOID)ReportStatusToSCMgr(
+ SERVICE_STOPPED,
+ dwErr,
+ 0);
+
+ return;
+} // ServiceMain
+
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID WINAPI
+ServiceCtrl(
+ DWORD dwCtrlCode
+ )
+/*++
+
+Routine Description:
+
+ Called by the SCM whenever ControlService() is called on this service.
+
+Arguments:
+
+ dwCtrlCode - type of control requested
+
+Return Values:
+
+ None.
+
+--*/
+{
+ DWORD dwState = SERVICE_RUNNING;
+
+ // Handle the requested control code.
+ //
+ switch(dwCtrlCode) {
+ // Stop the service.
+ //
+ case SERVICE_CONTROL_STOP:
+ dwState = SERVICE_STOP_PENDING;
+ ssStatus.dwCurrentState = SERVICE_STOP_PENDING;
+ break;
+
+ // Update the service status.
+ //
+ case SERVICE_CONTROL_INTERROGATE:
+ break;
+
+ // invalid control code
+ //
+ default:
+ break;
+
+ }
+
+ ReportStatusToSCMgr(dwState, NO_ERROR, 0);
+
+ if ( SERVICE_CONTROL_STOP == dwCtrlCode )
+ {
+ ServiceStop();
+ }
+} // ServiceCtrl
+
+
+
+/////////////////////////////////////////////////////////////////////////
+BOOL
+ReportStatusToSCMgr(
+ DWORD dwCurrentState,
+ DWORD dwWin32ExitCode,
+ DWORD dwWaitHint
+ )
+/*++
+
+Routine Description:
+
+ Sets the current status of the service and reports it to the SCM.
+
+Arguments:
+
+ dwCurrentState - the state of the service
+ dwWin32ExitCode - error code to report
+ dwWaitHint - worst case estimate to next checkpoint
+
+Return Values:
+
+ None.
+
+--*/
+{
+ static DWORD dwCheckPoint = 1;
+ BOOL fResult = TRUE;
+
+ ssStatus.dwControlsAccepted = 0;
+ if ( !bDebug ) { // when debugging we don't report to the SCM
+ if (dwCurrentState == SERVICE_START_PENDING)
+ ssStatus.dwControlsAccepted = 0;
+ else
+ ssStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
+
+ ssStatus.dwCurrentState = dwCurrentState;
+ ssStatus.dwWin32ExitCode = dwWin32ExitCode;
+ ssStatus.dwWaitHint = dwWaitHint;
+
+ if ( ( dwCurrentState == SERVICE_RUNNING ) ||
+ ( dwCurrentState == SERVICE_STOPPED ) )
+ ssStatus.dwCheckPoint = 0;
+ else
+ ssStatus.dwCheckPoint = dwCheckPoint++;
+
+
+ // Report the status of the service to the service control manager.
+ //
+ if (!(fResult = SetServiceStatus( sshStatusHandle, &ssStatus))) {
+ MessageLogWrite(TEXT("SetServiceStatus"));
+ }
+ }
+ return fResult;
+} // ReportStatusToSCMgr
+
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+MessageLogWrite(
+ LPTSTR lpszMsg
+ )
+/*++
+
+Routine Description:
+
+ Logs an error message.
+
+Arguments:
+
+ lpszMsg - text for message
+
+Return Values:
+
+ None.
+
+--*/
+{
+ TCHAR szMsg[256];
+ HANDLE hEventSource;
+ LPTSTR lpszStrings[2];
+
+
+ if ( !bDebug ) {
+ dwErr = GetLastError();
+
+ // Use event logging to log the error.
+ //
+ hEventSource = RegisterEventSource(NULL, TEXT(SZSERVICENAME));
+
+ _stprintf(szMsg, TEXT("%s error: %d"), TEXT(SZSERVICENAME), dwErr);
+ lpszStrings[0] = szMsg;
+ lpszStrings[1] = lpszMsg;
+
+ if (hEventSource != NULL) {
+ ReportEvent(hEventSource, // handle of event source
+ EVENTLOG_ERROR_TYPE, // event type
+ 0, // event category
+ 0, // event ID
+ NULL, // current user's SID
+ 2, // strings in lpszStrings
+ 0, // no bytes of raw data
+ lpszStrings, // array of error strings
+ NULL); // no raw data
+
+ (VOID) DeregisterEventSource(hEventSource);
+ }
+ }
+} // MessageLogWrite
+
+
+
+
+/////////////////////////////////////////////////////////////////////////
+//
+// The following code handles service installation and removal
+//
+/////////////////////////////////////////////////////////////////////////
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+CmdInstallService()
+/*++
+
+Routine Description:
+
+ Installs the service.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ None.
+
+--*/
+{
+ SC_HANDLE schService;
+ SC_HANDLE schSCManager;
+
+ TCHAR szPath[512];
+
+ if ( GetModuleFileName( NULL, szPath, 512 ) == 0 ) {
+ _tprintf(TEXT("Unable to install %s - %s\n"), TEXT(SZSERVICEDISPLAYNAME), GetLastErrorText(szErr, 256));
+ return;
+ }
+
+ schSCManager = OpenSCManager(
+ NULL, // machine (NULL == local)
+ NULL, // database (NULL == default)
+ SC_MANAGER_ALL_ACCESS // access required
+ );
+ if ( schSCManager ) {
+ schService = CreateService(
+ schSCManager, // SCManager database
+ TEXT(SZSERVICENAME), // name of service
+ TEXT(SZSERVICEDISPLAYNAME), // name to display
+ SERVICE_ALL_ACCESS, // desired access
+ SERVICE_WIN32_OWN_PROCESS, // service type
+ SERVICE_DEMAND_START, // start type
+ SERVICE_ERROR_NORMAL, // error control type
+ szPath, // service's binary
+ NULL, // no load ordering group
+ NULL, // no tag identifier
+ TEXT(SZDEPENDENCIES), // dependencies
+ NULL, // LocalSystem account
+ NULL); // no password
+
+ if ( schService ) {
+ _tprintf(TEXT("%s installed.\n"), TEXT(SZSERVICEDISPLAYNAME) );
+ CloseServiceHandle(schService);
+ } else {
+ _tprintf(TEXT("CreateService failed - %s\n"), GetLastErrorText(szErr, 256));
+ }
+
+ CloseServiceHandle(schSCManager);
+ } else
+ _tprintf(TEXT("OpenSCManager failed - %s\n"), GetLastErrorText(szErr,256));
+} // CmdInstallService
+
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+CmdRemoveService()
+/*++
+
+Routine Description:
+
+ Stops and removes the service.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ None.
+
+--*/
+{
+ SC_HANDLE schService;
+ SC_HANDLE schSCManager;
+
+ schSCManager = OpenSCManager(
+ NULL, // machine (NULL == local)
+ NULL, // database (NULL == default)
+ SC_MANAGER_ALL_ACCESS // access required
+ );
+ if ( schSCManager ) {
+ schService = OpenService(schSCManager, TEXT(SZSERVICENAME), SERVICE_ALL_ACCESS);
+
+ if (schService) {
+ // try to stop the service
+ if ( ControlService( schService, SERVICE_CONTROL_STOP, &ssStatus ) ) {
+ _tprintf(TEXT("Stopping %s."), TEXT(SZSERVICEDISPLAYNAME));
+ Sleep( 1000 );
+
+ while( QueryServiceStatus( schService, &ssStatus ) ) {
+ if ( ssStatus.dwCurrentState == SERVICE_STOP_PENDING ) {
+ _tprintf(TEXT("."));
+ Sleep( 1000 );
+ } else
+ break;
+ }
+
+ if ( ssStatus.dwCurrentState == SERVICE_STOPPED )
+ _tprintf(TEXT("\n%s stopped.\n"), TEXT(SZSERVICEDISPLAYNAME) );
+ else
+ _tprintf(TEXT("\n%s failed to stop.\n"), TEXT(SZSERVICEDISPLAYNAME) );
+
+ }
+
+ // now remove the service
+ if( DeleteService(schService) )
+ _tprintf(TEXT("%s removed.\n"), TEXT(SZSERVICEDISPLAYNAME) );
+ else
+ _tprintf(TEXT("DeleteService failed - %s\n"), GetLastErrorText(szErr,256));
+
+
+ CloseServiceHandle(schService);
+ } else
+ _tprintf(TEXT("OpenService failed - %s\n"), GetLastErrorText(szErr,256));
+
+ CloseServiceHandle(schSCManager);
+ } else
+ _tprintf(TEXT("OpenSCManager failed - %s\n"), GetLastErrorText(szErr,256));
+
+} // CmdRemoveService
+
+
+
+
+/////////////////////////////////////////////////////////////////////////
+//
+// Routines for running the service as a console app
+//
+/////////////////////////////////////////////////////////////////////////
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID CmdDebugService(
+ int argc,
+ char ** argv
+ )
+/*++
+
+Routine Description:
+
+ Runs the service as a console application
+
+Arguments:
+
+ argc - number of command line arguments
+ argv - array of command line arguments
+
+Return Values:
+
+ None.
+
+--*/
+{
+ DWORD dwArgc;
+ LPTSTR *lpszArgv;
+
+#ifdef UNICODE
+ lpszArgv = CommandLineToArgvW(GetCommandLineW(), &(dwArgc) );
+#else
+ dwArgc = (DWORD) argc;
+ lpszArgv = argv;
+#endif
+
+ _tprintf(TEXT("Debugging %s.\n"), TEXT(SZSERVICEDISPLAYNAME));
+
+ SetConsoleCtrlHandler( ControlHandler, TRUE );
+
+ ServiceStart( dwArgc, lpszArgv );
+} // CmdDebugService
+
+
+/////////////////////////////////////////////////////////////////////////
+BOOL WINAPI
+ControlHandler (
+ DWORD dwCtrlType
+ )
+/*++
+
+Routine Description:
+
+ Handle console control events.
+
+Arguments:
+
+ dwCtrlType - type of control event
+ lpszMsg - text for message
+
+Return Values:
+
+ True - handled
+ False - unhandled
+
+--*/
+{
+
+ switch( dwCtrlType ) {
+ case CTRL_BREAK_EVENT: // use Ctrl+C or Ctrl+Break to simulate
+ case CTRL_C_EVENT: // SERVICE_CONTROL_STOP in debug mode
+ _tprintf(TEXT("Stopping %s.\n"), TEXT(SZSERVICEDISPLAYNAME));
+ ServiceStop();
+ return TRUE;
+ break;
+
+ }
+
+ return FALSE;
+
+} // ControlHandler
+
+
+/////////////////////////////////////////////////////////////////////////
+LPTSTR
+GetLastErrorText(
+ LPTSTR lpszBuf,
+ DWORD dwSize
+ )
+/*++
+
+Routine Description:
+
+ Copies last error message text to string.
+
+Arguments:
+
+ lpszBuf - destination buffer
+ dwSize - size of buffer
+
+Return Values:
+
+ destination buffer
+
+--*/
+{
+ DWORD dwRet;
+ LPTSTR lpszTemp = NULL;
+
+ dwRet = FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |FORMAT_MESSAGE_ARGUMENT_ARRAY,
+ NULL,
+ GetLastError(),
+ LANG_NEUTRAL,
+ (LPTSTR)&lpszTemp,
+ 0,
+ NULL );
+
+ // supplied buffer is not long enough
+ if ( !dwRet || ( (long)dwSize < (long)dwRet+14 ) )
+ lpszBuf[0] = TEXT('\0');
+ else {
+ lpszTemp[lstrlen(lpszTemp)-2] = TEXT('\0'); //remove cr and newline character
+ _stprintf( lpszBuf, TEXT("%s (0x%x)"), lpszTemp, GetLastError() );
+ }
+
+ if ( lpszTemp )
+ LocalFree((HLOCAL) lpszTemp );
+
+ return lpszBuf;
+} // GetLastErrorText
diff --git a/private/net/svcdlls/lls/server/service.h b/private/net/svcdlls/lls/server/service.h
new file mode 100644
index 000000000..c7a92147a
--- /dev/null
+++ b/private/net/svcdlls/lls/server/service.h
@@ -0,0 +1,59 @@
+/*++
+
+Copyright (c) 1994 Microsoft Corporation
+
+Module Name:
+
+ Service.h
+
+Abstract:
+
+
+Author:
+
+ Arthur Hanson (arth) Dec 07, 1994
+
+Environment:
+
+Revision History:
+
+--*/
+
+#ifndef _SERVICE_H
+#define _SERVICE_H
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+// name of the executable
+#define SZAPPNAME "LLSrv"
+
+// internal name of the service
+#define SZSERVICENAME "LicenseLoggingService"
+
+// displayed name of the service
+#define SZSERVICEDISPLAYNAME "License Logging Service"
+
+// list of service dependencies - "dep1\0dep2\0\0"
+#define SZDEPENDENCIES ""
+//////////////////////////////////////////////////////////////////////////////
+
+
+
+VOID ServiceStart(DWORD dwArgc, LPTSTR *lpszArgv);
+VOID ServiceStop();
+
+
+
+BOOL ReportStatusToSCMgr(DWORD dwCurrentState, DWORD dwWin32ExitCode, DWORD dwWaitHint);
+VOID MessageLogWrite(LPTSTR lpszMsg);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/private/net/svcdlls/lls/server/sources b/private/net/svcdlls/lls/server/sources
new file mode 100644
index 000000000..f7128e073
--- /dev/null
+++ b/private/net/svcdlls/lls/server/sources
@@ -0,0 +1,84 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+
+Author:
+
+ Steve Wood (stevewo) 12-Apr-1990
+
+NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl
+
+!ENDIF
+
+MAJORCOMP=lls
+MINORCOMP=server
+
+TARGETNAME=llssrv
+TARGETPATH=obj
+TARGETTYPE=LIBRARY
+
+INCLUDES=$(BASEDIR)\public\sdk\inc;$(BASEDIR)\private\inc;$(SOURCES_PATH)..\inc
+
+SOURCES= \
+ $(SOURCES_PATH)certdb.c \
+ $(SOURCES_PATH)llsutil.c \
+ $(SOURCES_PATH)service.c \
+ $(SOURCES_PATH)msvctbl.c \
+ $(SOURCES_PATH)registry.c \
+ $(SOURCES_PATH)mapping.c \
+ $(SOURCES_PATH)purchase.c \
+ $(SOURCES_PATH)perseat.c \
+ $(SOURCES_PATH)server.c \
+ $(SOURCES_PATH)repl.c \
+ $(SOURCES_PATH)rpc.c \
+ $(SOURCES_PATH)pack.c \
+ $(SOURCES_PATH)scaven.c \
+ $(SOURCES_PATH)llsrpc_s.c \
+ $(SOURCES_PATH)lsapi_s.c \
+ $(SOURCES_PATH)llsdbg_s.c \
+ $(SOURCES_PATH)svctbl.c \
+ $(SOURCES_PATH)llssrv.c \
+ $(SOURCES_PATH)llssrv.rc
+
+UMTYPE=windows
+UMAPPL=llssrv
+UMRES=$(@R).res
+NTTARGETFILE1=obj\*\llssrv.res
+UMLIBS= \
+ $(SOURCES_PATH)..\common\obj\*\llscomm.lib \
+ obj\*\llssrv.lib \
+ $(BASEDIR)\public\sdk\lib\*\rpcutil.lib \
+ $(BASEDIR)\public\sdk\lib\*\rpcrt4.lib \
+ $(BASEDIR)\public\sdk\lib\*\samlib.lib \
+ $(BASEDIR)\public\sdk\lib\*\samsrv.lib \
+ $(BASEDIR)\public\sdk\lib\*\ntdll.lib \
+ $(BASEDIR)\public\sdk\lib\*\shell32.lib \
+ $(BASEDIR)\public\sdk\lib\*\netapi32.lib
+
+
+TARGETLIBS= \
+ $(SOURCES_PATH)..\common\obj\*\llscomm.lib \
+ $(BASEDIR)\public\sdk\lib\*\rpcutil.lib \
+ $(BASEDIR)\public\sdk\lib\*\rpcrt4.lib \
+ $(BASEDIR)\public\sdk\lib\*\rpcndr.lib \
+ $(BASEDIR)\public\sdk\lib\*\kernel32.lib \
+ $(BASEDIR)\public\sdk\lib\*\advapi32.lib \
+ $(BASEDIR)\public\sdk\lib\*\samsrv.lib \
+ $(BASEDIR)\public\sdk\lib\*\netapi32.lib \
+ $(BASEDIR)\public\sdk\lib\*\nlrepl.lib
+
+C_DEFINES=$(C_DEFINES) -DRPC_NO_WINDOWS_H -DUNICODE -D_UNICODE
+
+USE_CRTDLL=1
diff --git a/private/net/svcdlls/lls/server/svctbl.c b/private/net/svcdlls/lls/server/svctbl.c
new file mode 100644
index 000000000..a4e8e4307
--- /dev/null
+++ b/private/net/svcdlls/lls/server/svctbl.c
@@ -0,0 +1,902 @@
+/*++
+
+Copyright (c) 1994 Microsoft Corporation
+
+Module Name:
+
+ svctbl.c
+
+Abstract:
+
+ Service Table routines. Handles all access to service table
+ for keeping track of running services and session counts to those
+ services.
+
+
+Author:
+
+ Arthur Hanson (arth) 07-Dec-1994
+
+Revision History:
+
+ Jeff Parham (jeffparh) 05-Dec-1995
+ o Integrated per seat and per server purchase models for secure
+ certificates.
+ o Added logging of per server license rejections.
+
+--*/
+
+#include <stdlib.h>
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windows.h>
+#include <lm.h>
+
+#include "llsapi.h"
+#include "debug.h"
+#include "llssrv.h"
+#include "registry.h"
+#include "ntlsapi.h"
+#include "mapping.h"
+#include "msvctbl.h"
+#include "svctbl.h"
+#include "perseat.h"
+#include "llsevent.h"
+#include "llsutil.h"
+#include "purchase.h"
+
+
+//
+// Must have ending space for version number placeholder!
+//
+#define FILE_PRINT "FilePrint "
+#define FILE_PRINT_BASE "FilePrint"
+#define FILE_PRINT_VERSION_NDX ( 9 )
+
+#define REMOTE_ACCESS "REMOTE_ACCESS "
+#define REMOTE_ACCESS_BASE "REMOTE_ACCESS"
+
+extern ULONG NumFilePrintEntries;
+extern LPTSTR *FilePrintTable;
+
+
+ULONG ServiceListSize = 0;
+PSERVICE_RECORD *ServiceList = NULL;
+PSERVICE_RECORD *ServiceFreeList = NULL;
+
+
+RTL_RESOURCE ServiceListLock;
+
+int __cdecl MServiceRecordCompare(const void *arg1, const void *arg2);
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+ServiceListInit()
+
+/*++
+
+Routine Description:
+
+ Creates the service table, used for tracking the services and session
+ count. This will pull the initial services from the registry.
+
+ The table is linear so a binary search can be used on the table, so
+ some extra records are initialized so that each time we add a new
+ service we don't have to do a realloc. We also assume that adding
+ new services is a relatively rare occurance, since we need to sort
+ it each time.
+
+ The service table is guarded by a read and write semaphore. Multiple
+ reads can occur, but a write blocks everything.
+
+ The service table has two default entries for FilePrint and REMOTE_ACCESS.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ BOOL PerSeatLicensing;
+ ULONG SessionLimit;
+ PSERVICE_RECORD Service;
+
+ RtlInitializeResource(&ServiceListLock);
+
+ //
+ // Just need to init FilePrint values...
+ //
+ Service = ServiceListAdd(TEXT(FILE_PRINT), FILE_PRINT_VERSION_NDX );
+ RegistryInitValues(TEXT(FILE_PRINT_BASE), &PerSeatLicensing, &SessionLimit);
+
+ //
+ // Need to init RAS separatly as it uses File/Print Licenses.
+ //
+ Service = ServiceListAdd(TEXT(REMOTE_ACCESS), lstrlen(TEXT(REMOTE_ACCESS)) - 1);
+ if (Service != NULL) {
+ Service->MaxSessionCount = SessionLimit;
+ Service->PerSeatLicensing = PerSeatLicensing;
+ }
+
+} // ServiceListInit
+
+
+/////////////////////////////////////////////////////////////////////////
+int __cdecl ServiceListCompare(const void *arg1, const void *arg2) {
+ PSERVICE_RECORD Svc1, Svc2;
+
+ Svc1 = (PSERVICE_RECORD) *((PSERVICE_RECORD *) arg1);
+ Svc2 = (PSERVICE_RECORD) *((PSERVICE_RECORD *) arg2);
+
+ return lstrcmpi( Svc1->Name, Svc2->Name);
+
+} // ServiceListCompare
+
+
+PSERVICE_RECORD
+ServiceListFind(
+ LPTSTR ServiceName
+ )
+
+/*++
+
+Routine Description:
+
+ Internal routine to actually do binary search on ServiceList, this
+ does not do any locking as we expect the wrapper routine to do this.
+ The search is a simple binary search.
+
+Arguments:
+
+ ServiceName -
+
+Return Value:
+
+ Pointer to found service table entry or NULL if not found.
+
+--*/
+
+{
+ LONG begin = 0;
+ LONG end = (LONG) ServiceListSize - 1;
+ LONG cur;
+ int match;
+ PSERVICE_RECORD Service;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: ServiceListFind\n"));
+#endif
+ if ((ServiceName == NULL) || (ServiceListSize == 0))
+ return NULL;
+
+ while (end >= begin) {
+ // go halfway in-between
+ cur = (begin + end) / 2;
+ Service = ServiceList[cur];
+
+ // compare the two result into match
+ match = lstrcmpi(ServiceName, Service->Name);
+
+ if (match < 0)
+ // move new begin
+ end = cur - 1;
+ else
+ begin = cur + 1;
+
+ if (match == 0)
+ return Service;
+ }
+
+ return NULL;
+
+} // ServiceListFind
+
+
+/////////////////////////////////////////////////////////////////////////
+DWORD
+VersionToDWORD(LPTSTR Version)
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ LPSTR pVer;
+ DWORD Ver = 0;
+ char tmpStr[10];
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: VersionToDWORD\n"));
+#endif
+
+ if ((Version == NULL) || (*Version == TEXT('\0')))
+ return Ver;
+
+ //
+ // Do major version number
+ //
+ tmpStr[0] = '\0';
+ WideCharToMultiByte(CP_ACP, 0, Version, -1, tmpStr, 10, NULL, NULL);
+ Ver = (ULONG) atoi(tmpStr);
+ Ver *= 0x10000;
+
+ //
+ // Now minor - look for period
+ //
+ pVer = tmpStr;
+ while ((*pVer != '\0') && (*pVer != '.'))
+ pVer++;
+
+ if (*pVer == '.') {
+ pVer++;
+ Ver += atoi(pVer);
+ }
+
+ return Ver;
+
+} // VersionToDWORD
+
+
+/////////////////////////////////////////////////////////////////////////
+PSERVICE_RECORD
+ServiceListAdd(
+ LPTSTR ServiceName,
+ ULONG VersionIndex
+ )
+
+/*++
+
+Routine Description:
+
+ Adds a service to the service table. This will also cause a poll of
+ the registry to get the initial values for session limits and the
+ type of licensing being used.
+
+Arguments:
+
+ ServiceName -
+
+Return Value:
+
+ Pointer to added service table entry, or NULL if failed.
+
+--*/
+
+{
+ ULONG i;
+ ULONG SessionLimit = 0;
+ BOOL PerSeatLicensing = FALSE;
+ PSERVICE_RECORD NewService;
+ LPTSTR NewServiceName, pDisplayName, pFamilyDisplayName;
+ PSERVICE_RECORD CurrentRecord = NULL;
+ PMASTER_SERVICE_RECORD mService;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: ServiceListAdd\n"));
+#endif
+ //
+ // We do a double check here to see if another thread just got done
+ // adding the service, between when we checked last and actually got
+ // the write lock.
+ //
+ CurrentRecord = ServiceListFind(ServiceName);
+ if (CurrentRecord != NULL) {
+ return CurrentRecord;
+ }
+
+ //
+ // Allocate space for table (zero init it).
+ //
+ if (ServiceList == NULL) {
+ ServiceList = (PSERVICE_RECORD *) LocalAlloc(LPTR, sizeof(PSERVICE_RECORD) );
+ ServiceFreeList = (PSERVICE_RECORD *) LocalAlloc(LPTR, sizeof(PSERVICE_RECORD) );
+ } else {
+ ServiceList = (PSERVICE_RECORD *) LocalReAlloc(ServiceList, sizeof(PSERVICE_RECORD) * (ServiceListSize + 1), LHND);
+ ServiceFreeList = (PSERVICE_RECORD *) LocalReAlloc(ServiceFreeList, sizeof(PSERVICE_RECORD) * (ServiceListSize + 1), LHND);
+ }
+
+ //
+ // Make sure we could allocate service table
+ //
+ if ((ServiceList == NULL) || (ServiceFreeList == NULL)) {
+ ASSERT(FALSE);
+ ServiceFreeList = NULL;
+ ServiceList = NULL;
+ ServiceListSize = 0;
+ return NULL;
+ }
+
+ //
+ // Allocate space for saving off Service Name - we will take a space, then
+ // the version string onto the end of the Product Name. Therefore the
+ // product name will be something like "Microsoft SQL 4.2a". We maintain
+ // a pointer to the version, so that we can convert the space to a NULL
+ // and then get the product and version string separatly. Keeping them
+ // together simplifies the qsort and binary search routines.
+ //
+ NewService = (PSERVICE_RECORD) LocalAlloc(LPTR, sizeof(SERVICE_RECORD));
+ if (NewService == NULL) {
+ ASSERT(FALSE);
+ return NULL;
+ }
+
+ ServiceList[ServiceListSize] = NewService;
+ ServiceFreeList[ServiceListSize] = NewService;
+
+ NewServiceName = (LPTSTR) LocalAlloc(LPTR, (lstrlen(ServiceName) + 1) * sizeof(TCHAR));
+ if (NewServiceName == NULL) {
+ ASSERT(FALSE);
+ LocalFree(NewService);
+ return NULL;
+ }
+
+ // now copy it over...
+ NewService->Name = NewServiceName;
+ lstrcpy(NewService->Name, ServiceName);
+
+ //
+ // Allocate space for Root Name
+ //
+ NewService->Name[VersionIndex] = TEXT('\0');
+ NewServiceName = (LPTSTR) LocalAlloc(LPTR, (lstrlen(NewService->Name) + 1) * sizeof(TCHAR));
+
+ if (NewServiceName == NULL) {
+ ASSERT(FALSE);
+ LocalFree(NewService->Name);
+ LocalFree(NewService);
+ return NULL;
+ }
+
+ lstrcpy(NewServiceName, NewService->Name);
+ NewService->Name[VersionIndex] = TEXT(' ');
+
+ // point service structure to it...
+ NewService->FamilyName = NewServiceName;
+
+ //
+ // Allocate space for Display Name
+ //
+ RegistryDisplayNameGet(NewService->FamilyName, NewService->Name, &pDisplayName);
+
+ if (pDisplayName == NULL) {
+ ASSERT(FALSE);
+ LocalFree(NewService->Name);
+ LocalFree(NewService->FamilyName);
+ LocalFree(NewService);
+ return NULL;
+ }
+
+ // point service structure to it...
+ NewService->DisplayName = pDisplayName;
+
+ RegistryFamilyDisplayNameGet(NewService->FamilyName, NewService->DisplayName, &pFamilyDisplayName);
+
+ if (pFamilyDisplayName == NULL) {
+ ASSERT(FALSE);
+ LocalFree(NewService->Name);
+ LocalFree(NewService->FamilyName);
+ LocalFree(NewService->DisplayName);
+ LocalFree(NewService);
+ return NULL;
+ }
+
+ // point service structure to it...
+ NewService->FamilyDisplayName = pFamilyDisplayName;
+
+ //
+ // Update table size and init entry, including reading init values
+ // from registry.
+ //
+ NewService->Version = VersionToDWORD(&ServiceName[VersionIndex + 1]);
+
+ // Init values from registry...
+ RegistryInitService(NewService->FamilyName, &PerSeatLicensing, &SessionLimit);
+
+ if ( PerSeatLicensing )
+ {
+ // per seat mode
+ NewService->MaxSessionCount = 0;
+ }
+ else if ( ServiceIsSecure( NewService->DisplayName ) )
+ {
+ // per server mode with a secure product; requires certificate
+ NewService->MaxSessionCount = ProductLicensesGet( NewService->DisplayName, TRUE );
+ }
+ else
+ {
+ // per server mode with an unsecure product; use limit from registry
+ NewService->MaxSessionCount = SessionLimit;
+ }
+
+ NewService->PerSeatLicensing = PerSeatLicensing;
+ NewService->SessionCount = 0;
+ NewService->Index = ServiceListSize;
+ RtlInitializeCriticalSection(&NewService->ServiceLock);
+
+ if (lstrcmpi(ServiceName, TEXT(REMOTE_ACCESS))) {
+ RtlAcquireResourceExclusive(&MasterServiceListLock, TRUE);
+ mService = MasterServiceListAdd( NewService->FamilyDisplayName, NewService->DisplayName, NewService->Version);
+ RtlReleaseResource(&MasterServiceListLock);
+
+ if (mService == NULL) {
+ ASSERT(FALSE);
+ } else {
+ NewService->MasterService = mService;
+
+ //
+ // In case this got added from the local service list table and we
+ // didn't have a version # yet.
+ //
+ if (mService->Version == 0) {
+ PMASTER_SERVICE_ROOT ServiceRoot = NULL;
+
+ //
+ // Fixup next pointer chain
+ //
+ ServiceRoot = mService->Family;
+ i = 0;
+ while ((i < ServiceRoot->ServiceTableSize) && (MasterServiceTable[ServiceRoot->Services[i]]->Version < NewService->Version))
+ i++;
+
+ mService->next = 0;
+ mService->Version = NewService->Version;
+ if (i > 0) {
+ if (MasterServiceTable[ServiceRoot->Services[i - 1]]->next == mService->Index + 1)
+ mService->next = 0;
+ else
+ mService->next = MasterServiceTable[ServiceRoot->Services[i - 1]]->next;
+
+ if (MasterServiceTable[ServiceRoot->Services[i - 1]] != mService)
+ MasterServiceTable[ServiceRoot->Services[i - 1]]->next = mService->Index + 1;
+ }
+
+ // Resort it in order of the versions
+ qsort((void *) ServiceRoot->Services, (size_t) ServiceRoot->ServiceTableSize, sizeof(PMASTER_SERVICE_RECORD), MServiceRecordCompare);
+ }
+ }
+
+ }
+
+ ServiceListSize++;
+
+ // Have added the entry - now need to sort it in order of the service names
+ qsort((void *) ServiceList, (size_t) ServiceListSize, sizeof(PSERVICE_RECORD), ServiceListCompare);
+
+ return NewService;
+} // ServiceListAdd
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+ServiceListResynch( )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ PSERVICE_RECORD Service;
+ BOOL PerSeatLicensing;
+ ULONG SessionLimit;
+ ULONG i = 0;
+ PSERVICE_RECORD FilePrintService;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: ServiceListReSynch\n"));
+#endif
+ if (ServiceList == NULL)
+ return;
+
+ //
+ // Need to update list so get exclusive access.
+ //
+ RtlAcquireResourceExclusive(&ServiceListLock, TRUE);
+
+ for (i = 0; i < ServiceListSize; i++) {
+ //
+ // Note: We will init REMOTE_ACCESS with bogus values here, but we
+ // reset it to the correct values below. Since we have exclusive access
+ // to the table, this is fine (and faster than always checking for
+ // REMOTE_ACCESS).
+ //
+ RegistryInitService((ServiceList[i])->FamilyName, &PerSeatLicensing, &SessionLimit);
+
+ if ( PerSeatLicensing )
+ {
+ // per seat mode
+ (ServiceList[i])->MaxSessionCount = 0;
+ }
+ else if ( ServiceIsSecure( (ServiceList[i])->DisplayName ) )
+ {
+ // per server mode with a secure product; requires certificate
+ (ServiceList[i])->MaxSessionCount = ProductLicensesGet( (ServiceList[i])->DisplayName, TRUE );
+ }
+ else
+ {
+ // per server mode with an unsecure product; use limit from registry
+ (ServiceList[i])->MaxSessionCount = SessionLimit;
+ }
+
+ (ServiceList[i])->PerSeatLicensing = PerSeatLicensing;
+ }
+
+ //
+ // Need to init RAS separatly as it uses File/Print Licenses.
+ //
+ Service = ServiceListFind(TEXT(REMOTE_ACCESS));
+ FilePrintService = ServiceListFind(TEXT(FILE_PRINT));
+
+ ASSERT( NULL != Service );
+ ASSERT( NULL != FilePrintService );
+
+ if ( ( NULL != Service ) && ( NULL != FilePrintService ) )
+ {
+ Service->MaxSessionCount = FilePrintService->MaxSessionCount;
+ Service->PerSeatLicensing = FilePrintService->PerSeatLicensing;
+ }
+
+ RtlReleaseResource(&ServiceListLock);
+
+ return;
+} // ServiceListResynch
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+DispatchRequestLicense(
+ ULONG DataType,
+ PVOID Data,
+ LPTSTR ServiceID,
+ ULONG VersionIndex,
+ BOOL IsAdmin,
+ ULONG *Handle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+ ServiceID -
+
+ IsAdmin -
+
+ Handle -
+
+Return Value:
+
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ PSERVICE_RECORD Service;
+ ULONG SessionCount;
+ ULONG TableEntry;
+ LPTSTR pServiceID;
+ BOOL NoLicense = FALSE;
+ BOOL PerSeat;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: DispatchRequestLicense\n"));
+#endif
+
+ *Handle = 0xFFFFFFFF;
+ pServiceID = ServiceID;
+
+ // we only need read access since we aren't adding at this point
+ RtlAcquireResourceShared( &ServiceListLock, TRUE );
+
+ // check if in FilePrint table, if so then we use FilePrint as the name
+ ServiceID[ VersionIndex ] = TEXT('\0');
+ if ( ServiceFindInTable( ServiceID, FilePrintTable, NumFilePrintEntries, &TableEntry ) )
+ {
+ pServiceID = TEXT(FILE_PRINT);
+ }
+ ServiceID[ VersionIndex ] = TEXT(' ');
+
+ Service = ServiceListFind( pServiceID );
+
+ if (Service == NULL)
+ {
+ // couldn't find service in list, so add it
+ RtlConvertSharedToExclusive(&ServiceListLock);
+ Service = ServiceListAdd( pServiceID, VersionIndex );
+ RtlConvertExclusiveToShared(&ServiceListLock);
+ }
+
+ if (Service != NULL)
+ {
+ // service found or added successfully
+
+ *Handle = (ULONG) Service->Index;
+
+ RtlEnterCriticalSection(&Service->ServiceLock);
+ SessionCount = Service->SessionCount + 1;
+
+#if DBG
+ if (TraceFlags & TRACE_LICENSE_REQUEST)
+ dprintf(TEXT("LLS: [0x%lX] %s License: %ld of %ld\n"), Service, Service->Name, SessionCount, Service->MaxSessionCount);
+#endif
+
+ if (SessionCount > Service->HighMark)
+ {
+ Service->HighMark = SessionCount;
+ }
+
+ PerSeat = Service->PerSeatLicensing;
+
+ if ( (SessionCount > Service->MaxSessionCount) && !IsAdmin )
+ {
+ if ( PerSeat )
+ {
+ // for node licenses, allow limit to be exceeded
+ // NOTE: The above comparison doesn't even make sense for per seat
+ // licenses, since MaxSessionCount tracks only per server
+ // licenses, never per seat licenses! -JBP
+ Service->SessionCount++;
+ }
+ else
+ {
+ // zero tolerance for exceeding limits on concurrent licenses
+
+ TCHAR szFullUserName[ MAX_DOMAINNAME_LENGTH + MAX_USERNAME_LENGTH + 3 ] = TEXT("");
+ LPWSTR apszSubString[ 2 ];
+ DWORD dwError = ERROR_SUCCESS;
+
+ if ( NT_LS_USER_NAME == DataType )
+ {
+ apszSubString[ 0 ] = (LPWSTR) Data;
+ }
+ else
+ {
+ TCHAR szUserName[ MAX_USERNAME_LENGTH + 1 ];
+ TCHAR szDomainName[ MAX_DOMAINNAME_LENGTH + 1 ];
+ DWORD cbUserName;
+ DWORD cbDomainName;
+ SID_NAME_USE snu;
+
+ cbUserName = sizeof( szUserName );
+ cbDomainName = sizeof( szDomainName );
+
+ if ( LookupAccountSid( NULL, (PSID) Data, szUserName, &cbUserName, szDomainName, &cbDomainName, &snu ) )
+ {
+ lstrcpy( szFullUserName, szDomainName );
+ lstrcat( szFullUserName, TEXT( "\\" ) );
+ lstrcat( szFullUserName, szUserName );
+ }
+ else
+ {
+ dwError = GetLastError();
+ }
+
+ apszSubString[ 0 ] = szFullUserName;
+ }
+
+ apszSubString[ 1 ] = ServiceID;
+
+ LogEvent( LLS_EVENT_USER_NO_LICENSE, 2, apszSubString, dwError );
+
+ NoLicense = TRUE;
+ }
+ }
+ else
+ {
+ // within valid license limits or user is an admin
+ Service->SessionCount++;
+ }
+
+ RtlLeaveCriticalSection(&Service->ServiceLock);
+ RtlReleaseResource(&ServiceListLock);
+
+ if ( PerSeat )
+ {
+ // per node ("per seat") license
+
+ // translate REMOTE_ACCESS into FILE_PRINT before adding to
+ // per seat license records
+ if ( !lstrcmpi( ServiceID, TEXT( REMOTE_ACCESS ) ) )
+ {
+ RtlAcquireResourceShared(&ServiceListLock, TRUE);
+ Service = ServiceListFind(TEXT(FILE_PRINT));
+ RtlReleaseResource(&ServiceListLock);
+
+ ASSERT(Service != NULL);
+ }
+
+ UserListUpdate( DataType, Data, Service );
+ }
+ else
+ {
+ // concurrent use ("per server") license
+ if (NoLicense)
+ {
+ Status = LS_INSUFFICIENT_UNITS;
+ *Handle = 0xFFFFFFFF;
+ }
+ }
+ }
+ else
+ {
+ // could neither find nor create service entry
+
+ RtlReleaseResource(&ServiceListLock);
+#if DBG
+ dprintf( TEXT( "DispatchRequestLicense(): Could neither find nor create service entry.\n" ) );
+#endif
+ }
+
+ return Status;
+} // DispatchRequestLicense
+
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+DispatchFreeLicense(
+ ULONG Handle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+ Handle -
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PSERVICE_RECORD Service;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: DispatchFreeLicense\n"));
+#endif
+ //
+ // We only need read access since we aren't adding at this point.
+ //
+ RtlAcquireResourceShared(&ServiceListLock, TRUE);
+
+#if DBG
+ if (TraceFlags & TRACE_LICENSE_FREE)
+ dprintf(TEXT("Free Handle: 0x%lX\n"), Handle);
+#endif
+ if (Handle < ServiceListSize) {
+ Service = ServiceFreeList[Handle];
+ RtlEnterCriticalSection(&Service->ServiceLock);
+ if (Service->SessionCount > 0)
+ Service->SessionCount--;
+ RtlLeaveCriticalSection(&Service->ServiceLock);
+ } else {
+#if DBG
+ dprintf(TEXT("Passed invalid Free Handle: 0x%lX\n"), Handle);
+#endif
+ }
+
+ RtlReleaseResource(&ServiceListLock);
+
+} // DispatchFreeLicense
+
+
+#if DBG
+/////////////////////////////////////////////////////////////////////////
+VOID
+ServiceListDebugDump( )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ ULONG i = 0;
+
+ //
+ // Need to scan list so get read access.
+ //
+ RtlAcquireResourceShared(&ServiceListLock, TRUE);
+
+ dprintf(TEXT("Service Table, # Entries: %lu\n"), ServiceListSize);
+ if (ServiceList == NULL)
+ goto ServiceListDebugDumpExit;
+
+ for (i = 0; i < ServiceListSize; i++) {
+ if ((ServiceList[i])->PerSeatLicensing)
+ dprintf(TEXT("%3lu) PerSeat: Y MS: %4lu CS: %4lu HM: %4lu [%3lu] Svc: %s\n"),
+ i + 1, ServiceList[i]->MaxSessionCount, ServiceList[i]->SessionCount, ServiceList[i]->HighMark, ServiceList[i]->Index, ServiceList[i]->Name);
+ else
+ dprintf(TEXT("%3lu) PerSeat: N MS: %4lu CS: %4lu HM: %4lu [%3lu] Svc: %s\n"),
+ i + 1, ServiceList[i]->MaxSessionCount, ServiceList[i]->SessionCount, ServiceList[i]->HighMark, ServiceList[i]->Index, ServiceList[i]->Name);
+ }
+
+ServiceListDebugDumpExit:
+ RtlReleaseResource(&ServiceListLock);
+
+ return;
+} // ServiceListDebugDump
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+ServiceListDebugInfoDump( PVOID Data )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ PSERVICE_RECORD CurrentRecord = NULL;
+
+ dprintf(TEXT("Service Table, # Entries: %lu\n"), ServiceListSize);
+
+ if (lstrlen((LPWSTR) Data) > 0) {
+ CurrentRecord = ServiceListFind((LPWSTR) Data);
+ if (CurrentRecord != NULL) {
+ if (CurrentRecord->PerSeatLicensing)
+ dprintf(TEXT(" PerSeat: Y MS: %4lu CS: %4lu HM: %4lu [%3lu] Svc: %s\n"),
+ CurrentRecord->MaxSessionCount, CurrentRecord->SessionCount, CurrentRecord->HighMark, CurrentRecord->Index, CurrentRecord->Name);
+ else
+ dprintf(TEXT(" PerSeat: N MS: %4lu CS: %4lu HM: %4lu [%3lu] Svc: %s\n"),
+ CurrentRecord->MaxSessionCount, CurrentRecord->SessionCount, CurrentRecord->HighMark, CurrentRecord->Index, CurrentRecord->Name);
+ }
+ }
+
+} // ServiceListDebugInfoDump
+
+#endif
diff --git a/private/net/svcdlls/lls/server/svctbl.h b/private/net/svcdlls/lls/server/svctbl.h
new file mode 100644
index 000000000..e5bbb3759
--- /dev/null
+++ b/private/net/svcdlls/lls/server/svctbl.h
@@ -0,0 +1,74 @@
+/*++
+
+Copyright (c) 1994 Microsoft Corporation
+
+Module Name:
+
+ SvcTbl.h
+
+Abstract:
+
+
+Author:
+
+ Arthur Hanson (arth) Dec 07, 1994
+
+Environment:
+
+Revision History:
+
+--*/
+
+
+#ifndef _LLS_SVCTBL_H
+#define _LLS_SVCTBL_H
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct _SERVICE_RECORD {
+ DWORD Index;
+ LPTSTR Name;
+ LPTSTR DisplayName;
+ DWORD Version;
+ LPTSTR FamilyName;
+ LPTSTR FamilyDisplayName;
+
+ PMASTER_SERVICE_RECORD MasterService;
+
+ BOOL PerSeatLicensing;
+
+ RTL_CRITICAL_SECTION ServiceLock;
+ ULONG SessionCount; // # sessions current active
+ ULONG MaxSessionCount; // Max # simultaneous sessions
+ ULONG HighMark; // Max # simultaneous sessions ever attempted
+} SERVICE_RECORD, *PSERVICE_RECORD;
+
+
+extern ULONG ServiceListSize;
+extern PSERVICE_RECORD *ServiceList;
+extern PSERVICE_RECORD *ServiceFreeList;
+extern RTL_RESOURCE ServiceListLock;
+
+
+VOID ServiceListInit();
+PSERVICE_RECORD ServiceListAdd( LPTSTR ServiceName, ULONG VersionIndex );
+PSERVICE_RECORD ServiceListFind( LPTSTR ServiceName );
+VOID ServiceListResynch( );
+NTSTATUS DispatchRequestLicense( ULONG DataType, PVOID Data, LPTSTR ServiceID, ULONG VersionIndex, BOOL IsAdmin, ULONG *Handle );
+VOID DispatchFreeLicense( ULONG Handle );
+DWORD VersionToDWORD(LPTSTR Version);
+
+#if DBG
+VOID ServiceListDebugDump( );
+VOID ServiceListDebugInfoDump( );
+#endif
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/private/net/svcdlls/lls/test/common/llsdbg.c b/private/net/svcdlls/lls/test/common/llsdbg.c
new file mode 100644
index 000000000..8713a66e4
--- /dev/null
+++ b/private/net/svcdlls/lls/test/common/llsdbg.c
@@ -0,0 +1,441 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ llsdbg.c
+
+Abstract:
+
+ Client side debugging RPC wrappers for License Logging Service.
+
+Author:
+
+ Arthur Hanson (arth) 30-Jan-1995
+
+Revision History:
+
+--*/
+
+#include <nt.h>
+#include <ntlsa.h>
+#include <ntsam.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windows.h>
+#include <lm.h>
+
+#include "debug.h"
+#include "llsdbg_c.h"
+
+
+LPTSTR pszStringBinding = NULL;
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsDebugInit( )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ RPC_STATUS Status;
+ LPTSTR pszUuid = NULL;
+ LPTSTR pszProtocolSequence = NULL;
+ LPTSTR pszNetworkAddress = NULL;
+ LPTSTR pszEndpoint = NULL;
+ LPTSTR pszOptions = NULL;
+ TCHAR pComputer[MAX_COMPUTERNAME_LENGTH + 1];
+ ULONG Size;
+
+ pszProtocolSequence = TEXT("ncalrpc");
+ pszEndpoint = TEXT(LLS_LPC_ENDPOINT);
+ pszNetworkAddress = NULL;
+
+ // Compose a string binding
+ Status = RpcStringBindingComposeW(pszUuid,
+ pszProtocolSequence,
+ pszNetworkAddress,
+ pszEndpoint,
+ pszOptions,
+ &pszStringBinding);
+ if(Status) {
+#ifdef DEBUG
+ dprintf(TEXT("RpcStringBindingComposeW Failed: 0x%lX\n"), Status);
+#endif
+ return I_RpcMapWin32Status(Status);
+ }
+
+ // Bind using the created string binding...
+ Status = RpcBindingFromStringBindingW(pszStringBinding, &llsdbgrpc_handle);
+ if(Status) {
+#ifdef DEBUG
+ dprintf(TEXT("RpcBindingFromStringBindingW Failed: 0x%lX\n"), Status);
+#endif
+ return I_RpcMapWin32Status(Status);
+ }
+
+ return I_RpcMapWin32Status(Status);
+
+} // LlsDebugInit
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsClose( )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ RPC_STATUS Status;
+
+ Status = RpcStringFree(&pszStringBinding);
+ if (Status )
+ return(Status);
+
+ Status = RpcBindingFree(&llsdbgrpc_handle);
+ return Status;
+
+} // LlsClose
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsDbgTableDump(
+ DWORD Table
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+
+ try {
+ Status = LlsrDbgTableDump( Table );
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#ifdef DEBUG
+ dprintf(TEXT("ERROR LLSDBG RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ return Status;
+} // LlsDbgTableDump
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsDbgTableInfoDump(
+ DWORD Table,
+ LPTSTR Item
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+
+ try {
+ Status = LlsrDbgTableInfoDump( Table, Item );
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#ifdef DEBUG
+ dprintf(TEXT("ERROR LLSDBG RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ return Status;
+} // LlsDbgTableInfoDump
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsDbgTableFlush(
+ DWORD Table
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+
+ try {
+ Status = LlsrDbgTableFlush( Table );
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#ifdef DEBUG
+ dprintf(TEXT("ERROR LLSDBG RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ return Status;
+} // LlsDbgTableFlush
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsDbgTraceSet(
+ DWORD Flags
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+
+ try {
+ Status = LlsrDbgTraceSet( Flags );
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#ifdef DEBUG
+ dprintf(TEXT("ERROR LLSDBG RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ return Status;
+} // LlsDbgTraceSet
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsDbgConfigDump( )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+
+ try {
+ Status = LlsrDbgConfigDump();
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#ifdef DEBUG
+ dprintf(TEXT("ERROR LLSDBG RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ return Status;
+} // LlsDbgConfigDump
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsDbgReplicationForce( )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+
+ try {
+ Status = LlsrDbgReplicationForce();
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#ifdef DEBUG
+ dprintf(TEXT("ERROR LLSDBG RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ return Status;
+} // LlsDbgReplicationForce
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsDbgReplicationDeny( )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+
+ try {
+ Status = LlsrDbgReplicationDeny();
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#ifdef DEBUG
+ dprintf(TEXT("ERROR LLSDBG RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ return Status;
+} // LlsDbgReplicationDeny
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsDbgRegistryUpdateForce( )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+
+ try {
+ Status = LlsrDbgRegistryUpdateForce();
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#ifdef DEBUG
+ dprintf(TEXT("ERROR LLSDBG RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ return Status;
+} // LlsDbgRegistryUpdateForce
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsDbgDatabaseFlush( )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+
+ try {
+ Status = LlsrDbgDatabaseFlush();
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#ifdef DEBUG
+ dprintf(TEXT("ERROR LLSDBG RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ return Status;
+} // LlsDbgDatabaseFlush
diff --git a/private/net/svcdlls/lls/test/common/llsdbg.h b/private/net/svcdlls/lls/test/common/llsdbg.h
new file mode 100644
index 000000000..99083767d
--- /dev/null
+++ b/private/net/svcdlls/lls/test/common/llsdbg.h
@@ -0,0 +1,47 @@
+/*++
+
+Copyright (c) 1994 Microsoft Corporation
+
+Module Name:
+
+ LlsDbg.h
+
+Abstract:
+
+
+Author:
+
+ Arthur Hanson (arth) Dec 07, 1994
+
+Environment:
+
+Revision History:
+
+--*/
+
+#ifndef _LLS_LLSDBG_H
+#define _LLS_LLSDBG_H
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+NTSTATUS NTAPI LlsDebugInit( );
+NTSTATUS NTAPI LlsClose( );
+
+NTSTATUS LlsDbgTableDump( DWORD Table );
+NTSTATUS LlsDbgTableInfoDump( DWORD Table, LPTSTR Item );
+NTSTATUS LlsDbgTableFlush( DWORD Table );
+NTSTATUS LlsDbgTraceSet( DWORD Flags );
+NTSTATUS LlsDbgConfigDump( );
+NTSTATUS LlsDbgReplicationForce( );
+NTSTATUS LlsDbgReplicationDeny( );
+NTSTATUS LlsDbgRegistryUpdateForce( );
+NTSTATUS LlsDbgDatabaseFlush( );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/private/net/svcdlls/lls/test/common/makefile b/private/net/svcdlls/lls/test/common/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/net/svcdlls/lls/test/common/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/net/svcdlls/lls/test/common/sources b/private/net/svcdlls/lls/test/common/sources
new file mode 100644
index 000000000..8942b20a6
--- /dev/null
+++ b/private/net/svcdlls/lls/test/common/sources
@@ -0,0 +1,45 @@
+
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+
+Author:
+
+ Steve Wood (stevewo) 12-Apr-1990
+
+NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl
+
+!ENDIF
+
+MAJORCOMP=lls
+MINORCOMP=common
+
+TARGETNAME=llsdbg
+TARGETPATH=obj
+TARGETTYPE=LIBRARY
+
+INCLUDES=..\..\inc;$(_NTROOT)\public\sdk\inc;$(_NTROOT)\private\inc
+
+C_DEFINES=-DRPC_NO_WINDOWS_H -DUNICODE -D_UNICODE -DDEBUG
+
+USE_CRTDLL=1
+
+
+
+SOURCES= \
+ llsdbg_c.c \
+ llsdbg.c
+
+UMTYPE=windows
diff --git a/private/net/svcdlls/lls/test/ct/initdata.bat b/private/net/svcdlls/lls/test/ct/initdata.bat
new file mode 100644
index 000000000..f47b754fd
--- /dev/null
+++ b/private/net/svcdlls/lls/test/ct/initdata.bat
@@ -0,0 +1,70 @@
+@echo off
+REM
+SET domain=TestDomain
+REM
+llscmd rpc llslicenseadd "SQL 4.2" "Mr. Slate" 15 "New employees"
+llscmd rpc llslicenseadd "SQL 4.2" "Mr. Slate" 10 "Authoried by Mr. Slate"
+llscmd rpc llslicenseadd "SNA 3.0" "Fred Flinstone" 1 "Who let me in"
+llscmd rpc llslicenseadd "Exchange 1.0" "Mr. Slate" 30 "On the beta"
+llscmd rpc llslicenseadd "Microsoft BackOffice" "Admin" 3 "xxx"
+
+llscmd rpc llsGroupadd Lab2 2 "Computer Lab 2/2222"
+llscmd rpc llsGroupadd Lab1 4 "Computer Lab 1/1111"
+llscmd rpc llsGroupadd Lab3 5 "Computer Lab 3/3333"
+
+llscmd rpc llsGroupuseradd Lab1 %domain%\Fred
+
+llscmd rpc llsGroupuseradd Lab2 %domain%\Barney
+llscmd rpc llsGroupuseradd Lab2 %domain%\Wilma
+
+llscmd rpc llsGroupuseradd Lab3 %domain%\Pebbles
+llscmd rpc llsGroupuseradd Lab3 %domain%\BamBam
+llscmd rpc llsGroupuseradd Lab3 %domain%\Mr.Slate
+
+llscmd add user %domain%\\"Fred" SQL 4.2
+llscmd add user %domain%\\"Fred" SQL 4.2
+llscmd add user %domain%\\"Fred" SQL 4.2
+
+llscmd add user %domain%\\"Barney" SQL 4.2
+llscmd add user %domain%\\"Barney" SQL 4.2
+llscmd add user %domain%\\"Barney" SQL 4.2
+llscmd add user %domain%\\"Barney" SQL 4.2
+llscmd add user %domain%\\"Barney" SQL 4.2
+llscmd add user %domain%\\"Barney" SQL 4.2
+llscmd add user %domain%\\"Barney" SQL 4.2
+llscmd add user %domain%\\"Barney" SQL 4.2
+llscmd add user %domain%\\"Wilma" SQL 4.2
+llscmd add user %domain%\\"Pebbles" SQL 4.2
+llscmd add user %domain%\\"BamBam" SQL 4.2
+llscmd add user %domain%\\"BamBam" SQL 4.2
+llscmd add user %domain%\\"BamBam" SQL 4.2
+llscmd add user %domain%\\"BamBam" SQL 4.2
+llscmd add user %domain%\\"BamBam" SQL 4.2
+llscmd add user %domain%\\"Mr.Slate" SNA 3.0
+llscmd add user %domain%\\"GeorgeJ" SNA 3.0
+llscmd add user %domain%\\"AstroJ" SNA 3.0
+
+llscmd add user %domain%\\"Fred" Exchange 1.0
+llscmd add user %domain%\\"Fred" Exchange 1.0
+llscmd add user %domain%\\"Fred" Exchange 1.0
+llscmd add user %domain%\\"Barney" Exchange 1.0
+llscmd add user %domain%\\"Barney" Exchange 1.0
+llscmd add user %domain%\\"Barney" Exchange 1.0
+llscmd add user %domain%\\"Barney" Exchange 1.0
+llscmd add user %domain%\\"Wilma" Exchange 1.0
+llscmd add user %domain%\\"Wilma" Exchange 1.0
+llscmd add user %domain%\\"Pebbles" Exchange 1.0
+llscmd add user %domain%\\"Pebbles" Exchange 1.0
+llscmd add user %domain%\\"Pebbles" Exchange 1.0
+llscmd add user %domain%\\"Pebbles" Exchange 1.0
+llscmd add user %domain%\\"Pebbles" Exchange 1.0
+llscmd add user %domain%\\"Pebbles" Exchange 1.0
+llscmd add user %domain%\\"Pebbles" Exchange 1.0
+llscmd add user %domain%\\"Pebbles" Exchange 1.0
+llscmd add user %domain%\\"Pebbles" Exchange 1.0
+llscmd add user %domain%\\"Pebbles" Exchange 1.0
+llscmd add user %domain%\\"Pebbles" Exchange 1.0
+llscmd add user %domain%\\"Pebbles" Exchange 1.0
+llscmd add user %domain%\\"Pebbles" Exchange 1.0
+llscmd add user %domain%\\"BamBam" Exchange 1.0
+llscmd add user %domain%\\"Mr.Slate" Exchange 1.0
diff --git a/private/net/svcdlls/lls/test/ct/lic.bat b/private/net/svcdlls/lls/test/ct/lic.bat
new file mode 100644
index 000000000..796b9b6be
--- /dev/null
+++ b/private/net/svcdlls/lls/test/ct/lic.bat
@@ -0,0 +1,186 @@
+llscmd rpc llslicenseadd "Money 1.0" "TestUser" 5 "Add License Money 1.0"
+llscmd rpc llslicenseadd "Schedule+ 1.0" "TestUser" 5 "Add License Schedule+ 1.0"
+llscmd rpc llslicenseadd "Mail 1.0" "TestUser" 5 "Add License Mail 1.0"
+llscmd rpc llslicenseadd "MONEY30.WIN 1.0" "TestUser" 5 "Add License MONEY30.WIN 1.0"
+llscmd rpc llslicenseadd "MP420A.DOS 1.0" "TestUser" 5 "Add License MP420A.DOS 1.0"
+llscmd rpc llslicenseadd "MSMAIL.ALL 1.0" "TestUser" 5 "Add License MSMAIL.ALL 1.0"
+llscmd rpc llslicenseadd "Win32 1.0" "TestUser" 5 "Add License Win32 1.0"
+llscmd rpc llslicenseadd "Win32s 1.0" "TestUser" 5 "Add License Win32s 1.0"
+llscmd rpc llslicenseadd "Excel 1.0" "TestUser" 5 "Add License Excel 1.0"
+llscmd rpc llslicenseadd "Word 1.0" "TestUser" 5 "Add License Word 1.0"
+llscmd rpc llslicenseadd "ARCADE10.WIN 1.0" "TestUser" 5 "Add License ARCADE10.WIN 1.0"
+llscmd rpc llslicenseadd "ARTGALL.WIN 1.0" "TestUser" 5 "Add License ARTGALL.WIN 1.0"
+llscmd rpc llslicenseadd "VCP100.WIN 1.0" "TestUser" 5 "Add License VCP100.WIN 1.0"
+llscmd rpc llslicenseadd "WINLIB 1.0" "TestUser" 5 "Add License WINLIB 1.0"
+llscmd rpc llslicenseadd "ACCESS20.WIN 1.0" "TestUser" 5 "Add License ACCESS20.WIN 1.0"
+llscmd rpc llslicenseadd "FORTRN32.WIN 1.0" "TestUser" 5 "Add License FORTRN32.WIN 1.0"
+llscmd rpc llslicenseadd "IMSL.NT 1.0" "TestUser" 5 "Add License IMSL.NT 1.0"
+llscmd rpc llslicenseadd "IMSL32.DOS 1.0" "TestUser" 5 "Add License IMSL32.DOS 1.0"
+llscmd rpc llslicenseadd "IMSL51.DOS 1.0" "TestUser" 5 "Add License IMSL51.DOS 1.0"
+llscmd rpc llslicenseadd "Chicago 1.0" "TestUser" 5 "Add License Chicago 1.0"
+llscmd rpc llslicenseadd "Windows95 1.0" "TestUser" 5 "Add License Windows95 1.0"
+llscmd rpc llslicenseadd "Access 1.0" "TestUser" 5 "Add License Access 1.0"
+llscmd rpc llslicenseadd "MASM611 1.0" "TestUser" 5 "Add License MASM611 1.0"
+llscmd rpc llslicenseadd "MFCKIT 1.0" "TestUser" 5 "Add License MFCKIT 1.0"
+llscmd rpc llslicenseadd "mt300a.all 1.0" "TestUser" 5 "Add License mt300a.all 1.0"
+llscmd rpc llslicenseadd "QWGRAPH 1.0" "TestUser" 5 "Add License QWGRAPH 1.0"
+llscmd rpc llslicenseadd "VB10.DOS 1.0" "TestUser" 5 "Add License VB10.DOS 1.0"
+llscmd rpc llslicenseadd "CBT 1.0" "TestUser" 5 "Add License CBT 1.0"
+llscmd rpc llslicenseadd "EISPAK11.WIN 1.0" "TestUser" 5 "Add License EISPAK11.WIN 1.0"
+llscmd rpc llslicenseadd "ENCART95.WIN 1.0" "TestUser" 5 "Add License ENCART95.WIN 1.0"
+llscmd rpc llslicenseadd "EXCEL30.PM 1.0" "TestUser" 5 "Add License EXCEL30.PM 1.0"
+llscmd rpc llslicenseadd "VB30.WIN 1.0" "TestUser" 5 "Add License VB30.WIN 1.0"
+llscmd rpc llslicenseadd "MASM 1.0" "TestUser" 5 "Add License MASM 1.0"
+llscmd rpc llslicenseadd "OLE 1.0" "TestUser" 5 "Add License OLE 1.0"
+llscmd rpc llslicenseadd "Chart 1.0" "TestUser" 5 "Add License Chart 1.0"
+llscmd rpc llslicenseadd "Encarta 1.0" "TestUser" 5 "Add License Encarta 1.0"
+llscmd rpc llslicenseadd "FlightSim 1.0" "TestUser" 5 "Add License FlightSim 1.0"
+llscmd rpc llslicenseadd "Paradox 1.0" "TestUser" 5 "Add License Paradox 1.0"
+llscmd rpc llslicenseadd "VC150.WIN 1.0" "TestUser" 5 "Add License VC150.WIN 1.0"
+llscmd rpc llslicenseadd "VC20.MAC 1.0" "TestUser" 5 "Add License VC20.MAC 1.0"
+llscmd rpc llslicenseadd "IMAGER10.WIN 1.0" "TestUser" 5 "Add License IMAGER10.WIN 1.0"
+llscmd rpc llslicenseadd "LRND300.DOS 1.0" "TestUser" 5 "Add License LRND300.DOS 1.0"
+llscmd rpc llslicenseadd "MMGOLF10.WIN 1.0" "TestUser" 5 "Add License MMGOLF10.WIN 1.0"
+llscmd rpc llslicenseadd "MOM.NT 1.0" "TestUser" 5 "Add License MOM.NT 1.0"
+llscmd rpc llslicenseadd "MSDOS622.DOS 1.0 " "TestUser" 5 "Add License MSDOS622.DOS 1.0"
+llscmd rpc llslicenseadd "MSKBD1.ALL 1.0" "TestUser" 5 "Add License MSKBD1.ALL 1.0"
+llscmd rpc llslicenseadd "MSLOGO.TTF 1.0" "TestUser" 5 "Add License MSLOGO.TTF 1.0"
+llscmd rpc llslicenseadd "MSSMS10 1.0" "TestUser" 5 "Add License MSSMS10 1.0"
+llscmd rpc llslicenseadd "MOM42.WIN 1.0" "TestUser" 5 "Add License MOM42.WIN 1.0"
+llscmd rpc llslicenseadd "SNA 1.0" "TestUser" 5 "Add License SNA 1.0"
+llscmd rpc llslicenseadd "Exchange 1.0" "TestUser" 5 "Add License Exchange 1.0"
+llscmd rpc llslicenseadd "MS_EFORM.WIN 1.0" "TestUser" 5 "Add License MS_EFORM.WIN 1.0"
+llscmd rpc llslicenseadd "ODAKIT 1.0" "TestUser" 5 "Add License ODAKIT 1.0"
+llscmd rpc llslicenseadd "ODBC210.WIN 1.0" "TestUser" 5 "Add License ODBC210.WIN 1.0"
+llscmd rpc llslicenseadd "VC20.NT 1.0" "TestUser" 5 "Add License VC20.NT 1.0"
+llscmd rpc llslicenseadd "BOB.WIN 1.0" "TestUser" 5 "Add License BOB.WIN 1.0"
+llscmd rpc llslicenseadd "BOOK94.WIN 1.0" "TestUser" 5 "Add License BOOK94.WIN 1.0"
+llscmd rpc llslicenseadd "BOWEP.WIN 1.0" "TestUser" 5 "Add License BOWEP.WIN 1.0"
+llscmd rpc llslicenseadd "EXCEL50.NT 1.0" "TestUser" 5 "Add License EXCEL50.NT 1.0"
+llscmd rpc llslicenseadd "EXCEL50C.WIN 1.0" "TestUser" 5 "Add License EXCEL50C.WIN 1.0"
+llscmd rpc llslicenseadd "FLTSIM5a.DOS 1.0" "TestUser" 5 "Add License FLTSIM5a.DOS 1.0"
+llscmd rpc llslicenseadd "FOXPR26A.DOS 1.0" "TestUser" 5 "Add License FOXPR26A.DOS 1.0"
+llscmd rpc llslicenseadd "FOXPR26A.WIN 1.0" "TestUser" 5 "Add License FOXPR26A.WIN 1.0"
+llscmd rpc llslicenseadd "FOXPRO26.UNX 1.0" "TestUser" 5 "Add License FOXPRO26.UNX 1.0"
+llscmd rpc llslicenseadd "WKGRP311.DOS 1.0" "TestUser" 5 "Add License WKGRP311.DOS 1.0"
+llscmd rpc llslicenseadd "wlo09 1.0" "TestUser" 5 "Add License wlo09 1.0"
+llscmd rpc llslicenseadd "wlo10 1.0" "TestUser" 5 "Add License wlo10 1.0"
+llscmd rpc llslicenseadd "ACCSOLPK.WIN 1.0" "TestUser" 5 "Add License ACCSOLPK.WIN 1.0"
+llscmd rpc llslicenseadd "ACCUPTLS.WIN 1.0" "TestUser" 5 "Add License ACCUPTLS.WIN 1.0"
+llscmd rpc llslicenseadd "ADT20.WIN 1.0" "TestUser" 5 "Add License ADT20.WIN 1.0"
+llscmd rpc llslicenseadd "GS100.DOS 1.0" "TestUser" 5 "Add License GS100.DOS 1.0"
+llscmd rpc llslicenseadd "ODBCDDP2.NT 1.0" "TestUser" 5 "Add License ODBCDDP2.NT 1.0"
+llscmd rpc llslicenseadd "ODBCDDP2.WIN 1.0" "TestUser" 5 "Add License ODBCDDP2.WIN 1.0"
+llscmd rpc llslicenseadd "OFF42C.WIN 1.0" "TestUser" 5 "Add License OFF42C.WIN 1.0"
+llscmd rpc llslicenseadd "OFF43c.WIN 1.0" "TestUser" 5 "Add License OFF43c.WIN 1.0"
+llscmd rpc llslicenseadd "OFFAST10.WIN 1.0" "TestUser" 5 "Add License OFFAST10.WIN 1.0"
+llscmd rpc llslicenseadd "OFFDK10.WIN 1.0" "TestUser" 5 "Add License OFFDK10.WIN 1.0"
+llscmd rpc llslicenseadd "FONTPACK.WIN 1.0" "TestUser" 5 "Add License FONTPACK.WIN 1.0"
+llscmd rpc llslicenseadd "FONTPAK2.WIN 1.0" "TestUser" 5 "Add License FONTPAK2.WIN 1.0"
+llscmd rpc llslicenseadd "HPFNTSET.WIN 1.0" "TestUser" 5 "Add License HPFNTSET.WIN 1.0"
+llscmd rpc llslicenseadd "hyper.100 1.0" "TestUser" 5 "Add License hyper.100 1.0"
+llscmd rpc llslicenseadd "IMSL.NT 1.0" "TestUser" 5 "Add License IMSL.NT 1.0"
+llscmd rpc llslicenseadd "IMSL32.DOS 1.0" "TestUser" 5 "Add License IMSL32.DOS 1.0"
+llscmd rpc llslicenseadd "LM22 1.0" "TestUser" 5 "Add License LM22 1.0"
+llscmd rpc llslicenseadd "LMRES10.OS2 1.0" "TestUser" 5 "Add License LMRES10.OS2 1.0"
+llscmd rpc llslicenseadd "LMSFMAC1.0A 1.0" "TestUser" 5 "Add License LMSFMAC1.0A 1.0"
+llscmd rpc llslicenseadd "OFFICE.NT 1.0" "TestUser" 5 "Add License OFFICE.NT 1.0"
+llscmd rpc llslicenseadd "OLE202.WIN 1.0" "TestUser" 5 "Add License OLE202.WIN 1.0"
+llscmd rpc llslicenseadd "ONLN200.WIN 1.0" "TestUser" 5 "Add License ONLN200.WIN 1.0"
+llscmd rpc llslicenseadd "PROJ40.WIN 1.0" "TestUser" 5 "Add License PROJ40.WIN 1.0"
+llscmd rpc llslicenseadd "MSTCPIP.ALL 1.0" "TestUser" 5 "Add License MSTCPIP.ALL 1.0"
+llscmd rpc llslicenseadd "MVWR200.WIN 1.0" "TestUser" 5 "Add License MVWR200.WIN 1.0"
+llscmd rpc llslicenseadd "NETMON 1.0" "TestUser" 5 "Add License NETMON 1.0"
+llscmd rpc llslicenseadd "OS2-1.31 1.0" "TestUser" 5 "Add License OS2-1.31 1.0"
+llscmd rpc llslicenseadd "PMSUB35.NT 1.0" "TestUser" 5 "Add License PMSUB35.NT 1.0"
+llscmd rpc llslicenseadd "RAS11.LM 1.0" "TestUser" 5 "Add License RAS11.LM 1.0"
+llscmd rpc llslicenseadd "PUB20a.WIN 1.0" "TestUser" 5 "Add License PUB20a.WIN 1.0"
+llscmd rpc llslicenseadd "PUBDSIGN.WIN 1.0" "TestUser" 5 "Add License PUBDSIGN.WIN 1.0"
+llscmd rpc llslicenseadd "SAMPLER 1.0" "TestUser" 5 "Add License SAMPLER 1.0"
+llscmd rpc llslicenseadd "SCENES20.WIN 1.0" "TestUser" 5 "Add License SCENES20.WIN 1.0"
+llscmd rpc llslicenseadd "SCHED10A.WIN 1.0" "TestUser" 5 "Add License SCHED10A.WIN 1.0"
+llscmd rpc llslicenseadd "SGML10.WIN 1.0" "TestUser" 5 "Add License SGML10.WIN 1.0"
+llscmd rpc llslicenseadd "SLM.ALL 1.0" "TestUser" 5 "Add License SLM.ALL 1.0"
+llscmd rpc llslicenseadd "SNDBIT10.WIN 1.0" "TestUser" 5 "Add License SNDBIT10.WIN 1.0"
+llscmd rpc llslicenseadd "SPACE10.DOS 1.0" "TestUser" 5 "Add License SPACE10.DOS 1.0"
+llscmd rpc llslicenseadd "SS30.DOS 1.0" "TestUser" 5 "Add License SS30.DOS 1.0"
+llscmd rpc llslicenseadd "ss31std.win 1.0" "TestUser" 5 "Add License ss31std.win 1.0"
+llscmd rpc llslicenseadd "WA100.WIN 1.0" "TestUser" 5 "Add License WA100.WIN 1.0"
+llscmd rpc llslicenseadd "WEP10B.WIN 1.0" "TestUser" 5 "Add License WEP10B.WIN 1.0"
+llscmd rpc llslicenseadd "WEP20.WIN 1.0" "TestUser" 5 "Add License WEP20.WIN 1.0"
+llscmd rpc llslicenseadd "WEP30.WIN 1.0" "TestUser" 5 "Add License WEP30.WIN 1.0"
+llscmd rpc llslicenseadd "WEP40.WIN 1.0" "TestUser" 5 "Add License WEP40.WIN 1.0"
+llscmd rpc llslicenseadd "WGTPLT11.WIN 1.0" "TestUser" 5 "Add License WGTPLT11.WIN 1.0"
+llscmd rpc llslicenseadd "WORD11B.PM 1.0" "TestUser" 5 "Add License WORD11B.PM 1.0"
+llscmd rpc llslicenseadd "WORD6.DOS 1.0" "TestUser" 5 "Add License WORD6.DOS 1.0"
+llscmd rpc llslicenseadd "WORD60.NT 1.0" "TestUser" 5 "Add License WORD60.NT 1.0"
+llscmd rpc llslicenseadd "WORD60C.WIN 1.0" "TestUser" 5 "Add License WORD60C.WIN 1.0"
+llscmd rpc llslicenseadd "word6cnv.nt 1.0" "TestUser" 5 "Add License word6cnv.nt 1.0"
+llscmd rpc llslicenseadd "WORD6CNV.WIN 1.0" "TestUser" 5 "Add License WORD6CNV.WIN 1.0"
+llscmd rpc llslicenseadd "WORDVW60.WIN 1.0" "TestUser" 5 "Add License WORDVW60.WIN 1.0"
+llscmd rpc llslicenseadd "WORKS30.DOS 1.0" "TestUser" 5 "Add License WORKS30.DOS 1.0"
+llscmd rpc llslicenseadd "WORKS30B.WIN 1.0" "TestUser" 5 "Add License WORKS30B.WIN 1.0"
+llscmd rpc llslicenseadd "WPP310.WIN 1.0" "TestUser" 5 "Add License WPP310.WIN 1.0"
+llscmd rpc llslicenseadd "WRITER10.WIN 1.0" "TestUser" 5 "Add License WRITER10.WIN 1.0"
+llscmd rpc llslicenseadd "3com 1.0" "TestUser" 5 "Add License 3com 1.0"
+llscmd rpc llslicenseadd "BLPNT901.ALL 1.0" "TestUser" 5 "Add License BLPNT901.ALL 1.0"
+llscmd rpc llslicenseadd "ARTIST10.WIN 1.0" "TestUser" 5 "Add License ARTIST10.WIN 1.0"
+llscmd rpc llslicenseadd "BASEBL94.WIN 1.0" "TestUser" 5 "Add License BASEBL94.WIN 1.0"
+llscmd rpc llslicenseadd "BA331.DOS 1.0" "TestUser" 5 "Add License BA331.DOS 1.0"
+llscmd rpc llslicenseadd "OS/2 1.0" "TestUser" 5 "Add License OS/2 1.0"
+llscmd rpc llslicenseadd "NetWare 1.0" "TestUser" 5 "Add License NetWare 1.0"
+llscmd rpc llslicenseadd "DOS 1.0" "TestUser" 5 "Add License DOS 1.0"
+llscmd rpc llslicenseadd "C 1.0" "TestUser" 5 "Add License C 1.0"
+llscmd rpc llslicenseadd "CHART300.DOS 1.0" "TestUser" 5 "Add License CHART300.DOS 1.0"
+llscmd rpc llslicenseadd "CINEMA94.WIN 1.0" "TestUser" 5 "Add License CINEMA94.WIN 1.0"
+llscmd rpc llslicenseadd "DELTA10A.WIN 1.0" "TestUser" 5 "Add License DELTA10A.WIN 1.0"
+llscmd rpc llslicenseadd "DINOSAUR.WIN 1.0" "TestUser" 5 "Add License DINOSAUR.WIN 1.0"
+llscmd rpc llslicenseadd "DRAW10.WIN 1.0" "TestUser" 5 "Add License DRAW10.WIN 1.0"
+llscmd rpc llslicenseadd "COBOL500 1.0" "TestUser" 5 "Add License COBOL500 1.0"
+llscmd rpc llslicenseadd "FORT510A.DOS 1.0" "TestUser" 5 "Add License FORT510A.DOS 1.0"
+llscmd rpc llslicenseadd "FORTRN32.NT 1.0" "TestUser" 5 "Add License FORTRN32.NT 1.0"
+llscmd rpc llslicenseadd "VC21.NT 1.0" "TestUser" 5 "Add License VC21.NT 1.0"
+llscmd rpc llslicenseadd "cdex 1.0" "TestUser" 5 "Add License cdex 1.0"
+llscmd rpc llslicenseadd "CLIENTS.NET 1.0" "TestUser" 5 "Add License CLIENTS.NET 1.0"
+llscmd rpc llslicenseadd "CMSRVWS.11 1.0" "TestUser" 5 "Add License CMSRVWS.11 1.0"
+llscmd rpc llslicenseadd "DCIDDK10 1.0" "TestUser" 5 "Add License DCIDDK10 1.0"
+llscmd rpc llslicenseadd "PPT40c.WIN 1.0" "TestUser" 5 "Add License PPT40c.WIN 1.0"
+llscmd rpc llslicenseadd "PROFIT1B.WIN 1.0" "TestUser" 5 "Add License PROFIT1B.WIN 1.0"
+llscmd rpc llslicenseadd "PROJ40.DOS 1.0" "TestUser" 5 "Add License PROJ40.DOS 1.0"
+llscmd rpc llslicenseadd "LMUNIX 1.0" "TestUser" 5 "Add License LMUNIX 1.0"
+llscmd rpc llslicenseadd "MOUSE10a.ALL 1.0" "TestUser" 5 "Add License MOUSE10a.ALL 1.0"
+llscmd rpc llslicenseadd "MSD211.DOS 1.0" "TestUser" 5 "Add License MSD211.DOS 1.0"
+llscmd rpc llslicenseadd "RESKIT.NT 1.0" "TestUser" 5 "Add License RESKIT.NT 1.0"
+llscmd rpc llslicenseadd "SNA21.NT 1.0" "TestUser" 5 "Add License SNA21.NT 1.0"
+llscmd rpc llslicenseadd "SQL.NT 1.0" "TestUser" 5 "Add License SQL.NT 1.0"
+llscmd rpc llslicenseadd "SQL42B.OS2 1.0" "TestUser" 5 "Add License SQL42B.OS2 1.0"
+llscmd rpc llslicenseadd "SQLRES10.OS2 1.0" "TestUser" 5 "Add License SQLRES10.OS2 1.0"
+llscmd rpc llslicenseadd "WFW311.WIN 1.0" "TestUser" 5 "Add License WFW311.WIN 1.0"
+llscmd rpc llslicenseadd "WFWCONN.WIN 1.0" "TestUser" 5 "Add License WFWCONN.WIN 1.0"
+llscmd rpc llslicenseadd "WIN31 1.0" "TestUser" 5 "Add License WIN31 1.0"
+llscmd rpc llslicenseadd "WING10.WIN 1.0" "TestUser" 5 "Add License WING10.WIN 1.0"
+llscmd rpc llslicenseadd "winlogin.win 1.0" "TestUser" 5 "Add License winlogin.win 1.0"
+llscmd rpc llslicenseadd "WINNT.NT 1.0" "TestUser" 5 "Add License WINNT.NT 1.0"
+llscmd rpc llslicenseadd "WINNT35.SRV 1.0" "TestUser" 5 "Add License WINNT35.SRV 1.0"
+llscmd rpc llslicenseadd "WMDLRSDK.WIN 1.0" "TestUser" 5 "Add License WMDLRSDK.WIN 1.0"
+llscmd rpc llslicenseadd "GOLF20.WIN 1.0" "TestUser" 5 "Add License GOLF20.WIN 1.0"
+llscmd rpc llslicenseadd "GRAPH50.WIN 1.0" "TestUser" 5 "Add License GRAPH50.WIN 1.0"
+llscmd rpc llslicenseadd "GREETING.WIN 1.0" "TestUser" 5 "Add License GREETING.WIN 1.0"
+llscmd rpc llslicenseadd "VC20A.NT 1.0" "TestUser" 5 "Add License VC20A.NT 1.0"
+llscmd rpc llslicenseadd "WINNT35.WKS 1.0" "TestUser" 5 "Add License WINNT35.WKS 1.0"
+llscmd rpc llslicenseadd "wpen100a.win 1.0" "TestUser" 5 "Add License wpen100a.win 1.0"
+llscmd rpc llslicenseadd "WPS.WIN 1.0" "TestUser" 5 "Add License WPS.WIN 1.0"
+llscmd rpc llslicenseadd "TAPI10.WIN 1.0" "TestUser" 5 "Add License TAPI10.WIN 1.0"
+llscmd rpc llslicenseadd "TCPIP.ALL 1.0" "TestUser" 5 "Add License TCPIP.ALL 1.0"
+llscmd rpc llslicenseadd "TCPUTIL1.0 1.0" "TestUser" 5 "Add License TCPUTIL1.0 1.0"
+llscmd rpc llslicenseadd "VFW11.WIN 1.0" "TestUser" 5 "Add License VFW11.WIN 1.0"
+llscmd rpc llslicenseadd "WRESKIT2.00 1.0" "TestUser" 5 "Add License WRESKIT2.00 1.0"
+llscmd rpc llslicenseadd "WSS20.WIN 1.0" "TestUser" 5 "Add License WSS20.WIN 1.0"
+llscmd rpc llslicenseadd "MacWord 1.0" "TestUser" 5 "Add License MacWord 1.0"
+llscmd rpc llslicenseadd "PowerPoint 1.0" "TestUser" 5 "Add License PowerPoint 1.0"
+llscmd rpc llslicenseadd "BoundsChecker 1.0" "TestUser" 5 "Add License BoundsChecker 1.0"
+llscmd rpc llslicenseadd "Quattro 1.0" "TestUser" 5 "Add License Quattro 1.0"
+llscmd rpc llslicenseadd "WordPerfect 1.0" "TestUser" 5 "Add License WordPerfect 1.0"
+llscmd rpc llslicenseadd "Office 1.0" "TestUser" 5 "Add License Office 1.0"
+llscmd rpc llslicenseadd "PerfectOffice 1.0" "TestUser" 5 "Add License PerfectOffice 1.0"
+llscmd rpc llslicenseadd "SQL 1.0" "TestUser" 5 "Add License SQL 1.0"
diff --git a/private/net/svcdlls/lls/test/ct/lic1.bat b/private/net/svcdlls/lls/test/ct/lic1.bat
new file mode 100644
index 000000000..796b9b6be
--- /dev/null
+++ b/private/net/svcdlls/lls/test/ct/lic1.bat
@@ -0,0 +1,186 @@
+llscmd rpc llslicenseadd "Money 1.0" "TestUser" 5 "Add License Money 1.0"
+llscmd rpc llslicenseadd "Schedule+ 1.0" "TestUser" 5 "Add License Schedule+ 1.0"
+llscmd rpc llslicenseadd "Mail 1.0" "TestUser" 5 "Add License Mail 1.0"
+llscmd rpc llslicenseadd "MONEY30.WIN 1.0" "TestUser" 5 "Add License MONEY30.WIN 1.0"
+llscmd rpc llslicenseadd "MP420A.DOS 1.0" "TestUser" 5 "Add License MP420A.DOS 1.0"
+llscmd rpc llslicenseadd "MSMAIL.ALL 1.0" "TestUser" 5 "Add License MSMAIL.ALL 1.0"
+llscmd rpc llslicenseadd "Win32 1.0" "TestUser" 5 "Add License Win32 1.0"
+llscmd rpc llslicenseadd "Win32s 1.0" "TestUser" 5 "Add License Win32s 1.0"
+llscmd rpc llslicenseadd "Excel 1.0" "TestUser" 5 "Add License Excel 1.0"
+llscmd rpc llslicenseadd "Word 1.0" "TestUser" 5 "Add License Word 1.0"
+llscmd rpc llslicenseadd "ARCADE10.WIN 1.0" "TestUser" 5 "Add License ARCADE10.WIN 1.0"
+llscmd rpc llslicenseadd "ARTGALL.WIN 1.0" "TestUser" 5 "Add License ARTGALL.WIN 1.0"
+llscmd rpc llslicenseadd "VCP100.WIN 1.0" "TestUser" 5 "Add License VCP100.WIN 1.0"
+llscmd rpc llslicenseadd "WINLIB 1.0" "TestUser" 5 "Add License WINLIB 1.0"
+llscmd rpc llslicenseadd "ACCESS20.WIN 1.0" "TestUser" 5 "Add License ACCESS20.WIN 1.0"
+llscmd rpc llslicenseadd "FORTRN32.WIN 1.0" "TestUser" 5 "Add License FORTRN32.WIN 1.0"
+llscmd rpc llslicenseadd "IMSL.NT 1.0" "TestUser" 5 "Add License IMSL.NT 1.0"
+llscmd rpc llslicenseadd "IMSL32.DOS 1.0" "TestUser" 5 "Add License IMSL32.DOS 1.0"
+llscmd rpc llslicenseadd "IMSL51.DOS 1.0" "TestUser" 5 "Add License IMSL51.DOS 1.0"
+llscmd rpc llslicenseadd "Chicago 1.0" "TestUser" 5 "Add License Chicago 1.0"
+llscmd rpc llslicenseadd "Windows95 1.0" "TestUser" 5 "Add License Windows95 1.0"
+llscmd rpc llslicenseadd "Access 1.0" "TestUser" 5 "Add License Access 1.0"
+llscmd rpc llslicenseadd "MASM611 1.0" "TestUser" 5 "Add License MASM611 1.0"
+llscmd rpc llslicenseadd "MFCKIT 1.0" "TestUser" 5 "Add License MFCKIT 1.0"
+llscmd rpc llslicenseadd "mt300a.all 1.0" "TestUser" 5 "Add License mt300a.all 1.0"
+llscmd rpc llslicenseadd "QWGRAPH 1.0" "TestUser" 5 "Add License QWGRAPH 1.0"
+llscmd rpc llslicenseadd "VB10.DOS 1.0" "TestUser" 5 "Add License VB10.DOS 1.0"
+llscmd rpc llslicenseadd "CBT 1.0" "TestUser" 5 "Add License CBT 1.0"
+llscmd rpc llslicenseadd "EISPAK11.WIN 1.0" "TestUser" 5 "Add License EISPAK11.WIN 1.0"
+llscmd rpc llslicenseadd "ENCART95.WIN 1.0" "TestUser" 5 "Add License ENCART95.WIN 1.0"
+llscmd rpc llslicenseadd "EXCEL30.PM 1.0" "TestUser" 5 "Add License EXCEL30.PM 1.0"
+llscmd rpc llslicenseadd "VB30.WIN 1.0" "TestUser" 5 "Add License VB30.WIN 1.0"
+llscmd rpc llslicenseadd "MASM 1.0" "TestUser" 5 "Add License MASM 1.0"
+llscmd rpc llslicenseadd "OLE 1.0" "TestUser" 5 "Add License OLE 1.0"
+llscmd rpc llslicenseadd "Chart 1.0" "TestUser" 5 "Add License Chart 1.0"
+llscmd rpc llslicenseadd "Encarta 1.0" "TestUser" 5 "Add License Encarta 1.0"
+llscmd rpc llslicenseadd "FlightSim 1.0" "TestUser" 5 "Add License FlightSim 1.0"
+llscmd rpc llslicenseadd "Paradox 1.0" "TestUser" 5 "Add License Paradox 1.0"
+llscmd rpc llslicenseadd "VC150.WIN 1.0" "TestUser" 5 "Add License VC150.WIN 1.0"
+llscmd rpc llslicenseadd "VC20.MAC 1.0" "TestUser" 5 "Add License VC20.MAC 1.0"
+llscmd rpc llslicenseadd "IMAGER10.WIN 1.0" "TestUser" 5 "Add License IMAGER10.WIN 1.0"
+llscmd rpc llslicenseadd "LRND300.DOS 1.0" "TestUser" 5 "Add License LRND300.DOS 1.0"
+llscmd rpc llslicenseadd "MMGOLF10.WIN 1.0" "TestUser" 5 "Add License MMGOLF10.WIN 1.0"
+llscmd rpc llslicenseadd "MOM.NT 1.0" "TestUser" 5 "Add License MOM.NT 1.0"
+llscmd rpc llslicenseadd "MSDOS622.DOS 1.0 " "TestUser" 5 "Add License MSDOS622.DOS 1.0"
+llscmd rpc llslicenseadd "MSKBD1.ALL 1.0" "TestUser" 5 "Add License MSKBD1.ALL 1.0"
+llscmd rpc llslicenseadd "MSLOGO.TTF 1.0" "TestUser" 5 "Add License MSLOGO.TTF 1.0"
+llscmd rpc llslicenseadd "MSSMS10 1.0" "TestUser" 5 "Add License MSSMS10 1.0"
+llscmd rpc llslicenseadd "MOM42.WIN 1.0" "TestUser" 5 "Add License MOM42.WIN 1.0"
+llscmd rpc llslicenseadd "SNA 1.0" "TestUser" 5 "Add License SNA 1.0"
+llscmd rpc llslicenseadd "Exchange 1.0" "TestUser" 5 "Add License Exchange 1.0"
+llscmd rpc llslicenseadd "MS_EFORM.WIN 1.0" "TestUser" 5 "Add License MS_EFORM.WIN 1.0"
+llscmd rpc llslicenseadd "ODAKIT 1.0" "TestUser" 5 "Add License ODAKIT 1.0"
+llscmd rpc llslicenseadd "ODBC210.WIN 1.0" "TestUser" 5 "Add License ODBC210.WIN 1.0"
+llscmd rpc llslicenseadd "VC20.NT 1.0" "TestUser" 5 "Add License VC20.NT 1.0"
+llscmd rpc llslicenseadd "BOB.WIN 1.0" "TestUser" 5 "Add License BOB.WIN 1.0"
+llscmd rpc llslicenseadd "BOOK94.WIN 1.0" "TestUser" 5 "Add License BOOK94.WIN 1.0"
+llscmd rpc llslicenseadd "BOWEP.WIN 1.0" "TestUser" 5 "Add License BOWEP.WIN 1.0"
+llscmd rpc llslicenseadd "EXCEL50.NT 1.0" "TestUser" 5 "Add License EXCEL50.NT 1.0"
+llscmd rpc llslicenseadd "EXCEL50C.WIN 1.0" "TestUser" 5 "Add License EXCEL50C.WIN 1.0"
+llscmd rpc llslicenseadd "FLTSIM5a.DOS 1.0" "TestUser" 5 "Add License FLTSIM5a.DOS 1.0"
+llscmd rpc llslicenseadd "FOXPR26A.DOS 1.0" "TestUser" 5 "Add License FOXPR26A.DOS 1.0"
+llscmd rpc llslicenseadd "FOXPR26A.WIN 1.0" "TestUser" 5 "Add License FOXPR26A.WIN 1.0"
+llscmd rpc llslicenseadd "FOXPRO26.UNX 1.0" "TestUser" 5 "Add License FOXPRO26.UNX 1.0"
+llscmd rpc llslicenseadd "WKGRP311.DOS 1.0" "TestUser" 5 "Add License WKGRP311.DOS 1.0"
+llscmd rpc llslicenseadd "wlo09 1.0" "TestUser" 5 "Add License wlo09 1.0"
+llscmd rpc llslicenseadd "wlo10 1.0" "TestUser" 5 "Add License wlo10 1.0"
+llscmd rpc llslicenseadd "ACCSOLPK.WIN 1.0" "TestUser" 5 "Add License ACCSOLPK.WIN 1.0"
+llscmd rpc llslicenseadd "ACCUPTLS.WIN 1.0" "TestUser" 5 "Add License ACCUPTLS.WIN 1.0"
+llscmd rpc llslicenseadd "ADT20.WIN 1.0" "TestUser" 5 "Add License ADT20.WIN 1.0"
+llscmd rpc llslicenseadd "GS100.DOS 1.0" "TestUser" 5 "Add License GS100.DOS 1.0"
+llscmd rpc llslicenseadd "ODBCDDP2.NT 1.0" "TestUser" 5 "Add License ODBCDDP2.NT 1.0"
+llscmd rpc llslicenseadd "ODBCDDP2.WIN 1.0" "TestUser" 5 "Add License ODBCDDP2.WIN 1.0"
+llscmd rpc llslicenseadd "OFF42C.WIN 1.0" "TestUser" 5 "Add License OFF42C.WIN 1.0"
+llscmd rpc llslicenseadd "OFF43c.WIN 1.0" "TestUser" 5 "Add License OFF43c.WIN 1.0"
+llscmd rpc llslicenseadd "OFFAST10.WIN 1.0" "TestUser" 5 "Add License OFFAST10.WIN 1.0"
+llscmd rpc llslicenseadd "OFFDK10.WIN 1.0" "TestUser" 5 "Add License OFFDK10.WIN 1.0"
+llscmd rpc llslicenseadd "FONTPACK.WIN 1.0" "TestUser" 5 "Add License FONTPACK.WIN 1.0"
+llscmd rpc llslicenseadd "FONTPAK2.WIN 1.0" "TestUser" 5 "Add License FONTPAK2.WIN 1.0"
+llscmd rpc llslicenseadd "HPFNTSET.WIN 1.0" "TestUser" 5 "Add License HPFNTSET.WIN 1.0"
+llscmd rpc llslicenseadd "hyper.100 1.0" "TestUser" 5 "Add License hyper.100 1.0"
+llscmd rpc llslicenseadd "IMSL.NT 1.0" "TestUser" 5 "Add License IMSL.NT 1.0"
+llscmd rpc llslicenseadd "IMSL32.DOS 1.0" "TestUser" 5 "Add License IMSL32.DOS 1.0"
+llscmd rpc llslicenseadd "LM22 1.0" "TestUser" 5 "Add License LM22 1.0"
+llscmd rpc llslicenseadd "LMRES10.OS2 1.0" "TestUser" 5 "Add License LMRES10.OS2 1.0"
+llscmd rpc llslicenseadd "LMSFMAC1.0A 1.0" "TestUser" 5 "Add License LMSFMAC1.0A 1.0"
+llscmd rpc llslicenseadd "OFFICE.NT 1.0" "TestUser" 5 "Add License OFFICE.NT 1.0"
+llscmd rpc llslicenseadd "OLE202.WIN 1.0" "TestUser" 5 "Add License OLE202.WIN 1.0"
+llscmd rpc llslicenseadd "ONLN200.WIN 1.0" "TestUser" 5 "Add License ONLN200.WIN 1.0"
+llscmd rpc llslicenseadd "PROJ40.WIN 1.0" "TestUser" 5 "Add License PROJ40.WIN 1.0"
+llscmd rpc llslicenseadd "MSTCPIP.ALL 1.0" "TestUser" 5 "Add License MSTCPIP.ALL 1.0"
+llscmd rpc llslicenseadd "MVWR200.WIN 1.0" "TestUser" 5 "Add License MVWR200.WIN 1.0"
+llscmd rpc llslicenseadd "NETMON 1.0" "TestUser" 5 "Add License NETMON 1.0"
+llscmd rpc llslicenseadd "OS2-1.31 1.0" "TestUser" 5 "Add License OS2-1.31 1.0"
+llscmd rpc llslicenseadd "PMSUB35.NT 1.0" "TestUser" 5 "Add License PMSUB35.NT 1.0"
+llscmd rpc llslicenseadd "RAS11.LM 1.0" "TestUser" 5 "Add License RAS11.LM 1.0"
+llscmd rpc llslicenseadd "PUB20a.WIN 1.0" "TestUser" 5 "Add License PUB20a.WIN 1.0"
+llscmd rpc llslicenseadd "PUBDSIGN.WIN 1.0" "TestUser" 5 "Add License PUBDSIGN.WIN 1.0"
+llscmd rpc llslicenseadd "SAMPLER 1.0" "TestUser" 5 "Add License SAMPLER 1.0"
+llscmd rpc llslicenseadd "SCENES20.WIN 1.0" "TestUser" 5 "Add License SCENES20.WIN 1.0"
+llscmd rpc llslicenseadd "SCHED10A.WIN 1.0" "TestUser" 5 "Add License SCHED10A.WIN 1.0"
+llscmd rpc llslicenseadd "SGML10.WIN 1.0" "TestUser" 5 "Add License SGML10.WIN 1.0"
+llscmd rpc llslicenseadd "SLM.ALL 1.0" "TestUser" 5 "Add License SLM.ALL 1.0"
+llscmd rpc llslicenseadd "SNDBIT10.WIN 1.0" "TestUser" 5 "Add License SNDBIT10.WIN 1.0"
+llscmd rpc llslicenseadd "SPACE10.DOS 1.0" "TestUser" 5 "Add License SPACE10.DOS 1.0"
+llscmd rpc llslicenseadd "SS30.DOS 1.0" "TestUser" 5 "Add License SS30.DOS 1.0"
+llscmd rpc llslicenseadd "ss31std.win 1.0" "TestUser" 5 "Add License ss31std.win 1.0"
+llscmd rpc llslicenseadd "WA100.WIN 1.0" "TestUser" 5 "Add License WA100.WIN 1.0"
+llscmd rpc llslicenseadd "WEP10B.WIN 1.0" "TestUser" 5 "Add License WEP10B.WIN 1.0"
+llscmd rpc llslicenseadd "WEP20.WIN 1.0" "TestUser" 5 "Add License WEP20.WIN 1.0"
+llscmd rpc llslicenseadd "WEP30.WIN 1.0" "TestUser" 5 "Add License WEP30.WIN 1.0"
+llscmd rpc llslicenseadd "WEP40.WIN 1.0" "TestUser" 5 "Add License WEP40.WIN 1.0"
+llscmd rpc llslicenseadd "WGTPLT11.WIN 1.0" "TestUser" 5 "Add License WGTPLT11.WIN 1.0"
+llscmd rpc llslicenseadd "WORD11B.PM 1.0" "TestUser" 5 "Add License WORD11B.PM 1.0"
+llscmd rpc llslicenseadd "WORD6.DOS 1.0" "TestUser" 5 "Add License WORD6.DOS 1.0"
+llscmd rpc llslicenseadd "WORD60.NT 1.0" "TestUser" 5 "Add License WORD60.NT 1.0"
+llscmd rpc llslicenseadd "WORD60C.WIN 1.0" "TestUser" 5 "Add License WORD60C.WIN 1.0"
+llscmd rpc llslicenseadd "word6cnv.nt 1.0" "TestUser" 5 "Add License word6cnv.nt 1.0"
+llscmd rpc llslicenseadd "WORD6CNV.WIN 1.0" "TestUser" 5 "Add License WORD6CNV.WIN 1.0"
+llscmd rpc llslicenseadd "WORDVW60.WIN 1.0" "TestUser" 5 "Add License WORDVW60.WIN 1.0"
+llscmd rpc llslicenseadd "WORKS30.DOS 1.0" "TestUser" 5 "Add License WORKS30.DOS 1.0"
+llscmd rpc llslicenseadd "WORKS30B.WIN 1.0" "TestUser" 5 "Add License WORKS30B.WIN 1.0"
+llscmd rpc llslicenseadd "WPP310.WIN 1.0" "TestUser" 5 "Add License WPP310.WIN 1.0"
+llscmd rpc llslicenseadd "WRITER10.WIN 1.0" "TestUser" 5 "Add License WRITER10.WIN 1.0"
+llscmd rpc llslicenseadd "3com 1.0" "TestUser" 5 "Add License 3com 1.0"
+llscmd rpc llslicenseadd "BLPNT901.ALL 1.0" "TestUser" 5 "Add License BLPNT901.ALL 1.0"
+llscmd rpc llslicenseadd "ARTIST10.WIN 1.0" "TestUser" 5 "Add License ARTIST10.WIN 1.0"
+llscmd rpc llslicenseadd "BASEBL94.WIN 1.0" "TestUser" 5 "Add License BASEBL94.WIN 1.0"
+llscmd rpc llslicenseadd "BA331.DOS 1.0" "TestUser" 5 "Add License BA331.DOS 1.0"
+llscmd rpc llslicenseadd "OS/2 1.0" "TestUser" 5 "Add License OS/2 1.0"
+llscmd rpc llslicenseadd "NetWare 1.0" "TestUser" 5 "Add License NetWare 1.0"
+llscmd rpc llslicenseadd "DOS 1.0" "TestUser" 5 "Add License DOS 1.0"
+llscmd rpc llslicenseadd "C 1.0" "TestUser" 5 "Add License C 1.0"
+llscmd rpc llslicenseadd "CHART300.DOS 1.0" "TestUser" 5 "Add License CHART300.DOS 1.0"
+llscmd rpc llslicenseadd "CINEMA94.WIN 1.0" "TestUser" 5 "Add License CINEMA94.WIN 1.0"
+llscmd rpc llslicenseadd "DELTA10A.WIN 1.0" "TestUser" 5 "Add License DELTA10A.WIN 1.0"
+llscmd rpc llslicenseadd "DINOSAUR.WIN 1.0" "TestUser" 5 "Add License DINOSAUR.WIN 1.0"
+llscmd rpc llslicenseadd "DRAW10.WIN 1.0" "TestUser" 5 "Add License DRAW10.WIN 1.0"
+llscmd rpc llslicenseadd "COBOL500 1.0" "TestUser" 5 "Add License COBOL500 1.0"
+llscmd rpc llslicenseadd "FORT510A.DOS 1.0" "TestUser" 5 "Add License FORT510A.DOS 1.0"
+llscmd rpc llslicenseadd "FORTRN32.NT 1.0" "TestUser" 5 "Add License FORTRN32.NT 1.0"
+llscmd rpc llslicenseadd "VC21.NT 1.0" "TestUser" 5 "Add License VC21.NT 1.0"
+llscmd rpc llslicenseadd "cdex 1.0" "TestUser" 5 "Add License cdex 1.0"
+llscmd rpc llslicenseadd "CLIENTS.NET 1.0" "TestUser" 5 "Add License CLIENTS.NET 1.0"
+llscmd rpc llslicenseadd "CMSRVWS.11 1.0" "TestUser" 5 "Add License CMSRVWS.11 1.0"
+llscmd rpc llslicenseadd "DCIDDK10 1.0" "TestUser" 5 "Add License DCIDDK10 1.0"
+llscmd rpc llslicenseadd "PPT40c.WIN 1.0" "TestUser" 5 "Add License PPT40c.WIN 1.0"
+llscmd rpc llslicenseadd "PROFIT1B.WIN 1.0" "TestUser" 5 "Add License PROFIT1B.WIN 1.0"
+llscmd rpc llslicenseadd "PROJ40.DOS 1.0" "TestUser" 5 "Add License PROJ40.DOS 1.0"
+llscmd rpc llslicenseadd "LMUNIX 1.0" "TestUser" 5 "Add License LMUNIX 1.0"
+llscmd rpc llslicenseadd "MOUSE10a.ALL 1.0" "TestUser" 5 "Add License MOUSE10a.ALL 1.0"
+llscmd rpc llslicenseadd "MSD211.DOS 1.0" "TestUser" 5 "Add License MSD211.DOS 1.0"
+llscmd rpc llslicenseadd "RESKIT.NT 1.0" "TestUser" 5 "Add License RESKIT.NT 1.0"
+llscmd rpc llslicenseadd "SNA21.NT 1.0" "TestUser" 5 "Add License SNA21.NT 1.0"
+llscmd rpc llslicenseadd "SQL.NT 1.0" "TestUser" 5 "Add License SQL.NT 1.0"
+llscmd rpc llslicenseadd "SQL42B.OS2 1.0" "TestUser" 5 "Add License SQL42B.OS2 1.0"
+llscmd rpc llslicenseadd "SQLRES10.OS2 1.0" "TestUser" 5 "Add License SQLRES10.OS2 1.0"
+llscmd rpc llslicenseadd "WFW311.WIN 1.0" "TestUser" 5 "Add License WFW311.WIN 1.0"
+llscmd rpc llslicenseadd "WFWCONN.WIN 1.0" "TestUser" 5 "Add License WFWCONN.WIN 1.0"
+llscmd rpc llslicenseadd "WIN31 1.0" "TestUser" 5 "Add License WIN31 1.0"
+llscmd rpc llslicenseadd "WING10.WIN 1.0" "TestUser" 5 "Add License WING10.WIN 1.0"
+llscmd rpc llslicenseadd "winlogin.win 1.0" "TestUser" 5 "Add License winlogin.win 1.0"
+llscmd rpc llslicenseadd "WINNT.NT 1.0" "TestUser" 5 "Add License WINNT.NT 1.0"
+llscmd rpc llslicenseadd "WINNT35.SRV 1.0" "TestUser" 5 "Add License WINNT35.SRV 1.0"
+llscmd rpc llslicenseadd "WMDLRSDK.WIN 1.0" "TestUser" 5 "Add License WMDLRSDK.WIN 1.0"
+llscmd rpc llslicenseadd "GOLF20.WIN 1.0" "TestUser" 5 "Add License GOLF20.WIN 1.0"
+llscmd rpc llslicenseadd "GRAPH50.WIN 1.0" "TestUser" 5 "Add License GRAPH50.WIN 1.0"
+llscmd rpc llslicenseadd "GREETING.WIN 1.0" "TestUser" 5 "Add License GREETING.WIN 1.0"
+llscmd rpc llslicenseadd "VC20A.NT 1.0" "TestUser" 5 "Add License VC20A.NT 1.0"
+llscmd rpc llslicenseadd "WINNT35.WKS 1.0" "TestUser" 5 "Add License WINNT35.WKS 1.0"
+llscmd rpc llslicenseadd "wpen100a.win 1.0" "TestUser" 5 "Add License wpen100a.win 1.0"
+llscmd rpc llslicenseadd "WPS.WIN 1.0" "TestUser" 5 "Add License WPS.WIN 1.0"
+llscmd rpc llslicenseadd "TAPI10.WIN 1.0" "TestUser" 5 "Add License TAPI10.WIN 1.0"
+llscmd rpc llslicenseadd "TCPIP.ALL 1.0" "TestUser" 5 "Add License TCPIP.ALL 1.0"
+llscmd rpc llslicenseadd "TCPUTIL1.0 1.0" "TestUser" 5 "Add License TCPUTIL1.0 1.0"
+llscmd rpc llslicenseadd "VFW11.WIN 1.0" "TestUser" 5 "Add License VFW11.WIN 1.0"
+llscmd rpc llslicenseadd "WRESKIT2.00 1.0" "TestUser" 5 "Add License WRESKIT2.00 1.0"
+llscmd rpc llslicenseadd "WSS20.WIN 1.0" "TestUser" 5 "Add License WSS20.WIN 1.0"
+llscmd rpc llslicenseadd "MacWord 1.0" "TestUser" 5 "Add License MacWord 1.0"
+llscmd rpc llslicenseadd "PowerPoint 1.0" "TestUser" 5 "Add License PowerPoint 1.0"
+llscmd rpc llslicenseadd "BoundsChecker 1.0" "TestUser" 5 "Add License BoundsChecker 1.0"
+llscmd rpc llslicenseadd "Quattro 1.0" "TestUser" 5 "Add License Quattro 1.0"
+llscmd rpc llslicenseadd "WordPerfect 1.0" "TestUser" 5 "Add License WordPerfect 1.0"
+llscmd rpc llslicenseadd "Office 1.0" "TestUser" 5 "Add License Office 1.0"
+llscmd rpc llslicenseadd "PerfectOffice 1.0" "TestUser" 5 "Add License PerfectOffice 1.0"
+llscmd rpc llslicenseadd "SQL 1.0" "TestUser" 5 "Add License SQL 1.0"
diff --git a/private/net/svcdlls/lls/test/ct/lic2.bat b/private/net/svcdlls/lls/test/ct/lic2.bat
new file mode 100644
index 000000000..9fb7d805b
--- /dev/null
+++ b/private/net/svcdlls/lls/test/ct/lic2.bat
@@ -0,0 +1,186 @@
+llscmd rpc llslicenseadd "Money 3.1" "TestUser" 1 "Add License Money 3.1"
+llscmd rpc llslicenseadd "Schedule+ 3.1" "TestUser" 2 "Add License Schedule+ 3.1"
+llscmd rpc llslicenseadd "Mail 3.1" "TestUser" 3 "Add License Mail 3.1"
+llscmd rpc llslicenseadd "MONEY30.WIN 3.1" "TestUser" 4 "Add License MONEY30.WIN 3.1"
+llscmd rpc llslicenseadd "MP420A.DOS 3.1" "TestUser" 5 "Add License MP420A.DOS 3.1"
+llscmd rpc llslicenseadd "MSMAIL.ALL 3.1" "TestUser" 6 "Add License MSMAIL.ALL 3.1"
+llscmd rpc llslicenseadd "Win32 3.1" "TestUser" 7 "Add License Win32 3.1"
+llscmd rpc llslicenseadd "Win32s 3.1" "TestUser" 8 "Add License Win32s 3.1"
+llscmd rpc llslicenseadd "Excel 3.1" "TestUser" 9 "Add License Excel 3.1"
+llscmd rpc llslicenseadd "Word 3.1" "TestUser" 10 "Add License Word 3.1"
+llscmd rpc llslicenseadd "ARCADE10.WIN 3.1" "TestUser" 11 "Add License ARCADE10.WIN 3.1"
+llscmd rpc llslicenseadd "ARTGALL.WIN 3.1" "TestUser" 12 "Add License ARTGALL.WIN 3.1"
+llscmd rpc llslicenseadd "VCP100.WIN 3.1" "TestUser" 13 "Add License VCP100.WIN 3.1"
+llscmd rpc llslicenseadd "WINLIB 3.1" "TestUser" 14 "Add License WINLIB 3.1"
+llscmd rpc llslicenseadd "ACCESS20.WIN 3.1" "TestUser" 15 "Add License ACCESS20.WIN 3.1"
+llscmd rpc llslicenseadd "FORTRN32.WIN 3.1" "TestUser" 16 "Add License FORTRN32.WIN 3.1"
+llscmd rpc llslicenseadd "IMSL.NT 3.1" "TestUser" 17 "Add License IMSL.NT 3.1"
+llscmd rpc llslicenseadd "IMSL32.DOS 3.1" "TestUser" 18 "Add License IMSL32.DOS 3.1"
+llscmd rpc llslicenseadd "IMSL51.DOS 3.1" "TestUser" 19 "Add License IMSL51.DOS 3.1"
+llscmd rpc llslicenseadd "Chicago 3.1" "TestUser" 20 "Add License Chicago 3.1"
+llscmd rpc llslicenseadd "Windows95 3.1" "TestUser" 21 "Add License Windows95 3.1"
+llscmd rpc llslicenseadd "Access 3.1" "TestUser" 22 "Add License Access 3.1"
+llscmd rpc llslicenseadd "MASM611 3.1" "TestUser" 23 "Add License MASM611 3.1"
+llscmd rpc llslicenseadd "MFCKIT 3.1" "TestUser" 24 "Add License MFCKIT 3.1"
+llscmd rpc llslicenseadd "mt300a.all 3.1" "TestUser" 25 "Add License mt300a.all 3.1"
+llscmd rpc llslicenseadd "QWGRAPH 3.1" "TestUser" 26 "Add License QWGRAPH 3.1"
+llscmd rpc llslicenseadd "VB10.DOS 3.1" "TestUser" 27 "Add License VB10.DOS 3.1"
+llscmd rpc llslicenseadd "CBT 3.1" "TestUser" 28 "Add License CBT 3.1"
+llscmd rpc llslicenseadd "EISPAK11.WIN 3.1" "TestUser" 29 "Add License EISPAK11.WIN 3.1"
+llscmd rpc llslicenseadd "ENCART95.WIN 3.1" "TestUser" 30 "Add License ENCART95.WIN 3.1"
+llscmd rpc llslicenseadd "EXCEL30.PM 3.1" "TestUser" 31 "Add License EXCEL30.PM 3.1"
+llscmd rpc llslicenseadd "VB30.WIN 3.1" "TestUser" 32 "Add License VB30.WIN 3.1"
+llscmd rpc llslicenseadd "MASM 3.1" "TestUser" 33 "Add License MASM 3.1"
+llscmd rpc llslicenseadd "OLE 3.1" "TestUser" 34 "Add License OLE 3.1"
+llscmd rpc llslicenseadd "Chart 3.1" "TestUser" 35 "Add License Chart 3.1"
+llscmd rpc llslicenseadd "Encarta 3.1" "TestUser" 36 "Add License Encarta 3.1"
+llscmd rpc llslicenseadd "FlightSim 3.1" "TestUser" 37 "Add License FlightSim 3.1"
+llscmd rpc llslicenseadd "Paradox 3.1" "TestUser" 38 "Add License Paradox 3.1"
+llscmd rpc llslicenseadd "VC150.WIN 3.1" "TestUser" 39 "Add License VC150.WIN 3.1"
+llscmd rpc llslicenseadd "VC20.MAC 3.1" "TestUser" 40 "Add License VC20.MAC 3.1"
+llscmd rpc llslicenseadd "IMAGER10.WIN 3.1" "TestUser" 41 "Add License IMAGER10.WIN 3.1"
+llscmd rpc llslicenseadd "LRND300.DOS 3.1" "TestUser" 42 "Add License LRND300.DOS 3.1"
+llscmd rpc llslicenseadd "MMGOLF10.WIN 3.1" "TestUser" 43 "Add License MMGOLF10.WIN 3.1"
+llscmd rpc llslicenseadd "MOM.NT 3.1" "TestUser" 44 "Add License MOM.NT 3.1"
+llscmd rpc llslicenseadd "MSDOS622.DOS 3.1 " "TestUser" 45 "Add License MSDOS622.DOS 3.1"
+llscmd rpc llslicenseadd "MSKBD1.ALL 3.1" "TestUser" 46 "Add License MSKBD1.ALL 3.1"
+llscmd rpc llslicenseadd "MSLOGO.TTF 3.1" "TestUser" 47 "Add License MSLOGO.TTF 3.1"
+llscmd rpc llslicenseadd "MSSMS10 3.1" "TestUser" 48 "Add License MSSMS10 3.1"
+llscmd rpc llslicenseadd "MOM42.WIN 3.1" "TestUser" 49 "Add License MOM42.WIN 3.1"
+llscmd rpc llslicenseadd "SNA 3.1" "TestUser" 50 "Add License SNA 3.1"
+llscmd rpc llslicenseadd "Exchange 3.1" "TestUser" 51 "Add License Exchange 3.1"
+llscmd rpc llslicenseadd "MS_EFORM.WIN 3.1" "TestUser" 52 "Add License MS_EFORM.WIN 3.1"
+llscmd rpc llslicenseadd "ODAKIT 3.1" "TestUser" 53 "Add License ODAKIT 3.1"
+llscmd rpc llslicenseadd "ODBC210.WIN 3.1" "TestUser" 54 "Add License ODBC210.WIN 3.1"
+llscmd rpc llslicenseadd "VC20.NT 3.1" "TestUser" 55 "Add License VC20.NT 3.1"
+llscmd rpc llslicenseadd "BOB.WIN 3.1" "TestUser" 56 "Add License BOB.WIN 3.1"
+llscmd rpc llslicenseadd "BOOK94.WIN 3.1" "TestUser" 57 "Add License BOOK94.WIN 3.1"
+llscmd rpc llslicenseadd "BOWEP.WIN 3.1" "TestUser" 58 "Add License BOWEP.WIN 3.1"
+llscmd rpc llslicenseadd "EXCEL50.NT 3.1" "TestUser" 59 "Add License EXCEL50.NT 3.1"
+llscmd rpc llslicenseadd "EXCEL50C.WIN 3.1" "TestUser" 60 "Add License EXCEL50C.WIN 3.1"
+llscmd rpc llslicenseadd "FLTSIM5a.DOS 3.1" "TestUser" 61 "Add License FLTSIM5a.DOS 3.1"
+llscmd rpc llslicenseadd "FOXPR26A.DOS 3.1" "TestUser" 62 "Add License FOXPR26A.DOS 3.1"
+llscmd rpc llslicenseadd "FOXPR26A.WIN 3.1" "TestUser" 63 "Add License FOXPR26A.WIN 3.1"
+llscmd rpc llslicenseadd "FOXPRO26.UNX 3.1" "TestUser" 64 "Add License FOXPRO26.UNX 3.1"
+llscmd rpc llslicenseadd "WKGRP311.DOS 3.1" "TestUser" 65 "Add License WKGRP311.DOS 3.1"
+llscmd rpc llslicenseadd "wlo09 3.1" "TestUser" 66 "Add License wlo09 3.1"
+llscmd rpc llslicenseadd "wlo10 3.1" "TestUser" 67 "Add License wlo10 3.1"
+llscmd rpc llslicenseadd "ACCSOLPK.WIN 3.1" "TestUser" 68 "Add License ACCSOLPK.WIN 3.1"
+llscmd rpc llslicenseadd "ACCUPTLS.WIN 3.1" "TestUser" 69 "Add License ACCUPTLS.WIN 3.1"
+llscmd rpc llslicenseadd "ADT20.WIN 3.1" "TestUser" 70 "Add License ADT20.WIN 3.1"
+llscmd rpc llslicenseadd "GS100.DOS 3.1" "TestUser" 71 "Add License GS100.DOS 3.1"
+llscmd rpc llslicenseadd "ODBCDDP2.NT 3.1" "TestUser" 72 "Add License ODBCDDP2.NT 3.1"
+llscmd rpc llslicenseadd "ODBCDDP2.WIN 3.1" "TestUser" 73 "Add License ODBCDDP2.WIN 3.1"
+llscmd rpc llslicenseadd "OFF42C.WIN 3.1" "TestUser" 74 "Add License OFF42C.WIN 3.1"
+llscmd rpc llslicenseadd "OFF43c.WIN 3.1" "TestUser" 75 "Add License OFF43c.WIN 3.1"
+llscmd rpc llslicenseadd "OFFAST10.WIN 3.1" "TestUser" 76 "Add License OFFAST10.WIN 3.1"
+llscmd rpc llslicenseadd "OFFDK10.WIN 3.1" "TestUser" 77 "Add License OFFDK10.WIN 3.1"
+llscmd rpc llslicenseadd "FONTPACK.WIN 3.1" "TestUser" 78 "Add License FONTPACK.WIN 3.1"
+llscmd rpc llslicenseadd "FONTPAK2.WIN 3.1" "TestUser" 79 "Add License FONTPAK2.WIN 3.1"
+llscmd rpc llslicenseadd "HPFNTSET.WIN 3.1" "TestUser" 80 "Add License HPFNTSET.WIN 3.1"
+llscmd rpc llslicenseadd "hyper.100 3.1" "TestUser" 81 "Add License hyper.100 3.1"
+llscmd rpc llslicenseadd "IMSL.NT 3.1" "TestUser" 82 "Add License IMSL.NT 3.1"
+llscmd rpc llslicenseadd "IMSL32.DOS 3.1" "TestUser" 83 "Add License IMSL32.DOS 3.1"
+llscmd rpc llslicenseadd "LM22 3.1" "TestUser" 84 "Add License LM22 3.1"
+llscmd rpc llslicenseadd "LMRES10.OS2 3.1" "TestUser" 85 "Add License LMRES10.OS2 3.1"
+llscmd rpc llslicenseadd "LMSFMAC3.1A 3.1" "TestUser" 86 "Add License LMSFMAC3.1A 3.1"
+llscmd rpc llslicenseadd "OFFICE.NT 3.1" "TestUser" 87 "Add License OFFICE.NT 3.1"
+llscmd rpc llslicenseadd "OLE202.WIN 3.1" "TestUser" 88 "Add License OLE202.WIN 3.1"
+llscmd rpc llslicenseadd "ONLN200.WIN 3.1" "TestUser" 89 "Add License ONLN200.WIN 3.1"
+llscmd rpc llslicenseadd "PROJ40.WIN 3.1" "TestUser" 90 "Add License PROJ40.WIN 3.1"
+llscmd rpc llslicenseadd "MSTCPIP.ALL 3.1" "TestUser" 91 "Add License MSTCPIP.ALL 3.1"
+llscmd rpc llslicenseadd "MVWR200.WIN 3.1" "TestUser" 92 "Add License MVWR200.WIN 3.1"
+llscmd rpc llslicenseadd "NETMON 3.1" "TestUser" 93 "Add License NETMON 3.1"
+llscmd rpc llslicenseadd "OS2-1.31 3.1" "TestUser" 94 "Add License OS2-1.31 3.1"
+llscmd rpc llslicenseadd "PMSUB35.NT 3.1" "TestUser" 95 "Add License PMSUB35.NT 3.1"
+llscmd rpc llslicenseadd "RAS11.LM 3.1" "TestUser" 96 "Add License RAS11.LM 3.1"
+llscmd rpc llslicenseadd "PUB20a.WIN 3.1" "TestUser" 97 "Add License PUB20a.WIN 3.1"
+llscmd rpc llslicenseadd "PUBDSIGN.WIN 3.1" "TestUser" 98 "Add License PUBDSIGN.WIN 3.1"
+llscmd rpc llslicenseadd "SAMPLER 3.1" "TestUser" 99 "Add License SAMPLER 3.1"
+llscmd rpc llslicenseadd "SCENES20.WIN 3.1" "TestUser" 100 "Add License SCENES20.WIN 3.1"
+llscmd rpc llslicenseadd "SCHED10A.WIN 3.1" "TestUser" 101 "Add License SCHED10A.WIN 3.1"
+llscmd rpc llslicenseadd "SGML10.WIN 3.1" "TestUser" 102 "Add License SGML10.WIN 3.1"
+llscmd rpc llslicenseadd "SLM.ALL 3.1" "TestUser" 103 "Add License SLM.ALL 3.1"
+llscmd rpc llslicenseadd "SNDBIT10.WIN 3.1" "TestUser" 104 "Add License SNDBIT10.WIN 3.1"
+llscmd rpc llslicenseadd "SPACE10.DOS 3.1" "TestUser" 105 "Add License SPACE10.DOS 3.1"
+llscmd rpc llslicenseadd "SS30.DOS 3.1" "TestUser" 105 "Add License SS30.DOS 3.1"
+llscmd rpc llslicenseadd "ss31std.win 3.1" "TestUser" 105 "Add License ss31std.win 3.1"
+llscmd rpc llslicenseadd "WA100.WIN 3.1" "TestUser" 105 "Add License WA100.WIN 3.1"
+llscmd rpc llslicenseadd "WEP10B.WIN 3.1" "TestUser" 105 "Add License WEP10B.WIN 3.1"
+llscmd rpc llslicenseadd "WEP20.WIN 3.1" "TestUser" 115 "Add License WEP20.WIN 3.1"
+llscmd rpc llslicenseadd "WEP30.WIN 3.1" "TestUser" 115 "Add License WEP30.WIN 3.1"
+llscmd rpc llslicenseadd "WEP40.WIN 3.1" "TestUser" 115 "Add License WEP40.WIN 3.1"
+llscmd rpc llslicenseadd "WGTPLT11.WIN 3.1" "TestUser" 115 "Add License WGTPLT11.WIN 3.1"
+llscmd rpc llslicenseadd "WORD11B.PM 3.1" "TestUser" 115 "Add License WORD11B.PM 3.1"
+llscmd rpc llslicenseadd "WORD6.DOS 3.1" "TestUser" 115 "Add License WORD6.DOS 3.1"
+llscmd rpc llslicenseadd "WORD60.NT 3.1" "TestUser" 115 "Add License WORD60.NT 3.1"
+llscmd rpc llslicenseadd "WORD60C.WIN 3.1" "TestUser" 115 "Add License WORD60C.WIN 3.1"
+llscmd rpc llslicenseadd "word6cnv.nt 3.1" "TestUser" 115 "Add License word6cnv.nt 3.1"
+llscmd rpc llslicenseadd "WORD6CNV.WIN 3.1" "TestUser" 115 "Add License WORD6CNV.WIN 3.1"
+llscmd rpc llslicenseadd "WORDVW60.WIN 3.1" "TestUser" 125 "Add License WORDVW60.WIN 3.1"
+llscmd rpc llslicenseadd "WORKS30.DOS 3.1" "TestUser" 125 "Add License WORKS30.DOS 3.1"
+llscmd rpc llslicenseadd "WORKS30B.WIN 3.1" "TestUser" 125 "Add License WORKS30B.WIN 3.1"
+llscmd rpc llslicenseadd "WPP310.WIN 3.1" "TestUser" 125 "Add License WPP310.WIN 3.1"
+llscmd rpc llslicenseadd "WRITER10.WIN 3.1" "TestUser" 125 "Add License WRITER10.WIN 3.1"
+llscmd rpc llslicenseadd "3com 3.1" "TestUser" 125 "Add License 3com 3.1"
+llscmd rpc llslicenseadd "BLPNT901.ALL 3.1" "TestUser" 125 "Add License BLPNT901.ALL 3.1"
+llscmd rpc llslicenseadd "ARTIST10.WIN 3.1" "TestUser" 125 "Add License ARTIST10.WIN 3.1"
+llscmd rpc llslicenseadd "BASEBL94.WIN 3.1" "TestUser" 125 "Add License BASEBL94.WIN 3.1"
+llscmd rpc llslicenseadd "BA331.DOS 3.1" "TestUser" 125 "Add License BA331.DOS 3.1"
+llscmd rpc llslicenseadd "OS/2 3.1" "TestUser" 135 "Add License OS/2 3.1"
+llscmd rpc llslicenseadd "NetWare 3.1" "TestUser" 135 "Add License NetWare 3.1"
+llscmd rpc llslicenseadd "DOS 3.1" "TestUser" 135 "Add License DOS 3.1"
+llscmd rpc llslicenseadd "C 3.1" "TestUser" 135 "Add License C 3.1"
+llscmd rpc llslicenseadd "CHART300.DOS 3.1" "TestUser" 135 "Add License CHART300.DOS 3.1"
+llscmd rpc llslicenseadd "CINEMA94.WIN 3.1" "TestUser" 135 "Add License CINEMA94.WIN 3.1"
+llscmd rpc llslicenseadd "DELTA10A.WIN 3.1" "TestUser" 135 "Add License DELTA10A.WIN 3.1"
+llscmd rpc llslicenseadd "DINOSAUR.WIN 3.1" "TestUser" 135 "Add License DINOSAUR.WIN 3.1"
+llscmd rpc llslicenseadd "DRAW10.WIN 3.1" "TestUser" 135 "Add License DRAW10.WIN 3.1"
+llscmd rpc llslicenseadd "COBOL500 3.1" "TestUser" 135 "Add License COBOL500 3.1"
+llscmd rpc llslicenseadd "FORT510A.DOS 3.1" "TestUser" 145 "Add License FORT510A.DOS 3.1"
+llscmd rpc llslicenseadd "FORTRN32.NT 3.1" "TestUser" 145 "Add License FORTRN32.NT 3.1"
+llscmd rpc llslicenseadd "VC21.NT 3.1" "TestUser" 145 "Add License VC21.NT 3.1"
+llscmd rpc llslicenseadd "cdex 3.1" "TestUser" 145 "Add License cdex 3.1"
+llscmd rpc llslicenseadd "CLIENTS.NET 3.1" "TestUser" 145 "Add License CLIENTS.NET 3.1"
+llscmd rpc llslicenseadd "CMSRVWS.11 3.1" "TestUser" 145 "Add License CMSRVWS.11 3.1"
+llscmd rpc llslicenseadd "DCIDDK10 3.1" "TestUser" 145 "Add License DCIDDK10 3.1"
+llscmd rpc llslicenseadd "PPT40c.WIN 3.1" "TestUser" 145 "Add License PPT40c.WIN 3.1"
+llscmd rpc llslicenseadd "PROFIT1B.WIN 3.1" "TestUser" 145 "Add License PROFIT1B.WIN 3.1"
+llscmd rpc llslicenseadd "PROJ40.DOS 3.1" "TestUser" 145 "Add License PROJ40.DOS 3.1"
+llscmd rpc llslicenseadd "LMUNIX 3.1" "TestUser" 155 "Add License LMUNIX 3.1"
+llscmd rpc llslicenseadd "MOUSE10a.ALL 3.1" "TestUser" 155 "Add License MOUSE10a.ALL 3.1"
+llscmd rpc llslicenseadd "MSD211.DOS 3.1" "TestUser" 155 "Add License MSD211.DOS 3.1"
+llscmd rpc llslicenseadd "RESKIT.NT 3.1" "TestUser" 155 "Add License RESKIT.NT 3.1"
+llscmd rpc llslicenseadd "SNA21.NT 3.1" "TestUser" 155 "Add License SNA21.NT 3.1"
+llscmd rpc llslicenseadd "SQL.NT 3.1" "TestUser" 155 "Add License SQL.NT 3.1"
+llscmd rpc llslicenseadd "SQL42B.OS2 3.1" "TestUser" 155 "Add License SQL42B.OS2 3.1"
+llscmd rpc llslicenseadd "SQLRES10.OS2 3.1" "TestUser" 155 "Add License SQLRES10.OS2 3.1"
+llscmd rpc llslicenseadd "WFW311.WIN 3.1" "TestUser" 155 "Add License WFW311.WIN 3.1"
+llscmd rpc llslicenseadd "WFWCONN.WIN 3.1" "TestUser" 155 "Add License WFWCONN.WIN 3.1"
+llscmd rpc llslicenseadd "WIN31 3.1" "TestUser" 165 "Add License WIN31 3.1"
+llscmd rpc llslicenseadd "WING10.WIN 3.1" "TestUser" 165 "Add License WING10.WIN 3.1"
+llscmd rpc llslicenseadd "winlogin.win 3.1" "TestUser" 165 "Add License winlogin.win 3.1"
+llscmd rpc llslicenseadd "WINNT.NT 3.1" "TestUser" 165 "Add License WINNT.NT 3.1"
+llscmd rpc llslicenseadd "WINNT35.SRV 3.1" "TestUser" 165 "Add License WINNT35.SRV 3.1"
+llscmd rpc llslicenseadd "WMDLRSDK.WIN 3.1" "TestUser" 165 "Add License WMDLRSDK.WIN 3.1"
+llscmd rpc llslicenseadd "GOLF20.WIN 3.1" "TestUser" 165 "Add License GOLF20.WIN 3.1"
+llscmd rpc llslicenseadd "GRAPH50.WIN 3.1" "TestUser" 165 "Add License GRAPH50.WIN 3.1"
+llscmd rpc llslicenseadd "GREETING.WIN 3.1" "TestUser" 165 "Add License GREETING.WIN 3.1"
+llscmd rpc llslicenseadd "VC20A.NT 3.1" "TestUser" 165 "Add License VC20A.NT 3.1"
+llscmd rpc llslicenseadd "WINNT35.WKS 3.1" "TestUser" 175 "Add License WINNT35.WKS 3.1"
+llscmd rpc llslicenseadd "wpen100a.win 3.1" "TestUser" 175 "Add License wpen100a.win 3.1"
+llscmd rpc llslicenseadd "WPS.WIN 3.1" "TestUser" 175 "Add License WPS.WIN 3.1"
+llscmd rpc llslicenseadd "TAPI10.WIN 3.1" "TestUser" 175 "Add License TAPI10.WIN 3.1"
+llscmd rpc llslicenseadd "TCPIP.ALL 3.1" "TestUser" 175 "Add License TCPIP.ALL 3.1"
+llscmd rpc llslicenseadd "TCPUTIL3.1 3.1" "TestUser" 175 "Add License TCPUTIL3.1 3.1"
+llscmd rpc llslicenseadd "VFW11.WIN 3.1" "TestUser" 175 "Add License VFW11.WIN 3.1"
+llscmd rpc llslicenseadd "WRESKIT2.00 3.1" "TestUser" 175 "Add License WRESKIT2.00 3.1"
+llscmd rpc llslicenseadd "WSS20.WIN 3.1" "TestUser" 175 "Add License WSS20.WIN 3.1"
+llscmd rpc llslicenseadd "MacWord 3.1" "TestUser" 175 "Add License MacWord 3.1"
+llscmd rpc llslicenseadd "PowerPoint 3.1" "TestUser" 185 "Add License PowerPoint 3.1"
+llscmd rpc llslicenseadd "BoundsChecker 3.1" "TestUser" 185 "Add License BoundsChecker 3.1"
+llscmd rpc llslicenseadd "Quattro 3.1" "TestUser" 185 "Add License Quattro 3.1"
+llscmd rpc llslicenseadd "WordPerfect 3.1" "TestUser" 185 "Add License WordPerfect 3.1"
+llscmd rpc llslicenseadd "Office 3.1" "TestUser" 185 "Add License Office 3.1"
+llscmd rpc llslicenseadd "PerfectOffice 3.1" "TestUser" 185 "Add License PerfectOffice 3.1"
+llscmd rpc llslicenseadd "SQL 3.1" "TestUser" 185 "Add License SQL 3.1"
diff --git a/private/net/svcdlls/lls/test/ct/license.txt b/private/net/svcdlls/lls/test/ct/license.txt
new file mode 100644
index 000000000..04ba5362e
--- /dev/null
+++ b/private/net/svcdlls/lls/test/ct/license.txt
@@ -0,0 +1,248 @@
+License Logging Service
+Last Update: 11-21-94 6:30pm
+by: arth
+
+What is this licensing Stuff?
+------------------------------
+
+All back-office applications, and all the server-services will now track
+useage and check for license compliance. There will be two licensing modes:
+
+ 1. Per Seat - A per workstation license for the server services on a
+ network. This basically counts up how many unique users (based on
+ the username) have ever used the service and warns the admin when the
+ license limit has been reached.
+ 2. Concurrent - A maximum simultaneous user limit on a server. I.E. Limit
+ the number of simultaneous sessions that can connect to a service.
+
+Any server-service or back-office application can use either of these
+licensing modes on any particular server as selected by the admin. Therefore
+on the same server SQL could be running in Per Seat mode and RAS could be
+running in Concurrent licensing mode.
+
+File and print services (SMB Server, FPNW Server, Print Service, MAC) will use
+a shared pool of licenses in Concurrent licensing mode. Therefore if
+File and Print licensing limit is set to 100 and 50 people are using the SMB
+server, and 50 people are using the FPNW server then no more users can access
+any of the file and print services.
+
+During installation the setup program will ask what mode of licensing the
+admin is using, and if using Concurrent licensing, what session limit to
+enforce (I.E. how many licenses the admin has bought). The NT setup program
+will do this for file and print services. The back-office applications
+(SQL, SNA, etc..) will handle this license configuration in their individual
+setup programs.
+
+What must I do?
+---------------
+
+First you will need the header and libs from NT build 856 or later to get the
+latest license API and LsaLogonUser changes.
+
+If you have your own installation/setup program then you need to put up
+a dialog to check for the mode of licensing and session limit. Thomaspa's
+group is working on a DLL and common dialog for this. This information is
+written out to the registry.
+
+In the service itself there are basically three different options depending
+on what your needs are:
+
+ 1. Let LsaLogonUser handle licensing for you.
+ 2. Directly call the License API's and have it handle all licensing for you.
+ 3. Handle Concurrent based licensing yourself and call the License API's
+ for Per Seat licensing.
+
+1. If you call LsaLogonUser to validate connections then it is fairly easy.
+First you need to call LSALogonUser with LSA_CALL_LICENSE_SERVER
+(from sdk\inc\ntlsa.h) or'd into the AuthenticationParameter. This tells
+LsaLogonUser to call the license service, the default is to not call the
+Licensing service.
+
+With this bit set, LsaLogonUser will call the license API for you, and the
+License API will determine what mode of licensing should be used and act
+appropriatly.
+
+You must also check the return from LsaLogonUser for the new return
+STATUS_LICENSE_QUOTA_EXCEEDED. This means the license limit was exceeded
+and you should not let the user on.
+
+ Note: When the token received from LsaLogonUser is freed the License
+ service will free the session. LsaLogonUser already returns a token and
+ expects the code to free it, so no changes should be required in your code
+ for this.
+
+2. If you call the Licensing API's directly the changes are also fairly easy.
+There are two API's NtLicenseRequest and NtLicenseFree (from sdk\inc\ntlsapi.h)
+Call NtLicenseRequest when a session is established and NtLicenseFree when a
+session is terminated.
+
+This method is used by the SMB server because at the time that it makes the
+call to LsaLogonUser the service doesn't know if the connection is for
+file i/o or pipe i/o, and pipe i/o isn't supposed to be licensed.
+
+The Licensing API will determine what type of licensing mode should be used
+and act accordingly without any special code in the service.
+
+3. Doing it this way means you handle the Concurrent licensing yourself.
+You must read from the registry the licensing mode and if in Concurrent
+licensing mode you must track the session limits and reject users. You
+should also have a background thread that periodically checks the registry
+to see if the licensing info has changed. Only call the Licensing API's
+if you are in Per Seat licensing mode.
+
+SQL does it this way as currently SQL tracks session connection and tear
+down in a way that is difficult to integrate with the licensing API, and it
+is easier for them to do it themselves.
+
+Note: If your service is one of the "file and print services" then you *MUST*
+use one of the other options and let the License API handle Concurrent
+Licensing. This allows the License API code to limit the file and print
+services using a shared pool of licenses.
+
+I need to call the Licensing API directly, how do I do this?
+------------------------------------------------------------
+
+First, let me (arth) know that you need to call the API directly so if there
+are any changes then I can let you know about them.
+
+The header file is in %NT_ROOT%\public\sdk\inc\ntlsapi.h
+The DLL is in %NT_ROOT%\public\sdk\lib\*\ntlsapi.dll
+
+The only two function calls you care about are:
+
+LS_STATUS_CODE LS_API_ENTRY NtLicenseRequest(
+ LPSTR ProductName,
+ LPSTR Version,
+ LS_HANDLE FAR *LicenseHandle,
+ NT_LS_DATA *NtData);
+
+
+LS_STATUS_CODE LS_API_ENTRY NtLSFreeHandle(
+ LS_HANDLE LicenseHandle );
+
+There are currently only three return codes you need to worry about:
+
+ LS_SUCCESS - Everything worked
+ LS_INSUFFICIENT_UNITS - The license limit was exceeded (reject user)
+ LS_RESOURCES_UNAVAILABLE - Out of memory
+
+The NtData field is used to pass in the username or SID and is defined as:
+
+typedef struct {
+ ULONG DataType; // Type of the following data, ie. user name, sid...
+ VOID *Data; // Actual data. username, sid, etc...
+ // if call the unicode API character data
+ // must be in unicode as well
+ BOOL IsAdmin;
+} NT_LS_DATA;
+
+Set DataType to NT_LS_USER_NAME (defined in ntlsapi.h) if the Data field will
+contain a username, and NT_LS_USER_SID if it will contain a SID.
+Note: Please pass in a username if possible as it is quicker.
+
+IsAdmin is a bool used to tell if the user is an Admin. This allows an admin
+to still get a connection if the session limit is reached. If you don't want
+or care if an admin is allowed to connect when the session limit is reached,
+then just always pass in FALSE for this field.
+
+A typical call would therefore look something like:
+
+{
+ NT_LS_DATA LsData;
+ LS_STATUS_CODE err;
+ LS_HANDLE LicenseHandle;
+
+ ...
+
+ LsData.DataType = NT_LS_USER_NAME;
+ LsData.Data = (VOID *) MyUserNameField; // wherever you keep the username
+ LsData.IsAdmin = FALSE; // or whatever appropriate in your case.
+
+ err = NtLicenseRequest("Microsoft SQL Server", // Your service name
+ "4.0", // Version of product
+ &LicenseHandle,
+ &LsData);
+
+ switch (err) {
+ case LS_SUCCESS:
+ // Go ahead and do what you want
+ break;
+
+ case LS_INSUFFICIENT_UNITS:
+ // Disallow user connection
+ break;
+
+ case LS_RESOURCES_UNAVAILABLE:
+ // License Service got an out of memory so report error as approp.
+ break;
+ }
+
+
+ ...
+
+ NtLSFreeHandle(LicenseHandle);
+
+ ...
+
+}
+
+ANSI and Unicode endpoints are both provided. The Data field values should
+be in ANSI if calling the ANSI API's and Unicode if calling the Unicode API's.
+
+My server is in kernel mode, how do I call these API's?
+-------------------------------------------------------
+
+All kernel mode servers should have a user-mode service (at least when I
+checked before they all did). You need to thunk up to your user-mode
+service and then have it make the API call.
+
+If this causes a big problem then let me know. The License API DLL will need
+to make an LPC call to the License Service so when this is coded you could
+just make the LPC call directly, but sticking with using the DLL makes it
+easier to change things in the future.
+
+
+My server is connectionless, and using the Licensing API's isn't feasible.
+--------------------------------------------------------------------------
+
+Send me (arth) mail, these have to be handled on a case by case basis. Some
+of the internet servers have this problem and the current plan is to only call
+the licensing API's for authenticated connections.
+
+I'm handling Concurrent licensing Directly, what is the registry format?
+------------------------------------------------------------------------
+
+The following is the format that server apps should use for setting and
+checking the mode they operate in:
+
+ Key = \HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\LicenseInfo
+ Value= ErrorControl : REG_DWORD : 0x1
+ Value= Start : REG_DWORD : 0x3
+ Value= Type : REG_DWORD : 0x4
+
+Subkeys :
+
+ \SNA
+ \SQL
+ \FilePrint
+
+Value for All Subkeys=
+ Mode : REG_DWORD : (0x0 = Per Seat Mode, 0x1 = Concurrent/Per Server Mode)
+ ConcurrentLimit : REG_DWORD : (0x<limit>,
+ ie. 0x100 = 256 concurrent user limit)
+ FlipAllow : REG_DWORD : (0x0 = can change license mode, 0x1 license mode
+ can't be changed. Server apps are only allowed to
+ switch their license mode once, so after the first
+ switch, this value would be set to non-zero, then
+ the UI will warn about further changes to the
+ licence mode.)
+
+Issue: Server/service apps should poll the value every hour or so in case of
+change. Otherwise, if more licenses are added, or the mode changes, is it
+acceptable to require the server apps to be stopped, and restarted?
+
+
+What is the timeline for this stuff?
+------------------------------------
+
+These changes need to be in the servers by the PPC release.
diff --git a/private/net/svcdlls/lls/test/ct/lls.doc b/private/net/svcdlls/lls/test/ct/lls.doc
new file mode 100644
index 000000000..8ddad2ee0
--- /dev/null
+++ b/private/net/svcdlls/lls/test/ct/lls.doc
Binary files differ
diff --git a/private/net/svcdlls/lls/test/ct/manyp.bat b/private/net/svcdlls/lls/test/ct/manyp.bat
new file mode 100644
index 000000000..b23a3e206
--- /dev/null
+++ b/private/net/svcdlls/lls/test/ct/manyp.bat
@@ -0,0 +1,76 @@
+@echo off
+llscmd add user manyp prod1 4.0
+llscmd add user manyp prod1 3.0
+llscmd add user manyp prod1 1.0
+llscmd add user manyp prod1 6.0
+llscmd add user manyp prod1 8.0
+llscmd add user manyp prod1 9.0
+llscmd add user manyp prod1 2.0
+llscmd add user manyp prod1 5.0
+llscmd add user manyp prod1 7.0
+llscmd add user manyp prod1 4.2
+llscmd add user manyp prod1 3.3
+llscmd add user manyp prod1 1.5
+llscmd add user manyp prod1 6.1
+llscmd add user manyp prod1 8.7
+llscmd add user manyp prod1 9.1
+llscmd add user manyp prod1 2.7
+llscmd add user manyp prod1 5.3
+llscmd add user manyp prod1 7.1
+
+llscmd add user manyp prodx 4.0
+llscmd add user manyp prodx 3.0
+llscmd add user manyp prodx 1.0
+llscmd add user manyp prodx 6.0
+llscmd add user manyp prodx 8.0
+llscmd add user manyp prodx 9.0
+llscmd add user manyp prodx 2.0
+llscmd add user manyp prodx 5.0
+llscmd add user manyp prodx 7.0
+llscmd add user manyp prodx 4.2
+llscmd add user manyp prodx 3.3
+llscmd add user manyp prodx 1.5
+llscmd add user manyp prodx 6.1
+llscmd add user manyp prodx 8.7
+llscmd add user manyp prodx 9.1
+llscmd add user manyp prodx 2.7
+llscmd add user manyp prodx 5.3
+llscmd add user manyp prodx 7.1
+
+llscmd add user manyp proda 4.0
+llscmd add user manyp proda 3.0
+llscmd add user manyp proda 1.0
+llscmd add user manyp proda 6.0
+llscmd add user manyp proda 8.0
+llscmd add user manyp proda 9.0
+llscmd add user manyp proda 2.0
+llscmd add user manyp proda 5.0
+llscmd add user manyp proda 7.0
+llscmd add user manyp proda 4.2
+llscmd add user manyp proda 3.3
+llscmd add user manyp proda 1.5
+llscmd add user manyp proda 6.1
+llscmd add user manyp proda 8.7
+llscmd add user manyp proda 9.1
+llscmd add user manyp proda 2.7
+llscmd add user manyp proda 5.3
+llscmd add user manyp proda 7.1
+
+llscmd add user manyp prodv 4.0
+llscmd add user manyp prodd 3.0
+llscmd add user manyp prodw 1.0
+llscmd add user manyp prod3 6.0
+llscmd add user manyp prod7 8.0
+llscmd add user manyp prodh 9.0
+llscmd add user manyp prodj 2.0
+llscmd add user manyp prodk 5.0
+llscmd add user manyp prodl 7.0
+llscmd add user manyp prods 4.2
+llscmd add user manyp prod2 3.3
+llscmd add user manyp prod4 1.5
+llscmd add user manyp prodv 6.1
+llscmd add user manyp prodf 8.7
+llscmd add user manyp prody 9.1
+llscmd add user manyp produ 2.7
+llscmd add user manyp prod8 5.3
+llscmd add user manyp prod9 7.1
diff --git a/private/net/svcdlls/lls/test/ct/map1.bat b/private/net/svcdlls/lls/test/ct/map1.bat
new file mode 100644
index 000000000..d6dd4c551
--- /dev/null
+++ b/private/net/svcdlls/lls/test/ct/map1.bat
@@ -0,0 +1,95 @@
+llscmd rpc llsmappingadd BigMap1 1 "Map - BigMap1"
+llscmd rpc llsmappingadd BigMap2 2 "Map - BigMap2"
+llscmd rpc llsmappingadd BigMap3 3 "Map - BigMap3"
+llscmd rpc llsmappingadd BigMap4 4 "Map - BigMap4"
+llscmd rpc llsmappingadd BigMap5 5 "Map - BigMap5"
+llscmd rpc llsmappingadd BigMap6 6 "Map - BigMap6"
+llscmd rpc llsmappingadd BigMap7 7 "Map - BigMap7"
+llscmd rpc llsmappingadd BigMap8 8 "Map - BigMap8"
+llscmd rpc llsmappingadd BigMap9 9 "Map - BigMap9"
+llscmd rpc llsmappingadd BigMap10 10 "Map - BigMap10"
+llscmd rpc llsmappingadd BigMap11 11 "Map - BigMap11"
+llscmd rpc llsmappingadd BigMap12 12 "Map - BigMap12"
+llscmd rpc llsmappingadd BigMap13 13 "Map - BigMap13"
+llscmd rpc llsmappingadd BigMap14 14 "Map - BigMap14"
+llscmd rpc llsmappingadd BigMap15 15 "Map - BigMap15"
+llscmd rpc llsmappingadd BigMap16 16 "Map - BigMap16"
+llscmd rpc llsmappingadd BigMap17 17 "Map - BigMap17"
+llscmd rpc llsmappingadd BigMap18 18 "Map - BigMap18"
+llscmd rpc llsmappingadd BigMap19 19 "Map - BigMap19"
+llscmd rpc llsmappingadd BigMap20 20 "Map - BigMap20"
+llscmd rpc llsmappingadd BigMap21 21 "Map - BigMap21"
+llscmd rpc llsmappingadd BigMap22 22 "Map - BigMap22"
+llscmd rpc llsmappingadd BigMap23 23 "Map - BigMap23"
+llscmd rpc llsmappingadd BigMap24 24 "Map - BigMap24"
+llscmd rpc llsmappingadd BigMap25 25 "Map - BigMap25"
+llscmd rpc llsmappingadd BigMap26 26 "Map - BigMap26"
+llscmd rpc llsmappingadd BigMap27 27 "Map - BigMap27"
+llscmd rpc llsmappingadd BigMap28 28 "Map - BigMap28"
+llscmd rpc llsmappingadd BigMap29 29 "Map - BigMap29"
+llscmd rpc llsmappingadd BigMap30 30 "Map - BigMap30"
+llscmd rpc llsmappingadd BigMap31 31 "Map - BigMap31"
+llscmd rpc llsmappingadd BigMap32 32 "Map - BigMap32"
+llscmd rpc llsmappingadd BigMap33 33 "Map - BigMap33"
+llscmd rpc llsmappingadd BigMap34 34 "Map - BigMap34"
+llscmd rpc llsmappingadd BigMap35 35 "Map - BigMap35"
+llscmd rpc llsmappingadd BigMap36 36 "Map - BigMap36"
+llscmd rpc llsmappingadd BigMap37 37 "Map - BigMap37"
+llscmd rpc llsmappingadd BigMap38 38 "Map - BigMap38"
+llscmd rpc llsmappingadd BigMap39 39 "Map - BigMap39"
+llscmd rpc llsmappingadd BigMap40 40 "Map - BigMap40"
+llscmd rpc llsmappingadd BigMap41 41 "Map - BigMap41"
+llscmd rpc llsmappingadd BigMap42 42 "Map - BigMap42"
+llscmd rpc llsmappingadd BigMap43 43 "Map - BigMap43"
+llscmd rpc llsmappingadd BigMap44 44 "Map - BigMap44"
+llscmd rpc llsmappingadd BigMap45 45 "Map - BigMap45"
+llscmd rpc llsmappingadd BigMap46 46 "Map - BigMap46"
+llscmd rpc llsmappingadd BigMap47 47 "Map - BigMap47"
+llscmd rpc llsmappingadd BigMap48 48 "Map - BigMap48"
+llscmd rpc llsmappingadd BigMap49 49 "Map - BigMap49"
+llscmd rpc llsmappingadd BigMap50 50 "Map - BigMap50"
+llscmd rpc llsmappingadd BigMap51 51 "Map - BigMap51"
+llscmd rpc llsmappingadd BigMap52 52 "Map - BigMap52"
+llscmd rpc llsmappingadd BigMap53 53 "Map - BigMap53"
+llscmd rpc llsmappingadd BigMap54 54 "Map - BigMap54"
+llscmd rpc llsmappingadd BigMap55 55 "Map - BigMap55"
+llscmd rpc llsmappingadd BigMap56 56 "Map - BigMap56"
+llscmd rpc llsmappingadd BigMap57 57 "Map - BigMap57"
+llscmd rpc llsmappingadd BigMap58 58 "Map - BigMap58"
+llscmd rpc llsmappingadd BigMap59 59 "Map - BigMap59"
+llscmd rpc llsmappingadd BigMap60 60 "Map - BigMap60"
+llscmd rpc llsmappingadd BigMap61 61 "Map - BigMap61"
+llscmd rpc llsmappingadd BigMap62 62 "Map - BigMap62"
+llscmd rpc llsmappingadd BigMap63 63 "Map - BigMap63"
+llscmd rpc llsmappingadd BigMap64 64 "Map - BigMap64"
+llscmd rpc llsmappingadd BigMap65 65 "Map - BigMap65"
+llscmd rpc llsmappingadd BigMap66 66 "Map - BigMap66"
+llscmd rpc llsmappingadd BigMap67 67 "Map - BigMap67"
+llscmd rpc llsmappingadd BigMap68 68 "Map - BigMap68"
+llscmd rpc llsmappingadd BigMap69 69 "Map - BigMap69"
+llscmd rpc llsmappingadd BigMap70 70 "Map - BigMap70"
+llscmd rpc llsmappingadd BigMap71 71 "Map - BigMap71"
+llscmd rpc llsmappingadd BigMap72 72 "Map - BigMap72"
+llscmd rpc llsmappingadd BigMap73 73 "Map - BigMap73"
+llscmd rpc llsmappingadd BigMap74 74 "Map - BigMap74"
+llscmd rpc llsmappingadd BigMap75 75 "Map - BigMap75"
+llscmd rpc llsmappingadd BigMap76 76 "Map - BigMap76"
+llscmd rpc llsmappingadd BigMap77 77 "Map - BigMap77"
+llscmd rpc llsmappingadd BigMap78 78 "Map - BigMap78"
+llscmd rpc llsmappingadd BigMap79 79 "Map - BigMap79"
+llscmd rpc llsmappingadd BigMap80 80 "Map - BigMap80"
+llscmd rpc llsmappingadd BigMap81 81 "Map - BigMap81"
+llscmd rpc llsmappingadd BigMap82 82 "Map - BigMap82"
+llscmd rpc llsmappingadd BigMap83 83 "Map - BigMap83"
+llscmd rpc llsmappingadd BigMap84 84 "Map - BigMap84"
+llscmd rpc llsmappingadd BigMap85 85 "Map - BigMap85"
+llscmd rpc llsmappingadd BigMap86 86 "Map - BigMap86"
+llscmd rpc llsmappingadd BigMap87 87 "Map - BigMap87"
+llscmd rpc llsmappingadd BigMap88 88 "Map - BigMap88"
+llscmd rpc llsmappingadd BigMap89 89 "Map - BigMap89"
+llscmd rpc llsmappingadd BigMap90 90 "Map - BigMap90"
+llscmd rpc llsmappingadd BigMap91 91 "Map - BigMap91"
+llscmd rpc llsmappingadd BigMap92 92 "Map - BigMap92"
+llscmd rpc llsmappingadd BigMap93 93 "Map - BigMap93"
+llscmd rpc llsmappingadd BigMap94 94 "Map - BigMap94"
+llscmd rpc llsmappingadd BigMap95 95 "Map - BigMap95"
diff --git a/private/net/svcdlls/lls/test/ct/mp.bat b/private/net/svcdlls/lls/test/ct/mp.bat
new file mode 100644
index 000000000..6cb912cf7
--- /dev/null
+++ b/private/net/svcdlls/lls/test/ct/mp.bat
@@ -0,0 +1,14 @@
+:loop
+start llscmd add file n.dat 0 %1 %2 2
+start llscmd add file n1.dat 0 %1 %2 2
+start llscmd add file n2.dat 0 %1 %2 2
+start llscmd add file n3.dat 0 %1 %2 2
+start llscmd add file n4.dat 0 %1 %2 2
+start llscmd add file n5.dat 0 %1 %2 2
+start llscmd add file n6.dat 0 %1 %2 2
+start llscmd add file n7.dat 0 %1 %2 2
+start llscmd add file n8.dat 0 %1 %2 2
+start llscmd add file n9.dat 0 %1 %2 2
+start llscmd add file n10.dat 0 %1 %2 2
+sleep 900
+goto loop
diff --git a/private/net/svcdlls/lls/test/ct/n.dat b/private/net/svcdlls/lls/test/ct/n.dat
new file mode 100644
index 000000000..1b1e5a0f1
--- /dev/null
+++ b/private/net/svcdlls/lls/test/ct/n.dat
@@ -0,0 +1,1689 @@
+h-ouse
+-ronaar
+r-rja
+n-txfa
+nin-aa
+cab-ram
+joh-nac
+martad
+acca
+rrea
+markad
+acma
+byrona
+yosha
+julioa
+ccaa
+bertag
+ibmma
+acm-ah
+nor-iya
+hakana
+lisac
+johnal
+melisa
+boba
+rayall
+deba
+je'ffal
+rallen
+l-aural
+lallyn
+venesa
+an-naal
+nancya
+din-oa
+bain
+marioa
+r-aquea
+vitora
+s-cotam
+renam
+e'glea
+audrea
+chrisa
+ntxha
+curta
+davand
+dav'ean
+ntxga
+gavana
+ac'ja
+j_od
+attma
+naomia
+r_oy-and
+sallya
+toman
+m-a'tsan
+jamiea
+r--ickan
+jana
+je'-ffan
+jiman
+bevaok
+takaya
+y'-'-umia
+ca-rloa
+takaar
+shinar
+yukaa
+tonyar
+hiroar
+suza
+kenar
+eadarm
+loria
+nt-xra
+richar
+carnst
+arnulw
+mayuma
+hidas
+richat
+idoa
+ntxbat
+markat
+kevana
+gillea
+micha
+joset
+ntxba
+gma
+saorib
+vikkib
+garyba
+suz-ieb
+michba
+danbag
+frankb
+larryb
+sbaird
+rrkeb
+jolynb
+paulb
+sbaker
+valb
+leeb
+geoba
+dougba
+ctpsb
+hughb
+galb
+leonib
+patba
+ntxjb
+rrjba
+donbar
+jacbar
+roberb
+larrb
+allpak
+mosesb
+donnab
+cibelb
+vspb
+peteba
+abates
+marcb
+pbcnz
+jbattl
+jimbau
+cbaum
+danbau
+bilbax
+kathbe
+ntx'-ab
+celinb
+karenb
+philb
+darryb
+ctpdb
+scre9
+ccsb
+ste'pbe
+ccjben
+leneb
+ntxnb
+pbenoi
+vscb
+micheb
+rrjimb
+chadb
+kar-ib
+nat-hb
+dou-gbe
+kimmob
+amyb
+decdb
+ebenb
+acabe
+andb
+ntxsbe
+michbe
+geralb
+juditb
+savlob
+dsiwm
+willbi
+ellenb
+joseb
+bla-sb
+thomb
+annicb
+marbin
+ntxmb
+daveb
+dirkb
+suebi
+stefb
+patbi
+hilmib
+heleb
+rrc-b
+rrc-b
+rrc-b
+rrc-b
+rrc-b
+rrc-b
+rrc-b
+rrc-b
+cblack
+richbl
+brynb
+alanb
+hyattb
+donnbl
+cblasi
+sbliss
+janebl
+fransb
+inface
+lucibo
+pboehm
+joyb
+pboffo
+rrc-b
+johnbo
+airex
+bobb
+kierab
+laurbo
+danbol
+cathab
+kathbo
+cathb
+coreyb
+garyb
+dadcmb
+nicolb
+ismb
+davidb
+tomb
+ntxsb
+timbo
+ronbo
+benjab
+toddb
+cheryb
+vskb
+shellb
+timboy
+bboyle
+jboyle
+kaijab
+rrc-b
+myriab
+mbrand
+brianb
+darreb
+simonb
+geneb
+tracyb
+lisbd
+ibmdb
+pbrenn
+sineab
+susb
+bryanb
+ebrew
+donbr
+noelb
+elizbr
+galeb
+davb
+billbr
+bribro
+sophib
+henryb
+philbr
+donb
+amybr
+cbrown
+dianab
+acjb
+kirstb
+lesb
+rbrown
+ntxrbr
+albru
+sbrug
+ludob
+vssb
+paulbr
+ccrb
+michbu
+larrbu
+lisab
+carolb
+peteb
+actb
+klausb
+telen
+sbuhr
+louisb
+miltb
+heathb
+helmb
+bburke
+catbu
+billb
+dburn
+kaeb
+ronb
+eileeb
+brettb
+laurab
+ccfb
+gunteb
+gregb
+robbu
+robut
+acwb
+faribu
+noelby
+ntxpbw
+eddac
+oscarc
+scre12
+anneca
+jocelc
+barbca
+janinc
+jasonc
+michca
+edc
+pedroc
+suzc
+wandac
+davc
+carenc
+joanc
+leeca
+markca
+pcamp
+julioc
+mikec
+lucisc
+georgc
+tomasb
+antonc
+stevec
+jimc
+dencar
+tomc
+ntxpc
+annsc
+jcarne
+nanc
+patcar
+loric
+patca
+davic
+kimca
+acmc
+wilmac
+maurc
+kcart
+gregco
+ginnyc
+iainc
+philc
+riccar
+steca
+ntxsc
+louisc
+gianca
+annec
+jjcav
+kencav
+ntxgc
+jeffca
+palmac
+vsmc
+forms
+ntxfc
+cchad
+cchali
+troyc
+cecilc
+carmc
+jasch
+sophic
+abbotc
+inac
+lchang
+nicolc
+vchang
+clintc
+billch
+kench
+lojc
+ntxac
+johnch
+sorlet
+kimc
+weiyc
+rrycc
+ritac
+pierrc
+tomch
+henryc
+willch
+rrkc
+ntxsch
+daich
+yukoc
+abm
+acgc
+ntxjch
+rrtcp
+jojoc
+krisch
+ronch
+javch
+ntxrac
+sebasc
+andrwc
+gkkc
+yvonnc
+kingsc
+bibich
+ntxkch
+philci
+ccipol
+paulc
+tomci
+waltci
+andyc
+bencl
+dclark
+ntxjcl
+johcl
+kclark
+vspc
+rrsc
+derekc
+johncl
+ntxdc
+ibmrc
+shimaa
+silkea
+davida
+chrial
+eallen
+susana
+joseal
+stefam
+paulam
+mettea
+stinev
+henrik
+ralpha
+robann
+csilla
+erica
+marcoa
+malia
+ericas
+alexat
+maubry
+anab
+alexib
+nicolb
+urib
+shantb
+mikeba
+andbar
+beckyb
+scottb
+leonib
+mariab
+reemb
+maddib
+marieb
+romab
+ericba
+brianb
+josefb
+chadb
+ronb
+almub
+tinab
+monikb
+joelb
+yannb
+tarunb
+stephb
+veronb
+stefab
+bernb
+dougbl
+boccom
+laurib
+johanb
+erinb
+sinbo
+michab
+markbo
+julb
+kamilb
+laurb
+colinb
+taniab
+pbrad
+jonbr
+edb
+annieb
+jmbrie
+lisab
+joakib
+kbrost
+aaronb
+paulbr
+seambr
+elainb
+kaib
+alainb
+courtb
+richbu
+liambu
+ferbu
+hanneb
+felicb
+seanb
+maytec
+nelsc
+armanc
+nicoc
+camcar
+juanjc
+tiziac
+marcoc
+tonyc
+jpcast
+damiac
+ignac
+sabinc
+gordc
+jaruc
+cbchae
+ammonc
+jaksch
+stephc
+jimc
+normac
+fredc
+richch
+cheric
+ericc
+dmchoi
+gabc
+plchua
+josefc
+crisci
+mikecl
+coracl
+danco
+antonc
+maxinc
+karenc
+cecelw
+alberc
+claudc
+ajc
+valerc
+maxicu
+brenc
+wolfz
+maxic
+peteda
+jacqd
+robd
+bobk
+hiltom
+katjad
+marnod
+serged
+antond
+michde
+freddv
+hansd
+tdiver
+ylvad
+emerd
+chelod
+julied
+jdrage
+markdr
+chridu
+yvand
+delphd
+leed
+michd
+betind
+stephv
+georgd
+owenea
+stepea
+owene
+kime
+rone
+helene
+annete
+marcee
+gabreh
+nathfa
+helenf
+masudf
+jeruf
+brunof
+marif
+debbif
+jonf
+joachf
+cathf
+pennyf
+kimbef
+andref
+billf
+damfin
+solfo
+jacquf
+pacof
+antonf
+tomfr
+bjornf
+mickeg
+werng
+mariag
+julieg
+georgg
+roxang
+beng
+mardcg
+rafg
+jimeg
+maiog
+laurga
+giagat
+mattga
+laurg
+bgenar
+fredge
+lidiag
+stefag
+michag
+minah
+mgietl
+robg
+juligm
+stephg
+dannyg
+davidg
+xavg
+sophig
+danieg
+cgold
+merisg
+osnatg
+josem
+diman
+fredgo
+erendg
+luisgo
+rosag
+grantg
+andygo
+sgort
+jeanpg
+geneg
+scottg
+chiarg
+richgr
+bruceg
+adamg
+avishg
+sandg
+sophg
+andreg
+ugrob
+sissyg
+guenth
+jhaas
+erezh
+mariha
+tommyh
+ahaigh
+svenh
+jackih
+johnha
+perh
+katreh
+gerhar
+charlh
+sanjh
+robhau
+tomokh
+erich
+brenhe
+andreh
+markuh
+raphh
+bernh
+brendh
+olafh
+klaush
+enidh
+ayolh
+jorgeh
+bobhe
+adrih
+loreth
+juanjh
+stefah
+gerdh
+kathah
+maxh
+micheh
+juergh
+scotth
+torhol
+toddh
+szabh
+martah
+shood
+jillh
+shannh
+clarah
+liborh
+jhosty
+chanth
+amyhs
+helenh
+chhsu
+franh
+janyh
+tracyh
+dusth
+davhug
+bradh
+moyah
+jormah
+mustai
+kevini
+leei
+junkoi
+jamest
+karenj
+joej
+davidj
+haylj
+rosej
+gertj
+lanaj
+hollyj
+jcjan
+isek
+alexka
+giselk
+mihok
+annemk
+peteke
+lizak
+hyeyk
+hskim
+bking
+timk
+cko
+fannyk
+davek
+rolank
+craigk
+martik
+kotay
+fredk
+carolk
+georgk
+chrisk
+geirk
+juliak
+andrk
+mank
+kerstk
+tckuo
+tomokk
+skwan
+tony
+markul
+johnla
+drewl
+darlan
+kenl
+oritl
+benjla
+klau
+markl
+mlaur
+delphl
+pyledu
+evinl
+dlee
+hjlee
+hklee
+ivyl
+jonsul
+kwlee
+sjlee
+wglee
+ivole
+peterl
+steple
+nikil
+serenl
+glinah
+lsliau
+hannal
+rafael
+angell
+roblim
+merjal
+billli
+avihal
+birdlo
+sergel
+alberl
+helenl
+carlol
+josel
+juanl
+annel
+lilyl
+gerryl
+vicenl
+nathl
+edlf
+editl
+derekl
+saml
+alphal
+kirbyl
+luisl
+dickl
+torull
+cristm
+mmaerk
+leonm
+sergm
+brendm
+andrwm
+tinym
+magdem
+franma
+kaym
+nichm
+caswem
+arnam
+rickm
+carlam
+helenm
+farish
+majda
+sarya
+monaa
+ahmada
+tawfad
+dach
+krissa
+krisa
+paula
+aleena
+debraa
+bja
+radams
+stepha
+josepa
+colbya
+allana
+shola
+dawna
+wahaba
+conraa
+paulal
+ralexa
+ricka
+willal
+beckya
+darrea
+bkena
+saraha
+trical
+callis
+bga
+anasal
+emada
+marya
+kamila
+marca
+amyan
+ericaa
+janeta
+tinaa
+garant
+franka
+joea
+davapp
+denisa
+stevar
+briana
+mikea
+randya
+rafar
+noela
+michas
+davea
+kellas
+petash
+alexa
+paulat
+teresa
+daveat
+heidia
+mikeau
+amya
+cloa
+akilaz
+aprib
+tricib
+bruceb
+chrisb
+ryanba
+tomb
+tohoub
+pbain
+bwb
+gayleb
+branb
+sharib
+johnb
+dbanks
+rayb
+cbarb
+waldb
+larrba
+jeremb
+mimid
+garyba
+bpb
+hughb
+kenba
+markba
+alib
+koyb
+patb
+dbaur
+jerib
+alexb
+brubax
+gregbe
+laurib
+annb
+aarob
+johnbe
+karbec
+davb
+dbeers
+bmbe
+teresb
+paulbe
+aaronb
+griogb
+ericb
+tracbe
+janeb
+bradb
+leslib
+tinab
+dougbe
+jonib
+joelbe
+sharb
+dougb
+andreb
+kberg
+ryanb
+cynthb
+dberry
+cherbe
+bmarcb
+tamib
+gileb
+patbe
+mikebi
+bryab
+kathb
+jamebi
+kenb
+billbi
+jennib
+danbl
+jasonb
+philib
+alisbl
+amybl
+camib
+alexbl
+patbl
+carlab
+aprilb
+marcbl
+brandb
+kristb
+jbolic
+garybo
+ellenb
+eribo
+courtb
+coletb
+larryb
+marcb
+johnbo
+ronb
+kimbb
+launab
+willbo
+ericbo
+cboyd
+gboyd
+dianab
+glennb
+debb
+kimb
+cnamb
+rbrand
+jimbr
+jeffbr
+ritab
+ednakb
+lisab
+fumiko
+josepb
+janicb
+davib
+joanb
+jacb
+johnbr
+cynb
+dbrown
+djbrow
+jbrown
+lesb
+scottb
+sbrown
+btb
+lynnb
+maryb
+lisabr
+rogb
+bbrunn
+rubenb
+virgbr
+normb
+jeffb
+connib
+balb
+robinb
+garybu
+khoib
+janb
+johnbu
+peterb
+dewbur
+aileeb
+stevbu
+patbur
+lorrib
+sanhb
+kaarib
+sbutch
+bab
+johbu
+bsb
+sandyb
+anitab
+donbyr
+briac
+dcain
+deanc
+bobca
+davec
+lancec
+craigc
+jocan
+scotca
+phylc
+acarav
+chrisc
+bnc
+johnca
+jasonc
+johca
+richc
+robcar
+therca
+kerryc
+karenc
+franc
+chrica
+davic
+paulc
+cindyc
+tiffc
+richca
+sheilc
+markca
+santhc
+amyc
+sethc
+mikec
+robc
+jamiec
+salc
+micch
+cchai
+lizch
+karlc
+bbc
+alexch
+kchang
+kirsch
+rogc
+cync
+gregch
+marcic
+kchase
+avac
+samch
+denche
+bjch
+allenc
+andyc
+robtch
+dchiu
+cchov
+mchow
+waic
+charch
+melinc
+melodc
+deechr
+ninac
+jchung
+tinc
+yongc
+robch
+charcl
+gregc
+arletc
+rosecl
+carolc
+sherrc
+brianc
+gcluff
+philc
+ccobb
+bvc
+stevco
+jaschc
+chadc
+darrec
+bkc
+thomc
+bjc
+billc
+kevct
+yannac
+royco
+jcomb
+dianec
+rosac
+dcook
+davidc
+tcook
+gregco
+chasc
+jeffco
+lcope
+cnadc
+liscor
+charc
+stevc
+kandcw
+peteco
+lisaco
+scottc
+chipc
+brc
+chrcox
+gretc
+willc
+bwc
+danc
+russc
+joaoac
+royc
+doylec
+barbcr
+floydc
+timc
+angelc
+kenc
+charlc
+dcurle
+robync
+ronc
+davcu
+btc
+maziad
+suzied
+kdahl
+pauld
+gregmd
+ldalto
+tuand
+harda
+tomd
+almond
+debdas
+jand
+hendav
+tomda
+robdav
+bodied
+dand
+jeffda
+kated
+samd
+miked
+raqdm
+danld
+stevd
+marisd
+sylvad
+cyndid
+nicod
+normd
+francd
+gennd
+joed
+cdemai
+nicold
+andyd
+stuard
+russd
+candye
+kathd
+kathrd
+heathd
+jeffd
+johndi
+seandi
+jdirks
+markdi
+briand
+michd
+timdo
+carodo
+stand
+amyd
+josed
+pauldo
+windd
+carld
+teresd
+johndo
+davidd
+irinad
+rondru
+audied
+sanjad
+darona
+maiko
+yumiak
+cecila
+reneea
+sandia
+ulrika
+chara
+harai
+tomoka
+eduarv
+chucka
+normaa
+miaa
+kersta
+annaba
+stefa
+adaya
+paala
+laylaa
+kirstb
+naokib
+scotba
+erikb
+bevb
+garyb
+annb
+pederb
+brendb
+joeb
+shellb
+karlb
+ubazik
+donnab
+armanb
+paulib
+yukob
+larsb
+lourdb
+robbi
+chribi
+ainslb
+cblair
+ernstb
+maribo
+thomb
+nickb
+sboyd
+johnbr
+daveb
+cbrown
+stuarb
+alineb
+maxb
+kevinb
+kathyc
+maggic
+geovac
+tinac
+marutc
+israc
+rayc
+eunhyo
+sycho
+amitc
+adriac
+shellc
+angeco
+lilic
+elisac
+ccrane
+samc
+ferguc
+barbc
+madiec
+tracec
+yukikd
+karend
+rolfdp
+larryd
+fredde
+cmed
+jand
+andred
+lised
+anad
+chardo
+patd
+denduf
+barryd
+orland
+jamied
+ryane
+susane
+keithe
+mohame
+briane
+linge
+done
+michfe
+mirnaf
+trevfi
+ericf
+sioflo
+lisaf
+sterlf
+stevef
+peggyf
+mfrey
+derekf
+geralf
+mariyf
+naokof
+tamakf
+miwaf
+yumikf
+keikof
+shawng
+marthg
+olivig
+lisaga
+jeffga
+mongm
+xavige
+roseg
+sandyg
+larsg
+paulgo
+camilg
+tomomg
+dongr
+andreg
+paulg
+sgray
+johng
+timg
+sgreen
+rgregg
+peerg
+markh
+lisah
+zachh
+gregh
+mikeha
+robh
+ryanh
+arih
+satomh
+jennha
+akirah
+rossh
+taeh
+kyokoh
+erich
+carinh
+earlh
+fumikh
+collh
+yoshih
+ysho
+davhof
+jkhong
+eriho
+goh
+jeanh
+path
+satoi
+isaoi
+akikoi
+levi
+fuyuki
+yukoi
+michii
+noriki
+etsuki
+nozomi
+hiroi
+yukai
+balraj
+barbj
+travj
+endoj
+johanj
+emilyj
+gregj
+chrisj
+susanj
+kaisak
+ayumik
+hisakk
+megk
+minnak
+yokok
+kkato
+yukok
+denrec
+clionk
+donkel
+masayk
+jennik
+alik
+ikuek
+jeanhe
+minjuk
+yskim
+klausk
+masaek
+emikok
+saorik
+akemik
+bobk
+tkubo
+hansk
+yasuek
+bjl
+alexal
+randyl
+stephl
+markl
+waynel
+jennl
+johnle
+michel
+markli
+waltl
+hazell
+cathlo
+gyorgl
+jodyl
+bgl
+elvil
+maglo
+candyl
+kathlo
+luisfl
+sergl
+mariol
+aidal
+kempl
+ewaldl
+ruthl
+patm
+joem
+chrim
+hegem
+youkom
+keikom
+mayum
+nobum
+angiem
+nannm
+melism
+errolm
+jerrym
+charlm
+mitchm
+stasim
+peterm
+chrime
+misahm
+tomokm
+ericmo
+hermm
+patmo
+angelm
+robmu
+madhum
+davmul
+sharym
+sachn
+chiekn
+hirokn
+sachin
+naozn
+hyojn
+basiln
+miran
+hegen
+michn
+michin
+maarn
+bayarn
+jonnor
+monikn
+mobara
+kaoruo
+kyokoo
+masato
+hideko
+takako
+mihook
+tadaso
+sollis
+jaceko
+juano
+monipa
+manpal
+parkky
+stevpa
+kamp
+magdap
+sperez
+scotpe
+curtp
+brianp
+kerstp
+cynthp
+lynnp
+annikp
+adriar
+laurar
+peterr
+alexmr
+oliver
+larsr
+hrawet
+isabcr
+kater
+warrob
+mariar
+marilr
+sandrr
+ellear
+annr
+btro
+drush
+akikos
+kazuks
+mihos
+mikasa
+norsak
+mikes
+stevsa
+juansc
+satoes
+erikas
+keikos
+emikos
+noriks
+yasuns
+jims
+tsukas
+shiges
+birsch
+robsc
+harals
+marls
+olafs
+birgis
+mseki
+masaes
+kaorus
+ramis
+bjjs
+shizus
+yoshis
+vics
+karens
+jessmi
+richso
+monsot
+terrys
+susast
+hugos
+etsuks
+davsum
+krists
+masaks
+terues
+yumis
+aleksz
+yukot
+nortak
+keikot
+johnt
+yumikt
+timeat
+bct
+stevet
+pamt
+shinot
+junet
+urmilt
+szilvt
+michit
+germat
+keikto
+glent
+marktr
+juliet
+shimat
+ttsuji
+yukikt
+melt
+jturn
+chieu
+akikou
+megumu
+naokou
+juandu
+diegov
+veronv
+mariov
+mariav
+billw
+helenw
+lorriw
+kanakw
+coreyw
+carlaw
+ronw
+aliciw
+scottw
+royw
+vanwh
+evaw
+bodilw
+arnew
+mikewi
+robwim
diff --git a/private/net/svcdlls/lls/test/ct/n1.dat b/private/net/svcdlls/lls/test/ct/n1.dat
new file mode 100644
index 000000000..2ac6c1d55
--- /dev/null
+++ b/private/net/svcdlls/lls/test/ct/n1.dat
@@ -0,0 +1,1000 @@
+house1
+ronaar1
+rrja1
+ntxfa1
+ninaa1
+cabram1
+johnac1
+martad1
+acca1
+rrea1
+markad1
+acma1
+byrona1
+yosha1
+julioa1
+ccaa1
+bertag1
+ibmma1
+acmah1
+noriya1
+hakana1
+lisac1
+johnal1
+melisa1
+boba1
+rayall1
+deba1
+jeffal1
+rallen1
+laural1
+lallyn1
+venesa1
+annaal1
+nancya1
+dinoa1
+bain1
+marioa1
+raquea1
+vitora1
+scotam1
+renam1
+eglea1
+audrea1
+chrisa1
+ntxha1
+curta1
+davand1
+davean1
+ntxga1
+gavana1
+acja1
+jod1
+attma1
+naomia1
+royand1
+sallya1
+toman1
+matsan1
+jamiea1
+rickan1
+jana1
+jeffan1
+jiman1
+bevaok1
+takaya1
+yumia1
+carloa1
+takaar1
+shinar1
+yukaa1
+tonyar1
+hiroar1
+suza1
+kenar1
+eadarm1
+loria1
+ntxra1
+richar1
+carnst1
+arnulw1
+mayuma1
+hidas1
+richat1
+idoa1
+ntxbat1
+markat1
+kevana1
+gillea1
+micha1
+joset1
+ntxba1
+gma1
+saorib1
+vikkib1
+garyba1
+suzieb1
+michba1
+danbag1
+frankb1
+larryb1
+sbaird1
+rrkeb1
+jolynb1
+paulb1
+sbaker1
+valb1
+leeb1
+geoba1
+dougba1
+ctpsb1
+hughb1
+galb1
+leonib1
+patba1
+ntxjb1
+rrjba1
+donbar1
+jacbar1
+roberb1
+larrb1
+allpak1
+mosesb1
+donnab1
+cibelb1
+vspb1
+peteba1
+abates1
+marcb1
+pbcnz1
+jbattl1
+jimbau1
+cbaum1
+danbau1
+bilbax1
+kathbe1
+ntxab1
+celinb1
+karenb1
+philb1
+darryb1
+ctpdb1
+scre91
+ccsb1
+stepbe1
+ccjben1
+leneb1
+ntxnb1
+pbenoi1
+vscb1
+micheb1
+rrjimb1
+chadb1
+karib1
+nathb1
+dougbe1
+kimmob1
+amyb1
+decdb1
+ebenb1
+acabe1
+andb1
+ntxsbe1
+michbe1
+geralb1
+juditb1
+savlob1
+dsiwm1
+willbi1
+ellenb1
+joseb1
+blasb1
+thomb1
+annicb1
+marbin1
+ntxmb1
+daveb1
+dirkb1
+suebi1
+stefb1
+patbi1
+hilmib1
+heleb1
+rrcb1
+cblack1
+richbl1
+brynb1
+alanb1
+hyattb1
+donnbl1
+cblasi1
+sbliss1
+janebl1
+fransb1
+inface1
+lucibo1
+pboehm1
+joyb1
+pboffo1
+johnbo1
+airex1
+bobb1
+kierab1
+laurbo1
+danbol1
+cathab1
+kathbo1
+cathb1
+coreyb1
+garyb1
+dadcmb1
+nicolb1
+ismb1
+davidb1
+tomb1
+ntxsb1
+timbo1
+ronbo1
+benjab1
+toddb1
+cheryb1
+vskb1
+shellb1
+timboy1
+bboyle1
+jboyle1
+kaijab1
+myriab1
+mbrand1
+brianb1
+darreb1
+simonb1
+geneb1
+tracyb1
+lisbd1
+ibmdb1
+pbrenn1
+sineab1
+susb1
+bryanb1
+ebrew1
+donbr1
+noelb1
+elizbr1
+galeb1
+davb1
+billbr1
+bribro1
+sophib1
+henryb1
+philbr1
+donb1
+amybr1
+cbrown1
+dianab1
+acjb1
+kirstb1
+lesb1
+rbrown1
+ntxrbr1
+albru1
+sbrug1
+ludob1
+vssb1
+paulbr1
+ccrb1
+michbu1
+larrbu1
+lisab1
+carolb1
+peteb1
+actb1
+klausb1
+telen1
+sbuhr1
+louisb1
+miltb1
+heathb1
+helmb1
+bburke1
+catbu1
+billb1
+dburn1
+kaeb1
+ronb1
+eileeb1
+brettb1
+laurab1
+ccfb1
+gunteb1
+gregb1
+robbu1
+robut1
+acwb1
+faribu1
+noelby1
+ntxpbw1
+eddac1
+oscarc1
+scre121
+anneca1
+jocelc1
+barbca1
+janinc1
+jasonc1
+michca1
+edc1
+pedroc1
+suzc1
+wandac1
+davc1
+carenc1
+joanc1
+leeca1
+markca1
+pcamp1
+julioc1
+mikec1
+lucisc1
+georgc1
+tomasb1
+antonc1
+stevec1
+jimc1
+dencar1
+tomc1
+ntxpc1
+annsc1
+jcarne1
+nanc1
+patcar1
+loric1
+patca1
+davic1
+kimca1
+acmc1
+wilmac1
+maurc1
+kcart1
+gregco1
+ginnyc1
+iainc1
+philc1
+riccar1
+steca1
+ntxsc1
+louisc1
+gianca1
+annec1
+jjcav1
+kencav1
+ntxgc1
+jeffca1
+palmac1
+vsmc1
+forms1
+ntxfc1
+cchad1
+cchali1
+troyc1
+cecilc1
+carmc1
+jasch1
+sophic1
+abbotc1
+inac1
+lchang1
+nicolc1
+vchang1
+clintc1
+billch1
+kench1
+lojc1
+ntxac1
+johnch1
+sorlet1
+kimc1
+weiyc1
+rrycc1
+ritac1
+pierrc1
+tomch1
+henryc1
+willch1
+rrkc1
+ntxsch1
+daich1
+yukoc1
+abm1
+acgc1
+ntxjch1
+rrtcp1
+jojoc1
+krisch1
+ronch1
+javch1
+ntxrac1
+sebasc1
+andrwc1
+gkkc1
+yvonnc1
+kingsc1
+bibich1
+ntxkch1
+philci1
+ccipol1
+paulc1
+tomci1
+waltci1
+andyc1
+bencl1
+dclark1
+ntxjcl1
+johcl1
+kclark1
+vspc1
+rrsc1
+derekc1
+johncl1
+ntxdc1
+ibmrc1
+shimaa1
+silkea1
+davida1
+chrial1
+eallen1
+susana1
+joseal1
+stefam1
+paulam1
+mettea1
+stinev1
+henrik1
+ralpha1
+robann1
+csilla1
+erica1
+marcoa1
+malia1
+ericas1
+alexat1
+maubry1
+anab1
+alexib1
+nicolb1
+urib1
+shantb1
+mikeba1
+andbar1
+beckyb1
+scottb1
+leonib1
+mariab1
+reemb1
+maddib1
+marieb1
+romab1
+ericba1
+brianb1
+josefb1
+chadb1
+ronb1
+almub1
+tinab1
+monikb1
+joelb1
+yannb1
+tarunb1
+stephb1
+veronb1
+stefab1
+bernb1
+dougbl1
+boccom1
+laurib1
+johanb1
+erinb1
+sinbo1
+michab1
+markbo1
+julb1
+kamilb1
+laurb1
+colinb1
+taniab1
+pbrad1
+jonbr1
+edb1
+annieb1
+jmbrie1
+lisab1
+joakib1
+kbrost1
+aaronb1
+paulbr1
+seambr1
+elainb1
+kaib1
+alainb1
+courtb1
+richbu1
+liambu1
+ferbu1
+hanneb1
+felicb1
+seanb1
+maytec1
+nelsc1
+armanc1
+nicoc1
+camcar1
+juanjc1
+tiziac1
+marcoc1
+tonyc1
+jpcast1
+damiac1
+ignac1
+sabinc1
+gordc1
+jaruc1
+cbchae1
+ammonc1
+jaksch1
+stephc1
+jimc1
+normac1
+fredc1
+richch1
+cheric1
+ericc1
+dmchoi1
+gabc1
+plchua1
+josefc1
+crisci1
+mikecl1
+coracl1
+danco1
+antonc1
+maxinc1
+karenc1
+cecelw1
+alberc1
+claudc1
+ajc1
+valerc1
+maxicu1
+brenc1
+wolfz1
+maxic1
+peteda1
+jacqd1
+robd1
+bobk1
+hiltom1
+katjad1
+marnod1
+serged1
+antond1
+michde1
+freddv1
+hansd1
+tdiver1
+ylvad1
+emerd1
+chelod1
+julied1
+jdrage1
+markdr1
+chridu1
+yvand1
+delphd1
+leed1
+michd1
+betind1
+stephv1
+georgd1
+owenea1
+stepea1
+owene1
+kime1
+rone1
+helene1
+annete1
+marcee1
+gabreh1
+nathfa1
+helenf1
+masudf1
+jeruf1
+brunof1
+marif1
+debbif1
+jonf1
+joachf1
+cathf1
+pennyf1
+kimbef1
+andref1
+billf1
+damfin1
+solfo1
+jacquf1
+pacof1
+antonf1
+tomfr1
+bjornf1
+mickeg1
+werng1
+mariag1
+julieg1
+georgg1
+roxang1
+beng1
+mardcg1
+rafg1
+jimeg1
+maiog1
+laurga1
+giagat1
+mattga1
+laurg1
+bgenar1
+fredge1
+lidiag1
+stefag1
+michag1
+minah1
+mgietl1
+robg1
+juligm1
+stephg1
+dannyg1
+davidg1
+xavg1
+sophig1
+danieg1
+cgold1
+merisg1
+osnatg1
+josem1
+diman1
+fredgo1
+erendg1
+luisgo1
+rosag1
+grantg1
+andygo1
+sgort1
+jeanpg1
+geneg1
+scottg1
+chiarg1
+richgr1
+bruceg1
+adamg1
+avishg1
+sandg1
+sophg1
+andreg1
+ugrob1
+sissyg1
+guenth1
+jhaas1
+erezh1
+mariha1
+tommyh1
+ahaigh1
+svenh1
+jackih1
+johnha1
+perh1
+katreh1
+gerhar1
+charlh1
+sanjh1
+robhau1
+tomokh1
+erich1
+brenhe1
+andreh1
+markuh1
+raphh1
+bernh1
+brendh1
+olafh1
+klaush1
+enidh1
+ayolh1
+jorgeh1
+bobhe1
+adrih1
+loreth1
+juanjh1
+stefah1
+gerdh1
+kathah1
+maxh1
+micheh1
+juergh1
+scotth1
+torhol1
+toddh1
+szabh1
+martah1
+shood1
+jillh1
+shannh1
+clarah1
+liborh1
+jhosty1
+chanth1
+amyhs1
+helenh1
+chhsu1
+franh1
+janyh1
+tracyh1
+dusth1
+davhug1
+bradh1
+moyah1
+jormah1
+mustai1
+kevini1
+leei1
+junkoi1
+jamest1
+karenj1
+joej1
+davidj1
+haylj1
+rosej1
+gertj1
+lanaj1
+hollyj1
+jcjan1
+isek1
+alexka1
+giselk1
+mihok1
+annemk1
+peteke1
+lizak1
+hyeyk1
+hskim1
+bking1
+timk1
+cko1
+fannyk1
+davek1
+rolank1
+craigk1
+martik1
+kotay1
+fredk1
+carolk1
+georgk1
+chrisk
+geirk1
+juliak1
+andrk1
+mank1
+kerstk1
+tckuo1
+tomokk1
+skwan1
+tony1
+markul1
+johnla1
+drewl1
+darlan1
+kenl1
+oritl1
+benjla1
+klau1
+markl1
+mlaur1
+delph1
+pyledu1
+evinl1
+dlee1
+hjlee1
+hklee1
+ivyl1
+jonsul1
+kwlee1
+sjlee1
+wglee1
+ivole1
+peterl1
+steple1
+nikil1
+serenl1
+glinah1
+lsliau1
+hannal1
+rafael1
+angell1
+roblim1
+merjal1
+billli1
+avihal1
+birdlo1
+sergel1
+alberl1
+helenl1
+carlol1
+josel1
+juanl1
+annel1
+lilyl1
+gerryl1
+vicenl1
+nathl1
+edlf1
+editl1
+derekl1
+saml1
+alphal1
+kirbyl1
+luisl1
+dickl1
+torull1
+cristm1
+mmaerk1
+leonm1
+sergm1
+brendm1
+andrwm1
+tinym1
+magdem1
+franma1
+kaym1
+nichm1
+caswem1
+arnam1
+rickm1
+carlam1
+helenm1
+farish1
+majda1
+sarya1
+monaa1
+ahmada1
+tawfad1
+dach1
+krissa1
+krisa1
+paula1
+aleena1
+debraa1
+bja1
+radams1
+stepha1
+josepa1
+colbya1
+allana1
+shola1
+dawna1
+wahaba1
+conraa1
+paulal1
+ralexa1
+ricka1
+willal1
+beckya1
+darrea1
+bkena1
+saraha1
+trical1
+callis1
+bga1
+anasal1
+emada1
+marya1
+kamila1
+marca1
+amyan1
+ericaa1
+janeta1
+tinaa1
+garant1
+franka1
+joea1
+davapp1
+denisa1
+stevar1
+briana1
+mikea1
+randya1
+rafar1
+noela1
+michas1
+davea1
+kellas1
+petash1
+alexa1
+paulat1
+teresa1
+daveat1
+heidia1
+mikeau1
+amya1
+cloa1
+akilaz1
+aprib1
+tricib1
+bruceb1
+chrisb1
+ryanba1
+tomb1
+tohoub1
+pbain1
+bwb1
+gayleb1
+branb1
+sharib1
+johnb1
+dbanks1
+rayb1
+cbarb1
+waldb1
+larrba1
+jeremb1
+mimid1
+garyba1
+bpb1
+hughb1
+kenba1
+markba1
+alib1
+koyb1
+patb1
+dbaur1
+jerib1
+alexb1
+brubax1
+gregbe1
+laurib1
+annb1
+aarob1
+johnbe1
+karbec1
+davb1
+dbeers1
+bmbe1
+teresb1
+paulbe1
+aaronb1
+griogb1
+ericb1
+tracbe1
+janeb1
+bradb1
+leslib1
+tinab1
+dougbe1
+jonib1
+joelbe1
+sharb1
+dougb1
+andreb1
+kberg1
+ryanb1
+cynthb1
+dberry1
+cherbe1
+bmarcb1
+tamib1
+gileb1
+patbe1
+mikebi1
+bryab1
+kathb1
+jamebi1
+kenb1
+billbi1
+jennib1
+danbl1
+jasonb1
+philib1
+alisbl1
+amybl1
+camib1
+alexbl1
+patbl1
+carlab1
+aprilb1
+marcbl1
+brandb1
+kristb1
+jbolic1
+garybo1
+ellenb1
+eribo1
+courtb1
+coletb1
+larryb1
+marcb1
diff --git a/private/net/svcdlls/lls/test/ct/n10.dat b/private/net/svcdlls/lls/test/ct/n10.dat
new file mode 100644
index 000000000..0ef1bcec9
--- /dev/null
+++ b/private/net/svcdlls/lls/test/ct/n10.dat
@@ -0,0 +1,1000 @@
+house10
+ronaar10
+rrja10
+ntxfa10
+ninaa10
+cabram10
+johnac10
+martad10
+acca10
+rrea10
+markad10
+acma10
+byrona10
+yosha10
+julioa10
+ccaa10
+bertag10
+ibmma10
+acmah10
+noriya10
+hakana10
+lisac10
+johnal10
+melisa10
+boba10
+rayall10
+deba10
+jeffal10
+rallen10
+laural10
+lallyn10
+venesa10
+annaal10
+nancya10
+dinoa10
+bain10
+marioa10
+raquea10
+vitora10
+scotam10
+renam10
+eglea10
+audrea10
+chrisa10
+ntxha10
+curta10
+davand10
+davean10
+ntxga10
+gavana10
+acja10
+jod10
+attma10
+naomia10
+royand10
+sallya10
+toman10
+matsan10
+jamiea10
+rickan10
+jana10
+jeffan10
+jiman10
+bevaok10
+takaya10
+yumia10
+carloa10
+takaar10
+shinar10
+yukaa10
+tonyar10
+hiroar10
+suza10
+kenar10
+eadarm10
+loria10
+ntxra10
+richar10
+carnst10
+arnulw10
+mayuma10
+hidas10
+richat10
+idoa10
+ntxbat10
+markat10
+kevana10
+gillea10
+micha10
+joset10
+ntxba10
+gma10
+saorib10
+vikkib10
+garyba10
+suzieb10
+michba10
+danbag10
+frankb10
+larryb10
+sbaird10
+rrkeb10
+jolynb10
+paulb10
+sbaker10
+valb10
+leeb10
+geoba10
+dougba10
+ctpsb10
+hughb10
+galb10
+leonib10
+patba10
+ntxjb10
+rrjba10
+donbar10
+jacbar10
+roberb10
+larrb10
+allpak10
+mosesb10
+donnab10
+cibelb10
+vspb10
+peteba10
+abates10
+marcb10
+pbcnz10
+jbattl10
+jimbau10
+cbaum10
+danbau10
+bilbax10
+kathbe10
+ntxab10
+celinb10
+karenb10
+philb10
+darryb10
+ctpdb10
+scre910
+ccsb10
+stepbe10
+ccjben10
+leneb10
+ntxnb10
+pbenoi10
+vscb10
+micheb10
+rrjimb10
+chadb10
+karib10
+nathb10
+dougbe10
+kimmob10
+amyb10
+decdb10
+ebenb10
+acabe10
+andb10
+ntxsbe10
+michbe10
+geralb10
+juditb10
+savlob10
+dsiwm10
+willbi10
+ellenb10
+joseb10
+blasb10
+thomb10
+annicb10
+marbin10
+ntxmb10
+daveb10
+dirkb10
+suebi10
+stefb10
+patbi10
+hilmib10
+heleb10
+rrcb10
+cblack10
+richbl10
+brynb10
+alanb10
+hyattb10
+donnbl10
+cblasi10
+sbliss10
+janebl10
+fransb10
+inface10
+lucibo10
+pboehm10
+joyb10
+pboffo10
+johnbo10
+airex10
+bobb10
+kierab10
+laurbo10
+danbol10
+cathab10
+kathbo10
+cathb10
+coreyb10
+garyb10
+dadcmb10
+nicolb10
+ismb10
+davidb10
+tomb10
+ntxsb10
+timbo10
+ronbo10
+benjab10
+toddb10
+cheryb10
+vskb10
+shellb10
+timboy10
+bboyle10
+jboyle10
+kaijab10
+myriab10
+mbrand10
+brianb10
+darreb10
+simonb10
+geneb10
+tracyb10
+lisbd10
+ibmdb10
+pbrenn10
+sineab10
+susb10
+bryanb10
+ebrew10
+donbr10
+noelb10
+elizbr10
+galeb10
+davb10
+billbr10
+bribro10
+sophib10
+henryb10
+philbr10
+donb10
+amybr10
+cbrown10
+dianab10
+acjb10
+kirstb10
+lesb10
+rbrown10
+ntxrbr10
+albru10
+sbrug10
+ludob10
+vssb10
+paulbr10
+ccrb10
+michbu10
+larrbu10
+lisab10
+carolb10
+peteb10
+actb10
+klausb10
+telen10
+sbuhr10
+louisb10
+miltb10
+heathb10
+helmb10
+bburke10
+catbu10
+billb10
+dburn10
+kaeb10
+ronb10
+eileeb10
+brettb10
+laurab10
+ccfb10
+gunteb10
+gregb10
+robbu10
+robut10
+acwb10
+faribu10
+noelby10
+ntxpbw10
+eddac10
+oscarc10
+scre10210
+anneca10
+jocelc10
+barbca10
+janinc10
+jasonc10
+michca10
+edc10
+pedroc10
+suzc10
+wandac10
+davc10
+carenc10
+joanc10
+leeca10
+markca10
+pcamp10
+julioc10
+mikec10
+lucisc10
+georgc10
+tomasb10
+antonc10
+stevec10
+jimc10
+dencar10
+tomc10
+ntxpc10
+annsc10
+jcarne10
+nanc10
+patcar10
+loric10
+patca10
+davic10
+kimca10
+acmc10
+wilmac10
+maurc10
+kcart10
+gregco10
+ginnyc10
+iainc10
+philc10
+riccar10
+steca10
+ntxsc10
+louisc10
+gianca10
+annec10
+jjcav10
+kencav10
+ntxgc10
+jeffca10
+palmac10
+vsmc10
+forms10
+ntxfc10
+cchad10
+cchali10
+troyc10
+cecilc10
+carmc10
+jasch10
+sophic10
+abbotc10
+inac10
+lchang10
+nicolc10
+vchang10
+clintc10
+billch10
+kench10
+lojc10
+ntxac10
+johnch10
+sorlet10
+kimc10
+weiyc10
+rrycc10
+ritac10
+pierrc10
+tomch10
+henryc10
+willch10
+rrkc10
+ntxsch10
+daich10
+yukoc10
+abm10
+acgc10
+ntxjch10
+rrtcp10
+jojoc10
+krisch10
+ronch10
+javch10
+ntxrac10
+sebasc10
+andrwc10
+gkkc10
+yvonnc10
+kingsc10
+bibich10
+ntxkch10
+philci10
+ccipol10
+paulc10
+tomci10
+waltci10
+andyc10
+bencl10
+dclark10
+ntxjcl10
+johcl10
+kclark10
+vspc10
+rrsc10
+derekc10
+johncl10
+ntxdc10
+ibmrc10
+shimaa10
+silkea10
+davida10
+chrial10
+eallen10
+susana10
+joseal10
+stefam10
+paulam10
+mettea10
+stinev10
+henrik10
+ralpha10
+robann10
+csilla10
+erica10
+marcoa10
+malia10
+ericas10
+alexat10
+maubry10
+anab10
+alexib10
+nicolb10
+urib10
+shantb10
+mikeba10
+andbar10
+beckyb10
+scottb10
+leonib10
+mariab10
+reemb10
+maddib10
+marieb10
+romab10
+ericba10
+brianb10
+josefb10
+chadb10
+ronb10
+almub10
+tinab10
+monikb10
+joelb10
+yannb10
+tarunb10
+stephb10
+veronb10
+stefab10
+bernb10
+dougbl10
+boccom10
+laurib10
+johanb10
+erinb10
+sinbo10
+michab10
+markbo10
+julb10
+kamilb10
+laurb10
+colinb10
+taniab10
+pbrad10
+jonbr10
+edb10
+annieb10
+jmbrie10
+lisab10
+joakib10
+kbrost10
+aaronb10
+paulbr10
+seambr10
+elainb10
+kaib10
+alainb10
+courtb10
+richbu10
+liambu10
+ferbu10
+hanneb10
+felicb10
+seanb10
+maytec10
+nelsc10
+armanc10
+nicoc10
+camcar10
+juanjc10
+tiziac10
+marcoc10
+tonyc10
+jpcast10
+damiac10
+ignac10
+sabinc10
+gordc10
+jaruc10
+cbchae10
+ammonc10
+jaksch10
+stephc10
+jimc10
+normac10
+fredc10
+richch10
+cheric10
+ericc10
+dmchoi10
+gabc10
+plchua10
+josefc10
+crisci10
+mikecl10
+coracl10
+danco10
+antonc10
+maxinc10
+karenc10
+cecelw10
+alberc10
+claudc10
+ajc10
+valerc10
+maxicu10
+brenc10
+wolfz10
+maxic10
+peteda10
+jacqd10
+robd10
+bobk10
+hiltom10
+katjad10
+marnod10
+serged10
+antond10
+michde10
+freddv10
+hansd10
+tdiver10
+ylvad10
+emerd10
+chelod10
+julied10
+jdrage10
+markdr10
+chridu10
+yvand10
+delphd10
+leed10
+michd10
+betind10
+stephv10
+georgd10
+owenea10
+stepea10
+owene10
+kime10
+rone10
+helene10
+annete10
+marcee10
+gabreh10
+nathfa10
+helenf10
+masudf10
+jeruf10
+brunof10
+marif10
+debbif10
+jonf10
+joachf10
+cathf10
+pennyf10
+kimbef10
+andref10
+billf10
+damfin10
+solfo10
+jacquf10
+pacof10
+antonf10
+tomfr10
+bjornf10
+mickeg10
+werng10
+mariag10
+julieg10
+georgg10
+roxang10
+beng10
+mardcg10
+rafg10
+jimeg10
+maiog10
+laurga10
+giagat10
+mattga10
+laurg10
+bgenar10
+fredge10
+lidiag10
+stefag10
+michag10
+minah10
+mgietl10
+robg10
+juligm10
+stephg10
+dannyg10
+davidg10
+xavg10
+sophig10
+danieg10
+cgold10
+merisg10
+osnatg10
+josem10
+diman10
+fredgo10
+erendg10
+luisgo10
+rosag10
+grantg10
+andygo10
+sgort10
+jeanpg10
+geneg10
+scottg10
+chiarg10
+richgr10
+bruceg10
+adamg10
+avishg10
+sandg10
+sophg10
+andreg10
+ugrob10
+sissyg10
+guenth10
+jhaas10
+erezh10
+mariha10
+tommyh10
+ahaigh10
+svenh10
+jackih10
+johnha10
+perh10
+katreh10
+gerhar10
+charlh10
+sanjh10
+robhau10
+tomokh10
+erich10
+brenhe10
+andreh10
+markuh10
+raphh10
+bernh10
+brendh10
+olafh10
+klaush10
+enidh10
+ayolh10
+jorgeh10
+bobhe10
+adrih10
+loreth10
+juanjh10
+stefah10
+gerdh10
+kathah10
+maxh10
+micheh10
+juergh10
+scotth10
+torhol10
+toddh10
+szabh10
+martah10
+shood10
+jillh10
+shannh10
+clarah10
+liborh10
+jhosty10
+chanth10
+amyhs10
+helenh10
+chhsu10
+franh10
+janyh10
+tracyh10
+dusth10
+davhug10
+bradh10
+moyah10
+jormah10
+mustai10
+kevini10
+leei10
+junkoi10
+jamest10
+karenj10
+joej10
+davidj10
+haylj10
+rosej10
+gertj10
+lanaj10
+hollyj10
+jcjan10
+isek10
+alexka10
+giselk10
+mihok10
+annemk10
+peteke10
+lizak10
+hyeyk10
+hskim10
+bking10
+timk10
+cko10
+fannyk10
+davek10
+rolank10
+craigk10
+martik10
+kotay10
+fredk10
+carolk10
+georgk10
+chrisk
+geirk10
+juliak10
+andrk10
+mank10
+kerstk10
+tckuo10
+tomokk10
+skwan10
+tony10
+markul10
+johnla10
+drewl10
+darlan10
+kenl10
+oritl10
+benjla10
+klau10
+markl10
+mlaur10
+delph10
+pyledu10
+evinl10
+dlee10
+hjlee10
+hklee10
+ivyl10
+jonsul10
+kwlee10
+sjlee10
+wglee10
+ivole10
+peterl10
+steple10
+nikil10
+serenl10
+glinah10
+lsliau10
+hannal10
+rafael10
+angell10
+roblim10
+merjal10
+billli10
+avihal10
+birdlo10
+sergel10
+alberl10
+helenl10
+carlol10
+josel10
+juanl10
+annel10
+lilyl10
+gerryl10
+vicenl10
+nathl10
+edlf10
+editl10
+derekl10
+saml10
+alphal10
+kirbyl10
+luisl10
+dickl10
+torull10
+cristm10
+mmaerk10
+leonm10
+sergm10
+brendm10
+andrwm10
+tinym10
+magdem10
+franma10
+kaym10
+nichm10
+caswem10
+arnam10
+rickm10
+carlam10
+helenm10
+farish10
+majda10
+sarya10
+monaa10
+ahmada10
+tawfad10
+dach10
+krissa10
+krisa10
+paula10
+aleena10
+debraa10
+bja10
+radams10
+stepha10
+josepa10
+colbya10
+allana10
+shola10
+dawna10
+wahaba10
+conraa10
+paulal10
+ralexa10
+ricka10
+willal10
+beckya10
+darrea10
+bkena10
+saraha10
+trical10
+callis10
+bga10
+anasal10
+emada10
+marya10
+kamila10
+marca10
+amyan10
+ericaa10
+janeta10
+tinaa10
+garant10
+franka10
+joea10
+davapp10
+denisa10
+stevar10
+briana10
+mikea10
+randya10
+rafar10
+noela10
+michas10
+davea10
+kellas10
+petash10
+alexa10
+paulat10
+teresa10
+daveat10
+heidia10
+mikeau10
+amya10
+cloa10
+akilaz10
+aprib10
+tricib10
+bruceb10
+chrisb10
+ryanba10
+tomb10
+tohoub10
+pbain10
+bwb10
+gayleb10
+branb10
+sharib10
+johnb10
+dbanks10
+rayb10
+cbarb10
+waldb10
+larrba10
+jeremb10
+mimid10
+garyba10
+bpb10
+hughb10
+kenba10
+markba10
+alib10
+koyb10
+patb10
+dbaur10
+jerib10
+alexb10
+brubax10
+gregbe10
+laurib10
+annb10
+aarob10
+johnbe10
+karbec10
+davb10
+dbeers10
+bmbe10
+teresb10
+paulbe10
+aaronb10
+griogb10
+ericb10
+tracbe10
+janeb10
+bradb10
+leslib10
+tinab10
+dougbe10
+jonib10
+joelbe10
+sharb10
+dougb10
+andreb10
+kberg10
+ryanb10
+cynthb10
+dberry10
+cherbe10
+bmarcb10
+tamib10
+gileb10
+patbe10
+mikebi10
+bryab10
+kathb10
+jamebi10
+kenb10
+billbi10
+jennib10
+danbl10
+jasonb10
+philib10
+alisbl10
+amybl10
+camib10
+alexbl10
+patbl10
+carlab10
+aprilb10
+marcbl10
+brandb10
+kristb10
+jbolic10
+garybo10
+ellenb10
+eribo10
+courtb10
+coletb10
+larryb10
+marcb10
diff --git a/private/net/svcdlls/lls/test/ct/n2.dat b/private/net/svcdlls/lls/test/ct/n2.dat
new file mode 100644
index 000000000..bd0b3f8d2
--- /dev/null
+++ b/private/net/svcdlls/lls/test/ct/n2.dat
@@ -0,0 +1,1000 @@
+house2
+ronaar2
+rrja2
+ntxfa2
+ninaa2
+cabram2
+johnac2
+martad2
+acca2
+rrea2
+markad2
+acma2
+byrona2
+yosha2
+julioa2
+ccaa2
+bertag2
+ibmma2
+acmah2
+noriya2
+hakana2
+lisac2
+johnal2
+melisa2
+boba2
+rayall2
+deba2
+jeffal2
+rallen2
+laural2
+lallyn2
+venesa2
+annaal2
+nancya2
+dinoa2
+bain2
+marioa2
+raquea2
+vitora2
+scotam2
+renam2
+eglea2
+audrea2
+chrisa2
+ntxha2
+curta2
+davand2
+davean2
+ntxga2
+gavana2
+acja2
+jod2
+attma2
+naomia2
+royand2
+sallya2
+toman2
+matsan2
+jamiea2
+rickan2
+jana2
+jeffan2
+jiman2
+bevaok2
+takaya2
+yumia2
+carloa2
+takaar2
+shinar2
+yukaa2
+tonyar2
+hiroar2
+suza2
+kenar2
+eadarm2
+loria2
+ntxra2
+richar2
+carnst2
+arnulw2
+mayuma2
+hidas2
+richat2
+idoa2
+ntxbat2
+markat2
+kevana2
+gillea2
+micha2
+joset2
+ntxba2
+gma2
+saorib2
+vikkib2
+garyba2
+suzieb2
+michba2
+danbag2
+frankb2
+larryb2
+sbaird2
+rrkeb2
+jolynb2
+paulb2
+sbaker2
+valb2
+leeb2
+geoba2
+dougba2
+ctpsb2
+hughb2
+galb2
+leonib2
+patba2
+ntxjb2
+rrjba2
+donbar2
+jacbar2
+roberb2
+larrb2
+allpak2
+mosesb2
+donnab2
+cibelb2
+vspb2
+peteba2
+abates2
+marcb2
+pbcnz2
+jbattl2
+jimbau2
+cbaum2
+danbau2
+bilbax2
+kathbe2
+ntxab2
+celinb2
+karenb2
+philb2
+darryb2
+ctpdb2
+scre92
+ccsb2
+stepbe2
+ccjben2
+leneb2
+ntxnb2
+pbenoi2
+vscb2
+micheb2
+rrjimb2
+chadb2
+karib2
+nathb2
+dougbe2
+kimmob2
+amyb2
+decdb2
+ebenb2
+acabe2
+andb2
+ntxsbe2
+michbe2
+geralb2
+juditb2
+savlob2
+dsiwm2
+willbi2
+ellenb2
+joseb2
+blasb2
+thomb2
+annicb2
+marbin2
+ntxmb2
+daveb2
+dirkb2
+suebi2
+stefb2
+patbi2
+hilmib2
+heleb2
+rrcb2
+cblack2
+richbl2
+brynb2
+alanb2
+hyattb2
+donnbl2
+cblasi2
+sbliss2
+janebl2
+fransb2
+inface2
+lucibo2
+pboehm2
+joyb2
+pboffo2
+johnbo2
+airex2
+bobb2
+kierab2
+laurbo2
+danbol2
+cathab2
+kathbo2
+cathb2
+coreyb2
+garyb2
+dadcmb2
+nicolb2
+ismb2
+davidb2
+tomb2
+ntxsb2
+timbo2
+ronbo2
+benjab2
+toddb2
+cheryb2
+vskb2
+shellb2
+timboy2
+bboyle2
+jboyle2
+kaijab2
+myriab2
+mbrand2
+brianb2
+darreb2
+simonb2
+geneb2
+tracyb2
+lisbd2
+ibmdb2
+pbrenn2
+sineab2
+susb2
+bryanb2
+ebrew2
+donbr2
+noelb2
+elizbr2
+galeb2
+davb2
+billbr2
+bribro2
+sophib2
+henryb2
+philbr2
+donb2
+amybr2
+cbrown2
+dianab2
+acjb2
+kirstb2
+lesb2
+rbrown2
+ntxrbr2
+albru2
+sbrug2
+ludob2
+vssb2
+paulbr2
+ccrb2
+michbu2
+larrbu2
+lisab2
+carolb2
+peteb2
+actb2
+klausb2
+telen2
+sbuhr2
+louisb2
+miltb2
+heathb2
+helmb2
+bburke2
+catbu2
+billb2
+dburn2
+kaeb2
+ronb2
+eileeb2
+brettb2
+laurab2
+ccfb2
+gunteb2
+gregb2
+robbu2
+robut2
+acwb2
+faribu2
+noelby2
+ntxpbw2
+eddac2
+oscarc2
+scre222
+anneca2
+jocelc2
+barbca2
+janinc2
+jasonc2
+michca2
+edc2
+pedroc2
+suzc2
+wandac2
+davc2
+carenc2
+joanc2
+leeca2
+markca2
+pcamp2
+julioc2
+mikec2
+lucisc2
+georgc2
+tomasb2
+antonc2
+stevec2
+jimc2
+dencar2
+tomc2
+ntxpc2
+annsc2
+jcarne2
+nanc2
+patcar2
+loric2
+patca2
+davic2
+kimca2
+acmc2
+wilmac2
+maurc2
+kcart2
+gregco2
+ginnyc2
+iainc2
+philc2
+riccar2
+steca2
+ntxsc2
+louisc2
+gianca2
+annec2
+jjcav2
+kencav2
+ntxgc2
+jeffca2
+palmac2
+vsmc2
+forms2
+ntxfc2
+cchad2
+cchali2
+troyc2
+cecilc2
+carmc2
+jasch2
+sophic2
+abbotc2
+inac2
+lchang2
+nicolc2
+vchang2
+clintc2
+billch2
+kench2
+lojc2
+ntxac2
+johnch2
+sorlet2
+kimc2
+weiyc2
+rrycc2
+ritac2
+pierrc2
+tomch2
+henryc2
+willch2
+rrkc2
+ntxsch2
+daich2
+yukoc2
+abm2
+acgc2
+ntxjch2
+rrtcp2
+jojoc2
+krisch2
+ronch2
+javch2
+ntxrac2
+sebasc2
+andrwc2
+gkkc2
+yvonnc2
+kingsc2
+bibich2
+ntxkch2
+philci2
+ccipol2
+paulc2
+tomci2
+waltci2
+andyc2
+bencl2
+dclark2
+ntxjcl2
+johcl2
+kclark2
+vspc2
+rrsc2
+derekc2
+johncl2
+ntxdc2
+ibmrc2
+shimaa2
+silkea2
+davida2
+chrial2
+eallen2
+susana2
+joseal2
+stefam2
+paulam2
+mettea2
+stinev2
+henrik2
+ralpha2
+robann2
+csilla2
+erica2
+marcoa2
+malia2
+ericas2
+alexat2
+maubry2
+anab2
+alexib2
+nicolb2
+urib2
+shantb2
+mikeba2
+andbar2
+beckyb2
+scottb2
+leonib2
+mariab2
+reemb2
+maddib2
+marieb2
+romab2
+ericba2
+brianb2
+josefb2
+chadb2
+ronb2
+almub2
+tinab2
+monikb2
+joelb2
+yannb2
+tarunb2
+stephb2
+veronb2
+stefab2
+bernb2
+dougbl2
+boccom2
+laurib2
+johanb2
+erinb2
+sinbo2
+michab2
+markbo2
+julb2
+kamilb2
+laurb2
+colinb2
+taniab2
+pbrad2
+jonbr2
+edb2
+annieb2
+jmbrie2
+lisab2
+joakib2
+kbrost2
+aaronb2
+paulbr2
+seambr2
+elainb2
+kaib2
+alainb2
+courtb2
+richbu2
+liambu2
+ferbu2
+hanneb2
+felicb2
+seanb2
+maytec2
+nelsc2
+armanc2
+nicoc2
+camcar2
+juanjc2
+tiziac2
+marcoc2
+tonyc2
+jpcast2
+damiac2
+ignac2
+sabinc2
+gordc2
+jaruc2
+cbchae2
+ammonc2
+jaksch2
+stephc2
+jimc2
+normac2
+fredc2
+richch2
+cheric2
+ericc2
+dmchoi2
+gabc2
+plchua2
+josefc2
+crisci2
+mikecl2
+coracl2
+danco2
+antonc2
+maxinc2
+karenc2
+cecelw2
+alberc2
+claudc2
+ajc2
+valerc2
+maxicu2
+brenc2
+wolfz2
+maxic2
+peteda2
+jacqd2
+robd2
+bobk2
+hiltom2
+katjad2
+marnod2
+serged2
+antond2
+michde2
+freddv2
+hansd2
+tdiver2
+ylvad2
+emerd2
+chelod2
+julied2
+jdrage2
+markdr2
+chridu2
+yvand2
+delphd2
+leed2
+michd2
+betind2
+stephv2
+georgd2
+owenea2
+stepea2
+owene2
+kime2
+rone2
+helene2
+annete2
+marcee2
+gabreh2
+nathfa2
+helenf2
+masudf2
+jeruf2
+brunof2
+marif2
+debbif2
+jonf2
+joachf2
+cathf2
+pennyf2
+kimbef2
+andref2
+billf2
+damfin2
+solfo2
+jacquf2
+pacof2
+antonf2
+tomfr2
+bjornf2
+mickeg2
+werng2
+mariag2
+julieg2
+georgg2
+roxang2
+beng2
+mardcg2
+rafg2
+jimeg2
+maiog2
+laurga2
+giagat2
+mattga2
+laurg2
+bgenar2
+fredge2
+lidiag2
+stefag2
+michag2
+minah2
+mgietl2
+robg2
+juligm2
+stephg2
+dannyg2
+davidg2
+xavg2
+sophig2
+danieg2
+cgold2
+merisg2
+osnatg2
+josem2
+diman2
+fredgo2
+erendg2
+luisgo2
+rosag2
+grantg2
+andygo2
+sgort2
+jeanpg2
+geneg2
+scottg2
+chiarg2
+richgr2
+bruceg2
+adamg2
+avishg2
+sandg2
+sophg2
+andreg2
+ugrob2
+sissyg2
+guenth2
+jhaas2
+erezh2
+mariha2
+tommyh2
+ahaigh2
+svenh2
+jackih2
+johnha2
+perh2
+katreh2
+gerhar2
+charlh2
+sanjh2
+robhau2
+tomokh2
+erich2
+brenhe2
+andreh2
+markuh2
+raphh2
+bernh2
+brendh2
+olafh2
+klaush2
+enidh2
+ayolh2
+jorgeh2
+bobhe2
+adrih2
+loreth2
+juanjh2
+stefah2
+gerdh2
+kathah2
+maxh2
+micheh2
+juergh2
+scotth2
+torhol2
+toddh2
+szabh2
+martah2
+shood2
+jillh2
+shannh2
+clarah2
+liborh2
+jhosty2
+chanth2
+amyhs2
+helenh2
+chhsu2
+franh2
+janyh2
+tracyh2
+dusth2
+davhug2
+bradh2
+moyah2
+jormah2
+mustai2
+kevini2
+leei2
+junkoi2
+jamest2
+karenj2
+joej2
+davidj2
+haylj2
+rosej2
+gertj2
+lanaj2
+hollyj2
+jcjan2
+isek2
+alexka2
+giselk2
+mihok2
+annemk2
+peteke2
+lizak2
+hyeyk2
+hskim2
+bking2
+timk2
+cko2
+fannyk2
+davek2
+rolank2
+craigk2
+martik2
+kotay2
+fredk2
+carolk2
+georgk2
+chrisk
+geirk2
+juliak2
+andrk2
+mank2
+kerstk2
+tckuo2
+tomokk2
+skwan2
+tony2
+markul2
+johnla2
+drewl2
+darlan2
+kenl2
+oritl2
+benjla2
+klau2
+markl2
+mlaur2
+delph2
+pyledu2
+evinl2
+dlee2
+hjlee2
+hklee2
+ivyl2
+jonsul2
+kwlee2
+sjlee2
+wglee2
+ivole2
+peterl2
+steple2
+nikil2
+serenl2
+glinah2
+lsliau2
+hannal2
+rafael2
+angell2
+roblim2
+merjal2
+billli2
+avihal2
+birdlo2
+sergel2
+alberl2
+helenl2
+carlol2
+josel2
+juanl2
+annel2
+lilyl2
+gerryl2
+vicenl2
+nathl2
+edlf2
+editl2
+derekl2
+saml2
+alphal2
+kirbyl2
+luisl2
+dickl2
+torull2
+cristm2
+mmaerk2
+leonm2
+sergm2
+brendm2
+andrwm2
+tinym2
+magdem2
+franma2
+kaym2
+nichm2
+caswem2
+arnam2
+rickm2
+carlam2
+helenm2
+farish2
+majda2
+sarya2
+monaa2
+ahmada2
+tawfad2
+dach2
+krissa2
+krisa2
+paula2
+aleena2
+debraa2
+bja2
+radams2
+stepha2
+josepa2
+colbya2
+allana2
+shola2
+dawna2
+wahaba2
+conraa2
+paulal2
+ralexa2
+ricka2
+willal2
+beckya2
+darrea2
+bkena2
+saraha2
+trical2
+callis2
+bga2
+anasal2
+emada2
+marya2
+kamila2
+marca2
+amyan2
+ericaa2
+janeta2
+tinaa2
+garant2
+franka2
+joea2
+davapp2
+denisa2
+stevar2
+briana2
+mikea2
+randya2
+rafar2
+noela2
+michas2
+davea2
+kellas2
+petash2
+alexa2
+paulat2
+teresa2
+daveat2
+heidia2
+mikeau2
+amya2
+cloa2
+akilaz2
+aprib2
+tricib2
+bruceb2
+chrisb2
+ryanba2
+tomb2
+tohoub2
+pbain2
+bwb2
+gayleb2
+branb2
+sharib2
+johnb2
+dbanks2
+rayb2
+cbarb2
+waldb2
+larrba2
+jeremb2
+mimid2
+garyba2
+bpb2
+hughb2
+kenba2
+markba2
+alib2
+koyb2
+patb2
+dbaur2
+jerib2
+alexb2
+brubax2
+gregbe2
+laurib2
+annb2
+aarob2
+johnbe2
+karbec2
+davb2
+dbeers2
+bmbe2
+teresb2
+paulbe2
+aaronb2
+griogb2
+ericb2
+tracbe2
+janeb2
+bradb2
+leslib2
+tinab2
+dougbe2
+jonib2
+joelbe2
+sharb2
+dougb2
+andreb2
+kberg2
+ryanb2
+cynthb2
+dberry2
+cherbe2
+bmarcb2
+tamib2
+gileb2
+patbe2
+mikebi2
+bryab2
+kathb2
+jamebi2
+kenb2
+billbi2
+jennib2
+danbl2
+jasonb2
+philib2
+alisbl2
+amybl2
+camib2
+alexbl2
+patbl2
+carlab2
+aprilb2
+marcbl2
+brandb2
+kristb2
+jbolic2
+garybo2
+ellenb2
+eribo2
+courtb2
+coletb2
+larryb2
+marcb2
diff --git a/private/net/svcdlls/lls/test/ct/n3.dat b/private/net/svcdlls/lls/test/ct/n3.dat
new file mode 100644
index 000000000..97fcd0ecd
--- /dev/null
+++ b/private/net/svcdlls/lls/test/ct/n3.dat
@@ -0,0 +1,1000 @@
+house3
+ronaar3
+rrja3
+ntxfa3
+ninaa3
+cabram3
+johnac3
+martad3
+acca3
+rrea3
+markad3
+acma3
+byrona3
+yosha3
+julioa3
+ccaa3
+bertag3
+ibmma3
+acmah3
+noriya3
+hakana3
+lisac3
+johnal3
+melisa3
+boba3
+rayall3
+deba3
+jeffal3
+rallen3
+laural3
+lallyn3
+venesa3
+annaal3
+nancya3
+dinoa3
+bain3
+marioa3
+raquea3
+vitora3
+scotam3
+renam3
+eglea3
+audrea3
+chrisa3
+ntxha3
+curta3
+davand3
+davean3
+ntxga3
+gavana3
+acja3
+jod3
+attma3
+naomia3
+royand3
+sallya3
+toman3
+matsan3
+jamiea3
+rickan3
+jana3
+jeffan3
+jiman3
+bevaok3
+takaya3
+yumia3
+carloa3
+takaar3
+shinar3
+yukaa3
+tonyar3
+hiroar3
+suza3
+kenar3
+eadarm3
+loria3
+ntxra3
+richar3
+carnst3
+arnulw3
+mayuma3
+hidas3
+richat3
+idoa3
+ntxbat3
+markat3
+kevana3
+gillea3
+micha3
+joset3
+ntxba3
+gma3
+saorib3
+vikkib3
+garyba3
+suzieb3
+michba3
+danbag3
+frankb3
+larryb3
+sbaird3
+rrkeb3
+jolynb3
+paulb3
+sbaker3
+valb3
+leeb3
+geoba3
+dougba3
+ctpsb3
+hughb3
+galb3
+leonib3
+patba3
+ntxjb3
+rrjba3
+donbar3
+jacbar3
+roberb3
+larrb3
+allpak3
+mosesb3
+donnab3
+cibelb3
+vspb3
+peteba3
+abates3
+marcb3
+pbcnz3
+jbattl3
+jimbau3
+cbaum3
+danbau3
+bilbax3
+kathbe3
+ntxab3
+celinb3
+karenb3
+philb3
+darryb3
+ctpdb3
+scre93
+ccsb3
+stepbe3
+ccjben3
+leneb3
+ntxnb3
+pbenoi3
+vscb3
+micheb3
+rrjimb3
+chadb3
+karib3
+nathb3
+dougbe3
+kimmob3
+amyb3
+decdb3
+ebenb3
+acabe3
+andb3
+ntxsbe3
+michbe3
+geralb3
+juditb3
+savlob3
+dsiwm3
+willbi3
+ellenb3
+joseb3
+blasb3
+thomb3
+annicb3
+marbin3
+ntxmb3
+daveb3
+dirkb3
+suebi3
+stefb3
+patbi3
+hilmib3
+heleb3
+rrcb3
+cblack3
+richbl3
+brynb3
+alanb3
+hyattb3
+donnbl3
+cblasi3
+sbliss3
+janebl3
+fransb3
+inface3
+lucibo3
+pboehm3
+joyb3
+pboffo3
+johnbo3
+airex3
+bobb3
+kierab3
+laurbo3
+danbol3
+cathab3
+kathbo3
+cathb3
+coreyb3
+garyb3
+dadcmb3
+nicolb3
+ismb3
+davidb3
+tomb3
+ntxsb3
+timbo3
+ronbo3
+benjab3
+toddb3
+cheryb3
+vskb3
+shellb3
+timboy3
+bboyle3
+jboyle3
+kaijab3
+myriab3
+mbrand3
+brianb3
+darreb3
+simonb3
+geneb3
+tracyb3
+lisbd3
+ibmdb3
+pbrenn3
+sineab3
+susb3
+bryanb3
+ebrew3
+donbr3
+noelb3
+elizbr3
+galeb3
+davb3
+billbr3
+bribro3
+sophib3
+henryb3
+philbr3
+donb3
+amybr3
+cbrown3
+dianab3
+acjb3
+kirstb3
+lesb3
+rbrown3
+ntxrbr3
+albru3
+sbrug3
+ludob3
+vssb3
+paulbr3
+ccrb3
+michbu3
+larrbu3
+lisab3
+carolb3
+peteb3
+actb3
+klausb3
+telen3
+sbuhr3
+louisb3
+miltb3
+heathb3
+helmb3
+bburke3
+catbu3
+billb3
+dburn3
+kaeb3
+ronb3
+eileeb3
+brettb3
+laurab3
+ccfb3
+gunteb3
+gregb3
+robbu3
+robut3
+acwb3
+faribu3
+noelby3
+ntxpbw3
+eddac3
+oscarc3
+scre323
+anneca3
+jocelc3
+barbca3
+janinc3
+jasonc3
+michca3
+edc3
+pedroc3
+suzc3
+wandac3
+davc3
+carenc3
+joanc3
+leeca3
+markca3
+pcamp3
+julioc3
+mikec3
+lucisc3
+georgc3
+tomasb3
+antonc3
+stevec3
+jimc3
+dencar3
+tomc3
+ntxpc3
+annsc3
+jcarne3
+nanc3
+patcar3
+loric3
+patca3
+davic3
+kimca3
+acmc3
+wilmac3
+maurc3
+kcart3
+gregco3
+ginnyc3
+iainc3
+philc3
+riccar3
+steca3
+ntxsc3
+louisc3
+gianca3
+annec3
+jjcav3
+kencav3
+ntxgc3
+jeffca3
+palmac3
+vsmc3
+forms3
+ntxfc3
+cchad3
+cchali3
+troyc3
+cecilc3
+carmc3
+jasch3
+sophic3
+abbotc3
+inac3
+lchang3
+nicolc3
+vchang3
+clintc3
+billch3
+kench3
+lojc3
+ntxac3
+johnch3
+sorlet3
+kimc3
+weiyc3
+rrycc3
+ritac3
+pierrc3
+tomch3
+henryc3
+willch3
+rrkc3
+ntxsch3
+daich3
+yukoc3
+abm3
+acgc3
+ntxjch3
+rrtcp3
+jojoc3
+krisch3
+ronch3
+javch3
+ntxrac3
+sebasc3
+andrwc3
+gkkc3
+yvonnc3
+kingsc3
+bibich3
+ntxkch3
+philci3
+ccipol3
+paulc3
+tomci3
+waltci3
+andyc3
+bencl3
+dclark3
+ntxjcl3
+johcl3
+kclark3
+vspc3
+rrsc3
+derekc3
+johncl3
+ntxdc3
+ibmrc3
+shimaa3
+silkea3
+davida3
+chrial3
+eallen3
+susana3
+joseal3
+stefam3
+paulam3
+mettea3
+stinev3
+henrik3
+ralpha3
+robann3
+csilla3
+erica3
+marcoa3
+malia3
+ericas3
+alexat3
+maubry3
+anab3
+alexib3
+nicolb3
+urib3
+shantb3
+mikeba3
+andbar3
+beckyb3
+scottb3
+leonib3
+mariab3
+reemb3
+maddib3
+marieb3
+romab3
+ericba3
+brianb3
+josefb3
+chadb3
+ronb3
+almub3
+tinab3
+monikb3
+joelb3
+yannb3
+tarunb3
+stephb3
+veronb3
+stefab3
+bernb3
+dougbl3
+boccom3
+laurib3
+johanb3
+erinb3
+sinbo3
+michab3
+markbo3
+julb3
+kamilb3
+laurb3
+colinb3
+taniab3
+pbrad3
+jonbr3
+edb3
+annieb3
+jmbrie3
+lisab3
+joakib3
+kbrost3
+aaronb3
+paulbr3
+seambr3
+elainb3
+kaib3
+alainb3
+courtb3
+richbu3
+liambu3
+ferbu3
+hanneb3
+felicb3
+seanb3
+maytec3
+nelsc3
+armanc3
+nicoc3
+camcar3
+juanjc3
+tiziac3
+marcoc3
+tonyc3
+jpcast3
+damiac3
+ignac3
+sabinc3
+gordc3
+jaruc3
+cbchae3
+ammonc3
+jaksch3
+stephc3
+jimc3
+normac3
+fredc3
+richch3
+cheric3
+ericc3
+dmchoi3
+gabc3
+plchua3
+josefc3
+crisci3
+mikecl3
+coracl3
+danco3
+antonc3
+maxinc3
+karenc3
+cecelw3
+alberc3
+claudc3
+ajc3
+valerc3
+maxicu3
+brenc3
+wolfz3
+maxic3
+peteda3
+jacqd3
+robd3
+bobk3
+hiltom3
+katjad3
+marnod3
+serged3
+antond3
+michde3
+freddv3
+hansd3
+tdiver3
+ylvad3
+emerd3
+chelod3
+julied3
+jdrage3
+markdr3
+chridu3
+yvand3
+delphd3
+leed3
+michd3
+betind3
+stephv3
+georgd3
+owenea3
+stepea3
+owene3
+kime3
+rone3
+helene3
+annete3
+marcee3
+gabreh3
+nathfa3
+helenf3
+masudf3
+jeruf3
+brunof3
+marif3
+debbif3
+jonf3
+joachf3
+cathf3
+pennyf3
+kimbef3
+andref3
+billf3
+damfin3
+solfo3
+jacquf3
+pacof3
+antonf3
+tomfr3
+bjornf3
+mickeg3
+werng3
+mariag3
+julieg3
+georgg3
+roxang3
+beng3
+mardcg3
+rafg3
+jimeg3
+maiog3
+laurga3
+giagat3
+mattga3
+laurg3
+bgenar3
+fredge3
+lidiag3
+stefag3
+michag3
+minah3
+mgietl3
+robg3
+juligm3
+stephg3
+dannyg3
+davidg3
+xavg3
+sophig3
+danieg3
+cgold3
+merisg3
+osnatg3
+josem3
+diman3
+fredgo3
+erendg3
+luisgo3
+rosag3
+grantg3
+andygo3
+sgort3
+jeanpg3
+geneg3
+scottg3
+chiarg3
+richgr3
+bruceg3
+adamg3
+avishg3
+sandg3
+sophg3
+andreg3
+ugrob3
+sissyg3
+guenth3
+jhaas3
+erezh3
+mariha3
+tommyh3
+ahaigh3
+svenh3
+jackih3
+johnha3
+perh3
+katreh3
+gerhar3
+charlh3
+sanjh3
+robhau3
+tomokh3
+erich3
+brenhe3
+andreh3
+markuh3
+raphh3
+bernh3
+brendh3
+olafh3
+klaush3
+enidh3
+ayolh3
+jorgeh3
+bobhe3
+adrih3
+loreth3
+juanjh3
+stefah3
+gerdh3
+kathah3
+maxh3
+micheh3
+juergh3
+scotth3
+torhol3
+toddh3
+szabh3
+martah3
+shood3
+jillh3
+shannh3
+clarah3
+liborh3
+jhosty3
+chanth3
+amyhs3
+helenh3
+chhsu3
+franh3
+janyh3
+tracyh3
+dusth3
+davhug3
+bradh3
+moyah3
+jormah3
+mustai3
+kevini3
+leei3
+junkoi3
+jamest3
+karenj3
+joej3
+davidj3
+haylj3
+rosej3
+gertj3
+lanaj3
+hollyj3
+jcjan3
+isek3
+alexka3
+giselk3
+mihok3
+annemk3
+peteke3
+lizak3
+hyeyk3
+hskim3
+bking3
+timk3
+cko3
+fannyk3
+davek3
+rolank3
+craigk3
+martik3
+kotay3
+fredk3
+carolk3
+georgk3
+chrisk
+geirk3
+juliak3
+andrk3
+mank3
+kerstk3
+tckuo3
+tomokk3
+skwan3
+tony3
+markul3
+johnla3
+drewl3
+darlan3
+kenl3
+oritl3
+benjla3
+klau3
+markl3
+mlaur3
+delph3
+pyledu3
+evinl3
+dlee3
+hjlee3
+hklee3
+ivyl3
+jonsul3
+kwlee3
+sjlee3
+wglee3
+ivole3
+peterl3
+steple3
+nikil3
+serenl3
+glinah3
+lsliau3
+hannal3
+rafael3
+angell3
+roblim3
+merjal3
+billli3
+avihal3
+birdlo3
+sergel3
+alberl3
+helenl3
+carlol3
+josel3
+juanl3
+annel3
+lilyl3
+gerryl3
+vicenl3
+nathl3
+edlf3
+editl3
+derekl3
+saml3
+alphal3
+kirbyl3
+luisl3
+dickl3
+torull3
+cristm3
+mmaerk3
+leonm3
+sergm3
+brendm3
+andrwm3
+tinym3
+magdem3
+franma3
+kaym3
+nichm3
+caswem3
+arnam3
+rickm3
+carlam3
+helenm3
+farish3
+majda3
+sarya3
+monaa3
+ahmada3
+tawfad3
+dach3
+krissa3
+krisa3
+paula3
+aleena3
+debraa3
+bja3
+radams3
+stepha3
+josepa3
+colbya3
+allana3
+shola3
+dawna3
+wahaba3
+conraa3
+paulal3
+ralexa3
+ricka3
+willal3
+beckya3
+darrea3
+bkena3
+saraha3
+trical3
+callis3
+bga3
+anasal3
+emada3
+marya3
+kamila3
+marca3
+amyan3
+ericaa3
+janeta3
+tinaa3
+garant3
+franka3
+joea3
+davapp3
+denisa3
+stevar3
+briana3
+mikea3
+randya3
+rafar3
+noela3
+michas3
+davea3
+kellas3
+petash3
+alexa3
+paulat3
+teresa3
+daveat3
+heidia3
+mikeau3
+amya3
+cloa3
+akilaz3
+aprib3
+tricib3
+bruceb3
+chrisb3
+ryanba3
+tomb3
+tohoub3
+pbain3
+bwb3
+gayleb3
+branb3
+sharib3
+johnb3
+dbanks3
+rayb3
+cbarb3
+waldb3
+larrba3
+jeremb3
+mimid3
+garyba3
+bpb3
+hughb3
+kenba3
+markba3
+alib3
+koyb3
+patb3
+dbaur3
+jerib3
+alexb3
+brubax3
+gregbe3
+laurib3
+annb3
+aarob3
+johnbe3
+karbec3
+davb3
+dbeers3
+bmbe3
+teresb3
+paulbe3
+aaronb3
+griogb3
+ericb3
+tracbe3
+janeb3
+bradb3
+leslib3
+tinab3
+dougbe3
+jonib3
+joelbe3
+sharb3
+dougb3
+andreb3
+kberg3
+ryanb3
+cynthb3
+dberry3
+cherbe3
+bmarcb3
+tamib3
+gileb3
+patbe3
+mikebi3
+bryab3
+kathb3
+jamebi3
+kenb3
+billbi3
+jennib3
+danbl3
+jasonb3
+philib3
+alisbl3
+amybl3
+camib3
+alexbl3
+patbl3
+carlab3
+aprilb3
+marcbl3
+brandb3
+kristb3
+jbolic3
+garybo3
+ellenb3
+eribo3
+courtb3
+coletb3
+larryb3
+marcb3
diff --git a/private/net/svcdlls/lls/test/ct/n4.dat b/private/net/svcdlls/lls/test/ct/n4.dat
new file mode 100644
index 000000000..156eb0463
--- /dev/null
+++ b/private/net/svcdlls/lls/test/ct/n4.dat
@@ -0,0 +1,1000 @@
+house4
+ronaar4
+rrja4
+ntxfa4
+ninaa4
+cabram4
+johnac4
+martad4
+acca4
+rrea4
+markad4
+acma4
+byrona4
+yosha4
+julioa4
+ccaa4
+bertag4
+ibmma4
+acmah4
+noriya4
+hakana4
+lisac4
+johnal4
+melisa4
+boba4
+rayall4
+deba4
+jeffal4
+rallen4
+laural4
+lallyn4
+venesa4
+annaal4
+nancya4
+dinoa4
+bain4
+marioa4
+raquea4
+vitora4
+scotam4
+renam4
+eglea4
+audrea4
+chrisa4
+ntxha4
+curta4
+davand4
+davean4
+ntxga4
+gavana4
+acja4
+jod4
+attma4
+naomia4
+royand4
+sallya4
+toman4
+matsan4
+jamiea4
+rickan4
+jana4
+jeffan4
+jiman4
+bevaok4
+takaya4
+yumia4
+carloa4
+takaar4
+shinar4
+yukaa4
+tonyar4
+hiroar4
+suza4
+kenar4
+eadarm4
+loria4
+ntxra4
+richar4
+carnst4
+arnulw4
+mayuma4
+hidas4
+richat4
+idoa4
+ntxbat4
+markat4
+kevana4
+gillea4
+micha4
+joset4
+ntxba4
+gma4
+saorib4
+vikkib4
+garyba4
+suzieb4
+michba4
+danbag4
+frankb4
+larryb4
+sbaird4
+rrkeb4
+jolynb4
+paulb4
+sbaker4
+valb4
+leeb4
+geoba4
+dougba4
+ctpsb4
+hughb4
+galb4
+leonib4
+patba4
+ntxjb4
+rrjba4
+donbar4
+jacbar4
+roberb4
+larrb4
+allpak4
+mosesb4
+donnab4
+cibelb4
+vspb4
+peteba4
+abates4
+marcb4
+pbcnz4
+jbattl4
+jimbau4
+cbaum4
+danbau4
+bilbax4
+kathbe4
+ntxab4
+celinb4
+karenb4
+philb4
+darryb4
+ctpdb4
+scre94
+ccsb4
+stepbe4
+ccjben4
+leneb4
+ntxnb4
+pbenoi4
+vscb4
+micheb4
+rrjimb4
+chadb4
+karib4
+nathb4
+dougbe4
+kimmob4
+amyb4
+decdb4
+ebenb4
+acabe4
+andb4
+ntxsbe4
+michbe4
+geralb4
+juditb4
+savlob4
+dsiwm4
+willbi4
+ellenb4
+joseb4
+blasb4
+thomb4
+annicb4
+marbin4
+ntxmb4
+daveb4
+dirkb4
+suebi4
+stefb4
+patbi4
+hilmib4
+heleb4
+rrcb4
+cblack4
+richbl4
+brynb4
+alanb4
+hyattb4
+donnbl4
+cblasi4
+sbliss4
+janebl4
+fransb4
+inface4
+lucibo4
+pboehm4
+joyb4
+pboffo4
+johnbo4
+airex4
+bobb4
+kierab4
+laurbo4
+danbol4
+cathab4
+kathbo4
+cathb4
+coreyb4
+garyb4
+dadcmb4
+nicolb4
+ismb4
+davidb4
+tomb4
+ntxsb4
+timbo4
+ronbo4
+benjab4
+toddb4
+cheryb4
+vskb4
+shellb4
+timboy4
+bboyle4
+jboyle4
+kaijab4
+myriab4
+mbrand4
+brianb4
+darreb4
+simonb4
+geneb4
+tracyb4
+lisbd4
+ibmdb4
+pbrenn4
+sineab4
+susb4
+bryanb4
+ebrew4
+donbr4
+noelb4
+elizbr4
+galeb4
+davb4
+billbr4
+bribro4
+sophib4
+henryb4
+philbr4
+donb4
+amybr4
+cbrown4
+dianab4
+acjb4
+kirstb4
+lesb4
+rbrown4
+ntxrbr4
+albru4
+sbrug4
+ludob4
+vssb4
+paulbr4
+ccrb4
+michbu4
+larrbu4
+lisab4
+carolb4
+peteb4
+actb4
+klausb4
+telen4
+sbuhr4
+louisb4
+miltb4
+heathb4
+helmb4
+bburke4
+catbu4
+billb4
+dburn4
+kaeb4
+ronb4
+eileeb4
+brettb4
+laurab4
+ccfb4
+gunteb4
+gregb4
+robbu4
+robut4
+acwb4
+faribu4
+noelby4
+ntxpbw4
+eddac4
+oscarc4
+scre424
+anneca4
+jocelc4
+barbca4
+janinc4
+jasonc4
+michca4
+edc4
+pedroc4
+suzc4
+wandac4
+davc4
+carenc4
+joanc4
+leeca4
+markca4
+pcamp4
+julioc4
+mikec4
+lucisc4
+georgc4
+tomasb4
+antonc4
+stevec4
+jimc4
+dencar4
+tomc4
+ntxpc4
+annsc4
+jcarne4
+nanc4
+patcar4
+loric4
+patca4
+davic4
+kimca4
+acmc4
+wilmac4
+maurc4
+kcart4
+gregco4
+ginnyc4
+iainc4
+philc4
+riccar4
+steca4
+ntxsc4
+louisc4
+gianca4
+annec4
+jjcav4
+kencav4
+ntxgc4
+jeffca4
+palmac4
+vsmc4
+forms4
+ntxfc4
+cchad4
+cchali4
+troyc4
+cecilc4
+carmc4
+jasch4
+sophic4
+abbotc4
+inac4
+lchang4
+nicolc4
+vchang4
+clintc4
+billch4
+kench4
+lojc4
+ntxac4
+johnch4
+sorlet4
+kimc4
+weiyc4
+rrycc4
+ritac4
+pierrc4
+tomch4
+henryc4
+willch4
+rrkc4
+ntxsch4
+daich4
+yukoc4
+abm4
+acgc4
+ntxjch4
+rrtcp4
+jojoc4
+krisch4
+ronch4
+javch4
+ntxrac4
+sebasc4
+andrwc4
+gkkc4
+yvonnc4
+kingsc4
+bibich4
+ntxkch4
+philci4
+ccipol4
+paulc4
+tomci4
+waltci4
+andyc4
+bencl4
+dclark4
+ntxjcl4
+johcl4
+kclark4
+vspc4
+rrsc4
+derekc4
+johncl4
+ntxdc4
+ibmrc4
+shimaa4
+silkea4
+davida4
+chrial4
+eallen4
+susana4
+joseal4
+stefam4
+paulam4
+mettea4
+stinev4
+henrik4
+ralpha4
+robann4
+csilla4
+erica4
+marcoa4
+malia4
+ericas4
+alexat4
+maubry4
+anab4
+alexib4
+nicolb4
+urib4
+shantb4
+mikeba4
+andbar4
+beckyb4
+scottb4
+leonib4
+mariab4
+reemb4
+maddib4
+marieb4
+romab4
+ericba4
+brianb4
+josefb4
+chadb4
+ronb4
+almub4
+tinab4
+monikb4
+joelb4
+yannb4
+tarunb4
+stephb4
+veronb4
+stefab4
+bernb4
+dougbl4
+boccom4
+laurib4
+johanb4
+erinb4
+sinbo4
+michab4
+markbo4
+julb4
+kamilb4
+laurb4
+colinb4
+taniab4
+pbrad4
+jonbr4
+edb4
+annieb4
+jmbrie4
+lisab4
+joakib4
+kbrost4
+aaronb4
+paulbr4
+seambr4
+elainb4
+kaib4
+alainb4
+courtb4
+richbu4
+liambu4
+ferbu4
+hanneb4
+felicb4
+seanb4
+maytec4
+nelsc4
+armanc4
+nicoc4
+camcar4
+juanjc4
+tiziac4
+marcoc4
+tonyc4
+jpcast4
+damiac4
+ignac4
+sabinc4
+gordc4
+jaruc4
+cbchae4
+ammonc4
+jaksch4
+stephc4
+jimc4
+normac4
+fredc4
+richch4
+cheric4
+ericc4
+dmchoi4
+gabc4
+plchua4
+josefc4
+crisci4
+mikecl4
+coracl4
+danco4
+antonc4
+maxinc4
+karenc4
+cecelw4
+alberc4
+claudc4
+ajc4
+valerc4
+maxicu4
+brenc4
+wolfz4
+maxic4
+peteda4
+jacqd4
+robd4
+bobk4
+hiltom4
+katjad4
+marnod4
+serged4
+antond4
+michde4
+freddv4
+hansd4
+tdiver4
+ylvad4
+emerd4
+chelod4
+julied4
+jdrage4
+markdr4
+chridu4
+yvand4
+delphd4
+leed4
+michd4
+betind4
+stephv4
+georgd4
+owenea4
+stepea4
+owene4
+kime4
+rone4
+helene4
+annete4
+marcee4
+gabreh4
+nathfa4
+helenf4
+masudf4
+jeruf4
+brunof4
+marif4
+debbif4
+jonf4
+joachf4
+cathf4
+pennyf4
+kimbef4
+andref4
+billf4
+damfin4
+solfo4
+jacquf4
+pacof4
+antonf4
+tomfr4
+bjornf4
+mickeg4
+werng4
+mariag4
+julieg4
+georgg4
+roxang4
+beng4
+mardcg4
+rafg4
+jimeg4
+maiog4
+laurga4
+giagat4
+mattga4
+laurg4
+bgenar4
+fredge4
+lidiag4
+stefag4
+michag4
+minah4
+mgietl4
+robg4
+juligm4
+stephg4
+dannyg4
+davidg4
+xavg4
+sophig4
+danieg4
+cgold4
+merisg4
+osnatg4
+josem4
+diman4
+fredgo4
+erendg4
+luisgo4
+rosag4
+grantg4
+andygo4
+sgort4
+jeanpg4
+geneg4
+scottg4
+chiarg4
+richgr4
+bruceg4
+adamg4
+avishg4
+sandg4
+sophg4
+andreg4
+ugrob4
+sissyg4
+guenth4
+jhaas4
+erezh4
+mariha4
+tommyh4
+ahaigh4
+svenh4
+jackih4
+johnha4
+perh4
+katreh4
+gerhar4
+charlh4
+sanjh4
+robhau4
+tomokh4
+erich4
+brenhe4
+andreh4
+markuh4
+raphh4
+bernh4
+brendh4
+olafh4
+klaush4
+enidh4
+ayolh4
+jorgeh4
+bobhe4
+adrih4
+loreth4
+juanjh4
+stefah4
+gerdh4
+kathah4
+maxh4
+micheh4
+juergh4
+scotth4
+torhol4
+toddh4
+szabh4
+martah4
+shood4
+jillh4
+shannh4
+clarah4
+liborh4
+jhosty4
+chanth4
+amyhs4
+helenh4
+chhsu4
+franh4
+janyh4
+tracyh4
+dusth4
+davhug4
+bradh4
+moyah4
+jormah4
+mustai4
+kevini4
+leei4
+junkoi4
+jamest4
+karenj4
+joej4
+davidj4
+haylj4
+rosej4
+gertj4
+lanaj4
+hollyj4
+jcjan4
+isek4
+alexka4
+giselk4
+mihok4
+annemk4
+peteke4
+lizak4
+hyeyk4
+hskim4
+bking4
+timk4
+cko4
+fannyk4
+davek4
+rolank4
+craigk4
+martik4
+kotay4
+fredk4
+carolk4
+georgk4
+chrisk
+geirk4
+juliak4
+andrk4
+mank4
+kerstk4
+tckuo4
+tomokk4
+skwan4
+tony4
+markul4
+johnla4
+drewl4
+darlan4
+kenl4
+oritl4
+benjla4
+klau4
+markl4
+mlaur4
+delph4
+pyledu4
+evinl4
+dlee4
+hjlee4
+hklee4
+ivyl4
+jonsul4
+kwlee4
+sjlee4
+wglee4
+ivole4
+peterl4
+steple4
+nikil4
+serenl4
+glinah4
+lsliau4
+hannal4
+rafael4
+angell4
+roblim4
+merjal4
+billli4
+avihal4
+birdlo4
+sergel4
+alberl4
+helenl4
+carlol4
+josel4
+juanl4
+annel4
+lilyl4
+gerryl4
+vicenl4
+nathl4
+edlf4
+editl4
+derekl4
+saml4
+alphal4
+kirbyl4
+luisl4
+dickl4
+torull4
+cristm4
+mmaerk4
+leonm4
+sergm4
+brendm4
+andrwm4
+tinym4
+magdem4
+franma4
+kaym4
+nichm4
+caswem4
+arnam4
+rickm4
+carlam4
+helenm4
+farish4
+majda4
+sarya4
+monaa4
+ahmada4
+tawfad4
+dach4
+krissa4
+krisa4
+paula4
+aleena4
+debraa4
+bja4
+radams4
+stepha4
+josepa4
+colbya4
+allana4
+shola4
+dawna4
+wahaba4
+conraa4
+paulal4
+ralexa4
+ricka4
+willal4
+beckya4
+darrea4
+bkena4
+saraha4
+trical4
+callis4
+bga4
+anasal4
+emada4
+marya4
+kamila4
+marca4
+amyan4
+ericaa4
+janeta4
+tinaa4
+garant4
+franka4
+joea4
+davapp4
+denisa4
+stevar4
+briana4
+mikea4
+randya4
+rafar4
+noela4
+michas4
+davea4
+kellas4
+petash4
+alexa4
+paulat4
+teresa4
+daveat4
+heidia4
+mikeau4
+amya4
+cloa4
+akilaz4
+aprib4
+tricib4
+bruceb4
+chrisb4
+ryanba4
+tomb4
+tohoub4
+pbain4
+bwb4
+gayleb4
+branb4
+sharib4
+johnb4
+dbanks4
+rayb4
+cbarb4
+waldb4
+larrba4
+jeremb4
+mimid4
+garyba4
+bpb4
+hughb4
+kenba4
+markba4
+alib4
+koyb4
+patb4
+dbaur4
+jerib4
+alexb4
+brubax4
+gregbe4
+laurib4
+annb4
+aarob4
+johnbe4
+karbec4
+davb4
+dbeers4
+bmbe4
+teresb4
+paulbe4
+aaronb4
+griogb4
+ericb4
+tracbe4
+janeb4
+bradb4
+leslib4
+tinab4
+dougbe4
+jonib4
+joelbe4
+sharb4
+dougb4
+andreb4
+kberg4
+ryanb4
+cynthb4
+dberry4
+cherbe4
+bmarcb4
+tamib4
+gileb4
+patbe4
+mikebi4
+bryab4
+kathb4
+jamebi4
+kenb4
+billbi4
+jennib4
+danbl4
+jasonb4
+philib4
+alisbl4
+amybl4
+camib4
+alexbl4
+patbl4
+carlab4
+aprilb4
+marcbl4
+brandb4
+kristb4
+jbolic4
+garybo4
+ellenb4
+eribo4
+courtb4
+coletb4
+larryb4
+marcb4
diff --git a/private/net/svcdlls/lls/test/ct/n5.dat b/private/net/svcdlls/lls/test/ct/n5.dat
new file mode 100644
index 000000000..47c9b3c3d
--- /dev/null
+++ b/private/net/svcdlls/lls/test/ct/n5.dat
@@ -0,0 +1,1000 @@
+house5
+ronaar5
+rrja5
+ntxfa5
+ninaa5
+cabram5
+johnac5
+martad5
+acca5
+rrea5
+markad5
+acma5
+byrona5
+yosha5
+julioa5
+ccaa5
+bertag5
+ibmma5
+acmah5
+noriya5
+hakana5
+lisac5
+johnal5
+melisa5
+boba5
+rayall5
+deba5
+jeffal5
+rallen5
+laural5
+lallyn5
+venesa5
+annaal5
+nancya5
+dinoa5
+bain5
+marioa5
+raquea5
+vitora5
+scotam5
+renam5
+eglea5
+audrea5
+chrisa5
+ntxha5
+curta5
+davand5
+davean5
+ntxga5
+gavana5
+acja5
+jod5
+attma5
+naomia5
+royand5
+sallya5
+toman5
+matsan5
+jamiea5
+rickan5
+jana5
+jeffan5
+jiman5
+bevaok5
+takaya5
+yumia5
+carloa5
+takaar5
+shinar5
+yukaa5
+tonyar5
+hiroar5
+suza5
+kenar5
+eadarm5
+loria5
+ntxra5
+richar5
+carnst5
+arnulw5
+mayuma5
+hidas5
+richat5
+idoa5
+ntxbat5
+markat5
+kevana5
+gillea5
+micha5
+joset5
+ntxba5
+gma5
+saorib5
+vikkib5
+garyba5
+suzieb5
+michba5
+danbag5
+frankb5
+larryb5
+sbaird5
+rrkeb5
+jolynb5
+paulb5
+sbaker5
+valb5
+leeb5
+geoba5
+dougba5
+ctpsb5
+hughb5
+galb5
+leonib5
+patba5
+ntxjb5
+rrjba5
+donbar5
+jacbar5
+roberb5
+larrb5
+allpak5
+mosesb5
+donnab5
+cibelb5
+vspb5
+peteba5
+abates5
+marcb5
+pbcnz5
+jbattl5
+jimbau5
+cbaum5
+danbau5
+bilbax5
+kathbe5
+ntxab5
+celinb5
+karenb5
+philb5
+darryb5
+ctpdb5
+scre95
+ccsb5
+stepbe5
+ccjben5
+leneb5
+ntxnb5
+pbenoi5
+vscb5
+micheb5
+rrjimb5
+chadb5
+karib5
+nathb5
+dougbe5
+kimmob5
+amyb5
+decdb5
+ebenb5
+acabe5
+andb5
+ntxsbe5
+michbe5
+geralb5
+juditb5
+savlob5
+dsiwm5
+willbi5
+ellenb5
+joseb5
+blasb5
+thomb5
+annicb5
+marbin5
+ntxmb5
+daveb5
+dirkb5
+suebi5
+stefb5
+patbi5
+hilmib5
+heleb5
+rrcb5
+cblack5
+richbl5
+brynb5
+alanb5
+hyattb5
+donnbl5
+cblasi5
+sbliss5
+janebl5
+fransb5
+inface5
+lucibo5
+pboehm5
+joyb5
+pboffo5
+johnbo5
+airex5
+bobb5
+kierab5
+laurbo5
+danbol5
+cathab5
+kathbo5
+cathb5
+coreyb5
+garyb5
+dadcmb5
+nicolb5
+ismb5
+davidb5
+tomb5
+ntxsb5
+timbo5
+ronbo5
+benjab5
+toddb5
+cheryb5
+vskb5
+shellb5
+timboy5
+bboyle5
+jboyle5
+kaijab5
+myriab5
+mbrand5
+brianb5
+darreb5
+simonb5
+geneb5
+tracyb5
+lisbd5
+ibmdb5
+pbrenn5
+sineab5
+susb5
+bryanb5
+ebrew5
+donbr5
+noelb5
+elizbr5
+galeb5
+davb5
+billbr5
+bribro5
+sophib5
+henryb5
+philbr5
+donb5
+amybr5
+cbrown5
+dianab5
+acjb5
+kirstb5
+lesb5
+rbrown5
+ntxrbr5
+albru5
+sbrug5
+ludob5
+vssb5
+paulbr5
+ccrb5
+michbu5
+larrbu5
+lisab5
+carolb5
+peteb5
+actb5
+klausb5
+telen5
+sbuhr5
+louisb5
+miltb5
+heathb5
+helmb5
+bburke5
+catbu5
+billb5
+dburn5
+kaeb5
+ronb5
+eileeb5
+brettb5
+laurab5
+ccfb5
+gunteb5
+gregb5
+robbu5
+robut5
+acwb5
+faribu5
+noelby5
+ntxpbw5
+eddac5
+oscarc5
+scre525
+anneca5
+jocelc5
+barbca5
+janinc5
+jasonc5
+michca5
+edc5
+pedroc5
+suzc5
+wandac5
+davc5
+carenc5
+joanc5
+leeca5
+markca5
+pcamp5
+julioc5
+mikec5
+lucisc5
+georgc5
+tomasb5
+antonc5
+stevec5
+jimc5
+dencar5
+tomc5
+ntxpc5
+annsc5
+jcarne5
+nanc5
+patcar5
+loric5
+patca5
+davic5
+kimca5
+acmc5
+wilmac5
+maurc5
+kcart5
+gregco5
+ginnyc5
+iainc5
+philc5
+riccar5
+steca5
+ntxsc5
+louisc5
+gianca5
+annec5
+jjcav5
+kencav5
+ntxgc5
+jeffca5
+palmac5
+vsmc5
+forms5
+ntxfc5
+cchad5
+cchali5
+troyc5
+cecilc5
+carmc5
+jasch5
+sophic5
+abbotc5
+inac5
+lchang5
+nicolc5
+vchang5
+clintc5
+billch5
+kench5
+lojc5
+ntxac5
+johnch5
+sorlet5
+kimc5
+weiyc5
+rrycc5
+ritac5
+pierrc5
+tomch5
+henryc5
+willch5
+rrkc5
+ntxsch5
+daich5
+yukoc5
+abm5
+acgc5
+ntxjch5
+rrtcp5
+jojoc5
+krisch5
+ronch5
+javch5
+ntxrac5
+sebasc5
+andrwc5
+gkkc5
+yvonnc5
+kingsc5
+bibich5
+ntxkch5
+philci5
+ccipol5
+paulc5
+tomci5
+waltci5
+andyc5
+bencl5
+dclark5
+ntxjcl5
+johcl5
+kclark5
+vspc5
+rrsc5
+derekc5
+johncl5
+ntxdc5
+ibmrc5
+shimaa5
+silkea5
+davida5
+chrial5
+eallen5
+susana5
+joseal5
+stefam5
+paulam5
+mettea5
+stinev5
+henrik5
+ralpha5
+robann5
+csilla5
+erica5
+marcoa5
+malia5
+ericas5
+alexat5
+maubry5
+anab5
+alexib5
+nicolb5
+urib5
+shantb5
+mikeba5
+andbar5
+beckyb5
+scottb5
+leonib5
+mariab5
+reemb5
+maddib5
+marieb5
+romab5
+ericba5
+brianb5
+josefb5
+chadb5
+ronb5
+almub5
+tinab5
+monikb5
+joelb5
+yannb5
+tarunb5
+stephb5
+veronb5
+stefab5
+bernb5
+dougbl5
+boccom5
+laurib5
+johanb5
+erinb5
+sinbo5
+michab5
+markbo5
+julb5
+kamilb5
+laurb5
+colinb5
+taniab5
+pbrad5
+jonbr5
+edb5
+annieb5
+jmbrie5
+lisab5
+joakib5
+kbrost5
+aaronb5
+paulbr5
+seambr5
+elainb5
+kaib5
+alainb5
+courtb5
+richbu5
+liambu5
+ferbu5
+hanneb5
+felicb5
+seanb5
+maytec5
+nelsc5
+armanc5
+nicoc5
+camcar5
+juanjc5
+tiziac5
+marcoc5
+tonyc5
+jpcast5
+damiac5
+ignac5
+sabinc5
+gordc5
+jaruc5
+cbchae5
+ammonc5
+jaksch5
+stephc5
+jimc5
+normac5
+fredc5
+richch5
+cheric5
+ericc5
+dmchoi5
+gabc5
+plchua5
+josefc5
+crisci5
+mikecl5
+coracl5
+danco5
+antonc5
+maxinc5
+karenc5
+cecelw5
+alberc5
+claudc5
+ajc5
+valerc5
+maxicu5
+brenc5
+wolfz5
+maxic5
+peteda5
+jacqd5
+robd5
+bobk5
+hiltom5
+katjad5
+marnod5
+serged5
+antond5
+michde5
+freddv5
+hansd5
+tdiver5
+ylvad5
+emerd5
+chelod5
+julied5
+jdrage5
+markdr5
+chridu5
+yvand5
+delphd5
+leed5
+michd5
+betind5
+stephv5
+georgd5
+owenea5
+stepea5
+owene5
+kime5
+rone5
+helene5
+annete5
+marcee5
+gabreh5
+nathfa5
+helenf5
+masudf5
+jeruf5
+brunof5
+marif5
+debbif5
+jonf5
+joachf5
+cathf5
+pennyf5
+kimbef5
+andref5
+billf5
+damfin5
+solfo5
+jacquf5
+pacof5
+antonf5
+tomfr5
+bjornf5
+mickeg5
+werng5
+mariag5
+julieg5
+georgg5
+roxang5
+beng5
+mardcg5
+rafg5
+jimeg5
+maiog5
+laurga5
+giagat5
+mattga5
+laurg5
+bgenar5
+fredge5
+lidiag5
+stefag5
+michag5
+minah5
+mgietl5
+robg5
+juligm5
+stephg5
+dannyg5
+davidg5
+xavg5
+sophig5
+danieg5
+cgold5
+merisg5
+osnatg5
+josem5
+diman5
+fredgo5
+erendg5
+luisgo5
+rosag5
+grantg5
+andygo5
+sgort5
+jeanpg5
+geneg5
+scottg5
+chiarg5
+richgr5
+bruceg5
+adamg5
+avishg5
+sandg5
+sophg5
+andreg5
+ugrob5
+sissyg5
+guenth5
+jhaas5
+erezh5
+mariha5
+tommyh5
+ahaigh5
+svenh5
+jackih5
+johnha5
+perh5
+katreh5
+gerhar5
+charlh5
+sanjh5
+robhau5
+tomokh5
+erich5
+brenhe5
+andreh5
+markuh5
+raphh5
+bernh5
+brendh5
+olafh5
+klaush5
+enidh5
+ayolh5
+jorgeh5
+bobhe5
+adrih5
+loreth5
+juanjh5
+stefah5
+gerdh5
+kathah5
+maxh5
+micheh5
+juergh5
+scotth5
+torhol5
+toddh5
+szabh5
+martah5
+shood5
+jillh5
+shannh5
+clarah5
+liborh5
+jhosty5
+chanth5
+amyhs5
+helenh5
+chhsu5
+franh5
+janyh5
+tracyh5
+dusth5
+davhug5
+bradh5
+moyah5
+jormah5
+mustai5
+kevini5
+leei5
+junkoi5
+jamest5
+karenj5
+joej5
+davidj5
+haylj5
+rosej5
+gertj5
+lanaj5
+hollyj5
+jcjan5
+isek5
+alexka5
+giselk5
+mihok5
+annemk5
+peteke5
+lizak5
+hyeyk5
+hskim5
+bking5
+timk5
+cko5
+fannyk5
+davek5
+rolank5
+craigk5
+martik5
+kotay5
+fredk5
+carolk5
+georgk5
+chrisk
+geirk5
+juliak5
+andrk5
+mank5
+kerstk5
+tckuo5
+tomokk5
+skwan5
+tony5
+markul5
+johnla5
+drewl5
+darlan5
+kenl5
+oritl5
+benjla5
+klau5
+markl5
+mlaur5
+delph5
+pyledu5
+evinl5
+dlee5
+hjlee5
+hklee5
+ivyl5
+jonsul5
+kwlee5
+sjlee5
+wglee5
+ivole5
+peterl5
+steple5
+nikil5
+serenl5
+glinah5
+lsliau5
+hannal5
+rafael5
+angell5
+roblim5
+merjal5
+billli5
+avihal5
+birdlo5
+sergel5
+alberl5
+helenl5
+carlol5
+josel5
+juanl5
+annel5
+lilyl5
+gerryl5
+vicenl5
+nathl5
+edlf5
+editl5
+derekl5
+saml5
+alphal5
+kirbyl5
+luisl5
+dickl5
+torull5
+cristm5
+mmaerk5
+leonm5
+sergm5
+brendm5
+andrwm5
+tinym5
+magdem5
+franma5
+kaym5
+nichm5
+caswem5
+arnam5
+rickm5
+carlam5
+helenm5
+farish5
+majda5
+sarya5
+monaa5
+ahmada5
+tawfad5
+dach5
+krissa5
+krisa5
+paula5
+aleena5
+debraa5
+bja5
+radams5
+stepha5
+josepa5
+colbya5
+allana5
+shola5
+dawna5
+wahaba5
+conraa5
+paulal5
+ralexa5
+ricka5
+willal5
+beckya5
+darrea5
+bkena5
+saraha5
+trical5
+callis5
+bga5
+anasal5
+emada5
+marya5
+kamila5
+marca5
+amyan5
+ericaa5
+janeta5
+tinaa5
+garant5
+franka5
+joea5
+davapp5
+denisa5
+stevar5
+briana5
+mikea5
+randya5
+rafar5
+noela5
+michas5
+davea5
+kellas5
+petash5
+alexa5
+paulat5
+teresa5
+daveat5
+heidia5
+mikeau5
+amya5
+cloa5
+akilaz5
+aprib5
+tricib5
+bruceb5
+chrisb5
+ryanba5
+tomb5
+tohoub5
+pbain5
+bwb5
+gayleb5
+branb5
+sharib5
+johnb5
+dbanks5
+rayb5
+cbarb5
+waldb5
+larrba5
+jeremb5
+mimid5
+garyba5
+bpb5
+hughb5
+kenba5
+markba5
+alib5
+koyb5
+patb5
+dbaur5
+jerib5
+alexb5
+brubax5
+gregbe5
+laurib5
+annb5
+aarob5
+johnbe5
+karbec5
+davb5
+dbeers5
+bmbe5
+teresb5
+paulbe5
+aaronb5
+griogb5
+ericb5
+tracbe5
+janeb5
+bradb5
+leslib5
+tinab5
+dougbe5
+jonib5
+joelbe5
+sharb5
+dougb5
+andreb5
+kberg5
+ryanb5
+cynthb5
+dberry5
+cherbe5
+bmarcb5
+tamib5
+gileb5
+patbe5
+mikebi5
+bryab5
+kathb5
+jamebi5
+kenb5
+billbi5
+jennib5
+danbl5
+jasonb5
+philib5
+alisbl5
+amybl5
+camib5
+alexbl5
+patbl5
+carlab5
+aprilb5
+marcbl5
+brandb5
+kristb5
+jbolic5
+garybo5
+ellenb5
+eribo5
+courtb5
+coletb5
+larryb5
+marcb5
diff --git a/private/net/svcdlls/lls/test/ct/n6.dat b/private/net/svcdlls/lls/test/ct/n6.dat
new file mode 100644
index 000000000..15af57ae9
--- /dev/null
+++ b/private/net/svcdlls/lls/test/ct/n6.dat
@@ -0,0 +1,1000 @@
+house6
+ronaar6
+rrja6
+ntxfa6
+ninaa6
+cabram6
+johnac6
+martad6
+acca6
+rrea6
+markad6
+acma6
+byrona6
+yosha6
+julioa6
+ccaa6
+bertag6
+ibmma6
+acmah6
+noriya6
+hakana6
+lisac6
+johnal6
+melisa6
+boba6
+rayall6
+deba6
+jeffal6
+rallen6
+laural6
+lallyn6
+venesa6
+annaal6
+nancya6
+dinoa6
+bain6
+marioa6
+raquea6
+vitora6
+scotam6
+renam6
+eglea6
+audrea6
+chrisa6
+ntxha6
+curta6
+davand6
+davean6
+ntxga6
+gavana6
+acja6
+jod6
+attma6
+naomia6
+royand6
+sallya6
+toman6
+matsan6
+jamiea6
+rickan6
+jana6
+jeffan6
+jiman6
+bevaok6
+takaya6
+yumia6
+carloa6
+takaar6
+shinar6
+yukaa6
+tonyar6
+hiroar6
+suza6
+kenar6
+eadarm6
+loria6
+ntxra6
+richar6
+carnst6
+arnulw6
+mayuma6
+hidas6
+richat6
+idoa6
+ntxbat6
+markat6
+kevana6
+gillea6
+micha6
+joset6
+ntxba6
+gma6
+saorib6
+vikkib6
+garyba6
+suzieb6
+michba6
+danbag6
+frankb6
+larryb6
+sbaird6
+rrkeb6
+jolynb6
+paulb6
+sbaker6
+valb6
+leeb6
+geoba6
+dougba6
+ctpsb6
+hughb6
+galb6
+leonib6
+patba6
+ntxjb6
+rrjba6
+donbar6
+jacbar6
+roberb6
+larrb6
+allpak6
+mosesb6
+donnab6
+cibelb6
+vspb6
+peteba6
+abates6
+marcb6
+pbcnz6
+jbattl6
+jimbau6
+cbaum6
+danbau6
+bilbax6
+kathbe6
+ntxab6
+celinb6
+karenb6
+philb6
+darryb6
+ctpdb6
+scre96
+ccsb6
+stepbe6
+ccjben6
+leneb6
+ntxnb6
+pbenoi6
+vscb6
+micheb6
+rrjimb6
+chadb6
+karib6
+nathb6
+dougbe6
+kimmob6
+amyb6
+decdb6
+ebenb6
+acabe6
+andb6
+ntxsbe6
+michbe6
+geralb6
+juditb6
+savlob6
+dsiwm6
+willbi6
+ellenb6
+joseb6
+blasb6
+thomb6
+annicb6
+marbin6
+ntxmb6
+daveb6
+dirkb6
+suebi6
+stefb6
+patbi6
+hilmib6
+heleb6
+rrcb6
+cblack6
+richbl6
+brynb6
+alanb6
+hyattb6
+donnbl6
+cblasi6
+sbliss6
+janebl6
+fransb6
+inface6
+lucibo6
+pboehm6
+joyb6
+pboffo6
+johnbo6
+airex6
+bobb6
+kierab6
+laurbo6
+danbol6
+cathab6
+kathbo6
+cathb6
+coreyb6
+garyb6
+dadcmb6
+nicolb6
+ismb6
+davidb6
+tomb6
+ntxsb6
+timbo6
+ronbo6
+benjab6
+toddb6
+cheryb6
+vskb6
+shellb6
+timboy6
+bboyle6
+jboyle6
+kaijab6
+myriab6
+mbrand6
+brianb6
+darreb6
+simonb6
+geneb6
+tracyb6
+lisbd6
+ibmdb6
+pbrenn6
+sineab6
+susb6
+bryanb6
+ebrew6
+donbr6
+noelb6
+elizbr6
+galeb6
+davb6
+billbr6
+bribro6
+sophib6
+henryb6
+philbr6
+donb6
+amybr6
+cbrown6
+dianab6
+acjb6
+kirstb6
+lesb6
+rbrown6
+ntxrbr6
+albru6
+sbrug6
+ludob6
+vssb6
+paulbr6
+ccrb6
+michbu6
+larrbu6
+lisab6
+carolb6
+peteb6
+actb6
+klausb6
+telen6
+sbuhr6
+louisb6
+miltb6
+heathb6
+helmb6
+bburke6
+catbu6
+billb6
+dburn6
+kaeb6
+ronb6
+eileeb6
+brettb6
+laurab6
+ccfb6
+gunteb6
+gregb6
+robbu6
+robut6
+acwb6
+faribu6
+noelby6
+ntxpbw6
+eddac6
+oscarc6
+scre626
+anneca6
+jocelc6
+barbca6
+janinc6
+jasonc6
+michca6
+edc6
+pedroc6
+suzc6
+wandac6
+davc6
+carenc6
+joanc6
+leeca6
+markca6
+pcamp6
+julioc6
+mikec6
+lucisc6
+georgc6
+tomasb6
+antonc6
+stevec6
+jimc6
+dencar6
+tomc6
+ntxpc6
+annsc6
+jcarne6
+nanc6
+patcar6
+loric6
+patca6
+davic6
+kimca6
+acmc6
+wilmac6
+maurc6
+kcart6
+gregco6
+ginnyc6
+iainc6
+philc6
+riccar6
+steca6
+ntxsc6
+louisc6
+gianca6
+annec6
+jjcav6
+kencav6
+ntxgc6
+jeffca6
+palmac6
+vsmc6
+forms6
+ntxfc6
+cchad6
+cchali6
+troyc6
+cecilc6
+carmc6
+jasch6
+sophic6
+abbotc6
+inac6
+lchang6
+nicolc6
+vchang6
+clintc6
+billch6
+kench6
+lojc6
+ntxac6
+johnch6
+sorlet6
+kimc6
+weiyc6
+rrycc6
+ritac6
+pierrc6
+tomch6
+henryc6
+willch6
+rrkc6
+ntxsch6
+daich6
+yukoc6
+abm6
+acgc6
+ntxjch6
+rrtcp6
+jojoc6
+krisch6
+ronch6
+javch6
+ntxrac6
+sebasc6
+andrwc6
+gkkc6
+yvonnc6
+kingsc6
+bibich6
+ntxkch6
+philci6
+ccipol6
+paulc6
+tomci6
+waltci6
+andyc6
+bencl6
+dclark6
+ntxjcl6
+johcl6
+kclark6
+vspc6
+rrsc6
+derekc6
+johncl6
+ntxdc6
+ibmrc6
+shimaa6
+silkea6
+davida6
+chrial6
+eallen6
+susana6
+joseal6
+stefam6
+paulam6
+mettea6
+stinev6
+henrik6
+ralpha6
+robann6
+csilla6
+erica6
+marcoa6
+malia6
+ericas6
+alexat6
+maubry6
+anab6
+alexib6
+nicolb6
+urib6
+shantb6
+mikeba6
+andbar6
+beckyb6
+scottb6
+leonib6
+mariab6
+reemb6
+maddib6
+marieb6
+romab6
+ericba6
+brianb6
+josefb6
+chadb6
+ronb6
+almub6
+tinab6
+monikb6
+joelb6
+yannb6
+tarunb6
+stephb6
+veronb6
+stefab6
+bernb6
+dougbl6
+boccom6
+laurib6
+johanb6
+erinb6
+sinbo6
+michab6
+markbo6
+julb6
+kamilb6
+laurb6
+colinb6
+taniab6
+pbrad6
+jonbr6
+edb6
+annieb6
+jmbrie6
+lisab6
+joakib6
+kbrost6
+aaronb6
+paulbr6
+seambr6
+elainb6
+kaib6
+alainb6
+courtb6
+richbu6
+liambu6
+ferbu6
+hanneb6
+felicb6
+seanb6
+maytec6
+nelsc6
+armanc6
+nicoc6
+camcar6
+juanjc6
+tiziac6
+marcoc6
+tonyc6
+jpcast6
+damiac6
+ignac6
+sabinc6
+gordc6
+jaruc6
+cbchae6
+ammonc6
+jaksch6
+stephc6
+jimc6
+normac6
+fredc6
+richch6
+cheric6
+ericc6
+dmchoi6
+gabc6
+plchua6
+josefc6
+crisci6
+mikecl6
+coracl6
+danco6
+antonc6
+maxinc6
+karenc6
+cecelw6
+alberc6
+claudc6
+ajc6
+valerc6
+maxicu6
+brenc6
+wolfz6
+maxic6
+peteda6
+jacqd6
+robd6
+bobk6
+hiltom6
+katjad6
+marnod6
+serged6
+antond6
+michde6
+freddv6
+hansd6
+tdiver6
+ylvad6
+emerd6
+chelod6
+julied6
+jdrage6
+markdr6
+chridu6
+yvand6
+delphd6
+leed6
+michd6
+betind6
+stephv6
+georgd6
+owenea6
+stepea6
+owene6
+kime6
+rone6
+helene6
+annete6
+marcee6
+gabreh6
+nathfa6
+helenf6
+masudf6
+jeruf6
+brunof6
+marif6
+debbif6
+jonf6
+joachf6
+cathf6
+pennyf6
+kimbef6
+andref6
+billf6
+damfin6
+solfo6
+jacquf6
+pacof6
+antonf6
+tomfr6
+bjornf6
+mickeg6
+werng6
+mariag6
+julieg6
+georgg6
+roxang6
+beng6
+mardcg6
+rafg6
+jimeg6
+maiog6
+laurga6
+giagat6
+mattga6
+laurg6
+bgenar6
+fredge6
+lidiag6
+stefag6
+michag6
+minah6
+mgietl6
+robg6
+juligm6
+stephg6
+dannyg6
+davidg6
+xavg6
+sophig6
+danieg6
+cgold6
+merisg6
+osnatg6
+josem6
+diman6
+fredgo6
+erendg6
+luisgo6
+rosag6
+grantg6
+andygo6
+sgort6
+jeanpg6
+geneg6
+scottg6
+chiarg6
+richgr6
+bruceg6
+adamg6
+avishg6
+sandg6
+sophg6
+andreg6
+ugrob6
+sissyg6
+guenth6
+jhaas6
+erezh6
+mariha6
+tommyh6
+ahaigh6
+svenh6
+jackih6
+johnha6
+perh6
+katreh6
+gerhar6
+charlh6
+sanjh6
+robhau6
+tomokh6
+erich6
+brenhe6
+andreh6
+markuh6
+raphh6
+bernh6
+brendh6
+olafh6
+klaush6
+enidh6
+ayolh6
+jorgeh6
+bobhe6
+adrih6
+loreth6
+juanjh6
+stefah6
+gerdh6
+kathah6
+maxh6
+micheh6
+juergh6
+scotth6
+torhol6
+toddh6
+szabh6
+martah6
+shood6
+jillh6
+shannh6
+clarah6
+liborh6
+jhosty6
+chanth6
+amyhs6
+helenh6
+chhsu6
+franh6
+janyh6
+tracyh6
+dusth6
+davhug6
+bradh6
+moyah6
+jormah6
+mustai6
+kevini6
+leei6
+junkoi6
+jamest6
+karenj6
+joej6
+davidj6
+haylj6
+rosej6
+gertj6
+lanaj6
+hollyj6
+jcjan6
+isek6
+alexka6
+giselk6
+mihok6
+annemk6
+peteke6
+lizak6
+hyeyk6
+hskim6
+bking6
+timk6
+cko6
+fannyk6
+davek6
+rolank6
+craigk6
+martik6
+kotay6
+fredk6
+carolk6
+georgk6
+chrisk
+geirk6
+juliak6
+andrk6
+mank6
+kerstk6
+tckuo6
+tomokk6
+skwan6
+tony6
+markul6
+johnla6
+drewl6
+darlan6
+kenl6
+oritl6
+benjla6
+klau6
+markl6
+mlaur6
+delph6
+pyledu6
+evinl6
+dlee6
+hjlee6
+hklee6
+ivyl6
+jonsul6
+kwlee6
+sjlee6
+wglee6
+ivole6
+peterl6
+steple6
+nikil6
+serenl6
+glinah6
+lsliau6
+hannal6
+rafael6
+angell6
+roblim6
+merjal6
+billli6
+avihal6
+birdlo6
+sergel6
+alberl6
+helenl6
+carlol6
+josel6
+juanl6
+annel6
+lilyl6
+gerryl6
+vicenl6
+nathl6
+edlf6
+editl6
+derekl6
+saml6
+alphal6
+kirbyl6
+luisl6
+dickl6
+torull6
+cristm6
+mmaerk6
+leonm6
+sergm6
+brendm6
+andrwm6
+tinym6
+magdem6
+franma6
+kaym6
+nichm6
+caswem6
+arnam6
+rickm6
+carlam6
+helenm6
+farish6
+majda6
+sarya6
+monaa6
+ahmada6
+tawfad6
+dach6
+krissa6
+krisa6
+paula6
+aleena6
+debraa6
+bja6
+radams6
+stepha6
+josepa6
+colbya6
+allana6
+shola6
+dawna6
+wahaba6
+conraa6
+paulal6
+ralexa6
+ricka6
+willal6
+beckya6
+darrea6
+bkena6
+saraha6
+trical6
+callis6
+bga6
+anasal6
+emada6
+marya6
+kamila6
+marca6
+amyan6
+ericaa6
+janeta6
+tinaa6
+garant6
+franka6
+joea6
+davapp6
+denisa6
+stevar6
+briana6
+mikea6
+randya6
+rafar6
+noela6
+michas6
+davea6
+kellas6
+petash6
+alexa6
+paulat6
+teresa6
+daveat6
+heidia6
+mikeau6
+amya6
+cloa6
+akilaz6
+aprib6
+tricib6
+bruceb6
+chrisb6
+ryanba6
+tomb6
+tohoub6
+pbain6
+bwb6
+gayleb6
+branb6
+sharib6
+johnb6
+dbanks6
+rayb6
+cbarb6
+waldb6
+larrba6
+jeremb6
+mimid6
+garyba6
+bpb6
+hughb6
+kenba6
+markba6
+alib6
+koyb6
+patb6
+dbaur6
+jerib6
+alexb6
+brubax6
+gregbe6
+laurib6
+annb6
+aarob6
+johnbe6
+karbec6
+davb6
+dbeers6
+bmbe6
+teresb6
+paulbe6
+aaronb6
+griogb6
+ericb6
+tracbe6
+janeb6
+bradb6
+leslib6
+tinab6
+dougbe6
+jonib6
+joelbe6
+sharb6
+dougb6
+andreb6
+kberg6
+ryanb6
+cynthb6
+dberry6
+cherbe6
+bmarcb6
+tamib6
+gileb6
+patbe6
+mikebi6
+bryab6
+kathb6
+jamebi6
+kenb6
+billbi6
+jennib6
+danbl6
+jasonb6
+philib6
+alisbl6
+amybl6
+camib6
+alexbl6
+patbl6
+carlab6
+aprilb6
+marcbl6
+brandb6
+kristb6
+jbolic6
+garybo6
+ellenb6
+eribo6
+courtb6
+coletb6
+larryb6
+marcb6
diff --git a/private/net/svcdlls/lls/test/ct/n7.dat b/private/net/svcdlls/lls/test/ct/n7.dat
new file mode 100644
index 000000000..b6a9900c6
--- /dev/null
+++ b/private/net/svcdlls/lls/test/ct/n7.dat
@@ -0,0 +1,1000 @@
+house7
+ronaar7
+rrja7
+ntxfa7
+ninaa7
+cabram7
+johnac7
+martad7
+acca7
+rrea7
+markad7
+acma7
+byrona7
+yosha7
+julioa7
+ccaa7
+bertag7
+ibmma7
+acmah7
+noriya7
+hakana7
+lisac7
+johnal7
+melisa7
+boba7
+rayall7
+deba7
+jeffal7
+rallen7
+laural7
+lallyn7
+venesa7
+annaal7
+nancya7
+dinoa7
+bain7
+marioa7
+raquea7
+vitora7
+scotam7
+renam7
+eglea7
+audrea7
+chrisa7
+ntxha7
+curta7
+davand7
+davean7
+ntxga7
+gavana7
+acja7
+jod7
+attma7
+naomia7
+royand7
+sallya7
+toman7
+matsan7
+jamiea7
+rickan7
+jana7
+jeffan7
+jiman7
+bevaok7
+takaya7
+yumia7
+carloa7
+takaar7
+shinar7
+yukaa7
+tonyar7
+hiroar7
+suza7
+kenar7
+eadarm7
+loria7
+ntxra7
+richar7
+carnst7
+arnulw7
+mayuma7
+hidas7
+richat7
+idoa7
+ntxbat7
+markat7
+kevana7
+gillea7
+micha7
+joset7
+ntxba7
+gma7
+saorib7
+vikkib7
+garyba7
+suzieb7
+michba7
+danbag7
+frankb7
+larryb7
+sbaird7
+rrkeb7
+jolynb7
+paulb7
+sbaker7
+valb7
+leeb7
+geoba7
+dougba7
+ctpsb7
+hughb7
+galb7
+leonib7
+patba7
+ntxjb7
+rrjba7
+donbar7
+jacbar7
+roberb7
+larrb7
+allpak7
+mosesb7
+donnab7
+cibelb7
+vspb7
+peteba7
+abates7
+marcb7
+pbcnz7
+jbattl7
+jimbau7
+cbaum7
+danbau7
+bilbax7
+kathbe7
+ntxab7
+celinb7
+karenb7
+philb7
+darryb7
+ctpdb7
+scre97
+ccsb7
+stepbe7
+ccjben7
+leneb7
+ntxnb7
+pbenoi7
+vscb7
+micheb7
+rrjimb7
+chadb7
+karib7
+nathb7
+dougbe7
+kimmob7
+amyb7
+decdb7
+ebenb7
+acabe7
+andb7
+ntxsbe7
+michbe7
+geralb7
+juditb7
+savlob7
+dsiwm7
+willbi7
+ellenb7
+joseb7
+blasb7
+thomb7
+annicb7
+marbin7
+ntxmb7
+daveb7
+dirkb7
+suebi7
+stefb7
+patbi7
+hilmib7
+heleb7
+rrcb7
+cblack7
+richbl7
+brynb7
+alanb7
+hyattb7
+donnbl7
+cblasi7
+sbliss7
+janebl7
+fransb7
+inface7
+lucibo7
+pboehm7
+joyb7
+pboffo7
+johnbo7
+airex7
+bobb7
+kierab7
+laurbo7
+danbol7
+cathab7
+kathbo7
+cathb7
+coreyb7
+garyb7
+dadcmb7
+nicolb7
+ismb7
+davidb7
+tomb7
+ntxsb7
+timbo7
+ronbo7
+benjab7
+toddb7
+cheryb7
+vskb7
+shellb7
+timboy7
+bboyle7
+jboyle7
+kaijab7
+myriab7
+mbrand7
+brianb7
+darreb7
+simonb7
+geneb7
+tracyb7
+lisbd7
+ibmdb7
+pbrenn7
+sineab7
+susb7
+bryanb7
+ebrew7
+donbr7
+noelb7
+elizbr7
+galeb7
+davb7
+billbr7
+bribro7
+sophib7
+henryb7
+philbr7
+donb7
+amybr7
+cbrown7
+dianab7
+acjb7
+kirstb7
+lesb7
+rbrown7
+ntxrbr7
+albru7
+sbrug7
+ludob7
+vssb7
+paulbr7
+ccrb7
+michbu7
+larrbu7
+lisab7
+carolb7
+peteb7
+actb7
+klausb7
+telen7
+sbuhr7
+louisb7
+miltb7
+heathb7
+helmb7
+bburke7
+catbu7
+billb7
+dburn7
+kaeb7
+ronb7
+eileeb7
+brettb7
+laurab7
+ccfb7
+gunteb7
+gregb7
+robbu7
+robut7
+acwb7
+faribu7
+noelby7
+ntxpbw7
+eddac7
+oscarc7
+scre727
+anneca7
+jocelc7
+barbca7
+janinc7
+jasonc7
+michca7
+edc7
+pedroc7
+suzc7
+wandac7
+davc7
+carenc7
+joanc7
+leeca7
+markca7
+pcamp7
+julioc7
+mikec7
+lucisc7
+georgc7
+tomasb7
+antonc7
+stevec7
+jimc7
+dencar7
+tomc7
+ntxpc7
+annsc7
+jcarne7
+nanc7
+patcar7
+loric7
+patca7
+davic7
+kimca7
+acmc7
+wilmac7
+maurc7
+kcart7
+gregco7
+ginnyc7
+iainc7
+philc7
+riccar7
+steca7
+ntxsc7
+louisc7
+gianca7
+annec7
+jjcav7
+kencav7
+ntxgc7
+jeffca7
+palmac7
+vsmc7
+forms7
+ntxfc7
+cchad7
+cchali7
+troyc7
+cecilc7
+carmc7
+jasch7
+sophic7
+abbotc7
+inac7
+lchang7
+nicolc7
+vchang7
+clintc7
+billch7
+kench7
+lojc7
+ntxac7
+johnch7
+sorlet7
+kimc7
+weiyc7
+rrycc7
+ritac7
+pierrc7
+tomch7
+henryc7
+willch7
+rrkc7
+ntxsch7
+daich7
+yukoc7
+abm7
+acgc7
+ntxjch7
+rrtcp7
+jojoc7
+krisch7
+ronch7
+javch7
+ntxrac7
+sebasc7
+andrwc7
+gkkc7
+yvonnc7
+kingsc7
+bibich7
+ntxkch7
+philci7
+ccipol7
+paulc7
+tomci7
+waltci7
+andyc7
+bencl7
+dclark7
+ntxjcl7
+johcl7
+kclark7
+vspc7
+rrsc7
+derekc7
+johncl7
+ntxdc7
+ibmrc7
+shimaa7
+silkea7
+davida7
+chrial7
+eallen7
+susana7
+joseal7
+stefam7
+paulam7
+mettea7
+stinev7
+henrik7
+ralpha7
+robann7
+csilla7
+erica7
+marcoa7
+malia7
+ericas7
+alexat7
+maubry7
+anab7
+alexib7
+nicolb7
+urib7
+shantb7
+mikeba7
+andbar7
+beckyb7
+scottb7
+leonib7
+mariab7
+reemb7
+maddib7
+marieb7
+romab7
+ericba7
+brianb7
+josefb7
+chadb7
+ronb7
+almub7
+tinab7
+monikb7
+joelb7
+yannb7
+tarunb7
+stephb7
+veronb7
+stefab7
+bernb7
+dougbl7
+boccom7
+laurib7
+johanb7
+erinb7
+sinbo7
+michab7
+markbo7
+julb7
+kamilb7
+laurb7
+colinb7
+taniab7
+pbrad7
+jonbr7
+edb7
+annieb7
+jmbrie7
+lisab7
+joakib7
+kbrost7
+aaronb7
+paulbr7
+seambr7
+elainb7
+kaib7
+alainb7
+courtb7
+richbu7
+liambu7
+ferbu7
+hanneb7
+felicb7
+seanb7
+maytec7
+nelsc7
+armanc7
+nicoc7
+camcar7
+juanjc7
+tiziac7
+marcoc7
+tonyc7
+jpcast7
+damiac7
+ignac7
+sabinc7
+gordc7
+jaruc7
+cbchae7
+ammonc7
+jaksch7
+stephc7
+jimc7
+normac7
+fredc7
+richch7
+cheric7
+ericc7
+dmchoi7
+gabc7
+plchua7
+josefc7
+crisci7
+mikecl7
+coracl7
+danco7
+antonc7
+maxinc7
+karenc7
+cecelw7
+alberc7
+claudc7
+ajc7
+valerc7
+maxicu7
+brenc7
+wolfz7
+maxic7
+peteda7
+jacqd7
+robd7
+bobk7
+hiltom7
+katjad7
+marnod7
+serged7
+antond7
+michde7
+freddv7
+hansd7
+tdiver7
+ylvad7
+emerd7
+chelod7
+julied7
+jdrage7
+markdr7
+chridu7
+yvand7
+delphd7
+leed7
+michd7
+betind7
+stephv7
+georgd7
+owenea7
+stepea7
+owene7
+kime7
+rone7
+helene7
+annete7
+marcee7
+gabreh7
+nathfa7
+helenf7
+masudf7
+jeruf7
+brunof7
+marif7
+debbif7
+jonf7
+joachf7
+cathf7
+pennyf7
+kimbef7
+andref7
+billf7
+damfin7
+solfo7
+jacquf7
+pacof7
+antonf7
+tomfr7
+bjornf7
+mickeg7
+werng7
+mariag7
+julieg7
+georgg7
+roxang7
+beng7
+mardcg7
+rafg7
+jimeg7
+maiog7
+laurga7
+giagat7
+mattga7
+laurg7
+bgenar7
+fredge7
+lidiag7
+stefag7
+michag7
+minah7
+mgietl7
+robg7
+juligm7
+stephg7
+dannyg7
+davidg7
+xavg7
+sophig7
+danieg7
+cgold7
+merisg7
+osnatg7
+josem7
+diman7
+fredgo7
+erendg7
+luisgo7
+rosag7
+grantg7
+andygo7
+sgort7
+jeanpg7
+geneg7
+scottg7
+chiarg7
+richgr7
+bruceg7
+adamg7
+avishg7
+sandg7
+sophg7
+andreg7
+ugrob7
+sissyg7
+guenth7
+jhaas7
+erezh7
+mariha7
+tommyh7
+ahaigh7
+svenh7
+jackih7
+johnha7
+perh7
+katreh7
+gerhar7
+charlh7
+sanjh7
+robhau7
+tomokh7
+erich7
+brenhe7
+andreh7
+markuh7
+raphh7
+bernh7
+brendh7
+olafh7
+klaush7
+enidh7
+ayolh7
+jorgeh7
+bobhe7
+adrih7
+loreth7
+juanjh7
+stefah7
+gerdh7
+kathah7
+maxh7
+micheh7
+juergh7
+scotth7
+torhol7
+toddh7
+szabh7
+martah7
+shood7
+jillh7
+shannh7
+clarah7
+liborh7
+jhosty7
+chanth7
+amyhs7
+helenh7
+chhsu7
+franh7
+janyh7
+tracyh7
+dusth7
+davhug7
+bradh7
+moyah7
+jormah7
+mustai7
+kevini7
+leei7
+junkoi7
+jamest7
+karenj7
+joej7
+davidj7
+haylj7
+rosej7
+gertj7
+lanaj7
+hollyj7
+jcjan7
+isek7
+alexka7
+giselk7
+mihok7
+annemk7
+peteke7
+lizak7
+hyeyk7
+hskim7
+bking7
+timk7
+cko7
+fannyk7
+davek7
+rolank7
+craigk7
+martik7
+kotay7
+fredk7
+carolk7
+georgk7
+chrisk
+geirk7
+juliak7
+andrk7
+mank7
+kerstk7
+tckuo7
+tomokk7
+skwan7
+tony7
+markul7
+johnla7
+drewl7
+darlan7
+kenl7
+oritl7
+benjla7
+klau7
+markl7
+mlaur7
+delph7
+pyledu7
+evinl7
+dlee7
+hjlee7
+hklee7
+ivyl7
+jonsul7
+kwlee7
+sjlee7
+wglee7
+ivole7
+peterl7
+steple7
+nikil7
+serenl7
+glinah7
+lsliau7
+hannal7
+rafael7
+angell7
+roblim7
+merjal7
+billli7
+avihal7
+birdlo7
+sergel7
+alberl7
+helenl7
+carlol7
+josel7
+juanl7
+annel7
+lilyl7
+gerryl7
+vicenl7
+nathl7
+edlf7
+editl7
+derekl7
+saml7
+alphal7
+kirbyl7
+luisl7
+dickl7
+torull7
+cristm7
+mmaerk7
+leonm7
+sergm7
+brendm7
+andrwm7
+tinym7
+magdem7
+franma7
+kaym7
+nichm7
+caswem7
+arnam7
+rickm7
+carlam7
+helenm7
+farish7
+majda7
+sarya7
+monaa7
+ahmada7
+tawfad7
+dach7
+krissa7
+krisa7
+paula7
+aleena7
+debraa7
+bja7
+radams7
+stepha7
+josepa7
+colbya7
+allana7
+shola7
+dawna7
+wahaba7
+conraa7
+paulal7
+ralexa7
+ricka7
+willal7
+beckya7
+darrea7
+bkena7
+saraha7
+trical7
+callis7
+bga7
+anasal7
+emada7
+marya7
+kamila7
+marca7
+amyan7
+ericaa7
+janeta7
+tinaa7
+garant7
+franka7
+joea7
+davapp7
+denisa7
+stevar7
+briana7
+mikea7
+randya7
+rafar7
+noela7
+michas7
+davea7
+kellas7
+petash7
+alexa7
+paulat7
+teresa7
+daveat7
+heidia7
+mikeau7
+amya7
+cloa7
+akilaz7
+aprib7
+tricib7
+bruceb7
+chrisb7
+ryanba7
+tomb7
+tohoub7
+pbain7
+bwb7
+gayleb7
+branb7
+sharib7
+johnb7
+dbanks7
+rayb7
+cbarb7
+waldb7
+larrba7
+jeremb7
+mimid7
+garyba7
+bpb7
+hughb7
+kenba7
+markba7
+alib7
+koyb7
+patb7
+dbaur7
+jerib7
+alexb7
+brubax7
+gregbe7
+laurib7
+annb7
+aarob7
+johnbe7
+karbec7
+davb7
+dbeers7
+bmbe7
+teresb7
+paulbe7
+aaronb7
+griogb7
+ericb7
+tracbe7
+janeb7
+bradb7
+leslib7
+tinab7
+dougbe7
+jonib7
+joelbe7
+sharb7
+dougb7
+andreb7
+kberg7
+ryanb7
+cynthb7
+dberry7
+cherbe7
+bmarcb7
+tamib7
+gileb7
+patbe7
+mikebi7
+bryab7
+kathb7
+jamebi7
+kenb7
+billbi7
+jennib7
+danbl7
+jasonb7
+philib7
+alisbl7
+amybl7
+camib7
+alexbl7
+patbl7
+carlab7
+aprilb7
+marcbl7
+brandb7
+kristb7
+jbolic7
+garybo7
+ellenb7
+eribo7
+courtb7
+coletb7
+larryb7
+marcb7
diff --git a/private/net/svcdlls/lls/test/ct/n8.dat b/private/net/svcdlls/lls/test/ct/n8.dat
new file mode 100644
index 000000000..97a4aa494
--- /dev/null
+++ b/private/net/svcdlls/lls/test/ct/n8.dat
@@ -0,0 +1,1000 @@
+house8
+ronaar8
+rrja8
+ntxfa8
+ninaa8
+cabram8
+johnac8
+martad8
+acca8
+rrea8
+markad8
+acma8
+byrona8
+yosha8
+julioa8
+ccaa8
+bertag8
+ibmma8
+acmah8
+noriya8
+hakana8
+lisac8
+johnal8
+melisa8
+boba8
+rayall8
+deba8
+jeffal8
+rallen8
+laural8
+lallyn8
+venesa8
+annaal8
+nancya8
+dinoa8
+bain8
+marioa8
+raquea8
+vitora8
+scotam8
+renam8
+eglea8
+audrea8
+chrisa8
+ntxha8
+curta8
+davand8
+davean8
+ntxga8
+gavana8
+acja8
+jod8
+attma8
+naomia8
+royand8
+sallya8
+toman8
+matsan8
+jamiea8
+rickan8
+jana8
+jeffan8
+jiman8
+bevaok8
+takaya8
+yumia8
+carloa8
+takaar8
+shinar8
+yukaa8
+tonyar8
+hiroar8
+suza8
+kenar8
+eadarm8
+loria8
+ntxra8
+richar8
+carnst8
+arnulw8
+mayuma8
+hidas8
+richat8
+idoa8
+ntxbat8
+markat8
+kevana8
+gillea8
+micha8
+joset8
+ntxba8
+gma8
+saorib8
+vikkib8
+garyba8
+suzieb8
+michba8
+danbag8
+frankb8
+larryb8
+sbaird8
+rrkeb8
+jolynb8
+paulb8
+sbaker8
+valb8
+leeb8
+geoba8
+dougba8
+ctpsb8
+hughb8
+galb8
+leonib8
+patba8
+ntxjb8
+rrjba8
+donbar8
+jacbar8
+roberb8
+larrb8
+allpak8
+mosesb8
+donnab8
+cibelb8
+vspb8
+peteba8
+abates8
+marcb8
+pbcnz8
+jbattl8
+jimbau8
+cbaum8
+danbau8
+bilbax8
+kathbe8
+ntxab8
+celinb8
+karenb8
+philb8
+darryb8
+ctpdb8
+scre98
+ccsb8
+stepbe8
+ccjben8
+leneb8
+ntxnb8
+pbenoi8
+vscb8
+micheb8
+rrjimb8
+chadb8
+karib8
+nathb8
+dougbe8
+kimmob8
+amyb8
+decdb8
+ebenb8
+acabe8
+andb8
+ntxsbe8
+michbe8
+geralb8
+juditb8
+savlob8
+dsiwm8
+willbi8
+ellenb8
+joseb8
+blasb8
+thomb8
+annicb8
+marbin8
+ntxmb8
+daveb8
+dirkb8
+suebi8
+stefb8
+patbi8
+hilmib8
+heleb8
+rrcb8
+cblack8
+richbl8
+brynb8
+alanb8
+hyattb8
+donnbl8
+cblasi8
+sbliss8
+janebl8
+fransb8
+inface8
+lucibo8
+pboehm8
+joyb8
+pboffo8
+johnbo8
+airex8
+bobb8
+kierab8
+laurbo8
+danbol8
+cathab8
+kathbo8
+cathb8
+coreyb8
+garyb8
+dadcmb8
+nicolb8
+ismb8
+davidb8
+tomb8
+ntxsb8
+timbo8
+ronbo8
+benjab8
+toddb8
+cheryb8
+vskb8
+shellb8
+timboy8
+bboyle8
+jboyle8
+kaijab8
+myriab8
+mbrand8
+brianb8
+darreb8
+simonb8
+geneb8
+tracyb8
+lisbd8
+ibmdb8
+pbrenn8
+sineab8
+susb8
+bryanb8
+ebrew8
+donbr8
+noelb8
+elizbr8
+galeb8
+davb8
+billbr8
+bribro8
+sophib8
+henryb8
+philbr8
+donb8
+amybr8
+cbrown8
+dianab8
+acjb8
+kirstb8
+lesb8
+rbrown8
+ntxrbr8
+albru8
+sbrug8
+ludob8
+vssb8
+paulbr8
+ccrb8
+michbu8
+larrbu8
+lisab8
+carolb8
+peteb8
+actb8
+klausb8
+telen8
+sbuhr8
+louisb8
+miltb8
+heathb8
+helmb8
+bburke8
+catbu8
+billb8
+dburn8
+kaeb8
+ronb8
+eileeb8
+brettb8
+laurab8
+ccfb8
+gunteb8
+gregb8
+robbu8
+robut8
+acwb8
+faribu8
+noelby8
+ntxpbw8
+eddac8
+oscarc8
+scre828
+anneca8
+jocelc8
+barbca8
+janinc8
+jasonc8
+michca8
+edc8
+pedroc8
+suzc8
+wandac8
+davc8
+carenc8
+joanc8
+leeca8
+markca8
+pcamp8
+julioc8
+mikec8
+lucisc8
+georgc8
+tomasb8
+antonc8
+stevec8
+jimc8
+dencar8
+tomc8
+ntxpc8
+annsc8
+jcarne8
+nanc8
+patcar8
+loric8
+patca8
+davic8
+kimca8
+acmc8
+wilmac8
+maurc8
+kcart8
+gregco8
+ginnyc8
+iainc8
+philc8
+riccar8
+steca8
+ntxsc8
+louisc8
+gianca8
+annec8
+jjcav8
+kencav8
+ntxgc8
+jeffca8
+palmac8
+vsmc8
+forms8
+ntxfc8
+cchad8
+cchali8
+troyc8
+cecilc8
+carmc8
+jasch8
+sophic8
+abbotc8
+inac8
+lchang8
+nicolc8
+vchang8
+clintc8
+billch8
+kench8
+lojc8
+ntxac8
+johnch8
+sorlet8
+kimc8
+weiyc8
+rrycc8
+ritac8
+pierrc8
+tomch8
+henryc8
+willch8
+rrkc8
+ntxsch8
+daich8
+yukoc8
+abm8
+acgc8
+ntxjch8
+rrtcp8
+jojoc8
+krisch8
+ronch8
+javch8
+ntxrac8
+sebasc8
+andrwc8
+gkkc8
+yvonnc8
+kingsc8
+bibich8
+ntxkch8
+philci8
+ccipol8
+paulc8
+tomci8
+waltci8
+andyc8
+bencl8
+dclark8
+ntxjcl8
+johcl8
+kclark8
+vspc8
+rrsc8
+derekc8
+johncl8
+ntxdc8
+ibmrc8
+shimaa8
+silkea8
+davida8
+chrial8
+eallen8
+susana8
+joseal8
+stefam8
+paulam8
+mettea8
+stinev8
+henrik8
+ralpha8
+robann8
+csilla8
+erica8
+marcoa8
+malia8
+ericas8
+alexat8
+maubry8
+anab8
+alexib8
+nicolb8
+urib8
+shantb8
+mikeba8
+andbar8
+beckyb8
+scottb8
+leonib8
+mariab8
+reemb8
+maddib8
+marieb8
+romab8
+ericba8
+brianb8
+josefb8
+chadb8
+ronb8
+almub8
+tinab8
+monikb8
+joelb8
+yannb8
+tarunb8
+stephb8
+veronb8
+stefab8
+bernb8
+dougbl8
+boccom8
+laurib8
+johanb8
+erinb8
+sinbo8
+michab8
+markbo8
+julb8
+kamilb8
+laurb8
+colinb8
+taniab8
+pbrad8
+jonbr8
+edb8
+annieb8
+jmbrie8
+lisab8
+joakib8
+kbrost8
+aaronb8
+paulbr8
+seambr8
+elainb8
+kaib8
+alainb8
+courtb8
+richbu8
+liambu8
+ferbu8
+hanneb8
+felicb8
+seanb8
+maytec8
+nelsc8
+armanc8
+nicoc8
+camcar8
+juanjc8
+tiziac8
+marcoc8
+tonyc8
+jpcast8
+damiac8
+ignac8
+sabinc8
+gordc8
+jaruc8
+cbchae8
+ammonc8
+jaksch8
+stephc8
+jimc8
+normac8
+fredc8
+richch8
+cheric8
+ericc8
+dmchoi8
+gabc8
+plchua8
+josefc8
+crisci8
+mikecl8
+coracl8
+danco8
+antonc8
+maxinc8
+karenc8
+cecelw8
+alberc8
+claudc8
+ajc8
+valerc8
+maxicu8
+brenc8
+wolfz8
+maxic8
+peteda8
+jacqd8
+robd8
+bobk8
+hiltom8
+katjad8
+marnod8
+serged8
+antond8
+michde8
+freddv8
+hansd8
+tdiver8
+ylvad8
+emerd8
+chelod8
+julied8
+jdrage8
+markdr8
+chridu8
+yvand8
+delphd8
+leed8
+michd8
+betind8
+stephv8
+georgd8
+owenea8
+stepea8
+owene8
+kime8
+rone8
+helene8
+annete8
+marcee8
+gabreh8
+nathfa8
+helenf8
+masudf8
+jeruf8
+brunof8
+marif8
+debbif8
+jonf8
+joachf8
+cathf8
+pennyf8
+kimbef8
+andref8
+billf8
+damfin8
+solfo8
+jacquf8
+pacof8
+antonf8
+tomfr8
+bjornf8
+mickeg8
+werng8
+mariag8
+julieg8
+georgg8
+roxang8
+beng8
+mardcg8
+rafg8
+jimeg8
+maiog8
+laurga8
+giagat8
+mattga8
+laurg8
+bgenar8
+fredge8
+lidiag8
+stefag8
+michag8
+minah8
+mgietl8
+robg8
+juligm8
+stephg8
+dannyg8
+davidg8
+xavg8
+sophig8
+danieg8
+cgold8
+merisg8
+osnatg8
+josem8
+diman8
+fredgo8
+erendg8
+luisgo8
+rosag8
+grantg8
+andygo8
+sgort8
+jeanpg8
+geneg8
+scottg8
+chiarg8
+richgr8
+bruceg8
+adamg8
+avishg8
+sandg8
+sophg8
+andreg8
+ugrob8
+sissyg8
+guenth8
+jhaas8
+erezh8
+mariha8
+tommyh8
+ahaigh8
+svenh8
+jackih8
+johnha8
+perh8
+katreh8
+gerhar8
+charlh8
+sanjh8
+robhau8
+tomokh8
+erich8
+brenhe8
+andreh8
+markuh8
+raphh8
+bernh8
+brendh8
+olafh8
+klaush8
+enidh8
+ayolh8
+jorgeh8
+bobhe8
+adrih8
+loreth8
+juanjh8
+stefah8
+gerdh8
+kathah8
+maxh8
+micheh8
+juergh8
+scotth8
+torhol8
+toddh8
+szabh8
+martah8
+shood8
+jillh8
+shannh8
+clarah8
+liborh8
+jhosty8
+chanth8
+amyhs8
+helenh8
+chhsu8
+franh8
+janyh8
+tracyh8
+dusth8
+davhug8
+bradh8
+moyah8
+jormah8
+mustai8
+kevini8
+leei8
+junkoi8
+jamest8
+karenj8
+joej8
+davidj8
+haylj8
+rosej8
+gertj8
+lanaj8
+hollyj8
+jcjan8
+isek8
+alexka8
+giselk8
+mihok8
+annemk8
+peteke8
+lizak8
+hyeyk8
+hskim8
+bking8
+timk8
+cko8
+fannyk8
+davek8
+rolank8
+craigk8
+martik8
+kotay8
+fredk8
+carolk8
+georgk8
+chrisk
+geirk8
+juliak8
+andrk8
+mank8
+kerstk8
+tckuo8
+tomokk8
+skwan8
+tony8
+markul8
+johnla8
+drewl8
+darlan8
+kenl8
+oritl8
+benjla8
+klau8
+markl8
+mlaur8
+delph8
+pyledu8
+evinl8
+dlee8
+hjlee8
+hklee8
+ivyl8
+jonsul8
+kwlee8
+sjlee8
+wglee8
+ivole8
+peterl8
+steple8
+nikil8
+serenl8
+glinah8
+lsliau8
+hannal8
+rafael8
+angell8
+roblim8
+merjal8
+billli8
+avihal8
+birdlo8
+sergel8
+alberl8
+helenl8
+carlol8
+josel8
+juanl8
+annel8
+lilyl8
+gerryl8
+vicenl8
+nathl8
+edlf8
+editl8
+derekl8
+saml8
+alphal8
+kirbyl8
+luisl8
+dickl8
+torull8
+cristm8
+mmaerk8
+leonm8
+sergm8
+brendm8
+andrwm8
+tinym8
+magdem8
+franma8
+kaym8
+nichm8
+caswem8
+arnam8
+rickm8
+carlam8
+helenm8
+farish8
+majda8
+sarya8
+monaa8
+ahmada8
+tawfad8
+dach8
+krissa8
+krisa8
+paula8
+aleena8
+debraa8
+bja8
+radams8
+stepha8
+josepa8
+colbya8
+allana8
+shola8
+dawna8
+wahaba8
+conraa8
+paulal8
+ralexa8
+ricka8
+willal8
+beckya8
+darrea8
+bkena8
+saraha8
+trical8
+callis8
+bga8
+anasal8
+emada8
+marya8
+kamila8
+marca8
+amyan8
+ericaa8
+janeta8
+tinaa8
+garant8
+franka8
+joea8
+davapp8
+denisa8
+stevar8
+briana8
+mikea8
+randya8
+rafar8
+noela8
+michas8
+davea8
+kellas8
+petash8
+alexa8
+paulat8
+teresa8
+daveat8
+heidia8
+mikeau8
+amya8
+cloa8
+akilaz8
+aprib8
+tricib8
+bruceb8
+chrisb8
+ryanba8
+tomb8
+tohoub8
+pbain8
+bwb8
+gayleb8
+branb8
+sharib8
+johnb8
+dbanks8
+rayb8
+cbarb8
+waldb8
+larrba8
+jeremb8
+mimid8
+garyba8
+bpb8
+hughb8
+kenba8
+markba8
+alib8
+koyb8
+patb8
+dbaur8
+jerib8
+alexb8
+brubax8
+gregbe8
+laurib8
+annb8
+aarob8
+johnbe8
+karbec8
+davb8
+dbeers8
+bmbe8
+teresb8
+paulbe8
+aaronb8
+griogb8
+ericb8
+tracbe8
+janeb8
+bradb8
+leslib8
+tinab8
+dougbe8
+jonib8
+joelbe8
+sharb8
+dougb8
+andreb8
+kberg8
+ryanb8
+cynthb8
+dberry8
+cherbe8
+bmarcb8
+tamib8
+gileb8
+patbe8
+mikebi8
+bryab8
+kathb8
+jamebi8
+kenb8
+billbi8
+jennib8
+danbl8
+jasonb8
+philib8
+alisbl8
+amybl8
+camib8
+alexbl8
+patbl8
+carlab8
+aprilb8
+marcbl8
+brandb8
+kristb8
+jbolic8
+garybo8
+ellenb8
+eribo8
+courtb8
+coletb8
+larryb8
+marcb8
diff --git a/private/net/svcdlls/lls/test/ct/n9.dat b/private/net/svcdlls/lls/test/ct/n9.dat
new file mode 100644
index 000000000..11f601fd5
--- /dev/null
+++ b/private/net/svcdlls/lls/test/ct/n9.dat
@@ -0,0 +1,1000 @@
+house9
+ronaar9
+rrja9
+ntxfa9
+ninaa9
+cabram9
+johnac9
+martad9
+acca9
+rrea9
+markad9
+acma9
+byrona9
+yosha9
+julioa9
+ccaa9
+bertag9
+ibmma9
+acmah9
+noriya9
+hakana9
+lisac9
+johnal9
+melisa9
+boba9
+rayall9
+deba9
+jeffal9
+rallen9
+laural9
+lallyn9
+venesa9
+annaal9
+nancya9
+dinoa9
+bain9
+marioa9
+raquea9
+vitora9
+scotam9
+renam9
+eglea9
+audrea9
+chrisa9
+ntxha9
+curta9
+davand9
+davean9
+ntxga9
+gavana9
+acja9
+jod9
+attma9
+naomia9
+royand9
+sallya9
+toman9
+matsan9
+jamiea9
+rickan9
+jana9
+jeffan9
+jiman9
+bevaok9
+takaya9
+yumia9
+carloa9
+takaar9
+shinar9
+yukaa9
+tonyar9
+hiroar9
+suza9
+kenar9
+eadarm9
+loria9
+ntxra9
+richar9
+carnst9
+arnulw9
+mayuma9
+hidas9
+richat9
+idoa9
+ntxbat9
+markat9
+kevana9
+gillea9
+micha9
+joset9
+ntxba9
+gma9
+saorib9
+vikkib9
+garyba9
+suzieb9
+michba9
+danbag9
+frankb9
+larryb9
+sbaird9
+rrkeb9
+jolynb9
+paulb9
+sbaker9
+valb9
+leeb9
+geoba9
+dougba9
+ctpsb9
+hughb9
+galb9
+leonib9
+patba9
+ntxjb9
+rrjba9
+donbar9
+jacbar9
+roberb9
+larrb9
+allpak9
+mosesb9
+donnab9
+cibelb9
+vspb9
+peteba9
+abates9
+marcb9
+pbcnz9
+jbattl9
+jimbau9
+cbaum9
+danbau9
+bilbax9
+kathbe9
+ntxab9
+celinb9
+karenb9
+philb9
+darryb9
+ctpdb9
+scre99
+ccsb9
+stepbe9
+ccjben9
+leneb9
+ntxnb9
+pbenoi9
+vscb9
+micheb9
+rrjimb9
+chadb9
+karib9
+nathb9
+dougbe9
+kimmob9
+amyb9
+decdb9
+ebenb9
+acabe9
+andb9
+ntxsbe9
+michbe9
+geralb9
+juditb9
+savlob9
+dsiwm9
+willbi9
+ellenb9
+joseb9
+blasb9
+thomb9
+annicb9
+marbin9
+ntxmb9
+daveb9
+dirkb9
+suebi9
+stefb9
+patbi9
+hilmib9
+heleb9
+rrcb9
+cblack9
+richbl9
+brynb9
+alanb9
+hyattb9
+donnbl9
+cblasi9
+sbliss9
+janebl9
+fransb9
+inface9
+lucibo9
+pboehm9
+joyb9
+pboffo9
+johnbo9
+airex9
+bobb9
+kierab9
+laurbo9
+danbol9
+cathab9
+kathbo9
+cathb9
+coreyb9
+garyb9
+dadcmb9
+nicolb9
+ismb9
+davidb9
+tomb9
+ntxsb9
+timbo9
+ronbo9
+benjab9
+toddb9
+cheryb9
+vskb9
+shellb9
+timboy9
+bboyle9
+jboyle9
+kaijab9
+myriab9
+mbrand9
+brianb9
+darreb9
+simonb9
+geneb9
+tracyb9
+lisbd9
+ibmdb9
+pbrenn9
+sineab9
+susb9
+bryanb9
+ebrew9
+donbr9
+noelb9
+elizbr9
+galeb9
+davb9
+billbr9
+bribro9
+sophib9
+henryb9
+philbr9
+donb9
+amybr9
+cbrown9
+dianab9
+acjb9
+kirstb9
+lesb9
+rbrown9
+ntxrbr9
+albru9
+sbrug9
+ludob9
+vssb9
+paulbr9
+ccrb9
+michbu9
+larrbu9
+lisab9
+carolb9
+peteb9
+actb9
+klausb9
+telen9
+sbuhr9
+louisb9
+miltb9
+heathb9
+helmb9
+bburke9
+catbu9
+billb9
+dburn9
+kaeb9
+ronb9
+eileeb9
+brettb9
+laurab9
+ccfb9
+gunteb9
+gregb9
+robbu9
+robut9
+acwb9
+faribu9
+noelby9
+ntxpbw9
+eddac9
+oscarc9
+scre929
+anneca9
+jocelc9
+barbca9
+janinc9
+jasonc9
+michca9
+edc9
+pedroc9
+suzc9
+wandac9
+davc9
+carenc9
+joanc9
+leeca9
+markca9
+pcamp9
+julioc9
+mikec9
+lucisc9
+georgc9
+tomasb9
+antonc9
+stevec9
+jimc9
+dencar9
+tomc9
+ntxpc9
+annsc9
+jcarne9
+nanc9
+patcar9
+loric9
+patca9
+davic9
+kimca9
+acmc9
+wilmac9
+maurc9
+kcart9
+gregco9
+ginnyc9
+iainc9
+philc9
+riccar9
+steca9
+ntxsc9
+louisc9
+gianca9
+annec9
+jjcav9
+kencav9
+ntxgc9
+jeffca9
+palmac9
+vsmc9
+forms9
+ntxfc9
+cchad9
+cchali9
+troyc9
+cecilc9
+carmc9
+jasch9
+sophic9
+abbotc9
+inac9
+lchang9
+nicolc9
+vchang9
+clintc9
+billch9
+kench9
+lojc9
+ntxac9
+johnch9
+sorlet9
+kimc9
+weiyc9
+rrycc9
+ritac9
+pierrc9
+tomch9
+henryc9
+willch9
+rrkc9
+ntxsch9
+daich9
+yukoc9
+abm9
+acgc9
+ntxjch9
+rrtcp9
+jojoc9
+krisch9
+ronch9
+javch9
+ntxrac9
+sebasc9
+andrwc9
+gkkc9
+yvonnc9
+kingsc9
+bibich9
+ntxkch9
+philci9
+ccipol9
+paulc9
+tomci9
+waltci9
+andyc9
+bencl9
+dclark9
+ntxjcl9
+johcl9
+kclark9
+vspc9
+rrsc9
+derekc9
+johncl9
+ntxdc9
+ibmrc9
+shimaa9
+silkea9
+davida9
+chrial9
+eallen9
+susana9
+joseal9
+stefam9
+paulam9
+mettea9
+stinev9
+henrik9
+ralpha9
+robann9
+csilla9
+erica9
+marcoa9
+malia9
+ericas9
+alexat9
+maubry9
+anab9
+alexib9
+nicolb9
+urib9
+shantb9
+mikeba9
+andbar9
+beckyb9
+scottb9
+leonib9
+mariab9
+reemb9
+maddib9
+marieb9
+romab9
+ericba9
+brianb9
+josefb9
+chadb9
+ronb9
+almub9
+tinab9
+monikb9
+joelb9
+yannb9
+tarunb9
+stephb9
+veronb9
+stefab9
+bernb9
+dougbl9
+boccom9
+laurib9
+johanb9
+erinb9
+sinbo9
+michab9
+markbo9
+julb9
+kamilb9
+laurb9
+colinb9
+taniab9
+pbrad9
+jonbr9
+edb9
+annieb9
+jmbrie9
+lisab9
+joakib9
+kbrost9
+aaronb9
+paulbr9
+seambr9
+elainb9
+kaib9
+alainb9
+courtb9
+richbu9
+liambu9
+ferbu9
+hanneb9
+felicb9
+seanb9
+maytec9
+nelsc9
+armanc9
+nicoc9
+camcar9
+juanjc9
+tiziac9
+marcoc9
+tonyc9
+jpcast9
+damiac9
+ignac9
+sabinc9
+gordc9
+jaruc9
+cbchae9
+ammonc9
+jaksch9
+stephc9
+jimc9
+normac9
+fredc9
+richch9
+cheric9
+ericc9
+dmchoi9
+gabc9
+plchua9
+josefc9
+crisci9
+mikecl9
+coracl9
+danco9
+antonc9
+maxinc9
+karenc9
+cecelw9
+alberc9
+claudc9
+ajc9
+valerc9
+maxicu9
+brenc9
+wolfz9
+maxic9
+peteda9
+jacqd9
+robd9
+bobk9
+hiltom9
+katjad9
+marnod9
+serged9
+antond9
+michde9
+freddv9
+hansd9
+tdiver9
+ylvad9
+emerd9
+chelod9
+julied9
+jdrage9
+markdr9
+chridu9
+yvand9
+delphd9
+leed9
+michd9
+betind9
+stephv9
+georgd9
+owenea9
+stepea9
+owene9
+kime9
+rone9
+helene9
+annete9
+marcee9
+gabreh9
+nathfa9
+helenf9
+masudf9
+jeruf9
+brunof9
+marif9
+debbif9
+jonf9
+joachf9
+cathf9
+pennyf9
+kimbef9
+andref9
+billf9
+damfin9
+solfo9
+jacquf9
+pacof9
+antonf9
+tomfr9
+bjornf9
+mickeg9
+werng9
+mariag9
+julieg9
+georgg9
+roxang9
+beng9
+mardcg9
+rafg9
+jimeg9
+maiog9
+laurga9
+giagat9
+mattga9
+laurg9
+bgenar9
+fredge9
+lidiag9
+stefag9
+michag9
+minah9
+mgietl9
+robg9
+juligm9
+stephg9
+dannyg9
+davidg9
+xavg9
+sophig9
+danieg9
+cgold9
+merisg9
+osnatg9
+josem9
+diman9
+fredgo9
+erendg9
+luisgo9
+rosag9
+grantg9
+andygo9
+sgort9
+jeanpg9
+geneg9
+scottg9
+chiarg9
+richgr9
+bruceg9
+adamg9
+avishg9
+sandg9
+sophg9
+andreg9
+ugrob9
+sissyg9
+guenth9
+jhaas9
+erezh9
+mariha9
+tommyh9
+ahaigh9
+svenh9
+jackih9
+johnha9
+perh9
+katreh9
+gerhar9
+charlh9
+sanjh9
+robhau9
+tomokh9
+erich9
+brenhe9
+andreh9
+markuh9
+raphh9
+bernh9
+brendh9
+olafh9
+klaush9
+enidh9
+ayolh9
+jorgeh9
+bobhe9
+adrih9
+loreth9
+juanjh9
+stefah9
+gerdh9
+kathah9
+maxh9
+micheh9
+juergh9
+scotth9
+torhol9
+toddh9
+szabh9
+martah9
+shood9
+jillh9
+shannh9
+clarah9
+liborh9
+jhosty9
+chanth9
+amyhs9
+helenh9
+chhsu9
+franh9
+janyh9
+tracyh9
+dusth9
+davhug9
+bradh9
+moyah9
+jormah9
+mustai9
+kevini9
+leei9
+junkoi9
+jamest9
+karenj9
+joej9
+davidj9
+haylj9
+rosej9
+gertj9
+lanaj9
+hollyj9
+jcjan9
+isek9
+alexka9
+giselk9
+mihok9
+annemk9
+peteke9
+lizak9
+hyeyk9
+hskim9
+bking9
+timk9
+cko9
+fannyk9
+davek9
+rolank9
+craigk9
+martik9
+kotay9
+fredk9
+carolk9
+georgk9
+chrisk
+geirk9
+juliak9
+andrk9
+mank9
+kerstk9
+tckuo9
+tomokk9
+skwan9
+tony9
+markul9
+johnla9
+drewl9
+darlan9
+kenl9
+oritl9
+benjla9
+klau9
+markl9
+mlaur9
+delph9
+pyledu9
+evinl9
+dlee9
+hjlee9
+hklee9
+ivyl9
+jonsul9
+kwlee9
+sjlee9
+wglee9
+ivole9
+peterl9
+steple9
+nikil9
+serenl9
+glinah9
+lsliau9
+hannal9
+rafael9
+angell9
+roblim9
+merjal9
+billli9
+avihal9
+birdlo9
+sergel9
+alberl9
+helenl9
+carlol9
+josel9
+juanl9
+annel9
+lilyl9
+gerryl9
+vicenl9
+nathl9
+edlf9
+editl9
+derekl9
+saml9
+alphal9
+kirbyl9
+luisl9
+dickl9
+torull9
+cristm9
+mmaerk9
+leonm9
+sergm9
+brendm9
+andrwm9
+tinym9
+magdem9
+franma9
+kaym9
+nichm9
+caswem9
+arnam9
+rickm9
+carlam9
+helenm9
+farish9
+majda9
+sarya9
+monaa9
+ahmada9
+tawfad9
+dach9
+krissa9
+krisa9
+paula9
+aleena9
+debraa9
+bja9
+radams9
+stepha9
+josepa9
+colbya9
+allana9
+shola9
+dawna9
+wahaba9
+conraa9
+paulal9
+ralexa9
+ricka9
+willal9
+beckya9
+darrea9
+bkena9
+saraha9
+trical9
+callis9
+bga9
+anasal9
+emada9
+marya9
+kamila9
+marca9
+amyan9
+ericaa9
+janeta9
+tinaa9
+garant9
+franka9
+joea9
+davapp9
+denisa9
+stevar9
+briana9
+mikea9
+randya9
+rafar9
+noela9
+michas9
+davea9
+kellas9
+petash9
+alexa9
+paulat9
+teresa9
+daveat9
+heidia9
+mikeau9
+amya9
+cloa9
+akilaz9
+aprib9
+tricib9
+bruceb9
+chrisb9
+ryanba9
+tomb9
+tohoub9
+pbain9
+bwb9
+gayleb9
+branb9
+sharib9
+johnb9
+dbanks9
+rayb9
+cbarb9
+waldb9
+larrba9
+jeremb9
+mimid9
+garyba9
+bpb9
+hughb9
+kenba9
+markba9
+alib9
+koyb9
+patb9
+dbaur9
+jerib9
+alexb9
+brubax9
+gregbe9
+laurib9
+annb9
+aarob9
+johnbe9
+karbec9
+davb9
+dbeers9
+bmbe9
+teresb9
+paulbe9
+aaronb9
+griogb9
+ericb9
+tracbe9
+janeb9
+bradb9
+leslib9
+tinab9
+dougbe9
+jonib9
+joelbe9
+sharb9
+dougb9
+andreb9
+kberg9
+ryanb9
+cynthb9
+dberry9
+cherbe9
+bmarcb9
+tamib9
+gileb9
+patbe9
+mikebi9
+bryab9
+kathb9
+jamebi9
+kenb9
+billbi9
+jennib9
+danbl9
+jasonb9
+philib9
+alisbl9
+amybl9
+camib9
+alexbl9
+patbl9
+carlab9
+aprilb9
+marcbl9
+brandb9
+kristb9
+jbolic9
+garybo9
+ellenb9
+eribo9
+courtb9
+coletb9
+larryb9
+marcb9
diff --git a/private/net/svcdlls/lls/test/ct/p1.bat b/private/net/svcdlls/lls/test/ct/p1.bat
new file mode 100644
index 000000000..cd59f24c0
--- /dev/null
+++ b/private/net/svcdlls/lls/test/ct/p1.bat
@@ -0,0 +1,198 @@
+llscmd add user 4user FORT510A.DOS 1.0
+llscmd add user 4user FORTRN32.NT 1.0
+llscmd add user 4user VC21.NT 1.0
+llscmd add user 4user cdex 1.0
+llscmd add user 4user CLIENTS.NET 1.0
+llscmd add user 4user CMSRVWS.11 1.0
+llscmd add user 1user wlo09 1.0
+llscmd add user 2user wlo10 1.0
+llscmd add user 2user ACCSOLPK.WIN 1.0
+llscmd add user 2user ACCUPTLS.WIN 1.0
+llscmd add user 2user ADT20.WIN 1.0
+llscmd add user 1user Money 1.0
+llscmd add user 2user FOXPR26A.WIN 1.0
+llscmd add user 2user FOXPRO26.UNX 1.0
+llscmd add user 2user WKGRP311.DOS 1.0
+llscmd add user 4user DRAW10.WIN 1.0
+llscmd add user 4user COBOL500 1.0
+llscmd add user 4user FORT510A.DOS 1.0
+llscmd add user 4user FORTRN32.NT 1.0
+llscmd add user 4user VC21.NT 1.0
+llscmd add user 4user cdex 1.0
+llscmd add user 4user CLIENTS.NET 1.0
+llscmd add user 4user CMSRVWS.11 1.0
+llscmd add user 1user Mail 1.0
+llscmd add user 1user MONEY30.WIN 1.0
+llscmd add user 1user MP420A.DOS 1.0
+llscmd add user 1user MSMAIL.ALL 1.0
+llscmd add user 1user Win32 1.0
+llscmd add user 1user Win32s 1.0
+llscmd add user 1user Excel 1.0
+llscmd add user 1user Word 1.0
+llscmd add user 1user ARCADE10.WIN 1.0
+llscmd add user 1user ARTGALL.WIN 1.0
+llscmd add user 1user VCP100.WIN 1.0
+llscmd add user 1user WINLIB 1.0
+llscmd add user 1user ACCESS20.WIN 1.0
+llscmd add user 1user FORTRN32.WIN 1.0
+llscmd add user 1user IMSL.NT 1.0
+llscmd add user 1user IMSL32.DOS 1.0
+llscmd add user 1user IMSL51.DOS 1.0
+llscmd add user 1user Chicago 1.0
+llscmd add user 1user OFFDK10.WIN 1.0
+llscmd add user 5user TCPIP.ALL 1.0
+llscmd add user 5user TCPUTIL1.0 1.0
+llscmd add user 5user VFW11.WIN 1.0
+llscmd add user 5user WRESKIT2.00 1.0
+llscmd add user 5user WSS20.WIN 1.0
+llscmd add user 5user MacWord 1.0
+llscmd add user 5user PowerPoint 1.0
+llscmd add user 1user FONTPACK.WIN 1.0
+llscmd add user 1user FONTPAK2.WIN 1.0
+llscmd add user 1user Windows95 1.0
+llscmd add user 1user Access 1.0
+llscmd add user 1user MASM611 1.0
+llscmd add user 1user MFCKIT 1.0
+llscmd add user 1user mt300a.all 1.0
+llscmd add user 1user QWGRAPH 1.0
+llscmd add user 1user VB10.DOS 1.0
+llscmd add user 1user CBT 1.0
+llscmd add user 1user EISPAK11.WIN 1.0
+llscmd add user 1user ENCART95.WIN 1.0
+llscmd add user 1user EXCEL30.PM 1.0
+llscmd add user 1user VB30.WIN 1.0
+llscmd add user 1user MASM 1.0
+llscmd add user 2user OLE 1.0
+llscmd add user 2user Chart 1.0
+llscmd add user 2user Encarta 1.0
+llscmd add user 2user FlightSim 1.0
+llscmd add user 2user Paradox 1.0
+llscmd add user 2user VC150.WIN 1.0
+llscmd add user 2user VC20.MAC 1.0
+llscmd add user 2user IMAGER10.WIN 1.0
+llscmd add user 2user LRND300.DOS 1.0
+llscmd add user 2user MMGOLF10.WIN 1.0
+llscmd add user 2user MOM.NT 1.0
+llscmd add user 2user MSDOS622.DOS 1.0
+llscmd add user 2user MSKBD1.ALL 1.0
+llscmd add user 2user MSLOGO.TTF 1.0
+llscmd add user 2user MSSMS10 1.0
+llscmd add user 2user MOM42.WIN 1.0
+llscmd add user 2user SNA 1.0
+llscmd add user 1user Exchange 1.0
+llscmd add user 2user MS_EFORM.WIN 1.0
+llscmd add user 1user ODAKIT 1.0
+llscmd add user 2user ODBC210.WIN 1.0
+llscmd add user 1user VC20.NT 1.0
+llscmd add user 2user BOB.WIN 1.0
+llscmd add user 1user BOOK94.WIN 1.0
+llscmd add user 2user BOWEP.WIN 1.0
+llscmd add user 2user EXCEL50.NT 1.0
+llscmd add user 2user EXCEL50C.WIN 1.0
+llscmd add user 2user FLTSIM5a.DOS 1.0
+llscmd add user 2user FOXPR26A.DOS 1.0
+llscmd add user 2user GS100.DOS 1.0
+llscmd add user 2user ODBCDDP2.NT 1.0
+llscmd add user 2user ODBCDDP2.WIN 1.0
+llscmd add user 2user OFF42C.WIN 1.0
+llscmd add user 2user OFF43c.WIN 1.0
+llscmd add user 2user OFFAST10.WIN 1.0
+llscmd add user 1user HPFNTSET.WIN 1.0
+llscmd add user 1user hyper.100 1.0
+llscmd add user 2user IMSL.NT 1.0
+llscmd add user 3user IMSL32.DOS 1.0
+llscmd add user 3user LM22 1.0
+llscmd add user 3user LMRES10.OS2 1.0
+llscmd add user 3user LMSFMAC1.0A 1.0
+llscmd add user 3user OFFICE.NT 1.0
+llscmd add user 3user OLE202.WIN 1.0
+llscmd add user 3user ONLN200.WIN 1.0
+llscmd add user 3user PROJ40.WIN 1.0
+llscmd add user 3user MSTCPIP.ALL 1.0
+llscmd add user 3user MVWR200.WIN 1.0
+llscmd add user 3user NETMON 1.0
+llscmd add user 3user OS2-1.31 1.0
+llscmd add user 3user PMSUB35.NT 1.0
+llscmd add user 3user RAS11.LM 1.0
+llscmd add user 3user PUB20a.WIN 1.0
+llscmd add user 3user PUBDSIGN.WIN 1.0
+llscmd add user 3user SAMPLER 1.0
+llscmd add user 3user SCENES20.WIN 1.0
+llscmd add user 3user SCHED10A.WIN 1.0
+llscmd add user 3user SGML10.WIN 1.0
+llscmd add user 3user SLM.ALL 1.0
+llscmd add user 3user SNDBIT10.WIN 1.0
+llscmd add user 3user SPACE10.DOS 1.0
+llscmd add user 3user SS30.DOS 1.0
+llscmd add user 3user ss31std.win 1.0
+llscmd add user 3user WA100.WIN 1.0
+llscmd add user 5user WMDLRSDK.WIN 1.0
+llscmd add user 5user GOLF20.WIN 1.0
+llscmd add user 5user GRAPH50.WIN 1.0
+llscmd add user 5user GREETING.WIN 1.0
+llscmd add user 5user VC20A.NT 1.0
+llscmd add user 3user WEP10B.WIN 1.0
+llscmd add user 3user WEP20.WIN 1.0
+llscmd add user 3user WEP30.WIN 1.0
+llscmd add user 3user WEP40.WIN 1.0
+llscmd add user 3user WGTPLT11.WIN 1.0
+llscmd add user 3user WORD11B.PM 1.0
+llscmd add user 3user WORD6.DOS 1.0
+llscmd add user 3user WORD60.NT 1.0
+llscmd add user 3user WORD60C.WIN 1.0
+llscmd add user 3user word6cnv.nt 1.0
+llscmd add user 3user WORD6CNV.WIN 1.0
+llscmd add user 3user WORDVW60.WIN 1.0
+llscmd add user 3user WORKS30.DOS 1.0
+llscmd add user 4user DOS 1.0
+llscmd add user 4user C 1.0
+llscmd add user 4user CHART300.DOS 1.0
+llscmd add user 4user CINEMA94.WIN 1.0
+llscmd add user 4user DELTA10A.WIN 1.0
+llscmd add user 4user DINOSAUR.WIN 1.0
+llscmd add user 4user DCIDDK10 1.0
+llscmd add user 4user PPT40c.WIN 1.0
+llscmd add user 4user PROFIT1B.WIN 1.0
+llscmd add user 4user PROJ40.DOS 1.0
+llscmd add user 4user LMUNIX 1.0
+llscmd add user 4user MOUSE10a.ALL 1.0
+llscmd add user 4user MSD211.DOS 1.0
+llscmd add user 4user RESKIT.NT 1.0
+llscmd add user 4user SNA21.NT 1.0
+llscmd add user 4user SQL.NT 1.0
+llscmd add user 4user SQL42B.OS2 1.0
+llscmd add user 4user SQLRES10.OS2 1.0
+llscmd add user 4user WFW311.WIN 1.0
+llscmd add user 3user WORKS30B.WIN 1.0
+llscmd add user 3user WPP310.WIN 1.0
+llscmd add user 3user WRITER10.WIN 1.0
+llscmd add user 4user 3com 1.0
+llscmd add user 4user BLPNT901.ALL 1.0
+llscmd add user 4user ARTIST10.WIN 1.0
+llscmd add user 4user BASEBL94.WIN 1.0
+llscmd add user 4user BA331.DOS 1.0
+llscmd add user 4user OS/2 1.0
+llscmd add user 4user NetWare 1.0
+llscmd add user 4user WFWCONN.WIN 1.0
+llscmd add user 4user WIN31 1.0
+llscmd add user 4user WING10.WIN 1.0
+llscmd add user 4user winlogin.win 1.0
+llscmd add user 5user WINNT.NT 1.0
+llscmd add user 5user WINNT35.SRV 1.0
+llscmd add user 5user WINNT35.WKS 1.0
+llscmd add user 5user wpen100a.win 1.0
+llscmd add user 5user WPS.WIN 1.0
+llscmd add user 1user wlo09 1.0
+llscmd add user 2user wlo10 1.0
+llscmd add user 2user ACCSOLPK.WIN 1.0
+llscmd add user 2user ACCUPTLS.WIN 1.0
+llscmd add user 2user ADT20.WIN 1.0
+llscmd add user 1user Money 1.0
+llscmd add user 1user Schedule+ 1.0
+llscmd add user 5user TAPI10.WIN 1.0
+llscmd add user 5user BoundsChecker 1.0
+llscmd add user 5user Quattro 1.0
+llscmd add user 5user WordPerfect 1.0
+llscmd add user 5user Office 1.0
+llscmd add user 5user PerfectOffice 1.0
+llscmd add user 5user SQL 1.0
diff --git a/private/net/svcdlls/lls/test/ct/p2.bat b/private/net/svcdlls/lls/test/ct/p2.bat
new file mode 100644
index 000000000..83f3fa676
--- /dev/null
+++ b/private/net/svcdlls/lls/test/ct/p2.bat
@@ -0,0 +1,186 @@
+llscmd add user 1user Money 1.3
+llscmd add user 1user Schedule+ 1.3
+llscmd add user 1user Mail 1.3
+llscmd add user 1user MONEY30.WIN 1.3
+llscmd add user 1user MP420A.DOS 1.3
+llscmd add user 1user MSMAIL.ALL 1.3
+llscmd add user 1user Win32 1.3
+llscmd add user 1user Win32s 1.3
+llscmd add user 1user Excel 1.3
+llscmd add user 1user Word 1.3
+llscmd add user 1user ARCADE10.WIN 1.3
+llscmd add user 1user ARTGALL.WIN 1.3
+llscmd add user 1user VCP100.WIN 1.3
+llscmd add user 2user WINLIB 1.3
+llscmd add user 2user ACCESS20.WIN 1.3
+llscmd add user 2user FORTRN32.WIN 1.3
+llscmd add user 2user IMSL.NT 1.3
+llscmd add user 2user IMSL32.DOS 1.3
+llscmd add user 2user IMSL51.DOS 1.3
+llscmd add user 2user Chicago 1.3
+llscmd add user 2user Windows95 1.3
+llscmd add user 2user Access 1.3
+llscmd add user 2user MASM611 1.3
+llscmd add user 2user MFCKIT 1.3
+llscmd add user 2user mt300a.all 1.3
+llscmd add user 2user QWGRAPH 1.3
+llscmd add user 2user VB10.DOS 1.3
+llscmd add user 2user CBT 1.3
+llscmd add user 2user EISPAK11.WIN 1.3
+llscmd add user 2user ENCART95.WIN 1.3
+llscmd add user 2user EXCEL30.PM 1.3
+llscmd add user 2user VB30.WIN 1.3
+llscmd add user 2user MASM 1.3
+llscmd add user 2user OLE 1.3
+llscmd add user 2user Chart 1.3
+llscmd add user 2user Encarta 1.3
+llscmd add user 2user FlightSim 1.3
+llscmd add user 2user Paradox 1.3
+llscmd add user 2user VC150.WIN 1.3
+llscmd add user 2user VC20.MAC 1.3
+llscmd add user 2user IMAGER10.WIN 1.3
+llscmd add user 2user LRND300.DOS 1.3
+llscmd add user 2user MMGOLF10.WIN 1.3
+llscmd add user 3user MOM.NT 1.3
+llscmd add user 3user MSDOS622.DOS 1.3
+llscmd add user 3user MSKBD1.ALL 1.3
+llscmd add user 3user MSLOGO.TTF 1.3
+llscmd add user 3user MSSMS10 1.3
+llscmd add user 3user MOM42.WIN 1.3
+llscmd add user 3user SNA 1.3
+llscmd add user 3user Exchange 1.3
+llscmd add user 3user MS_EFORM.WIN 1.3
+llscmd add user 4user ODAKIT 1.3
+llscmd add user 4user ODBC210.WIN 1.3
+llscmd add user 4user VC20.NT 1.3
+llscmd add user 4user BOB.WIN 1.3
+llscmd add user 4user BOOK94.WIN 1.3
+llscmd add user 4user BOWEP.WIN 1.3
+llscmd add user 4user EXCEL50.NT 1.3
+llscmd add user 4user EXCEL50C.WIN 1.3
+llscmd add user 4user FLTSIM5a.DOS 1.3
+llscmd add user 4user FOXPR26A.DOS 1.3
+llscmd add user 4user FOXPR26A.WIN 1.3
+llscmd add user 4user FOXPRO26.UNX 1.3
+llscmd add user 4user WKGRP311.DOS 1.3
+llscmd add user 5user wlo09 1.3
+llscmd add user 5user wlo10 1.3
+llscmd add user 5user ACCSOLPK.WIN 1.3
+llscmd add user 5user ACCUPTLS.WIN 1.3
+llscmd add user 5user ADT20.WIN 1.3
+llscmd add user 5user GS100.DOS 1.3
+llscmd add user 5user ODBCDDP2.NT 1.3
+llscmd add user 5user ODBCDDP2.WIN 1.3
+llscmd add user 5user OFF42C.WIN 1.3
+llscmd add user 5user OFF43c.WIN 1.3
+llscmd add user 5user OFFAST10.WIN 1.3
+llscmd add user 5user OFFDK10.WIN 1.3
+llscmd add user 5user FONTPACK.WIN 1.3
+llscmd add user 5user FONTPAK2.WIN 1.3
+llscmd add user 5user HPFNTSET.WIN 1.3
+llscmd add user 5user hyper.100 1.3
+llscmd add user 5user IMSL.NT 1.3
+llscmd add user 5user IMSL32.DOS 1.3
+llscmd add user 5user LM22 1.3
+llscmd add user 5user LMRES10.OS2 1.3
+llscmd add user 5user LMSFMAC1.3A 1.3
+llscmd add user 5user OFFICE.NT 1.3
+llscmd add user 5user OLE202.WIN 1.3
+llscmd add user 5user ONLN200.WIN 1.3
+llscmd add user 5user PROJ40.WIN 1.3
+llscmd add user 5user MSTCPIP.ALL 1.3
+llscmd add user 5user MVWR200.WIN 1.3
+llscmd add user 5user NETMON 1.3
+llscmd add user 5user OS2-1.31 1.3
+llscmd add user 5user PMSUB35.NT 1.3
+llscmd add user 5user RAS11.LM 1.3
+llscmd add user 5user PUB20a.WIN 1.3
+llscmd add user 5user PUBDSIGN.WIN 1.3
+llscmd add user 5user SAMPLER 1.3
+llscmd add user 5user SCENES20.WIN 1.3
+llscmd add user 5user SCHED10A.WIN 1.3
+llscmd add user 5user SGML10.WIN 1.3
+llscmd add user 5user SLM.ALL 1.3
+llscmd add user 5user SNDBIT10.WIN 1.3
+llscmd add user 5user SPACE10.DOS 1.3
+llscmd add user 5user SS30.DOS 1.3
+llscmd add user 4user ss31std.win 1.3
+llscmd add user 4user WA100.WIN 1.3
+llscmd add user 4user WEP10B.WIN 1.3
+llscmd add user 1user WEP20.WIN 1.3
+llscmd add user 1user WEP30.WIN 1.3
+llscmd add user 1user WEP40.WIN 1.3
+llscmd add user 1user WGTPLT11.WIN 1.3
+llscmd add user 1user WORD11B.PM 1.3
+llscmd add user 1user WORD6.DOS 1.3
+llscmd add user 1user WORD60.NT 1.3
+llscmd add user 1user WORD60C.WIN 1.3
+llscmd add user 1user word6cnv.nt 1.3
+llscmd add user 1user WORD6CNV.WIN 1.3
+llscmd add user 1user WORDVW60.WIN 1.3
+llscmd add user 1user WORKS30.DOS 1.3
+llscmd add user 1user WORKS30B.WIN 1.3
+llscmd add user 1user WPP310.WIN 1.3
+llscmd add user 1user WRITER10.WIN 1.3
+llscmd add user 1user 3com 1.3
+llscmd add user 3user BLPNT901.ALL 1.3
+llscmd add user 3user ARTIST10.WIN 1.3
+llscmd add user 3user BASEBL94.WIN 1.3
+llscmd add user 3user BA331.DOS 1.3
+llscmd add user 3user OS/2 1.3
+llscmd add user 3user NetWare 1.3
+llscmd add user 4user DOS 1.3
+llscmd add user 4user C 1.3
+llscmd add user 4user CHART300.DOS 1.3
+llscmd add user 4user CINEMA94.WIN 1.3
+llscmd add user 4user DELTA10A.WIN 1.3
+llscmd add user 4user DINOSAUR.WIN 1.3
+llscmd add user 4user DRAW10.WIN 1.3
+llscmd add user 5user COBOL500 1.3
+llscmd add user 5user FORT510A.DOS 1.3
+llscmd add user 5user FORTRN32.NT 1.3
+llscmd add user 5user VC21.NT 1.3
+llscmd add user 1user cdex 1.3
+llscmd add user 1user CLIENTS.NET 1.3
+llscmd add user 1user CMSRVWS.11 1.3
+llscmd add user 1user DCIDDK10 1.3
+llscmd add user 1user PPT40c.WIN 1.3
+llscmd add user 1user PROFIT1B.WIN 1.3
+llscmd add user 1user PROJ40.DOS 1.3
+llscmd add user 1user LMUNIX 1.3
+llscmd add user 1user MOUSE10a.ALL 1.3
+llscmd add user 1user MSD211.DOS 1.3
+llscmd add user 1user RESKIT.NT 1.3
+llscmd add user 1user SNA21.NT 1.3
+llscmd add user 1user SQL.NT 1.3
+llscmd add user 1user SQL42B.OS2 1.3
+llscmd add user 1user SQLRES10.OS2 1.3
+llscmd add user 1user WFW311.WIN 1.3
+llscmd add user 1user WFWCONN.WIN 1.3
+llscmd add user 1user WIN31 1.3
+llscmd add user 2user WING10.WIN 1.3
+llscmd add user 2user winlogin.win 1.3
+llscmd add user 2user WINNT.NT 1.3
+llscmd add user 2user WINNT35.SRV 1.3
+llscmd add user 2user WMDLRSDK.WIN 1.3
+llscmd add user 2user GOLF20.WIN 1.3
+llscmd add user 2user GRAPH50.WIN 1.3
+llscmd add user 2user GREETING.WIN 1.3
+llscmd add user 2user VC20A.NT 1.3
+llscmd add user 2user WINNT35.WKS 1.3
+llscmd add user 2user wpen100a.win 1.3
+llscmd add user 2user WPS.WIN 1.3
+llscmd add user 2user TAPI10.WIN 1.3
+llscmd add user 2user TCPIP.ALL 1.3
+llscmd add user 2user TCPUTIL1.3 1.3
+llscmd add user 2user VFW11.WIN 1.3
+llscmd add user 2user WRESKIT2.00 1.3
+llscmd add user 2user WSS20.WIN 1.3
+llscmd add user 2user MacWord 1.3
+llscmd add user 2user PowerPoint 1.3
+llscmd add user 2user BoundsChecker 1.3
+llscmd add user 2user Quattro 1.3
+llscmd add user 2user WordPerfect 1.3
+llscmd add user 2user Office 1.3
+llscmd add user 2user PerfectOffice 1.3
+llscmd add user 2user SQL 1.3
diff --git a/private/net/svcdlls/lls/test/ct/p3.bat b/private/net/svcdlls/lls/test/ct/p3.bat
new file mode 100644
index 000000000..99b5cd9e1
--- /dev/null
+++ b/private/net/svcdlls/lls/test/ct/p3.bat
@@ -0,0 +1,186 @@
+llscmd add user 1user Money 3.1
+llscmd add user 1user Schedule+ 3.1
+llscmd add user 1user Mail 3.1
+llscmd add user 1user MONEY30.WIN 3.1
+llscmd add user 1user MP420A.DOS 3.1
+llscmd add user 1user MSMAIL.ALL 3.1
+llscmd add user 1user Win32 3.1
+llscmd add user 1user Win32s 3.1
+llscmd add user 1user Excel 3.1
+llscmd add user 1user Word 3.1
+llscmd add user 1user ARCADE10.WIN 3.1
+llscmd add user 1user ARTGALL.WIN 3.1
+llscmd add user 1user VCP100.WIN 3.1
+llscmd add user 1user WINLIB 3.1
+llscmd add user 1user ACCESS20.WIN 3.1
+llscmd add user 1user FORTRN32.WIN 3.1
+llscmd add user 1user IMSL.NT 3.1
+llscmd add user 1user IMSL32.DOS 3.1
+llscmd add user 1user IMSL51.DOS 3.1
+llscmd add user 1user Chicago 3.1
+llscmd add user 1user Windows95 3.1
+llscmd add user 1user Access 3.1
+llscmd add user 1user MASM611 3.1
+llscmd add user 1user MFCKIT 3.1
+llscmd add user 2user mt300a.all 3.1
+llscmd add user 2user QWGRAPH 3.1
+llscmd add user 2user VB10.DOS 3.1
+llscmd add user 2user CBT 3.1
+llscmd add user 2user EISPAK11.WIN 3.1
+llscmd add user 2user ENCART95.WIN 3.1
+llscmd add user 2user EXCEL30.PM 3.1
+llscmd add user 2user VB30.WIN 3.1
+llscmd add user 2user MASM 3.1
+llscmd add user 2user OLE 3.1
+llscmd add user 2user Chart 3.1
+llscmd add user 2user Encarta 3.1
+llscmd add user 3user FlightSim 3.1
+llscmd add user 3user Paradox 3.1
+llscmd add user 3user VC150.WIN 3.1
+llscmd add user 3user VC20.MAC 3.1
+llscmd add user 3user IMAGER10.WIN 3.1
+llscmd add user 3user LRND300.DOS 3.1
+llscmd add user 3user MMGOLF10.WIN 3.1
+llscmd add user 3user MOM.NT 3.1
+llscmd add user 3user MSDOS622.DOS 3.1
+llscmd add user 3user MSKBD1.ALL 3.1
+llscmd add user 3user MSLOGO.TTF 3.1
+llscmd add user 3user MSSMS10 3.1
+llscmd add user 3user MOM42.WIN 3.1
+llscmd add user 3user SNA 3.1
+llscmd add user 3user Exchange 3.1
+llscmd add user 3user MS_EFORM.WIN 3.1
+llscmd add user 3user ODAKIT 3.1
+llscmd add user 3user ODBC210.WIN 3.1
+llscmd add user 3user VC20.NT 3.1
+llscmd add user 3user BOB.WIN 3.1
+llscmd add user 3user BOOK94.WIN 3.1
+llscmd add user 3user BOWEP.WIN 3.1
+llscmd add user 3user EXCEL50.NT 3.1
+llscmd add user 3user EXCEL50C.WIN 3.1
+llscmd add user 3user FLTSIM5a.DOS 3.1
+llscmd add user 3user FOXPR26A.DOS 3.1
+llscmd add user 3user FOXPR26A.WIN 3.1
+llscmd add user 3user FOXPRO26.UNX 3.1
+llscmd add user 3user WKGRP311.DOS 3.1
+llscmd add user 3user wlo09 3.1
+llscmd add user 3user wlo10 3.1
+llscmd add user 3user ACCSOLPK.WIN 3.1
+llscmd add user 3user ACCUPTLS.WIN 3.1
+llscmd add user 3user ADT20.WIN 3.1
+llscmd add user 3user GS100.DOS 3.1
+llscmd add user 1user ODBCDDP2.NT 3.1
+llscmd add user 1user ODBCDDP2.WIN 3.1
+llscmd add user 1user OFF42C.WIN 3.1
+llscmd add user 1user OFF43c.WIN 3.1
+llscmd add user 1user OFFAST10.WIN 3.1
+llscmd add user 1user OFFDK10.WIN 3.1
+llscmd add user 1user FONTPACK.WIN 3.1
+llscmd add user 1user FONTPAK2.WIN 3.1
+llscmd add user 1user HPFNTSET.WIN 3.1
+llscmd add user 1user hyper.100 3.1
+llscmd add user 1user IMSL.NT 3.1
+llscmd add user 1user IMSL32.DOS 3.1
+llscmd add user 1user LM22 3.1
+llscmd add user 1user LMRES10.OS2 3.1
+llscmd add user 1user LMSFMAC3.1A 3.1
+llscmd add user 1user OFFICE.NT 3.1
+llscmd add user 1user OLE202.WIN 3.1
+llscmd add user 3user ONLN200.WIN 3.1
+llscmd add user 3user PROJ40.WIN 3.1
+llscmd add user 3user MSTCPIP.ALL 3.1
+llscmd add user 3user MVWR200.WIN 3.1
+llscmd add user 3user NETMON 3.1
+llscmd add user 3user OS2-1.31 3.1
+llscmd add user 3user PMSUB35.NT 3.1
+llscmd add user 3user RAS11.LM 3.1
+llscmd add user 3user PUB20a.WIN 3.1
+llscmd add user 3user PUBDSIGN.WIN 3.1
+llscmd add user 3user SAMPLER 3.1
+llscmd add user 3user SCENES20.WIN 3.1
+llscmd add user 3user SCHED10A.WIN 3.1
+llscmd add user 3user SGML10.WIN 3.1
+llscmd add user 3user SLM.ALL 3.1
+llscmd add user 4user SNDBIT10.WIN 3.1
+llscmd add user 4user SPACE10.DOS 3.1
+llscmd add user 4user SS30.DOS 3.1
+llscmd add user 4user ss31std.win 3.1
+llscmd add user 4user WA100.WIN 3.1
+llscmd add user 4user WEP10B.WIN 3.1
+llscmd add user 4user WEP20.WIN 3.1
+llscmd add user 4user WEP30.WIN 3.1
+llscmd add user 4user WEP40.WIN 3.1
+llscmd add user 4user WGTPLT11.WIN 3.1
+llscmd add user 4user WORD11B.PM 3.1
+llscmd add user 4user WORD6.DOS 3.1
+llscmd add user 4user WORD60.NT 3.1
+llscmd add user 4user WORD60C.WIN 3.1
+llscmd add user 4user word6cnv.nt 3.1
+llscmd add user 4user WORD6CNV.WIN 3.1
+llscmd add user 4user WORDVW60.WIN 3.1
+llscmd add user 4user WORKS30.DOS 3.1
+llscmd add user 4user WORKS30B.WIN 3.1
+llscmd add user 4user WPP310.WIN 3.1
+llscmd add user 4user WRITER10.WIN 3.1
+llscmd add user 4user 3com 3.1
+llscmd add user 4user BLPNT901.ALL 3.1
+llscmd add user 4user ARTIST10.WIN 3.1
+llscmd add user 4user BASEBL94.WIN 3.1
+llscmd add user 4user BA331.DOS 3.1
+llscmd add user 4user OS/2 3.1
+llscmd add user 4user NetWare 3.1
+llscmd add user 4user DOS 3.1
+llscmd add user 4user C 3.1
+llscmd add user 2user CHART300.DOS 3.1
+llscmd add user 2user CINEMA94.WIN 3.1
+llscmd add user 2user DELTA10A.WIN 3.1
+llscmd add user 2user DINOSAUR.WIN 3.1
+llscmd add user 2user DRAW10.WIN 3.1
+llscmd add user 2user COBOL500 3.1
+llscmd add user 2user FORT510A.DOS 3.1
+llscmd add user 2user FORTRN32.NT 3.1
+llscmd add user 2user VC21.NT 3.1
+llscmd add user 1user cdex 3.1
+llscmd add user 1user CLIENTS.NET 3.1
+llscmd add user 1user CMSRVWS.11 3.1
+llscmd add user 1user DCIDDK10 3.1
+llscmd add user 1user PPT40c.WIN 3.1
+llscmd add user 1user PROFIT1B.WIN 3.1
+llscmd add user 1user PROJ40.DOS 3.1
+llscmd add user 1user LMUNIX 3.1
+llscmd add user 1user MOUSE10a.ALL 3.1
+llscmd add user 1user MSD211.DOS 3.1
+llscmd add user 1user RESKIT.NT 3.1
+llscmd add user 3user SNA21.NT 3.1
+llscmd add user 3user SQL.NT 3.1
+llscmd add user 3user SQL42B.OS2 3.1
+llscmd add user 3user SQLRES10.OS2 3.1
+llscmd add user 3user WFW311.WIN 3.1
+llscmd add user 3user WFWCONN.WIN 3.1
+llscmd add user 3user WIN31 3.1
+llscmd add user 3user WING10.WIN 3.1
+llscmd add user 3user winlogin.win 3.1
+llscmd add user 1user WINNT.NT 3.1
+llscmd add user 1user WINNT35.SRV 3.1
+llscmd add user 1user WMDLRSDK.WIN 3.1
+llscmd add user 1user GOLF20.WIN 3.1
+llscmd add user 1user GRAPH50.WIN 3.1
+llscmd add user 1user GREETING.WIN 3.1
+llscmd add user 1user VC20A.NT 3.1
+llscmd add user 1user WINNT35.WKS 3.1
+llscmd add user 1user wpen100a.win 3.1
+llscmd add user 1user WPS.WIN 3.1
+llscmd add user 1user TAPI10.WIN 3.1
+llscmd add user 1user TCPIP.ALL 3.1
+llscmd add user 1user TCPUTIL3.1 3.1
+llscmd add user 1user VFW11.WIN 3.1
+llscmd add user 2user WRESKIT2.00 3.1
+llscmd add user 2user WSS20.WIN 3.1
+llscmd add user 2user MacWord 3.1
+llscmd add user 2user PowerPoint 3.1
+llscmd add user 2user BoundsChecker 3.1
+llscmd add user 2user Quattro 3.1
+llscmd add user 2user WordPerfect 3.1
+llscmd add user 2user Office 3.1
+llscmd add user 2user PerfectOffice 3.1
+llscmd add user 2user SQL 3.1
diff --git a/private/net/svcdlls/lls/test/ct/p4.bat b/private/net/svcdlls/lls/test/ct/p4.bat
new file mode 100644
index 000000000..0b5f8ef26
--- /dev/null
+++ b/private/net/svcdlls/lls/test/ct/p4.bat
@@ -0,0 +1,186 @@
+Money 1.0
+Schedule+ 1.0
+Mail 1.0
+MONEY30.WIN 1.0
+MP420A.DOS 1.0
+MSMAIL.ALL 1.0
+Win32 1.0
+Win32s 1.0
+Excel 1.0
+Word 1.0
+ARCADE10.WIN 1.0
+ARTGALL.WIN 1.0
+VCP100.WIN 1.0
+WINLIB 1.0
+ACCESS20.WIN 1.0
+FORTRN32.WIN 1.0
+IMSL.NT 1.0
+IMSL32.DOS 1.0
+IMSL51.DOS 1.0
+Chicago 1.0
+Windows95 1.0
+Access 1.0
+MASM611 1.0
+MFCKIT 1.0
+mt300a.all 1.0
+QWGRAPH 1.0
+VB10.DOS 1.0
+CBT 1.0
+EISPAK11.WIN 1.0
+ENCART95.WIN 1.0
+EXCEL30.PM 1.0
+VB30.WIN 1.0
+MASM 1.0
+OLE 1.0
+Chart 1.0
+Encarta 1.0
+FlightSim 1.0
+Paradox 1.0
+VC150.WIN 1.0
+VC20.MAC 1.0
+IMAGER10.WIN 1.0
+LRND300.DOS 1.0
+MMGOLF10.WIN 1.0
+MOM.NT 1.0
+MSDOS622.DOS 1.0
+MSKBD1.ALL 1.0
+MSLOGO.TTF 1.0
+MSSMS10 1.0
+MOM42.WIN 1.0
+SNA 1.0
+Exchange 1.0
+MS_EFORM.WIN 1.0
+ODAKIT 1.0
+ODBC210.WIN 1.0
+VC20.NT 1.0
+BOB.WIN 1.0
+BOOK94.WIN 1.0
+BOWEP.WIN 1.0
+EXCEL50.NT 1.0
+EXCEL50C.WIN 1.0
+FLTSIM5a.DOS 1.0
+FOXPR26A.DOS 1.0
+FOXPR26A.WIN 1.0
+FOXPRO26.UNX 1.0
+WKGRP311.DOS 1.0
+wlo09 1.0
+wlo10 1.0
+ACCSOLPK.WIN 1.0
+ACCUPTLS.WIN 1.0
+ADT20.WIN 1.0
+GS100.DOS 1.0
+ODBCDDP2.NT 1.0
+ODBCDDP2.WIN 1.0
+OFF42C.WIN 1.0
+OFF43c.WIN 1.0
+OFFAST10.WIN 1.0
+OFFDK10.WIN 1.0
+FONTPACK.WIN 1.0
+FONTPAK2.WIN 1.0
+HPFNTSET.WIN 1.0
+hyper.100 1.0
+IMSL.NT 1.0
+IMSL32.DOS 1.0
+LM22 1.0
+LMRES10.OS2 1.0
+LMSFMAC1.0A 1.0
+OFFICE.NT 1.0
+OLE202.WIN 1.0
+ONLN200.WIN 1.0
+PROJ40.WIN 1.0
+MSTCPIP.ALL 1.0
+MVWR200.WIN 1.0
+NETMON 1.0
+OS2-1.31 1.0
+PMSUB35.NT 1.0
+RAS11.LM 1.0
+PUB20a.WIN 1.0
+PUBDSIGN.WIN 1.0
+SAMPLER 1.0
+SCENES20.WIN 1.0
+SCHED10A.WIN 1.0
+SGML10.WIN 1.0
+SLM.ALL 1.0
+SNDBIT10.WIN 1.0
+SPACE10.DOS 1.0
+SS30.DOS 1.0
+ss31std.win 1.0
+WA100.WIN 1.0
+WEP10B.WIN 1.0
+WEP20.WIN 1.0
+WEP30.WIN 1.0
+WEP40.WIN 1.0
+WGTPLT11.WIN 1.0
+WORD11B.PM 1.0
+WORD6.DOS 1.0
+WORD60.NT 1.0
+WORD60C.WIN 1.0
+word6cnv.nt 1.0
+WORD6CNV.WIN 1.0
+WORDVW60.WIN 1.0
+WORKS30.DOS 1.0
+WORKS30B.WIN 1.0
+WPP310.WIN 1.0
+WRITER10.WIN 1.0
+3com 1.0
+BLPNT901.ALL 1.0
+ARTIST10.WIN 1.0
+BASEBL94.WIN 1.0
+BA331.DOS 1.0
+OS/2 1.0
+NetWare 1.0
+DOS 1.0
+C 1.0
+CHART300.DOS 1.0
+CINEMA94.WIN 1.0
+DELTA10A.WIN 1.0
+DINOSAUR.WIN 1.0
+DRAW10.WIN 1.0
+COBOL500 1.0
+FORT510A.DOS 1.0
+FORTRN32.NT 1.0
+VC21.NT 1.0
+cdex 1.0
+CLIENTS.NET 1.0
+CMSRVWS.11 1.0
+DCIDDK10 1.0
+PPT40c.WIN 1.0
+PROFIT1B.WIN 1.0
+PROJ40.DOS 1.0
+LMUNIX 1.0
+MOUSE10a.ALL 1.0
+MSD211.DOS 1.0
+RESKIT.NT 1.0
+SNA21.NT 1.0
+SQL.NT 1.0
+SQL42B.OS2 1.0
+SQLRES10.OS2 1.0
+WFW311.WIN 1.0
+WFWCONN.WIN 1.0
+WIN31 1.0
+WING10.WIN 1.0
+winlogin.win 1.0
+WINNT.NT 1.0
+WINNT35.SRV 1.0
+WMDLRSDK.WIN 1.0
+GOLF20.WIN 1.0
+GRAPH50.WIN 1.0
+GREETING.WIN 1.0
+VC20A.NT 1.0
+WINNT35.WKS 1.0
+wpen100a.win 1.0
+WPS.WIN 1.0
+TAPI10.WIN 1.0
+TCPIP.ALL 1.0
+TCPUTIL1.0 1.0
+VFW11.WIN 1.0
+WRESKIT2.00 1.0
+WSS20.WIN 1.0
+MacWord 1.0
+PowerPoint 1.0
+BoundsChecker 1.0
+Quattro 1.0
+WordPerfect 1.0
+Office 1.0
+PerfectOffice 1.0
+SQL 1.0
diff --git a/private/net/svcdlls/lls/test/ct/p5.bat b/private/net/svcdlls/lls/test/ct/p5.bat
new file mode 100644
index 000000000..0b5f8ef26
--- /dev/null
+++ b/private/net/svcdlls/lls/test/ct/p5.bat
@@ -0,0 +1,186 @@
+Money 1.0
+Schedule+ 1.0
+Mail 1.0
+MONEY30.WIN 1.0
+MP420A.DOS 1.0
+MSMAIL.ALL 1.0
+Win32 1.0
+Win32s 1.0
+Excel 1.0
+Word 1.0
+ARCADE10.WIN 1.0
+ARTGALL.WIN 1.0
+VCP100.WIN 1.0
+WINLIB 1.0
+ACCESS20.WIN 1.0
+FORTRN32.WIN 1.0
+IMSL.NT 1.0
+IMSL32.DOS 1.0
+IMSL51.DOS 1.0
+Chicago 1.0
+Windows95 1.0
+Access 1.0
+MASM611 1.0
+MFCKIT 1.0
+mt300a.all 1.0
+QWGRAPH 1.0
+VB10.DOS 1.0
+CBT 1.0
+EISPAK11.WIN 1.0
+ENCART95.WIN 1.0
+EXCEL30.PM 1.0
+VB30.WIN 1.0
+MASM 1.0
+OLE 1.0
+Chart 1.0
+Encarta 1.0
+FlightSim 1.0
+Paradox 1.0
+VC150.WIN 1.0
+VC20.MAC 1.0
+IMAGER10.WIN 1.0
+LRND300.DOS 1.0
+MMGOLF10.WIN 1.0
+MOM.NT 1.0
+MSDOS622.DOS 1.0
+MSKBD1.ALL 1.0
+MSLOGO.TTF 1.0
+MSSMS10 1.0
+MOM42.WIN 1.0
+SNA 1.0
+Exchange 1.0
+MS_EFORM.WIN 1.0
+ODAKIT 1.0
+ODBC210.WIN 1.0
+VC20.NT 1.0
+BOB.WIN 1.0
+BOOK94.WIN 1.0
+BOWEP.WIN 1.0
+EXCEL50.NT 1.0
+EXCEL50C.WIN 1.0
+FLTSIM5a.DOS 1.0
+FOXPR26A.DOS 1.0
+FOXPR26A.WIN 1.0
+FOXPRO26.UNX 1.0
+WKGRP311.DOS 1.0
+wlo09 1.0
+wlo10 1.0
+ACCSOLPK.WIN 1.0
+ACCUPTLS.WIN 1.0
+ADT20.WIN 1.0
+GS100.DOS 1.0
+ODBCDDP2.NT 1.0
+ODBCDDP2.WIN 1.0
+OFF42C.WIN 1.0
+OFF43c.WIN 1.0
+OFFAST10.WIN 1.0
+OFFDK10.WIN 1.0
+FONTPACK.WIN 1.0
+FONTPAK2.WIN 1.0
+HPFNTSET.WIN 1.0
+hyper.100 1.0
+IMSL.NT 1.0
+IMSL32.DOS 1.0
+LM22 1.0
+LMRES10.OS2 1.0
+LMSFMAC1.0A 1.0
+OFFICE.NT 1.0
+OLE202.WIN 1.0
+ONLN200.WIN 1.0
+PROJ40.WIN 1.0
+MSTCPIP.ALL 1.0
+MVWR200.WIN 1.0
+NETMON 1.0
+OS2-1.31 1.0
+PMSUB35.NT 1.0
+RAS11.LM 1.0
+PUB20a.WIN 1.0
+PUBDSIGN.WIN 1.0
+SAMPLER 1.0
+SCENES20.WIN 1.0
+SCHED10A.WIN 1.0
+SGML10.WIN 1.0
+SLM.ALL 1.0
+SNDBIT10.WIN 1.0
+SPACE10.DOS 1.0
+SS30.DOS 1.0
+ss31std.win 1.0
+WA100.WIN 1.0
+WEP10B.WIN 1.0
+WEP20.WIN 1.0
+WEP30.WIN 1.0
+WEP40.WIN 1.0
+WGTPLT11.WIN 1.0
+WORD11B.PM 1.0
+WORD6.DOS 1.0
+WORD60.NT 1.0
+WORD60C.WIN 1.0
+word6cnv.nt 1.0
+WORD6CNV.WIN 1.0
+WORDVW60.WIN 1.0
+WORKS30.DOS 1.0
+WORKS30B.WIN 1.0
+WPP310.WIN 1.0
+WRITER10.WIN 1.0
+3com 1.0
+BLPNT901.ALL 1.0
+ARTIST10.WIN 1.0
+BASEBL94.WIN 1.0
+BA331.DOS 1.0
+OS/2 1.0
+NetWare 1.0
+DOS 1.0
+C 1.0
+CHART300.DOS 1.0
+CINEMA94.WIN 1.0
+DELTA10A.WIN 1.0
+DINOSAUR.WIN 1.0
+DRAW10.WIN 1.0
+COBOL500 1.0
+FORT510A.DOS 1.0
+FORTRN32.NT 1.0
+VC21.NT 1.0
+cdex 1.0
+CLIENTS.NET 1.0
+CMSRVWS.11 1.0
+DCIDDK10 1.0
+PPT40c.WIN 1.0
+PROFIT1B.WIN 1.0
+PROJ40.DOS 1.0
+LMUNIX 1.0
+MOUSE10a.ALL 1.0
+MSD211.DOS 1.0
+RESKIT.NT 1.0
+SNA21.NT 1.0
+SQL.NT 1.0
+SQL42B.OS2 1.0
+SQLRES10.OS2 1.0
+WFW311.WIN 1.0
+WFWCONN.WIN 1.0
+WIN31 1.0
+WING10.WIN 1.0
+winlogin.win 1.0
+WINNT.NT 1.0
+WINNT35.SRV 1.0
+WMDLRSDK.WIN 1.0
+GOLF20.WIN 1.0
+GRAPH50.WIN 1.0
+GREETING.WIN 1.0
+VC20A.NT 1.0
+WINNT35.WKS 1.0
+wpen100a.win 1.0
+WPS.WIN 1.0
+TAPI10.WIN 1.0
+TCPIP.ALL 1.0
+TCPUTIL1.0 1.0
+VFW11.WIN 1.0
+WRESKIT2.00 1.0
+WSS20.WIN 1.0
+MacWord 1.0
+PowerPoint 1.0
+BoundsChecker 1.0
+Quattro 1.0
+WordPerfect 1.0
+Office 1.0
+PerfectOffice 1.0
+SQL 1.0
diff --git a/private/net/svcdlls/lls/test/ct/prod.bat b/private/net/svcdlls/lls/test/ct/prod.bat
new file mode 100644
index 000000000..0b5f8ef26
--- /dev/null
+++ b/private/net/svcdlls/lls/test/ct/prod.bat
@@ -0,0 +1,186 @@
+Money 1.0
+Schedule+ 1.0
+Mail 1.0
+MONEY30.WIN 1.0
+MP420A.DOS 1.0
+MSMAIL.ALL 1.0
+Win32 1.0
+Win32s 1.0
+Excel 1.0
+Word 1.0
+ARCADE10.WIN 1.0
+ARTGALL.WIN 1.0
+VCP100.WIN 1.0
+WINLIB 1.0
+ACCESS20.WIN 1.0
+FORTRN32.WIN 1.0
+IMSL.NT 1.0
+IMSL32.DOS 1.0
+IMSL51.DOS 1.0
+Chicago 1.0
+Windows95 1.0
+Access 1.0
+MASM611 1.0
+MFCKIT 1.0
+mt300a.all 1.0
+QWGRAPH 1.0
+VB10.DOS 1.0
+CBT 1.0
+EISPAK11.WIN 1.0
+ENCART95.WIN 1.0
+EXCEL30.PM 1.0
+VB30.WIN 1.0
+MASM 1.0
+OLE 1.0
+Chart 1.0
+Encarta 1.0
+FlightSim 1.0
+Paradox 1.0
+VC150.WIN 1.0
+VC20.MAC 1.0
+IMAGER10.WIN 1.0
+LRND300.DOS 1.0
+MMGOLF10.WIN 1.0
+MOM.NT 1.0
+MSDOS622.DOS 1.0
+MSKBD1.ALL 1.0
+MSLOGO.TTF 1.0
+MSSMS10 1.0
+MOM42.WIN 1.0
+SNA 1.0
+Exchange 1.0
+MS_EFORM.WIN 1.0
+ODAKIT 1.0
+ODBC210.WIN 1.0
+VC20.NT 1.0
+BOB.WIN 1.0
+BOOK94.WIN 1.0
+BOWEP.WIN 1.0
+EXCEL50.NT 1.0
+EXCEL50C.WIN 1.0
+FLTSIM5a.DOS 1.0
+FOXPR26A.DOS 1.0
+FOXPR26A.WIN 1.0
+FOXPRO26.UNX 1.0
+WKGRP311.DOS 1.0
+wlo09 1.0
+wlo10 1.0
+ACCSOLPK.WIN 1.0
+ACCUPTLS.WIN 1.0
+ADT20.WIN 1.0
+GS100.DOS 1.0
+ODBCDDP2.NT 1.0
+ODBCDDP2.WIN 1.0
+OFF42C.WIN 1.0
+OFF43c.WIN 1.0
+OFFAST10.WIN 1.0
+OFFDK10.WIN 1.0
+FONTPACK.WIN 1.0
+FONTPAK2.WIN 1.0
+HPFNTSET.WIN 1.0
+hyper.100 1.0
+IMSL.NT 1.0
+IMSL32.DOS 1.0
+LM22 1.0
+LMRES10.OS2 1.0
+LMSFMAC1.0A 1.0
+OFFICE.NT 1.0
+OLE202.WIN 1.0
+ONLN200.WIN 1.0
+PROJ40.WIN 1.0
+MSTCPIP.ALL 1.0
+MVWR200.WIN 1.0
+NETMON 1.0
+OS2-1.31 1.0
+PMSUB35.NT 1.0
+RAS11.LM 1.0
+PUB20a.WIN 1.0
+PUBDSIGN.WIN 1.0
+SAMPLER 1.0
+SCENES20.WIN 1.0
+SCHED10A.WIN 1.0
+SGML10.WIN 1.0
+SLM.ALL 1.0
+SNDBIT10.WIN 1.0
+SPACE10.DOS 1.0
+SS30.DOS 1.0
+ss31std.win 1.0
+WA100.WIN 1.0
+WEP10B.WIN 1.0
+WEP20.WIN 1.0
+WEP30.WIN 1.0
+WEP40.WIN 1.0
+WGTPLT11.WIN 1.0
+WORD11B.PM 1.0
+WORD6.DOS 1.0
+WORD60.NT 1.0
+WORD60C.WIN 1.0
+word6cnv.nt 1.0
+WORD6CNV.WIN 1.0
+WORDVW60.WIN 1.0
+WORKS30.DOS 1.0
+WORKS30B.WIN 1.0
+WPP310.WIN 1.0
+WRITER10.WIN 1.0
+3com 1.0
+BLPNT901.ALL 1.0
+ARTIST10.WIN 1.0
+BASEBL94.WIN 1.0
+BA331.DOS 1.0
+OS/2 1.0
+NetWare 1.0
+DOS 1.0
+C 1.0
+CHART300.DOS 1.0
+CINEMA94.WIN 1.0
+DELTA10A.WIN 1.0
+DINOSAUR.WIN 1.0
+DRAW10.WIN 1.0
+COBOL500 1.0
+FORT510A.DOS 1.0
+FORTRN32.NT 1.0
+VC21.NT 1.0
+cdex 1.0
+CLIENTS.NET 1.0
+CMSRVWS.11 1.0
+DCIDDK10 1.0
+PPT40c.WIN 1.0
+PROFIT1B.WIN 1.0
+PROJ40.DOS 1.0
+LMUNIX 1.0
+MOUSE10a.ALL 1.0
+MSD211.DOS 1.0
+RESKIT.NT 1.0
+SNA21.NT 1.0
+SQL.NT 1.0
+SQL42B.OS2 1.0
+SQLRES10.OS2 1.0
+WFW311.WIN 1.0
+WFWCONN.WIN 1.0
+WIN31 1.0
+WING10.WIN 1.0
+winlogin.win 1.0
+WINNT.NT 1.0
+WINNT35.SRV 1.0
+WMDLRSDK.WIN 1.0
+GOLF20.WIN 1.0
+GRAPH50.WIN 1.0
+GREETING.WIN 1.0
+VC20A.NT 1.0
+WINNT35.WKS 1.0
+wpen100a.win 1.0
+WPS.WIN 1.0
+TAPI10.WIN 1.0
+TCPIP.ALL 1.0
+TCPUTIL1.0 1.0
+VFW11.WIN 1.0
+WRESKIT2.00 1.0
+WSS20.WIN 1.0
+MacWord 1.0
+PowerPoint 1.0
+BoundsChecker 1.0
+Quattro 1.0
+WordPerfect 1.0
+Office 1.0
+PerfectOffice 1.0
+SQL 1.0
diff --git a/private/net/svcdlls/lls/test/ct/prod.dat b/private/net/svcdlls/lls/test/ct/prod.dat
new file mode 100644
index 000000000..e0edb458c
--- /dev/null
+++ b/private/net/svcdlls/lls/test/ct/prod.dat
@@ -0,0 +1,186 @@
+Money
+Schedule+
+Mail
+MONEY30.WIN
+MP420A.DOS
+MSMAIL.ALL
+Win32
+Win32s
+Excel
+Word
+ARCADE10.WIN
+ARTGALL.WIN
+VCP100.WIN
+WINLIB
+ACCESS20.WIN
+FORTRN32.WIN
+IMSL.NT
+IMSL32.DOS
+IMSL51.DOS
+Chicago
+Windows95
+Access
+MASM611
+MFCKIT
+mt300a.all
+QWGRAPH
+VB10.DOS
+CBT
+EISPAK11.WIN
+ENCART95.WIN
+EXCEL30.PM
+VB30.WIN
+MASM
+OLE
+Chart
+Encarta
+FlightSim
+Paradox
+VC150.WIN
+VC20.MAC
+IMAGER10.WIN
+LRND300.DOS
+MMGOLF10.WIN
+MOM.NT
+MSDOS622.DOS
+MSKBD1.ALL
+MSLOGO.TTF
+MSSMS10
+MOM42.WIN
+SNA
+Exchange
+MS_EFORM.WIN
+ODAKIT
+ODBC210.WIN
+VC20.NT
+BOB.WIN
+BOOK94.WIN
+BOWEP.WIN
+EXCEL50.NT
+EXCEL50C.WIN
+FLTSIM5a.DOS
+FOXPR26A.DOS
+FOXPR26A.WIN
+FOXPRO26.UNX
+WKGRP311.DOS
+wlo09
+wlo10
+ACCSOLPK.WIN
+ACCUPTLS.WIN
+ADT20.WIN
+GS100.DOS
+ODBCDDP2.NT
+ODBCDDP2.WIN
+OFF42C.WIN
+OFF43c.WIN
+OFFAST10.WIN
+OFFDK10.WIN
+FONTPACK.WIN
+FONTPAK2.WIN
+HPFNTSET.WIN
+hyper.100
+IMSL.NT
+IMSL32.DOS
+LM22
+LMRES10.OS2
+LMSFMAC1.0A
+OFFICE.NT
+OLE202.WIN
+ONLN200.WIN
+PROJ40.WIN
+MSTCPIP.ALL
+MVWR200.WIN
+NETMON
+OS2-1.31
+PMSUB35.NT
+RAS11.LM
+PUB20a.WIN
+PUBDSIGN.WIN
+SAMPLER
+SCENES20.WIN
+SCHED10A.WIN
+SGML10.WIN
+SLM.ALL
+SNDBIT10.WIN
+SPACE10.DOS
+SS30.DOS
+ss31std.win
+WA100.WIN
+WEP10B.WIN
+WEP20.WIN
+WEP30.WIN
+WEP40.WIN
+WGTPLT11.WIN
+WORD11B.PM
+WORD6.DOS
+WORD60.NT
+WORD60C.WIN
+word6cnv.nt
+WORD6CNV.WIN
+WORDVW60.WIN
+WORKS30.DOS
+WORKS30B.WIN
+WPP310.WIN
+WRITER10.WIN
+3com
+BLPNT901.ALL
+ARTIST10.WIN
+BASEBL94.WIN
+BA331.DOS
+OS/2
+NetWare
+DOS
+C
+CHART300.DOS
+CINEMA94.WIN
+DELTA10A.WIN
+DINOSAUR.WIN
+DRAW10.WIN
+COBOL500
+FORT510A.DOS
+FORTRN32.NT
+VC21.NT
+cdex
+CLIENTS.NET
+CMSRVWS.11
+DCIDDK10
+PPT40c.WIN
+PROFIT1B.WIN
+PROJ40.DOS
+LMUNIX
+MOUSE10a.ALL
+MSD211.DOS
+RESKIT.NT
+SNA21.NT
+SQL.NT
+SQL42B.OS2
+SQLRES10.OS2
+WFW311.WIN
+WFWCONN.WIN
+WIN31
+WING10.WIN
+winlogin.win
+WINNT.NT
+WINNT35.SRV
+WMDLRSDK.WIN
+GOLF20.WIN
+GRAPH50.WIN
+GREETING.WIN
+VC20A.NT
+WINNT35.WKS
+wpen100a.win
+WPS.WIN
+TAPI10.WIN
+TCPIP.ALL
+TCPUTIL1.0
+VFW11.WIN
+WRESKIT2.00
+WSS20.WIN
+MacWord
+PowerPoint
+BoundsChecker
+Quattro
+WordPerfect
+Office
+PerfectOffice
+SQL
diff --git a/private/net/svcdlls/lls/test/ct/rpc.bat b/private/net/svcdlls/lls/test/ct/rpc.bat
new file mode 100644
index 000000000..3f4a2d01c
--- /dev/null
+++ b/private/net/svcdlls/lls/test/ct/rpc.bat
@@ -0,0 +1,139 @@
+
+if NOT '%1' == '' set srv=%1
+if NOT '%2' == '' set user=%2
+if NOT '%3' == '' set domain=%3
+
+if '%srv%' == '' goto usage
+if '%user%' == '' goto usage
+if '%domain%' == '' goto usage
+goto init
+
+:usage
+echo Usage: rpc.bat servername Username
+goto done
+
+
+:init
+
+llscmd rpc %srv% llslicenseadd "SQL 4.2" "Mr. Slate" 5 "New employees"
+llscmd rpc %srv% llslicenseadd "SQL 4.2" "Betty Rubble" 2 "Authoried by Mr. Slate"
+llscmd rpc %srv% llslicenseadd "SNA 3.0" "Fred Flinstone" 1 "Who let me in"
+llscmd rpc %srv% llslicenseadd "Exchange 1.0" "Mr. Slate" 99 "On the beta"
+
+llscmd rpc %srv% llsmappingadd Lab2 2 "Computer Lab 2/2222"
+llscmd rpc %srv% llsmappingadd Lab1 1 "Computer Lab 1/1111"
+llscmd rpc %srv% llsmappingadd Lab3 3 "Computer Lab 3/3333"
+
+llscmd rpc %srv% llsmappinguseradd Lab1 %domain%\Fred
+
+llscmd rpc %srv% llsmappinguseradd Lab2 %domain%\Barney
+llscmd rpc %srv% llsmappinguseradd Lab2 %domain%\Wilma
+
+llscmd rpc %srv% llsmappinguseradd Lab3 %domain%\Pebbles
+llscmd rpc %srv% llsmappinguseradd Lab3 %domain%\BamBam
+llscmd rpc %srv% llsmappinguseradd Lab3 %domain%\Mr.Slate
+
+echo off
+rem llscmd add user %domain%\\"Fred" SQL 4.2
+rem llscmd add user %domain%\\"Barney" SQL 4.2
+rem llscmd add user %domain%\\"Wilma" SQL 4.2
+rem llscmd add user %domain%\\"Pebbles" SQL 4.2
+rem llscmd add user %domain%\\"BamBam" SQL 4.2
+rem llscmd add user %domain%\\"Mr.Slate" SNA 3.0
+rem llscmd add user %domain%\\"GeorgeJ" SNA 3.0
+rem llscmd add user %domain%\\"AstroJ" SNA 3.0
+
+rem llscmd add user %domain%\\"Fred" Exchange 1.0
+rem llscmd add user %domain%\\"Barney" Exchange 1.0
+rem llscmd add user %domain%\\"Wilma" Exchange 1.0
+rem llscmd add user %domain%\\"Pebbles" Exchange 1.0
+rem llscmd add user %domain%\\"BamBam" Exchange 1.0
+rem llscmd add user %domain%\\"Mr.Slate" Exchange 1.0
+rem llscmd add user %domain%\\"GeorgeJ" Exchange 1.0
+
+
+
+
+
+rem llscmd rpc [\\server] LlsLicenseEnum
+llscmd rpc %srv% LlsLicenseEnum
+
+
+Rem llscmd rpc [\\server] LlsLicenseAdd product admin quantity comment
+llscmd rpc %srv% LlsLicenseAdd myproduct %user% 1 "test commnet"
+
+
+Rem llscmd rpc [\\server] LlsProductEnum Level
+llscmd rpc %srv% LlsProductEnum 0
+
+
+Rem llscmd rpc [\\server] LlsProductUserEnum Level Product
+llscmd rpc %srv% LlsProductUserEnum 0 myproduct
+
+
+Rem llscmd rpc [\\server] LlsProductLicenseEnum level product version
+llscmd rpc %srv% LlsProductLicenseEnum 0 myproduct 1.0
+
+
+Rem llscmd rpc [\\server] LlsUserEnum Level
+llscmd rpc %srv% LlsUserEnum 0
+
+
+Rem llscmd rpc [\\server] LlsUserProductEnum Level user
+llscmd rpc %srv% LlsUserProductEnum 0 %user%
+
+
+Rem llscmd rpc [\\server] LlsUserInfoGet Level User
+llscmd rpc %srv% LlsUserInfoGet 0 %user%
+
+
+Rem llscmd rpc [\\server] LlsUserInfoSet Level User Mapping [BACKOFFICE]
+llscmd rpc %srv% LlsUserInfoSet 0 %user% XXX
+
+
+Rem llscmd rpc [\\server] LlsUserDelete User
+llscmd rpc %srv% LlsUserDelete %user%
+
+
+Rem llscmd rpc [\\server] LlsMappingEnum Level
+llscmd rpc %srv% LlsMappingEnum 0
+
+
+Rem llscmd rpc [\\server] LlsMappingInfoGet Level Mapping
+llscmd rpc %srv% LlsMappingInfoGet 0 XXX
+
+
+Rem llscmd rpc [\\server] LlsMappingInfoSet Level Mapping Licenses Comment
+llscmd rpc %srv% LlsMappingInfoSet 0 XXX 2 "test comment 2"
+
+
+Rem llscmd rpc [\\server] LlsMappingUserEnum Level Mapping
+llscmd rpc %srv% LlsMappingUserEnum 0 XXX
+
+
+Rem llscmd rpc [\\server] LlsMappingUserAdd Mapping User
+llscmd rpc %srv% LlsMappingUserAdd XXX %user%
+
+
+Rem llscmd rpc [\\server] LlsMappingUserDelete Mapping User
+llscmd rpc %srv% LlsMappingUserDelete XXX %user%
+
+
+Rem llscmd rpc [\\server] LlsMappingAdd Product admin quantity comment
+llscmd rpc %srv% LlsMappingAdd myproduct %user% 3 "test comment 3"
+
+
+Rem llscmd rpc [\\server] LlsMappingDelete Mapping
+llscmd rpc %srv% LlsMappingDelete XXX
+
+
+Rem llscmd rpc [\\server] LlsServiceInfoGet
+llscmd rpc %srv% LlsServiceInfoGet
+
+
+Rem llscmd rpc [\\server] LlsServiceInfoSet repl-mode time enterprise
+rem repl-mode 0=replicate every..(time) 1=replicat @...(time)
+llscmd rpc %srv% LlsServiceInfoSet 0 12:00
+
+done:
+rpc.bat
diff --git a/private/net/svcdlls/lls/test/dirs b/private/net/svcdlls/lls/test/dirs
new file mode 100644
index 000000000..3a980091d
--- /dev/null
+++ b/private/net/svcdlls/lls/test/dirs
@@ -0,0 +1 @@
+DIRS=common llscmd llsdbg
diff --git a/private/net/svcdlls/lls/test/llscmd/llscmd.c b/private/net/svcdlls/lls/test/llscmd/llscmd.c
new file mode 100644
index 000000000..a3e24abb5
--- /dev/null
+++ b/private/net/svcdlls/lls/test/llscmd/llscmd.c
@@ -0,0 +1,1616 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ llscmd.c
+
+Abstract:
+
+ License Logging Service test program.
+
+Author:
+
+ Arthur Hanson (arth) 1-26-95
+
+Environment:
+
+Revision History:
+
+--*/
+
+
+#ifndef UNICODE
+#define UNICODE
+#endif
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windows.h>
+#include <lmcons.h>
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ntlsapi.h>
+#include "..\..\inc\llsapi.h"
+#include "..\..\inc\debug.h"
+#include "..\common\llsdbg.h"
+
+#define MAX_CHAR_LEN 100
+#define MAX_WSTR_LEN 100
+
+char *ProgramName;
+
+//
+// This handles arguments from command line or from a file - Un-Marshall all
+// the commands into this array and then process the array so we can use
+// a common processing routine.
+//
+// Array allows up to 10 command line arguments, each 100 chars long.
+//
+ULONG NumberArguments = 0;
+ULONG Index = 0;
+char Arguments[10][MAX_CHAR_LEN];
+LS_STATUS_CODE RetCode;
+
+char tmpStr[MAX_CHAR_LEN];
+TCHAR UserName[MAX_CHAR_LEN];
+TCHAR ProductName[MAX_CHAR_LEN];
+TCHAR Version[MAX_CHAR_LEN];
+
+BOOLEAN IsAdmin = FALSE;
+BOOL NoFree = FALSE;
+
+BYTE bufSID[1024];
+DWORD cbbufSid = sizeof(bufSID);
+
+HANDLE LlsDllHandle = NULL;
+FARPROC LicenseRequest;
+FARPROC LicenseFree;
+NT_LS_DATA Data;
+
+BOOL LocalRPC = TRUE;
+LLS_HANDLE RPCHandle = NULL;
+TCHAR Server[MAX_CHAR_LEN];
+
+
+HANDLE LlsRPCHandle = NULL;
+FARPROC pLlsConnectEnterprise;
+FARPROC pLlsClose;
+FARPROC pLlsFreeMemory;
+FARPROC pLlsLicenseEnum;
+FARPROC pLlsLicenseAdd;
+FARPROC pLlsProductEnum;
+FARPROC pLlsProductUserEnum;
+FARPROC pLlsProductLicenseEnum;
+FARPROC pLlsUserEnum;
+FARPROC pLlsUserProductEnum;
+FARPROC pLlsUserProductDelete;
+FARPROC pLlsUserInfoGet;
+FARPROC pLlsUserInfoSet;
+FARPROC pLlsUserDelete;
+FARPROC pLlsMappingEnum;
+FARPROC pLlsMappingInfoGet;
+FARPROC pLlsMappingInfoSet;
+FARPROC pLlsMappingUserEnum;
+FARPROC pLlsMappingUserAdd;
+FARPROC pLlsMappingUserDelete;
+FARPROC pLlsMappingAdd;
+FARPROC pLlsMappingDelete;
+FARPROC pLlsServiceInfoGet;
+FARPROC pLlsServiceInfoSet;
+
+
+#define DEFAULT_PRODUCT "Test Product"
+#define DEFAULT_USER ""
+#define DEFAULT_VERSION "12.345"
+
+
+#ifdef xxxx
+/////////////////////////////////////////////////////////////////////////
+void ParseWord(char **lpch, LPSTR tmpStr) {
+ char *ch;
+ char *lch;
+ char *pch;
+
+ ch = *lpch;
+ lch = pch = tmpStr;
+
+ // remove any leading whitespace
+ while(*ch && ((*ch == ' ') || (*ch == '\t')))
+ ch++;
+
+ // transfer it to tmpStr (via pch pointer)
+ while (*ch && (*ch != ',')) {
+ // keep track of last non-whitespace
+ if ((*ch != ' ') && (*ch != '\t')) {
+ lch = pch;
+ lch++;
+ }
+
+ *pch++ = *ch++;
+ }
+
+ if (*ch == ',')
+ ch++;
+
+ // NULL terminate before last section of whitespace
+ *lch = '\0';
+ *lpch = ch;
+
+} // ParseWord
+
+
+/////////////////////////////////////////////////////////////////////////
+void map_ParseUser(LPTSTR Line, LPTSTR Name, LPTSTR NewName, LPTSTR Password) {
+ TCHAR *pch = Line;
+
+ lstrcpy(Name, TEXT(""));
+ lstrcpy(NewName, TEXT(""));
+ lstrcpy(Password, TEXT(""));
+
+ if (Line == NULL)
+ return;
+
+ ParseWord(&pch, Name);
+ if (lstrlen(Name) >= MAX_USER_NAME_LEN)
+ lstrcpy(Name, TEXT(""));
+
+ ParseWord(&pch, NewName);
+ if (lstrlen(NewName) >= MAX_USER_NAME_LEN)
+ lstrcpy(NewName, TEXT(""));
+
+ ParseWord(&pch, Password);
+ if (lstrlen(Password) > MAX_PW_LEN)
+ lstrcpy(Password, TEXT(""));
+
+} // map_ParseUser
+#endif
+
+
+void ReadLicenses(char *FileName, int NumLines) {
+ char line[MAX_CHAR_LEN], *result;
+ FILE *stream;
+ TCHAR wline[MAX_CHAR_LEN];
+ ULONG lHandle;
+ PULONG plHandle;
+ int i = 0;
+
+ plHandle = &lHandle;
+
+ if ((stream = fopen(FileName, "r")) != NULL) {
+ while ( ((i == 0) || (i < NumLines)) && (fgets(line, 100, stream) != NULL)) {
+ // get rid of ending newline
+ if (line[strlen(line) - 1] == '\n')
+ line[strlen(line) - 1] = '\0';
+
+ // convert to Unicode
+ OemToCharBuff(line, wline, strlen(line) + 1);
+ Data.DataType = 0;
+ Data.Data = (PVOID) wline;
+ Data.IsAdmin = IsAdmin;
+ RetCode = (*LicenseRequest) ( ProductName, Version, plHandle, &Data);
+ printf("LicenseRequest Return Code: 0X%XH\n",RetCode);
+
+ if (!NoFree) {
+ RetCode = (*LicenseFree) ( *plHandle );
+ printf("LicenseFree Return Code: 0X%XH\n",RetCode);
+ }
+
+ if (NumLines)
+ i++;
+
+ }
+ }
+
+ fclose(stream);
+
+} // ReadLicenses
+
+
+
+/////////////////////////////////////////////////////////////////////////
+BOOL NameToSid(WCHAR *pszServer, WCHAR *pszName, BYTE *buffer, DWORD *bufsiz) {
+ WCHAR szDomain[DNLEN+1];
+ DWORD cchDomain = sizeof(szDomain)/sizeof(szDomain[0]);
+ SID_NAME_USE snu;
+
+ return (LookupAccountNameW( pszServer,
+ pszName,
+ buffer,
+ bufsiz,
+ (unsigned short *) szDomain,
+ &cchDomain,
+ &snu));
+} // NameToSid
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID ShowUsageAdd( ) {
+ printf("The syntax of this command is:\n\n");
+ printf("%s ADD [\n", ProgramName);
+ printf("\tUSER [NOFREE] [ADMIN] username [ product version ] |\n");
+ printf("\tSID [NOFREE] [ADMIN] username [ product version ] |\n");
+ printf("\tFILE filename #lines [NOFREE] [ADMIN] [ product version ] ]\n");
+
+} // ShowUsageAdd
+
+
+VOID DispatchAdd(int argc, char **argv) {
+ int Index = 3;
+ char *aProduct, *aVersion, *aUser, *FileName;
+ BOOL IsSid = FALSE;
+ BOOL IsFile = FALSE;
+ ULONG lHandle;
+ PULONG plHandle;
+ UNICODE_STRING UString;
+ char tUser[MAX_CHAR_LEN];
+ char tProduct[MAX_CHAR_LEN];
+ int FileCount = 0;
+
+ plHandle = &lHandle;
+
+ if (argc < 4) {
+ ShowUsageAdd();
+ exit(0);
+ }
+
+ //
+ // Make sure the first param is correct.
+ //
+ if ( (!_strcmpi(argv[2], "USER")) || (!_strcmpi(argv[2], "SID")) ||
+ (!_strcmpi(argv[2], "FILE")) ) {
+
+ if (!_strcmpi(argv[2], "SID"))
+ IsSid = TRUE;
+
+ if (!_strcmpi(argv[2], "FILE")) {
+ if (argc < 5) {
+ ShowUsageAdd();
+ exit(0);
+ }
+
+ FileName = argv[Index];
+ Index++;
+ FileCount = atoi(argv[Index]);
+ Index++;
+ IsFile = TRUE;
+
+ if (argc > Index + 1)
+ // Save off argv so if it is productname we don't ucase it
+ strcpy(tProduct, argv[Index]);
+
+ }
+
+ lstrcpy(UserName, TEXT(DEFAULT_USER));
+ aUser = DEFAULT_USER;
+
+ lstrcpy(ProductName, TEXT(DEFAULT_PRODUCT));
+ aProduct = DEFAULT_PRODUCT;
+
+ lstrcpy(Version, TEXT(DEFAULT_VERSION));
+ aVersion = DEFAULT_VERSION;
+
+ // Save off argv so if it is username so we don't ucase it
+ if (argc > Index)
+ strcpy(tUser, argv[Index]);
+
+ if ((argc > Index + 1) && (!_strcmpi(argv[Index], "NOFREE"))) {
+ if (argc < Index + 2) {
+ ShowUsageAdd();
+ exit(0);
+ }
+
+ Index++;
+ strcpy(tUser, argv[Index]);
+ NoFree = TRUE;
+
+ if (IsFile)
+ if (argc > Index + 1)
+ strcpy(tProduct, argv[Index]);
+
+ }
+
+ if ((argc > Index + 1) && (!_strcmpi(argv[Index], "ADMIN"))) {
+ if (argc < Index + 2) {
+ ShowUsageAdd();
+ exit(0);
+ }
+
+ Index++;
+ strcpy(tUser, argv[Index]);
+ IsAdmin = TRUE;
+
+ if (IsFile)
+ if (argc > Index + 1)
+ strcpy(tProduct, argv[Index]);
+
+ }
+
+ //
+ // If this is "USER" or "SID" then the next parm is the username, else
+ // if "FILE" then it can only be the product and version.
+ //
+ if (!IsFile) {
+ // Convert UserName to Unicode
+ aUser = tUser;
+ OemToCharBuff(aUser, UserName, strlen(aUser) + 1);
+ Index++;
+ }
+
+ // Check for product and version
+ if (argc > Index + 1) {
+ // Convert ProductName to Unicode
+ if (IsFile)
+ aProduct = tProduct;
+ else
+ aProduct = argv[Index];
+
+ OemToCharBuff(aProduct, ProductName, strlen(aProduct) + 1);
+ Index++;
+
+ // Convert Version to Unicode
+ aVersion = argv[Index];
+ OemToCharBuff(aVersion, Version, strlen(aVersion) + 1);
+ Index++;
+ }
+
+ //
+ // If "FILE" then branch off to file reading sub-routines now that
+ // we have all the defaults from the command line.
+ //
+ if (IsFile) {
+ ReadLicenses(FileName, FileCount);
+ return;
+ }
+
+ //
+ // Handle "SID" and "USER"
+ //
+ if (IsSid) {
+
+ if(NameToSid(TEXT(""), UserName, bufSID, &cbbufSid)) {
+ RtlConvertSidToUnicodeString(&UString, (PSID) bufSID, TRUE);
+ WideCharToMultiByte(CP_ACP, 0, UString.Buffer, -1, tmpStr, 100, NULL, NULL);
+ printf("Added Prod: %s Ver: %s User: %s\n", aProduct, aVersion, tmpStr);
+ RtlFreeUnicodeString(&UString);
+
+ Data.DataType = 1;
+ Data.Data = (PVOID) bufSID;
+ Data.IsAdmin = IsAdmin;
+
+ RetCode = (*LicenseRequest) ( ProductName, Version, plHandle, &Data);
+ printf("LicenseRequest Return Code: 0X%XH\n",RetCode);
+ } else {
+ printf("\nWARNING! SID Not Converted\n");
+ return;
+ }
+
+ } else {
+ Data.DataType = 0;
+ Data.Data = (PVOID) UserName;
+ Data.IsAdmin = IsAdmin;
+
+ RetCode = (*LicenseRequest) ( ProductName, Version, plHandle, &Data);
+ printf("Added Prod: %s Ver: %s User: %s\n", aProduct, aVersion, aUser);
+ printf("LicenseRequest Return Code: 0X%XH\n",RetCode);
+ }
+
+ if (!NoFree) {
+ RetCode = (*LicenseFree) ( *plHandle );
+ printf("LicenseFree Return Code: 0X%XH\n",RetCode);
+ }
+ } else {
+ ShowUsageAdd();
+ exit(0);
+ }
+
+} // DispatchAdd
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID ShowUsageFile( ) {
+ printf("The syntax of this command is:\n\n");
+ printf("%s FILE [filename]\n", ProgramName);
+
+} // ShowUsageFile
+
+
+VOID DispatchFile( ) {
+} // DispatchFile
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID ShowUsageReplicate( ) {
+ printf("The syntax of this command is:\n\n");
+ printf("%s REPLICATE FORCE\n", ProgramName);
+
+} // ShowUsageReplicate
+
+
+VOID DispatchReplicate() {
+ Index++;
+
+ if (NumberArguments <= Index) {
+ ShowUsageReplicate();
+ exit(0);
+ }
+
+ if (!_strcmpi(Arguments[Index], "FORCE")) {
+ LlsDbgReplicationForce( );
+ }
+
+} // DispatchReplicate
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID DispatchFlush() {
+ Index++;
+
+ LlsDbgDatabaseFlush( );
+
+} // DispatchReplicate
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID InitRpcFunctions() {
+
+ LlsRPCHandle = LoadLibrary(TEXT("LLSRPC.DLL"));
+ if (LlsRPCHandle == NULL) {
+ printf("Error loading LLSRPC.DLL\n");
+ exit(1);
+ }
+
+ pLlsConnectEnterprise = GetProcAddress(LlsRPCHandle, ("LlsConnectEnterpriseW"));
+ pLlsClose = GetProcAddress(LlsRPCHandle, ("LlsClose"));
+ pLlsFreeMemory = GetProcAddress(LlsRPCHandle, ("LlsFreeMemory"));
+ pLlsLicenseEnum = GetProcAddress(LlsRPCHandle, ("LlsLicenseEnumW"));
+ pLlsLicenseAdd = GetProcAddress(LlsRPCHandle, ("LlsLicenseAddW"));
+ pLlsProductEnum = GetProcAddress(LlsRPCHandle, ("LlsProductEnumW"));
+ pLlsProductUserEnum = GetProcAddress(LlsRPCHandle, ("LlsProductUserEnumW"));
+ pLlsProductLicenseEnum = GetProcAddress(LlsRPCHandle, ("LlsProductLicenseEnumW"));
+ pLlsUserEnum = GetProcAddress(LlsRPCHandle, ("LlsUserEnumW"));
+ pLlsUserProductEnum = GetProcAddress(LlsRPCHandle, ("LlsUserProductEnumW"));
+ pLlsUserProductDelete = GetProcAddress(LlsRPCHandle, ("LlsUserProductDeleteW"));
+ pLlsUserInfoGet = GetProcAddress(LlsRPCHandle, ("LlsUserInfoGetW"));
+ pLlsUserInfoSet = GetProcAddress(LlsRPCHandle, ("LlsUserInfoSetW"));
+ pLlsUserDelete = GetProcAddress(LlsRPCHandle, ("LlsUserDeleteW"));
+ pLlsMappingEnum = GetProcAddress(LlsRPCHandle, ("LlsGroupEnumW"));
+ pLlsMappingInfoGet = GetProcAddress(LlsRPCHandle, ("LlsGroupInfoGetW"));
+ pLlsMappingInfoSet = GetProcAddress(LlsRPCHandle, ("LlsGroupInfoSetW"));
+ pLlsMappingUserEnum = GetProcAddress(LlsRPCHandle, ("LlsGroupUserEnumW"));
+ pLlsMappingUserAdd = GetProcAddress(LlsRPCHandle, ("LlsGroupUserAddW"));
+ pLlsMappingUserDelete = GetProcAddress(LlsRPCHandle, ("LlsGroupUserDeleteW"));
+ pLlsMappingAdd = GetProcAddress(LlsRPCHandle, ("LlsGroupAddW"));
+ pLlsMappingDelete = GetProcAddress(LlsRPCHandle, ("LlsGroupDeleteW"));
+ pLlsServiceInfoGet = GetProcAddress(LlsRPCHandle, ("LlsServiceInfoGetW"));
+ pLlsServiceInfoSet = GetProcAddress(LlsRPCHandle, ("LlsServiceInfoSetW"));
+
+ if ( (pLlsConnectEnterprise == NULL) || (pLlsClose == NULL) || (pLlsFreeMemory == NULL) ||
+ (pLlsLicenseEnum == NULL) || (pLlsLicenseAdd == NULL) || (pLlsProductEnum == NULL) ||
+ (pLlsProductUserEnum == NULL) || (pLlsProductLicenseEnum == NULL) ||
+ (pLlsUserEnum == NULL) || (pLlsUserProductEnum == NULL) || (pLlsUserInfoGet == NULL) ||
+ (pLlsUserInfoSet == NULL) || (pLlsUserDelete == NULL) || (pLlsMappingEnum == NULL) ||
+ (pLlsMappingInfoGet == NULL) || (pLlsMappingInfoSet == NULL) || (pLlsMappingUserEnum == NULL) ||
+ (pLlsMappingUserAdd == NULL) || (pLlsMappingUserDelete == NULL) ||
+ (pLlsMappingAdd == NULL) || (pLlsMappingDelete == NULL) || (pLlsServiceInfoGet == NULL) ||
+ (pLlsServiceInfoSet == NULL) ) {
+
+ printf("Error loading functions from LLSRPC.DLL\n");
+ exit(1);
+ }
+
+} // InitRpcFunctions
+
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID ShowUsageLlsLicenseEnum( ) {
+ printf("The syntax of this command is:\n\n");
+ printf("%s RPC [\\\\server] LlsLicenseEnum\n", ProgramName);
+} // ShowUsageLlsLicenseEnum
+
+
+VOID Do_LlsLicenseEnum() {
+ NTSTATUS Status;
+ ULONG i;
+ DWORD Level;
+ PLLS_LICENSE_INFO_0 LicenseInfo;
+ DWORD MaxLength, EntriesRead, TotalEntries, ResumeHandle;
+ static char Product[MAX_CHAR_LEN];
+ static char Admin[MAX_CHAR_LEN];
+ static char Comment[MAX_CHAR_LEN];
+
+ Level = 0;
+ MaxLength = MAXULONG;
+ EntriesRead = TotalEntries = ResumeHandle = 0;
+
+ printf("UserEnum Level 0\n");
+ Status = (*pLlsLicenseEnum) ( RPCHandle, Level, &LicenseInfo, MaxLength, &EntriesRead, &TotalEntries, &ResumeHandle );
+
+ if (Status == ERROR_SUCCESS) {
+ for (i = 0; i < EntriesRead; i++) {
+ WideCharToMultiByte(CP_ACP, 0, LicenseInfo[i].Product, -1, Product, 100, NULL, NULL);
+ WideCharToMultiByte(CP_ACP, 0, LicenseInfo[i].Admin, -1, Admin, 100, NULL, NULL);
+ WideCharToMultiByte(CP_ACP, 0, LicenseInfo[i].Comment, -1, Comment, 100, NULL, NULL);
+ printf(" Prod: %s Qty: %lu Admin: %s Comment: %s\n", Product, LicenseInfo[i].Quantity, Admin, Comment);
+ }
+ }
+
+} // Do_LlsLicenseEnum
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID ShowUsageLlsLicenseAdd( ) {
+ printf("The syntax of this command is:\n\n");
+ printf("%s RPC [\\\\server] LlsLicenseAdd product admin quantity comment\n", ProgramName);
+} // ShowUsageLlsLicenseAdd
+
+
+VOID Do_LlsLicenseAdd() {
+ NTSTATUS Status;
+ DWORD Level = 0;
+ LONG Quantity;
+ LLS_LICENSE_INFO_0 LicenseInfo;
+ static TCHAR Admin[MAX_CHAR_LEN];
+ static TCHAR Comment[MAX_CHAR_LEN];
+ static TCHAR Product[MAX_CHAR_LEN];
+
+ //
+ // Check for level
+ //
+ if (NumberArguments <= Index) {
+ ShowUsageLlsLicenseAdd();
+ exit(0);
+ }
+
+ OemToCharBuff(Arguments[Index], Product, strlen(Arguments[Index]) + 1);
+ Index++;
+
+ OemToCharBuff(Arguments[Index], Admin, strlen(Arguments[Index]) + 1);
+ Index++;
+
+ Quantity = (LONG) atoi(Arguments[Index]);
+ Index++;
+
+ OemToCharBuff(Arguments[Index], Comment, strlen(Arguments[Index]) + 1);
+ Index++;
+
+ LicenseInfo.Product = Product;
+ LicenseInfo.Quantity = Quantity;
+ LicenseInfo.Date = 0;
+ LicenseInfo.Admin = Admin;
+ LicenseInfo.Comment = Comment;
+
+ Status = (*pLlsLicenseAdd) ( RPCHandle, Level, (LPBYTE) &LicenseInfo );
+
+} // Do_LlsLicenseAdd
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID ShowUsageLlsProductEnum( ) {
+ printf("The syntax of this command is:\n\n");
+ printf("%s RPC [\\\\server] LlsProductEnum level\n", ProgramName);
+} // ShowUsageLlsProductEnum
+
+
+VOID Do_LlsProductEnum() {
+ NTSTATUS Status;
+ ULONG i;
+ DWORD Level;
+ PLLS_PRODUCT_INFO_0 ProductInfo0;
+ PLLS_PRODUCT_INFO_1 ProductInfo1;
+ DWORD MaxLength, EntriesRead, TotalEntries, ResumeHandle;
+
+ //
+ // Check for level
+ //
+ if (NumberArguments <= Index) {
+ ShowUsageLlsProductEnum();
+ exit(0);
+ }
+
+ Level = (DWORD) atoi(Arguments[Index]);
+ MaxLength = MAXULONG;
+ EntriesRead = TotalEntries = ResumeHandle = 0;
+ if (Level == 0) {
+ printf("ProductEnum Level 0\n");
+ Status = (*pLlsProductEnum) ( RPCHandle, Level, &ProductInfo0, MaxLength, &EntriesRead, &TotalEntries, &ResumeHandle );
+
+ if (Status == ERROR_SUCCESS) {
+ for (i = 0; i < EntriesRead; i++) {
+ WideCharToMultiByte(CP_ACP, 0, ProductInfo0[i].Product, -1, tmpStr, 100, NULL, NULL);
+ printf(" Name: %s\n", tmpStr);
+ }
+ }
+ } else if (Level == 1) {
+ printf("ProductEnum Level 1\n");
+ Status = (*pLlsProductEnum) ( RPCHandle, Level, &ProductInfo1, MaxLength, &EntriesRead, &TotalEntries, &ResumeHandle );
+
+ if (Status == ERROR_SUCCESS) {
+ for (i = 0; i < EntriesRead; i++) {
+ WideCharToMultiByte(CP_ACP, 0, ProductInfo1[i].Product, -1, tmpStr, 100, NULL, NULL);
+ printf(" Name: %s\n", tmpStr);
+ }
+ }
+ } else {
+ printf("LlsProductEnum Incorrect Level\n");
+ exit(0);
+ }
+
+} // Do_LlsProductEnum
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID ShowUsageLlsProductUserEnum( ) {
+ printf("The syntax of this command is:\n\n");
+ printf("%s RPC [\\\\server] LlsProductUserEnum level product\n", ProgramName);
+} // ShowUsageLlsProductUserEnum
+
+
+VOID Do_LlsProductUserEnum() {
+
+ //
+ // Check for level
+ //
+ if (NumberArguments <= Index) {
+ ShowUsageLlsProductUserEnum();
+ exit(0);
+ }
+
+// Status = (*pLlsProductUserEnum) ( RPCHandle );
+
+} // Do_LlsProductUserEnum
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID ShowUsageLlsProductLicenseEnum( ) {
+ printf("The syntax of this command is:\n\n");
+ printf("%s RPC [\\\\server] LlsProductLicenseEnum level product version\n", ProgramName);
+} // ShowUsageLlsProductLicenseEnum
+
+
+VOID Do_LlsProductLicenseEnum() {
+
+ //
+ // Check for level
+ //
+ if (NumberArguments <= Index) {
+ ShowUsageLlsProductLicenseEnum();
+ exit(0);
+ }
+
+// Status = (*pLlsProductLicenseEnum) ( RPCHandle );
+
+} // Do_LlsProductLicenseEnum
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID ShowUsageLlsUserEnum( ) {
+ printf("The syntax of this command is:\n\n");
+ printf("%s RPC [\\\\server] LlsUserEnum level\n", ProgramName);
+} // ShowUsageLlsUserEnum
+
+
+VOID Do_LlsUserEnum() {
+ NTSTATUS Status;
+ ULONG i;
+ DWORD Level;
+ PLLS_USER_INFO_0 UserInfo0;
+ PLLS_USER_INFO_1 UserInfo1;
+ DWORD MaxLength, EntriesRead, TotalEntries, ResumeHandle;
+
+ //
+ // Check for level
+ //
+ if (NumberArguments <= Index) {
+ ShowUsageLlsUserEnum();
+ exit(0);
+ }
+
+ Level = (DWORD) atoi(Arguments[Index]);
+ MaxLength = MAXULONG;
+ EntriesRead = TotalEntries = ResumeHandle = 0;
+ if (Level == 0) {
+ printf("UserEnum Level 0\n");
+ Status = (*pLlsUserEnum) ( RPCHandle, Level, &UserInfo0, MaxLength, &EntriesRead, &TotalEntries, &ResumeHandle );
+
+ if (Status == ERROR_SUCCESS) {
+ for (i = 0; i < EntriesRead; i++) {
+ WideCharToMultiByte(CP_ACP, 0, UserInfo0[i].Name, -1, tmpStr, 100, NULL, NULL);
+ printf(" Name: %s\n", tmpStr);
+ }
+ }
+ } else if (Level == 1) {
+ printf("UserEnum Level 1\n");
+ Status = (*pLlsUserEnum) ( RPCHandle, Level, &UserInfo1, MaxLength, &EntriesRead, &TotalEntries, &ResumeHandle );
+
+ if (Status == ERROR_SUCCESS) {
+ for (i = 0; i < EntriesRead; i++) {
+ WideCharToMultiByte(CP_ACP, 0, UserInfo1[i].Name, -1, tmpStr, 100, NULL, NULL);
+ printf(" Name: %s\n", tmpStr);
+ }
+ }
+ } else {
+ printf("LlsUserEnum Incorrect Level\n");
+ exit(0);
+ }
+
+} // Do_LlsUserEnum
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID ShowUsageLlsUserProductEnum( ) {
+ printf("The syntax of this command is:\n\n");
+ printf("%s RPC [\\\\server] LlsUserProductEnum level user\n", ProgramName);
+} // ShowUsageLlsUserProductEnum
+
+
+VOID Do_LlsUserProductEnum() {
+ NTSTATUS Status;
+ ULONG i;
+ DWORD Level;
+ PLLS_USER_PRODUCT_INFO_0 ProductInfo0;
+ PLLS_USER_PRODUCT_INFO_1 ProductInfo1;
+ TCHAR User[MAX_CHAR_LEN];
+ DWORD MaxLength, EntriesRead, TotalEntries, ResumeHandle;
+
+ //
+ // Check for level
+ //
+ if (NumberArguments <= Index) {
+ ShowUsageLlsUserProductEnum();
+ exit(0);
+ }
+
+ Level = (DWORD) atoi(Arguments[Index]);
+ Index++;
+
+ MaxLength = MAXULONG;
+ EntriesRead = TotalEntries = ResumeHandle = 0;
+
+ OemToCharBuff(Arguments[Index], User, strlen(Arguments[Index]) + 1);
+ Index++;
+
+ if (Level == 0) {
+ printf("UserProductEnum Level 0\n");
+ Status = (*pLlsUserProductEnum) ( RPCHandle, User, Level, &ProductInfo0, MaxLength, &EntriesRead, &TotalEntries, &ResumeHandle );
+
+ if (Status == ERROR_SUCCESS) {
+ for (i = 0; i < EntriesRead; i++) {
+ WideCharToMultiByte(CP_ACP, 0, ProductInfo0[i].Product, -1, tmpStr, 100, NULL, NULL);
+ printf(" Name: %s\n", tmpStr);
+ }
+ }
+ } else if (Level == 1) {
+ printf("UserProductEnum Level 1\n");
+ Status = (*pLlsUserProductEnum) ( RPCHandle, User, Level, &ProductInfo1, MaxLength, &EntriesRead, &TotalEntries, &ResumeHandle );
+
+ if (Status == ERROR_SUCCESS) {
+ for (i = 0; i < EntriesRead; i++) {
+ WideCharToMultiByte(CP_ACP, 0, ProductInfo1[i].Product, -1, tmpStr, 100, NULL, NULL);
+ printf(" Name: %s LastUsed: %lu Useage: %lu\n", tmpStr, ProductInfo1[i].LastUsed, ProductInfo1[i].UsageCount);
+ }
+ }
+ } else {
+ printf("LlsUserProductEnum Incorrect Level\n");
+ exit(0);
+ }
+
+} // Do_LlsUserProductEnum
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID ShowUsageLlsUserProductDelete( ) {
+ printf("The syntax of this command is:\n\n");
+ printf("%s RPC [\\\\server] LlsUserProductDelete user product\n", ProgramName);
+} // ShowUsageLlsUserProductDelete
+
+
+VOID Do_LlsUserProductDelete() {
+ NTSTATUS Status;
+ WCHAR wUsName[MAX_WSTR_LEN];
+ WCHAR wPrName[MAX_WSTR_LEN];
+ CHAR lpUser[MAX_CHAR_LEN];
+ CHAR lpProduct[MAX_CHAR_LEN];
+
+ if (NumberArguments <= Index) {
+ ShowUsageLlsUserProductDelete();
+ exit(0);
+ }
+
+ MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, Arguments[Index], -1, wUsName, MAX_WSTR_LEN);
+ Index++;
+
+ if (NumberArguments <= Index) {
+ ShowUsageLlsUserProductDelete();
+ exit(0);
+ }
+
+ MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, Arguments[Index], -1, wPrName, MAX_WSTR_LEN);
+
+
+ Status = (*pLlsUserProductDelete) ( RPCHandle, wUsName, wPrName);
+
+ WideCharToMultiByte(CP_ACP, 0, wUsName, -1, lpUser, MAX_CHAR_LEN, NULL, NULL);
+ WideCharToMultiByte(CP_ACP, 0, wPrName, -1, lpProduct, MAX_CHAR_LEN, NULL, NULL);
+ printf("Status= %ld, UserName= %s, Product= %s\n", Status, lpUser, lpProduct);
+
+} // Do_LlsUserProductDelete
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID ShowUsageLlsUserInfoGet( ) {
+ printf("The syntax of this command is:\n\n");
+ printf("%s RPC [\\\\server] LlsUserInfoGet level user\n", ProgramName);
+} // ShowUsageLlsUserInfoGet
+
+
+VOID Do_LlsUserInfoGet() {
+
+ //
+ // Check for level
+ //
+ if (NumberArguments <= Index) {
+ ShowUsageLlsUserInfoGet();
+ exit(0);
+ }
+
+// Status = (*pLlsUserInfoGet) ( RPCHandle );
+
+} // Do_LlsUserInfoGet
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID ShowUsageLlsUserInfoSet( ) {
+ printf("The syntax of this command is:\n\n");
+ printf("%s RPC [\\\\server] LlsUserInfoSet User [BACKOFFICE]\n", ProgramName);
+ printf(" if BACKOFFICE is specified flag is set to TRUE, else FALSE\n");
+} // ShowUsageLlsUserInfoSet
+
+
+VOID Do_LlsUserInfoSet() {
+ NTSTATUS Status;
+ DWORD dwLevel, dwFlags;
+ LLS_USER_INFO_1 LlsUserInfo1;
+ CHAR lpUser[MAX_CHAR_LEN];
+ WCHAR wUsName[MAX_WSTR_LEN];
+
+ memset(&LlsUserInfo1, 0, sizeof(LLS_USER_INFO_1));
+
+ dwFlags = 0;
+ dwLevel = 1;
+
+ if (NumberArguments <= Index) {
+ ShowUsageLlsUserInfoSet();
+ exit(0);
+ }
+
+ strcpy(lpUser, Arguments[Index]);
+ Index++;
+
+ if (NumberArguments > Index) {
+
+ if(!_strcmpi("BACKOFFICE", Arguments[Index])){
+ dwFlags = LLS_FLAG_SUITE_USE;
+
+ } else {
+ ShowUsageLlsUserInfoSet();
+ exit(0);
+ }
+ } else {
+ //
+ // Take away the BackOffice status
+ //
+ dwFlags = 0;
+
+ }
+
+ MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, lpUser, -1, wUsName, MAX_WSTR_LEN);
+ LlsUserInfo1.Name = wUsName;
+ LlsUserInfo1.Flags = dwFlags;
+
+
+ printf("\nUser=%s, dwFlags=%ld", lpUser, dwFlags);
+
+ Status = (*pLlsUserInfoSet) (RPCHandle, wUsName, dwLevel, (LPBYTE)(&LlsUserInfo1));
+
+ printf("\nStatus = %ld\n", Status);
+
+} // Do_LlsUserInfoSet
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID ShowUsageLlsUserDelete( ) {
+ printf("The syntax of this command is:\n\n");
+ printf("%s RPC [\\\\server] LlsUserDelete user\n", ProgramName);
+} // ShowUsageLlsUserDelete
+
+
+VOID Do_LlsUserDelete() {
+ NTSTATUS Status;
+ WCHAR wUsName[MAX_WSTR_LEN];
+
+ if (NumberArguments <= Index) {
+ ShowUsageLlsUserDelete();
+ exit(0);
+ }
+
+ MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, Arguments[Index], -1, wUsName, MAX_WSTR_LEN);
+
+ Status = (*pLlsUserDelete) ( RPCHandle, wUsName);
+ printf("\nStatus = %ld", Status);
+
+} // Do_LlsUserDelete
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID ShowUsageLlsMappingEnum( ) {
+ printf("The syntax of this command is:\n\n");
+ printf("%s RPC [\\\\server] LlsGroupEnum level\n", ProgramName);
+} // ShowUsageLlsMappingEnum
+
+
+VOID Do_LlsMappingEnum() {
+ NTSTATUS Status;
+ ULONG i;
+ DWORD Level;
+ PLLS_GROUP_INFO_0 MappingInfo0;
+ PLLS_GROUP_INFO_1 MappingInfo1;
+ char MappingName[MAX_CHAR_LEN];
+ char Comment[MAX_CHAR_LEN];
+ DWORD MaxLength, EntriesRead, TotalEntries, ResumeHandle;
+
+ //
+ // Check for level
+ //
+ if (NumberArguments <= Index) {
+ ShowUsageLlsUserEnum();
+ exit(0);
+ }
+
+ Level = (DWORD) atoi(Arguments[Index]);
+ MaxLength = MAXULONG;
+ EntriesRead = TotalEntries = ResumeHandle = 0;
+ if (Level == 0) {
+ printf("GroupEnum Level 0\n");
+ Status = (*pLlsMappingEnum) ( RPCHandle, Level, &MappingInfo0, MaxLength, &EntriesRead, &TotalEntries, &ResumeHandle );
+
+ if (Status == ERROR_SUCCESS) {
+ for (i = 0; i < EntriesRead; i++) {
+ WideCharToMultiByte(CP_ACP, 0, MappingInfo0[i].Name, -1, tmpStr, 100, NULL, NULL);
+ printf(" Name: %s\n", tmpStr);
+ }
+ }
+ } else if (Level == 1) {
+ printf("GroupEnum Level 1\n");
+ Status = (*pLlsMappingEnum) ( RPCHandle, Level, &MappingInfo1, MaxLength, &EntriesRead, &TotalEntries, &ResumeHandle );
+
+ if (Status == ERROR_SUCCESS) {
+ for (i = 0; i < EntriesRead; i++) {
+ WideCharToMultiByte(CP_ACP, 0, MappingInfo1[i].Name, -1, MappingName, 100, NULL, NULL);
+ WideCharToMultiByte(CP_ACP, 0, MappingInfo1[i].Comment, -1, Comment, 100, NULL, NULL);
+ printf(" Name: %s Comment: %s Licenses: %li\n", MappingName, Comment, MappingInfo1[i].Licenses);
+ }
+ }
+ } else {
+ printf("LlsGroupEnum Incorrect Level\n");
+ exit(0);
+ }
+
+
+} // Do_LlsMappingEnum
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID ShowUsageLlsMappingInfoGet( ) {
+ printf("The syntax of this command is:\n\n");
+ printf("%s RPC [\\\\server] LlsGroupInfoGet level LicenseGroup\n", ProgramName);
+} // ShowUsageLlsMappingInfoGet
+
+
+VOID Do_LlsMappingInfoGet() {
+
+ //
+ // Check for level
+ //
+ if (NumberArguments <= Index) {
+ ShowUsageLlsMappingInfoGet();
+ exit(0);
+ }
+
+// Status = (*pLlsMappingInfoGet) ( RPCHandle );
+
+} // Do_LlsMappingInfoGet
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID ShowUsageLlsMappingInfoSet( ) {
+ printf("The syntax of this command is:\n\n");
+ printf("%s RPC [\\\\server] LlsGroupInfoSet Level LicenseGroup licenses comment\n", ProgramName);
+} // ShowUsageLlsMappingInfoSet
+
+
+VOID Do_LlsMappingInfoSet() {
+ NTSTATUS Status;
+ DWORD dwLevel, dwLicenses;
+ LLS_GROUP_INFO_1 GpInfo;
+ WCHAR wGpName[MAX_WSTR_LEN];
+ WCHAR wComment[MAX_WSTR_LEN];
+
+ memset(&GpInfo, 0, sizeof(LLS_GROUP_INFO_1));
+
+ //
+ // Check for level
+ //
+ if (NumberArguments <= Index) {
+ ShowUsageLlsMappingInfoSet();
+ exit(0);
+ }
+ dwLevel = (DWORD)atoi(Arguments[Index]);
+
+ Index++;
+ if (NumberArguments <= Index) {
+ ShowUsageLlsMappingInfoSet();
+ exit(0);
+ }
+ MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, Arguments[Index], -1, wGpName, MAX_WSTR_LEN);
+
+ Index++;
+ if (NumberArguments <= Index) {
+ ShowUsageLlsMappingInfoSet();
+ exit(0);
+ }
+ dwLicenses = (DWORD)atoi(Arguments[Index]);
+
+ Index++;
+ if (NumberArguments > Index)
+ MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, Arguments[Index], -1, wComment, MAX_WSTR_LEN);
+ else
+ lstrcpy(wComment, L"");
+
+ GpInfo.Name = wGpName;
+ GpInfo.Comment = wComment;
+ GpInfo.Licenses = dwLicenses;
+
+ Status = (*pLlsMappingInfoSet) ( RPCHandle, wGpName, dwLevel,(LPBYTE)(&GpInfo));
+ printf("\nStatus = %ld\n",Status);
+} // Do_LlsMappingInfoSet
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID ShowUsageLlsMappingUserEnum( ) {
+ printf("The syntax of this command is:\n\n");
+ printf("%s RPC [\\\\server] LlsGroupUserEnum level LicenseGroup\n", ProgramName);
+} // ShowUsageLlsMappingUserEnum
+
+
+VOID Do_LlsMappingUserEnum() {
+
+ //
+ // Check for level
+ //
+ if (NumberArguments <= Index) {
+ ShowUsageLlsMappingUserEnum();
+ exit(0);
+ }
+
+// Status = (*pLlsMappingUserEnum) ( RPCHandle );
+
+} // Do_LlsMappingUserEnum
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID ShowUsageLlsMappingUserAdd( ) {
+ printf("The syntax of this command is:\n\n");
+ printf("%s RPC [\\\\server] LlsGroupUserAdd LicenseGroup user\n", ProgramName);
+} // ShowUsageLlsMappingUserAdd
+
+
+VOID Do_LlsMappingUserAdd() {
+ NTSTATUS Status;
+ static TCHAR Mapping[MAX_CHAR_LEN];
+ static TCHAR User[MAX_CHAR_LEN];
+
+ //
+ // Check for level
+ //
+ if (NumberArguments <= Index) {
+ ShowUsageLlsMappingUserAdd();
+ exit(0);
+ }
+
+ OemToCharBuff(Arguments[Index], Mapping, strlen(Arguments[Index]) + 1);
+ Index++;
+
+ OemToCharBuff(Arguments[Index], User, strlen(Arguments[Index]) + 1);
+ Index++;
+
+ Status = (*pLlsMappingUserAdd) ( RPCHandle, Mapping, User );
+
+} // Do_LlsMappingUserAdd
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID ShowUsageLlsMappingUserDelete( ) {
+ printf("The syntax of this command is:\n\n");
+ printf("%s RPC [\\\\server] LlsGroupUserDelete LicenseGroup User\n", ProgramName);
+} // ShowUsageLlsMappingUserDelete
+
+
+VOID Do_LlsMappingUserDelete() {
+ NTSTATUS Status;
+ WCHAR wUsName[MAX_WSTR_LEN];
+ WCHAR wGpName[MAX_WSTR_LEN];
+
+ if (NumberArguments <= Index) {
+ ShowUsageLlsMappingUserDelete();
+ exit(0);
+ }
+
+ MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, Arguments[Index], -1, wGpName, MAX_WSTR_LEN);
+
+ Index++;
+ if (NumberArguments <= Index) {
+ ShowUsageLlsMappingUserDelete();
+ exit(0);
+ }
+
+ MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, Arguments[Index], -1, wUsName, MAX_WSTR_LEN);
+
+ Status = (*pLlsMappingUserDelete) ( RPCHandle, wGpName, wUsName);
+ printf("\nStatus = %ld\n",Status);
+
+} // Do_LlsMappingUserDelete
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID ShowUsageLlsMappingAdd( ) {
+ printf("The syntax of this command is:\n\n");
+ printf("%s RPC [\\\\server] LlsGroupAdd LicenseGroup licenses comment\n", ProgramName);
+} // ShowUsageLlsMappingAdd
+
+
+VOID Do_LlsMappingAdd() {
+ NTSTATUS Status;
+ DWORD Level = 1;
+ LONG Quantity;
+ LLS_GROUP_INFO_1 MappingInfo;
+ static TCHAR Name[MAX_CHAR_LEN];
+ static TCHAR Comment[MAX_CHAR_LEN];
+
+ //
+ // Check for level
+ //
+ if (NumberArguments <= Index) {
+ ShowUsageLlsLicenseAdd();
+ exit(0);
+ }
+
+ OemToCharBuff(Arguments[Index], Name, strlen(Arguments[Index]) + 1);
+ Index++;
+
+ Quantity = (LONG) atoi(Arguments[Index]);
+ Index++;
+
+ OemToCharBuff(Arguments[Index], Comment, strlen(Arguments[Index]) + 1);
+ Index++;
+
+ MappingInfo.Name = Name;
+ MappingInfo.Comment = Comment;
+ MappingInfo.Licenses = Quantity;
+
+ Status = (*pLlsMappingAdd) ( RPCHandle, Level, (LPBYTE) &MappingInfo );
+
+} // Do_LlsMappingAdd
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID ShowUsageLlsMappingDelete( ) {
+ printf("The syntax of this command is:\n\n");
+ printf("%s RPC [\\\\server] LlsGroupDelete LicenseGroup\n", ProgramName);
+} // ShowUsageLlsMappingDelete
+
+
+VOID Do_LlsMappingDelete() {
+ NTSTATUS Status;
+ WCHAR wGpName[MAX_WSTR_LEN];
+
+ if (NumberArguments <= Index) {
+ ShowUsageLlsMappingDelete();
+ exit(0);
+ }
+
+ MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, Arguments[Index], -1, wGpName, MAX_WSTR_LEN);
+
+ Status = (*pLlsMappingDelete) ( RPCHandle, wGpName);
+ printf("\nStatus = %ld\n",Status);
+
+} // Do_LlsMappingDelete
+
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID ShowUsageLlsServiceInfoGet( ) {
+ printf("The syntax of this command is:\n\n");
+ printf("%s RPC [\\\\server] LlsServiceInfoGet\n", ProgramName);
+} // ShowUsageLlsServiceInfoGet
+
+
+VOID Do_LlsServiceInfoGet() {
+
+// Status = (*pLlsServiceInfoGet) ( RPCHandle );
+
+} // Do_LlsServiceInfoGet
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID ShowUsageLlsServiceInfoSet( ) {
+ printf("The syntax of this command is:\n\n");
+ printf("%s RPC [\\\\server] LlsServiceInfoGet repl-mode time enterprise\n", ProgramName);
+ printf(" repl-mode: 0 = Replicate Every...(time)\n");
+ printf(" 1 = Replicate @... (time)\n");
+} // ShowUsageLlsServiceInfoSet
+
+
+VOID Do_LlsServiceInfoSet() {
+
+ //
+ // Check for level
+ //
+ if (NumberArguments <= Index) {
+ ShowUsageLlsServiceInfoSet();
+ exit(0);
+ }
+
+// Status = (*pLlsServiceInfoSet) ( RPCHandle );
+
+} // Do_LlsServiceInfoSet
+
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID ShowUsageRpc( ) {
+ printf("The syntax of this command is:\n\n");
+ printf("%s RPC [\\\\server] [\n", ProgramName);
+ printf("\tLlsLicenseEnum | LlsLicenseAdd | LlsProductEnum |\n");
+ printf("\tLlsProductUserEnum | LlsProductLicenseEnum | LlsUserEnum |\n");
+ printf("\tLlsUserProductEnum | LlsUserInfoGet | LlsUserInfoSet |\n");
+ printf("\tLlsUserDelete | LlsGroupEnum | LlsGroupInfoGet |\n");
+ printf("\tLlsGroupInfoSet | LlsGroupUserEnum | LlsGroupUserAdd |\n");
+ printf("\tLlsGroupUserDelete | LlsGroupAdd | LlsGroupDelete |\n");
+ printf("\tLlsServiceInfoGet | LlsServiceInfoSet ]\n");
+
+} // ShowUsageRpc
+
+
+VOID DispatchRpc() {
+ NTSTATUS Status;
+ PLLS_CONNECT_INFO_0 pConnectInfo;
+
+ Index++;
+
+ if ((NumberArguments <= Index) || (strlen(Arguments[Index]) < 3) ) {
+ ShowUsageRpc();
+ exit(0);
+ }
+
+ //
+ // Check if a server has been specified (we already did length check
+ // above to prevent access violation.
+ //
+ if ((Arguments[Index][0] == '\\') && (Arguments[Index][1] == '\\')) {
+ LocalRPC = FALSE;
+ OemToCharBuff(Arguments[Index], Server, strlen(Arguments[Index]) + 1);
+ printf("RPC to Server: %s\n", Arguments[Index]);
+ Index++;
+ }
+
+ if (LocalRPC)
+ Status = (*pLlsConnectEnterprise) ( NULL, &RPCHandle, 0, (LPBYTE *) &pConnectInfo );
+ else
+ Status = (*pLlsConnectEnterprise) ( Server, &RPCHandle, 0, (LPBYTE *) &pConnectInfo );
+
+ if (Status != STATUS_SUCCESS) {
+ printf("Error: LlsConnectEnterprise failed: 0x%lX\n", Status);
+ exit(1);
+ }
+
+ if (RPCHandle == NULL) {
+ printf("Error: RPCHandle NULL\n");
+ exit(1);
+ }
+
+ //
+ // The API's (LlsConnectEnterprise, LlsClose, LlsFreeMemory) are used with all the
+ // other API's, so no provision is made for them.
+ //
+
+ Index++; // +1 to take care of actual RPC command
+ if (NumberArguments <= Index) {
+ ShowUsageRpc();
+ exit(0);
+ }
+
+ // LlsLicenseEnum
+ if (!_strcmpi(Arguments[Index - 1], "LLSLICENSEENUM"))
+ Do_LlsLicenseEnum();
+
+ // LlsLicenseAdd
+ if (!_strcmpi(Arguments[Index - 1], "LLSLICENSEADD"))
+ Do_LlsLicenseAdd();
+
+ // LlsProductEnum
+ if (!_strcmpi(Arguments[Index - 1], "LLSPRODUCTENUM"))
+ Do_LlsProductEnum();
+
+ // LlsProductUserEnum
+ if (!_strcmpi(Arguments[Index - 1], "LLSPRODUCTUSERENUM"))
+ Do_LlsProductUserEnum();
+
+ // LlsProductLicenseEnum
+ if (!_strcmpi(Arguments[Index - 1], "LLSPRODUCTLICENSEENUM"))
+ Do_LlsProductLicenseEnum();
+
+ // LlsUserEnum
+ if (!_strcmpi(Arguments[Index - 1], "LLSUSERENUM"))
+ Do_LlsUserEnum();
+
+ // LlsUserProductEnum
+ if (!_strcmpi(Arguments[Index - 1], "LLSUSERPRODUCTENUM"))
+ Do_LlsUserProductEnum();
+
+ // LlsUserInfoGet
+ if (!_strcmpi(Arguments[Index - 1], "LLSUSERINFOGET"))
+ Do_LlsUserInfoGet();
+
+ // LlsUserInfoSet
+ if (!_strcmpi(Arguments[Index - 1], "LLSUSERINFOSET"))
+ Do_LlsUserInfoSet();
+
+ // LlsUserDelete
+ if (!_strcmpi(Arguments[Index - 1], "LLSUSERDELETE"))
+ Do_LlsUserDelete();
+
+ // LlsMappingEnum
+ if (!_strcmpi(Arguments[Index - 1], "LLSGROUPENUM"))
+ Do_LlsMappingEnum();
+
+ // LlsMappingInfoGet
+ if (!_strcmpi(Arguments[Index - 1], "LLSGROUPINFOGET"))
+ Do_LlsMappingInfoGet();
+
+ // LlsMappingInfoSet
+ if (!_strcmpi(Arguments[Index - 1], "LLSGROUPINFOSET"))
+ Do_LlsMappingInfoSet();
+
+ // LlsMappingUserEnum
+ if (!_strcmpi(Arguments[Index - 1], "LLSGROUPUSERENUM"))
+ Do_LlsMappingUserEnum();
+
+ // LlsMappingUserAdd
+ if (!_strcmpi(Arguments[Index - 1], "LLSGROUPUSERADD"))
+ Do_LlsMappingUserAdd();
+
+ // LlsMappingUserDelete
+ if (!_strcmpi(Arguments[Index - 1], "LLSGROUPUSERDELETE"))
+ Do_LlsMappingUserDelete();
+
+ // LlsMappingAdd
+ if (!_strcmpi(Arguments[Index - 1], "LLSGROUPADD"))
+ Do_LlsMappingAdd();
+
+ // LlsMappingDelete
+ if (!_strcmpi(Arguments[Index - 1], "LLSGROUPDELETE"))
+ Do_LlsMappingDelete();
+
+ // LlsServiceInfoGet
+ if (!_strcmpi(Arguments[Index - 1], "LLSSERVICEINFOGET"))
+ Do_LlsServiceInfoGet();
+
+ // LlsServiceInfoSet
+ if (!_strcmpi(Arguments[Index - 1], "LLSSERVICEINFOSET"))
+ Do_LlsServiceInfoSet();
+
+ Status = (*pLlsClose) ( RPCHandle );
+
+} // DispatchRpc
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID ShowUsageTrace( ) {
+ printf("The syntax of this command is:\n\n");
+ printf("%s TRACE level\n\n", ProgramName);
+ printf(" level can be a combination of:\n\n");
+
+ printf(" TRACE_FUNCTION_TRACE 0x0001 ( 1)\n");
+ printf(" TRACE_WARNINGS 0x0002 ( 2)\n");
+ printf(" TRACE_PACK 0x0004 ( 4)\n");
+ printf(" TRACE_LICENSE_REQUEST 0x0008 ( 8)\n");
+ printf(" TRACE_LICENSE_FREE 0x0010 ( 16)\n");
+ printf(" TRACE_REGISTRY 0x0020 ( 32)\n");
+ printf(" TRACE_REPLICATION 0x0040 ( 64)\n");
+ printf(" TRACE_LPC 0x0080 ( 128)\n");
+ printf(" TRACE_RPC 0x0100 ( 256)\n");
+ printf(" TRACE_INIT 0x0200 ( 512)\n");
+ printf(" TRACE_DATABASE 0x0400 (1024)\n");
+} // ShowUsageTrace
+
+
+VOID DispatchTrace() {
+ Index++;
+
+ if (NumberArguments <= Index) {
+ ShowUsageTrace();
+ exit(0);
+ }
+
+ LlsDbgTraceSet( atoi(Arguments[Index]) );
+} // DispatchTrace
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID ShowUsageHelp( ) {
+ printf("The syntax of this command is:\n\n");
+ printf("%s HELP [ ADD | FILE | HELP | REPLICATE | RPC | TRACE ]\n", ProgramName);
+ exit(0);
+
+} // ShowUsageHelp
+
+
+VOID DispatchHelp() {
+ Index++;
+
+ if (NumberArguments <= Index) {
+ ShowUsageHelp();
+ exit(0);
+ }
+
+ if (!_strcmpi(Arguments[Index], "ADD")) {
+ ShowUsageAdd();
+ printf("\nGenerates license requests with usernames or SID's. The usernames\n");
+ printf("can be from the command line or a file.\n");
+ printf("\nUSER is for adding username\n");
+ printf("SID will convert the given username into a SID in the local domain\n");
+ printf(" if [NOFREE] is specified the license is not freed after the call\n");
+ printf(" if [ADMIN] is specified then the the user is considered an admim\n\n");
+ printf("FILE #lines will read from a file in format:\n");
+ printf("[NOFREE] [ADMIN] username [product version]\n");
+ printf(" quotes may be used to embed spaces in names and commas or\n");
+ printf(" spaces can be used to delimit the fields.\n");
+ printf("#lines gives the # of lines will be read, if 0 all the file is used\n");
+
+ } else if (!_strcmpi(Arguments[Index], "FILE")) {
+ ShowUsageFile();
+ printf("\nParses a file executing the commands in it - this works alot better\n");
+ printf("then putting llscmd's in a batch file since the DLL's and such don't\n");
+ printf("have to be continually re-loaded.\n");
+
+ } else if (!_strcmpi(Arguments[Index], "HELP")) {
+ ShowUsageHelp();
+ printf("\nWhat do you think? Gives help on the commands\n");
+
+ } else if (!_strcmpi(Arguments[Index], "REPLICATE")) {
+ ShowUsageReplicate();
+ printf("\nTells the service to allow or disallow replication requests and\n");
+ printf("can be used to force the service to replicate it's information up.\n");
+
+ } else if (!_strcmpi(Arguments[Index], "RPC")) {
+ ShowUsageRpc();
+ printf("\nAllows the issuing of RPC commands that the UI uses.\n");
+
+ } else if (!_strcmpi(Arguments[Index], "TRACE")) {
+ ShowUsageTrace();
+ printf("\nSets the level of debug output the service spits out to the debugger\n");
+ printf("Can be used to turn on API level tracing, or function entry/exit tracing\n");
+ printf("\n");
+ printf(" *** Note: LLSTRACE is much easier to use for setting tracing levels.\n");
+
+ } else
+ ShowUsageHelp();
+
+} // DispatchHelp
+
+
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID ShowUsage( ) {
+ printf("\n");
+ printf("***************************************************************\n");
+ printf("* License Logging Service (LLS) Debugging Program - Version 0.6\n");
+ printf("* Copyright (c) Microsoft Corp 1995 - All rights reserved\n");
+ printf("\n");
+
+ printf("The syntax of this command is:\n\n");
+ printf("%s [ ADD | FILE | HELP | REPLICATE | RPC | TRACE ]\n", ProgramName);
+ exit(0);
+
+} // ShowUsage
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID ArgvToArguments(int argc, char **argv) {
+ LONG i;
+
+ NumberArguments = 0;
+ Index = 0;
+
+ if (argc > 10)
+ return;
+
+ //
+ // Ignore argv[0] (program name) - otherwise loop through and copy
+ // parms to our array, converting to Unicode along the way...
+ //
+ for (i = 1; i < argc; i++) {
+ strcpy(Arguments[i-1], argv[i]);
+ NumberArguments++;
+ }
+
+} // ArgvToArguments
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID _CRTAPI1 main(int argc, char **argv) {
+ int i;
+
+ ProgramName = _strupr(argv[0]);
+ ArgvToArguments(argc, argv);
+
+ //
+ // If no command line arguments then we can't do much
+ //
+ if (NumberArguments == 0)
+ ShowUsage();
+
+ //
+ // Don't LLSInitLPC for Add so it works like regular program and you
+ // don't have the LPC port initialized twice by the same process...
+ //
+ if (!_strcmpi(Arguments[Index], "ADD")) {
+ LlsDllHandle = LoadLibrary(TEXT("NtLSAPI.DLL"));
+ if (LlsDllHandle == NULL) {
+ printf("Error loading NtLSAPI.DLL\n");
+ exit(1);
+ }
+
+ LicenseRequest = GetProcAddress(LlsDllHandle, "NtLicenseRequestW");
+ LicenseFree = GetProcAddress(LlsDllHandle, "NtLSFreeHandle");
+
+ if ((LicenseRequest == NULL) || (LicenseFree == NULL)) {
+ printf("Error loading LicenseRequest or LicenseFree\n");
+ exit(1);
+ }
+
+ DispatchAdd(argc, argv);
+ FreeLibrary(LlsDllHandle);
+
+ } else {
+
+ LlsDebugInit( );
+
+ if (!_strcmpi(Arguments[Index], "FILE")) {
+ InitRpcFunctions();
+ DispatchFile();
+ } else if (!_strcmpi(Arguments[Index], "HELP"))
+ DispatchHelp();
+
+ else if (!_strcmpi(Arguments[Index], "REPLICATE"))
+ DispatchReplicate();
+
+ else if (!_strcmpi(Arguments[Index], "RPC")) {
+ InitRpcFunctions();
+ DispatchRpc();
+ } else if (!_strcmpi(Arguments[Index], "TRACE"))
+ DispatchTrace();
+ else if (!_strcmpi(Arguments[Index], "FLUSH"))
+ DispatchFlush();
+ else
+ ShowUsage();
+ }
+
+} // main()
diff --git a/private/net/svcdlls/lls/test/llscmd/llscmd.rc b/private/net/svcdlls/lls/test/llscmd/llscmd.rc
new file mode 100644
index 000000000..81787e030
--- /dev/null
+++ b/private/net/svcdlls/lls/test/llscmd/llscmd.rc
@@ -0,0 +1,12 @@
+#include <windows.h>
+#include <ntverp.h>
+
+#define VER_FILETYPE VFT_APP
+#define VER_FILESUBTYPE VFT2_UNKNOWN
+#define VER_FILEDESCRIPTION_STR "Microsoft\256 License Server Cmd Test Utility"
+
+#define VER_INTERNALNAME_STR "llscmd.exe"
+#define VER_ORIGINALFILENAME_STR "llscmd.exe"
+
+#include <common.ver>
+
diff --git a/private/net/svcdlls/lls/test/llscmd/makefile b/private/net/svcdlls/lls/test/llscmd/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/net/svcdlls/lls/test/llscmd/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/net/svcdlls/lls/test/llscmd/sources b/private/net/svcdlls/lls/test/llscmd/sources
new file mode 100644
index 000000000..44d5bc90a
--- /dev/null
+++ b/private/net/svcdlls/lls/test/llscmd/sources
@@ -0,0 +1,72 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+
+Author:
+
+ Steve Wood (stevewo) 12-Apr-1990
+
+NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl
+
+!ENDIF
+
+MAJORCOMP=lls
+MINORCOMP=server
+
+TARGETNAME=llscmd
+TARGETPATH=obj
+TARGETTYPE=PROGRAM
+
+INCLUDES=$(BASEDIR)\public\sdk\inc;$(BASEDIR)\private\inc;..\..\inc
+
+
+
+SOURCES=llscmd.c llscmd.rc
+
+UMTYPE=console
+UMLIBS= \
+ ..\..\common\obj\*\llscomm.lib \
+ ..\common\obj\*\llsdbg.lib \
+ $(BASEDIR)\public\sdk\lib\*\rpcutil.lib \
+ $(BASEDIR)\public\sdk\lib\*\ntlsapi.lib \
+ $(BASEDIR)\public\sdk\lib\*\rpcrt4.lib \
+ $(BASEDIR)\public\sdk\lib\*\samlib.lib \
+ $(BASEDIR)\public\sdk\lib\*\samsrv.lib \
+ $(BASEDIR)\public\sdk\lib\*\ntdll.lib \
+ $(BASEDIR)\public\sdk\lib\*\shell32.lib \
+ $(BASEDIR)\public\sdk\lib\*\crtdll.lib \
+ $(BASEDIR)\Public\sdk\Lib\*\advapi32.lib \
+ $(BASEDIR)\public\sdk\lib\*\kernel32.lib \
+ $(BASEDIR)\public\sdk\lib\*\user32.lib
+
+
+TARGETLIBS= \
+ ..\..\common\obj\*\llscomm.lib \
+ ..\common\obj\*\llsdbg.lib \
+ $(BASEDIR)\public\sdk\lib\*\rpcutil.lib \
+ $(BASEDIR)\public\sdk\lib\*\rpcutil.lib \
+ $(BASEDIR)\public\sdk\lib\*\rpcrt4.lib \
+ $(BASEDIR)\public\sdk\lib\*\rpcndr.lib \
+ $(BASEDIR)\public\sdk\lib\*\kernel32.lib \
+ $(BASEDIR)\public\sdk\lib\*\advapi32.lib \
+ $(BASEDIR)\public\sdk\lib\*\samsrv.lib \
+ $(BASEDIR)\public\sdk\lib\*\nlrepl.lib \
+ $(BASEDIR)\public\sdk\lib\*\crtdll.lib \
+ $(BASEDIR)\Public\sdk\Lib\*\advapi32.lib \
+ $(BASEDIR)\public\sdk\lib\*\kernel32.lib \
+ $(BASEDIR)\public\sdk\lib\*\user32.lib
+
+C_DEFINES=-DRPC_NO_WINDOWS_H -DUNICODE -D_UNICODE
+USE_CRTDLL=1
diff --git a/private/net/svcdlls/lls/test/llsdbg/llsdbg.c b/private/net/svcdlls/lls/test/llsdbg/llsdbg.c
new file mode 100644
index 000000000..7750f03f4
--- /dev/null
+++ b/private/net/svcdlls/lls/test/llsdbg/llsdbg.c
@@ -0,0 +1,448 @@
+/*++
+
+Copyright (c) 1994 Microsoft Corporation
+
+Module Name:
+
+ LlsDbg.c
+
+Abstract:
+
+Author:
+
+ Arthur Hanson (arth) Dec 07, 1994
+
+Environment:
+
+Revision History:
+
+--*/
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windows.h>
+#include "resource.h"
+#include "..\..\inc\debug.h"
+#include "..\common\llsdbg.h"
+
+
+HINSTANCE hInst; // current instance
+
+TCHAR szAppName[] = TEXT("LlsDbg"); // The name of this application
+TCHAR ProgPath[MAX_PATH + 1];
+
+HICON MyIcon;
+HWND hDlgMain;
+
+
+DWORD DebugFlags = 0;
+LRESULT CALLBACK DlgMain( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam );
+
+#define ITEM_DATA_SIZE 60
+TCHAR ItemData[ITEM_DATA_SIZE + 1];
+
+/////////////////////////////////////////////////////////////////////////
+int APIENTRY
+WinMain(
+ HINSTANCE hInstance,
+ HINSTANCE hPrevInstance,
+ LPSTR lpCmdLine,
+ int nCmdShow
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ LPTSTR ptr;
+ DLGPROC lpproc;
+ HACCEL haccel;
+ MSG msg;
+
+ hInst = hInstance;
+
+ MyIcon = LoadIcon(hInst, szAppName);
+
+ if (!hPrevInstance) {
+ }
+
+ lpproc = MakeProcInstance((DLGPROC) DlgMain, hInst);
+ hDlgMain = CreateDialog(hInst, szAppName, NULL, lpproc);
+
+ ShowWindow(hDlgMain, nCmdShow);
+
+ while (GetMessage(&msg, NULL, 0, 0)) {
+ if ((hDlgMain == 0) || !IsDialogMessage(hDlgMain, &msg)) {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+ }
+
+ FreeProcInstance(lpproc);
+
+ DestroyIcon(MyIcon);
+
+ return msg.wParam;
+
+} // WinMain
+
+
+/////////////////////////////////////////////////////////////////////////
+LRESULT CALLBACK
+DlgTrace(
+ HWND hDlg,
+ UINT message,
+ WPARAM wParam,
+ LPARAM lParam
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ int wmId, wmEvent;
+ PAINTSTRUCT ps;
+ HDC hDC;
+ RECT rc;
+
+ switch (message) {
+ case WM_INITDIALOG:
+ PostMessage(hDlg, WM_COMMAND, ID_INIT, 0L);
+ break;
+
+ case WM_ERASEBKGND:
+
+ // Process so icon background isn't painted grey by CTL3D - main dlg
+ // can't be DS_MODALFRAME either, or else a frame is painted around
+ // the icon.
+ if (IsIconic(hDlg))
+ return TRUE;
+
+ break;
+
+ case WM_COMMAND:
+ wmId = LOWORD(wParam);
+ wmEvent = HIWORD(wParam);
+
+ switch (wmId) {
+ case ID_INIT:
+
+ case ID_UPDATE:
+ if (DebugFlags & TRACE_FUNCTION_TRACE)
+ SendDlgItemMessage(hDlg, IDC_FUNCTION, BM_SETCHECK, 1, 0);
+ else
+ SendDlgItemMessage(hDlg, IDC_FUNCTION, BM_SETCHECK, 0, 0);
+
+ if (DebugFlags & TRACE_WARNINGS)
+ SendDlgItemMessage(hDlg, IDC_WARNINGS, BM_SETCHECK, 1, 0);
+ else
+ SendDlgItemMessage(hDlg, IDC_WARNINGS, BM_SETCHECK, 0, 0);
+
+ if (DebugFlags & TRACE_PACK)
+ SendDlgItemMessage(hDlg, IDC_PACK, BM_SETCHECK, 1, 0);
+ else
+ SendDlgItemMessage(hDlg, IDC_PACK, BM_SETCHECK, 0, 0);
+
+ if (DebugFlags & TRACE_LICENSE_REQUEST)
+ SendDlgItemMessage(hDlg, IDC_REQUEST, BM_SETCHECK, 1, 0);
+ else
+ SendDlgItemMessage(hDlg, IDC_REQUEST, BM_SETCHECK, 0, 0);
+
+ if (DebugFlags & TRACE_LICENSE_FREE)
+ SendDlgItemMessage(hDlg, IDC_FREE, BM_SETCHECK, 1, 0);
+ else
+ SendDlgItemMessage(hDlg, IDC_FREE, BM_SETCHECK, 0, 0);
+
+ if (DebugFlags & TRACE_REGISTRY)
+ SendDlgItemMessage(hDlg, IDC_REGISTRY, BM_SETCHECK, 1, 0);
+ else
+ SendDlgItemMessage(hDlg, IDC_REGISTRY, BM_SETCHECK, 0, 0);
+
+ if (DebugFlags & TRACE_REPLICATION)
+ SendDlgItemMessage(hDlg, IDC_REPLICATION, BM_SETCHECK, 1, 0);
+ else
+ SendDlgItemMessage(hDlg, IDC_REPLICATION, BM_SETCHECK, 0, 0);
+
+ if (DebugFlags & TRACE_RPC)
+ SendDlgItemMessage(hDlg, IDC_RPC, BM_SETCHECK, 1, 0);
+ else
+ SendDlgItemMessage(hDlg, IDC_RPC, BM_SETCHECK, 0, 0);
+
+ if (DebugFlags & TRACE_INIT)
+ SendDlgItemMessage(hDlg, IDC_INIT, BM_SETCHECK, 1, 0);
+ else
+ SendDlgItemMessage(hDlg, IDC_INIT, BM_SETCHECK, 0, 0);
+
+ if (DebugFlags & TRACE_DATABASE)
+ SendDlgItemMessage(hDlg, IDC_DATABASE, BM_SETCHECK, 1, 0);
+ else
+ SendDlgItemMessage(hDlg, IDC_DATABASE, BM_SETCHECK, 0, 0);
+
+ break;
+
+ case IDC_SET:
+ DebugFlags = 0;
+
+ if (SendDlgItemMessage(hDlg, IDC_FUNCTION, BM_GETCHECK, 0, 0) == 1)
+ DebugFlags |= TRACE_FUNCTION_TRACE;
+
+ if (SendDlgItemMessage(hDlg, IDC_WARNINGS, BM_GETCHECK, 0, 0) == 1)
+ DebugFlags |= TRACE_WARNINGS;
+
+ if (SendDlgItemMessage(hDlg, IDC_PACK, BM_GETCHECK, 0, 0) == 1)
+ DebugFlags |= TRACE_PACK;
+
+ if (SendDlgItemMessage(hDlg, IDC_REQUEST, BM_GETCHECK, 0, 0) == 1)
+ DebugFlags |= TRACE_LICENSE_REQUEST;
+
+ if (SendDlgItemMessage(hDlg, IDC_FREE, BM_GETCHECK, 0, 0) == 1)
+ DebugFlags |= TRACE_LICENSE_FREE;
+
+ if (SendDlgItemMessage(hDlg, IDC_REGISTRY, BM_GETCHECK, 0, 0) == 1)
+ DebugFlags |= TRACE_REGISTRY;
+
+ if (SendDlgItemMessage(hDlg, IDC_REPLICATION, BM_GETCHECK, 0, 0) == 1)
+ DebugFlags |= TRACE_REPLICATION;
+
+ if (SendDlgItemMessage(hDlg, IDC_RPC, BM_GETCHECK, 0, 0) == 1)
+ DebugFlags |= TRACE_RPC;
+
+ if (SendDlgItemMessage(hDlg, IDC_INIT, BM_GETCHECK, 0, 0) == 1)
+ DebugFlags |= TRACE_INIT;
+
+ if (SendDlgItemMessage(hDlg, IDC_DATABASE, BM_GETCHECK, 0, 0) == 1)
+ DebugFlags |= TRACE_DATABASE;
+
+ LlsDbgTraceSet( DebugFlags );
+ break;
+
+ case IDC_RESET:
+ DebugFlags = 0;
+ PostMessage(hDlg, WM_COMMAND, ID_UPDATE, 0L);
+ break;
+
+ case IDCANCEL:
+ EndDialog(hDlg, 0);
+ return (TRUE);
+ break;
+
+ }
+
+ break;
+
+ default:
+ break;
+ }
+
+
+ return (FALSE); // Didn't process the message
+
+} // DlgTrace
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+DlgTrace_Do(HWND hDlg) {
+ DLGPROC lpfnDlg;
+
+ lpfnDlg = MakeProcInstance((DLGPROC)DlgTrace, hInst);
+ DialogBox(hInst, TEXT("LlsTrace"), hDlg, lpfnDlg) ;
+ FreeProcInstance(lpfnDlg);
+
+} // DlgTrace_Do
+
+
+/////////////////////////////////////////////////////////////////////////
+LRESULT CALLBACK
+DlgMain(
+ HWND hDlg,
+ UINT message,
+ WPARAM wParam,
+ LPARAM lParam
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ HWND hCtrl;
+ int wmId, wmEvent;
+ DWORD dwData, dwIndex;
+ PAINTSTRUCT ps;
+ HDC hDC;
+ RECT rc;
+
+ switch (message) {
+ case WM_INITDIALOG:
+ PostMessage(hDlg, WM_COMMAND, ID_INIT, 0L);
+
+ break;
+
+ case WM_DESTROY:
+ PostQuitMessage(0);
+ break;
+
+ case WM_PAINT:
+ hDC = BeginPaint(hDlg, &ps);
+ if (IsIconic(hDlg)) {
+ GetClientRect(hDlg, &rc);
+ DrawIcon(hDC, rc.left, rc.top, MyIcon);
+ }
+
+ EndPaint(hDlg, &ps);
+ break;
+
+ case WM_ERASEBKGND:
+
+ // Process so icon background isn't painted grey by CTL3D - main dlg
+ // can't be DS_MODALFRAME either, or else a frame is painted around
+ // the icon.
+ if (IsIconic(hDlg))
+ return TRUE;
+
+ break;
+
+ case WM_COMMAND:
+ wmId = LOWORD(wParam);
+ wmEvent = HIWORD(wParam);
+
+ switch (wmId) {
+ case ID_INIT:
+ LlsDebugInit( );
+
+ //
+ // Limit edit box to some reasonable value
+ //
+ hCtrl = GetDlgItem(hDlg, IDC_EDIT1);
+ PostMessage(hCtrl, EM_LIMITTEXT, (WPARAM) ITEM_DATA_SIZE, 0);
+
+ //
+ // Fill up the combo box with our tables
+ //
+ hCtrl = GetDlgItem(hDlg, IDC_COMBO1);
+ dwIndex = SendMessage(hCtrl, CB_ADDSTRING, (WPARAM) 0, (LPARAM) TEXT("Service"));
+ SendMessage(hCtrl, CB_SETITEMDATA, (WPARAM) dwIndex, (LPARAM) SERVICE_TABLE_NUM);
+
+ dwIndex = SendMessage(hCtrl, CB_ADDSTRING, (WPARAM) 0, (LPARAM) TEXT("User"));
+ SendMessage(hCtrl, CB_SETITEMDATA, (WPARAM) dwIndex, (LPARAM) USER_TABLE_NUM);
+
+ dwIndex = SendMessage(hCtrl, CB_ADDSTRING, (WPARAM) 0, (LPARAM) TEXT("SID"));
+ SendMessage(hCtrl, CB_SETITEMDATA, (WPARAM) dwIndex, (LPARAM) SID_TABLE_NUM);
+
+ dwIndex = SendMessage(hCtrl, CB_ADDSTRING, (WPARAM) 0, (LPARAM) TEXT("License"));
+ SendMessage(hCtrl, CB_SETITEMDATA, (WPARAM) dwIndex, (LPARAM) LICENSE_TABLE_NUM);
+
+ dwIndex = SendMessage(hCtrl, CB_ADDSTRING, (WPARAM) 0, (LPARAM) TEXT("Add Cache"));
+ SendMessage(hCtrl, CB_SETITEMDATA, (WPARAM) dwIndex, (LPARAM) ADD_CACHE_TABLE_NUM);
+
+ dwIndex = SendMessage(hCtrl, CB_ADDSTRING, (WPARAM) 0, (LPARAM) TEXT("Master Service"));
+ SendMessage(hCtrl, CB_SETITEMDATA, (WPARAM) dwIndex, (LPARAM) MASTER_SERVICE_TABLE_NUM);
+
+ dwIndex = SendMessage(hCtrl, CB_ADDSTRING, (WPARAM) 0, (LPARAM) TEXT("Service Family"));
+ SendMessage(hCtrl, CB_SETITEMDATA, (WPARAM) dwIndex, (LPARAM) SERVICE_FAMILY_TABLE_NUM);
+
+ dwIndex = SendMessage(hCtrl, CB_ADDSTRING, (WPARAM) 0, (LPARAM) TEXT("License Group"));
+ SendMessage(hCtrl, CB_SETITEMDATA, (WPARAM) dwIndex, (LPARAM) MAPPING_TABLE_NUM);
+
+ dwIndex = SendMessage(hCtrl, CB_ADDSTRING, (WPARAM) 0, (LPARAM) TEXT("Server"));
+ SendMessage(hCtrl, CB_SETITEMDATA, (WPARAM) dwIndex, (LPARAM) SERVER_TABLE_NUM);
+
+ dwIndex = SendMessage(hCtrl, CB_ADDSTRING, (WPARAM) 0, (LPARAM) TEXT("Secure Products"));
+ SendMessage(hCtrl, CB_SETITEMDATA, (WPARAM) dwIndex, (LPARAM) SECURE_PRODUCT_TABLE_NUM);
+
+ dwIndex = SendMessage(hCtrl, CB_ADDSTRING, (WPARAM) 0, (LPARAM) TEXT("Certificate Database"));
+ SendMessage(hCtrl, CB_SETITEMDATA, (WPARAM) dwIndex, (LPARAM) CERTIFICATE_TABLE_NUM);
+
+ SendMessage(hCtrl, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) TEXT("Add Cache"));
+ break;
+
+ case IDC_DUMPALL:
+ hCtrl = GetDlgItem(hDlg, IDC_COMBO1);
+ dwIndex = SendMessage(hCtrl, CB_GETCURSEL, 0, 0L);
+
+ if (dwIndex != CB_ERR) {
+ dwData = SendMessage(hCtrl, CB_GETITEMDATA, dwIndex, 0L);
+ LlsDbgTableDump( dwData );
+ }
+
+ break;
+
+ case IDC_DUMPITEM:
+ hCtrl = GetDlgItem(hDlg, IDC_COMBO1);
+ dwIndex = SendMessage(hCtrl, CB_GETCURSEL, 0, 0L);
+
+ if (dwIndex != CB_ERR) {
+ dwData = SendMessage(hCtrl, CB_GETITEMDATA, dwIndex, 0L);
+
+ hCtrl = GetDlgItem(hDlg, IDC_EDIT1);
+ * (WORD *)ItemData = sizeof(ItemData);
+ SendMessage(hCtrl, EM_GETLINE, 0, (LPARAM) ItemData);
+
+ LlsDbgTableInfoDump( dwData, ItemData );
+ }
+
+ break;
+
+ case IDC_TRACE:
+ DlgTrace_Do(hDlg);
+ break;
+
+ case IDC_CONFIG:
+ LlsDbgConfigDump();
+ break;
+
+ case IDC_FORCEREPLICATE:
+ LlsDbgReplicationForce();
+ break;
+
+ case IDCANCEL:
+ PostMessage(hDlg, WM_DESTROY, 0, 0);
+ LlsClose();
+ break;
+
+ }
+
+ break;
+
+ default:
+ break;
+ }
+
+
+ return (FALSE); // Didn't process the message
+
+} // DlgMain
diff --git a/private/net/svcdlls/lls/test/llsdbg/llsdbg.ico b/private/net/svcdlls/lls/test/llsdbg/llsdbg.ico
new file mode 100644
index 000000000..162d88db8
--- /dev/null
+++ b/private/net/svcdlls/lls/test/llsdbg/llsdbg.ico
Binary files differ
diff --git a/private/net/svcdlls/lls/test/llsdbg/llsdbg.rc b/private/net/svcdlls/lls/test/llsdbg/llsdbg.rc
new file mode 100644
index 000000000..e78ee293b
--- /dev/null
+++ b/private/net/svcdlls/lls/test/llsdbg/llsdbg.rc
@@ -0,0 +1,89 @@
+
+#include <windows.h>
+#include <ntverp.h>
+
+#define VER_FILETYPE VFT_APP
+#define VER_FILESUBTYPE VFT2_UNKNOWN
+#define VER_FILEDESCRIPTION_STR "Microsoft\256 LLS Debug Utility"
+
+#define VER_INTERNALNAME_STR "llsdbg.exe"
+#define VER_ORIGINALFILENAME_STR "llsdbg.exe"
+
+#include <common.ver>
+
+#include "resource.h"
+#include "..\..\inc\debug.h"
+
+LlsDbg ICON llsdbg.ico
+
+LlsDbg DIALOG DISCARDABLE 15, 40, 200, 150
+STYLE WS_MINIMIZEBOX | WS_CAPTION | WS_SYSMENU | WS_VISIBLE | WS_POPUP
+CAPTION "LLS Debug Utility"
+FONT 8, "MS Shell Dlg"
+{
+ LTEXT "Table:",IDC_TABLE, 10,10,40,8, WS_VISIBLE
+ COMBOBOX IDC_COMBO1, 50,10,85,60,CBS_DROPDOWNLIST | CBS_SORT |
+ WS_VSCROLL | WS_TABSTOP | WS_VISIBLE
+
+ LTEXT "Item:",IDC_ITEM, 10,30,40,8, WS_VISIBLE
+ EDITTEXT IDC_EDIT1, 50,30,85,12, ES_AUTOHSCROLL | WS_VISIBLE
+
+ PUSHBUTTON "Dump &All", IDC_DUMPALL, 140,10,40,14, WS_VISIBLE
+ PUSHBUTTON "Dump &Item", IDC_DUMPITEM, 140,30,40,14, WS_VISIBLE
+
+
+ PUSHBUTTON "Set &Trace", IDC_TRACE, 70,50,60,14
+ PUSHBUTTON "Dump &Config", IDC_CONFIG, 70,70,60,14
+ PUSHBUTTON "&Replicate", IDC_FORCEREPLICATE, 70,90,60,14
+ PUSHBUTTON "E&xit", IDCANCEL, 70,110,60,14
+
+
+ LTEXT "You must use a debugger to receive this output",IDC_STATIC
+ 10, 135, 170, 18
+
+}
+
+
+LlsTrace DIALOG DISCARDABLE 15, 40, 170, 140
+STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
+CAPTION "LLS Set Trace"
+FONT 8, "MS Shell Dlg"
+{
+ DEFPUSHBUTTON "&Set Trace", IDC_SET, 120,6,40,14
+ PUSHBUTTON "&Reset All", IDC_RESET, 120,26,40,14
+ PUSHBUTTON "&Cancel", IDCANCEL, 120,46,40,14
+
+ CONTROL "Warnings", IDC_WARNINGS,
+ "Button", BS_AUTOCHECKBOX | WS_TABSTOP | WS_VISIBLE,
+ 10, 10, 100, 8
+ CONTROL "Function Trace", IDC_FUNCTION,
+ "Button", BS_AUTOCHECKBOX | WS_TABSTOP | WS_VISIBLE,
+ 10, 20, 100, 8
+ CONTROL "Data Pack Functions", IDC_PACK,
+ "Button", BS_AUTOCHECKBOX | WS_TABSTOP | WS_VISIBLE,
+ 10, 30, 100, 8
+ CONTROL "License Request", IDC_REQUEST,
+ "Button", BS_AUTOCHECKBOX | WS_TABSTOP | WS_VISIBLE,
+ 10, 40, 100, 8
+ CONTROL "License Free", IDC_FREE,
+ "Button", BS_AUTOCHECKBOX | WS_TABSTOP | WS_VISIBLE,
+ 10, 50, 100, 8
+ CONTROL "Registry Functions", IDC_REGISTRY,
+ "Button", BS_AUTOCHECKBOX | WS_TABSTOP | WS_VISIBLE,
+ 10, 60, 100, 8
+ CONTROL "Replication", IDC_REPLICATION,
+ "Button", BS_AUTOCHECKBOX | WS_TABSTOP | WS_VISIBLE,
+ 10, 70, 100, 8
+ CONTROL "RPC", IDC_RPC,
+ "Button", BS_AUTOCHECKBOX | WS_TABSTOP | WS_VISIBLE,
+ 10, 80, 100, 8
+ CONTROL "Init", IDC_INIT,
+ "Button", BS_AUTOCHECKBOX | WS_TABSTOP | WS_VISIBLE,
+ 10, 90, 100, 8
+ CONTROL "Database", IDC_DATABASE,
+ "Button", BS_AUTOCHECKBOX | WS_TABSTOP | WS_VISIBLE,
+ 10, 100, 100, 8
+
+ LTEXT "You must use a debugger to receive this output",IDC_STATIC
+ 10, 115, 130, 18
+}
diff --git a/private/net/svcdlls/lls/test/llsdbg/makefile b/private/net/svcdlls/lls/test/llsdbg/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/net/svcdlls/lls/test/llsdbg/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/net/svcdlls/lls/test/llsdbg/resource.h b/private/net/svcdlls/lls/test/llsdbg/resource.h
new file mode 100644
index 000000000..a814f452c
--- /dev/null
+++ b/private/net/svcdlls/lls/test/llsdbg/resource.h
@@ -0,0 +1,55 @@
+/*+-------------------------------------------------------------------------+
+ | Copyright 1993-1994 (C) Microsoft Corporation - All rights reserved. |
+ +-------------------------------------------------------------------------+*/
+
+#ifndef _HRESOURCE_
+#define _HRESOURCE_
+
+#ifdef __cplusplus
+extern "C"{
+#endif
+
+// +------------------------------------------------------------------------+
+// | Main MoveIt Dialog
+// +------------------------------------------------------------------------+
+
+#define IDC_STATIC -1
+#define IDR_MAINFRAME 1
+#define IDD_ABOUTBOX 999
+
+#define ID_INIT 1000
+
+#define IDC_SET 1001
+#define IDC_RESET 1002
+
+#define IDC_TABCTRL 1003
+#define IDC_COMBO1 1004
+#define IDC_EDIT1 1005
+
+#define IDC_WARNINGS 1010
+#define IDC_FUNCTION 1011
+#define IDC_PACK 1012
+#define IDC_REQUEST 1013
+#define IDC_FREE 1014
+#define IDC_REGISTRY 1015
+#define IDC_REPLICATION 1016
+#define IDC_RPC 1017
+#define IDC_INIT 1018
+#define IDC_DATABASE 1019
+
+#define IDC_TABLE 1050
+#define IDC_DUMPALL 1051
+#define IDC_DUMPITEM 1052
+#define IDC_ITEM 1053
+
+#define IDC_FORCEREPLICATE 1100
+#define IDC_TRACE 1101
+#define IDC_CONFIG 1102
+
+#define ID_UPDATE 1500
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/private/net/svcdlls/lls/test/llsdbg/sources b/private/net/svcdlls/lls/test/llsdbg/sources
new file mode 100644
index 000000000..5bf898aa5
--- /dev/null
+++ b/private/net/svcdlls/lls/test/llsdbg/sources
@@ -0,0 +1,23 @@
+TARGETNAME=llsdbg
+TARGETPATH=obj
+TARGETTYPE=PROGRAM
+
+USE_CRTDLL=1
+
+INCLUDES=$(_NTROOT)\private\inc;
+C_DEFINES= -DUNICODE -D_UNICODE
+
+SOURCES= \
+ llsdbg.c \
+ llsdbg.rc
+
+UMTYPE=windows
+UMENTRY=winmain
+UMLIBS= \
+ ..\common\obj\*\llsdbg.lib \
+ ..\..\common\obj\*\llscomm.lib \
+ $(BASEDIR)\public\sdk\lib\*\rpcutil.lib \
+ $(BASEDIR)\public\sdk\lib\*\rpcrt4.lib \
+ $(BASEDIR)\public\sdk\lib\*\ntdll.lib \
+ $(BASEDIR)\public\sdk\lib\*\mpr.lib \
+ $(BASEDIR)\public\sdk\lib\*\comdlg32.lib
diff --git a/private/net/svcdlls/logonsrv/client/daytona/makefile b/private/net/svcdlls/logonsrv/client/daytona/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/net/svcdlls/logonsrv/client/daytona/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/net/svcdlls/logonsrv/client/daytona/sources b/private/net/svcdlls/logonsrv/client/daytona/sources
new file mode 100644
index 000000000..5118d0a71
--- /dev/null
+++ b/private/net/svcdlls/logonsrv/client/daytona/sources
@@ -0,0 +1,93 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+
+Author:
+
+ Cliff Van Dyke (CliffV) 27-Jun-1991
+
+
+Revision History:
+
+!ENDIF
+
+#
+# The TARGETNAME variable is defined by the developer. It is the name of
+# the target (component) that is being built by this makefile. It
+# should NOT include any path or file extension information.
+#
+
+MAJORCOMP = logonsrv
+MINORCOMP = client
+TARGETNAME= logonsrv
+
+
+NTPROFILEINPUT=YES
+
+#
+# The TARGETPATH and TARGETTYPE varialbes are defined by the developer.
+# The first specifies where the target is to be build. The second specifies
+# the type of target (either PROGRAM, DYNLINK or LIBRARY)
+#
+
+TARGETPATH=obj
+
+TARGETTYPE=LIBRARY
+
+TARGETLIBS=
+
+#
+# The INCLUDES variable specifies any include paths that are specific to
+# this source directory. Separate multiple directory paths with single
+# semicolons. Relative path specifications are okay.
+#
+
+INCLUDES=..;..\..;..\..\..\..\inc;..\..\..\..\api;$(BASEDIR)\public\sdk\inc;..\..\..\..\..\inc
+
+C_DEFINES=-DRPC_NO_WINDOWS_H
+MSC_WARNING_LEVEL=/W3 /WX
+
+#
+# The SOURCES variable is defined by the developer. It is a list of all the
+# source files for this component. Each source file should be on a separate
+# line using the line continuation character. This will minimize merge
+# conflicts if two developers adding source files to the same component.
+#
+
+!IFNDEF DISABLE_NET_UNICODE
+UNICODE=1
+NET_C_DEFINES=-DUNICODE
+!ENDIF
+
+SOURCES= \
+ ..\getdcnam.c \
+ ..\getdclst.c \
+ ..\logonapi.c \
+ ..\rpcbind.c \
+ ..\ssiapi.c \
+ ..\logon_c.c
+
+UMTYPE=windows
+
+
+#
+# Defining the NTTARGETFILES variable causes MAKEFILE.DEF to attempt to
+# include .\makefile.inc immediately after it specifies the top
+# level targets (all, clean and loc) and their dependencies. MAKEFILE.DEF
+# also expands the value of the NTTARGETFILES variable at the end of the
+# list of dependencies for the all target. Useful for specifying additional
+# targets and dependencies that don't fit the general case covered by
+# MAKEFILE.DEF
+#
diff --git a/private/net/svcdlls/logonsrv/client/dirs b/private/net/svcdlls/logonsrv/client/dirs
new file mode 100644
index 000000000..d6f158a31
--- /dev/null
+++ b/private/net/svcdlls/logonsrv/client/dirs
@@ -0,0 +1,32 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ dirs.
+
+Abstract:
+
+ This file specifies the subdirectories of the current directory that
+ contain component makefiles.
+
+
+Author:
+
+ Steve Wood (stevewo) 17-Apr-1990
+
+NOTE: Commented description of this file is in \nt\bak\bin\dirs.tpl
+
+!ENDIF
+
+#
+# The real important directory
+
+DIRS=daytona
+
+#
+# The future cool directory
+#
+
+OPTIONAL_DIRS=
diff --git a/private/net/svcdlls/logonsrv/client/getdclst.c b/private/net/svcdlls/logonsrv/client/getdclst.c
new file mode 100644
index 000000000..aa6d1be7d
--- /dev/null
+++ b/private/net/svcdlls/logonsrv/client/getdclst.c
@@ -0,0 +1,437 @@
+/*++
+
+Copyright (c) 1987-1992 Microsoft Corporation
+
+Module Name:
+
+ getdclst.c
+
+Abstract:
+
+ I_NetGetDCList API
+
+Author:
+
+ 04-Feb-1992 (CliffV)
+
+Environment:
+
+ User mode only.
+ Contains NT-specific code.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+--*/
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <rpc.h>
+#include <logon_c.h>// includes lmcons.h, lmaccess.h, netlogon.h, ssi.h, windef.h
+
+#include <debuglib.h> // IF_DEBUG()
+#include <lmapibuf.h>
+#include <lmerr.h>
+#include <lmserver.h> // SV_TYPE_* defines
+#include <netdebug.h> // NetpKdPrint
+#include <netlib.h> // NetpGetDomainName
+#include <ntlsa.h> // LsaTrust list
+#include <tstring.h> // STRLEN
+#include <stdlib.h> // wcslen
+
+
+DBGSTATIC NET_API_STATUS
+InternalNetGetDCList (
+ IN LPWSTR ServerName OPTIONAL,
+ IN LPWSTR TrustedDomainName,
+ OUT PULONG DCCount,
+ OUT PUNICODE_STRING * DCNames
+ )
+
+/*++
+
+Routine Description:
+
+ Get the names of the NT Domain Controllers in a domain. The information
+ is returned in a form suitable for storing in the LSA's
+ TRUSTED_CONTROLLERS_INFO structure.
+
+ Ideally, ServerName should be the name of a Domain Controller in the
+ specified domain. However, one should first try specifying ServerName
+ as the name of the PDC in the trusting domain. If that fails,
+ the UI can prompt for the name of a DC in the domain.
+
+
+Arguments:
+
+ ServerName - name of remote server (null for local).
+
+ TrustedDomainName - name of domain.
+
+ DCCount - Returns the number of entries in the DCNames array.
+
+ DCNames - Returns a pointer to an array of names of NT Domain Controllers
+ in the specified domain. The first entry is the name of the NT PDC.
+ The first entry will be NULL if the PDC cannot be found.
+ The buffer should be deallocated using NetApiBufferFree.
+
+Return Value:
+
+ NERR_Success - Success.
+ ERROR_INVALID_NAME Badly formed domain name
+ NERR_DCNotFound - No DC's were found in the domain
+
+--*/
+{
+ NET_API_STATUS NetStatus;
+
+ PSERVER_INFO_101 ServerInfo101 = NULL;
+ DWORD EntriesRead;
+ DWORD TotalEntries;
+
+ DWORD Size = 0;
+ BOOLEAN PdcFound = FALSE;
+ PUNICODE_STRING ReturnBuffer = NULL;
+ ULONG ReturnCount = 0;
+
+ PUNICODE_STRING CurrentBuffer;
+ ULONG CurrentIndex;
+ LPWSTR Where;
+
+ DWORD i;
+
+
+
+ //
+ // Enumerate ALL PDCs and BDCs in the domain.
+ // We'll filter out NT DC's ourselves.
+ //
+ *DCCount = 0;
+
+ NetStatus = NetServerEnum( ServerName,
+ 101,
+ (LPBYTE *) &ServerInfo101,
+ MAX_PREFERRED_LENGTH,
+ &EntriesRead,
+ &TotalEntries,
+ SV_TYPE_DOMAIN_CTRL | SV_TYPE_DOMAIN_BAKCTRL,
+ TrustedDomainName,
+ NULL ); // Resume Handle
+
+ if ( NetStatus != NERR_Success ) {
+ IF_DEBUG( LOGON ) {
+ NetpKdPrint((
+ "InternalNetGetDCList: cannot NetServerEnum '%ws': %ld 0X%lx\n",
+ ServerName, NetStatus, NetStatus));
+ }
+ goto Cleanup;
+ }
+
+ //
+ // Compute the size of the information to return.
+ //
+
+ for ( i=0; i<EntriesRead; i++ ) {
+
+ IF_DEBUG( LOGON ) {
+ NetpKdPrint((
+ "InternalNetGetDCList: '%ws': enumerated %ws\n",
+ ServerName,
+ ServerInfo101[i].sv101_name ));
+ }
+
+ //
+ // Skip non-NT entries
+ //
+
+ if ( (ServerInfo101[i].sv101_type & SV_TYPE_NT) == 0 ) {
+ IF_DEBUG( LOGON ) {
+ NetpKdPrint((
+ "InternalNetGetDCList: '%ws': %ws is not NT\n",
+ ServerName,
+ ServerInfo101[i].sv101_name ));
+ }
+ continue;
+ }
+
+ //
+ // Remember whether the PDC was found
+ //
+
+ if ( ServerInfo101[i].sv101_type & SV_TYPE_DOMAIN_CTRL ) {
+ IF_DEBUG( LOGON ) {
+ NetpKdPrint((
+ "InternalNetGetDCList: '%ws': %ws is the PDC\n",
+ ServerName,
+ ServerInfo101[i].sv101_name ));
+ }
+ PdcFound = TRUE;
+ }
+
+ //
+ // Leave room for for the UNICODE_STRING structure and the string
+ // itself (including leadind \\'s.
+ //
+
+ (*DCCount) ++;
+ Size += sizeof(UNICODE_STRING) +
+ (STRLEN(ServerInfo101[i].sv101_name) + 3) * sizeof(WCHAR);
+
+ }
+
+ //
+ // We must find at least one NT server.
+ //
+
+ if ( *DCCount == 0 ) {
+ NetStatus = NERR_DCNotFound;
+ goto Cleanup;
+ }
+
+ if ( !PdcFound ) {
+ IF_DEBUG( LOGON ) {
+ NetpKdPrint((
+ "InternalNetGetDCList: '%ws': PDC not found\n",
+ ServerName ));
+ }
+ (*DCCount) ++;
+ Size += sizeof(UNICODE_STRING);
+ }
+
+ //
+ // Allocate the return buffer.
+ //
+
+ NetStatus = NetApiBufferAllocate( Size, (LPVOID *) &ReturnBuffer );
+
+ if ( NetStatus != NERR_Success ) {
+ goto Cleanup;
+ }
+
+ Where = (LPWSTR) (ReturnBuffer + *DCCount);
+
+
+ //
+ // Fill in the return buffer.
+ //
+
+ CurrentIndex = 1; // The first (zeroeth) entry is for the PDC.
+ RtlInitUnicodeString( ReturnBuffer, NULL );
+
+ for ( i=0; i<EntriesRead; i++ ) {
+
+ //
+ // Skip non-NT entries
+ //
+
+ if ( (ServerInfo101[i].sv101_type & SV_TYPE_NT) == 0 ) {
+ continue;
+ }
+
+ //
+ // Determine which entry to fill in.
+ //
+ // If multiple PDC's were found, the first one is assumed
+ // to be the real PDC>
+ //
+
+ if ( (ServerInfo101[i].sv101_type & SV_TYPE_DOMAIN_CTRL) &&
+ ReturnBuffer->Buffer == NULL ) {
+ CurrentBuffer = ReturnBuffer;
+
+ } else {
+
+ NetpAssert( CurrentIndex < *DCCount );
+
+ CurrentBuffer = &ReturnBuffer[CurrentIndex];
+ CurrentIndex++;
+ }
+
+ //
+ // Copy the string itself to the return buffer
+ //
+ NetpAssert( ServerInfo101[i].sv101_name[0] != L'\\' );
+ *(Where) = '\\';
+ *(Where+1) = '\\';
+ NetpCopyTStrToWStr( Where+2, ServerInfo101[i].sv101_name );
+
+ //
+ // Set the UNICODE_STRING to point to it.
+ //
+
+ RtlInitUnicodeString( CurrentBuffer, Where );
+
+ Where += (wcslen(Where) + 1);
+
+ }
+
+ NetpAssert( CurrentIndex == *DCCount );
+
+ NetStatus = NERR_Success;
+
+
+ //
+ // Cleanup locally used resources
+ //
+Cleanup:
+
+ if ( ServerInfo101 != NULL ) {
+ NetApiBufferFree( ServerInfo101 );
+ }
+
+ if ( NetStatus != NERR_Success ) {
+ if ( ReturnBuffer != NULL ) {
+ NetApiBufferFree( ReturnBuffer );
+ ReturnBuffer = NULL;
+ }
+ *DCCount = 0;
+ }
+
+ //
+ // Return the information to the caller.
+ //
+
+ *DCNames = ReturnBuffer;
+
+ return NetStatus;
+
+}
+
+
+
+NET_API_STATUS NET_API_FUNCTION
+I_NetGetDCList (
+ IN LPWSTR ServerName OPTIONAL,
+ IN LPWSTR TrustedDomainName,
+ OUT PULONG DCCount,
+ OUT PUNICODE_STRING * DCNames
+ )
+
+/*++
+
+Routine Description:
+
+ Get the names of the NT Domain Controllers in a domain. The information
+ is returned in a form suitable for storing in the LSA's
+ TRUSTED_CONTROLLERS_INFO structure.
+
+ Ideally, ServerName should be the name of a Domain Controller in the
+ specified domain. However, one should first try specifying ServerName
+ as NULL in which case this API will try the the following machines:
+
+ * The local machine.
+ * The PDC of the primary domain of the local machine,
+ * The PDC of the named trusted domain,
+ * Each of the DC's in the LSA's current DC list for the named trusted
+ domain.
+
+ If this "NULL" case fails, the UI should prompt for the name of a DC
+ in the trusted domain. This handles the case where the trusted domain
+ cannot be reached via the above listed servers.
+
+Arguments:
+
+ ServerName - name of remote server (null for special case).
+
+ TrustedDomainName - name of domain.
+
+ DCCount - Returns the number of entries in the DCNames array.
+
+ DCNames - Returns a pointer to an array of names of NT Domain Controllers
+ in the specified domain. The first entry is the name of the NT PDC.
+ The first entry will be NULL if the PDC cannot be found.
+ The buffer should be deallocated using NetApiBufferFree.
+
+Return Value:
+
+ NERR_Success - Success.
+ ERROR_INVALID_NAME Badly formed domain name
+ NERR_DCNotFound - No DC's were found in the domain. Perhaps,
+ a ServerName should be specified.
+
+--*/
+{
+ NET_API_STATUS NetStatus;
+ NET_API_STATUS SavedNetStatus;
+
+ LPWSTR DCName = NULL;
+
+ //
+ // Initialization
+ //
+ *DCCount = 0;
+
+
+
+ //
+ // Try straight forward way to get the DC list.
+ //
+
+ NetStatus = InternalNetGetDCList( ServerName,
+ TrustedDomainName,
+ DCCount,
+ DCNames );
+
+ if ( NetStatus == NERR_Success || ServerName != NULL ) {
+ SavedNetStatus = NetStatus;
+ goto Cleanup;
+ }
+
+ SavedNetStatus = NetStatus;
+
+
+
+ //
+ // Simply use the PDC name as the DC list.
+ //
+ // NetServerEnum might be several minutes out of date. NetGetDCName
+ // broadcasts to find the server, so that information will be more
+ // current.
+ //
+
+ NetStatus = NetGetDCName( NULL, TrustedDomainName, (LPBYTE*)&DCName);
+
+ if ( NetStatus == NERR_Success ) {
+
+ PUNICODE_STRING ReturnBuffer = NULL;
+ DWORD Size;
+ LPWSTR Where;
+
+ Size = sizeof(UNICODE_STRING) +
+ (wcslen(DCName) + 1) * sizeof(WCHAR);
+
+ NetStatus = NetApiBufferAllocate( Size, (LPVOID *) &ReturnBuffer );
+
+ if ( NetStatus != NERR_Success ) {
+ goto Cleanup;
+ }
+
+ Where = (LPWSTR)((LPBYTE)ReturnBuffer + sizeof(UNICODE_STRING));
+
+ wcscpy( Where, DCName );
+
+ RtlInitUnicodeString( ReturnBuffer, Where );
+
+
+ *DCNames = ReturnBuffer;
+ *DCCount = 1;
+
+ SavedNetStatus = NERR_Success;
+ }
+
+
+ //
+ // Cleanup locally used resources.
+ //
+Cleanup:
+
+ if( DCName != NULL ) {
+ (VOID) NetApiBufferFree( DCName );
+ }
+
+ //
+ // Return the status code from the original request.
+ //
+ return SavedNetStatus;
+}
diff --git a/private/net/svcdlls/logonsrv/client/getdcnam.c b/private/net/svcdlls/logonsrv/client/getdcnam.c
new file mode 100644
index 000000000..19eccda51
--- /dev/null
+++ b/private/net/svcdlls/logonsrv/client/getdcnam.c
@@ -0,0 +1,849 @@
+/*++
+
+Copyright (c) 1987-1991 Microsoft Corporation
+
+Module Name:
+
+ getdcnam.c
+
+Abstract:
+
+ NetGetDCName API
+
+Author:
+
+ Ported from Lan Man 2.0
+
+Environment:
+
+ User mode only.
+ Contains NT-specific code.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+ 09-Feb-1989 (PaulC)
+ Created file, to hold NetGetDCName.
+
+ 18-Apr-1989 (Ericpe)
+ Implemented NetGetDCName.
+
+ 30-May-1989 (DannyGl)
+ Reduced DosReadMailslot timeout.
+
+ 07-Jul-1989 (NealF)
+ Use I_NetNameCanonicalize
+
+ 27-Jul-1989 (WilliamW)
+ Use WIN3 manifest for WIN3.0 compatibility
+
+ 03-Jan-1990 (WilliamW)
+ canonicalize domain and use I_NetCompareName
+
+ 08-Jun-1991 (CliffV)
+ Ported to NT
+
+ 23-Jul-1991 JohnRo
+ Implement downlevel NetGetDCName.
+
+--*/
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#ifdef _CAIRO_
+#define INC_OLE2
+#include <windows.h>
+#endif // _CAIRO_
+#include <rpc.h>
+#include <logon_c.h>// includes lmcons.h, lmaccess.h, netlogon.h, ssi.h, windef.h
+
+#include <winbase.h>
+
+#include <accessp.h>
+#include <align.h>
+#include <debuglib.h> // IF_DEBUG()
+#include <icanon.h> // NAMETYPE_* defines NetpIsRemote(), NIRFLAG_ equates.
+#include <lmapibuf.h>
+#include <lmerr.h>
+#include <lmremutl.h> // SUPPORTS_* defines
+#include <lmserver.h> // SV_TYPE_* defines
+#include <lmsvc.h> // SERVICE_* defines
+#include <lmwksta.h>
+#include <logonp.h> // NetpLogon routines
+#include <names.h> // NetpIsDomainNameValid
+#include <nlbind.h> // Netlogon RPC binding cache init routines
+#include <netdebug.h> // NetpKdPrint
+#include <netlib.h> // NetpMemoryFree
+#include <netrpc.h>
+#include <rxdomain.h> // RxNetGetDCName().
+#include <string.h>
+#include <stdlib.h>
+#ifdef _CAIRO_
+#include <lmapibuf.h>
+#define SECURITY_WIN32
+#include <security.h>
+#include <dsapi.h>
+#endif // _CAIRO_
+
+
+
+//
+// Maintain a cache of the correct answer for the Primary Domain.
+//
+DBGSTATIC CRITICAL_SECTION GlobalDCNameCritSect;
+DBGSTATIC WCHAR GlobalPrimaryDCName[UNCLEN+1];
+DBGSTATIC WCHAR GlobalPrimaryDomainName[DNLEN+1];
+
+#define LOCKDOMAINSEM() EnterCriticalSection( &GlobalDCNameCritSect )
+#define FREEDOMAINSEM() LeaveCriticalSection( &GlobalDCNameCritSect )
+
+// end global dll data
+
+
+//
+// Local definitions used throughout this file.
+//
+
+#define DOMAIN_OTHER 0
+#define DOMAIN_PRIMARY 1
+
+#ifdef _CAIRO_
+WCHAR PrimaryDomainName[DNLEN+1];
+#endif // _CAIRO_
+
+
+NET_API_STATUS
+DCNameInitialize(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ Perform per-process initialization.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ Status of the operation.
+
+--*/
+{
+#ifdef _CAIRO_
+ WCHAR CairoDomainName[MAX_PATH+1];
+ ULONG DomainNameLength = MAX_PATH;
+ HRESULT Status;
+#endif // _CAIRO_
+
+
+ //
+ // Initialize the critsect that protects the DCName cache
+ //
+
+ InitializeCriticalSection( &GlobalDCNameCritSect );
+
+ //
+ // Clear the cache.
+ //
+
+ GlobalPrimaryDCName[0] = '\0';
+ GlobalPrimaryDomainName[0] = '\0';
+
+ //
+ // Initialize the RPC binding cache.
+ //
+
+ NlBindingAttachDll();
+
+#ifdef _CAIRO_
+ //
+ // Initialize local domain name cache
+ //
+
+ Status = DSGetDomainName(CairoDomainName,&DomainNameLength);
+ if (SUCCEEDED(Status)) {
+ CairoDomainToNtDomain(CairoDomainName,PrimaryDomainName);
+ } else {
+ //
+ // BUGBUG: is this the correct thing to do?
+ //
+
+ PrimaryDomainName[0] = L'\0';
+ }
+
+#endif // _CAIRO_
+ return NERR_Success;
+}
+
+
+VOID
+DCNameClose(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ Perform per-process cleanup.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+{
+
+ //
+ // Delete the critsect that protects the DCName cache
+ //
+
+ DeleteCriticalSection( &GlobalDCNameCritSect );
+
+ //
+ // Shutdown the RPC binding cache.
+ //
+
+ NlBindingDetachDll();
+
+}
+
+
+NET_API_STATUS
+DCNameValidate(
+ IN LPWSTR ServerName,
+ IN LPWSTR DomainName,
+ OUT LPBYTE *Buffer
+ )
+/*++
+
+Routine Description:
+
+ Ensure the named server is the PDC of the named domain.
+
+Arguments:
+
+ ServerName -- Suggest PDC server name (with leading \\'s).
+
+ DomainName -- Domain that ServerName is PDC of.
+
+ Buffer - Returns a pointer to an allcated buffer containing the
+ servername of the PDC of the domain. The server name is prefixed
+ by \\. The buffer should be deallocated using NetApiBufferFree.
+
+Return Value:
+
+ NERR_Success -- Server is PDC of specified domain.
+ Other sundry status codes.
+
+--*/
+{
+ NET_API_STATUS NetStatus;
+ PWKSTA_INFO_100 WkstaInfo100 = NULL;
+ PSERVER_INFO_101 ServerInfo101 = NULL;
+
+ //
+ // Ensure the specified server if it is a PDC.
+ //
+ // The call to ServerGetInfo below could be replaced with a call
+ // to UserModalsGet at level 1, which would provide a sort of
+ // referral information in case the machine is no longer a DC.
+ // In the case that the machine is no longer the primary,
+ // at this point we could potentially make use of the information
+ // that the machine we just queried sent us about who it thinks
+ // is its primary machine. Using this referral could save us the
+ // broadcast that follows, but who knows how long the referral
+ // chain could get. This could be modified later.
+ //
+
+ NetStatus = NetServerGetInfo( ServerName, 101, (LPBYTE *)&ServerInfo101 );
+ if ( NetStatus != NERR_Success ) {
+ goto Cleanup;
+ }
+
+ if ( (ServerInfo101->sv101_type & SV_TYPE_DOMAIN_CTRL) == 0 ) {
+ NetStatus = NERR_DCNotFound;
+ goto Cleanup;
+ }
+
+ //
+ // Ensure this PDC is still controlling the domain we think it should be.
+ //
+
+ NetStatus = NetWkstaGetInfo( ServerName, 100, (LPBYTE * )&WkstaInfo100);
+ if ( NetStatus != NERR_Success ) {
+ goto Cleanup;
+ }
+
+ if ( I_NetNameCompare( NULL,
+ DomainName,
+ WkstaInfo100->wki100_langroup,
+ NAMETYPE_DOMAIN,
+ 0L) != 0 ) {
+ NetStatus = NERR_DCNotFound;
+ goto Cleanup;
+ }
+
+
+ //
+ // Allocate a buffer to return to the caller and fill it in
+ //
+
+ NetStatus = NetapipBufferAllocate(
+ (wcslen(ServerName) + 1) * sizeof(WCHAR),
+ (LPVOID *) Buffer );
+
+ if ( NetStatus != NERR_Success ) {
+ IF_DEBUG( LOGON ) {
+ NetpKdPrint(( "NetGetDCName: cannot allocate response buffer.\n"));
+ }
+ goto Cleanup;
+ }
+
+ wcscpy((LPWSTR)*Buffer, ServerName );
+
+ NetStatus = NERR_Success;
+
+Cleanup:
+ if ( ServerInfo101 != NULL ) {
+ NetApiBufferFree( ServerInfo101 );
+ }
+
+ if ( WkstaInfo100 != NULL ) {
+ NetApiBufferFree( WkstaInfo100 );
+ }
+
+ return NetStatus;
+
+}
+
+#ifdef _CAIRO_
+
+NET_API_STATUS NET_API_FUNCTION
+NetGetCairoDCName(
+ IN LPWSTR DomainName,
+ OUT LPBYTE *Buffer
+ )
+/*++
+
+Routine Description:
+
+ If the requested domain is the local domain, finds the domain controller
+
+Arguments:
+
+ DomainName - name of domain (null for primary domain)
+
+ Buffer - Returns a pointer to an allcated buffer containing the
+ servername of a DC of the domain. The server name is prefixed
+ by \\. The buffer should be deallocated using NetApiBufferFree.
+
+Returns:
+
+ NERR_Success - Found a DC successfully
+
+ NERR_DCNotFound - the domain requested was the local domain
+ but no DCs could be found
+
+ ERROR_NO_LOGON_SERVERS - the domain was not the local domain
+ and no DCs could be found
+
+--*/
+{
+ HRESULT hrRet;
+ NET_API_STATUS NetStatus;
+ PDomainKdcInfo pDomainInfo;
+ ULONG Index;
+
+ if ((DomainName == NULL) ||
+ (!_wcsicmp(DomainName,PrimaryDomainName))) {
+
+ hrRet = FindDomainController(
+ NULL,
+ 0, // unused address type
+ &pDomainInfo
+ );
+ } else {
+ return(NERR_DCNotFound);
+ }
+
+ if (FAILED(hrRet))
+ {
+ return(hrRet);
+ }
+ for (Index = 0; Index < pDomainInfo->KdcInfo[0].AddressCount ; Index++ )
+ {
+ if (pDomainInfo->KdcInfo[0].KdcAddress[Index].AddressType == ADDRESS_TYPE_NETBIOS)
+ {
+ NetStatus = NetapipBufferAllocate(
+ pDomainInfo->KdcInfo[0].KdcAddress[Index].cbAddressString
+ + 2 * sizeof(WCHAR),
+ (PVOID *) Buffer);
+ if (NetStatus != NERR_Success)
+ {
+ hrRet = ERROR_NOT_ENOUGH_MEMORY;
+ }
+ else
+ {
+ wcscpy((LPWSTR) *Buffer,L"\\\\");
+ wcscat((LPWSTR) *Buffer, (LPWSTR) pDomainInfo->KdcInfo[0].KdcAddress[Index].AddressString);
+ hrRet = NERR_Success;
+ }
+ }
+ }
+ FreeContextBuffer(pDomainInfo);
+ return(hrRet);
+
+}
+
+
+#endif // _CAIRO_
+
+NET_API_STATUS NET_API_FUNCTION
+NetGetDCName (
+ IN LPCWSTR ServerName OPTIONAL,
+ IN LPCWSTR DomainName OPTIONAL,
+ OUT LPBYTE *Buffer
+ )
+
+/*++
+
+Routine Description:
+
+ Get the name of the primary domain controller for a domain.
+
+Arguments:
+
+ ServerName - name of remote server (null for local)
+
+ DomainName - name of domain (null for primary)
+
+ Buffer - Returns a pointer to an allcated buffer containing the
+ servername of the PDC of the domain. The server name is prefixed
+ by \\. The buffer should be deallocated using NetApiBufferFree.
+
+Return Value:
+
+ NERR_Success - Success. Buffer contains PDC name prefixed by \\.
+ NERR_DCNotFound No DC found for this domain.
+ ERROR_INVALID_NAME Badly formed domain name
+
+--*/
+{
+ NET_API_STATUS NetStatus = 0;
+
+ //
+ // Points to the actual domain to query.
+ //
+
+ LPWSTR DomainToQuery;
+ DWORD WhichDomain = DOMAIN_OTHER;
+ DWORD Version;
+
+
+ LPWSTR UnicodeComputerName = NULL;
+ LPWSTR PrimaryDomainName = NULL;
+ BOOLEAN IsWorkgroupName;
+
+
+ //
+ // API SECURITY - Anyone can call anytime. No code required.
+ //
+
+ //
+ // Check if API is to be remoted, and handle downlevel case if so.
+ //
+
+ if ( (ServerName != NULL) && ( ServerName[0] != '\0') ) {
+ TCHAR UncCanonServerName[UNCLEN+1];
+ DWORD LocalOrRemote;
+
+ NetStatus = NetpIsRemote(
+ (LPWSTR) ServerName, // uncanon server name
+ & LocalOrRemote,
+ UncCanonServerName, // output: canon
+ NIRFLAG_MAPLOCAL // flags: map null to local name
+ );
+ if (NetStatus != NERR_Success) {
+ goto Cleanup;
+ }
+ if (LocalOrRemote == ISREMOTE) {
+
+
+ //
+ // Do the RPC call with an exception handler since RPC will raise an
+ // exception if anything fails. It is up to us to figure out what
+ // to do once the exception is raised.
+ //
+
+ NET_REMOTE_TRY_RPC
+
+ //
+ // Call RPC version of the API.
+ //
+ *Buffer = NULL;
+
+ NetStatus = NetrGetDCName(
+ (LPWSTR) ServerName,
+ (LPWSTR) DomainName,
+ (LPWSTR *)Buffer );
+
+ NET_REMOTE_RPC_FAILED(
+ "NetGetDCName",
+ UncCanonServerName,
+ NetStatus,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_NETLOGON )
+
+ //
+ // BUGBUG: Check if it's really a downlevel machine!
+ //
+
+ NetStatus = RxNetGetDCName(
+ UncCanonServerName,
+ (LPWSTR) DomainName,
+ (LPBYTE *) Buffer // may be allocated
+ );
+
+
+ NET_REMOTE_END
+
+ goto Cleanup;
+
+ }
+
+ //
+ // Must be explicit reference to local machine. Fall through and
+ // handle it.
+ //
+
+ }
+
+ //
+ // Validate the DomainName
+ //
+
+ if (( DomainName != NULL ) && ( DomainName[0] != '\0' )) {
+ if ( !NetpIsDomainNameValid((LPWSTR)DomainName) ) {
+ NetStatus = NERR_DCNotFound;
+ goto Cleanup;
+ }
+ }
+
+ //
+ // Determine the PrimaryDomainName
+ //
+
+ NetStatus = NetpGetDomainNameEx( &PrimaryDomainName, &IsWorkgroupName );
+
+ if ( NetStatus != NERR_Success ) {
+ IF_DEBUG( LOGON ) {
+ NetpKdPrint(( "NetGetDCName: cannot call NetpGetDomainName: %ld\n",
+ NetStatus));
+ }
+ goto Cleanup;
+ }
+
+
+ //
+ // If the given domain is NULL, the NULL string or matches
+ // the our domain, check for cache validity and make the
+ // query domain our domain.
+ //
+
+ if ( (DomainName == NULL) || (DomainName[0] == '\0') ||
+ (I_NetNameCompare( NULL,
+ (LPWSTR) DomainName,
+ PrimaryDomainName,
+ NAMETYPE_DOMAIN,
+ 0L) == 0) ) {
+
+
+ //
+ // if the current primary domain is not the same as the one we
+ // have cached, refresh the one in the cache and void the cached
+ // primary DC name.
+ //
+
+ LOCKDOMAINSEM();
+
+ if (I_NetNameCompare( NULL,
+ PrimaryDomainName,
+ GlobalPrimaryDomainName,
+ NAMETYPE_DOMAIN,
+ 0L) != 0 ) {
+
+ wcsncpy( GlobalPrimaryDomainName,
+ PrimaryDomainName,
+ DNLEN);
+ GlobalPrimaryDomainName[DNLEN] = '\0';
+ GlobalPrimaryDCName[0] = '\0';
+ }
+ FREEDOMAINSEM();
+
+ //
+ // Remember which domain to query.
+ //
+
+ DomainToQuery = PrimaryDomainName;
+ WhichDomain = DOMAIN_PRIMARY;
+
+ //
+ // This is a request on some non-NULL other domain.
+ //
+
+ } else {
+
+ DomainToQuery = (LPWSTR) DomainName;
+ }
+
+
+ //
+ // If the query domain is the primary domain AND
+ // the primary DC name is cached
+ // request the named DC to confirm that it is still the DC.
+ //
+
+ if ( WhichDomain == DOMAIN_PRIMARY ) {
+ LOCKDOMAINSEM();
+ if (GlobalPrimaryDCName[0] != '\0') {
+ WCHAR CachedPrimaryDCName[UNCLEN+1];
+ wcscpy(CachedPrimaryDCName, GlobalPrimaryDCName);
+ // Don't keep the cache locked while we validate
+ FREEDOMAINSEM();
+
+ NetStatus = DCNameValidate( CachedPrimaryDCName,
+ DomainToQuery,
+ Buffer );
+
+ if ( NetStatus == NERR_Success ) {
+ goto Cleanup;
+ }
+
+ LOCKDOMAINSEM();
+ GlobalPrimaryDCName[0] = '\0';
+ }
+ FREEDOMAINSEM();
+ }
+
+
+
+ //
+ // If we get here, it means that we need to broadcast to find the name
+ // of the DC for the given domain, if there is one. DomainToQuery
+ // points to the name of the domain to ask about. First we open a mailslot
+ // to get the response. We send the request and listen for the response.
+ //
+
+
+
+ //
+ // Pick out the computername from the Workstation information
+ //
+
+ NetStatus = NetpGetComputerName( &UnicodeComputerName );
+
+ if ( NetStatus != NERR_Success ) {
+ IF_DEBUG( LOGON ) {
+ NetpKdPrint((
+ "NetGetDCName: cannot call NetpGetComputerName: %ld\n",
+ NetStatus));
+ }
+ goto Cleanup;
+ }
+
+
+ //
+ // Broadcast to the domain to get the primary DC name.
+ //
+ // If we're querying our primary domain,
+ // we know this isn't a lanman domain. So we can optimize.
+ // If this machine is a member of a workgroup,
+ // don't optimize since there might be a LANMAN PDC in the domain.
+ //
+
+ NetStatus = NetpLogonGetDCName(
+ UnicodeComputerName,
+ DomainToQuery,
+ (WhichDomain == DOMAIN_PRIMARY && !IsWorkgroupName) ?
+ NETLOGON_PRIMARY_DOMAIN : 0,
+ (LPWSTR *)Buffer,
+ &Version );
+
+
+ if ( NetStatus == NERR_Success ) {
+ goto CacheIt;
+ }
+
+ IF_DEBUG( LOGON ) {
+ NetpKdPrint(("NetGetDCName: Error from NetpLogonGetDCName: %ld\n",
+ NetStatus));
+ }
+
+ switch (NetStatus) {
+ case ERROR_ACCESS_DENIED:
+ case ERROR_BAD_NETPATH:
+ case NERR_NetNotStarted:
+ case NERR_WkstaNotStarted:
+ case NERR_ServerNotStarted:
+ case NERR_BrowserNotStarted:
+ case NERR_ServiceNotInstalled:
+ case NERR_BadTransactConfig:
+ goto Cleanup;
+ }
+
+ //
+ // If none of the methods have succeeded,
+ // Just return DcNotFound
+ //
+
+ NetStatus = NERR_DCNotFound;
+ goto Cleanup;
+
+
+ //
+ // Cache the response.
+ //
+CacheIt:
+
+ if ( WhichDomain == DOMAIN_PRIMARY ) {
+ LOCKDOMAINSEM();
+ wcsncpy(GlobalPrimaryDCName, (LPWSTR)*Buffer, UNCLEN);
+ GlobalPrimaryDCName[UNCLEN] = '\0';
+ FREEDOMAINSEM();
+ }
+
+
+ NetStatus = NERR_Success;
+
+
+Cleanup:
+
+ //
+ // Cleanup all locally used resources
+ //
+
+ if ( PrimaryDomainName != NULL ) {
+ NetApiBufferFree( PrimaryDomainName );
+ }
+
+ if ( UnicodeComputerName != NULL ) {
+ NetApiBufferFree( UnicodeComputerName );
+ }
+
+ return NetStatus;
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetGetAnyDCName (
+ IN LPCWSTR ServerName OPTIONAL,
+ IN LPCWSTR DomainName OPTIONAL,
+ OUT LPBYTE *Buffer
+ )
+
+/*++
+
+Routine Description:
+
+ Get the name of the any domain controller for a domain that is directly trusted
+ by ServerName.
+
+
+ If ServerName is a standalone Windows NT Workstation or standalone Windows NT Server,
+ no DomainName is valid.
+
+ If ServerName is a Windows NT Workstation that is a member of a domain or a
+ Windows NT Server member server,
+ the DomainName must the the domain ServerName is a member of.
+
+ If ServerName is a Windows NT Server domain controller,
+ the DomainName must be one of the domains trusted by the
+ domain the server is a controller for.
+
+ The domain controller found is guaranteed to have been up at one point during
+ this API call.
+
+Arguments:
+
+ ServerName - name of remote server (null for local)
+
+ DomainName - name of domain (null for primary domain)
+
+ Buffer - Returns a pointer to an allcated buffer containing the
+ servername of a DC of the domain. The server name is prefixed
+ by \\. The buffer should be deallocated using NetApiBufferFree.
+
+Return Value:
+
+ ERROR_SUCCESS - Success. Buffer contains DC name prefixed by \\.
+
+ ERROR_NO_LOGON_SERVERS - No DC could be found
+
+ ERROR_NO_SUCH_DOMAIN - The specified domain is not a trusted domain.
+
+ ERROR_NO_TRUST_LSA_SECRET - The client side of the trust relationship is
+ broken.
+
+ ERROR_NO_TRUST_SAM_ACCOUNT - The server side of the trust relationship is
+ broken or the password is broken.
+
+ ERROR_DOMAIN_TRUST_INCONSISTENT - The server that responded is not a proper
+ domain controller of the specified domain.
+
+--*/
+{
+ NET_API_STATUS NetStatus;
+
+#ifdef _CAIRO_
+ //
+ // Try a Cairo domain first
+ //
+
+ NetStatus = NetGetCairoDCName( (LPWSTR) DomainName,Buffer);
+
+ if (NetStatus != NERR_DCNotFound)
+ {
+ return(NetStatus);
+ }
+#endif // _CAIRO_
+
+ //
+ // Do the RPC call with an exception handler since RPC will raise an
+ // exception if anything fails. It is up to us to figure out what
+ // to do once the exception is raised.
+ //
+
+ RpcTryExcept {
+
+ *Buffer = NULL; // Force RPC to allocate
+
+ //
+ // Call RPC version of the API.
+ //
+
+ NetStatus = NetrGetAnyDCName(
+ (LPWSTR) ServerName,
+ (LPWSTR) DomainName,
+ (LPWSTR *) Buffer );
+
+ } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
+
+ NetStatus = RpcExceptionCode();
+
+ } RpcEndExcept;
+
+ IF_DEBUG( LOGON ) {
+ NetpKdPrint(("NetGetAnyDCName rc = %lu 0x%lx\n",
+ NetStatus, NetStatus));
+ }
+
+ return NetStatus;
+}
diff --git a/private/net/svcdlls/logonsrv/client/logonapi.c b/private/net/svcdlls/logonsrv/client/logonapi.c
new file mode 100644
index 000000000..0cb5e1143
--- /dev/null
+++ b/private/net/svcdlls/logonsrv/client/logonapi.c
@@ -0,0 +1,502 @@
+/*++
+
+Copyright (c) 1991-92 Microsoft Corporation
+
+Module Name:
+
+ logonapi.c
+
+Abstract:
+
+ This module contains the Netlogon API RPC client stubs.
+
+
+Author:
+
+ Cliff Van Dyke (CliffV) 27-Jun-1991
+
+[Environment:]
+
+ User Mode - Win32
+
+Revision History:
+
+ 27-Jun-1991 CliffV
+ Created
+
+--*/
+
+//
+// INCLUDES
+//
+
+#include <nt.h>
+#include <ntrtl.h>
+
+#include <rpc.h>
+#include <logon_c.h>// includes lmcons.h, lmaccess.h, netlogon.h, ssi.h, windef.h
+
+#include <crypt.h> // Encryption routines.
+#include <debuglib.h> // IF_DEBUG()
+#include <lmerr.h> // NERR_ and ERROR_ equates.
+#include <netdebug.h> // NetpKdPrint
+
+
+NET_API_STATUS NET_API_FUNCTION
+I_NetLogonUasLogon (
+ IN LPWSTR UserName,
+ IN LPWSTR Workstation,
+ OUT PNETLOGON_VALIDATION_UAS_INFO *ValidationInformation
+)
+/*++
+
+Routine Description:
+
+ This function is called by the XACT server when processing a
+ I_NetWkstaUserLogon XACT SMB. This feature allows a UAS client to
+ logon to a SAM domain controller.
+
+Arguments:
+
+ UserName -- Account name of the user logging on.
+
+ Workstation -- The workstation from which the user is logging on.
+
+ ValidationInformation -- Returns the requested validation
+ information.
+
+
+Return Value:
+
+ NERR_SUCCESS if there was no error. Otherwise, the error code is
+ returned.
+
+
+--*/
+{
+ NET_API_STATUS NetStatus;
+ LPWSTR ServerName = NULL; // Not supported remotely
+
+ //
+ // Do the RPC call with an exception handler since RPC will raise an
+ // exception if anything fails. It is up to us to figure out what
+ // to do once the exception is raised.
+ //
+
+ RpcTryExcept {
+
+ *ValidationInformation = NULL; // Force RPC to allocate
+ //
+ // Call RPC version of the API.
+ //
+
+ NetStatus = NetrLogonUasLogon(
+ (LPWSTR) ServerName,
+ UserName,
+ Workstation,
+ ValidationInformation );
+
+ } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
+
+ NetStatus = RpcExceptionCode();
+
+ } RpcEndExcept;
+
+ IF_DEBUG( LOGON ) {
+ NetpKdPrint(("NetrLogonUasLogon rc = %lu 0x%lx\n",
+ NetStatus, NetStatus));
+ }
+
+ return NetStatus;
+}
+
+
+NET_API_STATUS
+I_NetLogonUasLogoff (
+ IN LPWSTR UserName,
+ IN LPWSTR Workstation,
+ OUT PNETLOGON_LOGOFF_UAS_INFO LogoffInformation
+)
+/*++
+
+Routine Description:
+
+ This function is called by the XACT server when processing a
+ I_NetWkstaUserLogoff XACT SMB. This feature allows a UAS client to
+ logoff from a SAM domain controller. The request is authenticated,
+ the entry is removed for this user from the logon session table
+ maintained by the Netlogon service for NetLogonEnum, and logoff
+ information is returned to the caller.
+
+ The server portion of I_NetLogonUasLogoff (in the Netlogon service)
+ compares the user name and workstation name specified in the
+ LogonInformation with the user name and workstation name from the
+ impersonation token. If they don't match, I_NetLogonUasLogoff fails
+ indicating the access is denied.
+
+ Group SECURITY_LOCAL is refused access to this function. Membership
+ in SECURITY_LOCAL implies that this call was made locally and not
+ through the XACT server.
+
+ The Netlogon service cannot be sure that this function was called by
+ the XACT server. Therefore, the Netlogon service will not simply
+ delete the entry from the logon session table. Rather, the logon
+ session table entry will be marked invisible outside of the Netlogon
+ service (i.e., it will not be returned by NetLogonEnum) until a valid
+ LOGON_WKSTINFO_RESPONSE is received for the entry. The Netlogon
+ service will immediately interrogate the client (as described above
+ for LOGON_WKSTINFO_RESPONSE) and temporarily increase the
+ interrogation frequency to at least once a minute. The logon session
+ table entry will reappear as soon as a function of interrogation if
+ this isn't a true logoff request.
+
+Arguments:
+
+ UserName -- Account name of the user logging off.
+
+ Workstation -- The workstation from which the user is logging
+ off.
+
+ LogoffInformation -- Returns the requested logoff information.
+
+Return Value:
+
+ The Net status code.
+
+--*/
+{
+ NET_API_STATUS NetStatus;
+ LPWSTR ServerName = NULL; // Not supported remotely
+
+ //
+ // Do the RPC call with an exception handler since RPC will raise an
+ // exception if anything fails. It is up to us to figure out what
+ // to do once the exception is raised.
+ //
+
+ RpcTryExcept {
+
+ //
+ // Call RPC version of the API.
+ //
+
+ NetStatus = NetrLogonUasLogoff(
+ (LPWSTR) ServerName,
+ UserName,
+ Workstation,
+ LogoffInformation );
+
+ } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
+
+ NetStatus = RpcExceptionCode();
+
+ } RpcEndExcept;
+
+ IF_DEBUG( LOGON ) {
+ NetpKdPrint(("NetrLogonUasLogoff rc = %lu 0x%lx\n",
+ NetStatus, NetStatus));
+ }
+
+ return NetStatus;
+}
+
+
+NTSTATUS
+I_NetLogonSamLogon (
+ IN LPWSTR LogonServer OPTIONAL,
+ IN LPWSTR ComputerName OPTIONAL,
+ IN PNETLOGON_AUTHENTICATOR Authenticator OPTIONAL,
+ OUT PNETLOGON_AUTHENTICATOR ReturnAuthenticator OPTIONAL,
+ IN NETLOGON_LOGON_INFO_CLASS LogonLevel,
+ IN LPBYTE LogonInformation,
+ IN NETLOGON_VALIDATION_INFO_CLASS ValidationLevel,
+ OUT LPBYTE * ValidationInformation,
+ OUT PBOOLEAN Authoritative
+ )
+
+/*++
+
+Routine Description:
+
+ This function is called by an NT client to process an interactive or
+ network logon. This function passes a domain name, user name and
+ credentials to the Netlogon service and returns information needed to
+ build a token. It is called in three instances:
+
+ * It is called by the LSA's MSV1_0 authentication package for any
+ NT system that has LanMan installed. The MSV1_0 authentication
+ package calls SAM directly if LanMan is not installed. In this
+ case, this function is a local function and requires the caller
+ to have SE_TCB privilege. The local Netlogon service will
+ either handle this request directly (validating the request with
+ the local SAM database) or will forward this request to the
+ appropriate domain controller as documented in sections 2.4 and
+ 2.5.
+
+ * It is called by a Netlogon service on a workstation to a DC in
+ the Primary Domain of the workstation as documented in section
+ 2.4. In this case, this function uses a secure channel set up
+ between the two Netlogon services.
+
+ * It is called by a Netlogon service on a DC to a DC in a trusted
+ domain as documented in section 2.5. In this case, this
+ function uses a secure channel set up between the two Netlogon
+ services.
+
+ The Netlogon service validates the specified credentials. If they
+ are valid, adds an entry for this LogonId, UserName, and Workstation
+ into the logon session table. The entry is added to the logon
+ session table only in the domain defining the specified user's
+ account.
+
+ This service is also used to process a re-logon request.
+
+
+Arguments:
+
+ LogonServer -- Supplies the name of the logon server to process
+ this logon request. This field should be null to indicate
+ this is a call from the MSV1_0 authentication package to the
+ local Netlogon service.
+
+ ComputerName -- Name of the machine making the call. This field
+ should be null to indicate this is a call from the MSV1_0
+ authentication package to the local Netlogon service.
+
+ Authenticator -- supplied by the client. This field should be
+ null to indicate this is a call from the MSV1_0
+ authentication package to the local Netlogon service.
+
+ ReturnAuthenticator -- Receives an authenticator returned by the
+ server. This field should be null to indicate this is a call
+ from the MSV1_0 authentication package to the local Netlogon
+ service.
+
+ LogonLevel -- Specifies the level of information given in
+ LogonInformation.
+
+ LogonInformation -- Specifies the description for the user
+ logging on.
+
+ ValidationLevel -- Specifies the level of information returned in
+ ValidationInformation. Must be NetlogonValidationSamInformation.
+
+ ValidationInformation -- Returns the requested validation
+ information.
+
+ Authoritative -- Returns whether the status returned is an
+ authoritative status which should be returned to the original
+ caller. If not, this logon request may be tried again on another
+ domain controller. This parameter is returned regardless of the
+ status code.
+
+Return Value:
+
+ STATUS_SUCCESS: if there was no error.
+
+ STATUS_NO_LOGON_SERVERS -- Either Pass-thru authentication or
+ Trusted Domain Authentication could not contact the requested
+ Domain Controller.
+
+ STATUS_INVALID_INFO_CLASS -- Either LogonLevel or ValidationLevel is
+ invalid.
+
+ STATUS_INVALID_PARAMETER -- Another Parameter is invalid.
+
+ STATUS_ACCESS_DENIED -- The caller does not have access to call this
+ API.
+
+ STATUS_NO_SUCH_USER -- Indicates that the user specified in
+ LogonInformation does not exist. This status should not be returned
+ to the originally caller. It should be mapped to STATUS_LOGON_FAILURE.
+
+ STATUS_WRONG_PASSWORD -- Indicates that the password information in
+ LogonInformation was incorrect. This status should not be returned
+ to the originally caller. It should be mapped to STATUS_LOGON_FAILURE.
+
+ STATUS_INVALID_LOGON_HOURES -- The user is not authorized to logon
+ at this time.
+
+ STATUS_INVALID_WORKSTATION -- The user is not authorized to logon
+ from the specified workstation.
+
+ STATUS_PASSWORD_EXPIRED -- The password for the user has expired.
+
+ STATUS_ACCOUNT_DISABLED -- The user's account has been disabled.
+
+ .
+ .
+ .
+
+
+
+--*/
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ NETLOGON_LEVEL RpcLogonInformation;
+ NETLOGON_VALIDATION RpcValidationInformation;
+
+ //
+ // Do the RPC call with an exception handler since RPC will raise an
+ // exception if anything fails. It is up to us to figure out what
+ // to do once the exception is raised.
+ //
+
+ RpcTryExcept {
+
+ //
+ // Call RPC version of the API.
+ //
+
+ RpcLogonInformation.LogonInteractive =
+ (PNETLOGON_INTERACTIVE_INFO) LogonInformation;
+
+ RpcValidationInformation.ValidationSam = NULL;
+
+ Status = NetrLogonSamLogon(
+ LogonServer,
+ ComputerName,
+ Authenticator,
+ ReturnAuthenticator,
+ LogonLevel,
+ &RpcLogonInformation,
+ ValidationLevel,
+ &RpcValidationInformation,
+ Authoritative );
+
+ *ValidationInformation = (LPBYTE)
+ RpcValidationInformation.ValidationSam;
+
+ } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
+
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+
+ } RpcEndExcept;
+
+ IF_DEBUG( LOGON ) {
+ NetpKdPrint(("I_NetLogonSamLogon rc = %lu 0x%lx\n", Status, Status));
+ }
+
+ return Status;
+}
+
+
+
+
+NTSTATUS NET_API_FUNCTION
+I_NetLogonSamLogoff (
+ IN LPWSTR LogonServer OPTIONAL,
+ IN LPWSTR ComputerName OPTIONAL,
+ IN PNETLOGON_AUTHENTICATOR Authenticator OPTIONAL,
+ OUT PNETLOGON_AUTHENTICATOR ReturnAuthenticator OPTIONAL,
+ IN NETLOGON_LOGON_INFO_CLASS LogonLevel,
+ IN LPBYTE LogonInformation
+)
+/*++
+
+Routine Description:
+
+ This function is called by an NT client to process an interactive
+ logoff. It is not called for the network logoff case since the
+ Netlogon service does not maintain any context for network logons.
+
+ This function does the following. It authenticates the request. It
+ updates the logon statistics in the SAM database on whichever machine
+ or domain defines this user account. It updates the logon session
+ table in the primary domain of the machine making the request. And
+ it returns logoff information to the caller.
+
+ This function is called in same scenarios that I_NetLogonSamLogon is
+ called:
+
+ * It is called by the LSA's MSV1_0 authentication package to
+ support LsaApLogonTerminated. In this case, this function is a
+ local function and requires the caller to have SE_TCB privilege.
+ The local Netlogon service will either handle this request
+ directly (if LogonDomainName indicates this request was
+ validated locally) or will forward this request to the
+ appropriate domain controller as documented in sections 2.4 and
+ 2.5.
+
+ * It is called by a Netlogon service on a workstation to a DC in
+ the Primary Domain of the workstation as documented in section
+ 2.4. In this case, this function uses a secure channel set up
+ between the two Netlogon services.
+
+ * It is called by a Netlogon service on a DC to a DC in a trusted
+ domain as documented in section 2.5. In this case, this
+ function uses a secure channel set up between the two Netlogon
+ services.
+
+ When this function is a remote function, it is sent to the DC over a
+ NULL session.
+
+Arguments:
+
+ LogonServer -- Supplies the name of the logon server which logged
+ this user on. This field should be null to indicate this is
+ a call from the MSV1_0 authentication package to the local
+ Netlogon service.
+
+ ComputerName -- Name of the machine making the call. This field
+ should be null to indicate this is a call from the MSV1_0
+ authentication package to the local Netlogon service.
+
+ Authenticator -- supplied by the client. This field should be
+ null to indicate this is a call from the MSV1_0
+ authentication package to the local Netlogon service.
+
+ ReturnAuthenticator -- Receives an authenticator returned by the
+ server. This field should be null to indicate this is a call
+ from the MSV1_0 authentication package to the local Netlogon
+ service.
+
+ LogonLevel -- Specifies the level of information given in
+ LogonInformation.
+
+ LogonInformation -- Specifies the logon domain name, logon Id,
+ user name and workstation name of the user logging off.
+
+Return Value:
+
+--*/
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ NETLOGON_LEVEL RpcLogonInformation;
+
+ //
+ // Do the RPC call with an exception handler since RPC will raise an
+ // exception if anything fails. It is up to us to figure out what
+ // to do once the exception is raised.
+ //
+
+ RpcTryExcept {
+
+ //
+ // Call RPC version of the API.
+ //
+
+ RpcLogonInformation.LogonInteractive =
+ (PNETLOGON_INTERACTIVE_INFO) LogonInformation;
+
+ Status = NetrLogonSamLogoff(
+ LogonServer,
+ ComputerName,
+ Authenticator,
+ ReturnAuthenticator,
+ LogonLevel,
+ &RpcLogonInformation );
+
+ } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
+
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+
+ } RpcEndExcept;
+
+ IF_DEBUG( LOGON ) {
+ NetpKdPrint(("I_NetLogonSamLogoff rc = %lu 0x%lx\n", Status, Status));
+ }
+ return Status;
+}
diff --git a/private/net/svcdlls/logonsrv/client/rpcbind.c b/private/net/svcdlls/logonsrv/client/rpcbind.c
new file mode 100644
index 000000000..723f40852
--- /dev/null
+++ b/private/net/svcdlls/logonsrv/client/rpcbind.c
@@ -0,0 +1,625 @@
+/*++
+
+Copyright (c) 1990-1992 Microsoft Corporation
+
+Module Name:
+
+ rpcbind.c
+
+Abstract:
+
+ Contains the RPC bind and un-bind routines for the Timesource
+ Service.
+
+Author:
+
+ Rajen Shah (rajens) 02-Apr-1991
+
+Environment:
+
+ User Mode -Win32
+
+Revision History:
+
+ 02-Apr-1991 RajenS
+ created
+ 22-May-1992 JohnRo
+ RAID 9829: winsvc.h and related file cleanup.
+
+--*/
+
+//
+// INCLUDES
+//
+#define NOSERVICE // Avoid <winsvc.h> vs. <lmsvc.h> conflicts.
+#include <nt.h>
+#include <stdlib.h>
+#include <string.h>
+#include <rpc.h>
+#include <logon_c.h> // includes lmcons.h, lmaccess.h, netlogon.h, ssi.h, windef.h
+#include <lmerr.h> // NERR_ and ERROR_ equates.
+#include <lmsvc.h>
+#include <ntrpcp.h>
+#include <tstring.h> // IS_PATH_SEPARATOR ...
+#include <nlbind.h> // Prototypes for these routines
+#include <icanon.h> // NAMETYPE_*
+
+//
+// DataTypes
+//
+
+typedef struct _CACHE_ENTRY {
+ LIST_ENTRY Next;
+ OEM_STRING OemServerNameString;
+ RPC_BINDING_HANDLE RpcBindingHandle;
+ ULONG ReferenceCount;
+} CACHE_ENTRY, *PCACHE_ENTRY;
+
+//
+// STATIC GLOBALS
+//
+
+//
+// Maintain a cache of RPC binding handles.
+//
+
+CRITICAL_SECTION NlGlobalBindingCacheCritSect;
+LIST_ENTRY NlGlobalBindingCache;
+
+
+
+VOID
+NlBindingAttachDll (
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ Initialize the RPC binding handle cache on process attach.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ //
+ // Initialize the Global Cache Critical Section
+ //
+
+ InitializeCriticalSection( &NlGlobalBindingCacheCritSect );
+ InitializeListHead( &NlGlobalBindingCache );
+
+}
+
+
+VOID
+NlBindingDetachDll (
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ Cleanup the RPC binding handle cache on process detach.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+{
+
+ //
+ // The binding cache must be empty,
+ // Netlogon cleans up after itself,
+ // no one else uses the cache.
+ //
+
+ ASSERT( IsListEmpty( &NlGlobalBindingCache ) );
+ DeleteCriticalSection( &NlGlobalBindingCacheCritSect );
+}
+
+
+PCACHE_ENTRY
+NlBindingFindCacheEntry (
+ IN LPWSTR UncServerName
+ )
+
+/*++
+
+Routine Description:
+
+ Find the specfied cache entry.
+
+ Entered with the NlGlobalBindingCacheCritSect locked.
+
+Arguments:
+
+ UncServerName - Name of the server to lookup
+
+Return Value:
+
+ NULL - Cache entry not found.
+
+--*/
+{
+ NTSTATUS Status;
+ PLIST_ENTRY ListEntry;
+ PCACHE_ENTRY CacheEntry;
+
+ UNICODE_STRING UnicodeServerNameString;
+ OEM_STRING OemServerNameString;
+ CHAR OemServerName[CNLEN+1];
+
+ // ?? Optimize by converting names to uppercase oem outside of loop
+
+ //
+ // Ensure the passed in parameter is really a UNC name
+ //
+
+ if ( UncServerName == NULL ||
+ !IS_PATH_SEPARATOR( UncServerName[0] ) ||
+ !IS_PATH_SEPARATOR( UncServerName[1] ) ) {
+ return NULL;
+ }
+
+ //
+ // Convert the server name to OEM once for faster comparison
+ //
+
+ RtlInitUnicodeString( &UnicodeServerNameString, UncServerName+2 );
+ OemServerNameString.Buffer = OemServerName;
+ OemServerNameString.MaximumLength = sizeof(OemServerName);
+ OemServerNameString.Length = 0;
+
+ Status = RtlUpcaseUnicodeStringToOemString(
+ &OemServerNameString,
+ &UnicodeServerNameString,
+ FALSE );
+
+ if ( !NT_SUCCESS(Status) ) {
+ return NULL;
+ }
+
+ //
+ // Loop through the cache finding the entry.
+ //
+
+ for ( ListEntry = NlGlobalBindingCache.Flink;
+ ListEntry != &NlGlobalBindingCache;
+ ListEntry = ListEntry->Flink ) {
+
+ CacheEntry = CONTAINING_RECORD( ListEntry, CACHE_ENTRY, Next );
+
+ if ( RtlEqualString( &OemServerNameString,
+ &CacheEntry->OemServerNameString,
+ FALSE ) ) {
+ return CacheEntry;
+ }
+
+ }
+
+ return NULL;
+}
+
+
+NTSTATUS
+NlBindingAddServerToCache (
+ IN LPWSTR UncServerName
+ )
+
+/*++
+
+Routine Description:
+
+ Bind to the specified server and add it to the binding cache.
+
+Arguments:
+
+ UncServerName - UNC Name of the server to bind to.
+
+Return Value:
+
+ Status of the operation
+
+--*/
+{
+ NTSTATUS Status;
+ RPC_STATUS RpcStatus;
+ PCACHE_ENTRY CacheEntry;
+
+ ASSERT ( UncServerName != NULL &&
+ IS_PATH_SEPARATOR( UncServerName[0] ) &&
+ IS_PATH_SEPARATOR( UncServerName[1] ) );
+
+ //
+ // If there already is an entry in the cache,
+ // just increment the reference count.
+ //
+
+ EnterCriticalSection( &NlGlobalBindingCacheCritSect );
+
+ CacheEntry = NlBindingFindCacheEntry( UncServerName );
+
+ if ( CacheEntry != NULL ) {
+
+ CacheEntry->ReferenceCount++;
+ Status = STATUS_SUCCESS;
+
+ //
+ // Otherwise, allocate an entry and bind to the named server.
+ //
+
+ } else {
+
+ UNICODE_STRING UnicodeServerNameString;
+ OEM_STRING OemServerNameString;
+ CHAR OemServerName[CNLEN+1];
+
+ //
+ // Convert the server name to OEM for faster comparison
+ //
+
+ RtlInitUnicodeString( &UnicodeServerNameString, UncServerName+2 );
+ OemServerNameString.Buffer = OemServerName;
+ OemServerNameString.MaximumLength = sizeof(OemServerName);
+ OemServerNameString.Length = 0;
+
+ Status = RtlUpcaseUnicodeStringToOemString(
+ &OemServerNameString,
+ &UnicodeServerNameString,
+ FALSE );
+
+ if ( NT_SUCCESS(Status) ) {
+
+ //
+ // Allocate the cache entry
+ //
+
+ CacheEntry = LocalAlloc( 0,
+ sizeof(CACHE_ENTRY) +
+ OemServerNameString.Length );
+
+ if ( CacheEntry == NULL ) {
+
+ Status = STATUS_NO_MEMORY;
+
+ } else {
+
+
+ //
+ // Initialize the cache entry.
+ //
+ // The caller has a 'reference' to the entry.
+ //
+
+ CacheEntry->OemServerNameString.Buffer = (LPSTR)(CacheEntry+1);
+ CacheEntry->OemServerNameString.Length =
+ CacheEntry->OemServerNameString.MaximumLength =
+ OemServerNameString.Length;
+ RtlCopyMemory( CacheEntry->OemServerNameString.Buffer,
+ OemServerNameString.Buffer,
+ CacheEntry->OemServerNameString.Length );
+
+ CacheEntry->ReferenceCount = 1;
+
+ //
+ // Bind to the server
+ // (Don't hold the crit sect for this potentially very long time.)
+ //
+
+ LeaveCriticalSection( &NlGlobalBindingCacheCritSect );
+ RpcStatus = RpcpBindRpc (
+ UncServerName,
+ SERVICE_NETLOGON,
+ L"Security=Impersonation Dynamic False",
+ &CacheEntry->RpcBindingHandle );
+ EnterCriticalSection( &NlGlobalBindingCacheCritSect );
+
+ if ( RpcStatus == 0 ) {
+
+ //
+ // Link the cache entry into the list
+ //
+ // If this were a general purpose routine, I'd have to check
+ // if someone inserted this cache entry already while we had
+ // the crit sect unlocked. However, the only caller is the
+ // netlogon service that has exclusive access to this client.
+ //
+
+ InsertHeadList( &NlGlobalBindingCache, &CacheEntry->Next );
+ Status = STATUS_SUCCESS;
+
+ } else {
+
+ Status = I_RpcMapWin32Status( RpcStatus );
+
+ (VOID) LocalFree( CacheEntry );
+ }
+
+ }
+ }
+
+ }
+
+ //
+ // Return to the caller.
+ //
+
+ LeaveCriticalSection( &NlGlobalBindingCacheCritSect );
+
+ return Status;
+}
+
+
+NTSTATUS
+NlBindingDecrementAndUnlock (
+ IN PCACHE_ENTRY CacheEntry
+ )
+
+/*++
+
+Routine Description:
+
+ Decrement the reference count and unlock the NlGlobalBindingCacheCritSect.
+
+ If the reference count reaches 0, unbind the interface, unlink the cache
+ entry and delete it.
+
+ Entered with the NlGlobalBindingCacheCritSect locked.
+
+Arguments:
+
+ UncServerName - UNC Name of the server to bind to.
+
+Return Value:
+
+ Status of the operation
+
+--*/
+{
+ NTSTATUS Status;
+ RPC_STATUS RpcStatus;
+
+ //
+ // Decrement the reference count
+ //
+ // If it didn't reach zero, just unlock the crit sect and return.
+ //
+
+ if ( (--CacheEntry->ReferenceCount) != 0 ) {
+
+ LeaveCriticalSection( &NlGlobalBindingCacheCritSect );
+ return STATUS_SUCCESS;
+
+ }
+
+ //
+ // Remove the entry from the list and unlock the crit sect.
+ //
+ // Once the entry is removed from the list, we can safely unlock the
+ // crit sect. Then we can unbind (a potentially lengthy operation) with
+ // the crit sect unlocked.
+ //
+
+ RemoveEntryList( &CacheEntry->Next );
+ LeaveCriticalSection( &NlGlobalBindingCacheCritSect );
+
+ //
+ // Unbind and delete the cache entry.
+ //
+
+ RpcStatus = RpcpUnbindRpc( CacheEntry->RpcBindingHandle );
+
+ if ( RpcStatus != 0 ) {
+ Status = I_RpcMapWin32Status( RpcStatus );
+ } else {
+ Status = STATUS_SUCCESS;
+ }
+
+ (VOID) LocalFree( CacheEntry );
+
+ return Status;
+
+}
+
+
+NTSTATUS
+NlBindingRemoveServerFromCache (
+ IN LPWSTR UncServerName
+ )
+
+/*++
+
+Routine Description:
+
+ Unbind to the specified server and remove it from the binding cache.
+
+Arguments:
+
+ UncServerName - UNC Name of the server to unbind from.
+
+Return Value:
+
+ Status of the operation
+
+--*/
+{
+ NTSTATUS Status;
+ PCACHE_ENTRY CacheEntry;
+
+ ASSERT ( UncServerName != NULL &&
+ IS_PATH_SEPARATOR( UncServerName[0] ) &&
+ IS_PATH_SEPARATOR( UncServerName[1] ) );
+
+ //
+ // If there is no cache entry,
+ // silently ignore the situation.
+ //
+
+ EnterCriticalSection( &NlGlobalBindingCacheCritSect );
+
+ CacheEntry = NlBindingFindCacheEntry( UncServerName );
+
+ if ( CacheEntry == NULL ) {
+
+ ASSERT( FALSE );
+ LeaveCriticalSection( &NlGlobalBindingCacheCritSect );
+ return STATUS_SUCCESS;
+ }
+
+ //
+ // Decrement the reference count and unlock the crit sect.
+ //
+
+ Status = NlBindingDecrementAndUnlock( CacheEntry );
+
+ return Status;
+}
+
+
+
+handle_t
+LOGONSRV_HANDLE_bind (
+ LOGONSRV_HANDLE UncServerName)
+
+/*++
+
+Routine Description:
+ This routine calls a common bind routine that is shared by all services.
+
+Arguments:
+
+ UncServerName - A pointer to a string containing the name of the server
+ to bind with.
+
+Return Value:
+
+ The binding handle is returned to the stub routine. If the
+ binding is unsuccessful, a NULL will be returned.
+
+--*/
+{
+ handle_t RpcBindingHandle;
+ RPC_STATUS RpcStatus;
+ PCACHE_ENTRY CacheEntry;
+
+
+ //
+ // If there is a cache entry,
+ // increment the reference count and use the cached handle
+ //
+
+ EnterCriticalSection( &NlGlobalBindingCacheCritSect );
+
+ CacheEntry = NlBindingFindCacheEntry( UncServerName );
+
+ if ( CacheEntry != NULL ) {
+
+ CacheEntry->ReferenceCount ++;
+ RpcBindingHandle = CacheEntry->RpcBindingHandle;
+ LeaveCriticalSection( &NlGlobalBindingCacheCritSect );
+
+ return RpcBindingHandle;
+ }
+
+ LeaveCriticalSection( &NlGlobalBindingCacheCritSect );
+
+ //
+ // If there is no cache entry,
+ // simply create a new binding.
+ //
+
+ RpcStatus = RpcpBindRpc (
+ UncServerName,
+ SERVICE_NETLOGON,
+ L"Security=Impersonation Dynamic False",
+ &RpcBindingHandle );
+
+ if ( RpcStatus != 0 ) {
+ RpcBindingHandle = NULL;
+ }
+
+ return RpcBindingHandle;
+
+}
+
+
+
+void
+LOGONSRV_HANDLE_unbind (
+ LOGONSRV_HANDLE UncServerName,
+ handle_t RpcBindingHandle)
+
+/*++
+
+Routine Description:
+
+ This routine calls a common unbind routine that is shared by
+ all services.
+
+
+Arguments:
+
+ UncServerName - This is the name of the server from which to unbind.
+
+ RpcBindingHandle - This is the binding handle that is to be closed.
+
+Return Value:
+
+ none.
+
+--*/
+{
+ RPC_STATUS RpcStatus;
+ PLIST_ENTRY ListEntry;
+ PCACHE_ENTRY CacheEntry;
+
+ //
+ // Loop through the cache finding the entry.
+ //
+
+ EnterCriticalSection( &NlGlobalBindingCacheCritSect );
+ for ( ListEntry = NlGlobalBindingCache.Flink;
+ ListEntry != &NlGlobalBindingCache;
+ ListEntry = ListEntry->Flink ) {
+
+ CacheEntry = CONTAINING_RECORD( ListEntry, CACHE_ENTRY, Next );
+
+ //
+ // If the cache entry was found,
+ // decrement the reference count and unlock the crit sect.
+ //
+
+ if ( RpcBindingHandle == CacheEntry->RpcBindingHandle ) {
+ (VOID) NlBindingDecrementAndUnlock( CacheEntry );
+ return;
+ }
+
+ }
+ LeaveCriticalSection( &NlGlobalBindingCacheCritSect );
+
+
+ //
+ // Just Unbind the handle
+ //
+
+ RpcStatus = RpcpUnbindRpc( RpcBindingHandle );
+ return;
+
+ UNREFERENCED_PARAMETER(UncServerName);
+
+}
+
diff --git a/private/net/svcdlls/logonsrv/client/ssiapi.c b/private/net/svcdlls/logonsrv/client/ssiapi.c
new file mode 100644
index 000000000..e20a5eddd
--- /dev/null
+++ b/private/net/svcdlls/logonsrv/client/ssiapi.c
@@ -0,0 +1,1446 @@
+/*++
+
+Copyright (c) 1987-1992 Microsoft Corporation
+
+Module Name:
+
+ ssiapi.c
+
+Abstract:
+
+ Authentication and replication API routines (client side).
+
+Author:
+
+ Cliff Van Dyke (cliffv) 30-Jul-1991
+
+Environment:
+
+ User mode only.
+ Contains NT-specific code.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+--*/
+
+#include <nt.h> // LARGE_INTEGER definition
+#include <ntrtl.h> // LARGE_INTEGER definition
+#include <nturtl.h> // LARGE_INTEGER definition
+
+#include <rpc.h> // Needed by logon.h
+#include <logon_c.h> // includes lmcons.h, lmaccess.h, netlogon.h, ssi.h, windef.h
+
+#include <debuglib.h> // IF_DEBUG()
+#include <lmerr.h> // NERR_* defines
+#include <netdebug.h> // NetpKdPrint
+#include "..\server\ssiapi.h"
+
+
+
+NTSTATUS
+I_NetServerReqChallenge(
+ IN LPWSTR PrimaryName OPTIONAL,
+ IN LPWSTR ComputerName,
+ IN PNETLOGON_CREDENTIAL ClientChallenge,
+ OUT PNETLOGON_CREDENTIAL ServerChallenge
+ )
+/*++
+
+Routine Description:
+
+ This is the client side of I_NetServerReqChallenge.
+
+ I_NetLogonRequestChallenge is the first of two functions used by a client
+ to process an authentication with a domain controller (DC). (See
+ I_NetServerAuthenticate below.) It is called for
+ a BDC (or member server) authenticating with a PDC for replication
+ purposes.
+
+ This function passes a challenge to the PDC and the PDC passes a challenge
+ back to the caller.
+
+Arguments:
+
+ PrimaryName -- Supplies the name of the PrimaryDomainController we wish to
+ authenticate with.
+
+ ComputerName -- Name of the BDC or member server making the call.
+
+ ClientChallenge -- 64 bit challenge supplied by the BDC or member server.
+
+ ServerChallenge -- Receives 64 bit challenge from the PDC.
+
+Return Value:
+
+ The status of the operation.
+
+--*/
+
+{
+ NTSTATUS Status = 0;
+
+ //
+ // Do the RPC call with an exception handler since RPC will raise an
+ // exception if anything fails. It is up to us to figure out what
+ // to do once the exception is raised.
+ //
+
+ RpcTryExcept {
+
+ //
+ // Call RPC version of the API.
+ //
+
+ Status = NetrServerReqChallenge(
+ PrimaryName,
+ ComputerName,
+ ClientChallenge,
+ ServerChallenge );
+
+ } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
+
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+
+ } RpcEndExcept;
+
+ IF_DEBUG( LOGON ) {
+ NetpKdPrint(("I_NetServerReqChallenge rc = %lu 0x%lx\n",
+ Status, Status));
+ }
+
+ return Status;
+}
+
+
+NTSTATUS
+I_NetServerAuthenticate(
+ IN LPWSTR PrimaryName OPTIONAL,
+ IN LPWSTR AccountName,
+ IN NETLOGON_SECURE_CHANNEL_TYPE AccountType,
+ IN LPWSTR ComputerName,
+ IN PNETLOGON_CREDENTIAL ClientCredential,
+ OUT PNETLOGON_CREDENTIAL ServerCredential
+ )
+/*++
+
+Routine Description:
+
+ This is the client side of I_NetServerAuthenticate
+
+ I_NetServerAuthenticate is the second of two functions used by a client
+ Netlogon service to authenticate with another Netlogon service.
+ (See I_NetServerReqChallenge above.) Both a SAM or UAS server authenticates
+ using this function.
+
+ This function passes a credential to the DC and the DC passes a credential
+ back to the caller.
+
+
+Arguments:
+
+ PrimaryName -- Supplies the name of the DC we wish to authenticate with.
+
+ AccountName -- Name of the Account to authenticate with.
+
+ SecureChannelType -- The type of the account being accessed. This field
+ must be set to UasServerSecureChannel to indicate a call from
+ downlevel (LanMan 2.x and below) BDC or member server.
+
+ ComputerName -- Name of the BDC or member server making the call.
+
+ ClientCredential -- 64 bit credential supplied by the BDC or member server.
+
+ ServerCredential -- Receives 64 bit credential from the PDC.
+
+Return Value:
+
+ The status of the operation.
+
+--*/
+{
+ NTSTATUS Status = 0;
+
+ //
+ // Do the RPC call with an exception handler since RPC will raise an
+ // exception if anything fails. It is up to us to figure out what
+ // to do once the exception is raised.
+ //
+
+ RpcTryExcept {
+
+ //
+ // Call RPC version of the API.
+ //
+
+ Status = NetrServerAuthenticate(
+ PrimaryName,
+ AccountName,
+ AccountType,
+ ComputerName,
+ ClientCredential,
+ ServerCredential );
+
+ } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
+
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+
+ } RpcEndExcept;
+
+
+ IF_DEBUG( LOGON ) {
+ NetpKdPrint(("I_NetServerAuthenticate rc = %lu 0x%lx\n",
+ Status, Status));
+ }
+
+ return Status;
+}
+
+
+NTSTATUS
+I_NetServerAuthenticate2(
+ IN LPWSTR PrimaryName OPTIONAL,
+ IN LPWSTR AccountName,
+ IN NETLOGON_SECURE_CHANNEL_TYPE AccountType,
+ IN LPWSTR ComputerName,
+ IN PNETLOGON_CREDENTIAL ClientCredential,
+ OUT PNETLOGON_CREDENTIAL ServerCredential,
+ IN OUT PULONG NegotiatedFlags
+ )
+/*++
+
+Routine Description:
+
+ This is the client side of I_NetServerAuthenticate
+
+ I_NetServerAuthenticate is the second of two functions used by a client
+ Netlogon service to authenticate with another Netlogon service.
+ (See I_NetServerReqChallenge above.) Both a SAM or UAS server authenticates
+ using this function.
+
+ This function passes a credential to the DC and the DC passes a credential
+ back to the caller.
+
+
+Arguments:
+
+ PrimaryName -- Supplies the name of the DC we wish to authenticate with.
+
+ AccountName -- Name of the Account to authenticate with.
+
+ SecureChannelType -- The type of the account being accessed. This field
+ must be set to UasServerSecureChannel to indicate a call from
+ downlevel (LanMan 2.x and below) BDC or member server.
+
+ ComputerName -- Name of the BDC or member server making the call.
+
+ ClientCredential -- 64 bit credential supplied by the BDC or member server.
+
+ ServerCredential -- Receives 64 bit credential from the PDC.
+
+ NegotiatedFlags -- Specifies flags indicating what features the BDC supports.
+ Returns a subset of those flags indicating what features the PDC supports.
+ The PDC/BDC should ignore any bits that it doesn't understand.
+
+Return Value:
+
+ The status of the operation.
+
+--*/
+{
+ NTSTATUS Status = 0;
+
+ //
+ // Do the RPC call with an exception handler since RPC will raise an
+ // exception if anything fails. It is up to us to figure out what
+ // to do once the exception is raised.
+ //
+
+ RpcTryExcept {
+
+ //
+ // Call RPC version of the API.
+ //
+
+ Status = NetrServerAuthenticate2(
+ PrimaryName,
+ AccountName,
+ AccountType,
+ ComputerName,
+ ClientCredential,
+ ServerCredential,
+ NegotiatedFlags );
+
+ } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
+
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+
+ } RpcEndExcept;
+
+
+ IF_DEBUG( LOGON ) {
+ NetpKdPrint(("I_NetServerAuthenticate2 rc = %lu 0x%lx\n",
+ Status, Status));
+ }
+
+ return Status;
+}
+
+
+NTSTATUS
+I_NetServerPasswordSet(
+ IN LPWSTR PrimaryName OPTIONAL,
+ IN LPWSTR AccountName,
+ IN NETLOGON_SECURE_CHANNEL_TYPE AccountType,
+ IN LPWSTR ComputerName,
+ IN PNETLOGON_AUTHENTICATOR Authenticator,
+ OUT PNETLOGON_AUTHENTICATOR ReturnAuthenticator,
+ IN PENCRYPTED_LM_OWF_PASSWORD UasNewPassword
+ )
+/*++
+
+Routine Description:
+
+ This function is used to change the password for the account being
+ used to maintain a secure channel. This function can only be called
+ by a server which has previously authenticated with a DC by calling
+ I_NetServerAuthenticate.
+
+ The call is made differently depending on the account type:
+
+ * A domain account password is changed from the PDC in the
+ trusting domain. The I_NetServerPasswordSet call is made to any
+ DC in the trusted domain.
+
+ * A server account password is changed from the specific server.
+ The I_NetServerPasswordSet call is made to the PDC in the domain
+ the server belongs to.
+
+ * A workstation account password is changed from the specific
+ workstation. The I_NetServerPasswordSet call is made to a DC in
+ the domain the server belongs to.
+
+ For domain accounts and workstation accounts, the server being called
+ may be a BDC in the specific domain. In that case, the BDC will
+ validate the request and pass it on to the PDC of the domain using
+ the server account secure channel. If the PDC of the domain is
+ currently not available, the BDC will return STATUS_NO_LOGON_SERVERS. Since
+ the UasNewPassword is passed encrypted by the session key, such a BDC
+ will decrypt the UasNewPassword using the original session key and
+ will re-encrypt it with the session key for its session to its PDC
+ before passing the request on.
+
+ This function uses RPC to contact the DC named by PrimaryName.
+
+Arguments:
+
+ PrimaryName -- Name of the PDC to change the servers password
+ with. NULL indicates this call is a local call being made on
+ behalf of a UAS server by the XACT server.
+
+ AccountName -- Name of the account to change the password for.
+
+ AccountType -- The type of account being accessed. This field must
+ be set to UasServerAccount to indicate a call from a downlevel
+
+ ComputerName -- Name of the BDC or member making the call.
+
+ Authenticator -- supplied by the server.
+
+ ReturnAuthenticator -- Receives an authenticator returned by the PDC.
+
+ UasNewPassword -- The new password for the server. This
+ Password is generated by automatic means using
+ random number genertaor seeded with the current Time
+ It is assumed that the machine generated password
+ was used as key to encrypt STD text and "sesskey"
+ obtained via Challenge/Authenticate sequence was
+ used to further encrypt it before passing to this api.
+ i.e. UasNewPassword = E2(E1(STD_TXT, PW), SK)
+
+Return Value:
+
+ NT status code.
+
+--*/
+{
+ NTSTATUS Status = 0;
+
+ //
+ // Do the RPC call with an exception handler since RPC will raise an
+ // exception if anything fails. It is up to us to figure out what
+ // to do once the exception is raised.
+ //
+
+ RpcTryExcept {
+
+ //
+ // Call RPC version of the API.
+ //
+
+ Status = NetrServerPasswordSet(
+ PrimaryName,
+ AccountName,
+ AccountType,
+ ComputerName,
+ Authenticator,
+ ReturnAuthenticator,
+ UasNewPassword );
+
+ } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
+
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+
+ } RpcEndExcept;
+
+ IF_DEBUG( LOGON ) {
+ NetpKdPrint(("I_NetServerPasswordSet rc = %lu 0x%lx\n",
+ Status, Status));
+ }
+
+ return Status;
+}
+
+
+
+NTSTATUS
+I_NetDatabaseDeltas (
+ IN LPWSTR PrimaryName,
+ IN LPWSTR ComputerName,
+ IN PNETLOGON_AUTHENTICATOR Authenticator,
+ OUT PNETLOGON_AUTHENTICATOR ReturnAuthenticator,
+ IN DWORD DatabaseID,
+ IN OUT PNLPR_MODIFIED_COUNT DomainModifiedCount,
+ OUT PNETLOGON_DELTA_ENUM_ARRAY *DeltaArray,
+ IN DWORD PreferredMaximumLength
+ )
+/*++
+
+Routine Description:
+
+ This function is used by a SAM BDC or SAM member server to request
+ SAM-style account delta information from a SAM PDC. This function
+ can only be called by a server which has previously authenticated
+ with the PDC by calling I_NetServerAuthenticate. This function uses
+ RPC to contact the Netlogon service on the PDC.
+
+ This function returns a list of deltas. A delta describes an
+ individual domain, user or group and all of the field values for that
+ object. The PDC maintains a list of deltas not including all of the
+ field values for that object. Rather, the PDC retrieves the field
+ values from SAM and returns those values from this call. The PDC
+ optimizes the data returned on this call by only returning the field
+ values for a particular object once on a single invocation of this
+ function. This optimizes the typical case where multiple deltas
+ exist for a single object (e.g., an application modified many fields
+ of the same user during a short period of time using different calls
+ to the SAM service).
+
+Arguments:
+
+ PrimaryName -- Name of the PDC to retrieve the deltas from.
+
+ ComputerName -- Name of the BDC or member server making the call.
+
+ Authenticator -- supplied by the server.
+
+ ReturnAuthenticator -- Receives an authenticator returned by the PDC.
+
+ DomainModifiedCount -- Specifies the DomainModifiedCount of the
+ last delta retrieved by the server. Returns the
+ DomainModifiedCount of the last delta returned from the PDC
+ on this call.
+
+ DeltaArray -- Receives a pointer to a buffer where the information
+ is placed. The information returned is an array of
+ NETLOGON_DELTA_ENUM structures.
+
+ PreferredMaximumLength - Preferred maximum length of returned
+ data (in 8-bit bytes). This is not a hard upper limit, but
+ serves as a guide to the server. Due to data conversion
+ between systems with different natural data sizes, the actual
+ amount of data returned may be greater than this value.
+
+Return Value:
+
+ STATUS_SUCCESS -- The function completed successfully.
+
+ STATUS_SYNCHRONIZATION_REQUIRED -- The replicant is totally out of sync and
+ should call I_NetDatabaseSync to do a full synchronization with
+ the PDC.
+
+ STATUS_MORE_ENTRIES -- The replicant should call again to get more
+ data.
+
+ STATUS_ACCESS_DENIED -- The replicant should re-authenticate with
+ the PDC.
+
+
+--*/
+{
+ NTSTATUS Status = 0;
+
+ //
+ // Do the RPC call with an exception handler since RPC will raise an
+ // exception if anything fails. It is up to us to figure out what
+ // to do once the exception is raised.
+ //
+
+ RpcTryExcept {
+
+ //
+ // Call RPC version of the API.
+ //
+ *DeltaArray = NULL; // Force RPC to allocate
+
+ Status = NetrDatabaseDeltas(
+ PrimaryName,
+ ComputerName,
+ Authenticator,
+ ReturnAuthenticator,
+ DatabaseID,
+ DomainModifiedCount,
+ DeltaArray,
+ PreferredMaximumLength );
+
+ } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
+
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+
+ } RpcEndExcept;
+
+ NetpKdPrint(("I_NetDatabaseDeltas rc = %lu 0x%lx\n", Status, Status));
+
+ return Status;
+}
+
+
+NTSTATUS
+I_NetDatabaseSync (
+ IN LPWSTR PrimaryName,
+ IN LPWSTR ComputerName,
+ IN PNETLOGON_AUTHENTICATOR Authenticator,
+ OUT PNETLOGON_AUTHENTICATOR ReturnAuthenticator,
+ IN DWORD DatabaseID,
+ IN OUT PULONG SamSyncContext,
+ OUT PNETLOGON_DELTA_ENUM_ARRAY *DeltaArray,
+ IN DWORD PreferredMaximumLength
+ )
+/*++
+
+Routine Description:
+
+ This function is used by a SAM BDC or SAM member server to request
+ the entire SAM database from a SAM PDC in SAM-style format. This
+ function can only be called by a server which has previously
+ authenticated with the PDC by calling I_NetServerAuthenticate. This
+ function uses RPC to contact the Netlogon service on the PDC.
+
+ This function uses the find-first find-next model to return portions
+ of the SAM database at a time. The SAM database is returned as a
+ list of deltas like those returned from I_NetDatabaseDeltas. The
+ following deltas are returned for each domain:
+
+ * One AddOrChangeDomain delta, followed by
+
+ * One AddOrChangeGroup delta for each group, followed by,
+
+ * One AddOrChangeUser delta for each user, followed by
+
+ * One ChangeGroupMembership delta for each group
+
+
+Arguments:
+
+ PrimaryName -- Name of the PDC to retrieve the deltas from.
+
+ ComputerName -- Name of the BDC or member server making the call.
+
+ Authenticator -- supplied by the server.
+
+ ReturnAuthenticator -- Receives an authenticator returned by the PDC.
+
+ SamSyncContext -- Specifies context needed to continue the
+ operation. The caller should treat this as an opaque
+ value. The value should be zero before the first call.
+
+ DeltaArray -- Receives a pointer to a buffer where the information
+ is placed. The information returned is an array of
+ NETLOGON_DELTA_ENUM structures.
+
+ PreferredMaximumLength - Preferred maximum length of returned
+ data (in 8-bit bytes). This is not a hard upper limit, but
+ serves as a guide to the server. Due to data conversion
+ between systems with different natural data sizes, the actual
+ amount of data returned may be greater than this value.
+
+Return Value:
+
+ STATUS_SUCCESS -- The function completed successfully.
+
+ STATUS_SYNCHRONIZATION_REQUIRED -- The replicant is totally out of sync and
+ should call I_NetDatabaseSync to do a full synchronization with
+ the PDC.
+
+ STATUS_MORE_ENTRIES -- The replicant should call again to get more
+ data.
+
+ STATUS_ACCESS_DENIED -- The replicant should re-authenticate with
+ the PDC.
+
+
+--*/
+{
+ NTSTATUS Status = 0;
+
+ //
+ // Do the RPC call with an exception handler since RPC will raise an
+ // exception if anything fails. It is up to us to figure out what
+ // to do once the exception is raised.
+ //
+
+ RpcTryExcept {
+
+ //
+ // Call RPC version of the API.
+ //
+ *DeltaArray = NULL; // Force RPC to allocate
+
+ Status = NetrDatabaseSync(
+ PrimaryName,
+ ComputerName,
+ Authenticator,
+ ReturnAuthenticator,
+ DatabaseID,
+ SamSyncContext,
+ DeltaArray,
+ PreferredMaximumLength );
+
+ } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
+
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+
+ } RpcEndExcept;
+
+
+ IF_DEBUG( LOGON ) {
+ NetpKdPrint(("I_NetDatabaseSync rc = %lu 0x%lx\n", Status, Status));
+ }
+
+ return Status;
+}
+
+
+NTSTATUS
+I_NetDatabaseSync2 (
+ IN LPWSTR PrimaryName,
+ IN LPWSTR ComputerName,
+ IN PNETLOGON_AUTHENTICATOR Authenticator,
+ OUT PNETLOGON_AUTHENTICATOR ReturnAuthenticator,
+ IN DWORD DatabaseID,
+ IN SYNC_STATE RestartState,
+ IN OUT PULONG SamSyncContext,
+ OUT PNETLOGON_DELTA_ENUM_ARRAY *DeltaArray,
+ IN DWORD PreferredMaximumLength
+ )
+/*++
+
+Routine Description:
+
+ This function is used by a SAM BDC or SAM member server to request
+ the entire SAM database from a SAM PDC in SAM-style format. This
+ function can only be called by a server which has previously
+ authenticated with the PDC by calling I_NetServerAuthenticate. This
+ function uses RPC to contact the Netlogon service on the PDC.
+
+ This function uses the find-first find-next model to return portions
+ of the SAM database at a time. The SAM database is returned as a
+ list of deltas like those returned from I_NetDatabaseDeltas. The
+ following deltas are returned for each domain:
+
+ * One AddOrChangeDomain delta, followed by
+
+ * One AddOrChangeGroup delta for each group, followed by,
+
+ * One AddOrChangeUser delta for each user, followed by
+
+ * One ChangeGroupMembership delta for each group
+
+
+Arguments:
+
+ PrimaryName -- Name of the PDC to retrieve the deltas from.
+
+ ComputerName -- Name of the BDC or member server making the call.
+
+ Authenticator -- supplied by the server.
+
+ ReturnAuthenticator -- Receives an authenticator returned by the PDC.
+
+ RestartState -- Specifies whether this is a restart of the full sync and how
+ to interpret SyncContext. This value should be NormalState unless this
+ is the restart of a full sync.
+
+ However, if the caller is continuing a full sync after a reboot,
+ the following values are used:
+
+ GroupState - SyncContext is the global group rid to continue with.
+ UserState - SyncContext is the user rid to continue with
+ GroupMemberState - SyncContext is the global group rid to continue with
+ AliasState - SyncContext should be zero to restart at first alias
+ AliasMemberState - SyncContext should be zero to restart at first alias
+
+ One cannot continue the LSA database in this way.
+
+ SamSyncContext -- Specifies context needed to continue the
+ operation. The caller should treat this as an opaque
+ value. The value should be zero before the first call.
+
+ DeltaArray -- Receives a pointer to a buffer where the information
+ is placed. The information returned is an array of
+ NETLOGON_DELTA_ENUM structures.
+
+ PreferredMaximumLength - Preferred maximum length of returned
+ data (in 8-bit bytes). This is not a hard upper limit, but
+ serves as a guide to the server. Due to data conversion
+ between systems with different natural data sizes, the actual
+ amount of data returned may be greater than this value.
+
+Return Value:
+
+ STATUS_SUCCESS -- The function completed successfully.
+
+ STATUS_SYNCHRONIZATION_REQUIRED -- The replicant is totally out of sync and
+ should call I_NetDatabaseSync to do a full synchronization with
+ the PDC.
+
+ STATUS_MORE_ENTRIES -- The replicant should call again to get more
+ data.
+
+ STATUS_ACCESS_DENIED -- The replicant should re-authenticate with
+ the PDC.
+
+
+--*/
+{
+ NTSTATUS Status = 0;
+
+ //
+ // Do the RPC call with an exception handler since RPC will raise an
+ // exception if anything fails. It is up to us to figure out what
+ // to do once the exception is raised.
+ //
+
+ RpcTryExcept {
+
+ //
+ // Call RPC version of the API.
+ //
+ *DeltaArray = NULL; // Force RPC to allocate
+
+ Status = NetrDatabaseSync2(
+ PrimaryName,
+ ComputerName,
+ Authenticator,
+ ReturnAuthenticator,
+ DatabaseID,
+ RestartState,
+ SamSyncContext,
+ DeltaArray,
+ PreferredMaximumLength );
+
+ } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
+
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+
+ } RpcEndExcept;
+
+
+ IF_DEBUG( LOGON ) {
+ NetpKdPrint(("I_NetDatabaseSync rc = %lu 0x%lx\n", Status, Status));
+ }
+
+ return Status;
+}
+
+
+
+NET_API_STATUS NET_API_FUNCTION
+I_NetAccountDeltas (
+ IN LPWSTR PrimaryName,
+ IN LPWSTR ComputerName,
+ IN PNETLOGON_AUTHENTICATOR Authenticator,
+ OUT PNETLOGON_AUTHENTICATOR ReturnAuthenticator,
+ IN PUAS_INFO_0 RecordId,
+ IN DWORD Count,
+ IN DWORD Level,
+ OUT LPBYTE Buffer,
+ IN DWORD BufferSize,
+ OUT PULONG CountReturned,
+ OUT PULONG TotalEntries,
+ OUT PUAS_INFO_0 NextRecordId
+ )
+/*++
+
+Routine Description:
+
+ This function is used by a UAS BDC or UAS member server to request
+ UAS-style account change information. This function can only be
+ called by a server which has previously authenticated with the PDC by
+ calling I_NetServerAuthenticate.
+
+ This function is only called by the XACT server upon receipt of a
+ I_NetAccountDeltas XACT SMB from a UAS BDC or a UAS member server.
+ As such, many of the parameters are opaque since the XACT server
+ doesn't need to interpret any of that data. This function uses RPC
+ to contact the Netlogon service.
+
+ The LanMan 3.0 SSI Functional Specification describes the operation
+ of this function.
+
+Arguments:
+
+ PrimaryName -- Must be NULL to indicate this call is a local call
+ being made on behalf of a UAS server by the XACT server.
+
+ ComputerName -- Name of the BDC or member making the call.
+
+ Authenticator -- supplied by the server.
+
+ ReturnAuthenticator -- Receives an authenticator returned by the PDC.
+
+ RecordId -- Supplies an opaque buffer indicating the last record
+ received from a previous call to this function.
+
+ Count -- Supplies the number of Delta records requested.
+
+ Level -- Reserved. Must be zero.
+
+ Buffer -- Returns opaque data representing the information to be
+ returned.
+
+ BufferSize -- Size of buffer in bytes.
+
+ CountReturned -- Returns the number of records returned in buffer.
+
+ TotalEntries -- Returns the total number of records available.
+
+ NextRecordId -- Returns an opaque buffer identifying the last
+ record received by this function.
+
+
+Return Value:
+
+ Status code
+
+--*/
+{
+ NET_API_STATUS NetStatus;
+
+ //
+ // Do the RPC call with an exception handler since RPC will raise an
+ // exception if anything fails. It is up to us to figure out what
+ // to do once the exception is raised.
+ //
+
+ RpcTryExcept {
+
+ //
+ // Call RPC version of the API.
+ //
+
+ NetStatus = NetrAccountDeltas (
+ PrimaryName,
+ ComputerName,
+ Authenticator,
+ ReturnAuthenticator,
+ RecordId,
+ Count,
+ Level,
+ Buffer,
+ BufferSize,
+ CountReturned,
+ TotalEntries,
+ NextRecordId );
+
+ } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
+
+ NetStatus = RpcExceptionCode();
+
+ } RpcEndExcept;
+
+ IF_DEBUG( LOGON ) {
+ NetpKdPrint(("I_NetAccountDeltas rc = %lu 0x%lx\n",
+ NetStatus, NetStatus));
+ }
+
+ return NetStatus;
+}
+
+
+
+NET_API_STATUS NET_API_FUNCTION
+I_NetAccountSync (
+ IN LPWSTR PrimaryName,
+ IN LPWSTR ComputerName,
+ IN PNETLOGON_AUTHENTICATOR Authenticator,
+ OUT PNETLOGON_AUTHENTICATOR ReturnAuthenticator,
+ IN DWORD Reference,
+ IN DWORD Level,
+ OUT LPBYTE Buffer,
+ IN DWORD BufferSize,
+ OUT PULONG CountReturned,
+ OUT PULONG TotalEntries,
+ OUT PULONG NextReference,
+ OUT PUAS_INFO_0 LastRecordId
+ )
+/*++
+
+Routine Description:
+
+ This function is used by a UAS BDC or UAS member server to request
+ the entire user accounts database. This function can only be called
+ by a server which has previously authenticated with the PDC by
+ calling I_NetServerAuthenticate.
+
+ This function is only called by the XACT server upon receipt of a
+ I_NetAccountSync XACT SMB from a UAS BDC or a UAS member server. As
+ such, many of the parameters are opaque since the XACT server doesn't
+ need to interpret any of that data. This function uses RPC to
+ contact the Netlogon service.
+
+ The LanMan 3.0 SSI Functional Specification describes the operation
+ of this function.
+
+ "Reference" and "NextReference" are treated as below.
+
+ 1. "Reference" should hold either 0 or value of "NextReference"
+ from previous call to this API.
+ 2. Send the modals and ALL group records in the first call. The API
+ expects the bufffer to be large enough to hold this info (worst
+ case size would be
+ MAXGROUP * (sizeof(struct group_info_1) + MAXCOMMENTSZ)
+ + sizeof(struct user_modals_info_0)
+ which, for now, will be 256 * (26 + 49) + 16 = 19216 bytes
+
+Arguments:
+
+ PrimaryName -- Must be NULL to indicate this call is a local call
+ being made on behalf of a UAS server by the XACT server.
+
+ ComputerName -- Name of the BDC or member making the call.
+
+ Authenticator -- supplied by the server.
+
+ ReturnAuthenticator -- Receives an authenticator returned by the PDC.
+
+ Reference -- Supplies find-first find-next handle returned by the
+ previous call to this function or 0 if it is the first call.
+
+ Level -- Reserved. Must be zero.
+
+ Buffer -- Returns opaque data representing the information to be
+ returned.
+
+ BufferLen -- Length of buffer in bytes.
+
+ CountReturned -- Returns the number of records returned in buffer.
+
+ TotalEntries -- Returns the total number of records available.
+
+ NextReference -- Returns a find-first find-next handle to be
+ provided on the next call.
+
+ LastRecordId -- Returns an opaque buffer identifying the last
+ record received by this function.
+
+
+Return Value:
+
+ Status code.
+
+--*/
+
+{
+ NET_API_STATUS NetStatus;
+
+ //
+ // Do the RPC call with an exception handler since RPC will raise an
+ // exception if anything fails. It is up to us to figure out what
+ // to do once the exception is raised.
+ //
+
+ RpcTryExcept {
+
+ //
+ // Call RPC version of the API.
+ //
+
+ NetStatus = NetrAccountSync (
+ PrimaryName,
+ ComputerName,
+ Authenticator,
+ ReturnAuthenticator,
+ Reference,
+ Level,
+ Buffer,
+ BufferSize,
+ CountReturned,
+ TotalEntries,
+ NextReference,
+ LastRecordId );
+
+
+ } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
+
+ NetStatus = RpcExceptionCode();
+
+ } RpcEndExcept;
+
+ IF_DEBUG( LOGON ) {
+ NetpKdPrint(("I_NetAccountSync rc = %lu 0x%lx\n",
+ NetStatus, NetStatus));
+ }
+
+ return NetStatus;
+}
+
+
+
+
+NET_API_STATUS NET_API_FUNCTION
+I_NetLogonControl(
+ IN LPCWSTR ServerName OPTIONAL,
+ IN DWORD FunctionCode,
+ IN DWORD QueryLevel,
+ OUT LPBYTE *QueryInformation
+ )
+
+/*++
+
+Routine Description:
+
+ This function controls various aspects of the Netlogon service. It
+ can be used to request that a BDC ensure that its copy of the SAM
+ database is brought up to date. It can, also, be used to determine
+ if a BDC currently has a secure channel open to the PDC.
+
+ Only an Admin, Account Operator or Server Operator may call this
+ function.
+
+Arguments:
+
+ ServerName - The name of the remote server.
+
+ FunctionCode - Defines the operation to be performed. The valid
+ values are:
+
+ FunctionCode Values
+
+ NETLOGON_CONTROL_QUERY - No operation. Merely returns the
+ information requested.
+
+ NETLOGON_CONTROL_REPLICATE: Forces the SAM database on a BDC
+ to be brought in sync with the copy on the PDC. This
+ operation does NOT imply a full synchronize. The
+ Netlogon service will merely replicate any outstanding
+ differences if possible.
+
+ NETLOGON_CONTROL_SYNCHRONIZE: Forces a BDC to get a
+ completely new copy of the SAM database from the PDC.
+ This operation will perform a full synchronize.
+
+ NETLOGON_CONTROL_PDC_REPLICATE: Forces a PDC to ask each BDC
+ to replicate now.
+
+ QueryLevel - Indicates what information should be returned from
+ the Netlogon Service. Must be 1.
+
+ QueryInformation - Returns a pointer to a buffer which contains the
+ requested information. The buffer must be freed using
+ NetApiBufferFree.
+
+
+Return Value:
+
+ NERR_Success: the operation was successful
+
+ ERROR_NOT_SUPPORTED: Function code is not valid on the specified
+ server. (e.g. NETLOGON_CONTROL_REPLICATE was passed to a PDC).
+
+--*/
+{
+ NET_API_STATUS NetStatus;
+ NETLOGON_CONTROL_QUERY_INFORMATION RpcQueryInformation;
+
+ //
+ // Do the RPC call with an exception handler since RPC will raise an
+ // exception if anything fails. It is up to us to figure out what
+ // to do once the exception is raised.
+ //
+
+ RpcTryExcept {
+
+ //
+ // Call RPC version of the API.
+ //
+
+ RpcQueryInformation.NetlogonInfo1 = NULL; // Force RPC to allocate
+
+ NetStatus = NetrLogonControl (
+ (LPWSTR) ServerName OPTIONAL,
+ FunctionCode,
+ QueryLevel,
+ &RpcQueryInformation );
+
+ *QueryInformation = (LPBYTE) RpcQueryInformation.NetlogonInfo1;
+
+
+ } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
+
+ NetStatus = RpcExceptionCode();
+
+ } RpcEndExcept;
+
+ IF_DEBUG( LOGON ) {
+ NetpKdPrint(("I_NetLogonControl rc = %lu 0x%lx\n",
+ NetStatus, NetStatus));
+ }
+
+ return NetStatus;
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+I_NetLogonControl2(
+ IN LPCWSTR ServerName OPTIONAL,
+ IN DWORD FunctionCode,
+ IN DWORD QueryLevel,
+ IN LPBYTE InputData,
+ OUT LPBYTE *QueryInformation
+ )
+
+/*++
+
+Routine Description:
+
+ This is similar to the I_NetLogonControl function but it accepts
+ more generic input data according to the function code specified.
+
+ This function controls various aspects of the Netlogon service. It
+ can be used to request that a BDC ensure that its copy of the SAM
+ database is brought up to date. It can, also, be used to determine
+ if a BDC currently has a secure channel open to the PDC.
+
+ Only an Admin, Account Operator or Server Operator may call this
+ function.
+
+Arguments:
+
+ ServerName - The name of the remote server.
+
+ FunctionCode - Defines the operation to be performed. The valid
+ values are:
+
+ FunctionCode Values
+
+ NETLOGON_CONTROL_QUERY - No operation. Merely returns the
+ information requested.
+
+ NETLOGON_CONTROL_REPLICATE: Forces the SAM database on a BDC
+ to be brought in sync with the copy on the PDC. This
+ operation does NOT imply a full synchronize. The
+ Netlogon service will merely replicate any outstanding
+ differences if possible.
+
+ NETLOGON_CONTROL_SYNCHRONIZE: Forces a BDC to get a
+ completely new copy of the SAM database from the PDC.
+ This operation will perform a full synchronize.
+
+ NETLOGON_CONTROL_PDC_REPLICATE: Forces a PDC to ask each BDC
+ to replicate now.
+
+ NETLOGON_CONTROL_REDISCOVER: Forces a DC to rediscover the
+ specified trusted domain DC.
+
+ NETLOGON_CONTROL_TC_QUERY: Query the status of the specified
+ trusted domain secure channel.
+
+ QueryLevel - Indicates what information should be returned from
+ the Netlogon Service. Must be 1.
+
+ InputData - According to the function code specified this parameter
+ will carry input data. NETLOGON_CONTROL_REDISCOVER and
+ NETLOGON_CONTROL_TC_QUERY function code specify the trusted
+ domain name (LPWSTR type) here.
+
+ QueryInformation - Returns a pointer to a buffer which contains the
+ requested information. The buffer must be freed using
+ NetApiBufferFree.
+
+
+Return Value:
+
+ NERR_Success: the operation was successful
+
+ ERROR_NOT_SUPPORTED: Function code is not valid on the specified
+ server. (e.g. NETLOGON_CONTROL_REPLICATE was passed to a PDC).
+
+--*/
+{
+ NET_API_STATUS NetStatus;
+ NETLOGON_CONTROL_QUERY_INFORMATION RpcQueryInformation;
+
+ //
+ // Do the RPC call with an exception handler since RPC will raise an
+ // exception if anything fails. It is up to us to figure out what
+ // to do once the exception is raised.
+ //
+
+ RpcTryExcept {
+
+ //
+ // Call RPC version of the API.
+ //
+ // Use new Control2Ex if either QueryLevel or FunctionCode is new
+ //
+
+ RpcQueryInformation.NetlogonInfo1 = NULL; // Force RPC to allocate
+
+ switch ( FunctionCode ) {
+ case NETLOGON_CONTROL_QUERY:
+ case NETLOGON_CONTROL_REPLICATE:
+ case NETLOGON_CONTROL_SYNCHRONIZE:
+ case NETLOGON_CONTROL_PDC_REPLICATE:
+ case NETLOGON_CONTROL_REDISCOVER:
+ case NETLOGON_CONTROL_TC_QUERY:
+ case NETLOGON_CONTROL_TRANSPORT_NOTIFY:
+ case NETLOGON_CONTROL_BACKUP_CHANGE_LOG:
+ case NETLOGON_CONTROL_TRUNCATE_LOG:
+ case NETLOGON_CONTROL_SET_DBFLAG:
+ case NETLOGON_CONTROL_BREAKPOINT:
+
+ if ( QueryLevel >= 1 && QueryLevel <= 3 ) {
+ NetStatus = NetrLogonControl2 (
+ (LPWSTR) ServerName OPTIONAL,
+ FunctionCode,
+ QueryLevel,
+ (PNETLOGON_CONTROL_DATA_INFORMATION)InputData,
+ &RpcQueryInformation );
+ } else if ( QueryLevel == 4 ) {
+ NetStatus = NetrLogonControl2Ex (
+ (LPWSTR) ServerName OPTIONAL,
+ FunctionCode,
+ QueryLevel,
+ (PNETLOGON_CONTROL_DATA_INFORMATION)InputData,
+ &RpcQueryInformation );
+ } else {
+ NetStatus = ERROR_INVALID_LEVEL;
+ }
+ break;
+ case NETLOGON_CONTROL_FIND_USER:
+ case NETLOGON_CONTROL_UNLOAD_NETLOGON_DLL:
+ if ( QueryLevel >= 1 && QueryLevel <= 4 ) {
+ NetStatus = NetrLogonControl2Ex (
+ (LPWSTR) ServerName OPTIONAL,
+ FunctionCode,
+ QueryLevel,
+ (PNETLOGON_CONTROL_DATA_INFORMATION)InputData,
+ &RpcQueryInformation );
+ } else {
+ NetStatus = ERROR_INVALID_LEVEL;
+ }
+ break;
+ default:
+ NetStatus = ERROR_INVALID_LEVEL;
+ }
+
+ *QueryInformation = (LPBYTE) RpcQueryInformation.NetlogonInfo1;
+
+ } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
+
+ NetStatus = RpcExceptionCode();
+
+ } RpcEndExcept;
+
+ IF_DEBUG( LOGON ) {
+ NetpKdPrint(("I_NetLogonControl rc = %lu 0x%lx\n",
+ NetStatus, NetStatus));
+ }
+
+ return NetStatus;
+}
+
+
+NTSTATUS
+I_NetDatabaseRedo(
+ IN LPWSTR PrimaryName,
+ IN LPWSTR ComputerName,
+ IN PNETLOGON_AUTHENTICATOR Authenticator,
+ OUT PNETLOGON_AUTHENTICATOR ReturnAuthenticator,
+ IN LPBYTE ChangeLogEntry,
+ IN DWORD ChangeLogEntrySize,
+ OUT PNETLOGON_DELTA_ENUM_ARRAY *DeltaArray
+ )
+/*++
+
+Routine Description:
+
+ This function is used by a SAM BDC to request infomation about a single
+ account. This function can only be called by a server which has previously
+ authenticated with the PDC by calling I_NetServerAuthenticate. This
+ function uses RPC to contact the Netlogon service on the PDC.
+
+Arguments:
+
+ PrimaryName -- Name of the PDC to retrieve the delta from.
+
+ ComputerName -- Name of the BDC making the call.
+
+ Authenticator -- supplied by the server.
+
+ ReturnAuthenticator -- Receives an authenticator returned by the PDC.
+
+ ChangeLogEntry -- A description of the account to be queried.
+
+ ChangeLogEntrySize -- Size (in bytes) of the ChangeLogEntry.
+
+ DeltaArray -- Receives a pointer to a buffer where the information
+ is placed. The information returned is an array of
+ NETLOGON_DELTA_ENUM structures.
+
+Return Value:
+
+ STATUS_SUCCESS -- The function completed successfully.
+
+ STATUS_ACCESS_DENIED -- The replicant should re-authenticate with
+ the PDC.
+
+--*/
+{
+ NTSTATUS Status = 0;
+
+ //
+ // Do the RPC call with an exception handler since RPC will raise an
+ // exception if anything fails. It is up to us to figure out what
+ // to do once the exception is raised.
+ //
+
+ RpcTryExcept {
+
+ //
+ // Call RPC version of the API.
+ //
+ *DeltaArray = NULL; // Force RPC to allocate
+
+ Status = NetrDatabaseRedo(
+ PrimaryName,
+ ComputerName,
+ Authenticator,
+ ReturnAuthenticator,
+ ChangeLogEntry,
+ ChangeLogEntrySize,
+ DeltaArray );
+
+ } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
+
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+
+ } RpcEndExcept;
+
+
+ IF_DEBUG( LOGON ) {
+ NetpKdPrint(("I_NetDatabaseSync rc = %lu 0x%lx\n", Status, Status));
+ }
+
+ return Status;
+}
+
+
+NTSTATUS
+NetEnumerateTrustedDomains (
+ IN LPWSTR ServerName OPTIONAL,
+ OUT LPWSTR *DomainNames
+ )
+
+/*++
+
+Routine Description:
+
+ This API returns the names of the domains trusted by the domain ServerName is a member of.
+ ServerName must be an NT workstation or NT non-DC server.
+
+ The returned list does not include the domain ServerName is directly a member of.
+
+ Netlogon implements this API by calling LsaEnumerateTrustedDomains on a DC in the
+ domain ServerName is a member of. However, Netlogon returns cached information if
+ it has been less than 5 minutes since the last call was made or if no DC is available.
+ Netlogon's cache of Trusted domain names is maintained in the registry across reboots.
+ As such, the list is available upon boot even if no DC is available.
+
+
+Arguments:
+
+ ServerName - name of remote server (null for local). ServerName must be an NT workstation
+ or NT non-DC server.
+
+ DomainNames - Returns an allocated buffer containing the list of trusted domains in
+ MULTI-SZ format (i.e., each string is terminated by a zero character, the next string
+ immediately follows, the sequence is terminated by zero length domain name). The
+ buffer should be freed using NetApiBufferFree.
+
+Return Value:
+
+
+ ERROR_SUCCESS - Success.
+
+ STATUS_NOT_SUPPORTED - ServerName is not an NT workstation or NT non-DC server.
+
+ STATUS_NO_LOGON_SERVERS - No DC could be found and no cached information is available.
+
+ STATUS_NO_TRUST_LSA_SECRET - The client side of the trust relationship is
+ broken and no cached information is available.
+
+ STATUS_NO_TRUST_SAM_ACCOUNT - The server side of the trust relationship is
+ broken or the password is broken and no cached information is available.
+
+--*/
+{
+ NTSTATUS Status = 0;
+ DOMAIN_NAME_BUFFER DomainNameBuffer;
+
+ //
+ // Do the RPC call with an exception handler since RPC will raise an
+ // exception if anything fails. It is up to us to figure out what
+ // to do once the exception is raised.
+ //
+
+ RpcTryExcept {
+
+ //
+ // Call RPC version of the API.
+ //
+ DomainNameBuffer.DomainNameByteCount = 0;
+ DomainNameBuffer.DomainNames = NULL; // Force RPC to allocate
+
+ Status = NetrEnumerateTrustedDomains(
+ ServerName,
+ &DomainNameBuffer );
+
+ if ( NT_SUCCESS(Status) ) {
+ *DomainNames = (LPWSTR) DomainNameBuffer.DomainNames;
+ }
+
+ } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
+
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+
+ } RpcEndExcept;
+
+
+ IF_DEBUG( LOGON ) {
+ NetpKdPrint(("NetEnumerateDomainNames rc = %lu 0x%lx\n", Status, Status));
+ }
+
+ return Status;
+}
diff --git a/private/net/svcdlls/logonsrv/dirs b/private/net/svcdlls/logonsrv/dirs
new file mode 100644
index 000000000..0bb93420f
--- /dev/null
+++ b/private/net/svcdlls/logonsrv/dirs
@@ -0,0 +1,47 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ dirs.
+
+Abstract:
+
+ This file specifies the subdirectories of the current directory that
+ contain component makefiles.
+
+
+Author:
+
+ Steve Wood (stevewo) 17-Apr-1990
+
+NOTE: Commented description of this file is in \nt\bak\bin\dirs.tpl
+
+!ENDIF
+
+#
+# This macro is defined by the developer. It is a list of all subdirectories
+# that build required components. Each subdirectory should be on a separate
+# line using the line continuation character. This will minimize merge
+# conflicts if two developers adding source files to the same component.
+# The order of the directories is the order that they will be built when
+# doing a build.
+#
+
+DIRS=server \
+ client \
+ monitor
+
+
+#
+# This macro is defined by the developer. It is a list of all subdirectories
+# that build optional components. Each subdirectory should be on a separate
+# line using the line continuation character. This will minimize merge
+# conflicts if two developers adding source files to the same component.
+# The order of the directories is the order that they will be built when
+# doing a build.
+#
+
+#OPTIONAL_DIRS=dir8 \
+# dir9
diff --git a/private/net/svcdlls/logonsrv/imports.h b/private/net/svcdlls/logonsrv/imports.h
new file mode 100644
index 000000000..4b8d0f7c7
--- /dev/null
+++ b/private/net/svcdlls/logonsrv/imports.h
@@ -0,0 +1,40 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ imports.h
+
+Abstract:
+
+ This file allows us to include standard system header files in the
+ .idl file. The main .idl file imports a file called import.idl.
+ This allows the .idl file to use the types defined in these header
+ files. It also causes the following line to be added in the
+ MIDL generated header file:
+
+ #include "imports.h"
+
+ Thus these types are available to the RPC stub routines as well.
+
+Author:
+
+ Dan Lafferty (danl) 07-May-1991
+
+Revision History:
+
+
+--*/
+
+
+#include <nt.h> // LARGE_INTEGER definition
+#include <lsass.h> // OLD_LARGE_INTEGER definition
+#include <windef.h>
+#include <lmcons.h>
+#include <ntsam.h>
+#include <lmaccess.h>
+#include <netlogon.h>
+#include <crypt.h>
+#include <logonmsv.h>
+#include <ssi.h>
diff --git a/private/net/svcdlls/logonsrv/imports.idl b/private/net/svcdlls/logonsrv/imports.idl
new file mode 100644
index 000000000..c3f430aa2
--- /dev/null
+++ b/private/net/svcdlls/logonsrv/imports.idl
@@ -0,0 +1,58 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ imports.idl
+
+Abstract:
+
+ This file is useful for creating RPC interfaces that require the use
+ of types defined in other header files. The .idl file for the RPC
+ product should contain a line in the interface body that imports this
+ file. For example:
+
+ import "imports.idl";
+
+ Doing this causes the MIDL generated header file to contain the
+ #include lines that are in this file.
+
+ If this technique is not used, and instead the .idl file for the RPC
+ product simply contains #include <windef.h>, then the contents of
+ windef.h will be expanded in the MIDL generated header file. This
+ can lead to duplicate definition problems later when the RPC client
+ or RPC server code needs to include both the MIDL generated header file
+ and a file that is included in windef.h.
+
+Author:
+
+ Dan Lafferty (danl) 20-Mar-1991
+
+Environment:
+
+ User Mode - Win32 - for use with the MIDL compiler
+
+
+Revision History:
+
+ 03-Apr-1991 danl
+ created
+
+--*/
+
+
+[
+ uuid(12345678-1234-ABCD-EF00-9948756789AB),
+#ifdef __midl
+ ms_union,
+#endif // __midl
+ version(2.0)
+]
+interface logon_imports
+
+{
+#define MIDL_PASS
+#include "imports.h"
+
+}
diff --git a/private/net/svcdlls/logonsrv/logon.idl b/private/net/svcdlls/logonsrv/logon.idl
new file mode 100644
index 000000000..9408649cd
--- /dev/null
+++ b/private/net/svcdlls/logonsrv/logon.idl
@@ -0,0 +1,857 @@
+/*++
+
+Copyright (c) 1990 Microsoft Corporation
+
+Module Name:
+
+ LOGON.IDL
+
+Abstract:
+
+ Contains the Netr (Net Remote) RPC interface specification for the
+ API associated with the Netlogon Service.
+
+ Also contains the RPC specific data structures for these API.
+
+Author:
+
+ Cliff Van Dyke (CliffV) 25-Jun-1991
+
+Environment:
+
+ User Mode - Win32
+
+Revision History:
+
+ 25-Jun-1991 CliffV
+ created
+
+ 04-Apr-1992 MadanA
+ Added support for LSA replication.
+
+--*/
+
+//
+// Interface Attributes
+//
+
+[
+ uuid(12345678-1234-ABCD-EF00-01234567CFFB),
+ version(1.0),
+#ifdef __midl
+ ms_union,
+#endif // __midl
+ pointer_default(unique)
+]
+
+//
+// Interface Keyword
+//
+
+interface logon
+
+//
+// Interface Body
+//
+
+{
+
+#define _RPC_
+
+import "imports.idl"; // import all the include files
+#include <lmcons.h> // Needed for prototype below
+
+//
+// FunctionCode values for I_NetLogonControl.
+//
+
+#define NETLOGON_CONTROL_QUERY 1 // No-op: just query
+#define NETLOGON_CONTROL_REPLICATE 2 // Force replicate on BDC
+#define NETLOGON_CONTROL_SYNCHRONIZE 3 // Force synchronize on BDC
+#define NETLOGON_CONTROL_PDC_REPLICATE 4 // Force PDC to broadcast change
+#define NETLOGON_CONTROL_REDISCOVER 5 // Force to re-discover trusted domain DCs
+#define NETLOGON_CONTROL_TC_QUERY 6 // Query status of specified trusted channel status
+#define NETLOGON_CONTROL_TRANSPORT_NOTIFY 7 // Notify netlogon that a new transport has come online
+#define NETLOGON_CONTROL_FIND_USER 8 // Find named user in a trusted domain
+
+// Debug function codes
+
+#define NETLOGON_CONTROL_BACKUP_CHANGE_LOG 0xFFFC
+#define NETLOGON_CONTROL_TRUNCATE_LOG 0xFFFD
+#define NETLOGON_CONTROL_SET_DBFLAG 0xFFFE
+#define NETLOGON_CONTROL_BREAKPOINT 0xFFFF
+
+typedef [handle] wchar_t * LOGONSRV_HANDLE;
+
+//
+// Data types for rpc stubs.
+//
+
+// ?? the following data types should come from LSA or SAM idl definitions
+
+//
+// We must hide the PSID in a structure to avoid too many *'s in a
+// field that uses size_is - otherwise MIDL has a fit.
+//
+
+typedef struct _NLPR_SID_INFORMATION {
+
+ PISID SidPointer;
+
+} NLPR_SID_INFORMATION, *PNLPR_SID_INFORMATION;
+
+
+//
+// Define an array of pointers to SIDs
+//
+
+typedef struct _NLPR_SID_ARRAY {
+
+ //
+ // Indicates the number of Elements in the array.
+ //
+
+ ULONG Count;
+
+ //
+ // Points to the array of sid-pointers
+ //
+
+ [size_is(Count)] PNLPR_SID_INFORMATION Sids;
+
+} NLPR_SID_ARRAY, *PNLPR_SID_ARRAY;
+
+
+//
+// Two-way encrypted value structure in Self-relative form. This
+// is just like a String.
+//
+
+typedef struct _NLPR_CR_CIPHER_VALUE {
+
+ ULONG Length;
+ ULONG MaximumLength;
+ [size_is(MaximumLength), length_is(Length)] PUCHAR Buffer;
+
+} NLPR_CR_CIPHER_VALUE, *PNLPR_CR_CIPHER_VALUE;
+
+
+typedef struct _NLPR_LOGON_HOURS {
+
+ USHORT UnitsPerWeek;
+
+ //
+ // Points to an array of bitmask. The bits represent either days,
+ // hours or minutes in the week depending upon the value of
+ // UnitsPerWeek. (Technically, they could represent any division of
+ // time not finer than minute granularity).
+
+ // Day granularity is specified by specifying SAM_DAYS_PER_WEEK.
+ // Hours granularity is specified by specifying SAM_HOURS_PER_WEEK.
+ // Minute granularity is specified by specifying
+ // SAM_MINUTES_PER_WEEK. The number of bytes pointed to by this
+ // field is ((UnitsPerWeek + 7) / 8) and may not exceed
+ // ((SAM_MINUTES_PER_WEEK+7)/8 == 1260).
+ //
+
+ [size_is(1260), length_is((UnitsPerWeek+7)/8)] PUCHAR LogonHours;
+
+} NLPR_LOGON_HOURS, *PNLPR_LOGON_HOURS;
+
+
+typedef struct _NLPR_USER_PRIVATE_INFO {
+
+ BOOLEAN SensitiveData;
+
+ //
+ // If SesitiveData is TRUE then the data is encrypted using
+ // sessionkey across wire.
+ //
+
+ ULONG DataLength;
+ [size_is(DataLength)] PUCHAR Data;
+
+} NLPR_USER_PRIVATE_INFO, *PNLPR_USER_PRIVATE_INFO;
+
+#pragma pack(4)
+typedef struct _NLPR_MODIFIED_COUNT {
+
+ OLD_LARGE_INTEGER ModifiedCount;
+
+} NLPR_MODIFIED_COUNT, *PNLPR_MODIFIED_COUNT;
+#pragma pack()
+
+#pragma pack(4)
+typedef struct _NLPR_QUOTA_LIMITS {
+ ULONG PagedPoolLimit;
+ ULONG NonPagedPoolLimit;
+ ULONG MinimumWorkingSetSize;
+ ULONG MaximumWorkingSetSize;
+ ULONG PagefileLimit;
+ OLD_LARGE_INTEGER TimeLimit;
+} NLPR_QUOTA_LIMITS, *PNLPR_QUOTA_LIMITS;
+#pragma pack()
+
+//
+// Enumeration structure returned from I_NetSamDeltas and I_NetSamSync
+//
+
+//
+// Structure to completely describe a user.
+//
+
+#pragma pack(4)
+typedef struct _NETLOGON_DELTA_USER {
+ UNICODE_STRING UserName;
+ UNICODE_STRING FullName;
+ ULONG UserId;
+ ULONG PrimaryGroupId;
+ UNICODE_STRING HomeDirectory;
+ UNICODE_STRING HomeDirectoryDrive;
+ UNICODE_STRING ScriptPath;
+ UNICODE_STRING AdminComment;
+ UNICODE_STRING WorkStations;
+ OLD_LARGE_INTEGER LastLogon;
+ OLD_LARGE_INTEGER LastLogoff;
+ NLPR_LOGON_HOURS LogonHours;
+ USHORT BadPasswordCount;
+ USHORT LogonCount;
+ OLD_LARGE_INTEGER PasswordLastSet;
+ OLD_LARGE_INTEGER AccountExpires;
+ ULONG UserAccountControl;
+
+ //
+ // The following fields are duplicates of information already in
+ // the Private data. Starting in NT 1.0A, these fields are zeroed.
+ //
+ ENCRYPTED_NT_OWF_PASSWORD EncryptedNtOwfPassword;
+ ENCRYPTED_LM_OWF_PASSWORD EncryptedLmOwfPassword;
+ BOOLEAN NtPasswordPresent;
+ BOOLEAN LmPasswordPresent;
+ BOOLEAN PasswordExpired;
+
+ UNICODE_STRING UserComment;
+ UNICODE_STRING Parameters;
+ USHORT CountryCode;
+ USHORT CodePage;
+
+ NLPR_USER_PRIVATE_INFO PrivateData; // password history
+
+ SECURITY_INFORMATION SecurityInformation;
+ ULONG SecuritySize;
+ [size_is(SecuritySize)] PUCHAR SecurityDescriptor;
+
+ UNICODE_STRING DummyString1; // used for profile path.
+ UNICODE_STRING DummyString2;
+ UNICODE_STRING DummyString3;
+ UNICODE_STRING DummyString4;
+ ULONG DummyLong1; // used for LastBadPasswordTime.HighPart
+ ULONG DummyLong2; // used for LastBadPasswordTime.LowPart
+ ULONG DummyLong3;
+ ULONG DummyLong4;
+
+} NETLOGON_DELTA_USER, *PNETLOGON_DELTA_USER;
+#pragma pack()
+
+//
+// Structure to completely describe a group.
+//
+typedef struct _NETLOGON_DELTA_GROUP {
+ UNICODE_STRING Name;
+ ULONG RelativeId;
+ ULONG Attributes;
+ UNICODE_STRING AdminComment;
+
+ SECURITY_INFORMATION SecurityInformation;
+ ULONG SecuritySize;
+ [size_is(SecuritySize)] PUCHAR SecurityDescriptor;
+
+ UNICODE_STRING DummyString1;
+ UNICODE_STRING DummyString2;
+ UNICODE_STRING DummyString3;
+ UNICODE_STRING DummyString4;
+ ULONG DummyLong1;
+ ULONG DummyLong2;
+ ULONG DummyLong3;
+ ULONG DummyLong4;
+} NETLOGON_DELTA_GROUP, *PNETLOGON_DELTA_GROUP;
+
+
+//
+// Structure to completely describe all the members of a group.
+//
+typedef struct _NETLOGON_DELTA_GROUP_MEMBER {
+ [size_is(MemberCount)] PULONG MemberIds;
+ [size_is(MemberCount)] PULONG Attributes;
+ ULONG MemberCount;
+
+ ULONG DummyLong1;
+ ULONG DummyLong2;
+ ULONG DummyLong3;
+ ULONG DummyLong4;
+} NETLOGON_DELTA_GROUP_MEMBER, *PNETLOGON_DELTA_GROUP_MEMBER;
+
+//
+// Structure to completely describe a alias.
+//
+typedef struct _NETLOGON_DELTA_ALIAS {
+ UNICODE_STRING Name;
+ ULONG RelativeId;
+// UNICODE_STRING AdminComment;
+
+ SECURITY_INFORMATION SecurityInformation;
+ ULONG SecuritySize;
+ [size_is(SecuritySize)] PUCHAR SecurityDescriptor;
+
+ UNICODE_STRING DummyString1; // used for admin comment
+ UNICODE_STRING DummyString2;
+ UNICODE_STRING DummyString3;
+ UNICODE_STRING DummyString4;
+ ULONG DummyLong1;
+ ULONG DummyLong2;
+ ULONG DummyLong3;
+ ULONG DummyLong4;
+} NETLOGON_DELTA_ALIAS, *PNETLOGON_DELTA_ALIAS;
+
+
+//
+// Structure to completely describe all the members of a alias.
+//
+typedef struct _NETLOGON_DELTA_ALIAS_MEMBER {
+ NLPR_SID_ARRAY Members;
+
+ ULONG DummyLong1;
+ ULONG DummyLong2;
+ ULONG DummyLong3;
+ ULONG DummyLong4;
+} NETLOGON_DELTA_ALIAS_MEMBER, *PNETLOGON_DELTA_ALIAS_MEMBER;
+
+//
+// Structure to completely describe a domain.
+//
+#pragma pack(4)
+typedef struct _NETLOGON_DELTA_DOMAIN {
+ UNICODE_STRING DomainName;
+ UNICODE_STRING OemInformation;
+ OLD_LARGE_INTEGER ForceLogoff;
+ USHORT MinPasswordLength;
+ USHORT PasswordHistoryLength;
+ OLD_LARGE_INTEGER MaxPasswordAge;
+ OLD_LARGE_INTEGER MinPasswordAge;
+
+ OLD_LARGE_INTEGER DomainModifiedCount;
+ OLD_LARGE_INTEGER DomainCreationTime;
+
+ // All this information is maintained separately on each system.
+#ifdef notdef
+ UNICODE_STRING ReplicaSourceNodeName;
+ DOMAIN_SERVER_ENABLE_STATE DomainServerState;
+ DOMAIN_SERVER_ROLE DomainServerRole;
+#endif // notdef
+
+ SECURITY_INFORMATION SecurityInformation;
+ ULONG SecuritySize;
+ [size_is(SecuritySize)] PUCHAR SecurityDescriptor;
+
+ UNICODE_STRING DummyString1; // used to replicate DOMAIN_LOCKOUT_INFORMATION
+ UNICODE_STRING DummyString2;
+ UNICODE_STRING DummyString3;
+ UNICODE_STRING DummyString4;
+ ULONG DummyLong1; // used to replicate PasswordProperties
+ ULONG DummyLong2;
+ ULONG DummyLong3;
+ ULONG DummyLong4;
+} NETLOGON_DELTA_DOMAIN, *PNETLOGON_DELTA_DOMAIN;
+#pragma pack()
+
+typedef struct _NETLOGON_DELTA_RENAME {
+ UNICODE_STRING OldName;
+ UNICODE_STRING NewName;
+
+ UNICODE_STRING DummyString1;
+ UNICODE_STRING DummyString2;
+ UNICODE_STRING DummyString3;
+ UNICODE_STRING DummyString4;
+ ULONG DummyLong1;
+ ULONG DummyLong2;
+ ULONG DummyLong3;
+ ULONG DummyLong4;
+} NETLOGON_RENAME_GROUP, *PNETLOGON_DELTA_RENAME_GROUP,
+ NETLOGON_RENAME_USER, *PNETLOGON_DELTA_RENAME_USER,
+ NETLOGON_RENAME_ALIAS, *PNETLOGON_DELTA_RENAME_ALIAS;
+
+#pragma pack(4)
+typedef struct _NETLOGON_DELTA_POLICY {
+ ULONG MaximumLogSize;
+ OLD_LARGE_INTEGER AuditRetentionPeriod;
+
+ BOOLEAN AuditingMode;
+ ULONG MaximumAuditEventCount;
+ [size_is(MaximumAuditEventCount + 1)] PULONG EventAuditingOptions;
+
+ UNICODE_STRING PrimaryDomainName;
+ PISID PrimaryDomainSid;
+
+ NLPR_QUOTA_LIMITS QuotaLimits;
+
+ OLD_LARGE_INTEGER ModifiedId;
+ OLD_LARGE_INTEGER DatabaseCreationTime;
+
+ SECURITY_INFORMATION SecurityInformation;
+ ULONG SecuritySize;
+ [size_is(SecuritySize)] PUCHAR SecurityDescriptor;
+
+ UNICODE_STRING DummyString1;
+ UNICODE_STRING DummyString2;
+ UNICODE_STRING DummyString3;
+ UNICODE_STRING DummyString4;
+ ULONG DummyLong1;
+ ULONG DummyLong2;
+ ULONG DummyLong3;
+ ULONG DummyLong4;
+} NETLOGON_DELTA_POLICY, *PNETLOGON_DELTA_POLICY;
+#pragma pack()
+
+typedef struct _NETLOGON_DELTA_TRUSTED_DOMAINS {
+ UNICODE_STRING DomainName;
+ ULONG NumControllerEntries;
+ [size_is(NumControllerEntries)] PUNICODE_STRING ControllerNames;
+
+ SECURITY_INFORMATION SecurityInformation;
+ ULONG SecuritySize;
+ [size_is(SecuritySize)] PUCHAR SecurityDescriptor;
+
+ UNICODE_STRING DummyString1;
+ UNICODE_STRING DummyString2;
+ UNICODE_STRING DummyString3;
+ UNICODE_STRING DummyString4;
+ ULONG DummyLong1; // used for posix offset.
+ ULONG DummyLong2;
+ ULONG DummyLong3;
+ ULONG DummyLong4;
+} NETLOGON_DELTA_TRUSTED_DOMAINS, *PNETLOGON_DELTA_TRUSTED_DOMAINS;
+
+typedef struct _NETLOGON_DELTA_ACCOUNTS {
+ ULONG PrivilegeEntries;
+ ULONG PrivilegeControl;
+ [size_is(PrivilegeEntries)] PULONG PrivilegeAttributes;
+ [size_is(PrivilegeEntries)] PUNICODE_STRING PrivilegeNames;
+
+ NLPR_QUOTA_LIMITS QuotaLimits;
+ ULONG SystemAccessFlags;
+
+ SECURITY_INFORMATION SecurityInformation;
+ ULONG SecuritySize;
+ [size_is(SecuritySize)] PUCHAR SecurityDescriptor;
+
+ UNICODE_STRING DummyString1;
+ UNICODE_STRING DummyString2;
+ UNICODE_STRING DummyString3;
+ UNICODE_STRING DummyString4;
+ ULONG DummyLong1;
+ ULONG DummyLong2;
+ ULONG DummyLong3;
+ ULONG DummyLong4;
+} NETLOGON_DELTA_ACCOUNTS, *PNETLOGON_DELTA_ACCOUNTS;
+
+#pragma pack(4)
+typedef struct _NETLOGON_DELTA_SECRET {
+ NLPR_CR_CIPHER_VALUE CurrentValue;
+ OLD_LARGE_INTEGER CurrentValueSetTime;
+ NLPR_CR_CIPHER_VALUE OldValue;
+ OLD_LARGE_INTEGER OldValueSetTime;
+
+ SECURITY_INFORMATION SecurityInformation;
+ ULONG SecuritySize;
+ [size_is(SecuritySize)] PUCHAR SecurityDescriptor;
+
+ UNICODE_STRING DummyString1;
+ UNICODE_STRING DummyString2;
+ UNICODE_STRING DummyString3;
+ UNICODE_STRING DummyString4;
+ ULONG DummyLong1;
+ ULONG DummyLong2;
+ ULONG DummyLong3;
+ ULONG DummyLong4;
+} NETLOGON_DELTA_SECRET, *PNETLOGON_DELTA_SECRET;
+#pragma pack()
+
+typedef struct _NETLOGON_DELTA_DELETE {
+ [string] wchar_t * AccountName;
+
+ UNICODE_STRING DummyString1;
+ UNICODE_STRING DummyString2;
+ UNICODE_STRING DummyString3;
+ UNICODE_STRING DummyString4;
+ ULONG DummyLong1;
+ ULONG DummyLong2;
+ ULONG DummyLong3;
+ ULONG DummyLong4;
+} NETLOGON_DELTA_DELETE_GROUP, *PNETLOGON_DELTA_DELETE_GROUP,
+ NETLOGON_DELTA_DELETE_USER, *PNETLOGON_DELTA_DELETE_USER;
+
+//
+// A Union of each of the above types.
+//
+#pragma pack(4)
+typedef [switch_type(NETLOGON_DELTA_TYPE)] union _NETLOGON_DELTA_UNION {
+ [case(AddOrChangeDomain)] PNETLOGON_DELTA_DOMAIN DeltaDomain;
+ [case(AddOrChangeGroup)] PNETLOGON_DELTA_GROUP DeltaGroup;
+ [case(RenameGroup)] PNETLOGON_DELTA_RENAME_GROUP DeltaRenameGroup;
+ [case(AddOrChangeUser)] PNETLOGON_DELTA_USER DeltaUser;
+ [case(RenameUser)] PNETLOGON_DELTA_RENAME_USER DeltaRenameUser;
+ [case(ChangeGroupMembership)] PNETLOGON_DELTA_GROUP_MEMBER DeltaGroupMember;
+ [case(AddOrChangeAlias)] PNETLOGON_DELTA_ALIAS DeltaAlias;
+ [case(RenameAlias)] PNETLOGON_DELTA_RENAME_ALIAS DeltaRenameAlias;
+ [case(ChangeAliasMembership)] PNETLOGON_DELTA_ALIAS_MEMBER DeltaAliasMember;
+ [case(AddOrChangeLsaPolicy)] PNETLOGON_DELTA_POLICY DeltaPolicy;
+ [case(AddOrChangeLsaTDomain)] PNETLOGON_DELTA_TRUSTED_DOMAINS DeltaTDomains;
+ [case(AddOrChangeLsaAccount)] PNETLOGON_DELTA_ACCOUNTS DeltaAccounts;
+ [case(AddOrChangeLsaSecret)] PNETLOGON_DELTA_SECRET DeltaSecret;
+ [case(DeleteGroupByName)] PNETLOGON_DELTA_DELETE_GROUP DeltaDeleteGroup;
+ [case(DeleteUserByName)] PNETLOGON_DELTA_DELETE_USER DeltaDeleteUser;
+ [case(SerialNumberSkip)] PNLPR_MODIFIED_COUNT DeltaSerialNumberSkip;
+ [default] ; // Ship nothing for Delete Cases
+} NETLOGON_DELTA_UNION, *PNETLOGON_DELTA_UNION;
+#pragma pack()
+
+typedef [switch_type(NETLOGON_DELTA_TYPE)] union _NETLOGON_DELTA_ID_UNION {
+ [case(AddOrChangeDomain,
+ AddOrChangeGroup,
+ DeleteGroup,
+ RenameGroup,
+ AddOrChangeUser,
+ DeleteUser,
+ RenameUser,
+ ChangeGroupMembership,
+ AddOrChangeAlias,
+ DeleteAlias,
+ RenameAlias,
+ ChangeAliasMembership,
+ DeleteGroupByName,
+ DeleteUserByName )] ULONG Rid;
+
+ [case(AddOrChangeLsaPolicy,
+ AddOrChangeLsaTDomain,
+ DeleteLsaTDomain,
+ AddOrChangeLsaAccount,
+ DeleteLsaAccount)] PISID Sid;
+ [case(AddOrChangeLsaSecret,
+ DeleteLsaSecret)] [string] wchar_t * Name;
+ [default] ;
+} NETLOGON_DELTA_ID_UNION, *PNETLOGON_DELTA_ID_UNION;
+
+
+//
+// A common structure to describe a single enumerated object.
+//
+#pragma pack(4)
+typedef struct _NETLOGON_DELTA_ENUM {
+ NETLOGON_DELTA_TYPE DeltaType;
+ [switch_is(DeltaType)] NETLOGON_DELTA_ID_UNION DeltaID;
+ [switch_is(DeltaType)] NETLOGON_DELTA_UNION DeltaUnion;
+} NETLOGON_DELTA_ENUM, *PNETLOGON_DELTA_ENUM;
+#pragma pack()
+
+//
+// Structure that defines the array of enumerated objects.
+//
+
+#pragma pack(4)
+typedef struct _NETLOGON_DELTA_ENUM_ARRAY {
+ DWORD CountReturned;
+ [size_is(CountReturned)] PNETLOGON_DELTA_ENUM Deltas;
+} NETLOGON_DELTA_ENUM_ARRAY, *PNETLOGON_DELTA_ENUM_ARRAY;
+#pragma pack()
+
+//
+// Function Prototypes - Logon Service
+//
+
+
+NET_API_STATUS
+NetrLogonUasLogon (
+ [in,unique,string] LOGONSRV_HANDLE ServerName,
+ [in, string] wchar_t * UserName,
+ [in, string] wchar_t * Workstation,
+ [out] PNETLOGON_VALIDATION_UAS_INFO *ValidationInformation
+ );
+
+NET_API_STATUS
+NetrLogonUasLogoff (
+ [in,unique,string] LOGONSRV_HANDLE ServerName,
+ [in, string] wchar_t * UserName,
+ [in, string] wchar_t * Workstation,
+ [out] PNETLOGON_LOGOFF_UAS_INFO LogoffInformation
+ );
+
+//
+// NetrLogonSam routines
+//
+typedef [switch_type(enum _NETLOGON_LOGON_INFO_CLASS)]
+ union _NETLOGON_LEVEL {
+ [case(NetlogonInteractiveInformation)]
+ PNETLOGON_INTERACTIVE_INFO LogonInteractive;
+ [case(NetlogonServiceInformation)]
+ PNETLOGON_SERVICE_INFO LogonService;
+ [case(NetlogonNetworkInformation)]
+ PNETLOGON_NETWORK_INFO LogonNetwork;
+ [default]
+ ;
+} NETLOGON_LEVEL, * PNETLOGON_LEVEL;
+
+typedef [switch_type(enum _NETLOGON_LOGON_INFO_CLASS)]
+ union _NETLOGON_VALIDATION {
+ [case(NetlogonValidationSamInfo)]
+ PNETLOGON_VALIDATION_SAM_INFO ValidationSam;
+ [case(NetlogonValidationSamInfo2)]
+ PNETLOGON_VALIDATION_SAM_INFO2 ValidationSam2;
+ [default]
+ ;
+} NETLOGON_VALIDATION, * PNETLOGON_VALIDATION;
+
+NTSTATUS
+NetrLogonSamLogon (
+ [in,unique,string] LOGONSRV_HANDLE LogonServer,
+ [in,string,unique] wchar_t * ComputerName,
+ [in,unique] PNETLOGON_AUTHENTICATOR Authenticator,
+ [in,out,unique] PNETLOGON_AUTHENTICATOR ReturnAuthenticator,
+ [in] NETLOGON_LOGON_INFO_CLASS LogonLevel,
+ [in,switch_is(LogonLevel)] PNETLOGON_LEVEL LogonInformation,
+ [in] NETLOGON_VALIDATION_INFO_CLASS ValidationLevel,
+ [out,switch_is(ValidationLevel)] PNETLOGON_VALIDATION ValidationInformation,
+ [out] PBOOLEAN Authoritative
+ );
+
+NTSTATUS
+NetrLogonSamLogoff (
+ [in,unique,string] LOGONSRV_HANDLE LogonServer,
+ [in,string,unique] wchar_t * ComputerName,
+ [in,unique] PNETLOGON_AUTHENTICATOR Authenticator,
+ [in,out,unique] PNETLOGON_AUTHENTICATOR ReturnAuthenticator,
+ [in] NETLOGON_LOGON_INFO_CLASS LogonLevel,
+ [in,switch_is(LogonLevel)] PNETLOGON_LEVEL LogonInformation
+);
+
+NTSTATUS
+NetrServerReqChallenge (
+ [in,unique,string] LOGONSRV_HANDLE PrimaryName,
+ [in, string] wchar_t * ComputerName,
+ [in] PNETLOGON_CREDENTIAL ClientChallenge,
+ [out] PNETLOGON_CREDENTIAL ServerChallenge
+ );
+
+NTSTATUS
+NetrServerAuthenticate (
+ [in,unique,string] LOGONSRV_HANDLE PrimaryName,
+ [in,string] wchar_t * AccountName,
+ [in] NETLOGON_SECURE_CHANNEL_TYPE AccountType,
+ [in, string] wchar_t * ComputerName,
+ [in] PNETLOGON_CREDENTIAL ClientCredential,
+ [out] PNETLOGON_CREDENTIAL ServerCredential
+ );
+
+NTSTATUS
+NetrServerPasswordSet (
+ [in,unique,string] LOGONSRV_HANDLE PrimaryName,
+ [in,string] wchar_t * AccountName,
+ [in] NETLOGON_SECURE_CHANNEL_TYPE AccountType,
+ [in, string] wchar_t * ComputerName,
+ [in] PNETLOGON_AUTHENTICATOR Authenticator,
+ [out] PNETLOGON_AUTHENTICATOR ReturnAuthenticator,
+ [in] PENCRYPTED_LM_OWF_PASSWORD UasNewPassword
+ );
+
+//
+// Replication Routines
+//
+
+
+NTSTATUS
+NetrDatabaseDeltas (
+ [in, string] LOGONSRV_HANDLE primaryname,
+ [in, string] wchar_t * computername,
+ [in] PNETLOGON_AUTHENTICATOR authenticator,
+ [in,out] PNETLOGON_AUTHENTICATOR ret_auth,
+ [in] DWORD DatabaseID,
+ [in, out] PNLPR_MODIFIED_COUNT DomainModifiedCount,
+ [out] PNETLOGON_DELTA_ENUM_ARRAY *DeltaArray,
+ [in] DWORD PreferredMaximumLength
+ );
+
+NTSTATUS
+NetrDatabaseSync (
+ [in, string] LOGONSRV_HANDLE PrimaryName,
+ [in, string] wchar_t * ComputerName,
+ [in] PNETLOGON_AUTHENTICATOR Authenticator,
+ [in,out] PNETLOGON_AUTHENTICATOR ReturnAuthenticator,
+ [in] DWORD DatabaseID,
+ [in, out] PULONG SyncContext,
+ [out] PNETLOGON_DELTA_ENUM_ARRAY *DeltaArray,
+ [in] DWORD PreferredMaximumLength
+ );
+
+NTSTATUS
+NetrAccountDeltas (
+ [in, unique, string] LOGONSRV_HANDLE PrimaryName,
+ [in, string] wchar_t * ComputerName,
+ [in] PNETLOGON_AUTHENTICATOR Authenticator,
+ [in,out] PNETLOGON_AUTHENTICATOR ReturnAuthenticator,
+ [in] PUAS_INFO_0 RecordId,
+ [in] DWORD Count,
+ [in] DWORD Level,
+ [out, size_is(BufferSize)] LPBYTE Buffer,
+ [in] DWORD BufferSize,
+ [out] PULONG CountReturned,
+ [out] PULONG TotalEntries,
+ [out] PUAS_INFO_0 NextRecordId
+ );
+
+NTSTATUS
+NetrAccountSync (
+ [in, unique, string] LOGONSRV_HANDLE PrimaryName,
+ [in, string] wchar_t * ComputerName,
+ [in] PNETLOGON_AUTHENTICATOR Authenticator,
+ [in,out] PNETLOGON_AUTHENTICATOR ReturnAuthenticator,
+ [in] DWORD Reference,
+ [in] DWORD Level,
+ [out, size_is(BufferSize) ] LPBYTE Buffer,
+ [in] DWORD BufferSize,
+ [out] PULONG CountReturned,
+ [out] PULONG TotalEntries,
+ [out] PULONG NextReference,
+ [out] PUAS_INFO_0 LastRecordId
+ );
+
+
+NET_API_STATUS
+NetrGetDCName (
+ [in, string] LOGONSRV_HANDLE ServerName,
+ [in, unique, string] wchar_t *DomainName,
+ [out, string] wchar_t **Buffer
+ );
+
+//
+// I_NetLogonControl
+//
+
+typedef [switch_type(DWORD)] union _NETLOGON_CONTROL_DATA_INFORMATION {
+ [case(NETLOGON_CONTROL_REDISCOVER,
+ NETLOGON_CONTROL_TC_QUERY)] [string] wchar_t * TrustedDomainName;
+ [case(NETLOGON_CONTROL_SET_DBFLAG)] DWORD DebugFlag;
+ [case(NETLOGON_CONTROL_FIND_USER)] [string] wchar_t * UserName;
+ [default]
+ ;
+} NETLOGON_CONTROL_DATA_INFORMATION, * PNETLOGON_CONTROL_DATA_INFORMATION;
+
+typedef [switch_type(DWORD)] union _NETLOGON_CONTROL_QUERY_INFORMATION {
+ [case(1)] PNETLOGON_INFO_1 NetlogonInfo1;
+ [case(2)] PNETLOGON_INFO_2 NetlogonInfo2;
+ [case(3)] PNETLOGON_INFO_3 NetlogonInfo3;
+ [case(4)] PNETLOGON_INFO_4 NetlogonInfo4;
+ [default] ;
+} NETLOGON_CONTROL_QUERY_INFORMATION, * PNETLOGON_CONTROL_QUERY_INFORMATION;
+
+NET_API_STATUS
+NetrLogonControl(
+ [in, unique, string] LOGONSRV_HANDLE ServerName,
+ [in] DWORD FunctionCode,
+ [in] DWORD QueryLevel,
+ [out,switch_is(QueryLevel)] PNETLOGON_CONTROL_QUERY_INFORMATION Buffer
+ );
+
+NET_API_STATUS
+NetrGetAnyDCName (
+ [in, unique, string] LOGONSRV_HANDLE ServerName,
+ [in, unique, string] wchar_t *DomainName,
+ [out, string] wchar_t **Buffer
+ );
+
+NET_API_STATUS
+NetrLogonControl2(
+ [in, unique, string] LOGONSRV_HANDLE ServerName,
+ [in] DWORD FunctionCode,
+ [in] DWORD QueryLevel,
+ [in,switch_is(FunctionCode)] PNETLOGON_CONTROL_DATA_INFORMATION Data,
+ [out,switch_is(QueryLevel)] PNETLOGON_CONTROL_QUERY_INFORMATION Buffer
+ );
+
+NTSTATUS
+NetrServerAuthenticate2 (
+ [in,unique,string] LOGONSRV_HANDLE PrimaryName,
+ [in,string] wchar_t * AccountName,
+ [in] NETLOGON_SECURE_CHANNEL_TYPE AccountType,
+ [in, string] wchar_t * ComputerName,
+ [in] PNETLOGON_CREDENTIAL ClientCredential,
+ [out] PNETLOGON_CREDENTIAL ServerCredential,
+ [in,out] PULONG NegotiateFlags
+ );
+
+//
+// The Sync state indicates tracks the progression of the sync.
+// NlSynchronize() depends on these being in order.
+//
+
+typedef enum _SYNC_STATE {
+ NormalState,
+ DomainState,
+ GroupState,
+ UasBuiltinGroupState,
+ UserState,
+ GroupMemberState,
+ AliasState,
+ AliasMemberState,
+ SamDoneState
+} SYNC_STATE, *PSYNC_STATE;
+
+NTSTATUS
+NetrDatabaseSync2 (
+ [in, string] LOGONSRV_HANDLE PrimaryName,
+ [in, string] wchar_t * ComputerName,
+ [in] PNETLOGON_AUTHENTICATOR Authenticator,
+ [in,out] PNETLOGON_AUTHENTICATOR ReturnAuthenticator,
+ [in] DWORD DatabaseID,
+ [in] SYNC_STATE RestartState,
+ [in, out] PULONG SyncContext,
+ [out] PNETLOGON_DELTA_ENUM_ARRAY *DeltaArray,
+ [in] DWORD PreferredMaximumLength
+ );
+
+NTSTATUS
+NetrDatabaseRedo(
+ [in, string] LOGONSRV_HANDLE PrimaryName,
+ [in, string] wchar_t * ComputerName,
+ [in] PNETLOGON_AUTHENTICATOR Authenticator,
+ [in,out] PNETLOGON_AUTHENTICATOR ReturnAuthenticator,
+ [in, size_is(ChangeLogEntrySize)] PUCHAR ChangeLogEntry,
+ [in] DWORD ChangeLogEntrySize,
+ [out] PNETLOGON_DELTA_ENUM_ARRAY *DeltaArray
+ );
+
+// Same as NetrLogonControl2, but support QueryLevel of 4
+// and function code of NETLOGON_CONTROL_FIND_USER
+NET_API_STATUS
+NetrLogonControl2Ex(
+ [in, unique, string] LOGONSRV_HANDLE ServerName,
+ [in] DWORD FunctionCode,
+ [in] DWORD QueryLevel,
+ [in,switch_is(FunctionCode)] PNETLOGON_CONTROL_DATA_INFORMATION Data,
+ [out,switch_is(QueryLevel)] PNETLOGON_CONTROL_QUERY_INFORMATION Buffer
+ );
+
+//
+// Routine to enumerate trusted domains.
+//
+
+typedef struct _DOMAIN_NAME_BUFFER {
+ ULONG DomainNameByteCount;
+ [unique, size_is(DomainNameByteCount)] PUCHAR DomainNames;
+} DOMAIN_NAME_BUFFER, *PDOMAIN_NAME_BUFFER;
+
+NTSTATUS
+NetrEnumerateTrustedDomains (
+ [in, unique, string] LOGONSRV_HANDLE ServerName,
+ [out] PDOMAIN_NAME_BUFFER DomainNameBuffer
+ );
+
+}
diff --git a/private/net/svcdlls/logonsrv/logoncli.acf b/private/net/svcdlls/logonsrv/logoncli.acf
new file mode 100644
index 000000000..6aed7c1b4
--- /dev/null
+++ b/private/net/svcdlls/logonsrv/logoncli.acf
@@ -0,0 +1,12 @@
+[ implicit_handle( handle_t logon_bhandle )
+] interface logon
+{
+
+typedef [allocate(all_nodes)] PNETLOGON_VALIDATION_SAM_INFO;
+typedef [allocate(all_nodes)] PNETLOGON_VALIDATION_SAM_INFO2;
+typedef [allocate(all_nodes)] PNETLOGON_VALIDATION_UAS_INFO;
+typedef [allocate(all_nodes)] PNETLOGON_INFO_2;
+
+typedef [allocate(all_nodes_aligned)] PNETLOGON_DELTA_ENUM_ARRAY;
+
+}
diff --git a/private/net/svcdlls/logonsrv/logonsrv.acf b/private/net/svcdlls/logonsrv/logonsrv.acf
new file mode 100644
index 000000000..5fe3164e4
--- /dev/null
+++ b/private/net/svcdlls/logonsrv/logonsrv.acf
@@ -0,0 +1,10 @@
+[ implicit_handle( handle_t logon_bhandle )
+] interface logon
+{
+
+typedef [allocate(all_nodes)] PNETLOGON_VALIDATION_SAM_INFO;
+typedef [allocate(all_nodes)] PNETLOGON_VALIDATION_SAM_INFO2;
+typedef [allocate(all_nodes)] PNETLOGON_VALIDATION_UAS_INFO;
+typedef [allocate(all_nodes)] PISID;
+typedef [allocate(all_nodes)] PNLPR_SID_INFORMATION;
+}
diff --git a/private/net/svcdlls/logonsrv/makefil0 b/private/net/svcdlls/logonsrv/makefil0
new file mode 100644
index 000000000..7bb384112
--- /dev/null
+++ b/private/net/svcdlls/logonsrv/makefil0
@@ -0,0 +1,80 @@
+#
+# This is the MIDL compile phase of the build process.
+#
+# The following is where you put the name of your .idl file without
+# the .idl extension:
+#
+
+!INCLUDE $(NTMAKEENV)\makefile.plt
+
+IDL_NAME = logon
+IMPORT = imports
+
+CLIENT_H = logon_c.h
+SERVER_H = logon_s.h
+
+CLIENT_ACF = logoncli.acf
+SERVER_ACF = logonsrv.acf
+
+!IFNDEF DISABLE_NET_UNICODE
+UNICODE=1
+NET_C_DEFINES=-DUNICODE
+!ENDIF
+
+SDKINC = $(BASEDIR)\public\sdk\inc
+NETINC = ..\..\inc
+SDKCRTINC = $(BASEDIR)\public\sdk\inc\crt
+PRIVINC = ..\..\..\inc
+
+INCS = -I$(SDKINC) -I$(SDKCRTINC) -I$(PRIVINC) -I$(NETINC)
+
+CLIENT_TARGETS = client\$(IDL_NAME)_c.c .\client\$(CLIENT_H)
+SERVER_TARGETS = server\$(IDL_NAME)_s.c .\server\$(SERVER_H)
+
+TARGETS = $(CLIENT_TARGETS) $(SERVER_TARGETS)
+
+EXTRN_DEPENDS = $(SDKINC)\lmcons.h \
+ $(SDKINC)\windef.h \
+ $(SDKINC)\nt.h \
+ $(SDKINC)\ntsam.h \
+ $(SDKINC)\lmaccess.h \
+ $(PRIVINC)\netlogon.h \
+ $(PRIVINC)\crypt.h \
+ $(PRIVINC)\logonmsv.h \
+ $(NETINC)\ssi.h
+
+CLIENT_FLAGS = -acf $(CLIENT_ACF) -header $(CLIENT_H) -oldnames
+SERVER_FLAGS = -acf $(SERVER_ACF) -header $(SERVER_H) -oldnames
+
+CPP = -cpp_cmd "$(MIDL_CPP)" $(MIDL_FLAGS) $(C_DEFINES) $(NET_C_DEFINES)
+
+#
+# Define Products and Dependencies
+#
+
+all: $(TARGETS) $(EXTRN_DEPENDS)
+!IF "$(BUILDMSG)" != ""
+ @ech ; $(BUILDMSG) ;
+!ENDIF
+
+clean: delsrc all
+
+delsrc:
+ erase $(TARGETS)
+
+#
+# MIDL COMPILE
+#
+
+$(CLIENT_TARGETS) : .\$(IDL_NAME).idl .\$(IMPORT).idl .\$(IMPORT).h $(EXTRN_DEPENDS) .\$(CLIENT_ACF)
+ midl -Oi -error allocation -error ref -ms_ext -c_ext $(CPP) $(CLIENT_FLAGS) .\$(IDL_NAME).idl $(INCS)
+ IF EXIST $(IDL_NAME)_c.c copy $(IDL_NAME)_c.c .\client & del $(IDL_NAME)_c.c
+ IF EXIST $(IDL_NAME)_s.c del $(IDL_NAME)_s.c
+ IF EXIST $(CLIENT_H) copy $(CLIENT_H) .\client & del $(CLIENT_H)
+
+
+$(SERVER_TARGETS) : .\$(IDL_NAME).idl .\$(IMPORT).idl .\$(IMPORT).h $(EXTRN_DEPENDS) .\$(SERVER_ACF)
+ midl -error stub_data -error allocation -error ref -ms_ext -c_ext $(CPP) $(SERVER_FLAGS) .\$(IDL_NAME).idl $(INCS)
+ IF EXIST $(IDL_NAME)_c.c del $(IDL_NAME)_c.c
+ IF EXIST $(IDL_NAME)_s.c copy $(IDL_NAME)_s.c .\server & del $(IDL_NAME)_s.c
+ IF EXIST $(SERVER_H) copy $(SERVER_H) .\server & del $(SERVER_H)
diff --git a/private/net/svcdlls/logonsrv/monitor/makefile b/private/net/svcdlls/logonsrv/monitor/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/net/svcdlls/logonsrv/monitor/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/net/svcdlls/logonsrv/monitor/makefile.inc b/private/net/svcdlls/logonsrv/monitor/makefile.inc
new file mode 100644
index 000000000..1676d5e04
--- /dev/null
+++ b/private/net/svcdlls/logonsrv/monitor/makefile.inc
@@ -0,0 +1,2 @@
+obj\$(TARGET_DIRECTORY)\nlmon.res: nlmon.rc
+
diff --git a/private/net/svcdlls/logonsrv/monitor/monutil.c b/private/net/svcdlls/logonsrv/monitor/monutil.c
new file mode 100644
index 000000000..a42c2d8dc
--- /dev/null
+++ b/private/net/svcdlls/logonsrv/monitor/monutil.c
@@ -0,0 +1,3232 @@
+/*--
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ monutil.c
+
+Abstract:
+
+ Trusted Domain monitor program support functions.
+
+Author:
+
+ 10-May-1993 (madana)
+
+Environment:
+
+ User mode only.
+ Contains NT-specific code.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+--*/
+
+#define GLOBAL_DEF
+
+#include <nlmon.h>
+
+#define NlMonpInitUnicodeString( _dst_, _src_, _buf_) \
+ (_dst_)->Length = (_src_)->Length; \
+ (_dst_)->MaximumLength = (_src_)->Length + sizeof(WCHAR); \
+ (_dst_)->Buffer = (LPWSTR) (_buf_); \
+ RtlCopyMemory( (LPWSTR) (_buf_), (_src_)->Buffer, (_src_)->Length ); \
+ ((LPWSTR) (_buf_))[ (_src_)->Length / sizeof(WCHAR) ] = '\0';
+
+
+PLIST_ENTRY
+FindNamedEntry(
+ PLIST_ENTRY List,
+ PUNICODE_STRING Name
+ )
+/*++
+
+Routine Description:
+
+ This function returns the specified named entry pointer if the entry
+ doesn't exist it returns NULL.
+
+Arguments:
+
+ DCList - List to browse.
+
+ Name - name of the entry whose pointer will be returned.
+
+Return Value:
+
+ Domain entry pointer.
+
+--*/
+{
+ PLIST_ENTRY NextEntry;
+ PENTRY Entry;
+
+ for( NextEntry = List->Flink;
+ NextEntry != List; NextEntry = NextEntry->Flink ) {
+
+ Entry = (PENTRY) NextEntry;
+
+ if( RtlCompareUnicodeString( &Entry->Name, Name, TRUE ) == 0 ) {
+
+ //
+ // entry already there in the list. return the entry
+ // pointer.
+ //
+
+ return( NextEntry );
+ }
+ }
+
+ //
+ // entry not found.
+ //
+
+ return NULL;
+}
+
+
+PDOMAIN_ENTRY
+AddToDomainList(
+ PUNICODE_STRING DomainName
+ )
+/*++
+
+Routine Description:
+
+ Create a new domain entry and add to GlobalDomains list.
+
+ Enter with list locked.
+
+Arguments:
+
+ DomainName - name of the new domain added to the list.
+
+Return Value:
+
+ Pointer to the new domain structure.
+
+--*/
+{
+ PDOMAIN_ENTRY NewEntry;
+
+ //
+ // if this domain is already in the list, just return the entry
+ // pointer.
+ //
+
+ NewEntry = (PDOMAIN_ENTRY) FindNamedEntry(
+ &GlobalDomains,
+ DomainName );
+
+ if( NewEntry != NULL ) {
+
+ //
+ // Domain entry already in there.
+ //
+
+ return(NewEntry);
+ }
+
+
+ //
+ // Allocate space for the new entry
+ //
+
+ NewEntry = NetpMemoryAllocate(
+ sizeof(DOMAIN_ENTRY) +
+ DomainName->Length + sizeof(WCHAR) );
+
+ if( NewEntry == NULL ) {
+ NlMonDbgPrint(("AddToDomainList: can't allocate memory for "
+ "new domain entry.\n"));
+ return NULL;
+ }
+
+ NlMonpInitUnicodeString(
+ &NewEntry->Name, DomainName, (LPWSTR) (NewEntry + 1) );
+
+ InitializeListHead( &NewEntry->DCList );
+ InitializeListHead( &NewEntry->TrustedDomainList );
+ NewEntry->DomainState = DomainUnknown;
+ NewEntry->ReferenceCount = 0;
+ NewEntry->IsMonitoredDomain = FALSE;
+ NewEntry->UpdateFlags = 0;
+ NewEntry->ThreadHandle = NULL;
+ NewEntry->ThreadTerminateFlag = FALSE;
+ NewEntry->LastUpdateTime = 0;
+
+ //
+ // add it to domain list.
+ //
+
+ InsertTailList(&GlobalDomains, (PLIST_ENTRY)NewEntry);
+
+ return NewEntry;
+}
+
+PMONITORED_DOMAIN_ENTRY
+AddToMonitoredDomainList(
+ PUNICODE_STRING DomainName
+ )
+/*++
+
+Routine Description:
+
+ Create a new monitored domain entry and add it to the
+ GlobalDomainsMonitored list.
+
+ Enter with list locked.
+
+Arguments:
+
+ DomainName - name of the new domain added to the list.
+
+Return Value:
+
+ Pointer to the new domain structure.
+
+--*/
+{
+ PDOMAIN_ENTRY DomainEntry;
+ PMONITORED_DOMAIN_ENTRY NewEntry;
+
+ //
+ // if the domain is already monitored, just return entry pointer.
+ //
+
+ NewEntry = (PMONITORED_DOMAIN_ENTRY)
+ FindNamedEntry( &GlobalDomainsMonitored, DomainName );
+
+ if( NewEntry != NULL ) {
+ NewEntry->DeleteFlag = FALSE;
+ return NewEntry;
+ }
+
+ //
+ // allocate space for the new domain that will be monitored.
+ //
+
+ NewEntry = NetpMemoryAllocate(
+ sizeof(MONITORED_DOMAIN_ENTRY) +
+ DomainName->Length + sizeof(WCHAR) );
+
+ if( NewEntry == NULL ) {
+ NlMonDbgPrint(("AddToMonitoredDomainList: "
+ "can't allocate memory for "
+ "new domain entry.\n"));
+ return NULL;
+ }
+
+ NlMonpInitUnicodeString(
+ &NewEntry->Name, DomainName, (LPWSTR) (NewEntry + 1) );
+
+ DomainEntry = AddToDomainList( DomainName );
+
+ if( DomainEntry == NULL ) {
+
+ //
+ // can't create new domain entry.
+ //
+
+ NetpMemoryFree( NewEntry );
+ return(NULL);
+ }
+
+ NewEntry->DomainEntry = DomainEntry;
+ NewEntry->DeleteFlag = FALSE;
+ DomainEntry->ReferenceCount++;
+ DomainEntry->IsMonitoredDomain = TRUE;
+
+
+ //
+ // add it to GlobalDomainsMonitored list.
+ //
+
+ InsertTailList(&GlobalDomainsMonitored, (PLIST_ENTRY)NewEntry);
+
+ return(NewEntry);
+}
+
+PTRUSTED_DOMAIN_ENTRY
+AddToTrustedDomainList(
+ PLIST_ENTRY List,
+ PUNICODE_STRING DomainName
+ )
+/*++
+
+Routine Description:
+
+ Create a new monitored domain entry and add it to the
+ GlobalDomainsTrusted list.
+
+ Enter with list locked.
+
+Arguments:
+
+ List - List to be modified.
+
+ DomainName - name of the new domain added to the list.
+
+Return Value:
+
+ Pointer to the new domain structure.
+
+--*/
+{
+ PDOMAIN_ENTRY DomainEntry;
+ PTRUSTED_DOMAIN_ENTRY NewEntry;
+
+ //
+ // if the domain is already trusted, just return entry pointer.
+ //
+
+ NewEntry = (PTRUSTED_DOMAIN_ENTRY)
+ FindNamedEntry( List, DomainName );
+
+ if( NewEntry != NULL ) {
+ NewEntry->DeleteFlag = FALSE;
+ return NewEntry;
+ }
+
+ //
+ // allocate space for the new domain that will be monitored.
+ //
+
+ NewEntry = NetpMemoryAllocate(
+ sizeof(TRUSTED_DOMAIN_ENTRY) +
+ DomainName->Length + sizeof(WCHAR) );
+
+ if( NewEntry == NULL ) {
+ NlMonDbgPrint(("AddToTrustedDomainList: "
+ "can't allocate memory for "
+ "new domain entry.\n"));
+ return NULL;
+ }
+
+ NlMonpInitUnicodeString(
+ &NewEntry->Name, DomainName, (LPWSTR) (NewEntry + 1) );
+
+ DomainEntry = AddToDomainList( DomainName );
+
+ if( DomainEntry == NULL ) {
+ //
+ // can't create new domain entry.
+ //
+
+ NetpMemoryFree( NewEntry );
+ return(NULL);
+ }
+
+ NewEntry->DomainEntry = DomainEntry;
+ NewEntry->DeleteFlag = FALSE;
+ DomainEntry->ReferenceCount++;
+
+
+ //
+ // add it to list.
+ //
+
+ InsertTailList(List, (PLIST_ENTRY)NewEntry);
+
+ return(NewEntry);
+}
+
+BOOL
+InitDomainListW(
+ LPWSTR DomainList
+ )
+/*++
+
+Routine Description:
+
+ Parse comma separated domain list.
+
+Arguments:
+
+ DomainList - comma separate domain list.
+
+Return Value:
+
+ TRUE - if successfully parsed.
+ FALSE - iff the list is bad.
+
+--*/
+{
+ WCHAR DomainName[DNLEN + 1];
+ PWCHAR d;
+ PWCHAR p;
+ DWORD Len;
+
+
+ p = DomainList;
+
+ if( *p == L'\0' ) {
+ return(FALSE);
+ }
+
+ while (*p != L'\0') {
+
+ d = DomainName; // destination to next domain name.
+
+ while( (*p != L'\0') && (*p == L' ') ) {
+ p++; // skip leading blanks.
+ }
+
+ //
+ // read next domain name.
+ //
+
+ while( (*p != L'\0') && (*p != L',') ) {
+
+ if( d < DomainName + DNLEN ) {
+ *d++ = (WCHAR) (*p++);
+ }
+ }
+
+ if( *p != L'\0' ) {
+ p++; // skip comma.
+ }
+
+ //
+ // delete tail end blanks.
+ //
+
+ while ( (d > DomainName) && (*(d-1) == L' ') ) {
+ d--;
+ }
+
+ *d = L'\0';
+
+ if( Len = wcslen(DomainName) ) {
+
+ UNICODE_STRING UnicodeDomainName;
+
+ RtlInitUnicodeString( &UnicodeDomainName, DomainName );
+
+ LOCK_LISTS();
+ if( AddToMonitoredDomainList( &UnicodeDomainName ) == NULL ) {
+ UNLOCK_LISTS();
+ return(FALSE);
+ }
+ UNLOCK_LISTS();
+ }
+ }
+
+ if( IsListEmpty( &GlobalDomainsMonitored ) ) {
+ return(FALSE);
+ }
+
+ return(TRUE);
+
+}
+
+VOID
+ConvertServerAcctNameToServerName(
+ PUNICODE_STRING ServerAccountName
+ )
+/*++
+
+Routine Description:
+
+ Convert user account type name to server type name. By current convension
+ the servers account name has a "$" sign at the end. ie.
+
+ ServerAccountName = ServerName + "$"
+
+Arguments:
+
+ ServerAccountName - server account name.
+
+Return Value:
+
+ none.
+
+--*/
+{
+ //
+ // strip off "$" sign from account name.
+ //
+
+ ServerAccountName->Length -= sizeof(WCHAR);
+
+ return;
+}
+
+NTSTATUS
+QueryLsaInfo(
+ PUNICODE_STRING ServerName,
+ ACCESS_MASK DesiredAccess,
+ POLICY_INFORMATION_CLASS InformationClass,
+ PVOID *Info,
+ PLSA_HANDLE ReturnHandle //optional
+ )
+/*++
+
+Routine Description:
+
+ Open LSA database on the remote server, query requested info and
+ return lsa handle if asked otherwise close it.
+
+Arguments:
+
+ ServerName - Remote machine name.
+
+ DesiredAccess - Required access.
+
+ InformationClass - info class to be returned.
+
+ Info - pointer to a location where the return info buffer pointer is
+ placed. Caller should free this buffer.
+
+ ReturnHandle - if this is non-NULL pointer, LSA handle is returned
+ here. caller should close this handle after use.
+
+Return Value:
+
+ NTSTATUS.
+
+--*/
+{
+
+ NTSTATUS Status;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ LSA_HANDLE PolicyHandle;
+
+ *Info = NULL;
+
+ //
+ // Open Local policy.
+ //
+
+ INIT_OBJ_ATTR(ObjectAttributes);
+
+ Status = LsaOpenPolicy( ServerName,
+ &ObjectAttributes,
+ DesiredAccess,
+ &PolicyHandle );
+
+ if( !NT_SUCCESS(Status) ) {
+
+ NlMonDbgPrint(("QueryLsaInfo: "
+ "Cannot open LSA Policy database on server %wZ, %lx.\n",
+ ServerName, Status ));
+ return Status;
+ }
+
+ //
+ // read primary domain info.
+ //
+
+ Status = LsaQueryInformationPolicy(
+ PolicyHandle,
+ InformationClass,
+ Info );
+
+ if( !NT_SUCCESS(Status) ) {
+
+ NlMonDbgPrint(("QueryLsaInfo: "
+ "Can't read domain info from %wZ's database, %lx.\n",
+ ServerName, Status));
+
+ LsaClose(PolicyHandle);
+ return Status;
+ }
+
+ if( ReturnHandle != NULL ) {
+ *ReturnHandle = PolicyHandle;
+ }
+ else {
+ LsaClose(PolicyHandle);
+ }
+
+ return Status;
+}
+
+NET_API_STATUS
+IsValidNTDC(
+ PUNICODE_STRING ServerName,
+ PUNICODE_STRING DomainName
+ )
+/*++
+
+Routine Description:
+
+ This function determines that the given server is valid NT Domain
+ Controller on the given domain.
+
+Arguments:
+
+ ServerName - name of the server which has to be validated.
+
+ DomainName - name of the domain on which this DC is member.
+
+Return Value:
+
+ NET_API_STATUS code.
+
+--*/
+{
+ NET_API_STATUS NetStatus;
+
+ PWKSTA_INFO_100 WkstaInfo100 = NULL;
+ PSERVER_INFO_101 ServerInfo101 = NULL;
+
+ //
+ // ServerName should be non-null string.
+ //
+
+ if( (ServerName->Length <= 0) ||
+ (ServerName->Buffer == NULL) ||
+ (*ServerName->Buffer == L'\0') ) {
+
+ NetStatus = NERR_BadServiceName;
+ goto Cleanup;
+ }
+
+ //
+ // check to see that the server is still member of specified domain.
+ //
+
+ NetStatus = NetWkstaGetInfo(
+ ServerName->Buffer,
+ 100,
+ (LPBYTE *)&WkstaInfo100
+ );
+
+ if( (GlobalTerminateFlag) || (NetStatus != NERR_Success) ) {
+ goto Cleanup;
+ }
+
+ if ( I_NetNameCompare( NULL,
+ DomainName->Buffer,
+ WkstaInfo100->wki100_langroup,
+ NAMETYPE_DOMAIN,
+ 0L) != 0 ) {
+ goto Cleanup;
+ }
+
+
+ //
+ // check the server type is appropriate.
+ //
+
+ NetStatus = NetServerGetInfo(
+ ServerName->Buffer,
+ 101,
+ (LPBYTE *)&ServerInfo101
+ );
+
+ if( (GlobalTerminateFlag) || (NetStatus != NERR_Success) ) {
+ goto Cleanup;
+ }
+
+ if (!((ServerInfo101->sv101_type &
+ (SV_TYPE_DOMAIN_CTRL | SV_TYPE_DOMAIN_BAKCTRL)) &&
+ (ServerInfo101->sv101_type & SV_TYPE_NT)) ) {
+
+ //
+ // this server isn't NT Domain Controller.
+ //
+
+ NetStatus = ERROR_INVALID_DOMAIN_ROLE;
+ }
+
+
+Cleanup:
+
+ if ( ServerInfo101 != NULL ) {
+ NetApiBufferFree( ServerInfo101 );
+ }
+
+ if ( WkstaInfo100 != NULL ) {
+ NetApiBufferFree( WkstaInfo100 );
+ }
+
+ IF_DEBUG( VERBOSE ) {
+ NlMonDbgPrint(("IsValidNTDC: ServerName = %wZ, "
+ "DomainName = %wZ, and NetStatus = %ld .\n",
+ ServerName, DomainName, NetStatus ));
+ }
+
+ return NetStatus;
+}
+
+NET_API_STATUS
+ValidateDC(
+ PDC_ENTRY DCEntry,
+ PUNICODE_STRING DomainName
+ )
+/*++
+
+Routine Description:
+
+ This function validates the given DCEntry and sets various fields of
+ this structures.
+
+Arguments:
+
+ DCEntry - pointer to DC entry structure.
+
+ DomainName - name of the domain of which this DC is member.
+
+Return Value:
+
+ NET_API_STATUS code.
+
+--*/
+{
+ NET_API_STATUS NetStatus = NERR_Success;
+ PWKSTA_INFO_100 WkstaInfo100 = NULL;
+ PSERVER_INFO_101 ServerInfo101 = NULL;
+ PNETLOGON_INFO_1 NetlogonInfo1 = NULL;
+ LPWSTR InputDataPtr = NULL;
+
+ //
+ // lock this list while we update this entry status.
+ //
+
+ LOCK_LISTS();
+
+ //
+ // retry once in RETRY_COUNT calls.
+ //
+
+ if( ( DCEntry->State == DCOffLine ) ||
+ ( DCEntry->DCStatus != NERR_Success) ) {
+ if( DCEntry->RetryCount != 0 ) {
+ DCEntry->RetryCount = (DCEntry->RetryCount + 1) % RETRY_COUNT;
+ NetStatus = DCEntry->DCStatus;
+ goto Cleanup;
+ }
+ DCEntry->RetryCount = (DCEntry->RetryCount + 1) % RETRY_COUNT;
+ }
+
+
+ //
+ // check to see that the server is still on specified domain
+ //
+
+ UNLOCK_LISTS();
+ NetStatus = NetWkstaGetInfo(
+ DCEntry->DCName.Buffer,
+ 100,
+ (LPBYTE * )&WkstaInfo100 );
+ LOCK_LISTS();
+
+ if ( NetStatus != NERR_Success ) {
+
+ if( NetStatus == ERROR_BAD_NETPATH ) {
+ DCEntry->State = DCOffLine;
+ }
+ goto Cleanup;
+ }
+
+ if( GlobalTerminateFlag ) {
+ goto Cleanup;
+ }
+
+ if ( I_NetNameCompare( NULL,
+ DomainName->Buffer,
+ WkstaInfo100->wki100_langroup,
+ NAMETYPE_DOMAIN,
+ 0L) != 0 ) {
+
+ NetStatus = ERROR_INVALID_DOMAIN_STATE;
+ goto Cleanup;
+ }
+
+
+ //
+ // check the server type is appropriate.
+ //
+
+ UNLOCK_LISTS();
+ NetStatus = NetServerGetInfo(
+ DCEntry->DCName.Buffer,
+ 101,
+ (LPBYTE *)&ServerInfo101 );
+ LOCK_LISTS();
+
+ if( (GlobalTerminateFlag) || (NetStatus != NERR_Success) ) {
+ goto Cleanup;
+ }
+
+ if( ServerInfo101->sv101_type & SV_TYPE_NT ) {
+
+ if ( ServerInfo101->sv101_type & SV_TYPE_DOMAIN_CTRL ) {
+ DCEntry->Type = NTPDC;
+ } else if ( ServerInfo101->sv101_type & SV_TYPE_DOMAIN_BAKCTRL ) {
+ DCEntry->Type = NTBDC;
+ } else {
+ NetStatus = ERROR_INVALID_DOMAIN_ROLE;
+ goto Cleanup;
+ }
+ }
+ else {
+ if ( ServerInfo101->sv101_type & SV_TYPE_DOMAIN_BAKCTRL ) {
+ DCEntry->Type = LMBDC;
+ }
+ else {
+ NetStatus = ERROR_INVALID_DOMAIN_ROLE;
+ goto Cleanup;
+ }
+ }
+
+ if( DCEntry->Type != LMBDC ) {
+
+ //
+ // Query netlogon to determine replication status and connection
+ // status to its PDC.
+ //
+
+ UNLOCK_LISTS();
+ NetStatus = I_NetLogonControl2(
+ DCEntry->DCName.Buffer,
+ NETLOGON_CONTROL_QUERY,
+ 1,
+ (LPBYTE)&InputDataPtr,
+ (LPBYTE *)&NetlogonInfo1 );
+ LOCK_LISTS();
+
+ if( (GlobalTerminateFlag) || (NetStatus != NERR_Success) ) {
+ goto Cleanup;
+ }
+
+ DCEntry->ReplicationStatus = NetlogonInfo1->netlog1_flags;
+ DCEntry->PDCLinkStatus = NetlogonInfo1->netlog1_pdc_connection_status;
+ }
+
+ DCEntry->State = DCOnLine;
+Cleanup:
+
+ if ( NetlogonInfo1 != NULL ) {
+ NetApiBufferFree( NetlogonInfo1 );
+ }
+
+ if ( ServerInfo101 != NULL ) {
+ NetApiBufferFree( ServerInfo101 );
+ }
+
+ if ( WkstaInfo100 != NULL ) {
+ NetApiBufferFree( WkstaInfo100 );
+ }
+
+ if ( NetStatus != NERR_Success ) {
+
+ //
+ // set other status to unknown.
+ //
+
+ DCEntry->ReplicationStatus = UNKNOWN_REPLICATION_STATE;
+ DCEntry->PDCLinkStatus = ERROR_BAD_NETPATH;
+ }
+
+ DCEntry->DCStatus = NetStatus;
+ UNLOCK_LISTS();
+
+ IF_DEBUG( VERBOSE ) {
+ NlMonDbgPrint(("ValidateDC: ServerName = %wZ, "
+ "DomainName = %wZ, and NetStatus = %ld.\n",
+ &DCEntry->DCName, DomainName, NetStatus ));
+ }
+
+ return NetStatus;
+}
+
+PDC_ENTRY
+CreateDCEntry (
+ PUNICODE_STRING DCName,
+ DC_TYPE Type
+ )
+/*++
+
+Routine Description:
+
+ Create a DC Entry;
+
+Arguments:
+
+ DCName - entry name in unc form.
+
+ Type - DC type.
+
+Return Value:
+
+ Entry pointer. If it can't allocate memory, it returns NULL pointer.
+
+--*/
+{
+
+ PDC_ENTRY DCEntry;
+
+ DCEntry = NetpMemoryAllocate(
+ sizeof(DC_ENTRY) +
+ DCName->Length + sizeof(WCHAR) );
+
+ if( DCEntry != NULL ) {
+
+ NlMonpInitUnicodeString(
+ &DCEntry->DCName, DCName, (LPWSTR) (DCEntry + 1) );
+
+ DCEntry->State = DCOffLine;
+ DCEntry->Type = Type;
+ DCEntry->DCStatus = ERROR_BAD_NETPATH;
+ DCEntry->ReplicationStatus = UNKNOWN_REPLICATION_STATE; //unknown state.
+ DCEntry->PDCLinkStatus = ERROR_BAD_NETPATH;
+ DCEntry->DeleteFlag = FALSE;
+ DCEntry->RetryCount = 0;
+ InitializeListHead( &DCEntry->TrustedDCs );
+ DCEntry->TDCLinkState = FALSE;
+ }
+
+ return( DCEntry );
+}
+
+NTSTATUS
+UpdateDCListFromNTServerAccounts(
+ SAM_HANDLE SamHandle,
+ PLIST_ENTRY DCList
+ )
+/*++
+
+Routine Description:
+
+ Merge NT server accounts defined in the database to DCList.
+
+Arguments:
+
+ SamHandle : Handle SAM database.
+
+ DCList : linked list of DCs.
+
+Return Value:
+
+ NTSTATUS code.
+
+--*/
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ PDOMAIN_DISPLAY_MACHINE MachineInformation = NULL;
+ DWORD SamIndex = 0;
+ DWORD EntriesRead;
+ ULONG TotalBytesAvailable;
+ ULONG BytesReturned;
+
+ DWORD i;
+
+ PDC_ENTRY DCEntry;
+
+ //
+ // enumerate NT Server accounts
+ //
+
+ do {
+ //
+ // Get the list of machine accounts from SAM
+ //
+
+ Status = SamQueryDisplayInformation(
+ SamHandle,
+ DomainDisplayMachine,
+ SamIndex,
+ MACHINES_PER_PASS,
+ 0xFFFFFFFF,
+ &TotalBytesAvailable,
+ &BytesReturned,
+ &EntriesRead,
+ &MachineInformation );
+
+ if ( (Status != STATUS_NO_MORE_ENTRIES) &&
+ (!NT_SUCCESS(Status)) ) {
+ NlMonDbgPrint(("UpdateDCListFromNTServerAccounts: "
+ "SamrQueryDisplayInformation returned, %lx.\n",
+ Status));
+ goto Cleanup;
+ }
+
+ if( GlobalTerminateFlag ) {
+ goto Cleanup;
+ }
+
+ //
+ // Set up for the next call to Sam.
+ //
+
+ if ( Status == STATUS_MORE_ENTRIES ) {
+ SamIndex = MachineInformation[EntriesRead-1].Index + 1;
+ }
+
+
+ //
+ // Loop though the list of machine accounts finding the Server accounts.
+ //
+
+ for ( i = 0; i < EntriesRead; i++ ) {
+
+ //
+ // Ensure the machine account is a server account.
+ //
+
+ if ( MachineInformation[i].AccountControl &
+ USER_SERVER_TRUST_ACCOUNT ) {
+
+ WCHAR UncComputerName[CNLEN + 3];
+ UNICODE_STRING UnicodeComputerName;
+
+ ConvertServerAcctNameToServerName(
+ &MachineInformation[i].Machine ); // in place conversion.
+
+ //
+ // form unicode unc computer name.
+ //
+
+ UncComputerName[0] = UncComputerName[1] = L'\\';
+
+ RtlCopyMemory(
+ UncComputerName + 2,
+ MachineInformation[i].Machine.Buffer,
+ MachineInformation[i].Machine.Length );
+
+ UncComputerName[
+ MachineInformation[i].Machine.Length /
+ sizeof(WCHAR) + 2] = L'\0';
+
+ RtlInitUnicodeString( &UnicodeComputerName, UncComputerName);
+
+ IF_DEBUG( VERBOSE ) {
+ NlMonDbgPrint(("UpdateDCListFromNTServerAccounts: "
+ "NT Server %wZ is found on database.\n",
+ &UnicodeComputerName ));
+ }
+
+ DCEntry = (PDC_ENTRY) FindNamedEntry(
+ DCList,
+ &UnicodeComputerName );
+
+ if( DCEntry != NULL ) {
+
+ DCEntry->DeleteFlag = FALSE;
+ }
+ else {
+
+ DCEntry = CreateDCEntry( &UnicodeComputerName, NTBDC );
+
+ //
+ // silently ignore NULL entry.
+ //
+
+ if( DCEntry != NULL ) {
+
+ //
+ // add to list.
+ //
+
+ LOCK_LISTS();
+ InsertTailList(DCList, (PLIST_ENTRY)DCEntry);
+ UNLOCK_LISTS();
+ }
+ }
+ }
+ }
+
+ //
+ // free up the memory used up memory.
+ //
+
+ if( MachineInformation != NULL ) {
+ (VOID) SamFreeMemory( MachineInformation );
+ MachineInformation = NULL;
+ }
+
+ } while ( Status == STATUS_MORE_ENTRIES );
+
+Cleanup:
+
+ if( MachineInformation != NULL ) {
+ (VOID) SamFreeMemory( MachineInformation );
+ }
+
+ return Status;
+}
+
+NTSTATUS
+UpdateDCListFromLMServerAccounts(
+ SAM_HANDLE SamHandle,
+ PLIST_ENTRY DCList
+ )
+/*++
+
+Routine Description:
+
+ Read LM server accounts from SAM "Servers" global group and update
+ the DC list using this accounts.
+
+Arguments:
+
+ SamHandle : Handle SAM database.
+
+ DCList : linked list of DCs.
+
+Return Value:
+
+ NTSTATUS code.
+
+--*/
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ UNICODE_STRING NameString;
+ PSID_NAME_USE NameUse = NULL;
+ PULONG RelativeId = NULL;
+ SAM_HANDLE GroupHandle = NULL;
+
+ PULONG MemberAttributes = NULL;
+ PULONG MemberIds = NULL;
+ ULONG MemberCount;
+ PUNICODE_STRING MemberNames = NULL;
+ PSID_NAME_USE MemberNameUse = NULL;
+
+ PDC_ENTRY DCEntry;
+ DWORD i;
+
+ //
+ // Get RID of SERVERS group.
+ //
+
+ RtlInitUnicodeString( &NameString, SERVERS_GROUP );
+ Status = SamLookupNamesInDomain(
+ SamHandle,
+ 1,
+ &NameString,
+ &RelativeId,
+ &NameUse );
+
+ if ( !NT_SUCCESS(Status) ) {
+ NlMonDbgPrint(( "UpdateDCListFromLMServerAccounts: SamLookupNamesInDomain "
+ " failed to transulate %wZ %lx.\n",
+ &NameString,
+ Status ));
+ goto Cleanup;
+ }
+
+ if ( *NameUse != SidTypeGroup ) {
+ NlMonDbgPrint(( "UpdateDCListFromLMServerAccounts: %wZ is not "
+ "SidTypeGroup, %ld.\n",
+ &NameString,
+ *NameUse ));
+ goto Cleanup;
+ }
+
+ //
+ // open group object.
+ //
+
+ Status = SamOpenGroup(
+ SamHandle,
+ GROUP_LIST_MEMBERS,
+ *RelativeId,
+ &GroupHandle);
+
+ if ( !NT_SUCCESS(Status) ) {
+ NlMonDbgPrint(( "UpdateDCListFromLMServerAccounts: SamOpenGroup of %wZ "
+ "failed, %lx.\n",
+ &NameString,
+ Status ));
+ goto Cleanup;
+ }
+
+ //
+ // get group members.
+ //
+
+ Status = SamGetMembersInGroup(
+ GroupHandle,
+ &MemberIds,
+ &MemberAttributes,
+ &MemberCount );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ NlMonDbgPrint(("UpdateDCListFromLMServerAccounts: SamGetMembersInGroup "
+ "returned %lx.\n", Status ));
+ goto Cleanup;
+ }
+
+ if( GlobalTerminateFlag ) {
+ goto Cleanup;
+ }
+
+ if( MemberCount == 0) {
+ //
+ // nothing more to do.
+ //
+ goto Cleanup;
+ }
+
+ //
+ // trasulate members IDs.
+ //
+
+ Status = SamLookupIdsInDomain(
+ SamHandle,
+ MemberCount,
+ MemberIds,
+ &MemberNames,
+ &MemberNameUse );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ NlMonDbgPrint((
+ "UpdateDCListFromLMServerAccounts: SamLookupIdsInDomain "
+ "returned %lx.\n", Status ));
+ goto Cleanup;
+ }
+
+ if( GlobalTerminateFlag ) {
+ goto Cleanup;
+ }
+
+ //
+ // add user members to list.
+ //
+
+ for (i = 0 ; i < MemberCount; i++) {
+
+ WCHAR UncComputerName[CNLEN + 3];
+ UNICODE_STRING UnicodeComputerName;
+
+ if ( MemberNameUse[i] != SidTypeUser ) {
+ continue;
+ }
+
+ //
+ // form unicode unc computer name.
+ //
+
+ UncComputerName[0] = UncComputerName[1] = L'\\';
+
+ RtlCopyMemory(
+ UncComputerName + 2,
+ MemberNames[i].Buffer,
+ MemberNames[i].Length );
+
+ UncComputerName[MemberNames[i].Length / sizeof(WCHAR) + 2] = L'\0';
+ RtlInitUnicodeString( &UnicodeComputerName, UncComputerName);
+
+ IF_DEBUG( VERBOSE ) {
+ NlMonDbgPrint(("UpdateDCListFromLMServerAccounts: "
+ "LM Server %wZ is found on database.\n",
+ &UnicodeComputerName ));
+ }
+
+ DCEntry = (PDC_ENTRY) FindNamedEntry(
+ DCList,
+ &UnicodeComputerName );
+
+ if( DCEntry != NULL ) {
+
+ DCEntry->DeleteFlag = FALSE;
+ }
+ else {
+
+ //
+ // create new entry and add to list
+ //
+
+ DCEntry = CreateDCEntry( &UnicodeComputerName, LMBDC );
+
+ //
+ // silently ignore NULL entries.
+ //
+
+ if( DCEntry != NULL ) {
+
+ //
+ // add to list.
+ //
+
+ LOCK_LISTS();
+ InsertTailList(DCList, (PLIST_ENTRY)DCEntry);
+ UNLOCK_LISTS();
+ }
+
+ }
+ }
+
+Cleanup:
+
+ //
+ // Free up local resources.
+ //
+
+ if( NameUse != NULL ) {
+ (VOID) SamFreeMemory( NameUse );
+ }
+
+ if( RelativeId != NULL ) {
+ (VOID) SamFreeMemory( RelativeId );
+ }
+ if( MemberAttributes != NULL ) {
+ (VOID) SamFreeMemory( MemberAttributes );
+ }
+
+ if( MemberIds != NULL ) {
+ (VOID) SamFreeMemory( MemberIds );
+ }
+ if( MemberNames != NULL ) {
+ (VOID) SamFreeMemory( MemberNames );
+ }
+
+ if( MemberNameUse != NULL ) {
+ (VOID) SamFreeMemory( MemberNameUse );
+ }
+
+ if( GroupHandle != NULL ) {
+ SamCloseHandle( GroupHandle );
+ }
+
+ return Status;
+}
+
+VOID
+UpdateDCListFromDatabase(
+ PUNICODE_STRING DomainName,
+ PLIST_ENTRY DCList
+ )
+/*++
+
+Routine Description:
+
+ Update the List of DCs on a given domain using database entries.
+
+Arguments:
+
+ DomainName : name of the domain whose DCList to be updated.
+
+ DCList : linked list of DCs. DCList must be non-empty.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ NTSTATUS Status;
+
+ PUNICODE_STRING ServerName;
+
+ PPOLICY_ACCOUNT_DOMAIN_INFO AccountDomainInfo = NULL;
+ SAM_HANDLE SamConnectHandle = NULL;
+ SAM_HANDLE SamHandle = NULL;
+
+ PLIST_ENTRY NextEntry;
+ PDC_ENTRY DCEntry;
+
+ //
+ // pick a dc from current list.
+ //
+
+ ServerName = NULL;
+ for( NextEntry = DCList->Flink;
+ NextEntry != DCList; NextEntry = NextEntry->Flink ) {
+
+ DCEntry = (PDC_ENTRY) NextEntry;
+
+ if( GlobalTerminateFlag ) {
+ break;
+ }
+
+ if( IsValidNTDC( &DCEntry->DCName, DomainName ) == NERR_Success ) {
+ ServerName = &DCEntry->DCName;
+ break;
+ }
+ }
+
+ if( ServerName == NULL ) {
+ NlMonDbgPrint(( "UpdateDCListFromDatabase: "
+ "No DC is valid in %wZ domain list.\n", DomainName));
+ return;
+ }
+
+ IF_DEBUG( UPDATE ) {
+ NlMonDbgPrint(( "UpdateDCListFromDatabase: "
+ "picked %wZ to get DCList from its database.\n",
+ ServerName));
+ }
+
+ //
+ // Get Domain SID info from policy database.
+ //
+
+ Status = QueryLsaInfo(
+ ServerName,
+ POLICY_VIEW_LOCAL_INFORMATION,
+ PolicyAccountDomainInformation,
+ (PVOID *)&AccountDomainInfo,
+ NULL ) ;
+
+ if( !NT_SUCCESS( Status ) ) {
+ NlMonDbgPrint(("UpdateDCListFromDatabase: QueryLsaInfo returned %lx.\n",
+ Status ));
+ goto Cleanup;
+ }
+
+ //
+ // Connect to SAM.
+ //
+
+ Status = SamConnect(
+ ServerName,
+ &SamConnectHandle,
+ SAM_SERVER_LOOKUP_DOMAIN,
+ NULL); // object attributes.
+
+
+ if( !NT_SUCCESS( Status ) ) {
+ NlMonDbgPrint(("UpdateDCListFromDatabase: SamConnect returned %lx.\n",
+ Status ));
+ SamConnectHandle = NULL;
+ goto Cleanup;
+ }
+
+ //
+ // Open Account SAM domain.
+ //
+
+ Status = SamOpenDomain(
+ SamConnectHandle,
+ DOMAIN_LIST_ACCOUNTS | DOMAIN_LOOKUP,
+ AccountDomainInfo->DomainSid,
+ &SamHandle );
+
+ if( !NT_SUCCESS( Status ) ) {
+ NlMonDbgPrint(("UpdateDCListFromDatabase: SamOpenDomain returned "
+ "%lx.\n", Status ));
+ SamHandle = NULL;
+ goto Cleanup;
+ }
+
+ //
+ // mark all entries in the current list to be deleted.
+ //
+
+ for( NextEntry = DCList->Flink;
+ NextEntry != DCList; NextEntry = NextEntry->Flink ) {
+
+ DCEntry = (PDC_ENTRY) NextEntry;
+ DCEntry->DeleteFlag = TRUE;
+ }
+
+ if( GlobalTerminateFlag ) {
+ goto Cleanup;
+ }
+
+ //
+ // enumerate NT Server accounts
+ //
+
+ Status = UpdateDCListFromNTServerAccounts( SamHandle, DCList );
+
+ if( !NT_SUCCESS( Status ) ) {
+ NlMonDbgPrint(("UpdateDCListFromDatabase: "
+ "UpdateDCListFromNTServerAccounts returned "
+ "%lx.\n", Status ));
+ goto Cleanup;
+ }
+
+ if( GlobalTerminateFlag ) {
+ goto Cleanup;
+ }
+
+ //
+ // Now enumerate down level DCs.
+ //
+
+ Status = UpdateDCListFromLMServerAccounts( SamHandle, DCList );
+
+ if( !NT_SUCCESS( Status ) ) {
+ NlMonDbgPrint(("UpdateDCListFromDatabase: "
+ "UpdateDCListFromLMServerAccounts returned "
+ "%lx.\n", Status ));
+ goto Cleanup;
+ }
+
+Cleanup :
+
+ //
+ // if we have successfully updated the list then
+ // delete entries that are marked deleted otherwise
+ // mark them undelete.
+ //
+
+ LOCK_LISTS();
+ for( NextEntry = DCList->Flink;
+ NextEntry != DCList; ) {
+
+ DCEntry = (PDC_ENTRY) NextEntry;
+ NextEntry = NextEntry->Flink;
+
+ if( DCEntry->DeleteFlag ) {
+
+ if( Status == STATUS_SUCCESS ) {
+ DCEntry->DeleteFlag = FALSE;
+ }
+ else {
+ RemoveEntryList( (PLIST_ENTRY)DCEntry );
+ NetpMemoryFree( DCEntry );
+ }
+ }
+ }
+ UNLOCK_LISTS();
+
+ if( AccountDomainInfo != NULL ) {
+ (VOID) LsaFreeMemory( AccountDomainInfo );
+ }
+
+ if( SamHandle != NULL ) {
+ (VOID) SamCloseHandle ( SamHandle );
+ }
+
+ if( SamConnectHandle != NULL ) {
+ (VOID) SamCloseHandle ( SamConnectHandle );
+ }
+
+ return;
+}
+
+VOID
+UpdateDCListByServerEnum(
+ PUNICODE_STRING DomainName,
+ PLIST_ENTRY DCList
+ )
+/*++
+
+Routine Description:
+
+ Update the List of DCs on a given domain by calling NetServerEnum.
+ If NetServerEnum failes, it calls NetGetDCName to atleast determine
+ PDC of the domain.
+
+Arguments:
+
+ DomainName : name of the domain whose DCList to be updated.
+
+ DCList : linked list of DCs.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ NET_API_STATUS NetStatus;
+
+ PSERVER_INFO_101 ServerInfo101 = NULL;
+ DWORD EntriesRead;
+ DWORD TotalEntries;
+
+ PDC_ENTRY DCEntry;
+ DWORD i;
+
+ NetStatus = NetServerEnum( NULL,
+ 101,
+ (LPBYTE *) &ServerInfo101,
+ (ULONG)(-1), // Prefmaxlen
+ &EntriesRead,
+ &TotalEntries,
+ SV_TYPE_DOMAIN_CTRL | SV_TYPE_DOMAIN_BAKCTRL,
+ DomainName->Buffer,
+ NULL ); // Resume Handle
+
+ if( NetStatus != NERR_Success ) {
+
+ if( NetStatus != ERROR_NO_BROWSER_SERVERS_FOUND ) {
+ NlMonDbgPrint(("UpdateDCListByServerEnum: "
+ "NetServerEnum called with domain name %wZ Failed, "
+ "%ld.\n", DomainName, NetStatus));
+ }
+
+ ServerInfo101 = NULL;
+ EntriesRead = 0;
+
+ }
+
+ if( GlobalTerminateFlag ) {
+ goto Cleanup;
+ }
+
+ //
+ // update DC List using ServerEnumList.
+ //
+
+ for( i = 0; i < EntriesRead; i++ ) {
+
+ WCHAR UncComputerName[CNLEN + 3];
+ UNICODE_STRING UnicodeComputerName;
+ DC_TYPE ServerType;
+
+ UncComputerName[0] = UncComputerName[1] = L'\\';
+ wcscpy( UncComputerName + 2, ServerInfo101[i].sv101_name );
+ RtlInitUnicodeString(&UnicodeComputerName, UncComputerName);
+
+ if ( (ServerInfo101[i].sv101_type & SV_TYPE_NT) ) {
+ if ( ServerInfo101[i].sv101_type & SV_TYPE_DOMAIN_CTRL ) {
+ ServerType = NTPDC;
+ }
+ else {
+ ServerType = NTBDC;
+ }
+ }
+ else {
+ if ( ServerInfo101[i].sv101_type & SV_TYPE_DOMAIN_BAKCTRL ) {
+ ServerType = LMBDC;
+ }
+ else {
+ NlMonDbgPrint(("UpdateDCListByServerEnum: "
+ "NetServerEnum called with domain name %wZ "
+ "returned LM PDC %wZ.\n",
+ DomainName, &UnicodeComputerName ));
+ ServerType = LMBDC;
+ }
+ }
+
+ IF_DEBUG( VERBOSE ) {
+ NlMonDbgPrint(("UpdateDCListByServerEnum: "
+ "Server %wZ found in NetServerEnumList.\n",
+ &UnicodeComputerName ));
+ }
+
+ DCEntry = (PDC_ENTRY) FindNamedEntry( DCList, &UnicodeComputerName );
+
+ if( DCEntry != NULL ) {
+ DCEntry->Type = ServerType;
+ DCEntry->State = DCOnLine;
+ }
+ else {
+
+ //
+ // create new entry and add to list
+ //
+
+ DCEntry = CreateDCEntry( &UnicodeComputerName, ServerType );
+
+ //
+ // silently ignore NULL entries.
+ //
+
+ if( DCEntry != NULL ) {
+
+ //
+ // add to list.
+ //
+
+ DCEntry->State = DCOnLine;
+ LOCK_LISTS();
+ InsertTailList(DCList, (PLIST_ENTRY)DCEntry);
+ UNLOCK_LISTS();
+ }
+ }
+ }
+
+Cleanup:
+
+ if( ServerInfo101 != NULL ) {
+ NetApiBufferFree( ServerInfo101 );
+ }
+
+ return;
+}
+
+VOID
+UpdateTrustList(
+ PDOMAIN_ENTRY Domain
+ )
+/*++
+
+Routine Description:
+
+ This function creates/updates the trusted domains list.
+
+Arguments:
+
+ Domain : pointer domain structure.
+
+
+Return Value:
+
+ None.
+
+--*/
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ DWORD i;
+ PUNICODE_STRING ServerName = NULL;
+ PLIST_ENTRY ListHead;
+ PLIST_ENTRY NextEntry;
+ PDC_ENTRY DCEntry;
+
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ LSA_HANDLE PolicyHandle = NULL;
+ LSA_ENUMERATION_HANDLE EnumContext;
+ PLSA_TRUST_INFORMATION TrustedDomainList = NULL;
+ DWORD Entries;
+
+ PTRUSTED_DOMAIN_ENTRY TDEntry;
+
+ IF_DEBUG( TRUST ) {
+ NlMonDbgPrint(("UpdateTrustList: called for domain %wZ.\n",
+ &Domain->Name ));
+ }
+
+ //
+ // pick up a DC to query the trusted domain list.
+ //
+
+ ListHead = &Domain->DCList;
+
+ //
+ // first try possibly good DCs.
+ //
+
+ for( NextEntry = ListHead->Flink;
+ NextEntry != ListHead; NextEntry = NextEntry->Flink ) {
+
+ DCEntry = (PDC_ENTRY) NextEntry;
+
+ if( (DCEntry->State == DCOnLine) &&
+ ( DCEntry->DCStatus == NERR_Success ) ) {
+
+ if( IsValidNTDC( &DCEntry->DCName, &Domain->Name ) == NERR_Success ) {
+
+ ServerName = &DCEntry->DCName;
+ break;
+ }
+ }
+ }
+
+ if( ServerName == NULL ) {
+
+ //
+ // now try all.
+ //
+
+ for( NextEntry = ListHead->Flink;
+ NextEntry != ListHead; NextEntry = NextEntry->Flink ) {
+
+ DCEntry = (PDC_ENTRY) NextEntry;
+
+ if( IsValidNTDC( &DCEntry->DCName, &Domain->Name ) == NERR_Success ) {
+
+ ServerName = &DCEntry->DCName;
+ break;
+ }
+ }
+ }
+
+
+ if( ServerName == NULL ) {
+
+ IF_DEBUG( TRUST ) {
+ NlMonDbgPrint(( "UpdateTrustList: "
+ "No DC is valid in %wZ domain list.\n",
+ &Domain->Name));
+ }
+
+ goto Cleanup;
+ }
+
+ IF_DEBUG( TRUST ) {
+ NlMonDbgPrint(("UpdateTrustList: for domain %wZ picked %wZ to query "
+ "trusted domains.\n",
+ &Domain->Name, ServerName ));
+ }
+
+ //
+ // Open policy database.
+ //
+
+ INIT_OBJ_ATTR(ObjectAttributes);
+
+ Status = LsaOpenPolicy( ServerName,
+ &ObjectAttributes,
+ POLICY_VIEW_LOCAL_INFORMATION,
+ &PolicyHandle );
+
+ if( !NT_SUCCESS(Status) ) {
+
+ NlMonDbgPrint(("UpdateTrustList: "
+ "Cannot open LSA Policy on server %wZ, %lx.\n",
+ ServerName, Status ));
+ goto Cleanup;
+ }
+
+ //
+ // enum trusted domains.
+ //
+
+ EnumContext = 0;
+
+ Status = LsaEnumerateTrustedDomains(
+ PolicyHandle,
+ &EnumContext,
+ &TrustedDomainList,
+ (ULONG)-1,
+ &Entries );
+
+ if( !NT_SUCCESS(Status) &&
+ (Status != STATUS_NO_MORE_ENTRIES) ) {
+
+ NlMonDbgPrint(("UpdateTrustList: "
+ "Cannot Enumerate trust list on server %wZ, %lx.\n",
+ ServerName, Status ));
+ goto Cleanup;
+ }
+
+ if( GlobalTerminateFlag ) {
+ goto Cleanup;
+ }
+
+ //
+ // update trust list.
+ //
+
+ ListHead = &Domain->TrustedDomainList;
+
+ for ( i = 0; i < Entries; i++) {
+
+ TDEntry = (PTRUSTED_DOMAIN_ENTRY)
+ FindNamedEntry(
+ ListHead,
+ &TrustedDomainList[i].Name );
+
+ if( TDEntry == NULL ) {
+
+ IF_DEBUG( VERBOSE ) {
+ NlMonDbgPrint(("UpdateTrustList: "
+ "%wZ is added to %wZ domain trust list.\n",
+ &TrustedDomainList[i].Name,
+ &Domain->Name ));
+ }
+
+ //
+ // add this entry to list.
+ //
+
+ LOCK_LISTS();
+ if( AddToTrustedDomainList(
+ ListHead,
+ &TrustedDomainList[i].Name ) == NULL ) {
+
+ UNLOCK_LISTS();
+ NlMonDbgPrint(("UpdateTrustList: can't allocate memory for "
+ "new trusted domain entry.\n" ));
+ goto Cleanup;
+ }
+ UNLOCK_LISTS();
+ }
+ }
+
+Cleanup :
+
+ if( TrustedDomainList != NULL ) {
+ LsaFreeMemory( TrustedDomainList );
+ }
+
+ if( PolicyHandle != NULL ) {
+ LsaClose( PolicyHandle );
+ }
+
+ return;
+}
+
+VOID
+UpdateTrustConnectionList(
+ PDOMAIN_ENTRY Domain
+ )
+/*++
+
+Routine Description:
+
+ This function creates/updates trust connection entries of all DCs.
+
+Arguments:
+
+ Domain : pointer domain structure.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PLIST_ENTRY DCList;
+ PLIST_ENTRY NextDCEntry;
+ PDC_ENTRY DCEntry;
+
+ PLIST_ENTRY TrustConnectList;
+ PTD_LINK TrustConnectEntry;
+
+ PLIST_ENTRY TrustList;
+ PLIST_ENTRY NextTrustEntry;
+ PTRUSTED_DOMAIN_ENTRY TrustEntry;
+
+ //
+ // for each DC on this domain.
+ //
+
+ DCList = &Domain->DCList;
+ TrustList = &Domain->TrustedDomainList;
+
+ for( NextDCEntry = DCList->Flink;
+ NextDCEntry != DCList; NextDCEntry = NextDCEntry->Flink ) {
+
+ DCEntry = (PDC_ENTRY) NextDCEntry;
+
+ //
+ // do this update only for running DC
+ // and it should be NT DC.
+ //
+
+ if( (DCEntry->State != DCOnLine) ||
+ (DCEntry->Type == LMBDC) ){
+ continue;
+ }
+
+
+ TrustConnectList = &DCEntry->TrustedDCs;
+
+ //
+ // for each trusted domain, get connection status.
+ //
+
+ for( NextTrustEntry = TrustList->Flink;
+ NextTrustEntry != TrustList;
+ NextTrustEntry = NextTrustEntry->Flink ) {
+
+ TrustEntry = (PTRUSTED_DOMAIN_ENTRY) NextTrustEntry;
+
+ //
+ // search in the current list.
+ //
+
+ TrustConnectEntry = (PTD_LINK) FindNamedEntry(
+ TrustConnectList,
+ &TrustEntry->Name );
+
+ if( TrustConnectEntry == NULL ) {
+
+ PTD_LINK NewTrustConnectEntry;
+
+ //
+ // create new entry.
+ //
+
+ NewTrustConnectEntry =
+ NetpMemoryAllocate(
+ sizeof(TD_LINK) +
+ TrustEntry->Name.Length + sizeof(WCHAR) +
+ (CNLEN + 3) * sizeof(WCHAR)
+ // max computer name space + '\\' + '\0'
+ );
+
+ if( NewTrustConnectEntry == NULL ) {
+ NlMonDbgPrint(("UpdateTrustConnectionList: can't allocate "
+ "memory for new connect entry.\n"));
+ return;
+ }
+
+ InitializeListHead(&NewTrustConnectEntry->NextEntry);
+
+ NlMonpInitUnicodeString(
+ &NewTrustConnectEntry->TDName,
+ &TrustEntry->Name,
+ (LPWSTR)(NewTrustConnectEntry + 1) );
+
+ NewTrustConnectEntry->DCName.Length = 0;
+ NewTrustConnectEntry->DCName.MaximumLength =
+ (CNLEN + 3) * sizeof(WCHAR);
+ NewTrustConnectEntry->DCName.Buffer = (WCHAR *)
+ ((PCHAR)(NewTrustConnectEntry->TDName.Buffer) +
+ NewTrustConnectEntry->TDName.MaximumLength);
+
+ NewTrustConnectEntry->SecureChannelStatus = ERROR_BAD_NETPATH;
+ NewTrustConnectEntry->DeleteFlag = FALSE;
+
+ IF_DEBUG( VERBOSE ) {
+ NlMonDbgPrint(("UpdateTrustConnectionList: "
+ "Trust Connection entry for DC %wZ of "
+ "domain %wZ added.\n",
+ &DCEntry->DCName,
+ &TrustEntry->Name ));
+
+ }
+
+ //
+ // add to trust connect list.
+ //
+
+ LOCK_LISTS();
+ InsertTailList(
+ TrustConnectList,
+ (PLIST_ENTRY)NewTrustConnectEntry);
+ UNLOCK_LISTS();
+
+ }
+ }
+ }
+
+ return;
+}
+
+VOID
+ValidateTrustConnectionList(
+ PDC_ENTRY DCEntry,
+ BOOL ValidateTrustedDCs
+ )
+/*++
+
+Routine Description:
+
+ This function determines trust DC for each trusted domain to which
+ the given DC having connection.
+
+Arguments:
+
+ DCEntry : pointer DC entry structure.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PLIST_ENTRY TrustConnectList;
+ PLIST_ENTRY NextTrustConnectEntry;
+ PTD_LINK TrustConnectEntry;
+ BOOL TDCLinkState = TRUE;
+
+ //
+ // do this update only for running DC
+ // and it should be NT DC.
+ //
+
+ if( (DCEntry->State != DCOnLine) ||
+ (DCEntry->Type == LMBDC) ) {
+ return;
+ }
+
+ IF_DEBUG( TRUST ) {
+ NlMonDbgPrint(("ValidateTrustConnectionList: "
+ "Trust Connections for the %wZ DC are validated.\n",
+ &DCEntry->DCName ));
+ }
+
+ TrustConnectList = &DCEntry->TrustedDCs;
+
+ for( NextTrustConnectEntry = TrustConnectList->Flink;
+ NextTrustConnectEntry != TrustConnectList;
+ NextTrustConnectEntry = NextTrustConnectEntry->Flink ) {
+
+ NET_API_STATUS NetStatus;
+ PNETLOGON_INFO_2 NetlogonInfo2 = NULL;
+
+ TrustConnectEntry = (PTD_LINK) NextTrustConnectEntry;
+
+ if( GlobalTerminateFlag ) {
+ break;
+ }
+
+ //
+ // find trusted DC for this domain and its secure channel
+ // status.
+ //
+
+ NetStatus = I_NetLogonControl2(
+ DCEntry->DCName.Buffer,
+ NETLOGON_CONTROL_TC_QUERY,
+ 2,
+ (LPBYTE)&TrustConnectEntry->TDName.Buffer,
+ (LPBYTE *)&NetlogonInfo2 );
+
+
+ if( NetStatus != NERR_Success ) {
+
+ IF_DEBUG( TRUST ) {
+ NlMonDbgPrint(("ValidateTrustConnectionList: "
+ "I_NetLogonControl2 (%wZ) call to query trust "
+ "channel status of %wZ domain failed, (%ld).\n",
+ &DCEntry->DCName,
+ &TrustConnectEntry->TDName,
+ NetStatus ));
+ }
+
+ //
+ // Cleanup the previous DC Name.
+ //
+
+ LOCK_LISTS();
+ TrustConnectEntry->DCName.Length = 0;
+ *TrustConnectEntry->DCName.Buffer = L'\0';
+
+ TrustConnectEntry->SecureChannelStatus = NetStatus;
+ UNLOCK_LISTS();
+
+ TDCLinkState = FALSE;
+ continue;
+ }
+
+ IF_DEBUG( VERBOSE ) {
+ NlMonDbgPrint(("ValidateTrustConnectionList: "
+ "I_NetLogonControl2 (%wZ) call to query trust "
+ "channel status of %wZ domain returned : "
+ "TDCName = %ws, TCStatus = %ld.\n",
+ &DCEntry->DCName,
+ &TrustConnectEntry->TDName,
+ NetlogonInfo2->netlog2_trusted_dc_name,
+ NetlogonInfo2->netlog2_tc_connection_status ));
+ }
+
+ //
+ // copy this DC name.
+ //
+
+ LOCK_LISTS();
+ TrustConnectEntry->DCName.Length =
+ wcslen( NetlogonInfo2->netlog2_trusted_dc_name ) * sizeof(WCHAR);
+
+ RtlCopyMemory(
+ TrustConnectEntry->DCName.Buffer,
+ NetlogonInfo2->netlog2_trusted_dc_name,
+ TrustConnectEntry->DCName.Length + sizeof(WCHAR) );
+ // copy terminator also
+
+ TrustConnectEntry->SecureChannelStatus =
+ NetlogonInfo2->netlog2_tc_connection_status;
+
+ if( TrustConnectEntry->SecureChannelStatus != NERR_Success ) {
+ TDCLinkState = FALSE;
+ }
+
+ //
+ // update DC status info.
+ //
+
+ DCEntry->ReplicationStatus = NetlogonInfo2->netlog2_flags;
+ DCEntry->PDCLinkStatus = NetlogonInfo2->netlog2_pdc_connection_status;
+ UNLOCK_LISTS();
+
+ //
+ // free up API memory.
+ //
+
+ NetApiBufferFree( NetlogonInfo2 );
+ NetlogonInfo2 = NULL;
+
+ //
+ // validate the this DC. Only non-null server and successful
+ // connection.
+ //
+
+ if( (TrustConnectEntry->SecureChannelStatus == NERR_Success) &&
+ (*TrustConnectEntry->DCName.Buffer != L'\0') ) {
+
+ if( ValidateTrustedDCs ) {
+
+
+ NetStatus = IsValidNTDC(
+ &TrustConnectEntry->DCName,
+ &TrustConnectEntry->TDName );
+
+ if( NetStatus != NERR_Success ) {
+
+ IF_DEBUG( TRUST ) {
+
+ NlMonDbgPrint(("ValidateTrustConnectionList: "
+ "%wZ's trust DC %wZ is invalid for "
+ "domain %wz.\n",
+ &DCEntry->DCName,
+ &TrustConnectEntry->DCName,
+ &TrustConnectEntry->TDName ));
+ }
+
+ //
+ // hack, hack, hack ...
+ //
+ // For foreign trusted domains, the above check will
+ // return ERROR_LOGON_FAILURE. Just ignore this
+ // error for now. When the domain wide credential is
+ // implemeted this problem will be cured.
+ //
+
+
+ if( NetStatus != ERROR_LOGON_FAILURE ) {
+ TrustConnectEntry->SecureChannelStatus = NetStatus;
+ TDCLinkState = FALSE;
+ }
+ }
+ }
+ }
+ }
+
+ //
+ // finally set the state of the trusted dc link for this DC.
+ //
+
+ DCEntry->TDCLinkState = TDCLinkState;
+}
+
+VOID
+UpdateDomainState(
+ PDOMAIN_ENTRY DomainEntry
+ )
+/*++
+
+Routine Description:
+
+ Update the status of the given domain. The status is determined as
+ below :
+
+ 1. if all DCs status and the Trusted channel status are success then
+ DomainState is set to DomainSuccess.
+
+ 2. if all online DCs status are success and if any of the secure
+ channel status is non-success or any of the BDC is offline then the
+ Domainstate is set to DomainProblem.
+
+ 3. if any of the domain status is non-success or the PDC is
+ offline then the DomainState is set to DomainSick.
+
+ 4. if none of the DC is online, then DomainState is set to
+ DomainDown.
+
+Arguments:
+
+ DomainEntry : pointer to domain structure.
+
+Return Value:
+
+ NONE.
+
+--*/
+{
+ PLIST_ENTRY DCList;
+ PLIST_ENTRY NextEntry;
+ PDC_ENTRY DCEntry;
+ BOOL DomainDownFlag = TRUE;
+ BOOL PDCOnLine = FALSE;
+
+ DCList = &(DomainEntry->DCList);
+
+ for( NextEntry = DCList->Flink;
+ NextEntry != DCList; NextEntry = NextEntry->Flink ) {
+
+ DCEntry = (PDC_ENTRY) NextEntry;
+
+ if( DCEntry->State == DCOnLine ) {
+
+ DomainDownFlag = FALSE;
+
+ if( DCEntry->DCStatus != ERROR_SUCCESS ) {
+ DomainEntry->DomainState = DomainSick;
+ return;
+ }
+
+ //
+ // if this is PDC, mark PDC is heathy.
+ //
+
+ if( DCEntry->Type == NTPDC ) {
+ PDCOnLine = TRUE;
+ }
+ }
+ }
+
+ if( DomainDownFlag ) {
+ DomainEntry->DomainState = DomainDown;
+ return;
+ }
+
+ //
+ // if PDC is not online ..
+ //
+
+ if( !PDCOnLine ) {
+ DomainEntry->DomainState = DomainSick;
+ return;
+ }
+
+ //
+ // now determine the secure channel status
+ //
+
+ DCList = &(DomainEntry->DCList);
+ for( NextEntry = DCList->Flink;
+ NextEntry != DCList; NextEntry = NextEntry->Flink ) {
+
+ DCEntry = (PDC_ENTRY) NextEntry;
+
+ //
+ // if a DC is offline ..
+ //
+
+ if( DCEntry->State == DCOffLine ) {
+ DomainEntry->DomainState = DomainProblem;
+ return;
+ }
+
+ if( DCEntry->Type == LMBDC ) {
+
+ //
+ // LMBDC does not have trusted secure channel.
+ //
+
+ continue;
+ }
+
+ //
+ // examine the PDC secure channel status.
+ //
+
+ if( (DCEntry->PDCLinkStatus != ERROR_SUCCESS) ||
+ (DCEntry->TDCLinkState == FALSE) ) {
+ DomainEntry->DomainState = DomainProblem;
+ return;
+ }
+ }
+
+ DomainEntry->DomainState = DomainSuccess;
+ return;
+}
+
+BOOL
+IsDomainUpdateThreadRunning(
+ HANDLE *ThreadHandle
+ )
+/*++
+
+Routine Description:
+
+ Test if the Domain update thread is running
+
+ Enter with GlobalDomainUpdateThreadCritSect locked.
+
+Arguments:
+
+ ThreadHandle - pointer to thread handle.
+
+Return Value:
+
+ TRUE - The domain update thread is running
+
+ FALSE - The domain update thread is not running.
+
+--*/
+{
+ DWORD WaitStatus;
+
+ //
+ // Determine if the Update thread is already running.
+ //
+
+ if ( *ThreadHandle != NULL ) {
+
+ //
+ // Time out immediately if the thread is still running.
+ //
+
+ WaitStatus = WaitForSingleObject(
+ *ThreadHandle,
+ 0 );
+
+ if ( WaitStatus == WAIT_TIMEOUT ) {
+ return TRUE;
+
+ } else if ( WaitStatus == 0 ) {
+ CloseHandle( *ThreadHandle );
+ *ThreadHandle = NULL;
+ return FALSE;
+
+ } else {
+ NlMonDbgPrint((
+ "IsDomainUpdateThreadRunning: "
+ "Cannot WaitFor Domain Update thread: %ld\n",
+ WaitStatus ));
+ return TRUE;
+ }
+
+ }
+
+ return FALSE;
+}
+
+VOID
+StopDomainUpdateThread(
+ HANDLE *ThreadHandle,
+ BOOL *ThreadTerminateFlag
+ )
+/*++
+
+Routine Description:
+
+ Stops the domain update thread if it is running and waits for it to
+ stop.
+
+ Enter with GlobalDomainUpdateThreadCritSect locked.
+
+Arguments:
+
+ ThreadHandle - pointer to thread handle.
+
+ ThreadTerminateFlag - pointer to thread terminate flag.
+
+Return Value:
+
+ NONE
+
+--*/
+{
+ //
+ // Ask the domain update thread to stop running.
+ //
+
+ *ThreadTerminateFlag = TRUE;
+
+ //
+ // Determine if the domain update thread is already running.
+ //
+
+ if ( *ThreadHandle != NULL ) {
+
+ //
+ // We've asked the thread to stop. It should do so soon.
+ // Wait for it to stop.
+ //
+
+ DWORD WaitStatus;
+
+ WaitStatus = WaitForSingleObject( *ThreadHandle,
+ 5*60*1000 ); // 5 minutes
+
+ if ( WaitStatus != 0 ) {
+ if ( WaitStatus == WAIT_TIMEOUT ) {
+ NlMonDbgPrint(("NlStopDomainUpdateThread"
+ "WaitForSingleObject 5-minute timeout.\n" ));
+
+ //
+ // kill the thread.
+ //
+
+ TerminateThread( *ThreadHandle, (DWORD)-1 );
+
+ } else {
+ NlMonDbgPrint(("NlStopDomainUpdateThread"
+ "WaitForSingleObject error: %ld\n",
+ WaitStatus));
+ }
+ }
+
+ CloseHandle( *ThreadHandle );
+ *ThreadHandle = NULL;
+ }
+
+ *ThreadTerminateFlag = FALSE;
+
+ return;
+}
+
+BOOL
+StartDomainUpdateThread(
+ PDOMAIN_ENTRY DomainEntry,
+ DWORD UpdateFlags
+ )
+/*++
+
+Routine Description:
+
+ Start the Domain Update thread if it is not already running.
+
+ NOTE: LOCK_LISTS() should be locked when this function is called.
+ since we do update the domain structure here.
+
+Arguments:
+
+ DomainEntry - Pointer to domain structure.
+
+Return Value:
+ None
+
+--*/
+{
+ DWORD LocalThreadHandle;
+
+ //
+ // If the domain update thread is already running, do nothing.
+ //
+
+ EnterCriticalSection( &GlobalDomainUpdateThreadCritSect );
+ if ( IsDomainUpdateThreadRunning( &DomainEntry->ThreadHandle ) ) {
+ LeaveCriticalSection( &GlobalDomainUpdateThreadCritSect );
+ return FALSE;
+ }
+
+ //
+ // if the last update was done within the GlobalUpdateTimeMSec
+ // msecs then ignore
+
+ //
+ // Initialize the domain update parameters
+ //
+
+ DomainEntry->ThreadTerminateFlag = FALSE;
+ DomainEntry->UpdateFlags = UpdateFlags;
+ DomainEntry->DomainState = DomainUnknown;
+
+ DomainEntry->ThreadHandle = CreateThread(
+ NULL, // No security attributes
+ THREAD_STACKSIZE,
+ (LPTHREAD_START_ROUTINE)
+ DomainUpdateThread,
+ (LPVOID)DomainEntry,
+ 0, // No special creation flags
+ &LocalThreadHandle );
+
+ if ( DomainEntry->ThreadHandle == NULL ) {
+
+ NlMonDbgPrint(("StartDomainUpdateThread"
+ "Can't create domain update Thread %lu\n",
+ GetLastError() ));
+
+ LeaveCriticalSection( &GlobalDomainUpdateThreadCritSect );
+ return FALSE;
+ }
+
+ LeaveCriticalSection( &GlobalDomainUpdateThreadCritSect );
+ return TRUE;
+
+}
+
+VOID
+DomainUpdateThread(
+ PDOMAIN_ENTRY DomainEntry
+ )
+/*++
+
+Routine Description:
+
+ Update and validate status of DCs and Trusted DCs of the specifed
+ Monitored domain.
+
+Arguments:
+
+ DomainEntry : Pointer to the domain entry to be updated.
+
+Return Value:
+
+ NONE.
+
+--*/
+{
+ PLIST_ENTRY DCList;
+ PLIST_ENTRY NextEntry;
+ PDC_ENTRY DCEntry;
+ DWORD UpdateFlags = DomainEntry->UpdateFlags;
+ BOOL ThreadTerminate = DomainEntry->ThreadTerminateFlag;
+
+ //
+ // monitored domain first.
+ //
+
+ if (DomainEntry->IsMonitoredDomain ) {
+
+ //
+ // update lists.
+ //
+
+ if( UpdateFlags & UPDATE_DCS_FROM_SERVER_ENUM ) {
+ UpdateDCListByServerEnum(
+ &DomainEntry->Name,
+ &DomainEntry->DCList );
+ }
+
+ //
+ // if we are asked to terminate, do so.
+ //
+
+ if( GlobalTerminateFlag || ThreadTerminate ) {
+ return;
+ }
+
+ if( UpdateFlags & UPDATE_DCS_FROM_DATABASE ) {
+ UpdateDCListFromDatabase(
+ &DomainEntry->Name,
+ &DomainEntry->DCList );
+ }
+
+ //
+ // if we are asked to terminate, do so.
+ //
+
+ if( GlobalTerminateFlag || ThreadTerminate) {
+ return;
+ }
+
+ if( UpdateFlags & UPDATE_TRUST_DOMAINS_FROM_DATABASE ) {
+ UpdateTrustList( DomainEntry );
+
+ //
+ // if we are asked to terminate, do so.
+ //
+
+ if( GlobalTerminateFlag || ThreadTerminate ) {
+ return;
+ }
+
+ UpdateTrustConnectionList( DomainEntry );
+
+ //
+ // start update threads for new trusted domain introduced
+ // here.
+ //
+
+ if( GlobalMonitorTrust ) {
+ UpdateAndValidateLists( UpdateFlags, FALSE );
+ }
+ }
+
+ //
+ // validate list content.
+ //
+
+ if( UpdateFlags & VALIDATE_DCS ) {
+
+ DCList = &(DomainEntry->DCList);
+
+ for( NextEntry = DCList->Flink;
+ NextEntry != DCList; NextEntry = NextEntry->Flink ) {
+
+ DCEntry = (PDC_ENTRY) NextEntry;
+
+ //
+ // if we are asked to terminate, do so.
+ //
+
+ if( GlobalTerminateFlag || ThreadTerminate ) {
+ return;
+ }
+
+ (VOID) ValidateDC( DCEntry, &DomainEntry->Name );
+
+ //
+ // update trust connection status.
+ //
+
+ ValidateTrustConnectionList(
+ DCEntry,
+ (BOOL)(UpdateFlags & VALIDATE_TRUST_CONNECTIONS) );
+ }
+ }
+ }
+
+ //
+ // trusted domain.
+ //
+
+ else {
+
+ if( UpdateFlags & UPDATE_TRUST_DCS_FROM_SERVER_ENUM ) {
+ UpdateDCListByServerEnum(
+ &DomainEntry->Name,
+ &DomainEntry->DCList );
+ }
+
+ //
+ // if we are asked to terminate, do so.
+ //
+
+ if( GlobalTerminateFlag || ThreadTerminate ) {
+ return;
+ }
+
+ if( UpdateFlags & UPDATE_TRUST_DCS_FROM_DATABASE ) {
+ UpdateDCListFromDatabase(
+ &DomainEntry->Name,
+ &DomainEntry->DCList );
+ }
+
+ //
+ // if we are asked to terminate, do so.
+ //
+
+ if( GlobalTerminateFlag || ThreadTerminate ) {
+ return;
+ }
+
+ //
+ // validate list content.
+ //
+
+ if( UpdateFlags & VALIDATE_TRUST_DCS ) {
+
+ DCList = &(DomainEntry->DCList);
+
+ for( NextEntry = DCList->Flink;
+ NextEntry != DCList; NextEntry = NextEntry->Flink ) {
+
+ DCEntry = (PDC_ENTRY) NextEntry;
+
+ //
+ // if we are asked to terminate, do so.
+ //
+
+ if( GlobalTerminateFlag || ThreadTerminate ) {
+ return;
+ }
+
+ (VOID) ValidateDC( DCEntry, &DomainEntry->Name );
+ }
+ }
+ }
+
+ //
+ // update domain status.
+ //
+
+ UpdateDomainState( DomainEntry );
+
+ //
+ // Set GloballUpdateEvent to enable the display thread to
+ // display the update.
+ //
+
+ if ( !SetEvent( GlobalUpdateEvent ) ) {
+ DWORD WinError;
+
+ WinError = GetLastError();
+ NlMonDbgPrint(("UpdateAndValidateDomain: Cannot set "
+ "GlobalUpdateEvent: %lu\n",
+ WinError ));
+ }
+
+ //
+ // finally set the last update time.
+ //
+
+
+ DomainEntry->LastUpdateTime = GetCurrentTime();
+}
+
+VOID
+UpdateAndValidateLists(
+ DWORD UpdateFlags,
+ BOOL ForceFlag
+ )
+/*++
+
+Routine Description:
+
+ Update and validate all lists.
+
+Arguments:
+
+ UpdateFlags : This bit mapped flags indicate what need to be update
+ during this pass.
+
+Return Value:
+
+ NONE.
+
+--*/
+{
+ PLIST_ENTRY DomainList;
+ PLIST_ENTRY NextDomainEntry;
+ PMONITORED_DOMAIN_ENTRY DomainMonEntry;
+ PDOMAIN_ENTRY DomainEntry;
+ DWORD CurrentTime;
+
+ //
+ // delete if any GlobalDomainsMonitored entry marked to be deleted.
+ //
+
+ LOCK_LISTS();
+ DomainList = &GlobalDomainsMonitored;
+ for( NextDomainEntry = DomainList->Flink;
+ NextDomainEntry != DomainList; ) {
+
+ PLIST_ENTRY TDomainList;
+ PLIST_ENTRY NextTDomainEntry;
+ PTRUSTED_DOMAIN_ENTRY TDomainEntry;
+
+ DomainMonEntry = (PMONITORED_DOMAIN_ENTRY)NextDomainEntry;
+ NextDomainEntry = NextDomainEntry->Flink;
+
+ if( DomainMonEntry->DeleteFlag ) {
+
+ //
+ // unreference from GlobalDomains.
+ //
+
+ DomainMonEntry->DomainEntry->ReferenceCount--;
+ NetpAssert(DomainMonEntry->DomainEntry->ReferenceCount >= 0);
+
+ //
+ // unreference the trusted domains from GlobalDomains list.
+ //
+
+ TDomainList = &DomainMonEntry->DomainEntry->TrustedDomainList;
+
+ for( NextTDomainEntry = TDomainList->Flink;
+ NextTDomainEntry != TDomainList;
+ NextTDomainEntry = NextTDomainEntry->Flink ) {
+
+ TDomainEntry = (PTRUSTED_DOMAIN_ENTRY)NextTDomainEntry;
+
+ TDomainEntry->DomainEntry->ReferenceCount--;
+ NetpAssert(TDomainEntry->DomainEntry->ReferenceCount >= 0);
+ }
+
+ RemoveEntryList( (PLIST_ENTRY)DomainMonEntry );
+ NetpMemoryFree( DomainMonEntry );
+ }
+ }
+
+ //
+ // remove GlobalDomains entries that are not referenced anymore.
+ //
+
+ DomainList = &GlobalDomains;
+ for( NextDomainEntry = DomainList->Flink;
+ NextDomainEntry != DomainList; ) {
+
+ DomainEntry = (PDOMAIN_ENTRY)NextDomainEntry;
+ NextDomainEntry = NextDomainEntry->Flink;
+
+ if( DomainEntry->ReferenceCount == 0 ) {
+
+ //
+ // if the DomainUpdateThread is running, postpone this
+ // delete.
+ //
+
+ EnterCriticalSection( &GlobalDomainUpdateThreadCritSect );
+ if ( !IsDomainUpdateThreadRunning( &DomainEntry->ThreadHandle ) ) {
+ RemoveEntryList( (PLIST_ENTRY)DomainEntry );
+ CleanupDomainEntry( DomainEntry );
+ }
+ LeaveCriticalSection( &GlobalDomainUpdateThreadCritSect );
+ }
+ }
+
+ CurrentTime = GetCurrentTime();
+
+ //
+ // if we are monitoring trusted domains also then update
+ // GlobalDomains otherwise update just GlobalDomainsMonitored.
+ //
+
+ if( GlobalMonitorTrust ) {
+ DomainList = &GlobalDomains;
+ }
+ else {
+ DomainList = &GlobalDomainsMonitored;
+ }
+
+ for( NextDomainEntry = DomainList->Flink;
+ NextDomainEntry != DomainList;
+ NextDomainEntry = NextDomainEntry->Flink ) {
+
+ if( GlobalMonitorTrust ) {
+ DomainEntry = (PDOMAIN_ENTRY)NextDomainEntry;
+ }
+ else {
+ DomainEntry =
+ ((PMONITORED_DOMAIN_ENTRY)NextDomainEntry)->DomainEntry;
+ }
+
+ //
+ // if we are asked to terminate, do so.
+ //
+
+ if( GlobalTerminateFlag ) {
+ break;
+ }
+
+ //
+ // if the last update was done within the last
+ // GlobalUpdateTimeMSec msecs then don't start UpdateThread.
+ // However start the UpdateThread during startup and when
+ // forced.
+ //
+ // Also takecare of wrap around
+ //
+
+ if( (ForceFlag == TRUE) ||
+ (DomainEntry->LastUpdateTime == 0) ||
+ (CurrentTime - DomainEntry->LastUpdateTime > GlobalUpdateTimeMSec ) ) {
+
+ StartDomainUpdateThread( DomainEntry, UpdateFlags );
+ }
+ }
+
+ UNLOCK_LISTS();
+}
+
+DWORD
+InitGlobals(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ Initialize all global variables.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ GlobalTrace = CONST_GLOBALTRACE;
+ GlobalMonitorTrust = CONST_GLOBALMONITORTRUST;
+ GlobalUpdateTimeMSec = CONST_GLOBALUPDATETIME * 60000;
+
+ InitializeListHead( &GlobalDomains );
+ InitializeListHead( &GlobalDomainsMonitored );
+ InitializeCriticalSection( &GlobalListCritSect );
+ InitializeCriticalSection( &GlobalDomainUpdateThreadCritSect );
+
+ GlobalTerminateFlag = FALSE;
+
+ GlobalTerminateEvent = CreateEvent( NULL, // No security attributes
+ TRUE, // Must be manual reset
+ FALSE, // Initially not signaled
+ NULL );// No name
+
+ if( GlobalTerminateEvent == NULL ) {
+
+ DWORD WinError;
+
+ WinError = GetLastError();
+ NlMonDbgPrint(("Can't create GlobalTerminateEvent %lu.\n", WinError));
+
+ return( WinError );
+ }
+
+ GlobalRefreshEvent = CreateEvent( NULL, // No security attributes
+ FALSE, // Must be auto reset
+ FALSE, // Initially not signaled
+ NULL );// No name
+
+ if( GlobalRefreshEvent == NULL ) {
+
+ DWORD WinError;
+
+ WinError = GetLastError();
+ NlMonDbgPrint(("Can't create GlobalRefreshEvent %lu.\n", WinError));
+
+ CloseHandle( GlobalTerminateEvent );
+ return( WinError );
+ }
+
+ GlobalRefreshDoneEvent = CreateEvent( NULL, // No security attributes
+ FALSE, // Must be auto reset
+ FALSE, // Initially not signaled
+ NULL );// No name
+
+ if( GlobalRefreshDoneEvent == NULL ) {
+
+ DWORD WinError;
+
+ WinError = GetLastError();
+ NlMonDbgPrint(("Can't create GlobalRefresheDoneEvent %lu.\n", WinError));
+
+ CloseHandle( GlobalTerminateEvent );
+ CloseHandle( GlobalRefreshEvent );
+ return( WinError );
+ }
+
+ GlobalUpdateEvent = CreateEvent( NULL, // No security attributes
+ FALSE, // Must be auto reset
+ FALSE, // Initially not signaled
+ NULL ); // No name
+
+ if( GlobalUpdateEvent == NULL ) {
+
+ DWORD WinError;
+
+ WinError = GetLastError();
+ NlMonDbgPrint(("Can't create GlobalUpdateEvent %lu.\n", WinError));
+
+ CloseHandle( GlobalTerminateEvent );
+ CloseHandle( GlobalRefreshEvent );
+ CloseHandle( GlobalRefreshDoneEvent );
+ return( WinError );
+ }
+
+ GlobalInitialized = TRUE;
+ return(ERROR_SUCCESS);
+}
+
+VOID
+FreeList(
+ PLIST_ENTRY List
+ )
+/*++
+
+Routine Description:
+
+ Freeup entries in a given lists.
+
+Arguments:
+
+ List : pointer to a list.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PLIST_ENTRY NextEntry;
+
+ while ( !IsListEmpty(List) ) {
+ NextEntry = RemoveHeadList(List);
+ NetpMemoryFree( NextEntry );
+ }
+}
+
+VOID
+CleanupDomainEntry(
+ PDOMAIN_ENTRY DomainEntry
+ )
+/*++
+
+Routine Description:
+
+ Frees a domain entry and its lists.
+
+Arguments:
+
+ DomainEntry : pointer to domain structure.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PLIST_ENTRY DCList;
+ PLIST_ENTRY NextEntry;
+ PDC_ENTRY DCEntry;
+
+
+ //
+ // first free trusted dc lists
+ //
+
+ DCList = &(DomainEntry->DCList);
+ for( NextEntry = DCList->Flink;
+ NextEntry != DCList; NextEntry = NextEntry->Flink ) {
+
+ DCEntry = (PDC_ENTRY) NextEntry;
+ FreeList( &DCEntry->TrustedDCs );
+ }
+
+ //
+ // now free the DC list.
+ //
+
+ FreeList( &DomainEntry->DCList );
+
+ //
+ // now free trusted domain list.
+ //
+
+ FreeList( &DomainEntry->TrustedDomainList );
+}
+
+VOID
+CleanupLists(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ Freeup all lists.
+
+Arguments:
+
+ none.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PDOMAIN_ENTRY DomainEntry;
+ PLIST_ENTRY NextDomainEntry;
+
+
+ LOCK_LISTS();
+
+ while ( !IsListEmpty(&GlobalDomains) ) {
+
+ NextDomainEntry = RemoveHeadList(&GlobalDomains);
+ DomainEntry = (PDOMAIN_ENTRY)NextDomainEntry;
+ CleanupDomainEntry( DomainEntry );
+ }
+
+ FreeList(&GlobalDomainsMonitored);
+ UNLOCK_LISTS();
+}
+
+
+VOID
+WorkerThread(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This thread updates the lists and validate the list content.
+
+Arguments:
+
+ none.
+
+Return Value:
+
+ None.
+
+--*/
+{
+#define COUNT_UPDATE_FROM_DATABASE 50
+#define COUNT_VALIDATE_TRUST_CONNECTIONS 5
+
+#define WAIT_COUNT 2
+#define REFRESH_EVENT 0
+#define TERMINATE_EVENT 1
+
+ DWORD LoopCount;
+ DWORD WaitStatus;
+ HANDLE WaitHandles[ WAIT_COUNT ];
+ DWORD UpdateFlags;
+
+ //
+ // perpare wait event array.
+ //
+
+ WaitHandles[REFRESH_EVENT] = GlobalRefreshEvent;
+ WaitHandles[TERMINATE_EVENT] = GlobalTerminateEvent;
+
+ LoopCount = 1;
+ for( ;; ) {
+
+ //
+ // wait for one of the following event to happen :
+ //
+ // 1. GlobalRefreshEvent
+ // 2. GlobalTerminateEvent.
+ // 3. Timeout in GlobalUpdateTimeMSec
+ //
+
+ WaitStatus = WaitForMultipleObjects(
+ WAIT_COUNT,
+ WaitHandles,
+ FALSE, // Wait for ANY handle
+ GlobalUpdateTimeMSec );
+
+ switch ( WaitStatus ) {
+ case WAIT_TIMEOUT: // timeout
+ //
+ // determine what to update.
+ //
+
+ if( LoopCount % COUNT_UPDATE_FROM_DATABASE ) {
+ UpdateFlags = UPDATE_FROM_DATABASE;
+ }
+ else if( LoopCount % COUNT_VALIDATE_TRUST_CONNECTIONS) {
+ UpdateFlags = UPDATE_TRUST_CONNECTIONS_STATUS;
+ }
+ else {
+ UpdateFlags = STANDARD_UPDATE;
+ }
+
+ UpdateAndValidateLists( UpdateFlags, FALSE );
+
+ //
+ // also indicate to the UI that the domain state has been
+ // changed.
+ //
+
+ if ( !SetEvent( GlobalUpdateEvent ) ) {
+ DWORD WinError;
+
+ WinError = GetLastError();
+ NlMonDbgPrint(("WokerThread: Cannot set "
+ "GlobalUpdateEvent: %lu\n",
+ WinError ));
+ }
+
+ LoopCount++;
+ break;
+
+ case REFRESH_EVENT:
+
+ UpdateAndValidateLists( UPDATE_ALL, TRUE );
+
+ if ( !SetEvent( GlobalRefreshDoneEvent ) ) {
+ DWORD WinError;
+
+ WinError = GetLastError();
+ NlMonDbgPrint(("WokerThread: Cannot set "
+ "GlobalRefreshDoneEvent: %lu\n",
+ WinError ));
+ }
+
+ //
+ // also indicate to the UI that the domain state has been
+ // changed.
+ //
+
+ if ( !SetEvent( GlobalUpdateEvent ) ) {
+ DWORD WinError;
+
+ WinError = GetLastError();
+ NlMonDbgPrint(("WokerThread: Cannot set "
+ "GlobalUpdateEvent: %lu\n",
+ WinError ));
+ }
+
+ break;
+
+ case TERMINATE_EVENT:
+ //
+ // done.
+ //
+
+ goto Cleanup;
+ break;
+
+ default:
+ NlMonDbgPrint((
+ "WorkerThread: WaitForMultipleObjects error: %ld\n",
+ WaitStatus));
+ break;
+ }
+
+ }
+
+Cleanup:
+ return;
+}
+
diff --git a/private/net/svcdlls/logonsrv/monitor/nlmon.c b/private/net/svcdlls/logonsrv/monitor/nlmon.c
new file mode 100644
index 000000000..71226d7cb
--- /dev/null
+++ b/private/net/svcdlls/logonsrv/monitor/nlmon.c
@@ -0,0 +1,922 @@
+/*--
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ nlmon.c
+
+Abstract:
+
+ Trusted Domain monitor program.
+
+Author:
+
+ 10-May-1993 (madana)
+
+Environment:
+
+ User mode only.
+ Contains NT-specific code.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+
+--*/
+
+#include <nlmon.h>
+
+VOID
+PrintUsage()
+/*++
+
+Routine Description:
+
+ Print usage of this apps.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ printf( "Usage: nlmon "
+ DOMAIN_PARAM "<DomainList> "
+ MONTRUST_PARAM "<Yes/No> "
+ UPDATE_PARAM "<Mins> "
+ DEBUG_PARAM "<HexValue> "
+ "\n" );
+
+ printf(
+ "\n"
+ " " DOMAIN_PARAM "<DomainList> - Specify comma separated domain list to monitor, default is Primary/Account Domain \n"
+ " " MONTRUST_PARAM "<Yes/No> - Specify to monitor trusted domains also, default is NO \n"
+ " " UPDATE_PARAM "<Mins> - Specify refresh time \n"
+ " " DEBUG_PARAM "<HexValue> - debug out level \n"
+ "\n" );
+}
+
+VOID
+DisplayDCEntryStatus(
+ PDC_ENTRY DCEntry
+)
+/*++
+
+Routine Description:
+
+ Display the content of DC Entry. List must be locked when this
+ function is called.
+
+Arguments:
+
+ DCEntry - pointer dc structure.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ LPWSTR DCStateStr, DCTypeStr, DCReplStatusStr;
+
+ if( (DCEntry->State == DCOffLine) && (DCEntry->RetryCount == 1) ) {
+ return; // print status only when status is updated.
+ }
+
+ switch( DCEntry->State ) {
+ case DCOnLine:
+ DCStateStr = DCSTATE_ONLINE;
+ break;
+ case DCOffLine:
+ DCStateStr = DCSTATE_OFFLINE;
+ break;
+ default:
+ DCStateStr = UNKNOWN;
+ }
+
+ switch( DCEntry->Type ) {
+ case NTPDC:
+ DCTypeStr = TYPE_NTPDC;
+ break;
+ case NTBDC:
+ DCTypeStr = TYPE_NTBDC;
+ break;
+ case LMBDC:
+ DCTypeStr = TYPE_LMBDC;
+ break;
+ default:
+ DCTypeStr = UNKNOWN;
+ break;
+ }
+
+ if ( DCEntry->ReplicationStatus == 0 ) {
+ DCReplStatusStr = REPL_STATE_SYNC;
+ }
+ else if ( DCEntry->ReplicationStatus & NETLOGON_REPLICATION_IN_PROGRESS) {
+ DCReplStatusStr = REPL_STATE_PROGRESS;
+ }
+ else if ( DCEntry->ReplicationStatus & NETLOGON_REPLICATION_NEEDED ) {
+ DCReplStatusStr = REPL_STATE_REQ;
+ } else {
+ DCReplStatusStr = UNKNOWN;
+ }
+
+ printf("%-15wZ %-10ws %-10ws %-10ld %-10ws %-10ld\n",
+ &DCEntry->DCName, DCStateStr, DCTypeStr,
+ DCEntry->DCStatus, DCReplStatusStr,
+ DCEntry->PDCLinkStatus );
+
+ return;
+}
+
+VOID
+PrintTime(
+ VOID
+ )
+{
+ SYSTEMTIME SystemTime;
+
+ //
+ // print time.
+ //
+
+ GetLocalTime( &SystemTime );
+ printf( "TIME : [%02u/%02u %02u:%02u:%02u]\n",
+ SystemTime.wMonth,
+ SystemTime.wDay,
+ SystemTime.wHour,
+ SystemTime.wMinute,
+ SystemTime.wSecond );
+}
+
+VOID
+DisplayLists(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ Display the content of the global domain lists.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PLIST_ENTRY DomainList;
+ PLIST_ENTRY NextDomainEntry;
+ PMONITORED_DOMAIN_ENTRY DomainMonEntry;
+
+ PLIST_ENTRY DCList;
+ PLIST_ENTRY NextEntry;
+ PDC_ENTRY DCEntry;
+
+ PLIST_ENTRY TrustConnectList;
+ PLIST_ENTRY NextTrustConnectEntry;
+ PTD_LINK TrustConnectEntry;
+
+ //
+ // lock lists so that the update is paused while we display the
+ // current content.
+ //
+
+ LOCK_LISTS();
+
+ PrintTime();
+
+ DomainList = &GlobalDomainsMonitored;
+ for( NextDomainEntry = DomainList->Flink;
+ NextDomainEntry != DomainList;
+ NextDomainEntry = NextDomainEntry->Flink ) {
+
+ DomainMonEntry = (PMONITORED_DOMAIN_ENTRY)NextDomainEntry;
+
+ DCList = &(DomainMonEntry->DomainEntry->DCList);
+
+ if( IsListEmpty( DCList ) ) {
+ continue;
+ }
+
+ //
+ // if we are asked to terminate, do so.
+ //
+
+ if( GlobalTerminateFlag ) {
+ break;
+ }
+
+ printf("DomainName: %wZ \n", &DomainMonEntry->Name);
+ printf("%-15s %-10s %-10s %-10s %-10s %-10s\n",
+ "ServerName", "DCState", "DCType", "DCStatus",
+ "ReplStatus", "PDCLinkStatus" );
+
+ for( NextEntry = DCList->Flink;
+ NextEntry != DCList; NextEntry = NextEntry->Flink ) {
+
+ DCEntry = (PDC_ENTRY) NextEntry;
+ DisplayDCEntryStatus( DCEntry );
+
+ TrustConnectList = &DCEntry->TrustedDCs;
+
+ if( IsListEmpty( TrustConnectList ) ) {
+ continue;
+ }
+
+ //
+ // if we are asked to terminate, do so.
+ //
+
+ if( GlobalTerminateFlag ) {
+ break;
+ }
+
+ //
+ // print connection status for each trusted DC.
+ //
+
+ printf("\n");
+ printf(" " "Trusted DC List:\n" );
+
+ printf(" "
+ "%-15s %-15s %-10s\n",
+ "TDomainName", "TDCName", "TSCStatus" );
+
+
+ for( NextTrustConnectEntry = TrustConnectList->Flink;
+ NextTrustConnectEntry != TrustConnectList;
+ NextTrustConnectEntry = NextTrustConnectEntry->Flink ) {
+
+ TrustConnectEntry = (PTD_LINK) NextTrustConnectEntry;
+
+
+ printf(" "
+ "%-15wZ %-15wZ %-10ld\n",
+ &TrustConnectEntry->TDName,
+ &TrustConnectEntry->DCName,
+ TrustConnectEntry->SecureChannelStatus );
+ }
+ printf("\n");
+
+ }
+
+ //
+ // print status trusted domain DCs.
+ //
+
+ if( GlobalMonitorTrust ) {
+
+ PLIST_ENTRY TrustedDomainEntry;
+ PLIST_ENTRY NextTrustedDomainEntry;
+ PTRUSTED_DOMAIN_ENTRY TrustedDomain;
+
+ TrustedDomainEntry = &DomainMonEntry->DomainEntry->TrustedDomainList;
+
+ if( !IsListEmpty( TrustedDomainEntry ) ) {
+
+ //
+ // if we are asked to terminate, do so.
+ //
+
+ if( GlobalTerminateFlag ) {
+ break;
+ }
+
+ printf(" " "Trusted Domain DCs:\n" );
+
+ for( NextTrustedDomainEntry = TrustedDomainEntry->Flink;
+ NextTrustedDomainEntry != TrustedDomainEntry;
+ NextTrustedDomainEntry = NextTrustedDomainEntry->Flink ) {
+
+ TrustedDomain = (PTRUSTED_DOMAIN_ENTRY) NextTrustedDomainEntry;
+
+ DCList = &TrustedDomain->DomainEntry->DCList;
+
+ printf(" "
+ "DomainName: %wZ \n",
+ &TrustedDomain->Name);
+
+ if( IsListEmpty( DCList ) ) {
+ printf(" " " " "EMPTY. \n");
+ continue;
+ }
+
+ printf(" "
+ "%-15s %-10s %-10s %-10s %-10s %-10s\n",
+ "ServerName", "DCState", "DCType", "DCStatus",
+ "ReplStatus", "PDCLinkStatus" );
+
+ for( NextEntry = DCList->Flink;
+ NextEntry != DCList; NextEntry = NextEntry->Flink ) {
+
+ DCEntry = (PDC_ENTRY) NextEntry;
+ printf(" ");
+ DisplayDCEntryStatus( DCEntry );
+ }
+ }
+ }
+
+ }
+
+ printf("%s\n", DOMAINLINE);
+ }
+
+ printf("%s\n", SESSLINE);
+
+ UNLOCK_LISTS();
+}
+
+BOOL
+InitDomainList(
+ PCHAR DomainList
+ )
+/*++
+
+Routine Description:
+
+ Parse comma separated domain list.
+
+Arguments:
+
+ DomainList - comma separate domain list.
+
+Return Value:
+
+ TRUE - if successfully parsed.
+ FALSE - iff the list is bad.
+
+--*/
+{
+ WCHAR DomainName[DNLEN + 1];
+ PWCHAR d;
+ PCHAR p;
+ DWORD Len;
+
+
+ p = DomainList;
+
+ if( *p == '\0' ) {
+ return(FALSE);
+ }
+
+ while (*p != '\0') {
+
+ d = DomainName; // destination to next domain name.
+
+ while( (*p != '\0') && (*p == ' ') ) {
+ p++; // skip leading blanks.
+ }
+
+ //
+ // read next domain name.
+ //
+
+ while( (*p != '\0') && (*p != ',') ) {
+
+ if( d < DomainName + DNLEN ) {
+ *d++ = (WCHAR) (*p++);
+ }
+ }
+
+ if( *p != '\0' ) {
+ p++; // skip comma.
+ }
+
+ //
+ // delete tail end blanks.
+ //
+
+ while ( (d > DomainName) && (*(d-1) == ' ') ) {
+ d--;
+ }
+
+ *d = L'\0';
+
+ if( Len = wcslen(DomainName) ) {
+
+ UNICODE_STRING UnicodeDomainName;
+
+ RtlInitUnicodeString( &UnicodeDomainName, DomainName );
+
+ LOCK_LISTS();
+ if( AddToMonitoredDomainList( &UnicodeDomainName ) == NULL ) {
+ UNLOCK_LISTS();
+ return(FALSE);
+ }
+ UNLOCK_LISTS();
+ }
+ }
+
+ if( IsListEmpty( &GlobalDomainsMonitored ) ) {
+ return(FALSE);
+ }
+
+ return(TRUE);
+
+}
+
+BOOL
+ParseInputParams(
+ IN int argc,
+ IN char ** argv
+ )
+/*++
+
+Routine Description:
+
+ Parses input parameters and sets appropriate global variables.
+
+Arguments:
+
+ argc - the number of command-line arguments.
+
+ argv - an array of pointers to the arguments.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PCHAR NextArg;
+ PCHAR EndArg;
+ DWORD i;
+ NT_PRODUCT_TYPE NtProductType;
+
+
+ //
+ // Loop through the arguments handle each in turn
+ //
+
+ for ( i = 1; i < (DWORD)argc; i++ ) {
+
+ //
+ // Handle /DOMAINLIST:
+ //
+
+ NextArg = argv[i];
+
+ if ( _strnicmp( NextArg, DOMAIN_PARAM, sizeof(DOMAIN_PARAM) - 1 ) == 0 ) {
+
+ NextArg = NextArg + sizeof(DOMAIN_PARAM) - 1;
+
+ if( !InitDomainList( NextArg ) ) {
+ PrintUsage();
+ return(FALSE);
+ }
+ }
+ else if ( _strnicmp( NextArg, MONTRUST_PARAM, sizeof(MONTRUST_PARAM) - 1 ) == 0 ) {
+
+ NextArg = NextArg + sizeof(MONTRUST_PARAM) - 1;
+
+ if( _strnicmp( NextArg, YES_PARAM, sizeof(YES_PARAM) -1 ) == 0 ) {
+ GlobalMonitorTrust = TRUE;
+ }
+ else if( _strnicmp( NextArg, NO_PARAM, sizeof(NO_PARAM) -1 ) == 0 ) {
+ GlobalMonitorTrust = FALSE;
+ }
+ else {
+ PrintUsage();
+ return(FALSE);
+ }
+ }
+ else if ( _strnicmp( NextArg, UPDATE_PARAM, sizeof(UPDATE_PARAM) - 1 ) == 0 ) {
+
+ NextArg = NextArg + sizeof(UPDATE_PARAM) - 1;
+
+ GlobalUpdateTimeMSec = strtoul( NextArg, &EndArg, 10 ) * 60000;
+
+ if( (INT)GlobalUpdateTimeMSec <= 0 ) {
+ PrintUsage();
+ return(FALSE);
+ }
+ }
+ else if ( _strnicmp( NextArg, DEBUG_PARAM, sizeof(DEBUG_PARAM) - 1 ) == 0 ) {
+
+ NextArg = NextArg + sizeof(DEBUG_PARAM) - 1;
+ GlobalTrace = strtoul( NextArg, &EndArg, 16 );
+ }
+ else {
+ PrintUsage();
+ return(FALSE);
+ }
+ }
+
+ if( IsListEmpty( &GlobalDomainsMonitored ) ) {
+
+ NTSTATUS Status;
+ PPOLICY_ACCOUNT_DOMAIN_INFO DomainInfo;
+
+ //
+ // Read product type
+ //
+
+ if ( RtlGetNtProductType( &NtProductType ) ) {
+ if ( (NtProductType != NtProductWinNt) &&
+ (NtProductType != NtProductLanManNt) &&
+ (NtProductType != NtProductServer) ) {
+
+ NlMonDbgPrint(("ParseInputParams: Invalid Product Type.\n"));
+
+ return(FALSE);
+ }
+ }
+ else {
+ NlMonDbgPrint(("ParseInputParams: Can't read product type.\n"));
+ }
+
+ Status = QueryLsaInfo(
+ NULL,
+ POLICY_VIEW_LOCAL_INFORMATION,
+ (NtProductType == NtProductLanManNt) ?
+ PolicyAccountDomainInformation :
+ PolicyPrimaryDomainInformation,
+ (PVOID *) &DomainInfo,
+ NULL );
+
+ LOCK_LISTS();
+ if( ( DomainInfo->DomainName.Length == 0 ) ||
+ ( AddToMonitoredDomainList( &DomainInfo->DomainName ) == NULL ) ) {
+
+ LsaFreeMemory( DomainInfo );
+ UNLOCK_LISTS();
+ return(FALSE);
+ }
+ UNLOCK_LISTS();
+
+ LsaFreeMemory( DomainInfo );
+ }
+
+
+ IF_DEBUG( INIT ) {
+ PLIST_ENTRY DomainList;
+ PLIST_ENTRY NextDomainEntry;
+ PMONITORED_DOMAIN_ENTRY DomainMonEntry;
+
+
+ NlMonDbgPrint(("Domains Monitored:\n"));
+
+ DomainList = &GlobalDomainsMonitored;
+ i = 1;
+ for( NextDomainEntry = DomainList->Flink;
+ NextDomainEntry != DomainList;
+ NextDomainEntry = NextDomainEntry->Flink ) {
+
+ DomainMonEntry = (PMONITORED_DOMAIN_ENTRY)NextDomainEntry;
+
+ NlMonDbgPrint((" "
+ "%ld: %wZ\n", i++, &DomainMonEntry->Name ));
+ }
+
+ NlMonDbgPrint(("MonitorTrust: %s \n", (GlobalMonitorTrust) ? "YES" : "NO" ));
+ NlMonDbgPrint(("UpdateTime: %ld \n\n", GlobalUpdateTimeMSec ));
+ }
+
+ return(TRUE);
+}
+
+VOID
+CleanupGlobals(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ Free all resources consumed.
+
+Arguments:
+
+ none.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ DWORD WaitStatus;
+
+ //
+ // wait for other threads to go away.
+ //
+
+ WaitStatus = WaitForSingleObject(
+ GlobalCmdProcessThreadHandle,
+ THREAD_WAIT_TIME );
+
+ if ( WaitStatus != 0 ) {
+ if ( WaitStatus == WAIT_TIMEOUT ) {
+ NlMonDbgPrint(("CleanupGlobals: "
+ "CmdProcess thread doesn't stop: %ld\n",
+ WaitStatus ));
+ } else {
+ NlMonDbgPrint(("CleanupGlobals: "
+ "Cannot WaitFor CmdProcess thread: %ld\n",
+ WaitStatus ));
+ }
+ }
+
+ CloseHandle( GlobalCmdProcessThreadHandle );
+ GlobalCmdProcessThreadHandle = NULL;
+
+ WaitStatus = WaitForSingleObject(
+ GlobalWorkerThreadHandle,
+ THREAD_WAIT_TIME );
+
+ if ( WaitStatus != 0 ) {
+ if ( WaitStatus == WAIT_TIMEOUT ) {
+ NlMonDbgPrint(("CleanupGlobals: "
+ "Worker thread doesn't stop: %ld\n",
+ WaitStatus ));
+ } else {
+ NlMonDbgPrint(("CleanupGlobals: "
+ "Cannot WaitFor Worker thread: %ld\n",
+ WaitStatus ));
+ }
+ }
+
+ CloseHandle( GlobalWorkerThreadHandle );
+ GlobalWorkerThreadHandle = NULL;
+
+ //
+ // now cleanup all lists.
+ //
+
+ CleanupLists();
+
+ //
+ // delete list critsect.
+ //
+
+ DeleteCriticalSection( &GlobalListCritSect );
+ DeleteCriticalSection( &GlobalDomainUpdateThreadCritSect );
+
+ //
+ // close event handles.
+ //
+
+ if( !CloseHandle( GlobalRefreshDoneEvent ) ) {
+ NlMonDbgPrint((
+ "Cleanup: CloseHandle GlobalRefreshDoneEvent error: %lu\n",
+ GetLastError() ));
+ }
+
+ if( !CloseHandle( GlobalRefreshEvent ) ) {
+ NlMonDbgPrint((
+ "Cleanup: CloseHandle GlobalRefreshEvent error: %lu\n",
+ GetLastError() ));
+ }
+
+ if( !CloseHandle( GlobalUpdateEvent ) ) {
+ NlMonDbgPrint((
+ "Cleanup: CloseHandle GlobalUpdateEvent error: %lu\n",
+ GetLastError() ));
+ }
+
+ if( !CloseHandle( GlobalTerminateEvent ) ) {
+ NlMonDbgPrint((
+ "Cleanup: CloseHandle GlobalTerminateEvent error: %lu\n",
+ GetLastError() ));
+ }
+}
+
+
+VOID
+CmdProcessThread(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This thread process input commands.
+
+Arguments:
+
+Return Value:
+
+ None.
+
+--*/
+{
+ CHAR InputCmd;
+
+ for( ;; ) {
+
+ //
+ // read next input command.
+ //
+
+ InputCmd = _getch();
+
+ switch( InputCmd ) {
+ case 'd':
+ case 'D':
+ DisplayLists();
+ break;
+
+ case 'r':
+ case 'R':
+
+ if ( !SetEvent( GlobalRefreshEvent ) ) {
+ NlMonDbgPrint(("CmdProcessThread: Cannot set "
+ "GlobalRefreshEvent: %lu\n",
+ GetLastError() ));
+ }
+ return;
+ break;
+
+ case EOF:
+ case '\003':
+ case 'q':
+ case 'Q':
+
+ if ( !SetEvent( GlobalTerminateEvent ) ) {
+ NlMonDbgPrint(("CmdProcessThread: Cannot set "
+ "termination event: %lu\n",
+ GetLastError() ));
+ }
+ else {
+ GlobalTerminateFlag = TRUE;
+ }
+
+ return;
+ break;
+
+ case 'h':
+ case 'H':
+ printf( "CmdUsage:\n"
+ " " "D/d: Display the last known status of servers.\n"
+ " " "R/r: Refresh list content.\n"
+ " " "H/h: Display this message.\n"
+ " " "Q/q: Quit this apps.\n"
+ "\n" );
+ break;
+
+ default:
+ break;
+ }
+ }
+}
+
+int _CRTAPI1
+main(
+ IN int argc,
+ IN char ** argv
+ )
+/*++
+
+Routine Description:
+
+ Monitors Trusted Domain DCs by calling various network control and
+ GetInfo APIs.
+
+Arguments:
+
+ argc - the number of command-line arguments.
+
+ argv - an array of pointers to the arguments.
+
+Return Value:
+
+ Exit status
+
+--*/
+{
+ DWORD ThreadHandle;
+
+#define WAIT_COUNT 2
+
+#define UPDATE_EVENT 0
+#define TERMINATE_EVENT 1
+
+ DWORD WaitStatus;
+ HANDLE WaitHandles[ WAIT_COUNT ];
+
+ DWORD WinError;
+
+ PrintTime();
+
+ //
+ // Initialize Globals.
+ //
+
+ WinError = InitGlobals();
+ if( WinError != ERROR_SUCCESS) {
+ return( WinError );
+ }
+
+ //
+ // parse input parameters.
+ //
+
+ if( !ParseInputParams( argc, argv ) ) {
+ goto Cleanup;
+ }
+
+ //
+ // Make initial DCList and TrustDomainList of domains we monitor.
+ //
+
+ UpdateAndValidateLists( UPDATE_ALL, TRUE );
+
+ //
+ // create worker thread.
+ //
+
+ GlobalWorkerThreadHandle =
+ CreateThread(
+ NULL, // No security attributes
+ THREAD_STACKSIZE,
+ (LPTHREAD_START_ROUTINE) WorkerThread,
+ NULL,
+ 0, // No special creation flags
+ &ThreadHandle );
+
+ if ( GlobalWorkerThreadHandle == NULL ) {
+
+ NlMonDbgPrint(("Can't create Worker Thread %lu.\n", GetLastError()));
+
+ goto Cleanup;
+ }
+
+ //
+ // create command processing thread.
+ //
+
+ GlobalCmdProcessThreadHandle =
+ CreateThread(
+ NULL, // No security attributes
+ THREAD_STACKSIZE,
+ (LPTHREAD_START_ROUTINE) CmdProcessThread,
+ NULL,
+ 0, // No special creation flags
+ &ThreadHandle );
+
+ if ( GlobalCmdProcessThreadHandle == NULL ) {
+
+ NlMonDbgPrint(("Can't create Command processing Thread %lu.\n",
+ GetLastError()));
+ goto Cleanup;
+ }
+
+ //
+ // perpare wait event array.
+ //
+
+ WaitHandles[UPDATE_EVENT] = GlobalUpdateEvent;
+ WaitHandles[TERMINATE_EVENT] = GlobalTerminateEvent;
+
+ for( ;; ) {
+
+ //
+ // wait for one of the following event to happen :
+ //
+ // 1. GlobalUpdateEvent
+ // 2. GlobalTerminateEvent.
+ //
+
+ WaitStatus = WaitForMultipleObjects(
+ WAIT_COUNT,
+ WaitHandles,
+ FALSE, // Wait for ANY handle
+ INFINITE );
+
+ switch ( WaitStatus ) {
+ case UPDATE_EVENT:
+ DisplayLists();
+ break;
+
+ case TERMINATE_EVENT:
+ //
+ // done.
+ //
+ goto Cleanup;
+
+ default:
+ NlMonDbgPrint((
+ "main: WaitForMultipleObjects error: %ld\n",
+ WaitStatus));
+ break;
+ }
+
+ }
+
+Cleanup:
+
+ CleanupGlobals();
+
+ return(0);
+}
+
diff --git a/private/net/svcdlls/logonsrv/monitor/nlmon.rc b/private/net/svcdlls/logonsrv/monitor/nlmon.rc
new file mode 100644
index 000000000..f304eef03
--- /dev/null
+++ b/private/net/svcdlls/logonsrv/monitor/nlmon.rc
@@ -0,0 +1,12 @@
+#include <windows.h>
+#include <ntverp.h>
+
+#define VER_FILETYPE VFT_APP
+#define VER_FILESUBTYPE VFT2_UNKNOWN
+#define VER_FILEDESCRIPTION_STR "Microsoft\256 NetWork Licence Monitor Utility"
+
+#define VER_INTERNALNAME_STR "nlmon.exe"
+#define VER_ORIGINALFILENAME_STR "nlmon.exe"
+
+#include <common.ver>
+
diff --git a/private/net/svcdlls/logonsrv/monitor/sources b/private/net/svcdlls/logonsrv/monitor/sources
new file mode 100644
index 000000000..845590ad2
--- /dev/null
+++ b/private/net/svcdlls/logonsrv/monitor/sources
@@ -0,0 +1,106 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+
+Author:
+
+
+Revision History:
+
+!ENDIF
+
+#
+# The MAJORCOMP and MINORCOMP variables are defined
+# so that $(MAJORCOMP)$(MINORCOMP)filename can be used in
+# cross compiling to provide unique filenames in a flat namespace.
+#
+
+MAJORCOMP=net
+MINORCOMP=logonsrv
+
+#
+# The TARGETNAME variable is defined by the developer. It is the name of
+# the target (component) that is being built by this makefile. It
+# should NOT include any path or file extension information.
+#
+
+TARGETNAME=nlmonlib
+
+#
+# The TARGETPATH and TARGETTYPE variables are defined by the developer.
+# The first specifies where the target is to be build. The second specifies
+# the type of target (either PROGRAM, DYNLINK, LIBRARY, UMAPPL_NOLIB or
+# BOOTPGM). UMAPPL_NOLIB is used when you're only building user-mode
+# apps and don't need to build a library.
+#
+
+TARGETPATH=$(BASEDIR)\public\sdk\lib
+
+TARGETTYPE=LIBRARY
+
+#
+# The TARGETLIBS specifies additional libraries to link with you target
+# image. Each library path specification should contain an asterisk (*)
+# where the machine specific subdirectory name should go.
+#
+
+TARGETLIBS=
+
+
+#
+# The INCLUDES variable specifies any include paths that are specific to
+# this source directory. Separate multiple directory paths with single
+# semicolons. Relative path specifications are okay.
+#
+
+INCLUDES=.;..;..\..\..\inc;..\..\..\..\inc
+
+#
+# The SOURCES variable is defined by the developer. It is a list of all the
+# source files for this component. Each source file should be on a separate
+# line using the line continuation character. This will minimize merge
+# conflicts if two developers adding source files to the same component.
+#
+
+!IFNDEF DISABLE_NET_UNICODE
+UNICODE=1
+NET_C_DEFINES=-DUNICODE
+!ENDIF
+
+SOURCES= monutil.c \
+ winutil.c
+
+#
+# This line makes the apps. to use crtdll.dll instead of libc.lib
+#
+
+USE_CRTDLL=1
+
+#
+# Next specify one or more user mode test programs and their type
+# UMTEST is used for optional test programs. UMAPPL is used for
+# programs that always get built when the directory is built.
+#
+
+UMTYPE=console
+UMAPPL=nlmon
+UMRES=$(@R).res
+UMLIBS= $(BASEDIR)\Public\Sdk\Lib\*\nlmonlib.lib \
+ $(BASEDIR)\Public\Sdk\Lib\*\netlib.lib \
+ $(BASEDIR)\Public\Sdk\Lib\*\samlib.lib \
+ $(BASEDIR)\public\sdk\lib\*\netapi32.lib \
+ $(BASEDIR)\public\sdk\lib\*\ntdll.lib
+
+NTTARGETFILE1=obj\*\nlmon.res
diff --git a/private/net/svcdlls/logonsrv/monitor/winutil.c b/private/net/svcdlls/logonsrv/monitor/winutil.c
new file mode 100644
index 000000000..634c6eea1
--- /dev/null
+++ b/private/net/svcdlls/logonsrv/monitor/winutil.c
@@ -0,0 +1,922 @@
+/*--
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ monutil.c
+
+Abstract:
+
+ Contains support functions required for GUI version of the monitor
+ program.
+
+Author:
+
+ 14-Jun-1993 (madana)
+
+Environment:
+
+ User mode only.
+ Contains NT-specific code.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+--*/
+
+#include <nlmon.h>
+
+VOID
+CleanupWin(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ Free all resources consumed.
+
+Arguments:
+
+ none.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ //
+ // now cleanup all lists.
+ //
+
+ CleanupLists();
+
+ //
+ // delete list critsect.
+ //
+
+ DeleteCriticalSection( &GlobalListCritSect );
+ DeleteCriticalSection( &GlobalDomainUpdateThreadCritSect );
+
+ //
+ // close event handles.
+ //
+
+ if( !CloseHandle( GlobalRefreshDoneEvent ) ) {
+ NlMonDbgPrint((
+ "CleanupWin: CloseHandle GlobalRefreshDoneEvent error: %lu\n",
+ GetLastError() ));
+ }
+
+ if( !CloseHandle( GlobalRefreshEvent ) ) {
+ NlMonDbgPrint((
+ "CleanupWin: CloseHandle GlobalRefreshEvent error: %lu\n",
+ GetLastError() ));
+ }
+
+ if( !CloseHandle( GlobalUpdateEvent ) ) {
+ NlMonDbgPrint((
+ "CleanupWin: CloseHandle GlobalUpdateEvent error: %lu\n",
+ GetLastError() ));
+ }
+
+ if( !CloseHandle( GlobalTerminateEvent ) ) {
+ NlMonDbgPrint((
+ "CleanupWin: CloseHandle GlobalTerminateEvent error: %lu\n",
+ GetLastError() ));
+ }
+}
+
+DWORD
+StartMonitor(
+ LPWSTR DomainList,
+ DWORD Interval,
+ BOOL MonitorTD
+ )
+/*++
+
+Routine Description:
+
+ This function sets up necessary data structures and starts a worker
+ thread to update the domain status at the given interval.
+
+Arguments:
+
+ DomainList : list of domains (separated by comma) to be monitored.
+
+ Interval : Status update interval in millisecond.
+
+ MonitorTD : Whether to update the trusted domain DC list or not.
+
+Return Value:
+
+ NT Status code.
+
+--*/
+{
+ DWORD ThreadHandle;
+ DWORD WinError;
+
+ //
+ // Initialize Globals.
+ //
+
+ WinError = InitGlobals();
+
+ if( WinError != ERROR_SUCCESS ) {
+ return(WinError);
+ }
+
+ //
+ // parse input parameters.
+ //
+
+ GlobalMonitorTrust = MonitorTD;
+ GlobalUpdateTimeMSec = Interval;
+ (VOID)InitDomainListW( DomainList );
+
+ //
+ // initial complete update.
+ //
+
+ UpdateAndValidateLists( UPDATE_ALL, TRUE );
+
+ //
+ // create worker thread.
+ //
+
+ GlobalWorkerThreadHandle =
+ CreateThread(
+ NULL, // No security attributes
+ THREAD_STACKSIZE,
+ (LPTHREAD_START_ROUTINE) WorkerThread,
+ NULL,
+ 0, // No special creation flags
+ &ThreadHandle );
+
+ if ( GlobalWorkerThreadHandle == NULL ) {
+
+ DWORD WinError;
+
+ WinError = GetLastError();
+
+ NlMonDbgPrint(("Can't create Worker Thread %lu.\n", WinError));
+
+ CleanupWin();
+ return( WinError );
+ }
+
+ return( ERROR_SUCCESS );
+}
+
+VOID
+StopMonitor(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This will stop the worker thread, cleanup the lists and free up all
+ resources consumed.
+
+Arguments:
+
+ none.
+
+Return Value:
+
+ none.
+
+--*/
+{
+ DWORD WinError;
+ DWORD WaitStatus;
+
+ PLIST_ENTRY DomainList;
+ PLIST_ENTRY NextDomainEntry;
+ PDOMAIN_ENTRY DomainEntry;
+
+ //
+ // Set Terminate Event to stop the worker.
+ //
+
+ if ( !SetEvent( GlobalTerminateEvent ) ) {
+
+ WinError = GetLastError();
+ NlMonDbgPrint(("StopMonitor: Cannot set "
+ "termination event: %lu\n",
+ WinError ));
+ return;
+ }
+
+ GlobalTerminateFlag = TRUE;
+
+ WaitStatus = WaitForSingleObject(
+ GlobalWorkerThreadHandle,
+ THREAD_WAIT_TIME );
+
+ if ( WaitStatus != 0 ) {
+ if ( WaitStatus == WAIT_TIMEOUT ) {
+ NlMonDbgPrint(("StopMonitor: "
+ "Worker thread doesn't stop: %ld\n",
+ WaitStatus ));
+ } else {
+ NlMonDbgPrint(("StopMonitor: "
+ "Cannot WaitFor Worker thread: %ld\n",
+ WaitStatus ));
+ }
+ }
+
+ CloseHandle( GlobalWorkerThreadHandle );
+ GlobalWorkerThreadHandle = NULL;
+
+ //
+ // Stop all DomainUpdateThreads.
+ //
+
+ LOCK_LISTS();
+ EnterCriticalSection( &GlobalDomainUpdateThreadCritSect );
+
+ DomainList = &GlobalDomains;
+ for( NextDomainEntry = DomainList->Flink;
+ NextDomainEntry != DomainList;
+ NextDomainEntry = NextDomainEntry->Flink ) {
+
+ DomainEntry = (PDOMAIN_ENTRY)NextDomainEntry;
+
+ if ( IsDomainUpdateThreadRunning( &DomainEntry->ThreadHandle ) ) {
+
+ //
+ // Wait and stop this thread. Unlock the lists so that the
+ // DomainUpdateThread can complete.
+ //
+
+ UNLOCK_LISTS();
+ StopDomainUpdateThread(
+ &DomainEntry->ThreadHandle,
+ &DomainEntry->ThreadTerminateFlag );
+
+ //
+ // since we dropped the list above, restart from begining.
+ //
+ LOCK_LISTS();
+ NextDomainEntry = &GlobalDomains;
+ }
+ }
+
+ LeaveCriticalSection( &GlobalDomainUpdateThreadCritSect );
+ UNLOCK_LISTS();
+
+ CleanupWin();
+ return;
+}
+
+DOMAIN_STATE
+QueryHealth(
+ const LPWSTR DomainName
+ )
+/*++
+
+Routine Description:
+
+ This function returns health of given domain.
+
+Arguments:
+
+ DomainName : Domain name of whose health will be returned.
+
+Return Value:
+
+ Domain State.
+
+--*/
+{
+ UNICODE_STRING UnicodeDomainName;
+ PMONITORED_DOMAIN_ENTRY DomainEntry;
+ DOMAIN_STATE State = DomainUnknown;
+
+ if (GlobalInitialized)
+ {
+ RtlInitUnicodeString( &UnicodeDomainName, DomainName );
+
+ //
+ // search the specified domain.
+ //
+
+ LOCK_LISTS();
+
+ DomainEntry = (PMONITORED_DOMAIN_ENTRY) FindNamedEntry(
+ &GlobalDomainsMonitored,
+ &UnicodeDomainName );
+
+ if( (DomainEntry == NULL) || (DomainEntry->DeleteFlag) ) {
+ State = DomainUnknown;
+ }
+ else {
+ State = DomainEntry->DomainEntry->DomainState;
+ }
+
+ UNLOCK_LISTS();
+ }
+
+ return(State);
+}
+
+
+LPWSTR
+QueryPDC(
+ const LPWSTR DomainName
+ )
+/*++
+
+Routine Description:
+
+ This function returns PDC name of the given domain.
+
+Arguments:
+
+ DomainName : Domain name of whose PDC name will be returned.
+
+Return Value:
+
+ PDC Name.
+
+--*/
+{
+ UNICODE_STRING UnicodeDomainName;
+ PMONITORED_DOMAIN_ENTRY DomainEntry;
+
+ PLIST_ENTRY DCList;
+ PLIST_ENTRY NextDCEntry;
+ PDC_ENTRY DCEntry;
+
+ if (GlobalInitialized)
+ {
+ RtlInitUnicodeString( &UnicodeDomainName, DomainName );
+
+ //
+ // search the specified domain.
+ //
+
+ LOCK_LISTS();
+ DomainEntry = (PMONITORED_DOMAIN_ENTRY) FindNamedEntry(
+ &GlobalDomainsMonitored,
+ &UnicodeDomainName );
+
+ if( (DomainEntry == NULL) || (DomainEntry->DeleteFlag) ) {
+ UNLOCK_LISTS();
+ return(NULL);
+ }
+
+ //
+ // Search for the PDC from the DC List.
+ //
+
+ DCList = &DomainEntry->DomainEntry->DCList;
+ for( NextDCEntry = DCList->Flink;
+ NextDCEntry != DCList;
+ NextDCEntry = NextDCEntry->Flink ) {
+
+ DCEntry = (PDC_ENTRY)NextDCEntry;
+
+ if( DCEntry->Type == NTPDC ) {
+
+ LPWSTR PDCName;
+
+ //
+ // Capture the DC Name.
+ //
+
+ PDCName = NetpMemoryAllocate(
+ DCEntry->DCName.MaximumLength );
+
+ if( PDCName != NULL ) {
+
+ RtlCopyMemory(
+ PDCName,
+ DCEntry->DCName.Buffer,
+ DCEntry->DCName.MaximumLength );
+
+ }
+
+ UNLOCK_LISTS();
+ return( PDCName );
+ }
+
+ }
+
+ UNLOCK_LISTS();
+ }
+
+ return( NULL );
+}
+
+PLIST_ENTRY
+QueryTrustedDomain(
+ const LPWSTR DomainName
+ )
+/*++
+
+Routine Description:
+
+ This function returns a trusted domain list of the specified
+ domain.
+
+ This function must be called after LOCKING the list and the
+ caller must UNLOCK the list after usage.
+
+Arguments:
+
+ DomainName : Name of the domain whose trusted domain list is
+ returned.
+
+Return Value:
+
+ Trusted Domain list.
+
+--*/
+{
+ UNICODE_STRING UnicodeDomainName;
+ PMONITORED_DOMAIN_ENTRY DomainEntry;
+
+ RtlInitUnicodeString( &UnicodeDomainName, DomainName );
+
+ //
+ // search the specified domain.
+ //
+
+ DomainEntry = (PMONITORED_DOMAIN_ENTRY) FindNamedEntry(
+ &GlobalDomainsMonitored,
+ &UnicodeDomainName );
+
+ if( (DomainEntry == NULL) || (DomainEntry->DeleteFlag) ) {
+ return(NULL);
+ }
+
+ return( &DomainEntry->DomainEntry->TrustedDomainList );
+}
+
+PLIST_ENTRY
+QueryDCList(
+ const LPWSTR DomainName
+ )
+/*++
+
+Routine Description:
+
+ This function returns the pointer to a doublely link list
+ data structures of all DCs in the given domain.
+
+ This function must be called after LOCKING the list and the
+ caller must UNLOCK the list after usage.
+
+Arguments:
+
+ DomainName : Name of the domain whose DC list data structure will be
+ returned.
+
+Return Value:
+
+ List pointer.
+
+--*/
+{
+ UNICODE_STRING UnicodeDomainName;
+ PMONITORED_DOMAIN_ENTRY DomainEntry;
+
+ RtlInitUnicodeString( &UnicodeDomainName, DomainName );
+
+ //
+ // search the specified domain.
+ //
+
+ DomainEntry = (PMONITORED_DOMAIN_ENTRY) FindNamedEntry(
+ &GlobalDomainsMonitored,
+ &UnicodeDomainName );
+
+ if( (DomainEntry == NULL) || (DomainEntry->DeleteFlag) ) {
+ return(NULL);
+ }
+
+ return( &DomainEntry->DomainEntry->DCList );
+}
+
+PLIST_ENTRY
+QueryTDLink(
+ const LPWSTR DomainName,
+ const LPWSTR DCName
+ )
+/*++
+
+Routine Description:
+
+ This function returns the list of trusted DCs.
+
+ This function must be called after LOCKING the list and the
+ caller must UNLOCK the list after usage.
+
+Arguments:
+
+ DCName : Name of the DC whose trusted DCs list is returned.
+
+Return Value:
+
+ List pointer.
+
+--*/
+{
+ UNICODE_STRING UnicodeDomainName;
+ UNICODE_STRING UnicodeDCName;
+ PMONITORED_DOMAIN_ENTRY DomainEntry;
+ PDC_ENTRY DCEntry;
+
+ RtlInitUnicodeString( &UnicodeDomainName, DomainName );
+ RtlInitUnicodeString( &UnicodeDCName, DCName );
+
+ //
+ // search the specified domain.
+ //
+
+ DomainEntry = (PMONITORED_DOMAIN_ENTRY) FindNamedEntry(
+ &GlobalDomainsMonitored,
+ &UnicodeDomainName );
+
+ if( (DomainEntry == NULL) || (DomainEntry->DeleteFlag) ) {
+ return(NULL);
+ }
+
+ //
+ // search the specified DC.
+ //
+
+ DCEntry = (PDC_ENTRY) FindNamedEntry(
+ &DomainEntry->DomainEntry->DCList,
+ &UnicodeDCName );
+
+ if( DCEntry == NULL ) {
+ return(NULL);
+ }
+
+ return( &DCEntry->TrustedDCs );
+}
+
+PLIST_ENTRY
+QueryTDCList(
+ const LPWSTR DomainName,
+ const LPWSTR TrustedDomainName
+ )
+/*++
+
+Routine Description:
+
+ This function returns a trusted domain's dc list of the specified
+ domain.
+
+ This function must be called after LOCKING the list and the
+ caller must UNLOCK the list after usage.
+
+Arguments:
+
+ DomainName : Name of the domain whose trusted domain list is
+ returned.
+
+ TrustedDomainName: The name of the trusted domain
+Return Value:
+
+ Trusted Domain's dc list.
+
+--*/
+{
+ UNICODE_STRING UnicodeDomainName;
+ UNICODE_STRING UnicodeTrustedDomainName;
+ PMONITORED_DOMAIN_ENTRY DomainEntry;
+ PTRUSTED_DOMAIN_ENTRY TDEntry;
+
+ RtlInitUnicodeString( &UnicodeDomainName, DomainName );
+ RtlInitUnicodeString( &UnicodeTrustedDomainName, TrustedDomainName );
+
+ //
+ // search the specified domain.
+ //
+
+ DomainEntry = (PMONITORED_DOMAIN_ENTRY) FindNamedEntry(
+ &GlobalDomainsMonitored,
+ &UnicodeDomainName );
+
+ if( (DomainEntry == NULL) || (DomainEntry->DeleteFlag) ) {
+ return(NULL);
+ }
+
+ TDEntry = (PTRUSTED_DOMAIN_ENTRY) FindNamedEntry(
+ &DomainEntry->DomainEntry->TrustedDomainList,
+ &UnicodeTrustedDomainName);
+
+ if (TDEntry == NULL)
+ {
+ return(NULL);
+ }
+
+ return( &TDEntry->DomainEntry->DCList );
+}
+
+DWORD
+DisConnect(
+ const LPWSTR DomainName,
+ const LPWSTR DCName,
+ const LPWSTR TrustedDomainName
+ )
+/*++
+
+Routine Description:
+
+ This function disconnects from the current trusted DC of the
+ specified trusted domain and makes a discovery of the another
+ trusted DC.
+
+Arguments:
+
+ DCName : Name of the DC whose specified trusted DC is disconnected.
+
+ TrustedDomainName : Name of the trusted domain DC whose trusted DC
+ is disconnected.
+
+Return Value:
+
+ NT status code.
+
+--*/
+{
+ NET_API_STATUS NetStatus;
+ PNETLOGON_INFO_2 NetlogonInfo2 = NULL;
+
+ UNICODE_STRING UnicodeDomainName;
+ UNICODE_STRING UnicodeTrustedDomainName;
+ UNICODE_STRING UnicodeDCName;
+ PMONITORED_DOMAIN_ENTRY DomainEntry;
+ PTD_LINK TDLinkEntry;
+ PDC_ENTRY DCEntry;
+
+ PLIST_ENTRY TDLinkList;
+ PLIST_ENTRY NextTDLinkEntry;
+ BOOL TDCLinkState;
+
+ RtlInitUnicodeString( &UnicodeDomainName, DomainName );
+ RtlInitUnicodeString( &UnicodeTrustedDomainName, TrustedDomainName );
+ RtlInitUnicodeString( &UnicodeDCName, DCName );
+
+ NetStatus = I_NetLogonControl2(
+ DCName,
+ NETLOGON_CONTROL_REDISCOVER,
+ 2,
+ (LPBYTE)&TrustedDomainName,
+ (LPBYTE *)&NetlogonInfo2 );
+
+ //
+ // search the specified domain.
+ //
+
+ LOCK_LISTS();
+ DomainEntry = (PMONITORED_DOMAIN_ENTRY) FindNamedEntry(
+ &GlobalDomainsMonitored,
+ &UnicodeDomainName );
+
+ if( (DomainEntry == NULL) || (DomainEntry->DeleteFlag) ) {
+ NetStatus = ERROR_NO_SUCH_DOMAIN;
+ goto Cleanup;
+ }
+
+ DCEntry = (PDC_ENTRY) FindNamedEntry(
+ &DomainEntry->DomainEntry->DCList,
+ &UnicodeDCName);
+
+ if (DCEntry == NULL) {
+ NetStatus = NERR_DCNotFound;
+ goto Cleanup;
+ }
+
+ TDLinkEntry = (PTD_LINK) FindNamedEntry(
+ &DCEntry->TrustedDCs,
+ &UnicodeTrustedDomainName);
+
+ if (TDLinkEntry == NULL) {
+ NetStatus = ERROR_NO_SUCH_DOMAIN;
+ goto Cleanup;
+ }
+
+
+ if ( NetStatus != NERR_Success ) {
+ //
+ // Cleanup the previous DC Name.
+ //
+
+ TDLinkEntry->DCName.Length = 0;
+ *TDLinkEntry->DCName.Buffer = L'\0';
+
+ TDLinkEntry->SecureChannelStatus = NetStatus;
+ goto Cleanup;
+ }
+
+
+ //
+ // update TDLinkEntry.
+ //
+
+ //
+ // copy this DC name.
+ //
+
+ TDLinkEntry->DCName.Length =
+ wcslen( NetlogonInfo2->netlog2_trusted_dc_name ) * sizeof(WCHAR);
+
+ RtlCopyMemory(
+ TDLinkEntry->DCName.Buffer,
+ NetlogonInfo2->netlog2_trusted_dc_name,
+ TDLinkEntry->DCName.Length + sizeof(WCHAR) );
+ // copy terminator also
+
+ TDLinkEntry->SecureChannelStatus =
+ NetlogonInfo2->netlog2_tc_connection_status;
+
+
+ //
+ // update DC status info.
+ //
+
+ DCEntry->ReplicationStatus = NetlogonInfo2->netlog2_flags;
+ DCEntry->PDCLinkStatus = NetlogonInfo2->netlog2_pdc_connection_status;
+
+ //
+ // validate trusted DC.
+ //
+
+ if( (TDLinkEntry->SecureChannelStatus == NERR_Success) &&
+ (*TDLinkEntry->DCName.Buffer != L'\0') ) {
+
+ NET_API_STATUS LocalNetStatus;
+
+ LocalNetStatus = IsValidNTDC(
+ &TDLinkEntry->DCName,
+ &TDLinkEntry->TDName );
+
+ if( LocalNetStatus != NERR_Success ) {
+
+ //
+ // hack, hack, hack ...
+ //
+ // For foreign trusted domains, the above check will
+ // return ERROR_LOGON_FAILURE. Just ignore this
+ // error for now. When the domain wide credential is
+ // implemeted this problem will be cured.
+ //
+
+
+ if( LocalNetStatus != ERROR_LOGON_FAILURE ) {
+ TDLinkEntry->SecureChannelStatus = LocalNetStatus;
+ TDCLinkState = FALSE;
+ }
+ }
+
+ }
+
+ //
+ // recompute trust connection status and domain status.
+ //
+
+ TDLinkList = &DCEntry->TrustedDCs;
+ TDCLinkState = TRUE;
+
+ for( NextTDLinkEntry = TDLinkList->Flink;
+ NextTDLinkEntry != TDLinkList;
+ NextTDLinkEntry = NextTDLinkEntry->Flink ) {
+
+ TDLinkEntry = (PTD_LINK) NextTDLinkEntry;
+
+ if( TDLinkEntry->SecureChannelStatus != NERR_Success ) {
+ TDCLinkState = FALSE;
+ }
+ }
+ DCEntry->TDCLinkState = TDCLinkState;
+
+ //
+ // recompute domain status if the update is not in progress.
+ //
+
+ if( DomainEntry->DomainEntry->DomainState != DomainUnknown ) {
+ UpdateDomainState( DomainEntry->DomainEntry );
+
+ //
+ // also notify UI.
+ //
+
+ SetEvent( GlobalUpdateEvent );
+ }
+
+Cleanup:
+
+ if( NetlogonInfo2 != NULL ) {
+ NetApiBufferFree( NetlogonInfo2 );
+ }
+
+ UNLOCK_LISTS();
+ return(NetStatus);
+}
+
+VOID
+AddDomainToList(
+ const LPWSTR DomainName
+ )
+/*++
+
+Routine Description:
+
+ This function adds the new specified domain to domain list.
+
+Arguments:
+
+ DomainName : Name of the domain to be added to list.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ UNICODE_STRING UnicodeDomainName;
+ PMONITORED_DOMAIN_ENTRY DomainEntry;
+
+ RtlInitUnicodeString( &UnicodeDomainName, DomainName );
+
+ LOCK_LISTS();
+ (VOID)AddToMonitoredDomainList( &UnicodeDomainName );
+
+ //
+ // update this domain DC list.
+ //
+
+ DomainEntry = (PMONITORED_DOMAIN_ENTRY) FindNamedEntry(
+ &GlobalDomainsMonitored,
+ &UnicodeDomainName );
+ if( DomainEntry == NULL) {
+ UNLOCK_LISTS();
+ return;
+ }
+
+ StartDomainUpdateThread( DomainEntry->DomainEntry, UPDATE_ALL );
+ UNLOCK_LISTS();
+
+
+ return;
+}
+
+VOID
+RemoveDomainFromList(
+ const LPWSTR DomainName
+ )
+/*++
+
+Routine Description:
+
+ This function removes the new specified domain from domain list.
+
+Arguments:
+
+ DomainName : Name of the domain to be removed to list.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ UNICODE_STRING UnicodeDomainName;
+ PMONITORED_DOMAIN_ENTRY DomainEntry;
+
+ RtlInitUnicodeString( &UnicodeDomainName, DomainName );
+
+ //
+ // search the specified domain.
+ //
+
+ LOCK_LISTS();
+ DomainEntry = (PMONITORED_DOMAIN_ENTRY) FindNamedEntry(
+ &GlobalDomainsMonitored,
+ &UnicodeDomainName );
+
+ if( (DomainEntry == NULL) || (DomainEntry->DeleteFlag) ) {
+ UNLOCK_LISTS();
+ return;
+ }
+
+ //
+ // mark this entry is dead.
+ //
+
+ DomainEntry->DeleteFlag = TRUE;
+
+ UNLOCK_LISTS();
+ return;
+}
+
diff --git a/private/net/svcdlls/logonsrv/nlbind.h b/private/net/svcdlls/logonsrv/nlbind.h
new file mode 100644
index 000000000..a7502830a
--- /dev/null
+++ b/private/net/svcdlls/logonsrv/nlbind.h
@@ -0,0 +1,45 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ nlbind.h
+
+Abstract:
+
+ Interface to the Netlogon service RPC handle cacheing routines
+
+Author:
+
+ Cliff Van Dyke (01-Oct-1993)
+
+Revision History:
+
+--*/
+
+////////////////////////////////////////////////////////////////////////////
+//
+// Procedure forwards
+//
+////////////////////////////////////////////////////////////////////////////
+
+VOID
+NlBindingAttachDll (
+ VOID
+ );
+
+VOID
+NlBindingDetachDll (
+ VOID
+ );
+
+NTSTATUS
+NlBindingAddServerToCache (
+ IN LPWSTR UncServerName
+ );
+
+NTSTATUS
+NlBindingRemoveServerFromCache (
+ IN LPWSTR UncServerName
+ );
diff --git a/private/net/svcdlls/logonsrv/server/announce.c b/private/net/svcdlls/logonsrv/server/announce.c
new file mode 100644
index 000000000..499cdde79
--- /dev/null
+++ b/private/net/svcdlls/logonsrv/server/announce.c
@@ -0,0 +1,1243 @@
+/*++
+
+Copyright (c) 1987-1992 Microsoft Corporation
+
+Module Name:
+
+ announce.c
+
+Abstract:
+
+ Routines to handle ssi announcements.
+
+Author:
+
+ Ported from Lan Man 2.0
+
+Environment:
+
+ User mode only.
+ Contains NT-specific code.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+ 21-May-1991 (cliffv)
+ Ported to NT. Converted to NT style.
+
+ 02-Jan-1992 (madana)
+ added support for builtin/multidomain replication.
+--*/
+
+//
+// Common include files.
+//
+#include <logonsrv.h> // Include files common to entire service
+
+//
+// Include files specific to this .c file
+//
+
+#include <alertmsg.h> // ALERT_* defines
+#include <stddef.h> // offsetof
+#include <stdlib.h> // max()
+
+//
+// Maximum number of pulses that we allow a BDC to ignore before ignoring it.
+//
+#define MAX_PULSE_TIMEOUT 3
+
+VOID
+NlRemovePendingBdc(
+ IN PSERVER_SESSION ServerSession
+ )
+/*++
+
+Routine Description:
+
+ Remove the specified Server Session from the list of pending BDCs.
+
+ Enter with the ServerSessionTable Sem locked
+
+Arguments:
+
+ ServerSession -- Pointer to the server session structure to remove from the
+ list.
+
+Return Value:
+
+ None.
+
+--*/
+{
+
+ //
+ // Ensure the server session is really on the list.
+ //
+
+ if ( (ServerSession->SsFlags & SS_PENDING_BDC) == 0 ) {
+ return;
+ }
+
+ //
+ // Decrement the count of pending BDCs
+ //
+
+ NlAssert( NlGlobalPendingBdcCount > 0 );
+ NlGlobalPendingBdcCount --;
+
+ //
+ // If this is the last BDC in the pending list,
+ // turn off the timer.
+ //
+
+ if ( NlGlobalPendingBdcCount == 0 ) {
+ NlGlobalPendingBdcTimer.Period = (DWORD) MAILSLOT_WAIT_FOREVER;
+ }
+
+ //
+ // Remove the pending BDC from the list of pending BDCs.
+ //
+
+ RemoveEntryList( &ServerSession->SsPendingBdcList );
+
+ //
+ // Turn off the flag indicating we're in the list.
+ //
+
+ ServerSession->SsFlags &= ~SS_PENDING_BDC;
+
+ NlPrint((NL_PULSE_MORE,
+ "NlRemovePendingBdc: %s: Removed from pending list. Count: %ld\n",
+ ServerSession->SsComputerName,
+ NlGlobalPendingBdcCount ));
+
+}
+
+
+VOID
+NlAddPendingBdc(
+ IN PSERVER_SESSION ServerSession
+ )
+/*++
+
+Routine Description:
+
+ Add the specified Server Session to the list of pending BDCs.
+
+ Enter with the ServerSessionTable Sem locked
+
+Arguments:
+
+ ServerSession -- Pointer to the server session structure to add to the
+ list.
+
+Return Value:
+
+ None.
+
+--*/
+{
+
+ //
+ // Ensure the server session is really off the list.
+ //
+
+ if ( ServerSession->SsFlags & SS_PENDING_BDC ) {
+ return;
+ }
+
+ //
+ // If this is the first pending BDC,
+ // start the timer.
+ //
+
+ if ( NlGlobalPendingBdcCount == 0 ) {
+ // Run the timer at twice the frequency of the timeout to ensure that
+ // entries don't have to wait nearly twice the timeout period before
+ // they expire.
+ NlGlobalPendingBdcTimer.Period = NlGlobalPulseTimeout1Parameter * 500;
+ (VOID) NtQuerySystemTime( &NlGlobalPendingBdcTimer.StartTime );
+
+ //
+ // Tell the main thread that I've changed a timer.
+ //
+
+ if ( !SetEvent( NlGlobalTimerEvent ) ) {
+ NlPrint(( NL_CRITICAL,
+ "NlAddPendingBdc: %s: SetEvent2 failed %ld\n",
+ ServerSession->SsComputerName,
+ GetLastError() ));
+ }
+
+ }
+
+ //
+ // Increment the count of pending BDCs
+ //
+
+ NlGlobalPendingBdcCount ++;
+
+ //
+ // Add the pending BDC to the list of pending BDCs.
+ //
+
+ InsertTailList( &NlGlobalPendingBdcList, &ServerSession->SsPendingBdcList );
+
+ //
+ // Turn on the flag indicating we're in the list.
+ //
+
+ ServerSession->SsFlags |= SS_PENDING_BDC;
+
+ NlPrint((NL_PULSE_MORE,
+ "NlAddPendingBdc: %s: Added to pending list. Count: %ld\n",
+ ServerSession->SsComputerName,
+ NlGlobalPendingBdcCount ));
+
+}
+
+
+VOID
+NetpLogonPutDBInfo(
+ IN PDB_CHANGE_INFO DBInfo,
+ IN OUT PCHAR * Where
+)
+/*++
+
+Routine Description:
+
+ Put Database info structure in mailslot buffer.
+
+Arguments:
+
+ DBInfo : database info structure pointer.
+
+ Where : indirect pointer to mailslot buffer. Database info
+ is copied over here. When returned this pointer is
+ updated to point the end of mailslot buffer.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ NetpLogonPutBytes( &DBInfo->DBIndex, sizeof(DBInfo->DBIndex), Where);
+
+ NetpLogonPutBytes( &(DBInfo->LargeSerialNumber),
+ sizeof(DBInfo->LargeSerialNumber),
+ Where);
+
+ NetpLogonPutBytes( &(DBInfo->NtDateAndTime),
+ sizeof(DBInfo->NtDateAndTime),
+ Where);
+}
+
+
+VOID
+NetpLogonUpdateDBInfo(
+ IN PLARGE_INTEGER SerialNumber,
+ IN OUT PCHAR * Where
+)
+/*++
+
+Routine Description:
+
+ Update the Serial Number within the already packed mailslot buffer.
+
+Arguments:
+
+ SerialNumber: New SerialNumber.
+
+ Where : indirect pointer to mailslot buffer. Database info
+ is copied over here. When returned this pointer is
+ updated to point the end of mailslot buffer.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ *Where += sizeof(DWORD);
+
+ NetpLogonPutBytes( SerialNumber, sizeof(LARGE_INTEGER), Where);
+
+ *Where += sizeof(LARGE_INTEGER);
+}
+
+
+
+BOOLEAN
+NlAllocatePrimaryAnnouncement(
+ OUT PNETLOGON_DB_CHANGE *UasChangeBuffer,
+ OUT LPDWORD UasChangeSize,
+ OUT PCHAR *DbChangeInfoPointer
+ )
+/*++
+
+Routine Description:
+
+ Build and allocate an UAS_CHANGE message which describes the latest
+ account database changes.
+
+Arguments:
+
+ UasChangeBuffer - Returns a pointer to the buffer containing the message.
+ The caller is responsible for freeing the buffer using NetpMemoryFree.
+
+ UasChangeSize - Returns the size (in bytes) of the allocated buffer.
+
+ DbChangeInfoPointer - Returns the address of the DB Change info
+ within the allocated buffer. The field is not properly aligned.
+
+Return Value:
+
+ TRUE - iff the buffer could be successfully allocated.
+
+
+--*/
+{
+ PNETLOGON_DB_CHANGE UasChange;
+ DB_CHANGE_INFO DBChangeInfo;
+ ULONG DateAndTime1970;
+
+ DWORD NumDBs;
+ PCHAR Where;
+
+ DWORD i;
+ DWORD DomainSidSize;
+
+ //
+ // allocate space for this message.
+ //
+
+ DomainSidSize = RtlLengthSid( NlGlobalPrimaryDomainId );
+
+ UasChange = NetpMemoryAllocate(
+ sizeof(NETLOGON_DB_CHANGE)+
+ (NUM_DBS - 1) * sizeof(DB_CHANGE_INFO) +
+ (DomainSidSize - 1) +
+ sizeof(DWORD) // for DWORD alignment of SID
+ );
+
+ if( UasChange == NULL ) {
+
+ NlPrint((NL_CRITICAL, "NlAllocatePrimaryAnnouncement can't allocate memory\n" ));
+ return FALSE;
+ }
+
+
+ //
+ // Build the UasChange message using the latest domain modified
+ // information from SAM.
+ //
+
+ UasChange->Opcode = LOGON_UAS_CHANGE;
+
+ LOCK_CHANGELOG();
+ SmbPutUlong( &UasChange->LowSerialNumber,
+ NlGlobalChangeLogDesc.SerialNumber[SAM_DB].LowPart);
+
+ if (!RtlTimeToSecondsSince1970( &NlGlobalDBInfoArray[SAM_DB].CreationTime,
+ &DateAndTime1970 )) {
+ NlPrint((NL_CRITICAL, "DomainCreationTime can't be converted\n" ));
+ DateAndTime1970 = 0;
+ }
+ SmbPutUlong( &UasChange->DateAndTime, DateAndTime1970 );
+
+ // Tell the BDC we only intend to send pulses infrequently
+ SmbPutUlong( &UasChange->Pulse, NlGlobalPulseMaximumParameter);
+
+ // Set the randomize parameter to the value it should be for lanman
+ // BDCs. The caller will change it for NT BDCs
+ SmbPutUlong( &UasChange->Random,
+ max(NlGlobalRandomizeParameter,
+ NlGlobalPulseParameter/10) );
+
+ Where = UasChange->PrimaryDCName;
+
+ NetpLogonPutOemString( NlGlobalAnsiPrimaryName,
+ sizeof(UasChange->PrimaryDCName),
+ &Where );
+
+ NetpLogonPutOemString( NlGlobalAnsiDomainName,
+ sizeof(UasChange->DomainName),
+ &Where );
+
+ //
+ // builtin domain support
+ //
+
+ NetpLogonPutUnicodeString( NlGlobalUnicodePrimaryName,
+ sizeof(UasChange->UnicodePrimaryDCName),
+ &Where );
+
+ NetpLogonPutUnicodeString( NlGlobalUnicodeDomainName,
+ sizeof(UasChange->UnicodeDomainName),
+ &Where );
+
+ //
+ // number of database info that follow
+ //
+
+ NumDBs = NUM_DBS;
+
+ NetpLogonPutBytes( &NumDBs, sizeof(NumDBs), &Where );
+
+ *DbChangeInfoPointer = Where;
+ for( i = 0; i < NUM_DBS; i++) {
+
+ DBChangeInfo.DBIndex =
+ NlGlobalDBInfoArray[i].DBIndex;
+ DBChangeInfo.LargeSerialNumber =
+ NlGlobalChangeLogDesc.SerialNumber[i];
+ DBChangeInfo.NtDateAndTime =
+ NlGlobalDBInfoArray[i].CreationTime;
+
+ NetpLogonPutDBInfo( &DBChangeInfo, &Where );
+ }
+
+ //
+ // place domain SID in the message.
+ //
+
+ NetpLogonPutBytes( &DomainSidSize, sizeof(DomainSidSize), &Where );
+ NetpLogonPutDomainSID( NlGlobalPrimaryDomainId, DomainSidSize, &Where );
+
+ NetpLogonPutNtToken( &Where );
+ UNLOCK_CHANGELOG();
+
+
+ *UasChangeSize = (DWORD)(Where - (PCHAR)UasChange);
+ *UasChangeBuffer = UasChange;
+ return TRUE;
+}
+
+
+
+VOID
+NlPrimaryAnnouncementFinish(
+ IN PSERVER_SESSION ServerSession,
+ IN DWORD DatabaseId,
+ IN PLARGE_INTEGER SerialNumber
+ )
+/*++
+
+Routine Description:
+
+ Indicate that the specified BDC has completed replication of the specified
+ database.
+
+ Note: this BDC might not be on the pending list at at if it was doing the
+ replication on its own accord. This routine is designed to handle that
+ eventuality.
+
+Arguments:
+
+ ServerSession -- Pointer to the server session structure to remove from the
+ list.
+
+ DatabaseID -- Database ID of the database
+
+ SerialNumber -- SerialNumber of the latest delta returned to the BDC.
+ NULL indicates a full sync just completed
+
+
+Return Value:
+
+ None.
+
+--*/
+{
+ BOOLEAN SendPulse = FALSE;
+ //
+ // Mark the session that the replication of this particular database
+ // has finished.
+ //
+
+ LOCK_SERVER_SESSION_TABLE();
+ ServerSession->SsFlags &= ~NlGlobalDBInfoArray[DatabaseId].DBSessionFlag;
+
+ //
+ // If all of the databases are now replicated, OR
+ // the BDC just finished a full sync on one or more of its database,
+ // remove this BDC from the pending list.
+ //
+
+ if ( (ServerSession->SsFlags & SS_REPL_MASK) == 0 || SerialNumber == NULL ) {
+ NlPrint((NL_PULSE_MORE,
+ "NlPrimaryAnnouncementFinish: %s: all databases are now in sync on BDC\n",
+ ServerSession->SsComputerName ));
+ NlRemovePendingBdc( ServerSession );
+ SendPulse = TRUE;
+ }
+
+ //
+ // If a full sync just completed,
+ // force a partial sync so we can update our serial numbers.
+ //
+
+ if ( SerialNumber == NULL ) {
+
+ ServerSession->SsBdcDbSerialNumber[DatabaseId].QuadPart = 0;
+ ServerSession->SsFlags |= NlGlobalDBInfoArray[DatabaseId].DBSessionFlag;
+
+ //
+ // Save the current serial number for this BDC.
+ //
+
+ } else {
+ ServerSession->SsBdcDbSerialNumber[DatabaseId] = *SerialNumber;
+ }
+
+ NlPrint((NL_PULSE_MORE,
+ "NlPrimaryAnnouncementFinish: %s: " FORMAT_LPWSTR " SerialNumber: %lx %lx\n",
+ ServerSession->SsComputerName,
+ NlGlobalDBInfoArray[DatabaseId].DBName,
+ ServerSession->SsBdcDbSerialNumber[DatabaseId].HighPart,
+ ServerSession->SsBdcDbSerialNumber[DatabaseId].LowPart ));
+
+ UNLOCK_SERVER_SESSION_TABLE();
+
+ //
+ // If this BDC is finished,
+ // try to send a pulse to more BDCs.
+ //
+
+ if ( SendPulse ) {
+ NlPrimaryAnnouncement( ANNOUNCE_CONTINUE );
+ }
+}
+
+
+VOID
+NlPrimaryAnnouncementTimeout(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ The primary announcement timer has expired.
+
+ Handle timing out any BDC's that haven't responded yet.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ LARGE_INTEGER TimeNow;
+ BOOLEAN SendPulse = FALSE;
+ PLIST_ENTRY ListEntry;
+ PSERVER_SESSION ServerSession;
+
+ //
+ // Get the current time of day
+ //
+
+ (VOID) NtQuerySystemTime( &TimeNow );
+
+ //
+ // Handle each BDC that has a pulse pending.
+ //
+
+ LOCK_SERVER_SESSION_TABLE();
+
+ for ( ListEntry = NlGlobalPendingBdcList.Flink ;
+ ListEntry != &NlGlobalPendingBdcList ;
+ ListEntry = ListEntry->Flink) {
+
+
+ ServerSession = CONTAINING_RECORD( ListEntry, SERVER_SESSION, SsPendingBdcList );
+
+ //
+ // Ignore entries that haven't timed out yet.
+ //
+
+ if ( ServerSession->SsLastPulseTime.QuadPart +
+ NlGlobalPulseTimeout1.QuadPart >
+ TimeNow.QuadPart ) {
+
+ continue;
+ }
+
+ //
+ // If the pulse has been sent and there has been no response at all,
+ // OR there hasn't been another response in a VERY long time
+ // time this entry out.
+ //
+ if ( (ServerSession->SsFlags & SS_PULSE_SENT) ||
+ (ServerSession->SsLastPulseTime.QuadPart +
+ NlGlobalPulseTimeout2.QuadPart <=
+ TimeNow.QuadPart) ) {
+
+ //
+ // Increment the count of times this BDC has timed out.
+ //
+ if ( ServerSession->SsPulseTimeoutCount < MAX_PULSE_TIMEOUT ) {
+ ServerSession->SsPulseTimeoutCount++;
+ }
+
+ //
+ // Remove this entry from the queue.
+ //
+
+ NlPrint((NL_PULSE_MORE,
+ "NlPrimaryAnnouncementTimeout: %s: BDC didn't respond to pulse.\n",
+ ServerSession->SsComputerName ));
+ NlRemovePendingBdc( ServerSession );
+
+ //
+ // Indicate we should send more pulses
+ //
+
+ SendPulse = TRUE;
+
+ }
+
+ }
+
+ UNLOCK_SERVER_SESSION_TABLE();
+
+ //
+ // If any entry has timed out,
+ // try to send a pulse to more BDCs.
+ //
+
+ if ( SendPulse ) {
+ NlPrimaryAnnouncement( ANNOUNCE_CONTINUE );
+ }
+}
+
+
+
+VOID
+NlPrimaryAnnouncement(
+ IN DWORD AnnounceFlags
+ )
+/*++
+
+Routine Description:
+
+ Periodic broadcast of messages to domain containing latest
+ account database changes.
+
+Arguments:
+
+ AnnounceFlags - Flags requesting special handling of the announcement.
+
+ ANNOUNCE_FORCE -- set to indicate that the pulse should be forced to
+ all BDCs in the domain.
+
+ ANNOUNCE_CONTINUE -- set to indicate that this call is a
+ continuation of a previous call to process all entries.
+
+ ANNOUNCE_IMMEDIATE -- set to indicate that this call is a result
+ of a request for immediate replication
+
+Return Value:
+
+ None.
+
+--*/
+{
+ NTSTATUS Status;
+ PNETLOGON_DB_CHANGE UasChange;
+ DWORD UasChangeSize;
+ PCHAR DbChangeInfoPointer;
+ ULONG LanmanRandomize;
+ LARGE_INTEGER TimeNow;
+ DWORD SessionFlags;
+
+ PSERVER_SESSION ServerSession;
+ PLIST_ENTRY ListEntry;
+ static ULONG EntriesHandled = 0;
+ static BOOLEAN ImmediateAnnouncement;
+
+
+ NlPrint((NL_PULSE_MORE, "NlPrimaryAnnouncement: Entered %ld\n", AnnounceFlags ));
+
+ //
+ // Allocate the UAS_CHANGE message to send.
+ //
+
+ if ( !NlAllocatePrimaryAnnouncement( &UasChange,
+ &UasChangeSize,
+ &DbChangeInfoPointer ) ) {
+ return;
+ }
+ LanmanRandomize = SmbGetUlong( &UasChange->Random );
+
+ //
+ // Get the current time of day.
+ //
+
+ (VOID) NtQuerySystemTime( &TimeNow );
+
+ //
+ // If we need to force the pulse to all the BDCs,
+ // mark that we've not done any entries yet, and
+ // mark each entry that a pulse is needed.
+ //
+
+
+ LOCK_SERVER_SESSION_TABLE();
+ if ( AnnounceFlags & ANNOUNCE_FORCE ) {
+ EntriesHandled = 0;
+
+ for ( ListEntry = NlGlobalBdcServerSessionList.Flink ;
+ ListEntry != &NlGlobalBdcServerSessionList ;
+ ListEntry = ListEntry->Flink) {
+
+
+ ServerSession = CONTAINING_RECORD( ListEntry, SERVER_SESSION, SsBdcList );
+
+ ServerSession->SsFlags |= SS_FORCE_PULSE;
+
+ }
+
+ }
+
+ //
+ // If this isn't a continuation of a previous request to send out pulses,
+ // Reset the count of BDCs that have been handled.
+ //
+
+ if ( (AnnounceFlags & ANNOUNCE_CONTINUE) == 0 ) {
+ EntriesHandled = 0;
+
+ //
+ // Remember whether this was an immediate announcement for the
+ // initial call and all of the continuations.
+ //
+ ImmediateAnnouncement = (AnnounceFlags & ANNOUNCE_IMMEDIATE) != 0;
+ }
+
+
+ //
+ // Loop sending announcements until
+ // we have the maximum number of announcements outstanding, OR
+ // we've processed all the entries in the list.
+ //
+
+ while ( NlGlobalPendingBdcCount < NlGlobalPulseConcurrencyParameter &&
+ EntriesHandled < NlGlobalBdcServerSessionCount ) {
+
+ BOOLEAN SendPulse;
+ LPWSTR TransportName;
+
+ //
+ // If netlogon is exitting,
+ // stop sending announcements
+ //
+
+ if ( NlGlobalTerminate ) {
+ break;
+ }
+
+ //
+ // Get the server session entry for the next BDC in the list.
+ //
+ // The BDC Server Session list is maintained in the order pulses should
+ // be sent. As a pulse is sent (or is skipped), the entry is placed
+ // at the tail of the list. This gives each BDC a chance at a pulse
+ // before any BDC is repeated.
+
+ ListEntry = NlGlobalBdcServerSessionList.Flink ;
+ ServerSession = CONTAINING_RECORD( ListEntry, SERVER_SESSION, SsBdcList );
+ SendPulse = FALSE;
+ TransportName = ServerSession->SsTransportName;
+
+ //
+ // Determine if we should send an announcement to this BDC.
+ //
+ // LM BDCs are anti-social. They need a pulse every time, but
+ // they refuse to call us back when we send them one. Therefore,
+ // we'll just send them the pulse and not wait for a response.
+ //
+ //
+
+ if ( ServerSession->SsFlags & SS_LM_BDC ) {
+
+ NlPrint((NL_PULSE_MORE,
+ "NlPrimaryAnnouncement: %s: always send a pulse to a LanMan BDC\n",
+ ServerSession->SsComputerName ));
+ SendPulse = TRUE;
+ SessionFlags = 0;
+
+ //
+ // Send a pluse unconditionally if a pulse is being forced.
+ //
+
+ } else if ( ServerSession->SsFlags & SS_FORCE_PULSE ) {
+
+ NlPrint((NL_PULSE_MORE,
+ "NlPrimaryAnnouncement: %s: pulse forced to be sent\n",
+ ServerSession->SsComputerName ));
+ SendPulse = TRUE;
+ ServerSession->SsFlags &= ~SS_FORCE_PULSE;
+ SessionFlags = SS_REPL_MASK;
+ TransportName = NULL; // Send on all transports
+
+ //
+ // Only send to any other BDC if there isn't a pulse outstanding and
+ // previous announcements haven't been ignored.
+ //
+
+ } else if ( (ServerSession->SsFlags & SS_PENDING_BDC) == 0 &&
+ ServerSession->SsPulseTimeoutCount < MAX_PULSE_TIMEOUT ) {
+
+ ULONG i;
+ SessionFlags = 0;
+
+ //
+ // Only send an announcement if at least one database is out
+ // of sync.
+ //
+
+ for( i = 0; i < NUM_DBS; i++) {
+
+ //
+ // If we need to know the serial number of the BDC,
+ // force the replication.
+ //
+
+ if ( ServerSession->SsFlags &
+ NlGlobalDBInfoArray[i].DBSessionFlag ) {
+
+ NlPrint((NL_PULSE_MORE,
+ "NlPrimaryAnnouncement: %s: " FORMAT_LPWSTR " database serial number needed. Pulse sent.\n",
+ ServerSession->SsComputerName,
+ NlGlobalDBInfoArray[i].DBName ));
+ SendPulse = TRUE;
+ SessionFlags |= NlGlobalDBInfoArray[i].DBSessionFlag;
+
+ //
+ // If the BDC is out of sync with us,
+ // tell it.
+ //
+
+ } else if ( NlGlobalChangeLogDesc.SerialNumber[i].QuadPart >
+ ServerSession->SsBdcDbSerialNumber[i].QuadPart ) {
+ NlPrint((NL_PULSE_MORE,
+ "NlPrimaryAnnouncement: %s: " FORMAT_LPWSTR " database is out of sync. Pulse sent.\n",
+ ServerSession->SsComputerName,
+ NlGlobalDBInfoArray[i].DBName ));
+ SendPulse = TRUE;
+ SessionFlags |= NlGlobalDBInfoArray[i].DBSessionFlag;
+
+ }
+ }
+
+ //
+ // Fix a timing window on NT 1.0 BDCs.
+ //
+ // During promotion of a BDC to PDC, the following events occur:
+ // Two server accounts are changed on the old PDC and
+ // are marked for immediate replication.
+ // The Server manager asks the new PDC to partial sync.
+ //
+ // If the first immediate replication starts immediately, and the
+ // second immediate replication pulse is ignored because replication
+ // is in progress, and the first replication has finished the SAM
+ // database and is working on the LSA database when the server
+ // manager partial sync request comes in, then that request will be
+ // ignored (rightfully) since replication is still in progress.
+ // However, an NT 1.0 BDC will replicator thread will not go back to
+ // do the SAM database once it finishes with the LSA database. So
+ // the replicator thread terminates with the SAM database still
+ // needing replication. The server manager (rightfully) interprets
+ // this as an error.
+ //
+ // Our solution is to set the backoff period on such "immediate"
+ // replication attempts to the same value an NT 1.0 PDC would use.
+ // This typically prevents the initial replication from starting in
+ // the first place.
+ //
+ // Only do it for NT 1.0 BDCs since we're risking being overloaded.
+ //
+
+ if ( ImmediateAnnouncement &&
+ SendPulse &&
+ (ServerSession->SsFlags & SS_AUTHENTICATED) &&
+ (ServerSession->SsNegotiatedFlags & NETLOGON_SUPPORTS_PERSISTENT_BDC) == 0 ) {
+ SessionFlags = 0;
+ }
+ }
+
+ //
+ // Send a pulse unconditionally if it has been PulseMaximum since the
+ // latest pulse.
+ //
+
+ if ( !SendPulse &&
+ (ServerSession->SsLastPulseTime.QuadPart +
+ NlGlobalPulseMaximum.QuadPart <= TimeNow.QuadPart) ) {
+
+ NlPrint((NL_PULSE_MORE,
+ "NlPrimaryAnnouncement: %s: Maximum pulse since previous pulse. Pulse sent.\n",
+ ServerSession->SsComputerName ));
+ SendPulse = TRUE;
+ SessionFlags = 0;
+ TransportName = NULL; // Send on all transports
+ }
+
+ //
+ // Put this entry at the tail of the list regardless of whether
+ // we'll actually send an announcement to it.
+ //
+
+ RemoveEntryList( ListEntry );
+ InsertTailList( &NlGlobalBdcServerSessionList, ListEntry );
+ EntriesHandled ++;
+
+ //
+ // Send the announcement.
+ //
+
+ if ( SendPulse ) {
+ CHAR ComputerName[CNLEN+1];
+ PCHAR Where;
+ ULONG i;
+
+ //
+ // Add this BDC to the list of BDCs that have a pulse pending.
+ //
+ // Don't add this BDC to the list if we don't expect a response.
+ // We don't expect a response from an LM BDC. We don't expect
+ // a response from a BDC that is merely getting its PulseMaximum
+ // pulse.
+ //
+ // If we don't expect a response, set the backoff period to a
+ // large value to prevent a large load on the PDC in the case
+ // that the BDC does actuall respond.
+ //
+ // If we expect a response, set the backoff period to almost
+ // immediately since we're waiting for them.
+ //
+
+ if ( SessionFlags == 0 ) {
+ SmbPutUlong( &UasChange->Random, LanmanRandomize );
+ } else {
+ NlAddPendingBdc( ServerSession );
+ SmbPutUlong( &UasChange->Random, NlGlobalRandomizeParameter );
+ }
+
+ //
+ // Indicate that the pulse has been sent.
+ // This flag is set from the time we send the pulse until the
+ // first time the BDC responds. We use this to detect failed
+ // BDCs that have a secure channel up.
+ //
+
+ ServerSession->SsFlags &= ~SS_REPL_MASK;
+ ServerSession->SsFlags |= SS_PULSE_SENT | SessionFlags;
+ (VOID) NtQuerySystemTime( &TimeNow );
+ ServerSession->SsLastPulseTime = TimeNow;
+
+ //
+ // Don't keep the server session locked since sending the mailslot
+ // message takes a long time.
+ //
+
+ (VOID) lstrcpyA(ComputerName, ServerSession->SsComputerName );
+
+ UNLOCK_SERVER_SESSION_TABLE();
+
+ //
+ // Update the message to be specific to this BDC.
+ //
+ // If we need the BDC to respond,
+ // set the serial number to make the BDC think it has a lot
+ // of deltas to pick up.
+ //
+
+ LOCK_CHANGELOG();
+ Where = DbChangeInfoPointer;
+ for( i = 0; i < NUM_DBS; i++) {
+ LARGE_INTEGER SerialNumber;
+
+ SerialNumber = NlGlobalChangeLogDesc.SerialNumber[i];
+
+ if ( NlGlobalDBInfoArray[i].DBSessionFlag & SessionFlags ) {
+ //
+ // Don't change the high part since
+ // a) NT 1.0 BDCs will do a full sync if there are too
+ // many changes.
+ // b) The high part contains the PDC promotion count.
+ //
+ SerialNumber.LowPart = 0xFFFFFFFF;
+ }
+
+ NetpLogonUpdateDBInfo( &SerialNumber, &Where );
+ }
+ UNLOCK_CHANGELOG();
+
+
+
+ //
+ // Send the datagram to this BDC.
+ // Failure isn't fatal
+ //
+
+ Status = NlBrowserSendDatagram(
+ ComputerName,
+ TransportName,
+ NETLOGON_LM_MAILSLOT_A,
+ UasChange,
+ UasChangeSize );
+
+ if ( !NT_SUCCESS(Status) ) {
+ NlPrint((NL_CRITICAL,
+ "Cannot send datagram to '%s' 0x%lx\n",
+ ComputerName,
+ Status ));
+ }
+
+ LOCK_SERVER_SESSION_TABLE();
+
+ } else {
+ NlPrint((NL_PULSE_MORE,
+ "NlPrimaryAnnouncement: %s: pulse not needed at this time.\n",
+ ServerSession->SsComputerName ));
+ }
+
+ }
+
+ UNLOCK_SERVER_SESSION_TABLE();
+
+
+ //
+ // Free up message memory.
+ //
+
+ NetpMemoryFree( UasChange );
+
+ NlPrint((NL_PULSE_MORE, "NlPrimaryAnnouncement: Return\n" ));
+ return;
+}
+
+
+
+VOID
+NlLanmanPrimaryAnnouncement(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ Periodic broadcast of messages to domain containing latest
+ account database changes.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ NTSTATUS Status;
+ PNETLOGON_DB_CHANGE UasChange;
+ DWORD UasChangeSize;
+ PCHAR DbChangeInfoPointer;
+
+ PSERVER_SESSION ServerSession;
+ PLIST_ENTRY ListEntry;
+
+ NlPrint((NL_PULSE_MORE, "NlLanmanPrimaryAnnouncement: Entered\n" ));
+
+ //
+ // Allocate the UAS_CHANGE message to send.
+ //
+
+ if ( !NlAllocatePrimaryAnnouncement( &UasChange,
+ &UasChangeSize,
+ &DbChangeInfoPointer ) ) {
+ return;
+ }
+
+ //
+ // Send the message to each Lanman BDC.
+ //
+
+ LOCK_SERVER_SESSION_TABLE();
+
+ for ( ListEntry = NlGlobalBdcServerSessionList.Flink ;
+ ListEntry != &NlGlobalBdcServerSessionList ;
+ ListEntry = ListEntry->Flink) {
+
+
+ //
+ // Only send this message to Lanman BDCs
+ //
+
+ ServerSession = CONTAINING_RECORD( ListEntry, SERVER_SESSION, SsBdcList );
+
+ if ( (ServerSession->SsFlags & SS_LM_BDC) == 0) {
+ continue;
+ }
+
+ //
+ // If netlogon is exitting,
+ // stop sending announcements
+ //
+
+ if ( NlGlobalTerminate ) {
+ break;
+ }
+
+
+ //
+ // Send the datagram to this BDC.
+ // Failure isn't fatal
+ //
+
+ NlPrint((NL_PULSE_MORE,
+ "NlLanmanPrimaryAnnouncement: %s: pulse message sent to LANMAN BDC.\n",
+ ServerSession->SsComputerName ));
+
+ Status = NlBrowserSendDatagram(
+ ServerSession->SsComputerName,
+ ServerSession->SsTransportName,
+ NETLOGON_LM_MAILSLOT_A,
+ UasChange,
+ UasChangeSize );
+
+ if ( !NT_SUCCESS(Status) ) {
+ NlPrint((NL_CRITICAL,
+ "Cannot send datagram to '%s' 0x%lx\n",
+ ServerSession->SsComputerName,
+ Status ));
+ }
+
+
+ }
+
+ UNLOCK_SERVER_SESSION_TABLE();
+
+
+ //
+ // Free up message memory.
+ //
+
+ NetpMemoryFree( UasChange );
+
+ NlPrint((NL_PULSE_MORE, "NlLanmanPrimaryAnnouncement: Return\n" ));
+ return;
+}
+
+
+VOID
+NlAnnouncePrimaryStart(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ Announce to all machines in the domain that a
+ Primary Domain Controller is successfully up
+ and running.
+
+ announcement made with LOGON_START_PRIMARY opcode
+
+ Assumptions:
+ NlGlobalAnsiPrimaryName and NlGlobalAnsiDomainName globals initialized
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ NTSTATUS Status;
+ NETLOGON_PRIMARY NetlogonPrimary;
+ PCHAR Where;
+ PLIST_ENTRY ListEntry;
+ PSERVER_SESSION ServerSession;
+
+
+ //
+ // Build an announcement containing the name of the primary and
+ // a token indicating LM 2.0 compatibility.
+ //
+
+ NetlogonPrimary.Opcode = LOGON_START_PRIMARY;
+
+ Where = NetlogonPrimary.PrimaryDCName;
+
+ NetpLogonPutOemString( NlGlobalAnsiPrimaryName,
+ sizeof(NetlogonPrimary.PrimaryDCName),
+ &Where);
+
+ NetpLogonPutLM20Token( &Where );
+
+ //
+ // Send the message to each Lanman BDC.
+ //
+
+ LOCK_SERVER_SESSION_TABLE();
+
+ for ( ListEntry = NlGlobalBdcServerSessionList.Flink ;
+ ListEntry != &NlGlobalBdcServerSessionList ;
+ ListEntry = ListEntry->Flink) {
+
+
+ //
+ // Only send this message to Lanman BDCs
+ //
+
+ ServerSession = CONTAINING_RECORD( ListEntry, SERVER_SESSION, SsBdcList );
+
+ if ( (ServerSession->SsFlags & SS_LM_BDC) == 0) {
+ continue;
+ }
+
+ //
+ // Send the datagram.
+ //
+
+ NlPrint((NL_PULSE_MORE,
+ "NlAnnouncementPrimaryStart: %s: primary start message sent to LANMAN BDC.\n",
+ ServerSession->SsComputerName ));
+
+ Status = NlBrowserSendDatagram(
+ ServerSession->SsComputerName,
+ ServerSession->SsTransportName,
+ NETLOGON_LM_MAILSLOT_A,
+ &NetlogonPrimary,
+ (DWORD)(Where - (PCHAR)&NetlogonPrimary) );
+
+ if ( !NT_SUCCESS(Status) ) {
+ NlPrint((NL_CRITICAL,
+ "Cannot send datagram to '%s' 0x%lx\n",
+ ServerSession->SsComputerName,
+ Status ));
+ }
+
+ }
+
+ UNLOCK_SERVER_SESSION_TABLE();
+
+ return;
+}
diff --git a/private/net/svcdlls/logonsrv/server/changelg.c b/private/net/svcdlls/logonsrv/server/changelg.c
new file mode 100644
index 000000000..e017d0720
--- /dev/null
+++ b/private/net/svcdlls/logonsrv/server/changelg.c
@@ -0,0 +1,1818 @@
+/*++
+
+Copyright (c) 1987-1991 Microsoft Corporation
+
+Module Name:
+
+ changelg.c
+
+Abstract:
+
+ Change Log implementation.
+
+ This file implements the change log. It is isolated in this file
+ because it has several restrictions.
+
+ * The globals maintained by this module are initialized during
+ netlogon.dll process attach. They are cleaned up netlogon.dll
+ process detach.
+
+ * These procedures are used by SAM, LSA, and the netlogon service.
+ The LSA should be the first to load netlogon.dll. It should
+ then immediately call I_NetNotifyRole before allowing SAM or the
+ netlogon service to start.
+
+ * These procedures cannot use any globals initialized by the netlogon
+ service.
+
+Author:
+
+ Ported from Lan Man 2.0
+
+Environment:
+
+ User mode only.
+ Contains NT-specific code.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+ 22-Jul-1991 (cliffv)
+ Ported to NT. Converted to NT style.
+
+ 02-Jan-1992 (madana)
+ added support for builtin/multidomain replication.
+
+ 04-Apr-1992 (madana)
+ Added support for LSA replication.
+
+--*/
+
+//
+// Common include files.
+//
+
+#include <nt.h> // LARGE_INTEGER definition
+#include <ntrtl.h> // LARGE_INTEGER definition
+#include <nturtl.h> // LARGE_INTEGER definition
+#include <ntlsa.h> // needed by changelg.h
+
+#define NOMINMAX // Avoid redefinition of min and max in stdlib.h
+#include <rpc.h> // Needed by logon.h
+#include <logon_s.h>// includes lmcons.h, lmaccess.h, netlogon.h,
+ // ssi.h, windef.h
+#include <winbase.h>
+#include <stdio.h> // sprintf ...
+
+//
+// Include files specific to this .c file
+//
+#include <config.h> // net config helpers.
+#include <configp.h> // USE_WIN32_CONFIG (if defined), etc.
+#include <confname.h> // SECTION_ equates, NETLOGON_KEYWORD_ equates.
+#include "iniparm.h" // defaults
+
+//
+// BEWARE: Be careful about adding netlogon.dll specific include files here.
+// This module is call by SAM and LSA. The netlogon service may not yet
+// be running. Therefore, guard against referencing netlogon.dll globals
+// other than those defined in changelg.h.
+//
+
+#include <samrpc.h> // Needed by samisrv.h
+#include <samisrv.h> // Needed by changelg.h
+#include <lsarpc.h> // Needed by lsrvdata.h and logonsrv.h
+#define CHANGELOG_ALLOCATE
+#include <changelg.h> // Local procedure definitions
+#undef CHANGELOG_ALLOCATE
+
+#include <lmerrlog.h> // NELOG_* defined here ..
+#include <netlib.h> // NetpMemoryAllocate
+#include <netlibnt.h> // NetpNtStatusToApiStatus
+
+#define DEBUG_ALLOCATE
+#include <nldebug.h> // Netlogon debugging
+#undef DEBUG_ALLOCATE
+#include <align.h>
+#include <nlp.h> // NlpWriteEventlog defined here.
+
+#include <nlrepl.h> // I_Net* definitions
+#include <chworker.h> // worker functions
+#include "chutil.h" // utility functions
+
+
+enum {
+ ChangeLogPrimary,
+ ChangeLogBackup,
+ ChangeLogMemberWorkstation,
+ ChangeLogUnknown
+ } NlGlobalChangeLogRole;
+
+//
+// from parse.c
+//
+
+NET_API_STATUS
+NlParseOne(
+ IN LPNET_CONFIG_HANDLE SectionHandle,
+ IN LPWSTR Keyword,
+ IN ULONG DefaultValue,
+ IN ULONG MinimumValue,
+ IN ULONG MaximumValue,
+ OUT PULONG Value
+ );
+
+
+
+NTSTATUS
+NlSendChangeLogNotification(
+ IN enum CHANGELOG_NOTIFICATION_TYPE EntryType,
+ IN PUNICODE_STRING ObjectName,
+ IN PSID ObjectSid,
+ IN ULONG ObjectRid
+ )
+/*++
+
+Routine Description:
+
+ Put a ChangeLog Notification entry for netlogon to pick up.
+
+Arguments:
+
+ EntryType - The type of the entry being inserted
+
+ ObjectName - The name of the account being changed.
+
+ ObjectSid - Sid of the account be changed.
+
+ ObjectRid - Rid of the object being changed.
+
+Return Value:
+
+ Status of the operation.
+
+--*/
+{
+ PCHANGELOG_NOTIFICATION Notification;
+ LPBYTE Where;
+ ULONG SidSize = 0;
+ ULONG NameSize = 0;
+ ULONG Size;
+
+ //
+ // If the netlogon service isn't running (or at least starting),
+ // don't queue messages to it.
+ //
+
+ if( NlGlobalChangeLogNetlogonState == NetlogonStopped ) {
+ return STATUS_SUCCESS;
+ }
+
+ //
+ // Allocate a buffer for the object name.
+ //
+
+ if ( ObjectSid != NULL ) {
+ SidSize = RtlLengthSid( ObjectSid );
+ }
+
+ if ( ObjectName != NULL ) {
+ NameSize = ObjectName->Length + sizeof(WCHAR);
+ }
+
+ Size = sizeof(*Notification) + SidSize + NameSize;
+ Size = ROUND_UP_COUNT( Size, ALIGN_WORST );
+
+ Notification = NetpMemoryAllocate( Size );
+
+ if ( Notification == NULL ) {
+ return STATUS_NO_MEMORY;
+ }
+
+ Notification->EntryType = EntryType;
+ Notification->ObjectRid = ObjectRid;
+
+ Where = (LPBYTE) (Notification + 1);
+
+ //
+ // Copy the object sid into the buffer.
+ //
+
+ if ( ObjectSid != NULL ) {
+ RtlCopyMemory( Where, ObjectSid, SidSize );
+ Notification->ObjectSid = (PSID) Where;
+ Where += SidSize;
+ } else {
+ Notification->ObjectSid = NULL;
+ }
+
+
+ //
+ // Copy the new server name into the buffer.
+ //
+
+ if ( ObjectName != NULL ) {
+ Where = ROUND_UP_POINTER( Where, ALIGN_WCHAR );
+ RtlCopyMemory( Where, ObjectName->Buffer, ObjectName->Length );
+ ((LPWSTR)Where)[ObjectName->Length/sizeof(WCHAR)] = L'\0';
+
+ RtlInitUnicodeString( &Notification->ObjectName, (LPWSTR)Where);
+ Where += NameSize;
+ } else {
+ RtlInitUnicodeString( &Notification->ObjectName, NULL);
+ }
+
+ //
+ // Indicate we're about to send the event.
+ //
+
+ NlPrint((NL_CHANGELOG,
+ "NlSendChangeLogNotification: sent %ld for %wZ Rid: 0x%lx Sid: ",
+ Notification->EntryType,
+ &Notification->ObjectName,
+ Notification->ObjectRid ));
+ NlpDumpSid( NL_CHANGELOG, Notification->ObjectSid );
+
+
+
+ //
+ // Insert the entry into the list
+ //
+
+ LOCK_CHANGELOG();
+ InsertTailList( &NlGlobalChangeLogNotifications, &Notification->Next );
+ UNLOCK_CHANGELOG();
+
+ if ( !SetEvent( NlGlobalChangeLogEvent ) ) {
+ NlPrint((NL_CRITICAL,
+ "Cannot set ChangeLog event: %lu\n",
+ GetLastError() ));
+ }
+
+ return STATUS_SUCCESS;
+}
+
+
+VOID
+NlLmBdcListSet(
+ IN ULONG LmBdcCount,
+ IN PULONG LmBdcRidArray
+ )
+/*++
+
+Routine Description:
+
+ Set the list of LM BDCs to the specified list.
+
+Arguments:
+
+ LmBdcCount - Number of BDCs in the list
+
+ LmBdcRidArray - Array of Rids of Lanman BDC accounts.
+
+Return Value:
+
+ None
+
+--*/
+{
+ //
+ // If a previous array exists,
+ // delete it.
+ //
+ LOCK_CHANGELOG();
+ if ( NlGlobalLmBdcRidArray != NULL ) {
+ NetpMemoryFree( NlGlobalLmBdcRidArray );
+ NlGlobalLmBdcRidArray = NULL;
+ NlGlobalLmBdcCount = 0;
+ }
+
+ //
+ // Allocate the new array.
+ //
+
+ NlGlobalLmBdcRidArray = NetpMemoryAllocate( LmBdcCount * sizeof(ULONG) );
+
+ if ( NlGlobalLmBdcRidArray != NULL ) {
+ RtlCopyMemory( NlGlobalLmBdcRidArray,
+ LmBdcRidArray,
+ LmBdcCount * sizeof(ULONG) );
+ NlGlobalLmBdcCount = LmBdcCount;
+ }
+ UNLOCK_CHANGELOG();
+}
+
+
+PULONG
+NlLmBdcListFind(
+ IN ULONG Rid
+ )
+/*++
+
+Routine Description:
+
+ Returns a pointer to the specified RID in the LM BDC list.
+
+ Enter with the change log crit sect locked.
+
+Arguments:
+
+ Rid - Rid of the Lanman BDC being found
+
+Return Value:
+
+ NULL, if the entry can't be found
+
+--*/
+{
+ ULONG i;
+
+ //
+ // Simply loop through the array entries.
+ //
+
+ for ( i=0; i<NlGlobalLmBdcCount; i++ ) {
+ if ( NlGlobalLmBdcRidArray[i] == Rid ) {
+ return &NlGlobalLmBdcRidArray[i];
+ }
+ }
+
+ return NULL;
+}
+
+
+
+VOID
+NlLmBdcListAdd(
+ IN ULONG Rid
+ )
+/*++
+
+Routine Description:
+
+ Add the specified RID to the LM BDC list.
+
+ Notify the changelog worker thread and the netlogon service of the new BDC.
+
+Arguments:
+
+ Rid - Rid of the Lanman BDC being added
+
+Return Value:
+
+ None
+
+--*/
+{
+ //
+ // Ensure the RID doesn't already exist in the array.
+ //
+
+ LOCK_CHANGELOG();
+
+ if ( NlLmBdcListFind( Rid ) == NULL ) {
+
+ //
+ // Allocate a larger array.
+ // (NetpMemoryReallocate properly handles the case where the
+ // array didn't previously exist.)
+ //
+
+ NlGlobalLmBdcRidArray = NetpMemoryReallocate(
+ NlGlobalLmBdcRidArray,
+ (NlGlobalLmBdcCount+1) * sizeof(ULONG) );
+
+ if ( NlGlobalLmBdcRidArray == NULL ) {
+ NlGlobalLmBdcCount = 0;
+ UNLOCK_CHANGELOG();
+ return;
+ }
+
+ //
+ // Set the RID into the array entry.
+ //
+
+ NlGlobalLmBdcRidArray[NlGlobalLmBdcCount] = Rid;
+ NlGlobalLmBdcCount ++;
+ NlPrint((NL_CHANGELOG,
+ "NlLmBdcListAdd: Lm Bdc 0x%lx added (%ld)\n",
+ Rid,
+ NlGlobalLmBdcCount ));
+
+ //
+ // Start the changelog worker thread if it's not running.
+ //
+
+ (VOID) NlStartChangeLogWorkerThread();
+
+ //
+ // Tell netlogon that a downlevel BDC has been added.
+ //
+
+ (VOID) NlSendChangeLogNotification(
+ ChangeLogLmServerAdded,
+ NULL,
+ NULL,
+ Rid );
+
+ }
+
+ UNLOCK_CHANGELOG();
+}
+
+
+
+VOID
+NlLmBdcListDel(
+ IN ULONG Rid
+ )
+/*++
+
+Routine Description:
+
+ Delete the specified RID from the LM BDC list.
+
+ This routine is specifically designed to be called on ALL user account
+ deletions. Since the user account might have been a member of the servers
+ group, all user account deletions are checked to see if they represent
+ an LM BDC.
+
+ Notify the changelog worker thread and the netlogon service of the deleted BDC.
+
+Arguments:
+
+ Rid - Rid of the Lanman BDC being deleted.
+
+Return Value:
+
+ None
+
+--*/
+{
+ PULONG RidEntry;
+
+ //
+ // If the entry exists,
+ // delete it by copying the last entry of the array on top of this one
+ // and making the array one entry smaller.
+ //
+
+ LOCK_CHANGELOG();
+
+ RidEntry = NlLmBdcListFind( Rid );
+
+ if ( RidEntry != NULL ) {
+ *RidEntry = NlGlobalLmBdcRidArray[ NlGlobalLmBdcCount-1 ];
+ NlGlobalLmBdcCount --;
+ NlPrint((NL_CHANGELOG,
+ "NlLmBdcListDel: Lm Bdc 0x%lx deleted (%ld)\n",
+ Rid,
+ NlGlobalLmBdcCount ));
+
+ if ( NlGlobalLmBdcCount == 0 ) {
+ NetpMemoryFree( NlGlobalLmBdcRidArray );
+ NlGlobalLmBdcRidArray = NULL;
+ }
+
+ //
+ // worker thread must be running now
+ //
+
+ if( IsChangeLogWorkerRunning() ) {
+
+ (VOID) NlAddWorkerQueueEntry( ServersGroupDel, 0 );
+
+ } else {
+ NlAssert( FALSE );
+ }
+
+ //
+ // Tell netlogon that a downlevel BDC has been removed.
+ //
+
+ (VOID) NlSendChangeLogNotification(
+ ChangeLogLmServerDeleted,
+ NULL,
+ NULL,
+ Rid );
+ }
+
+ UNLOCK_CHANGELOG();
+}
+
+
+
+NTSTATUS
+I_NetNotifyDelta (
+ IN SECURITY_DB_TYPE DbType,
+ IN LARGE_INTEGER SerialNumber,
+ IN SECURITY_DB_DELTA_TYPE DeltaType,
+ IN SECURITY_DB_OBJECT_TYPE ObjectType,
+ IN ULONG ObjectRid,
+ IN PSID ObjectSid,
+ IN PUNICODE_STRING ObjectName,
+ IN DWORD ReplicateImmediately,
+ IN PSAM_DELTA_DATA MemberId
+ )
+/*++
+
+Routine Description:
+
+ This function is called by the SAM and LSA services after each
+ change is made to the SAM and LSA databases. The services describe
+ the type of object that is modified, the type of modification made
+ on the object, the serial number of this modification etc. This
+ information is stored for later retrieval when a BDC or member
+ server wants a copy of this change. See the description of
+ I_NetSamDeltas for a description of how the change log is used.
+
+ Add a change log entry to circular change log maintained in cache as
+ well as on the disk and update the head and tail pointers
+
+ It is assumed that Tail points to a block where this new change log
+ entry may be stored.
+
+Arguments:
+
+ DbType - Type of the database that has been modified.
+
+ SerialNumber - The value of the DomainModifiedCount field for the
+ domain following the modification.
+
+ DeltaType - The type of modification that has been made on the object.
+
+ ObjectType - The type of object that has been modified.
+
+ ObjectRid - The relative ID of the object that has been modified.
+ This parameter is valid only when the object type specified is
+ either SecurityDbObjectSamUser, SecurityDbObjectSamGroup or
+ SecurityDbObjectSamAlias otherwise this parameter is set to zero.
+
+ ObjectSid - The SID of the object that has been modified. If the object
+ modified is in a SAM database, ObjectSid is the DomainId of the Domain
+ containing the object.
+
+ ObjectName - The name of the secret object when the object type
+ specified is SecurityDbObjectLsaSecret or the old name of the object
+ when the object type specified is either SecurityDbObjectSamUser,
+ SecurityDbObjectSamGroup or SecurityDbObjectSamAlias and the delta
+ type is SecurityDbRename otherwise this parameter is set to NULL.
+
+ ReplicateImmediately - TRUE if the change should be immediately
+ replicated to all BDCs. A password change should set the flag
+ TRUE.
+
+ MemberId - This parameter is specified when group/alias membership
+ is modified. This structure will then point to the member's ID that
+ has been updated.
+
+Return Value:
+
+ STATUS_SUCCESS - The Service completed successfully.
+
+--*/
+{
+ NTSTATUS Status;
+ CHANGELOG_ENTRY ChangeLogEntry;
+ NETLOGON_DELTA_TYPE NetlogonDeltaType;
+ USHORT Flags = 0;
+ BOOL LanmanReplicateImmediately = FALSE;
+
+ //
+ // Ensure the role is right. Otherwise, all the globals used below
+ // aren't initialized.
+ //
+
+ if ( NlGlobalChangeLogRole != ChangeLogPrimary ) {
+ return STATUS_INVALID_DOMAIN_ROLE;
+ }
+
+ //
+ // Also make sure that the change log cache is available.
+ //
+
+ if ( NlGlobalChangeLogDesc.Buffer == NULL ) {
+ return STATUS_INVALID_DOMAIN_ROLE;
+ }
+
+
+ //
+ // Determine the database index.
+ //
+
+ if( DbType == SecurityDbLsa ) {
+
+ ChangeLogEntry.DBIndex = LSA_DB;
+
+ } else if( DbType == SecurityDbSam ) {
+
+ if ( RtlEqualSid( ObjectSid, NlGlobalChWorkerBuiltinDomainSid )) {
+
+ ChangeLogEntry.DBIndex = BUILTIN_DB;
+
+ } else {
+
+ ChangeLogEntry.DBIndex = SAM_DB;
+
+ }
+
+ //
+ // For the SAM database, we no longer need the ObjectSid.
+ // Null out the pointer to prevent us from storing it in the
+ // changelog.
+ //
+
+ ObjectSid = NULL;
+
+ } else {
+
+ //
+ // unknown database, do nothing.
+ //
+
+ return STATUS_SUCCESS;
+ }
+
+
+
+ //
+ // Map object type and delta type to NetlogonDeltaType
+ //
+
+ switch( ObjectType ) {
+ case SecurityDbObjectLsaPolicy:
+
+ switch (DeltaType) {
+ case SecurityDbNew:
+ case SecurityDbChange:
+ NetlogonDeltaType = AddOrChangeLsaPolicy;
+ break;
+
+ // unknown delta type
+ default:
+ return STATUS_SUCCESS;
+ }
+ break;
+
+
+ case SecurityDbObjectLsaTDomain:
+
+ switch (DeltaType) {
+ case SecurityDbNew:
+ case SecurityDbChange:
+ NetlogonDeltaType = AddOrChangeLsaTDomain;
+
+ //
+ // Tell the netlogon service to update its in-memory list now.
+ //
+ (VOID) NlSendChangeLogNotification( ChangeLogTrustAdded,
+ NULL,
+ ObjectSid,
+ 0 );
+ break;
+
+ case SecurityDbDelete:
+ NetlogonDeltaType = DeleteLsaTDomain;
+
+ //
+ // Tell the netlogon service to update its in-memory list now.
+ //
+ (VOID) NlSendChangeLogNotification( ChangeLogTrustDeleted,
+ NULL,
+ ObjectSid,
+ 0 );
+ break;
+
+ // unknown delta type
+ default:
+ return STATUS_SUCCESS;
+ }
+ break;
+
+
+ case SecurityDbObjectLsaAccount:
+
+ switch (DeltaType) {
+ case SecurityDbNew:
+ case SecurityDbChange:
+ NetlogonDeltaType = AddOrChangeLsaAccount;
+ break;
+
+ case SecurityDbDelete:
+ NetlogonDeltaType = DeleteLsaAccount;
+ break;
+
+ // unknown delta type
+ default:
+ return STATUS_SUCCESS;
+ }
+ break;
+
+
+ case SecurityDbObjectLsaSecret:
+
+ switch (DeltaType) {
+ case SecurityDbNew:
+ case SecurityDbChange:
+ NetlogonDeltaType = AddOrChangeLsaSecret;
+ break;
+
+ case SecurityDbDelete:
+ NetlogonDeltaType = DeleteLsaSecret;
+ break;
+
+ // unknown delta type
+ default:
+ return STATUS_SUCCESS;
+ }
+ break;
+
+
+ case SecurityDbObjectSamDomain:
+
+ switch (DeltaType) {
+ case SecurityDbNew:
+ case SecurityDbChange:
+ NetlogonDeltaType = AddOrChangeDomain;
+ break;
+
+ // unknown delta type
+ default:
+ return STATUS_SUCCESS;
+ }
+ break;
+
+ case SecurityDbObjectSamUser:
+
+ switch (DeltaType) {
+ case SecurityDbChangePassword:
+ Flags |= CHANGELOG_PASSWORD_CHANGE;
+ LanmanReplicateImmediately = TRUE;
+ NetlogonDeltaType = AddOrChangeUser;
+ break;
+
+ case SecurityDbNew:
+
+ //
+ // For down-level system, a newly added user needs to
+ // have it's membership in "Domain Users" updated, too.
+ // The following worker entry will add the additional
+ // delta entry and increment the serial number
+ // accordingly.
+ //
+
+ LOCK_CHANGELOG();
+ if( IsChangeLogWorkerRunning() ) {
+ (VOID) NlAddWorkerQueueEntry( ChangeLogAddUser, ObjectRid );
+ }
+ UNLOCK_CHANGELOG();
+
+ NetlogonDeltaType = AddOrChangeUser;
+ break;
+
+ case SecurityDbChange:
+ NetlogonDeltaType = AddOrChangeUser;
+ break;
+
+ //
+ // This is a dummy delta sent by chworker to indicate that "Domain Users"
+ // was added as a member of this user.
+ //
+ case SecurityDbChangeMemberAdd:
+ Flags |= CHANGELOG_DOMAINUSERS_CHANGED;
+ NetlogonDeltaType = AddOrChangeUser;
+ break;
+
+ case SecurityDbDelete:
+
+ //
+ // This might be a Lanman BDC so check to be sure.
+ //
+
+ NlLmBdcListDel( ObjectRid );
+
+ NetlogonDeltaType = DeleteUser;
+ break;
+
+
+ case SecurityDbRename:
+ NetlogonDeltaType = RenameUser;
+
+ //
+ // For down-level system, Rename user is handled as two
+ // deltas, viz. 1) Delete old user and 2) Add new user.
+ // The following worker entry will add the additional
+ // delta entry and increment the serial number
+ // accordingly.
+ //
+
+ LOCK_CHANGELOG();
+
+ if( IsChangeLogWorkerRunning() ) {
+ (VOID) NlAddWorkerQueueEntry( ChangeLogRenameUser, ObjectRid );
+ }
+
+ UNLOCK_CHANGELOG();
+
+ break;
+
+ //
+ // unknown delta type
+ //
+
+ default:
+ return STATUS_SUCCESS;
+ }
+
+ break;
+
+ case SecurityDbObjectSamGroup:
+
+ switch ( DeltaType ) {
+ case SecurityDbNew:
+ case SecurityDbChange:
+ NetlogonDeltaType = AddOrChangeGroup;
+ break;
+
+ case SecurityDbDelete:
+ NetlogonDeltaType = DeleteGroup;
+
+ //
+ // when a global group is deleted, we also delete it
+ // from the special group list, if it is included
+ // in the list.
+ //
+
+ LOCK_CHANGELOG();
+
+ if( IsChangeLogWorkerRunning() ) {
+
+ PGLOBAL_GROUP_ENTRY GroupEntry;
+
+ GroupEntry = NlGetGroupEntry (
+ &NlGlobalSpecialServerGroupList,
+ ObjectRid );
+
+ if( GroupEntry != NULL ) {
+
+ RemoveEntryList( &GroupEntry->Next );
+ NetpMemoryFree( GroupEntry );
+ }
+
+ }
+
+ UNLOCK_CHANGELOG();
+ break;
+
+ case SecurityDbRename:
+ NetlogonDeltaType = RenameGroup;
+
+ //
+ // For down-level system, Rename group is handled as
+ // three deltas, viz. 1) Delete old group, 2) Add new
+ // group and 3. Changemembership of new group. The
+ // following worker entry will add the additional
+ // two delta entries and increment the serial number
+ // accordingly.
+ //
+
+ LOCK_CHANGELOG();
+
+ if( IsChangeLogWorkerRunning() ) {
+ (VOID) NlAddWorkerQueueEntry( ChangeLogRenameGroup, ObjectRid );
+
+ }
+
+ UNLOCK_CHANGELOG();
+
+ break;
+
+ case SecurityDbChangeMemberAdd:
+ case SecurityDbChangeMemberSet:
+ case SecurityDbChangeMemberDel: {
+
+ UNICODE_STRING ServersGroup;
+
+ NetlogonDeltaType = ChangeGroupMembership;
+
+ //
+ // without object name we can't do much here.
+ //
+ if( ObjectName == NULL ) {
+ break;
+ }
+
+ //
+ // do something for down level
+ //
+
+ RtlInitUnicodeString( &ServersGroup, SSI_SERVER_GROUP_W );
+
+ LOCK_CHANGELOG();
+
+ if( RtlEqualUnicodeString(
+ &ServersGroup, ObjectName, (BOOLEAN)TRUE ) ) {
+
+ //
+ // Handle a new LM BDC.
+ //
+
+ if( DeltaType == SecurityDbChangeMemberAdd ) {
+
+ NlLmBdcListAdd( MemberId->GroupMemberId.MemberRid );
+
+
+ //
+ // Handle an LM BDC being deleted.
+ //
+
+ } else if( DeltaType == SecurityDbChangeMemberDel ) {
+
+ NlLmBdcListDel( MemberId->GroupMemberId.MemberRid );
+ }
+
+ } else {
+
+ if( IsChangeLogWorkerRunning() ) {
+
+ //
+ // Change log work is running. If the global groups
+ // list watched is empty, add this delta in the
+ // queue anyway, otherwise add this delta to entry
+ // only if this group is monitored.
+ //
+
+ if( IsListEmpty( &NlGlobalSpecialServerGroupList ) ||
+
+ ( NlGetGroupEntry(
+ &NlGlobalSpecialServerGroupList,
+ ObjectRid ) != NULL ) ) {
+
+
+ (VOID) NlAddWorkerQueueEntry(
+ ChangeLogGroupMembership,
+ MemberId->GroupMemberId.MemberRid );
+
+ }
+ }
+ }
+
+ UNLOCK_CHANGELOG();
+
+ break;
+ }
+
+ //
+ // unknown delta type
+ //
+ default:
+ return STATUS_SUCCESS;
+ }
+ break;
+
+ case SecurityDbObjectSamAlias:
+
+ switch (DeltaType) {
+ case SecurityDbNew:
+ case SecurityDbChange:
+ NetlogonDeltaType = AddOrChangeAlias;
+ break;
+
+ case SecurityDbDelete:
+ NetlogonDeltaType = DeleteAlias;
+ break;
+
+ case SecurityDbRename:
+ NetlogonDeltaType = RenameAlias;
+ break;
+
+ case SecurityDbChangeMemberAdd:
+ case SecurityDbChangeMemberSet:
+ case SecurityDbChangeMemberDel:
+
+ NetlogonDeltaType = ChangeAliasMembership;
+
+ LOCK_CHANGELOG();
+
+ //
+ // if this delta is BUILTIN domain delta and the group
+ // modified is special group then add this delta to
+ // workers queue if it is running.
+ //
+
+ if ( (ChangeLogEntry.DBIndex == BUILTIN_DB) &&
+ ( IsChangeLogWorkerRunning() ) &&
+ ( IsSpecialLocalGroup( ObjectRid ) ) ) {
+
+ ULONG Rid;
+ PUCHAR SubAuthorityCount;
+ BOOLEAN EqualSid;
+
+ //
+ // if the member modified belongs to the local SAM
+ // database.
+ //
+
+ SubAuthorityCount =
+ RtlSubAuthorityCountSid(
+ MemberId->AliasMemberId.MemberSid);
+
+ (*SubAuthorityCount)--;
+
+ if( NlGlobalChWorkerSamDomainSid != NULL ) {
+
+ EqualSid = RtlEqualSid(
+ NlGlobalChWorkerSamDomainSid,
+ MemberId->AliasMemberId.MemberSid);
+ } else {
+ EqualSid = FALSE;
+ }
+
+ (*SubAuthorityCount)++;
+
+ if( EqualSid ) {
+
+ Rid = *RtlSubAuthoritySid(
+ MemberId->AliasMemberId.MemberSid,
+ (*SubAuthorityCount) -1 );
+
+ (VOID) NlAddWorkerQueueEntry(
+ ChangeLogAliasMembership,
+ Rid );
+
+
+ //
+ // add this member in the global group list,
+ // since this member may be a global group and we
+ // don't want to miss any delta made on this group.
+ // Worker thread will adjust the list and remove
+ // unwanted user entries from the list.
+ //
+
+ Status = NlAddGroupEntry(
+ &NlGlobalSpecialServerGroupList,
+ Rid );
+
+ if ( !NT_SUCCESS(Status) ) {
+
+ NlPrint((NL_CRITICAL,
+ "NlAddGroupEntry failed %lx\n",
+ Status ) );
+ }
+ }
+ }
+
+ UNLOCK_CHANGELOG();
+
+ break;
+
+ // unknown delta type
+ default:
+ return STATUS_SUCCESS;
+ }
+ break;
+
+ default:
+
+ // unknown object type
+ return STATUS_SUCCESS;
+
+ }
+
+
+ //
+ // Build the changelog entry and write it to the changelog
+ //
+
+ ChangeLogEntry.DeltaType = NetlogonDeltaType;
+ ChangeLogEntry.SerialNumber = SerialNumber;
+ ChangeLogEntry.ObjectRid = ObjectRid;
+ ChangeLogEntry.Flags = ReplicateImmediately ? CHANGELOG_REPLICATE_IMMEDIATELY : 0;
+ ChangeLogEntry.Flags |= Flags;
+
+ (VOID) NlWriteChangeLogEntry( &NlGlobalChangeLogDesc, &ChangeLogEntry, ObjectSid, ObjectName, TRUE );
+
+
+ //
+ // If this change requires immediate replication, do so
+ //
+
+ if( ReplicateImmediately ) {
+
+ LOCK_CHANGELOG();
+ NlGlobalChangeLogReplicateImmediately = TRUE;
+ UNLOCK_CHANGELOG();
+
+ if ( !SetEvent( NlGlobalChangeLogEvent ) ) {
+ NlPrint((NL_CRITICAL,
+ "Cannot set ChangeLog event: %lu\n",
+ GetLastError() ));
+ }
+
+
+ //
+ // If this change requires immediate replication to Lanman BDCs, do so
+ //
+
+ } else if( LanmanReplicateImmediately ) {
+
+ LOCK_CHANGELOG();
+ NlGlobalChangeLogLanmanReplicateImmediately = TRUE;
+ UNLOCK_CHANGELOG();
+
+ if ( !SetEvent( NlGlobalChangeLogEvent ) ) {
+ NlPrint((NL_CRITICAL,
+ "Cannot set ChangeLog event: %lu\n",
+ GetLastError() ));
+ }
+
+ }
+
+ return STATUS_SUCCESS;
+}
+
+
+
+
+NTSTATUS
+NlInitChangeLogBuffer(
+ VOID
+)
+/*++
+
+Routine Description:
+
+ Open the change log file (netlogon.chg) for reading or writing one or
+ more records. Create this file if it does not exist or is out of
+ sync with the SAM database (see note below).
+
+ This file must be opened for R/W (deny-none share mode) at the time
+ the cache is initialized. If the file already exists when NETLOGON
+ service started, its contents will be cached in its entirety
+ provided the last change log record bears the same serial number as
+ the serial number field in SAM database else this file will be
+ removed and a new one created. If the change log file did not exist
+ then it will be created.
+
+Arguments:
+
+ NONE
+
+Return Value:
+
+ NT Status code
+
+--*/
+{
+ NTSTATUS Status;
+ NET_API_STATUS NetStatus;
+
+ UINT WindowsDirectoryLength;
+ WCHAR ChangeLogFile[PATHLEN+1];
+
+ LPNET_CONFIG_HANDLE SectionHandle = NULL;
+ DWORD NewChangeLogSize;
+
+ //
+ // Initialize
+ //
+
+ LOCK_CHANGELOG();
+
+
+ //
+ // Get the size of the changelog.
+
+
+ //
+ // Open the NetLogon configuration section.
+ //
+
+ NewChangeLogSize = DEFAULT_CHANGELOGSIZE;
+ NetStatus = NetpOpenConfigData(
+ &SectionHandle,
+ NULL, // no server name.
+#if defined(USE_WIN32_CONFIG)
+ SERVICE_NETLOGON,
+#else
+ SECT_NT_NETLOGON, // section name
+#endif
+ TRUE ); // we only want readonly access
+
+ if ( NetStatus == NO_ERROR ) {
+
+ (VOID) NlParseOne( SectionHandle,
+ NETLOGON_KEYWORD_CHANGELOGSIZE,
+ DEFAULT_CHANGELOGSIZE,
+ MIN_CHANGELOGSIZE,
+ MAX_CHANGELOGSIZE,
+ &NewChangeLogSize );
+
+ (VOID) NetpCloseConfigData( SectionHandle );
+ }
+
+ NewChangeLogSize = ROUND_UP_COUNT( NewChangeLogSize, ALIGN_WORST);
+
+ NlPrint((NL_INIT, "ChangeLogSize: 0x%lx\n", NewChangeLogSize ));
+
+
+ //
+ // Build the change log file name
+ //
+
+ WindowsDirectoryLength = GetWindowsDirectoryW(
+ NlGlobalChangeLogFilePrefix,
+ sizeof(NlGlobalChangeLogFilePrefix)/sizeof(WCHAR) );
+
+ if ( WindowsDirectoryLength == 0 ) {
+
+ NlPrint((NL_CRITICAL,"Unable to get changelog file directory name, "
+ "WinError = %ld \n", GetLastError() ));
+
+ NlGlobalChangeLogFilePrefix[0] = L'\0';
+ goto CleanChangeLogFile;
+ }
+
+ if ( WindowsDirectoryLength * sizeof(WCHAR) + sizeof(CHANGELOG_FILE_PREFIX) +
+ CHANGELOG_FILE_POSTFIX_LENGTH * sizeof(WCHAR)
+ > sizeof(NlGlobalChangeLogFilePrefix) ) {
+
+ NlPrint((NL_CRITICAL,"Changelog file directory name length is "
+ "too long \n" ));
+
+ NlGlobalChangeLogFilePrefix[0] = L'\0';
+ goto CleanChangeLogFile;
+ }
+
+ wcscat( NlGlobalChangeLogFilePrefix, CHANGELOG_FILE_PREFIX );
+
+
+ //
+ // Read in the existing changelog file.
+ //
+
+ wcscpy( ChangeLogFile, NlGlobalChangeLogFilePrefix );
+ wcscat( ChangeLogFile, CHANGELOG_FILE_POSTFIX );
+
+ InitChangeLogDesc( &NlGlobalChangeLogDesc );
+ Status = NlOpenChangeLogFile( ChangeLogFile, &NlGlobalChangeLogDesc, FALSE );
+
+ if ( !NT_SUCCESS(Status) ) {
+ goto CleanChangeLogFile;
+ }
+
+
+ //
+ // Convert the changelog file to the right size/version.
+ //
+
+ Status = NlResizeChangeLogFile( &NlGlobalChangeLogDesc, NewChangeLogSize );
+
+ if ( !NT_SUCCESS(Status) ) {
+ goto CleanChangeLogFile;
+ }
+
+ goto Cleanup;
+
+
+ //
+ // CleanChangeLogFile
+ //
+
+CleanChangeLogFile:
+
+ //
+ // If we just need to start with a newly initialized file,
+ // do it.
+ //
+
+ Status = NlResetChangeLog( &NlGlobalChangeLogDesc, NewChangeLogSize );
+
+Cleanup:
+
+ //
+ // start changelog worker thread
+ //
+
+ if ( NT_SUCCESS(Status) ) {
+
+ if ( NlGlobalChangeLogRole == ChangeLogPrimary ) {
+ (VOID)NlStartChangeLogWorkerThread();
+ }
+
+ //
+ // Free any resources on error.
+ //
+
+ } else {
+ NlCloseChangeLogFile( &NlGlobalChangeLogDesc );
+ }
+
+ UNLOCK_CHANGELOG();
+
+ return Status;
+}
+
+
+NTSTATUS
+I_NetNotifyRole (
+ IN POLICY_LSA_SERVER_ROLE Role
+ )
+/*++
+
+Routine Description:
+
+ This function is called by the LSA service upon LSA initialization
+ and when LSA changes domain role. This routine will initialize the
+ change log cache if the role specified is PDC or delete the change
+ log cache if the role specified is other than PDC.
+
+ When this function initializing the change log if the change log
+ currently exists on disk, the cache will be initialized from disk.
+ LSA should treat errors from this routine as non-fatal. LSA should
+ log the errors so they may be corrected then continue
+ initialization. However, LSA should treat the system databases as
+ read-only in this case.
+
+Arguments:
+
+ Role - Current role of the server.
+
+Return Value:
+
+ STATUS_SUCCESS - The Service completed successfully.
+
+--*/
+{
+ NTSTATUS Status;
+
+ //
+ // If the netlogon service is running,
+ // then we can't change role so simply return.
+ //
+
+ if( NlGlobalChangeLogNetlogonState != NetlogonStopped ) {
+ return STATUS_SUCCESS;
+ }
+
+ //
+ // If this is a workstation, simply return.
+ //
+
+ if ( NlGlobalChangeLogRole == ChangeLogMemberWorkstation ) {
+ return STATUS_SUCCESS;
+ }
+
+ //
+ // Set our role to the new value.
+ //
+
+ if( Role == PolicyServerRolePrimary) {
+
+ NlGlobalChangeLogRole = ChangeLogPrimary;
+
+ } else {
+
+ NlGlobalChangeLogRole = ChangeLogBackup;
+ }
+
+ //
+ // Delete any previous change log buffer and initialize it again.
+ // (This allows the size to be changed on every role change.)
+ //
+
+ NlCloseChangeLogFile( &NlGlobalChangeLogDesc );
+
+ Status = NlInitChangeLogBuffer();
+
+ return Status;
+}
+
+
+
+NTSTATUS
+I_NetNotifyMachineAccount (
+ IN ULONG ObjectRid,
+ IN PSID DomainSid,
+ IN ULONG OldUserAccountControl,
+ IN ULONG NewUserAccountControl,
+ IN PUNICODE_STRING ObjectName
+ )
+/*++
+
+Routine Description:
+
+ This function is called by the SAM to indicate that the account type
+ of a machine account has changed. Specifically, if
+ USER_INTERDOMAIN_TRUST_ACCOUNT, USER_WORKSTATION_TRUST_ACCOUNT, or
+ USER_SERVER_TRUST_ACCOUNT change for a particular account, this
+ routine is called to let Netlogon know of the account change.
+
+ This function is called for both PDC and BDC.
+
+Arguments:
+
+ ObjectRid - The relative ID of the object that has been modified.
+
+ DomainSid - Specifies the SID of the Domain containing the object.
+
+ OldUserAccountControl - Specifies the previous value of the
+ UserAccountControl field of the user.
+
+ NewUserAccountControl - Specifies the new (current) value of the
+ UserAccountControl field of the user.
+
+ ObjectName - The name of the account being changed.
+
+Return Value:
+
+ Status of the operation.
+
+--*/
+{
+ NTSTATUS Status;
+
+ //
+ // If the netlogon service isn't running,
+ // Don't bother with the coming and going of accounts.
+ //
+
+ if( NlGlobalChangeLogNetlogonState == NetlogonStopped ) {
+ return(STATUS_SUCCESS);
+ }
+
+ //
+ // If this is windows NT,
+ // There is nothing to maintain.
+ //
+
+ if ( NlGlobalChangeLogRole == ChangeLogMemberWorkstation ) {
+ return(STATUS_SUCCESS);
+ }
+
+
+ //
+ // Make available just the machine account bits.
+ //
+
+ OldUserAccountControl &= USER_MACHINE_ACCOUNT_MASK;
+ NewUserAccountControl &= USER_MACHINE_ACCOUNT_MASK;
+ NlAssert( OldUserAccountControl == 0 || NewUserAccountControl == 0 );
+ NlAssert( OldUserAccountControl != 0 || NewUserAccountControl != 0 );
+
+
+ //
+ // Handle deletion of a Server Trust Account
+ //
+
+ if ( OldUserAccountControl == USER_SERVER_TRUST_ACCOUNT ) {
+
+ Status = NlSendChangeLogNotification( ChangeLogNtServerDeleted,
+ ObjectName,
+ NULL,
+ 0 );
+
+
+ //
+ // Handle deletion of a Domain Trust Account
+ //
+
+ } else if ( OldUserAccountControl == USER_INTERDOMAIN_TRUST_ACCOUNT ) {
+
+ Status = NlSendChangeLogNotification( ChangeLogTrustedDomainDeleted,
+ ObjectName,
+ NULL,
+ 0 );
+
+
+ //
+ // Handle deletion of a Workstation Trust Account
+ //
+
+ } else if ( OldUserAccountControl == USER_WORKSTATION_TRUST_ACCOUNT ) {
+
+ Status = NlSendChangeLogNotification( ChangeLogWorkstationDeleted,
+ ObjectName,
+ NULL,
+ 0 );
+
+ //
+ // Handle creation of a Server Trust Account
+ //
+
+ } else if ( NewUserAccountControl == USER_SERVER_TRUST_ACCOUNT ) {
+
+ if ( NlGlobalChangeLogRole == ChangeLogPrimary ) {
+ Status = NlSendChangeLogNotification( ChangeLogNtServerAdded,
+ ObjectName,
+ NULL,
+ ObjectRid );
+ } else {
+ Status = STATUS_SUCCESS;
+ }
+
+ //
+ // Ignore all other changes for now.
+ //
+
+ } else {
+
+ Status = STATUS_SUCCESS;
+ }
+
+ return Status;
+ UNREFERENCED_PARAMETER( DomainSid );
+}
+
+
+NTSTATUS
+NlInitChangeLog(
+ VOID
+)
+/*++
+
+Routine Description:
+
+ Do the portion of ChangeLog initialization which happens on process
+ attach for netlogon.dll.
+
+ Specifically, Initialize the NlGlobalChangeLogCritSect and several
+ other global variables.
+
+Arguments:
+
+ NONE
+
+Return Value:
+
+ NT Status code
+
+--*/
+{
+ LARGE_INTEGER DomainPromotionIncrement = DOMAIN_PROMOTION_INCREMENT;
+ LARGE_INTEGER DomainPromotionMask = DOMAIN_PROMOTION_MASK;
+ NTSTATUS Status;
+
+ SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
+ NT_PRODUCT_TYPE NtProductType;
+
+
+ //
+ // Initialize the critical section and anything process detach depends on.
+ //
+
+ InitializeCriticalSection( &NlGlobalChangeLogCritSect );
+#if DBG
+ InitializeCriticalSection( &NlGlobalLogFileCritSect );
+ NlGlobalTrace = 0xFFFFFFFF;
+ NlGlobalLogFile = INVALID_HANDLE_VALUE;
+ NlGlobalLogFileMaxSize = DEFAULT_MAXIMUM_LOGFILE_SIZE;
+#endif // DBG
+ InitChangeLogDesc( &NlGlobalChangeLogDesc );
+ NlGlobalChWorkerBuiltinDomainSid = NULL;
+ NlGlobalChWorkerSamDomainSid = NULL;
+
+ NlGlobalChangeLogNetlogonState = NetlogonStopped;
+ NlGlobalChangeLogEvent = NULL;
+ NlGlobalChangeLogReplicateImmediately = FALSE;
+ NlGlobalChangeLogLanmanReplicateImmediately = FALSE;
+ InitializeListHead( &NlGlobalChangeLogNotifications );
+
+
+ NlGlobalChWorkerSamServerHandle = NULL;
+ NlGlobalChWorkerPolicyHandle = NULL;
+ NlGlobalChWorkerSamDBHandle = NULL;
+ NlGlobalChWorkerBuiltinDBHandle = NULL;
+
+ NlGlobalChangeLogWorkerQueueEvent = NULL;
+ InitializeListHead(&NlGlobalChangeLogWorkerQueue);
+ InitializeListHead(&NlGlobalSpecialServerGroupList);
+
+ NlGlobalChangeLogWorkerThreadHandle = NULL;
+ NlGlobalChangeLogWorkInit = FALSE;
+
+ NlGlobalChangeLogWorkerTerminate = FALSE;
+ NlGlobalChangeLogFilePrefix[0] = L'\0';
+ NlGlobalChangeLogPromotionIncrement = DomainPromotionIncrement;
+ NlGlobalChangeLogPromotionMask = DomainPromotionMask.HighPart;
+
+ NlGlobalLmBdcRidArray = NULL;
+ NlGlobalLmBdcCount = 0;
+
+ //
+ // Initialize the Role.
+ //
+ // For Windows-NT, just set the role to member workstation once and for all.
+ //
+ // For LanMan-Nt initially set it to "unknown" to prevent the
+ // changelog from being maintained until LSA calls I_NetNotifyRole.
+ //
+
+ if ( !RtlGetNtProductType( &NtProductType ) ) {
+ NtProductType = NtProductWinNt;
+ }
+
+ if ( NtProductType == NtProductLanManNt ) {
+ NlGlobalChangeLogRole = ChangeLogUnknown;
+ } else {
+ NlGlobalChangeLogRole = ChangeLogMemberWorkstation;
+ }
+
+ //
+ // Initialize the events that are used by the LanmanNt PDC.
+ //
+
+ if ( NtProductType == NtProductLanManNt ) {
+
+ //
+ // Create special change log notify event.
+ //
+
+ NlGlobalChangeLogEvent =
+ CreateEvent( NULL, // No security attributes
+ FALSE, // Is automatically reset
+ FALSE, // Initially not signaled
+ NULL ); // No name
+
+ if ( NlGlobalChangeLogEvent == NULL ) {
+ NET_API_STATUS NetStatus;
+
+ NetStatus = GetLastError();
+ NlPrint((NL_CRITICAL, "Cannot create ChangeLog Event %lu\n",
+ NetStatus ));
+ return (int) NetpApiStatusToNtStatus(NetStatus);
+ }
+
+ //
+ // Create worker queue notify event.
+ //
+
+ NlGlobalChangeLogWorkerQueueEvent =
+ CreateEvent( NULL, // No security attributes
+ FALSE, // Is automatically reset
+ FALSE, // Initially not signaled
+ NULL ); // No name
+
+ if ( NlGlobalChangeLogWorkerQueueEvent == NULL ) {
+ NET_API_STATUS NetStatus;
+
+ NetStatus = GetLastError();
+ NlPrint((NL_CRITICAL,
+ "Cannot create Worker Queue Event %lu\n",
+ NetStatus ));
+ return (int) NetpApiStatusToNtStatus(NetStatus);
+ }
+
+ //
+ // Build a Sid for the SAM Builtin domain
+ //
+
+ Status = RtlAllocateAndInitializeSid(
+ &NtAuthority,
+ 1, // Sub Authority Count
+ SECURITY_BUILTIN_DOMAIN_RID,
+ 0, // Unused
+ 0, // Unused
+ 0, // Unused
+ 0, // Unused
+ 0, // Unused
+ 0, // Unused
+ 0, // Unused
+ &NlGlobalChWorkerBuiltinDomainSid);
+
+ if ( !NT_SUCCESS(Status) ) {
+ goto Cleanup;
+ }
+ }
+
+ //
+ // Success...
+ //
+
+
+ Status = STATUS_SUCCESS;
+
+ //
+ // Cleanup
+ //
+
+Cleanup:
+
+ return Status;
+}
+
+//
+// netlogon.dll never detaches
+//
+#ifdef NETLOGON_PROCESS_DETACH
+
+NTSTATUS
+NlCloseChangeLog(
+ VOID
+)
+/*++
+
+Routine Description:
+
+ Frees any resources consumed by NlInitChangeLog.
+
+Arguments:
+
+ NONE
+
+Return Value:
+
+ NT Status code
+
+--*/
+{
+
+ if ( (NlGlobalChangeLogDesc.FileHandle == INVALID_HANDLE_VALUE) &&
+ (NlGlobalChangeLogRole == ChangeLogPrimary) ) {
+
+ //
+ // try to save change log cache one last time.
+ //
+
+ (VOID)NlCreateChangeLogFile( &NlGlobalChangeLogDesc );
+ }
+
+ if ( NlGlobalChangeLogDesc.FileHandle != INVALID_HANDLE_VALUE ) {
+ CloseHandle( NlGlobalChangeLogDesc.FileHandle );
+ NlGlobalChangeLogDesc.FileHandle = INVALID_HANDLE_VALUE;
+ }
+ NlGlobalChangeLogFilePrefix[0] = L'\0';
+
+ if ( NlGlobalChangeLogDesc.Buffer != NULL ) {
+ NetpMemoryFree( NlGlobalChangeLogDesc.Buffer );
+ NlGlobalChangeLogDesc.Buffer = NULL;
+ }
+
+ if ( NlGlobalChWorkerBuiltinDomainSid != NULL ) {
+ RtlFreeSid( NlGlobalChWorkerBuiltinDomainSid );
+ NlGlobalChWorkerBuiltinDomainSid = NULL;
+ }
+
+ if ( NlGlobalChWorkerSamDomainSid != NULL ) {
+ NetpMemoryFree( NlGlobalChWorkerSamDomainSid );
+ NlGlobalChWorkerSamDomainSid = NULL;
+ }
+
+ if ( NlGlobalChangeLogEvent != NULL ) {
+ (VOID) CloseHandle(NlGlobalChangeLogEvent);
+ NlGlobalChangeLogEvent = NULL;
+ }
+
+ if ( NlGlobalChangeLogWorkerQueueEvent != NULL ) {
+ (VOID) CloseHandle(NlGlobalChangeLogWorkerQueueEvent);
+ NlGlobalChangeLogWorkerQueueEvent = NULL;
+ }
+
+ //
+ // if worker thread running, stop it.
+ //
+
+ NlStopChangeLogWorker();
+
+ LOCK_CHANGELOG();
+
+ NlAssert( IsListEmpty( &NlGlobalChangeLogNotifications ) );
+ NlAssert( IsListEmpty( &NlGlobalChangeLogWorkerQueue ) );
+
+ UNLOCK_CHANGELOG();
+
+ NlGlobalChangeLogWorkInit = FALSE;
+
+ //
+ // close all handles
+ //
+
+ if ( NlGlobalChWorkerSamServerHandle != NULL ) {
+
+ (VOID)SamrCloseHandle( &NlGlobalChWorkerSamServerHandle);
+ }
+
+ if ( NlGlobalChWorkerPolicyHandle != NULL ) {
+
+ (VOID)LsarClose( &NlGlobalChWorkerPolicyHandle);
+ }
+
+ if ( NlGlobalChWorkerSamDBHandle != NULL ) {
+
+ (VOID)SamrCloseHandle( &NlGlobalChWorkerSamDBHandle);
+ }
+
+ if ( NlGlobalChWorkerBuiltinDBHandle != NULL ) {
+
+ (VOID)SamrCloseHandle( &NlGlobalChWorkerBuiltinDBHandle);
+ }
+
+ DeleteCriticalSection( &NlGlobalChangeLogCritSect );
+#if DBG
+ DeleteCriticalSection( &NlGlobalLogFileCritSect );
+#endif // DBG
+
+ return STATUS_SUCCESS;
+
+}
+#endif // NETLOGON_PROCESS_DETACH
diff --git a/private/net/svcdlls/logonsrv/server/changelg.h b/private/net/svcdlls/logonsrv/server/changelg.h
new file mode 100644
index 000000000..6054b7a91
--- /dev/null
+++ b/private/net/svcdlls/logonsrv/server/changelg.h
@@ -0,0 +1,224 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ changelg.h
+
+Abstract:
+
+ Defines and routines needed to interface with changelg.c.
+ Read the comments in the abstract for changelg.c to determine the
+ restrictions on the use of that module.
+
+Author:
+
+ Cliff Van Dyke (cliffv) 07-May-1992
+
+Environment:
+
+ User mode only.
+ Contains NT-specific code.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+ 02-Jan-1992 (madana)
+ added support for builtin/multidomain replication.
+
+--*/
+
+#if ( _MSC_VER >= 800 )
+#pragma warning ( 3 : 4100 ) // enable "Unreferenced formal parameter"
+#pragma warning ( 3 : 4219 ) // enable "trailing ',' used for variable argument list"
+#endif
+
+//
+// changelg.c will #include this file with CHANGELOG_ALLOCATE defined.
+// That will cause each of these variables to be allocated.
+//
+#ifdef CHANGELOG_ALLOCATE
+#define EXTERN
+#else
+#define EXTERN extern
+#endif
+
+#define THREAD_STACKSIZE 8192
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Structures and variables describing the Change Log
+//
+/////////////////////////////////////////////////////////////////////////////
+
+//
+// Change log entry is a variable length record, the variable fields SID and
+// ObjectName will follow the structure.
+//
+
+typedef struct _CHANGELOG_ENTRY_V3 {
+ LARGE_INTEGER SerialNumber; // always align this on 8 byte boundary
+
+ DWORD Size;
+ USHORT DeltaType;
+ UCHAR DBIndex;
+ UCHAR ReplicateImmediately;
+
+ ULONG ObjectRid;
+ USHORT ObjectSidOffset;
+ USHORT ObjectNameOffset; // null terminated unicode string
+} CHANGELOG_ENTRY_V3, *PCHANGELOG_ENTRY_V3;
+
+typedef struct _CHANGELOG_ENTRY {
+ LARGE_INTEGER SerialNumber; // always align this on 8 byte boundary
+
+ ULONG ObjectRid;
+
+ USHORT Flags;
+#define CHANGELOG_REPLICATE_IMMEDIATELY 0x01
+#define CHANGELOG_PASSWORD_CHANGE 0x02
+#define CHANGELOG_SID_SPECIFIED 0x04
+#define CHANGELOG_NAME_SPECIFIED 0x08
+#define CHANGELOG_PDC_PROMOTION 0x10
+#define CHANGELOG_DOMAINUSERS_CHANGED 0x20
+ UCHAR DBIndex;
+ UCHAR DeltaType;
+
+} CHANGELOG_ENTRY, *PCHANGELOG_ENTRY;
+
+
+//
+// List of changes the netlogon needs to be aware of.
+//
+
+typedef struct _CHANGELOG_NOTIFICATION {
+ LIST_ENTRY Next;
+
+ enum CHANGELOG_NOTIFICATION_TYPE {
+ ChangeLogNtServerAdded, // ObjectName/ObjectRid specified
+ ChangeLogNtServerDeleted, // ObjectName specified
+ ChangeLogWorkstationDeleted, // ObjectName specified
+ ChangeLogTrustedDomainDeleted, // ObjectName specified
+ ChangeLogTrustAdded, // ObjectSid specified
+ ChangeLogTrustDeleted, // ObjectSid specified
+ ChangeLogLmServerAdded, // ObjectRid specified
+ ChangeLogLmServerDeleted // ObjectRid specified
+ } EntryType;
+
+ UNICODE_STRING ObjectName;
+
+ PSID ObjectSid;
+
+ ULONG ObjectRid;
+
+} CHANGELOG_NOTIFICATION, *PCHANGELOG_NOTIFICATION;
+
+//
+// To serialize change log access
+//
+
+EXTERN CRITICAL_SECTION NlGlobalChangeLogCritSect;
+
+#define LOCK_CHANGELOG() EnterCriticalSection( &NlGlobalChangeLogCritSect )
+#define UNLOCK_CHANGELOG() LeaveCriticalSection( &NlGlobalChangeLogCritSect )
+
+//
+// Index to supported data bases.
+//
+
+#define SAM_DB 0 // index to SAM database structure
+#define BUILTIN_DB 1 // index to BUILTIN database structure
+#define LSA_DB 2 // index to LSA database
+#define VOID_DB 3 // index to unused database (used to mark changelog
+ // entry as invalid)
+
+#define NUM_DBS 3 // number of databases supported
+
+
+//
+// Amount SAM/LSA increments serial number by on promotion.
+//
+EXTERN LARGE_INTEGER NlGlobalChangeLogPromotionIncrement;
+EXTERN LONG NlGlobalChangeLogPromotionMask;
+
+
+
+//
+// Netlogon started flag, used by the changelog to determine the
+// netlogon service is successfully started and initialization
+// completed.
+//
+
+EXTERN enum {
+ NetlogonStopped,
+ NetlogonStarting,
+ NetlogonStarted
+} NlGlobalChangeLogNetlogonState;
+
+
+
+//
+// Event to indicate that something interesting is being logged to the
+// change log. The booleans below (protected by NlGlobalChangeLogCritSect)
+// indicate the actual interesting event.
+//
+
+EXTERN HANDLE NlGlobalChangeLogEvent;
+
+//
+// Indicates that a "replicate immediately" event has happened.
+//
+
+EXTERN BOOL NlGlobalChangeLogReplicateImmediately;
+
+//
+// Indicates we need to "replicate immediately" to Lanman BDCs
+//
+
+EXTERN BOOL NlGlobalChangeLogLanmanReplicateImmediately;
+
+//
+// List of MachineAccount changes
+//
+
+EXTERN LIST_ENTRY NlGlobalChangeLogNotifications;
+
+//
+// List of Rids of Lanman BDC accounts.
+//
+
+EXTERN PULONG NlGlobalLmBdcRidArray;
+EXTERN ULONG NlGlobalLmBdcCount;
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Procedure forwards
+//
+/////////////////////////////////////////////////////////////////////////////
+
+
+NTSTATUS
+NlInitChangeLog(
+ VOID
+);
+
+#ifdef NETLOGON_PROCESS_DETACH
+NTSTATUS
+NlCloseChangeLog(
+ VOID
+);
+#endif // NETLOGON_PROCESS_DETACH
+
+DWORD
+NlBackupChangeLogFile(
+ VOID
+ );
+
+VOID
+NlLmBdcListSet(
+ IN ULONG LmBdcCount,
+ IN PULONG LmBdcRidArray
+ );
+
+#undef EXTERN
diff --git a/private/net/svcdlls/logonsrv/server/chutil.c b/private/net/svcdlls/logonsrv/server/chutil.c
new file mode 100644
index 000000000..16bf9493c
--- /dev/null
+++ b/private/net/svcdlls/logonsrv/server/chutil.c
@@ -0,0 +1,4337 @@
+/*++
+
+Copyright (c) 1987-1991 Microsoft Corporation
+
+Module Name:
+
+ chutil.c
+
+Abstract:
+
+ Change Log utility routines.
+
+Author:
+
+
+Environment:
+
+ User mode only.
+ Contains NT-specific code.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+ 11-Jan-1994 (cliffv)
+ Split out from changelg.c
+
+--*/
+
+//
+// Common include files.
+//
+
+#include <nt.h> // LARGE_INTEGER definition
+#include <ntrtl.h> // LARGE_INTEGER definition
+#include <nturtl.h> // LARGE_INTEGER definition
+#include <ntlsa.h> // needed by changelg.h
+
+#define NOMINMAX // Avoid redefinition of min and max in stdlib.h
+#include <rpc.h> // Needed by logon.h
+#include <logon_s.h>// includes lmcons.h, lmaccess.h, netlogon.h,
+ // ssi.h, windef.h
+#include <winbase.h>
+#include <stdio.h> // sprintf ...
+
+//
+// Include files specific to this .c file
+//
+#include "iniparm.h" // defaults
+
+//
+// BEWARE: Be careful about adding netlogon.dll specific include files here.
+// This module is call by SAM and LSA. The netlogon service may not yet
+// be running. Therefore, guard against referencing netlogon.dll globals
+// other than those defined in changelg.h.
+//
+
+#include <samrpc.h> // Needed by samisrv.h
+#include <samisrv.h> // Needed by changelg.h
+#include <changelg.h> // Local procedure definitions
+
+#include <lmerrlog.h> // NELOG_* defined here ..
+#include <netlib.h> // NetpMemoryAllocate
+#include <netlibnt.h> // NetpNtStatusToApiStatus
+
+#include <debugfmt.h> // FORMAT_*
+#include <nldebug.h> // Netlogon debugging
+#include <align.h>
+#include <string.h> // strncmp
+#include <nlp.h> // NlpWriteEventlog defined here.
+
+#define CHUTIL_ALLOCATE
+#include "chutil.h" // Local data definitions
+#undef CHUTIL_ALLOCATE
+
+
+
+/* NlCreateChangeLogFile and NlWriteChangeLogBytes reference each other */
+NTSTATUS
+NlWriteChangeLogBytes(
+ IN PCHANGELOG_DESCRIPTOR ChangeLogDesc,
+ IN LPBYTE Buffer,
+ IN DWORD BufferSize,
+ IN BOOLEAN FlushIt
+ );
+
+
+
+
+NTSTATUS
+NlCreateChangeLogFile(
+ IN PCHANGELOG_DESCRIPTOR ChangeLogDesc
+ )
+/*++
+
+Routine Description:
+
+ Try to create a change log file. If it is successful then it sets
+ the file handle in ChangeLogDesc, otherwise it leaves the handle invalid.
+
+ NOTE: This function must be called with the change log locked.
+
+Arguments:
+
+ ChangeLogDesc -- Description of the Changelog buffer being used
+
+Return Value:
+
+ STATUS_SUCCESS - The Service completed successfully.
+
+--*/
+{
+ NTSTATUS Status;
+ WCHAR ChangeLogFile[PATHLEN+1];
+
+ NlAssert( ChangeLogDesc->FileHandle == INVALID_HANDLE_VALUE );
+
+ //
+ // if the change file name is unknown, terminate the operation.
+ //
+
+ if( NlGlobalChangeLogFilePrefix[0] == L'\0' ) {
+ return STATUS_NO_SUCH_FILE;
+ }
+
+ //
+ // Create change log file. If it exists already then truncate it.
+ //
+ // Note : if a valid change log file exists on the system, then we
+ // would have opened at initialization time.
+ //
+
+ wcscpy( ChangeLogFile, NlGlobalChangeLogFilePrefix );
+ wcscat( ChangeLogFile,
+ ChangeLogDesc->RedoLog ? REDO_FILE_POSTFIX : CHANGELOG_FILE_POSTFIX );
+
+ ChangeLogDesc->FileHandle = CreateFileW(
+ ChangeLogFile,
+ GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ, // allow backups and debugging
+ NULL, // Supply better security ??
+ CREATE_ALWAYS, // Overwrites always
+ FILE_ATTRIBUTE_NORMAL,
+ NULL ); // No template
+
+ if (ChangeLogDesc->FileHandle == INVALID_HANDLE_VALUE) {
+
+ Status = NetpApiStatusToNtStatus( GetLastError());
+ NlPrint((NL_CRITICAL,"Unable to create changelog file: 0x%lx \n", Status));
+ return Status;
+ }
+
+ //
+ // Write cache in backup changelog file if the cache is valid.
+ //
+
+ if( ChangeLogDesc->Buffer != NULL ) {
+ Status = NlWriteChangeLogBytes(
+ ChangeLogDesc,
+ ChangeLogDesc->Buffer,
+ ChangeLogDesc->BufferSize,
+ TRUE ); // Flush the bytes to disk
+
+ return Status;
+
+ }
+
+ return STATUS_SUCCESS;
+
+}
+
+
+NTSTATUS
+NlFlushChangeLog(
+ IN PCHANGELOG_DESCRIPTOR ChangeLogDesc
+ )
+/*++
+
+Routine Description:
+
+ Flush any dirty buffers to the change log file itself.
+ Ensure they are flushed to disk.
+
+ NOTE: This function must be called with the change log locked.
+
+Arguments:
+
+ ChangeLogDesc -- Description of the Changelog buffer being used
+
+Return Value:
+
+ Status of the operation.
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ OVERLAPPED Overlapped;
+ DWORD BytesWritten;
+ DWORD BufferSize;
+
+ //
+ // If there's nothing to do,
+ // just return.
+ //
+
+ if ( ChangeLogDesc->LastDirtyByte == 0 ) {
+ return STATUS_SUCCESS;
+ }
+
+
+ //
+ // Write to the file.
+ //
+
+ if ( ChangeLogDesc->FileHandle == INVALID_HANDLE_VALUE ) {
+
+ Status = NlCreateChangeLogFile( ChangeLogDesc );
+
+ //
+ // This must have written entire buffer if it is successful
+ // creating the change log file.
+ //
+
+ goto Cleanup;
+ }
+
+ //
+ // if we are unable to create this into the changelog file, work
+ // with internal cache, but notify admin by sending admin alert.
+ //
+
+ if ( ChangeLogDesc->FileHandle != INVALID_HANDLE_VALUE ) {
+
+#ifdef notdef
+ NlPrint((NL_CHANGELOG, "NlFlushChangeLog: %ld to %ld\n",
+ ChangeLogDesc->FirstDirtyByte,
+ ChangeLogDesc->LastDirtyByte ));
+#endif // notdef
+
+ //
+ // Seek to appropriate offset in the file.
+ //
+
+ RtlZeroMemory( &Overlapped, sizeof(Overlapped) );
+ Overlapped.Offset = ChangeLogDesc->FirstDirtyByte;
+
+ //
+ // Actually write to the file.
+ //
+
+ BufferSize = ChangeLogDesc->LastDirtyByte -
+ ChangeLogDesc->FirstDirtyByte + 1;
+
+ if ( !WriteFile( ChangeLogDesc->FileHandle,
+ &ChangeLogDesc->Buffer[ChangeLogDesc->FirstDirtyByte],
+ BufferSize,
+ &BytesWritten,
+ &Overlapped ) ) {
+
+ Status = NetpApiStatusToNtStatus( GetLastError() );
+ NlPrint((NL_CRITICAL, "Write to ChangeLog failed 0x%lx\n",
+ Status ));
+
+ //
+ // Recreate changelog file
+ //
+
+ CloseHandle( ChangeLogDesc->FileHandle );
+ ChangeLogDesc->FileHandle = INVALID_HANDLE_VALUE;
+
+ goto Cleanup;
+ }
+
+ //
+ // Ensure all the bytes made it.
+ //
+
+ if ( BytesWritten != BufferSize ) {
+ NlPrint((NL_CRITICAL,
+ "Write to ChangeLog bad byte count %ld s.b. %ld\n",
+ BytesWritten,
+ BufferSize ));
+
+ //
+ // Recreate changelog file
+ //
+
+ CloseHandle( ChangeLogDesc->FileHandle );
+ ChangeLogDesc->FileHandle = INVALID_HANDLE_VALUE;
+
+ Status = STATUS_BUFFER_TOO_SMALL;
+ goto Cleanup;
+ }
+
+ //
+ // Force the modifications to disk.
+ //
+
+ if ( !FlushFileBuffers( ChangeLogDesc->FileHandle ) ) {
+
+ Status = NetpApiStatusToNtStatus( GetLastError() );
+ NlPrint((NL_CRITICAL, "Flush to ChangeLog failed 0x%lx\n", Status ));
+
+ //
+ // Recreate changelog file
+ //
+
+ CloseHandle( ChangeLogDesc->FileHandle );
+ ChangeLogDesc->FileHandle = INVALID_HANDLE_VALUE;
+
+ goto Cleanup;
+ }
+
+ //
+ // Indicate these byte successfully made it out to disk.
+ //
+
+ ChangeLogDesc->FirstDirtyByte = 0;
+ ChangeLogDesc->LastDirtyByte = 0;
+ }
+
+Cleanup:
+
+ if( !NT_SUCCESS(Status) ) {
+
+ //
+ // Write event log.
+ //
+
+ NlpWriteEventlog (
+ NELOG_NetlogonChangeLogCorrupt,
+ EVENTLOG_ERROR_TYPE,
+ (LPBYTE)&Status,
+ sizeof(Status),
+ NULL,
+ 0 );
+ }
+
+ return Status;
+}
+
+NTSTATUS
+NlWriteChangeLogBytes(
+ IN PCHANGELOG_DESCRIPTOR ChangeLogDesc,
+ IN LPBYTE Buffer,
+ IN DWORD BufferSize,
+ IN BOOLEAN FlushIt
+ )
+/*++
+
+Routine Description:
+
+ Write bytes from the changelog cache to the change log file.
+
+Arguments:
+
+ ChangeLogDesc -- Description of the Changelog buffer being used
+
+ Buffer - Address within the changelog cache to write.
+
+ BufferSize - Number of bytes to write.
+
+ FlushIt - TRUE if the bytes are to be flushed to disk
+
+Return Value:
+
+ Status of the operation.
+
+--*/
+
+{
+ NTSTATUS Status;
+ ULONG FirstDirtyByte;
+ ULONG LastDirtyByte;
+
+ //
+ // Compute the new range of dirty bytes.
+ //
+
+ FirstDirtyByte = ((LPBYTE)Buffer) - ((LPBYTE)ChangeLogDesc->Buffer);
+ LastDirtyByte = FirstDirtyByte + BufferSize - 1;
+
+#ifdef notdef
+ NlPrint((NL_CHANGELOG, "NlWriteChangeLogBytes: %ld to %ld\n",
+ FirstDirtyByte,
+ LastDirtyByte ));
+#endif // notdef
+
+ if ( ChangeLogDesc->LastDirtyByte == 0 ) {
+ ChangeLogDesc->FirstDirtyByte = FirstDirtyByte;
+ ChangeLogDesc->LastDirtyByte = LastDirtyByte;
+ } else {
+ if ( ChangeLogDesc->FirstDirtyByte > FirstDirtyByte ) {
+ ChangeLogDesc->FirstDirtyByte = FirstDirtyByte;
+ }
+ if ( ChangeLogDesc->LastDirtyByte < LastDirtyByte ) {
+ ChangeLogDesc->LastDirtyByte = LastDirtyByte;
+ }
+ }
+
+ //
+ // If the bytes are to be flushed,
+ // do so.
+ //
+
+ if ( FlushIt ) {
+ Status = NlFlushChangeLog( ChangeLogDesc );
+ return Status;
+ }
+ return STATUS_SUCCESS;
+}
+
+
+
+
+PCHANGELOG_BLOCK_HEADER
+NlMoveToNextChangeLogBlock(
+ IN PCHANGELOG_DESCRIPTOR ChangeLogDesc,
+ IN PCHANGELOG_BLOCK_HEADER BlockPtr
+ )
+
+/*++
+
+Routine Description:
+
+ This function accepts a pointer to a change log
+ block and returns the pointer to the next change log block in the
+ buffer. It however wraps around the change log cache.
+
+ NOTE: This function must be called with the change log locked.
+
+Arguments:
+
+ ChangeLogDesc -- Description of the Changelog buffer being used
+
+ BlockPtr - pointer to a change log block.
+
+Return Value:
+
+ Returns the pointer to the next change log block in the list.
+
+--*/
+{
+ PCHANGELOG_BLOCK_HEADER ReturnPtr;
+
+ ReturnPtr = (PCHANGELOG_BLOCK_HEADER)
+ ((LPBYTE)BlockPtr + BlockPtr->BlockSize);
+
+
+ NlAssert( (LPBYTE)ReturnPtr <= ChangeLogDesc->BufferEnd );
+
+ if( (LPBYTE)ReturnPtr >= ChangeLogDesc->BufferEnd ) {
+
+ //
+ // wrap around
+ //
+
+ ReturnPtr = ChangeLogDesc->FirstBlock;
+ }
+
+ return ReturnPtr;
+
+}
+
+
+PCHANGELOG_BLOCK_HEADER
+NlMoveToPrevChangeLogBlock(
+ IN PCHANGELOG_DESCRIPTOR ChangeLogDesc,
+ IN PCHANGELOG_BLOCK_HEADER BlockPtr
+ )
+
+/*++
+
+Routine Description:
+
+ This function accepts a pointer to a change log
+ block and returns the pointer to the next change log block in the
+ buffer. It however wraps around the change log cache.
+
+ NOTE: This function must be called with the change log locked.
+
+Arguments:
+
+ ChangeLogDesc -- Description of the Changelog buffer being used
+
+ BlockPtr - pointer to a change log block.
+
+Return Value:
+
+ Returns the pointer to the next change log block in the list.
+
+--*/
+{
+ PCHANGELOG_BLOCK_HEADER ReturnPtr;
+ PCHANGELOG_BLOCK_TRAILER ReturnTrailer;
+
+ //
+ // If this is the first block in the buffer,
+ // return the last block in the buffer.
+ //
+
+ if ( BlockPtr == ChangeLogDesc->FirstBlock ) {
+ ReturnTrailer = (PCHANGELOG_BLOCK_TRAILER)
+ (ChangeLogDesc->BufferEnd - sizeof(CHANGELOG_BLOCK_TRAILER));
+
+ //
+ // Otherwise return the buffer immediately before this one.
+ //
+
+ } else {
+ ReturnTrailer = (PCHANGELOG_BLOCK_TRAILER)
+ (((LPBYTE)BlockPtr) - sizeof(CHANGELOG_BLOCK_TRAILER));
+ }
+
+
+ ReturnPtr = (PCHANGELOG_BLOCK_HEADER)
+ ((LPBYTE)ReturnTrailer -
+ ReturnTrailer->BlockSize +
+ sizeof(CHANGELOG_BLOCK_TRAILER) );
+
+
+ NlAssert( ReturnPtr >= ChangeLogDesc->FirstBlock );
+ NlAssert( (LPBYTE)ReturnPtr < ChangeLogDesc->BufferEnd );
+
+ return ReturnPtr;
+
+}
+
+
+
+NTSTATUS
+NlAllocChangeLogBlock(
+ IN PCHANGELOG_DESCRIPTOR ChangeLogDesc,
+ IN DWORD BlockSize,
+ OUT PCHANGELOG_BLOCK_HEADER *AllocatedBlock
+ )
+/*++
+
+Routine Description:
+
+ This function will allocate a change log block from the free block
+ at the tail of the change log circular list. If the available free
+ block size is less than the required size than it will enlarge the
+ free block by the freeing up change logs from the header. Once the
+ free block is larger then it will cut the block to the required size
+ and adjust the free block pointer.
+
+ NOTE: This function must be called with the change log locked.
+
+Arguments:
+
+ ChangeLogDesc -- Description of the Changelog buffer being used
+
+ BlockSize - size of the change log block required.
+
+ AllocatedBlock - Returns the pointer to the block that is allocated.
+
+Return Value:
+
+ Status of the operation
+
+--*/
+{
+ PCHANGELOG_BLOCK_HEADER FreeBlock;
+ PCHANGELOG_BLOCK_HEADER NewBlock;
+ DWORD ReqBlockSize;
+ DWORD AllocatedBlockSize;
+
+ //
+ // pump up the size to include block header, block trailer,
+ // and to align to DWORD.
+ //
+ // Add in the size of the new free block immediately following the new
+ // block.
+ //
+
+ AllocatedBlockSize =
+ ROUND_UP_COUNT( sizeof(CHANGELOG_BLOCK_HEADER), ALIGN_WORST) +
+ ROUND_UP_COUNT( BlockSize+sizeof(CHANGELOG_BLOCK_TRAILER), ALIGN_WORST);
+
+ ReqBlockSize = AllocatedBlockSize +
+ ROUND_UP_COUNT( sizeof(CHANGELOG_BLOCK_HEADER), ALIGN_WORST) +
+ ROUND_UP_COUNT( sizeof(CHANGELOG_BLOCK_TRAILER), ALIGN_WORST );
+
+ NlAssert( ReqBlockSize < ChangeLogDesc->BufferSize - 16 );
+
+
+ //
+ // If the current free block isn't big enough,
+ // make it big enough.
+ //
+
+ FreeBlock = ChangeLogDesc->Tail;
+
+ NlAssert( FreeBlock->BlockState == BlockFree );
+
+ while ( FreeBlock->BlockSize <= ReqBlockSize ) {
+
+ //
+ // If this is a re-do log,
+ // make the freeblock bigger by re-allocating the buffer.
+
+ if ( ChangeLogDesc->RedoLog ) {
+ NTSTATUS Status;
+
+ Status = NlResizeChangeLogFile(
+ ChangeLogDesc,
+ ChangeLogDesc->BufferSize + REDO_LOG_INCREMENT );
+
+
+ if ( !NT_SUCCESS(Status) ) {
+ return Status;
+ }
+
+ //
+ // The free block is in a different allocated buffer.
+ //
+
+ FreeBlock = ChangeLogDesc->Tail;
+
+ NlAssert( FreeBlock->BlockState == BlockFree );
+
+ //
+ // If this is a change log,
+ // make the free block bigger by wrapping around.
+ //
+
+ } else {
+ PCHANGELOG_BLOCK_HEADER NextFreeBlock;
+
+ NextFreeBlock = NlMoveToNextChangeLogBlock( ChangeLogDesc, FreeBlock );
+
+
+ //
+ // If this free block is the end block in the cache,
+ // so make this as a 'hole' block and wrap around for
+ // next free block.
+ //
+
+ if( (LPBYTE)NextFreeBlock !=
+ (LPBYTE)FreeBlock + FreeBlock->BlockSize ) {
+
+ NlAssert( ((LPBYTE)FreeBlock + FreeBlock->BlockSize) ==
+ ChangeLogDesc->BufferEnd );
+
+ NlAssert( NextFreeBlock == ChangeLogDesc->FirstBlock );
+
+ FreeBlock->BlockState = BlockHole;
+
+ //
+ // Write the 'hole' block status in the file.
+ // (Write the entire block since the block size in the trailer
+ // may have changed on previous iterations of this loop.)
+ //
+
+ (VOID) NlWriteChangeLogBytes( ChangeLogDesc,
+ (LPBYTE) FreeBlock,
+ FreeBlock->BlockSize,
+ TRUE ); // Flush the bytes to disk
+
+ //
+ // The free block is now at the front of the cache.
+ //
+
+ FreeBlock = ChangeLogDesc->FirstBlock;
+ FreeBlock->BlockState = BlockFree;
+
+ //
+ // Otherwise, enlarge the current free block by merging the next
+ // block into it. The next free block is either a used block or
+ // the 'hole' block.
+ //
+ } else {
+
+ //
+ // If we've just deleted a used block,
+ // adjust the entry count.
+ //
+ // VOID_DB entries are "deleted" entries and have already adjusted
+ // the entry count.
+ //
+ if ( NextFreeBlock->BlockState == BlockUsed ) {
+ DWORD DBIndex = ((PCHANGELOG_ENTRY)(NextFreeBlock+1))->DBIndex;
+ if ( DBIndex != VOID_DB ) {
+ ChangeLogDesc->EntryCount[DBIndex] --;
+ }
+ }
+
+ FreeBlock->BlockSize += NextFreeBlock->BlockSize;
+ ChangeLogBlockTrailer(FreeBlock)->BlockSize = FreeBlock->BlockSize;
+ }
+
+
+ //
+ // If we've consumed the head of the cache,
+ // move the head of the cache to the next block.
+ //
+
+ if ( NextFreeBlock == ChangeLogDesc->Head ) {
+
+ ChangeLogDesc->Head = NlMoveToNextChangeLogBlock( ChangeLogDesc,
+ NextFreeBlock );
+
+ //
+ // if we have moved the global header to hole block,
+ // skip and merge it to free block
+ //
+
+ NextFreeBlock = ChangeLogDesc->Head;
+
+ if (NextFreeBlock->BlockState == BlockHole ) {
+
+ FreeBlock->BlockSize += NextFreeBlock->BlockSize;
+ ChangeLogBlockTrailer(FreeBlock)->BlockSize = FreeBlock->BlockSize;
+
+ ChangeLogDesc->Head =
+ NlMoveToNextChangeLogBlock( ChangeLogDesc, NextFreeBlock );
+ }
+ }
+ }
+
+
+ NlAssert(ChangeLogDesc->Head->BlockState == BlockUsed );
+
+ }
+
+ NlAssert( (FreeBlock >= ChangeLogDesc->FirstBlock) &&
+ (FreeBlock->BlockSize <= ChangeLogDesc->BufferSize) &&
+ ( ((LPBYTE)FreeBlock + FreeBlock->BlockSize) <=
+ ChangeLogDesc->BufferEnd) );
+
+ //
+ // Cut the free block ...
+ //
+
+ NewBlock = FreeBlock;
+
+ FreeBlock = (PCHANGELOG_BLOCK_HEADER)
+ ((LPBYTE)FreeBlock + AllocatedBlockSize);
+
+ FreeBlock->BlockState = BlockFree;
+ FreeBlock->BlockSize = NewBlock->BlockSize - AllocatedBlockSize;
+ ChangeLogBlockTrailer(FreeBlock)->BlockSize = FreeBlock->BlockSize;
+
+ ChangeLogDesc->Tail = FreeBlock;
+
+ RtlZeroMemory( NewBlock, AllocatedBlockSize );
+ NewBlock->BlockState = BlockUsed;
+ NewBlock->BlockSize = AllocatedBlockSize;
+ ChangeLogBlockTrailer(NewBlock)->BlockSize = NewBlock->BlockSize;
+
+ NlAssert( (NewBlock >= ChangeLogDesc->FirstBlock) &&
+ ( ((LPBYTE)NewBlock + BlockSize) <= ChangeLogDesc->BufferEnd) );
+
+ *AllocatedBlock = NewBlock;
+
+ return STATUS_SUCCESS;
+
+}
+
+
+PCHANGELOG_ENTRY
+NlMoveToNextChangeLogEntry(
+ IN PCHANGELOG_DESCRIPTOR ChangeLogDesc,
+ IN PCHANGELOG_ENTRY ChangeLogEntry
+ )
+
+/*++
+
+Routine Description:
+
+ This function is a worker routine to scan the change log list. This
+ accepts a pointer to a change log structure and returns a pointer to
+ the next change log structure. It returns NULL pointer if the given
+ struct is the last change log structure in the list.
+
+ NOTE: This function must be called with the change log locked.
+
+Arguments:
+
+ ChangeLogDesc -- Description of the Changelog buffer being used
+
+ ChangeLogEntry - pointer to a change log strcuture.
+
+Return Value:
+
+ Returns the pointer to the next change log structure in the list.
+
+--*/
+{
+ PCHANGELOG_BLOCK_HEADER ChangeLogBlock;
+
+ ChangeLogBlock = (PCHANGELOG_BLOCK_HEADER)
+ ( (LPBYTE) ChangeLogEntry - sizeof(CHANGELOG_BLOCK_HEADER) );
+
+ NlAssert( ChangeLogBlock->BlockState == BlockUsed );
+
+ ChangeLogBlock = NlMoveToNextChangeLogBlock( ChangeLogDesc, ChangeLogBlock );
+
+ //
+ // If we're at the end of the list,
+ // return null
+ //
+ if ( ChangeLogBlock->BlockState == BlockFree ) {
+ return NULL;
+
+
+ //
+ // Skip this block, there will be only one 'Hole' block in the
+ // list.
+ //
+ } else if ( ChangeLogBlock->BlockState == BlockHole ) {
+
+
+ ChangeLogBlock = NlMoveToNextChangeLogBlock( ChangeLogDesc, ChangeLogBlock );
+
+ if ( ChangeLogBlock->BlockState == BlockFree ) {
+ return NULL;
+ }
+
+ }
+
+ NlAssert( ChangeLogBlock->BlockState == BlockUsed );
+
+ return (PCHANGELOG_ENTRY)
+ ( (LPBYTE)ChangeLogBlock + sizeof(CHANGELOG_BLOCK_HEADER) );
+
+}
+
+
+PCHANGELOG_ENTRY
+NlMoveToPrevChangeLogEntry(
+ IN PCHANGELOG_DESCRIPTOR ChangeLogDesc,
+ IN PCHANGELOG_ENTRY ChangeLogEntry
+ )
+
+/*++
+
+Routine Description:
+
+ This function is a worker routine to scan the change log list. This
+ accepts a pointer to a change log structure and returns a pointer to
+ the previous change log structure. It returns NULL pointer if the given
+ struct is the first change log structure in the list.
+
+ NOTE: This function must be called with the change log locked.
+
+Arguments:
+
+ ChangeLogDesc -- Description of the Changelog buffer being used
+
+ ChangeLogEntry - pointer to a change log strcuture.
+
+Return Value:
+
+ Returns the pointer to the next change log structure in the list.
+
+--*/
+{
+ PCHANGELOG_BLOCK_HEADER ChangeLogBlock;
+
+ ChangeLogBlock = (PCHANGELOG_BLOCK_HEADER)
+ ( (LPBYTE) ChangeLogEntry - sizeof(CHANGELOG_BLOCK_HEADER) );
+
+ NlAssert( ChangeLogBlock->BlockState == BlockUsed ||
+ ChangeLogBlock->BlockState == BlockFree );
+
+ ChangeLogBlock = NlMoveToPrevChangeLogBlock( ChangeLogDesc, ChangeLogBlock );
+
+ //
+ // If we're at the end of the list,
+ // return null
+ //
+ if ( ChangeLogBlock->BlockState == BlockFree ) {
+ return NULL;
+
+
+ //
+ // Skip this block, there will be only one 'Hole' block in the
+ // list.
+ //
+ } else if ( ChangeLogBlock->BlockState == BlockHole ) {
+
+
+ ChangeLogBlock = NlMoveToPrevChangeLogBlock( ChangeLogDesc, ChangeLogBlock );
+
+ if ( ChangeLogBlock->BlockState == BlockFree ) {
+ return NULL;
+ }
+
+ }
+
+ NlAssert( ChangeLogBlock->BlockState == BlockUsed );
+
+ return (PCHANGELOG_ENTRY)
+ ( (LPBYTE)ChangeLogBlock + sizeof(CHANGELOG_BLOCK_HEADER) );
+
+}
+
+
+PCHANGELOG_ENTRY
+NlFindFirstChangeLogEntry(
+ IN PCHANGELOG_DESCRIPTOR ChangeLogDesc,
+ IN DWORD DBIndex
+ )
+/*++
+
+Routine Description:
+
+ Returns a pointer to the first change log entry for the specified
+ database.
+
+ NOTE: This function must be called with the change log locked.
+
+Arguments:
+
+ ChangeLogDesc -- Description of the Changelog buffer being used
+
+ DBIndex - Describes which database to find the changelog entry for.
+
+Return Value:
+
+ Non-NULL - change log entry found
+
+ NULL - No such entry exists.
+
+--*/
+{
+ PCHANGELOG_ENTRY ChangeLogEntry;
+
+ //
+ // If nothing has ever been written to the change log,
+ // indicate nothing is available.
+ //
+
+ if ( ChangeLogIsEmpty( ChangeLogDesc ) ) {
+ return NULL;
+ }
+
+ for ( ChangeLogEntry = (PCHANGELOG_ENTRY) (ChangeLogDesc->Head + 1);
+ ChangeLogEntry != NULL ;
+ ChangeLogEntry = NlMoveToNextChangeLogEntry( ChangeLogDesc, ChangeLogEntry) ) {
+
+ if( ChangeLogEntry->DBIndex == (UCHAR) DBIndex ) {
+ break;
+ }
+ }
+
+ return ChangeLogEntry;
+}
+
+
+
+PCHANGELOG_ENTRY
+NlFindChangeLogEntry(
+ IN PCHANGELOG_DESCRIPTOR ChangeLogDesc,
+ IN LARGE_INTEGER SerialNumber,
+ IN BOOL DownLevel,
+ IN BOOL NeedExactMatch,
+ IN DWORD DBIndex
+ )
+/*++
+
+Routine Description:
+
+ Search the change log entry in change log cache for a given serial
+ number
+
+ NOTE: This function must be called with the change log locked.
+
+Arguments:
+
+ ChangeLogDesc -- Description of the Changelog buffer being used
+
+ SerialNumber - Serial number of the entry to find.
+
+ DownLevel - True if only the least significant portion of the serial
+ number needs to match.
+
+ NeedExactMatch - True if the caller wants us to exactly match the
+ specified serial number.
+
+ DBIndex - Describes which database to find the changelog entry for.
+
+Return Value:
+
+ Non-NULL - change log entry found
+
+ NULL - No such entry exists.
+
+--*/
+{
+ PCHANGELOG_ENTRY ChangeLogEntry;
+ PCHANGELOG_ENTRY PriorChangeLogEntry = NULL;
+
+ //
+ // If nothing has ever been written to the change log,
+ // indicate nothing is available.
+ //
+
+ if ( ChangeLogIsEmpty( ChangeLogDesc ) ) {
+ return NULL;
+ }
+
+ //
+ // Search from the tail of the changelog. For huge changelogs, this should
+ // reduce the working set size since we almost always search for one of
+ // the last few entries.
+ //
+
+ ChangeLogEntry = (PCHANGELOG_ENTRY) (ChangeLogDesc->Tail + 1);
+
+
+ while ( ( ChangeLogEntry =
+ NlMoveToPrevChangeLogEntry( ChangeLogDesc, ChangeLogEntry) ) != NULL ) {
+
+ if( ChangeLogEntry->DBIndex == (UCHAR) DBIndex ) {
+
+ if ( DownLevel ) {
+ if ( ChangeLogEntry->SerialNumber.LowPart ==
+ SerialNumber.LowPart ) {
+ return ChangeLogEntry;
+ }
+ } else {
+ if ( IsSerialNumberEqual( ChangeLogDesc, ChangeLogEntry, &SerialNumber) ){
+ if ( NeedExactMatch &&
+ ChangeLogEntry->SerialNumber.QuadPart != SerialNumber.QuadPart ) {
+ return NULL;
+ }
+ return ChangeLogEntry;
+ }
+
+ //
+ // For the redo log,
+ // find the smallest change log entry that is greater than or equal to
+ // the requested number.
+ //
+
+ if ( ChangeLogDesc->RedoLog && !NeedExactMatch) {
+ if ( ChangeLogEntry->SerialNumber.QuadPart < SerialNumber.QuadPart ) {
+ return PriorChangeLogEntry;
+ }
+
+ }
+ }
+
+ PriorChangeLogEntry = ChangeLogEntry;
+
+ }
+ }
+
+ if ( ChangeLogDesc->RedoLog && !NeedExactMatch ) {
+ return PriorChangeLogEntry;
+ } else {
+ return NULL;
+ }
+}
+
+
+PCHANGELOG_ENTRY
+NlDuplicateChangeLogEntry(
+ IN PCHANGELOG_ENTRY ChangeLogEntry,
+ OUT LPDWORD ChangeLogEntrySize OPTIONAL
+ )
+/*++
+
+Routine Description:
+
+ Duplicate the specified changelog entry into an allocated buffer.
+
+ NOTE: This function must be called with the change log locked.
+
+Arguments:
+
+ ChangeLogEntry -- points to the changelog entry to duplicate
+
+ ChangeLogEntrySize - Optionally returns the size (in bytes) of the
+ returned change log entry.
+
+Return Value:
+
+ NULL - Not enough memory to duplicate the change log entry
+
+ Non-NULL - returns a pointer to the duplicate change log entry. This buffer
+ must be freed via NetpMemoryFree.
+
+--*/
+{
+ PCHANGELOG_ENTRY TempChangeLogEntry;
+ ULONG Size;
+ PCHANGELOG_BLOCK_HEADER ChangeLogBlock;
+
+ ChangeLogBlock = (PCHANGELOG_BLOCK_HEADER)
+ ( (LPBYTE) ChangeLogEntry - sizeof(CHANGELOG_BLOCK_HEADER) );
+
+ Size = ChangeLogBlock->BlockSize -
+ sizeof(CHANGELOG_BLOCK_HEADER) -
+ sizeof(CHANGELOG_BLOCK_TRAILER);
+
+ TempChangeLogEntry = (PCHANGELOG_ENTRY) NetpMemoryAllocate( Size );
+
+ if( TempChangeLogEntry == NULL ) {
+ return NULL;
+ }
+
+ RtlCopyMemory( TempChangeLogEntry, ChangeLogEntry, Size );
+
+ if ( ChangeLogEntrySize != NULL ) {
+ *ChangeLogEntrySize = Size;
+ }
+
+ return TempChangeLogEntry;
+}
+
+
+
+PCHANGELOG_ENTRY
+NlFindPromotionChangeLogEntry(
+ IN PCHANGELOG_DESCRIPTOR ChangeLogDesc,
+ IN LARGE_INTEGER SerialNumber,
+ IN DWORD DBIndex
+ )
+/*++
+
+Routine Description:
+
+ Find the last change log entry with the same promotion count
+ as SerialNumber.
+
+ NOTE: This function must be called with the change log locked.
+
+Arguments:
+
+ ChangeLogDesc -- Description of the Changelog buffer being used
+
+ SerialNumber - Serial number containing the promotion count to query.
+
+ DBIndex - Describes which database to find the changelog entry for.
+
+Return Value:
+
+ Non-NULL - returns a pointer to the duplicate change log entry. This buffer
+ must be freed via NetpMemoryFree.
+
+ NULL - No such entry exists.
+
+--*/
+{
+ PCHANGELOG_ENTRY ChangeLogEntry;
+ LONG GoalPromotionCount;
+ LONG PromotionCount;
+
+ //
+ // If nothing has ever been written to the change log,
+ // indicate nothing is available.
+ //
+
+ if ( ChangeLogIsEmpty( ChangeLogDesc ) ) {
+ return NULL;
+ }
+
+
+
+ //
+ // Search from the tail of the changelog. For huge changelogs, this should
+ // reduce the working set size since we almost always search for one of
+ // the last few entries.
+ //
+
+ ChangeLogEntry = (PCHANGELOG_ENTRY) (ChangeLogDesc->Tail + 1);
+ GoalPromotionCount = SerialNumber.HighPart & NlGlobalChangeLogPromotionMask;
+
+ while ( ( ChangeLogEntry =
+ NlMoveToPrevChangeLogEntry( ChangeLogDesc, ChangeLogEntry) ) != NULL ) {
+
+ if( ChangeLogEntry->DBIndex == (UCHAR) DBIndex ) {
+ PromotionCount = ChangeLogEntry->SerialNumber.HighPart & NlGlobalChangeLogPromotionMask;
+
+ //
+ // If the Current Change Log entry has a greater promotion count,
+ // continue searching backward.
+ //
+
+ if ( PromotionCount > GoalPromotionCount ) {
+ continue;
+ }
+
+ //
+ // If the current change log entry has a smaller promotion count,
+ // indicate we couldn't find a change log entry.
+ //
+
+ if ( PromotionCount < GoalPromotionCount ) {
+ break;
+ }
+
+ //
+ // Otherwise, success
+ //
+
+ return NlDuplicateChangeLogEntry( ChangeLogEntry, NULL );
+
+ }
+ }
+
+ return NULL;
+}
+
+
+PCHANGELOG_ENTRY
+NlGetNextDownlevelChangeLogEntry(
+ ULONG DownlevelSerialNumber
+ )
+/*++
+
+Routine Description:
+
+ Find the change log entry for the delta with a serial number greater
+ than the one specified.
+
+ NOTE: This function must be called with the change log locked.
+
+Arguments:
+
+ DownlevelSerialNumber - The downlevel serial number
+
+Return Value:
+
+ Non-NULL - change log entry found. This changelog entry must be
+ deallocated using NetpMemoryFree.
+
+ NULL - No such entry exists.
+
+--*/
+{
+ PCHANGELOG_ENTRY ChangeLogEntry;
+ LARGE_INTEGER SerialNumber;
+
+ SerialNumber.QuadPart = DownlevelSerialNumber + 1;
+
+ ChangeLogEntry = NlFindChangeLogEntry( &NlGlobalChangeLogDesc, SerialNumber, TRUE, TRUE, SAM_DB);
+
+ if ( ChangeLogEntry == NULL ||
+ ChangeLogEntry->DeltaType == DummyChangeLogEntry ) {
+ return NULL;
+ }
+
+ return NlDuplicateChangeLogEntry( ChangeLogEntry, NULL );
+}
+
+
+PCHANGELOG_ENTRY
+NlFindNextChangeLogEntry(
+ IN PCHANGELOG_DESCRIPTOR ChangeLogDesc,
+ IN PCHANGELOG_ENTRY LastChangeLogEntry,
+ IN DWORD DBIndex
+ )
+/*++
+
+Routine Description:
+
+ Find the next change log entry in change log following a particular
+ changelog entry.
+
+ NOTE: This function must be called with the change log locked.
+
+Arguments:
+
+ ChangeLogDesc -- Description of the Changelog buffer being used
+
+ LastChangeLogEntry - last found changelog entry.
+
+ DBIndex - database index of the next entry to find
+
+Return Value:
+
+ Non-null - change log entry found
+
+ NULL - No such entry exists.
+
+
+--*/
+{
+ PCHANGELOG_ENTRY NextChangeLogEntry = LastChangeLogEntry;
+ LARGE_INTEGER SerialNumber;
+
+ //
+ // Loop through the log finding this entry starting from the last
+ // found record.
+ //
+
+ SerialNumber.QuadPart = LastChangeLogEntry->SerialNumber.QuadPart + 1;
+ while ( ( NextChangeLogEntry =
+ NlMoveToNextChangeLogEntry( ChangeLogDesc, NextChangeLogEntry) ) != NULL ) {
+
+ if( NextChangeLogEntry->DBIndex == DBIndex ) {
+
+ //
+ // next log entry in the change log for
+ // this database. The serial number should match.
+ //
+
+ if ( !IsSerialNumberEqual( ChangeLogDesc, NextChangeLogEntry, &SerialNumber) ) {
+
+ //
+ // For the redo log, the serial numbers merely need to be ascending.
+ //
+
+ if ( !ChangeLogDesc->RedoLog ||
+ NextChangeLogEntry->SerialNumber.QuadPart < SerialNumber.QuadPart ) {
+
+ NlPrint((NL_CRITICAL,
+ "NlFindNextChangeLogEntry: Serial numbers not contigous %lx %lx and %lx %lx\n",
+ NextChangeLogEntry->SerialNumber.HighPart,
+ NextChangeLogEntry->SerialNumber.LowPart,
+ SerialNumber.HighPart,
+ SerialNumber.LowPart ));
+
+ //
+ // write event log
+ //
+
+ NlpWriteEventlog (
+ NELOG_NetlogonChangeLogCorrupt,
+ EVENTLOG_ERROR_TYPE,
+ (LPBYTE)&DBIndex,
+ sizeof(DBIndex),
+ NULL,
+ 0 );
+
+ return NULL;
+ }
+
+ }
+
+ return NextChangeLogEntry;
+
+ }
+ }
+
+ return NULL;
+}
+
+
+BOOLEAN
+NlCompareChangeLogEntries(
+ IN PCHANGELOG_ENTRY ChangeLogEntry1,
+ IN PCHANGELOG_ENTRY ChangeLogEntry2
+ )
+/*++
+
+Routine Description:
+
+ The two change log entries are compared to see if the are for the same
+ object. If
+
+Arguments:
+
+ ChangeLogEntry1 - First change log entry to compare.
+
+ ChangeLogEntry2 - Second change log entry to compare.
+
+Return Value:
+
+ TRUE - iff the change log entries are for the same object.
+
+--*/
+{
+ //
+ // Ensure the DbIndex is the same for both entries.
+ //
+
+ if ( ChangeLogEntry1->DBIndex != ChangeLogEntry2->DBIndex ) {
+ return FALSE;
+ }
+
+ //
+ // Ensure the entries both describe the same object type.
+ //
+
+ if ( ChangeLogEntry1->DeltaType >= MAX_DELETE_DELTA ) {
+ NlPrint(( NL_CRITICAL,
+ "NlCompateChangeLogEntries: invalid delta type %lx\n",
+ ChangeLogEntry1->DeltaType ));
+ return FALSE;
+ }
+
+ if ( ChangeLogEntry2->DeltaType >= MAX_DELETE_DELTA ) {
+ NlPrint(( NL_CRITICAL,
+ "NlCompateChangeLogEntries: invalid delta type %lx\n",
+ ChangeLogEntry2->DeltaType ));
+ return FALSE;
+ }
+
+ if ( NlGlobalDeleteDeltaType[ChangeLogEntry1->DeltaType] !=
+ NlGlobalDeleteDeltaType[ChangeLogEntry2->DeltaType] ) {
+ return FALSE;
+ }
+
+ //
+ // Depending on the delta type, ensure the entries refer to the same object.
+ //
+
+ switch(ChangeLogEntry1->DeltaType) {
+
+ case AddOrChangeGroup:
+ case DeleteGroup:
+ case RenameGroup:
+ case AddOrChangeUser:
+ case DeleteUser:
+ case RenameUser:
+ case ChangeGroupMembership:
+ case AddOrChangeAlias:
+ case DeleteAlias:
+ case RenameAlias:
+ case ChangeAliasMembership:
+
+ if (ChangeLogEntry1->ObjectRid == ChangeLogEntry2->ObjectRid ) {
+ return TRUE;
+ }
+ break;
+
+
+ case AddOrChangeLsaTDomain:
+ case DeleteLsaTDomain:
+ case AddOrChangeLsaAccount:
+ case DeleteLsaAccount:
+
+ NlAssert( ChangeLogEntry1->Flags & CHANGELOG_SID_SPECIFIED );
+ NlAssert( ChangeLogEntry2->Flags & CHANGELOG_SID_SPECIFIED );
+
+ if( (ChangeLogEntry1->Flags & CHANGELOG_SID_SPECIFIED) == 0 ||
+ (ChangeLogEntry2->Flags & CHANGELOG_SID_SPECIFIED) == 0) {
+ break;
+ }
+
+ if( RtlEqualSid(
+ (PSID)((LPBYTE)ChangeLogEntry1 + sizeof(CHANGELOG_ENTRY)),
+ (PSID)((LPBYTE)ChangeLogEntry2 + sizeof(CHANGELOG_ENTRY))) ) {
+
+ return TRUE;
+ }
+ break;
+
+ case AddOrChangeLsaSecret:
+ case DeleteLsaSecret:
+
+ NlAssert( ChangeLogEntry1->Flags & CHANGELOG_NAME_SPECIFIED );
+ NlAssert( ChangeLogEntry2->Flags & CHANGELOG_NAME_SPECIFIED );
+
+ if( (ChangeLogEntry1->Flags & CHANGELOG_NAME_SPECIFIED) == 0 ||
+ (ChangeLogEntry2->Flags & CHANGELOG_NAME_SPECIFIED) == 0 ) {
+ break;
+ }
+
+ if( _wcsicmp(
+ (LPWSTR)((LPBYTE)ChangeLogEntry1 + sizeof(CHANGELOG_ENTRY)),
+ (LPWSTR)((LPBYTE)ChangeLogEntry2 + sizeof(CHANGELOG_ENTRY))
+ ) == 0 ) {
+
+ return TRUE;
+ }
+ break;
+
+ case AddOrChangeLsaPolicy:
+ case AddOrChangeDomain:
+ return TRUE;
+
+ default:
+ NlPrint((NL_CRITICAL,
+ "NlCompareChangeLogEntries: invalid delta type %lx\n",
+ ChangeLogEntry1->DeltaType ));
+ break;
+ }
+
+ return FALSE;
+}
+
+
+PCHANGELOG_ENTRY
+NlGetNextChangeLogEntry(
+ IN PCHANGELOG_DESCRIPTOR ChangeLogDesc,
+ IN LARGE_INTEGER SerialNumber,
+ IN DWORD DBIndex,
+ OUT LPDWORD ChangeLogEntrySize OPTIONAL
+ )
+/*++
+
+Routine Description:
+
+ Search the change log entry in change log cache for a given serial
+ number.
+
+Arguments:
+
+ ChangeLogDesc -- Description of the Changelog buffer to use.
+
+ SerialNumber - Serial number preceeding that of the entry to find.
+
+ DBIndex - Describes which database to find the changelog entry for.
+
+ ChangeLogEntrySize - Optionally returns the size (in bytes) of the
+ returned change log entry.
+
+Return Value:
+
+ Non-NULL - returns a pointer to a duplicate of the found change log entry.
+ This buffer must be freed via NetpMemoryFree.
+
+ NULL - No such entry exists.
+
+
+
+--*/
+{
+ PCHANGELOG_ENTRY ChangeLogEntry;
+
+
+ //
+ // Increment the serial number, get the change log entry, duplicate it
+ //
+
+ LOCK_CHANGELOG();
+ SerialNumber.QuadPart += 1;
+ ChangeLogEntry = NlFindChangeLogEntry(
+ ChangeLogDesc,
+ SerialNumber,
+ FALSE,
+ FALSE,
+ DBIndex );
+
+ if ( ChangeLogEntry != NULL ) {
+ ChangeLogEntry = NlDuplicateChangeLogEntry(ChangeLogEntry, ChangeLogEntrySize );
+ }
+
+ UNLOCK_CHANGELOG();
+ return ChangeLogEntry;
+}
+
+
+PCHANGELOG_ENTRY
+NlGetNextUniqueChangeLogEntry(
+ IN PCHANGELOG_DESCRIPTOR ChangeLogDesc,
+ IN LARGE_INTEGER SerialNumber,
+ IN DWORD DBIndex,
+ OUT LPDWORD ChangeLogEntrySize OPTIONAL
+ )
+/*++
+
+Routine Description:
+
+ Search the change log entry in change log cache for a given serial
+ number. If there are more than one change log entry for the same
+ object then this routine will return the last log entry of that
+ object.
+
+ NOTE: This function must be called with the change log locked.
+
+Arguments:
+
+ ChangeLogDesc -- Description of the Changelog buffer to use.
+
+ SerialNumber - Serial number preceeding that of the entry to find.
+
+ DBIndex - Describes which database to find the changelog entry for.
+
+ ChangeLogEntrySize - Optionally returns the size (in bytes) of the
+ returned change log entry.
+
+Return Value:
+
+ Non-NULL - returns a pointer to a duplicate of the found change log entry.
+ This buffer must be freed via NetpMemoryFree.
+
+ NULL - No such entry exists.
+
+
+
+--*/
+{
+ PCHANGELOG_ENTRY ChangeLogEntry;
+ PCHANGELOG_ENTRY NextChangeLogEntry;
+ PCHANGELOG_ENTRY FoundChangeLogEntry;
+
+
+ //
+ // Get the first entry we want to deal with.
+ //
+ SerialNumber.QuadPart += 1;
+ ChangeLogEntry = NlFindChangeLogEntry(
+ ChangeLogDesc,
+ SerialNumber,
+ FALSE,
+ FALSE,
+ DBIndex );
+
+ if ( ChangeLogEntry == NULL ) {
+ return NULL;
+ }
+
+
+ //
+ // Skip over any leading dummy change log entries
+ //
+
+ while ( ChangeLogEntry->DeltaType == DummyChangeLogEntry ) {
+
+ //
+ // Get the next change log entry to compare with.
+ //
+
+ NextChangeLogEntry = NlFindNextChangeLogEntry( ChangeLogDesc,
+ ChangeLogEntry,
+ DBIndex );
+
+ if( NextChangeLogEntry == NULL ) {
+ return NULL;
+ }
+
+ //
+ // skip 'ChangeLogEntry' entry
+ //
+
+ ChangeLogEntry = NextChangeLogEntry;
+ }
+
+
+ //
+ // Check to see if the next entry is a "duplicate" of this entry.
+ //
+
+ FoundChangeLogEntry = ChangeLogEntry;
+
+ for (;;) {
+
+ //
+ // Don't walk past a change log entry for a promotion.
+ // Promotions don't happen very often, but passing the BDC the
+ // change log entry will allow it to do a better job of building
+ // its own change log.
+ //
+
+ if ( FoundChangeLogEntry->Flags & CHANGELOG_PDC_PROMOTION ) {
+ break;
+ }
+
+ //
+ // Get the next change log entry to compare with.
+ //
+
+ NextChangeLogEntry = NlFindNextChangeLogEntry( ChangeLogDesc,
+ ChangeLogEntry,
+ DBIndex );
+
+ if( NextChangeLogEntry == NULL ) {
+ break;
+ }
+
+ //
+ // Just skip any dummy entries.
+ //
+
+ if ( NextChangeLogEntry->DeltaType == DummyChangeLogEntry ) {
+ ChangeLogEntry = NextChangeLogEntry;
+ continue;
+ }
+
+ //
+ // if 'FoundChangeLogEntry' and 'NextChangeLogEntry' entries are
+ // for different objects or are different delta types.
+ // then return 'FoundChangeLogEntry' to the caller.
+ //
+
+ if ( FoundChangeLogEntry->DeltaType != NextChangeLogEntry->DeltaType ||
+ !NlCompareChangeLogEntries( FoundChangeLogEntry, NextChangeLogEntry ) ){
+ break;
+
+ }
+
+
+ //
+ // Skip 'FoundChangeLogEntry' entry
+ // Mark this entry as the being the best one to return.
+ //
+
+ ChangeLogEntry = NextChangeLogEntry;
+ FoundChangeLogEntry = ChangeLogEntry;
+ }
+
+ return NlDuplicateChangeLogEntry(FoundChangeLogEntry, ChangeLogEntrySize );
+}
+
+
+BOOL
+NlRecoverChangeLog(
+ PCHANGELOG_ENTRY OrigChangeLogEntry
+ )
+
+/*++
+
+Routine Description:
+
+ This routine traverses the change log list from current change log entry
+ determines whether the current change log can be ignored under
+ special conditions.
+
+Arguments:
+
+ OrigChangeLogEntry - pointer to log structure that is under investigation.
+
+Return Value:
+
+ TRUE - if the given change log can be ignored.
+
+ FALSE - otherwise.
+
+--*/
+{
+ PCHANGELOG_ENTRY NextChangeLogEntry;
+ BOOLEAN ReturnValue;
+
+ //
+ // Find the original change log entry.
+ //
+
+ LOCK_CHANGELOG();
+ NextChangeLogEntry = NlFindChangeLogEntry(
+ &NlGlobalChangeLogDesc,
+ OrigChangeLogEntry->SerialNumber,
+ FALSE, // Not downlevel
+ FALSE, // Not exact match
+ OrigChangeLogEntry->DBIndex );
+
+ if (NextChangeLogEntry == NULL) {
+ ReturnValue = FALSE;
+ goto Cleanup;
+ }
+
+ if ( OrigChangeLogEntry->DeltaType >= MAX_DELETE_DELTA ) {
+ NlPrint(( NL_CRITICAL,
+ "NlRecoverChangeLog: invalid delta type %lx\n",
+ OrigChangeLogEntry->DeltaType ));
+ ReturnValue = FALSE;
+ goto Cleanup;
+ }
+
+ //
+ // Loop for each entry with a greater serial number.
+ //
+
+ for (;;) {
+
+ NextChangeLogEntry = NlFindNextChangeLogEntry(
+ &NlGlobalChangeLogDesc,
+ NextChangeLogEntry,
+ OrigChangeLogEntry->DBIndex );
+
+ if (NextChangeLogEntry == NULL) {
+ break;
+ }
+
+ //
+ // If the delta we found is the type that deletes the original delta,
+ // and the objects described by the two deltas are the same,
+ // tell the caller to not worry about the original delta failing.
+ //
+
+ if ( NextChangeLogEntry->DeltaType ==
+ NlGlobalDeleteDeltaType[OrigChangeLogEntry->DeltaType] &&
+ NlCompareChangeLogEntries( OrigChangeLogEntry,
+ NextChangeLogEntry ) ) {
+ ReturnValue = TRUE;
+ goto Cleanup;
+ }
+
+ }
+
+ ReturnValue = FALSE;
+
+Cleanup:
+ UNLOCK_CHANGELOG();
+ return ReturnValue;
+
+}
+
+
+VOID
+NlVoidChangeLogEntry(
+ IN PCHANGELOG_DESCRIPTOR ChangeLogDesc,
+ IN PCHANGELOG_ENTRY ChangeLogEntry,
+ IN BOOLEAN FlushIt
+ )
+/*++
+
+Routine Description:
+
+ Mark a changelog entry as void. If there are no more change log entries in the file,
+ the file is deleted.
+
+ NOTE: This function must be called with the change log locked.
+
+Arguments:
+
+ ChangeLogDesc -- Description of the Changelog buffer to use.
+
+ ChangeLogEntry -- Change Log Entry to mark as void.
+
+ FlushIt - TRUE if the bytes are to be flushed to disk
+
+Return Value:
+
+ None.
+
+--*/
+{
+ DWORD DBIndex = ChangeLogEntry->DBIndex;
+
+
+ //
+ // Mark the changelog entry as being deleted.
+ // (and force the change to disk).
+ //
+
+ NlPrint((NL_CHANGELOG,
+ "NlVoidChangeLogEntry: %lx %lx: deleting change log entry.\n",
+ ChangeLogEntry->SerialNumber.HighPart,
+ ChangeLogEntry->SerialNumber.LowPart ));
+
+ ChangeLogDesc->EntryCount[DBIndex] --;
+
+ ChangeLogEntry->DBIndex = VOID_DB;
+
+ (VOID) NlWriteChangeLogBytes(
+ ChangeLogDesc,
+ &ChangeLogEntry->DBIndex,
+ sizeof(ChangeLogEntry->DBIndex),
+ FlushIt );
+
+ //
+ // If the changelog is now empty,
+ // delete it.
+ //
+ // Only delete a redo log.
+
+ if ( ChangeLogDesc->RedoLog && ChangeLogDesc->EntryCount[DBIndex] == 0 ) {
+ DWORD i;
+ for( i = 0; i < NUM_DBS; i++ ) {
+ if (ChangeLogDesc->EntryCount[i] != 0 ) {
+ break;
+ }
+ }
+
+ if ( i == NUM_DBS ) {
+ WCHAR ChangeLogFile[PATHLEN+1];
+
+ NlPrint(( NL_CHANGELOG,
+ "NlVoidChangeLogEntry: redo log is now empty. Delete it.\n" ));
+
+ //
+ // Close the file and delete the buffer.
+ //
+
+ NlCloseChangeLogFile( ChangeLogDesc );
+
+ //
+ // Delete the file itself.
+ //
+
+ wcscpy( ChangeLogFile, NlGlobalChangeLogFilePrefix );
+ wcscat( ChangeLogFile,
+ ChangeLogDesc->RedoLog ? REDO_FILE_POSTFIX : CHANGELOG_FILE_POSTFIX );
+ if ( !DeleteFile( ChangeLogFile ) ) {
+ NlPrint(( NL_CRITICAL,
+ "NlVoidChangeLogEntry: cannot delete redo log %ld.\n",
+ GetLastError() ));
+ }
+
+
+ }
+ }
+
+ return;
+}
+
+
+VOID
+NlDeleteChangeLogEntry(
+ IN PCHANGELOG_DESCRIPTOR ChangeLogDesc,
+ IN DWORD DBIndex,
+ IN LARGE_INTEGER SerialNumber
+ )
+/*++
+
+Routine Description:
+
+ This routine deletes the change log entry with the particular serial number.
+
+Arguments:
+
+ ChangeLogDesc -- Description of the Changelog buffer to use.
+
+ DBIndex - Describes which database to find the changelog entry for.
+
+ SerialNumber - Serial number of the entry to find.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PCHANGELOG_ENTRY ChangeLogEntry;
+
+
+
+ //
+ // Find the specified change log entry.
+ //
+
+ LOCK_CHANGELOG();
+ ChangeLogEntry = NlFindChangeLogEntry(
+ ChangeLogDesc,
+ SerialNumber,
+ FALSE, // Not downlevel
+ TRUE, // Exact match
+ DBIndex );
+
+ if (ChangeLogEntry != NULL) {
+
+ //
+ // Mark the changelog entry as being deleted.
+ // (and force the change to disk).
+ //
+
+ NlVoidChangeLogEntry( ChangeLogDesc, ChangeLogEntry, TRUE );
+
+ } else {
+ NlPrint((NL_CRITICAL,
+ "NlDeleteChangeLogEntry: %lx %lx: couldn't find change log entry.\n",
+ ChangeLogEntry->SerialNumber.HighPart,
+ ChangeLogEntry->SerialNumber.LowPart ));
+ }
+
+ UNLOCK_CHANGELOG();
+ return;
+}
+
+
+NTSTATUS
+NlCopyChangeLogEntry(
+ IN PCHANGELOG_DESCRIPTOR SourceChangeLogDesc,
+ IN PCHANGELOG_ENTRY SourceChangeLogEntry,
+ IN PCHANGELOG_DESCRIPTOR DestChangeLogDesc
+)
+/*++
+
+Routine Description:
+
+ Copies the specified change log entry for the specified "source" change log to
+ the specified "destination" change log. The caller is responsible for flushing the
+ entry to disk by calling NlFlushChangeLog.
+
+ NOTE: This function must be called with the change log locked.
+
+Arguments:
+
+ SourceChangeLogDesc -- a description of the Changelog buffer to copy from
+
+ SourceChangeLogEntry -- The particular entry to copy
+
+ DestChangeLogDesc -- a description of the ChangelogBuffer to copy to
+
+Return Value:
+
+ NT Status code
+
+--*/
+{
+ NTSTATUS Status;
+ CHANGELOG_ENTRY DestChangeLogEntry;
+ PSID ObjectSid;
+ UNICODE_STRING ObjectNameString;
+ PUNICODE_STRING ObjectName;
+
+ //
+ // If this entry has been marked void, ignore it.
+ //
+
+ if ( SourceChangeLogEntry->DBIndex == VOID_DB ) {
+ return STATUS_SUCCESS;
+ }
+
+ //
+ // Build a version 4 changelog entry from a version 3 one.
+ //
+
+ ObjectSid = NULL;
+ ObjectName = NULL;
+
+ if ( SourceChangeLogDesc->Version3 ) {
+ PCHANGELOG_ENTRY_V3 Version3;
+
+ Version3 = (PCHANGELOG_ENTRY_V3)SourceChangeLogEntry;
+
+ DestChangeLogEntry.SerialNumber = Version3->SerialNumber;
+ DestChangeLogEntry.DeltaType = (BYTE) Version3->DeltaType;
+ DestChangeLogEntry.DBIndex = Version3->DBIndex;
+ DestChangeLogEntry.ObjectRid = Version3->ObjectRid;
+ DestChangeLogEntry.Flags = Version3->ReplicateImmediately ?
+ CHANGELOG_REPLICATE_IMMEDIATELY :
+ 0;
+ if ( Version3->ObjectSidOffset ) {
+ ObjectSid = (PSID)(((LPBYTE)Version3) +
+ Version3->ObjectSidOffset);
+ }
+ if ( Version3->ObjectNameOffset ) {
+ RtlInitUnicodeString( &ObjectNameString,
+ (LPWSTR)(((LPBYTE)Version3) +
+ Version3->ObjectNameOffset));
+ ObjectName = &ObjectNameString;
+ }
+
+ //
+ // Build a version 4 changelog entry from a version 4 one.
+ //
+ } else {
+
+ DestChangeLogEntry = *SourceChangeLogEntry;
+
+ if ( SourceChangeLogEntry->Flags & CHANGELOG_SID_SPECIFIED ) {
+ ObjectSid = (PSID)(((LPBYTE)SourceChangeLogEntry) +
+ sizeof(CHANGELOG_ENTRY));
+ } else if ( SourceChangeLogEntry->Flags & CHANGELOG_NAME_SPECIFIED ) {
+ RtlInitUnicodeString( &ObjectNameString,
+ (LPWSTR)(((LPBYTE)SourceChangeLogEntry) +
+ sizeof(CHANGELOG_ENTRY)));
+ ObjectName = &ObjectNameString;
+ }
+
+
+ }
+
+
+ Status = NlWriteChangeLogEntry( DestChangeLogDesc,
+ &DestChangeLogEntry,
+ ObjectSid,
+ ObjectName,
+ FALSE ); // Don't flush to disk
+
+ return Status;
+}
+
+
+BOOLEAN
+NlFixChangeLog(
+ IN PCHANGELOG_DESCRIPTOR ChangeLogDesc,
+ IN DWORD DBIndex,
+ IN LARGE_INTEGER SerialNumber,
+ IN BOOLEAN CopyEntriesToRedoLog
+ )
+/*++
+
+Routine Description:
+
+ This routine scans the change log and 'removes' all change log entries
+ with a serial number greater than the one specified.
+
+ NOTE: This function must be called with the change log locked.
+
+Arguments:
+
+ ChangeLogDesc -- Description of the Changelog buffer to use.
+
+ DBIndex - Describes which database to find the changelog entry for.
+
+ SerialNumber - Serial number of the entry to find.
+
+ CopyEntriesToRedoLog - TRUE to indicate that all deleted entries need to be copied
+ to the redo log.
+
+Return Value:
+
+ TRUE -- if the entry specied by SerialNumber was found.
+
+--*/
+{
+ PCHANGELOG_ENTRY ChangeLogEntry;
+ BOOLEAN SkipFirstEntry = TRUE;
+
+ //
+ // In all cases,
+ // the new serial number of the change log is the one passed in.
+ //
+
+ ChangeLogDesc->SerialNumber[DBIndex] = SerialNumber;
+
+ //
+ // Find the specified change log entry.
+ //
+
+ ChangeLogEntry = NlFindChangeLogEntry(
+ ChangeLogDesc,
+ SerialNumber,
+ FALSE, // Not downlevel
+ TRUE, // exact match
+ DBIndex );
+
+ if (ChangeLogEntry == NULL) {
+
+ //
+ // If we can't find the entry,
+ // simply start from the beginning and delete all entries for this
+ // database.
+ //
+
+ if ( !CopyEntriesToRedoLog ) {
+ ChangeLogEntry = NlFindFirstChangeLogEntry( ChangeLogDesc, DBIndex );
+ SkipFirstEntry = FALSE;
+ }
+
+ if (ChangeLogEntry == NULL) {
+ return FALSE;
+ }
+ }
+
+
+ //
+ // Loop for each entry with a greater serial number.
+ //
+
+ for (;;) {
+
+ //
+ // Skip past the previous entry.
+ //
+ // Don't do this the first time if we want to start at the very beginning.
+ //
+
+ if ( SkipFirstEntry ) {
+ ChangeLogEntry = NlFindNextChangeLogEntry( ChangeLogDesc,
+ ChangeLogEntry,
+ DBIndex );
+ } else {
+ SkipFirstEntry = TRUE;
+ }
+
+
+ if (ChangeLogEntry == NULL) {
+ break;
+ }
+
+ //
+ // Write the entry to the redo log.
+ //
+
+ if ( CopyEntriesToRedoLog ) {
+ NTSTATUS TempStatus;
+ NlAssert( ChangeLogDesc != &NlGlobalRedoLogDesc );
+
+ TempStatus = NlCopyChangeLogEntry( ChangeLogDesc,
+ ChangeLogEntry,
+ &NlGlobalRedoLogDesc );
+
+ if ( !NT_SUCCESS(TempStatus) ) {
+ NlPrint(( NL_CRITICAL,
+ "NlFixChangeLog: Cannot write redo log 0x%lx\n",
+ TempStatus ));
+ }
+ }
+
+ //
+ // Mark the changelog entry as being deleted.
+ // (but don't flush to disk yet).
+ //
+
+ NlVoidChangeLogEntry( ChangeLogDesc, ChangeLogEntry, FALSE );
+
+ //
+ // If deleteing the change log entry caused the changelog to be deleted,
+ // exit now since 'ChangeLogEntry' points to freed memory.
+ //
+
+ if ( ChangeLogDesc->EntryCount[DBIndex] == 0 ) {
+ break;
+ }
+
+ }
+
+ //
+ // Flush all the changes to disk.
+ //
+
+ (VOID) NlFlushChangeLog( ChangeLogDesc );
+ if ( CopyEntriesToRedoLog ) {
+ NlFlushChangeLog( &NlGlobalRedoLogDesc );
+ }
+
+
+ return TRUE;
+}
+
+
+BOOL
+NlValidateChangeLogEntry(
+ IN PCHANGELOG_ENTRY ChangeLogEntry,
+ IN DWORD ChangeLogEntrySize
+ )
+/*++
+
+Routine Description:
+
+ Validate the a ChangeLogEntry is structurally sound.
+
+Arguments:
+
+ ChangeLogEntry: pointer to a change log entry.
+
+ ChangeLogEntrySize -- Size (in bytes) of the change log entry not including
+ header and trailer.
+
+Return Value:
+
+ TRUE: if the given entry is valid
+
+ FALSE: otherwise.
+
+--*/
+{
+
+ //
+ // Ensure the entry is big enough.
+ //
+
+ if ( ChangeLogEntrySize < sizeof(CHANGELOG_ENTRY) ) {
+ NlPrint((NL_CRITICAL,
+ "NlValidateChangeLogEntry: Entry size is too small: %ld\n",
+ ChangeLogEntrySize ));
+ return FALSE;
+ }
+
+ //
+ // Ensure strings are zero terminated.
+ //
+
+ if ( ChangeLogEntry->Flags & CHANGELOG_NAME_SPECIFIED ) {
+
+ LPWSTR ZeroTerminator = (LPWSTR)(ChangeLogEntry+1);
+ BOOLEAN ZeroTerminatorFound = FALSE;
+
+ if ( ChangeLogEntry->Flags & CHANGELOG_SID_SPECIFIED ) {
+ NlPrint((NL_CRITICAL,
+ "NlValidateChangeLogEntry: %lx %lx: both Name and Sid specified.\n",
+ ChangeLogEntry->SerialNumber.HighPart,
+ ChangeLogEntry->SerialNumber.LowPart ));
+ return FALSE;
+ }
+
+ while ( (DWORD)((LPBYTE)ZeroTerminator - (LPBYTE) ChangeLogEntry) <
+ ChangeLogEntrySize - 1 ) {
+
+ if ( *ZeroTerminator == L'\0' ) {
+ ZeroTerminatorFound = TRUE;
+ break;
+ }
+ ZeroTerminator ++;
+ }
+
+ if ( !ZeroTerminatorFound ) {
+ NlPrint((NL_CRITICAL,
+ "NlValidateChangeLogEntry: %lx %lx: String not zero terminated. (no string)\n",
+ ChangeLogEntry->SerialNumber.HighPart,
+ ChangeLogEntry->SerialNumber.LowPart ));
+ return FALSE;
+ }
+
+ }
+
+ //
+ // Ensure the sid is entirely within the block.
+ //
+
+ if ( ChangeLogEntry->Flags & CHANGELOG_SID_SPECIFIED ) {
+
+ if ( GetSidLengthRequired(0) >
+ ChangeLogEntrySize - sizeof(*ChangeLogEntry) ||
+ RtlLengthSid( (PSID)(ChangeLogEntry+1) ) >
+ ChangeLogEntrySize - sizeof(*ChangeLogEntry) ) {
+ NlPrint((NL_CRITICAL,
+ "NlValidateChangeLogEntry: %lx %lx: Sid too large.\n",
+ ChangeLogEntry->SerialNumber.HighPart,
+ ChangeLogEntry->SerialNumber.LowPart ));
+ return FALSE;
+ }
+
+ }
+
+ //
+ // Ensure the database # is valid.
+ // ARGH! Allow VOID_DB.
+ //
+
+ if ( ChangeLogEntry->DBIndex > NUM_DBS ) {
+ NlPrint((NL_CRITICAL,
+ "NlValidateChangeLogEntry: %lx %lx: DBIndex is bad %ld.\n",
+ ChangeLogEntry->SerialNumber.HighPart,
+ ChangeLogEntry->SerialNumber.LowPart,
+ ChangeLogEntry->DBIndex ));
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+BOOL
+ValidateThisEntry(
+ IN OUT PCHANGELOG_DESCRIPTOR ChangeLogDesc,
+ IN PCHANGELOG_ENTRY ChangeLogEntry,
+ IN OUT PLARGE_INTEGER NextSerialNumber,
+ IN BOOLEAN InitialCall
+ )
+/*++
+
+Routine Description:
+
+ Determine the given log entry is a valid next log in the change log
+ list.
+
+ NOTE: This function must be called with the change log locked.
+
+Arguments:
+
+ ChangeLogDesc -- Description of the Changelog buffer to validate.
+
+ ChangeLogEntry: pointer to a new log entry.
+
+ NextSerialNumber: pointer to an array of serial numbers.
+ (NULL if serial numbers aren't to be validated.)
+
+ Initialcall: TRUE iff SerialNumber array should be initialized.
+
+Return Value:
+
+ TRUE: if the given entry is a valid next entry.
+
+ FALSE: otherwise.
+
+Assumed: non-empty ChangeLog list.
+
+--*/
+{
+ PCHANGELOG_BLOCK_HEADER Block = ((PCHANGELOG_BLOCK_HEADER)ChangeLogEntry) - 1;
+
+ //
+ // Do Version 3 specific things
+ //
+
+ if ( ChangeLogDesc->Version3 ) {
+
+ //
+ // Ensure the block is big enough.
+ //
+
+ if ( Block->BlockSize <
+ sizeof(CHANGELOG_ENTRY_V3) + sizeof(CHANGELOG_BLOCK_HEADER) ) {
+ NlPrint((NL_CRITICAL,
+ "ValidateThisEntry: Block size is too small: %ld\n",
+ Block->BlockSize ));
+ return FALSE;
+ }
+
+ //
+ // Ensure the database # is valid.
+ //
+
+ if ( ChangeLogEntry->DBIndex > NUM_DBS ) {
+ NlPrint((NL_CRITICAL,
+ "ValidateThisEntry: %lx %lx: DBIndex is bad %ld.\n",
+ ChangeLogEntry->SerialNumber.HighPart,
+ ChangeLogEntry->SerialNumber.LowPart,
+ ChangeLogEntry->DBIndex ));
+ return FALSE;
+ }
+
+
+ //
+ // Do version 4 specific validation
+ //
+
+ } else {
+
+ //
+ // Ensure the block is big enough.
+ //
+
+ if ( Block->BlockSize <
+ sizeof(CHANGELOG_BLOCK_HEADER) +
+ sizeof(CHANGELOG_ENTRY) +
+ sizeof(CHANGELOG_BLOCK_TRAILER) ) {
+
+ NlPrint((NL_CRITICAL,
+ "ValidateThisEntry: Block size is too small: %ld\n",
+ Block->BlockSize ));
+ return FALSE;
+ }
+
+
+ //
+ // Validate the contents of the block itself.
+ //
+
+ if ( !NlValidateChangeLogEntry(
+ ChangeLogEntry,
+ Block->BlockSize -
+ sizeof(CHANGELOG_BLOCK_HEADER) -
+ sizeof(CHANGELOG_BLOCK_TRAILER) ) ) {
+
+ return FALSE;
+ }
+
+ }
+
+
+ //
+ // Validate the serial number sequence.
+ //
+
+ if ( ChangeLogEntry->DBIndex != VOID_DB && NextSerialNumber != NULL ) {
+
+ //
+ // If this is the first entry in the database,
+ // Save its serial number.
+ //
+
+ if ( NextSerialNumber[ChangeLogEntry->DBIndex].QuadPart == 0 ) {
+
+ //
+ // first entry for this database
+ //
+
+ NextSerialNumber[ChangeLogEntry->DBIndex] = ChangeLogEntry->SerialNumber;
+
+
+ //
+ // Otherwise ensure the serial number is the value expected.
+ //
+
+ } else {
+
+ if ( !IsSerialNumberEqual(
+ ChangeLogDesc,
+ ChangeLogEntry,
+ &NextSerialNumber[ChangeLogEntry->DBIndex] )){
+
+ //
+ // For the redo log, the serial numbers merely need to be ascending.
+ //
+
+ if ( !ChangeLogDesc->RedoLog ||
+ ChangeLogEntry->SerialNumber.QuadPart <
+ NextSerialNumber[ChangeLogEntry->DBIndex].QuadPart ){
+
+
+ NlPrint((NL_CRITICAL,
+ "ValidateThisEntry: %lx %lx: Serial number is bad. s.b. %lx %lx\n",
+ ChangeLogEntry->SerialNumber.HighPart,
+ ChangeLogEntry->SerialNumber.LowPart,
+ NextSerialNumber[ChangeLogEntry->DBIndex].HighPart,
+ NextSerialNumber[ChangeLogEntry->DBIndex].LowPart ));
+ return FALSE;
+ }
+ }
+ }
+
+ //
+ // Increment next expected serial number
+ //
+
+ NextSerialNumber[ChangeLogEntry->DBIndex].QuadPart =
+ ChangeLogEntry->SerialNumber.QuadPart + 1;
+
+
+ //
+ // The current entry specifies the highest serial number for its
+ // database.
+ //
+
+ if ( InitialCall ) {
+ ChangeLogDesc->SerialNumber[ChangeLogEntry->DBIndex] =
+ ChangeLogEntry->SerialNumber;
+ ChangeLogDesc->EntryCount[ChangeLogEntry->DBIndex] ++;
+ }
+
+ }
+
+
+ return TRUE;
+}
+
+
+BOOL
+ValidateBlock(
+ IN OUT PCHANGELOG_DESCRIPTOR ChangeLogDesc,
+ IN PCHANGELOG_BLOCK_HEADER Block,
+ IN OUT LARGE_INTEGER *NextSerialNumber,
+ IN BOOLEAN InitialCall
+ )
+/*++
+
+Routine Description:
+
+ Validate a changelog block.
+
+ NOTE: This function must be called with the change log locked.
+
+Arguments:
+
+ ChangeLogDesc -- Description of the Changelog buffer to validate.
+
+ Block: pointer to the change log block to validate
+
+ NextSerialNumber: pointer to an array of serial numbers.
+ (NULL if serial numbers aren't to be validated.)
+
+ InitializeCall: TRUE iff SerialNumber array should be initialized.
+
+Return Value:
+
+ TRUE: if the given entry is a valid next entry.
+
+ FALSE: otherwise.
+
+--*/
+{
+ //
+ // Ensure Block size is properly aligned.
+ //
+
+ if ( Block->BlockSize != ROUND_UP_COUNT(Block->BlockSize, ALIGN_WORST) ) {
+ NlPrint((NL_CRITICAL,
+ "ValidateBlock: Block size alignment is bad.\n" ));
+ return FALSE;
+ }
+
+
+ //
+ // Ensure the block is contained in the cache.
+ //
+
+ if ( Block->BlockSize > ChangeLogDesc->BufferSize ||
+ ((LPBYTE)Block + Block->BlockSize) > ChangeLogDesc->BufferEnd ) {
+ NlPrint((NL_CRITICAL,
+ "ValidateBlock: Block extends beyond end of buffer.\n" ));
+ return FALSE;
+
+ }
+
+
+ //
+ // Do Version 3 specific things
+ //
+
+ if ( ChangeLogDesc->Version3 ) {
+
+ //
+ // Ensure the block is big enough.
+ //
+
+ if ( Block->BlockSize < sizeof(CHANGELOG_BLOCK_HEADER) ) {
+ NlPrint((NL_CRITICAL,
+ "ValidateBlock: Block size is too small: %ld\n",
+ Block->BlockSize ));
+ return FALSE;
+ }
+
+
+ //
+ // Do version 4 specific validation
+ //
+
+ } else {
+
+ //
+ // Ensure the block is big enough.
+ //
+
+ if ( Block->BlockSize <
+ sizeof(CHANGELOG_BLOCK_HEADER) +
+ sizeof(CHANGELOG_BLOCK_TRAILER) ) {
+
+ NlPrint((NL_CRITICAL,
+ "ValidateBlock: Block size is too small: %ld\n",
+ Block->BlockSize ));
+ return FALSE;
+ }
+
+ //
+ // Ensure trailer and header match
+ //
+
+ if ( ChangeLogBlockTrailer(Block)->BlockSize != Block->BlockSize ) {
+ NlPrint((NL_CRITICAL,
+ "ValidateBlock: Header/Trailer block size mismatch: %ld %ld (Trailer fixed).\n",
+ Block->BlockSize,
+ ChangeLogBlockTrailer(Block)->BlockSize ));
+ ChangeLogBlockTrailer(Block)->BlockSize = Block->BlockSize;
+ }
+
+
+ }
+
+ //
+ // Free blocks have no other checking to do
+ //
+ switch ( Block->BlockState ) {
+ case BlockFree:
+
+ break;
+
+ //
+ // Used blocks have more checking to do.
+ //
+
+ case BlockUsed:
+
+ if ( !ValidateThisEntry( ChangeLogDesc,
+ (PCHANGELOG_ENTRY)(Block+1),
+ NextSerialNumber,
+ InitialCall )) {
+ return FALSE;
+ }
+ break;
+
+
+ //
+ // The hole is allowed only at the end of the buffer.
+ //
+
+ case BlockHole:
+ if ( (LPBYTE)Block + Block->BlockSize != ChangeLogDesc->BufferEnd ) {
+ NlPrint((NL_CRITICAL,
+ "ValidateBlock: Hole block in middle of buffer (buffer truncated).\n" ));
+ Block->BlockSize = ChangeLogDesc->BufferEnd - (LPBYTE)Block;
+ }
+ break;
+
+ default:
+ NlPrint((NL_CRITICAL,
+ "ValidateBlock: Invalid block type %ld.\n",
+ Block->BlockState ));
+ return FALSE;
+ }
+
+
+ return TRUE;
+}
+
+
+BOOL
+ValidateList(
+ IN OUT PCHANGELOG_DESCRIPTOR ChangeLogDesc,
+ IN BOOLEAN InitialCall
+ )
+/*++
+
+Routine Description:
+
+ Determine the given header is a valid header. It is done by
+ traversing the circular buffer starting from the given header and
+ validate each entry.
+
+ NOTE: This function must be called with the change log locked.
+
+Arguments:
+
+ ChangeLogDesc -- Description of the Changelog buffer to validate.
+
+ InitialCall: TRUE iff SerialNumber Array and EntryCount should
+ be initialized.
+
+Return Value:
+
+ TRUE: if the given header is valid.
+
+ FALSE: otherwise
+
+
+--*/
+{
+
+ LARGE_INTEGER NextSerialNumber[NUM_DBS];
+ PCHANGELOG_BLOCK_HEADER ChangeLogBlock;
+ DWORD j;
+
+ //
+ // setup a NextSerialNumber array first.
+ //
+
+ for( j = 0; j < NUM_DBS; j++ ) {
+
+ NextSerialNumber[j].QuadPart = 0;
+
+ if ( InitialCall ) {
+ ChangeLogDesc->SerialNumber[j].QuadPart = 0;
+ }
+ }
+
+ //
+ // The cache is valid if it is empty.
+ //
+
+ if ( ChangeLogIsEmpty(ChangeLogDesc) ) {
+ return TRUE;
+ }
+
+ //
+ // Validate each block
+ //
+
+ for ( ChangeLogBlock = ChangeLogDesc->Head;
+ ;
+ ChangeLogBlock = NlMoveToNextChangeLogBlock( ChangeLogDesc, ChangeLogBlock) ) {
+
+ //
+ // Validate the block.
+ //
+
+ if( !ValidateBlock( ChangeLogDesc,
+ ChangeLogBlock,
+ NextSerialNumber,
+ InitialCall) ) {
+ return FALSE;
+ }
+
+ //
+ // Stop when we get to the end.
+ //
+ if ( ChangeLogBlock->BlockState == BlockFree ) {
+ break;
+ }
+
+ }
+
+ return TRUE;
+
+}
+
+
+BOOL
+InitChangeLogHeadAndTail(
+ IN OUT PCHANGELOG_DESCRIPTOR ChangeLogDesc,
+ IN BOOLEAN NewChangeLog
+ )
+
+/*++
+
+Routine Description:
+
+ This function initializes the global head and tail pointers of change
+ log block list. The change log cache is made up of variable length
+ blocks, each block has a header containing the length of the block
+ and the block state ( BlockFree, BlockUsed and BlockHole ). The
+ last block in the change log block list is always the free block,
+ all other blocks in the cache are used blocks except a block at the
+ end of the cache may be a unused block known as 'hole' block. So
+ the head of the change log block list is the block that is just next
+ to the free block and the tail is the free block.
+
+ NOTE: This function must be called with the change log locked.
+
+Arguments:
+
+ ChangeLogDesc -- Description of the Changelog buffer to analyze.
+ On entry, Buffer and BufferSize describe the allocated block containing
+ the change log read from disk.
+ On TRUE return, all the fields are filled in.
+
+ NewChangeLog -- True if no entries are in the change log
+
+Return Value:
+
+ TRUE: if valid head and tail are successfully initialized.
+
+ FALSE: if valid head and tail can't be determined. This may be due
+ to the corrupted change log file.
+
+--*/
+{
+ PCHANGELOG_BLOCK_HEADER Block;
+ PCHANGELOG_BLOCK_HEADER FreeBlock;
+ DWORD i;
+
+ ChangeLogDesc->BufferEnd =
+ ChangeLogDesc->Buffer + ChangeLogDesc->BufferSize;
+
+ //
+ // Compute the address of the first physical cache entry.
+ //
+ ChangeLogDesc->FirstBlock = (PCHANGELOG_BLOCK_HEADER)
+ (ChangeLogDesc->Buffer +
+ sizeof(CHANGELOG_SIG));
+
+ ChangeLogDesc->FirstBlock = (PCHANGELOG_BLOCK_HEADER)
+ ROUND_UP_POINTER ( ChangeLogDesc->FirstBlock, ALIGN_WORST );
+
+ //
+ // Clear the count of entries in the change log and the serial numbers
+ // (We'll compute them later when we call ValidateList().)
+
+ for( i = 0; i < NUM_DBS; i++ ) {
+ ChangeLogDesc->EntryCount[i] = 0;
+ ChangeLogDesc->SerialNumber[i].QuadPart = 0;
+ }
+
+
+ //
+ // If this is a new change log,
+ // Initialize the Change Log Cache to zero.
+ //
+
+ Block = ChangeLogDesc->FirstBlock;
+
+ if ( NewChangeLog ) {
+
+ RtlZeroMemory(ChangeLogDesc->Buffer, ChangeLogDesc->BufferSize);
+ (VOID) lstrcpyA( (PCHAR)ChangeLogDesc->Buffer, CHANGELOG_SIG);
+
+ Block->BlockState = BlockFree;
+
+ Block->BlockSize =
+ (ChangeLogDesc->BufferEnd - (LPBYTE)ChangeLogDesc->FirstBlock);
+ ChangeLogBlockTrailer(Block)->BlockSize = Block->BlockSize;
+
+ ChangeLogDesc->Version3 = FALSE;
+ ChangeLogDesc->Head = ChangeLogDesc->Tail = ChangeLogDesc->FirstBlock;
+ return TRUE;
+ }
+
+ //
+ // If no entries have been written to the changelog,
+ // simply initialize the head and tail to the block start.
+ //
+
+ if ( ChangeLogIsEmpty( ChangeLogDesc ) ) {
+
+ ChangeLogDesc->Head = ChangeLogDesc->Tail = ChangeLogDesc->FirstBlock;
+
+ NlPrint((NL_CHANGELOG,
+ "InitChangeLogHeadAndTail: Change log is empty.\n" ));
+ return TRUE;
+ }
+
+ //
+ // Loop through the cache looking for a free block.
+ //
+
+ FreeBlock = NULL;
+
+ do {
+
+ //
+ // Validate the block's integrity.
+ //
+
+ if ( !ValidateBlock( ChangeLogDesc, Block, NULL, FALSE )) {
+ return FALSE;
+ }
+
+ //
+ // Just remember where the free block is.
+ //
+
+ if ( Block->BlockState == BlockFree ) {
+
+ if ( FreeBlock != NULL ) {
+ NlPrint((NL_CRITICAL,
+ "InitChangeLogHeadAndTail: Multiple free blocks found.\n" ));
+ return FALSE;
+ }
+
+ FreeBlock = Block;
+ }
+
+ //
+ // Move to next block
+ //
+
+ Block = (PCHANGELOG_BLOCK_HEADER) ((LPBYTE)Block + Block->BlockSize);
+
+ } while ( (LPBYTE)Block < ChangeLogDesc->BufferEnd );
+
+ //
+ // If we didn't find a free block,
+ // the changelog is corrupt.
+ //
+
+ if ( FreeBlock == NULL ) {
+ NlPrint((NL_CRITICAL,
+ "InitChangeLogHeadAndTail: No Free block anywhere in buffer.\n" ));
+ return FALSE;
+ }
+
+ //
+ // We found the free block.
+ // (The tail pointer always points to the free block.)
+ //
+
+ ChangeLogDesc->Tail = FreeBlock;
+
+ //
+ // If free block is the last block in the change log block
+ // list, the head of the list is the first block in
+ // the list.
+ //
+ if( ((LPBYTE)FreeBlock + FreeBlock->BlockSize) >=
+ ChangeLogDesc->BufferEnd ) {
+
+ ChangeLogDesc->Head = ChangeLogDesc->FirstBlock;
+
+ //
+ //
+ // Otherwise, the head of the list is immediately after the tail.
+ //
+
+ } else {
+
+ //
+ // A redo log needs the free block at the end.
+ //
+ if ( ChangeLogDesc->RedoLog ) {
+ NlPrint((NL_CRITICAL,
+ "InitChangeLogHeadAndTail: Re-do log has Free block in middle of buffer.\n" ));
+ return FALSE;
+ }
+
+ ChangeLogDesc->Head = (PCHANGELOG_BLOCK_HEADER)
+ ((LPBYTE)FreeBlock + FreeBlock->BlockSize);
+ }
+
+
+ //
+ // Validate the list before returning from here.
+ //
+
+ if ( !ValidateList( ChangeLogDesc, TRUE) ) {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+NTSTATUS
+NlResetChangeLog(
+ IN PCHANGELOG_DESCRIPTOR ChangeLogDesc,
+ IN DWORD NewChangeLogSize
+ )
+/*++
+
+Routine Description:
+
+ This function resets the change log cache and change log file. This
+ function is called from InitChangeLog() function to afresh the
+ change log. This function may also be called from
+ I_NetNotifyDelta() function when the serial number of the new entry
+ is out of order.
+
+ NOTE: This function must be called with the change log locked.
+
+Arguments:
+
+ ChangeLogDesc -- Description of the Changelog buffer being used
+
+ NewChangeLogSize -- Size (in bytes) of the new change log.
+
+Return Value:
+
+ NT Status code
+
+--*/
+{
+ NTSTATUS Status;
+
+ //
+ // Start with a clean slate.
+ //
+
+ NlCloseChangeLogFile( ChangeLogDesc );
+
+ //
+ // Allocate a buffer.
+ //
+
+ ChangeLogDesc->BufferSize = NewChangeLogSize;
+
+ ChangeLogDesc->Buffer = NetpMemoryAllocate(ChangeLogDesc->BufferSize );
+
+ if ( ChangeLogDesc->Buffer == NULL ) {
+ return STATUS_NO_MEMORY;
+ }
+
+
+ //
+ // Initialize the Change Log Cache to zero.
+ //
+
+ (VOID) InitChangeLogHeadAndTail( ChangeLogDesc, TRUE );
+
+ //
+ // Write the cache to the file.
+ //
+
+ Status = NlWriteChangeLogBytes( ChangeLogDesc,
+ ChangeLogDesc->Buffer,
+ ChangeLogDesc->BufferSize,
+ TRUE ); // Flush the bytes to disk
+
+ return Status;
+}
+
+#if DBG
+
+VOID
+PrintChangeLogEntry(
+ PCHANGELOG_ENTRY ChangeLogEntry
+ )
+/*++
+
+Routine Description:
+
+ This routine print the content of the given changelog entry.
+
+Arguments:
+
+ ChangeLogEntry -- pointer to the change log entry to print
+
+Return Value:
+
+ none.
+
+--*/
+{
+ LPSTR DeltaName;
+
+ switch ( ChangeLogEntry->DeltaType ) {
+ case AddOrChangeDomain:
+ DeltaName = "AddOrChangeDomain";
+ break;
+ case AddOrChangeGroup:
+ DeltaName = "AddOrChangeGroup";
+ break;
+ case DeleteGroupByName:
+ case DeleteGroup:
+ DeltaName = "DeleteGroup";
+ break;
+ case RenameGroup:
+ DeltaName = "RenameGroup";
+ break;
+ case AddOrChangeUser:
+ DeltaName = "AddOrChangeUser";
+ break;
+ case DeleteUserByName:
+ case DeleteUser:
+ DeltaName = "DeleteUser";
+ break;
+ case RenameUser:
+ DeltaName = "RenameUser";
+ break;
+ case ChangeGroupMembership:
+ DeltaName = "ChangeGroupMembership";
+ break;
+ case AddOrChangeAlias:
+ DeltaName = "AddOrChangeAlias";
+ break;
+ case DeleteAlias:
+ DeltaName = "DeleteAlias";
+ break;
+ case RenameAlias:
+ DeltaName = "RenameAlias";
+ break;
+ case ChangeAliasMembership:
+ DeltaName = "ChangeAliasMembership";
+ break;
+ case AddOrChangeLsaPolicy:
+ DeltaName = "AddOrChangeLsaPolicy";
+ break;
+ case AddOrChangeLsaTDomain:
+ DeltaName = "AddOrChangeLsaTDomain";
+ break;
+ case DeleteLsaTDomain:
+ DeltaName = "DeleteLsaTDomain";
+ break;
+ case AddOrChangeLsaAccount:
+ DeltaName = "AddOrChangeLsaAccount";
+ break;
+ case DeleteLsaAccount:
+ DeltaName = "DeleteLsaAccount";
+ break;
+ case AddOrChangeLsaSecret:
+ DeltaName = "AddOrChangeLsaSecret";
+ break;
+ case DeleteLsaSecret:
+ DeltaName = "DeleteLsaSecret";
+ break;
+ case SerialNumberSkip:
+ DeltaName = "SerialNumberSkip";
+ break;
+ case DummyChangeLogEntry:
+ DeltaName = "DummyChangeLogEntry";
+ break;
+
+ default:
+ DeltaName ="(Unknown)";
+ break;
+ }
+
+ NlPrint((NL_CHANGELOG,
+ "DeltaType %s (%ld) SerialNumber: %lx %lx",
+ DeltaName,
+ ChangeLogEntry->DeltaType,
+ ChangeLogEntry->SerialNumber.HighPart,
+ ChangeLogEntry->SerialNumber.LowPart ));
+
+ if ( ChangeLogEntry->ObjectRid != 0 ) {
+ NlPrint((NL_CHANGELOG," Rid: 0x%lx", ChangeLogEntry->ObjectRid ));
+ }
+ if ( ChangeLogEntry->Flags & CHANGELOG_REPLICATE_IMMEDIATELY ) {
+ NlPrint((NL_CHANGELOG," Immediately" ));
+ }
+ if ( ChangeLogEntry->Flags & CHANGELOG_PDC_PROMOTION ) {
+ NlPrint((NL_CHANGELOG," Promotion" ));
+ }
+ if ( ChangeLogEntry->Flags & CHANGELOG_PASSWORD_CHANGE ) {
+ NlPrint((NL_CHANGELOG," PasswordChanged" ));
+ }
+ if ( ChangeLogEntry->Flags & CHANGELOG_DOMAINUSERS_CHANGED ) {
+ NlPrint((NL_CHANGELOG," DomainUsersChanged" ));
+ }
+
+
+ if( ChangeLogEntry->Flags & CHANGELOG_NAME_SPECIFIED ) {
+ NlPrint(( NL_CHANGELOG, " Name: '" FORMAT_LPWSTR "'",
+ (LPWSTR)((PBYTE)(ChangeLogEntry)+ sizeof(CHANGELOG_ENTRY))));
+ }
+
+ if( ChangeLogEntry->Flags & CHANGELOG_SID_SPECIFIED ) {
+ NlPrint((NL_CHANGELOG," Sid: "));
+ NlpDumpSid( NL_CHANGELOG,
+ (PSID)((PBYTE)(ChangeLogEntry)+ sizeof(CHANGELOG_ENTRY)) );
+ } else {
+ NlPrint((NL_CHANGELOG,"\n" ));
+ }
+}
+#endif // DBG
+
+
+NTSTATUS
+NlWriteChangeLogEntry(
+ IN PCHANGELOG_DESCRIPTOR ChangeLogDesc,
+ IN PCHANGELOG_ENTRY ChangeLogEntry,
+ IN PSID ObjectSid,
+ IN PUNICODE_STRING ObjectName,
+ IN BOOLEAN FlushIt
+ )
+/*++
+
+Routine Description:
+
+ This is the actual worker for the I_NetNotifyDelta(). This function
+ acquires the sufficient size memory block from the change log
+ buffer, writes the fixed and variable portions of the change log
+ delta in change log buffer and also writes the delta into change log
+ file.
+
+Arguments:
+
+ ChangeLogDesc -- Description of the Changelog buffer being used
+
+ ChangeLogEntry - pointer to the fixed portion of the change log.
+
+ ObjectSid - pointer to the variable field SID.
+
+ ObjectName - pointer to the variable field Name.
+
+ FlushIt - True if the written bytes are to be flushed to disk
+
+Return Value:
+
+ STATUS_SUCCESS - The Service completed successfully.
+
+--*/
+
+{
+ NTSTATUS Status;
+ DWORD LogSize;
+ PCHANGELOG_BLOCK_HEADER LogBlock;
+ PCHANGELOG_BLOCK_HEADER FreeBlock;
+ LPBYTE AllocatedChangeLogEntry;
+
+ //
+ // Make sure that the change log cache is available.
+ //
+
+ if ( ChangeLogDesc->Buffer == NULL ) {
+ WCHAR ChangeLogFile[PATHLEN+1];
+
+ if ( !ChangeLogDesc->RedoLog ) {
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ //
+ // Read in the existing changelog file.
+ //
+
+ wcscpy( ChangeLogFile, NlGlobalChangeLogFilePrefix );
+ wcscat( ChangeLogFile, REDO_FILE_POSTFIX );
+
+ Status = NlOpenChangeLogFile( ChangeLogFile, ChangeLogDesc, FALSE );
+
+ if ( !NT_SUCCESS(Status) ) {
+
+ Status = NlResetChangeLog( ChangeLogDesc, REDO_LOG_INITIAL_SIZE );
+
+ if ( !NT_SUCCESS(Status) ) {
+ return Status;
+ }
+ }
+ }
+
+ //
+ // Fill in the serial number for redo log entries
+ //
+
+ if ( ChangeLogDesc->RedoLog ) {
+ ChangeLogEntry->SerialNumber.QuadPart =
+ ChangeLogDesc->SerialNumber[ChangeLogEntry->DBIndex].QuadPart + 1;
+ }
+
+
+ //
+ // Determine the size of this change log entry.
+ //
+
+ LogSize = sizeof(CHANGELOG_ENTRY);
+
+ //
+ // Ensure we've got the right data for those deltas we care about
+ //
+
+ switch (ChangeLogEntry->DeltaType) {
+ case AddOrChangeLsaTDomain:
+ case DeleteLsaTDomain:
+ case AddOrChangeLsaAccount:
+ case DeleteLsaAccount:
+ NlAssert( ObjectSid != NULL );
+ if( ObjectSid != NULL ) {
+ ChangeLogEntry->Flags |= CHANGELOG_SID_SPECIFIED;
+ LogSize += RtlLengthSid( ObjectSid );
+ }
+ break;
+
+ case AddOrChangeLsaSecret:
+ case DeleteLsaSecret:
+ case DeleteGroup:
+ case RenameGroup:
+ case DeleteUser:
+ case RenameUser:
+
+ NlAssert( ObjectName != NULL && ObjectName->Buffer != NULL && ObjectName->Length != 0 );
+ if( ObjectName != NULL && ObjectName->Buffer != NULL && ObjectName->Length != 0 ) {
+ ChangeLogEntry->Flags |= CHANGELOG_NAME_SPECIFIED;
+ LogSize += ObjectName->Length + sizeof(WCHAR);
+ }
+ break;
+
+ //
+ // For all other delta types, save the data if it's there.
+ //
+ default:
+
+ if( ObjectName != NULL && ObjectName->Buffer != NULL && ObjectName->Length != 0 ) {
+ ChangeLogEntry->Flags |= CHANGELOG_NAME_SPECIFIED;
+ LogSize += ObjectName->Length + sizeof(WCHAR);
+ } else if( ObjectSid != NULL ) {
+ ChangeLogEntry->Flags |= CHANGELOG_SID_SPECIFIED;
+ LogSize += RtlLengthSid( ObjectSid );
+ }
+ break;
+
+ }
+
+
+
+ //
+ // Serialize access to the change log
+ //
+
+ LOCK_CHANGELOG();
+
+ //
+ // Validate the serial number order of this new entry
+ //
+ // If we're out of sync with the caller,
+ // clear the change log and start all over again.
+ //
+ // The global serial number array entry for this database must either
+ // be zero (indicating no entries for this database) or one less than
+ // the new serial number being added.
+ //
+
+ if ( ChangeLogDesc->SerialNumber[ChangeLogEntry->DBIndex].QuadPart != 0 ) {
+ LARGE_INTEGER ExpectedSerialNumber;
+ LARGE_INTEGER OldSerialNumber;
+
+ ExpectedSerialNumber.QuadPart =
+ ChangeLogDesc->SerialNumber[ChangeLogEntry->DBIndex].QuadPart + 1;
+
+ //
+ // If the serial number jumped by the promotion increment,
+ // set the flag in the change log entry indicating this is
+ // a promotion to PDC.
+ //
+
+ if ( ChangeLogEntry->SerialNumber.QuadPart ==
+ ExpectedSerialNumber.QuadPart +
+ NlGlobalChangeLogPromotionIncrement.QuadPart ) {
+
+ ChangeLogEntry->Flags |= CHANGELOG_PDC_PROMOTION;
+ }
+
+ if ( !IsSerialNumberEqual( ChangeLogDesc,
+ ChangeLogEntry,
+ &ExpectedSerialNumber )) {
+
+ NlPrint((NL_CRITICAL,
+ "NlWriteChangeLogEntry: Serial numbers not contigous %lx %lx and %lx %lx\n",
+ ChangeLogEntry->SerialNumber.HighPart,
+ ChangeLogEntry->SerialNumber.LowPart,
+ ExpectedSerialNumber.HighPart,
+ ExpectedSerialNumber.LowPart ));
+
+ //
+ // write event log.
+ //
+
+ NlpWriteEventlog (
+ NELOG_NetlogonChangeLogCorrupt,
+ EVENTLOG_ERROR_TYPE,
+ (LPBYTE)&(ChangeLogEntry->DBIndex),
+ sizeof(ChangeLogEntry->DBIndex),
+ NULL,
+ 0 );
+
+
+ //
+ // If the change log is merely newer than the SAM database,
+ // we truncate entries newer than what exists in SAM.
+ //
+
+ OldSerialNumber.QuadPart = ChangeLogEntry->SerialNumber.QuadPart - 1;
+
+ (VOID) NlFixChangeLog( ChangeLogDesc, ChangeLogEntry->DBIndex, OldSerialNumber, FALSE );
+ }
+
+ //
+ // If this is the first entry written to the change log for this database,
+ // mark it as a promotion.
+ //
+
+ } else {
+ //
+ // Only mark entries that might possibly be a promotion.
+ //
+ switch (ChangeLogEntry->DeltaType) {
+ case AddOrChangeDomain:
+ case AddOrChangeLsaPolicy:
+ ChangeLogEntry->Flags |= CHANGELOG_PDC_PROMOTION;
+ break;
+ }
+ }
+
+
+ //
+ // Validate the list before changing anything
+ //
+
+ NlAssert( ValidateList( ChangeLogDesc, FALSE) );
+
+
+ //
+ // copy fixed portion
+ //
+
+ Status = NlAllocChangeLogBlock( ChangeLogDesc, LogSize, &LogBlock );
+ if ( !NT_SUCCESS(Status) ) {
+ goto Cleanup;
+ }
+ AllocatedChangeLogEntry = ((LPBYTE)LogBlock) + sizeof(CHANGELOG_BLOCK_HEADER);
+ RtlCopyMemory( AllocatedChangeLogEntry, ChangeLogEntry, sizeof(CHANGELOG_ENTRY) );
+
+
+ //
+ // copy variable fields
+ //
+
+ if( ChangeLogEntry->Flags & CHANGELOG_SID_SPECIFIED ) {
+
+ RtlCopyMemory( AllocatedChangeLogEntry + sizeof(CHANGELOG_ENTRY),
+ ObjectSid,
+ RtlLengthSid( ObjectSid ) );
+ } else if( ChangeLogEntry->Flags & CHANGELOG_NAME_SPECIFIED ) {
+
+ RtlCopyMemory( AllocatedChangeLogEntry + sizeof(CHANGELOG_ENTRY),
+ ObjectName->Buffer,
+ ObjectName->Length );
+
+ //
+ // terminate unicode string
+ //
+
+ *(WCHAR *)(AllocatedChangeLogEntry + sizeof(CHANGELOG_ENTRY) +
+ ObjectName->Length) = 0;
+ }
+
+ //
+ // Be verbose
+ //
+
+#if DBG
+ PrintChangeLogEntry( (PCHANGELOG_ENTRY)AllocatedChangeLogEntry );
+#endif // DBG
+
+
+
+ //
+ // Write the cache entry to the file.
+ //
+ // Actually, write this entry plus the header and trailer of the free
+ // block that follows. If the free block is huge, write the free
+ // block trailer separately.
+ //
+
+ FreeBlock =
+ (PCHANGELOG_BLOCK_HEADER)((LPBYTE)LogBlock + LogBlock->BlockSize);
+
+ if ( FreeBlock->BlockSize >= 4096 ) {
+
+ Status = NlWriteChangeLogBytes(
+ ChangeLogDesc,
+ (LPBYTE)LogBlock,
+ LogBlock->BlockSize + sizeof(CHANGELOG_BLOCK_HEADER),
+ FlushIt );
+
+ if ( NT_SUCCESS(Status) ) {
+ Status = NlWriteChangeLogBytes(
+ ChangeLogDesc,
+ (LPBYTE)ChangeLogBlockTrailer(FreeBlock),
+ sizeof(CHANGELOG_BLOCK_TRAILER),
+ FlushIt );
+ }
+
+ } else {
+
+ Status = NlWriteChangeLogBytes(
+ ChangeLogDesc,
+ (LPBYTE)LogBlock,
+ LogBlock->BlockSize + FreeBlock->BlockSize,
+ FlushIt );
+ }
+
+
+ //
+ // Done.
+ //
+
+ ChangeLogDesc->SerialNumber[ChangeLogEntry->DBIndex] = ChangeLogEntry->SerialNumber;
+ ChangeLogDesc->EntryCount[ChangeLogEntry->DBIndex] ++;
+
+ //
+ // Validate the list before returning from here.
+ //
+Cleanup:
+
+ NlAssert( ValidateList( ChangeLogDesc, FALSE) );
+
+
+ UNLOCK_CHANGELOG();
+ return Status;
+}
+
+NTSTATUS
+NlWriteDeltaToChangeLog(
+ IN PCHANGELOG_DESCRIPTOR ChangeLogDesc,
+ IN PNETLOGON_DELTA_ENUM Delta,
+ IN ULONG DBIndex,
+ IN OUT PLARGE_INTEGER SerialNumber
+ )
+/*++
+
+Routine Description:
+
+ This routine convert the Delta returned from the PDC and converts it into
+ a change log entry on the BDC. The change log entry is written to the
+ change log on the BDC.
+
+ The change log is silently maintained but isn't used until the BDC is
+ promoted to a PDC.
+
+ The caller is responsible for flushing the change log to disk.
+
+Arguments:
+
+ ChangeLogDesc -- Description of the Changelog buffer being used
+
+ Delta - Delta returned from the PDC
+
+ DBIndex - Index to the database being modified.
+
+ SerialNumber - On input, this is the Serial Number of this delta.
+ On output, this is the Serial Number of the next delta.
+
+ This parameter isn't needed when writing the redo log.
+
+Return Value:
+
+ Status of the operation.
+
+--*/
+{
+ NTSTATUS Status;
+ NTSTATUS CumulativeStatus = STATUS_SUCCESS;
+
+ CHANGELOG_ENTRY Log;
+ PSID ObjectSid = NULL;
+ UNICODE_STRING ObjectNameString;
+ PUNICODE_STRING ObjectName = NULL;
+ LPWSTR DeltaName = NULL;
+ LARGE_INTEGER SerialNumberCount;
+ LARGE_INTEGER SerialNumberOfThisDelta;
+
+
+ static BOOLEAN SkipNextDelta = FALSE;
+
+ //
+ // Save the serial number of this delta.
+ // Compute the default value of the serial number of the next delta.
+ //
+ // Don't touch the serial number for the redo log.
+ //
+
+ if ( !ChangeLogDesc->RedoLog ) {
+ Log.SerialNumber = *SerialNumber;
+ SerialNumber->QuadPart += 1;
+ }
+
+
+ //
+ // If the previous delta was a SerialNumberSkip delta that decrement the
+ // Serial Number by one,
+ // and the current delta has the same serial number as the last one
+ // written to the change log,
+ // just ignore this delta.
+ //
+ // This handles the case where the PDC passed us two deltas for a single
+ // serial number. We log the "first" one passed to us.
+ //
+ // The PDC does this in the case it has to ship us two deltas to represent
+ // a single change (e.g., the group and group membership on a group rename).
+ //
+
+ if ( !ChangeLogDesc->RedoLog && SkipNextDelta ) {
+ SkipNextDelta = FALSE;
+ LOCK_CHANGELOG();
+ if ( ChangeLogDesc->SerialNumber[DBIndex].QuadPart ==
+ SerialNumber->QuadPart - 1 ) {
+
+
+ NlPrint((NL_CHANGELOG,
+ "NlWriteDeltaToChangeLog: Don't log this delta %lx %lx\n",
+ ChangeLogDesc->SerialNumber[DBIndex].HighPart,
+ ChangeLogDesc->SerialNumber[DBIndex].LowPart ));
+ UNLOCK_CHANGELOG();
+
+ return STATUS_SUCCESS;
+ }
+ UNLOCK_CHANGELOG();
+ }
+
+
+ //
+ // Build the change log entry.
+ //
+
+ Log.DeltaType = (UCHAR) Delta->DeltaType;
+
+ Log.DBIndex = (UCHAR) DBIndex;
+
+ //
+ // Clear the Rid for now. We'll set it to the right value later.
+ Log.ObjectRid = 0;
+
+ // We lose the REPLICATE_IMMEDIATELY and PASSWORD_CHANGE flags
+ // but they are for informational purposes only anyway.
+ Log.Flags = 0;
+
+
+
+ //
+ // Handle each delta type differently
+ //
+
+ switch ( Delta->DeltaType ) {
+ case DeleteGroupByName:
+
+ Log.DeltaType = DeleteGroup;
+ DeltaName = Delta->DeltaUnion.DeltaDeleteGroup->AccountName;
+ Log.ObjectRid = Delta->DeltaID.Rid;
+ break;
+
+ case DeleteUserByName:
+
+ Log.DeltaType = DeleteUser;
+ DeltaName = Delta->DeltaUnion.DeltaDeleteUser->AccountName;
+ Log.ObjectRid = Delta->DeltaID.Rid;
+ break;
+
+ case RenameGroup:
+ case RenameUser:
+
+ ObjectName = &Delta->DeltaUnion.DeltaRenameUser->OldName;
+ Log.ObjectRid = Delta->DeltaID.Rid;
+ break;
+
+ case AddOrChangeDomain:
+ case AddOrChangeGroup:
+ case AddOrChangeUser:
+ case ChangeGroupMembership:
+ case AddOrChangeAlias:
+ case DeleteAlias:
+ case RenameAlias:
+ case ChangeAliasMembership:
+
+ Log.ObjectRid = Delta->DeltaID.Rid;
+ break;
+
+ case AddOrChangeLsaTDomain:
+ case DeleteLsaTDomain:
+ case AddOrChangeLsaAccount:
+ case DeleteLsaAccount:
+
+ ObjectSid = (PSID)(Delta->DeltaID.Sid);
+ break;
+
+
+ // There is only one LSA Policy. It need not be further identified.
+ case AddOrChangeLsaPolicy:
+ break;
+
+ case AddOrChangeLsaSecret:
+ case DeleteLsaSecret:
+
+ DeltaName = Delta->DeltaID.Name;
+ break;
+
+ //
+ // The SerialNumberSkip delta tells us the serial number of the 'next'
+ // delta if it isn't the default.
+ //
+ // Notice that the SerialNumber on the change log entry is the serial number
+ // of the first missing delta.
+ //
+ case SerialNumberSkip:
+
+ //
+ // Ignore serial number deltas in the redo log.
+ //
+
+ if ( ChangeLogDesc->RedoLog ) {
+ break;
+ }
+
+ //
+ // Adjust the serial number of the next delta.
+ //
+
+ SerialNumberOfThisDelta = Log.SerialNumber;
+
+ OLD_TO_NEW_LARGE_INTEGER(
+ Delta->DeltaUnion.DeltaSerialNumberSkip->ModifiedCount,
+ *SerialNumber );
+
+ NlPrint((NL_CHANGELOG,
+ "NlWriteDeltaToChangeLog: Serial number skip from %lx %lx to %lx %lx: ",
+ SerialNumberOfThisDelta.HighPart,
+ SerialNumberOfThisDelta.LowPart,
+ SerialNumber->HighPart,
+ SerialNumber->LowPart ));
+
+ //
+ // If the serial number is being set forward,
+ // write several dummy deltas to the change log.
+ //
+
+ if ( SerialNumberOfThisDelta.QuadPart < SerialNumber->QuadPart ) {
+ //
+ // If the serial number of the NextDelta indicates PDC promotion,
+ // adjust what we think the serial number of this delta is.
+ //
+
+ if ( SerialNumberOfThisDelta.QuadPart +
+ NlGlobalChangeLogPromotionIncrement.QuadPart <=
+ SerialNumber->QuadPart ) {
+
+
+ NlPrint((NL_CHANGELOG, "PDC promotion " ));
+ SerialNumberOfThisDelta.QuadPart +=
+ NlGlobalChangeLogPromotionIncrement.QuadPart;
+ }
+
+
+ SerialNumberCount.QuadPart =
+ SerialNumber->QuadPart - SerialNumberOfThisDelta.QuadPart;
+
+ NlPrint(( NL_CHANGELOG,
+ "forward skip of %ld deltas\n", SerialNumberCount ));
+
+ //
+ // If the number of skipped deltas is ridiculously large,
+ // just skip the delta writes and let the next delta write
+ // reset the change log.
+ //
+
+ if ( SerialNumberCount.QuadPart <= 500 ) {
+ DWORD i;
+
+ Log.DeltaType = DummyChangeLogEntry;
+
+ for ( i=0; i<SerialNumberCount.LowPart ; i++ ) {
+ Status = NlWriteChangeLogEntry( ChangeLogDesc, &Log, NULL, NULL, FALSE );
+ if ( CumulativeStatus == STATUS_SUCCESS ) {
+ CumulativeStatus = Status;
+ }
+ Log.SerialNumber.QuadPart++;
+ }
+
+ }
+
+ //
+ // If the serial number isn't being changed,
+ // this is a no-op.
+ //
+
+ } else if ( SerialNumberOfThisDelta.QuadPart < SerialNumber->QuadPart ) {
+
+ /* Do Nothing Here */
+
+ //
+ // If the SerialNumber went backwards,
+ // differentiate between the case of multiple deltas per serial number and
+ // the serial number actually going backwards.
+ //
+ } else {
+
+ //
+ // If the serial number is being set back by one,
+ // and this delta is a duplicate of one we've just received from the PDC,
+ // see comment at the top of this routine,
+ // don't write any deltas at this time.
+
+ LOCK_CHANGELOG();
+ if ( SerialNumberOfThisDelta.QuadPart - 1 == SerialNumber->QuadPart &&
+ NlGlobalChangeLogDesc.SerialNumber[DBIndex].QuadPart == SerialNumber->QuadPart ) {
+
+ NlPrint((NL_CHANGELOG, "Back by one\n" ));
+ SkipNextDelta = TRUE;
+
+ //
+ // Otherwise, the serial number on the PDC is less than the serial number on the BDC.
+ //
+ // Recover by converting the Change Log Entries on this BDC to redo log entries.
+ // We'll reset the serial number on the database to match the PDC.
+ // The redo log will ensure the "extra" changes get undone.
+ //
+
+ } else {
+ NlPrint((NL_CHANGELOG, "backward skip (recovering)\n" ));
+
+ if ( !NlFixChangeLog( ChangeLogDesc, DBIndex, *SerialNumber, TRUE ) ) {
+ CumulativeStatus = STATUS_SYNCHRONIZATION_REQUIRED;
+ }
+
+ //
+ // Set the expected serial number to match the change log.
+ //
+ // Above we deleted all change log entries "greater" than the
+ // reset serial number. Add to that the fact that the PDC
+ // doesn't actually send us the delta.
+ //
+ SerialNumber->QuadPart ++;
+ }
+
+ UNLOCK_CHANGELOG();
+ }
+
+ return CumulativeStatus;
+
+
+ //
+ // We should never get these two delta types from NT3.5 PDC
+ //
+ case DeleteGroup: // Needs name too
+ case DeleteUser: // Needs name too
+ default:
+ NlPrint((NL_CRITICAL, "NlWriteDeltaToChangeLog: invalid delta type %lx\n", Delta->DeltaType ));
+ NlAssert( FALSE );
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ //
+ // If we found a name in the delta,
+ // put it in the changelog entry.
+ //
+
+ if ( DeltaName != NULL ) {
+ RtlInitUnicodeString( &ObjectNameString, DeltaName );
+ ObjectName = &ObjectNameString;
+ }
+
+ Status = NlWriteChangeLogEntry(
+ ChangeLogDesc,
+ &Log,
+ ObjectSid,
+ ObjectName,
+ ChangeLogDesc->RedoLog ); // If redo log, flush it to disk right now
+
+ return Status;
+
+}
+
+
+
+
+NTSTATUS
+NlOpenChangeLogFile(
+ IN LPWSTR ChangeLogFileName,
+ OUT PCHANGELOG_DESCRIPTOR ChangeLogDesc,
+ IN BOOLEAN ReadOnly
+)
+/*++
+
+Routine Description:
+
+ Open the change log file (netlogon.chg) for reading or writing one or
+ more records. Create this file if it does not exist or is out of
+ sync with the SAM database (see note below).
+
+ This file must be opened for R/W (deny-none share mode) at the time
+ the cache is initialized. If the file already exists when NETLOGON
+ service started, its contents will be cached in its entirety
+ provided the last change log record bears the same serial number as
+ the serial number field in SAM database else this file will be
+ removed and a new one created. If the change log file did not exist
+ then it will be created.
+
+ NOTE: This function must be called with the change log locked.
+
+Arguments:
+
+ ChangeLogFileName - Name of the changelog file to open.
+
+ ChangeLogDesc -- On success, returns a description of the Changelog buffer
+ being used
+
+ ReadOnly -- True if the file should be openned read only.
+
+Return Value:
+
+ NT Status code
+
+--*/
+{
+
+ DWORD WinError;
+ DWORD BytesRead;
+ DWORD MinChangeLogSize;
+
+ //
+ // Open change log file if exists
+ //
+
+ ChangeLogDesc->FileHandle = CreateFileW(
+ ChangeLogFileName,
+ ReadOnly ? GENERIC_READ : (GENERIC_READ | GENERIC_WRITE),
+ ReadOnly ? (FILE_SHARE_READ | FILE_SHARE_WRITE) : FILE_SHARE_READ, // allow backups and debugging
+ NULL, // Supply better security ??
+ OPEN_EXISTING, // Only open it if it exists
+ FILE_ATTRIBUTE_NORMAL,
+ NULL ); // No template
+
+ if ( ChangeLogDesc->FileHandle == INVALID_HANDLE_VALUE) {
+ WinError = GetLastError();
+
+ NlPrint(( ChangeLogDesc->RedoLog ? NL_CHANGELOG : NL_CRITICAL,
+ FORMAT_LPWSTR ": Unable to open. %ld\n",
+ ChangeLogFileName,
+ WinError ));
+
+ goto Cleanup;
+ }
+
+ //
+ // Get the size of the file.
+ //
+
+ ChangeLogDesc->BufferSize = GetFileSize( ChangeLogDesc->FileHandle, NULL );
+
+ // ?? consider aligning to ALIGN_WORST
+ if ( ChangeLogDesc->RedoLog ) {
+ MinChangeLogSize = REDO_LOG_INITIAL_SIZE;
+ } else {
+ MinChangeLogSize = MIN_CHANGELOGSIZE;
+ }
+
+ if ( ChangeLogDesc->BufferSize < MinChangeLogSize ||
+ ChangeLogDesc->BufferSize > MAX_CHANGELOGSIZE ) {
+
+ WinError = ERROR_INTERNAL_DB_CORRUPTION;
+
+ NlPrint((NL_CRITICAL, FORMAT_LPWSTR ": Changelog size is invalid. %ld.\n",
+ ChangeLogFileName,
+ ChangeLogDesc->BufferSize ));
+ goto Cleanup;
+ }
+
+ //
+ // Allocate and initialize the change log cache.
+ //
+
+ ChangeLogDesc->Buffer = NetpMemoryAllocate( ChangeLogDesc->BufferSize );
+ if (ChangeLogDesc->Buffer == NULL) {
+ WinError = ERROR_NOT_ENOUGH_MEMORY;
+ goto Cleanup;
+ }
+
+
+ RtlZeroMemory(ChangeLogDesc->Buffer, ChangeLogDesc->BufferSize);
+
+
+ //
+ // Check the signature at the front of the change log.
+ //
+ // It won't be there if we just created the file.
+ //
+
+ if ( !ReadFile( ChangeLogDesc->FileHandle,
+ ChangeLogDesc->Buffer,
+ ChangeLogDesc->BufferSize,
+ &BytesRead,
+ NULL ) ) { // Not Overlapped
+
+ WinError = GetLastError();
+
+ NlPrint(( NL_CRITICAL,
+ FORMAT_LPWSTR ": Unable to read from changelog file. %ld\n",
+ ChangeLogFileName,
+ WinError ));
+
+ goto Cleanup;
+ }
+
+ if ( BytesRead != ChangeLogDesc->BufferSize ) {
+
+ WinError = ERROR_INTERNAL_DB_CORRUPTION;
+
+ NlPrint(( NL_CRITICAL,
+ FORMAT_LPWSTR ": Couldn't read entire file. %ld\n",
+ ChangeLogFileName,
+ WinError ));
+
+
+ goto Cleanup;
+ }
+
+ if ( strncmp((PCHAR)ChangeLogDesc->Buffer,
+ CHANGELOG_SIG, sizeof(CHANGELOG_SIG)) == 0) {
+ ChangeLogDesc->Version3 = FALSE;
+
+ } else if ( strncmp((PCHAR)ChangeLogDesc->Buffer,
+ CHANGELOG_SIG_V3, sizeof(CHANGELOG_SIG_V3)) == 0) {
+ ChangeLogDesc->Version3 = TRUE;
+ } else {
+ WinError = ERROR_INTERNAL_ERROR;
+
+ NlPrint(( NL_CRITICAL,
+ FORMAT_LPWSTR ": Invalid signature. %ld\n",
+ ChangeLogFileName,
+ WinError ));
+
+ goto Cleanup;
+ }
+
+
+ //
+ // Find the Head and Tail pointers of the circular log.
+ //
+
+ if( !InitChangeLogHeadAndTail( ChangeLogDesc, FALSE ) ) {
+ WinError = ERROR_INTERNAL_DB_CORRUPTION;
+
+ NlPrint(( NL_CRITICAL,
+ FORMAT_LPWSTR ": couldn't find head/tail. %ld\n",
+ ChangeLogFileName,
+ WinError ));
+
+ goto Cleanup;
+ }
+
+
+
+ WinError = NO_ERROR;
+
+ //
+ // Free any resources on error.
+ //
+Cleanup:
+
+ if ( WinError != NO_ERROR ) {
+ NlCloseChangeLogFile( ChangeLogDesc );
+ }
+
+ return NetpApiStatusToNtStatus(WinError);
+}
+
+
+
+VOID
+NlCloseChangeLogFile(
+ IN PCHANGELOG_DESCRIPTOR ChangeLogDesc
+)
+/*++
+
+Routine Description:
+
+ This function closes the change log file and frees up the resources
+ consumed by the change log desriptor.
+
+Arguments:
+
+ ChangeLogDesc -- Description of the Changelog buffer being used
+
+Return Value:
+
+ NT Status code
+
+--*/
+{
+
+ LOCK_CHANGELOG();
+
+ //
+ // free up the change log cache.
+ //
+
+ if ( ChangeLogDesc->Buffer != NULL ) {
+ NetpMemoryFree( ChangeLogDesc->Buffer );
+ ChangeLogDesc->Buffer = NULL;
+ }
+
+ ChangeLogDesc->Head = NULL;
+ ChangeLogDesc->Tail = NULL;
+
+ ChangeLogDesc->FirstBlock = NULL;
+ ChangeLogDesc->BufferEnd = NULL;
+
+ ChangeLogDesc->LastDirtyByte = 0;
+ ChangeLogDesc->FirstDirtyByte = 0;
+
+ //
+ // Close the change log file
+ //
+
+ if ( ChangeLogDesc->FileHandle != INVALID_HANDLE_VALUE ) {
+ CloseHandle( ChangeLogDesc->FileHandle );
+ ChangeLogDesc->FileHandle = INVALID_HANDLE_VALUE;
+ }
+
+ UNLOCK_CHANGELOG();
+
+ return;
+}
+
+
+
+NTSTATUS
+NlResizeChangeLogFile(
+ IN OUT PCHANGELOG_DESCRIPTOR ChangeLogDesc,
+ IN DWORD NewChangeLogSize
+)
+/*++
+
+Routine Description:
+
+ The buffer described by ChageLogDesc is converted to
+ the size requested by NewChangeLogSize and is converted from any
+ old format change log to the latest format.
+
+ NOTE: This function must be called with the change log locked.
+
+Arguments:
+
+ ChangeLogDesc -- a description of the Changelog buffer.
+
+ NewChangeLogSize -- Size (in bytes) of the new change log.
+
+Return Value:
+
+ NT Status code
+
+ On error, the ChangeLogDesc will still be intact. Merely the size
+ changes will not have happened
+
+--*/
+{
+ CHANGELOG_DESCRIPTOR OutChangeLogDesc;
+ NTSTATUS Status;
+
+ //
+ // If the current buffer is perfect,
+ // just use it.
+ //
+
+ if ( !ChangeLogDesc->Version3 &&
+ ChangeLogDesc->BufferSize == NewChangeLogSize ) {
+ return STATUS_SUCCESS;
+ }
+
+ //
+ // Initialize the template change log descriptor
+ //
+
+ InitChangeLogDesc( &OutChangeLogDesc );
+ OutChangeLogDesc.RedoLog = ChangeLogDesc->RedoLog;
+
+ //
+ // Close the file so we can resize it.
+ //
+
+ if ( ChangeLogDesc->FileHandle != INVALID_HANDLE_VALUE ) {
+ CloseHandle( ChangeLogDesc->FileHandle );
+ ChangeLogDesc->FileHandle = INVALID_HANDLE_VALUE;
+ }
+
+ //
+ // Start with a newly initialized change log,
+ //
+
+ Status = NlResetChangeLog( &OutChangeLogDesc, NewChangeLogSize );
+
+ if ( !NT_SUCCESS(Status)) {
+ return Status;
+ }
+
+ //
+ // We're done if the old change log is empty.
+ //
+
+ if ( !ChangeLogIsEmpty(ChangeLogDesc) ) {
+
+ //
+ // Loop through the old change log copying it to the new changelog,
+ //
+
+ PCHANGELOG_ENTRY SourceChangeLogEntry = (PCHANGELOG_ENTRY)
+ (ChangeLogDesc->Head + 1);
+
+ do {
+ Status = NlCopyChangeLogEntry( ChangeLogDesc,
+ SourceChangeLogEntry,
+ &OutChangeLogDesc );
+
+ if ( !NT_SUCCESS(Status) ) {
+ NlCloseChangeLogFile( &OutChangeLogDesc );
+ return Status;
+ }
+
+ } while ( (SourceChangeLogEntry =
+ NlMoveToNextChangeLogEntry( ChangeLogDesc, SourceChangeLogEntry )) != NULL );
+
+ //
+ // Flsuh all the changes to the change log file now.
+ //
+
+ Status = NlFlushChangeLog( &OutChangeLogDesc );
+
+ if ( !NT_SUCCESS(Status) ) {
+ NlCloseChangeLogFile( &OutChangeLogDesc );
+ return Status;
+ }
+
+ }
+
+ //
+ // Free the old change log buffer.
+ //
+
+ NlCloseChangeLogFile( ChangeLogDesc );
+
+ //
+ // Copy the new descriptor over the old descriptor
+ //
+
+ *ChangeLogDesc = OutChangeLogDesc;
+
+ return STATUS_SUCCESS;
+}
+
+
+#if DBG
+
+DWORD
+NlBackupChangeLogFile(
+ )
+/*++
+
+Routine Description:
+
+ Backup change log content. Since the cache and the change log file
+ content are identical, write cache content to the backup file.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ STATUS_SUCCESS - The Service completed successfully.
+
+--*/
+{
+ HANDLE BackupChangeLogHandle;
+
+ WCHAR BackupChangelogFile[PATHLEN+1];
+ DWORD WinError;
+
+ if( NlGlobalChangeLogFilePrefix[0] == L'\0' ) {
+
+ return ERROR_FILE_NOT_FOUND;
+ }
+
+ //
+ // make backup file name.
+ //
+
+ wcscpy( BackupChangelogFile, NlGlobalChangeLogFilePrefix );
+ wcscat( BackupChangelogFile, BACKUP_CHANGELOG_FILE_POSTFIX );
+
+
+
+ //
+ // Create change log file. If it exists already then truncate it.
+ //
+ // Note : if a valid change log file exists on the system, then we
+ // would have opened at initialization time.
+ //
+
+ BackupChangeLogHandle = CreateFileW(
+ BackupChangelogFile,
+ GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ, // allow backups and debugging
+ NULL, // Supply better security ??
+ CREATE_ALWAYS, // Overwrites always
+ FILE_ATTRIBUTE_NORMAL,
+ NULL ); // No template
+
+ if (BackupChangeLogHandle == INVALID_HANDLE_VALUE) {
+
+
+ NlPrint((NL_CRITICAL,"Unable to create backup changelog file "
+ "WinError = %ld \n", WinError = GetLastError() ));
+
+ return WinError;
+ }
+
+ //
+ // Write cache in changelog file if the cache is valid.
+ //
+
+ if( NlGlobalChangeLogDesc.Buffer != NULL ) {
+
+ OVERLAPPED Overlapped;
+ DWORD BytesWritten;
+
+ //
+ // Seek to appropriate offset in the file.
+ //
+
+ RtlZeroMemory( &Overlapped, sizeof(Overlapped) );
+
+ LOCK_CHANGELOG();
+
+ if ( !WriteFile( BackupChangeLogHandle,
+ NlGlobalChangeLogDesc.Buffer,
+ NlGlobalChangeLogDesc.BufferSize,
+ &BytesWritten,
+ &Overlapped ) ) {
+
+ UNLOCK_CHANGELOG();
+ NlPrint((NL_CRITICAL, "Write to Backup ChangeLog failed %ld\n",
+ WinError = GetLastError() ));
+
+ goto Cleanup;
+ }
+
+ UNLOCK_CHANGELOG();
+
+ //
+ // Ensure all the bytes made it.
+ //
+
+ if ( BytesWritten != NlGlobalChangeLogDesc.BufferSize ) {
+ NlPrint((NL_CRITICAL,
+ "Write to Backup ChangeLog bad byte count %ld s.b. %ld\n",
+ BytesWritten,
+ NlGlobalChangeLogDesc.BufferSize ));
+
+ goto Cleanup;
+ }
+ }
+
+Cleanup:
+
+ CloseHandle( BackupChangeLogHandle );
+ return ERROR_SUCCESS;
+}
+
+#endif // DBG
+
diff --git a/private/net/svcdlls/logonsrv/server/chutil.h b/private/net/svcdlls/logonsrv/server/chutil.h
new file mode 100644
index 000000000..09e98ee23
--- /dev/null
+++ b/private/net/svcdlls/logonsrv/server/chutil.h
@@ -0,0 +1,495 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ chutil.h
+
+Abstract:
+
+ Definitions of the internals of the changelog.
+
+ Currently only included sparingly.
+
+Author:
+
+ Cliff Van Dyke (cliffv) 07-May-1992
+
+Environment:
+
+ User mode only.
+ Contains NT-specific code.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+ 02-Jan-1992 (madana)
+ added support for builtin/multidomain replication.
+
+--*/
+
+#if ( _MSC_VER >= 800 )
+#pragma warning ( 3 : 4100 ) // enable "Unreferenced formal parameter"
+#pragma warning ( 3 : 4219 ) // enable "trailing ',' used for variable argument list"
+#endif
+
+//
+// chutil.c will #include this file with CHUTIL_ALLOCATE defined.
+// That will cause each of these variables to be allocated.
+//
+#ifdef CHUTIL_ALLOCATE
+#define EXTERN
+#else
+#define EXTERN extern
+#endif
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Structures and variables describing the Change Log.
+//
+/////////////////////////////////////////////////////////////////////////////
+
+//
+// All of the following data is private to changelg.c and nltest1.c
+//
+
+//
+// change log file name
+//
+
+#define CHANGELOG_FILE_PREFIX L"\\NETLOGON"
+
+#define CHANGELOG_FILE_POSTFIX_LENGTH 4 // Length of all the following postfixes
+#define CHANGELOG_FILE_POSTFIX L".CHG"
+#define BACKUP_CHANGELOG_FILE_POSTFIX L".BKP"
+#define REDO_FILE_POSTFIX L".RDO"
+
+//
+// Initial size and size increment of a redo log.
+//
+#define REDO_LOG_INITIAL_SIZE 1024
+#define REDO_LOG_INCREMENT 1024
+
+//
+// Signature at front of changelog file
+//
+
+#define CHANGELOG_SIG_V3 "NT CHANGELOG 3"
+#define CHANGELOG_SIG "NT CHANGELOG 4"
+
+//
+// Change log block state
+//
+
+typedef enum _CHANGELOG_BLOCK_STATE {
+ BlockFree = 1,
+ BlockUsed,
+ BlockHole
+} CHANGELOG_BLOCK_STATE, *PCHANGELOG_BLOCK_STATE;
+
+//
+// change log memory block header
+//
+
+typedef struct _CHANGELOG_BLOCK_HEADER {
+ DWORD BlockSize;
+ CHANGELOG_BLOCK_STATE BlockState;
+} CHANGELOG_BLOCK_HEADER, *PCHANGELOG_BLOCK_HEADER;
+
+typedef struct _CHANGELOG_BLOCK_TRAILER {
+ DWORD BlockSize;
+} CHANGELOG_BLOCK_TRAILER, *PCHANGELOG_BLOCK_TRAILER;
+
+//
+// Macro to find a trailer (given a header)
+//
+
+#define ChangeLogBlockTrailer( _Header ) ( (PCHANGELOG_BLOCK_TRAILER)(\
+ ((LPBYTE)(_Header)) + \
+ (_Header)->BlockSize - \
+ sizeof(CHANGELOG_BLOCK_TRAILER) ))
+
+//
+// Macro to find if the change log describe be a particular
+// changelog descriptor is empty.
+//
+//
+
+#define ChangeLogIsEmpty( _Desc ) \
+( \
+ (_Desc)->FirstBlock == NULL || \
+ ((_Desc)->FirstBlock->BlockState == BlockFree && \
+ (_Desc)->FirstBlock->BlockSize >= \
+ (DWORD)((_Desc)->BufferEnd - (LPBYTE)(_Desc)->FirstBlock) ) \
+)
+
+//
+// Macro to initialize a changelog desriptor.
+//
+
+#define InitChangeLogDesc( _Desc ) \
+ RtlZeroMemory( (_Desc), sizeof( *(_Desc) ) ); \
+ (_Desc)->FileHandle = INVALID_HANDLE_VALUE;
+
+//
+// Macro to determine if the serial number on the change log entry matches
+// the serial number specified.
+//
+// The serial numbers match if there is an exact match or
+// if the changelog entry contains the serial number at the instant of promotion and the
+// requested serial number is the corresponding pre-promotion value.
+//
+
+#define IsSerialNumberEqual( _ChangeLogDesc, _ChangeLogEntry, _SerialNumber ) \
+( \
+ (_ChangeLogEntry)->SerialNumber.QuadPart == (_SerialNumber)->QuadPart || \
+ (((_ChangeLogEntry)->Flags & CHANGELOG_PDC_PROMOTION) && \
+ (_ChangeLogEntry)->SerialNumber.QuadPart == \
+ (_SerialNumber)->QuadPart + NlGlobalChangeLogPromotionIncrement.QuadPart ) \
+)
+
+
+//
+// variables describing the change log
+//
+
+typedef struct _CHANGELOG_DESCRIPTOR {
+
+ //
+ // Start and end of the allocated block.
+ //
+ LPBYTE Buffer; // Cache of the changelog contents
+ ULONG BufferSize; // Size (in bytes) of the buffer
+ LPBYTE BufferEnd; // Address of first byte beyond the end of the buffer
+
+ //
+ // Offset of the first and last dirty bytes
+ //
+
+ ULONG FirstDirtyByte;
+ ULONG LastDirtyByte;
+
+ //
+ // Address of the first physical block in the change log
+ //
+ PCHANGELOG_BLOCK_HEADER FirstBlock; // where delta buffer starts
+
+ //
+ // Description of the circular list of change log entries.
+ //
+ PCHANGELOG_BLOCK_HEADER Head; // start reading logs from here
+ PCHANGELOG_BLOCK_HEADER Tail; // where next log is written
+
+ //
+ // Serial Number of each database.
+ //
+ // Access is serialized via NlGlobalChangeLogCritSect
+ //
+
+ LARGE_INTEGER SerialNumber[NUM_DBS];
+
+ //
+ // Number of change log entries in the log for the specified database
+ //
+
+ DWORD EntryCount[NUM_DBS];
+
+ //
+ // Handle to file acting as backing store for the buffer.
+ //
+
+ HANDLE FileHandle; // handle for change log file
+
+ //
+ // Version 3: True to indicate this is a version 3 buffer.
+ //
+
+ BOOLEAN Version3;
+
+ //
+ // True if this is a re-do log and not a change log
+ //
+
+ BOOLEAN RedoLog;
+
+} CHANGELOG_DESCRIPTOR, *PCHANGELOG_DESCRIPTOR;
+
+//
+// The change log is a log of ALL changes made to the SAM/LSA databases. The
+// change log is maintained in serial number order.
+//
+// The redo log is a log of changes that need to be re-applied to a BDC. The
+// redo log is not maintained in any particular order.
+//
+EXTERN CHANGELOG_DESCRIPTOR NlGlobalChangeLogDesc;
+EXTERN CHANGELOG_DESCRIPTOR NlGlobalRedoLogDesc;
+EXTERN WCHAR NlGlobalChangeLogFilePrefix[PATHLEN+1]; // Changelog file name. (w/o postfix)
+
+
+//
+// Tables of related delta types
+//
+
+//
+// Table of delete delta types.
+// Index into the table with a delta type,
+// the entry is the delta type that is used to delete the object.
+//
+// There are some objects that can't be deleted. In that case, this table
+// contains a delta type that uniquely identifies the object. That allows
+// this table to be used to see if two deltas describe the same object type.
+//
+
+EXTERN const NETLOGON_DELTA_TYPE NlGlobalDeleteDeltaType[]
+#ifdef CHUTIL_ALLOCATE
+= {
+ AddOrChangeDomain, // 0 is an invalid delta type
+ AddOrChangeDomain, // AddOrChangeDomain,
+ DeleteGroup, // AddOrChangeGroup,
+ DeleteGroup, // DeleteGroup,
+ DeleteGroup, // RenameGroup,
+ DeleteUser, // AddOrChangeUser,
+ DeleteUser, // DeleteUser,
+ DeleteUser, // RenameUser,
+ DeleteGroup, // ChangeGroupMembership,
+ DeleteAlias, // AddOrChangeAlias,
+ DeleteAlias, // DeleteAlias,
+ DeleteAlias, // RenameAlias,
+ DeleteAlias, // ChangeAliasMembership,
+ AddOrChangeLsaPolicy, // AddOrChangeLsaPolicy,
+ DeleteLsaTDomain, // AddOrChangeLsaTDomain,
+ DeleteLsaTDomain, // DeleteLsaTDomain,
+ DeleteLsaAccount, // AddOrChangeLsaAccount,
+ DeleteLsaAccount, // DeleteLsaAccount,
+ DeleteLsaSecret, // AddOrChangeLsaSecret,
+ DeleteLsaSecret, // DeleteLsaSecret,
+ DeleteGroup, // DeleteGroupByName,
+ DeleteUser, // DeleteUserByName,
+ SerialNumberSkip, // SerialNumberSkip,
+ DummyChangeLogEntry // DummyChangeLogEntry
+}
+#endif // CHUTIL_ALLOCATE
+;
+
+#define MAX_DELETE_DELTA \
+ (sizeof(NlGlobalDeleteDeltaType)/sizeof(NlGlobalDeleteDeltaType[0]))
+
+
+//
+// Table of add delta types.
+// Index into the table with a delta type,
+// the entry is the delta type that is used to add the object.
+//
+// There are some objects that can't be added. In that case, this table
+// contains a delta type that uniquely identifies the object. That allows
+// this table to be used to see if two deltas describe the same object type.
+//
+// In the table, Groups and Aliases are represented as renames. This causes
+// NlPackSingleDelta to return both the group attributes and the group
+// membership.
+//
+
+EXTERN const NETLOGON_DELTA_TYPE NlGlobalAddDeltaType[]
+#ifdef CHUTIL_ALLOCATE
+= {
+ AddOrChangeDomain, // 0 is an invalid delta type
+ AddOrChangeDomain, // AddOrChangeDomain,
+ RenameGroup, // AddOrChangeGroup,
+ RenameGroup, // DeleteGroup,
+ RenameGroup, // RenameGroup,
+ AddOrChangeUser, // AddOrChangeUser,
+ AddOrChangeUser, // DeleteUser,
+ AddOrChangeUser, // RenameUser,
+ RenameGroup, // ChangeGroupMembership,
+ RenameAlias, // AddOrChangeAlias,
+ RenameAlias, // DeleteAlias,
+ RenameAlias, // RenameAlias,
+ RenameAlias, // ChangeAliasMembership,
+ AddOrChangeLsaPolicy, // AddOrChangeLsaPolicy,
+ AddOrChangeLsaTDomain, // AddOrChangeLsaTDomain,
+ AddOrChangeLsaTDomain, // DeleteLsaTDomain,
+ AddOrChangeLsaAccount, // AddOrChangeLsaAccount,
+ AddOrChangeLsaAccount, // DeleteLsaAccount,
+ AddOrChangeLsaSecret, // AddOrChangeLsaSecret,
+ AddOrChangeLsaSecret, // DeleteLsaSecret,
+ RenameGroup, // DeleteGroupByName,
+ AddOrChangeUser, // DeleteUserByName,
+ SerialNumberSkip, // SerialNumberSkip,
+ DummyChangeLogEntry // DummyChangeLogEntry
+}
+#endif // CHUTIL_ALLOCATE
+;
+
+#define MAX_ADD_DELTA DummyChangeLogEntry
+
+
+
+//
+// Table of Status Codes indicating the object doesn't exist.
+// Index into the table with a delta type.
+//
+// Map to STATUS_SUCCESS for the invalid cases to explicitly avoid other error
+// codes.
+
+EXTERN const NTSTATUS NlGlobalObjectNotFoundStatus[]
+#ifdef CHUTIL_ALLOCATE
+= {
+ STATUS_SUCCESS, // 0 is an invalid delta type
+ STATUS_NO_SUCH_DOMAIN, // AddOrChangeDomain,
+ STATUS_NO_SUCH_GROUP, // AddOrChangeGroup,
+ STATUS_NO_SUCH_GROUP, // DeleteGroup,
+ STATUS_NO_SUCH_GROUP, // RenameGroup,
+ STATUS_NO_SUCH_USER, // AddOrChangeUser,
+ STATUS_NO_SUCH_USER, // DeleteUser,
+ STATUS_NO_SUCH_USER, // RenameUser,
+ STATUS_NO_SUCH_GROUP, // ChangeGroupMembership,
+ STATUS_NO_SUCH_ALIAS, // AddOrChangeAlias,
+ STATUS_NO_SUCH_ALIAS, // DeleteAlias,
+ STATUS_NO_SUCH_ALIAS, // RenameAlias,
+ STATUS_NO_SUCH_ALIAS, // ChangeAliasMembership,
+ STATUS_SUCCESS, // AddOrChangeLsaPolicy,
+ STATUS_OBJECT_NAME_NOT_FOUND, // AddOrChangeLsaTDomain,
+ STATUS_OBJECT_NAME_NOT_FOUND, // DeleteLsaTDomain,
+ STATUS_OBJECT_NAME_NOT_FOUND, // AddOrChangeLsaAccount,
+ STATUS_OBJECT_NAME_NOT_FOUND, // DeleteLsaAccount,
+ STATUS_OBJECT_NAME_NOT_FOUND, // AddOrChangeLsaSecret,
+ STATUS_OBJECT_NAME_NOT_FOUND, // DeleteLsaSecret,
+ STATUS_NO_SUCH_GROUP, // DeleteGroupByName,
+ STATUS_NO_SUCH_USER, // DeleteUserByName,
+ STATUS_SUCCESS, // SerialNumberSkip,
+ STATUS_SUCCESS // DummyChangeLogEntry
+}
+#endif // CHUTIL_ALLOCATE
+;
+
+#define MAX_OBJECT_NOT_FOUND_STATUS DummyChangeLogEntry
+
+#define IsObjectNotFoundStatus( _DeltaType, _NtStatus ) \
+ (((ULONG)(_DeltaType) > MAX_OBJECT_NOT_FOUND_STATUS ) ? \
+ FALSE : \
+ (NlGlobalObjectNotFoundStatus[ (_DeltaType) ] == (_NtStatus)) )
+
+//
+// chutil.c
+//
+
+NTSTATUS
+NlCreateChangeLogFile(
+ IN PCHANGELOG_DESCRIPTOR ChangeLogDesc
+ );
+
+NTSTATUS
+NlFlushChangeLog(
+ IN PCHANGELOG_DESCRIPTOR ChangeLogDesc
+ );
+
+PCHANGELOG_ENTRY
+NlMoveToNextChangeLogEntry(
+ IN PCHANGELOG_DESCRIPTOR ChangeLogDesc,
+ IN PCHANGELOG_ENTRY ChangeLogEntry
+ );
+
+VOID
+PrintChangeLogEntry(
+ IN PCHANGELOG_ENTRY ChangeLogEntry
+ );
+
+NTSTATUS
+NlResetChangeLog(
+ IN PCHANGELOG_DESCRIPTOR ChangeLogDesc,
+ IN DWORD NewChangeLogSize
+ );
+
+NTSTATUS
+NlOpenChangeLogFile(
+ IN LPWSTR ChangeLogFileName,
+ OUT PCHANGELOG_DESCRIPTOR ChangeLogDesc,
+ IN BOOLEAN ReadOnly
+ );
+
+VOID
+NlCloseChangeLogFile(
+ IN PCHANGELOG_DESCRIPTOR ChangeLogDesc
+);
+
+NTSTATUS
+NlResizeChangeLogFile(
+ IN OUT PCHANGELOG_DESCRIPTOR ChangeLogDesc,
+ IN DWORD NewChangeLogSize
+);
+
+NTSTATUS
+NlWriteChangeLogEntry(
+ IN PCHANGELOG_DESCRIPTOR ChangeLogDesc,
+ IN PCHANGELOG_ENTRY ChangeLogEntry,
+ IN PSID ObjectSid,
+ IN PUNICODE_STRING ObjectName,
+ IN BOOLEAN FlushIt
+ );
+
+NTSTATUS
+NlWriteDeltaToChangeLog(
+ IN PCHANGELOG_DESCRIPTOR ChangeLogDesc,
+ IN PNETLOGON_DELTA_ENUM Delta,
+ IN ULONG DBIndex,
+ IN OUT PLARGE_INTEGER SerialNumber
+ );
+
+PCHANGELOG_ENTRY
+NlGetNextDownlevelChangeLogEntry(
+ ULONG DownlevelSerialNumber
+ );
+
+PCHANGELOG_ENTRY
+NlFindPromotionChangeLogEntry(
+ IN PCHANGELOG_DESCRIPTOR ChangeLogDesc,
+ IN LARGE_INTEGER SerialNumber,
+ IN DWORD DBIndex
+ );
+
+PCHANGELOG_ENTRY
+NlGetNextChangeLogEntry(
+ IN PCHANGELOG_DESCRIPTOR ChangeLogDesc,
+ IN LARGE_INTEGER SerialNumber,
+ IN DWORD DBIndex,
+ OUT LPDWORD ChangeLogEntrySize OPTIONAL
+ );
+
+PCHANGELOG_ENTRY
+NlGetNextUniqueChangeLogEntry(
+ IN PCHANGELOG_DESCRIPTOR ChangeLogDesc,
+ IN LARGE_INTEGER SerialNumber,
+ IN DWORD DBIndex,
+ OUT LPDWORD ChangeLogEntrySize OPTIONAL
+ );
+
+BOOL
+NlRecoverChangeLog(
+ PCHANGELOG_ENTRY ChangeLogEntry
+ );
+
+VOID
+NlDeleteChangeLogEntry(
+ IN PCHANGELOG_DESCRIPTOR ChangeLogDesc,
+ IN DWORD DBIndex,
+ IN LARGE_INTEGER SerialNumber
+ );
+
+BOOLEAN
+NlFixChangeLog(
+ IN PCHANGELOG_DESCRIPTOR ChangeLogDesc,
+ IN DWORD DBIndex,
+ IN LARGE_INTEGER SerialNumber,
+ IN BOOLEAN CopyEntriesToRedoLog
+ );
+
+BOOL
+NlValidateChangeLogEntry(
+ IN PCHANGELOG_ENTRY ChangeLogEntry,
+ IN DWORD ChangeLogEntrySize
+ );
+
+#undef EXTERN
+
diff --git a/private/net/svcdlls/logonsrv/server/chworker.c b/private/net/svcdlls/logonsrv/server/chworker.c
new file mode 100644
index 000000000..2b567796d
--- /dev/null
+++ b/private/net/svcdlls/logonsrv/server/chworker.c
@@ -0,0 +1,1714 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ worker.c
+
+Abstract:
+
+ Special Local groups replication to down level system
+ implementation.
+
+ This file contains the code required for the change log worker
+ thread. The worker thread maintains the list special local groups of
+ the BUILTIN database system and the list global groups that are
+ members of the special local group. Once a membership change is
+ found in one of the maintained groups, this thread simulate a user
+ delta for the new member and thus a change is reflected to the DL
+ system.
+
+Author:
+
+ Madan Appiah
+
+Environment:
+
+ User mode only.
+ Contains NT-specific code.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+ 13-Dec-1992 (Madana)
+ Created this file
+
+--*/
+
+//
+// Common include files.
+//
+
+#include <nt.h> // LARGE_INTEGER definition
+#include <ntrtl.h> // LARGE_INTEGER definition
+#include <nturtl.h> // LARGE_INTEGER definition
+#include <ntlsa.h> // needed by changelg.h
+
+#define NOMINMAX // Avoid redefinition of min and max in stdlib.h
+#include <rpc.h> // Needed by logon.h
+#include <logon_s.h>// includes lmcons.h, lmaccess.h, netlogon.h,
+ // ssi.h, windef.h
+#include <winbase.h>
+#include <stdio.h> // sprintf ...
+
+//
+// BEWARE: Be careful about adding netlogon.dll specific include files here.
+// This module is call by SAM and LSA. The netlogon service may not yet
+// be running. Therefore, guard against referencing netlogon.dll globals
+// other than those defined in changelg.h.
+//
+
+#include <samrpc.h> // Needed by samisrv.h
+#include <samisrv.h> // Needed by changelg.h
+#include <lsarpc.h> // Needed by lsrvdata.h and logonsrv.h
+#include <lsaisrv.h> // LsaI routines
+#include <changelg.h> // Local procedure definitions
+
+#include <lmerr.h> // NERR_Success defined here ..
+#include <lmerrlog.h> // NELOG_* defined here ..
+#include <lmapibuf.h> // NetApiBufferFree defined here ..
+#include <netlib.h> // NetpMemoryAllocate
+#include <netlibnt.h> // NetpNtStatusToApiStatus
+
+#define DEBUG_ALLOCATE
+#include <nldebug.h> // Netlogon debugging
+#undef DEBUG_ALLOCATE
+#include <align.h>
+#include <string.h> // strncmp
+#include <nlp.h> // NlpWriteEventlog defined here.
+
+#include <nlrepl.h> // I_Net* definitions
+
+#define WORKER_ALLOCATE
+#include <chworker.h>
+#undef WORKER_ALLOCATE
+
+//
+// Special Local ID array
+//
+
+ULONG NlGlobalSpecialLocalGroupIDs[] = {
+ DOMAIN_ALIAS_RID_ADMINS,
+ DOMAIN_ALIAS_RID_USERS,
+ DOMAIN_ALIAS_RID_GUESTS,
+ DOMAIN_ALIAS_RID_ACCOUNT_OPS,
+ DOMAIN_ALIAS_RID_SYSTEM_OPS,
+ DOMAIN_ALIAS_RID_PRINT_OPS } ;
+
+#define NUM_SP_LOCAL_GROUPS \
+ (sizeof(NlGlobalSpecialLocalGroupIDs) / \
+ sizeof(NlGlobalSpecialLocalGroupIDs[0]))
+
+
+
+BOOLEAN
+IsSpecialLocalGroup(
+ ULONG Rid
+ )
+/*++
+
+Routine Description:
+
+ This procedure determines that the given RID is one among the
+ special local group RID.
+
+Arguments:
+
+ Rid : Rid to test.
+
+Return Value:
+
+ TRUE : if the given RID is special local group.
+
+ FALSE : otherwise.
+
+--*/
+{
+ DWORD i;
+
+ for (i = 0; i < NUM_SP_LOCAL_GROUPS; i++ ) {
+
+ if( NlGlobalSpecialLocalGroupIDs[i] == Rid ) {
+
+ return( TRUE );
+ }
+ }
+
+ return( FALSE );
+
+}
+
+
+VOID
+NlSimulateUserDelta(
+ ULONG Rid
+ )
+/*++
+
+Routine Description:
+
+ This procedure calls SAM service to generate change to a user record
+ and increment the serial number.
+
+Arguments:
+
+ Rid : Rid of the new delta.
+
+Return Value:
+
+ none
+
+--*/
+{
+ NTSTATUS Status;
+
+ Status = SamINotifyDelta(
+ NlGlobalChWorkerSamDBHandle,
+ SecurityDbChange,
+ SecurityDbObjectSamUser,
+ Rid,
+ NULL,
+ FALSE,
+ NULL );
+
+ if ( !NT_SUCCESS(Status) ) {
+
+ NlPrint((NL_CRITICAL, "SamINotifyDelta failed %lx\n", Status ) );
+ }
+}
+
+
+NTSTATUS
+NlAddWorkerQueueEntry(
+ enum WORKER_QUEUE_ENTRY_TYPE EntryType,
+ ULONG Rid
+ )
+/*++
+
+Routine Description:
+
+ This procedure adds a new entry to worker queue. It sets queue event
+ so that worker thread wakes up to process this new entry.
+
+ Enter with NlGlobalChangeLogCritSect locked.
+
+Arguments:
+
+ EntryType : type of the new entry.
+
+ Rid : Rid of the member that caused this new entry.
+
+Return Value:
+
+ STATUS_NO_MEMORY - if no memory available for the new entry.
+
+--*/
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ PWORKER_QUEUE_ENTRY Entry;
+
+ Entry = (PWORKER_QUEUE_ENTRY)NetpMemoryAllocate(
+ sizeof(WORKER_QUEUE_ENTRY) );
+
+ if( Entry == NULL ) {
+
+ Status = STATUS_NO_MEMORY;
+ goto Cleanup;
+ }
+
+ Entry->EntryType = EntryType;
+ Entry->Rid = Rid;
+
+ //
+ // add to list
+ //
+
+ InsertTailList( &NlGlobalChangeLogWorkerQueue, &Entry->Next );
+
+ //
+ // awake worker thread.
+ //
+
+ if ( !SetEvent( NlGlobalChangeLogWorkerQueueEvent ) ) {
+ DWORD WinError;
+
+ WinError = GetLastError();
+
+ NlPrint((NL_CRITICAL,
+ "Cannot set ChangeLog worker queue event: %lu\n",
+ WinError ));
+
+ Status = NetpApiStatusToNtStatus( WinError );
+ }
+
+Cleanup:
+
+ if ( !NT_SUCCESS(Status) ) {
+
+ NlPrint((NL_CRITICAL, "NlAddWorkerQueueEntry failed %lx\n",Status ) );
+ }
+
+ return( Status );
+
+}
+
+
+PGLOBAL_GROUP_ENTRY
+NlGetGroupEntry(
+ PLIST_ENTRY GroupList,
+ ULONG Rid
+ )
+/*++
+
+Routine Description:
+
+ This procedure returns a group entry from the list. It returns NULL
+ if the requested entry is not in the list.
+
+ Enter with NlGlobalChangeLogCritSect locked if GroupList points to
+ NlGlobalSpecialServerGroupList.
+
+Arguments:
+
+ GroupList : list to search.
+
+ Rid : Rid of the entry wanted.
+
+Return Value:
+
+ NULL : if there is no entry.
+ otherwise : pointer to the entry.
+
+--*/
+{
+ PLIST_ENTRY ListEntry;
+ PGLOBAL_GROUP_ENTRY GroupEntry;
+
+ for ( ListEntry = GroupList->Flink;
+ ListEntry != GroupList;
+ ListEntry = ListEntry->Flink ) {
+
+ GroupEntry =
+ CONTAINING_RECORD( ListEntry, GLOBAL_GROUP_ENTRY, Next );
+
+ if( GroupEntry->Rid == Rid ) {
+
+ return( GroupEntry );
+ }
+ }
+
+ return( NULL );
+}
+
+
+NTSTATUS
+NlAddGroupEntry(
+ PLIST_ENTRY GroupList,
+ ULONG Rid
+ )
+/*++
+
+Routine Description:
+
+ This procedure adds a group entry to a global group list.
+
+ Enter with NlGlobalChangeLogCritSect locked if GroupList points to
+ NlGlobalSpecialServerGroupList.
+
+Arguments:
+
+ GroupList : List to modify.
+
+ Rid : Rid of the new group entry.
+
+ Name : Name of the new group entry.
+
+Return Value:
+
+ STATUS_NO_MEMORY - if no memory available for the new entry.
+
+--*/
+{
+ PGLOBAL_GROUP_ENTRY Entry;
+
+ //
+ // If the entry already exists,
+ // don't add it again.
+ //
+ Entry = NlGetGroupEntry( GroupList, Rid );
+
+ if ( Entry != NULL) {
+ return STATUS_SUCCESS;
+ }
+
+ //
+ // get memory for this entry
+ //
+
+ Entry = (PGLOBAL_GROUP_ENTRY)NetpMemoryAllocate(
+ sizeof(GLOBAL_GROUP_ENTRY) );
+
+ if( Entry == NULL ) {
+
+ return( STATUS_NO_MEMORY );
+ }
+
+ Entry->Rid = Rid;
+
+ //
+ // add to list
+ //
+
+ InsertTailList( GroupList, &Entry->Next );
+
+ return( STATUS_SUCCESS );
+
+}
+
+
+NTSTATUS
+NlAddGlobalGroupsToList(
+ PLIST_ENTRY GroupList,
+ ULONG LocalGroupID
+ )
+/*++
+
+Routine Description:
+
+ This procedure adds the global groups that are member of the given
+ alias.
+
+Arguments:
+
+ GroupList : List to munch.
+
+ LocalGroupId : Rid of the local group.
+
+Return Value:
+
+ Return NT Status code.
+
+--*/
+{
+ NTSTATUS Status;
+ SAMPR_HANDLE AliasHandle = NULL;
+
+ SAMPR_PSID_ARRAY Members = {0, NULL};
+ PULONG RidArray = NULL;
+ DWORD RidArrayLength = 0;
+ SAMPR_RETURNED_USTRING_ARRAY Names = {0, NULL};
+ SAMPR_ULONG_ARRAY Use = {0, NULL};
+ DWORD i;
+
+ //
+ // Open Local Group
+ //
+
+ Status = SamrOpenAlias(
+ NlGlobalChWorkerBuiltinDBHandle,
+ 0, // No desired access
+ LocalGroupID,
+ &AliasHandle );
+
+ if (!NT_SUCCESS(Status)) {
+ AliasHandle = NULL;
+ goto Cleanup;
+ }
+
+ //
+ // Enumerate members in this local group.
+ //
+
+ Status = SamrGetMembersInAlias(
+ AliasHandle,
+ &Members );
+
+ if (!NT_SUCCESS(Status)) {
+ Members.Sids = NULL;
+ goto Cleanup;
+ }
+
+ //
+ // Determine the SIDs that belong to the Account Domain and get the
+ // RIDs of them.
+ //
+
+ if( Members.Count == 0) {
+
+ Status = STATUS_SUCCESS;
+ goto Cleanup;
+ }
+
+ //
+ // Allocate the maximum size RID array required.
+ //
+
+ RidArray = (PULONG)NetpMemoryAllocate( Members.Count * sizeof(ULONG) );
+
+ if( RidArray == NULL ) {
+ Status = STATUS_NO_MEMORY;
+ goto Cleanup;
+ }
+
+ for( i = 0; i < Members.Count; i++) {
+
+ PUCHAR SubAuthorityCount;
+ BOOLEAN EqualSid;
+
+ SubAuthorityCount =
+ RtlSubAuthorityCountSid(Members.Sids[i].SidPointer);
+
+ (*SubAuthorityCount)--;
+ EqualSid = RtlEqualSid( NlGlobalChWorkerSamDomainSid,
+ Members.Sids[i].SidPointer );
+ (*SubAuthorityCount)++;
+
+ if( EqualSid ) {
+
+ RidArray[RidArrayLength] =
+ *RtlSubAuthoritySid( Members.Sids[i].SidPointer,
+ (*SubAuthorityCount) -1 );
+ RidArrayLength++;
+ }
+ }
+
+ if( RidArrayLength == 0) {
+
+ Status = STATUS_SUCCESS;
+ goto Cleanup;
+ }
+
+ //
+ // Get Group RIDs and add them to list.
+ //
+
+ Status = SamrLookupIdsInDomain( NlGlobalChWorkerSamDBHandle,
+ RidArrayLength,
+ RidArray,
+ &Names,
+ &Use );
+
+ if ( !NT_SUCCESS(Status) ) {
+
+ Names.Element = NULL;
+ Use.Element = NULL;
+
+ if( Status == STATUS_NONE_MAPPED ) {
+
+ //
+ // if no SID is mapped, we can't do much here.
+ //
+
+ NlPrint((NL_CRITICAL,
+ "NlAddGlobalGroupsToList could not map any SID from "
+ "local group, RID = %lx \n", LocalGroupID ));
+
+ Status = STATUS_SUCCESS;
+ }
+
+ goto Cleanup;
+ }
+
+ NlAssert( Names.Count == RidArrayLength );
+ NlAssert( Names.Element != NULL );
+ NlAssert( Use.Count == RidArrayLength );
+ NlAssert( Use.Element != NULL );
+
+ //
+ // Find groups and add them to list.
+ //
+
+ for( i = 0; i < RidArrayLength; i++ ) {
+
+ if( Use.Element[i] == SidTypeGroup ) {
+
+ //
+ // we found a group, add it to the list if it is not there
+ // already.
+ //
+
+ if( NlGetGroupEntry( GroupList, RidArray[i] ) != NULL ) {
+
+ //
+ // entry already in the list.
+ //
+
+ continue;
+ }
+
+ //
+ // add an entry to the list.
+ //
+
+ Status = NlAddGroupEntry( GroupList, RidArray[i] );
+
+ if ( !NT_SUCCESS(Status) ) {
+ goto Cleanup;
+ }
+ }
+ }
+
+Cleanup:
+
+ if( Names.Element != NULL ) {
+ SamIFree_SAMPR_RETURNED_USTRING_ARRAY( &Names );
+ }
+
+ if( Use.Element != NULL ) {
+ SamIFree_SAMPR_ULONG_ARRAY( &Use );
+ }
+
+ if( RidArray != NULL ) {
+ NetpMemoryFree( RidArray );
+ }
+
+ if ( Members.Sids != NULL ) {
+ SamIFree_SAMPR_PSID_ARRAY( (PSAMPR_PSID_ARRAY)&Members );
+ }
+
+ if( AliasHandle != NULL ) {
+ SamrCloseHandle( &AliasHandle );
+ }
+
+ if ( !NT_SUCCESS(Status) ) {
+
+ NlPrint((NL_CRITICAL, "NlAddGlobalGroupsToList failed %lx\n",
+ Status ));
+ }
+
+ return( Status );
+}
+
+
+NTSTATUS
+NlInitSpecialGroupList(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This routine browses through the following special local groups and
+ forms a list global groups that are members of the local groups.
+
+ Special Local groups :
+
+ 1. DOMAIN_ALIAS_RID_ADMINS
+ 2. DOMAIN_ALIAS_RID_USERS
+ 3. DOMAIN_ALIAS_RID_GUESTS
+ 4. DOMAIN_ALIAS_RID_ACCOUNT_OPS
+ 5. DOMAIN_ALIAS_RID_SYSTEM_OPS
+ 6. DOMAIN_ALIAS_RID_PRINT_OPS
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ Return NT Status code.
+
+--*/
+{
+
+ NTSTATUS Status;
+ LIST_ENTRY SpecialServerGroupList;
+ DWORD i;
+
+ InitializeListHead(&SpecialServerGroupList);
+
+ for (i = 0; i < NUM_SP_LOCAL_GROUPS; i++ ) {
+
+ Status = NlAddGlobalGroupsToList(
+ &SpecialServerGroupList,
+ NlGlobalSpecialLocalGroupIDs[i] );
+
+ if ( !NT_SUCCESS(Status) ) {
+ return( Status );
+ }
+ }
+
+ //
+ // install new list in global data.
+ //
+
+ LOCK_CHANGELOG();
+
+ NlAssert( IsListEmpty(&NlGlobalSpecialServerGroupList) );
+
+ //
+ // install list in global data
+ //
+
+ NlGlobalSpecialServerGroupList = SpecialServerGroupList;
+ (SpecialServerGroupList.Flink)->Blink = &NlGlobalSpecialServerGroupList;
+ (SpecialServerGroupList.Blink)->Flink = &NlGlobalSpecialServerGroupList;
+
+ UNLOCK_CHANGELOG();
+
+ return( Status );
+}
+
+
+BOOL
+NlIsServersGroupEmpty(
+ ULONG ServersGroupRid
+ )
+/*++
+
+Routine Description:
+
+ This procedure determines whether the Servers group is empty or not.
+
+Arguments:
+
+ Rid : Rid of the SERVERS group. If it is zero then determine the RID
+ by lookup.
+
+Return Value:
+
+ FALSE : If the servers group exist and it is non-empty.
+ TRUE : otherwise
+
+--*/
+{
+ NTSTATUS Status;
+
+ SAMPR_ULONG_ARRAY RelativeIdArray = {0, NULL};
+ SAMPR_ULONG_ARRAY UseArray = {0, NULL};
+ RPC_UNICODE_STRING GroupNameString;
+ SAMPR_HANDLE GroupHandle = NULL;
+ BOOL ReturnValue = TRUE;
+
+ PSAMPR_GET_MEMBERS_BUFFER MembersBuffer = NULL;
+
+ if ( ServersGroupRid == 0 ) {
+
+ //
+ // Convert the group name to a RelativeId.
+ //
+
+ RtlInitUnicodeString( (PUNICODE_STRING)&GroupNameString,
+ SSI_SERVER_GROUP_W );
+
+ Status = SamrLookupNamesInDomain(
+ NlGlobalChWorkerSamDBHandle,
+ 1,
+ &GroupNameString,
+ &RelativeIdArray,
+ &UseArray );
+
+ if ( !NT_SUCCESS(Status) ) {
+
+ RelativeIdArray.Element = NULL;
+ UseArray.Element = NULL;
+ goto Cleanup;
+ }
+
+ //
+ // we should get back exactly one entry of info back.
+ //
+
+ NlAssert( UseArray.Count == 1 );
+ NlAssert( UseArray.Element != NULL );
+ NlAssert( RelativeIdArray.Count == 1 );
+ NlAssert( RelativeIdArray.Element != NULL );
+
+ if ( UseArray.Element[0] != SidTypeGroup ) {
+ goto Cleanup;
+ }
+
+ ServersGroupRid = RelativeIdArray.Element[0];
+ }
+
+ //
+ // Open the SERVERS group
+ //
+
+ Status = SamrOpenGroup( NlGlobalChWorkerSamDBHandle,
+ 0, // No desired access
+ ServersGroupRid,
+ &GroupHandle );
+
+ if ( !NT_SUCCESS(Status) ) {
+ GroupHandle = NULL;
+ goto Cleanup;
+ }
+
+ //
+ // enumerate members in the group.
+ //
+
+ Status = SamrGetMembersInGroup( GroupHandle, &MembersBuffer );
+
+ if (!NT_SUCCESS(Status)) {
+ MembersBuffer = NULL;
+ goto Cleanup;
+ }
+
+ if ( MembersBuffer->MemberCount != 0 ) {
+
+ //
+ // atleast a member in there.
+ //
+
+ ReturnValue = FALSE;
+
+ //
+ // Save the list of LmBdcs
+
+ NlLmBdcListSet( MembersBuffer->MemberCount,
+ MembersBuffer->Members );
+ }
+
+Cleanup:
+
+ SamIFree_SAMPR_ULONG_ARRAY( &RelativeIdArray );
+ SamIFree_SAMPR_ULONG_ARRAY( &UseArray );
+
+ if ( MembersBuffer != NULL ) {
+ SamIFree_SAMPR_GET_MEMBERS_BUFFER( MembersBuffer );
+ }
+
+ if( GroupHandle != NULL ) {
+ (VOID) SamrCloseHandle( &GroupHandle );
+ }
+
+ return ReturnValue;
+}
+
+
+BOOLEAN
+NlProcessQueueEntry(
+ PWORKER_QUEUE_ENTRY Entry
+ )
+/*++
+
+Routine Description:
+
+ This procedure processes an entry that has come from the worker
+ queue.
+
+Arguments:
+
+ WorkerQueueEntry : pointer to worker structure.
+
+Return Value:
+
+ TRUE : if we need to continue processing more entries.
+ FALSE : if we need to terminate the worker.
+
+--*/
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ ULONG Rid = Entry->Rid;
+ BOOLEAN ReturnValue = TRUE;
+ SAMPR_RETURNED_USTRING_ARRAY Names = {0, NULL};
+ SAMPR_ULONG_ARRAY Use = {0, NULL};
+ SAMPR_HANDLE GroupHandle = NULL;
+ PSAMPR_GET_MEMBERS_BUFFER MembersBuffer = NULL;
+ SAMPR_HANDLE UserHandle = NULL;
+
+ PSAMPR_GET_GROUPS_BUFFER GroupsBuffer = NULL;
+ PGLOBAL_GROUP_ENTRY GroupEntry;
+
+ DWORD i;
+
+
+ //
+ // The membership of a special local group is being changed,
+ // force each lanman BDC to re-sync with each user that's being
+ // added-to/removed-from the local group.
+ //
+ switch ( Entry->EntryType ) {
+ case ChangeLogAliasMembership :
+
+ //
+ // determine Rid Type.
+ //
+
+ Status = SamrLookupIdsInDomain(
+ NlGlobalChWorkerSamDBHandle,
+ 1,
+ &Rid,
+ &Names,
+ &Use );
+
+ if ( !NT_SUCCESS(Status) ) {
+ Names.Element = NULL;
+ Use.Element = NULL;
+ goto Cleanup;
+ }
+
+ NlAssert( Names.Count == 1 );
+ NlAssert( Names.Element != NULL );
+ NlAssert( Use.Count == 1 );
+ NlAssert( Use.Element != NULL );
+
+ if( Use.Element[0] == SidTypeUser ) {
+
+ NlSimulateUserDelta( Rid );
+
+ //
+ // if this users is added unknowingly to the global group
+ // list, remove it now.
+ //
+
+ LOCK_CHANGELOG();
+
+ GroupEntry = NlGetGroupEntry (
+ &NlGlobalSpecialServerGroupList,
+ Rid );
+
+ if( GroupEntry != NULL ) {
+
+ RemoveEntryList( &GroupEntry->Next );
+ NetpMemoryFree( GroupEntry );
+ }
+
+ UNLOCK_CHANGELOG();
+
+
+ } else if( Use.Element[0] == SidTypeGroup ) {
+
+ DWORD i;
+
+ //
+ // simulate deltas for all members in this group.
+ //
+
+ Status = SamrOpenGroup( NlGlobalChWorkerSamDBHandle,
+ 0, // No desired access
+ Rid,
+ &GroupHandle );
+
+ if (!NT_SUCCESS(Status)) {
+ GroupHandle = NULL;
+ goto Cleanup;
+ }
+
+ Status = SamrGetMembersInGroup( GroupHandle, &MembersBuffer );
+
+ if (!NT_SUCCESS(Status)) {
+ MembersBuffer = NULL;
+ goto Cleanup;
+ }
+
+ for( i = 0; i < MembersBuffer->MemberCount; i++) {
+
+ NlSimulateUserDelta( MembersBuffer->Members[i] );
+ }
+
+#if DBG
+ //
+ // Ensure the change log thread already added this group
+ //
+
+ LOCK_CHANGELOG();
+
+ GroupEntry = NlGetGroupEntry (
+ &NlGlobalSpecialServerGroupList,
+ Rid );
+
+ UNLOCK_CHANGELOG();
+
+ NlAssert( GroupEntry != NULL );
+#endif // DBG
+
+ } else {
+
+ //
+ // ignore any other changes
+ //
+
+ NlAssert( FALSE );
+ }
+
+ break;
+
+ //
+ // The group membership of the special group has changed.
+ // Force Lanman BDCs to re-sync the user being added-to/removed-from
+ // the domain.
+ //
+ case ChangeLogGroupMembership :
+
+ //
+ // determine Rid Type.
+ //
+
+ Status = SamrLookupIdsInDomain(
+ NlGlobalChWorkerSamDBHandle,
+ 1,
+ &Rid,
+ &Names,
+ &Use );
+
+ if ( !NT_SUCCESS(Status) ) {
+ Names.Element = NULL;
+ Use.Element = NULL;
+ goto Cleanup;
+ }
+
+ NlAssert( Names.Count == 1 );
+ NlAssert( Names.Element != NULL );
+ NlAssert( Use.Count == 1 );
+ NlAssert( Use.Element != NULL );
+
+ NlAssert( Use.Element[0] == SidTypeUser );
+
+
+ if( Use.Element[0] == SidTypeUser ) {
+
+ NlSimulateUserDelta( Rid );
+ }
+
+ break;
+
+
+ //
+ // A member was deleted from the SERVERS group.
+ // Check to see if this thread can terminate.
+ //
+ case ServersGroupDel :
+
+ //
+ // if the server group is empty then terminate worker thread.
+ //
+
+ if ( NlGlobalLmBdcCount == 0 ) {
+ ReturnValue = FALSE;
+ }
+
+ break;
+
+
+ //
+ // Rename user is handled as multiple deltas:
+ // 1) Delete old user and
+ // 2) Add new user.
+ // 3) Update membership of each group the user is a member of
+ //
+ case ChangeLogRenameUser :
+
+ //
+ // simulate a user change so that an user account with
+ // new name will be created on the down level system.
+ //
+
+ NlSimulateUserDelta( Rid );
+
+ //
+ // create deltas to make his group membership correct on the
+ // down level machine.
+ //
+
+ Status = SamrOpenUser( NlGlobalChWorkerSamDBHandle,
+ 0, // No desired access
+ Rid,
+ &UserHandle );
+
+ if (!NT_SUCCESS(Status)) {
+ UserHandle = NULL;
+ goto Cleanup;
+ }
+
+ Status = SamrGetGroupsForUser( UserHandle, &GroupsBuffer );
+
+ if (!NT_SUCCESS(Status)) {
+ GroupsBuffer = NULL;
+ goto Cleanup;
+ }
+
+ for( i = 0; i < GroupsBuffer->MembershipCount; i++) {
+
+ Status = SamINotifyDelta(
+ NlGlobalChWorkerSamDBHandle,
+ SecurityDbChangeMemberAdd,
+ SecurityDbObjectSamGroup,
+ GroupsBuffer->Groups[i].RelativeId,
+ NULL,
+ FALSE,
+ NULL );
+
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+ }
+
+ break;
+
+ //
+ // A newly added user needs to have it's membership in "Domain Users" updated, too.
+ //
+ // Here we simply supply a corresponding change user membership delta which
+ // ends up as a AddOrChangeUser delta with the CHANGELOG_DOMAINUSERS_CHANGED
+ // flag set. NetrAccountDeltas interprets that flag to mean
+ // "send the membership of this user".
+ //
+ case ChangeLogAddUser:
+
+ Status = SamINotifyDelta(
+ NlGlobalChWorkerSamDBHandle,
+ SecurityDbChangeMemberAdd,
+ SecurityDbObjectSamUser,
+ Rid,
+ NULL,
+ FALSE,
+ NULL );
+
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+ break;
+
+
+ //
+ // Rename group is handled as three deltas:
+ // 1) Delete old group,
+ // 2) Add new group and
+ // 3) Changemembership of new group.
+ //
+ case ChangeLogRenameGroup :
+
+ //
+ // simulate a group change so that a group account with
+ // new name will be created on the down level system. Also
+ // simulate a changemembership delta so that the members are
+ // added to the new group appropriately.
+ //
+
+ Status = SamINotifyDelta(
+ NlGlobalChWorkerSamDBHandle,
+ SecurityDbChange,
+ SecurityDbObjectSamGroup,
+ Rid,
+ NULL,
+ FALSE,
+ NULL );
+
+ if ( NT_SUCCESS(Status) ) {
+
+ Status = SamINotifyDelta(
+ NlGlobalChWorkerSamDBHandle,
+ SecurityDbChangeMemberAdd,
+ SecurityDbObjectSamGroup,
+ Rid,
+ NULL,
+ FALSE,
+ NULL );
+ }
+
+ if ( !NT_SUCCESS(Status) ) {
+
+ NlPrint((NL_CRITICAL, "SamINotifyDelta failed %lx\n", Status ) );
+ }
+
+ break;
+
+ default:
+
+ NlPrint((NL_CRITICAL,
+ "NlProcessQueueEntry found unknown queue entry : %lx\n",
+ Entry->EntryType ));
+ break;
+
+ }
+
+Cleanup:
+
+ if( Names.Element != NULL ) {
+ SamIFree_SAMPR_RETURNED_USTRING_ARRAY( &Names );
+ }
+
+ if( Use.Element != NULL ) {
+ SamIFree_SAMPR_ULONG_ARRAY( &Use );
+ }
+
+ if ( MembersBuffer != NULL ) {
+ SamIFree_SAMPR_GET_MEMBERS_BUFFER( MembersBuffer );
+ }
+
+ if( GroupHandle != NULL ) {
+ (VOID) SamrCloseHandle( &GroupHandle );
+ }
+
+ if ( GroupsBuffer != NULL ) {
+ SamIFree_SAMPR_GET_GROUPS_BUFFER( GroupsBuffer );
+ }
+
+ if( UserHandle != NULL ) {
+ (VOID) SamrCloseHandle( &UserHandle );
+ }
+
+ if ( !NT_SUCCESS(Status) ) {
+
+ NlPrint((NL_CRITICAL,
+ "NlProcessQueueEntry failed : %lx\n",
+ Status ));
+
+ }
+
+ return ReturnValue;
+
+}
+
+
+VOID
+NlChangeLogWorker(
+ IN LPVOID ChangeLogWorkerParam
+ )
+/*++
+
+Routine Description:
+
+ This thread performs the special operations that are required to
+ replicate the special local groups such as Administrator, Server
+ Operartors, etc., in the NT BUILTIN database to the
+ down level systems.
+
+ This thread comes up first time during system bootup and initializes
+ required global data. If this NT (PDC) System is replicating to any
+ down level system then it stays back, otherwise it terminates. Also
+ when a down level system is added to the domain, this thread is
+ created if it is not running on the system before.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ Return when there is no down level system on the domain.
+
+--*/
+{
+ NTSTATUS Status;
+
+#if DBG
+ DWORD Count;
+#endif
+
+
+ NlPrint((NL_CHANGELOG, "ChangeLogWorker Thread is starting \n"));
+
+ //
+ // check if have initialize the global data before
+ //
+
+ if ( !NlGlobalChangeLogWorkInit ) {
+
+ PLSAPR_POLICY_INFORMATION PolicyAccountDomainInfo = NULL;
+ DWORD DomainSidLength;
+
+ //
+ // wait for SAM service to start.
+ //
+
+ if( !NlWaitForSamService(FALSE) ) {
+
+ NlPrint((NL_CRITICAL, "Sam server failed start."));
+ goto Cleanup;
+ }
+
+ //
+ // Open Sam Server
+ //
+
+ Status = SamIConnect( NULL, // No server name
+ &NlGlobalChWorkerSamServerHandle,
+ 0, // Ignore desired access
+ (BOOLEAN) TRUE );
+ // Indicate we are privileged
+
+ if ( !NT_SUCCESS(Status) ) {
+
+ NlPrint((NL_CRITICAL,
+ "Failed to connect to SAM server %lx\n", Status ));
+
+ NlGlobalChWorkerSamServerHandle = NULL;
+ goto Cleanup;
+ }
+
+ //
+ // Open Policy Domain
+ //
+
+ Status = LsaIOpenPolicyTrusted( &NlGlobalChWorkerPolicyHandle );
+
+ if ( !NT_SUCCESS(Status) ) {
+
+ NlPrint((NL_CRITICAL,
+ "Failed to Open LSA database %lx\n", Status ));
+
+ NlGlobalChWorkerPolicyHandle = NULL;
+ goto Cleanup;
+ }
+
+ //
+ // Open BuiltIn Domain database
+ //
+ // Note, build in domain SID is made during dll init time.
+ //
+
+
+ Status = SamrOpenDomain( NlGlobalChWorkerSamServerHandle,
+ DOMAIN_ALL_ACCESS,
+ NlGlobalChWorkerBuiltinDomainSid,
+ &NlGlobalChWorkerBuiltinDBHandle );
+
+ if ( !NT_SUCCESS(Status) ) {
+
+ NlPrint((NL_CRITICAL,
+ "Failed to Open BUILTIN database %lx\n", Status ));
+ NlGlobalChWorkerBuiltinDBHandle = NULL;
+ goto Cleanup;
+ }
+
+ //
+ // Query account domain SID.
+ //
+
+ Status = LsarQueryInformationPolicy(
+ NlGlobalChWorkerPolicyHandle,
+ PolicyAccountDomainInformation,
+ &PolicyAccountDomainInfo );
+
+ if ( !NT_SUCCESS(Status) ) {
+
+ NlPrint((NL_CRITICAL,
+ "Failed to Query Account domain Sid from LSA %lx\n",
+ Status ));
+
+ goto Cleanup;
+ }
+
+ if ( PolicyAccountDomainInfo->PolicyAccountDomainInfo.DomainSid == NULL ) {
+
+ LsaIFree_LSAPR_POLICY_INFORMATION(
+ PolicyAccountDomainInformation,
+ PolicyAccountDomainInfo );
+
+ NlPrint((NL_CRITICAL, "Account domain info from LSA invalid.\n"));
+ goto Cleanup;
+ }
+
+ //
+ // copy domain SID to global data.
+ //
+
+ DomainSidLength = RtlLengthSid( PolicyAccountDomainInfo->
+ PolicyAccountDomainInfo.DomainSid );
+
+ NlGlobalChWorkerSamDomainSid = (PSID)NetpMemoryAllocate( DomainSidLength );
+
+ if( NlGlobalChWorkerSamDomainSid == NULL ) {
+
+ Status = STATUS_NO_MEMORY;
+
+ NlPrint((NL_CRITICAL,
+ "NlChangeLogWorker is out of memory.\n"));
+
+ goto Cleanup;
+ }
+
+ Status = RtlCopySid(
+ DomainSidLength,
+ NlGlobalChWorkerSamDomainSid,
+ PolicyAccountDomainInfo->
+ PolicyAccountDomainInfo.DomainSid );
+
+ if ( !NT_SUCCESS(Status) ) {
+
+ NlPrint((NL_CRITICAL,
+ "Failed to copy SAM Domain sid %lx\n", Status ));
+ goto Cleanup;
+ }
+
+ //
+ // Free up Account domain info, we don't need any more.
+ //
+
+ LsaIFree_LSAPR_POLICY_INFORMATION(
+ PolicyAccountDomainInformation,
+ PolicyAccountDomainInfo );
+
+ //
+ // Open Account domain
+ //
+
+ Status = SamrOpenDomain( NlGlobalChWorkerSamServerHandle,
+ DOMAIN_ALL_ACCESS,
+ NlGlobalChWorkerSamDomainSid,
+ &NlGlobalChWorkerSamDBHandle );
+
+ if ( !NT_SUCCESS(Status) ) {
+
+ NlPrint((NL_CRITICAL,
+ "Failed to Open SAM database %lx\n", Status ));
+ NlGlobalChWorkerSamDBHandle = NULL;
+ goto Cleanup;
+ }
+
+ //
+ // Initialization done. Never do it again.
+ //
+
+ NlGlobalChangeLogWorkInit = TRUE;
+
+ }
+
+ //
+ // If SERVERS global group is empty then it implies that we don't
+ // have any down level system on this domain. so we can stop this
+ // thread.
+ //
+
+ if ( NlIsServersGroupEmpty( 0 ) ) {
+
+ NlPrint((NL_CHANGELOG, "Servers Group is empty \n "));
+
+ goto Cleanup;
+ }
+
+ //
+ // Initialize NlGlobalSpecialServerGroupList.
+ //
+
+ Status = NlInitSpecialGroupList();
+
+ if ( !NT_SUCCESS(Status) ) {
+
+ NlPrint((NL_CRITICAL,
+ "Failed to initialize Special group list %lx\n", Status ));
+ goto Cleanup;
+ }
+
+ //
+ // process worker queue forever, terminate when we are asked to do
+ // so or when the SERVERS group goes empty.
+ //
+
+ for( ;; ) {
+
+ DWORD WaitStatus;
+
+ //
+ // wait on the queue to become non-empty
+ //
+
+ WaitStatus = WaitForSingleObject(
+ NlGlobalChangeLogWorkerQueueEvent,
+ (DWORD)(-1) );
+
+ if ( WaitStatus != 0 ) {
+
+ NlPrint((NL_CRITICAL,
+ "Change log worker failed, "
+ "WaitForSingleObject error: %ld\n",
+ WaitStatus));
+ break;
+ }
+
+ //
+ // empty worker queue.
+ //
+
+#if DBG
+ Count = 0;
+#endif
+ for (;;) {
+
+ PLIST_ENTRY ListEntry;
+ PWORKER_QUEUE_ENTRY WorkerQueueEntry;
+
+ //
+ // if we are asked to leave, do so.
+ //
+
+ if( NlGlobalChangeLogWorkerTerminate ) {
+
+ NlPrint((NL_CHANGELOG,
+ "ChangeLogWorker is asked to leave \n"));
+
+ goto Cleanup;
+ }
+
+ LOCK_CHANGELOG();
+
+ if( IsListEmpty( &NlGlobalChangeLogWorkerQueue ) ) {
+
+ UNLOCK_CHANGELOG();
+ break;
+ }
+
+ ListEntry = RemoveHeadList( &NlGlobalChangeLogWorkerQueue );
+
+ UNLOCK_CHANGELOG();
+
+ WorkerQueueEntry = CONTAINING_RECORD( ListEntry,
+ WORKER_QUEUE_ENTRY,
+ Next );
+
+ //
+ // process an queue entry.
+ //
+
+ if( !NlProcessQueueEntry( WorkerQueueEntry ) ) {
+
+ NlPrint((NL_CHANGELOG, "Servers group becomes empty \n"));
+
+ NetpMemoryFree( WorkerQueueEntry );
+ goto Cleanup;
+ }
+
+ //
+ // Free this entry.
+ //
+
+ NetpMemoryFree( WorkerQueueEntry );
+#if DBG
+ Count++;
+#endif
+ }
+
+ NlPrint((NL_CHANGELOG,
+ "Changelog worker processed %lu entries.\n", Count) );
+ }
+
+Cleanup:
+
+ //
+ // empty worker queue and group list
+ //
+
+ LOCK_CHANGELOG();
+
+#if DBG
+ Count = 0;
+#endif
+ while ( !IsListEmpty( &NlGlobalChangeLogWorkerQueue ) ) {
+ PLIST_ENTRY ListEntry;
+ PWORKER_QUEUE_ENTRY WorkerQueueEntry;
+
+ ListEntry = RemoveHeadList( &NlGlobalChangeLogWorkerQueue );
+
+ WorkerQueueEntry = CONTAINING_RECORD( ListEntry,
+ WORKER_QUEUE_ENTRY,
+ Next );
+ NetpMemoryFree( WorkerQueueEntry );
+#if DBG
+ Count++;
+#endif
+ }
+
+#if DBG
+ if ( Count != 0 ) {
+ NlPrint((NL_CHANGELOG,
+ "Changelog worker did not process %lu entries.\n", Count) );
+ }
+#endif
+
+ while ( !IsListEmpty( &NlGlobalSpecialServerGroupList ) ) {
+ PLIST_ENTRY ListEntry;
+ PGLOBAL_GROUP_ENTRY ServerEntry;
+
+ ListEntry = RemoveHeadList( &NlGlobalSpecialServerGroupList );
+
+ ServerEntry = CONTAINING_RECORD( ListEntry,
+ GLOBAL_GROUP_ENTRY,
+ Next );
+ NetpMemoryFree( ServerEntry );
+ }
+
+ UNLOCK_CHANGELOG();
+
+ NlPrint((NL_CHANGELOG, "ChangeLogWorker Thread is exiting \n"));
+
+ return;
+ UNREFERENCED_PARAMETER( ChangeLogWorkerParam );
+}
+
+
+
+BOOL
+NlStartChangeLogWorkerThread(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ Start the Change Log Worker thread if it is not already running.
+
+ Enter with NlGlobalChangeLogCritSect locked.
+
+Arguments:
+
+ None.
+
+Return Value:
+ None.
+
+--*/
+{
+ DWORD ThreadHandle;
+
+ //
+ // If the worker thread is already running, do nothing.
+ //
+
+ if ( IsChangeLogWorkerRunning() ) {
+ return FALSE;
+ }
+
+ NlGlobalChangeLogWorkerTerminate = FALSE;
+
+ NlGlobalChangeLogWorkerThreadHandle = CreateThread(
+ NULL, // No security attributes
+ THREAD_STACKSIZE,
+ (LPTHREAD_START_ROUTINE)
+ NlChangeLogWorker,
+ NULL,
+ 0, // No special creation flags
+ &ThreadHandle );
+
+ if ( NlGlobalChangeLogWorkerThreadHandle == NULL ) {
+
+ //
+ // ?? Shouldn't we do something in non-debug case
+ //
+
+ NlPrint((NL_CRITICAL, "Can't create change log worker thread %lu\n",
+ GetLastError() ));
+
+ return FALSE;
+ }
+
+ return TRUE;
+
+}
+
+
+VOID
+NlStopChangeLogWorker(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ Stops the worker thread if it is running and waits for it to stop.
+
+Arguments:
+
+ NONE
+
+Return Value:
+
+ NONE
+
+--*/
+{
+ //
+ // Ask the worker to stop running.
+ //
+
+ NlGlobalChangeLogWorkerTerminate = TRUE;
+
+ //
+ // Determine if the worker thread is already running.
+ //
+
+ if ( NlGlobalChangeLogWorkerThreadHandle != NULL ) {
+
+ //
+ // awake worker thread.
+ //
+
+ if ( !SetEvent( NlGlobalChangeLogWorkerQueueEvent ) ) {
+ NlPrint((NL_CRITICAL,
+ "Cannot set ChangeLog worker queue event: %lu\n",
+ GetLastError() ));
+ }
+
+ //
+ // We've asked the worker to stop. It should do so soon.
+ // Wait for it to stop.
+ //
+
+ NlWaitForSingleObject( "Wait for worker to stop",
+ NlGlobalChangeLogWorkerThreadHandle );
+
+
+ CloseHandle( NlGlobalChangeLogWorkerThreadHandle );
+ NlGlobalChangeLogWorkerThreadHandle = NULL;
+
+ }
+
+ NlGlobalChangeLogWorkerTerminate = FALSE;
+
+ return;
+}
+
+
+BOOL
+IsChangeLogWorkerRunning(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ Test if the change log worker thread is running
+
+ Enter with NlGlobalChangeLogCritSect locked.
+
+Arguments:
+
+ NONE
+
+Return Value:
+
+ TRUE - if the worker thread is running.
+
+ FALSE - if the worker thread is not running.
+
+--*/
+{
+ DWORD WaitStatus;
+
+ //
+ // Determine if the worker thread is already running.
+ //
+
+ if ( NlGlobalChangeLogWorkerThreadHandle != NULL ) {
+
+ //
+ // Time out immediately if the worker is still running.
+ //
+
+ WaitStatus = WaitForSingleObject(
+ NlGlobalChangeLogWorkerThreadHandle, 0 );
+
+ if ( WaitStatus == WAIT_TIMEOUT ) {
+ return TRUE;
+
+ } else if ( WaitStatus == 0 ) {
+ CloseHandle( NlGlobalChangeLogWorkerThreadHandle );
+ NlGlobalChangeLogWorkerThreadHandle = NULL;
+ return FALSE;
+
+ } else {
+ NlPrint((NL_CRITICAL,
+ "Cannot WaitFor Change Log Worker thread: %ld\n",
+ WaitStatus ));
+ return TRUE;
+ }
+
+ }
+
+ return FALSE;
+}
+
diff --git a/private/net/svcdlls/logonsrv/server/chworker.h b/private/net/svcdlls/logonsrv/server/chworker.h
new file mode 100644
index 000000000..b2bed81d9
--- /dev/null
+++ b/private/net/svcdlls/logonsrv/server/chworker.h
@@ -0,0 +1,215 @@
+/*++
+
+Copyright (c) 1991-1992 Microsoft Corporation
+
+Module Name:
+
+ worker.h
+
+Abstract:
+
+ Defines and routines needed to interface with worker.c.
+
+Author:
+
+ Madan Appiah (madana) 13-Dec-1992
+
+Environment:
+
+ User mode only.
+ Contains NT-specific code.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+ 13-Dec-1992 (madana)
+ Created this file.
+
+--*/
+
+//
+// worker.c will #include this file with WORKER_ALLOCATE defined.
+// That will cause each of these variables to be allocated.
+//
+
+#ifdef WORKER_ALLOCATE
+#define EXTERN
+#else
+#define EXTERN extern
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Structures and variables describing the Change Log
+//
+/////////////////////////////////////////////////////////////////////////////
+
+//
+// Global Group list entry.
+//
+
+typedef struct _GLOBAL_GROUP_ENTRY {
+ LIST_ENTRY Next;
+
+ ULONG Rid;
+} GLOBAL_GROUP_ENTRY, *PGLOBAL_GROUP_ENTRY;
+
+//
+// ChangeLog Worker queue entry.
+//
+
+typedef struct _WORKER_QUEUE_ENTRY {
+ LIST_ENTRY Next;
+
+ enum WORKER_QUEUE_ENTRY_TYPE {
+ ChangeLogAliasMembership,
+ ChangeLogGroupMembership,
+ ServersGroupDel,
+ ChangeLogRenameUser,
+ ChangeLogRenameGroup,
+ ChangeLogAddUser
+ } EntryType;
+
+ ULONG Rid;
+
+} WORKER_QUEUE_ENTRY, *PWORKER_QUEUE_ENTRY;
+
+//
+// Changelog worker thread variables
+//
+
+EXTERN SAMPR_HANDLE NlGlobalChWorkerSamServerHandle; // Handle to Sam Server database
+EXTERN LSAPR_HANDLE NlGlobalChWorkerPolicyHandle; // Handle to Policy Database
+
+EXTERN SAM_HANDLE NlGlobalChWorkerSamDBHandle; // database handle to access SAM database
+EXTERN SAM_HANDLE NlGlobalChWorkerBuiltinDBHandle; // database handle to access BUILTIN database
+
+EXTERN PSID NlGlobalChWorkerBuiltinDomainSid; // Sid of builtin domain
+EXTERN PSID NlGlobalChWorkerSamDomainSid; // Sid of sam domain
+
+//
+// Event to indicate that an entry is added to the changelog
+// worker queue.
+//
+
+EXTERN HANDLE NlGlobalChangeLogWorkerQueueEvent;
+
+//
+// Queue containing the change logs for down level special group
+// coversions. ChangeLog threads write entries to this queue and
+// the worker thread reads entries from this thread. This queue is
+// protected by NlGlobalChangeLogCritSect.
+//
+
+EXTERN LIST_ENTRY NlGlobalChangeLogWorkerQueue;
+
+//
+// List containing list of Global Groups that are members of special
+// local groups such Administrator, Server Operators etc., This list
+// initially built by the worker thread and then updated by the
+// changelog threads. This list is also protected by
+// NlGlobalChangeLogCritSect.
+//
+
+EXTERN LIST_ENTRY NlGlobalSpecialServerGroupList;
+
+//
+// Change log worker thread handle. This is projected by the
+// NlGlobalChangeLogCritSect.
+//
+
+EXTERN HANDLE NlGlobalChangeLogWorkerThreadHandle;
+
+//
+// Flag to indicate the Global data that are required for change log
+// worker have been initialized successfully.
+//
+
+EXTERN BOOL NlGlobalChangeLogWorkInit;
+
+//
+// Flag to stop the change log worker thread.
+// Protected by the NlGlobalChangeLogCritSect.
+//
+
+EXTERN BOOL NlGlobalChangeLogWorkerTerminate;
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Procedure forwards
+//
+/////////////////////////////////////////////////////////////////////////////
+
+BOOLEAN
+IsSpecialLocalGroup(
+ ULONG Rid
+ );
+
+VOID
+NlSimulateUserDelta(
+ ULONG Rid
+ );
+
+NTSTATUS
+NlAddWorkerQueueEntry(
+ enum WORKER_QUEUE_ENTRY_TYPE EntryType,
+ ULONG Rid
+ );
+
+PGLOBAL_GROUP_ENTRY
+NlGetGroupEntry(
+ PLIST_ENTRY GroupList,
+ ULONG Rid
+ );
+
+NTSTATUS
+NlAddGroupEntry(
+ PLIST_ENTRY GroupList,
+ ULONG Rid
+ );
+
+NTSTATUS
+NlAddGlobalGroupsToList(
+ PLIST_ENTRY GroupList,
+ ULONG LocalGroupID
+ );
+
+NTSTATUS
+NlInitSpecialGroupList(
+ VOID
+ );
+
+BOOL
+NlIsServersGroupEmpty(
+ ULONG ServersGroupRid
+ );
+
+BOOLEAN
+NlProcessQueueEntry(
+ PWORKER_QUEUE_ENTRY Entry
+ );
+
+VOID
+NlChangeLogWorker(
+ IN LPVOID ChangeLogWorkerParam
+ );
+
+BOOL
+NlStartChangeLogWorkerThread(
+ VOID
+ );
+
+VOID
+NlStopChangeLogWorker(
+ VOID
+ );
+
+BOOL
+IsChangeLogWorkerRunning(
+ VOID
+ );
+
+#undef EXTERN
+
+
diff --git a/private/net/svcdlls/logonsrv/server/error.c b/private/net/svcdlls/logonsrv/server/error.c
new file mode 100644
index 000000000..0b2d377d1
--- /dev/null
+++ b/private/net/svcdlls/logonsrv/server/error.c
@@ -0,0 +1,820 @@
+/*++
+
+Copyright (c) 1987-1991 Microsoft Corporation
+
+Module Name:
+
+ error.c
+
+Abstract:
+
+ Error routines for Netlogon service
+
+Author:
+
+ Ported from Lan Man 2.0
+
+Environment:
+
+ User mode only.
+ Contains NT-specific code.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+ 29-May-1991 (cliffv)
+ Ported to NT. Converted to NT style.
+
+--*/
+
+//
+// Common include files.
+//
+#include <logonsrv.h> // Include files common to entire service
+
+//
+// Include files specific to this .c file
+//
+
+#include <lmalert.h> // LAN Manager alert routines
+
+#include <lmerr.h> // NERR_Success
+#include <nlsecure.h> // NlGlobalNetlogonSecurityDescriptor ...
+#include <ntrpcp.h> // RpcpDeleteInterface ...
+#include <tstring.h> // Transitional string routines.
+#include <Secobj.h> // need for NetpDeleteSecurityObject
+
+
+
+NET_API_STATUS
+NlCleanup(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ Cleanup all global resources.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ PLIST_ENTRY ListEntry;
+ DWORD i;
+ BOOLEAN WaitForMsv;
+
+ //
+ // Let the ChangeLog routines know that Netlogon is not started.
+ //
+
+ NlGlobalChangeLogNetlogonState = NetlogonStopped;
+
+ //
+ // Timeout any async discoveries.
+ //
+ // The MainLoop thread is no longer running to complete them.
+ //
+
+ if ( NlGlobalSSICritSectInit ) {
+ NlDcDiscoveryExpired( TRUE );
+ }
+
+
+ //
+ // Indicate to external waiters that we're not running.
+ //
+
+ if ( NlGlobalStartedEvent != NULL ) {
+ //
+ // Reset it first in case some other process is preventing its deletion.
+ //
+ (VOID) NtResetEvent( NlGlobalStartedEvent, NULL );
+ (VOID) NtClose( NlGlobalStartedEvent );
+ NlGlobalStartedEvent = NULL;
+ }
+
+
+ //
+ // Stop the RPC server (Wait for outstanding calls to complete)
+ //
+
+ if ( NlGlobalRpcServerStarted ) {
+ Status = RpcServerUnregisterIf ( logon_ServerIfHandle, 0, TRUE );
+ NlAssert( Status == RPC_S_OK );
+ NlGlobalRpcServerStarted = FALSE;
+ }
+
+
+ //
+ // Tell all the MSV threads to leave netlogon.dll.
+ //
+
+ EnterCriticalSection( &NlGlobalMsvCritSect );
+ if ( NlGlobalMsvEnabled ) {
+ NlGlobalMsvEnabled = FALSE;
+ WaitForMsv = (NlGlobalMsvThreadCount > 0 );
+ } else {
+ WaitForMsv = FALSE;
+ }
+ LeaveCriticalSection( &NlGlobalMsvCritSect );
+
+ //
+ // Wait for the MSV threads to leave netlogon.dll
+ //
+
+ if ( NlGlobalMsvTerminateEvent != NULL ) {
+
+ if ( WaitForMsv ) {
+ WaitForSingleObject( NlGlobalMsvTerminateEvent, INFINITE );
+ }
+
+ (VOID) CloseHandle( NlGlobalMsvTerminateEvent );
+ NlGlobalMsvTerminateEvent = NULL;
+
+ }
+
+
+ //
+ // Cleanup scavenger thread.
+ // Wait for the scavenger thread to exit.
+ //
+
+ EnterCriticalSection( &NlGlobalScavengerCritSect );
+ NlStopScavenger();
+ LeaveCriticalSection( &NlGlobalScavengerCritSect );
+
+ DeleteCriticalSection( &NlGlobalScavengerCritSect );
+
+
+ //
+ // Need to cleanup replicator if only the thread started up successfully.
+ //
+
+ if( NlGlobalSSICritSectInit == TRUE ) {
+
+ //
+ // Wait for the replicator thread to exit.
+ //
+
+ EnterCriticalSection( &NlGlobalReplicatorCritSect );
+ NlStopReplicator();
+ LeaveCriticalSection( &NlGlobalReplicatorCritSect );
+
+ //
+ // Delete the Event used to ask the replicator to exit.
+ //
+
+ if( !CloseHandle( NlGlobalReplicatorTerminateEvent ) ) {
+ NlPrint((NL_CRITICAL,
+ "CloseHandle NlGlobalReplicatorTerminateEvent error: %lu\n",
+ GetLastError() ));
+ }
+
+
+
+ //
+ // Free the server session table.
+ //
+
+ LOCK_SERVER_SESSION_TABLE();
+
+ while ( (ListEntry = NlGlobalServerSessionTable.Flink) !=
+ &NlGlobalServerSessionTable ) {
+
+ PSERVER_SESSION ServerSession;
+
+ ServerSession =
+ CONTAINING_RECORD(ListEntry, SERVER_SESSION, SsSeqList);
+
+ // Indicate we no longer need the server session anymore.
+ ServerSession->SsLmBdcAccountRid = 0;
+ ServerSession->SsNtBdcAccountRid = 0;
+
+ NlFreeServerSession( ServerSession );
+ }
+
+
+ NlAssert( IsListEmpty( &NlGlobalBdcServerSessionList ) );
+ NlAssert( IsListEmpty( &NlGlobalPendingBdcList ) );
+
+
+ if ( NlGlobalServerSessionHashTable != NULL ) {
+ NetpMemoryFree( NlGlobalServerSessionHashTable );
+ NlGlobalServerSessionHashTable = NULL;
+ }
+ UNLOCK_SERVER_SESSION_TABLE();
+
+
+
+
+ //
+ // Free the Trust List
+ //
+
+ LOCK_TRUST_LIST();
+
+ while ( (ListEntry = NlGlobalTrustList.Flink) != &NlGlobalTrustList ) {
+ PCLIENT_SESSION ClientSession;
+
+ ClientSession =
+ CONTAINING_RECORD(ListEntry, CLIENT_SESSION, CsNext );
+
+ NlAssert( ClientSession->CsReferenceCount == 0 );
+
+ NlFreeClientSession( ClientSession );
+ }
+
+ InitializeListHead( &NlGlobalTrustList );
+ NlGlobalTrustListLength = 0;
+
+ if ( NlGlobalTrustedDomainList != NULL ) {
+ NetpMemoryFree( NlGlobalTrustedDomainList );
+ NlGlobalTrustedDomainList = NULL;
+ NlGlobalTrustedDomainCount = 0;
+ NlGlobalTrustedDomainListKnown = FALSE;
+ NlGlobalTrustedDomainListTime.QuadPart = 0;
+ }
+
+ UNLOCK_TRUST_LIST();
+
+
+ //
+ // Free the misc SSI critical sections.
+ //
+
+ DeleteCriticalSection( &NlGlobalServerSessionTableCritSect );
+ NlGlobalSSICritSectInit = FALSE;
+
+ //
+ // Free the transport list
+ //
+
+ NlTransportClose();
+
+ }
+
+
+ //
+ // Free the Global Client Session structure.
+ //
+
+ if ( NlGlobalClientSession != NULL ) {
+ NlAssert( NlGlobalClientSession->CsReferenceCount == 1 );
+ NlUnrefClientSession( NlGlobalClientSession );
+ NlFreeClientSession( NlGlobalClientSession );
+ NlGlobalClientSession = NULL;
+ }
+
+ DeleteCriticalSection( &NlGlobalReplicatorCritSect );
+ DeleteCriticalSection( &NlGlobalTrustListCritSect );
+ DeleteCriticalSection( &NlGlobalDcDiscoveryCritSect );
+
+
+ //
+ // Free up resources
+ //
+
+ if ( NlGlobalAnsiComputerName != NULL ) {
+ NetpMemoryFree( NlGlobalAnsiComputerName );
+ NlGlobalAnsiComputerName = NULL;
+ }
+
+ if ( NlGlobalAnsiDomainName != NULL ) {
+ NetpMemoryFree( NlGlobalAnsiDomainName );
+ NlGlobalAnsiDomainName = NULL;
+ }
+
+ if ( NlGlobalPrimaryDomainId != NULL ) {
+ NetpMemoryFree( NlGlobalPrimaryDomainId );
+ NlGlobalPrimaryDomainId = NULL;
+ }
+
+ for( i = 0; i < NUM_DBS; i++ ) {
+ if ( NlGlobalDBInfoArray[i].DBId != NULL ) {
+ NetpMemoryFree( NlGlobalDBInfoArray[i].DBId );
+ NlGlobalDBInfoArray[i].DBId = NULL;
+ }
+ }
+ DeleteCriticalSection( &NlGlobalDbInfoCritSect );
+
+ if ( NlGlobalNetlogonSecurityDescriptor != NULL ) {
+ NetpDeleteSecurityObject( &NlGlobalNetlogonSecurityDescriptor );
+ NlGlobalNetlogonSecurityDescriptor = NULL;
+ }
+
+ //
+ // Close the redo log if it's open
+ //
+
+ if ( NlGlobalRole == RoleBackup ) {
+ NlCloseChangeLogFile( &NlGlobalRedoLogDesc );
+ }
+
+ //
+ // delete well known SIDs if they are allocated already.
+ //
+
+ NetpFreeWellKnownSids();
+
+
+ //
+ // Close the Sam Handles
+ //
+
+ if ( NlGlobalSamServerHandle != NULL ) {
+ Status = SamrCloseHandle( &NlGlobalSamServerHandle);
+ NlAssert( NT_SUCCESS(Status) );
+ }
+
+ for( i = 0; i < NUM_DBS; i++ ) {
+
+ if ( NlGlobalDBInfoArray[i].DBIndex == LSA_DB) {
+
+ //
+ // this handle is same as NlGlobalPolicyHandle, so
+ // don't close it here.
+ //
+
+ continue;
+ }
+
+ if ( NlGlobalDBInfoArray[i].DBHandle != NULL ) {
+
+ Status = SamrCloseHandle( &NlGlobalDBInfoArray[i].DBHandle );
+ NlAssert( NT_SUCCESS(Status) );
+
+ }
+
+ }
+
+
+ //
+ // Close the LsaHandles
+ //
+
+ if ( NlGlobalPolicyHandle != NULL ) {
+ Status = LsarClose( &NlGlobalPolicyHandle );
+ NlAssert( NT_SUCCESS(Status) );
+ }
+
+
+
+ //
+ // Close the browser
+ //
+
+ NlBrowserClose();
+
+
+ //
+ // Delete the timer event
+ //
+
+ if ( NlGlobalTimerEvent != NULL ) {
+ (VOID) CloseHandle( NlGlobalTimerEvent );
+ NlGlobalTimerEvent = NULL;
+ }
+
+
+ //
+ // Set the service state to uninstalled, and tell the service controller.
+ //
+
+ NlGlobalServiceStatus.dwCurrentState = SERVICE_STOPPED;
+ NlGlobalServiceStatus.dwCheckPoint = 0;
+ NlGlobalServiceStatus.dwWaitHint = 0;
+
+ if( !SetServiceStatus( NlGlobalServiceHandle,
+ &NlGlobalServiceStatus ) ) {
+
+ NlPrint((NL_CRITICAL, "SetServiceStatus error: %lu\n",
+ GetLastError() ));
+ }
+
+ //
+ // Close service handle, we need not to close this handle.
+ //
+
+#ifdef notdef
+ // This service handle can not be closed
+ CloseServiceHandle( NlGlobalServiceHandle );
+#endif // notdef
+
+
+ //
+ // Close the handle to the debug file.
+ //
+
+#if DBG
+ EnterCriticalSection( &NlGlobalLogFileCritSect );
+ if ( NlGlobalLogFile != INVALID_HANDLE_VALUE ) {
+ CloseHandle( NlGlobalLogFile );
+ NlGlobalLogFile = INVALID_HANDLE_VALUE;
+ }
+ LeaveCriticalSection( &NlGlobalLogFileCritSect );
+
+ if( NlGlobalDebugSharePath != NULL ) {
+ NetpMemoryFree( NlGlobalDebugSharePath );
+ NlGlobalDebugSharePath = NULL;
+ }
+#endif // DBG
+
+ //
+ // Delete the Event used to ask Netlogon to exit.
+ //
+
+ if( !CloseHandle( NlGlobalTerminateEvent ) ) {
+ NlPrint((NL_CRITICAL,
+ "CloseHandle NlGlobalTerminateEvent error: %lu\n",
+ GetLastError() ));
+ }
+
+
+
+ //
+ // Return an exit status to our caller.
+ //
+ return (NET_API_STATUS)
+ ((NlGlobalServiceStatus.dwWin32ExitCode == ERROR_SERVICE_SPECIFIC_ERROR) ?
+ NlGlobalServiceStatus.dwServiceSpecificExitCode :
+ NlGlobalServiceStatus.dwWin32ExitCode);
+
+}
+
+
+
+
+VOID
+NlExit(
+ IN DWORD ServiceError,
+ IN DWORD Data,
+ IN NL_EXIT_CODE ExitCode,
+ IN LPWSTR ErrorString
+ )
+/*++
+
+Routine Description:
+
+ Registers service as uninstalled with error code.
+
+Arguments:
+
+ ServiceError - Service specific error code
+
+ Data - a DWORD of data to be logged with the message.
+ No data is logged if this is zero.
+
+ ExitCode - Indicates whether the message should be logged to the eventlog
+ and whether Data is a status code that should be appended to the bottom
+ of the message:
+
+ ErrorString - Error string, used to print it on debugger.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ IF_DEBUG( MISC ) {
+
+ NlPrint((NL_MISC, "NlExit: Netlogon exiting %lu 0x%lx",
+ ServiceError,
+ ServiceError ));
+
+ if ( Data ) {
+ NlPrint((NL_MISC, " Data: %lu 0x%lx", Data, Data ));
+ }
+
+ if( ErrorString != NULL ) {
+ NlPrint((NL_MISC, " '" FORMAT_LPWSTR "'", ErrorString ));
+ }
+
+ NlPrint(( NL_MISC, "\n"));
+
+ }
+
+ //
+ // Record our exit in the event log.
+ //
+
+ if ( ExitCode != DontLogError ) {
+ LPWSTR MsgStrings[2];
+ ULONG MessageCount = 0;
+
+ if ( ErrorString != NULL ) {
+ MsgStrings[MessageCount] = ErrorString;
+ MessageCount ++;
+ }
+
+ if ( ExitCode == LogErrorAndNtStatus ) {
+ MsgStrings[MessageCount] = (LPWSTR) Data;
+ MessageCount ++;
+ MessageCount |= LAST_MESSAGE_IS_NTSTATUS;
+ } else if ( ExitCode == LogErrorAndNetStatus ) {
+ MsgStrings[MessageCount] = (LPWSTR) Data;
+ MessageCount ++;
+ MessageCount |= LAST_MESSAGE_IS_NETSTATUS;
+ }
+
+
+ NlpWriteEventlog( ServiceError,
+ EVENTLOG_ERROR_TYPE,
+ (Data) ? (LPBYTE) &Data : NULL,
+ (Data) ? sizeof(Data) : 0,
+ (MessageCount != 0) ? MsgStrings : NULL,
+ MessageCount );
+ }
+
+ //
+ // Set the service state to stop pending.
+ //
+
+ NlGlobalServiceStatus.dwCurrentState = SERVICE_STOP_PENDING;
+ NlGlobalServiceStatus.dwWaitHint = NETLOGON_INSTALL_WAIT;
+ NlGlobalServiceStatus.dwCheckPoint = 0;
+
+ SET_SERVICE_EXITCODE(
+ ServiceError,
+ NlGlobalServiceStatus.dwWin32ExitCode,
+ NlGlobalServiceStatus.dwServiceSpecificExitCode
+ );
+
+ //
+ // Tell the service controller what our state is.
+ //
+
+ if( !SetServiceStatus( NlGlobalServiceHandle,
+ &NlGlobalServiceStatus ) ) {
+
+ NlPrint((NL_CRITICAL, "SetServiceStatus error: %lu\n",
+ GetLastError() ));
+ }
+
+ //
+ // Indicate that all threads should exit.
+ //
+
+ NlGlobalTerminate = TRUE;
+
+ if ( !SetEvent( NlGlobalTerminateEvent ) ) {
+ NlPrint((NL_CRITICAL, "Cannot set termination event: %lu\n",
+ GetLastError() ));
+ }
+
+}
+
+
+
+BOOL
+GiveInstallHints(
+ IN BOOL Started
+ )
+/*++
+
+Routine Description:
+
+ Give hints to the installer of the service that installation is progressing.
+
+Arguments:
+
+ Started -- Set true to tell the service controller that we're done starting.
+
+Return Value:
+
+ TRUE -- iff install hint was accepted.
+
+--*/
+{
+
+ //
+ // If we're not installing,
+ // we don't need this install hint.
+ //
+
+ if ( NlGlobalServiceStatus.dwCurrentState != SERVICE_START_PENDING ) {
+ return TRUE;
+ }
+
+
+ //
+ // If we've been asked to exit,
+ // return FALSE immediately asking the caller to exit.
+ //
+
+ if ( NlGlobalTerminate ) {
+ return FALSE;
+ }
+
+
+ //
+ // Tell the service controller our current state.
+ //
+
+ if ( Started ) {
+ NlGlobalServiceStatus.dwCurrentState = SERVICE_RUNNING;
+ NlGlobalServiceStatus.dwCheckPoint = 0;
+ NlGlobalServiceStatus.dwWaitHint = 0;
+ } else {
+ NlGlobalServiceStatus.dwCheckPoint++;
+ }
+
+ if( !SetServiceStatus( NlGlobalServiceHandle, &NlGlobalServiceStatus ) ) {
+ NlExit( NELOG_NetlogonSystemError, GetLastError(), LogErrorAndNetStatus, NULL);
+ return FALSE;
+ }
+
+ return TRUE;
+
+}
+
+
+VOID
+NlControlHandler(
+ IN DWORD opcode
+ )
+/*++
+
+Routine Description:
+
+ Process and respond to a control signal from the service controller.
+
+Arguments:
+
+ opcode - Supplies a value which specifies the action for the Netlogon
+ service to perform.
+
+Return Value:
+
+ None.
+
+--*/
+{
+
+ NlPrint((NL_MISC, "In control handler (Opcode: %ld)\n", opcode ));
+
+ //
+ // Handle an uninstall request.
+ //
+
+ switch (opcode) {
+ case SERVICE_CONTROL_STOP: /* Uninstall required */
+
+ //
+ // Request the service to exit.
+ //
+ // NlExit also sets the service status to UNINSTALL_PENDING
+ // and tells the service controller.
+ //
+
+ NlExit( NERR_Success, 0, DontLogError, NULL);
+ return;
+
+ //
+ // Pause the service.
+ //
+
+ case SERVICE_CONTROL_PAUSE:
+
+ NlGlobalServiceStatus.dwCurrentState = SERVICE_PAUSED;
+ break;
+
+ //
+ // Continute the service.
+ //
+
+ case SERVICE_CONTROL_CONTINUE:
+
+ NlGlobalServiceStatus.dwCurrentState = SERVICE_RUNNING;
+ break;
+
+ //
+ // By default, just return the current status.
+ //
+
+ case SERVICE_CONTROL_INTERROGATE:
+ default:
+ break;
+ }
+
+ //
+ // Always respond with the current status.
+ //
+
+ if( !SetServiceStatus( NlGlobalServiceHandle,
+ &NlGlobalServiceStatus ) ) {
+
+ NlPrint((NL_CRITICAL, "SetServiceStatus error: %lu\n",
+ GetLastError() ));
+ }
+
+ return;
+}
+
+
+VOID
+RaiseAlert(
+ IN DWORD alert_no,
+ IN LPWSTR *string_array
+ )
+/*++
+
+Routine Description:
+
+ Raise NETLOGON specific Admin alerts.
+
+Arguments:
+
+ alert_no - The alert to be raised, text in alertmsg.h
+
+ string_array - array of strings terminated by NULL string.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ NET_API_STATUS NetStatus;
+ LPWSTR *SArray;
+ PCHAR Next;
+ PCHAR End;
+
+ char message[ALERTSZ + sizeof(ADMIN_OTHER_INFO)];
+ PADMIN_OTHER_INFO admin = (PADMIN_OTHER_INFO) message;
+
+ IF_DEBUG( MISC ) {
+ DWORD i;
+
+ NlPrint((NL_CRITICAL,"Alert: %ld ", alert_no ));
+
+ for( SArray = string_array, i = 0; *SArray != NULL; SArray++, i++ ) {
+ NlPrint((NL_CRITICAL,"\"" FORMAT_LPWSTR "\" ", *SArray ));
+ }
+
+ NlPrint((NL_CRITICAL,"\n" ));
+ }
+
+ //
+ // Build the variable data
+ //
+ admin->alrtad_errcode = alert_no;
+ admin->alrtad_numstrings = 0;
+
+ Next = (PCHAR) ALERT_VAR_DATA(admin);
+ End = Next + ALERTSZ;
+
+ //
+ // now take care of (optional) char strings
+ //
+
+ for( SArray = string_array; *SArray != NULL; SArray++ ) {
+ DWORD StringLen;
+
+ StringLen = (wcslen(*SArray) + 1) * sizeof(WCHAR);
+
+ if( Next + StringLen < End ) {
+
+ //
+ // copy next string.
+ //
+
+ RtlCopyMemory(Next, *SArray, StringLen);
+ Next += StringLen;
+ admin->alrtad_numstrings++;
+ }
+ else {
+
+ NlPrint((NL_CRITICAL,"Error raising alert, Can't fit all "
+ "message strings in the alert buffer \n" ));
+
+ return;
+ }
+ }
+
+ //
+ // Call alerter.
+ //
+
+ NetStatus = NetAlertRaiseEx(
+ ALERT_ADMIN_EVENT,
+ message,
+ (DWORD)((PCHAR)Next - (PCHAR)message),
+ SERVICE_NETLOGON );
+
+ if ( NetStatus != NERR_Success ) {
+ NlPrint((NL_CRITICAL,"Error raising alert %lu\n", NetStatus));
+ }
+
+ return;
+}
diff --git a/private/net/svcdlls/logonsrv/server/iniparm.h b/private/net/svcdlls/logonsrv/server/iniparm.h
new file mode 100644
index 000000000..22e197d45
--- /dev/null
+++ b/private/net/svcdlls/logonsrv/server/iniparm.h
@@ -0,0 +1,325 @@
+/*++
+
+Copyright (c) 1987-92 Microsoft Corporation
+
+Module Name:
+
+ iniparm.h
+
+Abstract:
+
+ Initiail values of startup parameters.
+
+Author:
+
+ Ported from Lan Man 2.0
+
+Revision History:
+
+ 21-May-1991 (cliffv)
+ Ported to NT. Converted to NT style.
+ 07-May-1992 JohnRo
+ Use net config helpers for NetLogon.
+
+--*/
+
+
+#ifndef _INIPARM_
+#define _INIPARM_
+
+//
+// Pulse period (in seconds):
+//
+// Defines the typical pulse frequency. All SAM/LSA changes made within this
+// time are collected together. After this time, a pulse is sent to each BDC
+// needing the changes. No pulse is sent to a BDC that is up to date.
+//
+#define DEFAULT_PULSE (5*60) // 5 mins
+#define MAX_PULSE (48*60*60) // 2 days
+#define MIN_PULSE 60 // 1 min
+
+//
+// Pulse concurrency (in number of concurrent mailslot messages).
+//
+// Netlogon sends pulses to individual BDCs. The BDCs respond asking for any
+// database changes. To control the maximum load these responses place on the
+// PDC, the PDC will only have this many pulses "pending" at once. The PDC
+// should be sufficiently powerful to support this many concurrent replication
+// RPC calls.
+//
+// Increasing this number increases the load on the PDC.
+// Decreasing this number increases the time it takes for a domain with a
+// large number of BDC to get a SAM/LSA change.
+
+#define DEFAULT_PULSECONCURRENCY 10
+#define MAX_PULSECONCURRENCY 500
+#define MIN_PULSECONCURRENCY 1
+
+//
+// Maximum pulse period (in seconds):
+//
+// Defines the maximum pulse frequency. Every BDC will be sent at least one
+// pulse at this frequency regardless of whether its database is up to date.
+//
+
+#define DEFAULT_PULSEMAXIMUM (2*60*60) // 2 hours
+#define MAX_PULSEMAXIMUM (48*60*60) // 2 days
+#define MIN_PULSEMAXIMUM 60 // 1 min
+
+//
+// Pulse timeout period (in seconds):
+//
+// When a BDC is sent a pulse, it must respond within this time period. If
+// not, the BDC is considered to be non-responsive. A non-responsive BDC is
+// not counted against the "Pulse Concurrency" limit allowing the PDC to
+// send a pulse to another BDC in the domain.
+//
+// If this number is too large, a domain with a large number of non-responsive
+// BDCs will take a long time to complete a partial replication.
+//
+// If this number is too small, a slow BDC may be falsely accused of being
+// non-responsive. When the BDC finally does respond, it will partial
+// replicate from the PDC unduly increasing the load on the PDC.
+//
+#define DEFAULT_PULSETIMEOUT1 10 // 10 seconds
+#define MAX_PULSETIMEOUT1 (2*60) // 2 min
+#define MIN_PULSETIMEOUT1 1 // 1 second
+
+//
+// Maximum Partial replication timeout (in seconds):
+//
+// Even though a BDC initially responds to a pulse (as described for
+// PULSETIMEOUT1), it must continue making replication progress or the
+// BDC will be considered non-responsive. Each time the BDC calls the PDC,
+// the BDC is given another PULSETIMEOUT2 seconds to be considered responsive.
+//
+// If this number is too large, a slow BDC (or one which has its replication
+// rate artificially governed) will consume one of the PULSECONCURRENCY slots.
+//
+// If this number is too small, the load on the PDC will be unduly increased
+// because of the large number of BDC doing a partial sync.
+//
+// NOTE: This parameter only affect the cases where a BDC cannot retrieve all the
+// changes to the SAM/LSA database in a single RPC call. This will only
+// happen if a large number of changes are made to the database.
+
+#define DEFAULT_PULSETIMEOUT2 (5*60) // 5 minutes
+#define MAX_PULSETIMEOUT2 (1*60*60) // 1 hour
+#define MIN_PULSETIMEOUT2 (1*60) // 1 minute
+
+//
+// BDC random backoff (in seconds):
+//
+// When the BDC receives a pulse, it will back off between zero and RANDOMIZE
+// seconds before calling the PDC. In Lanman and NT 1.0, the pulse was
+// broadcast to all BDCs simultaneously and the BDCs used this mechanism to
+// ensure they didn't overload the PDC. As of NT 1.0A, the pulse is sent
+// to individual BDCs so this parameter should be minimized.
+//
+// This parameter should be smaller than PULSETIMEOUT1.
+//
+// Consider that the time to replicate a SAM/LSA change to all the BDCs in a
+// domain will be greater than:
+//
+// ((RANDOMIZE/2) * NumberOfBdcsInDomain) / PULSECONCURRENCY
+//
+#define DEFAULT_RANDOMIZE 1 // 1 secs
+#define MAX_RANDOMIZE 120 // 2 mins
+#define MIN_RANDOMIZE 0 // 0 secs
+
+//
+// BDC Replication Governor (in percent)
+//
+// If the BDC is connected to the PDC via a slow WAN link, the amount of
+// replication load on the WAN link can be adjusted. Lowering this percentage
+// reduces both the size of the data transferred on each call to the PDC and
+// frequency of those calls. For instance, setting ReplicationGovernor to 50%
+// will use a 64Kb buffer rather than a 128Kb buffer and will only have a
+// replication call outstanding on the net a maximum of 50% of the time.
+//
+// Don't be tempted to set the ReplicationGovernor too low. Otherwise,
+// replication may never complete.
+//
+// A value of 0 will cause netlogon to NEVER replicate. The SAM/LSA database
+// will be allowed to get completely out of sync.
+//
+// This parameter must be set individually on each BDC.
+//
+
+#define DEFAULT_GOVERNOR 100
+#define MAX_GOVERNOR 100
+#define MIN_GOVERNOR 0
+
+//
+// ChangeLogSize (in bytes)
+//
+// This is the size of the Change Log file. Each change to the SAM/LSA database
+// is represented by an entry in the change log. The changelog is maintained
+// as a circular buffer with the oldest entry being overwritten by the newest
+// entry. If a BDC does a partial sync and requests an entry that has been
+// overwritten, the BDC is forced to do a full sync.
+//
+// The minimum (and typical) size of an entry is 32 bytes. Some entries are
+// larger. (e.g., a 64K changelog holds about 2000 changes)
+//
+// This parameter need only be set larger if:
+//
+// a) full syncs are prohibitively expensive, AND
+// b) one or more BDCs are expected to not request a partial sync within 2000
+// changes.
+//
+// For instance, if a BDC dials in nightly to do a partial sync and on some
+// days 4000 changes are made to the SAM/LSA database, this parameter should
+// be set to 128K.
+//
+// This parameter need only be set on the PDC. If a different PDC is promoted,
+// it should be set on that PDC also.
+//
+
+#define DEFAULT_CHANGELOGSIZE (64*1024)
+#define MAX_CHANGELOGSIZE (4*1024*1024)
+#define MIN_CHANGELOGSIZE (64*1024)
+
+//
+// MaximumMailslotMessages (in number of messages)
+//
+// This parameter determines the maximum number of mailslot messages that will
+// be queued to the netlogon service. Even though the Netlogon service is
+// designed to process incoming mailslot messages immediately, the netlogon
+// service can get backed up processing requests.
+//
+// Each mailslot message consumes about 1500 bytes of non-paged pool until it
+// is process. By setting this parameter low, you can govern the maximum
+// amount of non-paged pool that can be consumed.
+//
+// If you set this parameter too low, netlogon may miss important incoming
+// mailslot messages.
+//
+
+#define DEFAULT_MAXIMUMMAILSLOTMESSAGES 500
+#define MAX_MAXIMUMMAILSLOTMESSAGES 0xFFFFFFFF
+#define MIN_MAXIMUMMAILSLOTMESSAGES 1
+
+//
+// MailslotMessageTimeout (in seconds)
+//
+// This parameter specifies the maximum acceptable age of an incoming
+// mailslot message. If netlogon receives a mailslot messages that arrived
+// longer ago than this, it will ignore the message. This allows netlogon
+// to process messages that are more recent. The theory is that the client
+// that originally sent the older mailslot message is no longer waiting for
+// the response so we shouldn't bother sending a response.
+//
+// If you set this parameter too low, netlogon will ignore important incoming
+// mailslot messages.
+//
+// Ideally, netlogon processes each mailslot message in a fraction of a second.
+// This parameter is only significant if the NTAS server is overloaded.
+//
+
+#define DEFAULT_MAILSLOTMESSAGETIMEOUT 10
+#define MAX_MAILSLOTMESSAGETIMEOUT 0xFFFFFFFF
+#define MIN_MAILSLOTMESSAGETIMEOUT 5
+
+//
+// MailslotDuplicateTimeout (in seconds)
+//
+// This parameter specifies the interval over which duplicate incoming
+// mailslot messages will be ignored. Netlogon compares each mailslot
+// message received with the previous mailslot message received. If the
+// previous message was received within this many seconds and the messages
+// are identical, this message will be ignored. The theory is that the
+// duplicate messages are caused by clients sending on multiple transports and
+// that netlogon needs to only reply on one of those transports saving network
+// bandwidth.
+//
+// Set this parameter to zero to disable this feature. You should disable this
+// feature if your network is configured such that this machine can see
+// certain incoming mailslot messages but can't respond to them. For instance,
+// a PDC may be separated from an NT workstation by a bridge/router.
+// The bridge/router might filter outgoing NBF broadcasts, but allow incoming
+// one. As such, netlogon might respond to an NBF mailslot message (only to
+// be filtered out by the bridge/router) and not respond to a subsequent NBT
+// mailslot message. Disabling this feature (or preferably reconfiguring the
+// bridge/router) solves this problem.
+//
+// If you set this parameter too high, netlogon will ignore retry attempts
+// from a client.
+//
+
+#define DEFAULT_MAILSLOTDUPLICATETIMEOUT 2
+#define MAX_MAILSLOTDUPLICATETIMEOUT 5
+#define MIN_MAILSLOTDUPLICATETIMEOUT 0
+
+//
+// ExpectedDialupDelay (in seconds)
+//
+// This parameter specifies the time it takes for a dialup router to dial when
+// sending a message from this client machine to a domain trusted by this client
+// machine. Typically, netlogon assumes a domain controller is reachable in a
+// short (e.g., 15 seconds) time period. Setting ExpectedDialupDelay informs
+// Netlogon to expect an ADDITIONAL delay of the time specified.
+//
+// Currently, netlogon adjusts the following two times based on the
+// ExpectedDialupDelay:
+//
+// 1) When discovering a DC in a trusted domain, Netlogon sends a 3 mailslot
+// messages to the trusted domain at ( 5 + ExpectedDialupDelay/3 ) second
+// intervals Synchronous discoveries will not be timed out for 3 times that
+// interval.
+// 2) An API call over a secure channel to a discovered DC will timeout only
+// after (45 + ExpectedDialupDelay) seconds.
+//
+// This parameter should remain zero unless a dialup router exists between this
+// machine and its trusted domain.
+//
+// If this parameter is set too high, legitimate cases where no DC is available in
+// a trusted domain will take an extraordinary amount of time to detect.
+//
+
+
+#define DEFAULT_EXPECTEDDIALUPDELAY 0
+#define MAX_EXPECTEDDIALUPDELAY (10*60) // 10 minutes
+#define MIN_EXPECTEDDIALUPDELAY 0
+
+//
+// ScavengeInterval (in seconds)
+//
+// This parameter adjusts the interval at which netlogon performs the following
+// scavenging operations:
+//
+// * Checks to see if a password on a secure channel needs to be changed.
+//
+// * Checks to see if a secure channel has been idle for a long time.
+//
+// * On DCs, sends a mailslot message to each trusted domain for a DC hasn't been
+// discovered.
+//
+// * On PDC, attempts to add the <DomainName>[1B] netbios name if it hasn't
+// already been successfully added.
+//
+// None of these operations are critical. 15 minutes is optimal in all but extreme
+// cases. For instance, if a DC is separated from a trusted domain by an
+// expensive (e.g., ISDN) line, this parameter might be adjusted upward to avoid
+// frequent automatic discovery of DCs in a trusted domain.
+//
+
+#define DEFAULT_SCAVENGEINTERVAL (15*60) // 15 minutes
+#define MAX_SCAVENGEINTERVAL (48*60*60) // 2 days
+#define MIN_SCAVENGEINTERVAL 60 // 1 minute
+
+
+//
+// How frequently we scavenge the LogonTable.
+//
+#define LOGON_INTERROGATE_PERIOD (15*60*1000) // make it 15 mins
+
+
+#define DEFAULT_SYNCHRONIZE FALSE
+
+#define DEFAULT_DISABLE_PASSWORD_CHANGE 0
+#define DEFAULT_REFUSE_PASSWORD_CHANGE 0
+
+#define DEFAULT_SCRIPTS TEXT("REPL\\IMPORT\\SCRIPTS")
+
+#endif // _INIPARM_
diff --git a/private/net/svcdlls/logonsrv/server/logonapi.c b/private/net/svcdlls/logonsrv/server/logonapi.c
new file mode 100644
index 000000000..bcc31ada6
--- /dev/null
+++ b/private/net/svcdlls/logonsrv/server/logonapi.c
@@ -0,0 +1,3426 @@
+/*++
+
+Copyright (c) 1987-1991 Microsoft Corporation
+
+Module Name:
+
+ logonapi.c
+
+Abstract:
+
+ Remote Logon API routines.
+
+Author:
+
+ Cliff Van Dyke (cliffv) 28-Jun-1991
+
+Environment:
+
+ User mode only.
+ Contains NT-specific code.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+ Madana - Fixed several bugs.
+
+--*/
+
+//
+// Common include files.
+//
+
+#include <logonsrv.h> // Include files common to entire service
+
+//
+// Include files specific to this .c file
+//
+
+
+#include <accessp.h> // Routines shared with NetUser Apis
+#include <align.h> // ROUND_UP_COUNT ...
+#include <lmaudit.h> // AE_*
+#include <lmerr.h>
+#include <nlsecure.h> // Security Descriptor for APIs
+#include <secobj.h> // NetpAccessCheck
+#include <stddef.h> // offsetof()
+#include <rpcutil.h> // NetpRpcStatusToApiStatus()
+#include <align.h> // ROUND_UP_COUTN ...
+
+
+NET_API_STATUS
+NlEnsureClientIsNamedUser(
+ IN LPWSTR UserName
+ )
+/*++
+
+Routine Description:
+
+ Ensure the client is the named user.
+
+Arguments:
+
+ UserName - name of the user to check.
+
+Return Value:
+
+ NT status code.
+
+--*/
+{
+ NET_API_STATUS NetStatus;
+ RPC_STATUS RpcStatus;
+ NTSTATUS Status;
+ HANDLE TokenHandle = NULL;
+ PTOKEN_USER TokenUserInfo = NULL;
+ ULONG TokenUserInfoSize;
+ ULONG UserId;
+ PSID UserSid;
+ SAMPR_HANDLE UserHandle = NULL;
+
+ //
+ // Get the relative ID of the specified user.
+ //
+
+ Status = NlSamOpenNamedUser( UserName, &UserHandle, &UserId );
+
+ if ( !NT_SUCCESS(Status) ) {
+ NlPrint(( NL_CRITICAL,
+ "NlEnsureClientIsNamedUser: %ws: NlSamOpenNamedUser failed 0x%lx\n",
+ UserName,
+ Status ));
+ NetStatus = NetpNtStatusToApiStatus( Status );
+ goto Cleanup;
+ }
+
+
+ //
+ // Impersonate the client while we check him out.
+ //
+
+ RpcStatus = RpcImpersonateClient( NULL );
+
+ if ( RpcStatus != RPC_S_OK ) {
+ NlPrint(( NL_CRITICAL,
+ "NlEnsureClientIsNamedUser: %ws: RpcImpersonateClient failed 0x%lx\n",
+ UserName,
+ RpcStatus ));
+ NetStatus = NetpRpcStatusToApiStatus( RpcStatus );
+ goto Cleanup;
+ }
+
+ //
+ // Compare the username specified with that in
+ // the impersonation token to ensure the caller isn't bogus.
+ //
+ // Do this by opening the token,
+ // querying the token user info,
+ // and ensuring the returned SID is for this user.
+ //
+
+ Status = NtOpenThreadToken(
+ NtCurrentThread(),
+ TOKEN_QUERY,
+ (BOOLEAN) TRUE, // Use the logon service's security context
+ // to open the token
+ &TokenHandle );
+
+ if ( !NT_SUCCESS( Status )) {
+ NlPrint(( NL_CRITICAL,
+ "NlEnsureClientIsNamedUser: %ws: NtOpenThreadToken failed 0x%lx\n",
+ UserName,
+ Status ));
+ NetStatus = NetpNtStatusToApiStatus( Status );
+ goto Cleanup;
+ }
+
+ //
+ // Get the user's SID for the token.
+ //
+
+ Status = NtQueryInformationToken(
+ TokenHandle,
+ TokenUser,
+ &TokenUserInfo,
+ 0,
+ &TokenUserInfoSize );
+
+ if ( Status != STATUS_BUFFER_TOO_SMALL ) {
+ NlPrint(( NL_CRITICAL,
+ "NlEnsureClientIsNamedUser: %ws: NtOpenQueryInformationThread failed 0x%lx\n",
+ UserName,
+ Status ));
+ NetStatus = NetpNtStatusToApiStatus( Status );
+ goto Cleanup;
+ }
+
+ TokenUserInfo = NetpMemoryAllocate( TokenUserInfoSize );
+
+ if ( TokenUserInfo == NULL ) {
+ NetStatus = ERROR_NOT_ENOUGH_MEMORY;
+ goto Cleanup;
+ }
+
+ Status = NtQueryInformationToken(
+ TokenHandle,
+ TokenUser,
+ TokenUserInfo,
+ TokenUserInfoSize,
+ &TokenUserInfoSize );
+
+ if ( !NT_SUCCESS(Status) ) {
+ NlPrint(( NL_CRITICAL,
+ "NlEnsureClientIsNamedUser: %ws: NtOpenQueryInformationThread (again) failed 0x%lx\n",
+ UserName,
+ Status ));
+ NetStatus = NetpNtStatusToApiStatus( Status );
+ goto Cleanup;
+ }
+
+ UserSid = TokenUserInfo->User.Sid;
+
+
+ //
+ // Ensure the last subauthority matches the UserId
+ //
+
+ if ( UserId !=
+ *RtlSubAuthoritySid( UserSid, (*RtlSubAuthorityCountSid(UserSid))-1 )){
+ NlPrint(( NL_CRITICAL,
+ "NlEnsureClientIsNamedUser: %ws: UserId mismatch 0x%lx\n",
+ UserName,
+ UserId ));
+
+ NlpDumpSid( NL_CRITICAL, UserSid );
+
+ NetStatus = ERROR_ACCESS_DENIED;
+ goto Cleanup;
+ }
+
+ //
+ // Convert the User's sid to a DomainId and ensure it is our domain Id.
+ //
+
+ (*RtlSubAuthorityCountSid(UserSid)) --;
+ if ( !RtlEqualSid( (PSID) NlGlobalDBInfoArray[SAM_DB].DBId, UserSid ) ) {
+ NlPrint(( NL_CRITICAL,
+ "NlEnsureClientIsNamedUser: %ws: DomainId mismatch 0x%lx\n",
+ UserName,
+ UserId ));
+
+ NlpDumpSid( NL_CRITICAL, UserSid );
+ NlpDumpSid( NL_CRITICAL, (PSID) NlGlobalDBInfoArray[SAM_DB].DBId );
+
+ NetStatus = ERROR_ACCESS_DENIED;
+ goto Cleanup;
+ }
+
+ //
+ // Done
+ //
+
+ NetStatus = NERR_Success;
+Cleanup:
+
+ //
+ // Clean up locally used resources.
+ //
+
+ if ( TokenHandle != NULL ) {
+ (VOID) NtClose( TokenHandle );
+ }
+
+ if ( TokenUserInfo != NULL ) {
+ NetpMemoryFree( TokenUserInfo );
+ }
+
+ //
+ // revert to system, so that we can close
+ // the user handle properly.
+ //
+
+ (VOID) RpcRevertToSelf();
+
+ if ( UserHandle != NULL ) {
+ SamrCloseHandle( &UserHandle );
+ }
+
+ return NetStatus;
+}
+
+
+NET_API_STATUS
+NetrLogonUasLogon (
+ IN LPWSTR ServerName,
+ IN LPWSTR UserName,
+ IN LPWSTR Workstation,
+ OUT PNETLOGON_VALIDATION_UAS_INFO *ValidationInformation
+)
+/*++
+
+Routine Description:
+
+ Server side of I_NetLogonUasLogon.
+
+ This function is called by the XACT server when processing a
+ I_NetWkstaUserLogon XACT SMB. This feature allows a UAS client to
+ logon to a SAM domain controller.
+
+Arguments:
+
+ ServerName -- Server to perform this operation on. Must be NULL.
+
+ UserName -- Account name of the user logging on.
+
+ Workstation -- The workstation from which the user is logging on.
+
+ ValidationInformation -- Returns the requested validation
+ information.
+
+
+Return Value:
+
+ NERR_SUCCESS if there was no error. Otherwise, the error code is
+ returned.
+
+
+--*/
+{
+ NET_API_STATUS NetStatus;
+ NTSTATUS Status;
+
+ NETLOGON_INTERACTIVE_INFO LogonInteractive;
+ PNETLOGON_VALIDATION_SAM_INFO SamInfo = NULL;
+
+
+ PNETLOGON_VALIDATION_UAS_INFO usrlog1 = NULL;
+ DWORD ValidationSize;
+ LPWSTR EndOfVariableData;
+ BOOLEAN Authoritative;
+ BOOLEAN BadPasswordCountZeroed;
+
+ LARGE_INTEGER TempTime;
+
+ //
+ // This API is not supported on workstations.
+ //
+
+ if ( NlGlobalRole == RoleMemberWorkstation ) {
+ return ERROR_NOT_SUPPORTED;
+ }
+
+
+ //
+ // This API can only be called locally. (By the XACT server).
+ //
+
+ if ( ServerName != NULL ) {
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ //
+ // Initialization
+ //
+
+ *ValidationInformation = NULL;
+
+
+ //
+ // Perform access validation on the caller.
+ //
+
+ NetStatus = NetpAccessCheck(
+ NlGlobalNetlogonSecurityDescriptor, // Security descriptor
+ NETLOGON_UAS_LOGON_ACCESS, // Desired access
+ &NlGlobalNetlogonInfoMapping ); // Generic mapping
+
+ if ( NetStatus != NERR_Success) {
+
+ NlPrint((NL_CRITICAL,"NetrLogonUasLogon of " FORMAT_LPWSTR " from "
+ FORMAT_LPWSTR " failed NetpAccessCheck\n",
+ UserName, Workstation));
+ NetStatus = ERROR_ACCESS_DENIED;
+ goto Cleanup;
+ }
+
+
+
+ //
+ // Ensure the client is actually the named user.
+ //
+ // The server has already validated the password.
+ // The XACT server has already verified that the workstation name is
+ // correct.
+ //
+
+ NetStatus = NlEnsureClientIsNamedUser( UserName );
+
+ if ( NetStatus != NERR_Success ) {
+ NlPrint((NL_CRITICAL,"NetrLogonUasLogon of " FORMAT_LPWSTR " from "
+ FORMAT_LPWSTR " failed NlEnsureClientIsNamedUser\n",
+ UserName, Workstation));
+ NetStatus = ERROR_ACCESS_DENIED;
+ goto Cleanup;
+ }
+
+
+ //
+ // Validate the user against the local SAM database.
+ //
+
+ RtlInitUnicodeString( &LogonInteractive.Identity.LogonDomainName, NULL );
+ LogonInteractive.Identity.ParameterControl = 0;
+ RtlZeroMemory( &LogonInteractive.Identity.LogonId,
+ sizeof(LogonInteractive.Identity.LogonId) );
+ RtlInitUnicodeString( &LogonInteractive.Identity.UserName, UserName );
+ RtlInitUnicodeString( &LogonInteractive.Identity.Workstation, Workstation );
+
+ Status = MsvSamValidate( NlGlobalDBInfoArray[SAM_DB].DBHandle,
+ NlGlobalUasCompatibilityMode,
+ NullSecureChannel, // Skip password check
+ &NlGlobalUnicodeComputerNameString,
+ &NlGlobalAccountDomainName,
+ NlGlobalDBInfoArray[SAM_DB].DBId,
+ NetlogonInteractiveInformation,
+ &LogonInteractive,
+ NetlogonValidationSamInfo,
+ (PVOID *)&SamInfo,
+ &Authoritative,
+ &BadPasswordCountZeroed,
+ MSVSAM_SPECIFIED );
+
+ if ( !NT_SUCCESS( Status )) {
+ NetStatus = NetpNtStatusToApiStatus( Status );
+ goto Cleanup;
+ }
+
+
+ //
+ // Allocate a return buffer
+ //
+
+ ValidationSize = sizeof( NETLOGON_VALIDATION_UAS_INFO ) +
+ SamInfo->EffectiveName.Length + sizeof(WCHAR) +
+ (wcslen( NlGlobalUncUnicodeComputerName ) +1) * sizeof(WCHAR) +
+ NlGlobalAccountDomainName.Length + sizeof(WCHAR) +
+ SamInfo->LogonScript.Length + sizeof(WCHAR);
+
+ ValidationSize = ROUND_UP_COUNT( ValidationSize, ALIGN_WCHAR );
+
+ usrlog1 = MIDL_user_allocate( ValidationSize );
+
+ if ( usrlog1 == NULL ) {
+ NetStatus = ERROR_NOT_ENOUGH_MEMORY;
+ goto Cleanup;
+ }
+
+ //
+ // Convert the SAM information to the right format for LM 2.0
+ //
+
+ EndOfVariableData = (LPWSTR) (((PCHAR)usrlog1) + ValidationSize);
+
+ if ( !NetpCopyStringToBuffer(
+ SamInfo->EffectiveName.Buffer,
+ SamInfo->EffectiveName.Length / sizeof(WCHAR),
+ (LPBYTE) (usrlog1 + 1),
+ &EndOfVariableData,
+ &usrlog1->usrlog1_eff_name ) ) {
+
+ NetStatus = NERR_InternalError ;
+ goto Cleanup;
+ }
+
+ Status = NlGetUserPriv(
+ SamInfo->GroupCount,
+ (PGROUP_MEMBERSHIP) SamInfo->GroupIds,
+ SamInfo->UserId,
+ &usrlog1->usrlog1_priv,
+ &usrlog1->usrlog1_auth_flags );
+
+ if ( !NT_SUCCESS( Status )) {
+ NetStatus = NetpNtStatusToApiStatus( Status );
+ goto Cleanup;
+ }
+
+ usrlog1->usrlog1_num_logons = 0;
+ usrlog1->usrlog1_bad_pw_count = SamInfo->BadPasswordCount;
+
+ OLD_TO_NEW_LARGE_INTEGER( SamInfo->LogonTime, TempTime);
+
+ if ( !RtlTimeToSecondsSince1970( &TempTime,
+ &usrlog1->usrlog1_last_logon) ) {
+ usrlog1->usrlog1_last_logon = 0;
+ }
+
+ OLD_TO_NEW_LARGE_INTEGER( SamInfo->LogoffTime, TempTime);
+
+ if ( !RtlTimeToSecondsSince1970( &TempTime,
+ &usrlog1->usrlog1_last_logoff) ) {
+ usrlog1->usrlog1_last_logoff = TIMEQ_FOREVER;
+ }
+
+ OLD_TO_NEW_LARGE_INTEGER( SamInfo->KickOffTime, TempTime);
+
+ if ( !RtlTimeToSecondsSince1970( &TempTime,
+ &usrlog1->usrlog1_logoff_time) ) {
+ usrlog1->usrlog1_logoff_time = TIMEQ_FOREVER;
+ }
+
+ if ( !RtlTimeToSecondsSince1970( &TempTime,
+ &usrlog1->usrlog1_kickoff_time) ) {
+ usrlog1->usrlog1_kickoff_time = TIMEQ_FOREVER;
+ }
+
+ OLD_TO_NEW_LARGE_INTEGER( SamInfo->PasswordLastSet, TempTime);
+
+ usrlog1->usrlog1_password_age =
+ NetpGetElapsedSeconds( &TempTime );
+
+ OLD_TO_NEW_LARGE_INTEGER( SamInfo->PasswordCanChange, TempTime);
+
+ if ( !RtlTimeToSecondsSince1970( &TempTime,
+ &usrlog1->usrlog1_pw_can_change) ) {
+ usrlog1->usrlog1_pw_can_change = TIMEQ_FOREVER;
+ }
+
+ OLD_TO_NEW_LARGE_INTEGER( SamInfo->PasswordMustChange, TempTime);
+
+ if ( !RtlTimeToSecondsSince1970( &TempTime,
+ &usrlog1->usrlog1_pw_must_change) ) {
+ usrlog1->usrlog1_pw_must_change = TIMEQ_FOREVER;
+ }
+
+
+ usrlog1->usrlog1_computer = NlGlobalUncUnicodeComputerName;
+ if ( !NetpPackString(
+ &usrlog1->usrlog1_computer,
+ (LPBYTE) (usrlog1 + 1),
+ &EndOfVariableData )) {
+
+ NetStatus = NERR_InternalError ;
+ goto Cleanup;
+ }
+
+ if ( !NetpCopyStringToBuffer(
+ NlGlobalAccountDomainName.Buffer,
+ NlGlobalAccountDomainName.Length / sizeof(WCHAR),
+ (LPBYTE) (usrlog1 + 1),
+ &EndOfVariableData,
+ &usrlog1->usrlog1_domain ) ) {
+
+ NetStatus = NERR_InternalError ;
+ goto Cleanup;
+ }
+
+ if ( !NetpCopyStringToBuffer(
+ SamInfo->LogonScript.Buffer,
+ SamInfo->LogonScript.Length / sizeof(WCHAR),
+ (LPBYTE) (usrlog1 + 1),
+ &EndOfVariableData,
+ &usrlog1->usrlog1_script_path ) ) {
+
+ NetStatus = NERR_InternalError ;
+ goto Cleanup;
+ }
+
+ NetStatus = NERR_Success;
+
+ //
+ // Done
+ //
+
+Cleanup:
+
+ //
+ // Clean up locally used resources.
+ //
+
+ if ( SamInfo != NULL ) {
+ MIDL_user_free( SamInfo );
+ }
+
+ if ( NetStatus != NERR_Success ) {
+ if ( usrlog1 != NULL ) {
+ MIDL_user_free( usrlog1 );
+ usrlog1 = NULL;
+ }
+ }
+
+ NlPrint((NL_LOGON,"NetrLogonUasLogon of " FORMAT_LPWSTR " from "
+ FORMAT_LPWSTR " returns %lu\n",
+ UserName, Workstation, NetStatus ));
+
+ *ValidationInformation = usrlog1;
+
+ return(NetStatus);
+}
+
+
+NET_API_STATUS
+NetrLogonUasLogoff (
+ IN LPWSTR ServerName OPTIONAL,
+ IN LPWSTR UserName,
+ IN LPWSTR Workstation,
+ OUT PNETLOGON_LOGOFF_UAS_INFO LogoffInformation
+)
+/*++
+
+Routine Description:
+
+ This function is called by the XACT server when processing a
+ I_NetWkstaUserLogoff XACT SMB. This feature allows a UAS client to
+ logoff from a SAM domain controller. The request is authenticated,
+ the entry is removed for this user from the logon session table
+ maintained by the Netlogon service for NetLogonEnum, and logoff
+ information is returned to the caller.
+
+ The server portion of I_NetLogonUasLogoff (in the Netlogon service)
+ compares the user name and workstation name specified in the
+ LogonInformation with the user name and workstation name from the
+ impersonation token. If they don't match, I_NetLogonUasLogoff fails
+ indicating the access is denied.
+
+ Group SECURITY_LOCAL is refused access to this function. Membership
+ in SECURITY_LOCAL implies that this call was made locally and not
+ through the XACT server.
+
+ The Netlogon service cannot be sure that this function was called by
+ the XACT server. Therefore, the Netlogon service will not simply
+ delete the entry from the logon session table. Rather, the logon
+ session table entry will be marked invisible outside of the Netlogon
+ service (i.e., it will not be returned by NetLogonEnum) until a valid
+ LOGON_WKSTINFO_RESPONSE is received for the entry. The Netlogon
+ service will immediately interrogate the client (as described above
+ for LOGON_WKSTINFO_RESPONSE) and temporarily increase the
+ interrogation frequency to at least once a minute. The logon session
+ table entry will reappear as soon as a function of interrogation if
+ this isn't a true logoff request.
+
+Arguments:
+
+ ServerName -- Reserved. Must be NULL.
+
+ UserName -- Account name of the user logging off.
+
+ Workstation -- The workstation from which the user is logging
+ off.
+
+ LogoffInformation -- Returns the requested logoff information.
+
+Return Value:
+
+ The Net status code.
+
+--*/
+{
+ NET_API_STATUS NetStatus;
+ NTSTATUS Status;
+
+ NETLOGON_INTERACTIVE_INFO LogonInteractive;
+
+ PNETLOGON_LOGOFF_UAS_INFO usrlog1 = NULL;
+
+ //
+ // This API is not supported on workstations.
+ //
+
+ if ( NlGlobalRole == RoleMemberWorkstation ) {
+ return ERROR_NOT_SUPPORTED;
+ }
+
+
+ //
+ // This API can only be called locally. (By the XACT server).
+ //
+
+ if ( ServerName != NULL ) {
+ return ERROR_INVALID_PARAMETER;
+ }
+
+
+
+ //
+ // Perform access validation on the caller.
+ //
+
+ NetStatus = NetpAccessCheck(
+ NlGlobalNetlogonSecurityDescriptor, // Security descriptor
+ NETLOGON_UAS_LOGOFF_ACCESS, // Desired access
+ &NlGlobalNetlogonInfoMapping ); // Generic mapping
+
+ if ( NetStatus != NERR_Success) {
+ NlPrint((NL_CRITICAL,"NetrLogonUasLogoff of " FORMAT_LPWSTR " from "
+ FORMAT_LPWSTR " failed NetpAccessCheck\n",
+ UserName, Workstation));
+ NetStatus = ERROR_ACCESS_DENIED;
+ goto Cleanup;
+ }
+
+
+
+ //
+ // Ensure the client is actually the named user.
+ //
+ // The server has already validated the password.
+ // The XACT server has already verified that the workstation name is
+ // correct.
+ //
+
+#ifdef notdef // Some clients (WFW 3.11) can call this over the null session
+ NetStatus = NlEnsureClientIsNamedUser( UserName );
+
+ if ( NetStatus != NERR_Success ) {
+ NlPrint((NL_CRITICAL,"NetrLogonUasLogoff of " FORMAT_LPWSTR " from "
+ FORMAT_LPWSTR " failed NlEnsureClientIsNamedUser\n",
+ UserName, Workstation));
+ NetStatus = ERROR_ACCESS_DENIED;
+ goto Cleanup;
+ }
+#endif // notdef
+
+
+ //
+ // Build the LogonInformation to return
+ //
+
+ LogoffInformation->Duration = 0;
+ LogoffInformation->LogonCount = 0;
+
+
+ //
+ // Update the LastLogoff time in the SAM database.
+ //
+
+ RtlInitUnicodeString( &LogonInteractive.Identity.LogonDomainName, NULL );
+ LogonInteractive.Identity.ParameterControl = 0;
+ RtlZeroMemory( &LogonInteractive.Identity.LogonId,
+ sizeof(LogonInteractive.Identity.LogonId) );
+ RtlInitUnicodeString( &LogonInteractive.Identity.UserName, UserName );
+ RtlInitUnicodeString( &LogonInteractive.Identity.Workstation, Workstation );
+
+ Status = MsvSamLogoff(
+ NlGlobalDBInfoArray[SAM_DB].DBHandle,
+ NetlogonInteractiveInformation,
+ &LogonInteractive );
+
+ if (!NT_SUCCESS(Status)) {
+ NetStatus = NetpNtStatusToApiStatus( Status );
+ goto Cleanup;
+ }
+
+ //
+ // Cleanup
+ //
+
+Cleanup:
+
+ //
+ // Clean up locally used resources.
+ //
+
+ NlPrint((NL_LOGON,"NetrLogonUasLogoff of " FORMAT_LPWSTR " from "
+ FORMAT_LPWSTR " returns %lu\n",
+ UserName, Workstation, NetStatus));
+ return NetStatus;
+}
+
+
+VOID
+NlpDecryptLogonInformation (
+ IN NETLOGON_LOGON_INFO_CLASS LogonLevel,
+ IN OUT LPBYTE LogonInformation,
+ IN PSESSION_INFO SessionInfo
+)
+/*++
+
+Routine Description:
+
+ This function decrypts the sensitive information in the LogonInformation
+ structure. The decryption is done in place.
+
+Arguments:
+
+ LogonLevel -- Specifies the level of information given in
+ LogonInformation.
+
+ LogonInformation -- Specifies the description for the user
+ logging on.
+
+ SessionInfo -- The session key to encrypt with and negotiate flags
+
+
+Return Value:
+
+ None.
+
+--*/
+{
+
+ //
+ // Only the interactive and service logon information is encrypted.
+ //
+
+ if ( LogonLevel == NetlogonInteractiveInformation ||
+ LogonLevel == NetlogonServiceInformation ) {
+
+ PNETLOGON_INTERACTIVE_INFO LogonInteractive;
+
+ LogonInteractive =
+ (PNETLOGON_INTERACTIVE_INFO) LogonInformation;
+
+
+ //
+ // If both sides support RC4 encryption,
+ // decrypt both the LM OWF and NT OWF passwords using RC4.
+ //
+
+ if ( SessionInfo->NegotiatedFlags & NETLOGON_SUPPORTS_RC4_ENCRYPTION ) {
+
+ NlDecryptRC4( &LogonInteractive->LmOwfPassword,
+ sizeof(LogonInteractive->LmOwfPassword),
+ SessionInfo );
+
+ NlDecryptRC4( &LogonInteractive->NtOwfPassword,
+ sizeof(LogonInteractive->NtOwfPassword),
+ SessionInfo );
+
+
+ //
+ // If the other side is running NT 1.0,
+ // use the slower DES based encryption.
+ //
+
+ } else {
+
+ NTSTATUS Status;
+ ENCRYPTED_LM_OWF_PASSWORD EncryptedLmOwfPassword;
+ ENCRYPTED_NT_OWF_PASSWORD EncryptedNtOwfPassword;
+
+ //
+ // Decrypt the LM_OWF password.
+ //
+
+ NlAssert( ENCRYPTED_LM_OWF_PASSWORD_LENGTH ==
+ LM_OWF_PASSWORD_LENGTH );
+ NlAssert(LM_OWF_PASSWORD_LENGTH == sizeof(SessionInfo->SessionKey));
+ EncryptedLmOwfPassword =
+ * ((PENCRYPTED_LM_OWF_PASSWORD) &LogonInteractive->LmOwfPassword);
+
+ Status = RtlDecryptLmOwfPwdWithLmOwfPwd(
+ &EncryptedLmOwfPassword,
+ (PLM_OWF_PASSWORD) &SessionInfo->SessionKey,
+ &LogonInteractive->LmOwfPassword );
+ NlAssert( NT_SUCCESS(Status) );
+
+ //
+ // Decrypt the NT_OWF password.
+ //
+
+ NlAssert( ENCRYPTED_NT_OWF_PASSWORD_LENGTH ==
+ NT_OWF_PASSWORD_LENGTH );
+ NlAssert(NT_OWF_PASSWORD_LENGTH == sizeof(SessionInfo->SessionKey));
+ EncryptedNtOwfPassword =
+ * ((PENCRYPTED_NT_OWF_PASSWORD) &LogonInteractive->NtOwfPassword);
+
+ Status = RtlDecryptNtOwfPwdWithNtOwfPwd(
+ &EncryptedNtOwfPassword,
+ (PNT_OWF_PASSWORD) &SessionInfo->SessionKey,
+ &LogonInteractive->NtOwfPassword );
+ NlAssert( NT_SUCCESS(Status) );
+ }
+ }
+
+ return;
+}
+
+
+VOID
+NlpEncryptLogonInformation (
+ IN NETLOGON_LOGON_INFO_CLASS LogonLevel,
+ IN OUT LPBYTE LogonInformation,
+ IN PSESSION_INFO SessionInfo
+)
+/*++
+
+Routine Description:
+
+ This function encrypts the sensitive information in the LogonInformation
+ structure. The encryption is done in place.
+
+Arguments:
+
+ LogonLevel -- Specifies the level of information given in
+ LogonInformation.
+
+ LogonInformation -- Specifies the description for the user
+ logging on.
+
+ SessionInfo -- The session key to encrypt with and negotiate flags
+
+
+Return Value:
+
+ None.
+
+--*/
+{
+ NTSTATUS Status;
+
+
+ //
+ // Only the interactive and service logon information is encrypted.
+ //
+
+ if ( LogonLevel == NetlogonInteractiveInformation ||
+ LogonLevel == NetlogonServiceInformation ) {
+
+ PNETLOGON_INTERACTIVE_INFO LogonInteractive;
+
+ LogonInteractive =
+ (PNETLOGON_INTERACTIVE_INFO) LogonInformation;
+
+
+ //
+ // If both sides support RC4 encryption, use it.
+ // encrypt both the LM OWF and NT OWF passwords using RC4.
+ //
+
+ if ( SessionInfo->NegotiatedFlags & NETLOGON_SUPPORTS_RC4_ENCRYPTION ) {
+
+ NlEncryptRC4( &LogonInteractive->LmOwfPassword,
+ sizeof(LogonInteractive->LmOwfPassword),
+ SessionInfo );
+
+ NlEncryptRC4( &LogonInteractive->NtOwfPassword,
+ sizeof(LogonInteractive->NtOwfPassword),
+ SessionInfo );
+
+
+ //
+ // If the other side is running NT 1.0,
+ // use the slower DES based encryption.
+ //
+
+ } else {
+ ENCRYPTED_LM_OWF_PASSWORD EncryptedLmOwfPassword;
+ ENCRYPTED_NT_OWF_PASSWORD EncryptedNtOwfPassword;
+
+ //
+ // Encrypt the LM_OWF password.
+ //
+
+ NlAssert( ENCRYPTED_LM_OWF_PASSWORD_LENGTH ==
+ LM_OWF_PASSWORD_LENGTH );
+ NlAssert(LM_OWF_PASSWORD_LENGTH == sizeof(SessionInfo->SessionKey));
+
+ Status = RtlEncryptLmOwfPwdWithLmOwfPwd(
+ &LogonInteractive->LmOwfPassword,
+ (PLM_OWF_PASSWORD) &SessionInfo->SessionKey,
+ &EncryptedLmOwfPassword );
+
+ NlAssert( NT_SUCCESS(Status) );
+
+ *((PENCRYPTED_LM_OWF_PASSWORD) &LogonInteractive->LmOwfPassword) =
+ EncryptedLmOwfPassword;
+
+ //
+ // Encrypt the NT_OWF password.
+ //
+
+ NlAssert( ENCRYPTED_NT_OWF_PASSWORD_LENGTH ==
+ NT_OWF_PASSWORD_LENGTH );
+ NlAssert(NT_OWF_PASSWORD_LENGTH == sizeof(SessionInfo->SessionKey));
+
+ Status = RtlEncryptNtOwfPwdWithNtOwfPwd(
+ &LogonInteractive->NtOwfPassword,
+ (PNT_OWF_PASSWORD) &SessionInfo->SessionKey,
+ &EncryptedNtOwfPassword );
+
+ NlAssert( NT_SUCCESS(Status) );
+
+ *((PENCRYPTED_NT_OWF_PASSWORD) &LogonInteractive->NtOwfPassword) =
+ EncryptedNtOwfPassword;
+ }
+ }
+
+ return;
+
+}
+
+
+
+VOID
+NlpDecryptValidationInformation (
+ IN NETLOGON_LOGON_INFO_CLASS LogonLevel,
+ IN NETLOGON_VALIDATION_INFO_CLASS ValidationLevel,
+ IN OUT LPBYTE ValidationInformation,
+ IN PSESSION_INFO SessionInfo
+)
+/*++
+
+Routine Description:
+
+ This function decrypts the sensitive information in the
+ ValidationInformation structure. The decryption is done in place.
+
+Arguments:
+
+ LogonLevel -- Specifies the Logon level used to obtain
+ ValidationInformation.
+
+ ValidationLevel -- Specifies the level of information given in
+ ValidationInformation.
+
+ ValidationInformation -- Specifies the description for the user
+ logging on.
+
+ SessionInfo -- The session key to encrypt with and negotiated flags.
+
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PNETLOGON_VALIDATION_SAM_INFO ValidationInfo;
+
+ //
+ // Check the validation level.
+ //
+
+ if ( (ValidationLevel != NetlogonValidationSamInfo) &&
+ (ValidationLevel != NetlogonValidationSamInfo2) ) {
+ return;
+ }
+
+ //
+ // Only network logons contain information which is sensitive.
+ //
+
+ if ( LogonLevel != NetlogonNetworkInformation ) {
+ return;
+ }
+
+ ValidationInfo = (PNETLOGON_VALIDATION_SAM_INFO) ValidationInformation;
+
+
+
+ //
+ // If we're suppossed to use RC4,
+ // Decrypt both the NT and LM session keys using RC4.
+ //
+
+ if ( SessionInfo->NegotiatedFlags & NETLOGON_SUPPORTS_RC4_ENCRYPTION ) {
+
+ NlDecryptRC4( &ValidationInfo->UserSessionKey,
+ sizeof(ValidationInfo->UserSessionKey),
+ SessionInfo );
+
+ NlDecryptRC4( &ValidationInfo->ExpansionRoom[SAMINFO_LM_SESSION_KEY],
+ SAMINFO_LM_SESSION_KEY_SIZE,
+ SessionInfo );
+
+ //
+ // If the other side is running NT1.0,
+ // be compatible.
+ //
+ } else {
+
+ NTSTATUS Status;
+ CLEAR_BLOCK ClearBlock;
+ DWORD i;
+ LPBYTE DataBuffer =
+ (LPBYTE) &ValidationInfo->ExpansionRoom[SAMINFO_LM_SESSION_KEY];
+
+ //
+ // Decrypt the LmSessionKey
+ //
+
+ NlAssert( CLEAR_BLOCK_LENGTH == CYPHER_BLOCK_LENGTH );
+ NlAssert( (SAMINFO_LM_SESSION_KEY_SIZE % CLEAR_BLOCK_LENGTH) == 0 );
+
+ //
+ // Loop decrypting a block at a time
+ //
+
+ for (i=0; i<SAMINFO_LM_SESSION_KEY_SIZE/CLEAR_BLOCK_LENGTH; i++ ) {
+ Status = RtlDecryptBlock(
+ (PCYPHER_BLOCK)DataBuffer,
+ (PBLOCK_KEY)&SessionInfo->SessionKey,
+ &ClearBlock );
+ NlAssert( NT_SUCCESS( Status ) );
+
+ //
+ // Copy the clear text back into the original buffer.
+ //
+
+ RtlCopyMemory( DataBuffer, &ClearBlock, CLEAR_BLOCK_LENGTH );
+ DataBuffer += CLEAR_BLOCK_LENGTH;
+ }
+
+ }
+
+
+ return;
+}
+
+
+VOID
+NlpEncryptValidationInformation (
+ IN NETLOGON_LOGON_INFO_CLASS LogonLevel,
+ IN NETLOGON_VALIDATION_INFO_CLASS ValidationLevel,
+ IN OUT LPBYTE ValidationInformation,
+ IN PSESSION_INFO SessionInfo
+)
+/*++
+
+Routine Description:
+
+ This function encrypts the sensitive information in the
+ ValidationInformation structure. The encryption is done in place.
+
+Arguments:
+
+ LogonLevel -- Specifies the Logon level used to obtain
+ ValidationInformation.
+
+ ValidationLevel -- Specifies the level of information given in
+ ValidationInformation.
+
+ ValidationInformation -- Specifies the description for the user
+ logging on.
+
+ SessionInfo -- The session key to encrypt with and negotiated flags.
+
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PNETLOGON_VALIDATION_SAM_INFO ValidationInfo;
+
+ //
+ // Check the validation level.
+ //
+
+ if ( (ValidationLevel != NetlogonValidationSamInfo) &&
+ (ValidationLevel != NetlogonValidationSamInfo2) ) {
+ return;
+ }
+
+ //
+ // Only network logons contain information which is sensitive.
+ //
+
+ if ( LogonLevel != NetlogonNetworkInformation ) {
+ return;
+ }
+
+ ValidationInfo = (PNETLOGON_VALIDATION_SAM_INFO) ValidationInformation;
+
+
+ //
+ // If we're suppossed to use RC4,
+ // Encrypt both the NT and LM session keys using RC4.
+ //
+
+ if ( SessionInfo->NegotiatedFlags & NETLOGON_SUPPORTS_RC4_ENCRYPTION ) {
+
+ NlEncryptRC4( &ValidationInfo->UserSessionKey,
+ sizeof(ValidationInfo->UserSessionKey),
+ SessionInfo );
+
+ NlEncryptRC4( &ValidationInfo->ExpansionRoom[SAMINFO_LM_SESSION_KEY],
+ SAMINFO_LM_SESSION_KEY_SIZE,
+ SessionInfo );
+
+ //
+ // If the other side is running NT1.0,
+ // be compatible.
+ //
+ } else {
+
+ NTSTATUS Status;
+ CLEAR_BLOCK ClearBlock;
+ DWORD i;
+ LPBYTE DataBuffer =
+ (LPBYTE) &ValidationInfo->ExpansionRoom[SAMINFO_LM_SESSION_KEY];
+
+
+ //
+ // Encrypt the LmSessionKey
+ //
+ // Loop decrypting a block at a time
+ //
+
+ for (i=0; i<SAMINFO_LM_SESSION_KEY_SIZE/CLEAR_BLOCK_LENGTH; i++ ) {
+
+ //
+ // Copy the clear text onto the stack
+ //
+
+ RtlCopyMemory( &ClearBlock, DataBuffer, CLEAR_BLOCK_LENGTH );
+
+ Status = RtlEncryptBlock(
+ &ClearBlock,
+ (PBLOCK_KEY)&SessionInfo->SessionKey,
+ (PCYPHER_BLOCK)DataBuffer );
+
+ NlAssert( NT_SUCCESS( Status ) );
+
+ DataBuffer += CLEAR_BLOCK_LENGTH;
+ }
+
+ }
+
+ return;
+
+}
+
+
+
+
+NTSTATUS
+NlpConvertSamInfoToSamInfo2 (
+ IN OUT LPBYTE * ValidationInformation
+)
+/*++
+
+Routine Description:
+
+ This function converts a NETLOGON_VALIDATION_SAM_INFO from a NT1.0 server
+ into a NETLOGON_VALIDATION_SAM_INFO2. This is necessary because it
+ is not possible to tell RPC what kind of structure is being returned.
+
+Arguments:
+
+
+ ValidationInformation -- Specifies the NETLOGON_VALIDATION_SAM_INFO
+ to convert.
+ logging on.
+
+ SessionInfo -- The session key to encrypt with and negotiated flags.
+
+Return Value:
+
+ STATUS_INSUFFICIENT_RESOURCES: not enough memory to allocate the new
+ structure.
+
+--*/
+{
+ ULONG Length;
+ PNETLOGON_VALIDATION_SAM_INFO SamInfo = (PNETLOGON_VALIDATION_SAM_INFO) *ValidationInformation;
+ PNETLOGON_VALIDATION_SAM_INFO2 SamInfo2;
+ PBYTE Where;
+
+ //
+ // Calculate the size of the new structure
+ //
+
+ Length = sizeof( NETLOGON_VALIDATION_SAM_INFO2 )
+ + SamInfo->GroupCount * sizeof(GROUP_MEMBERSHIP)
+ + RtlLengthSid( SamInfo->LogonDomainId );
+
+ //
+ // Round up now to take into account the round up in the
+ // middle of marshalling
+ //
+
+ Length = ROUND_UP_COUNT(Length, sizeof(WCHAR))
+ + SamInfo->LogonDomainName.Length + sizeof(WCHAR)
+ + SamInfo->LogonServer.Length + sizeof(WCHAR)
+ + SamInfo->EffectiveName.Length + sizeof(WCHAR)
+ + SamInfo->FullName.Length + sizeof(WCHAR)
+ + SamInfo->LogonScript.Length + sizeof(WCHAR)
+ + SamInfo->ProfilePath.Length + sizeof(WCHAR)
+ + SamInfo->HomeDirectory.Length + sizeof(WCHAR)
+ + SamInfo->HomeDirectoryDrive.Length + sizeof(WCHAR);
+
+
+ Length = ROUND_UP_COUNT( Length, sizeof(WCHAR) );
+
+ SamInfo2 = (PNETLOGON_VALIDATION_SAM_INFO2) MIDL_user_allocate( Length );
+
+ if ( !SamInfo2 ) {
+ *ValidationInformation = NULL;
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ //
+ // First copy the whole structure, since most parts are the same
+ //
+
+ RtlCopyMemory(SamInfo2,SamInfo,sizeof(NETLOGON_VALIDATION_SAM_INFO));
+
+ SamInfo2->SidCount = 0;
+ SamInfo2->ExtraSids = NULL;
+
+ //
+ // Copy all the variable length data
+ //
+
+ Where = (PBYTE) (SamInfo2 + 1);
+
+ RtlCopyMemory(
+ Where,
+ SamInfo->GroupIds,
+ SamInfo->GroupCount * sizeof( GROUP_MEMBERSHIP) );
+
+ SamInfo2->GroupIds = (PGROUP_MEMBERSHIP) Where;
+ Where += SamInfo->GroupCount * sizeof( GROUP_MEMBERSHIP );
+
+ RtlCopyMemory(
+ Where,
+ SamInfo->LogonDomainId,
+ RtlLengthSid( SamInfo->LogonDomainId ) );
+
+ SamInfo2->LogonDomainId = (PSID) Where;
+ Where += RtlLengthSid( SamInfo->LogonDomainId );
+
+ //
+ // Copy the WCHAR-aligned data
+ //
+ Where = ROUND_UP_POINTER(Where, sizeof(WCHAR) );
+
+ NlpPutString( &SamInfo2->EffectiveName,
+ &SamInfo->EffectiveName,
+ &Where );
+
+ NlpPutString( &SamInfo2->FullName,
+ &SamInfo->FullName,
+ &Where );
+
+ NlpPutString( &SamInfo2->LogonScript,
+ &SamInfo->LogonScript,
+ &Where );
+
+ NlpPutString( &SamInfo2->ProfilePath,
+ &SamInfo->ProfilePath,
+ &Where );
+
+ NlpPutString( &SamInfo2->HomeDirectory,
+ &SamInfo->HomeDirectory,
+ &Where );
+
+ NlpPutString( &SamInfo2->HomeDirectoryDrive,
+ &SamInfo->HomeDirectoryDrive,
+ &Where );
+
+ NlpPutString( &SamInfo2->LogonServer,
+ &SamInfo->LogonServer,
+ &Where );
+
+ NlpPutString( &SamInfo2->LogonDomainName,
+ &SamInfo->LogonDomainName,
+ &Where );
+
+
+
+ MIDL_user_free(SamInfo);
+
+ *ValidationInformation = (LPBYTE) SamInfo2;
+
+ return STATUS_SUCCESS;
+
+}
+
+
+NTSTATUS
+NlpUserValidateHigher (
+ IN PCLIENT_SESSION ClientSession,
+ IN NETLOGON_LOGON_INFO_CLASS LogonLevel,
+ IN LPBYTE LogonInformation,
+ IN NETLOGON_VALIDATION_INFO_CLASS ValidationLevel,
+ OUT LPBYTE * ValidationInformation,
+ OUT PBOOLEAN Authoritative
+)
+/*++
+
+Routine Description:
+
+ This function sends a user validation request to a higher authority.
+
+Arguments:
+
+ ClientSession -- Secure channel to send this request over. The Client
+ Session should be referenced.
+
+ LogonLevel -- Specifies the level of information given in
+ LogonInformation. Has already been validated.
+
+ LogonInformation -- Specifies the description for the user
+ logging on.
+
+ ValidationLevel -- Specifies the level of information returned in
+ ValidationInformation. Must be NetlogonValidationSamInfo or
+ NetlogonValidationSamInfo2.
+
+ ValidationInformation -- Returns the requested validation
+ information. This buffer must be freed using MIDL_user_free.
+
+ Authoritative -- Returns whether the status returned is an
+ authoritative status which should be returned to the original
+ caller. If not, this logon request may be tried again on another
+ domain controller. This parameter is returned regardless of the
+ status code.
+
+Return Value:
+
+ STATUS_SUCCESS: if there was no error.
+
+ STATUS_NO_LOGON_SERVERS: cannot connect to the higher authority.
+
+ STATUS_NO_TRUST_LSA_SECRET:
+ STATUS_TRUSTED_DOMAIN_FAILURE:
+ STATUS_TRUSTED_RELATIONSHIP_FAILURE:
+ can't authenticate with higer authority
+
+ Otherwise, the error code is returned.
+
+
+--*/
+{
+ NTSTATUS Status;
+ NETLOGON_AUTHENTICATOR OurAuthenticator;
+ NETLOGON_AUTHENTICATOR ReturnAuthenticator;
+ BOOLEAN FirstTry = TRUE;
+ SESSION_INFO SessionInfo;
+ NETLOGON_VALIDATION_INFO_CLASS RemoteValidationLevel;
+
+ //
+ // Mark us as a writer of the ClientSession
+ //
+
+ NlAssert( ClientSession->CsReferenceCount > 0 );
+ if ( !NlTimeoutSetWriterClientSession( ClientSession, WRITER_WAIT_PERIOD ) ) {
+ NlPrint((NL_CRITICAL, "NlpUserValidateHigher: Can't become writer of client session.\n" ));
+ *Authoritative = TRUE;
+ return STATUS_NO_LOGON_SERVERS;
+ }
+
+ //
+ // If we don't currently have a session set up to the higher authority,
+ // set one up.
+ //
+
+FirstTryFailed:
+ if ( ClientSession->CsState != CS_AUTHENTICATED ) {
+
+ //
+ // If we've tried to authenticate recently,
+ // don't bother trying again.
+ //
+
+ if ( !NlTimeToReauthenticate( ClientSession ) ) {
+ Status = ClientSession->CsConnectionStatus;
+ NlAssert( !NT_SUCCESS(Status) );
+ NlAssert( !NT_SUCCESS(Status) || Status == STATUS_SUCCESS );
+ NlAssert( !NT_SUCCESS(Status) || *ValidationInformation != NULL );
+ *Authoritative = TRUE;
+ goto Cleanup;
+
+ }
+
+ //
+ // Try to set up the session.
+ //
+
+ Status = NlSessionSetup( ClientSession );
+
+ if ( !NT_SUCCESS(Status) ) {
+
+ switch(Status) {
+
+ case STATUS_NO_TRUST_LSA_SECRET:
+ case STATUS_NO_TRUST_SAM_ACCOUNT:
+ case STATUS_ACCESS_DENIED:
+ case STATUS_NO_LOGON_SERVERS:
+ break;
+
+ default:
+ Status = STATUS_NO_LOGON_SERVERS;
+ break;
+ }
+
+ NlAssert( !NT_SUCCESS(Status) || Status == STATUS_SUCCESS );
+ NlAssert( !NT_SUCCESS(Status) || *ValidationInformation != NULL );
+ *Authoritative = TRUE;
+ goto Cleanup;
+ }
+ }
+
+ SessionInfo.SessionKey = ClientSession->CsSessionKey;
+ SessionInfo.NegotiatedFlags = ClientSession->CsNegotiatedFlags;
+
+ //
+ // If we are talking to a DC that doesn't support returning multiple
+ // SIDs, make sure to only ask for NetlogonValidationSamInfo
+ //
+
+ if (!(SessionInfo.NegotiatedFlags & NETLOGON_SUPPORTS_MULTIPLE_SIDS)) {
+ RemoteValidationLevel = NetlogonValidationSamInfo;
+ } else {
+ RemoteValidationLevel = ValidationLevel;
+ }
+ //
+ // Build the Authenticator for this request on the secure channel
+ //
+
+ NlBuildAuthenticator(
+ &ClientSession->CsAuthenticationSeed,
+ &ClientSession->CsSessionKey,
+ &OurAuthenticator );
+
+
+ //
+ // Make the request across the secure channel.
+ //
+
+ NlpEncryptLogonInformation( LogonLevel, LogonInformation, &SessionInfo );
+
+ Status = NlStartApiClientSession( ClientSession, TRUE );
+
+ if ( NT_SUCCESS(Status) ) {
+ Status = I_NetLogonSamLogon(
+ ClientSession->CsUncServerName,
+ NlGlobalUnicodeComputerName,
+ &OurAuthenticator,
+ &ReturnAuthenticator,
+ LogonLevel,
+ LogonInformation,
+ RemoteValidationLevel,
+ ValidationInformation,
+ Authoritative );
+ NlAssert( !NT_SUCCESS(Status) || Status == STATUS_SUCCESS );
+ NlAssert( !NT_SUCCESS(Status) || *ValidationInformation != NULL );
+ }
+
+ // NOTE: This call may drop the secure channel behind our back
+ (VOID) NlFinishApiClientSession( ClientSession, TRUE );
+
+ NlpDecryptLogonInformation( LogonLevel, LogonInformation, &SessionInfo );
+
+ if ( NT_SUCCESS(Status) ) {
+ NlAssert( *ValidationInformation != NULL );
+ }
+
+
+ //
+ // Verify authenticator of the server on the other side and update our seed.
+ //
+ // If the server denied access or the server's authenticator is wrong,
+ // Force a re-authentication.
+ //
+ //
+
+#ifdef BAD_ALIGNMENT
+ NlPrint((NL_CHALLENGE_RES,"NlpUserValidateHigher: Seed = %lx %lx\n",
+ ((DWORD *) (&ClientSession->CsAuthenticationSeed))[0],
+ ((DWORD *) (&ClientSession->CsAuthenticationSeed))[1]));
+
+
+ NlPrint((NL_CHALLENGE_RES,"NlpUserValidateHigher: SessionKey = %lx %lx\n",
+ ((DWORD *) (&ClientSession->CsSessionKey))[0],
+ ((DWORD *) (&ClientSession->CsSessionKey))[1]));
+
+ NlPrint((NL_CHALLENGE_RES,"NlpUserValidateHigher: Return Authenticator = %lx %lx\n",
+ ((DWORD *) (&ReturnAuthenticator.Credential))[0],
+ ((DWORD *) (&ReturnAuthenticator.Credential))[1]));
+#endif // BAD_ALIGNMENT
+
+ if ( Status == STATUS_ACCESS_DENIED ||
+ !NlUpdateSeed(
+ &ClientSession->CsAuthenticationSeed,
+ &ReturnAuthenticator.Credential,
+ &ClientSession->CsSessionKey) ) {
+
+
+ Status = STATUS_ACCESS_DENIED;
+ NlSetStatusClientSession( ClientSession, Status );
+
+ //
+ // Perhaps the netlogon service on the server has just restarted.
+ // Try just once to set up a session to the server again.
+ //
+ if ( FirstTry ) {
+ FirstTry = FALSE;
+ goto FirstTryFailed;
+ }
+
+ *Authoritative = TRUE;
+ NlAssert( !NT_SUCCESS(Status) || Status == STATUS_SUCCESS );
+ NlAssert( !NT_SUCCESS(Status) || *ValidationInformation != NULL );
+ goto Cleanup;
+ }
+
+ //
+ // Clean up after a successful call to higher authority.
+ //
+
+ if ( NT_SUCCESS(Status) ) {
+ PNETLOGON_VALIDATION_SAM_INFO2 ValidationInfo;
+
+
+ //
+ // The server encrypted the validation information before sending it
+ // over the wire. Decrypt it.
+ //
+
+ NlpDecryptValidationInformation (
+ LogonLevel,
+ RemoteValidationLevel,
+ *ValidationInformation,
+ &SessionInfo );
+
+
+ //
+ // If the returned data was a VALIDATION_SAM_INFO and the caller
+ // wanted a VALIDATION_SAM_INFO2 convert it.
+ //
+
+ if ( RemoteValidationLevel != ValidationLevel) {
+
+ NlAssert( ValidationLevel == NetlogonValidationSamInfo2 );
+ NlAssert( RemoteValidationLevel == NetlogonValidationSamInfo );
+
+ if (!NT_SUCCESS( NlpConvertSamInfoToSamInfo2( ValidationInformation ) ) ) {
+ *ValidationInformation = NULL;
+ *Authoritative = FALSE;
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto Cleanup;
+ }
+ }
+
+ //
+ // Ensure the returned SID and domain name are correct.
+ //
+
+ ValidationInfo =
+ (PNETLOGON_VALIDATION_SAM_INFO2) *ValidationInformation;
+
+ //
+ // If we validated on a trusted domain,
+ // the higher authority must have returned his own domain name,
+ // and must have returned his own domain sid.
+ //
+
+ if ( ClientSession->CsSecureChannelType == TrustedDomainSecureChannel ){
+
+ if ( !RtlEqualDomainName( &ValidationInfo->LogonDomainName,
+ &ClientSession->CsDomainName ) ||
+ !RtlEqualSid( ValidationInfo->LogonDomainId,
+ ClientSession->CsDomainId ) ) {
+
+ Status = STATUS_DOMAIN_TRUST_INCONSISTENT;
+ MIDL_user_free( *ValidationInformation );
+ *ValidationInformation = NULL;
+ *Authoritative = TRUE;
+ NlAssert( !NT_SUCCESS(Status) || Status == STATUS_SUCCESS );
+ NlAssert( !NT_SUCCESS(Status) || *ValidationInformation != NULL );
+ }
+
+ //
+ // If we validated on our primary domain,
+ // only verify the domain sid if the primary domain itself validated
+ // the logon.
+ //
+
+ } else if ( ClientSession->CsSecureChannelType ==
+ WorkstationSecureChannel ){
+
+ if ( RtlEqualDomainName( &ValidationInfo->LogonDomainName,
+ &ClientSession->CsDomainName ) &&
+ !RtlEqualSid( ValidationInfo->LogonDomainId,
+ ClientSession->CsDomainId ) ) {
+
+ Status = STATUS_DOMAIN_TRUST_INCONSISTENT;
+ MIDL_user_free( *ValidationInformation );
+ *ValidationInformation = NULL;
+ *Authoritative = TRUE;
+ NlAssert( !NT_SUCCESS(Status) || Status == STATUS_SUCCESS );
+ NlAssert( !NT_SUCCESS(Status) || *ValidationInformation != NULL );
+ }
+ }
+ }
+
+Cleanup:
+ NlAssert( !NT_SUCCESS(Status) || Status == STATUS_SUCCESS );
+ NlAssert( !NT_SUCCESS(Status) || *ValidationInformation != NULL );
+
+ //
+ // We are no longer a writer of the client session.
+ //
+ NlResetWriterClientSession( ClientSession );
+ return Status;
+
+}
+
+
+NTSTATUS
+NlpUserLogoffHigher (
+ IN PCLIENT_SESSION ClientSession,
+ IN NETLOGON_LOGON_INFO_CLASS LogonLevel,
+ IN LPBYTE LogonInformation
+)
+/*++
+
+Routine Description:
+
+ This function sends a user validation request to a higher authority.
+
+Arguments:
+
+ ClientSession -- Secure channel to send this request over. The Client
+ Session should be referenced.
+
+ LogonLevel -- Specifies the level of information given in
+ LogonInformation. Has already been validated.
+
+ LogonInformation -- Specifies the description for the user
+ logging on.
+
+Return Value:
+
+ STATUS_SUCCESS: if there was no error.
+ STATUS_NO_LOGON_SERVERS: cannot connect to the higher authority.
+
+ STATUS_NO_TRUST_LSA_SECRET:
+ STATUS_TRUSTED_DOMAIN_FAILURE:
+ STATUS_TRUSTED_RELATIONSHIP_FAILURE:
+ can't authenticate with higer authority
+
+ Otherwise, the error code is returned.
+
+
+--*/
+{
+ NTSTATUS Status;
+ NETLOGON_AUTHENTICATOR OurAuthenticator;
+ NETLOGON_AUTHENTICATOR ReturnAuthenticator;
+ BOOLEAN FirstTry = TRUE;
+
+ //
+ // Mark us as a writer of the ClientSession
+ //
+
+ NlAssert( ClientSession->CsReferenceCount > 0 );
+ if ( !NlTimeoutSetWriterClientSession( ClientSession, WRITER_WAIT_PERIOD ) ) {
+ NlPrint((NL_CRITICAL, "NlpUserLogoffHigher: Can't become writer of client session.\n" ));
+ return STATUS_NO_LOGON_SERVERS;
+ }
+
+ //
+ // If we don't currently have a session set up to the higher authority,
+ // set one up.
+ //
+
+FirstTryFailed:
+ if ( ClientSession->CsState != CS_AUTHENTICATED ) {
+
+ //
+ // If we've tried to authenticate recently,
+ // don't bother trying again.
+ //
+
+ if ( !NlTimeToReauthenticate( ClientSession ) ) {
+ Status = ClientSession->CsConnectionStatus;
+ goto Cleanup;
+
+ }
+
+ Status = NlSessionSetup( ClientSession );
+
+ if ( !NT_SUCCESS(Status) ) {
+
+ switch(Status) {
+
+ case STATUS_NO_TRUST_LSA_SECRET:
+ case STATUS_NO_TRUST_SAM_ACCOUNT:
+ case STATUS_ACCESS_DENIED:
+ case STATUS_NO_LOGON_SERVERS:
+ break;
+
+ default:
+ Status = STATUS_NO_LOGON_SERVERS;
+ break;
+ }
+
+ goto Cleanup;
+ }
+ }
+
+ //
+ // Build the Authenticator for this request on the secure channel
+ //
+
+ NlBuildAuthenticator(
+ &ClientSession->CsAuthenticationSeed,
+ &ClientSession->CsSessionKey,
+ &OurAuthenticator );
+
+ //
+ // Make the request across the secure channel.
+ //
+
+ Status = NlStartApiClientSession( ClientSession, TRUE );
+
+ if ( NT_SUCCESS(Status) ) {
+ Status = I_NetLogonSamLogoff(
+ ClientSession->CsUncServerName,
+ NlGlobalUnicodeComputerName,
+ &OurAuthenticator,
+ &ReturnAuthenticator,
+ LogonLevel,
+ LogonInformation );
+ }
+
+ // NOTE: This call may drop the secure channel behind our back
+ (VOID) NlFinishApiClientSession( ClientSession, TRUE );
+
+
+ //
+ // Verify authenticator of the server on the other side and update our seed.
+ //
+ // If the server denied access or the server's authenticator is wrong,
+ // Force a re-authentication.
+ //
+ //
+
+ if ( Status == STATUS_ACCESS_DENIED ||
+ !NlUpdateSeed(
+ &ClientSession->CsAuthenticationSeed,
+ &ReturnAuthenticator.Credential,
+ &ClientSession->CsSessionKey) ) {
+
+ Status = STATUS_ACCESS_DENIED;
+ NlSetStatusClientSession( ClientSession, Status );
+
+ //
+ // Perhaps the netlogon service in the server has just restarted.
+ // Try just once to set up a session to the server again.
+ //
+ if ( FirstTry ) {
+ FirstTry = FALSE;
+ goto FirstTryFailed;
+ }
+ goto Cleanup;
+ }
+
+Cleanup:
+
+ //
+ // We are no longer a writer of the client session.
+ //
+ NlResetWriterClientSession( ClientSession );
+ return Status;
+
+}
+
+
+NTSTATUS
+NlpUserValidateOnPdc (
+ IN NETLOGON_LOGON_INFO_CLASS LogonLevel,
+ IN LPBYTE LogonInformation,
+ IN NETLOGON_VALIDATION_INFO_CLASS ValidationLevel,
+ OUT LPBYTE * ValidationInformation,
+ OUT PBOOLEAN Authoritative
+)
+/*++
+
+Routine Description:
+
+ This function sends a user validation request to the PDC in this same
+ domain. Currently, this is called from a BDC after getting a password
+ mismatch. The theory is that the password might be right on the PDC but
+ it merely hasn't replicated yet.
+
+Arguments:
+
+ LogonLevel -- Specifies the level of information given in
+ LogonInformation. Has already been validated.
+
+ LogonInformation -- Specifies the description for the user
+ logging on.
+
+ ValidationLevel -- Specifies the level of information returned in
+ ValidationInformation. Must be NetlogonValidationSamInfo or
+ NetlogonValidationSamInfo2.
+
+ ValidationInformation -- Returns the requested validation
+ information. This buffer must be freed using MIDL_user_free.
+
+ Authoritative -- Returns whether the status returned is an
+ authoritative status which should be returned to the original
+ caller. If not, this logon request may be tried again on another
+ domain controller. This parameter is returned regardless of the
+ status code.
+
+Return Value:
+
+ STATUS_SUCCESS: if there was no error.
+
+ STATUS_NO_LOGON_SERVERS: cannot connect to the higher authority.
+
+ STATUS_NO_TRUST_LSA_SECRET:
+ STATUS_TRUSTED_DOMAIN_FAILURE:
+ STATUS_TRUSTED_RELATIONSHIP_FAILURE:
+ can't authenticate with higer authority
+
+ Otherwise, the error code is returned.
+
+
+--*/
+{
+ NTSTATUS Status;
+
+ //
+ // If this isn't a BDC,
+ // There's nothing to do here.
+ //
+
+ if ( NlGlobalRole != RoleBackup ) {
+ return STATUS_INVALID_DOMAIN_ROLE;
+ }
+
+ //
+ // The normal pass-thru authentication logic handles this quite nicely.
+ //
+
+ Status = NlpUserValidateHigher(
+ NlGlobalClientSession,
+ LogonLevel,
+ LogonInformation,
+ ValidationLevel,
+ ValidationInformation,
+ Authoritative );
+
+#if DBG
+ if ( NT_SUCCESS(Status) ) {
+
+ IF_DEBUG( LOGON ) {
+ PNETLOGON_LOGON_IDENTITY_INFO LogonInfo;
+ LPWSTR LogonType;
+
+
+ LogonInfo = (PNETLOGON_LOGON_IDENTITY_INFO)
+ &((PNETLOGON_LEVEL)LogonInformation)->LogonInteractive;
+
+ if ( LogonLevel == NetlogonInteractiveInformation ) {
+ LogonType = L"Interactive";
+ } else if ( LogonLevel == NetlogonNetworkInformation ) {
+ LogonType = L"Network";
+ } else if ( LogonLevel == NetlogonServiceInformation ) {
+ LogonType = L"Service";
+ } else {
+ LogonType = L"[Unknown]";
+ }
+
+ NlPrint((NL_LOGON,
+ "SamLogon: " FORMAT_LPWSTR " logon of %wZ\\%wZ "
+ "from %wZ successfully handled on PDC.\n",
+ LogonType,
+ &LogonInfo->LogonDomainName,
+ &LogonInfo->UserName,
+ &LogonInfo->Workstation ));
+ }
+ }
+#endif // DBG
+
+ return Status;
+
+}
+
+
+
+VOID
+NlpZeroBadPasswordCountOnPdc (
+ IN NETLOGON_LOGON_INFO_CLASS LogonLevel,
+ IN LPBYTE LogonInformation
+)
+/*++
+
+Routine Description:
+
+ This function zeros the BadPasswordCount field for the specified user
+ on the PDC.
+
+Arguments:
+
+ LogonLevel -- Specifies the level of information given in
+ LogonInformation. Has already been validated.
+
+ LogonInformation -- Specifies the description for the user
+ logging on.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ NTSTATUS Status;
+ BOOLEAN Authoritative;
+ LPBYTE ValidationInformation = NULL;
+
+ //
+ // We only call this function on a BDC and if the BDC has just zeroed
+ // the BadPasswordCount because of successful logon. Therefore,
+ // we can zero the BadPasswordCount on the PDC by doing the logon over
+ // again on the PDC.
+ //
+
+ Status = NlpUserValidateOnPdc (
+ LogonLevel,
+ LogonInformation,
+ NetlogonValidationSamInfo,
+ &ValidationInformation,
+ &Authoritative );
+
+ if ( NT_SUCCESS(Status) ) {
+ MIDL_user_free( ValidationInformation );
+ }
+}
+
+
+NTSTATUS
+NlpUserValidate (
+ IN NETLOGON_SECURE_CHANNEL_TYPE SecureChannelType,
+ IN NETLOGON_LOGON_INFO_CLASS LogonLevel,
+ IN LPBYTE LogonInformation,
+ IN NETLOGON_VALIDATION_INFO_CLASS ValidationLevel,
+ OUT LPBYTE * ValidationInformation,
+ OUT PBOOLEAN Authoritative
+)
+/*++
+
+Routine Description:
+
+ This function processes an interactive or network logon.
+ It is a worker routine for I_NetSamLogon. I_NetSamLogon handles the
+ details of validating the caller. This function handles the details
+ of whether to validate locally or pass the request on. MsvValidateSam
+ does the actual local validation.
+
+ session table only in the domain defining the specified user's
+ account.
+
+ This service is also used to process a re-logon request.
+
+
+Arguments:
+
+ SecureChannelType -- Type of secure channel this request was made over.
+
+ LogonLevel -- Specifies the level of information given in
+ LogonInformation. Has already been validated.
+
+ LogonInformation -- Specifies the description for the user
+ logging on.
+
+ ValidationLevel -- Specifies the level of information returned in
+ ValidationInformation. Must be NetlogonValidationSamInfo or
+ NetlogonValidationSamInfo2.
+
+ ValidationInformation -- Returns the requested validation
+ information. This buffer must be freed using MIDL_user_free.
+
+ Authoritative -- Returns whether the status returned is an
+ authoritative status which should be returned to the original
+ caller. If not, this logon request may be tried again on another
+ domain controller. This parameter is returned regardless of the
+ status code.
+
+Return Value:
+
+ STATUS_SUCCESS: if there was no error.
+ Otherwise, the error code is
+ returned.
+
+
+--*/
+{
+ NTSTATUS Status;
+ NTSTATUS DefaultStatus = STATUS_NO_SUCH_USER;
+
+ PNETLOGON_LOGON_IDENTITY_INFO LogonInfo;
+ PCLIENT_SESSION ClientSession;
+ DWORD AccountsToTry = MSVSAM_SPECIFIED | MSVSAM_GUEST;
+ BOOLEAN BadPasswordCountZeroed;
+ BOOLEAN LogonToLocalDomain;
+
+ //
+ // Initialization
+ //
+
+ LogonInfo = (PNETLOGON_LOGON_IDENTITY_INFO) LogonInformation;
+ *Authoritative = FALSE;
+ LogonToLocalDomain = RtlEqualDomainName( &LogonInfo->LogonDomainName,
+ &NlGlobalAccountDomainName );
+
+
+
+ //
+ // Check to see if the account is in the local SAM database.
+ //
+ // The Theory:
+ // If a particular database is absolutely requested,
+ // we only try the account in the requested database.
+ //
+ // In the event that an account exists in multiple places in the hierarchy,
+ // we want to find the version of the account that is closest to the
+ // logged on machine (i.e., workstation first, primary domain, then
+ // trusted domain.). So we always try to local database before going
+ // to a higher authority.
+ //
+ // Finally, handle the case that this call is from a BDC in our own domain
+ // just checking to see if the PDC (us) has a better copy of the account
+ // than it does.
+ //
+
+ if ( LogonInfo->LogonDomainName.Length == 0 ||
+ LogonToLocalDomain ||
+ SecureChannelType == ServerSecureChannel ) {
+
+ //
+ // Indicate we've already tried the specified account and
+ // we won't need to try it again locally.
+ //
+
+ AccountsToTry &= ~MSVSAM_SPECIFIED;
+
+ Status = MsvSamValidate( NlGlobalDBInfoArray[SAM_DB].DBHandle,
+ NlGlobalUasCompatibilityMode,
+ SecureChannelType,
+ &NlGlobalUnicodeComputerNameString,
+ &NlGlobalAccountDomainName,
+ NlGlobalDBInfoArray[SAM_DB].DBId,
+ LogonLevel,
+ LogonInformation,
+ ValidationLevel,
+ (PVOID *)ValidationInformation,
+ Authoritative,
+ &BadPasswordCountZeroed,
+ MSVSAM_SPECIFIED );
+
+ //
+ // If this is a BDC and we zeroed the BadPasswordCount field,
+ // allow the PDC to do the same thing.
+ //
+
+ if ( BadPasswordCountZeroed ) {
+ NlpZeroBadPasswordCountOnPdc ( LogonLevel, LogonInformation );
+ }
+
+
+ //
+ // If the request is explicitly for this domain,
+ // The STATUS_NO_SUCH_USER answer is authoritative.
+ //
+
+ if ( LogonToLocalDomain && Status == STATUS_NO_SUCH_USER ) {
+ *Authoritative = TRUE;
+ }
+
+
+ //
+ // If this is one of our BDCs calling,
+ // return with whatever answer we got locally.
+ //
+
+ if ( SecureChannelType == ServerSecureChannel ) {
+ DefaultStatus = Status;
+ goto Cleanup;
+ }
+
+
+
+ //
+ // If the local SAM database authoritatively handled the logon attempt,
+ // just return.
+ //
+
+ if ( *Authoritative ) {
+ DefaultStatus = Status;
+
+ //
+ // If the problem is just that the password is wrong,
+ // try again on the PDC where the password may already be changed.
+ //
+
+ if ( BAD_PASSWORD(Status) ) {
+
+ BOOLEAN TempAuthoritative;
+
+ Status = NlpUserValidateOnPdc (
+ LogonLevel,
+ LogonInformation,
+ ValidationLevel,
+ ValidationInformation,
+ &TempAuthoritative );
+
+ // Ignore failures from the PDC
+ if ( NT_SUCCESS(Status) ) {
+ DefaultStatus = Status;
+ *Authoritative = TempAuthoritative;
+ }
+ }
+
+ goto Cleanup;
+ }
+
+ DefaultStatus = Status;
+ }
+
+
+ //
+ // If the request in not for this domain,
+ // or the domain name isn't specified (and we haven't found the account yet)
+ // send the request to a higher authority.
+ //
+
+ if ( LogonInfo->LogonDomainName.Length == 0 || !LogonToLocalDomain ) {
+
+
+ //
+ // If this machine is a workstation,
+ // send the request to the Primary Domain.
+ //
+
+ if ( NlGlobalRole == RoleMemberWorkstation ) {
+
+ Status = NlpUserValidateHigher(
+ NlGlobalClientSession,
+ LogonLevel,
+ LogonInformation,
+ ValidationLevel,
+ ValidationInformation,
+ Authoritative );
+
+ NlAssert( !NT_SUCCESS(Status) || Status == STATUS_SUCCESS );
+ NlAssert( !NT_SUCCESS(Status) || *ValidationInformation != NULL );
+
+
+ //
+ // return more appropriate error
+ //
+
+ if( (Status == STATUS_NO_TRUST_SAM_ACCOUNT) ||
+ (Status == STATUS_ACCESS_DENIED) ) {
+
+ Status = STATUS_TRUSTED_RELATIONSHIP_FAILURE;
+ }
+
+ //
+ // If the primary domain authoritatively handled the logon attempt,
+ // just return.
+ //
+
+ if ( *Authoritative ) {
+
+ //
+ // If we didn't actually talk to the primary domain,
+ // check locally if the domain requested is a trusted domain.
+ // (This list is only a cache so we had to try to contact the
+ // primary domain.)
+
+ if ( Status == STATUS_NO_LOGON_SERVERS ) {
+
+ //
+ // If the domain specified is trusted,
+ // then return the status to the caller.
+ // otherwise just press on.
+
+ if ( NlIsDomainTrusted ( &LogonInfo->LogonDomainName ) ) {
+ DefaultStatus = Status;
+ goto Cleanup;
+ } else {
+ //
+ // Set the return codes to look as though the primary
+ // determine this is an untrusted domain.
+ //
+ *Authoritative = FALSE;
+ Status = STATUS_NO_SUCH_USER;
+ }
+ } else {
+ DefaultStatus = Status;
+ goto Cleanup;
+ }
+ }
+
+
+ if ( Status != STATUS_NO_SUCH_USER ) {
+ DefaultStatus = Status;
+ }
+
+
+ //
+ // The machine is a Domain Controller.
+ //
+ // If this request was passed to us as a trusted domain request,
+ // There is no higher authority to pass the request to.
+ //
+
+ } else if ( SecureChannelType == TrustedDomainSecureChannel ) {
+
+ // DefaultStatus = STATUS_NO_SUCH_USER;
+
+
+ //
+ // This machine is a Domain Controller.
+ //
+ // This request is either a pass-thru request by a workstation in
+ // our domain, or this request came directly from the MSV
+ // authentication package.
+ //
+ // In either case, pass the request to the trusted domain.
+ //
+
+ } else {
+
+
+ //
+ // If this is the LanMan 2.0 case,
+ // Try to find the domain name by asking all the trusted
+ // domains if they define the account
+ //
+
+ if ( LogonInfo->LogonDomainName.Length == 0 ) {
+ LPWSTR UserName;
+
+
+ UserName = NlStringToLpwstr( &LogonInfo->UserName );
+ if ( UserName == NULL ) {
+ *Authoritative = FALSE;
+ DefaultStatus = STATUS_INSUFFICIENT_RESOURCES;
+ goto Cleanup;
+ }
+
+ ClientSession = NlPickDomainWithAccount( UserName,
+ USER_NORMAL_ACCOUNT );
+
+ NetpMemoryFree( UserName );
+
+
+ //
+ // It the domain is explicitly given,
+ // simply find the client session for that domain.
+ //
+
+ } else {
+
+ ClientSession =
+ NlFindNamedClientSession( &LogonInfo->LogonDomainName );
+
+ }
+
+ //
+ // If a trusted domain was determined,
+ // pass the logon request to the trusted domain.
+ //
+
+ if ( ClientSession != NULL ) {
+
+ Status = NlpUserValidateHigher(
+ ClientSession,
+ LogonLevel,
+ LogonInformation,
+ ValidationLevel,
+ ValidationInformation,
+ Authoritative );
+
+
+ NlUnrefClientSession( ClientSession );
+ NlAssert( !NT_SUCCESS(Status) || Status == STATUS_SUCCESS );
+ NlAssert( !NT_SUCCESS(Status) || *ValidationInformation != NULL );
+
+
+ //
+ // return more appropriate error
+ //
+
+ if( (Status == STATUS_NO_TRUST_LSA_SECRET) ||
+ (Status == STATUS_NO_TRUST_SAM_ACCOUNT) ||
+ (Status == STATUS_ACCESS_DENIED) ) {
+
+ Status = STATUS_TRUSTED_DOMAIN_FAILURE;
+ }
+
+ //
+ // Since the request is explicitly for a trusted domain,
+ // The STATUS_NO_SUCH_USER answer is authoritative.
+ //
+
+ if ( Status == STATUS_NO_SUCH_USER ) {
+ *Authoritative = TRUE;
+ }
+
+ //
+ // If the trusted domain authoritatively handled the
+ // logon attempt, just return.
+ //
+
+ if ( *Authoritative ) {
+ DefaultStatus = Status;
+ goto Cleanup;
+ }
+
+ DefaultStatus = Status;
+
+ }
+
+ }
+ }
+
+
+ //
+ // We have no authoritative answer from a higher authority and
+ // DefaultStatus is the higher authority's response.
+ //
+
+ NlAssert( ! *Authoritative );
+
+
+Cleanup:
+ NlAssert( !NT_SUCCESS(DefaultStatus) || DefaultStatus == STATUS_SUCCESS );
+ NlAssert( !NT_SUCCESS(DefaultStatus) || *ValidationInformation != NULL );
+ //
+ // If this is a network logon and this call in non-passthru,
+ // Try one last time to log on.
+ //
+
+ if ( LogonLevel == NetlogonNetworkInformation &&
+ SecureChannelType == MsvApSecureChannel ) {
+
+ //
+ // If the only reason we can't log the user on is that he has
+ // no user account, logging him on as guest is OK.
+ //
+ // There are actaully two cases here:
+ // * If the response is Authoritative, then the specified domain
+ // is trusted but the user has no account in the domain.
+ //
+ // * If the response in non-authoritative, then the specified domain
+ // is an untrusted domain.
+ //
+ // In either case, then right thing to do is to try the guest account.
+ //
+
+ if ( DefaultStatus != STATUS_NO_SUCH_USER ) {
+ AccountsToTry &= ~MSVSAM_GUEST;
+ }
+
+ //
+ // If this is not an authoritative response,
+ // then the domain specified isn't a trusted domain.
+ // try the specified account name too.
+ //
+ // The specified account name will probably be a remote account
+ // with the same username and password.
+ //
+
+ if ( *Authoritative ) {
+ AccountsToTry &= ~MSVSAM_SPECIFIED;
+ }
+
+
+ //
+ // Validate against the Local Sam database.
+ //
+
+ if ( AccountsToTry != 0 ) {
+ BOOLEAN TempAuthoritative;
+
+ Status = MsvSamValidate(
+ NlGlobalDBInfoArray[SAM_DB].DBHandle,
+ NlGlobalUasCompatibilityMode,
+ SecureChannelType,
+ &NlGlobalUnicodeComputerNameString,
+ &NlGlobalAccountDomainName,
+ NlGlobalDBInfoArray[SAM_DB].DBId,
+ LogonLevel,
+ LogonInformation,
+ ValidationLevel,
+ (PVOID *)ValidationInformation,
+ &TempAuthoritative,
+ &BadPasswordCountZeroed,
+ AccountsToTry );
+ NlAssert( !NT_SUCCESS(Status) || Status == STATUS_SUCCESS );
+ NlAssert( !NT_SUCCESS(Status) || *ValidationInformation != NULL );
+
+ //
+ // If this is a BDC and we zeroed the BadPasswordCount field,
+ // allow the PDC to do the same thing.
+ //
+
+ if ( BadPasswordCountZeroed ) {
+ NlpZeroBadPasswordCountOnPdc ( LogonLevel, LogonInformation );
+ }
+
+ //
+ // If the local SAM database authoritatively handled the
+ // logon attempt,
+ // just return.
+ //
+
+ if ( TempAuthoritative ) {
+ DefaultStatus = Status;
+ *Authoritative = TRUE;
+
+ //
+ // If the problem is just that the password is wrong,
+ // try again on the PDC where the password may already be
+ // changed.
+ //
+
+ if ( BAD_PASSWORD(Status) ) {
+
+ Status = NlpUserValidateOnPdc (
+ LogonLevel,
+ LogonInformation,
+ ValidationLevel,
+ ValidationInformation,
+ &TempAuthoritative );
+
+ // Ignore failures from the PDC
+ if ( NT_SUCCESS(Status) ) {
+ DefaultStatus = Status;
+ *Authoritative = TempAuthoritative;
+ }
+ }
+
+ //
+ // Here we must choose between the non-authoritative status in
+ // DefaultStatus and the non-authoritative status from the local
+ // SAM lookup. Use the one from the higher authority unless it
+ // isn't interesting.
+ //
+
+ } else {
+ if ( DefaultStatus == STATUS_NO_SUCH_USER ) {
+ DefaultStatus = Status;
+ }
+ }
+ }
+ }
+
+ return DefaultStatus;
+
+}
+
+
+NTSTATUS
+NetrLogonSamLogon (
+ IN LPWSTR LogonServer OPTIONAL,
+ IN LPWSTR ComputerName OPTIONAL,
+ IN PNETLOGON_AUTHENTICATOR Authenticator OPTIONAL,
+ OUT PNETLOGON_AUTHENTICATOR ReturnAuthenticator OPTIONAL,
+ IN NETLOGON_LOGON_INFO_CLASS LogonLevel,
+ IN PNETLOGON_LEVEL LogonInformation,
+ IN NETLOGON_VALIDATION_INFO_CLASS ValidationLevel,
+ OUT PNETLOGON_VALIDATION ValidationInformation,
+ OUT PBOOLEAN Authoritative
+)
+/*++
+
+Routine Description:
+
+ This function is called by an NT client to process an interactive or
+ network logon. This function passes a domain name, user name and
+ credentials to the Netlogon service and returns information needed to
+ build a token. It is called in three instances:
+
+ * It is called by the LSA's MSV1_0 authentication package for any
+ NT system that has LanMan installed. The MSV1_0 authentication
+ package calls SAM directly if LanMan is not installed. In this
+ case, this function is a local function and requires the caller
+ to have SE_TCB privilege. The local Netlogon service will
+ either handle this request directly (validating the request with
+ the local SAM database) or will forward this request to the
+ appropriate domain controller as documented in sections 2.4 and
+ 2.5.
+
+ * It is called by a Netlogon service on a workstation to a DC in
+ the Primary Domain of the workstation as documented in section
+ 2.4. In this case, this function uses a secure channel set up
+ between the two Netlogon services.
+
+ * It is called by a Netlogon service on a DC to a DC in a trusted
+ domain as documented in section 2.5. In this case, this
+ function uses a secure channel set up between the two Netlogon
+ services.
+
+ The Netlogon service validates the specified credentials. If they
+ are valid, adds an entry for this LogonId, UserName, and Workstation
+ into the logon session table. The entry is added to the logon
+ session table only in the domain defining the specified user's
+ account.
+
+ This service is also used to process a re-logon request.
+
+
+Arguments:
+
+ LogonServer -- Supplies the name of the logon server to process
+ this logon request. This field should be null to indicate
+ this is a call from the MSV1_0 authentication package to the
+ local Netlogon service.
+
+ ComputerName -- Name of the machine making the call. This field
+ should be null to indicate this is a call from the MSV1_0
+ authentication package to the local Netlogon service.
+
+ Authenticator -- supplied by the client. This field should be
+ null to indicate this is a call from the MSV1_0
+ authentication package to the local Netlogon service.
+
+ ReturnAuthenticator -- Receives an authenticator returned by the
+ server. This field should be null to indicate this is a call
+ from the MSV1_0 authentication package to the local Netlogon
+ service.
+
+ LogonLevel -- Specifies the level of information given in
+ LogonInformation.
+
+ LogonInformation -- Specifies the description for the user
+ logging on.
+
+ ValidationLevel -- Specifies the level of information returned in
+ ValidationInformation. Must be NetlogonValidationSamInfo or
+ NetlogonValidationSamInfo2
+
+ ValidationInformation -- Returns the requested validation
+ information. This buffer must be freed using MIDL_user_free.
+
+ Authoritative -- Returns whether the status returned is an
+ authoritative status which should be returned to the original
+ caller. If not, this logon request may be tried again on another
+ domain controller. This parameter is returned regardless of the
+ status code.
+
+Return Value:
+
+ STATUS_SUCCESS: if there was no error.
+
+ STATUS_NO_LOGON_SERVERS -- no domain controller in the requested
+ domain is currently available to validate the logon request.
+
+ STATUS_NO_TRUST_LSA_SECRET -- there is no secret account in the
+ local LSA database to establish a secure channel to a DC.
+
+ STATUS_TRUSTED_DOMAIN_FAILURE -- the secure channel setup between
+ the domain controllers of the trust domains to pass-through
+ validate the logon request failed.
+
+ STATUS_TRUSTED_RELATIONSHIP_FAILURE -- the secure channel setup
+ between the workstation and the DC failed.
+
+ STATUS_INVALID_INFO_CLASS -- Either LogonLevel or ValidationLevel is
+ invalid.
+
+ STATUS_INVALID_PARAMETER -- Another Parameter is invalid.
+
+ STATUS_ACCESS_DENIED -- The caller does not have access to call this
+ API.
+
+ STATUS_NO_SUCH_USER -- Indicates that the user specified in
+ LogonInformation does not exist. This status should not be returned
+ to the originally caller. It should be mapped to STATUS_LOGON_FAILURE.
+
+ STATUS_WRONG_PASSWORD -- Indicates that the password information in
+ LogonInformation was incorrect. This status should not be returned
+ to the originally caller. It should be mapped to STATUS_LOGON_FAILURE.
+
+ STATUS_INVALID_LOGON_HOURES -- The user is not authorized to logon
+ at this time.
+
+ STATUS_INVALID_WORKSTATION -- The user is not authorized to logon
+ from the specified workstation.
+
+ STATUS_PASSWORD_EXPIRED -- The password for the user has expired.
+
+ STATUS_ACCOUNT_DISABLED -- The user's account has been disabled.
+
+ .
+ .
+ .
+
+--*/
+{
+ NTSTATUS Status;
+
+ PNETLOGON_LOGON_IDENTITY_INFO LogonInfo;
+
+ PSERVER_SESSION ServerSession;
+ NETLOGON_SECURE_CHANNEL_TYPE SecureChannelType;
+ SESSION_INFO SessionInfo;
+#if DBG
+ LPWSTR LogonType;
+#endif // DBG
+
+
+ //
+ // Check the LogonLevel
+ //
+
+ *Authoritative = TRUE;
+ ValidationInformation->ValidationSam = NULL;
+ SessionInfo.NegotiatedFlags = NETLOGON_SUPPORTS_MASK;
+
+ switch ( LogonLevel ) {
+ case NetlogonInteractiveInformation:
+ case NetlogonNetworkInformation:
+ case NetlogonServiceInformation:
+ break;
+
+ default:
+ *Authoritative = TRUE;
+ return STATUS_INVALID_INFO_CLASS;
+ }
+
+ LogonInfo = (PNETLOGON_LOGON_IDENTITY_INFO)
+ LogonInformation->LogonInteractive;
+
+#if DBG
+ if ( LogonLevel == NetlogonInteractiveInformation ) {
+ LogonType = L"Interactive";
+ } else if ( LogonLevel == NetlogonNetworkInformation ) {
+ LogonType = L"Network";
+ } else if ( LogonLevel == NetlogonServiceInformation ) {
+ LogonType = L"Service";
+ } else {
+ LogonType = L"[Unknown]";
+ }
+
+ IF_DEBUG( LOGON ) {
+ if ( ComputerName != NULL ) {
+ NlPrint((NL_LOGON,
+ "SamLogon: " FORMAT_LPWSTR " logon of %wZ\\%wZ "
+ "from %wZ (via " FORMAT_LPWSTR ") Entered\n",
+ LogonType,
+ &LogonInfo->LogonDomainName,
+ &LogonInfo->UserName,
+ &LogonInfo->Workstation,
+ ComputerName ));
+ } else {
+ NlPrint((NL_LOGON,
+ "SamLogon: " FORMAT_LPWSTR " logon of %wZ\\%wZ "
+ "from %wZ Entered\n",
+ LogonType,
+ &LogonInfo->LogonDomainName,
+ &LogonInfo->UserName,
+ &LogonInfo->Workstation ));
+ }
+ }
+#endif // DBG
+
+ //
+ // Check the ValidationLevel
+ //
+
+ switch (ValidationLevel) {
+ case NetlogonValidationSamInfo:
+ case NetlogonValidationSamInfo2:
+ break;
+
+ default:
+ *Authoritative = TRUE;
+ return STATUS_INVALID_INFO_CLASS;
+ }
+
+
+ //
+ // If MSV is calling when the netlogon service isn't running,
+ // tell it so.
+ //
+
+ EnterCriticalSection( &NlGlobalMsvCritSect );
+ if ( !NlGlobalMsvEnabled ) {
+ LeaveCriticalSection( &NlGlobalMsvCritSect );
+ return STATUS_NETLOGON_NOT_STARTED;
+ }
+ NlGlobalMsvThreadCount ++;
+ LeaveCriticalSection( &NlGlobalMsvCritSect );
+
+
+ //
+ // If we're being called from the MSV Authentication Package,
+ // require SE_TCB privilege.
+ //
+
+ if ( LogonServer == NULL &&
+ ComputerName == NULL &&
+ Authenticator == NULL &&
+ ReturnAuthenticator == NULL ) {
+
+ //
+ // ?? Do as I said
+ //
+
+ SecureChannelType = MsvApSecureChannel;
+
+
+ //
+ // If we're being called from another Netlogon Server,
+ // Verify the secure channel information.
+ //
+
+ } else {
+
+ //
+ // This API is not supported on workstations.
+ //
+
+ if ( NlGlobalRole == RoleMemberWorkstation ) {
+ Status = STATUS_NOT_SUPPORTED;
+ goto Cleanup;
+ }
+
+ //
+ // Arguments are no longer optional.
+ //
+
+ if ( LogonServer == NULL ||
+ ComputerName == NULL ||
+ Authenticator == NULL ||
+ ReturnAuthenticator == NULL ) {
+
+ *Authoritative = TRUE;
+ Status = STATUS_INVALID_PARAMETER;
+ goto Cleanup;
+ }
+
+
+ //
+ // Check the LogonServer name.
+ //
+
+ Status = NlVerifyWorkstation( LogonServer );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ *Authoritative = FALSE;
+ goto Cleanup;
+ }
+
+ //
+ // Find the server session entry for this session.
+ //
+
+ LOCK_SERVER_SESSION_TABLE();
+ ServerSession = NlFindNamedServerSession( ComputerName );
+
+ if (ServerSession == NULL) {
+ UNLOCK_SERVER_SESSION_TABLE();
+ *Authoritative = FALSE;
+ Status = STATUS_ACCESS_DENIED;
+ goto Cleanup;
+ }
+
+ //
+ // now verify the Authenticator and update seed if OK
+ //
+
+ Status = NlCheckAuthenticator( ServerSession,
+ Authenticator,
+ ReturnAuthenticator);
+
+ if ( !NT_SUCCESS(Status) ) {
+ UNLOCK_SERVER_SESSION_TABLE();
+ *Authoritative = FALSE;
+ goto Cleanup;
+ }
+
+ SecureChannelType = ServerSession->SsSecureChannelType;
+ SessionInfo.SessionKey = ServerSession->SsSessionKey;
+ SessionInfo.NegotiatedFlags = ServerSession->SsNegotiatedFlags;
+ UNLOCK_SERVER_SESSION_TABLE();
+
+ //
+ // Decrypt the password information
+ //
+
+ NlpDecryptLogonInformation ( LogonLevel, (LPBYTE) LogonInfo, &SessionInfo );
+
+ }
+
+
+
+
+
+ //
+ // If the logon service is paused then don't process this logon
+ // request any further.
+ //
+
+ if ( (NlGlobalServiceStatus.dwCurrentState == SERVICE_PAUSED) ||
+ ( NlGlobalFirstTimeFullSync == TRUE ) ) {
+
+ //
+ // Don't reject logons originating inside this
+ // machine. Such requests aren't really pass-thru requests.
+ //
+ // Don't reject logons from a BDC in our own domain. These logons
+ // support account lockout and authentication of users whose password
+ // has been updated on the PDC but not the BDC. Such pass-thru
+ // requests can only be handled by the PDC of the domain.
+ //
+
+ if ( SecureChannelType != MsvApSecureChannel &&
+ SecureChannelType != ServerSecureChannel ) {
+
+ //
+ // Return STATUS_ACCESS_DENIED to convince the caller to drop the
+ // secure channel to this logon server and reconnect to some other
+ // logon server.
+ //
+ *Authoritative = FALSE;
+ Status = STATUS_ACCESS_DENIED;
+ goto Cleanup;
+ }
+ }
+
+ //
+ // Validate the Request.
+ //
+
+ Status = NlpUserValidate( SecureChannelType,
+ LogonLevel,
+ (LPBYTE) LogonInfo,
+ ValidationLevel,
+ (LPBYTE *)&ValidationInformation->ValidationSam,
+ Authoritative );
+
+ if ( !NT_SUCCESS(Status) ) {
+ //
+ // If this is an NT 3.1 client,
+ // map NT 3.5 status codes to their NT 3.1 equivalents.
+ //
+ // The NETLOGON_SUPPORTS_ACCOUNT_LOCKOUT bit is really the wrong bit
+ // to be using, but all NT3.5 clients have it set and all NT3.1 clients
+ // don't, so it'll work for our purposes.
+ //
+
+ if ( (SessionInfo.NegotiatedFlags & NETLOGON_SUPPORTS_ACCOUNT_LOCKOUT) == 0 ) {
+ switch ( Status ) {
+ case STATUS_PASSWORD_MUST_CHANGE:
+ Status = STATUS_PASSWORD_EXPIRED;
+ break;
+ case STATUS_ACCOUNT_LOCKED_OUT:
+ Status = STATUS_ACCOUNT_DISABLED;
+ break;
+ }
+ }
+ goto Cleanup;
+ }
+
+ NlAssert( !NT_SUCCESS(Status) || Status == STATUS_SUCCESS );
+ NlAssert( !NT_SUCCESS(Status) || ValidationInformation->ValidationSam != NULL );
+
+
+
+ //
+ // If the validation information is being returned to a client on another
+ // machine, encrypt it before sending it over the wire.
+ //
+
+ if ( SecureChannelType != MsvApSecureChannel ) {
+ NlpEncryptValidationInformation (
+ LogonLevel,
+ ValidationLevel,
+ *((LPBYTE *) ValidationInformation),
+ &SessionInfo );
+ }
+
+
+ Status = STATUS_SUCCESS;
+
+ //
+ // Cleanup up before returning.
+ //
+
+Cleanup:
+ if ( !NT_SUCCESS(Status) ) {
+ if (ValidationInformation->ValidationSam != NULL) {
+ MIDL_user_free( ValidationInformation->ValidationSam );
+ ValidationInformation->ValidationSam = NULL;
+ }
+ }
+
+
+#if DBG
+ IF_DEBUG( LOGON ) {
+ if ( ComputerName != NULL ) {
+ NlPrint((NL_LOGON,
+ "SamLogon: " FORMAT_LPWSTR " logon of %wZ\\%wZ "
+ "from %wZ (via " FORMAT_LPWSTR ") Returns 0x%lX\n",
+ LogonType,
+ &LogonInfo->LogonDomainName,
+ &LogonInfo->UserName,
+ &LogonInfo->Workstation,
+ ComputerName,
+ Status ));
+ } else {
+ NlPrint((NL_LOGON,
+ "SamLogon: " FORMAT_LPWSTR
+ " logon of %wZ\\%wZ from %wZ Returns 0x%lX\n",
+ LogonType,
+ &LogonInfo->LogonDomainName,
+ &LogonInfo->UserName,
+ &LogonInfo->Workstation,
+ Status ));
+ }
+ }
+#endif // DBG
+
+
+ //
+ // Indicate that the MSV thread has left netlogon.dll
+ //
+
+ EnterCriticalSection( &NlGlobalMsvCritSect );
+ NlGlobalMsvThreadCount --;
+ if ( NlGlobalMsvThreadCount == 0 && !NlGlobalMsvEnabled ) {
+ if ( !SetEvent( NlGlobalMsvTerminateEvent ) ) {
+ NlPrint((NL_CRITICAL, "Cannot set MSV termination event: %lu\n",
+ GetLastError() ));
+ }
+ }
+ LeaveCriticalSection( &NlGlobalMsvCritSect );
+
+ return Status;
+}
+
+
+NTSTATUS
+NetrLogonSamLogoff (
+ IN LPWSTR LogonServer OPTIONAL,
+ IN LPWSTR ComputerName OPTIONAL,
+ IN PNETLOGON_AUTHENTICATOR Authenticator OPTIONAL,
+ OUT PNETLOGON_AUTHENTICATOR ReturnAuthenticator OPTIONAL,
+ IN NETLOGON_LOGON_INFO_CLASS LogonLevel,
+ IN PNETLOGON_LEVEL LogonInformation
+)
+/*++
+
+Routine Description:
+
+ This function is called by an NT client to process an interactive
+ logoff. It is not called for the network logoff case since the
+ Netlogon service does not maintain any context for network logons.
+
+ This function does the following. It authenticates the request. It
+ updates the logon statistics in the SAM database on whichever machine
+ or domain defines this user account. It updates the logon session
+ table in the primary domain of the machine making the request. And
+ it returns logoff information to the caller.
+
+ This function is called in same scenarios that I_NetLogonSamLogon is
+ called:
+
+ * It is called by the LSA's MSV1_0 authentication package to
+ support LsaApLogonTerminated. In this case, this function is a
+ local function and requires the caller to have SE_TCB privilege.
+ The local Netlogon service will either handle this request
+ directly (if LogonDomainName indicates this request was
+ validated locally) or will forward this request to the
+ appropriate domain controller as documented in sections 2.4 and
+ 2.5.
+
+ * It is called by a Netlogon service on a workstation to a DC in
+ the Primary Domain of the workstation as documented in section
+ 2.4. In this case, this function uses a secure channel set up
+ between the two Netlogon services.
+
+ * It is called by a Netlogon service on a DC to a DC in a trusted
+ domain as documented in section 2.5. In this case, this
+ function uses a secure channel set up between the two Netlogon
+ services.
+
+ When this function is a remote function, it is sent to the DC over a
+ NULL session.
+
+Arguments:
+
+ LogonServer -- Supplies the name of the logon server which logged
+ this user on. This field should be null to indicate this is
+ a call from the MSV1_0 authentication package to the local
+ Netlogon service.
+
+ ComputerName -- Name of the machine making the call. This field
+ should be null to indicate this is a call from the MSV1_0
+ authentication package to the local Netlogon service.
+
+ Authenticator -- supplied by the client. This field should be
+ null to indicate this is a call from the MSV1_0
+ authentication package to the local Netlogon service.
+
+ ReturnAuthenticator -- Receives an authenticator returned by the
+ server. This field should be null to indicate this is a call
+ from the MSV1_0 authentication package to the local Netlogon
+ service.
+
+ LogonLevel -- Specifies the level of information given in
+ LogonInformation.
+
+ LogonInformation -- Specifies the logon domain name, logon Id,
+ user name and workstation name of the user logging off.
+
+Return Value:
+
+--*/
+{
+ NTSTATUS Status;
+ PNETLOGON_LOGON_IDENTITY_INFO LogonInfo;
+
+ PSERVER_SESSION ServerSession;
+ NETLOGON_SECURE_CHANNEL_TYPE SecureChannelType;
+ PCLIENT_SESSION ClientSession;
+#if DBG
+ LPWSTR LogonType;
+#endif // DBG
+
+ //
+ // Check the LogonLevel
+ //
+
+ if ( LogonLevel != NetlogonInteractiveInformation ) {
+ return STATUS_INVALID_INFO_CLASS;
+ }
+
+ LogonInfo = (PNETLOGON_LOGON_IDENTITY_INFO)
+ LogonInformation->LogonInteractive;
+
+#if DBG
+ if ( LogonLevel == NetlogonInteractiveInformation ) {
+ LogonType = L"Interactive";
+ } else {
+ LogonType = L"[Unknown]";
+ }
+
+ NlPrint((NL_LOGON,
+ "NetrLogonSamLogoff: " FORMAT_LPWSTR
+ " logoff of %wZ\\%wZ from %wZ Entered\n",
+ LogonType,
+ &LogonInfo->LogonDomainName,
+ &LogonInfo->UserName,
+ &LogonInfo->Workstation ));
+#endif // DBG
+
+
+ //
+ // Sanity check the username and domain name.
+ //
+
+ if ( LogonInfo->UserName.Length == 0 ||
+ LogonInfo->LogonDomainName.Length == 0 ) {
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ //
+ // If MSV is calling when the netlogon service isn't running,
+ // tell it so.
+ //
+
+ EnterCriticalSection( &NlGlobalMsvCritSect );
+ if ( !NlGlobalMsvEnabled ) {
+ LeaveCriticalSection( &NlGlobalMsvCritSect );
+ return STATUS_NETLOGON_NOT_STARTED;
+ }
+ NlGlobalMsvThreadCount ++;
+ LeaveCriticalSection( &NlGlobalMsvCritSect );
+
+
+
+ //
+ // If we've been called from the local msv1_0,
+ // special case the secure channel type.
+ //
+
+ if ( LogonServer == NULL &&
+ ComputerName == NULL &&
+ Authenticator == NULL &&
+ ReturnAuthenticator == NULL ) {
+
+ SecureChannelType = MsvApSecureChannel;
+
+ //
+ // If we're being called from another Netlogon Server,
+ // Verify the secure channel information.
+ //
+
+ } else {
+
+ //
+ // This API is not supported on workstations.
+ //
+
+ if ( NlGlobalRole == RoleMemberWorkstation ) {
+ Status = STATUS_NOT_SUPPORTED;
+ goto Cleanup;
+ }
+
+ //
+ // Arguments are no longer optional.
+ //
+
+ if ( LogonServer == NULL ||
+ ComputerName == NULL ||
+ Authenticator == NULL ||
+ ReturnAuthenticator == NULL ) {
+
+ Status = STATUS_INVALID_PARAMETER;
+ goto Cleanup;
+ }
+
+
+ //
+ // Check the LogonServer name.
+ //
+
+ Status = NlVerifyWorkstation( LogonServer );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto Cleanup;
+ }
+
+ //
+ // Find the server session entry for this secure channel.
+ //
+
+ LOCK_SERVER_SESSION_TABLE();
+ ServerSession = NlFindNamedServerSession( ComputerName );
+
+ if (ServerSession == NULL) {
+ UNLOCK_SERVER_SESSION_TABLE();
+ Status = STATUS_ACCESS_DENIED;
+ goto Cleanup;
+ }
+
+ //
+ // Now verify the Authenticator and update seed if OK
+ //
+
+ Status = NlCheckAuthenticator(
+ ServerSession,
+ Authenticator,
+ ReturnAuthenticator);
+
+ if ( !NT_SUCCESS(Status) ) {
+ UNLOCK_SERVER_SESSION_TABLE();
+ goto Cleanup;
+ }
+
+ SecureChannelType = ServerSession->SsSecureChannelType;
+
+ UNLOCK_SERVER_SESSION_TABLE();
+
+
+ }
+
+ //
+ // If this is the domain that logged this user on,
+ // update the logon statistics.
+ //
+
+ if ( RtlEqualDomainName( &LogonInfo->LogonDomainName,
+ &NlGlobalAccountDomainName) ) {
+
+ Status = MsvSamLogoff(
+ NlGlobalDBInfoArray[SAM_DB].DBHandle,
+ LogonLevel,
+ LogonInfo );
+
+ if ( !NT_SUCCESS(Status) ) {
+ goto Cleanup;
+ }
+
+ //
+ // If this is not the domain that logged this user on,
+ // pass the request to a higher authority.
+ //
+
+ } else {
+
+ //
+ // If this machine is a workstation,
+ // send the request to the Primary Domain.
+ //
+
+ if ( NlGlobalRole == RoleMemberWorkstation ) {
+
+ Status = NlpUserLogoffHigher(
+ NlGlobalClientSession,
+ LogonLevel,
+ (LPBYTE) LogonInfo );
+
+ //
+ // return more appropriate error
+ //
+
+ if( (Status == STATUS_NO_TRUST_SAM_ACCOUNT) ||
+ (Status == STATUS_ACCESS_DENIED) ) {
+
+ Status = STATUS_TRUSTED_RELATIONSHIP_FAILURE;
+ }
+
+ goto Cleanup;
+
+
+ //
+ // The machine is a Domain Controller.
+ //
+ // If this request was passed to us as a trusted domain request,
+ // There is no higher authority to pass the request to.
+ //
+
+ } else if ( SecureChannelType == TrustedDomainSecureChannel ) {
+
+ Status = STATUS_NO_SUCH_DOMAIN;
+ goto Cleanup;
+
+
+ //
+ // This machine is a Domain Controller.
+ //
+ // This request is either a pass-thru request by a workstation in
+ // our domain, or this request came directly from the MSV
+ // authentication package.
+ //
+ // In either case, pass the request to the trusted domain.
+ //
+
+ } else {
+
+
+ //
+ // Send the request to the appropriate Trusted Domain.
+ //
+ // Find the ClientSession structure for the domain.
+ //
+
+ ClientSession =
+ NlFindNamedClientSession( &LogonInfo->LogonDomainName );
+
+ if ( ClientSession == NULL ) {
+ Status = STATUS_NO_SUCH_DOMAIN;
+ goto Cleanup;
+ }
+
+ Status = NlpUserLogoffHigher(
+ ClientSession,
+ LogonLevel,
+ (LPBYTE) LogonInfo );
+
+ NlUnrefClientSession( ClientSession );
+
+ //
+ // return more appropriate error
+ //
+
+ if( (Status == STATUS_NO_TRUST_LSA_SECRET) ||
+ (Status == STATUS_NO_TRUST_SAM_ACCOUNT) ||
+ (Status == STATUS_ACCESS_DENIED) ) {
+
+ Status = STATUS_TRUSTED_DOMAIN_FAILURE;
+ }
+
+ }
+ }
+
+Cleanup:
+
+ //
+ // If the request failed, be carefull to not leak authentication
+ // information.
+ //
+
+ if ( Status == STATUS_ACCESS_DENIED ) {
+ if ( ReturnAuthenticator != NULL ) {
+ RtlZeroMemory( ReturnAuthenticator, sizeof(*ReturnAuthenticator) );
+ }
+
+ }
+
+
+#if DBG
+ NlPrint((NL_LOGON,
+ "NetrLogonSamLogoff: " FORMAT_LPWSTR
+ " logoff of %wZ\\%wZ from %wZ returns %lX\n",
+ LogonType,
+ &LogonInfo->LogonDomainName,
+ &LogonInfo->UserName,
+ &LogonInfo->Workstation,
+ Status ));
+#endif // DBG
+
+ //
+ // Indicate that the MSV thread has left netlogon.dll
+ //
+
+ EnterCriticalSection( &NlGlobalMsvCritSect );
+ NlGlobalMsvThreadCount --;
+ if ( NlGlobalMsvThreadCount == 0 && !NlGlobalMsvEnabled ) {
+ if ( !SetEvent( NlGlobalMsvTerminateEvent ) ) {
+ NlPrint((NL_CRITICAL, "Cannot set MSV termination event: %lu\n",
+ GetLastError() ));
+ }
+ }
+ LeaveCriticalSection( &NlGlobalMsvCritSect );
+
+ return Status;
+}
+
+
+NET_API_STATUS
+NetrGetDCName (
+ IN LPWSTR ServerName OPTIONAL,
+ IN LPWSTR DomainName OPTIONAL,
+ OUT LPWSTR *Buffer
+ )
+
+/*++
+
+Routine Description:
+
+ Get the name of the primary domain controller for a domain.
+
+Arguments:
+
+ ServerName - name of remote server (null for local)
+
+ DomainName - name of domain (null for primary)
+
+ Buffer - Returns a pointer to an allcated buffer containing the
+ servername of the PDC of the domain. The server name is prefixed
+ by \\. The buffer should be deallocated using NetApiBufferFree.
+
+Return Value:
+
+ NERR_Success - Success. Buffer contains PDC name prefixed by \\.
+ NERR_DCNotFound No DC found for this domain.
+ ERROR_INVALID_NAME Badly formed domain name
+
+--*/
+{
+ NET_API_STATUS NetStatus;
+ UNREFERENCED_PARAMETER( ServerName );
+
+ //
+ // This API is not supported on workstations.
+ //
+
+ if ( NlGlobalRole == RoleMemberWorkstation ) {
+ return ERROR_NOT_SUPPORTED;
+ }
+
+ //
+ // Simply call the API which handles the local case specially.
+ //
+
+ NetStatus = NetGetDCName( NULL, DomainName, (LPBYTE *)Buffer );
+
+ return NetStatus;
+}
diff --git a/private/net/svcdlls/logonsrv/server/logonsrv.h b/private/net/svcdlls/logonsrv/server/logonsrv.h
new file mode 100644
index 000000000..fd152e82a
--- /dev/null
+++ b/private/net/svcdlls/logonsrv/server/logonsrv.h
@@ -0,0 +1,392 @@
+/*++
+
+Copyright (c) 1987-1992 Microsoft Corporation
+
+Module Name:
+
+ logonsrv.h
+
+Abstract:
+
+ Netlogon service internal constants and definitions.
+
+Author:
+
+ Ported from Lan Man 2.0
+
+Revision History:
+
+ 21-May-1991 (cliffv)
+ Ported to NT. Converted to NT style.
+ 09-Apr-1992 JohnRo
+ Prepare for WCHAR.H (_wcsicmp vs _wcscmpi, etc).
+
+--*/
+
+////////////////////////////////////////////////////////////////////////////
+//
+// Common include files needed by ALL netlogon server files
+//
+////////////////////////////////////////////////////////////////////////////
+
+#if ( _MSC_VER >= 800 )
+#pragma warning ( 3 : 4100 ) // enable "Unreferenced formal parameter"
+#pragma warning ( 3 : 4219 ) // enable "trailing ',' used for variable argument list"
+#endif
+
+#include <nt.h> // LARGE_INTEGER definition
+#include <ntrtl.h> // LARGE_INTEGER definition
+#include <nturtl.h> // LARGE_INTEGER definition
+#include <ntlsa.h> // Needed by lsrvdata.h
+
+#define NOMINMAX // Avoid redefinition of min and max in stdlib.h
+#include <rpc.h> // Needed by logon.h
+#include <logon_s.h> // includes lmcons.h, lmaccess.h, netlogon.h, ssi.h, windef.h
+
+#include <winbase.h>
+
+#include <lmerrlog.h> // NELOG_*
+#include <lmsname.h> // Needed for NETLOGON service name
+#include <winsvc.h> // Needed for new service controller APIs
+#include <logonp.h> // NetpLogon routines
+#include <samrpc.h> // Needed by lsrvdata.h and logonsrv.h
+#include <samisrv.h> // SamIFree routines
+#include "changelg.h" // Change log support
+#include "chutil.h" // Change log support
+#include <lsarpc.h> // Needed by lsrvdata.h and logonsrv.h
+#include <lsaisrv.h> // LsaI routines
+#include "ssiinit.h" // Misc global definitions
+#include <icanon.h> // NAMETYPE_* defines
+#include "lsrvdata.h" // Globals
+#include <debugfmt.h> // FORMAT_*
+#include <netlib.h> // NetpCopy...
+#include <netlibnt.h> // NetpNtStatusToApiStatus
+#include "nldebug.h" // Netlogon debugging
+#include "nlp.h" // Nlp routines
+#include <stdlib.h> // wcs routines
+
+
+
+//
+// On x86, allow bad alignment in debug statements.
+//
+
+#ifdef _X86_
+#define BAD_ALIGNMENT
+#endif // _X86_
+
+
+
+
+
+#define NETLOGON_SCRIPTS_SHARE TEXT( "NETLOGON" )
+#define IPC_SHARE TEXT( "IPC$" )
+
+#define THREAD_STACKSIZE 8192
+#define MAX_LOGONREQ_COUNT 3
+
+
+#define NETLOGON_INSTALL_WAIT 30000 // 30 secs
+
+
+
+////////////////////////////////////////////////////////////////////////
+//
+// NlNameCompare
+//
+// I_NetNameCompare but always takes UNICODE strings
+//
+////////////////////////////////////////////////////////////////////////
+
+#define NlNameCompare( _name1, _name2, _nametype ) \
+ I_NetNameCompare(NULL, (_name1), (_name2), (_nametype), 0 )
+
+
+//
+// Exit codes for NlExit
+//
+
+typedef enum {
+ DontLogError,
+ LogError,
+ LogErrorAndNtStatus,
+ LogErrorAndNetStatus
+} NL_EXIT_CODE;
+
+////////////////////////////////////////////////////////////////////////
+//
+// Procedure Forwards
+//
+////////////////////////////////////////////////////////////////////////
+
+//
+// error.c
+//
+
+NET_API_STATUS
+NlCleanup(
+ VOID
+ );
+
+VOID
+NlExit(
+ IN DWORD ServiceError,
+ IN DWORD Data,
+ IN NL_EXIT_CODE ExitCode,
+ IN LPWSTR ErrorString
+ );
+
+BOOL
+GiveInstallHints(
+ IN BOOL Started
+ );
+
+VOID
+NlControlHandler(
+ IN DWORD opcode
+ );
+
+VOID
+RaiseAlert(
+ IN DWORD alert_no,
+ IN LPWSTR *string_array
+ );
+
+//
+// Nlparse.c
+//
+
+BOOL
+Nlparse(
+ VOID
+ );
+
+//
+// announce.c
+//
+
+VOID
+NlRemovePendingBdc(
+ IN PSERVER_SESSION ServerSession
+ );
+
+VOID
+NlPrimaryAnnouncementFinish(
+ IN PSERVER_SESSION ServerSession,
+ IN DWORD DatabaseId,
+ IN PLARGE_INTEGER SerialNumber
+ );
+
+VOID
+NlPrimaryAnnouncementTimeout(
+ VOID
+ );
+
+VOID
+NlPrimaryAnnouncement(
+ IN DWORD AnnounceFlags
+ );
+
+#define ANNOUNCE_FORCE 0x01
+#define ANNOUNCE_CONTINUE 0x02
+#define ANNOUNCE_IMMEDIATE 0x04
+
+
+VOID
+NlLanmanPrimaryAnnouncement(
+ VOID
+ );
+
+VOID
+NlAnnouncePrimaryStart(
+ VOID
+ );
+
+
+
+//
+// lsrvutil.c
+//
+
+BOOL
+NlSetPrimaryName(
+ IN LPWSTR PrimaryName
+ );
+
+BOOL
+NlResetFirstTimeFullSync(
+ IN DWORD DBIndex
+ );
+
+NTSTATUS
+NlSessionSetup(
+ IN OUT PCLIENT_SESSION ClientSession
+ );
+
+BOOLEAN
+NlTimeHasElapsed(
+ IN LARGE_INTEGER StartTime,
+ IN DWORD Timeout
+ );
+
+BOOLEAN
+NlTimeToReauthenticate(
+ IN PCLIENT_SESSION ClientSession
+ );
+
+NTSTATUS
+NlNewSessionSetup(
+ IN LPWSTR primary
+ );
+
+NTSTATUS
+NlAuthenticate(
+ IN LPWSTR AccountName,
+ IN NETLOGON_SECURE_CHANNEL_TYPE SecureChannelType,
+ IN LPWSTR ComputerName,
+ IN PNETLOGON_CREDENTIAL ClientCredential,
+ OUT PNETLOGON_CREDENTIAL ServerCredential,
+ IN ULONG NegotiatedFlags
+ );
+
+NET_API_STATUS
+NlCreateShare(
+ LPWSTR SharePath,
+ LPWSTR ShareName
+ );
+
+NTSTATUS
+NlForceStartupSync(
+ PDB_INFO DBInfo
+ );
+
+BOOL
+NlCheckUpdateNotices(
+ IN PNETLOGON_DB_CHANGE UasChange,
+ IN DWORD UasChangeSize
+ );
+
+VOID
+NlStopReplicator(
+ VOID
+ );
+
+BOOL
+IsReplicatorRunning(
+ VOID
+ );
+
+BOOL
+NlStartReplicatorThread(
+ IN DWORD RandomSleep
+ );
+
+NTSTATUS
+NlSamOpenNamedUser(
+ IN LPWSTR UserName,
+ OUT SAMPR_HANDLE *UserHandle OPTIONAL,
+ OUT PULONG UserId OPTIONAL
+ );
+
+NTSTATUS
+NlChangePassword(
+ PCLIENT_SESSION ClientSession
+ );
+
+NTSTATUS
+NlCheckMachineAccount(
+ IN LPWSTR AccountName,
+ IN NETLOGON_SECURE_CHANNEL_TYPE SecureChannelType
+ );
+
+NTSTATUS
+NlOpenSecret(
+ IN PCLIENT_SESSION ClientSession,
+ IN ULONG DesiredAccess,
+ OUT PLSAPR_HANDLE SecretHandle
+ );
+
+NTSTATUS
+NlGetUserPriv(
+ IN ULONG GroupCount,
+ IN PGROUP_MEMBERSHIP Groups,
+ IN ULONG UserRelativeId,
+ OUT LPDWORD Priv,
+ OUT LPDWORD AuthFlags
+ );
+
+//
+// netlogon.c
+//
+
+int
+NlNetlogonMain(
+ IN DWORD argc,
+ IN LPWSTR *argv
+ );
+
+VOID
+NlScavenger(
+ IN LPVOID ScavengerParam
+ );
+
+BOOL
+IsScavengerRunning(
+ VOID
+ );
+
+VOID
+NlStopScavenger(
+ VOID
+ );
+
+BOOL
+NlStartScavengerThread(
+ );
+
+//
+// mailslot.c
+//
+
+BOOL
+NlBrowserOpen(
+ VOID
+ );
+
+VOID
+NlBrowserClose(
+ VOID
+ );
+
+NTSTATUS
+NlBrowserSendDatagram(
+ IN LPSTR OemServerName,
+ IN LPWSTR TransportName,
+ IN LPSTR OemMailslotName,
+ IN PVOID Buffer,
+ IN ULONG BufferSize
+ );
+
+VOID
+NlBrowserAddName(
+ VOID
+ );
+
+VOID
+NlMailslotPostRead(
+ IN BOOLEAN IgnoreDuplicatesOfPreviousMessage
+ );
+
+BOOL
+NlMailslotOverlappedResult(
+ OUT LPBYTE *Message,
+ OUT PULONG BytesRead,
+ OUT LPWSTR *Transport,
+ OUT PBOOLEAN IgnoreDuplicatesOfPreviousMessage
+ );
+
+//
+// oldstub.c
+//
+
+void _fgs__NETLOGON_DELTA_ENUM (NETLOGON_DELTA_ENUM * _source);
diff --git a/private/net/svcdlls/logonsrv/server/lsarepl.c b/private/net/svcdlls/logonsrv/server/lsarepl.c
new file mode 100644
index 000000000..ba140f4d6
--- /dev/null
+++ b/private/net/svcdlls/logonsrv/server/lsarepl.c
@@ -0,0 +1,2184 @@
+/*++
+
+Copyright (c) 1987-1992 Microsoft Corporation
+
+Module Name:
+
+ lsarepl.c
+
+Abstract:
+
+ Low level LSA Replication functions.
+
+Author:
+
+ 06-Apr-1992 (madana)
+ Created for LSA replication.
+
+Environment:
+
+ User mode only.
+ Contains NT-specific code.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+--*/
+
+//
+// Common include files.
+//
+
+#include <align.h>
+#include <logonsrv.h> // Include files common to entire service
+
+#include <replutil.h>
+#include <lsarepl.h>
+
+
+NTSTATUS
+NlPackLsaPolicy(
+ IN OUT PNETLOGON_DELTA_ENUM Delta,
+ IN PDB_INFO DBInfo,
+ IN LPDWORD BufferSize )
+/*++
+
+Routine Description:
+
+ Pack a description of the LSA policy info into the specified buffer.
+
+Arguments:
+
+ Delta: pointer to the delta structure where the new delta will
+ be returned.
+
+ DBInfo: pointer to the database info structure.
+
+ BufferSize: size of MIDL buffer that is consumed for this delta is
+ returned here.
+
+Return Value:
+
+ NT status code.
+
+--*/
+{
+ NTSTATUS Status;
+ ULONG i;
+
+ PLSAPR_SR_SECURITY_DESCRIPTOR SecurityDescriptor = NULL;
+
+ PLSAPR_POLICY_INFORMATION PolicyAuditLogInfo = NULL;
+ PLSAPR_POLICY_INFORMATION PolicyAuditEventsInfo = NULL;
+ PLSAPR_POLICY_INFORMATION PolicyPrimaryDomainInfo = NULL;
+ PLSAPR_POLICY_INFORMATION PolicyDefaultQuotaInfo = NULL;
+ PLSAPR_POLICY_INFORMATION PolicyModificationInfo = NULL;
+
+ PNETLOGON_DELTA_POLICY DeltaPolicy = NULL;
+
+ DEFPACKTIMER;
+ DEFLSATIMER;
+
+ INITPACKTIMER;
+ INITLSATIMER;
+
+ STARTPACKTIMER;
+
+ NlPrint((NL_SYNC_MORE, "Packing Policy Object\n"));
+
+ *BufferSize = 0;
+
+ Delta->DeltaType = AddOrChangeLsaPolicy;
+ Delta->DeltaUnion.DeltaPolicy = NULL;
+
+ QUERY_LSA_SECOBJ_INFO(DBInfo->DBHandle);
+
+ STARTLSATIMER;
+
+ Status = LsarQueryInformationPolicy(
+ DBInfo->DBHandle,
+ PolicyAuditLogInformation,
+ &PolicyAuditLogInfo);
+
+ STOPLSATIMER;
+
+ if (!NT_SUCCESS(Status)) {
+ PolicyAuditLogInfo = NULL;
+ goto Cleanup;
+ }
+
+ STARTLSATIMER;
+
+ Status = LsarQueryInformationPolicy(
+ DBInfo->DBHandle,
+ PolicyAuditEventsInformation,
+ &PolicyAuditEventsInfo);
+
+ STOPLSATIMER;
+
+ if (!NT_SUCCESS(Status)) {
+ PolicyAuditEventsInfo = NULL;
+ goto Cleanup;
+ }
+
+ STARTLSATIMER;
+
+ Status = LsarQueryInformationPolicy(
+ DBInfo->DBHandle,
+ PolicyPrimaryDomainInformation,
+ &PolicyPrimaryDomainInfo);
+
+ STOPLSATIMER;
+
+ if (!NT_SUCCESS(Status)) {
+ PolicyPrimaryDomainInfo = NULL;
+ goto Cleanup;
+ }
+
+
+ STARTLSATIMER;
+
+ Status = LsarQueryInformationPolicy(
+ DBInfo->DBHandle,
+ PolicyDefaultQuotaInformation,
+ &PolicyDefaultQuotaInfo);
+
+ STOPLSATIMER;
+
+ if (!NT_SUCCESS(Status)) {
+ PolicyDefaultQuotaInfo = NULL;
+ goto Cleanup;
+ }
+
+ STARTLSATIMER;
+
+ Status = LsarQueryInformationPolicy(
+ DBInfo->DBHandle,
+ PolicyModificationInformation,
+ &PolicyModificationInfo);
+
+ STOPLSATIMER;
+
+ if (!NT_SUCCESS(Status)) {
+ PolicyModificationInfo = NULL;
+ goto Cleanup;
+ }
+
+ //
+ // Fill in the delta structure
+ //
+
+ //
+ // copy SID info (There is only one policy database. It has no SID).
+ //
+
+ Delta->DeltaID.Sid = NULL;
+
+ //
+ // allocate delta buffer
+ //
+
+ DeltaPolicy = (PNETLOGON_DELTA_POLICY)
+ MIDL_user_allocate( sizeof(NETLOGON_DELTA_POLICY) );
+
+ if( DeltaPolicy == NULL ) {
+
+ Status = STATUS_NO_MEMORY;
+ goto Cleanup;
+ }
+
+ //
+ // wipe off the buffer so that cleanup will not be in fault.
+ //
+
+ RtlZeroMemory( DeltaPolicy, sizeof(NETLOGON_DELTA_POLICY) );
+
+ Delta->DeltaUnion.DeltaPolicy = DeltaPolicy;
+ *BufferSize += sizeof(NETLOGON_DELTA_POLICY);
+
+ DeltaPolicy->MaximumLogSize =
+ PolicyAuditLogInfo->PolicyAuditLogInfo.MaximumLogSize;
+ DeltaPolicy->AuditRetentionPeriod;
+ PolicyAuditLogInfo->PolicyAuditLogInfo.AuditRetentionPeriod;
+
+ DeltaPolicy->AuditingMode =
+ PolicyAuditEventsInfo->
+ PolicyAuditEventsInfo.AuditingMode;
+ DeltaPolicy->MaximumAuditEventCount =
+ PolicyAuditEventsInfo->
+ PolicyAuditEventsInfo.MaximumAuditEventCount;
+
+ *BufferSize += NlCopyData(
+ (LPBYTE *)&(PolicyAuditEventsInfo->
+ PolicyAuditEventsInfo.EventAuditingOptions),
+ (LPBYTE *)&(DeltaPolicy->EventAuditingOptions),
+ (DeltaPolicy->MaximumAuditEventCount + 1) *
+ sizeof(ULONG));
+
+ // Tell the BDC to 'set' these bits and not just 'or' them in to the current ones
+ for ( i=0; i<DeltaPolicy->MaximumAuditEventCount; i++ ) {
+ DeltaPolicy->EventAuditingOptions[i] |= POLICY_AUDIT_EVENT_NONE;
+ }
+
+ //
+ // sanitity check, EventAuditingOptions size is ULONG size.
+ //
+
+ NlAssert(sizeof(*(PolicyAuditEventsInfo->
+ PolicyAuditEventsInfo.EventAuditingOptions)) ==
+ sizeof(ULONG) );
+
+ *BufferSize += NlCopyUnicodeString(
+ (PUNICODE_STRING)&PolicyPrimaryDomainInfo->
+ PolicyPrimaryDomainInfo.Name,
+ &DeltaPolicy->PrimaryDomainName );
+
+ *BufferSize += NlCopyData(
+ (LPBYTE *)&(PolicyPrimaryDomainInfo->
+ PolicyPrimaryDomainInfo.Sid),
+ (LPBYTE *)&(DeltaPolicy->PrimaryDomainSid),
+ RtlLengthSid((PSID)(PolicyPrimaryDomainInfo->
+ PolicyPrimaryDomainInfo.Sid) ));
+
+ DeltaPolicy->QuotaLimits.PagedPoolLimit =
+ PolicyDefaultQuotaInfo->PolicyDefaultQuotaInfo.QuotaLimits.PagedPoolLimit;
+ DeltaPolicy->QuotaLimits.NonPagedPoolLimit =
+ PolicyDefaultQuotaInfo->PolicyDefaultQuotaInfo.QuotaLimits.NonPagedPoolLimit;
+ DeltaPolicy->QuotaLimits.MinimumWorkingSetSize =
+ PolicyDefaultQuotaInfo->PolicyDefaultQuotaInfo.QuotaLimits.MinimumWorkingSetSize;
+ DeltaPolicy->QuotaLimits.MaximumWorkingSetSize =
+ PolicyDefaultQuotaInfo->PolicyDefaultQuotaInfo.QuotaLimits.MaximumWorkingSetSize;
+ DeltaPolicy->QuotaLimits.PagefileLimit =
+ PolicyDefaultQuotaInfo->PolicyDefaultQuotaInfo.QuotaLimits.PagefileLimit;
+
+ NEW_TO_OLD_LARGE_INTEGER(
+ PolicyDefaultQuotaInfo->PolicyDefaultQuotaInfo.QuotaLimits.TimeLimit,
+ DeltaPolicy->QuotaLimits.TimeLimit );
+
+ NEW_TO_OLD_LARGE_INTEGER(
+ PolicyModificationInfo->PolicyModificationInfo.ModifiedId,
+ DeltaPolicy->ModifiedId );
+
+ NEW_TO_OLD_LARGE_INTEGER(
+ PolicyModificationInfo->PolicyModificationInfo.DatabaseCreationTime,
+ DeltaPolicy->DatabaseCreationTime );
+
+
+ DELTA_SECOBJ_INFO(DeltaPolicy);
+
+ INIT_PLACE_HOLDER(DeltaPolicy);
+
+ //
+ // All Done
+ //
+
+ Status = STATUS_SUCCESS;
+
+Cleanup:
+
+ STARTLSATIMER;
+
+ if ( SecurityDescriptor != NULL ) {
+ LsaIFree_LSAPR_SR_SECURITY_DESCRIPTOR( SecurityDescriptor );
+ }
+
+ if ( PolicyAuditLogInfo != NULL ) {
+ LsaIFree_LSAPR_POLICY_INFORMATION(
+ PolicyAuditLogInformation,
+ PolicyAuditLogInfo );
+ }
+
+ if ( PolicyAuditEventsInfo != NULL ) {
+ LsaIFree_LSAPR_POLICY_INFORMATION(
+ PolicyAuditEventsInformation,
+ PolicyAuditEventsInfo );
+ }
+
+ if ( PolicyPrimaryDomainInfo != NULL ) {
+ LsaIFree_LSAPR_POLICY_INFORMATION(
+ PolicyPrimaryDomainInformation,
+ PolicyPrimaryDomainInfo );
+ }
+
+ if ( PolicyDefaultQuotaInfo != NULL ) {
+ LsaIFree_LSAPR_POLICY_INFORMATION(
+ PolicyDefaultQuotaInformation,
+ PolicyDefaultQuotaInfo );
+ }
+
+ if ( PolicyModificationInfo != NULL ) {
+ LsaIFree_LSAPR_POLICY_INFORMATION(
+ PolicyModificationInformation,
+ PolicyModificationInfo );
+ }
+
+ STOPLSATIMER;
+
+ if( !NT_SUCCESS(Status) ) {
+ NlFreeDBDelta( Delta );
+ *BufferSize = 0;
+ }
+
+
+ STOPPACKTIMER;
+
+ NlPrint((NL_REPL_OBJ_TIME,"Time taken to pack POLICY object:\n"));
+ PRINTPACKTIMER;
+ PRINTLSATIMER;
+
+ return(Status);
+}
+
+
+NTSTATUS
+NlPackLsaTDomain(
+ IN PSID Sid,
+ IN OUT PNETLOGON_DELTA_ENUM Delta,
+ IN PDB_INFO DBInfo,
+ IN LPDWORD BufferSize )
+/*++
+
+Routine Description:
+
+ Pack a description of the specified trusted domain info into the
+ specified buffer.
+
+Arguments:
+
+ Sid - The SID of the trusted domain.
+
+ Delta: pointer to the delta structure where the new delta will
+ be returned.
+
+ DBInfo: pointer to the database info structure.
+
+ BufferSize: size of MIDL buffer that is consumed for this delta is
+ returned here.
+
+Return Value:
+
+ NT status code.
+
+--*/
+{
+ NTSTATUS Status;
+
+ LSAPR_HANDLE TrustedDomainHandle = NULL;
+ PLSAPR_TRUSTED_DOMAIN_INFO TrustedControllersInfo = NULL;
+ PLSAPR_TRUSTED_DOMAIN_INFO TrustedDomainNameInfo = NULL;
+ PLSAPR_TRUSTED_DOMAIN_INFO TrustedPosixOffsetInfo = NULL;
+
+ PLSAPR_SR_SECURITY_DESCRIPTOR SecurityDescriptor = NULL;
+
+ PNETLOGON_DELTA_TRUSTED_DOMAINS DeltaTDomain = NULL;
+
+ DWORD i;
+ DWORD Entries;
+ DWORD Size = 0;
+ PLSAPR_UNICODE_STRING UnicodeControllerName;
+
+ DEFPACKTIMER;
+ DEFLSATIMER;
+
+ INITPACKTIMER;
+ INITLSATIMER;
+
+ STARTPACKTIMER;
+
+ NlPrint((NL_SYNC_MORE, "Packing Trusted Domain Object\n"));
+
+ *BufferSize = 0;
+
+ Delta->DeltaType = AddOrChangeLsaTDomain;
+ Delta->DeltaID.Sid = NULL;
+ Delta->DeltaUnion.DeltaTDomains = NULL;
+
+ //
+ // open trusted domain
+ //
+
+ STARTLSATIMER;
+
+ Status = LsarOpenTrustedDomain(
+ DBInfo->DBHandle,
+ (PLSAPR_SID)Sid,
+ 0,
+ &TrustedDomainHandle );
+
+ STOPLSATIMER;
+
+ if (!NT_SUCCESS(Status)) {
+ TrustedDomainHandle = NULL;
+ goto Cleanup;
+ }
+
+ QUERY_LSA_SECOBJ_INFO(TrustedDomainHandle);
+
+ STARTLSATIMER;
+
+ Status = LsarQueryInfoTrustedDomain(
+ TrustedDomainHandle,
+ TrustedControllersInformation,
+ &TrustedControllersInfo );
+
+ STOPLSATIMER;
+
+ if (!NT_SUCCESS(Status)) {
+ TrustedControllersInfo = NULL;
+ goto Cleanup;
+ }
+
+ STARTLSATIMER;
+
+ Status = LsarQueryInfoTrustedDomain(
+ TrustedDomainHandle,
+ TrustedDomainNameInformation,
+ &TrustedDomainNameInfo );
+
+ STOPLSATIMER;
+
+ if (!NT_SUCCESS(Status)) {
+ TrustedDomainNameInfo = NULL;
+ goto Cleanup;
+ }
+
+ NlPrint((NL_SYNC_MORE,
+ "\t Trusted Domain Object name %wZ\n",
+ (PUNICODE_STRING)&TrustedDomainNameInfo->
+ TrustedDomainNameInfo.Name ));
+
+ STARTLSATIMER;
+
+ Status = LsarQueryInfoTrustedDomain(
+ TrustedDomainHandle,
+ TrustedPosixOffsetInformation,
+ &TrustedPosixOffsetInfo );
+
+ STOPLSATIMER;
+
+ if (!NT_SUCCESS(Status)) {
+ TrustedPosixOffsetInfo = NULL;
+ goto Cleanup;
+ }
+
+ //
+ // Fill in the delta structure
+ //
+
+ //
+ // copy SID info
+ //
+
+ Delta->DeltaID.Sid = MIDL_user_allocate( RtlLengthSid(Sid) );
+
+
+ if( Delta->DeltaID.Sid == NULL ) {
+
+ Status = STATUS_NO_MEMORY;
+ goto Cleanup;
+ }
+
+ RtlCopyMemory( Delta->DeltaID.Sid, Sid, RtlLengthSid(Sid) );
+
+ //
+ // allocate delta buffer
+ //
+
+ DeltaTDomain = (PNETLOGON_DELTA_TRUSTED_DOMAINS)
+ MIDL_user_allocate( sizeof(NETLOGON_DELTA_TRUSTED_DOMAINS) );
+
+ if( DeltaTDomain == NULL ) {
+
+ Status = STATUS_NO_MEMORY;
+ goto Cleanup;
+ }
+
+ //
+ // wipe off the buffer so that cleanup will not be in fault.
+ //
+
+ RtlZeroMemory( DeltaTDomain, sizeof(NETLOGON_DELTA_TRUSTED_DOMAINS) );
+
+ Delta->DeltaUnion.DeltaTDomains = DeltaTDomain;
+ *BufferSize += sizeof(NETLOGON_DELTA_TRUSTED_DOMAINS);
+
+ *BufferSize += NlCopyUnicodeString(
+ (PUNICODE_STRING)&TrustedDomainNameInfo->
+ TrustedDomainNameInfo.Name,
+ &DeltaTDomain->DomainName );
+
+ Entries = DeltaTDomain->NumControllerEntries =
+ TrustedControllersInfo->TrustedControllersInfo.Entries;
+
+ //
+ // compute size of controller names.
+ //
+
+ for( i = 0, UnicodeControllerName
+ = TrustedControllersInfo->TrustedControllersInfo.Names;
+ i < Entries;
+ i++, UnicodeControllerName++ ) {
+
+ Size += (sizeof(LSAPR_UNICODE_STRING) +
+ UnicodeControllerName->Length);
+ }
+
+ *BufferSize += NlCopyData(
+ (LPBYTE *)&(TrustedControllersInfo->
+ TrustedControllersInfo.Names),
+ (LPBYTE *)&(DeltaTDomain->ControllerNames),
+ Size);
+
+ DELTA_SECOBJ_INFO(DeltaTDomain);
+ INIT_PLACE_HOLDER(DeltaTDomain);
+
+ //
+ // send Posix Offset info across using place holder.
+ //
+
+ DeltaTDomain->DummyLong1 =
+ TrustedPosixOffsetInfo->TrustedPosixOffsetInfo.Offset;
+
+ //
+ // All Done
+ //
+
+ Status = STATUS_SUCCESS;
+
+Cleanup:
+
+ STARTLSATIMER;
+
+ if ( TrustedDomainHandle != NULL ) {
+ LsarClose( &TrustedDomainHandle );
+ }
+
+ if ( SecurityDescriptor != NULL ) {
+ LsaIFree_LSAPR_SR_SECURITY_DESCRIPTOR( SecurityDescriptor );
+ }
+
+ if ( TrustedControllersInfo != NULL ) {
+ LsaIFree_LSAPR_TRUSTED_DOMAIN_INFO(
+ TrustedControllersInformation,
+ TrustedControllersInfo );
+ }
+
+ if ( TrustedDomainNameInfo != NULL ) {
+ LsaIFree_LSAPR_TRUSTED_DOMAIN_INFO(
+ TrustedDomainNameInformation,
+ TrustedDomainNameInfo );
+ }
+
+ if ( TrustedPosixOffsetInfo != NULL ) {
+ LsaIFree_LSAPR_TRUSTED_DOMAIN_INFO(
+ TrustedPosixOffsetInformation,
+ TrustedPosixOffsetInfo );
+ }
+
+ STOPLSATIMER;
+
+ if( !NT_SUCCESS(Status) ) {
+ NlFreeDBDelta( Delta );
+ *BufferSize = 0;
+ }
+
+ STOPPACKTIMER;
+
+ NlPrint((NL_REPL_OBJ_TIME,"Time taken to pack TDOMAIN object:\n"));
+ PRINTPACKTIMER;
+ PRINTLSATIMER;
+
+ return(Status);
+}
+
+
+NTSTATUS
+NlPackLsaAccount(
+ IN PSID Sid,
+ IN OUT PNETLOGON_DELTA_ENUM Delta,
+ IN PDB_INFO DBInfo,
+ IN LPDWORD BufferSize,
+ IN PSESSION_INFO SessionInfo
+ )
+/*++
+
+Routine Description:
+
+ Pack a description of the specified LSA account info into the
+ specified buffer.
+
+Arguments:
+
+ Sid - The SID of the LSA account.
+
+ Delta: pointer to the delta structure where the new delta will
+ be returned.
+
+ DBInfo: pointer to the database info structure.
+
+ BufferSize: size of MIDL buffer that is consumed for this delta is
+ returned here.
+
+ SessionInfo: Info describing BDC that's calling us
+
+Return Value:
+
+ NT status code.
+
+--*/
+{
+ NTSTATUS Status;
+
+ PLSAPR_SR_SECURITY_DESCRIPTOR SecurityDescriptor = NULL;
+
+ PNETLOGON_DELTA_ACCOUNTS DeltaAccount = NULL;
+ LSAPR_HANDLE AccountHandle = NULL;
+
+ PLSAPR_PRIVILEGE_SET Privileges = NULL;
+ QUOTA_LIMITS QuotaLimits;
+ ULONG SystemAccessFlags;
+
+ PULONG PrivilegeAttributes;
+ PUNICODE_STRING PrivilegeNames;
+ LUID MachineAccountPrivilegeLuid;
+ DWORD CopiedPrivilegeCount;
+
+ DWORD i;
+ DWORD Size;
+
+ DEFPACKTIMER;
+ DEFLSATIMER;
+
+ INITPACKTIMER;
+ INITLSATIMER;
+
+ STARTPACKTIMER;
+
+ NlPrint((NL_SYNC_MORE, "Packing Lsa Account Object\n"));
+
+ *BufferSize = 0;
+ MachineAccountPrivilegeLuid = RtlConvertLongToLuid(SE_MACHINE_ACCOUNT_PRIVILEGE);
+
+ Delta->DeltaType = AddOrChangeLsaAccount;
+ Delta->DeltaID.Sid = NULL;
+ Delta->DeltaUnion.DeltaAccounts = NULL;
+
+ //
+ // open lsa account
+ //
+
+ STARTLSATIMER;
+
+ Status = LsarOpenAccount(
+ DBInfo->DBHandle,
+ (PLSAPR_SID)Sid,
+ 0,
+ &AccountHandle );
+
+ STOPLSATIMER;
+
+ if (!NT_SUCCESS(Status)) {
+ AccountHandle = NULL;
+ goto Cleanup;
+ }
+
+ QUERY_LSA_SECOBJ_INFO(AccountHandle);
+
+ STARTLSATIMER;
+
+ Status = LsarEnumeratePrivilegesAccount(
+ AccountHandle,
+ &Privileges );
+
+ STOPLSATIMER;
+
+ if (!NT_SUCCESS(Status)) {
+ Privileges = NULL;
+ goto Cleanup;
+ }
+
+ STARTLSATIMER;
+
+ Status = LsarGetQuotasForAccount(
+ AccountHandle,
+ &QuotaLimits );
+
+ STOPLSATIMER;
+
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+
+ STARTLSATIMER;
+
+ Status = LsarGetSystemAccessAccount(
+ AccountHandle,
+ &SystemAccessFlags );
+
+ STOPLSATIMER;
+
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+
+ //
+ // Fill in the delta structure
+ //
+
+ //
+ // copy SID info
+ //
+
+ Delta->DeltaID.Sid = MIDL_user_allocate( RtlLengthSid(Sid) );
+
+
+ if( Delta->DeltaID.Sid == NULL ) {
+
+ Status = STATUS_NO_MEMORY;
+ goto Cleanup;
+ }
+
+ RtlCopyMemory( Delta->DeltaID.Sid, Sid, RtlLengthSid(Sid) );
+
+ //
+ // allocate delta buffer
+ //
+
+ DeltaAccount = (PNETLOGON_DELTA_ACCOUNTS)
+ MIDL_user_allocate( sizeof(NETLOGON_DELTA_ACCOUNTS) );
+
+ if( DeltaAccount == NULL ) {
+
+ Status = STATUS_NO_MEMORY;
+ goto Cleanup;
+ }
+
+ //
+ // wipe off the buffer so that cleanup will not be in fault.
+ //
+
+ RtlZeroMemory( DeltaAccount, sizeof(NETLOGON_DELTA_ACCOUNTS) );
+
+ Delta->DeltaUnion.DeltaAccounts = DeltaAccount;
+ *BufferSize += sizeof(NETLOGON_DELTA_ACCOUNTS);
+
+ DeltaAccount->PrivilegeControl = Privileges->Control;
+
+ DeltaAccount->PrivilegeEntries = 0;
+ DeltaAccount->PrivilegeAttributes = NULL;
+ DeltaAccount->PrivilegeNames = NULL;
+
+ Size = Privileges->PrivilegeCount * sizeof(ULONG);
+
+ PrivilegeAttributes = MIDL_user_allocate( Size );
+
+ if( PrivilegeAttributes == NULL ) {
+
+ Status = STATUS_NO_MEMORY;
+ goto Cleanup;
+ }
+
+ DeltaAccount->PrivilegeAttributes = PrivilegeAttributes;
+ *BufferSize += Size;
+
+ Size = Privileges->PrivilegeCount * sizeof(UNICODE_STRING);
+
+ PrivilegeNames = MIDL_user_allocate( Size );
+
+ if( PrivilegeNames == NULL ) {
+
+ Status = STATUS_NO_MEMORY;
+ goto Cleanup;
+ }
+
+ DeltaAccount->PrivilegeNames = PrivilegeNames;
+ *BufferSize += Size;
+
+ //
+ // now fill up Privilege Attributes and Names
+ //
+
+ CopiedPrivilegeCount = 0;
+ for( i = 0; i < Privileges->PrivilegeCount; i++ ) {
+
+ //
+ // Don't replicate SeMachineAccount privilege to NT 1.0. It can't handle it.
+ // (Use the SUPPORTS_ACCOUNT_LOCKOUT bit so we don't have to consume
+ // another bit.)
+ //
+ if ( (SessionInfo->NegotiatedFlags & NETLOGON_SUPPORTS_ACCOUNT_LOCKOUT) ||
+ (!RtlEqualLuid((PLUID)(&Privileges->Privilege[i].Luid),
+ &MachineAccountPrivilegeLuid ))) {
+
+ PLSAPR_UNICODE_STRING PrivName = NULL;
+
+ *PrivilegeAttributes = Privileges->Privilege[i].Attributes;
+
+
+ //
+ // convert LUID to Name
+ //
+
+ STARTLSATIMER;
+
+ Status = LsarLookupPrivilegeName(
+ DBInfo->DBHandle,
+ (PLUID)&Privileges->Privilege[i].Luid,
+ &PrivName );
+
+ STOPLSATIMER;
+
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+
+ *BufferSize += NlCopyUnicodeString(
+ (PUNICODE_STRING)PrivName,
+ PrivilegeNames );
+
+ LsaIFree_LSAPR_UNICODE_STRING( PrivName );
+ CopiedPrivilegeCount ++;
+ PrivilegeAttributes++;
+ PrivilegeNames++;
+ } else {
+ NlPrint((NL_SYNC_MORE,
+ "NlPackLsaAccount: ignored privilege %ld %ld\n",
+ (PLUID)(&Privileges->Privilege[i].Luid)->HighPart,
+ (PLUID)(&Privileges->Privilege[i].Luid)->LowPart ));
+ }
+ }
+ DeltaAccount->PrivilegeEntries = CopiedPrivilegeCount;
+
+ DeltaAccount->QuotaLimits.PagedPoolLimit = QuotaLimits.PagedPoolLimit;
+ DeltaAccount->QuotaLimits.NonPagedPoolLimit = QuotaLimits.NonPagedPoolLimit;
+ DeltaAccount->QuotaLimits.MinimumWorkingSetSize = QuotaLimits.MinimumWorkingSetSize;
+ DeltaAccount->QuotaLimits.MaximumWorkingSetSize = QuotaLimits.MaximumWorkingSetSize;
+ DeltaAccount->QuotaLimits.PagefileLimit = QuotaLimits.PagefileLimit;
+ NEW_TO_OLD_LARGE_INTEGER(
+ QuotaLimits.TimeLimit,
+ DeltaAccount->QuotaLimits.TimeLimit );
+
+ DeltaAccount->SystemAccessFlags = SystemAccessFlags;
+
+ DELTA_SECOBJ_INFO(DeltaAccount);
+ INIT_PLACE_HOLDER(DeltaAccount);
+
+ //
+ // All Done
+ //
+
+ Status = STATUS_SUCCESS;
+
+Cleanup:
+
+ STARTLSATIMER;
+
+ if ( AccountHandle != NULL ) {
+ LsarClose( &AccountHandle );
+ }
+
+ if ( SecurityDescriptor != NULL ) {
+ LsaIFree_LSAPR_SR_SECURITY_DESCRIPTOR( SecurityDescriptor );
+ }
+
+ if ( Privileges != NULL ) {
+ LsaIFree_LSAPR_PRIVILEGE_SET( Privileges );
+ }
+
+ STOPLSATIMER;
+
+ if( !NT_SUCCESS(Status) ) {
+ NlFreeDBDelta( Delta );
+ *BufferSize = 0;
+ }
+
+ STOPPACKTIMER;
+
+ NlPrint((NL_REPL_OBJ_TIME,"Time taken to pack LSAACCOUNT object:\n"));
+ PRINTPACKTIMER;
+ PRINTLSATIMER;
+
+ return(Status);
+
+}
+
+
+
+NTSTATUS
+NlPackLsaSecret(
+ IN PUNICODE_STRING Name,
+ IN OUT PNETLOGON_DELTA_ENUM Delta,
+ IN PDB_INFO DBInfo,
+ IN LPDWORD BufferSize,
+ IN PSESSION_INFO SessionInfo
+ )
+/*++
+
+Routine Description:
+
+ Pack a description of the specified LSA secret info into the
+ specified buffer.
+
+Arguments:
+
+ Name - Name of the secret.
+
+ Delta: pointer to the delta structure where the new delta will
+ be returned.
+
+ DBInfo: pointer to the database info structure.
+
+ BufferSize: size of MIDL buffer that is consumed for this delta is
+ returned here.
+
+ SessionInfo: Information shared between BDC and PDC
+
+Return Value:
+
+ NT status code.
+
+--*/
+{
+ NTSTATUS Status;
+
+ PLSAPR_SR_SECURITY_DESCRIPTOR SecurityDescriptor = NULL;
+
+ LSAPR_HANDLE SecretHandle = NULL;
+
+ PNETLOGON_DELTA_SECRET DeltaSecret = NULL;
+
+ PLSAPR_CR_CIPHER_VALUE CurrentValue = NULL;
+ PLSAPR_CR_CIPHER_VALUE OldValue = NULL;
+ LARGE_INTEGER CurrentValueSetTime;
+ LARGE_INTEGER OldValueSetTime;
+
+ DEFPACKTIMER;
+ DEFLSATIMER;
+
+ INITPACKTIMER;
+ INITLSATIMER;
+
+ STARTPACKTIMER;
+
+ NlPrint((NL_SYNC_MORE, "Packing Secret Object: %wZ\n", Name));
+
+ //
+ // we should be packing only GLOBAL secrets
+ //
+
+ NlAssert(
+ (Name->Length / sizeof(WCHAR) >
+ LSA_GLOBAL_SECRET_PREFIX_LENGTH ) &&
+ (_wcsnicmp( Name->Buffer,
+ LSA_GLOBAL_SECRET_PREFIX,
+ LSA_GLOBAL_SECRET_PREFIX_LENGTH ) == 0) );
+
+ *BufferSize = 0;
+
+ Delta->DeltaType = AddOrChangeLsaSecret;
+ Delta->DeltaID.Name = NULL;
+ Delta->DeltaUnion.DeltaPolicy = NULL;
+
+ //
+ // open lsa account
+ //
+
+ STARTLSATIMER;
+
+ Status = LsarOpenSecret(
+ DBInfo->DBHandle,
+ (PLSAPR_UNICODE_STRING)Name,
+ 0,
+ &SecretHandle );
+
+ STOPLSATIMER;
+
+ if (!NT_SUCCESS(Status)) {
+ SecretHandle = NULL;
+ goto Cleanup;
+ }
+
+ QUERY_LSA_SECOBJ_INFO(SecretHandle);
+
+ STARTLSATIMER;
+
+ Status = LsarQuerySecret(
+ SecretHandle,
+ &CurrentValue,
+ &CurrentValueSetTime,
+ &OldValue,
+ &OldValueSetTime );
+
+ STOPLSATIMER;
+
+ if (!NT_SUCCESS(Status)) {
+ CurrentValue = NULL;
+ OldValue = NULL;
+ goto Cleanup;
+ }
+
+ //
+ // Fill in the delta structure
+ //
+
+ //
+ // copy ID field
+ //
+
+ Delta->DeltaID.Name =
+ MIDL_user_allocate( Name->Length + sizeof(WCHAR) );
+
+ if( Delta->DeltaID.Name == NULL ) {
+
+ Status = STATUS_NO_MEMORY;
+ goto Cleanup;
+ }
+
+ wcsncpy( Delta->DeltaID.Name,
+ Name->Buffer,
+ Name->Length / sizeof(WCHAR) );
+
+ //
+ // terminate string
+ //
+
+ Delta->DeltaID.Name[ Name->Length / sizeof(WCHAR) ] = L'\0';
+
+
+ DeltaSecret = (PNETLOGON_DELTA_SECRET)
+ MIDL_user_allocate( sizeof(NETLOGON_DELTA_SECRET) );
+
+ if( DeltaSecret == NULL ) {
+ Status = STATUS_NO_MEMORY;
+ goto Cleanup;
+ }
+
+ //
+ // wipe off the buffer so that cleanup will not be in fault.
+ //
+
+ RtlZeroMemory( DeltaSecret, sizeof(NETLOGON_DELTA_SECRET) );
+
+ Delta->DeltaUnion.DeltaSecret = DeltaSecret;
+ *BufferSize += sizeof(NETLOGON_DELTA_SECRET);
+
+ NEW_TO_OLD_LARGE_INTEGER(
+ CurrentValueSetTime,
+ DeltaSecret->CurrentValueSetTime );
+
+ NEW_TO_OLD_LARGE_INTEGER(
+ OldValueSetTime,
+ DeltaSecret->OldValueSetTime );
+
+ if( CurrentValue != NULL && CurrentValue->Buffer != NULL && CurrentValue->Length != 0) {
+
+ //
+ // Copy the secret into an allocated buffer and encrypt it in place.
+ // Don't use the LSA's buffer since it a ALLOCATE_ALL_NODES.
+ //
+
+ DeltaSecret->CurrentValue.Buffer =
+ MIDL_user_allocate( CurrentValue->Length );
+
+ if( DeltaSecret->CurrentValue.Buffer == NULL ) {
+ Status = STATUS_NO_MEMORY;
+ goto Cleanup;
+ }
+
+ DeltaSecret->CurrentValue.Length =
+ DeltaSecret->CurrentValue.MaximumLength = CurrentValue->Length;
+ RtlCopyMemory( DeltaSecret->CurrentValue.Buffer,
+ CurrentValue->Buffer,
+ CurrentValue->Length );
+
+
+ //
+ // secret values are encrypted using session keys.
+ //
+
+ Status = NlEncryptSensitiveData(
+ (PCRYPT_BUFFER) &DeltaSecret->CurrentValue,
+ SessionInfo );
+
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+
+ } else {
+
+ DeltaSecret->CurrentValue.Length = 0;
+ DeltaSecret->CurrentValue.MaximumLength = 0;
+ DeltaSecret->CurrentValue.Buffer = NULL;
+ }
+
+ *BufferSize += DeltaSecret->CurrentValue.MaximumLength;
+
+ if( OldValue != NULL && OldValue->Buffer != NULL && OldValue->Length != 0 ) {
+
+ //
+ // Copy the secret into an allocated buffer and encrypt it in place.
+ // Don't use the LSA's buffer since it a ALLOCATE_ALL_NODES.
+ //
+
+ DeltaSecret->OldValue.Buffer =
+ MIDL_user_allocate( OldValue->Length );
+
+ if( DeltaSecret->OldValue.Buffer == NULL ) {
+ Status = STATUS_NO_MEMORY;
+ goto Cleanup;
+ }
+
+ DeltaSecret->OldValue.Length =
+ DeltaSecret->OldValue.MaximumLength = OldValue->Length;
+ RtlCopyMemory( DeltaSecret->OldValue.Buffer,
+ OldValue->Buffer,
+ OldValue->Length );
+
+
+ //
+ // secret values are encrypted using session keys.
+ //
+
+ Status = NlEncryptSensitiveData(
+ (PCRYPT_BUFFER) &DeltaSecret->OldValue,
+ SessionInfo );
+
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+
+ } else {
+
+ DeltaSecret->OldValue.Length = 0;
+ DeltaSecret->OldValue.MaximumLength = 0;
+ DeltaSecret->OldValue.Buffer = NULL;
+ }
+
+ *BufferSize += DeltaSecret->OldValue.MaximumLength;
+
+ DELTA_SECOBJ_INFO(DeltaSecret);
+ INIT_PLACE_HOLDER(DeltaSecret);
+
+ //
+ // All Done
+ //
+
+ Status = STATUS_SUCCESS;
+
+Cleanup:
+
+ STARTLSATIMER;
+
+ if ( SecretHandle != NULL ) {
+ LsarClose( &SecretHandle );
+ }
+
+ if ( SecurityDescriptor != NULL ) {
+ LsaIFree_LSAPR_SR_SECURITY_DESCRIPTOR( SecurityDescriptor );
+ }
+
+ if( CurrentValue != NULL ) {
+ LsaIFree_LSAPR_CR_CIPHER_VALUE( CurrentValue );
+ }
+
+ if( OldValue != NULL ) {
+ LsaIFree_LSAPR_CR_CIPHER_VALUE( OldValue );
+ }
+
+ STOPLSATIMER;
+
+ if( !NT_SUCCESS(Status) ) {
+ NlFreeDBDelta( Delta );
+ *BufferSize = 0;
+ }
+
+ STOPPACKTIMER;
+
+ NlPrint((NL_REPL_OBJ_TIME,"Time taken to pack SECRET object:\n"));
+ PRINTPACKTIMER;
+ PRINTLSATIMER;
+
+ return(Status);
+
+}
+
+
+NTSTATUS
+NlUnpackLsaPolicy(
+ IN PNETLOGON_DELTA_POLICY DeltaLsaPolicy,
+ IN PDB_INFO DBInfo )
+/*++
+
+Routine Description:
+
+ Set the LSA policy info to look like the specified buffer.
+
+Arguments:
+
+ DeltaLsaPolicy - Description of the LSA policy.
+
+ DBInfo - pointer to the database info structure.
+
+Return Value:
+
+ NT status code.
+
+--*/
+{
+ NTSTATUS Status;
+ ULONG i;
+
+ LSAPR_SR_SECURITY_DESCRIPTOR SecurityDescriptor;
+
+ LSAPR_POLICY_INFORMATION PolicyInformation;
+
+ DEFUNPACKTIMER;
+ DEFLSATIMER;
+
+ INITUNPACKTIMER;
+ INITLSATIMER;
+
+ STARTUNPACKTIMER;
+
+ NlPrint((NL_SYNC_MORE, "UnPacking Policy Object\n"));
+
+ SET_LSA_SECOBJ_INFO(DeltaLsaPolicy, DBInfo->DBHandle);
+
+ //
+ // Set the audit log information
+ //
+ // ?? Query PolicyAuditLogInformation before set.
+ //
+
+
+ PolicyInformation.PolicyAuditLogInfo.AuditLogPercentFull = 0;
+ // ignored for set
+ PolicyInformation.PolicyAuditLogInfo.MaximumLogSize =
+ DeltaLsaPolicy->MaximumLogSize;
+
+ OLD_TO_NEW_LARGE_INTEGER(
+ DeltaLsaPolicy->AuditRetentionPeriod,
+ PolicyInformation.PolicyAuditLogInfo.AuditRetentionPeriod );
+
+ PolicyInformation.PolicyAuditLogInfo.AuditLogFullShutdownInProgress = 0;
+ // ignored for set
+ // PolicyInformation.PolicyAuditLogInfo.TimeToShutdown = 0;
+ // ignored for set
+
+ STARTLSATIMER;
+
+ Status = LsarSetInformationPolicy(
+ DBInfo->DBHandle,
+ PolicyAuditLogInformation,
+ &PolicyInformation );
+
+ STOPLSATIMER;
+
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+
+ //
+ // set audit event info.
+ //
+
+ // 'set' these bits and not just 'or' them in to the current ones
+ for ( i=0; i<DeltaLsaPolicy->MaximumAuditEventCount; i++ ) {
+ DeltaLsaPolicy->EventAuditingOptions[i] |= POLICY_AUDIT_EVENT_NONE;
+ }
+ PolicyInformation.PolicyAuditEventsInfo.AuditingMode =
+ DeltaLsaPolicy->AuditingMode;
+ PolicyInformation.PolicyAuditEventsInfo.MaximumAuditEventCount =
+ DeltaLsaPolicy->MaximumAuditEventCount;
+ PolicyInformation.PolicyAuditEventsInfo.EventAuditingOptions =
+ DeltaLsaPolicy->EventAuditingOptions;
+
+ STARTLSATIMER;
+
+ Status = LsarSetInformationPolicy(
+ DBInfo->DBHandle,
+ PolicyAuditEventsInformation,
+ &PolicyInformation);
+
+ STOPLSATIMER;
+
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+
+ //
+ // Don't set the Primary Domain information.
+ //
+ // The UI (i.e., setup) originally set this information correctly.
+ // Later, the UI (i.e., NCPA) on the PDC can be used to change the domain
+ // name. We don't want the new domain name to replicate accidentally.
+ // Rather, we want the admin to change the domain name individually on
+ // each BDC.
+ //
+
+ //
+ // set quoto limit
+ //
+
+ PolicyInformation.PolicyDefaultQuotaInfo.QuotaLimits.PagedPoolLimit =
+ DeltaLsaPolicy->QuotaLimits.PagedPoolLimit;
+ PolicyInformation.PolicyDefaultQuotaInfo.QuotaLimits.NonPagedPoolLimit =
+ DeltaLsaPolicy->QuotaLimits.NonPagedPoolLimit;
+ PolicyInformation.PolicyDefaultQuotaInfo.QuotaLimits.MinimumWorkingSetSize =
+ DeltaLsaPolicy->QuotaLimits.MinimumWorkingSetSize;
+ PolicyInformation.PolicyDefaultQuotaInfo.QuotaLimits.MaximumWorkingSetSize =
+ DeltaLsaPolicy->QuotaLimits.MaximumWorkingSetSize;
+ PolicyInformation.PolicyDefaultQuotaInfo.QuotaLimits.PagefileLimit =
+ DeltaLsaPolicy->QuotaLimits.PagefileLimit;
+
+ OLD_TO_NEW_LARGE_INTEGER(
+ DeltaLsaPolicy->QuotaLimits.TimeLimit,
+ PolicyInformation.PolicyDefaultQuotaInfo.QuotaLimits.TimeLimit );
+
+ STARTLSATIMER;
+
+ Status = LsarSetInformationPolicy(
+ DBInfo->DBHandle,
+ PolicyDefaultQuotaInformation,
+ &PolicyInformation);
+
+ STOPLSATIMER;
+
+ //
+ // Don't unpack ModifiedId and DatabaseCreationTime !! These will
+ // be handled separately during a full sync.
+ //
+
+Cleanup:
+
+ STOPUNPACKTIMER;
+
+ NlPrint((NL_REPL_OBJ_TIME,"Time taken to unpack POLICY object:\n"));
+ PRINTUNPACKTIMER;
+ PRINTLSATIMER;
+
+ return(Status);
+}
+
+
+NTSTATUS
+NlUnpackLsaTDomain(
+ IN PISID Sid,
+ IN PNETLOGON_DELTA_TRUSTED_DOMAINS DeltaLsaTDomain,
+ IN PDB_INFO DBInfo )
+/*++
+
+Routine Description:
+
+ Set the specified Trusted Domain info to look like the specified
+ buffer.
+
+Arguments:
+
+ Sid - The Sid of the trusted domain.
+
+ DeltaLsaPolicy - Description of the truted domain.
+
+ DBInfo - pointer to the database info structure.
+
+Return Value:
+
+ NT status code.
+
+--*/
+{
+ NTSTATUS Status;
+
+ LSAPR_SR_SECURITY_DESCRIPTOR SecurityDescriptor;
+ LSAPR_HANDLE TrustedDomainHandle = NULL;
+
+ LSAPR_TRUSTED_DOMAIN_INFO TrustedInfo;
+
+ DEFUNPACKTIMER;
+ DEFLSATIMER;
+
+ INITUNPACKTIMER;
+ INITLSATIMER;
+
+ STARTUNPACKTIMER;
+
+ NlPrint((NL_SYNC_MORE, "UnPacking Trusted Domain Object: %wZ\n",
+ &DeltaLsaTDomain->DomainName ));
+
+ //
+ // open trusted domain
+ //
+
+ STARTLSATIMER;
+
+ Status = LsarOpenTrustedDomain(
+ DBInfo->DBHandle,
+ (PLSAPR_SID)Sid,
+ 0,
+ &TrustedDomainHandle );
+
+ STOPLSATIMER;
+
+ if (!NT_SUCCESS(Status)) {
+
+ LSAPR_TRUST_INFORMATION DomainInfo;
+
+ if( Status != STATUS_OBJECT_NAME_NOT_FOUND ) {
+ goto Cleanup;
+ }
+
+ //
+ // if this account is not there then this may be a new one added
+ // so just create it.
+ //
+
+ DomainInfo.Name.Length =
+ DeltaLsaTDomain->DomainName.Length;
+ DomainInfo.Name.MaximumLength =
+ DeltaLsaTDomain->DomainName.MaximumLength;
+ DomainInfo.Name.Buffer =
+ DeltaLsaTDomain->DomainName.Buffer;
+
+ DomainInfo.Sid = (PLSAPR_SID)Sid;
+
+ STARTLSATIMER;
+
+ Status = LsarCreateTrustedDomain(
+ DBInfo->DBHandle,
+ &DomainInfo,
+ 0,
+ &TrustedDomainHandle );
+
+ STOPLSATIMER;
+
+ if (!NT_SUCCESS(Status)) {
+
+ //
+ // give up ...
+ //
+
+ goto Cleanup;
+ }
+ }
+
+ SET_LSA_SECOBJ_INFO(DeltaLsaTDomain, TrustedDomainHandle);
+
+ //
+ // set controller information
+ //
+
+ TrustedInfo.TrustedControllersInfo.Entries =
+ DeltaLsaTDomain->NumControllerEntries;
+ TrustedInfo.TrustedControllersInfo.Names =
+ (PLSAPR_UNICODE_STRING)DeltaLsaTDomain->ControllerNames;
+
+ STARTLSATIMER;
+
+ Status = LsarSetInformationTrustedDomain(
+ TrustedDomainHandle,
+ TrustedControllersInformation,
+ &TrustedInfo );
+
+ STOPLSATIMER;
+
+ //
+ // set PosixOffset information
+ //
+
+ TrustedInfo.TrustedPosixOffsetInfo.Offset =
+ DeltaLsaTDomain->DummyLong1;
+
+ STARTLSATIMER;
+
+ Status = LsarSetInformationTrustedDomain(
+ TrustedDomainHandle,
+ TrustedPosixOffsetInformation,
+ &TrustedInfo );
+
+ STOPLSATIMER;
+
+ //
+ // The BDC needs to keep its internal trust list up to date.
+ //
+
+ NlUpdateTrustListBySid( (PSID)Sid, &DeltaLsaTDomain->DomainName );
+
+Cleanup:
+
+ if(TrustedDomainHandle != NULL) {
+
+ STARTLSATIMER;
+
+ LsarClose(&TrustedDomainHandle);
+
+ STOPLSATIMER;
+
+ }
+
+ STOPUNPACKTIMER;
+
+ NlPrint((NL_REPL_OBJ_TIME,"Time taken to unpack TDOMAIN object:\n"));
+ PRINTUNPACKTIMER;
+ PRINTLSATIMER;
+
+ return(Status);
+}
+
+
+NTSTATUS
+NlUnpackLsaAccount(
+ IN PISID Sid,
+ IN PNETLOGON_DELTA_ACCOUNTS DeltaLsaAccount,
+ IN PDB_INFO DBInfo )
+/*++
+
+Routine Description:
+
+ Set the LSA account info to look like the specified buffer.
+
+Arguments:
+
+ Sid - The Sid of the LSA account.
+
+ DeltaLsaPolicy - Description of the LSA account.
+
+ DBInfo - pointer to the database info structure.
+
+Return Value:
+
+ NT status code.
+
+--*/
+{
+ NTSTATUS Status;
+
+ LSAPR_SR_SECURITY_DESCRIPTOR SecurityDescriptor;
+
+ LSAPR_HANDLE AccountHandle = NULL;
+ PLSAPR_PRIVILEGE_SET NewPrivSet = NULL;
+
+ DWORD i;
+ DWORD NewPrivIndex;
+ PULONG PrivAttr;
+ PUNICODE_STRING PrivName;
+ QUOTA_LIMITS QuotaLimits;
+
+ DEFUNPACKTIMER;
+ DEFLSATIMER;
+
+ INITUNPACKTIMER;
+ INITLSATIMER;
+
+ STARTUNPACKTIMER;
+
+ NlPrint((NL_SYNC_MORE, "UnPacking Lsa Account Object\n"));
+
+ //
+ // open lsa account
+ //
+
+ STARTLSATIMER;
+
+ Status = LsarOpenAccount(
+ DBInfo->DBHandle,
+ (PLSAPR_SID)Sid,
+ 0,
+ &AccountHandle );
+
+ STOPLSATIMER;
+
+ if (!NT_SUCCESS(Status)) {
+
+ if( Status != STATUS_OBJECT_NAME_NOT_FOUND ) {
+ goto Cleanup;
+ }
+
+ //
+ // if this account is not there then this may be a new one added
+ // so just create it.
+ //
+
+ STARTLSATIMER;
+
+ Status = LsarCreateAccount(
+ DBInfo->DBHandle,
+ (PLSAPR_SID)Sid,
+ 0,
+ &AccountHandle );
+
+ STOPLSATIMER;
+
+ if (!NT_SUCCESS(Status)) {
+
+ //
+ // give up ...
+ //
+
+ goto Cleanup;
+ }
+ }
+
+ SET_LSA_SECOBJ_INFO(DeltaLsaAccount, AccountHandle);
+
+ //
+ // remove all old privileges of the account
+ //
+
+ STARTLSATIMER;
+
+ Status = LsarRemovePrivilegesFromAccount(
+ AccountHandle,
+ (BOOLEAN) TRUE,
+ NULL );
+
+ STOPLSATIMER;
+
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+
+
+ //
+ // make new privilege set
+ //
+
+ NewPrivSet = (PLSAPR_PRIVILEGE_SET)MIDL_user_allocate(
+ sizeof(LSAPR_PRIVILEGE_SET) +
+ (DeltaLsaAccount->PrivilegeEntries *
+ sizeof(LUID_AND_ATTRIBUTES)) );
+
+ if( NewPrivSet == NULL ) {
+
+ Status = STATUS_NO_MEMORY;
+ goto Cleanup;
+ }
+
+ NewPrivSet->Control = DeltaLsaAccount->PrivilegeControl;
+
+ PrivAttr = DeltaLsaAccount->PrivilegeAttributes;
+ PrivName = DeltaLsaAccount->PrivilegeNames;
+ NewPrivIndex = 0;
+
+ for (i = 0; i < DeltaLsaAccount->PrivilegeEntries;
+ i++, PrivAttr++, PrivName++ ) {
+
+ NewPrivSet->Privilege[NewPrivIndex].Attributes = *PrivAttr;
+
+ //
+ // convert privilege name to LUID
+ //
+
+ STARTLSATIMER;
+
+ Status = LsarLookupPrivilegeValue(
+ DBInfo->DBHandle,
+ (PLSAPR_UNICODE_STRING)PrivName,
+ (PLUID)&NewPrivSet->Privilege[NewPrivIndex].Luid );
+
+ STOPLSATIMER;
+
+ if (!NT_SUCCESS(Status)) {
+ //
+ // If the PDC has a privilege we don't understand,
+ // silently ignore that privilege.
+ //
+ if ( Status == STATUS_NO_SUCH_PRIVILEGE ) {
+ NlPrint((NL_SYNC_MORE,
+ "Lsa Account replication ignored %wZ privilege\n",
+ PrivName ));
+
+ continue;
+ }
+ goto Cleanup;
+ }
+
+ NewPrivIndex ++;
+
+ }
+
+ NewPrivSet->PrivilegeCount = NewPrivIndex;
+
+ //
+ // set new privileges of the account
+ //
+
+ if ( NewPrivSet->PrivilegeCount > 0 ) {
+ STARTLSATIMER;
+
+ Status = LsarAddPrivilegesToAccount(
+ AccountHandle,
+ NewPrivSet );
+
+ STOPLSATIMER;
+
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+ }
+
+ STARTLSATIMER;
+
+ QuotaLimits.PagedPoolLimit = DeltaLsaAccount->QuotaLimits.PagedPoolLimit;
+ QuotaLimits.NonPagedPoolLimit = DeltaLsaAccount->QuotaLimits.NonPagedPoolLimit;
+ QuotaLimits.MinimumWorkingSetSize = DeltaLsaAccount->QuotaLimits.MinimumWorkingSetSize;
+ QuotaLimits.MaximumWorkingSetSize = DeltaLsaAccount->QuotaLimits.MaximumWorkingSetSize;
+ QuotaLimits.PagefileLimit = DeltaLsaAccount->QuotaLimits.PagefileLimit;
+
+ OLD_TO_NEW_LARGE_INTEGER(
+ DeltaLsaAccount->QuotaLimits.TimeLimit,
+ QuotaLimits.TimeLimit );
+
+ Status = LsarSetQuotasForAccount(
+ AccountHandle,
+ &QuotaLimits );
+
+ STOPLSATIMER;
+
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+
+ STARTLSATIMER;
+
+ Status = LsarSetSystemAccessAccount(
+ AccountHandle,
+ DeltaLsaAccount->SystemAccessFlags );
+
+ STOPLSATIMER;
+
+Cleanup:
+
+ if(AccountHandle != NULL) {
+
+ STARTLSATIMER;
+
+ LsarClose(&AccountHandle);
+
+ STOPLSATIMER;
+
+ }
+
+ if( NewPrivSet != NULL ) {
+ MIDL_user_free( NewPrivSet );
+ }
+
+ STOPUNPACKTIMER;
+
+ NlPrint((NL_REPL_OBJ_TIME,"Time taken to unpack LSAACCOUNT object:\n"));
+ PRINTUNPACKTIMER;
+ PRINTLSATIMER;
+
+ return(Status);
+}
+
+
+NTSTATUS
+NlUnpackLsaSecret(
+ IN LPWSTR Name,
+ IN PNETLOGON_DELTA_SECRET DeltaLsaSecret,
+ IN PDB_INFO DBInfo,
+ IN PSESSION_INFO SessionInfo
+ )
+/*++
+
+Routine Description:
+
+ Set the LSA secret info to look like the specified buffer.
+
+Arguments:
+
+ Name - The Sid of the secret.
+
+ DeltaLsaPolicy - Description of the user.
+
+ DBInfo - pointer to the database info structure.
+
+ SessionInfo - Info shared between PDC and BDC
+
+Return Value:
+
+ NT status code.
+
+--*/
+{
+
+ NTSTATUS Status;
+
+ LSAPR_SR_SECURITY_DESCRIPTOR SecurityDescriptor;
+
+ LSAPR_HANDLE SecretHandle = NULL;
+ UNICODE_STRING UnicodeName;
+
+ LSAPR_CR_CIPHER_VALUE CrCurrentValue = {0, 0, NULL };
+ LSAPR_CR_CIPHER_VALUE CrOldValue = {0, 0, NULL };
+
+ LARGE_INTEGER CurrentValueSetTime;
+ LARGE_INTEGER OldValueSetTime;
+
+ DEFUNPACKTIMER;
+ DEFLSATIMER;
+
+ NlPrint((NL_SYNC_MORE,
+ "UnPacking Secret Object: " FORMAT_LPWSTR "\n", Name));
+
+ //
+ // we should be unpacking only the GLOBAL secrets
+ //
+
+ NlAssert(
+ (wcslen( Name ) >
+ LSA_GLOBAL_SECRET_PREFIX_LENGTH ) &&
+ (_wcsnicmp( Name,
+ LSA_GLOBAL_SECRET_PREFIX,
+ LSA_GLOBAL_SECRET_PREFIX_LENGTH ) == 0) );
+
+
+
+
+ INITUNPACKTIMER;
+ INITLSATIMER;
+
+ STARTUNPACKTIMER;
+
+ RtlInitUnicodeString(&UnicodeName, Name);
+
+ //
+ // open trusted domain
+ //
+
+ STARTLSATIMER;
+
+ Status = LsarOpenSecret(
+ DBInfo->DBHandle,
+ (PLSAPR_UNICODE_STRING)&UnicodeName,
+ 0,
+ &SecretHandle );
+
+ STOPLSATIMER;
+
+ if (!NT_SUCCESS(Status)) {
+
+ if( Status != STATUS_OBJECT_NAME_NOT_FOUND ) {
+ goto Cleanup;
+ }
+
+ //
+ // if this account is not there then this may be a new one added
+ // so just create it.
+ //
+
+ STARTLSATIMER;
+
+ Status = LsarCreateSecret(
+ DBInfo->DBHandle,
+ (PLSAPR_UNICODE_STRING)&UnicodeName,
+ 0,
+ &SecretHandle );
+
+ STOPLSATIMER;
+
+ if (!NT_SUCCESS(Status)) {
+
+ //
+ // give up ...
+ //
+
+ goto Cleanup;
+ }
+ }
+
+ SET_LSA_SECOBJ_INFO(DeltaLsaSecret, SecretHandle);
+
+ //
+ // set secret values
+ //
+
+ //
+ // decrypt secret values.
+ //
+
+ if( DeltaLsaSecret->CurrentValue.Buffer != NULL ) {
+
+ Status = NlDecryptSensitiveData(
+ (PCRYPT_BUFFER) &DeltaLsaSecret->CurrentValue,
+ (PCRYPT_BUFFER) &CrCurrentValue,
+ SessionInfo );
+
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+
+ } else {
+
+ CrCurrentValue.Length = 0;
+ CrCurrentValue.MaximumLength = 0;
+ CrCurrentValue.Buffer = NULL;
+ }
+
+ if( DeltaLsaSecret->OldValue.Buffer != NULL ) {
+
+ Status = NlDecryptSensitiveData(
+ (PCRYPT_BUFFER) &DeltaLsaSecret->OldValue,
+ (PCRYPT_BUFFER) &CrOldValue,
+ SessionInfo );
+
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+
+ } else {
+
+ CrOldValue.Length = 0;
+ CrOldValue.MaximumLength = 0;
+ CrOldValue.Buffer = NULL;
+ }
+
+ STARTLSATIMER;
+
+ Status = LsarSetSecret(
+ SecretHandle,
+ &CrCurrentValue,
+ &CrOldValue );
+
+ STOPLSATIMER;
+
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+
+ //
+ // set secret times
+ //
+
+ STARTLSATIMER;
+
+ OLD_TO_NEW_LARGE_INTEGER(
+ DeltaLsaSecret->CurrentValueSetTime,
+ CurrentValueSetTime );
+
+ OLD_TO_NEW_LARGE_INTEGER(
+ DeltaLsaSecret->OldValueSetTime,
+ OldValueSetTime );
+
+
+ Status = LsaISetTimesSecret(
+ SecretHandle,
+ &CurrentValueSetTime,
+ &OldValueSetTime );
+
+ STOPLSATIMER;
+
+Cleanup:
+
+ //
+ // clean up decrypt buffers
+ //
+
+ if( CrCurrentValue.Buffer != NULL ) {
+
+ MIDL_user_free( CrCurrentValue.Buffer );
+ }
+
+ if( CrOldValue.Buffer != NULL ) {
+
+ MIDL_user_free( CrOldValue.Buffer );
+ }
+
+ if(SecretHandle != NULL) {
+
+ STARTLSATIMER;
+
+ LsarClose(&SecretHandle);
+
+ STOPLSATIMER;
+
+ }
+
+ STOPUNPACKTIMER;
+
+ NlPrint((NL_REPL_OBJ_TIME,"Time taken to unpack SECRET object:\n"));
+ PRINTUNPACKTIMER;
+ PRINTLSATIMER;
+
+ return(Status);
+}
+
+
+NTSTATUS
+NlDeleteLsaTDomain(
+ IN PISID Sid,
+ IN PDB_INFO DBInfo )
+/*++
+
+Routine Description:
+
+ Delete the specified trusted domain account from LSA.
+
+Arguments:
+
+ Sid - The Sid of the trusted domain.
+
+ DBInfo - pointer to the database info structure.
+
+Return Value:
+
+ NT status code.
+
+--*/
+{
+ NTSTATUS Status;
+
+ LSAPR_HANDLE TrustedDomainHandle = NULL;
+
+ NlPrint((NL_SYNC_MORE, "Delete Trusted Domain Object\n"));
+
+ //
+ // open trusted domain
+ //
+
+ Status = LsarOpenTrustedDomain(
+ DBInfo->DBHandle,
+ (PLSAPR_SID)Sid,
+ 0,
+ &TrustedDomainHandle );
+
+ if( Status == STATUS_OBJECT_NAME_NOT_FOUND ) {
+
+ return(STATUS_SUCCESS);
+ }
+
+ if (NT_SUCCESS(Status)) {
+
+ Status = LsarDelete(TrustedDomainHandle);
+ }
+
+ //
+ // The BDC needs to keep its internal trust list up to date.
+ //
+
+ NlUpdateTrustListBySid( (PSID) Sid, NULL );
+
+ return(Status);
+}
+
+
+NTSTATUS
+NlDeleteLsaAccount(
+ IN PISID Sid,
+ IN PDB_INFO DBInfo )
+/*++
+
+Routine Description:
+
+ Delete the specified LSA account.
+
+Arguments:
+
+ Sid - The Sid of the LSA account.
+
+ DBInfo - pointer to the database info structure.
+
+Return Value:
+
+ NT status code.
+
+--*/
+{
+ NTSTATUS Status;
+
+ LSAPR_HANDLE AccountHandle = NULL;
+
+ NlPrint((NL_SYNC_MORE, "Delete Lsa Account Object\n"));
+
+ //
+ // open trusted domain
+ //
+
+ Status = LsarOpenAccount(
+ DBInfo->DBHandle,
+ (PLSAPR_SID)Sid,
+ 0,
+ &AccountHandle );
+
+ if( Status == STATUS_OBJECT_NAME_NOT_FOUND ) {
+
+ return(STATUS_SUCCESS);
+ }
+
+ if (NT_SUCCESS(Status)) {
+
+ Status = LsarDelete(AccountHandle);
+ }
+
+ return(Status);
+
+}
+
+
+NTSTATUS
+NlDeleteLsaSecret(
+ IN LPWSTR Name,
+ IN PDB_INFO DBInfo )
+/*++
+
+Routine Description:
+
+ Delete the specified LSA secret.
+
+Arguments:
+
+ Name - The Sid of the secret.
+
+ DBInfo - pointer to the database info structure.
+
+Return Value:
+
+ NT status code.
+
+--*/
+{
+
+ NTSTATUS Status;
+
+ LSAPR_HANDLE SecretHandle = NULL;
+ UNICODE_STRING UnicodeName;
+
+ //
+ // SPECIAL CASE:
+ //
+ // DONOT REPLICATE machine account secret, which is
+ // not replicated and is specific to the machine.
+ //
+
+ if( !_wcsicmp( Name, SSI_SECRET_NAME ) ) {
+
+ return STATUS_SUCCESS;
+ }
+
+ RtlInitUnicodeString(&UnicodeName, Name);
+
+ NlPrint((NL_SYNC_MORE, "Delete Secret Object: %wZ\n", &UnicodeName));
+
+ //
+ // open trusted domain
+ //
+
+ Status = LsarOpenSecret(
+ DBInfo->DBHandle,
+ (PLSAPR_UNICODE_STRING)&UnicodeName,
+ 0,
+ &SecretHandle );
+
+ if( Status == STATUS_OBJECT_NAME_NOT_FOUND ) {
+
+ return(STATUS_SUCCESS);
+ }
+
+ if (NT_SUCCESS(Status)) {
+
+ Status = LsarDelete(SecretHandle);
+ }
+
+ return(Status);
+
+}
diff --git a/private/net/svcdlls/logonsrv/server/lsarepl.h b/private/net/svcdlls/logonsrv/server/lsarepl.h
new file mode 100644
index 000000000..53854e133
--- /dev/null
+++ b/private/net/svcdlls/logonsrv/server/lsarepl.h
@@ -0,0 +1,100 @@
+/*++
+
+Copyright (c) 1987-1992 Microsoft Corporation
+
+Module Name:
+
+ lsarepl.h
+
+Abstract:
+
+ Function prototypes for Low level LSA Replication functions
+
+Author:
+
+ 06-Apr-1992 (madana)
+ Created for LSA replication.
+
+Environment:
+
+ User mode only.
+ Contains NT-specific code.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+--*/
+
+//
+// lsarepl.c
+//
+
+NTSTATUS
+NlPackLsaPolicy(
+ IN OUT PNETLOGON_DELTA_ENUM Delta,
+ IN PDB_INFO DBInfo,
+ IN LPDWORD BufferSize );
+
+NTSTATUS
+NlPackLsaTDomain(
+ IN PSID Sid,
+ IN OUT PNETLOGON_DELTA_ENUM Delta,
+ IN PDB_INFO DBInfo,
+ IN LPDWORD BufferSize );
+
+NTSTATUS
+NlPackLsaAccount(
+ IN PSID Sid,
+ IN OUT PNETLOGON_DELTA_ENUM Delta,
+ IN PDB_INFO DBInfo,
+ IN LPDWORD BufferSize,
+ IN PSESSION_INFO SessionInfo
+ );
+
+NTSTATUS
+NlPackLsaSecret(
+ IN PUNICODE_STRING Name,
+ IN OUT PNETLOGON_DELTA_ENUM Delta,
+ IN PDB_INFO DBInfo,
+ IN LPDWORD BufferSize,
+ IN PSESSION_INFO SessionInfo );
+
+NTSTATUS
+NlUnpackLsaPolicy(
+ IN PNETLOGON_DELTA_POLICY DeltaLsaPolicy,
+ IN PDB_INFO DBInfo );
+
+NTSTATUS
+NlUnpackLsaTDomain(
+ IN PISID Sid,
+ IN PNETLOGON_DELTA_TRUSTED_DOMAINS DeltaLsaTDomain,
+ IN PDB_INFO DBInfo );
+
+NTSTATUS
+NlUnpackLsaAccount(
+ IN PISID Sid,
+ IN PNETLOGON_DELTA_ACCOUNTS DeltaLsaAccount,
+ IN PDB_INFO DBInfo );
+
+NTSTATUS
+NlUnpackLsaSecret(
+ IN LPWSTR Name,
+ IN PNETLOGON_DELTA_SECRET DeltaLsaSecret,
+ IN PDB_INFO DBInfo,
+ IN PSESSION_INFO SessionInfo
+ );
+
+NTSTATUS
+NlDeleteLsaTDomain(
+ IN PISID Sid,
+ IN PDB_INFO DBInfo );
+
+NTSTATUS
+NlDeleteLsaAccount(
+ IN PISID Sid,
+ IN PDB_INFO DBInfo );
+
+NTSTATUS
+NlDeleteLsaSecret(
+ IN LPWSTR Name,
+ IN PDB_INFO DBInfo );
diff --git a/private/net/svcdlls/logonsrv/server/lsrvdata.h b/private/net/svcdlls/logonsrv/server/lsrvdata.h
new file mode 100644
index 000000000..dc091b7e6
--- /dev/null
+++ b/private/net/svcdlls/logonsrv/server/lsrvdata.h
@@ -0,0 +1,228 @@
+/*++
+
+Copyright (c) 1987-1992 Microsoft Corporation
+
+Module Name:
+
+ lsrvdata.h
+
+Abstract:
+
+ Netlogon service global variable external and definitions
+
+Author:
+
+ Ported from Lan Man 2.0
+
+Revision History:
+
+ 21-May-1991 (cliffv)
+ Ported to NT. Converted to NT style.
+
+ 02-Jan-1992 (madana)
+ added support for builtin/multidomain replication.
+ 07-May-1992 JohnRo
+ Use net config helpers for NetLogon.
+
+--*/
+
+
+#ifndef _LSRVDATA_
+#define _LSRVDATA_
+
+
+//
+// netlogon.c will #include this file with LSRVDATA_ALLOCATE defined.
+// That will cause each of these variables to be allocated.
+//
+#ifdef LSRVDATA_ALLOCATE
+#define EXTERN
+#else
+#define EXTERN extern
+#endif
+
+///////////////////////////////////////////////////////////////////////////
+//
+// Modifiable Variables: these variables change over time.
+//
+///////////////////////////////////////////////////////////////////////////
+
+//
+// Global NetStatus of the Netlogon service
+//
+
+EXTERN SERVICE_STATUS NlGlobalServiceStatus;
+EXTERN SERVICE_STATUS_HANDLE NlGlobalServiceHandle;
+
+//
+// The server name of the current PDC.
+//
+
+EXTERN CHAR NlGlobalAnsiPrimaryName[CNLEN+1];
+EXTERN WCHAR NlGlobalUncPrimaryName[UNCLEN+1];
+EXTERN LPWSTR NlGlobalUnicodePrimaryName;
+
+//
+// Global SAM Modes.
+//
+// We track these values as SAM tells us that they have changed.
+//
+
+EXTERN BOOLEAN NlGlobalUasCompatibilityMode;
+
+//
+// Boolean so that we only warn the user once about having too many global
+// groups.
+
+EXTERN BOOLEAN NlGlobalTooManyGlobalGroups;
+
+
+///////////////////////////////////////////////////////////////////////////
+//
+// Read-only variables after initialization.
+//
+///////////////////////////////////////////////////////////////////////////
+
+
+//
+// Handle to wait on for mailslot reads
+//
+
+EXTERN HANDLE NlGlobalMailslotHandle;
+
+//
+// Flag to indicate when RPC has been started
+//
+
+EXTERN BOOL NlGlobalRpcServerStarted;
+
+//
+// Service Termination event.
+//
+
+EXTERN HANDLE NlGlobalTerminateEvent;
+EXTERN BOOL NlGlobalTerminate;
+EXTERN HANDLE NlGlobalReplicatorTerminateEvent;
+
+//
+// Service Started Event
+//
+
+EXTERN HANDLE NlGlobalStartedEvent;
+
+//
+// Timers need attention event.
+//
+
+EXTERN HANDLE NlGlobalTimerEvent;
+
+
+
+//
+// The computername of the local system.
+//
+
+EXTERN LPSTR NlGlobalAnsiComputerName;
+EXTERN LPWSTR NlGlobalUnicodeComputerName;
+EXTERN WCHAR NlGlobalUncUnicodeComputerName[UNCLEN + 1];
+EXTERN UNICODE_STRING NlGlobalUnicodeComputerNameString;
+
+//
+// Primary Domain Information:
+//
+// The Domain Name is maintained in Ansi and Unicode.
+//
+EXTERN LPSTR NlGlobalAnsiDomainName;
+EXTERN WCHAR NlGlobalUnicodeDomainName[DNLEN+1];
+EXTERN UNICODE_STRING NlGlobalUnicodeDomainNameString;
+EXTERN PSID NlGlobalPrimaryDomainId;
+
+
+//
+// Account Domain Information
+//
+EXTERN UNICODE_STRING NlGlobalAccountDomainName;
+
+//
+// Global DB Info array
+//
+EXTERN DB_INFO NlGlobalDBInfoArray[NUM_DBS];
+
+
+
+EXTERN SAMPR_HANDLE NlGlobalSamServerHandle; // Handle to Sam Server database
+EXTERN LSAPR_HANDLE NlGlobalPolicyHandle; // Handle to Policy Database
+
+typedef enum _NETLOGON_ROLE {
+ RolePrimary = 1,
+ RoleBackup,
+ RoleMemberWorkstation
+} NETLOGON_ROLE, * PNETLOGON_ROLE;
+
+EXTERN NETLOGON_ROLE NlGlobalRole;
+
+
+EXTERN WCHAR NlGlobalUnicodeScriptPath[PATHLEN + 1];
+
+
+//
+// Command line arguments.
+//
+
+EXTERN ULONG NlGlobalPulseParameter;
+EXTERN ULONG NlGlobalPulseMaximumParameter;
+EXTERN ULONG NlGlobalPulseConcurrencyParameter;
+EXTERN ULONG NlGlobalPulseTimeout1Parameter;
+EXTERN ULONG NlGlobalPulseTimeout2Parameter;
+EXTERN ULONG NlGlobalGovernorParameter;
+EXTERN BOOL NlGlobalDisablePasswordChangeParameter;
+EXTERN BOOL NlGlobalRefusePasswordChangeParameter;
+EXTERN ULONG NlGlobalRandomizeParameter;
+EXTERN BOOL NlGlobalSynchronizeParameter;
+EXTERN ULONG NlGlobalMaximumMailslotMessagesParameter;
+EXTERN ULONG NlGlobalMailslotMessageTimeoutParameter;
+EXTERN ULONG NlGlobalMailslotDuplicateTimeoutParameter;
+EXTERN ULONG NlGlobalExpectedDialupDelayParameter;
+EXTERN ULONG NlGlobalScavengeIntervalParameter;
+
+
+//
+// Parameters represented in 100ns units
+//
+EXTERN LARGE_INTEGER NlGlobalPulseMaximum;
+EXTERN LARGE_INTEGER NlGlobalPulseTimeout1;
+EXTERN LARGE_INTEGER NlGlobalPulseTimeout2;
+EXTERN LARGE_INTEGER NlGlobalMailslotMessageTimeout;
+EXTERN LARGE_INTEGER NlGlobalMailslotDuplicateTimeout;
+EXTERN ULONG NlGlobalShortApiCallPeriod;
+
+
+//
+// global flags used to pause the netlogon service when the database is
+// full synced first time.
+//
+
+EXTERN BOOL NlGlobalFirstTimeFullSync;
+
+
+//
+// Global variables required for scavenger thread.
+//
+
+EXTERN CRITICAL_SECTION NlGlobalScavengerCritSect;
+EXTERN HANDLE NlGlobalScavengerThreadHandle;
+EXTERN BOOL NlGlobalScavengerTerminate;
+
+//
+// Variables for cordinating MSV threads running in netlogon.dll
+//
+
+EXTERN CRITICAL_SECTION NlGlobalMsvCritSect;
+EXTERN HANDLE NlGlobalMsvTerminateEvent;
+EXTERN BOOL NlGlobalMsvEnabled;
+EXTERN ULONG NlGlobalMsvThreadCount;
+
+#undef EXTERN
+
+
+#endif // _LSRVDATA_
diff --git a/private/net/svcdlls/logonsrv/server/lsrvrepl.c b/private/net/svcdlls/logonsrv/server/lsrvrepl.c
new file mode 100644
index 000000000..621f5f34c
--- /dev/null
+++ b/private/net/svcdlls/logonsrv/server/lsrvrepl.c
@@ -0,0 +1,4700 @@
+/*++
+
+Copyright (c) 1987-1992 Microsoft Corporation
+
+Module Name:
+
+ lsrvrepl.c
+
+Abstract:
+
+ Utility functions for the netlogon replication service.
+
+Author:
+
+ Ported from Lan Man 2.0
+
+Environment:
+
+ User mode only.
+ Contains NT-specific code.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+ 00-Jun-1989 (PradyM)
+ modified lm10 code for new NETLOGON service
+
+ 00-Feb-1990 (PradyM)
+ bugfixes
+
+ 00-Aug-1990 (t-RichE)
+ added alerts for auth failure due to time slippage
+
+ 11-Jul-1991 (cliffv)
+ Ported to NT. Converted to NT style.
+
+ 02-Jan-1992 (madana)
+ added support for builtin/multidomain replication.
+
+ 09-Apr-1992 JohnRo
+ Prepare for WCHAR.H (_wcsicmp vs _wcscmpi, etc).
+
+ 21-Apr-1992 (madana)
+ spilt the lsrvutil.c into two files as:
+ lsrvutil.c - has general util functions
+ lsrvrepl.c - has netlogon replication functions
+
+--*/
+
+//
+// Common include files.
+//
+
+#include <logonsrv.h> // Include files common to entire service
+
+//
+// Include files specific to this .c file
+//
+
+// #include <accessp.h> // NetpAliasMemberToPriv
+#include <alertmsg.h> // Alert message text.
+#include <lmapibuf.h>
+#include <lmerr.h> // System Error Log definitions
+#include <lmserver.h> // server API functions and prototypes
+#include <lmshare.h> // share API functions and prototypes
+#include <msgtext.h> // MTXT_* defines
+#include <replutil.h> // UnpackSamXXX()
+#include <secobj.h> // NetpDomainIdToSid
+#include <ssiapi.h> // I_NetSamDeltas()
+#include <stddef.h> // offsetof
+#include <stdlib.h> // C library functions (rand, etc)
+#include <tstring.h> // IS_PATH_SEPARATOR ...
+#include <winreg.h> // registry API
+#include <wingdi.h> // LoadString()
+#include <winuser.h> // LoadString()
+
+#define MAX_LSA_PREF_LENGTH 0xFFFFFFFF // to get all objects
+#define MAX_SAM_PREF_LENGTH 0xFFFFFFFF // to get all objects
+
+//
+// Structure used to pass arguments to the replicator thread.
+//
+typedef struct _REPL_PARAM {
+ DWORD RandomSleep; // Number of millseconds to delay before working
+} REPL_PARAM, *PREPL_PARAM;
+
+//
+// enum typdef for SAM objects
+//
+
+typedef enum _LOCAL_SAM_ACCOUNT_TYPE {
+ UserAccount,
+ GroupAccount,
+ AliasAccount
+} LOCAL_SAM_ACCOUNT_TYPE;
+
+typedef enum _LOCAL_LSA_ACCOUNT_TYPE {
+ LsaAccount,
+ LsaTDomain,
+ LsaSecret
+} LOCAL_LSA_ACCOUNT_TYPE;
+
+//
+// The following variables are protected by the NlGlobalReplicatorCritSect
+//
+HANDLE NlGlobalReplicatorThreadHandle = NULL;
+BOOL NlGlobalReplicatorTerminate = FALSE;
+BOOL NlGlobalReplicatorIsRunning = FALSE;
+
+//
+// The following variable is only modified under the
+// NlGlobalReplicatorCritSect and when the replicator thread is not
+// running. It is referenced by the replicator thread.
+//
+
+REPL_PARAM NlGlobalReplParam; // Parameters to the replicator thread
+
+PULONG NlGlobalSamUserRids = NULL;
+ULONG NlGlobalSamUserCount = 0;
+PULONG NlGlobalSamGroupRids = NULL;
+ULONG NlGlobalSamGroupCount = 0;
+PSAMPR_ENUMERATION_BUFFER NlGlobalSamAliasesEnumBuffer = NULL;
+
+LSAPR_ACCOUNT_ENUM_BUFFER NlGlobalLsaAccountsEnumBuffer = {0, NULL};
+LSAPR_TRUSTED_ENUM_BUFFER NlGlobalLsaTDomainsEnumBuffer = {0, NULL};
+PVOID NlGlobalLsaSecretsEnumBuffer = NULL;
+ULONG NlGlobalLsaSecretCountReturned = 0;
+
+BOOLEAN NlGlobalLsaAccountsHack = FALSE;
+
+
+VOID
+NlLogSyncError(
+ IN PNETLOGON_DELTA_ENUM Delta,
+ IN PDB_INFO DBInfo,
+ IN NTSTATUS ReplicationStatus
+ )
+/*++
+
+Routine Description:
+
+ Logs an error describing the specific delta that an error occured on.
+
+Arguments:
+
+ Deltas - The delta which failed
+
+ DBInfo - Describes the database the operation was applied to
+
+ ReplicationStatus - Status of the failed operation
+
+Return Value:
+
+ NONE
+
+--*/
+{
+ NTSTATUS Status;
+
+ UNICODE_STRING AccountName;
+ WCHAR AccountNameBuffer[25];
+ BOOLEAN AccountNameIsAllocated = FALSE;
+ LPWSTR ZeroAccountName = NULL;
+
+ LPWSTR MsgStrings[5];
+ ULONG EventId = 0;
+
+ //
+ // Get the name of the account
+ //
+
+ switch ( Delta->DeltaType ) {
+ case AddOrChangeDomain:
+ EventId = NELOG_NetlogonFailedDomainDelta;
+ AccountName = ((PNETLOGON_DELTA_DOMAIN)(Delta->DeltaUnion.DeltaDomain))->
+ DomainName;
+ break;
+
+ case AddOrChangeGroup:
+ EventId = NELOG_NetlogonFailedGlobalGroupDelta;
+ AccountName = ((PNETLOGON_DELTA_GROUP)(Delta->DeltaUnion.DeltaGroup))->
+ Name;
+ break;
+
+ case AddOrChangeAlias:
+ EventId = NELOG_NetlogonFailedLocalGroupDelta;
+ AccountName = ((PNETLOGON_DELTA_ALIAS)(Delta->DeltaUnion.DeltaAlias))->
+ Name;
+ break;
+
+ case AddOrChangeUser:
+ EventId = NELOG_NetlogonFailedUserDelta;
+ AccountName = ((PNETLOGON_DELTA_USER)(Delta->DeltaUnion.DeltaUser))->
+ UserName;
+ break;
+
+ case ChangeGroupMembership:
+ case ChangeAliasMembership:
+ case DeleteGroup:
+ case DeleteAlias:
+ case DeleteUser:
+ case DeleteGroupByName:
+ case DeleteUserByName:
+
+ switch ( Delta->DeltaType ) {
+ case ChangeGroupMembership:
+ case DeleteGroup:
+ case DeleteGroupByName:
+ EventId = NELOG_NetlogonFailedGlobalGroupDelta; break;
+ case ChangeAliasMembership:
+ case DeleteAlias:
+ EventId = NELOG_NetlogonFailedLocalGroupDelta; break;
+ case DeleteUser:
+ case DeleteUserByName:
+ EventId = NELOG_NetlogonFailedUserDelta; break;
+ }
+
+ //
+ // If all we have is a RID,
+ // convert the RID to a unicode string.
+ //
+ wcscpy( AccountNameBuffer, L"Rid: 0x" );
+ ultow( Delta->DeltaID.Rid, AccountNameBuffer+7, 16 );
+ RtlInitUnicodeString( &AccountName, AccountNameBuffer );
+
+ break;
+
+ case RenameUser:
+ EventId = NELOG_NetlogonFailedUserDelta;
+ AccountName = ((PNETLOGON_DELTA_RENAME_USER)(Delta->DeltaUnion.DeltaRenameUser))->
+ OldName;
+ break;
+
+ case RenameGroup:
+ EventId = NELOG_NetlogonFailedGlobalGroupDelta;
+ AccountName = ((PNETLOGON_DELTA_RENAME_GROUP)(Delta->DeltaUnion.DeltaRenameUser))->
+ OldName;
+ break;
+
+ case RenameAlias:
+ EventId = NELOG_NetlogonFailedLocalGroupDelta;
+ AccountName = ((PNETLOGON_DELTA_RENAME_ALIAS)(Delta->DeltaUnion.DeltaRenameUser))->
+ OldName;
+ break;
+
+ case AddOrChangeLsaPolicy:
+ EventId = NELOG_NetlogonFailedPolicyDelta;
+ RtlInitUnicodeString( &AccountName, L"Policy");
+ break;
+
+ case AddOrChangeLsaTDomain:
+ EventId = NELOG_NetlogonFailedTrustedDomainDelta;
+ AccountName = ((PNETLOGON_DELTA_TRUSTED_DOMAINS)(Delta->DeltaUnion.DeltaTDomains))->
+ DomainName;
+ break;
+
+ case DeleteLsaSecret:
+ case AddOrChangeLsaSecret:
+ EventId = NELOG_NetlogonFailedSecretDelta;
+ RtlInitUnicodeString( &AccountName, Delta->DeltaID.Name);
+ break;
+
+ case AddOrChangeLsaAccount:
+ case DeleteLsaTDomain:
+ case DeleteLsaAccount:
+
+ if ( Delta->DeltaType == DeleteLsaTDomain ) {
+ EventId = NELOG_NetlogonFailedTrustedDomainDelta;
+ } else {
+ EventId = NELOG_NetlogonFailedAccountDelta;
+ }
+
+ //
+ // If all we have is a SID,
+ // convert the SID to a unicode string.
+ //
+ Status = RtlConvertSidToUnicodeString( &AccountName,
+ Delta->DeltaID.Sid,
+ TRUE );
+
+ if ( !NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+
+ AccountNameIsAllocated = TRUE;
+
+ break;
+
+ default:
+ NlPrint((NL_CRITICAL, "NlLogSyncError: Invalid delta type %lx\n", Delta->DeltaType ));
+ return;
+ }
+
+ NlAssert( EventId != 0 );
+
+ //
+ // Convert account name to zero terminated string.
+ //
+
+ ZeroAccountName = NetpMemoryAllocate( AccountName.Length + sizeof(WCHAR) );
+
+ if ( ZeroAccountName == NULL ) {
+ goto Cleanup;
+ }
+
+ RtlCopyMemory( ZeroAccountName, AccountName.Buffer, AccountName.Length );
+ ZeroAccountName[AccountName.Length/sizeof(WCHAR)] = L'\0';
+
+
+
+ //
+ // Write the event log message.
+ //
+
+ MsgStrings[0] = DBInfo->DBName;
+ MsgStrings[1] = ZeroAccountName;
+ MsgStrings[2] = NlGlobalUnicodePrimaryName;
+ MsgStrings[3] = (LPWSTR) ReplicationStatus;
+
+ NlpWriteEventlog (
+ EventId,
+ EVENTLOG_ERROR_TYPE,
+ (LPBYTE) &ReplicationStatus,
+ sizeof(ReplicationStatus),
+ MsgStrings,
+ 4 | LAST_MESSAGE_IS_NTSTATUS );
+
+
+ //
+ // Cleanup locals
+ //
+Cleanup:
+ if ( AccountNameIsAllocated ) {
+ RtlFreeUnicodeString( &AccountName );
+ }
+
+ if ( ZeroAccountName != NULL ) {
+ NetpMemoryFree( ZeroAccountName );
+ }
+
+}
+
+#if DBG
+
+VOID
+PrintFullSyncKey(
+ IN ULONG DBIndex,
+ IN PFULL_SYNC_KEY FullSyncKey,
+ IN LPSTR Header
+ )
+/*++
+
+Routine Description:
+
+ Print a full sync key for a particular server
+
+Arguments:
+
+ DBIndex - Database number of the value to query
+
+ FullSyncKey - FullSyncKey structure to print
+
+ Header - string to print before rest of text
+
+Return Value:
+
+ None
+
+--*/
+{
+ NlPrint(( NL_SYNC, "%s " FORMAT_LPWSTR " Full Sync Key:",
+ Header,
+ NlGlobalDBInfoArray[DBIndex].DBName ));
+
+ if ( FullSyncKey->SyncState == NormalState ) {
+ NlPrint(( NL_SYNC, " not in progress\n" ));
+ return;
+ }
+
+ switch ( FullSyncKey->SyncState ) {
+ case NormalState:
+ NlPrint(( NL_SYNC, " NormalState"));
+ break;
+ case DomainState:
+ NlPrint(( NL_SYNC, " DomainState"));
+ break;
+ case UserState:
+ NlPrint(( NL_SYNC, " UserState"));
+ break;
+ case GroupState:
+ NlPrint(( NL_SYNC, " GroupState"));
+ break;
+ case GroupMemberState:
+ NlPrint(( NL_SYNC, " GroupMemberState"));
+ break;
+ case AliasState:
+ NlPrint(( NL_SYNC, " AliasState"));
+ break;
+ case AliasMemberState:
+ NlPrint(( NL_SYNC, " AliasMemberState"));
+ break;
+ default:
+ NlPrint(( NL_SYNC, " Invalid state %ld", FullSyncKey->SyncState ));
+ break;
+ }
+
+ NlPrint(( NL_SYNC, " Continuation Rid: 0x%lx", FullSyncKey->ContinuationRid ));
+ NlPrint(( NL_SYNC, " PDC Serial Number: 0x%lx 0x%lx",
+ FullSyncKey->PdcSerialNumber.HighPart,
+ FullSyncKey->PdcSerialNumber.LowPart ));
+ NlPrint(( NL_SYNC, " PDC Domain Creation Time: 0x%lx 0x%lx\n",
+ FullSyncKey->PdcDomainCreationTime.HighPart,
+ FullSyncKey->PdcDomainCreationTime.LowPart ));
+}
+#else DBG
+#define PrintFullSyncKey( _x, _y, _z )
+#endif DBG
+
+
+
+HKEY
+NlOpenFullSyncKey(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ Create/Open the Netlogon\FullSync key in the registry.
+
+Arguments:
+
+ FullSyncKey - Value to write to registry. (NULL says delete entry)
+
+Return Value:
+
+ Return a handle to the key. NULL means the key couldn't be openned.
+
+--*/
+{
+ LONG RegStatus;
+
+ HKEY BaseHandle = NULL;
+ HKEY ParmHandle = NULL;
+ ULONG Disposition;
+
+ //
+ // Open the registry
+ //
+
+ RegStatus = RegConnectRegistryW( NULL,
+ HKEY_LOCAL_MACHINE,
+ &BaseHandle);
+
+ if ( RegStatus != ERROR_SUCCESS ) {
+ NlPrint(( NL_CRITICAL,
+ "NlOpenFullSyncKey: Cannot connect to registy %ld.\n",
+ RegStatus ));
+ return NULL;
+ }
+
+
+ //
+ // Open the key for Netlogon\FullSyncKey
+ //
+
+ RegStatus = RegCreateKeyExA(
+ BaseHandle,
+ NL_FULL_SYNC_KEY,
+ 0, //Reserved
+ NULL, // Class
+ REG_OPTION_NON_VOLATILE,
+ KEY_SET_VALUE | KEY_QUERY_VALUE,
+ NULL, // Security descriptor
+ &ParmHandle,
+ &Disposition );
+
+ RegCloseKey( BaseHandle );
+
+ if ( RegStatus != ERROR_SUCCESS ) {
+ NlPrint(( NL_CRITICAL,
+ "NlOpenFullSyncKey: Cannot create registy key %s %ld.\n",
+ NL_FULL_SYNC_KEY,
+ RegStatus ));
+ return NULL;
+ }
+
+ return ParmHandle;
+}
+
+
+VOID
+NlSetFullSyncKey(
+ ULONG DBIndex,
+ PFULL_SYNC_KEY FullSyncKey
+ )
+/*++
+
+Routine Description:
+
+ Sets the Netlogon\FullSync key to the specified value.
+
+Arguments:
+
+ DBIndex - Database number of the value to query
+
+ FullSyncKey - Value to write to registry. (NULL says delete entry)
+
+Return Value:
+
+ None.
+
+--*/
+{
+ LONG RegStatus;
+ FULL_SYNC_KEY NullFullSyncKey;
+ PFULL_SYNC_KEY LocalFullSyncKey;
+
+ HKEY ParmHandle = NULL;
+
+ //
+ // Open the key for Netlogon\FullSync
+ //
+
+ ParmHandle = NlOpenFullSyncKey( );
+
+ if (ParmHandle == NULL) {
+ goto Cleanup;
+ }
+
+ //
+ // Build the data to write to the registry.
+ //
+
+ if ( FullSyncKey == NULL) {
+ RtlZeroMemory( &NullFullSyncKey, sizeof(NullFullSyncKey));
+ NullFullSyncKey.Version = FULL_SYNC_KEY_VERSION;
+ NullFullSyncKey.SyncState = NormalState;
+ LocalFullSyncKey = &NullFullSyncKey;
+ } else {
+ LocalFullSyncKey = FullSyncKey;
+ }
+
+ //
+ // Set the value in the registry.
+ //
+
+ RegStatus = RegSetValueExW( ParmHandle,
+ NlGlobalDBInfoArray[DBIndex].DBName,
+ 0, // Reserved
+ REG_BINARY,
+ (LPBYTE)LocalFullSyncKey,
+ sizeof(*LocalFullSyncKey));
+
+ if ( RegStatus != ERROR_SUCCESS ) {
+ NlPrint(( NL_CRITICAL,
+ "NlSetFullSyncKey: Cannot Set '" NL_FULL_SYNC_KEY "\\" FORMAT_LPWSTR "' %ld.\n",
+ NlGlobalDBInfoArray[DBIndex].DBName,
+ RegStatus ));
+ goto Cleanup;
+ }
+
+ PrintFullSyncKey( DBIndex, LocalFullSyncKey, "Setting" );
+
+ //
+ // Be tidy.
+ //
+Cleanup:
+ if ( ParmHandle != NULL ) {
+ RegCloseKey( ParmHandle );
+ }
+ return;
+
+}
+
+
+VOID
+NlQueryFullSyncKey(
+ ULONG DBIndex,
+ PFULL_SYNC_KEY FullSyncKey
+ )
+/*++
+
+Routine Description:
+
+ Queries Netlogon\FullSync key current value.
+
+Arguments:
+
+ DBIndex - Database number of the value to query
+
+ FullSyncKey - Value queried from the registry
+
+Return Value:
+
+ None.
+
+--*/
+{
+ LONG RegStatus;
+ BOOLEAN Failed;
+ DWORD KeyType;
+ DWORD DataSize;
+
+
+ HKEY ParmHandle = NULL;
+
+ //
+ // Open the key for Netlogon\FullSync
+ //
+
+ ParmHandle = NlOpenFullSyncKey( );
+
+ if (ParmHandle == NULL) {
+ Failed = TRUE;
+ goto Cleanup;
+ }
+
+ //
+ // Set the value in the registry.
+ //
+
+ DataSize = sizeof(*FullSyncKey);
+ RegStatus = RegQueryValueExW( ParmHandle,
+ NlGlobalDBInfoArray[DBIndex].DBName,
+ 0, // Reserved
+ &KeyType,
+ (LPBYTE)FullSyncKey,
+ &DataSize );
+
+ if ( RegStatus != ERROR_SUCCESS ) {
+ NlPrint(( NL_CRITICAL,
+ "NlQueryFullSyncKey: Cannot query '" NL_FULL_SYNC_KEY "\\" FORMAT_LPWSTR "' %ld.\n",
+ NlGlobalDBInfoArray[DBIndex].DBName,
+ RegStatus ));
+ Failed = TRUE;
+ goto Cleanup;
+ }
+
+ //
+ // Validate the returned data.
+ //
+
+ if ( KeyType != REG_BINARY ||
+ DataSize != sizeof(*FullSyncKey) ) {
+ NlPrint(( NL_CRITICAL,
+ "NlQueryFullSyncKey: Key size/type wrong'" NL_FULL_SYNC_KEY "\\" FORMAT_LPWSTR "' %ld.\n",
+ NlGlobalDBInfoArray[DBIndex].DBName,
+ RegStatus ));
+
+ Failed = TRUE;
+ goto Cleanup;
+ }
+
+ if ( FullSyncKey->Version != FULL_SYNC_KEY_VERSION ) {
+ NlPrint(( NL_CRITICAL,
+ "NlQueryFullSyncKey: Version wrong '" NL_FULL_SYNC_KEY "\\" FORMAT_LPWSTR "' %ld.\n",
+ NlGlobalDBInfoArray[DBIndex].DBName,
+ FullSyncKey->Version ));
+
+ Failed = TRUE;
+ goto Cleanup;
+ }
+
+ if ( FullSyncKey->SyncState > SamDoneState ) {
+ NlPrint(( NL_CRITICAL,
+ "NlQueryFullSyncKey: SyncState wrong '" NL_FULL_SYNC_KEY "\\" FORMAT_LPWSTR "' %ld.\n",
+ NlGlobalDBInfoArray[DBIndex].DBName,
+ FullSyncKey->SyncState ));
+
+ Failed = TRUE;
+ goto Cleanup;
+ }
+
+ //
+ // Done.
+ //
+
+ Failed = FALSE;
+
+ //
+ // Be tidy.
+ //
+Cleanup:
+
+ //
+ // If we couldn't read the key,
+ // return the default key.
+ //
+
+ if ( Failed ) {
+ RtlZeroMemory( FullSyncKey, sizeof(*FullSyncKey));
+ FullSyncKey->Version = FULL_SYNC_KEY_VERSION;
+ FullSyncKey->SyncState = NormalState;
+ }
+
+ PrintFullSyncKey( DBIndex, FullSyncKey, "Query" );
+
+ if ( ParmHandle != NULL ) {
+ RegCloseKey( ParmHandle );
+ }
+ return;
+
+}
+
+
+NTSTATUS
+NlForceStartupSync(
+ PDB_INFO DBInfo
+ )
+/*++
+
+Routine Description:
+
+ Mark the specified database that a full sync is required. The database
+ is marked in memory and on disk to ensure a full sync is completed in
+ the event of a reboot.
+
+Arguments:
+
+ DBInfo - pointer to database info structure.
+
+Return Value:
+
+ NT Status Code
+
+--*/
+{
+ NTSTATUS Status;
+ LARGE_INTEGER LargeZero;
+
+
+ IF_DEBUG( BREAKPOINT ) {
+ NlAssert( FALSE );
+ }
+
+ //
+ // Mark the in-memory structure that a full sync is required.
+ //
+
+
+ EnterCriticalSection( &NlGlobalDbInfoCritSect );
+ DBInfo->UpdateRqd = TRUE;
+ DBInfo->FullSyncRequired = TRUE;
+ LeaveCriticalSection( &NlGlobalDbInfoCritSect );
+
+ //
+ // Mark the on-disk version in-case we reboot.
+ //
+
+ LargeZero.QuadPart = 0;
+ switch (DBInfo->DBIndex) {
+
+ //
+ // Mark a SAM database.
+ //
+
+ case SAM_DB:
+ case BUILTIN_DB:
+
+ Status = SamISetSerialNumberDomain(
+ DBInfo->DBHandle,
+ &LargeZero,
+ &LargeZero,
+ (BOOLEAN) TRUE );
+
+ break;
+
+ //
+ // Mark a policy database
+ //
+
+ case LSA_DB:
+
+ Status = LsaISetSerialNumberPolicy(
+ DBInfo->DBHandle,
+ &LargeZero,
+ &LargeZero,
+ (BOOLEAN) TRUE );
+ break;
+
+ }
+
+ NlPrint((NL_SYNC,
+ "NlForceStartupSync: Setting " FORMAT_LPWSTR " serial number to Zero\n",
+ DBInfo->DBName ));
+
+ return Status;
+}
+
+
+VOID
+FreeSamSyncTables(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This function frees the SAM enum buffers
+
+Arguments:
+
+ none
+
+Return Value:
+
+ none
+
+--*/
+{
+ if( NlGlobalSamUserRids != NULL ) {
+ MIDL_user_free( NlGlobalSamUserRids );
+ NlGlobalSamUserRids = NULL;
+ }
+ NlGlobalSamUserCount = 0;
+
+ if( NlGlobalSamGroupRids != NULL ) {
+ MIDL_user_free( NlGlobalSamGroupRids );
+ NlGlobalSamGroupRids = NULL;
+ }
+ NlGlobalSamGroupCount = 0;
+
+ if( NlGlobalSamAliasesEnumBuffer != NULL ) {
+ SamIFree_SAMPR_ENUMERATION_BUFFER( NlGlobalSamAliasesEnumBuffer );
+ NlGlobalSamAliasesEnumBuffer = NULL;
+ }
+}
+
+
+VOID
+FreeLsaSyncTables(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This function frees the LSA enum buffers
+
+Arguments:
+
+ none
+
+Return Value:
+
+ none
+
+--*/
+{
+ if( NlGlobalLsaAccountsEnumBuffer.Information != NULL ) {
+
+ LsaIFree_LSAPR_ACCOUNT_ENUM_BUFFER( &NlGlobalLsaAccountsEnumBuffer );
+ NlGlobalLsaAccountsEnumBuffer.Information = NULL;
+ NlGlobalLsaAccountsEnumBuffer.EntriesRead = 0;
+ }
+
+ if( NlGlobalLsaTDomainsEnumBuffer.Information != NULL ) {
+
+ LsaIFree_LSAPR_TRUSTED_ENUM_BUFFER( &NlGlobalLsaTDomainsEnumBuffer );
+ NlGlobalLsaTDomainsEnumBuffer.Information = NULL;
+ NlGlobalLsaTDomainsEnumBuffer.EntriesRead = 0;
+ }
+
+ if( NlGlobalLsaSecretsEnumBuffer != NULL ) {
+
+ MIDL_user_free( NlGlobalLsaSecretsEnumBuffer );
+ NlGlobalLsaSecretsEnumBuffer = NULL;
+ NlGlobalLsaSecretCountReturned = 0;
+ }
+}
+
+
+NTSTATUS
+InitSamSyncTables(
+ PDB_INFO DBInfo,
+ SYNC_STATE SyncState,
+ DWORD ContinuationRid
+ )
+/*++
+
+Routine Description:
+
+ This function enumerates the users, group and alias objects from the
+ existing database and leaves the enum buffers in the global
+ pointers.
+
+Arguments:
+
+ DBInfo - pointer to database info structure.
+
+ SyncState - State sync is continuing from
+
+ ContinuationRid - Rid of the last account successfully copied
+
+Return Value:
+
+ NT Status code.
+
+ Note: The enum buffers gotten from SAM are left in the global pointers
+ and they need to be freed up by the clean up function.
+
+--*/
+{
+ NTSTATUS Status;
+
+ SAM_ENUMERATE_HANDLE EnumerationContext;
+ ULONG CountReturned;
+
+ //
+ // sanity checks
+ //
+
+ NlAssert( NlGlobalSamUserRids == NULL );
+ NlAssert( NlGlobalSamGroupRids == NULL );
+ NlAssert( NlGlobalSamAliasesEnumBuffer == NULL );
+
+
+ //
+ // Enumerate users
+ //
+
+ if ( SyncState <= UserState ) {
+ Status = SamIEnumerateAccountRids(
+ DBInfo->DBHandle,
+ SAM_USER_ACCOUNT,
+ (SyncState == UserState) ? ContinuationRid : 0, // Return RIDs greater than this
+ MAX_SAM_PREF_LENGTH,
+ &NlGlobalSamUserCount,
+ &NlGlobalSamUserRids );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ NlGlobalSamUserRids = NULL;
+ NlGlobalSamUserCount = 0;
+ goto Cleanup;
+ }
+
+ NlAssert( Status != STATUS_MORE_ENTRIES );
+ }
+
+
+ //
+ // Enumerate groups
+ //
+
+ if ( SyncState <= GroupState ) {
+ Status = SamIEnumerateAccountRids(
+ DBInfo->DBHandle,
+ SAM_GLOBAL_GROUP_ACCOUNT,
+ (SyncState == GroupState) ? ContinuationRid : 0, // Return RIDs greater than this
+ MAX_SAM_PREF_LENGTH,
+ &NlGlobalSamGroupCount,
+ &NlGlobalSamGroupRids );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ NlGlobalSamGroupRids = NULL;
+ NlGlobalSamGroupCount = 0;
+ goto Cleanup;
+ }
+
+ NlAssert( Status != STATUS_MORE_ENTRIES );
+ }
+
+
+ //
+ // Enumerate Aliases
+ //
+
+ if ( SyncState <= AliasState ) {
+ EnumerationContext = 0;
+ Status = SamrEnumerateAliasesInDomain(
+ DBInfo->DBHandle,
+ &EnumerationContext,
+ &NlGlobalSamAliasesEnumBuffer,
+ MAX_SAM_PREF_LENGTH,
+ &CountReturned );
+
+ if ( !NT_SUCCESS(Status) ) {
+ NlGlobalSamAliasesEnumBuffer = NULL;
+ goto Cleanup;
+ }
+
+ //
+ // sanity checks
+ //
+
+ NlAssert( Status != STATUS_MORE_ENTRIES );
+ NlAssert( CountReturned ==
+ NlGlobalSamAliasesEnumBuffer->EntriesRead );
+ }
+
+ //
+ // Cleanup after ourselves
+ //
+
+Cleanup:
+
+ if( Status != STATUS_SUCCESS ) {
+ FreeSamSyncTables();
+ }
+
+ return Status;
+
+}
+
+
+NTSTATUS
+InitLsaSyncTables(
+ PDB_INFO DBInfo
+ )
+/*++
+
+Routine Description:
+
+ This function enumerates the lsa account, trusted domain and secret
+ objects from the existing database and leaves the enum buffers in
+ the global pointers.
+
+Arguments:
+
+ DBInfo - pointer to database info structure.
+
+Return Value:
+
+ NT Status code.
+
+ Note: This enum buffer got from LSA are left in the global pointers
+ and they need to be freed up by the clean up function.
+
+--*/
+{
+ NTSTATUS Status;
+
+ LSA_ENUMERATION_HANDLE EnumerationContext;
+
+ //
+ // sanity checks
+ //
+
+ NlAssert( NlGlobalLsaAccountsEnumBuffer.Information == NULL );
+ NlAssert( NlGlobalLsaTDomainsEnumBuffer.Information == NULL );
+ NlAssert( NlGlobalLsaSecretsEnumBuffer == NULL );
+
+ //
+ // enumerate lsa accounts
+ //
+
+ EnumerationContext = 0;
+ Status = LsarEnumerateAccounts(
+ DBInfo->DBHandle,
+ &EnumerationContext,
+ &NlGlobalLsaAccountsEnumBuffer,
+ MAX_LSA_PREF_LENGTH );
+
+ if ( !NT_SUCCESS(Status) ) {
+
+ NlGlobalLsaAccountsEnumBuffer.Information = NULL;
+ NlGlobalLsaAccountsEnumBuffer.EntriesRead = 0;
+
+ if( Status != STATUS_NO_MORE_ENTRIES ) {
+
+ goto Cleanup;
+ }
+ }
+
+ //
+ // set this flag to indicate that we haven't received any account
+ // record from PDC during full sync.
+ //
+
+ NlGlobalLsaAccountsHack = FALSE;
+
+ NlAssert( Status != STATUS_MORE_ENTRIES );
+
+ //
+ // enumerate lsa TDomains
+ //
+
+ EnumerationContext = 0;
+ Status = LsarEnumerateTrustedDomains(
+ DBInfo->DBHandle,
+ &EnumerationContext,
+ &NlGlobalLsaTDomainsEnumBuffer,
+ MAX_LSA_PREF_LENGTH );
+
+ if ( !NT_SUCCESS(Status) ) {
+
+ NlGlobalLsaTDomainsEnumBuffer.Information = NULL;
+ NlGlobalLsaTDomainsEnumBuffer.EntriesRead = 0;
+
+ if( Status != STATUS_NO_MORE_ENTRIES ) {
+
+ goto Cleanup;
+ }
+ }
+
+ //
+ // sanity checks
+ //
+
+ NlAssert( Status != STATUS_MORE_ENTRIES );
+
+ //
+ // Enumerate secrets
+ //
+
+ EnumerationContext = 0;
+ Status = LsaIEnumerateSecrets(
+ DBInfo->DBHandle,
+ &EnumerationContext,
+ &NlGlobalLsaSecretsEnumBuffer,
+ MAX_LSA_PREF_LENGTH,
+ &NlGlobalLsaSecretCountReturned );
+
+ if ( !NT_SUCCESS(Status) ) {
+
+ NlGlobalLsaSecretsEnumBuffer = NULL;
+ NlGlobalLsaSecretCountReturned = 0;
+
+ if( Status != STATUS_NO_MORE_ENTRIES ) {
+
+ goto Cleanup;
+ }
+ }
+
+ //
+ // sanity checks
+ //
+
+ NlAssert( Status != STATUS_MORE_ENTRIES );
+
+ Status = STATUS_SUCCESS;
+
+ //
+ // Cleanup after ourselves
+ //
+
+Cleanup:
+
+ if( Status != STATUS_SUCCESS ) {
+
+ FreeLsaSyncTables();
+ }
+
+ return Status;
+
+}
+
+
+VOID
+UpdateSamSyncTables(
+ IN LOCAL_SAM_ACCOUNT_TYPE AccountType,
+ IN ULONG RelativeId
+ )
+/*++
+
+Routine Description:
+
+ Zero out the specified relative ID in the enum buffer.
+
+Arguments:
+
+ AccountType - Type of the account object.
+
+ RelativeId - Relative ID to search for.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ ULONG i;
+ ULONG Entries;
+
+ if ( AccountType == AliasAccount ) {
+ PSAMPR_RID_ENUMERATION Entry;
+ Entry = NlGlobalSamAliasesEnumBuffer->Buffer;
+
+ //
+ // If there are no entries to mark,
+ // simply return.
+ //
+
+ if ( Entry == NULL ) {
+ return;
+ }
+
+ //
+ // mark the entry.
+ //
+
+ for (i = 0; i < NlGlobalSamAliasesEnumBuffer->EntriesRead; i++ ) {
+ if ( Entry[i].RelativeId == RelativeId ) {
+ Entry[i].RelativeId = 0;
+ return;
+ }
+ }
+
+ } else {
+
+ PULONG RidArray;
+
+ switch( AccountType ) {
+ case UserAccount:
+ Entries = NlGlobalSamUserCount;
+ RidArray = NlGlobalSamUserRids;
+ break;
+
+ case GroupAccount:
+ Entries = NlGlobalSamGroupCount;
+ RidArray = NlGlobalSamGroupRids;
+ break;
+ }
+
+ //
+ // If there are no entries to mark,
+ // simply return.
+ //
+
+ if ( RidArray == NULL ) {
+ return;
+ }
+
+ //
+ // mark the entry.
+ //
+
+ for (i = 0; i < Entries; i++ ) {
+ if ( RidArray[i] == RelativeId ) {
+ RidArray[i] = 0;
+ return;
+ }
+ }
+
+
+ }
+
+
+ NlPrint((NL_SYNC_MORE, "UpdateSamSyncTables: can't find entry 0x%lx\n",
+ RelativeId ));
+
+}
+
+
+VOID
+UpdateLsaSyncTables(
+ IN LOCAL_LSA_ACCOUNT_TYPE AccountType,
+ IN PVOID Key
+ )
+/*++
+
+Routine Description:
+
+ Free the specified Key in the enum buffer.
+
+Arguments:
+
+ AccountType - Type of the account object.
+
+ Sid - Key to search for, this will either be a pointer to a SID
+ (PSID) or pointer to a secret name (LPWSTR).
+
+Return Value:
+
+ None.
+
+--*/
+{
+ ULONG i;
+ ULONG Entries;
+
+ PLSAPR_ACCOUNT_INFORMATION LsaAccountEntry;
+ PLSAPR_TRUST_INFORMATION LsaTDomainEntry;
+ PLSAPR_UNICODE_STRING LsaSecretEntry;
+
+ switch( AccountType ) {
+
+ case LsaAccount:
+ Entries = NlGlobalLsaAccountsEnumBuffer.EntriesRead;
+ LsaAccountEntry = NlGlobalLsaAccountsEnumBuffer.Information;
+
+ //
+ // received an account record.
+ //
+
+ NlGlobalLsaAccountsHack = TRUE;
+
+ //
+ // mark the entry.
+ //
+
+ for (i = 0; i < Entries; i++, LsaAccountEntry++ ) {
+
+ if ( ( LsaAccountEntry->Sid != NULL ) &&
+ RtlEqualSid( (PSID)LsaAccountEntry->Sid,
+ (PSID)Key )) {
+
+ //
+ // match found, free it up and make the pointer NULL.
+ //
+
+ MIDL_user_free( LsaAccountEntry->Sid );
+ LsaAccountEntry->Sid = NULL;
+
+ return;
+ }
+ }
+
+ break;
+
+ case LsaTDomain:
+ Entries = NlGlobalLsaTDomainsEnumBuffer.EntriesRead;
+ LsaTDomainEntry = NlGlobalLsaTDomainsEnumBuffer.Information;
+
+ for (i = 0; i < Entries; i++, LsaTDomainEntry++ ) {
+
+ if ( ( LsaTDomainEntry->Sid != NULL ) &&
+ RtlEqualSid( (PSID)LsaTDomainEntry->Sid,
+ (PSID)Key )) {
+
+ //
+ // match found, free it up and make the pointer NULL.
+ //
+
+ MIDL_user_free( LsaTDomainEntry->Sid );
+ LsaTDomainEntry->Sid = NULL;
+
+ return;
+ }
+ }
+ break;
+
+ case LsaSecret:
+ Entries = NlGlobalLsaSecretCountReturned;
+ LsaSecretEntry = NlGlobalLsaSecretsEnumBuffer;
+
+ for (i = 0; i < Entries; i++, LsaSecretEntry++ ) {
+
+ if ( ( LsaSecretEntry->Buffer != NULL ) &&
+ !wcsncmp( LsaSecretEntry->Buffer,
+ (LPWSTR)Key,
+ LsaSecretEntry->Length /
+ sizeof(WCHAR) )) {
+
+ //
+ // match found, make the pointer NULL.
+ // since secret enum buffer is a single buffer
+ // consists of serveral secret names, we make the
+ // pointer NULL, but don't free it.
+ //
+
+ LsaSecretEntry->Buffer = NULL;
+
+ return;
+ }
+ }
+ break;
+ }
+
+ NlPrint((NL_SYNC_MORE, "UpdateLsaSyncTables: can't find entry\n"));
+
+}
+
+
+NTSTATUS
+CleanSamSyncTables(
+ PDB_INFO DBInfo
+ )
+/*++
+
+Routine Description:
+
+ Delete all users, groups, and aliases that remain in the sync
+ tables. These are users, groups, and aliases that existed in the
+ local database but not in the version on the PDC.
+
+Arguments:
+
+ DBInfo - pointer to database info structure.
+
+Return Value:
+
+ NT Status code.
+
+ Note: The enum buffers got from SAM by the init function are
+ freed in this function and the pointer are reset to NULL.
+
+--*/
+{
+ NTSTATUS Status;
+ NTSTATUS RetStatus = STATUS_SUCCESS;
+
+ ULONG i;
+
+ //
+ // Delete all the left over users.
+ //
+
+ for (i = 0; i < NlGlobalSamUserCount; i++ ) {
+
+ if ( NlGlobalSamUserRids[i] != 0 ) {
+
+ Status = NlDeleteSamUser(
+ DBInfo->DBHandle,
+ NlGlobalSamUserRids[i] );
+
+ if (!NT_SUCCESS(Status)) {
+
+ NlPrint((NL_CRITICAL,
+ "CleanSamSyncTables: error deleting user %lx %lX\n",
+ NlGlobalSamUserRids[i],
+ Status ));
+
+ RetStatus = Status;
+ continue;
+ }
+
+ NlPrint((NL_SYNC_MORE,
+ "CleanSamSyncTables: deleting user %lx\n",
+ NlGlobalSamUserRids[i] ));
+ }
+ }
+
+ //
+ // Delete all the left over Groups.
+ //
+
+ for (i = 0; i < NlGlobalSamGroupCount; i++ ) {
+
+ if ( NlGlobalSamGroupRids[i] != 0 ) {
+
+ Status = NlDeleteSamGroup(
+ DBInfo->DBHandle,
+ NlGlobalSamGroupRids[i] );
+
+ if (!NT_SUCCESS(Status)) {
+
+ NlPrint((NL_CRITICAL,
+ "CleanSamSyncTables: error deleting Group %lx %lX\n",
+ NlGlobalSamGroupRids[i],
+ Status ));
+
+ RetStatus = Status;
+ continue;
+ }
+
+ NlPrint((NL_SYNC_MORE,
+ "CleanSamSyncTables: deleting group %lx\n",
+ NlGlobalSamGroupRids[i] ));
+ }
+ }
+
+ //
+ // Delete all the left over Aliases.
+ //
+
+ if ( NlGlobalSamAliasesEnumBuffer != NULL ) {
+ PSAMPR_RID_ENUMERATION Entry;
+
+ Entry = NlGlobalSamAliasesEnumBuffer->Buffer;
+
+ for (i = 0; i < NlGlobalSamAliasesEnumBuffer->EntriesRead; i++, Entry++ ) {
+
+ if ( Entry->RelativeId != 0 ) {
+
+ Status = NlDeleteSamAlias(
+ DBInfo->DBHandle,
+ Entry->RelativeId );
+
+ if (!NT_SUCCESS(Status)) {
+
+ NlPrint((NL_CRITICAL,
+ "CleanSamSyncTables: error deleting Alias %lu %lX\n",
+ Entry->RelativeId,
+ Status ));
+
+ RetStatus = Status;
+ continue;
+ }
+
+ NlPrint((NL_SYNC_MORE,
+ "CleanSamSyncTables: deleting alias %lx\n",
+ Entry->RelativeId ));
+
+ }
+ }
+ }
+
+ //
+ // free up sam enum buffers
+ //
+
+ FreeSamSyncTables();
+
+ return RetStatus;
+}
+
+
+
+NTSTATUS
+CleanLsaSyncTables(
+ PDB_INFO DBInfo
+ )
+/*++
+
+Routine Description:
+
+ Delete all Lsa Accounts, Trusted Domains, and Secrets that remain in
+ the sync tables. These are Lsa Accounts, Trusted Domains, and
+ Secrets that existed in the local database but not in the version on
+ the PDC.
+
+Arguments:
+
+ DBInfo - pointer to database info structure.
+
+Return Value:
+
+ NT Status code.
+
+ Note: The enum buffers got from LSA by the init function are
+ freed in this function and the pointer are reset to NULL.
+
+--*/
+{
+ NTSTATUS Status;
+ NTSTATUS RetStatus = STATUS_SUCCESS;
+
+ ULONG i;
+ ULONG Entries;
+
+ PLSAPR_ACCOUNT_INFORMATION LsaAccountEntry;
+ PLSAPR_TRUST_INFORMATION LsaTDomainEntry;
+ PLSAPR_UNICODE_STRING LsaSecretEntry;
+
+ LSAPR_HANDLE LsaHandle;
+
+ //
+ // Delete all the left over Lsa accounts.
+ //
+
+ Entries = NlGlobalLsaAccountsEnumBuffer.EntriesRead;
+ LsaAccountEntry = NlGlobalLsaAccountsEnumBuffer.Information;
+
+ //
+ // if no account record received then the PDC must be running
+ // old build that can't enumerate accounts from LSA database. So
+ // don't delete the existing accounts on this database.
+ //
+
+ if( NlGlobalLsaAccountsHack == TRUE ) {
+
+ for (i = 0; i < Entries; i++, LsaAccountEntry++ ) {
+
+ if ( LsaAccountEntry->Sid != NULL ) {
+
+ Status = LsarOpenAccount(
+ DBInfo->DBHandle,
+ LsaAccountEntry->Sid,
+ 0, // No desired access
+ &LsaHandle );
+
+ if ( (!NT_SUCCESS(Status)) ||
+ (!NT_SUCCESS(
+ Status = LsarDelete( LsaHandle ))) ) {
+
+ NlPrint((NL_CRITICAL,
+ "CleanLsaSyncTables: error deleting LsaAccount %lX\n",
+ Status ));
+
+ RetStatus = Status;
+ continue;
+ }
+
+ }
+ }
+ }
+
+ //
+ // Delete all the left over trusted domain accounts.
+ //
+
+ Entries = NlGlobalLsaTDomainsEnumBuffer.EntriesRead;
+ LsaTDomainEntry = NlGlobalLsaTDomainsEnumBuffer.Information;
+
+ for (i = 0; i < Entries; i++, LsaTDomainEntry++ ) {
+
+ if ( LsaTDomainEntry->Sid != NULL ) {
+
+ Status = LsarOpenTrustedDomain(
+ DBInfo->DBHandle,
+ LsaTDomainEntry->Sid,
+ 0, // No desired access
+ &LsaHandle );
+
+ if ( (!NT_SUCCESS(Status)) ||
+ (!NT_SUCCESS(
+ Status = LsarDelete( LsaHandle ))) ) {
+
+ NlPrint((NL_CRITICAL,
+ "CleanLsaSyncTables: error deleting "
+ "TrustedDomain %lx\n",
+ Status ));
+
+ RetStatus = Status;
+ continue;
+ }
+
+ //
+ // The BDC needs to keep its internal trust list up to date.
+ //
+
+ NlUpdateTrustListBySid( LsaTDomainEntry->Sid, NULL );
+
+ }
+ }
+
+ //
+ // Delete all the left over secrets.
+ //
+
+ Entries = NlGlobalLsaSecretCountReturned;
+ LsaSecretEntry = (PLSAPR_UNICODE_STRING)NlGlobalLsaSecretsEnumBuffer;
+
+ for (i = 0; i < Entries; i++, LsaSecretEntry++ ) {
+
+ if ( LsaSecretEntry->Buffer != 0 ) {
+
+ //
+ // ignore local secret objects.
+ //
+
+ if( (LsaSecretEntry->Length / sizeof(WCHAR) >
+ LSA_GLOBAL_SECRET_PREFIX_LENGTH ) &&
+ (_wcsnicmp( LsaSecretEntry->Buffer,
+ LSA_GLOBAL_SECRET_PREFIX,
+ LSA_GLOBAL_SECRET_PREFIX_LENGTH ) == 0) ) {
+
+
+ Status = LsarOpenSecret(
+ DBInfo->DBHandle,
+ LsaSecretEntry,
+ 0, // No desired access
+ &LsaHandle );
+
+ if ( (!NT_SUCCESS(Status)) ||
+ (!NT_SUCCESS(
+ Status = LsarDelete( LsaHandle ))) ) {
+
+ NlPrint((NL_CRITICAL,
+ "CleanSyncTables: "
+ "error deleting LsaSecret (%wZ) %lx\n",
+ LsaSecretEntry, Status ));
+
+ RetStatus = Status;
+ continue;
+ }
+ }
+ }
+ }
+
+ //
+ // free up sam enum buffers
+ //
+
+ FreeLsaSyncTables();
+
+ return RetStatus;
+}
+
+
+NTSTATUS
+NlRecoverConflictingAccount(
+ IN PNETLOGON_DELTA_ENUM Delta,
+ IN PDB_INFO DBInfo,
+ ULONG ConflictingRid,
+ PSESSION_INFO SessionInfo,
+ NTSTATUS Status,
+ BOOLEAN CleanSyncTable,
+ PBOOLEAN ResourceError
+ )
+/*++
+
+Routine Description:
+
+ This procedure recovers the replication from conflicting account. It
+ deletes the conflicting account and create a new account with the
+ given RID.
+
+Arguments:
+
+ Delta: Delta record that is been processed.
+
+ ConflictingRid: Rid of the conflicting account currently on the
+ database.
+
+ SessionInfo: Information shared between PDC and BDC
+
+ Status: Status returned by SamICreateAccountByRid() call.
+
+ CleanSyncTable: if TRUE the Conflicting account is removed from sync
+ table.
+
+ ResourceError: Returns true if this machine is out of resources
+
+Return Value:
+
+ NT status code
+
+--*/
+{
+ NETLOGON_DELTA_TYPE DeltaType;
+
+ ULONG SaveRID;
+ ULONG DummyRID;
+
+ LOCAL_SAM_ACCOUNT_TYPE AccountType;
+
+ //
+ // if we are trying to a new add user, group or alias
+ // object and if there is an object already exists
+ // then delete the conflicting object and try adding
+ // new object again.
+ //
+
+ DeltaType = Delta->DeltaType;
+
+ if ( ( Status == STATUS_USER_EXISTS ||
+ Status == STATUS_GROUP_EXISTS ||
+ Status == STATUS_ALIAS_EXISTS ) &&
+
+ ( DeltaType == AddOrChangeUser ||
+ DeltaType == AddOrChangeGroup ||
+ DeltaType == AddOrChangeAlias ) ) {
+
+ NlPrint((NL_SYNC,
+ "NlRecoverConflictingAccount: "
+ "conflicting Account: DeltaType (%d), "
+ "Status(%lx), ConflictingRid(%lx)\n",
+ DeltaType, Status, ConflictingRid ));
+
+ SaveRID = Delta->DeltaID.Rid;
+
+ //
+ // Delete conflicting user/group/alias.
+ //
+
+ if ( Status == STATUS_USER_EXISTS ) {
+ Delta->DeltaType = DeleteUser;
+ AccountType = UserAccount;
+
+ } else if ( Status == STATUS_GROUP_EXISTS ) {
+ Delta->DeltaType = DeleteGroup;
+ AccountType = GroupAccount;
+
+ } else {
+ Delta->DeltaType = DeleteAlias;
+ AccountType = AliasAccount;
+ }
+
+ Delta->DeltaID.Rid = ConflictingRid;
+
+ Status = NlUnpackSam( Delta, DBInfo, &DummyRID, SessionInfo );
+
+ Delta->DeltaType = DeltaType;
+ Delta->DeltaID.Rid = SaveRID;
+
+ if ( NT_SUCCESS(Status) ) {
+
+ //
+ // Delete the deleted user/group/alias from the
+ // sync tables.
+ //
+
+ if( CleanSyncTable ) {
+
+ UpdateSamSyncTables( AccountType, ConflictingRid );
+ }
+
+ Delta->DeltaType = DeltaType;
+ Delta->DeltaID.Rid = SaveRID;
+
+ //
+ // Add the group
+ //
+
+ Status = NlUnpackSam( Delta, DBInfo, &DummyRID, SessionInfo );
+
+ }
+
+ }
+
+ //
+ // Log the failure
+ //
+
+ if ( !NT_SUCCESS( Status )) {
+
+ NlPrint((NL_CRITICAL,
+ "Unsuccessful NlUnpackSam: Status (%lx)\n",
+ Status ));
+
+ //
+ // Log which particular account had a problem.
+ //
+
+ NlLogSyncError( Delta, DBInfo, Status );
+
+ }
+
+ //
+ // If we failed for some temporary reason,
+ // stop the sync now to let the system cure itself.
+ //
+
+ *ResourceError = ( Status == STATUS_DISK_FULL ||
+ Status == STATUS_NO_MEMORY ||
+ Status == STATUS_INSUFFICIENT_RESOURCES);
+
+ return Status;
+}
+
+
+
+
+
+ULONG
+NlComputeSyncSleepTime(
+ IN PLARGE_INTEGER ApiStartTime,
+ IN PLARGE_INTEGER ApiFinishTime
+ )
+/*++
+
+Routine Description:
+
+ Compute the amount of time the caller should sleep to ensure we stay
+ within the ReplicationGovernor percentage.
+
+ This routine is called after all processing of the previous delta has
+ been completed on the BDC.
+
+Arguments:
+
+ ApiStartTime -- Time when the previous call to the PDC was made.
+
+ ApiFinishTime -- Time when the previous call to the PDC completed.
+
+Return Value:
+
+ Returns the time to sleep (in milliseconds)
+
+--*/
+{
+ LARGE_INTEGER GoalTimePerLoop;
+ LARGE_INTEGER TimeSpentSoFar;
+ LARGE_INTEGER TimeToSleep;
+ LARGE_INTEGER TimeOnWire;
+
+ //
+ // If the Governor isn't restricting the call rate,
+ // return now indicating no sleep is needed.
+ //
+ if ( NlGlobalGovernorParameter == 100 ) {
+ return 0;
+ }
+
+ //
+ // Since this option will only be used on slow WAN links,
+ // approximate the time spent on the wire as the time it took to complete
+ // the API call to the PDC.
+ //
+
+ TimeOnWire.QuadPart = ApiFinishTime->QuadPart - ApiStartTime->QuadPart;
+ if ( TimeOnWire.QuadPart <= 0 ) {
+ return 0;
+ }
+
+ //
+ // Compute the amount of time we need to spend grand total
+ // between successive calls to the PDC.
+ //
+
+ GoalTimePerLoop.QuadPart = TimeOnWire.QuadPart * 100;
+ GoalTimePerLoop.QuadPart /= NlGlobalGovernorParameter;
+
+ //
+ // Compute the amount of time we actually spent since the
+ // last call to the PDC.
+ //
+
+ (VOID)NtQuerySystemTime( &TimeSpentSoFar );
+ TimeSpentSoFar.QuadPart -= ApiStartTime->QuadPart;
+ if ( TimeSpentSoFar.QuadPart <= 0 ) {
+ return 0;
+ }
+
+ //
+ // Compute the amount of time we need to sleep.
+ //
+
+ TimeToSleep.QuadPart = GoalTimePerLoop.QuadPart - TimeSpentSoFar.QuadPart;
+ if ( TimeToSleep.QuadPart <= 0 ) {
+ return 0;
+ }
+
+ //
+ // Covert from 100-ns to milliseconds
+ //
+
+ TimeToSleep.QuadPart /= 10000;
+
+ if ( TimeToSleep.QuadPart > MAX_SYNC_SLEEP_TIME ) {
+ return MAX_SYNC_SLEEP_TIME;
+ }
+
+ return (DWORD)TimeToSleep.QuadPart;
+}
+
+
+NTSTATUS
+NlSynchronize(
+ IN PDB_INFO DBInfo
+ )
+/*++
+
+Routine Description:
+
+ To bring this database in sync with the primary. This function will
+ be called if synchronization was specified from command line via
+ /SYNC:Yes or STATUS_SYNCHRONIZATION_REQUIRED was encountered while
+ doing NetAccountDeltas or if we are hopelessly out of sync due to a
+ crash and are in recovery mode.
+
+ If this function failed to complete then the existing SAM database
+ on this machine will be hosed and could not be relied upon. Hence
+ if we fail the caller of this function should reset the primary
+ cookie in the header so that an automatic ReSync is forced as soon
+ as next announcement from the primary
+
+Arguments:
+
+ NONE.
+
+Return Value:
+
+ NT Status Code.
+
+--*/
+{
+ NTSTATUS Status;
+ NETLOGON_AUTHENTICATOR OurAuthenticator;
+ NETLOGON_AUTHENTICATOR ReturnAuthenticator;
+
+ ULONG SamSyncContext;
+ SYNC_STATE SyncStateForPdc;
+
+ NTSTATUS SyncStatus;
+ PNETLOGON_DELTA_ENUM_ARRAY DeltaArray = NULL;
+ DWORD DeltaIndex;
+ ULONG PreferredMaximum;
+
+ FULL_SYNC_KEY FullSyncKey;
+
+ LARGE_INTEGER ApiStartTime;
+ LARGE_INTEGER ApiFinishTime;
+ DWORD SyncSleepTime;
+
+ SESSION_INFO SessionInfo;
+
+ ULONG ConflictingRid;
+
+ LPWSTR MsgStrings[3];
+ BOOLEAN FirstTry = TRUE;
+
+ //
+ // Initialization.
+ //
+
+ PreferredMaximum = (SAM_DELTA_BUFFER_SIZE * NlGlobalGovernorParameter) / 100;
+
+ //
+ // Ensure that if we get interrupted in the middle that a newly started
+ // netlogon service will sync.
+ //
+
+ Status = NlForceStartupSync( DBInfo );
+ if ( !NT_SUCCESS(Status) ) {
+ return Status;
+ }
+
+ //
+ // If we're not currently authenticated with the PDC,
+ // do so now.
+ //
+
+FirstTryFailed:
+ if ( !NlTimeoutSetWriterClientSession( NlGlobalClientSession, WRITER_WAIT_PERIOD ) ) {
+ NlPrint((NL_CRITICAL, "NlSynchronize: Can't become writer of client session.\n" ));
+ Status = STATUS_NO_LOGON_SERVERS;
+ goto Cleanup;
+ }
+
+ if ( NlGlobalClientSession->CsState != CS_AUTHENTICATED ) {
+
+ Status = NlSessionSetup( NlGlobalClientSession );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ NlResetWriterClientSession( NlGlobalClientSession );
+ goto Cleanup;
+ }
+ }
+
+ //
+ // Grab a copy of the Negotiated Flags
+ //
+
+ SessionInfo.NegotiatedFlags = NlGlobalClientSession->CsNegotiatedFlags;
+
+ NlResetWriterClientSession( NlGlobalClientSession );
+
+
+
+ //
+ // If this thread has been asked to leave, do so.
+ //
+
+ if ( NlGlobalReplicatorTerminate ) {
+ NlPrint((NL_SYNC, "NlSynchronize: Asked to terminate\n" ));
+ Status = STATUS_THREAD_IS_TERMINATING;
+ goto Cleanup;
+ }
+
+ //
+ // Determine where the full sync left off.
+ //
+
+ NlQueryFullSyncKey( DBInfo->DBIndex, &FullSyncKey );
+
+ if ( SessionInfo.NegotiatedFlags & NETLOGON_SUPPORTS_FULL_SYNC_RESTART ) {
+ SamSyncContext = FullSyncKey.ContinuationRid;
+ SyncStateForPdc = FullSyncKey.SyncState;
+ } else {
+ SamSyncContext = 0;
+ SyncStateForPdc = NormalState;
+ }
+
+ //
+ // build sync tables
+ //
+
+ if ( FirstTry ) {
+ if( DBInfo->DBIndex == LSA_DB ) {
+ Status = InitLsaSyncTables( DBInfo );
+ } else {
+ Status = InitSamSyncTables( DBInfo, SyncStateForPdc, SamSyncContext );
+ }
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto Cleanup;
+ }
+ }
+
+ //
+ // Loop calling the PDC to get a bunch of deltas
+ //
+
+ SyncSleepTime = 0;
+ for (;;) {
+
+ DEFSSIAPITIMER;
+
+ INITSSIAPITIMER;
+
+ //
+ // Wait a while so we don't overburden the secure channel.
+ //
+
+ if ( SyncSleepTime != 0 ) {
+ NlPrint(( NL_SYNC,
+ "NlSynchronize: sleeping %ld for the governor.\n",
+ SyncSleepTime ));
+ (VOID) WaitForSingleObject( NlGlobalReplicatorTerminateEvent, SyncSleepTime );
+ }
+
+ //
+ // If this thread has been asked to leave, do so.
+ //
+
+ if ( NlGlobalReplicatorTerminate ) {
+ NlPrint((NL_SYNC, "NlSynchronize: Asked to terminate\n" ));
+ Status = STATUS_THREAD_IS_TERMINATING;
+ goto Cleanup;
+ }
+
+ //
+ // Build the Authenticator for this request to the PDC.
+ //
+
+ if ( !NlTimeoutSetWriterClientSession( NlGlobalClientSession, WRITER_WAIT_PERIOD ) ) {
+ NlPrint((NL_CRITICAL, "NlSynchronize: Can't become writer of client session.\n" ));
+ Status = STATUS_NO_LOGON_SERVERS;
+ goto Cleanup;
+ }
+
+ if ( NlGlobalClientSession->CsState != CS_AUTHENTICATED ) {
+ NlPrint((NL_CRITICAL, "NlSynchronize: Client session dropped.\n" ));
+ Status = NlGlobalClientSession->CsConnectionStatus;
+ NlResetWriterClientSession( NlGlobalClientSession );
+ goto Cleanup;
+ }
+
+ NlBuildAuthenticator(
+ &NlGlobalClientSession->CsAuthenticationSeed,
+ &NlGlobalClientSession->CsSessionKey,
+ &OurAuthenticator);
+
+
+ //
+ // copy session key to decrypt sensitive information.
+ // (Copy SessionKey again since we need to grab SessionKey with
+ // the write lock held and call the API to the PDC with the same
+ // write lock..)
+
+ SessionInfo.SessionKey = NlGlobalClientSession->CsSessionKey;
+ SessionInfo.NegotiatedFlags = NlGlobalClientSession->CsNegotiatedFlags;
+
+
+ SyncStatus = NlStartApiClientSession( NlGlobalClientSession, FALSE );
+
+
+ if (NT_SUCCESS(SyncStatus)) {
+ STARTSSIAPITIMER;
+
+ ApiStartTime = NlGlobalClientSession->CsApiTimer.StartTime;
+
+ if ( SessionInfo.NegotiatedFlags & NETLOGON_SUPPORTS_FULL_SYNC_RESTART ) {
+
+ SyncStatus = I_NetDatabaseSync2(
+ NlGlobalClientSession->CsUncServerName,
+ NlGlobalUnicodeComputerName,
+ &OurAuthenticator,
+ &ReturnAuthenticator,
+ DBInfo->DBIndex,
+ SyncStateForPdc,
+ &SamSyncContext,
+ &DeltaArray,
+ PreferredMaximum );
+ } else {
+ SyncStatus = I_NetDatabaseSync(
+ NlGlobalClientSession->CsUncServerName,
+ NlGlobalUnicodeComputerName,
+ &OurAuthenticator,
+ &ReturnAuthenticator,
+ DBInfo->DBIndex,
+ &SamSyncContext,
+ &DeltaArray,
+ PreferredMaximum );
+ }
+
+ if ( NlGlobalGovernorParameter != 100 ) {
+ (VOID) NtQuerySystemTime( &ApiFinishTime );
+ }
+ STOPSSIAPITIMER;
+ }
+ (VOID)NlFinishApiClientSession( NlGlobalClientSession, TRUE );
+
+ NlPrint((NL_REPL_TIME,"I_NetDatabaseSync Time:\n"));
+ PRINTSSIAPITIMER;
+
+ //
+ // On an access denied error, force an authentication.
+ //
+ // Returned authenticator may be invalid.
+ //
+
+ if ( (SyncStatus == STATUS_ACCESS_DENIED) ||
+ ( !NlUpdateSeed(
+ &NlGlobalClientSession->CsAuthenticationSeed,
+ &ReturnAuthenticator.Credential,
+ &NlGlobalClientSession->CsSessionKey) ) ) {
+
+ if ( NT_SUCCESS(SyncStatus) ) {
+ Status = STATUS_ACCESS_DENIED;
+ } else {
+ Status = SyncStatus;
+ }
+
+ NlPrint((NL_CRITICAL, "NlSynchronize: authentication failed: %lx\n", Status ));
+
+ NlSetStatusClientSession( NlGlobalClientSession, Status );
+
+ NlResetWriterClientSession( NlGlobalClientSession );
+
+ //
+ // Perhaps the netlogon service on the PDC has just restarted.
+ // Try just once to set up a session to the server again.
+ //
+ if ( FirstTry && SyncStatus == STATUS_ACCESS_DENIED ) {
+ FirstTry = FALSE;
+ goto FirstTryFailed;
+ }
+
+ goto Cleanup;
+ }
+
+ FirstTry = FALSE;
+ SyncStateForPdc = NormalState;
+
+ NlResetWriterClientSession( NlGlobalClientSession );
+
+
+
+ //
+ // Finally, error out
+ //
+
+ if ( !NT_SUCCESS( SyncStatus ) ) {
+
+ NlPrint((NL_CRITICAL,
+ "NlSynchronize: "
+ "I_NetDatabaseSync returning: Status (%lx)\n",
+ SyncStatus ));
+
+ Status = SyncStatus;
+ goto Cleanup;
+ }
+
+
+
+ //
+ // Loop through the deltas updating the local User and Group list.
+ //
+
+ for ( DeltaIndex = 0;
+ DeltaIndex < DeltaArray->CountReturned;
+ DeltaIndex++ ) {
+
+ //
+ // If this thread has been asked to leave, do so.
+ //
+
+ if ( NlGlobalReplicatorTerminate ) {
+ NlPrint((NL_SYNC, "NlSynchronize: Asked to terminate\n" ));
+ Status = STATUS_THREAD_IS_TERMINATING;
+ goto Cleanup;
+ }
+
+ //
+ // Unpack the buffer and apply changes to our database
+ //
+
+ Status = NlUnpackSam(
+ &(DeltaArray->Deltas)[DeltaIndex],
+ DBInfo,
+ &ConflictingRid,
+ &SessionInfo );
+
+ if ( ! NT_SUCCESS( Status ) ) {
+ BOOLEAN ResourceError;
+
+ Status = NlRecoverConflictingAccount(
+ &(DeltaArray->Deltas)[DeltaIndex],
+ DBInfo,
+ ConflictingRid,
+ &SessionInfo,
+ Status,
+ TRUE,
+ &ResourceError );
+
+ if ( !NT_SUCCESS( Status ) ) {
+
+ //
+ // If we failed for some temporary reason,
+ // stop the full sync now to let the system cure itself.
+ //
+
+ if ( ResourceError ) {
+ goto Cleanup;
+ }
+
+ //
+ // If the PDC supports redo,
+ // Write this delta to the redo log and otherwise ignore
+ // the failure.
+ //
+
+ if ( SessionInfo.NegotiatedFlags & NETLOGON_SUPPORTS_REDO ){
+ NTSTATUS TempStatus;
+
+ TempStatus = NlWriteDeltaToChangeLog(
+ &NlGlobalRedoLogDesc,
+ &(DeltaArray->Deltas)[DeltaIndex],
+ DBInfo->DBIndex,
+ NULL );
+
+ //
+ // If we successfully wrote to the redo log,
+ // there's no reason to fail the full sync
+ //
+
+ if ( NT_SUCCESS( TempStatus )) {
+ Status = STATUS_SUCCESS;
+ }
+
+ }
+
+ //
+ // If this is an unexpected failure,
+ // continue processing deltas.
+ //
+ // It is better to continue copying the database as
+ // much as possible than to quit now. The theory is that
+ // we've stumbled upon some circumstance we haven't
+ // anticipated. We'll put this BDC in the best shape
+ // we possibly can.
+ //
+ // Remember this status code until the end.
+ //
+
+ if ( FullSyncKey.CumulativeStatus == STATUS_SUCCESS ) {
+ FullSyncKey.CumulativeStatus = Status;
+ }
+
+ continue;
+ }
+ }
+
+ //
+ // Handle each delta type differently.
+ //
+
+ switch ( DeltaArray->Deltas[DeltaIndex].DeltaType ) {
+
+ //
+ // Capture the Domain header information as it appeared at the
+ // start of the SYNC on the PDC. We use this value to ensure
+ // we don't miss any Deltas.
+ //
+
+ case AddOrChangeDomain:
+
+ OLD_TO_NEW_LARGE_INTEGER(
+ (DeltaArray->Deltas[DeltaIndex]).DeltaUnion.
+ DeltaDomain->DomainModifiedCount,
+ FullSyncKey.PdcSerialNumber );
+
+ OLD_TO_NEW_LARGE_INTEGER(
+ (DeltaArray->Deltas[DeltaIndex]).DeltaUnion.
+ DeltaDomain->DomainCreationTime,
+ FullSyncKey.PdcDomainCreationTime );
+
+ break;
+
+ case AddOrChangeGroup:
+ UpdateSamSyncTables(
+ GroupAccount,
+ DeltaArray->Deltas[DeltaIndex].DeltaID.Rid);
+
+ FullSyncKey.SyncState = GroupState;
+ FullSyncKey.ContinuationRid =
+ DeltaArray->Deltas[DeltaIndex].DeltaID.Rid;
+ break;
+
+ case AddOrChangeUser:
+ UpdateSamSyncTables(
+ UserAccount,
+ DeltaArray->Deltas[DeltaIndex].DeltaID.Rid);
+
+ FullSyncKey.SyncState = UserState;
+ FullSyncKey.ContinuationRid =
+ DeltaArray->Deltas[DeltaIndex].DeltaID.Rid;
+ break;
+
+ case ChangeGroupMembership:
+ FullSyncKey.SyncState = GroupMemberState;
+ FullSyncKey.ContinuationRid =
+ DeltaArray->Deltas[DeltaIndex].DeltaID.Rid;
+ break;
+
+ case AddOrChangeAlias:
+ UpdateSamSyncTables(
+ AliasAccount,
+ DeltaArray->Deltas[DeltaIndex].DeltaID.Rid);
+
+ FullSyncKey.SyncState = AliasState;
+ FullSyncKey.ContinuationRid = 0;
+ break;
+
+ case ChangeAliasMembership:
+ FullSyncKey.SyncState = AliasMemberState;
+ FullSyncKey.ContinuationRid = 0;
+ break;
+
+ //
+ // Capture the policy header information as it appeared at
+ // the start of the SYNC on the PDC. We use this value to
+ // ensure we don't miss any Deltas.
+ //
+
+ case AddOrChangeLsaPolicy:
+
+ OLD_TO_NEW_LARGE_INTEGER(
+ (DeltaArray->Deltas[DeltaIndex]).DeltaUnion.
+ DeltaPolicy->ModifiedId,
+ FullSyncKey.PdcSerialNumber );
+
+ OLD_TO_NEW_LARGE_INTEGER(
+ (DeltaArray->Deltas[DeltaIndex]).DeltaUnion.
+ DeltaPolicy->DatabaseCreationTime,
+ FullSyncKey.PdcDomainCreationTime );
+
+ break;
+
+ case AddOrChangeLsaAccount:
+ UpdateLsaSyncTables(
+ LsaAccount,
+ DeltaArray->Deltas[DeltaIndex].DeltaID.Sid);
+ break;
+
+ case AddOrChangeLsaTDomain:
+ UpdateLsaSyncTables(
+ LsaTDomain,
+ DeltaArray->Deltas[DeltaIndex].DeltaID.Sid);
+ break;
+
+ case AddOrChangeLsaSecret:
+ UpdateLsaSyncTables(
+ LsaSecret,
+ DeltaArray->Deltas[DeltaIndex].DeltaID.Name);
+ break;
+ }
+
+ }
+
+ MIDL_user_free( DeltaArray );
+ DeltaArray = NULL;
+
+ //
+ // If the PDC has given us all of the deltas it has,
+ // we're all done.
+ //
+
+ if ( SyncStatus == STATUS_SUCCESS ) {
+ Status = STATUS_SUCCESS;
+ break;
+ }
+
+ //
+ // Force SAM to disk before saving the sync key.
+ //
+ // This'll ensure that the sync key doesn't indicate SAM is more
+ // recent than it really is.
+ //
+
+ if( DBInfo->DBIndex != LSA_DB ) {
+ LARGE_INTEGER LargeZero;
+
+ LargeZero.QuadPart = 0;
+
+ Status = SamISetSerialNumberDomain(
+ DBInfo->DBHandle,
+ &LargeZero,
+ &LargeZero,
+ (BOOLEAN) FALSE );
+
+ }
+
+
+ //
+ // Remember how far we've gotten in case a reboot happens.
+ //
+
+ NlSetFullSyncKey( DBInfo->DBIndex, &FullSyncKey );
+
+
+ //
+ // Compute the amount of time we need to wait before calling the PDC
+ // again.
+ //
+
+ SyncSleepTime = NlComputeSyncSleepTime( &ApiStartTime,
+ &ApiFinishTime );
+
+ }
+
+
+ //
+ // We've finished the full sync.
+ //
+ // If there were any errors we ignored along the way,
+ // don't clean up.
+ //
+
+ if ( !NT_SUCCESS(FullSyncKey.CumulativeStatus) ) {
+ Status = FullSyncKey.CumulativeStatus;
+
+ //
+ // Mark that the next full sync needs to start from the beginning.
+ //
+ NlSetFullSyncKey( DBInfo->DBIndex, NULL );
+
+ goto Cleanup;
+ }
+
+
+ //
+ // We've successfully replicated all information from the PDC.
+ //
+ // Delete any objects that don't exist in the PDC.
+ //
+
+ if( DBInfo->DBIndex == LSA_DB ) {
+ CleanLsaSyncTables( DBInfo );
+ } else {
+ CleanSamSyncTables( DBInfo );
+ }
+
+
+ //
+ // Set the domain/policy creation time and modified count to their
+ // values on the PDC at the beginning of the Sync.
+ //
+ // Reset the change log before mucking with the serial number in
+ // the change log descriptor.
+ //
+
+ LOCK_CHANGELOG();
+
+ (VOID) NlFixChangeLog( &NlGlobalChangeLogDesc,
+ DBInfo->DBIndex,
+ FullSyncKey.PdcSerialNumber,
+ FALSE ); // Don't copy deleted records to redo log
+ NlGlobalChangeLogDesc.SerialNumber[DBInfo->DBIndex] = FullSyncKey.PdcSerialNumber;
+ DBInfo->CreationTime = FullSyncKey.PdcDomainCreationTime;
+ UNLOCK_CHANGELOG();
+
+
+ NlPrint((NL_SYNC,
+ "NlSynchronize: Setting " FORMAT_LPWSTR " serial number to %lx %lx\n",
+ DBInfo->DBName,
+ FullSyncKey.PdcSerialNumber.HighPart,
+ FullSyncKey.PdcSerialNumber.LowPart ));
+
+ if( DBInfo->DBIndex == LSA_DB ) {
+
+ Status = LsaISetSerialNumberPolicy(
+ DBInfo->DBHandle,
+ &FullSyncKey.PdcSerialNumber,
+ &FullSyncKey.PdcDomainCreationTime,
+ (BOOLEAN) FALSE );
+
+ } else {
+
+ Status = SamISetSerialNumberDomain(
+ DBInfo->DBHandle,
+ &FullSyncKey.PdcSerialNumber,
+ &FullSyncKey.PdcDomainCreationTime,
+ (BOOLEAN) FALSE );
+
+ }
+
+
+ if (!NT_SUCCESS(Status)) {
+
+ NlPrint((NL_CRITICAL,
+ "NlSynchronize: Unable to set serial number: Status (%lx)\n",
+ Status ));
+
+ goto Cleanup;
+ }
+
+ //
+ // Mark that there is no full sync to continue
+ //
+
+ NlSetFullSyncKey( DBInfo->DBIndex, NULL );
+
+ //
+ // Mark that fact permanently in the database.
+ //
+ (VOID) NlResetFirstTimeFullSync( DBInfo->DBIndex );
+
+ Status = STATUS_SUCCESS;
+
+Cleanup:
+
+ //
+ // write event log
+ //
+
+ MsgStrings[0] = DBInfo->DBName;
+ MsgStrings[1] = NlGlobalUncPrimaryName;
+
+ if ( !NT_SUCCESS( Status ) ) {
+
+ if ( !NlGlobalReplicatorTerminate ) {
+
+ MsgStrings[2] = (LPWSTR) Status;
+ NlpWriteEventlog(
+ NELOG_NetlogonFullSyncFailed,
+ EVENTLOG_WARNING_TYPE,
+ (LPBYTE)&Status,
+ sizeof(Status),
+ MsgStrings,
+ 3 | LAST_MESSAGE_IS_NTSTATUS );
+ }
+ } else {
+
+ NlpWriteEventlog(
+ NELOG_NetlogonFullSyncSuccess,
+ EVENTLOG_INFORMATION_TYPE,
+ NULL,
+ 0,
+ MsgStrings,
+ 2 );
+ }
+
+ //
+ // Free locally used resources.
+ //
+
+ //
+ // free up sync tables
+ //
+
+ if( DBInfo->DBIndex == LSA_DB ) {
+ FreeLsaSyncTables();
+ } else {
+ FreeSamSyncTables();
+ }
+
+ if ( DeltaArray != NULL ) {
+ MIDL_user_free( DeltaArray );
+ }
+
+ if ( !NT_SUCCESS( Status ) ) {
+
+ NlPrint((NL_CRITICAL,
+ "NlSynchronize: returning unsuccessful: Status (%lx)\n",
+ Status ));
+ }
+
+ return Status;
+}
+
+
+NTSTATUS
+NlReplicateDeltas(
+ IN PDB_INFO DBInfo
+ )
+/*++
+
+Routine Description:
+
+ Get recent updates from primary and update our private UAS database.
+ Once this function starts it will get all the updates from primary
+ till our database is in sync.
+
+ This function is executed only at machines which may be running
+ NETLOGON service with member/backup role.
+
+ This procedure executes only in the replicator thread.
+
+Arguments:
+
+ ReplParam - Parameters governing the behavior of the replicator thread.
+
+Return Value:
+
+ Status of the operation.
+
+--*/
+{
+ NTSTATUS DeltaStatus;
+ NTSTATUS Status;
+
+ NETLOGON_AUTHENTICATOR OurAuthenticator;
+ NETLOGON_AUTHENTICATOR ReturnAuthenticator;
+
+ PNETLOGON_DELTA_ENUM_ARRAY DeltaArray = NULL;
+ DWORD DeltaIndex;
+ ULONG PreferredMaximum;
+
+ ULONG ConflictingRid;
+ LARGE_INTEGER LocalSerialNumber;
+ OLD_LARGE_INTEGER OldLocalSerialNumber;
+ LARGE_INTEGER ExpectedSerialNumber;
+
+ LARGE_INTEGER ApiStartTime;
+ LARGE_INTEGER ApiFinishTime;
+ DWORD SyncSleepTime;
+
+ SESSION_INFO SessionInfo;
+
+ DWORD DeltasApplied;
+ BOOLEAN FirstTry = TRUE;
+ BOOLEAN ForceFullSync = FALSE;
+
+ LPWSTR MsgStrings[3];
+
+ //
+ // Initialization.
+ //
+
+ PreferredMaximum = (SAM_DELTA_BUFFER_SIZE * NlGlobalGovernorParameter) / 100;
+
+
+
+ //
+ // If we're not currently authenticated with the PDC,
+ // do so now.
+ //
+
+FirstTryFailed:
+ if ( !NlTimeoutSetWriterClientSession( NlGlobalClientSession, WRITER_WAIT_PERIOD ) ) {
+ NlPrint((NL_CRITICAL, "NlReplicateDeltas: Can't become writer of client session.\n" ));
+ Status = STATUS_NO_LOGON_SERVERS;
+ goto Cleanup;
+ }
+
+ if ( NlGlobalClientSession->CsState != CS_AUTHENTICATED ) {
+ Status = NlSessionSetup( NlGlobalClientSession );
+ if ( !NT_SUCCESS( Status ) ) {
+ NlResetWriterClientSession( NlGlobalClientSession );
+ goto Cleanup;
+ }
+ }
+
+ NlResetWriterClientSession( NlGlobalClientSession );
+
+
+
+ //
+ // Loop calling the PDC to get a bunch of deltas
+ //
+
+ DeltasApplied = 0;
+ SyncSleepTime = 0;
+
+ for (;;) {
+
+ DEFSSIAPITIMER;
+
+ INITSSIAPITIMER;
+
+ //
+ // Wait a while so we don't overburden the secure channel.
+ //
+
+ if ( SyncSleepTime != 0 ) {
+ NlPrint(( NL_SYNC,
+ "NlReplicateDeltas: sleeping %ld for the governor.\n",
+ SyncSleepTime ));
+ (VOID) WaitForSingleObject( NlGlobalReplicatorTerminateEvent, SyncSleepTime );
+ }
+
+ //
+ // If this thread has been asked to leave, do so.
+ //
+
+ if ( NlGlobalReplicatorTerminate ) {
+ NlPrint((NL_SYNC, "NlReplicateDeltas: Asked to terminate\n" ));
+ Status = STATUS_THREAD_IS_TERMINATING;
+ goto Cleanup;
+ }
+
+ //
+ // Build the Authenticator for this request to the PDC.
+ //
+
+ if ( !NlTimeoutSetWriterClientSession( NlGlobalClientSession, WRITER_WAIT_PERIOD ) ) {
+ NlPrint((NL_CRITICAL, "NlReplicateDeltas: Can't become writer of client session.\n" ));
+ Status = STATUS_NO_LOGON_SERVERS;
+ goto Cleanup;
+ }
+
+ if ( NlGlobalClientSession->CsState != CS_AUTHENTICATED ) {
+ NlPrint((NL_CRITICAL, "NlReplicateDeltas: Client session dropped.\n" ));
+ Status = NlGlobalClientSession->CsConnectionStatus;
+ NlResetWriterClientSession( NlGlobalClientSession );
+ goto Cleanup;
+ }
+
+ NlBuildAuthenticator(
+ &NlGlobalClientSession->CsAuthenticationSeed,
+ &NlGlobalClientSession->CsSessionKey,
+ &OurAuthenticator );
+
+ LOCK_CHANGELOG();
+ LocalSerialNumber = NlGlobalChangeLogDesc.SerialNumber[DBInfo->DBIndex];
+ UNLOCK_CHANGELOG();
+
+ ExpectedSerialNumber.QuadPart = LocalSerialNumber.QuadPart + 1;
+ NEW_TO_OLD_LARGE_INTEGER( LocalSerialNumber, OldLocalSerialNumber );
+
+ DeltaStatus = NlStartApiClientSession( NlGlobalClientSession, FALSE );
+
+ if ( NT_SUCCESS(DeltaStatus) ) {
+ STARTSSIAPITIMER;
+
+ ApiStartTime = NlGlobalClientSession->CsApiTimer.StartTime;
+
+ DeltaStatus = I_NetDatabaseDeltas(
+ NlGlobalClientSession->CsUncServerName,
+ NlGlobalUnicodeComputerName,
+ &OurAuthenticator,
+ &ReturnAuthenticator,
+ DBInfo->DBIndex,
+ (PNLPR_MODIFIED_COUNT)&OldLocalSerialNumber,
+ &DeltaArray,
+ PreferredMaximum );
+
+ if ( NlGlobalGovernorParameter != 100 ) {
+ (VOID) NtQuerySystemTime( &ApiFinishTime );
+ }
+ STOPSSIAPITIMER;
+ }
+
+ (VOID)NlFinishApiClientSession( NlGlobalClientSession, TRUE );
+
+ OLD_TO_NEW_LARGE_INTEGER( OldLocalSerialNumber, LocalSerialNumber );
+
+ NlPrint((NL_REPL_TIME, "I_NetDatabaseDeltas Time:\n"));
+ PRINTSSIAPITIMER;
+
+
+ //
+ // On an access denied error, force an authentication.
+ //
+ // Returned authenticator may be invalid.
+ //
+ // Notice that all communications errors take this path rather
+ // than the path below which forces a full sync.
+ //
+
+ if ( (DeltaStatus == STATUS_ACCESS_DENIED) ||
+ ( !NlUpdateSeed(
+ &NlGlobalClientSession->CsAuthenticationSeed,
+ &ReturnAuthenticator.Credential,
+ &NlGlobalClientSession->CsSessionKey) ) ) {
+
+
+ if ( NT_SUCCESS(DeltaStatus) ) {
+ Status = STATUS_ACCESS_DENIED;
+ } else {
+ Status = DeltaStatus;
+ }
+
+ NlPrint((NL_CRITICAL, "NlReplicateDeltas: authentication failed.\n" ));
+ NlSetStatusClientSession( NlGlobalClientSession, Status );
+ NlResetWriterClientSession( NlGlobalClientSession );
+
+ //
+ // Perhaps the netlogon service on the PDC has just restarted.
+ // Try just once to set up a session to the server again.
+ //
+ if ( FirstTry && DeltaStatus == STATUS_ACCESS_DENIED ) {
+ FirstTry = FALSE;
+ goto FirstTryFailed;
+ }
+ goto Cleanup;
+ }
+
+ //
+ // Copy session key to decrypt sensitive information.
+ //
+
+ SessionInfo.SessionKey = NlGlobalClientSession->CsSessionKey;
+ SessionInfo.NegotiatedFlags = NlGlobalClientSession->CsNegotiatedFlags;
+ NlResetWriterClientSession( NlGlobalClientSession );
+
+
+ //
+ // Finally, error out
+ //
+
+ if ( !NT_SUCCESS( DeltaStatus ) ) {
+
+ NlPrint((NL_CRITICAL,
+ "NlReplicateDeltas: "
+ "I_NetDatabaseDeltas returning: Status (%lx)\n",
+ DeltaStatus ));
+
+ //
+ // since we can't handle any other error, call full sync.
+ //
+
+ ForceFullSync = TRUE;
+ Status = DeltaStatus;
+ goto Cleanup;
+ }
+
+ if ( DeltaArray->CountReturned == 0 ) {
+ Status = STATUS_SUCCESS;
+ goto Cleanup;
+ }
+
+ //
+ // Unpack the buffer and apply changes to appropriate database
+ //
+
+ for ( DeltaIndex=0;
+ DeltaIndex<DeltaArray->CountReturned;
+ DeltaIndex++ ) {
+
+ if ( NlGlobalReplicatorTerminate ) {
+ NlPrint((NL_SYNC, "NlReplicateDeltas: Asked to terminate\n" ));
+ Status = STATUS_THREAD_IS_TERMINATING;
+ goto Cleanup;
+ }
+
+ Status = NlUnpackSam(
+ &(DeltaArray->Deltas)[DeltaIndex] ,
+ DBInfo,
+ &ConflictingRid,
+ &SessionInfo );
+
+ if ( ! NT_SUCCESS( Status ) ) {
+ BOOLEAN ResourceError;
+
+ Status = NlRecoverConflictingAccount(
+ &(DeltaArray->Deltas)[DeltaIndex],
+ DBInfo,
+ ConflictingRid,
+ &SessionInfo,
+ Status,
+ FALSE,
+ &ResourceError );
+
+ if ( !NT_SUCCESS( Status ) ) {
+
+ //
+ // If we failed for some temporary reason,
+ // stop the full sync now to let the system cure itself.
+ //
+
+ if ( ResourceError ) {
+ goto Cleanup;
+ }
+
+ //
+ // If the PDC supports redo,
+ // Write this delta to the redo log and otherwise ignore
+ // the failure.
+
+ if ( SessionInfo.NegotiatedFlags & NETLOGON_SUPPORTS_REDO ){
+ Status = NlWriteDeltaToChangeLog(
+ &NlGlobalRedoLogDesc,
+ &(DeltaArray->Deltas)[DeltaIndex],
+ DBInfo->DBIndex,
+ NULL );
+
+ //
+ // If we can't write to the redo log,
+ // remember to get this delta again later.
+ //
+
+ if ( !NT_SUCCESS( Status )) {
+ goto Cleanup;
+ }
+
+ //
+ // If the PDC doesn't support redo,
+ // recover by doing a full sync.
+ //
+
+ } else {
+
+ NlPrint((NL_CRITICAL,
+ "NlReplicateDeltas: " FORMAT_LPWSTR
+ ": Force full sync since PDC returned an error we didn't recognize\n",
+ DBInfo->DBName,
+ Status ));
+ ForceFullSync = TRUE;
+ goto Cleanup;
+ }
+
+ }
+ }
+
+ //
+ // Write the delta to the changelog.
+ //
+
+ if ( SessionInfo.NegotiatedFlags & NETLOGON_SUPPORTS_BDC_CHANGELOG){
+ Status = NlWriteDeltaToChangeLog(
+ &NlGlobalChangeLogDesc,
+ &(DeltaArray->Deltas)[DeltaIndex],
+ DBInfo->DBIndex,
+ &ExpectedSerialNumber );
+
+ //
+ // Most failures can be ignored.
+ //
+ // However, if the PDC is behind this BDC and we couldn't back out our changes,
+ // we've done the best we could.
+ //
+
+ if ( Status == STATUS_SYNCHRONIZATION_REQUIRED ) {
+ NlPrint((NL_CRITICAL,
+ "NlReplicateDeltas: " FORMAT_LPWSTR
+ ": PDC is behind this BDC and our changelog doesn't have the changes in between.\n",
+ DBInfo->DBName,
+ Status ));
+ ForceFullSync = TRUE;
+ goto Cleanup;
+ }
+
+ }
+
+ }
+
+
+ DeltasApplied += DeltaArray->CountReturned;
+ MIDL_user_free( DeltaArray );
+ DeltaArray = NULL;
+
+ //
+ // Set the domain creation time and modified count to their values
+ // on the PDC.
+ //
+
+ LOCK_CHANGELOG();
+ NlGlobalChangeLogDesc.SerialNumber[DBInfo->DBIndex] = LocalSerialNumber;
+ if ( SessionInfo.NegotiatedFlags & NETLOGON_SUPPORTS_BDC_CHANGELOG) {
+ (VOID) NlFlushChangeLog( &NlGlobalChangeLogDesc );
+ }
+ UNLOCK_CHANGELOG();
+
+ NlPrint((NL_SYNC,
+ "NlReplicateDeltas: Setting " FORMAT_LPWSTR " serial number to %lx %lx\n",
+ DBInfo->DBName,
+ LocalSerialNumber.HighPart,
+ LocalSerialNumber.LowPart ));
+
+ if( DBInfo->DBIndex == LSA_DB ) {
+
+ Status = LsaISetSerialNumberPolicy(
+ DBInfo->DBHandle,
+ &LocalSerialNumber,
+ &DBInfo->CreationTime,
+ (BOOLEAN) FALSE );
+
+ } else {
+
+ Status = SamISetSerialNumberDomain(
+ DBInfo->DBHandle,
+ &LocalSerialNumber,
+ &DBInfo->CreationTime,
+ (BOOLEAN) FALSE );
+
+ }
+
+ if (!NT_SUCCESS(Status)) {
+
+ NlPrint((NL_CRITICAL,
+ "NlReplicateDeltas: "
+ "Unable to set serial number: Status (%lx)\n",
+ Status ));
+
+ goto Cleanup;
+ }
+
+ //
+ // Sanity check that the PDC returned good serial numbers.
+ //
+
+ if ( (SessionInfo.NegotiatedFlags & NETLOGON_SUPPORTS_BDC_CHANGELOG) &&
+ ExpectedSerialNumber.QuadPart - 1 != LocalSerialNumber.QuadPart ) {
+
+ ExpectedSerialNumber.QuadPart -= 1;
+ NlPrint((NL_CRITICAL,
+ "NlReplicateDeltas: " FORMAT_LPWSTR " PDC serial number info mismatch: PDC says %lx %lx We computed %lx %lx\n",
+ DBInfo->DBName,
+ LocalSerialNumber.HighPart,
+ LocalSerialNumber.LowPart,
+ ExpectedSerialNumber.HighPart,
+ ExpectedSerialNumber.LowPart ));
+
+ //
+ // Above we updated NlGlobalChangeLogDesc.SerialNumber to match LocalSerialNumber.
+ // Therefore, we need to ensure the actual change log entries match that.
+ //
+ // (This will only be caused by a logic error in the way serial numbers are
+ // computed.)
+ //
+
+ LOCK_CHANGELOG();
+ (VOID) NlFixChangeLog( &NlGlobalChangeLogDesc,
+ DBInfo->DBIndex,
+ LocalSerialNumber,
+ FALSE );
+ UNLOCK_CHANGELOG();
+ }
+
+ //
+ // If the PDC has given us all of the deltas it has,
+ // we're all done.
+ //
+
+ if ( DeltaStatus == STATUS_SUCCESS ) {
+ Status = STATUS_SUCCESS;
+ break;
+ }
+
+ //
+ // Compute the amount of time we need to wait before calling the PDC
+ // again.
+ //
+
+ SyncSleepTime = NlComputeSyncSleepTime( &ApiStartTime,
+ &ApiFinishTime );
+
+ }
+
+ //
+ // Mark that we've potentially replicated from a different PDC.
+ //
+ (VOID) NlResetFirstTimeFullSync( DBInfo->DBIndex );
+
+ Status = STATUS_SUCCESS;
+
+Cleanup:
+
+ //
+ // write event log
+ //
+
+ MsgStrings[0] = DBInfo->DBName;
+ MsgStrings[1] = NlGlobalUncPrimaryName;
+
+ if ( !NT_SUCCESS( Status ) ) {
+
+ if ( !NlGlobalReplicatorTerminate ) {
+
+ MsgStrings[2] = (LPWSTR) Status;
+
+ NlpWriteEventlog(
+ NELOG_NetlogonPartialSyncFailed,
+ EVENTLOG_WARNING_TYPE,
+ (LPBYTE)&Status,
+ sizeof(Status),
+ MsgStrings,
+ 3 | LAST_MESSAGE_IS_NTSTATUS );
+ }
+
+ } else {
+
+ if ( DeltasApplied != 0 ) {
+ WCHAR CountBuffer[20]; // random size
+
+ ultow( DeltasApplied, CountBuffer, 10);
+ MsgStrings[2] = CountBuffer;
+
+ NlpWriteEventlog(
+ NELOG_NetlogonPartialSyncSuccess,
+ EVENTLOG_INFORMATION_TYPE,
+ NULL,
+ 0,
+ MsgStrings,
+ 3 );
+ }
+
+ }
+
+ //
+ // Clean up any resources we're using.
+ //
+
+ if ( DeltaArray != NULL ) {
+ MIDL_user_free( DeltaArray );
+ }
+
+ if ( !NT_SUCCESS( Status ) ) {
+ if ( ForceFullSync ) {
+ Status = STATUS_SYNCHRONIZATION_REQUIRED;
+ }
+ NlPrint((NL_CRITICAL,
+ "NlReplicateDeltas: returning unsuccessful: Status (%lx)\n",
+ Status ));
+ }
+
+ return Status;
+}
+
+
+NTSTATUS
+NlProcessRedoLog(
+ IN PDB_INFO DBInfo
+ )
+/*++
+
+Routine Description:
+
+ Process the redo log on this BDC for the particular database.
+
+Arguments:
+
+ ChangeLogDesc -- Description of the Changelog buffer being used
+
+Return Value:
+
+ STATUS_SUCCESS - The Service completed successfully.
+
+
+--*/
+{
+ NTSTATUS Status;
+ NTSTATUS CumulativeStatus = STATUS_SUCCESS;
+ NETLOGON_AUTHENTICATOR OurAuthenticator;
+ NETLOGON_AUTHENTICATOR ReturnAuthenticator;
+
+ NTSTATUS SyncStatus;
+ PNETLOGON_DELTA_ENUM_ARRAY DeltaArray = NULL;
+ DWORD DeltasApplied;
+ DWORD DeltaIndex;
+ LARGE_INTEGER RunningSerialNumber;
+
+ LARGE_INTEGER ApiStartTime;
+ LARGE_INTEGER ApiFinishTime;
+ DWORD SyncSleepTime;
+
+ SESSION_INFO SessionInfo;
+
+ ULONG ConflictingRid;
+
+ LPWSTR MsgStrings[3];
+ BOOLEAN FirstTry = TRUE;
+ PCHANGELOG_ENTRY ChangeLogEntry = NULL;
+ DWORD ChangeLogEntrySize;
+
+
+ //
+ // Just return if the redo log is empty
+ //
+
+ LOCK_CHANGELOG();
+ if ( !NlGlobalRedoLogDesc.RedoLog ||
+ NlGlobalRedoLogDesc.EntryCount[DBInfo->DBIndex] == 0 ) {
+ UNLOCK_CHANGELOG();
+ return STATUS_SUCCESS;
+ }
+ UNLOCK_CHANGELOG();
+ NlPrint((NL_SYNC, "NlProcessRedoLog: " FORMAT_LPWSTR ": Entered\n", DBInfo->DBName ));
+
+
+ //
+ // If we're not currently authenticated with the PDC,
+ // do so now.
+ //
+
+FirstTryFailed:
+ if ( !NlTimeoutSetWriterClientSession( NlGlobalClientSession, WRITER_WAIT_PERIOD ) ) {
+ NlPrint((NL_CRITICAL, "NlProcessRedoLog: Can't become writer of client session.\n" ));
+ Status = STATUS_NO_LOGON_SERVERS;
+ goto Cleanup;
+ }
+
+ if ( NlGlobalClientSession->CsState != CS_AUTHENTICATED ) {
+
+ Status = NlSessionSetup( NlGlobalClientSession );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ NlResetWriterClientSession( NlGlobalClientSession );
+ goto Cleanup;
+ }
+ }
+ NlResetWriterClientSession( NlGlobalClientSession );
+
+
+ //
+ // Loop getting changes from the PDC
+ //
+
+ RunningSerialNumber.QuadPart = 0;
+ SyncSleepTime = 0;
+ DeltasApplied = 0;
+
+ for (;;) {
+
+ DEFSSIAPITIMER;
+
+ INITSSIAPITIMER;
+
+
+ //
+ // Get the next entry from the redo log.
+ //
+
+ ChangeLogEntry = NlGetNextChangeLogEntry(
+ &NlGlobalRedoLogDesc,
+ RunningSerialNumber,
+ DBInfo->DBIndex,
+ &ChangeLogEntrySize );
+
+ if ( ChangeLogEntry == NULL ) {
+ break;
+ }
+
+ RunningSerialNumber = ChangeLogEntry->SerialNumber;
+
+
+ //
+ // Wait a while so we don't overburden the secure channel.
+ //
+
+ if ( SyncSleepTime != 0 ) {
+ NlPrint(( NL_SYNC,
+ "NlProcessRedoLog: sleeping %ld for the governor.\n",
+ SyncSleepTime ));
+ (VOID) WaitForSingleObject( NlGlobalReplicatorTerminateEvent, SyncSleepTime );
+ }
+
+ //
+ // If this thread has been asked to leave, do so.
+ //
+
+ if ( NlGlobalReplicatorTerminate ) {
+ NlPrint((NL_SYNC, "NlProcessRedoLog: Asked to terminate\n" ));
+ Status = STATUS_THREAD_IS_TERMINATING;
+ goto Cleanup;
+ }
+
+ //
+ // If this redo log entry is bogus,
+ // don't confuse the PDC into asking us to full sync.
+ //
+ // This list of DeltaType's should be the list of deltas not handled
+ // by NlPackSingleDelta.
+ //
+
+ if ( ChangeLogEntry->DeltaType == DummyChangeLogEntry ||
+ ChangeLogEntry->DeltaType == SerialNumberSkip ) {
+
+ Status = STATUS_SUCCESS;
+
+ //
+ // Get the appropriate changes from the PDC.
+ //
+
+ } else {
+
+
+ //
+ // Build the Authenticator for this request to the PDC.
+ //
+
+ if ( !NlTimeoutSetWriterClientSession( NlGlobalClientSession, WRITER_WAIT_PERIOD ) ) {
+ NlPrint((NL_CRITICAL, "NlProcessRedoLog: Can't become writer of client session.\n" ));
+ Status = STATUS_NO_LOGON_SERVERS;
+ goto Cleanup;
+ }
+
+ if ( NlGlobalClientSession->CsState != CS_AUTHENTICATED ) {
+ NlPrint((NL_CRITICAL, "NlProcessRedoLog: Client session dropped.\n" ));
+ Status = NlGlobalClientSession->CsConnectionStatus;
+ NlResetWriterClientSession( NlGlobalClientSession );
+ goto Cleanup;
+ }
+
+ NlBuildAuthenticator(
+ &NlGlobalClientSession->CsAuthenticationSeed,
+ &NlGlobalClientSession->CsSessionKey,
+ &OurAuthenticator);
+
+
+ //
+ // copy session key to decrypt sensitive information.
+ //
+
+ SessionInfo.SessionKey = NlGlobalClientSession->CsSessionKey;
+ SessionInfo.NegotiatedFlags = NlGlobalClientSession->CsNegotiatedFlags;
+
+
+ //
+ // If the PDC doesn't support redo,
+ // force a full sync on this database and clear the redo log.
+ //
+
+ if ( (SessionInfo.NegotiatedFlags & NETLOGON_SUPPORTS_REDO) == 0 ) {
+
+ NlResetWriterClientSession( NlGlobalClientSession );
+
+ //
+ // Force a full sync on this database. That's what we would have
+ // done when we initially added this redo entry.
+ //
+
+ NlPrint(( NL_SYNC,
+ FORMAT_LPWSTR ": Force FULL SYNC because we have a redo log and PDC doesn't support redo.\n",
+ NlGlobalDBInfoArray[DBInfo->DBIndex].DBName ));
+
+ (VOID) NlForceStartupSync( &NlGlobalDBInfoArray[DBInfo->DBIndex] );
+
+ Status = STATUS_SYNCHRONIZATION_REQUIRED;
+ goto Cleanup;
+ }
+
+
+ //
+ // Get the data from the PDC.
+ //
+
+ SyncStatus = NlStartApiClientSession( NlGlobalClientSession, FALSE );
+
+ if (NT_SUCCESS(SyncStatus)) {
+ STARTSSIAPITIMER;
+
+ ApiStartTime = NlGlobalClientSession->CsApiTimer.StartTime;
+
+ SyncStatus = I_NetDatabaseRedo(
+ NlGlobalClientSession->CsUncServerName,
+ NlGlobalUnicodeComputerName,
+ &OurAuthenticator,
+ &ReturnAuthenticator,
+ (LPBYTE) ChangeLogEntry,
+ ChangeLogEntrySize,
+ &DeltaArray );
+
+ if ( NlGlobalGovernorParameter != 100 ) {
+ (VOID) NtQuerySystemTime( &ApiFinishTime );
+ }
+ STOPSSIAPITIMER;
+ }
+ (VOID)NlFinishApiClientSession( NlGlobalClientSession, TRUE );
+
+ NlPrint((NL_REPL_TIME,"I_NetDatabaseRedo Time:\n"));
+ PRINTSSIAPITIMER;
+
+ //
+ // On an access denied error, force an authentication.
+ //
+ // Returned authenticator may be invalid.
+ //
+
+ if ( (SyncStatus == STATUS_ACCESS_DENIED) ||
+ ( !NlUpdateSeed(
+ &NlGlobalClientSession->CsAuthenticationSeed,
+ &ReturnAuthenticator.Credential,
+ &NlGlobalClientSession->CsSessionKey) ) ) {
+
+ if ( NT_SUCCESS(SyncStatus) ) {
+ Status = STATUS_ACCESS_DENIED;
+ } else {
+ Status = SyncStatus;
+ }
+
+ NlPrint((NL_CRITICAL, "NlProcessRedoLog: authentication failed: %lx\n", Status ));
+
+ NlSetStatusClientSession( NlGlobalClientSession, Status );
+
+ NlResetWriterClientSession( NlGlobalClientSession );
+
+ //
+ // Perhaps the netlogon service on the PDC has just restarted.
+ // Try just once to set up a session to the server again.
+ //
+ if ( FirstTry && SyncStatus == STATUS_ACCESS_DENIED ) {
+ FirstTry = FALSE;
+ goto FirstTryFailed;
+ }
+
+ goto Cleanup;
+ }
+
+ FirstTry = FALSE;
+
+ NlResetWriterClientSession( NlGlobalClientSession );
+
+
+ //
+ // Finally, error out
+ //
+
+ if ( !NT_SUCCESS( SyncStatus ) ) {
+ NlPrint((NL_CRITICAL,
+ "NlProcessRedoLog: "
+ "I_NetDatabaseRedo returning: Status (%lx)\n",
+ SyncStatus ));
+
+ Status = SyncStatus;
+ goto Cleanup;
+ }
+
+ //
+ // Unpack the buffer and apply changes to appropriate database
+ //
+
+ for ( DeltaIndex=0;
+ DeltaIndex<DeltaArray->CountReturned;
+ DeltaIndex++ ) {
+
+ if ( NlGlobalReplicatorTerminate ) {
+ NlPrint((NL_SYNC, "NlProcessRedoLog: Asked to terminate\n" ));
+ Status = STATUS_THREAD_IS_TERMINATING;
+ goto Cleanup;
+ }
+
+ Status = NlUnpackSam(
+ &(DeltaArray->Deltas)[DeltaIndex] ,
+ DBInfo,
+ &ConflictingRid,
+ &SessionInfo );
+
+ if ( ! NT_SUCCESS( Status ) ) {
+ BOOLEAN ResourceError;
+
+ Status = NlRecoverConflictingAccount(
+ &(DeltaArray->Deltas)[DeltaIndex],
+ DBInfo,
+ ConflictingRid,
+ &SessionInfo,
+ Status,
+ FALSE,
+ &ResourceError );
+
+ if ( !NT_SUCCESS( Status ) ) {
+
+ //
+ // If we failed for some temporary reason,
+ // stop the full sync now to let the system cure itself.
+ //
+
+ if ( ResourceError ) {
+ goto Cleanup;
+ }
+
+ //
+ // If this is an unexpected failure,
+ // continue processing deltas.
+ //
+ // It is better to continue copying the database as
+ // much as possible than to quit now. The theory is that
+ // we've stumbled upon some circumstance we haven't
+ // anticipated. We'll put this BDC in the best shape
+ // we possibly can.
+ //
+ // Remember this status code until the end.
+ //
+
+ if ( NT_SUCCESS(CumulativeStatus) ) {
+ CumulativeStatus = Status;
+ }
+
+ continue;
+
+ }
+ }
+
+ DeltasApplied ++;
+
+ }
+
+ MIDL_user_free( DeltaArray );
+ DeltaArray = NULL;
+ }
+
+ //
+ // If the operation succeeded,
+ // delete this entry from the redo log.
+ //
+ if ( Status == STATUS_SUCCESS ) {
+
+ NlDeleteChangeLogEntry(
+ &NlGlobalRedoLogDesc,
+ ChangeLogEntry->DBIndex,
+ ChangeLogEntry->SerialNumber );
+ }
+
+ NetpMemoryFree( ChangeLogEntry );
+ ChangeLogEntry = NULL;
+
+
+ //
+ // Compute the amount of time we need to wait before calling the PDC
+ // again.
+ //
+
+ SyncSleepTime = NlComputeSyncSleepTime( &ApiStartTime,
+ &ApiFinishTime );
+
+ }
+
+
+ //
+ // We've finished the redo sync.
+ //
+
+ Status = CumulativeStatus;
+
+Cleanup:
+
+ //
+ // write event log
+ //
+
+ MsgStrings[0] = DBInfo->DBName;
+ MsgStrings[1] = NlGlobalUncPrimaryName;
+
+ if ( !NT_SUCCESS( Status ) ) {
+
+ if ( !NlGlobalReplicatorTerminate ) {
+
+ MsgStrings[2] = (LPWSTR) Status;
+
+ NlpWriteEventlog(
+ NELOG_NetlogonPartialSyncFailed,
+ EVENTLOG_WARNING_TYPE,
+ (LPBYTE)&Status,
+ sizeof(Status),
+ MsgStrings,
+ 3 | LAST_MESSAGE_IS_NTSTATUS );
+ }
+
+ } else {
+
+ if ( DeltasApplied != 0 ) {
+ WCHAR CountBuffer[20]; // random size
+
+ ultow( DeltasApplied, CountBuffer, 10);
+ MsgStrings[2] = CountBuffer;
+
+ NlpWriteEventlog(
+ NELOG_NetlogonPartialSyncSuccess,
+ EVENTLOG_INFORMATION_TYPE,
+ NULL,
+ 0,
+ MsgStrings,
+ 3 );
+ }
+
+ }
+
+ //
+ // Free locally used resources.
+ //
+
+
+ if( ChangeLogEntry != NULL) {
+ NetpMemoryFree( ChangeLogEntry );
+ }
+
+ if ( DeltaArray != NULL ) {
+ MIDL_user_free( DeltaArray );
+ }
+
+ if ( !NT_SUCCESS( Status ) ) {
+
+ //
+ // If we're going to do a full sync, clear the redo log.
+ //
+ // It no longer has value and if we don't clear it now we
+ // may end up forcing another full sync the next time we
+ // try to process the redo log.
+ //
+
+ if ( Status == STATUS_SYNCHRONIZATION_REQUIRED ) {
+
+ NlPrint((NL_CRITICAL,
+ "NlProcessRedoLog: Clearing redo log because full sync is needed.\n" ));
+
+ LOCK_CHANGELOG();
+ RunningSerialNumber.QuadPart = 0;
+ (VOID) NlFixChangeLog( &NlGlobalRedoLogDesc,
+ DBInfo->DBIndex,
+ RunningSerialNumber,
+ FALSE );
+ UNLOCK_CHANGELOG();
+ }
+
+ NlPrint((NL_CRITICAL,
+ "NlProcessRedoLog: returning unsuccessful: Status (%lx)\n",
+ Status ));
+ } else {
+ NlPrint((NL_SYNC, "NlProcessRedoLog: " FORMAT_LPWSTR ": Successful return\n", DBInfo->DBName ));
+ }
+
+ return Status;
+}
+
+
+DWORD
+NlReplicator(
+ IN LPVOID ReplParam
+ )
+/*++
+
+Routine Description:
+
+ This procedure is the main procedure for the replicator thread.
+ This thread is created to contact the PDC and update the local SAM
+ database to match the copy on the PDC.
+
+ Only one copy of this thread will be running at any time.
+
+Arguments:
+
+ ReplParam - Parameters governing the behavior of the replicator thread.
+
+Return Value:
+
+ Exit Status of the thread.
+
+--*/
+{
+ NTSTATUS Status;
+ DWORD i;
+ PDB_INFO DBInfo;
+ BOOLEAN AdminAlert = FALSE;
+ BOOLEAN SyncFailed;
+
+
+
+ //
+ // Sleep a little before contacting the PDC. This sleep prevents all
+ // the BDC and member servers from contacting the PDC at once.
+ //
+
+ NlPrint((NL_SYNC,
+ "NlReplicator: Thread starting Sleep: %ld\n",
+ ((PREPL_PARAM)ReplParam)->RandomSleep ));
+
+ (VOID) WaitForSingleObject( NlGlobalReplicatorTerminateEvent,
+ ((PREPL_PARAM)ReplParam)->RandomSleep );
+
+ //
+ // Mark each database that no sync has yet been done.
+ //
+
+ EnterCriticalSection( &NlGlobalDbInfoCritSect );
+ for( i = 0; i < NUM_DBS; i++ ) {
+ NlGlobalDBInfoArray[i].SyncDone = FALSE;
+ }
+ LeaveCriticalSection( &NlGlobalDbInfoCritSect );
+
+
+ //
+ // Loop until we've successfully finished replication.
+ //
+ // The PDC doesn't send periodic pulses to every BDC anymore.
+ // Therefore, the BDC is responsible for ensure it finishes getting
+ // any database changes it knows about.
+ //
+ for (;;) {
+
+ //
+ // If this thread has been asked to leave, do so.
+ //
+
+ if ( NlGlobalReplicatorTerminate ) {
+ NlPrint((NL_SYNC, "NlReplicator: Asked to terminate\n" ));
+ NlGlobalReplicatorIsRunning = FALSE;
+ return (DWORD) STATUS_THREAD_IS_TERMINATING;
+ }
+
+ //
+ // Ensure we have a secure channel to the PDC.
+ // If we don't have a secure channel to the PDC,
+ // we'll exit the thread and wait until the PDC starts before
+ // continuing.
+ //
+
+ if ( !NlTimeoutSetWriterClientSession( NlGlobalClientSession, WRITER_WAIT_PERIOD ) ) {
+ NlPrint((NL_CRITICAL, "NlReplicator: Can't become writer of client session.\n" ));
+ NlGlobalReplicatorIsRunning = FALSE;
+ return (DWORD) STATUS_THREAD_IS_TERMINATING;
+ }
+
+ if ( NlGlobalClientSession->CsState != CS_AUTHENTICATED ) {
+ Status = NlSessionSetup( NlGlobalClientSession );
+
+ if ( !NT_SUCCESS(Status)) {
+ NlResetWriterClientSession( NlGlobalClientSession );
+ NlPrint((NL_SYNC,
+ "NlReplicator: Replicator thread exitting since PDC is down.\n" ));
+ NlGlobalReplicatorIsRunning = FALSE;
+ return (DWORD) STATUS_THREAD_IS_TERMINATING;
+ }
+
+ }
+
+ NlResetWriterClientSession( NlGlobalClientSession );
+
+
+ //
+ // we need to update all databases one after another.
+ //
+
+ SyncFailed = FALSE;
+ for( i = 0; i < NUM_DBS; i++ ) {
+ BOOLEAN FullSyncRequired;
+ BOOLEAN PartialSyncRequired;
+
+
+ //
+ // If this particular database doesn't need to be updated,
+ // skip it.
+ //
+
+ DBInfo = &NlGlobalDBInfoArray[i];
+
+ LOCK_CHANGELOG();
+ EnterCriticalSection( &NlGlobalDbInfoCritSect );
+ if ( !DBInfo->UpdateRqd && NlGlobalRedoLogDesc.EntryCount[i] == 0 ) {
+ LeaveCriticalSection( &NlGlobalDbInfoCritSect );
+ UNLOCK_CHANGELOG();
+ continue;
+ }
+
+ FullSyncRequired = DBInfo->FullSyncRequired;
+ PartialSyncRequired = DBInfo->UpdateRqd;
+
+ DBInfo->UpdateRqd = FALSE;
+ DBInfo->FullSyncRequired = FALSE;
+
+ LeaveCriticalSection( &NlGlobalDbInfoCritSect );
+ UNLOCK_CHANGELOG();
+
+
+ //
+ // If we've switched PDCs and the current PDC is running NT1.0,
+ // force a full sync.
+ //
+ // We wait until now to make this check to ensure that we've set up a
+ // secure channel with the PDC. This prevents a rouge PDC from forcing
+ // us to full sync just by sending us a mailslot message.
+ //
+ // Check the 'SyncDone' flag to ensure we only force this full sync
+ // once. Otherwise, we'll force a full sync here multiple time.
+ // The first time will be legit. The remaining times will be
+ // because a partial sync is needed.
+ //
+
+ if (NlNameCompare( DBInfo->PrimaryName,
+ NlGlobalUnicodePrimaryName,
+ NAMETYPE_COMPUTER) != 0 &&
+ !DBInfo->SyncDone ) {
+
+ //
+ // If this is an NT 1.0 PDC,
+ // Mark this database that it needs a full sync.
+ //
+
+ if ( NlTimeoutSetWriterClientSession( NlGlobalClientSession, WRITER_WAIT_PERIOD ) ) {
+
+ if ( NlGlobalClientSession->CsState == CS_AUTHENTICATED &&
+ (NlGlobalClientSession->CsNegotiatedFlags &
+ NETLOGON_SUPPORTS_PROMOTION_COUNT) == 0 ){
+
+ FullSyncRequired = TRUE;
+
+ NlPrint((NL_CRITICAL,
+ "NlReplicator: " FORMAT_LPWSTR
+ ": Force FULL SYNC because new PDC is running NT 1.0.\n",
+ DBInfo->DBName ));
+ }
+
+ NlResetWriterClientSession( NlGlobalClientSession );
+ }
+ }
+
+ //
+ // If our caller says we're out of sync with the primary,
+ // do a full sync.
+ //
+ // If we've just finished doing a successfull full sync,
+ // then ignore whoever told us to do another one.
+ //
+
+ if ( FullSyncRequired && !DBInfo->SyncDone ) {
+
+ if( !AdminAlert ) {
+
+ LPWSTR AlertStrings[2];
+
+ //
+ // raise admin alert to inform a fullsync has been called by this
+ // server.
+ //
+
+ AlertStrings[0] = NlGlobalUnicodeComputerName;
+ AlertStrings[1] = NULL;
+
+ RaiseAlert( ALERT_NetlogonFullSync,
+ AlertStrings );
+
+ AdminAlert = TRUE;
+ }
+
+
+ Status = NlSynchronize( DBInfo );
+
+ //
+ // If we're not out of sync with the primary,
+ // just get the deltas from the caller.
+ //
+
+ } else if ( PartialSyncRequired ) {
+ Status = NlReplicateDeltas( DBInfo );
+
+ //
+ // Otherwise, just process the redo log
+ //
+
+ } else {
+
+ Status = NlProcessRedoLog( DBInfo );
+ }
+
+
+
+ //
+ // If the PDC thinks a full Sync is required,
+ // do a full sync now.
+ //
+
+ if (Status == STATUS_SYNCHRONIZATION_REQUIRED) {
+ NlPrint((NL_CRITICAL,
+ "NlReplicator: PDC says " FORMAT_LPWSTR " needs full sync.\n",
+ DBInfo->DBName ));
+
+ if( !AdminAlert ) {
+
+ LPWSTR AlertStrings[2];
+
+ //
+ // raise admin alter to inform a fullsync has been called by this
+ // server.
+ //
+
+ AlertStrings[0] = NlGlobalUnicodeComputerName;
+ AlertStrings[1] = NULL;
+
+ RaiseAlert( ALERT_NetlogonFullSync,
+ AlertStrings );
+
+ AdminAlert = TRUE;
+ }
+
+ FullSyncRequired = TRUE;
+ Status = NlSynchronize( DBInfo );
+ }
+
+ //
+ // If not successful, indicate we need to try again.
+ //
+
+ if ( !NT_SUCCESS( Status ) ) {
+ SyncFailed = TRUE;
+ EnterCriticalSection( &NlGlobalDbInfoCritSect );
+ DBInfo->UpdateRqd = TRUE;
+ DBInfo->FullSyncRequired = DBInfo->FullSyncRequired ||
+ FullSyncRequired;
+ LeaveCriticalSection( &NlGlobalDbInfoCritSect );
+
+ //
+ // If we've successfully done a full sync,
+ // ignore other requests to do so.
+ //
+
+ } else if (FullSyncRequired ) {
+ EnterCriticalSection( &NlGlobalDbInfoCritSect );
+ DBInfo->SyncDone = TRUE;
+ LeaveCriticalSection( &NlGlobalDbInfoCritSect );
+ }
+
+ }
+
+ //
+ // If we completed all databases,
+ // we're done.
+ //
+ // We have to re-check all the databases since someone may have requested
+ // a sync while we were in the loop above.
+ //
+
+ LOCK_CHANGELOG();
+ EnterCriticalSection( &NlGlobalDbInfoCritSect );
+ for( i = 0; i < NUM_DBS; i++ ) {
+ if ( NlGlobalDBInfoArray[i].UpdateRqd ||
+ NlGlobalRedoLogDesc.EntryCount[i] != 0 ) {
+ break;
+ }
+ }
+ LeaveCriticalSection( &NlGlobalDbInfoCritSect );
+ UNLOCK_CHANGELOG();
+
+ if ( i == NUM_DBS ) {
+ // Don't lock the ReplicatorCritSect within the replicator thread
+ NlGlobalReplicatorIsRunning = FALSE;
+ break;
+ }
+
+
+ //
+ // If the sync failed,
+ // wait a while before bothering the PDC again.
+ //
+
+ if ( SyncFailed ) {
+ ((PREPL_PARAM)ReplParam)->RandomSleep = NlGlobalPulseParameter * 1000;
+ NlPrint((NL_SYNC,
+ "NlReplicator: Sleeping: %ld\n",
+ ((PREPL_PARAM)ReplParam)->RandomSleep ));
+
+ (VOID) WaitForSingleObject( NlGlobalReplicatorTerminateEvent,
+ ((PREPL_PARAM)ReplParam)->RandomSleep );
+
+ }
+
+ }
+
+
+ //
+ // ASSERT( All databases are in sync )
+ //
+ //
+
+ //
+ //
+ // Continue netlogon if we have paused it for doing first
+ // time full sync.
+ //
+
+ NlGlobalFirstTimeFullSync = FALSE;
+
+
+ //
+ // We've done all we can. Exit the thread.
+ //
+
+ NlPrint((NL_SYNC,
+ "NlReplicator: Replicator thread exitting.\n" ));
+ return Status;
+}
+
+
+BOOL
+NlUpdateRequired (
+ IN PDB_CHANGE_INFO DBChangeInfo
+ )
+
+/*++
+
+Routine Description:
+
+ With the information arrived in the mailslot message, this routine
+ determines the Database require update. This routine also sets
+ internal appropriate fields in database structure (
+ NlGlobalDBInfoArray ) so that replication thread will sync the
+ database.
+
+Arguments:
+
+ DBChangeInfo: pointer to database change info structure.
+
+Return Value:
+
+ TRUE : if this database requires update.
+ FALSE : otherwise.
+
+--*/
+
+{
+ PDB_INFO DBInfo;
+ PSAMPR_DOMAIN_INFO_BUFFER DomainInfo = NULL;
+
+ LARGE_INTEGER LocalSerialNumber;
+ LARGE_INTEGER CreationTime;
+
+ if ( DBChangeInfo->DBIndex >= NUM_DBS ) {
+ return FALSE;
+ }
+
+ DBInfo = &NlGlobalDBInfoArray[DBChangeInfo->DBIndex];
+
+ //
+ // Pick up the current serial number of the database
+ //
+
+ LOCK_CHANGELOG();
+ LocalSerialNumber = NlGlobalChangeLogDesc.SerialNumber[DBChangeInfo->DBIndex];
+ CreationTime = DBInfo->CreationTime;
+ UNLOCK_CHANGELOG();
+
+
+ //
+ // We need a full sync if either:
+ // a) the local SAM database is marked as needing a sync.
+ // b) the domain creation times aren't the same.
+ //
+
+ if ( LocalSerialNumber.QuadPart == 0 ||
+ CreationTime.QuadPart == 0 ||
+ CreationTime.QuadPart != DBChangeInfo->NtDateAndTime.QuadPart ) {
+
+ //
+ // Tell the replicator thread that a full sync is needed.
+ //
+
+ EnterCriticalSection( &NlGlobalDbInfoCritSect );
+ DBInfo->UpdateRqd = TRUE;
+ DBInfo->FullSyncRequired = TRUE;
+ LeaveCriticalSection( &NlGlobalDbInfoCritSect );
+
+ NlPrint((NL_SYNC,
+ "NlUpdateRequired: " FORMAT_LPWSTR " requires full sync\n",
+ NlGlobalDBInfoArray[DBChangeInfo->DBIndex].DBName ));
+
+ NlPrint((NL_SYNC,
+ "\t PDC Serial Number %lx %lx .\n",
+ DBChangeInfo->LargeSerialNumber.HighPart,
+ DBChangeInfo->LargeSerialNumber.LowPart
+ ));
+
+ NlPrint((NL_SYNC,
+ "\t Local Serial Number %lx %lx .\n",
+ LocalSerialNumber.HighPart,
+ LocalSerialNumber.LowPart
+ ));
+
+ NlPrint((NL_SYNC,
+ "\t Local CreationTime %lx %lx .\n",
+ CreationTime.HighPart,
+ CreationTime.LowPart
+ ));
+
+ //
+ // If there are a few number of changes,
+ // just get those few changes.
+ //
+ // If the PDC wants us to call partial sync,
+ // oblige it.
+ //
+ // Do a partial sync even if this BDC is newer than the PDC. The PDC
+ // will give us a better indication of what to do when we call to get the deltas.
+ //
+
+ } else if ( DBChangeInfo->LargeSerialNumber.QuadPart != LocalSerialNumber.QuadPart ) {
+
+ //
+ // Tell the replicator this database needs a partial sync
+ //
+
+ EnterCriticalSection( &NlGlobalDbInfoCritSect );
+ DBInfo->UpdateRqd = TRUE;
+ LeaveCriticalSection( &NlGlobalDbInfoCritSect );
+
+ NlPrint((NL_SYNC,
+ "NlUpdateRequired: " FORMAT_LPWSTR " requires partial sync\n",
+ NlGlobalDBInfoArray[DBChangeInfo->DBIndex].DBName ));
+
+ NlPrint((NL_SYNC,
+ "\t PDC Serial Number %lx %lx .\n",
+ DBChangeInfo->LargeSerialNumber.HighPart,
+ DBChangeInfo->LargeSerialNumber.LowPart
+ ));
+
+ NlPrint((NL_SYNC,
+ "\t Local Serial Number %lx %lx .\n",
+ LocalSerialNumber.HighPart,
+ LocalSerialNumber.LowPart
+ ));
+
+ } else {
+
+ NlPrint((NL_SYNC,
+ "NlUpdateRequired: " FORMAT_LPWSTR " is in sync\n",
+ NlGlobalDBInfoArray[DBChangeInfo->DBIndex].DBName ));
+ }
+
+ return( DBInfo->UpdateRqd );
+}
+
+
+BOOL
+NlCheckUpdateNotices(
+ IN PNETLOGON_DB_CHANGE UasChange,
+ IN DWORD UasChangeSize
+ )
+/*++
+
+Routine Description:
+
+ Examine the update notice which came from Primary DC with
+ LOGON_UAS_CHANGE message. If there has been an update then get
+ those changes from primary so we stay in sync.
+
+ If replication is already in progress for whatever reason this
+ routine will return immediately causing cureent notice to be
+ ignored. That is OK since replication would ideally be governed by
+ the fact that there are some updates still out there and will run
+ till in sync.
+
+Arguments:
+
+ UasChange -- The UasChange message from the PDC.
+
+ UasChangeSize -- The size (in bytes) of the message.
+
+Return Value:
+
+ TRUE -- iff this message was valid and could be processed.
+
+--*/
+{
+ NTSTATUS Status;
+
+ //
+ // Unmarshalled information from the UasChange message.
+ //
+
+ PCHAR AnsiTemp;
+ LPWSTR AnncPrimary;
+ LPWSTR AnncDomain;
+ DWORD DBCount;
+ DB_CHANGE_INFO DBChangeInfo;
+ DWORD DomainSIDSize;
+
+
+ PCHAR Where;
+ PCHAR WhereDBChangeInfo;
+
+ DWORD StartReplicator = FALSE;
+ DWORD RandomSleep; // Number of millseconds to delay before working
+ DWORD i;
+
+
+ //
+ // Unmarshall the incoming message.
+ //
+
+ Where = UasChange->PrimaryDCName;
+ if ( !NetpLogonGetOemString( UasChange,
+ UasChangeSize,
+ &Where,
+ sizeof(UasChange->PrimaryDCName),
+ &AnsiTemp )) {
+ return FALSE;
+ }
+ if ( !NetpLogonGetOemString( UasChange,
+ UasChangeSize,
+ &Where,
+ sizeof(UasChange->DomainName),
+ &AnsiTemp )) {
+ return FALSE;
+ }
+
+ if ( !NetpLogonGetUnicodeString( UasChange,
+ UasChangeSize,
+ &Where,
+ sizeof(UasChange->UnicodePrimaryDCName),
+ &AnncPrimary )) {
+ return FALSE;
+ }
+ if ( !NetpLogonGetUnicodeString( UasChange,
+ UasChangeSize,
+ &Where,
+ sizeof(UasChange->UnicodeDomainName),
+ &AnncDomain )) {
+ return FALSE;
+ }
+
+ //
+ // Ensure message is for this domain.
+ //
+
+ if (NlNameCompare(AnncDomain,
+ NlGlobalUnicodeDomainName,
+ NAMETYPE_DOMAIN) != 0 ) {
+ return FALSE;
+ }
+
+ //
+ // Ignore our own broadcasts.
+ //
+
+ if (NlNameCompare(AnncPrimary,
+ NlGlobalUnicodeComputerName,
+ NAMETYPE_COMPUTER) == 0) {
+
+ NlAssert( NlGlobalRole == RolePrimary );
+ return FALSE;
+ }
+
+
+ //
+ // get DBCount from message
+ //
+
+ if ( !NetpLogonGetBytes( UasChange,
+ UasChangeSize,
+ &Where,
+ sizeof(UasChange->DBCount),
+ &DBCount )) {
+ return( FALSE );
+
+ }
+
+ WhereDBChangeInfo = Where;
+
+ //
+ // pass DB change info
+ //
+
+ for( i = 0; i < DBCount; i++ ) {
+
+ //
+ // Get DB_CHANGE_STRUCTURE
+ //
+
+ if( !NetpLogonGetDBInfo( UasChange,
+ UasChangeSize,
+ &Where,
+ &DBChangeInfo ) ) {
+
+ return FALSE;
+
+ }
+
+ }
+
+ //
+ // Check domain SID.
+ //
+ // Read Domain SID Length
+ //
+
+ if ( !NetpLogonGetBytes( UasChange,
+ UasChangeSize,
+ &Where,
+ sizeof(UasChange->DomainSidSize),
+ &DomainSIDSize )) {
+ return( FALSE );
+
+ }
+
+
+ //
+ // get and compare SID
+ //
+
+ if( DomainSIDSize > 0 ) {
+
+ PCHAR DomainSID;
+
+ if ( !NetpLogonGetDomainSID( UasChange,
+ UasChangeSize,
+ &Where,
+ DomainSIDSize,
+ &DomainSID )) {
+ return( FALSE );
+ }
+
+ //
+ // compare domain SIDs
+ //
+
+ if( !RtlEqualSid( NlGlobalPrimaryDomainId, DomainSID ) ) {
+
+ LPWSTR AlertStrings[4];
+
+ //
+ // alert admin.
+ //
+
+ AlertStrings[0] = AnncPrimary;
+ AlertStrings[1] = NlGlobalUnicodeDomainName;
+ AlertStrings[2] = NlGlobalUnicodeComputerName;
+ AlertStrings[3] = NULL;
+
+ RaiseAlert( ALERT_NetLogonMismatchSIDInMsg,
+ AlertStrings );
+
+ //
+ // Save the info in the eventlog
+ //
+
+ NlpWriteEventlog(
+ ALERT_NetLogonMismatchSIDInMsg,
+ EVENTLOG_ERROR_TYPE,
+ NULL,
+ 0,
+ AlertStrings,
+ 3 );
+
+
+ return( FALSE );
+ }
+
+ }
+
+ if( NlGlobalRole != RoleBackup ) {
+
+ //
+ // Duplicate PDC found on this domain
+ //
+
+ LPWSTR AlertStrings[4];
+
+ //
+ // alert admin.
+ //
+
+ AlertStrings[0] = AnncPrimary;
+ AlertStrings[1] = NlGlobalUnicodeComputerName;
+ AlertStrings[2] = NlGlobalUnicodeDomainName;
+ AlertStrings[3] = NULL;
+
+ RaiseAlert( ALERT_NetLogonDuplicatePDC,
+ AlertStrings );
+
+ //
+ // Save the info in the eventlog
+ //
+
+ NlpWriteEventlog(
+ ALERT_NetLogonDuplicatePDC,
+ EVENTLOG_ERROR_TYPE,
+ NULL,
+ 0,
+ AlertStrings,
+ 3 );
+
+ return( FALSE );
+ }
+
+ //
+ // If we don't currently have a session to a PDC,
+ // Or if this message is from a new PDC (we probably just missed
+ // the LOGON_START_PRIMARY message),
+ // set up a session to the new PDC.
+ //
+
+ Status = NlNewSessionSetup( AnncPrimary );
+ if ( !NT_SUCCESS( Status ) ) {
+ return FALSE;
+ }
+
+
+ //
+ // Update change log info now. However update only those DBs we
+ // support.
+ //
+
+ Where = WhereDBChangeInfo;
+
+ for( i = 0; i < NUM_DBS; i++ ) {
+
+ //
+ // Get DB_CHANGE_STRUCTURE
+ //
+
+ if( !NetpLogonGetDBInfo( UasChange,
+ UasChangeSize,
+ &Where,
+ &DBChangeInfo ) ) {
+
+ return FALSE;
+
+ }
+
+ StartReplicator += NlUpdateRequired( &DBChangeInfo );
+ }
+
+
+ //
+ // Start the replicator thread if it isn't already running.
+ //
+
+ if ( StartReplicator ) {
+
+ //
+ // Generate a pseudo random number in range 0 - random.
+ // Delay our delta/sync request by that much time
+ //
+ // Note that random number generator was seeded at startup time.
+ //
+
+ RandomSleep = SmbGetUlong( &UasChange->Random ) * 1000;
+ RandomSleep = (DWORD) rand() % RandomSleep;
+
+ return( NlStartReplicatorThread( RandomSleep ) );
+
+ }
+
+ return TRUE;
+}
+
+
+BOOL
+IsReplicatorRunning(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ Test if the replicator thread is running
+
+ Enter with NlGlobalReplicatorCritSect locked.
+
+Arguments:
+
+ NONE
+
+Return Value:
+
+ TRUE - The replicator thread is running
+
+ FALSE - The replicator thread is not running.
+
+--*/
+{
+ DWORD WaitStatus;
+
+ //
+ // Determine if the replicator thread is already running.
+ //
+
+ if ( NlGlobalReplicatorThreadHandle != NULL ) {
+
+ //
+ // Time out immediately if the replicator is still running.
+ //
+
+ WaitStatus = WaitForSingleObject( NlGlobalReplicatorThreadHandle, 0 );
+
+ if ( WaitStatus == WAIT_TIMEOUT ) {
+ //
+ // Handle the case that the replicator thread has finished
+ // processing, but is in the process of exitting.
+ //
+
+ if ( !NlGlobalReplicatorIsRunning ) {
+ NlStopReplicator();
+ return FALSE;
+ }
+ return TRUE;
+
+ } else if ( WaitStatus == 0 ) {
+ CloseHandle( NlGlobalReplicatorThreadHandle );
+ NlGlobalReplicatorThreadHandle = NULL;
+ return FALSE;
+
+ } else {
+ NlPrint((NL_CRITICAL,
+ "Cannot WaitFor replicator thread: %ld\n",
+ WaitStatus ));
+ return TRUE;
+ }
+
+ }
+
+ return FALSE;
+}
+
+
+VOID
+NlStopReplicator(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ Stops the replicator thread if it is running and waits for it to stop.
+
+ Enter with NlGlobalReplicatorCritSect locked.
+
+Arguments:
+
+ NONE
+
+Return Value:
+
+ NONE
+
+--*/
+{
+ //
+ // Ask the replicator to stop running.
+ //
+
+ NlGlobalReplicatorTerminate = TRUE;
+ if ( !SetEvent( NlGlobalReplicatorTerminateEvent ) ) {
+ NlPrint((NL_CRITICAL, "Cannot set replicator termination event: %lu\n",
+ GetLastError() ));
+ }
+
+ //
+ // Determine if the replicator thread is already running.
+ //
+
+ if ( NlGlobalReplicatorThreadHandle != NULL ) {
+
+ //
+ // We've asked the replicator to stop. It should do so soon.
+ // Wait for it to stop.
+ //
+
+ NlWaitForSingleObject( "Wait for replicator to stop",
+ NlGlobalReplicatorThreadHandle );
+
+
+ CloseHandle( NlGlobalReplicatorThreadHandle );
+ NlGlobalReplicatorThreadHandle = NULL;
+
+ }
+
+ if ( !ResetEvent( NlGlobalReplicatorTerminateEvent ) ) {
+ NlPrint((NL_CRITICAL, "Cannot set replicator termination event: %lu\n",
+ GetLastError() ));
+ }
+ NlGlobalReplicatorTerminate = FALSE;
+
+ return;
+}
+
+
+BOOL
+NlStartReplicatorThread(
+ IN DWORD RandomSleep
+ )
+/*++
+
+Routine Description:
+
+ Start the Replication thread if it is not already running.
+
+Arguments:
+
+ RandomSleep - Number of millseconds to delay before working
+
+Return Value:
+ None
+
+--*/
+{
+ DWORD ThreadHandle;
+
+ //
+ // If the replicator thread is already running, do nothing.
+ //
+
+ EnterCriticalSection( &NlGlobalReplicatorCritSect );
+ if ( IsReplicatorRunning() ) {
+ NlPrint((NL_SYNC, "The replicator thread is already running.\n"));
+ LeaveCriticalSection( &NlGlobalReplicatorCritSect );
+ return TRUE;
+ }
+
+ //
+ // If we're not supposed to ever replicate,
+ // do nothing.
+ //
+
+ if ( NlGlobalGovernorParameter == 0 ) {
+ NlPrint((NL_CRITICAL, "Don't start replicator because Governor is zero.\n"));
+ LeaveCriticalSection( &NlGlobalReplicatorCritSect );
+ return FALSE;
+ }
+
+ //
+ // Initialize the replication parameters
+ //
+
+ NlGlobalReplicatorTerminate = FALSE;
+
+ NlGlobalReplParam.RandomSleep = RandomSleep;
+
+ NlGlobalReplicatorThreadHandle = CreateThread(
+ NULL, // No security attributes
+ THREAD_STACKSIZE,
+ NlReplicator,
+ &NlGlobalReplParam,
+ 0, // No special creation flags
+ &ThreadHandle );
+
+ if ( NlGlobalReplicatorThreadHandle == NULL ) {
+
+ //
+ // ?? Shouldn't we do something in non-debug case
+ //
+
+ NlPrint((NL_CRITICAL, "Can't create replicator Thread %lu\n",
+ GetLastError() ));
+
+ LeaveCriticalSection( &NlGlobalReplicatorCritSect );
+ return FALSE;
+ }
+
+
+ NlGlobalReplicatorIsRunning = TRUE;
+
+ LeaveCriticalSection( &NlGlobalReplicatorCritSect );
+ return TRUE;
+
+}
diff --git a/private/net/svcdlls/logonsrv/server/lsrvutil.c b/private/net/svcdlls/logonsrv/server/lsrvutil.c
new file mode 100644
index 000000000..33ccc0be6
--- /dev/null
+++ b/private/net/svcdlls/logonsrv/server/lsrvutil.c
@@ -0,0 +1,3055 @@
+/*++
+
+Copyright (c) 1987-1992 Microsoft Corporation
+
+Module Name:
+
+ lsrvutil.c
+
+Abstract:
+
+ Utility functions for the netlogon service.
+
+Author:
+
+ Ported from Lan Man 2.0
+
+Environment:
+
+ User mode only.
+ Contains NT-specific code.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+ 00-Jun-1989 (PradyM)
+ modified lm10 code for new NETLOGON service
+
+ 00-Feb-1990 (PradyM)
+ bugfixes
+
+ 00-Aug-1990 (t-RichE)
+ added alerts for auth failure due to time slippage
+
+ 11-Jul-1991 (cliffv)
+ Ported to NT. Converted to NT style.
+
+ 02-Jan-1992 (madana)
+ added support for builtin/multidomain replication.
+
+ 09-Apr-1992 JohnRo
+ Prepare for WCHAR.H (_wcsicmp vs _wcscmpi, etc).
+
+--*/
+
+//
+// Common include files.
+//
+
+#include <logonsrv.h> // Include files common to entire service
+
+//
+// Include files specific to this .c file
+//
+
+#include <accessp.h> // NetpAliasMemberToPriv
+#include <alertmsg.h> // Alert message text.
+#include <align.h> // ROUND_UP_COUNT ...
+#include <lmapibuf.h>
+#include <lmerr.h> // System Error Log definitions
+#include <lmserver.h> // server API functions and prototypes
+#include <lmshare.h> // share API functions and prototypes
+#include <lmsvc.h> // SERVICE_UIC codes are defined here
+#include <msgtext.h> // MTXT_* defines
+#include <netcan.h> // NetpwPathCompare()
+#include <replutil.h> // UnpackSamXXX()
+#include <secobj.h> // NetpDomainIdToSid
+#include <ssiapi.h> // I_NetSamDeltas()
+#include <stddef.h> // offsetof
+#include <stdlib.h> // C library functions (rand, etc)
+#include <tstring.h> // IS_PATH_SEPARATOR ...
+
+/*lint -e740 */ /* don't complain about unusual cast */
+
+
+#define MAX_SSI_PWAGE (long) (7L*24L*60L*60L*1000L) // 7 days
+#define MAX_DC_AUTHENTICATION_WAIT (long) (45L*1000L) // 45 seconds
+#define MAX_WKSTA_AUTHENTICATION_WAIT (long) (45L*1000L) // 45 seconds
+
+//
+// We want to prevent too-frequent alerts from
+// being sent in case of Authentication failures.
+//
+
+#define MAX_ALERTS 10 // send one every 10 to 30 mins based on pulse
+
+
+VOID
+RaiseNetlogonAlert(
+ IN DWORD alertNum,
+ IN LPWSTR alertArg1,
+ IN LPWSTR alertArg2,
+ IN OUT DWORD *ptrAlertCount
+ )
+/*++
+
+Routine Description:
+
+ Raise an alert once per MAX_ALERTS occurances
+
+Arguments:
+
+ alertNum -- RaiseAlert() alert number.
+
+ alertArg1 -- RaiseAlert() argument 1.
+
+ alertArg2 -- RaiseAlert() argument 2.
+
+ ptrAlertCount -- Points to the count of occurence of this particular
+ alert. This routine increments it and will set the to that value
+ modulo MAX_ALERTS.
+
+Return Value:
+
+ NONE
+
+--*/
+{
+ LPWSTR AlertStrings[3];
+
+ AlertStrings[0] = alertArg1;
+ AlertStrings[1] = alertArg2;
+ AlertStrings[2] = NULL;
+
+ if (*ptrAlertCount == 0) {
+ RaiseAlert(alertNum, AlertStrings);
+ }
+ (*ptrAlertCount)++;
+ (*ptrAlertCount) %= MAX_ALERTS;
+}
+
+
+
+
+
+BOOL
+NlSetPrimaryName(
+ IN LPWSTR PrimaryName
+ )
+/*++
+
+Routine Description:
+
+ This routine sets the specified PDC name in the appropriate global
+ variables.
+
+Arguments:
+
+ PrimaryName - The servername of the PDC for this domain.
+
+Return Value:
+
+ TRUE - iff the operation was successfull.
+
+--*/
+{
+ LPSTR AnsiPrimaryName;
+ DWORD i;
+
+ //
+ // If the caller wants us to forget the primary name,
+ // just reset the globals.
+ //
+
+ if ( PrimaryName == NULL ) {
+ NlGlobalAnsiPrimaryName[0] = '\0';
+ NlGlobalUncPrimaryName[0] = L'\0';
+ NlGlobalUnicodePrimaryName = NlGlobalUncPrimaryName;
+ return TRUE;
+ }
+
+
+ //
+ // Anytime the PDC changes, force a partial sync on all databases.
+ //
+ // Since the PDC needs to know the serial number of our databases,
+ // this ensures we tell him.
+ //
+
+ EnterCriticalSection( &NlGlobalDbInfoCritSect );
+ for( i = 0; i < NUM_DBS; i++ ) {
+ NlGlobalDBInfoArray[i].UpdateRqd = TRUE;
+ }
+ LeaveCriticalSection( &NlGlobalDbInfoCritSect );
+
+ //
+ // Copy the primary name to the globals.
+ //
+
+ wcscpy( NlGlobalUncPrimaryName, L"\\\\" );
+ wcsncpy( NlGlobalUncPrimaryName+2,
+ PrimaryName,
+ (sizeof(NlGlobalUncPrimaryName)/sizeof(WCHAR)) - 2);
+ NlGlobalUncPrimaryName[ UNCLEN ] = '\0';
+ NlGlobalUnicodePrimaryName = NlGlobalUncPrimaryName + 2;
+
+ AnsiPrimaryName = NetpLogonUnicodeToOem( NlGlobalUnicodePrimaryName );
+ if ( AnsiPrimaryName == NULL ) {
+ NlGlobalAnsiPrimaryName[0] = '\0';
+ NlGlobalUncPrimaryName[0] = L'\0';
+ NlGlobalUnicodePrimaryName = NlGlobalUncPrimaryName;
+ return FALSE;
+ }
+ lstrcpynA( NlGlobalAnsiPrimaryName,
+ AnsiPrimaryName,
+ sizeof(NlGlobalAnsiPrimaryName) );
+ NlGlobalAnsiPrimaryName[ CNLEN ] = '\0';
+
+ NetpMemoryFree( AnsiPrimaryName );
+
+ return TRUE;
+}
+
+
+
+
+BOOL
+NlResetFirstTimeFullSync(
+ IN DWORD DBIndex
+ )
+/*++
+
+Routine Description:
+
+ If a database is currently marked as needing a first time full sync,
+ reset that requirement.
+
+Arguments:
+
+ DBIndex -- DB Index of the database being changed
+
+Return Value:
+
+ TRUE - iff the operation was successfull.
+
+--*/
+{
+ NTSTATUS Status;
+
+
+ //
+ // If the database is already marked,
+ // Don't bother marking it again.
+ //
+
+ if ( NlNameCompare( NlGlobalDBInfoArray[DBIndex].PrimaryName,
+ NlGlobalUnicodePrimaryName,
+ NAMETYPE_COMPUTER ) == 0 ) {
+ return TRUE;
+ }
+
+
+ //
+ // Handle the LSA specially
+ //
+
+ if ( DBIndex == LSA_DB ) {
+ LSAPR_POLICY_INFORMATION PolicyReplication;
+
+ RtlInitUnicodeString(
+ (PUNICODE_STRING)&PolicyReplication.PolicyReplicaSourceInfo.ReplicaSource,
+ NlGlobalUnicodePrimaryName );
+
+ RtlInitUnicodeString(
+ (PUNICODE_STRING)&PolicyReplication.PolicyReplicaSourceInfo.ReplicaAccountName,
+ NULL );
+
+ Status = LsarSetInformationPolicy(
+ NlGlobalDBInfoArray[DBIndex].DBHandle,
+ PolicyReplicaSourceInformation,
+ &PolicyReplication );
+
+ if ( !NT_SUCCESS(Status) ) {
+
+ NlPrint((NL_CRITICAL,
+ "NlResetFirstTimeFullSync: " FORMAT_LPWSTR
+ ": reset full sync failed 0x%lx " FORMAT_LPWSTR ".\n",
+ NlGlobalDBInfoArray[DBIndex].DBName,
+ Status,
+ NlGlobalUncPrimaryName ));
+
+ return FALSE;
+ }
+
+ //
+ // Handle a SAM database.
+ //
+
+ } else {
+
+ SAMPR_DOMAIN_REPLICATION_INFORMATION DomainReplication;
+
+ RtlInitUnicodeString(
+ (PUNICODE_STRING)&DomainReplication.ReplicaSourceNodeName,
+ NlGlobalUnicodePrimaryName );
+
+ Status = SamrSetInformationDomain(
+ NlGlobalDBInfoArray[DBIndex].DBHandle,
+ DomainReplicationInformation,
+ (PSAMPR_DOMAIN_INFO_BUFFER) &DomainReplication );
+
+ if ( !NT_SUCCESS(Status) ) {
+
+ NlPrint((NL_CRITICAL,
+ "NlResetFirstTimeFullSync: " FORMAT_LPWSTR
+ ": reset full sync failed 0x%lx " FORMAT_LPWSTR ".\n",
+ NlGlobalDBInfoArray[DBIndex].DBName,
+ Status,
+ NlGlobalUncPrimaryName ));
+
+ return FALSE;
+ }
+ }
+
+ //
+ // Set the in-memory copy to match.
+ //
+
+ wcscpy( NlGlobalDBInfoArray[DBIndex].PrimaryName, NlGlobalUnicodePrimaryName );
+
+ NlPrint((NL_SYNC,
+ "NlResetFirstTimeFullSync: " FORMAT_LPWSTR
+ ": Set ReplicaSource to " FORMAT_LPWSTR ".\n",
+ NlGlobalDBInfoArray[DBIndex].DBName,
+ NlGlobalUncPrimaryName ));
+
+ return TRUE;
+
+}
+
+
+NTSTATUS
+NlOpenSecret(
+ IN PCLIENT_SESSION ClientSession,
+ IN ULONG DesiredAccess,
+ OUT PLSAPR_HANDLE SecretHandle
+ )
+/*++
+
+Routine Description:
+
+
+ Open the Lsa Secret Object containing the password to be used for the
+ specified client session.
+
+Arguments:
+
+ ClientSession - Structure used to define the session.
+ On Input, the following fields must be set:
+ CsDomainName
+ CsSecureChannelType
+
+ DesiredAccess - Access required to the secret.
+
+ SecretHandle - Returns a handle to the secret.
+
+Return Value:
+
+ Status of operation.
+
+--*/
+{
+ NTSTATUS Status;
+ WCHAR SecretName[ LSA_GLOBAL_SECRET_PREFIX_LENGTH +
+ SSI_SECRET_PREFIX_LENGTH + DNLEN + 1 ];
+ UNICODE_STRING SecretNameString;
+
+ NlAssert( ClientSession->CsReferenceCount > 0 );
+
+ //
+ // Determine the name of the secret in LSA secret storage that
+ // defines the password for this account.
+ //
+ // Use:
+ // G$NETLOGON$DomainName for Domain accounts
+ // NETLOGON$MACHINE_ACCOUNT for workstation and server accounts
+ //
+ // Short form:
+ // G$$DomainName for Domain accounts
+ // $MACHINE.ACC for workstation and server accounts
+ //
+
+ switch ( ClientSession->CsSecureChannelType ) {
+ case TrustedDomainSecureChannel:
+ wcscpy( SecretName, LSA_GLOBAL_SECRET_PREFIX );
+ wcscat( SecretName, SSI_SECRET_PREFIX );
+ wcscat( SecretName, ClientSession->CsDomainName.Buffer );
+ break;
+
+ case ServerSecureChannel:
+ case WorkstationSecureChannel:
+ wcscpy( SecretName, SSI_SECRET_PREFIX );
+ wcscat( SecretName, SSI_SECRET_POSTFIX );
+ break;
+
+ default:
+ Status = STATUS_INTERNAL_ERROR;
+ NlPrint((NL_CRITICAL, "NlOpenSecret: Invalid account type\n"));
+ return Status;
+
+ }
+
+ //
+ // Get the Password of the account from LSA secret storage
+ //
+
+ RtlInitUnicodeString( &SecretNameString, SecretName );
+
+ Status = LsarOpenSecret(
+ NlGlobalPolicyHandle,
+ (PLSAPR_UNICODE_STRING)&SecretNameString,
+ DesiredAccess,
+ SecretHandle );
+
+ return Status;
+
+}
+
+
+BOOLEAN
+NlTimeToRediscover(
+ IN PCLIENT_SESSION ClientSession
+ )
+/*++
+
+Routine Description:
+
+ Determine if it is time to rediscover this Client Session.
+ If a session setup failure happens to a discovered DC,
+ rediscover the DC if the discovery happened a long time ago (more than 5 minutes).
+
+Arguments:
+
+ ClientSession - Structure used to define the session.
+
+Return Value:
+
+ TRUE -- iff it is time to re-discover
+
+--*/
+{
+ BOOLEAN ReturnBoolean;
+
+ EnterCriticalSection( &NlGlobalDcDiscoveryCritSect );
+ ReturnBoolean = NlTimeHasElapsed(
+ ClientSession->CsLastDiscoveryTime,
+ ClientSession->CsSecureChannelType == WorkstationSecureChannel ?
+ MAX_WKSTA_REAUTHENTICATION_WAIT :
+ MAX_DC_REAUTHENTICATION_WAIT );
+ LeaveCriticalSection( &NlGlobalDcDiscoveryCritSect );
+
+ return ReturnBoolean;
+}
+
+
+NTSTATUS
+NlSessionSetup(
+ IN OUT PCLIENT_SESSION ClientSession
+ )
+/*++
+
+Routine Description:
+
+ Verify that the requestor (this machine) has a valid account at
+ Primary Domain Controller (primary). The authentication
+ is done via an elaborate protocol. This routine will be
+ used only when NETLOGON service starts with role != primary.
+
+ The requestor (i.e. this machine) will generate a challenge
+ and send it to the Primary Domain Controller and will receive
+ a challenge from the primary in response. Now we will compute
+ credentials using primary's challenge and send it across and
+ wait for credentials, computed at primary using our initial
+ challenge, to be returned by PDC. Before computing credentials
+ a sessionkey will be built which uniquely identifies this
+ session and it will be returned to caller for future use.
+
+ If both machines authenticate then they keep the
+ ClientCredential and the session key for future use.
+
+Arguments:
+
+ ClientSession - Structure used to define the session.
+ On Input the following fields must be set:
+ CsState
+ CsDomainName
+ CsUncServerName (May be empty string depending on SecureChannelType)
+ CsAccountName
+ CsSecureChannelType
+ The caller must be a writer of the ClientSession.
+
+ On Output, the following fields will be set
+ CsConnectionStatus
+ CsState
+ CsSessionKey
+ CsAuthenticationSeed
+
+Return Value:
+
+ Status of operation.
+
+--*/
+{
+ NTSTATUS Status;
+
+ NETLOGON_CREDENTIAL ServerChallenge;
+ NETLOGON_CREDENTIAL ClientChallenge;
+ NETLOGON_CREDENTIAL ComputedServerCredential;
+ NETLOGON_CREDENTIAL ReturnedServerCredential;
+
+ LSAPR_HANDLE SecretHandle = NULL;
+
+ PLSAPR_CR_CIPHER_VALUE CrCurrentPassword = NULL;
+ PLSAPR_CR_CIPHER_VALUE CrOldPassword = NULL;
+ BOOLEAN WeDidDiscovery = FALSE;
+ BOOLEAN ErrorFromDiscoveredServer = FALSE;
+
+ //
+ // Used to indicate whether the current or the old password is being
+ // tried to access the DC.
+ // 0: implies the current password
+ // 1: implies the old password
+ // 2: implies both failed
+ //
+
+ DWORD State;
+
+ //
+ // Ensure we're a writer.
+ //
+
+ NlAssert( ClientSession->CsReferenceCount > 0 );
+ NlAssert( ClientSession->CsFlags & CS_WRITER );
+
+ NlPrint((NL_SESSION_SETUP,
+ "NlSessionSetup: %wZ Try Session setup\n",
+ &ClientSession->CsDomainName ));
+
+
+ //
+ // If we're free to pick the DC which services our request,
+ // do so.
+ //
+ // Apparently there was a problem with the previously chosen DC
+ // so we pick again here. (There is a chance we'll pick the same server.)
+ //
+
+ if ( ClientSession->CsState == CS_IDLE ) {
+
+ WeDidDiscovery = TRUE;
+
+ //
+ // Pick the name of a DC in the domain.
+ //
+
+ Status = NlDiscoverDc(ClientSession, DT_Synchronous ) ;
+
+ if ( !NT_SUCCESS(Status) ) {
+
+ NlPrint((NL_CRITICAL,
+ "NlSessionSetup: %wZ Session setup: "
+ "cannot pick trusted DC\n",
+ &ClientSession->CsDomainName ));
+
+ goto Cleanup;
+
+ }
+
+ }
+ NlAssert( ClientSession->CsState != CS_IDLE );
+
+
+ //
+ // Prepare our challenge
+ //
+
+FirstTryFailed:
+ NlComputeChallenge( &ClientChallenge );
+
+
+
+#ifdef BAD_ALIGNMENT
+ NlPrint((NL_CHALLENGE_RES,"NlSessionSetup: ClientChallenge = %lx %lx\n",
+ ((DWORD *)&ClientChallenge)[0],
+ ((DWORD *)&ClientChallenge)[1]));
+#endif // BAD_ALIGNMENT
+
+
+ //
+ // Get the Password of the account from LSA secret storage
+ //
+
+ Status = NlOpenSecret( ClientSession, SECRET_QUERY_VALUE, &SecretHandle );
+
+ if ( !NT_SUCCESS( Status ) ) {
+
+ NlPrint((NL_CRITICAL,
+ "NlSessionSetup: %wZ Session setup: "
+ "cannot NlOpenSecret 0x%lx\n",
+ &ClientSession->CsDomainName,
+ Status ));
+
+ //
+ // return more appropriate error.
+ //
+
+ Status = STATUS_NO_TRUST_LSA_SECRET;
+ goto Cleanup;
+ }
+
+ Status = LsarQuerySecret(
+ SecretHandle,
+ &CrCurrentPassword,
+ NULL,
+ &CrOldPassword,
+ NULL );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ NlPrint((NL_CRITICAL,
+ "NlSessionSetup: %wZ Session setup: "
+ "cannot LsaQuerySecret 0x%lx\n",
+ &ClientSession->CsDomainName,
+ Status ));
+
+ //
+ // return more appropriate error.
+ //
+
+ Status = STATUS_NO_TRUST_LSA_SECRET;
+ goto Cleanup;
+ }
+
+ //
+ // Try setting up a secure channel first using the CurrentPassword.
+ // If that fails, try using the OldPassword
+ //
+
+
+ for ( State = 0; ; State++ ) {
+
+ NT_OWF_PASSWORD NtOwfPassword;
+ UNICODE_STRING CurrentPassword;
+ PLSAPR_CR_CIPHER_VALUE PasswordToTry;
+
+
+ //
+ // Use the right password for this iteration
+ //
+
+ if ( State == 0 ) {
+ PasswordToTry = CrCurrentPassword;
+ } else if ( State == 1 ) {
+
+ if ( CrCurrentPassword != NULL &&
+ CrOldPassword != NULL &&
+ CrCurrentPassword->Buffer != NULL &&
+ CrOldPassword->Buffer != NULL &&
+ CrCurrentPassword->Length == CrOldPassword->Length &&
+ RtlEqualMemory( CrCurrentPassword->Buffer,
+ CrOldPassword->Buffer,
+ CrOldPassword->Length ) ) {
+
+ NlPrint((NL_CRITICAL,
+ "NlSessionSetup: %wZ new password is bad. Old password is same as new password.\n",
+ &ClientSession->CsDomainName ));
+ Status = STATUS_ACCESS_DENIED;
+ goto Cleanup;
+ }
+
+ PasswordToTry = CrOldPassword;
+ NlPrint((NL_CRITICAL,
+ "NlSessionSetup: %wZ new password is bad, try old one\n",
+ &ClientSession->CsDomainName ));
+ } else {
+ Status = STATUS_ACCESS_DENIED;
+ goto Cleanup;
+ }
+
+
+ //
+ // If this particular password isn't present in the LSA,
+ // just ignore it.
+ //
+
+ if ( PasswordToTry == NULL || PasswordToTry->Buffer == NULL ) {
+ continue;
+ }
+
+ CurrentPassword.Length = (USHORT)PasswordToTry->Length;
+ CurrentPassword.MaximumLength = (USHORT)PasswordToTry->MaximumLength;
+ CurrentPassword.Buffer = (LPWSTR)PasswordToTry->Buffer;
+
+
+ //
+ // Get the primary's challenge
+ //
+
+ NlAssert( ClientSession->CsState != CS_IDLE );
+ Status = NlStartApiClientSession( ClientSession, TRUE );
+
+ if ( NT_SUCCESS(Status) ) {
+ Status = I_NetServerReqChallenge(ClientSession->CsUncServerName,
+ NlGlobalUnicodeComputerName,
+ &ClientChallenge,
+ &ServerChallenge );
+ }
+
+ if ( !NlFinishApiClientSession( ClientSession, FALSE ) ) {
+ NlPrint((NL_CRITICAL,
+ "NlSessionSetup: %wZ Session setup: "
+ "cannot FinishApiClientSession for I_NetServerReqChallenge 0x%lx\n",
+ &ClientSession->CsDomainName,
+ Status ));
+ // Failure here indicates that the discovered server is really slow.
+ // Let the "ErrorFromDiscoveredServer" logic do the rediscovery.
+ if ( NT_SUCCESS(Status) ) {
+ // We're dropping the secure channel so
+ // ensure we don't use any successful status from the DC
+ Status = STATUS_NO_LOGON_SERVERS;
+ }
+ ErrorFromDiscoveredServer = TRUE;
+ goto Cleanup;
+ }
+
+ if ( !NT_SUCCESS( Status ) ) {
+ NlPrint((NL_CRITICAL,
+ "NlSessionSetup: %wZ Session setup: "
+ "cannot I_NetServerReqChallenge 0x%lx\n",
+ &ClientSession->CsDomainName,
+ Status ));
+ ErrorFromDiscoveredServer = TRUE;
+ goto Cleanup;
+ }
+
+#ifdef BAD_ALIGNMENT
+ NlPrint((NL_CHALLENGE_RES,"NlSessionSetup: ServerChallenge = %lx %lx\n",
+ ((DWORD *)&ServerChallenge)[0],
+ ((DWORD *)&ServerChallenge)[1]));
+#endif // BAD_ALIGNMENT
+
+
+ //
+ // Compute the NT OWF password for this user.
+ //
+
+ Status = RtlCalculateNtOwfPassword(
+ &CurrentPassword,
+ &NtOwfPassword );
+
+ if ( !NT_SUCCESS( Status ) ) {
+
+ //
+ // return more appropriate error.
+ //
+
+ Status = STATUS_NO_TRUST_LSA_SECRET;
+ goto Cleanup;
+ }
+
+
+#ifdef BAD_ALIGNMENT
+ NlPrint((NL_CHALLENGE_RES,"NlSessionSetup: Password = %lx %lx %lx %lx\n",
+ ((DWORD *) (&NtOwfPassword))[0],
+ ((DWORD *) (&NtOwfPassword))[1],
+ ((DWORD *) (&NtOwfPassword))[2],
+ ((DWORD *) (&NtOwfPassword))[3]));
+#endif // BAD_ALIGNMENT
+
+
+ //
+ // Actually compute the session key given the two challenges and the
+ // password.
+ //
+
+ NlMakeSessionKey(
+ &NtOwfPassword,
+ &ClientChallenge,
+ &ServerChallenge,
+ &ClientSession->CsSessionKey );
+
+
+#ifdef BAD_ALIGNMENT
+ NlPrint((NL_CHALLENGE_RES,"NlSessionSetup: SessionKey = %lx %lx %lx %lx\n",
+ ((DWORD *) (&ClientSession->CsSessionKey))[0],
+ ((DWORD *) (&ClientSession->CsSessionKey))[1],
+ ((DWORD *) (&ClientSession->CsSessionKey))[2],
+ ((DWORD *) (&ClientSession->CsSessionKey))[3]));
+#endif // BAD_ALIGNMENT
+
+
+ //
+ // Prepare credentials using our challenge.
+ //
+
+ NlComputeCredentials( &ClientChallenge,
+ &ClientSession->CsAuthenticationSeed,
+ &ClientSession->CsSessionKey );
+
+#ifdef BAD_ALIGNMENT
+ NlPrint((NL_CHALLENGE_RES,"NlSessionSetup: Authentication Seed = %lx %lx\n",
+ ((DWORD *) (&ClientSession->CsAuthenticationSeed))[0],
+ ((DWORD *) (&ClientSession->CsAuthenticationSeed))[1]));
+#endif // BAD_ALIGNMENT
+
+ //
+ // Send these credentials to primary. The primary will compute
+ // credentials using the challenge supplied by us and compare
+ // with these. If both match then it will compute credentials
+ // using its challenge and return it to us for verification
+ //
+
+ Status = NlStartApiClientSession( ClientSession, TRUE );
+
+ if ( NT_SUCCESS(Status) ) {
+ ClientSession->CsNegotiatedFlags = NETLOGON_SUPPORTS_MASK;
+ Status = I_NetServerAuthenticate2( ClientSession->CsUncServerName,
+ ClientSession->CsAccountName,
+ ClientSession->CsSecureChannelType,
+ NlGlobalUnicodeComputerName,
+ &ClientSession->CsAuthenticationSeed,
+ &ReturnedServerCredential,
+ &ClientSession->CsNegotiatedFlags );
+ }
+
+ if ( Status == RPC_NT_PROCNUM_OUT_OF_RANGE ) {
+ ClientSession->CsNegotiatedFlags = 0;
+ Status = I_NetServerAuthenticate( ClientSession->CsUncServerName,
+ ClientSession->CsAccountName,
+ ClientSession->CsSecureChannelType,
+ NlGlobalUnicodeComputerName,
+ &ClientSession->CsAuthenticationSeed,
+ &ReturnedServerCredential );
+ }
+
+ if ( !NlFinishApiClientSession( ClientSession, FALSE ) ) {
+ NlPrint((NL_CRITICAL,
+ "NlSessionSetup: %wZ Session setup: "
+ "cannot FinishApiClientSession for I_NetServerAuthenticate 0x%lx\n",
+ &ClientSession->CsDomainName,
+ Status ));
+ // Failure here indicates that the discovered server is really slow.
+ // Let the "ErrorFromDiscoveredServer" logic do the rediscovery.
+ if ( NT_SUCCESS(Status) ) {
+ // We're dropping the secure channel so
+ // ensure we don't use any successful status from the DC
+ Status = STATUS_NO_LOGON_SERVERS;
+ }
+ ErrorFromDiscoveredServer = TRUE;
+ goto Cleanup;
+ }
+
+ if ( !NT_SUCCESS( Status ) ) {
+ NlPrint((NL_CRITICAL,
+ "NlSessionSetup: %wZ Session setup: "
+ "cannot I_NetServerAuthenticate 0x%lx\n",
+ &ClientSession->CsDomainName,
+ Status ));
+ ErrorFromDiscoveredServer = TRUE;
+
+ //
+ // If access is denied, it might be because we weren't able to
+ // authenticate with the new password, try the old password.
+ //
+
+ if ( Status == STATUS_ACCESS_DENIED && State == 0 ) {
+ continue;
+ }
+ goto Cleanup;
+ }
+
+
+#ifdef BAD_ALIGNMENT
+ NlPrint((NL_CHALLENGE_RES,"NlSessionSetup: ServerCredential GOT = %lx %lx\n",
+ ((DWORD *) (&ReturnedServerCredential))[0],
+ ((DWORD *) (&ReturnedServerCredential))[1]));
+#endif // BAD_ALIGNMENT
+
+
+ //
+ // The DC returned a server credential to us,
+ // ensure the server credential matches the one we would compute.
+ //
+
+ NlComputeCredentials( &ServerChallenge,
+ &ComputedServerCredential,
+ &ClientSession->CsSessionKey);
+
+
+#ifdef BAD_ALIGNMENT
+ NlPrint((NL_CHALLENGE_RES,"NlSessionSetup: ServerCredential MADE = %lx %lx\n",
+ ((DWORD *) (&ComputedServerCredential))[0],
+ ((DWORD *) (&ComputedServerCredential))[1]));
+#endif // BAD_ALIGNMENT
+
+
+ if (RtlCompareMemory( &ReturnedServerCredential,
+ &ComputedServerCredential,
+ sizeof(ReturnedServerCredential)) !=
+ sizeof(ReturnedServerCredential)) {
+ Status = STATUS_ACCESS_DENIED;
+ NlPrint((NL_CRITICAL,
+ "NlSessionSetup: %wZ Session setup: "
+ "Servercredential don't match ours 0x%lx\n",
+ &ClientSession->CsDomainName,
+ Status));
+ goto Cleanup;
+ }
+
+ //
+ // If we've made it this far, we've successfully authenticated
+ // with the DC, drop out of the loop.
+ //
+
+ break;
+ }
+
+ //
+ // If we used the old password to authenticate,
+ // update the DC to the current password ASAP.
+ //
+
+ if ( State == 1 ) {
+ NlPrint((NL_CRITICAL,
+ "NlSessionSetup: %wZ old password succeeded\n",
+ &ClientSession->CsDomainName ));
+ LOCK_TRUST_LIST();
+ ClientSession->CsFlags |= CS_UPDATE_PASSWORD;
+ UNLOCK_TRUST_LIST();
+ }
+
+ Status = STATUS_SUCCESS;
+
+ //
+ // Cleanup
+ //
+
+Cleanup:
+
+ //
+ // Free locally used resources
+ //
+
+ if ( SecretHandle != NULL ) {
+ (VOID) LsarClose( &SecretHandle );
+ SecretHandle == NULL;
+ }
+
+ if ( CrCurrentPassword != NULL ) {
+ (VOID) LsaIFree_LSAPR_CR_CIPHER_VALUE ( CrCurrentPassword );
+ CrCurrentPassword = NULL;
+ }
+
+ if ( CrOldPassword != NULL ) {
+ (VOID) LsaIFree_LSAPR_CR_CIPHER_VALUE ( CrOldPassword );
+ CrOldPassword = NULL;
+ }
+
+
+ //
+ // Upon success, save the status and reset counters.
+ //
+
+ if ( NT_SUCCESS(Status) ) {
+
+ NlSetStatusClientSession( ClientSession, Status );
+ ClientSession->CsAuthAlertCount = 0;
+ ClientSession->CsTimeoutCount = 0;
+#if DBG
+ if ( ClientSession->CsNegotiatedFlags != NETLOGON_SUPPORTS_MASK ) {
+ NlPrint((NL_CRITICAL,
+ "NlSessionSetup: %wZ negotiated %lx flags rather than %lx\n",
+ &ClientSession->CsDomainName,
+ ClientSession->CsNegotiatedFlags,
+ NETLOGON_SUPPORTS_MASK ));
+ }
+#endif // DBG
+
+
+
+ //
+ // write event log and raise alert
+ //
+
+ } else {
+
+ WCHAR PreviouslyDiscoveredServer[UNCLEN+1];
+ LPWSTR MsgStrings[3];
+
+ //
+ // Save the name of the discovered server.
+ //
+
+ if ( *ClientSession->CsUncServerName != L'\0' ) {
+ wcscpy( PreviouslyDiscoveredServer, ClientSession->CsUncServerName );
+ } else {
+ wcscpy( PreviouslyDiscoveredServer, L"<Unknown>" );
+ }
+
+ //
+ // If we didn't do the discovery just now,
+ // and the failure came from the discovered machine,
+ // try the discovery again and redo the session setup.
+ //
+
+ if ( !WeDidDiscovery &&
+ ErrorFromDiscoveredServer &&
+ NlTimeToRediscover( ClientSession) ) {
+
+ NTSTATUS TempStatus;
+
+ NlPrint((NL_SESSION_SETUP,
+ "NlSessionSetup: %wZ Retry failed session setup since discovery wasn't recent.\n",
+ &ClientSession->CsDomainName ));
+
+
+ //
+ // Pick the name of a new DC in the domain.
+ //
+
+ NlSetStatusClientSession( ClientSession, STATUS_NO_LOGON_SERVERS );
+
+ TempStatus = NlDiscoverDc(ClientSession, DT_Synchronous );
+
+ if ( NT_SUCCESS(TempStatus) ) {
+
+ //
+ // Don't bother redoing the session setup if we picked the same DC.
+ //
+
+ if ( NlNameCompare( ClientSession->CsUncServerName+2,
+ PreviouslyDiscoveredServer+2,
+ NAMETYPE_COMPUTER ) != 0 ) {
+ WeDidDiscovery = TRUE;
+ goto FirstTryFailed;
+ } else {
+ NlPrint((NL_SESSION_SETUP,
+ "NlSessionSetup: %wZ Skip retry failed session setup since same DC discovered.\n",
+ &ClientSession->CsDomainName ));
+ }
+
+ } else {
+ NlPrint((NL_CRITICAL,
+ "NlSessionSetup: %wZ Session setup: "
+ "cannot re-pick trusted DC\n",
+ &ClientSession->CsDomainName ));
+
+ }
+ }
+
+ switch(Status) {
+
+ case STATUS_NO_TRUST_LSA_SECRET:
+
+ MsgStrings[0] = PreviouslyDiscoveredServer;
+ MsgStrings[1] = ClientSession->CsDomainName.Buffer;
+ MsgStrings[2] = NlGlobalUnicodeComputerName;
+
+ NlpWriteEventlog (NELOG_NetlogonAuthNoTrustLsaSecret,
+ EVENTLOG_ERROR_TYPE,
+ (LPBYTE) &Status,
+ sizeof(Status),
+ MsgStrings,
+ 3 );
+ break;
+
+ case STATUS_NO_TRUST_SAM_ACCOUNT:
+
+ MsgStrings[0] = PreviouslyDiscoveredServer;
+ MsgStrings[1] = ClientSession->CsDomainName.Buffer;
+ MsgStrings[2] = NlGlobalUnicodeComputerName;
+
+ NlpWriteEventlog (NELOG_NetlogonAuthNoTrustSamAccount,
+ EVENTLOG_ERROR_TYPE,
+ (LPBYTE) &Status,
+ sizeof(Status),
+ MsgStrings,
+ 3 );
+ break;
+
+ case STATUS_ACCESS_DENIED:
+
+ MsgStrings[0] = ClientSession->CsDomainName.Buffer;
+ MsgStrings[1] = PreviouslyDiscoveredServer;
+
+ NlpWriteEventlog (NELOG_NetlogonAuthDCFail,
+ EVENTLOG_ERROR_TYPE,
+ (LPBYTE) &Status,
+ sizeof(Status),
+ MsgStrings,
+ 2 );
+ break;
+
+ case STATUS_NO_LOGON_SERVERS:
+ default:
+
+ MsgStrings[0] = ClientSession->CsDomainName.Buffer;
+ MsgStrings[1] = (LPWSTR) Status;
+
+ NlpWriteEventlog (NELOG_NetlogonAuthNoDomainController,
+ EVENTLOG_ERROR_TYPE,
+ (LPBYTE) &Status,
+ sizeof(Status),
+ MsgStrings,
+ 2 | LAST_MESSAGE_IS_NTSTATUS );
+ break;
+ }
+
+
+ MsgStrings[0] = PreviouslyDiscoveredServer;
+
+ RaiseNetlogonAlert( ALERT_NetlogonAuthDCFail,
+ ClientSession->CsDomainName.Buffer,
+ MsgStrings[0],
+ &ClientSession->CsAuthAlertCount);
+
+ //
+ // ?? Is this how to handle failure for all account types.
+ //
+
+ switch(Status) {
+
+ case STATUS_NO_TRUST_LSA_SECRET:
+ case STATUS_NO_TRUST_SAM_ACCOUNT:
+ case STATUS_ACCESS_DENIED:
+
+ NlSetStatusClientSession( ClientSession, Status );
+ break;
+
+ default:
+
+ NlSetStatusClientSession( ClientSession, STATUS_NO_LOGON_SERVERS );
+ break;
+ }
+ }
+
+
+ //
+ // Mark the time we last tried to authenticate.
+ //
+ // We need to do this after NlSetStatusClientSession which zeros
+ // CsLastAuthenticationTry.
+ //
+
+ EnterCriticalSection( &NlGlobalDcDiscoveryCritSect );
+ NtQuerySystemTime( &ClientSession->CsLastAuthenticationTry );
+ LeaveCriticalSection( &NlGlobalDcDiscoveryCritSect );
+
+
+ NlPrint((NL_SESSION_SETUP,
+ "NlSessionSetup: %wZ Session setup %s\n",
+ &ClientSession->CsDomainName,
+ (NT_SUCCESS(ClientSession->CsConnectionStatus)) ? "Succeeded" : "Failed" ));
+
+ return Status;
+}
+
+
+BOOLEAN
+NlTimeHasElapsed(
+ IN LARGE_INTEGER StartTime,
+ IN DWORD Timeout
+ )
+/*++
+
+Routine Description:
+
+ Determine if "Timeout" milliseconds has has elapsed since StartTime.
+
+Arguments:
+
+ StartTime - Specifies an absolute time when the event started (100ns units).
+
+ Timeout - Specifies a relative time in milliseconds. 0xFFFFFFFF indicates
+ that the time will never expire.
+
+Return Value:
+
+ TRUE -- iff Timeout milliseconds have elapsed since StartTime.
+
+--*/
+{
+ LARGE_INTEGER TimeNow;
+ LARGE_INTEGER ElapsedTime;
+ LARGE_INTEGER Period;
+
+ //
+ // If the period to too large to handle (i.e., 0xffffffff is forever),
+ // just indicate that the timer has not expired.
+ //
+ // (0x7fffffff is a little over 24 days).
+ //
+
+ if ( Timeout> 0x7fffffff ) {
+ return FALSE;
+ }
+
+ //
+ // Compute the elapsed time since we last authenticated
+ //
+
+ NtQuerySystemTime( &TimeNow );
+ ElapsedTime.QuadPart = TimeNow.QuadPart - StartTime.QuadPart;
+
+ //
+ // Compute Period from milliseconds into 100ns units.
+ //
+
+ Period = RtlEnlargedIntegerMultiply( (LONG) Timeout, 10000 );
+
+
+ //
+ // If the elapsed time is negative (totally bogus) or greater than the
+ // maximum allowed, indicate the session should be reauthenticated.
+ //
+
+ if ( ElapsedTime.QuadPart < 0 || ElapsedTime.QuadPart > Period.QuadPart ) {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+BOOLEAN
+NlTimeToReauthenticate(
+ IN PCLIENT_SESSION ClientSession
+ )
+/*++
+
+Routine Description:
+
+ Determine if it is time to reauthenticate this Client Session.
+ To reduce the number of re-authentication attempts, we try
+ to re-authenticate only on demand and then only at most every 45
+ seconds.
+
+Arguments:
+
+ ClientSession - Structure used to define the session.
+
+Return Value:
+
+ TRUE -- iff it is time to re-authenticate
+
+--*/
+{
+ BOOLEAN ReturnBoolean;
+
+ EnterCriticalSection( &NlGlobalDcDiscoveryCritSect );
+ ReturnBoolean = NlTimeHasElapsed(
+ ClientSession->CsLastAuthenticationTry,
+ ClientSession->CsSecureChannelType == WorkstationSecureChannel ?
+ MAX_WKSTA_AUTHENTICATION_WAIT :
+ MAX_DC_AUTHENTICATION_WAIT );
+ LeaveCriticalSection( &NlGlobalDcDiscoveryCritSect );
+
+ return ReturnBoolean;
+}
+
+
+NTSTATUS
+NlNewSessionSetup(
+ IN LPWSTR primary
+ )
+/*++
+
+Routine Description:
+
+ Set up a session with a "new/different" primary. This routine
+ does what NlSessionSetup does, but also does the following:
+
+ * Coordinate with the replicator thread to ensure we don't remove
+ the current session out from under it.
+
+ * Set the Global indicating the name of the primary.
+
+ * Mark that a "partial sync" is required if this primary is different from
+ the previous primary. (The replicator thread may later decide to
+ do a full sync if the PDC is an NT 1.0 PDC.)
+
+Arguments:
+
+ primary - ptr to name of primary domain controller.
+
+Return Value:
+
+ Status of operation.
+
+--*/
+{
+ NTSTATUS Status;
+
+ //
+ // If we already have a session setup to the primary in question,
+ // don't bother doing it again.
+ //
+
+ if ( NlGlobalClientSession->CsState == CS_AUTHENTICATED &&
+ NlNameCompare( primary,
+ NlGlobalUnicodePrimaryName,
+ NAMETYPE_COMPUTER ) == 0 ) {
+
+ return STATUS_SUCCESS;
+
+ }
+
+
+ //
+ // Ask the replicator to terminate. Wait for it to do so.
+ // Don't allow the replicator to be started until we're done.
+ //
+
+ EnterCriticalSection( &NlGlobalReplicatorCritSect );
+
+ NlStopReplicator();
+
+ //
+ // Become a Writer of the ClientSession.
+ //
+ // Only wait for 10 seconds. This routine is called by the netlogon main
+ // thread. Another thread may have the ClientSession locked and need the
+ // main thread to finish a discovery.
+ //
+
+ if ( !NlTimeoutSetWriterClientSession( NlGlobalClientSession, 10 * 1000 ) ) {
+ LeaveCriticalSection( &NlGlobalReplicatorCritSect );
+
+ NlPrint((NL_CRITICAL,
+ "NlNewSessionSetup: " FORMAT_LPWSTR
+ ": cannot become writer of client session.\n",
+ primary ));
+
+ return STATUS_NO_LOGON_SERVERS;
+ }
+
+ //
+ // Drop the previous session and forget the old primary name.
+ //
+
+ NlSetStatusClientSession( NlGlobalClientSession, STATUS_NO_LOGON_SERVERS );
+
+ //
+ // Remember this new primary name
+ //
+
+ if ( !NlSetPrimaryName( primary ) ) {
+ NlResetWriterClientSession( NlGlobalClientSession );
+ LeaveCriticalSection( &NlGlobalReplicatorCritSect );
+ return STATUS_NO_MEMORY;
+ }
+
+ //
+ // Setup the session.
+ //
+ Status = NlSessionSetup( NlGlobalClientSession );
+
+ NlResetWriterClientSession( NlGlobalClientSession );
+
+ LeaveCriticalSection( &NlGlobalReplicatorCritSect );
+ return Status;
+
+}
+
+
+NTSTATUS
+NlAuthenticate(
+ IN LPWSTR AccountName,
+ IN NETLOGON_SECURE_CHANNEL_TYPE SecureChannelType,
+ IN LPWSTR ComputerName,
+ IN PNETLOGON_CREDENTIAL ClientCredential,
+ OUT PNETLOGON_CREDENTIAL ServerCredential,
+ IN ULONG NegotiatedFlags
+ )
+/*++
+
+Routine Description:
+
+ Authenticate the specified user using the specified credentials.
+ If the credentials match, return a ServerCredential to the caller so
+ the caller can be assured that we know the Session Key.
+
+ Previously, and entry must have been placed in the LogonTable for this
+ session. The LogonTable entry must contain the ClientChallenge and the
+ ServerChallenge used to compute the session key.
+
+ If this authentication attempt fails, the LogonTable entry is deleted.
+
+Arguments:
+
+ AccountName - Name of the account to authenticate with.
+
+ SecureChannelType - The type of the account being accessed.
+
+ ComputerName - The name of the workstation from which logon is occurring.
+
+ ClientCredential -- results of a function performed on
+ ClientChallenge using the account's password.
+
+ ServerCredential -- Receives the results of a similar
+ operation performed by the logon server on the ServerChallenge.
+
+ NegotiatedFlags -- Capabilities supported by both client and server.
+
+Return Value:
+
+ Status of operation.
+
+--*/
+{
+ NTSTATUS Status;
+ PSERVER_SESSION ServerSession;
+ NETLOGON_CREDENTIAL LocalClientCredential;
+
+ SAMPR_HANDLE UserHandle = NULL;
+ PSAMPR_USER_INFO_BUFFER UserPasswords = NULL;
+
+ NT_OWF_PASSWORD OwfPassword;
+ NETLOGON_CREDENTIAL ServerChallenge;
+
+ WCHAR LocalComputerName[CNLEN+1+1]; // '$' plus trailing '\0'
+
+
+ //
+ // Build the name of the computer trying to connect.
+ // If this is a BDC session from an emulated Cairo domain,
+ // the ComputerName will be the "real" computer name,
+ // and the emulated computer name is a function of the AccountName.
+ // So, always use the AccountName for BDC sessions.
+ //
+
+ if ( SecureChannelType == ServerSecureChannel ) {
+ wcsncpy( LocalComputerName, AccountName, CNLEN+1);
+ LocalComputerName[CNLEN+1] = L'\0';
+
+ // Ditch the trailing '$'
+ LocalComputerName[wcslen(LocalComputerName)-1] = L'\0';
+ } else {
+ wcsncpy( LocalComputerName, ComputerName, CNLEN+1);
+ LocalComputerName[CNLEN] = L'\0';
+ }
+
+ //
+ // we need to retrieve the original challenge supplied by Workstation
+ //
+
+ LOCK_SERVER_SESSION_TABLE();
+ ServerSession = NlFindNamedServerSession( LocalComputerName );
+ if (ServerSession == NULL) {
+ UNLOCK_SERVER_SESSION_TABLE();
+ NlPrint((NL_CRITICAL,
+ "NlAuthenticate: Can't NlFindNamedServerSession " FORMAT_LPWSTR "\n",
+ LocalComputerName ));
+ return STATUS_ACCESS_DENIED;
+ }
+
+ //
+ // If the caller claims to be a BDC,
+ // make sure he has a BDC account.
+ //
+ // This shouldn't happen in reality, but other sections of code rely on
+ // the secure channel type matching the SS_BDC bit.
+ //
+
+ if ( IS_BDC_CHANNEL( SecureChannelType) ) {
+ if ((ServerSession->SsFlags & SS_BDC) == 0 ) {
+ UNLOCK_SERVER_SESSION_TABLE();
+ NlPrint((NL_CRITICAL,
+ "NlAuthenticate: BDC connecting on non-BDC channel " FORMAT_LPWSTR "\n",
+ ComputerName ));
+ return STATUS_ACCESS_DENIED;
+ }
+ } else {
+ if ( ServerSession->SsFlags & SS_BDC ) {
+ LPWSTR MsgStrings[4];
+ UNLOCK_SERVER_SESSION_TABLE();
+
+ NlPrint((NL_CRITICAL,
+ "NlAuthenticate: non-BDC connecting on BDC channel " FORMAT_LPWSTR "\n",
+ ComputerName ));
+ //
+ // This can actually occur if a machine has a BDC account and
+ // then is later converted a DC in another domain. So, give an
+ // explicit message for this problem.
+ //
+ MsgStrings[0] = NlGlobalUnicodeComputerName;
+ MsgStrings[1] = ComputerName;
+ MsgStrings[2] = NlGlobalUnicodeDomainName;
+ MsgStrings[3] = AccountName;
+
+ NlpWriteEventlog (NELOG_NetlogonSessionTypeWrong,
+ EVENTLOG_ERROR_TYPE,
+ NULL,
+ 0,
+ MsgStrings,
+ 4 );
+ return STATUS_ACCESS_DENIED;
+ }
+ }
+
+
+ //
+ // Prevent entry from being deleted, but drop the global lock.
+ //
+ // Beware of server with two concurrent calls outstanding
+ // (must have rebooted.)
+ //
+
+ if (ServerSession->SsFlags & SS_LOCKED ) {
+ UNLOCK_SERVER_SESSION_TABLE();
+ NlPrint((NL_CRITICAL,
+ "NlAuthenticate: session locked " FORMAT_LPWSTR "\n",
+ ComputerName ));
+ return STATUS_ACCESS_DENIED;
+ }
+ ServerSession->SsFlags |= SS_LOCKED;
+ ServerSession->SsSecureChannelType = SecureChannelType;
+
+ UNLOCK_SERVER_SESSION_TABLE();
+
+ //
+ // Figure out what transport this connection came in on so we can send out
+ // mailslot messages only on the particular transport.
+ // Don't do it for workstations. We don't send mailslot messages there.
+ //
+
+ if ( ServerSession->SsFlags & SS_BDC ) {
+ //
+ // Don't use 'LocalComputerName'. For Cairo emulated BDCs, that
+ // machine doesn't have a session to us.
+ //
+ ServerSession->SsTransportName = NlTransportLookup( ComputerName );
+ }
+
+ //
+ // Get the encrypted password from SAM.
+ //
+
+ Status = NlSamOpenNamedUser( AccountName, &UserHandle, NULL );
+
+ if ( !NT_SUCCESS(Status) ) {
+ NlPrint((NL_CRITICAL,
+ "NlAuthenticate: Cannot NlSamOpenNamedUser " FORMAT_LPWSTR "\n",
+ AccountName ));
+ goto Cleanup;
+ }
+
+
+ Status = SamrQueryInformationUser(
+ UserHandle,
+ UserInternal1Information,
+ &UserPasswords );
+
+ if (!NT_SUCCESS(Status)) {
+ NlPrint((NL_CRITICAL,
+ "NlAuthenticate: Cannot SamrQueryInformationUser " FORMAT_LPWSTR "\n",
+ AccountName ));
+ UserPasswords = NULL;
+ goto Cleanup;
+ }
+
+ //
+ // If the authentication is from an NT client,
+ // use the NT OWF Password,
+ // otherwise, use the LM OWF password.
+ //
+
+ if ( SecureChannelType == UasServerSecureChannel ) {
+ if ( !UserPasswords->Internal1.LmPasswordPresent ) {
+ NlPrint((NL_CRITICAL,
+ "NlAuthenticate: No LM Password for " FORMAT_LPWSTR "\n",
+ AccountName ));
+
+ Status = STATUS_ACCESS_DENIED;
+ goto Cleanup;
+ }
+
+ RtlCopyMemory( &OwfPassword,
+ &UserPasswords->Internal1.EncryptedLmOwfPassword,
+ sizeof(OwfPassword) );
+ } else {
+ if ( UserPasswords->Internal1.NtPasswordPresent ) {
+
+ RtlCopyMemory( &OwfPassword,
+ &UserPasswords->Internal1.EncryptedNtOwfPassword,
+ sizeof(OwfPassword) );
+
+ // Allow for the case the the account has no password at all.
+ } else if ( !UserPasswords->Internal1.LmPasswordPresent ) {
+ UNICODE_STRING TempUnicodeString;
+
+ RtlInitUnicodeString(&TempUnicodeString, NULL);
+ Status = RtlCalculateNtOwfPassword(&TempUnicodeString,
+ &OwfPassword);
+
+ } else {
+
+ NlPrint((NL_CRITICAL,
+ "NlAuthenticate: No NT Password for " FORMAT_LPWSTR "\n",
+ AccountName ));
+
+ Status = STATUS_ACCESS_DENIED;
+ goto Cleanup;
+ }
+ }
+
+
+
+ //
+ // Actually compute the session key given the two challenges and the
+ // password.
+ //
+
+ RtlCopyMemory( &ServerChallenge,
+ &ServerSession->SsSessionKey,
+ sizeof(ServerChallenge) );
+
+#ifdef BAD_ALIGNMENT
+ NlPrint((NL_CHALLENGE_RES,"NlAuthenticate: Password = %lx %lx %lx %lx\n",
+ ((DWORD *) (&OwfPassword))[0],
+ ((DWORD *) (&OwfPassword))[1],
+ ((DWORD *) (&OwfPassword))[2],
+ ((DWORD *) (&OwfPassword))[3]));
+
+ NlPrint((NL_CHALLENGE_RES,"NlAuthenticate: ClientChallenge = %lx %lx\n",
+ ((DWORD *) (&ServerSession->SsAuthenticationSeed))[0],
+ ((DWORD *) (&ServerSession->SsAuthenticationSeed))[1]));
+
+ NlPrint((NL_CHALLENGE_RES,"NlAuthenticate: ServerChallenge = %lx %lx\n",
+ ((DWORD *) (&ServerChallenge))[0],
+ ((DWORD *) (&ServerChallenge))[1]));
+#endif // BAD_ALIGNMENT
+
+
+ //
+ // Actually compute the session key given the two challenges and the
+ // password.
+ //
+
+ NlMakeSessionKey(
+ &OwfPassword,
+ &ServerSession->SsAuthenticationSeed,
+ &ServerChallenge,
+ &ServerSession->SsSessionKey );
+
+
+#ifdef BAD_ALIGNMENT
+ NlPrint((NL_CHALLENGE_RES,"NlAuthenticate: SessionKey = %lx %lx %lx %lx\n",
+ ((DWORD *) (&ServerSession->SsSessionKey))[0],
+ ((DWORD *) (&ServerSession->SsSessionKey))[1],
+ ((DWORD *) (&ServerSession->SsSessionKey))[2],
+ ((DWORD *) (&ServerSession->SsSessionKey))[3]));
+#endif // BAD_ALIGNMENT
+
+
+ //
+ // Compute ClientCredential to verify the one supplied by ComputerName
+ //
+
+ NlComputeCredentials( &ServerSession->SsAuthenticationSeed,
+ &LocalClientCredential,
+ &ServerSession->SsSessionKey);
+
+
+#ifdef BAD_ALIGNMENT
+ NlPrint((NL_CHALLENGE_RES,"NlAuthenticate: ClientCredential GOT = %lx %lx\n",
+ ((DWORD *) (ClientCredential))[0],
+ ((DWORD *) (ClientCredential))[1]));
+
+
+ NlPrint((NL_CHALLENGE_RES,"NlAuthenticate: ClientCredential MADE = %lx %lx\n",
+ ((DWORD *) (&LocalClientCredential))[0],
+ ((DWORD *) (&LocalClientCredential))[1]));
+#endif // BAD_ALIGNMENT
+
+
+ //
+ // verify the computed credentials with those supplied
+ //
+
+ if( RtlCompareMemory( ClientCredential,
+ &LocalClientCredential,
+ sizeof(LocalClientCredential)) !=
+ sizeof(LocalClientCredential)) {
+ Status = STATUS_ACCESS_DENIED;
+ goto Cleanup;
+ }
+
+ RtlCopyMemory( &ServerSession->SsAuthenticationSeed,
+ &LocalClientCredential,
+ sizeof(LocalClientCredential));
+
+ //
+ // Compute ServerCredential from ServerChallenge to be returned to caller
+ //
+
+ NlComputeCredentials( &ServerChallenge,
+ ServerCredential,
+ &ServerSession->SsSessionKey );
+
+
+#ifdef BAD_ALIGNMENT
+ NlPrint((NL_CHALLENGE_RES,"NlAuthenticate: ServerCredential SEND = %lx %lx\n",
+ ((DWORD *) (ServerCredential))[0],
+ ((DWORD *) (ServerCredential))[1]));
+#endif // BAD_ALIGNMENT
+
+
+ Status = STATUS_SUCCESS;
+
+Cleanup:
+
+ //
+ // Allow the entry to disappear.
+ //
+
+ LOCK_SERVER_SESSION_TABLE();
+ if ( NT_SUCCESS( Status ) ) {
+ ServerSession->SsFlags |= SS_AUTHENTICATED;
+ ServerSession->SsNegotiatedFlags = NegotiatedFlags;
+
+ //
+ // If this is a NT BDC,
+ // force it to do a partial sync from us so we can find out
+ // the serial numbers of each of its databases.
+ //
+ // This is especially important for NT 1.0 BDCs which need this
+ // "encouragement" when it reboots. It is probably good on NT 1.0a
+ // BDCs since setting up a secure channel only happens at startup
+ // (in which case it is already going to make the calls) or after
+ // some error condition (in which case the increased paranoia is
+ // is good thing).
+ //
+
+ if ( SecureChannelType == ServerSecureChannel ) {
+ ServerSession->SsFlags |= SS_REPL_MASK;
+ }
+ }
+
+ ServerSession->SsFlags &= ~SS_CHALLENGE;
+ UNLOCK_SERVER_SESSION_TABLE();
+
+ NlUnlockServerSession( ServerSession );
+
+ //
+ // Delete the ServerSession upon error
+ //
+
+ if ( !NT_SUCCESS( Status ) ) {
+ NlFreeNamedServerSession( LocalComputerName, FALSE );
+ }
+
+ //
+ // Free locally used resources.
+ //
+
+ if ( UserPasswords != NULL ) {
+ SamIFree_SAMPR_USER_INFO_BUFFER( UserPasswords,
+ UserInternal1Information);
+ }
+
+ if ( UserHandle != NULL ) {
+ SamrCloseHandle( &UserHandle );
+ }
+
+ return Status;
+}
+
+
+
+
+NET_API_STATUS
+NlCreateShare(
+ LPWSTR SharePath,
+ LPWSTR ShareName
+ )
+/*++
+
+Routine Description:
+
+ Share the netlogon scripts directory.
+
+Arguments:
+
+ SharePath - Path that the new share should be point to.
+
+ ShareName - Name of the share.
+
+Return Value:
+
+ TRUE: if successful
+ FALSE: if error (NlExit was called)
+
+--*/
+{
+ NTSTATUS Status;
+ NET_API_STATUS NetStatus;
+ SHARE_INFO_502 ShareInfo502;
+
+ WORD AnsiSize;
+ CHAR AnsiRemark[NNLEN+1];
+ TCHAR Remark[NNLEN+1];
+
+ ACE_DATA AceData[] = {
+ {ACCESS_ALLOWED_ACE_TYPE, 0, 0,
+ GENERIC_EXECUTE | GENERIC_READ, &WorldSid}
+ };
+
+
+ //
+ // Build the structure describing the share.
+ //
+
+ ShareInfo502.shi502_path = SharePath;
+ ShareInfo502.shi502_security_descriptor = NULL;
+
+ NlPrint((NL_INIT, "Path to be shared is " FORMAT_LPWSTR "\n",
+ SharePath));
+
+ NetStatus = (NET_API_STATUS) DosGetMessage(
+ NULL, // No insertion strings
+ 0, // No insertion strings
+ AnsiRemark,
+ sizeof(AnsiRemark),
+ MTXT_LOGON_SRV_SHARE_REMARK,
+ MESSAGE_FILENAME,
+ &AnsiSize );
+
+ if ( NetStatus == NERR_Success ) {
+ NetpCopyStrToTStr( Remark, AnsiRemark );
+ ShareInfo502.shi502_remark = Remark;
+ } else {
+ ShareInfo502.shi502_remark = TEXT( "" );
+ }
+
+ ShareInfo502.shi502_netname = ShareName;
+ ShareInfo502.shi502_type = STYPE_DISKTREE;
+ ShareInfo502.shi502_permissions = ACCESS_READ;
+ ShareInfo502.shi502_max_uses = 0xffffffff;
+ ShareInfo502.shi502_passwd = TEXT("");
+
+ //
+ // Set the security descriptor on the share
+ //
+
+ //
+ // Create a security descriptor containing the DACL.
+ //
+
+ Status = NetpCreateSecurityDescriptor(
+ AceData,
+ (sizeof(AceData)/sizeof(AceData[0])),
+ NULL, // Default the owner Sid
+ NULL, // Default the primary group
+ &ShareInfo502.shi502_security_descriptor );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ NlPrint((NL_CRITICAL,
+ FORMAT_LPWSTR ": Cannot create security descriptor 0x%lx\n",
+ SharePath, Status ));
+
+ NetStatus = NetpNtStatusToApiStatus( Status );
+ return NetStatus;
+ }
+
+
+ //
+ // Create the share.
+ //
+
+ NetStatus = NetShareAdd(NULL, 502, (LPBYTE) &ShareInfo502, NULL);
+
+ if (NetStatus == NERR_DuplicateShare) {
+
+ PSHARE_INFO_2 ShareInfo2 = NULL;
+
+ NlPrint((NL_INIT, "The netlogon share (" FORMAT_LPWSTR
+ ") already exists. \n", ShareName));
+
+ //
+ // check to see the shared path is same.
+ //
+
+ NetStatus = NetShareGetInfo( NULL,
+ NETLOGON_SCRIPTS_SHARE,
+ 2,
+ (LPBYTE *) &ShareInfo2 );
+
+ if ( NetStatus == NERR_Success ) {
+
+ //
+ // compare path names.
+ //
+ // Note: NlGlobalUnicodeScriptPath is path canonicalized already.
+ //
+ //
+
+ NlPrint((NL_INIT, "The netlogon share current path is "
+ FORMAT_LPWSTR "\n", SharePath));
+
+ if( NetpwPathCompare(
+ NlGlobalUnicodeScriptPath,
+ ShareInfo2->shi2_path, 0, 0 ) != 0 ) {
+
+ //
+ // delete share.
+ //
+
+ NetStatus = NetShareDel( NULL, NETLOGON_SCRIPTS_SHARE, 0);
+
+ if( NetStatus == NERR_Success ) {
+
+ //
+ // Recreate share.
+ //
+
+ NetStatus = NetShareAdd(NULL,
+ 502,
+ (LPBYTE) &ShareInfo502,
+ NULL);
+
+ if( NetStatus == NERR_Success ) {
+
+ NlPrint((NL_INIT, "The netlogon share ("
+ FORMAT_LPWSTR ") is recreated with new path "
+ FORMAT_LPWSTR "\n",
+ ShareName, SharePath ));
+ }
+
+ }
+ }
+ }
+
+ if( ShareInfo2 != NULL ) {
+ NetpMemoryFree( ShareInfo2 );
+ }
+ }
+
+ //
+ // Free the security descriptor
+ //
+
+ NetpMemoryFree( ShareInfo502.shi502_security_descriptor );
+
+
+ if ( NetStatus != NERR_Success ) {
+
+ NlPrint((NL_CRITICAL,
+ "Error %lu attempting to create-share " FORMAT_LPWSTR "\n",
+ NetStatus, ShareName ));
+ return NetStatus;
+
+ }
+
+ return NERR_Success;
+}
+
+
+
+NTSTATUS
+NlSamOpenNamedUser(
+ IN LPWSTR UserName,
+ OUT SAMPR_HANDLE *UserHandle OPTIONAL,
+ OUT PULONG UserId OPTIONAL
+ )
+/*++
+
+Routine Description:
+
+ Utility routine to open a Sam user given the username.
+
+Arguments:
+
+ UserName - Name of user to open
+
+ UserHandle - Optionally returns a handle to the opened user.
+
+ UserId - Optionally returns the relative ID of the opened user.
+
+Return Value:
+
+--*/
+{
+ NTSTATUS Status;
+
+ SAMPR_ULONG_ARRAY RelativeIdArray;
+ SAMPR_ULONG_ARRAY UseArray;
+ RPC_UNICODE_STRING UserNameString;
+ SAMPR_HANDLE LocalUserHandle = NULL;
+
+ //
+ // Convert the user name to a RelativeId.
+ //
+
+ RtlInitUnicodeString( (PUNICODE_STRING)&UserNameString, UserName );
+ RelativeIdArray.Count = 1;
+ RelativeIdArray.Element = NULL;
+ UseArray.Count = 1;
+ UseArray.Element = NULL;
+
+ Status = SamrLookupNamesInDomain(
+ NlGlobalDBInfoArray[SAM_DB].DBHandle,
+ 1,
+ &UserNameString,
+ &RelativeIdArray,
+ &UseArray );
+
+ if ( !NT_SUCCESS(Status) ) {
+ RelativeIdArray.Element = NULL;
+ UseArray.Element = NULL;
+ if ( Status == STATUS_NONE_MAPPED ) {
+ Status = STATUS_NO_SUCH_USER;
+ }
+ goto Cleanup;
+ }
+
+ //
+ // we should get back exactly one entry of info back.
+ //
+
+ NlAssert( UseArray.Count == 1 );
+ NlAssert( RelativeIdArray.Count == 1 );
+ NlAssert( UseArray.Element != NULL );
+ NlAssert( RelativeIdArray.Element != NULL );
+
+ if ( UseArray.Element[0] != SidTypeUser ) {
+ Status = STATUS_NO_SUCH_USER;
+ goto Cleanup;
+ }
+
+ //
+ // Open the user
+ //
+
+ if ( UserHandle != NULL ) {
+ Status = SamrOpenUser( NlGlobalDBInfoArray[SAM_DB].DBHandle,
+ 0, // No desired access
+ RelativeIdArray.Element[0],
+ &LocalUserHandle );
+
+ if ( !NT_SUCCESS(Status) ) {
+ LocalUserHandle = NULL;
+ goto Cleanup;
+ }
+ }
+
+ //
+ // Free locally used resources.
+ //
+
+Cleanup:
+
+ //
+ // Return information to the caller.
+ //
+
+ if ( NT_SUCCESS(Status) ) {
+ if ( UserHandle != NULL ) {
+ *UserHandle = LocalUserHandle;
+ LocalUserHandle = NULL;
+ }
+
+ if ( UserId != NULL ) {
+ *UserId = RelativeIdArray.Element[0];
+ }
+
+ }
+
+ //
+ // Close the user handle if we don't need it returned.
+ //
+
+ if ( LocalUserHandle != NULL ) {
+ SamrCloseHandle( &LocalUserHandle );
+ }
+
+ //
+ // Free locally used resources.
+ //
+
+ SamIFree_SAMPR_ULONG_ARRAY( &RelativeIdArray );
+ SamIFree_SAMPR_ULONG_ARRAY( &UseArray );
+
+ return Status;
+
+}
+
+
+
+NTSTATUS
+NlChangePassword(
+ PCLIENT_SESSION ClientSession
+ )
+/*++
+
+Routine Description:
+
+ Change this machine's password at the primary.
+ Also update password locally if the call succeeded.
+
+ To determine if the password of "machine account"
+ needs to be changed. If the password is older than
+ 7 days then it must be changed asap. We will defer
+ changing the password if we know before hand that
+ primary dc is down since our call will fail anyway.
+
+Arguments:
+
+ ClientSession - Structure describing the session to change the password
+ for. The specified structure must be referenced.
+
+Return Value:
+
+ NT Status code
+
+--*/
+{
+ NTSTATUS Status;
+ NETLOGON_AUTHENTICATOR OurAuthenticator;
+ NETLOGON_AUTHENTICATOR ReturnAuthenticator;
+
+ LM_OWF_PASSWORD OwfPassword;
+ ENCRYPTED_LM_OWF_PASSWORD SessKeyEncrPassword;
+
+ LSAPR_HANDLE SecretHandle = NULL;
+ PLSAPR_CR_CIPHER_VALUE CrCurrentPassword = NULL;
+ LARGE_INTEGER CurrentPasswordTime;
+ PLSAPR_CR_CIPHER_VALUE CrPreviousPassword = NULL;
+
+ CHAR ClearTextPassword[LM_OWF_PASSWORD_LENGTH];
+ LSAPR_CR_CIPHER_VALUE CrClearTextPasswordString;
+ UNICODE_STRING ClearTextPasswordString;
+
+ BOOL PasswordChangedOnServer = FALSE;
+ BOOL LsaSecretChanged = FALSE;
+ BOOL DefaultPasswordBeingChanged = FALSE;
+
+ //
+ // Initialization
+ //
+
+ NlAssert( ClientSession->CsReferenceCount > 0 );
+
+ //
+ // If the password change was refused by the DC,
+ // Don't ever try to change the password again (until the next reboot).
+ //
+ // This could have been written to try every MAX_SSI_PWAGE. However,
+ // that gets complex if you take into consideration the CS_UPDATE_PASSWORD
+ // case where the time stamp on the LSA Secret doesn't get changed.
+ //
+
+ LOCK_TRUST_LIST();
+ if ( ClientSession->CsFlags & CS_PASSWORD_REFUSED ) {
+ UNLOCK_TRUST_LIST();
+ return STATUS_SUCCESS;
+ }
+ UNLOCK_TRUST_LIST();
+
+
+ //
+ // If the replicator thread is running, do nothing.
+ //
+ // The replicator will be talking to the PDC over the secure channel
+ // and we'd rather not disturb it. In theory we could change the
+ // password out from under the replication (the replicator appropriately
+ // becomes a writer of the ClientSession). However, a replication
+ // is important enough that we'd rather not take a chance that the
+ // recovery logic below might drop the secure channel.
+ //
+ // We only do a spot check here since we don't want to keep the
+ // NlGlobalReplicatorCritSect locked across a session setup. Since
+ // session setup might do a discovery, we'll end up deadlocking when that
+ // discovery then tries to start the replicator thread.
+ //
+
+ if ( NlGlobalRole == RoleBackup) {
+
+ EnterCriticalSection( &NlGlobalReplicatorCritSect );
+
+ if ( IsReplicatorRunning() ) {
+ LeaveCriticalSection( &NlGlobalReplicatorCritSect );
+ return STATUS_SUCCESS;
+ }
+
+ LeaveCriticalSection( &NlGlobalReplicatorCritSect );
+ }
+
+
+
+ //
+ // Become a writer of the ClientSession.
+ //
+
+ if ( !NlTimeoutSetWriterClientSession( ClientSession, WRITER_WAIT_PERIOD ) ) {
+ NlPrint((NL_CRITICAL, "NlChangePassword: Can't become writer of client session.\n" ));
+ return STATUS_NO_LOGON_SERVERS;
+ }
+
+
+ //
+ // Determine the time the password was last changed
+ //
+
+ Status = NlOpenSecret(
+ ClientSession,
+ SECRET_QUERY_VALUE | SECRET_SET_VALUE,
+ &SecretHandle );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ NlPrint((NL_CRITICAL,
+ "NlChangePassword: %wZ: Cannot LsarOpenSecret %lX\n",
+ &ClientSession->CsDomainName,
+ Status));
+ goto Cleanup;
+ }
+
+ Status = LsarQuerySecret(
+ SecretHandle,
+ &CrCurrentPassword,
+ &CurrentPasswordTime,
+ &CrPreviousPassword,
+ NULL );
+
+ if ( !NT_SUCCESS(Status) ) {
+ NlPrint((NL_CRITICAL,
+ "NlChangePassword: %wZ: Cannot LsarQuerySecret %lX\n",
+ &ClientSession->CsDomainName,
+ Status));
+ goto Cleanup;
+ }
+
+
+ //
+ // If the (old or new) password is still the default password
+ // (lower case computer name),
+ // or the password is null (a convenient default for domain trust),
+ // Flag that fact.
+ //
+
+ if ( CrCurrentPassword == NULL ) {
+ CrClearTextPasswordString.Buffer = NULL;
+ CrClearTextPasswordString.Length = 0;
+ CrClearTextPasswordString.MaximumLength = 0;
+ } else {
+ CrClearTextPasswordString = *CrCurrentPassword;
+ }
+
+ ClearTextPasswordString.Buffer = (LPWSTR)CrClearTextPasswordString.Buffer;
+ ClearTextPasswordString.Length = (USHORT)CrClearTextPasswordString.Length;
+ ClearTextPasswordString.MaximumLength = (USHORT)CrClearTextPasswordString.MaximumLength;
+
+ if ( ClearTextPasswordString.Length == 0 ||
+ RtlEqualComputerName( &NlGlobalUnicodeComputerNameString,
+ &ClearTextPasswordString ) ) {
+ DefaultPasswordBeingChanged = TRUE;
+ NlPrint((NL_SESSION_SETUP,
+ "NlChangePassword: %wZ: New LsaSecret is default value.\n",
+ &ClientSession->CsDomainName ));
+
+ } else if ( CrPreviousPassword == NULL || CrPreviousPassword->Length == 0 ) {
+
+ DefaultPasswordBeingChanged = TRUE;
+ NlPrint((NL_SESSION_SETUP,
+ "NlChangePassword: %wZ: Old LsaSecret is NULL.\n",
+ &ClientSession->CsDomainName ));
+ } else {
+ UNICODE_STRING PreviousClearTextPasswordString;
+
+ PreviousClearTextPasswordString.Buffer = (LPWSTR)CrPreviousPassword->Buffer;
+ PreviousClearTextPasswordString.Length = (USHORT)CrPreviousPassword->Length;
+ PreviousClearTextPasswordString.MaximumLength = (USHORT)CrPreviousPassword->MaximumLength;
+
+ if ( RtlEqualComputerName( &NlGlobalUnicodeComputerNameString,
+ &PreviousClearTextPasswordString ) ) {
+ DefaultPasswordBeingChanged = TRUE;
+ NlPrint((NL_SESSION_SETUP,
+ "NlChangePassword: %wZ: Old LsaSecret is default value.\n",
+ &ClientSession->CsDomainName ));
+ }
+ }
+
+
+ //
+ // If the password has not yet expired,
+ // and the password is not the default,
+ // just return.
+ //
+
+ LOCK_TRUST_LIST();
+ if ( (ClientSession->CsFlags & CS_UPDATE_PASSWORD) == 0 &&
+ !NlTimeHasElapsed( CurrentPasswordTime, MAX_SSI_PWAGE ) &&
+ !DefaultPasswordBeingChanged ) {
+
+ UNLOCK_TRUST_LIST();
+ Status = STATUS_SUCCESS;
+ goto Cleanup;
+ }
+ UNLOCK_TRUST_LIST();
+
+
+ //
+ // If the session isn't authenticated,
+ // do so now.
+ //
+ // We're careful to not force this authentication unless the password
+ // needs to be changed.
+ //
+ if ( ClientSession->CsState != CS_AUTHENTICATED ) {
+
+ //
+ // If we've tried to authenticate recently,
+ // don't bother trying again.
+ //
+
+ if ( !NlTimeToReauthenticate( ClientSession ) ) {
+ Status = ClientSession->CsConnectionStatus;
+ goto Cleanup;
+
+ }
+
+ //
+ // Try to set up the session.
+ //
+
+ Status = NlSessionSetup( ClientSession );
+
+ if ( !NT_SUCCESS(Status) ) {
+ goto Cleanup;
+ }
+ }
+
+
+ //
+ // Once we change the password in LsaSecret storage,
+ // all future attempts to change the password should use the value
+ // from LsaSecret storage. The secure channel is using the old
+ // value of the password.
+ //
+
+ LOCK_TRUST_LIST();
+ if (ClientSession->CsFlags & CS_UPDATE_PASSWORD) {
+ NlPrint((NL_SESSION_SETUP,
+ "NlChangePassword: %wZ: Password already updated in secret\n",
+ &ClientSession->CsDomainName ));
+
+ //
+ // Handle the case where LsaSecret storage has not yet been updated.
+ //
+
+ } else {
+ NETLOGON_CREDENTIAL tmp;
+ PUSHORT p;
+ PUSHORT op;
+ USHORT i;
+
+
+ //
+ // Build a new clear text password using:
+ // 1) the current credentials (Some function of the old password)
+ // 2) the time of day.
+ // 3) a random number.
+ //
+
+ tmp = ClientSession->CsAuthenticationSeed;
+ RtlZeroMemory( ClearTextPassword, sizeof(ClearTextPassword));
+ p = (PUSHORT) &tmp;
+ op = (PUSHORT) ClearTextPassword;
+
+ for (i = 0; i < sizeof(ClearTextPassword)/sizeof(*op); i++) {
+ LARGE_INTEGER TimeNow;
+ srand(*p);
+ NtQuerySystemTime( &TimeNow );
+ *op = rand() + (USHORT)TimeNow.LowPart;
+ // Srvmgr later uses this password as a zero terminated unicode string
+ if ( *op == 0 ) {
+ *op=1;
+ }
+ p++;
+ op++;
+ }
+
+ ClearTextPasswordString.Buffer =
+ (LPWSTR)(CrClearTextPasswordString.Buffer =
+ (PUCHAR)ClearTextPassword);
+
+ ClearTextPasswordString.Length =
+ (USHORT)(CrClearTextPasswordString.Length =
+ sizeof(ClearTextPassword));
+
+ ClearTextPasswordString.MaximumLength =
+ (USHORT)(CrClearTextPasswordString.MaximumLength =
+ CrClearTextPasswordString.Length);
+
+ //
+ // Save this new cleartext password in LsaSecret storage.
+ //
+ // Set the OldValue to the perviously obtained CurrentValue.
+ //
+
+ Status = LsarSetSecret(
+ SecretHandle,
+ &CrClearTextPasswordString,
+ CrCurrentPassword );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ NlPrint((NL_CRITICAL,
+ "NlChangePassword: %wZ: Cannot LsarSetSecret %lX\n",
+ &ClientSession->CsDomainName,
+ Status));
+ UNLOCK_TRUST_LIST();
+ goto Cleanup;
+ }
+
+ //
+ // Flag that we've updated the password in LsaSecret storage.
+ //
+
+ LsaSecretChanged = TRUE;
+ ClientSession->CsFlags |= CS_UPDATE_PASSWORD;
+ NlPrint((NL_SESSION_SETUP,
+ "NlChangePassword: %wZ: Flag password changed in LsaSecret\n",
+ &ClientSession->CsDomainName ));
+
+ }
+ UNLOCK_TRUST_LIST();
+
+
+ //
+ // Perform the initial encryption.
+ //
+
+ Status = RtlCalculateNtOwfPassword( &ClearTextPasswordString, &OwfPassword);
+
+ if ( !NT_SUCCESS( Status )) {
+ NlPrint((NL_CRITICAL,
+ "NlChangePassword: %wZ: Cannot RtlCalculateNtOwfPassword %lX\n",
+ &ClientSession->CsDomainName,
+ Status));
+ goto Cleanup;
+ }
+
+ //
+ // Encrypt the password again with the session key.
+ // The PDC will decrypt it on the other side.
+ //
+
+ Status = RtlEncryptNtOwfPwdWithNtOwfPwd(
+ &OwfPassword,
+ (PNT_OWF_PASSWORD) &ClientSession->CsSessionKey,
+ &SessKeyEncrPassword) ;
+
+ if ( !NT_SUCCESS( Status )) {
+ NlPrint((NL_CRITICAL,
+ "NlChangePassword: %wZ: "
+ "Cannot RtlEncryptNtOwfPwdWithNtOwfPwd %lX\n",
+ &ClientSession->CsDomainName,
+ Status));
+ goto Cleanup;
+ }
+
+
+ //
+ // Build the Authenticator for this request to the PDC.
+ //
+
+ NlBuildAuthenticator(
+ &ClientSession->CsAuthenticationSeed,
+ &ClientSession->CsSessionKey,
+ &OurAuthenticator);
+
+
+ //
+ // Change the password on the machine our connection is to.
+ //
+
+ Status = NlStartApiClientSession( ClientSession, TRUE );
+
+ if ( NT_SUCCESS(Status) ) {
+ Status = I_NetServerPasswordSet( ClientSession->CsUncServerName,
+ ClientSession->CsAccountName,
+ ClientSession->CsSecureChannelType,
+ NlGlobalUnicodeComputerName,
+ &OurAuthenticator,
+ &ReturnAuthenticator,
+ &SessKeyEncrPassword);
+ }
+
+ // NOTE: This call may drop the secure channel behind our back
+ (VOID) NlFinishApiClientSession( ClientSession, TRUE );
+
+
+ //
+ // Now verify primary's authenticator and update our seed
+ //
+
+ if ( Status != STATUS_ACCESS_DENIED ) {
+ PasswordChangedOnServer = TRUE;
+
+ if (!NlUpdateSeed( &ClientSession->CsAuthenticationSeed,
+ &ReturnAuthenticator.Credential,
+ &ClientSession->CsSessionKey) ) {
+ if ( NT_SUCCESS(Status) ) {
+ Status = STATUS_ACCESS_DENIED;
+ }
+ }
+ }
+
+ //
+ // If the server refused the change,
+ // put the lsa secret back the way it was.
+ // pretend the change was successful.
+ //
+
+ if ( Status == STATUS_WRONG_PASSWORD ) {
+
+ NlPrint((NL_SESSION_SETUP,
+ "NlChangePassword: %wZ: PDC refused to change password\n",
+ &ClientSession->CsDomainName ));
+ //
+ // If we changed the LSA secret,
+ // put it back.
+ //
+
+ LOCK_TRUST_LIST();
+ if ( LsaSecretChanged ) {
+ NlPrint((NL_SESSION_SETUP,
+ "NlChangePassword: %wZ: undoing LSA secret change.\n",
+ &ClientSession->CsDomainName ));
+
+ Status = LsarSetSecret(
+ SecretHandle,
+ CrCurrentPassword,
+ CrPreviousPassword );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ NlPrint((NL_CRITICAL,
+ "NlChangePassword: %wZ: Cannot undo LsarSetSecret %lX\n",
+ &ClientSession->CsDomainName,
+ Status));
+ UNLOCK_TRUST_LIST();
+ goto Cleanup;
+ }
+
+ //
+ // Undo what we've done above.
+ //
+ ClientSession->CsFlags &= ~CS_UPDATE_PASSWORD;
+ }
+
+ //
+ // Prevent us from trying too frequently.
+ //
+
+ ClientSession->CsFlags |= CS_PASSWORD_REFUSED;
+ UNLOCK_TRUST_LIST();
+
+ //
+ // Avoid special cleanup below.
+ //
+ PasswordChangedOnServer = FALSE;
+ Status = STATUS_SUCCESS;
+ }
+
+ //
+ // Common exit
+ //
+
+Cleanup:
+
+ if ( PasswordChangedOnServer ) {
+
+ //
+ // On success,
+ // Indicate that the password has now been updated on the
+ // PDC so the old password is no longer in use.
+ //
+
+ if ( NT_SUCCESS( Status ) ) {
+
+ LOCK_TRUST_LIST();
+ ClientSession->CsFlags &= ~CS_UPDATE_PASSWORD;
+
+ NlPrint((NL_SESSION_SETUP,
+ "NlChangePassword: %wZ: Flag password updated on PDC\n",
+ &ClientSession->CsDomainName ));
+
+ //
+ // If the default password was changed,
+ // avoid leaving the default password around as the old
+ // password. Otherwise, a bogus DC could convince us to use
+ // the bogus DC via the default password.
+ //
+
+ if ( DefaultPasswordBeingChanged ) {
+ NlPrint((NL_SESSION_SETUP,
+ "NlChangePassword: %wZ: Setting LsaSecret old password to same as new password\n",
+ &ClientSession->CsDomainName ));
+
+ Status = LsarSetSecret(
+ SecretHandle,
+ &CrClearTextPasswordString,
+ &CrClearTextPasswordString );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ NlPrint((NL_CRITICAL,
+ "NlChangePassword: %wZ: Cannot LsarSetSecret to set old password %lX\n",
+ &ClientSession->CsDomainName,
+ Status));
+ UNLOCK_TRUST_LIST();
+ goto Cleanup;
+ }
+
+ }
+ UNLOCK_TRUST_LIST();
+
+
+
+ //
+ // Notify the Admin that he'll have to manually set this server's
+ // password on both this server and the PDC.
+ //
+
+ } else {
+
+ LPWSTR MsgStrings[2];
+
+ //
+ // Drop the secure channel
+ //
+
+ NlSetStatusClientSession( ClientSession, Status );
+
+ //
+ // write event log
+ //
+
+ MsgStrings[0] = ClientSession->CsAccountName;
+ MsgStrings[1] = (LPWSTR) Status;
+
+ NlpWriteEventlog (
+ NELOG_NetlogonPasswdSetFailed,
+ EVENTLOG_ERROR_TYPE,
+ (LPBYTE) & Status,
+ sizeof(Status),
+ MsgStrings,
+ 2 | LAST_MESSAGE_IS_NTSTATUS );
+ }
+
+
+ }
+
+
+ //
+ // Clean up locally used resources.
+ //
+
+ if ( SecretHandle != NULL ) {
+ (VOID) LsarClose( &SecretHandle );
+ }
+
+ if ( CrCurrentPassword != NULL ) {
+ (VOID) LsaIFree_LSAPR_CR_CIPHER_VALUE ( CrCurrentPassword );
+ }
+
+ if ( CrPreviousPassword != NULL ) {
+ (VOID) LsaIFree_LSAPR_CR_CIPHER_VALUE ( CrPreviousPassword );
+ }
+
+ NlResetWriterClientSession( ClientSession );
+
+ return Status;
+}
+
+
+NTSTATUS
+NlCheckMachineAccount(
+ IN LPWSTR AccountName,
+ IN NETLOGON_SECURE_CHANNEL_TYPE SecureChannelType
+ )
+/*++
+
+Routine Description:
+
+ Check the machine account:
+ Ensure the SecureChannelType is valid,
+ Verify that the account exists,
+ Ensure the group implied by the account type is valid,
+ Ensure the user account is a member of that group,
+ Ensure the user account is the right account type.
+
+Arguments:
+
+ AccountName - The name of the account.
+
+ SecureChannelType - The type of the account.
+
+Return Value:
+
+ STATUS_SUCCESS - the requestor is a member of group
+ Other NT status code.
+
+--*/
+{
+ NTSTATUS Status;
+
+ SAMPR_HANDLE UserHandle = NULL;
+ PSAMPR_USER_INFO_BUFFER UserControl = NULL;
+ DWORD DesiredAccountControl;
+ LPWSTR AccountPostfix;
+
+ LPWSTR GroupName;
+ SAMPR_ULONG_ARRAY RelativeIdArray;
+ SAMPR_ULONG_ARRAY UseArray;
+
+ PSAMPR_GET_GROUPS_BUFFER Groups = NULL;
+
+ RelativeIdArray.Count = 1;
+ RelativeIdArray.Element = NULL;
+ UseArray.Count = 1;
+ UseArray.Element = NULL;
+
+ //
+ // Validate the secure channel type.
+ //
+
+ switch (SecureChannelType) {
+ case WorkstationSecureChannel:
+ GroupName = NULL;
+ DesiredAccountControl = USER_WORKSTATION_TRUST_ACCOUNT;
+ AccountPostfix = SSI_ACCOUNT_NAME_POSTFIX;
+ break;
+
+ case ServerSecureChannel:
+ GroupName = NULL;
+ DesiredAccountControl = USER_SERVER_TRUST_ACCOUNT;
+ AccountPostfix = SSI_ACCOUNT_NAME_POSTFIX;
+ break;
+
+ case UasServerSecureChannel:
+ GroupName = SSI_SERVER_GROUP_W;
+ DesiredAccountControl = USER_NORMAL_ACCOUNT;
+ AccountPostfix = NULL;
+ break;
+
+ case TrustedDomainSecureChannel:
+ GroupName = NULL;
+ DesiredAccountControl = USER_INTERDOMAIN_TRUST_ACCOUNT;
+ AccountPostfix = SSI_ACCOUNT_NAME_POSTFIX;
+ break;
+
+ default:
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ //
+ // Ensure the account name has the correct postfix.
+ //
+
+ if ( AccountPostfix != NULL ) {
+ DWORD Length = wcslen( AccountName );
+
+ if ( Length <= SSI_ACCOUNT_NAME_POSTFIX_LENGTH ) {
+ return STATUS_NO_SUCH_USER;
+ }
+
+ if ( _wcsicmp(&AccountName[Length - SSI_ACCOUNT_NAME_POSTFIX_LENGTH],
+ SSI_ACCOUNT_NAME_POSTFIX) != 0 ) {
+ return STATUS_NO_SUCH_USER;
+ }
+ }
+
+
+
+ //
+ // Open the account.
+ //
+
+ Status = NlSamOpenNamedUser( AccountName, &UserHandle, NULL );
+
+ if ( !NT_SUCCESS(Status) ) {
+ goto Cleanup;
+ }
+
+
+ //
+ // Get the account control information and
+ // Ensure the Account type matches the account type on the account.
+ //
+
+ Status = SamrQueryInformationUser(
+ UserHandle,
+ UserControlInformation,
+ &UserControl );
+
+ if (!NT_SUCCESS(Status)) {
+ UserControl = NULL;
+ Status = STATUS_NO_SUCH_USER;
+ goto Cleanup;
+ }
+
+ if ( (UserControl->Control.UserAccountControl & USER_ACCOUNT_TYPE_MASK)
+ != DesiredAccountControl ) {
+ Status = STATUS_NO_SUCH_USER;
+ goto Cleanup;
+ }
+
+
+
+ //
+ // Ensure the account is a member of the correct group.
+ //
+
+ if ( GroupName != NULL ) {
+
+ RPC_UNICODE_STRING GroupNameString;
+ ULONG i;
+
+ //
+ // Convert the group name to a RelativeId.
+ //
+
+ RtlInitUnicodeString( (PUNICODE_STRING)&GroupNameString, GroupName );
+
+ Status = SamrLookupNamesInDomain(
+ NlGlobalDBInfoArray[SAM_DB].DBHandle,
+ 1,
+ &GroupNameString,
+ &RelativeIdArray,
+ &UseArray );
+
+
+ if ( !NT_SUCCESS(Status) ) {
+ RelativeIdArray.Element = NULL;
+ UseArray.Element = NULL;
+ if ( Status == STATUS_NONE_MAPPED ) {
+ Status = STATUS_NO_SUCH_GROUP;
+ }
+ goto Cleanup;
+ }
+
+ if ( UseArray.Element[0] != SidTypeGroup ) {
+ Status = STATUS_NO_SUCH_GROUP;
+ goto Cleanup;
+ }
+
+ //
+ // Open the account and determine the groups it belongs to
+ //
+
+ Status = SamrGetGroupsForUser( UserHandle,
+ &Groups );
+
+ if ( !NT_SUCCESS(Status) ) {
+ Groups = NULL;
+ goto Cleanup;
+ }
+
+ //
+ // Walk thru the buffer looking for group SERVERS
+ //
+
+ for ( i=0; i<Groups->MembershipCount; i++ ) {
+ if ( Groups->Groups[i].RelativeId == RelativeIdArray.Element[0] ) {
+ break; // found
+ }
+ }
+
+ //
+ // if this machine not a member of SERVERS quit
+ //
+
+ if (i == Groups->MembershipCount) {
+ Status = STATUS_MEMBER_NOT_IN_GROUP;
+ goto Cleanup;
+ }
+ }
+
+ Status = STATUS_SUCCESS;
+
+ //
+ // Cleanup
+ //
+Cleanup:
+
+ //
+ // Free locally used resources
+ //
+ if ( UserControl != NULL ) {
+ SamIFree_SAMPR_USER_INFO_BUFFER( UserControl, UserControlInformation );
+ }
+
+ if ( Groups != NULL ) {
+ SamIFree_SAMPR_GET_GROUPS_BUFFER( Groups );
+ }
+
+ SamIFree_SAMPR_ULONG_ARRAY( &RelativeIdArray );
+ SamIFree_SAMPR_ULONG_ARRAY( &UseArray );
+
+ if ( UserHandle != NULL ) {
+ SamrCloseHandle( &UserHandle );
+ }
+
+ return Status;
+}
+
+
+
+NTSTATUS
+NlGetUserPriv(
+ IN ULONG GroupCount,
+ IN PGROUP_MEMBERSHIP Groups,
+ IN ULONG UserRelativeId,
+ OUT LPDWORD Priv,
+ OUT LPDWORD AuthFlags
+ )
+
+/*++
+
+Routine Description:
+
+ Determines the Priv and AuthFlags for the specified user.
+
+Arguments:
+
+ GroupCount - Number of groups this user is a member of
+
+ Groups - Array of groups this user is a member of.
+
+ UserRelativeId - Relative ID of the user to query.
+
+ Priv - Returns the Lanman 2.0 Privilege level for the specified user.
+
+ AuthFlags - Returns the Lanman 2.0 Authflags for the specified user.
+
+
+Return Value:
+
+ Status of the operation.
+
+--*/
+
+{
+ NET_API_STATUS NetStatus;
+ NTSTATUS Status;
+
+ ULONG GroupIndex;
+ PSID *UserSids = NULL;
+ ULONG UserSidCount = 0;
+ SAMPR_PSID_ARRAY SamSidArray;
+ SAMPR_ULONG_ARRAY Aliases;
+
+ //
+ // Initialization
+ //
+
+ Aliases.Element = NULL;
+
+ //
+ // Allocate a buffer to point to the SIDs we're interested in
+ // alias membership for.
+ //
+
+ UserSids = (PSID *)
+ NetpMemoryAllocate( (GroupCount+1) * sizeof(PSID) );
+
+ if ( UserSids == NULL ) {
+ Status = STATUS_NO_MEMORY;
+ goto Cleanup;
+ }
+
+ //
+ // Add the User's Sid to the Array of Sids.
+ //
+
+ NetStatus = NetpDomainIdToSid( NlGlobalDBInfoArray[SAM_DB].DBId,
+ UserRelativeId,
+ &UserSids[0] );
+
+ if ( NetStatus != NERR_Success ) {
+ Status = NetpApiStatusToNtStatus( NetStatus );
+ goto Cleanup;
+ }
+
+ UserSidCount ++;
+
+
+
+ //
+ // Add each group the user is a member of to the array of Sids.
+ //
+
+ for ( GroupIndex = 0; GroupIndex < GroupCount; GroupIndex ++ ){
+
+ NetStatus = NetpDomainIdToSid( NlGlobalDBInfoArray[SAM_DB].DBId,
+ Groups[GroupIndex].RelativeId,
+ &UserSids[GroupIndex+1] );
+
+ if ( NetStatus != NERR_Success ) {
+ Status = NetpApiStatusToNtStatus( NetStatus );
+ goto Cleanup;
+ }
+
+ UserSidCount ++;
+ }
+
+
+ //
+ // Find out which aliases in the builtin domain this user is a member of.
+ //
+
+ SamSidArray.Count = UserSidCount;
+ SamSidArray.Sids = (PSAMPR_SID_INFORMATION) UserSids;
+ Status = SamrGetAliasMembership( NlGlobalDBInfoArray[BUILTIN_DB].DBHandle,
+ &SamSidArray,
+ &Aliases );
+
+ if ( !NT_SUCCESS(Status) ) {
+ Aliases.Element = NULL;
+ NlPrint((NL_CRITICAL,
+ "NlGetUserPriv: SamGetAliasMembership returns %lX\n",
+ Status ));
+ goto Cleanup;
+ }
+
+ //
+ // Convert the alias membership to priv and auth flags
+ //
+
+ NetpAliasMemberToPriv(
+ Aliases.Count,
+ Aliases.Element,
+ Priv,
+ AuthFlags );
+
+ Status = STATUS_SUCCESS;
+
+ //
+ // Free Locally used resources.
+ //
+Cleanup:
+ if ( Aliases.Element != NULL ) {
+ SamIFree_SAMPR_ULONG_ARRAY ( &Aliases );
+ }
+
+ if ( UserSids != NULL ) {
+
+ for ( GroupIndex = 0; GroupIndex < UserSidCount; GroupIndex ++ ) {
+ NetpMemoryFree( UserSids[GroupIndex] );
+ }
+
+ NetpMemoryFree( UserSids );
+ }
+
+ return Status;
+}
+/*lint +e740 */ /* don't complain about unusual cast */
diff --git a/private/net/svcdlls/logonsrv/server/mailslot.c b/private/net/svcdlls/logonsrv/server/mailslot.c
new file mode 100644
index 000000000..565f2c370
--- /dev/null
+++ b/private/net/svcdlls/logonsrv/server/mailslot.c
@@ -0,0 +1,1238 @@
+/*--
+
+
+Copyright (c) 1987-1993 Microsoft Corporation
+
+Module Name:
+
+ mailslot.c
+
+Abstract:
+
+ Routines for doing I/O on the netlogon service's mailslots.
+
+Author:
+
+ 03-Nov-1993 (cliffv)
+
+Environment:
+
+ User mode only.
+ Contains NT-specific code.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+--*/
+
+//
+// Common include files.
+//
+
+#include <logonsrv.h> // Include files common to entire service
+
+//
+// Include files specific to this module
+#include <ntddbrow.h> // LARGE_INTEGER definition
+#include <align.h> // ALIGN_WCHAR
+#include <lmerr.h> // System Error Log definitions
+#include <lmsvc.h> // SERVICE_UIC codes are defined here
+#include <ntddbrow.h> // Interface to browser driver
+#include <stdlib.h> // max()
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Structure describing one of the primary mailslots the netlogon service
+// will read messages from.
+//
+// This structure is used only by netlogon's main thread and therefore needs
+// no synchronization.
+//
+/////////////////////////////////////////////////////////////////////////////
+
+//
+// Define maximum buffer size returned from the browser.
+//
+// Header returned by browser + actual mailslot message size + name of
+// mailslot + name of transport.
+//
+
+#define MAILSLOT_MESSAGE_SIZE \
+ (sizeof(NETLOGON_MAILSLOT)+ \
+ NETLOGON_MAX_MS_SIZE + \
+ (NETLOGON_LM_MAILSLOT_LEN+1) * sizeof(WCHAR) + \
+ (MAXIMUM_FILENAME_LENGTH+1) * sizeof(WCHAR))
+
+typedef struct _NETLOGON_MAILSLOT_DESC {
+
+ HANDLE BrowserHandle; // Handle to the browser device driver
+
+ HANDLE BrowserReadEvent;// Handle to wait on for overlapped I/O
+
+ OVERLAPPED Overlapped; // Governs overlapped I/O
+
+ BOOL ReadPending; // True if a read operation is pending
+
+ BOOL NameAdded; // True if Domain<1B> name has been added
+
+ BOOL AddNameEventLogged;// True if Domain<1B> name add failed at least once
+
+ LPBYTE CurrentMessage; // Pointer to Message1 or Message2 below
+
+ LPBYTE PreviousMessage; // Previous value of CurrentMessage
+
+ //
+ // Buffer containing message from browser
+ //
+ // The buffers are alternated allowing us to compare if an incoming
+ // message is identical to the previous message.
+ //
+ // Leave room so the actual used portion of each buffer is properly aligned.
+ // The NETLOGON_MAILSLOT struct begins with a LARGE_INTEGER which must be
+ // aligned.
+
+ BYTE Message1[ MAILSLOT_MESSAGE_SIZE + ALIGN_WORST ];
+ BYTE Message2[ MAILSLOT_MESSAGE_SIZE + ALIGN_WORST ];
+
+} NETLOGON_MAILSLOT_DESC, *PNETLOGON_MAILSLOT_DESC;
+
+PNETLOGON_MAILSLOT_DESC NlGlobalMailslotDesc;
+
+
+
+
+HANDLE
+NlBrowserCreateEvent(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ Creates an event to be used in a DeviceIoControl to the browser.
+
+ ?? Consider caching one or two events to reduce the number of create
+ events
+
+Arguments:
+
+ None
+
+Return Value:
+
+ Handle to an event or NULL if the event couldn't be allocated.
+
+--*/
+{
+ HANDLE EventHandle;
+ //
+ // Create a completion event
+ //
+
+ EventHandle = CreateEvent(
+ NULL, // No security ettibutes
+ TRUE, // Manual reset
+ FALSE, // Initially not signaled
+ NULL); // No Name
+
+ if ( EventHandle == NULL ) {
+ NlPrint((NL_CRITICAL, "Cannot create Browser read event %ld\n", GetLastError() ));
+ }
+
+ return EventHandle;
+}
+
+
+VOID
+NlBrowserCloseEvent(
+ IN HANDLE EventHandle
+ )
+/*++
+
+Routine Description:
+
+ Closes an event used in a DeviceIoControl to the browser.
+
+Arguments:
+
+ EventHandle - Handle of the event to close
+
+Return Value:
+
+ None.
+
+--*/
+{
+ (VOID) CloseHandle( EventHandle );
+}
+
+
+VOID
+NlBrowserClose(
+ VOID
+ );
+
+
+NTSTATUS
+NlBrowserDeviceIoControl(
+ IN DWORD FunctionCode,
+ IN PLMDR_REQUEST_PACKET RequestPacket,
+ IN DWORD RequestPacketSize,
+ IN LPBYTE Buffer,
+ IN DWORD BufferSize
+ )
+/*++
+
+Routine Description:
+
+ Send a DeviceIoControl syncrhonously to the browser.
+
+Arguments:
+
+ FunctionCode - DeviceIoControl function code
+
+ RequestPacket - The request packet to send.
+
+ RequestPacketSize - Size (in bytes) of the request packet.
+
+ Buffer - Additional buffer to pass to the browser
+
+ BufferSize - Size (in bytes) of Buffer
+
+Return Value:
+
+ Status of the operation.
+
+--*/
+{
+ NTSTATUS Status;
+ DWORD WinStatus;
+ OVERLAPPED Overlapped;
+ DWORD BytesReturned;
+
+ //
+ // Initialization
+ //
+
+ if ( NlGlobalMailslotDesc == NULL ||
+ NlGlobalMailslotDesc->BrowserHandle == NULL ) {
+ return ERROR_NOT_SUPPORTED;
+ }
+
+ RequestPacket->Version = LMDR_REQUEST_PACKET_VERSION;
+
+ //
+ // Get a completion event
+ //
+
+ Overlapped.hEvent = NlBrowserCreateEvent();
+
+ if ( Overlapped.hEvent == NULL ) {
+ return GetLastError();
+ }
+
+ //
+ // Send the request to the Datagram Receiver device driver.
+ //
+
+ if ( !DeviceIoControl(
+ NlGlobalMailslotDesc->BrowserHandle,
+ FunctionCode,
+ RequestPacket,
+ RequestPacketSize,
+ Buffer,
+ BufferSize,
+ &BytesReturned,
+ &Overlapped )) {
+
+ WinStatus = GetLastError();
+
+ if ( WinStatus == ERROR_IO_PENDING ) {
+ if ( !GetOverlappedResult( NlGlobalMailslotDesc->BrowserHandle,
+ &Overlapped,
+ &BytesReturned,
+ TRUE )) {
+ WinStatus = GetLastError();
+ } else {
+ WinStatus = NO_ERROR;
+ }
+ }
+ } else {
+ WinStatus = NO_ERROR;
+ }
+
+ //
+ // Delete the completion event
+ //
+
+ NlBrowserCloseEvent( Overlapped.hEvent );
+
+
+ if ( WinStatus ) {
+ NlPrint((NL_CRITICAL,"ioctl to Browser returns %ld\n", WinStatus));
+ Status = NetpApiStatusToNtStatus( WinStatus );
+ } else {
+ Status = STATUS_SUCCESS;
+ }
+
+
+ return Status;
+}
+
+
+
+VOID
+NlBrowserAddName(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ Add the Domain<1B> name. The is the name NetGetDcName uses to identify
+ the PDC.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ NTSTATUS Status;
+
+ BYTE Buffer[sizeof(LMDR_REQUEST_PACKET) +
+ (max(CNLEN, DNLEN) + 1) * sizeof(WCHAR)];
+ PLMDR_REQUEST_PACKET RequestPacket = (PLMDR_REQUEST_PACKET) Buffer;
+
+ //
+ // Add the <domain>0x1B name.
+ //
+ // This is the name NetGetDcName uses to identify the PDC.
+ //
+
+ if ( NlGlobalRole == RolePrimary && !NlGlobalMailslotDesc->NameAdded ) {
+
+ RequestPacket->TransportName.Length = 0;
+ RequestPacket->TransportName.Buffer = NULL;
+ RequestPacket->Parameters.AddDelName.Type = PrimaryDomainBrowser;
+ RequestPacket->Parameters.AddDelName.DgReceiverNameLength =
+ NlGlobalUnicodeDomainNameString.Length;
+
+ wcscpy( RequestPacket->Parameters.AddDelName.Name,
+ NlGlobalUnicodeDomainNameString.Buffer );
+
+ Status = NlBrowserDeviceIoControl(
+ IOCTL_LMDR_ADD_NAME,
+ RequestPacket,
+ sizeof(LMDR_REQUEST_PACKET) +
+ NlGlobalUnicodeDomainNameString.Length +
+ sizeof(WCHAR),
+ NULL,
+ 0 );
+
+ if (NT_SUCCESS(Status)) {
+ NlGlobalMailslotDesc->NameAdded = TRUE;
+ } else {
+
+ if ( !NlGlobalMailslotDesc->AddNameEventLogged ) {
+ LPWSTR MsgStrings[2];
+
+ MsgStrings[0] = NlGlobalUnicodeDomainName;
+ MsgStrings[1] = (LPWSTR) Status;
+
+ NlpWriteEventlog(
+ NELOG_NetlogonAddNameFailure,
+ EVENTLOG_WARNING_TYPE,
+ (LPBYTE)&Status,
+ sizeof(Status),
+ MsgStrings,
+ 2 | LAST_MESSAGE_IS_NTSTATUS );
+
+ NlGlobalMailslotDesc->AddNameEventLogged = TRUE;
+ }
+ NlPrint((NL_CRITICAL,"Can't add the 0x1B name: 0x%lx\n", Status));
+ }
+ }
+
+ return;
+}
+
+
+
+BOOL
+NlBrowserOpen(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This routine opens the NT LAN Man Datagram Receiver driver and prepares
+ for reading mailslot messages from it.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ TRUE -- iff initialization is successful.
+
+ if false, NlExit will already have been called.
+
+--*/
+{
+ NTSTATUS Status;
+ BOOL ReturnValue;
+
+ UNICODE_STRING DeviceName;
+
+ IO_STATUS_BLOCK IoStatusBlock;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+
+ BYTE Buffer[sizeof(LMDR_REQUEST_PACKET) +
+ (max(CNLEN, DNLEN) + 1) * sizeof(WCHAR)];
+ PLMDR_REQUEST_PACKET RequestPacket = (PLMDR_REQUEST_PACKET) Buffer;
+
+
+ //
+ // Allocate the mailslot descriptor for this mailslot
+ //
+
+ NlGlobalMailslotDesc = NetpMemoryAllocate( sizeof(NETLOGON_MAILSLOT_DESC) );
+
+ if ( NlGlobalMailslotDesc == NULL ) {
+ NlExit( SERVICE_UIC_RESOURCE, ERROR_NOT_ENOUGH_MEMORY, LogError, NULL);
+ return FALSE;
+ }
+
+ RtlZeroMemory( NlGlobalMailslotDesc, sizeof(NETLOGON_MAILSLOT_DESC) );
+
+ NlGlobalMailslotDesc->CurrentMessage =
+ ROUND_UP_POINTER( NlGlobalMailslotDesc->Message1, ALIGN_WORST);
+
+
+ //
+ // Open the browser device.
+ //
+ RtlInitUnicodeString(&DeviceName, DD_BROWSER_DEVICE_NAME_U);
+
+ InitializeObjectAttributes(
+ &ObjectAttributes,
+ &DeviceName,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL
+ );
+
+ Status = NtOpenFile(
+ &NlGlobalMailslotDesc->BrowserHandle,
+ SYNCHRONIZE,
+ &ObjectAttributes,
+ &IoStatusBlock,
+ 0,
+ 0
+ );
+
+ if (NT_SUCCESS(Status)) {
+ Status = IoStatusBlock.Status;
+ }
+
+ if (! NT_SUCCESS(Status)) {
+ NlPrint((NL_CRITICAL,"NtOpenFile browser driver failed: 0x%lx\n",
+ Status));
+ ReturnValue = FALSE;
+ goto Cleanup;
+ }
+
+ //
+ // Create a completion event
+ //
+
+ NlGlobalMailslotHandle =
+ NlGlobalMailslotDesc->BrowserReadEvent = NlBrowserCreateEvent();
+
+ if ( NlGlobalMailslotDesc->BrowserReadEvent == NULL ) {
+ Status = NetpApiStatusToNtStatus( GetLastError() );
+ ReturnValue = FALSE;
+ goto Cleanup;
+ }
+
+
+ //
+ // Set the maximum number of messages to be queued
+ //
+
+ RequestPacket->TransportName.Length = 0;
+ RequestPacket->TransportName.Buffer = NULL;
+ RequestPacket->Parameters.NetlogonMailslotEnable.MaxMessageCount =
+ NlGlobalMaximumMailslotMessagesParameter;
+
+ Status = NlBrowserDeviceIoControl(
+ IOCTL_LMDR_NETLOGON_MAILSLOT_ENABLE,
+ RequestPacket,
+ sizeof(LMDR_REQUEST_PACKET),
+ NULL,
+ 0 );
+
+ if (! NT_SUCCESS(Status)) {
+ NlPrint((NL_CRITICAL,"Can't set browser max message count: 0x%lx\n",
+ Status));
+ ReturnValue = FALSE;
+ goto Cleanup;
+ }
+
+
+ //
+ // Add the Domain<1B> name.
+ //
+
+ NlBrowserAddName();
+
+ ReturnValue = TRUE;
+
+Cleanup:
+ if ( !ReturnValue ) {
+ NlExit( NELOG_NetlogonBrowserDriver, Status, LogErrorAndNtStatus, NULL);
+ NlBrowserClose();
+ }
+
+ return ReturnValue;
+}
+
+
+VOID
+NlBrowserClose(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This routine cleans up after a NlBrowserInitialize()
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ IO_STATUS_BLOCK IoSb;
+ NTSTATUS Status;
+
+ BYTE Buffer[sizeof(LMDR_REQUEST_PACKET) +
+ (max(CNLEN, DNLEN) + 1) * sizeof(WCHAR)];
+ PLMDR_REQUEST_PACKET RequestPacket = (PLMDR_REQUEST_PACKET) Buffer;
+
+ if ( NlGlobalMailslotDesc == NULL) {
+ return;
+ }
+
+
+ //
+ // If we've opened the browser, clean up.
+ //
+
+ if ( NlGlobalMailslotDesc->BrowserHandle != NULL ) {
+
+ //
+ // Tell the browser to stop queueing messages
+ //
+
+ RequestPacket->TransportName.Length = 0;
+ RequestPacket->TransportName.Buffer = NULL;
+ RequestPacket->Parameters.NetlogonMailslotEnable.MaxMessageCount = 0;
+
+ Status = NlBrowserDeviceIoControl(
+ IOCTL_LMDR_NETLOGON_MAILSLOT_ENABLE,
+ RequestPacket,
+ sizeof(LMDR_REQUEST_PACKET),
+ NULL,
+ 0 );
+
+ if (! NT_SUCCESS(Status)) {
+ NlPrint((NL_CRITICAL,"Can't reset browser max message count: 0x%lx\n",
+ Status));
+ }
+
+
+ //
+ // Delete the <domain>0x1B name.
+ //
+
+ if ( NlGlobalRole == RolePrimary ) {
+
+ RequestPacket->TransportName.Length = 0;
+ RequestPacket->TransportName.Buffer = NULL;
+ RequestPacket->Parameters.AddDelName.Type = PrimaryDomainBrowser;
+ RequestPacket->Parameters.AddDelName.DgReceiverNameLength =
+ NlGlobalUnicodeDomainNameString.Length;
+
+ wcscpy( RequestPacket->Parameters.AddDelName.Name,
+ NlGlobalUnicodeDomainNameString.Buffer );
+
+ Status = NlBrowserDeviceIoControl(
+ IOCTL_LMDR_DELETE_NAME,
+ RequestPacket,
+ sizeof(LMDR_REQUEST_PACKET) +
+ NlGlobalUnicodeDomainNameString.Length +
+ sizeof(WCHAR),
+ NULL,
+ 0 );
+
+ if (! NT_SUCCESS(Status)) {
+ NlPrint((NL_CRITICAL,"Can't remove the 0x1B name: 0x%lx\n",
+ Status));
+ }
+ }
+
+ //
+ // Cancel the I/O operations outstanding on the browser.
+ //
+
+ NtCancelIoFile(NlGlobalMailslotDesc->BrowserHandle, &IoSb);
+
+ //
+ // Close the handle to the browser
+ //
+
+ NtClose(NlGlobalMailslotDesc->BrowserHandle);
+ NlGlobalMailslotDesc->BrowserHandle = NULL;
+ }
+
+ //
+ // Close the global browser read event
+ //
+
+ if ( NlGlobalMailslotDesc->BrowserReadEvent != NULL ) {
+ NlBrowserCloseEvent(NlGlobalMailslotDesc->BrowserReadEvent);
+ }
+ NlGlobalMailslotHandle = NULL;
+
+ //
+ // Free the descriptor describing the browser
+ //
+
+ NetpMemoryFree( NlGlobalMailslotDesc );
+ NlGlobalMailslotDesc = NULL;
+
+}
+
+
+NTSTATUS
+NlBrowserSendDatagram(
+ IN LPSTR OemServerName,
+ IN LPWSTR TransportName,
+ IN LPSTR OemMailslotName,
+ IN PVOID Buffer,
+ IN ULONG BufferSize
+ )
+/*++
+
+Routine Description:
+
+ Send the specified mailslot message to the specified mailslot on the
+ specified server on the specified transport..
+
+Arguments:
+
+ OemServerName -- Name of the server to send to.
+
+ TransportName -- Name of the transport to send on.
+ Use NULL to send on all transports.
+
+ OemMailslotName -- Name of the mailslot to send to.
+
+ Buffer -- Specifies a pointer to the mailslot message to send.
+
+ BufferSize -- Size in bytes of the mailslot message
+
+Return Value:
+
+ Status of the operation.
+
+--*/
+{
+ PLMDR_REQUEST_PACKET RequestPacket = NULL;
+
+ UNICODE_STRING ServerNameString;
+ OEM_STRING TempOemString;
+ DWORD OemMailslotNameSize;
+ DWORD TransportNameSize;
+
+ NTSTATUS Status;
+ LPBYTE Where;
+
+ //
+ // If the transport isn't specified,
+ // send on all transports.
+ //
+
+ if ( TransportName == NULL ) {
+ CHAR FullMailslotName[(UNCLEN+2) + NETLOGON_LM_MAILSLOT_LEN];
+
+ //
+ // Build the mailslot name.
+ //
+
+ (VOID) lstrcpyA(FullMailslotName, "\\\\" );
+ (VOID) lstrcatA(FullMailslotName, OemServerName );
+ (VOID) lstrcatA(FullMailslotName, OemMailslotName );
+
+ Status = NlpWriteMailslotA( FullMailslotName,
+ Buffer,
+ BufferSize );
+
+ return Status;
+ }
+
+
+ //
+ // Ensure we've initialized.
+ //
+
+ ServerNameString.Buffer = NULL;
+
+
+ //
+ // Convert the server name to uppercase.
+ //
+
+ RtlInitString( &TempOemString, OemServerName );
+ Status = RtlOemStringToUnicodeString( &ServerNameString,
+ &TempOemString,
+ TRUE ); // Allocate buffer
+ if ( !NT_SUCCESS(Status) ) {
+ NlPrint((NL_CRITICAL,
+ "NlBrowserSendDatagram: Cannot convert server name %08lx\n",
+ Status));
+ goto Cleanup;
+ }
+
+
+ //
+ // Allocate a request packet.
+ //
+
+ OemMailslotNameSize = lstrlenA(OemMailslotName) + 1;
+ TransportNameSize = (wcslen(TransportName) + 1) * sizeof(WCHAR);
+
+ RequestPacket = NetpMemoryAllocate(
+ sizeof(LMDR_REQUEST_PACKET) +
+ TransportNameSize +
+ OemMailslotNameSize +
+ ServerNameString.Length +
+ sizeof(WCHAR)) ; // For alignment
+
+ if (RequestPacket == NULL) {
+ Status = STATUS_NO_MEMORY;
+ goto Cleanup;
+ }
+
+
+
+ //
+ // Fill in the Request Packet.
+ //
+
+ RequestPacket->Type = Datagram;
+ RequestPacket->Parameters.SendDatagram.DestinationNameType = ComputerName;
+
+
+ //
+ // Fill in the name of the machine to send the mailslot message to.
+ //
+
+ RequestPacket->Parameters.SendDatagram.NameLength = ServerNameString.Length;
+
+ Where = (LPBYTE) RequestPacket->Parameters.SendDatagram.Name;
+ RtlMoveMemory( Where, ServerNameString.Buffer, ServerNameString.Length );
+ Where += ServerNameString.Length;
+
+
+ //
+ // Fill in the name of the mailslot to send to.
+ //
+
+ RequestPacket->Parameters.SendDatagram.MailslotNameLength =
+ OemMailslotNameSize;
+ strcpy( Where, OemMailslotName);
+ Where += OemMailslotNameSize;
+ Where = ROUND_UP_POINTER( Where, ALIGN_WCHAR );
+
+
+ //
+ // Fill in the TransportName
+ //
+
+ wcscpy( (LPWSTR) Where, TransportName);
+ RtlInitUnicodeString( &RequestPacket->TransportName, (LPWSTR) Where );
+ Where += TransportNameSize;
+
+
+ //
+ // Send the request to the browser.
+ //
+
+ Status = NlBrowserDeviceIoControl(
+ IOCTL_LMDR_WRITE_MAILSLOT,
+ RequestPacket,
+ Where - (LPBYTE)RequestPacket,
+ Buffer,
+ BufferSize );
+
+
+ //
+ // Free locally used resources.
+ //
+Cleanup:
+
+ if ( RequestPacket != NULL ) {
+ NetpMemoryFree( RequestPacket );
+ }
+
+ if ( ServerNameString.Buffer != NULL ) {
+ RtlFreeUnicodeString( &ServerNameString );
+ }
+
+ NlPrint((NL_MAILSLOT_TEXT,
+ "Sent out message to %s%s on transport " FORMAT_LPWSTR ".\n",
+ OemServerName,
+ OemMailslotName,
+ TransportName ));
+
+#if DBG
+ NlpDumpBuffer( NL_MAILSLOT_TEXT, Buffer, BufferSize );
+#endif // DBG
+
+ return Status;
+}
+
+
+
+VOID
+NlMailslotPostRead(
+ IN BOOLEAN IgnoreDuplicatesOfPreviousMessage
+ )
+
+/*++
+
+Routine Description:
+
+ Post a read on the mailslot if one isn't already posted.
+
+Arguments:
+
+ IgnoreDuplicatesOfPreviousMessage - TRUE to indicate that the next
+ message read should be ignored if it is a duplicate of the previous
+ message.
+
+Return Value:
+
+ TRUE -- iff successful.
+
+--*/
+{
+ NET_API_STATUS WinStatus;
+ ULONG LocalBytesRead;
+
+ //
+ // If a read is already pending,
+ // immediately return to caller.
+ //
+
+ if ( NlGlobalMailslotDesc->ReadPending ) {
+ return;
+ }
+
+ //
+ // Decide which buffer to read into.
+ //
+ // Switch back and forth so we always have the current buffer and the
+ // previous buffer.
+ //
+
+ if ( IgnoreDuplicatesOfPreviousMessage ) {
+ NlGlobalMailslotDesc->PreviousMessage = NlGlobalMailslotDesc->CurrentMessage;
+ if ( NlGlobalMailslotDesc->CurrentMessage >= NlGlobalMailslotDesc->Message2 ) {
+ NlGlobalMailslotDesc->CurrentMessage =
+ ROUND_UP_POINTER( NlGlobalMailslotDesc->Message1, ALIGN_WORST);
+ } else {
+ NlGlobalMailslotDesc->CurrentMessage =
+ ROUND_UP_POINTER( NlGlobalMailslotDesc->Message2, ALIGN_WORST);
+ }
+
+ //
+ // If duplicates of the previous message need not be ignored,
+ // indicate so.
+ // Don't bother switching the buffer pointers.
+ //
+
+ } else {
+ NlGlobalMailslotDesc->PreviousMessage = NULL;
+ }
+
+
+ //
+ // Post an overlapped read to the mailslot.
+ //
+
+ RtlZeroMemory( &NlGlobalMailslotDesc->Overlapped,
+ sizeof(NlGlobalMailslotDesc->Overlapped) );
+
+ NlGlobalMailslotDesc->Overlapped.hEvent = NlGlobalMailslotDesc->BrowserReadEvent;
+
+ if ( !DeviceIoControl(
+ NlGlobalMailslotDesc->BrowserHandle,
+ IOCTL_LMDR_NETLOGON_MAILSLOT_READ,
+ NULL,
+ 0,
+ NlGlobalMailslotDesc->CurrentMessage,
+ MAILSLOT_MESSAGE_SIZE,
+ &LocalBytesRead,
+ &NlGlobalMailslotDesc->Overlapped )) {
+
+ WinStatus = GetLastError();
+
+ //
+ // On error, wait a second before returning. This ensures we don't
+ // consume the system in an infinite loop. We don't shutdown netlogon
+ // because the error might be a temporary low memory condition.
+ //
+
+ if( WinStatus != ERROR_IO_PENDING ) {
+ LPWSTR MsgStrings[1];
+
+ NlPrint((NL_CRITICAL,
+ "Error in reading mailslot message from browser"
+ ". WinStatus = %ld\n",
+ WinStatus ));
+
+ MsgStrings[0] = (LPWSTR) WinStatus;
+
+ NlpWriteEventlog( NELOG_NetlogonFailedToReadMailslot,
+ EVENTLOG_WARNING_TYPE,
+ (LPBYTE)&WinStatus,
+ sizeof(WinStatus),
+ MsgStrings,
+ 1 | LAST_MESSAGE_IS_NETSTATUS );
+
+ Sleep( 1000 );
+
+ } else {
+ NlGlobalMailslotDesc->ReadPending = TRUE;
+ }
+
+ } else {
+ NlGlobalMailslotDesc->ReadPending = TRUE;
+ }
+
+ return;
+
+}
+
+
+BOOL
+NlMailslotOverlappedResult(
+ OUT LPBYTE *Message,
+ OUT PULONG BytesRead,
+ OUT LPWSTR *Transport,
+ OUT PBOOLEAN IgnoreDuplicatesOfPreviousMessage
+ )
+
+/*++
+
+Routine Description:
+
+ Get the overlapped result of a previous mailslot read.
+
+Arguments:
+
+ Message - Returns a pointer to the buffer containing the message
+
+ BytesRead - Returns the number of bytes read into the buffer
+
+ Transport - Returns a pointer to the name of the transport the message
+ was received on.
+
+ IgnoreDuplicatesOfPreviousMessage - Indicates that duplicates of the
+ previous message are to be ignored.
+
+Return Value:
+
+ TRUE -- iff successful.
+
+--*/
+{
+ NET_API_STATUS WinStatus;
+ ULONG LocalBytesRead;
+ LARGE_INTEGER TimeNow;
+ PNETLOGON_MAILSLOT NetlogonMailslot;
+
+ //
+ // Default to not ignoring duplicate messages.
+ // (Only ignore duplicates if a message has been properly processed.)
+
+ *IgnoreDuplicatesOfPreviousMessage = FALSE;
+
+ //
+ // Always post another read regardless of the success or failure of
+ // GetOverlappedResult.
+ // We don't know the failure mode of GetOverlappedResult, so we don't
+ // know in the failure case if we're discarding a mailslot message.
+ // But we do know that there is no read pending, so make sure we
+ // issue another one.
+ //
+
+ NlGlobalMailslotDesc->ReadPending = FALSE; // no read pending anymore
+
+
+ //
+ // Get the result of the last read
+ //
+
+ if( !GetOverlappedResult( NlGlobalMailslotDesc->BrowserHandle,
+ &NlGlobalMailslotDesc->Overlapped,
+ &LocalBytesRead,
+ TRUE) ) { // wait for the read to complete.
+
+ LPWSTR MsgStrings[1];
+
+ // On error, wait a second before returning. This ensures we don't
+ // consume the system in an infinite loop. We don't shutdown netlogon
+ // because the error might be a temporary low memory condition.
+ //
+
+ WinStatus = GetLastError();
+
+ NlPrint((NL_CRITICAL,
+ "Error in GetOverlappedResult on mailslot read"
+ ". WinStatus = %ld\n",
+ WinStatus ));
+
+ MsgStrings[0] = (LPWSTR) WinStatus;
+
+ NlpWriteEventlog( NELOG_NetlogonFailedToReadMailslot,
+ EVENTLOG_WARNING_TYPE,
+ (LPBYTE)&WinStatus,
+ sizeof(WinStatus),
+ MsgStrings,
+ 1 | LAST_MESSAGE_IS_NETSTATUS );
+
+ Sleep( 1000 );
+
+ return FALSE;
+
+ }
+
+ //
+ // On success,
+ // Return the mailslot message to the caller.
+
+
+ NetlogonMailslot = (PNETLOGON_MAILSLOT) NlGlobalMailslotDesc->CurrentMessage;
+
+
+ //
+ // Return pointers into the buffer returned by the browser
+ //
+
+ *Message = &NlGlobalMailslotDesc->CurrentMessage[
+ NetlogonMailslot->MailslotMessageOffset];
+ *BytesRead = NetlogonMailslot->MailslotMessageSize;
+ *Transport = (LPWSTR) &NlGlobalMailslotDesc->CurrentMessage[
+ NetlogonMailslot->TransportNameOffset];
+
+ NlPrint(( NL_MAILSLOT,
+ "Received mailslot opcode 0x%x on transport: " FORMAT_LPWSTR,
+ ((PNETLOGON_LOGON_QUERY)*Message)->Opcode,
+ *Transport ));
+
+ //
+ // Determine if we can discard an ancient or duplicate message
+ //
+ // Only discard messages that are either expensive to process on this
+ // machine or generate excessive traffic to respond to. Don't discard
+ // messages that we've worked hard to get (e.g., discovery responses).
+ //
+
+ switch ( ((PNETLOGON_LOGON_QUERY)*Message)->Opcode) {
+ case LOGON_REQUEST:
+ case LOGON_SAM_LOGON_REQUEST:
+ case LOGON_PRIMARY_QUERY:
+
+ //
+ // If the message is too old,
+ // discard it.
+ //
+ (VOID) NtQuerySystemTime( &TimeNow );
+ if ( NetlogonMailslot->TimeReceived.QuadPart +
+ NlGlobalMailslotMessageTimeout.QuadPart <
+ TimeNow.QuadPart ) {
+ NlPrint((NL_MAILSLOT, " (Discarded as too old.)\n" ));
+ return FALSE;
+ }
+
+ //
+ // If the previous message was recent,
+ // and this message is identical to it,
+ // discard the current message.
+ //
+
+ if ( NlGlobalMailslotDesc->PreviousMessage != NULL ) {
+ PNETLOGON_MAILSLOT PreviousNetlogonMailslot;
+
+ PreviousNetlogonMailslot = (PNETLOGON_MAILSLOT)
+ NlGlobalMailslotDesc->PreviousMessage;
+
+ if ( (PreviousNetlogonMailslot->TimeReceived.QuadPart +
+ NlGlobalMailslotDuplicateTimeout.QuadPart >
+ NetlogonMailslot->TimeReceived.QuadPart) &&
+
+ (PreviousNetlogonMailslot->MailslotMessageSize ==
+ NetlogonMailslot->MailslotMessageSize) &&
+
+ RtlCompareMemory(
+ &NlGlobalMailslotDesc->CurrentMessage[
+ NetlogonMailslot->MailslotMessageOffset],
+ &NlGlobalMailslotDesc->PreviousMessage[
+ PreviousNetlogonMailslot->MailslotMessageOffset],
+ NetlogonMailslot->MailslotMessageSize ) ==
+ NetlogonMailslot->MailslotMessageSize ) {
+
+
+ //
+ // Ensure the next comparison is to the timestamp of the
+ // message we actually responded to.
+ //
+
+ NetlogonMailslot->TimeReceived =
+ PreviousNetlogonMailslot->TimeReceived;
+
+
+ NlPrint((NL_MAILSLOT, " (Discarded as duplicate of previous.)\n" ));
+ *IgnoreDuplicatesOfPreviousMessage = TRUE;
+ return FALSE;
+
+ }
+ }
+ }
+
+ NlPrint(( NL_MAILSLOT, "\n" ));
+
+ NlpDumpBuffer(NL_MAILSLOT_TEXT, *Message, *BytesRead);
+
+ return TRUE;
+
+}
+
+
+
+NTSTATUS
+NlpWriteMailslot(
+ IN LPWSTR MailslotName,
+ IN LPVOID Buffer,
+ IN DWORD BufferSize
+ )
+
+/*++
+
+Routine Description:
+
+ Write a message to a named mailslot
+
+Arguments:
+
+ MailslotName - Unicode name of the mailslot to write to.
+
+ Buffer - Data to write to the mailslot.
+
+ BufferSize - Number of bytes to write to the mailslot.
+
+Return Value:
+
+ NT status code for the operation
+
+--*/
+
+{
+ NTSTATUS Status;
+ NET_API_STATUS NetStatus;
+
+ //
+ // Write the mailslot message.
+ //
+
+ NetStatus = NetpLogonWriteMailslot( MailslotName, Buffer, BufferSize );
+ if ( NetStatus != NERR_Success ) {
+ Status = NetpApiStatusToNtStatus( NetStatus );
+ NlPrint((NL_CRITICAL, "NetpLogonWriteMailslot failed %lx\n", Status));
+ return Status;
+ }
+
+ NlPrint((NL_MAILSLOT_TEXT, "Sent out message to " FORMAT_LPWSTR " on all transports.\n",
+ MailslotName));
+
+#if DBG
+ NlpDumpBuffer( NL_MAILSLOT_TEXT, Buffer, BufferSize );
+#endif // DBG
+
+ return STATUS_SUCCESS;
+}
+
+
+NTSTATUS
+NlpWriteMailslotA(
+ IN LPSTR MailslotName,
+ IN LPVOID Buffer,
+ IN DWORD BufferSize
+ )
+
+/*++
+
+Routine Description:
+
+ Write a message to a named mailslot
+
+Arguments:
+
+ MailslotName - ANSI Name of the mailslot to write to.
+
+ Buffer - Data to write to the mailslot.
+
+ BufferSize - Number of bytes to write to the mailslot.
+
+Return Value:
+
+ NT status code for the operation.
+
+--*/
+
+{
+ NTSTATUS Status;
+ LPWSTR UnicodeMailslotName;
+
+ //
+ // Convert mailslot name to unicode and call common routine.
+ //
+
+ UnicodeMailslotName = NetpLogonOemToUnicode( MailslotName );
+ if ( UnicodeMailslotName == NULL ) {
+ return STATUS_NO_MEMORY;
+ }
+
+ Status = NlpWriteMailslot( UnicodeMailslotName, Buffer, BufferSize );
+
+ NetpMemoryFree( UnicodeMailslotName );
+
+ return Status;
+}
diff --git a/private/net/svcdlls/logonsrv/server/makefile b/private/net/svcdlls/logonsrv/server/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/net/svcdlls/logonsrv/server/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/net/svcdlls/logonsrv/server/makefile.inc b/private/net/svcdlls/logonsrv/server/makefile.inc
new file mode 100644
index 000000000..dfb642706
--- /dev/null
+++ b/private/net/svcdlls/logonsrv/server/makefile.inc
@@ -0,0 +1 @@
+obj\$(TARGET_DIRECTORY)\nltest.res: nltest.rc
diff --git a/private/net/svcdlls/logonsrv/server/netlogon.c b/private/net/svcdlls/logonsrv/server/netlogon.c
new file mode 100644
index 000000000..291c8bf08
--- /dev/null
+++ b/private/net/svcdlls/logonsrv/server/netlogon.c
@@ -0,0 +1,4427 @@
+/*--
+
+
+Copyright (c) 1987-1992 Microsoft Corporation
+
+Module Name:
+
+ netlogon.c
+
+Abstract:
+
+ Entry point and main thread of Netlogon service.
+
+Author:
+
+ Ported from Lan Man 2.0
+
+Environment:
+
+ User mode only.
+ Contains NT-specific code.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+ 21-Nov-1990 (madana)
+ added code for update (reverse replication) and lockout support.
+
+ 21-Nov-1990 (madana)
+ server type support.
+
+ 21-May-1991 (cliffv)
+ Ported to NT. Converted to NT style.
+
+--*/
+
+
+//
+// Common include files.
+//
+
+#define LSRVDATA_ALLOCATE // Allocate data from lsrvdata.h
+#include <logonsrv.h> // Include files common to entire service
+#undef LSRVDATA_ALLOCATE
+
+//
+// Include files specific to this .c file
+//
+
+#include <alertmsg.h> // Alert message text.
+#include <ctype.h> // C library type functions
+#include <iniparm.h> // initial values of global variables
+#include <lmapibuf.h> // NetApiBufferFree
+#include <lmbrowsr.h> // I_BrowserResetNetlogonState
+#include <lmerr.h> // System Error Log definitions
+#include <lmserver.h> // Server API defines and prototypes
+#include <lmwksta.h> // WKSTA API defines and prototypes
+#include <lmsvc.h> // SERVICE_UIC codes are defined here
+#include <nlsecure.h> // NlCreateNetlogonObjects
+#include <ntlsa.h> // Defines policy database
+#include <ntrpcp.h> // Rpcp routines
+#include <replutil.h>
+#include <samisrv.h> // SamIConnect
+#include <srvann.h> // Service announcement
+#include <stddef.h> // offsetof
+#include <stdlib.h> // C library functions: rand()
+#include <string.h> // strnicmp ...
+#include <tstring.h> // IS_PATH_SEPARATOR ...
+#include <secobj.h> // BuiltinDomainSID defined here ..
+
+
+#define INTERROGATE_RESP_DELAY 2000 // may want to tune it
+#define MAX_PRIMARY_TRACK_FAIL 3 // Primary pulse slips
+
+
+
+BOOLEAN
+NetlogonDllInit (
+ IN PVOID DllHandle,
+ IN ULONG Reason,
+ IN PCONTEXT Context OPTIONAL
+ )
+
+/*++
+
+Routine Description:
+
+ This is the DLL initialization routine for netlogon.dll.
+
+Arguments:
+
+ Standard.
+
+Return Value:
+
+ TRUE iff initialization succeeded.
+
+--*/
+{
+ NTSTATUS Status;
+ UNREFERENCED_PARAMETER(DllHandle); // avoid compiler warnings
+ UNREFERENCED_PARAMETER(Context); // avoid compiler warnings
+
+
+ //
+ // Handle attaching netlogon.dll to a new process.
+ //
+
+ if (Reason == DLL_PROCESS_ATTACH) {
+
+ if ( !DisableThreadLibraryCalls( DllHandle ) ) {
+ KdPrint(("NETLOGON.DLL: DisableThreadLibraryCalls failed: %ld\n",
+ GetLastError() ));
+ }
+ Status = NlInitChangeLog();
+#if DBG
+ if ( !NT_SUCCESS( Status ) ) {
+ KdPrint(("NETLOGON.DLL: Changelog initialization failed: %lx\n",
+ Status ));
+ }
+#endif // DBG
+
+ //
+ // Initialize the Critical Section used to serialize access to
+ // variables shared by MSV threads and netlogon threads.
+ //
+
+ InitializeCriticalSection( &NlGlobalMsvCritSect );
+ NlGlobalMsvEnabled = FALSE;
+ NlGlobalMsvThreadCount = 0;
+ NlGlobalMsvTerminateEvent = NULL;
+
+
+ //
+ // Handle detaching netlogon.dll from a process.
+ //
+
+//
+// netlogon.dll never detaches
+//
+#ifdef NETLOGON_PROCESS_DETACH
+
+ } else if (Reason == DLL_PROCESS_DETACH) {
+ Status = NlCloseChangeLog();
+#if DBG
+ if ( !NT_SUCCESS( Status ) ) {
+ KdPrint(("NETLOGON.DLL: Changelog initialization failed: %lx\n",
+ Status ));
+ }
+#endif // DBG
+
+ //
+ // Delete the Critical Section used to serialize access to
+ // variables shared by MSV threads and netlogon threads.
+ //
+
+ DeleteCriticalSection( &NlGlobalMsvCritSect );
+#endif // NETLOGON_PROCESS_DETACH
+
+ } else {
+ Status = STATUS_SUCCESS;
+ }
+
+ return (BOOLEAN)(NT_SUCCESS(Status));
+
+}
+
+
+
+BOOLEAN
+NlInitDBSerialNumber(
+ IN OUT PLARGE_INTEGER SerialNumber,
+ IN OUT PLARGE_INTEGER CreationTime,
+ IN PUNICODE_STRING ReplicaSource,
+ IN DWORD DBIndex
+ )
+
+/*++
+
+Routine Description:
+
+ Set the SerialNumber and CreationTime in the NlGlobalDBInfoArray data
+ structure.
+
+ On the PDC,
+ Validate that it matches the value found in the change log.
+ Ensure the values are non-zero.
+
+Arguments:
+
+ SerialNumber - Specifies the serial number found in the database.
+ On return, specifies the serial number to write to the database
+
+ CreationTime - Specifies the creation time found in the database.
+ On return, specifies the creation time to write to the database
+
+ ReplicaSource - Specifies the replica source for the datbase.
+
+ DBIndex -- DB Index of the database being initialized
+
+Return Value:
+
+ TRUE -- iff the serial number and creation time need to be written back
+ to the database.
+
+--*/
+
+{
+ BOOLEAN ReturnValue = FALSE;
+
+ //
+ // Save the name of the Replica source.
+ //
+
+ wcsncpy( NlGlobalDBInfoArray[DBIndex].PrimaryName,
+ ReplicaSource->Buffer,
+ ReplicaSource->Length / sizeof(WCHAR) );
+
+ NlGlobalDBInfoArray[DBIndex].PrimaryName[
+ ReplicaSource->Length / sizeof(WCHAR) ] = L'\0';
+
+
+ //
+ // If we're running as the primary,
+ // check to see if we are a newly promoted primary that was in
+ // the middle of a full sync before we were promoted.
+ //
+
+ if (NlGlobalRole == RolePrimary) {
+
+ if ( SerialNumber->QuadPart == 0 || CreationTime->QuadPart == 0 ) {
+
+ NlPrint(( NL_CRITICAL,
+ "NlInitDbSerialNumber: " FORMAT_LPWSTR
+ ": Pdc has bogus Serial number %lx %lx or Creation Time %lx %lx (reset).\n",
+ NlGlobalDBInfoArray[DBIndex].DBName,
+ SerialNumber->HighPart,
+ SerialNumber->LowPart,
+ CreationTime->HighPart,
+ CreationTime->LowPart ));
+
+ //
+ // This is the primary,
+ // we probably shouldn't be replicating from a partial database,
+ // but at least set the replication information to something
+ // reasonable.
+ //
+
+ (VOID) NtQuerySystemTime( CreationTime );
+ SerialNumber->QuadPart = 1;
+ ReturnValue = TRUE;
+
+ }
+
+ NlGlobalDBInfoArray[DBIndex].UpdateRqd = FALSE;
+
+
+ //
+ // If we aren't the primary flag that an update is required,
+ //
+
+ } else {
+
+ //
+ // If we've never had a full sync on this database,
+ // force one now.
+ //
+
+ if ( ReplicaSource->Length == 0 ) {
+
+ //
+ // Set this flag so that we can pause the netlogon service
+ // when we do the full sync.
+ //
+ NlGlobalFirstTimeFullSync = TRUE;
+
+ NlGlobalDBInfoArray[DBIndex].UpdateRqd = TRUE;
+ NlGlobalDBInfoArray[DBIndex].FullSyncRequired = TRUE;
+
+ NlPrint(( NL_CRITICAL,
+ "NlInitDbSerialNumber: " FORMAT_LPWSTR
+ ": Force FULL SYNC because first sync after install.\n",
+ NlGlobalDBInfoArray[DBIndex].DBName ));
+
+ } else {
+
+ //
+ // If we were in the middle of a full sync when we stopped,
+ // continue it now.
+ //
+ if ( SerialNumber->QuadPart == 0 || CreationTime->QuadPart == 0 ) {
+
+ NlGlobalDBInfoArray[DBIndex].UpdateRqd = TRUE;
+ NlGlobalDBInfoArray[DBIndex].FullSyncRequired = TRUE;
+
+ NlPrint(( NL_CRITICAL,
+ "NlInitDbSerialNumber: " FORMAT_LPWSTR
+ " is marked as needing FULL SYNC.\n",
+ NlGlobalDBInfoArray[DBIndex].DBName ));
+
+ }
+
+ NlPrint(( NL_SYNC,
+ "NlInitDbSerialNumber: " FORMAT_LPWSTR
+ ": Last sync done from \\\\" FORMAT_LPWSTR "\n",
+ NlGlobalDBInfoArray[DBIndex].DBName,
+ NlGlobalDBInfoArray[DBIndex].PrimaryName ));
+
+ }
+
+ }
+
+
+
+ //
+ // The global serial number array has already been initialized
+ // from the changelog. If that information is wrong, just reset the
+ // changelog now.
+ //
+
+
+ LOCK_CHANGELOG();
+
+ //
+ // If there was no serial number in the changelog for this database,
+ // set it now.
+ //
+
+ if ( NlGlobalChangeLogDesc.SerialNumber[DBIndex].QuadPart == 0 ) {
+
+ NlPrint((NL_SYNC, "NlInitDbSerialNumber: " FORMAT_LPWSTR
+ ": No serial number in change log (set to %lx %lx)\n",
+ NlGlobalDBInfoArray[DBIndex].DBName,
+ SerialNumber->HighPart,
+ SerialNumber->LowPart ));
+
+
+ NlGlobalChangeLogDesc.SerialNumber[DBIndex] = *SerialNumber;
+
+ //
+ // If the serial number in the changelog is greater than the
+ // serial number in the database, this is caused by the changelog
+ // being flushed to disk and the SAM database not being flushed.
+ //
+ // Cure this problem by deleting the superfluous changelog entries.
+ //
+
+ } else if ( NlGlobalChangeLogDesc.SerialNumber[DBIndex].QuadPart !=
+ SerialNumber->QuadPart ) {
+
+ NlPrint((NL_SYNC, "NlInitDbSerialNumber: " FORMAT_LPWSTR
+ ": Changelog serial number different than database: "
+ "ChangeLog = %lx %lx DB = %lx %lx\n",
+ NlGlobalDBInfoArray[DBIndex].DBName,
+ NlGlobalChangeLogDesc.SerialNumber[DBIndex].HighPart,
+ NlGlobalChangeLogDesc.SerialNumber[DBIndex].LowPart,
+ SerialNumber->HighPart,
+ SerialNumber->LowPart ));
+
+ (VOID) NlFixChangeLog( &NlGlobalChangeLogDesc, DBIndex, *SerialNumber, FALSE );
+
+ } else {
+
+ NlPrint((NL_SYNC, "NlInitDbSerialNumber: " FORMAT_LPWSTR
+ ": Serial number is %lx %lx\n",
+ NlGlobalDBInfoArray[DBIndex].DBName,
+ SerialNumber->HighPart,
+ SerialNumber->LowPart ));
+ }
+
+ //
+ // In all cases,
+ // set the globals to match the database.
+ //
+
+ NlGlobalChangeLogDesc.SerialNumber[DBIndex] = *SerialNumber;
+ NlGlobalDBInfoArray[DBIndex].CreationTime = *CreationTime;
+
+ UNLOCK_CHANGELOG();
+
+
+ return ReturnValue;
+}
+
+
+NTSTATUS
+NlInitLsaDBInfo(
+ DWORD DBIndex
+ )
+
+/*++
+
+Routine Description:
+
+ Initialize NlGlobalDBInfoArray data structure. Some of the LSA
+ database info is already determined in ValidateStartup functions, so
+ those values are used here.
+
+Arguments:
+
+ DBIndex -- DB Index of the database being initialized
+
+Return Value:
+
+ NT status code.
+
+--*/
+
+{
+
+ NTSTATUS Status;
+ PLSAPR_POLICY_INFORMATION PolicyInfo = NULL;
+
+ //
+ // Initialize LSA database info.
+ //
+
+ NlGlobalDBInfoArray[DBIndex].DBIndex = DBIndex;
+ NlGlobalDBInfoArray[DBIndex].DBName = L"LSA";
+ NlGlobalDBInfoArray[DBIndex].DBSessionFlag = SS_LSA_REPL_NEEDED;
+
+ //
+ // Database ID field contains nothing for LSA database since
+ // there will be only one LSA database on the system.
+ //
+
+ NlGlobalDBInfoArray[DBIndex].DBId = NULL;
+
+ NlGlobalDBInfoArray[DBIndex].DBHandle = NlGlobalPolicyHandle;
+
+ //
+ // Forgo this initialization on a workstation.
+ //
+
+ if ( NlGlobalRole != RoleMemberWorkstation ) {
+ LARGE_INTEGER SerialNumber;
+ LARGE_INTEGER CreationTime;
+
+ //
+ // Get the replica source name
+ //
+
+ Status = LsarQueryInformationPolicy(
+ NlGlobalDBInfoArray[DBIndex].DBHandle,
+ PolicyReplicaSourceInformation,
+ &PolicyInfo );
+
+ if ( !NT_SUCCESS(Status) ) {
+ PolicyInfo = NULL;
+ goto Cleanup;
+ }
+
+ //
+ // Get the LSA Modified information.
+ //
+
+ Status = LsaIGetSerialNumberPolicy(
+ NlGlobalDBInfoArray[DBIndex].DBHandle,
+ &SerialNumber,
+ &CreationTime );
+
+ if ( !NT_SUCCESS(Status) ) {
+ goto Cleanup;
+ }
+
+ //
+ // Set the SerialNumber and CreationTime in the globals.
+ //
+
+ if ( NlInitDBSerialNumber(
+ &SerialNumber,
+ &CreationTime,
+ (PUNICODE_STRING)&PolicyInfo->PolicyReplicaSourceInfo.ReplicaSource,
+ DBIndex ) ) {
+
+
+ Status = LsaISetSerialNumberPolicy(
+ NlGlobalDBInfoArray[DBIndex].DBHandle,
+ &SerialNumber,
+ &CreationTime,
+ (BOOLEAN) FALSE );
+
+ if ( !NT_SUCCESS(Status) ) {
+ goto Cleanup;
+ }
+
+ }
+ }
+
+Cleanup:
+
+ if ( PolicyInfo != NULL ) {
+ LsaIFree_LSAPR_POLICY_INFORMATION(
+ PolicyReplicaSourceInformation,
+ PolicyInfo );
+ }
+
+ return Status;
+
+}
+
+
+NTSTATUS
+NlInitSamDBInfo(
+ DWORD DBIndex,
+ PSID DomainId
+ )
+
+/*++
+
+Routine Description:
+
+ Initialize NlGlobalDBInfoArray data structure. Some of the SAM database
+ info is already determined in ValidateStartup functions, so those
+ values are used here. For BUILTIN database, the database is opened,
+ database handle is obtained and other DB info
+ queried and initialized in this function.
+
+Arguments:
+
+ DBIndex -- DB Index of the database being initialized
+
+ DomainId -- Domain Sid of the database to open/initialize.
+
+Return Value:
+
+ NT status code.
+
+--*/
+
+{
+
+ NTSTATUS Status;
+ PSAMPR_DOMAIN_INFO_BUFFER DomainModified = NULL;
+ PSAMPR_DOMAIN_INFO_BUFFER DomainServerRole = NULL;
+ PSAMPR_DOMAIN_INFO_BUFFER DomainReplica = NULL;
+
+ BOOLEAN FixRole = FALSE;
+ DOMAIN_SERVER_ROLE DesiredRole;
+
+
+
+ //
+ // Initialize SAM database info.
+ //
+
+ NlGlobalDBInfoArray[DBIndex].DBIndex = DBIndex;
+ if ( DBIndex == SAM_DB ) {
+ NlGlobalDBInfoArray[DBIndex].DBName = L"SAM";
+ NlGlobalDBInfoArray[DBIndex].DBSessionFlag = SS_ACCOUNT_REPL_NEEDED;
+ } else {
+ NlGlobalDBInfoArray[DBIndex].DBName = L"BUILTIN";
+ NlGlobalDBInfoArray[DBIndex].DBSessionFlag = SS_BUILTIN_REPL_NEEDED;
+ }
+
+ NlGlobalDBInfoArray[DBIndex].DBId = NetpMemoryAllocate( RtlLengthSid( DomainId ));
+
+ if ( NlGlobalDBInfoArray[DBIndex].DBId == NULL ) {
+ return STATUS_NO_MEMORY;
+ }
+
+ RtlCopyMemory( NlGlobalDBInfoArray[DBIndex].DBId,
+ DomainId,
+ RtlLengthSid( DomainId ));
+
+ //
+ // Open the domain.
+ //
+
+ Status = SamrOpenDomain( NlGlobalSamServerHandle,
+ DOMAIN_ALL_ACCESS,
+ NlGlobalDBInfoArray[DBIndex].DBId,
+ &NlGlobalDBInfoArray[DBIndex].DBHandle );
+
+ if ( !NT_SUCCESS(Status) ) {
+ NlGlobalDBInfoArray[DBIndex].DBHandle = NULL;
+ goto Cleanup;
+ }
+
+
+
+ //
+ // Ensure the role in SAM is compatible with Netlogon's role
+ //
+
+ Status = SamrQueryInformationDomain( NlGlobalDBInfoArray[DBIndex].DBHandle,
+ DomainServerRoleInformation,
+ &DomainServerRole );
+ if ( !NT_SUCCESS(Status) ) {
+ DomainServerRole = NULL;
+ goto Cleanup;
+ }
+
+ switch ( NlGlobalRole ) {
+ case RolePrimary:
+ case RoleMemberWorkstation:
+ if ( DomainServerRole->Role.DomainServerRole != DomainServerRolePrimary ) {
+ FixRole = TRUE;
+ DesiredRole = DomainServerRolePrimary;
+ }
+ break;
+ case RoleBackup:
+ if ( DomainServerRole->Role.DomainServerRole != DomainServerRoleBackup ) {
+ FixRole = TRUE;
+ DesiredRole = DomainServerRoleBackup;
+ }
+ break;
+
+ default:
+ Status = STATUS_INVALID_DOMAIN_ROLE;
+ goto Cleanup;
+ }
+
+ if ( FixRole) {
+ NlPrint(( NL_CRITICAL,
+ "NlInitSamDbInfo: " FORMAT_LPWSTR
+ ": Role is %ld which doesn't match LSA's role. (Fixed)\n",
+ NlGlobalDBInfoArray[DBIndex].DBName,
+ DomainServerRole->Role.DomainServerRole ));
+
+ DomainServerRole->Role.DomainServerRole = DesiredRole;
+
+ Status = SamrSetInformationDomain(
+ NlGlobalDBInfoArray[DBIndex].DBHandle,
+ DomainServerRoleInformation,
+ DomainServerRole );
+
+ if ( !NT_SUCCESS(Status) ) {
+ DomainServerRole = NULL;
+ goto Cleanup;
+ }
+ }
+
+
+
+ //
+ // Forgo this initialization on a workstation.
+ //
+
+ if ( NlGlobalRole != RoleMemberWorkstation ) {
+
+ //
+ // Get the replica source name.
+ //
+
+ Status = SamrQueryInformationDomain(
+ NlGlobalDBInfoArray[DBIndex].DBHandle,
+ DomainReplicationInformation,
+ &DomainReplica );
+
+ if ( !NT_SUCCESS(Status) ) {
+ DomainReplica = NULL;
+ goto Cleanup;
+ }
+
+ //
+ // Get the Domain Modified information.
+ //
+
+ Status = SamrQueryInformationDomain(
+ NlGlobalDBInfoArray[DBIndex].DBHandle,
+ DomainModifiedInformation2,
+ &DomainModified );
+
+ if ( !NT_SUCCESS(Status) ) {
+ DomainModified = NULL;
+ goto Cleanup;
+ }
+
+ //
+ // Set the SerialNumber and CreationTime in the globals.
+ //
+
+ if ( NlInitDBSerialNumber(
+ &DomainModified->Modified2.DomainModifiedCount,
+ &DomainModified->Modified2.CreationTime,
+ (PUNICODE_STRING)&DomainReplica->Replication.ReplicaSourceNodeName,
+ DBIndex ) ) {
+
+ Status = SamISetSerialNumberDomain(
+ NlGlobalDBInfoArray[DBIndex].DBHandle,
+ &DomainModified->Modified2.DomainModifiedCount,
+ &DomainModified->Modified2.CreationTime,
+ (BOOLEAN) FALSE );
+
+ if ( !NT_SUCCESS(Status) ) {
+ goto Cleanup;
+ }
+ }
+ }
+
+Cleanup:
+
+ //
+ // Free locally used resources.
+ //
+ if ( DomainModified != NULL ) {
+ SamIFree_SAMPR_DOMAIN_INFO_BUFFER( DomainModified,
+ DomainModifiedInformation2 );
+ }
+
+ if ( DomainServerRole != NULL ) {
+ SamIFree_SAMPR_DOMAIN_INFO_BUFFER( DomainServerRole,
+ DomainServerRoleInformation);
+ }
+
+ if ( DomainReplica != NULL ) {
+ SamIFree_SAMPR_DOMAIN_INFO_BUFFER( DomainReplica,
+ DomainReplicationInformation );
+ }
+
+ return Status;
+
+}
+
+
+BOOL
+NlSetDomainName(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This routine gets the primary domain name from the LSA and stores
+ that name in global variables.
+
+Arguments:
+
+ NONE.
+
+Return Value:
+
+ TRUE -- Iff the domain name can be saved.
+
+--*/
+{
+ NTSTATUS Status;
+
+ PLSAPR_POLICY_INFORMATION PolicyInfo;
+
+
+ //
+ // Get the Primary Domain Name from the LSA.
+ //
+
+ Status = LsarQueryInformationPolicy(
+ NlGlobalPolicyHandle,
+ PolicyPrimaryDomainInformation,
+ &PolicyInfo );
+
+ if ( !NT_SUCCESS(Status) ) {
+ NlExit( SERVICE_UIC_M_DATABASE_ERROR, Status, LogError, NULL);
+ return FALSE;
+ }
+
+ if ( PolicyInfo->PolicyPrimaryDomainInfo.Name.Length == 0 ||
+ PolicyInfo->PolicyPrimaryDomainInfo.Name.Length >
+ DNLEN * sizeof(WCHAR) ||
+ PolicyInfo->PolicyPrimaryDomainInfo.Sid == NULL ) {
+
+ LsaIFree_LSAPR_POLICY_INFORMATION(
+ PolicyPrimaryDomainInformation,
+ PolicyInfo );
+
+ NlPrint((NL_CRITICAL, "Primary domain info from LSA invalid.\n"));
+ NlExit( SERVICE_UIC_M_UAS_INVALID_ROLE, 0, LogError, NULL);
+ return FALSE;
+ }
+
+
+ //
+ // Copy name to the globals.
+ //
+
+ RtlCopyMemory( NlGlobalUnicodeDomainName,
+ PolicyInfo->PolicyPrimaryDomainInfo.Name.Buffer,
+ PolicyInfo->PolicyPrimaryDomainInfo.Name.Length );
+
+ NlGlobalUnicodeDomainName[
+ PolicyInfo->PolicyPrimaryDomainInfo.Name.Length /
+ sizeof(WCHAR)] = L'\0';
+
+ RtlInitUnicodeString( &NlGlobalUnicodeDomainNameString,
+ NlGlobalUnicodeDomainName);
+
+ //
+ // This routine is only called once during initialization so previous
+ // storage need not be freed.
+ //
+
+ NlGlobalAnsiDomainName =
+ NetpLogonUnicodeToOem( NlGlobalUnicodeDomainName);
+
+ if ( NlGlobalAnsiDomainName == NULL ) {
+
+ LsaIFree_LSAPR_POLICY_INFORMATION(
+ PolicyPrimaryDomainInformation,
+ PolicyInfo );
+
+ NlExit( SERVICE_UIC_RESOURCE, ERROR_NOT_ENOUGH_MEMORY, LogError, NULL);
+ return FALSE;
+ }
+
+ //
+ // Save the SID in a global
+ //
+
+ NlGlobalPrimaryDomainId = NetpMemoryAllocate(
+ RtlLengthSid( (PSID)PolicyInfo->PolicyPrimaryDomainInfo.Sid ));
+
+ if ( NlGlobalPrimaryDomainId == NULL ) {
+
+ LsaIFree_LSAPR_POLICY_INFORMATION(
+ PolicyPrimaryDomainInformation,
+ PolicyInfo );
+
+ NlExit( SERVICE_UIC_RESOURCE, ERROR_NOT_ENOUGH_MEMORY, LogError, NULL);
+ return FALSE;
+ }
+
+ RtlCopyMemory( NlGlobalPrimaryDomainId,
+ PolicyInfo->PolicyPrimaryDomainInfo.Sid,
+ RtlLengthSid( PolicyInfo->PolicyPrimaryDomainInfo.Sid ));
+
+ LsaIFree_LSAPR_POLICY_INFORMATION(
+ PolicyPrimaryDomainInformation,
+ PolicyInfo );
+
+
+ return TRUE;
+}
+
+
+
+BOOL
+NlInitWorkstation(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ Do workstation specific initialization.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ TRUE -- iff initialization is successful.
+
+--*/
+{
+ //
+ // Ensure the primary and account domain ID are different.
+ //
+
+ if ( RtlEqualSid( NlGlobalDBInfoArray[SAM_DB].DBId, NlGlobalPrimaryDomainId ) ) {
+
+ LPWSTR AlertStrings[3];
+
+ //
+ // alert admin.
+ //
+
+ AlertStrings[0] = NlGlobalUnicodeComputerName;
+ AlertStrings[1] = NlGlobalUnicodeDomainName;
+ AlertStrings[2] = NULL;
+
+ //
+ // Save the info in the eventlog
+ //
+
+ NlpWriteEventlog(
+ ALERT_NetLogonSidConflict,
+ EVENTLOG_ERROR_TYPE,
+ NlGlobalPrimaryDomainId,
+ RtlLengthSid( NlGlobalPrimaryDomainId ),
+ AlertStrings,
+ 2 );
+
+ //
+ // This isn't fatal. (Just drop through)
+ //
+ }
+
+
+ //
+ // Set up the Client Session structure to identify the domain and
+ // account used to talk to the DC.
+ //
+
+ if ( !GiveInstallHints( FALSE ) ) {
+ return FALSE;
+ }
+
+ NlGlobalClientSession = NlAllocateClientSession(
+ &NlGlobalUnicodeDomainNameString,
+ NlGlobalPrimaryDomainId,
+ WorkstationSecureChannel );
+
+ if ( NlGlobalClientSession == NULL ) {
+ NlExit( SERVICE_UIC_RESOURCE, ERROR_NOT_ENOUGH_MEMORY, LogError, NULL);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+
+BOOL
+NlInitDomainController(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ Do Domain Controller specific initialization.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ TRUE -- iff initialization is successful.
+
+--*/
+{
+ NTSTATUS Status;
+ NET_API_STATUS NetStatus;
+ WCHAR ChangeLogFile[PATHLEN+1];
+
+ LPWSTR DCName;
+ DWORD Version;
+
+ BOOL DeferAuth = FALSE;
+
+
+
+ //
+ // Handle if there is no other PDC running in this domain.
+ //
+
+ if ( !GiveInstallHints( FALSE ) ) {
+ return FALSE;
+ }
+
+ NetStatus = NetpLogonGetDCName( NlGlobalUnicodeComputerName,
+ NlGlobalUnicodeDomainName,
+ 0,
+ &DCName,
+ &Version );
+
+ if ( !GiveInstallHints( FALSE ) ) {
+ return FALSE;
+ }
+
+ if ( NetStatus != NERR_Success) {
+
+ //
+ // If we are the first primary in the domain,
+ // Remember that we are the primary.
+ //
+
+ if (NlGlobalRole == RolePrimary) {
+
+ if ( !NlSetPrimaryName( NlGlobalUnicodeComputerName ) ) {
+ NlExit( SERVICE_UIC_M_DATABASE_ERROR, 0, LogError, NULL);
+ return FALSE;
+ }
+
+
+ //
+ // Handle starting a BDC when there is not current primary in
+ // this domain.
+ //
+
+ } else if ( NlGlobalRole == RoleBackup ) {
+
+ NlpWriteEventlog( SERVICE_UIC_M_NETLOGON_NO_DC,
+ EVENTLOG_ERROR_TYPE,
+ NULL,
+ 0,
+ NULL,
+ 0 );
+
+ //
+ // Start normally but defer authentication with the
+ // primary until it starts.
+ //
+
+ DeferAuth = TRUE;
+
+ }
+
+ //
+ // There is a primary dc running in this domain
+ //
+
+ } else {
+
+ //
+ // Since there already is a primary in the domain,
+ // we cannot become the primary.
+ //
+
+ if (NlGlobalRole == RolePrimary) {
+
+ //
+ // Don't worry if this is a BDC telling us that we're the PDC.
+ //
+
+ if ( NlNameCompare( NlGlobalUnicodeComputerName,
+ DCName + 2,
+ NAMETYPE_COMPUTER) != 0 ){
+ NlExit(SERVICE_UIC_M_NETLOGON_DC_CFLCT, 0, LogError, NULL);
+ (VOID) NetApiBufferFree( DCName );
+ return FALSE;
+ }
+
+ } else {
+
+ //
+ // If the primary is NOT an NT primary,
+ // refuse to start.
+ //
+ // An NT BDC or member server cannot replicate from a downlevel PDC.
+ //
+
+ if ( Version != LMNT_MESSAGE ) {
+ PSERVER_INFO_101 ServerInfo101 = NULL;
+
+ //
+ // This might just be a LM 2.1A (or newer) BDC responding on
+ // behalf of an NT PDC.
+ //
+ // Ask the PDC if it is NT.
+ //
+
+ NetStatus = NetServerGetInfo( DCName,
+ 101,
+ (LPBYTE *)&ServerInfo101 );
+ if ( NetStatus != NERR_Success ) {
+ NlPrint((NL_CRITICAL,
+ "can't NetServerGetInfo on primary " FORMAT_LPWSTR
+ " %ld.\n",
+ DCName,
+ NetStatus ));
+ (VOID) NetApiBufferFree( DCName );
+ NlExit(SERVICE_UIC_M_NETLOGON_NO_DC, NetStatus, LogError, NULL);
+ return FALSE;
+ }
+
+ if ( (ServerInfo101->sv101_type & SV_TYPE_DOMAIN_CTRL) == 0 ) {
+ NetApiBufferFree( ServerInfo101 );
+ NlPrint((NL_CRITICAL, "PDC " FORMAT_LPWSTR " really isn't a PDC\n",
+ DCName ));
+ (VOID) NetApiBufferFree( DCName );
+ NlExit(SERVICE_UIC_M_NETLOGON_NO_DC, 0, LogError, NULL);
+ return FALSE;
+ }
+
+ if ( (ServerInfo101->sv101_type & SV_TYPE_NT) == 0 ) {
+ NetApiBufferFree( ServerInfo101 );
+ NlPrint((NL_CRITICAL, "PDC " FORMAT_LPWSTR
+ " really isn't an NT PDC\n", DCName ));
+ (VOID) NetApiBufferFree( DCName );
+ NlExit(SERVICE_UIC_M_NETLOGON_NO_DC, 0, LogError, NULL);
+ return FALSE;
+ }
+ NetApiBufferFree( ServerInfo101 );
+ }
+
+ }
+
+ //
+ // Remember this primary name.
+ //
+
+ if ( !NlSetPrimaryName( DCName + 2 ) ) {
+ NlExit(SERVICE_UIC_M_DATABASE_ERROR, 0, LogError, NULL);
+ (VOID) NetApiBufferFree( DCName );
+ return FALSE;
+ }
+
+ (VOID) NetApiBufferFree( DCName );
+
+ }
+
+
+ //
+ // Open the browser so we can send and receive mailslot messages.
+ //
+
+ if ( !GiveInstallHints( FALSE ) ) {
+ return FALSE;
+ }
+
+ if ( !NlBrowserOpen() ) {
+ return FALSE;
+ }
+
+
+
+ //
+ // Here ensure that the Secret Password exists.
+ // (If the secret password doesn't exist, we'll never be able
+ // to establish a session to the PDC and netlogon shouldn't be
+ // running).
+ //
+
+ if ( NlGlobalRole == RoleBackup ) {
+
+ LSAPR_HANDLE SecretHandle;
+
+ //
+ // Set up the Client Session structure to identify the domain and
+ // account used to talk to the PDC.
+ //
+
+ NlGlobalClientSession = NlAllocateClientSession(
+ &NlGlobalUnicodeDomainNameString,
+ NlGlobalPrimaryDomainId,
+ ServerSecureChannel );
+
+ if ( NlGlobalClientSession == NULL ) {
+ NlExit( SERVICE_UIC_RESOURCE, ERROR_NOT_ENOUGH_MEMORY, LogError, NULL);
+ return FALSE;
+ }
+
+ Status = NlOpenSecret( NlGlobalClientSession,
+ SECRET_QUERY_VALUE | SECRET_SET_VALUE,
+ &SecretHandle );
+
+ if ( !NT_SUCCESS(Status) ) {
+ NlExit( SERVICE_UIC_M_LSA_MACHINE_ACCT, Status, LogError, NULL );
+ return FALSE;
+ }
+
+ (VOID) LsarClose( &SecretHandle );
+
+ }
+
+ //
+ // Check that the server is installed or install pending
+ //
+
+ if ( !GiveInstallHints( FALSE ) ) {
+ return FALSE;
+ }
+
+ if ( !NetpIsServiceStarted( SERVICE_SERVER ) ){
+ NlExit( NERR_ServerNotStarted, 0, LogError, NULL);
+ return FALSE;
+ }
+
+ //
+ // Allocate & initialize the data structs and storage for replication
+ //
+
+
+ Status = NlInitSSI();
+ if ( !NT_SUCCESS(Status) ) {
+ NlExit( NELOG_NetlogonSSIInitError, Status, LogErrorAndNtStatus, NULL);
+ return FALSE;
+ }
+
+ //
+ // Create the event the replicator thread waits on to terminate.
+ //
+
+ NlGlobalReplicatorTerminateEvent = CreateEvent(
+ NULL, // No security attributes
+ TRUE, // Must be manually reset
+ FALSE, // Initially not signaled
+ NULL ); // No name
+
+ if ( NlGlobalReplicatorTerminateEvent == NULL ) {
+ NetStatus = GetLastError();
+ NlPrint((NL_CRITICAL, "Cannot create replicator termination Event %lu\n",
+ NetStatus ));
+ NlExit( NELOG_NetlogonSystemError, NetStatus, LogErrorAndNetStatus, NULL);
+ return FALSE;
+ }
+
+ //
+ // On a BDC, set up a session to the PDC now.
+ //
+ // If the PDC was previously found,
+ // require now that we successfully establish a session
+ // else
+ // wait till the PDC identifies itself
+ //
+
+ if (NlGlobalRole == RoleBackup ) {
+
+ if ( !DeferAuth ) {
+
+ if ( !GiveInstallHints( FALSE ) ) {
+ return FALSE;
+ }
+ (VOID) NlTimeoutSetWriterClientSession( NlGlobalClientSession, 0xFFFFFFFF );
+ Status = NlSessionSetup( NlGlobalClientSession );
+ NlResetWriterClientSession( NlGlobalClientSession );
+
+ //
+ // Treat it as fatal if the PDC explicitly denies us access.
+ //
+
+ if ( Status == STATUS_NO_TRUST_SAM_ACCOUNT ||
+ Status == STATUS_ACCESS_DENIED ) {
+
+ // NlSessionSetup already logged the error
+ NlExit( NetpNtStatusToApiStatus(Status), 0, DontLogError, NULL);
+ return FALSE;
+ }
+ }
+
+ }
+
+
+ //
+ // Determine the trust list from the LSA.
+ //
+ if ( !GiveInstallHints( FALSE ) ) {
+ return FALSE;
+ }
+
+ Status = NlInitTrustList();
+
+ if ( !NT_SUCCESS(Status) ) {
+ NlExit( NELOG_NetlogonFailedToUpdateTrustList, Status, LogErrorAndNtStatus, NULL);
+ return FALSE;
+ }
+
+ //
+ // Create NETLOGON share.
+ //
+
+ if ( !GiveInstallHints( FALSE ) ) {
+ return FALSE;
+ }
+
+ NetStatus = NlCreateShare( NlGlobalUnicodeScriptPath,
+ NETLOGON_SCRIPTS_SHARE) ;
+
+ if ( NetStatus != NERR_Success ) {
+ LPWSTR MsgStrings[2];
+
+ NlPrint((NL_CRITICAL, "NlCreateShare %lu\n", NetStatus ));
+
+ MsgStrings[0] = NlGlobalUnicodeScriptPath;
+ MsgStrings[1] = (LPWSTR) NetStatus;
+
+ NlpWriteEventlog (NELOG_NetlogonFailedToCreateShare,
+ EVENTLOG_ERROR_TYPE,
+ (LPBYTE) &NetStatus,
+ sizeof(NetStatus),
+ MsgStrings,
+ 2 | LAST_MESSAGE_IS_NETSTATUS );
+
+ /* This isn't fatal. Just continue */
+ }
+
+#if DBG
+
+ //
+ // create debug share. Ignore error.
+ //
+
+ if( NlCreateShare(
+ NlGlobalDebugSharePath,
+ DEBUG_SHARE_NAME ) != NERR_Success ) {
+ NlPrint((NL_CRITICAL, "Can't create Debug share (%ws, %ws).\n",
+ NlGlobalDebugSharePath, DEBUG_SHARE_NAME ));
+ }
+
+#endif
+
+ //
+ // If a redo log exists,
+ // open it on a BDC,
+ // delete it on a PDC.
+ //
+
+ wcscpy( ChangeLogFile, NlGlobalChangeLogFilePrefix );
+ wcscat( ChangeLogFile, REDO_FILE_POSTFIX );
+
+ if (NlGlobalRole == RoleBackup ) {
+
+ //
+ // Read in the existing redo log file.
+ //
+ // It's OK if the file simply doesn't exist,
+ // we'll create it when we need it.
+ //
+
+ Status = NlOpenChangeLogFile( ChangeLogFile, &NlGlobalRedoLogDesc, FALSE );
+
+ if ( !NT_SUCCESS(Status) && Status != STATUS_NO_SUCH_FILE ) {
+
+ NlpWriteEventlog (
+ NELOG_NetlogonChangeLogCorrupt,
+ EVENTLOG_ERROR_TYPE,
+ (LPBYTE)&Status,
+ sizeof(Status),
+ NULL,
+ 0 );
+
+ NlPrint((NL_CRITICAL, "Deleting broken redo log\n" ));
+
+ (VOID) DeleteFileW( ChangeLogFile );
+
+ }
+
+ } else {
+ (VOID) DeleteFileW( ChangeLogFile );
+ }
+
+ //
+ // Successful initialization.
+ //
+
+ return TRUE;
+}
+
+
+ULONG
+NlServerType(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ Determines server type, that is used to set in service table.
+
+Arguments:
+
+ none.
+
+Return Value:
+
+ SV_TYPE_DOMAIN_CTRL if role is primary domain controller
+ SV_TYPE_DOMAIN_BAKCTRL if backup
+ 0 if none of the above
+
+
+--*/
+{
+ switch (NlGlobalRole) {
+ case RolePrimary:
+ return SV_TYPE_DOMAIN_CTRL;
+ case RoleBackup:
+ return SV_TYPE_DOMAIN_BAKCTRL;
+ default:
+ return 0;
+ }
+}
+
+
+
+BOOL
+NlInit(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ Initialize NETLOGON service related data structs after verfiying that
+ all conditions for startup have been satisfied. Will also create a
+ mailslot to listen to requests from clients and create two shares to
+ allow execution of logon scripts.
+
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ TRUE -- iff initialization is successful.
+
+--*/
+{
+ NTSTATUS Status;
+ NET_API_STATUS NetStatus;
+
+ LARGE_INTEGER TimeNow;
+ OBJECT_ATTRIBUTES EventAttributes;
+ UNICODE_STRING EventName;
+
+ PSAMPR_DOMAIN_INFO_BUFFER DomainInfo = NULL;
+
+ PLSAPR_POLICY_INFORMATION PolicyLsaServerRole = NULL;
+ PLSAPR_POLICY_INFORMATION PolicyAccountDomainInfo = NULL;
+
+ NT_PRODUCT_TYPE NtProductType;
+ DWORD ComputerNameLength;
+
+
+
+ //
+ // Let the ChangeLog routines know that Netlogon is started.
+ //
+
+ NlGlobalChangeLogNetlogonState = NetlogonStarting;
+
+
+ //
+ // seed the pseudo random number generator
+ //
+
+ (VOID) NtQuerySystemTime( &TimeNow );
+ srand( TimeNow.LowPart );
+
+
+ //
+ // Check that the redirector is installed, will exit on error.
+ //
+
+ if ( !GiveInstallHints( FALSE ) ) {
+ return FALSE;
+ }
+
+ if ( !NetpIsServiceStarted( SERVICE_WORKSTATION ) ){
+ NlExit( NERR_WkstaNotStarted, 0, LogError, NULL);
+ return FALSE;
+ }
+
+
+ //
+ // Get the local computer name.
+ //
+
+ if ( !GiveInstallHints( FALSE ) ) {
+ return FALSE;
+ }
+
+ NlGlobalUncUnicodeComputerName[0] = '\\';
+ NlGlobalUncUnicodeComputerName[1] = '\\';
+
+ ComputerNameLength =
+ (sizeof(NlGlobalUncUnicodeComputerName)/sizeof(WCHAR)) - 2;
+
+ if ( !GetComputerNameW( NlGlobalUncUnicodeComputerName+2,
+ &ComputerNameLength ) ) {
+ NlExit( NELOG_NetlogonSystemError, GetLastError(), LogErrorAndNetStatus, NULL);
+ return FALSE;
+ }
+
+ NlGlobalUnicodeComputerName = NlGlobalUncUnicodeComputerName + 2;
+
+ RtlInitUnicodeString( &NlGlobalUnicodeComputerNameString,
+ NlGlobalUnicodeComputerName );
+
+ NlGlobalAnsiComputerName =
+ NetpLogonUnicodeToOem( NlGlobalUnicodeComputerName );
+ if ( NlGlobalAnsiComputerName == NULL ) {
+ NlExit( SERVICE_UIC_RESOURCE, ERROR_NOT_ENOUGH_MEMORY, LogError, NULL);
+ return FALSE;
+ }
+
+
+ //
+ // Open the LSA.
+ //
+
+ Status = LsaIOpenPolicyTrusted( &NlGlobalPolicyHandle );
+
+ if ( !NT_SUCCESS(Status) ) {
+ NlExit( SERVICE_UIC_M_DATABASE_ERROR, Status, LogError, NULL);
+ return FALSE;
+ }
+
+ //
+ // Determine whether the Role of the local LSA is primary or backup.
+ //
+
+ Status = LsarQueryInformationPolicy(
+ NlGlobalPolicyHandle,
+ PolicyLsaServerRoleInformation,
+ &PolicyLsaServerRole );
+
+ if ( !NT_SUCCESS(Status) ) {
+ NlExit( SERVICE_UIC_M_DATABASE_ERROR, Status, LogError, NULL);
+ return FALSE;
+ }
+
+ switch (PolicyLsaServerRole->
+ PolicyServerRoleInfo.LsaServerRole) {
+ case PolicyServerRolePrimary:
+ NlGlobalRole = RolePrimary;
+ break;
+
+ case PolicyServerRoleBackup:
+ NlGlobalRole = RoleBackup;
+ break;
+
+ default:
+
+ LsaIFree_LSAPR_POLICY_INFORMATION(
+ PolicyLsaServerRoleInformation,
+ PolicyLsaServerRole );
+
+ NlExit( SERVICE_UIC_M_UAS_INVALID_ROLE, 0, LogError, NULL);
+ return FALSE;
+ }
+
+ LsaIFree_LSAPR_POLICY_INFORMATION(
+ PolicyLsaServerRoleInformation,
+ PolicyLsaServerRole );
+
+
+
+ //
+ // If this is Windows-NT running,
+ // change the role to RoleMemberWorkstation.
+ //
+ // All error conditions default to RoleMemberWorkstation.
+ //
+
+ if ( RtlGetNtProductType( &NtProductType ) ) {
+ if ( NtProductType != NtProductLanManNt ) {
+ NlGlobalRole = RoleMemberWorkstation;
+ }
+ } else {
+ NlGlobalRole = RoleMemberWorkstation;
+ }
+
+
+
+ //
+ // Get the Primary Domain name from LSA and save it in globals.
+ //
+
+ if ( !NlSetDomainName() ) {
+ return FALSE;
+ }
+
+ //
+ // If this is a workstation,
+ // get the cached trusted domain list from the registry.
+ //
+
+ if ( NlGlobalRole == RoleMemberWorkstation ) {
+ LPWSTR TrustedDomainList = NULL;
+ BOOL TrustedDomainListKnown;
+
+ //
+ // If NCPA has just joined a domain,
+ // and has pre-determined the trusted domain list for us,
+ // pick up that list.
+ //
+ // When this machine joins a domain,
+ // NCPA caches the trusted domain list where we can find it. That ensures the
+ // trusted domain list is available upon reboot even before we dial via RAS. Winlogon
+ // can therefore get the trusted domain list from us under those circumstances.
+ //
+
+ NetStatus = NlReadRegTrustedDomainList (
+ NlGlobalUnicodeDomainName,
+ TRUE, // Delete this registry key since we no longer need it.
+ &TrustedDomainList,
+ &TrustedDomainListKnown );
+
+
+ if ( NetStatus != NO_ERROR ) {
+ NlExit( SERVICE_UIC_M_DATABASE_ERROR, NetStatus, LogError, NULL);
+ return FALSE;
+ }
+
+ //
+ // If there is a cached list,
+ // Save it back in the registry for future starts.
+ //
+
+ if ( TrustedDomainListKnown ) {
+ NlPrint(( NL_INIT,
+ "Replacing trusted domain list with one for newly joined %ws domain.\n",
+ NlGlobalUnicodeDomainName));
+ NlSaveTrustedDomainList ( TrustedDomainList );
+
+ //
+ // Otherwise, read the current one from the registry.
+ //
+
+ } else {
+ NlPrint(( NL_INIT, "Getting cached trusted domain list from registry.\n" ));
+ NetStatus = NlReadRegTrustedDomainList (
+ NULL,
+ FALSE,
+ &TrustedDomainList,
+ &TrustedDomainListKnown );
+
+ if ( NetStatus != NO_ERROR ) {
+ NlExit( SERVICE_UIC_M_DATABASE_ERROR, NetStatus, LogError, NULL);
+ return FALSE;
+ }
+ }
+
+ //
+ // In all cases, set the trusted domain list into globals.
+ //
+
+ (VOID) NlSetTrustedDomainList( TrustedDomainList, TrustedDomainListKnown );
+
+ if ( TrustedDomainList != NULL ) {
+ NetApiBufferFree( TrustedDomainList );
+ }
+
+ }
+
+
+ //
+ // Determine the name of the local Sam Account database as the
+ // user reference it when logging on.
+ //
+ // On a workstation, it is the workstation name.
+ // On a DC, it is the primary domain name.
+ //
+
+ if ( NlGlobalRole == RoleMemberWorkstation ) {
+ RtlInitUnicodeString( &NlGlobalAccountDomainName,
+ NlGlobalUnicodeComputerName );
+ } else {
+ RtlInitUnicodeString( &NlGlobalAccountDomainName,
+ NlGlobalUnicodeDomainName );
+ }
+
+ //
+ // Initialize LSA database info.
+ //
+
+ Status = NlInitLsaDBInfo( LSA_DB );
+
+ if ( !NT_SUCCESS(Status) ) {
+ NlExit( SERVICE_UIC_M_DATABASE_ERROR, Status, LogError, NULL);
+ return FALSE;
+ }
+
+ //
+ // Compute the Domain ID of the SAM Account domain.
+ //
+
+ Status = LsarQueryInformationPolicy(
+ NlGlobalPolicyHandle,
+ PolicyAccountDomainInformation,
+ &PolicyAccountDomainInfo );
+
+ if ( !NT_SUCCESS(Status) ) {
+ NlExit( SERVICE_UIC_M_DATABASE_ERROR, Status, LogError, NULL);
+ return FALSE;
+ }
+
+ if ( PolicyAccountDomainInfo->PolicyAccountDomainInfo.DomainSid == NULL ) {
+
+ LsaIFree_LSAPR_POLICY_INFORMATION(
+ PolicyAccountDomainInformation,
+ PolicyAccountDomainInfo );
+
+ NlPrint((NL_CRITICAL, "Account domain info from LSA invalid.\n"));
+ NlExit( SERVICE_UIC_M_DATABASE_ERROR, 0, LogError, NULL);
+ return FALSE;
+ }
+
+
+
+
+ //
+ // Wait for SAM to start
+ //
+
+ if ( !GiveInstallHints( FALSE ) ) {
+ return FALSE;
+ }
+
+ if ( !NlWaitForSamService( TRUE ) ) {
+ NlExit( SERVICE_UIC_M_DATABASE_ERROR, 0, LogError, NULL);
+ return FALSE;
+ }
+
+
+ //
+ // Open our connection with SAM
+ //
+
+ if ( !GiveInstallHints( FALSE ) ) {
+ return FALSE;
+ }
+
+ Status = SamIConnect( NULL, // No server name
+ &NlGlobalSamServerHandle,
+ 0, // Ignore desired access
+ (BOOLEAN) TRUE ); // Indicate we are privileged
+
+ if ( !NT_SUCCESS(Status) ) {
+ NlGlobalSamServerHandle = NULL;
+ NlExit( SERVICE_UIC_M_DATABASE_ERROR, Status, LogError, NULL);
+ return FALSE;
+ }
+
+
+ //
+ // Open the Sam Account domain.
+ //
+
+ Status = NlInitSamDBInfo(
+ SAM_DB,
+ PolicyAccountDomainInfo->PolicyAccountDomainInfo.DomainSid );
+
+ LsaIFree_LSAPR_POLICY_INFORMATION(
+ PolicyAccountDomainInformation,
+ PolicyAccountDomainInfo );
+
+ if ( !NT_SUCCESS(Status) ) {
+ NlExit( SERVICE_UIC_M_DATABASE_ERROR, Status, LogError, NULL);
+ return FALSE;
+ }
+
+
+ //
+ // Create well know SID for netlogon.dll
+ //
+
+ Status = NetpCreateWellKnownSids( NULL );
+
+ if( !NT_SUCCESS( Status ) ) {
+ NetStatus = NetpNtStatusToApiStatus( Status );
+ NlExit( SERVICE_UIC_RESOURCE, NetStatus, LogError, NULL);
+ return FALSE;
+ }
+
+
+ //
+ // Create the security descriptors we'll use for the APIs
+ //
+
+ Status = NlCreateNetlogonObjects();
+
+ if ( !NT_SUCCESS(Status) ) {
+ NlExit( NELOG_NetlogonSystemError, Status, LogErrorAndNtStatus, NULL);
+ return FALSE;
+ }
+
+
+
+ //
+ // Open the SAM Builtin domain.
+ //
+
+ Status = NlInitSamDBInfo( BUILTIN_DB, BuiltinDomainSid );
+
+ if ( !NT_SUCCESS(Status) ) {
+ NlExit( SERVICE_UIC_M_DATABASE_ERROR, Status, LogError, NULL);
+ return FALSE;
+ }
+
+
+
+ //
+ // Get our UAS compatibility mode from SAM
+ //
+
+ Status = SamrQueryInformationDomain(
+ NlGlobalDBInfoArray[SAM_DB].DBHandle,
+ DomainGeneralInformation,
+ &DomainInfo );
+
+ if (!NT_SUCCESS(Status)) {
+ DomainInfo = NULL;
+ NlExit( SERVICE_UIC_M_DATABASE_ERROR, Status, LogError, NULL);
+ return FALSE;
+ }
+
+ NlGlobalUasCompatibilityMode = DomainInfo->General.UasCompatibilityRequired;
+ IF_DEBUG( CRITICAL ) {
+ if ( !NlGlobalUasCompatibilityMode ) {
+ NlPrint((NL_CRITICAL, "ERROR: UasCompatibility mode is off.\n"));
+ }
+ }
+
+ SamIFree_SAMPR_DOMAIN_INFO_BUFFER( DomainInfo, DomainGeneralInformation );
+
+
+
+ //
+ // Create Timer event
+ //
+
+ NlGlobalTimerEvent = CreateEvent(
+ NULL, // No special security
+ FALSE, // Auto Reset
+ FALSE, // No Timers need no attention
+ NULL ); // No name
+
+ if ( NlGlobalTimerEvent == NULL ) {
+ NlExit( NELOG_NetlogonSystemError, GetLastError(), LogErrorAndNetStatus, NULL);
+ return FALSE;
+ }
+
+
+
+
+ //
+ // Do Workstation or Domain Controller specific initialization
+ //
+
+ if ( !GiveInstallHints( FALSE ) ) {
+ return FALSE;
+ }
+
+ if ( NlGlobalRole == RoleMemberWorkstation ) {
+ if ( !NlInitWorkstation() ) {
+ return FALSE;
+ }
+ } else {
+ if ( !NlInitDomainController() ) {
+ return FALSE;
+ }
+ }
+
+ //
+ // Create an event that is signalled when the last MSV thread leaves
+ // a netlogon routine.
+ //
+
+ NlGlobalMsvTerminateEvent = CreateEvent( NULL, // No security attributes
+ TRUE, // Must be manually reset
+ FALSE, // Initially not signaled
+ NULL ); // No name
+
+ if ( NlGlobalMsvTerminateEvent == NULL ) {
+ NlExit( NELOG_NetlogonSystemError, GetLastError(), LogErrorAndNetStatus, NULL);
+ return FALSE;
+ }
+
+ NlGlobalMsvEnabled = TRUE;
+
+ //
+ // We are now ready to act as a Netlogon service
+ // Enable RPC
+ //
+
+ if ( !GiveInstallHints( FALSE ) ) {
+ return FALSE;
+ }
+
+
+ NlPrint((NL_INIT,"Starting RPC server.\n"));
+
+ //
+ // NOTE: Now all RPC servers in lsass.exe (now winlogon) share the same
+ // pipe name. However, in order to support communication with
+ // version 1.0 of WinNt, it is necessary for the Client Pipe name
+ // to remain the same as it was in version 1.0. Mapping to the new
+ // name is performed in the Named Pipe File System code.
+ //
+ NetStatus = RpcpAddInterface ( L"lsass", logon_ServerIfHandle );
+
+ if (NetStatus != NERR_Success) {
+ NlExit( NELOG_NetlogonFailedToAddRpcInterface, NetStatus, LogErrorAndNetStatus, NULL );
+ return FALSE;
+ }
+
+ NlGlobalRpcServerStarted = TRUE;
+
+
+
+ //
+ // Tell the ServiceController what services we provide.
+ //
+
+ if ( !I_ScSetServiceBits( NlGlobalServiceHandle,
+ NlServerType(),
+ TRUE, // Set bits on
+ TRUE, // Force immediate announcement
+ NULL)) { // All transports
+
+ NetStatus = GetLastError();
+
+ NlPrint((NL_CRITICAL,"Couldn't I_ScSetServiceBits %ld 0x%lx.\n",
+ NetStatus, NetStatus ));
+ NlExit( NELOG_NetlogonSystemError, NetStatus, LogErrorAndNetStatus, NULL );
+ return FALSE;
+ }
+
+ //
+ // Tell the browser that the role may have changed
+ //
+
+ if ( !GiveInstallHints( FALSE ) ) {
+ return FALSE;
+ }
+
+ NetStatus = I_BrowserResetNetlogonState( NULL );
+
+ if ( NetStatus != NERR_Success ) {
+ NlPrint((NL_INIT,"Couldn't I_BrowserResetNetlogonState %ld 0x%lx.\n",
+ NetStatus, NetStatus ));
+ // This isn't fatal
+ }
+
+
+
+ //
+ // Let the ChangeLog routines know that Netlogon is started.
+ //
+
+ NlGlobalChangeLogNetlogonState = NetlogonStarted;
+
+
+
+
+ //
+ // Set an event telling anyone wanting to call NETLOGON that we're
+ // initialized.
+ //
+
+ if ( !GiveInstallHints( FALSE ) ) {
+ return FALSE;
+ }
+
+ RtlInitUnicodeString( &EventName, L"\\NETLOGON_SERVICE_STARTED");
+ InitializeObjectAttributes( &EventAttributes, &EventName, 0, 0, NULL );
+
+ Status = NtCreateEvent(
+ &NlGlobalStartedEvent,
+ SYNCHRONIZE|EVENT_MODIFY_STATE,
+ &EventAttributes,
+ NotificationEvent,
+ (BOOLEAN) FALSE // The event is initially not signaled
+ );
+
+ if ( !NT_SUCCESS(Status)) {
+
+ //
+ // If the event already exists, a waiting thread beat us to
+ // creating it. Just open it.
+ //
+
+ if( Status == STATUS_OBJECT_NAME_EXISTS ||
+ Status == STATUS_OBJECT_NAME_COLLISION ) {
+
+ Status = NtOpenEvent( &NlGlobalStartedEvent,
+ SYNCHRONIZE|EVENT_MODIFY_STATE,
+ &EventAttributes );
+
+ }
+ if ( !NT_SUCCESS(Status)) {
+
+ NlPrint((NL_CRITICAL,
+ " Failed to open NETLOGON_SERVICE_STARTED event. %lX\n",
+ Status ));
+ NlPrint((NL_CRITICAL,
+ " Failing to initialize SAM Server.\n"));
+ NlExit( SERVICE_UIC_SYSTEM, Status, LogError, NULL);
+ return FALSE;
+ }
+ }
+
+ Status = NtSetEvent( NlGlobalStartedEvent, NULL );
+ if ( !NT_SUCCESS(Status)) {
+ NlPrint((NL_CRITICAL,
+ "Failed to set NETLOGON_SERVICE_STARTED event. %lX\n",
+ Status ));
+ NlPrint((NL_CRITICAL, " Failing to initialize SAM Server.\n"));
+
+ NtClose(NlGlobalStartedEvent);
+ NlExit( SERVICE_UIC_SYSTEM, Status, LogError, NULL);
+ return FALSE;
+ }
+
+ //
+ // Don't close the event handle. Closing it would delete the event and
+ // a future waiter would never see it be set.
+ //
+
+
+ //
+ // Announce that we're started
+ //
+
+ if (NlGlobalRole == RolePrimary) {
+
+ if ( !GiveInstallHints( FALSE ) ) {
+ return FALSE;
+ }
+ NlAnnouncePrimaryStart();
+ }
+
+
+ //
+ // we are just about done, this will be final hint
+ //
+
+ if ( !GiveInstallHints( TRUE ) ) {
+ return FALSE;
+ }
+
+
+
+
+ //
+ // If we're not the primary,
+ // sync the SAM database as requested.
+ //
+
+ if ( NlGlobalRole == RoleBackup ) {
+ DWORD i;
+
+ //
+ // Give the BDC a chance to change its password before the replicator
+ // starts.
+ //
+
+ NlChangePassword( NlGlobalClientSession );
+
+ //
+ // Handle each database separately.
+ //
+
+ for( i = 0; i < NUM_DBS; i++ ) {
+
+ //
+ // if /UPDATE:YES has been specified then FORCE replication
+ //
+
+ if ( NlGlobalSynchronizeParameter ) {
+
+ NlPrint(( NL_SYNC,
+ FORMAT_LPWSTR ": Force FULL SYNC because UPDATE was specified.\n",
+ NlGlobalDBInfoArray[i].DBName ));
+
+
+ //
+ // Do a complete full sync (don't restart it).
+ //
+
+ NlSetFullSyncKey( i, NULL );
+
+ Status = NlForceStartupSync( &NlGlobalDBInfoArray[i] );
+
+ if ( !NT_SUCCESS(Status) ) {
+ NlExit( SERVICE_UIC_M_DATABASE_ERROR,
+ NetpNtStatusToApiStatus(Status),
+ LogError,
+ NULL);
+ return FALSE;
+ }
+ }
+
+
+ }
+
+ //
+ // See if there is any reason to start replication
+ //
+
+ for ( i = 0; i < NUM_DBS; i++ ) {
+
+ if ( NlGlobalDBInfoArray[i].UpdateRqd ||
+ NlGlobalRedoLogDesc.EntryCount[i] != 0 ) {
+
+ if ( NlGlobalClientSession->CsState != CS_IDLE ) {
+ NlPrint((NL_SYNC, "Starting replicator on startup.\n"));
+ (VOID) NlStartReplicatorThread( 0 );
+ }
+ break;
+ }
+ }
+
+ //
+ // Clear the full sync key on the primary to avoid confusion if we ever
+ // demote to a BDC.
+ //
+ } else if ( NlGlobalRole == RolePrimary ) {
+
+ DWORD i;
+
+ for( i = 0; i < NUM_DBS; i++ ) {
+ NlSetFullSyncKey( i, NULL );
+ (VOID) NlResetFirstTimeFullSync( i );
+ }
+
+ }
+
+
+
+ //
+ // Successful initialization.
+ //
+
+ return TRUE;
+}
+
+
+BOOLEAN
+LogonRequestHandler(
+ IN DWORD Version,
+ IN LPWSTR UnicodeUserName,
+ IN DWORD RequestCount,
+ IN LPSTR OemWorkstationName,
+ IN LPSTR OemMailslotName,
+ IN LPWSTR TransportName,
+ IN ULONG AllowableAccountControlBits
+ )
+
+/*++
+
+Routine Description:
+
+ Respond appropriate to a LM 1.0, LM 2.0 or SAM logon request.
+
+ Requests from LM1.0 clients to be handled differently
+ since response_buffer size has changed due to PATHLEN.
+
+Arguments:
+
+ Version - The version of the input message. This parameter determine
+ the version of the response.
+
+ UnicodeUserName - The name of the user logging on.
+
+ RequestCount - The number of times this user has repeated the logon request.
+
+ OemWorkstationName - The name of the workstation where the user is
+ logging onto.
+
+ OemMailslotName - The name of the mailslot to respond to.
+
+ AllowableAccountControlBits - A mask of allowable SAM account types that
+ are allowed to satisfy this request.
+
+Return Value:
+
+ TRUE if the any duplicates of this message should be ignored.
+
+--*/
+{
+ NTSTATUS Status;
+
+ USHORT Response = 0;
+ PSAMPR_USER_INFO_BUFFER UserControl = NULL;
+
+ NETLOGON_LOGON_RESPONSE2 Response2;
+ NETLOGON_SAM_LOGON_RESPONSE SamResponse;
+ PCHAR Where;
+ BOOLEAN IgnoreDuplicatesOfThisMessage = FALSE;
+
+ SAMPR_HANDLE UserHandle = NULL;
+
+ //
+ // Logons are not processed if the service is paused
+ //
+
+ if ( (NlGlobalServiceStatus.dwCurrentState == SERVICE_PAUSED) ||
+ ( NlGlobalFirstTimeFullSync == TRUE ) ) {
+
+ if ( Version == LMNT_MESSAGE ) {
+ Response = LOGON_SAM_PAUSE_RESPONSE;
+ } else {
+
+ //
+ // Don't respond to immediately to non-nt clients. They treat
+ // "paused" responses as fatal. That's just not so.
+ // There may be many other DCs that are able to process the logon.
+ //
+ if ( RequestCount >= MAX_LOGONREQ_COUNT &&
+ NlGlobalServiceStatus.dwCurrentState == SERVICE_PAUSED ) {
+ Response = LOGON_PAUSE_RESPONSE;
+ }
+ }
+
+ goto Cleanup;
+ }
+
+
+ //
+ // If this user does not have an account in SAM,
+ // immediately return a response indicating so.
+ //
+ // All we are trying to do here is ensuring that this guy
+ // has a valid account except that we are not checking the
+ // password
+ //
+ // This is done so that STANDALONE logons for non existent
+ // users can be done in very first try, speeding up the response
+ // to user and reducing processing on DCs/BCs.
+ //
+
+ Status = NlSamOpenNamedUser( UnicodeUserName, &UserHandle, NULL );
+
+ if ( !NT_SUCCESS(Status) ) {
+
+ if ( Status == STATUS_NO_SUCH_USER ) {
+
+ if ( Version == LMNT_MESSAGE ) {
+ Response = LOGON_SAM_USER_UNKNOWN;
+ } else if ( Version == LM20_MESSAGE ) {
+ Response = LOGON_USER_UNKNOWN;
+ }
+ }
+
+ goto Cleanup;
+ }
+
+
+ //
+ // Get the account control information.
+ //
+
+ Status = SamrQueryInformationUser(
+ UserHandle,
+ UserControlInformation,
+ &UserControl );
+
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+
+ //
+ // Disallow use of disabled accounts.
+ //
+ // We use this message to determine if a trusted domain has a
+ // particular account. Since the UI recommend disabling an account
+ // rather than deleting it (conservation of rids and all that),
+ // we shouldn't respond that we have the account if we really don't.
+ //
+ // We don't check the disabled bit in the downlevel case. Downlevel
+ // interactive logons are directed at a single particular domain.
+ // It is better here that we indicate we have the account so later
+ // he'll get a better error code indicating that the account is
+ // disabled, rather than allowing him to logon standalone.
+ //
+
+ if ( Version == LMNT_MESSAGE &&
+ (UserControl->Control.UserAccountControl & USER_ACCOUNT_DISABLED) ) {
+ Response = LOGON_SAM_USER_UNKNOWN;
+ goto Cleanup;
+ }
+
+ //
+ // Ensure the Account type matches those valid for logons.
+ //
+ if ( (UserControl->Control.UserAccountControl &
+ USER_ACCOUNT_TYPE_MASK &
+ AllowableAccountControlBits )
+ == 0 ) {
+
+ if ( Version == LMNT_MESSAGE ) {
+ Response = LOGON_SAM_USER_UNKNOWN;
+ } else if ( Version == LM20_MESSAGE ) {
+ Response = LOGON_USER_UNKNOWN;
+ }
+ goto Cleanup;
+ }
+
+ //
+ // For SAM clients, respond immediately.
+ //
+
+ if ( Version == LMNT_MESSAGE ) {
+ Response = LOGON_SAM_LOGON_RESPONSE;
+ goto Cleanup;
+
+ //
+ // For LM 2.0 clients, respond immediately.
+ //
+
+ } else if ( Version == LM20_MESSAGE ) {
+ Response = LOGON_RESPONSE2;
+ goto Cleanup;
+
+ //
+ // For LM 1.0 clients,
+ // don't support the request.
+ //
+
+ } else {
+ Response = LOGON_USER_UNKNOWN;
+ goto Cleanup;
+ }
+
+Cleanup:
+
+ //
+ // Always good to debug
+ //
+
+ NlPrint((NL_LOGON,
+ "%s logon mailslot message for " FORMAT_LPWSTR " from \\\\%s"
+ ". Response 0x%lx\n",
+ Version == LMNT_MESSAGE ? "Sam" : "Uas",
+ UnicodeUserName,
+ OemWorkstationName,
+ Response ));
+
+ //
+ // If we should respond to the caller, do so now.
+ //
+
+ switch (Response) {
+ case LOGON_SAM_PAUSE_RESPONSE:
+ case LOGON_SAM_USER_UNKNOWN:
+ case LOGON_SAM_LOGON_RESPONSE:
+ SamResponse.Opcode = Response;
+
+ Where = (PCHAR) SamResponse.UnicodeLogonServer;
+ NetpLogonPutUnicodeString( NlGlobalUncUnicodeComputerName,
+ sizeof(SamResponse.UnicodeLogonServer),
+ &Where );
+ NetpLogonPutUnicodeString( UnicodeUserName,
+ sizeof(SamResponse.UnicodeUserName),
+ &Where );
+ NetpLogonPutUnicodeString( NlGlobalUnicodeDomainName,
+ sizeof(SamResponse.UnicodeDomainName),
+ &Where );
+ NetpLogonPutNtToken( &Where );
+
+ Status = NlBrowserSendDatagram( OemWorkstationName,
+ TransportName,
+ OemMailslotName,
+ &SamResponse,
+ Where - (PCHAR)&SamResponse );
+
+ if ( NT_SUCCESS(Status) ) {
+ IgnoreDuplicatesOfThisMessage = TRUE;
+ }
+ break;
+
+ case LOGON_RESPONSE2:
+ case LOGON_USER_UNKNOWN:
+ case LOGON_PAUSE_RESPONSE:
+
+ Response2.Opcode = Response;
+
+ Where = Response2.LogonServer;
+ (VOID) lstrcpyA( Where, "\\\\");
+ Where += 2;
+ NetpLogonPutOemString( NlGlobalAnsiComputerName,
+ sizeof(Response2.LogonServer) - 2,
+ &Where );
+ NetpLogonPutLM20Token( &Where );
+
+
+ Status = NlBrowserSendDatagram( OemWorkstationName,
+ TransportName,
+ OemMailslotName,
+ &Response2,
+ Where - (PCHAR)&Response2 );
+
+ if ( NT_SUCCESS(Status) ) {
+ IgnoreDuplicatesOfThisMessage = TRUE;
+ }
+ break;
+
+ default:
+ IgnoreDuplicatesOfThisMessage = TRUE;
+ break;
+ }
+
+ //
+ // Free up any locally used resources.
+ //
+
+ if ( UserControl != NULL ) {
+ SamIFree_SAMPR_USER_INFO_BUFFER( UserControl, UserControlInformation );
+ }
+
+ if ( UserHandle != NULL ) {
+ (VOID) SamrCloseHandle( &UserHandle );
+ }
+
+ return IgnoreDuplicatesOfThisMessage;
+
+}
+
+
+BOOLEAN
+PrimaryQueryHandler(
+ IN DWORD Version,
+ IN LPSTR OemWorkstationName,
+ IN LPSTR OemMailslotName,
+ IN LPWSTR TransportName
+ )
+
+/*++
+
+Routine Description:
+
+ Respond appropriately to a primary query request.
+
+Arguments:
+
+ Version - The version of the input message.
+
+ OemWorkstationName - The name of the workstation where the user is
+ logging onto.
+
+ OemMailslotName - The name of the mailslot to respond to.
+
+ TransportName - The name of the transport to respond on.
+
+Return Value:
+
+ TRUE if the any duplicates of this message should be ignored.
+
+--*/
+{
+ NTSTATUS Status;
+ NETLOGON_PRIMARY Response;
+ PCHAR Where;
+ BOOLEAN IgnoreDuplicatesOfThisMessage = FALSE;
+
+ //
+ // If we're a PDC,
+ // always respond.
+ //
+
+ if ( NlGlobalRole == RolePrimary ) {
+ goto Respond;
+ }
+
+
+ //
+ // ASSERT: This machine is a BDC
+ //
+
+ NlAssert( NlGlobalRole == RoleBackup );
+
+
+ //
+ // Always respond to this message if this query is from a downlevel
+ // PDC trying to start up.
+ //
+ //
+ // If this request is from NetGetDCName, ignore the request.
+ // We know the NetGetDCName uses a mailslot name that is
+ // randomly generated and that LM2.0 netlogon uses the
+ // standard netlogon mailslot name to find out if another PDC
+ // is already running.
+ //
+
+ if ( Version != LMNT_MESSAGE &&
+ lstrcmpiA( OemMailslotName, NETLOGON_LM_MAILSLOT_A ) == 0 ) {
+
+ NlPrint((NL_CRITICAL,
+ "PrimaryQueryHandler: Preventing Lanman PDC from starting "
+ "in this NT domain. \\\\%s.\n",
+ OemWorkstationName ));
+ goto Respond;
+ }
+
+ //
+ // If the caller is an NT machine,
+ // don't respond to this request.
+ //
+ // NT 3.1 clients have a sophisticated TCP/IP stack which ensures that the
+ // request reaches the real PDC so we don't need to respond.
+ //
+ // NT 3.5 clients, Chicago clients and newer (8/8/94) WFW clients send
+ // directly to the Domain<1B> address which is registered by the PDC.
+ //
+
+ if ( Version == LMNT_MESSAGE || Version == LMWFW_MESSAGE ) {
+ IgnoreDuplicatesOfThisMessage = TRUE;
+ goto Cleanup;
+ }
+
+
+ //
+ // If this machine is on a WAN,
+ // this primary query message may have reached us but not the PDC.
+ // Therefore, we'll respond to the request if we know who the PDC is.
+ //
+
+ if ( *NlGlobalUnicodePrimaryName == L'\0' ) {
+ NlPrint((NL_MAILSLOT,
+ "PrimaryQueryHandler: This BDC doesn't know who primary is." ));
+ IgnoreDuplicatesOfThisMessage = TRUE;
+ goto Cleanup;
+ }
+
+ //
+ // Ensure we have a session up to the PDC.
+ // This is our evidence that the machine we think is the PDC really
+ // is the PDC.
+ //
+
+ if ( NlGlobalClientSession->CsState != CS_AUTHENTICATED ) {
+ NlPrint((NL_MAILSLOT,
+ "PrimaryQueryHandler: This BDC doesn't have a session to the PDC.\n" ));
+ IgnoreDuplicatesOfThisMessage = TRUE;
+ goto Cleanup;
+ }
+
+
+
+ //
+ // Respond to the query
+ //
+Respond:
+
+ NlPrint((NL_MAILSLOT,
+ "%s Primary Query mailslot message from %s. "
+ "Response " FORMAT_LPWSTR "\n",
+ Version == LMNT_MESSAGE ? "Sam" : "Uas",
+ OemWorkstationName,
+ NlGlobalUncPrimaryName ));
+
+ //
+ // Build the response
+ //
+ // If we are the Primary DC, tell the caller our computername.
+ // If we are a backup DC,
+ // tell the downlevel PDC who we think the primary is.
+ //
+
+ Response.Opcode = LOGON_PRIMARY_RESPONSE;
+
+ Where = Response.PrimaryDCName;
+ NetpLogonPutOemString(
+ NlGlobalAnsiPrimaryName,
+ sizeof( Response.PrimaryDCName),
+ &Where );
+
+ //
+ // If this is an NT query,
+ // add the NT specific response.
+ //
+ if ( Version == LMNT_MESSAGE ) {
+ NetpLogonPutUnicodeString(
+ NlGlobalUnicodePrimaryName,
+ sizeof(Response.UnicodePrimaryDCName),
+ &Where );
+
+ NetpLogonPutUnicodeString(
+ NlGlobalUnicodeDomainName,
+ sizeof(Response.UnicodeDomainName),
+ &Where );
+
+ NetpLogonPutNtToken( &Where );
+ }
+
+
+ Status = NlBrowserSendDatagram( OemWorkstationName,
+ TransportName,
+ OemMailslotName,
+ &Response,
+ (DWORD)(Where - (PCHAR)&Response) );
+
+ if ( NT_SUCCESS(Status) ) {
+ IgnoreDuplicatesOfThisMessage = TRUE;
+ }
+
+ //
+ // Free Locally used resources
+ //
+Cleanup:
+
+ return IgnoreDuplicatesOfThisMessage;
+}
+
+BOOL
+TimerExpired(
+ IN PTIMER Timer,
+ IN PLARGE_INTEGER TimeNow,
+ IN OUT LPDWORD Timeout
+ )
+
+/*++
+
+Routine Description:
+
+ Determine whether a timer has expired. If not, adjust the passed in
+ timeout value to take this timer into account.
+
+Arguments:
+
+ Timer - Specifies the timer to check.
+
+ TimeNow - Specifies the current time of day in NT standard time.
+
+ Timeout - Specifies the current amount of time (in milliseconds)
+ that the caller intends to wait for a timer to expire.
+ If this timer has not expired, this value is adjusted to the
+ smaller of the current value and the amount of time remaining
+ on the passed in timer.
+
+Return Value:
+
+ TRUE - if the timer has expired.
+
+--*/
+
+{
+ LARGE_INTEGER Period;
+ LARGE_INTEGER ExpirationTime;
+ LARGE_INTEGER ElapsedTime;
+ LARGE_INTEGER TimeRemaining;
+ LARGE_INTEGER MillisecondsRemaining;
+
+/*lint -e569 */ /* don't complain about 32-bit to 31-bit initialize */
+ LARGE_INTEGER BaseGetTickMagicDivisor = { 0xe219652c, 0xd1b71758 };
+/*lint +e569 */ /* don't complain about 32-bit to 31-bit initialize */
+ CCHAR BaseGetTickMagicShiftCount = 13;
+
+ //
+ // If the period to too large to handle (i.e., 0xffffffff is forever),
+ // just indicate that the timer has not expired.
+ //
+
+ if ( Timer->Period > 0x7fffffff ) {
+ return FALSE;
+ }
+
+ //
+ // If time has gone backwards (someone changed the clock),
+ // just start the timer over again.
+ //
+ // The kernel automatically updates the system time to the CMOS clock
+ // periodically. If we just expired the timer when time went backwards,
+ // we'd risk periodically falsely triggering the timeout.
+ //
+
+ ElapsedTime.QuadPart = TimeNow->QuadPart - Timer->StartTime.QuadPart;
+
+ if ( ElapsedTime.QuadPart < 0 ) {
+ Timer->StartTime = *TimeNow;
+ }
+
+ //
+ // Convert the period from milliseconds to 100ns units.
+ //
+
+ Period = RtlEnlargedIntegerMultiply( (LONG) Timer->Period, 10000 );
+
+ //
+ // Compute the expiration time.
+ //
+
+ ExpirationTime.QuadPart = Timer->StartTime.QuadPart + Period.QuadPart;
+
+ //
+ // Compute the Time remaining on the timer.
+ //
+
+ TimeRemaining.QuadPart = ExpirationTime.QuadPart - TimeNow->QuadPart;
+
+ //
+ // If the timer has expired, tell the caller so.
+ //
+
+ if ( TimeRemaining.QuadPart <= 0 ) {
+ return TRUE;
+ }
+
+
+
+ //
+ // If the timer hasn't expired, compute the number of milliseconds
+ // remaining.
+ //
+
+ MillisecondsRemaining = RtlExtendedMagicDivide(
+ TimeRemaining,
+ BaseGetTickMagicDivisor,
+ BaseGetTickMagicShiftCount );
+
+ NlAssert( MillisecondsRemaining.HighPart == 0 );
+ NlAssert( MillisecondsRemaining.LowPart < 0x7fffffff );
+
+ //
+ // Adjust the running timeout to be the smaller of the current value
+ // and the value computed for this timer.
+ //
+
+ if ( *Timeout > MillisecondsRemaining.LowPart ) {
+ *Timeout = MillisecondsRemaining.LowPart;
+ }
+
+ return FALSE;
+
+}
+
+VOID
+NlScavenger(
+ IN LPVOID ScavengerParam
+)
+/*++
+
+Routine Description:
+
+ This function performs the scavenger operation. This function is
+ called every 15 mins interval. On workstation this function is
+ executed in the main netlogon thread, but on server this function is
+ executed on the scavenger thread, thus making the main thread to
+ process the mailslot messages better.
+
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ Return iff the service is to exit or all scavenger operations
+ for this turn are completed.
+
+--*/
+{
+
+
+ //
+ // Change password if neccessary
+ //
+
+ if ( (NlGlobalRole == RoleMemberWorkstation ||
+ NlGlobalRole == RoleBackup) &&
+ !NlGlobalDisablePasswordChangeParameter ) {
+
+ (VOID) NlChangePassword( NlGlobalClientSession );
+ }
+
+
+
+ //
+ // Change the password on each entry in the trust list.
+ //
+
+ if ( NlGlobalRole == RolePrimary ) {
+ PLIST_ENTRY ListEntry;
+ PCLIENT_SESSION ClientSession;
+
+ //
+ // Reset all the flags indicating we need to check the password
+ //
+
+ LOCK_TRUST_LIST();
+ for ( ListEntry = NlGlobalTrustList.Flink ;
+ ListEntry != &NlGlobalTrustList ;
+ ListEntry = ListEntry->Flink) {
+
+ ClientSession = CONTAINING_RECORD( ListEntry,
+ CLIENT_SESSION,
+ CsNext );
+
+ ClientSession->CsFlags |= CS_CHECK_PASSWORD;
+ }
+
+ for ( ListEntry = NlGlobalTrustList.Flink ;
+ ListEntry != &NlGlobalTrustList ;
+ ) {
+
+ ClientSession = CONTAINING_RECORD( ListEntry,
+ CLIENT_SESSION,
+ CsNext );
+
+ if ( (ClientSession->CsFlags & CS_CHECK_PASSWORD) == 0 ) {
+ ListEntry = ListEntry->Flink;
+ continue;
+ }
+ ClientSession->CsFlags &= ~CS_CHECK_PASSWORD;
+
+ NlRefClientSession( ClientSession );
+ UNLOCK_TRUST_LIST();
+
+
+ //
+ // Change the password for this trusted domain.
+ //
+
+ (VOID) NlChangePassword( ClientSession );
+
+ NlUnrefClientSession( ClientSession );
+
+ //
+ // check to see iff we have been asked to leave.
+ //
+
+ if( NlGlobalScavengerTerminate == TRUE ) {
+
+ return;
+ }
+
+ LOCK_TRUST_LIST();
+
+ // Start again at the beginning.
+ ListEntry = NlGlobalTrustList.Flink;
+
+ }
+ UNLOCK_TRUST_LIST();
+
+ }
+
+
+ //
+ // Scavenge through the server session table.
+ //
+
+ if ( NlGlobalRole == RolePrimary || NlGlobalRole == RoleBackup ) {
+ NlServerSessionScavenger();
+
+ //
+ // Pick a DC for each non-authenicated entry in the trust list.
+ //
+
+ NlPickTrustedDcForEntireTrustList();
+
+ }
+
+ //
+ // Ensure our Domain<1B> name is registered.
+ //
+
+ NlBrowserAddName();
+
+ UNREFERENCED_PARAMETER( ScavengerParam );
+}
+
+
+BOOL
+IsScavengerRunning(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ Test if the scavenger thread is running
+
+ Enter with NlGlobalScavengerCritSect locked.
+
+Arguments:
+
+ NONE
+
+Return Value:
+
+ TRUE - The scavenger thread is running
+
+ FALSE - The scavenger thread is not running.
+
+--*/
+{
+ DWORD WaitStatus;
+
+ //
+ // Determine if the scavenger thread is already running.
+ //
+
+ if ( NlGlobalScavengerThreadHandle != NULL ) {
+
+ //
+ // Time out immediately if the scavenger is still running.
+ //
+
+ WaitStatus = WaitForSingleObject(
+ NlGlobalScavengerThreadHandle,
+ 0 );
+
+ if ( WaitStatus == WAIT_TIMEOUT ) {
+ return TRUE;
+
+ } else if ( WaitStatus == 0 ) {
+ CloseHandle( NlGlobalScavengerThreadHandle );
+ NlGlobalScavengerThreadHandle = NULL;
+ return FALSE;
+
+ } else {
+ NlPrint((NL_CRITICAL,
+ "Cannot WaitFor scavenger thread: %ld\n",
+ WaitStatus ));
+ return TRUE;
+ }
+
+ }
+
+ return FALSE;
+}
+
+
+VOID
+NlStopScavenger(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ Stops the scavenger thread if it is running and waits for it to
+ stop.
+
+ Enter with NlGlobalScavengerCritSect locked.
+
+Arguments:
+
+ NONE
+
+Return Value:
+
+ NONE
+
+--*/
+{
+ //
+ // Ask the scavenger to stop running.
+ //
+
+ NlGlobalScavengerTerminate = TRUE;
+
+ //
+ // Determine if the scavenger thread is already running.
+ //
+
+ if ( NlGlobalScavengerThreadHandle != NULL ) {
+
+ //
+ // We've asked the scavenger to stop. It should do so soon.
+ // Wait for it to stop.
+ //
+
+ NlWaitForSingleObject( "Wait for scavenger to stop",
+ NlGlobalScavengerThreadHandle );
+
+
+ CloseHandle( NlGlobalScavengerThreadHandle );
+ NlGlobalScavengerThreadHandle = NULL;
+
+ }
+
+ NlGlobalScavengerTerminate = FALSE;
+
+ return;
+}
+
+
+BOOL
+NlStartScavengerThread(
+ )
+/*++
+
+Routine Description:
+
+ Start the scavenger thread if it is not already running.
+
+Arguments:
+ None
+
+Return Value:
+ None
+
+--*/
+{
+ DWORD ThreadHandle;
+
+ //
+ // If the scavenger thread is already running, do nothing.
+ //
+
+ EnterCriticalSection( &NlGlobalScavengerCritSect );
+ if ( IsScavengerRunning() ) {
+ LeaveCriticalSection( &NlGlobalScavengerCritSect );
+ return FALSE;
+ }
+
+ //
+ // Initialize the scavenger parameters
+ //
+
+ NlGlobalScavengerTerminate = FALSE;
+
+ NlGlobalScavengerThreadHandle = CreateThread(
+ NULL, // No security attributes
+ THREAD_STACKSIZE,
+ (LPTHREAD_START_ROUTINE)
+ NlScavenger,
+ NULL,
+ 0, // No special creation flags
+ &ThreadHandle );
+
+ if ( NlGlobalScavengerThreadHandle == NULL ) {
+
+ //
+ // ?? Shouldn't we do something in non-debug case
+ //
+
+ NlPrint((NL_CRITICAL, "Can't create scavenger Thread %lu\n",
+ GetLastError() ));
+
+ LeaveCriticalSection( &NlGlobalScavengerCritSect );
+ return FALSE;
+ }
+
+ LeaveCriticalSection( &NlGlobalScavengerCritSect );
+ return TRUE;
+
+}
+
+
+VOID
+NlMainLoop(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+
+ Waits for a logon request to arrive at the NETLOGON mailslot.
+
+ This routine, also, processes several periodic events. These events
+ are timed by computing a timeout value on the mailslot read which is the
+ time needed before the nearest periodic event needs to be processed.
+ After such a timeout, this routine processes the event.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ Return iff the service is to exit.
+
+ mail slot error occurred, eg if someone deleted the NETLOGON
+ mail slot explicitly or if the logon server share has been deleted
+ and cannot be re-shared.
+
+--*/
+{
+ NET_API_STATUS NetStatus;
+ DWORD WaitStatus;
+
+ DWORD BytesRead;
+ LPBYTE Message;
+ LPWSTR TransportName;
+
+ DWORD ResponseBufferSize;
+ // ?? Get these off the workstation stack
+ BYTE resp_buf[NETLOGON_MAX_MS_SIZE]; // Buffer to build response in
+
+ //
+ // Variables for unmarshalling the message read.
+ //
+
+ DWORD Version;
+ DWORD VersionFlags;
+ PCHAR Where;
+ LPSTR OemWorkstationName;
+ LPSTR AnsiUserName;
+ LPSTR OemMailslotName;
+
+ LPWSTR UnicodeWorkstationName;
+ LPWSTR UnicodeUserName;
+
+ LPSTR AnsiTemp;
+
+ LPWSTR UnicodeTemp;
+ BOOLEAN IgnoreDuplicatesOfThisMessage;
+
+
+
+ //
+ // Variables controlling mailslot read timeout
+ //
+
+ DWORD MainLoopTimeout = 0;
+ LARGE_INTEGER TimeNow;
+
+ TIMER ScavengerTimer;
+ TIMER AnnouncerTimer;
+
+#define NL_WAIT_TERMINATE 0
+#define NL_WAIT_TIMER 1
+#define NL_WAIT_MAILSLOT 2
+#define NL_WAIT_NOTIFY 3
+
+#define NL_WAIT_COUNT 4
+
+ HANDLE WaitHandles[ NL_WAIT_COUNT ];
+ DWORD WaitCount = 0;
+
+
+
+ //
+ // Initialize handles to wait on.
+ //
+
+ WaitHandles[NL_WAIT_TERMINATE] = NlGlobalTerminateEvent;
+ WaitCount++;
+ WaitHandles[NL_WAIT_TIMER] = NlGlobalTimerEvent;
+ WaitCount++;
+
+ if ( NlGlobalRole == RolePrimary || NlGlobalRole == RoleBackup ) {
+ WaitHandles[NL_WAIT_MAILSLOT] = NlGlobalMailslotHandle;
+ WaitCount++;
+
+ //
+ // When netlogon is run during retail setup
+ // (in an attempt to replicate the databases to a BDC),
+ // the role is Workstation at the instant netlogon.dll is loaded,
+ // therefore, the ChangeLogEvent won't have been initialized.
+ //
+
+ if ( NlGlobalChangeLogEvent != NULL ) {
+ WaitHandles[NL_WAIT_NOTIFY] = NlGlobalChangeLogEvent;
+ WaitCount++;
+ }
+ }
+
+ NlAssert( WaitCount <= NL_WAIT_COUNT );
+
+
+ //
+ // Set up a secure channel to any DC in the domain.
+ // Don't fail if setup is impossible.
+ //
+ // We wait until now since this is a potentially lengthy operation.
+ // If the user on the workstation is trying to logon immediately after
+ // reboot, we'd rather have him wait in netlogon (where we have more
+ // control) than have him waiting in MSV.
+ //
+
+ if ( NlGlobalRole == RoleMemberWorkstation ) {
+ (VOID) NlTimeoutSetWriterClientSession( NlGlobalClientSession, 0xFFFFFFFF );
+ (VOID) NlSessionSetup( NlGlobalClientSession );
+ NlResetWriterClientSession( NlGlobalClientSession );
+ }
+
+
+
+ //
+ // Force the scavenger to start immediately.
+ //
+ // We want the password on the trust account to change immediately
+ // on the very first boot.
+ //
+
+ (VOID) NtQuerySystemTime( &TimeNow );
+
+ ScavengerTimer.StartTime.QuadPart = 0;
+ ScavengerTimer.Period = NlGlobalScavengeIntervalParameter * 1000L;
+
+
+
+ //
+ // Force the announce to happen immediately.
+ //
+ // We use this initial announcement in case the "Primary Start"
+ // message was lost and this is the first boot of a new PDC.
+ // This ensures that all BDCs receive the name of the new PDC quickly
+ // so they respond correctly to "Primary Query" requests.
+ //
+
+ AnnouncerTimer.StartTime.QuadPart = 0;
+ AnnouncerTimer.Period = NlGlobalPulseParameter * 1000L;
+
+
+
+ //
+ // Ensure we don't immediately time out the discovery timer.
+ //
+
+ NlGlobalDcDiscoveryTimer.StartTime = TimeNow;
+ NlGlobalApiTimer.StartTime = TimeNow;
+
+
+
+ NlPrint((NL_INIT, "Started successfully\n" ));
+
+ //
+ // Loop reading from the Netlogon mailslot
+ //
+
+ IgnoreDuplicatesOfThisMessage = FALSE;
+ for ( ;; ) {
+ DWORD Timeout;
+
+
+
+ //
+ // Issue a mailslot read request if we are domain controller and
+ // there is no outstanding read request pending.
+ //
+
+ if (NlGlobalRole == RolePrimary || NlGlobalRole == RoleBackup) {
+ NlMailslotPostRead( IgnoreDuplicatesOfThisMessage );
+ IgnoreDuplicatesOfThisMessage = FALSE;
+ }
+
+
+
+
+ //
+ // Wait for the next interesting event.
+ //
+ // On each iteration of the loop,
+ // we do an "extra" wait with a timeout of 0 to force mailslot
+ // processing to be more important that timeout processing.
+ //
+ // Since we can only compute a non-zero timeout by processing the
+ // timeout events, using a constant 0 allows us to process all
+ // non-timeout events before we compute the next true timeout value.
+ //
+ // This is especially important for handling async discovery.
+ // Our mailslot may be full of responses to discovery queries and
+ // we only have a 5 second timer before we ask for more responses.
+ // We want to avoid asking for additional responses until we finish
+ // processing those we have.
+ //
+
+ if ( MainLoopTimeout != 0 ) {
+ NlPrint((NL_MAILSLOT,
+ "Going to wait on mailslot. (Timeout: %ld)\n",
+ MainLoopTimeout));
+ }
+
+ WaitStatus = WaitForMultipleObjects( WaitCount,
+ WaitHandles,
+ FALSE, // Wait for ANY handle
+ MainLoopTimeout );
+
+ MainLoopTimeout = 0; // Set default timeout
+
+
+ //
+ // If we've been asked to terminate,
+ // do so immediately
+ //
+
+ switch ( WaitStatus ) {
+ case NL_WAIT_TERMINATE: // service termination
+ goto Cleanup;
+
+
+ //
+ // Process timeouts and determine the timeout for the next iteration
+ //
+
+ case WAIT_TIMEOUT: // timeout
+ case NL_WAIT_TIMER: // someone changed a timer
+
+ //
+ // Assume there is no timeout to do.
+ //
+
+ Timeout = (DWORD) -1;
+ (VOID) NtQuerySystemTime( &TimeNow );
+
+
+ //
+ // On the primary, timeout announcements to BDCs
+ //
+
+ if ( NlGlobalRole == RolePrimary ) {
+ if ( TimerExpired( &NlGlobalPendingBdcTimer, &TimeNow, &Timeout )) {
+ NlPrimaryAnnouncementTimeout();
+ NlGlobalPendingBdcTimer.StartTime = TimeNow;
+ continue;
+ }
+ }
+
+
+
+
+
+ //
+ // Check the scavenger timer
+ //
+
+ if ( TimerExpired( &ScavengerTimer, &TimeNow, &Timeout ) ) {
+
+ if ( NlGlobalRole == RoleMemberWorkstation ) {
+
+ //
+ // On workstation run the scavenger on main thread.
+ //
+
+ NlScavenger(NULL);
+
+ } else {
+ //
+ // On server, start scavenger thread if it is not
+ // running already.
+ //
+
+ (VOID)NlStartScavengerThread();
+ }
+
+ ScavengerTimer.StartTime = TimeNow;
+ continue;
+ }
+
+
+
+
+
+ //
+ // Check the DC discovery timer.
+ //
+
+ if ( TimerExpired( &NlGlobalDcDiscoveryTimer, &TimeNow, &Timeout)) {
+
+ NlDcDiscoveryExpired( FALSE );
+
+ //
+ // The above operation might have taken a significant fraction
+ // of DISCOVERY_PERIOD. So, set the timer to the current time
+ // rather than TimeNow to allow time for responses to come in.
+ //
+ (VOID) NtQuerySystemTime( &NlGlobalDcDiscoveryTimer.StartTime );
+ continue;
+ }
+
+
+ //
+ // Check the API timer
+ //
+
+ if ( TimerExpired( &NlGlobalApiTimer, &TimeNow, &Timeout)) {
+
+ NlTimeoutApiClientSession();
+ NlGlobalApiTimer.StartTime = TimeNow;
+ continue;
+ }
+
+
+
+
+ //
+ // If we're the primary,
+ // periodically do announcements
+ //
+
+ if (NlGlobalRole == RolePrimary &&
+ TimerExpired( &AnnouncerTimer, &TimeNow, &Timeout ) ) {
+
+ NlPrimaryAnnouncement( 0 );
+ AnnouncerTimer.StartTime = TimeNow;
+ continue;
+ }
+
+ //
+ // If we've gotten this far,
+ // we know the only thing left to do is to wait for the next event.
+ //
+
+ MainLoopTimeout = Timeout;
+ continue;
+
+
+
+
+ //
+ // Process mailslot messages.
+ //
+
+ case NL_WAIT_MAILSLOT: // mailslot message
+
+ if ( !NlMailslotOverlappedResult( &Message,
+ &BytesRead,
+ &TransportName,
+ &IgnoreDuplicatesOfThisMessage )){
+ // Just continue if there really isn't a message
+ continue;
+ }
+
+ break;
+
+
+
+
+ //
+ // Process interesting changelog events.
+ //
+
+ case NL_WAIT_NOTIFY: // Something interesting Logged to change log
+
+
+
+
+ //
+ // If a "replicate immediately" event has happened,
+ // send a primary announcement.
+ //
+ LOCK_CHANGELOG();
+ if ( NlGlobalChangeLogReplicateImmediately ) {
+
+ NlGlobalChangeLogReplicateImmediately = FALSE;
+ NlGlobalChangeLogLanmanReplicateImmediately = FALSE;
+
+ UNLOCK_CHANGELOG();
+
+ //
+ // Ignore this event on BDCs.
+ //
+ // This event is never set on a BDC. It may have been set
+ // prior to the role change while this machine was a PDC.
+ //
+
+ if ( NlGlobalRole == RolePrimary ) {
+ NlPrimaryAnnouncement( ANNOUNCE_IMMEDIATE );
+ }
+ LOCK_CHANGELOG();
+ }
+
+ //
+ // If a "replicate immediately to Lanman" event happened,
+ // send a primary announcement to the lanman BDCs.
+ //
+ if ( NlGlobalChangeLogLanmanReplicateImmediately ) {
+
+ NlGlobalChangeLogLanmanReplicateImmediately = FALSE;
+
+ UNLOCK_CHANGELOG();
+
+ //
+ // Ignore this event on BDCs.
+ //
+ // This event is never set on a BDC. It may have been set
+ // prior to the role change while this machine was a PDC.
+ //
+
+ if ( NlGlobalRole == RolePrimary ) {
+ NlLanmanPrimaryAnnouncement();
+ }
+ LOCK_CHANGELOG();
+ }
+
+ //
+ // Process any notifications that need processing
+ //
+
+ while ( !IsListEmpty( &NlGlobalChangeLogNotifications ) ) {
+ PLIST_ENTRY ListEntry;
+ PCHANGELOG_NOTIFICATION Notification;
+
+ ListEntry = RemoveHeadList( &NlGlobalChangeLogNotifications );
+ UNLOCK_CHANGELOG();
+
+ Notification = CONTAINING_RECORD(
+ ListEntry,
+ CHANGELOG_NOTIFICATION,
+ Next );
+
+ switch ( Notification->EntryType ) {
+ case ChangeLogLmServerAdded:
+ // This event happens on a PDC only
+ (VOID) NlAddBdcServerSession( Notification->ObjectRid,
+ NULL,
+ SS_BDC | SS_LM_BDC );
+ break;
+
+ case ChangeLogLmServerDeleted:
+ // This event happens on a PDC only
+ NlFreeLmBdcServerSession( Notification->ObjectRid );
+ break;
+
+ case ChangeLogNtServerAdded:
+ // This event happens on a PDC only
+ (VOID) NlAddBdcServerSession( Notification->ObjectRid,
+ &Notification->ObjectName,
+ SS_BDC );
+ break;
+
+ case ChangeLogWorkstationDeleted:
+ case ChangeLogTrustedDomainDeleted:
+ case ChangeLogNtServerDeleted:
+ // This event happens on both a PDC and BDC
+ NlFreeServerSessionForAccount( &Notification->ObjectName );
+ break;
+
+ case ChangeLogTrustAdded:
+ case ChangeLogTrustDeleted:
+ if ( NlGlobalRole == RolePrimary ) {
+ NlUpdateTrustListBySid( Notification->ObjectSid, NULL );
+ }
+ break;
+
+ default:
+ NlPrint((NL_CRITICAL,
+ "Invalid ChangeLogNotification: %ld %wZ\n",
+ Notification->EntryType,
+ &Notification->ObjectName ));
+
+ }
+
+ NetpMemoryFree( Notification );
+ LOCK_CHANGELOG();
+ }
+
+ UNLOCK_CHANGELOG();
+ continue;
+
+
+ default:
+ NetStatus = GetLastError();
+ NlExit(NELOG_NetlogonSystemError, NetStatus, LogErrorAndNetStatus, NULL);
+ goto Cleanup;
+ }
+
+
+
+
+ //
+ // ASSERT: Message and BytesRead describe a newly read message
+ //
+ //
+ // Got a message. Check for bad length just in case.
+ //
+
+ if (BytesRead < sizeof(unsigned short) ) {
+ NlPrint((NL_CRITICAL,"message size bad %ld\n", BytesRead ));
+ continue; // Need at least an opcode
+ }
+
+ //
+ // Here with a request to process in the Message.
+ //
+
+ Version = NetpLogonGetMessageVersion( Message, &BytesRead, &VersionFlags );
+
+ if (Version == LMUNKNOWNNT_MESSAGE) {
+
+ //
+ // received a non-supported NT message.
+ //
+
+ NlPrint((NL_CRITICAL,
+ "Received a non-supported NT message, Opcode is 0x%x\n",
+ ((PNETLOGON_LOGON_QUERY)Message)->Opcode ));
+
+ continue;
+ }
+
+
+ switch ( ((PNETLOGON_LOGON_QUERY)Message)->Opcode) {
+
+ //
+ // Handle a logon request from a UAS client
+ //
+
+ case LOGON_REQUEST: {
+ USHORT RequestCount;
+
+ //
+ // Unmarshall the incoming message.
+ //
+
+ if ( Version == LMNT_MESSAGE ) {
+ break;
+ }
+
+ Where = ((PNETLOGON_LOGON_REQUEST)Message)->ComputerName;
+ if ( !NetpLogonGetOemString(
+ (PNETLOGON_LOGON_REQUEST)Message,
+ BytesRead,
+ &Where,
+ sizeof( ((PNETLOGON_LOGON_REQUEST)Message)->ComputerName),
+ &OemWorkstationName )) {
+ break;
+ }
+ if ( !NetpLogonGetOemString(
+ (PNETLOGON_LOGON_REQUEST)Message,
+ BytesRead,
+ &Where,
+ sizeof( ((PNETLOGON_LOGON_REQUEST)Message)->UserName),
+ &AnsiUserName )) {
+ break;
+ }
+ if ( !NetpLogonGetOemString(
+ (PNETLOGON_LOGON_REQUEST)Message,
+ BytesRead,
+ &Where,
+ sizeof( ((PNETLOGON_LOGON_REQUEST)Message)->MailslotName),
+ &OemMailslotName )) {
+ break;
+ }
+
+ // LM 2.x puts request count right before token
+ Where = Message + BytesRead - 2;
+ if ( !NetpLogonGetBytes(
+ (PNETLOGON_LOGON_REQUEST)Message,
+ BytesRead,
+ &Where,
+ sizeof( ((PNETLOGON_LOGON_REQUEST)Message)->RequestCount),
+ &RequestCount )) {
+ break;
+ }
+
+ //
+ // Handle the logon request
+ //
+
+ UnicodeUserName = NetpLogonOemToUnicode( AnsiUserName );
+ if ( UnicodeUserName == NULL ) {
+ break;
+ }
+
+ IgnoreDuplicatesOfThisMessage = LogonRequestHandler(
+ Version,
+ UnicodeUserName,
+ RequestCount,
+ OemWorkstationName,
+ OemMailslotName,
+ TransportName,
+ USER_NORMAL_ACCOUNT );
+
+ NetpMemoryFree( UnicodeUserName );
+
+
+ break;
+ }
+
+ //
+ // Handle a logon request from a SAM client
+ //
+
+ case LOGON_SAM_LOGON_REQUEST: {
+ USHORT RequestCount;
+ ULONG AllowableAccountControlBits;
+
+ //
+ // Unmarshall the incoming message.
+ //
+
+
+ if ( Version != LMNT_MESSAGE ) {
+ break;
+ }
+
+ RequestCount = ((PNETLOGON_SAM_LOGON_REQUEST)Message)->RequestCount;
+
+ Where = (PCHAR)
+ (((PNETLOGON_SAM_LOGON_REQUEST)Message)->UnicodeComputerName);
+
+ if ( !NetpLogonGetUnicodeString(
+ (PNETLOGON_SAM_LOGON_REQUEST)Message,
+ BytesRead,
+ &Where,
+ sizeof( ((PNETLOGON_SAM_LOGON_REQUEST)Message)->
+ UnicodeComputerName),
+ &UnicodeWorkstationName )) {
+ break;
+ }
+ if ( !NetpLogonGetUnicodeString(
+ (PNETLOGON_SAM_LOGON_REQUEST)Message,
+ BytesRead,
+ &Where,
+ sizeof( ((PNETLOGON_SAM_LOGON_REQUEST)Message)->
+ UnicodeUserName),
+ &UnicodeUserName )) {
+ break;
+ }
+ if ( !NetpLogonGetOemString(
+ (PNETLOGON_SAM_LOGON_REQUEST)Message,
+ BytesRead,
+ &Where,
+ sizeof( ((PNETLOGON_SAM_LOGON_REQUEST)Message)->
+ MailslotName),
+ &OemMailslotName )) {
+ break;
+ }
+ if ( !NetpLogonGetBytes(
+ (PNETLOGON_SAM_LOGON_REQUEST)Message,
+ BytesRead,
+ &Where,
+ sizeof( ((PNETLOGON_SAM_LOGON_REQUEST)Message)->
+ AllowableAccountControlBits),
+ &AllowableAccountControlBits )) {
+ break;
+ }
+
+ //
+ // compare it with primary domain id.
+ //
+ // Don't make the following check mandatory. Chicago is
+ // considering using this message type. Oct 1993.
+ //
+
+
+ if( Where < ((PCHAR)Message + BytesRead ) ) {
+
+ DWORD DomainSidSize;
+
+ //
+ // Read Domain SID Length
+ //
+
+ if ( !NetpLogonGetBytes(
+ (PNETLOGON_SAM_LOGON_REQUEST)Message,
+ BytesRead,
+ &Where,
+ sizeof( ((PNETLOGON_SAM_LOGON_REQUEST)Message)->
+ DomainSidSize),
+ &DomainSidSize )) {
+
+ break;
+
+ }
+
+
+ //
+ // get and compare SID
+ //
+
+ if( DomainSidSize > 0 ) {
+
+ PCHAR DomainSid;
+
+ if ( !NetpLogonGetDomainSID(
+ (PNETLOGON_SAM_LOGON_REQUEST)Message,
+ BytesRead,
+ &Where,
+ DomainSidSize,
+ &DomainSid )) {
+
+ break;
+ }
+
+ //
+ // compare domain SIDs
+ //
+
+ if( !RtlEqualSid( NlGlobalPrimaryDomainId, DomainSid ) ) {
+
+ LPWSTR AlertStrings[4];
+
+ //
+ // alert admin.
+ //
+
+ AlertStrings[0] = UnicodeWorkstationName;
+ AlertStrings[1] = NlGlobalUnicodeComputerName;
+ AlertStrings[2] = NlGlobalUnicodeDomainName;
+ AlertStrings[3] = NULL;
+
+ RaiseAlert( ALERT_NetLogonUntrustedClient,
+ AlertStrings );
+
+ //
+ // Save the info in the eventlog
+ //
+
+ NlpWriteEventlog(
+ ALERT_NetLogonUntrustedClient,
+ EVENTLOG_ERROR_TYPE,
+ NULL,
+ 0,
+ AlertStrings,
+ 3 );
+
+ break;
+ }
+ }
+ }
+
+ OemWorkstationName =
+ NetpLogonUnicodeToOem( UnicodeWorkstationName );
+
+ if( OemWorkstationName == NULL ) {
+
+ NlPrint((NL_CRITICAL,
+ "Out of memory to send logon response\n"));
+ break;
+ }
+
+ //
+ // Handle the logon request
+ //
+
+ IgnoreDuplicatesOfThisMessage = LogonRequestHandler(
+ Version,
+ UnicodeUserName,
+ RequestCount,
+ OemWorkstationName,
+ OemMailslotName,
+ TransportName,
+ AllowableAccountControlBits );
+
+ NetpMemoryFree( OemWorkstationName );
+
+ break;
+ }
+
+ //
+ // Handle Logon Central query.
+ //
+ // This query could be sent by either LM1.0, LM 2.0 or LM NT Netlogon
+ // services. We ignore LM 2.0 and LM NT queries since they are merely
+ // trying
+ // to find out if there are any LM1.0 netlogon services in the domain.
+ // For LM 1.0 we respond with a LOGON_CENTRAL_RESPONSE to prevent the
+ // starting LM1.0 netlogon service from starting.
+ //
+
+ case LOGON_CENTRAL_QUERY:
+
+ if ( Version != LMUNKNOWN_MESSAGE ) {
+ break;
+ }
+
+ //
+ // Drop on through to LOGON_DISTRIB_QUERY to send the response
+ //
+
+
+ //
+ // Handle a Logon Disrib query
+ //
+ // LM2.0 NETLOGON server never sends this query hence it
+ // must be another LM1.0 NETLOGON server trying to start up
+ // in non-centralized mode. LM2.0 NETLOGON server will respond
+ // with LOGON_CENTRAL_RESPONSE to prevent this.
+ //
+
+ case LOGON_DISTRIB_QUERY:
+
+
+ //
+ // Unmarshall the incoming message.
+ //
+
+ Where = ((PNETLOGON_LOGON_QUERY)Message)->ComputerName;
+ if ( !NetpLogonGetOemString(
+ Message,
+ BytesRead,
+ &Where,
+ sizeof( ((PNETLOGON_LOGON_QUERY)Message)->ComputerName ),
+ &OemWorkstationName )) {
+ break;
+ }
+ if ( !NetpLogonGetOemString(
+ Message,
+ BytesRead,
+ &Where,
+ sizeof( ((PNETLOGON_LOGON_QUERY)Message)->MailslotName ),
+ &OemMailslotName )) {
+ break;
+ }
+
+ //
+ // Build the response
+ //
+
+ ((PNETLOGON_LOGON_QUERY)resp_buf)->Opcode = LOGON_CENTRAL_RESPONSE;
+ ResponseBufferSize = sizeof( unsigned short); // opcode only
+
+ (VOID) NlBrowserSendDatagram( OemWorkstationName,
+ TransportName,
+ OemMailslotName,
+ resp_buf,
+ ResponseBufferSize );
+
+ break;
+
+
+ //
+ // Handle LOGON_PRIMARY_QUERY
+ //
+ // If we're the PDC, always respond to this message
+ // identifying ourselves.
+ //
+ // Otherwise, only respond to the message if it is from a downlevel
+ // netlogon trying to see if it can start up as a PDC. In that
+ // case, pretend we are a PDC to prevent the downlevel PDC from
+ // starting.
+ //
+ //
+
+ case LOGON_PRIMARY_QUERY:
+
+
+ //
+ // Unmarshall the incoming message.
+ //
+
+
+ Where =((PNETLOGON_LOGON_QUERY)Message)->ComputerName;
+ if ( !NetpLogonGetOemString(
+ Message,
+ BytesRead,
+ &Where,
+ sizeof( ((PNETLOGON_LOGON_QUERY)Message)->ComputerName ),
+ &OemWorkstationName )) {
+
+ break;
+ }
+ if ( !NetpLogonGetOemString(
+ Message,
+ BytesRead,
+ &Where,
+ sizeof( ((PNETLOGON_LOGON_QUERY)Message)->MailslotName ),
+ &OemMailslotName )) {
+ break;
+ }
+
+ //
+ // Handle the primary query request
+ //
+
+ IgnoreDuplicatesOfThisMessage =
+ PrimaryQueryHandler( Version,
+ OemWorkstationName,
+ OemMailslotName,
+ TransportName );
+
+
+ break;
+
+
+ //
+ // Handle LOGON_FAIL_PRIMARY
+ //
+
+ case LOGON_FAIL_PRIMARY:
+
+ //
+ // If we are the primary,
+ // let everyone know we are really alive.
+ //
+
+ if ( NlGlobalRole == RolePrimary ) {
+ // Send a primary start to the LM BDCs
+ NlAnnouncePrimaryStart();
+ // Send a UAS_CHANGE to everyone.
+ NlPrimaryAnnouncement( 0 );
+ break;
+ }
+
+ break;
+
+
+ //
+ // Handle LOGON_UAS_CHANGE
+ //
+
+ case LOGON_UAS_CHANGE:
+
+
+ //
+ // Only accept messages from an NT PDC.
+ //
+
+ if ( Version != LMNT_MESSAGE ) {
+ break;
+ }
+
+ //
+ // Unblock replication thread if neccessary
+ //
+
+ (VOID) NlCheckUpdateNotices(
+ (PNETLOGON_DB_CHANGE)Message,
+ BytesRead );
+
+ break;
+
+
+
+
+
+ //
+ // Handle LOGON_START_PRIMARY
+ //
+ //
+ // We may be here under any of these three cases:
+ // 1) A Primary is coming up for the very first time.
+ // 2) A previous primary went down and the
+ // same or a new primary is starting.
+ //
+
+ case LOGON_START_PRIMARY:
+
+ //
+ // Ignore our own broadcast.
+ //
+
+ if (NlGlobalRole == RolePrimary) {
+ break;
+ }
+
+
+ //
+ // Unmarshall the message.
+ //
+
+ if ( Version != LMNT_MESSAGE ) {
+ break;
+ }
+
+
+ Where =((PNETLOGON_PRIMARY)Message)->PrimaryDCName;
+ if ( !NetpLogonGetOemString(
+ Message,
+ BytesRead,
+ &Where,
+ sizeof( ((PNETLOGON_PRIMARY)Message)->PrimaryDCName ),
+ &AnsiTemp )) {
+ break;
+ }
+
+ if ( !NetpLogonGetUnicodeString(
+ Message,
+ BytesRead,
+ &Where,
+ sizeof( ((PNETLOGON_PRIMARY)Message)->UnicodePrimaryDCName),
+ &UnicodeTemp )) {
+ break;
+ }
+
+ //
+ // If the domain name is in the message,
+ // ensure this message is from correct domain.
+ //
+
+ if( Where < ((PCHAR)Message + BytesRead ) ) {
+
+ LPWSTR UnicodeDomainName;
+
+ if ( !NetpLogonGetUnicodeString(
+ Message,
+ BytesRead,
+ &Where,
+ sizeof(((PNETLOGON_PRIMARY)Message)->UnicodeDomainName),
+ &UnicodeDomainName )) {
+
+ NlPrint((NL_CRITICAL,
+ FORMAT_LPWSTR
+ ": Primary Start message had invalid domain name.\n",
+ UnicodeTemp ));
+
+ break;
+ }
+
+ if ( NlNameCompare( UnicodeDomainName,
+ NlGlobalUnicodeDomainName,
+ NAMETYPE_DOMAIN ) != 0 ) {
+
+ NlPrint((NL_CRITICAL,
+ FORMAT_LPWSTR
+ ": Primary Start message from wrong domain "
+ FORMAT_LPWSTR "\n",
+ UnicodeTemp,
+ UnicodeDomainName ));
+
+ break;
+ }
+
+
+ NlPrint((NL_MAILSLOT,
+ FORMAT_LPWSTR
+ ": Primary Start message from correct domain "
+ FORMAT_LPWSTR "\n",
+ UnicodeTemp,
+ UnicodeDomainName ));
+
+ }
+
+ //
+ // Set up a session with the new primary.
+ //
+
+ (VOID) NlNewSessionSetup( UnicodeTemp );
+
+ break;
+
+
+
+ //
+ // Handle DC discovery responses
+ //
+
+ case LOGON_SAM_LOGON_RESPONSE:
+ case LOGON_SAM_USER_UNKNOWN:
+ case LOGON_SAM_PAUSE_RESPONSE:
+
+ //
+ // Only accept messages from an NT PDC.
+ //
+
+ if ( Version != LMNT_MESSAGE ) {
+ break;
+ }
+
+
+ NlDcDiscoveryHandler(
+ (PNETLOGON_SAM_LOGON_RESPONSE)Message,
+ BytesRead,
+ TransportName,
+ Version );
+
+ break;
+
+ //
+ // Messages used for NetLogonEnum support.
+ //
+ // Simply ignore the messages
+ //
+
+ case LOGON_NO_USER:
+ case LOGON_RELOGON_RESPONSE:
+ case LOGON_WKSTINFO_RESPONSE:
+ case LOGON_SAM_WKSTINFO_RESPONSE:
+
+ break;
+
+
+ //
+ // Handle unidentified opcodes
+ //
+
+ default:
+
+ //
+ // Unknown request, continue for re-issue of read.
+ //
+
+ NlPrint((NL_CRITICAL,
+ "Unknown op-code in mailslot message 0x%x\n",
+ ((PNETLOGON_LOGON_QUERY)Message)->Opcode ));
+
+ break;
+ }
+
+ }
+
+Cleanup:
+
+ return;
+}
+
+
+int
+NlNetlogonMain(
+ IN DWORD argc,
+ IN LPWSTR *argv
+ )
+
+/*++
+
+Routine Description:
+
+ Main routine for Netlogon service.
+
+ This routine initializes the netlogon service. This thread becomes
+ the thread that reads logon mailslot messages.
+
+Arguments:
+
+ argc, argv - Command line arguments for the service.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ NET_API_STATUS NetStatus;
+ PDB_INFO DBInfo;
+ DWORD i;
+
+
+
+ //
+ // Initialize all global variable.
+ //
+ // We can't rely on this happening at load time since this address
+ // space is shared by other services.
+ //
+
+ NlGlobalAnsiPrimaryName[0] = '\0';
+ NlGlobalUncPrimaryName[0] = L'\0';
+ NlGlobalUnicodePrimaryName = NlGlobalUncPrimaryName;
+ NlGlobalUasCompatibilityMode = TRUE;
+ // NlGlobalInfiniteTime.HighPart = 0x7FFFFFFF;
+ // NlGlobalInfiniteTime.LowPart = 0xFFFFFFFF;
+ NlGlobalMailslotHandle = NULL;
+ NlGlobalRpcServerStarted = FALSE;
+ NlGlobalAnsiComputerName = NULL;
+ NlGlobalUncUnicodeComputerName[0] = L'\0';
+ NlGlobalUnicodeComputerName = NlGlobalUncUnicodeComputerName;
+ RtlInitUnicodeString( &NlGlobalUnicodeComputerNameString, NULL );
+ NlGlobalAnsiDomainName = NULL;
+ NlGlobalUnicodeDomainName[0] = L'\0';
+ NlGlobalPrimaryDomainId = NULL;
+ NlGlobalSamServerHandle = NULL;
+ NlGlobalPolicyHandle = NULL;
+ NlGlobalRole = RoleMemberWorkstation;
+ NlGlobalUnicodeScriptPath[0] = L'\0';
+ NlGlobalPulseParameter = DEFAULT_PULSE;
+ NlGlobalRandomizeParameter = DEFAULT_RANDOMIZE;
+ NlGlobalSynchronizeParameter = DEFAULT_SYNCHRONIZE;
+ NlGlobalPulseMaximumParameter = DEFAULT_PULSEMAXIMUM;
+ NlGlobalPulseConcurrencyParameter = DEFAULT_PULSECONCURRENCY;
+ NlGlobalPulseTimeout1Parameter = DEFAULT_PULSETIMEOUT1;
+ NlGlobalPulseTimeout2Parameter = DEFAULT_PULSETIMEOUT2;
+ NlGlobalNetlogonSecurityDescriptor = NULL;
+ NlGlobalTooManyGlobalGroups = FALSE;
+
+
+ NlGlobalServerSessionHashTable = NULL;
+ InitializeListHead( &NlGlobalServerSessionTable );
+ InitializeListHead( &NlGlobalBdcServerSessionList );
+ NlGlobalBdcServerSessionCount = 0;
+
+ NlGlobalTransportList = NULL;
+ NlGlobalTransportCount = 0;
+
+ InitializeListHead( &NlGlobalPendingBdcList );
+ NlGlobalPendingBdcCount = 0;
+ NlGlobalPendingBdcTimer.Period = (DWORD) MAILSLOT_WAIT_FOREVER;
+
+ InitializeListHead( &NlGlobalTrustList );
+ NlGlobalTrustListLength = 0;
+
+ NlGlobalSSICritSectInit = FALSE;
+ NlGlobalTerminateEvent = NULL;
+ NlGlobalReplicatorTerminateEvent = NULL;
+ NlGlobalStartedEvent = NULL;
+ NlGlobalTimerEvent = NULL;
+
+ NlGlobalServiceHandle = (SERVICE_STATUS_HANDLE) NULL;
+
+ NlGlobalServiceStatus.dwServiceType = SERVICE_WIN32;
+ NlGlobalServiceStatus.dwCurrentState = SERVICE_START_PENDING;
+ NlGlobalServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP |
+ SERVICE_ACCEPT_PAUSE_CONTINUE;
+ NlGlobalServiceStatus.dwCheckPoint = 0;
+ NlGlobalServiceStatus.dwWaitHint = NETLOGON_INSTALL_WAIT;
+
+ SET_SERVICE_EXITCODE(
+ NO_ERROR,
+ NlGlobalServiceStatus.dwWin32ExitCode,
+ NlGlobalServiceStatus.dwServiceSpecificExitCode
+ );
+
+ NlGlobalClientSession = NULL;
+ NlGlobalTrustedDomainList = NULL;
+ NlGlobalTrustedDomainCount = 0;
+ NlGlobalTrustedDomainListKnown = FALSE;
+ NlGlobalTrustedDomainListTime.QuadPart = 0;
+ NlGlobalDcDiscoveryCount = 0;
+ NlGlobalDcDiscoveryTimer.Period = (DWORD) MAILSLOT_WAIT_FOREVER;
+ NlGlobalBindingHandleCount = 0;
+ NlGlobalApiTimer.Period = (DWORD) MAILSLOT_WAIT_FOREVER;
+
+#if DBG
+ NlGlobalTrace = 0;
+ NlGlobalLogFile = INVALID_HANDLE_VALUE;
+ NlGlobalDebugSharePath = NULL;
+#endif // DBG
+
+
+ for( i = 0, DBInfo = &NlGlobalDBInfoArray[0];
+ i < NUM_DBS;
+ i++, DBInfo++ ) {
+
+ RtlZeroMemory( DBInfo, sizeof(*DBInfo) );
+
+ // Force a partial sync on all databases to let the PDC know
+ // what our serial number is.
+ DBInfo->UpdateRqd = TRUE;
+
+ }
+ NlGlobalFirstTimeFullSync = FALSE;
+
+ NlGlobalScavengerThreadHandle = NULL;
+ NlGlobalScavengerTerminate = FALSE;
+
+ InitChangeLogDesc( &NlGlobalRedoLogDesc )
+ NlGlobalRedoLogDesc.RedoLog = TRUE;
+
+
+
+ //
+ // Setup things needed before NlExit can be called
+ //
+
+ NlGlobalTerminate = FALSE;
+
+ NlGlobalTerminateEvent = CreateEvent( NULL, // No security attributes
+ TRUE, // Must be manually reset
+ FALSE, // Initially not signaled
+ NULL ); // No name
+
+ if ( NlGlobalTerminateEvent == NULL ) {
+ NetStatus = GetLastError();
+ NlPrint((NL_CRITICAL, "Cannot create termination Event %lu\n",
+ NetStatus ));
+ return (int) NetStatus;
+ }
+
+
+ //
+ // Initialize trust table crit sect.
+ //
+
+ InitializeCriticalSection( &NlGlobalTrustListCritSect );
+ InitializeCriticalSection( &NlGlobalReplicatorCritSect );
+ InitializeCriticalSection( &NlGlobalDbInfoCritSect );
+ InitializeCriticalSection( &NlGlobalDcDiscoveryCritSect );
+
+
+ //
+ // Initialize scavenger thread crit sect.
+ //
+
+ InitializeCriticalSection( &NlGlobalScavengerCritSect );
+
+
+ //
+ // Tell the service controller we've started.
+ //
+ // ?? - Need to set up security descriptor.
+ //
+
+ NlPrint((NL_INIT,"Calling RegisterServiceCtrlHandler\n"));
+
+ NlGlobalServiceHandle =
+ RegisterServiceCtrlHandler( SERVICE_NETLOGON, NlControlHandler);
+
+ if (NlGlobalServiceHandle == (SERVICE_STATUS_HANDLE) NULL) {
+ LPWSTR MsgStrings[1];
+
+ NetStatus = GetLastError();
+
+ NlPrint((NL_CRITICAL, "RegisterServiceCtrlHandler failed %lu\n",
+ NetStatus ));
+
+ MsgStrings[0] = (LPWSTR) NetStatus;
+
+ NlpWriteEventlog (NELOG_NetlogonFailedToRegisterSC,
+ EVENTLOG_ERROR_TYPE,
+ (LPBYTE) &NetStatus,
+ sizeof(NetStatus),
+ MsgStrings,
+ 1 | LAST_MESSAGE_IS_NETSTATUS );
+
+ return (int) NetStatus;
+ }
+
+ if ( !GiveInstallHints( FALSE ) ) {
+ goto Cleanup;
+ }
+
+
+
+ //
+ // Nlparse the command line (.ini) arguments
+ // it will set globals reflecting switch settings
+ //
+
+ if (! Nlparse() ) {
+ goto Cleanup;
+ }
+
+ NlPrint((NL_INIT,"Command line parsed successfully ...\n"));
+
+#ifdef notdef
+#if DBG
+ if ( NlGlobalTrace == 0) {
+ NlGlobalTrace = 0x2284FFFF;
+ }
+#endif // DBG
+#endif // notdef
+
+
+
+ //
+ // Enter the debugger.
+ //
+ // Wait 'til now since we don't want the service controller to time us out.
+ //
+
+
+ IF_DEBUG( BREAKPOINT ) {
+ DbgBreakPoint( );
+ }
+
+
+
+ //
+ // Do startup checks, initialize data structs and do prelim setups
+ //
+
+ if ( !NlInit() ) {
+ goto Cleanup;
+ }
+
+
+
+
+
+ //
+ // Loop till the service is to exit.
+ //
+
+ NlMainLoop();
+
+ //
+ // Common exit point
+ //
+
+Cleanup:
+
+ //
+ // Cleanup and return to our caller.
+ //
+
+ return (int) NlCleanup();
+ UNREFERENCED_PARAMETER( argc );
+ UNREFERENCED_PARAMETER( argv );
+
+}
diff --git a/private/net/svcdlls/logonsrv/server/netlogon.def b/private/net/svcdlls/logonsrv/server/netlogon.def
new file mode 100644
index 000000000..9afd4c24e
--- /dev/null
+++ b/private/net/svcdlls/logonsrv/server/netlogon.def
@@ -0,0 +1,15 @@
+LIBRARY NETLOGON
+
+DESCRIPTION 'NETLOGON'
+
+EXPORTS
+
+ I_NetNotifyRole
+ I_NetNotifyDelta
+ I_NetNotifyMachineAccount
+ I_NetGetAnyDCName
+ NlNetlogonMain
+ NetrLogonSamLogon
+ NetrLogonSamLogoff
+
+DATA SINGLE SHARED
diff --git a/private/net/svcdlls/logonsrv/server/netlogon.prf b/private/net/svcdlls/logonsrv/server/netlogon.prf
new file mode 100644
index 000000000..b631fe81f
--- /dev/null
+++ b/private/net/svcdlls/logonsrv/server/netlogon.prf
@@ -0,0 +1,507 @@
+NlComputeChallenge@4
+NlTimeHasElapsed@12
+NlSessionSetup@8
+NlStartApiClientSession@8
+NlComputeCredentials@12
+NlWaitForSingleObject@8
+NlResetWriterClientSession@4
+NlBuildAuthenticator@12
+NlUpdateSeed@12
+NlpDecryptLogonInformation@12
+NlpEncryptLogonInformation@12
+NlpUserValidateHigher@24
+NlpUserValidate@24
+NetrLogonSamLogon@36
+NlFinishApiClientSession@8
+NlRefClientSession@4
+NlUnrefClientSession@4
+NlFindNamedClientSession@4
+NetlogonDllInit@12
+NlpDecryptValidationInformation@16
+TimerExpired@12
+NlOpenSecret@12
+NlTimeToReauthenticate@4
+NlMakeSessionKey@16
+NlSetStatusClientSession@8
+logon_NetrLogonControl2@4
+_fgs__NETLOGON_INFO_2@4
+_fgu__NETLOGON_CONTROL_DATA_INFORMATION@8
+NetrLogonControl2@20
+NlTimeoutOneApiClientSession@4
+NlTimeoutApiClientSession@0
+NetpCreateWellKnownSids@4
+NetpFreeWellKnownSids@0
+NetpAllocateAndInitializeSid@12
+NetpDomainIdToSid@12
+NetpCreateSecurityDescriptor@20
+NetpCreateSecurityObject@24
+NetpDeleteSecurityObject@4
+NetpInitializeAllowedAce@24
+NetpInitializeDeniedAce@24
+NetpInitializeAuditAce@24
+NetpAccessCheckAndAudit@20
+DbgPrint
+DosGetMessage@28
+DosInsMessage@28
+DosPutMessage@12
+MyAllocUnicode@8
+MyAllocUnicodeVector@12
+MyFreeUnicode@4
+MyFreeUnicodeVector@8
+I_BrowserResetNetlogonState@4
+I_NetDatabaseDeltas@32
+I_NetDatabaseSync@32
+I_NetLogonSamLogoff@24
+I_NetLogonSamLogon@36
+I_NetNameCompare@20
+I_NetPathCanonicalize@28
+I_NetServerAuthenticate@24
+I_NetServerAuthenticate2@28
+I_NetServerPasswordSet@28
+I_NetServerReqChallenge@16
+LsaIEnumerateSecrets@20
+LsaIFree_LSAPR_ACCOUNT_ENUM_BUFFER@4
+LsaIFree_LSAPR_CR_CIPHER_VALUE@4
+LsaIFree_LSAPR_POLICY_INFORMATION@8
+LsaIFree_LSAPR_PRIVILEGE_SET@4
+LsaIFree_LSAPR_SR_SECURITY_DESCRIPTOR@4
+LsaIFree_LSAPR_TRUSTED_DOMAIN_INFO@8
+LsaIFree_LSAPR_TRUSTED_ENUM_BUFFER@4
+LsaIFree_LSAPR_UNICODE_STRING@4
+LsaIOpenPolicyTrusted@4
+LsaISetSerialNumberPolicy@16
+LsaISetTimesSecret@12
+LsarAddPrivilegesToAccount@8
+LsarClose@4
+LsarCreateAccount@16
+LsarCreateSecret@16
+LsarCreateTrustedDomain@16
+LsarDelete@4
+LsarEnumerateAccounts@16
+LsarEnumeratePrivilegesAccount@8
+LsarEnumerateTrustedDomains@16
+LsarGetQuotasForAccount@8
+LsarGetSystemAccessAccount@8
+LsarLookupPrivilegeName@12
+LsarLookupPrivilegeValue@12
+LsarOpenAccount@16
+LsarOpenSecret@16
+LsarOpenTrustedDomain@16
+LsarQueryInfoTrustedDomain@12
+LsarQueryInformationPolicy@12
+LsarQuerySecret@20
+LsarQuerySecurityObject@12
+LsarRemovePrivilegesFromAccount@12
+LsarSetInformationPolicy@12
+LsarSetInformationTrustedDomain@12
+LsarSetQuotasForAccount@8
+LsarSetSecret@12
+LsarSetSecurityObject@12
+LsarSetSystemAccessAccount@8
+MIDL_user_allocate@4
+MIDL_user_free@4
+MIDL_user_reallocate@8
+MIDL_user_size@4
+MsvSamLogoff@12
+MsvSamValidate@52
+NetAlertRaiseEx@16
+NetApiBufferAllocate@8
+NetApiBufferFree@4
+NetGetDCName@12
+NetServerGetInfo@12
+NetShareAdd@16
+NetShareDel@12
+NetShareGetInfo@16
+NetUseDel@12
+NetapipBufferAllocate@8
+NetpGetAllowedAce@12
+NetpAccountControlToFlags@8
+NetpDeltaTimeToSeconds@8
+NetpSecondsToDeltaTime@4
+NetpAliasMemberToPriv@16
+NetpGetElapsedSeconds@4
+NetpConvertWorkstationList@4
+NetpAllocStringFromTStr@4
+NetpAllocStrFromStr@4
+NetpAllocStrFromWStr@4
+NetpAllocTStrFromString@8
+NetpAllocWStrFromStr@4
+NetpAllocWStrFromWStr@4
+NetpPackString@12
+NetpCopyStringToBuffer@20
+NetpCopyDataToBuffer@24
+NetpAllocateEnumBuffer@24
+NetpNtStatusToApiStatus@4
+NetpApiStatusToNtStatus@4
+NetpAssertFailed@16
+NetpDbgPrint
+NetpHexDump@8
+NetpCloseConfigData@4
+NetpCopyStringToTStr@8
+NetpCopyWStrToStr@8
+NetpCopyStrToWStr@8
+NetpNCopyStrToWStr@12
+NetpNCopyWStrToStr@12
+NetpGetConfigBool@16
+NetpGetConfigDword@16
+NetpGetWinRegConfigMaxSizes@12
+NetpGetConfigMaxSizes@12
+NetpGetConfigValue@12
+NetpExpandConfigString@12
+NetpInitOemString@8
+NetpIsRemote@16
+NetpIsServiceStarted@4
+NetpIsRemoteServiceStarted@8
+NetpGetPrimaryGroupFromMacField@8
+NetpIsComputerNameValid@4
+NetpIsLocalNameValid@4
+NetpCanonRemoteName@4
+NetpIsGroupNameValid@4
+NetpIsMacPrimaryGroupFieldValid@4
+NetpIsPrintQueueNameValid@4
+NetpIsRemoteNameValid@4
+NetpIsUncComputerNameValid@4
+NetpNamesMatch@8
+NetpIsUserNameValid@4
+I_NetNameCanonicalize@24
+NetpLogonPutOemString@12
+NetpLogonPutUnicodeString@12
+NetpLogonPutDomainSID@12
+NetpLogonPutBytes@12
+NetpLogonPutDBInfo@8
+NetpLogonGetMessageVersion@12
+NetpLogonGetOemString@20
+NetpLogonGetUnicodeString@20
+NetpLogonGetDomainSID@20
+NetpLogonGetBytes@20
+NetpLogonGetDBInfo@16
+NetpLogonOemToUnicode@4
+NetpLogonUnicodeToOem@4
+NetpLogonWriteMailslot@12
+NetpLogonCreateRandomMailslot@8
+NetpLogonGetDCName@16
+NetpMemoryAllocate@4
+NetpMemoryFree@4
+NetpMemoryReallocate@8
+NetpOpenConfigData@16
+NetpOpenConfigDataEx@20
+NetpRotateLogonHoursPhase1@8
+NetpRotateLogonHoursPhase2@12
+NetpRotateLogonHours@12
+NetpWriteEventlog@28
+NetpwPathCompare@16
+NetpwPathCanonicalize@24
+CanonicalizePathName@20
+NetpwPathType@12
+GetToken@16
+NtAccessCheckAndAuditAlarm@44
+NtClose@4
+NtCreateEvent@20
+NtOpenEvent@12
+NtOpenProcessToken@12
+NtOpenThreadToken@16
+NtQueryInformationToken@20
+NtQuerySystemTime@4
+NtResetEvent@8
+NtSetEvent@8
+RtlAddAce@20
+RtlAllocateAndInitializeSid@44
+RtlAssert@16
+RtlCompareMemory@12
+RtlConvertSidToUnicodeString@12
+RtlCopySid@12
+RtlCreateAcl@12
+RtlCreateEnvironment@8
+RtlCreateSecurityDescriptor@8
+RtlDeleteSecurityObject@4
+RtlDestroyEnvironment@4
+RtlDetermineDosPathNameType_U@4
+RtlEqualComputerName@8
+RtlEqualDomainName@8
+RtlEqualSid@8
+RtlEqualUnicodeString@12
+RtlExpandEnvironmentStrings_U@16
+RtlExtendedIntegerMultiply@12
+RtlExtendedMagicDivide@20
+RtlFreeOemString@4
+RtlFreeUnicodeString@4
+RtlGetAce@12
+RtlGetDaclSecurityDescriptor@16
+RtlGetNtProductType@4
+RtlInitAnsiString@8
+RtlInitString@8
+RtlInitUnicodeString@8
+RtlInitializeSid@12
+RtlIntegerToChar@16
+RtlIsDosDeviceName_U@4
+RtlLengthRequiredSid@4
+RtlLengthSid@4
+RtlNewSecurityObject@24
+RtlNtStatusToDosError@4
+RtlOemStringToUnicodeString@12
+RtlQueryInformationAcl@16
+RtlQueryTimeZoneInformation@4
+RtlSetDaclSecurityDescriptor@16
+RtlSetEnvironmentVariable@12
+RtlSetGroupSecurityDescriptor@12
+RtlSetOwnerSecurityDescriptor@12
+RtlSetSaclSecurityDescriptor@16
+RtlSubAuthorityCountSid@4
+RtlSubAuthoritySid@8
+RtlSystemTimeToLocalTime@8
+RtlTimeToSecondsSince1970@8
+RtlTimeToSecondsSince1980@8
+RtlUnicodeStringToOemString@12
+_except_handler2
+_itoa
+_wcsicmp
+_wcsnicmp
+_wcsupr
+fprintf
+iswctype
+memmove
+rand
+sprintf
+srand
+strncmp
+strncpy
+time
+towupper
+vsprintf
+wcscat
+wcschr
+wcscmp
+wcscpy
+wcscspn
+wcslen
+wcsncmp
+wcsncpy
+wcsspn
+wtol@4
+logon_NetrLogonSamLogon@4
+_fgs__STRING@4
+_fgs__UNICODE_STRING@4
+_fgs__NETLOGON_LOGON_IDENTITY_INFO@4
+_fgs__NETLOGON_NETWORK_INFO@4
+_fgu__NETLOGON_LEVEL@8
+_fgu__NETLOGON_VALIDATION@8
+NlpWriteMailslot@12
+NlPrimaryAnnouncement@4
+NlLanmanPrimaryAnnouncement@0
+NlChangePassword@4
+NlScavenger@4
+IsScavengerRunning@0
+NlStartScavengerThread@0
+NlServerSessionScavenger@0
+NlPickTrustedDcForEntireTrustList@0
+GiveInstallHints@4
+NlMoveToNextChangeLogBlock@8
+NlMoveToNextChangeLogEntry@8
+ValidateThisEntry@16
+ValidateList@8
+NlDcDiscoveryMachine@24
+NlWaitForSamService@4
+NlSamOpenNamedUser@12
+NlGetHashVal@8
+NlFindNamedServerSession@4
+NlAnnouncePrimaryStart@0
+NlWriteChangeLogBytes@16
+NlAllocChangeLogBlock@12
+NlWriteChangeLogEntry@20
+I_NetNotifyDelta@40
+I_NetNotifyRole@4
+NlInitChangeLogBuffer@0
+InitChangeLogHeadAndTail@8
+NlInitChangeLog@0
+NlGetGroupEntry@8
+NlAddGroupEntry@8
+NlAddGlobalGroupsToList@8
+NlInitSpecialGroupList@0
+NlIsServersGroupEmpty@4
+NlChangeLogWorker@4
+NlStartChangeLogWorkerThread@0
+IsChangeLogWorkerRunning@0
+logon_NetrServerReqChallenge@4
+logon_NetrServerAuthenticate@4
+NlSetPrimaryName@4
+NlCheckMachineAccount@8
+NlInitDBSerialNumber@20
+NlServerType@0
+NlInitLsaDBInfo@4
+NlInitSamDBInfo@8
+NlSetDomainName@0
+NlDcDiscoveryHandler@16
+NlInitDomainController@0
+NlInit@0
+NlMainLoop@0
+NlNetlogonMain@8
+NlCreateShare@8
+NlAuthenticate@24
+NlUnlockServerSession@4
+NlpAtoX@4
+NlCreateNetlogonObjects@0
+Nlparse@0
+NlInitSSI@0
+NlVerifyWorkstation@4
+NetrServerReqChallenge@16
+NetrServerAuthenticate@24
+NetrServerAuthenticate2@28
+NlInsertServerSession@20
+NlAllocateClientSession@12
+NlInitTrustList@0
+NlUpdateTrustListBySid@8
+NlDiscoverDc@8
+NlCreateChangeLogFile@4
+NlResetChangeLog@8
+NlSendChangeLogNotification@16
+NlCloseChangeLogFile@4
+NlFixChangeLog@20
+I_NetNotifyMachineAccount@20
+NlFindChangeLogEntry@20
+NlFindNextChangeLogEntry@12
+NlGetNextUniqueChangeLogEntry@20
+NlRecoverChangeLog@4
+IsSpecialLocalGroup@4
+NlSimulateUserDelta@4
+NlAddWorkerQueueEntry@8
+NlProcessQueueEntry@4
+NlStopChangeLogWorker@0
+NlCleanup@0
+NlExit@16
+NlControlHandler@4
+RaiseAlert@8
+logon_NetrAccountDeltas@4
+logon_NetrGetAnyDCName@4
+logon_NetrGetDCName@4
+logon_NetrServerPasswordSet@4
+logon_NetrLogonUasLogoff@4
+logon_NetrDatabaseSync@4
+logon_NetrAccountSync@4
+logon_NetrLogonSamLogoff@4
+logon_NetrLogonControl@4
+logon_NetrLogonUasLogon@4
+logon_NetrDatabaseDeltas@4
+_fgs__NETLOGON_INTERACTIVE_INFO@4
+_fgs__NETLOGON_SERVICE_INFO@4
+_fgs__NETLOGON_VALIDATION_SAM_INFO@4
+_fgs__NETLOGON_VALIDATION_UAS_INFO@4
+_fgs__NLPR_SID_INFORMATION@4
+_fgs__NLPR_SID_ARRAY@4
+_fgs__NLPR_CR_CIPHER_VALUE@4
+_fgs__NLPR_LOGON_HOURS@4
+_fgs__NLPR_USER_PRIVATE_INFO@4
+_fgs__NETLOGON_DELTA_USER@4
+_fgs__NETLOGON_DELTA_GROUP@4
+_fgs__NETLOGON_DELTA_GROUP_MEMBER@4
+_fgs__NETLOGON_DELTA_ALIAS@4
+_fgs__NETLOGON_DELTA_ALIAS_MEMBER@4
+_fgs__NETLOGON_DELTA_DOMAIN@4
+_fgs__NETLOGON_DELTA_RENAME@4
+_fgs__NETLOGON_DELTA_POLICY@4
+_fgs__NETLOGON_DELTA_TRUSTED_DOMAINS@4
+_fgs__NETLOGON_DELTA_ACCOUNTS@4
+_fgs__NETLOGON_DELTA_SECRET@4
+_fgu__NETLOGON_DELTA_UNION@8
+_fgu__NETLOGON_DELTA_ID_UNION@8
+_fgs__NETLOGON_DELTA_ENUM@4
+_fgs__NETLOGON_DELTA_ENUM_ARRAY@4
+_fgu__NETLOGON_CONTROL_QUERY_INFORMATION@8
+NetrLogonUasLogon@16
+NetrLogonUasLogoff@16
+NlpEncryptValidationInformation@16
+NlpUserLogoffHigher@12
+NlpUserValidateOnPdc@20
+NetrLogonSamLogoff@24
+NetrGetDCName@12
+RaiseNetlogonAlert@16
+NlNewSessionSetup@4
+NlGetUserPriv@20
+NlInitWorkstation@0
+LogonRequestHandler@24
+PrimaryQueryHandler@12
+NlStopScavenger@0
+NlStringToLpwstr@4
+NlStringToLpstr@4
+NlpWriteMailslotA@12
+NlpWriteEventlog@24
+SpecialGroupOp@8
+NlPackUasHeader@16
+NlPackVarLenField@28
+NlPackUasUser@20
+NlPackUasGroup@16
+NlPackUasBuiltinGroup@12
+NlPackUasGroupMember@12
+NlPackUasUserGroupMember@12
+NlPackUasDomain@8
+NlPackUasDelete@20
+NlCopyUnicodeString@8
+NlCopyData@12
+NlFreeDBDelta@4
+NlFreeDBDeltaArray@8
+NlPackSamUser@20
+NlEncryptSensitiveData@8
+NlPackSamGroup@16
+NlPackSamGroupMember@16
+NlPackSamAlias@16
+NlPackSamAliasMember@16
+NlPackSamDomain@12
+NlUnpackSamUser@16
+NlDecryptSensitiveData@12
+NlDeleteSamUser@8
+NlDeleteSamGroup@8
+NlDeleteSamAlias@8
+NlUnpackSamGroup@12
+NlUnpackSamGroupMember@12
+NlUnpackSamAlias@12
+NlUnpackSamAliasMember@12
+NlUnpackSamDomain@8
+NlUnpackSam@16
+NetrServerPasswordSet@28
+NetrDatabaseDeltas@32
+NlSyncSamDatabase@24
+NlSyncLsaDatabase@20
+NetrDatabaseSync@32
+NetrAccountDeltas@48
+NetrAccountSync@48
+NetrLogonControl@16
+NetrGetAnyDCName@12
+I_NetGetAnyDCName@8
+NlCheckAuthenticator@12
+NlEnsureClientIsNamedUser@4
+NlFreeServerSession@4
+NlFreeNamedServerSession@8
+NlFreeServerSessionForAccount@4
+NlFreeClientSession@4
+NlReadSamLogonResponse@16
+NlDcDiscoveryExpired@4
+NlCaptureServerClientSession@8
+NlPickDomainWithAccount@8
+NlPackLsaPolicy@12
+NlPackLsaTDomain@16
+NlPackLsaAccount@20
+NlPackLsaSecret@20
+NlUnpackLsaPolicy@8
+NlUnpackLsaTDomain@12
+NlUnpackLsaAccount@12
+NlUnpackLsaSecret@16
+NlDeleteLsaTDomain@8
+NlDeleteLsaAccount@8
+NlDeleteLsaSecret@8
+NlForceStartupSync@4
+FreeSamSyncTables@0
+FreeLsaSyncTables@0
+InitSamSyncTables@4
+InitLsaSyncTables@4
+UpdateSamSyncTables@8
+UpdateLsaSyncTables@8
+CleanSamSyncTables@4
+CleanLsaSyncTables@4
+NlRecoverConflictingAccount@24
+NlSynchronize@4
+NlReplicateDeltas@4
+NlReplicator@4
+NlUpdateRequired@4
+NlCheckUpdateNotices@8
+IsReplicatorRunning@0
+NlStartReplicatorThread@4
+NlStopReplicator@0
diff --git a/private/net/svcdlls/logonsrv/server/netlogon.rc b/private/net/svcdlls/logonsrv/server/netlogon.rc
new file mode 100644
index 000000000..b0b79e383
--- /dev/null
+++ b/private/net/svcdlls/logonsrv/server/netlogon.rc
@@ -0,0 +1,12 @@
+#include <windows.h>
+
+#include <ntverp.h>
+
+#define VER_FILETYPE VFT_DLL
+#define VER_FILESUBTYPE VFT2_UNKNOWN
+#define VER_FILEDESCRIPTION_STR "Net Logon Services DLL"
+#define VER_INTERNALNAME_STR "NetLogon.DLL"
+#define VER_ORIGINALFILENAME_STR "NetLogon.DLL"
+
+#include "common.ver"
+
diff --git a/private/net/svcdlls/logonsrv/server/nldebug.h b/private/net/svcdlls/logonsrv/server/nldebug.h
new file mode 100644
index 000000000..95e99bbfd
--- /dev/null
+++ b/private/net/svcdlls/logonsrv/server/nldebug.h
@@ -0,0 +1,180 @@
+/*++
+
+Copyright (c) 1987-1992 Microsoft Corporation
+
+Module Name:
+
+ nldebug.h
+
+Abstract:
+
+ Netlogon service debug support
+
+Author:
+
+ Ported from Lan Man 2.0
+
+Revision History:
+
+ 21-May-1991 (cliffv)
+ Ported to NT. Converted to NT style.
+ 09-Apr-1992 JohnRo
+ Prepare for WCHAR.H (_wcsicmp vs _wcscmpi, etc).
+
+--*/
+
+//
+// changelg.c will #include this file with DEBUG_ALLOCATE defined.
+// That will cause each of these variables to be allocated.
+//
+#ifdef DEBUG_ALLOCATE
+#define EXTERN
+#else
+#define EXTERN extern
+#endif
+
+
+////////////////////////////////////////////////////////////////////////
+//
+// Debug Definititions
+//
+////////////////////////////////////////////////////////////////////////
+
+#define NL_INIT 0x00000001 // Initialization
+#define NL_MISC 0x00000002 // Misc debug
+#define NL_LOGON 0x00000004 // Logon processing
+#define NL_SYNC 0x00000008 // Synchronization and replication
+#define NL_MAILSLOT 0x00000010 // Mailslot messages
+#define NL_PULSE 0x00000020 // Pulse processing
+#define NL_CRITICAL 0x00000100 // Only real important errors
+#define NL_SESSION_SETUP 0x00000200 // Trusted Domain maintenance
+#define NL_PACK 0x00000800 // Pack/Unpack of sync messages
+#define NL_SERVER_SESS 0x00001000 // Server session maintenance
+#define NL_CHANGELOG 0x00002000 // Change Log references
+
+//
+// Very verbose bits
+//
+
+#define NL_PULSE_MORE 0x00040000 // Verbose pulse processing
+#define NL_SESSION_MORE 0x00080000 // Verbose session management
+#define NL_REPL_TIME 0x00100000 // replication timing output
+#define NL_REPL_OBJ_TIME 0x00200000 // replication objects get/set timing output
+#define NL_ENCRYPT 0x00400000 // debug encrypt and decrypt acorss net
+#define NL_SYNC_MORE 0x00800000 // additional replication dbgprint
+#define NL_PACK_VERBOSE 0x01000000 // Verbose Pack/Unpack
+#define NL_MAILSLOT_TEXT 0x02000000 // Verbose Mailslot messages
+#define NL_CHALLENGE_RES 0x04000000 // challenge response debug
+#define NL_NETLIB 0x08000000 // Netlogon portion of Netlib
+
+//
+// Control bits.
+//
+
+#ifdef DONT_REQUIRE_ACCOUNT
+#define NL_DONT_REQUIRE_ACCOUNT 0x00020000 // Don't require account on DC discovery
+#endif DONT_REQUIRE_ACCOUNT
+
+#define NL_INHIBIT_CANCEL 0x10000000 // Don't cancel API calls
+#define NL_TIMESTAMP 0x20000000 // TimeStamp each output line
+#define NL_ONECHANGE_REPL 0x40000000 // Only replicate one change per call
+#define NL_BREAKPOINT 0x80000000 // Enter debugger on startup
+
+
+
+#if DBG || defined(NLTEST_IMAGE)
+
+EXTERN DWORD NlGlobalTrace;
+
+//
+// Debug share path.
+//
+
+EXTERN LPWSTR NlGlobalDebugSharePath;
+
+#define IF_DEBUG(Function) \
+ if (NlGlobalTrace & NL_ ## Function)
+
+#define NlPrint(_x_) NlPrintRoutine _x_
+
+VOID
+NlAssertFailed(
+ IN PVOID FailedAssertion,
+ IN PVOID FileName,
+ IN ULONG LineNumber,
+ IN PCHAR Message OPTIONAL
+ );
+
+#define NlAssert(Predicate) \
+ { \
+ if (!(Predicate)) \
+ NlAssertFailed( #Predicate, __FILE__, __LINE__, NULL ); \
+ }
+
+
+#define DEBUG_DIR L"\\debug"
+#define DEBUG_FILE L"\\netlogon.log"
+#define DEBUG_BAK_FILE L"\\netlogon.bak"
+
+#define DEBUG_SHARE_NAME L"DEBUG"
+
+VOID
+NlPrintRoutine(
+ IN DWORD DebugFlag,
+ IN LPSTR FORMATSTRING, // PRINTF()-STYLE FORMAT STRING.
+ ... // OTHER ARGUMENTS ARE POSSIBLE.
+ );
+
+VOID
+NlpDumpHexData(
+ IN DWORD DebugFlag,
+ IN LPDWORD Buffer,
+ IN DWORD BufferSize
+ );
+
+VOID
+NlpDumpSid(
+ IN DWORD DebugFlag,
+ IN PSID Sid
+ );
+
+VOID
+NlpDumpBuffer(
+ IN DWORD DebugFlag,
+ IN PVOID Buffer,
+ IN DWORD BufferSize
+ );
+
+VOID
+NlOpenDebugFile(
+ IN BOOL ReopenFlag
+ );
+
+//
+// Debug log file
+//
+
+EXTERN HANDLE NlGlobalLogFile;
+#define DEFAULT_MAXIMUM_LOGFILE_SIZE 20000000
+EXTERN DWORD NlGlobalLogFileMaxSize;
+
+//
+// To serialize access to log file.
+//
+
+EXTERN CRITICAL_SECTION NlGlobalLogFileCritSect;
+
+#else
+
+#define IF_DEBUG(Function) if (FALSE)
+
+// Nondebug version.
+#define NlpDumpHexData /* no output; ignore arguments */
+#define NlpDumpBuffer
+#define NlpDumpSid
+#define NlPrint(_x_)
+#define NlAssert(Predicate) /* no output; ignore arguments */
+
+#endif // DBG
+
+#undef EXTERN
diff --git a/private/net/svcdlls/logonsrv/server/nlp.c b/private/net/svcdlls/logonsrv/server/nlp.c
new file mode 100644
index 000000000..59947fb36
--- /dev/null
+++ b/private/net/svcdlls/logonsrv/server/nlp.c
@@ -0,0 +1,1246 @@
+/*++
+
+Copyright (c) 1987-1992 Microsoft Corporation
+
+Module Name:
+
+ nlp.c
+
+Abstract:
+
+ Private Netlogon service utility routines.
+
+Author:
+
+ Cliff Van Dyke (cliffv) 7-Jun-1991
+
+Environment:
+
+ User mode only.
+ Contains NT-specific code.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+ 08-May-1992 JohnRo
+ Use net config helpers for NetLogon.
+ Use <prefix.h> equates.
+--*/
+
+//
+// Common include files.
+//
+
+#include <logonsrv.h> // Include files common to entire service
+
+//
+// Include files specific to this .c file
+//
+
+#include <lmerr.h> // NERR_*
+#include <winerror.h> // NO_ERROR
+#include <prefix.h> // PREFIX_ equates.
+#include <stdlib.h> // C library functions: rand()
+#include <stdarg.h> // va_list, etc.
+#include <stdio.h> // vsprintf().
+#include <tstr.h> // TCHAR_ equates.
+#include <align.h> // alignment macros defined
+
+
+LPWSTR
+NlStringToLpwstr(
+ IN PUNICODE_STRING String
+ )
+
+/*++
+
+Routine Description:
+
+ Convert a Unicode String to an LPWSTR.
+
+Arguments:
+
+ String - Unicode String to copy
+
+Return Value:
+
+ LPWSTR in a NetpMemoryAllocate'd buffer.
+ NULL is returned if there is no memory.
+
+--*/
+
+{
+ LPWSTR Buffer;
+
+ Buffer = NetpMemoryAllocate( String->Length + sizeof(WCHAR) );
+
+ if ( Buffer != NULL ) {
+ RtlCopyMemory( Buffer, String->Buffer, String->Length );
+ Buffer[ String->Length / sizeof(WCHAR) ] = L'\0';
+ }
+
+ return Buffer;
+}
+
+
+LPSTR
+NlStringToLpstr(
+ IN PUNICODE_STRING String
+ )
+
+/*++
+
+Routine Description:
+
+ Convert a Unicode String to an LPSTR.
+
+Arguments:
+
+ String - Unicode String to copy
+
+Return Value:
+
+ LPWSTR in a NetpMemoryAllocate'd buffer.
+ NULL is returned if there is no memory.
+
+--*/
+
+{
+ NTSTATUS Status;
+ STRING AnsiString;
+
+ AnsiString.MaximumLength = (USHORT) RtlUnicodeStringToOemSize( String );
+
+ AnsiString.Buffer = NetpMemoryAllocate( AnsiString.MaximumLength );
+
+ if ( AnsiString.Buffer != NULL ) {
+ Status = RtlUnicodeStringToOemString( &AnsiString,
+ String,
+ (BOOLEAN) FALSE );
+ if ( !NT_SUCCESS( Status ) ) {
+ NetpMemoryFree( AnsiString.Buffer );
+ return NULL;
+ }
+ }
+
+ return AnsiString.Buffer;
+}
+
+
+VOID
+NlpPutString(
+ IN PUNICODE_STRING OutString,
+ IN PUNICODE_STRING InString,
+ IN PUCHAR *Where
+ )
+
+/*++
+
+Routine Description:
+
+ This routine copies the InString string to the memory pointed to by
+ the Where parameter, and fixes the OutString string to point to that
+ new copy.
+
+Parameters:
+
+ OutString - A pointer to a destination NT string
+
+ InString - A pointer to an NT string to be copied
+
+ Where - A pointer to space to put the actual string for the
+ OutString. The pointer is adjusted to point to the first byte
+ following the copied string.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ ASSERT( OutString != NULL );
+ ASSERT( InString != NULL );
+ ASSERT( Where != NULL && *Where != NULL);
+ ASSERT( *Where == ROUND_UP_POINTER( *Where, sizeof(WCHAR) ) );
+#ifdef notdef
+ KdPrint(("NlpPutString: %ld %Z\n", InString->Length, InString ));
+ KdPrint((" InString: %lx %lx OutString: %lx Where: %lx\n", InString,
+ InString->Buffer, OutString, *Where ));
+#endif
+
+ if ( InString->Length > 0 ) {
+
+ OutString->Buffer = (PWCH) *Where;
+ OutString->MaximumLength = (USHORT)(InString->Length + sizeof(WCHAR));
+
+ RtlCopyUnicodeString( OutString, InString );
+
+ *Where += InString->Length;
+// *((WCHAR *)(*Where)) = L'\0';
+ *(*Where) = '\0';
+ *(*Where + 1) = '\0';
+ *Where += 2;
+
+ } else {
+ RtlInitUnicodeString(OutString, NULL);
+ }
+#ifdef notdef
+ KdPrint((" OutString: %ld %lx\n", OutString->Length, OutString->Buffer));
+#endif
+
+ return;
+}
+
+
+VOID
+NlpWriteEventlog (
+ IN DWORD EventID,
+ IN DWORD EventType,
+ IN LPBYTE RawDataBuffer OPTIONAL,
+ IN DWORD RawDataSize,
+ IN LPWSTR *StringArray,
+ IN DWORD StringCount
+ )
+/*++
+
+Routine Description:
+
+ Stub routine for calling Event Log.
+
+Arguments:
+
+ EventID - event log ID.
+
+ EventType - Type of event.
+
+ RawDataBuffer - Data to be logged with the error.
+
+ numbyte - Size in bytes of "RawDataBuffer"
+
+ StringArray - array of null-terminated strings.
+
+ StringCount - number of zero terminated strings in "StringArray". The following
+ flags can be OR'd in to the count:
+
+ LAST_MESSAGE_IS_NTSTATUS 0x80000000
+ LAST_MESSAGE_IS_NETSTATUS 0x40000000
+
+Return Value:
+
+ None.
+
+--*/
+{
+ DWORD ErrorCode;
+ WCHAR ErrorNumberBuffer[25];
+
+ //
+ // If an NT status code was passed in,
+ // convert it to a net status code.
+ //
+
+ if ( StringCount & LAST_MESSAGE_IS_NTSTATUS ) {
+ StringCount &= ~LAST_MESSAGE_IS_NTSTATUS;
+
+ //
+ // Do the "better" error mapping when eventviewer ParameterMessageFile
+ // can be a list of files. Then, add netmsg.dll to the list.
+ //
+ // StringArray[StringCount-1] = (LPWSTR) NetpNtStatusToApiStatus( (NTSTATUS) StringArray[StringCount-1] );
+ if ( (NTSTATUS)StringArray[StringCount-1] == STATUS_SYNCHRONIZATION_REQUIRED ) {
+ StringArray[StringCount-1] = (LPWSTR) NERR_SyncRequired;
+ } else {
+ StringArray[StringCount-1] = (LPWSTR) RtlNtStatusToDosError( (NTSTATUS) StringArray[StringCount-1] );
+ }
+
+ StringCount |= LAST_MESSAGE_IS_NETSTATUS;
+ }
+
+ //
+ // If a net/windows status code was passed in,
+ // convert to the the %%N format the eventviewer knows.
+ //
+
+ if ( StringCount & LAST_MESSAGE_IS_NETSTATUS ) {
+ StringCount &= ~LAST_MESSAGE_IS_NETSTATUS;
+
+ wcscpy( ErrorNumberBuffer, L"%%" );
+ ultow( (ULONG) StringArray[StringCount-1], ErrorNumberBuffer+2, 10 );
+ StringArray[StringCount-1] = ErrorNumberBuffer;
+
+ }
+
+
+ //
+ // Dump the event to the debug file.
+ //
+
+#if DBG
+ IF_DEBUG( MISC ) {
+ DWORD i;
+ NlPrint((NL_MISC, "Eventlog: %ld (%ld) ",
+ EventID,
+ EventType ));
+
+ for (i = 0; i < StringCount; i++ ) {
+ NlPrint((NL_MISC, "\"" FORMAT_LPWSTR "\" ", StringArray[i] ));
+ }
+
+ if( RawDataSize ) {
+ if ( RawDataSize < 16 && COUNT_IS_ALIGNED( RawDataSize, sizeof(DWORD)) ) {
+ NlpDumpHexData( NL_MISC,
+ (LPDWORD) RawDataBuffer,
+ RawDataSize/sizeof(DWORD) );
+ } else {
+ NlPrint((NL_MISC, "\n" ));
+ NlpDumpBuffer( NL_MISC, RawDataBuffer, RawDataSize );
+ }
+ } else {
+ NlPrint((NL_MISC, "\n" ));
+ }
+
+ }
+#endif // DBG
+
+ //
+ // write event
+ //
+
+ ErrorCode = NetpWriteEventlog(
+ SERVICE_NETLOGON,
+ EventID,
+ EventType,
+ StringCount,
+ StringArray,
+ RawDataSize,
+ RawDataBuffer);
+
+
+ IF_DEBUG( MISC ) {
+ if( ErrorCode != NO_ERROR ) {
+ NlPrint((NL_CRITICAL,
+ "Error writing this event in the eventlog, Status = %ld\n",
+ ErrorCode ));
+ }
+ }
+
+ return;
+}
+
+#if DBG
+
+VOID
+NlpDumpBuffer(
+ IN DWORD DebugFlag,
+ PVOID Buffer,
+ DWORD BufferSize
+ )
+/*++
+
+Routine Description:
+
+ Dumps the buffer content on to the debugger output.
+
+Arguments:
+
+ DebugFlag: Debug flag to pass on to NlPrintRoutine
+
+ Buffer: buffer pointer.
+
+ BufferSize: size of the buffer.
+
+Return Value:
+
+ none
+
+--*/
+{
+#define NUM_CHARS 16
+
+ DWORD i, limit;
+ CHAR TextBuffer[NUM_CHARS + 1];
+ LPBYTE BufferPtr = Buffer;
+
+ //
+ // If we aren't debugging this functionality, just return.
+ //
+ if ( (NlGlobalTrace & DebugFlag) == 0 ) {
+ return;
+ }
+
+ NlPrint((0,"------------------------------------\n"));
+
+ //
+ // Hex dump of the bytes
+ //
+ limit = ((BufferSize - 1) / NUM_CHARS + 1) * NUM_CHARS;
+
+ for (i = 0; i < limit; i++) {
+
+ if (i < BufferSize) {
+
+ NlPrint((0,"%02x ", BufferPtr[i]));
+
+ if (BufferPtr[i] < 31 ) {
+ TextBuffer[i % NUM_CHARS] = '.';
+ } else if (BufferPtr[i] == '\0') {
+ TextBuffer[i % NUM_CHARS] = ' ';
+ } else {
+ TextBuffer[i % NUM_CHARS] = (CHAR) BufferPtr[i];
+ }
+
+ } else {
+
+ NlPrint((0," "));
+ TextBuffer[i % NUM_CHARS] = ' ';
+
+ }
+
+ if ((i + 1) % NUM_CHARS == 0) {
+ TextBuffer[NUM_CHARS] = 0;
+ NlPrint((0," %s\n", TextBuffer));
+ }
+
+ }
+
+ NlPrint((0,"------------------------------------\n"));
+}
+
+
+VOID
+NlpDumpHexData(
+ IN DWORD DebugFlag,
+ LPDWORD Buffer,
+ DWORD BufferSize
+ )
+/*++
+
+Routine Description:
+
+ Dumps the hex data on to the debugger output.
+
+Arguments:
+
+ DebugFlag: Debug flag to pass on to NlPrintRoutine
+
+ Buffer: buffer pointer to hex data.
+
+ BufferSize: size of the buffer.
+
+Return Value:
+
+ none
+
+--*/
+{
+ DWORD i;
+
+ //
+ // If we aren't debugging this functionality, just return.
+ //
+ if ( (NlGlobalTrace & DebugFlag) == 0 ) {
+ return;
+ }
+
+#ifndef MIPS
+
+ // data alignment problem on mips ??
+
+ if( !POINTER_IS_ALIGNED( Buffer, ALIGN_DWORD) ) {
+ return;
+ }
+
+#endif //MIPS
+
+ for(i = 0; i < BufferSize; i++) {
+
+ if( i != 0 && i % 4 == 0 ) {
+ NlPrint((0,"\n"));
+ }
+
+ NlPrint((0,"%08lx ", Buffer[i]));
+ }
+
+ NlPrint((0,"\n"));
+
+}
+
+
+VOID
+NlpDumpSid(
+ IN DWORD DebugFlag,
+ IN PSID Sid OPTIONAL
+ )
+/*++
+
+Routine Description:
+
+ Dumps a SID to the debugger output
+
+Arguments:
+
+ DebugFlag - Debug flag to pass on to NlPrintRoutine
+
+ Sid - SID to output
+
+Return Value:
+
+ none
+
+--*/
+{
+
+
+ //
+ // If we aren't debugging this functionality, just return.
+ //
+ if ( (NlGlobalTrace & DebugFlag) == 0 ) {
+ return;
+ }
+
+ //
+ // Output the SID
+ //
+
+ if ( Sid == NULL ) {
+ NlPrint((0, "(null)\n"));
+ } else {
+ UNICODE_STRING SidString;
+ NTSTATUS Status;
+
+ Status = RtlConvertSidToUnicodeString( &SidString, Sid, TRUE );
+
+ if ( !NT_SUCCESS(Status) ) {
+ NlPrint((0, "Invalid 0x%lX\n", Status ));
+ } else {
+ NlPrint((0, "%wZ\n", &SidString ));
+ RtlFreeUnicodeString( &SidString );
+ }
+ }
+
+}
+#endif // DBG
+
+
+DWORD
+NlpAtoX(
+ IN LPWSTR String
+ )
+/*++
+
+Routine Description:
+
+ Converts hexadecimal string to DWORD integer.
+
+ Accepts the following form of hex string
+
+ 0[x-X][0-9, a-f, A-F]*
+
+Arguments:
+
+ String: hexadecimal string.
+
+Return Value:
+
+ Decimal value of the hex string.
+
+--*/
+
+{
+ DWORD Value = 0;
+
+ if( String == NULL )
+ return 0;
+
+ if( *String != TEXT('0') )
+ return 0;
+
+ String++;
+
+ if( *String == TCHAR_EOS )
+ return 0;
+
+ if( ( *String != TEXT('x') ) && ( *String != TEXT('X') ) )
+ return 0;
+
+ String++;
+
+ while(*String != TCHAR_EOS ) {
+
+ if( (*String >= TEXT('0')) && (*String <= TEXT('9')) ) {
+ Value = Value * 16 + ( *String - '0');
+ } else if( (*String >= TEXT('a')) && (*String <= TEXT('f')) ) {
+ Value = Value * 16 + ( 10 + *String - TEXT('a'));
+ } else if( (*String >= TEXT('A')) && (*String <= TEXT('F')) ) {
+ Value = Value * 16 + ( 10 + *String - TEXT('A'));
+ } else {
+ break;
+ }
+ String++;
+ }
+
+ return Value;
+}
+
+
+VOID
+NlWaitForSingleObject(
+ IN LPSTR WaitReason,
+ IN HANDLE WaitHandle
+ )
+/*++
+
+Routine Description:
+
+ Waits an infinite amount of time for the specified handle.
+
+Arguments:
+
+ WaitReason - Text describing what we're waiting on
+
+ WaitHandle - Handle to wait on
+
+Return Value:
+
+ None
+
+--*/
+
+{
+ DWORD WaitStatus;
+
+ //
+ // Loop waiting.
+ //
+
+ for (;;) {
+ WaitStatus = WaitForSingleObject( WaitHandle,
+ 2*60*1000 ); // Two minutes
+
+ if ( WaitStatus == WAIT_TIMEOUT ) {
+ NlPrint((NL_CRITICAL,
+ "WaitForSingleObject 2-minute timeout (Rewaiting): %s\n",
+ WaitReason ));
+ continue;
+
+ } else if ( WaitStatus == 0 ) {
+ break;
+
+ } else {
+ NlPrint((NL_CRITICAL,
+ "WaitForSingleObject error: %ld %s\n",
+ WaitStatus,
+ WaitReason ));
+ UNREFERENCED_PARAMETER(WaitReason);
+ break;
+ }
+ }
+
+}
+
+
+BOOLEAN
+NlWaitForSamService(
+ BOOLEAN NetlogonServiceCalling
+ )
+/*++
+
+Routine Description:
+
+ This procedure waits for the SAM service to start and to complete
+ all its initialization.
+
+Arguments:
+
+ NetlogonServiceCalling:
+ TRUE if this is the netlogon service proper calling
+ FALSE if this is the changelog worker thread calling
+
+Return Value:
+
+ TRUE : if the SAM service is successfully starts.
+
+ FALSE : if the SAM service can't start.
+
+--*/
+{
+ NTSTATUS Status;
+ DWORD WaitStatus;
+ UNICODE_STRING EventName;
+ HANDLE EventHandle;
+ OBJECT_ATTRIBUTES EventAttributes;
+
+ //
+ // open SAM event
+ //
+
+ RtlInitUnicodeString( &EventName, L"\\SAM_SERVICE_STARTED");
+ InitializeObjectAttributes( &EventAttributes, &EventName, 0, 0, NULL );
+
+ Status = NtOpenEvent( &EventHandle,
+ SYNCHRONIZE|EVENT_MODIFY_STATE,
+ &EventAttributes );
+
+ if ( !NT_SUCCESS(Status)) {
+
+ if( Status == STATUS_OBJECT_NAME_NOT_FOUND ) {
+
+ //
+ // SAM hasn't created this event yet, let us create it now.
+ // SAM opens this event to set it.
+ //
+
+ Status = NtCreateEvent(
+ &EventHandle,
+ SYNCHRONIZE|EVENT_MODIFY_STATE,
+ &EventAttributes,
+ NotificationEvent,
+ FALSE // The event is initially not signaled
+ );
+
+ if( Status == STATUS_OBJECT_NAME_EXISTS ||
+ Status == STATUS_OBJECT_NAME_COLLISION ) {
+
+ //
+ // second change, if the SAM created the event before we
+ // do.
+ //
+
+ Status = NtOpenEvent( &EventHandle,
+ SYNCHRONIZE|EVENT_MODIFY_STATE,
+ &EventAttributes );
+
+ }
+ }
+
+ if ( !NT_SUCCESS(Status)) {
+
+ //
+ // could not make the event handle
+ //
+
+ NlPrint((NL_CRITICAL,
+ "NlWaitForSamService couldn't make the event handle : "
+ "%lx\n", Status));
+
+ return( FALSE );
+ }
+ }
+
+ //
+ // Loop waiting.
+ //
+
+ for (;;) {
+ WaitStatus = WaitForSingleObject( EventHandle,
+ 5*1000 ); // 5 Seconds
+
+ if ( WaitStatus == WAIT_TIMEOUT ) {
+ if ( NetlogonServiceCalling ) {
+ NlPrint((NL_CRITICAL,
+ "NlWaitForSamService 5-second timeout (Rewaiting)\n" ));
+ if (!GiveInstallHints( FALSE )) {
+ (VOID) NtClose( EventHandle );
+ return FALSE;
+ }
+ }
+ continue;
+
+ } else if ( WaitStatus == 0 ) {
+ break;
+
+ } else {
+ NlPrint((NL_CRITICAL,
+ "NlWaitForSamService: error %ld\n",
+ WaitStatus ));
+ (VOID) NtClose( EventHandle );
+ return FALSE;
+ }
+ }
+
+ (VOID) NtClose( EventHandle );
+ return TRUE;
+
+}
+
+
+
+
+#if DBG
+
+
+VOID
+NlOpenDebugFile(
+ IN BOOL ReopenFlag
+ )
+/*++
+
+Routine Description:
+
+ Opens or re-opens the debug file
+
+Arguments:
+
+ ReopenFlag - TRUE to indicate the debug file is to be closed, renamed,
+ and recreated.
+
+Return Value:
+
+ None
+
+--*/
+
+{
+ WCHAR LogFileName[500];
+ WCHAR BakFileName[500];
+ DWORD FileAttributes;
+ DWORD PathLength;
+ DWORD WinError;
+
+ //
+ // Close the handle to the debug file, if it is currently open
+ //
+
+ EnterCriticalSection( &NlGlobalLogFileCritSect );
+ if ( NlGlobalLogFile != INVALID_HANDLE_VALUE ) {
+ CloseHandle( NlGlobalLogFile );
+ NlGlobalLogFile = INVALID_HANDLE_VALUE;
+ }
+ LeaveCriticalSection( &NlGlobalLogFileCritSect );
+
+ //
+ // make debug directory path first, if it is not made before.
+ //
+ if( NlGlobalDebugSharePath == NULL ) {
+
+ if ( !GetWindowsDirectoryW(
+ LogFileName,
+ sizeof(LogFileName)/sizeof(WCHAR) ) ) {
+ NlPrint((NL_CRITICAL, "Window Directory Path can't be "
+ "retrieved, %lu.\n", GetLastError() ));
+ return;
+ }
+
+ //
+ // check debug path length.
+ //
+
+ PathLength = wcslen(LogFileName) * sizeof(WCHAR) +
+ sizeof(DEBUG_DIR) + sizeof(WCHAR);
+
+ if( (PathLength + sizeof(DEBUG_FILE) > sizeof(LogFileName) ) ||
+ (PathLength + sizeof(DEBUG_BAK_FILE) > sizeof(BakFileName) ) ) {
+
+ NlPrint((NL_CRITICAL, "Debug directory path (%ws) length is too long.\n",
+ LogFileName));
+ goto ErrorReturn;
+ }
+
+ wcscat(LogFileName, DEBUG_DIR);
+
+ //
+ // copy debug directory name to global var.
+ //
+
+ NlGlobalDebugSharePath =
+ NetpMemoryAllocate( (wcslen(LogFileName) + 1) * sizeof(WCHAR) );
+
+ if( NlGlobalDebugSharePath == NULL ) {
+ NlPrint((NL_CRITICAL, "Can't allocated memory for debug share "
+ "(%ws).\n", LogFileName));
+ goto ErrorReturn;
+ }
+
+ wcscpy(NlGlobalDebugSharePath, LogFileName);
+ }
+ else {
+ wcscpy(LogFileName, NlGlobalDebugSharePath);
+ }
+
+ //
+ // Check this path exists.
+ //
+
+ FileAttributes = GetFileAttributesW( LogFileName );
+
+ if( FileAttributes == 0xFFFFFFFF ) {
+
+ WinError = GetLastError();
+ if( WinError == ERROR_FILE_NOT_FOUND ) {
+
+ //
+ // Create debug directory.
+ //
+
+ if( !CreateDirectoryW( LogFileName, NULL) ) {
+ NlPrint((NL_CRITICAL, "Can't create Debug directory (%ws), "
+ "%lu.\n", LogFileName, GetLastError() ));
+ goto ErrorReturn;
+ }
+
+ }
+ else {
+ NlPrint((NL_CRITICAL, "Can't Get File attributes(%ws), "
+ "%lu.\n", LogFileName, WinError ));
+ goto ErrorReturn;
+ }
+ }
+ else {
+
+ //
+ // if this is not a directory.
+ //
+
+ if(!(FileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
+
+ NlPrint((NL_CRITICAL, "Debug directory path (%ws) exists "
+ "as file.\n", LogFileName));
+ goto ErrorReturn;
+ }
+ }
+
+ //
+ // Create the name of the old and new log file names
+ //
+
+ (VOID) wcscpy( BakFileName, LogFileName );
+ (VOID) wcscat( LogFileName, L"\\Netlogon.log" );
+ (VOID) wcscat( BakFileName, L"\\Netlogon.bak" );
+
+
+ //
+ // If this is a re-open,
+ // delete the backup file,
+ // rename the current file to the backup file.
+ //
+
+ if ( ReopenFlag ) {
+
+ if ( !DeleteFile( BakFileName ) ) {
+ WinError = GetLastError();
+ if ( WinError != ERROR_FILE_NOT_FOUND ) {
+ NlPrint((NL_CRITICAL,
+ "Cannot delete " FORMAT_LPWSTR "(%ld)\n",
+ BakFileName,
+ WinError ));
+ NlPrint((NL_CRITICAL, " Try to re-open the file.\n"));
+ ReopenFlag = FALSE; // Don't truncate the file
+ }
+ }
+ }
+
+ if ( ReopenFlag ) {
+ if ( !MoveFile( LogFileName, BakFileName ) ) {
+ NlPrint((NL_CRITICAL,
+ "Cannot rename " FORMAT_LPWSTR " to " FORMAT_LPWSTR
+ " (%ld)\n",
+ LogFileName,
+ BakFileName,
+ GetLastError() ));
+ NlPrint((NL_CRITICAL,
+ " Try to re-open the file.\n"));
+ ReopenFlag = FALSE; // Don't truncate the file
+ }
+ }
+
+ //
+ // Open the file.
+ //
+
+ EnterCriticalSection( &NlGlobalLogFileCritSect );
+ NlGlobalLogFile = CreateFileW( LogFileName,
+ GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL,
+ ReopenFlag ? CREATE_ALWAYS : OPEN_ALWAYS,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL );
+
+
+ if ( NlGlobalLogFile == INVALID_HANDLE_VALUE ) {
+ NlPrint((NL_CRITICAL, "cannot open " FORMAT_LPWSTR ",\n",
+ LogFileName ));
+ LeaveCriticalSection( &NlGlobalLogFileCritSect );
+ goto ErrorReturn;
+ } else {
+ // Position the log file at the end
+ (VOID) SetFilePointer( NlGlobalLogFile,
+ 0,
+ NULL,
+ FILE_END );
+ }
+
+ LeaveCriticalSection( &NlGlobalLogFileCritSect );
+ return;
+
+ErrorReturn:
+ NlPrint((NL_CRITICAL,
+ " Debug output will be written to debug terminal.\n"));
+ return;
+}
+
+#define MAX_PRINTF_LEN 1024 // Arbitrary.
+
+VOID
+NlPrintRoutine(
+ IN DWORD DebugFlag,
+ IN LPSTR Format,
+ ...
+ )
+
+{
+ va_list arglist;
+ char OutputBuffer[MAX_PRINTF_LEN];
+ ULONG length;
+ DWORD BytesWritten;
+ static BeginningOfLine = TRUE;
+ static LineCount = 0;
+ static TruncateLogFileInProgress = FALSE;
+
+ //
+ // If we aren't debugging this functionality, just return.
+ //
+ if ( DebugFlag != 0 && (NlGlobalTrace & DebugFlag) == 0 ) {
+ return;
+ }
+
+ //
+ // vsprintf isn't multithreaded + we don't want to intermingle output
+ // from different threads.
+ //
+
+ EnterCriticalSection( &NlGlobalLogFileCritSect );
+ length = 0;
+
+ //
+ // Handle the beginning of a new line.
+ //
+ //
+
+ if ( BeginningOfLine ) {
+
+ //
+ // If the log file is getting huge,
+ // truncate it.
+ //
+
+ if ( NlGlobalLogFile != INVALID_HANDLE_VALUE &&
+ !TruncateLogFileInProgress ) {
+
+ //
+ // Only check every 50 lines,
+ //
+
+ LineCount++;
+ if ( LineCount >= 50 ) {
+ DWORD FileSize;
+ LineCount = 0;
+
+ //
+ // Is the log file too big?
+ //
+
+ FileSize = GetFileSize( NlGlobalLogFile, NULL );
+ if ( FileSize == 0xFFFFFFFF ) {
+ (void) DbgPrint( "[NETLOGON] Cannot GetFileSize %ld\n",
+ GetLastError );
+ } else if ( FileSize > NlGlobalLogFileMaxSize ) {
+ TruncateLogFileInProgress = TRUE;
+ LeaveCriticalSection( &NlGlobalLogFileCritSect );
+ NlOpenDebugFile( TRUE );
+ NlPrint(( NL_MISC,
+ "Logfile truncated because it was larger than %ld bytes\n",
+ NlGlobalLogFileMaxSize ));
+ EnterCriticalSection( &NlGlobalLogFileCritSect );
+ TruncateLogFileInProgress = FALSE;
+ }
+
+ }
+ }
+
+ //
+ // If we're writing to the debug terminal,
+ // indicate this is a Netlogon message.
+ //
+
+ if ( NlGlobalLogFile == INVALID_HANDLE_VALUE ) {
+ length += (ULONG) sprintf( &OutputBuffer[length], "[NETLOGON] " );
+ }
+
+ //
+ // Put the timestamp at the begining of the line.
+ //
+ IF_DEBUG( TIMESTAMP ) {
+ SYSTEMTIME SystemTime;
+ GetLocalTime( &SystemTime );
+ length += (ULONG) sprintf( &OutputBuffer[length],
+ "%02u/%02u %02u:%02u:%02u ",
+ SystemTime.wMonth,
+ SystemTime.wDay,
+ SystemTime.wHour,
+ SystemTime.wMinute,
+ SystemTime.wSecond );
+ }
+
+ //
+ // Indicate the type of message on the line
+ //
+ {
+ char *Text;
+
+ switch (DebugFlag) {
+ case NL_INIT:
+ Text = "INIT"; break;
+ case NL_MISC:
+ Text = "MISC"; break;
+ case NL_LOGON:
+ Text = "LOGON"; break;
+ case NL_SYNC:
+ case NL_PACK:
+ case NL_PACK_VERBOSE:
+ case NL_REPL_TIME:
+ case NL_REPL_OBJ_TIME:
+ case NL_SYNC_MORE:
+ Text = "SYNC"; break;
+ case NL_ENCRYPT:
+ Text = "ENCRYPT"; break;
+ case NL_MAILSLOT:
+ case NL_MAILSLOT_TEXT:
+ case NL_NETLIB:
+ Text = "MAILSLOT"; break;
+ case NL_CRITICAL:
+ Text = "CRITICAL"; break;
+ case NL_SESSION_SETUP:
+ case NL_SESSION_MORE:
+ case NL_CHALLENGE_RES:
+ case NL_INHIBIT_CANCEL:
+ case NL_SERVER_SESS:
+ Text = "SESSION"; break;
+ case NL_CHANGELOG:
+ Text = "CHANGELOG"; break;
+ case NL_PULSE:
+ case NL_PULSE_MORE:
+ Text = "PULSE"; break;
+
+ case NL_TIMESTAMP:
+ case NL_BREAKPOINT:
+ default:
+ Text = "UNKNOWN"; break;
+
+ case 0:
+ Text = NULL;
+ }
+ if ( Text != NULL ) {
+ length += (ULONG) sprintf( &OutputBuffer[length], "[%s] ", Text );
+ }
+ }
+ }
+
+ //
+ // Put a the information requested by the caller onto the line
+ //
+
+ va_start(arglist, Format);
+
+ length += (ULONG) vsprintf(&OutputBuffer[length], Format, arglist);
+ BeginningOfLine = (length > 0 && OutputBuffer[length-1] == '\n' );
+ if ( BeginningOfLine ) {
+ OutputBuffer[length-1] = '\r';
+ OutputBuffer[length] = '\n';
+ OutputBuffer[length+1] = '\0';
+ length++;
+ }
+
+ va_end(arglist);
+
+ NlAssert(length <= MAX_PRINTF_LEN);
+
+
+ //
+ // If the log file isn't open,
+ // just output to the debug terminal
+ //
+
+ if ( NlGlobalLogFile == INVALID_HANDLE_VALUE ) {
+ (void) DbgPrint( (PCH) OutputBuffer);
+
+ //
+ // Write the debug info to the log file.
+ //
+
+ } else {
+ if ( !WriteFile( NlGlobalLogFile,
+ OutputBuffer,
+ length,
+ &BytesWritten,
+ NULL ) ) {
+ (void) DbgPrint( (PCH) OutputBuffer);
+ }
+
+ }
+
+ LeaveCriticalSection( &NlGlobalLogFileCritSect );
+
+} // NlPrint
+
+//
+// Have my own version of RtlAssert so debug versions of netlogon really assert on
+// free builds.
+//
+VOID
+NlAssertFailed(
+ IN PVOID FailedAssertion,
+ IN PVOID FileName,
+ IN ULONG LineNumber,
+ IN PCHAR Message OPTIONAL
+ )
+{
+ char Response[ 2 ];
+
+ while (TRUE) {
+ NlPrint(( NL_CRITICAL, "Assertion failed: %s%s (Source File: %s, line %ld)\n",
+ Message ? Message : "",
+ FailedAssertion,
+ FileName,
+ LineNumber
+ ));
+
+ DbgPrint( "\n*** Assertion failed: %s%s\n*** Source File: %s, line %ld\n\n",
+ Message ? Message : "",
+ FailedAssertion,
+ FileName,
+ LineNumber
+ );
+
+ DbgPrompt( "Break, Ignore, Terminate Process or Terminate Thread (bipt)? ",
+ Response,
+ sizeof( Response )
+ );
+ switch (Response[0]) {
+ case 'B':
+ case 'b':
+ DbgBreakPoint();
+ break;
+
+ case 'I':
+ case 'i':
+ return;
+
+ case 'P':
+ case 'p':
+ NtTerminateProcess( NtCurrentProcess(), STATUS_UNSUCCESSFUL );
+ break;
+
+ case 'T':
+ case 't':
+ NtTerminateThread( NtCurrentThread(), STATUS_UNSUCCESSFUL );
+ break;
+ }
+ }
+
+ DbgBreakPoint();
+ NtTerminateProcess( NtCurrentProcess(), STATUS_UNSUCCESSFUL );
+}
+
+#endif // DBG
diff --git a/private/net/svcdlls/logonsrv/server/nlp.h b/private/net/svcdlls/logonsrv/server/nlp.h
new file mode 100644
index 000000000..24fedd23a
--- /dev/null
+++ b/private/net/svcdlls/logonsrv/server/nlp.h
@@ -0,0 +1,95 @@
+/*++
+
+Copyright (c) 1987-1992 Microsoft Corporation
+
+Module Name:
+
+ nlp.h
+
+Abstract:
+
+ Private Netlogon service utility routines.
+
+Author:
+
+ Cliff Van Dyke (cliffv) 7-Jun-1991
+
+Environment:
+
+ User mode only.
+ Contains NT-specific code.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+--*/
+
+//
+// Special flags to NlpWriteEventlog
+//
+
+#define LAST_MESSAGE_IS_NTSTATUS 0x80000000
+#define LAST_MESSAGE_IS_NETSTATUS 0x40000000
+
+//
+// Procedure forwards from nlp.c
+//
+
+NTSTATUS
+NlpWriteMailslot(
+ IN LPWSTR MailslotName,
+ IN LPVOID Buffer,
+ IN DWORD BufferSize
+ );
+
+NTSTATUS
+NlpWriteMailslotA(
+ IN LPSTR MailslotName,
+ IN LPVOID Buffer,
+ IN DWORD BufferSize
+ );
+
+LPWSTR
+NlStringToLpwstr(
+ IN PUNICODE_STRING String
+ );
+
+LPSTR
+NlStringToLpstr(
+ IN PUNICODE_STRING String
+ );
+
+VOID
+NlpWriteEventlog (
+ IN DWORD EventID,
+ IN DWORD EventType,
+ IN LPBYTE buffer OPTIONAL,
+ IN DWORD numbytes,
+ IN LPWSTR *msgbuf,
+ IN DWORD strcount
+ );
+
+
+DWORD
+NlpAtoX(
+ IN LPWSTR String
+ );
+
+VOID
+NlWaitForSingleObject(
+ IN LPSTR WaitReason,
+ IN HANDLE WaitHandle
+ );
+
+BOOLEAN
+NlWaitForSamService(
+ BOOLEAN NetlogonServiceCalling
+ );
+
+VOID
+NlpPutString(
+ IN PUNICODE_STRING OutString,
+ IN PUNICODE_STRING InString,
+ IN PUCHAR *Where
+ );
+
diff --git a/private/net/svcdlls/logonsrv/server/nlsecure.c b/private/net/svcdlls/logonsrv/server/nlsecure.c
new file mode 100644
index 000000000..271a3082b
--- /dev/null
+++ b/private/net/svcdlls/logonsrv/server/nlsecure.c
@@ -0,0 +1,104 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ nlsecure.c
+
+Abstract:
+
+ This module contains the Netlogon service support routines
+ which create security objects and enforce security _access checking.
+
+Author:
+
+ Cliff Van Dyke (CliffV) 22-Aug-1991
+
+Revision History:
+
+--*/
+
+#include <nt.h>
+
+#include <windef.h>
+
+#include <lmcons.h>
+#include <secobj.h>
+
+#include <logonsrv.h>
+
+#define NLSECURE_ALLOCATE // Force globals to be allocated
+#include "nlsecure.h"
+
+
+NTSTATUS
+NlCreateNetlogonObjects(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This function creates the workstation user-mode objects which are
+ represented by security descriptors.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NT status code
+
+--*/
+{
+ NTSTATUS Status;
+
+ //
+ // Order matters! These ACEs are inserted into the DACL in the
+ // following order. Security access is granted or denied based on
+ // the order of the ACEs in the DACL.
+ //
+ //
+ // Members of Group SECURITY_LOCAL aren't allowed to do a UAS logon
+ // to force it to be done remotely.
+ //
+
+ ACE_DATA AceData[] = {
+
+ {ACCESS_DENIED_ACE_TYPE, 0, 0,
+ NETLOGON_UAS_LOGON_ACCESS |
+ NETLOGON_UAS_LOGOFF_ACCESS,
+ &LocalSid},
+
+ {ACCESS_ALLOWED_ACE_TYPE, 0, 0,
+ GENERIC_ALL, &AliasAdminsSid},
+
+ {ACCESS_ALLOWED_ACE_TYPE, 0, 0,
+ NETLOGON_CONTROL_ACCESS, &AliasAccountOpsSid},
+
+ {ACCESS_ALLOWED_ACE_TYPE, 0, 0,
+ NETLOGON_CONTROL_ACCESS, &AliasSystemOpsSid},
+
+ {ACCESS_ALLOWED_ACE_TYPE, 0, 0,
+ NETLOGON_UAS_LOGON_ACCESS |
+ NETLOGON_UAS_LOGOFF_ACCESS |
+ NETLOGON_QUERY_ACCESS, &WorldSid}
+ };
+
+ //
+ // Actually create the security descriptor.
+ //
+
+ Status = NetpCreateSecurityObject(
+ AceData,
+ sizeof(AceData)/sizeof(AceData[0]),
+ LocalSystemSid,
+ LocalSystemSid,
+ &NlGlobalNetlogonInfoMapping,
+ &NlGlobalNetlogonSecurityDescriptor );
+
+ return Status;
+
+}
diff --git a/private/net/svcdlls/logonsrv/server/nlsecure.h b/private/net/svcdlls/logonsrv/server/nlsecure.h
new file mode 100644
index 000000000..e9a880006
--- /dev/null
+++ b/private/net/svcdlls/logonsrv/server/nlsecure.h
@@ -0,0 +1,88 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ nlsecure.h
+
+Abstract:
+
+ Private header file to be included by Netlogon service modules that
+ need to enforce security.
+
+Author:
+
+ Cliff Van Dyke (CliffV) 22-Aug-1991
+
+Revision History:
+
+--*/
+
+#ifndef _NLSECURE_INCLUDED_
+#define _NLSECURE_INCLUDED_
+
+//
+// nlsecure.c will #include this file with NLSECURE_ALLOCATE defined.
+// That will cause each of these variables to be allocated.
+//
+#ifdef NLSECURE_ALLOCATE
+#define EXTERN
+#else
+#define EXTERN extern
+#endif
+
+//-------------------------------------------------------------------//
+// //
+// Object specific access masks //
+// //
+//-------------------------------------------------------------------//
+
+//
+// ConfigurationInfo specific access masks
+//
+#define NETLOGON_UAS_LOGON_ACCESS 0x0001
+#define NETLOGON_UAS_LOGOFF_ACCESS 0x0002
+#define NETLOGON_CONTROL_ACCESS 0x0004
+#define NETLOGON_QUERY_ACCESS 0x0008
+
+#define NETLOGON_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | \
+ NETLOGON_UAS_LOGON_ACCESS | \
+ NETLOGON_UAS_LOGOFF_ACCESS | \
+ NETLOGON_CONTROL_ACCESS | \
+ NETLOGON_QUERY_ACCESS )
+
+
+//
+// Object type names for audit alarm tracking
+//
+#define NETLOGON_SERVICE_OBJECT TEXT("NetlogonService")
+
+//
+// Security descriptors of Netlogon Service objects to control user accesses.
+//
+
+EXTERN PSECURITY_DESCRIPTOR NlGlobalNetlogonSecurityDescriptor;
+
+//
+// Generic mapping for each Netlogon Service object object
+//
+
+EXTERN GENERIC_MAPPING NlGlobalNetlogonInfoMapping
+#ifdef NLSECURE_ALLOCATE
+ = {
+ STANDARD_RIGHTS_READ, // Generic read
+ STANDARD_RIGHTS_WRITE, // Generic write
+ STANDARD_RIGHTS_EXECUTE, // Generic execute
+ NETLOGON_ALL_ACCESS // Generic all
+ }
+#endif // NLSECURE_ALLOCATE
+ ;
+
+
+NTSTATUS
+NlCreateNetlogonObjects(
+ VOID
+ );
+
+#endif // ifndef _NLSECURE_INCLUDED_
diff --git a/private/net/svcdlls/logonsrv/server/nltest.c b/private/net/svcdlls/logonsrv/server/nltest.c
new file mode 100644
index 000000000..12d26b528
--- /dev/null
+++ b/private/net/svcdlls/logonsrv/server/nltest.c
@@ -0,0 +1,3664 @@
+/*--
+
+Copyright (c) 1987-1993 Microsoft Corporation
+
+Module Name:
+
+ nltest.c
+
+Abstract:
+
+ Test program for the Netlogon service.
+
+Author:
+
+ 13-Apr-1992 (cliffv)
+
+Environment:
+
+ User mode only.
+ Contains NT-specific code.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+ Madana - added various options.
+
+--*/
+
+
+//
+// Common include files.
+//
+
+#define NLTEST_IMAGE
+#include <logonsrv.h> // Include files common to entire service
+
+//
+// Include files specific to this .c file
+//
+#include <align.h>
+#include <stdio.h>
+#include <string.h>
+#include <strarray.h>
+#include <tstring.h>
+#include <lmerr.h>
+#include <lmapibuf.h>
+#include <ssiapi.h>
+#include <winreg.h>
+#include "..\..\..\..\newsam\server\samsrvp.h"
+#include <wtypes.h>
+#include <ntstatus.dbg>
+#include <winerror.dbg>
+
+
+VOID
+ListDeltas(
+ LPWSTR DeltaFileName,
+ BOOLEAN ListRedoFile
+ );
+
+DWORD NlGlobalTrace =0xFFFFFFFF;
+
+typedef struct _MAILSLOT_INFO {
+ CHAR Name[DNLEN+NETLOGON_NT_MAILSLOT_LEN+3];
+ HANDLE ResponseHandle;
+ BOOL State;
+ NETLOGON_SAM_LOGON_RESPONSE SamLogonResponse;
+ OVERLAPPED OverLapped;
+ BOOL ReadPending;
+} MAIL_INFO, PMAIL_INFO;
+
+MAIL_INFO GlobalMailInfo[64];
+DWORD GlobalIterationCount = 0;
+LPWSTR GlobalAccountName;
+HANDLE GlobalPostEvent;
+CRITICAL_SECTION GlobalPrintCritSect;
+
+
+VOID
+DumpBuffer(
+ PVOID Buffer,
+ DWORD BufferSize
+ )
+/*++
+Routine Description:
+
+ Dumps the buffer content on to the debugger output.
+
+Arguments:
+
+ Buffer: buffer pointer.
+
+ BufferSize: size of the buffer.
+
+Return Value:
+
+ none
+
+--*/
+{
+ DWORD j;
+ PULONG LongBuffer;
+ ULONG LongLength;
+
+ LongBuffer = Buffer;
+ LongLength = min( BufferSize, 24 )/4;
+
+ for(j = 0; j < LongLength; j++) {
+ printf("%08lx ", LongBuffer[j]);
+ }
+
+ if ( BufferSize != LongLength*4 ) {
+ printf( "..." );
+ }
+
+ printf("\n");
+
+}
+
+
+VOID
+NlpDumpSid(
+ IN DWORD DebugFlag,
+ IN PSID Sid OPTIONAL
+ )
+/*++
+
+Routine Description:
+
+ Dumps a SID to the debugger output
+
+Arguments:
+
+ DebugFlag - Debug flag to pass on to NlPrintRoutine
+
+ Sid - SID to output
+
+Return Value:
+
+ none
+
+--*/
+{
+
+ //
+ // Output the SID
+ //
+
+ if ( Sid == NULL ) {
+ NlPrint((0, "(null)\n"));
+ } else {
+ UNICODE_STRING SidString;
+ NTSTATUS Status;
+
+ Status = RtlConvertSidToUnicodeString( &SidString, Sid, TRUE );
+
+ if ( !NT_SUCCESS(Status) ) {
+ NlPrint((0, "Invalid 0x%lX\n", Status ));
+ } else {
+ NlPrint((0, "%wZ\n", &SidString ));
+ RtlFreeUnicodeString( &SidString );
+ }
+ }
+
+ UNREFERENCED_PARAMETER( DebugFlag );
+}
+
+VOID
+NlpDumpHexData(
+ IN DWORD DebugFlag,
+ LPDWORD Buffer,
+ DWORD BufferSize
+ )
+/*++
+Routine Description:
+
+ Dumps the buffer content on to the debugger output.
+
+Arguments:
+
+ Buffer: buffer pointer.
+
+ BufferSize: size of the buffer.
+
+Return Value:
+
+ none
+
+--*/
+{
+ DumpBuffer( Buffer, BufferSize );
+ UNREFERENCED_PARAMETER( DebugFlag );
+}
+
+
+VOID
+PrintTime(
+ LPSTR Comment,
+ LARGE_INTEGER ConvertTime
+ )
+/*++
+
+Routine Description:
+
+ Print the specified time
+
+Arguments:
+
+ Comment - Comment to print in front of the time
+
+ Time - GMT time to print (Nothing is printed if this is zero)
+
+Return Value:
+
+ None
+
+--*/
+{
+ //
+ // If we've been asked to convert an NT GMT time to ascii,
+ // Do so
+ //
+
+ if ( ConvertTime.QuadPart != 0 ) {
+ LARGE_INTEGER LocalTime;
+ TIME_FIELDS TimeFields;
+ NTSTATUS Status;
+
+ printf( "%s", Comment );
+
+ Status = RtlSystemTimeToLocalTime( &ConvertTime, &LocalTime );
+ if ( !NT_SUCCESS( Status )) {
+ printf( "Can't convert time from GMT to Local time\n" );
+ LocalTime = ConvertTime;
+ }
+
+ RtlTimeToTimeFields( &LocalTime, &TimeFields );
+
+ printf( "%8.8lx %8.8lx = %ld/%ld/%ld %ld:%2.2ld:%2.2ld\n",
+ ConvertTime.LowPart,
+ ConvertTime.HighPart,
+ TimeFields.Month,
+ TimeFields.Day,
+ TimeFields.Year,
+ TimeFields.Hour,
+ TimeFields.Minute,
+ TimeFields.Second );
+ }
+}
+
+LPSTR
+FindSymbolicNameForStatus(
+ DWORD Id
+ )
+{
+ ULONG i;
+
+ i = 0;
+ if (Id == 0) {
+ return "STATUS_SUCCESS";
+ }
+
+ if (Id & 0xC0000000) {
+ while (ntstatusSymbolicNames[ i ].SymbolicName) {
+ if (ntstatusSymbolicNames[ i ].MessageId == (NTSTATUS)Id) {
+ return ntstatusSymbolicNames[ i ].SymbolicName;
+ } else {
+ i += 1;
+ }
+ }
+ }
+
+ while (winerrorSymbolicNames[ i ].SymbolicName) {
+ if (winerrorSymbolicNames[ i ].MessageId == Id) {
+ return winerrorSymbolicNames[ i ].SymbolicName;
+ } else {
+ i += 1;
+ }
+ }
+
+#ifdef notdef
+ while (neteventSymbolicNames[ i ].SymbolicName) {
+ if (neteventSymbolicNames[ i ].MessageId == Id) {
+ return neteventSymbolicNames[ i ].SymbolicName
+ } else {
+ i += 1;
+ }
+ }
+#endif // notdef
+
+ return NULL;
+}
+
+
+VOID
+PrintStatus(
+ NET_API_STATUS NetStatus
+ )
+/*++
+
+Routine Description:
+
+ Print a net status code.
+
+Arguments:
+
+ NetStatus - The net status code to print.
+
+Return Value:
+
+ None
+
+--*/
+{
+ printf( "Status = %lu 0x%lx", NetStatus, NetStatus );
+
+ switch (NetStatus) {
+ case NERR_Success:
+ printf( " NERR_Success" );
+ break;
+
+ case NERR_DCNotFound:
+ printf( " NERR_DCNotFound" );
+ break;
+
+ case NERR_UserNotFound:
+ printf( " NERR_UserNotFound" );
+ break;
+
+ case NERR_NetNotStarted:
+ printf( " NERR_NetNotStarted" );
+ break;
+
+ case NERR_WkstaNotStarted:
+ printf( " NERR_WkstaNotStarted" );
+ break;
+
+ case NERR_ServerNotStarted:
+ printf( " NERR_ServerNotStarted" );
+ break;
+
+ case NERR_BrowserNotStarted:
+ printf( " NERR_BrowserNotStarted" );
+ break;
+
+ case NERR_ServiceNotInstalled:
+ printf( " NERR_ServiceNotInstalled" );
+ break;
+
+ case NERR_BadTransactConfig:
+ printf( " NERR_BadTransactConfig" );
+ break;
+
+ default:
+ printf( " %s", FindSymbolicNameForStatus( NetStatus ) );
+ break;
+
+ }
+
+ printf( "\n" );
+}
+
+VOID
+NlAssertFailed(
+ IN PVOID FailedAssertion,
+ IN PVOID FileName,
+ IN ULONG LineNumber,
+ IN PCHAR Message OPTIONAL
+ )
+{
+ printf( "\n*** Assertion failed: %s%s\n*** Source File: %s, line %ld\n\n",
+ Message ? Message : "",
+ FailedAssertion,
+ FileName,
+ LineNumber
+ );
+
+}
+
+
+VOID
+WhoWillLogMeOnResponse(
+ )
+
+/*++
+
+Routine Description:
+
+ This routine reads the responses that are received for the query
+ messages sent from the main thread.
+
+Arguments:
+
+ none
+
+Return Value:
+
+ None
+
+--*/
+{
+ DWORD i;
+ DWORD WaitCount;
+ DWORD IndexArray[64];
+ HANDLE HandleArray[64];
+
+ LPWSTR LocalDomainName;
+ LPWSTR LocalServerName;
+ LPWSTR LocalUserName;
+ PCHAR Where;
+ DWORD Version;
+ DWORD VersionFlags;
+ SYSTEMTIME SystemTime;
+
+ NETLOGON_SAM_LOGON_RESPONSE SamLogonResponse;
+ DWORD SamLogonResponseSize;
+ DWORD WaitStatus;
+ NET_API_STATUS NetStatus;
+ BOOL AllReceived;
+
+ for(;;) {
+
+ //
+ // make wait array.
+ //
+
+ WaitCount = 0;
+
+ AllReceived = TRUE;
+
+ for (i = 0; i < GlobalIterationCount; i++ ) {
+
+ //
+ if( GlobalMailInfo[i].State == TRUE ) {
+
+ //
+ // if a response is received.
+ //
+
+ continue;
+ }
+
+ AllReceived = FALSE;
+
+ //
+ // post a read.
+ //
+
+ if( GlobalMailInfo[i].ReadPending == FALSE ) {
+
+ if ( !ReadFile( GlobalMailInfo[i].ResponseHandle,
+ (PCHAR)&GlobalMailInfo[i].SamLogonResponse,
+ sizeof(NETLOGON_SAM_LOGON_RESPONSE),
+ &SamLogonResponseSize,
+ &GlobalMailInfo[i].OverLapped )) { // Overlapped I/O
+
+ NetStatus = GetLastError();
+
+ if( NetStatus != ERROR_IO_PENDING ) {
+
+ printf( "Cannot read mailslot (%s) : %ld\n",
+ GlobalMailInfo[i].Name,
+ NetStatus);
+ goto Cleanup;
+ }
+ }
+
+ GlobalMailInfo[i].ReadPending = TRUE;
+
+ }
+
+ HandleArray[WaitCount] = GlobalMailInfo[i].ResponseHandle;
+ IndexArray[WaitCount] = i;
+
+ WaitCount++;
+ }
+
+ if( (WaitCount == 0) ) {
+
+ if( AllReceived ) {
+
+ //
+ // we received responses for all messages, so we are
+ // done.
+ //
+
+ goto Cleanup;
+ }
+ else {
+
+ // wait for an query posted
+ //
+
+ WaitStatus = WaitForSingleObject( GlobalPostEvent, (DWORD) -1 );
+
+ if( WaitStatus != 0 ) {
+ printf("Can't successfully wait post event %ld\n",
+ WaitStatus );
+
+ goto Cleanup;
+ }
+
+ continue;
+ }
+ }
+
+ //
+ // wait for response.
+ //
+
+ WaitStatus = WaitForMultipleObjects(
+ WaitCount,
+ HandleArray,
+ FALSE, // Wait for ANY handle
+ 15000 ); // 3 * 5 Secs
+
+ if( WaitStatus == WAIT_TIMEOUT ) {
+
+ // we are done.
+
+ break;
+ }
+
+ if( (WaitStatus < 0) || (WaitStatus >= WaitCount ) ) {
+
+ printf("Invalid WaitStatus returned %ld\n", WaitStatus );
+ goto Cleanup;
+ }
+
+ //
+ // get index
+ //
+
+ i = IndexArray[WaitStatus];
+
+
+ //
+ // read response
+ //
+
+ if( !GetOverlappedResult(
+ GlobalMailInfo[i].ResponseHandle,
+ &GlobalMailInfo[i].OverLapped,
+ &SamLogonResponseSize,
+ TRUE) ) { // wait for the read complete.
+
+ printf("can't read overlapped response %ld",GetLastError() );
+ goto Cleanup;
+
+ }
+
+ SamLogonResponse = GlobalMailInfo[i].SamLogonResponse;
+
+ //
+ // indicate that we received a response.
+ //
+
+ GlobalMailInfo[i].State = TRUE;
+ GlobalMailInfo[i].ReadPending = FALSE;
+
+
+ GetLocalTime( &SystemTime );
+
+ EnterCriticalSection( &GlobalPrintCritSect );
+
+ printf( "[%02u:%02u:%02u] ",
+ SystemTime.wHour,
+ SystemTime.wMinute,
+ SystemTime.wSecond );
+
+ printf( "Response %ld: ", i);
+
+ //
+ // Ensure the version is expected.
+ //
+
+ Version = NetpLogonGetMessageVersion( &SamLogonResponse,
+ &SamLogonResponseSize,
+ &VersionFlags );
+
+ if ( Version != LMNT_MESSAGE ) {
+ printf("Response version not valid.\n");
+ goto Continue;
+ }
+
+ //
+ // Pick up the name of the server that responded.
+ //
+
+ Where = (PCHAR) &SamLogonResponse.UnicodeLogonServer;
+ if ( !NetpLogonGetUnicodeString(
+ (PCHAR)&SamLogonResponse,
+ SamLogonResponseSize,
+ &Where,
+ sizeof(SamLogonResponse.UnicodeLogonServer),
+ &LocalServerName ) ) {
+
+ printf("Response server name not formatted right\n");
+ goto Continue;
+ }
+
+ //
+ // Pick up the name of the account the response is for.
+ //
+
+ if ( !NetpLogonGetUnicodeString(
+ (PCHAR)&SamLogonResponse,
+ SamLogonResponseSize,
+ &Where,
+ sizeof(SamLogonResponse.UnicodeUserName ),
+ &LocalUserName ) ) {
+
+ printf("Response User name not formatted right\n");
+ goto Continue;
+ }
+
+ //
+ // Pick up the name of the domain the response is for.
+ //
+
+ if ( !NetpLogonGetUnicodeString(
+ (PCHAR)&SamLogonResponse,
+ SamLogonResponseSize,
+ &Where,
+ sizeof(SamLogonResponse.UnicodeUserName ),
+ &LocalDomainName ) ) {
+
+ printf("Response Domain name not formatted right\n");
+ goto Continue;
+ }
+
+ //
+ // If the response is for the correct account,
+ // break out of the loop.
+ //
+
+ if ( NlNameCompare(
+ GlobalAccountName,
+ LocalUserName,
+ NAMETYPE_USER)!=0){
+
+ printf("Response not for correct User name "
+ FORMAT_LPWSTR " s.b. " FORMAT_LPWSTR "\n",
+ LocalUserName, GlobalAccountName );
+ goto Continue;
+ }
+
+
+
+ printf( "S:" FORMAT_LPWSTR " D:" FORMAT_LPWSTR
+ " A:" FORMAT_LPWSTR,
+ LocalServerName,
+ LocalDomainName,
+ LocalUserName );
+
+ //
+ // If the DC recognizes our account,
+ // we've successfully found the DC.
+ //
+
+ switch (SamLogonResponse.Opcode) {
+ case LOGON_SAM_LOGON_RESPONSE:
+
+ printf( " (Act found)\n" );
+ break;
+
+ case LOGON_SAM_USER_UNKNOWN:
+
+ printf( " (Act not found)\n" );
+ break;
+
+ case LOGON_PAUSE_RESPONSE:
+
+ printf( " (netlogon paused)\n" );
+ break;
+
+ default:
+ printf( " (Unknown opcode: %lx)\n", SamLogonResponse.Opcode );
+ break;
+ }
+
+Continue:
+
+ LeaveCriticalSection( &GlobalPrintCritSect );
+ }
+
+Cleanup:
+
+ //
+ // print non-responsed mailslots.
+ //
+
+ for( i = 0; i < GlobalIterationCount; i++ ) {
+
+ if( GlobalMailInfo[i].State == FALSE ) {
+
+ printf("Didn't receive a response for mail "
+ "message %ld (%s)\n", i, GlobalMailInfo[i].Name );
+ }
+ }
+
+ return;
+}
+
+
+
+VOID
+WhoWillLogMeOn(
+ IN LPWSTR DomainName,
+ IN LPWSTR AccountName,
+ IN DWORD IterationCount
+ )
+
+/*++
+
+Routine Description:
+
+ Determine which DC will log the specified account on
+
+Arguments:
+
+ DomainName - name of the "doamin" to send the message to
+
+ AccountName - Name of our user account to find.
+
+ IterationCount - Number of consecutive messages to send.
+
+Return Value:
+
+ None
+
+--*/
+{
+
+ NET_API_STATUS NetStatus;
+ ULONG AllowableAccountControlBits = USER_ACCOUNT_TYPE_MASK;
+
+ WCHAR NetlogonMailslotName[DNLEN+NETLOGON_NT_MAILSLOT_LEN+4];
+ NETLOGON_SAM_LOGON_REQUEST SamLogonRequest;
+ PCHAR Where;
+ PCHAR WhereResponseMailslot;
+
+ HANDLE *ResponseMailslotHandle = NULL;
+
+ WCHAR ComputerName[MAX_COMPUTERNAME_LENGTH+1];
+ DWORD ComputerNameLength = MAX_COMPUTERNAME_LENGTH+1;
+
+ DWORD i;
+ DWORD j;
+ SYSTEMTIME SystemTime;
+
+ HANDLE ResponseThreadHandle;
+ DWORD ThreadId;
+ DWORD WaitStatus;
+ DWORD SamLogonResponseSize;
+
+ //
+ // support only 64 iterations
+ //
+
+ if( IterationCount > 64 ) {
+
+ printf("Interations set to 64, maximum supported\n");
+ IterationCount = 64;
+ }
+
+ GlobalIterationCount = IterationCount;
+ GlobalAccountName = AccountName;
+
+ InitializeCriticalSection( &GlobalPrintCritSect );
+
+ //
+ // Get out computer name
+ //
+
+ if (!GetComputerName( ComputerName, &ComputerNameLength ) ) {
+ printf( "Can't GetComputerName\n" );
+ return;
+ }
+
+ //
+ // create mailslots
+ //
+
+ for (i = 0; i < IterationCount; i++ ) {
+
+ //
+ // Create a mailslot for the DC's to respond to.
+ //
+
+ if (NetStatus = NetpLogonCreateRandomMailslot(
+ GlobalMailInfo[i].Name,
+ &GlobalMailInfo[i].ResponseHandle)){
+
+ printf( "Cannot create temp mailslot %ld\n", NetStatus );
+ goto Cleanup;
+ }
+
+ if ( !SetMailslotInfo( GlobalMailInfo[i].ResponseHandle,
+ (DWORD) MAILSLOT_WAIT_FOREVER ) ) {
+ printf( "Cannot set mailslot info %ld\n", GetLastError() );
+ goto Cleanup;
+ }
+
+ (void) memset( &GlobalMailInfo[i].OverLapped, '\0',
+ sizeof(OVERLAPPED) );
+
+ GlobalMailInfo[i].State = FALSE;
+ GlobalMailInfo[i].ReadPending = FALSE;
+ }
+
+
+ //
+ // create post event
+ //
+
+ GlobalPostEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
+
+ if( GlobalPostEvent == NULL ) {
+
+ printf("can't create post event %ld \n", GetLastError() );
+ goto Cleanup;
+ }
+
+ InitializeCriticalSection( &GlobalPrintCritSect );
+
+ //
+ // start response thread.
+ //
+
+ ResponseThreadHandle =
+ CreateThread(
+ NULL, // No security attributes
+ THREAD_STACKSIZE,
+ (LPTHREAD_START_ROUTINE)
+ WhoWillLogMeOnResponse,
+ NULL,
+ 0, // No special creation flags
+ &ThreadId );
+
+ if ( ResponseThreadHandle == NULL ) {
+
+ printf("can't create response thread %ld\n", GetLastError() );
+ goto Cleanup;
+ }
+
+ //
+ // Build the query message.
+ //
+
+ SamLogonRequest.Opcode = LOGON_SAM_LOGON_REQUEST;
+ SamLogonRequest.RequestCount = 0;
+
+ Where = (PCHAR) &SamLogonRequest.UnicodeComputerName;
+
+ NetpLogonPutUnicodeString(
+ ComputerName,
+ sizeof(SamLogonRequest.UnicodeComputerName),
+ &Where );
+
+ NetpLogonPutUnicodeString(
+ AccountName,
+ sizeof(SamLogonRequest.UnicodeUserName),
+ &Where );
+
+ WhereResponseMailslot = Where;
+
+ wcscpy( NetlogonMailslotName, L"\\\\" );
+ wcscat( NetlogonMailslotName, DomainName );
+ // wcscat( NetlogonMailslotName, L"*" ); // Don't add for computer name
+ wcscat( NetlogonMailslotName, NETLOGON_NT_MAILSLOT_W);
+
+ //
+ // Send atmost 3 messages/mailslot
+ //
+
+ for( j = 0; j < 3; j++ ) {
+
+ //
+ // Repeat the message multiple times to load the servers
+ //
+
+ for (i = 0; i < IterationCount; i++ ) {
+
+ if( GlobalMailInfo[i].State == TRUE ) {
+
+ //
+ // if a response is received.
+ //
+
+ continue;
+ }
+
+ Where = WhereResponseMailslot;
+
+ NetpLogonPutOemString(
+ GlobalMailInfo[i].Name,
+ sizeof(SamLogonRequest.MailslotName),
+ &Where );
+
+ NetpLogonPutBytes(
+ &AllowableAccountControlBits,
+ sizeof(SamLogonRequest.AllowableAccountControlBits),
+ &Where );
+
+ NetpLogonPutNtToken( &Where );
+
+ //
+ // Send the message to a DC for the domain.
+ //
+
+ NetStatus = NetpLogonWriteMailslot(
+ NetlogonMailslotName,
+ (PCHAR)&SamLogonRequest,
+ Where - (PCHAR)(&SamLogonRequest) );
+
+ if ( NetStatus != NERR_Success ) {
+ printf( "Cannot write netlogon mailslot: %ld\n", NetStatus);
+ goto Cleanup;
+ }
+
+ GetLocalTime( &SystemTime );
+
+ EnterCriticalSection( &GlobalPrintCritSect );
+
+ printf( "[%02u:%02u:%02u] ",
+ SystemTime.wHour,
+ SystemTime.wMinute,
+ SystemTime.wSecond );
+
+ printf( "Mail message %ld sent successfully (%s) \n",
+ i, GlobalMailInfo[i].Name );
+
+ LeaveCriticalSection( &GlobalPrintCritSect );
+
+ if( !SetEvent( GlobalPostEvent ) ) {
+ printf("Can't set post event %ld \n", GetLastError() );
+ goto Cleanup;
+ }
+
+
+ }
+
+ //
+ // wait 5 secs to see response thread received all responses.
+ //
+
+ WaitStatus = WaitForSingleObject( ResponseThreadHandle, 5000 );
+ // 15 secs. TIMEOUT
+
+ if( WaitStatus != WAIT_TIMEOUT ) {
+
+ if( WaitStatus != 0 ) {
+ printf("can't do WaitForSingleObject %ld\n", WaitStatus);
+ }
+
+ goto Cleanup;
+ }
+ }
+
+
+Cleanup:
+
+ //
+ // Wait for the response thread to complete.
+ //
+
+ if( ResponseThreadHandle != NULL ) {
+
+ WaitStatus = WaitForSingleObject( ResponseThreadHandle, 15000 );
+ // 15 secs. TIMEOUT
+
+ if( WaitStatus ) {
+
+ if( WaitStatus == WAIT_TIMEOUT ) {
+ printf("Can't stop response thread (TIMEOUT) \n");
+ } else {
+ printf("Can't stop response thread %ld \n", WaitStatus);
+ }
+ }
+
+ }
+
+
+ for (i = 0; i < IterationCount; i++ ) {
+
+ if( GlobalMailInfo[i].ResponseHandle != NULL ) {
+ CloseHandle( GlobalMailInfo[i].ResponseHandle);
+ }
+ }
+
+ if( GlobalPostEvent != NULL ) {
+ CloseHandle( GlobalPostEvent );
+ }
+
+ DeleteCriticalSection( &GlobalPrintCritSect );
+
+ return;
+}
+
+#define MAX_PRINTF_LEN 1024 // Arbitrary.
+
+VOID
+NlPrintRoutine(
+ IN DWORD DebugFlag,
+ IN LPSTR Format,
+ ...
+ )
+{
+ va_list arglist;
+ char OutputBuffer[MAX_PRINTF_LEN];
+
+ //
+ // Put a the information requested by the caller onto the line
+ //
+
+ va_start(arglist, Format);
+ (VOID) vsprintf(OutputBuffer, Format, arglist);
+ va_end(arglist);
+
+ printf( "%s", OutputBuffer );
+ return;
+ UNREFERENCED_PARAMETER( DebugFlag );
+}
+
+NTSTATUS
+SimulateFullSync(
+ LPWSTR PdcName,
+ LPWSTR MachineName
+ )
+/*++
+
+Routine Description:
+
+ This function simulate a full sync replication by calling
+ NetDatabaseSync API and simply ignoring successfully returned data.
+
+Arguments:
+
+ PdcName - Name of the PDC from where the database replicated.
+
+ MachineName - Name of the machine account used to authenticate.
+
+Return Value:
+
+ Network Status code.
+
+--*/
+{
+ NTSTATUS Status;
+
+ NETLOGON_CREDENTIAL ServerChallenge;
+ NETLOGON_CREDENTIAL ClientChallenge;
+ NETLOGON_CREDENTIAL ComputedServerCredential;
+ NETLOGON_CREDENTIAL ReturnedServerCredential;
+
+ NETLOGON_CREDENTIAL AuthenticationSeed;
+ NETLOGON_SESSION_KEY SessionKey;
+
+ NETLOGON_AUTHENTICATOR OurAuthenticator;
+ NETLOGON_AUTHENTICATOR ReturnAuthenticator;
+
+ UNICODE_STRING Password;
+ NT_OWF_PASSWORD NtOwfPassword;
+
+ ULONG SamSyncContext = 0;
+ PNETLOGON_DELTA_ENUM_ARRAY DeltaArray = NULL;
+
+ DWORD DatabaseIndex;
+ DWORD i;
+
+ WCHAR AccountName[SSI_ACCOUNT_NAME_LENGTH+1];
+
+ //
+ // Prepare our challenge
+ //
+
+ NlComputeChallenge( &ClientChallenge );
+
+ printf("ClientChallenge = %lx %lx\n",
+ ((DWORD*)&ClientChallenge)[0],
+ ((DWORD *)&ClientChallenge)[1]);
+
+ //
+ // Get the primary's challenge
+ //
+
+ Status = I_NetServerReqChallenge(PdcName,
+ MachineName,
+ &ClientChallenge,
+ &ServerChallenge );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ fprintf( stderr,
+ "I_NetServerReqChallenge to " FORMAT_LPWSTR
+ " returned 0x%lx\n",
+ PdcName,
+ Status );
+ return(Status);
+ }
+
+
+ printf("ServerChallenge = %lx %lx\n",
+ ((DWORD *)&ServerChallenge)[0],
+ ((DWORD *)&ServerChallenge)[1]);
+
+ Password.Length =
+ Password.MaximumLength = wcslen(MachineName) * sizeof(WCHAR);
+ Password.Buffer = MachineName;
+
+ //
+ // Compute the NT OWF password for this user.
+ //
+
+ Status = RtlCalculateNtOwfPassword( &Password, &NtOwfPassword );
+
+ if ( !NT_SUCCESS( Status ) ) {
+
+ fprintf(stderr, "Can't compute OWF password 0x%lx \n", Status );
+ return(Status);
+ }
+
+
+ printf("Password = %lx %lx %lx %lx\n",
+ ((DWORD *) (&NtOwfPassword))[0],
+ ((DWORD *) (&NtOwfPassword))[1],
+ ((DWORD *) (&NtOwfPassword))[2],
+ ((DWORD *) (&NtOwfPassword))[3]);
+
+
+ //
+ // Actually compute the session key given the two challenges and the
+ // password.
+ //
+
+ NlMakeSessionKey(
+ &NtOwfPassword,
+ &ClientChallenge,
+ &ServerChallenge,
+ &SessionKey );
+
+ printf("SessionKey = %lx %lx %lx %lx\n",
+ ((DWORD *) (&SessionKey))[0],
+ ((DWORD *) (&SessionKey))[1],
+ ((DWORD *) (&SessionKey))[2],
+ ((DWORD *) (&SessionKey))[3]);
+
+ //
+ // Prepare credentials using our challenge.
+ //
+
+ NlComputeCredentials( &ClientChallenge,
+ &AuthenticationSeed,
+ &SessionKey );
+
+ printf("ClientCredential = %lx %lx\n",
+ ((DWORD *) (&AuthenticationSeed))[0],
+ ((DWORD *) (&AuthenticationSeed))[1]);
+
+ //
+ // Send these credentials to primary. The primary will compute
+ // credentials using the challenge supplied by us and compare
+ // with these. If both match then it will compute credentials
+ // using its challenge and return it to us for verification
+ //
+
+ wcscpy( AccountName, MachineName );
+ wcscat( AccountName, SSI_ACCOUNT_NAME_POSTFIX);
+
+ Status = I_NetServerAuthenticate( PdcName,
+ AccountName,
+ ServerSecureChannel,
+ MachineName,
+ &AuthenticationSeed,
+ &ReturnedServerCredential );
+
+ if ( !NT_SUCCESS( Status ) ) {
+
+ fprintf(stderr,
+ "I_NetServerAuthenticate to " FORMAT_LPWSTR " 0x%lx\n",
+ &PdcName,
+ Status );
+
+ return(Status);
+
+ }
+
+
+ printf("ServerCredential GOT = %lx %lx\n",
+ ((DWORD *) (&ReturnedServerCredential))[0],
+ ((DWORD *) (&ReturnedServerCredential))[1]);
+
+
+ //
+ // The DC returned a server credential to us,
+ // ensure the server credential matches the one we would compute.
+ //
+
+ NlComputeCredentials( &ServerChallenge,
+ &ComputedServerCredential,
+ &SessionKey);
+
+
+ printf("ServerCredential MADE =%lx %lx\n",
+ ((DWORD *) (&ComputedServerCredential))[0],
+ ((DWORD *) (&ComputedServerCredential))[1]);
+
+
+ if (RtlCompareMemory( &ReturnedServerCredential,
+ &ComputedServerCredential,
+ sizeof(ReturnedServerCredential)) !=
+ sizeof(ReturnedServerCredential)) {
+
+ fprintf( stderr, "Access Denied \n");
+ return( STATUS_ACCESS_DENIED );
+ }
+
+
+ printf("Session Setup to " FORMAT_LPWSTR " completed successfully \n",
+ PdcName);
+
+ //
+ // retrive database info
+ //
+
+ for( DatabaseIndex = 0 ; DatabaseIndex < 3; DatabaseIndex++) {
+
+ SamSyncContext = 0;
+
+ for( i = 0; ; i++) {
+
+ NlBuildAuthenticator(
+ &AuthenticationSeed,
+ &SessionKey,
+ &OurAuthenticator);
+
+ Status = I_NetDatabaseSync(
+ PdcName,
+ MachineName,
+ &OurAuthenticator,
+ &ReturnAuthenticator,
+ DatabaseIndex,
+ &SamSyncContext,
+ &DeltaArray,
+ 128 * 1024 ); // 128K
+
+ if ( !NT_SUCCESS( Status ) ) {
+
+ fprintf( stderr,
+ "I_NetDatabaseSync to " FORMAT_LPWSTR " failed 0x%lx\n",
+ PdcName,
+ Status );
+
+ return(Status);
+ }
+
+ if ( ( !NlUpdateSeed(
+ &AuthenticationSeed,
+ &ReturnAuthenticator.Credential,
+ &SessionKey) ) ) {
+
+ fprintf(stderr, "NlUpdateSeed failed \n" );
+ return( STATUS_ACCESS_DENIED );
+ }
+
+ printf( "Received %ld Buffer data \n", i);
+ //
+ // ignore return data
+ //
+
+ MIDL_user_free( DeltaArray );
+
+ if ( Status == STATUS_SUCCESS ) {
+
+ break;
+ }
+
+ }
+
+ printf("FullSync replication of database %ld completed "
+ "successfully \n", DatabaseIndex );
+
+ }
+
+}
+
+
+LONG
+ForceRegOpenSubkey(
+ HKEY ParentHandle,
+ LPSTR KeyName,
+ LPSTR Subkey,
+ REGSAM DesiredAccess,
+ PHKEY ReturnHandle
+ )
+
+/*++
+
+Routine Description:
+
+ Open the specified key one subkey at a time defeating access denied by
+ setting the DACL to allow us access. This kludge is needed since the
+ security tree is shipped not allowing Administrators access.
+
+Arguments:
+
+ ParentHandle - Currently open handle
+
+ KeyName - Entire key name (for error messages only)
+
+ Subkey - Direct subkey of ParentHandle
+
+ DesiredAccess - Desired access to the new key
+
+ ReturnHandle - Returns an open handle to the newly opened key.
+
+
+Return Value:
+
+ Return TRUE for success.
+
+--*/
+
+{
+ LONG RegStatus;
+ LONG SavedStatus;
+ HKEY TempHandle = NULL;
+ BOOLEAN DaclChanged = FALSE;
+
+ SECURITY_INFORMATION SecurityInformation = DACL_SECURITY_INFORMATION;
+ DWORD OldSecurityDescriptorSize;
+ CHAR OldSecurityDescriptor[1024];
+ CHAR NewSecurityDescriptor[1024];
+
+ SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
+ PSID AdminSid = NULL;
+ BOOL DaclPresent;
+ BOOL DaclDefaulted;
+ PACL Dacl;
+ ACL_SIZE_INFORMATION AclSizeInfo;
+ ACCESS_ALLOWED_ACE *Ace;
+ DWORD i;
+
+
+ //
+ // Open the sub-key
+ //
+
+ SavedStatus = RegOpenKeyExA(
+ ParentHandle,
+ Subkey,
+ 0, //Reserved
+ DesiredAccess,
+ ReturnHandle );
+
+ if ( SavedStatus != ERROR_ACCESS_DENIED ) {
+ return SavedStatus;
+ }
+
+ //
+ // If access is denied,
+ // try changing the DACL to give us access
+ //
+
+ // printf( "Cannot RegOpenKey %s subkey %s ", KeyName, Subkey );
+ // PrintStatus( SavedStatus );
+
+ //
+ // Open again asking to change the DACL
+ //
+
+ RegStatus = RegOpenKeyExA(
+ ParentHandle,
+ Subkey,
+ 0, //Reserved
+ WRITE_DAC | READ_CONTROL,
+ &TempHandle );
+
+ if ( RegStatus != ERROR_SUCCESS) {
+ printf( "Cannot RegOpenKey to change DACL %s subkey %s ", KeyName, Subkey );
+ PrintStatus( RegStatus );
+ goto Cleanup;
+ }
+
+ //
+ // Get the current DACL so we can restore it.
+ //
+
+ OldSecurityDescriptorSize = sizeof(OldSecurityDescriptor);
+ RegStatus = RegGetKeySecurity(
+ TempHandle,
+ SecurityInformation,
+ (PSECURITY_DESCRIPTOR) OldSecurityDescriptor,
+ &OldSecurityDescriptorSize );
+
+ if ( RegStatus != ERROR_SUCCESS ) {
+ printf( "Cannot RegGetKeySecurity for %s subkey %s ", KeyName, Subkey );
+ PrintStatus( RegStatus );
+ goto Cleanup;
+ }
+
+ //
+ // Build the Administrators SID
+ //
+ if ( !AllocateAndInitializeSid( &NtAuthority,
+ 2, // two subauthorities
+ SECURITY_BUILTIN_DOMAIN_RID,
+ DOMAIN_ALIAS_RID_ADMINS,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ &AdminSid ) ) {
+ printf( "Cannot AllocateAndInitializeSid " );
+ PrintStatus( GetLastError() );
+ goto Cleanup;
+ }
+
+ //
+ // Change the DACL to allow all access
+ //
+
+ RtlCopyMemory( NewSecurityDescriptor,
+ OldSecurityDescriptor,
+ OldSecurityDescriptorSize );
+
+ if ( !GetSecurityDescriptorDacl(
+ (PSECURITY_DESCRIPTOR)NewSecurityDescriptor,
+ &DaclPresent,
+ &Dacl,
+ &DaclDefaulted )) {
+ printf( "Cannot GetSecurityDescriptorDacl for %s subkey %s ", KeyName, Subkey );
+ PrintStatus( GetLastError() );
+ goto Cleanup;
+ }
+
+ if ( !DaclPresent ) {
+ printf( "Cannot GetSecurityDescriptorDacl " );
+ printf( "Cannot DaclNotPresent for %s subkey %s ", KeyName, Subkey );
+ goto Cleanup;
+ }
+
+ if ( !GetAclInformation(
+ Dacl,
+ &AclSizeInfo,
+ sizeof(AclSizeInfo),
+ AclSizeInformation )) {
+ printf( "Cannot GetAclInformation for %s subkey %s ", KeyName, Subkey );
+ PrintStatus( GetLastError() );
+ goto Cleanup;
+ }
+
+
+
+ //
+ // Search for an administrators ACE and give it "DesiredAccess"
+ //
+
+ for ( i=0; i<AclSizeInfo.AceCount ; i++ ) {
+
+ if ( !GetAce( Dacl, i, (LPVOID *) &Ace ) ) {
+ printf( "Cannot GetAce %ld for %s subkey %s ", i, KeyName, Subkey );
+ PrintStatus( GetLastError() );
+ goto Cleanup;
+ }
+
+ if ( Ace->Header.AceType != ACCESS_ALLOWED_ACE_TYPE ) {
+ continue;
+ }
+
+ if ( !EqualSid( AdminSid, (PSID)&Ace->SidStart ) ) {
+ continue;
+ }
+
+ Ace->Mask |= DesiredAccess;
+ break;
+
+ }
+
+ if ( i >= AclSizeInfo.AceCount ) {
+ printf( "No Administrators Ace for %s subkey %s\n", KeyName, Subkey );
+ goto Cleanup;
+ }
+
+ //
+ // Actually set the new DACL on the key
+ //
+
+ RegStatus = RegSetKeySecurity(
+ TempHandle,
+ SecurityInformation,
+ (PSECURITY_DESCRIPTOR)NewSecurityDescriptor );
+
+ if ( RegStatus != ERROR_SUCCESS ) {
+ printf( "Cannot RegSetKeySecurity for %s subkey %s ", KeyName, Subkey );
+ PrintStatus( RegStatus );
+ goto Cleanup;
+ }
+
+ DaclChanged = TRUE;
+
+
+ //
+ // Open the sub-key again with the desired access
+ //
+
+ SavedStatus = RegOpenKeyExA(
+ ParentHandle,
+ Subkey,
+ 0, //Reserved
+ DesiredAccess,
+ ReturnHandle );
+
+ if ( SavedStatus != ERROR_SUCCESS ) {
+ printf( "Cannot RegOpenKeyEx following DACL change for %s subkey %s ", KeyName, Subkey );
+ PrintStatus( SavedStatus );
+ goto Cleanup;
+ }
+
+
+Cleanup:
+ if ( TempHandle != NULL ) {
+ //
+ // Restore DACL to original value.
+ //
+
+ if ( DaclChanged ) {
+
+ RegStatus = RegSetKeySecurity(
+ TempHandle,
+ SecurityInformation,
+ (PSECURITY_DESCRIPTOR)OldSecurityDescriptor );
+
+ if ( RegStatus != ERROR_SUCCESS ) {
+ printf( "Cannot RegSetKeySecurity to restore %s subkey %s ", KeyName, Subkey );
+ PrintStatus( RegStatus );
+ goto Cleanup;
+ }
+ }
+ (VOID) RegCloseKey( TempHandle );
+ }
+
+ if ( AdminSid != NULL ) {
+ (VOID) FreeSid( AdminSid );
+ }
+
+ return SavedStatus;
+
+}
+
+
+
+LONG
+ForceRegOpenKey(
+ HKEY BaseHandle,
+ LPSTR KeyName,
+ REGSAM DesiredAccess,
+ PHKEY ReturnHandle
+ )
+
+/*++
+
+Routine Description:
+
+ Open the specified key one subkey at a time defeating access denied by
+ setting the DACL to allow us access. This kludge is needed since the
+ security tree is shipped not allowing Administrators access.
+
+Arguments:
+
+ BaseHandle - Currently open handle
+
+ KeyName - Registry key to open relative to BaseHandle.
+
+ DesiredAccess - Desired access to the new key
+
+ ReturnHandle - Returns an open handle to the newly opened key.
+
+
+Return Value:
+
+ Return TRUE for success.
+
+--*/
+
+{
+ LONG RegStatus;
+ PCHAR StartOfSubkey;
+ PCHAR EndOfSubkey;
+ CHAR Subkey[512];
+ HKEY ParentHandle;
+
+ ASSERT( KeyName[0] != '\0' );
+
+
+ //
+ // Loop opening the next subkey.
+ //
+
+ EndOfSubkey = KeyName;
+ ParentHandle = BaseHandle;
+
+ for (;;) {
+
+
+ //
+ // Compute the name of the next subkey.
+ //
+
+ StartOfSubkey = EndOfSubkey;
+
+ for ( ;; ) {
+
+ if ( *EndOfSubkey == '\0' || *EndOfSubkey == '\\' ) {
+ strncpy( Subkey, StartOfSubkey, EndOfSubkey-StartOfSubkey );
+ Subkey[EndOfSubkey-StartOfSubkey] = '\0';
+ if ( *EndOfSubkey == '\\' ) {
+ EndOfSubkey ++;
+ }
+ break;
+ }
+ EndOfSubkey ++;
+ }
+
+
+ //
+ // Open the sub-key
+ //
+
+ RegStatus = ForceRegOpenSubkey(
+ ParentHandle,
+ KeyName,
+ Subkey,
+ DesiredAccess,
+ ReturnHandle );
+
+
+ //
+ // Close the parent handle and return any error condition.
+ //
+
+ if ( ParentHandle != BaseHandle ) {
+ (VOID) RegCloseKey( ParentHandle );
+ }
+
+ if( RegStatus != ERROR_SUCCESS ) {
+ *ReturnHandle = NULL;
+ return RegStatus;
+ }
+
+
+ //
+ // If this is the entire key name,
+ // we're done.
+ //
+
+ if ( *EndOfSubkey == '\0' ) {
+ return ERROR_SUCCESS;
+ }
+
+ ParentHandle = *ReturnHandle;
+
+ }
+
+}
+
+
+struct {
+ LPSTR Name;
+ enum {
+ UnicodeStringType,
+ HexDataType,
+ LmPasswordType,
+ NtPasswordType
+ } Type;
+} UserVariableDataTypes[] = {
+ { "SecurityDescriptor" , HexDataType },
+ { "AccountName" , UnicodeStringType },
+ { "FullName" , UnicodeStringType },
+ { "AdminComment" , UnicodeStringType },
+ { "UserComment" , UnicodeStringType },
+ { "Parameters" , UnicodeStringType },
+ { "HomeDirectory" , UnicodeStringType },
+ { "HomeDirectoryDrive" , UnicodeStringType },
+ { "ScriptPath" , UnicodeStringType },
+ { "ProfilePath" , UnicodeStringType },
+ { "Workstations" , UnicodeStringType },
+ { "LogonHours" , HexDataType },
+ { "Groups" , HexDataType },
+ { "LmOwfPassword" , LmPasswordType },
+ { "NtOwfPassword" , NtPasswordType },
+ { "NtPasswordHistory" , HexDataType },
+ { "LmPasswordHistory" , HexDataType }
+};
+
+
+VOID
+PrintUserInfo(
+ IN LPWSTR ServerName,
+ IN LPSTR UserName
+ )
+/*++
+
+Routine Description:
+
+ Print a user's description from the SAM database
+
+Arguments:
+
+ ServerName - Name of server to query
+
+ UserName - Name of user to query
+
+Return Value:
+
+ None
+
+--*/
+{
+ NTSTATUS Status;
+ LONG RegStatus;
+ ULONG i;
+
+ HKEY BaseHandle = NULL;
+ HKEY UserHandle = NULL;
+ HKEY RidHandle = NULL;
+
+ CHAR UserKey[200];
+ CHAR RidKey[200];
+ LONG Rid;
+ CHAR AnsiRid[20];
+
+ CHAR FixedData[1000];
+ ULONG FixedDataSize;
+ SAMP_V1_0A_FIXED_LENGTH_USER FixedUser1_0A;
+ PSAMP_V1_0A_FIXED_LENGTH_USER f;
+ PSAMP_V1_0_FIXED_LENGTH_USER f1_0;
+ BOOLEAN IsVersion1_0;
+
+ CHAR VariableData[32768];
+ ULONG VariableDataSize;
+ PSAMP_VARIABLE_LENGTH_ATTRIBUTE v;
+
+ LM_OWF_PASSWORD LmOwfPassword;
+ NT_OWF_PASSWORD NtOwfPassword;
+
+ //
+ // Open the registry
+ //
+
+ RegStatus = RegConnectRegistryW( ServerName,
+ HKEY_LOCAL_MACHINE,
+ &BaseHandle);
+
+ if ( RegStatus != ERROR_SUCCESS ) {
+ printf( "Cannot connect to registy on " FORMAT_LPWSTR " ", ServerName );
+ PrintStatus( RegStatus );
+ goto Cleanup;
+ }
+
+
+ //
+ // Open the key for this user name.
+ //
+
+ strcpy( UserKey, "SAM\\SAM\\Domains\\Account\\Users\\Names\\" );
+ strcat( UserKey, UserName );
+
+ RegStatus = ForceRegOpenKey( BaseHandle,
+ UserKey,
+ KEY_READ|KEY_QUERY_VALUE,
+ &UserHandle );
+
+ if ( RegStatus != ERROR_SUCCESS ) {
+ printf( "Cannot open %s ", UserKey );
+ PrintStatus( RegStatus );
+ goto Cleanup;
+ }
+
+ //
+ // Get the RID of the user
+ //
+
+ RegStatus = RegQueryValueExW( UserHandle,
+ NULL, // No name
+ NULL, // Reserved
+ &Rid, // Really the type
+ NULL, // Data not needed
+ NULL); // Data not needed
+
+ if ( RegStatus != ERROR_SUCCESS ) {
+ printf( "Cannot Query %s ", UserKey );
+ PrintStatus( RegStatus );
+ goto Cleanup;
+ }
+
+ printf( "User: %s\nRid: 0x%lx\n",
+ UserName,
+ Rid );
+
+
+ //
+ // Open the key for this user rid.
+ //
+
+ sprintf( AnsiRid, "%8.8lx", Rid );
+ strcpy( RidKey, "SAM\\SAM\\Domains\\Account\\Users\\" );
+ strcat( RidKey, AnsiRid );
+
+ RegStatus = ForceRegOpenKey( BaseHandle,
+ RidKey,
+ KEY_READ|KEY_QUERY_VALUE,
+ &RidHandle );
+
+ if ( RegStatus != ERROR_SUCCESS ) {
+ printf( "Cannot open %s ", RidKey );
+ PrintStatus( RegStatus );
+ goto Cleanup;
+ }
+
+
+ //
+ // Get the Fixed Values associated with this RID
+ //
+
+ FixedDataSize = sizeof(FixedData);
+ RegStatus = RegQueryValueExA( RidHandle,
+ "F", // Fixed value
+ NULL, // Reserved
+ NULL, // Type Not Needed
+ FixedData,
+ &FixedDataSize );
+
+ if ( RegStatus != ERROR_SUCCESS ) {
+ printf( "Cannot Query %s ", RidKey );
+ PrintStatus( RegStatus );
+ goto Cleanup;
+ }
+
+ //
+ // If the fixed length data is NT 1.0,
+ // convert it to NT 1.0A format.
+ //
+
+ if ( IsVersion1_0 = (FixedDataSize == sizeof(*f1_0)) ) {
+ f1_0 = (PSAMP_V1_0_FIXED_LENGTH_USER) &FixedData;
+ FixedUser1_0A.LastLogon = f1_0->LastLogon;
+ FixedUser1_0A.LastLogoff = f1_0->LastLogoff;
+ FixedUser1_0A.PasswordLastSet = f1_0->PasswordLastSet;
+ FixedUser1_0A.AccountExpires = f1_0->AccountExpires;
+ FixedUser1_0A.UserId = f1_0->UserId;
+ FixedUser1_0A.PrimaryGroupId = f1_0->PrimaryGroupId;
+ FixedUser1_0A.UserAccountControl = f1_0->UserAccountControl;
+ FixedUser1_0A.CountryCode = f1_0->CountryCode;
+ FixedUser1_0A.BadPasswordCount = f1_0->BadPasswordCount;
+ FixedUser1_0A.LogonCount = f1_0->LogonCount;
+ FixedUser1_0A.AdminCount = f1_0->AdminCount;
+ RtlCopyMemory( FixedData, &FixedUser1_0A, sizeof(FixedUser1_0A) );
+ }
+
+ //
+ // Print the fixed length data.
+ //
+
+ f = (PSAMP_V1_0A_FIXED_LENGTH_USER) &FixedData;
+
+ if ( !IsVersion1_0) {
+ printf( "Version: 0x%lx\n", f->Revision );
+ }
+
+ PrintTime( "LastLogon: ", f->LastLogon );
+ PrintTime( "LastLogoff: ", f->LastLogoff );
+ PrintTime( "PasswordLastSet: ", f->PasswordLastSet );
+ PrintTime( "AccountExpires: ", f->AccountExpires );
+ if ( !IsVersion1_0) {
+ PrintTime( "LastBadPasswordTime: ", f->LastBadPasswordTime );
+ }
+
+ printf( "PrimaryGroupId: 0x%lx\n", f->PrimaryGroupId );
+ printf( "UserAccountControl: 0x%lx\n", f->UserAccountControl );
+
+ printf( "CountryCode: 0x%lx\n", f->CountryCode );
+ printf( "CodePage: 0x%lx\n", f->CodePage );
+ printf( "BadPasswordCount: 0x%lx\n", f->BadPasswordCount );
+ printf( "LogonCount: 0x%lx\n", f->LogonCount );
+ printf( "AdminCount: 0x%lx\n", f->AdminCount );
+
+
+ //
+ // Get the Variable Values associated with this RID
+ //
+
+ VariableDataSize = sizeof(VariableData);
+ RegStatus = RegQueryValueExA( RidHandle,
+ "V", // Variable value
+ NULL, // Reserved
+ NULL, // Type Not Needed
+ VariableData,
+ &VariableDataSize );
+
+ if ( RegStatus != ERROR_SUCCESS ) {
+ printf( "Cannot Query %s \n", RidKey );
+ PrintStatus( RegStatus );
+ goto Cleanup;
+ }
+
+ //
+ // Loop printing all the attributes.
+ //
+
+ v = (PSAMP_VARIABLE_LENGTH_ATTRIBUTE) VariableData;
+
+ for ( i=0;
+ i<sizeof(UserVariableDataTypes)/sizeof(UserVariableDataTypes[0]);
+ i++ ) {
+
+ UNICODE_STRING UnicodeString;
+
+ //
+ // Make the offset relative to the beginning of the queried value.
+ //
+
+ v[i].Offset += SAMP_USER_VARIABLE_ATTRIBUTES *
+ sizeof(SAMP_VARIABLE_LENGTH_ATTRIBUTE);
+
+
+
+ //
+ // Ensure the data item descriptor is in the registry.
+ //
+
+ if ( ((PCHAR)&v[i]) > ((PCHAR)v)+VariableDataSize ) {
+ printf( "Variable data desc %ld not in variable value.\n", i );
+ goto Cleanup;
+ }
+
+ if ( v[i].Offset > (LONG) VariableDataSize ||
+ v[i].Offset + v[i].Length > VariableDataSize ) {
+ printf( "Variable data item %ld not in variable value.\n", i );
+ printf( "Offset: %ld Length: %ld Size: %ld\n",
+ v[i].Offset,
+ v[i].Length,
+ VariableDataSize );
+ goto Cleanup;
+
+ }
+
+ //
+ // Don't print null data.
+ //
+
+ if ( v[i].Length == 0 ) {
+ continue;
+ }
+
+ //
+ // Print the various types of data.
+ //
+
+ switch ( UserVariableDataTypes[i].Type ) {
+ case UnicodeStringType:
+
+ UnicodeString.Buffer = (PUSHORT)(((PCHAR)v)+v[i].Offset);
+ UnicodeString.Length = (USHORT)v[i].Length;
+ printf( "%s: %wZ\n", UserVariableDataTypes[i].Name, &UnicodeString);
+ break;
+
+ case LmPasswordType:
+ Status = RtlDecryptLmOwfPwdWithIndex(
+ (PENCRYPTED_LM_OWF_PASSWORD)(((PCHAR)v)+v[i].Offset),
+ &Rid,
+ &LmOwfPassword );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ printf( "Cannot decrypt LM password: " );
+ PrintStatus( Status );
+ goto Cleanup;
+ }
+
+ printf( "%s: ", UserVariableDataTypes[i].Name);
+ DumpBuffer( &LmOwfPassword, sizeof(LmOwfPassword ));
+ break;
+
+ case NtPasswordType:
+ Status = RtlDecryptNtOwfPwdWithIndex(
+ (PENCRYPTED_NT_OWF_PASSWORD)(((PCHAR)v)+v[i].Offset),
+ &Rid,
+ &NtOwfPassword );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ printf( "Cannot decrypt NT password: " );
+ PrintStatus( Status );
+ goto Cleanup;
+ }
+
+ printf( "%s: ", UserVariableDataTypes[i].Name);
+ DumpBuffer( &NtOwfPassword, sizeof(NtOwfPassword ));
+ break;
+
+
+ case HexDataType:
+
+ printf( "%s: ", UserVariableDataTypes[i].Name);
+ DumpBuffer( (((PCHAR)v)+v[i].Offset), v[i].Length );
+ break;
+ }
+ }
+
+
+ //
+ // Be tidy.
+ //
+Cleanup:
+ if ( UserHandle != NULL ) {
+ RegCloseKey( UserHandle );
+ }
+ if ( RidHandle != NULL ) {
+ RegCloseKey( RidHandle );
+ }
+ if ( BaseHandle != NULL ) {
+ RegCloseKey( BaseHandle );
+ }
+ return;
+
+}
+
+
+VOID
+SetDbflagInRegistry(
+ LPWSTR ServerName,
+ ULONG DbFlagValue
+ )
+/*++
+
+Routine Description:
+
+ Set the value DbFlagValue in the Netlogon service portion of the registry.
+
+Arguments:
+
+ ServerName - Name of the server to update
+
+ DbFlagValue - Value to set dbflag to.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ LONG RegStatus;
+ UCHAR AnsiDbFlag[20];
+ DWORD AnsiDbFlagLength;
+
+ HKEY BaseHandle = NULL;
+ HKEY ParmHandle = NULL;
+ LPSTR KeyName;
+#define NL_PARAM_KEY "SYSTEM\\CurrentControlSet\\Services\\Netlogon\\Parameters"
+
+ //
+ // Open the registry
+ //
+
+ RegStatus = RegConnectRegistryW( ServerName,
+ HKEY_LOCAL_MACHINE,
+ &BaseHandle);
+
+ if ( RegStatus != ERROR_SUCCESS ) {
+ printf( "Cannot connect to registy on " FORMAT_LPWSTR " ", ServerName );
+ PrintStatus( RegStatus );
+ goto Cleanup;
+ }
+
+
+ //
+ // Open the key for Netlogon\parameters.
+ //
+
+ KeyName = NL_PARAM_KEY;
+ RegStatus = ForceRegOpenKey(
+ BaseHandle,
+ KeyName,
+ KEY_SET_VALUE,
+ &ParmHandle );
+
+ if ( RegStatus != ERROR_SUCCESS ) {
+ printf( "Cannot open " NL_PARAM_KEY );
+ PrintStatus( RegStatus );
+ goto Cleanup;
+ }
+
+ //
+ // Set the DbFlag value into the registry.
+ //
+
+ AnsiDbFlagLength = sprintf( AnsiDbFlag, "0x%8.8lx", DbFlagValue );
+
+ RegStatus = RegSetValueExA( ParmHandle,
+ "DbFlag",
+ 0, // Reserved
+ REG_SZ,
+ AnsiDbFlag,
+ AnsiDbFlagLength + 1 );
+
+ if ( RegStatus != ERROR_SUCCESS ) {
+ printf( "Cannot Set %s:", KeyName );
+ PrintStatus( RegStatus );
+ goto Cleanup;
+ }
+
+ printf( "%s set to %s\n", KeyName, AnsiDbFlag );
+
+ //
+ // Be tidy.
+ //
+Cleanup:
+ if ( ParmHandle != NULL ) {
+ RegCloseKey( ParmHandle );
+ }
+ if ( BaseHandle != NULL ) {
+ RegCloseKey( BaseHandle );
+ }
+ return;
+
+}
+
+
+NET_API_STATUS
+UaspGetDomainId(
+ IN LPWSTR ServerName OPTIONAL,
+ OUT PSAM_HANDLE SamServerHandle OPTIONAL,
+ OUT PPOLICY_ACCOUNT_DOMAIN_INFO * AccountDomainInfo
+ )
+
+/*++
+
+Routine Description:
+
+ Return a domain ID of the account domain of a server.
+
+Arguments:
+
+ ServerName - A pointer to a string containing the name of the
+ Domain Controller (DC) to query. A NULL pointer
+ or string specifies the local machine.
+
+ SamServerHandle - Returns the SAM connection handle if the caller wants it.
+
+ DomainId - Receives a pointer to the domain ID.
+ Caller must deallocate buffer using NetpMemoryFree.
+
+Return Value:
+
+ Error code for the operation.
+
+--*/
+
+{
+ NET_API_STATUS NetStatus;
+ NTSTATUS Status;
+
+ SAM_HANDLE LocalSamHandle = NULL;
+
+ ACCESS_MASK LSADesiredAccess;
+ LSA_HANDLE LSAPolicyHandle = NULL;
+ OBJECT_ATTRIBUTES LSAObjectAttributes;
+
+ UNICODE_STRING ServerNameString;
+
+
+ //
+ // Connect to the SAM server
+ //
+
+ RtlInitUnicodeString( &ServerNameString, ServerName );
+
+ Status = SamConnect(
+ &ServerNameString,
+ &LocalSamHandle,
+ SAM_SERVER_LOOKUP_DOMAIN,
+ NULL);
+
+ if ( !NT_SUCCESS(Status)) {
+ printf( "UaspGetDomainId: Cannot connect to Sam %lX\n",Status );
+ LocalSamHandle = NULL;
+ NetStatus = NetpNtStatusToApiStatus( Status );
+ goto Cleanup;
+ }
+
+
+ //
+ // Open LSA to read account domain info.
+ //
+
+ //
+ // set desired access mask.
+ //
+
+ LSADesiredAccess = POLICY_VIEW_LOCAL_INFORMATION;
+
+ InitializeObjectAttributes( &LSAObjectAttributes,
+ NULL, // Name
+ 0, // Attributes
+ NULL, // Root
+ NULL ); // Security Descriptor
+
+ Status = LsaOpenPolicy( &ServerNameString,
+ &LSAObjectAttributes,
+ LSADesiredAccess,
+ &LSAPolicyHandle );
+
+ if( !NT_SUCCESS(Status) ) {
+
+ printf( "UaspGetDomainId: Cannot open LSA Policy %lX\n", Status );
+
+ NetStatus = NetpNtStatusToApiStatus( Status );
+ goto Cleanup;
+ }
+
+
+ //
+ // now read account domain info from LSA.
+ //
+
+ Status = LsaQueryInformationPolicy(
+ LSAPolicyHandle,
+ PolicyAccountDomainInformation,
+ (PVOID *) AccountDomainInfo );
+
+ if( !NT_SUCCESS(Status) ) {
+
+ printf( "UaspGetDomainId: "
+ "Cannot read LSA %lX\n", Status );
+
+ NetStatus = NetpNtStatusToApiStatus( Status );
+ goto Cleanup;
+ }
+
+ //
+ // Return the SAM connection handle to the caller if he wants it.
+ // Otherwise, disconnect from SAM.
+ //
+
+ if ( ARGUMENT_PRESENT( SamServerHandle ) ) {
+ *SamServerHandle = LocalSamHandle;
+ LocalSamHandle = NULL;
+ }
+
+ NetStatus = NERR_Success;
+
+
+ //
+ // Cleanup locally used resources
+ //
+Cleanup:
+ if ( LocalSamHandle != NULL ) {
+ (VOID) SamCloseHandle( LocalSamHandle );
+ }
+
+ if( LSAPolicyHandle != NULL ) {
+ LsaClose( LSAPolicyHandle );
+ }
+
+ return NetStatus;
+
+} // UaspGetDomainId
+
+
+NET_API_STATUS
+UaspOpenDomain(
+ IN LPWSTR ServerName OPTIONAL,
+ IN ULONG DesiredAccess,
+ OUT PSAM_HANDLE DomainHandle,
+ OUT PSID *DomainId OPTIONAL
+ )
+
+/*++
+
+Routine Description:
+
+ Return a SAM Connection handle and a domain handle given the server
+ name and the access desired to the domain.
+
+ Only a single thread in a process can open a domain at a time.
+ Subsequent threads block in this routine. This exclusive access allows
+ a single SAM connection handle to be cached. The routine
+ UaspCloseDomain closes the domain and allows other threads to proceed.
+
+Arguments:
+
+ ServerName - A pointer to a string containing the name of the remote
+ server containing the SAM database. A NULL pointer
+ or string specifies the local machine.
+
+ DesiredAccess - Supplies the access mask indicating which access types
+ are desired to the domain. This routine always requests DOMAIN_LOOKUP
+ access in addition to those specified.
+
+ DomainHandle - Receives the Domain handle to be used on future calls
+ to the SAM server.
+
+ DomainId - Recieves a pointer to the Sid of the domain. This domain ID
+ must be freed using NetpMemoryFree.
+
+Return Value:
+
+ Error code for the operation. NULL means initialization was successful.
+
+--*/
+
+{
+
+ NET_API_STATUS NetStatus;
+ NTSTATUS Status;
+ PSID LocalDomainId;
+ HANDLE SamServerHandle;
+ PPOLICY_ACCOUNT_DOMAIN_INFO AccountDomainInfo;
+
+ //
+ // Give everyone DOMAIN_LOOKUP access.
+ //
+
+ DesiredAccess |= DOMAIN_LOOKUP;
+
+ if ( ServerName == NULL ) {
+ ServerName = L"";
+ }
+
+ if ( *ServerName != L'\0' &&
+ (ServerName[0] != L'\\' || ServerName[1] != L'\\') ) {
+ return NERR_InvalidComputer;
+ }
+
+ //
+ // Connect to the SAM server and
+ // Determine the Domain Id of the account domain for this server.
+ //
+
+ NetStatus = UaspGetDomainId( ServerName,
+ &SamServerHandle,
+ &AccountDomainInfo );
+
+ if ( NetStatus != NERR_Success ) {
+ printf( "UaspUpdateCache: Cannot UaspGetDomainId %ld\n",
+ NetStatus );
+ return ( NetStatus );
+ }
+
+
+ //
+ // Choose the domain ID for the right SAM domain.
+ //
+
+ LocalDomainId = AccountDomainInfo->DomainSid;
+
+ //
+ // At this point the domain ID of the account domain of the server is
+ // cached. Open the domain.
+ //
+
+ Status = SamOpenDomain( SamServerHandle,
+ DesiredAccess,
+ LocalDomainId,
+ DomainHandle );
+
+ if ( !NT_SUCCESS( Status ) ) {
+
+ printf( "UaspOpenDomain: Cannot SamOpenDomain %lX\n", Status );
+ *DomainHandle = NULL;
+ return NetpNtStatusToApiStatus( Status );
+ }
+
+ //
+ // Return the DomainId to the caller in an allocated buffer
+ //
+
+ if (ARGUMENT_PRESENT( DomainId ) ) {
+ ULONG SidSize;
+ SidSize = RtlLengthSid( LocalDomainId );
+
+ *DomainId = NetpMemoryAllocate( SidSize );
+
+ if ( *DomainId == NULL ) {
+ (VOID) SamCloseHandle( *DomainHandle );
+ *DomainHandle = NULL;
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ if ( !NT_SUCCESS( RtlCopySid( SidSize, *DomainId, LocalDomainId) ) ) {
+ (VOID) SamCloseHandle( *DomainHandle );
+ *DomainHandle = NULL;
+ NetpMemoryFree( *DomainId );
+ *DomainId = NULL;
+ return NERR_InternalError;
+ }
+
+ }
+
+ return NERR_Success;
+
+} // UaspOpenDomain
+
+VOID
+SetLockout(
+ IN LPWSTR ServerName,
+ IN ULONG LockoutThreshold,
+ IN ULONG LockoutDuration,
+ IN ULONG LockoutWindow
+ )
+/*++
+
+Routine Description:
+
+ Set the lockout parameter on a domain.
+
+Arguments:
+
+Return Value:
+
+ Exit status
+
+--*/
+{
+ NTSTATUS Status;
+ NET_API_STATUS NetStatus;
+ DOMAIN_LOCKOUT_INFORMATION LockoutInfo;
+ HANDLE DomainHandle;
+
+
+ NetStatus = UaspOpenDomain(
+ ServerName,
+ DOMAIN_WRITE_PASSWORD_PARAMS,
+ &DomainHandle,
+ NULL );
+
+ if ( NetStatus != NERR_Success ) {
+ printf( "UaspOpenDomain failed %ld\n", NetStatus );
+ return;
+ }
+
+ //
+ // Fill in the info level structure
+ //
+
+ LockoutInfo.LockoutThreshold = (USHORT) LockoutThreshold;
+ // Convert times from seconds to 100ns
+ LockoutInfo.LockoutDuration =
+ RtlEnlargedIntegerMultiply( LockoutDuration, -10000000 );
+ LockoutInfo.LockoutObservationWindow =
+ RtlEnlargedIntegerMultiply( LockoutWindow, -10000000 );
+
+ //
+ // Set the information in SAM
+ //
+
+ Status = SamSetInformationDomain( DomainHandle,
+ DomainLockoutInformation,
+ &LockoutInfo );
+
+ if ( !NT_SUCCESS(Status) ) {
+ printf( "Can't SamSetInformationDomain 0x%lx\n", Status );
+ }
+
+
+}
+
+int _CRTAPI1
+main(
+ IN int argc,
+ IN char ** argv
+ )
+/*++
+
+Routine Description:
+
+ Drive the Netlogon service by calling I_NetLogonControl2.
+
+Arguments:
+
+ argc - the number of command-line arguments.
+
+ argv - an array of pointers to the arguments.
+
+Return Value:
+
+ Exit status
+
+--*/
+{
+ NTSTATUS Status;
+ NET_API_STATUS NetStatus;
+
+ LPSTR argument;
+ int i;
+ DWORD FunctionCode = 0;
+ LPSTR AnsiServerName = NULL;
+ CHAR AnsiUncServerName[UNCLEN+1];
+ LPSTR AnsiDomainName = NULL;
+ LPSTR AnsiTrustedDomainName = NULL;
+ LPWSTR TrustedDomainName = NULL;
+ LPSTR AnsiUserName = NULL;
+ LPSTR AnsiPassword = NULL;
+ LPSTR AnsiSimMachineName = NULL;
+ LPSTR AnsiDeltaFileName = NULL;
+ LPSTR ShutdownReason = NULL;
+ LPWSTR ServerName = NULL;
+ LPWSTR UserName = NULL;
+ PNETLOGON_INFO_1 NetlogonInfo1 = NULL;
+ ULONG Rid = 0;
+ DWORD Level = 1;
+ DWORD ShutdownSeconds;
+ LPBYTE InputDataPtr = NULL;
+
+ DWORD DbFlagValue;
+
+ LARGE_INTEGER ConvertTime;
+ ULONG IterationCount;
+
+ NT_OWF_PASSWORD NtOwfPassword;
+ BOOLEAN NtPasswordPresent = FALSE;
+ LM_OWF_PASSWORD LmOwfPassword;
+ BOOLEAN LmPasswordPresent = FALSE;
+ BOOLEAN GetPdcName = FALSE;
+ BOOLEAN GetTrustedDcName = FALSE;
+ BOOLEAN GetDcList = FALSE;
+ BOOLEAN WhoWill = FALSE;
+ BOOLEAN QuerySync = FALSE;
+ BOOLEAN SimFullSync = FALSE;
+ BOOLEAN QueryUser = FALSE;
+ BOOLEAN ListDeltasFlag = FALSE;
+ BOOLEAN ListRedoFlag = FALSE;
+ BOOLEAN ResetSecureChannelsFlag = FALSE;
+ BOOLEAN ShutdownAbort = FALSE;
+ BOOLEAN TrustedDomainsFlag = FALSE;
+
+ BOOLEAN DoLockout = FALSE;
+ ULONG LockoutThreshold;
+ ULONG LockoutDuration;
+ ULONG LockoutWindow;
+
+
+#define QUERY_PARAM "/QUERY"
+#define REPL_PARAM "/REPL"
+#define SYNC_PARAM "/SYNC"
+#define PDC_REPL_PARAM "/PDC_REPL"
+#define SERVER_PARAM "/SERVER:"
+#define PWD_PARAM "/PWD:"
+#define RID_PARAM "/RID:"
+#define USER_PARAM "/USER:"
+#define BP_PARAM "/BP"
+#define DBFLAG_PARAM "/DBFLAG:"
+#define DCLIST_PARAM "/DCLIST:"
+#define DCNAME_PARAM "/DCNAME:"
+#define DCTRUST_PARAM "/DCTRUST:"
+#define TRUNCATE_LOG_PARAM "/TRUNC"
+#define BACKUP_CHANGE_LOG_PARAM "/BKP_CHK"
+#define TIME_PARAM "/TIME:"
+#define WHOWILL_PARAM "/WHOWILL:"
+#define BDC_QUERY_PARAM "/BDC_QUERY:"
+#define LOGON_QUERY_PARAM "/LOGON_QUERY"
+#define SIM_SYNC_PARAM "/SIM_SYNC:"
+#define LIST_DELTAS_PARAM "/LIST_DELTAS:"
+#define LIST_REDO_PARAM "/LIST_REDO:"
+#define SC_RESET_PARAM "/SC_RESET:"
+#define SC_QUERY_PARAM "/SC_QUERY:"
+#define SHUTDOWN_PARAM "/SHUTDOWN:"
+#define SHUTDOWN_ABORT_PARAM "/SHUTDOWN_ABORT"
+#define LOCKOUT_PARAM "/LOCKOUT:"
+#define TRANSPORT_PARAM "/TRANSPORT_NOTIFY"
+#define FINDUSER_PARAM "/FINDUSER:"
+#define TRUSTED_DOMAINS_PARAM "/TRUSTED_DOMAINS"
+
+ //
+ // Set the netlib debug flag.
+ //
+ extern DWORD NetlibpTrace;
+ NetlibpTrace |= 0x8000; // NETLIB_DEBUG_LOGON
+
+ ConvertTime.QuadPart = 0;
+
+
+ //
+ // Loop through the arguments handle each in turn
+ //
+
+ for ( i=1; i<argc; i++ ) {
+
+ argument = argv[i];
+
+
+ //
+ // Handle /QUERY
+ //
+
+ if ( _stricmp( argument, QUERY_PARAM ) == 0 ) {
+ if ( FunctionCode != 0 ) {
+ goto Usage;
+ }
+
+ FunctionCode = NETLOGON_CONTROL_QUERY;
+
+
+ //
+ // Handle /SC_QUERY
+ //
+
+ } else if ( _strnicmp( argument,
+ SC_QUERY_PARAM,
+ sizeof(SC_QUERY_PARAM) - 1 ) == 0 ) {
+ if ( FunctionCode != 0 ) {
+ goto Usage;
+ }
+
+ FunctionCode = NETLOGON_CONTROL_TC_QUERY;
+
+ AnsiTrustedDomainName = &argument[sizeof(SC_QUERY_PARAM)-1];
+
+ TrustedDomainName = NetpAllocWStrFromStr( AnsiTrustedDomainName );
+
+ if ( TrustedDomainName == NULL ) {
+ fprintf( stderr, "Not enough memory\n" );
+ return(1);
+ }
+
+ Level = 2;
+ InputDataPtr = (LPBYTE)TrustedDomainName;
+
+
+ //
+ // Handle /FINDUSER
+ //
+
+ } else if ( _strnicmp( argument,
+ FINDUSER_PARAM,
+ sizeof(FINDUSER_PARAM) - 1 ) == 0 ) {
+ if ( FunctionCode != 0 ) {
+ goto Usage;
+ }
+
+ FunctionCode = NETLOGON_CONTROL_FIND_USER;
+
+ AnsiUserName = &argument[sizeof(FINDUSER_PARAM)-1];
+
+ TrustedDomainName = NetpAllocWStrFromStr( AnsiUserName );
+
+ if ( TrustedDomainName == NULL ) {
+ fprintf( stderr, "Not enough memory\n" );
+ return(1);
+ }
+
+ Level = 4;
+ InputDataPtr = (LPBYTE)TrustedDomainName;
+
+ //
+ // Handle /REPL
+ //
+
+ } else if (_stricmp(argument, REPL_PARAM ) == 0 ){
+ if ( FunctionCode != 0 ) {
+ goto Usage;
+ }
+
+ FunctionCode = NETLOGON_CONTROL_REPLICATE;
+
+
+ //
+ // Handle /SYNC
+ //
+
+ } else if (_stricmp(argument, SYNC_PARAM ) == 0 ){
+ if ( FunctionCode != 0 ) {
+ goto Usage;
+ }
+
+ FunctionCode = NETLOGON_CONTROL_SYNCHRONIZE;
+
+
+ //
+ // Handle /SC_RESET
+ //
+
+ } else if (_strnicmp(argument,
+ SC_RESET_PARAM,
+ sizeof(SC_RESET_PARAM) - 1 ) == 0 ){
+ if ( FunctionCode != 0 ) {
+ goto Usage;
+ }
+
+ FunctionCode = NETLOGON_CONTROL_REDISCOVER;
+ AnsiTrustedDomainName = &argument[sizeof(SC_RESET_PARAM)-1];
+
+ TrustedDomainName = NetpAllocWStrFromStr( AnsiTrustedDomainName );
+
+ if ( TrustedDomainName == NULL ) {
+ fprintf( stderr, "Not enough memory\n" );
+ return(1);
+ }
+
+ Level = 2;
+ InputDataPtr = (LPBYTE)TrustedDomainName;
+
+ //
+ // Handle /QUERY
+ //
+
+ } else if ( _stricmp( argument, TRANSPORT_PARAM ) == 0 ) {
+ if ( FunctionCode != 0 ) {
+ goto Usage;
+ }
+
+ FunctionCode = NETLOGON_CONTROL_TRANSPORT_NOTIFY;
+
+
+ //
+ // Handle /PDC_REPL
+ //
+
+ } else if (_stricmp(argument, PDC_REPL_PARAM ) == 0 ){
+ if ( FunctionCode != 0 ) {
+ goto Usage;
+ }
+
+ FunctionCode = NETLOGON_CONTROL_PDC_REPLICATE;
+
+
+ //
+ // Handle /BP
+ //
+
+ } else if (_stricmp(argument, BP_PARAM ) == 0 ){
+ if ( FunctionCode != 0 ) {
+ goto Usage;
+ }
+
+ FunctionCode = NETLOGON_CONTROL_BREAKPOINT;
+
+ //
+ // Handle /TRUNCATE_LOG
+ //
+
+ } else if (_stricmp(argument, TRUNCATE_LOG_PARAM ) == 0 ){
+ if ( FunctionCode != 0 ) {
+ goto Usage;
+ }
+
+ FunctionCode = NETLOGON_CONTROL_TRUNCATE_LOG;
+
+
+ //
+ // Handle /BKP_CHK
+ //
+
+ } else if (_stricmp(argument, BACKUP_CHANGE_LOG_PARAM ) == 0 ){
+ if ( FunctionCode != 0 ) {
+ goto Usage;
+ }
+
+ FunctionCode = NETLOGON_CONTROL_BACKUP_CHANGE_LOG;
+
+
+ //
+ // Handle /DBFLAG:dbflag
+ //
+
+ } else if (_strnicmp(argument,
+ DBFLAG_PARAM,
+ sizeof(DBFLAG_PARAM)-1 ) == 0 ){
+ char *end;
+
+ if ( FunctionCode != 0 ) {
+ goto Usage;
+ }
+
+ FunctionCode = NETLOGON_CONTROL_SET_DBFLAG;
+
+ DbFlagValue = strtoul( &argument[sizeof(DBFLAG_PARAM)-1], &end, 16 );
+ InputDataPtr = (LPBYTE)DbFlagValue;
+
+ //
+ // Handle /Time:LSL MSL
+ //
+
+ } else if (_strnicmp(argument,
+ TIME_PARAM,
+ sizeof(TIME_PARAM)-1 ) == 0 ){
+ char *end;
+
+ if ( ConvertTime.QuadPart != 0 ) {
+ goto Usage;
+ }
+
+ ConvertTime.LowPart = strtoul( &argument[sizeof(TIME_PARAM)-1], &end, 16 );
+ i++;
+ argument = argv[i];
+
+ ConvertTime.HighPart = strtoul( argument, &end, 16 );
+
+
+ //
+ // Handle /WHOWILL:Domain User [IterationCount]
+ //
+
+ } else if (_strnicmp(argument,
+ WHOWILL_PARAM,
+ sizeof(WHOWILL_PARAM)-1 ) == 0 ){
+ char *end;
+
+ if ( AnsiDomainName != NULL ) {
+ goto Usage;
+ }
+
+ AnsiDomainName = &argument[sizeof(WHOWILL_PARAM)-1];
+
+ i++;
+ argument = argv[i];
+ AnsiUserName = argument;
+
+ if ( i+1 < argc ) {
+ i++;
+ argument = argv[i];
+
+ IterationCount = strtoul( argument, &end, 16 );
+ } else {
+ IterationCount = 1;
+ }
+
+ WhoWill = TRUE;
+
+
+ //
+ // Handle /LOCKOUT:Threshold Duration Window
+ //
+
+ } else if (_strnicmp(argument,
+ LOCKOUT_PARAM,
+ sizeof(LOCKOUT_PARAM)-1 ) == 0 ){
+
+ char *end;
+
+ LockoutThreshold = strtoul( &argument[sizeof(LOCKOUT_PARAM)-1], &end, 10 );
+ i++;
+ argument = argv[i];
+
+ LockoutDuration = strtoul( argument, &end, 10 );
+ i++;
+ argument = argv[i];
+
+ LockoutWindow = strtoul( argument, &end, 10 );
+ DoLockout = TRUE;
+
+
+ //
+ // Handle /BDC_QUERY:Domain
+ //
+
+ } else if (_strnicmp(argument,
+ BDC_QUERY_PARAM,
+ sizeof(BDC_QUERY_PARAM)-1 ) == 0 ){
+
+ if ( AnsiDomainName != NULL ) {
+ goto Usage;
+ }
+
+ AnsiDomainName = &argument[sizeof(BDC_QUERY_PARAM)-1];
+ QuerySync = TRUE;
+
+ //
+ // Handle /LOGON_QUERY
+ //
+
+ } else if ( _stricmp( argument, LOGON_QUERY_PARAM ) == 0 ) {
+ if ( FunctionCode != 0 ) {
+ goto Usage;
+ }
+
+ FunctionCode = NETLOGON_CONTROL_QUERY;
+ Level = 3;
+
+ //
+ // Handle full sync simulation
+ //
+
+ } else if (_strnicmp(argument,
+ SIM_SYNC_PARAM,
+ sizeof(SIM_SYNC_PARAM)-1 ) == 0 ){
+
+ if ( AnsiDomainName != NULL ) {
+ goto Usage;
+ }
+
+ AnsiDomainName = &argument[sizeof(SIM_SYNC_PARAM)-1];
+
+ i++;
+
+ if( i >= argc ) {
+ goto Usage;
+ }
+
+ argument = argv[i];
+ AnsiSimMachineName = argument;
+
+ SimFullSync = TRUE;
+
+ //
+ // handle delta listing
+ //
+
+ } else if (_strnicmp(argument,
+ LIST_DELTAS_PARAM,
+ sizeof(LIST_DELTAS_PARAM)-1 ) == 0 ){
+
+ if ( AnsiDeltaFileName != NULL ) {
+ goto Usage;
+ }
+
+ AnsiDeltaFileName = &argument[sizeof(LIST_DELTAS_PARAM)-1];
+
+ ListDeltasFlag = TRUE;
+
+ //
+ // handle redo listing
+ //
+
+ } else if (_strnicmp(argument,
+ LIST_REDO_PARAM,
+ sizeof(LIST_REDO_PARAM)-1 ) == 0 ){
+
+ if ( AnsiDeltaFileName != NULL ) {
+ goto Usage;
+ }
+
+ AnsiDeltaFileName = &argument[sizeof(LIST_REDO_PARAM)-1];
+
+ ListRedoFlag = TRUE;
+
+ //
+ // Handle /DCLIST
+ //
+
+ } else if (_strnicmp(argument,
+ DCLIST_PARAM,
+ sizeof(DCLIST_PARAM)-1 ) == 0 ){
+
+ if ( AnsiDomainName != NULL ) {
+ goto Usage;
+ }
+
+ AnsiDomainName = &argument[sizeof(DCLIST_PARAM)-1];
+ GetDcList = TRUE;
+
+ //
+ // Handle /DCNAME
+ //
+
+ } else if (_strnicmp(argument,
+ DCNAME_PARAM,
+ sizeof(DCNAME_PARAM)-1 ) == 0 ){
+
+ if ( AnsiDomainName != NULL ) {
+ goto Usage;
+ }
+
+ AnsiDomainName = &argument[sizeof(DCNAME_PARAM)-1];
+ GetPdcName = TRUE;
+
+ //
+ // Handle /DCTRUST
+ //
+
+ } else if (_strnicmp(argument,
+ DCTRUST_PARAM,
+ sizeof(DCTRUST_PARAM)-1 ) == 0 ){
+
+ if ( AnsiDomainName != NULL ) {
+ goto Usage;
+ }
+
+ AnsiDomainName = &argument[sizeof(DCTRUST_PARAM)-1];
+ GetTrustedDcName = TRUE;
+
+
+ //
+ // Handle /SERVER:servername
+ //
+
+ } else if (_strnicmp(argument, SERVER_PARAM, sizeof(SERVER_PARAM)-1 ) == 0 ){
+ if ( AnsiServerName != NULL ) {
+ goto Usage;
+ }
+
+ AnsiServerName = &argument[sizeof(SERVER_PARAM)-1];
+
+
+ //
+ // Handle /PWD:password
+ //
+
+ } else if (_strnicmp(argument, PWD_PARAM, sizeof(PWD_PARAM)-1 ) == 0 ){
+ if ( AnsiPassword != NULL ) {
+ goto Usage;
+ }
+
+ AnsiPassword = &argument[sizeof(PWD_PARAM)-1];
+
+
+ //
+ // Handle /USER:username
+ //
+
+ } else if (_strnicmp(argument, USER_PARAM, sizeof(USER_PARAM)-1 ) == 0 ){
+ if ( AnsiUserName != NULL ) {
+ goto Usage;
+ }
+
+ AnsiUserName = &argument[sizeof(USER_PARAM)-1];
+ QueryUser = TRUE;
+
+
+ //
+ // Handle /RID:relative_id
+ //
+
+ } else if (_strnicmp(argument, RID_PARAM, sizeof(RID_PARAM)-1 ) == 0 ){
+ char *end;
+
+ if ( Rid != 0 ) {
+ goto Usage;
+ }
+
+ Rid = strtol( &argument[sizeof(RID_PARAM)-1], &end, 16 );
+
+ //
+ // Handle /SHUTDOWN:Reason seconds
+ //
+
+ } else if (_strnicmp(argument,
+ SHUTDOWN_PARAM,
+ sizeof(SHUTDOWN_PARAM)-1 ) == 0 ){
+
+ if ( ShutdownReason != NULL ) {
+ goto Usage;
+ }
+
+ ShutdownReason = &argument[sizeof(SHUTDOWN_PARAM)-1];
+
+ if ( i+1 < argc ) {
+ char *end;
+ i++;
+ argument = argv[i];
+ if ( !ISDIGIT(argument[0]) ) {
+ fprintf(stderr, "Second argument to " SHUTDOWN_PARAM " must be a number.\n\n");
+ goto Usage;
+ }
+ ShutdownSeconds = strtoul( argument, &end, 10 );
+ } else {
+ ShutdownSeconds = 60;
+ }
+
+
+ //
+ // Handle /SHUTDOWN_ABORT
+ //
+
+ } else if (_stricmp(argument, SHUTDOWN_ABORT_PARAM ) == 0 ){
+
+ ShutdownAbort = TRUE;
+
+ //
+ // Handle /TRUSTED_DOMAINS
+ //
+
+ } else if (_stricmp(argument, TRUSTED_DOMAINS_PARAM ) == 0 ){
+
+ TrustedDomainsFlag = TRUE;
+
+
+ //
+ // Handle all other parameters
+ //
+
+ } else {
+Usage:
+ fprintf( stderr, "Usage: nltest [/OPTIONS]\n\n" );
+
+ fprintf(
+ stderr,
+ "\n"
+ " " SERVER_PARAM "<ServerName> - Specify <ServerName>\n"
+ "\n"
+ " " QUERY_PARAM " - Query <ServerName> netlogon service\n"
+ " " REPL_PARAM " - Force replication on <ServerName> BDC\n"
+ " " SYNC_PARAM " - Force SYNC on <ServerName> BDC\n"
+ " " PDC_REPL_PARAM " - Force UAS change message from <ServerName> PDC\n"
+ "\n"
+ " " SC_QUERY_PARAM "<DomainName> - Query secure channel for <Domain> on <ServerName>\n"
+ " " SC_RESET_PARAM "<DomainName> - Reset secure channel for <Domain> on <ServerName>\n"
+ " " DCLIST_PARAM "<DomainName> - Get list of DC's for <DomainName>\n"
+ " " DCNAME_PARAM "<DomainName> - Get the PDC name for <DomainName>\n"
+ " " DCTRUST_PARAM "<DomainName> - Get name of DC is used for trust of <DomainName>\n"
+ " " WHOWILL_PARAM "<Domain>* <User> [<Iteration>] - See if <Domain> will log on <User>\n"
+ " " FINDUSER_PARAM "<User> - See which trusted <Domain> will log on <User>\n"
+ " " TRANSPORT_PARAM " - Notify of netlogon of new transport\n"
+ "\n"
+ " " BP_PARAM " - Force a BreakPoint in Netlogon on <ServerName>\n"
+ " " DBFLAG_PARAM "<HexFlags> - New debug flag\n"
+ " " TRUNCATE_LOG_PARAM " - Truncate log file (rename to *.bak)\n"
+ "\n"
+ " " PWD_PARAM "<CleartextPassword> - Specify Password to encrypt\n"
+ " " RID_PARAM "<HexRid> - RID to encrypt Password with\n"
+ " " USER_PARAM "<UserName> - Query User info on <ServerName>\n"
+ "\n"
+ " " TIME_PARAM "<Hex LSL> <Hex MSL> - Convert NT GMT time to ascii\n"
+ " " LOCKOUT_PARAM "<Thresh> <Duration> <Window> - set lockout parameters on a domain\n"
+ " " LOGON_QUERY_PARAM " - Query number of cumulative logon attempts\n"
+ " " TRUSTED_DOMAINS_PARAM " - Query names of domains trusted by workstation\n"
+ "\n"
+ " " BDC_QUERY_PARAM "<DomainName> - Query replication status of BDCs for <DomainName>\n"
+ " " SIM_SYNC_PARAM "<DomainName> <MachineName> - Simulate full sync replication\n"
+ "\n"
+ " " BACKUP_CHANGE_LOG_PARAM " - Backup Change log file (copy to netlogon.bkp)\n"
+ " " LIST_DELTAS_PARAM "<FileName> - display the content of given change log file \n"
+ " " LIST_REDO_PARAM "<FileName> - display the content of given redo log file \n"
+ "\n"
+ " " SHUTDOWN_PARAM "<Reason> [<Seconds>] - Shutdown <ServerName> for <Reason>\n"
+ " " SHUTDOWN_ABORT_PARAM " - Abort a system shutdown\n"
+ "\n" );
+ return(1);
+ }
+ }
+
+
+ //
+ // Convert the server name to unicode.
+ //
+
+ if ( AnsiServerName != NULL ) {
+ if ( AnsiServerName[0] == '\\' && AnsiServerName[1] == '\\' ) {
+ ServerName = NetpAllocWStrFromStr( AnsiServerName );
+ } else {
+ AnsiUncServerName[0] = '\\';
+ AnsiUncServerName[1] = '\\';
+ strcpy(AnsiUncServerName+2, AnsiServerName);
+ ServerName = NetpAllocWStrFromStr( AnsiUncServerName );
+ AnsiServerName = AnsiUncServerName;
+ }
+ }
+
+ //
+ // Convert the user name to unicode.
+ //
+
+ if ( AnsiUserName != NULL ) {
+
+ UserName = NetpAllocWStrFromStr( AnsiUserName );
+
+ if ( UserName == NULL ) {
+ fprintf( stderr, "Not enough memory\n" );
+ return(1);
+ }
+ }
+
+
+ //
+ // If we've been asked to contact the Netlogon service,
+ // Do so
+ //
+
+ if ( FunctionCode != 0 ) {
+
+
+ //
+ // The dbflag should be set in the registry as well as in netlogon
+ // proper.
+ //
+
+ if ( FunctionCode == NETLOGON_CONTROL_SET_DBFLAG ) {
+ SetDbflagInRegistry( ServerName, DbFlagValue );
+ }
+
+ NetStatus = I_NetLogonControl2( ServerName,
+ FunctionCode,
+ Level,
+ (LPBYTE) &InputDataPtr,
+ (LPBYTE *)&NetlogonInfo1 );
+
+ if ( NetStatus != NERR_Success ) {
+ fprintf( stderr, "I_NetLogonControl failed: " );
+ PrintStatus( NetStatus );
+ return(1);
+ }
+
+ if( (Level == 1) || (Level == 2) ) {
+
+ //
+ // Print level 1 information
+ //
+
+ printf( "Flags: %lx", NetlogonInfo1->netlog1_flags );
+
+ if ( NetlogonInfo1->netlog1_flags & NETLOGON_REPLICATION_IN_PROGRESS ) {
+
+ if ( NetlogonInfo1->netlog1_flags & NETLOGON_FULL_SYNC_REPLICATION ) {
+ printf( " FULL_SYNC " );
+ }
+ else {
+ printf( " PARTIAL_SYNC " );
+ }
+
+ printf( " REPLICATION_IN_PROGRESS" );
+ }
+ else if ( NetlogonInfo1->netlog1_flags & NETLOGON_REPLICATION_NEEDED ) {
+
+ if ( NetlogonInfo1->netlog1_flags & NETLOGON_FULL_SYNC_REPLICATION ) {
+ printf( " FULL_SYNC " );
+ }
+ else {
+ printf( " PARTIAL_SYNC " );
+ }
+
+ printf( " REPLICATION_NEEDED" );
+ }
+ if ( NetlogonInfo1->netlog1_flags & NETLOGON_REDO_NEEDED) {
+ printf( " REDO_NEEDED" );
+ }
+ printf( "\n" );
+
+ printf( "Connection ");
+ PrintStatus( NetlogonInfo1->netlog1_pdc_connection_status );
+ }
+
+ if( Level == 2 ) {
+
+ //
+ // Print level 2 only information
+ //
+
+ PNETLOGON_INFO_2 NetlogonInfo2;
+
+ NetlogonInfo2 = (PNETLOGON_INFO_2)NetlogonInfo1;
+
+ printf("Trusted DC Name %ws \n",
+ NetlogonInfo2->netlog2_trusted_dc_name );
+ printf("Trusted DC Connection Status ");
+ PrintStatus( NetlogonInfo2->netlog2_tc_connection_status );
+ }
+ if ( Level == 3 ) {
+ printf( "Number of attempted logons: %ld\n",
+ ((PNETLOGON_INFO_3)NetlogonInfo1)->netlog3_logon_attempts );
+ }
+ if( Level == 4 ) {
+
+ PNETLOGON_INFO_4 NetlogonInfo4;
+
+ NetlogonInfo4 = (PNETLOGON_INFO_4)NetlogonInfo1;
+
+ printf("Domain Name: %ws\n",
+ NetlogonInfo4->netlog4_trusted_domain_name );
+ printf("Trusted DC Name %ws \n",
+ NetlogonInfo4->netlog4_trusted_dc_name );
+ }
+
+ }
+
+ //
+ // If we've been asked to debug password encryption,
+ // do so.
+ //
+
+ if ( AnsiPassword != NULL ) {
+ LPWSTR Password = NULL;
+ UNICODE_STRING UnicodePasswordString;
+ STRING AnsiPasswordString;
+ CHAR LmPasswordBuffer[LM20_PWLEN + 1];
+
+ Password = NetpAllocWStrFromStr( AnsiPassword );
+ RtlInitUnicodeString( &UnicodePasswordString, Password );
+
+
+ //
+ // Compute the NT One-Way-Function of the password
+ //
+
+ Status = RtlCalculateNtOwfPassword( &UnicodePasswordString,
+ &NtOwfPassword );
+ if ( !NT_SUCCESS(Status) ) {
+ fprintf( stderr, "RtlCalculateNtOwfPassword failed: 0x%lx", Status);
+ return(1);
+ }
+
+ printf( "NT OWF Password for: %s ", AnsiPassword );
+ DumpBuffer( &NtOwfPassword, sizeof( NtOwfPassword ));
+ printf("\n");
+ NtPasswordPresent = TRUE;
+
+
+
+ //
+ // Compute the Ansi version to the Cleartext password.
+ //
+ // The Ansi version of the Cleartext password is at most 14 bytes long,
+ // exists in a trailing zero filled 15 byte buffer,
+ // is uppercased.
+ //
+
+ AnsiPasswordString.Buffer = LmPasswordBuffer;
+ AnsiPasswordString.MaximumLength = sizeof(LmPasswordBuffer);
+
+ RtlZeroMemory( LmPasswordBuffer, sizeof(LmPasswordBuffer) );
+
+ Status = RtlUpcaseUnicodeStringToOemString(
+ &AnsiPasswordString,
+ &UnicodePasswordString,
+ FALSE );
+
+ if ( !NT_SUCCESS(Status) ) {
+
+ RtlZeroMemory( LmPasswordBuffer, sizeof(LmPasswordBuffer) );
+ Status = STATUS_SUCCESS;
+
+ printf( "LM OWF Password for: %s\n", AnsiPassword );
+ printf( " ----- Password doesn't translate from unicode ----\n");
+ LmPasswordPresent = FALSE;
+
+ } else {
+
+ Status = RtlCalculateLmOwfPassword(
+ LmPasswordBuffer,
+ &LmOwfPassword);
+ printf( "LM OWF Password for: %s ", AnsiPassword );
+ DumpBuffer( &LmOwfPassword, sizeof( LmOwfPassword ));
+ printf("\n");
+ LmPasswordPresent = TRUE;
+ }
+
+ }
+
+ //
+ // If we've been given a Rid,
+ // use it to further encrypt the password
+ //
+
+ if ( Rid != 0 ) {
+ ENCRYPTED_LM_OWF_PASSWORD EncryptedLmOwfPassword;
+ ENCRYPTED_NT_OWF_PASSWORD EncryptedNtOwfPassword;
+
+ if ( NtPasswordPresent ) {
+
+ Status = RtlEncryptNtOwfPwdWithIndex(
+ &NtOwfPassword,
+ &Rid,
+ &EncryptedNtOwfPassword
+ );
+
+ printf( "NT OWF Password encrypted by: 0x%lx ", Rid );
+ if ( NT_SUCCESS( Status ) ) {
+ DumpBuffer( &EncryptedNtOwfPassword,sizeof(EncryptedNtOwfPassword));
+ printf("\n");
+ } else {
+ printf( "RtlEncryptNtOwfPwdWithIndex returns 0x%lx\n", Status );
+ }
+ }
+
+ if ( LmPasswordPresent ) {
+
+ Status = RtlEncryptLmOwfPwdWithIndex(
+ &LmOwfPassword,
+ &Rid,
+ &EncryptedLmOwfPassword
+ );
+
+ printf( "LM OWF Password encrypted by: 0x%lx ", Rid );
+ if ( NT_SUCCESS( Status ) ) {
+ DumpBuffer( &EncryptedLmOwfPassword,sizeof(EncryptedLmOwfPassword));
+ printf("\n");
+ } else {
+ printf( "RtlEncryptNtOwfPwdWithIndex returns 0x%lx\n", Status );
+ }
+ }
+ }
+
+ //
+ // If we've been asked to query a user,
+ // do so.
+ //
+
+ if ( QueryUser ) {
+ PrintUserInfo( ServerName, AnsiUserName );
+ }
+
+ //
+ // If we've been asked to get the list of domain controllers,
+ // Do so
+ //
+
+ if ( AnsiDomainName != NULL ) {
+ LPWSTR DomainName;
+
+ DomainName = NetpAllocWStrFromStr( AnsiDomainName );
+
+ if ( DomainName == NULL ) {
+ fprintf( stderr, "Not enough memory\n" );
+ return(1);
+ }
+
+ if ( GetPdcName ) {
+ LPWSTR PdcName;
+
+ NetStatus = NetGetDCName(
+ ServerName,
+ DomainName,
+ (LPBYTE *)&PdcName );
+
+ if ( NetStatus != NERR_Success ) {
+ fprintf( stderr, "NetGetDCName failed: " );
+ PrintStatus( NetStatus );
+ return(1);
+ }
+
+ printf( "PDC for Domain " FORMAT_LPWSTR " is " FORMAT_LPWSTR "\n",
+ DomainName, PdcName );
+
+ } else if ( GetDcList ) {
+ DWORD DCCount;
+ PUNICODE_STRING DCNames;
+ DWORD i;
+
+ NetStatus = I_NetGetDCList(
+ ServerName,
+ DomainName,
+ &DCCount,
+ &DCNames );
+
+ if ( NetStatus != NERR_Success ) {
+ fprintf( stderr, "I_NetGetDCList failed: ");
+ PrintStatus( NetStatus );
+ return(1);
+ }
+
+ printf( "List of DCs in Domain " FORMAT_LPWSTR "\n", DomainName );
+ for (i=0; i<DCCount; i++ ) {
+ if ( DCNames[i].Length > 0 ) {
+ printf(" %wZ", &DCNames[i] );
+ } else {
+ printf(" NULL");
+ }
+ if ( i==0 ) {
+ printf( " (PDC)");
+ }
+ printf("\n");
+ }
+
+ } else if ( GetTrustedDcName ) {
+ LPWSTR TrustedDcName;
+
+ NetStatus = NetGetAnyDCName(
+ ServerName,
+ DomainName,
+ (LPBYTE *)&TrustedDcName );
+
+ if ( NetStatus != NERR_Success ) {
+ fprintf( stderr, "NetGetAnyDCName failed: ");
+ PrintStatus( NetStatus );
+ return(1);
+ }
+
+ printf( "Trusted DC for Domain " FORMAT_LPWSTR " is " FORMAT_LPWSTR "\n",
+ DomainName, TrustedDcName );
+
+ } else if ( WhoWill ) {
+
+ WhoWillLogMeOn( DomainName, UserName, IterationCount );
+
+ } else if( QuerySync ) {
+
+ DWORD DCCount;
+ PUNICODE_STRING DCNames;
+ DWORD i;
+ PNETLOGON_INFO_1 SyncNetlogonInfo1 = NULL;
+ LPWSTR SyncServerName = NULL;
+
+ NetStatus = I_NetGetDCList(
+ ServerName,
+ DomainName,
+ &DCCount,
+ &DCNames );
+
+ if ( NetStatus != NERR_Success ) {
+ fprintf( stderr, "I_NetGetDCList failed: ");
+ PrintStatus( NetStatus );
+ return(1);
+ }
+
+ for (i=1; i<DCCount; i++ ) {
+
+ if ( DCNames[i].Length > 0 ) {
+ SyncServerName = DCNames[i].Buffer;
+ } else {
+ SyncServerName = NULL;
+ }
+
+ NetStatus = I_NetLogonControl(
+ SyncServerName,
+ NETLOGON_CONTROL_QUERY,
+ 1,
+ (LPBYTE *)&SyncNetlogonInfo1 );
+
+ if ( NetStatus != NERR_Success ) {
+ printf( "Server : " FORMAT_LPWSTR "\n", SyncServerName );
+ printf( "\tI_NetLogonControl failed: ");
+ PrintStatus( NetStatus );
+ }
+ else {
+
+ printf( "Server : " FORMAT_LPWSTR "\n", SyncServerName );
+
+ printf( "\tSyncState : " );
+
+ if ( SyncNetlogonInfo1->netlog1_flags == 0 ) {
+ printf( " IN_SYNC \n" );
+ }
+ else if ( SyncNetlogonInfo1->netlog1_flags & NETLOGON_REPLICATION_IN_PROGRESS ) {
+ printf( " REPLICATION_IN_PROGRESS \n" );
+ }
+ else if ( SyncNetlogonInfo1->netlog1_flags & NETLOGON_REPLICATION_NEEDED ) {
+ printf( " REPLICATION_NEEDED \n" );
+ } else {
+ printf( " UNKNOWN \n" );
+ }
+
+ printf( "\tConnectionState : ");
+ PrintStatus( SyncNetlogonInfo1->netlog1_pdc_connection_status );
+
+ NetApiBufferFree( SyncNetlogonInfo1 );
+ }
+ }
+ } else if( SimFullSync ) {
+
+ LPWSTR MachineName;
+ LPWSTR PdcName;
+
+ MachineName = NetpAllocWStrFromStr( AnsiSimMachineName );
+
+ if ( MachineName == NULL ) {
+ fprintf( stderr, "Not enough memory\n" );
+ return(1);
+ }
+
+ NetStatus = NetGetDCName(
+ ServerName,
+ DomainName,
+ (LPBYTE *)&PdcName );
+
+ if ( NetStatus != NERR_Success ) {
+ fprintf( stderr, "NetGetDCName failed: " );
+ PrintStatus( NetStatus );
+ return(1);
+ }
+
+ Status = SimulateFullSync( PdcName, MachineName );
+
+ if ( !NT_SUCCESS( Status )) {
+ return(1);
+ }
+ }
+ }
+
+ //
+ // if we are asked to display the change log file. do so.
+ //
+
+ if( ListDeltasFlag || ListRedoFlag ) {
+
+ LPWSTR DeltaFileName;
+
+ DeltaFileName = NetpAllocWStrFromStr( AnsiDeltaFileName );
+
+ if ( DeltaFileName == NULL ) {
+ fprintf( stderr, "Not enough memory\n" );
+ return(1);
+ }
+
+ ListDeltas( DeltaFileName, ListRedoFlag );
+ }
+
+
+ //
+ // Handle shutting down a system.
+ //
+
+ if ( ShutdownReason != NULL ) {
+ if ( !InitiateSystemShutdownA( AnsiServerName,
+ ShutdownReason,
+ ShutdownSeconds,
+ FALSE, // Don't lose unsaved changes
+ TRUE ) ) { // Reboot when done
+ fprintf( stderr, "InitiateSystemShutdown failed: ");
+ PrintStatus( GetLastError() );
+ return 1;
+ }
+ }
+
+ if ( ShutdownAbort ) {
+ if ( !AbortSystemShutdownA( AnsiServerName ) ) {
+ fprintf( stderr, "AbortSystemShutdown failed: ");
+ PrintStatus( GetLastError() );
+ return 1;
+ }
+ }
+
+ //
+ // Print the list of domains trusted by a workstation.
+ //
+ if ( TrustedDomainsFlag ) {
+ ULONG CurrentIndex;
+ ULONG EntryCount;
+ LPWSTR CurrentEntry;
+ LPWSTR TrustedDomainList;
+
+ Status = NetEnumerateTrustedDomains( ServerName, &TrustedDomainList );
+
+ if ( !NT_SUCCESS(Status) ) {
+ fprintf( stderr, "NetEnumerateTrustedDOmains failed: ");
+ PrintStatus( Status );
+ return 1;
+ }
+
+ EntryCount = NetpTStrArrayEntryCount( TrustedDomainList );
+
+ printf( "Trusted domain list:\n" );
+ CurrentEntry = TrustedDomainList;
+
+ for ( CurrentIndex=0; CurrentIndex<EntryCount; CurrentIndex++ ) {
+
+ printf( " %ws\n", CurrentEntry );
+
+ CurrentEntry += wcslen(CurrentEntry) + 1;
+
+ }
+
+ NetApiBufferFree( TrustedDomainList );
+ }
+
+ //
+ // Handle setting lockout parameters on a domain.
+ //
+
+ if ( DoLockout ) {
+ SetLockout( ServerName, LockoutThreshold, LockoutDuration, LockoutWindow );
+ }
+
+
+ //
+ // If we've been asked to convert an NT GMT time to ascii,
+ // Do so
+ //
+
+ PrintTime( "", ConvertTime );
+
+ printf("The command completed successfully\n");
+ return 0;
+
+}
diff --git a/private/net/svcdlls/logonsrv/server/nltest.prf b/private/net/svcdlls/logonsrv/server/nltest.prf
new file mode 100644
index 000000000..8b1378917
--- /dev/null
+++ b/private/net/svcdlls/logonsrv/server/nltest.prf
@@ -0,0 +1 @@
+
diff --git a/private/net/svcdlls/logonsrv/server/nltest.rc b/private/net/svcdlls/logonsrv/server/nltest.rc
new file mode 100644
index 000000000..a09177583
--- /dev/null
+++ b/private/net/svcdlls/logonsrv/server/nltest.rc
@@ -0,0 +1,12 @@
+#include <windows.h>
+#include <ntverp.h>
+
+#define VER_FILETYPE VFT_APP
+#define VER_FILESUBTYPE VFT2_UNKNOWN
+#define VER_FILEDESCRIPTION_STR "Microsoft\256 Logon Server Test Utility"
+
+#define VER_INTERNALNAME_STR "nltest.exe"
+#define VER_ORIGINALFILENAME_STR "nltest.exe"
+
+#include <common.ver>
+
diff --git a/private/net/svcdlls/logonsrv/server/nltest1.c b/private/net/svcdlls/logonsrv/server/nltest1.c
new file mode 100644
index 000000000..e9a09b164
--- /dev/null
+++ b/private/net/svcdlls/logonsrv/server/nltest1.c
@@ -0,0 +1,522 @@
+/*--
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ nltest.c
+
+Abstract:
+
+ Test program for the Netlogon service.
+
+Author:
+
+ 21-Apr-1993 (madana)
+
+Environment:
+
+ User mode only.
+ Contains NT-specific code.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+--*/
+
+
+//
+// Common include files.
+//
+
+#define NLTEST_IMAGE
+#include <logonsrv.h> // Include files common to entire service
+#include <stdio.h>
+#include <string.h>
+#include <align.h>
+
+//
+// delta entry in the list
+//
+
+typedef struct _DELTA_ENTRY {
+ LIST_ENTRY Next;
+ PCHANGELOG_ENTRY ChangeLogEntry;
+ DWORD Order;
+} DELTA_ENTRY, *PDELTA_ENTRY;
+
+
+LIST_ENTRY GlobalDeltaLists[NUM_DBS + 1];
+ // list of deltas, include VOID DB also.
+
+//
+// Externals needed by chutil.obj
+
+CRITICAL_SECTION NlGlobalChangeLogCritSect;
+LARGE_INTEGER NlGlobalChangeLogPromotionIncrement = DOMAIN_PROMOTION_INCREMENT;
+LARGE_INTEGER PromotionMask = DOMAIN_PROMOTION_MASK;
+LONG NlGlobalChangeLogPromotionMask;
+
+//
+// Stub routine needed by chutil.obj
+//
+
+VOID
+NlpWriteEventlog (
+ IN DWORD EventID,
+ IN DWORD EventType,
+ IN LPBYTE RawDataBuffer OPTIONAL,
+ IN DWORD RawDataSize,
+ IN LPWSTR *StringArray,
+ IN DWORD StringCount
+ )
+{
+ return;
+ UNREFERENCED_PARAMETER( EventID );
+ UNREFERENCED_PARAMETER( EventType );
+ UNREFERENCED_PARAMETER( RawDataBuffer );
+ UNREFERENCED_PARAMETER( RawDataSize );
+ UNREFERENCED_PARAMETER( StringArray );
+ UNREFERENCED_PARAMETER( StringCount );
+}
+
+
+VOID
+MakeDeltaLists(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This routine make list of deltas of individual databases.
+
+Arguments:
+
+ None
+
+Return Value:
+
+ none.
+
+--*/
+{
+
+ PCHANGELOG_ENTRY ChangeLogEntry;
+ DWORD j;
+ DWORD Order = 1;
+
+ //
+ // initialize list enties.
+ //
+
+ for( j = 0; j < NUM_DBS + 1; j++ ) {
+ InitializeListHead(&GlobalDeltaLists[j]);
+ }
+
+ //
+ // The cache is valid if it is empty.
+ //
+
+ if ( ChangeLogIsEmpty( &NlGlobalChangeLogDesc) ) {
+ return;
+ }
+
+ ChangeLogEntry = (PCHANGELOG_ENTRY)(NlGlobalChangeLogDesc.Head+1);
+ do {
+
+ PDELTA_ENTRY NewDelta;
+
+ //
+ // make delta entry to insert in the list
+ //
+
+ NewDelta = (PDELTA_ENTRY)NetpMemoryAllocate( sizeof(DELTA_ENTRY) );
+
+ if ( NewDelta == NULL ) {
+ fprintf( stderr, "Not enough memory\n" );
+ return;
+ }
+
+ NewDelta->ChangeLogEntry = ChangeLogEntry;
+ NewDelta->Order = Order++;
+
+ //
+ // add this entry to appropriate list.
+ //
+
+ InsertTailList( &GlobalDeltaLists[ChangeLogEntry->DBIndex],
+ &NewDelta->Next );
+
+
+ } while ( ( ChangeLogEntry =
+ NlMoveToNextChangeLogEntry(&NlGlobalChangeLogDesc, ChangeLogEntry) ) != NULL );
+
+ return;
+
+}
+
+#if !DBG
+// This routine is defined in chutil.obj for the debug version
+
+VOID
+PrintChangeLogEntry(
+ PCHANGELOG_ENTRY ChangeLogEntry
+ )
+/*++
+
+Routine Description:
+
+ This routine print the content of the given changelog entry.
+
+Arguments:
+
+ ChangeLogEntry -- pointer to the change log entry to print
+
+Return Value:
+
+ none.
+
+--*/
+{
+ LPSTR DeltaName;
+
+ switch ( ChangeLogEntry->DeltaType ) {
+ case AddOrChangeDomain:
+ DeltaName = "AddOrChangeDomain";
+ break;
+ case AddOrChangeGroup:
+ DeltaName = "AddOrChangeGroup";
+ break;
+ case DeleteGroupByName:
+ case DeleteGroup:
+ DeltaName = "DeleteGroup";
+ break;
+ case RenameGroup:
+ DeltaName = "RenameGroup";
+ break;
+ case AddOrChangeUser:
+ DeltaName = "AddOrChangeUser";
+ break;
+ case DeleteUserByName:
+ case DeleteUser:
+ DeltaName = "DeleteUser";
+ break;
+ case RenameUser:
+ DeltaName = "RenameUser";
+ break;
+ case ChangeGroupMembership:
+ DeltaName = "ChangeGroupMembership";
+ break;
+ case AddOrChangeAlias:
+ DeltaName = "AddOrChangeAlias";
+ break;
+ case DeleteAlias:
+ DeltaName = "DeleteAlias";
+ break;
+ case RenameAlias:
+ DeltaName = "RenameAlias";
+ break;
+ case ChangeAliasMembership:
+ DeltaName = "ChangeAliasMembership";
+ break;
+ case AddOrChangeLsaPolicy:
+ DeltaName = "AddOrChangeLsaPolicy";
+ break;
+ case AddOrChangeLsaTDomain:
+ DeltaName = "AddOrChangeLsaTDomain";
+ break;
+ case DeleteLsaTDomain:
+ DeltaName = "DeleteLsaTDomain";
+ break;
+ case AddOrChangeLsaAccount:
+ DeltaName = "AddOrChangeLsaAccount";
+ break;
+ case DeleteLsaAccount:
+ DeltaName = "DeleteLsaAccount";
+ break;
+ case AddOrChangeLsaSecret:
+ DeltaName = "AddOrChangeLsaSecret";
+ break;
+ case DeleteLsaSecret:
+ DeltaName = "DeleteLsaSecret";
+ break;
+ case SerialNumberSkip:
+ DeltaName = "SerialNumberSkip";
+ break;
+ case DummyChangeLogEntry:
+ DeltaName = "DummyChangeLogEntry";
+ break;
+
+ default:
+ DeltaName ="(Unknown)";
+ break;
+ }
+
+ NlPrint((NL_CHANGELOG,
+ "DeltaType %s (%ld) SerialNumber: %lx %lx",
+ DeltaName,
+ ChangeLogEntry->DeltaType,
+ ChangeLogEntry->SerialNumber.HighPart,
+ ChangeLogEntry->SerialNumber.LowPart ));
+
+ if ( ChangeLogEntry->ObjectRid != 0 ) {
+ NlPrint((NL_CHANGELOG," Rid: 0x%lx", ChangeLogEntry->ObjectRid ));
+ }
+ if ( ChangeLogEntry->Flags & CHANGELOG_REPLICATE_IMMEDIATELY ) {
+ NlPrint((NL_CHANGELOG," Immediately" ));
+ }
+ if ( ChangeLogEntry->Flags & CHANGELOG_PDC_PROMOTION ) {
+ NlPrint((NL_CHANGELOG," Promotion" ));
+ }
+ if ( ChangeLogEntry->Flags & CHANGELOG_PASSWORD_CHANGE ) {
+ NlPrint((NL_CHANGELOG," PasswordChanged" ));
+ }
+ if ( ChangeLogEntry->Flags & CHANGELOG_DOMAINUSERS_CHANGED ) {
+ NlPrint((NL_CHANGELOG," DomainUsersChanged" ));
+ }
+
+
+ if( ChangeLogEntry->Flags & CHANGELOG_NAME_SPECIFIED ) {
+ NlPrint(( NL_CHANGELOG, " Name: '" FORMAT_LPWSTR "'",
+ (LPWSTR)((PBYTE)(ChangeLogEntry)+ sizeof(CHANGELOG_ENTRY))));
+ }
+
+ if( ChangeLogEntry->Flags & CHANGELOG_SID_SPECIFIED ) {
+ NlPrint((NL_CHANGELOG," Sid: "));
+ NlpDumpSid( NL_CHANGELOG,
+ (PSID)((PBYTE)(ChangeLogEntry)+ sizeof(CHANGELOG_ENTRY)) );
+ } else {
+ NlPrint((NL_CHANGELOG,"\n" ));
+ }
+}
+#endif // DBG
+
+
+VOID
+PrintDelta(
+ PDELTA_ENTRY Delta
+ )
+/*++
+
+Routine Description:
+
+ This routine print the content of the given delta.
+
+Arguments:
+
+ Delta: pointer to a delta entry to be printed.
+
+Return Value:
+
+ none.
+
+--*/
+{
+ printf( "Order: %ld ", Delta->Order );
+ PrintChangeLogEntry( Delta->ChangeLogEntry );
+}
+
+
+VOID
+PrintDeltaLists(
+ )
+/*++
+
+Routine Description:
+
+ This routine prints deltas of individual databases and validates the
+ sequence.
+
+Arguments:
+
+ none.
+
+Return Value:
+
+ none.
+
+--*/
+{
+
+ DWORD j;
+ LARGE_INTEGER RunningChangeLogSerialNumber[NUM_DBS+1];
+
+ for( j = 0; j < NUM_DBS + 1; j++ ) {
+ RunningChangeLogSerialNumber[j].QuadPart = 0;
+ }
+
+ //
+ // for each database.
+ //
+ for( j = 0; j < NUM_DBS + 1; j++ ) {
+
+ if( j == SAM_DB ) {
+ printf("Deltas of SAM DATABASE \n\n" );
+ } else if( j == BUILTIN_DB ) {
+ printf("Deltas of BUILTIN DATABASE \n\n" );
+ } else if( j == LSA_DB ) {
+ printf("Deltas of LSA DATABASE \n\n" );
+ } else if( j == VOID_DB ) {
+ printf("VOID Deltas \n\n" );
+ }
+
+ while( !IsListEmpty( &GlobalDeltaLists[j] ) ) {
+
+ PDELTA_ENTRY NextDelta;
+ PCHANGELOG_ENTRY ChangeLogEntry;
+
+ NextDelta = (PDELTA_ENTRY)
+ RemoveHeadList( &GlobalDeltaLists[j] );
+
+ ChangeLogEntry = NextDelta->ChangeLogEntry;
+
+ //
+ // validate this delta.
+ //
+
+ if ( RunningChangeLogSerialNumber[j].QuadPart == 0 ) {
+
+ //
+ // first entry for this database
+ //
+ // Increment to next expected serial number
+ //
+
+ RunningChangeLogSerialNumber[j].QuadPart =
+ ChangeLogEntry->SerialNumber.QuadPart + 1;
+
+
+ //
+ // Otherwise ensure the serial number is the value expected.
+ //
+
+ } else {
+
+
+ //
+ // If the order is wrong,
+ // just report the problem.
+ //
+
+ if ( !IsSerialNumberEqual(
+ &NlGlobalChangeLogDesc,
+ ChangeLogEntry,
+ &RunningChangeLogSerialNumber[j] ) ) {
+
+ printf("*** THIS ENTRY IS OUT OF SEQUENCE *** \n");
+
+ }
+
+ RunningChangeLogSerialNumber[j].QuadPart =
+ ChangeLogEntry->SerialNumber.QuadPart + 1;
+ }
+
+
+
+ //
+ // print delta
+ //
+
+ PrintDelta( NextDelta );
+
+ //
+ // free this entry.
+ //
+
+ NetpMemoryFree( NextDelta );
+
+ }
+
+ printf("-----------------------------------------------\n");
+ }
+
+}
+
+VOID
+ListDeltas(
+ LPWSTR DeltaFileName,
+ BOOLEAN ListRedoFile
+ )
+/*++
+
+Routine Description:
+
+ This function prints out the content of the change log file in
+ readable format. Also it also checks the consistency of the change
+ log. If not, it will point out the inconsistency.
+
+Arguments:
+
+ DeltaFileName - name of the change log file.
+
+ ListRedoFile - True if this is a redo log and not a change log
+
+Return Value:
+
+ none.
+
+--*/
+{
+ NTSTATUS Status;
+
+ // Needed by routines in chutil.obj
+ InitializeCriticalSection( &NlGlobalChangeLogCritSect );
+ NlGlobalChangeLogPromotionMask = PromotionMask.HighPart;
+ InitChangeLogDesc( &NlGlobalChangeLogDesc );
+
+ NlGlobalChangeLogDesc.RedoLog = ListRedoFile;
+
+ //
+ // Read in the existing changelog file.
+ //
+
+ Status = NlOpenChangeLogFile( DeltaFileName, &NlGlobalChangeLogDesc, TRUE );
+
+ if ( !NT_SUCCESS(Status) ) {
+
+ fprintf( stderr, "Couldn't NlOpenChangeLogFile'" FORMAT_LPWSTR
+ "': 0x%lx \n",
+ DeltaFileName,
+ Status );
+
+ goto Cleanup;
+ }
+
+ //
+ // Write to this file if conversion needed.
+ //
+ if ( NlGlobalChangeLogDesc.Version3 ) {
+ printf( "Converting version 3 changelog to version 4 -- writing netlv40.chg\n");
+ wcscpy( NlGlobalChangeLogFilePrefix, L"netlv40" );
+ }
+
+ //
+ // Convert the changelog file to the right size/version.
+ //
+
+ Status = NlResizeChangeLogFile( &NlGlobalChangeLogDesc, NlGlobalChangeLogDesc.BufferSize );
+
+ if ( !NT_SUCCESS(Status) ) {
+
+ fprintf( stderr, "Couldn't NlOpenChangeLogFile'" FORMAT_LPWSTR
+ "': 0x%lx \n",
+ DeltaFileName,
+ Status );
+
+ goto Cleanup;
+ }
+
+ //
+ // print change log signature
+
+ printf( "FILE SIGNATURE : %s \n\n", NlGlobalChangeLogDesc.Buffer );
+
+ MakeDeltaLists();
+
+ PrintDeltaLists();
+
+Cleanup:
+
+ return;
+}
diff --git a/private/net/svcdlls/logonsrv/server/oldstub.c b/private/net/svcdlls/logonsrv/server/oldstub.c
new file mode 100644
index 000000000..eb47dde27
--- /dev/null
+++ b/private/net/svcdlls/logonsrv/server/oldstub.c
@@ -0,0 +1,457 @@
+/*++
+
+Copyright (c) 1994 Microsoft Corporation
+
+Module Name:
+
+ oldstub.c
+
+Abstract:
+
+ This file contains functions generated by midl v1.0. These
+ functions were designed to only be called by the stubs, but
+ these paticular functions are called by user code. This
+ file is needed in order to compile with midl v2.0 which
+ doesn't generated these paticular functions anymore.
+
+Author:
+
+ Mario Goertzel (MarioGo) Jan 10, 1994
+
+Environment:
+
+ User Mode - Win32
+
+Revision History:
+
+
+--*/
+
+#include "logon_s.h"
+
+/* routine that frees graph for struct _UNICODE_STRING */
+void _fgs__UNICODE_STRING (UNICODE_STRING * _source)
+ {
+ if (_source->Buffer !=0)
+ {
+ MIDL_user_free((void *)(_source->Buffer));
+ }
+ }
+
+/* routine that frees graph for struct _NLPR_SID_ARRAY */
+void _fgs__NLPR_SID_ARRAY (NLPR_SID_ARRAY * _source)
+ {
+ if (_source->Sids !=0)
+ {
+ MIDL_user_free((void *)(_source->Sids));
+ }
+ }
+
+/* routine that frees graph for struct _NLPR_CR_CIPHER_VALUE */
+void _fgs__NLPR_CR_CIPHER_VALUE (NLPR_CR_CIPHER_VALUE * _source)
+ {
+ if (_source->Buffer !=0)
+ {
+ MIDL_user_free((void *)(_source->Buffer));
+ }
+ }
+
+/* routine that frees graph for struct _NLPR_LOGON_HOURS */
+void _fgs__NLPR_LOGON_HOURS (NLPR_LOGON_HOURS * _source)
+ {
+ if (_source->LogonHours !=0)
+ {
+ MIDL_user_free((void *)(_source->LogonHours));
+ }
+ }
+
+/* routine that frees graph for struct _NLPR_USER_PRIVATE_INFO */
+void _fgs__NLPR_USER_PRIVATE_INFO (NLPR_USER_PRIVATE_INFO * _source)
+ {
+ if (_source->Data !=0)
+ {
+ MIDL_user_free((void *)(_source->Data));
+ }
+ }
+
+/* routine that frees graph for struct _NETLOGON_DELTA_USER */
+void _fgs__NETLOGON_DELTA_USER (NETLOGON_DELTA_USER * _source)
+ {
+ _fgs__UNICODE_STRING ((UNICODE_STRING *)&_source->UserName);
+ _fgs__UNICODE_STRING ((UNICODE_STRING *)&_source->FullName);
+ _fgs__UNICODE_STRING ((UNICODE_STRING *)&_source->HomeDirectory);
+ _fgs__UNICODE_STRING ((UNICODE_STRING *)&_source->HomeDirectoryDrive);
+ _fgs__UNICODE_STRING ((UNICODE_STRING *)&_source->ScriptPath);
+ _fgs__UNICODE_STRING ((UNICODE_STRING *)&_source->AdminComment);
+ _fgs__UNICODE_STRING ((UNICODE_STRING *)&_source->WorkStations);
+ _fgs__NLPR_LOGON_HOURS ((NLPR_LOGON_HOURS *)&_source->LogonHours);
+ _fgs__UNICODE_STRING ((UNICODE_STRING *)&_source->UserComment);
+ _fgs__UNICODE_STRING ((UNICODE_STRING *)&_source->Parameters);
+ _fgs__NLPR_USER_PRIVATE_INFO ((NLPR_USER_PRIVATE_INFO *)&_source->PrivateData);
+ if (_source->SecurityDescriptor !=0)
+ {
+ MIDL_user_free((void *)(_source->SecurityDescriptor));
+ }
+ _fgs__UNICODE_STRING ((UNICODE_STRING *)&_source->DummyString1);
+ _fgs__UNICODE_STRING ((UNICODE_STRING *)&_source->DummyString2);
+ _fgs__UNICODE_STRING ((UNICODE_STRING *)&_source->DummyString3);
+ _fgs__UNICODE_STRING ((UNICODE_STRING *)&_source->DummyString4);
+ }
+
+/* routine that frees graph for struct _NETLOGON_DELTA_GROUP */
+void _fgs__NETLOGON_DELTA_GROUP (NETLOGON_DELTA_GROUP * _source)
+ {
+ _fgs__UNICODE_STRING ((UNICODE_STRING *)&_source->Name);
+ _fgs__UNICODE_STRING ((UNICODE_STRING *)&_source->AdminComment);
+ if (_source->SecurityDescriptor !=0)
+ {
+ MIDL_user_free((void *)(_source->SecurityDescriptor));
+ }
+ _fgs__UNICODE_STRING ((UNICODE_STRING *)&_source->DummyString1);
+ _fgs__UNICODE_STRING ((UNICODE_STRING *)&_source->DummyString2);
+ _fgs__UNICODE_STRING ((UNICODE_STRING *)&_source->DummyString3);
+ _fgs__UNICODE_STRING ((UNICODE_STRING *)&_source->DummyString4);
+ }
+
+/* routine that frees graph for struct _NETLOGON_DELTA_GROUP_MEMBER */
+void _fgs__NETLOGON_DELTA_GROUP_MEMBER (NETLOGON_DELTA_GROUP_MEMBER * _source)
+ {
+ if (_source->MemberIds !=0)
+ {
+ MIDL_user_free((void *)(_source->MemberIds));
+ }
+ if (_source->Attributes !=0)
+ {
+ MIDL_user_free((void *)(_source->Attributes));
+ }
+ }
+
+/* routine that frees graph for struct _NETLOGON_DELTA_ALIAS */
+void _fgs__NETLOGON_DELTA_ALIAS (NETLOGON_DELTA_ALIAS * _source)
+ {
+ _fgs__UNICODE_STRING ((UNICODE_STRING *)&_source->Name);
+ if (_source->SecurityDescriptor !=0)
+ {
+ MIDL_user_free((void *)(_source->SecurityDescriptor));
+ }
+ _fgs__UNICODE_STRING ((UNICODE_STRING *)&_source->DummyString1);
+ _fgs__UNICODE_STRING ((UNICODE_STRING *)&_source->DummyString2);
+ _fgs__UNICODE_STRING ((UNICODE_STRING *)&_source->DummyString3);
+ _fgs__UNICODE_STRING ((UNICODE_STRING *)&_source->DummyString4);
+ }
+
+/* routine that frees graph for struct _NETLOGON_DELTA_ALIAS_MEMBER */
+void _fgs__NETLOGON_DELTA_ALIAS_MEMBER (NETLOGON_DELTA_ALIAS_MEMBER * _source)
+ {
+ _fgs__NLPR_SID_ARRAY ((NLPR_SID_ARRAY *)&_source->Members);
+ }
+
+/* routine that frees graph for struct _NETLOGON_DELTA_DOMAIN */
+void _fgs__NETLOGON_DELTA_DOMAIN (NETLOGON_DELTA_DOMAIN * _source)
+ {
+ _fgs__UNICODE_STRING ((UNICODE_STRING *)&_source->DomainName);
+ _fgs__UNICODE_STRING ((UNICODE_STRING *)&_source->OemInformation);
+ if (_source->SecurityDescriptor !=0)
+ {
+ MIDL_user_free((void *)(_source->SecurityDescriptor));
+ }
+ _fgs__UNICODE_STRING ((UNICODE_STRING *)&_source->DummyString1);
+ _fgs__UNICODE_STRING ((UNICODE_STRING *)&_source->DummyString2);
+ _fgs__UNICODE_STRING ((UNICODE_STRING *)&_source->DummyString3);
+ _fgs__UNICODE_STRING ((UNICODE_STRING *)&_source->DummyString4);
+ }
+
+/* routine that frees graph for struct _NETLOGON_DELTA_RENAME */
+void _fgs__NETLOGON_DELTA_RENAME (NETLOGON_RENAME_GROUP * _source)
+ {
+ _fgs__UNICODE_STRING ((UNICODE_STRING *)&_source->OldName);
+ _fgs__UNICODE_STRING ((UNICODE_STRING *)&_source->NewName);
+ _fgs__UNICODE_STRING ((UNICODE_STRING *)&_source->DummyString1);
+ _fgs__UNICODE_STRING ((UNICODE_STRING *)&_source->DummyString2);
+ _fgs__UNICODE_STRING ((UNICODE_STRING *)&_source->DummyString3);
+ _fgs__UNICODE_STRING ((UNICODE_STRING *)&_source->DummyString4);
+ }
+
+/* routine that frees graph for struct _NETLOGON_DELTA_POLICY */
+void _fgs__NETLOGON_DELTA_POLICY (NETLOGON_DELTA_POLICY * _source)
+ {
+ if (_source->EventAuditingOptions !=0)
+ {
+ MIDL_user_free((void *)(_source->EventAuditingOptions));
+ }
+ _fgs__UNICODE_STRING ((UNICODE_STRING *)&_source->PrimaryDomainName);
+ if (_source->PrimaryDomainSid !=0)
+ {
+ MIDL_user_free((void *)(_source->PrimaryDomainSid));
+ }
+ if (_source->SecurityDescriptor !=0)
+ {
+ MIDL_user_free((void *)(_source->SecurityDescriptor));
+ }
+ _fgs__UNICODE_STRING ((UNICODE_STRING *)&_source->DummyString1);
+ _fgs__UNICODE_STRING ((UNICODE_STRING *)&_source->DummyString2);
+ _fgs__UNICODE_STRING ((UNICODE_STRING *)&_source->DummyString3);
+ _fgs__UNICODE_STRING ((UNICODE_STRING *)&_source->DummyString4);
+ }
+
+/* routine that frees graph for struct _NETLOGON_DELTA_TRUSTED_DOMAINS */
+void _fgs__NETLOGON_DELTA_TRUSTED_DOMAINS (NETLOGON_DELTA_TRUSTED_DOMAINS * _source)
+ {
+ _fgs__UNICODE_STRING ((UNICODE_STRING *)&_source->DomainName);
+ if (_source->ControllerNames !=0)
+ {
+ {
+ unsigned long _sym15;
+ for (_sym15 = 0; _sym15 < (unsigned long )(0 + _source->NumControllerEntries); _sym15++)
+ {
+ _fgs__UNICODE_STRING ((UNICODE_STRING *)&_source->ControllerNames[_sym15]);
+ }
+ }
+ MIDL_user_free((void *)(_source->ControllerNames));
+ }
+ if (_source->SecurityDescriptor !=0)
+ {
+ MIDL_user_free((void *)(_source->SecurityDescriptor));
+ }
+ _fgs__UNICODE_STRING ((UNICODE_STRING *)&_source->DummyString1);
+ _fgs__UNICODE_STRING ((UNICODE_STRING *)&_source->DummyString2);
+ _fgs__UNICODE_STRING ((UNICODE_STRING *)&_source->DummyString3);
+ _fgs__UNICODE_STRING ((UNICODE_STRING *)&_source->DummyString4);
+ }
+
+/* routine that frees graph for struct _NETLOGON_DELTA_ACCOUNTS */
+void _fgs__NETLOGON_DELTA_ACCOUNTS (NETLOGON_DELTA_ACCOUNTS * _source)
+ {
+ if (_source->PrivilegeAttributes !=0)
+ {
+ MIDL_user_free((void *)(_source->PrivilegeAttributes));
+ }
+ if (_source->PrivilegeNames !=0)
+ {
+ {
+ unsigned long _sym21;
+ for (_sym21 = 0; _sym21 < (unsigned long )(0 + _source->PrivilegeEntries); _sym21++)
+ {
+ _fgs__UNICODE_STRING ((UNICODE_STRING *)&_source->PrivilegeNames[_sym21]);
+ }
+ }
+ MIDL_user_free((void *)(_source->PrivilegeNames));
+ }
+ if (_source->SecurityDescriptor !=0)
+ {
+ MIDL_user_free((void *)(_source->SecurityDescriptor));
+ }
+ _fgs__UNICODE_STRING ((UNICODE_STRING *)&_source->DummyString1);
+ _fgs__UNICODE_STRING ((UNICODE_STRING *)&_source->DummyString2);
+ _fgs__UNICODE_STRING ((UNICODE_STRING *)&_source->DummyString3);
+ _fgs__UNICODE_STRING ((UNICODE_STRING *)&_source->DummyString4);
+ }
+
+/* routine that frees graph for struct _NETLOGON_DELTA_SECRET */
+void _fgs__NETLOGON_DELTA_SECRET (NETLOGON_DELTA_SECRET * _source)
+ {
+ _fgs__NLPR_CR_CIPHER_VALUE ((NLPR_CR_CIPHER_VALUE *)&_source->CurrentValue);
+ _fgs__NLPR_CR_CIPHER_VALUE ((NLPR_CR_CIPHER_VALUE *)&_source->OldValue);
+ if (_source->SecurityDescriptor !=0)
+ {
+ MIDL_user_free((void *)(_source->SecurityDescriptor));
+ }
+ _fgs__UNICODE_STRING ((UNICODE_STRING *)&_source->DummyString1);
+ _fgs__UNICODE_STRING ((UNICODE_STRING *)&_source->DummyString2);
+ _fgs__UNICODE_STRING ((UNICODE_STRING *)&_source->DummyString3);
+ _fgs__UNICODE_STRING ((UNICODE_STRING *)&_source->DummyString4);
+ }
+
+// Written by CliffV since MIDL no longer generates these.
+/* routine that frees graph for struct _NETLOGON_DELTA_DELETE */
+void _fgs__NETLOGON_DELTA_DELETE (NETLOGON_DELTA_DELETE_USER * _source)
+ {
+ MIDL_user_free((void *)(_source->AccountName));
+ _fgs__UNICODE_STRING ((UNICODE_STRING *)&_source->DummyString1);
+ _fgs__UNICODE_STRING ((UNICODE_STRING *)&_source->DummyString2);
+ _fgs__UNICODE_STRING ((UNICODE_STRING *)&_source->DummyString3);
+ _fgs__UNICODE_STRING ((UNICODE_STRING *)&_source->DummyString4);
+ }
+
+
+/* routine that frees graph for union _NETLOGON_DELTA_UNION */
+void _fgu__NETLOGON_DELTA_UNION (NETLOGON_DELTA_UNION * _source, NETLOGON_DELTA_TYPE _branch)
+ {
+ switch (_branch)
+ {
+ case AddOrChangeDomain :
+ {
+ if (_source->DeltaDomain !=0)
+ {
+ _fgs__NETLOGON_DELTA_DOMAIN ((NETLOGON_DELTA_DOMAIN *)_source->DeltaDomain);
+ MIDL_user_free((void *)(_source->DeltaDomain));
+ }
+ break;
+ }
+ case AddOrChangeGroup :
+ {
+ if (_source->DeltaGroup !=0)
+ {
+ _fgs__NETLOGON_DELTA_GROUP ((NETLOGON_DELTA_GROUP *)_source->DeltaGroup);
+ MIDL_user_free((void *)(_source->DeltaGroup));
+ }
+ break;
+ }
+ case RenameGroup :
+ {
+ if (_source->DeltaRenameGroup !=0)
+ {
+ _fgs__NETLOGON_DELTA_RENAME ((NETLOGON_RENAME_GROUP *)_source->DeltaRenameGroup);
+ MIDL_user_free((void *)(_source->DeltaRenameGroup));
+ }
+ break;
+ }
+ case AddOrChangeUser :
+ {
+ if (_source->DeltaUser !=0)
+ {
+ _fgs__NETLOGON_DELTA_USER ((NETLOGON_DELTA_USER *)_source->DeltaUser);
+ MIDL_user_free((void *)(_source->DeltaUser));
+ }
+ break;
+ }
+ case RenameUser :
+ {
+ if (_source->DeltaRenameUser !=0)
+ {
+ _fgs__NETLOGON_DELTA_RENAME ((NETLOGON_RENAME_GROUP *)_source->DeltaRenameUser);
+ MIDL_user_free((void *)(_source->DeltaRenameUser));
+ }
+ break;
+ }
+ case ChangeGroupMembership :
+ {
+ if (_source->DeltaGroupMember !=0)
+ {
+ _fgs__NETLOGON_DELTA_GROUP_MEMBER ((NETLOGON_DELTA_GROUP_MEMBER *)_source->DeltaGroupMember);
+ MIDL_user_free((void *)(_source->DeltaGroupMember));
+ }
+ break;
+ }
+ case AddOrChangeAlias :
+ {
+ if (_source->DeltaAlias !=0)
+ {
+ _fgs__NETLOGON_DELTA_ALIAS ((NETLOGON_DELTA_ALIAS *)_source->DeltaAlias);
+ MIDL_user_free((void *)(_source->DeltaAlias));
+ }
+ break;
+ }
+ case RenameAlias :
+ {
+ if (_source->DeltaRenameAlias !=0)
+ {
+ _fgs__NETLOGON_DELTA_RENAME ((NETLOGON_RENAME_GROUP *)_source->DeltaRenameAlias);
+ MIDL_user_free((void *)(_source->DeltaRenameAlias));
+ }
+ break;
+ }
+ case ChangeAliasMembership :
+ {
+ if (_source->DeltaAliasMember !=0)
+ {
+ _fgs__NETLOGON_DELTA_ALIAS_MEMBER ((NETLOGON_DELTA_ALIAS_MEMBER *)_source->DeltaAliasMember);
+ MIDL_user_free((void *)(_source->DeltaAliasMember));
+ }
+ break;
+ }
+ case AddOrChangeLsaPolicy :
+ {
+ if (_source->DeltaPolicy !=0)
+ {
+ _fgs__NETLOGON_DELTA_POLICY ((NETLOGON_DELTA_POLICY *)_source->DeltaPolicy);
+ MIDL_user_free((void *)(_source->DeltaPolicy));
+ }
+ break;
+ }
+ case AddOrChangeLsaTDomain :
+ {
+ if (_source->DeltaTDomains !=0)
+ {
+ _fgs__NETLOGON_DELTA_TRUSTED_DOMAINS ((NETLOGON_DELTA_TRUSTED_DOMAINS *)_source->DeltaTDomains);
+ MIDL_user_free((void *)(_source->DeltaTDomains));
+ }
+ break;
+ }
+ case AddOrChangeLsaAccount :
+ {
+ if (_source->DeltaAccounts !=0)
+ {
+ _fgs__NETLOGON_DELTA_ACCOUNTS ((NETLOGON_DELTA_ACCOUNTS *)_source->DeltaAccounts);
+ MIDL_user_free((void *)(_source->DeltaAccounts));
+ }
+ break;
+ }
+ case AddOrChangeLsaSecret :
+ {
+ if (_source->DeltaSecret !=0)
+ {
+ _fgs__NETLOGON_DELTA_SECRET ((NETLOGON_DELTA_SECRET *)_source->DeltaSecret);
+ MIDL_user_free((void *)(_source->DeltaSecret));
+ }
+ break;
+ }
+ case DeleteUserByName:
+ case DeleteGroupByName:
+ if (_source->DeltaDeleteUser !=0) {
+ _fgs__NETLOGON_DELTA_DELETE ((NETLOGON_DELTA_DELETE_USER *)_source->DeltaDeleteUser);
+ MIDL_user_free((void *)(_source->DeltaDeleteUser));
+ }
+ break;
+ case SerialNumberSkip:
+ if (_source->DeltaSerialNumberSkip !=0) {
+ MIDL_user_free((void *)(_source->DeltaSerialNumberSkip));
+ }
+ break;
+ default :
+ {
+ break;
+ }
+ }
+ }
+
+/* routine that frees graph for union _NETLOGON_DELTA_ID_UNION */
+void _fgu__NETLOGON_DELTA_ID_UNION (NETLOGON_DELTA_ID_UNION * _source, NETLOGON_DELTA_TYPE _branch)
+ {
+ switch (_branch)
+ {
+ case AddOrChangeLsaPolicy :
+ case AddOrChangeLsaTDomain :
+ case DeleteLsaTDomain :
+ case AddOrChangeLsaAccount :
+ case DeleteLsaAccount :
+ {
+ if (_source->Sid !=0)
+ {
+ MIDL_user_free((void *)(_source->Sid));
+ }
+ break;
+ }
+ case AddOrChangeLsaSecret :
+ case DeleteLsaSecret :
+ {
+ if (_source->Name !=0)
+ {
+ MIDL_user_free((void *)(_source->Name));
+ }
+ break;
+ }
+ default :
+ {
+ break;
+ }
+ }
+ }
+
+/* routine that frees graph for struct _NETLOGON_DELTA_ENUM */
+void _fgs__NETLOGON_DELTA_ENUM (NETLOGON_DELTA_ENUM * _source)
+ {
+ _fgu__NETLOGON_DELTA_ID_UNION ((NETLOGON_DELTA_ID_UNION *)&_source->DeltaID, _source->DeltaType);
+ _fgu__NETLOGON_DELTA_UNION ((NETLOGON_DELTA_UNION *)&_source->DeltaUnion, _source->DeltaType);
+ }
+
diff --git a/private/net/svcdlls/logonsrv/server/parse.c b/private/net/svcdlls/logonsrv/server/parse.c
new file mode 100644
index 000000000..0a0f453b1
--- /dev/null
+++ b/private/net/svcdlls/logonsrv/server/parse.c
@@ -0,0 +1,464 @@
+/*++
+
+Copyright (c) 1987-1992 Microsoft Corporation
+
+Module Name:
+
+ parse.c
+
+Abstract:
+
+ Routine to parse the command line.
+
+Author:
+
+ Ported from Lan Man 2.0
+
+Environment:
+
+ User mode only.
+ Contains NT-specific code.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+ 01-Aug-1991 (cliffv)
+ Ported to NT. Converted to NT style.
+ 09-May-1992 JohnRo
+ Enable use of win32 registry.
+ Use net config helpers for NetLogon.
+ Fixed UNICODE bug handling debug file name.
+ Use <prefix.h> equates.
+
+--*/
+
+//
+// Common include files.
+//
+
+#include <logonsrv.h> // Include files common to entire service
+#include <config.h> // net config helpers.
+#include <configp.h> // USE_WIN32_CONFIG (if defined), etc.
+#include <confname.h> // SECTION_ equates, NETLOGON_KEYWORD_ equates.
+#include <lmapibuf.h> // NetApiBufferFree().
+#include <lmerr.h> // NERR_ equates.
+#include <lmsname.h> // SERVICE_NETLOGON.
+#include <lmsvc.h> // SERVICE_UIC codes are defined here
+#include <prefix.h> // PREFIX_ equates.
+#include <tstring.h> // NetpNCopy{type}To{type}.
+
+//
+// Include files specific to this .c file
+//
+
+#include <iniparm.h> // DEFAULT_, MIN_, and MAX_ equates.
+#include <stdlib.h> // C library functions (rand, etc)
+#include <string.h> // strnicmp
+#include <tstring.h> // NetpCopy...
+
+
+NET_API_STATUS
+NlParseOne(
+ IN LPNET_CONFIG_HANDLE SectionHandle,
+ IN LPWSTR Keyword,
+ IN ULONG DefaultValue,
+ IN ULONG MinimumValue,
+ IN ULONG MaximumValue,
+ OUT PULONG Value
+ )
+/*++
+
+Routine Description:
+
+ Get a single numeric parameter from the netlogon section of the registry.
+
+Arguments:
+
+ SectionHandle - Handle into the registry.
+
+ Keyword - Name of the value to read.
+
+ DefaultValue - Default value if parameter doesn't exist.
+
+ MinimumValue - Minumin valid value.
+
+ MaximumValue - Maximum valid value.
+
+ Value - Returns the value parsed.
+
+Return Value:
+
+ Status of the operation
+
+--*/
+{
+ NET_API_STATUS NetStatus;
+ LPWSTR ValueT = NULL;
+
+ //
+ // Determine if the value is specified in the registry at all.
+ //
+
+ NetStatus = NetpGetConfigValue (
+ SectionHandle,
+ Keyword,
+ &ValueT );
+
+ if( ValueT != NULL ) {
+ NetApiBufferFree( ValueT );
+ ValueT = NULL;
+ }
+
+ //
+ // If the value wasn't specified,
+ // use the default.
+ //
+
+ if ( NetStatus == NERR_CfgParamNotFound ) {
+ *Value = DefaultValue;
+
+ //
+ // If the value was specifed,
+ // get it from the registry.
+ //
+
+ } else {
+
+ NetStatus = NetpGetConfigDword (
+ SectionHandle,
+ Keyword, // keyword wanted
+ DefaultValue,
+ Value );
+
+ if (NetStatus == NO_ERROR) {
+ if ( *Value > MaximumValue || *Value < MinimumValue ) {
+ LPWSTR MsgStrings[1];
+
+ MsgStrings[0] = Keyword;
+
+ NlpWriteEventlog( SERVICE_UIC_BADPARMVAL,
+ EVENTLOG_WARNING_TYPE,
+ (LPBYTE)Value,
+ sizeof(*Value),
+ MsgStrings,
+ 1 );
+
+ if ( *Value > MaximumValue ) {
+ *Value = MaximumValue;
+ } else if ( *Value < MinimumValue ) {
+ *Value = MinimumValue;
+ }
+ }
+
+ } else {
+
+ return NetStatus;
+
+ }
+ }
+
+ return NERR_Success;
+}
+
+
+//
+// Table of numeric parameters to parse.
+//
+
+struct {
+ LPWSTR Keyword;
+ ULONG DefaultValue;
+ ULONG MinimumValue;
+ ULONG MaximumValue;
+ PULONG Value;
+} ParseTable[] =
+{
+{ NETLOGON_KEYWORD_PULSE, DEFAULT_PULSE, MIN_PULSE, MAX_PULSE, &NlGlobalPulseParameter },
+{ NETLOGON_KEYWORD_RANDOMIZE, DEFAULT_RANDOMIZE, MIN_RANDOMIZE, MAX_RANDOMIZE, &NlGlobalRandomizeParameter },
+{ NETLOGON_KEYWORD_PULSEMAXIMUM, DEFAULT_PULSEMAXIMUM, MIN_PULSEMAXIMUM, MAX_PULSEMAXIMUM, &NlGlobalPulseMaximumParameter },
+{ NETLOGON_KEYWORD_PULSECONCURRENCY, DEFAULT_PULSECONCURRENCY, MIN_PULSECONCURRENCY, MAX_PULSECONCURRENCY, &NlGlobalPulseConcurrencyParameter },
+{ NETLOGON_KEYWORD_PULSETIMEOUT1, DEFAULT_PULSETIMEOUT1, MIN_PULSETIMEOUT1, MAX_PULSETIMEOUT1, &NlGlobalPulseTimeout1Parameter },
+{ NETLOGON_KEYWORD_PULSETIMEOUT2, DEFAULT_PULSETIMEOUT2, MIN_PULSETIMEOUT2, MAX_PULSETIMEOUT2, &NlGlobalPulseTimeout2Parameter },
+{ NETLOGON_KEYWORD_GOVERNOR, DEFAULT_GOVERNOR, MIN_GOVERNOR, MAX_GOVERNOR, &NlGlobalGovernorParameter },
+{ NETLOGON_KEYWORD_MAXIMUMMAILSLOTMESSAGES, DEFAULT_MAXIMUMMAILSLOTMESSAGES, MIN_MAXIMUMMAILSLOTMESSAGES, MAX_MAXIMUMMAILSLOTMESSAGES, &NlGlobalMaximumMailslotMessagesParameter },
+{ NETLOGON_KEYWORD_MAILSLOTMESSAGETIMEOUT, DEFAULT_MAILSLOTMESSAGETIMEOUT, MIN_MAILSLOTMESSAGETIMEOUT, MAX_MAILSLOTMESSAGETIMEOUT, &NlGlobalMailslotMessageTimeoutParameter },
+{ NETLOGON_KEYWORD_MAILSLOTDUPLICATETIMEOUT,DEFAULT_MAILSLOTDUPLICATETIMEOUT,MIN_MAILSLOTDUPLICATETIMEOUT,MAX_MAILSLOTDUPLICATETIMEOUT,&NlGlobalMailslotDuplicateTimeoutParameter },
+{ NETLOGON_KEYWORD_EXPECTEDDIALUPDELAY, DEFAULT_EXPECTEDDIALUPDELAY, MIN_EXPECTEDDIALUPDELAY, MAX_EXPECTEDDIALUPDELAY, &NlGlobalExpectedDialupDelayParameter },
+{ NETLOGON_KEYWORD_SCAVENGEINTERVAL, DEFAULT_SCAVENGEINTERVAL, MIN_SCAVENGEINTERVAL, MAX_SCAVENGEINTERVAL, &NlGlobalScavengeIntervalParameter },
+#if DBG
+{ NETLOGON_KEYWORD_DBFLAG, 0, 0, 0xFFFFFFFF, &NlGlobalTrace },
+{ NETLOGON_KEYWORD_MAXIMUMLOGFILESIZE, DEFAULT_MAXIMUM_LOGFILE_SIZE, 0, 0xFFFFFFFF, &NlGlobalLogFileMaxSize },
+#endif // DBG
+};
+
+//
+// Table of boolean to parse.
+//
+
+struct {
+ LPWSTR Keyword;
+ BOOL DefaultValue;
+ PBOOL Value;
+} BoolParseTable[] =
+{
+{ NETLOGON_KEYWORD_UPDATE, DEFAULT_SYNCHRONIZE, &NlGlobalSynchronizeParameter },
+{ NETLOGON_KEYWORD_DISABLEPASSWORDCHANGE, DEFAULT_DISABLE_PASSWORD_CHANGE, &NlGlobalDisablePasswordChangeParameter },
+{ NETLOGON_KEYWORD_REFUSEPASSWORDCHANGE, DEFAULT_REFUSE_PASSWORD_CHANGE, &NlGlobalRefusePasswordChangeParameter },
+};
+
+
+BOOL
+Nlparse(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ Get parameters from registry.
+
+ All of the parameters are described in iniparm.h.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ TRUE -- iff the parse was successful.
+
+--*/
+{
+ NET_API_STATUS NetStatus;
+
+ LPWSTR ValueT = NULL;
+ LPWSTR Keyword = NULL;
+ ULONG i;
+
+
+ //
+ // Variables for scanning the configuration data.
+ //
+
+ LPNET_CONFIG_HANDLE SectionHandle = NULL;
+
+
+ //
+ // Open the NetLogon configuration section.
+ //
+
+ NetStatus = NetpOpenConfigData(
+ &SectionHandle,
+ NULL, // no server name.
+#if defined(USE_WIN32_CONFIG)
+ SERVICE_NETLOGON,
+#else
+ SECT_NT_NETLOGON, // section name
+#endif
+ TRUE ); // we only want readonly access
+
+ if ( NetStatus != NO_ERROR ) {
+ SectionHandle = NULL;
+ NlExit(SERVICE_UIC_BADPARMVAL, NetStatus, LogError, NULL );
+ goto Cleanup;
+ }
+
+ //
+ // Loop parsing all the numeric parameters.
+ //
+
+ for ( i=0; i<sizeof(ParseTable)/sizeof(ParseTable[0]); i++ ) {
+
+ NetStatus = NlParseOne(
+ SectionHandle,
+ ParseTable[i].Keyword,
+ ParseTable[i].DefaultValue,
+ ParseTable[i].MinimumValue,
+ ParseTable[i].MaximumValue,
+ ParseTable[i].Value );
+
+ if ( NetStatus != NERR_Success ) {
+ Keyword = ParseTable[i].Keyword;
+ goto Cleanup;
+ }
+ }
+
+ //
+ // Loop parsing all the boolean parameters.
+ //
+
+ for ( i=0; i<sizeof(BoolParseTable)/sizeof(BoolParseTable[0]); i++ ) {
+
+ NetStatus = NetpGetConfigBool (
+ SectionHandle,
+ BoolParseTable[i].Keyword,
+ BoolParseTable[i].DefaultValue,
+ BoolParseTable[i].Value );
+
+ if (NetStatus != NO_ERROR) {
+ Keyword = BoolParseTable[i].Keyword;
+ goto Cleanup;
+ }
+
+ }
+
+
+ //
+ // Get the "SCRIPTS" configured parameter
+ //
+
+ NetStatus = NetpGetConfigValue (
+ SectionHandle,
+ NETLOGON_KEYWORD_SCRIPTS, // key wanted
+ &ValueT ); // Must be freed by NetApiBufferFree().
+
+ //
+ // Handle the default
+ //
+ if (NetStatus == NERR_CfgParamNotFound) {
+ ValueT = NetpAllocWStrFromWStr( DEFAULT_SCRIPTS );
+ if ( ValueT == NULL ) {
+ NetStatus = ERROR_NOT_ENOUGH_MEMORY;
+ } else {
+ NetStatus = NO_ERROR;
+ }
+ }
+
+ if (NetStatus == NO_ERROR) {
+
+ TCHAR OutPathname[PATHLEN+1];
+ ULONG type;
+ NlAssert( ValueT != NULL );
+
+ //
+ // Convert the /Scripts: parameter or configured script path to a full
+ // pathname.
+ //
+
+ NetStatus = I_NetPathCanonicalize( NULL,
+ ValueT,
+ OutPathname,
+ sizeof(OutPathname),
+ NULL,
+ &type,
+ 0L );
+ if (NetStatus != NERR_Success ) {
+ Keyword = NETLOGON_KEYWORD_SCRIPTS;
+ goto Cleanup;
+ }
+
+ if (type == ITYPE_PATH_ABSD) {
+ NetpCopyTStrToWStr(NlGlobalUnicodeScriptPath, OutPathname);
+ } else if (type == ITYPE_PATH_RELND) {
+ if ( !GetWindowsDirectoryW(
+ NlGlobalUnicodeScriptPath,
+ sizeof(NlGlobalUnicodeScriptPath)/sizeof(WCHAR) ) ) {
+ NetStatus = GetLastError();
+ Keyword = NETLOGON_KEYWORD_SCRIPTS;
+ goto Cleanup;
+ }
+ wcscat( NlGlobalUnicodeScriptPath, L"\\" );
+ wcscat( NlGlobalUnicodeScriptPath, OutPathname );
+ } else {
+ Keyword = NETLOGON_KEYWORD_SCRIPTS;
+ NetStatus = NERR_BadComponent;
+ goto Cleanup;
+ }
+
+ (VOID) NetApiBufferFree( ValueT );
+ ValueT = NULL;
+ } else {
+ Keyword = NETLOGON_KEYWORD_SCRIPTS;
+ goto Cleanup;
+ }
+
+
+
+ //
+ // Convert parameters to a more convenient form.
+ //
+
+ // Convert to from seconds to 100ns
+ NlGlobalPulseMaximum =
+ RtlEnlargedIntegerMultiply( NlGlobalPulseMaximumParameter,
+ 10000000 );
+
+ // Convert to from seconds to 100ns
+ NlGlobalPulseTimeout1 =
+ RtlEnlargedIntegerMultiply( NlGlobalPulseTimeout1Parameter,
+ 10000000 );
+
+ // Convert to from seconds to 100ns
+ NlGlobalPulseTimeout2 =
+ RtlEnlargedIntegerMultiply( NlGlobalPulseTimeout2Parameter,
+ 10000000 );
+
+ // Convert to from seconds to 100ns
+ NlGlobalMailslotMessageTimeout =
+ RtlEnlargedIntegerMultiply( NlGlobalMailslotMessageTimeoutParameter,
+ 10000000 );
+
+ // Convert to from seconds to 100ns
+ NlGlobalMailslotDuplicateTimeout =
+ RtlEnlargedIntegerMultiply( NlGlobalMailslotDuplicateTimeoutParameter,
+ 10000000 );
+
+
+ NlGlobalShortApiCallPeriod =
+ SHORT_API_CALL_PERIOD + NlGlobalExpectedDialupDelayParameter * 1000;
+
+#if DBG
+ //
+ // Open the debug file
+ //
+
+ NlOpenDebugFile( FALSE );
+
+
+
+ NlPrint((NL_INIT, "Following are the effective values after parsing\n"));
+
+ NlPrint((NL_INIT," ScriptsParameter = " FORMAT_LPWSTR "\n",
+ NlGlobalUnicodeScriptPath));
+
+ for ( i=0; i<sizeof(ParseTable)/sizeof(ParseTable[0]); i++ ) {
+ NlPrint((NL_INIT," " FORMAT_LPWSTR " = %lu (0x%lx)\n",
+ ParseTable[i].Keyword,
+ *ParseTable[i].Value,
+ *ParseTable[i].Value ));
+ }
+
+ for ( i=0; i<sizeof(BoolParseTable)/sizeof(BoolParseTable[0]); i++ ) {
+
+ NlPrint((NL_INIT," " FORMAT_LPWSTR " = %s\n",
+ BoolParseTable[i].Keyword,
+ *BoolParseTable[i].Value ? "TRUE":"FALSE" ));
+ }
+
+ IF_DEBUG( NETLIB ) {
+ extern DWORD NetlibpTrace;
+ NetlibpTrace |= 0x8000; // NETLIB_DEBUG_LOGON
+ }
+#endif // DBG
+
+
+ NetStatus = NERR_Success;
+
+
+ //
+ // Free any locally used resources
+ //
+Cleanup:
+ if ( NetStatus != NERR_Success ) {
+ NlExit(SERVICE_UIC_BADPARMVAL, NetStatus, LogError, Keyword );
+ }
+
+ if ( ValueT != NULL) {
+ (VOID) NetApiBufferFree( ValueT );
+ }
+ if ( SectionHandle != NULL ) {
+ (VOID) NetpCloseConfigData( SectionHandle );
+ }
+
+ return (NetStatus == NERR_Success);
+}
diff --git a/private/net/svcdlls/logonsrv/server/repluas.c b/private/net/svcdlls/logonsrv/server/repluas.c
new file mode 100644
index 000000000..fbb272c61
--- /dev/null
+++ b/private/net/svcdlls/logonsrv/server/repluas.c
@@ -0,0 +1,2452 @@
+/*++
+
+Copyright (c) 1987-1992 Microsoft Corporation
+
+Module Name:
+
+ repluas.c
+
+Abstract:
+
+ Low level functions for SSI UAS Replication apis. These functions
+ are used only for downlevel supports.
+
+Author:
+
+ Ported from Lan Man 2.0
+
+Environment:
+
+ User mode only.
+ Contains NT-specific code.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+ 23-Aug-1991 (cliffv)
+ Ported to NT. Converted to NT style.
+ Madana
+ Fixed several problems.
+
+--*/
+
+//
+// Common include files.
+//
+
+#include <logonsrv.h> // Include files common to entire service
+
+//
+// Include files specific to this .c file
+//
+
+#include <accessp.h> // Routines shared with NetUser Apis
+#include <lmerr.h> // NERR_*
+#include <replutil.h> // Local procedure forwards
+#include <ntrpcp.h> // MIDL_user_free
+#include <secobj.h> // NetpDomainIdToSid
+#include <ssidelta.h>
+#include <stddef.h> // offsetof
+#include <loghours.h>
+
+//
+// Macro for setting a Unalligned Ushort.
+//
+
+#ifdef i386
+#define PutUnalignedUshort(DestAddress, Value) *(DestAddress) = (USHORT)(Value)
+#else
+#define PutUnalignedUshort(DestAddress,Value) { \
+ ( (PUCHAR)(DestAddress) )[0] = BYTE_0(Value); \
+ ( (PUCHAR)(DestAddress) )[1] = BYTE_1(Value); \
+ }
+#endif
+
+BOOLEAN
+SpecialGroupOp(
+ IN PUNICODE_STRING NameString,
+ IN OUT PDWORD Groups
+ )
+{
+
+ if( _wcsnicmp( NameString->Buffer,
+ UAS_BUILTIN_ADMINS_GROUP_NAME,
+ NameString->Length) == 0) {
+
+ if( Groups != NULL ) {
+ *Groups |= UAS_BUILTIN_ADMINS_GROUP;
+ }
+
+ return(TRUE);
+ }
+ else if( _wcsnicmp( NameString->Buffer,
+ UAS_BUILTIN_USERS_GROUP_NAME,
+ NameString->Length) == 0) {
+
+ if( Groups != NULL ) {
+ *Groups |= UAS_BUILTIN_USERS_GROUP;
+ }
+
+ return(TRUE);
+ }
+ else if( _wcsnicmp( NameString->Buffer,
+ UAS_BUILTIN_GUESTS_GROUP_NAME,
+ NameString->Length) == 0) {
+
+ if( Groups != NULL ) {
+ *Groups |= UAS_BUILTIN_GUESTS_GROUP;
+ }
+
+ return(TRUE);
+ }
+
+ return(FALSE);
+}
+
+
+NTSTATUS
+NlPackUasHeader(
+ IN BYTE Opcode,
+ IN DWORD InitialSize,
+ OUT PUSHORT *RecordSize,
+ IN OUT PBUFFER_DESCRIPTOR BufferDescriptor
+ )
+/*++
+
+Routine Description:
+
+ Place a header at the front of the record returned from I_NetAccountDeltas
+ or I_NetAccountSync.
+
+ A three-byte header is reserved as follows:
+ _____________________________________________
+ | length_word | opcode_byte | packed_struct |
+ ---------------------------------------------
+
+ The BufferDescriptor is updated to point to where the caller should
+ place the structure describing the delta.
+
+Arguments:
+
+ Opcode - Specified an OPCODE describing the data that will follow this
+ header. Use one the of the DELTA_ defines.
+
+ InitialSize - Specifies the size of the fixed-length portion of the
+ Uas record.
+
+ RecordSize - Returns a Pointer to where the total record size is to
+ be placed. Warning, the returned value is NOT properly aligned
+ an should only be set using PutUnalignedUshort.
+
+ BufferDescriptor - Describes the buffer to place the header into.
+ The descriptor is updated to reflect that the header has been
+ placed into the buffer.
+
+Return Value:
+
+ STATUS_SUCCESS -- ALL OK
+
+ STATUS_MORE_ENTRIES - The header does not fit into the buffer.
+
+--*/
+{
+ NlPrint((NL_PACK_VERBOSE,"NlPackUasHeader: Opcode: %ld InitialSize: 0x%lx\n",
+ Opcode,
+ InitialSize ));
+ NlPrint((NL_PACK_VERBOSE," Buffer: FixedDataEnd:%lx EndOfVar:%lx\n",
+ BufferDescriptor->FixedDataEnd,
+ BufferDescriptor->EndOfVariableData));
+
+ //
+ // Ensure the header fits in the return buffer.
+ //
+
+ if ( (LONG)(BufferDescriptor->EndOfVariableData -
+ BufferDescriptor->FixedDataEnd) <
+ (LONG)(NETLOGON_DELTA_HEADER_SIZE + InitialSize) ) {
+ NlPrint((NL_PACK," Header doesn't fit into buffer\n" ));
+
+ return STATUS_MORE_ENTRIES;
+ }
+
+ //
+ // Return a pointer to the RecordSize field of the header and initially
+ // set the RecordSize field to be the initial size.
+ //
+ // Return a pointer to where the record size should be stored
+ // and put the opcode in the header.
+ //
+
+ *RecordSize = (PUSHORT) BufferDescriptor->FixedDataEnd;
+ BufferDescriptor->FixedDataEnd += sizeof (unsigned short);
+
+ PutUnalignedUshort( *RecordSize, InitialSize + NETLOGON_DELTA_HEADER_SIZE );
+
+
+ //
+ // Set the Opcode into the header.
+ //
+
+ *(BufferDescriptor->FixedDataEnd) = Opcode;
+ BufferDescriptor->FixedDataEnd++;
+
+ return STATUS_SUCCESS;
+}
+
+
+
+
+NTSTATUS
+NlPackVarLenField(
+ IN RPC_UNICODE_STRING *UnicodeString,
+ IN OUT PBUFFER_DESCRIPTOR BufferDescriptor,
+ OUT PUSHORT StringOffset,
+ IN OUT USHORT *RunningOffset,
+ IN DWORD MaxStringLength,
+ IN BOOL TruncateSilently,
+ IN DWORD RelativeId
+ )
+/*++
+
+Routine Description:
+
+ Pack the specified UnicodeString into the specified buffer as an Ansi
+ zero terminated string.
+
+Arguments:
+
+ UnicodeString - Specifies the unicode String to pack into the buffer.
+
+ BufferDescriptor - Describes the buffer to pack the string into.
+ Specifically, FixedDataEnd describes where the corresponding
+ ANSI string will be placed. EndOfVariableData points to one
+ byte beyond the space available for the string. FixedDataEnd is
+ adjusted to reflect the copied data.
+
+ StringOffset - Receives the offset of the string from the beggining of
+ the base structure. If UnicodeString points to a NULL string, 0 is
+ returned as the offset; otherwise, this will either be the value
+ of RunningOffset as passed into this routine.
+
+ WARNING: This pointer need not be properly aligned.
+
+ RunningOffset - Specifies the current offset from the base structure.
+ This value is incremented by the size of the copied ANSI string
+ including the zero byte. The value returned is suitable for passing
+ to the next NlPackVarLenField call.
+
+ MaxStringLength - The maximum length in bytes (not including the zero byte)
+ of the resultant ANSI string.
+
+ TruncateSilently - Specifies the action to take if the ANSI string is
+ truncated to MaxStringLength. If TRUE, the string is silently
+ truncated. If FALSE, the string is truncated an a
+ STATUS_INVALID_PARAMETER error is return.
+
+ RelativeId -- If non-zero, specifies that unicode string is an account
+ name. RelativeId is the RelativeId of that account. Account names
+ are always converted to upper case for downlevel compatibility.
+ If an account name cannot be converted or truncated, a fictitious
+ account name, "RID########", will used.
+
+
+Return Value:
+
+ STATUS_SUCCESS -- ALL OK
+
+ STATUS_INVALID_PARAMETER - The specified UnicodeString is longer than
+ its UAS equivalent. Or Unicode character could not be mapped to
+ ANSI.
+
+ STATUS_MORE_ENTRIES - The String does not fit in the specified buffer.
+
+ STATUS_* - Other status codes.
+
+--*/
+{
+ NTSTATUS Status;
+
+ OEM_STRING AnsiString;
+ BOOLEAN AnsiStringAllocated = FALSE;
+
+ CHAR RidNameBufferAnsi[13]; // "RID" + ######## + '$' + '\0'
+
+ //
+ // Be Verbose
+ //
+
+ IF_DEBUG( PACK_VERBOSE ) {
+ if ( UnicodeString->Length != 0 ) {
+ NlPrint((NL_PACK_VERBOSE,
+ "NlPackVarLenField: String:%wZ (0x%lx) Maximum: 0x%lx\n",
+ UnicodeString,
+ UnicodeString->Length/sizeof(WCHAR),
+ UnicodeString->MaximumLength/sizeof(WCHAR) ));
+ NlPrint((NL_PACK_VERBOSE,
+ " Buffer: FixedDataEnd:%lx EndOfVar:%lx\n",
+ BufferDescriptor->FixedDataEnd,
+ BufferDescriptor->EndOfVariableData));
+ NlPrint((NL_PACK_VERBOSE,
+ " RunningOffset:0x%lx TruncateSilently: %ld\n",
+ *RunningOffset,
+ TruncateSilently ));
+ }
+ }
+
+
+
+ //
+ // Convert the string to OEM (upper casing Account Names)
+ //
+
+ if ( RelativeId != 0 ) {
+
+ Status = RtlUpcaseUnicodeStringToOemString(
+ &AnsiString,
+ (PUNICODE_STRING) UnicodeString,
+ (BOOLEAN) TRUE );
+ } else {
+
+ Status = RtlUnicodeStringToOemString(
+ &AnsiString,
+ (PUNICODE_STRING) UnicodeString,
+ (BOOLEAN) TRUE );
+
+ }
+
+ if ( !NT_SUCCESS(Status) ) {
+ if ( Status != STATUS_UNMAPPABLE_CHARACTER ) {
+ goto Cleanup;
+ }
+
+ NlPrint((NL_CRITICAL,
+ " String contains unmappable character (truncated)\n" ));
+ RtlInitString( &AnsiString, "" );
+
+ if ( TruncateSilently ) {
+ Status = STATUS_SUCCESS;
+ } else {
+ Status = STATUS_INVALID_PARAMETER;
+ }
+ } else {
+ AnsiStringAllocated = TRUE;
+ }
+
+
+ //
+ // Validate the length of the Ansi String.
+ //
+
+ if ( NT_SUCCESS(Status) ) {
+ if ( AnsiString.Length > (USHORT)MaxStringLength ) {
+ AnsiString.Length = (USHORT)MaxStringLength;
+ AnsiString.Buffer[(USHORT)MaxStringLength] = '\0';
+
+ NlPrint((NL_PACK_VERBOSE," String too long (truncated) '%Z'\n",
+ &AnsiString ));
+
+ if ( TruncateSilently ) {
+ Status = STATUS_SUCCESS;
+ } else {
+ Status = STATUS_INVALID_PARAMETER;
+ }
+ }
+ }
+
+
+ //
+ // Handle an invalid string.
+ //
+
+ if ( !NT_SUCCESS(Status) ) {
+
+ //
+ // If this name is an account name,
+ // convert the account name to RID#########.
+ //
+ // Non-account names have already been truncated appropriately.
+ //
+
+ if ( RelativeId != 0 ) {
+ NTSTATUS LocalStatus;
+
+ if ( AnsiStringAllocated ) {
+ RtlFreeOemString( &AnsiString );
+ AnsiStringAllocated = FALSE;
+ }
+
+ AnsiString.Length = 12;
+ AnsiString.MaximumLength = 13;
+ AnsiString.Buffer = RidNameBufferAnsi;
+ lstrcpyA( RidNameBufferAnsi, "RID" );
+ LocalStatus =
+ RtlIntegerToChar( RelativeId, 16, (-8), &RidNameBufferAnsi[3]);
+ NlAssert( NT_SUCCESS(LocalStatus) );
+ RidNameBufferAnsi[11] = '$'; // Obfuscate the name
+ RidNameBufferAnsi[12] = '\0'; // Null Terminate it.
+
+ NlPrint((NL_PACK,
+ " Complex account name converted to '%Z'\n",
+ &AnsiString ));
+ }
+
+ }
+
+ //
+ // Ensure the resultant ANSI string fits in the buffer.
+ //
+
+ if ( (LONG)AnsiString.Length >=
+ (LONG)(BufferDescriptor->EndOfVariableData
+ - BufferDescriptor->FixedDataEnd ) ) {
+
+ NlPrint((NL_CRITICAL," String too long for buffer (error)\n" ));
+ Status = STATUS_MORE_ENTRIES;
+ goto Cleanup;
+ }
+
+ //
+ // Copy the string into the buffer.
+ //
+
+ RtlCopyMemory( BufferDescriptor->FixedDataEnd,
+ AnsiString.Buffer,
+ AnsiString.Length + 1 );
+
+ PutUnalignedUshort( StringOffset, *RunningOffset );
+ BufferDescriptor->FixedDataEnd += AnsiString.Length + 1;
+ *RunningOffset += (USHORT) ( AnsiString.Length + 1 );
+
+ NlPrint((NL_PACK_VERBOSE,
+ " NewFixedDataEnd:%lx NewRunningOffset:0x%lx\n",
+ BufferDescriptor->FixedDataEnd,
+ *RunningOffset ));
+
+ // Status has already been set.
+
+ //
+ // Cleanup any locally allocated resources.
+ //
+
+Cleanup:
+ if ( AnsiStringAllocated ) {
+ RtlFreeOemString( &AnsiString );
+ }
+
+ return Status;
+}
+
+
+
+NTSTATUS
+NlPackUasUser (
+ IN ULONG RelativeId,
+ IN OUT PBUFFER_DESCRIPTOR BufferDescriptor,
+ IN PDB_INFO DBInfo,
+ IN PNETLOGON_SESSION_KEY SessionKey,
+ IN LONG RotateCount
+ )
+/*++
+
+Routine Description:
+
+ Pack a description of the specified user into the specified buffer.
+
+Arguments:
+
+ RelativeId - The relative Id of the user query.
+
+ BufferDescriptor - Points to a structure which describes the allocated
+ buffer.
+ This Routine updates EndOfVariableData and FixedDataEnd to reflect the
+ newly packed information.
+
+ DBInfo - Database info describing the SAM database to read from
+
+ SessionKey - Session Key to encrypt the password with.
+
+ RotateCount - Number of bits to rotate logon hours by
+
+Return Value:
+
+ STATUS_SUCCESS -- ALL OK
+
+ STATUS_MORE_ENTRIES - The record does not fit into the buffer.
+
+ STATUS_* - Other status codes.
+
+--*/
+{
+ NTSTATUS Status;
+ SAMPR_HANDLE UserHandle;
+ BUFFER_DESCRIPTOR OriginalBufferDescriptor;
+
+ //
+ // Record to pack into buffer.
+ //
+
+ PUSHORT RecordSize; // Pointer to record size field in record header.
+ PUSER_ADD_SET up;
+ USHORT RunningOffset;
+ ULONG TempUlong;
+ LARGE_INTEGER TempTime;
+
+ //
+ // Information returned from SAM
+ //
+
+ PSAMPR_USER_INFO_BUFFER UserAll = NULL;
+ PSAMPR_GET_GROUPS_BUFFER Groups = NULL;
+ BOOLEAN DaclPresent;
+ PACL UserDacl;
+ BOOLEAN DaclDefaulted;
+ RPC_UNICODE_STRING UasLogonServer;
+
+ //
+ // Variables describes membership in the special groups.
+ //
+
+ DWORD Priv;
+ DWORD AuthFlags;
+ DWORD Flags;
+
+ //
+ // time conversion.
+ //
+ LARGE_INTEGER LocalTime;
+
+
+ //
+ // Initialization.
+ //
+
+ OriginalBufferDescriptor = *BufferDescriptor;
+ RtlInitUnicodeString( (PUNICODE_STRING)&UasLogonServer, L"\\\\*" );
+
+ NlPrint((NL_SYNC_MORE, "NlPackUasUser Rid=0x%lx\n", RelativeId));
+
+ //
+ // Open a handle to the specified user.
+ //
+
+ Status = SamrOpenUser( DBInfo->DBHandle,
+ 0, // No desired access
+ RelativeId,
+ &UserHandle );
+
+ if (!NT_SUCCESS(Status)) {
+ UserHandle = NULL;
+
+ NlPrint((NL_CRITICAL,
+ "NlPackUasUser: SamrOpenUser returns 0x%lx\n",
+ Status ));
+ return Status;
+ }
+
+ //
+ // Find out everything there is to know about the user.
+ //
+
+ Status = SamrQueryInformationUser(
+ UserHandle,
+ UserAllInformation,
+ &UserAll );
+
+ if (!NT_SUCCESS(Status)) {
+ UserAll = NULL;
+
+ NlPrint((NL_CRITICAL,
+ "NlPackUasUser: SamrQueryInformationUser returns 0x%lx\n",
+ Status ));
+ goto Cleanup;
+ }
+
+
+ //
+ // skip this account if this is a machine account. However add dummy
+ // delta record so that the serial number on the down level BDC is
+ // incremented correctly.
+ //
+
+ if ( UserAll->All.UserAccountControl & USER_MACHINE_ACCOUNT_MASK ) {
+
+ NlPrint((NL_SYNC_MORE,
+ "NlPackUasUser skipping machine account '%wZ' \n",
+ &UserAll->All.UserName ));
+
+ Status = NlPackUasHeader( DELTA_RESERVED_OPCODE,
+ 0,
+ &RecordSize,
+ BufferDescriptor );
+ Status = STATUS_SUCCESS;
+
+ goto Cleanup;
+ }
+
+
+ //
+ // Determine the Priv and AuthFlags as a function of group membership.
+ //
+ // Determine all the groups this user is a member of
+ //
+
+ Status = SamrGetGroupsForUser( UserHandle,
+ &Groups );
+
+ if ( !NT_SUCCESS(Status) ) {
+ Groups = NULL;
+ NlPrint((NL_CRITICAL,
+ "NlPackUasUser: SamGetGroupsForUser returns 0x%lX\n",
+ Status ));
+ goto Cleanup;
+ }
+
+ Status = NlGetUserPriv(
+ Groups->MembershipCount, // Group Count
+ Groups->Groups, // Array of groups
+ RelativeId,
+ &Priv,
+ &AuthFlags );
+
+ if (!NT_SUCCESS(Status)) {
+ NlPrint((NL_CRITICAL,
+ "NlPackUasUser: NlGetUserPriv returns 0x%lX\n",
+ Status ));
+ goto Cleanup;
+ }
+
+
+ //
+ // Get a pointer to the UserDacl from the Security Descriptor.
+ //
+
+ Status = RtlGetDaclSecurityDescriptor(
+ (PSECURITY_DESCRIPTOR)
+ UserAll->All.SecurityDescriptor.SecurityDescriptor,
+ &DaclPresent,
+ &UserDacl,
+ &DaclDefaulted );
+
+
+ if ( ! NT_SUCCESS( Status ) ) {
+ NlPrint((NL_CRITICAL,
+ "NlPackUasUser: RtlGetDaclSecurityObject returns %lX\n",
+ Status ));
+ goto Cleanup;
+ }
+
+ if ( !DaclPresent ) {
+ UserDacl = NULL;
+ }
+
+
+ //
+ // Determine the Account control flags
+ // (Don't replicate machine accounts)
+ //
+
+ Flags = NetpAccountControlToFlags(
+ UserAll->All.UserAccountControl,
+ UserDacl );
+
+ if ( Flags & UF_MACHINE_ACCOUNT_MASK ) {
+ Flags |= UF_ACCOUNTDISABLE;
+ }
+
+ Flags &= UF_VALID_LM2X;
+
+
+ //
+ // Pack the header into the return buffer and ensure the fixed length
+ // portion fits.
+ //
+
+ RunningOffset = sizeof(USER_ADD_SET);
+ Status = NlPackUasHeader( DELTA_USERADD,
+ RunningOffset,
+ &RecordSize,
+ BufferDescriptor );
+
+ if ( Status != STATUS_SUCCESS ) {
+ NlPrint((NL_CRITICAL,
+ "NlPackUasUser: NlPackUasHeader returns %lX\n",
+ Status ));
+ goto Cleanup;
+ }
+
+ up = (PUSER_ADD_SET) BufferDescriptor->FixedDataEnd;
+ BufferDescriptor->FixedDataEnd += RunningOffset;
+
+
+
+ //
+ // Copy the password (Encrypted with the session key).
+ // The BDC/Member will decrypt it on the other side.
+ //
+ // If an LM compatible password is not available,
+ // just skip this account.
+ //
+ //
+
+ if ( UserAll->All.NtPasswordPresent &&
+ !UserAll->All.LmPasswordPresent ) {
+ Flags |= UF_ACCOUNTDISABLE;
+ }
+
+ if( UserAll->All.LmOwfPassword.Buffer != NULL ) {
+
+ NlAssert( sizeof(LM_OWF_PASSWORD) == UserAll->All.LmOwfPassword.Length);
+
+ Status = RtlEncryptLmOwfPwdWithLmOwfPwd(
+ (PLM_OWF_PASSWORD)UserAll->All.LmOwfPassword.Buffer,
+ (PLM_OWF_PASSWORD)SessionKey,
+ (PENCRYPTED_LM_OWF_PASSWORD) up->uas_password ) ;
+
+ if ( !NT_SUCCESS(Status) ) {
+ NlPrint((NL_CRITICAL,
+ "NlPackUasUser: RtlEncryptLmOwfPwdWithLmOwfPwd returns %lX\n",
+ Status ));
+ goto Cleanup;
+ }
+ }
+ else {
+
+ //
+ // this account has no LM compatible password.
+ // Encrypt NULL OWF password.
+ //
+
+ LM_OWF_PASSWORD NullLmOwfPassword;
+
+ NlAssert( !UserAll->All.LmPasswordPresent );
+
+ Status = RtlCalculateLmOwfPassword( "", &NullLmOwfPassword );
+
+ if ( !NT_SUCCESS(Status) ) {
+ NlPrint((NL_CRITICAL,
+ "NlPackUasUser: RtlCalculateLmOwfPassword returns "
+ "%lX\n", Status ));
+ goto Cleanup;
+ }
+
+ Status = RtlEncryptLmOwfPwdWithLmOwfPwd(
+ &NullLmOwfPassword,
+ (PLM_OWF_PASSWORD)SessionKey,
+ (PENCRYPTED_LM_OWF_PASSWORD) up->uas_password ) ;
+
+ if ( !NT_SUCCESS(Status) ) {
+ NlPrint((NL_CRITICAL,
+ "NlPackUasUser: RtlEncryptLmOwfPwdWithLmOwfPwd returns %lX\n",
+ Status ));
+ goto Cleanup;
+ }
+ }
+
+ //
+ // If the LogonHours are not compatible with downlevel systems,
+ // just skip this account.
+ //
+ // Compatible logon hours are either ALL hours permitted, or
+ // LogonHours are specified in terms of hours per week.
+ //
+ // We could potentially convert other UnitsPerWeek to SAM_HOURS_PER_WEEK
+ // but when we're in UAS compatibility mode, other values should
+ // not occur.
+ //
+
+ if ( UserAll->All.LogonHours.LogonHours == NULL ) {
+
+ DWORD i;
+ for ( i=0; i<sizeof(up->uas_logon_hours); i++ ) {
+ up->uas_logon_hours[i] = 0xFF;
+ }
+
+ } else if ( UserAll->All.LogonHours.UnitsPerWeek ==
+ SAM_HOURS_PER_WEEK ) {
+
+ RtlCopyMemory(up->uas_logon_hours,
+ UserAll->All.LogonHours.LogonHours,
+ SAM_HOURS_PER_WEEK/8 );
+
+ //
+ // Convert from GMT relative to local time relative
+ //
+
+ (VOID) NetpRotateLogonHoursPhase2( up->uas_logon_hours,
+ SAM_HOURS_PER_WEEK,
+ RotateCount );
+
+ } else {
+ DWORD i;
+ Flags |= UF_ACCOUNTDISABLE;
+ for ( i=0; i<sizeof(up->uas_logon_hours); i++ ) {
+ up->uas_logon_hours[i] = 0xFF;
+ }
+ }
+
+
+ //
+ // Fill in the fixed length fields
+ //
+
+ OLD_TO_NEW_LARGE_INTEGER( UserAll->All.PasswordLastSet, TempTime );
+
+ SmbPutUlong( &up->uas_password_age,
+ NetpGetElapsedSeconds( &TempTime ) );
+ SmbPutUshort( &up->uas_priv, (USHORT) Priv );
+
+ SmbPutUlong( &up->uas_auth_flags, AuthFlags );
+
+ OLD_TO_NEW_LARGE_INTEGER( UserAll->All.LastLogon, TempTime );
+
+ if ( NT_SUCCESS(RtlSystemTimeToLocalTime(
+ &TempTime,
+ &LocalTime ))) {
+
+ if ( !RtlTimeToSecondsSince1970( &LocalTime, &TempUlong) ) {
+ TempUlong = 0;
+ }
+ }
+ else {
+ TempUlong = 0;
+ }
+ SmbPutUlong( &up->uas_last_logon, TempUlong );
+
+ OLD_TO_NEW_LARGE_INTEGER( UserAll->All.LastLogoff, TempTime );
+
+ if ( NT_SUCCESS(RtlSystemTimeToLocalTime(
+ &TempTime,
+ &LocalTime ))) {
+
+ if ( !RtlTimeToSecondsSince1970( &LocalTime, &TempUlong) ) {
+ TempUlong = 0;
+ }
+ }
+ else {
+ TempUlong = 0;
+ }
+ SmbPutUlong( &up->uas_last_logoff, TempUlong );
+
+ OLD_TO_NEW_LARGE_INTEGER( UserAll->All.AccountExpires, TempTime );
+
+ if ( NT_SUCCESS(RtlSystemTimeToLocalTime(
+ &TempTime,
+ &LocalTime ))) {
+
+ if ( !RtlTimeToSecondsSince1970( &LocalTime, &TempUlong) ) {
+ TempUlong = TIMEQ_FOREVER;
+ }
+ }
+ else {
+ TempUlong = TIMEQ_FOREVER;
+ }
+ SmbPutUlong( &up->uas_acct_expires, TempUlong );
+
+ SmbPutUlong( &up->uas_max_storage, USER_MAXSTORAGE_UNLIMITED );
+ SmbPutUshort( &up->uas_units_per_week, SAM_HOURS_PER_WEEK );
+
+
+ SmbPutUshort( &up->uas_bad_pw_count, UserAll->All.BadPasswordCount );
+ SmbPutUshort( &up->uas_num_logons, UserAll->All.LogonCount );
+
+
+ SmbPutUshort( &up->uas_country_code, UserAll->All.CountryCode );
+ SmbPutUshort( &up->uas_code_page, UserAll->All.CodePage );
+
+ OLD_TO_NEW_LARGE_INTEGER( UserAll->All.PasswordLastSet, TempTime );
+
+ if ( NT_SUCCESS(RtlSystemTimeToLocalTime(
+ &TempTime,
+ &LocalTime ))) {
+
+ if ( !RtlTimeToSecondsSince1970( &LocalTime, &TempUlong) ) {
+ TempUlong = 0;
+ }
+ }
+ else {
+ TempUlong = 0;
+ }
+ SmbPutUlong( &up->uas_last, TempUlong );
+
+ //
+ // Don't replicate password history. A downlevel BDC will never
+ // be promoted to a PDC.
+ //
+
+ RtlFillMemory( up->uas_old_passwds, sizeof(up->uas_old_passwds), 0xFF );
+
+
+
+ //
+ // Pack the variable length fields at the end of the record and leave
+ // and offset (from the top of this struct) in the struct.
+ //
+
+ Status = NlPackVarLenField( &UserAll->All.UserName,
+ BufferDescriptor,
+ &up->uas_name,
+ &RunningOffset,
+ LM20_UNLEN,
+ FALSE, // NOT OK to truncate
+ RelativeId );
+
+ if ( Status != STATUS_SUCCESS ) {
+ if ( Status == STATUS_INVALID_PARAMETER ) {
+ Flags |= UF_ACCOUNTDISABLE;
+ } else {
+ goto Cleanup;
+ }
+ }
+
+ Status = NlPackVarLenField( &UserAll->All.HomeDirectory,
+ BufferDescriptor,
+ &up->uas_home_dir,
+ &RunningOffset,
+ LM20_PATHLEN,
+ FALSE, // NOT OK to truncate
+ 0 ); // Not an account name
+
+ if ( Status != STATUS_SUCCESS ) {
+ if ( Status == STATUS_INVALID_PARAMETER ) {
+ Flags |= UF_ACCOUNTDISABLE;
+ } else {
+ goto Cleanup;
+ }
+ }
+
+ Status = NlPackVarLenField( &UserAll->All.AdminComment,
+ BufferDescriptor,
+ &up->uas_comment,
+ &RunningOffset,
+ LM20_MAXCOMMENTSZ,
+ TRUE, // OK to truncate
+ 0 ); // Not an account name
+
+ if ( Status != STATUS_SUCCESS ) {
+ goto Cleanup;
+ }
+
+ Status = NlPackVarLenField( &UserAll->All.ScriptPath,
+ BufferDescriptor,
+ &up->uas_script_path,
+ &RunningOffset,
+ LM20_PATHLEN,
+ FALSE, // NOT OK to truncate
+ 0 ); // Not an account name
+
+ if ( Status != STATUS_SUCCESS ) {
+ if ( Status == STATUS_INVALID_PARAMETER ) {
+ Flags |= UF_ACCOUNTDISABLE;
+ } else {
+ goto Cleanup;
+ }
+ }
+
+ Status = NlPackVarLenField( &UserAll->All.FullName,
+ BufferDescriptor,
+ &up->uas_full_name,
+ &RunningOffset,
+ LM20_MAXCOMMENTSZ,
+ TRUE, // OK to truncate
+ 0 ); // Not an account name
+
+ if ( Status != STATUS_SUCCESS ) {
+ goto Cleanup;
+ }
+
+ Status = NlPackVarLenField( &UserAll->All.UserComment,
+ BufferDescriptor,
+ &up->uas_usr_comment,
+ &RunningOffset,
+ LM20_MAXCOMMENTSZ,
+ TRUE, // OK to truncate
+ 0 ); // Not an account name
+
+ if ( Status != STATUS_SUCCESS ) {
+ goto Cleanup;
+ }
+
+ Status = NlPackVarLenField( &UserAll->All.Parameters,
+ BufferDescriptor,
+ &up->uas_parms,
+ &RunningOffset,
+ LM20_MAXCOMMENTSZ,
+ TRUE, // OK to truncate
+ 0 ); // Not an account name
+
+ if ( Status != STATUS_SUCCESS ) {
+ goto Cleanup;
+ }
+
+ //
+ // Downlevel BDCs expect blank separated workstation list.
+ //
+
+ NetpConvertWorkstationList( (PUNICODE_STRING) &UserAll->All.WorkStations );
+
+ Status = NlPackVarLenField( &UserAll->All.WorkStations,
+ BufferDescriptor,
+ &up->uas_workstations,
+ &RunningOffset,
+ MAXWORKSTATIONS * (LM20_CNLEN+1),
+ FALSE, // NOT OK to truncate
+ 0 ); // Not an account name
+
+ if ( Status != STATUS_SUCCESS ) {
+ if ( Status == STATUS_INVALID_PARAMETER ) {
+ Flags |= UF_ACCOUNTDISABLE;
+ } else {
+ goto Cleanup;
+ }
+ }
+
+
+ // Copy the LogonServer constant "\\*" into the uas_logon_server field.
+ // SAM doesn't support this field.
+ Status = NlPackVarLenField( &UasLogonServer,
+ BufferDescriptor,
+ &up->uas_logon_server,
+ &RunningOffset,
+ LM20_UNCLEN+1,
+ FALSE, // NOT OK to truncate
+ 0 ); // Not an account name
+
+ if ( Status != STATUS_SUCCESS ) {
+ if ( Status == STATUS_INVALID_PARAMETER ) {
+ Flags |= UF_ACCOUNTDISABLE;
+ } else {
+ goto Cleanup;
+ }
+ }
+
+ //
+ // Finally, store the flags.
+ //
+ // We may have or'ed in the account disable bit if the account could
+ // not properly be replicated to the downlevel client.
+ //
+ SmbPutUshort( &up->uas_flags, (USHORT) Flags );
+
+
+ //
+ // Put the final record length into the header.
+ //
+
+ PutUnalignedUshort( RecordSize, RunningOffset + NETLOGON_DELTA_HEADER_SIZE);
+
+
+ //
+ // All Done
+ //
+
+ Status = STATUS_SUCCESS;
+
+Cleanup:
+ (VOID) SamrCloseHandle( &UserHandle );
+
+ if ( UserAll != NULL ) {
+ NlPrint((NL_SYNC_MORE,
+ "NlPackUasUser '%wZ': returns %lX\n",
+ &UserAll->All.UserName,
+ Status ));
+ SamIFree_SAMPR_USER_INFO_BUFFER( UserAll, UserAllInformation );
+ } else {
+ NlPrint((NL_SYNC_MORE, "NlPackUasUser: returns %lX\n", Status ));
+ }
+
+ if ( Groups != NULL ) {
+ SamIFree_SAMPR_GET_GROUPS_BUFFER( Groups );
+ }
+
+ //
+ // On error,
+ // return the buffer descriptor to where it was when this routine
+ // started to allow the caller to recover as it wishes.
+ //
+
+ if( Status != STATUS_SUCCESS ) {
+ *BufferDescriptor = OriginalBufferDescriptor;
+ }
+
+ return Status;
+}
+
+
+NTSTATUS
+NlPackUasGroup (
+ IN ULONG RelativeId,
+ IN OUT PBUFFER_DESCRIPTOR BufferDescriptor,
+ PDB_INFO DBInfo,
+ PDWORD UasBuiltinGroups
+ )
+/*++
+
+Routine Description:
+
+ Pack a description of the specified group into the specified buffer.
+
+Arguments:
+
+ RelativeId - The relative Id of the group query.
+
+ BufferDescriptor - Points to a structure which describes the allocated
+ buffer.
+ This Routine updates EndOfVariableData and FixedDataEnd to reflect the
+ newly packed information.
+
+ DBInfo - Database info describing the SAM database to read from
+
+Return Value:
+
+ STATUS_SUCCESS -- ALL OK
+
+ STATUS_MORE_ENTRIES - The record does not fit into the buffer.
+
+ STATUS_* - Other status codes.
+
+--*/
+{
+ NTSTATUS Status;
+ SAMPR_HANDLE GroupHandle;
+ // SECURITY_INFORMATION SecurityInformation;
+ BUFFER_DESCRIPTOR OriginalBufferDescriptor;
+
+ //
+ // Record to pack into buffer.
+ //
+
+ PUSHORT RecordSize; // Pointer to record size field in record header.
+ PGROUP_ADD_SET up;
+ USHORT RunningOffset;
+ USHORT TempUshort;
+
+ //
+ // Information returned from SAM
+ //
+
+ PSAMPR_GROUP_INFO_BUFFER GroupGeneral = NULL;
+
+
+ //
+ // Initialization.
+ //
+
+ OriginalBufferDescriptor = *BufferDescriptor;
+
+ NlPrint((NL_SYNC_MORE, "NlPackUasGroup Rid=0x%lx\n", RelativeId));
+
+ //
+ // Open a handle to the specified group.
+ //
+
+ Status = SamrOpenGroup( DBInfo->DBHandle,
+ 0, // No desired access
+ RelativeId,
+ &GroupHandle );
+
+ if (!NT_SUCCESS(Status)) {
+ GroupHandle = NULL;
+ NlPrint((NL_CRITICAL,
+ "NlPackUasGroup: SamrOpenGroup returns %lX\n",
+ Status ));
+ return Status;
+ }
+
+ //
+ // Find out everything there is to know about the group.
+ //
+
+ Status = SamrQueryInformationGroup(
+ GroupHandle,
+ GroupGeneralInformation,
+ &GroupGeneral );
+
+ if (!NT_SUCCESS(Status)) {
+ GroupGeneral = NULL;
+ NlPrint((NL_CRITICAL,
+ "NlPackUasGroup: SamrQueryInformationGroup returns %lX\n",
+ Status ));
+ goto Cleanup;
+ }
+
+ //
+ // Pack the header into the return buffer and ensure the fixed length
+ // portion fits.
+ //
+
+ RunningOffset = offsetof( GROUP_ADD_SET, gas_groupname ),
+ Status = NlPackUasHeader( DELTA_GROUPADD,
+ RunningOffset,
+ &RecordSize,
+ BufferDescriptor );
+
+ if ( Status != STATUS_SUCCESS ) {
+ NlPrint((NL_CRITICAL,
+ "NlPackUasGroup: "
+ "NlPackUasHeader returns %lX\n",
+ Status ));
+ goto Cleanup;
+ }
+
+ up = (PGROUP_ADD_SET) BufferDescriptor->FixedDataEnd;
+ BufferDescriptor->FixedDataEnd += RunningOffset;
+
+
+ //
+ // Pack the variable length fields at the end of the record.
+ //
+ // Since the group name is at a well-known offset, just put its
+ // offset in a temporary.
+ //
+
+ Status = NlPackVarLenField( &GroupGeneral->General.Name,
+ BufferDescriptor,
+ &TempUshort,
+ &RunningOffset,
+ LM20_GNLEN,
+ FALSE, // NOT OK to truncate
+ RelativeId );
+
+ if ( Status != STATUS_SUCCESS ) {
+ if ( Status != STATUS_INVALID_PARAMETER ) {
+ goto Cleanup;
+ }
+ }
+
+ Status = NlPackVarLenField( &GroupGeneral->General.AdminComment,
+ BufferDescriptor,
+ &up->gas_comment,
+ &RunningOffset,
+ LM20_MAXCOMMENTSZ,
+ TRUE, // OK to truncate
+ 0 ); // Not an account name
+
+ if ( Status != STATUS_SUCCESS ) {
+ goto Cleanup;
+ }
+
+ //
+ // Put the final record length into the header.
+ //
+
+ PutUnalignedUshort( RecordSize, RunningOffset + NETLOGON_DELTA_HEADER_SIZE);
+
+
+ //
+ // Set UasBuiltinGroup flag if we have packed one of the uas
+ // builtin group.
+ //
+
+ (VOID) SpecialGroupOp((PUNICODE_STRING) &GroupGeneral->General.Name,
+ UasBuiltinGroups );
+
+ //
+ // All Done
+ //
+
+ Status = STATUS_SUCCESS;
+
+Cleanup:
+ (VOID) SamrCloseHandle( &GroupHandle );
+
+ if ( GroupGeneral != NULL ) {
+ NlPrint((NL_SYNC_MORE,
+ "NlPackUasGroup '%wZ': returns %lX\n",
+ &GroupGeneral->General.Name,
+ Status ));
+ SamIFree_SAMPR_GROUP_INFO_BUFFER( GroupGeneral,
+ GroupGeneralInformation );
+ } else {
+ NlPrint((NL_SYNC_MORE, "NlPackUasGroup: returns %lX\n", Status ));
+ }
+
+ //
+ // On error,
+ // return the buffer descriptor to where it was when this routine
+ // started to allow the caller to recover as it wishes.
+ //
+
+ if( Status != STATUS_SUCCESS ) {
+ *BufferDescriptor = OriginalBufferDescriptor;
+ }
+
+ return Status;
+}
+
+
+NTSTATUS
+NlPackUasBuiltinGroup(
+ IN DWORD Index,
+ IN OUT PBUFFER_DESCRIPTOR BufferDescriptor,
+ IN PDWORD UasBuiltinGroup
+ )
+/*++
+
+Routine Description:
+
+ Pack the UAS builtin groups (such as admins, users, guests ...) in
+ the given buffer. It uses UasBuiltinGroup flag to determine that the
+ given group is already packed in the buffer.
+
+Arguments:
+
+ Index - index of the built in group.
+
+ UasBuiltinGroup - is a flag that holds already packed groups status.
+
+Return Value:
+
+ STATUS_SUCCESS -- ALL OK
+
+ STATUS_MORE_ENTRIES - The record does not fit into the buffer.
+
+ STATUS_* - Other status codes.
+
+--*/
+{
+
+ NTSTATUS Status = STATUS_SUCCESS;
+ UNICODE_STRING GroupName;
+ UNICODE_STRING GroupComment;
+
+ BUFFER_DESCRIPTOR OriginalBufferDescriptor;
+
+ //
+ // Record to pack into buffer.
+ //
+
+ PUSHORT RecordSize; // Pointer to record size field in record header.
+ PGROUP_ADD_SET up;
+ USHORT RunningOffset;
+ USHORT TempUshort;
+ DWORD CurrentGroup = 0;
+
+#define UAS_BUILTIN_ADMINS_GROUP_INDEX 0x00
+#define UAS_BUILTIN_USERS_GROUP_INDEX 0x01
+#define UAS_BUILTIN_GUESTS_GROUP_INDEX 0x02
+
+#define UAS_BUILTIN__GROUP_COMMENT L"Uas Builtin group"
+
+ //
+ // Initialization.
+ //
+
+ OriginalBufferDescriptor = *BufferDescriptor;
+
+ NlPrint((NL_SYNC_MORE, "NlPackUasBuiltinGroup entered\n" ));
+
+ switch( Index ) {
+ case UAS_BUILTIN_ADMINS_GROUP_INDEX:
+
+ if( (*UasBuiltinGroup & UAS_BUILTIN_ADMINS_GROUP) ) {
+ //
+ // this group is already packed so pack a dummy record here
+ // so that the entries returned will show the relatity.
+ //
+ Status = NlPackUasHeader( DELTA_RESERVED_OPCODE,
+ 0,
+ &RecordSize,
+ BufferDescriptor );
+
+ goto Cleanup;
+ }
+ RtlInitUnicodeString( &GroupName, UAS_BUILTIN_ADMINS_GROUP_NAME );
+ CurrentGroup = UAS_BUILTIN_ADMINS_GROUP;
+
+ break;
+
+ case UAS_BUILTIN_USERS_GROUP_INDEX:
+
+ if( (*UasBuiltinGroup & UAS_BUILTIN_USERS_GROUP) ) {
+ //
+ // this group is already packed so pack a dummy record here
+ // so that the entries returned will show the relatity.
+ //
+ Status = NlPackUasHeader( DELTA_RESERVED_OPCODE,
+ 0,
+ &RecordSize,
+ BufferDescriptor );
+
+ goto Cleanup;
+ }
+ RtlInitUnicodeString( &GroupName, UAS_BUILTIN_USERS_GROUP_NAME );
+ CurrentGroup = UAS_BUILTIN_USERS_GROUP;
+
+ break;
+
+ case UAS_BUILTIN_GUESTS_GROUP_INDEX:
+
+ if( (*UasBuiltinGroup & UAS_BUILTIN_GUESTS_GROUP) ) {
+ //
+ // this group is already packed so pack a dummy record here
+ // so that the entries returned will show the relatity.
+ //
+ Status = NlPackUasHeader( DELTA_RESERVED_OPCODE,
+ 0,
+ &RecordSize,
+ BufferDescriptor );
+
+ goto Cleanup;
+ }
+ RtlInitUnicodeString( &GroupName, UAS_BUILTIN_GUESTS_GROUP_NAME);
+ CurrentGroup = UAS_BUILTIN_GUESTS_GROUP;
+
+ break;
+
+ default:
+ NlPrint((NL_CRITICAL,
+ "NlPackUasBuiltinGroup: unexpected index value %lX\n",
+ Index ));
+
+ goto Cleanup;
+ }
+
+ RtlInitUnicodeString( &GroupComment, UAS_BUILTIN__GROUP_COMMENT);
+
+ //
+ // Pack the header into the return buffer and ensure the fixed length
+ // portion fits.
+ //
+
+ RunningOffset = offsetof( GROUP_ADD_SET, gas_groupname ),
+ Status = NlPackUasHeader( DELTA_GROUPADD,
+ RunningOffset,
+ &RecordSize,
+ BufferDescriptor );
+
+ if ( Status != STATUS_SUCCESS ) {
+ goto Cleanup;
+ }
+
+ up = (PGROUP_ADD_SET) BufferDescriptor->FixedDataEnd;
+ BufferDescriptor->FixedDataEnd += RunningOffset;
+
+
+ //
+ // Pack the variable length fields at the end of the record.
+ //
+ // Since the group name is at a well-known offset, just put its
+ // offset in a temporary.
+ //
+
+ Status = NlPackVarLenField( (RPC_UNICODE_STRING *)&GroupName,
+ BufferDescriptor,
+ &TempUshort,
+ &RunningOffset,
+ LM20_GNLEN,
+ FALSE, // NOT OK to truncate
+ 0 );
+
+ if ( Status != STATUS_SUCCESS ) {
+ if ( Status != STATUS_INVALID_PARAMETER ) {
+ goto Cleanup;
+ }
+ }
+
+ Status = NlPackVarLenField( (RPC_UNICODE_STRING *)&GroupComment,
+ BufferDescriptor,
+ &up->gas_comment,
+ &RunningOffset,
+ LM20_MAXCOMMENTSZ,
+ TRUE, // OK to truncate
+ 0 ); // Not an account name
+
+ if ( Status != STATUS_SUCCESS ) {
+ goto Cleanup;
+ }
+
+ //
+ // Put the final record length into the header.
+ //
+
+ PutUnalignedUshort( RecordSize, RunningOffset + NETLOGON_DELTA_HEADER_SIZE);
+
+ //
+ // set the group bit to indicate we have packed this group.
+ //
+
+ *UasBuiltinGroup |= CurrentGroup;
+
+ //
+ // All Done
+ //
+
+ Status = STATUS_SUCCESS;
+
+Cleanup:
+
+ if( Status != STATUS_SUCCESS ) {
+
+ //
+ // restore buffer if we are unsuccessful
+ //
+
+ *BufferDescriptor = OriginalBufferDescriptor;
+ }
+
+ NlPrint((NL_SYNC_MORE, "NlPackUasBuiltinGroup: returns %lX\n", Status ));
+
+ return(Status);
+}
+
+
+NTSTATUS
+NlPackUasGroupMember (
+ IN ULONG RelativeId,
+ IN OUT PBUFFER_DESCRIPTOR BufferDescriptor,
+ PDB_INFO DBInfo
+ )
+/*++
+
+Routine Description:
+
+ Pack a description of the membership of the specified group into
+ the specified buffer.
+
+
+ For these cases we will send names of users which
+ currently members of this group. The receiver will
+ use this list to call NetGroupSetUser to replace
+ existing list for this user (on requestor).
+
+ The format for these packets will be a count field
+ indicating the number of user names to follow the
+ group name which starts immediately after count.
+
+
+Arguments:
+
+ RelativeId - The relative Id of the group query.
+
+ BufferDescriptor - Points to a structure which describes the allocated
+ buffer.
+ This Routine updates EndOfVariableData and FixedDataEnd to reflect the
+ newly packed information.
+
+ DBInfo - Database info describing the SAM database to read from
+
+Return Value:
+
+ STATUS_SUCCESS -- ALL OK
+
+ STATUS_MORE_ENTRIES - The record does not fit into the buffer.
+
+ STATUS_* - Other status codes.
+
+--*/
+{
+ NTSTATUS Status;
+ SAMPR_HANDLE GroupHandle;
+ BUFFER_DESCRIPTOR OriginalBufferDescriptor;
+
+ //
+ // Information returned from SAM
+ //
+
+ PSAMPR_GROUP_INFO_BUFFER GroupName = NULL;
+ PSAMPR_GET_MEMBERS_BUFFER MembersBuffer = NULL;
+ ULONG i;
+ ULONG UserMemberCount;
+
+ //
+ // Record to pack into buffer.
+ //
+
+ PUSHORT RecordSize; // Pointer to record size field in record header.
+ PGROUP_USERS up;
+ USHORT RunningOffset;
+ USHORT TempUshort;
+
+ //
+ // Initialization
+ //
+
+ OriginalBufferDescriptor = *BufferDescriptor;
+
+ NlPrint((NL_SYNC_MORE, "NlPackUasGroupMember Rid=0x%lx\n", RelativeId));
+
+ //
+ // Open a handle to the specified group.
+ //
+
+ Status = SamrOpenGroup( DBInfo->DBHandle,
+ 0, // No desired access
+ RelativeId,
+ &GroupHandle );
+
+ if (!NT_SUCCESS(Status)) {
+ GroupHandle = NULL;
+ NlPrint((NL_CRITICAL,
+ "NlPackUasGroupMember: SamrOpenGroup returns %lX\n",
+ Status ));
+ return Status;
+ }
+
+ //
+ // Find out everything there is to know about the group.
+ //
+
+ Status = SamrQueryInformationGroup(
+ GroupHandle,
+ GroupNameInformation,
+ &GroupName );
+
+ if (!NT_SUCCESS(Status)) {
+ GroupName = NULL;
+ NlPrint((NL_CRITICAL,
+ "NlPackUasGroupMember: SamrQueryInformationGroup returns %lX\n",
+ Status ));
+ goto Cleanup;
+ }
+
+ Status = SamrGetMembersInGroup( GroupHandle, &MembersBuffer );
+
+ if (!NT_SUCCESS(Status)) {
+ MembersBuffer = NULL;
+ NlPrint((NL_CRITICAL,
+ "NlPackUasGroupMember: SamrGetMembersInGroup returns %lX\n",
+ Status ));
+ goto Cleanup;
+ }
+
+ //
+ // Pack the header into the return buffer and ensure the fixed length
+ // portion fits.
+ //
+
+ RunningOffset = offsetof( GROUP_USERS, groupname ),
+ Status = NlPackUasHeader( DELTA_GROUPSETUSERS,
+ RunningOffset,
+ &RecordSize,
+ BufferDescriptor );
+
+ if ( Status != STATUS_SUCCESS ) {
+ NlPrint((NL_CRITICAL,
+ "NlPackUasGroupMember: NlPackUasHeader returns %lX\n",
+ Status ));
+ goto Cleanup;
+ }
+
+ up = (PGROUP_USERS) BufferDescriptor->FixedDataEnd;
+ BufferDescriptor->FixedDataEnd += RunningOffset;
+
+
+ //
+ // Pack the variable length fields at the end of the record.
+ //
+ // Since the group name is at a well-known offset, just put its
+ // offset in a temporary.
+ //
+
+ Status = NlPackVarLenField( &GroupName->Name.Name,
+ BufferDescriptor,
+ &TempUshort,
+ &RunningOffset,
+ LM20_GNLEN,
+ FALSE, // NOT OK to truncate
+ RelativeId );
+
+ if ( Status != STATUS_SUCCESS ) {
+ if ( Status != STATUS_INVALID_PARAMETER ) {
+ goto Cleanup;
+ }
+ }
+
+ //
+ // Pack the member names immediately following the Group Name
+ //
+ // Count the number of members which are users.
+ // Downlevel systems only understand members that are users.
+ //
+
+ UserMemberCount = 0;
+
+ for ( i=0; i < MembersBuffer->MemberCount; i++ ) {
+
+ SAMPR_HANDLE UserHandle = NULL;
+ PSAMPR_USER_INFO_BUFFER UserAccount = NULL;
+
+ //
+ // open user account
+ //
+
+ Status = SamrOpenUser( DBInfo->DBHandle,
+ 0,
+ MembersBuffer->Members[i],
+ &UserHandle );
+
+
+ if (!NT_SUCCESS(Status)) {
+
+ NlPrint((NL_CRITICAL,
+ "NlPackUasGroupMember: SamrOpenUser returns 0x%lx\n",
+ Status ));
+
+ goto Cleanup;
+ }
+
+ //
+ // query user information.
+ //
+
+ Status = SamrQueryInformationUser(
+ UserHandle,
+ UserAccountInformation,
+ &UserAccount );
+
+ if (!NT_SUCCESS(Status)) {
+
+ NlPrint((NL_CRITICAL,
+ "NlPackUasGroupMember: SamrQueryInformationUser returns 0x%lx\n",
+ Status ));
+
+ (VOID) SamrCloseHandle( &UserHandle );
+ goto Cleanup;
+ }
+
+ //
+ // ignore machine accounts.
+ //
+
+ if ( !(UserAccount->Account.UserAccountControl &
+ USER_MACHINE_ACCOUNT_MASK ) ) {
+
+ UserMemberCount ++;
+ Status = NlPackVarLenField( &UserAccount->Account.UserName,
+ BufferDescriptor,
+ &TempUshort,
+ &RunningOffset,
+ LM20_UNLEN,
+ FALSE, // NOT OK to truncate
+ MembersBuffer->Members[i] );
+
+ if ( Status != STATUS_SUCCESS ) {
+ if ( Status != STATUS_INVALID_PARAMETER ) {
+
+ (VOID) SamrCloseHandle( &UserHandle );
+
+ SamIFree_SAMPR_USER_INFO_BUFFER( UserAccount,
+ UserAccountInformation );
+
+ goto Cleanup;
+ }
+ }
+ }
+ else {
+
+ NlPrint((NL_SYNC_MORE,
+ "NlPackUasGroupMember skipping machine account member '%wZ' \n",
+ &UserAccount->Account.UserName ));
+ }
+
+ //
+ // cleanup user info
+ //
+
+ (VOID) SamrCloseHandle( &UserHandle );
+ UserHandle = NULL;
+
+ SamIFree_SAMPR_USER_INFO_BUFFER( UserAccount,
+ UserAccountInformation );
+ UserAccount = NULL;
+ }
+
+ SmbPutUshort( &up->count, (USHORT) UserMemberCount);
+
+ //
+ // Put the final record length into the header.
+ //
+
+ PutUnalignedUshort( RecordSize, RunningOffset + NETLOGON_DELTA_HEADER_SIZE);
+
+
+ //
+ // All Done
+ //
+
+ Status = STATUS_SUCCESS;
+
+Cleanup:
+
+ if( GroupHandle != NULL ) {
+ (VOID) SamrCloseHandle( &GroupHandle );
+ }
+
+ if ( MembersBuffer != NULL ) {
+ SamIFree_SAMPR_GET_MEMBERS_BUFFER( MembersBuffer );
+ }
+
+ if ( GroupName != NULL ) {
+ NlPrint((NL_SYNC_MORE,
+ "NlPackUasGroupMembers '%wZ': returns %lX\n",
+ &GroupName->Name.Name,
+ Status ));
+ SamIFree_SAMPR_GROUP_INFO_BUFFER( GroupName, GroupNameInformation );
+ } else {
+ NlPrint((NL_SYNC_MORE,
+ "NlPackUasGroupMembers '%lX': returns %lX\n",
+ RelativeId,
+ Status ));
+ }
+
+ //
+ // On error,
+ // return the buffer descriptor to where it was when this routine
+ // started to allow the caller to recover as it wishes.
+ //
+
+ if( Status != STATUS_SUCCESS ) {
+ *BufferDescriptor = OriginalBufferDescriptor;
+ }
+
+ return Status;
+}
+
+
+NTSTATUS
+NlPackUasUserGroupMember (
+ IN ULONG RelativeId,
+ IN OUT PBUFFER_DESCRIPTOR BufferDescriptor,
+ PDB_INFO DBInfo
+ )
+/*++
+
+Routine Description:
+
+ Pack a description of the group membership of the specified user
+ into the specified buffer.
+
+
+ For these cases we will send names of groups in which currently the
+ user is member. The receiver will use this list to call
+ NetUserSetGroups to replace existing list for this user (on
+ requestor).
+
+ The format for these packets will be a count field indicating the
+ number of user names to follow the user name which starts
+ immediately after count and then the group names.
+
+
+Arguments:
+
+ RelativeId - The relative Id of the group query.
+
+ BufferDescriptor - Points to a structure which describes the allocated
+ buffer.
+ This Routine updates EndOfVariableData and FixedDataEnd to reflect the
+ newly packed information.
+
+ DBInfo - Database info describing the SAM database to read from
+
+Return Value:
+
+ STATUS_SUCCESS -- ALL OK
+
+ STATUS_MORE_ENTRIES - The record does not fit into the buffer.
+
+ STATUS_* - Other status codes.
+
+--*/
+{
+ NTSTATUS Status;
+ SAMPR_HANDLE UserHandle;
+ BUFFER_DESCRIPTOR OriginalBufferDescriptor;
+
+ //
+ // Information returned from SAM
+ //
+
+ PSAMPR_USER_INFO_BUFFER UserAccount = NULL;
+ PSAMPR_GET_GROUPS_BUFFER GroupsBuffer = NULL;
+ ULONG i;
+ ULONG GroupMemberCount;
+ SAMPR_RETURNED_USTRING_ARRAY NameBuffer;
+ SAMPR_ULONG_ARRAY UseBuffer;
+
+ //
+ // Record to pack into buffer.
+ //
+
+ PUSHORT RecordSize; // Pointer to record size field in record header.
+ PUSER_GROUPS up;
+ USHORT RunningOffset;
+ USHORT TempUshort;
+
+ //
+ // Initialization
+ //
+
+ NameBuffer.Element = NULL;
+ UseBuffer.Element = NULL;
+ OriginalBufferDescriptor = *BufferDescriptor;
+
+ NlPrint((NL_SYNC_MORE, "NlPackUasUserGroupMember Rid=0x%lx\n", RelativeId));
+
+ //
+ // Open a handle to the specified group.
+ //
+
+ Status = SamrOpenUser( DBInfo->DBHandle,
+ 0, // No desired access
+ RelativeId,
+ &UserHandle );
+
+ if (!NT_SUCCESS(Status)) {
+ UserHandle = NULL;
+ NlPrint((NL_CRITICAL,
+ "NlPackUasUserGroupMember: SamrOpenUser returns %lX\n",
+ Status ));
+ return Status;
+ }
+
+ //
+ // Find out user name.
+ //
+
+ Status = SamrQueryInformationUser(
+ UserHandle,
+ UserAccountInformation,
+ &UserAccount );
+
+ if (!NT_SUCCESS(Status)) {
+ UserAccount = NULL;
+ NlPrint((NL_CRITICAL,
+ "NlPackUasUserGroupMember: SamrQueryInformationUser returns %lX\n",
+ Status ));
+ goto Cleanup;
+ }
+
+ //
+ // skip machine accounts
+ //
+
+ if ( UserAccount->Account.UserAccountControl &
+ USER_MACHINE_ACCOUNT_MASK ) {
+
+ NlPrint((NL_SYNC_MORE,
+ "NlPackUasUserGroupMember skipping machine account "
+ "groupmembership : '%wZ' \n",
+ &UserAccount->Account.UserName ));
+
+ Status = NlPackUasHeader( DELTA_RESERVED_OPCODE,
+ 0,
+ &RecordSize,
+ BufferDescriptor );
+ Status = STATUS_SUCCESS;
+
+ goto Cleanup;
+ }
+
+
+ Status = SamrGetGroupsForUser( UserHandle, &GroupsBuffer );
+
+ if (!NT_SUCCESS(Status)) {
+ GroupsBuffer = NULL;
+ NlPrint((NL_CRITICAL,
+ "NlPackUasUserGroupMember: SamrGetGroupsForUsers returns %lX\n", Status ));
+ goto Cleanup;
+ }
+
+ // Sam doesn't like looking up ID if Members is NULL
+ if ( GroupsBuffer->MembershipCount != 0 ) {
+
+ PULONG GroupIDs;
+
+ GroupIDs = (PULONG) MIDL_user_allocate(
+ GroupsBuffer->MembershipCount * sizeof (ULONG) );
+
+ if( GroupIDs == NULL ) {
+ Status = STATUS_NO_MEMORY;
+
+ NlPrint((NL_CRITICAL,
+ "NlPackUasUserGroupMember: out of memory %lX\n", Status ));
+ goto Cleanup;
+ }
+
+
+ for ( i = 0; i < GroupsBuffer->MembershipCount; i++ ) {
+ GroupIDs[i] = GroupsBuffer->Groups[i].RelativeId;
+ }
+
+ Status = SamrLookupIdsInDomain(
+ DBInfo->DBHandle,
+ GroupsBuffer->MembershipCount,
+ GroupIDs,
+ &NameBuffer,
+ &UseBuffer );
+
+ //
+ // freeup local array anyway.
+ //
+
+ MIDL_user_free( GroupIDs);
+
+ if (!NT_SUCCESS(Status)) {
+
+ NameBuffer.Element = NULL;
+ UseBuffer.Element = NULL;
+
+ NlPrint((NL_CRITICAL,
+ "NlPackUasUserGroupMember: SamrLookupIdsInDomain returns %lX\n", Status ));
+ goto Cleanup;
+ }
+
+ NlAssert( GroupsBuffer->MembershipCount == UseBuffer.Count );
+ NlAssert( GroupsBuffer->MembershipCount == NameBuffer.Count );
+ }
+
+ //
+ // Pack the header into the return buffer and ensure the fixed length
+ // portion fits.
+ //
+
+ RunningOffset = offsetof( USER_GROUPS, username ),
+ Status = NlPackUasHeader( DELTA_USERSETGROUPS,
+ RunningOffset,
+ &RecordSize,
+ BufferDescriptor );
+
+ if ( Status != STATUS_SUCCESS ) {
+ NlPrint((NL_CRITICAL,
+ "NlPackUasUserGroupMember: NlPackUasHeader returns %lX\n", Status ));
+ goto Cleanup;
+ }
+
+ up = (PUSER_GROUPS) BufferDescriptor->FixedDataEnd;
+ BufferDescriptor->FixedDataEnd += RunningOffset;
+
+
+ //
+ // Pack the variable length fields at the end of the record.
+ //
+ // Since the group name is at a well-known offset, just put its
+ // offset in a temporary.
+ //
+
+ Status = NlPackVarLenField( &UserAccount->Account.UserName,
+ BufferDescriptor,
+ &TempUshort,
+ &RunningOffset,
+ LM20_UNLEN,
+ FALSE, // NOT OK to truncate
+ RelativeId );
+
+ if ( Status != STATUS_SUCCESS ) {
+
+ if ( Status != STATUS_INVALID_PARAMETER ) {
+ goto Cleanup;
+ }
+
+ }
+
+ //
+ // Pack the member names immediately following the Group Name
+ //
+ // Count the number of groups in which user members.
+ // Downlevel systems only understand groups.
+ //
+
+ GroupMemberCount = 0;
+
+ for ( i=0; i < GroupsBuffer->MembershipCount; i++ ) {
+
+ if ( UseBuffer.Element[i] == SidTypeGroup ) {
+
+ //
+ // ignore special group membership.
+ //
+
+ if( !SpecialGroupOp( (PUNICODE_STRING) &NameBuffer.Element[i], NULL ) ) {
+
+ GroupMemberCount ++;
+ Status = NlPackVarLenField( &NameBuffer.Element[i],
+ BufferDescriptor,
+ &TempUshort,
+ &RunningOffset,
+ LM20_UNLEN,
+ FALSE, // NOT OK to truncate
+ GroupsBuffer->Groups[i].RelativeId );
+
+ if ( Status != STATUS_SUCCESS ) {
+
+ if ( Status != STATUS_INVALID_PARAMETER ) {
+ goto Cleanup;
+ }
+
+ }
+ }
+ }
+ }
+
+ SmbPutUshort( &up->count, (USHORT) GroupMemberCount);
+
+ //
+ // Put the final record length into the header.
+ //
+
+ PutUnalignedUshort( RecordSize, RunningOffset + NETLOGON_DELTA_HEADER_SIZE);
+
+
+ //
+ // All Done
+ //
+
+ Status = STATUS_SUCCESS;
+
+Cleanup:
+
+ if( UserHandle != NULL ) {
+ (VOID) SamrCloseHandle( &UserHandle );
+ }
+
+ if ( GroupsBuffer != NULL ) {
+ SamIFree_SAMPR_GET_GROUPS_BUFFER( GroupsBuffer );
+ }
+
+ if ( UserAccount != NULL ) {
+ NlPrint((NL_SYNC_MORE,
+ "NlPackUasUserGroupMembers '%wZ': returns %lX\n",
+ &UserAccount->Account.UserName,
+ Status ));
+ SamIFree_SAMPR_USER_INFO_BUFFER( UserAccount, UserAccountInformation );
+
+ } else {
+ NlPrint((NL_SYNC_MORE,
+ "NlPackUasUserGroupMembers '%lX': returns %lX\n",
+ RelativeId,
+ Status ));
+ }
+
+ SamIFree_SAMPR_RETURNED_USTRING_ARRAY( &NameBuffer );
+ SamIFree_SAMPR_ULONG_ARRAY( &UseBuffer );
+
+ //
+ // On error,
+ // return the buffer descriptor to where it was when this routine
+ // started to allow the caller to recover as it wishes.
+ //
+
+ if( Status != STATUS_SUCCESS ) {
+ *BufferDescriptor = OriginalBufferDescriptor;
+ }
+
+ return Status;
+}
+
+
+NTSTATUS
+NlPackUasDomain (
+ IN OUT PBUFFER_DESCRIPTOR BufferDescriptor,
+ PDB_INFO DBInfo
+ )
+/*++
+
+Routine Description:
+
+ Pack a description of the sam domain into the specified buffer.
+
+Arguments:
+
+ BufferDescriptor - Points to a structure which describes the allocated
+ buffer.
+ This Routine updates EndOfVariableData and FixedDataEnd to reflect the
+ newly packed information.
+
+ DBInfo - Database info describing the SAM database to read from
+
+Return Value:
+
+ STATUS_SUCCESS -- ALL OK
+
+ STATUS_INVALID_PARAMETER - The specified user has some attribute which
+ prevents it from being replicated to a downlevel client.
+
+ STATUS_MORE_ENTRIES - The record does not fit into the buffer.
+
+ STATUS_* - Other status codes.
+
+--*/
+{
+ NTSTATUS Status;
+ BUFFER_DESCRIPTOR OriginalBufferDescriptor;
+
+ //
+ // Information returned from SAM
+ //
+
+ PSAMPR_DOMAIN_INFO_BUFFER DomainLogoff = NULL;
+ PSAMPR_DOMAIN_INFO_BUFFER DomainPassword = NULL;
+ LARGE_INTEGER TempTime;
+
+ //
+ // Record to pack into buffer.
+ //
+
+ PUSHORT RecordSize; // Pointer to record size field in record header.
+ PUSER_MODALS up;
+
+ //
+ // Initialization.
+ //
+
+ OriginalBufferDescriptor = *BufferDescriptor;
+
+ //
+ // Find out everything there is to know about the domain.
+ //
+
+ Status = SamrQueryInformationDomain(
+ DBInfo->DBHandle,
+ DomainLogoffInformation,
+ &DomainLogoff );
+
+ if (!NT_SUCCESS(Status)) {
+ DomainLogoff = NULL;
+ goto Cleanup;
+ }
+
+ Status = SamrQueryInformationDomain(
+ DBInfo->DBHandle,
+ DomainPasswordInformation,
+ &DomainPassword );
+
+ if (!NT_SUCCESS(Status)) {
+ DomainPassword = NULL;
+ goto Cleanup;
+ }
+
+
+ //
+ // Pack the header into the return buffer and ensure the fixed length
+ // portion fits.
+ //
+ // Fill in the record size too (since this is a fixed length record).
+ //
+
+ Status = NlPackUasHeader( DELTA_USERMODALSSET,
+ sizeof(USER_MODALS),
+ &RecordSize,
+ BufferDescriptor );
+
+ if ( Status != STATUS_SUCCESS ) {
+ goto Cleanup;
+ }
+
+ up = (PUSER_MODALS) BufferDescriptor->FixedDataEnd;
+ BufferDescriptor->FixedDataEnd += sizeof(USER_MODALS);
+
+
+ //
+ // Fill in the fixed length fields
+ //
+
+ SmbPutUshort( &up->umod_min_passwd_len,
+ DomainPassword->Password.MinPasswordLength );
+
+
+ OLD_TO_NEW_LARGE_INTEGER( DomainPassword->Password.MaxPasswordAge, TempTime );
+
+ SmbPutUlong(
+ &up->umod_max_passwd_age,
+ NetpDeltaTimeToSeconds( TempTime ));
+
+ OLD_TO_NEW_LARGE_INTEGER( DomainPassword->Password.MinPasswordAge, TempTime );
+
+ SmbPutUlong(
+ &up->umod_min_passwd_age,
+ NetpDeltaTimeToSeconds( TempTime ));
+
+ OLD_TO_NEW_LARGE_INTEGER( DomainLogoff->Logoff.ForceLogoff, TempTime );
+
+ SmbPutUlong( &up->umod_force_logoff,
+ NetpDeltaTimeToSeconds( TempTime ));
+
+
+ // Don't set the password history length greater than lanman's
+ if ( DomainPassword->Password.PasswordHistoryLength > DEF_MAX_PWHIST ) {
+ DomainPassword->Password.PasswordHistoryLength = DEF_MAX_PWHIST;
+ }
+ SmbPutUshort( &up->umod_password_hist_len,
+ DomainPassword->Password.PasswordHistoryLength);
+
+ // NT does do lockout (so the LM 2.1 BDCs in our domain shouldn't either)
+ SmbPutUshort( &up->umod_lockout_count, 0 );
+
+
+ //
+ // All Done
+ //
+
+ Status = STATUS_SUCCESS;
+
+Cleanup:
+
+ if ( DomainPassword != NULL ) {
+ SamIFree_SAMPR_DOMAIN_INFO_BUFFER( DomainPassword,
+ DomainPasswordInformation );
+ }
+
+ if ( DomainLogoff != NULL ) {
+ SamIFree_SAMPR_DOMAIN_INFO_BUFFER( DomainLogoff,
+ DomainLogoffInformation );
+ }
+
+ //
+ // On error,
+ // return the buffer descriptor to where it was when this routine
+ // started to allow the caller to recover as it wishes.
+ //
+
+ if( Status != STATUS_SUCCESS ) {
+ *BufferDescriptor = OriginalBufferDescriptor;
+ }
+
+ return Status;
+}
+
+
+
+NTSTATUS
+NlPackUasDelete (
+ IN NETLOGON_DELTA_TYPE DeltaType,
+ IN ULONG RelativeId,
+ IN LPWSTR AccountName,
+ IN OUT PBUFFER_DESCRIPTOR BufferDescriptor,
+ PDB_INFO DBInfo
+ )
+/*++
+
+Routine Description:
+
+ Pack a description of record deletion into the specified buffer.
+
+Arguments:
+
+ DeltaType - The specific delta type for this deletion.
+
+ RelativeId - The relative Id of the group or user to delete.
+
+ AccountName - The Account Name of the group or user to delete.
+
+ BufferDescriptor - Points to a structure which describes the allocated
+ buffer.
+ This Routine updates EndOfVariableData and FixedDataEnd to reflect the
+ newly packed information.
+
+ DBInfo - Database info describing the SAM database to read from
+
+Return Value:
+
+ STATUS_SUCCESS -- ALL OK
+
+ STATUS_INVALID_PARAMETER - The specified user has some attribute which
+ prevents it from being replicated to a downlevel client.
+
+ STATUS_MORE_ENTRIES - The record does not fit into the buffer.
+
+ STATUS_* - Other status codes.
+
+--*/
+{
+ NTSTATUS Status;
+ BUFFER_DESCRIPTOR OriginalBufferDescriptor;
+ UNICODE_STRING AccountNameString;
+
+ //
+ // Record to pack into buffer.
+ //
+
+ PUSHORT RecordSize; // Pointer to record size field in record header.
+ PUSER_GROUP_DEL up;
+ USHORT RunningOffset;
+ USHORT TempUshort;
+ BYTE UasDeltaType;
+
+ //
+ // Initialization.
+ //
+
+ OriginalBufferDescriptor = *BufferDescriptor;
+
+ //
+ // Pack the header into the return buffer and ensure the fixed length
+ // portion fits.
+ //
+
+ RunningOffset = 0; // There are no fixed length fields in this structure
+
+ if( (DeltaType == DeleteUser) ||
+ (DeltaType == RenameUser) ) {
+
+ UasDeltaType = DELTA_USERDEL;
+ }
+ else {
+
+ UasDeltaType = DELTA_GROUPDEL;
+ }
+
+ Status = NlPackUasHeader(
+ UasDeltaType,
+ RunningOffset,
+ &RecordSize,
+ BufferDescriptor );
+
+ if ( Status != STATUS_SUCCESS ) {
+ goto Cleanup;
+ }
+
+ up = (PUSER_GROUP_DEL) BufferDescriptor->FixedDataEnd;
+
+
+ //
+ // The group/user name to delete is the only field in the structure.
+ //
+ // Since the group/user name is at a well-known offset, just put its
+ // offset in a temporary.
+ //
+
+ RtlInitUnicodeString( &AccountNameString, AccountName );
+
+ Status = NlPackVarLenField( (PRPC_UNICODE_STRING)&AccountNameString,
+ BufferDescriptor,
+ &TempUshort,
+ &RunningOffset,
+ LM20_GNLEN,
+ FALSE, // NOT OK to truncate
+ RelativeId );
+
+ if ( Status != STATUS_SUCCESS ) {
+ if ( Status != STATUS_INVALID_PARAMETER ) {
+ goto Cleanup;
+ }
+ }
+
+
+ //
+ // Put the final record length into the header.
+ //
+
+ PutUnalignedUshort( RecordSize, RunningOffset + NETLOGON_DELTA_HEADER_SIZE);
+
+
+ //
+ // All Done
+ //
+
+ Status = STATUS_SUCCESS;
+
+Cleanup:
+
+ //
+ // On error,
+ // return the buffer descriptor to where it was when this routine
+ // started to allow the caller to recover as it wishes.
+ //
+
+ if( Status != STATUS_SUCCESS ) {
+ *BufferDescriptor = OriginalBufferDescriptor;
+ }
+
+ return Status;
+ UNREFERENCED_PARAMETER( DBInfo );
+}
diff --git a/private/net/svcdlls/logonsrv/server/replutil.c b/private/net/svcdlls/logonsrv/server/replutil.c
new file mode 100644
index 000000000..7ab4927bc
--- /dev/null
+++ b/private/net/svcdlls/logonsrv/server/replutil.c
@@ -0,0 +1,3608 @@
+/*++
+
+Copyright (c) 1987-1991 Microsoft Corporation
+
+Module Name:
+
+ replutil.c
+
+Abstract:
+
+ Low level functions for SSI Replication apis
+
+Author:
+
+ Ported from Lan Man 2.0
+
+Environment:
+
+ User mode only.
+ Contains NT-specific code.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+ 22-Jul-1991 (cliffv)
+ Ported to NT. Converted to NT style.
+
+ 02-Jan-1992 (madana)
+ added support for builtin/multidomain replication.
+
+ 04-Apr-1992 (madana)
+ Added support for LSA replication.
+
+--*/
+
+//
+// Common include files.
+//
+
+#include <logonsrv.h> // Include files common to entire service
+
+//
+// Include files specific to this .c file
+//
+
+#include <align.h>
+#include <accessp.h> // NetpConvertWorkstationList
+#include <replutil.h> // Local procedure forwards
+#include <lsarepl.h>
+
+
+
+DWORD
+NlCopyUnicodeString (
+ IN PUNICODE_STRING InString,
+ OUT PUNICODE_STRING OutString
+ )
+
+/*++
+
+Routine Description:
+
+ This routine copies the input string to the output. It assumes that
+ the input string is allocated by MIDL_user_allocate() and sets the
+ input string buffer pointer to NULL so that the buffer will be not
+ freed on return.
+
+Arguments:
+
+ InString - Points to the UNICODE string to copy.
+
+ OutString - Points to the UNICODE string which will be updated to point
+ to the input string.
+
+Return Value:
+
+ Return the size of the MIDL buffer.
+
+--*/
+{
+ if ( InString->Length == 0 || InString->Buffer == NULL ) {
+ OutString->Length = 0;
+ OutString->MaximumLength = 0;
+ OutString->Buffer = NULL;
+ } else {
+ OutString->Length = InString->Length;
+ OutString->MaximumLength = InString->Length;
+ OutString->Buffer = InString->Buffer;
+ InString->Buffer = NULL;
+ }
+
+ return( OutString->MaximumLength );
+}
+
+
+DWORD
+NlCopyData(
+ IN LPBYTE *InData,
+ OUT LPBYTE *OutData,
+ DWORD DataLength
+ )
+
+/*++
+
+Routine Description:
+
+ This routine copies the input data pointer to output data pointer.
+ It assumes that the input data buffer is allocated by the
+ MIDL_user_allocate() and sets the input buffer buffer pointer to
+ NULL on return so that the data buffer will not be freed by SamIFree
+ rountine.
+
+Arguments:
+
+ InData - Points to input data buffer pointer.
+
+ OutString - Pointer to output data buffer pointer.
+
+ DataLength - Length of input data.
+
+Return Value:
+
+ Return the size of the data copied.
+
+--*/
+{
+ *OutData = *InData;
+ *InData = NULL;
+
+ return(DataLength);
+}
+
+
+VOID
+NlFreeDBDelta(
+ IN PNETLOGON_DELTA_ENUM Delta
+ )
+/*++
+
+Routine Description:
+
+ This routine will free the midl buffers that are allocated for
+ a delta. This routine does nothing but call the midl generated free
+ routine.
+
+Arguments:
+
+ Delta: pointer to the delta structure which has to be freed.
+
+Return Value:
+
+ nothing
+
+--*/
+{
+ if( Delta != NULL ) {
+ _fgs__NETLOGON_DELTA_ENUM (Delta);
+ }
+}
+
+
+VOID
+NlFreeDBDeltaArray(
+ IN PNETLOGON_DELTA_ENUM DeltaArray,
+ IN DWORD ArraySize
+ )
+/*++
+
+Routine Description:
+
+ This routine will free up all delta entries in enum array and the
+ array itself.
+
+Arguments:
+
+ Delta: pointer to the delta structure array.
+
+ ArraySize: num of delta structures in the array.
+
+Return Value:
+
+ nothing
+
+--*/
+{
+ DWORD i;
+
+ if( DeltaArray != NULL ) {
+
+ for( i = 0; i < ArraySize; i++) {
+ NlFreeDBDelta( &DeltaArray[i] );
+ }
+
+ MIDL_user_free( DeltaArray );
+ }
+}
+
+
+
+NTSTATUS
+NlPackSamUser (
+ IN ULONG RelativeId,
+ IN OUT PNETLOGON_DELTA_ENUM Delta,
+ IN PDB_INFO DBInfo,
+ IN LPDWORD BufferSize,
+ IN PSESSION_INFO SessionInfo
+ )
+/*++
+
+Routine Description:
+
+ Pack a description of the specified user into the specified buffer.
+
+Arguments:
+
+ RelativeId - The relative Id of the user query.
+
+ Delta: pointer to the delta structure where the new delta will
+ be returned.
+
+ DBInfo: pointer to the database info structure.
+
+ BufferSize: size of MIDL buffer that is consumed for this delta is
+ returned here.
+
+ SessionInfo: Info describing BDC that's calling us
+
+Return Value:
+
+ NT status code.
+
+--*/
+{
+ NTSTATUS Status;
+ SAMPR_HANDLE UserHandle = NULL;
+ PNETLOGON_DELTA_USER DeltaUser;
+ PSAMPR_USER_INFO_BUFFER UserAll = NULL;
+
+
+
+ DEFPACKTIMER;
+ DEFSAMTIMER;
+
+ INITPACKTIMER;
+ INITSAMTIMER;
+
+ STARTPACKTIMER;
+
+ NlPrint((NL_SYNC_MORE, "Packing User Object %lx\n", RelativeId));
+
+ *BufferSize = 0;
+
+ Delta->DeltaType = AddOrChangeUser;
+ Delta->DeltaID.Rid = RelativeId;
+ Delta->DeltaUnion.DeltaUser = NULL;
+
+ //
+ // Open a handle to the specified user.
+ //
+
+ STARTSAMTIMER;
+
+ Status = SamrOpenUser( DBInfo->DBHandle,
+ 0, // No desired access
+ RelativeId,
+ &UserHandle );
+ STOPSAMTIMER;
+
+
+ if (!NT_SUCCESS(Status)) {
+ UserHandle = NULL;
+ goto Cleanup;
+ }
+
+
+
+ //
+ // Query everything there is to know about this user.
+ //
+
+ STARTSAMTIMER;
+
+ Status = SamrQueryInformationUser(
+ UserHandle,
+ UserInternal3Information,
+ &UserAll );
+ STOPSAMTIMER;
+
+
+ if (!NT_SUCCESS(Status)) {
+ UserAll = NULL;
+ goto Cleanup;
+ }
+
+
+ NlPrint((NL_SYNC_MORE,
+ "\t User Object name %wZ\n",
+ (PUNICODE_STRING)&UserAll->Internal3.I1.UserName));
+
+#define FIELDS_USED ( USER_ALL_USERNAME | \
+ USER_ALL_FULLNAME | \
+ USER_ALL_USERID | \
+ USER_ALL_PRIMARYGROUPID | \
+ USER_ALL_HOMEDIRECTORY | \
+ USER_ALL_HOMEDIRECTORYDRIVE | \
+ USER_ALL_SCRIPTPATH | \
+ USER_ALL_PROFILEPATH | \
+ USER_ALL_ADMINCOMMENT | \
+ USER_ALL_WORKSTATIONS | \
+ USER_ALL_LOGONHOURS | \
+ USER_ALL_LASTLOGON | \
+ USER_ALL_LASTLOGOFF | \
+ USER_ALL_BADPASSWORDCOUNT | \
+ USER_ALL_LOGONCOUNT | \
+ USER_ALL_PASSWORDLASTSET | \
+ USER_ALL_ACCOUNTEXPIRES | \
+ USER_ALL_USERACCOUNTCONTROL | \
+ USER_ALL_USERCOMMENT | \
+ USER_ALL_COUNTRYCODE | \
+ USER_ALL_CODEPAGE | \
+ USER_ALL_PARAMETERS | \
+ USER_ALL_NTPASSWORDPRESENT | \
+ USER_ALL_LMPASSWORDPRESENT | \
+ USER_ALL_PRIVATEDATA | \
+ USER_ALL_SECURITYDESCRIPTOR )
+
+ NlAssert( (UserAll->Internal3.I1.WhichFields & FIELDS_USED) == FIELDS_USED );
+
+
+
+ //
+ // Allocate a buffer to return to the caller.
+ //
+
+ DeltaUser = (PNETLOGON_DELTA_USER)
+ MIDL_user_allocate( sizeof(NETLOGON_DELTA_USER) );
+
+ if (DeltaUser == NULL) {
+ Status = STATUS_NO_MEMORY;
+ goto Cleanup;
+ }
+
+ //
+ // wipe off the buffer so that cleanup will not be in fault.
+ //
+
+ RtlZeroMemory( DeltaUser, sizeof(NETLOGON_DELTA_USER) );
+
+ Delta->DeltaUnion.DeltaUser = DeltaUser;
+ *BufferSize += sizeof(NETLOGON_DELTA_USER);
+
+ *BufferSize += NlCopyUnicodeString(
+ (PUNICODE_STRING)&UserAll->Internal3.I1.UserName,
+ &DeltaUser->UserName );
+
+ *BufferSize += NlCopyUnicodeString(
+ (PUNICODE_STRING)&UserAll->Internal3.I1.FullName,
+ &DeltaUser->FullName );
+
+ DeltaUser->UserId = UserAll->Internal3.I1.UserId;
+ DeltaUser->PrimaryGroupId = UserAll->Internal3.I1.PrimaryGroupId;
+
+ *BufferSize += NlCopyUnicodeString(
+ (PUNICODE_STRING)&UserAll->Internal3.I1.HomeDirectory,
+ &DeltaUser->HomeDirectory );
+
+ *BufferSize += NlCopyUnicodeString(
+ (PUNICODE_STRING)&UserAll->Internal3.I1.HomeDirectoryDrive,
+ &DeltaUser->HomeDirectoryDrive );
+
+ *BufferSize += NlCopyUnicodeString(
+ (PUNICODE_STRING)&UserAll->Internal3.I1.ScriptPath,
+ &DeltaUser->ScriptPath );
+
+ *BufferSize += NlCopyUnicodeString(
+ (PUNICODE_STRING)&UserAll->Internal3.I1.AdminComment,
+ &DeltaUser->AdminComment );
+
+ *BufferSize += NlCopyUnicodeString(
+ (PUNICODE_STRING)&UserAll->Internal3.I1.WorkStations,
+ &DeltaUser->WorkStations );
+
+ DeltaUser->LastLogon = UserAll->Internal3.I1.LastLogon;
+ DeltaUser->LastLogoff = UserAll->Internal3.I1.LastLogoff;
+
+ //
+ // Copy Logon Hours
+ //
+
+ DeltaUser->LogonHours.UnitsPerWeek = UserAll->Internal3.I1.LogonHours.UnitsPerWeek;
+ DeltaUser->LogonHours.LogonHours = UserAll->Internal3.I1.LogonHours.LogonHours;
+ UserAll->Internal3.I1.LogonHours.LogonHours = NULL; // Don't let SAM free this.
+ *BufferSize += (UserAll->Internal3.I1.LogonHours.UnitsPerWeek + 7) / 8;
+
+
+
+ DeltaUser->BadPasswordCount = UserAll->Internal3.I1.BadPasswordCount;
+ DeltaUser->LogonCount = UserAll->Internal3.I1.LogonCount;
+
+ DeltaUser->PasswordLastSet = UserAll->Internal3.I1.PasswordLastSet;
+ DeltaUser->AccountExpires = UserAll->Internal3.I1.AccountExpires;
+
+ //
+ // Don't copy lockout bit to BDC unless it understands it.
+ //
+
+ DeltaUser->UserAccountControl = UserAll->Internal3.I1.UserAccountControl;
+ if ( (SessionInfo->NegotiatedFlags & NETLOGON_SUPPORTS_ACCOUNT_LOCKOUT) == 0 ){
+ DeltaUser->UserAccountControl &= ~USER_ACCOUNT_AUTO_LOCKED;
+ }
+
+ *BufferSize += NlCopyUnicodeString(
+ (PUNICODE_STRING)&UserAll->Internal3.I1.UserComment,
+ &DeltaUser->UserComment );
+
+ *BufferSize += NlCopyUnicodeString(
+ (PUNICODE_STRING)&UserAll->Internal3.I1.Parameters,
+ &DeltaUser->Parameters );
+
+ DeltaUser->CountryCode = UserAll->Internal3.I1.CountryCode;
+ DeltaUser->CodePage = UserAll->Internal3.I1.CodePage;
+
+ //
+ // Set private data.
+ // Includes passwords and password history.
+ //
+
+ DeltaUser->PrivateData.SensitiveData = UserAll->Internal3.I1.PrivateDataSensitive;
+
+ if ( UserAll->Internal3.I1.PrivateDataSensitive ) {
+
+ CRYPT_BUFFER Data;
+
+ //
+ // encrypt private data using session key
+ // Re-use the SAM's buffer and encrypt it in place.
+ //
+
+ Data.Length = Data.MaximumLength = UserAll->Internal3.I1.PrivateData.Length;
+ Data.Buffer = (PUCHAR) UserAll->Internal3.I1.PrivateData.Buffer;
+ UserAll->Internal3.I1.PrivateData.Buffer = NULL;
+
+ Status = NlEncryptSensitiveData( &Data, SessionInfo );
+
+ if( !NT_SUCCESS(Status) ) {
+ goto Cleanup;
+ }
+
+ DeltaUser->PrivateData.DataLength = Data.Length;
+ DeltaUser->PrivateData.Data = Data.Buffer;
+ } else {
+
+ DeltaUser->PrivateData.DataLength = UserAll->Internal3.I1.PrivateData.Length;
+ DeltaUser->PrivateData.Data = (PUCHAR) UserAll->Internal3.I1.PrivateData.Buffer;
+
+ UserAll->Internal3.I1.PrivateData.Buffer = NULL;
+ }
+
+ { // ?? Macro requires a Local named SecurityDescriptor
+ PSAMPR_SR_SECURITY_DESCRIPTOR SecurityDescriptor;
+ SecurityDescriptor = &UserAll->Internal3.I1.SecurityDescriptor;
+ DELTA_SECOBJ_INFO(DeltaUser);
+ }
+
+ INIT_PLACE_HOLDER(DeltaUser);
+
+ //
+ // copy profile path in DummyStrings
+ //
+
+ *BufferSize += NlCopyUnicodeString(
+ (PUNICODE_STRING)&UserAll->Internal3.I1.ProfilePath,
+ &DeltaUser->DummyString1 );
+
+ //
+ // Copy LastBadPasswordTime to DummyLong1 and DummyLong2.
+ //
+
+ DeltaUser->DummyLong1 = UserAll->Internal3.LastBadPasswordTime.HighPart;
+ DeltaUser->DummyLong2 = UserAll->Internal3.LastBadPasswordTime.LowPart;
+
+ //
+ // All Done
+ //
+
+ Status = STATUS_SUCCESS;
+
+
+Cleanup:
+
+
+ STARTSAMTIMER;
+
+ if( UserHandle != NULL ) {
+ (VOID) SamrCloseHandle( &UserHandle );
+ }
+
+ if ( UserAll != NULL ) {
+ SamIFree_SAMPR_USER_INFO_BUFFER( UserAll, UserInternal3Information );
+ }
+
+ STOPSAMTIMER;
+
+ if( !NT_SUCCESS(Status) ) {
+ NlFreeDBDelta( Delta );
+ *BufferSize = 0;
+ }
+
+ STOPPACKTIMER;
+
+ NlPrint((NL_REPL_OBJ_TIME,"Time taken to pack USER object:\n"));
+ PRINTPACKTIMER;
+ PRINTSAMTIMER;
+
+ return Status;
+}
+
+
+NTSTATUS
+NlPackSamGroup (
+ IN ULONG RelativeId,
+ IN OUT PNETLOGON_DELTA_ENUM Delta,
+ IN PDB_INFO DBInfo,
+ LPDWORD BufferSize
+ )
+/*++
+
+Routine Description:
+
+ Pack a description of the specified group into the specified buffer.
+
+Arguments:
+
+ RelativeId - The relative Id of the group query.
+
+ Delta: pointer to the delta structure where the new delta will
+ be returned.
+
+ DBInfo: pointer to the database info structure.
+
+ BufferSize: size of MIDL buffer that is consumed for this delta is
+ returned here.
+
+Return Value:
+
+ NT status code.
+
+--*/
+{
+ NTSTATUS Status;
+ SAMPR_HANDLE GroupHandle = NULL;
+ PNETLOGON_DELTA_GROUP DeltaGroup;
+
+ //
+ // Information returned from SAM
+ //
+
+ PSAMPR_SR_SECURITY_DESCRIPTOR SecurityDescriptor = NULL;
+ PSAMPR_GROUP_INFO_BUFFER GroupGeneral = NULL;
+
+ DEFPACKTIMER;
+ DEFSAMTIMER;
+
+ INITPACKTIMER;
+ INITSAMTIMER;
+
+ STARTPACKTIMER;
+
+ NlPrint((NL_SYNC_MORE, "Packing Group Object %lx\n", RelativeId ));
+
+ *BufferSize = 0;
+
+ Delta->DeltaType = AddOrChangeGroup;
+ Delta->DeltaID.Rid = RelativeId;
+ Delta->DeltaUnion.DeltaGroup = NULL;
+
+ //
+ // Open a handle to the specified group.
+ //
+
+ STARTSAMTIMER;
+
+ Status = SamrOpenGroup( DBInfo->DBHandle,
+ 0, // No desired access
+ RelativeId,
+ &GroupHandle );
+
+ if (!NT_SUCCESS(Status)) {
+ GroupHandle = NULL;
+ goto Cleanup;
+ }
+
+ STOPSAMTIMER;
+
+ QUERY_SAM_SECOBJ_INFO(GroupHandle);
+
+ STARTSAMTIMER;
+
+ Status = SamrQueryInformationGroup(
+ GroupHandle,
+ GroupGeneralInformation,
+ &GroupGeneral );
+
+ STOPSAMTIMER;
+
+ if (!NT_SUCCESS(Status)) {
+ GroupGeneral = NULL;
+ goto Cleanup;
+ }
+
+ NlPrint((NL_SYNC_MORE,
+ "\t Group Object name %wZ\n",
+ (PUNICODE_STRING)&GroupGeneral->General.Name ));
+
+ DeltaGroup = (PNETLOGON_DELTA_GROUP)
+ MIDL_user_allocate( sizeof(NETLOGON_DELTA_GROUP) );
+
+ if( DeltaGroup == NULL ) {
+
+ Status = STATUS_NO_MEMORY;
+ goto Cleanup;
+ }
+
+ //
+ // wipe off the buffer so that cleanup will not be in fault.
+ //
+
+ RtlZeroMemory( DeltaGroup, sizeof(NETLOGON_DELTA_GROUP) );
+
+ Delta->DeltaUnion.DeltaGroup = DeltaGroup;
+ *BufferSize += sizeof(NETLOGON_DELTA_GROUP);
+
+ *BufferSize = NlCopyUnicodeString(
+ (PUNICODE_STRING)&GroupGeneral->General.Name,
+ &DeltaGroup->Name );
+
+ DeltaGroup->RelativeId = RelativeId;
+ DeltaGroup->Attributes = GroupGeneral->General.Attributes;
+
+ *BufferSize += NlCopyUnicodeString(
+ (PUNICODE_STRING)&GroupGeneral->General.AdminComment,
+ &DeltaGroup->AdminComment );
+
+ DeltaGroup->RelativeId = RelativeId;
+ DeltaGroup->Attributes = GroupGeneral->General.Attributes;
+
+ DELTA_SECOBJ_INFO(DeltaGroup);
+ INIT_PLACE_HOLDER(DeltaGroup);
+
+ //
+ // All Done
+ //
+
+ Status = STATUS_SUCCESS;
+
+Cleanup:
+ STARTSAMTIMER;
+
+ if( GroupHandle != NULL ) {
+ (VOID) SamrCloseHandle( &GroupHandle );
+ }
+
+ if ( SecurityDescriptor != NULL ) {
+ SamIFree_SAMPR_SR_SECURITY_DESCRIPTOR( SecurityDescriptor );
+ }
+
+ if ( GroupGeneral != NULL ) {
+ SamIFree_SAMPR_GROUP_INFO_BUFFER( GroupGeneral,
+ GroupGeneralInformation );
+ }
+
+ STOPSAMTIMER;
+
+ if( !NT_SUCCESS(Status) ) {
+ NlFreeDBDelta( Delta );
+ *BufferSize = 0;
+ }
+
+ STOPPACKTIMER;
+
+ NlPrint((NL_REPL_OBJ_TIME,"Time taken to pack GROUP object:\n"));
+ PRINTPACKTIMER;
+ PRINTSAMTIMER;
+
+ return Status;
+}
+
+
+NTSTATUS
+NlPackSamGroupMember (
+ IN ULONG RelativeId,
+ IN OUT PNETLOGON_DELTA_ENUM Delta,
+ IN PDB_INFO DBInfo,
+ LPDWORD BufferSize
+ )
+/*++
+
+Routine Description:
+
+ Pack a description of the membership of the specified group into
+ the specified buffer.
+
+Arguments:
+
+ RelativeId - The relative Id of the group query.
+
+ Delta: pointer to the delta structure where the new delta will
+ be returned.
+
+ DBInfo: pointer to the database info structure.
+
+ BufferSize: size of MIDL buffer that is consumed for this delta is
+ returned here.
+
+Return Value:
+
+ NT status code.
+
+--*/
+{
+ NTSTATUS Status;
+ SAMPR_HANDLE GroupHandle = NULL;
+ DWORD Size;
+ PNETLOGON_DELTA_GROUP_MEMBER DeltaGroupMember;
+
+ //
+ // Information returned from SAM
+ //
+
+ PSAMPR_GET_MEMBERS_BUFFER MembersBuffer = NULL;
+
+ DEFPACKTIMER;
+ DEFSAMTIMER;
+
+ INITPACKTIMER;
+ INITSAMTIMER;
+
+ STARTPACKTIMER;
+
+ NlPrint((NL_SYNC_MORE, "Packing GroupMember Object %lx\n", RelativeId));
+
+ *BufferSize = 0;
+
+ Delta->DeltaType = ChangeGroupMembership;
+ Delta->DeltaID.Rid = RelativeId;
+ Delta->DeltaUnion.DeltaGroupMember = NULL;
+
+ //
+ // Open a handle to the specified group.
+ //
+
+ STARTSAMTIMER;
+
+ Status = SamrOpenGroup( DBInfo->DBHandle,
+ 0, // No desired access
+ RelativeId,
+ &GroupHandle );
+
+ STOPSAMTIMER;
+
+ if (!NT_SUCCESS(Status)) {
+ GroupHandle = NULL;
+ goto Cleanup;
+ }
+
+ //
+ // Find out everything there is to know about the group.
+ //
+
+ STARTSAMTIMER;
+
+ Status = SamrGetMembersInGroup( GroupHandle, &MembersBuffer );
+
+ STOPSAMTIMER;
+
+ if (!NT_SUCCESS(Status)) {
+ MembersBuffer = NULL;
+ goto Cleanup;
+ }
+
+ DeltaGroupMember = (PNETLOGON_DELTA_GROUP_MEMBER)
+ MIDL_user_allocate( sizeof(NETLOGON_DELTA_GROUP_MEMBER) );
+
+ if( DeltaGroupMember == NULL ) {
+
+ Status = STATUS_NO_MEMORY;
+ goto Cleanup;
+ }
+
+ //
+ // wipe off the buffer so that cleanup will not be in fault.
+ //
+
+ RtlZeroMemory( DeltaGroupMember,
+ sizeof(NETLOGON_DELTA_GROUP_MEMBER) );
+
+ Delta->DeltaUnion.DeltaGroupMember = DeltaGroupMember;
+ *BufferSize += sizeof(NETLOGON_DELTA_GROUP_MEMBER);
+
+ if ( MembersBuffer->MemberCount != 0 ) {
+ Size = MembersBuffer->MemberCount * sizeof(*MembersBuffer->Members);
+
+ *BufferSize += NlCopyData(
+ (LPBYTE *)&MembersBuffer->Members,
+ (LPBYTE *)&DeltaGroupMember->MemberIds,
+ Size );
+
+ Size = MembersBuffer->MemberCount *
+ sizeof(*MembersBuffer->Attributes);
+
+ *BufferSize += NlCopyData(
+ (LPBYTE *)&MembersBuffer->Attributes,
+ (LPBYTE *)&DeltaGroupMember->Attributes,
+ Size );
+ }
+
+ DeltaGroupMember->MemberCount = MembersBuffer->MemberCount;
+
+ //
+ // Initialize placeholder strings to NULL.
+ //
+
+ DeltaGroupMember->DummyLong1 = 0;
+ DeltaGroupMember->DummyLong2 = 0;
+ DeltaGroupMember->DummyLong3 = 0;
+ DeltaGroupMember->DummyLong4 = 0;
+
+ //
+ // All Done
+ //
+
+ Status = STATUS_SUCCESS;
+
+Cleanup:
+ STARTSAMTIMER;
+
+ if( GroupHandle != NULL ) {
+ (VOID) SamrCloseHandle( &GroupHandle );
+ }
+
+ if ( MembersBuffer != NULL ) {
+ SamIFree_SAMPR_GET_MEMBERS_BUFFER( MembersBuffer );
+ }
+
+ STOPSAMTIMER;
+
+ if( !NT_SUCCESS(Status) ) {
+ NlFreeDBDelta( Delta );
+ *BufferSize = 0;
+ }
+
+ STOPPACKTIMER;
+
+ NlPrint((NL_REPL_OBJ_TIME,"Time taken to pack GROUPMEMBER object:\n"));
+ PRINTPACKTIMER;
+ PRINTSAMTIMER;
+
+ return Status;
+}
+
+
+NTSTATUS
+NlPackSamAlias (
+ IN ULONG RelativeId,
+ IN OUT PNETLOGON_DELTA_ENUM Delta,
+ IN PDB_INFO DBInfo,
+ LPDWORD BufferSize
+ )
+/*++
+
+Routine Description:
+
+ Pack a description of the specified alias into the specified buffer.
+
+Arguments:
+
+ RelativeId - The relative Id of the alias query.
+
+ Delta: pointer to the delta structure where the new delta will
+ be returned.
+
+ DBInfo: pointer to the database info structure.
+
+ BufferSize: size of MIDL buffer that is consumed for this delta is
+ returned here.
+
+Return Value:
+
+ NT status code.
+
+--*/
+{
+ NTSTATUS Status;
+ SAMPR_HANDLE AliasHandle = NULL;
+ PNETLOGON_DELTA_ALIAS DeltaAlias;
+
+ //
+ // Information returned from SAM
+ //
+
+ PSAMPR_SR_SECURITY_DESCRIPTOR SecurityDescriptor = NULL;
+
+ PSAMPR_ALIAS_INFO_BUFFER AliasGeneral = NULL;
+
+ DEFPACKTIMER;
+ DEFSAMTIMER;
+
+ INITPACKTIMER;
+ INITSAMTIMER;
+
+ STARTPACKTIMER;
+
+ NlPrint((NL_SYNC_MORE, "Packing Alias Object %lx\n", RelativeId));
+
+ *BufferSize = 0;
+
+ Delta->DeltaType = AddOrChangeAlias;
+ Delta->DeltaID.Rid = RelativeId;
+ Delta->DeltaUnion.DeltaAlias = NULL;
+
+ //
+ // Open a handle to the specified alias.
+ //
+
+ STARTSAMTIMER;
+
+ Status = SamrOpenAlias( DBInfo->DBHandle,
+ 0, // No desired access
+ RelativeId,
+ &AliasHandle );
+
+ STOPSAMTIMER;
+
+ if (!NT_SUCCESS(Status)) {
+ AliasHandle = NULL;
+ goto Cleanup;
+ }
+
+ QUERY_SAM_SECOBJ_INFO(AliasHandle);
+
+ //
+ // Determine the alias name.
+ //
+
+ STARTSAMTIMER;
+
+ Status = SamrQueryInformationAlias(
+ AliasHandle,
+ AliasGeneralInformation,
+ &AliasGeneral );
+
+ STOPSAMTIMER;
+
+ if (!NT_SUCCESS(Status)) {
+ AliasGeneral = NULL;
+ goto Cleanup;
+ }
+
+ NlPrint((NL_SYNC_MORE, "\t Alias Object name %wZ\n",
+ (PUNICODE_STRING)&(AliasGeneral->General.Name)));
+
+ DeltaAlias = (PNETLOGON_DELTA_ALIAS)
+ MIDL_user_allocate( sizeof(NETLOGON_DELTA_ALIAS) );
+
+ if( DeltaAlias == NULL ) {
+
+ Status = STATUS_NO_MEMORY;
+ goto Cleanup;
+ }
+
+ //
+ // wipe off the buffer so that cleanup will not be in fault.
+ //
+
+ RtlZeroMemory( DeltaAlias, sizeof(NETLOGON_DELTA_ALIAS) );
+
+ Delta->DeltaUnion.DeltaAlias = DeltaAlias;
+ *BufferSize += sizeof(NETLOGON_DELTA_ALIAS);
+
+ *BufferSize += NlCopyUnicodeString(
+ (PUNICODE_STRING)&(AliasGeneral->General.Name),
+ &DeltaAlias->Name );
+
+ DeltaAlias->RelativeId = RelativeId;
+
+ DELTA_SECOBJ_INFO(DeltaAlias);
+ INIT_PLACE_HOLDER(DeltaAlias);
+
+ //
+ // copy comment string
+ //
+
+ *BufferSize += NlCopyUnicodeString(
+ (PUNICODE_STRING)&(AliasGeneral->General.AdminComment),
+ &DeltaAlias->DummyString1 );
+
+ //
+ // All Done
+ //
+
+ Status = STATUS_SUCCESS;
+
+Cleanup:
+ STARTSAMTIMER;
+
+ if( AliasHandle != NULL ) {
+ (VOID) SamrCloseHandle( &AliasHandle );
+ }
+
+ if ( SecurityDescriptor != NULL ) {
+ SamIFree_SAMPR_SR_SECURITY_DESCRIPTOR( SecurityDescriptor );
+ }
+
+
+ if( AliasGeneral != NULL ) {
+
+ SamIFree_SAMPR_ALIAS_INFO_BUFFER (
+ AliasGeneral,
+ AliasGeneralInformation );
+ }
+
+ STOPSAMTIMER;
+
+ if( !NT_SUCCESS(Status) ) {
+ NlFreeDBDelta( Delta );
+ *BufferSize = 0;
+ }
+
+ STOPPACKTIMER;
+
+ NlPrint((NL_REPL_OBJ_TIME,"Time taken to pack ALIAS object:\n"));
+ PRINTPACKTIMER;
+ PRINTSAMTIMER;
+
+ return Status;
+}
+
+
+NTSTATUS
+NlPackSamAliasMember (
+ IN ULONG RelativeId,
+ IN OUT PNETLOGON_DELTA_ENUM Delta,
+ IN PDB_INFO DBInfo,
+ LPDWORD BufferSize
+ )
+/*++
+
+Routine Description:
+
+ Pack a description of the membership of the specified alias into
+ the specified buffer.
+
+Arguments:
+
+ RelativeId - The relative Id of the alias query.
+
+ Delta: pointer to the delta structure where the new delta will
+ be returned.
+
+ DBInfo: pointer to the database info structure.
+
+ BufferSize: size of MIDL buffer that is consumed for this delta is
+ returned here.
+
+Return Value:
+
+ NT status code.
+
+--*/
+{
+ NTSTATUS Status;
+ SAMPR_HANDLE AliasHandle = NULL;
+ PNETLOGON_DELTA_ALIAS_MEMBER DeltaAliasMember;
+ DWORD i;
+
+ //
+ // Information returned from SAM
+ //
+
+ NLPR_SID_ARRAY Members;
+ PNLPR_SID_INFORMATION Sids;
+
+ DEFPACKTIMER;
+ DEFSAMTIMER;
+
+ INITPACKTIMER;
+ INITSAMTIMER;
+
+ STARTPACKTIMER;
+
+ NlPrint((NL_SYNC_MORE, "Packing AliasMember Object %lx\n", RelativeId));
+
+ *BufferSize = 0;
+
+ Delta->DeltaType = ChangeAliasMembership;
+ Delta->DeltaID.Rid = RelativeId;
+ Delta->DeltaUnion.DeltaAliasMember = NULL;
+
+ Members.Sids = NULL;
+
+
+ //
+ // Open a handle to the specified alias.
+ //
+
+ STARTSAMTIMER;
+
+ Status = SamrOpenAlias( DBInfo->DBHandle,
+ 0, // No desired access
+ RelativeId,
+ &AliasHandle );
+
+ STOPSAMTIMER;
+
+ if (!NT_SUCCESS(Status)) {
+ AliasHandle = NULL;
+ goto Cleanup;
+ }
+
+ //
+ // Find out everything there is to know about the alias.
+ //
+
+ STARTSAMTIMER;
+
+ Status = SamrGetMembersInAlias( AliasHandle,
+ (PSAMPR_PSID_ARRAY)&Members );
+
+ STOPSAMTIMER;
+
+ if (!NT_SUCCESS(Status)) {
+ Members.Sids = NULL;
+ goto Cleanup;
+ }
+
+
+ DeltaAliasMember = (PNETLOGON_DELTA_ALIAS_MEMBER)
+ MIDL_user_allocate( sizeof(NETLOGON_DELTA_ALIAS_MEMBER) );
+
+ if( DeltaAliasMember == NULL ) {
+
+ Status = STATUS_NO_MEMORY;
+ goto Cleanup;
+ }
+
+ //
+ // wipe off the buffer so that cleanup will not be in fault.
+ //
+
+ RtlZeroMemory( DeltaAliasMember,
+ sizeof(NETLOGON_DELTA_ALIAS_MEMBER) );
+
+ Delta->DeltaUnion.DeltaAliasMember = DeltaAliasMember;
+ *BufferSize += sizeof(NETLOGON_DELTA_ALIAS_MEMBER);
+
+ //
+ // tie up sam return node to our return node
+ //
+
+ DeltaAliasMember->Members = Members;
+
+ //
+ // however, compute the MIDL buffer consumed for members node.
+ //
+
+ for(i = 0, Sids = Members.Sids; i < Members.Count; ++i, Sids++) {
+
+ *BufferSize += (sizeof(PNLPR_SID_INFORMATION) +
+ RtlLengthSid(Sids->SidPointer));
+
+ }
+
+ *BufferSize += sizeof(SAMPR_PSID_ARRAY);
+
+ //
+ // Initialize placeholder strings to NULL.
+ //
+
+ DeltaAliasMember->DummyLong1 = 0;
+ DeltaAliasMember->DummyLong2 = 0;
+ DeltaAliasMember->DummyLong3 = 0;
+ DeltaAliasMember->DummyLong4 = 0;
+
+ //
+ // All Done
+ //
+
+ Status = STATUS_SUCCESS;
+
+Cleanup:
+
+ STARTSAMTIMER;
+
+ if( AliasHandle != NULL ) {
+ (VOID) SamrCloseHandle( &AliasHandle );
+ }
+
+ if ( Members.Sids != NULL ) {
+
+ //
+ // don't free this node because we have tied up this
+ // node to our return info to RPC which will free it up
+ // when it is done with it.
+ //
+ // however, free this node under error conditions
+ //
+
+ }
+
+ if( !NT_SUCCESS(Status) ) {
+
+ SamIFree_SAMPR_PSID_ARRAY( (PSAMPR_PSID_ARRAY)&Members );
+
+ if( Delta->DeltaUnion.DeltaAliasMember != NULL ) {
+ Delta->DeltaUnion.DeltaAliasMember->Members.Sids = NULL;
+ }
+
+ NlFreeDBDelta( Delta );
+ *BufferSize = 0;
+ }
+
+ STOPSAMTIMER;
+
+ STOPPACKTIMER;
+
+ NlPrint((NL_REPL_OBJ_TIME,"Timing for ALIASMEBER object packing:\n"));
+ PRINTPACKTIMER;
+ PRINTSAMTIMER;
+
+ return Status;
+}
+
+
+NTSTATUS
+NlPackSamDomain (
+ IN OUT PNETLOGON_DELTA_ENUM Delta,
+ IN PDB_INFO DBInfo,
+ IN LPDWORD BufferSize
+ )
+/*++
+
+Routine Description:
+
+ Pack a description of the sam domain into the specified buffer.
+
+Arguments:
+
+ Delta: pointer to the delta structure where the new delta will
+ be returned.
+
+ DBInfo: pointer to the database info structure.
+
+ BufferSize: size of MIDL buffer that is consumed for this delta is
+ returned here.
+
+Return Value:
+
+ NT status code.
+
+--*/
+{
+ NTSTATUS Status;
+
+ PNETLOGON_DELTA_DOMAIN DeltaDomain = NULL;
+
+ //
+ // Information returned from SAM
+ //
+
+ PSAMPR_SR_SECURITY_DESCRIPTOR SecurityDescriptor = NULL;
+ PSAMPR_DOMAIN_INFO_BUFFER DomainGeneral = NULL;
+ PSAMPR_DOMAIN_INFO_BUFFER DomainPassword = NULL;
+ PSAMPR_DOMAIN_INFO_BUFFER DomainModified = NULL;
+ PSAMPR_DOMAIN_INFO_BUFFER DomainLockout = NULL;
+
+ DEFPACKTIMER;
+ DEFSAMTIMER;
+
+ INITPACKTIMER;
+ INITSAMTIMER;
+
+ STARTPACKTIMER;
+
+ NlPrint((NL_SYNC_MORE, "Packing Domain Object\n"));
+
+ *BufferSize = 0;
+
+ Delta->DeltaType = AddOrChangeDomain;
+ Delta->DeltaID.Rid = 0;
+ Delta->DeltaUnion.DeltaDomain = NULL;
+
+
+ QUERY_SAM_SECOBJ_INFO(DBInfo->DBHandle);
+
+ STARTSAMTIMER;
+
+ Status = SamrQueryInformationDomain(
+ DBInfo->DBHandle,
+ DomainGeneralInformation,
+ &DomainGeneral );
+
+ STOPSAMTIMER;
+
+ if (!NT_SUCCESS(Status)) {
+ DomainGeneral = NULL;
+ goto Cleanup;
+ }
+
+ //
+ // As a side effect, make sure our UAS Compatibility mode is correct.
+ //
+ NlGlobalUasCompatibilityMode =
+ DomainGeneral->General.UasCompatibilityRequired ;
+
+
+ STARTSAMTIMER;
+
+ Status = SamrQueryInformationDomain(
+ DBInfo->DBHandle,
+ DomainPasswordInformation,
+ &DomainPassword );
+
+ STOPSAMTIMER;
+
+ if (!NT_SUCCESS(Status)) {
+ DomainPassword = NULL;
+ goto Cleanup;
+ }
+
+ STARTSAMTIMER;
+
+ Status = SamrQueryInformationDomain(
+ DBInfo->DBHandle,
+ DomainModifiedInformation,
+ &DomainModified );
+
+ STOPSAMTIMER;
+
+ if (!NT_SUCCESS(Status)) {
+ DomainModified = NULL;
+ goto Cleanup;
+ }
+
+ STARTSAMTIMER;
+
+ Status = SamrQueryInformationDomain(
+ DBInfo->DBHandle,
+ DomainLockoutInformation,
+ &DomainLockout );
+
+ STOPSAMTIMER;
+
+ if (!NT_SUCCESS(Status)) {
+ DomainLockout = NULL;
+ goto Cleanup;
+ }
+
+ //
+ // Fill in the delta structure
+ //
+
+
+ DeltaDomain = (PNETLOGON_DELTA_DOMAIN)
+ MIDL_user_allocate( sizeof(NETLOGON_DELTA_DOMAIN) );
+
+ if( DeltaDomain == NULL ) {
+
+ Status = STATUS_NO_MEMORY;
+ goto Cleanup;
+ }
+
+ //
+ // Zero the buffer so that cleanup will not access violate.
+ //
+
+ RtlZeroMemory( DeltaDomain, sizeof(NETLOGON_DELTA_DOMAIN) );
+
+ Delta->DeltaUnion.DeltaDomain = DeltaDomain;
+ *BufferSize += sizeof(NETLOGON_DELTA_DOMAIN);
+
+ *BufferSize += NlCopyUnicodeString(
+ (PUNICODE_STRING)&DomainGeneral->General.DomainName,
+ &DeltaDomain->DomainName );
+
+ *BufferSize = NlCopyUnicodeString(
+ (PUNICODE_STRING)&DomainGeneral->General.OemInformation,
+ &DeltaDomain->OemInformation );
+
+ DeltaDomain->ForceLogoff = DomainGeneral->General.ForceLogoff;
+ DeltaDomain->MinPasswordLength =
+ DomainPassword->Password.MinPasswordLength;
+ DeltaDomain->PasswordHistoryLength =
+ DomainPassword->Password.PasswordHistoryLength;
+
+ NEW_TO_OLD_LARGE_INTEGER(
+ DomainPassword->Password.MaxPasswordAge,
+ DeltaDomain->MaxPasswordAge );
+
+ NEW_TO_OLD_LARGE_INTEGER(
+ DomainPassword->Password.MinPasswordAge,
+ DeltaDomain->MinPasswordAge );
+
+ NEW_TO_OLD_LARGE_INTEGER(
+ DomainModified->Modified.DomainModifiedCount,
+ DeltaDomain->DomainModifiedCount );
+
+ NEW_TO_OLD_LARGE_INTEGER(
+ DomainModified->Modified.CreationTime,
+ DeltaDomain->DomainCreationTime );
+
+
+ DELTA_SECOBJ_INFO(DeltaDomain);
+ INIT_PLACE_HOLDER(DeltaDomain);
+
+ //
+ // replicate PasswordProperties using reserved field.
+ //
+
+ DeltaDomain->DummyLong1 =
+ DomainPassword->Password.PasswordProperties;
+
+ //
+ // Replicate DOMAIN_LOCKOUT_INFORMATION using reserved field.
+ //
+
+ DeltaDomain->DummyString1.Buffer = (LPWSTR) DomainLockout;
+ DeltaDomain->DummyString1.MaximumLength =
+ DeltaDomain->DummyString1.Length = sizeof( DomainLockout->Lockout);
+ DomainLockout = NULL;
+
+ //
+ // All Done
+ //
+
+ Status = STATUS_SUCCESS;
+
+Cleanup:
+
+ STARTSAMTIMER;
+
+ if ( SecurityDescriptor != NULL ) {
+ SamIFree_SAMPR_SR_SECURITY_DESCRIPTOR( SecurityDescriptor );
+ }
+
+ if ( DomainGeneral != NULL ) {
+ SamIFree_SAMPR_DOMAIN_INFO_BUFFER( DomainGeneral,
+ DomainGeneralInformation );
+ }
+
+ if ( DomainPassword != NULL ) {
+ SamIFree_SAMPR_DOMAIN_INFO_BUFFER( DomainPassword,
+ DomainPasswordInformation );
+ }
+
+ if ( DomainModified != NULL ) {
+ SamIFree_SAMPR_DOMAIN_INFO_BUFFER( DomainModified,
+ DomainModifiedInformation );
+ }
+
+ if ( DomainLockout != NULL ) {
+ SamIFree_SAMPR_DOMAIN_INFO_BUFFER( DomainLockout,
+ DomainLockoutInformation );
+ }
+
+ STOPSAMTIMER;
+
+ if( !NT_SUCCESS(Status) ) {
+ NlFreeDBDelta( Delta );
+ *BufferSize = 0;
+ }
+
+ STOPPACKTIMER;
+
+ NlPrint((NL_REPL_OBJ_TIME,"Timing for DOMAIN object packing:\n"));
+ PRINTPACKTIMER;
+ PRINTSAMTIMER;
+
+ return Status;
+}
+
+
+
+
+NTSTATUS
+NlUnpackSamUser (
+ IN PNETLOGON_DELTA_USER DeltaUser,
+ IN PDB_INFO DBInfo,
+ OUT PULONG ConflictingRID,
+ IN PSESSION_INFO SessionInfo
+ )
+/*++
+
+Routine Description:
+
+ Set the Sam User to look like the specified buffer.
+
+Arguments:
+
+ DeltaUser - Description of the user.
+
+ SessionInfo - Info shared between PDC and BDC
+
+Return Value:
+
+ NT status code.
+
+--*/
+{
+ NTSTATUS Status;
+ SAMPR_HANDLE UserHandle = NULL;
+ SAMPR_USER_INFO_BUFFER UserInfo;
+#ifdef notdef
+ NT_OWF_PASSWORD NtOwfPassword;
+ LM_OWF_PASSWORD LmOwfPassword;
+#endif // notdef
+
+ PUCHAR PrivateDataAllocated = NULL;
+ LPWSTR WorkstationList = NULL;
+
+ BOOLEAN SetUserNameField = FALSE;
+
+ DEFUNPACKTIMER;
+ DEFSAMTIMER;
+
+ INITUNPACKTIMER;
+ INITSAMTIMER;
+
+ STARTUNPACKTIMER;
+
+ NlPrint((NL_SYNC_MORE, "UnPacking User Object (%lx) %wZ\n",
+ DeltaUser->UserId,
+ &DeltaUser->UserName ));
+
+ //
+ // Open a handle to the specified user.
+ //
+
+ STARTSAMTIMER;
+
+ Status = SamICreateAccountByRid(
+ DBInfo->DBHandle,
+ SamObjectUser,
+ DeltaUser->UserId,
+ (PRPC_UNICODE_STRING) &DeltaUser->UserName,
+ 0, // No desired access
+ &UserHandle,
+ ConflictingRID );
+
+ STOPSAMTIMER;
+
+ if (!NT_SUCCESS(Status)) {
+
+ if( (Status == STATUS_USER_EXISTS) &&
+ (DeltaUser->UserId == *ConflictingRID) ) {
+
+ //
+ // this user account has been renamed.
+ //
+
+ SetUserNameField = TRUE;
+
+ Status = SamrOpenUser(
+ DBInfo->DBHandle,
+ 0,
+ DeltaUser->UserId,
+ &UserHandle );
+
+ if (!NT_SUCCESS(Status)) {
+ UserHandle = NULL;
+ goto Cleanup;
+ }
+ }
+ else {
+
+ NlPrint((NL_SYNC_MORE, "Parameters to SamICreateAccountByRid:\n"
+ "\tDomainHandle = %lx\n"
+ "\tAccountType = %lx\n"
+ "\tRelativeId = %lx\n"
+ "\tAccountName = %wZ\n"
+ "\tDesiredAccess = %lx\n"
+ "\tAccountHandle = %lx\n"
+ "\tConflictingAccountRid = %lx\n",
+ (DWORD)(DBInfo->DBHandle),
+ (DWORD)SamObjectUser,
+ (DWORD)DeltaUser->UserId,
+ &DeltaUser->UserName,
+ 0,
+ (DWORD)&UserHandle,
+ (DWORD)*ConflictingRID ));
+
+ NlPrint((NL_SYNC_MORE, "SamICreateAccountByRid returning %lx\n",
+ Status ));
+
+ UserHandle = NULL;
+ goto Cleanup;
+ }
+ }
+
+
+ //
+ // Set the attributes of the user.
+ //
+ // Notice that the actual text strings remain in the DeltaUser
+ // structure. I only copy a pointer to them to the user specific
+ // structure.
+ //
+ RtlZeroMemory( &UserInfo, sizeof(UserInfo) );
+ UserInfo.Internal3.I1.WhichFields = 0;
+
+ //
+ // if this account is renamed then set user name.
+ //
+
+ if( SetUserNameField ) {
+ UserInfo.Internal3.I1.UserName =
+ * ((PRPC_UNICODE_STRING) &DeltaUser->UserName);
+ UserInfo.Internal3.I1.WhichFields |= USER_ALL_USERNAME;
+ }
+
+ UserInfo.Internal3.I1.SecurityDescriptor.SecurityDescriptor =
+ ((PSECURITY_DESCRIPTOR) DeltaUser->SecurityDescriptor);
+ UserInfo.Internal3.I1.SecurityDescriptor.Length = DeltaUser->SecuritySize;
+ UserInfo.Internal3.I1.WhichFields |= USER_ALL_SECURITYDESCRIPTOR;
+
+ UserInfo.Internal3.I1.FullName = * ((PRPC_UNICODE_STRING) &DeltaUser->FullName);
+ UserInfo.Internal3.I1.WhichFields |= USER_ALL_FULLNAME;
+
+ UserInfo.Internal3.I1.PrimaryGroupId = DeltaUser->PrimaryGroupId;
+ UserInfo.Internal3.I1.WhichFields |= USER_ALL_PRIMARYGROUPID;
+
+ UserInfo.Internal3.I1.HomeDirectory =
+ * ((PRPC_UNICODE_STRING) &DeltaUser->HomeDirectory);
+ UserInfo.Internal3.I1.WhichFields |= USER_ALL_HOMEDIRECTORY;
+
+ UserInfo.Internal3.I1.HomeDirectoryDrive =
+ * ((PRPC_UNICODE_STRING) &DeltaUser->HomeDirectoryDrive);
+ UserInfo.Internal3.I1.WhichFields |= USER_ALL_HOMEDIRECTORYDRIVE;
+
+ UserInfo.Internal3.I1.ScriptPath =
+ * ((PRPC_UNICODE_STRING) &DeltaUser->ScriptPath);
+ UserInfo.Internal3.I1.WhichFields |= USER_ALL_SCRIPTPATH;
+
+ UserInfo.Internal3.I1.ProfilePath =
+ * ((PRPC_UNICODE_STRING) &DeltaUser->DummyString1);
+ UserInfo.Internal3.I1.WhichFields |= USER_ALL_PROFILEPATH;
+
+ UserInfo.Internal3.I1.AdminComment =
+ * ((PRPC_UNICODE_STRING) &DeltaUser->AdminComment);
+ UserInfo.Internal3.I1.WhichFields |= USER_ALL_ADMINCOMMENT;
+
+ UserInfo.Internal3.I1.WhichFields |= USER_ALL_WORKSTATIONS;
+
+ //
+ // Don't replicate these. Retain the old values.
+ //
+
+ // UserInfo.Internal3.I1.LastLogon = DeltaUser->LastLogon;
+ // UserInfo.Internal3.I1.LastLogoff = DeltaUser->LastLogoff;
+ // UserInfo.Internal3.I1.LogonCount = DeltaUser->LogonCount;
+
+ UserInfo.Internal3.I1.PasswordLastSet = DeltaUser->PasswordLastSet;
+ UserInfo.Internal3.I1.WhichFields |= USER_ALL_PASSWORDLASTSET;
+
+ UserInfo.Internal3.I1.LogonHours.UnitsPerWeek = DeltaUser->LogonHours.UnitsPerWeek;
+ UserInfo.Internal3.I1.LogonHours.LogonHours = DeltaUser->LogonHours.LogonHours;
+ UserInfo.Internal3.I1.WhichFields |= USER_ALL_LOGONHOURS;
+
+ UserInfo.Internal3.I1.AccountExpires = DeltaUser->AccountExpires;
+ UserInfo.Internal3.I1.WhichFields |= USER_ALL_ACCOUNTEXPIRES;
+
+ UserInfo.Internal3.I1.UserAccountControl = DeltaUser->UserAccountControl;
+ UserInfo.Internal3.I1.WhichFields |= USER_ALL_USERACCOUNTCONTROL;
+
+ UserInfo.Internal3.I1.UserComment = *((PRPC_UNICODE_STRING) &DeltaUser->UserComment);
+ UserInfo.Internal3.I1.WhichFields |= USER_ALL_USERCOMMENT;
+
+ UserInfo.Internal3.I1.CountryCode = DeltaUser->CountryCode;
+ UserInfo.Internal3.I1.WhichFields |= USER_ALL_COUNTRYCODE;
+
+ UserInfo.Internal3.I1.CodePage = DeltaUser->CodePage;
+ UserInfo.Internal3.I1.WhichFields |= USER_ALL_CODEPAGE;
+
+ UserInfo.Internal3.I1.Parameters = * ((PRPC_UNICODE_STRING) &DeltaUser->Parameters);
+ UserInfo.Internal3.I1.WhichFields |= USER_ALL_PARAMETERS;
+
+ //
+ // Set workstation list information.
+ //
+ UserInfo.Internal3.I1.WorkStations =
+ * ((PRPC_UNICODE_STRING) &DeltaUser->WorkStations);
+
+
+
+ //
+ // Set private data
+ // Includes passwords and password history.
+ //
+
+ if( DeltaUser->PrivateData.SensitiveData ) {
+
+ CRYPT_BUFFER EncryptedData;
+ CRYPT_BUFFER Data;
+
+ //
+ // need to decrypt the private data
+ //
+
+ EncryptedData.Length =
+ EncryptedData.MaximumLength =
+ DeltaUser->PrivateData.DataLength;
+
+ EncryptedData.Buffer = DeltaUser->PrivateData.Data;
+
+
+ Status = NlDecryptSensitiveData(
+ &EncryptedData,
+ &Data,
+ SessionInfo );
+
+ if( !NT_SUCCESS( Status ) ) {
+ goto Cleanup;
+ }
+
+ PrivateDataAllocated = Data.Buffer;
+ UserInfo.Internal3.I1.PrivateData.Buffer = (WCHAR *) Data.Buffer;
+ UserInfo.Internal3.I1.PrivateData.Length = (USHORT) Data.Length;
+
+
+ } else {
+
+ UserInfo.Internal3.I1.PrivateData.Buffer = (WCHAR *) DeltaUser->PrivateData.Data;
+ UserInfo.Internal3.I1.PrivateData.Length = (USHORT)
+ DeltaUser->PrivateData.DataLength;
+ }
+ UserInfo.Internal3.I1.WhichFields |= USER_ALL_PRIVATEDATA;
+
+ //
+ // Copy LastBadPasswordTime from DummyLong1 and DummyLong2.
+ //
+
+ UserInfo.Internal3.I1.BadPasswordCount = DeltaUser->BadPasswordCount;
+ UserInfo.Internal3.LastBadPasswordTime.HighPart = DeltaUser->DummyLong1;
+ UserInfo.Internal3.LastBadPasswordTime.LowPart = DeltaUser->DummyLong2;
+ UserInfo.Internal3.I1.WhichFields |= USER_ALL_BADPASSWORDCOUNT;
+
+
+ //
+ // Finally, set the data in SAM
+ //
+
+ for (;;) {
+
+ NTSTATUS TempStatus;
+ SAMPR_HANDLE GroupHandle;
+
+ STARTSAMTIMER;
+
+ Status = SamrSetInformationUser(
+ UserHandle,
+ UserInternal3Information,
+ &UserInfo );
+
+ STOPSAMTIMER;
+
+ //
+ // If the User isn't a member of his primary group,
+ // make him one.
+ //
+
+ if ( Status == STATUS_MEMBER_NOT_IN_GROUP ) {
+
+ NlPrint((NL_CRITICAL,
+ "User (%lx) %wZ: User isn't member of his primary group (%lx) -- recover.\n",
+ DeltaUser->UserId,
+ &DeltaUser->UserName,
+ UserInfo.Internal3.I1.PrimaryGroupId ));
+
+ //
+ // Open the user's primary group.
+ //
+
+
+ STARTSAMTIMER;
+ TempStatus = SamrOpenGroup( DBInfo->DBHandle,
+ 0, // No desired access
+ UserInfo.Internal3.I1.PrimaryGroupId,
+ &GroupHandle );
+ STOPSAMTIMER;
+
+
+ if ( !NT_SUCCESS(TempStatus)) {
+
+ NlPrint((NL_CRITICAL,
+ "User (%lx) %wZ: Failed to open user's primary group %lx -- Status = 0x%lx.\n",
+ DeltaUser->UserId,
+ &DeltaUser->UserName,
+ UserInfo.Internal3.I1.PrimaryGroupId,
+ TempStatus ));
+
+ goto Cleanup;
+ }
+
+ //
+ // Add this user as a member of the group.
+ //
+
+ STARTSAMTIMER;
+ TempStatus = SamrAddMemberToGroup( GroupHandle,
+ DeltaUser->UserId,
+ SE_GROUP_MANDATORY |
+ SE_GROUP_ENABLED_BY_DEFAULT |
+ SE_GROUP_ENABLED );
+
+ SamrCloseHandle( &GroupHandle );
+ STOPSAMTIMER;
+
+ if ( !NT_SUCCESS(TempStatus) ) {
+ NlPrint((NL_CRITICAL,
+ "User (%lx) %wZ: Failed to make user member of primary group %lx -- Status = 0x%lx.\n",
+ DeltaUser->UserId,
+ &DeltaUser->UserName,
+ UserInfo.Internal3.I1.PrimaryGroupId,
+ TempStatus ));
+ goto Cleanup;
+ }
+
+ continue;
+
+ }
+
+ //
+ // No other conditions are handled.
+ //
+
+ break;
+
+ }
+
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+
+
+
+ Status = STATUS_SUCCESS;
+
+
+ //
+ // All Done
+ //
+
+Cleanup:
+
+
+ if ( UserHandle != NULL ) {
+ STARTSAMTIMER;
+ SamrCloseHandle( &UserHandle );
+ STOPSAMTIMER;
+ }
+
+
+ if( PrivateDataAllocated != NULL ) {
+ MIDL_user_free( PrivateDataAllocated );
+ }
+
+ if ( WorkstationList != NULL ) {
+ RtlFreeHeap( RtlProcessHeap(), 0, WorkstationList );
+ }
+
+ STOPUNPACKTIMER;
+
+ NlPrint((NL_REPL_OBJ_TIME,"Time taken to unpack USER object:\n"));
+ PRINTUNPACKTIMER;
+ PRINTSAMTIMER;
+
+ return Status;
+
+}
+
+
+NTSTATUS
+NlDeleteSamUser(
+ SAMPR_HANDLE DomainHandle,
+ ULONG Rid
+ )
+/*++
+
+Routine Description:
+
+ Delete an user object.
+
+Arguments:
+
+ DomainHandle - handle of the SAM domain.
+
+ Rid - Rid of the object to be deleted.
+
+Return Value:
+
+ NT status code.
+
+--*/
+{
+ NTSTATUS Status;
+ SAMPR_HANDLE UserHandle;
+
+ NlPrint((NL_SYNC_MORE, "Delete User Object %lx\n", Rid));
+
+ Status = SamrOpenUser( DomainHandle, 0, Rid, &UserHandle );
+
+ if ( NT_SUCCESS(Status) ) {
+ Status = SamrDeleteUser( &UserHandle );
+ }
+ else if ( Status == STATUS_NO_SUCH_USER ) {
+ Status = STATUS_SUCCESS;
+ }
+
+ if ( !NT_SUCCESS(Status) ) {
+ NlPrint((NL_CRITICAL, "Unable to delete User Object %lx\n", Rid));
+ }
+
+ return(Status);
+}
+
+
+NTSTATUS
+NlDeleteSamGroup(
+ SAMPR_HANDLE DomainHandle,
+ ULONG Rid
+ )
+/*++
+
+Routine Description:
+
+ Delete an group object.
+
+Arguments:
+
+ DomainHandle - handle of the SAM domain.
+
+ Rid - Rid of the object to be deleted.
+
+Return Value:
+
+ NT status code.
+
+--*/
+{
+ NTSTATUS Status;
+ SAMPR_HANDLE GroupHandle;
+
+ NlPrint((NL_SYNC_MORE, "Delete Group Object %lx\n", Rid));
+
+ Status = SamrOpenGroup( DomainHandle, 0, Rid, &GroupHandle );
+
+ if ( NT_SUCCESS(Status) ) {
+
+ Status = SamrDeleteGroup( &GroupHandle );
+
+ if ( Status == STATUS_MEMBER_IN_GROUP ) {
+
+ PSAMPR_GET_MEMBERS_BUFFER MembersBuffer = NULL;
+ DWORD i;
+
+ NlPrint((NL_SYNC_MORE, "Deleting Group Members before "
+ "deleting Group Object \n"));
+
+ //
+ // if we are unable to delete this group because we
+ // still have members in this group then remove members
+ // first and delete it again.
+ //
+
+ Status = SamrGetMembersInGroup( GroupHandle, &MembersBuffer );
+
+ if ( NT_SUCCESS(Status) ) {
+
+ NlAssert( MembersBuffer->MemberCount != 0 );
+
+ for( i = 0; i < MembersBuffer->MemberCount; i++) {
+
+ NlPrint((NL_SYNC_MORE, "Deleting Group Member %lx\n",
+ MembersBuffer->Members[i] ));
+
+ Status = SamrRemoveMemberFromGroup(
+ GroupHandle,
+ MembersBuffer->Members[i] );
+
+ if ( !NT_SUCCESS(Status) ) {
+ break;
+ }
+ }
+ }
+ else {
+
+ MembersBuffer = NULL;
+ }
+
+ if ( NT_SUCCESS(Status) ) {
+
+ //
+ // since we have deleted all members successfully,
+ // delete the group now.
+ //
+
+ Status = SamrDeleteGroup( &GroupHandle );
+ }
+
+ //
+ // free up the sam resource consumed here.
+ //
+
+ if ( MembersBuffer != NULL ) {
+ SamIFree_SAMPR_GET_MEMBERS_BUFFER( MembersBuffer );
+ }
+
+ }
+ }
+ else if ( Status == STATUS_NO_SUCH_GROUP ) {
+ Status = STATUS_SUCCESS;
+ }
+
+
+ if ( !NT_SUCCESS(Status) ) {
+ NlPrint((NL_CRITICAL, "Unable to delete Group Object %lx\n", Rid));
+ }
+
+ return(Status);
+}
+
+
+NTSTATUS
+NlDeleteSamAlias(
+ SAMPR_HANDLE DomainHandle,
+ ULONG Rid
+ )
+/*++
+
+Routine Description:
+
+ Delete an alias object.
+
+Arguments:
+
+ DomainHandle - handle of the SAM domain.
+
+ Rid - Rid of the object to be deleted.
+
+Return Value:
+
+ NT status code.
+
+--*/
+{
+ NTSTATUS Status;
+ SAMPR_HANDLE AliasHandle;
+
+ NlPrint((NL_SYNC_MORE, "Delete Alias Object %lx\n", Rid));
+
+ Status = SamrOpenAlias( DomainHandle, 0, Rid, &AliasHandle );
+
+ if ( NT_SUCCESS(Status) ) {
+
+ Status = SamrDeleteAlias( &AliasHandle );
+
+ if ( Status == STATUS_MEMBER_IN_ALIAS ) {
+
+ SAMPR_PSID_ARRAY Members = {0, NULL};
+ DWORD i;
+
+ NlPrint((NL_SYNC_MORE, "Deleting Alias Members before "
+ "deleting Alias Object \n"));
+
+ //
+ // if we are unable to delete this alias because we
+ // still have members in this alias then remove members
+ // first and delete it again.
+ //
+
+ Status = SamrGetMembersInAlias( AliasHandle, &Members );
+
+ if ( NT_SUCCESS(Status) ) {
+
+ NlAssert( Members.Count != 0 );
+
+ for( i = 0; i < Members.Count; i++) {
+
+ NlPrint((NL_SYNC_MORE, "Deleting Alias Member: " ));
+ NlpDumpSid( NL_SYNC_MORE, Members.Sids[i].SidPointer );
+
+ Status = SamrRemoveMemberFromAlias(
+ AliasHandle,
+ Members.Sids[i].SidPointer );
+
+ if ( !NT_SUCCESS(Status) ) {
+ break;
+ }
+ }
+ }
+ else {
+
+ Members.Sids = NULL;
+ }
+
+ if ( NT_SUCCESS(Status) ) {
+
+ //
+ // since we have deleted all members successfully,
+ // delete the alias now.
+ //
+
+ Status = SamrDeleteAlias( &AliasHandle );
+ }
+
+ //
+ // free up the sam resource consumed here.
+ //
+
+ if ( Members.Sids != NULL ) {
+ SamIFree_SAMPR_PSID_ARRAY( (PSAMPR_PSID_ARRAY)&Members );
+ }
+
+ }
+ }
+ else if ( Status == STATUS_NO_SUCH_ALIAS ) {
+ Status = STATUS_SUCCESS;
+ }
+
+ if ( !NT_SUCCESS(Status) ) {
+ NlPrint((NL_CRITICAL, "Unable to delete Alias Object %lx\n", Rid));
+ }
+
+ return(Status);
+}
+
+
+NTSTATUS
+NlUnpackSamGroup (
+ IN PNETLOGON_DELTA_GROUP DeltaGroup,
+ IN PDB_INFO DBInfo,
+ OUT PULONG ConflictingRID
+ )
+/*++
+
+Routine Description:
+
+ Set the Sam Group to look like the specified buffer.
+
+Arguments:
+
+ DeltaGroup - Description of the group.
+
+Return Value:
+
+ NT status code.
+
+--*/
+{
+ NTSTATUS Status;
+ SAMPR_HANDLE GroupHandle = NULL;
+ SAMPR_GROUP_INFO_BUFFER GroupInfo;
+ SAMPR_SR_SECURITY_DESCRIPTOR SecurityDescriptor;
+ BOOLEAN SetGroupNameField = FALSE;
+
+ DEFUNPACKTIMER;
+ DEFSAMTIMER;
+
+ INITUNPACKTIMER;
+ INITSAMTIMER;
+
+ STARTUNPACKTIMER;
+
+ NlPrint((NL_SYNC_MORE, "UnPacking Group Object (%lx) %wZ\n",
+ DeltaGroup->RelativeId,
+ &DeltaGroup->Name ));
+
+ //
+ // Open a handle to the specified group.
+ //
+
+ STARTSAMTIMER;
+
+ Status = SamICreateAccountByRid(
+ DBInfo->DBHandle,
+ SamObjectGroup,
+ DeltaGroup->RelativeId,
+ (PRPC_UNICODE_STRING) &DeltaGroup->Name,
+ 0, // No desired access
+ &GroupHandle,
+ ConflictingRID );
+
+ STOPSAMTIMER;
+
+ if (!NT_SUCCESS(Status)) {
+
+ if( (Status == STATUS_GROUP_EXISTS) &&
+ (DeltaGroup->RelativeId == *ConflictingRID) ) {
+
+ //
+ // this group account has been renamed.
+ //
+
+ SetGroupNameField = TRUE;
+
+ Status = SamrOpenGroup(
+ DBInfo->DBHandle,
+ 0,
+ DeltaGroup->RelativeId,
+ &GroupHandle );
+
+ if (!NT_SUCCESS(Status)) {
+ GroupHandle = NULL;
+ goto Cleanup;
+ }
+ }
+ else {
+
+ GroupHandle = NULL;
+ goto Cleanup;
+ }
+ }
+
+ SET_SAM_SECOBJ_INFO(DeltaGroup, GroupHandle);
+
+ //
+ // Set the other attributes of the group.
+ //
+ // Notice that the actual text strings remain in the DeltaGroup
+ // structure. I only copy a pointer to them to the group specific
+ // structure.
+ //
+
+ //
+ // if this account is renamed, then set new group name.
+ //
+
+ if ( SetGroupNameField ) {
+
+ GroupInfo.Name.Name =
+ * (PRPC_UNICODE_STRING) &DeltaGroup->Name;
+
+
+ STARTSAMTIMER;
+
+ Status = SamrSetInformationGroup(
+ GroupHandle,
+ GroupNameInformation,
+ &GroupInfo );
+
+ STOPSAMTIMER;
+ }
+
+ GroupInfo.Attribute.Attributes = DeltaGroup->Attributes;
+
+ STARTSAMTIMER;
+
+ Status = SamrSetInformationGroup(
+ GroupHandle,
+ GroupAttributeInformation,
+ &GroupInfo );
+
+ STOPSAMTIMER;
+
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+
+
+ GroupInfo.AdminComment.AdminComment =
+ * ((PRPC_UNICODE_STRING) &DeltaGroup->AdminComment);
+
+ STARTSAMTIMER;
+
+ Status = SamrSetInformationGroup(
+ GroupHandle,
+ GroupAdminCommentInformation,
+ &GroupInfo );
+
+ STOPSAMTIMER;
+
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+
+ Status = STATUS_SUCCESS;
+
+ //
+ // All Done
+ //
+
+Cleanup:
+
+ if ( GroupHandle != NULL ) {
+
+ STARTSAMTIMER;
+
+ SamrCloseHandle( &GroupHandle );
+
+ STOPSAMTIMER;
+
+ }
+
+ STOPUNPACKTIMER;
+
+ NlPrint((NL_REPL_OBJ_TIME,"Time taken to unpack GROUP object:\n"));
+ PRINTUNPACKTIMER;
+ PRINTSAMTIMER;
+
+ return Status;
+
+}
+
+
+NTSTATUS
+NlUnpackSamGroupMember (
+ IN ULONG RelativeId,
+ IN PNETLOGON_DELTA_GROUP_MEMBER DeltaGroupMember,
+ IN PDB_INFO DBInfo
+ )
+/*++
+
+Routine Description:
+
+ Set the Sam Group membership to look like the specified buffer.
+
+Arguments:
+
+ RelativeId - Relative Id of the group to open.
+
+ DeltaGroup - Description of the group membership.
+
+Return Value:
+
+ NT status code.
+
+--*/
+{
+ NTSTATUS Status;
+ SAMPR_HANDLE GroupHandle = NULL;
+
+ ULONG i;
+ ULONG j;
+
+ //
+ // Info returned from SAM.
+ //
+
+ PSAMPR_GET_MEMBERS_BUFFER OldMembersBuffer = NULL;
+
+ DEFUNPACKTIMER;
+ DEFSAMTIMER;
+
+
+ INITUNPACKTIMER;
+ INITSAMTIMER;
+
+ STARTUNPACKTIMER;
+
+ NlPrint((NL_SYNC_MORE, "UnPacking GroupMember Object %lx\n", RelativeId));
+
+ //
+ // Open a handle to the specified group.
+ //
+
+ STARTSAMTIMER;
+
+ Status = SamrOpenGroup( DBInfo->DBHandle,
+ 0, // No desired access
+ RelativeId,
+ &GroupHandle );
+
+ STOPSAMTIMER;
+
+ if (!NT_SUCCESS(Status)) {
+ GroupHandle = NULL;
+ goto Cleanup;
+ }
+
+ //
+ // Determine the current membership of the group.
+ //
+
+ STARTSAMTIMER;
+
+ Status = SamrGetMembersInGroup( GroupHandle, &OldMembersBuffer );
+
+ STOPSAMTIMER;
+
+ if (!NT_SUCCESS(Status)) {
+ OldMembersBuffer = NULL;
+ goto Cleanup;
+ }
+
+ //
+ // For each new member,
+ // If the member doesn't currently exist, add it.
+ // If the member exists but the attributes are wrong, change them.
+ //
+
+ for ( i=0; i<DeltaGroupMember->MemberCount; i++ ) {
+ BOOL MemberAlreadyExists;
+
+ //
+ // Check if this new member is already a member.
+ //
+
+ MemberAlreadyExists = FALSE;
+ for ( j=0; j<OldMembersBuffer->MemberCount; j++ ) {
+ if ( OldMembersBuffer->Members[j] == DeltaGroupMember->MemberIds[i] ) {
+ MemberAlreadyExists = TRUE;
+ OldMembersBuffer->Members[j] = 0; // Mark that we've used this entry
+ break;
+ }
+ }
+
+ //
+ // If the membership is not there already,
+ // add the new membership.
+ //
+
+ if ( !MemberAlreadyExists ) {
+ STARTSAMTIMER;
+
+ Status = SamrAddMemberToGroup( GroupHandle,
+ DeltaGroupMember->MemberIds[i],
+ DeltaGroupMember->Attributes[i] );
+
+ STOPSAMTIMER;
+
+ if (!NT_SUCCESS(Status)) {
+
+ //
+ // if this member is not created yet on the backup then
+ // ignore the error STATUS_NO_SUCH_USER, this user
+ // will be added to this group automatically when his
+ // account is created.
+ //
+ // We could let the redo log handle this problem, but that'll
+ // log an error to the event log.
+ //
+
+ if( Status != STATUS_NO_SUCH_USER ) {
+ goto Cleanup;
+ }
+ }
+
+ //
+ // If membership attributes are different,
+ // set the new attributes.
+ //
+
+ } else {
+
+ if ( DeltaGroupMember->Attributes[i] !=
+ OldMembersBuffer->Attributes[j] ) {
+
+ STARTSAMTIMER;
+
+ Status = SamrSetMemberAttributesOfGroup(
+ GroupHandle,
+ DeltaGroupMember->MemberIds[i],
+ DeltaGroupMember->Attributes[i] );
+
+ STOPSAMTIMER;
+
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+ }
+
+ }
+
+ }
+
+ //
+ // Loop through the list of old members, deleting those that should
+ // no longer exist.
+ //
+
+ for ( j=0; j<OldMembersBuffer->MemberCount; j++ ) {
+ if ( OldMembersBuffer->Members[j] != 0 ) {
+
+ STARTSAMTIMER;
+
+ Status = SamrRemoveMemberFromGroup(
+ GroupHandle,
+ OldMembersBuffer->Members[j]);
+
+ STOPSAMTIMER;
+
+ if (!NT_SUCCESS(Status)) {
+
+ //
+ // if this is the member's primary group then the
+ // user does exist on the primary, this membership
+ // should goaway when we do cleanup.
+ //
+
+ if( Status != STATUS_MEMBERS_PRIMARY_GROUP ) {
+
+ goto Cleanup;
+ }
+ }
+ }
+ }
+
+
+ Status = STATUS_SUCCESS;
+
+ //
+ // All Done
+ //
+
+Cleanup:
+
+ //
+ // Free up any locally used resources.
+ //
+
+ STARTSAMTIMER;
+
+ if ( OldMembersBuffer != NULL ) {
+ SamIFree_SAMPR_GET_MEMBERS_BUFFER( OldMembersBuffer );
+ }
+
+ if ( GroupHandle != NULL ) {
+ SamrCloseHandle( &GroupHandle );
+ }
+
+ STOPSAMTIMER;
+
+ STOPUNPACKTIMER;
+
+ NlPrint((NL_REPL_OBJ_TIME,"Time taken to unpack GROUP MEMBER object:\n"));
+ PRINTUNPACKTIMER;
+ PRINTSAMTIMER;
+
+ return Status;
+
+}
+
+
+NTSTATUS
+NlUnpackSamAlias (
+ IN PNETLOGON_DELTA_ALIAS DeltaAlias,
+ IN PDB_INFO DBInfo,
+ OUT PULONG ConflictingRID
+ )
+/*++
+
+Routine Description:
+
+ Set the Sam Alias to look like the specified buffer.
+
+Arguments:
+
+ DeltaAlias - Description of the alias.
+
+Return Value:
+
+ NT status code.
+
+--*/
+{
+ NTSTATUS Status;
+ SAMPR_HANDLE AliasHandle = NULL;
+
+ SAMPR_SR_SECURITY_DESCRIPTOR SecurityDescriptor;
+ SAMPR_ALIAS_INFO_BUFFER AliasInfo;
+ BOOLEAN SetAliasNameField = FALSE;
+
+ DEFUNPACKTIMER;
+ DEFSAMTIMER;
+
+ INITUNPACKTIMER;
+ INITSAMTIMER;
+
+ STARTUNPACKTIMER;
+
+ NlPrint((NL_SYNC_MORE, "UnPacking Alias Object (%lx) %wZ\n",
+ DeltaAlias->RelativeId,
+ &DeltaAlias->Name
+ ));
+
+ //
+ // Open a handle to the specified alias.
+ //
+
+ STARTSAMTIMER;
+
+ Status = SamICreateAccountByRid(
+ DBInfo->DBHandle,
+ SamObjectAlias,
+ DeltaAlias->RelativeId,
+ (PRPC_UNICODE_STRING) &DeltaAlias->Name,
+ 0, // No desired access
+ &AliasHandle,
+ ConflictingRID );
+
+ STOPSAMTIMER;
+
+ if (!NT_SUCCESS(Status)) {
+
+ if( (Status == STATUS_ALIAS_EXISTS) &&
+ (DeltaAlias->RelativeId == *ConflictingRID) ) {
+
+ //
+ // this group account has been renamed.
+ //
+
+ SetAliasNameField = TRUE;
+
+ Status = SamrOpenAlias(
+ DBInfo->DBHandle,
+ 0,
+ DeltaAlias->RelativeId,
+ &AliasHandle );
+
+ if (!NT_SUCCESS(Status)) {
+ AliasHandle = NULL;
+ goto Cleanup;
+ }
+ }
+ else {
+
+ AliasHandle = NULL;
+ goto Cleanup;
+ }
+ }
+
+ SET_SAM_SECOBJ_INFO(DeltaAlias, AliasHandle);
+
+ //
+ // set alias name if it has been renamed.
+ //
+
+ if ( SetAliasNameField ) {
+
+ AliasInfo.Name.Name =
+ * (PRPC_UNICODE_STRING) &DeltaAlias->Name;
+
+
+ STARTSAMTIMER;
+
+ Status = SamrSetInformationAlias(
+ AliasHandle,
+ AliasNameInformation,
+ &AliasInfo );
+
+ STOPSAMTIMER;
+
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+ }
+
+ AliasInfo.AdminComment.AdminComment =
+ * (PRPC_UNICODE_STRING) &DeltaAlias->DummyString1;
+
+
+ STARTSAMTIMER;
+
+ Status = SamrSetInformationAlias(
+ AliasHandle,
+ AliasAdminCommentInformation,
+ &AliasInfo );
+
+ STOPSAMTIMER;
+
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+
+ Status = STATUS_SUCCESS;
+
+ //
+ // All Done
+ //
+
+Cleanup:
+
+ if ( AliasHandle != NULL ) {
+
+ STARTSAMTIMER;
+
+ SamrCloseHandle( &AliasHandle );
+
+ STOPSAMTIMER;
+
+ }
+
+ STOPUNPACKTIMER;
+
+ NlPrint((NL_REPL_OBJ_TIME,"Time taken to unpack ALIAS object:\n"));
+ PRINTUNPACKTIMER;
+ PRINTSAMTIMER;
+
+ return Status;
+
+}
+
+
+NTSTATUS
+NlUnpackSamAliasMember (
+ IN ULONG RelativeId,
+ IN PNETLOGON_DELTA_ALIAS_MEMBER DeltaAliasMember,
+ IN PDB_INFO DBInfo
+ )
+/*++
+
+Routine Description:
+
+ Set the Sam Alias membership to look like the specified buffer.
+
+Arguments:
+
+ RelativeId - Relative Id of the alias to open.
+
+ DeltaAlias - Description of the alias membership.
+
+Return Value:
+
+ NT status code.
+
+--*/
+{
+ NTSTATUS Status;
+ SAMPR_HANDLE AliasHandle = NULL;
+
+ PNLPR_SID_INFORMATION DeltaSid;
+ PSAMPR_SID_INFORMATION MemberSid;
+
+ ULONG i;
+ ULONG j;
+
+ PBOOL MemberFound = NULL;
+
+ //
+ // Info returned from SAM.
+ //
+
+ SAMPR_PSID_ARRAY Members;
+
+ DEFUNPACKTIMER;
+ DEFSAMTIMER;
+
+ INITUNPACKTIMER;
+ INITSAMTIMER;
+
+ STARTUNPACKTIMER;
+
+ NlPrint((NL_SYNC_MORE, "UnPacking AliasMember Object %lx\n", RelativeId));
+
+ Members.Sids = NULL;
+
+ //
+ // Open a handle to the specified alias.
+ //
+
+ STARTSAMTIMER;
+
+ Status = SamrOpenAlias( DBInfo->DBHandle,
+ 0, // No desired access
+ RelativeId,
+ &AliasHandle );
+
+ STOPSAMTIMER;
+
+ if (!NT_SUCCESS(Status)) {
+ AliasHandle = NULL;
+ goto Cleanup;
+ }
+
+ //
+ // Determine the current membership of the alias.
+ //
+
+ STARTSAMTIMER;
+
+ Status = SamrGetMembersInAlias( AliasHandle, &Members );
+
+ STOPSAMTIMER;
+
+ if (!NT_SUCCESS(Status)) {
+ Members.Sids = NULL;
+ goto Cleanup;
+ }
+
+ //
+ // Setup MemberFound Array
+ //
+
+ if( Members.Count != 0) {
+
+ MemberFound = (PBOOL) NetpMemoryAllocate(
+ (sizeof(BOOL)) * Members.Count );
+
+ if( MemberFound == NULL ) {
+
+ Status = STATUS_NO_MEMORY;
+ goto Cleanup;
+ }
+
+ for ( j=0; j<Members.Count; j++ ) {
+
+ MemberFound[j] = FALSE;
+
+ }
+ }
+
+ //
+ // For each new member,
+ // If the member doesn't currently exist, add it.
+ // If the member exists but the attributes are wrong, change them.
+ //
+
+ DeltaSid = DeltaAliasMember->Members.Sids;
+
+ for ( i=0; i<DeltaAliasMember->Members.Count; i++, DeltaSid++ ) {
+
+ BOOL MemberAlreadyExists = FALSE;
+
+ //
+ // Check if this new member is already a member.
+ //
+
+ //
+ // first member sid pointer
+ //
+
+ MemberSid = Members.Sids;
+
+ for ( j=0; j<Members.Count; j++, MemberSid++ ) {
+
+
+ if ( RtlEqualSid( DeltaSid->SidPointer,
+ MemberSid->SidPointer ) ) {
+
+ MemberAlreadyExists = TRUE;
+ MemberFound[j] = TRUE; // Mark that we've used this entry
+
+ break;
+ }
+
+ }
+
+ //
+ // If the membership is not there already,
+ // add the new membership.
+ //
+
+ if ( !MemberAlreadyExists ) {
+
+ STARTSAMTIMER;
+
+ Status = SamrAddMemberToAlias(
+ AliasHandle,
+ (PRPC_SID)((*DeltaSid).SidPointer));
+
+ STOPSAMTIMER;
+
+
+ //
+ // If the newly added member doesn't exist,
+ // ignore the error.
+ // (Either this is a deleted user, and all is OK)
+ // (OR this is a newly added member and partial replication
+ // will pick it up.)
+ //
+ //
+ // However, DON'T IGNORE the above errors for BUILDIN
+ // database due to the following reason.
+ //
+ // During the initial database replication (or forced
+ // fullsync replication of all three databases) the ACCOUNT
+ // database is replicated first then the BUILTIN
+ // database. For some reason the ACCOUNT database
+ // fullsync replication fails, the fullsync replication
+ // of the BUILTIN database returns this error
+ // (STATUS_INVALID_MEMBER or STATUS_NO_SUCH_ERROR)
+ // because the builtin local group may contain the SID
+ // of the account database user/group which is not
+ // replicated successfully. So if we ignore this error,
+ // the builtin group membership is left incomplete.
+ //
+
+ if (!NT_SUCCESS(Status) &&
+ (( DBInfo->DBIndex == BUILTIN_DB) ||
+ ( (Status != STATUS_INVALID_MEMBER) &&
+ (Status != STATUS_NO_SUCH_MEMBER)) )) {
+
+ goto Cleanup;
+ }
+
+ }
+
+ }
+
+ //
+ // Loop through the list of old members, deleting those that should
+ // no longer exist.
+ //
+
+ MemberSid = Members.Sids;
+
+ for ( j=0; j<Members.Count; j++, MemberSid++ ) {
+
+ if ( MemberFound[j] == FALSE ) {
+
+ STARTSAMTIMER;
+
+ Status = SamrRemoveMemberFromAlias( AliasHandle,
+ MemberSid->SidPointer );
+
+ STOPSAMTIMER;
+
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+ }
+ }
+
+
+ Status = STATUS_SUCCESS;
+
+ //
+ // All Done
+ //
+
+Cleanup:
+
+ //
+ // Free up any locally used resources.
+ //
+
+ STARTSAMTIMER;
+
+ if ( Members.Sids != NULL ) {
+ SamIFree_SAMPR_PSID_ARRAY( &Members );
+ }
+
+ if ( AliasHandle != NULL ) {
+ SamrCloseHandle( &AliasHandle );
+ }
+
+ STOPSAMTIMER;
+
+ if( MemberFound != NULL ) {
+
+ NetpMemoryFree( MemberFound );
+
+ }
+
+ STOPUNPACKTIMER;
+
+ NlPrint((NL_REPL_OBJ_TIME,"Time taken to unpack ALIASMEMBER object:\n"));
+ PRINTUNPACKTIMER;
+ PRINTSAMTIMER;
+
+ return Status;
+
+}
+
+
+NTSTATUS
+NlUnpackSamDomain (
+ IN PNETLOGON_DELTA_DOMAIN DeltaDomain,
+ IN PDB_INFO DBInfo
+ )
+/*++
+
+Routine Description:
+
+ Set the SamDomain to look like the specified buffer.
+
+Arguments:
+
+ DeltaDomain - Description of the domain.
+
+Return Value:
+
+ NT status code.
+
+--*/
+{
+ NTSTATUS Status;
+
+ //
+ // Information as passed to SAM
+ //
+
+ SAMPR_DOMAIN_INFO_BUFFER DomainInfo;
+ SAMPR_SR_SECURITY_DESCRIPTOR SecurityDescriptor;
+
+ PSAMPR_DOMAIN_INFO_BUFFER QueryDomainInfoBuf = NULL;
+
+ DEFUNPACKTIMER;
+ DEFSAMTIMER;
+
+ INITUNPACKTIMER;
+ INITSAMTIMER;
+
+ STARTUNPACKTIMER;
+
+ NlPrint((NL_SYNC_MORE, "UnPacking Domain Object\n"));
+
+ SET_SAM_SECOBJ_INFO(DeltaDomain, DBInfo->DBHandle);
+
+ //
+ // Set the other attributes of the domain.
+ //
+
+ //
+ // We can't set the domain name information, however we can compare
+ // the name information on the database with the one we received
+ // in the delta. If they don't match we return appropriate error.
+ //
+
+ STARTSAMTIMER;
+
+ Status = SamrQueryInformationDomain(
+ DBInfo->DBHandle,
+ DomainNameInformation,
+ &QueryDomainInfoBuf );
+
+ STOPSAMTIMER;
+
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+
+ //
+ // compare name info.
+ //
+
+ if( !RtlEqualDomainName(
+ &DeltaDomain->DomainName,
+ (PUNICODE_STRING)&QueryDomainInfoBuf->Name.DomainName) ) {
+
+ Status = STATUS_NO_SUCH_DOMAIN; // ?? check status code.
+ goto Cleanup;
+ }
+
+ DomainInfo.Oem.OemInformation =
+ * ((PRPC_UNICODE_STRING) &DeltaDomain->OemInformation);
+
+ STARTSAMTIMER;
+
+ Status = SamrSetInformationDomain(
+ DBInfo->DBHandle,
+ DomainOemInformation,
+ &DomainInfo );
+
+ STOPSAMTIMER;
+
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+
+ OLD_TO_NEW_LARGE_INTEGER(
+ DeltaDomain->ForceLogoff,
+ DomainInfo.Logoff.ForceLogoff );
+
+ STARTSAMTIMER;
+
+ Status = SamrSetInformationDomain(
+ DBInfo->DBHandle,
+ DomainLogoffInformation,
+ &DomainInfo );
+
+ STOPSAMTIMER;
+
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+
+ DomainInfo.Password.MinPasswordLength = DeltaDomain->MinPasswordLength;
+ DomainInfo.Password.PasswordHistoryLength = DeltaDomain->PasswordHistoryLength;
+ DomainInfo.Password.PasswordProperties = DeltaDomain->DummyLong1;
+
+ OLD_TO_NEW_LARGE_INTEGER(
+ DeltaDomain->MaxPasswordAge,
+ DomainInfo.Password.MaxPasswordAge );
+
+ OLD_TO_NEW_LARGE_INTEGER(
+ DeltaDomain->MinPasswordAge,
+ DomainInfo.Password.MinPasswordAge );
+
+ STARTSAMTIMER;
+
+ Status = SamrSetInformationDomain(
+ DBInfo->DBHandle,
+ DomainPasswordInformation,
+ &DomainInfo );
+
+ STOPSAMTIMER;
+
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+
+ //
+ // If the PDC passed us lockout information,
+ // use it.
+ //
+ // NT 1.0 PDCs will pass a zero length.
+ //
+
+ if ( DeltaDomain->DummyString1.Length >= sizeof(DOMAIN_LOCKOUT_INFORMATION) ) {
+ RtlCopyMemory( &DomainInfo.Lockout,
+ DeltaDomain->DummyString1.Buffer,
+ DeltaDomain->DummyString1.Length );
+
+ STARTSAMTIMER;
+
+ Status = SamrSetInformationDomain(
+ DBInfo->DBHandle,
+ DomainLockoutInformation,
+ &DomainInfo );
+
+ STOPSAMTIMER;
+
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+
+ }
+
+
+ //
+ // Don't unpack DomainModifiedCount and DomainCreationDate!!
+ // These will be handled separately during a full sync.
+ //
+
+ //
+ // All Done
+ //
+
+ Status = STATUS_SUCCESS;
+
+Cleanup:
+
+ if ( QueryDomainInfoBuf != NULL ) {
+
+ STARTSAMTIMER;
+
+ SamIFree_SAMPR_DOMAIN_INFO_BUFFER( QueryDomainInfoBuf,
+ DomainNameInformation );
+ STOPSAMTIMER;
+ }
+
+ STOPUNPACKTIMER;
+
+ NlPrint((NL_REPL_OBJ_TIME,"Time taken to unpack DOMAIN object:\n"));
+ PRINTUNPACKTIMER;
+ PRINTSAMTIMER;
+
+
+ return Status;
+
+}
+
+
+
+//
+// builtin domain support
+//
+
+NTSTATUS
+NlUnpackSam(
+ IN PNETLOGON_DELTA_ENUM Delta,
+ IN PDB_INFO DBInfo,
+ OUT PULONG ConflictingRID,
+ PSESSION_INFO SessionInfo
+ )
+/*++
+
+Routine Description:
+
+ Install a delta into the local SAM database.
+
+Arguments:
+
+ Deltas - The delta to install
+
+ SessionInfo - Info shared between PDC and BDC
+
+Return Value:
+
+--*/
+{
+ NTSTATUS Status;
+
+ //
+ // Handle each delta type differently.
+ //
+
+ *ConflictingRID = 0;
+
+ switch ( Delta->DeltaType ) {
+ case AddOrChangeDomain:
+ Status = NlUnpackSamDomain(
+ Delta->DeltaUnion.DeltaDomain,
+ DBInfo);
+ break;
+
+ case AddOrChangeGroup:
+ Status = NlUnpackSamGroup(
+ Delta->DeltaUnion.DeltaGroup,
+ DBInfo,
+ ConflictingRID );
+ break;
+
+ case AddOrChangeAlias:
+ Status = NlUnpackSamAlias(
+ Delta->DeltaUnion.DeltaAlias,
+ DBInfo,
+ ConflictingRID );
+ break;
+
+ case AddOrChangeUser:
+ Status = NlUnpackSamUser(
+ Delta->DeltaUnion.DeltaUser,
+ DBInfo,
+ ConflictingRID,
+ SessionInfo );
+ break;
+
+ case ChangeGroupMembership:
+ Status = NlUnpackSamGroupMember(
+ Delta->DeltaID.Rid,
+ Delta->DeltaUnion.DeltaGroupMember,
+ DBInfo );
+ break;
+
+ case ChangeAliasMembership:
+ Status = NlUnpackSamAliasMember(
+ Delta->DeltaID.Rid,
+ Delta->DeltaUnion.DeltaAliasMember,
+ DBInfo );
+ break;
+
+ case DeleteGroup:
+ case DeleteGroupByName:
+ Status = NlDeleteSamGroup(
+ DBInfo->DBHandle,
+ Delta->DeltaID.Rid );
+
+ break;
+
+ case DeleteAlias:
+ Status = NlDeleteSamAlias(
+ DBInfo->DBHandle,
+ Delta->DeltaID.Rid );
+
+ break;
+
+ case DeleteUser:
+ case DeleteUserByName:
+ Status = NlDeleteSamUser(
+ DBInfo->DBHandle,
+ Delta->DeltaID.Rid );
+
+ break;
+
+ case AddOrChangeLsaPolicy:
+ Status = NlUnpackLsaPolicy(
+ Delta->DeltaUnion.DeltaPolicy,
+ DBInfo );
+ break;
+
+ case AddOrChangeLsaTDomain:
+ Status = NlUnpackLsaTDomain(
+ Delta->DeltaID.Sid,
+ Delta->DeltaUnion.DeltaTDomains,
+ DBInfo );
+ break;
+
+ case AddOrChangeLsaAccount:
+ Status = NlUnpackLsaAccount(
+ Delta->DeltaID.Sid,
+ Delta->DeltaUnion.DeltaAccounts,
+ DBInfo );
+ break;
+
+ case AddOrChangeLsaSecret:
+ Status = NlUnpackLsaSecret(
+ Delta->DeltaID.Name,
+ Delta->DeltaUnion.DeltaSecret,
+ DBInfo,
+ SessionInfo );
+ break;
+
+ case DeleteLsaTDomain:
+ Status = NlDeleteLsaTDomain(
+ Delta->DeltaID.Sid,
+ DBInfo );
+ break;
+
+ case DeleteLsaAccount:
+ Status = NlDeleteLsaAccount(
+ Delta->DeltaID.Sid,
+ DBInfo );
+ break;
+
+ case DeleteLsaSecret:
+ Status = NlDeleteLsaSecret(
+ Delta->DeltaID.Name,
+ DBInfo );
+ break;
+
+ // Nothing to unpack for this delta
+ case SerialNumberSkip:
+ Status = STATUS_SUCCESS;
+ break;
+
+ default:
+ NlPrint((NL_CRITICAL, "NlUnpackSam: invalid delta type %lx\n", Delta->DeltaType ));
+ Status = STATUS_SYNCHRONIZATION_REQUIRED;
+ }
+
+ return Status;
+}
+
+
+NTSTATUS
+NlEncryptSensitiveData(
+ IN OUT PCRYPT_BUFFER Data,
+ IN PSESSION_INFO SessionInfo
+ )
+/*++
+
+Routine Description:
+
+ Encrypt data using the the server session key.
+
+ Either DES or RC4 will be used depending on the negotiated flags in SessionInfo.
+
+Arguments:
+
+ Data: Pointer to the data to be decrypted. If the decrypted data is longer
+ than the encrypt data, this routine will allocate a buffer for
+ the returned data using MIDL_user_allocate and return a description to
+ that buffer here. In that case, this routine will free the buffer
+ containing the encrypted text data using MIDL_user_free.
+
+ SessionInfo: Info describing BDC that's calling us
+
+Return Value:
+
+ NT status code
+
+--*/
+{
+ NTSTATUS Status;
+ DATA_KEY KeyData;
+
+
+ //
+ // If both sides support RC4 encryption, use it.
+ //
+
+ if ( SessionInfo->NegotiatedFlags & NETLOGON_SUPPORTS_RC4_ENCRYPTION ) {
+
+ NlEncryptRC4( Data->Buffer, Data->Length, SessionInfo );
+ Status = STATUS_SUCCESS;
+
+
+ //
+ // If the other side is running NT 1.0,
+ // use the slower DES based encryption.
+ //
+
+ } else {
+ CYPHER_DATA TempData;
+
+ //
+ // Build a data buffer to describe the encryption key.
+ //
+
+ KeyData.Length = sizeof(NETLOGON_SESSION_KEY);
+ KeyData.MaximumLength = sizeof(NETLOGON_SESSION_KEY);
+ KeyData.Buffer = (PVOID)&SessionInfo->SessionKey;
+
+ //
+ // Build a data buffer to describe the encrypted data.
+ //
+
+ TempData.Length = 0;
+ TempData.MaximumLength = 0;
+ TempData.Buffer = NULL;
+
+ //
+ // First time make the encrypt call to determine the length.
+ //
+
+ Status = RtlEncryptData(
+ (PCLEAR_DATA)Data,
+ &KeyData,
+ &TempData );
+
+ if( Status != STATUS_BUFFER_TOO_SMALL ) {
+ return(Status);
+ }
+
+ //
+ // allocate output buffer.
+ //
+
+ TempData.MaximumLength = TempData.Length;
+ TempData.Buffer = MIDL_user_allocate( TempData.Length );
+
+ if( TempData.Buffer == NULL ) {
+ return(STATUS_NO_MEMORY);
+ }
+
+ //
+ // Encrypt the data.
+ //
+
+ IF_DEBUG( ENCRYPT ) {
+ NlPrint((NL_ENCRYPT, "NlEncryptSensitiveData: Clear data\n" ));
+ NlpDumpHexData( NL_ENCRYPT,
+ (LPDWORD)Data->Buffer,
+ Data->Length / sizeof(DWORD) );
+ }
+
+ Status = RtlEncryptData(
+ (PCLEAR_DATA)Data,
+ &KeyData,
+ &TempData );
+
+ IF_DEBUG( ENCRYPT ) {
+ NlPrint((NL_ENCRYPT, "NlEncryptSensitiveData: Encrypted data\n" ));
+ NlpDumpHexData( NL_ENCRYPT,
+ (LPDWORD)TempData.Buffer,
+ TempData.Length / sizeof(DWORD) );
+ }
+
+ //
+ // Return either the clear text or encrypted buffer to the caller.
+ //
+
+ if( NT_SUCCESS(Status) ) {
+ MIDL_user_free( Data->Buffer );
+ Data->Length = TempData.Length;
+ Data->MaximumLength = TempData.MaximumLength;
+ Data->Buffer = TempData.Buffer;
+ } else {
+ MIDL_user_free( TempData.Buffer );
+ }
+
+ }
+
+ return( Status );
+
+}
+
+
+NTSTATUS
+NlDecryptSensitiveData(
+ IN PCRYPT_BUFFER Data,
+ OUT PCRYPT_BUFFER DecryptedData,
+ IN PSESSION_INFO SessionInfo
+ )
+/*++
+
+Routine Description:
+
+ Decrypt data using the the server session key.
+
+ Either DES or RC4 will be used depending on the negotiated flags in SessionInfo.
+
+ Note: this routine doesn't decrypt in place since the caller typically
+ wants to save the encrypted data so that the operation can be retried.
+ Perhaps, I could mark the buffer that the decryption had already
+ taken place.
+
+Arguments:
+
+ Data: Pointer to the data to be decrypted.
+
+ DecryptedData: Returns a decriptor of the decypted data. The buffer
+ should be deallocated using MIDL_user_free.
+
+ SessionInfo: Info describing BDC that's calling us
+
+Return Value:
+
+ NT status code
+
+--*/
+{
+
+
+ //
+ // If both sides support RC4 encryption, use it.
+ //
+
+ if ( SessionInfo->NegotiatedFlags & NETLOGON_SUPPORTS_RC4_ENCRYPTION ) {
+
+ //
+ // Allocate a buffer and copy the encrypted data into it.
+ // RC4 decrypts in place.
+ //
+
+ DecryptedData->Buffer = MIDL_user_allocate( Data->Length );
+ if ( DecryptedData->Buffer == NULL ) {
+ return STATUS_NO_MEMORY;
+ }
+
+ DecryptedData->Length = DecryptedData->MaximumLength = Data->Length;
+ RtlCopyMemory( DecryptedData->Buffer, Data->Buffer, Data->Length );
+
+ NlDecryptRC4( DecryptedData->Buffer, DecryptedData->Length, SessionInfo );
+ return STATUS_SUCCESS;
+
+
+ //
+ // If the other side is running NT 1.0,
+ // use the slower DES based encryption.
+ //
+
+ } else {
+ NTSTATUS Status;
+ DATA_KEY KeyData;
+
+
+ //
+ // build keydata buffer.
+ //
+
+ KeyData.Length = sizeof(NETLOGON_SESSION_KEY);
+ KeyData.MaximumLength = sizeof(NETLOGON_SESSION_KEY);
+ KeyData.Buffer = (PVOID)&SessionInfo->SessionKey;
+
+
+ //
+ // First time make the decrypt call to determine the length.
+ //
+
+ DecryptedData->Length = 0;
+ DecryptedData->MaximumLength = 0;
+ DecryptedData->Buffer = NULL;
+
+ Status = RtlDecryptData( Data, &KeyData, DecryptedData );
+
+ if( Status != STATUS_BUFFER_TOO_SMALL ) {
+
+ if( NT_SUCCESS(Status) ) {
+
+ //
+ // set return buffer
+ //
+
+ DecryptedData->Length = 0;
+ DecryptedData->MaximumLength = 0;
+ DecryptedData->Buffer = NULL;
+ }
+
+ return(Status);
+ }
+
+ //
+ // allocate output buffer.
+ //
+
+ DecryptedData->MaximumLength = DecryptedData->Length;
+ DecryptedData->Buffer = MIDL_user_allocate( DecryptedData->Length );
+
+ if( DecryptedData->Buffer == NULL ) {
+ return(STATUS_NO_MEMORY);
+ }
+
+ //
+ // Decrypt the data
+ //
+
+ IF_DEBUG( ENCRYPT ) {
+ NlPrint((NL_ENCRYPT, "NlDecryptSensitiveData: Encrypted data\n" ));
+ NlpDumpHexData( NL_ENCRYPT,
+ (LPDWORD)Data->Buffer,
+ Data->Length / sizeof(DWORD) );
+ }
+
+ Status = RtlDecryptData( Data, &KeyData, DecryptedData);
+
+ IF_DEBUG( ENCRYPT ) {
+ NlPrint((NL_ENCRYPT, "NlDecryptSensitiveData: Clear data\n" ));
+ NlpDumpHexData( NL_ENCRYPT,
+ (LPDWORD)DecryptedData->Buffer,
+ DecryptedData->Length / sizeof(DWORD) );
+ }
+
+ //
+ // Free the buffer if we couldn't decrypt into it.
+ //
+
+ if ( !NT_SUCCESS(Status) ) {
+ MIDL_user_free( DecryptedData->Buffer );
+ DecryptedData->Buffer = NULL;
+ }
+
+ return Status;
+
+ }
+
+}
diff --git a/private/net/svcdlls/logonsrv/server/replutil.h b/private/net/svcdlls/logonsrv/server/replutil.h
new file mode 100644
index 000000000..41c292932
--- /dev/null
+++ b/private/net/svcdlls/logonsrv/server/replutil.h
@@ -0,0 +1,235 @@
+/*++
+
+Copyright (c) 1987-1991 Microsoft Corporation
+
+Module Name:
+
+ replutil.h
+
+Abstract:
+
+ Low level functions for SSI Replication apis
+
+Author:
+
+ Ported from Lan Man 2.0
+
+Environment:
+
+ User mode only.
+ Contains NT-specific code.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+ 22-Jul-1991 (cliffv)
+ Ported to NT. Converted to NT style.
+
+--*/
+
+//
+// Description of the FullSync key in the registry. The FullSync key stores sync
+// data in the registry across reboots.
+//
+#define NL_FULL_SYNC_KEY "SYSTEM\\CurrentControlSet\\Services\\Netlogon\\FullSync"
+
+#define FULL_SYNC_KEY_VERSION 1
+
+typedef struct _FULL_SYNC_KEY {
+ ULONG Version;
+ SYNC_STATE SyncState;
+ ULONG ContinuationRid;
+ NTSTATUS CumulativeStatus;
+ LARGE_INTEGER PdcSerialNumber;
+ LARGE_INTEGER PdcDomainCreationTime;
+} FULL_SYNC_KEY, *PFULL_SYNC_KEY;
+
+//
+// replutil.c
+//
+
+DWORD
+NlCopyUnicodeString (
+ IN PUNICODE_STRING InString,
+ OUT PUNICODE_STRING OutString
+ );
+
+DWORD
+NlCopyData(
+ IN LPBYTE *InData,
+ OUT LPBYTE *OutData,
+ DWORD DataLength
+ );
+
+VOID
+NlFreeDBDelta(
+ IN PNETLOGON_DELTA_ENUM Delta
+ );
+
+VOID
+NlFreeDBDeltaArray(
+ IN PNETLOGON_DELTA_ENUM DeltaArray,
+ IN DWORD ArraySize
+ );
+
+NTSTATUS
+NlPackSamUser (
+ IN ULONG RelativeId,
+ IN OUT PNETLOGON_DELTA_ENUM Delta,
+ IN PDB_INFO DBInfo,
+ OUT LPDWORD BufferSize,
+ IN PSESSION_INFO SessionInfo
+ );
+
+NTSTATUS
+NlPackSamGroup (
+ IN ULONG RelativeId,
+ IN OUT PNETLOGON_DELTA_ENUM Delta,
+ IN PDB_INFO DBInfo,
+ LPDWORD BufferSize
+ );
+
+NTSTATUS
+NlPackSamGroupMember (
+ IN ULONG RelativeId,
+ IN OUT PNETLOGON_DELTA_ENUM Delta,
+ IN PDB_INFO DBInfo,
+ LPDWORD BufferSize
+ );
+
+NTSTATUS
+NlPackSamAlias (
+ IN ULONG RelativeId,
+ IN OUT PNETLOGON_DELTA_ENUM Delta,
+ IN PDB_INFO DBInfo,
+ LPDWORD BufferSize
+ );
+
+NTSTATUS
+NlPackSamAliasMember (
+ IN ULONG RelativeId,
+ IN OUT PNETLOGON_DELTA_ENUM Delta,
+ IN PDB_INFO DBInfo,
+ LPDWORD BufferSize
+ );
+
+NTSTATUS
+NlPackSamDomain (
+ IN OUT PNETLOGON_DELTA_ENUM Delta,
+ IN PDB_INFO DBInfo,
+ IN LPDWORD BufferSize
+ );
+
+NTSTATUS
+NlUnpackSam(
+ IN PNETLOGON_DELTA_ENUM Delta,
+ IN PDB_INFO DBInfo,
+ OUT PULONG ConflictingRID,
+ PSESSION_INFO SessionInfo
+ );
+
+NTSTATUS
+NlEncryptSensitiveData(
+ IN OUT PCRYPT_BUFFER Data,
+ IN PSESSION_INFO SessionInfo
+ );
+
+NTSTATUS
+NlDecryptSensitiveData(
+ IN PCRYPT_BUFFER Data,
+ OUT PCRYPT_BUFFER DecryptedData,
+ IN PSESSION_INFO SessionInfo
+ );
+
+//
+// repluas.c
+//
+
+NTSTATUS
+NlPackUasHeader(
+ IN BYTE Opcode,
+ IN DWORD InitialSize,
+ OUT PUSHORT *RecordSize,
+ IN OUT PBUFFER_DESCRIPTOR BufferDescriptor
+ );
+
+NTSTATUS
+NlPackUasUser (
+ IN ULONG RelativeId,
+ IN OUT PBUFFER_DESCRIPTOR BufferDescriptor,
+ IN PDB_INFO DBInfo,
+ IN PNETLOGON_SESSION_KEY SessionKey,
+ IN LONG RotateCount
+ );
+
+NTSTATUS
+NlPackUasGroup (
+ IN ULONG RelativeId,
+ IN OUT PBUFFER_DESCRIPTOR BufferDescriptor,
+ IN PDB_INFO DBInfo,
+ IN OUT PDWORD UasBuiltinGroups
+ );
+
+NTSTATUS
+NlPackUasBuiltinGroup(
+ IN DWORD Index,
+ IN OUT PBUFFER_DESCRIPTOR BufferDescriptor,
+ IN PDWORD UasBuiltinGroup
+ );
+
+NTSTATUS
+NlPackUasGroupMember (
+ IN ULONG RelativeId,
+ IN OUT PBUFFER_DESCRIPTOR BufferDescriptor,
+ IN PDB_INFO DBInfo
+ );
+
+NTSTATUS
+NlPackUasUserGroupMember (
+ IN ULONG RelativeId,
+ IN OUT PBUFFER_DESCRIPTOR BufferDescriptor,
+ IN PDB_INFO DBInfo
+ );
+
+NTSTATUS
+NlPackUasDomain (
+ IN OUT PBUFFER_DESCRIPTOR BufferDescriptor,
+ IN PDB_INFO DBInfo
+ );
+
+NTSTATUS
+NlPackUasDelete (
+ IN NETLOGON_DELTA_TYPE DeltaType,
+ IN ULONG RelativeId,
+ IN LPWSTR AccountName,
+ IN OUT PBUFFER_DESCRIPTOR BufferDescriptor,
+ IN PDB_INFO DBInfo
+ );
+
+NTSTATUS
+NlDeleteSamUser(
+ SAMPR_HANDLE DomainHandle,
+ ULONG Rid
+ );
+
+NTSTATUS
+NlDeleteSamGroup(
+ SAMPR_HANDLE DomainHandle,
+ ULONG Rid
+ );
+
+NTSTATUS
+NlDeleteSamAlias(
+ SAMPR_HANDLE DomainHandle,
+ ULONG Rid
+ );
+
+//
+// lsrvrepl.c
+//
+
+VOID
+NlSetFullSyncKey(
+ ULONG DBIndex,
+ PFULL_SYNC_KEY FullSyncKey
+ );
diff --git a/private/net/svcdlls/logonsrv/server/sources b/private/net/svcdlls/logonsrv/server/sources
new file mode 100644
index 000000000..5cdbddbae
--- /dev/null
+++ b/private/net/svcdlls/logonsrv/server/sources
@@ -0,0 +1,115 @@
+!IF 0
+
+Copyright (c) 1989-92 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+Author:
+
+ Steve Wood (stevewo) 12-Apr-1990
+
+NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl
+
+!ENDIF
+
+#
+# The MAJORCOMP and MINORCOMP variables are defined
+# so that $(MAJORCOMP)$(MINORCOMP)filename can be used in
+# cross compiling to provide unique filenames in a flat namespace.
+#
+
+MAJORCOMP=net
+MINORCOMP=logonsrv
+
+TARGETNAME=netlogon
+TARGETPATH=$(BASEDIR)\public\sdk\lib
+
+TARGETTYPE=DYNLINK
+DLLENTRY=NetlogonDllInit
+
+TARGETLIBS= $(BASEDIR)\Public\Sdk\Lib\*\netlib.lib \
+ $(BASEDIR)\public\sdk\lib\*\netapi32.lib \
+ $(BASEDIR)\public\sdk\lib\*\samsrv.lib \
+ $(BASEDIR)\Public\Sdk\Lib\*\kernel32.lib \
+ $(BASEDIR)\Public\Sdk\Lib\*\advapi32.lib \
+ $(BASEDIR)\public\sdk\lib\*\lsasrv.lib \
+ $(BASEDIR)\public\sdk\lib\*\rpcutil.lib \
+ $(BASEDIR)\public\sdk\lib\*\msv1_0.lib \
+ $(BASEDIR)\public\sdk\lib\*\rpcrt4.lib \
+ $(BASEDIR)\public\sdk\lib\*\rpcndr.lib
+
+INCLUDES=.;..;..\..\..\inc;..\..\..\..\inc
+
+#
+# Indicate that a .prf file exists.
+#
+
+NTPROFILEINPUT=yes
+
+#
+# Set RPC_SERVER so that MIDL generated stubs use the version of structures
+# that contain non-opaque PISID instead of PSID.
+#
+C_DEFINES=-DRPC_SERVER -DRPC_NO_WINDOWS_H
+
+!IFNDEF DISABLE_NET_UNICODE
+UNICODE=1
+NET_C_DEFINES=-DUNICODE
+!ENDIF
+
+SOURCES= \
+ announce.c \
+ changelg.c \
+ chutil.c \
+ chworker.c \
+ error.c \
+ logon_s.c \
+ logonapi.c \
+ lsarepl.c \
+ lsrvrepl.c \
+ lsrvutil.c \
+ mailslot.c \
+ netlogon.c \
+ netlogon.rc \
+ nlp.c \
+ nlsecure.c \
+ oldstub.c \
+ parse.c \
+ repluas.c \
+ replutil.c \
+ srvsess.c \
+ ssiapi.c \
+ ssiauth.c \
+ trustutl.c
+
+#
+# Next specify options for the compiler.
+#
+
+MSC_WARNING_LEVEL=/W3 /WX
+
+USE_CRTDLL=1
+
+UMTYPE=console
+UMAPPL=nltest
+UMRES=$(@R).res
+UMLIBS= obj\*\ssiauth.obj \
+ obj\*\chutil.obj \
+ obj\*\nltest1.obj \
+ $(BASEDIR)\Public\Sdk\Lib\*\netlib.lib \
+ $(BASEDIR)\Public\Sdk\Lib\*\samlib.lib \
+ $(BASEDIR)\public\sdk\lib\*\netapi32.lib \
+ $(BASEDIR)\public\sdk\lib\*\ntdll.lib
+
+NTTARGETFILE1=obj\*\nltest.res
+
+
diff --git a/private/net/svcdlls/logonsrv/server/srvsess.c b/private/net/svcdlls/logonsrv/server/srvsess.c
new file mode 100644
index 000000000..d83ebfa24
--- /dev/null
+++ b/private/net/svcdlls/logonsrv/server/srvsess.c
@@ -0,0 +1,1728 @@
+/*++
+
+Copyright (c) 1987-1991 Microsoft Corporation
+
+Module Name:
+
+ srvsess.c
+
+Abstract:
+
+ Routines for managing the ServerSession structure.
+
+Author:
+
+ Ported from Lan Man 2.0
+
+Environment:
+
+ User mode only.
+ Contains NT-specific code.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+ 12-Jul-1991 (cliffv)
+ Ported to NT. Converted to NT style.
+
+--*/
+
+//
+// Common include files.
+//
+
+#define INITSSI_ALLOCATE // Allocate all ssiinit.h global variables
+#include <logonsrv.h> // Include files common to entire service
+#undef INITSSI_ALLOCATE
+
+//
+// Include files specific to this .c file
+//
+
+#include <lmapibuf.h>
+#include <lmaudit.h>
+#include <lmerr.h>
+#include <lmserver.h>
+#include <lmshare.h>
+#include <tstring.h> // TOUPPER
+
+#define MAX_WOC_INTERROGATE 8 // 2 hours
+#define KILL_SESSION_TIME (4*4*24) // 4 Days
+
+
+DWORD
+NlGetHashVal(
+ IN LPSTR UpcaseOemComputerName,
+ IN DWORD HashTableSize
+ )
+/*++
+
+Routine Description:
+
+ Generate a HashTable index for the specified ComputerName.
+
+ Notice that all sessions for a particular ComputerName hash to the same
+ value. The ComputerName make a suitable hash key all by itself.
+ Also, at times we visit all the session entries for a particular
+ ComputerName. By using only the ComputerName as the hash key, I
+ can limit my search to the single hash chain.
+
+Arguments:
+
+ UpcaseOemComputerName - The upper case OEM name of the computer on
+ the client side of the secure channel setup.
+
+ HashTableSize - Number of entries in the hash table (must be a power of 2)
+
+Return Value:
+
+ Returns an index into the HashTable.
+
+--*/
+{
+ UCHAR c;
+ DWORD value = 0;
+
+ while (c = *UpcaseOemComputerName++) {
+ value += (DWORD) c;
+ }
+
+ return (value & (HashTableSize-1));
+}
+
+
+
+NTSTATUS
+NlAddBdcServerSession(
+ IN ULONG ServerRid,
+ IN PUNICODE_STRING AccountName OPTIONAL,
+ IN DWORD Flags
+ )
+/*++
+
+Routine Description:
+
+ Create a server session to represent this BDC account.
+
+Arguments:
+
+ ServerRid - Rid of server to add to list.
+
+ AccountName - Optionally specifies the account name of the account.
+
+ Flags - Specifies the initial SsFlags to associate with the entry.
+
+Return Value:
+
+ Status of the operation.
+
+--*/
+{
+ NTSTATUS Status;
+ PUNICODE_STRING ServerName;
+ WCHAR LocalServerName[CNLEN+1];
+ LONG LocalServerNameSize;
+
+ SAMPR_ULONG_ARRAY Use = {0, NULL};
+ SAMPR_RETURNED_USTRING_ARRAY Names = {0, NULL};
+
+ //
+ // If we were given an account name,
+ // just use it.
+
+ if ( AccountName != NULL ) {
+
+ ServerName = AccountName;
+
+ //
+ // Convert the specified ServerRid into a server name.
+ //
+
+ } else {
+
+
+ Status = SamrLookupIdsInDomain(
+ NlGlobalDBInfoArray[SAM_DB].DBHandle,
+ 1,
+ &ServerRid,
+ &Names,
+ &Use );
+
+ if ( !NT_SUCCESS(Status) ) {
+ Names.Element = NULL;
+ Use.Element = NULL;
+ if ( Status = STATUS_NONE_MAPPED ) {
+ Status = STATUS_SUCCESS;
+ }
+ goto Cleanup;
+ }
+
+ NlAssert( Names.Count == 1 );
+ NlAssert( Names.Element != NULL );
+ NlAssert( Use.Count == 1 );
+ NlAssert( Use.Element != NULL );
+
+ if( Use.Element[0] != SidTypeUser ) {
+ Status = STATUS_SUCCESS;
+ goto Cleanup;
+ }
+
+ ServerName = (PUNICODE_STRING)&Names.Element[0];
+ }
+
+
+
+ //
+ // Build a zero terminated server name.
+ //
+ // Strip the trailing postfix.
+ //
+ // Ignore servers with malformed names. They aren't really BDCs so don't
+ // cloud the issue by failing to start netlogon.
+ //
+
+ LocalServerNameSize = ServerName->Length;
+ if ( (Flags & SS_LM_BDC) == 0 ) {
+ LocalServerNameSize -= SSI_ACCOUNT_NAME_POSTFIX_LENGTH * sizeof(WCHAR);
+ }
+
+ if ( LocalServerNameSize < 0 ||
+ LocalServerNameSize + sizeof(WCHAR) > sizeof(LocalServerName) ) {
+
+ NlPrint((NL_SERVER_SESS,
+ "NlAddBdcServerSession: %wZ: Skipping add of invalid server name\n",
+ ServerName ));
+ Status = STATUS_SUCCESS;
+ goto Cleanup;
+ }
+
+ RtlCopyMemory( LocalServerName, ServerName->Buffer, LocalServerNameSize );
+ LocalServerName[ LocalServerNameSize / sizeof(WCHAR) ] = L'\0';
+
+
+
+ //
+ // Don't add ourselves to the list.
+ //
+
+ if ( NlNameCompare( LocalServerName,
+ NlGlobalUnicodeComputerName,
+ NAMETYPE_COMPUTER ) == 0 ) {
+
+ NlPrint((NL_SERVER_SESS,
+ "NlAddBdcServerSession: " FORMAT_LPWSTR
+ ": Skipping add of ourself\n",
+ LocalServerName ));
+
+ Status = STATUS_SUCCESS;
+ goto Cleanup;
+ }
+
+ // Always force a pulse to a newly created server.
+ Status = NlInsertServerSession(
+ LocalServerName,
+ Flags | SS_FORCE_PULSE,
+ ServerRid,
+ NULL,
+ NULL );
+
+ if ( !NT_SUCCESS(Status) ) {
+ NlPrint((NL_CRITICAL,
+ "NlAddBdcServerSession: " FORMAT_LPWSTR
+ ": Couldn't create server session entry (0x%lx)\n",
+ LocalServerName,
+ Status ));
+ goto Cleanup;
+ }
+
+ NlPrint((NL_SERVER_SESS,
+ "NlAddBdcServerSession: " FORMAT_LPWSTR ": Added %s BDC account\n",
+ LocalServerName,
+ (Flags & SS_LM_BDC) ? "LANMAN" : "NT" ));
+
+ Status = STATUS_SUCCESS;
+
+Cleanup:
+
+ if( Names.Element != NULL ) {
+ SamIFree_SAMPR_RETURNED_USTRING_ARRAY( &Names );
+ }
+
+ if( Use.Element != NULL ) {
+ SamIFree_SAMPR_ULONG_ARRAY( &Use );
+ }
+
+ return Status;
+}
+
+
+
+NTSTATUS
+NlBuildLmBdcList(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ Get the list of all Lanman DC's in this domain from SAM.
+
+Arguments:
+
+ None
+
+Return Value:
+
+ Status of the operation.
+--*/
+{
+ NTSTATUS Status;
+
+ SAMPR_ULONG_ARRAY RelativeIdArray = {0, NULL};
+ SAMPR_ULONG_ARRAY UseArray = {0, NULL};
+ RPC_UNICODE_STRING GroupNameString;
+ SAMPR_HANDLE GroupHandle = NULL;
+ ULONG ServersGroupRid;
+
+ PSAMPR_GET_MEMBERS_BUFFER MembersBuffer = NULL;
+
+ ULONG i;
+
+
+ //
+ // Determine the RID of the Servers group.
+ //
+
+ RtlInitUnicodeString( (PUNICODE_STRING)&GroupNameString,
+ SSI_SERVER_GROUP_W );
+
+ Status = SamrLookupNamesInDomain(
+ NlGlobalDBInfoArray[SAM_DB].DBHandle,
+ 1,
+ &GroupNameString,
+ &RelativeIdArray,
+ &UseArray );
+
+ if ( !NT_SUCCESS(Status) ) {
+ RelativeIdArray.Element = NULL;
+ UseArray.Element = NULL;
+ // Its OK if the SERVERS group doesn't exist
+ if ( Status == STATUS_NONE_MAPPED ) {
+ Status = STATUS_SUCCESS;
+ }
+ goto Cleanup;
+ }
+
+ //
+ // We should get back exactly one entry of info back.
+ //
+
+ NlAssert( UseArray.Count == 1 );
+ NlAssert( UseArray.Element != NULL );
+ NlAssert( RelativeIdArray.Count == 1 );
+ NlAssert( RelativeIdArray.Element != NULL );
+
+ if ( UseArray.Element[0] != SidTypeGroup ) {
+ Status = STATUS_SUCCESS;
+ goto Cleanup;
+ }
+
+ ServersGroupRid = RelativeIdArray.Element[0];
+
+
+
+ //
+ // Open the SERVERS group
+ //
+
+ Status = SamrOpenGroup( NlGlobalDBInfoArray[SAM_DB].DBHandle,
+ 0, // No desired access
+ ServersGroupRid,
+ &GroupHandle );
+
+ if ( !NT_SUCCESS(Status) ) {
+ GroupHandle = NULL;
+ goto Cleanup;
+ }
+
+
+ //
+ // Enumerate members in the SERVERS group.
+ //
+
+ Status = SamrGetMembersInGroup( GroupHandle, &MembersBuffer );
+
+ if (!NT_SUCCESS(Status)) {
+ MembersBuffer = NULL;
+ goto Cleanup;
+ }
+
+
+ //
+ // For each member of the SERVERS group,
+ // add an entry in the downlevel servers table.
+ //
+
+ for ( i=0; i < MembersBuffer->MemberCount; i++ ) {
+
+ Status = NlAddBdcServerSession( MembersBuffer->Members[i],
+ NULL,
+ SS_BDC | SS_LM_BDC );
+
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+ }
+
+
+ //
+ // Success
+ //
+
+ Status = STATUS_SUCCESS;
+
+
+
+ //
+ // Free locally used resources.
+ //
+
+Cleanup:
+
+ SamIFree_SAMPR_ULONG_ARRAY( &RelativeIdArray );
+ SamIFree_SAMPR_ULONG_ARRAY( &UseArray );
+
+ if ( MembersBuffer != NULL ) {
+ SamIFree_SAMPR_GET_MEMBERS_BUFFER( MembersBuffer );
+ }
+
+ if( GroupHandle != NULL ) {
+ (VOID) SamrCloseHandle( &GroupHandle );
+ }
+
+ return Status;
+}
+
+//
+// Number of machine accounts read from SAM on each call
+//
+#define MACHINES_PER_PASS 250
+
+
+NTSTATUS
+NlBuildNtBdcList(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ Get the list of all Nt Bdc DC's in this domain from SAM.
+
+Arguments:
+
+ None
+
+Return Value:
+
+ Status of the operation.
+--*/
+{
+ NTSTATUS Status;
+ NTSTATUS SamStatus;
+
+ SAMPR_DISPLAY_INFO_BUFFER DisplayInformation;
+ PDOMAIN_DISPLAY_MACHINE MachineInformation = NULL;
+ ULONG SamIndex;
+
+
+
+ //
+ // Loop building a list of BDC names from SAM.
+ //
+ // On each iteration of the loop,
+ // get the next several machine accounts from SAM.
+ // determine which of those names are DC names.
+ // Merge the DC names into the list we're currently building of all DCs.
+ //
+
+ SamIndex = 0;
+ DisplayInformation.MachineInformation.Buffer = NULL;
+ do {
+ //
+ // Arguments to SamrQueryDisplayInformation
+ //
+ ULONG TotalBytesAvailable;
+ ULONG BytesReturned;
+ ULONG EntriesRead;
+
+ DWORD i;
+
+ //
+ // Get the list of machine accounts from SAM
+ //
+
+ SamStatus = SamrQueryDisplayInformation(
+ NlGlobalDBInfoArray[SAM_DB].DBHandle,
+ DomainDisplayMachine,
+ SamIndex,
+ MACHINES_PER_PASS,
+ 0xFFFFFFFF,
+ &TotalBytesAvailable,
+ &BytesReturned,
+ &DisplayInformation );
+
+ if ( !NT_SUCCESS(SamStatus) ) {
+ NlPrint((NL_CRITICAL,
+ "SamrQueryDisplayInformation failed: 0x%08lx\n",
+ Status));
+ Status = SamStatus;
+ goto Cleanup;
+ }
+
+ MachineInformation = (PDOMAIN_DISPLAY_MACHINE)
+ DisplayInformation.MachineInformation.Buffer;
+ EntriesRead = DisplayInformation.MachineInformation.EntriesRead;
+
+
+ //
+ // Set up for the next call to Sam.
+ //
+
+ if ( SamStatus == STATUS_MORE_ENTRIES ) {
+ SamIndex = MachineInformation[EntriesRead-1].Index + 1;
+ }
+
+
+ //
+ // Loop though the list of machine accounts finding the Server accounts.
+ //
+
+ for ( i=0; i<EntriesRead; i++ ) {
+
+ //
+ // Ensure the machine account is a server account.
+ //
+
+ if ( MachineInformation[i].AccountControl &
+ USER_SERVER_TRUST_ACCOUNT ) {
+
+
+ //
+ // Insert the server session.
+ //
+
+ Status = NlAddBdcServerSession(
+ MachineInformation[i].Rid,
+ &MachineInformation[i].Machine,
+ SS_BDC );
+
+ if ( !NT_SUCCESS(Status) ) {
+ goto Cleanup;
+ }
+
+ }
+ }
+
+ //
+ // Free the buffer returned from SAM.
+ //
+ SamIFree_SAMPR_DISPLAY_INFO_BUFFER( &DisplayInformation,
+ DomainDisplayMachine );
+ DisplayInformation.MachineInformation.Buffer = NULL;
+
+ } while ( SamStatus == STATUS_MORE_ENTRIES );
+
+ //
+ // Success
+ //
+
+ Status = STATUS_SUCCESS;
+
+
+
+ //
+ // Free locally used resources.
+ //
+Cleanup:
+
+ SamIFree_SAMPR_DISPLAY_INFO_BUFFER( &DisplayInformation,
+ DomainDisplayMachine );
+
+ return Status;
+}
+
+
+
+
+VOID
+NlTransportOpen(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ Initialize the list of transports
+
+Arguments:
+
+ None
+
+Return Value:
+
+ Status of the operation
+
+--*/
+{
+ NET_API_STATUS NetStatus;
+ PSERVER_TRANSPORT_INFO_0 TransportInfo0;
+ DWORD EntriesRead;
+ DWORD TotalEntries;
+ DWORD i;
+ DWORD BufferSize;
+ LPBYTE Where;
+
+ NlGlobalTransportCount = 0;
+ //
+ // Enumerate the transports supported by the server.
+ //
+
+ NetStatus = NetServerTransportEnum(
+ NULL, // local
+ 0, // level 0
+ (LPBYTE *) &TransportInfo0,
+ 0xFFFFFFFF, // PrefMaxLength
+ &EntriesRead,
+ &TotalEntries,
+ NULL ); // No resume handle
+
+ if ( NetStatus != NERR_Success && NetStatus != ERROR_MORE_DATA ) {
+ NlPrint(( NL_CRITICAL, "Cannot NetServerTransportEnum %ld\n", NetStatus ));
+ return;
+ }
+
+ if ( EntriesRead == 0 ) {
+ NlPrint(( NL_CRITICAL, "NetServerTransportEnum returned 0 entries\n" ));
+ (VOID) NetApiBufferFree( TransportInfo0 );
+ return;
+ }
+
+ //
+ // Allocate a buffer to contain just the transport names.
+ //
+
+ BufferSize = 0;
+ for ( i=0; i<EntriesRead; i++ ) {
+ BufferSize += sizeof(LPWSTR) +
+ wcslen(TransportInfo0[i].svti0_transportname) * sizeof(WCHAR) +
+ sizeof(WCHAR);
+ }
+
+ NlGlobalTransportList = NetpMemoryAllocate( BufferSize );
+
+ if ( NlGlobalTransportList == NULL ) {
+ NlPrint(( NL_CRITICAL, "NlTransportOpen: no memory\n" ));
+ (VOID) NetApiBufferFree( TransportInfo0 );
+ return;
+ }
+
+ //
+ // Copy the transport names into the buffer.
+ //
+
+ Where = (LPBYTE)(&NlGlobalTransportList[EntriesRead]);
+
+ for ( i=0; i<EntriesRead; i++ ) {
+ DWORD Size;
+
+ NlGlobalTransportList[i] = (LPWSTR) Where;
+
+ Size = wcslen(TransportInfo0[i].svti0_transportname) * sizeof(WCHAR) +
+ sizeof(WCHAR);
+ RtlCopyMemory( Where,
+ TransportInfo0[i].svti0_transportname,
+ Size );
+ Where += Size;
+ NlPrint(( NL_SERVER_SESS, "Server Transport %ld: " FORMAT_LPWSTR "\n",
+ i,
+ TransportInfo0[i].svti0_transportname ));
+ }
+
+ NlGlobalTransportCount = EntriesRead;
+ (VOID) NetApiBufferFree( TransportInfo0 );
+ return;
+}
+
+LPWSTR
+NlTransportLookupTransportName(
+ IN LPWSTR TransportName
+ )
+/*++
+
+Routine Description:
+
+ Returns a transport name equal to the one passed in. However, the
+ returned transport name is static and need not be freed.
+
+Arguments:
+
+ TransportName - Name of the transport to look up
+
+Return Value:
+
+ NULL - on any error
+
+ Otherwise, returns a pointer to the transport name
+
+--*/
+{
+ DWORD i;
+
+ //
+ // If we're not initialized yet,
+ // just return
+ //
+
+ if ( TransportName == NULL || NlGlobalTransportCount == 0 ) {
+ return NULL;
+ }
+
+ //
+ // Find this transport in the list of transports.
+ //
+
+ for ( i=0; i<NlGlobalTransportCount; i++ ) {
+ if ( _wcsicmp( TransportName, NlGlobalTransportList[i] ) == 0 ) {
+ return NlGlobalTransportList[i];
+ }
+ }
+
+ return NULL;
+}
+
+LPWSTR
+NlTransportLookup(
+ IN LPWSTR ClientName
+ )
+/*++
+
+Routine Description:
+
+ Determine what transport the specified client is using to access this
+ server.
+
+Arguments:
+
+ ClientName - Name of the client connected to this server.
+
+Return Value:
+
+ NULL - The client isn't currently connected
+
+ Otherwise, returns a pointer to the transport name
+
+--*/
+{
+ NET_API_STATUS NetStatus;
+ PSESSION_INFO_502 SessionInfo502;
+ DWORD EntriesRead;
+ DWORD TotalEntries;
+ DWORD i;
+ DWORD BestTime;
+ DWORD BestEntry;
+ LPWSTR TransportName;
+
+ WCHAR UncClientName[UNCLEN+1];
+
+ //
+ // If we're not initialized yet,
+ // just return
+ //
+
+ if ( NlGlobalTransportCount == 0 ) {
+ return NULL;
+ }
+
+ //
+ // Enumerate all the sessions from the particular client.
+ //
+
+ UncClientName[0] = '\\';
+ UncClientName[1] = '\\';
+ wcscpy( &UncClientName[2], ClientName );
+
+ NetStatus = NetSessionEnum(
+ NULL, // local
+ UncClientName, // Client to query
+ NULL, // user name
+ 502,
+ (LPBYTE *)&SessionInfo502,
+ 1024, // PrefMaxLength
+ &EntriesRead,
+ &TotalEntries,
+ NULL ); // No resume handle
+
+ if ( NetStatus != NERR_Success && NetStatus != ERROR_MORE_DATA ) {
+ NlPrint(( NL_CRITICAL,
+ "NlTransportLookup: " FORMAT_LPWSTR ": Cannot NetSessionEnum %ld\n",
+ UncClientName,
+ NetStatus ));
+ return NULL;
+ }
+
+ if ( EntriesRead == 0 ) {
+ NlPrint(( NL_CRITICAL,
+ "NlTransportLookup: " FORMAT_LPWSTR ": No session exists.\n",
+ UncClientName ));
+ (VOID) NetApiBufferFree( SessionInfo502 );
+ return NULL;
+ }
+
+ //
+ // Loop through the list of transports finding the best one.
+ //
+
+ BestTime = 0xFFFFFFFF;
+
+ for ( i=0; i<EntriesRead; i++ ) {
+#ifdef notdef
+ //
+ // We're only looking for null sessions
+ //
+ if ( SessionInfo502[i].sesi502_username != NULL ) {
+ continue;
+ }
+
+ NlPrint(( NL_SERVER_SESS, "NlTransportLookup: "
+ FORMAT_LPWSTR " as " FORMAT_LPWSTR " on " FORMAT_LPWSTR "\n",
+ UncClientName,
+ SessionInfo502[i].sesi502_username,
+ SessionInfo502[i].sesi502_transport ));
+#endif // notdef
+
+ //
+ // Find the latest session
+ //
+
+ if ( BestTime > SessionInfo502[i].sesi502_idle_time ) {
+
+ // NlPrint(( NL_SERVER_SESS, "NlTransportLookup: Best Entry\n" ));
+ BestEntry = i;
+ BestTime = SessionInfo502[i].sesi502_idle_time;
+ }
+ }
+
+ //
+ // If an entry was found,
+ // Find this transport in the list of transports.
+ //
+
+ if ( BestTime != 0xFFFFFFFF ) {
+ TransportName = NlTransportLookupTransportName(
+ SessionInfo502[BestEntry].sesi502_transport );
+ if ( TransportName == NULL ) {
+ NlPrint(( NL_CRITICAL,
+ "NlTransportLookup: " FORMAT_LPWSTR ": Transport not found\n",
+ SessionInfo502[BestEntry].sesi502_transport ));
+ } else {
+ NlPrint(( NL_SERVER_SESS,
+ "NlTransportLookup: " FORMAT_LPWSTR ": Use Transport " FORMAT_LPWSTR "\n",
+ UncClientName,
+ TransportName ));
+ }
+ } else {
+ TransportName = NULL;
+ }
+
+ (VOID) NetApiBufferFree( SessionInfo502 );
+ return TransportName;
+}
+
+
+VOID
+NlTransportClose(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ Free the list of transports
+
+Arguments:
+
+ None
+
+Return Value:
+
+ Status of the operation
+
+--*/
+{
+ NetpMemoryFree( NlGlobalTransportList );
+ NlGlobalTransportList = NULL;
+ NlGlobalTransportCount = 0;
+}
+
+
+
+
+NTSTATUS
+NlInitSSI(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ Allocate and Initialize SSI related data structures. It will
+ allocate two data structures: one to hold the hash table of pointers
+ (to linked list of member entries) and another to to serve as memory
+ pool.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NT Status Code
+
+--*/
+{
+ DWORD i;
+ NTSTATUS Status;
+
+ //
+ // Initialize the replicator critical section.
+ //
+
+ // InitializeCriticalSection( &NlGlobalReplicatorCritSect );
+ // InitializeCriticalSection( &NlGlobalTrustListCritSect );
+ InitializeCriticalSection( &NlGlobalServerSessionTableCritSect );
+ NlGlobalSSICritSectInit = TRUE;
+
+
+
+
+ //
+ // Allocate NlGlobalServerSessionHashTable on DCs
+ //
+
+ LOCK_SERVER_SESSION_TABLE();
+
+ NlGlobalServerSessionHashTable = (PLIST_ENTRY)
+ NetpMemoryAllocate( sizeof(LIST_ENTRY) *SERVER_SESSION_HASH_TABLE_SIZE);
+
+ if ( NlGlobalServerSessionHashTable == NULL ) {
+ UNLOCK_SERVER_SESSION_TABLE();
+ return STATUS_NO_MEMORY;
+ }
+
+ for ( i=0; i< SERVER_SESSION_HASH_TABLE_SIZE; i++ ) {
+ InitializeListHead( &NlGlobalServerSessionHashTable[i] );
+ }
+
+ UNLOCK_SERVER_SESSION_TABLE();
+
+ //
+ // On the PDC,
+ // Initialize the server session table to contain all the BDCs.
+ //
+
+ if ( NlGlobalRole == RolePrimary ) {
+ Status = NlBuildLmBdcList();
+
+ if ( NT_SUCCESS(Status) ) {
+ Status = NlBuildNtBdcList();
+ }
+ } else {
+ Status = STATUS_SUCCESS;
+ }
+
+ //
+ // Build a list of transports for later reference
+ //
+
+ NlTransportOpen();
+
+ return Status;
+}
+
+
+
+PSERVER_SESSION
+NlFindNamedServerSession(
+ IN LPWSTR ComputerName
+ )
+/*++
+
+Routine Description:
+
+ Find the specified entry in the Server Session Table.
+
+ Enter with the ServerSessionTable Sem locked
+
+
+Arguments:
+
+ ComputerName - The name of the computer on the client side of the
+ secure channel.
+
+Return Value:
+
+ Returns a pointer to pointer to the found entry. If there is no such
+ entry, return a pointer to NULL.
+
+--*/
+{
+ NTSTATUS Status;
+ PLIST_ENTRY ListEntry;
+ DWORD Index;
+ CHAR UpcaseOemComputerName[CNLEN+1];
+ ULONG OemComputerNameSize;
+
+ //
+ // Ensure the ServerSession Table is initialized.
+ //
+
+ if (NlGlobalServerSessionHashTable == NULL) {
+ return NULL;
+ }
+
+
+ //
+ // Convert the computername to uppercase OEM for easier comparison.
+ //
+
+ Status = RtlUpcaseUnicodeToOemN(
+ UpcaseOemComputerName,
+ sizeof(UpcaseOemComputerName)-1,
+ &OemComputerNameSize,
+ ComputerName,
+ wcslen(ComputerName)*sizeof(WCHAR) );
+
+ if ( !NT_SUCCESS(Status) ) {
+ return NULL;
+ }
+
+ UpcaseOemComputerName[OemComputerNameSize] = '\0';
+
+
+
+ //
+ // Loop through this hash chain trying the find the right entry.
+ //
+
+ Index = NlGetHashVal( UpcaseOemComputerName, SERVER_SESSION_HASH_TABLE_SIZE );
+
+ for ( ListEntry = NlGlobalServerSessionHashTable[Index].Flink ;
+ ListEntry != &NlGlobalServerSessionHashTable[Index] ;
+ ListEntry = ListEntry->Flink) {
+
+ PSERVER_SESSION ServerSession;
+
+ ServerSession = CONTAINING_RECORD( ListEntry, SERVER_SESSION, SsHashList );
+
+ //
+ // Compare the worstation name
+ //
+
+ if ( lstrcmpA( UpcaseOemComputerName,
+ ServerSession->SsComputerName ) != 0 ) {
+ continue;
+ }
+
+ return ServerSession;
+ }
+
+ return NULL;
+}
+
+
+NTSTATUS
+NlInsertServerSession(
+ IN LPWSTR ComputerName,
+ IN DWORD Flags,
+ IN ULONG AccountRid,
+ IN PNETLOGON_CREDENTIAL AuthenticationSeed OPTIONAL,
+ IN PNETLOGON_CREDENTIAL AuthenticationResponse OPTIONAL
+ )
+/*++
+
+Routine Description:
+
+ Inserts the described entry into the ServerSession Table.
+
+ The server session entry is created for two reasons: 1) it represents
+ the server side of a secure channel, and 2) on a PDC, it represents the
+ BDC account for a BDC in the domain. In the first role, it exists for
+ the duration of the secure channel (and this routine is called when the
+ client requests a challenge). In the second role, it exists as
+ long as the machine account exists (and this routine is called during
+ netlogon startup for each BDC account).
+
+ If an entry matching this ComputerName already exists
+ in the ServerSession Table, that entry will be overwritten.
+
+Arguments:
+
+ ComputerName - The name of the computer on the client side of the
+ secure channel.
+
+ Flags - Specifies the initial SsFlags to associate with the entry.
+ If the SS_BDC bit is set, the structure is considered to represent
+ a BDC account in the SAM database.
+
+ AccountRid - If this is a BDC session, this specifies the RID of the
+ server account.
+
+ AuthenticationSeed - Specifies the Initial Authentication Seed
+ to associate with the entry. Specified only if this call is
+ being made as result of a challenge request
+ (e.g. NetrServerRequestChallenge)
+
+ AuthenticationResponse - Specifies the Initial Authentication Response from
+ the remote system to associate with the entry. Specified only if
+ this call is being made as result of a challenge request
+ (e.g. NetrServerRequestChallenge)
+
+Return Value:
+
+ NT STATUS code.
+
+--*/
+{
+ NTSTATUS Status;
+ PSERVER_SESSION ServerSession;
+
+ LOCK_SERVER_SESSION_TABLE();
+
+ //
+ // If the is no current Server Session table entry,
+ // allocate one.
+ //
+
+ ServerSession = NlFindNamedServerSession(ComputerName);
+ if (ServerSession == NULL) {
+ DWORD Index;
+ ULONG ComputerNameSize;
+
+ //
+ // Allocate the ServerSession Entry
+ //
+
+ ServerSession = NetpMemoryAllocate( sizeof(SERVER_SESSION) );
+
+ if (ServerSession == NULL) {
+ UNLOCK_SERVER_SESSION_TABLE();
+ return STATUS_NO_MEMORY;
+ }
+
+ RtlZeroMemory( ServerSession, sizeof(SERVER_SESSION) );
+
+
+ //
+ // Fill in the fields of the ServerSession entry.
+ //
+
+ ServerSession->SsSecureChannelType = NullSecureChannel;
+ ServerSession->SsSync = NULL;
+ InitializeListHead( &ServerSession->SsBdcList );
+ InitializeListHead( &ServerSession->SsPendingBdcList );
+
+ //
+ // Convert the computername to uppercase OEM for easier comparison.
+ //
+
+ Status = RtlUpcaseUnicodeToOemN(
+ ServerSession->SsComputerName,
+ sizeof(ServerSession->SsComputerName)-1,
+ &ComputerNameSize,
+ ComputerName,
+ wcslen(ComputerName)*sizeof(WCHAR) );
+
+ if ( !NT_SUCCESS(Status) ) {
+ NetpMemoryFree( ServerSession );
+ UNLOCK_SERVER_SESSION_TABLE();
+ return Status;
+ }
+
+ ServerSession->SsComputerName[ComputerNameSize] = '\0';
+
+
+ //
+ // Link the allocated entry into the head of hash table.
+ //
+ // The theory is we lookup new entries more frequently than older
+ // entries.
+ //
+
+ Index = NlGetHashVal( ServerSession->SsComputerName, SERVER_SESSION_HASH_TABLE_SIZE );
+
+ InsertHeadList( &NlGlobalServerSessionHashTable[Index],
+ &ServerSession->SsHashList );
+
+ //
+ // Link this entry onto the tail of the Sequential ServerSessionTable.
+ //
+
+ InsertTailList( &NlGlobalServerSessionTable, &ServerSession->SsSeqList );
+
+
+
+ //
+ // Beware of server with two concurrent calls outstanding
+ // (must have rebooted.)
+ //
+
+ } else {
+
+ if (ServerSession->SsFlags & SS_LOCKED ) {
+ UNLOCK_SERVER_SESSION_TABLE();
+
+ NlPrint((NL_CRITICAL,
+ "NlInsertServerSession: Concurrent call detected.\n" ));
+
+ return STATUS_ACCESS_DENIED;
+ }
+ }
+
+
+ //
+ // Initialize BDC specific fields.
+ //
+
+ if ( Flags & SS_BDC ) {
+
+ //
+ // If we've already have an account for this BDC,
+ // Warn that there are multiple accounts.
+ //
+
+ if ( ServerSession->SsFlags & SS_BDC ) {
+ LPWSTR MsgStrings[1];
+
+ NlPrint((NL_CRITICAL,
+ "NlInsertServerSession: %s: has multiple machine accounts.\n",
+ ServerSession->SsComputerName ));
+ MsgStrings[0] = ComputerName;
+
+ NlpWriteEventlog(
+ NELOG_NetlogonDuplicateMachineAccounts,
+ EVENTLOG_ERROR_TYPE,
+ NULL,
+ 0,
+ MsgStrings,
+ 1 );
+
+ } else {
+ //
+ // Insert this entry at the front of the list of BDCs
+ //
+
+ InsertHeadList( &NlGlobalBdcServerSessionList,
+ &ServerSession->SsBdcList );
+ NlGlobalBdcServerSessionCount ++;
+ }
+
+ if ( Flags & SS_LM_BDC ) {
+ NlAssert( ServerSession->SsLmBdcAccountRid == 0 );
+ ServerSession->SsLmBdcAccountRid = AccountRid;
+ } else {
+ NlAssert( ServerSession->SsNtBdcAccountRid == 0 );
+ ServerSession->SsNtBdcAccountRid = AccountRid;
+ }
+
+
+ }
+
+ //
+ // Update the Server Session entry to reflect this new secure channel setup
+ //
+
+ ServerSession->SsCheck = 0;
+ ServerSession->SsSecureChannelType = NullSecureChannel;
+ ServerSession->SsNegotiatedFlags = 0;
+ ServerSession->SsTransportName = NULL;
+ ServerSession->SsFlags = ((USHORT) Flags) |
+ (ServerSession->SsFlags & SS_PERMANENT_FLAGS);
+
+ if ( AuthenticationSeed != NULL ) {
+ ServerSession->SsAuthenticationSeed = *AuthenticationSeed;
+ }
+
+ if ( AuthenticationResponse != NULL ) {
+ NlAssert( sizeof(*AuthenticationResponse) <= sizeof(ServerSession->SsSessionKey ));
+ RtlCopyMemory( &ServerSession->SsSessionKey,
+ AuthenticationResponse,
+ sizeof( *AuthenticationResponse ) );
+ }
+
+ UNLOCK_SERVER_SESSION_TABLE();
+ return STATUS_SUCCESS;
+}
+
+
+
+VOID
+NlFreeServerSession(
+ IN PSERVER_SESSION ServerSession
+ )
+/*++
+
+Routine Description:
+
+ Free the specified Server Session table entry.
+
+ This routine is called with the Server Session table locked.
+
+Arguments:
+
+ ServerSession - Specifies a pointer to the server session entry
+ to delete.
+
+Return Value:
+
+--*/
+{
+
+
+ //
+ // If someone has an outstanding pointer to this entry,
+ // delay the deletion for now.
+ //
+
+ if ( ServerSession->SsFlags & SS_LOCKED ) {
+ ServerSession->SsFlags |= SS_DELETE_ON_UNLOCK;
+ NlPrint((NL_SERVER_SESS,
+ "NlFreeServerSession: %s: Tried to free locked server session\n",
+ ServerSession->SsComputerName ));
+ return;
+ }
+
+ //
+ // If this entry represents a BDC account,
+ // don't delete the entry until the account is deleted.
+ //
+
+ if ( ServerSession->SsLmBdcAccountRid != 0 ||
+ ServerSession->SsNtBdcAccountRid != 0 ) {
+ NlPrint((NL_SERVER_SESS,
+ "NlFreeServerSession: %s: Didn't delete server session with BDC account.\n",
+ ServerSession->SsComputerName ));
+ return;
+ }
+
+ NlPrint((NL_SERVER_SESS,
+ "NlFreeServerSession: %s: Freed server session\n",
+ ServerSession->SsComputerName ));
+
+ //
+ // Delink the entry from the hash list.
+ //
+
+ RemoveEntryList( &ServerSession->SsHashList );
+
+ //
+ // Delink the entry from the sequential list.
+ //
+
+ RemoveEntryList( &ServerSession->SsSeqList );
+
+
+ //
+ // Handle special cleanup for the BDC_SERVER_SESSION
+ //
+
+ if ( ServerSession->SsFlags & SS_BDC ) {
+
+ //
+ // Remove the entry from the list of BDCs
+ //
+
+ RemoveEntryList( &ServerSession->SsBdcList );
+ NlGlobalBdcServerSessionCount --;
+
+ //
+ // Remove the entry from the list of pending BDCs
+ //
+
+ if ( ServerSession->SsFlags & SS_PENDING_BDC ) {
+ NlRemovePendingBdc( ServerSession );
+ }
+
+
+ //
+ // Clean up an sync context for this entry.
+ //
+
+ if ( ServerSession->SsSync != NULL ) {
+ CLEAN_SYNC_CONTEXT( ServerSession->SsSync );
+ NetpMemoryFree( ServerSession->SsSync );
+ }
+
+ }
+
+ //
+ // Delete the entry
+ //
+
+ NetpMemoryFree( ServerSession );
+
+}
+
+
+VOID
+NlUnlockServerSession(
+ IN PSERVER_SESSION ServerSession
+ )
+/*++
+
+Routine Description:
+
+ Unlock the specified Server Session table entry.
+
+Arguments:
+
+ ServerSession - Specifies a pointer to the server session entry to unlock.
+
+Return Value:
+
+--*/
+{
+
+ LOCK_SERVER_SESSION_TABLE();
+
+ //
+ // Unlock the entry.
+ //
+
+ NlAssert( ServerSession->SsFlags & SS_LOCKED );
+ ServerSession->SsFlags &= ~SS_LOCKED;
+
+ //
+ // If someone wanted to delete the entry while we had it locked,
+ // finish the deletion.
+ //
+
+ if ( ServerSession->SsFlags & SS_DELETE_ON_UNLOCK ) {
+ NlFreeServerSession( ServerSession );
+
+ //
+ // Indicate activity from the BDC
+ //
+
+ } else if (ServerSession->SsFlags & SS_PENDING_BDC) {
+ (VOID) NtQuerySystemTime( &ServerSession->SsLastPulseTime );
+ }
+
+ UNLOCK_SERVER_SESSION_TABLE();
+
+}
+
+
+
+VOID
+NlFreeLmBdcServerSession(
+ IN ULONG ServerRid
+ )
+/*++
+
+Routine Description:
+
+ Delete the specified Server Account from the Server Session list.
+
+Arguments:
+
+ ServerRid - Rid of server to add to list.
+
+Return Value:
+
+ None
+
+--*/
+{
+ PSERVER_SESSION ServerSession;
+ PLIST_ENTRY ListEntry;
+
+ LOCK_SERVER_SESSION_TABLE();
+
+ //
+ // Ensure the ServerSession Table is initialized.
+ //
+
+ if (NlGlobalServerSessionHashTable == NULL) {
+ return;
+ }
+
+ //
+ // Loop through the BDC list trying the find the right entry.
+ //
+
+ for ( ListEntry = NlGlobalBdcServerSessionList.Flink ;
+ ListEntry != &NlGlobalBdcServerSessionList ;
+ ListEntry = ListEntry->Flink) {
+
+
+ ServerSession = CONTAINING_RECORD( ListEntry, SERVER_SESSION, SsBdcList );
+
+ if ( ServerRid == ServerSession->SsLmBdcAccountRid ) {
+ break;
+ }
+
+ }
+
+ if ( ListEntry == &NlGlobalBdcServerSessionList ) {
+ UNLOCK_SERVER_SESSION_TABLE();
+ NlPrint((NL_CRITICAL,
+ "NlFreeLmBdcServerSession: %lx: Couldn't find"
+ " server session entry for this RID.\n",
+ ServerRid ));
+ return;
+ }
+
+ //
+ // Clear the Account Rid so the ServerSession entry will be deleted
+ //
+
+ ServerSession->SsLmBdcAccountRid = 0;
+
+ //
+ // Actually delete the entry.
+ //
+
+ NlFreeServerSession( ServerSession );
+
+ UNLOCK_SERVER_SESSION_TABLE();
+
+}
+
+
+
+VOID
+NlFreeNamedServerSession(
+ IN LPWSTR ComputerName,
+ IN BOOLEAN AccountBeingDeleted
+ )
+/*++
+
+Routine Description:
+
+ Frees the specified entry in the ServerSession Table.
+
+Arguments:
+
+ ComputerName - The name of the computer on the client side of the
+ secure channel.
+
+ AccountBeingDeleted - True to indicate that the account for this server
+ session is being deleted.
+
+Return Value:
+
+ An NT status code.
+
+--*/
+{
+ PSERVER_SESSION ServerSession;
+
+ LOCK_SERVER_SESSION_TABLE();
+
+ //
+ // Find the entry to delete.
+ //
+
+ ServerSession = NlFindNamedServerSession( ComputerName );
+
+ if ( ServerSession == NULL ) {
+ UNLOCK_SERVER_SESSION_TABLE();
+ return;
+ }
+
+ //
+ // If the account is being deleted,
+ // clear the account RID to allow the session structure to be deleted.
+ //
+ // (We might be deleting an workstation or trusted domain account here
+ // but that doesn't make any difference. In those cases, the account rid
+ // is already zero.)
+ //
+
+ if ( AccountBeingDeleted ) {
+ ServerSession->SsNtBdcAccountRid = 0;
+ }
+
+ //
+ // Actually delete the entry.
+ //
+
+ NlFreeServerSession( ServerSession );
+
+ UNLOCK_SERVER_SESSION_TABLE();
+
+}
+
+
+
+VOID
+NlFreeServerSessionForAccount(
+ IN PUNICODE_STRING AccountName
+ )
+/*++
+
+Routine Description:
+
+ Frees the specified entry in the ServerSession Table.
+
+Arguments:
+
+ AccountName - The name of the Account describing trust relationship being
+ deleted.
+
+Return Value:
+
+ None
+
+--*/
+{
+ WCHAR ComputerName[CNLEN+2]; // Extra for $ and \0
+
+ //
+ // Convert account name to a computer name by stripping the trailing
+ // postfix.
+ //
+
+ if ( AccountName->Length + sizeof(WCHAR) > sizeof(ComputerName) ||
+ AccountName->Length < SSI_ACCOUNT_NAME_POSTFIX_LENGTH * sizeof(WCHAR)){
+ return;
+ }
+
+ RtlCopyMemory( ComputerName, AccountName->Buffer, AccountName->Length );
+ ComputerName[ AccountName->Length / sizeof(WCHAR) -
+ SSI_ACCOUNT_NAME_POSTFIX_LENGTH ] = L'\0';
+
+ //
+ // Free the named server session (if any)
+ //
+
+ NlFreeNamedServerSession( ComputerName, TRUE );
+
+}
+
+
+
+VOID
+NlServerSessionScavenger(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ Scavenge the ServerSession Table.
+
+ For now, just clean up the SyncContext if a client doesn't use it
+ for a while.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PLIST_ENTRY ListEntry;
+
+ //
+ // Find the next table entry that needs to be scavenged
+ //
+
+ LOCK_SERVER_SESSION_TABLE();
+
+ for ( ListEntry = NlGlobalServerSessionTable.Flink ;
+ ListEntry != &NlGlobalServerSessionTable ;
+ ) {
+
+ PSERVER_SESSION ServerSession;
+
+ ServerSession =
+ CONTAINING_RECORD(ListEntry, SERVER_SESSION, SsSeqList);
+
+
+ //
+ // Grab a pointer to the next entry before deleting this one
+ //
+
+ ListEntry = ListEntry->Flink;
+
+ //
+ // Increment the number of times this entry has been checked.
+ //
+
+ ServerSession->SsCheck ++;
+
+
+ //
+ // If this entry in the Server Session table has been around for many
+ // days without the client calling,
+ // free it.
+ //
+ // We wait several days before deleting an old entry. If an entry is
+ // deleted, the client has to rediscover us which may cause a lot of
+ // net traffic. After several days, that additional traffic isn't
+ // significant.
+ //
+
+ if (ServerSession->SsCheck > KILL_SESSION_TIME ) {
+
+ NlPrint((NL_SERVER_SESS,
+ "NlServerSessionScavenger: %s: Free Server Session.\n",
+ ServerSession->SsComputerName ));
+
+ NlFreeServerSession( ServerSession );
+
+
+ //
+ // If this entry in the Server Session table has timed out,
+ // Clean up the SAM resources.
+ //
+
+ } else if (ServerSession->SsCheck > MAX_WOC_INTERROGATE) {
+
+ //
+ // Clean up the SYNC context for this session freeing up
+ // the SAM resources.
+ //
+ // We shouldn't timeout if the ServerSession Entry is locked,
+ // but we'll be careful anyway.
+ //
+
+ if ( (ServerSession->SsFlags & SS_LOCKED) == 0 &&
+ ServerSession->SsFlags & SS_BDC ) {
+
+ if ( ServerSession->SsSync != NULL ) {
+
+ NlPrint((NL_SERVER_SESS,
+ "NlServerSessionScavenger: %s: Cleanup Sync context.\n",
+ ServerSession->SsComputerName ));
+
+ CLEAN_SYNC_CONTEXT( ServerSession->SsSync );
+ NetpMemoryFree( ServerSession->SsSync );
+ ServerSession->SsSync = NULL;
+ }
+ }
+
+
+ }
+
+ } // end while
+
+ UNLOCK_SERVER_SESSION_TABLE();
+
+}
diff --git a/private/net/svcdlls/logonsrv/server/ssiapi.c b/private/net/svcdlls/logonsrv/server/ssiapi.c
new file mode 100644
index 000000000..f3f5e33fa
--- /dev/null
+++ b/private/net/svcdlls/logonsrv/server/ssiapi.c
@@ -0,0 +1,6401 @@
+/*++
+
+
+Copyright (c) 1987-1992 Microsoft Corporation
+
+Module Name:
+
+ ssiapi.c
+
+Abstract:
+
+ Authentication and replication API routines (server side).
+
+Author:
+
+ Cliff Van Dyke (cliffv) 28-Jun-1991
+
+Environment:
+
+ User mode only.
+ Contains NT-specific code.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+ 02-Jan-1992 (madana)
+ added support for builtin/multidomain replication.
+--*/
+
+//
+// Common include files.
+//
+
+#include <logonsrv.h> // Include files common to entire service
+
+//
+// Include files specific to this .c file
+//
+
+
+#include <lmerr.h>
+#include <replutil.h> // PackSamXXX()
+#include <lsarepl.h> // PackLsa ..
+#include <nlsecure.h> // Security information
+#include <nlrepl.h> // I_NetGetAnyDc
+#include <ntlsa.h> // LsaOpenPolicy, etc
+#include <secobj.h> // NetpAccessCheck
+#include <ssiapi.h>
+#include <tstring.h> // IS_PATH_SEPARATOR ...
+
+#include <lsarpc.h>
+#include <lsaisrv.h>
+#include <loghours.h>
+
+//
+// Define the maximum number of deltas returned on any single call
+//
+// Theoretically, MaxNumDeltas should be some function of
+// PreferredMaximumLength. However, by the time you allow for
+// the large swing in PreferredMaximumLength allowed by the BDC replication
+// Governor and then not wanting this buffer to be ridiculously large
+// when the full 128K is asked for, we find that 1000 entries is always
+// a reasonable compromise.
+//
+
+#define MAX_DELTA_COUNT 1000
+
+//
+// Maximum number of deltas that can be generated by a single change log entry.
+//
+#define MAX_DELTAS_PER_CHANGELOG 4
+
+
+NTSTATUS
+NlVerifyWorkstation(
+ IN LPWSTR ServerName OPTIONAL
+)
+
+/*++
+
+Routine Description:
+
+ Check the validity of the ServerName.
+
+Arguments:
+
+ ServerName - Name of the server this code is executing on.
+
+Return Value:
+
+ The status of the operation
+
+--*/
+{
+
+ //
+ // Check the Servername to ensure he wants to talk to us.
+ //
+
+ if ( ServerName != NULL ) {
+
+ if ( IS_PATH_SEPARATOR(ServerName[0]) &&
+ IS_PATH_SEPARATOR(ServerName[1])) {
+ ServerName += 2;
+ }
+
+ if ( NlNameCompare( ServerName,
+ NlGlobalUnicodeComputerName,
+ NAMETYPE_COMPUTER ) != 0 ) {
+
+ return STATUS_INVALID_COMPUTER_NAME;
+ }
+ }
+
+ return STATUS_SUCCESS;
+}
+
+
+NTSTATUS
+NetrServerReqChallenge(
+ IN LPWSTR PrimaryName OPTIONAL,
+ IN LPWSTR ComputerName,
+ IN PNETLOGON_CREDENTIAL ClientChallenge,
+ OUT PNETLOGON_CREDENTIAL ServerChallenge
+ )
+/*++
+
+Routine Description:
+
+ This is the server side of I_NetServerReqChallenge.
+
+ I_NetServerReqChallenge is the first of two functions used by a client
+ Netlogon service to authenticate with another Netlogon service.
+ (See I_NetServerAuthenticate below.)
+
+ This function passes a challenge to the DC and the DC passes a challenge
+ back to the caller.
+
+Arguments:
+
+ PrimaryName -- Supplies the name of the DC we wish to authenticate with.
+
+ ComputerName -- Name of the machine making the call.
+
+ ClientCredential -- 64 bit challenge supplied by the BDC or member server.
+
+ ServerCredential -- Receives 64 bit challenge from the PDC.
+
+Return Value:
+
+ The status of the operation.
+
+--*/
+
+{
+ NTSTATUS Status;
+
+ //
+ // This API is not supported on workstations.
+ //
+
+ if ( NlGlobalRole == RoleMemberWorkstation ) {
+ return STATUS_NOT_SUPPORTED;
+ }
+
+ //
+ // Check the primary name.
+ //
+
+ Status = NlVerifyWorkstation( PrimaryName );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto Cleanup;
+ }
+
+
+#ifdef BAD_ALIGNMENT
+ NlPrint((NL_CHALLENGE_RES,
+ "NetrServerReqChallenge: ClientChallenge = %lx %lx\n",
+ ((DWORD *) (ClientChallenge))[0],
+ ((DWORD *) (ClientChallenge))[1]));
+#endif // BAD_ALIGNMENT
+
+
+ //
+ // Compute ServerChallenge to pass back to requestor
+ //
+
+ NlComputeChallenge(ServerChallenge);
+
+
+#ifdef BAD_ALIGNMENT
+ NlPrint((NL_CHALLENGE_RES,
+ "NetrServerReqChallenge: ServerChallenge = %lx %lx\n",
+ ((DWORD *) (ServerChallenge))[0],
+ ((DWORD *) (ServerChallenge))[1]));
+#endif // BAD_ALIGNMENT
+
+
+ //
+ // Add this entry into the server session table.
+ //
+ // Remember both challenges until the corresponding I_NetAuthenticate call.
+ // Notice that both challenges are not yet SessionKey-encrypted
+ //
+
+ Status = NlInsertServerSession(
+ ComputerName,
+ SS_CHALLENGE, // challenge in progress
+ 0, // No Account rid
+ ClientChallenge,
+ ServerChallenge );
+
+ //
+ // Common exit point
+ //
+
+Cleanup:
+
+ //
+ // If the request failed, be carefull to not leak authentication
+ // information.
+ //
+
+ if ( !NT_SUCCESS(Status) ) {
+ RtlZeroMemory( ServerChallenge, sizeof(*ServerChallenge) );
+ }
+
+ return Status;
+}
+
+
+NTSTATUS
+NetrServerAuthenticate2(
+ IN LPWSTR PrimaryName OPTIONAL,
+ IN LPWSTR AccountName,
+ IN NETLOGON_SECURE_CHANNEL_TYPE SecureChannelType,
+ IN LPWSTR ComputerName,
+ IN PNETLOGON_CREDENTIAL ClientCredential,
+ OUT PNETLOGON_CREDENTIAL ServerCredential,
+ IN OUT PULONG NegotiatedFlags
+ )
+/*++
+
+Routine Description:
+
+ This is the server side of I_NetServerAuthenticate
+
+ I_NetServerAuthenticate is the second of two functions used by a client
+ Netlogon service to authenticate with another Netlogon service.
+ (See I_NetServerReqChallenge above.) Both a SAM or UAS server authenticates
+ using this function.
+
+ This function passes a credential to the DC and the DC passes a credential
+ back to the caller.
+
+
+Arguments:
+
+ PrimaryName -- Supplies the name of the DC we wish to authenticate with.
+
+ AccountName -- Name of the Account to authenticate with.
+
+ SecureChannelType -- The type of the account being accessed. This field must
+ be set to UasServerSecureChannel to indicate a call from downlevel (LanMan
+ 2.x and below) BDC or member server.
+
+ ComputerName -- Name of the BDC or member server making the call.
+
+ ClientCredential -- 64 bit credential supplied by the BDC or member server.
+
+ ServerCredential -- Receives 64 bit credential from the PDC.
+
+ NegotiatedFlags -- Specifies flags indicating what features the BDC supports.
+ Returns a subset of those flags indicating what features the PDC supports.
+ The PDC/BDC should ignore any bits that it doesn't understand.
+
+Return Value:
+
+ The status of the operation.
+
+--*/
+
+{
+ NTSTATUS Status;
+
+ NlPrint((NL_SESSION_SETUP,
+ "NetrServerAuthenticate entered: " FORMAT_LPWSTR " on account "
+ FORMAT_LPWSTR " (Negot: %lx)\n",
+ ComputerName, AccountName, *NegotiatedFlags ));
+
+ //
+ // This API is not supported on workstations.
+ //
+
+ if ( NlGlobalRole == RoleMemberWorkstation ) {
+ return STATUS_NOT_SUPPORTED;
+ }
+
+ //
+ // If CompatibilityMode is off,
+ // disallow this function for downlevel servers.
+ //
+
+ if ( SecureChannelType == UasServerSecureChannel && !NlGlobalUasCompatibilityMode ) {
+
+ NlPrint((NL_CRITICAL,"NetrServerAuthenticate "
+ "from LM 2.x and not compatibility mode\n"));
+
+ Status = STATUS_ACCESS_DENIED;
+ goto Cleanup;
+ }
+
+
+ //
+ // Check the primary name.
+ //
+
+ Status = NlVerifyWorkstation( PrimaryName );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ NlPrint((NL_CRITICAL,"NetrServerAuthenticate: "
+ "Error from NlVerifyWorkstation 0x%lx\n", Status));
+
+ goto Cleanup;
+ }
+
+ //
+ // Ensure that this machine account is valid.
+ // (Do everything but check the password.)
+ //
+
+ Status = NlCheckMachineAccount( AccountName, SecureChannelType );
+
+ if (!NT_SUCCESS( Status )) {
+
+ NlPrint((NL_CRITICAL,
+ "NetrServerAuthenticate: No machine account: "
+ FORMAT_LPWSTR " on account " FORMAT_LPWSTR "\n",
+ ComputerName, AccountName ));
+
+ //
+ // return more appropriate error
+ //
+
+ if ( SecureChannelType != UasServerSecureChannel &&
+ Status == STATUS_NO_SUCH_USER ) {
+
+ Status = STATUS_NO_TRUST_SAM_ACCOUNT;
+ }
+
+ goto Cleanup;
+ }
+
+ //
+ // Compute the NegotiatedFlags both sides support
+ //
+
+ *NegotiatedFlags &= NETLOGON_SUPPORTS_MASK;
+
+
+ //
+ // Authenticate the caller.
+ //
+
+ Status = NlAuthenticate( AccountName,
+ SecureChannelType,
+ ComputerName,
+ ClientCredential,
+ ServerCredential,
+ *NegotiatedFlags );
+
+ if ( !NT_SUCCESS(Status) ) {
+ NlPrint((NL_CRITICAL,
+ "NetrServerAuthenticate: Bad password: " FORMAT_LPWSTR
+ " on account " FORMAT_LPWSTR "\n",
+ ComputerName, AccountName ));
+
+ //
+ // return more appropriate error
+ //
+
+ if ( SecureChannelType != UasServerSecureChannel &&
+ Status == STATUS_NO_SUCH_USER ) {
+
+ Status = STATUS_NO_TRUST_SAM_ACCOUNT;
+ }
+
+ goto Cleanup;
+ }
+
+ Status = STATUS_SUCCESS;
+
+ NlPrint((NL_SESSION_SETUP,
+ "NetrServerAuthenticate returns Success: " FORMAT_LPWSTR
+ " on account " FORMAT_LPWSTR " (Negot: %lx)\n",
+ ComputerName, AccountName, *NegotiatedFlags ));
+
+ //
+ // Common exit point
+ //
+
+Cleanup:
+
+ //
+ // If the request failed, be carefull to not leak authentication
+ // information.
+ //
+
+ if ( !NT_SUCCESS(Status) ) {
+ RtlZeroMemory( ServerCredential, sizeof(*ServerCredential) );
+ }
+
+ //
+ // write event log
+ //
+
+ if ( !NT_SUCCESS( Status ) ) {
+
+ LPWSTR MsgStrings[3];
+
+ MsgStrings[0] = ComputerName;
+ MsgStrings[1] = AccountName;
+
+ if (Status == STATUS_NO_TRUST_SAM_ACCOUNT) {
+
+ NlpWriteEventlog( NELOG_NetlogonServerAuthNoTrustSamAccount,
+ EVENTLOG_ERROR_TYPE,
+ (LPBYTE) & Status,
+ sizeof(Status),
+ MsgStrings,
+ 2 );
+
+ } else {
+
+ MsgStrings[2] = (LPWSTR) Status;
+
+ NlpWriteEventlog( NELOG_NetlogonServerAuthFailed,
+ EVENTLOG_ERROR_TYPE,
+ (LPBYTE) & Status,
+ sizeof(Status),
+ MsgStrings,
+ 3 | LAST_MESSAGE_IS_NTSTATUS );
+ }
+ }
+
+ return Status;
+}
+
+
+NTSTATUS
+NetrServerAuthenticate(
+ IN LPWSTR PrimaryName OPTIONAL,
+ IN LPWSTR AccountName,
+ IN NETLOGON_SECURE_CHANNEL_TYPE SecureChannelType,
+ IN LPWSTR ComputerName,
+ IN PNETLOGON_CREDENTIAL ClientCredential,
+ OUT PNETLOGON_CREDENTIAL ServerCredential
+ )
+/*++
+
+Routine Description:
+
+
+ This is the NT 1.0 version of I_NetServerAuthenicate2.
+ I_NetServerAuthenticate2 was introduced in NT 3.5 (December 1993).
+
+Arguments:
+
+Return Value:
+
+ The status of the operation.
+
+--*/
+
+{
+ ULONG NegotiatedFlags = 0;
+
+ return NetrServerAuthenticate2( PrimaryName,
+ AccountName,
+ SecureChannelType,
+ ComputerName,
+ ClientCredential,
+ ServerCredential,
+ &NegotiatedFlags );
+
+}
+
+
+NTSTATUS
+NetrServerPasswordSet(
+ IN LPWSTR PrimaryName OPTIONAL,
+ IN LPWSTR AccountName,
+ IN NETLOGON_SECURE_CHANNEL_TYPE AccountType,
+ IN LPWSTR ComputerName,
+ IN PNETLOGON_AUTHENTICATOR Authenticator,
+ OUT PNETLOGON_AUTHENTICATOR ReturnAuthenticator,
+ IN PENCRYPTED_LM_OWF_PASSWORD UasNewPassword
+ )
+/*++
+
+Routine Description:
+
+ This function is used to change the password for the account being
+ used to maintain a secure channel. This function can only be called
+ by a server which has previously authenticated with a DC by calling
+ I_NetServerAuthenticate.
+
+ The call is made differently depending on the account type:
+
+ * A domain account password is changed from the PDC in the
+ trusting domain. The I_NetServerPasswordSet call is made to any
+ DC in the trusted domain.
+
+ * A server account password is changed from the specific server.
+ The I_NetServerPasswordSet call is made to the PDC in the domain
+ the server belongs to.
+
+ * A workstation account password is changed from the specific
+ workstation. The I_NetServerPasswordSet call is made to a DC in
+ the domain the server belongs to.
+
+ For domain accounts and workstation accounts, the server being called
+ may be a BDC in the specific domain. In that case, the BDC will
+ validate the request and pass it on to the PDC of the domain using
+ the server account secure channel. If the PDC of the domain is
+ currently not available, the BDC will return STATUS_NO_LOGON_SERVERS. Since
+ the UasNewPassword is passed encrypted by the session key, such a BDC
+ will decrypt the UasNewPassword using the original session key and
+ will re-encrypt it with the session key for its session to its PDC
+ before passing the request on.
+
+ This function uses RPC to contact the DC named by PrimaryName.
+
+Arguments:
+
+ PrimaryName -- Name of the PDC to change the servers password
+ with. NULL indicates this call is a local call being made on
+ behalf of a UAS server by the XACT server.
+
+ AccountName -- Name of the account to change the password for.
+
+ AccountType -- The type of account being accessed. This field must
+ be set to UasServerAccount to indicate a call from a downlevel
+
+ ComputerName -- Name of the BDC or member making the call.
+
+ Authenticator -- supplied by the server.
+
+ ReturnAuthenticator -- Receives an authenticator returned by the PDC.
+
+ UasNewPassword -- The new password for the server. This
+ Password is generated by automatic means using
+ random number genertaor seeded with the current Time
+ It is assumed that the machine generated password
+ was used as key to encrypt STD text and "sesskey"
+ obtained via Challenge/Authenticate sequence was
+ used to further encrypt it before passing to this api.
+ i.e. UasNewPassword = E2(E1(STD_TXT, PW), SK)
+
+Return Value:
+
+ NT status code.
+
+ STATUS_WRONG_PASSWORD - Indicates the server refuses to allow the password
+ to be changed. The client should continue to use the prior password.
+
+--*/
+{
+ NTSTATUS Status;
+ PSERVER_SESSION ServerSession;
+ LM_OWF_PASSWORD OwfPassword;
+ SAMPR_HANDLE UserHandle;
+
+ NlPrint((NL_SESSION_SETUP,
+ "NetrServerPasswordSet: Comp=" FORMAT_LPWSTR
+ " Acc=" FORMAT_LPWSTR " Entered\n",
+ ComputerName,
+ AccountName ));
+
+ //
+ // This API is not supported on workstations.
+ //
+
+ if ( NlGlobalRole == RoleMemberWorkstation ) {
+ return STATUS_NOT_SUPPORTED;
+ }
+
+ //
+ // Check the primary name.
+ //
+
+ Status = NlVerifyWorkstation( PrimaryName );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto Cleanup;
+ }
+
+
+ //
+ // Get the Session key for this session.
+ //
+
+ LOCK_SERVER_SESSION_TABLE();
+ ServerSession = NlFindNamedServerSession( ComputerName );
+
+ if (ServerSession == NULL) {
+ UNLOCK_SERVER_SESSION_TABLE();
+ Status = STATUS_ACCESS_DENIED;
+ goto Cleanup;
+ }
+
+ //
+ // decrypt the sessionkey from password
+ // i.e. OwfPassword = D2((E2(E1(STD_TXT, PW), SK)), SK)
+ // = E1(STD_TXT, PW)
+ // OwfPassword = One Way Function of the cleartext password.
+ //
+
+ if (Status = RtlDecryptLmOwfPwdWithLmOwfPwd(
+ UasNewPassword,
+ (PLM_OWF_PASSWORD) &ServerSession->SsSessionKey,
+ &OwfPassword )) {
+
+ UNLOCK_SERVER_SESSION_TABLE();
+ goto Cleanup;
+ }
+
+
+ //
+ // now verify the Authenticator and update seed if OK
+ //
+
+ Status = NlCheckAuthenticator( ServerSession,
+ Authenticator,
+ ReturnAuthenticator);
+
+ if ( !NT_SUCCESS(Status) ) {
+ UNLOCK_SERVER_SESSION_TABLE();
+ goto Cleanup;
+ }
+
+ //
+ // Check if we're refusing password changes
+ //
+ // Only refuse password changes if the client is a workstation and the
+ // client supports password changing.
+ //
+ // If this is a PDC and the request was passed-through a BDC,
+ // we don't have access to the NETLOGON_SUPPORTS flag of the workstation.
+ // As such, we don't return STATUS_WRONG_PASSWORD here. Rather, the
+ // solution is to set 'RefusePasswordChange' on all of the BDCs so they'll
+ // catch the password change attempt before passing through.
+ //
+
+ if ( NlGlobalRefusePasswordChangeParameter &&
+ ServerSession->SsSecureChannelType == WorkstationSecureChannel &&
+ (ServerSession->SsNegotiatedFlags & NETLOGON_SUPPORTS_REFUSE_CHANGE_PWD) != 0 ){
+
+ Status = STATUS_WRONG_PASSWORD;
+ UNLOCK_SERVER_SESSION_TABLE();
+ goto Cleanup;
+ }
+
+ UNLOCK_SERVER_SESSION_TABLE();
+
+ //
+ // If this machine is a BDC, just pass the request on to the PDC.
+ //
+
+ if ( NlGlobalRole == RoleBackup ) {
+ ENCRYPTED_LM_OWF_PASSWORD SessKeyEncrPassword;
+ NETLOGON_AUTHENTICATOR OurAuthenticator;
+ NETLOGON_AUTHENTICATOR OurReturnAuthenticator;
+
+ //
+ // Become a Writer of the ClientSession.
+ //
+
+ if ( !NlTimeoutSetWriterClientSession( NlGlobalClientSession, WRITER_WAIT_PERIOD ) ) {
+ NlPrint((NL_CRITICAL, "NetrServerPasswordSet: Can't become writer of client session.\n" ));
+ Status = STATUS_NO_LOGON_SERVERS;
+ goto Cleanup;
+ }
+
+ if ( NlGlobalClientSession->CsState != CS_AUTHENTICATED ) {
+ NlResetWriterClientSession( NlGlobalClientSession );
+ Status = NlGlobalClientSession->CsConnectionStatus;
+ goto Cleanup;
+ }
+
+ //
+ // Encrypt the password again with the session key.
+ // The PDC will decrypt it on the other side.
+ //
+
+ Status = RtlEncryptNtOwfPwdWithNtOwfPwd(
+ &OwfPassword,
+ (PNT_OWF_PASSWORD) &NlGlobalClientSession->CsSessionKey,
+ &SessKeyEncrPassword) ;
+
+ if ( !NT_SUCCESS( Status )) {
+ NlPrint((NL_CRITICAL,
+ "NetrServerPasswordSet: "
+ "Cannot RtlEncryptNtOwfPwdWithNtOwfPed %lX\n",
+ Status));
+ NlResetWriterClientSession( NlGlobalClientSession );
+ // Status = STATUS_NO_LOGON_SERVERS;
+ goto Cleanup;
+ }
+
+
+ //
+ // Build the Authenticator for this request to the PDC.
+ //
+
+ NlBuildAuthenticator(
+ &NlGlobalClientSession->CsAuthenticationSeed,
+ &NlGlobalClientSession->CsSessionKey,
+ &OurAuthenticator);
+
+
+ //
+ // Change the password on the machine our connection is to.
+ //
+
+ Status = NlStartApiClientSession( NlGlobalClientSession, TRUE );
+
+ if ( NT_SUCCESS(Status) ) {
+ Status = I_NetServerPasswordSet( NlGlobalClientSession->CsUncServerName,
+ AccountName,
+ AccountType,
+ NlGlobalUnicodeComputerName,
+ &OurAuthenticator,
+ &OurReturnAuthenticator,
+ &SessKeyEncrPassword);
+ }
+
+ // NOTE: This call may drop the secure channel behind our back
+ (VOID) NlFinishApiClientSession( NlGlobalClientSession, TRUE );
+
+
+ //
+ // Now verify primary's authenticator and update our seed
+ //
+
+ if ( Status == STATUS_ACCESS_DENIED ||
+ !NlUpdateSeed( &NlGlobalClientSession->CsAuthenticationSeed,
+ &OurReturnAuthenticator.Credential,
+ &NlGlobalClientSession->CsSessionKey) ) {
+ Status = STATUS_TRUSTED_DOMAIN_FAILURE;
+ NlSetStatusClientSession( NlGlobalClientSession,
+ STATUS_ACCESS_DENIED );
+ NlResetWriterClientSession( NlGlobalClientSession );
+ goto Cleanup;
+ }
+
+ if ( !NT_SUCCESS( Status )) {
+ // Status = STATUS_NO_LOGON_SERVERS;
+ NlResetWriterClientSession( NlGlobalClientSession );
+ goto Cleanup;
+ }
+
+ NlResetWriterClientSession( NlGlobalClientSession );
+
+
+ //
+ // If this machine is a PDC,
+ // do the request locally.
+ //
+
+ } else {
+ SAMPR_USER_INFO_BUFFER UserInfo;
+
+ //
+ // now get the requestor's current password
+ //
+
+ //
+ // Open the user that represents this server.
+ //
+
+ Status = NlSamOpenNamedUser( AccountName, &UserHandle, NULL );
+
+ if ( !NT_SUCCESS(Status) ) {
+ goto Cleanup;
+ }
+
+ //
+ // If the authentication is from an NT client,
+ // use the NT OWF Password,
+ // otherwise, use the LM OWF password.
+ //
+
+ UserInfo.Internal1.PasswordExpired = FALSE;
+ if ( AccountType == UasServerSecureChannel ) {
+ UserInfo.Internal1.NtPasswordPresent = FALSE;
+ UserInfo.Internal1.LmPasswordPresent = TRUE;
+ UserInfo.Internal1.EncryptedLmOwfPassword =
+ *((PENCRYPTED_LM_OWF_PASSWORD)(&OwfPassword));
+
+ } else {
+ UserInfo.Internal1.LmPasswordPresent = FALSE;
+ UserInfo.Internal1.NtPasswordPresent = TRUE;
+ UserInfo.Internal1.EncryptedNtOwfPassword =
+ *((PENCRYPTED_NT_OWF_PASSWORD)(&OwfPassword));
+ }
+
+ Status = SamrSetInformationUser(
+ UserHandle,
+ UserInternal1Information,
+ &UserInfo );
+
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+
+ (VOID) SamrCloseHandle( &UserHandle );
+
+ }
+
+ Status = STATUS_SUCCESS;
+
+ //
+ // Common exit point
+ //
+
+Cleanup:
+
+ //
+ // If the request failed, be carefull to not leak authentication
+ // information.
+ //
+
+ if ( Status == STATUS_ACCESS_DENIED ) {
+ RtlZeroMemory( ReturnAuthenticator, sizeof(*ReturnAuthenticator) );
+ }
+
+ NlPrint((NL_SESSION_SETUP,
+ "NetrServerPasswordSet: Comp=" FORMAT_LPWSTR
+ " Acc=" FORMAT_LPWSTR " returns 0x%lX\n",
+ ComputerName,
+ AccountName,
+ Status ));
+
+ return Status;
+}
+
+
+NTSTATUS
+NlPackSerialNumber (
+ IN PLARGE_INTEGER SerialNumber,
+ IN OUT PNETLOGON_DELTA_ENUM Delta,
+ IN LPDWORD BufferSize,
+ IN PSESSION_INFO SessionInfo
+ )
+/*++
+
+Routine Description:
+
+ Pack the specified serial number as a delta.
+
+Arguments:
+
+ SerialNumber - The serial number to pack.
+
+ Delta: pointer to the delta structure where the new delta will
+ be returned.
+
+ DBInfo: pointer to the database info structure.
+
+ BufferSize: size of MIDL buffer that is consumed for this delta is
+ returned here.
+
+ SessionInfo: Info describing BDC that's calling us
+
+Return Value:
+
+ NT status code.
+
+--*/
+{
+ PNLPR_MODIFIED_COUNT DeltaSerialNumberSkip;
+ PSAMPR_USER_INFO_BUFFER UserAll = NULL;
+
+ //
+ // Only pack this delta if the BDC expects it.
+ //
+
+ NlAssert( SessionInfo->NegotiatedFlags & NETLOGON_SUPPORTS_BDC_CHANGELOG);
+ UNREFERENCED_PARAMETER(SessionInfo);
+
+ NlPrint(( NL_SYNC_MORE,
+ "Packing skip to serial number delta: %lx %lx\n",
+ SerialNumber->HighPart,
+ SerialNumber->LowPart ));
+
+ *BufferSize = 0;
+
+ Delta->DeltaType = SerialNumberSkip;
+ Delta->DeltaID.Rid = 0;
+ Delta->DeltaUnion.DeltaSerialNumberSkip = NULL;
+
+ //
+ // Allocate a buffer to return to the caller.
+ //
+
+ DeltaSerialNumberSkip = (PNLPR_MODIFIED_COUNT)
+ MIDL_user_allocate( sizeof(*DeltaSerialNumberSkip) );
+
+ if (DeltaSerialNumberSkip == NULL) {
+ return STATUS_NO_MEMORY;
+ }
+
+ *BufferSize += sizeof(*DeltaSerialNumberSkip);
+
+ //
+ // Copy the serial number into the buffer.
+ //
+
+ RtlCopyMemory( &DeltaSerialNumberSkip->ModifiedCount,
+ SerialNumber,
+ sizeof( DeltaSerialNumberSkip->ModifiedCount ) );
+
+ Delta->DeltaUnion.DeltaSerialNumberSkip = DeltaSerialNumberSkip;
+
+
+ //
+ // All Done
+ //
+
+ return STATUS_SUCCESS;
+}
+
+
+
+NTSTATUS
+NlPackSingleDelta (
+ IN PCHANGELOG_ENTRY ChangeLogEntry,
+ IN OUT PNETLOGON_DELTA_ENUM_ARRAY DeltaArray,
+ OUT LPDWORD BufferConsumed,
+ IN PSESSION_INFO SessionInfo,
+ IN BOOLEAN ReturnSerialNumberDeltas
+ )
+/*++
+
+Routine Description:
+
+ Pack the deltas for a single change log entry.
+
+Arguments:
+
+ ChangeLogEntry - The Change Log Entry describing the account to pack.
+
+ DeltaArray - Describes the array of deltas. The appropriate deltas will
+ be added to the end of this array. The caller has guaranteed that
+ that is room for at least MAX_DELTAS_PER_CHANGELOG - 1
+ deltas to be added to the array.
+
+ BufferConsumed - returns the size of MIDL buffer that is consumed for the
+ returned deltas
+
+ SessionInfo: Info describing BDC that's calling us
+
+ ReturnSerialNumberDeltas -- True if serial number deltas should be returned
+ when needed.
+
+Return Value:
+
+ STATUS_SUCCESS -- The function completed successfully.
+
+--*/
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ PDB_INFO DBInfo;
+ DWORD BufferSize;
+
+ UNICODE_STRING UnicodeSecretName;
+ LPWSTR AccountName;
+ PSID Sid;
+
+ //
+ // Initialization
+ //
+
+ DBInfo = &NlGlobalDBInfoArray[ChangeLogEntry->DBIndex];
+ *BufferConsumed = 0;
+
+ //
+ // Macro to account for another delta array entry being consumed/returned
+ //
+
+# define MoveToNextDeltaArrayEntry( _BufferSize ) \
+ *BufferConsumed += (sizeof(NETLOGON_DELTA_ENUM) + _BufferSize); \
+ (DeltaArray->CountReturned)++;
+
+ //
+ // Put the data for the changelog entry into the user's buffer.
+ //
+
+ switch ( ChangeLogEntry->DeltaType ) {
+ case AddOrChangeDomain:
+ Status = NlPackSamDomain(
+ &((DeltaArray->Deltas)
+ [DeltaArray->CountReturned]),
+ DBInfo,
+ &BufferSize );
+ break;
+
+ case AddOrChangeGroup:
+ Status = NlPackSamGroup( ChangeLogEntry->ObjectRid,
+ &((DeltaArray->Deltas)
+ [DeltaArray->CountReturned]),
+ DBInfo,
+ &BufferSize );
+ break;
+
+ case ChangeGroupMembership: +
+ Status = NlPackSamGroupMember( ChangeLogEntry->ObjectRid,
+ &((DeltaArray->Deltas)
+ [DeltaArray->CountReturned]),
+ DBInfo,
+ &BufferSize );
+ break;
+
+ case RenameGroup:
+
+ //
+ // we treat the rename as three deltas.
+ // 1. AddorChangeGroup delta.
+ // Backup deletes the account with old name and creates
+ // an account with new name.
+ //
+ // 2. Delta to tell the BDC that delta (3) below is for the
+ // same serial number as delta (1) above.
+ //
+ // 3. ChangeGroupMembership delta.
+ // Backup readds all members to new group.
+ //
+
+ Status = NlPackSamGroup( ChangeLogEntry->ObjectRid,
+ &((DeltaArray->Deltas)
+ [DeltaArray->CountReturned]),
+ DBInfo,
+ &BufferSize );
+
+ if( !NT_SUCCESS( Status ) ) {
+ break;
+ }
+
+ MoveToNextDeltaArrayEntry( BufferSize );
+
+
+ if ( ReturnSerialNumberDeltas ) {
+
+ Status = NlPackSerialNumber(
+ &ChangeLogEntry->SerialNumber,
+ &((DeltaArray->Deltas)
+ [DeltaArray->CountReturned]),
+ &BufferSize,
+ SessionInfo );
+
+ if( !NT_SUCCESS( Status ) ) {
+ break;
+ }
+
+ MoveToNextDeltaArrayEntry( BufferSize );
+ }
+
+ Status = NlPackSamGroupMember( ChangeLogEntry->ObjectRid,
+ &((DeltaArray->Deltas)
+ [DeltaArray->CountReturned]),
+ DBInfo,
+ &BufferSize );
+
+ break;
+
+ case AddOrChangeUser:
+ case RenameUser:
+ Status = NlPackSamUser( ChangeLogEntry->ObjectRid,
+ &((DeltaArray->Deltas)
+ [DeltaArray->CountReturned]),
+ DBInfo,
+ &BufferSize,
+ SessionInfo );
+
+ break;
+
+ case AddOrChangeAlias:
+ Status = NlPackSamAlias( ChangeLogEntry->ObjectRid,
+ &((DeltaArray->Deltas)
+ [DeltaArray->CountReturned]),
+ DBInfo,
+ &BufferSize );
+ break;
+
+ case ChangeAliasMembership:
+ Status = NlPackSamAliasMember( ChangeLogEntry->ObjectRid,
+ &((DeltaArray->Deltas)
+ [DeltaArray->CountReturned]),
+ DBInfo,
+ &BufferSize );
+ break;
+
+ case RenameAlias:
+
+ //
+ // we treat the rename as two deltas.
+ // 1. AddorChangeAlias delta.
+ // Backup deletes the account with old name and creates
+ // an account with new name.
+ //
+ // 2. Delta to tell the BDC that delta (3) below is for the
+ // same serial number as delta (1) above.
+ //
+ // 3. ChangeAliasMembership delta.
+ // Backup readds all members to new alias.
+ //
+
+ Status = NlPackSamAlias( ChangeLogEntry->ObjectRid,
+ &((DeltaArray->Deltas)
+ [DeltaArray->CountReturned]),
+ DBInfo,
+ &BufferSize );
+
+ if( !NT_SUCCESS( Status ) ) {
+ break;
+ }
+
+ MoveToNextDeltaArrayEntry( BufferSize );
+
+ if ( ReturnSerialNumberDeltas ) {
+
+ Status = NlPackSerialNumber(
+ &ChangeLogEntry->SerialNumber,
+ &((DeltaArray->Deltas)
+ [DeltaArray->CountReturned]),
+ &BufferSize,
+ SessionInfo );
+
+ if( !NT_SUCCESS( Status ) ) {
+ break;
+ }
+
+ MoveToNextDeltaArrayEntry( BufferSize );
+ }
+
+ Status = NlPackSamAliasMember( ChangeLogEntry->ObjectRid,
+ &((DeltaArray->Deltas)
+ [DeltaArray->CountReturned]),
+ DBInfo,
+ &BufferSize );
+
+ break;
+
+ case AddOrChangeLsaPolicy:
+
+ Status = NlPackLsaPolicy(
+ &((DeltaArray->Deltas)[DeltaArray->CountReturned]),
+ DBInfo,
+ &BufferSize );
+
+ break;
+
+ case AddOrChangeLsaTDomain:
+
+ NlAssert( ChangeLogEntry->Flags & CHANGELOG_SID_SPECIFIED );
+
+ if( (ChangeLogEntry->Flags & CHANGELOG_SID_SPECIFIED) == 0 ) {
+ Status = STATUS_SYNCHRONIZATION_REQUIRED;
+ break;
+ }
+
+ Status = NlPackLsaTDomain(
+ (PSID) ((LPBYTE)ChangeLogEntry + sizeof(CHANGELOG_ENTRY)),
+ &((DeltaArray->Deltas)[DeltaArray->CountReturned]),
+ DBInfo,
+ &BufferSize );
+
+ break;
+
+ case AddOrChangeLsaAccount:
+
+ NlAssert( ChangeLogEntry->Flags & CHANGELOG_SID_SPECIFIED );
+
+ if( (ChangeLogEntry->Flags & CHANGELOG_SID_SPECIFIED) == 0 ) {
+ Status = STATUS_SYNCHRONIZATION_REQUIRED;
+ break;
+ }
+
+ Status = NlPackLsaAccount(
+ (PSID) ((LPBYTE)ChangeLogEntry + sizeof(CHANGELOG_ENTRY)),
+ &((DeltaArray->Deltas)[DeltaArray->CountReturned]),
+ DBInfo,
+ &BufferSize,
+ SessionInfo );
+
+ break;
+
+ case AddOrChangeLsaSecret:
+
+ NlAssert( ChangeLogEntry->Flags & CHANGELOG_NAME_SPECIFIED );
+
+ if( (ChangeLogEntry->Flags & CHANGELOG_NAME_SPECIFIED) == 0 ) {
+ Status = STATUS_SYNCHRONIZATION_REQUIRED;
+ break;
+ }
+
+ RtlInitUnicodeString(
+ &UnicodeSecretName,
+ (LPWSTR) ((LPBYTE)ChangeLogEntry + sizeof(CHANGELOG_ENTRY)) );
+
+ Status = NlPackLsaSecret(
+ &UnicodeSecretName,
+ &((DeltaArray->Deltas)[DeltaArray->CountReturned]),
+ DBInfo,
+ &BufferSize,
+ SessionInfo );
+
+ break;
+
+ case DeleteGroup:
+ case DeleteGroupByName:
+ case DeleteUser:
+ case DeleteUserByName:
+
+ //
+ // If this is an NT 3.5 BDC,
+ // send the account name upon account deletion.
+
+ if ( ReturnSerialNumberDeltas ) {
+
+ //
+ // Send the NT 3.5 BDC a special delta type indicating the
+ // Name is attached.
+ //
+ if ( ChangeLogEntry->DeltaType == DeleteGroup ) {
+ (DeltaArray->Deltas)[DeltaArray->CountReturned].DeltaType =
+ DeleteGroupByName;
+ } else if ( ChangeLogEntry->DeltaType == DeleteUser ) {
+ (DeltaArray->Deltas)[DeltaArray->CountReturned].DeltaType =
+ DeleteUserByName;
+ } else {
+ (DeltaArray->Deltas)[DeltaArray->CountReturned].DeltaType =
+ ChangeLogEntry->DeltaType;
+ }
+
+ (DeltaArray->Deltas)[DeltaArray->CountReturned].DeltaID.Rid =
+ ChangeLogEntry->ObjectRid;
+
+
+ //
+ // Add the account name to the entry.
+ //
+
+ NlAssert(ChangeLogEntry->Flags & CHANGELOG_NAME_SPECIFIED);
+
+ if( (ChangeLogEntry->Flags & CHANGELOG_NAME_SPECIFIED) == 0 ) {
+ Status = STATUS_SYNCHRONIZATION_REQUIRED;
+ break;
+ }
+
+ BufferSize = (wcslen(
+ (LPWSTR) ((LPBYTE)ChangeLogEntry +
+ sizeof(CHANGELOG_ENTRY))) + 1 ) *
+ sizeof(WCHAR);
+
+ AccountName = (LPWSTR) MIDL_user_allocate( BufferSize );
+
+ if (AccountName == NULL) {
+ Status = STATUS_NO_MEMORY;
+ break;
+ }
+
+ wcscpy( AccountName,
+ (LPWSTR) ((LPBYTE)ChangeLogEntry + sizeof(CHANGELOG_ENTRY)));
+
+ (DeltaArray->Deltas)[DeltaArray->CountReturned].
+ DeltaUnion.DeltaDeleteGroup =
+ MIDL_user_allocate(sizeof(struct _NETLOGON_DELTA_DELETE));
+
+ if ((DeltaArray->Deltas)[DeltaArray->CountReturned].
+ DeltaUnion.DeltaDeleteGroup == NULL ) {
+ MIDL_user_free(AccountName);
+ Status = STATUS_NO_MEMORY;
+ break;
+ }
+
+ INIT_PLACE_HOLDER( (DeltaArray->Deltas)[DeltaArray->CountReturned].
+ DeltaUnion.DeltaDeleteGroup );
+ (DeltaArray->Deltas)[DeltaArray->CountReturned].
+ DeltaUnion.DeltaDeleteGroup->AccountName = AccountName;
+
+ break; // out of switch
+ }
+
+ /* Drop through to handle NT 1.0 case. */
+
+ case DeleteAlias:
+
+ (DeltaArray->Deltas)[DeltaArray->CountReturned].DeltaType =
+ ChangeLogEntry->DeltaType;
+ (DeltaArray->Deltas)[DeltaArray->CountReturned].DeltaID.Rid =
+ ChangeLogEntry->ObjectRid;
+
+ BufferSize = 0;
+
+ break;
+
+ case DeleteLsaTDomain:
+ case DeleteLsaAccount:
+
+ NlAssert( ChangeLogEntry->Flags & CHANGELOG_SID_SPECIFIED );
+
+ if( (ChangeLogEntry->Flags & CHANGELOG_SID_SPECIFIED) == 0 ) {
+ Status = STATUS_SYNCHRONIZATION_REQUIRED;
+ break;
+ }
+
+ BufferSize =
+ RtlLengthSid( (PSID)((LPBYTE)ChangeLogEntry + sizeof(CHANGELOG_ENTRY)));
+
+ Sid = (PSID) MIDL_user_allocate( BufferSize );
+
+ if( Sid == NULL ) {
+ Status = STATUS_NO_MEMORY;
+ break;
+ }
+
+ Status = RtlCopySid (
+ BufferSize,
+ Sid,
+ (PSID) ((LPBYTE)ChangeLogEntry + sizeof(CHANGELOG_ENTRY)));
+
+ if( !NT_SUCCESS( Status ) ) {
+ MIDL_user_free( Sid );
+ break;
+ }
+
+
+ (DeltaArray->Deltas)[DeltaArray->CountReturned].DeltaType =
+ ChangeLogEntry->DeltaType;
+ (DeltaArray->Deltas)[DeltaArray->CountReturned].DeltaID.Sid =
+ Sid;
+
+ break;
+
+ case DeleteLsaSecret:
+
+ NlAssert(ChangeLogEntry->Flags & CHANGELOG_NAME_SPECIFIED);
+
+ if( (ChangeLogEntry->Flags & CHANGELOG_NAME_SPECIFIED) == 0 ) {
+ Status = STATUS_SYNCHRONIZATION_REQUIRED;
+ break;
+ }
+
+ BufferSize = (wcslen(
+ (LPWSTR) ((LPBYTE)ChangeLogEntry +
+ sizeof(CHANGELOG_ENTRY))) + 1 ) *
+ sizeof(WCHAR);
+
+ AccountName = (LPWSTR) MIDL_user_allocate( BufferSize );
+
+ if (AccountName == NULL) {
+ Status = STATUS_NO_MEMORY;
+ break;
+ }
+
+ wcscpy( AccountName,
+ (LPWSTR) ((LPBYTE)ChangeLogEntry + sizeof(CHANGELOG_ENTRY)));
+
+ (DeltaArray->Deltas)[DeltaArray->CountReturned].DeltaType =
+ ChangeLogEntry->DeltaType;
+ (DeltaArray->Deltas)[DeltaArray->CountReturned].DeltaID.Name =
+ AccountName;
+
+ break;
+
+ default:
+ NlPrint((NL_CRITICAL, "NlPackSingleDelta: Invalid delta type in change log\n"));
+
+ Status = STATUS_SYNCHRONIZATION_REQUIRED;
+ break;
+ }
+
+ if ( NT_SUCCESS(Status) ) {
+ MoveToNextDeltaArrayEntry( BufferSize );
+ }
+
+ return Status;
+#undef MoveToNextDeltaArrayEntry
+}
+
+
+NTSTATUS
+NetrDatabaseDeltas (
+ IN LPWSTR PrimaryName,
+ IN LPWSTR ComputerName,
+ IN PNETLOGON_AUTHENTICATOR Authenticator,
+ OUT PNETLOGON_AUTHENTICATOR ReturnAuthenticator,
+ IN DWORD DatabaseID,
+ IN OUT PNLPR_MODIFIED_COUNT NlDomainModifiedCount,
+ OUT PNETLOGON_DELTA_ENUM_ARRAY *DeltaArrayRet,
+ IN DWORD PreferredMaximumLength
+ )
+/*++
+
+Routine Description:
+
+ This function is used by a NTLANMAN BDC or SAM member server to
+ request SAM-style account delta information from a SAM PDC. This
+ function can only be called by a server which has previously
+ authenticated with the PDC by calling I_NetServerAuthenticate. This
+ function uses RPC to contact the Netlogon service on the PDC.
+
+ This function returns a list of deltas. A delta describes an
+ individual domain, user or group and all of the field values for that
+ object. The PDC maintains a list of deltas not including all of the
+ field values for that object. Rather, the PDC retrieves the field
+ values from SAM and returns those values from this call. The PDC
+ optimizes the data returned on this call by only returning the field
+ values for a particular object once on a single invocation of this
+ function. This optimizes the typical case where multiple deltas
+ exist for a single object (e.g., an application modified many fields
+ of the same user during a short period of time using different calls
+ to the SAM service).
+
+Arguments:
+
+ PrimaryName -- Name of the PDC to retrieve the deltas from.
+
+ ComputerName -- Name of the BDC or member server making the call.
+
+ Authenticator -- supplied by the server.
+
+ ReturnAuthenticator -- Receives an authenticator returned by the PDC.
+
+ DatabaseID -- Identifies the databse for which the deltas are requested.
+ For SAM database the ID is 0, for Builtin Domain the ID is 1. Other
+ databases may be defined later.
+
+ NlDomainModifiedCount -- Specifies the DomainModifiedCount of the
+ last delta retrieved by the server. Returns the
+ DomainModifiedCount of the last delta returned from the PDC
+ on this call.
+
+ Deltas -- Receives a pointer to a buffer where the information is
+ placed. The information returned is an array of
+ NETLOGON_DELTA_ENUM structures.
+
+ PreferredMaximumLength - Preferred maximum length of returned
+ data (in 8-bit bytes). This is not a hard upper limit, but
+ serves as a guide to the server. Due to data conversion
+ between systems with different natural data sizes, the actual
+ amount of data returned may be greater than this value.
+
+Return Value:
+
+ STATUS_SUCCESS -- The function completed successfully.
+
+ STATUS_SYNCHRONIZATION_REQUIRED -- The replicant is totally out of sync and
+ should call I_NetDataSync to do a full synchronization with
+ the PDC.
+
+ STATUS_MORE_ENTRIES -- The replicant should call again to get more
+ data.
+
+ STATUS_ACCESS_DENIED -- The replicant should re-authenticate with
+ the PDC.
+
+
+--*/
+{
+ NTSTATUS Status;
+ PSERVER_SESSION ServerSession = NULL;
+ PCHANGELOG_ENTRY ChangeLogEntry = NULL;
+ BOOLEAN PackThisEntry = TRUE;
+
+ BOOL ChangelogLocked = FALSE;
+
+ PDB_INFO DBInfo;
+ LARGE_INTEGER RunningSerialNumber;
+ LARGE_INTEGER PackedSerialNumber;
+ LARGE_INTEGER OriginalSerialNumber;
+
+ DWORD BufferConsumed = 0;
+ DWORD BufferSize = 0;
+
+ PNETLOGON_DELTA_ENUM_ARRAY DeltaArray;
+
+
+ SESSION_INFO SessionInfo;
+
+ DEFSSIAPITIMER;
+
+ INITSSIAPITIMER;
+ STARTSSIAPITIMER;
+
+ //
+ // This API is not supported on workstations.
+ //
+
+ if ( NlGlobalRole == RoleMemberWorkstation ) {
+ return STATUS_NOT_SUPPORTED;
+ }
+
+ //
+ // Initialization
+ //
+ if ( DatabaseID >= NUM_DBS ) {
+ return STATUS_INVALID_LEVEL;
+ }
+
+ *DeltaArrayRet = DeltaArray = (PNETLOGON_DELTA_ENUM_ARRAY)
+ MIDL_user_allocate( sizeof(NETLOGON_DELTA_ENUM_ARRAY) );
+
+ if( DeltaArray == NULL ) {
+ return STATUS_NO_MEMORY;
+ }
+
+ DeltaArray->CountReturned = 0;
+ DeltaArray->Deltas = NULL;
+ SessionInfo.NegotiatedFlags = 0;
+
+
+ DBInfo = &NlGlobalDBInfoArray[DatabaseID];
+
+ //
+ // Check the primary name.
+ //
+
+ Status = NlVerifyWorkstation( PrimaryName );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto Cleanup;
+ }
+
+ RtlCopyMemory( &RunningSerialNumber,
+ &NlDomainModifiedCount->ModifiedCount,
+ sizeof(RunningSerialNumber));
+
+ OriginalSerialNumber.QuadPart = RunningSerialNumber.QuadPart;
+ PackedSerialNumber.QuadPart = RunningSerialNumber.QuadPart;
+
+ NlPrint((NL_SYNC,
+ "NetrDatabaseDeltas: " FORMAT_LPWSTR " partial sync called by " FORMAT_LPWSTR
+ " SerialNumber:%lx %lx.\n",
+ DBInfo->DBName,
+ ComputerName,
+ RunningSerialNumber.HighPart,
+ RunningSerialNumber.LowPart ));
+
+ //
+ // Retrieve the requestor's entry to get sessionkey
+ //
+
+ LOCK_SERVER_SESSION_TABLE();
+ ServerSession = NlFindNamedServerSession( ComputerName );
+
+ if (ServerSession == NULL) {
+ UNLOCK_SERVER_SESSION_TABLE();
+ Status = STATUS_ACCESS_DENIED;
+ // Don't log this event since it happens in nature after a reboot
+ // or after we scavenge the server session.
+ goto CleanupNoEventlog;
+ }
+
+ //
+ // Allow this call only on ServerSecureChannel.
+ //
+
+ if( ServerSession->SsSecureChannelType != ServerSecureChannel ) {
+ UNLOCK_SERVER_SESSION_TABLE();
+ Status = STATUS_ACCESS_DENIED;
+ ServerSession = NULL;
+ goto Cleanup;
+ }
+
+ //
+ // Verify the Authenticator and update seed if OK
+ //
+
+ Status = NlCheckAuthenticator( ServerSession,
+ Authenticator,
+ ReturnAuthenticator);
+
+ if ( !NT_SUCCESS(Status) ) {
+ UNLOCK_SERVER_SESSION_TABLE();
+
+ NlPrint((NL_CRITICAL, "NetrDatabaseDeltas: authentication failed.\n" ));
+
+ ServerSession = NULL;
+ goto Cleanup;
+ }
+
+
+ //
+ // Prevent entry from being deleted, but drop the global lock.
+ //
+ // Beware of server with two concurrent calls outstanding
+ // (must have rebooted.)
+ //
+
+ if (ServerSession->SsFlags & SS_LOCKED ) {
+ UNLOCK_SERVER_SESSION_TABLE();
+
+ NlPrint((NL_CRITICAL, "NetrDatabaseDeltas: Concurrent call detected.\n" ));
+
+ Status = STATUS_ACCESS_DENIED;
+ ServerSession = NULL;
+ goto Cleanup;
+ }
+ ServerSession->SsFlags |= SS_LOCKED;
+
+ SessionInfo.SessionKey = ServerSession->SsSessionKey;
+ SessionInfo.NegotiatedFlags = ServerSession->SsNegotiatedFlags;
+
+ UNLOCK_SERVER_SESSION_TABLE();
+
+
+ //
+ // If the BDC is in sync,
+ // simply return.
+ //
+
+ LOCK_CHANGELOG();
+ ChangelogLocked = TRUE;
+
+ if ( RunningSerialNumber.QuadPart ==
+ NlGlobalChangeLogDesc.SerialNumber[DatabaseID].QuadPart ) {
+ Status = STATUS_SUCCESS;
+ goto Cleanup;
+ }
+
+ //
+ // Get a copy of the appropriate entry in the change_log.
+ // Note that the record_id contains last record received by client.
+ //
+
+ if ((ChangeLogEntry = NlGetNextUniqueChangeLogEntry(
+ &NlGlobalChangeLogDesc,
+ RunningSerialNumber,
+ DBInfo->DBIndex,
+ NULL ))== NULL) {
+
+ //
+ // Handle the case where the BDC has more recent changes than we do.
+ //
+ // Just return our newest change log entry with the same promotion count.
+ // The BDC will realize what's going on and un-do its newer changes.
+ //
+ // Only do this if our PromotionCount is greater than the BDCs. If
+ // our promotion count is equal to that of the BDC, either our change log
+ // has wrapped, or the BDC is royally confused.
+ //
+ // Don't be tempted to return a change log entry with an
+ // older promotion count. We'd have no way of knowing which delta
+ // to actually return to the caller.
+ //
+
+ if ( ((NlGlobalChangeLogDesc.SerialNumber[DatabaseID].HighPart &
+ NlGlobalChangeLogPromotionMask) >
+ (RunningSerialNumber.HighPart & NlGlobalChangeLogPromotionMask)) &&
+ (SessionInfo.NegotiatedFlags & NETLOGON_SUPPORTS_REDO) ) {
+
+ ChangeLogEntry = NlFindPromotionChangeLogEntry(
+ &NlGlobalChangeLogDesc,
+ RunningSerialNumber,
+ DBInfo->DBIndex );
+
+ //
+ // Don't actually pack this change log entry. We've found it
+ // so we can pack a "serial number" delta. But the BDC already
+ // has this particular change.
+ //
+
+ PackThisEntry = FALSE;
+ }
+
+ if ( ChangeLogEntry == NULL ) {
+ NlPrint((NL_CRITICAL,
+ "NetrDatabaseDeltas: "
+ "delta not found in cache, returning full required.\n" ));
+
+ Status = STATUS_SYNCHRONIZATION_REQUIRED;
+ goto Cleanup;
+ } else {
+ NlPrint((NL_SYNC, "NetrDatabaseDeltas: BDC more recent than PDC (recovering).\n" ));
+ }
+ }
+
+ UNLOCK_CHANGELOG();
+ ChangelogLocked = FALSE;
+
+ //
+ // Allocate memory for delta buffer.
+ //
+
+ DeltaArray->Deltas = (PNETLOGON_DELTA_ENUM) MIDL_user_allocate(
+ MAX_DELTA_COUNT * sizeof(NETLOGON_DELTA_ENUM) );
+
+ if( DeltaArray->Deltas == NULL ) {
+ Status = STATUS_NO_MEMORY;
+ goto Cleanup;
+ }
+
+
+ //
+ // wipe off the buffer so that cleanup will not be in fault.
+ //
+
+ RtlZeroMemory( DeltaArray->Deltas,
+ MAX_DELTA_COUNT * sizeof(NETLOGON_DELTA_ENUM) );
+
+
+ //
+ // Loop packing deltas as long as there is room for more deltas
+ //
+ // In some cases we pack multiple deltas on the wire for one entry in the
+ // change log, we want to ensure that all of these deltas are sent to
+ // the BDC on a single call.
+ //
+
+ while ( DeltaArray->CountReturned + MAX_DELTAS_PER_CHANGELOG <= MAX_DELTA_COUNT ) {
+
+ //
+ // If the serial number of the delta being packed isn't the one
+ // expected by the BDC, tell the BDC what the serial number is.
+ //
+
+ if ( ChangeLogEntry->SerialNumber.QuadPart !=
+ PackedSerialNumber.QuadPart + 1 ) {
+
+ if ( SessionInfo.NegotiatedFlags & NETLOGON_SUPPORTS_BDC_CHANGELOG){
+
+ Status = NlPackSerialNumber(
+ &ChangeLogEntry->SerialNumber,
+ &((DeltaArray->Deltas)
+ [DeltaArray->CountReturned]),
+ &BufferSize,
+ &SessionInfo );
+ if( !NT_SUCCESS( Status ) ) {
+ goto Cleanup;
+ }
+
+ BufferConsumed += BufferSize;
+ DeltaArray->CountReturned ++;
+
+ //
+ // If we're not really going to pack the entry,
+ // pretend that we already have.
+ //
+ if ( !PackThisEntry) {
+ PackedSerialNumber.QuadPart = ChangeLogEntry->SerialNumber.QuadPart;
+ }
+ }
+
+ }
+
+
+ if ( PackThisEntry ) {
+
+ //
+ // Put the data for the changelog entry into the user's buffer.
+ //
+
+ Status = NlPackSingleDelta( ChangeLogEntry,
+ DeltaArray,
+ &BufferSize,
+ &SessionInfo,
+ (BOOLEAN)((SessionInfo.NegotiatedFlags & NETLOGON_SUPPORTS_BDC_CHANGELOG) != 0) );
+
+ //
+ // If we successfully put the delta into the delta array,
+ // do the bookwork
+ //
+
+ if ( NT_SUCCESS( Status ) ) {
+
+ BufferConsumed += BufferSize;
+
+ PackedSerialNumber.QuadPart = ChangeLogEntry->SerialNumber.QuadPart;
+
+ NlPrint((NL_SYNC_MORE,
+ "NetrDatabaseDeltas: Modified count of the "
+ "packed record: %lx %lx\n",
+ ChangeLogEntry->SerialNumber.HighPart,
+ ChangeLogEntry->SerialNumber.LowPart ));
+
+
+ //
+ // In the case where an user/group/alias record was
+ // added and deleted before the delta was made we will
+ // trace the change log and see there is correpondance
+ // delete log. If we found one then ignore this delta
+ // and proceed to the next delta. If we couldn't find
+ // one then return error STATUS_SYNCHRONIZATION_REQUIRED.
+ //
+
+ } else if ( IsObjectNotFoundStatus( ChangeLogEntry->DeltaType, Status ) ) {
+
+ if( !NlRecoverChangeLog(ChangeLogEntry) ) {
+
+ NlPrint((NL_CRITICAL,
+ "NetrDatabaseDeltas: "
+ "object not found in database, returning full "
+ "required (%lx).\n", Status ));
+
+ Status = STATUS_SYNCHRONIZATION_REQUIRED;
+
+ IF_DEBUG( BREAKPOINT ) {
+ NlAssert( FALSE );
+ }
+
+ goto Cleanup;
+
+ } else {
+
+ //
+ // We found a delete delta, so ignore the original delta.
+ //
+
+ Status = STATUS_SUCCESS;
+ }
+
+ //
+ // All other errors are fatal
+ //
+
+ } else {
+ goto Cleanup;
+ }
+ }
+
+ PackThisEntry = TRUE;
+
+
+ //
+ // Free up used temp. record
+ //
+
+ RunningSerialNumber.QuadPart = ChangeLogEntry->SerialNumber.QuadPart;
+ NetpMemoryFree(ChangeLogEntry);
+ ChangeLogEntry = NULL;
+
+ //
+ // If we've returned all the entries, we're all done.
+ //
+
+ LOCK_CHANGELOG();
+ ChangelogLocked = TRUE;
+
+ if ((ChangeLogEntry = NlGetNextUniqueChangeLogEntry(
+ &NlGlobalChangeLogDesc,
+ RunningSerialNumber,
+ DBInfo->DBIndex,
+ NULL )) == NULL) {
+ Status = STATUS_SUCCESS;
+ goto Cleanup;
+ }
+
+ UNLOCK_CHANGELOG();
+ ChangelogLocked = FALSE;
+
+
+ //
+ // Don't return more data to the caller than he wants.
+ //
+
+ if( BufferConsumed >= PreferredMaximumLength) {
+ Status = STATUS_MORE_ENTRIES;
+ goto Cleanup;
+ }
+
+ //
+ // If we're debugging replication, return only one change to the caller.
+ //
+#if DBG
+ if ( NlGlobalTrace & NL_ONECHANGE_REPL ) {
+ Status = STATUS_MORE_ENTRIES;
+ goto Cleanup;
+ }
+#endif // DBG
+
+
+ //
+ // If the service is going down, stop packing deltas and
+ // return to the caller.
+ //
+
+ if( NlGlobalTerminate ) {
+
+ NlPrint((NL_CRITICAL, "NetrDatabaseDeltas is asked to return "
+ "when the service is going down.\n"));
+ Status = STATUS_MORE_ENTRIES;
+ goto Cleanup;
+ }
+
+ }
+
+ Status = STATUS_MORE_ENTRIES;
+
+Cleanup:
+
+ //
+ // write event log
+ //
+
+ if ( !NT_SUCCESS( Status ) ) {
+
+ LPWSTR MsgStrings[2];
+
+ MsgStrings[0] = ComputerName;
+ MsgStrings[1] = (LPWSTR) Status;
+
+ NlpWriteEventlog(
+ NELOG_NetlogonPartialSyncCallFailed,
+ EVENTLOG_WARNING_TYPE,
+ (LPBYTE)&Status,
+ sizeof(Status),
+ MsgStrings,
+ 2 | LAST_MESSAGE_IS_NTSTATUS );
+
+ } else {
+
+ //
+ // Log the successful replication only if deltas have been returned
+ // to the caller.
+ //
+ if ( DeltaArray->CountReturned != 0 ) {
+ LPWSTR MsgStrings[2];
+ WCHAR CountBuffer[20]; // random size
+
+ MsgStrings[0] = ComputerName;
+
+ ultow( DeltaArray->CountReturned, CountBuffer, 10);
+ MsgStrings[1] = CountBuffer;
+
+ NlpWriteEventlog(
+ NELOG_NetlogonPartialSyncCallSuccess,
+ EVENTLOG_INFORMATION_TYPE,
+ NULL,
+ 0,
+ MsgStrings,
+ 2 );
+ }
+
+ }
+
+
+ //
+ // Free up locally allocated resources.
+ //
+
+CleanupNoEventlog:
+
+ //
+ // Copy the serial number back to the caller
+ //
+
+ if ( NT_SUCCESS(Status)) {
+
+ RtlCopyMemory( &NlDomainModifiedCount->ModifiedCount,
+ &PackedSerialNumber,
+ sizeof(PackedSerialNumber));
+
+
+ //
+ // If this is an NT1.0 BDC,
+ // Only remember the latest Serial Number it asked for, AND
+ // force it the call back once it has updated the SerialNumber
+ // so we know what that serial number is.
+ //
+ // NT 3.5 BDCs "persistently" try to update their database to the
+ // PDCs version once they get a pulse indicating their database is
+ // out of date.
+ //
+
+ if ( (SessionInfo.NegotiatedFlags & NETLOGON_SUPPORTS_PERSISTENT_BDC) == 0 ) {
+
+ //
+ // Use the SerialNumber the BDC originally passed us.
+ //
+
+ PackedSerialNumber.QuadPart = OriginalSerialNumber.QuadPart;
+
+ //
+ // If we're returning any deltas at all,
+ // force the BDC to call us back.
+ //
+
+ if ( Status == STATUS_SUCCESS && DeltaArray->CountReturned != 0 ) {
+ Status = STATUS_MORE_ENTRIES;
+ }
+
+ }
+
+ //
+ // If we weren't successful,
+ // Don't return any deltas.
+ //
+
+ } else {
+ if ( DeltaArray->Deltas != NULL ) {
+ NlFreeDBDeltaArray( DeltaArray->Deltas, DeltaArray->CountReturned );
+ DeltaArray->Deltas = NULL;
+ }
+ DeltaArray->CountReturned = 0;
+
+ }
+
+ if ( ChangelogLocked ) {
+ UNLOCK_CHANGELOG();
+ }
+
+ if( ChangeLogEntry != NULL) {
+ NetpMemoryFree( ChangeLogEntry );
+ }
+
+ //
+ // Unlock the server session entry if we've locked it.
+ //
+
+ if ( ServerSession != NULL ) {
+
+ //
+ // If we are successfully returning these deltas to the BDC,
+ // update our tables to reflect the changes.
+ //
+
+ if ( Status == STATUS_SUCCESS ) {
+ NlPrimaryAnnouncementFinish( ServerSession,
+ DatabaseID,
+ &PackedSerialNumber );
+
+ }
+ NlUnlockServerSession( ServerSession );
+ }
+
+
+ NlPrint((NL_SYNC,
+ "NetrDatabaseDeltas: " FORMAT_LPWSTR " returning (0x%lx) to "
+ FORMAT_LPWSTR "\n",
+ DBInfo->DBName,
+ Status,
+ ComputerName ));
+
+ STOPSSIAPITIMER;
+
+ NlPrint((NL_REPL_TIME,"NetrDatabaseDeltas Time:\n"));
+ PRINTSSIAPITIMER;
+
+ return Status;
+
+}
+
+
+
+NTSTATUS
+NlSyncSamDatabase(
+ IN PSERVER_SESSION ServerSession,
+ IN DWORD DatabaseID,
+ IN SYNC_STATE RestartState,
+ IN OUT PULONG SyncContext,
+ IN OUT PNETLOGON_DELTA_ENUM_ARRAY DeltaArray,
+ IN DWORD PreferredMaximumLength,
+ IN PSESSION_INFO SessionInfo
+ )
+/*++
+
+Routine Description:
+
+ This function is a real worker for the NetrDatabaseSync function and
+ retrieves a SAM database in the delta buffer.
+
+ This function uses the find-first find-next model to return portions
+ of the SAM database at a time. The SAM database is returned as a
+ list of deltas like those returned from I_NetDatabaseDeltas. The
+ following deltas are returned for each domain:
+
+ * One AddOrChangeDomain delta, followed by
+
+ * One AddOrChangeGroup delta for each group, followed by,
+
+ * One AddOrChangeUser delta for each user, followed by
+
+ * One ChangeGroupMembership delta for each group followed by,
+
+ * One AddOrChangeAlias delta for each alias, followed by,
+
+ * One ChangeAliasMembership delta for each alias.
+
+
+Arguments:
+
+ ServerSession -- pointer to connection context.
+
+ DatabaseID -- Identifies the databse for which the deltas are requested.
+ For SAM database the ID is 0, for Builtin Domain the ID is 1. Other
+ databases may be defined later.
+
+ RestartState -- Specifies whether this is a restart of the full sync and how
+ to interpret SyncContext. This value should be NormalState unless this
+ is the restart of a full sync.
+
+ However, if the caller is continuing a full sync after a reboot,
+ the following values are used:
+
+ GroupState - SyncContext is the global group rid to continue with.
+ UserState - SyncContext is the user rid to continue with
+ GroupMemberState - SyncContext is the global group rid to continue with
+ AliasState - SyncContext should be zero to restart at first alias
+ AliasMemberState - SyncContext should be zero to restart at first alias
+
+ One cannot continue the LSA database in this way.
+
+ SyncContext -- Specifies context needed to continue the
+ operation. The caller should treat this as an opaque
+ value. The value should be zero before the first call.
+
+ DeltaArray -- Pointer to a buffer where the information
+ is placed. The information returned is an array of
+ NETLOGON_DELTA_ENUM structures.
+
+ PreferredMaximumLength - Preferred maximum length of returned
+ data (in 8-bit bytes). This is not a hard upper limit, but
+ serves as a guide to the server. Due to data conversion
+ between systems with different natural data sizes, the actual
+ amount of data returned may be greater than this value.
+
+ SessionInfo - Information shared between PDC and BDC.
+
+Return Value:
+
+ STATUS_SUCCESS -- The function completed successfully.
+
+ STATUS_MORE_ENTRIES -- The replicant should call again to get more
+ data.
+
+--*/
+{
+ NTSTATUS Status;
+
+ PSAM_SYNC_CONTEXT SamDBContext;
+
+ PDB_INFO DBInfo;
+
+ DWORD BufferConsumed = 0;
+ DWORD BufferSize;
+
+ DBInfo = &NlGlobalDBInfoArray[DatabaseID];
+
+
+ //
+ // Allocate memory for delta buffer.
+ //
+
+ DeltaArray->Deltas = (PNETLOGON_DELTA_ENUM) MIDL_user_allocate(
+ MAX_DELTA_COUNT * sizeof(NETLOGON_DELTA_ENUM) );
+
+ if( DeltaArray->Deltas == NULL ) {
+
+
+ NlPrint((NL_CRITICAL,
+ "NlSyncSamDatabase: Can't allocate %d bytes\n",
+ MAX_DELTA_COUNT * sizeof(NETLOGON_DELTA_ENUM) ));
+
+ return( STATUS_NO_MEMORY );
+ }
+
+
+ //
+ // wipe off the buffer so that cleanup will not be in fault.
+ //
+
+ RtlZeroMemory( DeltaArray->Deltas,
+ MAX_DELTA_COUNT * sizeof(NETLOGON_DELTA_ENUM) );
+
+
+ //
+ // If this is the first call or an explicit restart call,
+ // allocate and initialize the sync context.
+ //
+
+ if ( *SyncContext == 0 || RestartState != NormalState ) {
+
+ //
+ // If there already is a sync context,
+ // delete it.
+ //
+
+ if ( ServerSession->SsSync != NULL ) {
+ CLEAN_SYNC_CONTEXT( ServerSession->SsSync );
+ } else {
+
+ ServerSession->SsSync = NetpMemoryAllocate( sizeof(SYNC_CONTEXT) );
+ if ( ServerSession->SsSync == NULL ) {
+
+ Status = STATUS_NO_MEMORY;
+ goto Cleanup;
+ }
+ }
+
+ //
+ // Initialize all the fields in the newly allocated resume handle
+ // to indicate that SAM has never yet been called.
+ //
+
+ INIT_SYNC_CONTEXT( ServerSession->SsSync, SamDBContextType );
+
+ SamDBContext = &(ServerSession->SsSync->DBContext.Sam);
+ SamDBContext->SyncSerial = 1;
+
+ //
+ // Compute the continuation state based on the input parameters
+ //
+
+ switch ( RestartState ) {
+ case NormalState:
+
+ //
+ // Put the description of the Domain at the front of the buffer for the
+ // first call.
+ //
+
+ Status = NlPackSamDomain( &((DeltaArray->Deltas)[DeltaArray->CountReturned]),
+ DBInfo,
+ &BufferSize );
+
+ (DeltaArray->CountReturned)++;
+ BufferConsumed += BufferSize;
+
+ if ( !NT_SUCCESS(Status) ) {
+ goto Cleanup;
+ }
+
+ SamDBContext->SyncState = GroupState;
+ SamDBContext->SamEnumHandle = 0;
+ break;
+
+ case AliasState:
+ case AliasMemberState:
+ if ( *SyncContext != 0 ) {
+ NlPrint(( NL_CRITICAL,
+ "NlSyncSamDatabase: Cannot restart alias enumeration.\n" ));
+
+ Status = STATUS_INVALID_PARAMETER;
+ goto Cleanup;
+ }
+ /* Drop Through */
+
+ case GroupState:
+ case UserState:
+ case GroupMemberState:
+ SamDBContext->SyncState = RestartState;
+ SamDBContext->SamEnumHandle = *SyncContext;
+ break;
+
+ default:
+ NlPrint(( NL_CRITICAL,
+ "NlSyncSamDatabase: Invalid RestartState passed %ld.\n",
+ RestartState ));
+
+ Status = STATUS_INVALID_PARAMETER;
+ goto Cleanup;
+
+
+ }
+
+ } else {
+
+ NlAssert( ServerSession->SsSync != NULL);
+
+ if( ServerSession->SsSync == NULL) {
+
+ Status = STATUS_SYNCHRONIZATION_REQUIRED;
+ goto Cleanup;
+ }
+
+ NlAssert( ServerSession->SsSync->DBContextType ==
+ SamDBContextType);
+
+ if( ServerSession->SsSync->DBContextType !=
+ SamDBContextType ) {
+
+ Status = STATUS_SYNCHRONIZATION_REQUIRED;
+ goto Cleanup;
+ }
+
+ SamDBContext = &(ServerSession->SsSync->DBContext.Sam);
+
+ NlAssert( SamDBContext->SyncSerial == *SyncContext );
+
+ if( SamDBContext->SyncSerial != *SyncContext ) {
+
+ Status = STATUS_SYNCHRONIZATION_REQUIRED;
+ goto Cleanup;
+ }
+
+ SamDBContext->SyncSerial++;
+ }
+
+ //
+ // Loop for each entry placed in the output buffer
+ //
+ // Each iteration of the loop below puts one more entry into the array
+ // returned to the caller. The algorithm is split into 2 parts. The
+ // first part checks to see if we need to retrieve more information from
+ // SAM and gets the description of several users or group from SAM in a
+ // single call. The second part puts a single entry into the buffer
+ // returned to the caller.
+ //
+
+ while ( SamDBContext->SyncState != SamDoneState ) {
+
+ //
+ // If we've filled out pre-allocated array,
+ // return now.
+ //
+ if ( DeltaArray->CountReturned + MAX_DELTAS_PER_CHANGELOG > MAX_DELTA_COUNT ) {
+ Status = STATUS_MORE_ENTRIES;
+ goto Cleanup;
+ }
+
+
+ //
+ // Get more information from SAM
+ //
+ // Handle when we've not yet called SAM or we've already consumed
+ // all of the information returned on a previous call to SAM.
+ //
+ // This is a 'while' rather than an 'if' to handle the case
+ // where SAM returns zero entries.
+ //
+
+ while ( SamDBContext->Index >= SamDBContext->Count ) {
+
+ //
+ // Free any previous buffer returned from SAM.
+ //
+
+ if ( ServerSession->SsSync != NULL ) {
+ CLEAN_SYNC_CONTEXT( ServerSession->SsSync );
+ }
+
+ //
+ // If we've already gotten everything from SAM,
+ // we've finished all of the groups,
+ //
+ // If we've just done the groups,
+ // go on to do the users.
+ //
+ // If we've just done the users,
+ // go on to do the group memberships.
+ //
+ // If we've just done the group memberships,
+ // go on to do the alias.
+ //
+ // If we've just done the alias,
+ // go on to do the alias membership.
+ //
+ // If we've just done the alias memberships,
+ // we're all done.
+ //
+
+ if ( SamDBContext->SamAllDone ) {
+
+ SamDBContext->SamEnumHandle = 0;
+ SamDBContext->Index = 0;
+ SamDBContext->Count = 0;
+ SamDBContext->SamAllDone = FALSE;
+
+ if (SamDBContext->SyncState == GroupState ) {
+
+
+ NlPrint((NL_SYNC,
+ "NlSyncSamDatabase: packing user records.\n"));
+
+ SamDBContext->SyncState = UserState;
+ } else if (SamDBContext->SyncState == UserState ) {
+
+ NlPrint((NL_SYNC,
+ "NlSyncSamDatabase: "
+ "packing groupmember records.\n"));
+
+ SamDBContext->SyncState = GroupMemberState;
+ } else if (SamDBContext->SyncState == GroupMemberState ){
+
+ NlPrint((NL_SYNC,
+ "NlSyncSamDatabase: packing alias records.\n"));
+
+ SamDBContext->SyncState = AliasState;
+ } else if (SamDBContext->SyncState == AliasState ){
+
+ NlPrint((NL_SYNC,
+ "NlSyncSamDatabase: "
+ " packing aliasmember records.\n"));
+
+ SamDBContext->SyncState = AliasMemberState ;
+ } else if (SamDBContext->SyncState == AliasMemberState ){
+
+ NlPrint((NL_SYNC,
+ "NlSyncSamDatabase: packing done.\n"));
+
+ SamDBContext->SyncState = SamDoneState;
+ Status = STATUS_SUCCESS;
+ }
+
+ break;
+ }
+
+ //
+ // Do the actual enumeration
+ //
+
+ if (SamDBContext->SyncState == GroupState ||
+ SamDBContext->SyncState == GroupMemberState ) {
+
+ Status = SamIEnumerateAccountRids(
+ DBInfo->DBHandle,
+ SAM_GLOBAL_GROUP_ACCOUNT,
+ SamDBContext->SamEnumHandle, // Return RIDs greater than this
+ SAM_SYNC_PREF_MAX,
+ &SamDBContext->Count,
+ &SamDBContext->RidArray );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ SamDBContext->RidArray = NULL;
+ goto Cleanup;
+ }
+
+ if ( SamDBContext->Count != 0 ) {
+ SamDBContext->SamEnumHandle =
+ SamDBContext->RidArray[SamDBContext->Count-1];
+ }
+
+ } else if (SamDBContext->SyncState == UserState ) {
+
+
+ Status = SamIEnumerateAccountRids(
+ DBInfo->DBHandle,
+ SAM_USER_ACCOUNT,
+ SamDBContext->SamEnumHandle, // Return RIDs greater than this
+ SAM_SYNC_PREF_MAX,
+ &SamDBContext->Count,
+ &SamDBContext->RidArray );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ SamDBContext->RidArray = NULL;
+ goto Cleanup;
+ }
+
+ if ( SamDBContext->Count != 0 ) {
+ SamDBContext->SamEnumHandle =
+ SamDBContext->RidArray[SamDBContext->Count-1];
+ }
+
+ } else if (SamDBContext->SyncState == AliasState ||
+ SamDBContext->SyncState == AliasMemberState ) {
+
+ Status = SamrEnumerateAliasesInDomain(
+ DBInfo->DBHandle,
+ &SamDBContext->SamEnumHandle,
+ &SamDBContext->SamEnum,
+ SAM_SYNC_PREF_MAX,
+ &SamDBContext->Count );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ SamDBContext->SamEnum = NULL;
+ goto Cleanup;
+ }
+
+ NlAssert( SamDBContext->Count ==
+ SamDBContext->SamEnum->EntriesRead );
+
+ }
+
+
+ //
+ // If SAM says there is more information,
+ // just ensure he returned something to us on this call.
+ //
+
+ if ( Status == STATUS_MORE_ENTRIES ) {
+ NlAssert( SamDBContext->Count != 0 );
+
+ //
+ // If SAM says he's returned all of the information,
+ // remember not to ask SAM for more.
+ //
+
+ } else {
+ SamDBContext->SamAllDone = TRUE;
+ }
+
+ SamDBContext->Index = 0;
+ }
+
+ //
+ // Place this entry into the return buffer.
+ //
+
+ if ( SamDBContext->Count > 0 ) {
+
+ if (SamDBContext->SyncState == GroupState ) {
+ Status = NlPackSamGroup(
+ SamDBContext->RidArray[SamDBContext->Index],
+ &((DeltaArray->Deltas)[DeltaArray->CountReturned]),
+ DBInfo,
+ &BufferSize );
+
+ } else if (SamDBContext->SyncState == UserState ) {
+ Status = NlPackSamUser(
+ SamDBContext->RidArray[SamDBContext->Index],
+ &((DeltaArray->Deltas)[DeltaArray->CountReturned]),
+ DBInfo,
+ &BufferSize,
+ SessionInfo );
+
+ } else if (SamDBContext->SyncState == GroupMemberState ) {
+ Status = NlPackSamGroupMember(
+ SamDBContext->RidArray[SamDBContext->Index],
+ &((DeltaArray->Deltas)[DeltaArray->CountReturned]),
+ DBInfo,
+ &BufferSize );
+
+ } else if (SamDBContext->SyncState == AliasState ) {
+ Status = NlPackSamAlias(
+ SamDBContext->SamEnum->
+ Buffer[SamDBContext->Index].RelativeId,
+ &((DeltaArray->Deltas)[DeltaArray->CountReturned]),
+ DBInfo,
+ &BufferSize );
+
+ } else if (SamDBContext->SyncState == AliasMemberState ) {
+ Status = NlPackSamAliasMember(
+ SamDBContext->SamEnum->
+ Buffer[SamDBContext->Index].RelativeId,
+ &((DeltaArray->Deltas)[DeltaArray->CountReturned]),
+ DBInfo,
+ &BufferSize );
+ }
+
+ //
+ // If there was a real error or this group didn't fit,
+ // return to the caller.
+ //
+
+ if ( Status != STATUS_SUCCESS ) {
+ goto Cleanup;
+ }
+
+ SamDBContext->Index ++;
+ (DeltaArray->CountReturned)++;
+ BufferConsumed +=
+ (sizeof(NETLOGON_DELTA_ENUM) + BufferSize);
+
+ if( BufferConsumed >= PreferredMaximumLength) {
+ Status = STATUS_MORE_ENTRIES;
+ goto Cleanup;
+ }
+
+ //
+ // If we're debugging replication, return only one change to the caller.
+ //
+#if DBG
+ if ( NlGlobalTrace & NL_ONECHANGE_REPL ) {
+ Status = STATUS_MORE_ENTRIES;
+ goto Cleanup;
+ }
+#endif // DBG
+
+ //
+ // if the service is going down, stop packing records and
+ // return to the caller.
+ //
+ // Don't alarm the caller with the status code. He'll find out
+ // on the next call that we're no longer here.
+ //
+
+ if( NlGlobalTerminate ) {
+
+ NlPrint((NL_CRITICAL, "NetrDatabaseSync is asked to return "
+ "when the service is going down.\n"));
+ Status = STATUS_MORE_ENTRIES;
+ goto Cleanup;
+ }
+
+ }
+ }
+
+Cleanup:
+
+ //
+ // Set the return parameters to the proper values.
+ //
+
+ if ( NT_SUCCESS( Status ) ) {
+ *SyncContext = SamDBContext->SyncSerial;
+
+ } else {
+ if ( DeltaArray->Deltas != NULL ) {
+ NlFreeDBDeltaArray( DeltaArray->Deltas, DeltaArray->CountReturned );
+ DeltaArray->Deltas = NULL;
+ }
+ DeltaArray->CountReturned = 0;
+ *SyncContext = 0;
+
+ NlPrint((NL_CRITICAL,
+ "NlSyncSamDatabase: returning unsuccessful (%lx).\n",
+ Status));
+
+ }
+
+
+ return Status;
+
+}
+
+
+NTSTATUS
+NlSyncLsaDatabase(
+ IN PSERVER_SESSION ServerSession,
+ IN OUT PULONG SyncContext,
+ IN OUT PNETLOGON_DELTA_ENUM_ARRAY DeltaArray,
+ IN DWORD PreferredMaximumLength,
+ IN PSESSION_INFO SessionInfo
+ )
+/*++
+
+Routine Description:
+
+ This function is a real worker for the NetrDatabaseSync function and
+ retrieves the LSA database in the delta buffer.
+
+ This function uses the find-first find-next model to return portions
+ of the SAM database at a time. The SAM database is returned as a
+ list of deltas like those returned from I_NetDatabaseDeltas. The
+ following deltas are returned for each domain:
+
+ * One AddOrChangeLsaPolicy delta, followed by,
+
+ * One AddOrChangeLsaAccounts delta for each lsa account, followed by,
+
+ * One AddOrChangeLsaTDomain delta for each trusted domain, followed by,
+
+ * One AddOrChangeLsaSecret delta for each lsa secret.
+
+
+Arguments:
+
+ ServerSession -- pointer to connection context.
+
+ SyncContext -- Specifies context needed to continue the
+ operation. The caller should treat this as an opaque
+ value. The value should be zero before the first call.
+
+ DeltaArray -- Pointer to a buffer where the information
+ is placed. The information returned is an array of
+ NETLOGON_DELTA_ENUM structures.
+
+ PreferredMaximumLength - Preferred maximum length of returned
+ data (in 8-bit bytes). This is not a hard upper limit, but
+ serves as a guide to the server. Due to data conversion
+ between systems with different natural data sizes, the actual
+ amount of data returned may be greater than this value.
+
+ SessionInfo - Information shared between PDC and BDC.
+
+Return Value:
+
+ STATUS_SUCCESS -- The function completed successfully.
+
+ STATUS_MORE_ENTRIES -- The replicant should call again to get more
+ data.
+
+--*/
+{
+ NTSTATUS Status;
+
+ PLSA_SYNC_CONTEXT LsaDBContext;
+
+ PDB_INFO DBInfo;
+
+ DWORD BufferConsumed = 0;
+ DWORD BufferSize;
+ BOOL IgnoreDeltaObject = FALSE;
+
+ DBInfo = &NlGlobalDBInfoArray[LSA_DB];
+
+
+ //
+ // Allocate memory for delta buffer.
+ //
+
+ DeltaArray->Deltas = (PNETLOGON_DELTA_ENUM) MIDL_user_allocate(
+ MAX_DELTA_COUNT * sizeof(NETLOGON_DELTA_ENUM) );
+
+ if( DeltaArray->Deltas == NULL ) {
+
+ NlPrint((NL_CRITICAL,
+ "NlSyncLsaDatabase: Can't allocate %d bytes\n",
+ MAX_DELTA_COUNT * sizeof(NETLOGON_DELTA_ENUM) ));
+
+ return( STATUS_NO_MEMORY );
+ }
+
+
+ //
+ // wipe off the buffer so that cleanup will not be in fault.
+ //
+
+ RtlZeroMemory( DeltaArray->Deltas,
+ MAX_DELTA_COUNT * sizeof(NETLOGON_DELTA_ENUM) );
+
+ //
+ // If this is the first call, allocate and initialize the sync context.
+ //
+
+ if ( *SyncContext == 0 ) {
+
+ //
+ // If there already is a sync context,
+ // delete it.
+ //
+
+ if ( ServerSession->SsSync != NULL ) {
+ CLEAN_SYNC_CONTEXT( ServerSession->SsSync );
+ }
+ else {
+
+ ServerSession->SsSync = NetpMemoryAllocate( sizeof(SYNC_CONTEXT) );
+ if ( ServerSession->SsSync == NULL ) {
+ Status = STATUS_NO_MEMORY;
+ goto Cleanup;
+ }
+ }
+
+ //
+ // Initialize all the fields in the newly allocated resume handle
+ // to indicate that SAM has never yet been called.
+ //
+
+ INIT_SYNC_CONTEXT( ServerSession->SsSync, LsaDBContextType );
+
+ LsaDBContext = &(ServerSession->SsSync->DBContext.Lsa);
+
+ LsaDBContext->SyncState = AccountState;
+ LsaDBContext->SyncSerial = 1;
+ LsaDBContext->LsaEnumBufferType = EmptyEnumBuffer;
+
+
+ NlPrint((NL_SYNC,
+ "NlSyncLsaDatabase: "
+ "Starting full sync, packing lsa account records\n"));
+
+ //
+ // Put the description of the Policy at the front of the buffer for the
+ // first call.
+ //
+
+ Status = NlPackLsaPolicy(
+ &((DeltaArray->Deltas)[DeltaArray->CountReturned]),
+ DBInfo,
+ &BufferSize );
+
+ (DeltaArray->CountReturned)++;
+ BufferConsumed += BufferSize;
+
+ if ( !NT_SUCCESS(Status) ) {
+ goto Cleanup;
+ }
+
+ } else {
+
+ if( ServerSession->SsSync == NULL ) {
+
+ Status = STATUS_SYNCHRONIZATION_REQUIRED;
+ goto Cleanup;
+ }
+
+ NlAssert( ServerSession->SsSync->DBContextType ==
+ LsaDBContextType);
+
+ if( ServerSession->SsSync->DBContextType !=
+ LsaDBContextType) {
+
+ Status = STATUS_SYNCHRONIZATION_REQUIRED;
+ goto Cleanup;
+ }
+
+ LsaDBContext = &(ServerSession->SsSync->DBContext.Lsa);
+
+ NlAssert( LsaDBContext->SyncSerial == *SyncContext );
+
+ if( LsaDBContext->SyncSerial != *SyncContext ) {
+
+ Status = STATUS_SYNCHRONIZATION_REQUIRED;
+ goto Cleanup;
+ }
+
+ LsaDBContext->SyncSerial++;
+ }
+
+ //
+ // Loop for each entry placed in the output buffer
+ //
+ // Each iteration of the loop below puts one more entry into the array
+ // returned to the caller. The algorithm is split into 2 parts.
+ // The first part checks to see if we need to retrieve more information
+ // from LSA and gets the description of several accounts, TDomain or
+ // Secret from LSA in a single call. The second part puts a single
+ // entry into the buffer returned to the caller.
+ //
+
+ while ( LsaDBContext->SyncState != LsaDoneState ) {
+
+ //
+ // If we've filled out pre-allocated array,
+ // return now.
+ //
+ if ( DeltaArray->CountReturned + MAX_DELTAS_PER_CHANGELOG > MAX_DELTA_COUNT ) {
+ Status = STATUS_MORE_ENTRIES;
+ goto Cleanup;
+ }
+
+ //
+ // Get more information from LSA
+ //
+ // Handle when we've not yet called LSA or we've already consumed
+ // all of the information returned on a previous call to SAM.
+ //
+ // This is a 'while' rather than an 'if' to handle the case
+ // where LSA returns zero entries.
+ //
+
+ while ( LsaDBContext->Index >= LsaDBContext->Count ) {
+
+ //
+ // Free any previous buffer returned from SAM.
+ //
+
+ if ( ServerSession->SsSync != NULL ) {
+ CLEAN_SYNC_CONTEXT( ServerSession->SsSync );
+ }
+
+
+ //
+ // If we've already gotten everything from LSA,
+ // we've finished all of the accounts,
+ //
+ // If we've just done the accounts,
+ // go on to do the TDomains.
+ //
+ // If we've just done the TDomains,
+ // go on to do the Secrets
+ //
+ // If we've just done the Secret,
+ // we're all done.
+ //
+
+ if ( LsaDBContext->LsaAllDone ) {
+
+ LsaDBContext->LsaEnumHandle = 0;
+ LsaDBContext->Index = 0;
+ LsaDBContext->Count = 0;
+ LsaDBContext->LsaAllDone = FALSE;
+
+ if (LsaDBContext->SyncState == AccountState ) {
+
+
+ NlPrint((NL_SYNC,
+ "NlSyncLsaDatabase: "
+ " packing TDomain records.\n"));
+
+ LsaDBContext->SyncState = TDomainState;
+ } else if (LsaDBContext->SyncState == TDomainState ) {
+
+ NlPrint((NL_SYNC,
+ "NlSyncLsaDatabase: packing secret records.\n"));
+
+ LsaDBContext->SyncState = SecretState;
+ } else if (LsaDBContext->SyncState == SecretState ) {
+
+
+ NlPrint((NL_SYNC,
+ "NlSyncLsaDatabase: packing done.\n"));
+
+ LsaDBContext->SyncState = LsaDoneState;
+ LsaDBContext->LsaEnumBufferType = EmptyEnumBuffer;
+ Status = STATUS_SUCCESS;
+ }
+
+ break;
+ }
+
+ if (LsaDBContext->SyncState == AccountState ) {
+
+ LsaDBContext->LsaEnumBufferType = AccountEnumBuffer;
+
+ Status = LsarEnumerateAccounts(
+ DBInfo->DBHandle,
+ &LsaDBContext->LsaEnumHandle,
+ &LsaDBContext->LsaEnum.Account,
+ SAM_SYNC_PREF_MAX);
+
+ if (Status == STATUS_SUCCESS || Status == STATUS_MORE_ENTRIES ) {
+ LsaDBContext->Count =
+ LsaDBContext->LsaEnum.Account.EntriesRead;
+ }
+
+ } else if (LsaDBContext->SyncState == TDomainState ) {
+
+ LsaDBContext->LsaEnumBufferType = TDomainEnumBuffer;
+
+ Status = LsarEnumerateTrustedDomains(
+ DBInfo->DBHandle,
+ &LsaDBContext->LsaEnumHandle,
+ &LsaDBContext->LsaEnum.TDomain,
+ SAM_SYNC_PREF_MAX);
+
+ if (Status == STATUS_SUCCESS || Status == STATUS_MORE_ENTRIES ) {
+ LsaDBContext->Count =
+ LsaDBContext->LsaEnum.TDomain.EntriesRead;
+ }
+
+ } else if (LsaDBContext->SyncState == SecretState ) {
+
+ LsaDBContext->LsaEnumBufferType = SecretEnumBuffer;
+
+ Status = LsaIEnumerateSecrets(
+ DBInfo->DBHandle,
+ &LsaDBContext->LsaEnumHandle,
+ &LsaDBContext->LsaEnum.Secret,
+ SAM_SYNC_PREF_MAX,
+ &LsaDBContext->Count );
+
+ }
+
+ //
+ // If LSA says there is more information,
+ // just ensure he returned something to us on this call.
+ //
+
+ if ( Status == STATUS_SUCCESS || Status == STATUS_MORE_ENTRIES ) {
+ NlAssert( LsaDBContext->Count != 0 );
+
+ //
+ // If LSA says he's returned all of the information,
+ // remember not to ask it for more.
+ //
+
+ } else if ( Status == STATUS_NO_MORE_ENTRIES ) {
+ LsaDBContext->LsaAllDone = TRUE;
+ LsaDBContext->Count = 0;
+
+ //
+ // Any other error is fatal
+ //
+
+ } else {
+
+ LsaDBContext->LsaEnumBufferType = EmptyEnumBuffer;
+ LsaDBContext->Count = 0;
+ goto Cleanup;
+
+ }
+
+ LsaDBContext->Index = 0;
+ }
+
+
+ //
+ // Place this entry into the return buffer.
+ //
+
+ if ( LsaDBContext->Count > 0 ) {
+
+ if (LsaDBContext->SyncState == AccountState ) {
+
+ Status = NlPackLsaAccount(
+ LsaDBContext->LsaEnum.Account.
+ Information[LsaDBContext->Index].Sid,
+ &((DeltaArray->Deltas)[DeltaArray->CountReturned]),
+ DBInfo,
+ &BufferSize,
+ SessionInfo );
+
+ } else if (LsaDBContext->SyncState == TDomainState ) {
+
+ Status = NlPackLsaTDomain(
+ LsaDBContext->LsaEnum.TDomain.
+ Information[LsaDBContext->Index].Sid,
+ &((DeltaArray->Deltas)[DeltaArray->CountReturned]),
+ DBInfo,
+ &BufferSize );
+
+ } else if (LsaDBContext->SyncState == SecretState ) {
+
+ PUNICODE_STRING SecretName;
+
+ SecretName =
+ &((PUNICODE_STRING)LsaDBContext->LsaEnum.Secret)
+ [LsaDBContext->Index];
+
+ //
+ // ignore local secret objects.
+ //
+
+ if( (SecretName->Length / sizeof(WCHAR) >
+ LSA_GLOBAL_SECRET_PREFIX_LENGTH ) &&
+ (_wcsnicmp( SecretName->Buffer,
+ LSA_GLOBAL_SECRET_PREFIX,
+ LSA_GLOBAL_SECRET_PREFIX_LENGTH ) == 0)) {
+
+ Status = NlPackLsaSecret(
+ SecretName,
+ &((DeltaArray->Deltas)[DeltaArray->CountReturned]),
+ DBInfo,
+ &BufferSize,
+ SessionInfo );
+
+ } else {
+ Status = STATUS_SUCCESS;
+ IgnoreDeltaObject = TRUE;
+ BufferSize = 0;
+ }
+
+ }
+
+ //
+ // If there was a real error or this group didn't fit,
+ // return to the caller.
+ //
+
+ if ( Status != STATUS_SUCCESS ) {
+ goto Cleanup;
+ }
+
+ LsaDBContext->Index ++;
+
+ //
+ // if this object is ignored, don't modify return values.
+ //
+
+ if ( !IgnoreDeltaObject ) {
+
+ (DeltaArray->CountReturned)++;
+ BufferConsumed +=
+ (sizeof(NETLOGON_DELTA_ENUM) + BufferSize);
+
+ if( BufferConsumed >= PreferredMaximumLength) {
+ Status = STATUS_MORE_ENTRIES;
+ goto Cleanup;
+ }
+
+ //
+ // If we're debugging replication, return only one change to the caller.
+ //
+#if DBG
+ if ( NlGlobalTrace & NL_ONECHANGE_REPL ) {
+ Status = STATUS_MORE_ENTRIES;
+ goto Cleanup;
+ }
+
+#endif // DBG
+ } else {
+ IgnoreDeltaObject = FALSE;
+ }
+ }
+ }
+
+Cleanup:
+
+ //
+ // Set the return parameters to the proper values.
+ //
+
+ if ( NT_SUCCESS( Status ) ) {
+ *SyncContext = LsaDBContext->SyncSerial;
+
+ } else {
+ if ( DeltaArray->Deltas != NULL ) {
+ NlFreeDBDeltaArray( DeltaArray->Deltas, DeltaArray->CountReturned );
+ DeltaArray->Deltas = NULL;
+ }
+ DeltaArray->CountReturned = 0;
+ *SyncContext = 0;
+ }
+
+ if (!NT_SUCCESS(Status)) {
+
+ NlPrint((NL_CRITICAL,
+ "NlSyncLsaDatabase: returning unsuccessful (%lx).\n",
+ Status));
+ }
+
+
+ return Status;
+
+}
+
+
+NTSTATUS
+NetrDatabaseSync (
+ IN LPWSTR PrimaryName,
+ IN LPWSTR ComputerName,
+ IN PNETLOGON_AUTHENTICATOR Authenticator,
+ OUT PNETLOGON_AUTHENTICATOR ReturnAuthenticator,
+ IN DWORD DatabaseID,
+ IN OUT PULONG SyncContext,
+ OUT PNETLOGON_DELTA_ENUM_ARRAY *DeltaArrayRet,
+ IN DWORD PreferredMaximumLength
+ )
+/*++
+
+Routine Description:
+
+ NT 1.0 version of NetrDatabaseSync2. Don't pass the RestartState parameter.
+ Sync Context is all that is needed to identify the state.
+
+Arguments:
+
+ Same as NetrDatabaseSync2 (with the exception mentioned above).
+
+Return Value:
+
+ Save as NetrDatabaseSync2.
+
+--*/
+{
+ return NetrDatabaseSync2(
+ PrimaryName,
+ ComputerName,
+ Authenticator,
+ ReturnAuthenticator,
+ DatabaseID,
+ NormalState,
+ SyncContext,
+ DeltaArrayRet,
+ PreferredMaximumLength );
+
+}
+
+
+NTSTATUS
+NetrDatabaseSync2 (
+ IN LPWSTR PrimaryName,
+ IN LPWSTR ComputerName,
+ IN PNETLOGON_AUTHENTICATOR Authenticator,
+ OUT PNETLOGON_AUTHENTICATOR ReturnAuthenticator,
+ IN DWORD DatabaseID,
+ IN SYNC_STATE RestartState,
+ IN OUT PULONG SyncContext,
+ OUT PNETLOGON_DELTA_ENUM_ARRAY *DeltaArrayRet,
+ IN DWORD PreferredMaximumLength
+ )
+/*++
+
+Routine Description:
+
+ This function is used by a BDC server to request
+ the entire SAM/LSA database from a PDC in NTLANMAN-style format.
+ This function can only be called by a server which has previously
+ authenticated with the PDC by calling I_NetServerAuthenticate. This
+ function uses RPC to contact the Netlogon service on the PDC.
+
+Arguments:
+
+ PrimaryName -- Name of the PDC to retrieve the deltas from.
+
+ ComputerName -- Name of the BDC or member server making the call.
+
+ Authenticator -- supplied by the server.
+
+ ReturnAuthenticator -- Receives an authenticator returned by the PDC.
+
+ DatabaseID -- Identifies the databse for which the deltas are requested.
+ For SAM database the ID is 0, for Builtin Domain the ID is 1. Other
+ databases may be defined later.
+
+ RestartState -- Specifies whether this is a restart of the full sync and how
+ to interpret SyncContext. This value should be NormalState unless this
+ is the restart of a full sync.
+
+ However, if the caller is continuing a full sync after a reboot,
+ the following values are used:
+
+ GroupState - SyncContext is the global group rid to continue with.
+ UserState - SyncContext is the user rid to continue with
+ GroupMemberState - SyncContext is the global group rid to continue with
+ AliasState - SyncContext should be zero to restart at first alias
+ AliasMemberState - SyncContext should be zero to restart at first alias
+
+ One cannot continue the LSA database in this way.
+
+ SyncContext -- Specifies context needed to continue the
+ operation. The caller should treat this as an opaque
+ value. The value should be zero before the first call.
+
+ DeltaArray -- Receives a pointer to a buffer where the information
+ is placed. The information returned is an array of
+ NETLOGON_DELTA_ENUM structures.
+
+ PreferredMaximumLength - Preferred maximum length of returned
+ data (in 8-bit bytes). This is not a hard upper limit, but
+ serves as a guide to the server. Due to data conversion
+ between systems with different natural data sizes, the actual
+ amount of data returned may be greater than this value.
+
+Return Value:
+
+ STATUS_SUCCESS -- The function completed successfully.
+
+ STATUS_MORE_ENTRIES -- The replicant should call again to get more
+ data.
+
+ STATUS_ACCESS_DENIED -- The replicant should re-authenticate with
+ the PDC.
+
+
+--*/
+{
+ NTSTATUS Status;
+
+ PSERVER_SESSION ServerSession = NULL;
+ PNETLOGON_DELTA_ENUM_ARRAY DeltaArray;
+
+ SESSION_INFO SessionInfo;
+ PDB_INFO DBInfo;
+
+ DEFSSIAPITIMER;
+
+ INITSSIAPITIMER;
+ STARTSSIAPITIMER;
+
+ //
+ // This API is not supported on workstations.
+ //
+
+ if ( NlGlobalRole == RoleMemberWorkstation ) {
+ return STATUS_NOT_SUPPORTED;
+ }
+
+ if ( DatabaseID >= NUM_DBS ) {
+ return STATUS_INVALID_LEVEL;
+ }
+
+ DBInfo = &NlGlobalDBInfoArray[DatabaseID];
+
+ //
+ // Initialization
+ //
+
+ *DeltaArrayRet = DeltaArray = (PNETLOGON_DELTA_ENUM_ARRAY)
+ MIDL_user_allocate( sizeof(NETLOGON_DELTA_ENUM_ARRAY) );
+
+ if( DeltaArray == NULL ) {
+ return(STATUS_NO_MEMORY);
+ }
+
+ DeltaArray->Deltas = NULL;
+ DeltaArray->CountReturned = 0;
+
+
+ //
+ // Check the primary name.
+ //
+
+ Status = NlVerifyWorkstation( PrimaryName );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto Cleanup;
+ }
+
+
+ NlPrint((NL_SYNC,
+ "NetrDatabaseSync: " FORMAT_LPWSTR " full sync called by " FORMAT_LPWSTR " State: %ld Context: 0x%lx.\n",
+ DBInfo->DBName,
+ ComputerName,
+ RestartState,
+ *SyncContext ));
+
+
+ //
+ // Retrieve the requestor's entry to get sessionkey
+ //
+
+ LOCK_SERVER_SESSION_TABLE();
+ ServerSession = NlFindNamedServerSession( ComputerName );
+
+ if (ServerSession == NULL) {
+ UNLOCK_SERVER_SESSION_TABLE();
+ Status = STATUS_ACCESS_DENIED;
+ // Don't log this event since it happens in nature after a reboot
+ // or after we scavenge the server session.
+ goto CleanupNoEventLog;
+ }
+
+ //
+ // Allow this call only on ServerSecureChannel.
+ //
+
+ if( ServerSession->SsSecureChannelType != ServerSecureChannel ) {
+ UNLOCK_SERVER_SESSION_TABLE();
+ Status = STATUS_ACCESS_DENIED;
+ ServerSession = NULL;
+ goto Cleanup;
+ }
+
+ //
+ // Verify the Authenticator and update seed if OK
+ //
+
+ Status = NlCheckAuthenticator( ServerSession,
+ Authenticator,
+ ReturnAuthenticator);
+
+ if ( !NT_SUCCESS(Status) ) {
+ UNLOCK_SERVER_SESSION_TABLE();
+
+ NlPrint((NL_CRITICAL,
+ "NetrDatabaseSync: authentication failed.\n" ));
+
+ ServerSession = NULL;
+ goto Cleanup;
+ }
+
+
+ //
+ // Prevent entry from being deleted, but drop the global lock.
+ //
+ // Beware of server with two concurrent calls outstanding
+ // (must have rebooted.)
+ //
+
+ if (ServerSession->SsFlags & SS_LOCKED ) {
+ UNLOCK_SERVER_SESSION_TABLE();
+
+ NlPrint((NL_CRITICAL, "NetrDatabaseSync: Concurrent call detected.\n" ));
+
+ Status = STATUS_ACCESS_DENIED;
+ ServerSession = NULL;
+ goto Cleanup;
+ }
+ ServerSession->SsFlags |= SS_LOCKED;
+
+
+ SessionInfo.SessionKey = ServerSession->SsSessionKey;
+ SessionInfo.NegotiatedFlags = ServerSession->SsNegotiatedFlags;
+
+ UNLOCK_SERVER_SESSION_TABLE();
+
+ if( DatabaseID == LSA_DB ) {
+
+ NlAssert( RestartState == NormalState );
+
+ Status = NlSyncLsaDatabase( ServerSession,
+ SyncContext,
+ DeltaArray,
+ PreferredMaximumLength,
+ &SessionInfo );
+ } else {
+
+ Status = NlSyncSamDatabase( ServerSession,
+ DatabaseID,
+ RestartState,
+ SyncContext,
+ DeltaArray,
+ PreferredMaximumLength,
+ &SessionInfo );
+
+ }
+
+Cleanup:
+
+ //
+ // write event log
+ //
+
+ if ( !NT_SUCCESS( Status ) ) {
+
+ LPWSTR MsgStrings[2];
+
+ MsgStrings[0] = ComputerName;
+ MsgStrings[1] = (LPWSTR) Status;
+
+ NlpWriteEventlog(
+ NELOG_NetlogonFullSyncCallFailed,
+ EVENTLOG_WARNING_TYPE,
+ (LPBYTE)&Status,
+ sizeof(Status),
+ MsgStrings,
+ 2 | LAST_MESSAGE_IS_NTSTATUS );
+
+ }
+ else {
+
+ LPWSTR MsgStrings[2];
+ WCHAR CountBuffer[20]; // random size
+
+ MsgStrings[0] = ComputerName;
+
+ ultow( DeltaArray->CountReturned, CountBuffer, 10);
+ MsgStrings[1] = CountBuffer;
+
+ NlpWriteEventlog(
+ NELOG_NetlogonFullSyncCallSuccess,
+ EVENTLOG_INFORMATION_TYPE,
+ NULL,
+ 0,
+ MsgStrings,
+ 2 );
+
+ }
+
+ //
+ // Unlock the server session entry if we've locked it.
+ //
+CleanupNoEventLog:
+
+ if ( ServerSession != NULL ) {
+
+ //
+ // If we're done, free up the context structure,
+ //
+
+ if ( Status != STATUS_MORE_ENTRIES && ServerSession->SsSync != NULL ) {
+ CLEAN_SYNC_CONTEXT( ServerSession->SsSync );
+
+ NetpMemoryFree( ServerSession->SsSync );
+ ServerSession->SsSync = NULL;
+ }
+
+ //
+ // If we are successfully returning these deltas to the BDC,
+ // update our tables to reflect the changes.
+ //
+
+ if ( Status == STATUS_SUCCESS ) {
+ NlPrimaryAnnouncementFinish( ServerSession,
+ DatabaseID,
+ NULL );
+
+ }
+
+ NlUnlockServerSession( ServerSession );
+ }
+
+
+ NlPrint((NL_SYNC,
+ "NetrDatabaseSync: " FORMAT_LPWSTR " returning (0x%lx) to " FORMAT_LPWSTR " Context: 0x%lx.\n",
+ DBInfo->DBName,
+ Status,
+ ComputerName,
+ *SyncContext ));
+
+ STOPSSIAPITIMER;
+
+ NlPrint((NL_REPL_TIME,"NetrDatabaseSync Time:\n"));
+ PRINTSSIAPITIMER;
+
+ return Status;
+
+}
+
+
+NTSTATUS
+NetrDatabaseRedo(
+ IN LPWSTR PrimaryName,
+ IN LPWSTR ComputerName,
+ IN PNETLOGON_AUTHENTICATOR Authenticator,
+ OUT PNETLOGON_AUTHENTICATOR ReturnAuthenticator,
+ IN LPBYTE OrigChangeLogEntry,
+ IN DWORD ChangeLogEntrySize,
+ OUT PNETLOGON_DELTA_ENUM_ARRAY *DeltaArrayRet
+ )
+/*++
+
+Routine Description:
+
+ This function is used by a SAM BDC to request infomation about a single
+ account. This function can only be called by a server which has previously
+ authenticated with the PDC by calling I_NetServerAuthenticate. This
+ function uses RPC to contact the Netlogon service on the PDC.
+
+Arguments:
+
+ PrimaryName -- Name of the PDC to retrieve the delta from.
+
+ ComputerName -- Name of the BDC making the call.
+
+ Authenticator -- supplied by the server.
+
+ ReturnAuthenticator -- Receives an authenticator returned by the PDC.
+
+ ChangeLogEntry -- A description of the account to be queried.
+
+ ChangeLogEntrySize -- Size (in bytes) of the ChangeLogEntry.
+
+ DeltaArrayRet -- Receives a pointer to a buffer where the information is
+ placed. The information returned is an array of
+ NETLOGON_DELTA_ENUM structures.
+
+Return Value:
+
+ STATUS_SUCCESS -- The function completed successfully.
+
+ STATUS_ACCESS_DENIED -- The replicant should re-authenticate with
+ the PDC.
+
+--*/
+{
+ PCHANGELOG_ENTRY ChangeLogEntry;
+
+ NTSTATUS Status;
+ PSERVER_SESSION ServerSession = NULL;
+
+ LPWSTR MsgStrings[2];
+
+ DWORD BufferSize;
+
+ PNETLOGON_DELTA_ENUM_ARRAY DeltaArray;
+ SESSION_INFO SessionInfo;
+
+ DEFSSIAPITIMER;
+
+ INITSSIAPITIMER;
+ STARTSSIAPITIMER;
+
+ //
+ // This API is not supported on workstations.
+ //
+
+ if ( NlGlobalRole == RoleMemberWorkstation ) {
+ return STATUS_NOT_SUPPORTED;
+ }
+
+ //
+ // Initialization
+ //
+
+ ChangeLogEntry = (PCHANGELOG_ENTRY) OrigChangeLogEntry;
+ if ( !NlValidateChangeLogEntry( ChangeLogEntry, ChangeLogEntrySize ) ||
+ ChangeLogEntry->DBIndex >= NUM_DBS ) {
+ Status = STATUS_INVALID_PARAMETER;
+ goto Cleanup;
+ }
+
+
+ NlPrint((NL_SYNC,
+ "NetrDatabaseRedo: " FORMAT_LPWSTR " redo sync called by " FORMAT_LPWSTR
+ " with this change log entry:\n",
+ NlGlobalDBInfoArray[ChangeLogEntry->DBIndex].DBName,
+ ComputerName ));
+
+#if DBG
+ PrintChangeLogEntry( ChangeLogEntry );
+#endif // DBG
+
+ //
+ // The change log entry really represents an object and not an operation.
+ // Therefore, convert the delta type from whatever was passed to an
+ // "AddOrChange" operation. Then NlPackSingleDelta will return everything
+ // we know about the object.
+ //
+
+ ChangeLogEntry->DeltaType = NlGlobalAddDeltaType[ChangeLogEntry->DeltaType];
+
+ *DeltaArrayRet = DeltaArray = (PNETLOGON_DELTA_ENUM_ARRAY)
+ MIDL_user_allocate( sizeof(NETLOGON_DELTA_ENUM_ARRAY) );
+
+ if( DeltaArray == NULL ) {
+ return STATUS_NO_MEMORY;
+ }
+
+ DeltaArray->CountReturned = 0;
+ DeltaArray->Deltas = NULL;
+ SessionInfo.NegotiatedFlags = 0;
+
+
+
+ //
+ // Check the primary name.
+ //
+
+ Status = NlVerifyWorkstation( PrimaryName );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto Cleanup;
+ }
+
+ //
+ // Retrieve the requestor's entry to get sessionkey
+ //
+
+ LOCK_SERVER_SESSION_TABLE();
+ ServerSession = NlFindNamedServerSession( ComputerName );
+
+ if (ServerSession == NULL) {
+ UNLOCK_SERVER_SESSION_TABLE();
+ Status = STATUS_ACCESS_DENIED;
+ // Don't log this event since it happens in nature after a reboot
+ // or after we scavenge the server session.
+ goto CleanupNoEventlog;
+ }
+
+ //
+ // Allow this call only on ServerSecureChannel.
+ //
+
+ if( ServerSession->SsSecureChannelType != ServerSecureChannel ) {
+ UNLOCK_SERVER_SESSION_TABLE();
+ Status = STATUS_ACCESS_DENIED;
+ ServerSession = NULL;
+ goto Cleanup;
+ }
+
+ //
+ // Verify the Authenticator and update seed if OK
+ //
+
+ Status = NlCheckAuthenticator( ServerSession,
+ Authenticator,
+ ReturnAuthenticator);
+
+ if ( !NT_SUCCESS(Status) ) {
+ UNLOCK_SERVER_SESSION_TABLE();
+
+ NlPrint((NL_CRITICAL, "NetrDatabaseRedo: authentication failed.\n" ));
+
+ ServerSession = NULL;
+ goto Cleanup;
+ }
+
+
+ //
+ // Prevent entry from being deleted, but drop the global lock.
+ //
+ // Beware of server with two concurrent calls outstanding
+ // (must have rebooted.)
+ //
+
+ if (ServerSession->SsFlags & SS_LOCKED ) {
+ UNLOCK_SERVER_SESSION_TABLE();
+
+ NlPrint((NL_CRITICAL, "NetrDatabaseRedo: Concurrent call detected.\n" ));
+
+ Status = STATUS_ACCESS_DENIED;
+ ServerSession = NULL;
+ goto Cleanup;
+ }
+ ServerSession->SsFlags |= SS_LOCKED;
+
+ SessionInfo.SessionKey = ServerSession->SsSessionKey;
+ SessionInfo.NegotiatedFlags = ServerSession->SsNegotiatedFlags;
+
+ UNLOCK_SERVER_SESSION_TABLE();
+
+
+ //
+ // Allocate memory for delta buffer.
+ //
+
+ DeltaArray->Deltas = (PNETLOGON_DELTA_ENUM) MIDL_user_allocate(
+ MAX_DELTAS_PER_CHANGELOG * sizeof(NETLOGON_DELTA_ENUM) );
+
+ if( DeltaArray->Deltas == NULL ) {
+ Status = STATUS_NO_MEMORY;
+ goto Cleanup;
+ }
+
+
+ //
+ // wipe off the buffer so that cleanup will not be in fault.
+ //
+
+ RtlZeroMemory( DeltaArray->Deltas,
+ MAX_DELTAS_PER_CHANGELOG * sizeof(NETLOGON_DELTA_ENUM) );
+
+
+ //
+ // Put the data for the changelog entry into the user's buffer.
+ //
+
+ Status = NlPackSingleDelta( ChangeLogEntry,
+ DeltaArray,
+ &BufferSize,
+ &SessionInfo,
+ FALSE );
+
+
+ //
+ // If the only problem is that the object no longer exists,
+ // return a delta asking the BDC to delete the object.
+ //
+
+ if ( !NT_SUCCESS(Status) &&
+ IsObjectNotFoundStatus( ChangeLogEntry->DeltaType, Status ) ) {
+
+
+ NlPrint((NL_SYNC,
+ "NetrDatabaseRedo: " FORMAT_LPWSTR " object no longer exists (0x%lx) "
+ FORMAT_LPWSTR "\n",
+ NlGlobalDBInfoArray[ChangeLogEntry->DBIndex].DBName,
+ Status,
+ ComputerName ));
+
+ //
+ // Convert the change log entry into an appropriate delete delta type and
+ // try again.
+ //
+
+ ChangeLogEntry->DeltaType = NlGlobalDeleteDeltaType[ChangeLogEntry->DeltaType];
+
+ Status = NlPackSingleDelta( ChangeLogEntry,
+ DeltaArray,
+ &BufferSize,
+ &SessionInfo,
+ FALSE );
+
+ }
+
+Cleanup:
+
+ //
+ // write event log
+ //
+
+ if ( !NT_SUCCESS( Status ) ) {
+
+ MsgStrings[0] = ComputerName;
+ MsgStrings[1] = (LPWSTR) Status;
+
+ NlpWriteEventlog(
+ NELOG_NetlogonPartialSyncCallFailed,
+ EVENTLOG_WARNING_TYPE,
+ (LPBYTE)&Status,
+ sizeof(Status),
+ MsgStrings,
+ 2 | LAST_MESSAGE_IS_NTSTATUS );
+
+ } else {
+
+ //
+ // Log the successful replication only if deltas have been returned
+ // to the caller.
+ //
+ if ( DeltaArray->CountReturned != 0 ) {
+ LPWSTR MsgStrings[2];
+ WCHAR CountBuffer[20]; // random size
+
+ MsgStrings[0] = ComputerName;
+
+ ultow( DeltaArray->CountReturned, CountBuffer, 10);
+ MsgStrings[1] = CountBuffer;
+
+ NlpWriteEventlog(
+ NELOG_NetlogonPartialSyncCallSuccess,
+ EVENTLOG_INFORMATION_TYPE,
+ NULL,
+ 0,
+ MsgStrings,
+ 2 );
+ }
+
+ }
+
+
+ //
+ // Free up locally allocated resources.
+ //
+
+CleanupNoEventlog:
+
+ //
+ // If we weren't successful,
+ // Don't return any deltas.
+ //
+
+ if ( !NT_SUCCESS(Status)) {
+ if ( DeltaArray->Deltas != NULL ) {
+ NlFreeDBDeltaArray( DeltaArray->Deltas, DeltaArray->CountReturned );
+ DeltaArray->Deltas = NULL;
+ }
+ DeltaArray->CountReturned = 0;
+ }
+
+ //
+ // Unlock the server session entry if we've locked it.
+ //
+
+ if ( ServerSession != NULL ) {
+ NlUnlockServerSession( ServerSession );
+ }
+
+
+ NlPrint((NL_SYNC,
+ "NetrDatabaseRedo: " FORMAT_LPWSTR " returning (0x%lx) to "
+ FORMAT_LPWSTR "\n",
+ NlGlobalDBInfoArray[ChangeLogEntry->DBIndex].DBName,
+ Status,
+ ComputerName ));
+
+ STOPSSIAPITIMER;
+
+ NlPrint((NL_REPL_TIME,"NetrDatabaseRedo Time:\n"));
+ PRINTSSIAPITIMER;
+
+ return Status;
+
+}
+
+
+
+NTSTATUS
+NetrAccountDeltas (
+ IN LPWSTR PrimaryName,
+ IN LPWSTR ComputerName,
+ IN PNETLOGON_AUTHENTICATOR Authenticator,
+ OUT PNETLOGON_AUTHENTICATOR ReturnAuthenticator,
+ IN PUAS_INFO_0 RecordId,
+ IN DWORD Count,
+ IN DWORD Level,
+ OUT LPBYTE Buffer,
+ IN DWORD BufferSize,
+ OUT PULONG CountReturned,
+ OUT PULONG TotalEntries,
+ OUT PUAS_INFO_0 NextRecordId
+ )
+/*++
+
+Routine Description:
+
+ This function is used by a UAS BDC or UAS member server to request
+ UAS-style account change information. This function can only be
+ called by a server which has previously authenticated with the PDC by
+ calling I_NetServerAuthenticate.
+
+ This function is only called by the XACT server upon receipt of a
+ I_NetAccountDeltas XACT SMB from a UAS BDC or a UAS member server.
+ As such, many of the parameters are opaque since the XACT server
+ doesn't need to interpret any of that data. This function uses RPC
+ to contact the Netlogon service.
+
+ The LanMan 3.0 SSI Functional Specification describes the operation
+ of this function.
+
+Arguments:
+
+ PrimaryName -- Must be NULL to indicate this call is a local call
+ being made on behalf of a UAS server by the XACT server.
+
+ ComputerName -- Name of the BDC or member making the call.
+
+ Authenticator -- supplied by the server.
+
+ ReturnAuthenticator -- Receives an authenticator returned by the PDC.
+
+ RecordId -- Supplies an opaque buffer indicating the last record
+ received from a previous call to this function.
+
+ Count -- Supplies the number of Delta records requested.
+
+ Level -- Reserved. Must be zero.
+
+ Buffer -- Returns opaque data representing the information to be
+ returned.
+
+ BufferSize -- Size of buffer in bytes.
+
+ CountReturned -- Returns the number of records returned in buffer.
+
+ TotalEntries -- Returns the total number of records available.
+
+ NextRecordId -- Returns an opaque buffer identifying the last
+ record received by this function.
+
+
+Return Value:
+
+ NT status code.
+
+--*/
+{
+ NTSTATUS Status;
+ PSERVER_SESSION ServerSession = NULL;
+ PCHANGELOG_ENTRY ChangeLogEntry = NULL;
+
+ BOOL ChangelogLocked = FALSE;
+ BUFFER_DESCRIPTOR BufferDescriptor;
+
+ PDB_INFO DBInfo = &NlGlobalDBInfoArray[SAM_DB];
+ NETLOGON_SESSION_KEY SessionKey;
+
+ DWORD DummyFlag;
+ LONG RotateCount;
+ BOOL RotateCountComputed = FALSE;
+
+ //
+ // This API is not supported on workstations.
+ //
+
+ if ( NlGlobalRole == RoleMemberWorkstation ) {
+ return STATUS_NOT_SUPPORTED;
+ }
+
+
+ //
+ // If CompatibilityMode is off,
+ // disallow this function for downlevel servers.
+ //
+
+ if ( !NlGlobalUasCompatibilityMode ) {
+ Status = STATUS_ACCESS_DENIED;
+ goto Cleanup;
+ }
+
+ //
+ // Initialization
+ //
+
+ *CountReturned = 0;
+ *TotalEntries = 0;
+ *NextRecordId = *RecordId;
+
+ //
+ // Check the primary name.
+ //
+
+ Status = NlVerifyWorkstation( PrimaryName );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto Cleanup;
+ }
+
+
+ NlPrint((NL_SYNC,
+ "NetrAccountDeltas: UAS partial sync called by " FORMAT_LPWSTR
+ " with SerialNumber 0x%lx.\n",
+ ComputerName,
+ RecordId->SerialNumber ));
+
+ //
+ // we need to retrieve the requestor's entry to get sessionkey
+ //
+
+ LOCK_SERVER_SESSION_TABLE();
+ ServerSession = NlFindNamedServerSession( ComputerName );
+
+ if (ServerSession == NULL) {
+ UNLOCK_SERVER_SESSION_TABLE();
+ Status = STATUS_ACCESS_DENIED;
+ goto Cleanup;
+ }
+
+ //
+ // allow this call to go through only on UasServerSecureChannel.
+ //
+
+ if( ServerSession->SsSecureChannelType != UasServerSecureChannel ) {
+ UNLOCK_SERVER_SESSION_TABLE();
+ Status = STATUS_ACCESS_DENIED;
+ ServerSession = NULL;
+ goto Cleanup;
+ }
+
+ //
+ // now verify the Authenticator and update seed if OK
+ //
+
+ Status = NlCheckAuthenticator( ServerSession,
+ Authenticator,
+ ReturnAuthenticator);
+
+ if ( !NT_SUCCESS(Status) ) {
+ UNLOCK_SERVER_SESSION_TABLE();
+ ServerSession = NULL;
+ goto Cleanup;
+ }
+
+ SessionKey = ServerSession->SsSessionKey;
+
+ UNLOCK_SERVER_SESSION_TABLE();
+
+
+
+ //
+ // The requestor should have gotten his 'DomainModifiedCount' from
+ // a UasChange record we broadcast. Therefore, It should be less than
+ // or equal to the current DomainModifiedCount as set by SAM. If
+ // not, force a sync.
+ //
+ // A Downlevel machine only has the least significant 32 bits of the
+ // DomainModifiedCount. We'll just compare the least significant portion
+ // knowing that a sync will be forced on wraparound.
+ //
+
+ LOCK_CHANGELOG();
+ ChangelogLocked = TRUE;
+
+ if ( NlGlobalChangeLogDesc.SerialNumber[SAM_DB].LowPart <
+ RecordId->SerialNumber ) {
+ Status = STATUS_SYNCHRONIZATION_REQUIRED;
+ goto Cleanup;
+ }
+
+ if ( NlGlobalChangeLogDesc.SerialNumber[SAM_DB].LowPart ==
+ RecordId->SerialNumber ) {
+ Status = STATUS_SUCCESS;
+ goto Cleanup;
+ }
+
+ *TotalEntries = NlGlobalChangeLogDesc.SerialNumber[SAM_DB].LowPart -
+ RecordId->SerialNumber;
+
+ //
+ // Get a copy pointer to appropriate entry in change_log of primary.
+ // Note that the RecordId contains last record received by client.
+ //
+
+ ChangeLogEntry = NlGetNextDownlevelChangeLogEntry( RecordId->SerialNumber );
+
+ if ( ChangeLogEntry == NULL ) {
+ NlPrint((NL_CRITICAL,
+ "NetrDatabaseDeltas: "
+ "delta not found in cache, returning full required.\n" ));
+
+ Status = STATUS_SYNCHRONIZATION_REQUIRED;
+ goto Cleanup;
+ }
+
+ UNLOCK_CHANGELOG();
+ ChangelogLocked = FALSE;
+
+ //
+ // Build a buffer descriptor describing the buffer the caller passed in.
+ //
+
+ if ( Buffer == NULL || BufferSize == 0 ) {
+
+ Status = STATUS_BUFFER_TOO_SMALL;
+
+ if ( ServerSession->SsFlags & SS_UAS_BUFFER_OVERFLOW ) {
+
+ //
+ // since this client has already overflowed the
+ // databuffer once, he can't handle big size delta.
+ //
+
+ Status = STATUS_SYNCHRONIZATION_REQUIRED;
+ }
+
+ goto Cleanup;
+ }
+
+ BufferDescriptor.Buffer = Buffer;
+ BufferDescriptor.AllocSize = BufferSize;
+
+ BufferDescriptor.FixedDataEnd = BufferDescriptor.Buffer;
+ BufferDescriptor.EndOfVariableData = BufferDescriptor.Buffer +
+ BufferDescriptor.AllocSize;
+
+ //
+ // Loop through the delta table replicating each entry in the delta table.
+ //
+
+ for (;;) {
+
+ //
+ // If we've returned all the entries the caller wants, we're all done.
+ //
+
+ if ( (Count--) <= 0 ) {
+ Status = STATUS_SUCCESS;
+ goto Cleanup;
+ }
+
+
+ //
+ // Put the data for the changelog entry into the user's buffer.
+ //
+
+ switch ( ChangeLogEntry->DeltaType ) {
+ case AddOrChangeDomain:
+ Status = NlPackUasDomain( &BufferDescriptor, DBInfo);
+ break;
+
+ case AddOrChangeGroup:
+ Status = NlPackUasGroup( ChangeLogEntry->ObjectRid,
+ &BufferDescriptor,
+ DBInfo,
+ &DummyFlag );
+ break;
+
+ case AddOrChangeUser:
+
+
+ //
+ // If this is a user account whose membership in Domain Users
+ // hasn't been communicated to the lanman BDC,
+ // do so.
+ //
+
+ if ( ChangeLogEntry->Flags & CHANGELOG_DOMAINUSERS_CHANGED ) {
+ Status = NlPackUasUserGroupMember(
+ ChangeLogEntry->ObjectRid,
+ &BufferDescriptor,
+ DBInfo );
+
+ //
+ // Otherwise this is just a added or changed user
+ //
+
+ } else {
+
+ //
+ // Compute the RotateCount for LogonHours
+ //
+ // Do it only once.
+ //
+
+ if ( !RotateCountComputed ) {
+ if ( !NetpRotateLogonHoursPhase1( FALSE, &RotateCount ) ) {
+ Status = STATUS_INTERNAL_ERROR;
+ goto Cleanup;
+ }
+ RotateCountComputed = TRUE;
+ }
+
+ Status = NlPackUasUser( ChangeLogEntry->ObjectRid,
+ &BufferDescriptor,
+ DBInfo,
+ &SessionKey,
+ RotateCount );
+ }
+
+ break;
+
+ case ChangeGroupMembership:
+ Status = NlPackUasGroupMember( ChangeLogEntry->ObjectRid,
+ &BufferDescriptor,
+ DBInfo );
+ break;
+
+ case DeleteGroup:
+ case DeleteUser:
+ case RenameUser:
+ case RenameGroup:
+ if( ChangeLogEntry->Flags & CHANGELOG_NAME_SPECIFIED ) {
+ Status = NlPackUasDelete(
+ ChangeLogEntry->DeltaType,
+ ChangeLogEntry->ObjectRid,
+ (LPWSTR)
+ (((LPBYTE)ChangeLogEntry) + sizeof(CHANGELOG_ENTRY)),
+ &BufferDescriptor,
+ DBInfo );
+ } else {
+ Status = STATUS_NO_SUCH_USER;
+ }
+ break;
+
+ case AddOrChangeAlias:
+ case ChangeAliasMembership:
+ case DeleteAlias:
+ case RenameAlias:
+
+#define DELTA_RESERVED_OPCODE 255
+ {
+
+ //
+ // This record is incompatible with a downlevel
+ // system, send a dummy record over to the downlevel system to
+ // ensure the SerialNumber gets updated appropriately.
+ //
+
+ PUSHORT RecordSize; // ptr to record size field in record header.
+ Status = NlPackUasHeader( DELTA_RESERVED_OPCODE,
+ 0,
+ &RecordSize,
+ &BufferDescriptor );
+ }
+ break;
+
+ default:
+ NlPrint((NL_CRITICAL,
+ "NetrAccountDeltas: invalid delta type in change log\n"));
+ Status = STATUS_SYNCHRONIZATION_REQUIRED;
+ break;
+ }
+
+ //
+ // If the buffer is too small to fit this entry,
+ // If we returned at least one entry, simply tell the caller more
+ // are available.
+ //
+
+ if (Status == STATUS_MORE_ENTRIES) {
+ if (*CountReturned == 0) {
+
+ if ( ServerSession->SsFlags & SS_UAS_BUFFER_OVERFLOW ) {
+
+ //
+ // since this client has already overflowed the
+ // databuffer once, he can't handle big size delta.
+ //
+
+ Status = STATUS_SYNCHRONIZATION_REQUIRED;
+ }
+ else {
+
+ Status = STATUS_BUFFER_TOO_SMALL;
+
+ //
+ // remember that this client data buffer overflowed.
+ //
+
+ ServerSession->SsFlags |= SS_UAS_BUFFER_OVERFLOW;
+ }
+ }
+ goto Cleanup;
+ }
+
+ //
+ // ?? The follow is not taken care for the down level.
+ //
+ // In the case where an user/group/alias record was
+ // added and deleted before the delta was made we will
+ // trace the change log and see there is correpondance
+ // delete log. If we found one then ignore this delta
+ // and proceed to the next delta. If we couldn't find
+ // one then return error STATUS_SYNCHRONIZATION_REQUIRED.
+ //
+
+ if ( Status != STATUS_SUCCESS ) {
+ goto Cleanup;
+ }
+
+
+ //
+ // Tell the caller he has another entry returned.
+ //
+
+ (*CountReturned)++;
+ NextRecordId->SerialNumber = ChangeLogEntry->SerialNumber.LowPart;
+
+ //
+ // Free up used temp. record
+ //
+
+ NetpMemoryFree(ChangeLogEntry);
+ ChangeLogEntry = NULL;
+
+
+ //
+ // If we've returned all the entries, we're all done.
+ //
+
+ LOCK_CHANGELOG();
+ ChangelogLocked = TRUE;
+
+ ChangeLogEntry = NlGetNextDownlevelChangeLogEntry( NextRecordId->SerialNumber );
+
+ if ( ChangeLogEntry == NULL ) {
+ Status = STATUS_SUCCESS;
+ goto Cleanup;
+ }
+
+ UNLOCK_CHANGELOG();
+ ChangelogLocked = FALSE;
+
+ //
+ // If we're debugging replication, return only one change to the caller.
+ //
+#if DBG
+ if ( NlGlobalTrace & NL_ONECHANGE_REPL ) {
+ Status = STATUS_MORE_ENTRIES;
+ goto Cleanup;
+ }
+#endif // DBG
+
+ //
+ // if the service is going down, stop packing deltas and
+ // return to the caller.
+ //
+
+ if( NlGlobalTerminate ) {
+
+ NlPrint((NL_CRITICAL, "NetrAccountDeltas is asked to return "
+ "when the service is going down.\n"));
+ Status = STATUS_MORE_ENTRIES;
+ goto Cleanup;
+ }
+
+
+ }
+
+Cleanup:
+
+ //
+ // In the case where an user/group record was added and deleted
+ // before the delta was made we will map the errors such that
+ // the requesting machine will have to re-synchronize. It is
+ // the easiest, not neccessarily the best, way to get both
+ // machines in sync.
+ //
+
+ if (Status == STATUS_NO_SUCH_USER || Status == STATUS_NO_SUCH_GROUP ||
+ Status == STATUS_NONE_MAPPED ) {
+ Status = STATUS_SYNCHRONIZATION_REQUIRED;
+ }
+
+ //
+ // reset buffer over flag in server session structure
+ //
+
+ if( (Status != STATUS_BUFFER_TOO_SMALL) &&
+ (ServerSession != NULL) &&
+ (ServerSession->SsFlags & SS_UAS_BUFFER_OVERFLOW) ) {
+
+ ServerSession->SsFlags &= ~SS_UAS_BUFFER_OVERFLOW;
+ }
+
+ //
+ // Free up locally allocated resources.
+ //
+
+ if ( !NT_SUCCESS( Status ) ) {
+ *CountReturned = 0;
+ }
+
+ //
+ // There are always at least as many as we returned
+ //
+ if ( *TotalEntries < *CountReturned ) {
+ *TotalEntries = *CountReturned;
+ }
+
+ if ( ChangelogLocked ) {
+ UNLOCK_CHANGELOG();
+ }
+
+ if( ChangeLogEntry != NULL) {
+ NetpMemoryFree( ChangeLogEntry );
+ }
+
+ NlPrint((NL_SYNC,
+ "NetrAccountDeltas: UAS partial sync returns %lx to "
+ FORMAT_LPWSTR " Count: %ld Total:%ld\n",
+ Status,
+ ComputerName,
+ *CountReturned,
+ *TotalEntries ));
+
+
+ return Status;
+
+ UNREFERENCED_PARAMETER( Level );
+
+}
+
+
+
+
+NTSTATUS
+NetrAccountSync (
+ IN LPWSTR PrimaryName,
+ IN LPWSTR ComputerName,
+ IN PNETLOGON_AUTHENTICATOR Authenticator,
+ OUT PNETLOGON_AUTHENTICATOR ReturnAuthenticator,
+ IN DWORD Reference,
+ IN DWORD Level,
+ OUT LPBYTE Buffer,
+ IN DWORD BufferSize,
+ OUT PULONG CountReturned,
+ OUT PULONG TotalEntries,
+ OUT PULONG NextReference,
+ OUT PUAS_INFO_0 LastRecordId
+ )
+/*++
+
+Routine Description:
+
+ This function is used by a UAS BDC or UAS member server to request
+ the entire user accounts database. This function can only be called
+ by a server which has previously authenticated with the PDC by
+ calling I_NetServerAuthenticate.
+
+ This function is only called by the XACT server upon receipt of a
+ I_NetAccountSync XACT SMB from a UAS BDC or a UAS member server. As
+ such, many of the parameters are opaque since the XACT server doesn't
+ need to interpret any of that data. This function uses RPC to
+ contact the Netlogon service.
+
+ The LanMan 3.0 SSI Functional Specification describes the operation
+ of this function.
+
+ "reference" and "next_reference" are treated as below.
+
+ 1. "reference" should hold either 0 or value of "next_reference"
+ from previous call to this API.
+ 2. Send the modals and ALL group records in the first call. The API
+ expects the buffer to be large enough to hold this info (worst
+ case size would be
+ MAXGROUP * (sizeof(struct group_info_1) + MAXCOMMENTSZ)
+ + sizeof(struct user_modals_info_0)
+ which, for now, will be 256 * (26 + 49) + 16 = 19216 bytes
+
+Arguments:
+
+ PrimaryName -- Must be NULL to indicate this call is a local call
+ being made on behalf of a UAS server by the XACT server.
+
+ ComputerName -- Name of the BDC or member making the call.
+
+ Authenticator -- supplied by the server.
+
+ ReturnAuthenticator -- Receives an authenticator returned by the PDC.
+
+ Reference -- Supplies find-first find-next handle returned by the
+ previous call to this function or 0 if it is the first call.
+
+ Level -- Reserved. Must be zero.
+
+ Buffer -- Returns opaque data representing the information to be
+ returned.
+
+ BufferLen -- Length of buffer in bytes.
+
+ CountReturned -- Returns the number of records returned in buffer.
+
+ TotalEntries -- Returns the total number of records available.
+
+ NextReference -- Returns a find-first find-next handle to be
+ provided on the next call.
+
+ LastRecordId -- Returns an opaque buffer identifying the last
+ record received by this function.
+
+
+Return Value:
+
+ NT status code.
+
+--*/
+
+{
+ NTSTATUS Status;
+
+ BUFFER_DESCRIPTOR BufferDescriptor;
+ PSAMPR_DOMAIN_INFO_BUFFER DomainInfo = NULL;
+
+ PSERVER_SESSION ServerSession = NULL;
+ PCHAR Where;
+ PDB_INFO DBInfo = &NlGlobalDBInfoArray[SAM_DB];
+
+ PSAM_SYNC_CONTEXT SamDBContext;
+
+ LONG RotateCount;
+ BOOL RotateCountComputed = FALSE;
+
+ //
+ // This API is not supported on workstations.
+ //
+
+ if ( NlGlobalRole == RoleMemberWorkstation ) {
+ return STATUS_NOT_SUPPORTED;
+ }
+
+ //
+ // If CompatibilityMode is off,
+ // disallow this function for downlevel servers.
+ //
+
+ if ( !NlGlobalUasCompatibilityMode ) {
+ Status = STATUS_ACCESS_DENIED;
+ goto Cleanup;
+ }
+
+ //
+ // Initialization
+ //
+
+ *TotalEntries = 0;
+ *CountReturned = 0;
+ *NextReference = 0;
+
+
+ //
+ // Check the primary name.
+ //
+
+ Status = NlVerifyWorkstation( PrimaryName );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto Cleanup;
+ }
+
+
+ NlPrint((NL_SYNC,
+ "NetrAccountSync: UAS full sync called by " FORMAT_LPWSTR
+ " Reference= %lx.\n",
+ ComputerName,
+ Reference ));
+
+ //
+ // we need to retrieve the requestor's entry to get sessionkey
+ //
+
+ LOCK_SERVER_SESSION_TABLE();
+ ServerSession = NlFindNamedServerSession( ComputerName );
+
+ if (ServerSession == NULL) {
+ UNLOCK_SERVER_SESSION_TABLE();
+ Status = STATUS_ACCESS_DENIED;
+ goto Cleanup;
+ }
+
+ //
+ // allow this call to go through only on UasServerSecureChannel.
+ //
+
+ if( ServerSession->SsSecureChannelType != UasServerSecureChannel ) {
+ UNLOCK_SERVER_SESSION_TABLE();
+ ServerSession = NULL;
+ Status = STATUS_ACCESS_DENIED;
+ goto Cleanup;
+ }
+
+ //
+ // now verify the Authenticator and update seed if OK
+ //
+
+ Status = NlCheckAuthenticator( ServerSession,
+ Authenticator,
+ ReturnAuthenticator);
+
+ if ( !NT_SUCCESS(Status) ) {
+ UNLOCK_SERVER_SESSION_TABLE();
+ ServerSession = NULL;
+ goto Cleanup;
+ }
+
+
+ //
+ // Prevent entry from being deleted, but drop the global lock.
+ //
+ // Beware of server with two concurrent calls outstanding
+ // (must have rebooted.)
+ //
+
+ if (ServerSession->SsFlags & SS_LOCKED ) {
+ UNLOCK_SERVER_SESSION_TABLE();
+
+ NlPrint((NL_CRITICAL, "NetrAccountSync: Concurrent call detected.\n" ));
+ ServerSession = NULL;
+ Status = STATUS_ACCESS_DENIED;
+ goto Cleanup;
+ }
+ ServerSession->SsFlags |= SS_LOCKED;
+
+ UNLOCK_SERVER_SESSION_TABLE();
+
+ //
+ // Build a buffer descriptor describing the buffer the caller passed in.
+ //
+
+ if ( Buffer == NULL || BufferSize == 0 ) {
+ Status = STATUS_BUFFER_TOO_SMALL;
+ goto Cleanup;
+ }
+
+ BufferDescriptor.Buffer = Buffer;
+ BufferDescriptor.AllocSize = BufferSize;
+
+ BufferDescriptor.FixedDataEnd = BufferDescriptor.Buffer;
+ BufferDescriptor.EndOfVariableData = BufferDescriptor.Buffer +
+ BufferDescriptor.AllocSize;
+
+ //
+ // Compute the total number of entries.
+ //
+ //
+ // Calculate total entries i.e. total records avaialable
+ // modal rec + # group rec + # user rec (plus group membership
+ // information) + 3 record for UasBuiltinGroups
+ //
+
+ Status = SamrQueryInformationDomain( DBInfo->DBHandle,
+ DomainGeneralInformation,
+ &DomainInfo );
+ if ( !NT_SUCCESS(Status) ) {
+ DomainInfo = NULL;
+ goto Cleanup;
+ }
+
+ *TotalEntries = 1 + (DomainInfo->General.GroupCount * 2) +
+ DomainInfo->General.UserCount +
+ 3;
+
+ NlPrint((NL_SYNC,
+ "NetrAccountSync: GroupCount: %ld UserCount: %ld\n",
+ DomainInfo->General.GroupCount,
+ DomainInfo->General.UserCount ));
+
+
+
+ //
+ // Warn the user if there are too many global groups in the domain.
+ //
+ // Lanman only support 255 groups. However this includes the global groups
+ // LOCAL, ADMINS, USERS, and GUESTS. So only 251 real global groups are
+ // allowed before the Lanman BDC goes into an infinite full sync.
+ //
+ if (!NlGlobalTooManyGlobalGroups && DomainInfo->General.GroupCount > 251 ) {
+ NlGlobalTooManyGlobalGroups = TRUE;
+
+ NlpWriteEventlog (
+ NELOG_NetlogonTooManyGlobalGroups,
+ EVENTLOG_ERROR_TYPE,
+ NULL,
+ 0,
+ NULL,
+ 0 );
+ }
+
+ SamIFree_SAMPR_DOMAIN_INFO_BUFFER( DomainInfo, DomainGeneralInformation );
+
+ //
+ // If this is the first call, allocate and initialize the sync context.
+ //
+
+ if ( Reference == 0 ) {
+
+ //
+ // If there already is a sync context,
+ // delete it.
+ //
+
+ if ( ServerSession->SsSync != NULL ) {
+ CLEAN_SYNC_CONTEXT( ServerSession->SsSync );
+ } else {
+
+ ServerSession->SsSync = NetpMemoryAllocate( sizeof(SYNC_CONTEXT) );
+ if ( ServerSession->SsSync == NULL ) {
+ Status = STATUS_NO_MEMORY;
+ goto Cleanup;
+ }
+ }
+
+ //
+ // Initialize all the fields in the newly allocated resume handle
+ // to indicate that SAM has never yet been called.
+ //
+
+ INIT_SYNC_CONTEXT( ServerSession->SsSync, SamDBContextType );
+
+ SamDBContext = &(ServerSession->SsSync->DBContext.Sam);
+
+ SamDBContext->SyncState = GroupState;
+ SamDBContext->SyncSerial = 1;
+
+ //
+ // On the first record only, return the current serial number of
+ // the database so the caller can use it as a description of the
+ // database.
+ //
+
+ LastRecordId->SerialNumber =
+ NlGlobalChangeLogDesc.SerialNumber[SAM_DB].LowPart;
+ if (!RtlTimeToSecondsSince1970( &DBInfo->CreationTime,
+ &LastRecordId->TimeCreated )) {
+ NlPrint((NL_CRITICAL,
+ "NetAccountSync: DomainCreationTime can't be converted\n" ));
+ LastRecordId->TimeCreated = 0;
+ }
+ Where = LastRecordId->ComputerName;
+ NetpLogonPutOemString(
+ NlGlobalAnsiComputerName,
+ sizeof(LastRecordId->ComputerName),
+ &Where );
+
+ //
+ // Put the description of the Domain at the front of the buffer for the
+ // first call.
+ //
+
+ Status = NlPackUasDomain( &BufferDescriptor, DBInfo );
+
+ if ( Status != STATUS_SUCCESS ) {
+ Status = STATUS_BUFFER_TOO_SMALL;
+ goto Cleanup;
+ }
+
+ (*CountReturned)++;
+
+ } else {
+ if ( (ServerSession->SsSync == NULL) ||
+ (ServerSession->SsSync->DBContextType != SamDBContextType) ) {
+ Status = STATUS_SYNCHRONIZATION_REQUIRED;
+ goto Cleanup;
+ }
+
+ SamDBContext = &(ServerSession->SsSync->DBContext.Sam);
+
+ if ( SamDBContext->SyncSerial != Reference ) {
+ Status = STATUS_SYNCHRONIZATION_REQUIRED;
+ goto Cleanup;
+ }
+
+ SamDBContext->SyncSerial++;
+ }
+
+ //
+ // Loop for each entry placed in the output buffer
+ //
+ // Each iteration of the loop below puts one more entry into the array
+ // returned to the caller. The algorithm is split into 2 parts. The
+ // first part checks to see if we need to retrieve more information from
+ // SAM and gets the description of several users or groups from SAM in a
+ // single call. The second part puts a single entry into the buffer
+ // returned to the caller.
+ //
+
+ while ( SamDBContext->SyncState != SamDoneState ) {
+
+ //
+ // Get more information from SAM
+ //
+ // Handle when we've not yet called SAM or we've already consumed
+ // all of the information returned on a previous call to SAM.
+ //
+ // This is a 'while' rather than an 'if' to handle the case
+ // where SAM returns zero entries.
+ //
+
+ while ( SamDBContext->Index >= SamDBContext->Count ) {
+
+ //
+ // Free any previous buffer returned from SAM.
+ //
+
+ if ( ServerSession->SsSync != NULL ) {
+ CLEAN_SYNC_CONTEXT( ServerSession->SsSync );
+ }
+
+ //
+ // If we've already gotten everything from SAM,
+ // we've finished all of the groups,
+ //
+ // If we've just done the groups,
+ // go on to do the users.
+ //
+ // If we've just done the users,
+ // go on to do the group memberships.
+ //
+ // If we've just done the group memberships,
+ // we're all done.
+ //
+
+ if ( SamDBContext->SamAllDone ) {
+
+ SamDBContext->SamEnumHandle = 0;
+ SamDBContext->Index = 0;
+ SamDBContext->Count = 0;
+ SamDBContext->SamAllDone = FALSE;
+ SamDBContext->UasBuiltinGroups = 0;
+
+ if (SamDBContext->SyncState == GroupState ) {
+ SamDBContext->SyncState = UasBuiltinGroupState;
+ } else if (SamDBContext->SyncState == UasBuiltinGroupState ) {
+ SamDBContext->SyncState = UserState;
+ } else if (SamDBContext->SyncState == UserState ) {
+ SamDBContext->SyncState = SamDoneState;
+ Status = STATUS_SUCCESS;
+ }
+
+ break;
+ }
+
+ //
+ // Do the actual enumeration
+ //
+
+ if (SamDBContext->SyncState == GroupState) {
+
+ Status = SamrEnumerateGroupsInDomain(
+ DBInfo->DBHandle,
+ &SamDBContext->SamEnumHandle,
+ &SamDBContext->SamEnum,
+ SAM_SYNC_PREF_MAX,
+ &SamDBContext->Count );
+
+ } else if (SamDBContext->SyncState == UasBuiltinGroupState) {
+
+ SamDBContext->SamEnum = NULL;
+ SamDBContext->Count = UAS_BUILTIN_GROUPS_COUNT;
+
+ Status = STATUS_SUCCESS;
+
+ } else if (SamDBContext->SyncState == UserState ) {
+
+ Status = SamrEnumerateUsersInDomain(
+ DBInfo->DBHandle,
+ &SamDBContext->SamEnumHandle,
+ 0, // enumerate all accounts.
+ &SamDBContext->SamEnum,
+ SAM_SYNC_PREF_MAX,
+ &SamDBContext->Count );
+
+ } else {
+ NlPrint((NL_CRITICAL,
+ "NetrAccountSync: Invalid state: %ld\n",
+ SamDBContext->SyncState ));
+ }
+
+ if ( !NT_SUCCESS( Status ) ) {
+ SamDBContext->SamEnum = NULL;
+ goto Cleanup;
+ }
+
+#if DBG
+ if( SamDBContext->SamEnum != NULL ) {
+ NlAssert( SamDBContext->Count ==
+ SamDBContext->SamEnum->EntriesRead );
+ }
+#endif // DBG
+
+ //
+ // If SAM says there is more information,
+ // just ensure he returned something to us on this call.
+ //
+
+ if ( Status == STATUS_MORE_ENTRIES ) {
+ if ( SamDBContext->Count == 0 ) {
+ Status = STATUS_INTERNAL_ERROR;
+ goto Cleanup;
+ }
+
+ //
+ // If SAM says he's returned all of the information,
+ // remember not to ask SAM for more.
+ //
+
+ } else {
+ SamDBContext->SamAllDone = TRUE;
+ }
+
+ SamDBContext->Index = 0;
+ }
+
+ //
+ // Place this entry into the return buffer.
+ //
+
+ if ( SamDBContext->Count > 0 ) {
+
+ if (SamDBContext->SyncState == GroupState ) {
+ Status = NlPackUasGroup(
+ SamDBContext->SamEnum->
+ Buffer[SamDBContext->Index].RelativeId,
+ &BufferDescriptor,
+ DBInfo,
+ &SamDBContext->UasBuiltinGroups );
+
+ } else if (SamDBContext->SyncState == UasBuiltinGroupState ) {
+ Status = NlPackUasBuiltinGroup(
+ SamDBContext->Index,
+ &BufferDescriptor,
+ &SamDBContext->UasBuiltinGroups );
+
+ } else if (SamDBContext->SyncState == UserState ) {
+
+ BUFFER_DESCRIPTOR SavedBufferDescriptor;
+
+
+ //
+ // Compute the RotateCount for LogonHours
+ //
+ // Do it only once.
+ //
+
+ if ( !RotateCountComputed ) {
+ if ( !NetpRotateLogonHoursPhase1( FALSE, &RotateCount ) ) {
+ Status = STATUS_INTERNAL_ERROR;
+ goto Cleanup;
+ }
+ RotateCountComputed = TRUE;
+ }
+
+ //
+ // save buffer info so that we can restore it when
+ // we can't place user record and its group
+ // membership record in single transmit buffer.
+ //
+
+ SavedBufferDescriptor = BufferDescriptor;
+
+ Status = NlPackUasUser(
+ SamDBContext->SamEnum->
+ Buffer[SamDBContext->Index].RelativeId,
+ &BufferDescriptor,
+ DBInfo,
+ &ServerSession->SsSessionKey,
+ RotateCount );
+
+ //
+ // if we have successfully packed the user record, then
+ // place its group membership record immediately.
+ //
+
+ if ( Status == STATUS_SUCCESS ) {
+
+ Status = NlPackUasUserGroupMember(
+ SamDBContext->SamEnum->
+ Buffer[SamDBContext->Index].RelativeId,
+ &BufferDescriptor,
+ DBInfo );
+
+ if ( Status == STATUS_SUCCESS ) {
+
+ //
+ // increment record count
+ //
+
+ (*CountReturned)++;
+
+ } else {
+
+ BufferDescriptor = SavedBufferDescriptor;
+ }
+ }
+ }
+
+
+ //
+ // If the record was properly packed,
+ // just go on to the next record.
+ //
+
+ if ( Status != STATUS_SUCCESS ) {
+ goto Cleanup;
+ }
+
+ SamDBContext->Index ++;
+ (*CountReturned)++;
+
+ //
+ // If we're debugging replication, return only one change to the caller.
+ //
+#if DBG
+ if ( NlGlobalTrace & NL_ONECHANGE_REPL ) {
+ Status = STATUS_MORE_ENTRIES;
+ goto Cleanup;
+ }
+#endif // DBG
+
+ //
+ // if the service is going down, stop packing records and
+ // return to the caller.
+ //
+
+ if( NlGlobalTerminate ) {
+
+ NlPrint((NL_CRITICAL, "NetrAccountSync is asked to return "
+ "when the service is going down.\n"));
+ Status = STATUS_MORE_ENTRIES;
+ goto Cleanup;
+ }
+ }
+ }
+
+Cleanup:
+
+ //
+ // Set the return parameters to the proper values.
+ //
+
+ if ( NT_SUCCESS( Status ) ) {
+ if ( Status == STATUS_MORE_ENTRIES ) {
+ *NextReference = SamDBContext->SyncSerial;
+ } else {
+ *NextReference = (ULONG) -1;
+
+ }
+ } else {
+ *CountReturned = 0;
+ *NextReference = 0;
+ }
+
+
+ //
+ // Unlock the server session entry if we've locked it.
+ //
+
+ if ( ServerSession != NULL ) {
+
+ //
+ // If we're done, free up the context structure,
+ //
+
+ if ( Status != STATUS_MORE_ENTRIES && ServerSession->SsSync != NULL ) {
+ CLEAN_SYNC_CONTEXT( ServerSession->SsSync );
+
+ NetpMemoryFree( ServerSession->SsSync );
+ ServerSession->SsSync = NULL;
+ }
+
+ NlUnlockServerSession( ServerSession );
+ }
+
+ NlPrint((NL_SYNC,
+ "NetrAccountSync: UAS full sync returns %lx to "
+ FORMAT_LPWSTR "\n",
+ Status,
+ ComputerName ));
+
+ return Status;
+
+ UNREFERENCED_PARAMETER( Level );
+
+}
+
+
+NET_API_STATUS
+NetrLogonControl(
+ IN LPWSTR ServerName OPTIONAL,
+ IN DWORD FunctionCode,
+ IN DWORD QueryLevel,
+ OUT PNETLOGON_CONTROL_QUERY_INFORMATION QueryInformation
+ )
+
+/*++
+
+Routine Description:
+
+ This function controls various aspects of the Netlogon service. It
+ can be used to request that a BDC ensure that its copy of the SAM
+ database is brought up to date. It can, also, be used to determine
+ if a BDC currently has a secure channel open to the PDC.
+
+ Only an Admin, Account Operator or Server Operator may call this
+ function.
+
+Arguments:
+
+ ServerName - The name of the remote server.
+
+ FunctionCode - Defines the operation to be performed. The valid
+ values are:
+
+ FunctionCode Values
+
+ NETLOGON_CONTROL_QUERY - No operation. Merely returns the
+ information requested.
+
+ NETLOGON_CONTROL_REPLICATE: Forces the SAM database on a BDC
+ to be brought in sync with the copy on the PDC. This
+ operation does NOT imply a full synchronize. The
+ Netlogon service will merely replicate any outstanding
+ differences if possible.
+
+ NETLOGON_CONTROL_SYNCHRONIZE: Forces a BDC to get a
+ completely new copy of the SAM database from the PDC.
+ This operation will perform a full synchronize.
+
+ NETLOGON_CONTROL_PDC_REPLICATE: Forces a PDC to ask each BDC
+ to replicate now.
+
+ QueryLevel - Indicates what information should be returned from
+ the Netlogon Service. Must be 1.
+
+ QueryInformation - Returns a pointer to a buffer which contains the
+ requested information. The buffer must be freed using
+ NetApiBufferFree.
+
+
+Return Value:
+
+ NERR_Success: the operation was successful
+
+ ERROR_NOT_SUPPORTED: Function code is not valid on the specified
+ server. (e.g. NETLOGON_CONTROL_REPLICATE was passed to a PDC).
+
+--*/
+{
+ NET_API_STATUS NetStatus;
+
+ QueryInformation->NetlogonInfo1 = NULL;
+
+ switch( QueryLevel ) {
+ case (1):
+ break;
+ case (2):
+ NetStatus = ERROR_NOT_SUPPORTED;
+ goto Cleanup;
+
+ default:
+ NetStatus = ERROR_INVALID_LEVEL;
+ goto Cleanup;
+ }
+
+ //
+ // ensure the input data is valid.
+ //
+
+ switch( FunctionCode ) {
+ case NETLOGON_CONTROL_QUERY:
+ case NETLOGON_CONTROL_REPLICATE:
+ case NETLOGON_CONTROL_SYNCHRONIZE:
+ case NETLOGON_CONTROL_PDC_REPLICATE:
+
+#if DBG
+ case NETLOGON_CONTROL_BACKUP_CHANGE_LOG:
+ case NETLOGON_CONTROL_TRUNCATE_LOG:
+ case NETLOGON_CONTROL_BREAKPOINT:
+#endif // DBG
+
+ break;
+
+ default:
+ NetStatus = ERROR_NOT_SUPPORTED;
+ goto Cleanup;
+
+ }
+
+ NetStatus = NetrLogonControl2Ex(
+ ServerName,
+ FunctionCode,
+ QueryLevel,
+ NULL,
+ QueryInformation );
+
+Cleanup:
+
+ return( NetStatus );
+}
+
+NET_API_STATUS
+NetrLogonControl2(
+ IN LPWSTR ServerName OPTIONAL,
+ IN DWORD FunctionCode,
+ IN DWORD QueryLevel,
+ IN PNETLOGON_CONTROL_DATA_INFORMATION InputData,
+ OUT PNETLOGON_CONTROL_QUERY_INFORMATION QueryInformation
+ )
+
+/*++
+
+Routine Description:
+
+ Same as NetrLogonControl2Ex.
+
+ A client should never pass a QueryLevel of 4 to this procedure. We don't check since, if
+ they did, it's too late now. The client will access violate upon return.
+
+Arguments:
+
+ Same as NetrLogonControl2Ex.
+
+Return Value:
+
+
+--*/
+{
+ NET_API_STATUS NetStatus;
+
+ NetStatus = NetrLogonControl2Ex(
+ ServerName,
+ FunctionCode,
+ QueryLevel,
+ InputData,
+ QueryInformation );
+
+
+ return NetStatus;
+}
+
+
+
+NET_API_STATUS
+NetrLogonControl2Ex(
+ IN LPWSTR ServerName OPTIONAL,
+ IN DWORD FunctionCode,
+ IN DWORD QueryLevel,
+ IN PNETLOGON_CONTROL_DATA_INFORMATION InputData,
+ OUT PNETLOGON_CONTROL_QUERY_INFORMATION QueryInformation
+ )
+
+/*++
+
+Routine Description:
+
+ This function controls various aspects of the Netlogon service. It
+ can be used to request that a BDC ensure that its copy of the SAM
+ database is brought up to date. It can, also, be used to determine
+ if a BDC currently has a secure channel open to the PDC.
+
+ Only an Admin, Account Operator or Server Operator may call this
+ function.
+
+Arguments:
+
+ ServerName - The name of the remote server.
+
+ FunctionCode - Defines the operation to be performed. The valid
+ values are:
+
+ FunctionCode Values
+
+ NETLOGON_CONTROL_QUERY - No operation. Merely returns the
+ information requested.
+
+ NETLOGON_CONTROL_REPLICATE: Forces the SAM database on a BDC
+ to be brought in sync with the copy on the PDC. This
+ operation does NOT imply a full synchronize. The
+ Netlogon service will merely replicate any outstanding
+ differences if possible.
+
+ NETLOGON_CONTROL_SYNCHRONIZE: Forces a BDC to get a
+ completely new copy of the SAM database from the PDC.
+ This operation will perform a full synchronize.
+
+ NETLOGON_CONTROL_PDC_REPLICATE: Forces a PDC to ask each BDC
+ to replicate now.
+
+ NETLOGON_CONTROL_REDISCOVER: Forces a DC to rediscover the
+ specified trusted domain DC.
+
+ NETLOGON_CONTROL_TC_QUERY: Query the status of the specified
+ trusted domain secure channel.
+
+ NETLOGON_CONTROL_TRANSPORT_NOTIFY: Notifies netlogon that a new transport
+ has been added. Currently, it merely resets discovery timeouts allowing
+ all secure channel discoveries to be retried immediately. However, the
+ intention is to later add support for anything similar. The intention is that
+ a client can call this function after a new transport has been added (e.g., it
+ dialed a RAS link) and immediately before calling Netlogon (e.g., indirectly
+ by doing an LsaLogonUser).
+
+ QueryLevel - Indicates what information should be returned from
+ the Netlogon Service. Must be 1.
+
+ InputData - According to the function code specified this parameter
+ will carry input data. NETLOGON_CONTROL_REDISCOVER and
+ NETLOGON_CONTROL_TC_QUERY function code specify the trusted
+ domain name (LPWSTR type) here.
+ NETLOGON_CONTROL_FIND_USER function code specifies the user name
+ (LPWSTR type) here.
+
+ QueryInformation - Returns a pointer to a buffer which contains the
+ requested information. The buffer must be freed using
+ NetApiBufferFree.
+
+
+Return Value:
+
+ NERR_Success: the operation was successful
+
+ ERROR_NOT_SUPPORTED: Function code is not valid on the specified
+ server. (e.g. NETLOGON_CONTROL_REPLICATE was passed to a PDC).
+
+--*/
+{
+ NET_API_STATUS NetStatus;
+ NTSTATUS Status;
+ DWORD Flags = 0;
+ DWORD i;
+ DWORD InfoSize;
+ ACCESS_MASK DesiredAccess;
+
+ UNICODE_STRING DomainName;
+ PCLIENT_SESSION ClientSession = NULL;
+ LPWSTR TDCName = NULL;
+ LPWSTR TrustedDomainName = NULL;
+ WCHAR TDCBuffer[UNCLEN+1];
+
+
+ UNREFERENCED_PARAMETER( ServerName );
+
+ //
+ // Ensure the QueryLevel is valid
+ //
+
+ QueryInformation->NetlogonInfo1 = NULL;
+
+ switch( QueryLevel ) {
+ case (1):
+ case (2):
+ case (3):
+ case (4):
+ break;
+ default:
+ NetStatus = ERROR_INVALID_LEVEL;
+ goto Cleanup;
+ }
+
+ //
+ // ensure the input data is valid.
+ //
+
+ switch( FunctionCode ) {
+ case NETLOGON_CONTROL_REDISCOVER:
+ case NETLOGON_CONTROL_TC_QUERY:
+ case NETLOGON_CONTROL_FIND_USER:
+#if DBG
+ case NETLOGON_CONTROL_SET_DBFLAG:
+#endif // DBG
+
+ NlAssert( InputData != NULL );
+ if( InputData == NULL ) {
+ NetStatus = ERROR_INVALID_PARAMETER;
+ goto Cleanup;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ //
+ // compute access mask.
+ //
+
+ switch ( FunctionCode ) {
+
+ case NETLOGON_CONTROL_QUERY:
+ case NETLOGON_CONTROL_TC_QUERY:
+ case NETLOGON_CONTROL_TRANSPORT_NOTIFY:
+ DesiredAccess = NETLOGON_QUERY_ACCESS;
+ break;
+
+ case NETLOGON_CONTROL_REPLICATE:
+ case NETLOGON_CONTROL_SYNCHRONIZE:
+ case NETLOGON_CONTROL_PDC_REPLICATE:
+ case NETLOGON_CONTROL_REDISCOVER:
+ case NETLOGON_CONTROL_FIND_USER:
+#if DBG
+ case NETLOGON_CONTROL_BREAKPOINT:
+ case NETLOGON_CONTROL_SET_DBFLAG:
+ case NETLOGON_CONTROL_TRUNCATE_LOG:
+ case NETLOGON_CONTROL_BACKUP_CHANGE_LOG:
+#endif // DBG
+ default:
+ DesiredAccess = NETLOGON_CONTROL_ACCESS;
+ break;
+ }
+
+
+ //
+ // Perform access validation on the caller.
+ //
+
+ NetStatus = NetpAccessCheck(
+ NlGlobalNetlogonSecurityDescriptor, // Security descriptor
+ DesiredAccess, // Desired access
+ &NlGlobalNetlogonInfoMapping ); // Generic mapping
+
+ if ( NetStatus != NERR_Success) {
+ NetStatus = ERROR_ACCESS_DENIED;
+ goto Cleanup;
+ }
+
+
+ //
+ // Handle the various FunctionCodes
+ //
+
+ switch ( FunctionCode ) {
+
+ //
+ // On a query, do nothing but return status.
+ //
+
+ case NETLOGON_CONTROL_QUERY:
+ NlPrint((NL_MISC, "QUERY function received.\n" ));
+ break;
+
+ //
+ // Force a replication on a BDC.
+ //
+
+ case NETLOGON_CONTROL_REPLICATE:
+
+ //
+ // This FunctionCode is only valid on a BDC
+ //
+
+ if ( NlGlobalRole != RoleBackup ) {
+ NetStatus = ERROR_NOT_SUPPORTED;
+ goto Cleanup;
+ }
+
+ //
+ // Force a replicate on all databases.
+ //
+
+ NlPrint((NL_SYNC, "Force PARTIAL SYNC function received.\n" ));
+
+ EnterCriticalSection( &NlGlobalDbInfoCritSect );
+ for( i = 0; i < NUM_DBS; i++ ) {
+ NlGlobalDBInfoArray[i].UpdateRqd = TRUE;
+ }
+ LeaveCriticalSection( &NlGlobalDbInfoCritSect );
+
+ //
+ // Start the replicator now.
+ //
+
+ (VOID) NlStartReplicatorThread( 0 );
+
+ break;
+
+
+
+ //
+ // Force a full synchronize on a BDC.
+ //
+
+ case NETLOGON_CONTROL_SYNCHRONIZE:
+
+ //
+ // This FunctionCode is only valid on a BDC
+ //
+
+ if ( NlGlobalRole != RoleBackup ) {
+ NetStatus = ERROR_NOT_SUPPORTED;
+ goto Cleanup;
+ }
+
+ //
+ // Force a SYNC on all databases.
+ //
+
+ NlPrint((NL_SYNC, "Force FULL SYNC function received.\n" ));
+
+ for( i = 0; i < NUM_DBS; i++ ) {
+ (VOID) NlForceStartupSync( &NlGlobalDBInfoArray[i] );
+
+ //
+ // Do a complete full sync (don't restart it).
+ //
+ NlSetFullSyncKey( i, NULL );
+ }
+
+ //
+ // Stop the replicator.
+ //
+ // It might be in the middle of a full sync. This'll force it to
+ // start over again.
+ //
+ // It might be waiting for 5 minutes to start it's next iteration.
+ // This'll force it to start NOW.
+ //
+ // It might have marked that it's already done a full sync. This'll
+ // force it to do another one.
+ //
+
+ NlStopReplicator();
+
+
+ //
+ // Start the replicator now.
+ //
+
+ (VOID) NlStartReplicatorThread( 0 );
+
+ break;
+
+
+
+ //
+ // Force a PDC to broadcast a database change record.
+ //
+
+ case NETLOGON_CONTROL_PDC_REPLICATE:
+
+ //
+ // This FunctionCode is only valid on a PDC
+ //
+
+ if ( NlGlobalRole != RolePrimary ) {
+ NetStatus = ERROR_NOT_SUPPORTED;
+ goto Cleanup;
+ }
+
+ //
+ // Simply send the announcement. Any BDC that is out of date
+ // will replicate any changes.
+ //
+
+ NlPrint((NL_SYNC, "PDC REPLICATE function received.\n" ));
+ NlPrimaryAnnouncement( ANNOUNCE_FORCE );
+
+ break;
+
+
+ //
+ // Force to rediscover trusted domain DCs.
+ //
+
+ case NETLOGON_CONTROL_REDISCOVER:
+
+ NlPrint((NL_SESSION_SETUP , "NETLOGON_CONTROL_REDISCOVER function received.\n" ));
+
+ NlAssert( InputData->TrustedDomainName != NULL );
+ if( InputData->TrustedDomainName == NULL ) {
+
+ NlPrint((NL_CRITICAL, "NetrLogonControl called with "
+ "function code NETLOGON_CONTROL_REDISCOVER "
+ "specified NULL trusted domain name. \n" ));
+
+ NetStatus = ERROR_INVALID_PARAMETER;
+ goto Cleanup;
+ }
+
+ RtlInitUnicodeString(&DomainName, InputData->TrustedDomainName );
+
+ //
+ // get client structure for the specified domain.
+ //
+
+ ClientSession = NlFindNamedClientSession( &DomainName );
+
+ if( ClientSession == NULL ) {
+ NlPrint((NL_CRITICAL,
+ "NetrLogonControl can't find the client structure of "
+ "the domain %wZ specified.\n", &DomainName ));
+
+ NetStatus = ERROR_NO_SUCH_DOMAIN;
+ goto Cleanup;
+ }
+
+
+ //
+ // Force Discovery of a DC
+ //
+
+ if ( !NlTimeoutSetWriterClientSession( ClientSession, WRITER_WAIT_PERIOD ) ) {
+ NlPrint((NL_CRITICAL, "NetrLogonControl2: Can't become writer of client session.\n" ));
+ NetStatus = ERROR_NO_LOGON_SERVERS;
+ goto Cleanup;
+ } else {
+ NlSetStatusClientSession( ClientSession, STATUS_NO_LOGON_SERVERS );
+ Status = NlDiscoverDc( ClientSession, DT_Synchronous );
+ NlResetWriterClientSession( ClientSession );
+
+ if ( !NT_SUCCESS(Status) ) {
+
+ NlPrint((NL_CRITICAL,
+ "NetrLogonControl: %wZ: Discovery failed %lx\n",
+ &ClientSession->CsDomainName,
+ Status ));
+
+ NetStatus = NetpNtStatusToApiStatus( Status );
+ goto Cleanup;
+ }
+ }
+
+ break;
+
+ case NETLOGON_CONTROL_TC_QUERY:
+ NlPrint((NL_SESSION_SETUP , "NETLOGON_CONTROL_TC_QUERY function received.\n" ));
+ break;
+
+ //
+ // A client has added a new transport and needs us to use it.
+ // Mark all the client sessions that its OK to authentication NOW.
+ //
+
+ case NETLOGON_CONTROL_TRANSPORT_NOTIFY: {
+ PLIST_ENTRY ListEntry;
+ NlPrint((NL_SESSION_SETUP , "NETLOGON_CONTROL_TRANSPORT_NOTIFY function received.\n" ));
+
+ EnterCriticalSection( &NlGlobalDcDiscoveryCritSect );
+ LOCK_TRUST_LIST();
+
+ //
+ // Mark each entry to indicate we've not tried to authenticate recently
+ //
+
+ if ( NlGlobalClientSession != NULL ) {
+ if ( NlGlobalClientSession->CsState != CS_AUTHENTICATED ) {
+ NlPrint(( NL_SESSION_SETUP,
+ " %wZ: Zero LastAuth\n",
+ &NlGlobalClientSession->CsDomainName ));
+ NlGlobalClientSession->CsLastAuthenticationTry.QuadPart = 0;
+ }
+ }
+
+ for ( ListEntry = NlGlobalTrustList.Flink ;
+ ListEntry != &NlGlobalTrustList ;
+ ListEntry = ListEntry->Flink) {
+
+ ClientSession = CONTAINING_RECORD( ListEntry,
+ CLIENT_SESSION,
+ CsNext );
+
+ if ( ClientSession->CsState != CS_AUTHENTICATED ) {
+ NlPrint(( NL_SESSION_SETUP,
+ " %wZ: Zero LastAuth\n",
+ &ClientSession->CsDomainName ));
+ ClientSession->CsLastAuthenticationTry.QuadPart = 0;
+ }
+ }
+
+ UNLOCK_TRUST_LIST();
+ LeaveCriticalSection( &NlGlobalDcDiscoveryCritSect );
+
+ ClientSession = NULL;
+ break;
+ }
+
+ //
+ // Find a user in one of the trusted domains.
+ //
+
+ case NETLOGON_CONTROL_FIND_USER:
+ NlPrint((NL_MISC, "NETLOGON_CONTROL_FIND_USER function received for %ws.\n", InputData->UserName ));
+
+ //
+ // Find a user in one of the trusted domains.
+ //
+ // Allow machine accounts just as a handy extension.
+ // Don't find "Local User" accounts since we can't pass through to them
+ //
+ ClientSession = NlPickDomainWithAccount (
+ InputData->UserName,
+ USER_NORMAL_ACCOUNT | USER_MACHINE_ACCOUNT_MASK );
+
+ break;
+
+#if DBG
+ //
+ // Force a breakpoint
+ //
+
+ case NETLOGON_CONTROL_BREAKPOINT:
+ KdPrint(( "I_NetLogonControl Break Point\n"));
+ DbgBreakPoint();
+ break;
+
+ //
+ // Change the debug flags
+ //
+
+ case NETLOGON_CONTROL_SET_DBFLAG:
+ NlGlobalTrace = InputData->DebugFlag;
+ NlPrint((NL_MISC,"NlGlobalTrace is set to %lx\n", NlGlobalTrace ));
+
+ break;
+
+ //
+ // Truncate the log file
+ //
+
+ case NETLOGON_CONTROL_TRUNCATE_LOG:
+
+ NlOpenDebugFile( TRUE );
+ NlPrint((NL_MISC, "TRUNCATE_LOG function received.\n" ));
+ break;
+
+ //
+ // Backup changelog file
+ //
+
+ case NETLOGON_CONTROL_BACKUP_CHANGE_LOG:
+
+ NetStatus = NlBackupChangeLogFile();
+ NlPrint((NL_MISC, "BACKUP_CHANGE_LOG function received, (%ld).\n", NetStatus ));
+ break;
+
+#endif // DBG
+
+ //
+ // All other function codes are invalid.
+ //
+
+ default:
+ NetStatus = ERROR_NOT_SUPPORTED;
+ goto Cleanup;
+ }
+
+
+ //
+ // allocate return info structure.
+ //
+
+ switch( QueryLevel ) {
+ case (1):
+ InfoSize = sizeof(NETLOGON_INFO_1);
+ break;
+ case (2):
+ InfoSize = sizeof(NETLOGON_INFO_2);
+ break;
+ case (3):
+ InfoSize = sizeof(NETLOGON_INFO_3);
+ break;
+ case (4):
+ InfoSize = sizeof(NETLOGON_INFO_4);
+ break;
+ }
+
+ QueryInformation->NetlogonInfo1 = MIDL_user_allocate( InfoSize );
+
+ if ( QueryInformation->NetlogonInfo1 == NULL ) {
+ NetStatus = ERROR_NOT_ENOUGH_MEMORY;
+ goto Cleanup;
+ }
+
+
+ //
+ // Return DomainName and DC Name.
+ //
+ switch( QueryLevel ) {
+ case (4):
+ switch ( FunctionCode ) {
+ case NETLOGON_CONTROL_FIND_USER:
+
+ if (ClientSession == NULL) {
+ NetStatus = NERR_UserNotFound;
+ goto Cleanup;
+ }
+
+ //
+ // Capture the name of the server
+ // (even if it is an empty string.)
+ //
+
+ Status = NlCaptureServerClientSession( ClientSession, TDCBuffer );
+
+ TDCName = NetpAllocWStrFromWStr( TDCBuffer );
+
+ if ( TDCName == NULL ) {
+ NetStatus = ERROR_NOT_ENOUGH_MEMORY;
+ goto Cleanup;
+ }
+
+ QueryInformation->NetlogonInfo4->netlog4_trusted_dc_name = TDCName;
+
+ //
+ // Capture the name of the domain.
+ //
+
+ TrustedDomainName = NetpAllocWStrFromWStr( ClientSession->CsDomainName.Buffer );
+
+ if ( TrustedDomainName == NULL ) {
+ NetStatus = ERROR_NOT_ENOUGH_MEMORY;
+ goto Cleanup;
+ }
+
+ QueryInformation->NetlogonInfo4->netlog4_trusted_domain_name = TrustedDomainName;
+ break;
+
+ default:
+ NetStatus = ERROR_INVALID_PARAMETER;
+ goto Cleanup;
+ }
+ break;
+
+ //
+ // Return queried profile information.
+ //
+ case (3):
+ QueryInformation->NetlogonInfo3->netlog3_flags = 0;
+ QueryInformation->NetlogonInfo3->netlog3_logon_attempts =
+ MsvGetLogonAttemptCount();
+ QueryInformation->NetlogonInfo3->netlog3_reserved1 = 0;
+ QueryInformation->NetlogonInfo3->netlog3_reserved2 = 0;
+ QueryInformation->NetlogonInfo3->netlog3_reserved3 = 0;
+ QueryInformation->NetlogonInfo3->netlog3_reserved4 = 0;
+ QueryInformation->NetlogonInfo3->netlog3_reserved5 = 0;
+ break;
+
+ //
+ // Return secure channel specific information.
+ //
+ case (2):
+ switch ( FunctionCode ) {
+ case NETLOGON_CONTROL_REDISCOVER:
+ case NETLOGON_CONTROL_TC_QUERY:
+
+ if( ClientSession == NULL ) {
+
+ NlAssert( InputData->TrustedDomainName != NULL );
+ if( InputData->TrustedDomainName == NULL ) {
+
+ NlPrint((NL_CRITICAL,
+ "NetrLogonControl called to query at info "
+ "level specified NULL trusted domain name. \n" )) ;
+ NetStatus = ERROR_INVALID_PARAMETER;
+ goto Cleanup;
+ }
+
+ RtlInitUnicodeString(&DomainName, InputData->TrustedDomainName );
+
+ //
+ // get client structure for the specified domain.
+ //
+
+ ClientSession = NlFindNamedClientSession( &DomainName );
+
+ if( ClientSession == NULL ) {
+ NlPrint((NL_CRITICAL,
+ "NetrLogonControl can't find the client structure of "
+ "the domain %wZ specified.\n", &DomainName ));
+
+ NetStatus = ERROR_NO_SUCH_DOMAIN;
+ goto Cleanup;
+ }
+ }
+
+ //
+ // Capture the name of the server
+ // (even if it is an empty string.)
+ //
+
+ Status = NlCaptureServerClientSession( ClientSession, TDCBuffer );
+ QueryInformation->NetlogonInfo2->netlog2_tc_connection_status =
+ NetpNtStatusToApiStatus(Status);
+
+ TDCName = NetpAllocWStrFromWStr( TDCBuffer );
+
+ if ( TDCName == NULL ) {
+ NetStatus = ERROR_NOT_ENOUGH_MEMORY;
+ goto Cleanup;
+ }
+
+ QueryInformation->NetlogonInfo2->netlog2_trusted_dc_name = TDCName;
+ break;
+
+ default:
+ NetStatus = ERROR_INVALID_PARAMETER;
+ goto Cleanup;
+ }
+
+ //
+ // fall through to fill other fields of the info structure.
+ //
+
+
+ //
+ // Return status of secure channel to PDC.
+ //
+ case (1):
+
+ //
+ // If this is a BDC, query how replication is going.
+ //
+
+ if ( NlGlobalRole == RoleBackup ) {
+
+ //
+ // If this is a BDC tell the caller whether the replicator is running,
+ //
+
+ EnterCriticalSection( &NlGlobalReplicatorCritSect );
+ if ( IsReplicatorRunning() ) {
+ Flags |= NETLOGON_REPLICATION_IN_PROGRESS;
+ }
+ LeaveCriticalSection( &NlGlobalReplicatorCritSect );
+
+ EnterCriticalSection( &NlGlobalDbInfoCritSect );
+ for( i = 0; i < NUM_DBS; i++ ) {
+ if ( NlGlobalDBInfoArray[i].UpdateRqd ) {
+ Flags |= NETLOGON_REPLICATION_NEEDED;
+ }
+ if ( NlGlobalDBInfoArray[i].FullSyncRequired ) {
+ Flags |= NETLOGON_FULL_SYNC_REPLICATION;
+ }
+ if ( NlGlobalRedoLogDesc.EntryCount[i] != 0 ) {
+ Flags |= NETLOGON_REDO_NEEDED | NETLOGON_REPLICATION_NEEDED;
+ }
+ }
+ LeaveCriticalSection( &NlGlobalDbInfoCritSect );
+
+ }
+
+ //
+ // Fill in the return buffer
+ //
+
+ QueryInformation->NetlogonInfo1->netlog1_flags = Flags;
+ if ( NlGlobalRole == RolePrimary ) {
+ QueryInformation->NetlogonInfo1->netlog1_pdc_connection_status =
+ NERR_Success;
+ } else {
+ QueryInformation->NetlogonInfo1->netlog1_pdc_connection_status =
+ NetpNtStatusToApiStatus(
+ NlGlobalClientSession->CsConnectionStatus);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ NetStatus = NERR_Success;
+
+ //
+ // Free up locally used resources.
+ //
+Cleanup:
+
+ if( ClientSession != NULL ) {
+ NlUnrefClientSession( ClientSession );
+ }
+
+ if ( NetStatus != NERR_Success ) {
+
+ if ( QueryInformation->NetlogonInfo1 != NULL ) {
+ MIDL_user_free( QueryInformation->NetlogonInfo1 );
+ QueryInformation->NetlogonInfo1 = NULL;
+ }
+
+ if ( TDCName != NULL ) {
+ MIDL_user_free( TDCName );
+ }
+ if ( TrustedDomainName != NULL ) {
+ MIDL_user_free( TrustedDomainName );
+ }
+
+ }
+
+ return NetStatus;
+}
+
+
+NET_API_STATUS
+NetrGetAnyDCName (
+ IN LPWSTR ServerName OPTIONAL,
+ IN LPWSTR DomainName OPTIONAL,
+ OUT LPWSTR *Buffer
+ )
+
+/*++
+
+Routine Description:
+
+ Get the name of the any domain controller for a trusted domain.
+
+ The domain controller found in guaranteed to have be up at one point during
+ this API call.
+
+Arguments:
+
+ ServerName - name of remote server (null for local)
+
+ DomainName - name of domain (null for primary domain)
+
+ Buffer - Returns a pointer to an allcated buffer containing the
+ servername of a DC of the domain. The server name is prefixed
+ by \\. The buffer should be deallocated using NetApiBufferFree.
+
+Return Value:
+
+ ERROR_SUCCESS - Success. Buffer contains DC name prefixed by \\.
+
+ ERROR_NO_LOGON_SERVERS - No DC could be found
+
+ ERROR_NO_SUCH_DOMAIN - The specified domain is not a trusted domain.
+
+ ERROR_NO_TRUST_LSA_SECRET - The client side of the trust relationship is
+ broken.
+
+ ERROR_NO_TRUST_SAM_ACCOUNT - The server side of the trust relationship is
+ broken or the password is broken.
+
+ ERROR_DOMAIN_TRUST_INCONSISTENT - The server that responded is not a proper
+ domain controller of the specified domain.
+
+--*/
+{
+ NTSTATUS Status;
+
+ UNICODE_STRING DomainNameString;
+ UNICODE_STRING UncDcName;
+
+ UNREFERENCED_PARAMETER( ServerName );
+
+ //
+ // Fill in the primary domain name if the caller didn't specify one.
+ //
+
+ if ( DomainName == NULL || *DomainName == L'\0' ) {
+ RtlInitUnicodeString( &DomainNameString, NlGlobalUnicodeDomainName );
+ } else {
+ RtlInitUnicodeString( &DomainNameString, DomainName );
+ }
+
+ Status = I_NetGetAnyDCName( &DomainNameString,
+ &UncDcName );
+
+ if ( !NT_SUCCESS(Status) ) {
+ return NetpNtStatusToApiStatus(Status);
+ }
+
+ *Buffer = UncDcName.Buffer;
+ return NERR_Success;
+
+
+}
+
+
+NTSTATUS
+I_NetGetAnyDCName (
+ IN PUNICODE_STRING DomainName,
+ OUT PUNICODE_STRING Buffer
+ )
+
+/*++
+
+Routine Description:
+
+ Get the name of the any domain controller for a trusted domain.
+
+ The domain controller found in guaranteed to have be up at one point during
+ this API call. The machine is also guaranteed to be a DC in the domain
+ specified.
+
+ The caller of this routine should not have any locks held (it calls the
+ LSA back in several instances). This routine may take some time to execute.
+
+Arguments:
+
+ DomainName - name of domain
+
+ UncDcName - Returns a pointer to an allcated buffer containing the
+ servername of a DC of the domain. The server name is prefixed
+ by \\. The buffer should be deallocated using MIDL_user_free.
+
+Return Value:
+
+ STATUS_SUCCESS - Success. Buffer contains DC name prefixed by \\.
+
+ STATUS_NO_LOGON_SERVERS - No DC could be found
+
+ STATUS_NO_SUCH_DOMAIN - The specified domain is not a trusted domain.
+
+ STATUS_NO_TRUST_LSA_SECRET - The client side of the trust relationship is
+ broken.
+
+ STATUS_NO_TRUST_SAM_ACCOUNT - The server side of the trust relationship is
+ broken or the password is broken.
+
+ STATUS_DOMAIN_TRUST_INCONSISTENT - The server that responded is not a proper
+ domain controller of the specified domain.
+
+
+--*/
+{
+ NTSTATUS Status;
+
+ PCLIENT_SESSION ClientSession = NULL;
+ ULONG DiscoveryDone = FALSE;
+
+ UNICODE_STRING UncDcNameString;
+ WCHAR UncDcName[UNCLEN+1];
+
+ LSA_HANDLE LsaHandle = NULL;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ PPOLICY_ACCOUNT_DOMAIN_INFO AccountDomain = NULL;
+ PPOLICY_PRIMARY_DOMAIN_INFO PrimaryDomain = NULL;
+
+ RtlInitUnicodeString( Buffer, NULL );
+
+ //
+ // If netlogon is not running (LSA is calling us directly),
+ // don't let the caller proceed.
+
+ if ( NlGlobalChangeLogNetlogonState != NetlogonStarted ) {
+ Status = STATUS_NETLOGON_NOT_STARTED;
+ goto Cleanup;
+ }
+
+ //
+ // On the PDC or BDC,
+ // find the Client session for the domain.
+ // On workstations,
+ // find the primary domain client session.
+ //
+
+ ClientSession = NlFindNamedClientSession( DomainName );
+
+ if ( ClientSession == NULL ) {
+ NlPrint((NL_CRITICAL,
+ "I_NetGetAnyDcName: %wZ: No such trusted domain\n",
+ DomainName ));
+ Status = STATUS_NO_SUCH_DOMAIN;
+ goto Cleanup;
+ }
+
+
+ //
+ // Don't give up unless we've done discovery.
+
+ do {
+
+ //
+ // If we don't currently know the name of the server,
+ // discover one.
+ //
+
+ if ( ClientSession->CsState == CS_IDLE ) {
+
+ //
+ // If we've tried to authenticate recently,
+ // don't bother trying again.
+ //
+
+ if ( !NlTimeToReauthenticate( ClientSession ) ) {
+ Status = ClientSession->CsConnectionStatus;
+ goto Cleanup;
+
+ }
+
+ //
+ // Discover a DC
+ //
+
+ if ( !NlTimeoutSetWriterClientSession( ClientSession, WRITER_WAIT_PERIOD ) ) {
+ NlPrint((NL_CRITICAL, "I_NetGetAnyDcName: Can't become writer of client session.\n" ));
+ Status = STATUS_NO_LOGON_SERVERS;
+ goto Cleanup;
+ }
+
+
+ // Check again now that we're the writer
+ if ( ClientSession->CsState == CS_IDLE ) {
+ Status = NlDiscoverDc( ClientSession, DT_Synchronous );
+
+ if ( !NT_SUCCESS(Status) ) {
+ NlResetWriterClientSession( ClientSession );
+
+ NlPrint((NL_CRITICAL,
+ "I_NetGetAnyDcName: %wZ: Discovery failed %lx\n",
+ DomainName,
+ Status ));
+ goto Cleanup;
+ }
+
+ DiscoveryDone = TRUE;
+ }
+
+ NlResetWriterClientSession( ClientSession );
+
+ }
+
+
+
+ //
+ // Capture a copy of the DC the session is to.
+ //
+
+ Status = NlCaptureServerClientSession( ClientSession, UncDcName );
+
+ if ( !NT_SUCCESS(Status) ) {
+ continue;
+ }
+
+
+ //
+ // Cleanup resources from the previous iteration of the loop
+ //
+
+ if ( LsaHandle != NULL ) {
+ (VOID) LsaClose( LsaHandle );
+ LsaHandle = NULL;
+ }
+
+ if ( AccountDomain != NULL ) {
+ (VOID) LsaFreeMemory( AccountDomain );
+ AccountDomain= NULL;
+ }
+
+ if ( PrimaryDomain != NULL ) {
+ (VOID) LsaFreeMemory( PrimaryDomain );
+ PrimaryDomain = NULL;
+ }
+
+
+ //
+ // Open the policy database on the machine and query its primary and
+ // account domains.
+ //
+
+ RtlInitUnicodeString( &UncDcNameString, UncDcName );
+
+ InitializeObjectAttributes( &ObjectAttributes, NULL, 0, NULL, NULL );
+
+ Status = LsaOpenPolicy( &UncDcNameString,
+ &ObjectAttributes,
+ POLICY_VIEW_LOCAL_INFORMATION,
+ &LsaHandle );
+
+ if ( !NT_SUCCESS(Status) ) {
+
+ NlPrint((NL_CRITICAL,
+ "I_NetGetAnyDcName: %wZ"
+ ": LsaOpenPolicy failed on " FORMAT_LPWSTR " %lx\n",
+ DomainName,
+ UncDcName,
+ Status ));
+
+ Status = STATUS_NO_LOGON_SERVERS;
+ if ( !NlTimeoutSetWriterClientSession( ClientSession, WRITER_WAIT_PERIOD ) ) {
+ NlPrint((NL_CRITICAL, "I_NetGetAnyDcName: Can't become writer of client session.\n" ));
+ Status = STATUS_NO_LOGON_SERVERS;
+ goto Cleanup;
+ }
+ NlSetStatusClientSession( ClientSession, Status );
+ NlResetWriterClientSession( ClientSession );
+ continue;
+ }
+
+ Status = LsaQueryInformationPolicy( LsaHandle,
+ PolicyPrimaryDomainInformation,
+ &PrimaryDomain );
+
+ if ( !NT_SUCCESS(Status) ) {
+
+ NlPrint((NL_CRITICAL,
+ "I_NetGetAnyDcName: %wZ: LsaQueryInformationPolicy "
+ "(Primary) failed on " FORMAT_LPWSTR " %lx\n",
+ DomainName,
+ UncDcName,
+ Status ));
+
+ Status = STATUS_NO_LOGON_SERVERS;
+ if ( !NlTimeoutSetWriterClientSession( ClientSession, WRITER_WAIT_PERIOD ) ) {
+ NlPrint((NL_CRITICAL, "I_NetGetAnyDcName: Can't become writer of client session.\n" ));
+ Status = STATUS_NO_LOGON_SERVERS;
+ goto Cleanup;
+ }
+ NlSetStatusClientSession( ClientSession, Status );
+ NlResetWriterClientSession( ClientSession );
+ continue;
+ }
+
+ Status = LsaQueryInformationPolicy( LsaHandle,
+ PolicyAccountDomainInformation,
+ &AccountDomain );
+
+ if ( !NT_SUCCESS(Status) ) {
+
+ NlPrint((NL_CRITICAL,
+ "I_NetGetAnyDcName: %wZ: LsaQueryInformationPolicy "
+ "(Account) failed on " FORMAT_LPWSTR " %lx\n",
+ DomainName,
+ UncDcName,
+ Status ));
+
+ Status = STATUS_NO_LOGON_SERVERS;
+ if ( !NlTimeoutSetWriterClientSession( ClientSession, WRITER_WAIT_PERIOD ) ) {
+ NlPrint((NL_CRITICAL, "I_NetGetAnyDcName: Can't become writer of client session.\n" ));
+ Status = STATUS_NO_LOGON_SERVERS;
+ goto Cleanup;
+ }
+ NlSetStatusClientSession( ClientSession, Status );
+ NlResetWriterClientSession( ClientSession );
+ continue;
+ }
+
+
+ //
+ // Ensure the machine is really a member of the queried domain.
+ //
+
+ if ( !RtlEqualDomainName( DomainName, &PrimaryDomain->Name ) ) {
+
+ Status = STATUS_DOMAIN_TRUST_INCONSISTENT;
+
+ NlPrint((NL_CRITICAL,
+ "I_NetGetAnyDcName: %wZ: "
+ "Domain name mismatch %wZ from " FORMAT_LPWSTR ".\n",
+ DomainName,
+ &PrimaryDomain->Name,
+ UncDcName ));
+
+ if ( !NlTimeoutSetWriterClientSession( ClientSession, WRITER_WAIT_PERIOD ) ) {
+ NlPrint((NL_CRITICAL, "I_NetGetAnyDcName: Can't become writer of client session.\n" ));
+ Status = STATUS_NO_LOGON_SERVERS;
+ goto Cleanup;
+ }
+ NlSetStatusClientSession( ClientSession, Status );
+ NlResetWriterClientSession( ClientSession );
+ continue;
+ }
+
+ //
+ // Ensure the machine is still a DC.
+ //
+
+ if ( AccountDomain->DomainSid == NULL ||
+ PrimaryDomain->Sid == NULL ||
+ !RtlEqualSid( AccountDomain->DomainSid,
+ PrimaryDomain->Sid ) ) {
+
+ NlPrint((NL_CRITICAL,
+ "I_NetGetAnyDcName: %wZ: "
+ "Not-LanManNt mismatch from " FORMAT_LPWSTR ".\n",
+ DomainName,
+ UncDcName ));
+
+ Status = STATUS_DOMAIN_TRUST_INCONSISTENT;
+ if ( !NlTimeoutSetWriterClientSession( ClientSession, WRITER_WAIT_PERIOD ) ) {
+ NlPrint((NL_CRITICAL, "I_NetGetAnyDcName: Can't become writer of client session.\n" ));
+ Status = STATUS_NO_LOGON_SERVERS;
+ goto Cleanup;
+ }
+ NlSetStatusClientSession( ClientSession, Status );
+ NlResetWriterClientSession( ClientSession );
+ continue;
+ }
+
+ //
+ // Ensure the domain has the right sid.
+ //
+
+ if ( PrimaryDomain->Sid == NULL ||
+ !RtlEqualSid( ClientSession->CsDomainId,
+ PrimaryDomain->Sid ) ) {
+
+ NlPrint((NL_CRITICAL,
+ "I_NetGetAnyDcName: %wZ: "
+ "Sid mismatch from " FORMAT_LPWSTR ".\n",
+ DomainName,
+ UncDcName ));
+
+ Status = STATUS_DOMAIN_TRUST_INCONSISTENT;
+ if ( !NlTimeoutSetWriterClientSession( ClientSession, WRITER_WAIT_PERIOD ) ) {
+ NlPrint((NL_CRITICAL, "I_NetGetAnyDcName: Can't become writer of client session.\n" ));
+ Status = STATUS_NO_LOGON_SERVERS;
+ goto Cleanup;
+ }
+ NlSetStatusClientSession( ClientSession, Status );
+ NlResetWriterClientSession( ClientSession );
+ continue;
+ }
+
+
+ //
+ // We've found it.
+ //
+
+ Status = STATUS_SUCCESS;
+
+ } while ( !NT_SUCCESS(Status) && !DiscoveryDone );
+
+
+
+ //
+ // Free any locally used resources.
+ //
+Cleanup:
+
+ //
+ // Don't divulge too much to the caller.
+ //
+
+ if ( Status == STATUS_ACCESS_DENIED ) {
+ Status = STATUS_NO_TRUST_SAM_ACCOUNT;
+ }
+
+ if ( ClientSession != NULL ) {
+ NlUnrefClientSession( ClientSession );
+ }
+
+ if ( LsaHandle != NULL ) {
+ (VOID) LsaClose( LsaHandle );
+ }
+
+ if ( AccountDomain != NULL ) {
+ (VOID) LsaFreeMemory( AccountDomain );
+ }
+
+ if ( PrimaryDomain != NULL ) {
+ (VOID) LsaFreeMemory( PrimaryDomain );
+ }
+
+
+
+ //
+ // Return the DCName to the caller.
+ //
+
+ if ( NT_SUCCESS(Status) ) {
+ LPWSTR AllocatedUncDcName;
+
+ AllocatedUncDcName = NetpAllocWStrFromWStr( UncDcName );
+
+ if ( AllocatedUncDcName == NULL ) {
+ Status = STATUS_NO_MEMORY;
+ } else {
+ RtlInitUnicodeString( Buffer, AllocatedUncDcName );
+ }
+ }
+
+ return Status;
+}
diff --git a/private/net/svcdlls/logonsrv/server/ssiapi.h b/private/net/svcdlls/logonsrv/server/ssiapi.h
new file mode 100644
index 000000000..cc973de2d
--- /dev/null
+++ b/private/net/svcdlls/logonsrv/server/ssiapi.h
@@ -0,0 +1,81 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ ssiapi.h
+
+Abstract:
+
+ Declartions of APIs used between Netlogon Services for the NT to NT case.
+
+Author:
+
+ Cliff Van Dyke (cliffv) 25-Jul-1991
+
+Environment:
+
+ User mode only.
+ Contains NT-specific code.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+--*/
+
+//////////////////////////////////////////////////////////////////////
+//
+// API Interfaces used only between Netlogon and itself.
+//
+//////////////////////////////////////////////////////////////////////
+
+
+NTSTATUS
+I_NetDatabaseDeltas (
+ IN LPWSTR PrimaryName,
+ IN LPWSTR ComputerName,
+ IN PNETLOGON_AUTHENTICATOR Authenticator,
+ OUT PNETLOGON_AUTHENTICATOR ReturnAuthenticator,
+ IN DWORD DatabaseID,
+ IN OUT PNLPR_MODIFIED_COUNT DomainModifiedCount,
+ OUT PNETLOGON_DELTA_ENUM_ARRAY *DeltaArray,
+ IN DWORD PreferredMaximumLength
+ );
+
+NTSTATUS
+I_NetDatabaseSync (
+ IN LPWSTR PrimaryName,
+ IN LPWSTR ComputerName,
+ IN PNETLOGON_AUTHENTICATOR Authenticator,
+ OUT PNETLOGON_AUTHENTICATOR ReturnAuthenticator,
+ IN DWORD DatabaseID,
+ IN OUT PULONG SamSyncContext,
+ OUT PNETLOGON_DELTA_ENUM_ARRAY *DeltaArray,
+ IN DWORD PreferredMaximumLength
+ );
+
+NTSTATUS
+I_NetDatabaseSync2 (
+ IN LPWSTR PrimaryName,
+ IN LPWSTR ComputerName,
+ IN PNETLOGON_AUTHENTICATOR Authenticator,
+ OUT PNETLOGON_AUTHENTICATOR ReturnAuthenticator,
+ IN DWORD DatabaseID,
+ IN SYNC_STATE RestartState,
+ IN OUT PULONG SamSyncContext,
+ OUT PNETLOGON_DELTA_ENUM_ARRAY *DeltaArray,
+ IN DWORD PreferredMaximumLength
+ );
+
+NTSTATUS
+I_NetDatabaseRedo (
+ IN LPWSTR PrimaryName,
+ IN LPWSTR ComputerName,
+ IN PNETLOGON_AUTHENTICATOR Authenticator,
+ OUT PNETLOGON_AUTHENTICATOR ReturnAuthenticator,
+ IN LPBYTE ChangeLogEntry,
+ IN DWORD ChangeLogEntrySize,
+ OUT PNETLOGON_DELTA_ENUM_ARRAY *DeltaArray
+ );
+
diff --git a/private/net/svcdlls/logonsrv/server/ssiauth.c b/private/net/svcdlls/logonsrv/server/ssiauth.c
new file mode 100644
index 000000000..16bd7cf2e
--- /dev/null
+++ b/private/net/svcdlls/logonsrv/server/ssiauth.c
@@ -0,0 +1,788 @@
+/*++
+
+Copyright (c) 1987-1991 Microsoft Corporation
+
+Module Name:
+
+ ssiauth.c
+
+Abstract:
+
+ Authentication related functions
+
+Author:
+
+ Ported from Lan Man 2.0
+
+Environment:
+
+ User mode only.
+ Contains NT-specific code.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+ 12-Jul-1991 (cliffv)
+ Ported to NT. Converted to NT style.
+
+--*/
+
+//
+// Common include files.
+//
+
+#include <logonsrv.h> // Include files common to entire service
+
+//
+// Include files specific to this .c file
+//
+
+#include <lmerr.h> // NERR_*
+
+
+LONG NlGlobalSessionCounter = 0;
+
+
+VOID
+NlMakeSessionKey(
+ IN PNT_OWF_PASSWORD CryptKey,
+ IN PNETLOGON_CREDENTIAL ClientChallenge,
+ IN PNETLOGON_CREDENTIAL ServerChallenge,
+ OUT PNETLOGON_SESSION_KEY SessionKey
+ )
+/*++
+
+Routine Description:
+
+ Build an encryption key for use in authentication for
+ this RequestorName.
+
+Arguments:
+
+ CryptKey -- The OWF password of the user account being used.
+
+ ClientChallenge -- 8 byte (64 bit) number generated by caller
+
+ ServerChallenge -- 8 byte (64 bit) number generated by primary
+
+ SessionKey -- 8 byte (64 bit) number generated at both ends
+
+Return Value:
+
+ TRUE: Success
+ FALSE: Failure
+
+ NT status code.
+
+--*/
+{
+ NTSTATUS Status;
+ BLOCK_KEY BlockKey;
+ NETLOGON_SESSION_KEY TempSessionKey;
+
+ //
+ // we will have a 112 bit key (64 bit encrypted rest padded with 0s)
+ //
+
+ RtlZeroMemory(SessionKey, sizeof(NETLOGON_SESSION_KEY));
+
+ //
+ // SessionKey = C + P (arithmetic sum ignore carry)
+ //
+
+ *((unsigned long * ) SessionKey) =
+ *((unsigned long * ) ClientChallenge) +
+ *((unsigned long * ) ServerChallenge);
+
+ *((unsigned long * )((LPBYTE)SessionKey + 4)) =
+ *((unsigned long * )((LPBYTE)ClientChallenge + 4)) +
+ *((unsigned long * )((LPBYTE)ServerChallenge + 4));
+
+
+ //
+ // CryptKey is our 16 byte key to be used as described in codespec
+ // use first 7 bytes of CryptKey for first encryption
+ //
+
+ RtlCopyMemory( &BlockKey, CryptKey, BLOCK_KEY_LENGTH );
+
+ Status = RtlEncryptBlock(
+ (PCLEAR_BLOCK) SessionKey, // Clear text
+ &BlockKey, // Key
+ (PCYPHER_BLOCK) &TempSessionKey); // Cypher Block
+
+ NlAssert( NT_SUCCESS( Status ) );
+
+
+ //
+ // Further encrypt the encrypted "SessionKey" using upper 7 bytes
+ //
+
+ NlAssert( LM_OWF_PASSWORD_LENGTH == 2*BLOCK_KEY_LENGTH+2 );
+
+ RtlCopyMemory( &BlockKey,
+ ((PUCHAR)CryptKey) + 2 + BLOCK_KEY_LENGTH,
+ BLOCK_KEY_LENGTH );
+
+ Status = RtlEncryptBlock(
+ (PCLEAR_BLOCK) &TempSessionKey, // Clear text
+ &BlockKey, // Key
+ (PCYPHER_BLOCK) SessionKey); // Cypher Block
+
+ NlAssert( NT_SUCCESS( Status ) );
+
+ return;
+}
+
+
+NTSTATUS
+NlCheckAuthenticator(
+ IN OUT PSERVER_SESSION ServerSession,
+ IN PNETLOGON_AUTHENTICATOR Authenticator,
+ OUT PNETLOGON_AUTHENTICATOR ReturnAuthenticator
+ )
+/*++
+
+Routine Description:
+
+ Verify that supplied Authenticator is valid.
+ It is intended for use by the server side after initial authentication
+ has succeeded. This routine will modify the seed by
+ first adding the time-of-day received from the Authenticator
+ and then by incrementing it.
+
+ A ReturnAuthenticator is built based on the final seed.
+
+Arguments:
+
+ ServerSession - Pointer to the ServerSession structure. The following
+ fields are used:
+
+ SsAuthenticationSeed - Supplies the seed used for authentication and
+ returns the updated seed.
+
+ SsSessionKey - The session key used for encryption.
+
+ SsCheck - Is zeroed to indicate successful communication with the client.
+
+ Authenticator - The authenticator passed by the caller.
+
+ ReturnAuthenticator - The authenticator we'll return to the caller.
+
+Return Value:
+
+ STATUS_SUCCESS;
+ STATUS_ACCESS_DENIED;
+ STATUS_TIME_DIFFERENCE_AT_DC;
+
+--*/
+{
+
+ NETLOGON_CREDENTIAL TargetCredential;
+
+
+#ifdef notdef // Doesn't work if caller in different time zone
+
+ LARGE_INTEGER TimeNow;
+ long timeofday;
+ long timediff;
+
+ //
+ // First check if time-of-day is rational.
+ //
+
+ NtQuerySystemTime( &TimeNow );
+ RtlTimeToSecondsSince1970( &TimeNow, &timeofday );
+
+ timediff = timeofday - Authenticator->timestamp;
+ if (timediff < 0) {
+ timediff = Authenticator->timestamp - timeofday;
+ }
+
+ if (timediff > RATIONAL_TIME) {
+ return STATUS_TIME_DIFFERENCE_AT_DC;
+ }
+#endif // notdef
+
+
+#ifdef BAD_ALIGNMENT
+ NlPrint((NL_CHALLENGE_RES,"NlCheckAuthenticator: Seed = %lx %lx\n",
+ ((DWORD *) (&ServerSession->SsAuthenticationSeed))[0],
+ ((DWORD *) (&ServerSession->SsAuthenticationSeed))[1]));
+
+
+ NlPrint((NL_CHALLENGE_RES,
+ "NlCheckAuthenticator: SessionKey = %lx %lx %lx %lx\n",
+ ((DWORD *) (&ServerSession->SsSessionKey))[0],
+ ((DWORD *) (&ServerSession->SsSessionKey))[1],
+ ((DWORD *) (&ServerSession->SsSessionKey))[2],
+ ((DWORD *) (&ServerSession->SsSessionKey))[3]));
+
+ NlPrint((NL_CHALLENGE_RES,
+ "NlCheckAuthenticator: Client Authenticator GOT = %lx %lx\n",
+ ((DWORD *) (&Authenticator->Credential))[0],
+ ((DWORD *) (&Authenticator->Credential))[1]));
+
+ NlPrint((NL_CHALLENGE_RES,
+ "NlCheckAuthenticator: Time = %lx\n",
+ ((DWORD *) (&Authenticator->timestamp))[0] ));
+#endif // BAD_ALIGNMENT
+
+
+
+ //
+ // modify the seed before computing auth_credential for verification
+ // Two long words are added and overflow carry (if any) ignored
+ // This will leave upper 4 bytes unchanged
+ //
+
+ *((unsigned long * ) &ServerSession->SsAuthenticationSeed) += Authenticator->timestamp;
+
+
+#ifdef BAD_ALIGNMENT
+ NlPrint((NL_CHALLENGE_RES,
+ "NlCheckAuthenticator: Seed + TIME = %lx %lx\n",
+ ((DWORD *) (&ServerSession->SsAuthenticationSeed))[0],
+ ((DWORD *) (&ServerSession->SsAuthenticationSeed))[1]));
+#endif // BAD_ALIGNMENT
+
+
+ //
+ // Compute TargetCredential to verify the one supplied in the Authenticator
+ //
+
+ NlComputeCredentials( &ServerSession->SsAuthenticationSeed,
+ &TargetCredential,
+ &ServerSession->SsSessionKey );
+
+
+#ifdef BAD_ALIGNMENT
+ NlPrint((NL_CHALLENGE_RES,
+ "NlCheckAuthenticator: Client Authenticator MADE = %lx %lx\n",
+ ((DWORD *) (&TargetCredential))[0],
+ ((DWORD *) (&TargetCredential))[1]));
+#endif // BAD_ALIGNMENT
+
+ //
+ // verify the computed credentials with those supplied
+ // Authenticator must have used seed + time_of_day as seed
+ //
+
+ if (RtlCompareMemory( &Authenticator->Credential,
+ &TargetCredential,
+ sizeof(TargetCredential)) !=
+ sizeof(TargetCredential)) {
+ return STATUS_ACCESS_DENIED;
+ }
+
+ //
+ // modify our seed before computing the ReturnAuthenticator.
+ // The requestor will increment his seed if he matches this credentials.
+ //
+
+ (*((unsigned long * ) &ServerSession->SsAuthenticationSeed))++;
+
+ //
+ // compute ClientCredential to send back to requestor
+ //
+
+ NlComputeCredentials( &ServerSession->SsAuthenticationSeed,
+ &ReturnAuthenticator->Credential,
+ &ServerSession->SsSessionKey);
+
+
+#ifdef BAD_ALIGNMENT
+ NlPrint((NL_CHALLENGE_RES,
+ "NlCheckAuthenticator: Server Authenticator SEND = %lx %lx\n",
+ ((DWORD *) (&ReturnAuthenticator->Credential))[0],
+ ((DWORD *) (&ReturnAuthenticator->Credential))[1]));
+
+
+ NlPrint((NL_CHALLENGE_RES,
+ "NlCheckAuthenticator: Seed + time + 1= %lx %lx\n",
+ ((DWORD *) (&ServerSession->SsAuthenticationSeed))[0],
+ ((DWORD *) (&ServerSession->SsAuthenticationSeed))[1]));
+#endif // BAD_ALIGNMENT
+
+
+ //
+ // Indicate successful communication with the client
+ //
+
+ ServerSession->SsCheck = 0;
+ ServerSession->SsPulseTimeoutCount = 0;
+ ServerSession->SsFlags &= ~SS_PULSE_SENT;
+
+ return STATUS_SUCCESS;
+
+}
+
+
+VOID
+NlComputeCredentials(
+ IN PNETLOGON_CREDENTIAL Challenge,
+ OUT PNETLOGON_CREDENTIAL Credential,
+ IN PNETLOGON_SESSION_KEY SessionKey
+ )
+/*++
+
+Routine Description:
+
+ Calculate the credentials by encrypting the 8 byte
+ challenge with first 7 bytes of sessionkey and then
+ further encrypting it by next 7 bytes of sessionkey.
+
+Arguments:
+
+ Challenge - Supplies the 8 byte (64 bit) challenge
+
+ Credential - Returns the 8 byte (64 bit) number generated
+
+ SessionKey 14 byte (112 bit) encryption key
+
+Return Value:
+
+ NONE
+
+--*/
+{
+ NTSTATUS Status;
+ BLOCK_KEY BlockKey;
+ CYPHER_BLOCK IntermediateBlock;
+
+ RtlZeroMemory(Credential, sizeof(*Credential));
+
+ //
+ // use first 7 bytes of SessionKey for first encryption
+ //
+
+ RtlCopyMemory( &BlockKey, SessionKey, BLOCK_KEY_LENGTH );
+
+ Status = RtlEncryptBlock( (PCLEAR_BLOCK) Challenge, // Cleartext
+ &BlockKey, // Key
+ &IntermediateBlock ); // Cypher Block
+
+ NlAssert( NT_SUCCESS(Status) );
+
+ //
+ // further encrypt the encrypted Credential using next 7 bytes
+ //
+
+ RtlCopyMemory( &BlockKey,
+ ((PUCHAR)SessionKey) + BLOCK_KEY_LENGTH,
+ BLOCK_KEY_LENGTH );
+
+ Status = RtlEncryptBlock( (PCLEAR_BLOCK) &IntermediateBlock, // Cleartext
+ &BlockKey, // Key
+ Credential ); // Cypher Block
+
+ NlAssert( NT_SUCCESS(Status) );
+
+ return;
+
+}
+
+
+
+VOID
+NlComputeChallenge(
+ OUT PNETLOGON_CREDENTIAL Challenge
+ )
+
+/*++
+
+Routine Description:
+
+ Generates a 64 bit challenge
+
+ Make an 8 byte seed by filling in BigTime i.e. seconds
+ since Jan 1 1970 in lower four bytes and a counter in
+ upper four bytes. Counter is incremented after each use.
+ This seed is used as encryption key to encrypt standard
+ text which will be used as challenge.
+
+Arguments:
+
+ Challenge - Returns the computed challenge
+
+Return Value:
+
+ None.
+
+--*/
+{
+ NTSTATUS Status;
+ char Seed[PWLEN];
+ LM_OWF_PASSWORD BigChallenge;
+ LARGE_INTEGER TimeNow;
+
+
+ RtlZeroMemory(Seed, sizeof(Seed) );
+
+ //
+ // we need to remember ClientChallenge and RequestorName for future use
+ // put these into shared seg SSISEG
+ // NlGlobalSessionCounter is a global initialized to 0 at UAS init time
+ //
+
+
+ Status = NtQuerySystemTime( &TimeNow );
+ NlAssert( NT_SUCCESS(Status) );
+
+ Status = RtlTimeToSecondsSince1970( &TimeNow, ((unsigned long * ) Seed) );
+ NlAssert( NT_SUCCESS(Status) );
+
+ *((unsigned long * ) & Seed[4]) = NlGlobalSessionCounter++;
+
+ //
+ // Create ClientChallenge
+ //
+ // NOTE: RtlCalculateLmOwfPassword() will generate 16 byte txt
+ //
+
+ Status = RtlCalculateLmOwfPassword(Seed, &BigChallenge);
+ NlAssert( NT_SUCCESS(Status) );
+
+ //
+ // we need (or will use) only 8 bytes of this info
+ //
+
+ RtlCopyMemory(Challenge, &BigChallenge, sizeof(Challenge) );
+
+ return;
+}
+
+
+
+VOID
+NlBuildAuthenticator(
+ IN OUT PNETLOGON_CREDENTIAL AuthenticationSeed,
+ IN PNETLOGON_SESSION_KEY SessionKey,
+ OUT PNETLOGON_AUTHENTICATOR Authenticator
+ )
+/*++
+
+Routine Description:
+
+ Build the authenticator to be sent to primary.
+ This routine will modify the seed by adding the
+ time-of-day before computing the credentials.
+
+Arguments:
+
+ AuthenticationSeed -- The current authentication seed. This seed will
+ have the current time of day added to it prior to building the
+ Authenticator.
+
+ SessionKey - The Session Key used for encrypting the Authenticator.
+
+ Authenticator - The Authenticator to pass to the PDC for the current
+ call.
+
+Return Value:
+
+ NT Status code
+
+--*/
+{
+ NTSTATUS Status;
+ LARGE_INTEGER TimeNow;
+
+ //
+ // Use the current time of day to modify the authentication seed
+ //
+
+ RtlZeroMemory(Authenticator, sizeof(*Authenticator));
+
+ Status = NtQuerySystemTime( &TimeNow );
+ NlAssert( NT_SUCCESS(Status) );
+
+ Status = RtlTimeToSecondsSince1970( &TimeNow, &Authenticator->timestamp );
+ NlAssert( NT_SUCCESS(Status) );
+
+ //
+ // Modify the AuthenticationSeed before computing auth_credential for
+ // verification .
+ //
+ // Two long words are added and overflow carry (if any) ignored
+ // This will leave upper 4 bytes unchanged
+ //
+
+
+#ifdef BAD_ALIGNMENT
+ NlPrint((NL_CHALLENGE_RES,"NlBuildAuthenticator: Old Seed = %lx %lx\n",
+ ((DWORD *) (AuthenticationSeed))[0],
+ ((DWORD *) (AuthenticationSeed))[1]));
+
+ NlPrint((NL_CHALLENGE_RES,"NlBuildAuthenticator: Time = %lx\n",
+ ((DWORD *) (&Authenticator->timestamp))[0] ));
+#endif // BAD_ALIGNMENT
+
+
+
+ *((unsigned long * ) AuthenticationSeed) += Authenticator->timestamp;
+
+
+#ifdef BAD_ALIGNMENT
+ NlPrint((NL_CHALLENGE_RES,"NlBuildAuthenticator: New Seed = %lx %lx\n",
+ ((DWORD *) (AuthenticationSeed))[0],
+ ((DWORD *) (AuthenticationSeed))[1]));
+
+
+ NlPrint((NL_CHALLENGE_RES,
+ "NlBuildAuthenticator: SessionKey = %lx %lx %lx %lx\n",
+ ((DWORD *) (SessionKey))[0],
+ ((DWORD *) (SessionKey))[1],
+ ((DWORD *) (SessionKey))[2],
+ ((DWORD *) (SessionKey))[3]));
+#endif // BAD_ALIGNMENT
+
+
+ //
+ // compute AuthenticationSeed to verify the one supplied by Requestor
+ //
+
+ NlComputeCredentials( AuthenticationSeed,
+ &Authenticator->Credential,
+ SessionKey);
+
+
+#ifdef BAD_ALIGNMENT
+ NlPrint((NL_CHALLENGE_RES,"NlBuildAuthenticator: Client Authenticator = %lx %lx\n",
+ ((DWORD *) (&Authenticator->Credential))[0],
+ ((DWORD *) (&Authenticator->Credential))[1]));
+#endif // BAD_ALIGNMENT
+
+
+ return;
+
+}
+
+
+BOOL
+NlUpdateSeed(
+ IN OUT PNETLOGON_CREDENTIAL AuthenticationSeed,
+ IN PNETLOGON_CREDENTIAL TargetCredential,
+ IN PNETLOGON_SESSION_KEY SessionKey
+ )
+/*++
+
+Routine Description:
+
+ Called by the initiator of a communication over the secure channel
+ following a successful transaction.
+
+ The PDC would have incremented the seed so we must do so also.
+
+ We also verify that the incremented seed builds a credential identical
+ to the one passed back by the PDC.
+
+Arguments:
+
+ AuthenticationSeed - Pointer to the AuthenticationSeed to be incremented.
+
+ TargetCredential - Supplies the Credential that the incremented
+ AuthenticationSeed should encrypt to.
+
+ SessionKey - Supplies the encryption key to use for the encryption.
+
+Return Value:
+
+ TRUE: Success
+ FALSE: Failure
+
+--*/
+{
+ NETLOGON_CREDENTIAL NewCredential;
+
+ //
+ // modify our AuthenticationSeed before computing NewCredential to check
+ // those returned from primary (NewSeed = AuthenticationSeed+1)
+ //
+
+ (*((unsigned long * ) AuthenticationSeed))++;
+
+
+#ifdef BAD_ALIGNMENT
+ NlPrint((NL_CHALLENGE_RES,"NlUpdateSeed: Seed + time + 1= %lx %lx\n",
+ ((DWORD *) (AuthenticationSeed))[0],
+ ((DWORD *) (AuthenticationSeed))[1]));
+#endif // BAD_ALIGNMENT
+
+
+ //
+ // Compute ClientCredential to check which came from primary
+ //
+
+ NlComputeCredentials(AuthenticationSeed, &NewCredential, SessionKey);
+
+
+#ifdef BAD_ALIGNMENT
+ NlPrint((NL_CHALLENGE_RES,"NlUpdateSeed: Server Authenticator GOT = %lx %lx\n",
+ ((DWORD *) (TargetCredential))[0],
+ ((DWORD *) (TargetCredential))[1]));
+
+
+ NlPrint((NL_CHALLENGE_RES,"NlUpdateSeed: Server Authenticator MADE = %lx %lx\n",
+ ((DWORD *) (&NewCredential))[0],
+ ((DWORD *) (&NewCredential))[1]));
+#endif // BAD_ALIGNMENT
+
+
+ if (RtlCompareMemory( TargetCredential,
+ &NewCredential,
+ sizeof(NewCredential)) !=
+ sizeof(NewCredential)) {
+ return FALSE;
+ }
+
+ //
+ // Done
+ //
+
+ return TRUE;
+
+}
+
+
+VOID
+NlEncryptRC4(
+ IN OUT PVOID Buffer,
+ IN ULONG BufferSize,
+ IN PSESSION_INFO SessionInfo
+ )
+/*++
+
+Routine Description:
+
+ Encrypt data using RC4 with the session key as the key.
+
+Arguments:
+
+ Buffer -- Buffer containing the data to encrypt in place.
+
+ BufferSize -- Size (in bytes) of Buffer.
+
+ SessionInfo -- Info describing secure channel
+
+Return Value:
+
+ NT status code
+
+--*/
+{
+ NTSTATUS NtStatus;
+ DATA_KEY KeyData;
+ CRYPT_BUFFER Data;
+
+ //
+ // Build a data buffer to describe the encryption key.
+ //
+
+ KeyData.Length = sizeof(NETLOGON_SESSION_KEY);
+ KeyData.MaximumLength = sizeof(NETLOGON_SESSION_KEY);
+ KeyData.Buffer = (PVOID)&SessionInfo->SessionKey;
+
+ NlAssert( SessionInfo->NegotiatedFlags & NETLOGON_SUPPORTS_RC4_ENCRYPTION );
+
+ //
+ // Build a data buffer to decribe the encrypted data.
+ //
+
+ Data.Length = Data.MaximumLength = BufferSize;
+ Data.Buffer = Buffer;
+
+ //
+ // Encrypt the data.
+ //
+
+ IF_DEBUG( ENCRYPT ) {
+ NlPrint((NL_ENCRYPT, "NlEncryptRC4: Clear data\n" ));
+ NlpDumpHexData( NL_ENCRYPT,
+ (LPDWORD)Data.Buffer,
+ Data.Length / sizeof(DWORD) );
+ }
+
+ NtStatus = RtlEncryptData2( &Data, &KeyData );
+ NlAssert( NT_SUCCESS(NtStatus) );
+
+ IF_DEBUG( ENCRYPT ) {
+ NlPrint((NL_ENCRYPT, "NlEncryptRC4: Encrypted data\n" ));
+ NlpDumpHexData( NL_ENCRYPT,
+ (LPDWORD)Data.Buffer,
+ Data.Length / sizeof(DWORD) );
+ }
+
+}
+
+
+VOID
+NlDecryptRC4(
+ IN OUT PVOID Buffer,
+ IN ULONG BufferSize,
+ IN PSESSION_INFO SessionInfo
+ )
+/*++
+
+Routine Description:
+
+ Decrypt data using RC4 with the session key as the key.
+
+Arguments:
+
+ Buffer -- Buffer containing the data to decrypt in place.
+
+ BufferSize -- Size (in bytes) of Buffer.
+
+ SessionInfo -- Info describing secure channel
+
+Return Value:
+
+ NT status code
+
+--*/
+{
+ NTSTATUS NtStatus;
+ DATA_KEY KeyData;
+ CRYPT_BUFFER Data;
+
+ //
+ // Build a data buffer to describe the encryption key.
+ //
+
+ KeyData.Length = sizeof(NETLOGON_SESSION_KEY);
+ KeyData.MaximumLength = sizeof(NETLOGON_SESSION_KEY);
+ KeyData.Buffer = (PVOID)&SessionInfo->SessionKey;
+
+ NlAssert( SessionInfo->NegotiatedFlags & NETLOGON_SUPPORTS_RC4_ENCRYPTION );
+
+ //
+ // Build a data buffer to decribe the encrypted data.
+ //
+
+ Data.Length = Data.MaximumLength = BufferSize;
+ Data.Buffer = Buffer;
+
+ //
+ // Encrypt the data.
+ //
+
+
+ IF_DEBUG( ENCRYPT ) {
+ NlPrint((NL_ENCRYPT, "NlDecryptRC4: Encrypted data\n" ));
+ NlpDumpHexData( NL_ENCRYPT,
+ (LPDWORD)Data.Buffer,
+ Data.Length / sizeof(DWORD) );
+ }
+
+ NtStatus = RtlDecryptData2( &Data, &KeyData );
+ NlAssert( NT_SUCCESS(NtStatus) );
+
+ IF_DEBUG( ENCRYPT ) {
+ NlPrint((NL_ENCRYPT, "NlDecryptRC4: Clear data\n" ));
+ NlpDumpHexData( NL_ENCRYPT,
+ (LPDWORD)Data.Buffer,
+ Data.Length / sizeof(DWORD) );
+ }
+
+}
diff --git a/private/net/svcdlls/logonsrv/server/ssidelta.h b/private/net/svcdlls/logonsrv/server/ssidelta.h
new file mode 100644
index 000000000..afce04538
--- /dev/null
+++ b/private/net/svcdlls/logonsrv/server/ssidelta.h
@@ -0,0 +1,164 @@
+/*++
+
+Copyright (c) 1987-1991 Microsoft Corporation
+
+Module Name:
+
+ ssidelta.h
+
+Abstract:
+
+ Structure definitions for communicating deltas to downlevel UAS
+ BDC's and Member Servers.
+
+ This file contains information about the structures used for
+ replicating user and group records under SSI. These structs
+ are used to efficiently pack the information in these records.
+
+Author:
+
+ Ported from Lan Man 2.0
+
+Revision History:
+
+ 23-Aug-1991 (cliffv)
+ Ported to NT. Converted to NT style.
+
+ Madana - Fixed several bugs.
+
+--*/
+
+//
+// Force misalignment of the following structures
+//
+
+#ifndef NO_PACKING
+#include <packon.h>
+#endif // ndef NO_PACKING
+
+//
+// Valid flags to pass to an LM 2.x system
+//
+// Don't pass UF_HOMEDIR_REQUIRED since NT doesn't enforce the bit and
+// has no UI to set it. So, if the bit gets set by mistake (e.g., portuas or
+// netcmd) and there is no home directory, a lanman replicates forever because
+// of the bad data.
+
+#define UF_VALID_LM2X ( \
+ UF_SCRIPT | \
+ UF_ACCOUNTDISABLE | \
+ UF_PASSWD_NOTREQD | \
+ UF_PASSWD_CANT_CHANGE \
+ )
+
+//
+// Each UAS delta is prefixed by a 3-byte header. See NlPackUasHeader
+// for a description of the header.
+//
+
+#define NETLOGON_DELTA_HEADER_SIZE 3
+
+//
+// Maximum number of workstations allowed on downlevel systems
+//
+#define MAXWORKSTATIONS 8
+
+//
+// The header contains an opcode describing the particular delta.
+//
+
+#define DELTA_USERADD 1
+#define DELTA_USERDEL 2
+#define DELTA_USERSETINFO 3
+#define DELTA_USERSETGROUPS 4
+#define DELTA_USERMODALSSET 5
+#define DELTA_GROUPADD 6
+#define DELTA_GROUPDEL 7
+#define DELTA_GROUPSETINFO 8
+#define DELTA_GROUPADDUSER 9
+#define DELTA_GROUPDELUSER 10
+#define DELTA_GROUPSETUSERS 11
+#define DELTA_RESERVED_OPCODE 255
+
+
+typedef struct _USER_ADD_SET {
+ CHAR uas_password[LM_OWF_PASSWORD_LENGTH];
+ UCHAR uas_logon_hours[SAM_HOURS_PER_WEEK/8];
+ _ULONG( uas_password_age );
+ _USHORT( uas_priv );
+ _USHORT( uas_flags );
+ _ULONG( uas_auth_flags );
+ _ULONG( uas_last_logon );
+ _ULONG( uas_last_logoff );
+ _ULONG( uas_acct_expires );
+ _ULONG( uas_max_storage );
+ _USHORT( uas_units_per_week );
+ _USHORT( uas_bad_pw_count );
+ _USHORT( uas_num_logons );
+ _USHORT( uas_country_code );
+ _USHORT( uas_code_page );
+ _ULONG( uas_last );
+ CHAR uas_old_passwds[DEF_MAX_PWHIST * LM_OWF_PASSWORD_LENGTH];
+
+ //
+ // The following fields are misaligned and are only set via the
+ // NlPackVarLenField routine.
+ //
+
+ USHORT uas_name;
+ USHORT uas_home_dir;
+ USHORT uas_comment;
+ USHORT uas_script_path;
+ USHORT uas_full_name;
+ USHORT uas_usr_comment;
+ USHORT uas_parms;
+ USHORT uas_workstations;
+ USHORT uas_logon_server;
+} USER_ADD_SET, *PUSER_ADD_SET;
+
+
+
+typedef struct _USER_MODALS {
+ _USHORT( umod_min_passwd_len );
+ _ULONG( umod_max_passwd_age );
+ _ULONG( umod_min_passwd_age );
+ _ULONG( umod_force_logoff );
+ _USHORT( umod_password_hist_len );
+ _USHORT( umod_lockout_count );
+} USER_MODALS, *PUSER_MODALS;
+
+typedef struct _GROUP_ADD_SET {
+ USHORT gas_comment;
+ CHAR gas_groupname[1];
+} GROUP_ADD_SET, *PGROUP_ADD_SET;
+
+//
+// groupname followed by list of usernames. count == # users in list
+//
+typedef struct _GROUP_USERS {
+ _USHORT( count);
+ CHAR groupname[1];
+} GROUP_USERS, *PGROUP_USERS;
+
+//
+// username followed by list of groupnames. count == # groups in list
+//
+typedef struct _USER_GROUPS {
+ _USHORT( count);
+ CHAR username[1];
+} USER_GROUPS, *PUSER_GROUPS;
+
+//
+// username/groupname
+//
+typedef struct _USER_GROUP_DEL {
+ CHAR name[1];
+} USER_GROUP_DEL, *PUSER_GROUP_DEL;
+
+//
+// Turn structure packing back off
+//
+
+#ifndef NO_PACKING
+#include <packoff.h>
+#endif // ndef NO_PACKING
diff --git a/private/net/svcdlls/logonsrv/server/ssiinit.h b/private/net/svcdlls/logonsrv/server/ssiinit.h
new file mode 100644
index 000000000..857c29965
--- /dev/null
+++ b/private/net/svcdlls/logonsrv/server/ssiinit.h
@@ -0,0 +1,1375 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ ssiinit.h
+
+Abstract:
+
+ Private global variables, defines, and routine declarations used for
+ to implement SSI.
+
+Author:
+
+ Cliff Van Dyke (cliffv) 25-Jul-1991
+
+Environment:
+
+ User mode only.
+ Contains NT-specific code.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+ 02-Jan-1992 (madana)
+ added support for builtin/multidomain replication.
+
+ 04-10-1992 (madana)
+ added support for LSA replication.
+
+--*/
+
+//
+// srvsess.c will #include this file with INITSSI_ALLOCATE defined.
+// That will cause each of these variables to be allocated.
+//
+#ifdef INITSSI_ALLOCATE
+#define EXTERN
+#else
+#define EXTERN extern
+#endif
+
+// general purpose mainfests
+
+//
+// How frequently we scavenge the LogonTable.
+//
+#define LOGON_INTERROGATE_PERIOD (15*60*1000) // make it 15 mins
+
+
+//
+// How long we wait for a discovery response.
+//
+#define DISCOVERY_PERIOD (5*1000) // 5 seconds
+
+//
+// Maximum time we'll wait during full sync in an attempt to decrease
+// wan link utilization.
+//
+#define MAX_SYNC_SLEEP_TIME (60*60*1000) // 1 hour
+
+
+//
+// How big a buffer we request on a SAM delta or a SAM sync.
+//
+#define SAM_DELTA_BUFFER_SIZE (128*1024)
+
+//
+// The size of the largest mailslot message.
+//
+// All mailslot messages we receive are broadcast. The Win32 spec says
+// the limit on broadcast mailslot is 400 bytes. Really it is
+// 444 bytes (512 minus SMB header etc) - size of the mailslot name.
+// I'll use 444 to ensure this size is the largest I'll ever need.
+//
+
+#define NETLOGON_MAX_MS_SIZE 444
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Client Session definitions
+//
+/////////////////////////////////////////////////////////////////////////////
+
+//
+// An internal timer used to schedule a periodic event.
+//
+
+typedef struct _TIMER {
+ LARGE_INTEGER StartTime; // Start of period (NT absolute time)
+ DWORD Period; // length of period (miliseconds)
+} TIMER, *PTIMER;
+
+//
+// Client session.
+//
+// Structure to define the client side of a session to a DC.
+//
+
+typedef struct _CLIENT_SESSION {
+
+ //
+ // Each client session entry is in a doubly linked list defined by
+ // NlGlobalTrustList.
+ //
+ // Access serialized by NlGlobalTrustListCritSect.
+ //
+
+ LIST_ENTRY CsNext;
+
+
+ //
+ // Time when the last authentication attempt was made.
+ //
+ // When the CsState is CS_AUTHENTICATED, this field is the time the
+ // secure channel was setup.
+ //
+ // When the CsState is CS_IDLE, this field is the time of the last
+ // failed discovery or session setup. Or it may be zero, to indicate
+ // that it is OK to do another discovery at any time.
+ //
+ // When the CsState is CS_DC_PICKED, this field is zero indicating it is
+ // OK to do the session setup at any time. Or it may be the time of the
+ // last failed session set if different threads did the setup/discovery.
+ //
+ // Access serialized by NlGlobalDcDiscoveryCritSect
+ //
+
+ LARGE_INTEGER CsLastAuthenticationTry;
+
+ //
+ // Time when the last discovery attempt was made.
+ //
+ // The time is the time of completion of the last discovery attempt regardless
+ // of the success or failure of that attemp.
+ //
+ // Access serialized by NlGlobalDcDiscoveryCritSect
+ //
+
+ LARGE_INTEGER CsLastDiscoveryTime;
+
+ //
+ // Each API call made across this secure channel is timed by this timer.
+ // If the timer expires, the session to the server is forcefully
+ // terminated to ensure the client doesn't hang for a dead server.
+ //
+ // Access serialized by NlGlobalTrustListCritSect.
+ //
+
+ TIMER CsApiTimer;
+
+#define SHORT_API_CALL_PERIOD (45*1000) // Logon API lasts 45 seconds
+#define LONG_API_CALL_PERIOD (15*60*1000) // Replication API 15 minute
+#define BINDING_CACHE_PERIOD (3*60*1000) // Cache RPC handle for 3 minutes
+#define WRITER_WAIT_PERIOD NlGlobalShortApiCallPeriod // Max time to wait to become writer
+
+ //
+ // Name of the domain this connection is to
+ //
+
+ UNICODE_STRING CsDomainName;
+
+ //
+ // Domain ID of the domain this connection is to
+ //
+
+ PSID CsDomainId;
+
+ //
+ // Type of CsAccountName
+ //
+
+ NETLOGON_SECURE_CHANNEL_TYPE CsSecureChannelType;
+
+ //
+ // State of the connection to the server.
+ //
+ // Access serialized by NlGlobalDcDiscoveryCritSect
+ // This field can be read without the crit sect locked if
+ // the answer will only be used as a hint.
+ //
+
+ DWORD CsState;
+
+#define CS_IDLE 0 // No session is currently active
+#define CS_DC_PICKED 1 // The session has picked a DC for session
+#define CS_AUTHENTICATED 2 // The session is currently active
+
+
+ //
+ // Status of latest attempt to contact the server.
+ //
+ // When the CsState is CS_AUTHENTICATED, this field is STATUS_SUCCESS.
+ //
+ // When the CsState is CS_IDLE, this field is a non-successful status.
+ //
+ // When the CsState is CS_DC_PICKED, this field is the same non-successful
+ // status from when the CsState was last CS_IDLE.
+ //
+ // Access serialized by NlGlobalDcDiscoveryCritSect
+ // This field can be read without the crit sect locked if
+ // the answer will only be used as a hint.
+ //
+
+ NTSTATUS CsConnectionStatus;
+
+ //
+ // Access serialized by NlGlobalTrustListCritSect.
+ //
+
+ DWORD CsFlags;
+
+#define CS_UPDATE_PASSWORD 0x01 // Set if the password has already
+ // been changed on the client and
+ // needs changing on the server.
+
+#define CS_PASSWORD_REFUSED 0x02 // Set if DC refused a password change.
+
+#define CS_DELETE_ON_UNREF 0x04 // Delete entry when CsReferenceCount
+ // reaches zero.
+
+#define CS_WRITER 0x08 // Entry is being modified
+
+#define CS_HANDLE_TIMER 0x10 // Set if we need to handle timer expiration
+
+#define CS_CHECK_PASSWORD 0x20 // Set if we need to check the password
+
+#define CS_PICK_DC 0x40 // Set if we need to Pick a DC
+
+#define CS_REDISCOVER_DC 0x80 // Set when we need to Rediscover a DC
+
+#define CS_BINDING_CACHED 0x100 // Set if the binding handle is cached
+
+#define CS_HANDLE_API_TIMER 0x200 // Set if we need to handle API timer expiration
+
+ //
+ // Flags describing capabilities of both client and server.
+ //
+
+ ULONG CsNegotiatedFlags;
+
+ //
+ // Time Number of authentication attempts since last success.
+ //
+ // Access serialized by CsWriterSemaphore.
+ //
+
+ DWORD CsAuthAlertCount;
+
+ //
+ // Number of threads referencing this entry.
+ //
+ // Access serialized by NlGlobalTrustListCritSect.
+ //
+
+ DWORD CsReferenceCount;
+
+ //
+ // Writer semaphore.
+ //
+ // This semaphore is locked whenever there is a writer modifying
+ // fields in this client session.
+ //
+
+ HANDLE CsWriterSemaphore;
+
+
+ //
+ // The following fields are used by the NlDcDiscoveryMachine to keep track
+ // of discovery state.
+ //
+ // Access serialized by NlGlobalDcDiscoveryCritSect
+ //
+
+ DWORD CsDiscoveryRetryCount;
+
+ DWORD CsDiscoveryFlags;
+
+#define CS_DISCOVERY_IN_PROGRESS 0x01 // Discovery is currently happening
+#define CS_DISCOVERY_ASYNCHRONOUS 0x02 // Waiting on NlGlobalDiscoveryTimer
+
+ //
+ // This event is set to indicate that discovery is not in progress on this
+ // client session.
+ //
+
+ HANDLE CsDiscoveryEvent;
+
+ //
+ // API timout count. After each logon/logoff API call made to the
+ // server this count is incremented if the time taken to execute the
+ // this API is more than the specified TIMEOUT.
+ //
+ // Access serialized by CsWriterSemaphore.
+ //
+
+ DWORD CsTimeoutCount;
+
+#define MAX_DC_TIMEOUT_COUNT 2
+#define MAX_WKSTA_TIMEOUT_COUNT 2 // drop the session after this
+ // many timeouts and when it is
+ // time to reauthenticate.
+
+#define MAX_DC_API_TIMEOUT (long) (15L*1000L) // 15 seconds
+#define MAX_WKSTA_API_TIMEOUT (long) (15L*1000L) // 15 seconds
+
+#define MAX_DC_REAUTHENTICATION_WAIT (long) (5L*60L*1000L) // 5 mins
+#define MAX_WKSTA_REAUTHENTICATION_WAIT (long) (5L*60L*1000L) // 5 mins
+
+ //
+ // Authentication information.
+ //
+ // Access serialized by CsWriterSemaphore.
+ //
+
+ NETLOGON_CREDENTIAL CsAuthenticationSeed;
+ NETLOGON_SESSION_KEY CsSessionKey;
+
+ //
+ // Transport the server was discovered on.
+ //
+
+ LPWSTR CsTransportName;
+
+ //
+ // Name of the server this connection is to.
+ //
+ // Access serialized by CsWriterSemaphore or NlGlobalDcDiscoveryCritSect.
+ // Modification from Null to non-null serialized by
+ // NlGlobalDcDiscoveryCritSect
+ // (Modification from non-null to null requires both to be locked.)
+ //
+
+ WCHAR CsUncServerName[UNCLEN+1];
+
+
+ //
+ // Name of the account used to contact server
+ //
+
+ WCHAR CsAccountName[SSI_ACCOUNT_NAME_LENGTH+1];
+
+
+
+} CLIENT_SESSION, * PCLIENT_SESSION;
+
+//
+// To serialize access to trust list and NlGlobalClientSession
+//
+
+EXTERN CRITICAL_SECTION NlGlobalTrustListCritSect;
+
+//
+// The list of trusted domains
+//
+
+EXTERN LIST_ENTRY NlGlobalTrustList;
+EXTERN DWORD NlGlobalTrustListLength; // Number of entries in NlGlobalTrustList
+
+//
+// For workstations and non-DC servers,
+// maintain a list of domains trusted by our primary domain.
+//
+
+typedef struct {
+ CHAR DomainName[DNLEN+1];
+} TRUSTED_DOMAIN, *PTRUSTED_DOMAIN;
+
+EXTERN PTRUSTED_DOMAIN NlGlobalTrustedDomainList;
+EXTERN DWORD NlGlobalTrustedDomainCount;
+EXTERN BOOL NlGlobalTrustedDomainListKnown;
+EXTERN LARGE_INTEGER NlGlobalTrustedDomainListTime;
+
+//
+// For BDC, these are the credentials used to communicate with the PDC.
+// For a workstation, these are the credentials used to communicate with a DC.
+//
+
+EXTERN PCLIENT_SESSION NlGlobalClientSession;
+
+#define LOCK_TRUST_LIST() EnterCriticalSection( &NlGlobalTrustListCritSect )
+#define UNLOCK_TRUST_LIST() LeaveCriticalSection( &NlGlobalTrustListCritSect )
+
+//
+// Serialize DC Discovery activities
+//
+
+EXTERN CRITICAL_SECTION NlGlobalDcDiscoveryCritSect;
+
+//
+// Timer for timing out async DC discovery
+//
+
+EXTERN TIMER NlGlobalDcDiscoveryTimer;
+EXTERN DWORD NlGlobalDcDiscoveryCount;
+
+//
+// Timer for timing out API calls to trusted domains
+//
+// Serialized using NlGlobalTrustListCritSect.
+//
+
+EXTERN TIMER NlGlobalApiTimer;
+EXTERN DWORD NlGlobalBindingHandleCount;
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Server Session definitions
+//
+/////////////////////////////////////////////////////////////////////////////
+
+//
+// Sam Sync Context.
+//
+// A Sam sync context is maintained on the PDC for each BDC/member currently
+// doing a full sync.
+//
+typedef struct _SAM_SYNC_CONTEXT {
+
+ //
+ // The Sync state indicates tracks the progression of the sync.
+ //
+
+ SYNC_STATE SyncState;
+
+ //
+ // A serial number indicating the number of times the BDC/member
+ // has called us. We use this as a resume handle.
+ //
+
+ ULONG SyncSerial;
+
+ //
+ // The current Sam Enumeration information
+ //
+
+ SAM_ENUMERATE_HANDLE SamEnumHandle; // Current Sam Enum Handle
+ PSAMPR_ENUMERATION_BUFFER SamEnum; // Sam returned buffer
+ PULONG RidArray; // Array of enumerated Rids
+ ULONG Index; // Index to current entry
+ ULONG Count; // Total Number of entries
+
+ BOOL SamAllDone; // True, if Sam has completed
+
+#define UAS_BUILTIN_ADMINS_GROUP 0x01 // bit 0
+#define UAS_BUILTIN_USERS_GROUP 0x02 // bit 1
+#define UAS_BUILTIN_GUESTS_GROUP 0x04 // bit 2
+
+#define UAS_BUILTIN_GROUPS_COUNT 0x03
+
+#define UAS_BUILTIN_ADMINS_GROUP_NAME L"ADMINS"
+#define UAS_BUILTIN_USERS_GROUP_NAME L"USERS"
+#define UAS_BUILTIN_GUESTS_GROUP_NAME L"GUESTS"
+
+ DWORD UasBuiltinGroups; // flag to determine the
+ // presence of uas builtin
+ // groups
+
+} SAM_SYNC_CONTEXT, *PSAM_SYNC_CONTEXT;
+
+#define SAM_SYNC_PREF_MAX 1024 // Preferred max for Sam Sync
+
+
+//
+// Lsa Sync Context.
+//
+// A Lsa sync context is maintained on the PDC for each BDC/member
+// currently doing a full sync.
+//
+typedef struct _LSA_SYNC_CONTEXT {
+
+ //
+ // The Sync state indicates tracks the progression of the sync.
+ //
+
+ enum {
+ AccountState,
+ TDomainState,
+ SecretState,
+ LsaDoneState
+ } SyncState;
+
+ //
+ // A serial number indicating the number of times the BDC/member
+ // has called us. We use this as a resume handle.
+ //
+
+ ULONG SyncSerial;
+
+ //
+ // The current Lsa Enumeration information
+ //
+
+ LSA_ENUMERATION_HANDLE LsaEnumHandle; // Current Lsa Enum Handle
+
+ enum {
+ AccountEnumBuffer,
+ TDomainEnumBuffer,
+ SecretEnumBuffer,
+ EmptyEnumBuffer
+ } LsaEnumBufferType;
+
+ union {
+ LSAPR_ACCOUNT_ENUM_BUFFER Account;
+ LSAPR_TRUSTED_ENUM_BUFFER TDomain;
+ PVOID Secret;
+ } LsaEnum; // Lsa returned buffer
+
+ ULONG Index; // Index to current entry
+ ULONG Count; // Total Number of entries
+
+ BOOL LsaAllDone; // True, if Lsa has completed
+
+} LSA_SYNC_CONTEXT, *PLSA_SYNC_CONTEXT;
+
+//
+// union of lsa and sam context
+//
+
+typedef struct _SYNC_CONTEXT {
+ enum {
+ LsaDBContextType,
+ SamDBContextType
+ } DBContextType;
+
+ union {
+ LSA_SYNC_CONTEXT Lsa;
+ SAM_SYNC_CONTEXT Sam;
+ } DBContext;
+} SYNC_CONTEXT, *PSYNC_CONTEXT;
+
+//
+// Macro used to free any resources allocated by SAM.
+//
+// ?? check LsaIFree_LSAPR_* call parameters.
+//
+
+#define CLEAN_SYNC_CONTEXT( _Sync ) { \
+ if ( (_Sync)->DBContextType == LsaDBContextType ) { \
+ if ( (_Sync)->DBContext.Lsa.LsaEnumBufferType != \
+ EmptyEnumBuffer) { \
+ if ( (_Sync)->DBContext.Lsa.LsaEnumBufferType == \
+ AccountEnumBuffer) { \
+ LsaIFree_LSAPR_ACCOUNT_ENUM_BUFFER( \
+ &((_Sync)->DBContext.Lsa.LsaEnum.Account) ); \
+ } \
+ else if ( (_Sync)->DBContext.Lsa.LsaEnumBufferType == \
+ TDomainEnumBuffer) { \
+ LsaIFree_LSAPR_TRUSTED_ENUM_BUFFER( \
+ &((_Sync)->DBContext.Lsa.LsaEnum.TDomain) ); \
+ } \
+ else { \
+ MIDL_user_free( (_Sync)->DBContext.Lsa.LsaEnum.Secret );\
+ } \
+ (_Sync)->DBContext.Lsa.LsaEnumBufferType = \
+ EmptyEnumBuffer; \
+ } \
+ } else { \
+ if ( (_Sync)->DBContext.Sam.SamEnum != NULL ) { \
+ SamIFree_SAMPR_ENUMERATION_BUFFER( \
+ (_Sync)->DBContext.Sam.SamEnum ); \
+ (_Sync)->DBContext.Sam.SamEnum = NULL; \
+ } \
+ if ( (_Sync)->DBContext.Sam.RidArray != NULL ) { \
+ MIDL_user_free( (_Sync)->DBContext.Sam.RidArray );\
+ (_Sync)->DBContext.Sam.RidArray = NULL; \
+ } \
+ } \
+}
+
+//
+// Macro to initialize Sync Context
+//
+#define INIT_SYNC_CONTEXT( _Sync, _ContextType ) { \
+ RtlZeroMemory( (_Sync), sizeof( *(_Sync) ) ) ; \
+ (_Sync)->DBContextType = (_ContextType) ; \
+}
+
+//
+// macros for dynamic tuning.
+//
+
+#define IS_BDC_CHANNEL( _ChannelType ) \
+ ( (_ChannelType) == ServerSecureChannel || \
+ (_ChannelType) == UasServerSecureChannel )
+
+//
+// Server Session structure
+//
+// This structure represents the server side of a connection to a DC.
+//
+
+typedef struct _SERVER_SESSION {
+ //
+ // Each server session entry is in a doubly linked list for each hash bucket.
+ //
+
+ LIST_ENTRY SsHashList;
+
+ //
+ // Each server session entry is in a doubly linked list defined by
+ // NlGlobalServerSessionTable.
+ //
+
+ LIST_ENTRY SsSeqList;
+
+ //
+ // List of all BDCs headed by NlGlobalBdcServerSessionList.
+ //
+ // (The field is set only on BDC server session entries)
+ //
+ // Access serialized by NlGlobalServerSessionTableCritSect.
+ //
+
+ LIST_ENTRY SsBdcList;
+
+ //
+ // List of BDC's which have a pulse pending.
+ //
+
+ LIST_ENTRY SsPendingBdcList;
+
+ //
+ // Time when the last pulse was sent to this machine
+ //
+ // (The field is set only on BDC server session entries)
+ //
+
+ LARGE_INTEGER SsLastPulseTime;
+
+ //
+ // Current serial numbers of each database on the BDC.
+ //
+ // (The field is set only on BDC server session entries)
+ //
+
+ LARGE_INTEGER SsBdcDbSerialNumber[NUM_DBS];
+
+ //
+ // The computername uniquely identifies this server session entry.
+ //
+
+ NETLOGON_SECURE_CHANNEL_TYPE SsSecureChannelType;
+ CHAR SsComputerName[CNLEN+1];
+
+ //
+ // Rid of the server account
+ //
+ // (The field is set only on BDC server session entries)
+ //
+
+ ULONG SsLmBdcAccountRid;
+ ULONG SsNtBdcAccountRid;
+
+ //
+ // The number of times there has been no response to a pulse.
+ //
+
+ USHORT SsPulseTimeoutCount;
+
+ //
+ // The number of times this entry has been scavanged.
+ //
+
+ USHORT SsCheck;
+
+ //
+ // Flags describing the state of the current entry.
+ // See the SS_ defines below.
+ //
+
+ USHORT SsFlags;
+
+#define SS_CHALLENGE 0x0001 // Challenge is in progress
+#define SS_AUTHENTICATED 0x0002 // Remote side has be authenticated
+
+#define SS_LOCKED 0x0004 // Delay deletion requests for this entry
+ // While set, SsSessionKey may be referenced
+#define SS_DELETE_ON_UNLOCK 0x0008 // Delete entry when it is unlocked
+
+#define SS_BDC 0x0010 // BDC account exists for this Client
+#define SS_LM_BDC 0x0020 // Lanman BDC account exists for this entry is a
+#define SS_PENDING_BDC 0x0040 // BDC is on pending BDC list.
+
+#define SS_UAS_BUFFER_OVERFLOW 0x0100 // Previous downlevel API call
+ // returned STATUS_BUFFER_TOO_SMALL
+#define SS_FORCE_PULSE 0x0200 // Force a pulse message to this BDC.
+#define SS_PULSE_SENT 0x0400 // Pulse has been sent but has not
+ // been responded to yet
+#define SS_LSA_REPL_NEEDED 0x2000 // BDC needs LSA DB replicated
+#define SS_ACCOUNT_REPL_NEEDED 0x4000 // BDC needs SAM Account DB replicated
+#define SS_BUILTIN_REPL_NEEDED 0x8000 // BDC needs SAM Builtin DB replicated
+#define SS_REPL_MASK 0xE000 // BDC needs replication mask
+
+// Don't clear these on session setup
+#define SS_PERMANENT_FLAGS \
+ ( SS_BDC | SS_LM_BDC | SS_PENDING_BDC | SS_FORCE_PULSE | SS_REPL_MASK )
+
+ //
+ // Flags describing capabilities of both client and server.
+ //
+
+ ULONG SsNegotiatedFlags;
+
+ //
+ // Transport the client connected over.
+ //
+
+ LPWSTR SsTransportName;
+
+
+ //
+ // This is the ClientChallenge (during the challenge phase) and later
+ // the ClientCredential (after authentication is complete).
+ //
+
+ NETLOGON_CREDENTIAL SsAuthenticationSeed;
+
+ //
+ // This is the ServerChallenge (during the challenge phase) and later
+ // the SessionKey (after authentication is complete).
+ //
+
+ NETLOGON_SESSION_KEY SsSessionKey;
+
+ //
+ // A pointer to the Sync context.
+ //
+ // (The field is set only on BDC server session entries)
+ //
+
+ PSYNC_CONTEXT SsSync;
+
+} SERVER_SESSION, *PSERVER_SESSION;
+
+
+//
+// Structure shared by all PDC and BDC sync routines.
+// (And other users of secure channels.)
+//
+
+typedef struct _SESSION_INFO {
+
+ //
+ // Session Key shared by both client and server.
+ //
+
+ NETLOGON_SESSION_KEY SessionKey;
+
+ //
+ // Flags describing capabilities of both client and server.
+ //
+
+ ULONG NegotiatedFlags;
+
+} SESSION_INFO, *PSESSION_INFO;
+
+
+
+
+
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Structures and variables describing the database info.
+//
+/////////////////////////////////////////////////////////////////////////////
+
+typedef struct _DB_Info {
+ LARGE_INTEGER CreationTime; // database creation time
+ DWORD DBIndex; // index of Database table
+ SAM_HANDLE DBHandle; // database handle to access
+ PSID DBId; // database ID
+ LPWSTR DBName; // Name of the database
+ DWORD DBSessionFlag; // SS_ Flag representing this database
+
+ // Access to the following three fields are serialized by
+ // the NlGlobalDbInfoCritSect.
+ BOOLEAN UpdateRqd; // need to update the database
+ BOOLEAN FullSyncRequired; // Full sync needed on this database
+ BOOLEAN SyncDone; // Full sync has already been done on database
+
+ WCHAR PrimaryName[CNLEN+1]; // Primary this database last replicated from
+} DB_INFO, *PDB_INFO;
+
+EXTERN CRITICAL_SECTION NlGlobalDbInfoCritSect;
+
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// Global variables
+//
+////////////////////////////////////////////////////////////////////////////////
+
+//
+// Critical section serializing startup and stopping of the replicator thread.
+//
+EXTERN CRITICAL_SECTION NlGlobalReplicatorCritSect;
+EXTERN BOOL NlGlobalSSICritSectInit;
+
+
+
+
+
+//
+// Table of all Server Sessions
+// The size of the hash table must be a power-of-2.
+//
+#define SERVER_SESSION_HASH_TABLE_SIZE 64
+EXTERN CRITICAL_SECTION NlGlobalServerSessionTableCritSect;
+EXTERN PLIST_ENTRY NlGlobalServerSessionHashTable;
+EXTERN LIST_ENTRY NlGlobalServerSessionTable;
+EXTERN LIST_ENTRY NlGlobalBdcServerSessionList;
+EXTERN ULONG NlGlobalBdcServerSessionCount;
+
+//
+// List of all BDC's the PDC has sent a pulse to.
+//
+
+EXTERN LIST_ENTRY NlGlobalPendingBdcList;
+EXTERN ULONG NlGlobalPendingBdcCount;
+EXTERN TIMER NlGlobalPendingBdcTimer;
+
+#define LOCK_SERVER_SESSION_TABLE() \
+ EnterCriticalSection( &NlGlobalServerSessionTableCritSect )
+#define UNLOCK_SERVER_SESSION_TABLE() \
+ LeaveCriticalSection( &NlGlobalServerSessionTableCritSect )
+
+//
+// List of transports clients might connect to
+//
+EXTERN LPWSTR *NlGlobalTransportList;
+EXTERN DWORD NlGlobalTransportCount;
+
+#if DBG
+
+///////////////////////////////////////////////////////////////////////////////
+
+#define DEFPACKTIMER DWORD PackTimer, PackTimerTicks
+
+#define INITPACKTIMER PackTimer = 0;
+
+#define STARTPACKTIMER \
+ IF_DEBUG( REPL_OBJ_TIME ) { \
+ PackTimerTicks = GetTickCount(); \
+ }
+
+#define STOPPACKTIMER \
+ IF_DEBUG( REPL_OBJ_TIME ) { \
+ PackTimer += GetTickCount() - PackTimerTicks; \
+ }
+
+
+#define PRINTPACKTIMER \
+ IF_DEBUG( REPL_OBJ_TIME ) { \
+ NlPrint((NL_REPL_OBJ_TIME,"\tTime Taken to PACK this object = %d msecs\n", \
+ PackTimer )); \
+ }
+
+///////////////////////////////////////////////////////////////////////////////
+
+#define DEFUNPACKTIMER DWORD UnpackTimer, UnpackTimerTicks
+
+#define INITUNPACKTIMER UnpackTimer = 0;
+
+#define STARTUNPACKTIMER \
+ IF_DEBUG( REPL_OBJ_TIME ) { \
+ UnpackTimerTicks = GetTickCount(); \
+ }
+
+#define STOPUNPACKTIMER \
+ IF_DEBUG( REPL_OBJ_TIME ) { \
+ UnpackTimer += GetTickCount() - \
+ UnpackTimerTicks; \
+ }
+
+
+#define PRINTUNPACKTIMER \
+ IF_DEBUG( REPL_OBJ_TIME ) { \
+ NlPrint((NL_REPL_OBJ_TIME,"\tTime Taken to UNPACK this object = %d msecs\n", \
+ UnpackTimer )); \
+ }
+
+///////////////////////////////////////////////////////////////////////////////
+
+#define DEFSAMTIMER DWORD SamTimer, SamTimerTicks
+
+#define INITSAMTIMER SamTimer = 0;
+
+#define STARTSAMTIMER \
+ IF_DEBUG( REPL_OBJ_TIME ) { \
+ SamTimerTicks = GetTickCount(); \
+ }
+
+#define STOPSAMTIMER \
+ IF_DEBUG( REPL_OBJ_TIME ) { \
+ SamTimer += GetTickCount() - SamTimerTicks; \
+ }
+
+
+#define PRINTSAMTIMER \
+ IF_DEBUG( REPL_OBJ_TIME ) { \
+ NlPrint((NL_REPL_OBJ_TIME,"\tTime spent in SAM calls = %d msecs\n", \
+ SamTimer )); \
+ }
+
+///////////////////////////////////////////////////////////////////////////////
+
+#define DEFLSATIMER DWORD LsaTimer, LsaTimerTicks
+
+#define INITLSATIMER LsaTimer = 0;
+
+#define STARTLSATIMER \
+ IF_DEBUG( REPL_OBJ_TIME ) { \
+ LsaTimerTicks = GetTickCount(); \
+ }
+
+#define STOPLSATIMER \
+ IF_DEBUG( REPL_OBJ_TIME ) { \
+ LsaTimer += GetTickCount() - LsaTimerTicks; \
+ }
+
+
+#define PRINTLSATIMER \
+ IF_DEBUG( REPL_OBJ_TIME ) { \
+ NlPrint((NL_REPL_OBJ_TIME,"\tTime spent in LSA calls = %d msecs\n", \
+ LsaTimer )); \
+ }
+
+///////////////////////////////////////////////////////////////////////////////
+
+#define DEFSSIAPITIMER DWORD SsiApiTimer, SsiApiTimerTicks
+
+#define INITSSIAPITIMER SsiApiTimer = 0;
+
+#define STARTSSIAPITIMER \
+ IF_DEBUG( REPL_TIME ) { \
+ SsiApiTimerTicks = GetTickCount(); \
+ }
+
+#define STOPSSIAPITIMER \
+ IF_DEBUG( REPL_TIME ) { \
+ SsiApiTimer += GetTickCount() - \
+ SsiApiTimerTicks; \
+ }
+
+
+#define PRINTSSIAPITIMER \
+ IF_DEBUG( REPL_TIME ) { \
+ NlPrint((NL_REPL_TIME,"\tTime Taken by this SSIAPI call = %d msecs\n", \
+ SsiApiTimer )); \
+ }
+
+#else // DBG
+
+#define DEFPACKTIMER
+#define INITPACKTIMER
+#define STARTPACKTIMER
+#define STOPPACKTIMER
+#define PRINTPACKTIMER
+
+#define DEFUNPACKTIMER
+#define DEFUNPACKTICKS
+#define INITUNPACKTIMER
+#define STARTUNPACKTIMER
+#define STOPUNPACKTIMER
+#define PRINTUNPACKTIMER
+
+#define DEFSAMTIMER
+#define INITSAMTIMER
+#define STARTSAMTIMER
+#define STOPSAMTIMER
+#define PRINTSAMTIMER
+
+#define DEFLSATIMER
+#define INITLSATIMER
+#define STARTLSATIMER
+#define STOPLSATIMER
+#define PRINTLSATIMER
+
+#define DEFSSIAPITIMER
+#define INITSSIAPITIMER
+#define STARTSSIAPITIMER
+#define STOPSSIAPITIMER
+#define PRINTSSIAPITIMER
+
+#endif // DBG
+
+//
+// macros used in pack and unpack routines
+//
+
+#define SECURITYINFORMATION OWNER_SECURITY_INFORMATION | \
+ GROUP_SECURITY_INFORMATION | \
+ SACL_SECURITY_INFORMATION | \
+ DACL_SECURITY_INFORMATION
+
+#define INIT_PLACE_HOLDER(_x) \
+ RtlInitString( (PSTRING) &(_x)->DummyString1, NULL ); \
+ RtlInitString( (PSTRING) &(_x)->DummyString2, NULL ); \
+ RtlInitString( (PSTRING) &(_x)->DummyString3, NULL ); \
+ RtlInitString( (PSTRING) &(_x)->DummyString4, NULL ); \
+ (_x)->DummyLong1 = 0; \
+ (_x)->DummyLong2 = 0; \
+ (_x)->DummyLong3 = 0; \
+ (_x)->DummyLong4 = 0;
+
+#define QUERY_LSA_SECOBJ_INFO(_x) \
+ STARTLSATIMER; \
+ Status = LsarQuerySecurityObject( \
+ (_x), \
+ SECURITYINFORMATION, \
+ &SecurityDescriptor );\
+ STOPLSATIMER; \
+\
+ if (!NT_SUCCESS(Status)) { \
+ SecurityDescriptor = NULL; \
+ goto Cleanup; \
+ }
+
+#define QUERY_SAM_SECOBJ_INFO(_x) \
+ STARTSAMTIMER; \
+ Status = SamrQuerySecurityObject( \
+ (_x), \
+ SECURITYINFORMATION, \
+ &SecurityDescriptor );\
+ STOPSAMTIMER; \
+\
+ if (!NT_SUCCESS(Status)) { \
+ SecurityDescriptor = NULL; \
+ goto Cleanup; \
+ }
+
+
+#define SET_LSA_SECOBJ_INFO(_x, _y) \
+ SecurityDescriptor.Length = (_x)->SecuritySize; \
+ SecurityDescriptor.SecurityDescriptor = (_x)->SecurityDescriptor; \
+\
+ STARTLSATIMER; \
+ Status = LsarSetSecurityObject( \
+ (_y), \
+ (_x)->SecurityInformation, \
+ &SecurityDescriptor ); \
+ STOPLSATIMER; \
+\
+ if (!NT_SUCCESS(Status)) { \
+ goto Cleanup; \
+ }
+
+#define SET_SAM_SECOBJ_INFO(_x, _y) \
+ SecurityDescriptor.Length = (_x)->SecuritySize; \
+ SecurityDescriptor.SecurityDescriptor = (_x)->SecurityDescriptor; \
+\
+ STARTSAMTIMER; \
+ Status = SamrSetSecurityObject( \
+ (_y), \
+ (_x)->SecurityInformation, \
+ &SecurityDescriptor ); \
+ STOPSAMTIMER; \
+\
+ if (!NT_SUCCESS(Status)) { \
+ goto Cleanup; \
+ }
+
+
+#define DELTA_SECOBJ_INFO(_x) \
+ (_x)->SecurityInformation = SECURITYINFORMATION;\
+ (_x)->SecuritySize = SecurityDescriptor->Length;\
+\
+ *BufferSize += NlCopyData( \
+ (LPBYTE *)&SecurityDescriptor->SecurityDescriptor, \
+ (LPBYTE *)&(_x)->SecurityDescriptor, \
+ SecurityDescriptor->Length );
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// Procedure forwards.
+//
+///////////////////////////////////////////////////////////////////////////////
+
+//
+// ssiapi.c
+//
+
+NTSTATUS
+NlVerifyWorkstation(
+ IN LPWSTR PrimaryName OPTIONAL
+);
+
+
+//
+// srvsess.c
+//
+
+LPWSTR
+NlTransportLookupTransportName(
+ IN LPWSTR TransportName
+ );
+
+LPWSTR
+NlTransportLookup(
+ IN LPWSTR ClientName
+ );
+
+VOID
+NlTransportClose(
+ VOID
+ );
+
+NTSTATUS
+NlInitSSI(
+ VOID
+ );
+
+
+PSERVER_SESSION
+NlFindNamedServerSession(
+ IN LPWSTR ComputerName
+ );
+
+NTSTATUS
+NlInsertServerSession(
+ IN LPWSTR ComputerName,
+ IN DWORD Flags,
+ IN ULONG AccountRid,
+ IN PNETLOGON_CREDENTIAL AuthenticationSeed OPTIONAL,
+ IN PNETLOGON_CREDENTIAL AuthenticationResponse OPTIONAL
+ );
+
+NTSTATUS
+NlAddBdcServerSession(
+ IN ULONG ServerRid,
+ IN PUNICODE_STRING AccountName OPTIONAL,
+ IN DWORD Flags
+ );
+
+VOID
+NlFreeServerSession(
+ IN PSERVER_SESSION ServerSession
+ );
+
+VOID
+NlUnlockServerSession(
+ IN PSERVER_SESSION ServerSession
+ );
+
+VOID
+NlFreeLmBdcServerSession(
+ IN ULONG ServerRid
+ );
+
+VOID
+NlFreeNamedServerSession(
+ IN LPWSTR ComputerName,
+ IN BOOLEAN AccountBeingDeleted
+ );
+
+VOID
+NlFreeServerSessionForAccount(
+ IN PUNICODE_STRING AccountName
+ );
+
+VOID
+NlServerSessionScavenger(
+ VOID
+ );
+
+
+//
+// ssiauth.c
+//
+
+
+VOID
+NlMakeSessionKey(
+ IN PNT_OWF_PASSWORD CryptKey,
+ IN PNETLOGON_CREDENTIAL ClientChallenge,
+ IN PNETLOGON_CREDENTIAL ServerChallenge,
+ OUT PNETLOGON_SESSION_KEY SessionKey
+ );
+
+NTSTATUS
+NlCheckAuthenticator(
+ IN OUT PSERVER_SESSION ServerServerSession,
+ IN PNETLOGON_AUTHENTICATOR Authenticator,
+ OUT PNETLOGON_AUTHENTICATOR ReturnAuthenticator
+ );
+
+VOID
+NlComputeCredentials(
+ IN PNETLOGON_CREDENTIAL Challenge,
+ OUT PNETLOGON_CREDENTIAL Credential,
+ IN PNETLOGON_SESSION_KEY SessionKey
+ );
+
+VOID
+NlComputeChallenge(
+ OUT PNETLOGON_CREDENTIAL Challenge
+ );
+
+VOID
+NlBuildAuthenticator(
+ IN OUT PNETLOGON_CREDENTIAL AuthenticationSeed,
+ IN PNETLOGON_SESSION_KEY SessionKey,
+ OUT PNETLOGON_AUTHENTICATOR Authenticator
+ );
+
+BOOL
+NlUpdateSeed(
+ IN OUT PNETLOGON_CREDENTIAL AuthenticationSeed,
+ IN PNETLOGON_CREDENTIAL TargetCredential,
+ IN PNETLOGON_SESSION_KEY SessionKey
+ );
+
+VOID
+NlEncryptRC4(
+ IN OUT PVOID Buffer,
+ IN ULONG BufferSize,
+ IN PSESSION_INFO SessionInfo
+ );
+
+VOID
+NlDecryptRC4(
+ IN OUT PVOID Buffer,
+ IN ULONG BufferSize,
+ IN PSESSION_INFO SessionInfo
+ );
+
+//
+// trustutl.c
+//
+
+PCLIENT_SESSION
+NlFindNamedClientSession(
+ IN PUNICODE_STRING DomainName
+ );
+
+PCLIENT_SESSION
+NlAllocateClientSession(
+ IN PUNICODE_STRING DomainName,
+ IN PSID DomainId,
+ IN NETLOGON_SECURE_CHANNEL_TYPE SecureChannelType
+ );
+
+VOID
+NlFreeClientSession(
+ IN PCLIENT_SESSION ClientSession
+ );
+
+VOID
+NlRefClientSession(
+ IN PCLIENT_SESSION ClientSession
+ );
+
+VOID
+NlUnrefClientSession(
+ IN PCLIENT_SESSION ClientSession
+ );
+
+BOOL
+NlTimeoutSetWriterClientSession(
+ IN PCLIENT_SESSION ClientSession,
+ IN DWORD Timeout
+ );
+
+VOID
+NlResetWriterClientSession(
+ IN PCLIENT_SESSION ClientSession
+ );
+
+NTSTATUS
+NlCaptureServerClientSession (
+ IN PCLIENT_SESSION ClientSession,
+ OUT WCHAR UncServerName[UNCLEN+1]
+ );
+
+VOID
+NlSetStatusClientSession(
+ IN PCLIENT_SESSION ClientSession,
+ IN NTSTATUS CsConnectionStatus
+ );
+
+NTSTATUS
+NlInitTrustList(
+ VOID
+ );
+
+NTSTATUS
+NlUpdateTrustListBySid (
+ IN PSID DomainId,
+ IN PUNICODE_STRING DomainName OPTIONAL
+ );
+
+VOID
+NlPickTrustedDcForEntireTrustList(
+ VOID
+ );
+
+NTSTATUS
+NlSetTrustedDomainList (
+ IN LPWSTR TrustedDomainList,
+ IN BOOL TrustedDomainListKnown
+ );
+
+VOID
+NlSaveTrustedDomainList (
+ IN LPWSTR TrustedDomainList
+ );
+
+NET_API_STATUS
+NlReadRegTrustedDomainList (
+ IN LPWSTR NewDomainName OPTIONAL,
+ IN BOOL DeleteName,
+ OUT LPWSTR *TrustedDomainList,
+ OUT PBOOL TrustedDomainListKnown
+ );
+
+BOOLEAN
+NlIsDomainTrusted (
+ IN PUNICODE_STRING DomainName
+ );
+
+typedef enum _DISCOVERY_TYPE {
+ DT_Asynchronous,
+ DT_Synchronous,
+ DT_DeadDomain
+} DISCOVERY_TYPE;
+
+NTSTATUS
+NlDiscoverDc (
+ IN OUT PCLIENT_SESSION ClientSession,
+ IN DISCOVERY_TYPE DiscoveryType
+ );
+
+VOID
+NlDcDiscoveryExpired (
+ IN BOOLEAN Exitting
+ );
+
+NTSTATUS
+NlDcDiscoveryHandler (
+ IN PNETLOGON_SAM_LOGON_RESPONSE Message,
+ IN DWORD MessageSize,
+ IN LPWSTR TransportName,
+ IN DWORD Version
+ );
+
+PCLIENT_SESSION
+NlPickDomainWithAccount (
+ IN LPWSTR AccountName,
+ IN ULONG AllowableAccountControlBits
+ );
+
+NTSTATUS
+NlStartApiClientSession(
+ IN PCLIENT_SESSION ClientSession,
+ IN BOOLEAN QuickApiCall
+ );
+
+BOOLEAN
+NlFinishApiClientSession(
+ IN PCLIENT_SESSION ClientSession,
+ IN BOOLEAN OkToKillSession
+ );
+
+VOID
+NlTimeoutApiClientSession(
+ VOID
+ );
+
+#undef EXTERN
diff --git a/private/net/svcdlls/logonsrv/server/trustutl.c b/private/net/svcdlls/logonsrv/server/trustutl.c
new file mode 100644
index 000000000..33249ccaf
--- /dev/null
+++ b/private/net/svcdlls/logonsrv/server/trustutl.c
@@ -0,0 +1,4976 @@
+/*++
+
+Copyright (c) 1987-1992 Microsoft Corporation
+
+Module Name:
+
+ trustutl.c
+
+Abstract:
+
+ Utilities manange of trusted domain list.
+
+Author:
+
+ 30-Jan-92 (cliffv)
+
+Environment:
+
+ User mode only.
+ Contains NT-specific code.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+--*/
+
+//
+// Common include files.
+//
+
+#include <logonsrv.h> // Include files common to entire service
+
+//
+// Include files specific to this .c file
+//
+
+#include <ntlsa.h>
+#include <alertmsg.h> // ALERT_* defines
+#include <align.h>
+#include <config.h> // net config helpers.
+#include <confname.h> // SECTION_ equates, NETLOGON_KEYWORD_ equates.
+#include <lmerr.h>
+#include <stdlib.h> // C library functions (rand, etc)
+#include <tstring.h>
+#include <lmapibuf.h>
+#include <lmuse.h> // NetUseDel
+#include <names.h> // NetpIsUserNameValid
+#include <nlbind.h> // Netlogon RPC binding cache routines
+
+
+
+PCLIENT_SESSION
+NlFindNamedClientSession(
+ IN PUNICODE_STRING DomainName
+ )
+/*++
+
+Routine Description:
+
+ Find the specified entry in the Trust List.
+
+Arguments:
+
+ DomainName - The name of the domain to find.
+
+Return Value:
+
+ Returns a pointer to the found entry.
+ The found entry is returned referenced and must be dereferenced using
+ NlUnrefClientSession.
+
+ If there is no such entry, return NULL.
+
+--*/
+{
+ PCLIENT_SESSION ClientSession = NULL;
+
+ //
+ // On DC, look up the domain in the trusted domain list.
+ //
+
+ if ( NlGlobalRole == RoleBackup || NlGlobalRole == RolePrimary ) {
+ PLIST_ENTRY ListEntry;
+
+ //
+ // Lookup the ClientSession with the TrustList locked and reference
+ // the found entry before dropping the lock.
+ //
+
+ LOCK_TRUST_LIST();
+
+ for ( ListEntry = NlGlobalTrustList.Flink ;
+ ListEntry != &NlGlobalTrustList ;
+ ListEntry = ListEntry->Flink) {
+
+ ClientSession =
+ CONTAINING_RECORD( ListEntry, CLIENT_SESSION, CsNext );
+
+ if ( RtlEqualDomainName( &ClientSession->CsDomainName,
+ DomainName ) ) {
+ NlRefClientSession( ClientSession );
+ break;
+ }
+
+ ClientSession = NULL;
+
+ }
+
+ UNLOCK_TRUST_LIST();
+ }
+
+ //
+ // On a workstation or BDC, refer to the Primary domain.
+ //
+
+ if ( (NlGlobalRole == RoleBackup && ClientSession == NULL) ||
+ NlGlobalRole == RoleMemberWorkstation ) {
+
+ if ( RtlEqualDomainName( &NlGlobalUnicodeDomainNameString,
+ DomainName ) ) {
+ ClientSession = NlGlobalClientSession;
+ NlRefClientSession( ClientSession );
+ } else {
+ ClientSession = NULL;
+ }
+
+ }
+
+ return ClientSession;
+
+}
+
+
+
+PCLIENT_SESSION
+NlAllocateClientSession(
+ IN PUNICODE_STRING DomainName,
+ IN PSID DomainId,
+ IN NETLOGON_SECURE_CHANNEL_TYPE SecureChannelType
+ )
+/*++
+
+Routine Description:
+
+ Allocate a ClientSession structure and initialize it.
+
+ The allocated entry is returned referenced and must be dereferenced using
+ NlUnrefClientSession.
+
+Arguments:
+
+ DomainName - Specifies the DomainName of the entry.
+
+ DomainId - Specifies the DomainId of the Domain.
+
+ SecureChannelType -- Type of secure channel this ClientSession structure
+ will represent.
+
+Return Value:
+
+--*/
+{
+ PCLIENT_SESSION ClientSession;
+ ULONG ClientSessionSize;
+ ULONG SidSize;
+ PCHAR Where;
+
+ //
+ // Determine the size of the ClientSession structure.
+ //
+
+ SidSize = RtlLengthSid( DomainId );
+
+ if ( DomainName->Length > DNLEN * sizeof(WCHAR) ) {
+ NlPrint((NL_CRITICAL,
+ "NlAllocateClientSession given "
+ "too long domain name %wZ\n", DomainName ));
+ return NULL;
+ }
+
+ ClientSessionSize = sizeof(CLIENT_SESSION) +
+ SidSize +
+ DomainName->Length + sizeof(WCHAR);
+
+ //
+ // Allocate the Client Session Entry
+ //
+
+ ClientSession = NetpMemoryAllocate( ClientSessionSize );
+
+ if (ClientSession == NULL) {
+ return NULL;
+ }
+
+ RtlZeroMemory( ClientSession, ClientSessionSize );
+
+
+ //
+ // Initialize misc. fields.
+ //
+
+ ClientSession->CsDomainId = NULL;
+ *ClientSession->CsUncServerName = L'\0';
+ ClientSession->CsTransportName = NULL;
+ ClientSession->CsSecureChannelType = SecureChannelType;
+ ClientSession->CsState = CS_IDLE;
+ ClientSession->CsReferenceCount = 1;
+ ClientSession->CsConnectionStatus = STATUS_NO_LOGON_SERVERS;
+ ClientSession->CsDiscoveryRetryCount = 0;
+ ClientSession->CsDiscoveryFlags = 0;
+ ClientSession->CsTimeoutCount = 0;
+ ClientSession->CsApiTimer.Period = (DWORD) MAILSLOT_WAIT_FOREVER;
+
+ InitializeListHead( &ClientSession->CsNext );
+
+ //
+ // Build the account name as a function of the SecureChannelType.
+ //
+
+ switch (SecureChannelType) {
+ case WorkstationSecureChannel:
+ case ServerSecureChannel:
+ wcscpy( ClientSession->CsAccountName, NlGlobalUnicodeComputerName );
+ wcscat( ClientSession->CsAccountName, SSI_ACCOUNT_NAME_POSTFIX);
+ break;
+
+ case TrustedDomainSecureChannel:
+ wcscpy( ClientSession->CsAccountName, NlGlobalUnicodeDomainName );
+ wcscat( ClientSession->CsAccountName, SSI_ACCOUNT_NAME_POSTFIX);
+ break;
+
+ default:
+ NetpMemoryFree( ClientSession );
+ return NULL;
+ }
+
+ //
+ // Create the writer semaphore.
+ //
+
+ ClientSession->CsWriterSemaphore = CreateSemaphore(
+ NULL, // No special security
+ 1, // Initially not locked
+ 1, // At most 1 unlocker
+ NULL ); // No name
+
+ if ( ClientSession->CsWriterSemaphore == NULL ) {
+ NetpMemoryFree( ClientSession );
+ return NULL;
+ }
+
+
+
+
+ //
+ // Create the Discovery event.
+ //
+
+ ClientSession->CsDiscoveryEvent = CreateEvent(
+ NULL, // No special security
+ TRUE, // Manual Reset
+ FALSE, // No discovery initially happening
+ NULL ); // No name
+
+ if ( ClientSession->CsDiscoveryEvent == NULL ) {
+ CloseHandle( ClientSession->CsWriterSemaphore );
+ NetpMemoryFree( ClientSession );
+ return NULL;
+ }
+
+
+
+ //
+ // Copy the DomainId and DomainName to the buffer.
+ //
+
+ Where = (PCHAR)(ClientSession + 1);
+
+ NlAssert( Where == ROUND_UP_POINTER( Where, ALIGN_DWORD) );
+ ClientSession->CsDomainId = (PSID) Where;
+ NetpLogonPutBytes( DomainId, SidSize, &Where );
+
+ NlAssert( Where == ROUND_UP_POINTER( Where, ALIGN_WCHAR) );
+ ClientSession->CsDomainName.Buffer = (LPWSTR) Where;
+ ClientSession->CsDomainName.Length = DomainName->Length;
+ ClientSession->CsDomainName.MaximumLength = (USHORT)
+ (DomainName->Length + sizeof(WCHAR));
+ NetpLogonPutBytes( DomainName->Buffer, DomainName->Length, &Where );
+ *(Where++) = '\0';
+ *(Where++) = '\0';
+
+ return ClientSession;
+
+
+}
+
+
+VOID
+NlFreeClientSession(
+ IN PCLIENT_SESSION ClientSession
+ )
+/*++
+
+Routine Description:
+
+ Free the specified Trust list entry.
+
+ This routine is called with the Trust List locked.
+
+Arguments:
+
+ ClientSession - Specifies a pointer to the trust list entry to delete.
+
+Return Value:
+
+--*/
+{
+
+ //
+ // If someone has an outstanding pointer to this entry,
+ // delay the deletion for now.
+ //
+
+ if ( ClientSession->CsReferenceCount > 0 ) {
+ ClientSession->CsFlags |= CS_DELETE_ON_UNREF;
+ return;
+ }
+
+ //
+ // If this is a trusted domain secure channel,
+ // Delink the entry from the sequential list.
+ //
+
+ if (ClientSession->CsSecureChannelType == TrustedDomainSecureChannel ) {
+ RemoveEntryList( &ClientSession->CsNext );
+ NlGlobalTrustListLength --;
+ }
+
+ //
+ // Close the discovery event if it exists.
+ //
+
+ if ( ClientSession->CsDiscoveryEvent != NULL ) {
+ CloseHandle( ClientSession->CsDiscoveryEvent );
+ }
+
+ //
+ // Close the write synchronization handles.
+ //
+
+ (VOID) CloseHandle( ClientSession->CsWriterSemaphore );
+
+ //
+ // If there is an rpc binding handle to this server,
+ // unbind it.
+
+ if ( ClientSession->CsFlags & CS_BINDING_CACHED ) {
+
+ //
+ // Indicate the handle is no longer bound
+ //
+
+ NlGlobalBindingHandleCount --;
+
+ NlPrint((NL_SESSION_SETUP,
+ "NlFreeClientSession: %wZ: Unbind from server " FORMAT_LPWSTR ".\n",
+ &ClientSession->CsDomainName,
+ ClientSession->CsUncServerName ));
+ (VOID) NlBindingRemoveServerFromCache( ClientSession->CsUncServerName );
+ }
+
+ //
+ // Delete the entry
+ //
+
+ NetpMemoryFree( ClientSession );
+
+}
+
+
+VOID
+NlRefClientSession(
+ IN PCLIENT_SESSION ClientSession
+ )
+/*++
+
+Routine Description:
+
+ Mark the specified client session as referenced.
+
+ On Entry,
+ The trust list must be locked.
+
+Arguments:
+
+ ClientSession - Specifies a pointer to the trust list entry.
+
+Return Value:
+
+ None.
+
+--*/
+{
+
+ //
+ // Simply increment the reference count.
+ //
+
+ ClientSession->CsReferenceCount ++;
+}
+
+
+
+VOID
+NlUnrefClientSession(
+ IN PCLIENT_SESSION ClientSession
+ )
+/*++
+
+Routine Description:
+
+ Mark the specified client session as unreferenced.
+
+ On Entry,
+ The trust list entry must be referenced by the caller.
+ The caller must not be a writer of the trust list entry.
+
+ The trust list may be locked. But this routine will lock it again to
+ handle those cases where it isn't already locked.
+
+Arguments:
+
+ ClientSession - Specifies a pointer to the trust list entry.
+
+Return Value:
+
+--*/
+{
+
+ LOCK_TRUST_LIST();
+
+ //
+ // Dereference the entry.
+ //
+
+ NlAssert( ClientSession->CsReferenceCount > 0 );
+ ClientSession->CsReferenceCount --;
+
+ //
+ // If we're the last referencer and
+ // someone wanted to delete the entry while we had it referenced,
+ // finish the deletion.
+ //
+
+ if ( ClientSession->CsReferenceCount == 0 &&
+ (ClientSession->CsFlags & CS_DELETE_ON_UNREF) ) {
+ NlFreeClientSession( ClientSession );
+ }
+
+ UNLOCK_TRUST_LIST();
+
+}
+
+
+
+
+BOOL
+NlTimeoutSetWriterClientSession(
+ IN PCLIENT_SESSION ClientSession,
+ IN DWORD Timeout
+ )
+/*++
+
+Routine Description:
+
+ Become a writer of the specified client session but fail the operation if
+ we have to wait more than Timeout milliseconds.
+
+ A writer can "write" many of the fields in the client session structure.
+ See the comments in ssiinit.h for details.
+
+ On Entry,
+ The trust list must NOT be locked.
+ The trust list entry must be referenced by the caller.
+ The caller must NOT be a writer of the trust list entry.
+
+ Actually, the trust list can be locked if the caller passes in a short
+ timeout (for instance, zero milliseconds.) Specifying a longer timeout
+ violates the locking order.
+
+Arguments:
+
+ ClientSession - Specifies a pointer to the trust list entry.
+
+ Timeout - Maximum time (in milliseconds) to wait for a previous writer.
+
+Return Value:
+
+ TRUE - The caller is now the writer of the client session.
+
+ FALSE - The operation has timed out.
+
+--*/
+{
+ DWORD WaitStatus;
+ NlAssert( ClientSession->CsReferenceCount > 0 );
+
+ //
+ // Wait for other writers to finish.
+ //
+
+ WaitStatus = WaitForSingleObject( ClientSession->CsWriterSemaphore, Timeout );
+
+ if ( WaitStatus != 0 ) {
+ NlPrint(( NL_CRITICAL,
+ "NlTimeoutSetWriterClientSession timed out: %ld\n",
+ WaitStatus ));
+ return FALSE;
+ }
+
+
+ //
+ // Become a writer.
+ //
+ LOCK_TRUST_LIST();
+ ClientSession->CsFlags |= CS_WRITER;
+ UNLOCK_TRUST_LIST();
+
+ return TRUE;
+
+}
+
+
+
+VOID
+NlResetWriterClientSession(
+ IN PCLIENT_SESSION ClientSession
+ )
+/*++
+
+Routine Description:
+
+ Stop being a writer of the specified client session.
+
+ On Entry,
+ The trust list must NOT be locked.
+ The trust list entry must be referenced by the caller.
+ The caller must be a writer of the trust list entry.
+
+Arguments:
+
+ ClientSession - Specifies a pointer to the trust list entry.
+
+Return Value:
+
+--*/
+{
+
+ NlAssert( ClientSession->CsReferenceCount > 0 );
+ NlAssert( ClientSession->CsFlags & CS_WRITER );
+
+
+ //
+ // Stop being a writer.
+ //
+
+ LOCK_TRUST_LIST();
+ ClientSession->CsFlags &= ~CS_WRITER;
+ UNLOCK_TRUST_LIST();
+
+
+ //
+ // Allow writers to try again.
+ //
+
+ if ( !ReleaseSemaphore( ClientSession->CsWriterSemaphore, 1, NULL ) ) {
+ NlPrint((NL_CRITICAL,
+ "ReleaseSemaphore CsWriterSemaphore returned %ld\n",
+ GetLastError() ));
+ }
+
+}
+
+
+
+VOID
+NlSetStatusClientSession(
+ IN PCLIENT_SESSION ClientSession,
+ IN NTSTATUS CsConnectionStatus
+ )
+/*++
+
+Routine Description:
+
+ Set the connection state for this client session.
+
+ On Entry,
+ The trust list must NOT be locked.
+ The trust list entry must be referenced by the caller.
+ The caller must be a writer of the trust list entry.
+
+Arguments:
+
+ ClientSession - Specifies a pointer to the trust list entry.
+
+ CsConnectionStatus - the status of the connection.
+
+Return Value:
+
+--*/
+{
+ BOOLEAN UnbindFromServer = FALSE;
+ WCHAR UncServerName[UNCLEN+1];
+
+ NlAssert( ClientSession->CsReferenceCount > 0 );
+ NlAssert( ClientSession->CsFlags & CS_WRITER );
+
+ NlPrint((NL_SESSION_SETUP,
+ "NlSetStatusClientSession: %wZ: Set connection status to %lx\n",
+ &ClientSession->CsDomainName,
+ CsConnectionStatus ));
+
+ EnterCriticalSection( &NlGlobalDcDiscoveryCritSect );
+ ClientSession->CsConnectionStatus = CsConnectionStatus;
+ if ( NT_SUCCESS(CsConnectionStatus) ) {
+ ClientSession->CsState = CS_AUTHENTICATED;
+
+ //
+ // Handle setting the connection status to an error condition.
+ //
+
+ } else {
+
+ //
+ // If there is an rpc binding handle to this server,
+ // unbind it.
+
+ LOCK_TRUST_LIST();
+ if ( ClientSession->CsFlags & CS_BINDING_CACHED ) {
+
+ //
+ // Indicate the handle is no longer bound
+ //
+
+ ClientSession->CsFlags &= ~CS_BINDING_CACHED;
+ NlGlobalBindingHandleCount --;
+
+ //
+ // Capture the ServerName
+ //
+
+ wcscpy( UncServerName, ClientSession->CsUncServerName );
+ UnbindFromServer = TRUE;
+ }
+ UNLOCK_TRUST_LIST();
+
+ //
+ // If this is a BDC that just lost it's PDC,
+ // Indicate we don't know who the PDC is anymore.
+ //
+
+ if ( ClientSession->CsSecureChannelType == ServerSecureChannel ) {
+ NlSetPrimaryName( NULL );
+ }
+
+ //
+ // Indicate discovery is needed (And can be done at any time.)
+ //
+
+ ClientSession->CsState = CS_IDLE;
+ *ClientSession->CsUncServerName = L'\0';
+ ClientSession->CsTransportName = NULL;
+ ClientSession->CsTimeoutCount = 0;
+ ClientSession->CsLastAuthenticationTry.QuadPart = 0;
+
+ //
+ // Don't be tempted to clear CsAuthenticationSeed and CsSessionKey here.
+ // Even though the secure channel is gone, NlFinishApiClientSession may
+ // have dropped it. The caller of NlFinishApiClientSession will use
+ // the above two fields after the session is dropped in an attempt to
+ // complete the final call on the secure channel.
+ //
+
+
+ }
+
+ LeaveCriticalSection( &NlGlobalDcDiscoveryCritSect );
+
+
+ //
+ // Now that I have as many resources unlocked as possible,
+ // Unbind from this server.
+ //
+
+ if ( UnbindFromServer ) {
+ NlPrint((NL_SESSION_SETUP,
+ "NlSetStatusClientSession: %wZ: Unbind from server " FORMAT_LPWSTR ".\n",
+ &ClientSession->CsDomainName,
+ UncServerName ));
+ (VOID) NlBindingRemoveServerFromCache( UncServerName );
+ }
+
+}
+
+
+NTSTATUS
+NlInitTrustList(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ Initialize the in-memory trust list to match LSA's version.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ Status of the operation.
+
+--*/
+{
+ NTSTATUS Status;
+
+ LSA_ENUMERATION_HANDLE EnumerationContext = 0;
+ LSAPR_TRUSTED_ENUM_BUFFER LsaTrustList = {0, NULL};
+ ULONG LsaTrustListLength = 0;
+ ULONG LsaTrustListIndex = 0;
+ BOOL LsaAllDone = FALSE;
+
+
+ //
+ // Mark each entry in the trust list for deletion
+ //
+
+ //
+ // Loop through the LSA's list of domains
+ //
+ // For each entry found,
+ // If the entry already exits in the trust list,
+ // remove the mark for deletion.
+ // else
+ // allocate a new entry.
+ //
+
+ for (;; LsaTrustListIndex ++ ) {
+ PUNICODE_STRING DomainName;
+ PSID DomainId;
+
+ //
+ // Get more trusted domain names from the LSA.
+ //
+
+ if ( LsaTrustListIndex >= LsaTrustListLength ) {
+
+ //
+ // If we've already gotten everything from LSA,
+ // go delete entries that should be deleted.
+ //
+
+ if ( LsaAllDone ) {
+ break;
+ }
+
+ //
+ // Free any previous buffer returned from LSA.
+ //
+
+ if ( LsaTrustList.Information != NULL ) {
+
+ LsaIFree_LSAPR_TRUSTED_ENUM_BUFFER( &LsaTrustList );
+ LsaTrustList.Information = NULL;
+ }
+
+ //
+ // Do the actual enumeration
+ //
+
+ Status = LsarEnumerateTrustedDomains(
+ NlGlobalPolicyHandle,
+ &EnumerationContext,
+ &LsaTrustList,
+ 1024);
+
+ LsaTrustListLength = LsaTrustList.EntriesRead;
+
+ // If Lsa says he's returned all of the information,
+ // remember not to ask Lsa for more.
+ //
+
+ if ( Status == STATUS_NO_MORE_ENTRIES ) {
+ LsaAllDone = TRUE;
+ break;
+
+ //
+ // If Lsa says there is more information, just ensure he returned
+ // something to us on this call.
+ //
+
+ } else if ( NT_SUCCESS(Status) ) {
+ if ( LsaTrustListLength == 0 ) {
+ Status = STATUS_BUFFER_TOO_SMALL;
+ goto Cleanup;
+ }
+
+ //
+ // All other status' are errors.
+ //
+ } else {
+ goto Cleanup;
+ }
+
+ LsaTrustListIndex = 0;
+ }
+
+ //
+ // At this point LsaTrustList[LsaTrustListIndex] is the next entry
+ // returned from the LSA.
+ //
+
+ DomainName =
+ (PUNICODE_STRING)
+ &(LsaTrustList.Information[LsaTrustListIndex].Name);
+
+ DomainId =
+ (PSID)LsaTrustList.Information[LsaTrustListIndex].Sid;
+
+ NlPrint((NL_SESSION_SETUP, "NlInitTrustList: %wZ in LSA\n",
+ DomainName ));
+
+ if ( DomainName->Length > DNLEN * sizeof(WCHAR) ) {
+ NlPrint((NL_CRITICAL,
+ "LsarEnumerateTrustedDomains returned "
+ "too long domain name %wZ\n", DomainName ));
+ continue;
+ }
+
+ if ( RtlEqualDomainName( &NlGlobalUnicodeDomainNameString,
+ DomainName ) ) {
+ NlPrint((NL_SESSION_SETUP, "NlInitTrustList: %wZ "
+ "ignoring trust relationship to our own domain\n",
+ DomainName ));
+ continue;
+ }
+
+ //
+ // Update the in-memory trust list to match the LSA.
+ //
+
+ Status = NlUpdateTrustListBySid ( DomainId, DomainName );
+
+ if ( !NT_SUCCESS(Status) ) {
+ goto Cleanup;
+ }
+
+
+ }
+
+
+ //
+ // Trust list successfully updated.
+ //
+ Status = STATUS_SUCCESS;
+
+Cleanup:
+
+ if ( LsaTrustList.Information != NULL ) {
+ LsaIFree_LSAPR_TRUSTED_ENUM_BUFFER( &LsaTrustList );
+ }
+
+ return Status;
+}
+
+
+
+
+VOID
+NlPickTrustedDcForEntireTrustList(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ For each domain in the trust list where the DC has not been
+ available for at least 45 seconds, try to select a new DC.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ Status of the operation.
+
+--*/
+{
+ PLIST_ENTRY ListEntry;
+ PCLIENT_SESSION ClientSession;
+
+
+ LOCK_TRUST_LIST();
+
+ //
+ // Mark each entry to indicate we need to pick a DC.
+ //
+
+ for ( ListEntry = NlGlobalTrustList.Flink ;
+ ListEntry != &NlGlobalTrustList ;
+ ListEntry = ListEntry->Flink) {
+
+ ClientSession = CONTAINING_RECORD( ListEntry,
+ CLIENT_SESSION,
+ CsNext );
+
+ ClientSession->CsFlags |= CS_PICK_DC;
+ }
+
+
+ //
+ // Loop thru the trust list finding secure channels needing the DC
+ // to be picked.
+ //
+ for ( ListEntry = NlGlobalTrustList.Flink ;
+ ListEntry != &NlGlobalTrustList ;
+ ) {
+
+ ClientSession = CONTAINING_RECORD( ListEntry,
+ CLIENT_SESSION,
+ CsNext );
+
+ //
+ // If we've already done this entry,
+ // skip this entry.
+ //
+ if ( (ClientSession->CsFlags & CS_PICK_DC) == 0 ) {
+ ListEntry = ListEntry->Flink;
+ continue;
+ }
+ ClientSession->CsFlags &= ~CS_PICK_DC;
+
+ //
+ // If the DC is already picked,
+ // skip this entry.
+ //
+ if ( ClientSession->CsState != CS_IDLE ) {
+ ListEntry = ListEntry->Flink;
+ continue;
+ }
+
+ //
+ // Reference this entry while picking the DC.
+ //
+
+ NlRefClientSession( ClientSession );
+
+ UNLOCK_TRUST_LIST();
+
+ //
+ // Check if we've tried to authenticate recently.
+ // (Don't call NlTimeToReauthenticate with the trust list locked.
+ // It locks NlGlobalDcDiscoveryCritSect. That's the wrong locking
+ // order.)
+ //
+
+ if ( NlTimeToReauthenticate( ClientSession ) ) {
+
+ //
+ // Try to pick the DC for the session.
+ //
+
+ if ( NlTimeoutSetWriterClientSession( ClientSession, 10*1000 ) ) {
+ (VOID) NlDiscoverDc( ClientSession, DT_DeadDomain );
+ NlResetWriterClientSession( ClientSession );
+ }
+
+ }
+
+ //
+ // Since we dropped the trust list lock,
+ // we'll start the search from the front of the list.
+ //
+
+ NlUnrefClientSession( ClientSession );
+ LOCK_TRUST_LIST();
+
+ ListEntry = NlGlobalTrustList.Flink ;
+
+ }
+
+ UNLOCK_TRUST_LIST();
+
+ //
+ // On a BDC,
+ // ensure we know who the PDC is.
+ //
+ // In NT 3.1, we relied on the fact that the PDC sent us pulses every 5
+ // minutes. For NT 3.5, the PDC backs off after 3 such failed attempts and
+ // will only send a pulse every 2 hours. So, we'll take on the
+ // responsibility
+ //
+ if ( NlGlobalRole == RoleBackup &&
+ NlGlobalClientSession->CsState == CS_IDLE ) {
+
+
+
+ //
+ // Check if we've tried to authenticate recently.
+ // (Don't call NlTimeToReauthenticate with the trust list locked.
+ // It locks NlGlobalDcDiscoveryCritSect. That's the wrong locking
+ // order.)
+ //
+
+ NlRefClientSession( NlGlobalClientSession );
+ if ( NlTimeToReauthenticate( NlGlobalClientSession ) ) {
+
+ //
+ // Try to pick the DC for the session.
+ //
+
+ if ( NlTimeoutSetWriterClientSession( NlGlobalClientSession, 10*1000 ) ) {
+ (VOID) NlDiscoverDc( NlGlobalClientSession, DT_DeadDomain );
+ NlResetWriterClientSession( NlGlobalClientSession );
+ }
+
+ }
+ NlUnrefClientSession( NlGlobalClientSession );
+
+ }
+
+}
+
+
+BOOL
+NlReadSamLogonResponse (
+ IN HANDLE ResponseMailslotHandle,
+ IN LPWSTR AccountName,
+ OUT LPDWORD Opcode,
+ OUT LPWSTR *UncLogonServer
+ )
+
+/*++
+
+Routine Description:
+
+ Read a response from to a SamLogonRequest.
+
+Arguments:
+
+ ResponseMailslotHandle - Handle of mailslot to read.
+
+ AccountName - Name of the account the response is for.
+
+ Opcode - Returns the opcode from the message. This will be one of
+ LOGON_SAM_LOGON_RESPONSE or LOGON_SAM_USER_UNKNOWN.
+
+ UncLogonServer - Returns the UNC name of the logon server that responded.
+ This buffer is only returned if a valid message was received.
+ The buffer returned should be freed via NetpMemoryFree.
+
+
+Return Value:
+
+ TRUE: a valid message was received.
+ FALSE: a valid message was not received.
+
+--*/
+{
+ CHAR ResponseBuffer[MAX_RANDOM_MAILSLOT_RESPONSE];
+ PNETLOGON_SAM_LOGON_RESPONSE SamLogonResponse;
+ DWORD SamLogonResponseSize;
+ LPWSTR LocalServerName;
+ LPWSTR LocalUserName;
+ PCHAR Where;
+ DWORD Version;
+ DWORD VersionFlags;
+
+ //
+ // Loop ignoring responses which are garbled.
+ //
+
+ for ( ;; ) {
+
+ //
+ // Read the response from the response mailslot
+ // (This mailslot is set up with a 5 second timeout).
+ //
+
+ if ( !ReadFile( ResponseMailslotHandle,
+ ResponseBuffer,
+ sizeof(ResponseBuffer),
+ &SamLogonResponseSize,
+ NULL ) ) {
+
+ IF_DEBUG( MAILSLOT ) {
+ NET_API_STATUS NetStatus;
+ NetStatus = GetLastError();
+
+ if ( NetStatus != ERROR_SEM_TIMEOUT ) {
+ NlPrint((NL_CRITICAL,
+ "NlReadSamLogonResponse: "
+ "cannot read response mailslot: %ld\n",
+ NetStatus ));
+ }
+ }
+ return FALSE;
+ }
+
+ SamLogonResponse = (PNETLOGON_SAM_LOGON_RESPONSE) ResponseBuffer;
+
+ NlPrint((NL_MAILSLOT_TEXT, "NlReadSamLogonResponse opcode 0x%x\n",
+ SamLogonResponse->Opcode ));
+
+ NlpDumpBuffer(NL_MAILSLOT_TEXT, SamLogonResponse, SamLogonResponseSize);
+
+ //
+ // Ensure the opcode is expected.
+ // (Ignore responses from paused DCs, too.)
+ //
+
+ if ( SamLogonResponse->Opcode != LOGON_SAM_LOGON_RESPONSE &&
+ SamLogonResponse->Opcode != LOGON_SAM_USER_UNKNOWN ) {
+ NlPrint((NL_CRITICAL,
+ "NlReadSamLogonResponse: response opcode not valid. 0x%lx\n",
+ SamLogonResponse->Opcode ));
+ continue;
+ }
+
+ //
+ // Ensure the version is expected.
+ //
+
+ Version = NetpLogonGetMessageVersion( SamLogonResponse,
+ &SamLogonResponseSize,
+ &VersionFlags );
+
+ if ( Version != LMNT_MESSAGE ) {
+ NlPrint((NL_CRITICAL,"NlReadSamLogonResponse: version not valid 0x%lx 0x%lx.\n",
+ Version, VersionFlags ));
+ continue;
+ }
+
+ //
+ // Pick up the name of the server that responded.
+ //
+
+ Where = (PCHAR) &SamLogonResponse->UnicodeLogonServer;
+ if ( !NetpLogonGetUnicodeString(
+ SamLogonResponse,
+ SamLogonResponseSize,
+ &Where,
+ sizeof(SamLogonResponse->UnicodeLogonServer),
+ &LocalServerName ) ) {
+
+ NlPrint((NL_CRITICAL,
+ "NlReadSamLogonResponse: "
+ "server name not formatted right\n"));
+ continue;
+ }
+
+ //
+ // Ensure this is a UNC name.
+ //
+
+ if ( LocalServerName[0] != '\\' || LocalServerName[1] != '\\' ) {
+ NlPrint((NL_CRITICAL,
+ "NlReadSamLogonResponse: server name isn't UNC name\n"));
+ continue;
+
+ }
+
+ //
+ // Pick up the name of the account the response is for.
+ //
+
+ if ( !NetpLogonGetUnicodeString(
+ SamLogonResponse,
+ SamLogonResponseSize,
+ &Where,
+ sizeof(SamLogonResponse->UnicodeUserName ),
+ &LocalUserName ) ) {
+
+ NlPrint((NL_CRITICAL,
+ "NlReadSamLogonResponse: User name not formatted right\n"));
+ continue;
+ }
+
+ //
+ // If the response is for the correct account,
+ // break out of the loop.
+ //
+
+ if ( NlNameCompare( AccountName, LocalUserName, NAMETYPE_USER) == 0 ) {
+ break;
+ }
+
+ NlPrint((NL_CRITICAL,
+ "NlReadSamLogonResponse: User name " FORMAT_LPWSTR
+ " s.b. " FORMAT_LPWSTR ".\n",
+ LocalUserName,
+ AccountName ));
+
+
+ }
+
+ //
+ // Return the info to the caller.
+ //
+
+ *Opcode = SamLogonResponse->Opcode;
+ *UncLogonServer = NetpMemoryAllocate(
+ (wcslen(LocalServerName) + 1) * sizeof(WCHAR) );
+
+ if ( *UncLogonServer == NULL ) {
+ NlPrint((NL_CRITICAL, "NlReadSamLogonResponse: Not enough memory\n"));
+ return FALSE;
+ }
+
+ wcscpy( (*UncLogonServer), LocalServerName );
+
+ return TRUE;
+
+}
+
+
+VOID
+NlSaveTrustedDomainList (
+ IN LPWSTR TrustedDomainList
+ )
+
+/*++
+
+Routine Description:
+
+ Save the list of trusted domains to the registry.
+
+Arguments:
+
+ TrustedDomainList - Specifies a list of trusted domains in MULTI_SZ format.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ NET_API_STATUS NetStatus;
+
+ LPNET_CONFIG_HANDLE SectionHandle;
+ LPWSTR LocalTrustedDomainList;
+
+
+ //
+ // Open the NetLogon configuration section.
+ //
+
+ NetStatus = NetpOpenConfigData(
+ &SectionHandle,
+ NULL, // no server name.
+ SERVICE_NETLOGON,
+ FALSE ); // we write access
+
+ if ( NetStatus != NO_ERROR ) {
+ NlPrint((NL_CRITICAL,
+ "NlSaveTrustedDomainList: NetpOpenConfigData failed: %ld\n",
+ NetStatus ));
+
+ } else {
+
+ //
+ // Convert an empty list to a recognizable form
+ //
+
+ if ( TrustedDomainList == NULL ) {
+ LocalTrustedDomainList = L"\0";
+ } else {
+ LocalTrustedDomainList = TrustedDomainList;
+ }
+
+
+
+ //
+ // Write the domain list to the registry
+ //
+
+ NetStatus = NetpSetConfigTStrArray(
+ SectionHandle,
+ NETLOGON_KEYWORD_TRUSTEDDOMAINLIST,
+ LocalTrustedDomainList );
+
+ if ( NetStatus != NO_ERROR ) {
+ NlPrint((NL_CRITICAL,
+ "NlSaveTrustedDomainList: NetpSetConfigTStrArray failed: %ld\n",
+ NetStatus ));
+ }
+
+ (VOID) NetpCloseConfigData( SectionHandle );
+ }
+
+ return;
+}
+
+
+NET_API_STATUS
+NlReadRegTrustedDomainList (
+ IN LPWSTR NewDomainName OPTIONAL,
+ IN BOOL DeleteName,
+ OUT LPWSTR *TrustedDomainList,
+ OUT PBOOL TrustedDomainListKnown
+ )
+
+/*++
+
+Routine Description:
+
+ Read the list of trusted domains from the registry.
+
+Arguments:
+
+ NewDomainName - New DomainName of this domain. When this machine joins a domain,
+ NCPA caches the trusted domain list where we can find it. That ensures the
+ trusted domain list is available upon reboot even before we dial via RAS. Winlogon
+ can therefore get the trusted domain list from us under those circumstances.
+
+ DeleteName - TRUE if the name is to be deleted upon successful completion.
+
+ TrustedDomainList - Returns a list of trusted domains in MULTI_SZ format. Buffer
+ must be freed using NetApiBufferFree.
+
+ TrustedDomainListKnown - Returns true if we know the trusted domain list.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ NET_API_STATUS NetStatus;
+ LPNET_CONFIG_HANDLE SectionHandle = NULL;
+ WCHAR ValueName[sizeof(NETLOGON_KEYWORD_TRUSTEDDOMAINLIST)/sizeof(WCHAR)+1+DNLEN+1];
+
+
+
+ //
+ // Open the NetLogon configuration section.
+ //
+
+ *TrustedDomainListKnown = FALSE;
+ *TrustedDomainList = NULL;
+
+ NetStatus = NetpOpenConfigData(
+ &SectionHandle,
+ NULL, // no server name.
+ SERVICE_NETLOGON,
+ !DeleteName ); // Get Write access if deleting.
+
+ if ( NetStatus != NO_ERROR ) {
+ NlPrint((NL_CRITICAL,
+ "NlReadRegTrustedDomainList: NetpOpenConfigData failed: %ld\n",
+ NetStatus ));
+ }
+
+ //
+ // Get the "TrustedDomainList" configured parameter
+ //
+
+ if ( NewDomainName == NULL ) {
+ *ValueName = L'\0';
+ } else {
+ wcscpy( ValueName, NewDomainName );
+ wcscat( ValueName, L"_" );
+ }
+ wcscat( ValueName, NETLOGON_KEYWORD_TRUSTEDDOMAINLIST );
+
+ NetStatus = NetpGetConfigTStrArray (
+ SectionHandle,
+ ValueName,
+ TrustedDomainList ); // Must be freed by NetApiBufferFree().
+
+ //
+ // Handle the default
+ //
+
+ if (NetStatus == NERR_CfgParamNotFound) {
+ *TrustedDomainList = NULL;
+ } else if (NetStatus != NO_ERROR) {
+ NlPrint((NL_CRITICAL,
+ "NlReadRegTrustedDomainList: NetpGetConfigTStrArray failed: %ld\n",
+ NetStatus ));
+ goto Cleanup;
+ } else {
+ *TrustedDomainListKnown = TRUE;
+ }
+
+ if ( DeleteName && *TrustedDomainListKnown) {
+ NET_API_STATUS TempNetStatus;
+ TempNetStatus = NetpDeleteConfigKeyword ( SectionHandle, ValueName );
+
+ if ( TempNetStatus != NO_ERROR ) {
+ NlPrint((NL_CRITICAL,
+ "NlReadRegTrustedDomainList: NetpDeleteConfigKeyword failed: %ld\n",
+ TempNetStatus ));
+ }
+ }
+
+ NetStatus = NO_ERROR;
+
+Cleanup:
+ if ( SectionHandle != NULL ) {
+ (VOID) NetpCloseConfigData( SectionHandle );
+ }
+
+ return NetStatus;
+}
+
+
+NTSTATUS
+NlGetTrustedDomainList (
+ IN LPWSTR UncDcName,
+ OUT LPWSTR *TrustedDomainList
+ )
+
+/*++
+
+Routine Description:
+
+ Get the list of trusted domains from the specified DC.
+
+Arguments:
+
+ UncDcName - Specifies the name of a DC in the domain.
+
+ TrustedDomainList - Returns a list of trusted domains in MULTI_SZ format.
+ This list should be freed via NetpMemoryFree
+
+Return Value:
+
+ STATUS_SUCCESS - if the trust list was successfully returned
+
+--*/
+{
+ NTSTATUS Status;
+
+ LSA_HANDLE LsaHandle = NULL;
+ UNICODE_STRING UncDcNameString;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+
+ LSA_ENUMERATION_HANDLE EnumerationContext;
+ BOOLEAN AllDone = FALSE;
+
+ LPWSTR CurrentBuffer = NULL;
+ DWORD CurrentSize = 0;
+
+ PLSA_TRUST_INFORMATION TrustList = NULL;
+
+
+ //
+ // Open the policy database on the DC
+ //
+
+ RtlInitUnicodeString( &UncDcNameString, UncDcName );
+
+ InitializeObjectAttributes( &ObjectAttributes, NULL, 0, NULL, NULL );
+
+ Status = LsaOpenPolicy( &UncDcNameString,
+ &ObjectAttributes,
+ POLICY_VIEW_LOCAL_INFORMATION,
+ &LsaHandle );
+
+ if ( !NT_SUCCESS(Status) ) {
+
+ NlPrint((NL_CRITICAL,
+ "NlGetTrustedDomainList: " FORMAT_LPWSTR
+ ": LsaOpenPolicy failed: %lx\n",
+ UncDcName,
+ Status ));
+
+ LsaHandle = NULL;
+ goto Cleanup;
+
+ }
+
+ //
+ // Allocate the buffer in case there are no domains.
+ //
+
+ CurrentBuffer = NetpMemoryAllocate( sizeof(WCHAR) );
+
+ if ( CurrentBuffer == NULL ) {
+ Status = STATUS_NO_MEMORY;
+ goto Cleanup;
+ }
+
+ *CurrentBuffer = L'\0';
+
+ //
+ // Loop getting a list of trusted domains
+ //
+
+ EnumerationContext = 0;
+
+ do {
+ ULONG CountReturned;
+ ULONG i;
+ DWORD Size;
+ LPWSTR NewBuffer;
+ LPWSTR CurrentLoc;
+
+ //
+ // Free any buffers from a previous iteration.
+ //
+ if ( TrustList != NULL ) {
+ (VOID) LsaFreeMemory( TrustList );
+ }
+
+ //
+ // Get more trusted domains names
+ //
+
+ Status = LsaEnumerateTrustedDomains(
+ LsaHandle,
+ &EnumerationContext,
+ (PVOID *) &TrustList,
+ 0xFFFFFFFF,
+ &CountReturned );
+
+ if ( Status == STATUS_NO_MORE_ENTRIES ) {
+ AllDone = TRUE;
+ Status = STATUS_SUCCESS;
+ }
+
+ if ( !NT_SUCCESS(Status) && Status != STATUS_NO_MORE_ENTRIES ) {
+
+ NlPrint((NL_CRITICAL,
+ "NlGetTrustedDomainList: " FORMAT_LPWSTR
+ ": LsaEnumerateTrustedDomains failed: %lx\n",
+ UncDcName,
+ Status ));
+
+ TrustList = NULL;
+ goto Cleanup;
+ }
+
+ if ( CountReturned == 0 ) {
+ continue;
+ }
+
+
+ //
+ // Determine the size of names returned on this call.
+ //
+
+ Size = 0;
+ for ( i=0; i<CountReturned; i++ ) {
+ Size += TrustList[i].Name.Length + sizeof(WCHAR);
+ }
+
+ //
+ // Reallocate the buffer.
+ //
+
+ NewBuffer = NetpMemoryReallocate( CurrentBuffer, Size + CurrentSize + sizeof(WCHAR) );
+
+ if ( NewBuffer == NULL ) {
+ Status = STATUS_NO_MEMORY;
+ goto Cleanup;
+ }
+
+ CurrentBuffer = NewBuffer;
+ CurrentLoc = &NewBuffer[CurrentSize/sizeof(WCHAR)];
+ CurrentSize += Size;
+
+ //
+ // Handle each trusted domain.
+ //
+
+ for ( i=0; i<CountReturned; i++ ) {
+ LPWSTR CurrentDomainName;
+
+ //
+ // Copy the new domain name into the buffer
+ //
+
+ CurrentDomainName = CurrentLoc;
+
+ RtlCopyMemory( CurrentLoc,
+ TrustList[i].Name.Buffer,
+ TrustList[i].Name.Length );
+ CurrentLoc += TrustList[i].Name.Length / sizeof(WCHAR);
+
+ *(CurrentLoc++) = L'\0';
+ *CurrentLoc = L'\0'; // Place double terminator each time
+
+ //
+ // Ensure the SID of the trusted domain isn't the domain sid of this
+ // machine.
+ //
+
+ if ( RtlEqualSid( TrustList[i].Sid, NlGlobalDBInfoArray[SAM_DB].DBId )) {
+
+ LPWSTR AlertStrings[3];
+
+ //
+ // alert admin.
+ //
+
+ AlertStrings[0] = NlGlobalUnicodeComputerName;
+ AlertStrings[1] = CurrentDomainName;
+ AlertStrings[2] = NULL;
+
+ RaiseAlert( ALERT_NetLogonSidConflict,
+ AlertStrings );
+
+ //
+ // Save the info in the eventlog
+ //
+
+ NlpWriteEventlog(
+ ALERT_NetLogonSidConflict,
+ EVENTLOG_ERROR_TYPE,
+ TrustList[i].Sid,
+ RtlLengthSid( TrustList[i].Sid ),
+ AlertStrings,
+ 2 );
+
+ }
+ }
+
+ } while ( !AllDone );
+
+ //
+ // Save the collected information to the registry
+ //
+
+ NlSaveTrustedDomainList ( CurrentBuffer );
+
+ //
+ // Remember the list in globals
+ //
+
+ NlSetTrustedDomainList ( CurrentBuffer, TRUE );
+
+ Status = STATUS_SUCCESS;
+
+ //
+ // Free any locally used resources.
+ //
+Cleanup:
+
+ if ( LsaHandle != NULL ) {
+ (VOID) LsaClose( LsaHandle );
+ }
+
+ if ( TrustList != NULL ) {
+ (VOID) LsaFreeMemory( TrustList );
+ }
+
+ if ( !NT_SUCCESS(Status) ) {
+ if ( CurrentBuffer != NULL ) {
+ NetpMemoryFree( CurrentBuffer );
+ CurrentBuffer = NULL;
+ }
+ }
+ *TrustedDomainList = CurrentBuffer;
+ return Status;
+}
+
+
+NTSTATUS
+NlSetTrustedDomainList (
+ IN LPWSTR TrustedDomainList,
+ IN BOOL TrustedDomainListKnown
+ )
+
+/*++
+
+Routine Description:
+
+ Set the domain list in globals
+
+Arguments:
+
+ TrustedDomainList - Specifies a list of trusted domains in MULTI_SZ format.
+
+ TrustedDomainListKnown - TRUE if TrustedDomainList has been retrieved
+ from a DC in the domain.
+
+Return Value:
+
+ Status of the operation.
+
+ Upon failure, the previous list remains intact.
+
+--*/
+{
+ PTRUSTED_DOMAIN LocalTrustedDomainList;
+ DWORD LocalTrustedDomainCount;
+ DWORD LocalTrustedDomainSize;
+ DWORD i;
+
+ PTRUSTED_DOMAIN OldList;
+ LPWSTR CurrentEntry;
+
+
+ //
+ // If the new list is zero length,
+ // don't bother allocating anything.
+ //
+
+ if ( TrustedDomainList == NULL ) {
+ LocalTrustedDomainList = NULL;
+ LocalTrustedDomainCount = 0;
+
+ //
+ // Otherwise, build a buffer of the trusted domain list
+ //
+
+ } else {
+
+ //
+ // Allocate a buffer for the new list
+ //
+
+ LocalTrustedDomainCount = NetpTStrArrayEntryCount( TrustedDomainList );
+
+ LocalTrustedDomainList = NetpMemoryAllocate(
+ LocalTrustedDomainCount *
+ sizeof(TRUSTED_DOMAIN) );
+
+ if ( LocalTrustedDomainList == NULL && LocalTrustedDomainCount != 0 ) {
+ return STATUS_NO_MEMORY;
+ }
+
+ //
+ // Copy the names to the new structure upper casing them and
+ // converting to OEM.
+ //
+
+ NlPrint((NL_LOGON, "NlSetTrustedDomainList: New trusted domain list:\n" ));
+ CurrentEntry = TrustedDomainList;
+
+ for ( i=0; i<LocalTrustedDomainCount; i++ ) {
+ NTSTATUS Status;
+ UNICODE_STRING UnicodeString;
+ OEM_STRING OemString;
+
+ NlPrint((NL_LOGON, " " FORMAT_LPWSTR "\n", CurrentEntry ));
+
+ //
+ // Convert the input string to OEM
+ //
+
+ RtlInitUnicodeString( &UnicodeString, CurrentEntry );
+
+ OemString.Buffer = LocalTrustedDomainList[i].DomainName;
+ OemString.MaximumLength = sizeof(LocalTrustedDomainList[i].DomainName);
+
+ Status = RtlUpcaseUnicodeStringToOemString(
+ &OemString,
+ &UnicodeString,
+ FALSE ); // Don't Allocate dest
+
+ if ( !NT_SUCCESS( Status ) ) {
+ NlPrint(( NL_CRITICAL,
+ "Can't convert to OEM: " FORMAT_LPWSTR ": %lX\n",
+ CurrentEntry,
+ Status ));
+
+ NetpMemoryFree( LocalTrustedDomainList );
+ return Status;
+ }
+
+
+ CurrentEntry += (UnicodeString.Length / sizeof(WCHAR)) + 1;
+
+ }
+ }
+
+ //
+ // Swap in the new list
+
+ LOCK_TRUST_LIST();
+ OldList = NlGlobalTrustedDomainList;
+ NlGlobalTrustedDomainList = LocalTrustedDomainList;
+ NlGlobalTrustedDomainCount = LocalTrustedDomainCount;
+ NlGlobalTrustedDomainListKnown = TrustedDomainListKnown;
+ NtQuerySystemTime( &NlGlobalTrustedDomainListTime );
+ UNLOCK_TRUST_LIST();
+
+ //
+ // Free the old list.
+ //
+
+ if ( OldList != NULL ) {
+ NetpMemoryFree( OldList );
+ }
+
+ return STATUS_SUCCESS;
+}
+
+
+BOOLEAN
+NlIsDomainTrusted (
+ IN PUNICODE_STRING DomainName
+ )
+
+/*++
+
+Routine Description:
+
+ Determine if the specified domain is trusted.
+
+ If the trusted domain list has not been obtained from the DC,
+ indicate the domain is trusted. This causes the caller fall back to
+ the prior behaviour of indicating that the DC cannot be contacted.
+ This status code is special cased in MSV1_0 to indicate that cached
+ credentials should be tried. This ensures that a newly upgraded RAS
+ client continues to use cached credentials until it dials in the first
+ time.
+
+Arguments:
+
+
+ DomainName - Name of the domain to query.
+
+Return Value:
+
+ TRUE - if the domain name specified is a trusted domain.
+
+--*/
+{
+ NTSTATUS Status;
+ DWORD i;
+
+ OEM_STRING OemString;
+ CHAR OemBuffer[DNLEN+1];
+
+ OEM_STRING CurrentDomainName;
+
+ //
+ // If the no domain name was specified,
+ // indicate the domain is not trusted.
+ //
+
+ if ( DomainName == NULL || DomainName->Length == 0 ) {
+ return FALSE;
+ }
+
+ //
+ // Convert the input string to OEM
+ //
+
+ OemString.MaximumLength = sizeof(OemBuffer);
+ OemString.Buffer = OemBuffer;
+
+ Status = RtlUpcaseUnicodeStringToOemString( &OemString,
+ DomainName,
+ FALSE ); // Don't Allocate dest
+
+ if ( !NT_SUCCESS( Status ) ) {
+ return FALSE;
+ }
+
+ //
+ // Consider the Primary Domain to be a trusted domain, too
+ //
+
+ RtlInitString( &CurrentDomainName, NlGlobalAnsiDomainName );
+
+ if ( RtlEqualString( &OemString, &CurrentDomainName, FALSE ) ) {
+ return TRUE;
+ }
+
+ //
+ // If we have no trusted domain list,
+ // fall back to previous behavior.
+ //
+
+ if ( !NlGlobalTrustedDomainListKnown ) {
+ return TRUE;
+ }
+
+ //
+ // Compare the input trusted domain name to each element in the list
+ //
+
+ LOCK_TRUST_LIST();
+ for ( i=0; i<NlGlobalTrustedDomainCount; i++ ) {
+
+ RtlInitString( &CurrentDomainName,
+ NlGlobalTrustedDomainList[i].DomainName );
+
+ //
+ // Simply compare the bytes (both are already uppercased)
+ //
+ if ( RtlEqualString( &OemString, &CurrentDomainName, FALSE ) ) {
+ UNLOCK_TRUST_LIST();
+ return TRUE;
+ }
+
+ }
+ UNLOCK_TRUST_LIST();
+
+ //
+ // All other domains aren't trusted.
+ //
+
+ return FALSE;
+}
+
+
+//
+// Define the Actions to NlDcDiscoveryMachine.
+//
+
+typedef enum {
+ StartDiscovery,
+ DcFoundMessage,
+ DcNotFoundMessage,
+ DcTimerExpired
+} DISCOVERY_ACTION;
+
+//
+// number of broadcastings to get DC before reporting DC not found
+// error.
+//
+
+#define MAX_DC_RETRIES 3
+
+
+NTSTATUS
+NlDcDiscoveryMachine(
+ IN OUT PCLIENT_SESSION ClientSession,
+ IN DISCOVERY_ACTION Action,
+ IN LPWSTR UncDcName OPTIONAL,
+ IN LPWSTR TransportName OPTIONAL,
+ IN LPSTR ResponseMailslotName OPTIONAL,
+ IN DISCOVERY_TYPE DiscoveryType
+ )
+
+/*++
+
+Routine Description:
+
+ State machine to get the name of a DC in a domain.
+
+Arguments:
+
+ ClientSession -- Client session structure whose DC is to be picked.
+ The Client Session structure must be referenced.
+
+ Action -- The event which just occurred.
+
+ UncDcName -- If the Action is DcFoundMessage, this is the name of the newly
+ found domain controller.
+
+ TransportName -- If the Action is DcFoundMessage, this is the name of the
+ transport the domain controller can be reached on.
+
+ ResponseMailslotName -- If action is StartDiscovery or DcTimerExpired,
+ this name is the name of the mailslot that the response is sent to.
+
+ DiscoveryType -- Indicate synchronous, Asynchronous, or rediscovery of a
+ "Dead domain".
+
+
+Return Value:
+
+ STATUS_SUCCESS - if DC was found.
+ STATUS_PENDING - if discovery is still in progress and the caller should
+ call again in DISCOVERY_PERIOD with the DcTimerExpired action.
+
+ STATUS_NO_LOGON_SERVERS - if DC was not found.
+ STATUS_NO_TRUST_SAM_ACCOUNT - if DC was found but it does not have
+ an account for this machine.
+
+--*/
+{
+ NTSTATUS Status;
+
+ PNETLOGON_SAM_LOGON_REQUEST SamLogonRequest = NULL;
+
+ NlAssert( ClientSession->CsReferenceCount > 0 );
+ EnterCriticalSection( &NlGlobalDcDiscoveryCritSect );
+
+ //
+ // Handle a new request to start discovery and timer expiration.
+ //
+ switch (Action) {
+ case StartDiscovery:
+ case DcTimerExpired: {
+
+ DWORD DomainSidSize;
+ ULONG AllowableAccountControlBits;
+
+ WCHAR NetlogonMailslotName[DNLEN+NETLOGON_NT_MAILSLOT_LEN+5];
+ PCHAR Where;
+
+ //
+ // If discovery is currently going on,
+ // ignore this new request.
+ // If discovery isn't currently going on,
+ // ignore a timer expiration.
+ //
+
+ if ( (ClientSession->CsDiscoveryFlags & CS_DISCOVERY_IN_PROGRESS) &&
+ Action == StartDiscovery ){
+ Status = STATUS_SUCCESS;
+ goto Ignore;
+
+ } else if (
+ (ClientSession->CsDiscoveryFlags & CS_DISCOVERY_IN_PROGRESS) == 0 &&
+ Action == DcTimerExpired ){
+ if ( ClientSession->CsState == CS_IDLE ) {
+ Status = ClientSession->CsConnectionStatus;
+ } else {
+ Status = STATUS_SUCCESS;
+ }
+ goto Ignore;
+ }
+
+
+ //
+ // Increment/set the retry count
+ //
+
+ if ( Action == StartDiscovery ) {
+ ClientSession->CsDiscoveryFlags |= CS_DISCOVERY_IN_PROGRESS;
+ ClientSession->CsDiscoveryRetryCount = 0;
+
+
+ NlAssert( ClientSession->CsDiscoveryEvent != NULL );
+
+ if ( !ResetEvent( ClientSession->CsDiscoveryEvent ) ) {
+ NlPrint(( NL_CRITICAL,
+ "NlDcDiscoveryMachine: %ws: ResetEvent failed %ld\n",
+ ClientSession->CsDomainName.Buffer,
+ GetLastError() ));
+ }
+
+ NlPrint(( NL_SESSION_SETUP,
+ "NlDcDiscoveryMachine: %ws: Start Discovery\n",
+ ClientSession->CsDomainName.Buffer ));
+ } else {
+ ClientSession->CsDiscoveryRetryCount ++;
+ if ( ClientSession->CsDiscoveryRetryCount == MAX_DC_RETRIES ) {
+ NlPrint(( NL_CRITICAL,
+ "NlDcDiscoveryMachine: %ws: Discovery failed\n",
+ ClientSession->CsDomainName.Buffer ));
+ Status = STATUS_NO_LOGON_SERVERS;
+ goto Cleanup;
+ }
+ NlPrint(( NL_SESSION_SETUP,
+ "NlDcDiscoveryMachine: %ws: Discovery retry %ld\n",
+ ClientSession->CsDomainName.Buffer,
+ ClientSession->CsDiscoveryRetryCount ));
+ }
+
+
+ //
+ // Determine the Account type we're looking for.
+ //
+
+ if ( ClientSession->CsSecureChannelType == WorkstationSecureChannel ) {
+ AllowableAccountControlBits = USER_WORKSTATION_TRUST_ACCOUNT;
+ } else if ( ClientSession->CsSecureChannelType ==
+ TrustedDomainSecureChannel ) {
+ AllowableAccountControlBits = USER_INTERDOMAIN_TRUST_ACCOUNT;
+ } else if ( ClientSession->CsSecureChannelType ==
+ ServerSecureChannel ) {
+ AllowableAccountControlBits = USER_SERVER_TRUST_ACCOUNT;
+ } else {
+ NlPrint(( NL_CRITICAL,
+ "NlDcDiscoveryMachine: %ws: "
+ "invalid SecureChannelType retry %ld\n",
+ ClientSession->CsDomainName.Buffer,
+ ClientSession->CsSecureChannelType ));
+ Status = STATUS_NO_LOGON_SERVERS;
+ goto Cleanup;
+ }
+
+ //
+ // Initialization memory for the logon request message.
+ //
+
+ DomainSidSize = RtlLengthSid( ClientSession->CsDomainId );
+
+ SamLogonRequest = NetpMemoryAllocate(
+ sizeof(NETLOGON_SAM_LOGON_REQUEST) +
+ DomainSidSize +
+ sizeof(DWORD) // for SID alignment on 4 byte boundary
+ );
+
+ if( SamLogonRequest == NULL ) {
+ NlPrint(( NL_CRITICAL, "NlDcDiscoveryMachine can't allocate memory\n"));
+ // This isn't the real status, but callers handle this status
+ Status = STATUS_NO_LOGON_SERVERS;
+ goto Cleanup;
+ }
+
+
+ //
+ // Build the query message.
+ //
+
+ SamLogonRequest->Opcode = LOGON_SAM_LOGON_REQUEST;
+ SamLogonRequest->RequestCount =
+ (USHORT) ClientSession->CsDiscoveryRetryCount;
+
+ Where = (PCHAR) &SamLogonRequest->UnicodeComputerName;
+
+ NetpLogonPutUnicodeString(
+ NlGlobalUnicodeComputerName,
+ sizeof(SamLogonRequest->UnicodeComputerName),
+ &Where );
+
+ NetpLogonPutUnicodeString(
+ ClientSession->CsAccountName,
+ sizeof(SamLogonRequest->UnicodeUserName),
+ &Where );
+
+ NetpLogonPutOemString(
+ ResponseMailslotName,
+ sizeof(SamLogonRequest->MailslotName),
+ &Where );
+
+ NetpLogonPutBytes(
+ &AllowableAccountControlBits,
+ sizeof(SamLogonRequest->AllowableAccountControlBits),
+ &Where );
+
+ //
+ // place domain SID in the message.
+ //
+
+ NetpLogonPutBytes( &DomainSidSize, sizeof(DomainSidSize), &Where );
+ NetpLogonPutDomainSID( ClientSession->CsDomainId, DomainSidSize, &Where );
+
+ NetpLogonPutNtToken( &Where );
+
+
+ //
+ // Broadcast the message to each Netlogon service in the domain.
+ //
+ // We are sending to the DomainName* name which will be received by
+ // all NT DCs including those DCs on a WAN.
+ //
+ // When doing the discover of the PDC for this domain, send to
+ // DomainName** which only sends to Domain<1B> which is registered
+ // only by the PDC.
+ //
+
+ NetlogonMailslotName[0] = '\\';
+ NetlogonMailslotName[1] = '\\';
+ wcscpy(NetlogonMailslotName+2, ClientSession->CsDomainName.Buffer );
+ wcscat(NetlogonMailslotName, L"*" );
+ if ( ClientSession->CsSecureChannelType == ServerSecureChannel ) {
+ wcscat(NetlogonMailslotName, L"*" );
+ }
+ wcscat(NetlogonMailslotName, NETLOGON_NT_MAILSLOT_W );
+
+ Status = NlpWriteMailslot(
+ NetlogonMailslotName,
+ SamLogonRequest,
+ Where - (PCHAR)(SamLogonRequest) );
+
+ if ( !NT_SUCCESS(Status) ) {
+ NlPrint(( NL_CRITICAL,
+ "NlDcDiscoveryMachine: %ws: "
+ "cannot write netlogon mailslot 0x%lx\n",
+ ClientSession->CsDomainName.Buffer,
+ Status));
+
+ Status = STATUS_NO_LOGON_SERVERS;
+ goto Cleanup;
+ }
+
+
+ //
+ // If this is an asynchronous call and this is the first call,
+ // start the periodic timer.
+ //
+ if ( DiscoveryType == DT_Asynchronous && Action == StartDiscovery ) {
+ if ( NlGlobalDcDiscoveryCount == 0 ) {
+ NlGlobalDcDiscoveryTimer.Period =
+ DISCOVERY_PERIOD + NlGlobalExpectedDialupDelayParameter*1000/MAX_DC_RETRIES;
+ (VOID) NtQuerySystemTime( &NlGlobalDcDiscoveryTimer.StartTime );
+
+ //
+ // If netlogon is exitting,
+ // the main thread is already gone.
+ //
+
+ if ( NlGlobalTerminate ) {
+ Status = STATUS_NO_LOGON_SERVERS;
+ goto Cleanup;
+ }
+
+ //
+ // Tell the main thread that I've changed a timer.
+ //
+
+ if ( !SetEvent( NlGlobalTimerEvent ) ) {
+ NlPrint(( NL_CRITICAL,
+ "NlDcDiscoveryMachine: %ws: SetEvent2 failed %ld\n",
+ ClientSession->CsDomainName.Buffer,
+ GetLastError() ));
+ }
+
+ }
+ NlGlobalDcDiscoveryCount ++;
+ ClientSession->CsDiscoveryFlags |= CS_DISCOVERY_ASYNCHRONOUS;
+
+ //
+ // Don't let the session go away during discovery.
+ //
+ LOCK_TRUST_LIST();
+ NlRefClientSession( ClientSession );
+ UNLOCK_TRUST_LIST();
+
+ //
+ // If this is merely an attempt to revive a "dead" domain,
+ // we just send the single mailslot message above and exit discovery.
+ // If any DC responds, we'll pick up the response even though
+ // discovery isn't in progress.
+ //
+
+ } else if ( DiscoveryType == DT_DeadDomain ) {
+ Status = ClientSession->CsConnectionStatus;
+ goto Cleanup;
+ }
+
+ Status = STATUS_PENDING;
+ goto Ignore;
+
+ }
+
+ //
+ // Handle when a DC claims to be the DC for the requested domain.
+ //
+
+ case DcFoundMessage:
+
+ //
+ // If we already know the name of a DC,
+ // ignore this new name.
+ //
+ // When we implement doing discovery while a session is already up,
+ // we need to handle the case where someone has the ClientSession
+ // write locked. In that case, we should probably just hang the new
+ // DCname somewhere off the ClientSession structure and swap in the
+ // new DCname when the writer drops the write lock. ??
+ //
+
+ if ( ClientSession->CsState != CS_IDLE ) {
+
+ NlPrint(( NL_SESSION_SETUP,
+ "NlDcDiscoveryMachine: %ws: DC %ws ignored."
+ " DC previously found.\n",
+ ClientSession->CsDomainName.Buffer,
+ UncDcName ));
+ Status = STATUS_SUCCESS;
+ goto Ignore;
+ }
+
+
+ //
+ // Install the new DC name in the Client session
+ //
+
+ wcsncpy( ClientSession->CsUncServerName, UncDcName, UNCLEN );
+ ClientSession->CsUncServerName[UNCLEN] = L'\0';
+
+
+
+ //
+ // Save the transport this discovery came in on.
+ //
+ if ( TransportName == NULL ) {
+ NlPrint(( NL_SESSION_SETUP,
+ "NlDcDiscoveryMachine: %ws: Found DC %ws\n",
+ ClientSession->CsDomainName.Buffer,
+ UncDcName ));
+ } else {
+ NlPrint(( NL_SESSION_SETUP,
+ "NlDcDiscoveryMachine: %ws: Found DC %ws on transport %ws\n",
+ ClientSession->CsDomainName.Buffer,
+ UncDcName,
+ TransportName ));
+
+ ClientSession->CsTransportName =
+ NlTransportLookupTransportName( TransportName );
+
+ if ( ClientSession->CsTransportName == NULL ) {
+ NlPrint(( NL_CRITICAL,
+ "NlDcDiscoveryMachine: " FORMAT_LPWSTR ": Transport not found\n",
+ TransportName ));
+ }
+ }
+
+ //
+ // If this is a BDC discovering it's PDC,
+ // save the PDC name.
+ // Start the replicator and let it figure if it needs to be running.
+ //
+
+ if ( ClientSession->CsSecureChannelType == ServerSecureChannel ) {
+ NlSetPrimaryName( ClientSession->CsUncServerName+2 );
+ (VOID) NlStartReplicatorThread( 0 );
+ }
+
+
+
+ Status = STATUS_SUCCESS;
+ goto Cleanup;
+
+
+ case DcNotFoundMessage:
+
+ //
+ // If we already know the name of a DC,
+ // ignore this new name.
+ //
+
+ if ( ClientSession->CsState != CS_IDLE ) {
+
+ NlPrint(( NL_SESSION_SETUP,
+ "NlDcDiscoveryMachine: %ws: DC %ws ignored."
+ " DC previously found.\n",
+ ClientSession->CsDomainName.Buffer,
+ UncDcName ));
+ Status = STATUS_SUCCESS;
+ goto Ignore;
+ }
+
+ //
+ // If discovery isn't currently going on,
+ // ignore this extraneous message.
+ //
+
+ if ((ClientSession->CsDiscoveryFlags & CS_DISCOVERY_IN_PROGRESS) == 0 ){
+ NlPrint(( NL_SESSION_SETUP,
+ "NlDcDiscoveryMachine: %ws: DC %ws ignored."
+ " Discovery not in progress.\n",
+ ClientSession->CsDomainName.Buffer,
+ UncDcName ));
+ Status = ClientSession->CsConnectionStatus;
+ goto Ignore;
+ }
+
+ NlPrint(( NL_CRITICAL,
+ "NlDcDiscoveryMachine: %ws: "
+ "Received No Such Account message\n",
+ ClientSession->CsDomainName.Buffer));
+
+ Status = STATUS_NO_TRUST_SAM_ACCOUNT;
+ goto Cleanup;
+
+ }
+
+ //
+ // We never reach here.
+ //
+ NlAssert(FALSE);
+
+
+ //
+ // Handle discovery being completed.
+ //
+Cleanup:
+ //
+ // On success,
+ // Indicate that the session setup is allowed to happen immediately.
+ //
+ // Leave CsConnectionStatus with a "failure" status code until the
+ // secure channel is set up. Other, routines simply return
+ // CsConnectionStatus as the state of the secure channel.
+ //
+
+ if ( NT_SUCCESS(Status) ) {
+ ClientSession->CsLastAuthenticationTry.QuadPart = 0;
+ ClientSession->CsState = CS_DC_PICKED;
+
+ //
+ // On failure,
+ // Indicate that we've recently made the attempt to find a DC.
+ //
+
+ } else {
+ NtQuerySystemTime( &ClientSession->CsLastAuthenticationTry );
+ ClientSession->CsState = CS_IDLE;
+ ClientSession->CsConnectionStatus = Status;
+ }
+
+ NtQuerySystemTime( &ClientSession->CsLastDiscoveryTime );
+
+
+ //
+ // Tell the initiator that discover has completed.
+ //
+
+ ClientSession->CsDiscoveryFlags &= ~CS_DISCOVERY_IN_PROGRESS;
+
+ NlAssert( ClientSession->CsDiscoveryEvent != NULL );
+
+ if ( !SetEvent( ClientSession->CsDiscoveryEvent ) ) {
+ NlPrint(( NL_CRITICAL,
+ "NlDcDiscoveryMachine: %ws: SetEvent failed %ld\n",
+ ClientSession->CsDomainName.Buffer,
+ GetLastError() ));
+ }
+
+
+ //
+ // If this was an async discovery,
+ // turn the timer off.
+ //
+
+ if ( ClientSession->CsDiscoveryFlags & CS_DISCOVERY_ASYNCHRONOUS ) {
+ ClientSession->CsDiscoveryFlags &= ~CS_DISCOVERY_ASYNCHRONOUS;
+ NlGlobalDcDiscoveryCount--;
+ if ( NlGlobalDcDiscoveryCount == 0 ) {
+ NlGlobalDcDiscoveryTimer.Period = (DWORD) MAILSLOT_WAIT_FOREVER;
+ }
+
+ //
+ // We no longer care about the Client session
+ //
+ LOCK_TRUST_LIST();
+ NlUnrefClientSession( ClientSession );
+ UNLOCK_TRUST_LIST();
+ }
+
+
+ //
+ // Cleanup locally used resources.
+ //
+Ignore:
+
+ //
+ // free log request message.
+ //
+
+ if( SamLogonRequest != NULL ) {
+ NetpMemoryFree( SamLogonRequest );
+ }
+
+ //
+ // Unlock the crit sect and return.
+ //
+ LeaveCriticalSection( &NlGlobalDcDiscoveryCritSect );
+ return Status;
+
+}
+
+
+NTSTATUS
+NlDiscoverDc (
+ IN OUT PCLIENT_SESSION ClientSession,
+ IN DISCOVERY_TYPE DiscoveryType
+ )
+
+/*++
+
+Routine Description:
+
+ Get the name of a DC in a domain.
+
+ On Entry,
+ The trust list must NOT be locked.
+ The trust list entry must be referenced by the caller.
+ The caller must be a writer of the trust list entry.
+
+Arguments:
+
+ ClientSession -- Client session structure whose DC is to be picked.
+ The Client Session structure must be marked for write.
+
+ DiscoveryType -- Indicate synchronous, Asynchronous, or rediscovery of a
+ "Dead domain".
+
+Return Value:
+
+ STATUS_SUCCESS - if DC was found.
+ STATUS_PENDING - Operation is still in progress
+ STATUS_NO_LOGON_SERVERS - if DC was not found.
+ STATUS_NO_TRUST_SAM_ACCOUNT - if DC was found but it does not have
+ an account for this machine.
+
+--*/
+{
+ NTSTATUS Status;
+ HANDLE ResponseMailslotHandle = NULL;
+ CHAR ResponseMailslotName[PATHLEN+1];
+
+ NlAssert( ClientSession->CsReferenceCount > 0 );
+ NlAssert( ClientSession->CsFlags & CS_WRITER );
+
+
+
+ //
+ // If this is a BDC discovering its own PDC,
+ // and we've already discovered the PDC
+ // (via NetGetDcName or the PDC has spontaneously told us its name),
+ // just use that name.
+ //
+ // If we're our own PDC,
+ // we must have just been demoted to a BDC and haven't found PDC yet,
+ // in that case rediscover.
+ //
+
+ if ( ClientSession->CsSecureChannelType == ServerSecureChannel &&
+ *NlGlobalUnicodePrimaryName != L'\0' &&
+ NlNameCompare( NlGlobalUnicodePrimaryName,
+ NlGlobalUnicodeComputerName,
+ NAMETYPE_COMPUTER) != 0 ) {
+
+
+ //
+ // Just set the PDC name in the Client Session structure.
+ //
+
+ EnterCriticalSection( &NlGlobalDcDiscoveryCritSect );
+
+ wcscpy( ClientSession->CsUncServerName, NlGlobalUncPrimaryName );
+ ClientSession->CsLastAuthenticationTry.QuadPart = 0;
+ NtQuerySystemTime( &ClientSession->CsLastDiscoveryTime );
+ ClientSession->CsState = CS_DC_PICKED;
+
+ LeaveCriticalSection( &NlGlobalDcDiscoveryCritSect );
+
+ Status = STATUS_SUCCESS;
+ goto Cleanup;
+ }
+
+
+ //
+ // If this is a workstation,
+ // Create a mailslot for the DC's to respond to.
+ //
+
+ if ( NlGlobalRole == RoleMemberWorkstation ) {
+ NET_API_STATUS NetStatus;
+
+ NlAssert( DiscoveryType == DT_Synchronous );
+ NetStatus = NetpLogonCreateRandomMailslot( ResponseMailslotName,
+ &ResponseMailslotHandle);
+
+ if ( NetStatus != NERR_Success ) {
+
+ NlPrint((NL_CRITICAL,
+ "NlDiscoverDc: cannot create temp mailslot %ld\n",
+ NetStatus ));
+
+ Status = NetpApiStatusToNtStatus( NetStatus );
+ goto Cleanup;
+ }
+
+ //
+ // If the mailslot timeout shouldn't be the default 5 seconds,
+ // set it to the right value.
+ //
+
+ if ( NlGlobalExpectedDialupDelayParameter != 0 ) {
+
+ if ( !SetMailslotInfo(
+ ResponseMailslotHandle,
+ DISCOVERY_PERIOD + NlGlobalExpectedDialupDelayParameter*1000/MAX_DC_RETRIES ) ) {
+
+ NetStatus = GetLastError();
+
+ NlPrint((NL_CRITICAL,
+ "NlDiscoverDc: cannot change temp mailslot timeout %ld\n",
+ NetStatus ));
+
+ Status = NetpApiStatusToNtStatus( NetStatus );
+ goto Cleanup;
+ }
+
+
+ }
+
+ } else {
+ lstrcpyA( ResponseMailslotName, NETLOGON_NT_MAILSLOT_A );
+ }
+
+
+
+ //
+ // Start discovery.
+ //
+
+ Status = NlDcDiscoveryMachine( ClientSession,
+ StartDiscovery,
+ NULL,
+ NULL,
+ ResponseMailslotName,
+ DiscoveryType );
+
+ if ( !NT_SUCCESS(Status) || DiscoveryType != DT_Synchronous ) {
+ goto Cleanup;
+ }
+
+
+ //
+ // If the discovery machine asked us to call back every DISCOVERY_PERIOD,
+ // loop doing exactly that.
+ //
+
+ if ( Status == STATUS_PENDING ) {
+
+ //
+ // Loop waiting.
+ //
+
+ for (;;) {
+
+ DWORD WaitStatus;
+
+ //
+ // On non-workstations,
+ // the main loop gets the mailslot responses.
+ // (So just do the timeout here).
+ //
+
+ if ( NlGlobalRole != RoleMemberWorkstation ) {
+
+ //
+ // Wait for DISOVERY_PERIOD.
+ //
+
+ WaitStatus =
+ WaitForSingleObject( ClientSession->CsDiscoveryEvent,
+ DISCOVERY_PERIOD + NlGlobalExpectedDialupDelayParameter*1000/MAX_DC_RETRIES );
+
+
+ if ( WaitStatus == 0 ) {
+
+ break;
+
+ } else if ( WaitStatus != WAIT_TIMEOUT ) {
+
+ NlPrint((NL_CRITICAL,
+ "NlDiscoverDc: wait error: %ld\n",
+ WaitStatus ));
+ Status = NetpApiStatusToNtStatus( WaitStatus );
+ goto Cleanup;
+ }
+
+ // Drop through to indicate timer expiration
+
+ //
+ // Workstations do the mailslot read directly.
+ //
+
+ } else {
+ CHAR ResponseBuffer[MAX_RANDOM_MAILSLOT_RESPONSE];
+ PNETLOGON_SAM_LOGON_RESPONSE SamLogonResponse;
+ DWORD SamLogonResponseSize;
+
+
+ //
+ // Read the response from the response mailslot
+ // (This mailslot is set up with a 5 second timeout).
+ //
+
+ if ( ReadFile( ResponseMailslotHandle,
+ ResponseBuffer,
+ sizeof(ResponseBuffer),
+ &SamLogonResponseSize,
+ NULL ) ) {
+ DWORD Version;
+ DWORD VersionFlags;
+
+ SamLogonResponse =
+ (PNETLOGON_SAM_LOGON_RESPONSE) ResponseBuffer;
+
+ //
+ // get message version.
+ //
+
+ Version = NetpLogonGetMessageVersion(
+ SamLogonResponse,
+ &SamLogonResponseSize,
+ &VersionFlags );
+
+ //
+ // Handle the incoming message.
+ //
+
+ Status = NlDcDiscoveryHandler ( SamLogonResponse,
+ SamLogonResponseSize,
+ NULL, // Transport name
+ Version );
+
+ if ( Status != STATUS_PENDING ) {
+ goto Cleanup;
+ }
+
+ //
+ // Ignore badly formed responses.
+ //
+
+ continue;
+
+
+ } else {
+ WaitStatus = GetLastError();
+
+ if ( WaitStatus != ERROR_SEM_TIMEOUT ) {
+ NlPrint((NL_CRITICAL,
+ "NlDiscoverDc: "
+ "cannot read response mailslot: %ld\n",
+ WaitStatus ));
+ Status = NetpApiStatusToNtStatus( WaitStatus );
+ goto Cleanup;
+ }
+
+ }
+
+ }
+
+
+ //
+ // If we reach here,
+ // DISCOVERY_PERIOD has expired.
+ //
+
+ Status = NlDcDiscoveryMachine( ClientSession,
+ DcTimerExpired,
+ NULL,
+ NULL,
+ ResponseMailslotName,
+ DiscoveryType );
+
+ if ( Status != STATUS_PENDING ) {
+ goto Cleanup;
+ }
+
+ }
+
+ //
+ // If someone else started the discovery,
+ // just wait for that discovery to finish.
+ //
+
+ } else {
+
+ NlWaitForSingleObject( "Client Session waiting for discovery",
+ ClientSession->CsDiscoveryEvent );
+
+ }
+
+
+ //
+ // Return the status to the caller.
+ //
+
+ if ( ClientSession->CsState == CS_IDLE ) {
+ Status = ClientSession->CsConnectionStatus;
+ } else {
+ Status = STATUS_SUCCESS;
+ }
+
+Cleanup:
+ if ( ResponseMailslotHandle != NULL ) {
+ CloseHandle(ResponseMailslotHandle);
+ }
+
+ //
+ // If this is a workstation,
+ // get the trusted domain list from the discovered DC.
+ //
+
+ if ( NlGlobalRole == RoleMemberWorkstation && NT_SUCCESS(Status) ) {
+ NTSTATUS TempStatus;
+ LPWSTR TrustedDomainList;
+
+ TempStatus = NlGetTrustedDomainList (
+ ClientSession->CsUncServerName,
+ &TrustedDomainList );
+
+ if ( NT_SUCCESS( TempStatus ) ) {
+
+ NetpMemoryFree( TrustedDomainList );
+ }
+ }
+
+ return Status;
+}
+
+
+NTSTATUS
+NlUpdateTrustListBySid (
+ IN PSID DomainId,
+ IN PUNICODE_STRING DomainName OPTIONAL
+ )
+
+/*++
+
+Routine Description:
+
+ Update a single in-memory trust list entry to match the LSA.
+ Do async discovery on a domain.
+
+Arguments:
+
+ DomainId -- Domain Id of the domain to do the discovery for.
+
+ DomainName -- Specifies the DomainName of the domain. If this parameter
+ isn't specified, the LSA is queried for the name. If this parameter
+ is specified, the LSA is guaranteed to contain this domain.
+
+Return Value:
+
+ Status of the operation.
+
+--*/
+{
+ NTSTATUS Status;
+
+ PLIST_ENTRY ListEntry;
+ PCLIENT_SESSION ClientSession = NULL;
+ PUNICODE_STRING LocalDomainName;
+
+ LSAPR_HANDLE TrustedDomainHandle = NULL;
+ PLSAPR_TRUSTED_DOMAIN_INFO TrustedDomainName = NULL;
+
+
+ //
+ // If the domain name was passed in,
+ // there is no need to query the LSA for the name.
+ //
+
+ if ( DomainName != NULL ) {
+ LocalDomainName = DomainName;
+
+ //
+ // Determine if the TrustedDomain object exists in the LSA.
+ //
+
+ } else {
+
+ Status = LsarOpenTrustedDomain(
+ NlGlobalPolicyHandle,
+ DomainId,
+ TRUSTED_QUERY_DOMAIN_NAME,
+ &TrustedDomainHandle );
+
+ if ( NT_SUCCESS(Status) ) {
+
+ Status = LsarQueryInfoTrustedDomain(
+ TrustedDomainHandle,
+ TrustedDomainNameInformation,
+ &TrustedDomainName );
+
+ if ( !NT_SUCCESS(Status) ) {
+ NlPrint(( NL_CRITICAL,
+ "NlUpdateTrustListBySid: "
+ "cannot LsarQueryInfoTrustedDomain: %lx\n",
+ Status));
+ TrustedDomainName = NULL;
+ goto Cleanup;
+ }
+
+ LocalDomainName =
+ (PUNICODE_STRING)&TrustedDomainName->TrustedDomainNameInfo.Name;
+
+ } else {
+
+ LocalDomainName = NULL;
+
+ }
+
+ }
+
+ //
+ // Ensure the SID of the trusted domain isn't the domain sid of this
+ // machine.
+ //
+
+ if ( RtlEqualSid( DomainId, NlGlobalPrimaryDomainId )) {
+
+ LPWSTR AlertStrings[3];
+ WCHAR AlertDomainName[DNLEN+1];
+
+ //
+ // alert admin.
+ //
+
+
+ if ( LocalDomainName == NULL ||
+ LocalDomainName->Length > sizeof(AlertDomainName) ) {
+ AlertDomainName[0] = L'\0';
+ } else {
+ RtlCopyMemory( AlertDomainName, LocalDomainName->Buffer, LocalDomainName->Length );
+ AlertDomainName[ LocalDomainName->Length / sizeof(WCHAR) ] = L'\0';
+ }
+
+ AlertStrings[0] = NlGlobalUnicodeDomainName;
+ AlertStrings[1] = AlertDomainName;
+ AlertStrings[2] = NULL;
+
+ RaiseAlert( ALERT_NetLogonSidConflict,
+ AlertStrings );
+
+ //
+ // Save the info in the eventlog
+ //
+
+ NlpWriteEventlog(
+ ALERT_NetLogonSidConflict,
+ EVENTLOG_ERROR_TYPE,
+ DomainId,
+ RtlLengthSid( DomainId ),
+ AlertStrings,
+ 2 );
+
+ }
+
+
+ //
+ // Loop through the trust list finding the right entry.
+ //
+
+ LOCK_TRUST_LIST();
+ for ( ListEntry = NlGlobalTrustList.Flink ;
+ ListEntry != &NlGlobalTrustList ;
+ ListEntry = ListEntry->Flink) {
+
+ ClientSession = CONTAINING_RECORD( ListEntry, CLIENT_SESSION, CsNext );
+
+ if ( RtlEqualSid( ClientSession->CsDomainId, DomainId ) ) {
+ break;
+ }
+
+ ClientSession = NULL;
+
+ }
+
+
+
+ //
+ // At this point,
+ // LocalDomainName is NULL if the trust relationship doesn't exist in LSA
+ // ClientSession is NULL if the trust relationship doesn't exist in memory
+ //
+
+ //
+ // If the Trust exists in neither place,
+ // ignore this request.
+ //
+
+ if ( LocalDomainName == NULL && ClientSession == NULL ) {
+ UNLOCK_TRUST_LIST();
+ Status = STATUS_SUCCESS;
+ goto Cleanup;
+
+
+
+ //
+ // If the trust exists in the LSA but not in memory,
+ // add the trust entry.
+ //
+
+ } else if ( LocalDomainName != NULL && ClientSession == NULL ) {
+
+ ClientSession = NlAllocateClientSession(
+ LocalDomainName,
+ DomainId,
+ TrustedDomainSecureChannel );
+
+ if (ClientSession == NULL) {
+ UNLOCK_TRUST_LIST();
+ Status = STATUS_NO_MEMORY;
+ goto Cleanup;
+ }
+
+ //
+ // Link this entry onto the tail of the TrustList.
+ //
+
+ InsertTailList( &NlGlobalTrustList, &ClientSession->CsNext );
+ NlGlobalTrustListLength ++;
+
+ NlPrint((NL_SESSION_SETUP,
+ "NlUpdateTrustListBySid: " FORMAT_LPWSTR
+ ": Added to local trust list\n",
+ ClientSession->CsDomainName.Buffer ));
+
+
+
+ //
+ // If the trust exists in memory but not in the LSA,
+ // delete the entry.
+ //
+
+ } else if ( LocalDomainName == NULL && ClientSession != NULL ) {
+
+ NlPrint((NL_SESSION_SETUP,
+ "NlUpdateTrustListBySid: " FORMAT_LPWSTR
+ ": Deleted from local trust list\n",
+ ClientSession->CsDomainName.Buffer ));
+ NlFreeClientSession( ClientSession );
+ ClientSession = NULL;
+
+
+ //
+ // If the trust exists in both places,
+ // undo any pending deletion.
+ //
+
+ } else if ( LocalDomainName != NULL && ClientSession != NULL ) {
+
+ ClientSession->CsFlags &= ~CS_DELETE_ON_UNREF;
+ NlRefClientSession( ClientSession );
+
+ NlPrint((NL_SESSION_SETUP,
+ "NlUpdateTrustListBySid: " FORMAT_LPWSTR
+ ": Already in trust list\n",
+ ClientSession->CsDomainName.Buffer ));
+
+ }
+
+ UNLOCK_TRUST_LIST();
+
+ //
+ // If we haven't discovered a DC for this domain,
+ // and we haven't tried discovery recently,
+ // start the discovery asynchronously
+ //
+
+ if ( ClientSession != NULL &&
+ ClientSession->CsState == CS_IDLE &&
+ NlTimeToReauthenticate( ClientSession ) ) {
+
+ //
+ // Only wait for 45 seconds. This routine is called by the netlogon main
+ // thread. Another thread may have the ClientSession locked and need the
+ // main thread to finish a discovery.
+ //
+
+ if ( !NlTimeoutSetWriterClientSession( ClientSession, WRITER_WAIT_PERIOD ) ) {
+ Status = STATUS_SUCCESS;
+ goto Cleanup;
+ }
+
+ Status = NlDiscoverDc ( ClientSession, DT_Asynchronous );
+ NlResetWriterClientSession( ClientSession );
+
+ if ( Status == STATUS_PENDING ) {
+ Status = STATUS_SUCCESS;
+ }
+ goto Cleanup;
+ }
+
+ Status = STATUS_SUCCESS;
+
+ //
+ // Cleanup locally used resources.
+ //
+Cleanup:
+ if ( TrustedDomainName != NULL ) {
+ LsaIFree_LSAPR_TRUSTED_DOMAIN_INFO(
+ TrustedDomainNameInformation,
+ TrustedDomainName );
+ }
+
+ if ( TrustedDomainHandle != NULL ) {
+ NTSTATUS LocalStatus;
+ LocalStatus = LsarClose( &TrustedDomainHandle );
+ NlAssert( NT_SUCCESS( LocalStatus ));
+ }
+
+ if ( ClientSession != NULL ) {
+ NlUnrefClientSession( ClientSession );
+ }
+
+ return Status;
+}
+
+
+
+VOID
+NlDcDiscoveryExpired (
+ IN BOOLEAN Exitting
+ )
+
+/*++
+
+Routine Description:
+
+ Handle expiration of the DC discovery timer.
+
+Arguments:
+
+ NONE
+
+Return Value:
+
+ Exitting: TRUE if the netlogon service is exitting
+
+--*/
+{
+ PLIST_ENTRY ListEntry;
+ PCLIENT_SESSION ClientSession;
+
+
+ NlAssert( NlGlobalRole != RoleMemberWorkstation );
+
+
+ LOCK_TRUST_LIST();
+
+ //
+ // Mark each entry to indicate we've not yet handled the timer expiration
+ //
+
+ if ( !Exitting ) {
+ for ( ListEntry = NlGlobalTrustList.Flink ;
+ ListEntry != &NlGlobalTrustList ;
+ ListEntry = ListEntry->Flink) {
+
+ ClientSession = CONTAINING_RECORD( ListEntry,
+ CLIENT_SESSION,
+ CsNext );
+
+ ClientSession->CsFlags |= CS_HANDLE_TIMER;
+ }
+ }
+
+
+ //
+ // Loop thru the trust list handling timer expiration.
+ //
+
+ for ( ListEntry = NlGlobalTrustList.Flink ;
+ ListEntry != &NlGlobalTrustList ;
+ ) {
+
+ ClientSession = CONTAINING_RECORD( ListEntry,
+ CLIENT_SESSION,
+ CsNext );
+
+ //
+ // If we've already done this entry,
+ // skip this entry.
+ //
+ if ( !Exitting ) {
+ if ( (ClientSession->CsFlags & CS_HANDLE_TIMER) == 0 ) {
+ ListEntry = ListEntry->Flink;
+ continue;
+ }
+ ClientSession->CsFlags &= ~CS_HANDLE_TIMER;
+ }
+
+
+ //
+ // If async discovery isn't going on,
+ // skip this entry.
+ //
+
+ if ((ClientSession->CsDiscoveryFlags & CS_DISCOVERY_ASYNCHRONOUS) == 0){
+ ListEntry = ListEntry->Flink;
+ continue;
+ }
+
+ //
+ // Call the discovery machine with the trust list unlocked.
+ //
+
+ UNLOCK_TRUST_LIST();
+
+ (VOID) NlDcDiscoveryMachine( ClientSession,
+ DcTimerExpired,
+ NULL,
+ NULL,
+ NETLOGON_NT_MAILSLOT_A,
+ TRUE );
+
+ //
+ // Since we dropped the trust list lock,
+ // we'll start the search from the front of the list.
+ //
+
+ LOCK_TRUST_LIST();
+
+ ListEntry = NlGlobalTrustList.Flink ;
+
+ }
+
+ UNLOCK_TRUST_LIST();
+
+ //
+ // Complete the asynchronous discover on the Global client session.
+ //
+
+ if ( NlGlobalClientSession != NULL &&
+ NlGlobalClientSession->CsDiscoveryFlags & CS_DISCOVERY_ASYNCHRONOUS ) {
+ (VOID) NlDcDiscoveryMachine( NlGlobalClientSession,
+ DcTimerExpired,
+ NULL,
+ NULL,
+ NETLOGON_NT_MAILSLOT_A,
+ TRUE );
+
+ }
+
+}
+
+
+NTSTATUS
+NlDcDiscoveryHandler (
+ IN PNETLOGON_SAM_LOGON_RESPONSE Message,
+ IN DWORD MessageSize,
+ IN LPWSTR TransportName,
+ IN DWORD Version
+ )
+
+/*++
+
+Routine Description:
+
+ Handle a mailslot response to a DC Discovery request.
+
+Arguments:
+
+ Message -- The response message
+
+ MessageSize -- The size of the message in bytes.
+
+ TransportName -- Name of the transport the messages arrived on.
+
+ Version -- version info of the message.
+
+Return Value:
+
+ STATUS_SUCCESS - if DC was found.
+ STATUS_PENDING - if discovery is still in progress and the caller should
+ call again in DISCOVERY_PERIOD with the DcTimerExpired action.
+
+ STATUS_NO_LOGON_SERVERS - if DC was not found.
+ STATUS_NO_TRUST_SAM_ACCOUNT - if DC was found but it does not have
+ an account for this machine.
+
+--*/
+{
+ NTSTATUS Status;
+ LPWSTR LocalServerName;
+ LPWSTR LocalUserName;
+ LPWSTR LocalDomainName;
+ PCHAR Where;
+ PCLIENT_SESSION ClientSession = NULL;
+ UNICODE_STRING DomainNameString;
+
+
+ if ( Version != LMNT_MESSAGE ) {
+ NlPrint((NL_CRITICAL,
+ "NlDcDiscoveryHandler: version not valid.\n"));
+ Status = STATUS_PENDING;
+ goto Cleanup;
+ }
+
+
+ //
+ // Ignore messages from paused DCs.
+ //
+
+ if ( Message->Opcode != LOGON_SAM_LOGON_RESPONSE &&
+ Message->Opcode != LOGON_SAM_USER_UNKNOWN ) {
+ Status = STATUS_PENDING;
+ goto Cleanup;
+ }
+
+ //
+ // Pick up the name of the server that responded.
+ //
+
+ Where = (PCHAR) &Message->UnicodeLogonServer;
+ if ( !NetpLogonGetUnicodeString(
+ Message,
+ MessageSize,
+ &Where,
+ sizeof(Message->UnicodeLogonServer),
+ &LocalServerName ) ) {
+
+ NlPrint((NL_CRITICAL,
+ "NlDcDiscoveryHandler: server name not formatted right\n"));
+ Status = STATUS_PENDING;
+ goto Cleanup;
+ }
+
+ //
+ // Pick up the name of the account the response is for.
+ //
+
+ if ( !NetpLogonGetUnicodeString(
+ Message,
+ MessageSize,
+ &Where,
+ sizeof(Message->UnicodeUserName ),
+ &LocalUserName ) ) {
+
+ NlPrint((NL_CRITICAL,
+ "NlDcDiscoveryHandler: User name not formatted right\n"));
+ Status = STATUS_PENDING;
+ goto Cleanup;
+ }
+
+ //
+ // If the domain name is not in the message,
+ // ignore the message.
+ //
+
+ if( Where >= ((PCHAR)Message + MessageSize) ) {
+
+ NlPrint((NL_CRITICAL,
+ "NlDcDiscoveryHandler: "
+ "Response from %ws doesn't contain domain name\n",
+ LocalServerName ));
+
+ if ( NlGlobalRole == RoleMemberWorkstation ) {
+
+ LocalDomainName = NlGlobalUnicodeDomainName;
+
+ NlPrint((NL_SESSION_SETUP,
+ "NlDcDiscoveryHandler: "
+ "Workstation: Assuming %ws is in domain %ws\n",
+ LocalServerName,
+ LocalDomainName ));
+
+ } else {
+ Status = STATUS_PENDING;
+ goto Cleanup;
+ }
+
+
+ //
+ // Pick up the name of the domain the response is for.
+ //
+
+ } else {
+ if ( !NetpLogonGetUnicodeString(
+ Message,
+ MessageSize,
+ &Where,
+ sizeof(Message->UnicodeDomainName ),
+ &LocalDomainName ) ) {
+
+ NlPrint((NL_CRITICAL,
+ "NlDcDiscoveryHandler: "
+ " Domain name from %ws not formatted right\n",
+ LocalServerName ));
+ Status = STATUS_PENDING;
+ goto Cleanup;
+ }
+ }
+
+ //
+ // On the PDC or BDC,
+ // find the Client session for the domain.
+ // On workstations,
+ // find the primary domain client session.
+ //
+
+
+ RtlInitUnicodeString( &DomainNameString, LocalDomainName );
+
+ ClientSession = NlFindNamedClientSession( &DomainNameString );
+
+ if ( ClientSession == NULL ) {
+ NlPrint((NL_SESSION_SETUP,
+ "NlDcDiscoveryHandler: "
+ " Domain name %ws from %ws has no client session.\n",
+ LocalDomainName,
+ LocalServerName ));
+ Status = STATUS_PENDING;
+ goto Cleanup;
+ }
+
+
+
+
+ //
+ // Ensure the response is for the correct account.
+ //
+
+ if ( NlNameCompare( ClientSession->CsAccountName,
+ LocalUserName,
+ NAMETYPE_USER) != 0 ) {
+
+ NlPrint((NL_CRITICAL,
+ "NlDcDiscoveryHandler: "
+ " Domain name %ws from %ws has invalid account name %ws.\n",
+ LocalDomainName,
+ LocalServerName,
+ LocalUserName ));
+ Status = STATUS_PENDING;
+ goto Cleanup;
+ }
+
+ //
+ // Finally, tell the DC discovery machine what happened.
+ //
+
+
+#ifdef DONT_REQUIRE_ACCOUNT
+ IF_DEBUG( DONT_REQUIRE_ACCOUNT ) {
+ Message->Opcode = LOGON_SAM_LOGON_RESPONSE;
+ }
+#endif // DONT_REQUIRE_ACCOUNT
+
+ Status = NlDcDiscoveryMachine(
+ ClientSession,
+ (Message->Opcode == LOGON_SAM_LOGON_RESPONSE) ?
+ DcFoundMessage :
+ DcNotFoundMessage,
+ LocalServerName,
+ TransportName,
+ NULL,
+ FALSE );
+
+
+ //
+ // Free any locally used resources.
+ //
+Cleanup:
+ if ( ClientSession != NULL ) {
+ NlUnrefClientSession( ClientSession );
+ }
+
+ return Status;
+}
+
+
+
+
+
+NTSTATUS
+NlCaptureServerClientSession (
+ IN PCLIENT_SESSION ClientSession,
+ OUT WCHAR UncServerName[UNCLEN+1]
+ )
+/*++
+
+Routine Description:
+
+ Captures a copy of the UNC server name for the client session.
+
+ On Entry,
+ The trust list must NOT be locked.
+ The trust list entry must be referenced by the caller.
+ The caller must NOT be a writer of the trust list entry.
+
+Arguments:
+
+ ClientSession - Specifies a pointer to the trust list entry to use.
+
+ UncServerName - Returns the UNC name of the server for this client session.
+ If there is none, an empty string is returned.
+
+Return Value:
+
+ STATUS_SUCCESS - Server name was successfully copied.
+
+ Otherwise - Status of the secure channel
+--*/
+{
+ NTSTATUS Status;
+
+ NlAssert( ClientSession->CsReferenceCount > 0 );
+
+ EnterCriticalSection( &NlGlobalDcDiscoveryCritSect );
+ if ( ClientSession->CsState == CS_IDLE ) {
+ Status = ClientSession->CsConnectionStatus;
+ *UncServerName = L'\0';
+ } else {
+ Status = STATUS_SUCCESS;
+ wcscpy( UncServerName, ClientSession->CsUncServerName );
+ }
+ LeaveCriticalSection( &NlGlobalDcDiscoveryCritSect );
+
+ return Status;
+}
+
+
+PCLIENT_SESSION
+NlPickDomainWithAccount (
+ IN LPWSTR AccountName,
+ IN ULONG AllowableAccountControlBits
+ )
+
+/*++
+
+Routine Description:
+
+ Get the name of a trusted domain that defines a particular account.
+
+Arguments:
+
+ AccountName - Name of our user account to find.
+
+ AllowableAccountControlBits - A mask of allowable SAM account types that
+ are allowed to satisfy this request.
+
+Return Value:
+
+ Pointer to referenced ClientSession structure describing the secure channel
+ to the domain containing the account.
+
+ The returned ClientSession is referenced and should be unreferenced
+ using NlUnrefClientSession.
+
+ NULL - DC was not found.
+
+--*/
+{
+ NTSTATUS Status;
+ NET_API_STATUS NetStatus;
+
+ PCLIENT_SESSION ClientSession;
+ DWORD i;
+ PLIST_ENTRY ListEntry;
+ DWORD ResponsesPending;
+
+ NETLOGON_SAM_LOGON_REQUEST SamLogonRequest;
+ PCHAR Where;
+
+ HANDLE ResponseMailslotHandle = NULL;
+ CHAR ResponseMailslotName[PATHLEN+1];
+ DWORD Opcode;
+ DWORD DomainSidSize;
+
+
+ //
+ // Define a local list of trusted domains.
+ //
+
+ ULONG LocalTrustListLength;
+ ULONG Index;
+ struct _LOCAL_TRUST_LIST {
+
+ //
+ // TRUE if ALL processing is finished on this trusted domain.
+ //
+
+ BOOLEAN Done;
+
+ //
+ // TRUE if at least one discovery has been done on this trusted domain.
+ //
+
+ BOOLEAN DiscoveryDone;
+
+ //
+ // TRUE if discovery is in progress on this trusted domain.
+ //
+
+ BOOLEAN DoingDiscovery;
+
+ //
+ // Number of times we need to repeat the current domain discovery
+ // or finduser datagram for this current domain.
+ //
+
+ DWORD RetriesLeft;
+
+ //
+ // Pointer to referenced ClientSession structure for the domain.
+ //
+
+ PCLIENT_SESSION ClientSession;
+
+ //
+ // Server name for the domain.
+ //
+
+ WCHAR UncServerName[UNCLEN+1];
+
+ //
+ // Second Server name for the domain.
+ //
+
+ WCHAR UncServerName2[UNCLEN+1];
+
+ } *LocalTrustList = NULL;
+
+ //
+ // Be verbose.
+ //
+
+ NlPrint((NL_LOGON,
+ "NlPickDomainWithAccount: %ws: Algorithm entered.\n",
+ AccountName ));
+
+
+ //
+ // Don't allow bogus user names.
+ //
+ // NlReadSamLogonResponse uses NlNameCompare to ensure the response message
+ // is for this user. Since NlNameCompare canonicalizes both names, it will
+ // reject invalid syntax. That causes NlReadSamLogonResponse to ignore ALL
+ // response messages, thus causing multiple retries before failing. We'd
+ // rather fail here.
+ //
+
+ if ( !NetpIsUserNameValid( AccountName ) ){
+ NlPrint((NL_CRITICAL,
+ "NlPickDomainWithAccount: Username " FORMAT_LPWSTR
+ " is invalid syntax.\n",
+ AccountName ));
+ return NULL;
+ }
+
+ //
+ // Allocate a local list of trusted domains.
+ //
+
+ LOCK_TRUST_LIST();
+ LocalTrustListLength = NlGlobalTrustListLength;
+
+ LocalTrustList = (struct _LOCAL_TRUST_LIST *) NetpMemoryAllocate(
+ LocalTrustListLength * sizeof(struct _LOCAL_TRUST_LIST));
+
+ if ( LocalTrustList == NULL ) {
+ UNLOCK_TRUST_LIST();
+ ClientSession = NULL;
+ goto Cleanup;
+ }
+
+
+ //
+ // Build a local list of trusted domains we know DCs for.
+ //
+
+
+ Index = 0;
+ for ( ListEntry = NlGlobalTrustList.Flink ;
+ ListEntry != &NlGlobalTrustList ;
+ ListEntry = ListEntry->Flink) {
+
+ ClientSession = CONTAINING_RECORD( ListEntry, CLIENT_SESSION, CsNext );
+
+ //
+ // Add this Client Session to the list.
+ //
+
+ NlRefClientSession( ClientSession );
+
+ LocalTrustList[Index].ClientSession = ClientSession;
+ Index++;
+ }
+
+ UNLOCK_TRUST_LIST();
+
+
+ //
+ // Capture the name of the server for each client session.
+ //
+
+ for ( Index = 0; Index < LocalTrustListLength; Index ++ ) {
+
+ (VOID) NlCaptureServerClientSession(
+ LocalTrustList[Index].ClientSession,
+ LocalTrustList[Index].UncServerName );
+
+ *LocalTrustList[Index].UncServerName2 = L'\0';
+
+ //
+ // We're not done yet.
+ //
+
+ LocalTrustList[Index].Done = FALSE;
+
+ //
+ // If there is no DC discovered for this domain,
+ // don't try very hard to discover one.
+ // (Indeed, just one discovery datagram is all we need.)
+ //
+
+ if ( *LocalTrustList[Index].UncServerName == L'\0' ) {
+ LocalTrustList[Index].RetriesLeft = 1;
+ LocalTrustList[Index].DoingDiscovery = TRUE;
+ LocalTrustList[Index].DiscoveryDone = TRUE;
+
+ //
+ // If we know the DC for this domain,
+ // try sending to the current DC before discovering a new one.
+ //
+ } else {
+ LocalTrustList[Index].RetriesLeft = 3;
+ LocalTrustList[Index].DoingDiscovery = FALSE;
+ LocalTrustList[Index].DiscoveryDone = FALSE;
+ }
+
+ }
+
+ //
+ // Create a mailslot for the DC's to respond to.
+ //
+
+ if (NetStatus = NetpLogonCreateRandomMailslot( ResponseMailslotName,
+ &ResponseMailslotHandle)){
+ NlPrint((NL_CRITICAL,
+ "NlPickDomainWithAccount: cannot create temp mailslot %ld\n",
+ NetStatus ));
+ ClientSession = NULL;
+ goto Cleanup;
+ }
+
+ //
+ // Build the query message.
+ //
+
+ SamLogonRequest.Opcode = LOGON_SAM_LOGON_REQUEST;
+ SamLogonRequest.RequestCount = 0;
+
+ Where = (PCHAR) &SamLogonRequest.UnicodeComputerName;
+
+ NetpLogonPutUnicodeString(
+ NlGlobalUnicodeComputerName,
+ sizeof(SamLogonRequest.UnicodeComputerName),
+ &Where );
+
+ NetpLogonPutUnicodeString(
+ AccountName,
+ sizeof(SamLogonRequest.UnicodeUserName),
+ &Where );
+
+ NetpLogonPutOemString(
+ ResponseMailslotName,
+ sizeof(SamLogonRequest.MailslotName),
+ &Where );
+
+ NetpLogonPutBytes(
+ &AllowableAccountControlBits,
+ sizeof(SamLogonRequest.AllowableAccountControlBits),
+ &Where );
+
+ //
+ // place domain NULL SID in the message.
+ //
+
+ DomainSidSize = 0;
+ NetpLogonPutBytes( &DomainSidSize, sizeof(DomainSidSize), &Where );
+
+ NetpLogonPutNtToken( &Where );
+
+
+ //
+ // Try multiple times to get a response from each DC.
+ //
+
+ for (;; ) {
+
+ //
+ // Send the mailslot message to each domain that has not yet responded.
+ //
+
+ ResponsesPending = 0;
+
+ for ( Index = 0; Index < LocalTrustListLength; Index ++ ) {
+
+ //
+ // If this domain has already responded, ignore it.
+ //
+
+ if ( LocalTrustList[Index].Done ) {
+ continue;
+ }
+
+ //
+ // If we don't currently know the DC name for this domain,
+ // capture any that's been discovered since we started the algorithm.
+ //
+
+ ClientSession = LocalTrustList[Index].ClientSession;
+ if ( *LocalTrustList[Index].UncServerName == L'\0' ) {
+
+ (VOID) NlCaptureServerClientSession(
+ LocalTrustList[Index].ClientSession,
+ LocalTrustList[Index].UncServerName );
+
+ //
+ // Handle the case where we've now discovered a DC.
+ //
+
+ if ( *LocalTrustList[Index].UncServerName != L'\0' ) {
+
+ NlPrint((NL_LOGON,
+ "NlPickDomainWithAccount: %ws: Noticed domain %wZ has discovered a new DC %ws.\n",
+ AccountName,
+ &ClientSession->CsDomainName,
+ LocalTrustList[Index].UncServerName ));
+
+ //
+ // If we did the discovery,
+ //
+
+ if ( LocalTrustList[Index].DoingDiscovery ) {
+ LocalTrustList[Index].DoingDiscovery = FALSE;
+ LocalTrustList[Index].RetriesLeft = 3;
+ }
+ }
+ }
+
+ //
+ // If we're done retrying what we were doing,
+ // try something else.
+ //
+
+ if ( LocalTrustList[Index].RetriesLeft == 0 ) {
+ if ( LocalTrustList[Index].DiscoveryDone ) {
+ LocalTrustList[Index].Done = TRUE;
+
+ NlPrint((NL_LOGON,
+ "NlPickDomainWithAccount: %ws: Can't find DC for domain %wZ (ignore this domain).\n",
+ AccountName,
+ &ClientSession->CsDomainName ));
+
+ continue;
+ } else {
+
+ //
+ // Save the previous DC name since it might just be
+ // very slow in responding. We'll want to be able
+ // to recognize responses from the previous DC.
+ //
+
+ wcscpy( LocalTrustList[Index].UncServerName2,
+ LocalTrustList[Index].UncServerName );
+
+ *LocalTrustList[Index].UncServerName = L'\0';
+
+ LocalTrustList[Index].DoingDiscovery = TRUE;
+ LocalTrustList[Index].RetriesLeft = 3;
+ LocalTrustList[Index].DiscoveryDone = TRUE;
+ }
+ }
+
+ //
+ // Indicate we're trying something.
+ //
+
+ LocalTrustList[Index].RetriesLeft --;
+
+ ResponsesPending ++;
+
+ //
+ // If its time to discover a DC in the domain,
+ // do it.
+ //
+
+
+ if ( LocalTrustList[Index].DoingDiscovery ) {
+
+ //
+ // Discover a new server
+ //
+
+ if ( NlTimeoutSetWriterClientSession( ClientSession, 10*1000 ) ) {
+
+ //
+ // Only tear down an existing secure channel once.
+ //
+
+ if ( LocalTrustList[Index].RetriesLeft == 3 ) {
+ NlSetStatusClientSession( ClientSession,
+ STATUS_NO_LOGON_SERVERS );
+ }
+
+ //
+ // We can't afford to wait so only send a single
+ // discovery datagram.
+ //
+ // Since the discovery is asysnchronous,
+ // it is very unlikely that we'll actually
+ // be able to query the DC on this pass. As such, this
+ // entire iteration of the loop is typically
+ // dedicated to discovery.
+ //
+
+ (VOID) NlDiscoverDc( ClientSession, DT_DeadDomain );
+
+ NlResetWriterClientSession( ClientSession );
+
+ }
+
+ }
+
+ //
+ // Send the message to a DC for the domain.
+ //
+
+ if ( *LocalTrustList[Index].UncServerName != L'\0' ) {
+ CHAR OemServerName[CNLEN+1];
+
+ // Skip over \\ in unc server name
+ NetpCopyWStrToStr( OemServerName,
+ LocalTrustList[Index].UncServerName+2 );
+
+ Status = NlBrowserSendDatagram(
+ OemServerName,
+ ClientSession->CsTransportName,
+ NETLOGON_NT_MAILSLOT_A,
+ &SamLogonRequest,
+ Where - (PCHAR)(&SamLogonRequest) );
+
+ if ( !NT_SUCCESS(Status) ) {
+ NlPrint((NL_CRITICAL,
+ "NlPickDomainWithAccount: "
+ " cannot write netlogon mailslot: 0x%lx\n",
+ Status));
+ ClientSession = NULL;
+ goto Cleanup;
+ }
+ }
+
+ }
+
+ //
+ // If all of the domains are done,
+ // leave the loop.
+ //
+
+ if ( ResponsesPending == 0 ) {
+ break;
+ }
+
+ //
+ // See if any DC responds.
+ //
+
+ while ( ResponsesPending > 0 ) {
+ LPWSTR UncLogonServer;
+
+ //
+ // If we timed out,
+ // break out of the loop.
+ //
+
+ if ( !NlReadSamLogonResponse( ResponseMailslotHandle,
+ AccountName,
+ &Opcode,
+ &UncLogonServer ) ) {
+ break;
+ }
+
+ //
+ // Find out which DC responded
+ //
+ // ?? Optimize by converting to uppercase OEM outside of loop
+
+ for ( Index = 0; Index < LocalTrustListLength; Index ++ ) {
+
+ ClientSession = LocalTrustList[Index].ClientSession;
+
+ if ( (*LocalTrustList[Index].UncServerName != L'\0' &&
+ NlNameCompare( LocalTrustList[Index].UncServerName+2,
+ UncLogonServer+2,
+ NAMETYPE_COMPUTER ) == 0 ) ||
+ (*LocalTrustList[Index].UncServerName2 != L'\0' &&
+ NlNameCompare( LocalTrustList[Index].UncServerName2+2,
+ UncLogonServer+2,
+ NAMETYPE_COMPUTER ) == 0 ) ) {
+ break;
+ }
+ }
+
+ NetpMemoryFree( UncLogonServer );
+
+ //
+ // If the response wasn't for one of the DCs we sent to,
+ // ignore the response.
+ //
+
+ if ( Index >= LocalTrustListLength ) {
+ NlPrint((NL_CRITICAL,
+ "NlPickDomainWithAccount: Server %ws responded though we didn't query it for account %ws.",
+ UncLogonServer,
+ AccountName ));
+ continue;
+ }
+
+ //
+ // If the DC recognizes our account,
+ // we've successfully found the DC.
+ //
+
+ if ( Opcode == LOGON_SAM_LOGON_RESPONSE ) {
+ NlPrint((NL_LOGON,
+ "NlPickDomainWithAccount: "
+ "%wZ has account " FORMAT_LPWSTR "\n",
+ &ClientSession->CsDomainName,
+ AccountName ));
+ goto Cleanup;
+ }
+
+ //
+ // If this DC has already responded once,
+ // ignore the response,
+ //
+
+ if ( LocalTrustList[Index].Done ) {
+ continue;
+ }
+
+ //
+ // Mark another DC as having responded negatively.
+ //
+
+ NlPrint((NL_CRITICAL,
+ "NlPickDomainWithAccount: "
+ "%wZ responded negatively for account "
+ FORMAT_LPWSTR " 0x%x\n",
+ &ClientSession->CsDomainName,
+ AccountName,
+ Opcode ));
+
+ LocalTrustList[Index].Done = TRUE;
+ ResponsesPending --;
+
+ }
+ }
+
+ //
+ // No DC has the specified account.
+ //
+
+ ClientSession = NULL;
+
+ //
+ // Cleanup locally used resources.
+ //
+
+Cleanup:
+ if ( ResponseMailslotHandle != NULL ) {
+ CloseHandle(ResponseMailslotHandle);
+ }
+
+
+ //
+ // Unreference each client session structure and free the local trust list.
+ // (Keep the returned ClientSession referenced).
+ //
+
+ if ( LocalTrustList != NULL ) {
+
+ for (i=0; i<LocalTrustListLength; i++ ) {
+ if ( ClientSession != LocalTrustList[i].ClientSession ) {
+ NlUnrefClientSession( LocalTrustList[i].ClientSession );
+ }
+ }
+
+ NetpMemoryFree(LocalTrustList);
+ }
+
+ return ClientSession;
+}
+
+
+NTSTATUS
+NlStartApiClientSession(
+ IN PCLIENT_SESSION ClientSession,
+ IN BOOLEAN QuickApiCall
+ )
+/*++
+
+Routine Description:
+
+ Enable the timer for timing out an API call on the secure channel.
+
+ On Entry,
+ The trust list must NOT be locked.
+ The caller must be a writer of the trust list entry.
+
+Arguments:
+
+ ClientSession - Structure used to define the session.
+
+ QuickApiCall - True if this API call MUST finish in less than 45 seconds
+ and will in reality finish in less than 15 seconds unless something
+ is terribly wrong.
+
+Return Value:
+
+ Status of the RPC binding to the server
+
+--*/
+{
+ NTSTATUS Status;
+ BOOLEAN BindingHandleCached;
+ LARGE_INTEGER TimeNow;
+
+ //
+ // Save the current time.
+ // Start the timer on the API call.
+ //
+
+ LOCK_TRUST_LIST();
+ NtQuerySystemTime( &TimeNow );
+ ClientSession->CsApiTimer.StartTime = TimeNow;
+ ClientSession->CsApiTimer.Period =
+ QuickApiCall ? NlGlobalShortApiCallPeriod : LONG_API_CALL_PERIOD;
+
+ //
+ // If the global timer isn't running,
+ // start it and tell the main thread that I've changed a timer.
+ //
+
+ if ( NlGlobalBindingHandleCount == 0 ) {
+
+ if ( NlGlobalApiTimer.Period != NlGlobalShortApiCallPeriod ) {
+
+ NlGlobalApiTimer.Period = NlGlobalShortApiCallPeriod;
+ NlGlobalApiTimer.StartTime = TimeNow;
+
+ if ( !SetEvent( NlGlobalTimerEvent ) ) {
+ NlPrint(( NL_CRITICAL,
+ "NlStartApiClientSession: %ws: SetEvent failed %ld\n",
+ ClientSession->CsDomainName.Buffer,
+ GetLastError() ));
+ }
+ }
+ }
+
+
+ //
+ // Remember if the binding handle is cached, then mark it as cached.
+ //
+
+ BindingHandleCached = (ClientSession->CsFlags & CS_BINDING_CACHED) != 0;
+ ClientSession->CsFlags |= CS_BINDING_CACHED;
+
+
+ //
+ // Count the number of concurrent binding handles cached
+ //
+
+ if ( !BindingHandleCached ) {
+ NlGlobalBindingHandleCount ++;
+ }
+
+ UNLOCK_TRUST_LIST();
+
+ //
+ // If the binding handle isn't already cached,
+ // cache it now.
+ //
+
+ if ( !BindingHandleCached ) {
+
+ NlPrint((NL_SESSION_MORE,
+ "NlStartApiClientSession: %wZ: Bind to server " FORMAT_LPWSTR ".\n",
+ &ClientSession->CsDomainName,
+ ClientSession->CsUncServerName ));
+ NlAssert( ClientSession->CsState != CS_IDLE );
+ Status = NlBindingAddServerToCache ( ClientSession->CsUncServerName );
+
+ if ( !NT_SUCCESS(Status) ) {
+ LOCK_TRUST_LIST();
+ ClientSession->CsFlags &= ~CS_BINDING_CACHED;
+ NlGlobalBindingHandleCount --;
+ UNLOCK_TRUST_LIST();
+ }
+ } else {
+ Status = STATUS_SUCCESS;
+ }
+
+ return Status;
+
+}
+
+
+BOOLEAN
+NlFinishApiClientSession(
+ IN PCLIENT_SESSION ClientSession,
+ IN BOOLEAN OkToKillSession
+ )
+/*++
+
+Routine Description:
+
+ Disable the timer for timing out the API call.
+
+ Also, determine if it is time to pick a new DC since the current DC is
+ reponding so poorly. The decision is made from the number of
+ timeouts that happened during the last reauthentication time. If
+ timeoutcount is more than the limit, it sets the connection status
+ to CS_IDLE so that new DC will be picked up and new session will be
+ established.
+
+ On Entry,
+ The trust list must NOT be locked.
+ The caller must be a writer of the trust list entry.
+
+Arguments:
+
+ ClientSession - Structure used to define the session.
+
+ OkToKillSession - TRUE if it's OK to actually drop the secure channel.
+ Otherwise, this routine will simply return FALSE upon timeout and
+ depend on the caller to drop the secure channel.
+
+Return Value:
+
+ TRUE - API finished normally
+ FALSE - API timed out AND the ClientSession structure was torn down.
+ The caller shouldn't use the ClientSession structure without first
+ setting up another session. FALSE will only be return for a "quick"
+ API call.
+
+ FALSE does not imply that the API call failed. It should only be used
+ as an indication that the secure channel was torn down.
+
+--*/
+{
+ BOOLEAN SessionOk = TRUE;
+ TIMER ApiTimer;
+
+ //
+ // Grab a copy of the ApiTimer.
+ //
+ // Only a copy is needed and we don't want to keep the trust list locked
+ // while locking NlGlobalDcDiscoveryCritSect (wrong locking order) nor while
+ // freeing the session.
+ //
+
+ LOCK_TRUST_LIST();
+ ApiTimer = ClientSession->CsApiTimer;
+
+ //
+ // Turn off the timer for this API call.
+ //
+
+ ClientSession->CsApiTimer.Period = (DWORD) MAILSLOT_WAIT_FOREVER;
+
+ UNLOCK_TRUST_LIST();
+
+
+
+ //
+ // If this was a "quick" API call,
+ // and the API took too long,
+ // increment the count of times it timed out.
+ //
+
+ if ( ApiTimer.Period == NlGlobalShortApiCallPeriod ) {
+ if( NlTimeHasElapsed(
+ ApiTimer.StartTime,
+ ( ClientSession->CsSecureChannelType ==
+ WorkstationSecureChannel ?
+ MAX_WKSTA_API_TIMEOUT :
+ MAX_DC_API_TIMEOUT) + NlGlobalExpectedDialupDelayParameter*1000 ) ) {
+
+ //
+ // API timeout.
+ //
+
+ ClientSession->CsTimeoutCount++;
+
+ NlPrint((NL_CRITICAL,
+ "NlFinishApiClientSession: "
+ "timeout call to " FORMAT_LPWSTR ". Count: %lu \n",
+ ClientSession->CsUncServerName,
+ ClientSession->CsTimeoutCount));
+ }
+
+ //
+ // did we hit the limit ?
+ //
+
+ if( ClientSession->CsTimeoutCount >=
+ (DWORD)( ClientSession->CsSecureChannelType ==
+ WorkstationSecureChannel ?
+ MAX_WKSTA_TIMEOUT_COUNT :
+ MAX_DC_TIMEOUT_COUNT ) ) {
+
+ BOOL IsTimeHasElapsed;
+
+ //
+ // block CsLastAuthenticationTry access
+ //
+
+ EnterCriticalSection( &NlGlobalDcDiscoveryCritSect );
+
+ IsTimeHasElapsed =
+ NlTimeHasElapsed(
+ ClientSession->CsLastAuthenticationTry,
+ ( ClientSession->CsSecureChannelType ==
+ WorkstationSecureChannel ?
+ MAX_WKSTA_REAUTHENTICATION_WAIT :
+ MAX_DC_REAUTHENTICATION_WAIT) );
+
+ LeaveCriticalSection( &NlGlobalDcDiscoveryCritSect );
+
+ if( IsTimeHasElapsed ) {
+
+ NlPrint((NL_CRITICAL,
+ "NlFinishApiClientSession: "
+ "dropping the session to " FORMAT_LPWSTR "\n",
+ ClientSession->CsUncServerName ));
+
+ //
+ // timeoutcount limit exceeded and it is time to reauth.
+ //
+
+ SessionOk = FALSE;
+
+ //
+ // Only drop the secure channel if the caller requested it.
+ //
+
+ if ( OkToKillSession ) {
+ NlSetStatusClientSession( ClientSession, STATUS_NO_LOGON_SERVERS );
+
+ //
+ // Start asynchronous DC discovery if this is not a workstation.
+ //
+
+ if ( NlGlobalRole != RoleMemberWorkstation ) {
+ (VOID) NlDiscoverDc( ClientSession, DT_Asynchronous );
+ }
+ }
+
+ }
+ }
+ }
+
+ return SessionOk;
+}
+
+
+
+BOOLEAN
+NlTimeoutOneApiClientSession (
+ PCLIENT_SESSION ClientSession
+ )
+
+/*++
+
+Routine Description:
+
+ Timeout any API calls active specified client session structure
+
+Arguments:
+
+ ClientSession: Pointer to client session to time out
+
+ Enter with global trust list locked.
+
+Return Value:
+
+ TRUE - iff this routine temporarily dropped the global trust list lock.
+
+--*/
+{
+#define SHARE_TO_KILL L"\\IPC$"
+#define SHARE_TO_KILL_LENGTH 5
+
+ NET_API_STATUS NetStatus;
+ WCHAR ShareToKill[UNCLEN+SHARE_TO_KILL_LENGTH+1];
+ WCHAR UncServerName[UNCLEN+1];
+ BOOLEAN TrustListUnlocked = FALSE;
+
+ //
+ // Ignore non-existent sessions.
+ //
+
+ if ( ClientSession == NULL ) {
+ return FALSE;
+ }
+
+ //
+ // If an API call is in progress and has taken too long,
+ // Timeout the API call.
+ //
+
+ if ( NlTimeHasElapsed( ClientSession->CsApiTimer.StartTime,
+ ClientSession->CsApiTimer.Period ) ) {
+
+
+ //
+ // Save the server name but drop all our locks.
+ //
+
+ NlRefClientSession( ClientSession );
+ UNLOCK_TRUST_LIST();
+ (VOID) NlCaptureServerClientSession( ClientSession, ShareToKill );
+ NlUnrefClientSession( ClientSession );
+ TrustListUnlocked = TRUE;
+
+ //
+ // Now that we've unlocked the trust list,
+ // Drop the session to the server we've identified.
+ //
+
+ wcscat( ShareToKill, SHARE_TO_KILL );
+
+ NlPrint(( NL_CRITICAL,
+ "NlTimeoutApiClientSession: Start NetUseDel on "
+ FORMAT_LPWSTR "\n",
+ ShareToKill ));
+
+ IF_DEBUG( INHIBIT_CANCEL ) {
+ NlPrint(( NL_INHIBIT_CANCEL,
+ "NlimeoutApiClientSession: NetUseDel bypassed due to "
+ "INHIBIT_CANCEL Dbflag on " FORMAT_LPWSTR "\n",
+ ShareToKill ));
+ } else {
+ NetStatus = NetUseDel( NULL, ShareToKill, USE_LOTS_OF_FORCE );
+ }
+
+
+ NlPrint(( NL_CRITICAL,
+ "NlTimeoutApiClientSession: Completed NetUseDel on "
+ FORMAT_LPWSTR " (%ld)\n",
+ ShareToKill,
+ NetStatus ));
+
+
+ //
+ // If we have an RPC binding handle cached,
+ // and it has outlived its usefulness,
+ // purge it from the cache.
+ //
+
+ } else if ( (ClientSession->CsFlags & CS_BINDING_CACHED) != 0 &&
+ NlTimeHasElapsed( ClientSession->CsApiTimer.StartTime,
+ BINDING_CACHE_PERIOD ) ) {
+
+
+ //
+ // We must be a writer of the Client Session to unbind the RPC binding
+ // handle.
+ //
+ // Don't wait to become the writer because:
+ // A) We've violated the locking order by trying to become the writer
+ // with the trust list locked.
+ // B) The writer might be doing a long API call like replication and
+ // we're not willing to wait.
+ //
+
+ NlRefClientSession( ClientSession );
+ if ( NlTimeoutSetWriterClientSession( ClientSession, 0 ) ) {
+
+ //
+ // Indicate the handle is no longer cached.
+ //
+
+ ClientSession->CsFlags &= ~CS_BINDING_CACHED;
+ NlGlobalBindingHandleCount --;
+
+ //
+ // Save the server name but drop all our locks.
+ //
+
+ UNLOCK_TRUST_LIST();
+ (VOID) NlCaptureServerClientSession( ClientSession, UncServerName );
+ TrustListUnlocked = TRUE;
+
+
+ //
+ // Unbind this server.
+ //
+
+ NlPrint((NL_SESSION_MORE,
+ "NlTimeoutApiClientSession: %wZ: Unbind from server " FORMAT_LPWSTR ".\n",
+ &ClientSession->CsDomainName,
+ UncServerName ));
+ (VOID) NlBindingRemoveServerFromCache( UncServerName );
+
+ //
+ // Done being writer of the client session.
+ //
+
+ NlResetWriterClientSession( ClientSession );
+ }
+ NlUnrefClientSession( ClientSession );
+ }
+
+ if ( TrustListUnlocked ) {
+ LOCK_TRUST_LIST();
+ }
+ return TrustListUnlocked;
+}
+
+
+VOID
+NlTimeoutApiClientSession (
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ Timeout any API calls active on any of the client session structures
+
+Arguments:
+
+ NONE.
+
+Return Value:
+
+ NONE.
+
+--*/
+{
+ PCLIENT_SESSION ClientSession;
+ PLIST_ENTRY ListEntry;
+
+ //
+ // If there are no API calls outstanding,
+ // just reset the global timer.
+ //
+
+ NlPrint(( NL_SESSION_MORE, "NlTimeoutApiClientSession Called\n"));
+
+ LOCK_TRUST_LIST();
+ if ( NlGlobalBindingHandleCount == 0 ) {
+ NlGlobalApiTimer.Period = (DWORD) MAILSLOT_WAIT_FOREVER;
+
+
+ //
+ // If there are API calls outstanding,
+ // Loop through the trust list making a list of Servers to kill
+ //
+
+ } else {
+
+
+ //
+ // Mark each trust list entry indicating it needs to be handled
+ //
+
+ for ( ListEntry = NlGlobalTrustList.Flink ;
+ ListEntry != &NlGlobalTrustList ;
+ ListEntry = ListEntry->Flink) {
+
+ ClientSession = CONTAINING_RECORD( ListEntry,
+ CLIENT_SESSION,
+ CsNext );
+
+ ClientSession->CsFlags |= CS_HANDLE_API_TIMER;
+ }
+
+
+ //
+ // Loop thru the trust list handling API timeout
+ //
+
+ for ( ListEntry = NlGlobalTrustList.Flink ;
+ ListEntry != &NlGlobalTrustList ;
+ ) {
+
+ ClientSession = CONTAINING_RECORD( ListEntry,
+ CLIENT_SESSION,
+ CsNext );
+
+ //
+ // If we've already done this entry,
+ // skip this entry.
+ //
+
+ if ( (ClientSession->CsFlags & CS_HANDLE_API_TIMER) == 0 ) {
+ ListEntry = ListEntry->Flink;
+ continue;
+ }
+ ClientSession->CsFlags &= ~CS_HANDLE_API_TIMER;
+
+
+ //
+ // Handle timing out the API call and the RPC binding handle.
+ //
+ // If the routine had to drop the TrustList crit sect,
+ // start at the very beginning of the list.
+
+ if ( NlTimeoutOneApiClientSession ( ClientSession ) ) {
+ ListEntry = NlGlobalTrustList.Flink;
+ } else {
+ ListEntry = ListEntry->Flink;
+ }
+
+ }
+
+ //
+ // Do the global client session, too.
+ //
+
+ (VOID) NlTimeoutOneApiClientSession ( NlGlobalClientSession );
+
+ }
+
+ UNLOCK_TRUST_LIST();
+
+
+ return;
+}
+
+
+NTSTATUS
+NetrEnumerateTrustedDomains (
+ IN LPWSTR ServerName OPTIONAL,
+ OUT PDOMAIN_NAME_BUFFER DomainNameBuffer
+ )
+
+/*++
+
+Routine Description:
+
+ This API returns the names of the domains trusted by the domain ServerName is a member of.
+
+ The returned list does not include the domain ServerName is directly a member of.
+
+ Netlogon implements this API by calling LsaEnumerateTrustedDomains on a DC in the
+ domain ServerName is a member of. However, Netlogon returns cached information if
+ it has been less than 5 minutes since the last call was made or if no DC is available.
+ Netlogon's cache of Trusted domain names is maintained in the registry across reboots.
+ As such, the list is available upon boot even if no DC is available.
+
+
+Arguments:
+
+ ServerName - name of remote server (null for local). ServerName must be an NT workstation
+ or NT non-DC server.
+
+ DomainNameBuffer->DomainNames - Returns an allocated buffer containing the list of trusted domains in
+ MULTI-SZ format (i.e., each string is terminated by a zero character, the next string
+ immediately follows, the sequence is terminated by zero length domain name). The
+ buffer should be freed using NetApiBufferFree.
+
+ DomainNameBuffer->DomainNameByteCount - Number of bytes returned in DomainNames
+
+Return Value:
+
+
+ ERROR_SUCCESS - Success.
+
+ STATUS_NOT_SUPPORTED - This machine is not an NT workstation or NT non-DC server.
+
+ STATUS_NO_LOGON_SERVERS - No DC could be found and no cached information is available.
+
+ STATUS_NO_TRUST_LSA_SECRET - The client side of the trust relationship is
+ broken and no cached information is available.
+
+ STATUS_NO_TRUST_SAM_ACCOUNT - The server side of the trust relationship is
+ broken or the password is broken and no cached information is available.
+
+--*/
+{
+ NTSTATUS Status;
+
+ PCLIENT_SESSION ClientSession = NlGlobalClientSession;
+ ULONG DiscoveryDone = FALSE;
+
+ LPWSTR TrustedDomainList = NULL;
+ BOOL TrustedDomainListKnown;
+
+ UNICODE_STRING UncDcNameString;
+ WCHAR UncDcName[UNCLEN+1];
+
+ NlPrint((NL_MISC,
+ "NetrEnumerateTrustedDomains: Called.\n" ));
+
+ if ( NlGlobalRole == RoleMemberWorkstation ) {
+
+
+ //
+ // Don't give up unless we've done discovery.
+ //
+
+ do {
+
+ //
+ // If we don't currently know the name of the server,
+ // discover one.
+ //
+
+ if ( ClientSession->CsState == CS_IDLE ) {
+
+ //
+ // If we've tried to authenticate recently,
+ // don't bother trying again.
+ //
+
+ if ( !NlTimeToReauthenticate( ClientSession ) ) {
+ Status = ClientSession->CsConnectionStatus;
+ goto Cleanup;
+
+ }
+
+ //
+ // Discover a DC
+ //
+
+ if ( !NlTimeoutSetWriterClientSession( ClientSession, WRITER_WAIT_PERIOD ) ) {
+ NlPrint((NL_CRITICAL, "NetrEnumerateTrustedDomains: Can't become writer of client session.\n" ));
+ Status = STATUS_NO_LOGON_SERVERS;
+ goto Cleanup;
+ }
+
+
+ // Check again now that we're the writer
+ if ( ClientSession->CsState == CS_IDLE ) {
+ Status = NlDiscoverDc( ClientSession, DT_Synchronous );
+
+ if ( !NT_SUCCESS(Status) ) {
+ NlResetWriterClientSession( ClientSession );
+
+ NlPrint((NL_CRITICAL,
+ "NetrEnumerateTrustedDomains: Discovery failed %lx\n",
+ Status ));
+ goto Cleanup;
+ }
+
+ NlPrint((NL_MISC,
+ "NetrEnumerateTrustedDomains: Discovery succeeded\n" ));
+ DiscoveryDone = TRUE;
+ }
+
+ NlResetWriterClientSession( ClientSession );
+
+ }
+
+
+
+ //
+ // Capture a copy of the DC the session is to.
+ //
+
+ Status = NlCaptureServerClientSession( ClientSession, UncDcName );
+
+ if ( !NT_SUCCESS(Status) ) {
+ Status = STATUS_NO_LOGON_SERVERS;
+ if ( !NlTimeoutSetWriterClientSession( ClientSession, WRITER_WAIT_PERIOD ) ) {
+ NlPrint((NL_CRITICAL, "NetrEnumerateTrustedDomainsGetAnyDcName: Can't become writer of client session.\n" ));
+ Status = STATUS_NO_LOGON_SERVERS;
+ goto Cleanup;
+ }
+ NlSetStatusClientSession( ClientSession, Status );
+ NlResetWriterClientSession( ClientSession );
+ continue;
+ }
+
+ //
+ // If we don't have DCs in our cache or
+ // if it has been more than 5 minutes since we've refreshed our cache,
+ // get a new list from our primary domain.
+ //
+
+ if ( !NlGlobalTrustedDomainListKnown ||
+ NlTimeHasElapsed( NlGlobalTrustedDomainListTime, 5 * 60 * 1000 ) ) {
+
+ NlPrint((NL_MISC,
+ "NetrEnumerateTrustedDomains: Domain List collected from %ws\n", UncDcName ));
+
+ Status = NlGetTrustedDomainList (
+ UncDcName,
+ &TrustedDomainList );
+
+ if ( !NT_SUCCESS(Status) ) {
+ Status = STATUS_NO_LOGON_SERVERS;
+ if ( !NlTimeoutSetWriterClientSession( ClientSession, WRITER_WAIT_PERIOD ) ) {
+ NlPrint((NL_CRITICAL, "NetrEnumerateTrustedDomainsGetAnyDcName: Can't become writer of client session.\n" ));
+ Status = STATUS_NO_LOGON_SERVERS;
+ goto Cleanup;
+ }
+ NlSetStatusClientSession( ClientSession, Status );
+ NlResetWriterClientSession( ClientSession );
+ continue;
+ }
+
+ continue;
+
+
+ //
+ // Otherwise just use the cached information.
+ //
+ } else {
+ Status = STATUS_NO_LOGON_SERVERS;
+ goto Cleanup;
+ }
+
+ } while ( !NT_SUCCESS(Status) && !DiscoveryDone );
+
+
+
+ //
+ // Free any locally used resources.
+ //
+ Cleanup:
+
+ //
+ // Don't divulge too much to the caller.
+ //
+
+ if ( Status == STATUS_ACCESS_DENIED ) {
+ Status = STATUS_NO_TRUST_SAM_ACCOUNT;
+ }
+
+ //
+ // If we simply can't access a DC,
+ // return the cached information.
+ //
+
+ if ( !NT_SUCCESS(Status) ) {
+ NET_API_STATUS NetStatus;
+ NlPrint((NL_MISC,
+ "NetrEnumerateTrustedDomains: Domain List returned from cache.\n" ));
+
+ NetStatus = NlReadRegTrustedDomainList (
+ NULL,
+ FALSE, // Don't delete registry key
+ &TrustedDomainList,
+ &TrustedDomainListKnown );
+
+ // Leave 'Status' alone if we can't read from the cache.
+ if (NetStatus == NO_ERROR ) {
+ if ( TrustedDomainListKnown ) {
+ Status = STATUS_SUCCESS;
+ }
+ } else {
+ NlPrint((NL_CRITICAL,
+ "NetrEnumerateTrustedDomains: Can't get Domain List from cache: 0x%lX\n",
+ NetStatus ));
+ }
+ }
+
+ #ifdef notdef // We're using NlGlobalClientSession
+ if ( ClientSession != NULL ) {
+ NlUnrefClientSession( ClientSession );
+ }
+ #endif // notdef // We're using NlGlobalClientSession
+
+ //
+ // Return the DCName to the caller.
+ //
+
+ if ( NT_SUCCESS(Status) ) {
+ DomainNameBuffer->DomainNameByteCount = NetpTStrArraySize( TrustedDomainList );
+ DomainNameBuffer->DomainNames = (LPBYTE) TrustedDomainList;
+ } else {
+ if ( TrustedDomainList != NULL ) {
+ NetApiBufferFree( TrustedDomainList );
+ }
+ DomainNameBuffer->DomainNameByteCount = 0;
+ DomainNameBuffer->DomainNames = NULL;
+ }
+
+ NlPrint((NL_MISC,
+ "NetrEnumerateTrustedDomains: returns: 0x%lX\n",
+ Status ));
+ return Status;
+ } else {
+
+ //
+ // NlGlobalRole != RoleMemberWorksation
+ //
+
+#ifndef notdef
+ Status = NlGetTrustedDomainList (
+ NULL, // we want to query our own trusted domain list
+ &TrustedDomainList );
+
+ //
+ // Return the DCName to the caller.
+ //
+
+ if ( NT_SUCCESS(Status) ) {
+ DomainNameBuffer->DomainNameByteCount = NetpTStrArraySize( TrustedDomainList );
+ DomainNameBuffer->DomainNames = (LPBYTE) TrustedDomainList;
+ } else {
+ if ( TrustedDomainList != NULL ) {
+ NetApiBufferFree( TrustedDomainList );
+ }
+ DomainNameBuffer->DomainNameByteCount = 0;
+ DomainNameBuffer->DomainNames = NULL;
+ }
+
+ NlPrint((NL_MISC,
+ "NetrEnumerateTrustedDomains: returns: 0x%lX\n",
+ Status ));
+ return Status;
+#else
+
+ //
+ // BUGBUG: this code does not work because the list of
+ // client sessions does not accurately reflect the list of
+ // trusted domains. See bug 34234
+ //
+
+ PLIST_ENTRY ListEntry;
+ ULONG BufferLength;
+ LPWSTR CurrentLoc;
+
+ //
+ // Loop through the client sessions add first calculate the
+ // size required
+ //
+
+ BufferLength = sizeof(WCHAR);
+ LOCK_TRUST_LIST();
+
+ for ( ListEntry = NlGlobalTrustList.Flink ;
+ ListEntry != &NlGlobalTrustList ;
+ ListEntry = ListEntry->Flink) {
+
+ ClientSession =
+ CONTAINING_RECORD( ListEntry, CLIENT_SESSION, CsNext );
+
+ BufferLength += ClientSession->CsDomainName.Length + sizeof(WCHAR);
+
+ }
+
+ TrustedDomainList = (LPWSTR) NetpMemoryAllocate( BufferLength );
+
+ if (TrustedDomainList == NULL) {
+ Status = STATUS_NO_MEMORY;
+
+ } else {
+
+ Status = STATUS_SUCCESS;
+ *TrustedDomainList = L'\0';
+ CurrentLoc = TrustedDomainList;
+
+ //
+ // Now add all the trusted domains onto the string we
+ // allocated
+ //
+
+ for ( ListEntry = NlGlobalTrustList.Flink ;
+ ListEntry != &NlGlobalTrustList ;
+ ListEntry = ListEntry->Flink) {
+
+ ClientSession =
+ CONTAINING_RECORD( ListEntry, CLIENT_SESSION, CsNext );
+
+ RtlCopyMemory(
+ CurrentLoc,
+ ClientSession->CsDomainName.Buffer,
+ ClientSession->CsDomainName.Length
+ );
+ CurrentLoc += ClientSession->CsDomainName.Length / sizeof(WCHAR);
+
+ *(CurrentLoc++) = L'\0';
+ *CurrentLoc = L'\0'; // Place double terminator each time
+
+ }
+ }
+
+ UNLOCK_TRUST_LIST();
+
+ //
+ // Return the list of domains to the caller.
+ //
+
+
+ if ( NT_SUCCESS(Status) ) {
+ DomainNameBuffer->DomainNameByteCount = NetpTStrArraySize( TrustedDomainList );
+ DomainNameBuffer->DomainNames = (LPBYTE) TrustedDomainList;
+ } else {
+ if ( TrustedDomainList != NULL ) {
+ NetApiBufferFree( TrustedDomainList );
+ }
+ DomainNameBuffer->DomainNameByteCount = 0;
+ DomainNameBuffer->DomainNames = NULL;
+ }
+ return Status;
+#endif
+ }
+
+ UNREFERENCED_PARAMETER( ServerName );
+}
diff --git a/private/net/svcdlls/msgsvc/client/makefile b/private/net/svcdlls/msgsvc/client/makefile
new file mode 100644
index 000000000..f0db8e4a7
--- /dev/null
+++ b/private/net/svcdlls/msgsvc/client/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS LINE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/net/svcdlls/msgsvc/client/msg.c b/private/net/svcdlls/msgsvc/client/msg.c
new file mode 100644
index 000000000..ea13c73db
--- /dev/null
+++ b/private/net/svcdlls/msgsvc/client/msg.c
@@ -0,0 +1,475 @@
+/*++
+
+Copyright (c) 1990-1992 Microsoft Corporation
+
+Module Name:
+
+ MSG.C
+
+Abstract:
+
+ Test Routines for the Service Controller.
+
+Author:
+
+ Dan Lafferty (danl) 08-May-1991
+
+Environment:
+
+ User Mode - Win32
+
+Revision History:
+
+ 08-May-1991 danl
+ created
+
+--*/
+//
+// INCLUDES
+//
+#include <nt.h> // DbgPrint prototype
+#include <ntrtl.h> // DbgPrint prototype
+#include <nturtl.h> // needed for winbase.h
+
+#include <stdlib.h> // atoi
+#include <stdio.h> // printf
+#include <string.h> // strcmp
+#include <windows.h> // win32 typedefs
+#include <lmerr.h> // NERR_ error codes
+#include <windef.h> // win32 typedefs
+#include <lmcons.h>
+#include <lmmsg.h>
+#include <tstring.h> // Unicode
+
+//
+// FUNCTION PROTOTYPES
+//
+VOID
+DisplayStatus (
+ IN LPBYTE InfoStruct2,
+ IN DWORD level
+ );
+
+BOOL
+ConvertToUnicode(
+ OUT LPWSTR *UnicodeOut,
+ IN LPSTR AnsiIn
+ );
+
+BOOL
+MakeArgsUnicode (
+ DWORD argc,
+ PUCHAR argv[]
+ );
+
+
+/****************************************************************************/
+VOID _CRTAPI1
+main (
+ int argc,
+ PUCHAR argvA[]
+ )
+
+/*++
+
+Routine Description:
+
+ Allows manual testing of the Messenger Service by typing commands on
+ the command line such as:
+
+
+ msg add danl - adds a name (danl).
+ msg del danl - deletes a name (danl).
+ msg enum - enumerates the names (level 1)
+ msg enum -l1 - enumerates the names (level 1)
+ msg enum -l0 - enumerates the names (level 0)
+ msg enum rh= 2 - enum with resume handle =2 (level1)
+ msg enum maxlen= 50 - Does an enum with a 50 byte buffer.
+ msg query danl - gets info on danl. (level 1)
+ msg query danl -l0 - gets info on danl. (level 0)
+
+
+Arguments:
+
+
+
+Return Value:
+
+
+
+--*/
+
+{
+ NET_API_STATUS status;
+ LPBYTE InfoStruct;
+ DWORD entriesRead;
+ DWORD totalEntries;
+ DWORD resumeHandle;
+ DWORD prefMaxLen;
+ DWORD level;
+ DWORD specialFlag = FALSE;
+ DWORD i;
+ WCHAR name[50];
+ LPWSTR *argv;
+ LPWSTR pServerName;
+ LPTSTR *FixArgv;
+ INT argIndex;
+
+
+ if (argc <2) {
+ printf("ERROR: no command was given! (add, del, enum, query)\n");
+ return;
+ }
+
+ //
+ // Make the arguments unicode if necessary.
+ //
+ argv = (LPWSTR *)argvA;
+
+#ifdef UNICODE
+
+ if (!MakeArgsUnicode(argc, (PUCHAR *)argv)) {
+ return;
+ }
+
+#endif
+ FixArgv = (LPTSTR *)argv;
+
+ pServerName = NULL;
+ argIndex = 1;
+
+ if (STRNCMP (FixArgv[1], TEXT("\\\\"), 2) == 0) {
+ pServerName = FixArgv[1];
+ argIndex = 2;
+ }
+
+ if (_wcsicmp (FixArgv[argIndex], L"enum" ) == 0 ) {
+
+
+ resumeHandle = 0;
+ level = 1;
+ prefMaxLen = 0xffffffff;
+
+ if (argc > argIndex+1) {
+ if (_wcsicmp (FixArgv[argIndex+1], L"rh=") == 0) {
+ specialFlag = TRUE;
+ if (argc > argIndex+2) {
+ resumeHandle = wtol(FixArgv[argIndex+2]);
+ }
+ }
+
+ if (_wcsicmp (FixArgv[argIndex+1], L"-l0") == 0) {
+ specialFlag = TRUE;
+ level = 0;
+ }
+ if (_wcsicmp (FixArgv[argIndex+1], L"-l2") == 0) {
+ specialFlag = TRUE;
+ level = 2;
+ }
+ if (_wcsicmp (FixArgv[argIndex+1], L"-l3") == 0) {
+ specialFlag = TRUE;
+ level = 3;
+ }
+ if (_wcsicmp (FixArgv[argIndex+1], L"maxlen=") == 0) {
+ specialFlag = TRUE;
+ if (argc > argIndex+2) {
+ prefMaxLen = wtol(FixArgv[argIndex+2]);
+ }
+ }
+ }
+ if ( (argc < argIndex+2) ||
+ (specialFlag == TRUE) && (argc < argIndex+4) ) {
+
+ status = NetMessageNameEnum (
+ pServerName, // ServerName - Local version
+ level, // Level
+ &InfoStruct, // return status buffer pointer
+ prefMaxLen, // preferred max length
+ &entriesRead, // entries read
+ &totalEntries, // total entries
+ &resumeHandle); // resume handle
+
+ if ( (status == NERR_Success) ||
+ (status == ERROR_MORE_DATA) ){
+
+ printf("Enum: entriesRead = %d\n", entriesRead);
+ printf("Enum: totalEntries = %d\n", totalEntries);
+ printf("Enum: resumeHandle = %d\n", resumeHandle);
+
+ for (i=0; i<entriesRead; i++) {
+ DisplayStatus(InfoStruct,level);
+ switch(level) {
+ case 0:
+ InfoStruct += sizeof(MSG_INFO_0);
+ break;
+ case 1:
+ InfoStruct += sizeof(MSG_INFO_1);
+ break;
+ default:
+ printf("Bad InfoLevel\n");
+ }
+
+ }
+ }
+ else {
+ printf("[msg] NetServiceEnum FAILED, rc = %ld\n", status);
+ if (InfoStruct != NULL) {
+ DisplayStatus(InfoStruct,level);
+ }
+ }
+ }
+ }
+
+ else if (_wcsicmp (FixArgv[argIndex], L"query") == 0) {
+
+ level = 1;
+
+ if (argc > argIndex+1 ) {
+ wcscpy(name, FixArgv[argIndex+1]);
+ }
+ if (argc > argIndex+2) {
+ if (_wcsicmp (FixArgv[argIndex+2], TEXT("-l0")) ==0) {
+ level = 0;
+ }
+ }
+
+ status = NetMessageNameGetInfo (
+ pServerName, // ServerName Local version
+ name, // MessageName
+ level, // level
+ &InfoStruct); // buffer pointer
+
+ if (status == NERR_Success) {
+ DisplayStatus((LPBYTE)InfoStruct, level);
+ }
+ else {
+ printf("[msg] NetMessageNameGetInfo FAILED, rc = %ld\n", status);
+ }
+ }
+
+ else if (_wcsicmp (FixArgv[argIndex], L"add") == 0) {
+
+ if (argc < argIndex+2) {
+ printf("ERROR: no name given for add (msg add <name>)\n");
+ return;
+ }
+
+ wcscpy(name, FixArgv[argIndex+1]);
+
+ status = NetMessageNameAdd (
+ pServerName, // ServerName Local version
+ name); // Name to add
+
+ if (status == NERR_Success) {
+ printf("name added successfully\n");
+ }
+ else {
+ printf("[msg] NetMessageNameAdd FAILED, rc = %ld\n", status);
+ }
+ }
+
+ else if (_wcsicmp (FixArgv[argIndex], L"del") == 0) {
+
+ if (argc < argIndex+2) {
+ printf("ERROR: no name given for add (msg add <name>)\n");
+ return;
+ }
+
+ wcscpy(name, FixArgv[argIndex+1]);
+
+ status = NetMessageNameDel (
+ pServerName, // ServerName Local version
+ name); // Name to add
+
+ if (status == NERR_Success) {
+ printf("name deleted successfully\n");
+ }
+ else {
+ printf("[msg] NetMessageNameDel FAILED, rc = %ld\n", status);
+ }
+ }
+
+ else {
+ printf("[msg] Unrecognized Command\n");
+ }
+
+ return;
+}
+
+
+/****************************************************************************/
+VOID
+DisplayStatus (
+ IN LPBYTE InfoStruct,
+ IN DWORD level
+ )
+
+/*++
+
+Routine Description:
+
+ Displays the returned info buffer.
+
+Arguments:
+
+ InfoStruct2 - This is a pointer to a SERVICE_INFO_2 structure from which
+ information is to be displayed.
+
+Return Value:
+
+ none.
+
+--*/
+{
+ LPMSG_INFO_0 InfoStruct0;
+ LPMSG_INFO_1 InfoStruct1;
+
+ switch(level) {
+ case 0:
+ InfoStruct0 = (LPMSG_INFO_0)InfoStruct;
+ printf("\nNAME: %ws\n", InfoStruct0->msgi0_name);
+ break;
+ case 1:
+ InfoStruct1 = (LPMSG_INFO_1)InfoStruct;
+
+ printf("\nNAME: %ws\n", InfoStruct1->msgi1_name);
+ printf(" FWD_FLAG: %ld\n", InfoStruct1->msgi1_forward_flag);
+ printf(" FWD_STRING: %ws\n", InfoStruct1->msgi1_forward );
+ break;
+ default:
+ printf("DisplayStatus: illegal level\n");
+ }
+ return;
+}
+
+BOOL
+MakeArgsUnicode (
+ DWORD argc,
+ PUCHAR argv[]
+ )
+
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+Note:
+
+
+--*/
+{
+ DWORD i;
+
+ //
+ // ScConvertToUnicode allocates storage for each string.
+ // We will rely on process termination to free the memory.
+ //
+ for(i=0; i<argc; i++) {
+
+ if(!ConvertToUnicode( (LPWSTR *)&(argv[i]), argv[i])) {
+ printf("Couldn't convert argv[%d] to unicode\n",i);
+ return(FALSE);
+ }
+
+
+ }
+ return(TRUE);
+}
+
+BOOL
+ConvertToUnicode(
+ OUT LPWSTR *UnicodeOut,
+ IN LPSTR AnsiIn
+ )
+
+/*++
+
+Routine Description:
+
+ This function translates an AnsiString into a Unicode string.
+ A new string buffer is created by this function. If the call to
+ this function is successful, the caller must take responsibility for
+ the unicode string buffer that was allocated by this function.
+ The allocated buffer should be free'd with a call to LocalFree.
+
+ NOTE: This function allocates memory for the Unicode String.
+
+ BUGBUG: This should be changed to return either
+ ERROR_NOT_ENOUGH_MEMORY or ERROR_INVALID_PARAMETER
+
+Arguments:
+
+ AnsiIn - This is a pointer to an ansi string that is to be converted.
+
+ UnicodeOut - This is a pointer to a location where the pointer to the
+ unicode string is to be placed.
+
+Return Value:
+
+ TRUE - The conversion was successful.
+
+ FALSE - The conversion was unsuccessful. In this case a buffer for
+ the unicode string was not allocated.
+
+--*/
+{
+
+ NTSTATUS ntStatus;
+ DWORD bufSize;
+ UNICODE_STRING unicodeString;
+ OEM_STRING ansiString;
+
+ //
+ // Allocate a buffer for the unicode string.
+ //
+
+ bufSize = (strlen(AnsiIn)+1) * sizeof(WCHAR);
+
+ *UnicodeOut = (LPWSTR)LocalAlloc(LMEM_ZEROINIT, bufSize);
+
+ if (*UnicodeOut == NULL) {
+ printf("ScConvertToUnicode:LocalAlloc Failure %ld\n",GetLastError());
+ return(FALSE);
+ }
+
+ //
+ // Initialize the string structures
+ //
+ NetpInitOemString( &ansiString, AnsiIn);
+
+ unicodeString.Buffer = *UnicodeOut;
+ unicodeString.MaximumLength = (USHORT)bufSize;
+ unicodeString.Length = 0;
+
+ //
+ // Call the conversion function.
+ //
+ ntStatus = RtlOemStringToUnicodeString (
+ &unicodeString, // Destination
+ &ansiString, // Source
+ FALSE); // Allocate the destination
+
+ if (!NT_SUCCESS(ntStatus)) {
+
+ printf("ScConvertToUnicode:RtlOemStringToUnicodeString Failure %lx\n",
+ ntStatus);
+
+ return(FALSE);
+ }
+
+ //
+ // Fill in the pointer location with the unicode string buffer pointer.
+ //
+ *UnicodeOut = unicodeString.Buffer;
+
+ return(TRUE);
+
+}
+
diff --git a/private/net/svcdlls/msgsvc/client/msgbind.c b/private/net/svcdlls/msgsvc/client/msgbind.c
new file mode 100644
index 000000000..0d9c5d1bf
--- /dev/null
+++ b/private/net/svcdlls/msgsvc/client/msgbind.c
@@ -0,0 +1,117 @@
+/*++
+
+Copyright (c) 1990 Microsoft Corporation
+
+Module Name:
+
+ msgbind.c
+
+Abstract:
+
+ Contains the RPC bind and un-bind routines for the Service Controller.
+
+Author:
+
+ Dan Lafferty (danl) 29-May-1991
+
+Environment:
+
+ User Mode -Win32
+
+Revision History:
+
+
+--*/
+
+//
+// INCLUDES
+//
+#include <nt.h> // DbgPrint prototype
+#include <rpc.h> // DataTypes and runtime APIs
+#include <msgsvc.h> // generated by the MIDL complier
+#include <rpcutil.h> // NetRpc utils
+#include <netlib.h> // UNUSED macro
+
+
+
+/****************************************************************************/
+handle_t
+MSGSVC_HANDLE_bind (
+ MSGSVC_HANDLE ServerName)
+
+/*++
+
+Routine Description:
+ This routine calls a common bind routine that is shared by all services.
+ This routine is called from the messenger service client stubs when
+ it is necessary to bind to a server.
+
+Arguments:
+
+ ServerName - A pointer to a string containing the name of the server
+ to bind with.
+
+Return Value:
+
+ The binding handle is returned to the stub routine. If the
+ binding is unsuccessful, a NULL will be returned.
+
+--*/
+{
+ handle_t bindingHandle;
+ RPC_STATUS status;
+
+ status = NetpBindRpc (
+ ServerName,
+ L"msgsvc",
+ L"Security=Impersonation Dynamic False",
+ &bindingHandle);
+
+#ifdef DEBUG
+ DbgPrint("MSGSVC_HANDLE_bind:NetpBindRpc status=%d\n",status);
+ DbgPrint("MSGSVC_HANDLE_bind: handle=%d\n",bindingHandle);
+#endif
+
+ return( bindingHandle);
+}
+
+
+
+/****************************************************************************/
+void
+MSGSVC_HANDLE_unbind (
+ MSGSVC_HANDLE ServerName,
+ handle_t BindingHandle)
+
+/*++
+
+Routine Description:
+
+ This routine calls a common unbind routine that is shared by
+ all services.
+ This routine is called from the Messenger Service client stubs when
+ it is necessary to unbind to a server.
+
+
+Arguments:
+
+ ServerName - This is the name of the server from which to unbind.
+
+ BindingHandle - This is the binding handle that is to be closed.
+
+Return Value:
+
+ none.
+
+--*/
+{
+ UNUSED(ServerName); // This parameter is not used
+
+#ifdef DEBUG
+ DbgPrint("MSGSVC_HANDLE_unbind: handle=%d\n",BindingHandle);
+#endif
+
+ NetpUnbindRpc ( BindingHandle);
+ return;
+}
+
diff --git a/private/net/svcdlls/msgsvc/client/msgstub.c b/private/net/svcdlls/msgsvc/client/msgstub.c
new file mode 100644
index 000000000..a8006715f
--- /dev/null
+++ b/private/net/svcdlls/msgsvc/client/msgstub.c
@@ -0,0 +1,333 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ msgstub.c
+
+Abstract:
+
+ These are the Messenger Service API RPC client stubs.
+
+Author:
+
+ Dan Lafferty (danl) 06-Feb-1991
+
+Environment:
+
+ User Mode - Win32
+
+Revision History:
+
+ 27-Aug-1992 Danl
+ Added downlevel support & removed error mapping for RPC errors.
+
+ 06-Feb-1991 Danl
+ Created
+
+--*/
+
+//
+// INCLUDES
+//
+
+#include <nt.h> // DbgPrint prototype
+#include <ntrtl.h> // DbgPrint prototype
+#include <rpc.h> // DataTypes and runtime APIs
+
+#include <msgsvc.h> // generated by the MIDL complier
+#include <rpcutil.h> // NetRpc utils
+
+#include <lmsvc.h>
+#include <lmcons.h> // NET_API_STATUS
+#include <lmerr.h> // NetError codes
+#include <rxmsg.h> // RxNetMessage API
+#include <netlib.h> // NetpServiceIsStarted() (needed by netrpc.h).
+#include <netdebug.h> // needed for netrpc.h
+#include <netrpc.h> // NET_REMOTE macros.
+
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetMessageNameAdd (
+ IN LPCWSTR servername,
+ IN LPCWSTR msgname
+ )
+
+/*++
+
+Routine Description:
+
+ This is the DLL entrypoint for NetMessageNameAdd. This API adds a name
+ to the message name table.
+
+Arguments:
+
+ servername - Pointer to a string containing the name of the computer
+ that is to execute the API function.
+
+ msgname - Pointer to a string containing the name to be added.
+
+
+Return Value:
+
+ NERR_Success - The operation was successful.
+
+--*/
+{
+ NET_API_STATUS apiStatus;
+ DWORD OptionsSupported = 0;
+
+
+ NET_REMOTE_TRY_RPC
+
+ apiStatus = NetrMessageNameAdd ((LPWSTR)servername,(LPWSTR)msgname);
+
+ NET_REMOTE_RPC_FAILED("NetMessageNameAdd",
+ (LPWSTR)servername,
+ apiStatus,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_MESSENGER)
+
+ //
+ // Call downlevel version of the API.
+ //
+ apiStatus = RxNetMessageNameAdd((LPWSTR)servername,(LPWSTR)msgname);
+
+ NET_REMOTE_END
+
+ return(apiStatus);
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetMessageNameEnum (
+ IN LPCWSTR servername,
+ IN DWORD level,
+ OUT LPBYTE *bufptr,
+ IN DWORD prefmaxlen,
+ OUT LPDWORD entriesread,
+ OUT LPDWORD totalentries,
+ IN OUT LPDWORD resume_handle
+ )
+/*++
+
+Routine Description:
+
+ This is the DLL entrypoint for NetMessageNameEnum.
+
+Arguments:
+
+ servername - Pointer to a string containing the name of the computer
+ that is to execute the API function.
+
+ level - This indicates the level of information that is desired.
+
+ bufptr - A pointer to the location where the pointer to the returned
+ array of info structures is to be placed.
+
+ prefmaxlen - Indicates a maximum size limit that the caller will allow
+ for the return buffer.
+
+ entriesread - A pointer to the location where the number of entries
+ (data structures)read is to be returned.
+
+ totalentries - A pointer to the location which upon return indicates
+ the total number of entries in the table.
+
+ resumehandle - Pointer to a value that indicates where to resume
+ enumerating data.
+
+Return Value:
+
+ Nerr_Success - The operation was successful.
+
+
+Note:
+
+
+--*/
+{
+ NET_API_STATUS apiStatus;
+ GENERIC_ENUM_STRUCT infoStruct;
+ GENERIC_INFO_CONTAINER genericInfoContainer;
+ DWORD OptionsSupported = 0;
+
+
+ genericInfoContainer.Buffer = NULL;
+ genericInfoContainer.EntriesRead = 0;
+
+ infoStruct.Container = &genericInfoContainer;
+ infoStruct.Level = level;
+
+
+ NET_REMOTE_TRY_RPC
+
+ apiStatus = NetrMessageNameEnum (
+ (LPWSTR)servername,
+ (LPMSG_ENUM_STRUCT) &infoStruct,
+ prefmaxlen,
+ totalentries,
+ resume_handle);
+
+ if (apiStatus == NERR_Success || apiStatus == ERROR_MORE_DATA) {
+ *bufptr = (LPBYTE) genericInfoContainer.Buffer;
+ *entriesread = genericInfoContainer.EntriesRead;
+ }
+
+ NET_REMOTE_RPC_FAILED("NetMessageNameEnum",
+ (LPWSTR)servername,
+ apiStatus,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_MESSENGER )
+
+ //
+ // Call downlevel version of the API.
+ //
+ apiStatus = RxNetMessageNameEnum(
+ (LPWSTR)servername,
+ level,
+ bufptr,
+ prefmaxlen,
+ entriesread,
+ totalentries,
+ resume_handle);
+
+ NET_REMOTE_END
+
+
+ return(apiStatus);
+}
+
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetMessageNameGetInfo (
+ IN LPCWSTR servername,
+ IN LPCWSTR msgname,
+ IN DWORD level,
+ OUT LPBYTE *bufptr
+ )
+
+/*++
+
+Routine Description:
+
+ This is the DLL entrypoint for NetMessageNameGetInfo.
+
+Arguments:
+
+ servername - Pointer to a string containing the name of the computer
+ that is to execute the API function. Since this function is
+ executing on that computer, this information is not useful
+ by the time it gets here. It is really only useful on the RPC
+ client side.
+
+ msgname - Pointer to a string containing the name in the table
+ for which information is desired.
+
+ level - This indicates the level of information that is desired.
+
+ bufptr - Pointer to a Location where the pointer to the returned
+ information structure is to be placed.
+
+Return Value:
+
+ NERR_Success - The operation was successful.
+
+
+--*/
+
+{
+ NET_API_STATUS apiStatus;
+ DWORD OptionsSupported = 0;
+
+
+ *bufptr = NULL; // Must be NULL so RPC knows to till it in.
+
+ NET_REMOTE_TRY_RPC
+
+ apiStatus = NetrMessageNameGetInfo (
+ (LPWSTR)servername,
+ (LPWSTR)msgname,
+ level,
+ (LPMSG_INFO) bufptr);
+
+ NET_REMOTE_RPC_FAILED("NetMessageNameGetInfo",
+ (LPWSTR)servername,
+ apiStatus,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_MESSENGER)
+
+ //
+ // Call downlevel version of the API.
+ //
+ apiStatus = RxNetMessageNameGetInfo(
+ (LPWSTR)servername,
+ (LPWSTR)msgname,
+ level,
+ bufptr);
+ NET_REMOTE_END
+
+
+ return(apiStatus);
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetMessageNameDel (
+ IN LPCWSTR servername,
+ IN LPCWSTR msgname
+ )
+/*++
+
+Routine Description:
+
+ This is the DLL entrypoint for NetMessageNameDel. This API deletes
+ a name from the message name table.
+
+Arguments:
+
+ servername - Points to a string containing the name of the computer
+ that is to execute the API function.
+
+ msgname - Points to a string containing the name that is to be deleted
+ from the message name table.
+
+
+Return Value:
+
+ NERR_Success - The operation was successful
+
+
+
+--*/
+
+{
+ NET_API_STATUS apiStatus;
+ DWORD OptionsSupported = 0;
+
+
+
+ NET_REMOTE_TRY_RPC
+
+ apiStatus = NetrMessageNameDel ((LPWSTR)servername, (LPWSTR)msgname);
+
+ NET_REMOTE_RPC_FAILED("NetMessageNameDel",
+ (LPWSTR)servername,
+ apiStatus,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_MESSENGER)
+
+ //
+ // Call downlevel version of the API.
+ //
+ apiStatus = RxNetMessageNameDel((LPWSTR)servername,(LPWSTR)msgname);
+
+ NET_REMOTE_END
+
+ return(apiStatus);
+}
+
+
diff --git a/private/net/svcdlls/msgsvc/client/sources b/private/net/svcdlls/msgsvc/client/sources
new file mode 100644
index 000000000..8ef2b778c
--- /dev/null
+++ b/private/net/svcdlls/msgsvc/client/sources
@@ -0,0 +1,60 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+
+Author:
+
+ Dan Lafferty (danl) 5-May-1991
+
+
+Revision History:
+
+!ENDIF
+
+MAJORCOMP = net
+MINORCOMP = services
+TARGETNAME= msgsvc
+
+
+NTPROFILEINPUT=YES
+
+TARGETPATH=obj
+
+TARGETTYPE=LIBRARY
+
+TARGETLIBS=
+
+
+INCLUDES=.;..;..\..\..\inc;..\..\..\..\inc
+
+!IFNDEF DISABLE_NET_UNICODE
+UNICODE=1
+NET_C_DEFINES=-DUNICODE
+!ENDIF
+
+SOURCES=msgstub.c \
+ msgbind.c \
+ msgsvc_c.c
+
+
+UMTYPE= console
+UMTEST= msg
+UMLIBS= \
+ $(BASEDIR)\public\sdk\lib\*\rpcrt4.lib \
+ $(BASEDIR)\public\sdk\lib\*\netapi32.lib \
+ $(BASEDIR)\Public\Sdk\Lib\*\netlib.lib
+
+C_DEFINES=-DRPC_NO_WINDOWS_H
+
diff --git a/private/net/svcdlls/msgsvc/dirs b/private/net/svcdlls/msgsvc/dirs
new file mode 100644
index 000000000..cefe61416
--- /dev/null
+++ b/private/net/svcdlls/msgsvc/dirs
@@ -0,0 +1,28 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ dirs.
+
+Abstract:
+
+ This file specifies the subdirectories of the current directory that
+ contain component makefiles.
+
+
+Author:
+
+ Steve Wood (stevewo) 17-Apr-1990
+
+NOTE: Commented description of this file is in \nt\bak\bin\dirs.tpl
+
+!ENDIF
+
+
+DIRS=server \
+ client
+
+
+#OPTIONAL_DIRS=
diff --git a/private/net/svcdlls/msgsvc/docs/msgdata.ppt b/private/net/svcdlls/msgsvc/docs/msgdata.ppt
new file mode 100644
index 000000000..59e4340fc
--- /dev/null
+++ b/private/net/svcdlls/msgsvc/docs/msgdata.ppt
Binary files differ
diff --git a/private/net/svcdlls/msgsvc/imports.h b/private/net/svcdlls/msgsvc/imports.h
new file mode 100644
index 000000000..d5b1d5398
--- /dev/null
+++ b/private/net/svcdlls/msgsvc/imports.h
@@ -0,0 +1,10 @@
+#include <windef.h>
+#include <lmcons.h>
+
+#ifdef MIDL_PASS
+#ifdef UNICODE
+#define LPWSTR [string] wchar_t*
+#endif
+#endif
+
+#include <lmmsg.h>
diff --git a/private/net/svcdlls/msgsvc/imports.idl b/private/net/svcdlls/msgsvc/imports.idl
new file mode 100644
index 000000000..f8ce9137f
--- /dev/null
+++ b/private/net/svcdlls/msgsvc/imports.idl
@@ -0,0 +1,67 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ imports.idl
+
+Abstract:
+
+ This file is useful for creating RPC interfaces that require the use
+ of windef types. The .idl file for the RPC product should contain a
+ line in the interface body that imports this file. For example:
+
+ import "imports.h";
+
+ Doing this causes the MIDL generated header file to contain the
+ following line:
+
+ #include "imports.h"
+
+ If this technique is not used, and instead the .idl file for the RPC
+ product simply contains #include <importsf.h>, then the contents of
+ imports.h will be expanded in the MIDL generated header file. This
+ can lead to duplicate definition problems later when the RPC client
+ or RPC server code needs to include both the MIDL generated header file
+ and a file that is included in imports.h.
+
+Author:
+
+ Dan Lafferty (danl) 20-Mar-1991
+
+Environment:
+
+ User Mode - Win32 - for use with the MIDL compiler
+
+
+Revision History:
+
+ 03-Apr-1991 danl
+ created
+
+--*/
+
+[
+ uuid(12345678-1234-ABCD-EF00-9948756789AB),
+#ifdef __midl
+ ms_union,
+#endif // __midl
+ version(1.0)
+]
+interface imports
+
+{
+#define MIDL_PASS
+#include "imports.h"
+
+//
+// All .idl files need to contain at least one function prototype
+//
+
+DWORD
+Dummy(
+ [in] DWORD DummyParm);
+
+
+}
diff --git a/private/net/svcdlls/msgsvc/makefil0 b/private/net/svcdlls/msgsvc/makefil0
new file mode 100644
index 000000000..da0e40485
--- /dev/null
+++ b/private/net/svcdlls/msgsvc/makefil0
@@ -0,0 +1,58 @@
+#
+# This is the MIDL compile phase of the build process.
+#
+# The following is where you put the name of your .idl file without
+# the .idl extension:
+#
+
+!INCLUDE $(NTMAKEENV)\makefile.plt
+
+IDL_NAME = msgsvc
+
+!IFNDEF DISABLE_NET_UNICODE
+UNICODE=1
+NET_C_DEFINES=-DUNICODE
+!ENDIF
+
+SDKINC = $(BASEDIR)\public\sdk\inc
+SDKCRTINC = $(BASEDIR)\public\sdk\inc\crt
+NETINC = $(BASEDIR)\private\net\inc
+INCS = -I$(SDKINC) -I$(SDKCRTINC) -I$(NETINC)
+
+CPP = -cpp_cmd "$(MIDL_CPP)" $(MIDL_FLAGS) $(C_DEFINES) $(NET_C_DEFINES)
+
+CLIENT_TARGETS = client\$(IDL_NAME)_c.c \
+ .\$(IDL_NAME).h
+
+SERVER_TARGETS = server\$(IDL_NAME)_s.c
+
+EXTRN_DEPENDS = $(SDKINC)\lmcons.h \
+ $(SDKINC)\windef.h \
+ $(SDKINC)\lmmsg.h \
+ $(IDL_NAME).acf
+
+#
+# Define Products and Dependencies
+#
+
+all: $(CLIENT_TARGETS) $(SERVER_TARGETS) $(EXTRN_DEPENDS)
+!IF "$(BUILDMSG)" != ""
+ @ech ; $(BUILDMSG) ;
+!ENDIF
+
+clean: delete_source all
+
+delete_source:
+ erase $(CLIENT_TARGETS) $(SERVER_TARGETS)
+
+#
+# MIDL COMPILE
+#
+
+$(CLIENT_TARGETS) : $(IDL_NAME).idl $(EXTRN_DEPENDS)
+ midl -Oi -server none -oldnames -error allocation -error ref -ms_ext -c_ext $(CPP) $(IDL_NAME).idl $(INCS)
+ IF EXIST $(IDL_NAME)_c.c copy $(IDL_NAME)_c.c .\client & del $(IDL_NAME)_c.c
+
+$(SERVER_TARGETS) : $(IDL_NAME).idl $(EXTRN_DEPENDS)
+ midl -client none -oldnames -error stub_data -error allocation -error ref -ms_ext -c_ext $(CPP) $(IDL_NAME).idl $(INCS)
+ IF EXIST $(IDL_NAME)_s.c copy $(IDL_NAME)_s.c .\server & del $(IDL_NAME)_s.c
diff --git a/private/net/svcdlls/msgsvc/msgsvc.acf b/private/net/svcdlls/msgsvc/msgsvc.acf
new file mode 100644
index 000000000..93f783dea
--- /dev/null
+++ b/private/net/svcdlls/msgsvc/msgsvc.acf
@@ -0,0 +1,10 @@
+[implicit_handle (handle_t msgsvc_handle)]
+
+interface msgsvc
+
+{
+
+typedef [allocate(all_nodes)] LPMSG_INFO_0;
+typedef [allocate(all_nodes)] LPMSG_INFO_1;
+
+}
diff --git a/private/net/svcdlls/msgsvc/msgsvc.idl b/private/net/svcdlls/msgsvc/msgsvc.idl
new file mode 100644
index 000000000..dcd0739c0
--- /dev/null
+++ b/private/net/svcdlls/msgsvc/msgsvc.idl
@@ -0,0 +1,134 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ msgsvc.idl
+
+Abstract:
+
+ This is the IDL file that describes the RPC interface for the
+ remotable NetMessage API that reside in the messenger service.
+ NOTE: NetMessageBufferSend is in the workstation interface.
+
+Author:
+
+ Dan Lafferty (danl) 28-May-1991
+
+Environment:
+
+ User Mode -Win32
+
+Revision History:
+
+ 28-May-1991 danl
+ Created
+
+--*/
+
+//
+// Interface Attributes
+//
+
+[
+ uuid(17FDD703-1827-4E34-79D4-24A55C53BB37),
+ version(1.0),
+#ifdef __midl
+ ms_union,
+#endif // __midl
+ pointer_default(unique)
+]
+
+//
+// Interface Keyword
+//
+
+interface msgsvc
+
+//
+// Interface Body
+//
+
+{
+
+import "imports.idl";
+#include <lmcons.h>
+
+//
+// Define handle types
+//
+
+typedef [handle] LPWSTR MSGSVC_HANDLE;
+
+//
+// Data Structures Used for Enumeration
+//
+
+typedef struct _MSG_INFO_0_CONTAINER {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] LPMSG_INFO_0 Buffer;
+} MSG_INFO_0_CONTAINER, *PMSG_INFO_0_CONTAINER, *LPMSG_INFO_0_CONTAINER;
+
+typedef struct _MSG_INFO_1_CONTAINER {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] LPMSG_INFO_1 Buffer;
+} MSG_INFO_1_CONTAINER, *PMSG_INFO_1_CONTAINER, *LPMSG_INFO_1_CONTAINER;
+
+
+typedef struct _MSG_ENUM_STRUCT {
+ DWORD Level;
+ [switch_is(Level)] union _MSG_ENUM_UNION {
+ [case(0)] LPMSG_INFO_0_CONTAINER Level0;
+ [case(1)] LPMSG_INFO_1_CONTAINER Level1;
+ } MsgInfo;
+} MSG_ENUM_STRUCT, *PMSG_ENUM_STRUCT, *LPMSG_ENUM_STRUCT;
+
+
+//
+// Data Structures Used for GetInfo
+// (this would also be used for SetInfo if there was one.)
+//
+
+typedef [switch_type(DWORD)] union _MSG_INFO {
+ [case(0)] LPMSG_INFO_0 MsgInfo0;
+ [case(1)] LPMSG_INFO_1 MsgInfo1;
+} MSG_INFO, *PMSG_INFO, *LPMSG_INFO;
+
+
+//
+// Function Prototypes
+//
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetrMessageNameAdd (
+ [in,string,unique] MSGSVC_HANDLE ServerName,
+ [in,string] LPWSTR MsgName
+ );
+
+NET_API_STATUS NET_API_FUNCTION
+NetrMessageNameEnum (
+ [in,string,unique] MSGSVC_HANDLE ServerName,
+ [in,out] LPMSG_ENUM_STRUCT InfoStruct,
+ [in] DWORD PrefMaxLen,
+ [out] LPDWORD TotalEntries,
+ [in,out,unique] LPDWORD ResumeHandle
+ );
+
+NET_API_STATUS NET_API_FUNCTION
+NetrMessageNameGetInfo (
+ [in,string,unique] MSGSVC_HANDLE ServerName,
+ [in,string] LPWSTR MsgName,
+ [in] DWORD Level,
+ [out, switch_is(Level)] LPMSG_INFO InfoStruct
+ );
+
+NET_API_STATUS NET_API_FUNCTION
+NetrMessageNameDel (
+ [in,string,unique] MSGSVC_HANDLE ServerName,
+ [in,string] LPWSTR MsgName
+ );
+
+}
+
diff --git a/private/net/svcdlls/msgsvc/server/apidata.c b/private/net/svcdlls/msgsvc/server/apidata.c
new file mode 100644
index 000000000..618b56e8e
--- /dev/null
+++ b/private/net/svcdlls/msgsvc/server/apidata.c
@@ -0,0 +1,98 @@
+/********************************************************************/
+/** Microsoft LAN Manager **/
+/** Copyright(c) Microsoft Corp., 1987-1990 **/
+/********************************************************************/
+
+#define const
+
+//
+// msgdata.c - Contains all the global data used by the support functions
+// of the message server API.
+//
+
+#include <netcons.h> // needed by service.h
+#include <neterr.h> // Status code definitions.
+#include <error.h> // Status code definitions.
+#include <service.h> // defines for service API usage
+#include <ncb.h> // NCB defines
+#include <smb.h> // SMB defines
+#include <msrv.h> // General message server defines
+#include <srvparam.h> // General server type defines
+
+unsigned short smbret; // SMB return code
+unsigned char smbretclass; // SMB return class
+short mgid; // Message group i.d.
+NCB g_ncb; // NCB used for all send functions
+
+ulfp semPtr; // Pointer to data semaphore
+ucfp dataPtr; // Pointer to shared data area
+
+
+
+// Support Arrays
+//
+// These arrays (single dimensioned) contain one entry for each managed
+// network. This allows each thread (network) to have its own set of
+// "global" data. They are all in the same segment, the size of which is
+// computed by the following formula:
+//
+// size = NumNets * (sizeof(unsigned short) + sizeof(unsigned char) +
+// sizeof(ulfp))
+//
+
+
+
+unsigned short far * NetBios_Hdl; // NetBios handles, one per net
+unsigned char far * net_lana_num; // Lan adaptor numbers
+ul far * wakeupSem; // Semaphores to clear on NCB completion
+
+unsigned long MsgSegSem = 0L; // Protecting the per process data
+ // declared in this file.
+
+
+// Too avoid having an abundance of net errors to confuse the user, most of the
+ * net errors now map to NERR_NetworkError.
+
+
+DWORD const mpnetmes[] =
+ {
+ 0x23, // 00 Number of messages
+ NERR_NetworkError, // 01 NRC_BUFLEN -> invalid length
+ -1, // 02 NRC_BFULL , not expected
+ NERR_NetworkError, // 03 NRC_ILLCMD -> invalid command
+ -1, // 04 not defined
+ NERR_NetworkError, // 05 NRC_CMDTMO -> network busy
+ NERR_NetworkError, // 06 NRC_INCOMP -> messgae incomplete
+ -1, // 07 NRC_BADDR , not expected
+ NERR_NetworkError, // 08 NRC_SNUMOUT -> bad session
+ NERR_NoNetworkResource, // 09 NRC_NORES -> network busy
+ NERR_NetworkError, // 0a NRC_SCLOSED -> session closed
+ NERR_NetworkError, // 0b NRC_CMDCAN -> command cancelled
+ -1, // 0c NRC_DMAFAIL, unexpected
+ NERR_AlreadyExists, // 0d NRC_DUPNAME -> already exists
+ NERR_TooManyNames, // 0e NRC_NAMTFUL -> too many names
+ NERR_DeleteLater, // 0f NRC_ACTSES -> delete later
+ -1, // 10 NRC_INVALID , unexpected
+ NERR_NetworkError, // 11 NRC_LOCTFUL -> too many sessions
+ ERROR_REM_NOT_LIST, // 12 NRC_REMTFUL -> remote not listening*/
+ NERR_NetworkError, // 13 NRC_ILLNN -> bad name
+ NERR_NameNotFound, // 14 NRC_NOCALL -> name not found
+ ERROR_INVALID_PARAMETER, // 15 NRC_NOWILD -> bad parameter
+ NERR_DuplicateName, // 16 NRC_INUSE -> name in use, retry
+ ERROR_INVALID_PARAMETER, // 17 NRC_NAMERR -> bad parameter
+ NERR_NetworkError, // 18 NRC_SABORT -> session ended
+ NERR_DuplicateName, // 19 NRC_NAMCONF -> duplicate name
+ -1, // 1a not defined
+ -1, // 1b not defined
+ -1, // 1c not defined
+ -1, // 1d not defined
+ -1, // 1e not defined
+ -1, // 1f not defined
+ -1, // 20 not defined
+ NERR_NetworkError, // 21 NRC_IFBUSY -> network busy
+ NERR_NetworkError, // 22 NRC_TOOMANY -> retry later
+ NERR_NetworkError // 23 NRC_BRIDGE -> bridge error
+ };
+
+ LPTSTR MessageFileName;
+
diff --git a/private/net/svcdlls/msgsvc/server/apiutil.c b/private/net/svcdlls/msgsvc/server/apiutil.c
new file mode 100644
index 000000000..7cb70ae73
--- /dev/null
+++ b/private/net/svcdlls/msgsvc/server/apiutil.c
@@ -0,0 +1,496 @@
+/*++
+
+Copyright (c) 1991-1992 Microsoft Corporation
+
+Module Name:
+
+ apiutil.c
+
+Abstract:
+
+ Contains functions used by the Messenger API. This file contains the
+ following functions:
+
+ MsgIsValidMsgName
+ MsgMapNetError
+ MsgLookupName
+ message_sec_check
+ MsgGatherInfo
+ MsgUnformatName
+
+Author:
+
+ Dan Lafferty (danl) 22-Jul-1991
+
+Environment:
+
+ User Mode -Win32
+
+Notes:
+
+ These functions were ported from LM2.0. This file contains functions
+ from several LM2.0 files. Not all functions were used in this file
+ since some were made obsolete by the NT Service Model.
+ The following LM2.0 files were incorportated into this single file:
+
+ msgutils.c
+ msgutil2.c
+ dupname.c
+ netname.c
+
+Revision History:
+
+ 22-Jul-1991 danl
+ ported from LM2.0
+
+--*/
+
+//
+// Includes
+//
+
+
+#include "msrv.h"
+#include <tstring.h> // Unicode string macros
+#include <lmwksta.h>
+#include <lmmsg.h>
+
+#include <smbtypes.h> // needed for smb.h
+#include <smb.h> // Server Message Block definitions
+
+#include <icanon.h> // I_NetNameValidate
+#include <netlib.h> // NetpCopyStringToBuffer
+
+#include "msgdbg.h" // MSG_LOG
+#include "heap.h"
+#include "msgdata.h"
+#include "apiutil.h"
+
+
+//
+// Table of NetBios mappings to Net Errors.
+//
+ DWORD const mpnetmes[] =
+ {
+ 0x23, // 00 Number of messages
+ NERR_NetworkError, // 01 NRC_BUFLEN -> invalid length
+ 0xffffffff, // 02 NRC_BFULL , not expected
+ NERR_NetworkError, // 03 NRC_ILLCMD -> invalid command
+ 0xffffffff, // 04 not defined
+ NERR_NetworkError, // 05 NRC_CMDTMO -> network busy
+ NERR_NetworkError, // 06 NRC_INCOMP -> messgae incomplete
+ 0xffffffff, // 07 NRC_BADDR , not expected
+ NERR_NetworkError, // 08 NRC_SNUMOUT -> bad session
+ NERR_NoNetworkResource, // 09 NRC_NORES -> network busy
+ NERR_NetworkError, // 0a NRC_SCLOSED -> session closed
+ NERR_NetworkError, // 0b NRC_CMDCAN -> command cancelled
+ 0xffffffff, // 0c NRC_DMAFAIL, unexpected
+ NERR_AlreadyExists, // 0d NRC_DUPNAME -> already exists
+ NERR_TooManyNames, // 0e NRC_NAMTFUL -> too many names
+ NERR_DeleteLater, // 0f NRC_ACTSES -> delete later
+ 0xffffffff, // 10 NRC_INVALID , unexpected
+ NERR_NetworkError, // 11 NRC_LOCTFUL -> too many sessions
+ ERROR_REM_NOT_LIST, // 12 NRC_REMTFUL -> remote not listening*/
+ NERR_NetworkError, // 13 NRC_ILLNN -> bad name
+ NERR_NameNotFound, // 14 NRC_NOCALL -> name not found
+ ERROR_INVALID_PARAMETER, // 15 NRC_NOWILD -> bad parameter
+ NERR_DuplicateName, // 16 NRC_INUSE -> name in use, retry
+ ERROR_INVALID_PARAMETER, // 17 NRC_NAMERR -> bad parameter
+ NERR_NetworkError, // 18 NRC_SABORT -> session ended
+ NERR_DuplicateName, // 19 NRC_NAMCONF -> duplicate name
+ 0xffffffff, // 1a not defined
+ 0xffffffff, // 1b not defined
+ 0xffffffff, // 1c not defined
+ 0xffffffff, // 1d not defined
+ 0xffffffff, // 1e not defined
+ 0xffffffff, // 1f not defined
+ 0xffffffff, // 20 not defined
+ NERR_NetworkError, // 21 NRC_IFBUSY -> network busy
+ NERR_NetworkError, // 22 NRC_TOOMANY -> retry later
+ NERR_NetworkError // 23 NRC_BRIDGE -> bridge error
+ };
+
+
+DWORD
+MsgIsValidMsgName(
+ IN LPTSTR name
+ )
+
+/*++
+
+Routine Description:
+
+ Check for a valid messaging name.
+ This function checks for the validity of a messaging name.
+
+
+Arguments:
+
+ name - pointer to name to validate.
+
+
+Return Value:
+
+ Error code from I_NetNameValidate
+
+
+--*/
+
+{
+ TCHAR namebuf[MAX_PATH];
+ DWORD err_code;
+
+ STRNCPY( namebuf, name, (sizeof(namebuf)/sizeof(namebuf[0])-1));
+
+ namebuf[(sizeof(namebuf)/sizeof(namebuf[0]))-1] = TEXT('\0');
+
+ err_code = I_NetNameValidate(NULL, namebuf, NAMETYPE_COMPUTER, 0L);
+
+ if (err_code != 0) {
+ return( err_code);
+ }
+
+ //
+ // Any name beginning with a * must be rejected as the message
+ // server relies on being able to ASTAT the name, and an ASTAT
+ // name commencing with a * means ASTAT the local card.
+ //
+
+ if(namebuf[0] == TEXT('*')) {
+ return( ERROR_INVALID_PARAMETER);
+ }
+
+ return(NERR_Success);
+}
+
+
+DWORD
+MsgMapNetError(
+ IN UCHAR Code // Error code
+ )
+
+/*++
+
+Routine Description:
+
+ Map NetBios Error code to a message number
+
+Arguments:
+
+ code - Error code from NetBios (can be 0)
+
+Return Value:
+
+ Message code as defined in msgs.h
+
+--*/
+{
+ DWORD dwCode;
+
+ dwCode = 0 | (UCHAR)Code;
+
+ if( dwCode == 0) {
+ return(NERR_Success); // Special case
+ }
+
+ if((dwCode > 0) && (dwCode < mpnetmes[0])) {
+ return(mpnetmes[dwCode]);
+ }
+
+ return (NERR_NetworkError); // Can't map it!
+}
+
+DWORD
+MsgLookupName(
+ IN DWORD net, // The network card to search
+ IN LPSTR name // Formatted name (Non-unicode)
+ )
+
+/*++
+
+Routine Description:
+
+ This function looks up a formatted name in the name table in the
+ Message Server's shared data area.
+
+ This function looks the given name up in the name table in the shared
+ data area. In order to match the given name, the first NCBNAMLEN - 1
+ characters of the name in the name table must be identical to the same
+ characters in the given name, and the name in the name table must not be
+ marked as deleted. This function assumes that the shared data area is
+ accessible and that the global variable, dataPtr, is valid.
+
+Arguments:
+
+ name - pointer to formatted name
+
+
+Return Value:
+
+ DWORD - index into table if found, -1 otherwise
+
+--*/
+
+{
+ DWORD i; // Index
+
+ for(i = 0; i < NCBMAX; ++i) { // Loop to search for name
+
+ if( !memcmp( name, SD_NAMES(net,i), NCBNAMSZ - 1) &&
+ !(SD_NAMEFLAGS(net,i) & NFDEL) ) {
+
+ //
+ // Return index if match found
+ //
+ return(i);
+ }
+ }
+ return(0xffffffff); // No match
+}
+
+// message_sec_check
+//
+// A common routine to check caller priv/auth against that
+// required to call the message apis.
+//
+//
+
+NET_API_STATUS
+message_sec_check(VOID)
+{
+#ifdef later
+ //
+ // API security check. This call can be called by anyone locally,
+ // but only by admins in the remote case.
+
+ I_SecSyncSet(SECSYNC_READER);
+
+ if ( ( clevel == ACCESS_REMOTE ) &&
+ ( callinf != NULL ) &&
+ ( CALLER_PRIV(callinf) != USER_PRIV_ADMIN ) )
+ {
+ I_SecSyncClear(SECSYNC_READER);
+ return(ERROR_ACCESS_DENIED);
+ }
+ I_SecSyncClear(SECSYNC_READER);
+#endif
+ return (NERR_Success);
+}
+
+
+NET_API_STATUS
+MsgGatherInfo (
+ IN DWORD Level,
+ IN LPSTR FormattedName,
+ IN OUT LPBYTE *InfoBufPtr,
+ IN OUT LPBYTE *StringBufPtr
+ )
+
+/*++
+
+Routine Description:
+
+
+
+Arguments:
+
+ Level - Indicates the level of information that is being returned.
+
+ FormattedName - This is a name that messages are received by. This
+ name is formated for NCB transactions. Therefore, it is made
+ up of ANSI characters that are space padded to fill out a packet
+ of NCBNAMSZ characters. The last character is always a 03
+ (indicating a non-forwarded name).
+
+ InfoBufPtr - On input, this is a pointer to a pointer to where the
+ messenger information is to be placed. On successful return, this
+ location contains a pointer to the location where the next
+ information will be placed (on the next call to this function).
+
+ StringBufPtr - On input, thisis a pointer to a pointer to where the
+ NUL terminated name string for that info record is to be placed.
+ On successful return, this location contains a pointer to the
+ location where the next set of strings will be placed (on the
+ next call to this function).
+
+Return Value:
+
+ NERR_Success - The information was successfully gathered and placed
+ in the info buffer.
+
+ NERR_Internal_Error - The Formatted Name could not be correctly
+ translated into a meaningful Unicode Name.
+
+ ERROR_INVALID_LEVEL - An illegal info level was passed in.
+
+ ERROR_NOT_ENOUGH_MEMORY - Not enough room to store gathered information.
+
+--*/
+{
+ NET_API_STATUS status;
+ BOOL bStatus;
+ PCHAR fixedDataEnd; // pointer to free space from top of buffer.
+ LPMSG_INFO_0 infoBuf0;
+ LPMSG_INFO_1 infoBuf1;
+ TCHAR unicodeName[NCBNAMSZ];
+
+ //
+ // Convert the name to Unicode
+ //
+ status = MsgUnformatName(unicodeName, FormattedName);
+ if (status != NERR_Success) {
+ return(status);
+ }
+
+ switch (Level) {
+ case LEVEL_0:
+ infoBuf0 = (LPMSG_INFO_0)*InfoBufPtr;
+ fixedDataEnd = (PCHAR)infoBuf0 + sizeof(MSG_INFO_0);
+
+ if( fixedDataEnd >= *StringBufPtr) {
+ return(ERROR_NOT_ENOUGH_MEMORY);
+ }
+
+ bStatus = NetpCopyStringToBuffer (
+ unicodeName, // The String
+ STRLEN(unicodeName), // StringLength
+ fixedDataEnd, // FixedDataEnd
+ (PVOID)StringBufPtr, // EndOfVariableData
+ &infoBuf0->msgi0_name); // VariableDataPointer
+
+ if (bStatus == FALSE) {
+ MSG_LOG(TRACE,"MsgGatherInfo(level0): Not enough room\n",0);
+ return(ERROR_NOT_ENOUGH_MEMORY);
+ }
+ *InfoBufPtr = (LPBYTE)fixedDataEnd;
+ break;
+
+ case LEVEL_1:
+ infoBuf1 = (LPMSG_INFO_1)*InfoBufPtr;
+
+ fixedDataEnd = (PCHAR)infoBuf1 + sizeof(MSG_INFO_1);
+ if( fixedDataEnd >= *StringBufPtr) {
+ return(ERROR_NOT_ENOUGH_MEMORY);
+ }
+
+ bStatus = NetpCopyStringToBuffer (
+ unicodeName, // The String
+ STRLEN(unicodeName), // StringLength
+ fixedDataEnd, // FixedDataEnd
+ (PVOID)StringBufPtr, // EndOfVariableData
+ &infoBuf1->msgi1_name); // VariableDataPointer
+
+ if (bStatus == FALSE) {
+ MSG_LOG(TRACE,"MsgGatherInfo(level1): Not enough room\n",0);
+ return(ERROR_NOT_ENOUGH_MEMORY);
+ }
+
+ //
+ // Set all the forward information to NULL since forwarding
+ // is not supported.
+ //
+ infoBuf1->msgi1_forward_flag = 0;
+ infoBuf1->msgi1_forward = NULL;
+
+ *InfoBufPtr = (LPBYTE)fixedDataEnd;
+ break;
+
+ default:
+ MSG_LOG(TRACE,"MsgGatherInfo Invalid level\n",0);
+ return(ERROR_INVALID_LEVEL);
+ break;
+ }
+
+ return(NERR_Success);
+
+}
+
+
+NET_API_STATUS
+MsgUnformatName(
+ OUT LPTSTR UnicodeName,
+ IN LPSTR FormattedName
+ )
+
+/*++
+
+Routine Description:
+
+ This routine creates a Unicode NUL-Terminated version of a NetBios
+ Formatted name.
+
+Arguments:
+
+ UnicodeName - This is a pointer to a location where the un-formatted
+ NUL terminated Unicode Name is to be copied.
+
+ FormattedName - This is a pointer to an NCB formatted name. This
+ name always contains NCBNAMSZ characters of which the last character
+ is a code used for a forward/non-forward flag. These strings
+ are space padded.
+
+
+Return Value:
+
+ NERR_Success - The operation was successful.
+
+ NERR_Internal - The operation was unsuccessful.
+
+--*/
+{
+ UNICODE_STRING unicodeString;
+ OEM_STRING ansiString;
+ NTSTATUS ntStatus;
+ int i;
+
+ //
+ // Translate the ansi string in the name table to a unicode name
+ //
+#ifdef UNICODE
+ unicodeString.Length = (NCBNAMSZ -1) * sizeof(WCHAR);
+ unicodeString.MaximumLength = NCBNAMSZ * sizeof(WCHAR);
+ unicodeString.Buffer = (LPWSTR)UnicodeName;
+
+ ansiString.Length = NCBNAMSZ-1;
+ ansiString.MaximumLength = NCBNAMSZ;
+ ansiString.Buffer = FormattedName;
+
+ ntStatus = RtlOemStringToUnicodeString(
+ &unicodeString, // Destination
+ &ansiString, // Source
+ FALSE); // Don't allocate the destination.
+
+ if (!NT_SUCCESS(ntStatus)) {
+ MSG_LOG(ERROR,
+ "UnformatName:RtlOemStringToUnicodeString Failed rc=%X\n",
+ ntStatus);
+ //
+ // Indicate a failure
+ //
+ return(NERR_InternalError);
+ }
+#else
+ UNUSED (ntStatus);
+ UNUSED (ansiString);
+ UNUSED (unicodeString);
+ strncpy(UnicodeName, FormattedName, NCBNAMSZ-1);
+#endif
+
+ //
+ // Remove excess Space characters starting at the back (skipping
+ // the 03 flag character.
+ //
+ i = NCBNAMSZ-2;
+
+ while ( UnicodeName[i] == TEXT(' ')) {
+
+ UnicodeName[i--] = TEXT('\0');
+
+ if (i < 0) {
+ MSG_LOG(ERROR,
+ "UnformatName:Nothing but space characters\n",0);
+ return(NERR_InternalError);
+ }
+ }
+ return(NERR_Success);
+}
diff --git a/private/net/svcdlls/msgsvc/server/apiutil.h b/private/net/svcdlls/msgsvc/server/apiutil.h
new file mode 100644
index 000000000..00c305f7b
--- /dev/null
+++ b/private/net/svcdlls/msgsvc/server/apiutil.h
@@ -0,0 +1,75 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ apidata.h
+
+Abstract:
+
+ contains prototypes for api helper utility function.
+
+Author:
+
+ Dan Lafferty (danl) 25-Jul-1991
+
+Environment:
+
+ User Mode -Win32
+
+Notes:
+
+ optional-notes
+
+Revision History:
+
+ 25-Jul-1991 danl
+ created
+
+--*/
+
+#ifndef _APIUTIL_INCLUDED
+#define _APIUTIL_INCLUDED
+
+
+//
+// Function Prototypes
+//
+
+DWORD
+MsgIsValidMsgName(
+ IN LPTSTR name
+ );
+
+DWORD
+MsgMapNetError(
+ IN UCHAR Code // Error code
+ );
+
+DWORD
+MsgLookupName(
+ IN DWORD net, // The network card to search
+ IN LPSTR name // Formatted name (Non-unicode)
+ );
+
+NET_API_STATUS
+message_sec_check(VOID);
+
+NET_API_STATUS
+MsgGatherInfo (
+ IN DWORD Level,
+ IN LPSTR FormattedName,
+ IN OUT LPBYTE *InfoBufPtr,
+ IN OUT LPBYTE *StringBufPtr
+ );
+
+NET_API_STATUS
+MsgUnformatName(
+ LPTSTR UnicodeName,
+ LPSTR FormattedName
+ );
+
+
+#endif // _APIUTIL_INCLUDED
+
diff --git a/private/net/svcdlls/msgsvc/server/data.c b/private/net/svcdlls/msgsvc/server/data.c
new file mode 100644
index 000000000..ae52f2ae1
--- /dev/null
+++ b/private/net/svcdlls/msgsvc/server/data.c
@@ -0,0 +1,642 @@
+/*****************************************************************/
+/** Microsoft LAN Manager **/
+/** Copyright(c) Microsoft Corp., 1990 **/
+/*****************************************************************/
+// data.c
+//
+// This file contains most of the data declarations and set up routines
+// used by the messenger service.
+//
+//
+
+#include "msrv.h" // Message server declarations
+#include <rpc.h> // RPC_HANDLE
+#include <winsvc.h> // Defines for using service API
+
+#include <smbtypes.h> // needed for smb.h
+#include <smb.h> // Server Message Block definition
+#include <netlib.h> // UNUSED macro
+#include <align.h> // ROUND_UP_POINTER
+#include <timelib.h> // NET_TIME_FORMAT
+
+#include "msgdbg.h" // MSG_LOG
+#include <services.h> // LMSVCS_ENTRY_POINT, LMSVCS_GLOBAL_DATA
+
+/* Shared data segement
+ *
+ * NT NOTE:
+ * The NT Messenger has no need for shared data. However for purposes
+ * of porting code, this is still referred to as the shared data segment.
+ *
+ * The messenger shared data segment is arranged in the following manner.
+ *
+ * # of bytes Purpose
+ * ---------- -------
+ * 4 Data Access RAM Semaphore
+ * 2 Number of non-loopback nets installed
+ * 2 Messenger service started flag
+ * PATHLEN Message Log File Name
+ * 2 Logging Status Flag
+ * 2 Message Buffer Length
+ * 2 First Message in Queue
+ * 2 Last Message in Queue
+ * 15*NumNets Flags (one byte per name, per card.)
+ * 15*NumNets Name Numbers (one per name, per card. Used for some NCBs.)
+ * 15*16*NumNets Names (15, 16 byte names per card.)
+ * 15*16*NumNets Forward names (same as names.)
+ * 15*2*NumNets Index to current message (2 bytes, per name, per card.)
+ * N Message Buffer (Current Default N = 1750 bytes.)
+ *
+ * The size of the segement allocated to hold this data is computed by the
+ * following formula:
+ *
+ * size = (PATHLEN + N + 16 + (540 * NumNets))
+ *
+ * which with current values for PATHLEN and N, and a machine hooked up to
+ * two networks works out to be 2926 bytes.
+ *
+ * Most of data in this shared segment is to be accessed only through MACROS,
+ * which are defined in msrv.h. The variables defined below are used in
+ * these macros, and in the few other places that the data is to be accessed
+ * directly.
+ *
+ * Each slot on each network card has the following data associated with it:
+ *
+ * Name - the message name in that slot on the card, if it has one.
+ * Flags - one byte of status flags for that name. Contains values
+ * such as new, forwarded, deleted, etc...
+ * Name Number - Used by some NCB commands.
+ * NCB - A Network Control Block is dedicated to each slot. If
+ * the slot is not in use, the NCB is also available.
+ * NCB Buffer - A buffer associated with each NCB.
+ *
+ * This data is arranged in a set of two dimensional arrays, such that any
+ * item in any of these arrays at the same indexed position is associated
+ * with the same slot on the same card. Some of these arrays are found in
+ * the shared data segment, and are accessed using only the appropriate
+ * macros. (Yes this is silly, but comes from extending the old design to
+ * multiple nets.) Others are allocated separately, as discussed in the
+ * following sections.
+ *
+ */
+
+
+ LPBYTE dataPtr; // Pointer to shared data segment
+// DWORD dataSem; // Pointer to shared data access semaphore
+
+
+/* NCB Array
+ *
+ * The NCB array and NCB Buffer array are contained in the same segment.
+ * (Separate from the shared segment discussed above.) Both arrays are
+ * two dimensional, NumNets X NCBMAX in size. The size of the segment
+ * allocated to hold these arrays is computed by the following formula:
+ *
+ * size = NumNets * NCBMAX * (sizeof(struct ncb) + BUFLEN) +
+ * (2 * NumNets * sizeof(char far *))
+ *
+ * The second line of this is for the overhead of the pointers (for both
+ * arrays).
+ *
+ * Current values for the size of the "struct ncb" and BUFLEN allow us to
+ * fit 16 networks worth (256 NCBs and Buffers) into a single 64K segment.
+ * This number (16) has been chosen as the upper limit on the number of
+ * networks the multi-net messenger will manage at one time.
+ */
+
+
+ PNCB *ncbArray; // Two dimensional array of NCBs
+ LPBYTE *ncbBuffers; // Two-D array of NCB Buffers
+
+
+
+/* Service Arrays
+ *
+ * Servicing the completed asynchronous NCBs requires maintaining several
+ * arrays of general information. These arrays contain one entry for each
+ * NCB owned by the messenger, that is NumNets*NCBMAX. These arrays are
+ * allocated at run time and referenced through the following pointers.
+ * All are two-d arrays. All are located in the same segment, the size of
+ * which is computed using the folowing formula:
+ *
+ * size = ( (sizeof(short) + 1 + sizeof(struct ncb_status)) * TNCB ) +
+ * ( (4 * NumNets + TNCB) * sizeof(short far *) )
+ *
+ * where TNCB = NumNets * NCBMAX.
+ *
+ */
+
+
+ PCHAR *mpncbistate; // Message transfer state flags
+ PSHORT *mpncbimgid; // Message group i.d. numbers
+
+//
+// Service Routines
+//
+
+// NOTE: THIS TYPE WAS ALREADY TYPEDEF'D IN MSRV.H
+//typedef VOID (*PNCBIFCN) (
+// DWORD NetIndex, // Network Index
+// DWORD NcbIndex, // Network Control Block Index
+// CHAR RetVal // value returned by net bios
+// );
+//
+// typedef PNCBIFCN LPNCBIFCN;
+
+ LPNCBIFCN **mpncbifun;
+
+// void (*(far * far * mpncbifun))(short, int, char); // Service routines
+
+
+//struct ncb_status far * far * ncbStatus; // NCB Status structures
+
+ LPNCB_STATUS *ncbStatus;
+
+/* Support Arrays
+ *
+ * These arrays (single dimensioned) contain one entry for each managed
+ * network. This allows each thread (network) to have its own set of
+ * "global" data. They are all in the same segment, the size of which is
+ * computed by the following formula:
+ *
+ * size = NumNets * (sizeof(unsigned short) + sizeof(unsigned char) +
+ * sizeof(ulfp))
+ *
+ */
+
+
+//unsigned short far * NetBios_Hdl; // NetBios handles, one per net
+
+ LPBYTE net_lana_num; // Lan adaptor numbers
+ PHANDLE wakeupSem; // Semaphores to clear on NCB completion
+
+
+//
+// Other Global Data
+//
+// The other misc. global data that the messenger uses.
+//
+
+ DWORD MsgsvcDebugLevel; // Debug level flag used by MSG_LOG
+
+ LPTSTR MessageFileName;
+
+ //
+ // The local machine name and length/
+ //
+ TCHAR machineName[NCBNAMSZ+sizeof(TCHAR)];
+ SHORT MachineNameLen;
+
+ SHORT mgid; // The message group i.d. counter
+
+// USHORT g_install_state;
+
+//
+// The following is used to keep store the state of the messenger service
+// Either it is RUNNING or STOPPING.
+//
+ DWORD MsgrState;
+
+
+
+//
+// Handle returned by RegisterServiceCtrlHandle and needed to
+// set the service status via SetServiceStatus
+//
+SERVICE_STATUS_HANDLE MsgrStatusHandle;
+
+
+//
+// Global TimeFormat to be used for Messages.
+// Also, the critical section used to guard access to it.
+//
+ NET_TIME_FORMAT GlobalTimeFormat = {
+ NULL, // AMString
+ NULL, // PMString
+ TRUE, // TwelveHour
+ FALSE, // AMPM prefix
+ FALSE, // LeadingZero
+ NULL, // DateFormat
+ NULL}; // TimeSeparator
+
+ CRITICAL_SECTION TimeFormatCritSec;
+//
+// This string is used to mark the location of the time string in
+// a message header so that the display thread can find after it reads
+// it from the queue.
+//
+ LPSTR GlobalTimePlaceHolder="***";
+
+//
+// This is the string used in the title bar of the Message Box used
+// to display messages.
+// GlobalMessageBoxTitle will either point to the default string, or
+// to the string allocated in the FormatMessage Function.
+//
+ WCHAR DefaultMessageBoxTitle[]= L"Messenger Service";
+ LPWSTR GlobalAllocatedMsgTitle=NULL;
+ LPWSTR GlobalMessageBoxTitle=DefaultMessageBoxTitle;
+
+//
+// This is where well-known SIDs and pointers to RpcServer routines are
+// stored.
+//
+ PLMSVCS_GLOBAL_DATA MsgsvcGlobalData;
+
+
+//
+// Functions
+//
+// The following routines are defined for creating and destroying the
+// data (arrays, etc.) defined above.
+//
+
+//
+// InitNCBSeg
+//
+// Allocates and initializes the segment containing the NCB and NCB Buffer
+// arrays. Does not initialize the NCBs themselves. This is done by
+// InitNCBs();
+//
+
+NET_API_STATUS
+MsgInitNCBSeg(VOID)
+{
+
+ DWORD size;
+ DWORD i;
+
+ NET_API_STATUS status;
+ LPBYTE memPtr;
+
+ size = ((SD_NUMNETS() * sizeof(PNCB)) +
+ (SD_NUMNETS() * NCBMAX * sizeof(NCB)) +
+ (SD_NUMNETS() * sizeof(LPBYTE)) +
+ (SD_NUMNETS() * NCBMAX * BUFLEN) );
+
+
+ memPtr = LocalAlloc(LMEM_ZEROINIT, size);
+ if (memPtr == NULL) {
+ status = GetLastError();
+ MSG_LOG(ERROR,"SetUpMessageFile:LocalAlloc Failure %X\n",
+ status);
+ return(status);
+ }
+
+ MSG_LOG(TRACE,"InitNCBSeg: Allocated memory success\n",0);
+
+ //
+ // Set up NCB array
+ //
+ ncbArray = (PNCB *) memPtr;
+ memPtr += SD_NUMNETS() * sizeof(PNCB);
+
+ for ( i = 0; i < SD_NUMNETS(); i++ ) {
+ ncbArray[i] = (PNCB) memPtr;
+ memPtr += NCBMAX * sizeof(NCB);
+ }
+
+ //
+ // Initialize the ncbs
+ //
+ MsgInitNCBs();
+
+ //
+ // Set up NCB Buffer array
+ //
+ ncbBuffers = (LPBYTE *)memPtr;
+ memPtr += SD_NUMNETS() * sizeof(LPBYTE);
+
+ for ( i = 0; i < SD_NUMNETS(); i++ ) {
+ ncbBuffers[i] = (LPBYTE) memPtr;
+ memPtr += NCBMAX * BUFLEN;
+ }
+
+ return (NERR_Success);
+}
+
+
+VOID
+MsgFreeNCBSeg(VOID)
+{
+ HANDLE status;
+
+ status = LocalFree (ncbArray);
+ if (status != 0) {
+ MSG_LOG(ERROR,"FreeNCBSeg:LocalFree Failed %X\n",
+ GetLastError());
+ }
+
+ return;
+
+}
+
+
+/*
+ * InitNCBs - initialize Network Control Blocks
+ *
+ * This function initializes all the NCB's to appear as though
+ * they have not completed so that FindCompletedNCB() will not
+ * find any of them.
+ *
+ * MsgInitNCBs ()
+ *
+ * RETURN
+ * nothing
+ *
+ * SIDE EFFECTS
+ *
+ * Sets the NCB_CPLT field of each NCB in the NCB array to 0xFF.
+ * and the NCB_RETCODE field to 0 (needed for unistalling).
+ */
+
+VOID
+MsgInitNCBs(VOID)
+{
+ DWORD neti; // Network index
+ DWORD ncbi; // NCB index
+
+ for ( neti = 0; neti < SD_NUMNETS() ; neti++) {
+ for(ncbi = 0; ncbi < NCBMAX; ++ncbi) {
+ ncbArray[neti][ncbi].ncb_cmd_cplt = 0xff;
+ ncbArray[neti][ncbi].ncb_retcode = 0;
+ }
+ }
+}
+
+/* MsgInitServiceSeg
+ *
+ * Allocates and initializes the segment containing the Service
+ * arrays.
+ *
+ */
+
+NET_API_STATUS
+MsgInitServiceSeg()
+{
+
+ NET_API_STATUS status;
+ DWORD size;
+ int TNCB; // Total NCBs in the messenger.
+ DWORD i;
+ LPBYTE memPtr;
+
+ TNCB = SD_NUMNETS() * NCBMAX;
+
+ //
+ // Size is calculated as follows:
+ //
+ // The first line calculates the buffer space for mpncbistate (char)
+ // mpncbimgid (short) and ncbStatus.
+ //
+ // The second line calculates the space for the array of pointers
+ // for all four components plus the buffer space for mpncbifun - which
+ // is an array of pointers.
+ //
+ // *ALIGNMENT* (note the extra four bytes to resolve alignment problems)
+ //
+
+
+ size = ((SD_NUMNETS() * sizeof(PCHAR)) +
+ (SD_NUMNETS() * NCBMAX ) +
+ (SD_NUMNETS() * sizeof(PSHORT)) +
+ (SD_NUMNETS() * NCBMAX * sizeof(PSHORT)) +
+ (SD_NUMNETS() * NCBMAX * sizeof(SHORT)) +
+ (SD_NUMNETS() * sizeof(LPNCBIFCN *)) +
+ (SD_NUMNETS() * NCBMAX * sizeof(LPNCBIFCN)) +
+ (SD_NUMNETS() * sizeof(LPNCB_STATUS)) +
+ (SD_NUMNETS() * NCBMAX * sizeof(NCB_STATUS)) + 4);
+
+
+// size = ( (sizeof(short) + 1 + sizeof(NCB_STATUS)) * TNCB ) +
+// ( (4 * SD_NUMNETS() + TNCB) * sizeof(PBYTE) );
+
+ memPtr = LocalAlloc(LMEM_ZEROINIT, size);
+ if (memPtr == NULL) {
+ status = GetLastError();
+ MSG_LOG(ERROR,"InitServiceSeg:LocalAlloc Failure %X\n",
+ status);
+ return(status);
+ }
+
+ MSG_LOG(TRACE,"InitServiceSeg: Allocated memory success\n",0);
+
+ //
+ // Set up message transfer state flag array.
+ //
+ mpncbistate = (PCHAR *) memPtr;
+ memPtr += SD_NUMNETS() * sizeof(PCHAR);
+
+ for ( i = 0; i < SD_NUMNETS(); i++ ) {
+ mpncbistate[i] = (PCHAR) memPtr;
+ memPtr += NCBMAX;
+ }
+
+ //
+ // Set up message group i.d. number array
+ //
+ mpncbimgid = (PSHORT *)memPtr;
+ memPtr += SD_NUMNETS() * sizeof(PSHORT);
+
+ for ( i = 0; i < SD_NUMNETS(); i++ ) {
+ mpncbimgid[i] = (PSHORT) memPtr;
+ memPtr += NCBMAX * sizeof(SHORT);
+ }
+
+ // *ALIGNMENT*
+ memPtr = ROUND_UP_POINTER(memPtr,4);
+
+ //
+ // Set up service routine array
+ //
+ mpncbifun = (LPNCBIFCN **) memPtr;
+ memPtr += SD_NUMNETS() * sizeof(LPNCBIFCN *);
+
+ for ( i = 0; i < SD_NUMNETS(); i++ ) {
+ mpncbifun[i] = (LPNCBIFCN *) memPtr;
+ memPtr += NCBMAX * sizeof(LPNCBIFCN);
+ }
+
+ //
+ // Set up NCB status structure array
+ //
+ ncbStatus = (LPNCB_STATUS *)memPtr;
+ memPtr += SD_NUMNETS() * sizeof(LPNCB_STATUS);
+
+ for ( i = 0; i < SD_NUMNETS(); i++ ) {
+ ncbStatus[i] = (LPNCB_STATUS) memPtr;
+ memPtr += NCBMAX * sizeof(NCB_STATUS);
+ }
+
+ return (NERR_Success);
+
+}
+
+
+VOID
+MsgFreeServiceSeg(VOID)
+{
+
+ HANDLE status;
+
+ status = LocalFree (mpncbistate);
+ if (status != 0) {
+ MSG_LOG(ERROR,"FreeServiceSeg:LocalFree Failed %X\n",
+ GetLastError());
+ }
+
+ return;
+
+}
+
+
+/* MsgInitSupportSeg
+ *
+ * Allocates and initializes the segment containing the Support
+ * arrays.
+ *
+ */
+
+NET_API_STATUS
+MsgInitSupportSeg(VOID)
+{
+
+ unsigned int size;
+ DWORD i;
+ char far * memPtr;
+ DWORD status;
+
+ //
+ // Calculate the buffer size.
+ // *ALIGNMENT* (Note the extra four bytes for alignment)
+ //
+
+ size = ( (SD_NUMNETS() * sizeof(UCHAR) ) +
+ ((SD_NUMNETS() + 1) * sizeof(HANDLE)) + 4 );
+
+ memPtr = LocalAlloc(LMEM_ZEROINIT, size);
+ if (memPtr == NULL) {
+ status = GetLastError();
+ MSG_LOG(ERROR,"[MSG]InitSupportSeg:LocalAlloc Failure %X\n", status);
+ return(status);
+ }
+
+ //
+ // Set up net_lana_num array
+ //
+ net_lana_num = (unsigned char far *)memPtr;
+ memPtr += SD_NUMNETS() * sizeof(unsigned char);
+
+ // *ALIGNMENT*
+ memPtr = ROUND_UP_POINTER(memPtr,4);
+
+ //
+ // Set up wakeupSem array
+ //
+ wakeupSem = (PHANDLE)memPtr;
+ // + 1 for the group mailslot
+ memPtr += (SD_NUMNETS() + 1) * sizeof(HANDLE);
+
+ for ( i = 0; i < SD_NUMNETS() ; i++ )
+ wakeupSem[i] = (HANDLE)0;
+
+ return (NERR_Success);
+
+}
+
+
+VOID
+MsgFreeSupportSeg(VOID)
+{
+ HANDLE status;
+
+ status = LocalFree (net_lana_num);
+ if (status != 0) {
+ MSG_LOG(ERROR,"FreeSupportSeg:LocalFree Failed %X\n",
+ GetLastError());
+ }
+
+ return;
+}
+
+
+BOOL
+MsgDatabaseLock(
+ IN MSG_LOCK_REQUEST request,
+ IN LPSTR idString
+ )
+
+/*++
+
+Routine Description:
+
+ This routine handles all access to the Messenger Service database
+ lock. This lock is used to protect access in the shared data segment.
+
+ Reading the Database is handled with shared access. This allows several
+ threads to read the database at the same time.
+
+ Writing (or modifying) the database is handled with exclusive access.
+ This access is not granted if other threads have read access. However,
+ shared access can be made into exclusive access as long as no other
+ threads have shared or exclusive access.
+
+Arguments:
+
+ request - This indicates what should be done with the lock. Lock
+ requests are listed in dataman.h
+
+ idString - This is a string that identifies who is requesting the lock.
+ This is used for debugging purposes so I can see where in the code
+ a request is coming from.
+
+Return Value:
+
+ none:
+
+
+--*/
+
+{
+ BOOL status = TRUE;
+
+ static RTL_RESOURCE MSG_DatabaseLock;
+
+ switch(request) {
+ case MSG_INITIALIZE:
+ RtlInitializeResource( &MSG_DatabaseLock );
+ break;
+ case MSG_GET_SHARED:
+ MSG_LOG(LOCKS,"%s:Asking for MSG Database Lock shared...\n",idString);
+ status = RtlAcquireResourceShared( &MSG_DatabaseLock, TRUE );
+ MSG_LOG(LOCKS,"%s:Acquired MSG Database Lock shared\n",idString);
+ break;
+ case MSG_GET_EXCLUSIVE:
+ MSG_LOG(LOCKS,"%s:Asking for MSG Database Lock exclusive...\n",idString);
+ status = RtlAcquireResourceExclusive( &MSG_DatabaseLock, TRUE );
+ MSG_LOG(LOCKS,"%s:Acquired MSG Database Lock exclusive\n",idString);
+ break;
+ case MSG_RELEASE:
+ MSG_LOG(LOCKS,"%s:Releasing MSG Database Lock...\n",idString);
+ RtlReleaseResource( &MSG_DatabaseLock );
+ MSG_LOG(LOCKS,"%s:Released MSG Database Lock\n",idString);
+ break;
+ case MSG_DELETE:
+ RtlDeleteResource( &MSG_DatabaseLock );
+ break;
+ case MSG_MAKE_SHARED:
+ MSG_LOG(LOCKS,"%s:Converting MSG Database Lock to Shared...\n",idString);
+ RtlConvertExclusiveToShared( &MSG_DatabaseLock );
+ MSG_LOG(LOCKS,"%s:Converted MSG Database Lock to Shared\n",idString);
+ break;
+ case MSG_MAKE_EXCLUSIVE:
+ MSG_LOG(LOCKS,"%s:Converting MSG Database Lock to Exclusive...\n",idString);
+ RtlConvertSharedToExclusive( &MSG_DatabaseLock );
+ MSG_LOG(LOCKS,"%s:Converted MSG Database Lock to Exclusive\n",idString);
+ break;
+ default:
+ break;
+ }
+
+ return(status);
+}
diff --git a/private/net/svcdlls/msgsvc/server/display.c b/private/net/svcdlls/msgsvc/server/display.c
new file mode 100644
index 000000000..fbd8bfbbc
--- /dev/null
+++ b/private/net/svcdlls/msgsvc/server/display.c
@@ -0,0 +1,830 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ display.c
+
+Abstract:
+
+ This file contains functions that handle the displaying of messages.
+
+ Currently a message box is used to display messages. A message queueing
+ scheme has been setup so that the messenger worker threads can
+ call a function with a pointer to a message buffer. That message will
+ get copied into the queue so that the worker thread can go on gathering
+ more messages. When the display thread will be doing one of the following:
+ 1) Displaying a message - waiting for the user to press "ok".
+ 2) Sleeping - waiting for an event that will _tell it to _read the
+ message queue.
+
+ When the display thread completes displaying a message, it will check
+ the queue for the next message to display. If there are no further
+ messages, it will go to sleep until a message comes in.
+
+Author:
+
+ Dan Lafferty (danl) 24-Feb-1992
+
+Environment:
+
+ User Mode -Win32
+
+Notes:
+
+
+Revision History:
+
+ 04-Nov-1992 danl
+ MsgDisplayThread: Handle Extended Characters. This was done by
+ translating the Oem-style characters in the message to the unicode
+ equivalent, and then calling the Unicode version of the MessageBox Api.
+ It will still call the Ansi version of the MessageBox if the
+ string cannot be translated for some reason.
+
+ 26-Oct-1992 danl
+ MsgDisplayQueueAdd: Added Beep when message is added to queue.
+ Fixed bug where "if (status = TRUE)" caused the GlobalMsgDisplayEvent
+ to always be set.
+
+ 24-Feb-1992 danl
+ created
+
+--*/
+
+//
+// INCLUDES
+//
+#include "msrv.h"
+#include <msgdbg.h> // STATIC and MSG_LOG
+#include <string.h> // memcpy
+#include <winuser.h> // MessageBox
+#include "msgdata.h" // GlobalMsgDisplayEvent
+
+//
+// DEFINES
+//
+
+#define MAX_QUEUE_SIZE 5
+#define WAIT_FOREVER 0xffffffff
+
+//
+// Queue Entry Structure
+//
+typedef struct _QUEUE_ENTRY {
+ struct _QUEUE_ENTRY *Next;
+ DWORD BigTime;
+ CHAR Message[1];
+}QUEUE_ENTRY, *LPQUEUE_ENTRY;
+
+//
+// GLOBALS
+//
+
+ //
+ // This critical section serializes access to all the other globals.
+ //
+ STATIC CRITICAL_SECTION MsgDisplayCriticalSection;
+
+ //
+ // Used to wakeup the display thread if it was put to sleep due to
+ // not having a user desktop to display the message on.
+ //
+ STATIC HANDLE hGlobalDisplayEvent=NULL;
+
+ //
+ // These are the Display Queue pointers & counts.
+ //
+ STATIC LPQUEUE_ENTRY GlobalMsgQueueHead;
+ STATIC LPQUEUE_ENTRY GlobalMsgQueueTail;
+ STATIC DWORD GlobalMsgQueueCount = 0;
+
+ STATIC BOOL fGlobalInitialized=FALSE;
+
+ //
+ // This indicates whether there is a display thread already available that
+ // can service requests. If this is false, it means a new thread will
+ // need to be created.
+ //
+ STATIC HANDLE GlobalDisplayThread;
+
+//
+// Function Prototypes
+//
+
+
+STATIC BOOL
+MsgDisplayQueueRead(
+ OUT LPQUEUE_ENTRY *pQueueEntry
+ );
+
+STATIC DWORD
+MsgDisplayThread(
+ LPVOID parm
+ );
+
+STATIC VOID
+MsgMakeNewFormattedMsg(
+ LPSTR *ppHead,
+ LPSTR *ppTime,
+ LPSTR *ppBody,
+ DWORD BigTime
+ );
+
+
+BOOL
+MsgDisplayQueueAdd(
+ IN LPSTR pMsgBuffer,
+ IN DWORD MsgSize,
+ IN DWORD BigTime
+ )
+
+/*++
+
+Routine Description:
+
+ This function adds a Message to the display queue. If the queue is
+ full, the message is rejected.
+
+Arguments:
+
+ pMsgBuffer - This is a pointer to the buffer where the message is
+ stored. The message must be in the form of a pre-formatted
+ (with message header) NUL-terminated string of ansi characters.
+
+ MsgSize - Indicates the size (in bytes) of the message in the
+ message buffer, including the NUL terminator.
+
+ BigTime - This is a DWORD that indicates the time the message was
+ received.
+
+Return Value:
+
+ TRUE - The message was successfully stored in the queue.
+
+ FALSE - The message was rejected. Either the queue was full, or
+ there was not enough memory to store the message in the queue.
+
+
+--*/
+{
+ LPQUEUE_ENTRY pQueueEntry;
+ BOOL status;
+ DWORD threadId;
+
+ MSG_LOG(TRACE,"Adding a message to the display queue\n",0);
+
+ // ***************************
+ // **** LOCK QUEUE ACCESS ****
+ // ***************************
+ EnterCriticalSection(&MsgDisplayCriticalSection);
+
+ //
+ // Is there room for the message in the queue?
+ //
+
+ if (GlobalMsgQueueCount >= MAX_QUEUE_SIZE) {
+ MSG_LOG(TRACE,"DisplayQueueAdd: Max Queue Size Exceeded\n",0);
+ status = FALSE;
+ goto CleanExit;
+ }
+
+ //
+ // Allocate memory for the message in the queue.
+ //
+ pQueueEntry = (LPQUEUE_ENTRY)LocalAlloc(LMEM_FIXED, MsgSize + sizeof(QUEUE_ENTRY));
+
+ if (pQueueEntry == NULL) {
+ MSG_LOG(ERROR,"DisplayQueueAdd: Unable to allocate memory\n",0);
+ status = FALSE;
+ goto CleanExit;
+ }
+
+ //
+ // Copy the message into the queue entry.
+ //
+ pQueueEntry->Next = NULL;
+ memcpy(pQueueEntry->Message, pMsgBuffer, MsgSize);
+ pQueueEntry->BigTime = BigTime;
+
+ //
+ // Update the queue management pointer.
+ //
+
+ if (GlobalMsgQueueCount == 0) {
+ //
+ // There are no entries in the queue. So make the head
+ // and the tail equal.
+ //
+ GlobalMsgQueueTail = pQueueEntry;
+ GlobalMsgQueueHead = pQueueEntry;
+ }
+ else {
+ //
+ // Create the new Queue Tail and have the old tail's next pointer
+ // point to the new tail.
+ //
+ GlobalMsgQueueTail->Next = pQueueEntry;
+ GlobalMsgQueueTail = pQueueEntry;
+ }
+ GlobalMsgQueueCount++;
+ status = TRUE;
+
+ //
+ // If a display thread doesn't exist, then create one.
+ //
+ if (GlobalDisplayThread == NULL) {
+
+ hGlobalDisplayEvent = CreateEvent( NULL,
+ FALSE, // auto-reset
+ FALSE, // init to non-signaled
+ NULL );
+
+ GlobalDisplayThread = CreateThread (
+ NULL, // Thread Attributes
+ AD_STACK_SIZE, // StackSize
+ MsgDisplayThread, // lpStartAddress
+ (PVOID)NULL, // lpParameter
+ 0L, // Creation Flags
+ &threadId); // lpThreadId
+
+ if (GlobalDisplayThread == (HANDLE) NULL) {
+ //
+ // If we couldn't create the display thread, then we can't do
+ // much about it. Might as well leave the entry in the queue.
+ // Perhaps we can display it the next time around.
+ //
+ MSG_LOG(ERROR,"MsgDisplayQueueAdd:CreateThread FAILURE %ld\n",
+ GetLastError());
+
+ if (hGlobalDisplayEvent != NULL) {
+ CloseHandle(hGlobalDisplayEvent);
+ hGlobalDisplayEvent = NULL;
+ }
+ }
+ }
+
+CleanExit:
+
+ // *****************************
+ // **** UNLOCK QUEUE ACCESS ****
+ // *****************************
+ LeaveCriticalSection(&MsgDisplayCriticalSection);
+
+ //
+ // If we actually put something in the queue, then beep.
+ //
+ if (status == TRUE) {
+ MessageBeep(MB_OK);
+ }
+ return(status);
+}
+
+
+VOID
+MsgDisplayThreadWakeup()
+
+/*++
+
+Routine Description:
+
+ This function is called at shutdown, or for API requests. It causes
+ the display thread to wake up and read the queue again.
+
+ If the display thread cannot display the message because the MessageBox
+ call fails, then we assume it is because the user desktop is not avaiable
+ because the screensaver is on, or because the workstation is locked.
+ In this case, the display thread hangs around waiting for this
+ Event to get signalled. Winlogon calls one of the API entry points
+ in order to stimulate the display thread into action again.
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+{
+ // ***************************
+ // **** LOCK QUEUE ACCESS ****
+ // ***************************
+ EnterCriticalSection(&MsgDisplayCriticalSection);
+
+ if ( hGlobalDisplayEvent != (HANDLE)NULL ) {
+ SetEvent( hGlobalDisplayEvent );
+ }
+ // *****************************
+ // **** UNLOCK QUEUE ACCESS ****
+ // *****************************
+ LeaveCriticalSection(&MsgDisplayCriticalSection);
+}
+
+
+BOOL
+MsgDisplayInit(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ This function initializes everything having to do with the displaying
+ of messages. It does the following:
+
+ Initializes the Locks on global data
+ Creates event for display thread to wait on.
+ Starts the display thread that will read the msg queue.
+
+Arguments:
+
+ NONE
+
+Return Value:
+
+ Always TRUE.
+
+--*/
+{
+ MSG_LOG(TRACE,"Initializing the Message Display Code\n",0);
+ //
+ // Initialize the Critical Section that protects access to global data.
+ //
+
+ InitializeCriticalSection(&MsgDisplayCriticalSection);
+
+ GlobalMsgQueueHead = NULL;
+ GlobalMsgQueueTail = NULL;
+ GlobalMsgQueueCount = 0;
+
+ fGlobalInitialized = TRUE;
+
+ return(TRUE);
+}
+
+
+VOID
+MsgDisplayEnd(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ This function makes sure the Display Thread has completed its work,
+ and free's up all of its resources.
+
+ *** IMPORTANT ***
+ NOTE: This function should only be called when it is no longer possible
+ for the MsgDisplayQueueAdd function to get called.
+
+Arguments:
+
+ NONE.
+
+Return Value:
+
+ NONE.
+
+--*/
+{
+ LPQUEUE_ENTRY freeEntry;
+
+ if (!fGlobalInitialized) {
+ return;
+ }
+
+ // ***************************
+ // **** LOCK QUEUE ACCESS ****
+ // ***************************
+ EnterCriticalSection(&MsgDisplayCriticalSection);
+
+ if (GlobalDisplayThread != NULL) {
+ TerminateThread(GlobalDisplayThread,0);
+ CloseHandle( GlobalDisplayThread );
+ }
+
+ //
+ // To make sure a new thread won't be created...
+ //
+ GlobalDisplayThread = (HANDLE) 0xffffffff;
+
+ //
+ // Free memory in the queue
+ //
+ while(GlobalMsgQueueCount > 0) {
+
+ freeEntry = GlobalMsgQueueHead;
+ GlobalMsgQueueHead = GlobalMsgQueueHead->Next;
+ LocalFree(freeEntry);
+ GlobalMsgQueueCount--;
+
+ }
+ if (hGlobalDisplayEvent != NULL) {
+ CloseHandle(hGlobalDisplayEvent);
+ hGlobalDisplayEvent = NULL;
+ }
+ // *****************************
+ // **** UNLOCK QUEUE ACCESS ****
+ // *****************************
+ LeaveCriticalSection(&MsgDisplayCriticalSection);
+
+ DeleteCriticalSection(&MsgDisplayCriticalSection);
+
+ MSG_LOG(TRACE,"The Display has free'd resources and is terminating\n",0);
+}
+
+
+STATIC BOOL
+MsgDisplayQueueRead(
+ OUT LPQUEUE_ENTRY *pQueueEntry
+ )
+
+/*++
+
+Routine Description:
+
+ Pulls a display entry out of the display queue.
+
+Arguments:
+
+ pQueueEntry - This is a pointer to a location where a pointer to the
+ queue entry structure can be placed.
+
+Return Value:
+
+ TRUE - If an entry was found.
+ FALSE- If an entry wasn't found.
+
+Note on LOCKS:
+
+ The caller MUST hold the MsgDisplayCriticalSection Lock prior to calling
+ this function!!!
+
+
+--*/
+{
+ BOOL status;
+
+ //
+ // If there is data in the queue, then get the pointer to the queue
+ // entry from the queue head. Then decrement the queue count and
+ // set the queue head to the next entry (which could be zero if there
+ // are no more).
+ //
+ if (GlobalMsgQueueCount != 0) {
+ *pQueueEntry = GlobalMsgQueueHead;
+ GlobalMsgQueueCount--;
+ GlobalMsgQueueHead = (*pQueueEntry)->Next;
+ status = TRUE;
+ MSG_LOG(TRACE,"A message was read from the display queue\n",0);
+ }
+ else{
+ status = FALSE;
+ *pQueueEntry = NULL;
+ }
+
+ return(status);
+
+}
+
+
+STATIC DWORD
+MsgDisplayThread(
+ LPVOID parm
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+Note:
+
+ This worker thread expects that the critical section guarding the
+ global queue data is already initialized.
+
+
+--*/
+{
+ LPQUEUE_ENTRY pQueueEntry;
+ INT displayStatus;
+ DWORD msgrState;
+ UNICODE_STRING unicodeString;
+ OEM_STRING oemString;
+ NTSTATUS ntStatus;
+ BOOL unicodeFlag;
+ USHORT unicodeLength;
+ LPSTR pHead; // pointer to header portion of message
+ LPSTR pTime; // pointer to time portion of message
+ LPSTR pBody; // pointer to body of message (just after time)
+ DWORD BigTime;
+ BOOL MsgToRead=TRUE; // tells us whether or not to sleep.
+
+
+ UNREFERENCED_PARAMETER(parm);
+
+ pHead = NULL;
+ pQueueEntry = NULL;
+
+ do {
+
+ //
+ // If we are not currently working on displaying a message,
+ // then get a new message from the queue.
+ //
+ if (pHead == NULL) {
+
+ // ***************************
+ // **** LOCK QUEUE ACCESS ****
+ // ***************************
+ EnterCriticalSection(&MsgDisplayCriticalSection);
+
+ if (!MsgDisplayQueueRead(&pQueueEntry)) {
+
+ //
+ // No display entries in the queue. We can leave.
+ //
+ MsgToRead = FALSE;
+
+ CloseHandle(GlobalDisplayThread);
+ GlobalDisplayThread = NULL;
+
+ if (hGlobalDisplayEvent != NULL) {
+ CloseHandle(hGlobalDisplayEvent);
+ hGlobalDisplayEvent = NULL;
+ }
+
+ // *****************************
+ // **** UNLOCK QUEUE ACCESS ****
+ // *****************************
+ LeaveCriticalSection(&MsgDisplayCriticalSection);
+ //
+ // From this point on, we can't access any global
+ // variables.
+ //
+ }
+ else {
+ // *****************************
+ // **** UNLOCK QUEUE ACCESS ****
+ // *****************************
+ LeaveCriticalSection(&MsgDisplayCriticalSection);
+
+ //
+ // Process the entry.
+ //
+ BigTime = pQueueEntry->BigTime;
+ //
+ // Here we trash the pQueueEntry structure by pointing to the
+ // beginning and copying the message data starting at the
+ // first address. This is because MsgMakeNewFormattedMsg
+ // expects the message to begin at a address that can be
+ // released with LocalFree();
+ //
+ pHead = (LPSTR)pQueueEntry;
+ strcpy(pHead, pQueueEntry->Message);
+
+ pTime = strstr(pHead, GlobalTimePlaceHolder);
+ pBody = pTime+strlen(GlobalTimePlaceHolder);
+ if (pTime == NULL) {
+ pTime=pBody;
+ }
+ }
+ }
+ if (pHead != NULL) {
+ MsgMakeNewFormattedMsg(&pHead,&pTime,&pBody,BigTime);
+
+ unicodeFlag = TRUE;
+ //
+ // Convert the data from the OEM character set to the
+ // Unicode character set.
+ //
+ //
+ // Translate the oem string in the name table to a unicode name
+ //
+ RtlInitAnsiString( &oemString, pHead);
+
+ unicodeLength = oemString.Length * sizeof(WCHAR);
+
+ unicodeString.Buffer = (WCHAR *)LocalAlloc(
+ LMEM_ZEROINIT,
+ unicodeLength + sizeof(WCHAR));
+
+ if (unicodeString.Buffer == NULL) {
+
+ //
+ // Couldn't allocate for unicode buffer. Therefore we will
+ // display the message with the Ansi version of the
+ // message box API.
+ //
+ unicodeFlag = FALSE;
+ }
+ else {
+
+ unicodeString.Length = unicodeLength;
+ unicodeString.MaximumLength = unicodeLength + sizeof(WCHAR);
+
+ ntStatus = RtlOemStringToUnicodeString(
+ &unicodeString, // Destination
+ &oemString, // Source
+ FALSE); // Don't allocate the destination.
+
+ if (!NT_SUCCESS(ntStatus)) {
+ MSG_LOG(ERROR,
+ "MsgDisplayThread:RtlOemStringToUnicodeString Failed rc=%X\n",
+ ntStatus);
+ //
+ // Attempt to display with ANSI MessageBox
+ //
+ LocalFree(unicodeString.Buffer);
+ unicodeString.Buffer = NULL;
+ unicodeFlag = FALSE;
+ }
+ }
+
+ if (unicodeFlag) {
+
+ //
+ // Display the data in the QueueEntry as Unicode
+ //
+ MSG_LOG(TRACE,"Calling MessageBox (Unicode) Function\n",0);
+
+ displayStatus = MessageBoxW(
+ NULL,
+ unicodeString.Buffer,
+ GlobalMessageBoxTitle,
+ MB_OK | MB_SYSTEMMODAL | MB_SERVICE_NOTIFICATION |
+ MB_SETFOREGROUND | MB_DEFAULT_DESKTOP_ONLY);
+
+ if (displayStatus == 0) {
+ MSG_LOG1(TRACE,"MessageBox (unicode) Call failed %d\n",GetLastError());
+ WaitForSingleObject( hGlobalDisplayEvent, INFINITE );
+ }
+ else {
+ //
+ // Free up the data in the QueueEntry
+ //
+ LocalFree(pHead);
+ pHead = NULL;
+ }
+ LocalFree(unicodeString.Buffer);
+ unicodeString.Buffer = NULL;
+ unicodeFlag = FALSE;
+ }
+ else {
+ //
+ // Display the data in the QueueEntry as Ansi
+ //
+ MSG_LOG(TRACE,"Calling MessageBox (Ansi) Function\n",0);
+
+ displayStatus = MessageBoxA(
+ NULL,
+ pHead,
+ "Messenger Service",
+ MB_OK | MB_SYSTEMMODAL | MB_SERVICE_NOTIFICATION |
+ MB_SETFOREGROUND | MB_DEFAULT_DESKTOP_ONLY);
+
+ if (displayStatus == 0) {
+ MSG_LOG0(ERROR,"MessageBox (Ansi) Call failed\n");
+
+ WaitForSingleObject( hGlobalDisplayEvent, INFINITE );
+ }
+ else {
+ //
+ // Free up the data in the QueueEntry
+ // We only want to free this if the message was
+ // displayed.
+ //
+ LocalFree(pHead);
+ pHead = NULL;
+ }
+ }
+ }
+ msgrState = GetMsgrState();
+ }
+ while(MsgToRead && (msgrState != STOPPING) && (msgrState != STOPPED));
+
+ return(0);
+}
+
+
+STATIC VOID
+MsgMakeNewFormattedMsg(
+ LPSTR *ppHead,
+ LPSTR *ppTime,
+ LPSTR *ppBody,
+ DWORD BigTime
+ )
+
+/*++
+
+Routine Description:
+
+ This function returns a buffer containing an entire message that
+ consists of a single string of ansi (actually oem) characters.
+ Pointers to various areas (time and body) within this buffer are
+ also returned.
+
+ MEMORY MANAGEMENT NOTE:
+ *ppHead is expected to point to the top of the buffer. If the
+ message is reformatted, then this buffer will have been freed,
+ and a new buffer will have been allocated. It is expected that
+ the caller allocates the original buffer passed in, and that
+ the caller will free it when it is no longer needed.
+
+Arguments:
+
+ ppHead - Pointer to location that contains the pointer to the message
+ buffer.
+
+ ppTime - Pointer to location that contains the pointer to the time portion
+ of the message buffer.
+
+ ppBody - Pointer to location that immediately follows the time string.
+
+Return Value:
+
+ none. If this fails to allocate memory for the formatted message, then
+ the unformatted message should be displayed.
+
+--*/
+{
+ CHAR TimeBuf[NET_CTIME_FMT2_LEN+1];
+ DWORD BufSize;
+ LPSTR pTemp;
+ DWORD numChars;
+ LPSTR pOldHead;
+ time_t LocalTime;
+ struct tm TmTemp;
+
+ //
+ // Create a properly formatted time string.
+ //
+ NetpGmtTimeToLocalTime( (DWORD) BigTime, (LPDWORD) & LocalTime);
+ net_gmtime( &LocalTime, &TmTemp );
+
+ EnterCriticalSection(&TimeFormatCritSec);
+ NetpMakeTimeString(
+ &TmTemp,
+ &GlobalTimeFormat,
+ TimeBuf,
+ NET_CTIME_FMT2_LEN+1);
+ LeaveCriticalSection(&TimeFormatCritSec);
+
+ if (strncmp(TimeBuf, *ppTime, strlen(TimeBuf))==0) {
+ //
+ // If the newly formatted time string is the same as the existing
+ // time string, there is nothing to do so we just return.
+ //
+ MSG_LOG0(TRACE,"MsgMakeNewFormattedMsg: Time Format has not "
+ "changed - no update.\n");
+ return;
+ }
+
+ //
+ // Allocate a new message buffer
+ //
+ BufSize = strlen(TimeBuf) +
+ strlen(*ppHead) + 1 -
+ (*ppBody-*ppTime);
+
+ pTemp = LocalAlloc(LMEM_ZEROINIT, BufSize);
+ if (pTemp == NULL) {
+ MSG_LOG0(ERROR,"MsgMakeNewFormattedMsg: LocalAlloc failed\n");
+ return;
+ }
+ pOldHead = *ppHead;
+
+ //
+ // Copy the header of the message.
+ //
+ numChars = *ppTime-*ppHead;
+ strncpy(pTemp, *ppHead, numChars);
+ *ppHead = pTemp;
+
+ //
+ // Copy the time string
+ //
+ *ppTime = *ppHead+numChars;
+ strcpy(*ppTime,TimeBuf);
+
+ //
+ // Copy the Body of the message
+ //
+ pTemp = *ppBody;
+ *ppBody=*ppTime+strlen(*ppTime);
+ strcpy(*ppBody,pTemp);
+
+ LocalFree(pOldHead);
+ return;
+}
+
diff --git a/private/net/svcdlls/msgsvc/server/fmtncbna.c b/private/net/svcdlls/msgsvc/server/fmtncbna.c
new file mode 100644
index 000000000..fd277c0b5
--- /dev/null
+++ b/private/net/svcdlls/msgsvc/server/fmtncbna.c
@@ -0,0 +1,190 @@
+/*++
+
+Copyright (c) 1991-1992 Microsoft Corporation
+
+Module Name:
+
+ fmtncbna.c
+
+Abstract:
+
+ Contains a function for formatting a name NCB_style.
+
+Author:
+
+ Dan Lafferty (danl) 29-May-1991
+
+Environment:
+
+ User Mode -Win32
+
+Revision History:
+
+ 29-May-1991 danl
+ ported from LM2.0
+ 01-Oct-1991 danl
+ Working toward UNICODE.
+
+--*/
+#include <nt.h> // needed by tstring.h
+#include <windef.h> // needed by tstring.h
+#include <nt.h> // (Needed by <tstring.h>.)
+#include <windef.h> // (Needed by <tstring.h>.)
+#include <tstring.h> // STRLEN
+#include "msrv.h" // For prototype definitions
+#include "msgdbg.h" // MSG_LOG
+#include <netdebug.h> // NetpAssert
+#include <netlib.h> // UNUSED macro
+#include <netlibnt.h> // NetpNtStatusToApiStatus
+#include <icanon.h> // Canonicalization Routines
+
+
+
+NET_API_STATUS
+MsgFmtNcbName(
+ OUT PCHAR DestBuf,
+ IN LPTSTR Name,
+ IN DWORD Type)
+
+/*++
+
+Routine Description:
+
+ FmtNcbName - format a name NCB-style
+
+ Given a name, a name type, and a destination address, this
+ function copies the name and the type to the destination in
+ the format used in the name fields of a Network Control
+ Block.
+
+
+ SIDE EFFECTS
+
+ Modifies 16 bytes starting at the destination address.
+
+Arguments:
+
+ DestBuf - Pointer to the destination buffer.
+
+ Name - Unicode NUL-terminated name string
+
+ Type - Name type number (0, 3, 5, or 32) (3=NON_FWD, 5=FWD)
+
+
+
+Return Value:
+
+ NERR_Success - The operation was successful
+
+ Translated Return Code from the Rtl Translate routine.
+
+--*/
+
+ {
+ DWORD i; // Counter
+ NTSTATUS ntStatus;
+ NET_API_STATUS status;
+ OEM_STRING ansiString;
+ UNICODE_STRING unicodeString;
+ PCHAR pAnsiString;
+
+
+ //
+ // Force the name to be upper case.
+ //
+ status = NetpNameCanonicalize(
+ NULL,
+ Name,
+ Name,
+ STRSIZE(Name),
+ NAMETYPE_MESSAGEDEST,
+ 0);
+ if (status != NERR_Success) {
+ return(status);
+ }
+
+ //
+ // Convert the unicode name string into an ansi string - using the
+ // current locale.
+ //
+#ifdef UNICODE
+ unicodeString.Length = (USHORT)(STRLEN(Name)*sizeof(WCHAR));
+ unicodeString.MaximumLength = (USHORT)((STRLEN(Name)+1) * sizeof(WCHAR));
+ unicodeString.Buffer = Name;
+
+ ntStatus = RtlUnicodeStringToOemString(
+ &ansiString,
+ &unicodeString,
+ TRUE); // Allocate the ansiString Buffer.
+
+ if (!NT_SUCCESS(ntStatus)) {
+ MSG_LOG(ERROR,
+ "FmtNcbName:RtlUnicodeStringToOemString Failed rc=%X\n",
+ ntStatus);
+ //
+ // BUGBUG:
+ // What to do when translation fails?
+ // Translation Failed - use only the first 16 bytes?
+ // For now, I will just return an error.
+ //
+ // BUGBUG: Remove the assert when I have updated the calling routine
+ // to handle the error properly.
+ //
+ NetpAssert(NT_SUCCESS(ntStatus));
+
+ return(NetpNtStatusToApiStatus(ntStatus));
+ }
+
+ pAnsiString = ansiString.Buffer;
+ *(pAnsiString+ansiString.Length) = '\0';
+#else
+ UNUSED(ntStatus);
+ UNUSED(unicodeString);
+ UNUSED(ansiString);
+ pAnsiString = Name;
+#endif // UNICODE
+
+ //
+ // copy each character until a NUL is reached, or until NCBNAMSZ-1
+ // characters have been copied.
+ //
+ for (i=0; i < NCBNAMSZ - 1; ++i) {
+ if (*pAnsiString == '\0') {
+ break;
+ }
+
+ //
+ // Copy the Name
+ //
+
+ *DestBuf++ = *pAnsiString++;
+ }
+
+
+
+ //
+ // Free the buffer that RtlUnicodeStringToOemString created for us.
+ // NOTE: only the ansiString.Buffer portion is free'd.
+ //
+
+#ifdef UNICODE
+ RtlFreeOemString( &ansiString);
+#endif // UNICODE
+
+ //
+ // Pad the name field with spaces
+ //
+ for(; i < NCBNAMSZ - 1; ++i) {
+ *DestBuf++ = ' ';
+ }
+
+ //
+ // Set the name type.
+ //
+ NetpAssert( Type!=5 ); // 5 is not valid for NT.
+
+ *DestBuf = (CHAR) Type; // Set name type
+
+ return(NERR_Success);
+ }
+
diff --git a/private/net/svcdlls/msgsvc/server/grpmsngr.c b/private/net/svcdlls/msgsvc/server/grpmsngr.c
new file mode 100644
index 000000000..cc8d150a5
--- /dev/null
+++ b/private/net/svcdlls/msgsvc/server/grpmsngr.c
@@ -0,0 +1,352 @@
+
+/*--
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ grpmsngr.c
+
+Abstract:
+
+ This file contains the routines that provide support for messaging
+ in a multi-domaign lan. These routines comprise the group message thread
+ of the messenger service. The support of domain messaging is broken up
+ into two phases. The first, which takes place at initialization time of
+ the messenger, sets up a mailslot thru which the second phase receives the
+ information that it will need to process. The second, phase, which runs
+ in parallel with the existing messenger thread, reads messages out of this
+ mailslot and logs them.
+
+Author:
+
+ Dan Lafferty (danl) 17-Jul-1991
+
+Environment:
+
+ User Mode -Win32
+
+Notes:
+
+ These routines receive and manipulate ansi strings, not UNICODE strings.
+ The ANSI to UNICODE translation will be done in logsmb().
+
+Revision History:
+
+ 17-Jul-1991 danl
+ ported from LM2.0
+
+--*/
+
+#include "msrv.h"
+#include <string.h>
+#include <stdio.h>
+#include <netlib.h> // UNUSED macro
+
+#include "msgdbg.h" // MSG_LOG
+#include "msgdata.h"
+
+//
+// GLOBALS
+//
+ LPSTR DieMessage = "DIE";
+
+//
+// PROTOTYPES of internal functions
+//
+
+
+STATIC VOID
+MsgDisectMessage(
+ IN LPSTR message,
+ OUT LPSTR *from,
+ OUT LPSTR *to,
+ IN LPSTR text);
+
+
+//
+// Defines
+//
+
+
+//
+// Size of mailslot messages (bytes)
+//
+#define MESSNGR_MS_MSIZE 512
+
+//
+// size of mailslot (bytes)
+//
+#define MESSNGR_MS_SIZE (5*MESSNGR_MS_MSIZE)
+
+
+
+static char Msg_Buf[MESSNGR_MS_MSIZE]; // Buffer for messages
+
+static HANDLE GrpMailslotHandle;
+
+static OVERLAPPED Overlapped;
+
+static DWORD bytes_read = 0;
+
+
+NET_API_STATUS
+MsgInitGroupSupport(DWORD iGrpMailslotWakeupSem)
+{
+ DWORD err = 0; // Error code info from the group processor.
+
+ GrpMailslotHandle = CreateMailslotA(
+ MESSNGR_MS_NAME, // lpName
+ MESSNGR_MS_MSIZE, // nMaxMessageSize
+ MAILSLOT_WAIT_FOREVER, // lReadTimeout
+ NULL); // lpSecurityAttributes
+
+ if (GrpMailslotHandle == (HANDLE)-1) {
+ err = GetLastError();
+ MSG_LOG(ERROR,"GroupMsgProcessor: CreateMailslot FAILURE %d\n",
+ err);
+ }
+ else {
+ MSG_LOG1(GROUP,"InitGroupSupport: MailSlotHandle = 0x%lx\n",
+ GrpMailslotHandle);
+ wakeupSem[iGrpMailslotWakeupSem] = GrpMailslotHandle;
+ }
+
+ return err;
+}
+
+NET_API_STATUS
+MsgReadGroupMailslot(
+ PVOID pData,
+ DWORD dwWaitStatus)
+{
+ NET_API_STATUS Err = 0;
+
+ UNREFERENCED_PARAMETER(pData);
+ UNREFERENCED_PARAMETER(dwWaitStatus);
+ //
+ // Clean out receive buffers before each message
+ //
+ memset(Msg_Buf, 0, sizeof(Msg_Buf));
+ memset(&Overlapped, 0, sizeof(Overlapped));
+
+ if (!ReadFile(
+ GrpMailslotHandle,
+ Msg_Buf,
+ sizeof(Msg_Buf),
+ &bytes_read,
+ &Overlapped) ) {
+
+ Err = GetLastError();
+
+ if (Err == ERROR_INVALID_HANDLE) {
+ //
+ // BUGBUG: Eventlog.
+ // If this handle is no longer good, it means the
+ // mailslot system is down. So we can't go on using
+ // mailslots in the messenger. Therefore, we want to
+ // log that fact and shutdown this thread.
+ //
+ MsgErrorLogWrite(
+ Err, // Error Code
+ SERVICE_MESSENGER, // Component
+ NULL, // Buffer
+ 0L, // BufferSize
+ NULL, // Insertion strings
+ 0); // NumStrings
+ }
+ }
+ if (Err == ERROR_IO_PENDING) {
+ return(NO_ERROR);
+ }
+ return Err;
+}
+
+NET_API_STATUS
+MsgServeGroupMailslot()
+{
+ LPSTR from;
+ LPSTR to;
+ CHAR text[MAXGRPMSGLEN+3]; // +3 is for length word at
+ // start of string (for
+ // logsbm) and for NULL
+ // terminator at end. NOTE:
+ // disect_message() below
+ // makes assumptions about
+ // the length of this array.
+
+ //
+ // Process the message
+ //
+ if( !GetOverlappedResult( GrpMailslotHandle,
+ &Overlapped,
+ &bytes_read,
+ TRUE ) ) {
+ MSG_LOG1(ERROR,"MsgServeGroupMailslot: GetOverlappedResult failed %d\n",
+ GetLastError());
+ return(RUNNING);
+ }
+
+ //
+ // Check for Shutdown...
+ //
+ if ((bytes_read == 4) && (strcmp(Msg_Buf, DieMessage)==0)) {
+ if (GetMsgrState() == STOPPING) {
+ return(STOPPING);
+ }
+ }
+
+ MsgDisectMessage( Msg_Buf, &from, &to, text );
+
+ MSG_LOG(TRACE,"MailSlot Message Received\n",0);
+
+ Msglogsbm (from, to, text);
+
+ return(RUNNING);
+}
+
+
+/* Function: MsgDisectMessage
+ *
+ * This function isolates the details of the structure of the message
+ * that gets sent through the mailslot from the rest of the thread. Given
+ * the message buffer, this routine fills in the module globals From, To
+ * and Text with the proper parts of the message.
+ *
+ * ENTRY
+ *
+ * Expects one argument, which is a pointer to the buffer containing
+ * the message.
+ *
+ * EXIT
+ *
+ * This function does not return a value.
+ *
+ * SIDE EFFECTS
+ *
+ * Modifies the variables from, to and text. The Msg_Buf
+ * may also be modified.
+ * Assumes the length of Text is at least MAXGRPMSGLEN+3.
+ *
+ */
+
+
+VOID
+MsgDisectMessage(
+ IN LPSTR message,
+ OUT LPSTR *from,
+ OUT LPSTR *to,
+ IN LPSTR text)
+{
+
+ LPSTR txt_ptr;
+ PSHORT size_ptr;
+
+ *from = message;
+
+ *to = (*from) + strlen(*from) +1;
+
+ txt_ptr = (*to) + strlen(*to) +1;
+
+ text[2] = '\0';
+
+ strncpy(text+2, txt_ptr, MAXGRPMSGLEN);
+
+ //
+ // make sure it is NULL terminated
+ //
+
+ text[MAXGRPMSGLEN+2] = '\0';
+
+ //
+ // The first two bytes in the text buffer are to contain the length
+ // the message. (in bytes).
+ //
+ size_ptr = (PSHORT)text;
+ *size_ptr = (SHORT)strlen(text+2);
+
+}
+
+VOID
+MsgGrpThreadShutdown(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ This routine wakes up the wait on the Group Mailslot handle.
+
+Arguments:
+
+ none
+
+Return Value:
+
+ none
+
+--*/
+{
+ DWORD i;
+ DWORD numWritten;
+ HANDLE mailslotHandle;
+
+ //
+ // Wake up the Group Thread by sending "DIE" to its mailbox.
+ //
+
+ MSG_LOG(TRACE,"MsgThreadWakeup:Wake up Group Thread & tell it to DIE\n",0);
+
+ mailslotHandle = CreateFileA (
+ MESSNGR_MS_NAME, // lpFileName
+ GENERIC_WRITE, // dwDesiredAccess
+ FILE_SHARE_WRITE | FILE_SHARE_READ, // dwShareMode
+ NULL, // lpSecurityAttributes
+ OPEN_EXISTING, // dwCreationDisposition
+ FILE_ATTRIBUTE_NORMAL, // dwFileAttributes
+ 0L); // hTemplateFile
+
+ if (mailslotHandle == (HANDLE)-1) {
+ //
+ // A failure occured. It is assumed that the mailslot hasn't
+ // been created yet. In which case, the GrpMessageProcessor will
+ // check the MsgrState directly after it creates the Mailslot and
+ // will shut down as required.
+ //
+ MSG_LOG(TRACE,"MsgThreadWakeup: CreateFile on Mailslot Failed %d\n",
+ GetLastError());
+ return;
+ }
+
+ MSG_LOG(TRACE,"MsgGroupThreadShutdown: MailSlotHandle = 0x%lx\n",mailslotHandle);
+ if ( !WriteFile (
+ mailslotHandle,
+ DieMessage,
+ strlen(DieMessage)+1,
+ &numWritten,
+ NULL)) {
+
+ MSG_LOG(TRACE,"MsgThreadWakeup: WriteFile on Mailslot Failed %d\n",
+ GetLastError())
+ }
+
+ CloseHandle(mailslotHandle);
+
+ //
+ // Wait for the group messenger to be shutdown.
+ // We will wait up to 20.300 seconds for this before going on.
+ //
+ Sleep(300);
+ for (i=0; i<20; i++) {
+ if (wakeupSem[SD_NUMNETS()] == NULL) {
+ MSG_LOG0(TRACE,"MsgGroupThreadShutdown: Group Thread has completed\n");
+ break;
+ }
+ Sleep(1000);
+ }
+
+ return;
+}
+
+
diff --git a/private/net/svcdlls/msgsvc/server/heap.c b/private/net/svcdlls/msgsvc/server/heap.c
new file mode 100644
index 000000000..75fa132e1
--- /dev/null
+++ b/private/net/svcdlls/msgsvc/server/heap.c
@@ -0,0 +1,187 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ heap.c
+
+Abstract:
+
+ Contains a heap allocator function.
+
+Author:
+
+ Dan Lafferty (danl) 10-Jul-1991
+
+Environment:
+
+ User Mode - Win32
+
+Revision History:
+
+ 10-Jul-1991 danl
+ Ported from LM2.0
+
+--*/
+
+// static char *SCCSID = "@(#)heap.c 9.1 86/10/12";
+//
+// Simple heap allocator for small heaps in shared memory areas.
+//
+
+#include <windef.h> // USHORT definitions
+#include <heap.h> // Constants, macros, etc.
+#include <align.h> // ROUND_UP_COUNT
+
+ LPBYTE heap = 0; // Pointer to start of heap
+ DWORD heapln = 0; // Length of heap
+
+
+/*
+ * Msgheapalloc - simple heap allocator
+ *
+ * This function allocates blocks out of a far heap.
+ * It assumes that when it is called the static variable
+ * heap points to the far heap and the static variable heapln
+ * contains the length of the far heap.
+ *
+ * Msgheapalloc (cb)
+ *
+ * ENTRY
+ * cb - number of bytes to allocate including header
+ *
+ * RETURN
+ * index in far heap to start of block of length cb, or
+ * INULL if no such block can be found or if cb < sizeof(HEAPHDR).
+ *
+ * This function maintains a heap in which all
+ * blocks are implicitly linked. The header of a block is
+ * three bytes long. It contains the size of the block including
+ * the header and a one-byte flag which indicates whether the block
+ * is allocated or not. Any non-zero value indicates that a block
+ * is allocated. Note: Msgheapalloc() does NOT set the flag when it
+ * returns a block. It is up to the caller to mark a block as
+ * allocated. Unlike most heap allocators, Msgheapalloc() returns a
+ * pointer (index) to the header rather than just past the header.
+ * It does this because the message logging routines will need to
+ * know the lengths of blocks they process. Also, in addition to
+ * indicating that a block is allocated, the flag byte will be used
+ * to indicate the type of the block (i.e. single block message,
+ * multi-block message header, etc.). Since the logging routines
+ * will use the size of a block, it must be exactly the size
+ * requested.
+ *
+ * The algorithm used was chosen to minimize the size of the
+ * heap managing routines and to conform to the requirements
+ * of the logging routines.
+ *
+ * SIDE EFFECTS
+ *
+ * Changes the structure of the heap.
+ */
+
+DWORD
+Msgheapalloc(
+ IN DWORD NumBytes // No. of bytes to allocate
+ )
+{
+ DWORD i; // Index to return
+ DWORD newi; // New block index
+ DWORD nexti; // Next block index
+ DWORD numBytesNew; // New block size
+
+ //
+ // Must request at least siz bytes
+ //
+ if(NumBytes < sizeof(HEAPHDR)) {
+ return(INULL);
+ }
+
+ //
+ // *ALIGNMENT*
+ // If necessary, increase the requested size to cause the allocated
+ // block to fall on a 4-byte aligned boundary.
+ //
+
+ NumBytes = ROUND_UP_COUNT(NumBytes,4);
+
+ //
+ // This loop is used to traverse the heap by following the
+ // chain of blocks until either the end of the heap is reached
+ // or a free block of suitable size is found. Coalescing of
+ // adjacent free blocks is performed herein also.
+ //
+ //
+ // Loop to allocate block
+ //
+ for(i = 0; i < heapln; i += HP_SIZE(*HPTR(i))) {
+ //
+ // If free block found (hp_flag=0 indicates free),
+ //
+ if(HP_FLAG(*HPTR(i)) == 0) {
+ //
+ // A free block was found.
+ // At this point, check to see if the current block can be
+ // coalesced with the next block. We start with the offset of
+ // the current block.
+
+ nexti = i;
+
+ //
+ // Add to it the size of the next consecutive
+ // free blocks until we reach the end of the heap, or an
+ // allocated block is found.
+ //
+ while( (nexti < heapln) && (HP_FLAG(*HPTR(nexti))==0) ) {
+ nexti += HP_SIZE(*HPTR(nexti));
+ }
+
+ //
+ // Coalesce blocks all free blocks found thus far
+ //
+ HP_SIZE(*HPTR(i)) = nexti - i;
+
+ //
+ // At this point, attempt to allocate from the current
+ // free block. The current free block must be exactly
+ // the size we want or large enough to split, since we
+ // must return a block whose size is EXACTLY the size
+ // requested.
+ //
+ if(HP_SIZE(*HPTR(i)) == NumBytes) {
+ //
+ // Size is perfect
+ //
+ return(i);
+ }
+
+ if(HP_SIZE(*HPTR(i)) >= NumBytes + sizeof(HEAPHDR)) {
+ //
+ // If block is splittable, then get the index and size of
+ // the block that is left over after taking out what is
+ // needed from this allocate request.
+ //
+ newi = i + NumBytes;
+ numBytesNew = HP_SIZE(*HPTR(i)) - NumBytes;
+
+ //
+ // Create a header for the left-over block by marking
+ // it as free, and inserting the size.
+ //
+ HP_SIZE(*HPTR(newi)) = numBytesNew;
+ HP_FLAG(*HPTR(newi)) = 0;
+
+ //
+ // Update the header for the allocated block and
+ // return its index to the caller.
+ // NOTE: The caller is responsible for marking this block
+ // as allocated.
+ //
+ HP_SIZE(*HPTR(i)) = NumBytes;
+ return(i);
+ }
+ }
+ }
+ return(INULL); // Heap full
+}
diff --git a/private/net/svcdlls/msgsvc/server/heap.h b/private/net/svcdlls/msgsvc/server/heap.h
new file mode 100644
index 000000000..d5d2515c8
--- /dev/null
+++ b/private/net/svcdlls/msgsvc/server/heap.h
@@ -0,0 +1,54 @@
+/********************************************************************/
+/** Microsoft LAN Manager **/
+/** Copyright(c) Microsoft Corp., 1987-1990 **/
+/********************************************************************/
+
+#ifndef _HEAP_INCLUDED
+#define _HEAP_INCLUDED
+
+//static char *SCCSID = "@(#)heap.h 1.1 85/10/09";
+//
+// Shared Memory Heap Allocator include file
+//
+
+
+//
+// Constant definitions
+//
+#define INULL ((DWORD) -1)
+
+//
+// Structure and macro definitions
+//
+
+//
+// Heap Block Header
+//
+typedef struct blk {
+ DWORD hp_size; // Size of block incl. header
+ DWORD hp_flag; // Allocation flag
+}HEAPHDR, *PHEAPHDR, *LPHEAPHDR;
+
+#define HP_SIZE(x) (x).hp_size
+#define HP_FLAG(x) (x).hp_flag
+#define HPTR(x) ((LPHEAPHDR) &heap[(x)])
+#define CPTR(x) (&heap[(x)])
+#define Msgheapfree(x) HP_FLAG(*HPTR(x)) = 0
+
+//
+// Data
+//
+extern LPBYTE heap; // Pointer to start of heap
+extern DWORD heapln; // Length of heap
+
+//
+// Functions
+//
+
+DWORD
+Msgheapalloc(
+ IN DWORD NumBytes
+ );
+
+
+#endif // _HEAP_INCLUDED
diff --git a/private/net/svcdlls/msgsvc/server/makefile b/private/net/svcdlls/msgsvc/server/makefile
new file mode 100644
index 000000000..f0db8e4a7
--- /dev/null
+++ b/private/net/svcdlls/msgsvc/server/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS LINE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/net/svcdlls/msgsvc/server/meslog.c b/private/net/svcdlls/msgsvc/server/meslog.c
new file mode 100644
index 000000000..7dc382f28
--- /dev/null
+++ b/private/net/svcdlls/msgsvc/server/meslog.c
@@ -0,0 +1,869 @@
+/********************************************************************/
+/** Microsoft LAN Manager **/
+/** Copyright(c) Microsoft Corp., 1987-1992 **/
+/********************************************************************/
+
+
+/*
+** Routines to log messages
+**
+** If message logging is off, all messages are buffered. Further,
+** even if messages are being logged, multi-block messages must
+** be buffered since they must be spooled to the logging file or
+** device. Since there is only one message buffer in which to
+** buffer all messages, this buffer must be managed as a heap.
+** Also, messages are logged in a first-in-first-out manner,
+** so messages in the buffer must be kept in a queue. In order
+** to meet these goals, the following message blocks are defined:
+**
+** SBM - single-block message
+**
+** length - length of entire block (2 bytes)
+** code - identifies block as single-block message (1 byte)
+** link - link to next message in message queue (2 bytes)
+** date - date message received (2 bytes)
+** time - time message received (2 bytes)
+** from - name of sender (null-terminated string)
+** to - name of recipient (null-terminated string)
+** text - text of message (remainder of block)
+**
+** MBB - multi-block message header
+**
+** length - length of entire block (2 bytes)
+** code - identifies block as multi-block message header (1 byte)
+** link - link to next message in message queue (2 bytes)
+** date - date message received (2 bytes)
+** time - time message received (2 bytes)
+** btext - link to last text block (2 bytes)
+** ftext - link to first text block (2 bytes)
+** error - error flag (1 byte)
+** from - name of sender (null-terminated string)
+** to - name of recipient (null-terminated string)
+**
+** MBT - multi-block message text block
+**
+** length - length of entire block (2 bytes)
+** code - identifies block a multi-block message text (1 byte)
+** link - link to next text block (2 bytes)
+** text - text of message (remainder of block)
+**/
+
+//
+// Includes
+//
+
+#include "msrv.h"
+
+#include <string.h> // memcpy
+#include <tstring.h> // Unicode string macros
+#include <netdebug.h> // NetpAssert
+
+#include <lmalert.h> // Alert stuff BUGBUG: This is temporary
+
+#include <netlib.h> // UNUSED macro
+#include <netlibnt.h> // NetpNtStatusToApiStatus
+#include <netdebug.h> // NetpDbgHexDump
+#include <smbtypes.h> // needed for smb.h
+#include <smb.h> // Server Message Block definitions
+#include <lmerrlog.h> // NELOG_ messages
+#include <timelib.h> // time_now()
+#include <smbgtpt.h> // SMB field manipulation macros
+
+#include <winuser.h> // MessageBox
+
+#include "msgdbg.h" // MSG_LOG
+#include "msgdata.h"
+
+//
+// Defines for Hex Dump Function
+//
+#ifndef MIN
+#define MIN(a,b) ( ( (a) < (b) ) ? (a) : (b) )
+#endif
+
+#define DWORDS_PER_LINE 4
+#define BYTES_PER_LINE (DWORDS_PER_LINE * sizeof(DWORD))
+#define SPACE_BETWEEN_BYTES NetpKdPrint((" "))
+#define SPACE_BETWEEN_DWORDS NetpKdPrint((" "))
+//
+// Local Functions
+//
+
+NET_API_STATUS
+MsgOutputMsg (
+ USHORT AlertLength,
+ LPSTR AlertBuffer,
+ DWORD BigTime
+ );
+
+#if DBG
+VOID
+MsgDbgHexDumpLine(
+ IN LPBYTE StartAddr,
+ IN DWORD BytesInThisLine
+ );
+
+VOID
+MsgDbgHexDump(
+ IN LPBYTE StartAddr,
+ IN DWORD Length
+ );
+#endif //DBG
+
+//
+// Data
+//
+
+PSTD_ALERT alert_buf_ptr; // Pointer to DosAlloc'ed alert buffer
+USHORT alert_len; // Currently used length of alert buffer
+
+//
+// Defines
+//
+#define ERROR_LOG_SIZE 1024
+
+
+/*
+** Msglogmbb - log a multi-block message header
+**
+** This function is called to log a multi-block message header.
+** The message header is placed in the message buffer which resides
+** in the shared data area.
+**
+** This function stores the from and to information in the shared data
+** buffer and initializes the multi-block message header. Then it puts
+** a pointer to the multi-block header into the shared data pointer
+** location for that net index and name index.
+**
+** logmbb (from, to, net, ncbi)
+**
+** ENTRY
+** from - sender name
+** to - recipient name
+** net - network index
+** ncbi - Network Control Block index
+**
+** RETURN
+** zero if successful, non-zero if unable to buffer the message header
+**
+** SIDE EFFECTS
+**
+** Calls heapalloc() to obtain buffer space.
+**/
+
+DWORD
+Msglogmbb(
+ LPSTR from, // Name of sender
+ LPSTR to, // Name of recipient
+ DWORD net, // Which network ?
+ DWORD ncbi // Network Control Block index
+ )
+
+{
+ DWORD i; // Heap index
+ LPSTR fcp; // Far character pointer
+
+ //
+ // Block until the shared database is free
+ //
+ MsgDatabaseLock(MSG_GET_EXCLUSIVE,"logmbb");
+
+ //
+ // Allocate space for header
+ //
+ i = Msgheapalloc(sizeof(MBB) + strlen(from) + strlen(to) + 2);
+
+ if(i == INULL) { // If no buffer space
+ //
+ // Unlock the shared database
+ //
+
+ MsgDatabaseLock(MSG_RELEASE,"logmbb");
+
+ return((int) i); // Log fails
+ }
+
+ //
+ // Multi-block message
+ //
+ MBB_CODE(*MBBPTR(i)) = SMB_COM_SEND_START_MB_MESSAGE;
+ MBB_NEXT(*MBBPTR(i)) = INULL; // Last message in buffer
+ MBB_BIGTIME(*MBBPTR(i)) = time_now(); // Time of message
+ MBB_BTEXT(*MBBPTR(i)) = INULL; // No text yet
+ MBB_FTEXT(*MBBPTR(i)) = INULL; // No text yet
+ MBB_STATE(*MBBPTR(i)) = MESCONT; // Message in progress
+ fcp = CPTR(i + sizeof(MBB)); // Get far pointer into buffer
+ strcpy(fcp, from); // Copy the sender name
+ fcp += strlen(from) + 1; // Increment pointer
+ strcpy(fcp, to); // Copy the recipient name
+ SD_MESPTR(net,ncbi) = i; // Save index to this record
+
+ //
+ // Unlock the shared database
+ //
+
+ MsgDatabaseLock(MSG_RELEASE,"logmbb");
+
+ return(0); // Message logged successfully
+}
+
+/*
+** Msglogmbe - log end of a multi-block message
+**
+** This function is called to log a multi-block message end.
+** The message is marked as finished, and if logging is enabled,
+** an attempt is made to write the message to the log file. If
+** this attempt fails, or if logging is disabled, then the message
+** is placed in the message queue in the message buffer.
+**
+** The message is gathered up and placed in the alert buffer and an alert
+** is raised.
+**
+** logmbe (state, net,ncbi)
+**
+** ENTRY
+** state - final state of message
+** net - Network index
+** ncbi - Network Control Block index
+**
+** RETURN
+** int - BUFFERED if the message is left in the buffer
+** int - LOGGED if the message is written to the log file
+**
+** FOR NT:
+** SMB_ERR_SUCCESS - success in alerting
+** SMB_ERR_... - an error occured
+**
+**
+**
+** SIDE EFFECTS
+**
+** Calls mbmprint() to print the message if logging is enabled. Calls
+** mbmfree() to free the message if logging succeeds.
+**/
+
+UCHAR
+Msglogmbe(
+ DWORD state, // Final state of message
+ DWORD net, // Which network?
+ DWORD ncbi // Network Control Block index
+ )
+{
+ DWORD i; // Heap index
+ DWORD error; // Error code
+ DWORD meslog; // Message logging status
+ DWORD alert_flag; // Alert buffer allocated flag
+ DWORD status; // Dos error for error log
+ DWORD bufSize; // Buffer Size
+ UCHAR retcode; // Return code
+ DWORD bigtime; // Date and time of message
+
+ //
+ // Block until the shared database is free
+ //
+ MsgDatabaseLock(MSG_GET_EXCLUSIVE,"logmbe");
+
+ //
+ // First get a buffer for an alert
+ //
+
+ bufSize = sizeof( STD_ALERT) +
+ ALERT_MAX_DISPLAYED_MSG_SIZE +
+ (2*TXTMAX) + 2;
+
+ alert_buf_ptr = (PSTD_ALERT)LocalAlloc(LMEM_ZEROINIT, bufSize);
+
+ if (alert_buf_ptr == NULL) {
+ MSG_LOG(ERROR,"logmbe:Local Alloc failed\n",0);
+ alert_flag = 0xffffffff; // No alerting if Alloc failed
+ }
+ else {
+ alert_flag = 0; // File and alerting
+ alert_len = 0;
+
+ }
+
+ error = 0; // Assume no error
+ i = SD_MESPTR(net,ncbi); // Get index to message header
+ MBB_STATE(*MBBPTR(i)) = state; // Record final state
+
+ //
+ // If logging now disabled ...
+ //
+
+ if(!SD_MESLOG()) {
+ if( alert_flag == 0) {
+
+ //
+ // Format the message and put it in the alert buffer.
+ //
+ Msgmbmprint(1,i,0); // Alert only
+ }
+ }
+
+ //
+ // Add message to buffer queue if logging is off,
+ // or if the attempt to log the message failed.
+ //
+
+ meslog = SD_MESLOG(); // Get logging status
+
+ if(!meslog) { // If logging disabled
+ Msgmbmfree(i);
+ retcode = SMB_ERR_SUCCESS; // Not really, but only code which works
+ }
+
+ if(error != 0) {
+
+ //
+ // Report to error log
+ //
+
+ NetpAssert(0); // NT code should never get here.
+
+ MsgErrorLogWrite(
+ error,
+ SERVICE_MESSENGER,
+ (LPBYTE)&status,
+ sizeof(DWORD),
+ NULL,
+ 0);
+ }
+
+ //
+ // Now alert and free up alert buffer if it was successfully allocated
+ //
+
+ if( alert_flag == 0) {
+ //
+ // There is an alert buffer, output it.
+ //
+ bigtime = time_now(); // Get the time
+ MsgOutputMsg(alert_len, (LPSTR)alert_buf_ptr,bigtime);
+
+ LocalFree(alert_buf_ptr);
+ }
+
+ //
+ // Unlock the shared database
+ //
+
+ MsgDatabaseLock(MSG_RELEASE,"logmbe");
+
+ return(retcode); // Message arrived
+}
+
+/*
+** Msglogmbt - log a multi-block message text block
+**
+** This function is called to log a multi-block message text block.
+** The text block is placed in the message buffer which resides
+** in the shared data area. If there is insufficient room in the
+** buffer, logmbt() removes the header and any previous blocks of
+** the message from the buffer.
+**
+** This function gets the current message from the message pointer in
+** the shared data (for that net & name index). It looks in the header
+** to see if there are any text blocks already there. If so, it adds
+** this new one to the list and fixes the last block pointer to point to
+** it.
+**
+** logmbt (text, net, ncbi)
+**
+** ENTRY
+** text - text header
+** net - Network index
+** ncbi - Network Control Block index
+**
+** RETURN
+** zero if successful, non-zero if unable to buffer the message header
+**
+** SIDE EFFECTS
+**
+** Calls heapalloc() to obtain buffer space. Calls mbmfree() if a call to
+** heapalloc() fails.
+**/
+
+DWORD
+Msglogmbt(
+ LPSTR text, // Text of message
+ DWORD net, // Which network?
+ DWORD ncbi // Network Control Block index
+ )
+{
+ DWORD i; // Heap index
+ DWORD j; // Heap index
+ DWORD k; // Heap index
+ USHORT length; // Length of text
+
+ // *ALIGNMENT*
+ length = SmbGetUshort( (PUSHORT)text); // Get length of text block
+// length = *((PSHORT) text); // Get length of text block
+ text += sizeof(short); // Skip over length word
+
+ //
+ // Block until the shared database is free
+ //
+
+ MsgDatabaseLock(MSG_GET_EXCLUSIVE,"logmbt");
+
+ i = Msgheapalloc(sizeof(MBT) + length); // Allocate space for block
+
+ //
+ // If buffer space is available
+ //
+
+ if(i != INULL) {
+
+ //
+ // Multi-block message text
+ //
+ MBT_CODE(*MBTPTR(i)) = SMB_COM_SEND_TEXT_MB_MESSAGE;
+
+ MBT_NEXT(*MBTPTR(i)) = INULL; // Last text block so far
+
+ MBT_COUNT(*MBTPTR(i)) = (DWORD)length; // *ALIGNMENT2*
+
+ memcpy(CPTR(i + sizeof(MBT)), text, length);
+
+ // Copy text into buffer
+ j = SD_MESPTR(net, ncbi); // Get index to current message
+
+ if(MBB_FTEXT(*MBBPTR(j)) != INULL) {
+ //
+ // If there is text already, Get pointer to last block and
+ // add new block
+ //
+ k = MBB_BTEXT(*MBBPTR(j)); // Get pointer to last block
+ MBT_NEXT(*MBTPTR(k)) = i; // Add new block
+ }
+ else {
+ MBB_FTEXT(*MBBPTR(j)) = i; // Else set front pointer
+ }
+
+ MBB_BTEXT(*MBBPTR(j)) = i; // Set back pointer
+ i = 0; // Success
+ }
+ else {
+ Msgmbmfree(SD_MESPTR(net,ncbi)); // Else deallocate the message
+ }
+
+ //
+ // Unlock the shared database
+ //
+
+ MsgDatabaseLock(MSG_RELEASE,"logmbt");
+
+ return((int) i); // Return status
+}
+
+/*
+** Msglogsbm - log a single-block message
+**
+** This function is called to log a single-block message. If
+** logging is enabled, the message is written directly to the
+** logging file or device. If logging is disabled or if the
+** attempt to log the message fails, the message is placed in
+** the message buffer which resides in the shared data area.
+**
+** logsbm (from, to, text)
+**
+** ENTRY
+** from - sender name
+** to - recipient name
+** text - text of message
+**
+** RETURN
+** zero if successful, non-zero if unable to log the message
+**
+** SIDE EFFECTS
+**
+** Calls hdrprint(), txtprint(), and endprint() to print the message if
+** logging is enabled. Calls heapalloc() to obtain buffer space if
+** the message must be buffered.
+**/
+
+DWORD
+Msglogsbm(
+ LPSTR from, // Name of sender
+ LPSTR to, // Name of recipient
+ LPSTR text // Text of message
+ )
+{
+ DWORD i; // Heap index
+ DWORD error; // Error code
+ SHORT length; // Length of text
+ DWORD meslog; // Message logging status
+ DWORD alert_flag; // Alert buffer allocated flag
+ DWORD status; // DOS error from mespeint functions
+ DWORD bigtime; // Date and time of message
+ DWORD bufSize; // Buffer Size
+
+ //
+ // Block until the shared database is free
+ //
+ MsgDatabaseLock(MSG_GET_EXCLUSIVE,"logsbm");
+
+ //
+ // First get a buffer for an alert
+ //
+
+ bufSize = sizeof( STD_ALERT) +
+ ALERT_MAX_DISPLAYED_MSG_SIZE +
+ (2*TXTMAX) + 2;
+
+ alert_buf_ptr = (PSTD_ALERT)LocalAlloc(LMEM_ZEROINIT, bufSize);
+
+ if (alert_buf_ptr == NULL) {
+ MSG_LOG(ERROR,"Msglogsbm:Local Alloc failed\n",0);
+ alert_flag = 0xffffffff; // No alerting if Alloc failed
+ }
+ else {
+ alert_flag = 0; // File and alerting
+ alert_len = 0;
+ }
+
+ // *ALIGNMENT*
+ length = SmbGetUshort( (PUSHORT)text); // Get length of text block
+// length = *((PSHORT) text); // Get length of text
+ text += sizeof(short); // Skip over length word
+ error = 0; // Assume no errors
+
+ bigtime = time_now(); // Get the time
+
+
+ if(!SD_MESLOG()) // If logging disabled
+ {
+ if( alert_flag == 0) // If alert buf is valid
+ {
+ Msghdrprint(1,from, to, bigtime,0);
+ Msgtxtprint(1, text,length,0);
+ Msgendprint(1, MESSTOP,0);
+ }
+ }
+ meslog = SD_MESLOG(); // Get logging status
+ i = 0; // No way to fail if not logging
+
+ if(error != 0) {
+ DbgPrint("meslog.c:logsbm(before ErrorLogWrite): We should never get here\n");
+ NetpAssert(0);
+
+ MsgErrorLogWrite( // Report to error log
+ error,
+ SERVICE_MESSENGER,
+ (LPBYTE)&status,
+ sizeof(DWORD),
+ NULL,
+ 0);
+ }
+
+
+ // Now alert and free up alert buffer if it was successfully allocated
+
+ if( alert_flag == 0) { // There is an alert buffer
+
+ //
+ // There is an alert buffer, output it.
+ //
+ MsgOutputMsg(alert_len, (LPSTR)alert_buf_ptr, bigtime);
+
+ LocalFree(alert_buf_ptr);
+
+ }
+
+ //
+ // Unlock the shared database
+ //
+
+ MsgDatabaseLock(MSG_RELEASE,"logsbm");
+
+ return((int) i); // Return status
+
+}
+
+
+NET_API_STATUS
+MsgErrorLogWrite(
+ IN DWORD Code,
+ IN LPTSTR Component,
+ IN LPBYTE Buffer,
+ IN DWORD BufferSize,
+ IN LPSTR Strings,
+ IN DWORD NumStrings
+ )
+
+/*++
+
+Routine Description:
+
+ Writes an entry to the event manager on the local computer.
+
+ This function needs to get the error message text out of the message
+ file and send it to the event logger.
+
+Arguments:
+
+ Code - Specifies the code of the error that occured.
+
+ Component - Points to a NUL terminated string that specifies which
+ component encountered the error. UNICODE STRING.
+
+ Buffer - Points to a string of raw data associated with the error
+ condition.
+
+ BufferSize - size (in bytes) of the buffer.
+
+ Strings - NOT USED.
+ Points to NUL terminated strings that contain the
+ error message. ANSI STRINGS.
+
+ NumStrings - NOT USED.
+ Specifies how many concatenated NUL terminated strings
+ are stored in Strings.
+
+
+Return Value:
+
+
+
+--*/
+{
+ DWORD status;
+ WORD msglen=0;
+ LPBYTE msgBuf;
+
+ //
+ // Get a message associated with the message code from the message
+ // file.
+ //
+
+ msgBuf = (LPBYTE)LocalAlloc(LMEM_ZEROINIT, ERROR_LOG_SIZE);
+
+ if (msgBuf == NULL) {
+ status = GetLastError();
+ MSG_LOG(ERROR,"MsgErrorLogWrite: LocalAlloc FAILURE %X\n",
+ status);
+ return(status);
+ }
+
+ //
+ // TODO ITEM:
+ // If we actually used strings, then they must be converted to unicode.
+ // However, since they are never used, this isn't very important.
+ //
+
+ status = DosGetMessage (
+ &Strings, // String substitution table
+ (USHORT)NumStrings, // Num Entries in table above
+ msgBuf, // Buffer receiving message
+ ERROR_LOG_SIZE, // size of buffer receiving msg
+ (USHORT)Code, // message num to retrieve
+ MessageFileName, // Name of message file
+ &msglen); // Num bytes returned
+
+ if (status != NERR_Success) {
+ LocalFree(msgBuf);
+ return(status);
+ }
+
+#if DBG
+ //
+ // Give the message to the Event Logger.
+ // BUGBUG: for now just do a DbgPrint.
+ //
+ DbgPrint("MsgErrorLogWrite: COMPONENT = %ws\n",Component);
+ DbgPrint("MsgErrorLogWrite: %s\n",msgBuf);
+
+ if ( Buffer != NULL ) {
+ MsgDbgHexDump( (LPBYTE)Buffer, BufferSize);
+ }
+#endif //DBG
+
+ UNREFERENCED_PARAMETER(Buffer);
+ UNREFERENCED_PARAMETER(BufferSize);
+
+ LocalFree(msgBuf);
+ return(NERR_Success);
+}
+
+NET_API_STATUS
+MsgOutputMsg (
+ USHORT AlertLength,
+ LPSTR AlertBuffer,
+ DWORD BigTime
+ )
+
+/*++
+
+Routine Description:
+
+ This function translates the alert buffer from an Ansi String to a
+ Unicode String and outputs the buffer to whereever it is to go.
+ Currently this just becomes a DbgPrint.
+
+Arguments:
+
+ AlertLength - The number of bytes in the AlertBuffer.
+
+ AlertBuffer - This is a pointer to the buffer that contains the message
+ that is to be output. The buffer is expected to contain a
+ NUL Terminated Ansi String.
+
+ BigTime - The DWORD number that indicates the time the end of the
+ messsage was received.
+
+Return Value:
+
+
+
+--*/
+
+{
+ UNICODE_STRING unicodeString;
+ OEM_STRING ansiString;
+
+ NTSTATUS ntStatus;
+
+ //
+ // NUL Terminate the message.
+ // Translate the Ansi message to a Unicode Message.
+ //
+ AlertBuffer[AlertLength++] = '\0';
+
+ ansiString.Length = AlertLength;
+ ansiString.MaximumLength = AlertLength;
+ ansiString.Buffer = AlertBuffer;
+
+ ntStatus = RtlOemStringToUnicodeString(
+ &unicodeString, // Destination
+ &ansiString, // Source
+ TRUE); // Allocate the destination.
+
+ if (!NT_SUCCESS(ntStatus)) {
+ MSG_LOG(ERROR,
+ "MsgOutputMsg:RtlOemStringToUnicodeString Failed rc=%X\n",
+ ntStatus);
+
+ //
+ // EXPLANATION OF WHY IT RETURNS SUCCESS HERE.
+ // Returning success even though the alert is not raised is
+ // consistent with the LM2.0 code which doesn't check the
+ // return code for the NetAlertRaise API anyway. Returning
+ // anything else would require a re-design of how errors are
+ // handled by the caller of this routine.
+ //
+ return(NERR_Success);
+ }
+
+ //*******************************************************************
+ //
+ // PUT THE MESSAGE IN THE DISPLAY QUEUE
+ //
+
+ MsgDisplayQueueAdd( AlertBuffer, (DWORD)AlertLength,BigTime);
+
+ //
+ //
+ //*******************************************************************
+
+ RtlFreeUnicodeString(&unicodeString);
+ return(NERR_Success);
+}
+
+#if DBG
+VOID
+MsgDbgHexDumpLine(
+ IN LPBYTE StartAddr,
+ IN DWORD BytesInThisLine
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+{
+ LPBYTE BytePtr;
+ DWORD BytesDone;
+ DWORD HexPosition;
+
+ DbgPrint(FORMAT_LPVOID " ", (LPVOID) StartAddr);
+
+ BytePtr = StartAddr;
+ BytesDone = 0;
+ while (BytesDone < BytesInThisLine) {
+ DbgPrint("%02X", *BytePtr); // space for "xx" (see pad below).
+ SPACE_BETWEEN_BYTES;
+ ++BytesDone;
+ if ( (BytesDone % sizeof(DWORD)) == 0) {
+ SPACE_BETWEEN_DWORDS;
+ }
+ ++BytePtr;
+ }
+
+ HexPosition = BytesDone;
+ while (HexPosition < BYTES_PER_LINE) {
+ DbgPrint(" "); // space for "xx" (see byte above).
+ SPACE_BETWEEN_BYTES;
+ ++HexPosition;
+ if ( (HexPosition % sizeof(DWORD)) == 0) {
+ SPACE_BETWEEN_DWORDS;
+ }
+ }
+
+ BytePtr = StartAddr;
+ BytesDone = 0;
+ while (BytesDone < BytesInThisLine) {
+ if (isprint(*BytePtr)) {
+ DbgPrint( FORMAT_CHAR, (CHAR) *BytePtr );
+ } else {
+ DbgPrint( "." );
+ }
+ ++BytesDone;
+ ++BytePtr;
+ }
+ DbgPrint("\n");
+
+} // MsgDbgHexDumpLine
+
+VOID
+MsgDbgHexDump(
+ IN LPBYTE StartAddr,
+ IN DWORD Length
+ )
+/*++
+
+Routine Description:
+
+ MsgDbgHexDump: do a hex dump of some number of bytes to the debug
+ terminal or whatever. This is a no-op in a nondebug build.
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+{
+ DWORD BytesLeft = Length;
+ LPBYTE LinePtr = StartAddr;
+ DWORD LineSize;
+
+ while (BytesLeft > 0) {
+ LineSize = MIN(BytesLeft, BYTES_PER_LINE);
+ MsgDbgHexDumpLine( LinePtr, LineSize );
+ BytesLeft -= LineSize;
+ LinePtr += LineSize;
+ }
+
+} // NetpDbgHexDump
+
+#endif // DBG
diff --git a/private/net/svcdlls/msgsvc/server/mesprint.c b/private/net/svcdlls/msgsvc/server/mesprint.c
new file mode 100644
index 000000000..4f43882e8
--- /dev/null
+++ b/private/net/svcdlls/msgsvc/server/mesprint.c
@@ -0,0 +1,742 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ mesprint.c
+
+Abstract:
+
+ Routines that format messages and place them in the alert buffer.
+
+Author:
+
+ Dan Lafferty (danl) 16-Jul-1991
+
+Environment:
+
+ User Mode -Win32
+
+Revision History:
+
+ 16-Jul-1991 danl
+ ported from LM2.0
+
+--*/
+
+//
+// Includes
+//
+
+#include "msrv.h"
+
+#include <string.h> // memcpy
+#include <tstring.h> // Unicode string macros
+#include <netdebug.h> // NetpAssert
+
+#include <netlib.h> // UNUSED macro
+#include <smbtypes.h> // needed for smb.h
+#include <smb.h> // Server Message Block definitions
+#include <apperr.h> // APE_MSNGR_ definitions
+
+#include "msgdbg.h" // MSG_LOG
+#include "heap.h"
+#include "msgdata.h"
+#include <time.h> // struct tm, time_t
+
+//
+// Defines
+//
+
+//#define NET_CTIME_FMT2_LEN 22*sizeof(TCHAR)
+
+//
+// Local Functions
+//
+
+DWORD
+Msgappend_message(
+ IN USHORT msgno,
+ IN LPSTR buf,
+ IN LPSTR *strarr,
+ IN USHORT nstrings
+ );
+
+DWORD
+Msglog_write(
+ LPSTR text,
+ HANDLE file_handle
+ );
+
+//
+// Global alert buffer data areas. Not used when called from API
+//
+
+LPSTR alert_buf_ptr; // Pointer to DosAlloc'ed alert buffer
+USHORT alert_len; // Currently used length of alert buffer
+
+extern LPTSTR MessageFileName;
+
+
+/*
+** append_message --
+**
+** Gets a message from the message file, and appends it to the
+** given string buffer.
+** The message file used is the one named in the Global MessageFileName,
+** and thus in the Messenger we assume that SetUpMessageFile is called
+** before this to properly fill in this variable.
+**
+** NOTE: This function deals only with Ansi Strings - Not Unicode Strings.
+** The Unicode translation is done all at once just before the alert
+** is raised.
+**
+**/
+
+DWORD
+Msgappend_message(
+ IN USHORT msgno,
+ IN LPSTR buf,
+ IN LPSTR *strarr,
+ IN USHORT nstrings
+ )
+{
+
+ WORD msglen=0;
+ DWORD result;
+
+ LPSTR mymsgbuf = 0;
+ LPSTR msgfile = 0;
+ LPSTR pmb;
+
+
+ //
+ // get a segment to read the message into
+ //
+
+ result = 0;
+
+ mymsgbuf = LocalAlloc(LMEM_ZEROINIT,(int)max(MAXHEAD+1, MAXEND+1));
+
+ if (mymsgbuf == NULL) {
+ result = GetLastError();
+ MSG_LOG(ERROR,"append_message:LocalAlloc failed %X\n",result);
+ return (result);
+ }
+
+ //
+ // Need to fix DosGetMessage to only take ansi strings.
+ //
+
+ if (result == 0)
+ {
+ result = DosGetMessage(
+ strarr, // String substitution table
+ nstrings, // Num Entries in table above
+ mymsgbuf, // Buffer receiving message
+ (WORD)max(MAXHEAD, MAXEND), // size of buffer receiving msg
+ msgno, // message num to retrieve
+ MessageFileName, // Name of message file
+ &msglen); // Num bytes returned
+
+#ifdef later // Currently there is no backup name
+
+ if ( result != 0) {
+ //
+ // if the attempt to get the message out of the message file fails,
+ // get it out of the messages that were bound in to our exe at
+ // build time. These are the same, but are from bak.msg. The
+ // Backup message file is never really there, but the messages
+ // needed from it are bound in to the exe, so we will get them.
+ //
+
+ result = DosGetMessage(
+ strarr,
+ nstrings,
+ mymsgbuf,
+ (int)max(MAXHEAD, MAXEND),
+ msgno,
+ BACKUP_MSG_FILENAME,
+ (unsigned far *) &msglen);
+ }
+#endif
+ }
+
+ //
+ // if there is still an error we are in big trouble. Return whatever
+ // dosgetmessage put into the buffer for us. It is supposed to be
+ // printable.
+ //
+
+ if ( result != 0 ) {
+ LocalFree (mymsgbuf);
+ return (result);
+ }
+
+ mymsgbuf[msglen] = 0;
+
+#ifdef removeForNow
+ //
+ // NOTE: The following logic is skipped because DosGetMessage doesn't
+ // seem to return any NETxxxx field.
+ //
+ // now get rid of the NETxxxx: from the beginning (9 chars)
+ //
+
+ pmb = strchrf(mymsgbuf,' '); // find first space
+
+ if ( pmb == NULL ) {
+ pmb = mymsgbuf; // Just so strcatf doesn't GP Fault.
+ }
+ else {
+ pmb++; // start with next char
+ }
+
+ strcatf(buf,pmb); // copy over the buffer
+#else
+ UNUSED(pmb);
+ strcpy(buf,mymsgbuf); // copy over the buffer
+#endif
+
+ LocalFree (mymsgbuf);
+
+ return (result);
+
+}
+
+/*
+** Msgendprint - print end of message
+**
+** This function prints a delimiter marking the end of a message.
+**
+** endprint (action, state, file_handle)
+**
+** ENTRY
+** action : 0 = alert and file
+** -1 = file only
+** 1 = alert only
+** state - final state of message
+** file_handle - log file handle
+**
+** RETURN
+** 0 - Success, else file system error
+**
+** This function prints a delimiter marking the end of a message.
+** If the state is not the expected end-of-message state, the delimiter
+** signifies that the message terminated prematurely.
+**
+** SIDE EFFECTS
+**
+** None.
+**/
+
+DWORD
+Msgendprint(
+ int action, // Alert, File, or Alert and file
+ DWORD state, // Final state of message
+ HANDLE file_handle // Log file
+ )
+{
+
+ // ******************************************
+ //
+ // NOTE: This routine does nothing for NT
+ // since we don't do any logging.
+ //
+ // ******************************************
+ UNUSED(action);
+ UNUSED(state);
+ UNUSED(file_handle);
+ return(0);
+
+#ifdef remove
+
+ char end_buf[MAXEND+1];
+ USHORT msg_no;
+ LPSTR strarg[1];
+ LPSTR msg_end;
+
+
+ end_buf[0] = '\0';
+ end_buf[MAXEND] = '\0';
+
+ if(state == MESSTOP) {
+ msg_no = APE_MSNGR_GOODEND;
+ }
+ else {
+ msg_no = APE_MSNGR_BADEND;
+ }
+
+ //
+ // Set up the message to be printed. Try to get from message file.
+ // If the message file is not available, append_message gets the
+ // bound message out of memory.
+ //
+
+ strcpy(end_buf, "\n\r\n\r");
+
+ //
+ // The +4 is for the \n\r\n\r combination above.
+ //
+
+ Msgappend_message(msg_no, (char far *)(end_buf+4), strarg, 0);
+
+ //
+ // append_message ends all messages with a \r\n combination,
+ // which is fine except in this case. We want to overwrite
+ // the \r\n with the \n\r\n\r below.
+ //
+
+ msg_end = strrchr(end_buf, '\r');
+
+ if ( msg_end ) {
+ *msg_end = '\0';
+ }
+
+ // The worst that will happen here is that we won't find
+ // the last \r, which means that we will end up
+ // with an extra \r\n at the end of the message delimiter.
+ // This is better than gp-faulting becuase of assuming that
+ // the strrchrf will always find something.
+ //
+
+ strcat((char far *)end_buf, "\n\r\n\r");
+
+ //
+ // At this point end_buf contains the correct message ending.
+ //
+ //
+ // There is no need to send the delimiter to the alert if the message
+ // state is MESSTOP as the delimiter is not really part of the message
+ // and is only required for the log file.
+ //
+
+ if( (action >= 0) && ( state != MESSTOP) ) {
+
+ DbgPrint("mesprint.c:endprint1: we should never get here\n");
+
+ NetpAssert(0);
+
+ //
+ // if alerting and error
+ //
+
+ if( alert_len < ALERT_MAX_DISPLAYED_MSG_SIZE + 1) {
+
+ memcpy( &alert_buf_ptr[alert_len], end_buf, strlen(end_buf));
+
+ alert_len += strlen( end_buf);
+ }
+ }
+
+ if( action < 1) {
+
+ DbgPrint("mesprint.c:endprint2: we should never get here\n");
+ NetpAssert(0);
+
+ //
+ // if file and alert or file only
+ //
+
+ return( log_write(end_buf,file_handle) );
+ }
+
+ return(0); // Cannot fail on alert only
+#endif
+}
+
+/*
+** Msghdrprint - print a message header
+**
+** This function prints a message header using the time and
+** date format appropriate for the current country.
+**
+** hdrprint (action, from, to, date, time, file_handle)
+**
+** ENTRY
+** action : 0 = alert and file
+** -1 = file only
+** 1 = alert only
+** from - name of sender
+** to - name of intended recipient
+** bigtime - bigtime of message
+** file_handle - log file handle
+**
+** RETURN
+** 0 - Success, else file system error
+**
+** This function prints the given information in the appropriate
+** format. The names are passed as far pointers so that names in
+** the shared data area do not have to be copied into the automatic
+** data segment in order to print them.
+**
+** SIDE EFFECTS
+**
+** Calls the DOS to get country-dependent information.
+**/
+
+DWORD
+Msghdrprint(
+ int action, // Where to log the header to.
+ LPSTR from, // Name of sender
+ LPSTR to, // Name of recipient
+ DWORD bigtime, // Bigtime of message
+ HANDLE file_handle // Output file handle*/
+ )
+{
+ char hdr_buf[MAXHEAD+1]; // Buffer header text
+ char time_buf[NET_CTIME_FMT2_LEN+1];
+ DWORD status; // file write status
+ DWORD i=0; // Index into header_buf
+ LPSTR str_table[3]; // For DosGetMessage
+// time_t LocalTime;
+// struct tm TmTemp;
+
+ *(hdr_buf + MAXHEAD) = '\0'; // for strlen
+ hdr_buf[0] = '\0';
+
+ str_table[0] = from;
+ str_table[1] = to;
+
+ //
+ // Create a time string from the Time in the LONG format
+ //
+#ifdef REMOVE
+ NetpGmtTimeToLocalTime( (DWORD) bigtime, (LPDWORD) & LocalTime);
+ net_gmtime(&LocalTime, &TmTemp);
+ NetpMakeTimeString( &TmTemp, &GlobalTimeFormat, time_buf, NET_CTIME_FMT2_LEN);
+#endif
+ //******************************
+ //
+ // Because we queue messages, and a user my not be logged on when the
+ // message is queued. We want to instead, put a place-holder in the
+ // message buffer for the time. Later, when we read from the queue, we
+ // will add the time string formatted for the logged on user.
+ //
+ strcpy (time_buf, GlobalTimePlaceHolder);
+
+ //******************************
+
+ str_table[2] = time_buf;
+
+ // Try to get the message from the message file or from the backup
+ // in memory. This will always leave something in the hdr_buf that
+ // is printable, if not correct.
+ //
+
+ Msgappend_message(APE_MSNGR_HDR, hdr_buf, str_table, 3);
+
+ strcat( hdr_buf,"\r\n");
+
+ status = 0; // assume success
+
+ if( action >= 0 ) {
+
+ //
+ // If alert and file or alert only,
+ // then copy hdr_buf to alert buffer.
+ //
+ memcpy( &(alert_buf_ptr[alert_len]),
+ hdr_buf,
+ i = strlen(hdr_buf));
+
+ alert_len += (USHORT)i;
+ }
+
+ if( action < 1) {
+
+ DbgPrint("mesprint.c:hdrprint:We should never get here\n");
+ NetpAssert(0);
+ //
+ // if file and alert or file only, attempt to write
+ // header to log file.
+ //
+ status = Msglog_write(hdr_buf, file_handle);
+ }
+ return(status);
+}
+
+/*
+** Msgmbmfree - deallocate the pieces of a multi-block message
+**
+** Given an index to the header of a multi-block message, this function
+** deallocates the header block and all of the text blocks.
+**
+** mbmfree (mesi)
+**
+** ENTRY
+** mesi - index into the message buffer
+**
+** RETURN
+** nothing
+**
+** This function deallocates a multi-block message piece by piece.
+**
+** SIDE EFFECTS
+**
+** Calls heapfree() to deallocate each piece.
+**/
+
+VOID
+Msgmbmfree(
+ DWORD mesi // Message index
+ )
+
+{
+ DWORD text; // Index to text
+
+ text = MBB_FTEXT(*MBBPTR(mesi)); // Get the index to the text
+ Msgheapfree(mesi); // Deallocate the message header
+
+ //
+ // The following loop deallocates each text block in the chain.
+ //
+
+ while(text != INULL) { // While not at end of chain
+ mesi = text; // Save index
+ text = MBT_NEXT(*MBTPTR(text)); // Get link to next block
+ Msgheapfree(mesi); // Free this block
+ }
+}
+
+/*
+** Msgmbmprint - print a multi-block message
+**
+** This function writes a multi-block message to the log file.
+**
+** mbmprint (action, mesi, file_handle)
+**
+** ENTRY
+** action : 0 = alert and file
+** -1 = file only
+** 1 = alert only
+** mesi - index into the message buffer
+** file_handle - log file handle
+**
+** RETURN
+** 0 - Success, else file system error
+**
+** This function writes the message starting at the mesi'th byte in the
+** message buffer (in the shared data area) to the log file. It returns
+** the value EOF if the writing of the message fails.
+**
+** SIDE EFFECTS
+**
+** Calls hdrprint(), txtprint(), and endprint().
+**/
+
+DWORD
+Msgmbmprint(
+ int action, // Alert, File, or Alert and file
+ DWORD mesi, // Message index
+ HANDLE file_handle // Log file handle
+ )
+
+{
+ LPSTR from; // Sender
+ LPSTR to; // Recipient
+ DWORD text; // Index to text
+ DWORD state; // Final state of message
+ DWORD status; // File write status
+
+ from = &CPTR(mesi)[sizeof(MBB)]; // Get pointer to sender name
+ to = &from[strlen(from) + 1]; // Get pointer to recipient name
+
+ if((status = Msghdrprint(
+ action,
+ from,
+ to,
+ MBB_BIGTIME(*MBBPTR(mesi)),
+ file_handle)) != 0) {
+
+ return(status); // Fail if error on header write
+ }
+
+ state = MBB_STATE(*MBBPTR(mesi)); // Save the state
+ text = MBB_FTEXT(*MBBPTR(mesi)); // Get the index to the text
+
+ //
+ // The following loop prints out each text block in the chain.
+ //
+
+ while(text != INULL) { // While not at end of chain
+
+ if((status = Msgtxtprint(
+ action,
+ &CPTR(text)[sizeof(MBT)],
+ MBT_COUNT(*MBTPTR(text)), // *ALIGNMENT2*
+// MBT_SIZE(*MBTPTR(text)) - sizeof(MBT),
+ file_handle)) != 0) {
+
+ break; // If write error
+ }
+ text = MBT_NEXT(*MBTPTR(text)); // Get link to next block
+ }
+
+ if(status != 0) {
+ return(status); // Error on text write
+ }
+
+ //
+ // finish the message
+ //
+ // BUGBUG: On NT we shouldn't need to call endprint. If the asserts
+ // in endprint are never hit, then remove this altogether.
+ //
+
+ return(Msgendprint(action,state,file_handle));
+}
+
+/*
+** Msgtxtprint - print text of message
+**
+** This function prints a block of text.
+**
+** txtprint ( action, text, length, file_handle)
+**
+** ENTRY
+** action : 0 = alert and file
+** -1 = file only
+** 1 = alert only
+** text - pointer to text
+** length - length of text
+** file_handle - log file handle
+**
+** RETURN
+** 0 - Success, else file system error
+**
+** This function prints the given amount of text. The text pointer is
+** a far pointer so that text blocks in the shared data area do not have
+** to be copied into the automatic data segment in order to process
+** them.
+**
+** SIDE EFFECTS
+**
+** Converts the character '\024' to the sequence '\015', '\012' on output.
+**/
+
+DWORD
+Msgtxtprint(
+ int action, // Alert, File, or Alert and file
+ LPSTR text, // Pointer to text
+ DWORD length, // Length of text
+ HANDLE file_handle // Log file handle
+ )
+
+{
+ char buffer[2*TXTMAX+1]; // Text buffer
+ LPSTR cp; // Character pointer
+ DWORD i; // Counter
+
+ cp = buffer; // Initialize
+
+ //
+ // Loop to translate text
+ //
+ for(i = length; i != 0; --i) {
+
+ if(*text == '\024') {
+ //
+ // If IBM end-of-line character
+ //
+
+ ++length; // Length has increased
+ *cp++ = '\r'; // Carriage return
+ *cp++ = '\n'; // Linefeed
+ }
+ else {
+ //
+ // Else copy character as is
+ //
+
+ *cp++ = *text;
+ }
+ ++text; // Increment pointer
+ }
+ *cp = '\0'; // So can use log_write
+
+ if( action >= 0) {
+
+ //
+ // if alert and file or alert only
+ //
+
+ if( alert_len < ALERT_MAX_DISPLAYED_MSG_SIZE + 1) {
+ memcpy( &alert_buf_ptr[alert_len], buffer, strlen(buffer));
+ alert_len += strlen(buffer);
+ }
+ }
+
+ if( action < 1) {
+ //
+ // if file and alert or file only, write text to log file
+ //
+
+ return(Msglog_write(buffer,file_handle));
+ }
+ return(0); // Cannot fail on alert only
+}
+
+
+/*
+** Msgopen_append - opens the requested file for read/write and seeks to the
+** end of the file.
+**
+** open_append - ( file_name, file_handle_ptr)
+**
+** ENTRY
+** file_name - pointer to file_name
+** file_handle_ptr - pointer to unsigned short to store file pointer
+**
+** RETURN
+** 0 - Success, else file system error
+**
+** SIDE EFFECTS
+**
+**/
+
+DWORD
+Msgopen_append(
+ LPSTR file_name, // Name of file to open
+ PHANDLE file_handle_ptr // pointer to storage for file handle
+ )
+{
+ NetpAssert(0);
+ UNUSED (file_name);
+ UNUSED (file_handle_ptr);
+ return(0);
+}
+
+
+
+
+
+/*
+** Msglog_write - writes a text string to the log file..
+**
+** log_write - ( text, file_handle)
+**
+** ENTRY
+** text - text string to write to file.
+** file_handle - log file handle
+**
+** RETURN
+** 0 - Success, else file system error
+**
+** SIDE EFFECTS
+**
+**/
+
+DWORD
+Msglog_write(
+ LPSTR text, // String to write to log file*/
+ HANDLE file_handle // log file handle
+ )
+{
+ NetpAssert(0);
+ UNUSED (text);
+ UNUSED (file_handle);
+ return(0);
+}
+
diff --git a/private/net/svcdlls/msgsvc/server/msgapi.c b/private/net/svcdlls/msgsvc/server/msgapi.c
new file mode 100644
index 000000000..c9733f793
--- /dev/null
+++ b/private/net/svcdlls/msgsvc/server/msgapi.c
@@ -0,0 +1,1103 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ msgapi.c
+
+Abstract:
+
+ Provides API functions for the messaging system.
+
+Author:
+
+ Dan Lafferty (danl) 23-Jul-1991
+
+Environment:
+
+ User Mode -Win32
+
+Notes:
+
+ optional-notes
+
+Revision History:
+
+ 13-Jan-1993 danl
+ NetrMessageNameGetInfo: Allocation size calculation was incorrectly
+ trying to take the sizeof((NCBNAMSZ+1)*sizeof(WCHAR)). NCBNAMSZ is
+ a #define constant value.
+
+ 22-Jul-1991 danl
+ Ported from LM2.0
+
+--*/
+
+//
+// Includes
+//
+
+#include "msrv.h"
+
+#include <tstring.h> // Unicode string macros
+#include <lmmsg.h>
+
+#include <netlib.h> // UNUSED macro
+#include <msgrutil.h> // NetpNetBiosReset
+#include <rpc.h>
+#include <msgsvc.h> // MIDL generated header file
+#include "msgdbg.h" // MSG_LOG
+#include "heap.h"
+#include "msgdata.h"
+#include "apiutil.h"
+#include "msgsec.h" // Messenger Security Information
+#include <winbasep.h> // BUGBUG (where is this really - currently win/inc)
+#include <timelib.h> // NetpGetTimeFormat
+
+// Static data descriptor strings for remoting the Message APIs
+
+static char nulstr[] = "";
+
+
+
+NET_API_STATUS
+NetrMessageNameEnum(
+ IN LPWSTR ServerName,
+ IN OUT LPMSG_ENUM_STRUCT InfoStruct,
+ IN DWORD PrefMaxLen,
+ OUT LPDWORD TotalEntries,
+ IN OUT LPDWORD ResumeHandle OPTIONAL
+ )
+
+/*++
+
+Routine Description:
+
+ This function provides information about the message service name table
+ at two levels of detail.
+
+Arguments:
+
+ ServerName - Pointer to a string containing the name of the computer
+ that is to execute the API function.
+
+ InfoStruct - Pointer to a structure that contains the information that
+ RPC needs about the returned data. This structure contains the
+ following information:
+ Level - The desired information level - indicates how to
+ interpret the structure of the returned buffer.
+ EntriesRead - Indicates how many elements are returned in the
+ array of structures that are returned.
+ BufferPointer - Location for the pointer to the array of
+ structures that are being returned.
+
+ PrefMaxLen - Indicates a maximum size limit that the caller will allow
+ for the return buffer.
+
+ TotalEntries - Pointer to a value that upon return indicates the total
+ number of entries in the "active" database.
+
+ ResumeHandle - Inidcates where in the linked list to start the
+ enumeration. This is an optional parameter and can be NULL.
+
+Return Value:
+
+ NERR_Success - The operation was successful. EntriesRead is valid.
+
+ ERROR_INVALID_LEVEL - An invalid info level was passed in.
+
+ ERROR_MORE_DATA - Not all the information in the database could be
+ returned due to the limititation placed on buffer size by
+ PrefMaxLen. One or more information records will be found in
+ the buffer. EntriesRead is valid.
+
+ NERR_BufTooSmall - The limitation (PrefMaxLen) on buffer size didn't
+ allow any information to be returned. Not even a single record
+ could be fit in a buffer that small.
+
+ NERR_InternalError - A name in the name table could not be translated
+ from ansi characters to unicode characters. (Note: this
+ currently causes 0 entries to be returned.)
+
+
+--*/
+{
+
+ DWORD hResume = 0; // resume handle value
+ DWORD entriesRead = 0;
+ DWORD retBufSize;
+ LPBYTE infoBuf;
+ LPBYTE infoBufTemp;
+ LPBYTE stringBuf;
+
+ DWORD entry_length; // Length of one name entry in buf
+ DWORD i,j,k; // index for name loop and flags
+ NET_API_STATUS status=0;
+ DWORD neti; // net index
+
+
+ UNUSED (ServerName);
+
+ //
+ // If ResumeHandle is present and valid, initialize it.
+ //
+ if (ARGUMENT_PRESENT(ResumeHandle) && (*ResumeHandle < NCBMAX)) {
+ hResume = *ResumeHandle;
+ }
+
+ //
+ // Wakeup the display thread so that any queue'd messages can be
+ // displayed.
+ //
+ MsgDisplayThreadWakeup();
+
+ //
+ // Initialize some of the return counts.
+ //
+
+ *TotalEntries = 0;
+
+ //
+ // API security check. This call can be called by anyone locally,
+ // but only by admins in the remote case.
+ //
+
+ status = NetpAccessCheckAndAudit(
+ SERVICE_MESSENGER, // Subsystem Name
+ (LPWSTR)MESSAGE_NAME_OBJECT, // Object Type Name
+ MessageNameSd, // Security Descriptor
+ MSGR_MESSAGE_NAME_ENUM, // Desired Access
+ &MsgMessageNameMapping); // Generic Mapping
+
+ if (status != NERR_Success) {
+ MSG_LOG(TRACE,
+ "NetrMessageNameEnum:NetpAccessCheckAndAudit FAILED %X\n",
+ status);
+ return(ERROR_ACCESS_DENIED);
+ }
+
+ //
+ // Determine the size of one element in the returned array.
+ //
+ switch( InfoStruct->Level) {
+ case 0:
+ entry_length = sizeof(MSG_INFO_0);
+ break;
+ case 1:
+ entry_length = sizeof(MSG_INFO_1);
+ break;
+ default:
+ return(ERROR_INVALID_LEVEL);
+ }
+
+ //
+ // Allocate enough space for return buffer
+ //
+ if (PrefMaxLen == -1) {
+ //
+ // If the caller has not specified a size, calculate a size
+ // that will hold the entire enumeration.
+ //
+ retBufSize =
+ ((NCBMAX * ((NCBNAMSZ+1) * sizeof(WCHAR))) + // max possible num strings
+ (NCBMAX * entry_length)); // max possible num structs
+ }
+ else {
+ retBufSize = PrefMaxLen;
+ }
+
+ infoBuf = (LPBYTE)MIDL_user_allocate(retBufSize);
+ stringBuf = infoBuf + (retBufSize & ~1); // & ~1 to align Unicode strings
+
+ //
+ // Block until data free
+ //
+ MsgDatabaseLock(MSG_GET_EXCLUSIVE,"NetrMessageNameEnum");
+
+ //
+ // Now copy as many names from the shared data name table as will fit
+ // into the callers buffer. The shared data is locked so that the name
+ // table can not change while it is being copied (eg by someone
+ // deleting a forwarded name on this station after the check for a valid
+ // name has been made but before the name has been read). The level 1
+ // information is not copied in this loop as it requires network
+ // activity which must be avoided while the shared data is locked.
+ //
+
+ //
+ // HISTORY:
+ //
+ // The original LM2.0 code looked at the names on all nets, and
+ // threw away duplicate ones. This implies that a name may appear
+ // on one net and not on another. Although, this can never happen if
+ // the names are always added via NetMessageNameAdd since that API
+ // will not add the name unless it can be added to all nets. However,
+ // forwarded names are added via a network receive, and may be added
+ // from one net only.
+ //
+ // Since NT is not supporting forwarding, it is no longer necessary to
+ // check each net. Since the only way to add names if via NetServiceAdd,
+ // this will assure that the name listed for one network are the same
+ // as the names listed for the others.
+ //
+
+ infoBufTemp = infoBuf;
+ neti=j=0;
+ status = NERR_Success;
+
+ for(i=hResume; (i<NCBMAX) && (status==NERR_Success); ++i) {
+
+ if(!(SD_NAMEFLAGS(neti,i) & (NFDEL | NFDEL_PENDING))) {
+ //
+ // If a name is found we put it in the buffer if the
+ // following conditions are met. If we are processing
+ // the first net's names, put it in, it cannot be a
+ // duplicate. Otherwise, only put it in if it is not
+ // a duplicate of a name that is already in the user
+ // buffer.
+ // (NT_NOTE: duplicate names cannot occur).
+ //
+
+ //
+ // translate the name to unicode and put it into the buffer
+ //
+ status = MsgGatherInfo (
+ InfoStruct->Level,
+ SD_NAMES(neti,i),
+ &infoBufTemp,
+ &stringBuf);
+
+ if (status == NERR_Success) {
+ entriesRead++;
+ hResume++;
+ }
+ }
+ }
+
+ //
+ // Calculate the total number of entries by seeing how many names are
+ // left in the table and adding that to the entries read.
+ //
+ if (status == ERROR_NOT_ENOUGH_MEMORY) {
+
+ status = ERROR_MORE_DATA;
+
+ for (k=0; i < NCBMAX; i++) {
+ if(!(SD_NAMEFLAGS(neti,i) & (NFDEL | NFDEL_PENDING))) {
+ k++;
+ }
+ }
+ *TotalEntries = k;
+ }
+ *TotalEntries += entriesRead;
+
+ //
+ // Free up the shared data table
+ //
+ MsgDatabaseLock(MSG_RELEASE,"NetrMessageNameEnum");
+
+ //
+ // If some unexpected error occured, ( couldn't unformat the name
+ // - or a bogus info level was passed in), then return the error.
+ //
+
+ if ( ! ((status == NERR_Success) || (status == ERROR_MORE_DATA)) ) {
+ MIDL_user_free(infoBuf);
+ infoBuf = NULL;
+ entriesRead = 0;
+ hResume = 0;
+ return(status);
+ }
+
+ //
+ // if there were no entries read then either there were no more
+ // entries in the table, or the resume number was bogus.
+ // In this case, we want to free the allocated buffer storage.
+ //
+ if (entriesRead == 0) {
+ MIDL_user_free(infoBuf);
+ infoBuf = NULL;
+ entriesRead = 0;
+ hResume = 0;
+ status = NERR_Success;
+ if (*TotalEntries > 0) {
+ status = NERR_BufTooSmall;
+ }
+ }
+
+ //
+ // If we have finished enumerating everything, reset the resume
+ // handle to start at the beginning next time.
+ //
+ if (entriesRead == *TotalEntries) {
+ hResume = 0;
+ }
+
+ //
+ // Load up the information to return
+ //
+ switch(InfoStruct->Level) {
+ case 0:
+ InfoStruct->MsgInfo.Level0->EntriesRead = entriesRead;
+ InfoStruct->MsgInfo.Level0->Buffer = (PMSG_INFO_0)infoBuf;
+ break;
+ case 1:
+ InfoStruct->MsgInfo.Level0->EntriesRead = entriesRead;
+ InfoStruct->MsgInfo.Level0->Buffer = (PMSG_INFO_0)infoBuf;
+ break;
+ default:
+ return (ERROR_INVALID_LEVEL);
+ }
+
+ if (ARGUMENT_PRESENT(ResumeHandle)) {
+ *ResumeHandle = hResume;
+ }
+
+ return (status);
+}
+
+NET_API_STATUS
+NetrMessageNameGetInfo(
+ IN LPWSTR ServerName, // unicode server name, NULL if local
+ IN LPWSTR Name, // Ptr to asciz name to query
+ IN DWORD Level, // Level of detail requested
+ OUT LPMSG_INFO InfoStruct // Ptr to buffer for info
+ )
+
+/*++
+
+Routine Description:
+
+ This funtion provides forwarding information about a known message server
+ name table entry. However, since we do not support forwarding in NT,
+ this API is totally useless. We'll support it anyway though for
+ compatibility purposes.
+
+Arguments:
+
+ ServerName - Pointer to a string containing the name of the computer
+ that is to execute the API function.
+
+ Name - The Messaging name that we are to get info on.
+
+ Level - The level of information desired
+
+ InfoStruct - Pointer to a location where the pointer to the returned
+ information structure is to be placed.
+
+
+Return Value:
+
+
+
+--*/
+{
+ NET_API_STATUS status=NERR_Success;
+ LPMSG_INFO_0 infoBuf0;
+ LPMSG_INFO_1 infoBuf1;
+ CHAR formattedName[NCBNAMSZ];
+
+ UNUSED (ServerName);
+
+ //
+ // Wakeup the display thread so that any queue'd messages can be
+ // displayed.
+ //
+ MsgDisplayThreadWakeup();
+
+ //
+ // API security check. This call can be called by anyone locally,
+ // but only by admins in the remote case.
+ //
+
+ status = NetpAccessCheckAndAudit(
+ SERVICE_MESSENGER, // Subsystem Name
+ (LPWSTR)MESSAGE_NAME_OBJECT, // Object Type Name
+ MessageNameSd, // Security Descriptor
+ MSGR_MESSAGE_NAME_INFO_GET, // Desired Access
+ &MsgMessageNameMapping); // Generic Mapping
+
+ if (status != NERR_Success) {
+ MSG_LOG(TRACE,
+ "NetrMessageNameGetInfo:NetpAccessCheckAndAudit FAILED %X\n",
+ status);
+ return(ERROR_ACCESS_DENIED);
+ }
+
+ //
+ // Format the name so it matches what is stored in the name table.
+ //
+ status = MsgFmtNcbName(formattedName, Name, NAME_LOCAL_END);
+ if (status != NERR_Success) {
+ MSG_LOG(ERROR,"NetrMessageGetInfo: could not format name\n",0);
+ return (NERR_NotLocalName);
+ }
+
+
+ status = NERR_Success;
+
+ //
+ // Look for the name in the shared data name array. (1st net only).
+ //
+
+ if (MsgLookupName(0, formattedName) == -1) {
+ MSG_LOG(ERROR,"NetrMessageGetInfo: Name not in table\n",0);
+ status = NERR_NotLocalName;
+ return (status);
+ }
+
+ //
+ // Allocate storage for the returned buffer, and fill it in.
+ //
+
+ switch(Level) {
+ case 0:
+ infoBuf0 = (LPMSG_INFO_0)MIDL_user_allocate(
+ sizeof(MSG_INFO_0) + ((NCBNAMSZ+1)*sizeof(WCHAR)));
+ if (infoBuf0 == NULL) {
+ MSG_LOG(ERROR,
+ "NetrMessageNameGetInfo MIDL allocate FAILED %X\n",
+ GetLastError());
+ return( ERROR_NOT_ENOUGH_MEMORY);
+ }
+
+ //
+ // copy the name and set the pointer in the structure to point
+ // to it.
+ //
+ STRCPY((LPWSTR)(infoBuf0 + 1), Name);
+ infoBuf0->msgi0_name = (LPWSTR)(infoBuf0 + 1);
+ (*InfoStruct).MsgInfo0 = infoBuf0;
+
+ break;
+
+ case 1:
+ infoBuf1 = (LPMSG_INFO_1)MIDL_user_allocate(
+ sizeof(MSG_INFO_1) + ((NCBNAMSZ+1)*sizeof(WCHAR)) );
+
+ if (infoBuf1 == NULL) {
+ MSG_LOG(ERROR,
+ "NetrMessageNameGetInfo MIDL allocate FAILED %X\n",
+ GetLastError());
+ return( ERROR_NOT_ENOUGH_MEMORY);
+ }
+
+ //
+ // Copy the name, update pointers, and set forward info fields.
+ //
+ STRCPY((LPWSTR)(infoBuf1 + 1), Name);
+
+ infoBuf1->msgi1_name = (LPWSTR)(infoBuf1 + 1);
+ infoBuf1->msgi1_forward_flag = 0;
+ infoBuf1->msgi1_forward = NULL;
+
+ (*InfoStruct).MsgInfo1 = infoBuf1;
+ break;
+
+ default:
+ return(ERROR_INVALID_LEVEL);
+ }
+ return(NERR_Success);
+}
+
+
+
+NET_API_STATUS
+NetrMessageNameAdd(
+ LPWSTR ServerName, // NULL = local
+ LPWSTR Name // Pointer to name to add.
+ )
+
+/*++
+
+Routine Description:
+
+ This function performs a security check for all calls to this
+ RPC interface. Then it adds a new name to the Message
+ Server's name table by calling the MsgAddName function.
+
+Arguments:
+
+ ServerName - Pointer to a string containing the name of the computer
+ that is to execute the API function.
+
+ Name - A pointer to the name to be added.
+
+Return Value:
+
+ NERR_Success - The operation was successful.
+
+ ERROR_ACCESS_DENIED - If the Security Check Failed.
+
+ Assorted Error codes from MsgAddName.
+
+--*/
+
+{
+ NET_API_STATUS status=0;
+
+ UNUSED(ServerName);
+
+ //
+ // API security check. This call can be called by anyone locally,
+ // but only by admins in the remote case.
+ //
+
+ status = NetpAccessCheckAndAudit(
+ SERVICE_MESSENGER, // Subsystem Name
+ (LPWSTR)MESSAGE_NAME_OBJECT, // Object Type Name
+ MessageNameSd, // Security Descriptor
+ MSGR_MESSAGE_NAME_ADD, // Desired Access
+ &MsgMessageNameMapping); // Generic Mapping
+
+ if (status != NERR_Success) {
+ MSG_LOG(TRACE,
+ "NetrMessageNameAdd:NetpAccessCheckAndAudit FAILED %X\n",
+ status);
+ return(ERROR_ACCESS_DENIED);
+ }
+
+ //
+ // Save away the Time Format for this user.
+ //
+ EnterCriticalSection(&TimeFormatCritSec);
+
+ if (!CloseProfileUserMapping()) {
+ MSG_LOG0(ERROR, "NetrMessageNameAdd: CloseProfileUserMapping failed\n");
+ }
+
+ status = RpcImpersonateClient(NULL);
+ if (status != NERR_Success) {
+ MSG_LOG1(ERROR, "NetrMessageNameAdd: RpcImpersonateClient failed %d\n",
+ GetLastError());
+ }
+ if (!OpenProfileUserMapping()) {
+ MSG_LOG0(ERROR, "NetrMessageNameAdd: OpenProfileUserMapping failed\n");
+ }
+ NetpGetTimeFormat(&GlobalTimeFormat);
+
+ if (!CloseProfileUserMapping()) {
+ MSG_LOG0(ERROR, "NetrMessageNameAdd: CloseProfileUserMapping failed\n");
+ }
+
+ status = RpcRevertToSelf();
+ if (status != NERR_Success) {
+ MSG_LOG(ERROR, "NetrMessageNameAdd: RpcRevertToSelf failed %d\n",
+ GetLastError());
+ }
+ if (!OpenProfileUserMapping()) {
+ MSG_LOG0(ERROR, "NetrMessageNameAdd: 2nd-OpenProfileUserMapping failed\n");
+ }
+
+ LeaveCriticalSection(&TimeFormatCritSec);
+
+ //
+ // Since a new user may have just logged on, we want to check to see if
+ // there are any messages to be displayd.
+ //
+ MsgDisplayThreadWakeup();
+
+ //
+ // Call the function that actually adds the name.
+ //
+ return(MsgAddName(Name));
+
+}
+
+
+NET_API_STATUS
+MsgAddName(
+ LPWSTR Name
+ )
+
+/*++
+
+Routine Description:
+
+ This function adds a new name to the Message Server's name table.
+ It is available to be called internally (from within the Messenger
+ service).
+
+ The task of adding a new name to the Message Server's name table consists
+ of verifying that a session can be established for a new name (Note: this
+ check is subject to failure in a multiprocessing environment, since the
+ state of the adapter may change between the time of the check and the time
+ of the attempt to establish a session), verifying that the name does not
+ already exist in the local name table, adding the name to the local adapter
+ via an ADD NAME net bios call, adding the name to the Message Server's name
+ table and marking it as new, waking up the Message Server using the wakeup
+ semaphore, and checking to see if messages for the new name have been
+ forwarded (if they have been forwarded, the value of the fwd_action
+ flag is used to determine the action to be taken).
+
+
+ SIDE EFFECTS
+
+ Calls the net bios. May modify the Message Server's shared data area.
+ May call DosSemClear() on the wakeup semaphore.
+
+Arguments:
+
+ Name - A pointer to the name to be added.
+
+Return Value:
+
+ NERR_Success - The operation was successful.
+
+ assorted errors.
+
+--*/
+{
+ NCB ncb; // Network control block
+ TCHAR namebuf[NCBNAMSZ+2]; // General purpose name buffer
+ UCHAR net_err=0; // Storage for net error codes
+ NET_API_STATUS err_code=0; // Storage for return error codes
+ DWORD neti,i,name_i; // Index
+ NET_API_STATUS status=0;
+
+ if ( MsgIsValidMsgName( Name) != 0) {
+ return( ERROR_INVALID_NAME);
+ }
+
+ MSG_LOG(TRACE,"Attempting to add the following name: %ws\n",Name);
+
+ STRNCPY( namebuf, Name, NCBNAMSZ+1);
+ namebuf[NCBNAMSZ+1] = '\0';
+
+ //
+ // Initialize the NCB
+ //
+ clearncb(&ncb);
+
+ //
+ // Format the name for NetBios.
+ // This converts the Unicode string to ansi.
+ //
+ status = MsgFmtNcbName(ncb.ncb_name, namebuf, NAME_LOCAL_END);
+
+ if (status != NERR_Success) {
+ MSG_LOG(ERROR,"MsgAddName: could not format name\n",0);
+ return (ERROR_INVALID_NAME);
+ }
+
+ //
+ // Check if the local name already exists on any netcard
+ // in this machine. This check does not mean the name dosn't
+ // exist on some other machine on the network(s).
+ //
+
+ for ( neti = 0; neti < SD_NUMNETS(); neti++ ) {
+
+ for( i = 0, err_code = 0; i < 10; i++) {
+
+ name_i = MsgLookupName(neti, ncb.ncb_name);
+ if ((name_i) == -1) {
+ break;
+ }
+
+ if( (SD_NAMEFLAGS(neti,name_i) & NFDEL_PENDING) && (i < 9)) {
+
+ //
+ // Delete is pending so wait for it
+ //
+ Sleep(500L);
+ }
+ else {
+ //
+ // Setup error code
+ //
+ err_code = NERR_AlreadyExists;
+ break;
+ }
+ }
+
+ if ( err_code == NERR_AlreadyExists ) {
+ break;
+ }
+ }
+
+ if( err_code == 0) {
+ //
+ // Either the name was not forwarded or the fwd_action flag
+ // was set so go ahead and try to add the name to each net.
+ //
+
+ ncb.ncb_name[NCBNAMSZ - 1] = NAME_LOCAL_END;
+
+ //
+ // on each network
+ //
+ for ( neti = 0; neti < SD_NUMNETS(); neti++ ) {
+
+ //
+ // Gain access to the shared database.
+ //
+ MsgDatabaseLock(MSG_GET_EXCLUSIVE,"MsgAddName");
+
+ for(i = 0; i < NCBMAX; ++i) {
+ //
+ // Loop to find empty slot
+ //
+ if (SD_NAMEFLAGS(neti,i) & NFDEL) {
+ //
+ // If empty slot found, Lock slot in table and
+ // end the search
+ //
+ SD_NAMEFLAGS(neti,i) = NFLOCK;
+ MSG_LOG2(TRACE,"MsgAddName: Lock slot %d in table "
+ "for net %d\n",i,neti);
+ break;
+ }
+ }
+
+ //
+ // Unlock the shared database
+ //
+ MsgDatabaseLock(MSG_RELEASE, "MsgAddName");
+
+ if( i >= NCBMAX) {
+ //
+ // If no room in name table
+ //
+ err_code = NERR_TooManyNames;
+ }
+ else {
+ //
+ // Send ADDNAME
+ //
+ ncb.ncb_command = NCBADDNAME; // Add name (wait)
+ ncb.ncb_lana_num = net_lana_num[neti];
+
+ MSG_LOG1(TRACE,"MsgNameAdd: Calling sendncb for lana #%d...\n",
+ net_lana_num[neti]);
+ if ((net_err = Msgsendncb(&ncb,neti)) == 0)
+ {
+ MSG_LOG(TRACE,"MsgAddName: sendncb returned SUCCESS\n",0);
+ //
+ // successful add - Get the Lock.
+ //
+ MsgDatabaseLock(MSG_GET_EXCLUSIVE,"MsgAddName");
+ //
+ // Copy the name to shared memory
+ //
+ MSG_LOG3(TRACE,"MsgAddName: copy name (%s)\n\tto "
+ "shared data table (net,loc)(%d,%d)\n",
+ ncb.ncb_name, neti, i);
+ memcpy(SD_NAMES(neti,i),ncb.ncb_name, NCBNAMSZ);
+ //
+ // Set the name no.
+ //
+ SD_NAMENUMS(neti,i) = ncb.ncb_num ;
+ //
+ // Set new name flag
+ //
+ SD_NAMEFLAGS(neti,i) = NFNEW;
+ //
+ // Unlock share table
+ //
+ MsgDatabaseLock(MSG_RELEASE, "MsgAddName");
+
+ //
+ // START A SESSION for this name.
+ //
+
+ err_code = MsgNewName(neti,i);
+
+ if (err_code != NERR_Success) {
+ MSG_LOG(TRACE, "MsgAddName: A Session couldn't be "
+ "created for this name %d\n",err_code);
+
+
+ MSG_LOG(TRACE,"MsgAddName: Delete the name "
+ "that failed (%s)\n",ncb.ncb_name)
+ ncb.ncb_command = NCBDELNAME;
+
+ ncb.ncb_lana_num = net_lana_num[i];
+ net_err = Msgsendncb( &ncb, i);
+ if (net_err != 0) {
+ MSG_LOG(ERROR,"MsgAddName: Delete name "
+ "failed %d - pretend it's deleted anyway\n",net_err);
+ }
+
+ //
+ // Re-mark slot empty
+ //
+ SD_NAMEFLAGS(neti,i) = NFDEL;
+
+ MSG_LOG2(TRACE,"MsgAddName: UnLock slot %d in table "
+ "for net %d\n",i,neti);
+ MSG_LOG(TRACE,"MsgAddName: Name Deleted\n",0)
+ }
+ else {
+ //
+ //
+ // Wakeup the worker thread for that network.
+ //
+
+ SetEvent(wakeupSem[neti]);
+
+ }
+
+ }
+ else {
+ //
+ // else set error code
+ //
+ MSG_LOG(TRACE,
+ "MsgAddName: sendncb returned FAILURE 0x%x\n",
+ net_err);
+ err_code = MsgMapNetError(net_err);
+ //
+ // Re-mark slot empty
+ //
+ SD_NAMEFLAGS(neti,i) = NFDEL;
+ MSG_LOG2(TRACE,"MsgAddName: UnLock slot %d in table "
+ "for net %d\n",i,neti);
+ }
+ }
+
+ if ( err_code != NERR_Success ) {
+ //
+ //Try to delete the add names that were successful
+ //
+
+ for ( i = 0; i < neti; i++ ) {
+ MsgDatabaseLock(MSG_GET_EXCLUSIVE,"MsgAddName");
+
+ name_i = MsgLookupName(i,(char far *)(ncb.ncb_name));
+
+ if (name_i == -1) {
+ err_code = NERR_InternalError;
+ MsgDatabaseLock(MSG_RELEASE, "MsgAddName");
+ break;
+ }
+
+ MsgDatabaseLock(MSG_RELEASE, "MsgAddName");
+
+ //
+ // Delete name from card.
+ // If this call fails, we can't do much about it.
+ //
+ MSG_LOG1(TRACE,"MsgAddName: Delete the name that failed "
+ "for lana #%d\n",net_lana_num[i])
+ ncb.ncb_command = NCBDELNAME;
+
+ ncb.ncb_lana_num = net_lana_num[i];
+ Msgsendncb( &ncb, i);
+
+ //
+ // Re-mark slot empty
+ //
+ SD_NAMEFLAGS(i,name_i) = NFDEL;
+ MSG_LOG2(TRACE,"MsgAddName: UnLock slot %d in table "
+ "for net %d\n",i,neti);
+
+ }
+
+ //
+ // If an add was unsuccessful, stop the loop
+ //
+ break;
+
+ } // end else
+ } // end add names to net loop
+ } // end if ( !err_cd )
+
+ return(err_code); // Return status
+
+}
+
+NET_API_STATUS
+NetrMessageNameDel(
+ IN LPWSTR ServerName, // Blank = local, else remote.
+ IN LPWSTR Name // Pointer to name to be deleted
+ )
+
+/*++
+
+Routine Description:
+
+ This function deletes a name from the Message Server's name table.
+
+ This function is called to delete a name that has been added by the
+ user or by a remote computer via a Start Forwarding request to the
+ Message Server. The user has no way of specifying whether the given
+ name is an additional name or a forwarded name, but since forwarding
+ of messages to one's own computer is prohibited, both forms of the
+ name cannot exist on one machine (unless the message system has been
+ circumvented--a simple enough thing to do). The given name is looked
+ up in the shared data area, and, if it is found, a DELETE NAME net bios
+ call is issued. If this call is successful, then the Message Server
+ will remove the name from its name table in shared memory, so this
+ function does not have to do so.
+
+ SIDE EFFECTS
+
+ Calls the net bios. Accesses the shared data area.
+
+
+Arguments:
+
+ ServerName - Pointer to a string containing the name of the computer
+ that is to execute the API function.
+
+ Name - A pointer to the name to be deleted.
+
+
+Return Value:
+
+ NERR_Success - The operation was successful.
+
+--*/
+
+{
+ NCB ncb; // Network control block
+ DWORD flags; // Name flags
+ DWORD i; // Index into name table
+ DWORD neti; // Network Index
+
+ NET_API_STATUS status=0;
+ NET_API_STATUS end_result=0;
+ DWORD name_len;
+ UCHAR net_err;
+
+
+ UNUSED(ServerName);
+
+ //
+ // Wakeup the display thread so that any queue'd messages can be
+ // displayed.
+ //
+ MsgDisplayThreadWakeup();
+
+ //
+ // API security check. This call can be called by anyone locally,
+ // but only by admins in the remote case.
+ //
+
+ status = NetpAccessCheckAndAudit(
+ SERVICE_MESSENGER, // Subsystem Name
+ (LPWSTR)MESSAGE_NAME_OBJECT, // Object Type Name
+ MessageNameSd, // Security Descriptor
+ MSGR_MESSAGE_NAME_DEL, // Desired Access
+ &MsgMessageNameMapping); // Generic Mapping
+
+ if (status != NERR_Success) {
+ MSG_LOG(TRACE,
+ "NetrMessageNameDel:NetpAccessCheckAndAudit FAILED %X\n",
+ status);
+ return(ERROR_ACCESS_DENIED);
+ }
+
+ //
+ // Initialize the NCB
+ //
+ clearncb(&ncb);
+
+ //
+ // Format the username (this makes it non-unicode);
+ //
+ status = MsgFmtNcbName(ncb.ncb_name, Name, NAME_LOCAL_END);
+ if (status != NERR_Success) {
+ MSG_LOG(TRACE,"NetrMessageNameDel: could not format name\n",0);
+ return (NERR_NotLocalName);
+ }
+
+
+ end_result = NERR_Success;
+
+ //
+ // for all nets
+ //
+ for ( neti = 0; neti < SD_NUMNETS(); neti++ ) {
+
+ //
+ // Block until data free
+ //
+ MsgDatabaseLock(MSG_GET_EXCLUSIVE,"NetrMessageNameDel");
+
+ name_len = STRLEN(Name);
+
+ if((name_len > NCBNAMSZ) ||
+ ((i = MsgLookupName( neti, ncb.ncb_name))) == -1) {
+
+ //
+ // No such name to delete - exit
+ //
+ MsgDatabaseLock(MSG_RELEASE, "NetrMessageNameDel");
+ return(NERR_NotLocalName);
+ }
+
+ flags = SD_NAMEFLAGS(neti,i);
+
+
+ if( !(flags & (NFMACHNAME | NFLOCK)) &&
+ !(flags & NFFOR) ) {
+
+ //
+ // Show delete pending
+ //
+ SD_NAMEFLAGS(neti,i) |= NFDEL_PENDING;
+ }
+
+ MsgDatabaseLock(MSG_RELEASE, "NetrMessageNameDel");
+
+ if(flags & NFMACHNAME) {
+ //
+ // If name is computer name
+ //
+ return(NERR_DelComputerName);
+ }
+
+ if(flags & NFLOCK) {
+ //
+ // If name is locked
+ //
+ return(NERR_NameInUse);
+ }
+
+ //
+ // Delete the Name
+ //
+
+ ncb.ncb_command = NCBDELNAME; // Delete name (wait)
+ ncb.ncb_lana_num = net_lana_num[neti];
+
+ if( (net_err = Msgsendncb( &ncb, neti)) != 0 ) {
+
+ MSG_LOG(ERROR,"NetrMessageNameDel:send NCBDELNAME failed 0x%x\n",
+ net_err);
+ //
+ // The name that has been marked as delete pending was not
+ // successfully deleted so now go through all the work of
+ // finding the name again (cannot even use the same index
+ // in case deleted by another process) and remove the
+ // Del pending flag
+ //
+
+ //
+ // Attempt to block until data free but don't stop
+ // the recovery if can not block the data
+ //
+
+ MsgDatabaseLock(MSG_GET_EXCLUSIVE,"NetrMessageNameDel");
+
+ i = MsgLookupName(neti,ncb.ncb_name);
+ if(i != -1) {
+ SD_NAMEFLAGS(neti,i) &= ~NFDEL_PENDING;
+ }
+
+ MsgDatabaseLock(MSG_RELEASE, "NetrMessageNameDel");
+
+ status = MsgMapNetError(net_err); // Map network error status
+ end_result = NERR_IncompleteDel; // Unable to delete name
+ }
+
+ } // End for all nets
+
+ return(end_result);
+}
+
+
+
diff --git a/private/net/svcdlls/msgsvc/server/msgdata.h b/private/net/svcdlls/msgsvc/server/msgdata.h
new file mode 100644
index 000000000..399f9faf7
--- /dev/null
+++ b/private/net/svcdlls/msgsvc/server/msgdata.h
@@ -0,0 +1,66 @@
+/*****************************************************************/
+/** Microsoft LAN Manager **/
+/** Copyright(c) Microsoft Corp., 1990 **/
+/*****************************************************************/
+
+#ifndef _MSGDATA_INCLUDED
+#define _MSGDATA_INCLUDED
+
+#include <winsvc.h> // SERVICE_STATUS_HANDLE
+#include <lmsname.h> // SERVICE_MESSENGER
+#include <timelib.h>
+#include <msrv.h> // NCBNAMSZ
+#include <services.h> // LMSVCS_ENTRY_POINT, LMSVCS_GLOBAL_DATA
+//
+// See the file data.c for an explanation of all of these variables.
+//
+
+extern LPTSTR MessageFileName;
+
+extern LPBYTE dataPtr; // Pointer to shared data segment
+//extern ulfp dataSem; // Pointer to shared data access semaphore
+
+
+extern PNCB *ncbArray; // Two dimensional array of NCBs
+extern LPBYTE *ncbBuffers; // Two-D array of NCB Buffers
+
+
+extern PCHAR *mpncbistate; // Message transfer state flags
+extern PSHORT *mpncbimgid; // Message group i.d. numbers
+
+//extern void (*(**mpncbifun))(short, int, char); // Service routines
+extern LPNCBIFCN **mpncbifun;
+
+extern LPNCB_STATUS *ncbStatus; // NCB Status structures
+
+
+// extern USHORT *NetBios_Hdl; // NetBios handles, one per net
+extern LPBYTE net_lana_num; // Lan adaptor numbers
+extern PHANDLE wakeupSem; // Event to set on NCB completion
+
+
+extern TCHAR machineName[NCBNAMSZ+sizeof(TCHAR)]; // The local machine name
+
+extern SHORT MachineNameLen; // The length of the machine name
+
+extern SHORT mgid; // The message group i.d. counter
+
+extern USHORT g_install_state;
+
+
+extern SERVICE_STATUS_HANDLE MsgrStatusHandle;
+
+extern NET_TIME_FORMAT GlobalTimeFormat;
+extern CRITICAL_SECTION TimeFormatCritSec;
+extern HANDLE TimeFormatEvent;
+
+extern LPSTR GlobalTimePlaceHolder;
+
+extern LPWSTR DefaultMessageBoxTitle;
+extern LPWSTR GlobalAllocatedMsgTitle;
+extern LPWSTR GlobalMessageBoxTitle;
+
+extern PLMSVCS_GLOBAL_DATA MsgsvcGlobalData;
+
+#endif // _MSGDATA_INCLUDED
+
diff --git a/private/net/svcdlls/msgsvc/server/msgdbg.h b/private/net/svcdlls/msgsvc/server/msgdbg.h
new file mode 100644
index 000000000..90a602620
--- /dev/null
+++ b/private/net/svcdlls/msgsvc/server/msgdbg.h
@@ -0,0 +1,104 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ msgdbg.h
+
+Abstract:
+
+ Contains definitions used in debugging the messenger service.
+
+Author:
+
+ Dan Lafferty (danl) 08-Jul-1991
+
+Environment:
+
+ User Mode -Win32
+
+Revision History:
+
+ 14-Jan-1993 Danl
+ Created MSG_LOG functions for various number of arguments (up to 3).
+
+--*/
+
+#ifndef _MSGDBG_INCLUDED
+#define _MSGDBG_INCLUDED
+
+//
+// Information levels used in switch statements.
+//
+#define LEVEL_0 0L
+#define LEVEL_1 1L
+#define LEVEL_2 2L
+
+//
+// Debug macros and constants.
+//
+extern DWORD MsgsvcDebugLevel;
+
+//
+// The following allow debug print syntax to look like:
+//
+// SC_LOG(DEBUG_TRACE, "An error occured %x\n",status)
+//
+
+#if DBG
+
+#define MSG_LOG0(level,string) \
+ if( MsgsvcDebugLevel & (DEBUG_ ## level)){ \
+ DbgPrint("[MSGR]"); \
+ DbgPrint(string); \
+ }
+
+#define MSG_LOG1(level,string,var) \
+ if( MsgsvcDebugLevel & (DEBUG_ ## level)){ \
+ DbgPrint("[MSGR]"); \
+ DbgPrint(string,var); \
+ }
+
+#define MSG_LOG2(level,string,var1,var2) \
+ if( MsgsvcDebugLevel & (DEBUG_ ## level)){ \
+ DbgPrint("[MSGR]"); \
+ DbgPrint(string,var1,var2); \
+ }
+
+#define MSG_LOG3(level,string,var1,var2,var3) \
+ if( MsgsvcDebugLevel & (DEBUG_ ## level)){ \
+ DbgPrint("[MSGR]"); \
+ DbgPrint(string,var1,var2,var3); \
+ }
+
+#define MSG_LOG(level,string,var) \
+ if( MsgsvcDebugLevel & (DEBUG_ ## level)){ \
+ DbgPrint("[MSGR]"); \
+ DbgPrint(string,var); \
+ }
+
+#define STATIC
+
+#else //DBG
+
+#define MSG_LOG0(level,string)
+#define MSG_LOG1(level,string,var)
+#define MSG_LOG2(level,string,var1,var2)
+#define MSG_LOG3(level,string,var1,var2,var3)
+#define MSG_LOG(level,string,var)
+
+#define STATIC static
+
+#endif //DBG
+
+#define DEBUG_NONE 0x00000000
+#define DEBUG_ERROR 0x00000001
+#define DEBUG_TRACE 0x00000002
+#define DEBUG_LOCKS 0x00000004
+#define DEBUG_GROUP 0x00000008
+
+#define DEBUG_ALL 0xffffffff
+
+#endif // _MSGDBG_INCLUDED
+
diff --git a/private/net/svcdlls/msgsvc/server/msginit.c b/private/net/svcdlls/msgsvc/server/msginit.c
new file mode 100644
index 000000000..2fcd7a20f
--- /dev/null
+++ b/private/net/svcdlls/msgsvc/server/msginit.c
@@ -0,0 +1,1251 @@
+/*++
+
+Copyright (c) 1991-92 Microsoft Corporation
+
+Module Name:
+
+ msginit.c
+
+Abstract:
+
+ Messenger Service Initialization Routines.
+ The following is a list of functions in this file:
+
+ MsgInitializeMsgr
+ BufferInit
+ InitSharedData
+ SetComputerName
+ GetNumNets
+ MsgGetBufSize
+ SetUpMessageFile
+
+Author:
+
+ Dan Lafferty (danl) 18-Jul-1991
+
+Environment:
+
+ User Mode - Win32
+
+Notes:
+
+ optional-notes
+
+Revision History:
+
+ 27-Jun-1995 AnirudhS
+ LocalFree(dataPtr) must be called AFTER MsgFreeSupportSeg, because
+ the latter tries to _close a handle stored in dataPtr.
+
+ 08-Feb-1994 Danl
+ Removed the restriction that the memory allocated had to be
+ restricted to less than a 64K segment. We don't worry about
+ segments anymore.
+
+ 12-Jan-1993 Danl
+ In error paths where we call MsgCloseWakeupSems, I need to do the
+ LocalFree(dataPtr) after the call to MsgCloseWakeupSems. Otherwise,
+ it access violates because MsgCloseWakeupSems uses the shared date
+ in the block pointed to by the dataPtr.
+
+ 21-Apr-1992 JohnRo
+ Fixed bug printing a status when message name add fails.
+ Changed to use FORMAT_ equates throughout.
+
+ 18-Feb-1992 ritaw
+ Convert to Win32 service control APIs.
+
+ 18-Jul-1991 danl
+ Created as a composite of the original LM2,0 routines.
+
+
+--*/
+//
+// Includes
+//
+
+#include <stdlib.h> // atol
+#include "msrv.h" // Messenger prototypes and constants
+#include <winsvc.h> // Service control APIs
+
+#include <netdebug.h> // NetpAssert, FORMAT_ equates.
+#include <rpc.h> // DataTypes and runtime APIs
+#include <msgsvc.h> // generated by the MIDL complier
+
+#include <netlibnt.h> // NetpNtStatusToApiStatus prototypes
+#include <rpcutil.h> // NetpStartRpcServer
+
+#include <tstring.h> // Unicode string macros
+#include <string.h> // memcpy
+#include <lmwksta.h> // NetWrkstaTransportEnum
+#include <lmapibuf.h> // NetApiBufferFree
+#include <netlib.h> // UNUSED macro
+#include <msgrutil.h> // NetpNetBiosReset
+
+#include "msgdbg.h" // MSG_LOG
+#include "heap.h" // heap management routines and macros.
+#include "msgdata.h" // Global data
+#include "msgsec.h" // Messenger security information
+
+#include "msgnames.h" // MSGR_INTERFACE_NAME
+#include "msgtext.h" // MTXT_MsgsvcTitle
+
+//
+// Defines
+//
+// The following is the size of the first offset area in the shared buffer.
+// The size of this area is dependent on a runtime variable - the number
+// of nets. The BOOKKEEPING_SIZE is calculated from the SD offsets
+// described in msrv.h.
+//
+
+#define BOOKKEEPING_SIZE(n) (SDNAMEFLAGS + \
+ (n * NCBMAX) + \
+ (n * NCBMAX) + \
+ (n * NCBMAX * (NCBNAMSZ+4)) + \
+ (n * NCBMAX * (NCBNAMSZ+4)) + \
+ (n * NCBMAX * sizeof(DWORD)))
+
+#define MAXSEG (0xffff)
+
+#define LMI_PARM_M_SIZMESSBUF TEXT("/sizmessbuf")
+
+
+//
+// Global Data
+//
+
+ static DWORD bufferSize; // Message buffer size
+ static DWORD msrv_pid; // pid of message server
+
+ extern LPTSTR MessageFileName;
+
+//
+// Local Function Prototypes
+//
+
+STATIC VOID
+MsgBufferInit(
+ IN USHORT buflen
+ );
+
+NET_API_STATUS
+MsgInitSharedData(
+ DWORD NumNets
+ );
+
+STATIC BOOL
+MsgSetComputerName(
+ DWORD NumNets
+ );
+
+STATIC DWORD
+MsgGetNumNets(VOID);
+
+NET_API_STATUS
+MsgGetBufSize (
+ IN DWORD argc,
+ IN LPTSTR *argv,
+ OUT LPDWORD bufferSize,
+ OUT LPTSTR *lastarg
+ );
+
+DWORD
+MsgSetUpMessageFile(VOID);
+
+
+STATIC VOID
+MsgInitMessageBoxTitle(
+ VOID
+ );
+
+
+NET_API_STATUS
+MsgInitializeMsgr(
+ DWORD argc,
+ LPTSTR *argv
+ )
+
+/*++
+
+Routine Description:
+
+ Registers the control handler with the dispatcher thread. Then it
+ performs all initialization including the starting of the RPC server.
+ If any of the initialization fails, MsgStatusUpdate is called so that the
+ status is updated and the thread is terminated.
+
+Arguments:
+
+
+
+Return Value:
+
+
+
+--*/
+
+{
+ NET_API_STATUS status;
+ LPTSTR lastarg;
+ DWORD NumNets;
+ DWORD hint_count = 0;
+ DWORD msgrState;
+ DWORD bufLen;
+
+ //
+ // Initialize the Time format. Since no users are logged on, we use
+ // the information for the default user.
+ //
+ InitializeCriticalSection(&TimeFormatCritSec);
+ NetpGetTimeFormat(&GlobalTimeFormat);
+
+ //
+ // Initialize the Thread Manager. This initializes some locks used
+ // on the Thread and Status databases.
+ //
+ MsgThreadManagerInit();
+
+ //
+ // Initialize the status structure
+ //
+ MsgStatusInit();
+
+ //
+ // Register this service with the ControlHandler.
+ // Now we can accept control requests and be requested to UNINSTALL.
+ //
+
+ MSG_LOG(TRACE, "Calling NetServiceRegisterCtrlHandler\n",0);
+ if ((MsgrStatusHandle = RegisterServiceCtrlHandler(
+ SERVICE_MESSENGER,
+ MsgrCtrlHandler
+ )) == (SERVICE_STATUS_HANDLE) NULL) {
+
+ status = GetLastError();
+
+ MSG_LOG(ERROR,
+ "FAILURE: RegisterServiceCtrlHandler status = " FORMAT_API_STATUS
+ "\n", status);
+
+ return( MsgBeginForcedShutdown (
+ IMMEDIATE,
+ status));
+ }
+
+ //
+ // Notify that installation is pending
+ //
+
+ msgrState = MsgStatusUpdate(STARTING);
+
+ if (msgrState != STARTING) {
+ //
+ // An UNINSTALL control request must have been received
+ //
+ return(msgrState);
+ }
+
+ //
+ // Check that the workstation is started
+ //
+
+ MSG_LOG(TRACE, "Calling NetServiceControl\n",0);
+
+ if (! NetpIsServiceStarted(SERVICE_WORKSTATION)) {
+
+ MSG_LOG(ERROR, "WorkStation Service is not started\n",0);
+
+ return (MsgBeginForcedShutdown(
+ IMMEDIATE,
+ NERR_WkstaNotStarted));
+ }
+
+ // *** INSTALLATION HINT ***
+ msgrState = MsgStatusUpdate(STARTING);
+ if (msgrState != STARTING) {
+ return(msgrState);
+ }
+
+ //
+ // Check command line for buffer size. If none given,
+ // use the default buffer size.
+ // lastarg is a pointer to the argument string that was found
+ // to be bad.
+ //
+ status = MsgGetBufSize ( argc, argv, &bufferSize, &lastarg );
+
+ if (status != NERR_Success) {
+ MSG_LOG(ERROR, "MsgGetBufSize Failed\n",0);
+ return (MsgBeginForcedShutdown(
+ IMMEDIATE,
+ status));
+ }
+
+ // *** INSTALLATION HINT ***
+ msgrState = MsgStatusUpdate(STARTING);
+ if (msgrState != STARTING) {
+ return(msgrState);
+ }
+
+
+ if (bufferSize > MAX_SIZMESSBUF || bufferSize < MIN_SIZMESSBUF) {
+ MSG_LOG(ERROR, "Message Buffer Size is illegal\n",0);
+ return (MsgBeginForcedShutdown(
+ IMMEDIATE,
+ ERROR_INVALID_PARAMETER));
+ }
+
+ //
+ // This is the size of the buffer (that SDBUFFER points to) in the
+ // shared data area. This is calculated as:
+ //
+ // The size of a message buffer (bufferSize)
+ // plus
+ // space for 4 Multi-block message headers and names,
+ // plus
+ // space for one Multi-block text header for each text block that
+ // fits into the message buffer. (bufferSize/TXTMAX).
+ //
+ // The number of headers is rounded up by one.
+ // (bufferSize+TXTMAX-1)/TXTMAX
+ //
+ bufferSize += (4 * (sizeof(MBB) + (2 * NCBNAMSZ))) +
+ ((( (bufferSize+TXTMAX-1)/TXTMAX) + 1) * sizeof(MBT));
+
+ MSG_LOG(TRACE, "Calling MsgGetNumNets\n",0);
+ NumNets = MsgGetNumNets();
+ if ( NumNets == 0 ) {
+ MSG_LOG(ERROR, "No nets to connect to\n",0);
+ return (MsgBeginForcedShutdown(
+ IMMEDIATE,
+ NERR_NoNetworkResource));
+ }
+
+ // ***** INSTALLATION HINT *****
+ msgrState = MsgStatusUpdate(STARTING);
+ if (msgrState != STARTING) {
+ return(msgrState);
+ }
+
+ //
+ // Initialize shared memory areas.
+ //
+ MSG_LOG(TRACE, "Calling MsgInitSharedData\n",0);
+ status = MsgInitSharedData(NumNets);
+
+ if (status != NERR_Success) {
+ MSG_LOG(ERROR, "InitSharedData Failure\n",0);
+ return (MsgBeginForcedShutdown(
+ IMMEDIATE,
+ NERR_NoRoom));
+ }
+
+ // ***** INSTALLATION HINT *****
+ msgrState = MsgStatusUpdate(STARTING);
+ if (msgrState != STARTING) {
+ return(msgrState);
+ }
+
+ //*****************************************
+ //
+ // STUFF FROM Init_msrv() in MSRV.C
+ //
+ //*****************************************
+
+ heap = SD_BUFFER(); // Initialize data heap pointer
+ heapln = SD_BUFLEN(); // Initialize data heap length
+
+ //
+ // Set up the segement to hold the net bios handles, lana-nums
+ // and wakeup Semaphores.
+ //
+
+ MSG_LOG(TRACE, "Calling MsgInitSupportSeg\n",0);
+ if ( MsgInitSupportSeg() )
+ {
+ LocalFree (dataPtr); // Free shared data
+ MSG_LOG(ERROR, "InitSupportSeg Failed\n",0);
+ return (MsgBeginForcedShutdown(
+ IMMEDIATE,
+ NERR_NoRoom));
+ }
+
+ //
+ // Now initialize global net bios handles & lana nums.
+ //
+
+ MSG_LOG(TRACE, "Calling MsgInit_NetBios\n",0);
+ if (!MsgInit_NetBios())
+ {
+ MsgFreeSupportSeg();
+ LocalFree (dataPtr); // Free shared data
+ MSG_LOG(ERROR, "Init_NetBios Failed\n",0);
+ return (MsgBeginForcedShutdown(
+ IMMEDIATE,
+ ERROR_OPEN_FAILED));
+ }
+
+ //
+ // Get the wake up semaphore handles.
+ //
+
+ MSG_LOG(TRACE, "Calling MsgCreateWakeupSems\n",0);
+ if (!MsgCreateWakeupSems(SD_NUMNETS())) {
+ MsgCloseWakeupSems(); // Close the ones that have been created
+ MsgFreeSupportSeg();
+ LocalFree (dataPtr); // Free shared data
+ MSG_LOG(ERROR, "CreateWakeupSems Failed\n",0);
+ return (MsgBeginForcedShutdown(
+ IMMEDIATE,
+ NERR_NoRoom));
+ }
+
+ //
+ // Ask the Worksta for the computer name. If the computer
+ // has no name, then abort.
+ //
+ // The computername and the username are in unicode format.
+ //
+ // NOTE: the username that is returned is a name we may want to add
+ // to the table.
+ //
+
+ MSG_LOG(TRACE, "Getting the ComputerName\n",0);
+
+ bufLen = sizeof(machineName);
+
+ *machineName = TEXT('\0');
+
+ if (!GetComputerName(machineName,&bufLen)) {
+ MSG_LOG(ERROR,"GetComputerName failed \n",0);
+ status = GetLastError();
+ }
+
+
+ if ( (status != NERR_Success) ||
+ (*machineName == TEXT('\0')) || (*machineName == TEXT(' '))) {
+
+ //
+ // fatal error if no name
+ //
+ MsgCloseWakeupSems();
+ MsgFreeSupportSeg();
+ LocalFree (dataPtr); // Free shared data
+ MSG_LOG(ERROR, "GetWkstaNames Failed\n",0);
+ return (MsgBeginForcedShutdown(
+ IMMEDIATE,
+ NERR_NoComputerName));
+ }
+ machineName[NCBNAMSZ] = TEXT('\0'); // make sure it's terminated
+ MachineNameLen = (SHORT) STRLEN(machineName);
+
+ //
+ // Open NETBIOS for use by messenger.
+ // If any failures occur beyond this we must remember to close.
+ //
+ MsgsvcGlobalData->NetBiosOpen();
+
+ //
+ // Set computer name on adapters - if any
+ //
+ MSG_LOG(TRACE, "Calling MsgSetComputerName\n",0);
+ if(MsgSetComputerName(SD_NUMNETS()) != 0) {
+ MsgCloseWakeupSems();
+ MsgFreeSupportSeg();
+ LocalFree (dataPtr); // Free shared data
+ MSG_LOG(ERROR, "SetComputerName Failed\n",0);
+ return (MsgBeginForcedShutdown(
+ IMMEDIATE,
+ NERR_NoComputerName));
+ }
+
+ //
+ // Change from IMMEDIATE Shutdowns to PENDING shutdowns.
+ // This is because at this point we have names on the adapters
+ // to clean up.
+ //
+
+
+ //
+ // The NCBs and NCB buffers belong in a seperate segment so
+ // DosAlloc the segment here.
+ //
+
+ MSG_LOG(TRACE, "Calling MsgInitNCBSeg\n",0);
+ if( MsgInitNCBSeg() )
+ {
+ MSG_LOG(ERROR, "InitNCBSeg Failed\n",0);
+ return (MsgBeginForcedShutdown(
+ PENDING,
+ NERR_NoRoom));
+ }
+
+ //
+ // Allocate the the Service segment.
+ //
+
+ MSG_LOG(TRACE, "Calling MsgInitServiceSeg\n",0);
+ if ( MsgInitServiceSeg() )
+ {
+ MSG_LOG(ERROR, "InitServiceSeg Failed\n",0);
+ return (MsgBeginForcedShutdown(
+ PENDING,
+ NERR_NoRoom));
+ }
+
+ //
+ // Build the name of the file that is to be used to get the
+ // message header and tail. Don't care about errors, since
+ // any error will result in the file not being used and is
+ // resorting to the old standby strings.
+ //
+
+ MSG_LOG(TRACE, "Calling MsgSetUpMessageFile\n",0);
+ MsgSetUpMessageFile();
+
+ //
+ // Start the Group messenger thread to handle all domain messaging
+ // SD_NUMNETS() is the position in the wakeupSem array for the
+ // mailslot event.
+ //
+
+ MSG_LOG(TRACE, "Calling MsgInitGroupSupport\n",0);
+ if ( status = MsgInitGroupSupport( SD_NUMNETS() ) )
+ {
+ MSG_LOG(ERROR, "InitGroupSupport Failed\n",0);
+ return (MsgBeginForcedShutdown(
+ PENDING,
+ NERR_GrpMsgProcessor));
+ }
+
+ //
+ // Initialize the Display Code
+ //
+ if (!MsgDisplayInit()) {
+ MSG_LOG(ERROR, "Could not initialize the display functions\n",0);
+ return (MsgBeginForcedShutdown(
+ PENDING,
+ NERR_NoRoom)); // BUGBUG: Make this meaningful!
+ }
+
+
+ //
+ // Create the security descriptor that is to be used in access
+ // checks on the API interface.
+ //
+
+ MSG_LOG(TRACE, "Calling MsgCreateMessageNameObject\n",0);
+ status = MsgCreateMessageNameObject();
+
+ if (status != NERR_Success) {
+ MSG_LOG(ERROR, "MsgCreateMessageNameObject (security descriptor) "
+ "Failed\n", 0);
+ return (MsgBeginForcedShutdown(
+ PENDING,
+ status));
+ }
+
+ //
+ // Initialize the text for the message box title.
+ //
+ MsgInitMessageBoxTitle();
+
+ //
+ // Installation is successful and complete. If there is a
+ // user logged on then an attempt is made to add the user name
+ // to this message server. No attempt is made at error reporting
+ // if this fails, there may not be a user logged on, and if there is,
+ // the user name may already exist as a message name on another
+ // station.
+ //
+ // This is when we add usernames to the message table if we can.
+ // Sometime this needs to handle multiple users??? (not in version 1)
+ //
+
+ MsgAddUserNames();
+
+
+ //
+ // Start the Messengers RPC server.
+ //
+ // NOTE: Now all RPC servers in services.exe share the same pipe name.
+ // However, in order to support communication with version 1.0 of WinNt,
+ // it is necessary for the Client Pipe name to remain the same as
+ // it was in version 1.0. Mapping to the new name is performed in
+ // the Named Pipe File System code.
+ //
+
+ MSG_LOG(TRACE,
+ "MsgInitializeMsgr:Getting ready to start RPC server\n",0);
+
+ status = MsgsvcGlobalData->StartRpcServer(
+ MsgsvcGlobalData->SvcsRpcPipeName,
+ msgsvc_ServerIfHandle);
+
+ if (status != RPC_S_OK) {
+ MSG_LOG(ERROR, "RPC Initialization Failed " FORMAT_RPC_STATUS "\n",
+ status);
+
+ return (MsgBeginForcedShutdown(
+ PENDING,
+ status));
+ }
+
+ //
+ // Update the status to indicate that installation is complete.
+ // Get the current state back in case the ControlHandling thread has
+ // told us to shutdown.
+ //
+
+ MSG_LOG(TRACE, "Exiting MsgInitializeMsgr - Init Done!\n",0);
+
+ return (MsgStatusUpdate(RUNNING));
+}
+
+STATIC VOID
+MsgBufferInit(
+ IN USHORT buflen // Buffer length
+ )
+
+/*++
+
+Routine Description:
+
+ This function is called during initialization to set up
+ the message buffer in the shared data area.
+
+ This function assumes that the shared data area is locked
+ in memory, that the access semaphore for the shared data
+ area is set, and that the global far pointer, dataPtr, is
+ valid. BufferInit() initializes the heap structure of the
+ buffer.
+
+ SIDE EFFECTS
+
+ The buffer in shared memory is initialized.
+
+Arguments:
+
+ buflen - buffer length
+
+Return Value:
+
+ none
+
+--*/
+
+{
+ LPHEAPHDR hp; // Heap block pointer
+
+ hp = (LPHEAPHDR) SD_BUFFER(); // Get the address of buffer
+ HP_SIZE(*hp) = buflen; // Set the size of the first block
+ HP_FLAG(*hp) = 0; // Unallocated
+ SD_BUFLEN() = buflen; // Save the length of the buffer
+}
+
+DWORD
+MsgInitSharedData(
+ DWORD NumNets
+ )
+
+/*++
+
+Routine Description:
+
+ This function creates and initializes the shared data area.
+ It sets up the computer name and initializes the message
+ buffer.
+
+ SIDE EFFECTS
+
+ Calls MsgBufferInit().
+
+
+Arguments:
+
+ NumNets - Number of network adapters to support.
+
+Return Value:
+
+ RETURN
+ NERR_Success if the operation was successful
+
+ ERROR_NOT_ENOUGH_MEMORY - If the memory alloc for the shared
+ memory segment fails.
+
+--*/
+
+{
+
+ DWORD i,j; // Index
+ ULONG size;
+
+ //
+ // Create and initialize shared data area.
+ //
+ size = bufferSize + BOOKKEEPING_SIZE(NumNets);
+
+ dataPtr = (LPBYTE)LocalAlloc(LMEM_ZEROINIT, size);
+ if (dataPtr == NULL) {
+ MSG_LOG(ERROR,"[MSG]InitSharedData:LocalAlloc Failure "
+ FORMAT_API_STATUS "\n", GetLastError());
+ return(ERROR_NOT_ENOUGH_MEMORY);
+ }
+
+ //
+ // Initialize the shared data lock. The shared data is shared between
+ // the API threads and the worker threads.
+ //
+ MsgDatabaseLock(MSG_INITIALIZE,"InitSharedData");
+
+ //
+ // Initialize the "used-to-be shared" data
+ //
+ SD_NUMNETS() = NumNets;
+ SD_MSRV() = 0; // No message server active
+ SD_LOGNAM()[0] = '\0'; // No log file yet
+ SD_MESLOG() = 0; // Message logging disabled
+ SD_MESQF() = INULL; // Message queue is empty
+ SD_MESQB() = INULL;
+
+ for ( j = 0; j < SD_NUMNETS(); j++ ) {
+
+ for(i = 0; i < NCBMAX; ++i) {
+
+ //
+ // Mark entries as free
+ //
+ SD_NAMEFLAGS(j,i) = NFDEL;
+ }
+ }
+
+ //
+ // Initialize the message buffer
+ //
+ MsgBufferInit((USHORT)bufferSize);
+
+ //
+ // NT NOTE:
+ // Skip Initializing the Support Set and Wakeup sems.
+ // Init_msrv will end up doing that.
+ //
+
+ return(NERR_Success);
+}
+
+STATIC BOOL
+MsgSetComputerName(
+ IN DWORD NumNets
+ )
+
+/*++
+
+Routine Description:
+
+ This function sets up the shared data area for the computer name
+ so that it can receive messages.
+
+ This function sets things up so that the computer name will be able
+ to receive messages. First, it adds the user name form of the computer
+ name to the local adapter. If successful, it then initializes one slot
+ in the name table in the shared data area: a computer name
+ receiving messages
+
+ SIDE EFFECTS
+
+ Locks the init data segment around net bios usage.
+ Calls the net bios. Makes entries in the shared data area.
+
+
+Arguments:
+
+ NumNets - The number of network adapters that is supported
+
+Return Value:
+
+ 0 = success
+ non-zero = failure
+
+--*/
+
+{
+ NET_API_STATUS status;
+ NCB ncb; /* Network Control Block */
+// ASTAT adapterStatus; /* Adapter status buffer */
+ UCHAR res;
+ DWORD i;
+ unsigned short j;
+
+ struct {
+ ADAPTER_STATUS AdapterStatus;
+ NAME_BUFFER NameBuffer[16];
+ } Astat;
+
+
+ //
+ // Loop for each net.
+ //
+
+ for ( i = 0; i < NumNets; i++ )
+ {
+ // NEW-NEW-NEW-NEW-NEW-NEW-NEW-NEW-NEW-NEW-NEW-NEW-NEW-NEW-NEW
+ //
+ // Reset the adapter
+ //
+ MSG_LOG1(TRACE,"Calling NetBiosReset for lana #%d\n",net_lana_num[i]);
+ status = MsgsvcGlobalData->NetBiosReset(net_lana_num[i]);
+
+ if (status != NERR_Success) {
+ MSG_LOG(ERROR,"MsgSetComputerName: NetBiosReset failed "
+ FORMAT_API_STATUS "\n", status);
+ MSG_LOG(ERROR,"MsgSetComputerName: AdapterNum " FORMAT_DWORD
+ "\n",i);
+ //
+ // If it fails, skip to the Next Net.
+ //
+ continue;
+ }
+ //
+ //
+ // NEW-NEW-NEW-NEW-NEW-NEW-NEW-NEW-NEW-NEW-NEW-NEW-NEW-NEW-NEW
+
+ //
+ // Set call name for local adapter
+ //
+ clearncb(&ncb);
+ status = MsgFmtNcbName( ncb.ncb_name, machineName, 3);
+ if (status != NERR_Success) {
+ MSG_LOG(ERROR,"SetComputerName: Format name failed!",0);
+ return ((BOOL)1);
+ }
+ ncb.ncb_command = NCBADDNAME; // Add name (wait)
+ ncb.ncb_lana_num = net_lana_num[i]; // Use the LANMAN adapter
+
+ //
+ // Copy the name
+ // (At this point the name is ansi - not unicode)
+ //
+ memcpy(SD_NAMES(i,0), ncb.ncb_name, NCBNAMSZ);
+
+
+ MSG_LOG1(TRACE,"MsgSetComputerName: Adding ComputerName to lana #%d\n",
+ net_lana_num[i]);
+
+ if( (res = Msgsendncb( &ncb, i)) != 0)
+ {
+ MSG_LOG1(TRACE,"SetComputerName: NetBios ADDNAME failed 0x%x\n",res);
+
+ if( (res & 0xff) == NRC_DUPNAME)
+ {
+ //
+ // If the name already exists on the adapter card (the
+ // workstation may have added it), we want to get the
+ // name number and pretend that we just added it.
+ //
+ // Name already exists. Issue an ASTAT to find the name
+ // number.
+ //
+ clearncb(&ncb);
+ ncb.ncb_buffer = (char FAR *) &Astat; // Set buffer address
+ ncb.ncb_length = sizeof(Astat); // Set buffer length
+ ncb.ncb_callname[0] = '*'; // local adapter status
+ ncb.ncb_command = NCBASTAT; // Adapter status (wait)
+
+ res = Msgsendncb(&ncb,i);
+ if( res != NRC_GOODRET)
+ {
+ //
+ // Failed to add name
+ //
+
+ //Close_NetBios();
+ MSG_LOG1(ERROR, "SetComputerName:sendncb (ASTAT) failed 0x%x\n",res);
+ return((BOOL)1);
+ }
+
+ //
+ // Loop to name number
+ //
+ for(j = 0; j< Astat.AdapterStatus.name_count; ++j) {
+ if(((Astat.NameBuffer[j].name_flags & 7) == 4) &&
+ (memcmp( Astat.NameBuffer[j].name,
+ SD_NAMES(i,0),
+ NCBNAMSZ) == 0)) {
+
+ break; // Found the name
+ }
+ }
+
+ if( j == Astat.AdapterStatus.name_count)
+ {
+ //
+ // Failed to find
+ //
+
+ //Close_NetBios();
+ MSG_LOG(ERROR,
+ "SetComputerName:DupName-failed to find NameNum\n",0);
+ return((BOOL)1);
+ }
+ SD_NAMENUMS(i,0) = Astat.NameBuffer[j].name_num; // Save num
+ MSG_LOG1(TRACE,"SetComputerName: use existing name num (%d) instead\n",
+ Astat.NameBuffer[j].name_num);
+ }
+ else
+ {
+ //
+ // Fail if name not on the card after the call
+ //
+
+ // Close_NetBios();
+ MSG_LOG(ERROR, "SetComputerName:Name Not on Card. netbios rc = 0x%x\n",res);
+ return((BOOL)1);
+ }
+ }
+ else
+ {
+ SD_NAMENUMS(i,0) = ncb.ncb_num; // Save the name number
+ }
+
+
+ SD_NAMEFLAGS(i,0) = NFNEW | NFMACHNAME; // Name is new
+
+
+ } // End for all nets
+
+ // Close_NetBios();
+ return((BOOL)0); /* Success */
+}
+
+STATIC DWORD
+MsgGetNumNets(VOID)
+
+/*++
+
+Routine Description:
+
+
+
+Arguments:
+
+
+
+Return Value:
+
+
+
+--*/
+{
+
+ NCB ncb;
+ LANA_ENUM lanaBuffer;
+ unsigned char nbStatus;
+
+
+ //
+ // Find the number of networks by sending an enum request via Netbios.
+ //
+
+ clearncb(&ncb);
+ ncb.ncb_command = NCBENUM; // Enumerate LANA nums (wait)
+ ncb.ncb_buffer = (char FAR *)&lanaBuffer;
+ ncb.ncb_length = sizeof(LANA_ENUM);
+
+ nbStatus = Netbios (&ncb);
+ if (nbStatus != NRC_GOODRET) {
+ MSG_LOG(ERROR, "GetNumNets:Netbios LanaEnum failed rc="
+ FORMAT_DWORD "\n", (DWORD) nbStatus);
+ return(FALSE);
+ }
+
+ return((DWORD)lanaBuffer.length);
+
+#ifdef replaced
+
+ LPBYTE transportInfo;
+ int count=0;
+ USHORT loopback_found = 0;
+ NET_API_STATUS status;
+ DWORD entriesRead;
+ DWORD totalEntries;
+
+ //
+ // First try and find the networks mananged by the LAN manager
+ //
+ // NOTE: This call will fail if there are more than MSNGR_MAX_NETS
+ // in the machine. This is not a problem unless there are fewer
+ // than MSNGR_MAX_NETS that would qualify for messaging service.
+ // In this case, it might be argued that the messenger should start.
+ // For now, this is not the case. - ERICPE
+ //
+
+ status = NetWkstaTransportEnum (
+ NULL, // server name (local)
+ 0, // level
+ &transportInfo, // bufptr
+ -1, // preferred maximum length
+ &entriesRead, // entries read
+ &totalEntries, // total entries
+ NULL); // resumeHandle
+
+ //
+ // Free up the buffer that RPC allocated for us.
+ //
+ NetApiBufferFree(transportInfo);
+
+ if (status != NERR_Success) {
+ MSG_LOG(ERROR,"GetNumNets:NetWkstaTransportEnum failed "
+ FORMAT_API_STATUS "\n", status);
+ return(0);
+ }
+ MSG_LOG(TRACE,"GetNumNets: numnets = " FORMAT_DWORD "\n", totalEntries);
+
+ return(totalEntries);
+#endif
+}
+
+NET_API_STATUS
+MsgGetBufSize (
+ IN DWORD argc,
+ IN LPTSTR *argv,
+ OUT LPDWORD bufferSize,
+ OUT LPTSTR *lastarg
+ )
+
+/*++
+
+Routine Description:
+
+ This routine parses through the command line arguments in search of
+ the buffer size. This is the only valid argument and is of the
+ following form:
+ /sizmessbuf:[bytes]
+ Where bytes is the number of bytes for the message buffer defining
+ the size of messages the messenger can receive. The range is
+ 512-62000. The default is 4096.
+
+ If a passed in sizmessbuf is an illegal value, an error will be returned.
+
+ Arguments that are not switches (prefixed by "/") are ignored. Invalid
+ switches cause a failure, and the text of the bad switch is passed back
+ in lastarg.
+
+Arguments:
+
+ argc - This is the count of the number of arguments that is passed in.
+
+ argv - This is a pointer to an array of pointers to arguments.
+
+ bufferSize - This is a pointer to where the buffer size is to be stored.
+ If a bufferSize was not found on the command line, the default
+ is placed in here.
+
+ lastarg - This is a pointer to a location where a pointer to a
+ switch argument that was in error is to be placed.
+
+Return Value:
+
+ NERR_Success - No errors, the returned bufferSize is valid and
+ lastarg does not contain any error information.
+
+ ERROR_INVALID_PARAMETER - The command line switch is not a valid one for
+ this service.
+
+
+--*/
+{
+ DWORD switchNameSize;
+ BOOL fromCmdLine = FALSE;
+ DWORD i;
+ LPTSTR pSizeString;
+
+ //
+ // Check the passed in command args.
+ //
+ *lastarg = NULL;
+ *bufferSize = 0;
+
+ for (i=0; i<argc; i++) {
+ if ( *argv[i] == '/') {
+ switchNameSize = STRLEN(LMI_PARM_M_SIZMESSBUF);
+
+ if (STRNICMP (argv[i], LMI_PARM_M_SIZMESSBUF, switchNameSize) == 0) {
+ //
+ // We received a valid command line switch - see if it is
+ // a duplicate of one we already have.
+ //
+ if (fromCmdLine = TRUE) {
+ *lastarg = argv[i];
+ return( ERROR_INVALID_PARAMETER );
+ }
+ pSizeString = argv[i] + switchNameSize;
+
+ *bufferSize = ATOL(pSizeString);
+ fromCmdLine = TRUE;
+ }
+ else {
+ //
+ // We received an invalid command line switch
+ //
+ MSG_LOG(ERROR,"MsgGetBufSize:Invalid command line switch\n",0);
+ *lastarg = argv[i];
+ return( ERROR_INVALID_PARAMETER );
+ }
+ }
+ else {
+ //
+ // do nothing - skip things that don't look like switches
+ //
+ }
+ }
+ if (!fromCmdLine) {
+ //
+ // Attempt to get the buffer size from the configuration database.
+ // BUGBUG! do this sometime.
+ // Actually, it would be nice to do away with this config
+ // baggage. Leave it fixed size.
+ //
+
+ //
+ // If it isn't in the database, use the default.
+ //
+ *bufferSize = 8192;
+ }
+
+ if ((*bufferSize < 512) || (*bufferSize > 62000)) {
+ MSG_LOG(ERROR,"MsgGetBufSize:BufferSize out of range\n",0);
+ return( ERROR_INVALID_PARAMETER);
+ }
+
+ return (NERR_Success);
+}
+
+DWORD
+MsgSetUpMessageFile (
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ Builds the name of the message file that is to be used in any
+ subsequent DosGetMessage calls. The name is built in the Global
+ variable MessageFileName, and is constructed using the following
+ template.
+
+ lanroot\netprog\net.msg
+
+Arguments:
+
+ none
+
+Return Value:
+
+ NERR_Success - The operation was successful
+
+ ERROR_NOT_ENOUGH_MEMORY - Couldn't allocate memory for MessageFileName.
+
+
+--*/
+
+{
+
+ //
+ // allocate some space for the message file name to be built.
+ //
+ MessageFileName = (LPTSTR)LocalAlloc(LMEM_ZEROINIT, (MSGFILENAMLEN+sizeof(TCHAR)));
+
+ if (MessageFileName == NULL) {
+ MSG_LOG(ERROR,"[MSG]SetUpMessageFile:LocalAlloc Failure "
+ FORMAT_API_STATUS "\n", GetLastError());
+ return(ERROR_NOT_ENOUGH_MEMORY);
+ }
+
+ //
+ // BUGBUG: The following function finds the LAN root directory and
+ // builds a filename with that. What will we do in NT?
+ //
+#ifdef later
+ result = NetIMakeLMFileName ( MESSAGE_FILE, MessageFileName,
+ PATHLEN+1);
+ if ( result ) {
+ MessageFileName[0] = '\0';
+
+ }
+#endif
+
+ //
+ // Temporary fix. Use a fixed filename and use the temporary
+ // DosGetMessage (until we get a win32 equivalent).
+ // This message filename is defined in lmcons.h
+ //
+
+ STRCPY(MessageFileName,MESSAGE_FILENAME);
+
+ return (NERR_Success);
+
+}
+
+STATIC VOID
+MsgInitMessageBoxTitle(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ Obtains the title text for the message box used to display messages.
+ If the title is successfully obtained from the message file, then
+ that title is pointed to by GlobalAllocatedMsgTitle and
+ GlobalMessageBoxTitle. If unsuccessful, then GlobalMessageBoxTitle
+ left pointing to the DefaultMessageBoxTitle.
+
+ NOTE: If successful, a buffer is allocated by this function. The
+ pointer stored in GlobalAllocatedMsgTitle and it should be freed when
+ done with this buffer.
+
+Arguments:
+
+Return Value:
+
+ none
+
+--*/
+{
+ LPVOID hModule;
+ DWORD msgSize;
+ DWORD status=NO_ERROR;
+
+ GlobalAllocatedMsgTitle = NULL;
+
+ hModule = LoadLibrary( L"netmsg.dll");
+ if ( hModule == NULL) {
+ status = GetLastError();
+ MSG_LOG1(ERROR, "LoadLibrary() fails with winError = %d\n", GetLastError());
+ return;
+ }
+ msgSize = FormatMessageW(
+ FORMAT_MESSAGE_FROM_HMODULE | // dwFlags
+ FORMAT_MESSAGE_ARGUMENT_ARRAY |
+ FORMAT_MESSAGE_ALLOCATE_BUFFER,
+ hModule,
+ MTXT_MsgsvcTitle, // MessageId
+ 0, // dwLanguageId
+ (LPWSTR)&GlobalAllocatedMsgTitle, // lpBuffer
+ 0, // nSize
+ NULL);
+
+ if (msgSize == 0) {
+ status = GetLastError();
+ MSG_LOG1(ERROR,"Could not find MessageBox title in a message file %d\n",
+ status);
+ }
+ else {
+ GlobalMessageBoxTitle = GlobalAllocatedMsgTitle;
+ }
+ FreeLibrary(hModule);
+ return;
+}
diff --git a/private/net/svcdlls/msgsvc/server/msgmain.c b/private/net/svcdlls/msgsvc/server/msgmain.c
new file mode 100644
index 000000000..337d99108
--- /dev/null
+++ b/private/net/svcdlls/msgsvc/server/msgmain.c
@@ -0,0 +1,865 @@
+/*++
+
+Copyright (c) 1990 Microsoft Corporation
+
+Module Name:
+
+ msgmain.c
+
+Abstract:
+
+ This is the main routine for the NT OS/2 LAN Manager Messenger Service.
+ Functions in the file include:
+
+ MESSENGER_main
+ ParseArgs
+
+Author:
+
+ Dan Lafferty (danl) 20-Mar-1991
+
+Environment:
+
+ User Mode - Win32
+
+Revision History:
+
+ 14-Jun-1994 danl
+ Fixed problem where messenger put up an empty message as if it
+ received a mailslot message during init. The problem was the
+ order of the following events: CreateMailslot -> wait on handle ->
+ submit an async _read with that handle.
+ The new order was changed to: CreateMailslot -> submit async _read ->
+ wait on handle.
+ This causes the handle to not get signaled right away.
+ 20-Mar-1991 danl
+ created
+
+--*/
+
+//
+// INCLUDES
+//
+
+#include "msrv.h" // AdapterThread prototype,SESSION_STATUS
+
+#include <rpc.h> // RPC_HANDLE
+#include <rpcutil.h> // NetpStopRpcServer
+
+#include <string.h> // strlen
+#include <stdio.h> // sprintf
+#include <tstring.h> // Unicode string macros
+
+#include <netlib.h> //
+#include "msgdbg.h" // MSG_LOG & STATIC definitions
+#include "msgdata.h" // msrv_status
+#include <services.h> // LMSVCS_ENTRY_POINT, LMSVCS_GLOBAL_DATA
+
+extern RPC_IF_HANDLE msgsvc_ServerIfHandle;
+
+//
+// GLOBALS
+//
+ //
+ // This is the messenger services reference handle. It is used
+ // to make calls to the service controller for adding work items
+ // to the work queue.
+ //
+ HANDLE MsgSvcRefHandle = NULL;
+
+//
+// Local Function Prototypes
+//
+
+STATIC VOID
+MsgParseArgs(
+ DWORD argc,
+ LPTSTR *argv
+ );
+
+STATIC VOID
+Msgdummy_complete(
+ short c,
+ int a,
+ char b
+ );
+
+DWORD
+MsgGrpEventCompletion(
+ PVOID NetNum, // This passed in as context.
+ DWORD dwWaitStatus
+ );
+
+DWORD
+MsgNetEventCompletion(
+ PVOID NetNum, // This passed in as context.
+ DWORD dwWaitStatus
+ );
+
+
+VOID
+LMSVCS_ENTRY_POINT ( // MESSENGER_main
+ IN DWORD argc,
+ IN LPTSTR argv[],
+ PLMSVCS_GLOBAL_DATA SvcsGlobalData,
+ IN HANDLE SvcRefHandle
+ )
+
+/*++
+
+Routine Description:
+
+ This is the main routine for the Messenger Service
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+Note:
+
+
+--*/
+{
+ DWORD msgrState;
+ HANDLE hGrpEvent;
+ HANDLE hNetEvent;
+
+ MsgParseArgs(argc,argv); // FOR DEBUG ONLY
+
+ MsgSvcRefHandle = SvcRefHandle;
+ MsgsvcGlobalData = SvcsGlobalData;
+
+ msgrState = MsgInitializeMsgr(argc, argv);
+
+ if (msgrState != RUNNING) {
+
+ MSG_LOG(ERROR,"[MSG],Shutdown during initialization\n",0);
+ MsgsvcGlobalData->NetBiosClose();
+
+ //
+ // To get here, the msgrState must either be STOPPING or STOPPED.
+ // Shutdown the Messenger Service
+ //
+
+ if (msgrState == STOPPING) {
+ MsgrShutdown();
+ MsgStatusUpdate(STOPPED);
+ }
+
+ //
+ // Release Thread/Status critical section.
+ //
+ MsgThreadManagerEnd();
+
+ MSG_LOG(TRACE,"MESSENGER_main: Messenger main thread is returning\n\n",0);
+ return;
+ }
+ else {
+
+ //
+ // Post an async read on the Group Mailslot.
+ //
+
+ MSG_LOG0(GROUP,"MESSENGER_main: Submit the Group Mailslot ReadFile\n");
+
+ hGrpEvent = MsgsvcGlobalData->SvcsAddWorkItem(
+ NULL,
+ MsgReadGroupMailslot,
+ (PVOID)NULL,
+ SVC_IMMEDIATE_CALLBACK,
+ INFINITE,
+ MsgSvcRefHandle
+ );
+
+ if (hGrpEvent == NULL) {
+ MSG_LOG1(ERROR,"MESSENGER_main: Failed to setup ReadGroupMailslot %d\n",
+ GetLastError());
+ }
+
+
+ //
+ // Submit the work item that will wait on the mailslot handle.
+ // When the handle becomes signaled, the MsgGrpEventCompletion
+ // function will be called.
+ //
+ MSG_LOG1(GROUP,"MESSENGER_main: Mailslot handle to wait on "
+ " = 0x%lx\n",wakeupSem[SD_NUMNETS()]);
+
+ hGrpEvent = MsgsvcGlobalData->SvcsAddWorkItem(
+ wakeupSem[SD_NUMNETS()],
+ MsgGrpEventCompletion,
+ (PVOID)SD_NUMNETS(),
+ SVC_QUEUE_WORK_ITEM,
+ INFINITE,
+ MsgSvcRefHandle
+ );
+
+ if (hGrpEvent == NULL) {
+ //
+ // If we can't add the work item, then we won't ever listen
+ // for these kind of messages again.
+ //
+ MSG_LOG1(ERROR,"MESSENGER_main: SvcsAddWorkItem failed %d\n",
+ GetLastError());
+
+ //
+ // We want to stop the messenger in this case.
+ //
+
+ MsgBeginForcedShutdown(PENDING,GetLastError());
+
+ MsgDisplayThreadWakeup();
+ CloseHandle(wakeupSem[SD_NUMNETS()]);
+ MsgDisplayEnd();
+ MsgrShutdown();
+ MsgStatusUpdate(STOPPED);
+ MsgThreadManagerEnd();
+ return;
+ }
+
+ hNetEvent = MsgsvcGlobalData->SvcsAddWorkItem(
+ wakeupSem[0],
+ MsgNetEventCompletion,
+ (PVOID)NULL,
+ SVC_QUEUE_WORK_ITEM,
+ INFINITE,
+ MsgSvcRefHandle
+ );
+
+ if (hNetEvent == NULL) {
+ //
+ // If we can't add the work item, then we won't ever listen
+ // for these kind of messages again.
+ //
+ MSG_LOG1(ERROR,"MsgNetEventCompletion: SvcsAddWorkItem failed %d\n",
+ GetLastError());
+
+ //
+ // We want to stop the messenger in this case.
+ //
+
+ MsgBeginForcedShutdown(PENDING,GetLastError());
+
+ MsgDisplayThreadWakeup();
+ MsgGrpThreadShutdown();
+ MsgDisplayEnd();
+ MsgrShutdown();
+ MsgStatusUpdate(STOPPED);
+ MsgThreadManagerEnd();
+ }
+
+ MSG_LOG(TRACE,"MESSENGER_main: Messenger main thread is returning\n\n",0);
+ return;
+ }
+}
+
+
+
+DWORD
+MsgPause(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ Pause - wait for an event to occur
+
+ This function causes the Message Server to yield control of the CPU
+ until an event occurs that reqires processing by the Message Server.
+
+Arguments:
+ None
+
+Return:
+ The network which has a message waiting.
+
+Return Value:
+
+ none
+
+--*/
+
+{
+ DWORD waitStatus;
+
+
+ //
+ // Use event to wait for signal.
+ //
+ waitStatus = WaitForMultipleObjects (
+ SD_NUMNETS() + 1,
+ wakeupSem, // events to wait on.
+ FALSE, // Wait for any event
+ INFINITE); // Wait forever
+
+ if (waitStatus == WAIT_FAILED) {
+ MSG_LOG(ERROR,"[MSG]Pause:Wait for event error %lc\n",GetLastError());
+ }
+ return ( waitStatus );
+
+}
+
+
+VOID
+MsgParseArgs(
+ DWORD argc,
+ LPTSTR *argv
+ )
+
+/*++
+
+Routine Description:
+
+ NOTE: This is just temporary, or should be removed with an
+ #ifdef DBG.
+
+Arguments:
+
+
+
+Return Value:
+
+
+
+--*/
+
+{
+ DWORD i;
+
+
+ if (MsgsvcDebugLevel == 0) {
+ MsgsvcDebugLevel = DEBUG_ERROR;
+ }
+
+ for (i=0; i<argc; i++) {
+ if (STRNCMP (argv[i], TEXT("-d"), 2) == 0) {
+ if (STRICMP (argv[i], TEXT("-dTRACE")) == 0) {
+ MsgsvcDebugLevel = DEBUG_TRACE | DEBUG_ERROR;
+ }
+
+ else if (STRICMP (argv[i], TEXT("-dERROR")) == 0) {
+ MsgsvcDebugLevel = DEBUG_ERROR;
+ }
+
+ else if (STRICMP (argv[i], TEXT("-dALL")) == 0) {
+ MsgsvcDebugLevel = DEBUG_ALL;
+ }
+
+ else if (STRICMP (argv[i], TEXT("-dLOCKS")) == 0) {
+ MsgsvcDebugLevel = DEBUG_LOCKS | DEBUG_ERROR;
+ }
+ else if (STRICMP (argv[i], TEXT("-dGROUP")) == 0) {
+ MsgsvcDebugLevel = DEBUG_GROUP | DEBUG_ERROR;
+ }
+ }
+ }
+
+ return;
+}
+
+
+
+STATIC VOID
+MsgrShutdown(VOID)
+
+/*++
+
+Routine Description:
+
+ Tidies up the network card prior to exiting. All message server async
+ NCBs are cancelled, and message names are deleted.
+
+ When this routine is entered, it is expected that all the worker
+ threads have been notified of the impending shutdown. This routine
+ starts out by waiting for all of them to terminate. Then it continues
+ with cleaning up the NCB's and deleting names.
+
+Arguments:
+
+ none
+
+Return Value:
+
+ none
+
+--*/
+
+{
+ NET_API_STATUS status;
+ DWORD neti; // Network index
+ DWORD ncb_i,i; // ncb array index
+ NCB l_ncb; // local ncb
+ MSG_SESSION_STATUS sess_stat;
+ UCHAR ncbStatus;
+ int nbStatus;
+ DWORD index;
+
+ MSG_LOG(TRACE," in MsgrShutdown\n",0);
+
+ // *** SHUTDOWN HINT ***
+ MsgStatusUpdate (STOPPING);
+
+ //
+ // Shut down the RPC interface.
+ //
+ MSG_LOG(TRACE,"MsgrShutdown: Shut down RPC server\n",0);
+
+ MsgsvcGlobalData->StopRpcServer( msgsvc_ServerIfHandle );
+
+ // *** SHUTDOWN HINT ***
+ MsgStatusUpdate (STOPPING);
+
+ //
+ // Now clean up the NCB's
+ //
+
+ MSG_LOG(TRACE,"MsgrShutdown: Clean up NCBs\n",0);
+
+ for ( neti = 0; neti < SD_NUMNETS(); neti++ ) // For all nets
+ {
+ clearncb(&l_ncb);
+
+ //
+ // First check for any incomplete Async NCBs and cancel them.
+ // As a precaution set the function handler for all the
+ // async NCBs to point to a dummy function which will not reissue
+ // the NCBs when the complete with cancelled status.
+ //
+
+ l_ncb.ncb_lana_num = net_lana_num[neti]; // Use the LANMAN adapter
+ l_ncb.ncb_command = NCBCANCEL; // Cancel (wait)
+
+ for(ncb_i = 0; ncb_i < NCBMAX; ++ncb_i)
+ {
+ mpncbifun[neti][ncb_i] = (LPNCBIFCN)Msgdummy_complete;// Set function pointer
+
+ if((ncbArray[neti][ncb_i].ncb_cmd_cplt == (UCHAR) 0xff) &&
+ (ncbArray[neti][ncb_i].ncb_retcode == (UCHAR) 0xff)) {
+
+ //
+ // If pending NCB found
+ //
+
+ l_ncb.ncb_buffer = (char far *)&(ncbArray[neti][ncb_i]);
+
+ //
+ // There will always be an NCB reserved for cancels in the rdr
+ // but it may be in use so loop if the cancel status
+ // is NRC_NORES.
+ //
+
+ while( (ncbStatus = Msgsendncb(&l_ncb, neti)) == NRC_NORES) {
+ //
+ // Wait for half a sec
+ //
+ Sleep(500L);
+ }
+
+ MSG_LOG(TRACE,"Shutdown:Net #%d\n",neti);
+ MSG_LOG(TRACE,"Shutdown:Attempt to cancel rc = 0x%x\n",
+ ncbStatus);
+
+ //
+ // Now loop waiting for the cancelled ncb to complete.
+ // Any ncbs types which are not valid to cancel (eg Delete
+ // name) must complete so a wait loop here is safe.
+ //
+ // NT Change - This will only loop for 30 seconds before
+ // leaving - whether or not the CANCEL is complete.
+ //
+ status = NERR_InternalError;
+
+ for (i=0; i<60; i++) {
+ if (ncbArray[neti][ncb_i].ncb_cmd_cplt != (UCHAR) 0xff) {
+ status = NERR_Success;
+ break;
+ }
+ //
+ // Wait for half a sec
+ //
+ Sleep(500L);
+ }
+ if (status != NERR_Success) {
+ MSG_LOG(ERROR,
+ "MsgrShutdown: NCBCANCEL did not complete\n",0);
+ }
+ }
+ }
+
+ //
+ // All asyncronous ncbs cancelled completed. Now delete any
+ // messaging names active on the network card.
+ //
+
+ MSG_LOG(TRACE,"MsgrShutdown: All Async NCBs are cancelled\n",0);
+ MSG_LOG(TRACE,"MsgrShutdown: Delete messaging names\n",0);
+
+ for(i = 0; i < NCBMAX; ++i) // Loop to find active names slot
+ {
+ //
+ // If any of the NFDEL or NFDEL_PENDING flags are set for
+ // this name slot then there is no name on the card associated
+ // with it.
+ //
+
+ clearncb(&l_ncb);
+
+ if(!(SD_NAMEFLAGS(neti, i) &
+ (NFDEL | NFDEL_PENDING)))
+ {
+
+ //
+ // If there is a session active on this name, hang it up
+ // now or the delete name will fail
+ //
+
+ l_ncb.ncb_command = NCBSSTAT; // session status (wait)
+
+ memcpy(l_ncb.ncb_name, (SD_NAMES(neti, i)), NCBNAMSZ);
+
+ l_ncb.ncb_buffer = (char far *)&sess_stat;
+ l_ncb.ncb_length = sizeof(sess_stat);
+ l_ncb.ncb_lana_num = net_lana_num[neti];
+
+
+ nbStatus = Msgsendncb(&l_ncb, neti);
+ if(nbStatus == NRC_GOODRET) // If success
+ {
+ for (index=0; index < sess_stat.SessHead.num_sess ;index++) {
+
+ l_ncb.ncb_command = NCBHANGUP; // Hangup (wait)
+ l_ncb.ncb_lsn = sess_stat.SessBuffer[index].lsn;
+ l_ncb.ncb_lana_num = net_lana_num[neti];
+
+ nbStatus = Msgsendncb(&l_ncb, neti);
+ MSG_LOG3(TRACE,"HANGUP NetBios for Net #%d Session #%d "
+ "status = 0x%x\n",
+ neti,
+ sess_stat.SessBuffer[index].lsn,
+ nbStatus);
+
+ }
+ }
+ else {
+ MSG_LOG2(TRACE,"SessionSTAT NetBios Net #%d failed = 0x%x\n",
+ neti, nbStatus);
+ }
+
+ //
+ // With the current design of the message server there can
+ // be only one session per name so the name should now be
+ // clear of sessions and the delete name should work.
+ //
+
+ l_ncb.ncb_command = NCBDELNAME; // Del name (wait)
+ l_ncb.ncb_lana_num = net_lana_num[neti];
+
+ //
+ // Name is still in l_ncb.ncb_name from previous SESSTAT
+ //
+
+ nbStatus = Msgsendncb(&l_ncb, neti);
+ MSG_LOG2(TRACE,"DELNAME NetBios Net #%d status = 0x%x\n",
+ neti, nbStatus);
+ }
+ }
+ } // End for all nets loop
+
+ // *** SHUTDOWN HINT ***
+ MsgStatusUpdate (STOPPING);
+
+ MsgsvcGlobalData->NetBiosClose();
+
+ //
+ // All cleaning up done. Now free up all resources. The list of
+ // possible resources is as follows:
+ //
+ // memory to free: Handles to Close:
+ // --------------- -----------------
+ // ncbArray wakeupSems
+ // mpncbistate threadHandles
+ // net_lana_num
+ // MessageFileName
+ // dataPtr
+ //
+
+ MSG_LOG(TRACE,"MsgrShutdown: Free up Messenger Resources\n",0);
+
+ if (wakeupSem[0] != NULL) {
+ MsgCloseWakeupSems(); // wakeupSems
+ }
+
+ MsgThreadCloseAll(); // Thread Handles
+
+ if (ncbArray != NULL) {
+ MsgFreeNCBSeg(); // ncbArray
+ }
+
+ if (mpncbistate != NULL) {
+ MsgFreeServiceSeg(); // mpncbistate
+ }
+
+ if (net_lana_num != NULL) {
+ MsgFreeSupportSeg(); // net_lana_num
+ }
+
+ if (dataPtr != NULL) {
+ LocalFree (dataPtr); // dataPtr
+ }
+
+ if (MessageFileName != NULL) {
+ LocalFree (MessageFileName); // MessageFileName
+ }
+
+ if (GlobalAllocatedMsgTitle != NULL) {
+ LocalFree(GlobalAllocatedMsgTitle);
+ }
+
+ LocalFree(GlobalTimeFormat.AMString); // Time Format Structure
+ LocalFree(GlobalTimeFormat.PMString);
+ LocalFree(GlobalTimeFormat.DateFormat);
+ LocalFree(GlobalTimeFormat.TimeSeparator);
+ DeleteCriticalSection(&TimeFormatCritSec);
+
+
+ MSG_LOG(TRACE,"MsgrShutdown: Done with shutdown\n",0);
+
+ return;
+}
+
+
+VOID
+Msgdummy_complete(
+ short c,
+ int a,
+ char b
+ )
+{
+ // just to shut up compiler
+
+ MSG_LOG(TRACE,"In dummy_complete module\n",0);
+ UNUSED (a);
+ UNUSED (b);
+ UNUSED (c);
+}
+
+
+DWORD
+MsgNetEventCompletion(
+ PVOID NetNum, // This passed in as context.
+ DWORD dwWaitStatus
+ )
+
+/*++
+
+Routine Description:
+
+ This function is called when the event handle for one of the
+ nets becomes signaled.
+
+Arguments:
+
+ NetNum - This should always be zero.
+
+ dwWaitStatus - This is the return value from the WaitForMultipleObjects.
+ It indicates if the wait timed out, or if it terminated for some
+ other reason.
+
+Return Value:
+
+
+--*/
+{
+ DWORD neti;
+ HANDLE hNetEvent;
+ DWORD msgrState;
+ BOOL ncbComplete=FALSE;
+
+ if (dwWaitStatus == WAIT_FAILED) {
+ MSG_LOG1(ERROR, "MsgNetEventCompletion: The Wait Failed %d\n",
+ GetLastError());
+ //
+ // We can't really do anything here, so we will go on as
+ // if the handle were signaled and everything is ok.
+ //
+ }
+
+ msgrState = GetMsgrState();
+ if (msgrState == STOPPING) {
+ //
+ // Net 0 is considered the main Net, and this thread will
+ // stay around until all the other messenger threads are
+ // done shutting down.
+ // Threads for all the other nets simply return.
+ //
+ MsgGrpThreadShutdown();
+ MsgDisplayEnd();
+ MsgrShutdown();
+ MsgStatusUpdate(STOPPED);
+ //
+ // Release Thread/Status critical section.
+ //
+ MsgThreadManagerEnd();
+
+ MSG_LOG(TRACE,"MsgNetEventCompletion: Messenger main thread is returning\n\n",0);
+ }
+ else {
+
+ //
+ // Look through the NCB's for all nets and service all
+ // NCB's that are complete. Continue looping until one pass
+ // is made through the loop without any complete NCB's being found.
+ //
+ do {
+ ncbComplete = FALSE;
+ MSG_LOG0(TRACE,"MsgNetEventCompletion: Loop through all nets to look "
+ "for any complete NCBs\n");
+
+ for ( neti = 0; neti < SD_NUMNETS(); neti++ ) { // For all nets
+ ncbComplete |= MsgServeNCBs((DWORD)neti);
+ MsgServeNameReqs((DWORD)neti);
+ }
+ } while (ncbComplete);
+
+ //
+ // Setup for the next request.
+ // (submit another WorkItem to the service controller.)
+ //
+ MSG_LOG0(TRACE,"MsgNetEventCompletion: Setup for next Net Event\n");
+ hNetEvent = MsgsvcGlobalData->SvcsAddWorkItem(
+ wakeupSem[0],
+ MsgNetEventCompletion,
+ (PVOID)NULL,
+ SVC_QUEUE_WORK_ITEM,
+ INFINITE,
+ MsgSvcRefHandle
+ );
+
+ if (hNetEvent == NULL) {
+ //
+ // If we can't add the work item, then we won't ever listen
+ // for these kind of messages again.
+ //
+ MSG_LOG1(ERROR,"MsgNetEventCompletion: SvcsAddWorkItem failed %d\n",
+ GetLastError());
+ }
+
+ }
+ //
+ // This thread has done all that it can do. So we can return it
+ // to the service controller.
+ //
+ return(0);
+}
+
+DWORD
+MsgGrpEventCompletion(
+ PVOID NetNum, // This passed in as context.
+ DWORD dwWaitStatus
+ )
+
+/*++
+
+Routine Description:
+
+ This function is called when the mailslot handle for group
+ (domain-wide) messages becomes signalled.
+
+Arguments:
+
+ NetNum - This is the offset into the wakeupSem array where the
+ Mailslot handle is stored. This offset should be the same as
+ SD_NUMNETS().
+
+ dwWaitStatus - This is the return value from the WaitForMultipleObjects.
+ It indicates if the wait timed out, or if it terminated for some
+ other reason.
+
+Return Value:
+
+
+--*/
+{
+ HANDLE hGrpEvent;
+ DWORD msgrState;
+
+ MSG_LOG0(GROUP,"MsgGroupEventCompletion: entry point\n",);
+
+ if (dwWaitStatus == WAIT_FAILED) {
+ MSG_LOG1(ERROR, "MsgGrpEventCompletion: The Wait Failed %d\n",
+ GetLastError());
+ //
+ // We can't really do anything here, so we will go on as
+ // if the handle were signaled and everything is ok.
+ //
+ }
+
+ msgrState = MsgServeGroupMailslot();
+ if (msgrState == STOPPING) {
+ //
+ // Close the Mailslot Handle, and set the wakeupSem array value
+ // to NULL.
+ //
+ CloseHandle(wakeupSem[ SD_NUMNETS() ]); // Group Mailslot Handle
+ wakeupSem[SD_NUMNETS()] = NULL;
+ MSG_LOG0(TRACE,"MsgGroupEventCompletion: No longer listening for "
+ "group messages\n");
+ }
+ else {
+ //
+ // Setup for the next ReadFile. We have the service controller
+ // watcher thread make the call because it never goes away.
+ //
+ hGrpEvent = MsgsvcGlobalData->SvcsAddWorkItem(
+ NULL,
+ MsgReadGroupMailslot,
+ (PVOID)NULL,
+ SVC_IMMEDIATE_CALLBACK,
+ INFINITE,
+ MsgSvcRefHandle
+ );
+
+ if (hGrpEvent == NULL) {
+ MSG_LOG1(ERROR,"MsgGrpEventCompletion: Failed to setup ReadGroupMailslot %d\n",
+ GetLastError());
+ }
+
+ //
+ // Setup for the next request.
+ // (submit another WorkItem to the service controller.)
+ //
+ MSG_LOG0(TRACE,"MsgGroupEventCompletion: Setup for next Group Event\n");
+ MSG_LOG1(GROUP,"MsgGroupEventCompletion: Mailslot handle to wait on "
+ " = 0x%lx\n",wakeupSem[SD_NUMNETS()]);
+
+ hGrpEvent = MsgsvcGlobalData->SvcsAddWorkItem(
+ wakeupSem[SD_NUMNETS()],
+ MsgGrpEventCompletion,
+ (PVOID)SD_NUMNETS(),
+ SVC_QUEUE_WORK_ITEM,
+ INFINITE,
+ MsgSvcRefHandle
+ );
+
+ if (hGrpEvent == NULL) {
+ //
+ // If we can't add the work item, then we won't ever listen
+ // for these kind of messages again.
+ //
+ MSG_LOG1(ERROR,"MsgGrpEventCompletion: SvcsAddWorkItem failed %d\n",
+ GetLastError());
+ }
+
+ }
+ //
+ // This thread has done all that it can do. So we can return it
+ // to the service controller.
+ //
+ return(0);
+}
+
diff --git a/private/net/svcdlls/msgsvc/server/msgnames.h b/private/net/svcdlls/msgsvc/server/msgnames.h
new file mode 100644
index 000000000..802d3bedf
--- /dev/null
+++ b/private/net/svcdlls/msgsvc/server/msgnames.h
@@ -0,0 +1,29 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ msgnames.h
+
+Abstract:
+
+ Defines the Messenger names.
+
+Author:
+
+ Dan Lafferty (danl) 24-Jun-1991
+
+Revision History:
+
+
+--*/
+
+#ifndef _MSGNAMES_INCLUDED
+#define _MSGNAMES_INCLUDED
+
+#define MSGR_INTERFACE_NAME TEXT("msgsvc")
+
+
+#endif // _MSGNAMES_INCLUDED
+
diff --git a/private/net/svcdlls/msgsvc/server/msgnbios.c b/private/net/svcdlls/msgsvc/server/msgnbios.c
new file mode 100644
index 000000000..58a6e289d
--- /dev/null
+++ b/private/net/svcdlls/msgsvc/server/msgnbios.c
@@ -0,0 +1,299 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ msgnbios.c
+
+Abstract:
+
+ This file contains Routines used by the messenger to make netbios
+ calls and to obtain the computer and user names.
+ The following functions are included:
+
+ MsgInit_NetBios
+ Msgsendncb
+ MsgGetWkstaNames
+
+
+ NT_NOTE:
+
+ We need some way to determine which NCB's have unanswered listens
+ pending. For these, we need to send an NCBCANCEL command via
+ another NetBios call. The buffer will contain a pointer to the
+ listening NCB. Also, listens that are in the process of being
+ serviced must either be hung up, or allowed to complete.
+
+ All this is done during the MsgrShutdown.
+
+Author:
+
+ Dan Lafferty (danl) 27-Jun-1991
+
+Environment:
+
+ User Mode -Win32
+
+Notes:
+
+ NetBios3.0 is not a handle-based api. Therefore, there is no open or
+ close associated with it. In order to shut down properly, the
+ messenger will have to hangup or complete listens that are being
+ serviced. And it will have to send a cancel NCB for each listen
+ that is pending.
+
+Revision History:
+
+ 08-Apr-1994 danl
+ MsgAddUserNames: If call to NetWkstaUserEnum failed, this function
+ was still attempting to free the buffer that was returned.
+ Since no buffer is allocated in a failure case, the free mem
+ call in the path is being removed.
+ 27-Jun-1991 danl
+ ported from LM2.0
+
+--*/
+
+
+#include "msrv.h"
+
+#include "msgdbg.h" // MSG_LOG
+
+#include <tstring.h> // Unicode string macros
+#include <icanon.h> // I_NetNameCanonicalize
+#include <netlib.h> // UNUSED macro
+
+#include <lmwksta.h> // NetWorkstation API prototypes
+#include <lmapibuf.h> // NetApiBufferFree
+#include <netdebug.h> // NetpAssert, FORMAT_ equates.
+#include "msgdata.h"
+
+ //
+ // Note: we use the internal entrypoints to the apis because this
+ // file is shared by the message apis, which cannot call the net
+ // bios apis because of different required permissions.
+ //
+
+
+
+BOOL
+MsgInit_NetBios( VOID)
+
+/*++
+
+Routine Description:
+
+ This function fills the global array called net_lana_num with the
+ lan adapter numbers retrieved from A NetBios Enum call
+
+ NOTE: This assumes that space for the array is already set up.
+
+ The LM2.0 version of this also filled an array of NetBios Handles.
+ In LM2.0, the loopback driver was not included unless it was the only
+ network installed.
+
+Arguments:
+
+ none
+
+Return Value:
+
+ TRUE - No Error.
+ FALSE - An error occured.
+
+--*/
+
+ {
+ DWORD count=0;
+ NCB ncb;
+ LANA_ENUM lanaBuffer;
+ unsigned char i;
+ unsigned char nbStatus;
+
+
+ //
+ // Find the number of networks by sending an enum request via Netbios.
+ //
+
+ clearncb(&ncb);
+ ncb.ncb_command = NCBENUM; // Enumerate LANA nums (wait)
+ ncb.ncb_buffer = (char FAR *)&lanaBuffer;
+ ncb.ncb_length = sizeof(LANA_ENUM);
+
+ nbStatus = Netbios (&ncb);
+ if (nbStatus != NRC_GOODRET) {
+ MSG_LOG(ERROR, "Netbios LanaEnum failed rc=%d\n",nbStatus);
+ return(FALSE);
+ }
+
+ //
+ // Move the Adapter Numbers (lana) into the array that will contain them.
+ //
+ for (i=0; i<lanaBuffer.length; i++) {
+ MSG_LOG(TRACE,"adapter %d",i);
+ MSG_LOG(TRACE,"\b\b\b\b\b\b lananum= %d \n", lanaBuffer.lana[i]);
+ net_lana_num[count] = lanaBuffer.lana[i];
+ count++;
+
+ //
+ // Internal consistancy check. Make sure the arrays are only
+ // SD_NUMNETS long.
+ //
+ if ( count > SD_NUMNETS() ) {
+ MSG_LOG(
+ ERROR,
+ "NumNets from NetBios greater than value from Wksta count=%d\n",
+ count);
+ return(FALSE);
+ }
+ }
+
+ //
+ // Internal consistancy check again. We better not have opened
+ // more nets than the messenger thinks there are.
+ //
+
+ if ( count != SD_NUMNETS() ) {
+ return(FALSE);
+ }
+
+ return(TRUE);
+
+}
+
+UCHAR
+Msgsendncb(
+ PNCB NCB_ptr,
+ DWORD neti)
+
+/*++
+
+Routine Description:
+
+
+ This function performs a DosDevIOCtl call to send an NCB to the
+ net bios via a previously openned redirector and netbios handle.
+
+Arguments:
+
+ NCB_ptr - Points to the NCB to send to the net bios.
+ neti - Network index. Which netbios to submit it to?
+
+
+Return Value:
+
+
+ Error code from Net bios.
+
+--*/
+{
+ //
+ // NOTE: The new Netbios call doesn't use any handles, so the neti
+ // info is not used.
+ //
+
+ UNUSED (neti);
+ return (Netbios(NCB_ptr));
+
+#ifdef remove
+ return( NetBiosSubmit( NetBios_Hdl[neti], 0, (NCB far *) NCB_ptr));
+#endif
+}
+
+
+VOID
+MsgAddUserNames(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ This function used to get it's information about the username and
+ computername from the workstation service. Now, in NT, the username
+ is added when the user logs on. It is not automatically added by the
+ messenger.
+
+Arguments:
+
+
+ CompName - Pointer to buf for computer name. (must be NCBNAMSZ+1)
+ CompNameSize - Size in bytes of the buffer to receive the name.
+
+ UserName - Pointer to buffer for user name. (must be UNLEN+1)
+ UserNameSize - Size in bytes of the buffer to receive the name.
+
+Return Value:
+
+ NERR_Success - Aways returned. (Names are returned as NUL strings).
+
+
+--*/
+
+{
+
+ TCHAR UserName[UNLEN+1];
+ DWORD UserNameSize = sizeof(UserName);
+ DWORD i;
+ LPWKSTA_USER_INFO_0 userInfo0;
+ DWORD entriesRead;
+ DWORD totalEntries;
+ NET_API_STATUS status;
+
+ *UserName = TEXT('\0');
+
+ status = NetWkstaUserEnum(
+ NULL,
+ 0,
+ (LPBYTE *)&userInfo0,
+ 0xffffffff, // PreferredMaximiumLength
+ &entriesRead,
+ &totalEntries,
+ NULL); // resume handle
+
+ if (status != NERR_Success) {
+ MSG_LOG(ERROR,"GetWkstaNames:NetWkstaUserEnum FAILURE %X/n",status);
+ return;
+ }
+
+ for (i=0; i<entriesRead; i++ ) {
+
+ if (entriesRead == 0) {
+ //
+ // There are no users logged on at the time of this query.
+ //
+ MSG_LOG(TRACE,
+ "GetWkstaNames:NetWkstaUserEnum entriesRead=%d\n",
+ entriesRead);
+ }
+
+ if(userInfo0[i].wkui0_username != NULL) {
+ status = I_NetNameCanonicalize(
+ NULL,
+ userInfo0[i].wkui0_username,
+ UserName,
+ UserNameSize,
+ NAMETYPE_USER,
+ 0);
+ if (status != NERR_Success) {
+ MSG_LOG(ERROR,"I_NetNameCanonicalize failed %X\n",status);
+ }
+ }
+
+ if( *UserName != TEXT('\0')) { // Set up in GetWkstaNames */
+ MSG_LOG(TRACE, "Calling MsgAddName\n",0);
+ status = MsgAddName(UserName);
+ if (status != NERR_Success) {
+ MSG_LOG(
+ TRACE,
+ "MsgAddUserNames,MessageAddName FAILURE " FORMAT_API_STATUS
+ "\n",
+ status);
+ }
+ }
+ }
+ NetApiBufferFree(userInfo0);
+}
+
diff --git a/private/net/svcdlls/msgsvc/server/msgsec.c b/private/net/svcdlls/msgsvc/server/msgsec.c
new file mode 100644
index 000000000..5de790210
--- /dev/null
+++ b/private/net/svcdlls/msgsvc/server/msgsec.c
@@ -0,0 +1,122 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ msgsec.c
+
+Abstract:
+
+ This module contains the Messenger service support routines
+ which create security objects and enforce security _access checking.
+
+Author:
+
+ Dan Lafferty (danl) 07-Aug-1991
+
+Environment:
+
+ User Mode -Win32
+
+Revision History:
+
+ 07-Aug-1991 danl
+ created
+
+--*/
+
+//
+// Includes
+//
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windef.h>
+
+#include <lmcons.h> // NET_API_STATUS.
+#include <lmerr.h>
+#include <netlibnt.h>
+
+#include "msgdbg.h"
+#include "msgsec.h"
+#include "msgdata.h"
+
+
+//
+// Global Variables -
+//
+// Security Descriptor for Messenger Name object. This is used to control
+// access to the Messenger Name Table.
+//
+
+PSECURITY_DESCRIPTOR MessageNameSd;
+
+
+//
+// Structure that describes the mapping of Generic access rights to object
+// specific access rights for the Messenger Name Object.
+//
+
+GENERIC_MAPPING MsgMessageNameMapping = {
+ STANDARD_RIGHTS_READ | // Generic Read
+ MSGR_MESSAGE_NAME_INFO_GET |
+ MSGR_MESSAGE_NAME_ENUM,
+ STANDARD_RIGHTS_WRITE | // Generic Write
+ MSGR_MESSAGE_NAME_ADD |
+ MSGR_MESSAGE_NAME_DEL,
+ STANDARD_RIGHTS_EXECUTE, // Generic Execute
+ MSGR_MESSAGE_ALL_ACCESS // Generic all
+ };
+
+
+
+NET_API_STATUS
+MsgCreateMessageNameObject(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ This function creates the Messenger Message Name Object.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NET_API_STATUS - translated status returned from NetpCreateSecurityObject.
+
+--*/
+{
+ NTSTATUS ntStatus;
+
+ //
+ // Order matters! These ACEs are inserted into the DACL in the
+ // following order. Security access is granted or denied based on
+ // the order of the ACEs in the DACL.
+ //
+ // Admins, and local users are allowed to get and change all information.
+ //
+
+#define MESSAGE_NAME_ACES 2 // Number of ACES in this DACL
+
+ ACE_DATA AceData[MESSAGE_NAME_ACES] = {
+ {ACCESS_ALLOWED_ACE_TYPE, 0, 0, GENERIC_ALL, &MsgsvcGlobalData->LocalSid},
+ {ACCESS_ALLOWED_ACE_TYPE, 0, 0, GENERIC_ALL, &MsgsvcGlobalData->AliasAdminsSid}
+ };
+
+ ntStatus = NetpCreateSecurityObject(
+ AceData, // Ace Data
+ MESSAGE_NAME_ACES, // Ace Count
+ MsgsvcGlobalData->LocalSystemSid, // Owner Sid
+ MsgsvcGlobalData->LocalSystemSid, // Group Sid
+ &MsgMessageNameMapping, // Generic Mapping
+ &MessageNameSd); // New Descriptor
+
+ return(NetpNtStatusToApiStatus(ntStatus));
+}
diff --git a/private/net/svcdlls/msgsvc/server/msgsec.h b/private/net/svcdlls/msgsvc/server/msgsec.h
new file mode 100644
index 000000000..13d860a3c
--- /dev/null
+++ b/private/net/svcdlls/msgsvc/server/msgsec.h
@@ -0,0 +1,71 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ msgsec.h
+
+Abstract:
+
+ Private header file to be included by Messenger service modules that
+ need to enforce security.
+
+Author:
+
+ Dan Lafferty (danl) 20-Mar-1991
+
+Environment:
+
+ User Mode -Win32
+
+Revision History:
+
+ 07-Aug-1991 danl
+ created
+
+--*/
+#ifndef _MSGSEC_INCLUDED
+#define _MSGSEC_INCLUDED
+
+#include <secobj.h>
+
+//
+// Object specific access masks
+//
+
+#define MSGR_MESSAGE_NAME_INFO_GET 0x0001
+#define MSGR_MESSAGE_NAME_ENUM 0x0002
+#define MSGR_MESSAGE_NAME_ADD 0x0004
+#define MSGR_MESSAGE_NAME_DEL 0x0008
+
+
+#define MSGR_MESSAGE_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | \
+ MSGR_MESSAGE_NAME_INFO_GET | \
+ MSGR_MESSAGE_NAME_ENUM | \
+ MSGR_MESSAGE_NAME_ADD | \
+ MSGR_MESSAGE_NAME_DEL)
+
+
+//
+// Object type name for audit alarm tracking
+//
+#define MESSAGE_NAME_OBJECT TEXT("MsgrNameObject")
+
+//
+// Security descriptor for the messenger name object.
+//
+extern PSECURITY_DESCRIPTOR MessageNameSd;
+
+//
+// Generic mapping for the messenger name object
+//
+extern GENERIC_MAPPING MsgMessageNameMapping;
+
+NET_API_STATUS
+MsgCreateMessageNameObject(
+ VOID
+ );
+
+
+#endif // ifndef _MSGSEC_INCLUDED
diff --git a/private/net/svcdlls/msgsvc/server/msgsvc.def b/private/net/svcdlls/msgsvc/server/msgsvc.def
new file mode 100644
index 000000000..85ed37248
--- /dev/null
+++ b/private/net/svcdlls/msgsvc/server/msgsvc.def
@@ -0,0 +1,7 @@
+NAME msgsvc.dll
+
+DESCRIPTION 'NT Messenger Service'
+
+EXPORTS
+ ServiceEntry
+
diff --git a/private/net/svcdlls/msgsvc/server/msgsvc.rc b/private/net/svcdlls/msgsvc/server/msgsvc.rc
new file mode 100644
index 000000000..dc67cb43c
--- /dev/null
+++ b/private/net/svcdlls/msgsvc/server/msgsvc.rc
@@ -0,0 +1,13 @@
+#include <windows.h>
+
+#include <ntverp.h>
+
+#define VER_FILETYPE VFT_APP
+#define VER_FILESUBTYPE VFT2_UNKNOWN
+#define VER_FILEDESCRIPTION_STR "NT Messenger Service"
+#define VER_INTERNALNAME_STR "msgsvc.dll"
+#define VER_ORIGINALFILENAME_STR "msgsvc.dll"
+
+#include "common.ver"
+
+
diff --git a/private/net/svcdlls/msgsvc/server/msrv.h b/private/net/svcdlls/msgsvc/server/msrv.h
new file mode 100644
index 000000000..9c2f4be62
--- /dev/null
+++ b/private/net/svcdlls/msgsvc/server/msrv.h
@@ -0,0 +1,716 @@
+/********************************************************************/
+/** Microsoft LAN Manager **/
+/** Copyright(c) Microsoft Corp., 1987-1990 **/
+/********************************************************************/
+
+#ifndef _MSRV_INCLUDED
+#define _MSRV_INCLUDED
+
+#include <nt.h> // for ntrtl.h
+#include <ntrtl.h> // DbgPrint prototypes
+#include <nturtl.h> // needed for windows.h when I have nt.h
+#define WINMM_H
+#include <windows.h> // ExitThread prototype
+#include <lmcons.h>
+#include <lmerr.h>
+#include <nb30.h> // NetBios Prototypes and constants
+#include "heap.h"
+
+#ifdef LINT
+#define near
+#define far
+#define void int
+#endif // LINT
+
+
+#define clearncb(x) memset((char *)x,'\0',sizeof(NCB))
+#define clearncbf(x) memsetf((char far *)x,'\0',sizeof(NCB))
+
+#define so_to_far(seg, off) \
+ ((((long)(unsigned)(seg)) << 16) + (unsigned)(off))
+
+//
+// Constant definitions
+//
+
+#define BUFLEN 200 // Length of NCB_BUF
+#define DFBUFSIZ 1750 // Default message buffer size
+#define EOPFAIL (-2) // Failed to open log file
+#define EWRFAIL (-3) // Write to log file failed
+#define MSRVINST 0x0084 // Message Server installation flags
+#define MSRVI "I\204\0" // Server installation string
+#define MSRVD "D\204\0" // Server deinstallation string
+#define LOGNAMLEN PATHLEN // Log file name length (max)
+#define NCBMAX 16 // Maximum number of NCB's (used to be 15).
+ // (now it is 16 for alignment reasons).
+#define TXTMAX 128 // Maximum bytes of text per block
+#define MAXHEAD 80 // Maximum message header length
+#define MAXEND 60 // Maximum message end length
+#define MAXGRPMSGLEN 128 // Max domain message length
+#define AD_STACK_SIZE 4096 // Stack size for each adaptor thread
+
+#define GP_STACK_SIZE 4096 // Stack size for group processor thread
+#define SEM_WAIT 5000L // The # of msecs to wait for the grp thread
+ // to signal successful initialization.
+
+#define MAX_SIZMESSBUF 62000 // The max size for the message buffer
+#define MIN_SIZMESSBUF 512 // The min size for the message buffer
+#define MSNGR_MAX_NETS MAX_LANA // The maximum number of nets the messenger
+ // can handle. (Currently 12)
+
+#define MSGFILENAMLEN PATHLEN*sizeof(TCHAR)
+
+//
+// Messaging name end bytes
+//
+#define NAME_LOCAL_END '\003' // 16th byte in local NCB name
+#define NAME_REMOTE_END '\005' // 16th byte in remote NCB name
+
+//
+// Messenger Thread Manager States (used as return codes)
+//
+
+#define UPDATE_ONLY 0 // no change in state - just send current status.
+#define STARTING 1 // the messenger is initializing.
+#define RUNNING 2 // initialization completed normally - now running
+#define STOPPING 3 // uninstall pending
+#define STOPPED 4 // uninstalled
+
+//
+// Forced Shutdown PendingCodes
+//
+#define PENDING TRUE
+#define IMMEDIATE FALSE
+
+//
+// Message transfer states
+//
+#define MESSTART 0 // Message start state
+#define MESSTOP 1 // Message stop state
+#define MESCONT 2 // Message continued state
+#define MESERR 3 // Message error state
+
+
+//
+// Alert Size BUGBUG: This should go away when we figure out what is
+// replacing alerts.
+//
+
+#define ALERT_MAX_DISPLAYED_MSG_SIZE 4096
+
+
+//
+// Shared data area offsets
+//
+// NOTE:
+// These offsets which describe the header portion of the shared
+// data look like they should be part of a structure. The problem is
+// that some of the array sizes in the header are determined at runtime.
+// This is why they appear as macros.
+//
+
+//
+// Offset of the number of non-loopback nets
+//
+#define SDNUMNETS 0
+
+//
+// Offset of MSRV started flag
+//
+#define SDMSRV (SDNUMNETS + sizeof(DWORD))
+
+//
+// Offset of log file name
+//
+#define SDLOGNAM (SDMSRV + sizeof(DWORD))
+
+//
+// Offset of logging status flag
+//
+#define SDMESLOG (SDLOGNAM + LOGNAMLEN)
+
+//
+// Offset of buffer length
+//
+#define SDBUFLEN (SDMESLOG + sizeof(DWORD))
+
+//
+// Offset of message queue front
+//
+#define SDMESQF (SDBUFLEN + sizeof(DWORD))
+
+//
+// Offset of message queue back
+//
+#define SDMESQB (SDMESQF + sizeof(DWORD))
+
+//
+// Offset of name flag array
+//
+#define SDNAMEFLAGS (SDMESQB + sizeof(DWORD))
+
+//
+// Offset of name number array
+//
+#define SDNAMENUMS (SDNAMEFLAGS + (SD_NUMNETS() * NCBMAX))
+
+//
+// Offset of name array
+//
+#define SDNAMES (SDNAMENUMS + (SD_NUMNETS() * NCBMAX))
+
+//
+// Offset of forward name array
+// *ALIGNMENT* (note that 4 byte, rather than one, are placed on the end)
+//
+#define SDFWDNAMES (SDNAMES + ((SD_NUMNETS() * NCBMAX) * (NCBNAMSZ+4)))
+
+//
+// Offset of message pointer table
+// *ALIGNMENT* (note that 4 byte, rather than one, are placed on the end)
+//
+#define SDMESPTR (SDFWDNAMES + ((SD_NUMNETS() * NCBMAX) * (NCBNAMSZ+4)))
+
+//
+// Offset of buffer
+//
+#define SDBUFFER (SDMESPTR + ((SD_NUMNETS() * NCBMAX)* sizeof(DWORD)))
+
+//
+// Name Flag definitions
+//
+#define NFNEW 0x01 // New name
+#define NFDEL 0x02 // Name deleted
+#define NFFOR 0x04 // Messages forwarded
+#define NFFWDNAME 0x10 // Forward-name
+#define NFMACHNAME 0x20 // Machine name (undeletable)
+#define NFLOCK 0x40 // Name entry locked
+#define NFDEL_PENDING 0x80 // Delete name issued but not complete*/
+
+//
+// Memory area names
+//
+#define DATAMEM "\\SHAREMEM\\MSRV.DAT"
+#define INITMEM "\\SHAREMEM\\MSRVINIT.DAT"
+
+//
+// System semaphore definitions
+//
+#define WAKEUP_SEM "\\SEM\\MSRVWU"
+#define WAKEUPSEM_LEN 13 // The number of characters in WAKEUP_SEM +2
+
+
+//
+// The messenger mailslot for domain messaging
+//
+#ifdef remove
+#define MESSNGR_MS_NAME "\\mailslot\\messngr"
+#define MESSNGR_MS_NAME_LEN 17
+#endif
+
+#define MESSNGR_MS_NAME "\\\\.\\mailslot\\messngr"
+#define MESSNGR_MS_NAME_LEN 20
+
+
+//
+// The character used to separate the components of a domain message
+//
+#define SEPCHAR "\6"
+
+
+//
+// Memory allocator flags
+//
+// #define MEMMOVE 0x0002 // Movable memory flag
+// #define MEMWRIT 0x0080 // Writable memory flag
+
+
+//
+// Structure and macro definitions
+//
+
+#ifdef INULL // If heap structures defined
+
+//
+// Single-block message header
+//
+typedef struct {
+ HEAPHDR sbm_hp; // Heap block header
+ unsigned short sbm_next; // Link to next message
+ unsigned short align; // *ALIGNMENT*
+ unsigned long sbm_bigtime; // Date and time of message
+}SBM;
+
+#define SBM_SIZE(x) HP_SIZE((x).sbm_hp)
+#define SBM_CODE(x) HP_FLAG((x).sbm_hp)
+#define SBM_NEXT(x) (x).sbm_next
+#define SBM_BIGTIME(x) (x).sbm_bigtime
+#define SBMPTR(x) ((SBM far *) &heap[(x)])
+
+//
+// Multi-block message header
+//
+typedef struct {
+ HEAPHDR mbb_hp; // Heap block header
+ DWORD mbb_next; // Link to next message
+ DWORD mbb_bigtime; // Date of message
+ DWORD mbb_btext; // Link to last text block
+ DWORD mbb_ftext; // Link to first text block
+ DWORD mbb_state; // State flag
+}MBB;
+
+
+#define MBB_SIZE(x) HP_SIZE((x).mbb_hp)
+#define MBB_CODE(x) HP_FLAG((x).mbb_hp)
+#define MBB_NEXT(x) (x).mbb_next
+#define MBB_BIGTIME(x) (x).mbb_bigtime
+#define MBB_BTEXT(x) (x).mbb_btext
+#define MBB_FTEXT(x) (x).mbb_ftext
+#define MBB_STATE(x) (x).mbb_state
+#define MBBPTR(x) ((MBB far *) &heap[(x)])
+
+//
+// Multi-block message text
+//
+typedef struct {
+ HEAPHDR mbt_hp; // Heap block header
+ DWORD mbt_next; // Link to next block (offset)
+ DWORD mbt_bytecount; // *ALIGNMENT2*
+}MBT, *PMBT, *LPMBT;
+
+#define MBT_SIZE(x) HP_SIZE((x).mbt_hp)
+#define MBT_CODE(x) HP_FLAG((x).mbt_hp)
+#define MBT_NEXT(x) (x).mbt_next
+#define MBT_COUNT(x) (x).mbt_bytecount // *ALIGNMENT2*
+#define MBTPTR(x) ((LPMBT) &heap[(x)])
+
+#endif // INULL - End heap access macros
+
+//
+// A one session/name status structure
+//
+typedef struct _MSG_SESSION_STATUS{
+ SESSION_HEADER SessHead;
+ SESSION_BUFFER SessBuffer[NCBMAX];
+}MSG_SESSION_STATUS, *PMSG_SESSION_STATUS, *LPMSG_SESSION_STATUS;
+
+
+//
+// Shared data access macros
+//
+#define SD_NUMNETS() (((LPDWORD) &dataPtr[SDNUMNETS])[0])
+#define SD_MSRV() (((LPDWORD) &dataPtr[SDMSRV])[0])
+#define SD_NAMEFLAGS(n, x) ((&dataPtr[(SDNAMEFLAGS+(n*NCBMAX))])[(x)])
+#define SD_NAMENUMS(n, x) ((&dataPtr[(SDNAMENUMS+(n*NCBMAX))])[(x)])
+#define SD_NAMES(n, x) (&dataPtr[SDNAMES+(n*NCBMAX*(NCBNAMSZ+1))+(x*(NCBNAMSZ+1))])
+#define SD_FWDNAMES(n, x) (&dataPtr[SDFWDNAMES+(n*NCBMAX*(NCBNAMSZ+1))+(x*(NCBNAMSZ+1))])
+#define SD_LOGNAM() (&dataPtr[SDLOGNAM])
+#define SD_BUFLEN() (((LPDWORD) &dataPtr[SDBUFLEN])[0])
+#define SD_MESLOG() (((LPDWORD) &dataPtr[SDMESLOG])[0])
+#define SD_MESQF() (((LPDWORD) &dataPtr[SDMESQF])[0])
+#define SD_MESQB() (((LPDWORD) &dataPtr[SDMESQB])[0])
+#define SD_MESPTR(n, x) (((LPDWORD) &dataPtr[SDMESPTR+(n*NCBMAX*sizeof(DWORD))])[(x)])
+#define SD_BUFFER() (&dataPtr[SDBUFFER])
+
+
+
+//
+// structure for storing text messages
+//
+//typedef struct {
+// int msgnum;
+// char *msgtext;
+//} MESSAGE;
+
+typedef struct _NCB_STATUS {
+ int this_immediate;
+ int last_immediate;
+ unsigned char this_final;
+ unsigned char last_final;
+ unsigned char rep_count;
+ unsigned char align; // *ALIGNMENT*
+}NCB_STATUS, *PNCB_STATUS, *LPNCB_STATUS;
+
+NCB_STATUS ncb_status;
+
+
+#define SIG_IGNORE 1
+#define SIG_ACCEPT 2
+#define SIG_ERROR 3
+#define SIG_RESET 4
+
+//
+// g_install_state bit definitions
+//
+//#define IS_EXECED_MAIN 0x0001
+//#define IS_ALLOC_SEG 0x0002
+
+//
+// Timeout for waiting for the shared segment before giving up
+// and reporting an internal error.
+//
+//#define MSG_SEM_TO 60000L // 60 second timeout
+
+//
+// No. of repeated consectutive NCB errors required to abort the
+// message server.
+//
+
+#define SHUTDOWN_THRESHOLD 10
+
+// net send timeout and retry constatnts
+#define MAX_CALL_RETRY 5 // Retry a failed send up to 5 times
+#define CALL_RETRY_TIMEOUT 1 // second delay between retries
+
+
+//
+// ncb worker function type
+//
+typedef VOID (*PNCBIFCN) (
+ DWORD NetIndex, // Network Index
+ DWORD NcbIndex, // Network Control Block Index
+ CHAR RetVal // value returned by net bios
+ );
+
+typedef PNCBIFCN LPNCBIFCN;
+
+//
+// Database Lock requests for the MsgDatabaseLock function.
+//
+
+typedef enum _MSG_LOCK_REQUEST {
+ MSG_INITIALIZE,
+ MSG_GET_SHARED,
+ MSG_GET_EXCLUSIVE,
+ MSG_RELEASE,
+ MSG_DELETE,
+ MSG_MAKE_SHARED,
+ MSG_MAKE_EXCLUSIVE
+} MSG_LOCK_REQUEST, *PMSG_LOCK_REQUEST, *LPMSG_LOCK_REQUEST;
+
+
+//
+// Function Prototypes
+//
+
+
+DWORD
+GetMsgrState (
+ VOID
+ );
+
+NET_API_STATUS
+MsgAddName(
+ LPTSTR Name
+ );
+
+void
+MsgBufferInit(
+ unsigned short
+ );
+
+DWORD
+MsgGetPid(VOID);
+
+int
+MsgGetRemoteName(
+ const char far *,
+ char far *
+ );
+
+VOID
+MsgAddUserNames(
+ VOID
+ );
+
+void
+MsgInitNCBs(
+ void
+ );
+
+void
+MsgInitStatus(
+ short
+ );
+
+
+DWORD
+MsgBeginForcedShutdown(
+ IN BOOL PendingCode,
+ IN DWORD ExitCode
+ );
+
+BOOL
+MsgDatabaseLock(
+ IN MSG_LOCK_REQUEST request,
+ IN LPSTR idString
+ );
+
+BOOL
+MsgDisplayInit(
+ VOID
+ );
+
+BOOL
+MsgDisplayQueueAdd(
+ IN LPSTR pMsgBuffer,
+ IN DWORD MsgSize,
+ IN DWORD BigTime
+ );
+
+VOID
+MsgDisplayThreadWakeup(
+ VOID
+ );
+
+VOID
+MsgDisplayEnd(
+ VOID
+ );
+
+NET_API_STATUS
+MsgErrorLogWrite(
+ IN DWORD Code,
+ IN LPTSTR Component,
+ IN LPBYTE Buffer,
+ IN DWORD BufferSize,
+ IN LPSTR Strings,
+ IN DWORD NumStrings
+ );
+
+NET_API_STATUS
+MsgInitializeMsgr(
+ DWORD argc,
+ LPTSTR *argv
+ );
+
+NET_API_STATUS
+MsgNewName(
+ IN DWORD neti,
+ IN DWORD ncbi
+ );
+
+VOID
+MsgrShutdown(VOID);
+
+VOID
+MsgThreadWakeup(
+ VOID
+ );
+
+VOID
+MsgStatusInit(VOID);
+
+DWORD
+MsgStatusUpdate(
+ IN DWORD NewState
+ );
+
+VOID
+MsgThreadCloseAll(VOID);
+
+VOID
+MsgThreadManagerEnd(VOID);
+
+VOID
+MsgThreadManagerInit(VOID);
+
+
+NET_API_STATUS
+MsgThreadStart (
+ IN LPTHREAD_START_ROUTINE StartRoutine,
+ IN DWORD StackSize
+ );
+
+VOID
+MsgThreadTermAll(VOID);
+
+VOID
+MsgThreadVerifyTerm(VOID);
+
+
+BOOL
+MsgInit_NetBios(
+ VOID
+ );
+
+BOOL
+MsgServeNCBs(
+ DWORD net // Which network am I serving?
+ );
+
+VOID
+MsgServeNameReqs(
+ IN DWORD net
+ );
+
+DWORD
+MsgReadGroupMailslot(
+ PVOID pData,
+ DWORD dwWaitStatus);
+
+NET_API_STATUS
+MsgServeGroupMailslot();
+
+int
+MsgSetUserName(
+ char far *
+ );
+
+void
+MsgSuspendOtherThreads(
+ void
+ );
+
+DWORD
+Msgendprint(
+ int action, // Alert, File, or Alert and file
+ DWORD state, // Final state of message
+ HANDLE file_handle // Log file
+ );
+
+NET_API_STATUS
+MsgFmtNcbName(
+ OUT PCHAR DestBuf,
+ IN LPTSTR Name,
+ IN DWORD Type);
+
+
+DWORD
+Msghdrprint(
+ int action, // Where to log the header to.
+ LPSTR from, // Name of sender
+ LPSTR to, // Name of recipient
+ DWORD bigtime, // Bigtime of message
+ HANDLE file_handle // Output file handle*/
+ );
+
+DWORD
+Msglogmbb(
+ LPSTR from, // Name of sender
+ LPSTR to, // Name of recipient
+ DWORD net, // Which network ?
+ DWORD ncbi // Network Control Block index
+ );
+
+UCHAR
+Msglogmbe(
+ DWORD state, // Final state of message
+ DWORD net, // Which network?
+ DWORD ncbi // Network Control Block index
+ );
+
+DWORD
+Msglogmbt(
+ LPSTR text, // Text of message
+ DWORD net, // Which network?
+ DWORD ncbi // Network Control Block index
+ );
+
+DWORD
+Msglogsbm(
+ LPSTR from, // Name of sender
+ LPSTR to, // Name of recipient
+ LPSTR text // Text of message
+ );
+
+VOID
+Msgmbmfree(
+ DWORD mesi
+ );
+
+DWORD
+Msgmbmprint(
+ int action,
+ DWORD mesi,
+ HANDLE file_handle
+ );
+
+VOID
+MsgrCtrlHandler (
+ IN DWORD opcode
+ );
+
+DWORD
+Msgopen_append(
+ LPSTR file_name, // Name of file to open
+ PHANDLE file_handle_ptr // pointer to storage for file handle
+ );
+
+DWORD
+MsgPause(
+ VOID
+ );
+
+int
+Msgsbmprint(
+ unsigned short,
+ unsigned short
+ );
+
+UCHAR
+Msgsendncb(
+ PNCB NcbPtr,
+ DWORD neti
+ );
+
+int
+Msgsmbcheck(
+ LPBYTE buffer,
+ USHORT size,
+ UCHAR func,
+ int parms,
+ LPSTR fields
+ );
+
+NET_API_STATUS
+MsgStartListen(
+ DWORD net,
+ DWORD ncbi
+ );
+
+DWORD
+Msgtxtprint(
+ int action, // Alert, File, or Alert and file
+ LPSTR text, // Pointer to text
+ DWORD length, // Length of text
+ HANDLE file_handle // Log file handle
+ );
+
+NET_API_STATUS
+MsgInitNCBSeg(VOID);
+
+NET_API_STATUS
+MsgInitServiceSeg(VOID);
+
+NET_API_STATUS
+MsgInitSupportSeg(VOID);
+
+VOID
+MsgFreeNCBSeg(VOID);
+
+VOID
+MsgFreeServiceSeg(VOID);
+
+VOID
+MsgFreeSupportSeg(VOID);
+
+BOOL
+MsgCreateWakeupSems(
+ DWORD NumNets
+ );
+
+VOID
+MsgCloseWakeupSems(
+ VOID
+ );
+
+NET_API_STATUS
+MsgInitGroupSupport(DWORD iGrpMailslotWakeupSem);
+
+VOID
+MsgGrpThreadShutdown(
+ VOID
+ );
+
+#endif // MSRV_INCLUDED
diff --git a/private/net/svcdlls/msgsvc/server/servenam.c b/private/net/svcdlls/msgsvc/server/servenam.c
new file mode 100644
index 000000000..a4019ed6d
--- /dev/null
+++ b/private/net/svcdlls/msgsvc/server/servenam.c
@@ -0,0 +1,223 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ servenam.c
+
+Abstract:
+
+ Routines to service name requests. This file contains the following
+ functions:
+ FindNewName
+ NewName
+ ServeNameReqs
+
+Author:
+
+ Dan Lafferty (danl) 26-Jul-1991
+
+Environment:
+
+ User Mode -Win32
+
+Revision History:
+
+ 26-Jul-1991 danl
+ ported from LM2.0
+ 17-Oct-1991 JohnRo
+ Got rid of a MIPS compiler warning.
+
+--*/
+#include "msrv.h"
+
+#include <smbtypes.h> // needed for smb.h
+#include <smb.h> // Server Message Block definitions
+#include <string.h> // memcpy
+
+#include "msgdata.h"
+#include "msgdbg.h" // MSG_LOG
+
+
+//
+// Local Functions
+//
+
+DWORD
+MsgFindNewName(
+ IN DWORD net
+ );
+
+
+/*
+ * MsgFindNewName - find a new name
+ *
+ * This function scans the name table for a new entry and returns its index.
+ *
+ * FindNewName (net)
+ *
+ * ENTRY
+ * net - the network index to use
+ *
+ * RETURN
+ * int - index of new name if found, -1 if none found
+ *
+ * This function assumes the shared data segment is accessible.
+ */
+
+DWORD
+MsgFindNewName(
+ IN DWORD net
+ )
+
+{
+ int i;
+
+ //
+ // Loop to find new name
+ //
+
+ for(i = 0; i < NCBMAX; ++i) {
+ if(SD_NAMEFLAGS(net,i) & NFNEW)
+
+ //
+ // Return index if new name found
+ //
+ return(i);
+
+ }
+
+ return(0xffffffff); // No new names
+
+}
+
+/*
+ * MsgNewName - process a new name
+ *
+ * This function initializes the Network Control Block for a new name
+ * and calls the appropriate function to issue the first net bios call
+ * for that name.
+ *
+ * MsgNewName (neti,ncbi)
+ *
+ * ENTRY
+ * neti - Network index
+ * ncbi - Network Control Block index
+ *
+ * RETURN
+ * This function returns the status from calls to MsgStartListen().
+ * In NT when we add a name, we also need to make sure that we can
+ * get a session for that name before telling the user that the
+ * name was added successfully. If a failure occurs in StartListen,
+ * that will be returned thru here.
+ *
+ *
+ * This function assumes the shared data area is accessible.
+ */
+
+NET_API_STATUS
+MsgNewName(
+ IN DWORD neti, // Network index
+ IN DWORD ncbi // Name index
+ )
+
+{
+ unsigned char flags;
+ NET_API_STATUS status = NERR_Success;
+
+ //
+ // Block until shared data area is free
+ //
+ MsgDatabaseLock(MSG_GET_EXCLUSIVE,"NetName");
+
+ //
+ // If name still marked as new
+ //
+ if (SD_NAMEFLAGS(neti,ncbi) & NFNEW) {
+
+ //
+ // Turn off the new name bit
+ //
+ SD_NAMEFLAGS(neti,ncbi) &= ~NFNEW;
+
+ //
+ // copy the name into the NCB
+ //
+ memcpy((char far *)(ncbArray[neti][ncbi].ncb_name),
+ SD_NAMES(neti,ncbi),NCBNAMSZ);
+
+ //
+ // Set the buffer address
+ //
+ ncbArray[neti][ncbi].ncb_buffer =
+ (char far *)(&(ncbBuffers[neti][ncbi*BUFLEN]));
+
+ //
+ // Wake up semaphore address
+ //
+ ncbArray[neti][ncbi].ncb_event = (HANDLE)wakeupSem[neti];
+
+ //
+ // Use the LANMAN adapter
+ //
+ ncbArray[neti][ncbi].ncb_lana_num = net_lana_num[neti];
+
+ //
+ // Set the name number
+ //
+ ncbArray[neti][ncbi].ncb_num = SD_NAMENUMS(neti,ncbi);
+
+ flags = SD_NAMEFLAGS(neti,ncbi);
+
+ //
+ // Unlock the share table
+ //
+
+ MsgDatabaseLock(MSG_RELEASE, "NewName");
+
+
+ status = MsgStartListen(neti,ncbi); // Start listening for messages
+ MSG_LOG(TRACE,"MsgNewName: MsgStartListen Status = %ld\n",status);
+ }
+ else {
+ //
+ // Unlock the share table
+ //
+ MsgDatabaseLock(MSG_RELEASE, "NewName");
+ }
+ return(status);
+}
+
+/*
+ * MsgServeNameReqs - service new names
+ *
+ * This function scans the name table for new names to process. It scans
+ * and processes names until no more new names can be found.
+ *
+ * MsgServeNameReqs ()
+ *
+ * RETURN
+ * nothing
+ *
+ * This function gains access to the shared data area, finds and processes
+ * new names until no more can be found, and then releases the shared data
+ * area.
+ */
+
+VOID
+MsgServeNameReqs(
+ IN DWORD net // Net Index
+ )
+{
+ DWORD i; // Name index
+
+ //
+ // While new names are found, add them.
+ //
+
+ while( (i = MsgFindNewName(net)) != -1) {
+ MsgNewName(net,i);
+ }
+}
+
diff --git a/private/net/svcdlls/msgsvc/server/servencb.c b/private/net/svcdlls/msgsvc/server/servencb.c
new file mode 100644
index 000000000..2277bd8db
--- /dev/null
+++ b/private/net/svcdlls/msgsvc/server/servencb.c
@@ -0,0 +1,2023 @@
+/*++
+
+Copyright (c) 1991-1992 Microsoft Corporation
+
+Module Name:
+
+ servencb.c
+
+Abstract:
+
+ Routines to service completed NCB's. This file contains the following
+ functions:
+
+ MsgCallNetBios
+ MsgDeleteName
+ MsgGetMachineName
+ MsgHangupService
+ MsgListenService
+ Msgmblockbeg
+ Msgmblockend
+ Msgmblocktxt
+ MsgNetBiosError
+ MsgRecBcastService
+ MsgReceiveService
+ MsgRestart
+ Msgsblockmes
+ MsgSendAck
+ MsgSendService
+ MsgServeNCBs
+ MsgSesFullService
+ MsgStartListen
+ MsgStartRecBcast
+ MsgVerifySmb
+
+Author:
+
+ Dan Lafferty (danl) 15-Jul-1991
+
+Environment:
+
+ User Mode -Win32
+
+Revision History:
+
+ 27-Jul-1994 danl
+ MsgServeNCBs: This function now returns FALSE when the service
+ is to shut down.
+
+ 29-May-1992 danl
+ MsgListenService: reset the NRC_NORES error count when a good
+ return code accompanies the Listen completion.
+
+ 18-Feb-1992 ritaw
+ Convert to Win32 service control APIs.
+
+ 15-Jul-1991 danl
+ Ported from LM2.0
+
+--*/
+
+//
+// SMB translation
+//
+//
+// OLD NEW
+// SMB SMB_HEADER or PSMB_HEADER
+// -------- -------------------------
+// smb_idf Protocol
+// smb_com Command
+// smb_rcls ErrorClass
+// smb_reh Reserved
+// smb_err Error
+// smb_flg Flags
+// smb_flag2 Flags2
+// smb_res Reserved2
+// smb_gid Tid
+// smb_tid Pid
+// smb_pid Uid
+// smb_uid Mid
+// smb_mid Kludge
+
+//
+// Includes
+//
+
+#include "msrv.h"
+
+#include <tstring.h> // Unicode string macros
+#include <string.h> // memcpy
+#include <netdebug.h> // NetpAssert
+#include <lmerrlog.h> // NELOG_ messages
+
+#include <netlib.h> // UNUSED macro
+#include <smbtypes.h> // needed for smb.h
+#include <smb.h> // Server Message Block definitions
+#include <smbgtpt.h> // SMB field manipulation macros
+//#include <msgrutil.h> // NetpNetBiosReset
+#include <nb30.h> // NRC_GOODRET, ASYNC
+
+#include "msgdbg.h" // MSG_LOG
+#include "heap.h"
+#include "msgdata.h"
+#include "apiutil.h" // MsgMapNetError
+
+
+#define MAX_RETRIES 10
+
+//
+// Local Functions
+//
+
+STATIC NET_API_STATUS
+MsgCallNetBios(
+ DWORD net,
+ PNCB ncb,
+ DWORD ncbi
+ );
+
+STATIC VOID
+MsgDeleteName(
+ DWORD net,
+ DWORD ncbi
+ );
+
+STATIC VOID
+MsgGetMachineName(
+ DWORD net,
+ DWORD ncbi
+ );
+
+STATIC VOID
+MsgHangupService(
+ DWORD net,
+ DWORD ncbi,
+ CHAR retval
+ );
+
+STATIC VOID
+MsgListenService(
+ DWORD net,
+ DWORD ncbi,
+ CHAR retval
+ );
+
+STATIC VOID
+Msgmblockbeg(
+ DWORD net,
+ DWORD ncbi
+ );
+
+STATIC VOID
+Msgmblockend(
+ DWORD net,
+ DWORD ncbi
+ );
+
+STATIC VOID
+Msgmblocktxt(
+ DWORD net,
+ DWORD ncbi
+ );
+
+STATIC DWORD
+MsgNetBiosError(
+ DWORD net,
+ PNCB ncb,
+ char retval,
+ DWORD ncbi
+ );
+
+
+STATIC VOID
+MsgReceiveService(
+ DWORD net,
+ DWORD ncbi,
+ char retval
+ );
+
+STATIC VOID
+MsgRestart(
+ DWORD net,
+ DWORD ncbi
+ );
+
+STATIC VOID
+Msgsblockmes(
+ DWORD net,
+ DWORD ncbi
+ );
+
+STATIC VOID
+MsgSendAck(
+ DWORD net,
+ DWORD ncbi,
+ UCHAR smbrclass,
+ USHORT smbrcode
+ );
+
+STATIC VOID
+MsgSendService(
+ DWORD net,
+ DWORD ncbi,
+ CHAR retval
+ );
+
+STATIC VOID
+MsgSesFullService(
+ DWORD net,
+ DWORD ncbi,
+ char retval
+ );
+
+
+STATIC int
+MsgVerifySmb(
+ DWORD net,
+ DWORD ncbi,
+ UCHAR func,
+ int parms,
+ char *buffers
+ );
+
+
+#if DBG
+
+VOID
+MsgDumpNcb(
+ IN PNCB pNcb
+ );
+
+#endif //DBG
+
+
+/*
+ * MsgCallNetBios - issue a net bios call
+ *
+ * This function issues a net bios call and calls the
+ * error handler if that call results in an error.
+ *
+ * MsgCallNetBios (net, ncb, ncbi)
+ *
+ * ENTRY
+ * net - network index
+ * ncb - pointer to a Network Control Block
+ * ncbi - index of ncb in ncb array
+ *
+ * RETURN
+ * state of the Messenger: Either RUNNING or STOPPING.
+ *
+ * SIDE EFFECTS
+ *
+ * Calls NetBios() to actually issue the net bios call.
+ * Calls MsgNetBiosError() if there is an error.
+ */
+
+STATIC NET_API_STATUS
+MsgCallNetBios(
+ DWORD net, // Which network?
+ PNCB ncb, // Pointer to Network Control Block
+ DWORD ncbi
+ )
+{
+ int retval;
+
+ retval = Msgsendncb(ncb, net);
+
+ if (retval == NRC_GOODRET) {
+
+ //
+ // Clear err on success
+ //
+ ncbStatus[net][ncbi].last_immediate = 0;
+ ncbStatus[net][ncbi].this_immediate = 0;
+ }
+ else {
+ //
+ // NEW (11-4-91):
+ // --------------
+ // It is ok to get a Session Closed error if the state is STOP.
+ //
+ if ( (mpncbistate[net][ncbi] == MESSTOP) &&
+ (retval == NRC_SCLOSED) ) {
+
+ MSG_LOG(TRACE,"CallNetBios: At end of msg, Session is closed for NET %d\n",
+ net);
+ ncbStatus[net][ncbi].last_immediate = 0;
+ ncbStatus[net][ncbi].this_immediate = 0;
+ }
+ else {
+ //
+ // Else mark error
+ //
+ ncbStatus[net][ncbi].this_immediate = retval;
+ //
+ // Call the error handler if err
+ //
+ MSG_LOG(TRACE,"CallNetBios: net bios call failed 0x%x\n",retval);
+ MsgNetBiosError(net,ncb,(char)retval,ncbi);
+ return(MsgMapNetError((UCHAR)retval));
+ }
+ //
+ // Make sure the event for this thread is in the signaled state
+ // so that we can wake up and properly handle the error.
+ //
+ if (SetEvent(ncb->ncb_event) != TRUE) {
+ MSG_LOG(ERROR,"CallNetBios: SetEvent Failed\n",0);
+ }
+ }
+ return(NERR_Success);
+}
+
+/*
+ * MsgDeleteName - Delete a name from the Message Server's name table
+ *
+ * This function is called when a LISTEN, a RECEIVE BROADCAST DATAGRAM,
+ * or a RECEIVE ANY completes with the error code specifying that the
+ * name in question has been deleted. This function marks the appropriate
+ * entry in the flag table in the shared data area and sets the NCB_CPLT
+ * field of the appropriate NCB to 0xFF (so that FindCompletedNCB() will
+ * never find it).
+ *
+ * MsgDeleteName (net, ncbi)
+ *
+ * ENTRY
+ * net - network index
+ * ncbi - Network Control Block index
+ *
+ * RETURN
+ * nothing
+ *
+ * SIDE EFFECTS
+ *
+ * Modifies an NCB and the shared data area.
+ */
+
+STATIC VOID
+MsgDeleteName(
+ DWORD net, // Which network?
+ DWORD ncbi // Network Control Block index
+ )
+{
+ NCB ncb;
+ NET_API_STATUS status;
+
+ MSG_LOG(TRACE,"In MsgDeleteName %d\n",net);
+
+ if( SD_NAMEFLAGS(net,ncbi) & NFMACHNAME) {
+ //
+ // Name is the machine name. It may have been removed from the
+ // card by a board reset, so try to re-add it.
+ //
+ // NEW-NEW-NEW-NEW-NEW-NEW-NEW-NEW-NEW-NEW-NEW-NEW-NEW-NEW-NEW
+ //
+ // First reset the adapter
+ //
+ MSG_LOG1(TRACE,"Calling NetBiosReset for lana #%d\n",net_lana_num[net]);
+ status = MsgsvcGlobalData->NetBiosReset(net_lana_num[net]);
+
+ if (status != NERR_Success) {
+ MSG_LOG(ERROR,"MsgDeleteName: NetBiosReset failed %d\n",
+ status);
+ MSG_LOG(ERROR,"MsgDeleteName: AdapterNum %d\n",net);
+ //
+ // I'm not sure what to do if this fails.
+ //
+ }
+ //
+ //
+ // NEW-NEW-NEW-NEW-NEW-NEW-NEW-NEW-NEW-NEW-NEW-NEW-NEW-NEW-NEW
+
+ memcpy((char far *) ncb.ncb_name, SD_NAMES(net,ncbi),NCBNAMSZ);
+ ncb.ncb_command = NCBADDNAME; // Add name (wait)
+ ncb.ncb_lana_num = net_lana_num[net]; // Use the LANMAN adapter
+ Msgsendncb( &ncb, net);
+ MsgStartListen(net,ncbi);
+ }
+ else {
+ MsgDatabaseLock(MSG_GET_EXCLUSIVE,"MsgDeleteName"); // Wait for write access
+ SD_NAMEFLAGS(net,ncbi) = NFDEL; // Name is deleted
+ MsgDatabaseLock(MSG_RELEASE,"MsgDeleteName"); // Free lock on share table
+ ncbArray[net][ncbi].ncb_cmd_cplt = 0xff; // Simulate command in progress
+ }
+}
+
+/*
+ * MsgGetMachineName - process a Get Machine Name Server Message Block
+ *
+ * This function sends to the caller the local machine name in
+ * response to a Get Machine Name Server Message Block.
+ *
+ * MsgGetMachineName (net, ncbi)
+ *
+ * ENTRY
+ * net - Network index
+ * ncbi - Network Control Block index
+ *
+ * Globals used as input:
+ *
+ * machineName - Unicode version of the machine name.
+ *
+ * machineNameLen - The number of unicode characters in the machine
+ * name.
+ *
+ * RETURN
+ * nothing
+ *
+ * MsgGetMachineName() is called by MsgReceiveService (RecAnyService()).
+ * After verifying that the request is valid, this function builds
+ * an SMB containing the local machine name and sends it back to the
+ * caller.
+ *
+ * SIDE EFFECTS
+ *
+ * Calls MsgVerifySmb() and MsgCallNetBios(). Sets MsgSendService() to be the
+ * next service routine to be executed for the ncbi'th NCB.
+ */
+
+STATIC VOID
+MsgGetMachineName(
+ DWORD net, // Which network?
+ DWORD ncbi // Index to NCB
+ )
+{
+ PNCB ncb; // Pointer to NCB
+ LPBYTE buffer; // Pointer to SMB buffer
+ LPBYTE cp; // Save pointer
+ PSHORT bufLen; // Pointer to buffer length field in SMB;
+
+ NTSTATUS ntStatus;
+ OEM_STRING ansiString;
+ UNICODE_STRING unicodeString;
+
+ MSG_LOG(TRACE,"In MsgGetMachineName %d\n",net);
+
+ ncb = &ncbArray[net][ncbi]; // Get pointer to NCB
+
+ if(mpncbistate[net][ncbi] != MESSTART) {
+ //
+ // If wrong time for this block
+ //
+ // Hang up and start a new listen,
+ // log an error if mpncbistate[net][ncbi] == MESCONT;
+ // otherwise, do not log the error.
+ //
+
+ if(mpncbistate[net][ncbi] == MESCONT) {
+ //
+ // Log error if message in progress
+ //
+ Msglogmbe(MESERR,net,ncbi);
+ }
+ //
+ // HANGUP and LISTEN again
+ //
+ MsgRestart(net,ncbi);
+ return;
+ }
+
+ mpncbistate[net][ncbi] = MESSTOP; // End of message state
+
+ //
+ // Check if SMB is malformed
+ //
+ if(MsgVerifySmb(net,ncbi,SMB_COM_GET_MACHINE_NAME,0,"") != 0) {
+ return;
+ }
+
+ buffer = ncb->ncb_buffer; // Get pointer to buffer
+ cp = &buffer[sizeof(SMB_HEADER)]; // Skip to end of header
+ *cp++ = '\0'; // Return no parameters
+
+ //
+ // Length of name plus two
+ //
+ bufLen = (PSHORT)&cp[0];
+ *bufLen = MachineNameLen + (SHORT)2;
+
+ cp += sizeof(MachineNameLen); // Skip over buffer length field
+
+ *cp++ = '\004'; // Null-terminated string next
+
+#ifdef UNICODE
+ //
+ // Translate the machineName from Unicode to Ansi and place it into
+ // the buffer at the temp pointer location.
+ //
+ unicodeString.Length = (USHORT)(STRLEN(machineName)*sizeof(WCHAR));
+ unicodeString.MaximumLength = (USHORT)((STRLEN(machineName)+1) * sizeof(WCHAR));
+ unicodeString.Buffer = machineName;
+
+ ansiString.Length = MachineNameLen;
+ ansiString.MaximumLength = *bufLen;
+ ansiString.Buffer = cp;
+
+ ntStatus = RtlUnicodeStringToOemString(
+ &ansiString,
+ &unicodeString,
+ FALSE); // Don't Allocate the ansiString Buffer
+
+ if (!NT_SUCCESS(ntStatus)) {
+ MSG_LOG(ERROR,
+ "MsgGetMachineName:RtlUnicodeStringToOemString Failed rc=%X\n",
+ ntStatus);
+ return; // They return for other errors, so I will here too.
+ }
+
+ *(cp + ansiString.Length) = '\0';
+
+#else
+ UNUSED(unicodeString);
+ UNUSED(ansiString);
+ UNUSED(ntStatus);
+ strcpy( cp, (LPSTR)machineName); // Copy machine name
+#endif
+
+ cp += MachineNameLen + 1; // Skip over machine name
+
+ //
+ // Set length of buffer
+ //
+ ncb->ncb_length = (USHORT)(cp - buffer);
+
+ ncb->ncb_command = NCBSEND | ASYNCH; // Send (no wait)
+ mpncbifun[net][ncbi] = (LPNCBIFCN)MsgSendService; // Set function pointer
+ MsgCallNetBios(net,ncb,ncbi); // Issue the net bios call
+}
+
+/*
+ * MsgHangupService - Service completed HANGUP net bios calls
+ *
+ * This function is invoked by NCBService() to process completed
+ * HANGUP net bios calls. In response to a completed HANGUP,
+ * this function issues a new LISTEN net bios call.
+ *
+ * MsgHangupService (net, ncbi, retval)
+ *
+ * ENTRY
+ * net - network index
+ * ncbi - Network Control Block index
+ * retval - value returned from net bios call
+ *
+ * RETURN
+ * nothing
+ *
+ * SIDE EFFECTS
+ *
+ * Calls MsgStartListen() to issue a new LISTEN net bios call. Calls
+ * MsgNetBiosError() on errors it does not know how to deal with.
+ */
+
+STATIC VOID
+MsgHangupService(
+ DWORD net, // Which network
+ DWORD ncbi, // Index of completed NCB
+ CHAR retval // HANGUP return value
+ )
+{
+ MSG_LOG(TRACE,"In MsgHangupService %d\n",net);
+
+ switch(retval) { // Switch on return value
+ case NRC_GOODRET: // Success
+ case NRC_CMDTMO: // Command timed out
+ case NRC_SCLOSED: // Session closed
+ case NRC_SABORT: // Session ended abnormally
+
+ //
+ // BBSP - check if the name for this NCB ends in 0x3. If so,
+ // add the 0x5 version and don't reissue the listen on the 0x03.
+ // No need to worry about doing it on all nets, since on a machine
+ // with more than one the 0x05 name will never leave home, and
+ // the 0x03 version will never get a message.
+ //
+
+ MSG_LOG(TRACE," MsgHangupService: Issue a new LISTEN\n",0);
+ MsgStartListen(net,ncbi); // Issue a new LISTEN net bios call
+ break;
+
+ default:
+ //
+ // Invoke error handler
+ //
+ MSG_LOG(TRACE," MsgHangupService: Unknown return value %x\n",retval);
+ MsgNetBiosError(net,&ncbArray[net][ncbi],retval,ncbi);
+
+ //
+ // BBSP - check if the name for this NCB ends in 0x3. If so,
+ // add the 0x5 version and don't reissue the listen on the 0x03.
+ // See note above.
+ //
+
+ MSG_LOG(TRACE," MsgHangupService: Issue a new LISTEN\n",0);
+ MsgStartListen(net,ncbi); // Issue a new LISTEN net bios call
+ break;
+ }
+}
+
+/*
+ * MsgListenService - service completed LISTEN net bios calls
+ *
+ * This function is called when a LISTEN net bios call completes
+ * either due to an error or due to the establishment of a
+ * session. In the latter case, it initiates message reception.
+ *
+ * MsgListenService (net, ncbi, retval)
+ *
+ * ENTRY
+ * net - network index
+ * ncbi - Network Control Block index
+ * retval - value returned from NCB call
+ *
+ * RETURN
+ * nothing
+ *
+ * If a session is established, this function issues a RECEIVE ANY
+ * net bios call to initiate reception of a message. If the function
+ * is invoked because the net bios call has failed due to the deletion
+ * of a name from the local network adapter's name table, then this
+ * function calls the routine responsible for deleting names from the
+ * Message Server's data area. This is the mechanism by which the
+ * NETNAME command notofies the Message Server of a deletion.
+ *
+ * SIDE EFFECTS
+ *
+ * Calls MsgCallNetBios() to issue a RECEIVE ANY net bios call. Calls
+ * MsgDeleteName() if it is informed of the deletion of a name. Calls
+ * MsgNetBiosError() on errors it does not know how to deal with. Sets
+ * mpncbifun[ncbi] according to the net bios call it issues.
+ */
+
+STATIC VOID
+MsgListenService(
+ DWORD net, // Which network?
+ DWORD ncbi, // Index of completed NCB
+ CHAR retval // LISTEN return value
+ )
+{
+ PNCB ncb; // Pointer to completed NCB
+
+ static struct _SAVE_INFO {
+ DWORD net;
+ DWORD ncbi;
+ DWORD count;
+ } SaveInfo = {0xffffffff, 0xffffffff, 0};
+
+
+ MSG_LOG(TRACE,"In MsgListenService %d\n",net);
+
+ ncb = &ncbArray[net][ncbi]; // Get pointer to completed NCB
+
+ switch(retval) {
+ case NRC_GOODRET:
+ //
+ // Success
+ //
+
+ //
+ // Reset the No Resources error count if a good return code comes
+ // in for this name.
+ //
+ if ((SaveInfo.net == net) &&
+ (SaveInfo.ncbi == ncbi)) {
+
+ SaveInfo.count = 0;
+ }
+
+ mpncbistate[net][ncbi] = MESSTART; // Message start state
+ mpncbifun[net][ncbi] = (LPNCBIFCN)MsgReceiveService;
+ //
+ // Set function pointer
+ //
+ ncb->ncb_command = NCBRECV | ASYNCH;
+
+ //
+ // Receive any (no wait)
+ //
+ ncb->ncb_length = BUFLEN; // Reset length of buffer
+ MsgCallNetBios(net,ncb,ncbi); // Issue the net bios call
+ break;
+
+ case NRC_LOCTFUL:
+ //
+ // Session Table Full
+ // Log error in system error log file
+ //
+ MSG_LOG(TRACE,"[%d]MsgListenService: Session Table is full\n",net);
+ mpncbifun[net][ncbi] = (LPNCBIFCN)MsgSesFullService; // Set function pointer
+ ncb->ncb_command = NCBDELNAME | ASYNCH; // Delete name (no wait)
+ MsgCallNetBios(net,ncb,ncbi); // Issue the net bios call
+ break;
+
+ case NRC_NOWILD: // Name not found
+ // Name not found
+ // Name deleted between end of one session and start of next
+
+ case NRC_NAMERR:
+ //
+ // Name was deleted
+ //
+ MSG_LOG(TRACE,"[%d]MsgListenService: Name was deleted for some reason\n",net);
+ MsgDeleteName(net,ncbi); // Handle the deletion
+ break;
+
+ case NRC_NORES:
+ //
+ // We need to cover the case where we are adding a new name and
+ // starting a new listen. In this case, the thread that is adding
+ // the names will hangup and delete the name.
+ //
+ // So here we will sleep for a moment and then check to see if the
+ // name is still there. If not we just return without setting
+ // up to handle the NCB anymore. If the name is still there, then
+ // we travel down the default path and try again.
+ //
+
+ MSG_LOG(TRACE,"[%d]No Net Resources. SLEEP FOR A WHILE\n",net);
+ Sleep(5000);
+ MSG_LOG(TRACE,"[%d]No Net Resources. WAKEUP\n",net);
+
+ if (SD_NAMEFLAGS(net,ncbi) == NFDEL) {
+ MSG_LOG(TRACE,"[%d]MsgListenService: No Net Resources & Name Deleted\n",net);
+ ncb->ncb_cmd_cplt = 0xff;
+ }
+ else {
+ //
+ // If a session goes away and we can't gain the resources for
+ // it again, then we will attempt to re-connect MAX_RETRIES
+ // times. If we still cannot connect, then the name will be
+ // deleted.
+ //
+ // BUGBUG: It would be useful to somehow keep the name in
+ // the table, but give it a special status (ADDED - BUT NO
+ // SESSIONS AVAILABLE that could be returned to the user.
+ // This special status could also be used to allow us to
+ // wake-up periodically and try again.
+ //
+
+ if ((SaveInfo.net == net) &&
+ (SaveInfo.ncbi == ncbi)) {
+
+ if (SaveInfo.count >= MAX_RETRIES) {
+ //
+ // Delete the Name
+ //
+ // BUGBUG: Write to Error Log
+ //
+ MSG_LOG(ERROR,"Out of Resources, Deleting %s\n",
+ SD_NAMES(net,ncbi));
+
+ MsgDeleteName(net,ncbi);
+
+ }
+ else {
+ SaveInfo.count++;
+ MSG_LOG(TRACE,"MsgListenService: new SaveInfo count = %d\n",
+ SaveInfo.count);
+ }
+ }
+ else {
+ SaveInfo.net = net;
+ SaveInfo.ncbi = ncbi;
+ SaveInfo.count = 0;
+ }
+
+ }
+ break;
+
+ default:
+ //
+ // Other failure
+ //
+ MSG_LOG(TRACE,"MsgListenService: Unrecognized retval %x\n",retval);
+
+ MsgNetBiosError(net,ncb,retval,ncbi);
+
+ // The listen error has been logged. Now as much as possible to
+ // get another listen staterd. This involves performing
+ // a HangUp for this name (which should fail but might help
+ // to clear out the err) and then re-issuing the listen. If the
+ // same error occurs SHUTDOWN_THRESHOLD consecutive times then
+ // MsgNetBiosError will shut down the message server.
+ //
+
+ MsgRestart(net,ncbi); // Attempt to restart the Listen
+ break;
+ }
+}
+
+/*
+ * Msgmblockbeg - process the header of a multi-block message
+ *
+ * This function acknowledges receipt of the header of a multi-block
+ * message and initiates logging of that message.
+ *
+ * Msgmblockbeg (net, ncbi)
+ *
+ * ENTRY
+ * net - network index
+ * ncbi - Network Control Block index
+ *
+ * RETURN
+ * nothing
+ *
+ * This function is called from ReceivePost() (RecAnyPost()).
+ * It first check to see if it is appropriate for the ncbi'th
+ * name to have received a begin-multi-block-message SMB at the
+ * current time. It verifies the correctness of the SMB in the
+ * ncbi'th buffer. It initiates logging of the multi-block message,
+ * and it sends an acknowledgement to the sender of the message.
+ *
+ * SIDE EFFECTS
+ *
+ * Calls MsgRestart() to terminate the session if the SMB has arrived
+ * at a bad time. Calls MsgVerifySmb() to check the SMB for correctness.
+ * Calls logmbb() to begin logging. Calls MsgSendAck() to send an
+ * acknowledgement to the sender of the message.
+ */
+
+STATIC VOID
+Msgmblockbeg(
+ DWORD net, // Which network?
+ DWORD ncbi // Index to NCB
+ )
+{
+ PNCB ncb; // Pointer to NCB
+ LPBYTE buffer; // Pointer to SMB buffer
+ LPSTR cp; // Save pointer
+ LPSTR from; // From-name
+ LPSTR to; // To-name
+
+ MSG_LOG(TRACE,"In Msgmblockbeg %d\n",net);
+
+ ncb = &ncbArray[net][ncbi]; // Get pointer to NCB
+ if(mpncbistate[net][ncbi] != MESSTART) { // If wrong time for this block
+
+ //
+ // Hang up and start a new listen,
+ // log an error if mpncbistate[net][ncbi] == MESCONT;
+ // otherwise, do not log the error.
+ //
+ if(mpncbistate[net][ncbi] == MESCONT) {
+ //
+ // Log error if message in progress
+ //
+ Msglogmbe(MESERR,net,ncbi);
+ }
+
+ //
+ // HANGUP and LISTEN again
+ //
+ MsgRestart(net,ncbi);
+ return;
+ }
+ mpncbistate[net][ncbi] = MESCONT; // Processing multi-block message
+ if(MsgVerifySmb(net,ncbi,SMB_COM_SEND_START_MB_MESSAGE,0,"ss") != 0) {
+ //
+ // Check for malformed SMB
+ //
+ return;
+ }
+
+ buffer = ncb->ncb_buffer; // Get pointer to buffer
+ from = &buffer[sizeof(SMB_HEADER) + 4]; // Save pointer to from-name
+ to = &from[strlen(from) + 2]; // Save pointer to to-name
+
+ if(Msglogmbb(from,to,net,ncbi)) { // If attempt to log header fails
+ mpncbistate[net][ncbi] = MESERR; // Enter error state
+ //
+ // Send negative acknowledgement
+ //
+ MsgSendAck(net,ncbi,'\002',SMB_ERR_NO_ROOM);
+ return;
+ }
+
+ //
+ // Indicate message received
+ //
+ SmbPutUshort(&(((PSMB_HEADER)buffer)->Error), (USHORT)SMB_ERR_SUCCESS);
+
+// ((PSMB_HEADER)buffer)->Error = (USHORT)SMB_ERR_SUCCESS;
+
+ cp = &buffer[sizeof(SMB_HEADER)]; // Point just past header
+ *cp++ = '\001'; // One parameter
+ ((short UNALIGNED far *) cp)[0] = ++mgid; // Message group ID
+ mpncbimgid[net][ncbi] = mgid; // Save message group i.d.
+ ((short UNALIGNED far *) cp)[1] = 0; // No buffer
+ ncb->ncb_length = sizeof(SMB_HEADER) + 5; // Set length of buffer
+ ncb->ncb_command = NCBSEND | ASYNCH; // Send(no wait)
+
+ //
+ // Set function pointer & issue the net bios call
+ //
+
+ mpncbifun[net][ncbi] = (LPNCBIFCN)MsgSendService;
+
+ MsgCallNetBios(net,ncb,ncbi);
+}
+
+/*
+ * Msgmblockend - process end of a multi-block message
+ *
+ * This function acknowledges receipt of the end of a
+ * multi-block message and terminates logging of the message.
+ *
+ * Msgmblockend (net, ncbi)
+ *
+ * ENTRY
+ * net - network index
+ * ncbi - Network Control Block index
+ *
+ * RETURN
+ * nothing
+ *
+ * This function is called from ReceivePost() (RecAnyPost()).
+ * It first check to see if it is appropriate for the ncbi'th
+ * name to have received an end-multi-block-message SMB at the
+ * current time. It verifies the correctness of the SMB in the
+ * ncbi'th buffer. It terminates logging, and it sends an
+ * acknowledgement to the sender of the message.
+ *
+ * SIDE EFFECTS
+ *
+ * Calls MsgRestart() to terminate the session if the SMB has arrived
+ * at a bad time. Calls MsgVerifySmb() to check the SMB for correctness.
+ * Calls logmbe() to terminate logging. Calls MsgSendAck() to send an
+ * acknowledgement to the sender of the message.
+ */
+
+STATIC VOID
+Msgmblockend(
+ DWORD net, // Which network?
+ DWORD ncbi // Index to NCB
+ )
+{
+ PNCB ncb; // Pointer to NCB
+ LPBYTE buffer; // Pointer to SMB buffer
+ int error; // Error flag
+ char smbrclass; // SMB return class
+ unsigned short smbrcode; // SMB return code
+
+ MSG_LOG(TRACE,"In Msgmblockend %d\n",net);
+
+ ncb = &ncbArray[net][ncbi]; // Get pointer to NCB
+ if(mpncbistate[net][ncbi] != MESCONT) { // If wrong time for this block
+ //
+ // Hang up and start a new listen,
+ // no error to log since no message in progress.
+ // HANGUP and LISTEN again
+ //
+ MsgRestart(net,ncbi);
+ return;
+ }
+ mpncbistate[net][ncbi] = MESSTOP; // End of message state
+ if(MsgVerifySmb(net,ncbi,SMB_COM_SEND_END_MB_MESSAGE,1,"") != 0) {
+ //
+ // If SMB is malformed, log error and return
+ //
+ Msglogmbe(MESERR,net,ncbi);
+ return;
+ }
+ buffer = ncb->ncb_buffer; // Get pointer to buffer
+
+ if(*((short UNALIGNED far *) &buffer[sizeof(SMB_HEADER) + 1]) != mpncbimgid[net][ncbi]) {
+
+ //
+ // If i.d. does not match
+ //
+ error = 1; // Error found
+ smbrclass = '\002'; // Error return
+ smbrcode = SMB_ERR_ERROR; // Non-specific error
+ }
+ else {
+ //
+ // Else if message group i.d. okay
+ //
+ error = 0; // No error found
+ smbrclass = '\0'; // Good return
+ smbrcode = (USHORT)SMB_ERR_SUCCESS; // Message received
+ }
+ MsgSendAck(net,ncbi,smbrclass,smbrcode); // Send acknowledgement
+ if(!error) Msglogmbe(MESSTOP,net,ncbi); // Log end of message
+}
+
+/*
+ * Msgmblocktxt - process text of a multi-block message
+ *
+ * This function acknowledges receipt of a block of text of a
+ * multi-block message and logs that block.
+ *
+ * Msgmblocktxt (net, ncbi)
+ *
+ * ENTRY
+ * net - Network index
+ * ncbi - Network Control Block index
+ *
+ * RETURN
+ * nothing
+ *
+ * This function is called from ReceivePost() (RecAnyPost()).
+ * It first check to see if it is appropriate for the ncbi'th
+ * name to have received a multi-block-message-text SMB at the
+ * current time. It verifies the correctness of the SMB in the
+ * ncbi'th buffer. It logs the text block, and it sends an
+ * acknowledgement to the sender of the message.
+ *
+ * SIDE EFFECTS
+ *
+ * Calls MsgRestart() to terminate the session if the SMB has arrived
+ * at a bad time. Calls MsgVerifySmb() to check the SMB for correctness.
+ * Calls logmbt() to log the text block. Calls MsgSendAck() to send an
+ * acknowledgement to the sender of the message.
+ */
+
+STATIC VOID
+Msgmblocktxt(
+ DWORD net, // Which network?
+ DWORD ncbi // Index to NCB
+ )
+{
+ PNCB ncb; // Pointer to NCB
+ LPBYTE buffer; // Pointer to SMB buffer
+ LPSTR cp; // Save pointer
+ char smbrclass; // SMB return class
+ unsigned short smbrcode; // SMB return code
+
+ MSG_LOG(TRACE,"In Msgmblocktxt %d\n",net);
+
+ ncb = &ncbArray[net][ncbi]; // Get pointer to NCB
+ if(mpncbistate[net][ncbi] != MESCONT) { // If wrong time for this block
+ //
+ // HANGUP and start a new LISTEN.
+ // no error to log since no message in progress.
+ //
+ MsgRestart(net,ncbi);
+ return;
+ }
+ if(MsgVerifySmb(net,ncbi,SMB_COM_SEND_TEXT_MB_MESSAGE,1,"b") != 0) {
+ //
+ // If SMB is malformed
+ //
+ Msglogmbe(MESERR,net,ncbi); // Log error
+ return;
+ }
+ buffer = ncb->ncb_buffer; // Get pointer to buffer
+ cp = &buffer[sizeof(SMB_HEADER) + 1]; // Skip to message group i.d.
+
+ if(*((short UNALIGNED far *) cp) != mpncbimgid[net][ncbi]) {
+ //
+ // If i.d. does not match
+ //
+ smbrclass = '\002'; // Error return
+ smbrcode = SMB_ERR_ERROR; // Non-specific error
+ }
+ else if(Msglogmbt(&buffer[sizeof(SMB_HEADER) + 6], net, ncbi)) {
+ //
+ // Else if text cannot be logged
+ //
+ mpncbistate[net][ncbi] = MESERR; // Enter error state
+ smbrclass = '\002'; // Error return
+ smbrcode = SMB_ERR_NO_ROOM; // No room in buffer
+ }
+ else {
+ //
+ // Else if message logged okay
+ //
+ smbrclass = '\0'; // Good return
+ smbrcode = (USHORT)SMB_ERR_SUCCESS; // Message received
+ }
+
+ MsgSendAck(net,ncbi,smbrclass,smbrcode); // Send acknowledgement
+}
+
+/*
+ * MsgNetBiosError - process an error returned by a net bios call
+ *
+ * This function performs generic error handling for
+ * failed net bios calls. If the error is a fatal one because the error
+ * counted exceeded the SHUTDOWN_THRESHOLD, this routine begins a forced
+ * shutdown of the messenger. This shutdown will not complete until all
+ * threads have woken up and returned to the main loop where the
+ * messenger status is examined.
+ *
+ * MsgNetBiosError (net, ncb, retval, ncbi)
+ *
+ * ENTRY
+ * net - Network index
+ * ncb - Network Control Block pointer
+ * retval - value returned from the net bios call
+ * ncbi - ncb array index of ncb which resulted in this error
+ *
+ * RETURN
+ * state of the Messenger: Either RUNNING or STOPPING.
+ *
+ * Chcks in ncbStatus array that this is not a repeated error
+ * that has already been entered in the error log, and logs
+ * the error.
+ *
+ * SIDE EFFECTS
+ *
+ * Calls MsgErrorLogWrite() to log errors in the Network System Error Log.
+ * If this is a new error, the error status in the ncbStatus array for this
+ * ncb is updated so that the same error will not be reported if it is
+ * repeated.
+ */
+
+STATIC DWORD
+MsgNetBiosError(
+ DWORD net, // Which network?
+ PNCB ncb, // Pointer to NCB
+ char retval, // Error code
+ DWORD ncbi // Index of array causing the error
+ )
+{
+ //
+ // First check the immediate status for this ncb. If it is in error
+ // then this must be the error, else it is a final error code.
+ //
+
+ if (ncbStatus[net][ncbi].this_immediate != 0) {
+
+ if(ncbStatus[net][ncbi].this_immediate ==
+ ncbStatus[net][ncbi].last_immediate) {
+
+ if (++(ncbStatus[net][ncbi].rep_count) >= SHUTDOWN_THRESHOLD) {
+
+ //
+ // The same error has occured SHUTDOWN_THRESHOLD times in
+ // a row. Write to the event log and shutdown the messenger.
+ //
+
+ MsgErrorLogWrite(
+ NELOG_Msg_Shutdown,
+ SERVICE_MESSENGER,
+ (LPBYTE) ncb,
+ sizeof(NCB),
+ NULL,
+ 0);
+
+ MSG_LOG(ERROR,"MsgNetBiosError1:repeated MsgNetBiosError(ncb error) - shutting down\n",0);
+ return(MsgBeginForcedShutdown(
+ PENDING,
+ NERR_InternalError));
+ }
+ return(RUNNING); // Same as last error so don't report it
+ }
+ else {
+ //
+ // This error was not the same as the last error. So just
+ // update the last error place holder.
+ //
+ ncbStatus[net][ncbi].last_immediate =
+ ncbStatus[net][ncbi].this_immediate;
+ }
+ }
+ else {
+ //
+ // Must have been a final ret code (ncb completion code) that was
+ // in error.
+ //
+ if(ncbStatus[net][ncbi].this_final == ncbStatus[net][ncbi].last_final) {
+
+ if (++(ncbStatus[net][ncbi].rep_count) >= SHUTDOWN_THRESHOLD) {
+
+ MsgErrorLogWrite(
+ NELOG_Msg_Shutdown,
+ SERVICE_MESSENGER,
+ (LPBYTE) ncb,
+ sizeof(NCB),
+ NULL,
+ 0);
+
+ MSG_LOG(ERROR,"MsgNetBiosError2:repeated MsgNetBiosError (final ret code) - shutting down\n",0);
+ return(MsgBeginForcedShutdown(
+ PENDING,
+ NERR_InternalError));
+ }
+ return(RUNNING); // Same as last error so don't report it
+ }
+ else {
+ ncbStatus[net][ncbi].last_final = ncbStatus[net][ncbi].this_final;
+ }
+ }
+ //
+ // Here if a new error has occured so log it in the error log.
+ //
+
+ MsgErrorLogWrite(
+ NELOG_Ncb_Error,
+ SERVICE_MESSENGER,
+ (LPBYTE) ncb,
+ sizeof(NCB),
+ NULL,
+ 0); // Enter error in system error log
+
+
+ MSG_LOG(ERROR,"MsgNetBiosError3:An unexpected NCB was received 0x%x\n",retval);
+
+ UNUSED(retval);
+
+#if DBG
+ MsgDumpNcb(ncb);
+#endif
+
+ return (RUNNING);
+}
+
+/*
+ * MsgReceiveService - service a completed RECEIVE net bios call
+ *
+ * This function is called to service a completed RECEIVE net
+ * bios call. For successful completions, it examines the data
+ * received to determine which of the SMB-processing functions
+ * should be called.
+ *
+ * MsgReceiveService (net, ncbi, retval)
+ *
+ * ENTRY
+ * net - network index
+ * ncbi - Network Control Block index
+ * retval - value returned by the net bios
+ *
+ * RETURN
+ * nothing
+ *
+ * This function dispatches SMB's received to the proper processing
+ * function. It also handles a number of error conditions (noted
+ * in the code below).
+ *
+ * SIDE EFFECTS
+ *
+ * See handling of error conditions.
+ */
+
+STATIC VOID
+MsgReceiveService(
+ DWORD net, // Which network?
+ DWORD ncbi, // Index to completed NCB
+ char retval // SEND return value
+ )
+{
+ PNCB ncb; // Pointer to completed NCB
+ PSMB_HEADER smb; // Pointer to SMB header
+
+
+ MSG_LOG(TRACE,"In MsgReceiveService %d\n",net);
+
+ ncb = &ncbArray[net][ncbi]; // Get pointer to NCB
+
+ switch(retval) {
+
+ case NRC_GOODRET: // Success
+ if(ncb->ncb_length >= sizeof(SMB_HEADER)) {
+ //
+ // If we could have an SMB
+ //
+ smb = (PSMB_HEADER)ncb->ncb_buffer;
+
+ // Get pointer to buffer
+ switch(smb->Command) { // Switch on SMB function code
+ case SMB_COM_SEND_MESSAGE: // Single block message
+ Msgsblockmes(net,ncbi);
+ return;
+
+ case SMB_COM_SEND_START_MB_MESSAGE: // Beginning of multi-block message
+ Msgmblockbeg(net,ncbi);
+ return;
+
+ case SMB_COM_SEND_END_MB_MESSAGE: // End of multi-block message
+ Msgmblockend(net,ncbi);
+ return;
+
+ case SMB_COM_SEND_TEXT_MB_MESSAGE: // Text of multi-block message
+ Msgmblocktxt(net,ncbi);
+ return;
+
+ case SMB_COM_GET_MACHINE_NAME: // Get Machine Name
+ MsgGetMachineName(net,ncbi);
+ return;
+
+ case SMB_COM_FORWARD_USER_NAME: // Add forward-name
+ //
+ // Not supported in NT.
+ // for now fall through as if unrecognized SMB.
+ //
+
+ case SMB_COM_CANCEL_FORWARD: // Delete forward-name
+ //
+ // Not supported in NT.
+ // for now fall through as if unrecognized SMB.
+ //
+
+ default: // Unrecognized SMB
+ break;
+ }
+ }
+ //
+ // Enter error in system error log
+ //
+
+ MsgErrorLogWrite(
+ NELOG_Msg_Unexpected_SMB_Type,
+ SERVICE_MESSENGER,
+ (LPBYTE)ncb->ncb_buffer,
+ ncb->ncb_length,
+ NULL,
+ 0);
+
+ MSG_LOG(ERROR,"MsgReceiveService:An illegal SMB was received\n",0);
+ //
+ // HANGUP and LISTEN again
+ //
+ MsgRestart(net,ncbi);
+ break;
+
+ case NRC_CMDTMO: // Command timed out
+
+ if(mpncbistate[net][ncbi] == MESCONT) {
+ //
+ // If middle of multi-block message
+ //
+ Msglogmbe(MESERR,net,ncbi); // Log an error
+ }
+ //
+ // HANGUP and start new LISTEN
+ //
+ MsgRestart(net,ncbi);
+ break;
+
+ case NRC_SCLOSED: // Session closed
+ case NRC_SABORT: // Session ended abnormally
+
+ if(mpncbistate[net][ncbi] == MESCONT) {
+ //
+ // If middle of multi-block message, Log an error
+ //
+ Msglogmbe(MESERR,net,ncbi);
+ }
+ //
+ // Start a new LISTEN
+ //
+ MsgStartListen(net,ncbi);
+ break;
+
+ default: // Other errors
+ MSG_LOG(TRACE,"MsgReceiveService: Unrecognized retval %x\n",retval);
+
+ MsgNetBiosError(net,ncb,retval,ncbi);
+ MsgRestart(net,ncbi); // HANGUP and LISTEN again
+ break;
+ }
+}
+
+/*
+ * MsgRestart - issue a HANGUP net bios call
+ *
+ * This function is invoked to issue a HANGUP net bios call using
+ * a particular Network Control Block.
+ *
+ * MsgRestart (net, ncbi)
+ *
+ * ENTRY
+ * net - network index
+ * ncbi - Network Control Block index
+ *
+ * RETURN
+ * nothing
+ *
+ * This function assumes that the NCB_LSN, NCB_POST, and NCB_LANA
+ * fields of the Network Control Block are already properly set.
+ * It sets the NCB_CMD field.
+ *
+ * This function is named "MsgRestart" since the very next routine
+ * to process the NCB used to issue the HANGUP should be
+ * MsgHangupService() which always invokes MsgStartListen() (assuming
+ * the HANGUP completes properly). Thus, the net effect of
+ * calling MsgRestart() is to terminate the current session and
+ * issue a LISTEN to start a new one.
+ *
+ * SIDE EFFECTS
+ *
+ * Calls MsgCallNetBios() to issue the net bios call. Sets mpncbifun[ncbi]
+ * to the address of MsgHangupService().
+ */
+
+STATIC VOID
+MsgRestart(
+ DWORD net, // Which network?
+ DWORD ncbi // Index to NCB
+ )
+{
+ PNCB ncb; // Pointer to Network Control Block
+
+ MSG_LOG(TRACE,"In MsgRestart %d\n",net);
+
+ mpncbifun[net][ncbi] = (LPNCBIFCN)MsgHangupService; // Set function pointer
+ ncb = &ncbArray[net][ncbi]; // Get pointer to NCB
+ ncb->ncb_command = NCBHANGUP | ASYNCH; // Hang up (no wait)
+
+ MsgCallNetBios(net,ncb,ncbi); // Issue the net bios call
+}
+
+/*
+ * Msgsblockmes - process a single block message
+ *
+ * This function logs and acknowledges a single block message.
+ *
+ * Msgsblockmes (net, ncbi)
+ *
+ * ENTRY
+ * net - network index
+ * ncbi - Network Control Block index
+ *
+ * RETURN
+ * nothing
+ *
+ * This function is called from ReceivePost() (RecAnyPost()).
+ * It first check to see if it is appropriate for the ncbi'th
+ * name to have received a single block message SMB at the current
+ * time. It verifies the correctness of the SMB in the ncbi'th
+ * buffer. It attempts to log the single block message, and it
+ * sends an acknowledgement to the sender of the message.
+ *
+ * SIDE EFFECTS
+ *
+ * Calls MsgRestart() to terminate the session if the SMB has arrived
+ * at a bad time. Calls MsgVerifySmb() to check the SMB for correctness.
+ * Calls logsbm() to log the message. Calls MsgSendAck() to send an
+ * acknowledgement to the sender of the message.
+ */
+
+STATIC VOID
+Msgsblockmes(
+ DWORD net, // Which network ?
+ DWORD ncbi // Index to NCB
+ )
+{
+ PNCB ncb; // Pointer to NCB
+ LPBYTE buffer; // Pointer to SMB buffer
+ LPSTR cp; // Save pointer
+ LPSTR from; // From-name
+ LPSTR to; // To-name
+
+ MSG_LOG(TRACE,"In Msgsblockmes %d\n",net);
+
+ ncb = &ncbArray[net][ncbi]; // Get pointer to NCB
+
+ if(mpncbistate[net][ncbi] != MESSTART) {
+
+ //
+ // If wrong time for this block
+ // Hang up and start a new listen,
+ // log an error if mpncbistate[net][ncbi] == MESCONT;
+ // otherwise, do not log the error.
+ //
+ // Log error if message in progress
+ //
+
+ if(mpncbistate[net][ncbi] == MESCONT) {
+ Msglogmbe(MESERR,net,ncbi);
+ }
+
+ //
+ // HANGUP and LISTEN again
+ //
+
+ MsgRestart(net,ncbi);
+ return;
+ }
+
+ mpncbistate[net][ncbi] = MESSTOP; // End of message state
+
+ //
+ // Check for malformed SMB
+ //
+
+ if(MsgVerifySmb(net,ncbi,(unsigned char)SMB_COM_SEND_MESSAGE,0,"ssb") != 0) {
+ return;
+ }
+
+ buffer = ncb->ncb_buffer; // Get pointer to buffer
+
+ from = &buffer[sizeof(SMB_HEADER) + 4]; // Save pointer to from-name
+ to = &from[strlen(from) + 2]; // Save pointer to to-name
+ cp = &to[strlen(to) + 2]; // Skip over the name
+
+
+ if(Msglogsbm(from,to,cp)) {
+
+ //
+ // If message cannot be logged, enter error state
+ // and send error acknowledgement.
+ //
+ mpncbistate[net][ncbi] = MESERR;
+ MsgSendAck(net,ncbi,'\002',SMB_ERR_NO_ROOM);
+ }
+ else {
+ //
+ // Otherwise acknowledge success
+ //
+ MsgSendAck(net, ncbi, SMB_ERR_SUCCESS, (USHORT)SMB_ERR_SUCCESS);
+ }
+}
+
+/*
+ * MsgSendAck - send an SMB to acknowledge a network transaction
+ *
+ * This function is used to send a Server Message Block to some
+ * machine with whom a session has been established acknowledging
+ * (positively or negatively) the occurrence of some event pertaining
+ * to the session.
+ *
+ * MsgSendAck (net, ncbi, smbrclass, smbrcode)
+ *
+ * ENTRY
+ * net - Network index
+ * ncbi - Network Control Block index
+ * smbrclass - SMB return class
+ * smbrcode - SMB return code
+ *
+ * RETURN
+ * nothing
+ *
+ * Using the NCB index to locate the buffer containing the last SMB
+ * received in the session, this function sets the return class and
+ * the return code in that SMB according to its arguments and sends
+ * the SMB to the other party in the session. This function will
+ * not return any parameters or buffers in that SMB.
+ *
+ * SIDE EFFECTS
+ *
+ * This function calls MsgCallNetBios() to send the SMB, and it sets
+ * the function vector so that control will pass to Send Service()
+ * when the NCB completes (assuming, of course, that it doesn't
+ * fail immediately).
+ */
+
+STATIC VOID
+MsgSendAck(
+ DWORD net, // Which network?
+ DWORD ncbi, // Network Control Block Index
+ UCHAR smbrclass, // SMB return class
+ USHORT smbrcode // SMB return code
+ )
+{
+ PNCB ncb; // Pointer to NCB
+ LPBYTE buffer; // Pointer to buffer
+
+ MSG_LOG(TRACE,"In MsgSendAck %d\n",net);
+
+ ncb = &ncbArray[net][ncbi]; // Get pointer to NCB
+ buffer = ncb->ncb_buffer; // Get pointer to buffer
+
+ //
+ // No parameters, buffers
+ //
+ buffer[sizeof(SMB_HEADER)+2]=
+ buffer[sizeof(SMB_HEADER)+1]=
+ buffer[sizeof(SMB_HEADER)]= '\0';
+
+ //
+ // Set return information
+ //
+
+ ((PSMB_HEADER)buffer)->ErrorClass = smbrclass; // Set return class
+
+ SmbPutUshort( &(((PSMB_HEADER)buffer)->Error),smbrcode);// Set return code
+
+// ((PSMB_HEADER)buffer)->Error = smbrcode; // Set return code
+ ncb->ncb_length = sizeof(SMB_HEADER) + 3; // Set length of buffer
+ ncb->ncb_command = NCBSEND | ASYNCH; // Send (no wait)
+ mpncbifun[net][ncbi] = (LPNCBIFCN)MsgSendService; // Set function pointer
+
+ MsgCallNetBios(net,ncb,ncbi); // Issue the net bios call
+}
+
+/*
+ * MsgSendService - service a completed SEND net bios call
+ *
+ * This function is called to service a completed SEND net bios
+ * call. The usual course of action is to issue a RECEIVE (ANY)
+ * net bios call.
+ *
+ * MsgSendService (net, ncbi, retval)
+ *
+ * ENTRY
+ * net - network index
+ * ncbi - Network Control Block index
+ * retval - value returned by net bios
+ *
+ * RETURN
+ * nothing
+ *
+ * If a SEND net bios call has completed successfully, this function
+ * will issue a RECEIVE (ANY) net bios call in all cases. The com-
+ * pleted SEND represents one of the following cases:
+ *
+ * - Acknowledgement of a Single Block Message
+ * The message originator will HANG UP, completing the RECEIVE (ANY) call.
+ * - Acknowledgement of the start of a Multi-block Message
+ * The message originator will SEND a text block, completing the RECEIVE
+ * (ANY) call.
+ * - Acknowledgement of text of a Multi-block Message
+ * The message originator will SEND more text or the end of the message,
+ * completing the RECEIVE (ANY) call.
+ * - Acknowledgement of the end of a Multi-block Message
+ * The message originator will HANG UP, completing the RECEIVE (ANY) call.
+ * - Response to a Get Machine Name request
+ * The message originator will HANG UP, completing the RECEIVE (ANY) call.
+ * - Acknowledgement of a Forward Name request
+ * The message originator will HANG UP, completing the RECEIVE (ANY) call.
+ * - Acknowledgement of a Cancel Forward request
+ * The message originator will HANG UP, completing the RECEIVE (ANY) call.
+ * - An error response
+ * The message originator will HANG UP, completing the RECEIVE (ANY) call.
+ *
+ * In all cases, it is clear to the RECEIVE (ANY) service function what its
+ * course of action is.
+ *
+ * SIDE EFFECTS
+ *
+ * If a SEND has completed normally, this function issues a RECEIVE (ANY)
+ * net bios call. In some abnormal cases, this function calls MsgStartListen()
+ * to initiate a new session. In all other abnormal cases, it calls
+ * MsgNetBiosError().
+ */
+
+STATIC VOID
+MsgSendService(
+ DWORD net, // Which network?
+ DWORD ncbi, // Index of completed NCB
+ char retval // SEND return value
+ )
+{
+ PNCB ncb; // Pointer to completed NCB
+ PSMB_HEADER smb; // Pointer to SMB header
+
+ MSG_LOG(TRACE,"In MsgSendService %d\n",net);
+
+ ncb = &ncbArray[net][ncbi]; // Get pointer to completed NCB
+
+ switch(retval) {
+
+ case NRC_GOODRET: // Success
+ mpncbifun[net][ncbi] = (LPNCBIFCN)MsgReceiveService;
+
+ //
+ // Set function pointer
+ //
+ ncb->ncb_command = NCBRECV | ASYNCH; // Receive (no wait)
+ ncb->ncb_length = BUFLEN; // Set length of buffer
+ MsgCallNetBios(net,ncb,ncbi); // Issue the net bios call
+ break;
+
+ case NRC_CMDTMO: // Timeout
+ case NRC_SCLOSED: // Session closed
+ case NRC_SABORT: // Session ended abnormally
+
+ smb = (PSMB_HEADER)ncb->ncb_buffer; // Get pointer to SMB
+
+ if(smb->Command == SMB_COM_SEND_START_MB_MESSAGE || smb->Command == SMB_COM_SEND_TEXT_MB_MESSAGE) {
+
+ //
+ // Message ended abnormally
+ //
+ Msglogmbe(MESERR,net,ncbi);
+ }
+ //
+ // Issue a new LISTEN
+ //
+ MsgStartListen(net,ncbi);
+ break;
+
+ default: // Other failure
+ MSG_LOG(TRACE,"MsgSendService: Unrecognized retval %x\n",retval);
+ MsgNetBiosError(net,ncb,retval,ncbi);
+ //
+ // HANGUP and LISTEN again
+ //
+ MsgRestart(net,ncbi);
+ break;
+ }
+}
+
+/*
+ * MsgServeNCBs - service completed Network Control Blocks
+ *
+ * This function scans the array of NCB's looking for NCB's in
+ * need of service.
+ *
+ * MsgServeNCBs (net)
+ *
+ * ENTRY
+ * net - network to service NCBs on
+ *
+ * RETURN
+ * TRUE - If this function actually services a completed NCB.
+ * FALSE - If this function didn't find any completed NCB's, or if
+ * the service is supposed to stop.
+ *
+ * This function scans the array of NCB's until a completed NCB cannot be
+ * found. Each time a completed NCB is found, the service function specified
+ * in the service function vector (mpncbifun[]) is called to service that
+ * NCB.
+ *
+ * SIDE EFFECTS
+ *
+ * Maintains a private static index of the last NCB examined.
+ * Starts each search at first NCB after the last one serviced.
+ */
+
+BOOL
+MsgServeNCBs(
+ DWORD net // Which network am I serving?
+ )
+{
+ int counter; // A counter
+ BOOL found = FALSE; // Indicates if a completed NCB was found.
+
+ // Bugfix: each net has its own index, addressing
+ // its part NCB array. All index values are initiliazed to zero
+ // when the messenger starts. This solves the muti thread
+ // problem.
+
+ static int ncbIndexArray[MSNGR_MAX_NETS] = {0};
+ // NCB index array
+ DWORD ncbi; // NCB index for this net
+
+
+ //
+ // get NCB index for this net
+ //
+ ncbi = ncbIndexArray[net];
+
+ //
+ // Loop until none completed found
+ //
+ do {
+ //
+ // Loop to search NCB array
+ //
+ for(counter = NCBMAX; counter != 0; --counter, ++ncbi) {
+
+ if(ncbi >= NCBMAX) {
+ ncbi = 0;// Wrap around
+ }
+
+ if(ncbArray[net][ncbi].ncb_cmd_cplt != (unsigned char) 0xff) {
+ found=TRUE;
+ //
+ // If completed NCB found
+ //
+ if(ncbArray[net][ncbi].ncb_cmd_cplt == 0) {
+ //
+ // Clear err on success and error count
+ //
+ ncbStatus[net][ncbi].last_final = 0;
+ ncbStatus[net][ncbi].rep_count = 0;
+ }
+ else {
+ //
+ // Else mark error
+ //
+ ncbStatus[net][ncbi].this_final =
+ ncbArray[net][ncbi].ncb_cmd_cplt;
+
+
+ //
+ // If NetBios is failing with every call, we never
+ // return from this routine be cause there is always
+ // another NCB to service. Therefore, in error
+ // conditions it is necessary to check to see if a
+ // shutdown is in progress. If so, we want to return
+ // so that the adapter loop can handle the shutdown
+ // properly.
+ //
+ if (GetMsgrState() == STOPPING) {
+ ncbIndexArray[net] = ncbi;
+ return(FALSE);
+ }
+ }
+
+ //
+ // Call the service function
+ //
+ (*mpncbifun[net][ncbi])(net,ncbi,ncbArray[net][ncbi].ncb_cmd_cplt);
+
+ ++ncbi; // Start next search after this NCB
+ break; // Exit loop
+ }
+ }
+ }
+ while(counter != 0); // Loop until counter zero
+
+ // update NCB index
+ ncbIndexArray[net] = ncbi;
+ return(found);
+}
+
+/*
+ * MsgSesFullService - complete deletion of a name after a system error
+ *
+ * MsgSesFullService() completes the process of deleting a name from
+ * the message system when the message server is unable to establish
+ * a session for that name.
+ *
+ * MsgSesFullService (net, ncbi, retval)
+ *
+ * ENTRY
+ * net - Network index
+ * ncbi - Network Control Block index
+ * retval - value returned by net bios
+ *
+ * RETURN
+ * nothing
+ *
+ * MsgSesFullService() is called to finish the job of cleaning up when
+ * a LISTEN fails because the local network adapter's session table
+ * is full. Specifically, this function is called when the DELETE
+ * NAME net bios call completes.
+ *
+ * SIDE EFFECTS
+ *
+ * Calls MsgDeleteName() to release the deleted name's entry in the
+ * shared data area. Calls MsgNetBiosError() if the DELETE NAME net
+ * bios call produced unexpected results.
+ */
+
+STATIC VOID
+MsgSesFullService(
+ DWORD net, // Which network ?
+ DWORD ncbi, // Index of completed NCB
+ char retval // SEND return value
+ )
+
+{
+ MSG_LOG(TRACE,"In MsgSesFullService %d\n",net);
+
+ switch(retval) {
+
+ case NRC_GOODRET: // Success
+ case NRC_ACTSES: // Name deregistered
+
+ //
+ // Log deletion in system error log file
+ //
+ MsgDeleteName(net,ncbi); // Delete name from database
+ break;
+
+ default: // Failure
+
+ MSG_LOG(TRACE,"MsgSesFullService: Unrecognized retval %x\n",retval);
+ MsgNetBiosError(net, (PNCB)&ncbArray[ncbi], retval, ncbi);
+ break;
+ }
+}
+
+/*
+ * MsgStartListen - issue a LISTEN net bios call
+ *
+ * This function is invoked to issue a LISTEN net bios call using
+ * a particular Network Control Block. This function does not
+ * examine or change any of the shareable data corresponding to
+ * the NCB in question.
+ *
+ * MsgStartListen (net, ncbi)
+ *
+ * ENTRY
+ * net - network index
+ * ncbi - Network Control Block index
+ *
+ * RETURN
+ * DWORD status from the netbios call.
+ *
+ * This function assumes that the NCB_NAME, NCB_POST, and NCB_LANA
+ * fields of the Network Control Block are already set to the
+ * proper values. It sets the NCB_CNAME, NCB_RTO, NCB_STO, and
+ * NCB_CMD fields.
+ *
+ * SIDE EFFECTS
+ *
+ * Calls MsgCallNetBios() to issue the net bios call. Calls FmtNcbName()
+ * to set the NCB_CNAME field of the NCB. Sets mpncbifun[ncbi] to
+ * the address of MsgListenService().
+ */
+
+NET_API_STATUS
+MsgStartListen(
+ DWORD net, // Which network?
+ DWORD ncbi // Network Control Block index
+ )
+{
+ PNCB ncb; // Pointer to NCB
+ NET_API_STATUS status;
+ TCHAR name[2] = TEXT("*");
+
+ MSG_LOG(TRACE,"In MsgStartListen %d\n",net);
+
+ mpncbifun[net][ncbi] = (LPNCBIFCN)MsgListenService; // Set function pointer
+ ncb = &ncbArray[net][ncbi]; // Get pointer to NCB
+
+ //
+ // Set call name to a"anyone"
+ //
+ status = MsgFmtNcbName(ncb->ncb_callname,name,' ');
+ if (status != NERR_Success) {
+ //
+ // This is a bug if this can't be done.
+ //
+ MSG_LOG(ERROR,"MsgStartListen: NASTY BUG! Cannot format \"*\" name! %X\n",
+ status);
+ NetpAssert(0);
+ }
+
+ ncb->ncb_rto = 60; // Receives time out in 30 sec
+ ncb->ncb_sto = 40; // Sends time out in 20 sec
+ ncb->ncb_command = NCBLISTEN | ASYNCH; // Listen (no wait)
+
+ return(MsgCallNetBios(net,ncb,ncbi)); // Issue the net bios call
+}
+
+/*
+ * MsgVerifySmb - Verify the correctness of a Server Message Block
+ *
+ * This function verifies that a Server Message Block is properly
+ * formed. If it detects a malformed SMB, it terminates the session
+ * and returns a non-zero value.
+ *
+ * MsgVerifySmb (net, ncbi, func, parms, buffers)
+ *
+ * ENTRY
+ * net - network index
+ * ncbi - index to a Network Control Block
+ * func - SMB function code
+ * parms - number of parameters in SMB
+ * buffers - dope vector describing buffers in the SMB
+ *
+ * RETURN
+ * int - error code (zero means no error)
+ *
+ * SIDE EFFECTS
+ *
+ * Calls smbcheck() to check the SMB. Calls MsgRestart() if
+ * smbcheck() reports an error.
+ */
+
+STATIC int
+MsgVerifySmb(
+ DWORD net, // Which network?
+ DWORD ncbi, // Index to Network Control Block
+ UCHAR func, // SMB function code
+ int parms, // Count of parameters in SMB
+ LPSTR buffers // Dope vector of SMB buffers
+ )
+{
+ PNCB ncb; // Pointer to Network Control Block
+ int i; // Return code
+
+
+ ncb = &ncbArray[net][ncbi]; // Get pointer to NCB
+
+ i = Msgsmbcheck(
+ (ncb->ncb_buffer),
+ ncb->ncb_length,
+ func,
+ (char)parms,
+ buffers);
+
+ if (i != 0 ) {
+
+ //
+ // if SMB malformed, Enter error in system error log
+ //
+
+ MsgErrorLogWrite(
+ NELOG_SMB_Illegal,
+ SERVICE_MESSENGER,
+ (LPBYTE)ncb->ncb_buffer,
+ ncb->ncb_length,
+ NULL,
+ 0);
+
+ MSG_LOG(ERROR,"MsgVerifySmb:An illegal SMB was received\n",0);
+ //
+ // HANGUP
+ //
+ MsgRestart(net,ncbi);
+ }
+ return(i); // Return error code
+}
+
+#if DBG
+VOID
+MsgDumpNcb(
+ IN PNCB pNcb
+ )
+
+/*++
+
+Routine Description:
+
+ Displays the NCB on a debug terminal.
+
+Arguments:
+
+
+
+Return Value:
+
+
+
+--*/
+{
+ DbgPrint("NCBADDR: 0x%x\n"
+ "Command: 0x%x\n"
+ "RetCode: 0x%x\n"
+ "LanaNum: 0x%x\n"
+ "CmdCplt: 0x%x\n"
+ "Name : %s\n"
+ "callNam: %s\n",
+ pNcb, pNcb->ncb_command, pNcb->ncb_retcode, pNcb->ncb_lana_num,
+ pNcb->ncb_cmd_cplt, pNcb->ncb_name, pNcb->ncb_callname);
+
+}
+#endif // DBG
diff --git a/private/net/svcdlls/msgsvc/server/sighandl.c b/private/net/svcdlls/msgsvc/server/sighandl.c
new file mode 100644
index 000000000..c70091b3b
--- /dev/null
+++ b/private/net/svcdlls/msgsvc/server/sighandl.c
@@ -0,0 +1,98 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ sighandl.c
+
+Abstract:
+
+ The Messenger Service ControlHandling routines. This file contains
+ the following functions:
+
+ MsgrCtrlHandler
+ uninstall
+
+Author:
+
+ Dan Lafferty (danl) 17-Jul-1991
+
+Environment:
+
+ User Mode -Win32
+
+Revision History:
+
+ 17-Jul-1991 danl
+ Ported from LM2.0
+
+--*/
+
+//
+// Includes
+//
+
+#include "msrv.h" // Message server declarations
+#include <winsvc.h> // SERVICE_STOP
+
+#include <netlib.h> // UNUSED macro
+#include <msgdbg.h> // MSG_LOG
+#include "msgdata.h"
+
+
+
+VOID
+MsgrCtrlHandler(
+ IN DWORD opcode
+ )
+
+/*++
+
+Routine Description:
+
+ This function receives control requests that come in from the
+ Service Controller
+
+Arguments:
+
+ opcode - This is the control code.
+
+Return Value:
+
+
+
+--*/
+
+{
+ MSG_LOG(TRACE,"Control Request Received\n",0);
+
+ switch (opcode) {
+ case SERVICE_CONTROL_STOP:
+
+ MSG_LOG(TRACE,"Control Request = STOP\n",0);
+ //
+ // Start the de-installation. This call includes the sending of
+ // the new status to the Service Controller.
+ //
+
+ //
+ // Update the Service Status to the pending state. And wake up
+ // the display thread (if running) so it will read it.
+ //
+ MsgStatusUpdate (STOPPING);
+ MsgDisplayThreadWakeup();
+ SetEvent( wakeupSem[0] );
+ break;
+
+ case SERVICE_CONTROL_INTERROGATE:
+ MSG_LOG(TRACE,"Control Request = INTERROGATE\n",0);
+
+ default:
+ MSG_LOG(TRACE,"Control Request = INTERROGATE or OTHER\n",0);
+ MsgStatusUpdate (UPDATE_ONLY);
+ }
+
+ return;
+}
+
diff --git a/private/net/svcdlls/msgsvc/server/smbcheck.c b/private/net/svcdlls/msgsvc/server/smbcheck.c
new file mode 100644
index 000000000..10a9462f2
--- /dev/null
+++ b/private/net/svcdlls/msgsvc/server/smbcheck.c
@@ -0,0 +1,220 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ smbcheck.c
+
+Abstract:
+
+ Contains a routine for checking Server Message Block for
+ syntactical correctness.
+
+Author:
+
+ Dan Lafferty (danl) 17-Jul-1991
+
+Environment:
+
+ User Mode -Win32
+
+Notes:
+
+ These files assume that the buffers and strings are NOT Unicode - just
+ straight ansi.
+
+Revision History:
+
+ 17-Jul-1991 danl
+ ported from LM2.0
+
+--*/
+
+
+#include <windows.h>
+#include <lmcons.h> // network constants and stuff
+#include <smbtypes.h> // needed for smb.h
+#include <smb.h> // Server Message Block definitions
+#include <string.h> // strlen
+#include <nb30.h> // Needed in msrv.h
+
+/*
+** Msgsmbcheck - check Server Message Block for syntactical correctness
+**
+** This function is called to verify that a Server Message Block
+** is of the specified form. The function returns zero if the
+** SMB is correct; if an error is detected, a non-zero value
+** indicating the nature of the error is returned.
+**
+** smbcheck (buffer, size, func, parms, fields)
+**
+** ENTRY
+** buffer - a pointer to the buffer containing the SMB
+** size - the number of bytes in the buffer
+** func - the expected SMB function code
+** parms - the expected number of parameters
+** fields - a dope vector describing the expected buffer fields
+** within the SMB's buffer area (see below).
+**
+** RETURN
+** an integer status code; zero indicates no errors.
+**
+** An SMB is a variable length structure whose exact size
+** depends on the setting of certain fixed-offset fields
+** and whose exact format cannot be determined except by
+** examination of the whole structure. Smbcheck checks to
+** see that an SMB conforms to a set of specified conditions.
+** The "fields" parameter is a dope vector that describes the
+** individual fields to be found in the buffer section at the
+** end of the SMB. The vector is a null-terminated character
+** string. Currently, the elements of the string must be as
+** follows:
+**
+** 'b' - the next element in the buffer area should be
+** a variable length buffer prefixed with a byte
+** containing either 1 or 5 followed by two bytes
+** containing the size of the buffer.
+** 'd' - the next element in the buffer area is a null-terminated
+** string prefixed with a byte containing 2.
+** 'p' - the next element in the buffer area is a null-terminated
+** string prefixed with a byte containing 3.
+** 's' - the next element in the buffer area is a null-terminated
+** string prefixed with a byte containing 4.
+**
+** SIDE EFFECTS
+**
+** none
+**/
+
+int
+Msgsmbcheck(
+ LPBYTE buffer, // Buffer containing SMB
+ USHORT size, // size of SMB buffer (in bytes)
+ UCHAR func, // Function code
+ int parms, // Parameter count
+ LPSTR fields // Buffer fields dope vector
+ )
+
+{
+ PSMB_HEADER smb; // SMB header pointer
+ LPBYTE limit; // Upper limit
+
+
+ smb = (PSMB_HEADER) buffer; // Overlay header with buffer
+
+ //
+ // Must be long enough for header
+ //
+ if(size < sizeof(SMB_HEADER)) {
+ return(2);
+ }
+
+ //
+ // Message type must be 0xFF
+ //
+ if(smb->Protocol[0] != 0xff) {
+ return(3);
+ }
+
+ //
+ // Server must be "SMB"
+ //
+ if( smb->Protocol[1] != 'S' ||
+ smb->Protocol[2] != 'M' ||
+ smb->Protocol[3] != 'B') {
+ return(4);
+ }
+
+ //
+ // Must have proper function code
+ //
+ if(smb->Command != func) {
+ return(5);
+ }
+
+ limit = &buffer[size]; // Set upper limit of SMB
+
+ buffer += sizeof(SMB_HEADER); // Skip over header
+
+ //
+ // Parameter counts must match
+ //
+ if(*buffer++ != (BYTE)parms) {
+ return(6);
+ }
+
+ //
+ // Skip parameters and buffer size
+ //
+ buffer += (((SHORT)parms & 0xFF) + 1)*sizeof(SHORT);
+
+ //
+ // Check for overflow
+ //
+ if(buffer > limit) {
+ return(7);
+ }
+
+ //
+ // Loop to check buffer fields
+ //
+ while(*fields) {
+
+ //
+ // Switch on dope vector character
+ //
+ switch(*fields++) {
+
+ case 'b': // Variable length data block
+
+ if(*buffer != '\001' && *buffer != '\005') {
+ return(8);
+ }
+
+ //
+ // Check for block code
+ //
+ ++buffer; // Skip over block code
+ size = (USHORT)*buffer++ & (USHORT)0xFF; // Get low-byte size
+ size += ((USHORT)*buffer++ & (USHORT)0xFF)<< 8; // Get high-byte of buffer size
+ buffer += size; // Increment pointer
+
+ break;
+
+ case 'd': // Null-terminated dialect string
+
+ if(*buffer++ != '\002') { // Check for string code
+ return(9);
+ }
+ buffer += strlen(buffer) + 1; // Skip over the string
+ break;
+
+ case 'p': // Null-terminated path string
+
+ if(*buffer++ != '\003') { // Check for string code
+ return(10);
+ }
+ buffer += strlen(buffer) + 1; // Skip over the string
+ break;
+
+ case 's': // Null-terminated string
+
+ if(*buffer++ != '\004') { // Check for string code
+ return(11);
+ }
+ buffer += strlen(buffer) + 1; // Skip over the string
+ break;
+ }
+
+ //
+ // Check against end of block
+ //
+
+ if(buffer > limit) {
+ return(12);
+ }
+ }
+ return(buffer != limit); // Should be false
+}
+
diff --git a/private/net/svcdlls/msgsvc/server/sources b/private/net/svcdlls/msgsvc/server/sources
new file mode 100644
index 000000000..161a74d57
--- /dev/null
+++ b/private/net/svcdlls/msgsvc/server/sources
@@ -0,0 +1,84 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+
+Author:
+
+ Dan Lafferty (danl) 02-Nov-1993
+
+
+Revision History:
+
+!ENDIF
+
+#
+# The TARGETNAME variable is defined by the developer. It is the name of
+# the target (component) that is being built by this makefile. It
+# should NOT include any path or file extension information.
+#
+
+MAJORCOMP =net
+MINORCOMP =msgsvc
+
+TARGETNAME=msgsvc
+TARGETPATH=obj
+TARGETTYPE=DYNLINK
+TARGETLIBS= \
+ $(BASEDIR)\public\sdk\lib\*\kernel32.lib \
+ $(BASEDIR)\public\sdk\lib\*\advapi32.lib \
+ $(BASEDIR)\public\sdk\lib\*\user32.lib \
+ $(BASEDIR)\public\sdk\lib\*\netlib.lib \
+ $(BASEDIR)\public\sdk\lib\*\netapi32.lib \
+ $(BASEDIR)\public\sdk\lib\*\rpcrt4.lib \
+ $(BASEDIR)\public\sdk\lib\*\rpcndr.lib \
+ $(BASEDIR)\public\sdk\lib\*\rpcutil.lib
+
+INCLUDES=.;..;..\..\..\inc;..\..\..\..\inc;..\..\..\api;..\..\..\..\windows\inc
+
+!IFNDEF DISABLE_NET_UNICODE
+UNICODE=1
+NET_C_DEFINES=-DUNICODE
+!ENDIF
+
+SOURCES= \
+ apiutil.c \
+ data.c \
+ display.c \
+ fmtncbna.c \
+ grpmsngr.c \
+ heap.c \
+ msgapi.c \
+ meslog.c \
+ mesprint.c \
+ msginit.c \
+ msgmain.c \
+ msgnbios.c \
+ msgsec.c \
+ msgsvc.rc \
+ msgsvc_s.c \
+ servencb.c \
+ servenam.c \
+ sighandl.c \
+ smbcheck.c \
+ threads.c \
+ wakupsem.c
+
+
+C_DEFINES=-DRPC_NO_WINDOWS_H
+
+USE_CRTDLL=1
+
+UMTYPE=windows
+
diff --git a/private/net/svcdlls/msgsvc/server/threads.c b/private/net/svcdlls/msgsvc/server/threads.c
new file mode 100644
index 000000000..6478c5a3c
--- /dev/null
+++ b/private/net/svcdlls/msgsvc/server/threads.c
@@ -0,0 +1,544 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ threads.c
+
+Abstract:
+
+ This file contains routines that manage _access to a database of
+ worker thread handles and a database containing the current messenger
+ status (used to report status to the Service Controller). Access to
+ these two databases is controled via a Critical Section.
+
+ Functions for managing worker threads:
+
+ MsgThreadManagerInit
+ MsgThreadManagerEnd
+ MsgThreadCloseAll
+
+ Routines for managing _access to the status information and reporting:
+
+ MsgStatusInit
+ MsgBeginForcedShutdown
+ MsgStatusUpdate
+ GetMsgrState
+
+Author:
+
+ Dan Lafferty (danl) 17-Jul-1991
+
+Environment:
+
+ User Mode -Win32
+
+Notes:
+
+ These functions must be used carefully in order to be effective in
+ shutting the messenger threads down nicely if the shutdown happens
+ to occur during Messenger Initialization. This note explains when
+ each function is to be called.
+
+ MsgThreadManagerInit
+ This function must be called early on in the initialization process.
+ It should be called before NetRegisterCtrlDispatcher. This way,
+ it is impossible for an UNINSTALL request to be received prior to
+ initializing the Critical Section and the Messenger State.
+
+ MsgThreadManagerEnd
+ This function is called as one of the very last things that the
+ ControlHandler thread does. It deletes the Critical Section.
+
+ MsgThreadTermAll
+ This is called by the ControlHandler thread when it is time to
+ shutdown the messenger. It will terminate all threads in the
+ Thread Table so far, and set the Messenger State to STOPPING.
+
+
+Revision History:
+
+ 03-Nov-1992 Danl
+ Changed status reporting so that we only accept STOP controls if
+ the service is in the RUNNING state.
+
+ 18-Feb-1992 RitaW
+ Convert to Win32 service control APIs.
+
+ 02-Oct-1991 JohnRo
+ Work toward UNICODE.
+
+ 17-Jul-1991 danl
+ created
+
+--*/
+//
+// Includes
+//
+#include "msrv.h"
+
+#include <string.h> // strlen
+
+#include <winsvc.h> // SERVICE_STATUS
+#include <netlib.h> // UNUSED Macro
+#include "msgdbg.h" // MSG_LOG
+#include "msgdata.h"
+
+//
+// External Globals
+//
+ extern DWORD MsgrState; // (Either RUNNING or STOPPING);
+//
+// Static Data
+//
+
+ STATIC CRITICAL_SECTION ThreadCriticalSection;
+ STATIC SERVICE_STATUS MsgrStatus;
+ STATIC DWORD HintCount;
+ STATIC DWORD MsgrUninstallCode; // reason for uninstalling
+
+
+
+VOID
+MsgThreadManagerInit(VOID)
+
+/*++
+
+Routine Description:
+
+ Initializes the critical section that is used to guard access to the
+ thread and status database. Also initializes the handle table count
+ to 0.
+
+Arguments:
+
+ none
+
+Return Value:
+
+ none
+
+Note:
+
+
+--*/
+{
+ InitializeCriticalSection(&ThreadCriticalSection);
+}
+
+
+VOID
+MsgThreadManagerEnd(VOID)
+
+/*++
+
+Routine Description:
+
+ Deletes the critical section used to control access to the thread and
+ status database.
+
+Arguments:
+
+ none
+
+Return Value:
+
+ none
+
+Note:
+
+
+--*/
+{
+ DeleteCriticalSection(&ThreadCriticalSection);
+}
+
+
+
+VOID
+MsgThreadCloseAll(VOID)
+
+/*++
+
+Routine Description:
+
+ Closes all handles stored in the table of worker thread handles.
+
+Arguments:
+
+ none
+
+Return Value:
+
+ none
+
+Note:
+
+
+--*/
+{
+ EnterCriticalSection(&ThreadCriticalSection);
+ MsgrState = STOPPING;
+ LeaveCriticalSection(&ThreadCriticalSection);
+}
+
+
+
+VOID
+MsgStatusInit(VOID)
+
+/*++
+
+Routine Description:
+
+ Initializes the status database.
+
+Arguments:
+
+ none.
+
+Return Value:
+
+ none.
+
+Note:
+
+
+--*/
+{
+ EnterCriticalSection(&ThreadCriticalSection);
+
+ MsgrState=STARTING;
+
+ HintCount = 1;
+ MsgrUninstallCode = 0;
+
+ MsgrStatus.dwServiceType = SERVICE_WIN32;
+ MsgrStatus.dwCurrentState = SERVICE_START_PENDING;
+ MsgrStatus.dwControlsAccepted = 0;
+ MsgrStatus.dwCheckPoint = HintCount;
+ MsgrStatus.dwWaitHint = 20000; // 20 seconds
+
+ SET_SERVICE_EXITCODE(
+ NO_ERROR,
+ MsgrStatus.dwWin32ExitCode,
+ MsgrStatus.dwServiceSpecificExitCode
+ );
+
+
+ LeaveCriticalSection(&ThreadCriticalSection);
+ return;
+}
+
+DWORD
+MsgBeginForcedShutdown(
+ IN BOOL PendingCode,
+ IN DWORD ExitCode
+ )
+
+/*++
+
+Routine Description:
+
+ This function is called to set the appropriate status when a shutdown
+ is to occur due to an error in the Messenger. NOTE: if a shutdown is
+ based on a request from the Service Controller, MsgStatusUpdate is
+ called instead.
+
+ On a PENDING call, this routine will also wake up all messenger
+ threads so that they will also shut down.
+
+
+Arguments:
+
+ PendingCode - Indicates if the Shutdown is immediate or pending. If
+ PENDING, the shutdown will take some time, so a pending status is
+ sent to the ServiceController.
+
+ ExitCode - Indicates the reason for the shutdown.
+
+Return Value:
+
+ CurrentState - Contains the current state that the messenger is in
+ upon exit from this routine. In this case it will be STOPPED
+ if the PendingCode is PENDING, or STOPPING if the PendingCode
+ is IMMEDIATE.
+
+Note:
+
+
+--*/
+{
+ NET_API_STATUS status;
+
+ EnterCriticalSection(&ThreadCriticalSection);
+
+ //
+ // See if the messenger is already stopping for some reason.
+ // It could be that the ControlHandler thread received a control to
+ // stop the messenger just as we decided to stop ourselves.
+ //
+ if ((MsgrState != STOPPING) || (MsgrState != STOPPED)) {
+ if (PendingCode == PENDING) {
+ MsgrStatus.dwCurrentState = SERVICE_STOP_PENDING;
+ MsgrState = STOPPING;
+ }
+ else {
+ //
+ // The shutdown is to take immediate effect.
+ //
+ MsgrStatus.dwCurrentState = SERVICE_STOPPED;
+ MsgrStatus.dwControlsAccepted = 0;
+ MsgrStatus.dwCheckPoint = 0;
+ MsgrStatus.dwWaitHint = 0;
+ MsgrState = STOPPED;
+ }
+
+ MsgrUninstallCode = ExitCode;
+
+ SET_SERVICE_EXITCODE(
+ ExitCode,
+ MsgrStatus.dwWin32ExitCode,
+ MsgrStatus.dwServiceSpecificExitCode
+ );
+ }
+
+ //
+ // Send the new status to the service controller.
+ //
+ if (MsgrStatusHandle == (SERVICE_STATUS_HANDLE) NULL) {
+ MSG_LOG(ERROR,
+ "MsgBeginForcedShutdown, no handle to call SetServiceStatus\n", 0);
+
+ }
+ else if (! SetServiceStatus( MsgrStatusHandle, &MsgrStatus )) {
+
+ status = GetLastError();
+
+ if (status != NERR_Success) {
+ MSG_LOG(ERROR,
+ "MsgBeginForcedShutdown,SetServiceStatus Failed %X\n",
+ status);
+ }
+ }
+
+ status = MsgrState;
+ LeaveCriticalSection(&ThreadCriticalSection);
+ return(status);
+
+
+}
+
+
+DWORD
+MsgStatusUpdate(
+ IN DWORD NewState
+ )
+
+/*++
+
+Routine Description:
+
+ Sends a status to the Service Controller via SetServiceStatus.
+
+ The contents of the status message is controlled by this routine.
+ The caller simply passes in the desired state, and this routine does
+ the rest. For instance, if the Messenger passes in a STARTING state,
+ This routine will update the hint count that it maintains, and send
+ the appropriate information in the SetServiceStatus call.
+
+ This routine uses transitions in state to send determine which status
+ to send. For instance if the status was STARTING, and has changed
+ to RUNNING, this routine sends out an INSTALLED to the Service
+ Controller.
+
+Arguments:
+
+ NewState - Can be any of the state flags:
+ UPDATE_ONLY - Simply send out the current status
+ STARTING - The Messenger is in the process of initializing
+ RUNNING - The Messenger has finished with initialization
+ STOPPING - The Messenger is in the process of shutting down
+ STOPPED - The Messenger has completed the shutdown.
+
+Return Value:
+
+ CurrentState - This may not be the same as the NewState that was
+ passed in. It could be that the main thread is sending in a new
+ install state just after the Control Handler set the state to
+ STOPPING. In this case, the STOPPING state will be returned so as
+ to inform the main thread that a shut-down is in process.
+
+Note:
+
+
+--*/
+
+{
+ DWORD status;
+ BOOL inhibit = FALSE; // Used to inhibit sending the status
+ // to the service controller.
+
+ EnterCriticalSection(&ThreadCriticalSection);
+
+
+ if (NewState == STOPPED) {
+ if (MsgrState == STOPPED) {
+ //
+ // It was already stopped, don't send another SetServiceStatus.
+ //
+ inhibit = TRUE;
+ }
+ else {
+ //
+ // The shut down is complete, indicate that the messenger
+ // has stopped.
+ //
+ MsgrStatus.dwCurrentState = SERVICE_STOPPED;
+ MsgrStatus.dwControlsAccepted = 0;
+ MsgrStatus.dwCheckPoint = 0;
+ MsgrStatus.dwWaitHint = 0;
+
+ SET_SERVICE_EXITCODE(
+ MsgrUninstallCode,
+ MsgrStatus.dwWin32ExitCode,
+ MsgrStatus.dwServiceSpecificExitCode
+ );
+ }
+ MsgrState = NewState;
+ }
+ else {
+ //
+ // We are not being asked to change to the STOPPED state.
+ //
+ switch(MsgrState) {
+
+ case STARTING:
+ if (NewState == STOPPING) {
+
+ MsgrStatus.dwCurrentState = SERVICE_STOP_PENDING;
+ MsgrStatus.dwControlsAccepted = 0;
+ MsgrStatus.dwCheckPoint = HintCount++;
+ MsgrStatus.dwWaitHint = 20000; // 20 seconds
+ MsgrState = NewState;
+ }
+
+ else if (NewState == RUNNING) {
+
+ //
+ // The Messenger Service has completed installation.
+ //
+ MsgrStatus.dwCurrentState = SERVICE_RUNNING;
+ MsgrStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
+ MsgrStatus.dwCheckPoint = 0;
+ MsgrStatus.dwWaitHint = 0;
+
+ MsgrState = NewState;
+ }
+
+ else {
+ //
+ // The NewState must be STARTING. So update the pending
+ // count
+ //
+
+ MsgrStatus.dwCurrentState = SERVICE_START_PENDING;
+ MsgrStatus.dwControlsAccepted = 0;
+ MsgrStatus.dwCheckPoint = HintCount++;
+ MsgrStatus.dwWaitHint = 20000; // 20 seconds
+ }
+ break;
+
+ case RUNNING:
+ if (NewState == STOPPING) {
+
+ MsgrStatus.dwCurrentState = SERVICE_STOP_PENDING;
+ MsgrStatus.dwControlsAccepted = 0;
+ MsgrStatus.dwCheckPoint = HintCount++;
+ MsgrStatus.dwWaitHint = 20000; // 20 seconds
+
+ MsgrState = NewState;
+ }
+
+ break;
+
+ case STOPPING:
+ //
+ // No matter what else was passed in, force the status to
+ // indicate that a shutdown is pending.
+ //
+ MsgrStatus.dwCurrentState = SERVICE_STOP_PENDING;
+ MsgrStatus.dwControlsAccepted = 0;
+ MsgrStatus.dwCheckPoint = HintCount++;
+ MsgrStatus.dwWaitHint = 20000; // 20 seconds
+
+ break;
+
+ case STOPPED:
+ //
+ // We're already stopped. Therefore, an uninstalled status
+ // as already been sent. Do nothing.
+ //
+ inhibit = TRUE;
+ break;
+ }
+ }
+
+ if (!inhibit) {
+ if (MsgrStatusHandle == (SERVICE_STATUS_HANDLE) NULL) {
+ MSG_LOG(ERROR,
+ "MsgStatusUpdate, no handle to call SetServiceStatus\n", 0);
+
+ }
+ else if (! SetServiceStatus( MsgrStatusHandle, &MsgrStatus )) {
+
+ status = GetLastError();
+
+ if (status != NERR_Success) {
+ MSG_LOG(ERROR,
+ "MsgStatusUpdate, SetServiceStatus Failed %d\n",
+ status);
+ }
+ }
+ }
+
+ status = MsgrState;
+ LeaveCriticalSection(&ThreadCriticalSection);
+ return(status);
+}
+
+
+DWORD
+GetMsgrState (
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ Obtains the state of the Messenger Service. This state information
+ is protected as a critical section such that only one thread can
+ modify or read it at a time.
+
+Arguments:
+
+ none
+
+Return Value:
+
+ The Messenger State is returned as the return value.
+
+--*/
+{
+ DWORD status;
+
+ EnterCriticalSection(&ThreadCriticalSection);
+ status = MsgrState;
+ LeaveCriticalSection(&ThreadCriticalSection);
+
+ return(status);
+}
+
+
diff --git a/private/net/svcdlls/msgsvc/server/wakupsem.c b/private/net/svcdlls/msgsvc/server/wakupsem.c
new file mode 100644
index 000000000..35a8623d6
--- /dev/null
+++ b/private/net/svcdlls/msgsvc/server/wakupsem.c
@@ -0,0 +1,162 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ wakeupsem.c
+
+Abstract:
+
+ Contains functions for creating and deleting Events on which the
+ messenger threads will wait. The events get set if either data is
+ received, or a new name is added to the name table. These routines
+ were originally written for OS/2 semaphores.
+
+ Contains:
+ CreateWakeupSems
+ CloseWakeupSems
+
+Author:
+
+ Dan Lafferty (danl) 25-Jun-1991
+
+Environment:
+
+ User Mode - Win32
+
+Revision History:
+
+ 25-Jun-1991 danl
+ Ported from LM2.0
+
+--*/
+
+#include "msrv.h"
+#include "msgdbg.h" // MSG_LOG
+#include <netlib.h> // UNUSED macro
+#include "msgdata.h"
+
+
+BOOL
+MsgCreateWakeupSems(
+ DWORD NumNets
+ )
+
+/*++
+
+Routine Description:
+
+ This routine fills in the WakeupSem array with event handles for
+ each net. All nets share the same event handle, so when the handle
+ becomes signalled, the NCB array for each net needs to be examined.
+ The lost spot in the array is reserved for the Group Mailslot Handle.
+ This slot is identified as SD_NUMNETS().
+
+OLD
+ This routine creates the events used to wake up the Messenger worker
+ threads with notification of an event. These events are created with
+ auto-reset so that a single thread will be awoken, and the event will
+ automatically be reset (to the non-signaled state).
+
+ If the uninstall thread sets this right after a thread wakes up, it
+ will still be in the signaled state when the thread loops back and
+ waits. In this case it will wake up immediately and see it is time
+ to uninstall. If manual-reset events were used, we would risk having
+ a window where we would wake up and then manually reset the event -
+ essentially writing over the set state that the uninstall thread put
+ it in.
+ENDOLD
+
+Arguments:
+
+
+Return Value:
+
+
+Note:
+
+
+--*/
+
+{
+ DWORD i;
+ HANDLE hEvent=NULL;
+
+ //
+ // Create event
+ //
+
+ hEvent = CreateEvent(
+ NULL, // Event Attributes
+ FALSE, // ManualReset (auto-reset selected)
+ TRUE, // Initial State(signaled)
+ NULL); // Name
+
+ //
+ // NOTE that wakeupSem[NumNets] will be filled in with the Group
+ // mailslot handle.
+ //
+ for ( i = 0; i < NumNets; i++ ) // One per net + one group
+ {
+ wakeupSem[i] = hEvent;
+ }
+
+ if (hEvent == NULL) {
+ MSG_LOG(ERROR, "CreateWakeupSems:CreateEvent: FAILURE %X\n",
+ GetLastError());
+ return(FALSE);
+ }
+
+#ifdef REMOVE // UNTIL TESTED
+
+ BOOL bSuccess = TRUE;
+
+ //
+ // NOTE that wakeupSem[NumNets] will be filled in with the Group
+ // mailslot handle.
+ //
+ for ( i = 0; i < NumNets; i++ ) // One per net + one group
+ {
+ //
+ // Create events
+ //
+
+ wakeupSem[i] = CreateEvent(
+ NULL, // Event Attributes
+ FALSE, // ManualReset (auto-reset selected)
+ TRUE, // Initial State(signaled)
+ NULL); // Name
+
+ if (wakeupSem[i] == NULL) {
+ MSG_LOG(ERROR, "CreateWakeupSems:CreateEvent: FAILURE %X\n",
+ GetLastError());
+ bSuccess = FALSE;
+ }
+
+ }
+
+ return bSuccess;
+#endif //REMOVE
+ return(TRUE);
+}
+
+
+
+VOID
+MsgCloseWakeupSems()
+{
+ if ( wakeupSem[0] != NULL ) {
+ CloseHandle(wakeupSem[0]); // Net Event Handle
+ }
+
+#ifdef REMOVE // UNTIL TESTED
+ DWORD i;
+
+ for ( i = 0; i <= SD_NUMNETS() ; i++ ) {
+ if ( wakeupSem[i] != NULL ) {
+ CloseHandle(wakeupSem[i]);
+ }
+ }
+#endif //REMOVE
+}
diff --git a/private/net/svcdlls/ntlmssp/client/crc32.c b/private/net/svcdlls/ntlmssp/client/crc32.c
new file mode 100644
index 000000000..d0c1ec689
--- /dev/null
+++ b/private/net/svcdlls/ntlmssp/client/crc32.c
@@ -0,0 +1,91 @@
+/*++
+
+Copyright (c) 1994 Microsoft Corporation
+
+Module Name:
+
+ crc32.c
+
+Abstract:
+
+ CRC-32 alogorithm
+
+Author:
+
+ MikeSw
+
+Revision History:
+
+ 31-Mar-94 MikeSw Created
+
+--*/
+
+#include "crc32.h"
+
+//
+// This code comes from Dr. Dobbs Journal, May 1992
+//
+
+
+unsigned long CRCTable[256] = {
+ 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F,
+ 0xE963A535, 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988,
+ 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2,
+ 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
+ 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9,
+ 0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172,
+ 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C,
+ 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
+ 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423,
+ 0xCFBA9599, 0xB8BDA50F, 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924,
+ 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, 0x76DC4190, 0x01DB7106,
+ 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
+ 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D,
+ 0x91646C97, 0xE6635C01, 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E,
+ 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950,
+ 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
+ 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7,
+ 0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0,
+ 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, 0x5005713C, 0x270241AA,
+ 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
+ 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81,
+ 0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A,
+ 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, 0xE3630B12, 0x94643B84,
+ 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
+ 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB,
+ 0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC,
+ 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8, 0xA1D1937E,
+ 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
+ 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55,
+ 0x316E8EEF, 0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236,
+ 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, 0xC5BA3BBE, 0xB2BD0B28,
+ 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
+ 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F,
+ 0x72076785, 0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38,
+ 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242,
+ 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
+ 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69,
+ 0x616BFFD3, 0x166CCF45, 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2,
+ 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC,
+ 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
+ 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693,
+ 0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94,
+ 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D };
+
+
+void
+Crc32( unsigned long dwCrc,
+ unsigned long cbBuffer,
+ void * pvBuffer,
+ unsigned long * pNewCrc)
+{
+ unsigned char * pbBuffer = (unsigned char *) pvBuffer;
+
+ while (cbBuffer-- != 0)
+ {
+ dwCrc = (dwCrc >> 8) ^ CRCTable[(unsigned char) dwCrc ^ *pbBuffer++];
+ }
+ *pNewCrc = dwCrc;
+
+}
+
diff --git a/private/net/svcdlls/ntlmssp/client/crc32.h b/private/net/svcdlls/ntlmssp/client/crc32.h
new file mode 100644
index 000000000..07874bfcc
--- /dev/null
+++ b/private/net/svcdlls/ntlmssp/client/crc32.h
@@ -0,0 +1,37 @@
+/*++
+
+Copyright (c) 1994 Microsoft Corporation
+
+Module Name:
+
+ crc32.g
+
+Abstract:
+
+ CRC-32 alogorithm prototypes and constants
+
+Author:
+
+ MikeSw
+
+Revision History:
+
+ 31-Mar-94 MikeSw Created
+
+--*/
+
+
+
+//////////////////////////////////////////////////////////////
+//
+// Function prototypes for CRC-32
+//
+//////////////////////////////////////////////////////////////
+
+
+void
+Crc32( unsigned long crc,
+ unsigned long cbBuffer,
+ void * pvBuffer,
+ unsigned long * pNewCrc);
+
diff --git a/private/net/svcdlls/ntlmssp/client/dirs b/private/net/svcdlls/ntlmssp/client/dirs
new file mode 100644
index 000000000..903636d5b
--- /dev/null
+++ b/private/net/svcdlls/ntlmssp/client/dirs
@@ -0,0 +1,27 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ dirs.
+
+Abstract:
+
+ This file specifies the subdirectories of the current directory that
+ contain component makefiles.
+
+
+Author:
+
+ Steve Wood (stevewo) 17-Apr-1990
+
+NOTE: Commented description of this file is in \nt\bak\bin\dirs.tpl
+
+!ENDIF
+
+DIRS= \
+ dom \
+ export
+
+
diff --git a/private/net/svcdlls/ntlmssp/client/export/makefile b/private/net/svcdlls/ntlmssp/client/export/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/net/svcdlls/ntlmssp/client/export/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/net/svcdlls/ntlmssp/client/export/security.def b/private/net/svcdlls/ntlmssp/client/export/security.def
new file mode 100644
index 000000000..3ec75e06c
--- /dev/null
+++ b/private/net/svcdlls/ntlmssp/client/export/security.def
@@ -0,0 +1,41 @@
+LIBRARY SECURITY
+
+DESCRIPTION 'SECURITY'
+
+EXPORTS
+
+ InitSecurityInterfaceW
+ InitSecurityInterfaceA
+ EnumerateSecurityPackagesW
+ EnumerateSecurityPackagesA
+ AcquireCredentialsHandleW
+ AcquireCredentialsHandleA
+ FreeCredentialsHandle
+ InitializeSecurityContextW
+ InitializeSecurityContextA
+ AcceptSecurityContext
+ ImpersonateSecurityContext
+ RevertSecurityContext
+ QueryContextAttributesW
+ QueryContextAttributesA
+ DeleteSecurityContext
+ NtLmSspControl
+ MakeSignature
+ VerifySignature
+ FreeContextBuffer
+ ApplyControlToken
+ SealMessage
+ UnsealMessage
+ QuerySecurityPackageInfoA
+ QuerySecurityPackageInfoW
+ CompleteAuthToken
+ QueryCredentialsAttributesW
+ QueryCredentialsAttributesA
+ QuerySecurityContextToken
+
+ AddSecurityPackageA
+ AddSecurityPackageW
+ DeleteSecurityPackageA
+ DeleteSecurityPackageW
+
+DATA SINGLE SHARED
diff --git a/private/net/svcdlls/ntlmssp/client/export/sources b/private/net/svcdlls/ntlmssp/client/export/sources
new file mode 100644
index 000000000..e8a3f647b
--- /dev/null
+++ b/private/net/svcdlls/ntlmssp/client/export/sources
@@ -0,0 +1,9 @@
+!include ..\sources.inc
+
+C_DEFINES=$(C_DEFINES) -DEXPORT_BUILD -DEXPORT
+
+TARGETLIBS=$(TARGETLIBS) \
+ ..\..\common\export\obj\*\ntlmcomn.lib
+
+TARGETNAME=security
+
diff --git a/private/net/svcdlls/ntlmssp/client/init.c b/private/net/svcdlls/ntlmssp/client/init.c
new file mode 100644
index 000000000..13c1e7b84
--- /dev/null
+++ b/private/net/svcdlls/ntlmssp/client/init.c
@@ -0,0 +1,556 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ init.c
+
+Abstract:
+
+ NT LM Security Support Provider client side initialization.
+
+Author:
+
+ Cliff Van Dyke (CliffV) 29-Jun-1993
+
+Environment: User Mode
+
+Revision History:
+
+--*/
+
+
+#define NTLMSSPC_ALLOCATE // Allocate data from ntlmssps.h
+#define DEBUG_ALLOCATE // Allocate data from debug.h
+#include <ntlmsspc.h> // Include files common to DLL side of NtLmSsp
+#undef DEBUG_ALLOCATE
+#undef NTLMSSPC_ALLOCATE
+
+#include <ntlpcapi.h> // LPC data and routines
+#include <lmsname.h> // SERVICE_*
+#include <ntlmitf.h> // prototypes for interface functions
+
+//
+// Global Variables used by this modules
+//
+
+CRITICAL_SECTION SspDllCritSect; // Serializes access to all globals in module
+HANDLE SspDllLpcHandle; // Lpc Handle to NtLmSspService
+BOOLEAN SspDllCallLsaDirectly; // True iff we're running as Local System
+BOOLEAN SspDllCommonInitialized; // True iff we've called SspCommonInitialize
+ULONG SspCommonSecHandleValue = SEC_HANDLE_SECURITY;
+ // Placed in lower DWORD of contexts and creds
+
+
+BOOLEAN
+SspDllInit(
+ IN PVOID DllHandle,
+ IN ULONG Reason,
+ IN PCONTEXT Context OPTIONAL
+ )
+
+/*++
+
+Routine Description:
+
+ This is the Dll initialization routine for ntlmssp.dll
+
+Arguments:
+
+ Standard.
+
+Return Status:
+
+ TRUE: iff initialization succeeded
+
+--*/
+
+{
+
+ //
+ // On process attach,
+ // initialize the critical section,
+ // defer any additional initialization.
+ //
+
+ switch (Reason) {
+ case DLL_PROCESS_ATTACH:
+
+ DisableThreadLibraryCalls( DllHandle );
+ InitializeCriticalSection( &SspDllCritSect );
+ SspDllLpcHandle= NULL;
+ SspDllCallLsaDirectly = FALSE;
+ SspDllCommonInitialized = FALSE;
+
+ (void) SspInitLocalContexts();
+
+#if DBG
+ SspGlobalDbflag = SSP_CRITICAL;
+ InitializeCriticalSection( &SspGlobalLogFileCritSect );
+#endif // DBG
+
+ // Initialize the SecurityFunctionTable
+
+ RtlZeroMemory( &SspDllSecurityFunctionTableW,
+ sizeof(SspDllSecurityFunctionTableW) );
+
+ SspDllSecurityFunctionTableW.dwVersion = SECURITY_SUPPORT_PROVIDER_INTERFACE_VERSION;
+
+ SspDllSecurityFunctionTableW.EnumerateSecurityPackagesW = SspEnumerateSecurityPackagesW;
+ SspDllSecurityFunctionTableW.AcquireCredentialsHandleW = SspAcquireCredentialsHandleW;
+ SspDllSecurityFunctionTableW.FreeCredentialHandle = SspFreeCredentialsHandle;
+ SspDllSecurityFunctionTableW.InitializeSecurityContextW = SspInitializeSecurityContextW;
+ SspDllSecurityFunctionTableW.Reserved1 = NULL;
+ SspDllSecurityFunctionTableW.AcceptSecurityContext = SspAcceptSecurityContext;
+ SspDllSecurityFunctionTableW.CompleteAuthToken = SspCompleteAuthToken;
+ SspDllSecurityFunctionTableW.QueryContextAttributesW = SspQueryContextAttributesW;
+ SspDllSecurityFunctionTableW.Reserved2 = NULL;
+ SspDllSecurityFunctionTableW.DeleteSecurityContext = SspDeleteSecurityContext;
+ SspDllSecurityFunctionTableW.ApplyControlToken = SspApplyControlToken;
+ SspDllSecurityFunctionTableW.ImpersonateSecurityContext = SspImpersonateSecurityContext;
+ SspDllSecurityFunctionTableW.RevertSecurityContext = SspRevertSecurityContext;
+ SspDllSecurityFunctionTableW.MakeSignature = SspMakeSignature;
+ SspDllSecurityFunctionTableW.VerifySignature = SspVerifySignature;
+ SspDllSecurityFunctionTableW.FreeContextBuffer = NULL;
+ SspDllSecurityFunctionTableW.QuerySecurityPackageInfoW = SspQuerySecurityPackageInfoW;
+ SspDllSecurityFunctionTableW.Reserved3 = SspSealMessage;
+ SspDllSecurityFunctionTableW.Reserved4 = SspUnsealMessage;
+ SspDllSecurityFunctionTableW.QuerySecurityContextToken = SspQuerySecurityContextToken;
+
+ RtlZeroMemory( &SspDllSecurityFunctionTableA,
+ sizeof(SspDllSecurityFunctionTableA) );
+
+ SspDllSecurityFunctionTableA.dwVersion = SECURITY_SUPPORT_PROVIDER_INTERFACE_VERSION;
+
+ SspDllSecurityFunctionTableA.EnumerateSecurityPackagesA = SspEnumerateSecurityPackagesA;
+ SspDllSecurityFunctionTableA.AcquireCredentialsHandleA = SspAcquireCredentialsHandleA;
+ SspDllSecurityFunctionTableA.FreeCredentialHandle = SspFreeCredentialsHandle;
+ SspDllSecurityFunctionTableA.InitializeSecurityContextA = SspInitializeSecurityContextA;
+ SspDllSecurityFunctionTableA.Reserved1 = NULL;
+ SspDllSecurityFunctionTableA.AcceptSecurityContext = SspAcceptSecurityContext;
+ SspDllSecurityFunctionTableA.CompleteAuthToken = SspCompleteAuthToken;
+ SspDllSecurityFunctionTableA.QueryContextAttributesA = SspQueryContextAttributesA;
+ SspDllSecurityFunctionTableA.Reserved2 = NULL;
+ SspDllSecurityFunctionTableA.DeleteSecurityContext = SspDeleteSecurityContext;
+ SspDllSecurityFunctionTableA.ApplyControlToken = SspApplyControlToken;
+ SspDllSecurityFunctionTableA.ImpersonateSecurityContext = SspImpersonateSecurityContext;
+ SspDllSecurityFunctionTableA.RevertSecurityContext = SspRevertSecurityContext;
+ SspDllSecurityFunctionTableA.MakeSignature = SspMakeSignature;
+ SspDllSecurityFunctionTableA.VerifySignature = SspVerifySignature;
+ SspDllSecurityFunctionTableA.FreeContextBuffer = NULL;
+ SspDllSecurityFunctionTableA.QuerySecurityPackageInfoA = SspQuerySecurityPackageInfoA;
+ SspDllSecurityFunctionTableA.Reserved3 = SspSealMessage;
+ SspDllSecurityFunctionTableA.Reserved4 = SspUnsealMessage;
+ SspDllSecurityFunctionTableA.QuerySecurityContextToken = SspQuerySecurityContextToken;
+ break;
+
+ //
+ // Handle process detach.
+ //
+
+ case DLL_PROCESS_DETACH:
+
+
+ //
+ // Shutdown the common routines.
+ //
+
+ EnterCriticalSection( &SspDllCritSect );
+ if ( SspDllCommonInitialized ) {
+
+ SspCommonShutdown();
+ SspDllCallLsaDirectly = FALSE;
+ SspDllCommonInitialized = FALSE;
+ }
+
+
+ //
+ // Disconnect the LPC port
+ //
+
+ if ( SspDllLpcHandle != NULL ) {
+ NtClose( SspDllLpcHandle );
+ SspDllLpcHandle = NULL;
+ }
+
+ if (SspGlobalAliasAdminsSid != NULL) {
+ LocalFree(SspGlobalAliasAdminsSid);
+ }
+
+ if (SspGlobalLocalSystemSid != NULL) {
+ LocalFree(SspGlobalLocalSystemSid);
+ }
+
+ LeaveCriticalSection( &SspDllCritSect );
+
+ //
+ // Finally, Delete the critical section
+ //
+
+ DeleteCriticalSection( &SspDllCritSect );
+#if DBG
+ DeleteCriticalSection( &SspGlobalLogFileCritSect );
+#endif // DBG
+
+ break;
+
+ }
+
+ return(PackageMain(Reason));
+
+ UNREFERENCED_PARAMETER( Context );
+
+}
+
+
+
+BOOLEAN
+SspDllWaitForService(
+ ULONG Timeout
+ )
+
+/*++
+
+Routine Description:
+
+ Wait up to Timeout seconds for the NtLmSsp service to start.
+
+Arguments:
+
+ Timeout - Timeout for event (in seconds).
+
+Return Status:
+
+ True: service is started
+ False: Timeout (or service isn't started)
+
+--*/
+
+{
+ DWORD WinStatus;
+ SC_HANDLE ScManagerHandle = NULL;
+ SC_HANDLE ServiceHandle = NULL;
+ SERVICE_STATUS ServiceStatus;
+ BOOLEAN ReturnCode;
+
+ HANDLE EventHandle;
+
+ //
+ // If the NtLmSsp service is currently running,
+ // skip the rest of the tests.
+ //
+
+ EventHandle = OpenEventW( SYNCHRONIZE,
+ FALSE, // Don't inherit handle
+ NTLMSSP_RUNNING_EVENT );
+
+ if ( EventHandle != NULL ) {
+ DWORD WaitStatus;
+
+ WaitStatus = WaitForSingleObject( EventHandle, Timeout * 1000 );
+
+ (VOID) CloseHandle( EventHandle );
+
+ //
+ // If the event is signalled, the service is running.
+ // If we waited the full timeout time, the service is not running.
+ // Otherwise, proceed as though the event didn't exist.
+ //
+ switch ( WaitStatus ) {
+ case WAIT_OBJECT_0:
+ return TRUE;
+ case WAIT_TIMEOUT:
+ return FALSE;
+ }
+
+ SspPrint(( SSP_INIT, "Wait for %ws failed unexpectedly.\n",
+ NTLMSSP_RUNNING_EVENT ));
+
+ }
+
+
+ //
+ // Open a handle to the NtLmSsp service.
+ //
+
+ ScManagerHandle = OpenSCManager(
+ NULL,
+ NULL,
+ SC_MANAGER_CONNECT );
+
+ if (ScManagerHandle == NULL) {
+ SspPrint(( SSP_INIT, "SspDllWaitForService: OpenSCManager failed: "
+ "%lu\n", GetLastError()));
+ ReturnCode = FALSE;
+ goto Cleanup;
+ }
+
+ ServiceHandle = OpenServiceW(
+ ScManagerHandle,
+ SERVICE_NTLMSSP,
+ SERVICE_QUERY_STATUS | SERVICE_START );
+
+ if ( ServiceHandle == NULL ) {
+ SspPrint(( SSP_INIT, "SspDllWaitForService: OpenService failed: "
+ "%lu\n", GetLastError()));
+ ReturnCode = FALSE;
+ goto Cleanup;
+ }
+
+
+ //
+ // Loop waiting for the NtLmSsp service to start.
+ //
+
+ for (;;) {
+
+
+ //
+ // Query the status of the NtLmSsp service.
+ //
+
+ if (! QueryServiceStatus( ServiceHandle, &ServiceStatus )) {
+
+ SspPrint(( SSP_INIT, "SspDllWaitForService: QueryServiceStatus failed: "
+ "%lu\n", GetLastError() ));
+ ReturnCode = FALSE;
+ goto Cleanup;
+ }
+
+ //
+ // Return or continue waiting depending on the state of
+ // the NtLmSsp service.
+ //
+
+ switch( ServiceStatus.dwCurrentState) {
+ case SERVICE_RUNNING:
+ ReturnCode = TRUE;
+ goto Cleanup;
+
+ case SERVICE_STOPPED:
+
+ //
+ // If NtLmSsp has never been started on this boot,
+ // start it and continue waiting.
+ //
+
+ if (! StartService( ServiceHandle, 0, NULL )) {
+
+ WinStatus = GetLastError();
+
+ if ( WinStatus != ERROR_SERVICE_ALREADY_RUNNING ) {
+ SspPrint(( SSP_INIT, "SspDllWaitForService: StartService failed: "
+ "%lu\n", WinStatus ));
+ ReturnCode = FALSE;
+ goto Cleanup;
+ }
+ }
+
+
+
+ break;
+
+ //
+ // If NtLmSsp is trying to start up now,
+ // continue waiting for it to start.
+ //
+ case SERVICE_START_PENDING:
+ break;
+
+ //
+ // Any other state is bogus.
+ //
+ default:
+ SspPrint(( SSP_INIT, "SspDllWaitForService: "
+ "Invalid service state: %lu\n",
+ ServiceStatus.dwCurrentState ));
+ ReturnCode = FALSE;
+ goto Cleanup;
+
+ }
+
+ //
+ // If we've waited long enough for NtLmSsp to start,
+ // time out now.
+ //
+
+ if ( (--Timeout) == 0 ) {
+ ReturnCode = FALSE;
+ goto Cleanup;
+ }
+
+ //
+ // Wait a second for the NtLmSsp service to start.
+ //
+
+ Sleep( 1000 );
+
+ }
+
+ /* NOT REACHED */
+
+Cleanup:
+ if ( ScManagerHandle != NULL ) {
+ (VOID) CloseServiceHandle(ScManagerHandle);
+ }
+ if ( ServiceHandle != NULL ) {
+ (VOID) CloseServiceHandle(ServiceHandle);
+ }
+ return ReturnCode;
+}
+
+
+
+HANDLE
+SspDllGetLpcHandle(
+ IN BOOLEAN ForceReconnect,
+ OUT PBOOLEAN CallLsaDirectly
+ )
+
+/*++
+
+Routine Description:
+
+ Return a handle to the LPC port of the NtLmSsp service.
+
+ On initialization, waits for the NtLmSsp service to start.
+
+Arguments:
+
+ ForceReconnect -- TRUE specifies this is a reconnection to the NtLmSsp
+ service.
+
+ CallLsaDirectly -- Returns TRUE if this process is a logon process and
+ we need not indirect through the NtLmSsp service.
+
+Return Status:
+
+ Returns the LPC handle to use.
+
+ NULL means we cannot connect to the NtLmSsp service.
+
+
+--*/
+
+{
+ NTSTATUS Status;
+ UNICODE_STRING PortName;
+ SSP_REGISTER_CONNECT_INFO ConnectInfo;
+ ULONG ConnectInfoLength;
+ SECURITY_QUALITY_OF_SERVICE DynamicQos;
+ HANDLE LpcHandle;
+
+ //
+ // If we already have an LpcHandle,
+ // close it or return it depending on whether the caller is forcing
+ // a reconnect.
+ //
+
+ EnterCriticalSection( &SspDllCritSect );
+ if ( SspDllLpcHandle != NULL ) {
+
+ if ( ForceReconnect ) {
+ NtClose( SspDllLpcHandle );
+ SspDllLpcHandle = NULL;
+ } else {
+ LpcHandle = SspDllLpcHandle;
+ *CallLsaDirectly = SspDllCallLsaDirectly;
+ LeaveCriticalSection( &SspDllCritSect );
+ return LpcHandle;
+ }
+ }
+
+
+ //
+ // If the caller isn't forcing a reconnect,
+ // see if we can talk to LSA directly.
+ //
+
+ if ( !SspDllCommonInitialized ) {
+ Status = SspCommonInitialize();
+
+ if ( NT_SUCCESS(Status) ) {
+
+ //
+ // Flag the fact that we'll call LSA directly for the life of
+ // this process.
+ //
+
+ SspDllCallLsaDirectly = TRUE;
+
+ //
+ // Flag that we don't need to initialize SspCommon again.
+ //
+ SspDllCommonInitialized = TRUE;
+
+ //
+ // Drop through to create the LPC port anyway since we may
+ // need to go through the service in the case where the
+ // client and server are on the same system.
+ //
+
+ } else {
+ if ( Status != STATUS_NOT_LOGON_PROCESS ) {
+ SspPrint(( SSP_INIT, "SspCommonInitialize failed %lx\n", Status));
+ }
+ }
+ }
+
+ //
+ // Wait (up to 45 seconds) for the service to start.
+ //
+
+ if ( !SspDllWaitForService(45) ) {
+ LeaveCriticalSection( &SspDllCritSect );
+ return NULL;
+ }
+
+
+ //
+ // Set up the security quality of service parameters to use over the
+ // port. Use the most efficient (least overhead) - which is dynamic
+ // rather than static tracking.
+ //
+
+ DynamicQos.ImpersonationLevel = SecurityImpersonation;
+ DynamicQos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
+ DynamicQos.EffectiveOnly = FALSE;
+
+
+ //
+ // Connect to the NtLmSsp server
+ //
+
+ ConnectInfoLength = sizeof(SSP_REGISTER_CONNECT_INFO);
+ RtlInitUnicodeString(&PortName, NTLMSSP_LPC_PORT_NAME );
+
+ Status = NtConnectPort(
+ &SspDllLpcHandle,
+ &PortName,
+ &DynamicQos,
+ NULL,
+ NULL,
+ NULL,
+ &ConnectInfo,
+ &ConnectInfoLength );
+
+ if ( !NT_SUCCESS(Status) ) {
+ SspPrint(( SSP_INIT, "NtConnectPort function failed %lx\n", Status));
+ LeaveCriticalSection( &SspDllCritSect );
+ return NULL;
+ }
+
+ if ( !NT_SUCCESS(ConnectInfo.CompletionStatus) ) {
+ SspPrint(( SSP_INIT, "NtConnectPort connection failed %lx\n", Status));
+ LeaveCriticalSection( &SspDllCritSect );
+ return NULL;
+ }
+
+ LpcHandle = SspDllLpcHandle;
+ *CallLsaDirectly = SspDllCallLsaDirectly;
+ LeaveCriticalSection( &SspDllCritSect );
+
+ return LpcHandle;
+
+}
diff --git a/private/net/svcdlls/ntlmssp/client/ntlmsspc.h b/private/net/svcdlls/ntlmssp/client/ntlmsspc.h
new file mode 100644
index 000000000..e875825eb
--- /dev/null
+++ b/private/net/svcdlls/ntlmssp/client/ntlmsspc.h
@@ -0,0 +1,89 @@
+/*++
+
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ ntlmsspc.h
+
+Abstract:
+
+ Header file common to client side of the NT Lanman Security Support Provider
+ (NtLmSsp) Service.
+
+Author:
+
+ Cliff Van Dyke (CliffV) 01-Jul-1993
+
+Revision History:
+
+--*/
+
+#ifndef _NTLMSSPC_INCLUDED_
+#define _NTLMSSPC_INCLUDED_
+
+////////////////////////////////////////////////////////////////////////////
+//
+// Common include files needed by ALL NtLmSsp Client files
+//
+////////////////////////////////////////////////////////////////////////////
+
+#include <ntlmcomn.h> // Common defintions for DLL and SERVICE
+
+//
+// init.c will #include this file with NTLMSSPC_ALLOCATE defined.
+// That will cause each of these variables to be allocated.
+//
+#ifdef NTLMSSPC_ALLOCATE
+#define EXTERN
+#else
+#define EXTERN extern
+#endif
+
+
+
+////////////////////////////////////////////////////////////////////////
+//
+// Global Variables
+//
+////////////////////////////////////////////////////////////////////////
+
+
+//
+// Unicode version of table
+//
+
+EXTERN SecurityFunctionTableA SspDllSecurityFunctionTableA;
+
+//
+// Ansi version of table
+//
+
+EXTERN SecurityFunctionTableW SspDllSecurityFunctionTableW;
+
+
+//
+// Global SIDs used in assiging protection to impersonation tokens
+//
+
+EXTERN PSID SspGlobalAliasAdminsSid;
+EXTERN PSID SspGlobalLocalSystemSid;
+
+////////////////////////////////////////////////////////////////////////
+//
+// Procedure Forwards
+//
+////////////////////////////////////////////////////////////////////////
+
+//
+// Procedure forwards from init.c
+//
+
+HANDLE
+SspDllGetLpcHandle(
+ IN BOOLEAN ForceReconnect,
+ OUT PBOOLEAN CallLsaDirectly
+ );
+
+#endif // _NTLMSSPC_INCLUDED_
diff --git a/private/net/svcdlls/ntlmssp/client/security.rc b/private/net/svcdlls/ntlmssp/client/security.rc
new file mode 100644
index 000000000..aaad63faa
--- /dev/null
+++ b/private/net/svcdlls/ntlmssp/client/security.rc
@@ -0,0 +1,14 @@
+#include <windows.h>
+
+#include <ntverp.h>
+
+#define VER_FILETYPE VFT_DLL
+#define VER_FILESUBTYPE VFT2_UNKNOWN
+#define VER_FILEDESCRIPTION_STR "NtLm Security Support Provider Client DLL"
+#define VER_INTERNALNAME_STR "Security.DLL"
+#define VER_ORIGINALFILENAME_STR "Security.DLL"
+
+#define EXPORT_CONTROLLED
+
+#include "common.ver"
+
diff --git a/private/net/svcdlls/ntlmssp/client/sign.c b/private/net/svcdlls/ntlmssp/client/sign.c
new file mode 100644
index 000000000..ea4ea55a8
--- /dev/null
+++ b/private/net/svcdlls/ntlmssp/client/sign.c
@@ -0,0 +1,1811 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ sign.c
+
+Abstract:
+
+ API and support routines for handling local security contexts.
+
+Author:
+
+ MikeSw
+
+Revision History:
+
+ 29-Mar-1995 MikeSw Added SspCreateTokenDacl
+
+--*/
+
+
+//
+// Common include files.
+//
+
+#include <ntlmcomn.h> // Common definitions for DLL and SERVICE
+#include <ntlmsspi.h> // Data private to the common routines
+#include <ntlmsspc.h> // Include files common to DLL side of NtLmSsp
+#include <crypt.h> // Encryption constants and routine
+#include <rc4.h> // How to use RC4 routine
+#include <ntseapi.h> // token information
+#include "crc32.h" // How to use crc32
+
+typedef struct _CheaterContext {
+ struct _CheaterContext *pNext;
+ CtxtHandle hContext;
+ TimeStamp PasswordExpiry;
+ ULONG NegotiateFlags;
+ HANDLE TokenHandle;
+ ULONG Nonce;
+ LPWSTR ContextNames;
+ struct RC4_KEYSTRUCT Rc4Key;
+ UCHAR SessionKey[MSV1_0_USER_SESSION_KEY_LENGTH];
+} CheaterContext, * PCheaterContext;
+
+CRITICAL_SECTION csCheaterList;
+PCheaterContext pCheaterList;
+
+NTSTATUS
+SspGetTokenUser(
+ HANDLE Token,
+ PTOKEN_USER * pTokenUser
+ )
+/*++
+
+RoutineDescription:
+
+ Gets the TOKEN_USER from an open token
+
+Arguments:
+
+ Token - Handle to a token open for TOKEN_QUERY access
+
+Return Value:
+
+ STATUS_INSUFFICIENT_RESOURCES - not enough memory to complete the
+ function.
+
+ Errors from NtQueryInformationToken.
+
+--*/
+
+{
+ PTOKEN_USER LocalTokenUser = NULL;
+ NTSTATUS Status;
+ ULONG TokenUserSize = 0;
+
+ //
+ // Query the token user. First pass in NULL to get back the
+ // required size.
+ //
+
+ Status = NtQueryInformationToken(
+ Token,
+ TokenUser,
+ NULL,
+ 0,
+ &TokenUserSize
+ );
+
+ if (Status != STATUS_BUFFER_TOO_SMALL)
+ {
+ ASSERT(Status != STATUS_SUCCESS);
+ return(Status);
+ }
+
+ //
+ // Now allocate the required ammount of memory and try again.
+ //
+
+ LocalTokenUser = (PTOKEN_USER) LocalAlloc(0,TokenUserSize);
+ if (LocalTokenUser == NULL)
+ {
+ return(STATUS_INSUFFICIENT_RESOURCES);
+ }
+ Status = NtQueryInformationToken(
+ Token,
+ TokenUser,
+ LocalTokenUser,
+ TokenUserSize,
+ &TokenUserSize
+ );
+
+ if (NT_SUCCESS(Status))
+ {
+ *pTokenUser = LocalTokenUser;
+ }
+ else
+ {
+ LocalFree(LocalTokenUser);
+ }
+ return(Status);
+}
+
+NTSTATUS
+SspCreateTokenDacl(
+ HANDLE Token
+ )
+/*++
+
+RoutineDescription:
+
+ Creates a new DACL for the token granting the server and client
+ all access to the token.
+
+Arguments:
+
+ Token - Handle to an impersonation token open for TOKEN_QUERY and
+ WRITE_DAC
+
+Return Value:
+
+ STATUS_INSUFFICIENT_RESOURCES - insufficient memory to complete
+ the function.
+
+ Errors from NtSetSecurityObject
+
+--*/
+{
+ NTSTATUS Status;
+ PTOKEN_USER ProcessTokenUser = NULL;
+ PTOKEN_USER ThreadTokenUser = NULL;
+ HANDLE ProcessToken = NULL;
+ SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
+ ULONG AclLength;
+ PACL NewDacl = NULL;
+ SECURITY_DESCRIPTOR SecurityDescriptor;
+
+ //
+ // Build the two well known sids we need.
+ //
+
+ EnterCriticalSection(&csCheaterList);
+
+ if (SspGlobalLocalSystemSid == NULL)
+ {
+ Status = RtlAllocateAndInitializeSid(
+ &NtAuthority,
+ 1,
+ SECURITY_LOCAL_SYSTEM_RID,
+ 0,0,0,0,0,0,0,
+ &SspGlobalLocalSystemSid
+ );
+ if (!NT_SUCCESS(Status))
+ {
+ LeaveCriticalSection(&csCheaterList);
+ goto Cleanup;
+ }
+ }
+
+ if (SspGlobalAliasAdminsSid == NULL)
+ {
+
+ Status = RtlAllocateAndInitializeSid(
+ &NtAuthority,
+ 2,
+ SECURITY_BUILTIN_DOMAIN_RID,
+ DOMAIN_ALIAS_RID_ADMINS,
+ 0,0,0,0,0,0,
+ &SspGlobalAliasAdminsSid
+ );
+ if (!NT_SUCCESS(Status))
+ {
+ LeaveCriticalSection(&csCheaterList);
+ goto Cleanup;
+ }
+
+ }
+
+ LeaveCriticalSection(&csCheaterList);
+
+ //
+ // Open the process token to find out the user sid
+ //
+
+ Status = NtOpenProcessToken(
+ NtCurrentProcess(),
+ TOKEN_QUERY,
+ &ProcessToken
+ );
+ if (!NT_SUCCESS(Status))
+ {
+ goto Cleanup;
+ }
+
+ Status = SspGetTokenUser(
+ ProcessToken,
+ &ProcessTokenUser
+ );
+
+ if (!NT_SUCCESS(Status))
+ {
+ goto Cleanup;
+ }
+
+ //
+ // Now get the token user for the thread.
+ //
+ Status = SspGetTokenUser(
+ Token,
+ &ThreadTokenUser
+ );
+
+ if (!NT_SUCCESS(Status))
+ {
+ goto Cleanup;
+ }
+
+ AclLength = 4 * sizeof( ACCESS_ALLOWED_ACE ) - 4 * sizeof( ULONG ) +
+ RtlLengthSid( ProcessTokenUser->User.Sid ) +
+ RtlLengthSid( ThreadTokenUser->User.Sid ) +
+ RtlLengthSid( SspGlobalLocalSystemSid ) +
+ RtlLengthSid( SspGlobalAliasAdminsSid ) +
+ sizeof( ACL );
+
+ NewDacl = LocalAlloc(0, AclLength );
+
+ if (NewDacl == NULL) {
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto Cleanup;
+ }
+
+ Status = RtlCreateAcl( NewDacl, AclLength, ACL_REVISION2 );
+ ASSERT(NT_SUCCESS( Status ));
+
+ Status = RtlAddAccessAllowedAce (
+ NewDacl,
+ ACL_REVISION2,
+ TOKEN_ALL_ACCESS,
+ ProcessTokenUser->User.Sid
+ );
+ ASSERT( NT_SUCCESS( Status ));
+
+ Status = RtlAddAccessAllowedAce (
+ NewDacl,
+ ACL_REVISION2,
+ TOKEN_ALL_ACCESS,
+ ThreadTokenUser->User.Sid
+ );
+ ASSERT( NT_SUCCESS( Status ));
+
+ Status = RtlAddAccessAllowedAce (
+ NewDacl,
+ ACL_REVISION2,
+ TOKEN_ALL_ACCESS,
+ SspGlobalAliasAdminsSid
+ );
+ ASSERT( NT_SUCCESS( Status ));
+
+ Status = RtlAddAccessAllowedAce (
+ NewDacl,
+ ACL_REVISION2,
+ TOKEN_ALL_ACCESS,
+ SspGlobalLocalSystemSid
+ );
+ ASSERT( NT_SUCCESS( Status ));
+
+ Status = RtlCreateSecurityDescriptor (
+ &SecurityDescriptor,
+ SECURITY_DESCRIPTOR_REVISION
+ );
+ ASSERT( NT_SUCCESS( Status ));
+
+ Status = RtlSetDaclSecurityDescriptor(
+ &SecurityDescriptor,
+ TRUE,
+ NewDacl,
+ FALSE
+ );
+
+ ASSERT( NT_SUCCESS( Status ));
+
+ Status = NtSetSecurityObject(
+ Token,
+ DACL_SECURITY_INFORMATION,
+ &SecurityDescriptor
+ );
+
+ ASSERT( NT_SUCCESS( Status ));
+
+
+Cleanup:
+
+ if (ThreadTokenUser != NULL) {
+ LocalFree( ThreadTokenUser );
+ }
+
+ if (ProcessTokenUser != NULL) {
+ LocalFree( ProcessTokenUser );
+ }
+
+ if (NewDacl != NULL) {
+ LocalFree( NewDacl );
+ }
+
+ if (ProcessToken != NULL)
+ {
+ NtClose(ProcessToken);
+ }
+
+ return( Status );
+}
+
+
+
+
+VOID
+SspInitLocalContexts(VOID)
+/*++
+
+RoutineDescription:
+
+ Initializes the local context list
+
+Arguments:
+
+ none
+
+Return Value:
+
+ none
+
+--*/
+
+{
+ InitializeCriticalSection(&csCheaterList);
+ pCheaterList = NULL;
+}
+
+PCheaterContext
+SspLocateLocalContext(
+ IN PCtxtHandle phContext
+ )
+{
+ PCheaterContext pContext;
+
+ EnterCriticalSection(&csCheaterList);
+
+ pContext = pCheaterList;
+
+ while (pContext)
+ {
+ if (pContext->hContext.dwUpper == phContext->dwUpper)
+ {
+ break;
+ }
+ pContext = pContext->pNext;
+ }
+
+ LeaveCriticalSection(&csCheaterList);
+
+ return(pContext);
+}
+
+PCheaterContext
+SspAddLocalContext(
+ IN PCtxtHandle phContext,
+ IN PUCHAR pSessionKey,
+ IN ULONG NegotiateFlags,
+ IN HANDLE TokenHandle,
+ IN LPWSTR ContextNames
+ )
+
+/*++
+
+RoutineDescription:
+
+ Adds a context to the list of local contexts. If TokenHandle is
+ present, it will re-assign security to the token.
+
+Arguments:
+
+ phContext - Context handle of this context.
+ pSessionKey - Session key of this context.
+ NegotiateFlags - NegotiateFlags of this context.
+ TokenHandle - Handle to a token for this context.
+ ContextNames - Name for this context.
+
+Return Value:
+
+ Pointer to a new context, or NULL.
+
+--*/
+
+{
+ PCheaterContext pContext;
+
+
+ if (TokenHandle != NULL)
+ {
+ if (FAILED(SspCreateTokenDacl(TokenHandle)))
+ {
+ return(NULL);
+ }
+
+ }
+
+ pContext = LocalAlloc( LMEM_ZEROINIT, sizeof(CheaterContext) );
+
+ if (!pContext)
+ {
+ return(NULL);
+ }
+
+ pContext->NegotiateFlags = NegotiateFlags;
+
+#ifndef EXPORT_BUILD
+ if (NegotiateFlags & NTLMSSP_NEGOTIATE_STRONG_CRYPT) {
+
+ RtlCopyMemory( pContext->SessionKey,
+ pSessionKey,
+ MSV1_0_USER_SESSION_KEY_LENGTH);
+
+ } else
+#endif // EXPORT_BUILD
+
+ if (NegotiateFlags & NTLMSSP_NEGOTIATE_LM_KEY) {
+
+ RtlCopyMemory( pContext->SessionKey,
+ pSessionKey,
+ MSV1_0_LANMAN_SESSION_KEY_LENGTH);
+ } else {
+
+ RtlCopyMemory( pContext->SessionKey,
+ pSessionKey,
+ MSV1_0_USER_SESSION_KEY_LENGTH);
+ }
+
+ pContext->hContext = *phContext;
+ pContext->TokenHandle = TokenHandle;
+
+ pContext->ContextNames = (LPWSTR) LocalAlloc(0, (wcslen(ContextNames) + 1) * sizeof(WCHAR));
+ if (pContext->ContextNames == NULL)
+ {
+ LocalFree(pContext);
+ return(NULL);
+ }
+ wcscpy(pContext->ContextNames, ContextNames);
+
+
+ EnterCriticalSection(&csCheaterList);
+
+ ASSERT(SspLocateLocalContext(phContext) == NULL);
+
+ pContext->pNext = pCheaterList;
+ pCheaterList = pContext;
+
+ LeaveCriticalSection(&csCheaterList);
+
+ return(pContext);
+
+}
+
+
+BOOLEAN
+SspDeleteLocalContext(
+ IN PCheaterContext pContext
+ )
+
+/*++
+
+RoutineDescription:
+
+ Deletes a local context from the list of local context
+
+Arguments:
+
+ pContext - Context to delete.
+
+Return Value:
+
+ TRUE - the context was deleted
+ FALSE - the context was not on the list
+
+--*/
+
+{
+ PCheaterContext pSearch;
+ BOOLEAN bRet = TRUE;
+
+ EnterCriticalSection(&csCheaterList);
+
+ // Two cases: Either this heads the list, or it doesn't.
+
+ if (pContext == pCheaterList)
+ {
+ pCheaterList = pContext->pNext;
+ }
+ else
+ {
+ pSearch = pCheaterList;
+ while ((pSearch) && (pSearch->pNext != pContext))
+ {
+ pSearch = pSearch->pNext;
+ }
+ if (pSearch == NULL)
+ {
+ bRet = FALSE;
+ }
+ else
+ {
+ pSearch->pNext = pContext->pNext;
+ }
+
+ }
+
+ LeaveCriticalSection(&csCheaterList);
+
+ return(bRet);
+}
+
+VOID
+SspHandleLocalDelete(
+ IN PCtxtHandle phContext
+ )
+
+/*++
+
+RoutineDescription:
+
+ Handle deleting the local context for a real context
+
+Arguments:
+
+Return Value:
+
+ none
+
+--*/
+
+{
+ PCheaterContext pcContext;
+
+ pcContext = SspLocateLocalContext(phContext);
+ if (pcContext)
+ {
+ if (pcContext->TokenHandle != NULL)
+ {
+ NtClose(pcContext->TokenHandle);
+ }
+ if (SspDeleteLocalContext(pcContext)) {
+ if (pcContext->ContextNames != NULL)
+ {
+ LocalFree(pcContext->ContextNames);
+ }
+ LocalFree(pcContext);
+ }
+ else SspPrint(( SSP_CRITICAL, "Error deleting known context!\n" ));
+ }
+}
+
+SECURITY_STATUS
+SspMapContext(
+ IN PCtxtHandle phContext,
+ IN PUCHAR pSessionKey,
+ IN ULONG NegotiateFlags,
+ IN HANDLE TokenHandle,
+ IN LPWSTR ContextNames,
+ IN PTimeStamp PasswordExpiry OPTIONAL
+ )
+
+/*++
+
+RoutineDescription:
+
+ Create a local context for a real context
+
+Arguments:
+
+Return Value:
+
+--*/
+
+{
+ SECURITY_STATUS scRet = SEC_E_OK;
+ PCheaterContext pContext;
+
+
+ pContext = SspAddLocalContext(
+ phContext,
+ pSessionKey,
+ NegotiateFlags,
+ TokenHandle,
+ ContextNames );
+
+ if (pContext)
+ {
+ if (ARGUMENT_PRESENT(PasswordExpiry))
+ {
+ pContext->PasswordExpiry = *PasswordExpiry;
+ }
+ else
+ {
+ pContext->PasswordExpiry.QuadPart = 0;
+ }
+ pContext->Nonce = 0;
+#ifndef EXPORT_BUILD
+ if ((NegotiateFlags & NTLMSSP_NEGOTIATE_STRONG_CRYPT) != 0) {
+ rc4_key(&pContext->Rc4Key, MSV1_0_USER_SESSION_KEY_LENGTH, pContext->SessionKey);
+
+ } else
+#endif
+ if (NegotiateFlags & NTLMSSP_NEGOTIATE_LM_KEY)
+ {
+ UCHAR Key[MSV1_0_LANMAN_SESSION_KEY_LENGTH];
+
+ ASSERT(MSV1_0_LANMAN_SESSION_KEY_LENGTH == 8);
+
+ RtlCopyMemory(Key,pContext->SessionKey,5);
+
+ //
+ // Put a well-known salt at the end of the key to
+ // limit the changing part to 40 bits.
+ //
+
+ Key[5] = 0xe5;
+ Key[6] = 0x38;
+ Key[7] = 0xb0;
+
+ rc4_key(&pContext->Rc4Key, MSV1_0_LANMAN_SESSION_KEY_LENGTH, Key);
+ } else {
+ rc4_key(&pContext->Rc4Key, MSV1_0_USER_SESSION_KEY_LENGTH, pContext->SessionKey);
+ }
+ }
+ else scRet = SEC_E_INVALID_HANDLE;
+
+ return(scRet);
+}
+
+
+//
+// Bogus add-shift check sum
+//
+
+void
+SspGenCheckSum(
+ IN PSecBuffer pMessage,
+ OUT PNTLMSSP_MESSAGE_SIGNATURE pSig
+ )
+
+/*++
+
+RoutineDescription:
+
+ Generate a crc-32 checksum for a buffer
+
+Arguments:
+
+Return Value:
+
+--*/
+
+{
+ Crc32(pSig->CheckSum,pMessage->cbBuffer,pMessage->pvBuffer,&pSig->CheckSum);
+}
+
+
+VOID
+SspEncryptBuffer(
+ IN PCheaterContext pContext,
+ IN ULONG BufferSize,
+ IN OUT PVOID Buffer
+ )
+
+/*++
+
+RoutineDescription:
+
+ Encrypts a buffer with the RC4 key in the context. If the context
+ is for a datagram session, then the key is copied before being used
+ to encrypt the buffer.
+
+Arguments:
+
+ pContext - Context containing the key to encrypt the data
+
+ BufferSize - Length of buffer in bytes
+
+ Buffer - Buffer to encrypt.
+
+Return Value:
+
+--*/
+
+{
+ struct RC4_KEYSTRUCT TemporaryKey;
+ struct RC4_KEYSTRUCT * EncryptionKey = &pContext->Rc4Key;
+
+ if (BufferSize == 0)
+ {
+ return;
+ }
+ //
+ // For datagram we copy the key before encrypting so we don't
+ // have a changing key.
+ //
+
+ if ((pContext->NegotiateFlags & NTLMSSP_NEGOTIATE_DATAGRAM) != 0) {
+ RtlCopyMemory(
+ &TemporaryKey,
+ &pContext->Rc4Key,
+ sizeof(struct RC4_KEYSTRUCT)
+ );
+ EncryptionKey = &TemporaryKey;
+
+ }
+
+ rc4(
+ EncryptionKey,
+ BufferSize,
+ Buffer
+ );
+}
+
+SECURITY_STATUS
+SspHandleSignMessage(
+ IN OUT PCtxtHandle ContextHandle,
+ IN ULONG fQOP,
+ IN OUT PSecBufferDesc pMessage,
+ IN ULONG MessageSeqNo
+ )
+
+/*++
+
+RoutineDescription:
+
+ Handle signing a message
+
+Arguments:
+
+Return Value:
+
+--*/
+
+{
+ PCheaterContext pContext;
+ NTLMSSP_MESSAGE_SIGNATURE Sig;
+ PVOID pSig;
+ int Signature;
+ ULONG i;
+
+ UNREFERENCED_PARAMETER(fQOP);
+ UNREFERENCED_PARAMETER(MessageSeqNo);
+ pContext = SspLocateLocalContext(ContextHandle);
+
+ if (!pContext)
+ {
+ return(SEC_E_INVALID_HANDLE);
+ }
+
+
+ Signature = -1;
+ for (i = 0; i < pMessage->cBuffers; i++)
+ {
+ if ((pMessage->pBuffers[i].BufferType & 0xFF) == SECBUFFER_TOKEN)
+ {
+ Signature = i;
+ break;
+ }
+ }
+ if (Signature == -1)
+ {
+ return(SEC_E_INVALID_TOKEN);
+ }
+
+ if (pMessage->pBuffers[Signature].cbBuffer < NTLMSSP_MESSAGE_SIGNATURE_SIZE)
+ {
+ return(SEC_E_INVALID_TOKEN);
+ }
+
+ pSig = pMessage->pBuffers[Signature].pvBuffer;
+
+ //
+ // If sequence detect wasn't requested, put on an empty
+ // security token
+ //
+
+ if (!(pContext->NegotiateFlags & NTLMSSP_NEGOTIATE_SIGN))
+ {
+ RtlZeroMemory(&Sig,NTLMSSP_MESSAGE_SIGNATURE_SIZE);
+ Sig.Version = NTLMSSP_SIGN_VERSION;
+ RtlCopyMemory(
+ pSig,
+ &Sig,
+ NTLMSSP_MESSAGE_SIGNATURE_SIZE
+ );
+ return(SEC_E_OK);
+ }
+
+ //
+ // required by CRC-32 algorithm
+ //
+
+ Sig.CheckSum = 0xffffffff;
+
+ for (i = 0; i < pMessage->cBuffers ; i++ )
+ {
+ if (((pMessage->pBuffers[i].BufferType & 0xFF) == SECBUFFER_DATA) &&
+ !(pMessage->pBuffers[i].BufferType & SECBUFFER_READONLY))
+ {
+ SspGenCheckSum(&pMessage->pBuffers[i], &Sig);
+ }
+ }
+
+ //
+ // Required by CRC-32 algorithm
+ //
+
+ Sig.CheckSum ^= 0xffffffff;
+
+ //
+ // For datagram we rely on the message sequence number.
+ //
+
+
+ if ((pContext->NegotiateFlags & NTLMSSP_NEGOTIATE_DATAGRAM) == 0)
+ {
+ Sig.Nonce = pContext->Nonce++;
+ }
+ else
+ {
+ Sig.Nonce = MessageSeqNo;
+ }
+
+ Sig.Version = NTLMSSP_SIGN_VERSION;
+
+ SspEncryptBuffer(
+ pContext,
+ sizeof(NTLMSSP_MESSAGE_SIGNATURE) - sizeof(ULONG),
+ &Sig.RandomPad
+ );
+
+ pMessage->pBuffers[Signature].cbBuffer = sizeof(NTLMSSP_MESSAGE_SIGNATURE);
+
+ RtlCopyMemory(
+ pSig,
+ &Sig,
+ NTLMSSP_MESSAGE_SIGNATURE_SIZE
+ );
+
+
+ return(SEC_E_OK);
+
+
+}
+
+SECURITY_STATUS
+SspHandleVerifyMessage(
+ IN OUT PCtxtHandle ContextHandle,
+ IN OUT PSecBufferDesc pMessage,
+ IN ULONG MessageSeqNo,
+ OUT PULONG pfQOP
+ )
+
+/*++
+
+RoutineDescription:
+
+ Handle verifying a signed message
+
+Arguments:
+
+Return Value:
+
+--*/
+
+{
+ PCheaterContext pContext;
+ NTLMSSP_MESSAGE_SIGNATURE MessageSig;
+ NTLMSSP_MESSAGE_SIGNATURE Sig;
+ int Signature;
+ ULONG i;
+
+ UNREFERENCED_PARAMETER(pfQOP);
+ UNREFERENCED_PARAMETER(MessageSeqNo);
+
+ pContext = SspLocateLocalContext(ContextHandle);
+
+ if (!pContext)
+ {
+ return(SEC_E_INVALID_HANDLE);
+ }
+
+ Signature = -1;
+ for (i = 0; i < pMessage->cBuffers; i++)
+ {
+ if ((pMessage->pBuffers[i].BufferType & 0xFF) == SECBUFFER_TOKEN)
+ {
+ Signature = i;
+ break;
+ }
+ }
+ if (Signature == -1)
+ {
+ return(SEC_E_INVALID_TOKEN);
+ }
+
+ if (pMessage->pBuffers[Signature].cbBuffer < NTLMSSP_MESSAGE_SIGNATURE_SIZE)
+ {
+ return(SEC_E_INVALID_TOKEN);
+ }
+
+ RtlCopyMemory(
+ &MessageSig,
+ pMessage->pBuffers[Signature].pvBuffer,
+ NTLMSSP_MESSAGE_SIGNATURE_SIZE
+ );
+
+ //
+ // If sequence detect wasn't requested, put on an empty
+ // security token
+ //
+
+ if (!(pContext->NegotiateFlags & NTLMSSP_NEGOTIATE_SIGN))
+ {
+
+ RtlZeroMemory(&Sig,NTLMSSP_MESSAGE_SIGNATURE_SIZE);
+ Sig.Version = NTLMSSP_SIGN_VERSION;
+ if (!memcmp( &Sig, &MessageSig, NTLMSSP_MESSAGE_SIGNATURE_SIZE))
+ {
+ return(SEC_E_OK);
+ }
+ return(SEC_E_MESSAGE_ALTERED);
+ }
+
+ Sig.CheckSum = 0xffffffff;
+ for (i = 0; i < pMessage->cBuffers ; i++ )
+ {
+ if (((pMessage->pBuffers[i].BufferType & 0xFF) == SECBUFFER_DATA) &&
+ !(pMessage->pBuffers[i].BufferType & SECBUFFER_READONLY))
+ {
+ SspGenCheckSum(&pMessage->pBuffers[i], &Sig);
+ }
+ }
+
+ Sig.CheckSum ^= 0xffffffff;
+
+ //
+ // For datagram, rely on the message sequence number
+ //
+
+ if ((pContext->NegotiateFlags & NTLMSSP_NEGOTIATE_DATAGRAM) == 0)
+ {
+ Sig.Nonce = pContext->Nonce++;
+ }
+ else
+ {
+ Sig.Nonce = MessageSeqNo;
+ }
+ Sig.Version = NTLMSSP_SIGN_VERSION;
+
+ SspEncryptBuffer(
+ pContext,
+ sizeof(NTLMSSP_MESSAGE_SIGNATURE) - sizeof(ULONG),
+ &MessageSig.RandomPad
+ );
+
+
+
+ if (MessageSig.CheckSum != Sig.CheckSum)
+ {
+ return(SEC_E_MESSAGE_ALTERED);
+ }
+
+ if (MessageSig.Nonce != Sig.Nonce)
+ {
+ return(SEC_E_OUT_OF_SEQUENCE);
+ }
+
+
+ return(SEC_E_OK);
+
+}
+
+SECURITY_STATUS
+SspHandleSealMessage(
+ IN OUT PCtxtHandle ContextHandle,
+ IN ULONG fQOP,
+ IN OUT PSecBufferDesc pMessage,
+ IN ULONG MessageSeqNo
+ )
+
+/*++
+
+RoutineDescription:
+
+ Handle encrypting a message
+
+Arguments:
+
+Return Value:
+
+--*/
+
+{
+ PCheaterContext pContext;
+ NTLMSSP_MESSAGE_SIGNATURE Sig;
+ PVOID pSig;
+ int Signature;
+ ULONG i;
+
+ UNREFERENCED_PARAMETER(fQOP);
+ UNREFERENCED_PARAMETER(MessageSeqNo);
+ pContext = SspLocateLocalContext(ContextHandle);
+
+ if (!pContext)
+ {
+ return(SEC_E_INVALID_HANDLE);
+ }
+
+ Signature = -1;
+ for (i = 0; i < pMessage->cBuffers; i++)
+ {
+ if ((pMessage->pBuffers[i].BufferType & 0xFF) == SECBUFFER_TOKEN)
+ {
+ Signature = i;
+ break;
+ }
+ }
+ if (Signature == -1)
+ {
+ return(SEC_E_INVALID_TOKEN);
+ }
+
+ if (pMessage->pBuffers[Signature].cbBuffer < NTLMSSP_MESSAGE_SIGNATURE_SIZE)
+ {
+ return(SEC_E_INVALID_TOKEN);
+ }
+
+ pSig = pMessage->pBuffers[Signature].pvBuffer;
+
+ //
+ // required by CRC-32 algorithm
+ //
+
+ Sig.CheckSum = 0xffffffff;
+
+ for (i = 0; i < pMessage->cBuffers ; i++ )
+ {
+ if (((pMessage->pBuffers[i].BufferType & 0xFF) == SECBUFFER_DATA) &&
+ !(pMessage->pBuffers[i].BufferType & SECBUFFER_READONLY) &&
+ (pMessage->pBuffers[i].cbBuffer != 0))
+ {
+ SspGenCheckSum(&pMessage->pBuffers[i], &Sig);
+ SspEncryptBuffer(
+ pContext,
+ pMessage->pBuffers[i].cbBuffer,
+ pMessage->pBuffers[i].pvBuffer
+ );
+ }
+ }
+
+ //
+ // Required by CRC-32 algorithm
+ //
+
+ Sig.CheckSum ^= 0xffffffff;
+
+ //
+ // For datagram we rely on the message sequence number.
+ //
+
+ if ((pContext->NegotiateFlags & NTLMSSP_NEGOTIATE_DATAGRAM) == 0)
+ {
+ Sig.Nonce = pContext->Nonce++;
+ }
+ else
+ {
+ Sig.Nonce = MessageSeqNo;
+ }
+
+
+ Sig.Version = NTLMSSP_SIGN_VERSION;
+
+ SspEncryptBuffer(
+ pContext,
+ sizeof(NTLMSSP_MESSAGE_SIGNATURE) - sizeof(ULONG),
+ &Sig.RandomPad
+ );
+ pMessage->pBuffers[Signature].cbBuffer = sizeof(NTLMSSP_MESSAGE_SIGNATURE);
+
+ RtlCopyMemory(
+ pSig,
+ &Sig,
+ NTLMSSP_MESSAGE_SIGNATURE_SIZE
+ );
+
+ return(SEC_E_OK);
+
+
+}
+
+
+SECURITY_STATUS
+SspHandleUnsealMessage(
+ IN OUT PCtxtHandle ContextHandle,
+ IN OUT PSecBufferDesc pMessage,
+ IN ULONG MessageSeqNo,
+ OUT PULONG pfQOP
+ )
+
+/*++
+
+RoutineDescription:
+
+ Handle decrypting a message.
+
+Arguments:
+
+Return Value:
+
+--*/
+
+{
+ PCheaterContext pContext;
+ NTLMSSP_MESSAGE_SIGNATURE MessageSig;
+ NTLMSSP_MESSAGE_SIGNATURE Sig;
+ int Signature;
+ ULONG i;
+
+ UNREFERENCED_PARAMETER(pfQOP);
+ UNREFERENCED_PARAMETER(MessageSeqNo);
+
+ pContext = SspLocateLocalContext(ContextHandle);
+
+ if (!pContext)
+ {
+ return(SEC_E_INVALID_HANDLE);
+ }
+
+ Signature = -1;
+ for (i = 0; i < pMessage->cBuffers; i++)
+ {
+ if ((pMessage->pBuffers[i].BufferType & 0xFF) == SECBUFFER_TOKEN)
+ {
+ Signature = i;
+ break;
+ }
+ }
+ if (Signature == -1)
+ {
+ return(SEC_E_INVALID_TOKEN);
+ }
+
+ if (pMessage->pBuffers[Signature].cbBuffer < NTLMSSP_MESSAGE_SIGNATURE_SIZE)
+ {
+ return(SEC_E_INVALID_TOKEN);
+ }
+
+ RtlCopyMemory(
+ &MessageSig,
+ pMessage->pBuffers[Signature].pvBuffer,
+ NTLMSSP_MESSAGE_SIGNATURE_SIZE
+ );
+
+ Sig.CheckSum = 0xffffffff;
+ for (i = 0; i < pMessage->cBuffers ; i++ )
+ {
+ if (((pMessage->pBuffers[i].BufferType & 0xFF) == SECBUFFER_DATA) &&
+ !(pMessage->pBuffers[i].BufferType & SECBUFFER_READONLY) &&
+ (pMessage->pBuffers[i].cbBuffer != 0))
+ {
+ SspEncryptBuffer(
+ pContext,
+ pMessage->pBuffers[i].cbBuffer,
+ pMessage->pBuffers[i].pvBuffer
+ );
+ SspGenCheckSum(&pMessage->pBuffers[i], &Sig);
+ }
+ }
+
+ Sig.CheckSum ^= 0xffffffff;
+
+ //
+ // For datagram, rely on the message sequence number
+ //
+
+ if ((pContext->NegotiateFlags & NTLMSSP_NEGOTIATE_DATAGRAM) == 0)
+ {
+ Sig.Nonce = pContext->Nonce++;
+ }
+ else
+ {
+ Sig.Nonce = MessageSeqNo;
+ }
+
+ Sig.Version = NTLMSSP_SIGN_VERSION;
+
+ SspEncryptBuffer(
+ pContext,
+ sizeof(NTLMSSP_MESSAGE_SIGNATURE) - sizeof(ULONG),
+ &MessageSig.RandomPad
+ );
+
+
+ if (MessageSig.CheckSum != Sig.CheckSum)
+ {
+ return(SEC_E_MESSAGE_ALTERED);
+ }
+
+ if (MessageSig.Nonce != Sig.Nonce)
+ {
+ return(SEC_E_OUT_OF_SEQUENCE);
+ }
+
+ return(SEC_E_OK);
+
+}
+
+
+SECURITY_STATUS
+SspQuerySecurityContextToken(
+ IN PCtxtHandle ContextHandle,
+ OUT PHANDLE TokenHandle
+ )
+
+/*++
+
+Routine Description:
+
+ Returns a copy of a context token.
+
+Arguments:
+
+ ContextHandle - Context handle to impersonate.
+
+ TokenHandle - Receives a copy of the context token.
+
+Return Value:
+
+ STATUS_SUCCESS - Message handled
+
+ SEC_E_INVALID_HANDLE -- Context Handle is invalid
+
+--*/
+
+{
+ PCheaterContext Context;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ NTSTATUS Status;
+
+ InitializeObjectAttributes(
+ &ObjectAttributes,
+ NULL,
+ 0,
+ NULL,
+ NULL
+ );
+
+ //
+ // Make sure the context handle came from NTLMSSP
+ //
+
+ if ((ContextHandle->dwLower != SEC_HANDLE_NTLMSSPS) &&
+ (ContextHandle->dwLower != SEC_HANDLE_SECURITY))
+ {
+ return(SEC_E_INVALID_HANDLE);
+ }
+
+ Context = SspLocateLocalContext(ContextHandle);
+
+ if (Context == NULL) {
+ return(SEC_E_INVALID_HANDLE);
+ }
+
+
+ if (Context->TokenHandle == NULL) {
+ return(SEC_E_NO_IMPERSONATION);
+ }
+
+ //
+ // Impersonate the TokenHandle into the callers address space.
+ //
+
+ Status = NtDuplicateToken(
+ Context->TokenHandle,
+ 0, // copy existing access
+ &ObjectAttributes,
+ FALSE, // not effective only
+ TokenImpersonation,
+ TokenHandle
+ );
+
+
+ if (!NT_SUCCESS(Status)) {
+ return(SspNtStatusToSecStatus(Status, SEC_E_NO_IMPERSONATION));
+ }
+
+ return SEC_E_OK;
+
+}
+
+
+SECURITY_STATUS
+SsprImpersonateSecurityContext(
+ IN PCtxtHandle ContextHandle
+ )
+
+/*++
+
+Routine Description:
+
+ Impersonates a security context
+
+Arguments:
+
+ ContextHandle - Context handle to impersonate.
+
+Return Value:
+
+ STATUS_SUCCESS - Message handled
+
+ SEC_E_INVALID_HANDLE -- Context Handle is invalid
+
+--*/
+
+{
+ PCheaterContext Context;
+ NTSTATUS Status;
+
+
+ Context = SspLocateLocalContext(ContextHandle);
+
+ if (Context == NULL) {
+ return(SEC_E_INVALID_HANDLE);
+ }
+
+
+ if (Context->TokenHandle == NULL) {
+ return(SEC_E_NO_IMPERSONATION);
+ }
+
+ //
+ // Impersonate the TokenHandle into the callers address space.
+ //
+
+ Status = NtSetInformationThread(
+ NtCurrentThread(),
+ ThreadImpersonationToken,
+ (PVOID) &Context->TokenHandle,
+ (ULONG) sizeof(HANDLE));
+
+
+ if (!NT_SUCCESS(Status)) {
+ return(SspNtStatusToSecStatus(Status, SEC_E_NO_IMPERSONATION));
+ }
+
+ return SEC_E_OK;
+
+}
+
+
+
+SECURITY_STATUS
+SsprRevertSecurityContext(
+ IN PCtxtHandle ContextHandle
+ )
+
+/*++
+
+Routine Description:
+
+ Reverts a thread from a security context
+
+Arguments:
+
+ ContextHandle - Context handle to impersonate.
+
+Return Value:
+
+ STATUS_SUCCESS - Message handled
+
+ SEC_E_INVALID_HANDLE -- Context Handle is invalid
+
+--*/
+
+{
+ PCheaterContext Context;
+ NTSTATUS Status;
+ HANDLE NullTokenHandle = NULL;
+
+
+ Context = SspLocateLocalContext(ContextHandle);
+
+ if (Context == NULL) {
+ return(SEC_E_INVALID_HANDLE);
+ }
+
+
+ if (Context->TokenHandle == NULL) {
+ return(SEC_E_NO_IMPERSONATION);
+ }
+
+ //
+ // Impersonate the TokenHandle into the callers address space.
+ //
+
+ Status = NtSetInformationThread(
+ NtCurrentThread(),
+ ThreadImpersonationToken,
+ (PVOID) &NullTokenHandle,
+ (ULONG) sizeof(HANDLE));
+
+
+ if (!NT_SUCCESS(Status)) {
+ return(SspNtStatusToSecStatus(Status, SEC_E_NO_IMPERSONATION));
+ }
+
+ return SEC_E_OK;
+
+}
+
+
+
+SECURITY_STATUS
+SspGetContextNames(
+ IN PCheaterContext Context
+ )
+/*++
+
+Routine Description:
+
+ This routine obtains the names for a context by opening the token,
+ getting the User ID, and calling LookupAccountNameW on the user id.
+
+Arguments:
+
+ Context - Context to obtain names for
+
+Return Value:
+
+ STATUS_SUCCESS - Call completed successfully
+
+ SEC_E_INVALID_HANDLE -- Credential/Context Handle is invalid
+ SEC_E_UNSUPPORTED_FUNCTION -- Function code is not supported
+
+--*/
+{
+ PTOKEN_USER TokenUserInfo = NULL;
+ ULONG TokenUserSize = 0;
+ NTSTATUS Status;
+ WCHAR UserName[UNLEN+1];
+ ULONG UserNameLength = UNLEN+1;
+ WCHAR DomainName[DNLEN+1];
+ ULONG DomainNameLength = DNLEN+1;
+ SID_NAME_USE SidUse;
+ LPWSTR ContextNames = NULL;
+
+ ASSERT(Context->TokenHandle != NULL);
+
+
+ //
+ // Get the LogonId from the token.
+ //
+
+ Status = NtQueryInformationToken(
+ Context->TokenHandle,
+ TokenUser,
+ TokenUserInfo,
+ TokenUserSize,
+ &TokenUserSize );
+
+ if ( Status != STATUS_BUFFER_TOO_SMALL ) {
+ goto Cleanup;
+ }
+
+ TokenUserInfo = LocalAlloc( 0, TokenUserSize );
+
+ if ( TokenUserInfo == NULL ) {
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto Cleanup;
+ }
+
+ Status = NtQueryInformationToken(
+ Context->TokenHandle,
+ TokenUser,
+ TokenUserInfo,
+ TokenUserSize,
+ &TokenUserSize );
+
+ if ( !NT_SUCCESS(Status) ) {
+ goto Cleanup;
+ }
+
+ //
+ // Now that we have the user ID, calling LookupAccountName to translate
+ // it to a SID.
+ //
+
+ if (!LookupAccountSidW(
+ NULL, // local system
+ TokenUserInfo->User.Sid,
+ UserName,
+ &UserNameLength,
+ DomainName,
+ &DomainNameLength,
+ &SidUse
+ )) {
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto Cleanup;
+ }
+
+ //
+ // We now have the use & domain name - put them in the context.
+ //
+
+ ContextNames = LocalAlloc(0, (wcslen(UserName) + wcslen(DomainName) + 2) * sizeof(WCHAR));
+
+ if (ContextNames == NULL) {
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto Cleanup;
+ }
+
+ if (DomainName[0] != L'\0') {
+ wcscpy(ContextNames, DomainName);
+ wcscat(ContextNames, L"\\");
+
+ }
+ wcscat(ContextNames, UserName);
+ LocalFree(Context->ContextNames);
+ Context->ContextNames = ContextNames;
+ Status = STATUS_SUCCESS;
+
+Cleanup:
+ if (TokenUserInfo != NULL)
+ {
+ LocalFree(TokenUserInfo);
+ }
+ return(Status);
+
+}
+
+
+
+SECURITY_STATUS
+SspLocalQueryContextAttributes(
+ IN PCtxtHandle ContextHandle,
+ IN ULONG Attribute,
+ OUT PVOID Buffer
+ )
+/*++
+
+Routine Description:
+
+ This API allows a customer of the security services to determine
+ certain attributes of the context. These are: sizes, names, and
+ lifespan.
+
+Arguments:
+
+ ContextHandle - Handle to the context to query.
+
+ Attribute - Attribute to query.
+
+ #define SECPKG_ATTR_SIZES 0
+ #define SECPKG_ATTR_NAMES 1
+ #define SECPKG_ATTR_LIFESPAN 2
+
+ Buffer - Buffer to copy the data into. The buffer must be large enough
+ to fit the queried attribute.
+
+Return Value:
+
+ STATUS_SUCCESS - Call completed successfully
+
+ SEC_E_INVALID_HANDLE -- Credential/Context Handle is invalid
+ SEC_E_UNSUPPORTED_FUNCTION -- Function code is not supported
+
+--*/
+
+{
+ SECURITY_STATUS SecStatus = STATUS_SUCCESS;
+ PCheaterContext Context;
+
+
+ PSecPkgContext_Sizes ContextSizes;
+ PSecPkgContext_Lifespan ContextLifespan;
+ PSecPkgContext_DceInfo ContextDceInfo;
+ PSecPkgContext_Names ContextNames;
+ PSecPkgContext_PasswordExpiry PasswordExpires;
+ ULONG ContextNamesSize;
+ WCHAR Name[UNLEN+DNLEN+1];
+
+ //
+ // Initialization
+ //
+
+ SspPrint(( SSP_API, "SspQueryContextAttributes Entered\n" ));
+
+ Context = SspLocateLocalContext(ContextHandle);
+
+ if (Context == NULL) {
+ return(SEC_E_INVALID_HANDLE);
+ }
+
+ //
+ // Handle each of the various queried attributes
+ //
+
+ switch ( Attribute) {
+ case SECPKG_ATTR_SIZES:
+
+ ContextSizes = (PSecPkgContext_Sizes) Buffer;
+ ContextSizes->cbMaxToken = NTLMSP_MAX_TOKEN_SIZE;
+
+ if (Context->NegotiateFlags & (NTLMSSP_NEGOTIATE_ALWAYS_SIGN |
+ NTLMSSP_NEGOTIATE_SIGN |
+ NTLMSSP_NEGOTIATE_SEAL) ) {
+ ContextSizes->cbMaxSignature = NTLMSSP_MESSAGE_SIGNATURE_SIZE;
+ } else {
+ ContextSizes->cbMaxSignature = 0;
+ }
+
+ if (Context->NegotiateFlags & NTLMSSP_NEGOTIATE_SEAL) {
+ ContextSizes->cbBlockSize = 1;
+ ContextSizes->cbSecurityTrailer = NTLMSSP_MESSAGE_SIGNATURE_SIZE;
+ }
+ else
+ {
+ ContextSizes->cbBlockSize = 0;
+ ContextSizes->cbSecurityTrailer = 0;
+ }
+
+ break;
+
+ //
+ // No one uses the function so don't go to the overhead of maintaining
+ // the username in the context structure.
+ //
+
+ case SECPKG_ATTR_DCE_INFO:
+
+ //
+ // If the name isn't present and we have a token, get the name
+ // from the token.
+ //
+
+ if ((Context->ContextNames == NULL) ||
+ (Context->ContextNames[0] == L'\0') &&
+ (Context->TokenHandle != NULL))
+ {
+ }
+
+ ContextDceInfo = (PSecPkgContext_DceInfo) Buffer;
+
+ wcscpy(
+ (LPWSTR) ContextDceInfo->pPac,
+ Context->ContextNames
+ );
+
+ ContextDceInfo->AuthzSvc = 0;
+
+ break;
+
+ case SECPKG_ATTR_NAMES:
+
+ //
+ // If the name isn't present and we have a token, get the name
+ // from the token.
+ //
+
+ if (((Context->ContextNames == NULL) ||
+ (Context->ContextNames[0] == L'\0')) &&
+ (Context->TokenHandle != NULL)) {
+ SecStatus = SspGetContextNames(
+ Context
+ );
+ if (!NT_SUCCESS(SecStatus)) {
+ break;
+ }
+ }
+
+ ContextNames = (PSecPkgContext_Names) Buffer;
+
+ wcscpy(
+ ContextNames->sUserName,
+ Context->ContextNames
+ );
+
+ break;
+ case SECPKG_ATTR_PASSWORD_EXPIRY:
+ PasswordExpires = (PSecPkgContext_PasswordExpiry) Buffer;
+ if (Context->PasswordExpiry.QuadPart != 0)
+ {
+ PasswordExpires->tsPasswordExpires = Context->PasswordExpiry;
+ }
+ else
+ {
+ //
+ // This is the case on a client context.
+ //
+
+ SecStatus = SEC_E_UNSUPPORTED_FUNCTION;
+ }
+ break;
+ case SECPKG_ATTR_LIFESPAN:
+
+ //
+ // We don't support this
+ //
+
+ default:
+ SecStatus = SEC_E_NOT_SUPPORTED;
+ break;
+ }
+
+
+ //
+ // Free local resources
+ //
+
+
+ SspPrint(( SSP_API, "SspQueryContextAttributes returns 0x%lx\n", SecStatus ));
+ return SecStatus;
+}
+
+#ifdef notdef
+
+SECURITY_STATUS
+SspQueryPasswordExpiry(
+ IN PCtxtHandle ContextHandle,
+ OUT PTimeStamp PasswordExpiry
+ )
+/*++
+
+Routine Description:
+
+ This routine returns the expiration time of a context user's password
+
+Arguments:
+
+ ContextHandle - Handle to the context to query.
+
+ PasswordExpiry - Receives the date/time the password expires.
+
+Return Value:
+
+ STATUS_SUCCESS - Call completed successfully
+
+ SEC_E_INVALID_HANDLE -- Credential/Context Handle is invalid
+ SEC_E_UNSUPPORTED_FUNCTION -- Function code is not supported
+
+--*/
+
+{
+ SECURITY_STATUS SecStatus = STATUS_SUCCESS;
+ PCheaterContext Context;
+
+
+
+ //
+ // Initialization
+ //
+
+ SspPrint(( SSP_API, "SspQueryPasswordExpiry Entered\n" ));
+
+ Context = SspLocateLocalContext(ContextHandle);
+
+ if (Context == NULL) {
+ return(SEC_E_INVALID_HANDLE);
+ }
+
+ //
+ // If the expiration time is zero then this is a client context
+ // and we don't support it
+ //
+
+ if (Context->PasswordExpiry.QuadPart == 0)
+ {
+ return(SEC_E_UNSUPPORTED_FUNCTION);
+ }
+
+ *PasswordExpiry = Context->PasswordExpiry;
+
+ return SEC_E_OK;
+}
+
+#endif
diff --git a/private/net/svcdlls/ntlmssp/client/sources.inc b/private/net/svcdlls/ntlmssp/client/sources.inc
new file mode 100644
index 000000000..7fe9f6e69
--- /dev/null
+++ b/private/net/svcdlls/ntlmssp/client/sources.inc
@@ -0,0 +1,56 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+
+Author:
+
+ Steve Wood (stevewo) 12-Apr-1990
+
+NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl
+
+!ENDIF
+
+MAJORCOMP=net
+MINORCOMP=security
+
+#TARGETNAME=security
+TARGETPATH=obj
+TARGETPATHLIB=$(BASEDIR)\public\sdk\lib
+TARGETTYPE=DYNLINK
+TARGETLIBS= \
+ $(BASEDIR)\public\sdk\lib\*\kernel32.lib \
+ $(BASEDIR)\public\sdk\lib\*\advapi32.lib \
+ $(BASEDIR)\public\sdk\lib\*\netlib.lib \
+ $(BASEDIR)\public\sdk\lib\*\lsadll.lib \
+ $(BASEDIR)\private\lsa\crypt\engine\obj\*\rc4c.obj \
+
+LINKLIBS = \
+ ..\..\package\obj\*\secpkg.lib
+
+USE_NTDLL=1
+
+DLLENTRY=SspDllInit
+
+C_DEFINES=-DSECURITY_WIN32 -DUNICODE
+MSC_WARNING_LEVEL=/W3 /WX
+INCLUDES=..;..\..;..\..\common;..\..\..\..\inc;..\..\..\..\..\inc;..\..\..\..\..\lsa\crypt\engine
+
+SOURCES= \
+ ..\security.rc \
+ ..\init.c \
+ ..\stub.c \
+ ..\support.c \
+ ..\sign.c \
+ ..\crc32.c
diff --git a/private/net/svcdlls/ntlmssp/client/stub.c b/private/net/svcdlls/ntlmssp/client/stub.c
new file mode 100644
index 000000000..b4db4a3ff
--- /dev/null
+++ b/private/net/svcdlls/ntlmssp/client/stub.c
@@ -0,0 +1,2708 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ stub.c
+
+Abstract:
+
+ NT LM Security Support Provider client stubs.
+
+Author:
+
+ Cliff Van Dyke (CliffV) 29-Jun-1993
+
+Environment: User Mode
+
+Revision History:
+
+--*/
+
+#include <ntlmsspc.h> // Include files common to DLL side of NtLmSsp
+#include <ntlpcapi.h> // LPC data and routines
+#include <rpc.h> // PSEC_WINNT_AUTH_IDENTITY
+#include <stdlib.h> // wcstombs
+
+
+
+
+PSecurityFunctionTableA
+SspInitSecurityInterfaceA(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ RPC calls this function to get the addresses of all the other functions
+ that it might call (ANSI version).
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ A pointer to our static SecurityFunctionTable. The caller need
+ not deallocate this table.
+
+--*/
+
+{
+ return &SspDllSecurityFunctionTableA;
+}
+
+
+PSecurityFunctionTableW
+SspInitSecurityInterfaceW(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ RPC calls this function to get the addresses of all the other functions
+ that it might call (UNICODE version).
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ A pointer to our static SecurityFunctionTable. The caller need
+ not deallocate this table.
+
+--*/
+
+{
+ return &SspDllSecurityFunctionTableW;
+}
+
+
+
+SECURITY_STATUS
+SspQuerySecurityPackageInfoA(
+ IN CHAR * PackageName,
+ OUT PSecPkgInfoA *PackageInfo
+ )
+
+/*++
+
+Routine Description:
+
+ This API is intended to provide basic information about Security
+ Packages themselves. This information will include the bounds on sizes
+ of authentication information, credentials and contexts.
+
+Arguments:
+
+ PackageName - Name of the package being queried.
+
+ PackageInfo - Returns a pointer to an allocated block describing the
+ security package. The allocated block must be freed using
+ FreeContextBuffer.
+
+Return Value:
+
+ STATUS_SUCCESS -- Call completed successfully
+
+ SEC_E_PACKAGE_UNKNOWN -- Package being queried is not this package
+ SEC_E_INSUFFICIENT_MEMORY -- Not enough memory
+
+--*/
+{
+ CHAR *Where;
+
+ //
+ // Ensure the correct package name was passed in.
+ //
+
+ if ( _stricmp( PackageName, NTLMSP_NAME_A ) != 0 ) {
+ return SEC_E_PACKAGE_UNKNOWN;
+ }
+
+ //
+ // Allocate a buffer for the PackageInfo
+ //
+
+ *PackageInfo = LocalAlloc( 0, sizeof(SecPkgInfoW) +
+ sizeof(NTLMSP_NAME_A) +
+ sizeof(NTLMSP_COMMENT_A) );
+
+ if ( *PackageInfo == NULL ) {
+ return SEC_E_INSUFFICIENT_MEMORY;
+ }
+
+ //
+ // Fill in the information.
+ //
+
+ (*PackageInfo)->fCapabilities = NTLMSP_CAPABILITIES;
+
+ //
+ // Disable encryption if it is not permitted
+ //
+
+ if (!IsEncryptionPermitted()) {
+ (*PackageInfo)->fCapabilities &= ~SECPKG_FLAG_PRIVACY;
+ }
+
+ (*PackageInfo)->wVersion = NTLMSP_VERSION;
+ (*PackageInfo)->wRPCID = NTLMSP_RPCID;
+ (*PackageInfo)->cbMaxToken = NTLMSP_MAX_TOKEN_SIZE;
+
+ Where = (CHAR *)((*PackageInfo)+1);
+
+ (*PackageInfo)->Name = Where;
+ strcpy( Where, NTLMSP_NAME_A);
+ Where += strlen(Where) + 1;
+
+ (*PackageInfo)->Comment = Where;
+ strcpy( Where, NTLMSP_COMMENT_A);
+ Where += strlen(Where) + 1;
+
+ return STATUS_SUCCESS;
+}
+
+
+
+SECURITY_STATUS
+SspQuerySecurityPackageInfoW(
+ IN WCHAR * PackageName,
+ OUT PSecPkgInfoW *PackageInfo
+ )
+
+/*++
+
+Routine Description:
+
+ This API is intended to provide basic information about Security
+ Packages themselves. This information will include the bounds on sizes
+ of authentication information, credentials and contexts.
+
+Arguments:
+
+ PackageName - Name of the package being queried.
+
+ PackageInfo - Returns a pointer to an allocated block describing the
+ security package. The allocated block must be freed using
+ FreeContextBuffer.
+
+Return Value:
+
+ STATUS_SUCCESS -- Call completed successfully
+
+ SEC_E_PACKAGE_UNKNOWN -- Package being queried is not this package
+ SEC_E_INSUFFICIENT_MEMORY -- Not enough memory
+
+--*/
+{
+ WCHAR *Where;
+
+ //
+ // Ensure the correct package name was passed in.
+ //
+
+ if ( _wcsicmp( PackageName, NTLMSP_NAME ) != 0 ) {
+ return SEC_E_PACKAGE_UNKNOWN;
+ }
+
+ //
+ // Allocate a buffer for the PackageInfo
+ //
+
+ *PackageInfo = LocalAlloc( 0, sizeof(SecPkgInfoW) +
+ sizeof(NTLMSP_NAME) +
+ sizeof(NTLMSP_COMMENT) );
+
+ if ( *PackageInfo == NULL ) {
+ return SEC_E_INSUFFICIENT_MEMORY;
+ }
+
+ //
+ // Fill in the information.
+ //
+
+ (*PackageInfo)->fCapabilities = NTLMSP_CAPABILITIES;
+
+ //
+ // Disable encryption if it is not permitted
+ //
+
+ if (!IsEncryptionPermitted()) {
+ (*PackageInfo)->fCapabilities &= ~SECPKG_FLAG_PRIVACY;
+ }
+
+ (*PackageInfo)->wVersion = NTLMSP_VERSION;
+ (*PackageInfo)->wRPCID = NTLMSP_RPCID;
+ (*PackageInfo)->cbMaxToken = NTLMSP_MAX_TOKEN_SIZE;
+
+ Where = (WCHAR *)((*PackageInfo)+1);
+
+ (*PackageInfo)->Name = Where;
+ wcscpy( Where, NTLMSP_NAME);
+ Where += wcslen(Where) + 1;
+
+ (*PackageInfo)->Comment = Where;
+ wcscpy( Where, NTLMSP_COMMENT);
+ Where += wcslen(Where) + 1;
+
+ return STATUS_SUCCESS;
+}
+
+
+SECURITY_STATUS
+SspEnumerateSecurityPackagesA(
+ OUT PULONG PackageCount,
+ OUT PSecPkgInfoA *PackageInfo
+ )
+
+/*++
+
+Routine Description:
+
+ This API returns a list of Security Packages available to client (i.e.
+ those that are either loaded or can be loaded on demand). The caller
+ must free the returned buffer with FreeContextBuffer. This API returns
+ a list of all the security packages available to a service. The names
+ returned can then be used to acquire credential handles, as well as
+ determine which package in the system best satisfies the requirements
+ of the caller. It is assumed that all available packages can be
+ included in the single call.
+
+ This is really a dummy API that just returns information about this
+ security package. It is provided to ensure this security package has the
+ same interface as the multiplexer DLL does.
+
+Arguments:
+
+ PackageCount - Returns the number of packages supported.
+
+ PackageInfo - Returns an allocate array of structures
+ describing the security packages. The array must be freed
+ using FreeContextBuffer.
+
+Return Value:
+
+ STATUS_SUCCESS -- Call completed successfully
+
+ SEC_E_PACKAGE_UNKNOWN -- Package being queried is not this package
+ SEC_E_INSUFFICIENT_MEMORY -- Not enough memory
+
+--*/
+{
+ SECURITY_STATUS SecStatus;
+
+ //
+ // Get the information for this package.
+ //
+
+ SecStatus = SspQuerySecurityPackageInfoA( NTLMSP_NAME_A, PackageInfo );
+
+ if ( !NT_SUCCESS(SecStatus) ) {
+ return SecStatus;
+ }
+
+ *PackageCount = 1;
+
+ return STATUS_SUCCESS;
+
+}
+
+
+SECURITY_STATUS
+SspEnumerateSecurityPackagesW(
+ OUT PULONG PackageCount,
+ OUT PSecPkgInfoW *PackageInfo
+ )
+
+/*++
+
+Routine Description:
+
+ This API returns a list of Security Packages available to client (i.e.
+ those that are either loaded or can be loaded on demand). The caller
+ must free the returned buffer with FreeContextBuffer. This API returns
+ a list of all the security packages available to a service. The names
+ returned can then be used to acquire credential handles, as well as
+ determine which package in the system best satisfies the requirements
+ of the caller. It is assumed that all available packages can be
+ included in the single call.
+
+ This is really a dummy API that just returns information about this
+ security package. It is provided to ensure this security package has the
+ same interface as the multiplexer DLL does.
+
+Arguments:
+
+ PackageCount - Returns the number of packages supported.
+
+ PackageInfo - Returns an allocate array of structures
+ describing the security packages. The array must be freed
+ using FreeContextBuffer.
+
+Return Value:
+
+ STATUS_SUCCESS -- Call completed successfully
+
+ SEC_E_PACKAGE_UNKNOWN -- Package being queried is not this package
+ SEC_E_INSUFFICIENT_MEMORY -- Not enough memory
+
+--*/
+{
+ SECURITY_STATUS SecStatus;
+
+ //
+ // Get the information for this package.
+ //
+
+ SecStatus = SspQuerySecurityPackageInfoW( NTLMSP_NAME, PackageInfo );
+
+ if ( !NT_SUCCESS(SecStatus) ) {
+ return SecStatus;
+ }
+
+ *PackageCount = 1;
+
+ return STATUS_SUCCESS;
+
+}
+
+
+
+
+SECURITY_STATUS
+SspCallService(
+ IN HANDLE LpcHandle,
+ IN ULONG ApiNumber,
+ IN OUT PSSP_API_MESSAGE Message,
+ IN CSHORT MessageSize
+ )
+
+/*++
+
+Routine Description:
+
+ Calls the NTLMSSP service with the specified message and returns the response.
+
+Arguments:
+
+ LpcHandle - LPC handle to the NTLMSSP service
+
+ ApiNumber - API number of the API being called
+
+ Message - Message to pass to the service.
+
+ MessageSize - Size (in bytes) of the variable length portion of the message
+
+Return Value:
+
+ STATUS_SUCCESS -- Call completed successfully
+
+ SEC_E_NO_SPM -- Security Support Provider is not running
+ SEC_E_INSUFFICIENT_MEMORY -- Not enough memory
+
+--*/
+
+{
+ SECURITY_STATUS SecStatus;
+ NTSTATUS Status;
+
+ //
+ // Fill in the common fields of the LPC message.
+ //
+
+ Message->ApiNumber = ApiNumber;
+ Message->PortMessage.u1.s1.DataLength = MessageSize +
+ sizeof(SSP_API_NUMBER) +
+ sizeof(SECURITY_STATUS);
+ Message->PortMessage.u1.s1.TotalLength = Message->PortMessage.u1.s1.DataLength +
+ sizeof(PORT_MESSAGE);
+ Message->PortMessage.u2.ZeroInit = 0;
+
+
+ //
+ // Pass the message to the service and wait for a response.
+ //
+
+ Status = NtRequestWaitReplyPort( LpcHandle,
+ &Message->PortMessage,
+ &Message->PortMessage );
+
+ //
+ // If the NtLmSsp service has gone away,
+ // check to see if it has come back.
+ //
+
+ if ( Status == STATUS_PORT_DISCONNECTED ) {
+ BOOLEAN CallLsaDirectly;
+
+ LpcHandle = SspDllGetLpcHandle( TRUE, &CallLsaDirectly );
+
+ if ( LpcHandle == NULL ) {
+ SecStatus = SEC_E_NO_SPM;
+ return SecStatus;
+ }
+
+ Status = NtRequestWaitReplyPort( LpcHandle,
+ &Message->PortMessage,
+ &Message->PortMessage );
+
+ }
+
+ if ( !NT_SUCCESS(Status) ) {
+
+ SecStatus = SspNtStatusToSecStatus( Status, SEC_E_NO_SPM );
+ } else {
+ SecStatus = Message->ReturnedStatus;
+ }
+
+ return SecStatus;
+}
+
+
+SECURITY_STATUS
+SspUnicodeStringFromOemString(
+ IN LPSTR Oem, OPTIONAL
+ IN ULONG OemLength,
+ OUT LPWSTR * Unicode,
+ OUT PULONG UnicodeSize
+ )
+/*++
+
+Routine Description:
+
+ Converts an ascii null terminated string into the equivalent
+ unicode string.
+
+Arguments:
+
+ Oem - String to convert, may be NULL
+ OemLength - length, in characters, of string to convert
+ Unicode - Gets new string
+ UnicodeSize - gets size of new string in bytes
+
+Return Value:
+
+ SEC_E_INSUFFICIENT_MEMORY - out of memory
+
+--*/
+{
+ OEM_STRING OemString;
+ UNICODE_STRING UnicodeString;
+ NTSTATUS Status;
+
+ if ( Oem != NULL ) {
+ RtlInitString(
+ &OemString,
+ Oem
+ );
+ } else {
+ *Unicode = NULL;
+ *UnicodeSize = 0;
+ return(SEC_E_OK);
+ }
+
+ if (strlen(Oem) != OemLength) {
+ return(SEC_E_INVALID_TOKEN);
+ }
+
+ Status = RtlOemStringToUnicodeString(
+ &UnicodeString,
+ &OemString,
+ TRUE // allocate the string for me.
+ );
+
+ if (!NT_SUCCESS(Status)) {
+ return(SEC_E_INSUFFICIENT_MEMORY);
+ }
+
+ *Unicode = UnicodeString.Buffer;
+ *UnicodeSize = UnicodeString.Length + sizeof(WCHAR);
+ return(SEC_E_OK);
+}
+
+
+
+
+
+SECURITY_STATUS
+SspAcquireCredentialsHandleW(
+ IN WCHAR * PrincipalName,
+ IN WCHAR * PackageName,
+ IN ULONG CredentialUseFlags,
+ IN PLUID LogonId,
+ IN PVOID AuthData,
+ IN SEC_GET_KEY_FN GetKeyFunction,
+ IN PVOID GetKeyArgument,
+ OUT PCredHandle CredentialHandle,
+ OUT PTimeStamp Lifetime
+ )
+
+/*++
+
+Routine Description:
+
+ This API allows applications to acquire a handle to pre-existing
+ credentials associated with the user on whose behalf the call is made
+ i.e. under the identity this application is running. These pre-existing
+ credentials have been established through a system logon not described
+ here. Note that this is different from "login to the network" and does
+ not imply gathering of credentials.
+
+
+ This API returns a handle to the credentials of a principal (user, client)
+ as used by a specific security package. This handle can then be used
+ in subsequent calls to the Context APIs. This API will not let a
+ process obtain a handle to credentials that are not related to the
+ process; i.e. we won't allow a process to grab the credentials of
+ another user logged into the same machine. There is no way for us
+ to determine if a process is a trojan horse or not, if it is executed
+ by the user.
+
+Arguments:
+
+ PrincipalName - Name of the principal for whose credentials the handle
+ will reference. Note, if the process requesting the handle does
+ not have access to the credentials, an error will be returned.
+ A null string indicates that the process wants a handle to the
+ credentials of the user under whose security it is executing.
+
+ PackageName - Name of the package with which these credentials will
+ be used.
+
+ CredentialUseFlags - Flags indicating the way with which these
+ credentials will be used.
+
+ #define CRED_INBOUND 0x00000001
+ #define CRED_OUTBOUND 0x00000002
+ #define CRED_BOTH 0x00000003
+
+ The credentials created with CRED_INBOUND option can only be used
+ for (validating incoming calls and can not be used for making accesses.
+
+ LogonId - Pointer to NT style Logon Id which is a LUID. (Provided for
+ file system ; processes such as network redirectors.)
+
+ AuthData - If not NULL, specifies the credentials that override the
+ default values.
+
+ CredentialHandle - Returned credential handle.
+
+ Lifetime - Time that these credentials expire. The value returned in
+ this field depends on the security package.
+
+Return Value:
+
+ STATUS_SUCCESS -- Call completed successfully
+
+ SEC_E_NO_SPM -- Security Support Provider is not running
+ SEC_E_PACKAGE_UNKNOWN -- Package being queried is not this package
+ SEC_E_PRINCIPAL_UNKNOWN -- No such principal
+ SEC_E_NOT_OWNER -- caller does not own the specified credentials
+ SEC_E_INSUFFICIENT_MEMORY -- Not enough memory
+
+--*/
+
+{
+ SECURITY_STATUS SecStatus;
+ HANDLE LpcHandle = NULL;
+ LPWSTR DomainName = NULL;
+ ULONG DomainNameSize = 0;
+ LPWSTR UserName = NULL;
+ ULONG UserNameSize = 0;
+ LPWSTR Password = NULL;
+ ULONG PasswordSize = 0;
+ BOOLEAN CallLsaDirectly;
+ BOOLEAN DoUnicode = TRUE;
+
+ //
+ // Get an LPC Handle to the NtLmSsp service.
+ //
+
+ LpcHandle = SspDllGetLpcHandle( FALSE, &CallLsaDirectly );
+
+ if ( LpcHandle == NULL ) {
+ SecStatus = SEC_E_NO_SPM;
+ goto Cleanup;
+ }
+
+ //
+ // Validate the arguments
+ //
+
+ if ( _wcsicmp( PackageName, NTLMSP_NAME ) != 0 ) {
+ SecStatus = SEC_E_PACKAGE_UNKNOWN;
+ goto Cleanup;
+ }
+
+ if ( (CredentialUseFlags & SECPKG_CRED_OUTBOUND) &&
+ ARGUMENT_PRESENT(PrincipalName) && *PrincipalName != L'\0' ) {
+ SecStatus = SEC_E_UNKNOWN_CREDENTIALS;
+ goto Cleanup;
+ }
+
+ if ( ARGUMENT_PRESENT(LogonId) ) {
+ SecStatus = SEC_E_UNSUPPORTED_FUNCTION;
+ goto Cleanup;
+ }
+
+ if ( ARGUMENT_PRESENT(GetKeyFunction) ) {
+ SecStatus = SEC_E_UNSUPPORTED_FUNCTION;
+ goto Cleanup;
+ }
+
+ if ( ARGUMENT_PRESENT(GetKeyArgument) ) {
+ SecStatus = SEC_E_UNSUPPORTED_FUNCTION;
+ goto Cleanup;
+ }
+
+ //
+ // Break AuthData into it's components.
+ //
+
+ if ( AuthData != NULL ) {
+ PSEC_WINNT_AUTH_IDENTITY AuthIdentity;
+
+ AuthIdentity = (PSEC_WINNT_AUTH_IDENTITY) AuthData;
+
+ if (AuthIdentity->Flags & SEC_WINNT_AUTH_IDENTITY_UNICODE) {
+
+ if ( AuthIdentity->User != NULL ) {
+ UserName = AuthIdentity->User;
+ if ( AuthIdentity->UserLength != wcslen(AuthIdentity->User)) {
+ SecStatus = SEC_E_INVALID_TOKEN;
+ goto Cleanup;
+ }
+ UserNameSize = (AuthIdentity->UserLength + 1) * sizeof(WCHAR);
+ }
+
+ if ( AuthIdentity->Domain != NULL ) {
+ DomainName = AuthIdentity->Domain;
+ if ( AuthIdentity->DomainLength != wcslen(AuthIdentity->Domain)) {
+ SecStatus = SEC_E_INVALID_TOKEN;
+ goto Cleanup;
+ }
+ DomainNameSize = (AuthIdentity->DomainLength + 1) * sizeof(WCHAR);
+ }
+
+ if ( AuthIdentity->Password != NULL ) {
+ Password = AuthIdentity->Password;
+ if ( AuthIdentity->PasswordLength != wcslen(AuthIdentity->Password)) {
+ SecStatus = SEC_E_INVALID_TOKEN;
+ goto Cleanup;
+ }
+ PasswordSize = (AuthIdentity->PasswordLength + 1) * sizeof(WCHAR);
+ }
+
+ } else {
+
+ //
+ // OEM
+ //
+
+ if ((AuthIdentity->Flags & SEC_WINNT_AUTH_IDENTITY_ANSI) == 0) {
+ SecStatus = SEC_E_INVALID_TOKEN;
+ goto Cleanup;
+ }
+ DoUnicode = FALSE;
+
+ SecStatus = SspUnicodeStringFromOemString(
+ (LPSTR) AuthIdentity->User,
+ AuthIdentity->UserLength,
+ &UserName,
+ &UserNameSize
+ );
+
+ if (!NT_SUCCESS(SecStatus)) {
+ goto Cleanup;
+ }
+
+ SecStatus = SspUnicodeStringFromOemString(
+ (LPSTR) AuthIdentity->Domain,
+ AuthIdentity->DomainLength,
+ &DomainName,
+ &DomainNameSize
+ );
+ if (!NT_SUCCESS(SecStatus)) {
+ goto Cleanup;
+ }
+ SecStatus = SspUnicodeStringFromOemString(
+ (LPSTR) AuthIdentity->Password,
+ AuthIdentity->PasswordLength,
+ &Password,
+ &PasswordSize
+ );
+ if (!NT_SUCCESS(SecStatus)) {
+ goto Cleanup;
+ }
+
+ }
+
+ }
+
+ //
+ // If we can call the LSA directly,
+ // skip the NtLmSsp service.
+ //
+
+ if ( CallLsaDirectly ) {
+ LUID LogonId;
+ HANDLE ClientTokenHandle = NULL;
+
+ SecStatus = SspGetLogonId( &LogonId, &ClientTokenHandle );
+
+ if (NT_SUCCESS(SecStatus)) {
+
+ SecStatus = SsprAcquireCredentialHandle(
+ NULL, // No client connection
+ &ClientTokenHandle,
+ &LogonId,
+ CredentialUseFlags,
+ CredentialHandle,
+ Lifetime,
+ DomainName,
+ DomainNameSize,
+ UserName,
+ UserNameSize,
+ Password,
+ PasswordSize );
+
+ if ( SecStatus == SEC_I_CALL_NTLMSSP_SERVICE ) {
+ CallLsaDirectly = FALSE;
+ }
+ if (ClientTokenHandle != NULL) {
+ (VOID) NtClose(ClientTokenHandle);
+ }
+
+ } else {
+ goto Cleanup;
+ }
+
+
+ }
+
+ //
+ // Handle LPCing to the NtLmSsp service.
+ //
+
+ if ( !CallLsaDirectly ) {
+
+ SSP_API_MESSAGE Message;
+ PSSP_ACQUIRE_CREDENTIAL_HANDLE_ARGS Args;
+
+ //
+ // Copy the caller's arguments to the LPC message.
+ //
+
+ Args = &Message.Arguments.AcquireCredentialHandleArgs;
+ Args->CredentialUseFlags = CredentialUseFlags;
+ Args->DomainName = DomainName;
+ Args->DomainNameSize = DomainNameSize;
+ Args->UserName = UserName;
+ Args->UserNameSize = UserNameSize;
+ Args->Password = Password;
+ Args->PasswordSize = PasswordSize;
+
+
+ //
+ // Pass the message to the service and wait for a response.
+ //
+
+ SecStatus = SspCallService( LpcHandle,
+ SspLpcAcquireCredentialHandle,
+ &Message,
+ sizeof(*Args) );
+
+ if ( !NT_SUCCESS(SecStatus) ) {
+ goto Cleanup;
+ }
+
+
+ //
+ // Copy the return values to the caller.
+ //
+
+ *CredentialHandle = Args->CredentialHandle;
+ *Lifetime = Args->Lifetime;
+ }
+
+
+Cleanup:
+ if (!DoUnicode) {
+ UNICODE_STRING TempString;
+
+ TempString.Buffer = UserName;
+ RtlFreeUnicodeString(&TempString);
+
+ TempString.Buffer = DomainName;
+ RtlFreeUnicodeString(&TempString);
+
+ TempString.Buffer = Password;
+ RtlFreeUnicodeString(&TempString);
+
+ }
+ return SecStatus;
+
+}
+
+
+SECURITY_STATUS
+SspAcquireCredentialsHandleA(
+ IN CHAR * PrincipalName,
+ IN CHAR * PackageName,
+ IN ULONG CredentialUseFlags,
+ IN PLUID LogonId,
+ IN PVOID AuthData,
+ IN SEC_GET_KEY_FN GetKeyFunction,
+ IN PVOID GetKeyArgument,
+ OUT PCredHandle CredentialHandle,
+ OUT PTimeStamp Lifetime
+ )
+/*++
+
+Routine Description:
+
+ Ansi thunk to AcquireCredentialsHandleU
+
+--*/
+{
+ LPWSTR PackageNameW;
+ ULONG PackageNameLength = 0;
+ LPWSTR PrincipalNameW;
+ ULONG PrincipalNameLength = 0;
+ SECURITY_STATUS SecStatus;
+
+
+ if (PackageName != NULL) {
+ PackageNameLength = strlen(PackageName);
+ }
+
+ SecStatus = SspUnicodeStringFromOemString(
+ PackageName,
+ PackageNameLength,
+ &PackageNameW,
+ &PackageNameLength
+ );
+
+ if (!NT_SUCCESS(SecStatus)) {
+ return(SecStatus);
+ }
+
+ if (PrincipalName != NULL) {
+ PrincipalNameLength = strlen(PrincipalName);
+ }
+
+ SecStatus = SspUnicodeStringFromOemString(
+ PrincipalName,
+ PrincipalNameLength,
+ &PrincipalNameW,
+ &PrincipalNameLength
+ );
+
+ if (!NT_SUCCESS(SecStatus)) {
+ goto Cleanup;
+ }
+
+
+ if (NT_SUCCESS(SecStatus)) {
+
+ SecStatus = SspAcquireCredentialsHandleW(
+ PrincipalNameW,
+ PackageNameW,
+ CredentialUseFlags,
+ LogonId,
+ AuthData,
+ GetKeyFunction,
+ GetKeyArgument,
+ CredentialHandle,
+ Lifetime
+ );
+
+ }
+
+Cleanup:
+
+ LocalFree(PackageNameW);
+
+ if (PrincipalNameW != NULL) {
+ LocalFree(PrincipalNameW);
+ }
+
+ return(SecStatus);
+
+
+
+}
+
+
+
+SECURITY_STATUS
+SspFreeCredentialsHandle(
+ IN PCredHandle CredentialHandle
+ )
+
+/*++
+
+Routine Description:
+
+ This API is used to notify the security system that the credentials are
+ no longer needed and allows the application to free the handle acquired
+ in the call described above. When all references to this credential
+ set has been removed then the credentials may themselves be removed.
+
+Arguments:
+
+ CredentialHandle - Credential Handle obtained through
+ AcquireCredentialHandle.
+
+Return Value:
+
+
+ STATUS_SUCCESS -- Call completed successfully
+
+ SEC_E_NO_SPM -- Security Support Provider is not running
+ SEC_E_INVALID_HANDLE -- Credential Handle is invalid
+
+
+--*/
+
+{
+ SECURITY_STATUS SecStatus;
+ HANDLE LpcHandle = NULL;
+ BOOLEAN CallLsaDirectly;
+
+ //
+ // Get an LPC Handle to the NtLmSsp service.
+ //
+
+ LpcHandle = SspDllGetLpcHandle( FALSE, &CallLsaDirectly );
+
+ if ( LpcHandle == NULL ) {
+ SecStatus = SEC_E_NO_SPM;
+ goto Cleanup;
+ }
+
+
+ //
+ // If we can call the LSA directly,
+ // skip the NtLmSsp service.
+ //
+
+ if ( CallLsaDirectly ) {
+
+ SecStatus = SsprFreeCredentialHandle(
+ NULL, // No client connection
+ CredentialHandle );
+
+ if ( SecStatus == SEC_I_CALL_NTLMSSP_SERVICE ) {
+ CallLsaDirectly = FALSE;
+ }
+
+ }
+
+ //
+ // Handle LPCing to the NtLmSsp service.
+ //
+
+ if ( !CallLsaDirectly ) {
+ SSP_API_MESSAGE Message;
+ PSSP_FREE_CREDENTIAL_HANDLE_ARGS Args;
+
+ //
+ // Copy the caller's arguments to the LPC message.
+ //
+
+ Args = &Message.Arguments.FreeCredentialHandleArgs;
+ Args->CredentialHandle = *CredentialHandle;
+
+
+ //
+ // Pass the message to the service and wait for a response.
+ //
+
+ SecStatus = SspCallService( LpcHandle,
+ SspLpcFreeCredentialHandle,
+ &Message,
+ sizeof(*Args) );
+
+ if ( !NT_SUCCESS(SecStatus) ) {
+ goto Cleanup;
+ }
+
+ }
+
+
+Cleanup:
+ return SecStatus;
+
+}
+
+
+BOOLEAN
+SspGetTokenBuffer(
+ IN PSecBufferDesc TokenDescriptor OPTIONAL,
+ OUT PVOID * TokenBuffer,
+ OUT PULONG * TokenSize,
+ IN BOOLEAN ReadonlyOK
+ )
+
+/*++
+
+Routine Description:
+
+ This routine parses a Token Descriptor and pulls out the useful
+ information.
+
+Arguments:
+
+ TokenDescriptor - Descriptor of the buffer containing (or to contain) the
+ token. If not specified, TokenBuffer and TokenSize will be returned
+ as NULL.
+
+ TokenBuffer - Returns a pointer to the buffer for the token.
+
+ TokenSize - Returns a pointer to the location of the size of the buffer.
+
+ ReadonlyOK - TRUE if the token buffer may be readonly.
+
+Return Value:
+
+ TRUE - If token buffer was properly found.
+
+--*/
+
+{
+ ULONG i;
+
+ //
+ // If there is no TokenDescriptor passed in,
+ // just pass out NULL to our caller.
+ //
+
+ if ( !ARGUMENT_PRESENT( TokenDescriptor) ) {
+ *TokenBuffer = NULL;
+ *TokenSize = NULL;
+ return TRUE;
+ }
+
+ //
+ // Check the version of the descriptor.
+ //
+
+ if ( TokenDescriptor->ulVersion != SECBUFFER_VERSION ) {
+ return FALSE;
+ }
+
+ //
+ // Loop through each described buffer.
+ //
+
+ for ( i=0; i<TokenDescriptor->cBuffers ; i++ ) {
+ PSecBuffer Buffer = &TokenDescriptor->pBuffers[i];
+ if ( (Buffer->BufferType & (~SECBUFFER_READONLY)) == SECBUFFER_TOKEN ) {
+
+ //
+ // If the buffer is readonly and readonly isn't OK,
+ // reject the buffer.
+ //
+
+ if ( !ReadonlyOK && (Buffer->BufferType & SECBUFFER_READONLY) ) {
+ return FALSE;
+ }
+
+ //
+ // Return the requested information
+ //
+
+ *TokenBuffer = Buffer->pvBuffer;
+ *TokenSize = &Buffer->cbBuffer;
+ return TRUE;
+ }
+
+ }
+
+ //
+ // If we didn't have a buffeer, fine.
+ //
+
+ *TokenBuffer = NULL;
+ *TokenSize = NULL;
+ return TRUE;
+}
+
+
+
+
+SECURITY_STATUS
+SspInitializeSecurityContextW(
+ IN PCredHandle CredentialHandle,
+ IN PCtxtHandle OldContextHandle,
+ IN WCHAR * TargetName,
+ IN ULONG ContextReqFlags,
+ IN ULONG Reserved1,
+ IN ULONG TargetDataRep,
+ IN PSecBufferDesc InputToken,
+ IN ULONG Reserved2,
+ OUT PCtxtHandle NewContextHandle,
+ OUT PSecBufferDesc OutputToken,
+ OUT PULONG ContextAttributes,
+ OUT PTimeStamp ExpirationTime
+ )
+
+/*++
+
+Routine Description:
+
+ This routine initiates the outbound security context from a credential
+ handle. This results in the establishment of a security context
+ between the application and a remote peer. The routine returns a token
+ which must be passed to the remote peer which in turn submits it to the
+ local security implementation via the AcceptSecurityContext() call.
+ The token generated should be considered opaque by all callers.
+
+ This function is used by a client to initialize an outbound context.
+ For a two leg security package, the calling sequence is as follows: The
+ client calls the function with OldContextHandle set to NULL and
+ InputToken set either to NULL or to a pointer to a security package
+ specific data structure. The package returns a context handle in
+ NewContextHandle and a token in OutputToken. The handle can then be
+ used for message APIs if desired.
+
+ The OutputToken returned here is sent across to target server which
+ calls AcceptSecuirtyContext() with this token as an input argument and
+ may receive a token which is returned to the initiator so it can call
+ InitializeSecurityContext() again.
+
+ For a three leg (mutual authentication) security package, the calling
+ sequence is as follows: The client calls the function as above, but the
+ package will return SEC_I_CALLBACK_NEEDED. The client then sends the
+ output token to the server and waits for the server's reply. Upon
+ receipt of the server's response, the client calls this function again,
+ with OldContextHandle set to the handle that was returned from the
+ first call. The token received from the server is supplied in the
+ InputToken parameter. If the server has successfully responded, then
+ the package will respond with success, or it will invalidate the
+ context.
+
+ Initialization of security context may require more than one call to
+ this function depending upon the underlying authentication mechanism as
+ well as the "choices" indicated via ContextReqFlags. The
+ ContextReqFlags and ContextAttributes are bit masks representing
+ various context level functions viz. delegation, mutual
+ authentication, confidentiality, replay detection and sequence
+ detection.
+
+ When ISC_REQ_PROMPT_FOR_CREDS flag is set the security package always
+ prompts the user for credentials, irrespective of whether credentials
+ are present or not. If user indicated that the supplied credentials be
+ used then they will be stashed (overwriting existing ones if any) for
+ future use. The security packages will always prompt for credentials
+ if none existed, this optimizes for the most common case before a
+ credentials database is built. But the security packages can be
+ configured to not do that. Security packages will ensure that they
+ only prompt to the interactive user, for other logon sessions, this
+ flag is ignored.
+
+ When ISC_REQ_USE_SUPPLIED_CREDS flag is set the security package always
+ uses the credentials supplied in the InitializeSecurityContext() call
+ via InputToken parameter. If the package does not have any credentials
+ available it will prompt for them and record it as indicated above.
+
+ It is an error to set both these flags simultaneously.
+
+ If the ISC_REQ_ALLOCATE_MEMORY was specified then the caller must free
+ the memory pointed to by OutputToken by calling FreeContextBuffer().
+
+ For example, the InputToken may be the challenge from a LAN Manager or
+ NT file server. In this case, the OutputToken would be the NTLM
+ encrypted response to the challenge. The caller of this API can then
+ take the appropriate response (case-sensitive v. case-insensitive) and
+ return it to the server for an authenticated connection.
+
+
+Arguments:
+
+ CredentialHandle - Handle to the credentials to be used to
+ create the context.
+
+ OldContextHandle - Handle to the partially formed context, if this is
+ a second call (see above) or NULL if this is the first call.
+
+ TargetName - String indicating the target of the context. The name will
+ be security package specific. For example it will be a fully
+ qualified Cairo name for Kerberos package and can be UNC name or
+ domain name for the NTLM package.
+
+ ContextReqFlags - Requirements of the context, package specific.
+
+ #define ISC_REQ_DELEGATE 0x00000001
+ #define ISC_REQ_MUTUAL_AUTH 0x00000002
+ #define ISC_REQ_REPLAY_DETECT 0x00000004
+ #define ISC_REQ_SEQUENCE_DETECT 0x00000008
+ #define ISC_REQ_CONFIDENTIALITY 0x00000010
+ #define ISC_REQ_USE_SESSION_KEY 0x00000020
+ #define ISC_REQ_PROMT_FOR__CREDS 0x00000040
+ #define ISC_REQ_USE_SUPPLIED_CREDS 0x00000080
+ #define ISC_REQ_ALLOCATE_MEMORY 0x00000100
+ #define ISC_REQ_USE_DCE_STYLE 0x00000200
+
+ Reserved1 - Reserved value, MBZ.
+
+ TargetDataRep - Long indicating the data representation (byte ordering, etc)
+ on the target. The constant SECURITY_NATIVE_DREP may be supplied
+ by the transport indicating that the native format is in use.
+
+ InputToken - Pointer to the input token. In the first call this
+ token can either be NULL or may contain security package specific
+ information.
+
+ Reserved2 - Reserved value, MBZ.
+
+ NewContextHandle - New context handle. If this is a second call, this
+ can be the same as OldContextHandle.
+
+ OutputToken - Buffer to receive the output token.
+
+ ContextAttributes -Attributes of the context established.
+
+ #define ISC_RET_DELEGATE 0x00000001
+ #define ISC_RET_MUTUAL_AUTH 0x00000002
+ #define ISC_RET_REPLAY_DETECT 0x00000004
+ #define ISC_RET_SEQUENCE_DETECT 0x00000008
+ #define ISC_REP_CONFIDENTIALITY 0x00000010
+ #define ISC_REP_USE_SESSION_KEY 0x00000020
+ #define ISC_REP_USED_COLLECTED_CREDS 0x00000040
+ #define ISC_REP_USED_SUPPLIED_CREDS 0x00000080
+ #define ISC_REP_ALLOCATED_MEMORY 0x00000100
+ #define ISC_REP_USED_DCE_STYLE 0x00000200
+
+ ExpirationTime - Expiration time of the context.
+
+Return Value:
+
+ STATUS_SUCCESS - Message handled
+ SEC_I_CALLBACK_NEEDED -- Caller should call again later
+
+ SEC_E_NO_SPM -- Security Support Provider is not running
+ SEC_E_INVALID_TOKEN -- Token improperly formatted
+ SEC_E_INVALID_HANDLE -- Credential/Context Handle is invalid
+ SEC_E_BUFFER_TOO_SMALL -- Buffer for output token isn't big enough
+ SEC_E_NO_CREDENTIALS -- There are no credentials for this client
+ SEC_E_INSUFFICIENT_MEMORY -- Not enough memory
+
+--*/
+
+{
+ SECURITY_STATUS SecStatus;
+ HANDLE LpcHandle = NULL;
+ BOOLEAN CallLsaDirectly;
+ BOOLEAN FirstCallLsaDirectly = FALSE;
+
+ PVOID InputTokenBuffer;
+ PULONG InputTokenSize;
+ ULONG LocalInputTokenSize;
+
+ PVOID OutputTokenBuffer;
+ PULONG OutputTokenSize;
+ ULONG LocalOutputTokenSize;
+ LPWSTR DomainName = NULL;
+ ULONG DomainNameSize = 0;
+ LPWSTR UserName = NULL;
+ ULONG UserNameSize = 0;
+ LPWSTR Password = NULL;
+ ULONG PasswordSize = 0;
+ CtxtHandle TempContextHandle;
+ CtxtHandle OriginalContextHandle;
+ ULONG NegotiateFlags;
+ UCHAR SessionKey[MSV1_0_USER_SESSION_KEY_LENGTH];
+ WCHAR ContextNames[UNLEN+DNLEN+2];
+ LUID LogonId = {0,0};
+ HANDLE ClientTokenHandle = NULL;
+
+ ContextNames[0] = L'\0';
+
+ //
+ // Get an LPC Handle to the NtLmSsp service.
+ //
+
+ LpcHandle = SspDllGetLpcHandle( FALSE, &CallLsaDirectly );
+
+ if ( LpcHandle == NULL ) {
+ SecStatus = SEC_E_NO_SPM;
+ goto Cleanup;
+ }
+
+ //
+ // Check argument validity
+ //
+
+ if ( Reserved1 != 0 || Reserved2 != 0 ) {
+ SecStatus = STATUS_INVALID_PARAMETER;
+ goto Cleanup;
+ }
+
+#ifdef notdef // ? RPC passes 0x10 or 0 here depending on attitude
+ if ( TargetDataRep != SECURITY_NATIVE_DREP ) {
+ SecStatus = STATUS_INVALID_PARAMETER;
+ goto Cleanup;
+ }
+#else // notdef
+ UNREFERENCED_PARAMETER( TargetDataRep );
+#endif // notdef
+
+ if ( !SspGetTokenBuffer( InputToken,
+ &InputTokenBuffer,
+ &InputTokenSize,
+ TRUE ) ) {
+ SecStatus = SEC_E_INVALID_TOKEN;
+ goto Cleanup;
+ }
+
+ if ( InputTokenSize == NULL ) {
+ InputTokenSize = &LocalInputTokenSize;
+ LocalInputTokenSize = 0;
+ }
+
+ if ( !SspGetTokenBuffer( OutputToken,
+ &OutputTokenBuffer,
+ &OutputTokenSize,
+ FALSE ) ) {
+ SecStatus = SEC_E_INVALID_TOKEN;
+ goto Cleanup;
+ }
+
+
+ if (OutputTokenSize == NULL) {
+ OutputTokenSize = &LocalOutputTokenSize;
+ LocalOutputTokenSize = 0;
+ }
+
+ //
+ // Save the old context handle, in case someone changes it
+ //
+
+ if ( !ARGUMENT_PRESENT( OldContextHandle ) ) {
+ TempContextHandle.dwUpper = 0;
+ TempContextHandle.dwLower = 0;
+ } else {
+ TempContextHandle = *OldContextHandle;
+ }
+
+ OriginalContextHandle = TempContextHandle;
+
+
+
+ //
+ // If we can call the LSA directly,
+ // skip the NtLmSsp service.
+ //
+
+ if ( CallLsaDirectly ) {
+
+ //
+ // If no previous context was passed in this is the first call.
+ //
+
+ if ( !ARGUMENT_PRESENT( OldContextHandle ) ) {
+
+ if ( !ARGUMENT_PRESENT( CredentialHandle ) ) {
+ SecStatus = SEC_E_INVALID_HANDLE;
+ }
+
+ SecStatus = SsprHandleFirstCall(
+ NULL, // No client connection
+ CredentialHandle,
+ NewContextHandle,
+ ContextReqFlags,
+ *InputTokenSize,
+ InputTokenBuffer,
+ OutputTokenSize,
+ OutputTokenBuffer,
+ ContextAttributes,
+ ExpirationTime,
+ SessionKey,
+ &NegotiateFlags );
+
+ TempContextHandle = *NewContextHandle;
+ //
+ // If context was passed in, continue where we left off.
+ //
+
+ } else {
+
+ *NewContextHandle = *OldContextHandle;
+
+ SecStatus = SsprHandleChallengeMessage(
+ NULL, // No client connection
+ CredentialHandle,
+ &TempContextHandle,
+ NULL, // No client token
+ NULL, // No logon ID
+ ContextReqFlags,
+ NULL, // no domain name
+ 0,
+ NULL, // no user name
+ 0,
+ NULL, // no password
+ 0,
+ *InputTokenSize,
+ InputTokenBuffer,
+ OutputTokenSize,
+ OutputTokenBuffer,
+ ContextAttributes,
+ ExpirationTime,
+ SessionKey,
+ &NegotiateFlags,
+ ContextNames );
+
+ FirstCallLsaDirectly = TRUE;
+ }
+
+ if ( SecStatus == SEC_I_CALL_NTLMSSP_SERVICE ) {
+ CallLsaDirectly = FALSE;
+
+ //
+ // Get data needed for the new context from the old one
+ //
+
+ if ( ARGUMENT_PRESENT(OldContextHandle) ) {
+ SecStatus = SsprContextGetCredentials(
+ &OriginalContextHandle,
+ &DomainName,
+ &DomainNameSize,
+ &UserName,
+ &UserNameSize,
+ &Password,
+ &PasswordSize,
+ &ClientTokenHandle,
+ &LogonId
+ );
+ if (!NT_SUCCESS(SecStatus)) {
+ goto Cleanup;
+ }
+ }
+ }
+
+ }
+
+ //
+ // Handle LPCing to the NtLmSsp service.
+ //
+
+ if ( !CallLsaDirectly ) {
+ SSP_API_MESSAGE Message;
+ PSSP_INITIALIZE_SECURITY_CONTEXT_ARGS Args;
+
+
+ //
+ // Copy the caller's arguments to the LPC message.
+ //
+
+ Args = &Message.Arguments.InitializeSecurityContextArgs;
+
+ if ( !ARGUMENT_PRESENT( CredentialHandle ) ) {
+ Args->CredentialHandle.dwUpper = 0;
+ Args->CredentialHandle.dwLower = 0;
+ } else {
+ Args->CredentialHandle = *CredentialHandle;
+ }
+
+ Args->ContextHandle = TempContextHandle;
+
+ Args->ContextReqFlags = ContextReqFlags;
+ Args->InputTokenSize = *InputTokenSize;
+ Args->InputToken = InputTokenBuffer;
+ Args->OutputTokenSize = *OutputTokenSize;
+ Args->OutputToken = OutputTokenBuffer;
+ Args->DomainName = DomainName;
+ Args->DomainNameSize = DomainNameSize;
+ Args->UserName = UserName;
+ Args->UserNameSize = UserNameSize;
+ Args->Password = Password;
+ Args->PasswordSize = PasswordSize;
+ Args->ContextNames = ContextNames;
+ Args->LogonId = LogonId;
+ Args->ClientTokenHandle = ClientTokenHandle;
+
+
+ //
+ // Pass the message to the service and wait for a response.
+ //
+
+ SecStatus = SspCallService( LpcHandle,
+ SspLpcInitializeSecurityContext,
+ &Message,
+ sizeof(*Args) );
+
+ //
+ // This has to be returned on both success and failure
+ //
+
+ *ContextAttributes = Args->ContextAttributes;
+
+ if ( !NT_SUCCESS(SecStatus) ) {
+ goto Cleanup;
+ }
+
+
+
+ //
+ // Copy the return values to the caller.
+ //
+
+
+ TempContextHandle = Args->ContextHandle;
+ *NewContextHandle = Args->ContextHandle;
+ *OutputTokenSize = Args->OutputTokenSize;
+ *ExpirationTime = Args->ExpirationTime;
+ NegotiateFlags = Args->NegotiateFlags;
+ RtlCopyMemory(SessionKey,Args->SessionKey,MSV1_0_USER_SESSION_KEY_LENGTH);
+
+ if (ARGUMENT_PRESENT(OldContextHandle) && FirstCallLsaDirectly) {
+
+ //
+ // Update the context in the dll with the context in
+ // the service, and reset the new handle to be what it
+ // is supposed to be
+ //
+
+ SecStatus = SsprContextUpdateContext(
+ &OriginalContextHandle,
+ &Args->ContextHandle
+ );
+ *NewContextHandle = OriginalContextHandle;
+ }
+
+ }
+
+ //
+ // If the original handle is zero, set it to be the TempContextHandle.
+ // This is for the datagram case, where we map the context after the
+ // first call to initialize.
+ //
+
+ if ((OriginalContextHandle.dwLower == 0) &&
+ (OriginalContextHandle.dwUpper == 0)) {
+
+ OriginalContextHandle = TempContextHandle;
+ }
+ //
+ // Only map the context if this is the real authentication, not a re-auth
+ // or if this was datagram.
+ //
+
+ if (((SecStatus == SEC_I_CONTINUE_NEEDED) &&
+ ((*ContextAttributes & ISC_RET_DATAGRAM) != 0)) ||
+ ((SecStatus == SEC_E_OK) &&
+ ((*ContextAttributes & (SSP_RET_REAUTHENTICATION | ISC_RET_DATAGRAM)) == 0))) {
+
+ SECURITY_STATUS TempStatus;
+
+ TempStatus = SspMapContext(
+ &OriginalContextHandle,
+ SessionKey,
+ NegotiateFlags,
+ NULL, // no token handle for clients
+ ContextNames,
+ NULL // no password expiry for clients
+ );
+
+ if (!NT_SUCCESS(TempStatus)) {
+ SecStatus = TempStatus;
+ }
+ }
+
+ //
+ // Make sure this bit isn't sent to the caller
+ //
+
+ *ContextAttributes &= ~SSP_RET_REAUTHENTICATION;
+
+
+Cleanup:
+ if (DomainName != NULL) {
+ LocalFree(DomainName);
+ }
+ if (UserName != NULL) {
+ LocalFree(UserName);
+ }
+ if (Password != NULL) {
+ LocalFree(Password);
+ }
+ return SecStatus;
+
+ // Ignore TargetName. By convention, it is being passed as \\server\ipc$.
+ // This implementation makes no use of that information. Perhaps, I'll
+ // display it if I prompt for credentials.
+ //
+ UNREFERENCED_PARAMETER( TargetName );
+
+
+}
+
+
+SECURITY_STATUS
+SspInitializeSecurityContextA(
+ IN PCredHandle CredentialHandle,
+ IN PCtxtHandle OldContextHandle,
+ IN CHAR * TargetName,
+ IN ULONG ContextReqFlags,
+ IN ULONG Reserved1,
+ IN ULONG TargetDataRep,
+ IN PSecBufferDesc InputToken,
+ IN ULONG Reserved2,
+ OUT PCtxtHandle NewContextHandle,
+ OUT PSecBufferDesc OutputToken,
+ OUT PULONG ContextAttributes,
+ OUT PTimeStamp ExpirationTime
+ )
+/*++
+
+Routine Description:
+
+ Ansi thunk to InitializeSecurityContextU
+--*/
+{
+ LPWSTR TargetNameW;
+ ULONG TargetNameLength = 0;
+ SECURITY_STATUS SecStatus;
+
+ if (TargetName != NULL) {
+ TargetNameLength = strlen(TargetName);
+ }
+
+ SecStatus = SspUnicodeStringFromOemString(
+ TargetName,
+ TargetNameLength,
+ &TargetNameW,
+ &TargetNameLength
+ );
+
+ if (NT_SUCCESS(SecStatus)) {
+
+ SecStatus = SspInitializeSecurityContextW(
+ CredentialHandle,
+ OldContextHandle,
+ TargetNameW,
+ ContextReqFlags,
+ Reserved1,
+ TargetDataRep,
+ InputToken,
+ Reserved2,
+ NewContextHandle,
+ OutputToken,
+ ContextAttributes,
+ ExpirationTime
+ );
+
+ LocalFree(TargetNameW);
+ }
+
+ return(SecStatus);
+
+
+
+}
+
+
+
+SECURITY_STATUS
+SspAcceptSecurityContext(
+ IN PCredHandle CredentialHandle,
+ IN PCtxtHandle OldContextHandle,
+ IN PSecBufferDesc InputToken,
+ IN ULONG ContextReqFlags,
+ IN ULONG TargetDataRep,
+ OUT PCtxtHandle NewContextHandle,
+ IN PSecBufferDesc OutputToken,
+ OUT PULONG ContextAttributes,
+ OUT PTimeStamp ExpirationTime
+ )
+
+/*++
+
+Routine Description:
+
+ Allows a remotely initiated security context between the application
+ and a remote peer to be established. To complete the establishment of
+ context one or more reply tokens may be required from remote peer.
+
+ This function is the server counterpart to the
+ InitializeSecurityContext API. The ContextAttributes is a bit mask
+ representing various context level functions viz. delegation, mutual
+ authentication, confidentiality, replay detection and sequence
+ detection. This API is used by the server side. When a request comes
+ in, the server uses the ContextReqFlags parameter to specify what
+ it requires of the session. In this fashion, a server can specify that
+ clients must be capable of using a confidential or integrity checked
+ session, and fail clients that can't meet that demand. Alternatively,
+ a server can require nothing, and whatever the client can provide or
+ requires is returned in the pfContextAttributes parameter. For a
+ package that supports 3 leg mutual authentication, the calling sequence
+ would be: Client provides a token, server calls Accept the first time,
+ generating a reply token. The client uses this in a second call to
+ InitializeSecurityContext, and generates a final token. This token is
+ then used in the final call to Accept to complete the session. Another
+ example would be the LAN Manager/NT authentication style. The client
+ connects to negotiate a protocol. The server calls Accept to set up a
+ context and generate a challenge to the client. The client calls
+ InitializeSecurityContext and creates a response. The server then
+ calls Accept the final time to allow the package to verify the response
+ is appropriate for the challenge.
+
+Arguments:
+
+ CredentialHandle - Handle to the credentials to be used to
+ create the context.
+
+ OldContextHandle - Handle to the partially formed context, if this is
+ a second call (see above) or NULL if this is the first call.
+
+ InputToken - Pointer to the input token. In the first call this
+ token can either be NULL or may contain security package specific
+ information.
+
+ ContextReqFlags - Requirements of the context, package specific.
+
+ #define ASC_REQ_DELEGATE 0x00000001
+ #define ASC_REQ_MUTUAL_AUTH 0x00000002
+ #define ASC_REQ_REPLAY_DETECT 0x00000004
+ #define ASC_REQ_SEQUENCE_DETECT 0x00000008
+ #define ASC_REQ_CONFIDENTIALITY 0x00000010
+ #define ASC_REQ_USE_SESSION_KEY 0x00000020
+ #define ASC_REQ_ALLOCATE_MEMORY 0x00000100
+ #define ASC_REQ_USE_DCE_STYLE 0x00000200
+
+ TargetDataRep - Long indicating the data representation (byte ordering, etc)
+ on the target. The constant SECURITY_NATIVE_DREP may be supplied
+ by the transport indicating that the native format is in use.
+
+ NewContextHandle - New context handle. If this is a second call, this
+ can be the same as OldContextHandle.
+
+ OutputToken - Buffer to receive the output token.
+
+ ContextAttributes -Attributes of the context established.
+
+ #define ASC_RET_DELEGATE 0x00000001
+ #define ASC_RET_MUTUAL_AUTH 0x00000002
+ #define ASC_RET_REPLAY_DETECT 0x00000004
+ #define ASC_RET_SEQUENCE_DETECT 0x00000008
+ #define ASC_RET_CONFIDENTIALITY 0x00000010
+ #define ASC_RET_USE_SESSION_KEY 0x00000020
+ #define ASC_RET_ALLOCATED_BUFFERS 0x00000100
+ #define ASC_RET_USED_DCE_STYLE 0x00000200
+
+ ExpirationTime - Expiration time of the context.
+
+Return Value:
+
+ STATUS_SUCCESS - Message handled
+ SEC_I_CALLBACK_NEEDED -- Caller should call again later
+
+ SEC_E_NO_SPM -- Security Support Provider is not running
+ SEC_E_INVALID_TOKEN -- Token improperly formatted
+ SEC_E_INVALID_HANDLE -- Credential/Context Handle is invalid
+ SEC_E_BUFFER_TOO_SMALL -- Buffer for output token isn't big enough
+ SEC_E_LOGON_DENIED -- User is no allowed to logon to this server
+ SEC_E_INSUFFICIENT_MEMORY -- Not enough memory
+
+--*/
+
+{
+ SECURITY_STATUS SecStatus;
+ HANDLE LpcHandle = NULL;
+ BOOLEAN CallLsaDirectly;
+
+ PVOID InputTokenBuffer;
+ PULONG InputTokenSize;
+ ULONG LocalInputTokenSize;
+
+ PVOID OutputTokenBuffer;
+ PULONG OutputTokenSize;
+ ULONG LocalOutputTokenSize;
+ ULONG NegotiateFlags;
+ UCHAR SessionKey[MSV1_0_USER_SESSION_KEY_LENGTH];
+ HANDLE TokenHandle = NULL;
+ NTSTATUS SubStatus = STATUS_SUCCESS;
+ WCHAR ContextNames[UNLEN+DNLEN+2];
+ TimeStamp PasswordExpiry;
+
+ ContextNames[0] = L'\0';
+
+ //
+ // Get an LPC Handle to the NtLmSsp service.
+ //
+
+ LpcHandle = SspDllGetLpcHandle( FALSE, &CallLsaDirectly );
+
+ if ( LpcHandle == NULL ) {
+ SecStatus = SEC_E_NO_SPM;
+ goto Cleanup;
+ }
+
+ //
+ // Validate the arguments
+ //
+
+#ifdef notdef // ? RPC passes 0x10 here
+ if ( TargetDataRep != SECURITY_NATIVE_DREP ) {
+ SecStatus = STATUS_INVALID_PARAMETER;
+ goto Cleanup;
+ }
+#else // notdef
+ UNREFERENCED_PARAMETER( TargetDataRep );
+#endif // notdef
+
+
+ if ( !SspGetTokenBuffer( InputToken,
+ &InputTokenBuffer,
+ &InputTokenSize,
+ TRUE ) ) {
+ SecStatus = SEC_E_INVALID_TOKEN;
+ goto Cleanup;
+ }
+
+ if ( InputTokenSize == 0 ) {
+ InputTokenSize = &LocalInputTokenSize;
+ LocalInputTokenSize = 0;
+ }
+
+ if ( !SspGetTokenBuffer( OutputToken,
+ &OutputTokenBuffer,
+ &OutputTokenSize,
+ FALSE ) ) {
+ SecStatus = SEC_E_INVALID_TOKEN;
+ goto Cleanup;
+ }
+
+ if ( OutputTokenSize == 0 ) {
+ OutputTokenSize = &LocalOutputTokenSize;
+ LocalOutputTokenSize = 0;
+ }
+
+
+ //
+ // If we can call the LSA directly,
+ // skip the NtLmSsp service.
+ //
+
+ if ( CallLsaDirectly ) {
+
+ //
+ // If no previous context was passed in this is the first call.
+ //
+
+ if ( !ARGUMENT_PRESENT( OldContextHandle ) ) {
+
+ if ( !ARGUMENT_PRESENT( CredentialHandle ) ) {
+ SecStatus = SEC_E_INVALID_HANDLE;
+ }
+
+ SecStatus = SsprHandleNegotiateMessage(
+ NULL, // No client connection
+ CredentialHandle,
+ NewContextHandle,
+ ContextReqFlags,
+ *InputTokenSize,
+ InputTokenBuffer,
+ OutputTokenSize,
+ OutputTokenBuffer,
+ ContextAttributes,
+ ExpirationTime );
+
+ //
+ // If context was passed in, continue where we left off.
+ //
+
+ } else {
+
+ *NewContextHandle = *OldContextHandle;
+
+ SecStatus = SsprHandleAuthenticateMessage(
+ NULL, // No client connection
+ CredentialHandle,
+ NewContextHandle,
+ ContextReqFlags,
+ *InputTokenSize,
+ InputTokenBuffer,
+ OutputTokenSize,
+ OutputTokenBuffer,
+ ContextAttributes,
+ ExpirationTime,
+ SessionKey,
+ &NegotiateFlags,
+ &TokenHandle,
+ &SubStatus,
+ ContextNames,
+ &PasswordExpiry );
+ }
+
+ if ( SecStatus == SEC_I_CALL_NTLMSSP_SERVICE ) {
+ CallLsaDirectly = FALSE;
+ }
+
+ }
+
+ //
+ // Handle LPCing to the NtLmSsp service.
+ //
+
+ if ( !CallLsaDirectly ) {
+
+ SSP_API_MESSAGE Message;
+ PSSP_ACCEPT_SECURITY_CONTEXT_ARGS Args;
+
+ //
+ // Copy the caller's arguments to the LPC message.
+ //
+
+ Args = &Message.Arguments.AcceptSecurityContextArgs;
+
+ if ( !ARGUMENT_PRESENT( CredentialHandle ) ) {
+ Args->CredentialHandle.dwUpper = 0;
+ Args->CredentialHandle.dwLower = 0;
+ } else {
+ Args->CredentialHandle = *CredentialHandle;
+ }
+
+ if ( !ARGUMENT_PRESENT( OldContextHandle ) ) {
+ Args->ContextHandle.dwUpper = 0;
+ Args->ContextHandle.dwLower = 0;
+ } else {
+ Args->ContextHandle = *OldContextHandle;
+ }
+
+ Args->ContextReqFlags = ContextReqFlags;
+ Args->InputTokenSize = *InputTokenSize;
+ Args->InputToken = InputTokenBuffer;
+ Args->OutputTokenSize = *OutputTokenSize;
+ Args->OutputToken = OutputTokenBuffer;
+ Args->TokenHandle = NULL;
+ Args->SubStatus = STATUS_SUCCESS;
+ Args->ContextNames = ContextNames;
+
+
+
+ //
+ // Pass the message to the service and wait for a response.
+ //
+
+ SecStatus = SspCallService( LpcHandle,
+ SspLpcAcceptSecurityContext,
+ &Message,
+ sizeof(*Args) );
+
+ //
+ // This has to be copied on both success and failure
+ //
+
+ *ContextAttributes = Args->ContextAttributes;
+ SubStatus = Args->SubStatus;
+
+ if ( !NT_SUCCESS(SecStatus) ) {
+ goto Cleanup;
+ }
+
+
+ //
+ // Copy the return values to the caller.
+ //
+
+ *NewContextHandle = Args->ContextHandle;
+ *OutputTokenSize = Args->OutputTokenSize;
+ *ExpirationTime = Args->ExpirationTime;
+ NegotiateFlags = Args->NegotiateFlags;
+ TokenHandle = Args->TokenHandle;
+ RtlCopyMemory(SessionKey,Args->SessionKey, MSV1_0_USER_SESSION_KEY_LENGTH);
+ PasswordExpiry = Args->PasswordExpiry;
+ }
+
+ if ((SecStatus == SEC_E_OK) &&
+ !(*ContextAttributes & SSP_RET_REAUTHENTICATION)) {
+ SecStatus = SspMapContext(
+ NewContextHandle,
+ SessionKey,
+ NegotiateFlags,
+ TokenHandle,
+ ContextNames,
+ &PasswordExpiry
+ );
+ if (NT_SUCCESS(TokenHandle)) {
+ TokenHandle = NULL;
+ }
+
+ } else {
+
+ //
+ // Make sure this bit isn't sent to the caller
+ //
+
+ *ContextAttributes &= ~SSP_RET_REAUTHENTICATION;
+ }
+
+
+
+Cleanup:
+
+ if (TokenHandle != NULL) {
+ NtClose(TokenHandle);
+ }
+
+ SetLastError(RtlNtStatusToDosError(SubStatus));
+
+ return SecStatus;
+
+}
+
+
+
+
+SECURITY_STATUS
+SspImpersonateSecurityContext (
+ PCtxtHandle ContextHandle
+ )
+
+/*++
+
+Routine Description:
+
+
+ This API is allows service providers to impersonate the caller. This
+ API allows the application server to act as the client and thus all
+ necessary access controls are enforced.
+
+ The server must have obtained a valid context handle by submitting to
+ security system the incoming security token from the client via
+ AcceptSecurityContext() API. The server winds up with a context handle
+ if the inbound context was validated successfully. The API creates an
+ impersonation token and allows the thread or process to run with the
+ impersonation context. The application server must call
+ RevertSecurityContext() when it is done or wants to restore its own
+ security context.
+
+
+Arguments:
+
+ ContextHandle - Handle to the context to impersonate. This handle
+ must have been obtained in the AcceptSecurityContext() call.
+
+Return Value:
+
+ STATUS_SUCCESS - Call completed successfully
+
+ SEC_E_NO_SPM -- Security Support Provider is not running
+ SEC_E_INVALID_HANDLE -- Context Handle is invalid
+
+--*/
+{
+ return(SsprImpersonateSecurityContext(
+ ContextHandle ));
+
+}
+
+
+
+
+SECURITY_STATUS
+SspRevertSecurityContext (
+ PCtxtHandle ContextHandle
+ )
+
+/*++
+
+Routine Description:
+
+ This API is called by the service provider or an application server
+ when it wishes to stop impersonating the caller. The server must have
+ used this handle in the ImpersonateSecurityContext() API.
+
+Arguments:
+
+ ContextHandle - Handle to the context to query. This handle must have
+ been obtained in the AcceptSecurityContext() call and used
+ in the ImpersonateSecurityContext() call.
+
+Return Value:
+
+ STATUS_SUCCESS - Call completed successfully
+
+ SEC_E_NO_SPM -- Security Support Provider is not running
+ SEC_E_INVALID_HANDLE -- Credential/Context Handle is invalid
+ SEC_E_UNSUPPORTED_FUNCTION -- Function code is not supported
+
+--*/
+{
+ return( SsprRevertSecurityContext(
+ ContextHandle ));
+}
+
+
+
+
+
+
+SECURITY_STATUS
+SspQueryContextAttributesW(
+ IN PCtxtHandle ContextHandle,
+ IN ULONG Attribute,
+ OUT PVOID Buffer
+ )
+
+/*++
+
+Routine Description:
+
+ This API allows a customer of the security services to determine
+ certain attributes of the context. These are: sizes, names, and
+ lifespan.
+
+Arguments:
+
+ ContextHandle - Handle to the context to query.
+
+ Attribute - Attribute to query.
+
+ #define SECPKG_ATTR_SIZES 0
+ #define SECPKG_ATTR_NAMES 1
+ #define SECPKG_ATTR_LIFESPAN 2
+
+ Buffer - Buffer to copy the data into. The buffer must be large enough
+ to fit the queried attribute.
+
+Return Value:
+
+ STATUS_SUCCESS - Call completed successfully
+
+ SEC_E_NO_SPM -- Security Support Provider is not running
+ SEC_E_INVALID_HANDLE -- Credential/Context Handle is invalid
+ SEC_E_UNSUPPORTED_FUNCTION -- Function code is not supported
+
+--*/
+
+{
+ SECURITY_STATUS SecStatus;
+ HANDLE LpcHandle = NULL;
+ BOOLEAN CallLsaDirectly;
+ CtxtHandle TempContextHandle = *ContextHandle;
+ PSecPkgContext_NamesW Names = NULL;
+ PSecPkgContext_DceInfo DceInfo = NULL;
+
+ //
+ // Get an LPC Handle to the NtLmSsp service.
+ //
+
+ LpcHandle = SspDllGetLpcHandle( FALSE, &CallLsaDirectly );
+
+ if ( LpcHandle == NULL ) {
+ SecStatus = SEC_E_NO_SPM;
+ goto Cleanup;
+ }
+
+
+ //
+ // If caller wants names, we have to allocate the buffer now since the
+ // service can't allocate memory with LocalAlloc.
+ //
+
+ if (Attribute == SECPKG_ATTR_NAMES) {
+ Names = (PSecPkgContext_NamesW) Buffer;
+ Names->sUserName = (PWCHAR) LocalAlloc(0, (UNLEN+1) * sizeof(WCHAR));
+ if (Names->sUserName == NULL) {
+ SecStatus = SEC_E_INSUFFICIENT_MEMORY;
+ goto Cleanup;
+ }
+ }
+
+ //
+ // If caller wants dce info, we have to allocate the buffer now since the
+ // service can't allocate memory with LocalAlloc.
+ //
+
+ if (Attribute == SECPKG_ATTR_DCE_INFO) {
+ DceInfo = (PSecPkgContext_DceInfo) Buffer;
+ DceInfo->pPac = LocalAlloc(0, (UNLEN+1) * sizeof(WCHAR));
+ if (DceInfo->pPac == NULL) {
+ SecStatus = SEC_E_INSUFFICIENT_MEMORY;
+ goto Cleanup;
+ }
+ }
+
+ //
+ // First try the local contexts
+ //
+
+ SecStatus = SspLocalQueryContextAttributes(
+ ContextHandle,
+ Attribute,
+ Buffer
+ );
+
+ if (SecStatus != SEC_E_INVALID_HANDLE)
+ {
+ //
+ // If the context did not exist try the main list ofcontexts. This
+ // handles querying attributes before a context is finalized.
+ //
+
+ goto Cleanup;
+ }
+
+ //
+ // If we can call the LSA directly,
+ // skip the NtLmSsp service.
+ //
+
+ if ( CallLsaDirectly ) {
+
+ SecStatus = SsprQueryContextAttributes(
+ NULL, // No client connection
+ &TempContextHandle,
+ Attribute,
+ Buffer );
+
+ if ( SecStatus == SEC_I_CALL_NTLMSSP_SERVICE ) {
+ CallLsaDirectly = FALSE;
+ }
+
+ }
+
+ //
+ // Handle LPCing to the NtLmSsp service.
+ //
+
+ if ( !CallLsaDirectly ) {
+ SSP_API_MESSAGE Message;
+ PSSP_QUERY_CONTEXT_ATTRIBUTES_ARGS Args;
+
+ //
+ // Copy the caller's arguments to the LPC message.
+ //
+
+ Args = &Message.Arguments.QueryContextAttributesArgs;
+ Args->ContextHandle = TempContextHandle;
+ Args->Attribute = Attribute;
+ Args->Buffer = Buffer;
+
+
+ //
+ // Pass the message to the service and wait for a response.
+ //
+
+ SecStatus = SspCallService( LpcHandle,
+ SspLpcQueryContextAttributes,
+ &Message,
+ sizeof(*Args) );
+
+ if ( !NT_SUCCESS(SecStatus) ) {
+ goto Cleanup;
+ }
+
+
+ //
+ // Copy the return values to the caller.
+ //
+
+ /* None */
+
+ }
+
+Cleanup:
+ if (!NT_SUCCESS(SecStatus)) {
+ if ((Attribute == SECPKG_ATTR_NAMES) &&
+ (Names != NULL) &&
+ (Names->sUserName != NULL)) {
+
+ LocalFree(Names->sUserName);
+
+ } else if ((Attribute == SECPKG_ATTR_DCE_INFO) &&
+ (DceInfo != NULL) &&
+ (DceInfo->pPac != NULL)) {
+
+ LocalFree(DceInfo->pPac);
+
+ }
+ }
+ return SecStatus;
+
+}
+
+
+SECURITY_STATUS
+SspQueryContextAttributesA(
+ IN PCtxtHandle ContextHandle,
+ IN ULONG Attribute,
+ OUT PVOID Buffer
+ )
+/*++
+
+RoutineDescription:
+
+ Ansi thunk to QueryContextAttributesU.
+
+--*/
+{
+ SECURITY_STATUS SecStatus;
+
+ SecStatus = SspQueryContextAttributesW(
+ ContextHandle,
+ Attribute,
+ Buffer
+ );
+
+ //
+ // If that succeeded and the attribute was NAMES, convert from unicode
+ // to ansi
+ //
+
+ if (NT_SUCCESS(SecStatus) &&
+ (Attribute == SECPKG_ATTR_NAMES)) {
+
+ CHAR Name[UNLEN];
+ PSecPkgContext_NamesW ContextNames = (PSecPkgContext_NamesW) Buffer;
+
+ //
+ // Convert into the buffer and then copy over the unicode name
+ //
+
+ wcstombs(Name, ContextNames->sUserName, UNLEN);
+
+ strcpy((CHAR *) ContextNames->sUserName, Name);
+ }
+
+ return(SecStatus);
+}
+
+
+SECURITY_STATUS
+SspDeleteSecurityContext (
+ PCtxtHandle ContextHandle
+ )
+
+/*++
+
+Routine Description:
+
+ Deletes the local data structures associated with the specified
+ security context.
+
+ This API terminates a context on the local machine.
+
+Arguments:
+
+ ContextHandle - Handle to the context to delete
+
+
+Return Value:
+
+ STATUS_SUCCESS - Call completed successfully
+
+ SEC_E_NO_SPM -- Security Support Provider is not running
+ SEC_E_INVALID_HANDLE -- Credential/Context Handle is invalid
+
+--*/
+
+{
+ SECURITY_STATUS SecStatus;
+ HANDLE LpcHandle = NULL;
+ BOOLEAN CallLsaDirectly;
+ CtxtHandle TempContextHandle = *ContextHandle;
+
+ //
+ // Get an LPC Handle to the NtLmSsp service.
+ //
+
+ LpcHandle = SspDllGetLpcHandle( FALSE, &CallLsaDirectly );
+
+ if ( LpcHandle == NULL ) {
+ SecStatus = SEC_E_NO_SPM;
+ goto Cleanup;
+ }
+
+
+ //
+ // Delete any local context
+ //
+
+ SspHandleLocalDelete(ContextHandle);
+
+ //
+ // If we can call the LSA directly,
+ // skip the NtLmSsp service.
+ //
+
+ if ( CallLsaDirectly ) {
+
+ SecStatus = SsprDeleteSecurityContext(
+ NULL, // No client connection
+ &TempContextHandle );
+
+ if ( SecStatus == SEC_I_CALL_NTLMSSP_SERVICE ) {
+ CallLsaDirectly = FALSE;
+ }
+
+ }
+
+ //
+ // Handle LPCing to the NtLmSsp service.
+ //
+
+ if ( !CallLsaDirectly ) {
+ SSP_API_MESSAGE Message;
+ PSSP_DELETE_SECURITY_CONTEXT_ARGS Args;
+
+ //
+ // Copy the caller's arguments to the LPC message.
+ //
+
+ Args = &Message.Arguments.DeleteSecurityContextArgs;
+ Args->ContextHandle = TempContextHandle;
+
+
+ //
+ // Pass the message to the service and wait for a response.
+ //
+
+ SecStatus = SspCallService( LpcHandle,
+ SspLpcDeleteSecurityContext,
+ &Message,
+ sizeof(*Args) );
+
+ if ( !NT_SUCCESS(SecStatus) ) {
+ goto Cleanup;
+ }
+
+ }
+
+
+
+
+
+Cleanup:
+ return SecStatus;
+
+}
+
+
+SECURITY_STATUS
+NtLmSspControl(
+ IN ULONG FunctionCode,
+ IN ULONG Data
+ )
+
+/*++
+
+Routine Description:
+
+ This API allows the NtLmSsp service to be controlled in a test environment.
+
+Arguments:
+
+ FunctionCode - Specifies what function is to be performed.
+
+ Data - Specifies data specific to the function
+
+Return Value:
+
+ STATUS_SUCCESS - Call completed successfully
+
+ SEC_E_NO_SPM -- Security Support Provider is not running
+ SEC_E_UNSUPPORTED_FUNCTION -- Function code is not supported
+
+--*/
+
+{
+#if DBG
+ SECURITY_STATUS SecStatus;
+ HANDLE LpcHandle = NULL;
+ BOOLEAN CallLsaDirectly;
+ SSP_API_MESSAGE Message;
+ PSSP_NTLMSSP_CONTROL_ARGS Args;
+
+ //
+ // Get an LPC Handle to the NtLmSsp service.
+ //
+
+ LpcHandle = SspDllGetLpcHandle( FALSE, &CallLsaDirectly );
+
+ if ( LpcHandle == NULL ) {
+ SecStatus = SEC_E_NO_SPM;
+ goto Cleanup;
+ }
+
+
+ //
+ // Copy the caller's arguments to the LPC message.
+ //
+
+ Args = &Message.Arguments.NtLmSspControlArgs;
+ Args->FunctionCode = FunctionCode;
+ Args->Data = Data;
+
+
+ //
+ // Pass the message to the service and wait for a response.
+ //
+
+ SecStatus = SspCallService( LpcHandle,
+ SspLpcNtLmSspControl,
+ &Message,
+ sizeof(*Args) );
+
+ if ( !NT_SUCCESS(SecStatus) ) {
+ goto Cleanup;
+ }
+
+
+ //
+ // Copy the return values to the caller.
+ //
+
+ /* NONE */
+
+
+Cleanup:
+ return SecStatus;
+#else // DBG
+ return SEC_E_UNSUPPORTED_FUNCTION;
+ UNREFERENCED_PARAMETER( Data );
+ UNREFERENCED_PARAMETER( FunctionCode );
+#endif // DBG
+
+}
+
+
+SECURITY_STATUS SEC_ENTRY
+SspFreeContextBuffer (
+ void __SEC_FAR * ContextBuffer
+ )
+
+/*++
+
+Routine Description:
+
+ This API is provided to allow callers of security API such as
+ InitializeSecurityContext() for free the memory buffer allocated for
+ returning the outbound context token.
+
+Arguments:
+
+ ContextBuffer - Address of the buffer to be freed.
+
+Return Value:
+
+ STATUS_SUCCESS - Call completed successfully
+
+--*/
+
+{
+ //
+ // The only allocated buffer that NtLmSsp currently returns to the caller
+ // is from EnumeratePackages. It uses LocalAlloc to allocate memory. If
+ // we ever need memory to be allocated by the service, we have to rethink
+ // how this routine distinguishes between to two types of allocated memory.
+ //
+
+ (VOID) LocalFree( ContextBuffer );
+ return STATUS_SUCCESS;
+}
+
+
+//
+// The functions below are merely stub functions to ensure our function table
+// doesn't have any NULL values. They simply return SEC_E_UNSUPPORTED_FUNCTION
+// so no additional documentation is required.
+//
+
+
+SECURITY_STATUS
+SspApplyControlToken (
+ PCtxtHandle ContextHandle,
+ PSecBufferDesc Input
+ )
+{
+#if DBG
+ SspPrint(( SSP_API, "ApplyContextToken Called\n" ));
+#endif // DBG
+ return SEC_E_UNSUPPORTED_FUNCTION;
+ UNREFERENCED_PARAMETER( ContextHandle );
+ UNREFERENCED_PARAMETER( Input );
+}
+
+SECURITY_STATUS
+SspMakeSignature (
+ PCtxtHandle ContextHandle,
+ unsigned long QualityOfProtection,
+ PSecBufferDesc Message,
+ unsigned long SequenceNumber
+ )
+{
+ return(SspHandleSignMessage(ContextHandle,
+ QualityOfProtection,
+ Message,
+ SequenceNumber));
+}
+
+SECURITY_STATUS
+SspVerifySignature (
+ PCtxtHandle ContextHandle,
+ PSecBufferDesc Message,
+ unsigned long SequenceNumber,
+ unsigned long * QualityOfProtection
+ )
+{
+
+ return (SspHandleVerifyMessage(ContextHandle,
+ Message,
+ SequenceNumber,
+ QualityOfProtection));
+}
+
+SECURITY_STATUS
+SspSealMessage (
+ PCtxtHandle ContextHandle,
+ unsigned long QualityOfProtection,
+ PSecBufferDesc Message,
+ unsigned long SequenceNumber
+ )
+{
+ return(SspHandleSealMessage(ContextHandle,
+ QualityOfProtection,
+ Message,
+ SequenceNumber));
+}
+
+SECURITY_STATUS
+SspUnsealMessage (
+ PCtxtHandle ContextHandle,
+ PSecBufferDesc Message,
+ unsigned long SequenceNumber,
+ unsigned long * QualityOfProtection
+ )
+{
+
+ return (SspHandleUnsealMessage(ContextHandle,
+ Message,
+ SequenceNumber,
+ QualityOfProtection));
+}
+
+SECURITY_STATUS SEC_ENTRY
+SspCompleteAuthToken (
+ PCtxtHandle ContextHandle,
+ PSecBufferDesc BufferDescriptor
+ )
+{
+#if DBG
+ SspPrint(( SSP_API, "CompleteAuthToken Called\n" ));
+#endif // DBG
+ return SEC_E_UNSUPPORTED_FUNCTION;
+ UNREFERENCED_PARAMETER( ContextHandle );
+ UNREFERENCED_PARAMETER( BufferDescriptor );
+}
diff --git a/private/net/svcdlls/ntlmssp/client/support.c b/private/net/svcdlls/ntlmssp/client/support.c
new file mode 100644
index 000000000..16a96d064
--- /dev/null
+++ b/private/net/svcdlls/ntlmssp/client/support.c
@@ -0,0 +1,311 @@
+/*++
+
+Copyright (c) 1987-1993 Microsoft Corporation
+
+Module Name:
+
+ support.c
+
+Abstract:
+
+ Support routines allowing the NtLmSsp DLL side use the common routines
+ shared between the DLL and the SERVICE.
+
+ These routines exist in the DLL side. They are different implementations
+ of the same routines that exist on the SERVICE side. These implementations
+ are significantly simpler because they run in the address space of the
+ caller.
+
+Author:
+
+ Cliff Van Dyke (CliffV) 22-Sep-1993
+
+Environment:
+
+ User mode only.
+ Contains NT-specific code.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+--*/
+
+//
+// Common include files.
+//
+
+#include <ntlmsspc.h> // Include files common to DLL side of NtLmSsp
+
+
+#if DBG
+#include <stdio.h>
+#define MAX_PRINTF_LEN 1024 // Arbitrary.
+
+
+VOID
+SspPrintRoutine(
+ IN DWORD DebugFlag,
+ IN LPSTR Format,
+ ...
+ )
+
+{
+ va_list arglist;
+ char OutputBuffer[MAX_PRINTF_LEN];
+ ULONG length;
+ static BeginningOfLine = TRUE;
+ static LineCount = 0;
+
+ //
+ // If we aren't debugging this functionality, just return.
+ //
+ if ( DebugFlag != 0 && (SspGlobalDbflag & DebugFlag) == 0 ) {
+ return;
+ }
+
+ //
+ // vsprintf isn't multithreaded + we don't want to intermingle output
+ // from different threads.
+ //
+
+ EnterCriticalSection( &SspGlobalLogFileCritSect );
+ length = 0;
+
+ //
+ // Handle the beginning of a new line.
+ //
+ //
+
+ if ( BeginningOfLine ) {
+
+ //
+ // If we're writing to the debug terminal,
+ // indicate this is an NtLmSsp message.
+ //
+
+ length += (ULONG) sprintf( &OutputBuffer[length], "[NtLmSsp.dll] " );
+
+ //
+ // Put the timestamp at the begining of the line.
+ //
+ IF_DEBUG( TIMESTAMP ) {
+ SYSTEMTIME SystemTime;
+ GetLocalTime( &SystemTime );
+ length += (ULONG) sprintf( &OutputBuffer[length],
+ "%02u/%02u %02u:%02u:%02u ",
+ SystemTime.wMonth,
+ SystemTime.wDay,
+ SystemTime.wHour,
+ SystemTime.wMinute,
+ SystemTime.wSecond );
+ }
+
+ //
+ // Indicate the type of message on the line
+ //
+ {
+ char *Text;
+
+ switch (DebugFlag) {
+ case SSP_INIT:
+ Text = "INIT"; break;
+ case SSP_MISC:
+ Text = "MISC"; break;
+ case SSP_CRITICAL:
+ Text = "CRITICAL"; break;
+ case SSP_LPC:
+ case SSP_LPC_MORE:
+ Text = "LPC"; break;
+ case SSP_API:
+ case SSP_API_MORE:
+ Text = "API"; break;
+
+ default:
+ Text = "UNKNOWN"; break;
+
+ case 0:
+ Text = NULL;
+ }
+ if ( Text != NULL ) {
+ length += (ULONG) sprintf( &OutputBuffer[length], "[%s] ", Text );
+ }
+ }
+ }
+ //
+ // Put a the information requested by the caller onto the line
+ //
+
+ va_start(arglist, Format);
+
+ length += (ULONG) vsprintf(&OutputBuffer[length], Format, arglist);
+ BeginningOfLine = (length > 0 && OutputBuffer[length-1] == '\n' );
+
+ va_end(arglist);
+
+ ASSERT(length <= MAX_PRINTF_LEN);
+
+
+ //
+ // just output to the debug terminal
+ //
+
+ (void) DbgPrint( (PCH) OutputBuffer);
+
+ LeaveCriticalSection( &SspGlobalLogFileCritSect );
+
+} // SspPrintRoutine
+
+#endif // DBG
+
+
+SECURITY_STATUS
+SspLpcCopyToClientBuffer (
+ IN PSSP_CLIENT_CONNECTION ClientConnection,
+ IN ULONG Size,
+ OUT PVOID ClientBufferAddress,
+ IN PVOID LocalBufferAddress
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is used to copy information into a client process's address
+ space.
+
+ Since this version of this routine runs in the client process's address
+ space, the operation is trivial.
+
+Arguments:
+
+ ClientConnection - Is a pointer to a data structure representing the
+ client process.
+
+ Size - Indicates the Size of the buffer (in bytes) to be
+ copied.
+
+ ClientBufferAddress - Is the address of the buffer to receive the
+ data. This address is the address of the buffer within the
+ client process, not in the current process.
+
+ LocalBufferAddress - Points to the local buffer whose contents are to
+ be copied into the client address space.
+
+Return Status:
+
+ STATUS_SUCCESS - Indicates the routine completed successfully.
+
+
+--*/
+
+{
+
+ RtlCopyMemory( ClientBufferAddress, LocalBufferAddress, Size );
+ return STATUS_SUCCESS;
+ UNREFERENCED_PARAMETER( ClientConnection );
+}
+
+
+SECURITY_STATUS
+SspLpcCopyFromClientBuffer (
+ IN PSSP_CLIENT_CONNECTION ClientConnection,
+ IN ULONG Size,
+ OUT PVOID LocalBufferAddress,
+ IN PVOID ClientBufferAddress
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is used to copy information from a client process's address
+ space into a local buffer.
+
+ Since this version of this routine runs in the client process's address
+ space, the operation is trivial.
+
+Arguments:
+
+ ClientConnection - Is a pointer to a data structure representing the
+ client process.
+
+ Size - Indicates the Size of the buffer (in bytes) to be
+ copied.
+
+ LocalBufferAddress - Points to the local buffer into which the data is
+ to be copied.
+
+ ClientBufferAddress - Is the address of the client buffer whose
+ contents are to be copied. This address is the address of
+ the buffer within the client process, not in the current
+ process.
+
+Return Status:
+
+ STATUS_SUCCESS - Indicates the routine completed successfully.
+
+
+--*/
+
+{
+ RtlCopyMemory( LocalBufferAddress, ClientBufferAddress, Size );
+ return STATUS_SUCCESS;
+ UNREFERENCED_PARAMETER( ClientConnection );
+}
+
+
+SECURITY_STATUS
+SspLpcImpersonateTokenHandle(
+ IN PSSP_CLIENT_CONNECTION ClientConnection,
+ IN HANDLE TokenHandle,
+ IN PCLIENT_ID ClientId
+ )
+
+/*++
+
+Routine Description:
+
+ This routine causes the client thread to impersonate the specified
+ token handle.
+
+ Since this version of this routine runs in the client process's address
+ space, the operation is trivial.
+
+Arguments:
+
+ ClientConnection - Is a pointer to a data structure representing the
+ client process.
+
+ TokenHandle - TokenHandle in our address space.
+
+ ClientId - Pointer to the client ID of the client's thread.
+
+Return Status:
+
+ STATUS_SUCCESS - Indicates the routine completed successfully.
+
+
+--*/
+
+{
+ NTSTATUS Status;
+ SECURITY_STATUS SecStatus;
+
+ //
+ // Impersonate the token
+ //
+
+ Status = NtSetInformationThread( NtCurrentThread(),
+ ThreadImpersonationToken,
+ &TokenHandle,
+ sizeof(TokenHandle) );
+
+ if ( !NT_SUCCESS(Status) ) {
+ SecStatus = SspNtStatusToSecStatus( Status, SEC_E_NO_IMPERSONATION );
+ } else {
+ SecStatus = STATUS_SUCCESS;
+ }
+ return SecStatus;
+ UNREFERENCED_PARAMETER( ClientConnection );
+ UNREFERENCED_PARAMETER( ClientId );
+}
diff --git a/private/net/svcdlls/ntlmssp/common/context.c b/private/net/svcdlls/ntlmssp/common/context.c
new file mode 100644
index 000000000..c5ffdda05
--- /dev/null
+++ b/private/net/svcdlls/ntlmssp/common/context.c
@@ -0,0 +1,4646 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ context.c
+
+Abstract:
+
+ API and support routines for handling security contexts.
+
+Author:
+
+ Cliff Van Dyke (CliffV) 13-Jul-1993
+
+Revision History:
+
+--*/
+
+
+//
+// Common include files.
+//
+
+#include <ntlmcomn.h> // Common definitions for DLL and SERVICE
+#include <ntlmsspi.h> // Data private to the common routines
+#include <align.h> // ALIGN_WCHAR, etc
+#include <crypt.h> // Encryption constants and routine
+#include <rc4.h> // RC4 encryption types and functions
+
+//
+// Crit Sect to protect various globals in this module.
+//
+
+CRITICAL_SECTION SspContextCritSect;
+LIST_ENTRY SspContextList;
+
+
+//
+// Variables describing us as a Logon Process
+//
+
+HANDLE SspGlobalLogonProcessHandle;
+ULONG SspGlobalAuthenticationPackage;
+
+
+
+PSSP_CONTEXT
+SspContextReferenceContext(
+ IN PCtxtHandle ContextHandle,
+ IN PSSP_CLIENT_CONNECTION ClientConnection,
+ IN BOOLEAN RemoveContext
+ )
+
+/*++
+
+Routine Description:
+
+ This routine checks to see if the Context is for the specified
+ Client Connection, and references the Context if it is valid.
+
+ The caller may optionally request that the Context be
+ removed from the list of valid Contexts - preventing future
+ requests from finding this Context.
+
+Arguments:
+
+ ContextHandle - Points to the ContextHandle of the Context
+ to be referenced.
+
+ ClientConnection - Points to the client connection of the client
+ referencing the handle. (NULL means an internal reference.)
+
+ RemoveContext - This boolean value indicates whether the caller
+ wants the Context to be removed from the list
+ of Contexts. TRUE indicates the Context is to be removed.
+ FALSE indicates the Context is not to be removed.
+
+
+Return Value:
+
+ NULL - the Context was not found.
+
+ Otherwise - returns a pointer to the referenced Context.
+
+--*/
+
+{
+ PLIST_ENTRY ListEntry;
+ PSSP_CONTEXT Context;
+
+ //
+ // Sanity check
+ //
+
+ if ( ContextHandle->dwLower != SspCommonSecHandleValue ) {
+ return NULL;
+ }
+
+ //
+ // Acquire exclusive access to the Context list
+ //
+
+ EnterCriticalSection( &SspContextCritSect );
+
+
+ //
+ // Now walk the list of Contexts looking for a match.
+ //
+
+ for ( ListEntry = SspContextList.Flink;
+ ListEntry != &SspContextList;
+ ListEntry = ListEntry->Flink ) {
+
+ Context = CONTAINING_RECORD( ListEntry, SSP_CONTEXT, Next );
+
+
+ //
+ // Found a match ... reference this Context
+ // (if the Context is being removed, we would increment
+ // and then decrement the reference, so don't bother doing
+ // either - since they cancel each other out).
+ //
+
+ if ( Context == (PSSP_CONTEXT) ContextHandle->dwUpper &&
+ (ClientConnection == NULL ||
+ ClientConnection == Context->ClientConnection )) {
+
+
+ if (!RemoveContext) {
+
+ //
+ // Timeout this context if caller is not trying to remove it.
+ // We only timeout contexts that are being setup, not
+ // fully authenticated contexts.
+ //
+
+ if ( SspTimeHasElapsed( Context->StartTime,
+ Context->Interval ) ) {
+ if ( (Context->State != AuthenticatedState) &&
+ (Context->State != AuthenticateSentState) &&
+ (Context->State != PassedToServiceState) ) {
+ SspPrint(( SSP_API, "Context 0x%lx has timed out.\n",
+ ContextHandle->dwUpper ));
+
+ LeaveCriticalSection( &SspContextCritSect );
+ return NULL;
+ }
+ }
+
+ Context->References += 1;
+
+ } else {
+
+ RemoveEntryList( &Context->Next );
+ RemoveEntryList( &Context->NextForThisClient );
+ SspPrint(( SSP_API_MORE, "Delinked Context 0x%lx\n",
+ Context ));
+ }
+
+ LeaveCriticalSection( &SspContextCritSect );
+ return Context;
+
+ }
+
+ }
+
+
+ //
+ // No match found
+ //
+ SspPrint(( SSP_API, "Tried to reference unknown Context 0x%lx\n",
+ ContextHandle->dwUpper ));
+
+ LeaveCriticalSection( &SspContextCritSect );
+ return NULL;
+
+}
+
+
+VOID
+SspContextDereferenceContext(
+ PSSP_CONTEXT Context
+ )
+
+/*++
+
+Routine Description:
+
+ This routine decrements the specified Context's reference count.
+ If the reference count drops to zero, then the Context is deleted
+
+Arguments:
+
+ Context - Points to the Context to be dereferenced.
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ ULONG References;
+
+
+ //
+ // Decrement the reference count
+ //
+
+ EnterCriticalSection( &SspContextCritSect );
+ ASSERT( Context->References >= 1 );
+ References = -- Context->References;
+ LeaveCriticalSection( &SspContextCritSect );
+
+ //
+ // If the count dropped to zero, then run-down the Context
+ //
+
+ if (References == 0) {
+
+ SspPrint(( SSP_API_MORE, "Deleting Context 0x%lx\n",
+ Context ));
+
+
+ if ( Context->DomainName.Buffer != NULL ) {
+ (VOID) LocalFree( Context->DomainName.Buffer );
+ }
+ if ( Context->UserName.Buffer != NULL ) {
+ (VOID) LocalFree( Context->UserName.Buffer );
+ }
+ if ( Context->Password.Buffer != NULL ) {
+ (VOID) LocalFree( Context->Password.Buffer );
+ }
+ if ( Context->TokenHandle != NULL ) {
+ NTSTATUS IgnoreStatus;
+ IgnoreStatus = NtClose( Context->TokenHandle );
+ ASSERT( NT_SUCCESS(IgnoreStatus) );
+ }
+ if (Context->Credential != NULL) {
+ SspCredentialDereferenceCredential( Context->Credential );
+ }
+
+ (VOID) LocalFree( Context );
+
+ }
+
+ return;
+
+}
+
+
+PSSP_CONTEXT
+SspContextAllocateContext(
+ IN PSSP_CLIENT_CONNECTION ClientConnection
+ )
+
+/*++
+
+Routine Description:
+
+ This routine allocates the security context block, initializes it and
+ links it onto the specified credential.
+
+Arguments:
+
+ ClientConnection - Points to the client connection of the client
+ referencing the context. (NULL means an internal reference.)
+
+
+Return Value:
+
+ NULL -- Not enough memory to allocate context.
+
+ otherwise -- pointer to allocated and referenced context.
+
+--*/
+
+{
+ PSSP_CONTEXT Context;
+
+ //
+ // Allocate a Context block and initialize it.
+ //
+
+ Context = LocalAlloc( LMEM_ZEROINIT, sizeof(SSP_CONTEXT) );
+
+ if ( Context == NULL ) {
+ SspPrint(( SSP_API, "Cannot allocate Context.\n" ));
+ return NULL;
+ }
+
+ //
+ // The reference count is set to 2. 1 to indicate it is on the
+ // valid Context list, and one for the our own reference.
+ //
+ // Actually its on both a global credential list and a per client connection
+ // list, but we link/delink from both lists at the same time so a single
+ // reference count handles both.
+ //
+
+ Context->References = 2;
+ Context->ClientConnection = ClientConnection;
+ Context->NegotiateFlags = 0;
+ Context->ContextFlags = 0;
+ Context->State = IdleState;
+ RtlInitUnicodeString(
+ &Context->DomainName,
+ NULL
+ );
+ RtlInitUnicodeString(
+ &Context->UserName,
+ NULL
+ );
+ RtlInitUnicodeString(
+ &Context->Password,
+ NULL
+ );
+ Context->ServerContextHandle.dwLower = 0;
+ Context->ServerContextHandle.dwUpper = 0;
+ Context->TokenHandle = NULL;
+
+ //
+ // Timeout this context.
+ //
+
+ (VOID) NtQuerySystemTime( &Context->StartTime );
+ Context->Interval = NTLMSSP_MAX_LIFETIME;
+
+
+ //
+ // Add it to the list of valid Context handles.
+ //
+
+ EnterCriticalSection( &SspContextCritSect );
+ InsertHeadList( &SspContextList, &Context->Next );
+ if ( ClientConnection != NULL ) {
+ InsertHeadList( &ClientConnection->ContextHead, &Context->NextForThisClient );
+ } else {
+ InitializeListHead( &Context->NextForThisClient );
+ }
+ LeaveCriticalSection( &SspContextCritSect );
+
+ SspPrint(( SSP_API_MORE, "Added Context 0x%lx\n", Context ));
+
+ return Context;
+}
+
+
+
+
+VOID
+SspContextClientConnectionDropped(
+ PSSP_CLIENT_CONNECTION ClientConnection
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is called when the ClientConnection is dropped to allow
+ us to remove any Contexts for the ClientConnection.
+
+Arguments:
+
+ ClientConnection - Pointer to the ClientConnection that has been dropped.
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ //
+ // Drop any lingering Contexts
+ //
+
+ EnterCriticalSection( &SspContextCritSect );
+ while ( !IsListEmpty( &ClientConnection->ContextHead ) ) {
+ CtxtHandle ContextHandle;
+ PSSP_CONTEXT Context;
+
+ ContextHandle.dwUpper =
+ (LONG) CONTAINING_RECORD( ClientConnection->ContextHead.Flink,
+ SSP_CONTEXT,
+ NextForThisClient );
+
+ ContextHandle.dwLower = SspCommonSecHandleValue;
+
+ LeaveCriticalSection( &SspContextCritSect );
+
+ Context = SspContextReferenceContext(
+ &ContextHandle,
+ ClientConnection,
+ TRUE); // Remove Context
+
+ if ( Context != NULL ) {
+ SspContextDereferenceContext(Context);
+ }
+
+ EnterCriticalSection( &SspContextCritSect );
+ }
+ LeaveCriticalSection( &SspContextCritSect );
+
+}
+
+
+SECURITY_STATUS
+SspContextGetMessage(
+ IN PSSP_CLIENT_CONNECTION ClientConnection,
+ IN PVOID InputMessage,
+ IN ULONG InputMessageSize,
+ IN NTLM_MESSAGE_TYPE ExpectedMessageType,
+ OUT PVOID* OutputMessage
+ )
+
+/*++
+
+Routine Description:
+
+ This routine copies the InputMessage into the local address space.
+ This routine then validates the message header.
+
+Arguments:
+
+ ClientConnection - Describes the client process.
+
+ InputMessage - Address of the message in the client process.
+
+ InputMessageSize - Size of the message (in bytes).
+
+ ExpectedMessageType - The type of message the should be in the message
+ header.
+
+ OutputMessage - Returns a pointer to an allocated buffer that contains
+ the message. The buffer should be freed using LocalFree.
+
+
+Return Value:
+
+ STATUS_SUCCESS - Call completed successfully
+
+ SEC_E_INVALID_TOKEN -- Message improperly formatted
+ SEC_E_INSUFFICIENT_MEMORY -- Not enough memory to allocate message
+
+--*/
+
+{
+ SECURITY_STATUS SecStatus;
+ PNEGOTIATE_MESSAGE TypicalMessage;
+
+
+ //
+ // Allocate a local buffer for the message.
+ //
+
+ ASSERT( NTLMSP_MAX_TOKEN_SIZE >= NTLMSSP_MAX_MESSAGE_SIZE );
+ if ( InputMessageSize > NTLMSSP_MAX_MESSAGE_SIZE ) {
+ return SEC_E_INVALID_TOKEN;
+ }
+
+ TypicalMessage = LocalAlloc( 0, InputMessageSize );
+
+ if ( TypicalMessage == NULL ) {
+ return SEC_E_INSUFFICIENT_MEMORY;
+ }
+
+
+ //
+ // Copy the message into the buffer
+ //
+
+ SecStatus = SspLpcCopyFromClientBuffer (
+ ClientConnection,
+ InputMessageSize,
+ TypicalMessage,
+ InputMessage );
+
+ if ( !NT_SUCCESS(SecStatus) ) {
+ (VOID) LocalFree( TypicalMessage );
+ return SecStatus;
+ }
+
+
+ //
+ // Validate the message header.
+ //
+
+ if ( strncmp( TypicalMessage->Signature,
+ NTLMSSP_SIGNATURE,
+ sizeof(NTLMSSP_SIGNATURE)) != 0 ||
+ TypicalMessage->MessageType != ExpectedMessageType ) {
+
+ (VOID) LocalFree( TypicalMessage );
+ return SEC_E_INVALID_TOKEN;
+
+ }
+
+ *OutputMessage = TypicalMessage;
+ return STATUS_SUCCESS;
+}
+
+
+
+VOID
+SspContextCopyString(
+ IN PVOID MessageBuffer,
+ OUT PSTRING OutString,
+ IN PSTRING InString,
+ IN OUT PCHAR *Where,
+ IN BOOLEAN Absolute
+ )
+
+/*++
+
+Routine Description:
+
+ This routine copies the InString into the MessageBuffer at Where.
+ It then updates OutString to be a descriptor for the copied string. The
+ descriptor 'address' is an offset from the MessageBuffer unless 'Absolute'
+ is TRUE.
+
+ Where is updated to point to the next available space in the MessageBuffer.
+
+ The caller is responsible for any alignment requirements and for ensuring
+ there is room in the buffer for the string.
+
+Arguments:
+
+ MessageBuffer - Specifies the base address of the buffer being copied into.
+
+ OutString - Returns a descriptor for the copied string. The descriptor
+ is relative to the begining of the buffer.
+
+ InString - Specifies the string to copy.
+
+ Where - On input, points to where the string is to be copied.
+ On output, points to the first byte after the string.
+
+ Absolute - If TRUE, OutString->Buffer will be set to the actual buffer
+ address rather than an offset.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ //
+ // Copy the data to the Buffer.
+ //
+
+ if ( InString->Buffer != NULL ) {
+ RtlCopyMemory( *Where, InString->Buffer, InString->Length );
+ }
+
+ //
+ // Build a descriptor to the newly copied data.
+ //
+
+ OutString->Length = OutString->MaximumLength = InString->Length;
+ if ( Absolute ) {
+ OutString->Buffer = *Where;
+ } else {
+ OutString->Buffer = (PCHAR)(*Where - ((PCHAR)MessageBuffer));
+ }
+
+ //
+ // Update Where to point past the copied data.
+ //
+
+ *Where += InString->Length;
+
+}
+
+
+BOOLEAN
+SspConvertRelativeToAbsolute (
+ IN PVOID MessageBase,
+ IN ULONG MessageSize,
+ IN OUT PSTRING StringToRelocate,
+ IN BOOLEAN AlignToWchar,
+ IN BOOLEAN AllowNullString
+ )
+
+/*++
+
+Routine Description:
+
+ Convert a Relative string desriptor to be absolute.
+ Perform all boudary condition testing.
+
+Arguments:
+
+ MessageBase - a pointer to the base of the buffer that the string
+ is relative to. The MaximumLength field of the descriptor is
+ forced to be the same as the Length field.
+
+ MessageSize - Size of the message buffer (in bytes).
+
+ StringToRelocate - A pointer to the string descriptor to make absolute.
+
+ AlignToWchar - If TRUE the passed in StringToRelocate must describe
+ a buffer that is WCHAR aligned. If not, an error is returned.
+
+ AllowNullString - If TRUE, the passed in StringToRelocate may be
+ a zero length string.
+
+Return Value:
+
+ TRUE - The string descriptor is valid and was properly relocated.
+
+--*/
+
+{
+ ULONG Offset;
+
+ //
+ // If the buffer is allowed to be null,
+ // check that special case.
+ //
+
+ if ( AllowNullString ) {
+ if ( StringToRelocate->Length == 0 ) {
+ StringToRelocate->MaximumLength = StringToRelocate->Length;
+ StringToRelocate->Buffer = NULL;
+ return TRUE;
+ }
+ }
+
+ //
+ // Ensure the string in entirely within the message.
+ //
+
+ Offset = (ULONG) StringToRelocate->Buffer;
+
+ if ( Offset >= MessageSize ||
+ Offset + StringToRelocate->Length > MessageSize ) {
+ return FALSE;
+ }
+
+ //
+ // Ensure the buffer is properly aligned.
+ //
+
+ if ( AlignToWchar ) {
+ if ( !COUNT_IS_ALIGNED( Offset, ALIGN_WCHAR) ||
+ !COUNT_IS_ALIGNED( StringToRelocate->Length, ALIGN_WCHAR) ) {
+ return FALSE;
+ }
+ }
+
+ //
+ // Finally make the pointer absolute.
+ //
+
+ StringToRelocate->Buffer = (((PCHAR)MessageBase) + Offset);
+ StringToRelocate->MaximumLength = StringToRelocate->Length ;
+
+ return TRUE;
+}
+
+
+
+VOID
+SspContextComputeChallenge (
+ OUT CHAR Challenge[MSV1_0_CHALLENGE_LENGTH]
+ )
+
+/*++
+
+Routine Description:
+
+ Creates an encryption key to use as a challenge for a logon.
+
+ *** Although the MSV1_0 authentication package has a function that
+ returns an encryption key, we do not use that function in order
+ to avoid a trip through LPC and into LSA.
+
+ This routine was stolen from the 'GetEncryptionKey' routine in the
+ SMB server.
+
+Arguments:
+
+ Challenge - a pointer to a buffer which receives the challenge
+
+Return Value:
+
+ None
+
+--*/
+
+{
+ union {
+ LARGE_INTEGER time;
+ UCHAR bytes[8];
+ } u;
+ ULONG Seed;
+ ULONG UlongChallenge[2];
+ ULONG Result3;
+ static ULONG EncryptionKeyCount = 0;
+
+ //
+ // Create a pseudo-random 8-byte number by munging the system time
+ // for use as a random number seed.
+ //
+ // Start by getting the system time.
+ //
+
+ ASSERT( MSV1_0_CHALLENGE_LENGTH == 2 * sizeof(ULONG) );
+
+ (VOID) NtQuerySystemTime( &u.time );
+
+ //
+ // To ensure that we don't use the same system time twice, add in the
+ // count of the number of times this routine has been called. Then
+ // increment the counter.
+ //
+ // *** Since we don't use the low byte of the system time (it doesn't
+ // take on enough different values, because of the timer
+ // resolution), we increment the counter by 0x100.
+ //
+ // *** We don't interlock the counter because we don't really care
+ // if it's not 100% accurate.
+ //
+
+ u.time.LowPart += EncryptionKeyCount;
+
+ EncryptionKeyCount += 0x100;
+
+ //
+ // Now use parts of the system time as a seed for the random
+ // number generator.
+ //
+ // *** Because the middle two bytes of the low part of the system
+ // time change most rapidly, we use those in forming the seed.
+ //
+
+ Seed = ((u.bytes[1] + 1) << 0) |
+ ((u.bytes[2] + 0) << 8) |
+ ((u.bytes[2] - 1) << 16) |
+ ((u.bytes[1] + 0) << 24);
+
+ //
+ // Now get two random numbers. RtlRandom does not return negative
+ // numbers, so we pseudo-randomly negate them.
+ //
+ // *** Don't use RtlRandom because it generates non-random numbers for
+ // the first 128 calls or so. 9/10/93
+
+ UlongChallenge[0] = RtlUniform( &Seed );
+ UlongChallenge[1] = RtlUniform( &Seed );
+ Result3 = RtlUniform( &Seed );
+
+ if ( (Result3 & 0x1) != 0 ) {
+ UlongChallenge[0] |= 0x80000000;
+ }
+ if ( (Result3 & 0x2) != 0 ) {
+ UlongChallenge[1] |= 0x80000000;
+ }
+
+ //
+ // Return the challenge.
+ //
+
+ RtlCopyMemory( Challenge, UlongChallenge, MSV1_0_CHALLENGE_LENGTH );
+
+}
+
+
+
+TimeStamp
+SspContextGetTimeStamp(
+ IN PSSP_CONTEXT Context,
+ IN BOOLEAN GetExpirationTime
+ )
+/*++
+
+Routine Description:
+
+ Get the Start time or Expiration time for the specified context.
+
+Arguments:
+
+ Context - Pointer to the context to query
+
+ GetExpirationTime - If TRUE return the expiration time.
+ Otherwise, return the start time for the context.
+
+Return Value:
+
+ Returns the requested time as a local time.
+
+--*/
+
+{
+ NTSTATUS Status;
+ LARGE_INTEGER SystemTime;
+ LARGE_INTEGER LocalTime;
+ TimeStamp LocalTimeStamp;
+
+ //
+ // Get the requested time in NT system time format.
+ //
+
+ SystemTime = Context->StartTime;
+
+ if ( GetExpirationTime ) {
+ LARGE_INTEGER Interval;
+
+ //
+ // If the time is infinite, return that
+ //
+
+ if ( Context->Interval == INFINITE ) {
+ return SspGlobalForever;
+ }
+
+ //
+ // Compute the ending time in NT System Time.
+ //
+
+ Interval.QuadPart = Int32x32To64( (LONG) Context->Interval, 10000 );
+ SystemTime.QuadPart = Interval.QuadPart + SystemTime.QuadPart;
+ }
+
+ //
+ // Convert the time to local time
+ //
+
+ Status = RtlSystemTimeToLocalTime( &SystemTime, &LocalTime );
+
+ if ( !NT_SUCCESS(Status) ) {
+ return SspGlobalForever;
+ }
+
+ LocalTimeStamp.HighPart = LocalTime.HighPart;
+ LocalTimeStamp.LowPart = LocalTime.LowPart;
+
+ return LocalTimeStamp;
+
+}
+
+VOID
+SspContextSetTimeStamp(
+ IN PSSP_CONTEXT Context,
+ IN LARGE_INTEGER ExpirationTime
+ )
+/*++
+
+Routine Description:
+
+ Set the Expiration time for the specified context.
+
+Arguments:
+
+ Context - Pointer to the context to change
+
+ ExpirationTime - Expiration time to set
+
+Return Value:
+
+ NONE.
+
+--*/
+
+{
+
+ LARGE_INTEGER BaseGetTickMagicDivisor = { 0xe219652c, 0xd1b71758 };
+ CCHAR BaseGetTickMagicShiftCount = 13;
+
+ LARGE_INTEGER TimeRemaining;
+ LARGE_INTEGER MillisecondsRemaining;
+
+ //
+ // If the expiration time is infinite,
+ // so is the interval
+ //
+
+ if ( ExpirationTime.HighPart == 0x7FFFFFFF &&
+ ExpirationTime.LowPart == 0xFFFFFFFF ) {
+ Context->Interval = INFINITE;
+
+ //
+ // Handle non-infinite expiration times
+ //
+
+ } else {
+
+ //
+ // Compute the time remaining before the expiration time
+ //
+
+ TimeRemaining.QuadPart = ExpirationTime.QuadPart -
+ Context->StartTime.QuadPart;
+
+ //
+ // If the time has already expired,
+ // indicate so.
+ //
+
+ if ( TimeRemaining.QuadPart < 0 ) {
+
+ Context->Interval = 0;
+
+ //
+ // If the time hasn't expired, compute the number of milliseconds
+ // remaining.
+ //
+
+ } else {
+
+ MillisecondsRemaining = RtlExtendedMagicDivide(
+ TimeRemaining,
+ BaseGetTickMagicDivisor,
+ BaseGetTickMagicShiftCount );
+
+ if ( MillisecondsRemaining.HighPart == 0 &&
+ MillisecondsRemaining.LowPart < 0x7fffffff ) {
+
+ Context->Interval = MillisecondsRemaining.LowPart;
+
+ } else {
+
+ Context->Interval = INFINITE;
+ }
+ }
+
+ }
+
+}
+
+
+
+SECURITY_STATUS
+SsprHandleFirstCall(
+ IN PSSP_CLIENT_CONNECTION ClientConnection,
+ IN PCredHandle CredentialHandle,
+ IN OUT PCtxtHandle ContextHandle,
+ IN ULONG ContextReqFlags,
+ IN ULONG InputTokenSize,
+ IN PVOID InputToken,
+ IN OUT PULONG OutputTokenSize,
+ OUT PVOID OutputToken,
+ OUT PULONG ContextAttributes,
+ OUT PTimeStamp ExpirationTime,
+ OUT PUCHAR SessionKey,
+ OUT PULONG NegotiateFlags
+ )
+
+/*++
+
+Routine Description:
+
+ Handle the First Call part of InitializeSecurityContext.
+
+Arguments:
+
+ All arguments same as for InitializeSecurityContext
+
+Return Value:
+
+ STATUS_SUCCESS -- All OK
+ SEC_I_CALLBACK_NEEDED -- Caller should call again later
+
+ SEC_E_INVALID_HANDLE -- Credential/Context Handle is invalid
+ SEC_E_BUFFER_TOO_SMALL -- Buffer for output token isn't big enough
+ SEC_E_INSUFFICIENT_MEMORY -- Not enough memory
+
+--*/
+
+{
+ SECURITY_STATUS SecStatus;
+ PSSP_CONTEXT Context = NULL;
+ PSSP_CREDENTIAL Credential = NULL;
+
+ PNEGOTIATE_MESSAGE NegotiateMessage = NULL;
+ ULONG NegotiateMessageSize;
+ PCHAR Where;
+
+ //
+ // Initialization
+ //
+
+ *ContextAttributes = 0;
+ *NegotiateFlags = 0;
+
+ //
+ // Get a pointer to the credential
+ //
+
+ Credential = SspCredentialReferenceCredential(
+ CredentialHandle,
+ ClientConnection,
+ FALSE,
+ FALSE );
+
+ if ( Credential == NULL ) {
+ SspPrint(( SSP_API,
+ "SspHandleFirstCall: invalid credential handle.\n" ));
+ SecStatus = SEC_E_INVALID_HANDLE;
+ goto Cleanup;
+ }
+
+ if ( (Credential->CredentialUseFlags & SECPKG_CRED_OUTBOUND) == 0 ) {
+ SspPrint(( SSP_API, "SsprHandleFirstCall: invalid credential use.\n" ));
+ SecStatus = SEC_E_INVALID_CREDENTIAL_USE;
+ goto Cleanup;
+ }
+
+
+ //
+ // Allocate a new context
+ //
+
+ Context = SspContextAllocateContext( ClientConnection );
+
+ if ( Context == NULL ) {
+ SecStatus = SEC_E_INSUFFICIENT_MEMORY;
+ goto Cleanup;
+ }
+
+ //
+ // Build a handle to the newly created context.
+ //
+
+ ContextHandle->dwUpper = (DWORD) Context;
+ ContextHandle->dwLower = SspCommonSecHandleValue;
+
+
+ //
+ // We don't support any options.
+ //
+ // Complain about those that require we do something.
+ //
+
+ if ( (ContextReqFlags & (ISC_REQ_ALLOCATE_MEMORY |
+ ISC_REQ_PROMPT_FOR_CREDS |
+ ISC_REQ_USE_SUPPLIED_CREDS )) != 0 ) {
+
+ SspPrint(( SSP_API,
+ "SsprHandleFirstCall: invalid ContextReqFlags 0x%lx.\n",
+ ContextReqFlags ));
+ SecStatus = SEC_E_INVALID_CONTEXT_REQ;
+ goto Cleanup;
+ }
+
+ //
+ // Capture the default credentials from the credential structure.
+ //
+ if ( Credential->DomainName.Length != 0 ) {
+ SecStatus = SspDuplicateUnicodeString(
+ &Context->DomainName,
+ &Credential->DomainName
+ );
+ if (!NT_SUCCESS(SecStatus)) {
+ goto Cleanup;
+ }
+ }
+ if ( Credential->UserName.Length != 0 ) {
+ SecStatus = SspDuplicateUnicodeString(
+ &Context->UserName,
+ &Credential->UserName
+ );
+ if (!NT_SUCCESS(SecStatus)) {
+ goto Cleanup;
+ }
+ }
+
+ SecStatus = SspCredentialGetPassword(
+ Credential,
+ &Context->Password
+ );
+
+ if (!NT_SUCCESS(SecStatus)) {
+ goto Cleanup;
+ }
+
+
+
+ //
+ // Compute the negotiate flags
+ //
+
+ Context->NegotiateFlags = NTLMSSP_NEGOTIATE_UNICODE |
+ NTLMSSP_NEGOTIATE_OEM |
+ NTLMSSP_NEGOTIATE_NTLM |
+ NTLMSSP_NEGOTIATE_ALWAYS_SIGN;
+
+
+ //
+ // If the caller specified SEQUENCE_DETECT or REPLAY_DETECT,
+ // that means they want to use the MakeSignature/VerifySignature
+ // calls. Add this to the negotiate.
+ //
+
+ if ((ContextReqFlags & ISC_REQ_SEQUENCE_DETECT) ||
+ (ContextReqFlags & ISC_REQ_REPLAY_DETECT)) {
+ Context->NegotiateFlags |= NTLMSSP_NEGOTIATE_SIGN |
+#ifndef EXPORT_BUILD
+ NTLMSSP_NEGOTIATE_STRONG_CRYPT |
+#endif // EXPORT_BUILD
+ NTLMSSP_NEGOTIATE_LM_KEY;
+
+ *ContextAttributes |= ISC_REQ_SEQUENCE_DETECT;
+ Context->ContextFlags |= ISC_REQ_SEQUENCE_DETECT;
+ }
+
+
+ if (ContextReqFlags & ISC_REQ_CONFIDENTIALITY) {
+ if (SspGlobalEncryptionEnabled) {
+ Context->NegotiateFlags |= NTLMSSP_NEGOTIATE_SEAL |
+#ifndef EXPORT_BUILD
+ NTLMSSP_NEGOTIATE_STRONG_CRYPT |
+#endif // EXPORT_BUILD
+
+ NTLMSSP_NEGOTIATE_LM_KEY;
+
+ *ContextAttributes |= ISC_REQ_CONFIDENTIALITY;
+ Context->ContextFlags |= ISC_REQ_CONFIDENTIALITY;
+ } else {
+ SecStatus = SEC_E_UNSUPPORTED_FUNCTION;
+ goto Cleanup;
+ }
+ }
+
+ //
+ // Check if the caller wants identify level
+ //
+
+ if ((ContextReqFlags & ISC_REQ_IDENTIFY)!= 0) {
+ Context->NegotiateFlags |= NTLMSSP_NEGOTIATE_IDENTIFY;
+ *ContextAttributes |= ISC_RET_IDENTIFY;
+ Context->ContextFlags |= ISC_REQ_IDENTIFY;
+ Context->NegotiateFlags |= NTLMSSP_NEGOTIATE_IDENTIFY;
+ }
+
+
+ IF_DEBUG( USE_OEM ) {
+ Context->NegotiateFlags &= ~NTLMSSP_NEGOTIATE_UNICODE;
+ }
+
+ //
+ // For connection oriented security, we send a negotiate message to
+ // the server. For datagram, we get back the server's
+ // capabilities in the challenge message.
+ //
+
+
+
+
+ if ((ContextReqFlags & ISC_REQ_DATAGRAM) == 0) {
+
+
+ //
+ // Allocate a Negotiate message
+ //
+
+ NegotiateMessageSize = sizeof(*NegotiateMessage) +
+ SspGlobalOemComputerNameString.Length +
+ SspGlobalOemPrimaryDomainNameString.Length;
+
+ if ( NegotiateMessageSize > *OutputTokenSize ) {
+ SecStatus = SEC_E_BUFFER_TOO_SMALL;
+ goto Cleanup;
+ }
+
+ NegotiateMessage = LocalAlloc( LMEM_ZEROINIT, NegotiateMessageSize );
+
+ if ( NegotiateMessage == NULL ) {
+ SecStatus = SEC_E_INSUFFICIENT_MEMORY;
+ goto Cleanup;
+ }
+
+
+ //
+ // If this is the first call,
+ // build a Negotiate message.
+ //
+
+ strcpy( NegotiateMessage->Signature, NTLMSSP_SIGNATURE );
+ NegotiateMessage->MessageType = NtLmNegotiate;
+ NegotiateMessage->NegotiateFlags = Context->NegotiateFlags;
+
+ IF_DEBUG( REQUEST_TARGET ) {
+ NegotiateMessage->NegotiateFlags |= NTLMSSP_REQUEST_TARGET;
+ }
+
+
+ //
+ // Copy the DomainName and ComputerName into the negotiate message
+ // so the other side can determine if this is a call from the local system.
+ //
+ // Pass the names in the OEM character set since the character set hasn't
+ // been negotiated yet.
+ //
+ // Skip passing the workstation name if credentials were specified. This
+ // Ensures the other side doesn't fall into the case that this is the local
+ // system. We wan't to ensure the new credentials are authenticated.
+ //
+
+ Where = (PCHAR)(NegotiateMessage+1);
+
+ if ( Credential->DomainName.Length == 0 &&
+ Credential->UserName.Length == 0 &&
+ Credential->Password.Buffer == NULL ) {
+
+ SspContextCopyString( NegotiateMessage,
+ &NegotiateMessage->OemWorkstationName,
+ &SspGlobalOemComputerNameString,
+ &Where,
+ FALSE ); // Pointers are relative
+
+ NegotiateMessage->NegotiateFlags |= NTLMSSP_NEGOTIATE_OEM_WORKSTATION_SUPPLIED;
+ }
+
+ SspContextCopyString( NegotiateMessage,
+ &NegotiateMessage->OemDomainName,
+ &SspGlobalOemPrimaryDomainNameString,
+ &Where,
+ FALSE ); // Pointers are relative
+
+ NegotiateMessage->NegotiateFlags |= NTLMSSP_NEGOTIATE_OEM_DOMAIN_SUPPLIED;
+
+
+
+
+ SecStatus = SspLpcCopyToClientBuffer(
+ ClientConnection,
+ NegotiateMessageSize,
+ OutputToken,
+ NegotiateMessage );
+
+ if ( !NT_SUCCESS(SecStatus) ) {
+ goto Cleanup;
+ }
+
+ *OutputTokenSize = NegotiateMessageSize;
+
+
+ }
+
+ //
+ // Save a reference to the credential in the context.
+ //
+
+ Context->Credential = Credential;
+ Credential = NULL;
+
+ //
+ // Check for a caller requesting datagram security.
+ //
+
+ if ((ContextReqFlags & ISC_REQ_DATAGRAM) != 0 ) {
+
+ //
+ // Turn off strong crypt, because we can't negotiate it.
+ //
+#ifndef EXPORT_BUILD
+ Context->NegotiateFlags &= ~NTLMSSP_NEGOTIATE_STRONG_CRYPT;
+#endif // EXPORT_BUILD
+
+ Context->NegotiateFlags |= NTLMSSP_NEGOTIATE_DATAGRAM;
+
+ Context->ContextFlags |= ISC_REQ_DATAGRAM;
+ *ContextAttributes |= ISC_REQ_DATAGRAM;
+ *NegotiateFlags = Context->NegotiateFlags;
+
+ }
+
+ //
+ // If we are negotiating datagram or are building a domestic
+ // version, create a good session key
+ //
+
+#ifdef EXPORT_BUILD
+ if ((Context->NegotiateFlags & NTLMSSP_NEGOTIATE_DATAGRAM) != 0)
+#endif
+ {
+ RtlZeroMemory(
+ Context->SessionKey,
+ MSV1_0_USER_SESSION_KEY_LENGTH
+ );
+
+ //
+ // Generate a session key for this context if sign or seal was
+ // requested.
+ //
+
+ if (Context->NegotiateFlags & (NTLMSSP_NEGOTIATE_SIGN |
+ NTLMSSP_NEGOTIATE_SEAL) != 0) {
+
+ SspGenerateRandomBits(
+ Context->SessionKey,
+ MSV1_0_USER_SESSION_KEY_LENGTH
+ );
+ }
+ RtlCopyMemory(
+ SessionKey,
+ Context->SessionKey,
+ MSV1_0_USER_SESSION_KEY_LENGTH
+ );
+ }
+
+ //
+ // Return output parameters to the caller.
+ //
+
+ *ExpirationTime = SspContextGetTimeStamp( Context, TRUE );
+
+ SecStatus = SEC_I_CALLBACK_NEEDED;
+ Context->State = NegotiateSentState;
+
+ //
+ // Free and locally used resources.
+ //
+Cleanup:
+
+ if ( Context != NULL ) {
+
+ //
+ // If we failed,
+ // deallocate the context we allocated above.
+ //
+ // Delinking is a side effect of referencing, so do that.
+ //
+
+ if ( !NT_SUCCESS(SecStatus) ) {
+ PSSP_CONTEXT LocalContext;
+ LocalContext = SspContextReferenceContext( ContextHandle,
+ ClientConnection,
+ TRUE );
+
+ ASSERT( LocalContext != NULL );
+ if ( LocalContext != NULL ) {
+ SspContextDereferenceContext( LocalContext );
+ }
+ }
+
+ // Always dereference it.
+
+ SspContextDereferenceContext( Context );
+ }
+
+ if ( NegotiateMessage != NULL ) {
+ (VOID) LocalFree( NegotiateMessage );
+ }
+
+ if ( Credential != NULL ) {
+ SspCredentialDereferenceCredential( Credential );
+ }
+
+ return SecStatus;
+ UNREFERENCED_PARAMETER( InputToken );
+ UNREFERENCED_PARAMETER( InputTokenSize );
+}
+
+
+
+SECURITY_STATUS
+SsprHandleNegotiateMessage(
+ IN PSSP_CLIENT_CONNECTION ClientConnection,
+ IN PCredHandle CredentialHandle,
+ IN OUT PCtxtHandle ContextHandle,
+ IN ULONG ContextReqFlags,
+ IN ULONG InputTokenSize,
+ IN PVOID InputToken,
+ IN OUT PULONG OutputTokenSize,
+ OUT PVOID OutputToken,
+ OUT PULONG ContextAttributes,
+ OUT PTimeStamp ExpirationTime
+ )
+
+/*++
+
+Routine Description:
+
+ Handle the Negotiate message part of AcceptSecurityContext.
+
+Arguments:
+
+ All arguments same as for AcceptSecurityContext
+
+Return Value:
+
+ STATUS_SUCCESS - Message handled
+ SEC_I_CALLBACK_NEEDED -- Caller should call again later
+
+ SEC_E_INVALID_TOKEN -- Token improperly formatted
+ SEC_E_INVALID_HANDLE -- Credential/Context Handle is invalid
+ SEC_E_BUFFER_TOO_SMALL -- Buffer for output token isn't big enough
+ SEC_E_INSUFFICIENT_MEMORY -- Not enough memory
+
+--*/
+
+{
+ SECURITY_STATUS SecStatus;
+ PSSP_CONTEXT Context = NULL;
+ PSSP_CREDENTIAL Credential = NULL;
+ STRING TargetName;
+ ULONG TargetFlags = 0;
+
+ PNEGOTIATE_MESSAGE NegotiateMessage = NULL;
+
+ PCHALLENGE_MESSAGE ChallengeMessage = NULL;
+ ULONG ChallengeMessageSize;
+ PCHAR Where;
+
+ //
+ // Initialization
+ //
+
+ *ContextAttributes = 0;
+ RtlInitString( &TargetName, NULL );
+
+ //
+ // Make sure we didn't get any silly context requirements.
+ //
+
+ if ( (ContextReqFlags & ASC_REQ_ALLOCATE_MEMORY) != 0 ) {
+
+ SspPrint(( SSP_API,
+ "SsprHandleNegotiateMessage: invalid ContextReqFlags 0x%lx.\n",
+ ContextReqFlags ));
+ SecStatus = SEC_E_INVALID_CONTEXT_REQ;
+ goto Cleanup;
+ }
+
+ //
+ // Get a pointer to the credential
+ //
+
+ Credential = SspCredentialReferenceCredential(
+ CredentialHandle,
+ ClientConnection,
+ FALSE,
+ FALSE );
+
+ if ( Credential == NULL ) {
+
+ //
+ // If the credential is from the security.dll, ignore it.
+ //
+
+ if ( (CredentialHandle->dwLower != SEC_HANDLE_SECURITY) ||
+ (SspCommonSecHandleValue != SEC_HANDLE_NTLMSSPS) ) {
+
+ SspPrint(( SSP_API,
+ "SsprHandleNegotiateMessage: invalid credential handle.\n" ));
+ SecStatus = SEC_E_INVALID_HANDLE;
+ goto Cleanup;
+ }
+ } else {
+ if ( (Credential->CredentialUseFlags & SECPKG_CRED_INBOUND) == 0 ) {
+ SspPrint(( SSP_API,
+ "SsprHandleNegotiateMessage: invalid credential use.\n" ));
+ SecStatus = SEC_E_INVALID_CREDENTIAL_USE;
+ goto Cleanup;
+ }
+ }
+
+
+ //
+ // Allocate a new context
+ //
+
+ Context = SspContextAllocateContext( ClientConnection );
+
+ if ( Context == NULL ) {
+ SecStatus = SEC_E_INSUFFICIENT_MEMORY;
+ goto Cleanup;
+ }
+
+ //
+ // Build a handle to the newly created context.
+ //
+
+ ContextHandle->dwUpper = (DWORD) Context;
+ ContextHandle->dwLower = SspCommonSecHandleValue;
+
+
+
+
+ if ( ContextReqFlags & ISC_REQ_REPLAY_DETECT ||
+ ContextReqFlags & ISC_REQ_SEQUENCE_DETECT ) {
+
+ Context->ContextFlags = ISC_REQ_SEQUENCE_DETECT;
+ }
+
+ if ( ContextReqFlags & ISC_REQ_CONFIDENTIALITY ) {
+
+ if (SspGlobalEncryptionEnabled) {
+ Context->ContextFlags = ISC_REQ_CONFIDENTIALITY;
+ } else {
+ SecStatus = SEC_E_UNSUPPORTED_FUNCTION;
+ goto Cleanup;
+ }
+ }
+
+#ifdef notdef // ?? RPC sends me 0xa03 here
+ if ( ContextReqFlags & ~ISC_REQ_REPLAY_DETECT &
+ ~ISC_REQ_SEQUENCE_DETECT != 0 ) {
+
+ SspPrint(( SSP_API,
+ "SsprHandleNegotiateMessage: invalid ContextReqFlags 0x%lx.\n",
+ ContextReqFlags ));
+ SecStatus = SEC_E_INVALID_CONTEXT_REQ;
+ goto Cleanup;
+ }
+#else // notdef
+ UNREFERENCED_PARAMETER( ContextReqFlags );
+#endif // notdef
+
+
+
+
+
+
+ //
+ // Get the NegotiateMessage. If we are re-establishing a datagram
+ // context then there may not be one.
+ //
+
+ if ( InputTokenSize >= sizeof(OLD_NEGOTIATE_MESSAGE) ) {
+
+ SecStatus = SspContextGetMessage( ClientConnection,
+ InputToken,
+ InputTokenSize,
+ NtLmNegotiate,
+ &NegotiateMessage );
+
+ if ( !NT_SUCCESS(SecStatus) ) {
+ SspPrint(( SSP_API,
+ "SsprHandleNegotiateMessage: "
+ "NegotiateMessage GetMessage returns 0x%lx\n",
+ SecStatus ));
+ goto Cleanup;
+ }
+
+
+
+ //
+ // Compute the TargetName to return in the ChallengeMessage.
+ //
+
+ if ( NegotiateMessage->NegotiateFlags & NTLMSSP_REQUEST_TARGET ) {
+ // Ensure SspGlobalTargetName is up to date.
+ SspGetPrimaryDomainNameAndTargetName();
+ if ( NegotiateMessage->NegotiateFlags & NTLMSSP_NEGOTIATE_UNICODE ) {
+ TargetName = *((PSTRING)&SspGlobalTargetName);
+ } else {
+ TargetName = SspGlobalOemTargetName;
+ }
+ TargetFlags = NTLMSSP_REQUEST_TARGET | SspGlobalTargetFlags;
+
+ } else {
+ TargetFlags = 0;
+ }
+
+ //
+ // Allocate a Challenge message
+ //
+
+ ChallengeMessageSize = sizeof(*ChallengeMessage) + TargetName.Length;
+
+ if ( ChallengeMessageSize > *OutputTokenSize ) {
+ SecStatus = SEC_E_BUFFER_TOO_SMALL;
+ goto Cleanup;
+ }
+
+ ChallengeMessage = LocalAlloc( LMEM_ZEROINIT, ChallengeMessageSize );
+
+ if ( ChallengeMessage == NULL ) {
+ SecStatus = SEC_E_INSUFFICIENT_MEMORY;
+ goto Cleanup;
+ }
+
+ ChallengeMessage->NegotiateFlags = 0;
+
+
+ //
+ // Check that both sides can use the same authentication model. For
+ // compatibility with beta 1 and 2 (builds 612 and 683), no requested
+ // authentication type is assumed to be NTLM. If NetWare is explicitly
+ // asked for, it is assumed that NTLM would have been also, so if it
+ // wasn't, return an error.
+ //
+
+
+ if ( (NegotiateMessage->NegotiateFlags & NTLMSSP_NEGOTIATE_NETWARE) &&
+ !(NegotiateMessage->NegotiateFlags & NTLMSSP_NEGOTIATE_NTLM ) ) {
+ SecStatus = SEC_E_UNSUPPORTED_FUNCTION;
+ SspPrint(( SSP_API,
+ "SsprHandleNegotiateMessage: "
+ "NegotiateMessage asked for Netware only.\n" ));
+ goto Cleanup;
+ } else {
+ ChallengeMessage->NegotiateFlags |= NTLMSSP_NEGOTIATE_NTLM;
+ }
+
+ //
+ // Check if the caller requested that we use the LM session key instead
+ // of the NT session key.
+ //
+
+ if ( NegotiateMessage->NegotiateFlags & NTLMSSP_NEGOTIATE_LM_KEY ) {
+ ChallengeMessage->NegotiateFlags |= NTLMSSP_NEGOTIATE_LM_KEY;
+ }
+
+#ifndef EXPORT_BUILD
+ if ( NegotiateMessage->NegotiateFlags & NTLMSSP_NEGOTIATE_STRONG_CRYPT ) {
+ ChallengeMessage->NegotiateFlags |= NTLMSSP_NEGOTIATE_STRONG_CRYPT;
+ }
+
+#endif
+ //
+ // If the client wants to always sign messages, so be it.
+ //
+
+ if (NegotiateMessage->NegotiateFlags & NTLMSSP_NEGOTIATE_ALWAYS_SIGN ) {
+ ChallengeMessage->NegotiateFlags |= NTLMSSP_NEGOTIATE_ALWAYS_SIGN;
+ }
+
+ //
+ // If the caller wants identify level, so be it.
+ //
+
+ if (NegotiateMessage->NegotiateFlags & NTLMSSP_NEGOTIATE_IDENTIFY ) {
+ ChallengeMessage->NegotiateFlags |= NTLMSSP_NEGOTIATE_IDENTIFY;
+ }
+
+ //
+ // Determine if the caller wants OEM or UNICODE
+ //
+ // Prefer UNICODE if caller allows both.
+ //
+
+ if ( NegotiateMessage->NegotiateFlags & NTLMSSP_NEGOTIATE_UNICODE ) {
+ ChallengeMessage->NegotiateFlags |= NTLMSSP_NEGOTIATE_UNICODE;
+ } else if ( NegotiateMessage->NegotiateFlags & NTLMSSP_NEGOTIATE_OEM ){
+ ChallengeMessage->NegotiateFlags |= NTLMSSP_NEGOTIATE_OEM;
+ } else {
+ SecStatus = SEC_E_INVALID_TOKEN;
+ SspPrint(( SSP_API,
+ "SsprHandleNegotiateMessage: "
+ "NegotiateMessage bad NegotiateFlags 0x%lx\n",
+ NegotiateMessage->NegotiateFlags ));
+ goto Cleanup;
+ }
+
+ //
+ // Client wants Sign capability, OK.
+ //
+ if (NegotiateMessage->NegotiateFlags & NTLMSSP_NEGOTIATE_SIGN) {
+ ChallengeMessage->NegotiateFlags |= NTLMSSP_NEGOTIATE_SIGN;
+ }
+
+ //
+ // Client wants Seal, OK.
+ //
+
+ if (NegotiateMessage->NegotiateFlags & NTLMSSP_NEGOTIATE_SEAL)
+ {
+ ChallengeMessage->NegotiateFlags |= NTLMSSP_NEGOTIATE_SEAL;
+ }
+
+ //
+ // If the client supplied the Domain Name and User Name,
+ // and did not request datagram, see if the client is running
+ // on this local machine.
+ //
+ IF_DEBUG(NO_LOCAL){
+ }
+ else
+ {
+ if ( ( (NegotiateMessage->NegotiateFlags & NTLMSSP_NEGOTIATE_DATAGRAM) == 0) &&
+ ( (NegotiateMessage->NegotiateFlags &
+ (NTLMSSP_NEGOTIATE_OEM_DOMAIN_SUPPLIED|NTLMSSP_NEGOTIATE_OEM_WORKSTATION_SUPPLIED)) ==
+ (NTLMSSP_NEGOTIATE_OEM_DOMAIN_SUPPLIED|NTLMSSP_NEGOTIATE_OEM_WORKSTATION_SUPPLIED) ) ) {
+
+ //
+ // The client must pass the new negotiate message if they pass
+ // these flags
+ //
+
+ if (InputTokenSize < sizeof(NEGOTIATE_MESSAGE)) {
+ SecStatus = SEC_E_INVALID_TOKEN;
+ goto Cleanup;
+ }
+ //
+ // Convert the names to absolute references so we can compare them
+ //
+ if ( !SspConvertRelativeToAbsolute( NegotiateMessage,
+ InputTokenSize,
+ &NegotiateMessage->OemDomainName,
+ FALSE, // No special alignment
+ FALSE ) ) { // NULL not OK
+ SecStatus = SEC_E_INVALID_TOKEN;
+ goto Cleanup;
+ }
+
+ if ( !SspConvertRelativeToAbsolute( NegotiateMessage,
+ InputTokenSize,
+ &NegotiateMessage->OemWorkstationName,
+ FALSE, // No special alignment
+ FALSE ) ) { // NULL not OK
+ SecStatus = SEC_E_INVALID_TOKEN;
+ goto Cleanup;
+ }
+
+ //
+ // If both strings match,
+ // this is a local call.
+ //
+ // The strings have already been uppercased.
+ //
+
+ if ( RtlEqualString( &NegotiateMessage->OemWorkstationName,
+ &SspGlobalOemComputerNameString,
+ FALSE ) &&
+ RtlEqualString( &NegotiateMessage->OemDomainName,
+ &SspGlobalOemPrimaryDomainNameString,
+ FALSE ) ) {
+
+ //
+ // If this call is being handled by the security.dll directly
+ // then force it to call the NTLMSSP service
+ //
+
+ if ( SspCommonSecHandleValue != SEC_HANDLE_NTLMSSPS ) {
+ SecStatus = SEC_I_CALL_NTLMSSP_SERVICE;
+ goto Cleanup;
+ }
+ ChallengeMessage->NegotiateFlags |= NTLMSSP_NEGOTIATE_LOCAL_CALL;
+ ChallengeMessage->ServerContextHandleLower = ContextHandle->dwLower;
+ ChallengeMessage->ServerContextHandleUpper = ContextHandle->dwUpper;
+
+ }
+ }
+ }
+ //
+ // Check if datagram is being negotiated
+ //
+
+ if ( (NegotiateMessage->NegotiateFlags & NTLMSSP_NEGOTIATE_DATAGRAM) ==
+ NTLMSSP_NEGOTIATE_DATAGRAM) {
+ ChallengeMessage->NegotiateFlags |= NTLMSSP_NEGOTIATE_DATAGRAM;
+ }
+
+
+ } else {
+
+ //
+ // No negotiate message. We need to check if the caller is asking
+ // for datagram.
+ //
+
+ if ((ContextReqFlags & ISC_REQ_DATAGRAM) == 0 ) {
+ SspPrint(( SSP_API,
+ "SsprHandleNegotiateMessage: "
+ "NegotiateMessage size wrong %ld\n",
+ InputTokenSize ));
+ SecStatus = SEC_E_INVALID_TOKEN;
+ goto Cleanup;
+
+ }
+
+ //
+ // Allocate a Challenge message
+ //
+
+ ChallengeMessageSize = sizeof(*ChallengeMessage);
+
+ if ( ChallengeMessageSize > *OutputTokenSize ) {
+ SecStatus = SEC_E_BUFFER_TOO_SMALL;
+ goto Cleanup;
+ }
+
+ ChallengeMessage = LocalAlloc( LMEM_ZEROINIT, ChallengeMessageSize );
+
+ if ( ChallengeMessage == NULL ) {
+ SecStatus = SEC_E_INSUFFICIENT_MEMORY;
+ goto Cleanup;
+ }
+
+
+ //
+ // Record in the context that we are doing datagram. We will tell
+ // the client everything we can negotiate and let it decide what
+ // to negotiate. Note that we don't negotate strong crypt -
+ // with datagram we start encrypting data before we negotiate
+ // so we can't use it.
+ //
+
+ ChallengeMessage->NegotiateFlags = NTLMSSP_NEGOTIATE_DATAGRAM |
+ NTLMSSP_NEGOTIATE_UNICODE |
+ NTLMSSP_NEGOTIATE_OEM |
+ NTLMSSP_NEGOTIATE_SIGN |
+ NTLMSSP_NEGOTIATE_LM_KEY |
+ NTLMSSP_NEGOTIATE_NTLM |
+ NTLMSSP_NEGOTIATE_ALWAYS_SIGN |
+ NTLMSSP_NEGOTIATE_IDENTIFY;
+
+ if (SspGlobalEncryptionEnabled) {
+ ChallengeMessage->NegotiateFlags |= NTLMSSP_NEGOTIATE_SEAL;
+ }
+
+
+
+ }
+
+ //
+ // Build the Challenge Message
+ //
+
+ strcpy( ChallengeMessage->Signature, NTLMSSP_SIGNATURE );
+ ChallengeMessage->MessageType = NtLmChallenge;
+ SspContextComputeChallenge( ChallengeMessage->Challenge );
+
+ Where = (PCHAR)(ChallengeMessage+1);
+
+ SspContextCopyString( ChallengeMessage,
+ &ChallengeMessage->TargetName,
+ &TargetName,
+ &Where,
+ FALSE ); // Pointers are relative
+
+ ChallengeMessage->NegotiateFlags |= TargetFlags;
+
+
+ SecStatus = SspLpcCopyToClientBuffer(
+ ClientConnection,
+ ChallengeMessageSize,
+ OutputToken,
+ ChallengeMessage );
+
+ if ( !NT_SUCCESS(SecStatus) ) {
+ goto Cleanup;
+ }
+
+ *OutputTokenSize = ChallengeMessageSize;
+
+ //
+ // Save the Challenge and Negotiate Flags in the Context so it
+ // is available when the authenticate message comes in.
+ //
+
+ RtlCopyMemory( Context->Challenge,
+ ChallengeMessage->Challenge,
+ sizeof( Context->Challenge ) );
+
+ Context->NegotiateFlags = ChallengeMessage->NegotiateFlags;
+
+
+
+
+ //
+ // Return output parameters to the caller.
+ //
+ *ExpirationTime = SspContextGetTimeStamp( Context, TRUE );
+ Context->State = ChallengeSentState;
+
+ SecStatus = SEC_I_CALLBACK_NEEDED;
+
+ //
+ // Free and locally used resources.
+ //
+Cleanup:
+
+ if ( Context != NULL ) {
+
+ //
+ // If we failed,
+ // deallocate the context we allocated above.
+ //
+ // Delinking is a side effect of referencing, so do that.
+ //
+
+ if ( !NT_SUCCESS(SecStatus) ) {
+ PSSP_CONTEXT LocalContext;
+ LocalContext = SspContextReferenceContext( ContextHandle,
+ ClientConnection,
+ TRUE );
+
+ ASSERT( LocalContext != NULL );
+ if ( LocalContext != NULL ) {
+ SspContextDereferenceContext( LocalContext );
+ }
+ }
+
+ // Always dereference it.
+
+ SspContextDereferenceContext( Context );
+ }
+
+ if ( NegotiateMessage != NULL ) {
+ (VOID) LocalFree( NegotiateMessage );
+ }
+
+ if ( ChallengeMessage != NULL ) {
+ (VOID) LocalFree( ChallengeMessage );
+ }
+
+ if ( Credential != NULL ) {
+ SspCredentialDereferenceCredential( Credential );
+ }
+
+ return SecStatus;
+}
+
+
+
+SECURITY_STATUS
+SsprHandleChallengeMessage(
+ IN PSSP_CLIENT_CONNECTION ClientConnection,
+ IN PCredHandle CredentialHandle,
+ IN OUT PCtxtHandle ContextHandle,
+ IN HANDLE ClientTokenHandle,
+ IN PLUID LogonId,
+ IN ULONG ContextReqFlags,
+ IN LPWSTR ContextDomainName,
+ IN ULONG DomainNameSize,
+ IN LPWSTR ContextUserName,
+ IN ULONG UserNameSize,
+ IN LPWSTR ContextPassword,
+ IN ULONG PasswordSize,
+ IN ULONG InputTokenSize,
+ IN PVOID InputToken,
+ IN OUT PULONG OutputTokenSize,
+ OUT PVOID OutputToken,
+ OUT PULONG ContextAttributes,
+ OUT PTimeStamp ExpirationTime,
+ OUT PUCHAR SessionKey,
+ OUT PULONG NegotiateFlags,
+ OUT LPWSTR ContextNames
+ )
+
+/*++
+
+Routine Description:
+
+ Handle the Challenge message part of InitializeSecurityContext.
+
+Arguments:
+
+ ClientTokenHandle - Optionally passes in a handle to an impersonation
+ token of the client. This impersonation token will be passed directly
+ to the server if the server is running on the same machine. In that
+ case, this routine will NULL the ClientTokenHandle letting the caller
+ know that it need not close the handle. The server will close the handle
+ when it's done with it.
+
+ LogonId -- LogonId of the calling process.
+
+ DomainName,UserName,Password - Passed in credentials to be used for this
+ context.
+
+ DomainNameSize,userNameSize,PasswordSize - length in characters of the
+ credentials to be used for this context.
+
+ SessionKey - Session key to use for this context
+
+ NegotiateFlags - Flags negotiated for this context
+
+ ContextNames - Receives the domainname\username used for this
+ context if they were specified separately.
+
+ All other arguments same as for InitializeSecurityContext
+
+Return Value:
+
+ STATUS_SUCCESS - Message handled
+ SEC_I_CALLBACK_NEEDED -- Caller should call again later
+
+ SEC_E_INVALID_TOKEN -- Token improperly formatted
+ SEC_E_INVALID_HANDLE -- Credential/Context Handle is invalid
+ SEC_E_BUFFER_TOO_SMALL -- Buffer for output token isn't big enough
+ SEC_E_NO_CREDENTIALS -- There are no credentials for this client
+ SEC_E_INSUFFICIENT_MEMORY -- Not enough memory
+
+--*/
+
+{
+ SECURITY_STATUS SecStatus;
+ PSSP_CONTEXT Context = NULL;
+ PCHALLENGE_MESSAGE ChallengeMessage = NULL;
+ PAUTHENTICATE_MESSAGE AuthenticateMessage = NULL;
+ PMSV1_0_GETCHALLENRESP_RESPONSE ChallengeResponseMessage = NULL;
+ STRING UserName;
+ STRING DomainName;
+ STRING Workstation;
+ STRING LmChallengeResponse;
+ STRING NtChallengeResponse;
+ STRING DatagramSessionKey;
+ BOOLEAN DoUnicode = TRUE;
+ WCHAR Name[UNLEN+DNLEN+2];
+
+ NTSTATUS Status;
+ NTSTATUS ProtocolStatus;
+
+ LPBYTE GetChallengeResponseBuffer[
+ sizeof(MSV1_0_GETCHALLENRESP_REQUEST) +
+ (PWLEN+1) * sizeof(WCHAR) ];
+ PMSV1_0_GETCHALLENRESP_REQUEST GetChallengeResponse;
+ ULONG GetChallengeResponseSize;
+
+ ULONG ChallengeResponseSize;
+ ULONG AuthenticateMessageSize;
+ PCHAR Where;
+ UCHAR LocalSessionKey[MSV1_0_USER_SESSION_KEY_LENGTH];
+ UCHAR DatagramKey[MSV1_0_USER_SESSION_KEY_LENGTH];
+ PLUID ClientLogonId;
+
+ //
+ // Initialization
+ //
+
+ *ContextAttributes = 0;
+ UserName.Buffer = NULL;
+ DomainName.Buffer = NULL;
+ GetChallengeResponse =
+ (PMSV1_0_GETCHALLENRESP_REQUEST) GetChallengeResponseBuffer;
+
+
+ //
+ // Find the currently existing context.
+ //
+
+ Context = SspContextReferenceContext( ContextHandle,
+ ClientConnection,
+ FALSE );
+
+ if ( Context == NULL ) {
+
+ //
+ // Check if this is a handle from the security.dll instead of from
+ // ntlmssp service.
+ //
+
+ if ( (ContextHandle->dwLower == SEC_HANDLE_SECURITY) &&
+ (SspCommonSecHandleValue == SEC_HANDLE_NTLMSSPS) ) {
+
+ //
+ // The context was created in the security.dll and is being
+ // completed in the service. So we have to copy over the
+ // context structure to get all the flags.
+ //
+
+ SSP_CONTEXT ContextCopy;
+
+ SecStatus = SspLpcCopyFromClientBuffer(
+ ClientConnection,
+ sizeof(SSP_CONTEXT),
+ &ContextCopy,
+ (PVOID) ContextHandle->dwUpper
+ );
+ if (!NT_SUCCESS(SecStatus)) {
+ goto Cleanup;
+ }
+ Context = SspContextAllocateContext( ClientConnection );
+
+ if ( Context == NULL ) {
+ SecStatus = SEC_E_INSUFFICIENT_MEMORY;
+ goto Cleanup;
+ }
+
+ Context->NegotiateFlags = ContextCopy.NegotiateFlags;
+ Context->ContextFlags = ContextCopy.ContextFlags;
+ Context->State = NegotiateSentState;
+ ASSERT(ContextCopy.State == PassedToServiceState);
+ Context->StartTime = ContextCopy.StartTime;
+ Context->Interval = ContextCopy.Interval;
+
+ //
+ // Copy over the domain name, user name, and password
+ // from the old context
+ //
+
+ SecStatus = SspGetUnicodeStringFromClient(
+ ClientConnection,
+ ContextDomainName,
+ DomainNameSize,
+ DNLEN,
+ &Context->DomainName );
+
+ if ( !NT_SUCCESS(SecStatus) ) {
+ SspPrint(( SSP_API, "Cannot copy domain name.\n" ));
+ goto Cleanup;
+ }
+
+ SecStatus = SspGetUnicodeStringFromClient(
+ ClientConnection,
+ ContextUserName,
+ UserNameSize,
+ UNLEN,
+ &Context->UserName );
+
+ if ( !NT_SUCCESS(SecStatus) ) {
+ SspPrint(( SSP_API, "Cannot copy user name.\n" ));
+ goto Cleanup;
+ }
+
+ SecStatus = SspGetUnicodeStringFromClient(
+ ClientConnection,
+ ContextPassword,
+ PasswordSize,
+ PWLEN,
+ &Context->Password );
+
+ if ( !NT_SUCCESS(SecStatus) ) {
+ SspPrint(( SSP_API, "Cannot copy password.\n" ));
+ goto Cleanup;
+ }
+ SspHidePassword(&Context->Password);
+
+ ContextHandle->dwUpper = (DWORD) Context;
+ ContextHandle->dwLower = SspCommonSecHandleValue;
+
+ //
+ // Set the token and logon id to use to be the one from the
+ // client
+ //
+
+ ASSERT(ClientTokenHandle != NULL);
+ ClientLogonId = LogonId;
+ } else {
+ SecStatus = SEC_E_INVALID_HANDLE;
+ goto Cleanup;
+ }
+ } else {
+
+ //
+ // Check if this context has been passed to the service
+ //
+
+ if (Context->ServerContextHandle.dwUpper != 0) {
+ ASSERT(SspCommonSecHandleValue == SEC_HANDLE_SECURITY);
+ SecStatus = SEC_I_CALL_NTLMSSP_SERVICE;
+ *ContextHandle = Context->ServerContextHandle;
+ goto Cleanup;
+ }
+
+ //
+ // If this is not reauthentication (or is datagram reauthentication)
+ // pull the token out of the associated credential.
+ //
+
+ if ((Context->State != AuthenticateSentState) ||
+ ((Context->NegotiateFlags & NTLMSSP_NEGOTIATE_DATAGRAM) != 0)) {
+ ClientLogonId = &Context->Credential->LogonId;
+ ClientTokenHandle = Context->Credential->ClientTokenHandle;
+ }
+ }
+
+
+ //
+ // If we have already sent the authenticate message, then this must be
+ // RPC calling Initialize a third time to re-authenticate a connection.
+ // This happens when a new interface is called over an existing
+ // connection. What we do here is build a NULL authenticate message
+ // that the server will recognize and also ignore.
+ //
+
+ //
+ // That being said, if we are doing datagram style authentication then
+ // the story is different. The server may have dropped this security
+ // context and then the client sent another packet over. The server
+ // will then be trying to restore the context, so we need to build
+ // another authenticate message.
+ //
+
+
+ if ( Context->State == AuthenticateSentState ) {
+ AUTHENTICATE_MESSAGE NullMessage;
+
+ if (((Context->NegotiateFlags & NTLMSSP_NEGOTIATE_DATAGRAM) ==
+ NTLMSSP_NEGOTIATE_DATAGRAM) &&
+ (InputTokenSize != 0) &&
+ (InputToken != NULL) ) {
+
+ //
+ // we are doing a reauthentication for datagram, so let this
+ // through. We don't want the security.dll remapping this
+ // context.
+ //
+
+ *ContextAttributes |= SSP_RET_REAUTHENTICATION;
+
+ } else {
+
+
+ //
+ // To make sure this is the intended meaning of the call, check
+ // that the input token is NULL.
+ //
+
+ if ( (InputTokenSize != 0) || (InputToken != NULL) ) {
+
+ SecStatus = SEC_E_INVALID_TOKEN;
+ goto Cleanup;
+ }
+
+ if ( *OutputTokenSize < sizeof(NullMessage) ) {
+
+ SecStatus = SEC_E_BUFFER_TOO_SMALL;
+ }
+ else {
+
+ strcpy( NullMessage.Signature, NTLMSSP_SIGNATURE );
+ NullMessage.MessageType = NtLmAuthenticate;
+ RtlZeroMemory(&NullMessage.LmChallengeResponse,5*sizeof(STRING));
+ *OutputTokenSize = sizeof(NullMessage);
+ SecStatus = SspLpcCopyToClientBuffer(
+ ClientConnection,
+ sizeof(NullMessage),
+ OutputToken,
+ &NullMessage );
+ }
+
+ *ContextAttributes |= SSP_RET_REAUTHENTICATION;
+ goto Cleanup;
+
+ }
+
+
+ } else if ( Context->State != NegotiateSentState ) {
+ SspPrint(( SSP_API,
+ "SspHandleChallengeMessage: "
+ "Context not in NegotiateSentState\n" ));
+ SecStatus = SEC_E_OUT_OF_SEQUENCE;
+ goto Cleanup;
+ }
+
+
+
+
+
+
+ //
+ // We don't support any options.
+ //
+ // Complain about those that require we do something.
+ //
+
+ if ( (ContextReqFlags & (ISC_REQ_ALLOCATE_MEMORY |
+ ISC_REQ_PROMPT_FOR_CREDS |
+ ISC_REQ_USE_SUPPLIED_CREDS )) != 0 ) {
+
+ SspPrint(( SSP_API,
+ "SsprHandleChallengeMessage: invalid ContextReqFlags 0x%lx.\n",
+ ContextReqFlags ));
+ SecStatus = SEC_E_INVALID_CONTEXT_REQ;
+ goto Cleanup;
+ }
+
+ //
+ // Ignore the Credential Handle.
+ //
+ // Since this is the second call,
+ // the credential is implied by the Context.
+ // We could double check that the Credential Handle is either NULL or
+ // correct. However, our implementation doesn't maintain a close
+ // association between the two (actually no association) so checking
+ // would require a lot of overhead.
+ //
+
+ UNREFERENCED_PARAMETER( CredentialHandle );
+
+
+ //
+ // Get the ChallengeMessage.
+ //
+
+ if ( InputTokenSize < sizeof(OLD_CHALLENGE_MESSAGE) ) {
+ SspPrint(( SSP_API,
+ "SspHandleChallengeMessage: "
+ "ChallengeMessage size wrong %ld\n",
+ InputTokenSize ));
+ SecStatus = SEC_E_INVALID_TOKEN;
+ goto Cleanup;
+ }
+
+ SecStatus = SspContextGetMessage( ClientConnection,
+ InputToken,
+ InputTokenSize,
+ NtLmChallenge,
+ &ChallengeMessage );
+
+ if ( !NT_SUCCESS(SecStatus) ) {
+ SspPrint(( SSP_API,
+ "SspHandleChallengeMessage: "
+ "ChallengeMessage GetMessage returns 0x%lx\n",
+ SecStatus ));
+ goto Cleanup;
+ }
+
+
+ //
+ // Determine if the caller wants OEM or UNICODE
+ //
+
+ if ( ChallengeMessage->NegotiateFlags & NTLMSSP_NEGOTIATE_UNICODE ) {
+ Context->NegotiateFlags |= NTLMSSP_NEGOTIATE_UNICODE;
+ Context->NegotiateFlags &= ~NTLMSSP_NEGOTIATE_OEM;
+ DoUnicode = TRUE;
+ } else if ( ChallengeMessage->NegotiateFlags & NTLMSSP_NEGOTIATE_OEM ){
+ Context->NegotiateFlags |= NTLMSSP_NEGOTIATE_OEM;
+ Context->NegotiateFlags &= ~NTLMSSP_NEGOTIATE_UNICODE;
+ DoUnicode = FALSE;
+ } else {
+ SspPrint(( SSP_API,
+ "SspHandleChallengeMessage: "
+ "ChallengeMessage bad NegotiateFlags 0x%lx\n",
+ ChallengeMessage->NegotiateFlags ));
+ SecStatus = SEC_E_INVALID_TOKEN;
+ goto Cleanup;
+ }
+
+ //
+ // Copy other interesting negotiate flags into the context
+ //
+
+
+ if ( ChallengeMessage->NegotiateFlags & NTLMSSP_NEGOTIATE_LM_KEY ) {
+ Context->NegotiateFlags |= NTLMSSP_NEGOTIATE_LM_KEY;
+ } else {
+ Context->NegotiateFlags &= ~NTLMSSP_NEGOTIATE_LM_KEY;
+ }
+
+#ifndef EXPORT_BUILD
+ DbgPrint("Challenge message flags = 0x%x\n",ChallengeMessage->NegotiateFlags);
+ if ( (ChallengeMessage->NegotiateFlags & NTLMSSP_NEGOTIATE_STRONG_CRYPT) != 0 ) {
+ Context->NegotiateFlags |= NTLMSSP_NEGOTIATE_STRONG_CRYPT;
+ } else {
+ Context->NegotiateFlags &= ~NTLMSSP_NEGOTIATE_STRONG_CRYPT;
+ }
+ DbgPrint("Context flags = 0x%x\n",Context->NegotiateFlags);
+#endif // EXPORT_BUILD
+
+ if (ChallengeMessage->NegotiateFlags & NTLMSSP_NEGOTIATE_ALWAYS_SIGN ) {
+ Context->NegotiateFlags |= NTLMSSP_NEGOTIATE_ALWAYS_SIGN;
+ } else {
+ Context->NegotiateFlags &= ~NTLMSSP_NEGOTIATE_ALWAYS_SIGN;
+ }
+
+ //
+ // Determine that the caller negotated to NTLM or nothing, but not
+ // NetWare.
+ //
+
+ if ( (ChallengeMessage->NegotiateFlags & NTLMSSP_NEGOTIATE_NETWARE) &&
+ !(ChallengeMessage->NegotiateFlags & NTLMSSP_NEGOTIATE_NTLM ) ) {
+ SecStatus = SEC_E_UNSUPPORTED_FUNCTION;
+ SspPrint(( SSP_API,
+ "SsprHandleChallengeMessage: "
+ "ChallengeMessage asked for Netware only.\n" ));
+ goto Cleanup;
+ }
+
+ //
+ // Check if we negotiated for identify level
+ //
+
+ if (Context->NegotiateFlags & NTLMSSP_NEGOTIATE_IDENTIFY) {
+ if (ChallengeMessage->NegotiateFlags & NTLMSSP_NEGOTIATE_IDENTIFY) {
+
+ Context->ContextFlags |= ISC_REQ_IDENTIFY;
+ *ContextAttributes |= ISC_RET_IDENTIFY;
+ } else {
+ SecStatus = SEC_E_UNSUPPORTED_FUNCTION;
+ goto Cleanup;
+ }
+
+ }
+
+
+ //
+ // If the server is running on this same machine,
+ // just duplicate our caller's token and use it.
+ //
+
+ if ( ChallengeMessage->NegotiateFlags & NTLMSSP_NEGOTIATE_LOCAL_CALL ) {
+ CtxtHandle ServerContextHandle;
+ PSSP_CONTEXT ServerContext;
+ SECURITY_IMPERSONATION_LEVEL ImpersonationLevel = SecurityImpersonation;
+
+ Context->NegotiateFlags |= NTLMSSP_NEGOTIATE_LOCAL_CALL;
+
+ //
+ // We can only do local calls from in the NTLMSSP service, not from
+ // any other process
+ //
+
+ if ( SspCommonSecHandleValue == SEC_HANDLE_SECURITY ) {
+ SecStatus = SEC_I_CALL_NTLMSSP_SERVICE;
+ goto Cleanup;
+ }
+
+ //
+ // Require the new challenge message if we are going to access the
+ // server context handle
+ //
+
+ if ( InputTokenSize < sizeof(CHALLENGE_MESSAGE) ) {
+ SecStatus = SEC_E_INVALID_TOKEN;
+ goto Cleanup;
+ }
+ //
+ // Open the server's context here within this process.
+ //
+
+ ServerContextHandle.dwUpper = ChallengeMessage->ServerContextHandleUpper;
+ ServerContextHandle.dwLower = ChallengeMessage->ServerContextHandleLower;
+
+ ServerContext = SspContextReferenceContext(
+ &ServerContextHandle,
+ NULL,
+ FALSE );
+
+ if ( ServerContext == NULL ) {
+ //
+ // This means the server has lied about this being a local call or
+ // the server process has exitted.
+ //
+ SspPrint(( SSP_API,
+ "SspHandleChallengeMessage: "
+ "ChallengeMessage bad ServerContextHandle 0x%lx 0x%lx\n",
+ ChallengeMessage->ServerContextHandleUpper,
+ ChallengeMessage->ServerContextHandleLower ));
+ SecStatus = SEC_E_INVALID_TOKEN;
+ goto Cleanup;
+ }
+
+ if ((Context->NegotiateFlags & NTLMSSP_NEGOTIATE_IDENTIFY) != 0) {
+ ImpersonationLevel = SecurityIdentification;
+ }
+ SecStatus = SspDuplicateToken(
+ ClientTokenHandle,
+ ImpersonationLevel,
+ &ServerContext->TokenHandle
+ );
+
+ if (!NT_SUCCESS(SecStatus)) {
+ SspPrint(( SSP_API,
+ "SspHandleChallengeMessage: "
+ "Could not duplicate client token 0x%lx\n",
+ SecStatus ));
+ goto Cleanup;
+ }
+
+ SspContextDereferenceContext( ServerContext );
+
+ RtlZeroMemory(Context->SessionKey, MSV1_0_USER_SESSION_KEY_LENGTH);
+
+ //
+ // Don't pass any credentials in the authenticate message.
+ //
+ RtlInitString( &DomainName, NULL );
+ RtlInitString( &UserName, NULL );
+ RtlInitString( &Workstation, NULL );
+ RtlInitString( &NtChallengeResponse, NULL );
+ RtlInitString( &LmChallengeResponse, NULL );
+ RtlInitString( &DatagramSessionKey, NULL );
+
+ //
+ // If the server is running on a diffent machine,
+ // determine the caller's DomainName, UserName and ChallengeResponse
+ // to pass back in the AuthenicateMessage.
+ //
+ } else {
+
+ //
+ //
+ // Build the GetChallengeResponse message to pass to the LSA.
+ //
+
+ GetChallengeResponseSize = sizeof(*GetChallengeResponse);
+ GetChallengeResponse->MessageType = MsV1_0Lm20GetChallengeResponse;
+ GetChallengeResponse->ParameterControl = 0;
+ if ( Context->DomainName.Length == 0 ) {
+ GetChallengeResponse->ParameterControl |= RETURN_PRIMARY_LOGON_DOMAINNAME;
+ }
+ if ( Context->UserName.Length == 0 ) {
+ GetChallengeResponse->ParameterControl |= RETURN_PRIMARY_USERNAME;
+ }
+
+ //
+ // The password may be a zero length password
+ //
+
+ SspRevealPassword(&Context->Password);
+ GetChallengeResponse->Password = Context->Password;
+ if ( Context->Password.Buffer == NULL ) {
+ SECURITY_IMPERSONATION_LEVEL ImpersonationLevel;
+ ULONG TokenInformationSize = sizeof(SECURITY_IMPERSONATION_LEVEL);
+
+ GetChallengeResponse->ParameterControl |= USE_PRIMARY_PASSWORD;
+
+ //
+ // Check to make sure the client's impersonation level is
+ // less than or equal to what they are asking for on the
+ // server.
+ //
+
+ Status = NtQueryInformationToken(
+ ClientTokenHandle,
+ TokenImpersonationLevel,
+ (PVOID) &ImpersonationLevel,
+ TokenInformationSize,
+ &TokenInformationSize
+ );
+
+ //
+ // If the token is a primary token the return code will be
+ // STATUS_INVALID_INFO_CLASS because the token is forced to be
+ // equivalent to SecurityImpersonation.
+ //
+
+ if (NT_SUCCESS(Status)) {
+ if ((ImpersonationLevel == SecurityIdentification) &&
+ ((Context->ContextFlags & NTLMSSP_NEGOTIATE_IDENTIFY) == 0)) {
+ SecStatus = SEC_E_NO_CREDENTIALS;
+ goto Cleanup;
+ } else if (ImpersonationLevel != SecurityImpersonation) {
+ SecStatus = SEC_E_NO_CREDENTIALS;
+ }
+ } else if (Status != STATUS_INVALID_INFO_CLASS) {
+ SecStatus = SspNtStatusToSecStatus(
+ Status,
+ SEC_E_NO_CREDENTIALS
+ );
+ goto Cleanup;
+ }
+
+ } else {
+ // MSV needs the password to be 'in' the passed in buffer.
+ RtlCopyMemory( GetChallengeResponse+1,
+ GetChallengeResponse->Password.Buffer,
+ GetChallengeResponse->Password.Length + sizeof(WCHAR) );
+ GetChallengeResponse->Password.Buffer = (LPWSTR)(GetChallengeResponse+1);
+ GetChallengeResponseSize += GetChallengeResponse->Password.Length +
+ sizeof(WCHAR);
+ }
+
+ SspHidePassword(&Context->Password);
+
+ GetChallengeResponse->LogonId = *ClientLogonId;
+
+ RtlCopyMemory( &GetChallengeResponse->ChallengeToClient,
+ ChallengeMessage->Challenge,
+ MSV1_0_CHALLENGE_LENGTH );
+
+
+ //
+ // Get the DomainName, UserName, and ChallengeResponse from the MSV
+ //
+
+ Status = LsaCallAuthenticationPackage(
+ SspGlobalLogonProcessHandle,
+ SspGlobalAuthenticationPackage,
+ GetChallengeResponse,
+ GetChallengeResponseSize,
+ &ChallengeResponseMessage,
+ &ChallengeResponseSize,
+ &ProtocolStatus );
+
+ if ( !NT_SUCCESS(Status) ) {
+ SspPrint(( SSP_API,
+ "SspHandleChallengeMessage: "
+ "ChallengeMessage LsaCall to get ChallengeResponse returns 0x%lx\n",
+ Status ));
+ SecStatus = SspNtStatusToSecStatus( Status, SEC_E_NO_CREDENTIALS );
+ goto Cleanup;
+ }
+
+ if ( !NT_SUCCESS(ProtocolStatus) ) {
+ Status = ProtocolStatus;
+ SspPrint(( SSP_API,
+ "SspHandleChallengeMessage: "
+ "ChallengeMessage LsaCall to get ChallengeResponse returns ProtocolStatus 0x%lx\n",
+ Status ));
+ SecStatus = SspNtStatusToSecStatus( Status, SEC_E_NO_CREDENTIALS );
+ goto Cleanup;
+ }
+
+ //
+ // Normalize things by copying the default domain name and user name
+ // into the ChallengeResponseMessage structure.
+ //
+
+ if ( Context->DomainName.Length != 0 ) {
+ ChallengeResponseMessage->LogonDomainName = Context->DomainName;
+ }
+ if ( Context->UserName.Length != 0 ) {
+ ChallengeResponseMessage->UserName = Context->UserName;
+ }
+
+ //
+ // Convert the domainname/user name to the right character set.
+ //
+
+ if ( DoUnicode ) {
+ DomainName = *(PSTRING)&ChallengeResponseMessage->LogonDomainName;
+ UserName = *(PSTRING)&ChallengeResponseMessage->UserName;
+ Workstation = *(PSTRING)&SspGlobalUnicodeComputerNameString;
+ } else {
+ Status = RtlUpcaseUnicodeStringToOemString(
+ &DomainName,
+ &ChallengeResponseMessage->LogonDomainName,
+ TRUE);
+
+ if ( !NT_SUCCESS(Status) ) {
+ SecStatus = SspNtStatusToSecStatus( Status,
+ SEC_E_INSUFFICIENT_MEMORY );
+ goto Cleanup;
+ }
+
+ Status = RtlUpcaseUnicodeStringToOemString(
+ &UserName,
+ &ChallengeResponseMessage->UserName,
+ TRUE);
+
+ if ( !NT_SUCCESS(Status) ) {
+ SecStatus = SspNtStatusToSecStatus( Status,
+ SEC_E_INSUFFICIENT_MEMORY );
+ goto Cleanup;
+ }
+ Workstation = SspGlobalOemComputerNameString;
+
+ }
+
+ //
+ // Save the ChallengeResponses
+ //
+
+ LmChallengeResponse = ChallengeResponseMessage->CaseInsensitiveChallengeResponse;
+ NtChallengeResponse = ChallengeResponseMessage->CaseSensitiveChallengeResponse;
+
+ //
+ // Save the session key in the context for safe keeping unless we are
+ // doing datagram, in which case we already saved it.
+ //
+
+ if (Context->NegotiateFlags & NTLMSSP_NEGOTIATE_LM_KEY) {
+ LM_OWF_PASSWORD LmKey;
+ LM_RESPONSE LmResponseKey;
+
+ RtlZeroMemory(
+ LocalSessionKey,
+ MSV1_0_USER_SESSION_KEY_LENGTH
+ );
+
+ if (LmChallengeResponse.Length != LM_RESPONSE_LENGTH) {
+ SecStatus = SEC_E_UNSUPPORTED_FUNCTION;
+ goto Cleanup;
+ }
+
+ //
+ // The LM session key is made by taking the LM sesion key
+ // given to us by the LSA, extending it to LM_OWF_LENGTH
+ // with out salt, and then producing a new challenge-response
+ // with it and the original challenge response. The key is
+ // made from the first 8 bytes of the key.
+ //
+
+#ifndef EXPORT_BUILD
+ if (Context->NegotiateFlags & NTLMSSP_NEGOTIATE_STRONG_CRYPT) {
+ int i;
+
+ RtlCopyMemory( &LmKey,
+ ChallengeResponseMessage->LanmanSessionKey,
+ MSV1_0_LANMAN_SESSION_KEY_LENGTH );
+
+ memset( (PUCHAR)(&LmKey) + MSV1_0_LANMAN_SESSION_KEY_LENGTH,
+ NTLMSSP_KEY_SALT,
+ LM_OWF_PASSWORD_LENGTH - MSV1_0_LANMAN_SESSION_KEY_LENGTH );
+
+ //
+ // Mutate the key a bit so a caller can't spoof us
+ //
+
+ for (i = 0; i < MSV1_0_LANMAN_SESSION_KEY_LENGTH ; i++ ) {
+ ((PUCHAR)&LmKey)[i] ^= ChallengeResponseMessage->LanmanSessionKey[(i+MSV1_0_LANMAN_SESSION_KEY_LENGTH) % MSV1_0_LANMAN_SESSION_KEY_LENGTH];
+ }
+
+ Status = RtlCalculateLmResponse(
+ (PLM_CHALLENGE) LmChallengeResponse.Buffer,
+ &LmKey,
+ &LmResponseKey
+ );
+ } else
+
+#endif // EXPORT_BUILD
+ {
+
+ RtlCopyMemory( &LmKey,
+ ChallengeResponseMessage->LanmanSessionKey,
+ MSV1_0_LANMAN_SESSION_KEY_LENGTH );
+
+ memset( (PUCHAR)(&LmKey) + MSV1_0_LANMAN_SESSION_KEY_LENGTH,
+ NTLMSSP_KEY_SALT,
+ LM_OWF_PASSWORD_LENGTH - MSV1_0_LANMAN_SESSION_KEY_LENGTH );
+
+
+ Status = RtlCalculateLmResponse(
+ (PLM_CHALLENGE) LmChallengeResponse.Buffer,
+ &LmKey,
+ &LmResponseKey
+ );
+
+ }
+
+
+ if (!NT_SUCCESS(Status)) {
+ SecStatus = SspNtStatusToSecStatus( Status,
+ SEC_E_NO_CREDENTIALS );
+ goto Cleanup;
+ }
+
+ RtlCopyMemory(
+ LocalSessionKey,
+ &LmResponseKey,
+ MSV1_0_USER_SESSION_KEY_LENGTH
+ );
+
+ } else {
+
+ RtlCopyMemory( LocalSessionKey,
+ ChallengeResponseMessage->UserSessionKey,
+ MSV1_0_USER_SESSION_KEY_LENGTH);
+
+ }
+
+ //
+ // If we aren't doing datagram, store the session key in the
+ // context. Otherwise encrypt the session key to send to the
+ // server.
+ //
+
+ if ((Context->NegotiateFlags & ChallengeMessage->NegotiateFlags & (NTLMSSP_NEGOTIATE_DATAGRAM
+#ifndef EXPORT_BUILD
+ | NTLMSSP_NEGOTIATE_STRONG_CRYPT
+#endif // EXPORT_BUILD
+ ) ) == 0) {
+
+ RtlCopyMemory(
+ Context->SessionKey,
+ LocalSessionKey,
+ MSV1_0_USER_SESSION_KEY_LENGTH
+ );
+
+ RtlInitString( &DatagramSessionKey, NULL );
+
+ } else {
+ struct RC4_KEYSTRUCT Rc4Key;
+ rc4_key(
+ &Rc4Key,
+ MSV1_0_USER_SESSION_KEY_LENGTH,
+ LocalSessionKey
+ );
+
+
+ RtlCopyMemory(
+ DatagramKey,
+ Context->SessionKey,
+ MSV1_0_USER_SESSION_KEY_LENGTH
+ );
+ rc4(
+ &Rc4Key,
+ MSV1_0_USER_SESSION_KEY_LENGTH,
+ DatagramKey
+ );
+
+ DatagramSessionKey.Buffer = DatagramKey;
+ DatagramSessionKey.Length =
+ DatagramSessionKey.MaximumLength = MSV1_0_USER_SESSION_KEY_LENGTH;
+
+
+ }
+
+
+ }
+
+ //
+ // If the caller specified SEQUENCE_DETECT or REPLAY_DETECT,
+ // that means they want to use the MakeSignature/VerifySignature
+ // calls. Add this to the returned attributes and the context
+ // negotiate flags.
+ //
+
+ if ((Context->NegotiateFlags & ChallengeMessage->NegotiateFlags & NTLMSSP_NEGOTIATE_SIGN) ||
+ (ContextReqFlags & ISC_REQ_SEQUENCE_DETECT) ||
+ (ContextReqFlags & ISC_REQ_REPLAY_DETECT)) {
+
+ Context->ContextFlags |= ISC_REQ_SEQUENCE_DETECT;
+ *ContextAttributes |= ISC_REQ_SEQUENCE_DETECT;
+ Context->NegotiateFlags |= NTLMSSP_NEGOTIATE_SIGN;
+ }
+
+ if ((Context->NegotiateFlags & ChallengeMessage->NegotiateFlags & NTLMSSP_NEGOTIATE_SEAL) ||
+ (ContextReqFlags & ISC_REQ_CONFIDENTIALITY)) {
+ if (SspGlobalEncryptionEnabled) {
+ Context->NegotiateFlags |= NTLMSSP_NEGOTIATE_SEAL;
+ Context->ContextFlags |= ISC_REQ_CONFIDENTIALITY;
+ *ContextAttributes |= ISC_REQ_CONFIDENTIALITY;
+ } else {
+ SecStatus = SEC_E_UNSUPPORTED_FUNCTION;
+ goto Cleanup;
+ }
+ }
+
+ if ((Context->NegotiateFlags & ChallengeMessage->NegotiateFlags & NTLMSSP_NEGOTIATE_DATAGRAM) ==
+ NTLMSSP_NEGOTIATE_DATAGRAM ) {
+ *ContextAttributes |= ISC_RET_DATAGRAM;
+ Context->ContextFlags |= ISC_RET_DATAGRAM;
+ Context->NegotiateFlags |= NTLMSSP_NEGOTIATE_DATAGRAM;
+ }
+
+
+ //
+ // Allocate an authenticate message
+ //
+
+ AuthenticateMessageSize =
+ sizeof(*AuthenticateMessage) +
+ LmChallengeResponse.Length +
+ NtChallengeResponse.Length +
+ DomainName.Length +
+ UserName.Length +
+ Workstation.Length +
+ DatagramSessionKey.Length;
+
+
+
+ if ( AuthenticateMessageSize > *OutputTokenSize ) {
+ SecStatus = SEC_E_BUFFER_TOO_SMALL;
+ goto Cleanup;
+ }
+
+ AuthenticateMessage = LocalAlloc( 0, AuthenticateMessageSize );
+
+ if ( AuthenticateMessage == NULL ) {
+ SecStatus = SEC_E_INSUFFICIENT_MEMORY;
+ goto Cleanup;
+ }
+
+ //
+ // Build the authenticate message
+ //
+
+ strcpy( AuthenticateMessage->Signature, NTLMSSP_SIGNATURE );
+ AuthenticateMessage->MessageType = NtLmAuthenticate;
+
+ Where = (PCHAR)(AuthenticateMessage+1);
+
+ //
+ // Copy the strings needing 2 byte alignment.
+ //
+ SspContextCopyString( AuthenticateMessage,
+ &AuthenticateMessage->DomainName,
+ &DomainName,
+ &Where,
+ FALSE ); // Pointers are relative
+
+ SspContextCopyString( AuthenticateMessage,
+ &AuthenticateMessage->UserName,
+ &UserName,
+ &Where,
+ FALSE ); // Pointers are relative
+
+ SspContextCopyString( AuthenticateMessage,
+ &AuthenticateMessage->Workstation,
+ &Workstation,
+ &Where,
+ FALSE ); // Pointers are relative
+
+ //
+ // Copy the strings not needing special alignment.
+ //
+ SspContextCopyString( AuthenticateMessage,
+ &AuthenticateMessage->LmChallengeResponse,
+ &LmChallengeResponse,
+ &Where,
+ FALSE ); // Pointers are relative
+
+ SspContextCopyString( AuthenticateMessage,
+ &AuthenticateMessage->NtChallengeResponse,
+ &NtChallengeResponse,
+ &Where,
+ FALSE ); // Pointers are relative
+
+ SspContextCopyString( AuthenticateMessage,
+ &AuthenticateMessage->SessionKey,
+ &DatagramSessionKey,
+ &Where,
+ FALSE ); // Pointers are relative
+
+ AuthenticateMessage->NegotiateFlags = Context->NegotiateFlags;
+
+ //
+ // Copy the AuthenticateMessage to the caller's address space.
+ //
+
+ SecStatus = SspLpcCopyToClientBuffer(
+ ClientConnection,
+ AuthenticateMessageSize,
+ OutputToken,
+ AuthenticateMessage );
+
+ if ( !NT_SUCCESS(SecStatus) ) {
+ goto Cleanup;
+ }
+
+ *OutputTokenSize = AuthenticateMessageSize;
+
+
+ SspPrint((SSP_API,"Client session key = %p\n",Context->SessionKey));
+
+ //
+ // Return output parameters to the caller.
+ //
+
+ *ExpirationTime = SspContextGetTimeStamp( Context, TRUE );
+
+
+
+ //
+ // Compute the context names. Since the buffer is UNLEN+DNLEN+2 make
+ // sure the strings fit.
+ //
+
+ Name[0] = L'\0';
+
+ if (Context->UserName.Buffer != NULL) {
+ if (Context->DomainName.Buffer != NULL) {
+ if (Context->DomainName.Length / sizeof(WCHAR) <= DNLEN) {
+ wcscat(Name,Context->DomainName.Buffer);
+ wcscat(Name,L"\\");
+ }
+ }
+ if (Context->UserName.Length / sizeof(WCHAR) <= UNLEN) {
+ wcscat(Name,Context->UserName.Buffer);
+ } else {
+ Name[0] = L'\0';
+ }
+ }
+
+ //
+ // Copy it to the client
+ //
+
+ SecStatus = SspLpcCopyToClientBuffer(
+ ClientConnection,
+ (wcslen(Name) + 1) * sizeof(WCHAR),
+ ContextNames,
+ Name
+ );
+
+ if (!NT_SUCCESS(SecStatus)) {
+ goto Cleanup;
+ }
+
+ SecStatus = STATUS_SUCCESS;
+
+ //
+ // Free and locally used resources.
+ //
+Cleanup:
+
+ if ( Context != NULL ) {
+
+ //
+ // Don't allow this context to be used again.
+ //
+
+ if ( NT_SUCCESS(SecStatus) ) {
+ Context->State = AuthenticateSentState;
+ } else if ( SecStatus == SEC_I_CALL_NTLMSSP_SERVICE ) {
+ Context->State = PassedToServiceState;
+ }
+ else Context->State = IdleState;
+
+ RtlCopyMemory(
+ SessionKey,
+ Context->SessionKey,
+ MSV1_0_USER_SESSION_KEY_LENGTH );
+
+ *NegotiateFlags = Context->NegotiateFlags;
+
+ SspContextDereferenceContext( Context );
+ }
+
+ if ( ChallengeMessage != NULL ) {
+ (VOID) LocalFree( ChallengeMessage );
+ }
+
+ if ( AuthenticateMessage != NULL ) {
+ (VOID) LocalFree( AuthenticateMessage );
+ }
+
+ if ( ChallengeResponseMessage != NULL ) {
+ (VOID) LsaFreeReturnBuffer( ChallengeResponseMessage );
+ }
+
+ if ( !DoUnicode ) {
+ if ( DomainName.Buffer != NULL) {
+ RtlFreeOemString( &DomainName );
+ }
+ if ( UserName.Buffer != NULL) {
+ RtlFreeOemString( &UserName );
+ }
+ }
+
+ return SecStatus;
+}
+
+
+SECURITY_STATUS
+SsprHandleAuthenticateMessage(
+ IN PSSP_CLIENT_CONNECTION ClientConnection,
+ IN PCredHandle CredentialHandle,
+ IN OUT PCtxtHandle ContextHandle,
+ IN ULONG ContextReqFlags,
+ IN ULONG InputTokenSize,
+ IN PVOID InputToken,
+ IN OUT PULONG OutputTokenSize,
+ OUT PVOID OutputToken,
+ OUT PULONG ContextAttributes,
+ OUT PTimeStamp ExpirationTime,
+ OUT PUCHAR SessionKey,
+ OUT PULONG NegotiateFlags,
+ OUT PHANDLE TokenHandle,
+ OUT PNTSTATUS ApiSubStatus,
+ OUT LPWSTR ContextNames,
+ OUT PTimeStamp PasswordExpiry
+ )
+
+/*++
+
+Routine Description:
+
+ Handle the authenticate message part of AcceptSecurityContext.
+
+Arguments:
+
+ ClientConnection - Describes the client process.
+
+ SessionKey - The session key for the context, used for signing and sealing
+
+ NegotiateFlags - The flags negotiated for the context, used for sign & seal
+
+ ApiSubStatus - Returns the substatus for why the logon failed.
+
+ ContextNames - Receives the domainname\username for a non-shortcutted
+ authentication. This is a pointer in the client's address
+ space.
+
+ PasswordExpiry - Contains the time that the authenticated user's password
+ expires, or 0x7fffffff ffffffff for local callers.
+
+ All other arguments same as for AcceptSecurityContext
+
+
+Return Value:
+
+ STATUS_SUCCESS - Message handled
+
+ SEC_E_INVALID_TOKEN -- Token improperly formatted
+ SEC_E_INVALID_HANDLE -- Credential/Context Handle is invalid
+ SEC_E_BUFFER_TOO_SMALL -- Buffer for output token isn't big enough
+ SEC_E_LOGON_DENIED -- User is no allowed to logon to this server
+ SEC_E_INSUFFICIENT_MEMORY -- Not enough memory
+
+--*/
+
+{
+ SECURITY_STATUS SecStatus;
+ NTSTATUS Status;
+ PSSP_CONTEXT Context = NULL;
+
+ PNEGOTIATE_MESSAGE NegotiateMessage = NULL;
+ PAUTHENTICATE_MESSAGE AuthenticateMessage = NULL;
+ ULONG MsvLogonMessageSize;
+ PMSV1_0_LM20_LOGON MsvLogonMessage = NULL;
+ ULONG LogonProfileMessageSize;
+ PMSV1_0_LM20_LOGON_PROFILE LogonProfileMessage = NULL;
+
+ BOOLEAN DoUnicode = FALSE;
+ UNICODE_STRING DomainName;
+ UNICODE_STRING UserName;
+ UNICODE_STRING Workstation;
+ LARGE_INTEGER KickOffTime;
+
+ LUID LogonId;
+ HANDLE LocalTokenHandle = NULL;
+ BOOLEAN LocalTokenHandleOpenned = FALSE;
+ TOKEN_SOURCE SourceContext;
+ QUOTA_LIMITS Quotas;
+ NTSTATUS SubStatus;
+ STRING OriginName;
+ PCHAR Where;
+ UCHAR LocalSessionKey[LM_RESPONSE_LENGTH];
+ WCHAR Name[UNLEN+DNLEN+2];
+
+ ASSERT(LM_RESPONSE_LENGTH >= MSV1_0_USER_SESSION_KEY_LENGTH);
+
+ //
+ // Initialization
+ //
+
+ *ContextAttributes = 0;
+ DomainName.Buffer = NULL;
+ UserName.Buffer = NULL;
+ Workstation.Buffer = NULL;
+ *ApiSubStatus = STATUS_SUCCESS;
+ PasswordExpiry->LowPart = 0xffffffff;
+ PasswordExpiry->HighPart = 0x7fffffff;
+
+ //
+ // Find the currently existing context.
+ //
+
+ Context = SspContextReferenceContext( ContextHandle,
+ ClientConnection,
+ FALSE );
+
+ if ( Context == NULL ) {
+
+ //
+ // If we are running in the security.dll and the handle belongs
+ // to the NTLMSSP service, return a different error
+ //
+
+ if ( (ContextHandle->dwLower == SEC_HANDLE_NTLMSSPS) &&
+ (SspCommonSecHandleValue == SEC_HANDLE_SECURITY) ) {
+ SecStatus = SEC_I_CALL_NTLMSSP_SERVICE;
+ } else SecStatus = SEC_E_INVALID_HANDLE;
+
+ goto Cleanup;
+ }
+
+
+ if ( ( Context->State != ChallengeSentState) &&
+ ( Context->State != AuthenticatedState) ) {
+ SspPrint(( SSP_API,
+ "SspHandleAuthenticateMessage: "
+ "Context not in ChallengeSentState\n" ));
+ SecStatus = SEC_E_OUT_OF_SEQUENCE;
+ goto Cleanup;
+ }
+
+
+
+ //
+ // Ignore the Credential Handle.
+ //
+ // Since this is the second call,
+ // the credential is implied by the Context.
+ // We could double check that the Credential Handle is either NULL or
+ // correct. However, our implementation doesn't maintain a close
+ // association between the two (actually no association) so checking
+ // would require a lot of overhead.
+ //
+
+ UNREFERENCED_PARAMETER( CredentialHandle );
+
+
+
+ //
+ // Get the AuthenticateMessage.
+ //
+
+ if ( InputTokenSize < sizeof(OLD_AUTHENTICATE_MESSAGE) ) {
+ SspPrint(( SSP_API,
+ "SspHandleAuthenticateMessage: "
+ "AuthenticateMessage size wrong %ld\n",
+ InputTokenSize ));
+ SecStatus = SEC_E_INVALID_TOKEN;
+ goto Cleanup;
+ }
+
+ SecStatus = SspContextGetMessage( ClientConnection,
+ InputToken,
+ InputTokenSize,
+ NtLmAuthenticate,
+ &AuthenticateMessage );
+
+ if ( !NT_SUCCESS(SecStatus) ) {
+ SspPrint(( SSP_API,
+ "SspHandleAuthenticateMessage: "
+ "AuthenticateMessage GetMessage returns 0x%lx\n",
+ SecStatus ));
+ goto Cleanup;
+ }
+
+ //
+ // If the call comes and we have already authenticated, then it is
+ // probably RPC trying to reauthenticate, which happens when someone
+ // calls two interfaces on the same connection. In this case we don't
+ // have to do anything - we just return success and let them get on
+ // with it. We do want to check that the input token is all zeros,
+ // though.
+ //
+ //
+
+ if ( Context->State == AuthenticatedState ) {
+ AUTHENTICATE_MESSAGE NullMessage;
+
+ *OutputTokenSize = 0;
+
+ //
+ // Check that all the fields are null. There are 5 strings
+ // in the Authenticate message that have to be set to zero.
+ //
+
+ RtlZeroMemory(&NullMessage.LmChallengeResponse,5*sizeof(STRING));
+
+ if (memcmp(&AuthenticateMessage->LmChallengeResponse,
+ &NullMessage.LmChallengeResponse,
+ sizeof(STRING) * 5) ) {
+ SecStatus = SEC_E_INVALID_TOKEN;
+
+ }
+ else
+ {
+ *ContextAttributes = SSP_RET_REAUTHENTICATION;
+ SecStatus = STATUS_SUCCESS;
+ }
+ goto Cleanup;
+ }
+
+
+ //
+ // If we are re-establishing a datagram context, get the negotiate flags
+ // out of this message.
+ //
+
+ if ((Context->NegotiateFlags & (NTLMSSP_NEGOTIATE_DATAGRAM
+ ) ) != 0) {
+
+ if ((InputTokenSize < sizeof(AUTHENTICATE_MESSAGE)) ||
+ ((AuthenticateMessage->NegotiateFlags & NTLMSSP_NEGOTIATE_DATAGRAM) == 0) ) {
+ SecStatus = SEC_E_INVALID_TOKEN;
+ goto Cleanup;
+ }
+
+ Context->NegotiateFlags = AuthenticateMessage->NegotiateFlags;
+ }
+
+#ifndef EXPORT_BUILD
+ if ((Context->NegotiateFlags & NTLMSSP_NEGOTIATE_STRONG_CRYPT ) != 0) {
+
+ if (InputTokenSize < sizeof(AUTHENTICATE_MESSAGE)) {
+
+ SecStatus = SEC_E_INVALID_TOKEN;
+ goto Cleanup;
+ }
+ }
+#endif // EXPORT_BUILD
+
+ //
+ // Convert relative pointers to absolute.
+ //
+
+ DoUnicode = ( Context->NegotiateFlags & NTLMSSP_NEGOTIATE_UNICODE ) != 0;
+
+ if ( !SspConvertRelativeToAbsolute( AuthenticateMessage,
+ InputTokenSize,
+ &AuthenticateMessage->LmChallengeResponse,
+ FALSE, // No special alignment
+ TRUE ) ) { // NULL OK
+ SecStatus = SEC_E_INVALID_TOKEN;
+ goto Cleanup;
+ }
+
+ if ( !SspConvertRelativeToAbsolute( AuthenticateMessage,
+ InputTokenSize,
+ &AuthenticateMessage->NtChallengeResponse,
+ FALSE, // No special alignment
+ TRUE ) ) { // NULL OK
+ SecStatus = SEC_E_INVALID_TOKEN;
+ goto Cleanup;
+ }
+
+ if ( !SspConvertRelativeToAbsolute( AuthenticateMessage,
+ InputTokenSize,
+ &AuthenticateMessage->DomainName,
+ DoUnicode, // Unicode alignment
+ TRUE ) ) { // NULL OK
+ SecStatus = SEC_E_INVALID_TOKEN;
+ goto Cleanup;
+ }
+
+ if ( !SspConvertRelativeToAbsolute( AuthenticateMessage,
+ InputTokenSize,
+ &AuthenticateMessage->UserName,
+ DoUnicode, // Unicode alignment
+#ifdef notdef
+ //
+ // Allow null sessions. The server should guard against them if
+ // it doesn't want them.
+ //
+ FALSE )) { // User name cannot be NULL
+
+#endif // notdef
+ TRUE ) ) { // NULL OK
+ SecStatus = SEC_E_INVALID_TOKEN;
+ goto Cleanup;
+ }
+
+ if ( !SspConvertRelativeToAbsolute( AuthenticateMessage,
+ InputTokenSize,
+ &AuthenticateMessage->Workstation,
+ DoUnicode, // Unicode alignment
+ TRUE ) ) { // NULL OK
+ SecStatus = SEC_E_INVALID_TOKEN;
+ goto Cleanup;
+ }
+
+ //
+ // If this is datagram or strong crypto, get the session key
+ //
+
+ if ((Context->NegotiateFlags & (NTLMSSP_NEGOTIATE_DATAGRAM
+#ifndef EXPORT_BUILD
+ | NTLMSSP_NEGOTIATE_STRONG_CRYPT
+#endif // EXPORT_BUILD
+ ) ) != 0) {
+
+ if ( !SspConvertRelativeToAbsolute( AuthenticateMessage,
+ InputTokenSize,
+ &AuthenticateMessage->SessionKey,
+ FALSE, // No special alignment
+ TRUE ) ) { // NULL o.k.
+ SecStatus = SEC_E_INVALID_TOKEN;
+ goto Cleanup;
+ }
+
+ //
+ // It should only be NULL if this is a local call
+ //
+
+ if (((Context->NegotiateFlags & NTLMSSP_NEGOTIATE_LOCAL_CALL) == 0) &&
+ (AuthenticateMessage->SessionKey.Buffer == NULL)) {
+ SecStatus = SEC_E_INVALID_TOKEN;
+ goto Cleanup;
+ }
+
+ }
+
+ //
+ // Convert the domainname/user name/workstation to the right character set.
+ //
+
+
+ if ( DoUnicode ) {
+ DomainName = *(PUNICODE_STRING)&AuthenticateMessage->DomainName;
+ UserName = *(PUNICODE_STRING)&AuthenticateMessage->UserName;
+ Workstation = *(PUNICODE_STRING)&AuthenticateMessage->Workstation;
+
+ } else {
+ Status = RtlOemStringToUnicodeString(
+ &DomainName,
+ &AuthenticateMessage->DomainName,
+ TRUE);
+
+ if ( !NT_SUCCESS(Status) ) {
+ SecStatus = SspNtStatusToSecStatus( Status,
+ SEC_E_INSUFFICIENT_MEMORY );
+ goto Cleanup;
+ }
+
+ Status = RtlOemStringToUnicodeString(
+ &UserName,
+ &AuthenticateMessage->UserName,
+ TRUE);
+
+ if ( !NT_SUCCESS(Status) ) {
+ SecStatus = SspNtStatusToSecStatus( Status,
+ SEC_E_INSUFFICIENT_MEMORY );
+ goto Cleanup;
+ }
+
+ Status = RtlOemStringToUnicodeString(
+ &Workstation,
+ &AuthenticateMessage->Workstation,
+ TRUE);
+
+ if ( !NT_SUCCESS(Status) ) {
+ SecStatus = SspNtStatusToSecStatus( Status,
+ SEC_E_INSUFFICIENT_MEMORY );
+ goto Cleanup;
+ }
+
+ }
+
+
+ //
+ // If the client is on the same machine as we are,
+ // just use the token the client has already placed in our context structure,
+ //
+
+ if ( (Context->NegotiateFlags & NTLMSSP_NEGOTIATE_LOCAL_CALL ) &&
+ Context->TokenHandle != NULL &&
+ DomainName.Length == 0 &&
+ UserName.Length == 0 &&
+ Workstation.Length == 0 &&
+ AuthenticateMessage->NtChallengeResponse.Length == 0 &&
+ AuthenticateMessage->LmChallengeResponse.Length == 0 ) {
+
+ LocalTokenHandle = Context->TokenHandle;
+ Context->TokenHandle = NULL;
+
+ KickOffTime.HighPart = 0x7FFFFFFF;
+ KickOffTime.LowPart = 0xFFFFFFFF;
+
+ RtlZeroMemory(Context->SessionKey, MSV1_0_USER_SESSION_KEY_LENGTH);
+
+
+ //
+ // If the client is on a different machine than we are,
+ // use LsaLogonUser to create a token for the client.
+ //
+ } else {
+
+ //
+ // Store the user name and domain name
+ //
+
+ SecStatus = SspDuplicateUnicodeString(
+ &Context->UserName,
+ &UserName
+ );
+ if (!NT_SUCCESS(SecStatus)) {
+ goto Cleanup;
+ }
+
+ SecStatus = SspDuplicateUnicodeString(
+ &Context->DomainName,
+ &DomainName
+ );
+ if (!NT_SUCCESS(SecStatus)) {
+ goto Cleanup;
+ }
+
+
+ //
+ // Allocate an MSV1_0 network logon message
+ //
+
+ MsvLogonMessageSize =
+ sizeof(*MsvLogonMessage) +
+ DomainName.Length +
+ UserName.Length +
+ Workstation.Length +
+ AuthenticateMessage->NtChallengeResponse.Length +
+ AuthenticateMessage->LmChallengeResponse.Length;
+
+ MsvLogonMessage = LocalAlloc( 0, MsvLogonMessageSize );
+
+ if ( MsvLogonMessage == NULL ) {
+ SecStatus = SEC_E_INSUFFICIENT_MEMORY;
+ goto Cleanup;
+ }
+
+ //
+ // Build the MSV1_0 network logon message to pass to the LSA.
+ //
+
+ MsvLogonMessage->MessageType = MsV1_0NetworkLogon;
+
+ Where = (PCHAR)(MsvLogonMessage+1);
+
+ SspContextCopyString( MsvLogonMessage,
+ (PSTRING)&MsvLogonMessage->LogonDomainName,
+ (PSTRING)&DomainName,
+ &Where,
+ TRUE ); // Pointers are absolute
+
+ SspContextCopyString( MsvLogonMessage,
+ (PSTRING)&MsvLogonMessage->UserName,
+ (PSTRING)&UserName,
+ &Where,
+ TRUE ); // Pointers are absolute
+
+ SspContextCopyString( MsvLogonMessage,
+ (PSTRING)&MsvLogonMessage->Workstation,
+ (PSTRING)&Workstation,
+ &Where,
+ TRUE ); // Pointers are absolute
+
+ RtlCopyMemory( MsvLogonMessage->ChallengeToClient,
+ Context->Challenge,
+ sizeof( MsvLogonMessage->ChallengeToClient ) );
+
+ SspContextCopyString( MsvLogonMessage,
+ &MsvLogonMessage->CaseSensitiveChallengeResponse,
+ &AuthenticateMessage->NtChallengeResponse,
+ &Where,
+ TRUE ); // Pointers are absolute
+
+ SspContextCopyString( MsvLogonMessage,
+ &MsvLogonMessage->CaseInsensitiveChallengeResponse,
+ &AuthenticateMessage->LmChallengeResponse,
+ &Where,
+ TRUE ); // Pointers are absolute
+
+ //
+ // By passing in the RETURN_PASSWORD_EXPIRY flag, the password
+ // expiration time is returned in the logoff time
+ //
+
+ MsvLogonMessage->ParameterControl = MSV1_0_ALLOW_SERVER_TRUST_ACCOUNT |
+ MSV1_0_RETURN_PASSWORD_EXPIRY;
+
+
+ //
+ // Log this user on.
+ //
+
+ RtlInitString( &OriginName, NULL ); // No origin (could use F(workstaion))
+ strncpy( SourceContext.SourceName, "NtLmSsp ", sizeof(SourceContext.SourceName) );
+ RtlZeroMemory( &SourceContext.SourceIdentifier,
+ sizeof(SourceContext.SourceIdentifier) );
+
+ Status = LsaLogonUser(
+ SspGlobalLogonProcessHandle,
+ &OriginName,
+ Network,
+ SspGlobalAuthenticationPackage,
+ MsvLogonMessage,
+ MsvLogonMessageSize,
+ NULL, // No local groups
+ &SourceContext,
+ &LogonProfileMessage,
+ &LogonProfileMessageSize,
+ &LogonId,
+ &LocalTokenHandle,
+ &Quotas,
+ &SubStatus );
+
+ if ( !NT_SUCCESS(Status) ) {
+ SspPrint(( SSP_API,
+ "SspHandleAuthenticateMessage: "
+ "LsaLogonUser returns 0x%lx\n",
+ Status ));
+ SecStatus = SspNtStatusToSecStatus( Status, SEC_E_LOGON_DENIED );
+ if (Status == STATUS_ACCOUNT_RESTRICTION) {
+ *ApiSubStatus = SubStatus;
+ } else {
+ *ApiSubStatus = Status;
+ }
+ goto Cleanup;
+ }
+
+
+ if ( !NT_SUCCESS(SubStatus) ) {
+ SspPrint(( SSP_API,
+ "SspHandleAuthenticateMessage: "
+ "LsaLogonUser returns SubStatus of 0x%lx\n",
+ SubStatus ));
+ SecStatus = SspNtStatusToSecStatus( SubStatus,
+ SEC_E_LOGON_DENIED );
+ goto Cleanup;
+ }
+
+ LocalTokenHandleOpenned = TRUE;
+
+ //
+ // Don't allow cleartext password on the logon.
+ //
+
+ if ( LogonProfileMessage->UserFlags & LOGON_NOENCRYPTION ) {
+ SspPrint(( SSP_API,
+ "SspHandleAuthenticateMessage: "
+ "LsaLogonUser used cleartext password\n" ));
+ SecStatus = SEC_E_LOGON_DENIED;
+ goto Cleanup;
+
+ }
+
+ //
+ // If we did a guest logon, set the substatus to be STATUS_NO_SUCH_USER
+ //
+
+ if ( LogonProfileMessage->UserFlags & LOGON_GUEST ) {
+ *ApiSubStatus = STATUS_NO_SUCH_USER;
+ }
+
+ //
+ // If we need a different level of token,
+ // create it here and close the original. We only need to do this
+ // after a LsaLogonUser because the token duplicated into our
+ // process is already the correct level.
+ //
+
+ if (Context->NegotiateFlags & NTLMSSP_NEGOTIATE_IDENTIFY) {
+ HANDLE TempTokenHandle;
+ SecStatus = SspDuplicateToken(
+ LocalTokenHandle,
+ SecurityIdentification,
+ &TempTokenHandle
+ );
+
+ if (!NT_SUCCESS(SecStatus)) {
+ goto Cleanup;
+ } else {
+ (VOID) NtClose(LocalTokenHandle);
+ LocalTokenHandle = TempTokenHandle;
+ }
+ }
+
+
+ //
+ // Save important information about the caller.
+ //
+
+ KickOffTime = LogonProfileMessage->KickOffTime;
+
+ //
+ // By passing in the RETURN_PASSWORD_EXPIRY flag, the password
+ // expiration time is returned in the logoff time
+ //
+
+ *PasswordExpiry = LogonProfileMessage->LogoffTime;
+
+ //
+ // Copy out the session key. For unicode clients, use the UserSessionKey
+ // and for OEM clients use the LanMan session key.
+ //
+
+ if ( Context->NegotiateFlags & NTLMSSP_NEGOTIATE_LM_KEY ) {
+
+ LM_OWF_PASSWORD LmKey;
+
+
+ //
+ // If the response is not the right length (i.e this is a null session)
+ // fail now since we can't create a key.
+ //
+
+ if (AuthenticateMessage->LmChallengeResponse.Length != LM_RESPONSE_LENGTH) {
+ SecStatus = SEC_E_UNSUPPORTED_FUNCTION;
+ goto Cleanup;
+ }
+
+ RtlZeroMemory(
+ LocalSessionKey,
+ LM_RESPONSE_LENGTH
+ );
+#ifndef EXPORT_BUILD
+ if (Context->NegotiateFlags & NTLMSSP_NEGOTIATE_STRONG_CRYPT) {
+ int i;
+
+ RtlCopyMemory( &LmKey,
+ LogonProfileMessage->LanmanSessionKey,
+ MSV1_0_LANMAN_SESSION_KEY_LENGTH );
+
+ memset( (PUCHAR)(&LmKey) + MSV1_0_LANMAN_SESSION_KEY_LENGTH,
+ NTLMSSP_KEY_SALT,
+ LM_OWF_PASSWORD_LENGTH - MSV1_0_LANMAN_SESSION_KEY_LENGTH );
+
+ //
+ // Mutate the key a bit so a caller can't spoof us
+ //
+
+ for (i = 0; i < MSV1_0_LANMAN_SESSION_KEY_LENGTH ; i++ ) {
+ ((PUCHAR)&LmKey)[i] ^= LogonProfileMessage->LanmanSessionKey[(i+MSV1_0_LANMAN_SESSION_KEY_LENGTH) % MSV1_0_LANMAN_SESSION_KEY_LENGTH];
+ }
+
+ Status = RtlCalculateLmResponse(
+ (PLM_CHALLENGE) AuthenticateMessage->LmChallengeResponse.Buffer,
+ &LmKey,
+ (PLM_RESPONSE) LocalSessionKey );
+ } else
+
+#endif // EXPORT_BUILD
+ {
+
+ //
+ // The LM session key is made by taking the LM sesion key
+ // given to us by the LSA, extending it to LM_OWF_LENGTH
+ // with out salt, and then producing a new challenge-response
+ // with it and the original challenge response. The key is
+ // made from the first 8 bytes of the key.
+ //
+
+ RtlCopyMemory( &LmKey,
+ LogonProfileMessage->LanmanSessionKey,
+ MSV1_0_LANMAN_SESSION_KEY_LENGTH );
+
+ memset( (PUCHAR)(&LmKey) + MSV1_0_LANMAN_SESSION_KEY_LENGTH,
+ NTLMSSP_KEY_SALT,
+ LM_OWF_PASSWORD_LENGTH - MSV1_0_LANMAN_SESSION_KEY_LENGTH );
+
+
+ Status = RtlCalculateLmResponse(
+ (PLM_CHALLENGE) AuthenticateMessage->LmChallengeResponse.Buffer,
+ &LmKey,
+ (PLM_RESPONSE) LocalSessionKey );
+ }
+
+ if (!NT_SUCCESS(Status)) {
+ SecStatus = SspNtStatusToSecStatus( Status,
+ SEC_E_NO_CREDENTIALS );
+ goto Cleanup;
+ }
+
+
+ } else {
+
+ RtlCopyMemory( LocalSessionKey,
+ LogonProfileMessage->UserSessionKey,
+ MSV1_0_USER_SESSION_KEY_LENGTH);
+ }
+
+ //
+ // For anything but datagram, copy the just-computed session key
+ // into the context. Otherwise, decrypt the session key that
+ // came in the authenticate message.
+ //
+
+ if ((Context->NegotiateFlags & (NTLMSSP_NEGOTIATE_DATAGRAM
+#ifndef EXPORT_BUILD
+ | NTLMSSP_NEGOTIATE_STRONG_CRYPT
+#endif // EXPORT_BUILD
+ ) ) == 0) {
+
+ RtlCopyMemory(
+ Context->SessionKey,
+ LocalSessionKey,
+ MSV1_0_LANMAN_SESSION_KEY_LENGTH
+ );
+
+ } else {
+ struct RC4_KEYSTRUCT Rc4Key;
+
+ //
+ // Use the just-computed session key as an RC4 key to
+ // decrypt the real session key.
+ //
+
+ rc4_key(
+ &Rc4Key,
+ MSV1_0_USER_SESSION_KEY_LENGTH,
+ LocalSessionKey
+ );
+
+ RtlZeroMemory(
+ Context->SessionKey,
+ MSV1_0_USER_SESSION_KEY_LENGTH
+ );
+
+ RtlCopyMemory(
+ Context->SessionKey,
+ AuthenticateMessage->SessionKey.Buffer,
+ AuthenticateMessage->SessionKey.Length
+ );
+
+ rc4(
+ &Rc4Key,
+ MSV1_0_USER_SESSION_KEY_LENGTH,
+ Context->SessionKey
+ );
+
+
+ }
+
+
+ }
+
+
+ //
+ // Copy the logon domain name returned by the LSA if it is different
+ // from the one the caller passed in. This may happend with temp duplicate
+ // accounts and local accounts.
+ //
+
+ if ((LogonProfileMessage != NULL) &&
+ (LogonProfileMessage->LogonDomainName.Length != 0) &&
+ !RtlEqualUnicodeString(
+ &Context->DomainName,
+ &LogonProfileMessage->LogonDomainName,
+ TRUE // case insensitive
+ )) {
+
+ //
+ // erase the old domain name
+ //
+
+ if (Context->DomainName.Buffer != NULL) {
+ LocalFree(Context->DomainName.Buffer);
+ Context->DomainName.Buffer = NULL;
+ }
+ SecStatus = SspDuplicateUnicodeString(
+ &Context->DomainName,
+ &LogonProfileMessage->LogonDomainName
+ );
+
+ if (!NT_SUCCESS(SecStatus)) {
+ goto Cleanup;
+ }
+
+ }
+
+ //
+ // Allow the context to live until kickoff time.
+ //
+
+ SspContextSetTimeStamp( Context, KickOffTime );
+
+ //
+ // Return output parameters to the caller.
+ //
+
+ *ExpirationTime = SspContextGetTimeStamp( Context, TRUE );
+ *OutputTokenSize = 0;
+
+ //
+ // We only support replay and sequence detect options
+ //
+
+
+ if ( Context->NegotiateFlags & NTLMSSP_NEGOTIATE_SIGN) {
+ *ContextAttributes |= ISC_REQ_REPLAY_DETECT | ISC_REQ_SEQUENCE_DETECT;
+ }
+
+ if ( ContextReqFlags & ISC_REQ_REPLAY_DETECT ) {
+ *ContextAttributes |= ISC_REQ_REPLAY_DETECT;
+ }
+
+ if ( ContextReqFlags & ISC_REQ_SEQUENCE_DETECT ) {
+ *ContextAttributes |= ISC_REQ_SEQUENCE_DETECT;
+ }
+
+ //
+ // Compute the context names. Since the buffer is UNLEN+DNLEN+2 make
+ // sure the strings fit.
+ //
+
+ Name[0] = L'\0';
+
+ if (Context->UserName.Buffer != NULL) {
+ if (Context->DomainName.Buffer != NULL) {
+ if (Context->DomainName.Length / sizeof(WCHAR) <= DNLEN) {
+ wcscat(Name,Context->DomainName.Buffer);
+ wcscat(Name,L"\\");
+ }
+ }
+ if (Context->UserName.Length / sizeof(WCHAR) <= UNLEN) {
+ wcscat(Name,Context->UserName.Buffer);
+ } else {
+ Name[0] = L'\0';
+ }
+ }
+
+ //
+ // Copy it to the client
+ //
+
+ SecStatus = SspLpcCopyToClientBuffer(
+ ClientConnection,
+ (wcslen(Name) + 1) * sizeof(WCHAR),
+ ContextNames,
+ Name
+ );
+
+ if (!NT_SUCCESS(SecStatus)) {
+ goto Cleanup;
+ }
+
+
+ UNREFERENCED_PARAMETER( OutputToken );
+
+ SecStatus = STATUS_SUCCESS;
+
+
+ //
+ // Free and locally used resources.
+ //
+Cleanup:
+
+ if ( Context != NULL ) {
+ //
+ // Don't allow this context to be used again.
+ //
+ if ( NT_SUCCESS(SecStatus) ) {
+ Context->State = AuthenticatedState;
+
+ if ( LocalTokenHandle ) {
+ *TokenHandle = LocalTokenHandle;
+ }
+
+ LocalTokenHandle = NULL;
+
+ RtlCopyMemory(
+ SessionKey,
+ Context->SessionKey,
+ MSV1_0_USER_SESSION_KEY_LENGTH );
+
+ *NegotiateFlags = Context->NegotiateFlags;
+
+ } else {
+ Context->State = IdleState;
+ }
+ SspContextDereferenceContext( Context );
+ }
+
+ if ( NegotiateMessage != NULL ) {
+ (VOID) LocalFree( NegotiateMessage );
+ }
+
+ if ( AuthenticateMessage != NULL ) {
+ (VOID) LocalFree( AuthenticateMessage );
+ }
+
+ if ( MsvLogonMessage != NULL ) {
+ (VOID) LocalFree( MsvLogonMessage );
+ }
+
+
+ if ( LogonProfileMessage != NULL ) {
+ (VOID) LsaFreeReturnBuffer( LogonProfileMessage );
+ }
+
+ if ( LocalTokenHandle != NULL && LocalTokenHandleOpenned ) {
+ (VOID) NtClose( LocalTokenHandle );
+ }
+
+ if ( !DoUnicode ) {
+ if ( DomainName.Buffer != NULL) {
+ RtlFreeUnicodeString( &DomainName );
+ }
+ if ( UserName.Buffer != NULL) {
+ RtlFreeUnicodeString( &UserName );
+ }
+ if ( Workstation.Buffer != NULL) {
+ RtlFreeUnicodeString( &Workstation );
+ }
+ }
+
+ //
+ // Set a flag telling RPC not to destroy the connection yet
+ //
+
+ if (!NT_SUCCESS(SecStatus)) {
+ *ContextAttributes |= ASC_RET_THIRD_LEG_FAILED;
+ }
+
+ return SecStatus;
+}
+
+
+SECURITY_STATUS
+SsprQueryContextAttributes(
+ IN PSSP_CLIENT_CONNECTION ClientConnection,
+ IN PCtxtHandle ContextHandle,
+ IN ULONG Attribute,
+ OUT PVOID Buffer
+ )
+
+/*++
+
+Routine Description:
+
+ This API allows a customer of the security services to determine
+ certain attributes of the context. These are: sizes, names, and
+ lifespan.
+
+Arguments:
+
+ ClientConnection - Describes the client process.
+
+ ContextHandle - Handle to the context to query.
+
+ Attribute - Attribute to query.
+
+ #define SECPKG_ATTR_SIZES 0
+ #define SECPKG_ATTR_NAMES 1
+ #define SECPKG_ATTR_LIFESPAN 2
+
+ Buffer - Buffer to copy the data into. The buffer must be large enough
+ to fit the queried attribute.
+
+Return Value:
+
+ STATUS_SUCCESS - Call completed successfully
+
+ SEC_E_INVALID_HANDLE -- Credential/Context Handle is invalid
+ SEC_E_UNSUPPORTED_FUNCTION -- Function code is not supported
+
+--*/
+
+{
+ SECURITY_STATUS SecStatus;
+
+ PSSP_CONTEXT Context = NULL;
+
+ SecPkgContext_Sizes ContextSizes;
+ SecPkgContext_Lifespan ContextLifespan;
+ UCHAR ContextNamesBuffer[sizeof(SecPkgContext_Names)+UNLEN*sizeof(WCHAR)];
+ SecPkgContext_DceInfo ContextDceInfo;
+ SecPkgContext_Names ContextNames;
+ ULONG ContextNamesSize;
+ WCHAR Name[UNLEN+DNLEN+1];
+
+ //
+ // Initialization
+ //
+
+ SspPrint(( SSP_API, "SspQueryContextAttributes Entered\n" ));
+
+
+
+ //
+ // Find the currently existing context.
+ //
+
+ Context = SspContextReferenceContext( ContextHandle,
+ ClientConnection,
+ FALSE );
+
+ if ( Context == NULL ) {
+ if ( (ContextHandle->dwLower == SEC_HANDLE_NTLMSSPS) &&
+ (SspCommonSecHandleValue == SEC_HANDLE_SECURITY) ) {
+ SecStatus = SEC_I_CALL_NTLMSSP_SERVICE;
+ } else SecStatus = SEC_E_INVALID_HANDLE;
+ goto Cleanup;
+ }
+
+
+ //
+ // If this context is really on the ntlmssp server, don't query
+ // attributes here
+ //
+
+ if (Context->ServerContextHandle.dwUpper != 0) {
+ *ContextHandle = Context->ServerContextHandle;
+ SecStatus = SEC_I_CALL_NTLMSSP_SERVICE;
+ goto Cleanup;
+
+ }
+ //
+ // Handle each of the various queried attributes
+ //
+
+ switch ( Attribute) {
+ case SECPKG_ATTR_SIZES:
+
+ ContextSizes.cbMaxToken = NTLMSP_MAX_TOKEN_SIZE;
+
+ if (Context->NegotiateFlags & (NTLMSSP_NEGOTIATE_ALWAYS_SIGN |
+ NTLMSSP_NEGOTIATE_SIGN |
+ NTLMSSP_NEGOTIATE_SEAL) ) {
+ ContextSizes.cbMaxSignature = NTLMSSP_MESSAGE_SIGNATURE_SIZE;
+ } else {
+ ContextSizes.cbMaxSignature = 0;
+ }
+
+ if (Context->NegotiateFlags & NTLMSSP_NEGOTIATE_SEAL) {
+ ContextSizes.cbBlockSize = 1;
+ ContextSizes.cbSecurityTrailer = NTLMSSP_MESSAGE_SIGNATURE_SIZE;
+ }
+ else
+ {
+ ContextSizes.cbBlockSize = 0;
+ ContextSizes.cbSecurityTrailer = 0;
+ }
+
+ SecStatus = SspLpcCopyToClientBuffer(
+ ClientConnection,
+ sizeof(ContextSizes),
+ Buffer,
+ &ContextSizes );
+
+ break;
+
+ //
+ // No one uses the function so don't go to the overhead of maintaining
+ // the username in the context structure.
+ //
+
+ case SECPKG_ATTR_DCE_INFO:
+
+ SecStatus = SspLpcCopyFromClientBuffer (
+ ClientConnection,
+ sizeof(SecPkgContext_DceInfo),
+ &ContextDceInfo,
+ Buffer );
+
+ if (!NT_SUCCESS(SecStatus)) {
+ goto Cleanup;
+ }
+
+ //
+ // We would like to set the name to domain\user. If domain name
+ // exists, copy domain\. If user exists, append user to string
+ //
+
+ *Name = L'\0';
+
+ if (Context->UserName.Buffer != NULL) {
+ if (Context->DomainName.Buffer != NULL) {
+ wcscat(Name,Context->DomainName.Buffer);
+ wcscat(Name,L"\\");
+ }
+ wcscat(Name,Context->UserName.Buffer);
+ }
+
+ ContextNamesSize = (wcslen(Name) + 1) * sizeof(WCHAR);
+
+
+ SecStatus = SspLpcCopyToClientBuffer(
+ ClientConnection,
+ ContextNamesSize,
+ ContextDceInfo.pPac,
+ Name );
+
+ break;
+
+ case SECPKG_ATTR_NAMES:
+
+ SecStatus = SspLpcCopyFromClientBuffer (
+ ClientConnection,
+ sizeof(SecPkgContext_Names),
+ &ContextNames,
+ Buffer );
+
+ if (!NT_SUCCESS(SecStatus)) {
+ goto Cleanup;
+ }
+
+ //
+ // We would like to set the name to domain\user. If domain name
+ // exists, copy domain\. If user exists, append user to string
+ //
+
+ *Name = L'\0';
+
+ if (Context->UserName.Length != 0) {
+ if (Context->DomainName.Length != 0) {
+ wcscat(Name,Context->DomainName.Buffer);
+ wcscat(Name,L"\\");
+ }
+ wcscat(Name,Context->UserName.Buffer);
+ }
+
+ ContextNamesSize = (wcslen(Name) + 1) * sizeof(WCHAR);
+
+
+ SecStatus = SspLpcCopyToClientBuffer(
+ ClientConnection,
+ ContextNamesSize,
+ ContextNames.sUserName,
+ Name );
+
+ break;
+
+ case SECPKG_ATTR_LIFESPAN:
+
+ // Use the correct times here
+ ContextLifespan.tsStart = SspContextGetTimeStamp( Context, FALSE );
+ ContextLifespan.tsExpiry = SspContextGetTimeStamp( Context, TRUE );
+
+ SecStatus = SspLpcCopyToClientBuffer(
+ ClientConnection,
+ sizeof(ContextLifespan),
+ Buffer,
+ &ContextLifespan );
+
+ break;
+
+ default:
+ SecStatus = SEC_E_NOT_SUPPORTED;
+ break;
+ }
+
+
+ //
+ // Free local resources
+ //
+Cleanup:
+
+ if ( Context != NULL ) {
+ SspContextDereferenceContext( Context );
+ }
+
+ SspPrint(( SSP_API, "SspQueryContextAttributes returns 0x%lx\n", SecStatus ));
+ return SecStatus;
+}
+
+
+SECURITY_STATUS
+SsprDeleteSecurityContext (
+ IN PSSP_CLIENT_CONNECTION ClientConnection,
+ IN OUT PCtxtHandle ContextHandle
+ )
+
+/*++
+
+Routine Description:
+
+ Deletes the local data structures associated with the specified
+ security context and generates a token which is passed to a remote peer
+ so it too can remove the corresponding security context.
+
+ This API terminates a context on the local machine, and optionally
+ provides a token to be sent to the other machine. The OutputToken
+ generated by this call is to be sent to the remote peer (initiator or
+ acceptor). If the context was created with the I _REQ_ALLOCATE_MEMORY
+ flag, then the package will allocate a buffer for the output token.
+ Otherwise, it is the responsibility of the caller.
+
+Arguments:
+
+ ClientConnection - Describes the client process.
+
+ ContextHandle - Handle to the context to delete
+
+Return Value:
+
+ STATUS_SUCCESS - Call completed successfully
+
+ SEC_E_NO_SPM -- Security Support Provider is not running
+ SEC_E_INVALID_HANDLE -- Credential/Context Handle is invalid
+
+--*/
+
+{
+ SECURITY_STATUS SecStatus = STATUS_SUCCESS;
+ PSSP_CONTEXT Context = NULL;
+
+ //
+ // Initialization
+ //
+
+ SspPrint(( SSP_API, "SspDeleteSecurityContext Entered\n" ));
+
+
+
+ //
+ // Find the currently existing context (and delink it).
+ //
+
+ Context = SspContextReferenceContext( ContextHandle,
+ ClientConnection,
+ TRUE );
+
+ //
+ // If the context is a server context, return SEC_I_CALL_NTLMSSP_SERVICE.
+ // If there is also a context here, delete that first.
+ //
+
+ if ( Context == NULL ) {
+ if ( (ContextHandle->dwLower == SEC_HANDLE_NTLMSSPS) &&
+ (SspCommonSecHandleValue == SEC_HANDLE_SECURITY) ) {
+ SecStatus = SEC_I_CALL_NTLMSSP_SERVICE;
+ } else SecStatus = SEC_E_INVALID_HANDLE;
+ } else {
+
+ if (Context->ServerContextHandle.dwUpper != 0) {
+ *ContextHandle = Context->ServerContextHandle;
+ SecStatus = SEC_I_CALL_NTLMSSP_SERVICE;
+ }
+
+ SspContextDereferenceContext( Context );
+ }
+
+
+ SspPrint(( SSP_API, "SspDeleteSecurityContext returns 0x%lx\n", SecStatus ));
+ return SecStatus;
+}
+
+
+
+NTSTATUS
+SspContextRegisterLogonProcess(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ This function registers this process as a LogonProcess and looks up
+ the MSV1_0 authentication package.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ Status of the operation.
+
+ STATUS_NOT_LOGON_PROCESS - This process is not a logon process
+
+--*/
+
+{
+ LSA_OPERATIONAL_MODE SecurityMode;
+ NTSTATUS Status;
+ STRING LogonProcessName;
+ STRING PackageName;
+
+ //
+ // Register us as a logon process.
+ //
+
+ RtlInitAnsiString( &LogonProcessName, "NTLM Security Package" );
+ Status = LsaRegisterLogonProcess(
+ &LogonProcessName,
+ &SspGlobalLogonProcessHandle,
+ &SecurityMode );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ if ( Status == STATUS_PORT_CONNECTION_REFUSED ) {
+ Status = STATUS_NOT_LOGON_PROCESS;
+ }
+ return Status;
+ }
+
+ RtlInitAnsiString( &PackageName, MSV1_0_PACKAGE_NAME );
+ Status = LsaLookupAuthenticationPackage(
+ SspGlobalLogonProcessHandle,
+ &PackageName,
+ &SspGlobalAuthenticationPackage );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ (VOID) LsaDeregisterLogonProcess( SspGlobalLogonProcessHandle );
+ SspGlobalLogonProcessHandle = NULL;
+ return Status;
+ }
+
+ return STATUS_SUCCESS;
+
+}
+
+
+
+VOID
+SspContextDeregisterLogonProcess(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ This function deregisters this process as a LogonProcess.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ //
+ // Deregister us as a logon process.
+ //
+
+ if ( SspGlobalLogonProcessHandle != NULL ) {
+ (VOID) LsaDeregisterLogonProcess( SspGlobalLogonProcessHandle );
+ SspGlobalLogonProcessHandle = NULL;
+ }
+
+}
+
+
+
+
+NTSTATUS
+SspContextInitialize(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ This function initializes this module.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ Status of the operation.
+
+--*/
+
+{
+
+ //
+ // Initialize the Context list to be empty.
+ //
+
+ InitializeCriticalSection(&SspContextCritSect);
+ InitializeListHead( &SspContextList );
+
+ return STATUS_SUCCESS;
+
+}
+
+
+
+
+VOID
+SspContextTerminate(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ This function cleans up any dangling Contexts.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ Status of the operation.
+
+--*/
+
+{
+
+ //
+ // Drop any lingering Contexts
+ //
+
+ EnterCriticalSection( &SspContextCritSect );
+ while ( !IsListEmpty( &SspContextList ) ) {
+ CredHandle ContextHandle;
+ PSSP_CONTEXT Context;
+
+ ContextHandle.dwUpper =
+ (LONG) CONTAINING_RECORD( SspContextList.Flink,
+ SSP_CONTEXT,
+ Next );
+
+ ContextHandle.dwLower = SspCommonSecHandleValue;
+
+ LeaveCriticalSection( &SspContextCritSect );
+
+ Context = SspContextReferenceContext(
+ &ContextHandle,
+ NULL, // Don't know the Connection
+ TRUE); // Remove Context
+
+ if ( Context != NULL ) {
+ SspContextDereferenceContext(Context);
+ }
+
+ EnterCriticalSection( &SspContextCritSect );
+ }
+ LeaveCriticalSection( &SspContextCritSect );
+
+
+ //
+ // Delete the critical section
+ //
+
+ DeleteCriticalSection(&SspContextCritSect);
+
+ return;
+
+}
+
+
+SECURITY_STATUS
+SsprContextGetCredentials(
+ IN PCtxtHandle ContextHandle,
+ OUT LPWSTR * DomainName,
+ OUT PULONG DomainNameSize,
+ OUT LPWSTR * UserName,
+ OUT PULONG UserNameSize,
+ OUT LPWSTR * Password,
+ OUT PULONG PasswordSize,
+ OUT PHANDLE ClientTokenHandle,
+ OUT PLUID LogonId
+ )
+/*++
+
+Routine Description:
+
+ Returns the passed-in credentials of a context to the caller
+
+Arguments:
+
+ ContextHandle - handle to get credentials for
+ DomainName - gets pointer to domain name
+ DomainNameSize - gets size in bytes of domain name string
+ ...
+
+Return Value:
+
+ STATUS_SUCCESS - success
+ SEC_E_INVALID_HANDLE - ContextHandle doesn't reference a real context
+ SEC_E_INSUFFICIENT_MEMORY - out of memory
+
+--*/
+{
+ PSSP_CONTEXT Context;
+ SECURITY_STATUS SecStatus;
+
+ //
+ // Initialize parameters in case of error
+ //
+
+ *DomainName = NULL;
+ *DomainNameSize = 0;
+ *UserName = NULL;
+ *UserNameSize = 0;
+ *Password = NULL;
+ *PasswordSize = 0;
+
+ Context = SspContextReferenceContext(
+ ContextHandle,
+ NULL,
+ FALSE);
+
+ if (Context == NULL) {
+ return(SEC_E_INVALID_HANDLE);
+ }
+
+
+ //
+ // Copy out the domain name, username, and password, if they
+ // exist
+ //
+
+ if (Context->DomainName.Buffer != NULL) {
+ *DomainName = SspAllocWStrFromWStr(Context->DomainName.Buffer);
+ if (*DomainName == NULL) {
+ SecStatus = SEC_E_INSUFFICIENT_MEMORY;
+ goto Cleanup;
+ }
+ *DomainNameSize = (wcslen(*DomainName) + 1) * sizeof(WCHAR);
+ }
+
+ if (Context->UserName.Buffer != NULL) {
+ *UserName = SspAllocWStrFromWStr(Context->UserName.Buffer);
+ if (*UserName == NULL) {
+ SecStatus = SEC_E_INSUFFICIENT_MEMORY;
+ goto Cleanup;
+ }
+ *UserNameSize = (wcslen(*UserName) + 1) * sizeof(WCHAR);
+ }
+
+
+ if (Context->Password.Buffer != NULL) {
+ SspRevealPassword(&Context->Password);
+ *Password = SspAllocWStrFromWStr(Context->Password.Buffer);
+ SspHidePassword(&Context->Password);
+ if (*Password == NULL) {
+ SecStatus = SEC_E_INSUFFICIENT_MEMORY;
+ goto Cleanup;
+ }
+ *PasswordSize = (wcslen(*Password) + 1) * sizeof(WCHAR);
+ }
+ *LogonId = Context->Credential->LogonId;
+ *ClientTokenHandle = Context->Credential->ClientTokenHandle;
+
+ SecStatus = STATUS_SUCCESS;
+
+Cleanup:
+
+ if ( !NT_SUCCESS(SecStatus) ) {
+ if (*DomainName != NULL) {
+ LocalFree(*DomainName);
+ *DomainName = NULL;
+ *DomainNameSize = 0;
+ }
+ if (*UserName != NULL) {
+ LocalFree(*UserName);
+ *UserName = NULL;
+ *UserNameSize = 0;
+ }
+ if (*Password != NULL) {
+ LocalFree(*Password);
+ *Password = NULL;
+ *PasswordSize = 0;
+ }
+ }
+
+ SspContextDereferenceContext(Context);
+
+ return(SecStatus);
+}
+
+SECURITY_STATUS
+SsprContextUpdateContext(
+ PCtxtHandle OldContextHandle,
+ PCtxtHandle ServerContextHandle
+ )
+/*++
+
+Routine Description:
+
+ Updates a context with a server context handle
+
+Arguments:
+
+ ContextHandle - handle to set the server context handle for
+ ServerContextHandle - handle to set the server context handle to
+
+Return Value:
+
+ STATUS_SUCCESS - success
+ SEC_E_INVALID_HANDLE - ContextHandle doesn't reference a real context
+
+--*/
+{
+ PSSP_CONTEXT Context;
+
+ Context = SspContextReferenceContext(
+ OldContextHandle,
+ NULL,
+ FALSE);
+
+ if (Context == NULL) {
+ return(SEC_E_INVALID_HANDLE);
+ }
+
+ Context->ServerContextHandle = *ServerContextHandle;
+
+ SspContextDereferenceContext(Context);
+
+ return(STATUS_SUCCESS);
+}
diff --git a/private/net/svcdlls/ntlmssp/common/credhand.c b/private/net/svcdlls/ntlmssp/common/credhand.c
new file mode 100644
index 000000000..19ffcba18
--- /dev/null
+++ b/private/net/svcdlls/ntlmssp/common/credhand.c
@@ -0,0 +1,1119 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ credhand.c
+
+Abstract:
+
+ API and support routines for handling credential handles.
+
+Author:
+
+ Cliff Van Dyke (CliffV) 26-Jun-1993
+
+Revision History:
+
+--*/
+
+
+//
+// Common include files.
+//
+
+#include <ntlmcomn.h> // Common definitions for DLL and SERVICE
+#include <ntlmsspi.h> // Data private to the common routines
+#include <align.h> // ALIGN_WHCAR
+
+//
+// Crit Sect to protect various globals in this module.
+//
+
+CRITICAL_SECTION SspCredentialCritSect;
+
+
+
+LIST_ENTRY SspCredentialList;
+
+
+
+SECURITY_STATUS
+SspGetUnicodeStringFromClient(
+ IN PSSP_CLIENT_CONNECTION ClientConnection,
+ IN LPWSTR String,
+ IN ULONG StringSize,
+ IN ULONG MaximumLength,
+ OUT PUNICODE_STRING OutputString
+ )
+
+/*++
+
+Routine Description:
+
+ This routine copies the InputMessage into the local address space.
+ This routine then validates the message header.
+
+Arguments:
+
+ ClientConnection - Describes the client process.
+
+ String - Address of the string in the client process (must include
+ trailing zero character).
+
+ StringSize - Size of the string (in bytes).
+
+ MaximumLength - Maximum length of the string (in characters) (not including
+ the trailing zero characer).
+
+ OutputString - Returns a UNICODE_STRING with an allocated buffer that
+ contains the string. The buffer should be freed using LocalFree.
+ The field will be set NULL if the input string is NULL.
+
+Return Value:
+
+ STATUS_SUCCESS - Call completed successfully
+
+ SEC_E_INVALID_TOKEN -- Message improperly formatted
+ SEC_E_UNKNOWN_CREDENTIALS -- Credentials are improperly formed
+
+--*/
+
+{
+ SECURITY_STATUS SecStatus;
+ LPWSTR AllocatedString;
+
+
+ //
+ // If the caller didn't pass a string,
+ // just indicate so.
+ //
+
+ if ( String == NULL && StringSize == 0 ) {
+ RtlInitUnicodeString(
+ OutputString,
+ NULL
+ );
+ return STATUS_SUCCESS;
+ }
+
+ //
+ // Allocate a local buffer for the message.
+ //
+
+ if ( !COUNT_IS_ALIGNED(StringSize, ALIGN_WCHAR) ||
+ StringSize > (MaximumLength+1) * sizeof(WCHAR) ) {
+ return SEC_E_UNKNOWN_CREDENTIALS;
+ }
+
+ AllocatedString = LocalAlloc( 0, StringSize );
+
+ if ( AllocatedString == NULL ) {
+ return SEC_E_INSUFFICIENT_MEMORY;
+ }
+
+
+ //
+ // Copy the message into the buffer
+ //
+
+ SecStatus = SspLpcCopyFromClientBuffer (
+ ClientConnection,
+ StringSize,
+ AllocatedString,
+ String );
+
+ if ( !NT_SUCCESS(SecStatus) ) {
+ (VOID) LocalFree( AllocatedString );
+ return SecStatus;
+ }
+
+
+ //
+ // Ensure the string is trailing zero terminated.
+ //
+
+ if ( AllocatedString[(StringSize/sizeof(WCHAR))-1] != L'\0' ) {
+ (VOID) LocalFree( AllocatedString );
+ return SEC_E_UNKNOWN_CREDENTIALS;
+ }
+
+ OutputString->Buffer = AllocatedString;
+ OutputString->MaximumLength = (USHORT) StringSize;
+ OutputString->Length = (USHORT) (StringSize - sizeof(WCHAR));
+
+ return STATUS_SUCCESS;
+}
+
+
+
+
+PSSP_CREDENTIAL
+SspCredentialReferenceCredential(
+ IN PCredHandle CredentialHandle,
+ IN PSSP_CLIENT_CONNECTION ClientConnection,
+ IN BOOLEAN DereferenceCredential,
+ IN BOOLEAN ForceRemoveCredential
+ )
+
+/*++
+
+Routine Description:
+
+ This routine checks to see if the Credential is from a currently
+ active client, and references the Credential if it is valid.
+
+ The caller may optionally request that the client's Credential be
+ removed from the list of valid Credentials - preventing future
+ requests from finding this Credential.
+
+ For a client's Credential to be valid, the Credential value
+ must be on our list of active Credentials.
+
+
+Arguments:
+
+ CredentialHandle - Points to the CredentialHandle of the Credential
+ to be referenced.
+
+ ClientConnection - Points to the client connection of the client
+ referencing the handle. (NULL means an internal reference.)
+
+ DereferenceCredential - This boolean value indicates that that a call
+ a single instance of this credential handle should be freed. If there
+ are multiple instances, they should still continue to work.
+
+ ForceRemoveCredential - This boolean value indicates whether the caller
+ wants the logon process's Credential to be removed from the list
+ of Credentials. TRUE indicates the Credential is to be removed.
+ FALSE indicates the Credential is not to be removed.
+
+
+Return Value:
+
+ NULL - the Credential was not found.
+
+ Otherwise - returns a pointer to the referenced credential.
+
+--*/
+
+{
+ PLIST_ENTRY ListEntry;
+ PSSP_CREDENTIAL Credential;
+
+ //
+ // Sanity check
+ //
+
+ if ( CredentialHandle->dwLower != SspCommonSecHandleValue ) {
+ return NULL;
+ }
+
+ //
+ // Make sure that nobody tries to force removal without also
+ // trying to dereference the credential.
+ //
+
+ ASSERT(!(ForceRemoveCredential && !DereferenceCredential));
+
+ //
+ // Acquire exclusive access to the Credential list
+ //
+
+ EnterCriticalSection( &SspCredentialCritSect );
+
+
+ //
+ // Now walk the list of Credentials looking for a match.
+ //
+
+ for ( ListEntry = SspCredentialList.Flink;
+ ListEntry != &SspCredentialList;
+ ListEntry = ListEntry->Flink ) {
+
+ Credential = CONTAINING_RECORD( ListEntry, SSP_CREDENTIAL, Next );
+
+
+ //
+ // Found a match ... reference this Credential
+ // (if the Credential is being removed, we would increment
+ // and then decrement the reference, so don't bother doing
+ // either - since they cancel each other out).
+ //
+
+ if ( Credential == (PSSP_CREDENTIAL) CredentialHandle->dwUpper &&
+ (ClientConnection == NULL ||
+ ClientConnection == Credential->ClientConnection )) {
+
+
+ if (!DereferenceCredential) {
+ Credential->References += 1;
+ } else {
+
+ //
+ // Decremenent the credential references, indicating
+ // that a call to free
+
+ Credential->CredentialReferences--;
+
+ if (ForceRemoveCredential || (Credential->CredentialReferences == 0)) {
+
+ RemoveEntryList( &Credential->Next );
+ RemoveEntryList( &Credential->NextForThisClient );
+ Credential->Unlinked = TRUE;
+
+ //
+ // If we are forcing removal, get rid of the appropriate
+ // number of references from all the other instances.
+ // This is used when the client connection is dropped.
+ //
+
+ if (ForceRemoveCredential) {
+ Credential->References -= Credential->CredentialReferences;
+ }
+
+ Credential->CredentialReferences = 0;
+
+ SspPrint(( SSP_API_MORE, "Delinked Credential 0x%lx\n",
+ Credential ));
+
+ }
+ }
+
+ LeaveCriticalSection( &SspCredentialCritSect );
+ return Credential;
+
+ }
+
+ }
+
+
+ //
+ // No match found
+ //
+ SspPrint(( SSP_API, "Tried to reference unknown Credential 0x%lx\n",
+ CredentialHandle->dwUpper ));
+
+ LeaveCriticalSection( &SspCredentialCritSect );
+ return NULL;
+
+}
+
+SECURITY_STATUS
+SspCredentialGetPassword(
+ IN PSSP_CREDENTIAL Credential,
+ OUT PUNICODE_STRING Password
+ )
+/*++
+
+Routine Description:
+
+ This routine copies the password out of credential. It requires locking
+ the credential list because other threads may be hiding/revealing the
+ password and we need exclusive access to do that.
+
+Arguments:
+
+ Credential - Credential record to retrieve the password from.
+
+ Password - UNICODE_STRING to store the password in.
+
+
+Return Value:
+
+ SEC_E_INSUFFICIENT_MEMORY - there was not enough memory to copy
+ the password.
+
+--*/
+
+{
+ SECURITY_STATUS SecStatus = SEC_E_OK;
+ EnterCriticalSection(&SspCredentialCritSect);
+
+ SspRevealPassword(&Credential->Password);
+ if ( Credential->Password.Buffer != NULL ) {
+ SecStatus = SspDuplicateUnicodeString(
+ Password,
+ &Credential->Password
+ );
+ } else {
+ RtlInitUnicodeString(
+ Password,
+ NULL
+ );
+ }
+ if (NT_SUCCESS(SecStatus)) {
+ SspHidePassword(Password);
+ }
+
+ SspHidePassword(&Credential->Password);
+ LeaveCriticalSection(&SspCredentialCritSect);
+ return(SecStatus);
+}
+
+
+PSSP_CREDENTIAL
+SspCredentialLookupSupplementalCredential(
+ IN PSSP_CLIENT_CONNECTION ClientConnection,
+ IN PLUID LogonId,
+ IN PUNICODE_STRING UserName,
+ IN PUNICODE_STRING DomainName,
+ IN PUNICODE_STRING Password
+ )
+
+/*++
+
+Routine Description:
+
+ This routine walks the list of credentials for this client looking
+ for one that has the same supplemental credentials as those passed
+ in. If it is found, its reference count is increased and a pointer
+ to it is returned.
+
+
+Arguments:
+
+ ClientConnection - Points to the client connection of the client
+ referencing the handle.
+
+ UserName - User name to match.
+
+ DomainName - Domain name to match.
+
+ Password - Password to match.
+
+
+Return Value:
+
+ NULL - the Credential was not found.
+
+ Otherwise - returns a pointer to the referenced credential.
+
+--*/
+
+{
+ PLIST_ENTRY ListEntry;
+ PSSP_CREDENTIAL Credential;
+ PLIST_ENTRY ListHead;
+
+
+ //
+ // Acquire exclusive access to the Credential list
+ //
+
+ EnterCriticalSection( &SspCredentialCritSect );
+
+ if (ClientConnection == NULL) {
+ ListHead = &SspCredentialList;
+ } else {
+ ListHead = &ClientConnection->CredentialHead;
+ }
+
+ //
+ // Now walk the list of Credentials looking for a match.
+ //
+
+ for ( ListEntry = ListHead->Flink;
+ ListEntry != ListHead;
+ ListEntry = ListEntry->Flink ) {
+
+ if (ClientConnection != NULL) {
+ Credential = CONTAINING_RECORD( ListEntry, SSP_CREDENTIAL, NextForThisClient );
+ } else {
+ Credential = CONTAINING_RECORD( ListEntry, SSP_CREDENTIAL, Next );
+ }
+
+ //
+ // We are only looking for outbound credentials.
+ //
+
+ if ((Credential->CredentialUseFlags & SECPKG_CRED_OUTBOUND) == 0) {
+ continue;
+ }
+
+ //
+ // Check for a match
+ //
+ if ( RtlEqualUnicodeString(
+ UserName,
+ &Credential->UserName,
+ FALSE
+ ) &&
+ RtlEqualUnicodeString(
+ DomainName,
+ &Credential->DomainName,
+ FALSE
+ ) &&
+ RtlEqualLuid(
+ LogonId,
+ &Credential->LogonId
+ )) {
+
+ SspRevealPassword(&Credential->Password);
+
+ if (RtlEqualUnicodeString(
+ Password,
+ &Credential->Password,
+ FALSE
+ )) {
+
+ //
+ // Found a match - reference the credential
+ //
+
+ SspHidePassword(&Credential->Password);
+
+ //
+ // Reference the credential and indicate that
+ // it is in use as two different handles to the caller
+ // (who may call FreeCredentialsHandle twice)
+ //
+
+ Credential->References++;
+ Credential->CredentialReferences++;
+
+ LeaveCriticalSection( &SspCredentialCritSect );
+ return Credential;
+
+ }
+ SspHidePassword(&Credential->Password);
+
+
+ }
+
+ }
+
+
+ //
+ // No match found
+ //
+ SspPrint(( SSP_API, "Tried to reference unknown Credential\n" ));
+
+ LeaveCriticalSection( &SspCredentialCritSect );
+ return NULL;
+
+}
+
+
+VOID
+SspCredentialDereferenceCredential(
+ IN PSSP_CREDENTIAL Credential
+ )
+
+/*++
+
+Routine Description:
+
+ This routine decrements the specified Credential's reference count.
+ If the reference count drops to zero, then the Credential is deleted
+
+Arguments:
+
+ Credential - Points to the Credential to be dereferenced.
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ ULONG References;
+
+ //
+ // Decrement the reference count
+ //
+
+ EnterCriticalSection( &SspCredentialCritSect );
+ ASSERT( Credential->References >= 1 );
+
+ References = -- Credential->References;
+
+ LeaveCriticalSection( &SspCredentialCritSect );
+
+ //
+ // If the count dropped to zero, then run-down the Credential
+ //
+
+ if ( References == 0) {
+
+ SspPrint(( SSP_API_MORE, "Deleting Credential 0x%lx\n",
+ Credential ));
+
+ if ( Credential->DomainName.Buffer != NULL ) {
+ (VOID) LocalFree( Credential->DomainName.Buffer );
+ }
+ if ( Credential->UserName.Buffer != NULL ) {
+ (VOID) LocalFree( Credential->UserName.Buffer );
+ }
+ if ( Credential->Password.Buffer != NULL ) {
+ (VOID) LocalFree( Credential->Password.Buffer );
+ }
+
+ if (!Credential->Unlinked) {
+ RemoveEntryList( &Credential->Next );
+ RemoveEntryList( &Credential->NextForThisClient );
+ }
+
+ if (Credential->ClientTokenHandle != NULL) {
+ (VOID) NtClose(Credential->ClientTokenHandle);
+ }
+ (VOID) LocalFree( Credential );
+
+ }
+
+
+ return;
+
+}
+
+
+
+VOID
+SspCredentialClientConnectionDropped(
+ PSSP_CLIENT_CONNECTION ClientConnection
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is called when the ClientConnection is dropped to allow
+ us to remove any Credentials for the ClientConnection.
+
+Arguments:
+
+ ClientConnection - Pointer to the ClientConnection that has been dropped.
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ //
+ // Drop any lingering Credentials
+ //
+
+ EnterCriticalSection( &SspCredentialCritSect );
+ while ( !IsListEmpty( &ClientConnection->CredentialHead ) ) {
+ CredHandle CredentialHandle;
+ PSSP_CREDENTIAL Credential;
+
+ CredentialHandle.dwUpper =
+ (LONG) CONTAINING_RECORD( ClientConnection->CredentialHead.Flink,
+ SSP_CREDENTIAL,
+ NextForThisClient );
+
+ CredentialHandle.dwLower = SspCommonSecHandleValue;
+
+ LeaveCriticalSection( &SspCredentialCritSect );
+
+ Credential = SspCredentialReferenceCredential(
+ &CredentialHandle,
+ ClientConnection,
+ TRUE,
+ TRUE); // Remove Credential
+
+ if ( Credential != NULL ) {
+ SspCredentialDereferenceCredential(Credential);
+ }
+
+ EnterCriticalSection( &SspCredentialCritSect );
+ }
+ LeaveCriticalSection( &SspCredentialCritSect );
+
+}
+
+
+
+SECURITY_STATUS
+SsprAcquireCredentialHandle(
+ IN PSSP_CLIENT_CONNECTION ClientConnection,
+ IN PHANDLE ClientTokenHandle,
+ IN PLUID LogonId,
+ IN ULONG CredentialUseFlags,
+ OUT PCredHandle CredentialHandle,
+ OUT PTimeStamp Lifetime,
+ IN LPWSTR DomainName,
+ IN ULONG DomainNameSize,
+ IN LPWSTR UserName,
+ IN ULONG UserNameSize,
+ IN LPWSTR Password,
+ IN ULONG PasswordSize
+ )
+
+/*++
+
+Routine Description:
+
+ This API allows applications to acquire a handle to pre-existing
+ credentials associated with the user on whose behalf the call is made
+ i.e. under the identity this application is running. These pre-existing
+ credentials have been established through a system logon not described
+ here. Note that this is different from "login to the network" and does
+ not imply gathering of credentials.
+
+
+ This API returns a handle to the credentials of a principal (user, client)
+ as used by a specific security package. This handle can then be used
+ in subsequent calls to the Context APIs. This API will not let a
+ process obtain a handle to credentials that are not related to the
+ process; i.e. we won't allow a process to grab the credentials of
+ another user logged into the same machine. There is no way for us
+ to determine if a process is a trojan horse or not, if it is executed
+ by the user.
+
+Arguments:
+
+ ClientConnection - Describes the client process.
+
+ CredentialUseFlags - Flags indicating the way with which these
+ credentials will be used.
+
+ #define CRED_INBOUND 0x00000001
+ #define CRED_OUTBOUND 0x00000002
+ #define CRED_BOTH 0x00000003
+
+ The credentials created with CRED_INBOUND option can only be used
+ for (validating incoming calls and can not be used for making accesses.
+
+ CredentialHandle - Returned credential handle.
+
+ Lifetime - Time that these credentials expire. The value returned in
+ this field depends on the security package.
+
+ DomainName, DomainNameSize, UserName, UserNameSize, Password, PasswordSize -
+ Optional credentials for this user.
+
+Return Value:
+
+ STATUS_SUCCESS -- Call completed successfully
+
+ SEC_E_PRINCIPAL_UNKNOWN -- No such principal
+ SEC_E_NOT_OWNER -- caller does not own the specified credentials
+ SEC_E_INSUFFICIENT_MEMORY -- Not enough memory
+
+--*/
+
+{
+ SECURITY_STATUS SecStatus;
+ NTSTATUS Status;
+ PSSP_CREDENTIAL Credential = NULL;
+ UNICODE_STRING LocalDomainName;
+ UNICODE_STRING LocalUserName;
+ UNICODE_STRING LocalPassword;
+ TOKEN_STATISTICS TokenStatisticsInfo;
+ ULONG TokenStatisticsInfoSize = sizeof(TOKEN_STATISTICS);
+
+ //
+ // Initialization
+ //
+
+ RtlInitUnicodeString(
+ &LocalDomainName,
+ NULL
+ );
+
+ RtlInitUnicodeString(
+ &LocalUserName,
+ NULL
+ );
+
+ RtlInitUnicodeString(
+ &LocalPassword,
+ NULL
+ );
+
+ SspPrint(( SSP_API, "SsprAcquireCredentialHandle Entered\n" ));
+
+
+ //
+ // Ensure at least one Credential use bit is set.
+ //
+
+ if ( (CredentialUseFlags & (SECPKG_CRED_INBOUND|SECPKG_CRED_OUTBOUND)) == 0 ) {
+ SspPrint(( SSP_API,
+ "SsprAcquireCredentialHandle: invalid credential use.\n" ));
+ SecStatus = SEC_E_INVALID_CREDENTIAL_USE;
+ goto Cleanup;
+ }
+
+
+ //
+ // Copy the default credentials to the credential block
+ //
+
+ SecStatus = SspGetUnicodeStringFromClient(
+ ClientConnection,
+ DomainName,
+ DomainNameSize,
+ DNLEN,
+ &LocalDomainName );
+
+ if ( !NT_SUCCESS(SecStatus) ) {
+ SspPrint(( SSP_API, "Cannot copy domain name.\n" ));
+ goto Cleanup;
+ }
+
+ SecStatus = SspGetUnicodeStringFromClient(
+ ClientConnection,
+ UserName,
+ UserNameSize,
+ UNLEN,
+ &LocalUserName );
+
+ if ( !NT_SUCCESS(SecStatus) ) {
+ SspPrint(( SSP_API, "Cannot copy user name.\n" ));
+ goto Cleanup;
+ }
+
+ SecStatus = SspGetUnicodeStringFromClient(
+ ClientConnection,
+ Password,
+ PasswordSize,
+ PWLEN,
+ &LocalPassword );
+
+ if ( !NT_SUCCESS(SecStatus) ) {
+ SspPrint(( SSP_API, "Cannot copy password.\n" ));
+ goto Cleanup;
+ }
+
+
+ Status = NtQueryInformationToken(
+ *ClientTokenHandle,
+ TokenStatistics,
+ &TokenStatisticsInfo,
+ TokenStatisticsInfoSize,
+ &TokenStatisticsInfoSize );
+
+ if (!NT_SUCCESS(Status)) {
+ SecStatus = SspNtStatusToSecStatus( Status, SEC_E_NO_IMPERSONATION );
+ goto Cleanup;
+ }
+
+ //
+ // If this is an outbound credential, and supplemental credentials
+ // were supplied, look to see if we have already
+ // created one with this set of credentials. Note - this leaves
+ // the credential referenced, so if we fail further down we need to
+ // dereference the credential.
+ //
+
+ if ((CredentialUseFlags & SECPKG_CRED_OUTBOUND) != 0) {
+
+ Credential = SspCredentialLookupSupplementalCredential(
+ ClientConnection,
+ &TokenStatisticsInfo.AuthenticationId,
+ &LocalUserName,
+ &LocalDomainName,
+ &LocalPassword
+ );
+
+
+
+ }
+
+ //
+ // If we didn't just find a credential, create one now.
+ //
+
+ if (Credential == NULL) {
+
+ //
+ // Allocate a credential block and initialize it.
+ //
+
+ Credential = LocalAlloc( 0, sizeof(SSP_CREDENTIAL) );
+
+ if ( Credential == NULL ) {
+ SspPrint(( SSP_API, "Cannot allocate credential.\n" ));
+ SecStatus = SEC_E_INSUFFICIENT_MEMORY;
+ goto Cleanup;
+ }
+
+ //
+ // Actually its on both a global credential list and a per client connection
+ // list, but we link/delink from both lists at the same time so a single
+ // reference count handles both.
+ //
+
+ Credential->References = 1;
+ Credential->CredentialReferences = 1;
+ Credential->ClientConnection = ClientConnection;
+ Credential->CredentialUseFlags = CredentialUseFlags;
+ Credential->Unlinked = FALSE;
+
+ //
+ // Stick the token and logon ID in the credential
+ //
+
+ Credential->ClientTokenHandle = *ClientTokenHandle,
+ *ClientTokenHandle = NULL;
+ Credential->LogonId = *LogonId;
+
+ //
+ // Stick the supplemental credentials into the credential.
+ //
+
+ Credential->UserName = LocalUserName;
+ LocalUserName.Buffer = NULL;
+ Credential->DomainName = LocalDomainName;
+ LocalDomainName.Buffer = NULL;
+
+ SspHidePassword(&LocalPassword);
+ Credential->Password = LocalPassword;
+ LocalPassword.Buffer = NULL;
+
+ //
+ // Add it to the list of valid credential handles.
+ //
+
+ EnterCriticalSection( &SspCredentialCritSect );
+ InsertHeadList( &SspCredentialList, &Credential->Next );
+ if ( ClientConnection != NULL ) {
+ InsertHeadList( &ClientConnection->CredentialHead,
+ &Credential->NextForThisClient );
+ } else {
+ InitializeListHead( &Credential->NextForThisClient );
+ }
+ LeaveCriticalSection( &SspCredentialCritSect );
+
+ SspPrint(( SSP_API_MORE, "Added Credential 0x%lx\n", Credential ));
+
+ //
+ // Don't bother dereferencing because we already set the
+ // reference count to 1.
+ //
+
+ }
+
+ //
+ // Return output parameters to the caller.
+ //
+
+ CredentialHandle->dwUpper = (DWORD) Credential;
+ CredentialHandle->dwLower = SspCommonSecHandleValue;
+ *Lifetime = SspGlobalForever;
+
+ SecStatus = STATUS_SUCCESS;
+
+ //
+ // Free and locally used resources.
+ //
+
+Cleanup:
+
+ if ( !NT_SUCCESS(SecStatus) ) {
+
+ if ( Credential != NULL ) {
+ (VOID)LocalFree( Credential );
+ }
+
+ }
+ if (LocalUserName.Buffer != NULL) {
+ (VOID)LocalFree(LocalUserName.Buffer);
+ }
+
+ if (LocalDomainName.Buffer != NULL) {
+ (VOID)LocalFree(LocalDomainName.Buffer);
+ }
+
+ if (LocalPassword.Buffer != NULL) {
+ (VOID)LocalFree(LocalPassword.Buffer);
+ }
+
+ SspPrint(( SSP_API, "SspAcquireCredentialHandle returns 0x%lx\n", SecStatus ));
+
+ return SecStatus;
+}
+
+SECURITY_STATUS
+SsprFreeCredentialHandle(
+ IN PSSP_CLIENT_CONNECTION ClientConnection,
+ IN PCredHandle CredentialHandle
+ )
+
+/*++
+
+Routine Description:
+
+ This API is used to notify the security system that the credentials are
+ no longer needed and allows the application to free the handle acquired
+ in the call described above. When all references to this credential
+ set has been removed then the credentials may themselves be removed.
+
+Arguments:
+
+
+ ClientConnection - Describes the client process.
+
+ CredentialHandle - Credential Handle obtained through
+ AcquireCredentialHandle.
+
+Return Value:
+
+
+ STATUS_SUCCESS -- Call completed successfully
+
+ SEC_E_NO_SPM -- Security Support Provider is not running
+ SEC_E_INVALID_HANDLE -- Credential Handle is invalid
+
+
+--*/
+
+{
+ SECURITY_STATUS SecStatus;
+ PSSP_CREDENTIAL Credential;
+
+ //
+ // Initialization
+ //
+
+ SspPrint(( SSP_API, "SspFreeCredentialHandle Entered\n" ));
+
+ //
+ // Find the referenced credential and delink it.
+ //
+
+ Credential = SspCredentialReferenceCredential(
+ CredentialHandle,
+ ClientConnection,
+ TRUE, // remove the instance of the credential
+ FALSE);
+
+ if ( Credential == NULL ) {
+ SecStatus = SEC_E_INVALID_HANDLE;
+ goto Cleanup;
+ }
+
+ //
+ // Dereferencing the Credential will remove the client's reference
+ // to it, causing it to be rundown if nobody else is using it.
+ //
+
+ SspCredentialDereferenceCredential( Credential );
+
+
+ SecStatus = STATUS_SUCCESS;
+
+ //
+ // Free and locally used resources.
+ //
+Cleanup:
+
+ SspPrint(( SSP_API, "SspFreeCredentialHandle returns 0x%lx\n", SecStatus ));
+ return SecStatus;
+}
+
+
+
+
+NTSTATUS
+SspCredentialInitialize(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ This function initializes this module.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ Status of the operation.
+
+--*/
+
+{
+
+ //
+ // Initialize the Credential list to be empty.
+ //
+
+ InitializeCriticalSection(&SspCredentialCritSect);
+ InitializeListHead( &SspCredentialList );
+
+ return STATUS_SUCCESS;
+
+}
+
+
+
+
+VOID
+SspCredentialTerminate(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ This function cleans up any dangling credentials.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ Status of the operation.
+
+--*/
+
+{
+
+ //
+ // Drop any lingering Credentials
+ //
+
+ EnterCriticalSection( &SspCredentialCritSect );
+ while ( !IsListEmpty( &SspCredentialList ) ) {
+ CredHandle CredentialHandle;
+ PSSP_CREDENTIAL Credential;
+
+ CredentialHandle.dwUpper =
+ (LONG) CONTAINING_RECORD( SspCredentialList.Flink,
+ SSP_CREDENTIAL,
+ Next );
+
+ CredentialHandle.dwLower = SspCommonSecHandleValue;
+
+ LeaveCriticalSection( &SspCredentialCritSect );
+
+ Credential = SspCredentialReferenceCredential(
+ &CredentialHandle,
+ NULL, // Don't know the Connection
+ TRUE,
+ TRUE); // Remove Credential
+
+ if ( Credential != NULL ) {
+ SspCredentialDereferenceCredential(Credential);
+ }
+
+ EnterCriticalSection( &SspCredentialCritSect );
+ }
+ LeaveCriticalSection( &SspCredentialCritSect );
+
+
+ //
+ // Delete the critical section
+ //
+
+ DeleteCriticalSection(&SspCredentialCritSect);
+
+ return;
+
+}
diff --git a/private/net/svcdlls/ntlmssp/common/dirs b/private/net/svcdlls/ntlmssp/common/dirs
new file mode 100644
index 000000000..903636d5b
--- /dev/null
+++ b/private/net/svcdlls/ntlmssp/common/dirs
@@ -0,0 +1,27 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ dirs.
+
+Abstract:
+
+ This file specifies the subdirectories of the current directory that
+ contain component makefiles.
+
+
+Author:
+
+ Steve Wood (stevewo) 17-Apr-1990
+
+NOTE: Commented description of this file is in \nt\bak\bin\dirs.tpl
+
+!ENDIF
+
+DIRS= \
+ dom \
+ export
+
+
diff --git a/private/net/svcdlls/ntlmssp/common/dom/makefile b/private/net/svcdlls/ntlmssp/common/dom/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/net/svcdlls/ntlmssp/common/dom/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/net/svcdlls/ntlmssp/common/dom/sources b/private/net/svcdlls/ntlmssp/common/dom/sources
new file mode 100644
index 000000000..2d74511cb
--- /dev/null
+++ b/private/net/svcdlls/ntlmssp/common/dom/sources
@@ -0,0 +1,2 @@
+!include ..\sources.inc
+
diff --git a/private/net/svcdlls/ntlmssp/common/encrypt.c b/private/net/svcdlls/ntlmssp/common/encrypt.c
new file mode 100644
index 000000000..a6f3e2111
--- /dev/null
+++ b/private/net/svcdlls/ntlmssp/common/encrypt.c
@@ -0,0 +1,142 @@
+/*++
+
+Copyright (c) 1994 Microsoft Corporation
+
+Module Name:
+
+ encrypt.c
+
+Abstract:
+
+ Contains routine to check whether encryption is supported on this
+ system or not.
+
+Author:
+
+ Mike Swift (MikeSw) 2-Aug-1994
+
+Revision History:
+
+--*/
+
+#include <windows.h>
+#include <stdlib.h>
+#include <string.h>
+#include <rpc.h>
+
+#if 0
+BOOLEAN
+IsEncryptionPermitted(VOID)
+/*++
+
+Routine Description:
+
+ This routine checks whether encryption is permitted by opening the
+ key HKEY_LOCAL_MACHINE\software\microsoft\rpc\SecurityService
+ and looking at the value MaxAuthLevel. If that value exists and is not
+ RPC_C_AUTHN_LEVEL_PKT_PRIVACY, FALSE is returned.
+
+Arguments:
+
+ none
+
+
+Return Value:
+
+ TRUE - encryption is permitted
+ FALSE - encryption is not permitted
+
+
+--*/
+
+{
+ HKEY hRpcKey;
+ ULONG Status;
+ ULONG Value;
+ ULONG Length;
+
+ Status = RegOpenKey(
+ HKEY_LOCAL_MACHINE,
+ L"software\\microsoft\\rpc\\securityservice",
+ &hRpcKey);
+
+ if (Status != 0) {
+ return(FALSE);
+ }
+
+ Length = sizeof(ULONG);
+ Status = RegQueryValueEx(
+ hRpcKey,
+ L"MaxAuthLevel",
+ NULL, // Reserved
+ NULL, // Type
+ (LPBYTE) &Value,
+ &Length
+ );
+
+ RegCloseKey(hRpcKey);
+
+ if (Status != 0) {
+ return(TRUE);
+ }
+
+ if (Value == RPC_C_AUTHN_LEVEL_PKT_PRIVACY) {
+ return(TRUE);
+ }
+
+ return(FALSE);
+
+}
+#endif
+
+BOOLEAN
+IsEncryptionPermitted(VOID)
+/*++
+
+Routine Description:
+
+ This routine checks whether encryption is getting the system default
+ LCID and checking whether the country code is CTRY_FRANCE.
+
+Arguments:
+
+ none
+
+
+Return Value:
+
+ TRUE - encryption is permitted
+ FALSE - encryption is not permitted
+
+
+--*/
+
+{
+ LCID DefaultLcid;
+ WCHAR CountryCode[10];
+ ULONG CountryValue;
+
+ DefaultLcid = GetSystemDefaultLCID();
+
+ //
+ // Check if the default language is Standard French
+ //
+
+ if (LANGIDFROMLCID(DefaultLcid) == 0x40c) {
+ return(FALSE);
+ }
+
+ //
+ // Check if the users's country is set to FRANCE
+ //
+
+ if (GetLocaleInfo(DefaultLcid,LOCALE_ICOUNTRY,CountryCode,10) == 0) {
+ return(FALSE);
+ }
+ CountryValue = (ULONG) wcstol(CountryCode,NULL,10);
+ if (CountryValue == CTRY_FRANCE) {
+ return(FALSE);
+ }
+ return(TRUE);
+}
+
diff --git a/private/net/svcdlls/ntlmssp/common/export/makefile b/private/net/svcdlls/ntlmssp/common/export/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/net/svcdlls/ntlmssp/common/export/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/net/svcdlls/ntlmssp/common/export/sources b/private/net/svcdlls/ntlmssp/common/export/sources
new file mode 100644
index 000000000..9040440d5
--- /dev/null
+++ b/private/net/svcdlls/ntlmssp/common/export/sources
@@ -0,0 +1,3 @@
+!include ..\sources.inc
+
+C_DEFINES=$(C_DEFINES) -DEXPORT_BUILD
diff --git a/private/net/svcdlls/ntlmssp/common/initcomn.c b/private/net/svcdlls/ntlmssp/common/initcomn.c
new file mode 100644
index 000000000..796e51c36
--- /dev/null
+++ b/private/net/svcdlls/ntlmssp/common/initcomn.c
@@ -0,0 +1,310 @@
+/*--
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ init.c
+
+Abstract:
+
+ Initialization routines for NtLmSsp routines shared by DLL and SERVICE.
+
+Author:
+
+ Cliff Van Dyke (CliffV) 21-Sep-1993
+
+Environment:
+
+ User mode only.
+ Contains NT-specific code.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+--*/
+
+//
+// Common include files.
+//
+
+#define NTLMSSPI_ALLOCATE // Allocate data from ntlmsspi.h
+#include <ntlmcomn.h> // Include files common to Serice and DLL
+#include <ntlmsspi.h> // Data private to the common routines
+#undef NTLMSSPI_ALLOCATE
+#include <winnls.h> // Locale and country code information
+#include <wchar.h> // wcstol
+
+
+//
+// Local variables.
+//
+
+BOOLEAN SspGlobalCredentialInitialized;
+BOOLEAN SspGlobalContextInitialized;
+BOOLEAN SspGlobalLogonProcessInitialized;
+BOOLEAN SspGlobalRNGInitialized;
+
+
+NTSTATUS
+SspCommonInitialize(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ Initialize the data shared by the DLL and SERVICE.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ STATUS_SUCCESS - All OK
+ STATUS_NOT_LOGON_PROCESS - Caller is not a logon process
+ This initialization routine is designed to return this error first
+ so that the DLL can use this routine to determine if its caller is
+ a logon process. The DLL will ignore this error and call the service
+ to get its job done.
+ Otherwise - Various other errors are returned.
+
+--*/
+{
+ NTSTATUS Status;
+ ULONG ComputerNameLength;
+
+ SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
+
+
+ //
+ // Set all globals to their initial value.
+ //
+
+ SspGlobalCredentialInitialized = FALSE;
+ SspGlobalContextInitialized = FALSE;
+ SspGlobalLogonProcessInitialized = FALSE;
+
+ SspGlobalForever.HighPart = 0x7FFFFFFF;
+ SspGlobalForever.LowPart = 0xFFFFFFFF;
+
+ RtlInitUnicodeString( &SspGlobalUnicodeComputerNameString, NULL );
+ RtlInitString( &SspGlobalOemComputerNameString, NULL );
+ RtlInitString( &SspGlobalOemPrimaryDomainNameString, NULL );
+
+ RtlInitUnicodeString( &SspGlobalTargetName, NULL );
+ SspGlobalLsaPolicyHandle = NULL;
+ SspGlobalTargetFlags = 0;
+
+
+ //
+ // Determine if this machine is running NT or NT Advanced Server.
+ //
+
+ if ( !RtlGetNtProductType( &SspGlobalNtProductType ) ) {
+ SspGlobalNtProductType = NtProductWinNt;
+ }
+
+
+
+
+ //
+ // Register us as a logon process.
+ //
+
+ Status = SspContextRegisterLogonProcess();
+
+ if ( !NT_SUCCESS( Status) ) {
+ SspCommonShutdown();
+ return Status;
+ }
+
+ SspGlobalLogonProcessInitialized = TRUE;
+
+
+
+
+
+ //
+ // Get the name of this computer
+ //
+
+ ComputerNameLength =
+ (sizeof(SspGlobalUnicodeComputerName)/sizeof(WCHAR));
+
+ if ( !GetComputerNameW( SspGlobalUnicodeComputerName,
+ &ComputerNameLength ) ) {
+ SspCommonShutdown();
+ return STATUS_INVALID_COMPUTER_NAME;
+ }
+
+ RtlInitUnicodeString( &SspGlobalUnicodeComputerNameString,
+ SspGlobalUnicodeComputerName );
+
+ Status = RtlUpcaseUnicodeStringToOemString(
+ &SspGlobalOemComputerNameString,
+ &SspGlobalUnicodeComputerNameString,
+ TRUE );
+
+ if ( !NT_SUCCESS(Status) ) {
+ SspCommonShutdown();
+ return Status;
+ }
+
+ //
+ // Get the primary domain name of this computer.
+ //
+
+ SspGetPrimaryDomainNameAndTargetName();
+
+
+ //
+ // Create a local copy of the Local System sid
+ //
+
+ if ( !AllocateAndInitializeSid( &NtAuthority,
+ 1,
+ SECURITY_LOCAL_SYSTEM_RID,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ &SspGlobalLocalSystemSid ) ) {
+ SspCommonShutdown();
+ return STATUS_NO_MEMORY;
+ }
+
+
+
+
+
+
+ //
+ // Initialize Context manager
+ //
+
+ Status = SspContextInitialize();
+
+ if ( !NT_SUCCESS( Status) ) {
+ SspCommonShutdown();
+ return Status;
+ }
+
+ SspGlobalContextInitialized = TRUE;
+
+
+
+ //
+ // Initialize Credential manager
+ //
+
+ Status = SspCredentialInitialize();
+
+ if ( !NT_SUCCESS( Status) ) {
+ SspCommonShutdown();
+ return Status;
+ }
+
+ SspGlobalCredentialInitialized = TRUE;
+
+ //
+ // Get the locale and check if it is FRANCE, which doesn't allow
+ // encryption
+ //
+
+ SspGlobalEncryptionEnabled = IsEncryptionPermitted();
+
+ SspInitializeRNG();
+ SspGlobalRNGInitialized = TRUE;
+
+
+ return STATUS_SUCCESS;
+}
+
+
+
+VOID
+SspCommonShutdown(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ Cleanup the data shared by the DLL and SERVICE.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+{
+
+
+
+ //
+ // Stop the Context module.
+ //
+
+ if ( SspGlobalContextInitialized ) {
+ SspContextTerminate();
+ SspGlobalContextInitialized = FALSE;
+ }
+
+
+ //
+ // Stop the Credential module.
+ //
+
+ if ( SspGlobalCredentialInitialized ) {
+ SspCredentialTerminate();
+ SspGlobalCredentialInitialized = FALSE;
+ }
+
+
+ //
+ // Stop being a logon process
+ //
+
+ if ( SspGlobalLogonProcessInitialized ) {
+ SspContextDeregisterLogonProcess();
+ SspGlobalLogonProcessInitialized = FALSE;
+ }
+
+ //
+ // Free up misc resources
+ //
+
+ if ( SspGlobalOemComputerNameString.Buffer != NULL ) {
+ RtlFreeOemString( &SspGlobalOemComputerNameString );
+ SspGlobalOemComputerNameString.Buffer = NULL;
+ }
+
+ if ( SspGlobalOemPrimaryDomainNameString.Buffer != NULL ) {
+ RtlFreeOemString( &SspGlobalOemPrimaryDomainNameString );
+ SspGlobalOemPrimaryDomainNameString.Buffer = NULL;
+ }
+
+ if ( SspGlobalLocalSystemSid != NULL ) {
+ FreeSid( SspGlobalLocalSystemSid );
+ SspGlobalLocalSystemSid = NULL;
+ }
+
+ if (SspGlobalRNGInitialized) {
+ SspCleanupRNG();
+ SspGlobalRNGInitialized = FALSE;
+
+ }
+
+
+}
diff --git a/private/net/svcdlls/ntlmssp/common/ntlmsspi.h b/private/net/svcdlls/ntlmssp/common/ntlmsspi.h
new file mode 100644
index 000000000..29558c024
--- /dev/null
+++ b/private/net/svcdlls/ntlmssp/common/ntlmsspi.h
@@ -0,0 +1,526 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ ntlmsspi.h
+
+Abstract:
+
+ Header file describing the interface to code common to the
+ NT Lanman Security Support Provider (NtLmSsp) Service and the DLL.
+
+Author:
+
+ Cliff Van Dyke (CliffV) 17-Sep-1993
+
+Revision History:
+
+--*/
+
+#ifndef _NTLMSSPI_INCLUDED_
+#define _NTLMSSPI_INCLUDED_
+
+//
+// init.c will #include this file with NTLMCOMN_ALLOCATE defined.
+// That will cause each of these variables to be allocated.
+//
+#ifdef NTLMSSPI_ALLOCATE
+#define EXTERN
+#else
+#define EXTERN extern
+#endif
+
+
+////////////////////////////////////////////////////////////////////////
+//
+// Global Definitions
+//
+////////////////////////////////////////////////////////////////////////
+
+//
+// Description of a credential.
+//
+
+typedef struct _SSP_CREDENTIAL {
+
+ //
+ // Global list of all Credentials.
+ // (Serialized by SspCredentialCritSect)
+ //
+
+ LIST_ENTRY Next;
+
+
+ //
+ // List of all Credentials for this ClientConnection.
+ // (Serialized by SspCredentialCritSect)
+ //
+
+ LIST_ENTRY NextForThisClient;
+
+
+ //
+ // Used to prevent this Credential from being deleted prematurely.
+ // (Serialized by SspCredentialCritSect)
+ //
+
+ ULONG References;
+
+ //
+ // Used by this credential to allow a single credential to be
+ // returned from multiple calls to AcquireCredential.
+ //
+
+ ULONG CredentialReferences;
+
+ //
+ // Flag of how credential may be used.
+ //
+ // SECPKG_CRED_* flags
+ //
+
+ ULONG CredentialUseFlags;
+
+ //
+ // Default credentials on client context, on server context UserName
+ // holds a full user name (domain\user) and the other two should be
+ // NULL.
+ //
+
+ UNICODE_STRING DomainName;
+ UNICODE_STRING UserName;
+ UNICODE_STRING Password;
+
+
+ //
+ // ClientConnection that originally created the credential.
+ //
+ // Note: we don't have a reference to the ClientConnection. This field
+ // is merely used as a tag that someone who does have a reference to the
+ // ClientConnection can compare with. (Keep it a PVOID to prevent
+ // accidental use as anything else)
+ //
+
+ PVOID ClientConnection;
+
+ //
+ // Token Handle of client
+ //
+
+ PHANDLE ClientTokenHandle;
+
+ //
+ // Logon ID of the client
+ //
+
+ LUID LogonId;
+
+
+ //
+ // This flag should be set when the credential is unlinked
+ // from the list.
+ //
+
+ BOOLEAN Unlinked;
+
+
+
+} SSP_CREDENTIAL, *PSSP_CREDENTIAL;
+
+
+//
+// Description of a Context
+//
+
+
+typedef struct _SSP_CONTEXT {
+
+ //
+ // Global list of all Contexts
+ // (Serialized by SspContextCritSect)
+ //
+
+ LIST_ENTRY Next;
+
+
+ //
+ // List of all Contexts for this ClientConnection.
+ // (Serialized by SspContextCritSect)
+ //
+
+ LIST_ENTRY NextForThisClient;
+
+
+ //
+ // Timeout the context after awhile.
+ //
+
+ LARGE_INTEGER StartTime;
+ ULONG Interval;
+
+
+ //
+ // Used to prevent this Context from being deleted prematurely.
+ // (Serialized by SspContextCritSect)
+ //
+
+ ULONG References;
+
+ //
+ // ClientConnection that originally created the context.
+ //
+ // Note: we don't have a reference to the ClientConnection. This field
+ // is merely used as a tag that someone who does have a reference to the
+ // ClientConnection can compare with. (Keep it a PVOID to prevent
+ // accidental use as anything else)
+ //
+
+ PVOID ClientConnection;
+
+ //
+ // Maintain the Negotiated protocol
+ //
+
+ ULONG NegotiateFlags;
+
+ //
+ // Maintain the context requirements
+ //
+
+ ULONG ContextFlags;
+
+ //
+ // State of the context
+ //
+
+ enum {
+ IdleState,
+ NegotiateSentState, // Outbound context only
+ ChallengeSentState, // Inbound context only
+ AuthenticateSentState, // Outbound context only
+ AuthenticatedState, // Inbound context only
+ PassedToServiceState // Outbound context only
+ } State;
+
+ //
+ // Token Handle of authenticated user
+ // Only valid when in AuthenticatedState.
+ //
+
+ HANDLE TokenHandle;
+
+ //
+ // Referenced pointer to the credential used to create this
+ // context.
+ //
+
+ PSSP_CREDENTIAL Credential;
+
+ //
+ // The challenge passed to the client.
+ // Only valid when in ChallengeSentState.
+ //
+
+ UCHAR Challenge[MSV1_0_CHALLENGE_LENGTH];
+
+ //
+ // The session key calculated by the LSA
+ //
+
+ UCHAR SessionKey[MSV1_0_USER_SESSION_KEY_LENGTH];
+
+ //
+ // Default credentials.
+ //
+
+ UNICODE_STRING DomainName;
+ UNICODE_STRING UserName;
+ UNICODE_STRING Password;
+
+
+ CtxtHandle ServerContextHandle;
+
+} SSP_CONTEXT, *PSSP_CONTEXT;
+
+//
+// Maximum lifetime of a context
+//
+#define NTLMSSP_MAX_LIFETIME (2*60*1000) // 2 minutes
+
+
+
+////////////////////////////////////////////////////////////////////////
+//
+// Opaque Messages passed between client and server
+//
+////////////////////////////////////////////////////////////////////////
+
+#define NTLMSSP_SIGNATURE "NTLMSSP"
+
+//
+// MessageType for the following messages.
+//
+
+typedef enum {
+ NtLmNegotiate = 1,
+ NtLmChallenge,
+ NtLmAuthenticate
+} NTLM_MESSAGE_TYPE;
+
+//
+// Valid values of NegotiateFlags
+//
+
+#define NTLMSSP_NEGOTIATE_UNICODE 0x0001 // Text strings are in unicode
+#define NTLMSSP_NEGOTIATE_OEM 0x0002 // Text strings are in OEM
+#define NTLMSSP_REQUEST_TARGET 0x0004 // Server should return its
+ // authentication realm
+#define NTLMSSP_NEGOTIATE_SIGN 0x0010 // Request signature capability
+#define NTLMSSP_NEGOTIATE_SEAL 0x0020 // Request confidentiality
+#define NTLMSSP_NEGOTIATE_DATAGRAM 0x0040 // Use datagram style authentication
+#define NTLMSSP_NEGOTIATE_LM_KEY 0x0080 // Use LM session key for sign/seal
+
+#define NTLMSSP_NEGOTIATE_NETWARE 0x0100 // NetWare authentication
+#define NTLMSSP_NEGOTIATE_NTLM 0x0200 // NTLM authentication
+
+#define NTLMSSP_NEGOTIATE_OEM_DOMAIN_SUPPLIED 0x1000 // Domain Name supplied on negotiate
+#define NTLMSSP_NEGOTIATE_OEM_WORKSTATION_SUPPLIED 0x2000 // Workstation Name supplied on negotiate
+#define NTLMSSP_NEGOTIATE_LOCAL_CALL 0x4000 // Indicates client/server are same machine
+#define NTLMSSP_NEGOTIATE_ALWAYS_SIGN 0x8000 // Sign for all security levels
+
+
+
+
+//
+// Valid target types returned by the server in Negotiate Flags
+//
+
+#define NTLMSSP_TARGET_TYPE_DOMAIN 0x10000 // TargetName is a domain name
+#define NTLMSSP_TARGET_TYPE_SERVER 0x20000 // TargetName is a server name
+#define NTLMSSP_TARGET_TYPE_SHARE 0x40000 // TargetName is a share name
+
+//
+// Additional negotiated flags
+//
+
+#define NTLMSSP_NEGOTIATE_IDENTIFY 0x100000 // Create identify level token
+#ifndef EXPORT_BUILD
+#define NTLMSSP_NEGOTIATE_STRONG_CRYPT 0x200000 // Allow string encryption
+#endif // EXPORT_BUILD
+
+//
+// Opaque message returned from first call to InitializeSecurityContext
+//
+typedef struct _NEGOTIATE_MESSAGE {
+ UCHAR Signature[sizeof(NTLMSSP_SIGNATURE)];
+ NTLM_MESSAGE_TYPE MessageType;
+ ULONG NegotiateFlags;
+ STRING OemDomainName;
+ STRING OemWorkstationName;
+} NEGOTIATE_MESSAGE, *PNEGOTIATE_MESSAGE;
+
+
+//
+// Old version of the message, for old clients
+//
+
+typedef struct _OLD_NEGOTIATE_MESSAGE {
+ UCHAR Signature[sizeof(NTLMSSP_SIGNATURE)];
+ NTLM_MESSAGE_TYPE MessageType;
+ ULONG NegotiateFlags;
+} OLD_NEGOTIATE_MESSAGE, *POLD_NEGOTIATE_MESSAGE;
+
+//
+// Opaque message returned from first call to AcceptSecurityContext
+//
+typedef struct _CHALLENGE_MESSAGE {
+ UCHAR Signature[sizeof(NTLMSSP_SIGNATURE)];
+ NTLM_MESSAGE_TYPE MessageType;
+ STRING TargetName;
+ ULONG NegotiateFlags;
+ UCHAR Challenge[MSV1_0_CHALLENGE_LENGTH];
+ ULONG ServerContextHandleLower;
+ ULONG ServerContextHandleUpper;
+} CHALLENGE_MESSAGE, *PCHALLENGE_MESSAGE;
+
+//
+// Old version of the challenge message
+//
+
+typedef struct _OLD_CHALLENGE_MESSAGE {
+ UCHAR Signature[sizeof(NTLMSSP_SIGNATURE)];
+ NTLM_MESSAGE_TYPE MessageType;
+ STRING TargetName;
+ ULONG NegotiateFlags;
+ UCHAR Challenge[MSV1_0_CHALLENGE_LENGTH];
+} OLD_CHALLENGE_MESSAGE, *POLD_CHALLENGE_MESSAGE;
+
+//
+// Opaque message returned from second call to InitializeSecurityContext
+//
+typedef struct _AUTHENTICATE_MESSAGE {
+ UCHAR Signature[sizeof(NTLMSSP_SIGNATURE)];
+ NTLM_MESSAGE_TYPE MessageType;
+ STRING LmChallengeResponse;
+ STRING NtChallengeResponse;
+ STRING DomainName;
+ STRING UserName;
+ STRING Workstation;
+ STRING SessionKey;
+ ULONG NegotiateFlags;
+} AUTHENTICATE_MESSAGE, *PAUTHENTICATE_MESSAGE;
+
+typedef struct _OLD_AUTHENTICATE_MESSAGE {
+ UCHAR Signature[sizeof(NTLMSSP_SIGNATURE)];
+ NTLM_MESSAGE_TYPE MessageType;
+ STRING LmChallengeResponse;
+ STRING NtChallengeResponse;
+ STRING DomainName;
+ STRING UserName;
+ STRING Workstation;
+} OLD_AUTHENTICATE_MESSAGE, *POLD_AUTHENTICATE_MESSAGE;
+
+//
+// Size of the largest message
+// (The largest message is the AUTHENTICATE_MESSAGE)
+//
+
+#define NTLMSSP_MAX_MESSAGE_SIZE (sizeof(AUTHENTICATE_MESSAGE) + \
+ LM_RESPONSE_LENGTH + \
+ NT_RESPONSE_LENGTH + \
+ (DNLEN + 1) * sizeof(WCHAR) + \
+ (UNLEN + 1) * sizeof(WCHAR) + \
+ (CNLEN + 1) * sizeof(WCHAR))
+
+
+
+////////////////////////////////////////////////////////////////////////
+//
+// Global Variables
+//
+////////////////////////////////////////////////////////////////////////
+
+//
+// Useful constants
+//
+
+EXTERN TimeStamp SspGlobalForever;
+
+//
+// Crit Sect to protect various globals in context.c
+//
+
+EXTERN CRITICAL_SECTION SspContextCritSect;
+
+
+
+//
+// The computername of the local system.
+//
+
+EXTERN WCHAR SspGlobalUnicodeComputerName[CNLEN + 1];
+EXTERN UNICODE_STRING SspGlobalUnicodeComputerNameString;
+EXTERN STRING SspGlobalOemComputerNameString;
+
+//
+// The domain name of the local system
+//
+
+EXTERN WCHAR SspGlobalUnicodePrimaryDomainName[DNLEN + 1];
+EXTERN UNICODE_STRING SspGlobalUnicodePrimaryDomainNameString;
+EXTERN STRING SspGlobalOemPrimaryDomainNameString;
+
+//
+// The TargetName of the local system
+//
+
+EXTERN NT_PRODUCT_TYPE SspGlobalNtProductType;
+EXTERN UNICODE_STRING SspGlobalTargetName;
+EXTERN STRING SspGlobalOemTargetName;
+EXTERN ULONG SspGlobalTargetFlags;
+
+EXTERN LSA_HANDLE SspGlobalLsaPolicyHandle;
+EXTERN PSID SspGlobalLocalSystemSid;
+EXTERN BOOLEAN SspGlobalEncryptionEnabled;
+
+////////////////////////////////////////////////////////////////////////
+//
+// Procedure Forwards
+//
+////////////////////////////////////////////////////////////////////////
+
+
+//
+// Procedure forwards from credhand.c
+//
+
+NTSTATUS
+SspCredentialInitialize(
+ VOID
+ );
+
+VOID
+SspCredentialTerminate(
+ VOID
+ );
+
+PSSP_CREDENTIAL
+SspCredentialReferenceCredential(
+ IN PCredHandle CredentialHandle,
+ IN PSSP_CLIENT_CONNECTION ClientConnection,
+ IN BOOLEAN DereferenceCredential,
+ IN BOOLEAN ForceRemoveCredential
+ );
+
+VOID
+SspCredentialDereferenceCredential(
+ PSSP_CREDENTIAL Credential
+ );
+
+SECURITY_STATUS
+SspCredentialGetPassword(
+ IN PSSP_CREDENTIAL Credential,
+ OUT PUNICODE_STRING Password
+ );
+
+
+//
+// Procedure forwards from context.c
+//
+
+
+NTSTATUS
+SspContextRegisterLogonProcess(
+ VOID
+ );
+
+VOID
+SspContextDeregisterLogonProcess(
+ VOID
+ );
+
+NTSTATUS
+SspContextInitialize(
+ VOID
+ );
+
+VOID
+SspContextTerminate(
+ VOID
+ );
+
+VOID
+SspCleanupRNG(VOID);
+
+VOID
+SspInitializeRNG(VOID);
+
+VOID
+SspGenerateRandomBits(
+ PUCHAR pRandomData,
+ LONG cRandomData
+ );
+
+#endif // ifndef _NTLMSSPI_INCLUDED_
diff --git a/private/net/svcdlls/ntlmssp/common/rng.c b/private/net/svcdlls/ntlmssp/common/rng.c
new file mode 100644
index 000000000..0729b910e
--- /dev/null
+++ b/private/net/svcdlls/ntlmssp/common/rng.c
@@ -0,0 +1,181 @@
+//+---------------------------------------------------------------------------
+//
+// Microsoft Windows
+// Copyright (C) Microsoft Corporation, 1992 - 1995.
+//
+// File: rng.c
+//
+// Contents:
+//
+// Classes:
+//
+// Functions:
+//
+// History: 8-15-95 RichardW Created
+// 2-8-96 MikeSw Copied to NTLMSSP from SSL
+//
+//----------------------------------------------------------------------------
+
+#include <ntlmcomn.h> // Include files common to Serice and DLL
+#include <ntlmsspi.h> // Data private to the common routines
+#include <rc4.h>
+
+
+#define RNG_BITS 64
+#define RNG_BYTES_PER_BATCH 768
+
+typedef struct _SourceBits {
+ ULONG Valid;
+ BYTE Bits[RNG_BITS];
+} SourceBits, * PSourceBits;
+
+typedef struct _RNG_State {
+ RTL_CRITICAL_SECTION csLock;
+ LONG Available;
+ struct RC4_KEYSTRUCT key;
+} RNG_State;
+
+
+
+#define SEED_ARRAY_SIZE 16
+
+
+RNG_State SspRNG;
+
+#define LockRNG(r) EnterCriticalSection( &r.csLock )
+#define UnlockRNG(r) LeaveCriticalSection( &r.csLock )
+
+
+#define ADD_BYTE(p, x) \
+ ((PSourceBits) p)->Bits[ ((PSourceBits) p)->Valid++ ] = x; \
+ if (((PSourceBits) p)->Valid >= RNG_BITS ) \
+ { \
+ goto SystemData_Exit; \
+ }
+
+VOID
+SspGetSystemData(
+ PSourceBits pBits
+ )
+{
+ LARGE_INTEGER liPerf;
+ SYSTEMTIME SystemTime;
+ MEMORYSTATUS Mem;
+ DWORD i;
+
+
+ GetLocalTime(&SystemTime);
+
+ ADD_BYTE(pBits, LOBYTE( SystemTime.wMilliseconds ) );
+
+ ADD_BYTE(pBits, HIBYTE( SystemTime.wMilliseconds ) );
+
+ ADD_BYTE(pBits, LOBYTE( SystemTime.wSecond ) ^ LOBYTE( SystemTime.wMinute ) );
+
+ if (QueryPerformanceCounter(&liPerf))
+ {
+ ADD_BYTE(pBits, LOBYTE( LOWORD( liPerf.LowPart ) ) ^ LOBYTE( LOWORD( liPerf.HighPart ) ) );
+ ADD_BYTE(pBits, HIBYTE( LOWORD( liPerf.LowPart ) ) ^ LOBYTE( LOWORD( liPerf.HighPart ) ) );
+ ADD_BYTE(pBits, LOBYTE( HIWORD( liPerf.LowPart ) ) ^ LOBYTE( LOWORD( liPerf.HighPart ) ) );
+ ADD_BYTE(pBits, HIBYTE( HIWORD( liPerf.LowPart ) ) ^ LOBYTE( LOWORD( liPerf.HighPart ) ) );
+ }
+
+ GlobalMemoryStatus( &Mem );
+
+ ADD_BYTE(pBits, LOBYTE( HIWORD( Mem.dwAvailPhys ) ) );
+ ADD_BYTE(pBits, HIBYTE( HIWORD( Mem.dwAvailPhys ) ) );
+ ADD_BYTE(pBits, LOBYTE( HIWORD( Mem.dwAvailPageFile ) ) );
+ ADD_BYTE(pBits, HIBYTE( HIWORD( Mem.dwAvailPageFile ) ) );
+ ADD_BYTE(pBits, LOBYTE( HIWORD( Mem.dwAvailVirtual ) ) );
+
+ //
+ // TODO: Add other things, like cache manager, heap frag, etc.
+ //
+
+SystemData_Exit:
+
+ for (i = 0 ; i < pBits->Valid ; i++ )
+ {
+ pBits->Bits[ i ] ^= pBits->Bits[ pBits->Valid - i ];
+ }
+
+}
+
+VOID
+SspGenerateRandomBits(
+ PUCHAR pRandomData,
+ LONG cRandomData
+ )
+{
+ LONG i;
+ DWORD Bytes;
+ SourceBits Bits;
+
+ ZeroMemory( pRandomData, cRandomData );
+
+ LockRNG( SspRNG );
+
+ if (cRandomData > SspRNG.Available)
+ {
+ Bytes = SspRNG.Available;
+ }
+ else
+ {
+ Bytes = cRandomData;
+ }
+
+ cRandomData -= Bytes;
+
+ if (Bytes)
+ {
+ rc4( &SspRNG.key, Bytes, pRandomData );
+
+ SspRNG.Available -= Bytes;
+ }
+
+ if (cRandomData)
+ {
+ pRandomData += Bytes;
+
+ Bits.Valid = 0;
+
+ SspGetSystemData( &Bits );
+
+ ZeroMemory( &SspRNG.key, sizeof( SspRNG.key ) );
+
+ rc4_key( &SspRNG.key, Bits.Valid, Bits.Bits );
+
+ rc4( &SspRNG.key, cRandomData, pRandomData );
+
+ SspRNG.Available = RNG_BYTES_PER_BATCH - cRandomData ;
+
+ if (SspRNG.Available < 0)
+ {
+ SspRNG.Available = 0;
+ }
+
+ }
+
+ UnlockRNG( SspRNG );
+
+}
+
+
+VOID
+SspInitializeRNG(VOID)
+{
+ InitializeCriticalSection( &SspRNG.csLock );
+
+ LockRNG( SspRNG );
+
+ SspRNG.Available = 0;
+
+ UnlockRNG( SspRNG );
+
+}
+
+VOID
+SspCleanupRNG(VOID)
+{
+ DeleteCriticalSection(&SspRNG.csLock);
+}
diff --git a/private/net/svcdlls/ntlmssp/common/sources.inc b/private/net/svcdlls/ntlmssp/common/sources.inc
new file mode 100644
index 000000000..9c1f4160e
--- /dev/null
+++ b/private/net/svcdlls/ntlmssp/common/sources.inc
@@ -0,0 +1,122 @@
+!IF 0
+
+Copyright (c) 1989-92 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+Author:
+
+ Steve Wood (stevewo) 12-Apr-1990
+
+NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl
+
+!ENDIF
+
+#
+# The MAJORCOMP and MINORCOMP variables are defined
+# so that $(MAJORCOMP)$(MINORCOMP)filename can be used in
+# cross compiling to provide unique filenames in a flat namespace.
+#
+
+MAJORCOMP=net
+MINORCOMP=ntlmssp
+
+#
+# The TARGETNAME variable is defined by the developer. It is the name of
+# the target (component) that is being built by this makefile. It
+# should NOT include any path or file extension information.
+#
+
+TARGETNAME=ntlmcomn
+
+#
+# The TARGETPATH and TARGETTYPE variables are defined by the developer.
+# The first specifies where the target is to be build. The second specifies
+# the type of target (either PROGRAM, DYNLINK, LIBRARY, UMAPPL_NOLIB or
+# BOOTPGM). UMAPPL_NOLIB is used when you're only building user-mode
+# apps and don't need to build a library.
+#
+
+TARGETPATH=obj
+
+# Pick one of the following and delete the others
+TARGETTYPE=LIBRARY
+
+
+#
+# The TARGETLIBS specifies additional libraries to link with you target
+# image. Each library path specification should contain an asterisk (*)
+# where the machine specific subdirectory name should go.
+#
+
+#//TARGETLIBS= \
+#// $(BASEDIR)\public\sdk\lib\*\netlib.lib \
+#// $(BASEDIR)\public\sdk\lib\*\netapi32.lib \
+# $(BASEDIR)\public\sdk\lib\*\lsadll.lib \
+
+#
+# The INCLUDES variable specifies any include paths that are specific to
+# this source directory. Separate multiple directory paths with single
+# semicolons. Relative path specifications are okay.
+#
+
+INCLUDES=..;..\..;..\..\..\..\inc;..\..\..\..\..\inc;..\..\..\..\..\lsa\crypt\engine
+
+
+C_DEFINES=-DSECURITY_WIN32
+MSC_WARNING_LEVEL=/W3 /WX
+
+#
+# The SOURCES variable is defined by the developer. It is a list of all the
+# source files for this component. Each source file should be on a separate
+# line using the line continuation character. This will minimize merge
+# conflicts if two developers adding source files to the same component.
+#
+
+
+!IFNDEF DISABLE_NET_UNICODE
+UNICODE=1
+NET_C_DEFINES=-DUNICODE
+!ENDIF
+
+SOURCES= \
+ ..\context.c \
+ ..\credhand.c \
+ ..\initcomn.c \
+ ..\utility.c \
+ ..\encrypt.c \
+ ..\rng.c
+
+
+
+#
+# Next specify options for the compiler.
+#
+
+
+#
+# This line makes the netlogon.dll to use crtdll.dll instead of libc.lib
+#
+
+USE_CRTDLL=1
+
+
+#
+# Next specify one or more user mode test programs and their type
+# UMTEST is used for optional test programs. UMAPPL is used for
+# programs that always get built when the directory is built.
+#
+
+#UMTYPE=console
+#UMAPPL=ssptest
+#UMLIBS= \
+# $(BASEDIR)\public\sdk\lib\*\security.lib
diff --git a/private/net/svcdlls/ntlmssp/common/utility.c b/private/net/svcdlls/ntlmssp/common/utility.c
new file mode 100644
index 000000000..8fc6d81fd
--- /dev/null
+++ b/private/net/svcdlls/ntlmssp/common/utility.c
@@ -0,0 +1,633 @@
+/*++
+
+Copyright (c) 1987-1993 Microsoft Corporation
+
+Module Name:
+
+ utility.c
+
+Abstract:
+
+ Private NtLmSsp service utility routines.
+
+Author:
+
+ Cliff Van Dyke (cliffv) 9-Jun-1993
+
+Environment:
+
+ User mode only.
+ Contains NT-specific code.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+--*/
+
+//
+// Common include files.
+//
+
+#include <ntlmcomn.h> // Common definitions for DLL and SERVICE
+#include <ntlmsspi.h> // Data private to the common routines
+
+//
+// Include files specific to this .c file
+//
+
+#include <netlib.h> // NetpMemoryFree()
+#include <secobj.h> // ACE_DATA ...
+#include <stdio.h> // vsprintf().
+#include <tstr.h> // TCHAR_ equates.
+
+#define SSP_TOKEN_ACCESS (READ_CONTROL |\
+ WRITE_DAC |\
+ TOKEN_DUPLICATE |\
+ TOKEN_IMPERSONATE |\
+ TOKEN_QUERY |\
+ TOKEN_QUERY_SOURCE |\
+ TOKEN_ADJUST_PRIVILEGES |\
+ TOKEN_ADJUST_GROUPS |\
+ TOKEN_ADJUST_DEFAULT)
+
+
+
+
+SECURITY_STATUS
+SspNtStatusToSecStatus(
+ IN NTSTATUS NtStatus,
+ IN SECURITY_STATUS DefaultStatus
+ )
+/*++
+
+Routine Description:
+
+ Convert an NtStatus code to the corresponding Security status code
+
+Arguments:
+
+ NtStatus - NT status to convert
+
+Return Value:
+
+ Returns security status code.
+
+--*/
+{
+
+ switch(NtStatus){
+ case STATUS_NO_MEMORY:
+ return SEC_E_INSUFFICIENT_MEMORY;
+
+ case STATUS_SUCCESS:
+ return STATUS_SUCCESS;
+
+ default:
+ if ( DefaultStatus != 0 ) {
+ return DefaultStatus;
+ } else {
+ return NtStatus;
+ }
+
+ }
+ ASSERT(FALSE);
+}
+
+
+BOOLEAN
+SspTimeHasElapsed(
+ IN LARGE_INTEGER StartTime,
+ IN DWORD Timeout
+ )
+/*++
+
+Routine Description:
+
+ Determine if "Timeout" milliseconds have elapsed since StartTime.
+
+Arguments:
+
+ StartTime - Specifies an absolute time when the event started (100ns units).
+
+ Timeout - Specifies a relative time in milliseconds. 0xFFFFFFFF indicates
+ that the time will never expire.
+
+Return Value:
+
+ TRUE -- iff Timeout milliseconds have elapsed since StartTime.
+
+--*/
+{
+ LARGE_INTEGER TimeNow;
+ LARGE_INTEGER ElapsedTime;
+ LARGE_INTEGER Period;
+
+ //
+ // If the period to too large to handle (i.e., 0xffffffff is forever),
+ // just indicate that the timer has not expired.
+ //
+ // (0x7fffffff is a little over 24 days).
+ //
+
+ if ( Timeout> 0x7fffffff ) {
+ return FALSE;
+ }
+
+ //
+ // Compute the elapsed time
+ //
+
+ NtQuerySystemTime( &TimeNow );
+ ElapsedTime.QuadPart = TimeNow.QuadPart - StartTime.QuadPart;
+
+ //
+ // Convert Timeout from milliseconds into 100ns units.
+ //
+
+ Period.QuadPart = Int32x32To64( (LONG)Timeout, 10000 );
+
+
+ //
+ // If the elapsed time is negative (totally bogus),
+ // or greater than the maximum allowed,
+ // indicate the period has elapsed.
+ //
+
+ if ( ElapsedTime.QuadPart < 0 || ElapsedTime.QuadPart > Period.QuadPart ) {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+SECURITY_STATUS
+SspGetLogonId (
+ OUT PLUID LogonId,
+ OUT PHANDLE ReturnedTokenHandle OPTIONAL
+ )
+
+/*++
+
+Routine Description:
+
+ This routine gets the Logon Id of token of the calling thread.
+
+Arguments:
+
+ LogonId - Returns the Logon Id of the calling thread.
+
+ ReturnedTokenHandle - Optionally returns a token handle to an
+ impersonation token for the calling thread.
+
+Return Status:
+
+ STATUS_SUCCESS - Indicates the routine completed successfully.
+
+
+--*/
+
+{
+ NTSTATUS Status;
+ HANDLE NullImpersonationToken = NULL;
+ HANDLE TokenHandle = NULL;
+ PTOKEN_STATISTICS TokenStatisticsInfo = NULL;
+ ULONG TokenStatisticsInfoSize;
+
+ //
+ // Open the token,
+ //
+
+ Status = NtOpenThreadToken(
+ NtCurrentThread(),
+ TOKEN_DUPLICATE | TOKEN_QUERY | (ReturnedTokenHandle == NULL ? 0 : WRITE_DAC),
+ (BOOLEAN) TRUE, // Use the logon service's security context
+ // to open the token
+ &TokenHandle );
+
+ if ( Status == STATUS_NO_TOKEN ) {
+
+
+ Status = NtOpenProcessToken(
+ NtCurrentProcess(),
+ TOKEN_QUERY | TOKEN_DUPLICATE,
+ &TokenHandle );
+ }
+
+ if ( !NT_SUCCESS( Status )) {
+ goto Cleanup;
+ }
+
+
+ //
+ // Get the LogonId from the token.
+ //
+
+ Status = NtQueryInformationToken(
+ TokenHandle,
+ TokenStatistics,
+ &TokenStatisticsInfo,
+ 0,
+ &TokenStatisticsInfoSize );
+
+ if ( Status != STATUS_BUFFER_TOO_SMALL ) {
+ goto Cleanup;
+ }
+
+ TokenStatisticsInfo = LocalAlloc( 0, TokenStatisticsInfoSize );
+
+ if ( TokenStatisticsInfo == NULL ) {
+ Status = STATUS_NO_MEMORY;
+ goto Cleanup;
+ }
+
+ Status = NtQueryInformationToken(
+ TokenHandle,
+ TokenStatistics,
+ TokenStatisticsInfo,
+ TokenStatisticsInfoSize,
+ &TokenStatisticsInfoSize );
+
+ if ( !NT_SUCCESS(Status) ) {
+ goto Cleanup;
+ }
+
+ *LogonId = TokenStatisticsInfo->AuthenticationId;
+ Status = STATUS_SUCCESS;
+
+
+ //
+ // Clean up locally used resources.
+ //
+Cleanup:
+
+ //
+ // Return the token handle to the caller (if requested)
+ //
+
+ if ( ReturnedTokenHandle != NULL ) {
+ if ( !NT_SUCCESS(Status) ) {
+ *ReturnedTokenHandle = NULL;
+ } else {
+ *ReturnedTokenHandle = TokenHandle;
+ TokenHandle = NULL;
+ }
+ }
+
+
+ if ( TokenHandle != NULL ) {
+ (VOID) NtClose( TokenHandle );
+ }
+
+ if ( TokenStatisticsInfo != NULL ) {
+ (VOID) LocalFree( TokenStatisticsInfo );
+ }
+
+ return SspNtStatusToSecStatus( Status, SEC_E_NO_CREDENTIALS );
+}
+
+
+
+
+VOID
+SspGetPrimaryDomainNameAndTargetName(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ Ensure SspGlobalTargetName is up to date.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ NTSTATUS Status;
+ PPOLICY_PRIMARY_DOMAIN_INFO PrimaryDomain = NULL;
+
+ //
+ // If this is an Advanced Server and we've already read the domain name,
+ // just return now.
+ //
+
+ if ( SspGlobalNtProductType == NtProductLanManNt &&
+ SspGlobalTargetName.Length != 0 ) {
+ return;
+ }
+
+ //
+ // Open the Lsa Policy database if it isn't yet open.
+ //
+
+ if ( SspGlobalLsaPolicyHandle == NULL ) {
+ OBJECT_ATTRIBUTES ObjectAttributes;
+
+ InitializeObjectAttributes( &ObjectAttributes, NULL, 0, NULL, NULL );
+
+ Status = LsaOpenPolicy( NULL,
+ &ObjectAttributes,
+ POLICY_VIEW_LOCAL_INFORMATION,
+ &SspGlobalLsaPolicyHandle );
+
+ if ( !NT_SUCCESS(Status) ) {
+ SspGlobalLsaPolicyHandle = NULL;
+ return;
+ }
+
+ }
+
+
+ //
+ // Get the PrimaryDomain information
+ //
+
+ Status = LsaQueryInformationPolicy(
+ SspGlobalLsaPolicyHandle,
+ PolicyPrimaryDomainInformation,
+ &PrimaryDomain );
+
+ if ( NT_SUCCESS(Status) ) {
+
+ //
+ // Save the primary domain name.
+ //
+
+ wcsncpy( SspGlobalUnicodePrimaryDomainName,
+ PrimaryDomain->Name.Buffer,
+ DNLEN+1 );
+ SspGlobalUnicodePrimaryDomainName[DNLEN] = L'\0';
+
+ RtlInitUnicodeString( &SspGlobalUnicodePrimaryDomainNameString,
+ SspGlobalUnicodePrimaryDomainName );
+
+ Status = RtlUpcaseUnicodeStringToOemString(
+ &SspGlobalOemPrimaryDomainNameString,
+ &SspGlobalUnicodePrimaryDomainNameString,
+ TRUE );
+ if ( !NT_SUCCESS(Status) ) {
+ RtlInitString( &SspGlobalOemPrimaryDomainNameString, NULL );
+ }
+
+
+ //
+ // If this is a standalone windows NT workstation,
+ // use the computer name as the Target name.
+ //
+
+ if ( PrimaryDomain->Sid == NULL ) {
+ SspGlobalTargetName = SspGlobalUnicodeComputerNameString;
+ SspGlobalOemTargetName = SspGlobalOemComputerNameString;
+ SspGlobalTargetFlags = NTLMSSP_TARGET_TYPE_SERVER;
+ } else {
+ SspGlobalTargetName = SspGlobalUnicodePrimaryDomainNameString;
+ SspGlobalOemTargetName = SspGlobalOemPrimaryDomainNameString;
+ SspGlobalTargetFlags = NTLMSSP_TARGET_TYPE_DOMAIN;
+ }
+
+ }
+
+ //
+ // Close the Lsa Policy Database if we'll never use it again.
+ //
+
+ if ( SspGlobalNtProductType == NtProductLanManNt ) {
+ (VOID) LsaClose( SspGlobalLsaPolicyHandle );
+ SspGlobalLsaPolicyHandle = NULL;
+ }
+
+
+
+}
+
+
+
+SECURITY_STATUS
+SspDuplicateToken(
+ IN HANDLE OriginalToken,
+ IN SECURITY_IMPERSONATION_LEVEL ImpersonationLevel,
+ OUT PHANDLE DuplicatedToken
+ )
+/*++
+
+Routine Description:
+
+ Duplicates a token
+
+Arguments:
+
+ OriginalToken - Token to duplicate
+ DuplicatedToken - Receives handle to duplicated token
+
+Return Value:
+
+ Any error from NtDuplicateToken
+
+--*/
+{
+ NTSTATUS Status;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ SECURITY_QUALITY_OF_SERVICE QualityOfService;
+
+ InitializeObjectAttributes(
+ &ObjectAttributes,
+ NULL,
+ 0,
+ NULL,
+ NULL
+ );
+
+ QualityOfService.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
+ QualityOfService.EffectiveOnly = FALSE;
+ QualityOfService.ContextTrackingMode = SECURITY_STATIC_TRACKING;
+ QualityOfService.ImpersonationLevel = ImpersonationLevel;
+ ObjectAttributes.SecurityQualityOfService = &QualityOfService;
+
+ Status = NtDuplicateToken(
+ OriginalToken,
+ SSP_TOKEN_ACCESS,
+ &ObjectAttributes,
+ FALSE,
+ TokenImpersonation,
+ DuplicatedToken
+ );
+
+ return(SspNtStatusToSecStatus(Status, SEC_E_NO_IMPERSONATION));
+}
+
+
+LPWSTR
+SspAllocWStrFromWStr(
+ IN LPWSTR Unicode
+ )
+
+/*++
+
+Routine Description:
+
+ Allocate and copy unicode string (wide character strdup)
+
+Arguments:
+
+ Unicode - pointer to wide character string to make copy of
+
+Return Value:
+
+ NULL - There was some error in the conversion.
+
+ Otherwise, it returns a pointer to the zero terminated UNICODE string in
+ an allocated buffer. The buffer must be freed using LocalFree.
+
+--*/
+
+{
+ DWORD Size;
+ LPWSTR ptr;
+
+ Size = WCSSIZE(Unicode);
+ ptr = LocalAlloc(0, Size);
+ if ( ptr != NULL) {
+ RtlCopyMemory(ptr, Unicode, Size);
+ }
+ return ptr;
+}
+
+
+SECURITY_STATUS
+SspDuplicateUnicodeString(
+ OUT PUNICODE_STRING Destination,
+ IN PUNICODE_STRING Source
+ )
+/*++
+
+Routine Description:
+
+ Allocate and copy unicode string
+
+Arguments:
+
+ Destination - Receives duplicate string, allocated with LocalAlloc.
+
+ Source - Contains source unicode string.
+
+Return Value:
+
+ SEC_E_INSUFFICIENT_MEMORY - The routine was unable to allocate the
+ output string.
+
+
+--*/
+
+{
+ if (Source == NULL) {
+ Destination->Buffer = NULL;
+ Destination->Length = Destination->MaximumLength = 0;
+ return(SEC_E_OK);
+ }
+
+ Destination->Buffer = (LPWSTR) LocalAlloc(0, Source->Length + sizeof(WCHAR));
+ if (Destination->Buffer == NULL) {
+ return(SEC_E_INSUFFICIENT_MEMORY);
+ }
+
+ Destination->Length = Source->Length;
+ Destination->MaximumLength = Source->Length + sizeof(WCHAR);
+ RtlCopyMemory(
+ Destination->Buffer,
+ Source->Buffer,
+ Source->Length
+ );
+ Destination->Buffer[Source->Length/sizeof(WCHAR)] = L'\0';
+
+ return(SEC_E_OK);
+}
+
+
+VOID
+SspHidePassword(
+ IN OUT PUNICODE_STRING Password
+ )
+/*++
+
+Routine Description:
+
+ Run-encodes the password so that it is not very visually
+ distinguishable. This is so that if it makes it to a
+ paging file, it wont be obvious.
+
+
+ WARNING - This routine will use the upper portion of the
+ password's length field to store the seed used in encoding
+ password. Be careful you don't pass such a string to
+ a routine that looks at the length (like and RPC routine).
+
+Arguments:
+
+ Seed - The seed to use to hide the password.
+
+ PasswordSource - Contains password to hide.
+
+Return Value:
+
+
+--*/
+{
+ PSECURITY_SEED_AND_LENGTH SeedAndLength;
+ UCHAR LocalSeed;
+
+ LocalSeed = 0;
+
+
+ SeedAndLength = (PSECURITY_SEED_AND_LENGTH)&Password->Length;
+ //ASSERT(*((LPWCH)SeedAndLength+Password->Length) == 0);
+ ASSERT((SeedAndLength->Seed) == 0);
+
+ RtlRunEncodeUnicodeString(
+ &LocalSeed,
+ Password
+ );
+
+ SeedAndLength->Seed = LocalSeed;
+}
+
+
+VOID
+SspRevealPassword(
+ IN OUT PUNICODE_STRING HiddenPassword
+ )
+/*++
+
+Routine Description
+
+ Reveals a previously hidden password so that it
+ is plain text once again.
+
+Arguments:
+
+ HiddenPassword - Contains the password to reveal
+
+Return Value
+
+--*/
+{
+ PSECURITY_SEED_AND_LENGTH SeedAndLength;
+
+ UCHAR Seed;
+
+ SeedAndLength = (PSECURITY_SEED_AND_LENGTH)&HiddenPassword->Length;
+ Seed = SeedAndLength->Seed;
+ SeedAndLength->Seed = 0;
+
+ RtlRunDecodeUnicodeString(
+ Seed,
+ HiddenPassword
+ );
+
+}
+
diff --git a/private/net/svcdlls/ntlmssp/debug.h b/private/net/svcdlls/ntlmssp/debug.h
new file mode 100644
index 000000000..cef7630eb
--- /dev/null
+++ b/private/net/svcdlls/ntlmssp/debug.h
@@ -0,0 +1,142 @@
+/*++
+
+Copyright (c) 1987-1993 Microsoft Corporation
+
+Module Name:
+
+ debug.h
+
+Abstract:
+
+ NtLmSsp service debug support
+
+Author:
+
+ Ported from Lan Man 2.0
+
+Revision History:
+
+ 21-May-1991 (cliffv)
+ Ported to NT. Converted to NT style.
+ 09-Apr-1992 JohnRo
+ Prepare for WCHAR.H (_wcsicmp vs _wcscmpi, etc).
+
+--*/
+
+//
+// init.c will #include this file with DEBUG_ALLOCATE defined.
+// That will cause each of these variables to be allocated.
+//
+#ifdef DEBUG_ALLOCATE
+#define EXTERN
+#else
+#define EXTERN extern
+#endif
+
+
+////////////////////////////////////////////////////////////////////////
+//
+// Debug Definititions
+//
+////////////////////////////////////////////////////////////////////////
+
+#define SSP_INIT 0x00000001 // Initialization
+#define SSP_MISC 0x00000002 // Misc debug
+#define SSP_API 0x00000004 // API processing
+#define SSP_LPC 0x00000008 // LPC
+#define SSP_CRITICAL 0x00000100 // Only real important errors
+
+//
+// Very verbose bits
+//
+
+#define SSP_API_MORE 0x04000000 // verbose API
+#define SSP_LPC_MORE 0x08000000 // verbose LPC
+
+//
+// Control bits.
+//
+
+#define SSP_NO_LOCAL 0x10000000 // Force client to use OEM character set
+#define SSP_TIMESTAMP 0x20000000 // TimeStamp each output line
+#define SSP_REQUEST_TARGET 0x40000000 // Force client to ask for target name
+#define SSP_USE_OEM 0x80000000 // Force client to use OEM character set
+
+
+//
+// Name and directory of log file
+//
+
+#define DEBUG_DIR L"\\debug"
+#define DEBUG_FILE L"\\ntlmssp.log"
+#define DEBUG_BAK_FILE L"\\ntlmssp.bak"
+
+#if DBG
+
+EXTERN DWORD SspGlobalDbflag;
+
+#define IF_DEBUG(Function) \
+ if (SspGlobalDbflag & SSP_ ## Function)
+
+#define SspPrint(_x_) SspPrintRoutine _x_
+
+VOID
+SspPrintRoutine(
+ IN DWORD DebugFlag,
+ IN LPSTR FORMATSTRING, // PRINTF()-STYLE FORMAT STRING.
+ ... // OTHER ARGUMENTS ARE POSSIBLE.
+ );
+
+VOID
+SspDumpHexData(
+ IN DWORD DebugFlag,
+ IN LPDWORD Buffer,
+ IN DWORD BufferSize
+ );
+
+VOID
+SspDumpSid(
+ IN DWORD DebugFlag,
+ IN PSID Sid
+ );
+
+VOID
+SspDumpBuffer(
+ IN DWORD DebugFlag,
+ IN PVOID Buffer,
+ IN DWORD BufferSize
+ );
+
+VOID
+SspOpenDebugFile(
+ IN BOOL ReopenFlag
+ );
+
+//
+// Debug log file
+//
+
+EXTERN HANDLE SspGlobalLogFile;
+#define DEFAULT_MAXIMUM_LOGFILE_SIZE 20000000
+EXTERN DWORD SspGlobalLogFileMaxSize;
+
+//
+// To serialize access to log file.
+//
+
+EXTERN CRITICAL_SECTION SspGlobalLogFileCritSect;
+EXTERN LPWSTR SspGlobalDebugSharePath;
+
+#else
+
+#define IF_DEBUG(Function) if (FALSE)
+
+// Nondebug version.
+#define SspDumpHexData /* no output; ignore arguments */
+#define SspDumpBuffer
+#define SspDumpSid
+#define SspPrint(_x_)
+
+#endif // DBG
+
+#undef EXTERN
diff --git a/private/net/svcdlls/ntlmssp/dirs b/private/net/svcdlls/ntlmssp/dirs
new file mode 100644
index 000000000..bcb71eecc
--- /dev/null
+++ b/private/net/svcdlls/ntlmssp/dirs
@@ -0,0 +1,49 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ dirs.
+
+Abstract:
+
+ This file specifies the subdirectories of the current directory that
+ contain component makefiles.
+
+
+Author:
+
+ Steve Wood (stevewo) 17-Apr-1990
+
+NOTE: Commented description of this file is in \nt\bak\bin\dirs.tpl
+
+!ENDIF
+
+#
+# This macro is defined by the developer. It is a list of all subdirectories
+# that build required components. Each subdirectory should be on a separate
+# line using the line continuation character. This will minimize merge
+# conflicts if two developers adding source files to the same component.
+# The order of the directories is the order that they will be built when
+# doing a build.
+#
+
+DIRS= \
+ package \
+ common \
+ client \
+ server
+
+
+#
+# This macro is defined by the developer. It is a list of all subdirectories
+# that build optional components. Each subdirectory should be on a separate
+# line using the line continuation character. This will minimize merge
+# conflicts if two developers adding source files to the same component.
+# The order of the directories is the order that they will be built when
+# doing a build.
+#
+
+OPTIONAL_DIRS= hello
+
diff --git a/private/net/svcdlls/ntlmssp/hello/client/helloc.c b/private/net/svcdlls/ntlmssp/hello/client/helloc.c
new file mode 100644
index 000000000..7f798c214
--- /dev/null
+++ b/private/net/svcdlls/ntlmssp/hello/client/helloc.c
@@ -0,0 +1,190 @@
+/****************************************************************************
+ Microsoft RPC Version 1`1
+ Copyright Microsoft Corp. 1992
+ Hello Example
+
+ FILE: helloc.c
+ USAGE: client -n network_address
+ -p protocol_sequence
+ -e endpoint
+ -o options
+ -u uuid
+
+ PURPOSE: Client side of RPC distributed application
+ FUNCTIONS: main() - binds to server and calls remote procedure
+ COMMENTS:
+ This distributed application prints a string such as "hello, world"
+ on the server. The client manages its connection to the server.
+ The client uses the binding handle hello_IfHandle defined in the
+ file hello.h.
+
+****************************************************************************/
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <rpc.h> // RPC API functions, types
+#include "hello.h" // header file generated by MIDL compiler
+
+void Usage(char * pszProgramName)
+{
+ fprintf(stderr, "Usage: %s\n", pszProgramName);
+ fprintf(stderr, " -p protocol_sequence\n");
+ fprintf(stderr, " -n network_address\n");
+ fprintf(stderr, " -e endpoint\n");
+ fprintf(stderr, " -o options\n");
+ fprintf(stderr, " -u uuid\n");
+ fprintf(stderr, " -s string\n");
+ exit(1);
+}
+
+int _CRTAPI1
+main (argc, argv)
+ int argc;
+ char *argv[];
+{
+ RPC_STATUS status; // returned by RPC API function
+ unsigned char * pszUuid = "12345678-1234-1234-1234-123456789ABC";
+ unsigned char * pszProtocolSequence = "ncacn_np";
+ unsigned char * pszNetworkAddress = NULL;
+ unsigned char * pszEndpoint = "\\pipe\\hello";
+ unsigned char * pszOptions = NULL;
+ unsigned char * pszStringBinding = NULL;
+ unsigned char * pszString = "hello, world";
+ int i;
+
+ // allow the user to override settings with command line switches
+ for (i = 1; i < argc; i++) {
+ if ((*argv[i] == '-') || (*argv[i] == '/')) {
+ switch (tolower(*(argv[i]+1))) {
+ case 'p': // protocol sequence
+ pszProtocolSequence = argv[++i];
+ break;
+ case 'n': // network address
+ pszNetworkAddress = argv[++i];
+ break;
+ case 'e':
+ pszEndpoint = argv[++i];
+ break;
+ case 'o':
+ pszOptions = argv[++i];
+ break;
+ case 'u':
+ pszUuid = argv[++i];
+ break;
+ case 's':
+ pszString = argv[++i];
+ break;
+ case 'h':
+ case '?':
+ default:
+ Usage(argv[0]);
+ }
+ } else {
+ Usage(argv[0]);
+ }
+ }
+
+
+ // Use a convenience function to concatenate the elements of
+ // the string binding into the proper sequence.
+
+ status = RpcStringBindingCompose(pszUuid,
+ pszProtocolSequence,
+ pszNetworkAddress,
+ pszEndpoint,
+ pszOptions,
+ &pszStringBinding);
+
+ if (status) {
+ printf("RpcStringBindingCompose returned 0x%x\n", status);
+ exit(2);
+ }
+ printf("pszStringBinding = %s\n", pszStringBinding);
+
+
+
+ //
+ // Set the binding handle that will be used to bind to the server.
+ //
+
+ status = RpcBindingFromStringBinding(pszStringBinding,
+ &hello_IfHandle);
+ if (status) {
+ printf("RpcBindingFromStringBinding returned 0x%x\n", status);
+ exit(2);
+ }
+
+
+ //
+ // Tell RPC to do the security thing.
+ //
+
+ status = RpcBindingSetAuthInfo(
+ hello_IfHandle,
+ "frank",
+ RPC_C_AUTHN_LEVEL_CONNECT,
+ 10,
+ NULL,
+ RPC_C_AUTHZ_NAME );
+
+ if ( status ) {
+ printf("RpcBindingSetAuthInfo returned %ld\n", status);
+ exit(2);
+ }
+
+
+ //
+ // Do the actual RPC calls to the server.
+ //
+
+ printf(" print the string '%s' on the server\n", pszString);
+
+ RpcTryExcept {
+ int i;
+ for ( i=0; i<100 ; i++ ) {
+ HelloProc(pszString); // make call with user message
+ }
+ Shutdown(); // shut down the server side
+ } RpcExcept(1) {
+ printf("Runtime library reported an exception 0x%lx\n",
+ RpcExceptionCode());
+ } RpcEndExcept
+
+
+
+ // The calls to the remote procedures are complete.
+ // Free the string and the binding handle
+
+ status = RpcStringFree(&pszStringBinding); // remote calls done; unbind
+ if (status) {
+ printf("RpcStringFree returned 0x%x\n", status);
+ exit(2);
+ }
+
+ status = RpcBindingFree(&hello_IfHandle); // remote calls done; unbind
+ if (status) {
+ printf("RpcBindingFree returned 0x%x\n", status);
+ exit(2);
+ }
+
+
+ return 0;
+
+}
+
+// ====================================================================
+// MIDL allocate and free
+// ====================================================================
+
+
+void __RPC_FAR * __RPC_API MIDL_user_allocate(size_t len)
+{
+ return(malloc(len));
+}
+
+void __RPC_API MIDL_user_free(void __RPC_FAR * ptr)
+{
+ free(ptr);
+}
+
+/* end file helloc.c */
diff --git a/private/net/svcdlls/ntlmssp/hello/client/makefile b/private/net/svcdlls/ntlmssp/hello/client/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/net/svcdlls/ntlmssp/hello/client/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/net/svcdlls/ntlmssp/hello/client/sources b/private/net/svcdlls/ntlmssp/hello/client/sources
new file mode 100644
index 000000000..d142d9584
--- /dev/null
+++ b/private/net/svcdlls/ntlmssp/hello/client/sources
@@ -0,0 +1,48 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+
+Author:
+
+ Steve Wood (stevewo) 12-Apr-1990
+
+NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl
+
+!ENDIF
+
+MAJORCOMP=net
+MINORCOMP=helloc
+
+TARGETNAME=helloc
+TARGETPATH=obj
+TARGETTYPE=LIBRARY
+
+
+UMTYPE=console
+UMAPPL=helloc
+UMLIBS= \
+ obj\*\helloc.lib \
+ $(BASEDIR)\Public\Sdk\Lib\*\rpcrt4.lib \
+
+
+UNICODE=1
+
+USE_CRTDLL=1
+
+INCLUDES=..;
+
+SOURCES= \
+ hello_c.c
+
diff --git a/private/net/svcdlls/ntlmssp/hello/dirs b/private/net/svcdlls/ntlmssp/hello/dirs
new file mode 100644
index 000000000..ec9b364cf
--- /dev/null
+++ b/private/net/svcdlls/ntlmssp/hello/dirs
@@ -0,0 +1,47 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ dirs.
+
+Abstract:
+
+ This file specifies the subdirectories of the current directory that
+ contain component makefiles.
+
+
+Author:
+
+ Steve Wood (stevewo) 17-Apr-1990
+
+NOTE: Commented description of this file is in \nt\bak\bin\dirs.tpl
+
+!ENDIF
+
+#
+# This macro is defined by the developer. It is a list of all subdirectories
+# that build required components. Each subdirectory should be on a separate
+# line using the line continuation character. This will minimize merge
+# conflicts if two developers adding source files to the same component.
+# The order of the directories is the order that they will be built when
+# doing a build.
+#
+
+DIRS= \
+ server \
+ client
+
+
+#
+# This macro is defined by the developer. It is a list of all subdirectories
+# that build optional components. Each subdirectory should be on a separate
+# line using the line continuation character. This will minimize merge
+# conflicts if two developers adding source files to the same component.
+# The order of the directories is the order that they will be built when
+# doing a build.
+#
+
+# OPTIONAL_DIRS= hello
+
diff --git a/private/net/svcdlls/ntlmssp/hello/hello.acf b/private/net/svcdlls/ntlmssp/hello/hello.acf
new file mode 100644
index 000000000..e599df996
--- /dev/null
+++ b/private/net/svcdlls/ntlmssp/hello/hello.acf
@@ -0,0 +1,5 @@
+[implicit_handle(handle_t hello_IfHandle)]
+interface hello
+{
+
+}
diff --git a/private/net/svcdlls/ntlmssp/hello/hello.idl b/private/net/svcdlls/ntlmssp/hello/hello.idl
new file mode 100644
index 000000000..dafd86ad2
--- /dev/null
+++ b/private/net/svcdlls/ntlmssp/hello/hello.idl
@@ -0,0 +1,8 @@
+[ uuid (12345678-1234-1234-1234-123456789ABC),
+ version(1.0),
+ pointer_default(unique)]
+interface hello
+{
+void HelloProc([in, string] char * pszString);
+void Shutdown(void);
+}
diff --git a/private/net/svcdlls/ntlmssp/hello/makefile b/private/net/svcdlls/ntlmssp/hello/makefile
new file mode 100644
index 000000000..cc5975538
--- /dev/null
+++ b/private/net/svcdlls/ntlmssp/hello/makefile
@@ -0,0 +1,27 @@
+#*************************************************************#
+#** **#
+#** Microsoft RPC Examples **#
+#** hello Application **#
+#** Copyright(c) Microsoft Corp. 1992 **#
+#** **#
+#*************************************************************#
+
+!INCLUDE $(NTMAKEENV)\makefile.plt
+
+CPP = -cpp_cmd "$(MIDL_CPP)" $(MIDL_FLAGS) $(C_DEFINES) $(NET_C_DEFINES)
+IDL_NAME = hello
+
+# Stubs, auxiliary and header file from the IDL file
+hello.h hello_c.c hello_x.c hello_s.c hello_y.c : hello.idl hello.acf
+ midl -Oi -error allocation -error ref -ms_ext -c_ext $(CPP) $(CLIENT_FLAGS) .\$(IDL_NAME).idl $(INCS)
+ copy hello_c.c client
+ copy hello_s.c server
+
+
+
+clean :
+ -del hello_c.c
+ -del hello_x.c
+ -del hello_s.c
+ -del hello_y.c
+ -del hello.h
diff --git a/private/net/svcdlls/ntlmssp/hello/ntwin32.mak b/private/net/svcdlls/ntlmssp/hello/ntwin32.mak
new file mode 100644
index 000000000..b95bdec47
--- /dev/null
+++ b/private/net/svcdlls/ntlmssp/hello/ntwin32.mak
@@ -0,0 +1,64 @@
+# Win32 NMAKE definitions
+
+
+!IF "$(CPU)" == "i386"
+
+# Debug switches are default for current release
+#
+# These switches allow for source level debugging
+# with NTSD for local and global variables.
+
+
+CPUTYPE=1
+cdebug = -Zd -Od -Oy-
+
+cc = cl
+cflags = -c -G3d -W3 -Di386=1 $(cdebug)
+
+!ENDIF
+
+!IF "$(CPU)" == "MIPS"
+#declarations for use on self hosted MIPS box.
+
+CPUTYPE=2
+cc = cl
+cflags = -c -W3 -DMIPS=1
+!ENDIF
+
+!IF "$(CPU)" == "PPC"
+#declarations for use on self hosted PPC box.
+
+CPUTYPE=3
+cc = mcl
+cflags = -c -G3d -W3 -DPPC=1
+!ENDIF
+
+!IFNDEF CPUTYPE
+!ERROR Must specify CPU Environment Variable (i386 or MIPS or PPC )
+!ENDIF
+
+
+#Universal declaration
+
+cvars = -DWIN32
+linkdebug = -debug:full -debugtype:coff
+link = link $(linkdebug)
+
+# link flags - must be specified after $(link)
+#
+# conflags : creating a character based console application
+# guiflags : creating a GUI based "Windows" application
+
+conflags = -subsystem:console -entry:mainCRTStartup
+guiflags = -subsystem:windows -entry:WinMainCRTStartup
+
+# Link libraries - system import and C runtime libraries
+#
+# conlibs : libraries to link with for a console application
+# guilibs : libraries to link with for a "Windows" application
+#
+# note : $(LIB) is set in environment variables
+
+conlibs = $(LIB)\libcmt.lib $(LIB)\*.lib
+
+guilibs = $(LIB)\libcmt.lib $(LIB)\*.lib
diff --git a/private/net/svcdlls/ntlmssp/hello/server/hellos.c b/private/net/svcdlls/ntlmssp/hello/server/hellos.c
new file mode 100644
index 000000000..795560830
--- /dev/null
+++ b/private/net/svcdlls/ntlmssp/hello/server/hellos.c
@@ -0,0 +1,208 @@
+/****************************************************************************
+ Microsoft RPC Version 1.0
+ Copyright Microsoft Corp. 1992
+ Hello Example
+
+ FILE: hellos.c
+ USAGE: hellos
+ PURPOSE: Server side of RPC distributed application hello
+ FUNCTIONS: main() - registers server as RPC server
+ COMMENTS:
+ This distributed application prints "hello, world" on the server.
+ This version features a client that manages its connection to
+ the server. It uses the binding handle hello_IfHandle that is defined
+ in the generated header file hello.h.
+****************************************************************************/
+#include <stdlib.h>
+#include <windows.h>
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <rpc.h> // RPC data structures and APIs
+#include "hello.h" // header file generated by MIDL compiler
+
+void Usage(char * pszProgramName)
+{
+ fprintf(stderr, "Usage: %s\n", pszProgramName);
+ fprintf(stderr, " -p protocol_sequence\n");
+ fprintf(stderr, " -n network_address\n");
+ fprintf(stderr, " -e endpoint\n");
+ fprintf(stderr, " -o options\n");
+ fprintf(stderr, " -u uuid\n");
+ exit(1);
+}
+
+HANDLE TerminateEvent;
+
+int _CRTAPI1
+main (argc, argv)
+ int argc;
+ char *argv[];
+{
+ RPC_STATUS status;
+ unsigned char * pszUuid = "12345678-1234-1234-1234-123456789ABC";
+ unsigned char * pszProtocolSequence = "ncacn_np";
+ unsigned char * pszNetworkAddress = NULL;
+ unsigned char * pszEndpoint = "\\pipe\\hello";
+ unsigned char * pszOptions = NULL;
+ unsigned char * pszStringBinding = NULL;
+ int i;
+ DWORD WaitStatus;
+
+ // allow the user to override settings with command line switches
+ for (i = 1; i < argc; i++) {
+ if ((*argv[i] == '-') || (*argv[i] == '/')) {
+ switch (tolower(*(argv[i]+1))) {
+ case 'p': // protocol sequence
+ pszProtocolSequence = argv[++i];
+ break;
+ case 'n': // network address
+ pszNetworkAddress = argv[++i];
+ break;
+ case 'e':
+ pszEndpoint = argv[++i];
+ break;
+ case 'o':
+ pszOptions = argv[++i];
+ break;
+ case 'u':
+ pszUuid = argv[++i];
+ break;
+ case 'h':
+ case '?':
+ default:
+ Usage(argv[0]);
+ }
+ }
+ else
+ Usage(argv[0]);
+ }
+
+ //
+ // Create an event to wait on
+ //
+
+ TerminateEvent = CreateEvent( NULL, // No security attributes
+ TRUE, // Must be manually reset
+ FALSE, // Initially not signaled
+ NULL ); // No name
+
+ if ( TerminateEvent == NULL ) {
+ printf( "Couldn't CreateEvent %ld\n", GetLastError() );
+ return 2;
+ }
+
+
+ status = RpcServerUseProtseqEp(pszProtocolSequence,
+ 1, // maximum concurrent calls
+ pszEndpoint,
+ 0);
+ if (status) {
+ printf("RpcServerUseProtseqEp returned 0x%x\n", status);
+ exit(2);
+ }
+
+ status = RpcServerRegisterIf(hello_ServerIfHandle, 0, 0);
+ if (status) {
+ printf("RpcServerRegisterIf returned 0x%x\n", status);
+ exit(2);
+ }
+
+ status = RpcServerRegisterAuthInfo( "HelloS", 10, NULL, NULL );
+ if (status) {
+ printf("RpcServerRegisterAuthInfo returned 0x%x\n", status);
+ exit(2);
+ }
+
+ printf("Calling RpcServerListen\n");
+ status = RpcServerListen(1,12345,1);
+ if (status) {
+ printf("RpcServerListen returned: 0x%x\n", status);
+ exit(2);
+ }
+
+ WaitStatus = WaitForSingleObject( TerminateEvent, INFINITE );
+
+ if ( WaitStatus != WAIT_OBJECT_0 ) {
+ printf( "Couldn't WaitForSingleObject %ld %ld\n", WaitStatus, GetLastError() );
+ return 2;
+ }
+
+ return 0;
+
+} /* end main() */
+
+
+// ====================================================================
+// MIDL allocate and free
+// ====================================================================
+
+
+void __RPC_FAR * __RPC_API MIDL_user_allocate(size_t len)
+{
+ return(malloc(len));
+}
+
+void __RPC_API MIDL_user_free(void __RPC_FAR * ptr)
+{
+ free(ptr);
+}
+
+/*
+ PURPOSE: Remote procedures that are linked with the server
+ side of RPC distributed application
+ FUNCTIONS: HelloProc() - prints "hello, world" or other string
+ sent by client to server
+ COMMENTS:
+ This version of the distributed application that prints
+ "hello, world" (or other string) on the server features a client
+ that manages its connection to the server. It uses the binding
+ handle hello_IfHandle, defined in the file hello.h.
+****************************************************************************/
+
+void HelloProc(unsigned char * pszString)
+{
+ RPC_STATUS RpcStatus;
+
+ RpcStatus = RpcImpersonateClient( NULL );
+
+ if ( RpcStatus != RPC_S_OK ) {
+ printf( "RpcImpersonateClient Failed %ld\n", RpcStatus );
+ }
+ printf("%s\n", pszString);
+
+ RpcStatus = RpcRevertToSelf();
+
+ if ( RpcStatus != RPC_S_OK ) {
+ printf( "RpcRevertToSelf Failed %ld\n", RpcStatus );
+ }
+
+
+}
+
+void Shutdown(void)
+{
+ RPC_STATUS status;
+
+ printf("Calling RpcMgmtStopServerListening\n");
+ status = RpcMgmtStopServerListening(NULL);
+ if (status) {
+ printf("RpcMgmtStopServerListening returned: 0x%x\n", status);
+ exit(2);
+ }
+
+ printf("Calling RpcServerUnregisterIf\n");
+ status = RpcServerUnregisterIf(NULL, NULL, FALSE);
+ if (status) {
+ printf("RpcServerUnregisterIf returned 0x%x\n", status);
+ exit(2);
+ }
+
+ if ( !SetEvent( TerminateEvent) ) {
+ printf( "Couldn't SetEvent %ld\n", GetLastError() );
+ }
+
+}
+
+/* end hellos.c */
+
diff --git a/private/net/svcdlls/ntlmssp/hello/server/makefile b/private/net/svcdlls/ntlmssp/hello/server/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/net/svcdlls/ntlmssp/hello/server/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/net/svcdlls/ntlmssp/hello/server/sources b/private/net/svcdlls/ntlmssp/hello/server/sources
new file mode 100644
index 000000000..ca8a5db48
--- /dev/null
+++ b/private/net/svcdlls/ntlmssp/hello/server/sources
@@ -0,0 +1,46 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+
+Author:
+
+ Steve Wood (stevewo) 12-Apr-1990
+
+NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl
+
+!ENDIF
+
+MAJORCOMP=net
+MINORCOMP=hellos
+
+TARGETNAME=hellos
+TARGETPATH=obj
+TARGETTYPE=LIBRARY
+
+
+UMTYPE=console
+UMAPPL=hellos
+UMLIBS= \
+ obj\*\hellos.lib \
+ $(BASEDIR)\Public\Sdk\Lib\*\rpcrt4.lib \
+
+
+UNICODE=1
+
+INCLUDES=..;
+
+SOURCES= \
+ hello_s.c
+
diff --git a/private/net/svcdlls/ntlmssp/ntlmcomn.h b/private/net/svcdlls/ntlmssp/ntlmcomn.h
new file mode 100644
index 000000000..28c5a9456
--- /dev/null
+++ b/private/net/svcdlls/ntlmssp/ntlmcomn.h
@@ -0,0 +1,498 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ ntlmcomn.h
+
+Abstract:
+
+ Header file describing the interface to code common to the
+ NT Lanman Security Support Provider (NtLmSsp) Service and the DLL.
+
+Author:
+
+ Cliff Van Dyke (CliffV) 17-Sep-1993
+
+Revision History:
+
+--*/
+
+#ifndef _NTLMCOMN_INCLUDED_
+#define _NTLMCOMN_INCLUDED_
+
+////////////////////////////////////////////////////////////////////////////
+//
+// Common include files needed by ALL NtLmSsp files
+//
+////////////////////////////////////////////////////////////////////////////
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windef.h>
+#include <winbase.h>
+#include <winsvc.h> // Needed for service controller APIs
+#include <ntmsv1_0.h> // MSV 1.0 Authentication Package
+
+#include <security.h> // General definition of a Security Support Provider
+#include <spseal.h> // Prototypes for Seal & Unseal
+
+#include <ntlmsspd.h> // Common definitions between client and server
+#include <ntlmssp.h> // External definition of the NtLmSsp service
+#include <lmcons.h>
+#include <debug.h> // NtLmSsp debugging
+
+
+////////////////////////////////////////////////////////////////////////
+//
+// Global Definitions
+//
+////////////////////////////////////////////////////////////////////////
+
+//
+// LPC connections to this service
+//
+
+typedef struct _SSP_CLIENT_CONNECTION {
+
+ //
+ // Linked list of all client connections.
+ // (Serialized by SspLpcCritSect)
+
+ LIST_ENTRY Next;
+
+
+ //
+ // Number of references to this structure.
+ // (Serialized by SspLpcCritSect)
+
+ ULONG References;
+
+
+ //
+ // A handle to the client process. This handle is used to perform
+ // virtual memory operations within the client
+ // process (allocate, deallocate, read, write).
+ //
+
+ HANDLE ClientProcess;
+
+
+ //
+ // A handle to the LPC communication port created to communicate with
+ // this client. this port must be closed when the client deregisters.
+ //
+
+ HANDLE CommPort;
+
+ //
+ // Head of the list of credentials used by this Client
+ // (Serialized by SspCredentialCritSect)
+
+ LIST_ENTRY CredentialHead;
+
+ //
+ // Head of the list of contexts used by this Client
+ // (Serialized by SspContextCritSect)
+
+ LIST_ENTRY ContextHead;
+
+} SSP_CLIENT_CONNECTION, *PSSP_CLIENT_CONNECTION;
+
+//
+// Signature structure
+//
+
+typedef struct _NTLMSSP_MESSAGE_SIGNATURE {
+ ULONG Version;
+ ULONG RandomPad;
+ ULONG CheckSum;
+ ULONG Nonce;
+} NTLMSSP_MESSAGE_SIGNATURE, * PNTLMSSP_MESSAGE_SIGNATURE;
+
+#define NTLMSSP_MESSAGE_SIGNATURE_SIZE sizeof(NTLMSSP_MESSAGE_SIGNATURE)
+
+//
+// Version 1 is the structure above, using stream RC4 to encrypt the trailing
+// 12 bytes.
+//
+
+#define NTLMSSP_SIGN_VERSION 1
+
+#define NTLMSSP_KEY_SALT 0xbd
+
+
+////////////////////////////////////////////////////////////////////////
+//
+// Global variables
+//
+////////////////////////////////////////////////////////////////////////
+
+//
+// This value is put into the lower DWORD of handles. For NTLMSSP service,
+// it should be 1, and for clients who can call the LSA directly, it should
+// be one.
+//
+
+#define SEC_HANDLE_NTLMSSPS 0
+#define SEC_HANDLE_SECURITY 1
+
+extern ULONG SspCommonSecHandleValue;
+
+////////////////////////////////////////////////////////////////////////
+//
+// Procedure Forwards
+//
+////////////////////////////////////////////////////////////////////////
+
+//
+// Procedure forwards from init.c
+//
+
+NTSTATUS
+SspCommonInitialize(
+ VOID
+ );
+
+VOID
+SspCommonShutdown(
+ VOID
+ );
+
+
+//
+// Procedure forwards from utility.c
+//
+
+SECURITY_STATUS
+SspNtStatusToSecStatus(
+ IN NTSTATUS NtStatus,
+ IN SECURITY_STATUS DefaultStatus
+ );
+
+BOOLEAN
+SspTimeHasElapsed(
+ IN LARGE_INTEGER StartTime,
+ IN DWORD Timeout
+ );
+
+SECURITY_STATUS
+SspGetLogonId (
+ OUT PLUID LogonId,
+ OUT PHANDLE ReturnedTokenHandle OPTIONAL
+ );
+
+VOID
+SspGetPrimaryDomainNameAndTargetName(
+ VOID
+ );
+
+SECURITY_STATUS
+SspDuplicateToken(
+ IN HANDLE OriginalToken,
+ IN SECURITY_IMPERSONATION_LEVEL ImpersonationLevel,
+ OUT PHANDLE DuplicatedToken
+ );
+
+LPWSTR
+SspAllocWStrFromWStr(
+ IN LPWSTR Unicode
+ );
+
+SECURITY_STATUS
+SspDuplicateUnicodeString(
+ OUT PUNICODE_STRING Destination,
+ IN PUNICODE_STRING Source
+ );
+
+VOID
+SspHidePassword(
+ IN OUT PUNICODE_STRING Password
+ );
+
+VOID
+SspRevealPassword(
+ IN OUT PUNICODE_STRING HiddenPassword
+ );
+
+
+
+//
+// Procedure forwards from credhand.c
+//
+
+SECURITY_STATUS
+SsprAcquireCredentialHandle(
+ IN PSSP_CLIENT_CONNECTION ClientConnection,
+ IN PHANDLE ClientTokenHandle,
+ IN PLUID LogonId,
+ IN ULONG CredentialUseFlags,
+ OUT PCredHandle CredentialHandle,
+ OUT PTimeStamp Lifetime,
+ IN LPWSTR DomainName,
+ IN ULONG DomainNameSize,
+ IN LPWSTR UserName,
+ IN ULONG UserNameSize,
+ IN LPWSTR Password,
+ IN ULONG PasswordSize
+ );
+
+SECURITY_STATUS
+SsprFreeCredentialHandle(
+ IN PSSP_CLIENT_CONNECTION ClientConnection,
+ IN PCredHandle CredentialHandle
+ );
+
+VOID
+SspCredentialClientConnectionDropped(
+ PSSP_CLIENT_CONNECTION ClientConnection
+ );
+
+SECURITY_STATUS
+SspGetUnicodeStringFromClient(
+ IN PSSP_CLIENT_CONNECTION ClientConnection,
+ IN LPWSTR String,
+ IN ULONG StringSize,
+ IN ULONG MaximumLength,
+ OUT PUNICODE_STRING OutputString
+ );
+
+
+//
+// Procedure forwards from context.c
+//
+
+SECURITY_STATUS
+SsprHandleFirstCall(
+ IN PSSP_CLIENT_CONNECTION ClientConnection,
+ IN PCredHandle CredentialHandle,
+ IN OUT PCtxtHandle ContextHandle,
+ IN ULONG ContextReqFlags,
+ IN ULONG InputTokenSize,
+ IN PVOID InputToken,
+ IN OUT PULONG OutputTokenSize,
+ OUT PVOID OutputToken,
+ OUT PULONG ContextAttributes,
+ OUT PTimeStamp ExpirationTime,
+ OUT PUCHAR SessionKey,
+ OUT PULONG NegotiateFlags
+ );
+
+
+SECURITY_STATUS
+SsprHandleNegotiateMessage(
+ IN PSSP_CLIENT_CONNECTION ClientConnection,
+ IN PCredHandle CredentialHandle,
+ IN OUT PCtxtHandle ContextHandle,
+ IN ULONG ContextReqFlags,
+ IN ULONG InputTokenSize,
+ IN PVOID InputToken,
+ IN OUT PULONG OutputTokenSize,
+ OUT PVOID OutputToken,
+ OUT PULONG ContextAttributes,
+ OUT PTimeStamp ExpirationTime
+ );
+
+SECURITY_STATUS
+SsprHandleChallengeMessage(
+ IN PSSP_CLIENT_CONNECTION ClientConnection,
+ IN PCredHandle CredentialHandle,
+ IN OUT PCtxtHandle ContextHandle,
+ IN HANDLE ClientTokenHandle, OPTIONAL
+ IN PLUID LogonId, OPTIONAL
+ IN ULONG ContextReqFlags,
+ IN LPWSTR DomainName,
+ IN ULONG DomainNameSize,
+ IN LPWSTR UserName,
+ IN ULONG UserNameSize,
+ IN LPWSTR Password,
+ IN ULONG PasswordSize,
+ IN ULONG InputTokenSize,
+ IN PVOID InputToken,
+ IN OUT PULONG OutputTokenSize,
+ OUT PVOID OutputToken,
+ OUT PULONG ContextAttributes,
+ OUT PTimeStamp ExpirationTime,
+ OUT PUCHAR SessionKey,
+ OUT PULONG NegotiateFlags,
+ OUT LPWSTR ContextNames
+ );
+
+SECURITY_STATUS
+SsprHandleAuthenticateMessage(
+ IN PSSP_CLIENT_CONNECTION ClientConnection,
+ IN PCredHandle CredentialHandle,
+ IN OUT PCtxtHandle ContextHandle,
+ IN ULONG ContextReqFlags,
+ IN ULONG InputTokenSize,
+ IN PVOID InputToken,
+ IN OUT PULONG OutputTokenSize,
+ OUT PVOID OutputToken,
+ OUT PULONG ContextAttributes,
+ OUT PTimeStamp ExpirationTime,
+ OUT PUCHAR SessionKey,
+ OUT PULONG NegotiateFlags,
+ OUT PHANDLE TokenHandle,
+ OUT PNTSTATUS SubStatus,
+ OUT LPWSTR ContextNames,
+ OUT PTimeStamp PasswordExpiry
+ );
+
+SECURITY_STATUS
+SsprImpersonateSecurityContext(
+ IN PCtxtHandle ContextHandle
+ );
+
+SECURITY_STATUS
+SsprRevertSecurityContext(
+ IN PCtxtHandle ContextHandle
+ );
+
+SECURITY_STATUS
+SsprQueryContextAttributes(
+ IN PSSP_CLIENT_CONNECTION ClientConnection,
+ IN PCtxtHandle ContextHandle,
+ IN ULONG Attribute,
+ OUT PVOID Buffer
+ );
+
+SECURITY_STATUS
+SsprDeleteSecurityContext (
+ IN PSSP_CLIENT_CONNECTION ClientConnection,
+ PCtxtHandle ContextHandle
+ );
+
+VOID
+SspContextClientConnectionDropped(
+ PSSP_CLIENT_CONNECTION ClientConnection
+ );
+
+
+SECURITY_STATUS
+SsprContextGetCredentials(
+ IN PCtxtHandle ContextHandle,
+ OUT LPWSTR * DomainName,
+ OUT PULONG DomainNameSize,
+ OUT LPWSTR * UserName,
+ OUT PULONG UserNameSize,
+ OUT LPWSTR * Password,
+ OUT PULONG PasswordSize,
+ OUT PHANDLE ClientTokenHandle,
+ OUT PLUID LogonId
+ );
+
+SECURITY_STATUS
+SsprContextUpdateContext(
+ PCtxtHandle OldContextHandle,
+ PCtxtHandle ServerContextHandle
+ );
+
+//
+// Procedure forwards from encrypt.c
+//
+
+BOOLEAN
+IsEncryptionPermitted(VOID);
+
+//
+// Procedure forwards from sign.c
+//
+
+VOID
+SspInitLocalContexts(VOID);
+
+VOID
+SspReleaseLocalContexts(VOID);
+
+
+SECURITY_STATUS
+SspHandleSignMessage(
+ IN OUT PCtxtHandle ContextHandle,
+ IN ULONG fQOP,
+ IN OUT PSecBufferDesc pMessage,
+ IN ULONG MessageSeqNo
+ );
+
+SECURITY_STATUS
+SspHandleSealMessage(
+ IN OUT PCtxtHandle ContextHandle,
+ IN ULONG fQOP,
+ IN OUT PSecBufferDesc pMessage,
+ IN ULONG MessageSeqNo
+ );
+
+#define SSPR_CLIENT_CONTEXT 0x1
+#define SSPR_SERVER_CONTEXT 0x2
+
+SECURITY_STATUS
+SspMapContext(
+ IN PCtxtHandle phContext,
+ IN PUCHAR pSessionKey,
+ IN ULONG NegotiateFlags,
+ IN HANDLE TokenHandle,
+ IN LPWSTR ContextNames,
+ IN PTimeStamp PasswordExpiry OPTIONAL
+ );
+
+SECURITY_STATUS
+SspHandleVerifyMessage(
+ IN OUT PCtxtHandle ContextHandle,
+ IN OUT PSecBufferDesc pMessage,
+ IN ULONG MessageSeqNo,
+ OUT PULONG pfQOP
+ );
+
+SECURITY_STATUS
+SspHandleUnsealMessage(
+ IN OUT PCtxtHandle ContextHandle,
+ IN OUT PSecBufferDesc pMessage,
+ IN ULONG MessageSeqNo,
+ OUT PULONG pfQOP
+ );
+
+
+VOID
+SspHandleLocalDelete(
+ IN PCtxtHandle ContextHandle
+ );
+
+SECURITY_STATUS
+SspLocalQueryContextAttributes(
+ IN PCtxtHandle ContextHandle,
+ IN ULONG Attribute,
+ OUT PVOID Buffer
+ );
+
+//
+// Procedure forwards of routine with different implementations on
+// SERVICE and DLL
+// In the SERVICE, these are implemented in lpc.c.
+// In the DLL, these are implemented in support.c.
+//
+
+SECURITY_STATUS
+SspLpcCopyToClientBuffer (
+ IN PSSP_CLIENT_CONNECTION ClientConnection,
+ IN ULONG Size,
+ OUT PVOID ClientBufferAddress,
+ IN PVOID LocalBufferAddress
+ );
+
+SECURITY_STATUS
+SspLpcCopyFromClientBuffer (
+ IN PSSP_CLIENT_CONNECTION ClientConnection,
+ IN ULONG Size,
+ OUT PVOID LocalBufferAddress,
+ IN PVOID ClientBufferAddress
+ );
+
+SECURITY_STATUS
+SspLpcImpersonateTokenHandle(
+ IN PSSP_CLIENT_CONNECTION ClientConnection,
+ IN HANDLE TokenHandle,
+ IN PCLIENT_ID ClientId
+ );
+
+#endif // ifndef _NTLMCOMN_INCLUDED_
diff --git a/private/net/svcdlls/ntlmssp/ntlmitf.h b/private/net/svcdlls/ntlmssp/ntlmitf.h
new file mode 100644
index 000000000..1c3f960dc
--- /dev/null
+++ b/private/net/svcdlls/ntlmssp/ntlmitf.h
@@ -0,0 +1,253 @@
+//+---------------------------------------------------------------------------
+//
+// Microsoft Windows
+// Copyright (C) Microsoft Corporation, 1992 - 1993.
+//
+// File: ntlmitf.h
+//
+// Contents: Security Support Provider Interface
+// Prototypes and structure definitions
+//
+// Functions: Security Support Provider API
+//
+// History: 11-24-93 RichardW Created
+//
+//----------------------------------------------------------------------------
+
+
+SECURITY_STATUS SEC_ENTRY
+SspAcquireCredentialsHandleW(
+ SEC_WCHAR SEC_FAR * pszPrincipal, // Name of principal
+ SEC_WCHAR SEC_FAR * pszPackage, // Name of package
+ unsigned long fCredentialUse, // Flags indicating use
+ void SEC_FAR * pvLogonId, // Pointer to logon ID
+ void SEC_FAR * pAuthData, // Package specific data
+ SEC_GET_KEY_FN pGetKeyFn, // Pointer to GetKey() func
+ void SEC_FAR * pvGetKeyArgument, // Value to pass to GetKey()
+ PCredHandle phCredential, // (out) Cred Handle
+ PTimeStamp ptsExpiry // (out) Lifetime (optional)
+ );
+
+
+SECURITY_STATUS SEC_ENTRY
+SspAcquireCredentialsHandleA(
+ SEC_CHAR SEC_FAR * pszPrincipal, // Name of principal
+ SEC_CHAR SEC_FAR * pszPackage, // Name of package
+ unsigned long fCredentialUse, // Flags indicating use
+ void SEC_FAR * pvLogonId, // Pointer to logon ID
+ void SEC_FAR * pAuthData, // Package specific data
+ SEC_GET_KEY_FN pGetKeyFn, // Pointer to GetKey() func
+ void SEC_FAR * pvGetKeyArgument, // Value to pass to GetKey()
+ PCredHandle phCredential, // (out) Cred Handle
+ PTimeStamp ptsExpiry // (out) Lifetime (optional)
+ );
+
+
+SECURITY_STATUS SEC_ENTRY
+SspQueryCredentialsAttributesW(
+ PCredHandle phCredential, // Credential to query
+ unsigned long ulAttribute, // Attribute to query
+ void SEC_FAR * pBuffer // Buffer for attributes
+ );
+
+SECURITY_STATUS SEC_ENTRY
+SspQueryCredentialsAttributesA(
+ PCredHandle phCredential, // Credential to query
+ unsigned long ulAttribute, // Attribute to query
+ void SEC_FAR * pBuffer // Buffer for attributes
+ );
+
+
+
+SECURITY_STATUS SEC_ENTRY
+SspFreeCredentialsHandle(
+ PCredHandle phCredential // Handle to free
+ );
+
+SECURITY_STATUS SEC_ENTRY
+SspInitializeSecurityContextW(
+ PCredHandle phCredential, // Cred to base context
+ PCtxtHandle phContext, // Existing context (OPT)
+ SEC_WCHAR SEC_FAR * pszTargetName, // Name of target
+ unsigned long fContextReq, // Context Requirements
+ unsigned long Reserved1, // Reserved, MBZ
+ unsigned long TargetDataRep, // Data rep of target
+ PSecBufferDesc pInput, // Input Buffers
+ unsigned long Reserved2, // Reserved, MBZ
+ PCtxtHandle phNewContext, // (out) New Context handle
+ PSecBufferDesc pOutput, // (inout) Output Buffers
+ unsigned long SEC_FAR * pfContextAttr, // (out) Context attrs
+ PTimeStamp ptsExpiry // (out) Life span (OPT)
+ );
+
+
+SECURITY_STATUS SEC_ENTRY
+SspInitializeSecurityContextA(
+ PCredHandle phCredential, // Cred to base context
+ PCtxtHandle phContext, // Existing context (OPT)
+ SEC_CHAR SEC_FAR * pszTargetName, // Name of target
+ unsigned long fContextReq, // Context Requirements
+ unsigned long Reserved1, // Reserved, MBZ
+ unsigned long TargetDataRep, // Data rep of target
+ PSecBufferDesc pInput, // Input Buffers
+ unsigned long Reserved2, // Reserved, MBZ
+ PCtxtHandle phNewContext, // (out) New Context handle
+ PSecBufferDesc pOutput, // (inout) Output Buffers
+ unsigned long SEC_FAR * pfContextAttr, // (out) Context attrs
+ PTimeStamp ptsExpiry // (out) Life span (OPT)
+ );
+
+
+SECURITY_STATUS SEC_ENTRY
+SspAcceptSecurityContext(
+ PCredHandle phCredential, // Cred to base context
+ PCtxtHandle phContext, // Existing context (OPT)
+ PSecBufferDesc pInput, // Input buffer
+ unsigned long fContextReq, // Context Requirements
+ unsigned long TargetDataRep, // Target Data Rep
+ PCtxtHandle phNewContext, // (out) New context handle
+ PSecBufferDesc pOutput, // (inout) Output buffers
+ unsigned long SEC_FAR * pfContextAttr, // (out) Context attributes
+ PTimeStamp ptsExpiry // (out) Life span (OPT)
+ );
+
+
+SECURITY_STATUS SEC_ENTRY
+SspCompleteAuthToken(
+ PCtxtHandle phContext, // Context to complete
+ PSecBufferDesc pToken // Token to complete
+ );
+
+
+SECURITY_STATUS SEC_ENTRY
+SspImpersonateSecurityContext(
+ PCtxtHandle phContext // Context to impersonate
+ );
+
+
+
+SECURITY_STATUS SEC_ENTRY
+SspRevertSecurityContext(
+ PCtxtHandle phContext // Context from which to re
+ );
+
+
+SECURITY_STATUS SEC_ENTRY
+SspDeleteSecurityContext(
+ PCtxtHandle phContext // Context to delete
+ );
+
+
+SECURITY_STATUS SEC_ENTRY
+SspApplyControlToken(
+ PCtxtHandle phContext, // Context to modify
+ PSecBufferDesc pInput // Input token to apply
+ );
+
+
+
+SECURITY_STATUS SEC_ENTRY
+SspQueryContextAttributesW(
+ PCtxtHandle phContext, // Context to query
+ unsigned long ulAttribute, // Attribute to query
+ void SEC_FAR * pBuffer // Buffer for attributes
+ );
+
+SECURITY_STATUS SEC_ENTRY
+SspQueryContextAttributesA(
+ PCtxtHandle phContext, // Context to query
+ unsigned long ulAttribute, // Attribute to query
+ void SEC_FAR * pBuffer // Buffer for attributes
+ );
+
+
+SECURITY_STATUS SEC_ENTRY
+SspFreeContextBuffer(
+ void SEC_FAR * pvContextBuffer // buffer to free
+ );
+
+
+SECURITY_STATUS SEC_ENTRY
+SspMakeSignature(
+ PCtxtHandle phContext, // Context to use
+ unsigned long fQOP, // Quality of Protection
+ PSecBufferDesc pMessage, // Message to sign
+ unsigned long MessageSeqNo // Message Sequence Num.
+ );
+
+SECURITY_STATUS SEC_ENTRY
+SspVerifySignature(
+ PCtxtHandle phContext, // Context to use
+ PSecBufferDesc pMessage, // Message to verify
+ unsigned long MessageSeqNo, // Sequence Num.
+ unsigned long SEC_FAR * pfQOP // QOP used
+ );
+
+SECURITY_STATUS SEC_ENTRY
+SspSealMessage(
+ PCtxtHandle phContext, // Context to use
+ unsigned long fQOP, // Quality of Protection
+ PSecBufferDesc pMessage, // Message to sign
+ unsigned long MessageSeqNo // Message Sequence Num.
+ );
+
+SECURITY_STATUS SEC_ENTRY
+SspUnsealMessage(
+ PCtxtHandle phContext, // Context to use
+ PSecBufferDesc pMessage, // Message to verify
+ unsigned long MessageSeqNo, // Sequence Num.
+ unsigned long SEC_FAR * pfQOP // QOP used
+ );
+
+SECURITY_STATUS SEC_ENTRY
+SspEnumerateSecurityPackagesW(
+ unsigned long SEC_FAR * pcPackages, // Receives num. packages
+ PSecPkgInfoW SEC_FAR * ppPackageInfo // Receives array of info
+ );
+
+
+SECURITY_STATUS SEC_ENTRY
+SspEnumerateSecurityPackagesA(
+ unsigned long SEC_FAR * pcPackages, // Receives num. packages
+ PSecPkgInfoA SEC_FAR * ppPackageInfo // Receives array of info
+ );
+
+
+
+SECURITY_STATUS SEC_ENTRY
+SspQuerySecurityPackageInfoW(
+ SEC_WCHAR SEC_FAR * pszPackageName, // Name of package
+ PSecPkgInfoW SEC_FAR *ppPackageInfo // Receives package info
+ );
+
+
+
+SECURITY_STATUS SEC_ENTRY
+SspQuerySecurityPackageInfoA(
+ SEC_CHAR SEC_FAR * pszPackageName, // Name of package
+ PSecPkgInfoA SEC_FAR *ppPackageInfo // Receives package info
+ );
+
+
+SECURITY_STATUS SEC_ENTRY
+SspQuerySecurityContextToken(
+ PCtxtHandle phContext, // package to query
+ PHANDLE TokenHandle // receivese token
+ );
+
+
+PSecurityFunctionTableA SEC_ENTRY
+SspInitSecurityInterfaceA(
+ void
+ );
+
+PSecurityFunctionTableW SEC_ENTRY
+SspInitSecurityInterfaceW(
+ void
+ );
+
+
+BOOLEAN
+PackageMain(
+ ULONG Reason
+ );
diff --git a/private/net/svcdlls/ntlmssp/ntlmssp.h b/private/net/svcdlls/ntlmssp/ntlmssp.h
new file mode 100644
index 000000000..89bf1f215
--- /dev/null
+++ b/private/net/svcdlls/ntlmssp/ntlmssp.h
@@ -0,0 +1,106 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ nllmssp.h
+
+Abstract:
+
+ Externally visible definition of the NT Lanman Security Support Provider
+ (NtLmSsp) Service.
+
+Author:
+
+ Cliff Van Dyke (cliffv) 01-Jul-1993
+
+Environment:
+
+ User mode only.
+ Contains NT-specific code.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+ Borrowed from the Ciaro's ntlmssp.h by PeterWi.
+
+--*/
+
+#ifndef _NTLMSSP_
+#define _NTLMSSP_
+
+//
+// Defines for SecPkgInfo structure returned by QuerySecurityPackageInfo
+//
+
+#define NTLMSP_NAME_A "NTLM"
+#define NTLMSP_NAME L"NTLM"
+#define NTLMSP_COMMENT_A "NTLM Security Package"
+#define NTLMSP_COMMENT L"NTLM Security Package"
+#define NTLMSP_CAPABILITIES (SECPKG_FLAG_TOKEN_ONLY | \
+ SECPKG_FLAG_MULTI_REQUIRED | \
+ SECPKG_FLAG_CONNECTION | \
+ SECPKG_FLAG_INTEGRITY | \
+ SECPKG_FLAG_PRIVACY)
+
+#define NTLMSP_VERSION 1
+#define NTLMSP_RPCID 10 // RPC_C_AUTHN_WINNT from rpcdce.h
+#define NTLMSP_MAX_TOKEN_SIZE 0x300
+
+#if DBG
+//
+// Function codes to NtLmSspControl
+//
+
+#define NTLMSSP_BREAKPOINT 1
+#define NTLMSSP_DBFLAG 2
+#define NTLMSSP_TRUNCATE 3
+
+SECURITY_STATUS
+NtLmSspControl(
+ IN ULONG FunctionCode,
+ IN ULONG Data
+ );
+
+#endif // DBG
+
+
+//
+// Exported non-ssp function
+//
+
+SECURITY_STATUS
+SspQueryPasswordExpiry(
+ IN PCtxtHandle ContextHandle,
+ OUT PTimeStamp PasswordExpiry
+ );
+
+// includes that should go elsewhere.
+
+
+//
+// Move to net\inc\confname.h
+//
+
+#define NTLMSSP_KEYWORD_DBFLAG L"DBFlag"
+#define NTLMSSP_KEYWORD_MAXIMUMLOGFILESIZE L"MaximumLogFileSize"
+
+//
+// Move to secscode.h
+//
+
+#define SEC_E_PACKAGE_UNKNOWN SEC_E_SECPKG_NOT_FOUND
+#define SEC_E_BUFFER_TOO_SMALL SEC_E_INSUFFICIENT_MEMORY
+#define SEC_I_CALLBACK_NEEDED SEC_I_CONTINUE_NEEDED
+#define SEC_E_INVALID_CONTEXT_REQ SEC_E_NOT_SUPPORTED
+#define SEC_E_INVALID_CREDENTIAL_USE SEC_E_NOT_SUPPORTED
+#define SEC_I_CALL_NTLMSSP_SERVICE 0xFFFFFFFF
+
+//
+// Could be in sspi.h
+//
+
+#define SSP_RET_REAUTHENTICATION 0x8000000
+
+#endif // _NTLMSSP_
diff --git a/private/net/svcdlls/ntlmssp/ntlmsspd.h b/private/net/svcdlls/ntlmssp/ntlmsspd.h
new file mode 100644
index 000000000..e299c94c5
--- /dev/null
+++ b/private/net/svcdlls/ntlmssp/ntlmsspd.h
@@ -0,0 +1,197 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ nllmsspd.h
+
+Abstract:
+
+ Defines the interface between the client and server side of the
+ NT Lanman Security Support Provider NtLmSsp service.
+
+Author:
+
+ Cliff Van Dyke (cliffv) 08-Jun-1993
+
+Environment:
+
+ User mode only.
+ Contains NT-specific code.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+--*/
+
+#if ( _MSC_VER >= 800 )
+#pragma warning ( 3 : 4100 ) // enable "Unreferenced formal parameter"
+#pragma warning ( 3 : 4219 ) // enable "trailing ',' used for variable argument list"
+#endif
+
+#ifndef _NTLMSSPD_
+#define _NTLMSSPD_
+
+
+//
+// Name of LPC port
+//
+
+#define NTLMSSP_LPC_PORT_NAME L"\\NtLmSecuritySupportProviderPort"
+
+//
+// Used for connecting to the NtLmSsp LPC port.
+//
+
+typedef struct _SSP_REGISTER_CONNECT_INFO {
+ SECURITY_STATUS CompletionStatus;
+} SSP_REGISTER_CONNECT_INFO, *PSSP_REGISTER_CONNECT_INFO;
+
+
+//
+// Name of event indicating NtLmSsp service is running.
+//
+
+#define NTLMSSP_RUNNING_EVENT L"NtLmSecuritySupportProviderEvent"
+
+
+
+//
+// Message formats passed by LPC from client to server.
+//
+
+typedef enum _SSP_API_NUMBER {
+ SspLpcAcquireCredentialHandle,
+ SspLpcFreeCredentialHandle,
+ SspLpcInitializeSecurityContext,
+ SspLpcAcceptSecurityContext,
+ SspLpcQueryContextAttributes,
+ SspLpcDeleteSecurityContext,
+ SspLpcNtLmSspControl,
+ SspLpcNoop,
+ SspLpcMaxApiNumber
+} SSP_API_NUMBER, *PSSP_API_NUMBER;
+
+
+//
+// Each API results in a data structure containing the parameters
+// of that API being transmitted to the NtLmSsp server. This data structure
+// (SSP_API_MESSAGE) has a common header and a body which is dependent
+// upon the type of call being made. The following data structures are
+// the call-specific body formats.
+//
+
+typedef struct _SSP_ACQUIRE_CREDENTIAL_HANDLE_ARGS {
+ TimeStamp Lifetime; // OUT parameter
+ CredHandle CredentialHandle; // OUT parameter
+ ULONG CredentialUseFlags;
+ LPWSTR DomainName;
+ ULONG DomainNameSize;
+ LPWSTR UserName;
+ ULONG UserNameSize;
+ LPWSTR Password;
+ ULONG PasswordSize;
+} SSP_ACQUIRE_CREDENTIAL_HANDLE_ARGS, *PSSP_ACQUIRE_CREDENTIAL_HANDLE_ARGS;
+
+typedef struct _SSP_FREE_CREDENTIAL_HANDLE_ARGS {
+ CredHandle CredentialHandle;
+} SSP_FREE_CREDENTIAL_HANDLE_ARGS, *PSSP_FREE_CREDENTIAL_HANDLE_ARGS;
+
+typedef struct _SSP_INITIALIZE_SECURITY_CONTEXT_ARGS {
+ CredHandle CredentialHandle;
+ CtxtHandle ContextHandle; // IN/OUT parameter
+ LUID LogonId;
+ HANDLE ClientTokenHandle;
+ TimeStamp ExpirationTime; // OUT parameter
+ ULONG ContextReqFlags;
+ ULONG ContextAttributes; // OUT parameter
+ LPWSTR DomainName;
+ ULONG DomainNameSize;
+ LPWSTR UserName;
+ ULONG UserNameSize;
+ LPWSTR Password;
+ ULONG PasswordSize;
+ ULONG InputTokenSize;
+ PVOID InputToken;
+ ULONG OutputTokenSize; // IN/OUT parameter
+ PVOID OutputToken; // OUT parameter
+ UCHAR SessionKey[MSV1_0_USER_SESSION_KEY_LENGTH];
+ ULONG NegotiateFlags;
+ LPWSTR ContextNames;
+} SSP_INITIALIZE_SECURITY_CONTEXT_ARGS, *PSSP_INITIALIZE_SECURITY_CONTEXT_ARGS;
+
+typedef struct _SSP_ACCEPT_SECURITY_CONTEXT_ARGS {
+ CredHandle CredentialHandle;
+ CtxtHandle ContextHandle; // IN/OUT parameter
+ TimeStamp ExpirationTime; // OUT parameter
+ TimeStamp PasswordExpiry; // OUT parameter
+ ULONG ContextReqFlags;
+ ULONG ContextAttributes; // OUT parameter
+ ULONG InputTokenSize;
+ PVOID InputToken;
+ ULONG OutputTokenSize; // IN/OUT parameter
+ PVOID OutputToken; // OUT parameter
+ UCHAR SessionKey[MSV1_0_USER_SESSION_KEY_LENGTH];
+ ULONG NegotiateFlags;
+ HANDLE TokenHandle;
+ NTSTATUS SubStatus;
+ LPWSTR ContextNames;
+} SSP_ACCEPT_SECURITY_CONTEXT_ARGS, *PSSP_ACCEPT_SECURITY_CONTEXT_ARGS;
+
+typedef struct _SSP_IMPERSONATE_SECURITY_CONTEXT_ARGS {
+ CtxtHandle ContextHandle;
+} SSP_IMPERSONATE_SECURITY_CONTEXT_ARGS, *PSSP_IMPERSONATE_SECURITY_CONTEXT_ARGS;
+
+typedef struct _SSP_REVERT_SECURITY_CONTEXT_ARGS {
+ CtxtHandle ContextHandle;
+} SSP_REVERT_SECURITY_CONTEXT_ARGS, *PSSP_REVERT_SECURITY_CONTEXT_ARGS;
+
+typedef struct _SSP_QUERY_CONTEXT_ATTRIBUTES_ARGS {
+ CtxtHandle ContextHandle;
+ ULONG Attribute;
+ PVOID Buffer; // OUT parameter
+} SSP_QUERY_CONTEXT_ATTRIBUTES_ARGS, *PSSP_QUERY_CONTEXT_ATTRIBUTES_ARGS;
+
+typedef struct _SSP_DELETE_SECURITY_CONTEXT_ARGS {
+ CtxtHandle ContextHandle;
+} SSP_DELETE_SECURITY_CONTEXT_ARGS, *PSSP_DELETE_SECURITY_CONTEXT_ARGS;
+
+typedef struct _SSP_NTLMSSP_CONTROL_ARGS {
+ ULONG FunctionCode;
+ ULONG Data;
+} SSP_NTLMSSP_CONTROL_ARGS, *PSSP_NTLMSSP_CONTROL_ARGS;
+
+typedef struct _SSP_MAP_CONTEXT_KEYS_ARGS {
+ CtxtHandle hContext; // IN - Context to map
+ PVOID pvMappedContext; // OUT - Pointer to mapped context
+} SSP_MAP_CONTEXT_KEYS_ARGS, * PSSP_MAP_CONTEXT_KEYS_ARGS;
+
+//
+// This is the message that gets sent for every NtLmSsp LPC call.
+//
+
+typedef struct _SSP_API_MESSAGE {
+ PORT_MESSAGE PortMessage;
+ union {
+ SSP_REGISTER_CONNECT_INFO ConnectionRequest;
+ struct {
+ SSP_API_NUMBER ApiNumber;
+ SECURITY_STATUS ReturnedStatus;
+ union {
+ SSP_ACQUIRE_CREDENTIAL_HANDLE_ARGS AcquireCredentialHandleArgs;
+ SSP_FREE_CREDENTIAL_HANDLE_ARGS FreeCredentialHandleArgs;
+ SSP_INITIALIZE_SECURITY_CONTEXT_ARGS InitializeSecurityContextArgs;
+ SSP_ACCEPT_SECURITY_CONTEXT_ARGS AcceptSecurityContextArgs;
+ SSP_IMPERSONATE_SECURITY_CONTEXT_ARGS ImpersonateSecurityContextArgs;
+ SSP_REVERT_SECURITY_CONTEXT_ARGS RevertSecurityContextArgs;
+ SSP_QUERY_CONTEXT_ATTRIBUTES_ARGS QueryContextAttributesArgs;
+ SSP_DELETE_SECURITY_CONTEXT_ARGS DeleteSecurityContextArgs;
+ SSP_NTLMSSP_CONTROL_ARGS NtLmSspControlArgs;
+ } Arguments;
+ };
+ };
+} SSP_API_MESSAGE, *PSSP_API_MESSAGE;
+
+
+#endif // _NTLMSSPD_
diff --git a/private/net/svcdlls/ntlmssp/package/makefile b/private/net/svcdlls/ntlmssp/package/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/net/svcdlls/ntlmssp/package/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/net/svcdlls/ntlmssp/package/package.c b/private/net/svcdlls/ntlmssp/package/package.c
new file mode 100644
index 000000000..0c89f4e5d
--- /dev/null
+++ b/private/net/svcdlls/ntlmssp/package/package.c
@@ -0,0 +1,1538 @@
+//+-----------------------------------------------------------------------
+//
+// Microsoft Windows
+//
+// Copyright (c) Microsoft Corporation 1991 - 1992
+//
+// File: Package.c
+//
+// Contents: Package management routines for the security DLL
+//
+//
+// History: 12 Mar 92, RichardW Created
+// 17 Aug 92, RichardW Rearranged, commented, etc.
+// 08 Mar 94, MikeSw Moved to C++
+//
+//------------------------------------------------------------------------
+
+#include "secdll.h"
+#include <stdlib.h>
+#include <windows.h>
+#include <winreg.h>
+
+
+//
+// Declarations from built-in NTLM package
+//
+
+extern SecurityFunctionTable SspDllSecurityFunctionTable;
+
+PSecurityFunctionTable
+SspInitSecurityInterface(
+ VOID
+ );
+VOID
+SspCommonShutdown(
+ VOID
+ );
+
+//
+// Built-in package definitions
+//
+
+const INIT_SECURITY_INTERFACE_W BuiltinPackageInitW[] = {
+ SspInitSecurityInterfaceW,
+ SspInitSecurityInterfaceW
+ };
+const INIT_SECURITY_INTERFACE_A BuiltinPackageInitA[] = {
+ SspInitSecurityInterfaceA,
+ SspInitSecurityInterfaceA
+ };
+const EXIT_SECURITY_INTERFACE BuiltinPackageExit[] = {
+ NULL,
+ NULL
+ };
+
+
+
+//
+// Data for managing all packages
+//
+
+ULONG cPackages = 0;
+ULONG cBuiltInPackages = 0;
+
+PSecPkg pspPackages;
+
+CRITICAL_SECTION csSecurity;
+BOOLEAN PackageInitialized = FALSE;
+
+//+-------------------------------------------------------------------------
+//
+// Function: BindPackageFromDll()
+//
+// Synopsis: Binds a SP DLL to the current process.
+//
+// Effects:
+//
+// Arguments:
+//
+// Requires:
+//
+// Returns:
+//
+// Notes:
+//
+//--------------------------------------------------------------------------
+SECURITY_STATUS
+BindPackageFromDll(
+ LPWSTR pszFileName,
+ PSecPkg pspPackage,
+ INIT_SECURITY_INTERFACE_W * pfPackageInitW,
+ INIT_SECURITY_INTERFACE_A * pfPackageInitA
+ )
+{
+ HINSTANCE hPackage;
+
+ hPackage = LoadLibrary(pszFileName);
+ if (!hPackage)
+ {
+ return(SEC_E_SECPKG_NOT_FOUND);
+ }
+
+ pspPackage->hInstance = hPackage;
+
+ //
+ // Find the init function
+ //
+ // BUGBUG: this function only allows one package per DLL.
+ //
+
+ *pfPackageInitW = (INIT_SECURITY_INTERFACE_W)
+ GetProcAddress(hPackage,"InitSecurityInterfaceW");
+
+ *pfPackageInitA = (INIT_SECURITY_INTERFACE_A)
+ GetProcAddress(hPackage,"InitSecurityInterfaceA");
+
+ if ((*pfPackageInitW == NULL) &&
+ (*pfPackageInitA == NULL))
+ {
+ FreeLibrary(hPackage);
+ return(SEC_E_SECPKG_NOT_FOUND);
+ }
+
+ return(SEC_E_OK);
+}
+
+//+-------------------------------------------------------------------------
+//
+// Function: BindBuiltinPackage()
+//
+// Synopsis: Binds a SP DLL to the current process.
+//
+// Effects:
+//
+// Arguments:
+//
+// Requires:
+//
+// Returns:
+//
+// Notes:
+//
+//--------------------------------------------------------------------------
+SECURITY_STATUS
+BindBuiltinPackage(
+ ULONG ulPackageOrdinal,
+ PSecPkg pspPackage,
+ INIT_SECURITY_INTERFACE_W * pfPackageInitW,
+ INIT_SECURITY_INTERFACE_A * pfPackageInitA
+ )
+{
+ pspPackage->hInstance = NULL;
+
+ //
+ // Find the init function
+ //
+
+ *pfPackageInitW = BuiltinPackageInitW[ulPackageOrdinal];
+ *pfPackageInitA = BuiltinPackageInitA[ulPackageOrdinal];
+
+ if ((*pfPackageInitW == NULL) &&
+ (*pfPackageInitA == NULL))
+ {
+
+ return(SEC_E_SECPKG_NOT_FOUND);
+ }
+
+ //
+ // Store the exit function
+ //
+
+ pspPackage->pfUnloadPackage = BuiltinPackageExit[ulPackageOrdinal];
+
+ return(SEC_E_OK);
+}
+
+
+//+-------------------------------------------------------------------------
+//
+// Function: UnloadPackages
+//
+// Synopsis: Unloads the security packages (both DLL and built-in).
+// Designed to be called whenever this DLL is unloaded, or
+// by LoadPackages if init-time load fails.
+//
+// Effects:
+//
+// Arguments: cLoadedPackages - The number of packages loaded.
+//
+// Requires:
+//
+// Returns:
+//
+// Notes:
+//
+//--------------------------------------------------------------------------
+void
+UnloadPackages(
+ ULONG cLoadedPackages)
+{
+ //
+ // Make sure that no other threads are accessing the package list
+ //
+
+ GetProcessLock();
+
+ //
+ // Unload DLL packages, in reverse order
+ //
+
+ while (cLoadedPackages > cBuiltInPackages) {
+ cLoadedPackages--;
+
+ if (pspPackages[cLoadedPackages].hInstance != NULL) {
+ (VOID) FreeLibrary(pspPackages[cLoadedPackages].hInstance);
+ }
+
+ if (pspPackages[cLoadedPackages].PackageNameW != NULL) {
+ (VOID) LocalFree(pspPackages[cLoadedPackages].PackageNameW);
+ }
+
+ }
+
+ //
+ // Unload built-in packages
+ //
+
+ while (cLoadedPackages-- > 0) {
+
+ //
+ // Tell the package to clean up its resources
+ //
+
+ if (pspPackages[cLoadedPackages].pfUnloadPackage != NULL) {
+ (VOID) pspPackages[cLoadedPackages].pfUnloadPackage();
+ }
+
+
+ if (pspPackages[cLoadedPackages].PackageNameW != NULL) {
+ (VOID) LocalFree(pspPackages[cLoadedPackages].PackageNameW);
+ }
+ }
+
+ //
+ // Free pspPackages structure
+ //
+
+ if (pspPackages != NULL) {
+ (VOID) LocalFree(pspPackages);
+ pspPackages = NULL;
+ }
+
+ cPackages = cBuiltInPackages = 0;
+
+ FreeProcessLock();
+}
+
+//+-------------------------------------------------------------------------
+//
+// Function: LoadKnownPackages
+//
+// Synopsis: Binds, loads, and initializes a SP DLL
+//
+// Effects:
+//
+// Arguments:
+//
+// Requires:
+//
+// Returns:
+//
+// Notes:
+//
+//--------------------------------------------------------------------------
+SECURITY_STATUS
+LoadKnownPackages( ULONG cDllPackages,
+ LPWSTR * PackagePath)
+{
+
+ ULONG cIndex, cIndex2;
+ SECURITY_STATUS scRet;
+ INIT_SECURITY_INTERFACE_W InitPackageW;
+ INIT_SECURITY_INTERFACE_A InitPackageA;
+ PSecPkgInfoW pPackageInfoW = NULL;
+ PSecPkgInfoA pPackageInfoA = NULL;
+ ULONG cInstancePackages;
+ ULONG PackageNameLen;
+ PSecPkg TempPackages = NULL;
+
+
+ pspPackages = LocalAlloc(LMEM_ZEROINIT,
+ sizeof(SecPkg)
+ * (cDllPackages + BuiltinPackageCount));
+
+
+ if (pspPackages == NULL)
+ {
+ return(SEC_E_INSUFFICIENT_MEMORY);
+ }
+
+ ASSERT(cPackages == 0);
+
+ for (cIndex = 0; cIndex < BuiltinPackageCount ; cIndex++ )
+ {
+ scRet = BindBuiltinPackage( cIndex,
+ &pspPackages[cPackages],
+ &InitPackageW,
+ &InitPackageA);
+
+ if (!NT_SUCCESS(scRet))
+ {
+ continue;
+ }
+
+ if (InitPackageW != NULL)
+ {
+ pspPackages[cPackages].pftTableW = InitPackageW();
+ if (pspPackages[cPackages].pftTableW == NULL)
+ {
+ continue;
+ }
+ pspPackages[cPackages].pftTable =
+ pspPackages[cPackages].pftTableW;
+
+ }
+ if (InitPackageA != NULL)
+ {
+ pspPackages[cPackages].pftTableA = InitPackageA();
+ if (pspPackages[cPackages].pftTableA == NULL)
+ {
+ continue;
+ }
+
+
+ if (pspPackages[cPackages].pftTable == NULL)
+ {
+ pspPackages[cPackages].pftTable = (PSecurityFunctionTableW)
+ pspPackages[cPackages].pftTableA;
+ }
+
+ }
+
+ cPackages++;
+ }
+
+ cBuiltInPackages = cPackages;
+
+ for (cIndex = 0; cIndex < cDllPackages ; cIndex++)
+ {
+ scRet = BindPackageFromDll( PackagePath[cIndex],
+ &pspPackages[cPackages],
+ &InitPackageW,
+ &InitPackageA);
+
+ if (!NT_SUCCESS(scRet))
+ {
+ continue;
+ }
+
+ if (InitPackageW != NULL)
+ {
+ pspPackages[cPackages].pftTableW = InitPackageW();
+ if (pspPackages[cPackages].pftTableW == NULL)
+ {
+ continue;
+ }
+ pspPackages[cPackages].pftTable =
+ pspPackages[cPackages].pftTableW;
+
+ }
+ if (InitPackageA != NULL)
+ {
+ pspPackages[cPackages].pftTableA = InitPackageA();
+ if (pspPackages[cPackages].pftTableA == NULL)
+ {
+ continue;
+ }
+
+ if (pspPackages[cPackages].pftTable == NULL)
+ {
+ pspPackages[cPackages].pftTable = (PSecurityFunctionTableW)
+ pspPackages[cPackages].pftTableA;
+ }
+
+ }
+
+ cPackages++;
+ }
+
+ //
+ // Load the capabilites, names of all the packages.
+ //
+
+
+ cIndex = 0;
+ while (cIndex < cPackages)
+ {
+ if (pspPackages[cIndex].pftTableW != NULL)
+ {
+ scRet = pspPackages[cIndex].pftTableW->EnumerateSecurityPackagesW(
+ &cInstancePackages,
+ &pPackageInfoW);
+
+ if (!NT_SUCCESS(scRet))
+ {
+ goto Cleanup;
+ }
+
+ //
+ // We can only deal with one package per dll/table
+ //
+
+ if (cInstancePackages != 1)
+ {
+ //
+ // Reallocate the table of packages here.
+ //
+
+ TempPackages = LocalReAlloc(
+ pspPackages,
+ (cPackages + cInstancePackages - 1) * sizeof(SecPkg),
+ LMEM_ZEROINIT | LMEM_MOVEABLE
+ );
+ if (TempPackages == NULL) {
+ scRet = SEC_E_INSUFFICIENT_MEMORY;
+ goto Cleanup;
+ }
+
+ pspPackages = TempPackages;
+ //
+ // Move the other packages up
+ //
+
+ MoveMemory(
+ &pspPackages[cIndex+cInstancePackages],
+ &pspPackages[cIndex+1],
+ (cPackages - cIndex - 1) * sizeof(SecPkg)
+ );
+
+ //
+ // Initialize the new packages with the information
+ // supposed to already be there and the original
+ // package id
+ //
+
+ pspPackages[cIndex].dwPackageID = cIndex;
+ pspPackages[cIndex].dwOriginalPackageID = 0;
+ pspPackages[cIndex].fCapabilities = pPackageInfoW[0].fCapabilities;
+
+ for (cIndex2 = 1; cIndex2 < cInstancePackages ; cIndex2++ )
+ {
+ pspPackages[cIndex+cIndex2].pftTableA = pspPackages[cIndex].pftTableA;
+ pspPackages[cIndex+cIndex2].pftTableW = pspPackages[cIndex].pftTableW;
+ pspPackages[cIndex+cIndex2].pftTable = pspPackages[cIndex].pftTable;
+ pspPackages[cIndex+cIndex2].hInstance = NULL;
+ pspPackages[cIndex+cIndex2].dwPackageID = cIndex+cIndex2;
+ pspPackages[cIndex+cIndex2].dwOriginalPackageID = cIndex2;
+ pspPackages[cIndex+cIndex2].fCapabilities = pPackageInfoW[cIndex2].fCapabilities;
+ }
+ cPackages += cInstancePackages - 1;
+
+
+ }
+ else
+ {
+ pspPackages[cIndex].dwPackageID = cIndex;
+ if (cIndex < BuiltinPackageCount)
+ {
+ pspPackages[cIndex].dwOriginalPackageID = cIndex;
+ }
+ else
+ {
+ pspPackages[cIndex].dwOriginalPackageID = 0;
+ }
+ pspPackages[cIndex].fCapabilities = pPackageInfoW->fCapabilities;
+ }
+
+ for (cIndex2 = 0; cIndex2 < cInstancePackages ; cIndex2++)
+ {
+
+ //
+ // Allocate space for the package name. We allocate double the
+ // required amount so we can store the ansi name here too.
+ //
+
+ PackageNameLen = wcslen(pPackageInfoW[cIndex2].Name);
+ pspPackages[cIndex+cIndex2].PackageNameW =
+ LocalAlloc(0,(PackageNameLen + 1) * sizeof(WCHAR) * 2);
+ if (pspPackages[cIndex+cIndex2].PackageNameW == NULL)
+ {
+ scRet = SEC_E_INSUFFICIENT_MEMORY;
+ goto Cleanup;
+ }
+
+ wcscpy(pspPackages[cIndex+cIndex2].PackageNameW, pPackageInfoW[cIndex2].Name);
+
+ pspPackages[cIndex+cIndex2].PackageNameA = (LPSTR)
+ (pspPackages[cIndex+cIndex2].PackageNameW + PackageNameLen + 1);
+
+ wcstombs(
+ pspPackages[cIndex+cIndex2].PackageNameA,
+ pspPackages[cIndex+cIndex2].PackageNameW,
+ (PackageNameLen+1) * sizeof(WCHAR)
+ );
+
+
+ }
+
+
+ FreeContextBuffer(pPackageInfoW);
+ pPackageInfoW = NULL;
+
+ }
+ else
+ {
+ ASSERT(pspPackages[cIndex].pftTableA != NULL);
+
+ scRet = pspPackages[cIndex].pftTableA->EnumerateSecurityPackagesA(
+ &cInstancePackages,
+ &pPackageInfoA);
+
+ if (!NT_SUCCESS(scRet))
+ {
+ goto Cleanup;
+ }
+
+ //
+ // We can only deal with one package per dll/table
+ //
+
+ if (cInstancePackages != 1)
+ {
+ //
+ // Reallocate the table of packages here.
+ //
+
+ TempPackages = LocalReAlloc(
+ pspPackages,
+ (cPackages + cInstancePackages - 1) * sizeof(SecPkg),
+ LMEM_ZEROINIT | LMEM_MOVEABLE
+ );
+ if (TempPackages == NULL) {
+ scRet = SEC_E_INSUFFICIENT_MEMORY;
+ goto Cleanup;
+ }
+
+ pspPackages = TempPackages;
+
+ //
+ // Move the other packages up
+ //
+
+ MoveMemory(
+ &pspPackages[cIndex+cInstancePackages],
+ &pspPackages[cIndex+1],
+ (cPackages - cIndex - 1) * sizeof(SecPkg)
+ );
+
+ //
+ // Initialize the new packages with the information
+ // supposed to already be there and the original
+ // package id
+ //
+
+ pspPackages[cIndex].dwPackageID = cIndex;
+ pspPackages[cIndex].dwOriginalPackageID = 0;
+ pspPackages[cIndex].fCapabilities = pPackageInfoW[0].fCapabilities;
+
+ for (cIndex2 = 1; cIndex2 < cInstancePackages ; cIndex2++ )
+ {
+ pspPackages[cIndex+cIndex2].pftTableA = pspPackages[cIndex].pftTableA;
+ pspPackages[cIndex+cIndex2].pftTableW = pspPackages[cIndex].pftTableW;
+ pspPackages[cIndex+cIndex2].pftTable = pspPackages[cIndex].pftTable;
+ pspPackages[cIndex+cIndex2].hInstance = NULL;
+ pspPackages[cIndex+cIndex2].dwPackageID = cIndex+cIndex2;
+ pspPackages[cIndex+cIndex2].dwOriginalPackageID = cIndex2;
+ pspPackages[cIndex+cIndex2].fCapabilities = pPackageInfoA[cIndex2].fCapabilities;
+ }
+ cPackages += cInstancePackages - 1;
+
+ }
+ else
+ {
+ pspPackages[cIndex].dwPackageID = cIndex;
+ if (cIndex < BuiltinPackageCount)
+ {
+ pspPackages[cIndex].dwOriginalPackageID = cIndex;
+ }
+ else
+ {
+ pspPackages[cIndex].dwOriginalPackageID = 0;
+ }
+ pspPackages[cIndex].fCapabilities = pPackageInfoA->fCapabilities;
+ }
+
+ for (cIndex2 = 0; cIndex2 < cInstancePackages ; cIndex2++ )
+ {
+ //
+ // Allocate space for the package name. We allocate enough for
+ // both the ansi and wide character version.
+ //
+
+ PackageNameLen = lstrlenA(pPackageInfoA[cIndex2].Name);
+
+ pspPackages[cIndex+cIndex2].PackageNameW =
+ LocalAlloc(0,(PackageNameLen + 1) * (sizeof(CHAR) + sizeof(WCHAR)));
+ if (pspPackages[cIndex+cIndex2].PackageNameW == NULL)
+ {
+ scRet = SEC_E_INSUFFICIENT_MEMORY;
+ goto Cleanup;
+ }
+ pspPackages[cIndex+cIndex2].PackageNameA = (LPSTR)
+ (pspPackages[cIndex+cIndex2].PackageNameW + PackageNameLen + 1);
+
+ lstrcpyA(pspPackages[cIndex+cIndex2].PackageNameA, pPackageInfoA[cIndex2].Name);
+
+
+ mbstowcs(
+ pspPackages[cIndex+cIndex2].PackageNameW,
+ pspPackages[cIndex+cIndex2].PackageNameA,
+ (PackageNameLen+1) * sizeof(WCHAR)
+ );
+ }
+
+ FreeContextBuffer(pPackageInfoA);
+ pPackageInfoA = NULL;
+
+
+ }
+ cIndex += cInstancePackages;
+ }
+
+ return(SEC_E_OK);
+
+Cleanup:
+ if (pPackageInfoW != NULL) {
+ FreeContextBuffer(pPackageInfoW);
+ }
+
+ if (pPackageInfoA != NULL) {
+ FreeContextBuffer(pPackageInfoA);
+ }
+
+ UnloadPackages(min(cPackages + 1, cDllPackages + BuiltinPackageCount));
+
+ return(scRet);
+
+}
+
+//+-------------------------------------------------------------------------
+//
+// Function: LocalWcsTok
+//
+// Synopsis: takes a pointer to a string, returns a pointer to the next
+// token in the string and sets StringStart to point to the
+// end of the string.
+//
+// Effects:
+//
+// Arguments:
+//
+// Requires:
+//
+// Returns:
+//
+// Notes:
+//
+//
+//--------------------------------------------------------------------------
+
+
+LPWSTR
+LocalWcsTok(
+ LPWSTR String,
+ LPWSTR Token,
+ LPWSTR * NextStringStart
+ )
+{
+ ULONG Index;
+ ULONG Tokens;
+ LPWSTR StartString;
+ LPWSTR EndString;
+ BOOLEAN Found;
+
+ if (String == NULL)
+ {
+ *NextStringStart = NULL;
+ return(NULL);
+ }
+ Tokens = wcslen(Token);
+
+ //
+ // Find the beginning of the string.
+ //
+
+ StartString = (LPTSTR) String;
+ while (*StartString != L'\0')
+ {
+ Found = FALSE;
+ for (Index = 0; Index < Tokens; Index++)
+ {
+ if (*StartString == Token[Index])
+ {
+ StartString++;
+ Found = TRUE;
+ break;
+ }
+ }
+ if (!Found)
+ {
+ break;
+ }
+ }
+
+ //
+ // There are no more tokens in this string.
+ //
+
+ if (*StartString == L'\0')
+ {
+ *NextStringStart = NULL;
+ return(NULL);
+ }
+
+ EndString = StartString + 1;
+ while (*EndString != L'\0')
+ {
+ for (Index = 0; Index < Tokens; Index++)
+ {
+ if (*EndString == Token[Index])
+ {
+ *EndString = L'\0';
+ *NextStringStart = EndString+1;
+ return(StartString);
+ }
+ }
+ EndString++;
+ }
+ *NextStringStart = NULL;
+ return(StartString);
+
+}
+
+SECURITY_STATUS
+ReadPackageList(
+ PULONG pPackageCount,
+ LPWSTR * * pPackageArray)
+{
+ HKEY RootKey = NULL;
+ ULONG Error;
+ ULONG Type;
+ LPWSTR Packages = NULL;
+ ULONG PackageSize = 0;
+ LPWSTR PackageCopy = NULL;
+ ULONG PackageCount = 0;
+ LPWSTR PackageName;
+ LPWSTR * PackageArray = NULL;
+ ULONG Index;
+ SECURITY_STATUS Status;
+ LPWSTR TempString;
+
+ //
+ // Try to open the key. If it isn't there, that's o.k.
+ //
+
+ *pPackageCount = 0;
+ *pPackageArray = NULL;
+
+ Error = RegOpenKey(
+ HKEY_LOCAL_MACHINE,
+ L"System\\CurrentControlSet\\Control\\SecurityProviders",
+ &RootKey
+ );
+ if (Error != 0)
+ {
+ return( SEC_E_OK );
+ }
+
+ //
+ // Try to read the value. If the value is not there, that is
+ // o.k.
+ //
+
+ Error = RegQueryValueEx(
+ RootKey,
+ L"SecurityProviders",
+ NULL,
+ &Type,
+ (PUCHAR) Packages,
+ &PackageSize
+ );
+
+ if ((Error == ERROR_FILE_NOT_FOUND) ||
+ (Type != REG_SZ))
+ {
+ RegCloseKey(RootKey);
+
+ return( SEC_E_OK );
+ }
+ else if (Error != 0)
+ {
+ RegCloseKey(RootKey);
+ return(SEC_E_CANNOT_INSTALL);
+ }
+
+ if (PackageSize <= sizeof(UNICODE_NULL))
+ {
+ RegCloseKey(RootKey);
+
+ return( SEC_E_OK );
+ }
+
+ Packages = (LPWSTR) LocalAlloc(0,2 * PackageSize);
+ if (Packages == NULL)
+ {
+ RegCloseKey(RootKey);
+
+ return(SEC_E_INSUFFICIENT_MEMORY);
+ }
+
+ PackageCopy = (LPWSTR) ((PBYTE) Packages + PackageSize);
+
+ Error = RegQueryValueEx(
+ RootKey,
+ L"SecurityProviders",
+ NULL,
+ &Type,
+ (PUCHAR) Packages,
+ &PackageSize
+ );
+
+ RegCloseKey(RootKey);
+
+ if (Error != 0)
+ {
+ LocalFree(Packages);
+ return(SEC_E_CANNOT_INSTALL);
+ }
+
+ RtlCopyMemory(
+ PackageCopy,
+ Packages,
+ PackageSize
+ );
+
+
+ //
+ // Pull the package names out of the string to count the number
+ //
+
+ PackageName = LocalWcsTok(PackageCopy,L" ,", &TempString);
+ while (PackageName != NULL)
+ {
+ PackageCount++;
+ PackageName = LocalWcsTok(TempString, L" ,", &TempString);
+ }
+
+ //
+ // Now make an array of the package dll names.
+ //
+
+
+ PackageArray = (LPWSTR *) LocalAlloc(0,PackageCount * sizeof(LPWSTR));
+ if (PackageArray == NULL)
+ {
+ LocalFree(Packages);
+ return(SEC_E_INSUFFICIENT_MEMORY);
+ }
+
+ PackageName = LocalWcsTok(Packages,L" ,",&TempString);
+ Index = 0;
+ while (PackageName != NULL)
+ {
+ PackageArray[Index++] = PackageName;
+ PackageName = LocalWcsTok(TempString, L" ,",&TempString);
+ }
+
+ *pPackageCount = PackageCount;
+ *pPackageArray = PackageArray;
+
+ return( SEC_E_OK );
+
+}
+
+
+//+-------------------------------------------------------------------------
+//
+// Function: LoadAllPackages
+//
+// Synopsis: builds list of all external packages ( ones in other DLLs)
+//
+// Effects:
+//
+// Arguments:
+//
+// Requires:
+//
+// Returns:
+//
+// Notes:
+//
+//
+//--------------------------------------------------------------------------
+SECURITY_STATUS
+LoadAllPackages()
+{
+ SECURITY_STATUS Status;
+ LPWSTR * PackageArray = NULL;
+ ULONG PackageCount = 0;
+
+
+ Status = ReadPackageList( &PackageCount,
+ &PackageArray );
+
+ if ( NT_SUCCESS( Status ) )
+ {
+
+ Status = LoadKnownPackages(
+ PackageCount,
+ PackageArray
+ );
+
+
+ if ( PackageArray )
+ {
+ LocalFree( PackageArray[0] );
+
+ LocalFree( PackageArray );
+ }
+
+
+
+ if (NT_SUCCESS(Status))
+ {
+ PackageInitialized = TRUE;
+ }
+
+ }
+
+ return(Status);
+
+}
+
+
+//+---------------------------------------------------------------------------
+//
+// Function: LocatePackageW
+//
+// Synopsis: Locates a package from the dynamic array
+//
+// Arguments: [pszPackageName] -- Package name
+//
+// History: 9-10-93 RichardW Created
+//
+// Notes:
+//
+//----------------------------------------------------------------------------
+PSecPkg
+LocatePackageW(LPWSTR pszPackageName)
+{
+ ULONG cIndex;
+ PSecPkg pPackage = NULL;
+
+ //
+ // Now, we want this to be as fast as possible. BUT, this is the
+ // master pointer to the array of package controls. So, we have
+ // to do a MT-safe check to see if we can find the damn thing:
+ //
+
+ GetProcessLock();
+
+ //
+ // If the array has not been allocated yet, fail.
+ //
+
+ if (!pspPackages)
+ {
+ FreeProcessLock();
+ return(NULL);
+ }
+
+ for (cIndex = 0; cIndex < cPackages ; cIndex++ )
+ {
+ if (_wcsicmp(pszPackageName,pspPackages[cIndex].PackageNameW) == 0)
+ {
+ pPackage = &pspPackages[cIndex];
+ break;
+ }
+ }
+
+ FreeProcessLock();
+
+ return(pPackage);
+}
+
+
+//+---------------------------------------------------------------------------
+//
+// Function: LocatePackageA
+//
+// Synopsis: Locates a package from the dynamic array
+//
+// Arguments: [pszPackageName] -- Package name
+//
+// History: 9-10-93 RichardW Created
+//
+// Notes:
+//
+//----------------------------------------------------------------------------
+PSecPkg
+LocatePackageA(LPSTR pszPackageName)
+{
+ ULONG cIndex;
+ PSecPkg pPackage = NULL;
+
+ //
+ // Now, we want this to be as fast as possible. BUT, this is the
+ // master pointer to the array of package controls. So, we have
+ // to do a MT-safe check to see if we can find the damn thing:
+ //
+
+ GetProcessLock();
+
+ //
+ // If the array has not been allocated yet, fail.
+ //
+
+ if (!pspPackages)
+ {
+ FreeProcessLock();
+ return(NULL);
+ }
+
+ for (cIndex = 0; cIndex < cPackages ; cIndex++ )
+ {
+ if (lstrcmpiA(pszPackageName,pspPackages[cIndex].PackageNameA) == 0)
+ {
+ pPackage = &pspPackages[cIndex];
+ break;
+ }
+ }
+
+ FreeProcessLock();
+
+ return(pPackage);
+}
+
+
+
+//+---------------------------------------------------------------------------
+//
+// Function: DllMain
+//
+// Synopsis: Called when a process or thread attaches to or detaches from
+// a DLL
+//
+// Arguments: [Standard DllEntryPoint args]
+//
+// History: 6-20-94 DannyGl Created
+//
+// Notes:
+//
+//----------------------------------------------------------------------------
+BOOLEAN
+PackageMain(
+ ULONG fdwReason)
+{
+ switch(fdwReason)
+ {
+ case DLL_PROCESS_ATTACH:
+ InitializeCriticalSection(&csSecurity);
+
+
+ break;
+
+ case DLL_PROCESS_DETACH:
+
+ UnloadPackages(cPackages);
+
+ DeleteCriticalSection(&csSecurity);
+
+ break;
+
+ default:
+ break;
+ }
+
+ return TRUE;
+}
+
+
+//+---------------------------------------------------------------------------
+//
+// Function: InitializePackages
+//
+// Synopsis: Called whenever an API is called to make sure packages are
+// initialized.
+//
+// Arguments: none
+//
+// Notes:
+//
+//----------------------------------------------------------------------------
+
+SECURITY_STATUS
+InitializePackages(
+ )
+{
+ SECURITY_STATUS SecStatus = SEC_E_OK;
+ GetProcessLock();
+ if (!PackageInitialized)
+ {
+ SecStatus = LoadAllPackages();
+ }
+ FreeProcessLock();
+ return(SecStatus);
+}
+
+//+---------------------------------------------------------------------------
+//
+// Function: WritePackageList
+//
+// Synopsis: Writes package list back out to registry
+//
+// Arguments: [PackageCount] --
+// [PackageArray] --
+//
+// History: 6-13-96 RichardW Created
+//
+// Notes:
+//
+//----------------------------------------------------------------------------
+SECURITY_STATUS
+WritePackageList(
+ ULONG PackageCount,
+ PWSTR * PackageArray)
+{
+ HKEY Key;
+ int Error;
+ DWORD Disp;
+ DWORD Index;
+ DWORD Size;
+ PWSTR Value;
+ SECURITY_STATUS Status;
+
+ Error = RegCreateKeyEx(
+ HKEY_LOCAL_MACHINE,
+ L"System\\CurrentControlSet\\Control\\SecurityProviders",
+ 0,
+ NULL,
+ REG_OPTION_NON_VOLATILE,
+ KEY_READ | KEY_WRITE,
+ NULL,
+ &Key,
+ &Disp );
+
+
+ if ( Error == ERROR_SUCCESS )
+ {
+ if ( PackageCount == 0 )
+ {
+ RegDeleteValue( Key, L"SecurityProviders" );
+
+ RegCloseKey( Key );
+
+ return( SEC_E_OK );
+ }
+
+
+ Size = 0;
+
+ for ( Index = 0 ; Index < PackageCount ; Index ++ )
+ {
+ Size += wcslen( PackageArray[ Index ] ) + 1;
+ }
+
+ Value = LocalAlloc( LMEM_FIXED | LMEM_ZEROINIT, Size * sizeof(WCHAR) );
+
+ if ( Value )
+ {
+ *Value = L'\0';
+
+ for ( Index = 0 ; Index < PackageCount ; Index ++ )
+ {
+ wcscat( Value, PackageArray[ Index ] );
+ if (Index < PackageCount - 1)
+ {
+ wcscat( Value, L"," );
+ }
+ }
+
+ RegSetValueEx( Key,
+ L"SecurityProviders",
+ 0,
+ REG_SZ,
+ (PUCHAR) Value,
+ Size * sizeof(WCHAR) );
+
+ LocalFree( Value );
+
+ Status = SEC_E_OK ;
+
+ }
+ else
+ {
+ Status = SEC_E_INSUFFICIENT_MEMORY ;
+
+ }
+
+ RegCloseKey( Key );
+
+
+ }
+
+ else
+ {
+ Status = SEC_E_INTERNAL_ERROR ;
+ }
+
+ return( Status );
+}
+
+
+//+---------------------------------------------------------------------------
+//
+// Function: AddSecurityPackageW
+//
+// Synopsis: Adds a single package
+//
+// Arguments: [pszPackageName] --
+// [Reserved] --
+//
+// History: 6-13-96 RichardW Created
+//
+// Notes:
+//
+//----------------------------------------------------------------------------
+SECURITY_STATUS
+SEC_ENTRY
+AddSecurityPackageW(
+ LPWSTR pszPackageName,
+ PVOID Reserved)
+{
+ PWSTR * PackageArray;
+ ULONG PackageCount;
+ SECURITY_STATUS Status;
+ PWSTR * NewArray;
+ ULONG Index;
+ UNICODE_STRING Incoming;
+ UNICODE_STRING Test;
+
+ if ( Reserved )
+ {
+ return( SEC_E_INVALID_HANDLE );
+ }
+
+ Status = ReadPackageList( &PackageCount, &PackageArray );
+
+ if ( NT_SUCCESS( Status ) )
+ {
+ RtlInitUnicodeString( &Incoming, pszPackageName );
+
+ for ( Index = 0 ; Index < PackageCount ; Index++ )
+ {
+ RtlInitUnicodeString( &Test, PackageArray[Index]);
+
+ if (RtlCompareUnicodeString( &Incoming, &Test, TRUE ) == 0)
+ {
+ //
+ // Duplicate:
+ //
+
+ LocalFree( PackageArray[0] );
+
+ LocalFree( PackageArray );
+
+ return( SEC_E_OK );
+ }
+ }
+
+ PackageCount ++;
+
+ NewArray = LocalAlloc( LMEM_FIXED, sizeof( PWSTR ) * PackageCount );
+
+ if ( NewArray )
+ {
+ if ( PackageCount > 1 )
+ {
+ CopyMemory( NewArray,
+ PackageArray,
+ sizeof( PWSTR ) * (PackageCount - 1));
+ }
+
+ NewArray[ PackageCount - 1] = pszPackageName;
+
+ Status = WritePackageList( PackageCount, NewArray );
+
+ LocalFree( NewArray );
+ }
+ else
+ {
+ Status = SEC_E_INSUFFICIENT_MEMORY ;
+ }
+
+ if ( PackageArray )
+ {
+ LocalFree( PackageArray[ 0 ] );
+
+ LocalFree( PackageArray );
+
+ }
+
+
+ }
+
+ return( Status );
+
+}
+
+//+---------------------------------------------------------------------------
+//
+// Function: AddSecurityPackageA
+//
+// Synopsis: Ansi version
+//
+// Arguments: [pszPackageName] --
+// [Reserved] --
+//
+// History: 6-13-96 RichardW Created
+//
+// Notes:
+//
+//----------------------------------------------------------------------------
+SECURITY_STATUS
+SEC_ENTRY
+AddSecurityPackageA(
+ LPSTR pszPackageName,
+ PVOID Reserved )
+{
+ UNICODE_STRING Package;
+ NTSTATUS Status;
+ SECURITY_STATUS SecStatus;
+
+ Status = RtlCreateUnicodeStringFromAsciiz( &Package, pszPackageName );
+ if (NT_SUCCESS( Status ) )
+ {
+ SecStatus = AddSecurityPackageW( Package.Buffer, Reserved );
+
+ RtlFreeUnicodeString( &Package );
+
+ }
+ else
+ {
+ SecStatus = SEC_E_INSUFFICIENT_MEMORY;
+ }
+
+ return( SecStatus );
+
+}
+
+
+//+---------------------------------------------------------------------------
+//
+// Function: DeleteSecurityPackageW
+//
+// Synopsis: Deletes a single package
+//
+// Arguments: [pszPackageName] --
+//
+// History: 6-13-96 RichardW Created
+//
+// Notes:
+//
+//----------------------------------------------------------------------------
+SECURITY_STATUS
+SEC_ENTRY
+DeleteSecurityPackageW(
+ LPWSTR pszPackageName )
+{
+ PWSTR * PackageArray;
+ ULONG PackageCount;
+ SECURITY_STATUS Status;
+ PWSTR * NewArray;
+ ULONG Index;
+ ULONG NewIndex;
+ UNICODE_STRING Incoming;
+ UNICODE_STRING Test;
+ UNICODE_STRING Tail;
+ WCHAR FullPath[ MAX_PATH ];
+ PWSTR FilePart;
+ BOOL TailCompare;
+
+ RtlInitUnicodeString( &Incoming, pszPackageName );
+
+ //
+ // In case someone put a fully qualified name in the registry;
+ //
+
+
+ Status = ReadPackageList( &PackageCount, &PackageArray );
+
+ if ( NT_SUCCESS( Status ) )
+ {
+
+ NewArray = LocalAlloc( LMEM_FIXED | LMEM_ZEROINIT,
+ PackageCount * sizeof(PWSTR) );
+
+ if ( NewArray )
+ {
+ NewIndex = 0;
+
+
+ for ( Index = 0 ; Index < PackageCount ; Index++ )
+ {
+ RtlInitUnicodeString( &Test, PackageArray[Index]);
+
+ if (RtlCompareUnicodeString( &Incoming, &Test, TRUE ) != 0)
+ {
+ //
+ // Last chance. Cruft up a unicode string which is the
+ // tail of the Test value corresponding to the
+ // incoming, see if it is on some path we don't know.
+ //
+
+ if ( Test.Length >= Incoming.Length )
+ {
+
+ Tail.Length = Incoming.Length ;
+ Tail.MaximumLength = Tail.Length + sizeof( WCHAR );
+
+ Tail.Buffer = (PWSTR) (Test.Buffer +
+ ((Test.Length - Incoming.Length - sizeof(WCHAR)) /
+ sizeof(WCHAR) ) );
+
+ //
+ // Make sure this is on a boundary point, i.e. if told
+ // to delete sspc.dll, don't delete msnsspc.dll.
+ //
+
+ if ( Tail.Buffer[0] != L'\\' )
+ {
+ TailCompare = FALSE ;
+ }
+
+ else
+ {
+ Tail.Buffer ++ ;
+
+ TailCompare = RtlCompareUnicodeString( &Tail,
+ &Incoming, FALSE) == 0;
+ }
+
+ }
+ else
+ {
+ TailCompare = FALSE ;
+ }
+
+ if ( TailCompare == FALSE )
+ {
+
+ //
+ // This is a keeper; move it to the new list
+ //
+
+ NewArray[ NewIndex ++ ] = PackageArray[ Index ];
+ }
+
+ } // Short Name compare
+ }
+
+ //
+ // We have removed it if it existed; we have copied the array
+ // if it didn't.
+ //
+
+ Status = WritePackageList( NewIndex, NewArray );
+
+ LocalFree( NewArray );
+
+ }
+ else
+ {
+ Status = SEC_E_INSUFFICIENT_MEMORY ;
+ }
+
+ if (PackageArray)
+ {
+ LocalFree( PackageArray[0] );
+
+ LocalFree( PackageArray );
+ }
+
+ }
+
+ return( Status );
+
+}
+
+
+//+---------------------------------------------------------------------------
+//
+// Function: DeleteSecurityPackageA
+//
+// Synopsis: Ansi version
+//
+// Arguments: [pszPackageName] --
+//
+// History: 6-13-96 RichardW Created
+//
+// Notes:
+//
+//----------------------------------------------------------------------------
+SECURITY_STATUS
+SEC_ENTRY
+DeleteSecurityPackageA(
+ LPSTR pszPackageName )
+{
+ UNICODE_STRING Package;
+ NTSTATUS Status;
+ SECURITY_STATUS SecStatus;
+
+ Status = RtlCreateUnicodeStringFromAsciiz( &Package, pszPackageName );
+ if (NT_SUCCESS( Status ) )
+ {
+ SecStatus = DeleteSecurityPackageW( Package.Buffer );
+
+ RtlFreeUnicodeString( &Package );
+
+ }
+ else
+ {
+ SecStatus = SEC_E_INSUFFICIENT_MEMORY;
+ }
+
+ return( SecStatus );
+
+}
diff --git a/private/net/svcdlls/ntlmssp/package/secdll.h b/private/net/svcdlls/ntlmssp/package/secdll.h
new file mode 100644
index 000000000..5e9d074c8
--- /dev/null
+++ b/private/net/svcdlls/ntlmssp/package/secdll.h
@@ -0,0 +1,95 @@
+//+-----------------------------------------------------------------------
+//
+// File: SECDLL.H
+//
+// Contents: Security DLL private defines
+//
+//
+// History: 11 Mar 92 RichardW Recreated
+//
+//------------------------------------------------------------------------
+
+#ifndef __SECDLL_H__
+#define __SECDLL_H__
+
+// Global headerfinitions
+#include <ntlmcomn.h>
+#include <ntlmitf.h>
+
+typedef void (SEC_ENTRY * EXIT_SECURITY_INTERFACE) (void);
+
+typedef struct _SecPkg {
+ HINSTANCE hInstance;
+ ULONG dwPackageID;
+ ULONG dwOriginalPackageID;
+ ULONG fCapabilities;
+ ULONG fState;
+ LPWSTR PackageNameW;
+ LPSTR PackageNameA;
+ PSecurityFunctionTableA pftTableA; // Table for ansi-specific calls
+ PSecurityFunctionTableW pftTableW; // Table for unicode-specific calls
+ PSecurityFunctionTableW pftTable; // Table for non-specific calls
+ EXIT_SECURITY_INTERFACE pfUnloadPackage;
+} SecPkg, *PSecPkg;
+
+
+#if DBG
+
+
+
+#define DebugStmt(x) x
+
+//
+// BUGBUG: need to figure out how to debug print
+//
+
+#define DebugLog(x)
+
+#else
+
+#define DebugStmt(x)
+#define DebugLog(x)
+
+#endif
+
+
+
+
+
+
+//
+// Global variables
+//
+
+extern PSecPkg pspPackages;
+extern ULONG cPackages;
+extern RTL_CRITICAL_SECTION csSecurity;
+
+// Note: We switched from const to #define because the compiler doesn't optimize the former
+// extern const ULONG BuiltinPackageCount = sizeof(BuiltinPackageInitA) / sizeof(*BuiltinPackageInitA);
+
+#define BuiltinPackageCount 2
+
+
+//
+// Process wide synchronization
+//
+// NOTE: UPDATE THE MACRO if the name of the critical section changes
+//
+
+
+#define GetProcessLock() (void) EnterCriticalSection(&csSecurity)
+#define FreeProcessLock() (void) LeaveCriticalSection(&csSecurity)
+
+
+
+PSecPkg
+LocatePackageW(LPWSTR PackageNameW);
+
+PSecPkg
+LocatePackageA(LPSTR PackageNameA);
+
+SECURITY_STATUS
+InitializePackages();
+
+#endif // __SECDLL_H__
diff --git a/private/net/svcdlls/ntlmssp/package/sources b/private/net/svcdlls/ntlmssp/package/sources
new file mode 100644
index 000000000..3c6e91d99
--- /dev/null
+++ b/private/net/svcdlls/ntlmssp/package/sources
@@ -0,0 +1,42 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+
+Author:
+
+ Steve Wood (stevewo) 12-Apr-1990
+
+NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl
+
+!ENDIF
+
+MAJORCOMP=net
+MINORCOMP=security
+
+TARGETNAME=secpkg
+TARGETPATH=obj
+TARGETTYPE=LIBRARY
+TARGETLIBS= \
+
+C_DEFINES=-DSECURITY_WIN32 -DUNICODE
+MSC_WARNING_LEVEL=/W3 /WX
+INCLUDES=..;..\common;..\..\..\inc;..\..\..\..\inc;..\..\..\..\lsa\crypt\engine
+
+SOURCES= \
+ package.c \
+ stubs.c \
+ userstub.c \
+ stubsa.c \
+ userstba.c
diff --git a/private/net/svcdlls/ntlmssp/package/stubs.c b/private/net/svcdlls/ntlmssp/package/stubs.c
new file mode 100644
index 000000000..110f16bc9
--- /dev/null
+++ b/private/net/svcdlls/ntlmssp/package/stubs.c
@@ -0,0 +1,812 @@
+//+-----------------------------------------------------------------------
+//
+// Microsoft Windows
+//
+// Copyright (c) Microsoft Corporation 1992 - 1994
+//
+// File: stubs.cxx
+//
+// Contents: user-mode stubs for security API
+//
+//
+// History: 3/5/94 MikeSw Created
+//
+//------------------------------------------------------------------------
+#include "secdll.h"
+
+SecurityFunctionTableW SecTableW = {SECURITY_SUPPORT_PROVIDER_INTERFACE_VERSION,
+ EnumerateSecurityPackagesW,
+ NULL,
+ AcquireCredentialsHandleW,
+ FreeCredentialsHandle,
+ NULL, // LogonUser
+ InitializeSecurityContextW,
+ AcceptSecurityContext,
+ CompleteAuthToken,
+ DeleteSecurityContext,
+ ApplyControlToken,
+ QueryContextAttributesW,
+ ImpersonateSecurityContext,
+ RevertSecurityContext,
+ MakeSignature,
+ VerifySignature,
+ FreeContextBuffer,
+ QuerySecurityPackageInfoW,
+ SealMessage,
+ UnsealMessage,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ QuerySecurityContextToken
+ };
+
+
+
+//+-------------------------------------------------------------------------
+//
+// Function: InitSecurityInterfaceW
+//
+// Synopsis:
+//
+// Effects:
+//
+// Arguments:
+//
+// Requires:
+//
+// Returns:
+//
+// Notes:
+//
+//
+//--------------------------------------------------------------------------
+PSecurityFunctionTableW SEC_ENTRY
+InitSecurityInterfaceW(VOID)
+{
+
+ if (!NT_SUCCESS(InitializePackages()))
+ {
+ return(NULL);
+ }
+ return(&SecTableW);
+}
+
+
+//+-------------------------------------------------------------------------
+//
+// Function: AcquireCredentialsHandleW
+//
+// Synopsis:
+//
+// Effects:
+//
+// Arguments:
+//
+// Requires:
+//
+// Returns:
+//
+// Notes:
+//
+//
+//--------------------------------------------------------------------------
+
+
+
+SECURITY_STATUS SEC_ENTRY
+AcquireCredentialsHandleW(
+ LPWSTR pszPrincipal, // Name of principal
+ LPWSTR pszPackageName, // Name of package
+ unsigned long fCredentialUse, // Flags indicating use
+ void SEC_FAR * pvLogonId, // Pointer to logon ID
+ void SEC_FAR * pAuthData, // Package specific data
+ SEC_GET_KEY_FN pGetKeyFn, // Pointer to GetKey() func
+ void SEC_FAR * pvGetKeyArgument, // Value to pass to GetKey()
+ PCredHandle phCredential, // (out) Cred Handle
+ PTimeStamp ptsExpiry // (out) Lifetime (optional)
+ )
+{
+ SECURITY_STATUS scRet = SEC_E_OK;
+ PSecPkg pPackage;
+
+ scRet = InitializePackages();
+ if (!NT_SUCCESS(scRet))
+ {
+ return(scRet);
+ }
+
+ if (!pszPackageName)
+ {
+ return(SEC_E_SECPKG_NOT_FOUND);
+ }
+
+ if ((pPackage = LocatePackageW(pszPackageName)) == NULL)
+ {
+ return(SEC_E_SECPKG_NOT_FOUND);
+ }
+
+ scRet = pPackage->pftTableW->AcquireCredentialsHandleW(
+ pszPrincipal,
+ pszPackageName,
+ fCredentialUse,
+ pvLogonId,
+ pAuthData,
+ pGetKeyFn,
+ pvGetKeyArgument,
+ phCredential,
+ ptsExpiry);
+
+ //
+ // Only set the package id if it is not a builtin package.
+ //
+
+ if ((scRet == SEC_E_OK) &&
+ (pPackage->dwPackageID >= BuiltinPackageCount))
+ {
+ phCredential->dwLower = pPackage->dwPackageID;
+ }
+ return(scRet);
+
+}
+
+
+
+//+-------------------------------------------------------------------------
+//
+// Function: FreeCredentialsHandle
+//
+// Synopsis:
+//
+// Effects:
+//
+// Arguments:
+//
+// Requires:
+//
+// Returns:
+//
+// Notes:
+//
+//
+//--------------------------------------------------------------------------
+
+
+SECURITY_STATUS SEC_ENTRY
+FreeCredentialsHandle(
+ PCredHandle phCredential // Handle to free
+ )
+{
+ CredHandle TempCredHandle;
+
+ TempCredHandle.dwUpper = phCredential->dwUpper;
+ TempCredHandle.dwLower = pspPackages[phCredential->dwLower].dwOriginalPackageID;
+ return(pspPackages[phCredential->dwLower].pftTable->FreeCredentialHandle(&TempCredHandle));
+}
+
+
+
+
+//+-------------------------------------------------------------------------
+//
+// Function: InitializeSecurityContextW
+//
+// Synopsis:
+//
+// Effects:
+//
+// Arguments:
+//
+// Requires:
+//
+// Returns:
+//
+// Notes:
+//
+//
+//--------------------------------------------------------------------------
+
+
+SECURITY_STATUS SEC_ENTRY
+InitializeSecurityContextW(
+ PCredHandle phCredential, // Cred to base context
+ PCtxtHandle phContext, // Existing context (OPT)
+ LPWSTR pszTargetName, // Name of target
+ unsigned long fContextReq, // Context Requirements
+ unsigned long Reserved1, // Reserved, MBZ
+ unsigned long TargetDataRep, // Data rep of target
+ PSecBufferDesc pInput, // Input Buffers
+ unsigned long Reserved2, // Reserved, MBZ
+ PCtxtHandle phNewContext, // (out) New Context handle
+ PSecBufferDesc pOutput, // (inout) Output Buffers
+ unsigned long SEC_FAR * pfContextAttr, // (out) Context attrs
+ PTimeStamp ptsExpiry // (out) Life span (OPT)
+ )
+{
+ SECURITY_STATUS scRet = SEC_E_OK;
+ CredHandle TempCredHandle;
+ CtxtHandle TempCtxtHandle;
+ ULONG PackageID, OriginalPackageID;
+
+ //
+ // They need to provide at least one of these two
+ //
+
+ if (!phCredential && !phContext)
+ {
+ return(SEC_E_INVALID_HANDLE);
+ }
+
+
+ //
+ // If the credential pointer is not NULL, figure out the package ID
+ // from it. Use that to find the original package ID. If this
+ // isn't a builtin package (which makes special use of the lower part)
+ // set the temp handle to be the same upper value with the original
+ // package ID for the lower handle value.
+ //
+
+
+ if (phCredential != NULL)
+ {
+ PackageID = phCredential->dwLower;
+ OriginalPackageID = pspPackages[PackageID].dwOriginalPackageID;
+
+ if (PackageID >= BuiltinPackageCount)
+ {
+ TempCredHandle.dwUpper = phCredential->dwUpper;
+ TempCredHandle.dwLower = OriginalPackageID;
+
+ if (phContext != NULL)
+ {
+ TempCtxtHandle.dwUpper = phContext->dwUpper;
+ TempCtxtHandle.dwLower = OriginalPackageID;
+ }
+ }
+ else
+ {
+ TempCredHandle = *phCredential;
+ if (phContext != NULL)
+ {
+ TempCtxtHandle = *phContext;
+ }
+ }
+
+ }
+ else
+ {
+ //
+ // The credential handle is NULL, so pick the values from the
+ // context handle
+ //
+
+ PackageID = phContext->dwLower;
+ OriginalPackageID = pspPackages[PackageID].dwOriginalPackageID;
+
+ TempCtxtHandle.dwUpper = phContext->dwUpper;
+ if (PackageID >= BuiltinPackageCount)
+ {
+ TempCtxtHandle.dwLower = OriginalPackageID;
+ }
+ else
+ {
+ TempCtxtHandle.dwLower = PackageID;
+ }
+
+ }
+
+
+
+
+
+ scRet = pspPackages[PackageID].pftTableW->InitializeSecurityContextW(
+ (phCredential != NULL) ? &TempCredHandle : NULL,
+ (phContext != NULL) ? &TempCtxtHandle : NULL,
+ pszTargetName,
+ fContextReq,
+ Reserved1,
+ TargetDataRep,
+ pInput,
+ Reserved2,
+ phNewContext,
+ pOutput,
+ pfContextAttr,
+ ptsExpiry);
+
+ //
+ // Only set the lower part of the credential handle if we aren't
+ // dealing with builtin packages - they know what to do.
+ //
+
+ if (NT_SUCCESS(scRet) && (phNewContext) &&
+ (PackageID >= BuiltinPackageCount))
+ {
+ phNewContext->dwLower = PackageID;
+ }
+
+
+ return(scRet);
+}
+
+
+
+//+-------------------------------------------------------------------------
+//
+// Function: AcceptSecurityContext
+//
+// Synopsis:
+//
+// Effects:
+//
+// Arguments:
+//
+// Requires:
+//
+// Returns:
+//
+// Notes:
+//
+//
+//--------------------------------------------------------------------------
+
+
+SECURITY_STATUS SEC_ENTRY
+AcceptSecurityContext(
+ PCredHandle phCredential, // Cred to base context
+ PCtxtHandle phContext, // Existing context (OPT)
+ PSecBufferDesc pInput, // Input buffer
+ unsigned long fContextReq, // Context Requirements
+ unsigned long TargetDataRep, // Target Data Rep
+ PCtxtHandle phNewContext, // (out) New context handle
+ PSecBufferDesc pOutput, // (inout) Output buffers
+ unsigned long SEC_FAR * pfContextAttr, // (out) Context attributes
+ PTimeStamp ptsExpiry // (out) Life span (OPT)
+ )
+{
+ SECURITY_STATUS scRet = SEC_E_OK;
+ CredHandle TempCredHandle;
+ CtxtHandle TempCtxtHandle;
+ ULONG PackageID, OriginalPackageID;
+
+ //
+ // They need to provide at least one of these two
+ //
+
+ if (!phCredential && !phContext)
+ {
+ return(SEC_E_INVALID_HANDLE);
+ }
+
+
+ //
+ // If the credential pointer is not NULL, figure out the package ID
+ // from it. Use that to find the original package ID. If this
+ // isn't a builtin package (which makes special use of the lower part)
+ // set the temp handle to be the same upper value with the original
+ // package ID for the lower handle value.
+ //
+
+
+ if (phCredential != NULL)
+ {
+ PackageID = phCredential->dwLower;
+ OriginalPackageID = pspPackages[PackageID].dwOriginalPackageID;
+
+
+ if (PackageID >= BuiltinPackageCount)
+ {
+ TempCredHandle.dwUpper = phCredential->dwUpper;
+ TempCredHandle.dwLower = OriginalPackageID;
+
+ if (phContext != NULL)
+ {
+ TempCtxtHandle.dwUpper = phContext->dwUpper;
+ TempCtxtHandle.dwLower = OriginalPackageID;
+ }
+ }
+ else
+ {
+ TempCredHandle = *phCredential;
+ if (phContext != NULL)
+ {
+ TempCtxtHandle = *phContext;
+ }
+ }
+
+ }
+ else
+ {
+ //
+ // The credential handle is NULL, so pick the values from the
+ // context handle
+ //
+
+ PackageID = phContext->dwLower;
+ OriginalPackageID = pspPackages[PackageID].dwOriginalPackageID;
+
+ TempCtxtHandle.dwUpper = phContext->dwUpper;
+ TempCtxtHandle.dwLower = OriginalPackageID;
+
+ }
+
+
+
+
+ scRet = pspPackages[PackageID].pftTable->AcceptSecurityContext(
+ (phCredential != NULL) ? &TempCredHandle : NULL,
+ (phContext != NULL) ? & TempCtxtHandle : NULL,
+ pInput,
+ fContextReq,
+ TargetDataRep,
+ phNewContext,
+ pOutput,
+ pfContextAttr,
+ ptsExpiry);
+
+
+ //
+ // Only set the lower part of the credential handle if we aren't
+ // dealing with builtin packages - they know what to do.
+ //
+
+
+ if (NT_SUCCESS(scRet) && (phNewContext) &&
+ (PackageID >= BuiltinPackageCount))
+ {
+ phNewContext->dwLower = PackageID;
+ }
+
+ return(scRet);
+
+}
+
+
+
+
+
+
+//+-------------------------------------------------------------------------
+//
+// Function: DeleteSecurityContext
+//
+// Synopsis:
+//
+// Effects:
+//
+// Arguments:
+//
+// Requires:
+//
+// Returns:
+//
+// Notes:
+//
+//
+//--------------------------------------------------------------------------
+
+
+SECURITY_STATUS SEC_ENTRY
+DeleteSecurityContext(
+ PCtxtHandle phContext // Context to delete
+ )
+{
+ CtxtHandle TempCtxtHandle;
+
+ if (!phContext)
+ {
+ return(SEC_E_INVALID_HANDLE);
+ }
+
+ TempCtxtHandle.dwUpper = phContext->dwUpper;
+ TempCtxtHandle.dwLower = pspPackages[phContext->dwLower].dwOriginalPackageID;
+
+ return( pspPackages[phContext->dwLower].pftTable->DeleteSecurityContext(
+ &TempCtxtHandle));
+
+
+
+}
+
+
+
+//+-------------------------------------------------------------------------
+//
+// Function: ApplyControlToken
+//
+// Synopsis:
+//
+// Effects:
+//
+// Arguments:
+//
+// Requires:
+//
+// Returns:
+//
+// Notes:
+//
+//
+//--------------------------------------------------------------------------
+
+
+SECURITY_STATUS SEC_ENTRY
+ApplyControlToken(
+ PCtxtHandle phContext, // Context to modify
+ PSecBufferDesc pInput // Input token to apply
+ )
+{
+ SECURITY_STATUS scRet = SEC_E_OK;
+ CtxtHandle TempCtxtHandle;
+
+
+ if (!phContext)
+ {
+ return(SEC_E_INVALID_HANDLE);
+ }
+
+ TempCtxtHandle.dwUpper = phContext->dwUpper;
+ TempCtxtHandle.dwLower = pspPackages[phContext->dwLower].dwOriginalPackageID;
+
+ scRet = pspPackages[phContext->dwLower].pftTable->ApplyControlToken(
+ &TempCtxtHandle,
+ pInput);
+
+
+ return(scRet);
+
+
+}
+
+
+
+
+//+-------------------------------------------------------------------------
+//
+// Function: EnumerateSecurityPackagesW
+//
+// Synopsis:
+//
+// Effects:
+//
+// Arguments:
+//
+// Requires:
+//
+// Returns:
+//
+// Notes:
+//
+//
+//--------------------------------------------------------------------------
+
+
+
+SECURITY_STATUS SEC_ENTRY
+EnumerateSecurityPackagesW(
+ unsigned long SEC_FAR * pcPackages, // Receives num. packages
+ PSecPkgInfoW SEC_FAR * ppPackageInfo // Receives array of info
+ )
+{
+ SECURITY_STATUS scRet = SEC_E_OK;
+ PSecPkgInfoW * ppTempPkgInfo = NULL;
+ PSecPkgInfoW pFinalPkgInfo = NULL;
+ ULONG cIndex;
+ ULONG cTempPackages;
+ ULONG cbFinalSize = 0;
+ PBYTE Where;
+
+ scRet = InitializePackages();
+ if (!NT_SUCCESS(scRet))
+ {
+ return(scRet);
+ }
+
+ //
+ // NOTE:
+ //
+ // Because the first two packages are actually the same one, we
+ // use the count of packages - 1 for enumerating. In addition,
+ // we start at 1 instead of zero for calling Enumerate, and subtact
+ // 1 for the index into the output buffers.
+ //
+
+ ppTempPkgInfo = (PSecPkgInfoW *) LocalAlloc(LMEM_ZEROINIT,
+ sizeof(PSecPkgInfoW) * (cPackages-1));
+ if (ppTempPkgInfo == NULL)
+ {
+ return(SEC_E_INSUFFICIENT_MEMORY);
+ }
+
+ //
+ // Loop through the packages and enumerate packages from each one
+ //
+
+ for ( cIndex = 1; cIndex < cPackages ; cIndex++ )
+ {
+ scRet = pspPackages[cIndex].pftTableW->QuerySecurityPackageInfoW(
+ pspPackages[cIndex].PackageNameW,
+ &ppTempPkgInfo[cIndex-1]);
+
+ //
+ // We require that each package return only 1 package
+ //
+
+ if (scRet != SEC_E_OK)
+ {
+ goto Cleanup;
+ }
+ cbFinalSize += sizeof(SecPkgInfoW)
+ + (wcslen(ppTempPkgInfo[cIndex-1]->Name) + 1) * sizeof(WCHAR)
+ + (wcslen(ppTempPkgInfo[cIndex-1]->Comment) + 1) * sizeof(WCHAR);
+
+ }
+
+ //
+ // Copy the data into one buffer, so it can be freed with LocalFree.
+ //
+
+ pFinalPkgInfo = (PSecPkgInfoW) LocalAlloc(0,cbFinalSize);
+ if (pFinalPkgInfo == NULL)
+ {
+ scRet = SEC_E_INSUFFICIENT_MEMORY;
+ goto Cleanup;
+ }
+
+
+
+ Where = (cPackages - 1) * sizeof(SecPkgInfoW) + (PBYTE) pFinalPkgInfo;
+
+ for (cIndex = 0; cIndex < cPackages - 1 ; cIndex++ )
+ {
+ //
+ // First copy the whole structure
+ //
+
+ CopyMemory( &pFinalPkgInfo[cIndex],
+ ppTempPkgInfo[cIndex],
+ sizeof(SecPkgInfo));
+
+ //
+ // Now marshall the strings
+ //
+
+ pFinalPkgInfo[cIndex].Name = (LPWSTR) Where;
+
+ Where += (wcslen(ppTempPkgInfo[cIndex]->Name) + 1) * sizeof(WCHAR);
+ CopyMemory( pFinalPkgInfo[cIndex].Name,
+ ppTempPkgInfo[cIndex]->Name,
+ Where - (PBYTE) pFinalPkgInfo[cIndex].Name );
+
+ pFinalPkgInfo[cIndex].Comment = (LPWSTR) Where;
+ Where += (wcslen(ppTempPkgInfo[cIndex]->Comment) + 1) * sizeof(WCHAR);
+ CopyMemory( pFinalPkgInfo[cIndex].Comment,
+ ppTempPkgInfo[cIndex]->Comment,
+ Where - (PBYTE) pFinalPkgInfo[cIndex].Comment );
+
+ }
+ ASSERT(Where == (PBYTE) pFinalPkgInfo + cbFinalSize);
+
+ scRet = SEC_E_OK;
+
+
+
+Cleanup:
+
+ //
+ // Clean up the temporary copy
+ //
+
+ if (ppTempPkgInfo != NULL)
+ {
+ for (cIndex = 0; cIndex < cPackages - 1 ; cIndex++ )
+ {
+ if (ppTempPkgInfo[cIndex] != NULL)
+ {
+ FreeContextBuffer(ppTempPkgInfo[cIndex]);
+ }
+ }
+ LocalFree(ppTempPkgInfo);
+ }
+
+ if (!NT_SUCCESS(scRet))
+ {
+ if (pFinalPkgInfo != NULL)
+ {
+ LocalFree(pFinalPkgInfo);
+ }
+ }
+ else
+ {
+ *ppPackageInfo = pFinalPkgInfo;
+ *pcPackages = cPackages - 1;
+ }
+
+ return(scRet);
+
+}
+
+
+
+//+-------------------------------------------------------------------------
+//
+// Function: QuerySecurityPackageInfoW
+//
+// Synopsis:
+//
+// Effects:
+//
+// Arguments:
+//
+// Requires:
+//
+// Returns:
+//
+// Notes:
+//
+//
+//--------------------------------------------------------------------------
+
+
+SECURITY_STATUS SEC_ENTRY
+QuerySecurityPackageInfoW(
+ LPWSTR pszPackageName, // Name of package
+ PSecPkgInfoW SEC_FAR * pPackageInfo // Receives package info
+ )
+{
+ SECURITY_STATUS scRet;
+ PSecPkg pPackage;
+
+ scRet = InitializePackages();
+ if (!NT_SUCCESS(scRet))
+ {
+ return(scRet);
+ }
+
+ pPackage = LocatePackageW(pszPackageName);
+ if (pPackage == NULL)
+ {
+ return(SEC_E_SECPKG_NOT_FOUND);
+ }
+
+
+ scRet = pPackage->pftTableW->QuerySecurityPackageInfoW(
+ pszPackageName,
+ pPackageInfo
+ );
+
+ return(scRet);
+
+}
+
+
+
+
+
+
+//+-------------------------------------------------------------------------
+//
+// Function: FreeContextBuffer
+//
+// Synopsis:
+//
+// Effects:
+//
+// Arguments:
+//
+// Requires:
+//
+// Returns:
+//
+// Notes:
+//
+//
+//--------------------------------------------------------------------------
+
+SECURITY_STATUS SEC_ENTRY
+FreeContextBuffer(
+ void SEC_FAR * pvContextBuffer
+ )
+{
+ LocalFree(pvContextBuffer);
+ return(SEC_E_OK);
+}
diff --git a/private/net/svcdlls/ntlmssp/package/stubsa.c b/private/net/svcdlls/ntlmssp/package/stubsa.c
new file mode 100644
index 000000000..5ebc9c62c
--- /dev/null
+++ b/private/net/svcdlls/ntlmssp/package/stubsa.c
@@ -0,0 +1,516 @@
+//+-----------------------------------------------------------------------
+//
+// Microsoft Windows
+//
+// Copyright (c) Microsoft Corporation 1992 - 1994
+//
+// File: stubs.cxx
+//
+// Contents: user-mode stubs for security API
+//
+//
+// History: 3/5/94 MikeSw Created
+//
+//------------------------------------------------------------------------
+#include "secdll.h"
+
+SecurityFunctionTableA SecTableA = {SECURITY_SUPPORT_PROVIDER_INTERFACE_VERSION,
+ EnumerateSecurityPackagesA,
+ NULL,
+ AcquireCredentialsHandleA,
+ FreeCredentialsHandle,
+ NULL, // LogonUser
+ InitializeSecurityContextA,
+ AcceptSecurityContext,
+ CompleteAuthToken,
+ DeleteSecurityContext,
+ ApplyControlToken,
+ QueryContextAttributesA,
+ ImpersonateSecurityContext,
+ RevertSecurityContext,
+ MakeSignature,
+ VerifySignature,
+ FreeContextBuffer,
+ QuerySecurityPackageInfoA,
+ SealMessage,
+ UnsealMessage,
+ };
+
+
+
+//+-------------------------------------------------------------------------
+//
+// Function: InitSecurityInterface
+//
+// Synopsis:
+//
+// Effects:
+//
+// Arguments:
+//
+// Requires:
+//
+// Returns:
+//
+// Notes:
+//
+//
+//--------------------------------------------------------------------------
+PSecurityFunctionTableA SEC_ENTRY
+InitSecurityInterfaceA(VOID)
+{
+ if (!NT_SUCCESS(InitializePackages()))
+ {
+ return(NULL);
+ }
+ return(&SecTableA);
+}
+
+
+//+-------------------------------------------------------------------------
+//
+// Function: AcquireCredentialsHandleA
+//
+// Synopsis:
+//
+// Effects:
+//
+// Arguments:
+//
+// Requires:
+//
+// Returns:
+//
+// Notes:
+//
+//
+//--------------------------------------------------------------------------
+
+
+
+SECURITY_STATUS SEC_ENTRY
+AcquireCredentialsHandleA(
+ LPSTR pszPrincipal, // Name of principal
+ LPSTR pszPackageName, // Name of package
+ unsigned long fCredentialUse, // Flags indicating use
+ void SEC_FAR * pvLogonId, // Pointer to logon ID
+ void SEC_FAR * pAuthData, // Package specific data
+ SEC_GET_KEY_FN pGetKeyFn, // Pointer to GetKey() func
+ void SEC_FAR * pvGetKeyArgument, // Value to pass to GetKey()
+ PCredHandle phCredential, // (out) Cred Handle
+ PTimeStamp ptsExpiry // (out) Lifetime (optional)
+ )
+{
+ SECURITY_STATUS scRet = SEC_E_OK;
+ PSecPkg pPackage;
+
+ scRet = InitializePackages();
+ if (!NT_SUCCESS(scRet))
+ {
+ return(scRet);
+ }
+
+ if (!pszPackageName)
+ {
+ return(SEC_E_SECPKG_NOT_FOUND);
+ }
+
+ if ((pPackage = LocatePackageA(pszPackageName)) == NULL)
+ {
+ return(SEC_E_SECPKG_NOT_FOUND);
+ }
+
+ scRet = pPackage->pftTableA->AcquireCredentialsHandleA(
+ pszPrincipal,
+ pszPackageName,
+ fCredentialUse,
+ pvLogonId,
+ pAuthData,
+ pGetKeyFn,
+ pvGetKeyArgument,
+ phCredential,
+ ptsExpiry);
+
+ //
+ // Only set the package id if it is not a builtin package.
+ //
+
+ if ((scRet == SEC_E_OK) &&
+ (pPackage->dwPackageID >= BuiltinPackageCount))
+ {
+ phCredential->dwLower = pPackage->dwPackageID;
+ }
+ return(scRet);
+
+}
+
+
+
+
+//+-------------------------------------------------------------------------
+//
+// Function: InitializeSecurityContextA
+//
+// Synopsis:
+//
+// Effects:
+//
+// Arguments:
+//
+// Requires:
+//
+// Returns:
+//
+// Notes:
+//
+//
+//--------------------------------------------------------------------------
+
+
+SECURITY_STATUS SEC_ENTRY
+InitializeSecurityContextA(
+ PCredHandle phCredential, // Cred to base context
+ PCtxtHandle phContext, // Existing context (OPT)
+ LPSTR pszTargetName, // Name of target
+ unsigned long fContextReq, // Context Requirements
+ unsigned long Reserved1, // Reserved, MBZ
+ unsigned long TargetDataRep, // Data rep of target
+ PSecBufferDesc pInput, // Input Buffers
+ unsigned long Reserved2, // Reserved, MBZ
+ PCtxtHandle phNewContext, // (out) New Context handle
+ PSecBufferDesc pOutput, // (inout) Output Buffers
+ unsigned long SEC_FAR * pfContextAttr, // (out) Context attrs
+ PTimeStamp ptsExpiry // (out) Life span (OPT)
+ )
+{
+ SECURITY_STATUS scRet = SEC_E_OK;
+ CredHandle TempCredHandle;
+ CtxtHandle TempCtxtHandle;
+ ULONG PackageID, OriginalPackageID;
+
+ //
+ // They need to provide at least one of these two
+ //
+
+ if (!phCredential && !phContext)
+ {
+ return(SEC_E_INVALID_HANDLE);
+ }
+
+
+ //
+ // If the credential pointer is not NULL, figure out the package ID
+ // from it. Use that to find the original package ID. If this
+ // isn't a builtin package (which makes special use of the lower part)
+ // set the temp handle to be the same upper value with the original
+ // package ID for the lower handle value.
+ //
+
+
+ if (phCredential != NULL)
+ {
+ PackageID = phCredential->dwLower;
+ OriginalPackageID = pspPackages[PackageID].dwOriginalPackageID;
+
+ if (PackageID >= BuiltinPackageCount)
+ {
+ TempCredHandle.dwUpper = phCredential->dwUpper;
+ TempCredHandle.dwLower = OriginalPackageID;
+
+ if (phContext != NULL)
+ {
+ TempCtxtHandle.dwUpper = phContext->dwUpper;
+ TempCtxtHandle.dwLower = OriginalPackageID;
+ }
+ }
+ else
+ {
+ TempCredHandle = *phCredential;
+ if (phContext != NULL)
+ {
+ TempCtxtHandle = *phContext;
+ }
+ }
+
+ }
+ else
+ {
+ //
+ // The credential handle is NULL, so pick the values from the
+ // context handle
+ //
+
+ PackageID = phContext->dwLower;
+ OriginalPackageID = pspPackages[PackageID].dwOriginalPackageID;
+
+ TempCtxtHandle.dwUpper = phContext->dwUpper;
+ if (PackageID >= BuiltinPackageCount)
+ {
+ TempCtxtHandle.dwLower = OriginalPackageID;
+ }
+ else
+ {
+ TempCtxtHandle.dwLower = PackageID;
+ }
+
+ }
+
+
+
+
+
+ scRet = pspPackages[PackageID].pftTableA->InitializeSecurityContextA(
+ (phCredential != NULL) ? &TempCredHandle : NULL,
+ (phContext != NULL) ? &TempCtxtHandle : NULL,
+ pszTargetName,
+ fContextReq,
+ Reserved1,
+ TargetDataRep,
+ pInput,
+ Reserved2,
+ phNewContext,
+ pOutput,
+ pfContextAttr,
+ ptsExpiry);
+
+
+
+
+ //
+ // Only set the lower part of the credential handle if we aren't
+ // dealing with builtin packages - they know what to do.
+ //
+
+ if (NT_SUCCESS(scRet) && (phNewContext) &&
+ (PackageID >= BuiltinPackageCount))
+ {
+ phNewContext->dwLower = PackageID;
+ }
+
+
+ return(scRet);
+}
+
+
+
+
+
+//+-------------------------------------------------------------------------
+//
+// Function: EnumerateSecurityPackagesA
+//
+// Synopsis:
+//
+// Effects:
+//
+// Arguments:
+//
+// Requires:
+//
+// Returns:
+//
+// Notes:
+//
+//
+//--------------------------------------------------------------------------
+
+
+
+SECURITY_STATUS SEC_ENTRY
+EnumerateSecurityPackagesA(
+ unsigned long SEC_FAR * pcPackages, // Receives num. packages
+ PSecPkgInfoA SEC_FAR * ppPackageInfo // Receives array of info
+ )
+{
+ SECURITY_STATUS scRet = SEC_E_OK;
+ PSecPkgInfoA * ppTempPkgInfo = NULL;
+ PSecPkgInfoA pFinalPkgInfo = NULL;
+ ULONG cIndex;
+ ULONG cTempPackages;
+ ULONG cbFinalSize = 0;
+ PBYTE Where;
+
+ scRet = InitializePackages();
+ if (!NT_SUCCESS(scRet))
+ {
+ return(scRet);
+ }
+
+ //
+ // NOTE:
+ //
+ // Because the first two packages are actually the same one, we
+ // use the count of packages - 1 for enumerating. In addition,
+ // we start at 1 instead of zero for calling Enumerate, and subtact
+ // 1 for the index into the output buffers.
+ //
+
+ ppTempPkgInfo = (PSecPkgInfoA *) LocalAlloc(LMEM_ZEROINIT,
+ sizeof(PSecPkgInfoA) * (cPackages-1));
+ if (ppTempPkgInfo == NULL)
+ {
+ return(SEC_E_INSUFFICIENT_MEMORY);
+ }
+
+ //
+ // Loop through the packages and enumerate packages from each one
+ //
+
+ for ( cIndex = 1; cIndex < cPackages ; cIndex++ )
+ {
+ scRet = pspPackages[cIndex].pftTableA->QuerySecurityPackageInfoA(
+ pspPackages[cIndex].PackageNameA,
+ &ppTempPkgInfo[cIndex-1]);
+
+ //
+ // We require that each package return only 1 package
+ //
+
+ if (scRet != SEC_E_OK)
+ {
+ goto Cleanup;
+ }
+ cbFinalSize += sizeof(SecPkgInfoA)
+ + (lstrlenA(ppTempPkgInfo[cIndex-1]->Name) + 1) * sizeof(CHAR)
+ + (lstrlenA(ppTempPkgInfo[cIndex-1]->Comment) + 1) * sizeof(CHAR);
+ }
+
+ //
+ // Copy the data into one buffer, so it can be freed with LocalFree.
+ //
+
+ pFinalPkgInfo = (PSecPkgInfoA) LocalAlloc(0,cbFinalSize);
+ if (pFinalPkgInfo == NULL)
+ {
+ scRet = SEC_E_INSUFFICIENT_MEMORY;
+ goto Cleanup;
+ }
+
+
+
+ Where = (cPackages - 1) * sizeof(SecPkgInfoA) + (PBYTE) pFinalPkgInfo;
+
+ for (cIndex = 0; cIndex < cPackages - 1 ; cIndex++ )
+ {
+ //
+ // First copy the whole structure
+ //
+
+ CopyMemory( &pFinalPkgInfo[cIndex],
+ ppTempPkgInfo[cIndex],
+ sizeof(SecPkgInfo));
+
+ //
+ // Now marshall the strings
+ //
+
+ pFinalPkgInfo[cIndex].Name = (LPSTR) Where;
+
+ Where += (lstrlenA(ppTempPkgInfo[cIndex]->Name) + 1) * sizeof(CHAR);
+ CopyMemory( pFinalPkgInfo[cIndex].Name,
+ ppTempPkgInfo[cIndex]->Name,
+ Where - (PBYTE) pFinalPkgInfo[cIndex].Name );
+
+ pFinalPkgInfo[cIndex].Comment = (LPSTR) Where;
+ Where += (lstrlenA(ppTempPkgInfo[cIndex]->Comment) + 1) * sizeof(CHAR);
+ CopyMemory( pFinalPkgInfo[cIndex].Comment,
+ ppTempPkgInfo[cIndex]->Comment,
+ Where - (PBYTE) pFinalPkgInfo[cIndex].Comment );
+
+ }
+ ASSERT(Where == (PBYTE) pFinalPkgInfo + cbFinalSize);
+
+ scRet = SEC_E_OK;
+
+
+
+Cleanup:
+
+ //
+ // Clean up the temporary copy
+ //
+
+ if (ppTempPkgInfo != NULL)
+ {
+ for (cIndex = 0; cIndex < cPackages - 1 ; cIndex++ )
+ {
+ if (ppTempPkgInfo[cIndex] != NULL)
+ {
+ FreeContextBuffer(ppTempPkgInfo[cIndex]);
+ }
+ }
+ LocalFree(ppTempPkgInfo);
+ }
+
+ if (!NT_SUCCESS(scRet))
+ {
+ if (pFinalPkgInfo != NULL)
+ {
+ LocalFree(pFinalPkgInfo);
+ }
+ }
+ else
+ {
+ *ppPackageInfo = pFinalPkgInfo;
+ *pcPackages = cPackages - 1;
+ }
+
+ return(scRet);
+
+}
+
+
+
+//+-------------------------------------------------------------------------
+//
+// Function: QuerySecurityPackageInfo
+//
+// Synopsis:
+//
+// Effects:
+//
+// Arguments:
+//
+// Requires:
+//
+// Returns:
+//
+// Notes:
+//
+//
+//--------------------------------------------------------------------------
+
+
+SECURITY_STATUS SEC_ENTRY
+QuerySecurityPackageInfoA(
+ LPSTR pszPackageName, // Name of package
+ PSecPkgInfoA SEC_FAR * pPackageInfo // Receives package info
+ )
+{
+ SECURITY_STATUS scRet;
+ PSecPkg pPackage;
+
+
+ scRet = InitializePackages();
+ if (!NT_SUCCESS(scRet))
+ {
+ return(scRet);
+ }
+
+ pPackage = LocatePackageA(pszPackageName);
+ if (pPackage == NULL)
+ {
+ return(SEC_E_SECPKG_NOT_FOUND);
+ }
+
+
+ scRet = pPackage->pftTableA->QuerySecurityPackageInfoA(
+ pszPackageName,
+ pPackageInfo
+ );
+
+ return(scRet);
+
+}
+
+
diff --git a/private/net/svcdlls/ntlmssp/package/userstba.c b/private/net/svcdlls/ntlmssp/package/userstba.c
new file mode 100644
index 000000000..808dc28e1
--- /dev/null
+++ b/private/net/svcdlls/ntlmssp/package/userstba.c
@@ -0,0 +1,104 @@
+//+-----------------------------------------------------------------------
+//
+// Microsoft Windows
+//
+// Copyright (c) Microsoft Corporation 1992 - 1994
+//
+// File: userstub.cxx
+//
+// Contents: stubs for user-mode security APIs
+//
+//
+// History: 3-7-94 MikeSw Created
+//
+//------------------------------------------------------------------------
+
+#include "secdll.h"
+
+
+
+
+//+-------------------------------------------------------------------------
+//
+// Function: QueryContextAttributesA
+//
+// Synopsis:
+//
+// Effects:
+//
+// Arguments:
+//
+// Requires:
+//
+// Returns:
+//
+// Notes:
+//
+//
+//--------------------------------------------------------------------------
+
+
+
+SECURITY_STATUS SEC_ENTRY
+QueryContextAttributesA(
+ PCtxtHandle phContext, // Context to query
+ unsigned long ulAttribute, // Attribute to query
+ void SEC_FAR * pBuffer // Buffer for attributes
+ )
+{
+ CtxtHandle TempCtxtHandle;
+
+
+ TempCtxtHandle.dwUpper = phContext->dwUpper;
+ TempCtxtHandle.dwLower = pspPackages[phContext->dwLower].dwOriginalPackageID;
+ return( pspPackages[phContext->dwLower].pftTableA->QueryContextAttributesA(
+ &TempCtxtHandle,
+ ulAttribute,
+ pBuffer ) );
+}
+
+
+//+-------------------------------------------------------------------------
+//
+// Function: QueryCredentialsAttributesA
+//
+// Synopsis:
+//
+// Effects:
+//
+// Arguments:
+//
+// Requires:
+//
+// Returns:
+//
+// Notes:
+//
+//
+//--------------------------------------------------------------------------
+
+
+
+SECURITY_STATUS SEC_ENTRY
+QueryCredentialsAttributesA(
+ PCredHandle phCredentials, // Credentials to query
+ unsigned long ulAttribute, // Attribute to query
+ void SEC_FAR * pBuffer // Buffer for attributes
+ )
+{
+
+ return( SEC_E_UNSUPPORTED_FUNCTION );
+#if 0
+
+ return( pspPackages[phCredentials->dwLower].pftTableA->QueryCredentialsAttributesA(
+ phCredentials,
+ ulAttribute,
+ pBuffer ) );
+#endif
+ UNREFERENCED_PARAMETER(phCredentials);
+ UNREFERENCED_PARAMETER(ulAttribute);
+ UNREFERENCED_PARAMETER(pBuffer);
+
+}
+
+
diff --git a/private/net/svcdlls/ntlmssp/package/userstub.c b/private/net/svcdlls/ntlmssp/package/userstub.c
new file mode 100644
index 000000000..251d8667d
--- /dev/null
+++ b/private/net/svcdlls/ntlmssp/package/userstub.c
@@ -0,0 +1,464 @@
+//+-----------------------------------------------------------------------
+//
+// Microsoft Windows
+//
+// Copyright (c) Microsoft Corporation 1992 - 1994
+//
+// File: userstub.cxx
+//
+// Contents: stubs for user-mode security APIs
+//
+//
+// History: 3-7-94 MikeSw Created
+//
+//------------------------------------------------------------------------
+
+#include "secdll.h"
+
+
+
+
+//+-------------------------------------------------------------------------
+//
+// Function: CompleteAuthToken
+//
+// Synopsis:
+//
+// Effects:
+//
+// Arguments:
+//
+// Requires:
+//
+// Returns:
+//
+// Notes:
+//
+//
+//--------------------------------------------------------------------------
+
+
+
+SECURITY_STATUS SEC_ENTRY
+CompleteAuthToken(
+ PCtxtHandle phContext, // Context to complete
+ PSecBufferDesc pToken // Token to complete
+ )
+{
+ SECURITY_STATUS scRet;
+ CtxtHandle TempCtxtHandle;
+
+
+ TempCtxtHandle.dwUpper = phContext->dwUpper;
+ TempCtxtHandle.dwLower = pspPackages[phContext->dwLower].dwOriginalPackageID;
+
+ scRet = pspPackages[phContext->dwLower].pftTable->CompleteAuthToken(
+ &TempCtxtHandle,
+ pToken);
+ return(scRet);
+}
+
+
+
+//+-------------------------------------------------------------------------
+//
+// Function: ImpersonateSecurityContext
+//
+// Synopsis:
+//
+// Effects:
+//
+// Arguments:
+//
+// Requires:
+//
+// Returns:
+//
+// Notes:
+//
+//
+//--------------------------------------------------------------------------
+
+
+SECURITY_STATUS SEC_ENTRY
+ImpersonateSecurityContext(
+ PCtxtHandle phContext // Context to impersonate
+ )
+{
+
+ SECURITY_STATUS scRet;
+ CtxtHandle TempCtxtHandle;
+
+
+ TempCtxtHandle.dwUpper = phContext->dwUpper;
+ TempCtxtHandle.dwLower = pspPackages[phContext->dwLower].dwOriginalPackageID;
+
+ scRet = pspPackages[phContext->dwLower].pftTable->ImpersonateSecurityContext(
+ &TempCtxtHandle);
+ return(scRet);
+}
+
+
+
+//+-------------------------------------------------------------------------
+//
+// Function: ImpersonateSecurityContext
+//
+// Synopsis:
+//
+// Effects:
+//
+// Arguments:
+//
+// Requires:
+//
+// Returns:
+//
+// Notes:
+//
+//
+//--------------------------------------------------------------------------
+
+
+SECURITY_STATUS SEC_ENTRY
+QuerySecurityContextToken(
+ PCtxtHandle phContext,
+ PHANDLE TokenHandle
+
+ )
+{
+
+ SECURITY_STATUS scRet;
+ CtxtHandle TempCtxtHandle;
+
+
+ TempCtxtHandle.dwUpper = phContext->dwUpper;
+ TempCtxtHandle.dwLower = pspPackages[phContext->dwLower].dwOriginalPackageID;
+
+ if (pspPackages[phContext->dwLower].pftTable->QuerySecurityContextToken != NULL)
+ {
+ scRet = pspPackages[phContext->dwLower].pftTable->QuerySecurityContextToken(
+ &TempCtxtHandle,
+ TokenHandle);
+ } else
+ {
+ scRet = SEC_E_UNSUPPORTED_FUNCTION;
+ }
+ return(scRet);
+}
+
+
+
+//+-------------------------------------------------------------------------
+//
+// Function: RevertSecurityContext
+//
+// Synopsis:
+//
+// Effects:
+//
+// Arguments:
+//
+// Requires:
+//
+// Returns:
+//
+// Notes:
+//
+//
+//--------------------------------------------------------------------------
+
+
+SECURITY_STATUS SEC_ENTRY
+RevertSecurityContext(
+ PCtxtHandle phContext // Context from which to re
+ )
+{
+
+ SECURITY_STATUS scRet;
+ CtxtHandle TempCtxtHandle;
+
+
+ TempCtxtHandle.dwUpper = phContext->dwUpper;
+ TempCtxtHandle.dwLower = pspPackages[phContext->dwLower].dwOriginalPackageID;
+
+ scRet = pspPackages[phContext->dwLower].pftTable->RevertSecurityContext(
+ &TempCtxtHandle);
+ return(scRet);
+
+}
+
+
+//+-------------------------------------------------------------------------
+//
+// Function: QueryContextAttributes
+//
+// Synopsis:
+//
+// Effects:
+//
+// Arguments:
+//
+// Requires:
+//
+// Returns:
+//
+// Notes:
+//
+//
+//--------------------------------------------------------------------------
+
+
+
+SECURITY_STATUS SEC_ENTRY
+QueryContextAttributesW(
+ PCtxtHandle phContext, // Context to query
+ unsigned long ulAttribute, // Attribute to query
+ void SEC_FAR * pBuffer // Buffer for attributes
+ )
+{
+ CtxtHandle TempCtxtHandle;
+
+
+ TempCtxtHandle.dwUpper = phContext->dwUpper;
+ TempCtxtHandle.dwLower = pspPackages[phContext->dwLower].dwOriginalPackageID;
+
+ return( pspPackages[phContext->dwLower].pftTableW->QueryContextAttributesW(
+ &TempCtxtHandle,
+ ulAttribute,
+ pBuffer ) );
+}
+
+
+
+//+-------------------------------------------------------------------------
+//
+// Function: QueryCredentialsAttributes
+//
+// Synopsis:
+//
+// Effects:
+//
+// Arguments:
+//
+// Requires:
+//
+// Returns:
+//
+// Notes:
+//
+//
+//--------------------------------------------------------------------------
+
+
+
+SECURITY_STATUS SEC_ENTRY
+QueryCredentialsAttributesW(
+ PCredHandle phCredentials, // Credentials to query
+ unsigned long ulAttribute, // Attribute to query
+ void SEC_FAR * pBuffer // Buffer for attributes
+ )
+{
+ return( SEC_E_UNSUPPORTED_FUNCTION );
+#if 0
+ return (pspPackages[phCredentials->dwLower].pftTableW->QueryCredentialsAttributesW(
+ phCredentials,
+ ulAttribute,
+ pBuffer ) );
+#endif
+
+ UNREFERENCED_PARAMETER(phCredentials);
+ UNREFERENCED_PARAMETER(ulAttribute);
+ UNREFERENCED_PARAMETER(pBuffer);
+}
+
+
+//+-------------------------------------------------------------------------
+//
+// Function: MakeSignature
+//
+// Synopsis:
+//
+// Effects:
+//
+// Arguments: [phContext] -- context to use
+// [fQOP] -- quality of protection to use
+// [pMessage] -- message
+// [MessageSeqNo] -- sequence number of message
+//
+// Requires:
+//
+// Returns:
+//
+// Notes:
+//
+//--------------------------------------------------------------------------
+SECURITY_STATUS SEC_ENTRY
+MakeSignature( PCtxtHandle phContext,
+ ULONG fQOP,
+ PSecBufferDesc pMessage,
+ ULONG MessageSeqNo)
+{
+ SECURITY_STATUS scRet;
+ CtxtHandle TempCtxtHandle;
+
+
+ TempCtxtHandle.dwUpper = phContext->dwUpper;
+ TempCtxtHandle.dwLower = pspPackages[phContext->dwLower].dwOriginalPackageID;
+
+
+ // Verify parameters:
+
+ scRet = pspPackages[phContext->dwLower].pftTable->MakeSignature(
+ &TempCtxtHandle,
+ fQOP,
+ pMessage,
+ MessageSeqNo );
+
+
+ return(scRet);
+}
+
+
+
+//+-------------------------------------------------------------------------
+//
+// Function: VerifySignature
+//
+// Synopsis:
+//
+// Effects:
+//
+// Arguments: [phContext] -- Context performing the unseal
+// [pMessage] -- Message to verify
+// [MessageSeqNo] -- Sequence number of this message
+// [pfQOPUsed] -- quality of protection used
+//
+// Requires:
+//
+// Returns:
+//
+// Notes:
+//
+//--------------------------------------------------------------------------
+SECURITY_STATUS SEC_ENTRY
+VerifySignature(PCtxtHandle phContext,
+ PSecBufferDesc pMessage,
+ ULONG MessageSeqNo,
+ ULONG * pfQOP)
+{
+ SECURITY_STATUS scRet;
+ CtxtHandle TempCtxtHandle;
+
+
+ TempCtxtHandle.dwUpper = phContext->dwUpper;
+ TempCtxtHandle.dwLower = pspPackages[phContext->dwLower].dwOriginalPackageID;
+
+
+ scRet = pspPackages[phContext->dwLower].pftTable->VerifySignature(
+ &TempCtxtHandle,
+ pMessage,
+ MessageSeqNo,
+ pfQOP );
+
+ return(scRet);
+
+
+}
+
+//+---------------------------------------------------------------------------
+//
+// Function: SealMessage
+//
+// Synopsis: Seals a message
+//
+// Effects:
+//
+// Arguments: [phContext] -- context to use
+// [fQOP] -- quality of protection to use
+// [pMessage] -- message
+// [MessageSeqNo] -- sequence number of message
+//
+// History: 5-06-93 RichardW Created
+//
+// Notes:
+//
+//----------------------------------------------------------------------------
+
+
+SECURITY_STATUS SEC_ENTRY
+SealMessage( PCtxtHandle phContext,
+ ULONG fQOP,
+ PSecBufferDesc pMessage,
+ ULONG MessageSeqNo)
+{
+
+ SECURITY_STATUS scRet;
+ SEAL_MESSAGE_FN SealMessageFunc;
+ CtxtHandle TempCtxtHandle;
+
+
+ TempCtxtHandle.dwUpper = phContext->dwUpper;
+ TempCtxtHandle.dwLower = pspPackages[phContext->dwLower].dwOriginalPackageID;
+
+ SealMessageFunc = (SEAL_MESSAGE_FN) pspPackages[phContext->dwLower].pftTable->Reserved3;
+
+ scRet = (*SealMessageFunc)( &TempCtxtHandle,
+ fQOP,
+ pMessage,
+ MessageSeqNo );
+
+ return(scRet);
+
+}
+
+//+---------------------------------------------------------------------------
+//
+// Function: UnsealMessage
+//
+// Synopsis: Unseal a private message
+//
+// Arguments: [phContext] -- Context performing the unseal
+// [pMessage] -- Message to unseal
+// [MessageSeqNo] -- Sequence number of this message
+// [pfQOPUsed] -- quality of protection used
+//
+// History: 5-06-93 RichardW Created
+//
+// Notes:
+//
+//----------------------------------------------------------------------------
+
+
+SECURITY_STATUS SEC_ENTRY
+UnsealMessage( PCtxtHandle phContext,
+ PSecBufferDesc pMessage,
+ ULONG MessageSeqNo,
+ ULONG * pfQOP)
+{
+
+ SECURITY_STATUS scRet;
+ UNSEAL_MESSAGE_FN UnsealMessageFunc;
+ CtxtHandle TempCtxtHandle;
+
+
+ TempCtxtHandle.dwUpper = phContext->dwUpper;
+ TempCtxtHandle.dwLower = pspPackages[phContext->dwLower].dwOriginalPackageID;
+
+ UnsealMessageFunc = (UNSEAL_MESSAGE_FN) pspPackages[phContext->dwLower].pftTable->Reserved4;
+
+
+ scRet = (*UnsealMessageFunc)( &TempCtxtHandle,
+ pMessage,
+ MessageSeqNo,
+ pfQOP );
+
+ return(scRet);
+
+
+}
+
+
+
+
+
diff --git a/private/net/svcdlls/ntlmssp/server/api.c b/private/net/svcdlls/ntlmssp/server/api.c
new file mode 100644
index 000000000..c58706dde
--- /dev/null
+++ b/private/net/svcdlls/ntlmssp/server/api.c
@@ -0,0 +1,859 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ api.c
+
+Abstract:
+
+ NtLmSsp Service API dispatch routines.
+
+Author:
+
+ Cliff Van Dyke (CliffV) 26-Jun-1993
+
+Revision History:
+
+--*/
+
+
+//
+// Common include files.
+//
+
+#include <ntlmssps.h> // Include files common to server side of service
+
+//
+// Include files specific to this .c file
+//
+
+SECURITY_STATUS
+SspApiAcquireCredentialHandle(
+ IN PSSP_CLIENT_CONNECTION ClientConnection,
+ IN OUT PSSP_API_MESSAGE Message
+ )
+
+/*++
+
+Routine Description:
+
+
+ This API allows applications to acquire a handle to pre-existing
+ credentials associated with the user on whose behalf the call is made
+ i.e. under the identity this application is running. These pre-existing
+ credentials have been established through a system logon not described
+ here. Note that this is different from "login to the network" and does
+ not imply gathering of credentials.
+
+
+ This API returns a handle to the credentials of a principal (user, client)
+ as used by a specific security package. This handle can then be used
+ in subsequent calls to the Context APIs. This API will not let a
+ process obtain a handle to credentials that are not related to the
+ process; i.e. we won't allow a process to grab the credentials of
+ another user logged into the same machine. There is no way for us
+ to determine if a process is a trojan horse or not, if it is executed
+ by the user.
+
+Arguments:
+
+ ClientConnection - Describes the client process.
+
+ Message - Message from the caller. Returns the response to be passed to
+ the caller. The message contains all of the following fields:
+
+
+ PrincipalName - Name of the principal for whose credentials the handle
+ will reference. Note, if the process requesting the handle does
+ not have access to the credentials, an error will be returned.
+ A null string indicates that the process wants a handle to the
+ credentials of the user under whose security it is executing.
+
+ PackageName - Name of the package with which these credentials will
+ be used.
+
+ CredentialUseFlags - Flags indicating the way with which these
+ credentials will be used.
+
+ #define CRED_INBOUND 0x00000001
+ #define CRED_OUTBOUND 0x00000002
+ #define CRED_BOTH 0x00000003
+
+ The credentials created with CRED_INBOUND option can only be used
+ for (validating incoming calls and can not be used for making accesses.
+
+ LogonId - Pointer to NT style Logon Id which is a LUID. (Provided for
+ file system ; processes such as network redirectors.)
+
+ CredentialHandle - Returned credential handle.
+
+ Lifetime - Time that these credentials expire. The value returned in
+ this field depends on the security package.
+
+Return Value:
+
+ STATUS_SUCCESS -- Call completed successfully
+
+ SEC_E_INSUFFICIENT_MEMORY -- Not enough memory
+ SEC_E_PRINCIPAL_UNKNOWN -- No such principal
+ SEC_E_NOT_OWNER -- caller does not own the specified credentials
+
+--*/
+
+{
+ SECURITY_STATUS SecStatus;
+ PSSP_ACQUIRE_CREDENTIAL_HANDLE_ARGS Args;
+ HANDLE ClientTokenHandle = NULL;
+ LUID LogonId;
+
+ //
+ // Simply call the worker routine.
+ //
+
+ Args = &Message->Arguments.AcquireCredentialHandleArgs;
+
+
+ SecStatus = SspLpcGetLogonId(
+ ClientConnection,
+ Message,
+ &LogonId,
+ &ClientTokenHandle
+ );
+
+ if ( !NT_SUCCESS(SecStatus) ) {
+ SspPrint(( SSP_API,
+ "SspApiAcquireCredentialHandle: "
+ "GetLogonId returns 0x%lx\n",
+ SecStatus ));
+ } else {
+
+ SecStatus = SsprAcquireCredentialHandle(
+ ClientConnection,
+ &ClientTokenHandle,
+ &LogonId,
+ Args->CredentialUseFlags,
+ &Args->CredentialHandle,
+ &Args->Lifetime,
+ Args->DomainName,
+ Args->DomainNameSize,
+ Args->UserName,
+ Args->UserNameSize,
+ Args->Password,
+ Args->PasswordSize );
+ }
+
+ if ( ClientTokenHandle != NULL) {
+ NtClose(ClientTokenHandle);
+ }
+ return SecStatus;
+
+}
+
+SECURITY_STATUS
+SspApiFreeCredentialHandle(
+ IN PSSP_CLIENT_CONNECTION ClientConnection,
+ IN OUT PSSP_API_MESSAGE Message
+ )
+
+/*++
+
+
+ This API is used to notify the security system that the credentials are
+ no longer needed and allows the application to free the handle acquired
+ in the call described above. When all references to this credential
+ set has been removed then the credentials may themselves be removed.
+
+Arguments:
+
+ ClientConnection - Describes the client process.
+
+ Message - Message from the caller. Returns the response to be passed to
+ the caller. The message contains all of the following fields:
+
+ CredentialHandle - Credential Handle obtained through
+ AcquireCredentialHandle.
+
+Return Value:
+
+ STATUS_SUCCESS -- Call completed successfully
+
+ SEC_E_INVALID_HANDLE -- Credential Handle is invalid
+
+--*/
+
+{
+ SECURITY_STATUS SecStatus;
+ PSSP_FREE_CREDENTIAL_HANDLE_ARGS Args;
+
+ //
+ // Simply call the worker routine.
+ //
+
+ Args = &Message->Arguments.FreeCredentialHandleArgs;
+
+
+ SecStatus = SsprFreeCredentialHandle(
+ ClientConnection,
+ &Args->CredentialHandle );
+
+ return SecStatus;
+}
+
+
+SECURITY_STATUS
+SspApiInitializeSecurityContext(
+ IN PSSP_CLIENT_CONNECTION ClientConnection,
+ IN OUT PSSP_API_MESSAGE Message
+ )
+
+/*++
+
+Routine Description:
+
+ This routine initiates the outbound security context from a credential
+ handle. This results in the establishment of a security context
+ between the application and a remote peer. The routine returns a token
+ which must be passed to the remote peer which in turn submits it to the
+ local security implementation via the AcceptSecurityContext() call.
+ The token generated should be considered opaque by all callers.
+
+ This function is used by a client to initialize an outbound context.
+ For a two leg security package, the calling sequence is as follows: The
+ client calls the function with OldContextHandle set to NULL and
+ InputToken set either to NULL or to a pointer to a security package
+ specific data structure. The package returns a context handle in
+ NewContextHandle and a token in OutputToken. The handle can then be
+ used for message APIs if desired.
+
+ The OutputToken returned here is sent across to target server which
+ calls AcceptSecuirtyContext() with this token as an input argument and
+ may receive a token which is returned to the initiator so it can call
+ InitializeSecurityContext() again.
+
+ For a three leg (mutual authentication) security package, the calling
+ sequence is as follows: The client calls the function as above, but the
+ package will return SEC_I_CALLBACK_NEEDED. The client then sends the
+ output token to the server and waits for the server's reply. Upon
+ receipt of the server's response, the client calls this function again,
+ with OldContextHandle set to the handle that was returned from the
+ first call. The token received from the server is supplied in the
+ InputToken parameter. If the server has successfully responded, then
+ the package will respond with success, or it will invalidate the
+ context.
+
+ Initialization of security context may require more than one call to
+ this function depending upon the underlying authentication mechanism as
+ well as the "choices" indicated via ContextReqFlags. The
+ ContextReqFlags and ContextAttributes are bit masks representing
+ various context level functions viz. delegation, mutual
+ authentication, confidentiality, replay detection and sequence
+ detection.
+
+ When ISC_REQ_PROMPT_FOR_CREDS flag is set the security package always
+ prompts the user for credentials, irrespective of whether credentials
+ are present or not. If user indicated that the supplied credentials be
+ used then they will be stashed (overwriting existing ones if any) for
+ future use. The security packages will always prompt for credentials
+ if none existed, this optimizes for the most common case before a
+ credentials database is built. But the security packages can be
+ configured to not do that. Security packages will ensure that they
+ only prompt to the interactive user, for other logon sessions, this
+ flag is ignored.
+
+ When ISC_REQ_USE_SUPPLIED_CREDS flag is set the security package always
+ uses the credentials supplied in the InitializeSecurityContext() call
+ via InputToken parameter. If the package does not have any credentials
+ available it will prompt for them and record it as indicated above.
+
+ It is an error to set both these flags simultaneously.
+
+ If the ISC_REQ_ALLOCATE_MEMORY was specified then the caller must free
+ the memory pointed to by OutputToken by calling FreeContextBuffer().
+
+ For example, the InputToken may be the challenge from a LAN Manager or
+ NT file server. In this case, the OutputToken would be the NTLM
+ encrypted response to the challenge. The caller of this API can then
+ take the appropriate response (case-sensitive v. case-insensitive) and
+ return it to the server for an authenticated connection.
+
+
+
+Arguments:
+
+ ClientConnection - Describes the client process.
+
+ Message - Message from the caller. Returns the response to be passed to
+ the caller. The message contains all of the following fields:
+
+ CredentialHandle - Handle to the credentials to be used to
+ create the context.
+
+ ContextHandle - On input, the handle to the partially formed context, if this is
+ a second call (see above) or NULL if this is the first call.
+
+ On output, new context handle. If this is a second call, this
+ can be the same as phContext.
+
+ ContextReqFlags - Requirements of the context, package specific.
+
+ #define ISC_REQ_DELEGATE 0x00000001
+ #define ISC_REQ_MUTUAL_AUTH 0x00000002
+ #define ISC_REQ_REPLAY_DETECT 0x00000004
+ #define ISC_REQ_SEQUENCE_DETECT 0x00000008
+ #define ISC_REQ_CONFIDENTIALITY 0x00000010
+ #define ISC_REQ_USE_SESSION_KEY 0x00000020
+ #define ISC_REQ_PROMT_FOR__CREDS 0x00000040
+ #define ISC_REQ_USE_SUPPLIED_CREDS 0x00000080
+ #define ISC_REQ_ALLOCATE_MEMORY 0x00000100
+ #define ISC_REQ_USE_DCE_STYLE 0x00000200
+
+ InputTokenSize - Size of the input token, in bytes.
+
+ InputToken - Pointer to the input token. In the first call this
+ token can either be NULL or may contain security package specific
+ information.
+
+ OutputTokenSize - Size of the output token, in bytes.
+
+ OutputToken - Buffer to receive the output token.
+
+ ContextAttributes -Attributes of the context established.
+
+ #define ISC_RET_DELEGATE 0x00000001
+ #define ISC_RET_MUTUAL_AUTH 0x00000002
+ #define ISC_RET_REPLAY_DETECT 0x00000004
+ #define ISC_RET_SEQUENCE_DETECT 0x00000008
+ #define ISC_REP_CONFIDENTIALITY 0x00000010
+ #define ISC_REP_USE_SESSION_KEY 0x00000020
+ #define ISC_REP_USED_COLLECTED_CREDS 0x00000040
+ #define ISC_REP_USED_SUPPLIED_CREDS 0x00000080
+ #define ISC_REP_ALLOCATED_MEMORY 0x00000100
+ #define ISC_REP_USED_DCE_STYLE 0x00000200
+
+ ExpirationTime - Expiration time of the context.
+
+ SessionKey - Session key to be used for this context.
+
+ NegotiateFlags - Flags negotiated for this context.
+
+Return Value:
+
+ STATUS_SUCCESS - Message handled
+ SEC_I_CALLBACK_NEEDED -- Caller should call again later
+
+ SEC_E_INVALID_TOKEN -- Token improperly formatted
+ SEC_E_INVALID_HANDLE -- Credential/Context Handle is invalid
+ SEC_E_BUFFER_TOO_SMALL -- Buffer for output token isn't big enough
+ SEC_E_NO_CREDENTIALS -- There are no credentials for this client
+ SEC_E_INSUFFICIENT_MEMORY -- Not enough memory
+
+--*/
+
+{
+ SECURITY_STATUS SecStatus = SEC_E_OK;
+ PSSP_INITIALIZE_SECURITY_CONTEXT_ARGS Args;
+
+ //
+ // Simply call the worker routine.
+ //
+
+ SspPrint(( SSP_API, "SspApiInitializeSecurityContext Entered\n" ));
+ Args = &Message->Arguments.InitializeSecurityContextArgs;
+
+
+ //
+ // Handle the call differently depending on whether this is the first
+ // or second call.
+ //
+
+ if ( Args->ContextHandle.dwUpper == 0 && Args->ContextHandle.dwLower == 0 ){
+ SecStatus = SsprHandleFirstCall(
+ ClientConnection,
+ &Args->CredentialHandle,
+ &Args->ContextHandle,
+ Args->ContextReqFlags,
+ Args->InputTokenSize,
+ Args->InputToken,
+ &Args->OutputTokenSize,
+ Args->OutputToken,
+ &Args->ContextAttributes,
+ &Args->ExpirationTime,
+ Args->SessionKey,
+ &Args->NegotiateFlags );
+ } else {
+
+ if (Args->ClientTokenHandle != NULL) {
+ SecStatus = SspLpcDuplicateHandle(
+ ClientConnection,
+ TRUE, // from client
+ FALSE, // don't close source
+ Args->ClientTokenHandle, // input handle
+ &Args->ClientTokenHandle // output handle
+ );
+
+ }
+ if (NT_SUCCESS(SecStatus)) {
+
+ SecStatus = SsprHandleChallengeMessage(
+ ClientConnection,
+ &Args->CredentialHandle,
+ &Args->ContextHandle,
+ Args->ClientTokenHandle,
+ &Args->LogonId,
+ Args->ContextReqFlags,
+ Args->DomainName,
+ Args->DomainNameSize,
+ Args->UserName,
+ Args->UserNameSize,
+ Args->Password,
+ Args->PasswordSize,
+ Args->InputTokenSize,
+ Args->InputToken,
+ &Args->OutputTokenSize,
+ Args->OutputToken,
+ &Args->ContextAttributes,
+ &Args->ExpirationTime,
+ Args->SessionKey,
+ &Args->NegotiateFlags,
+ Args->ContextNames );
+ }
+
+ if (Args->ClientTokenHandle != NULL) {
+ (VOID) NtClose( Args->ClientTokenHandle );
+ }
+
+ }
+
+ SspPrint(( SSP_API, "SspApiInitializeSecurityContext returns 0x%lx\n", SecStatus ));
+ return SecStatus;
+}
+
+
+SECURITY_STATUS
+SspApiAcceptSecurityContext(
+ IN PSSP_CLIENT_CONNECTION ClientConnection,
+ IN OUT PSSP_API_MESSAGE Message
+ )
+
+/*++
+
+Routine Description:
+
+ Allows a remotely initiated security context between the application
+ and a remote peer to be established. To complete the establishment of
+ context one or more reply tokens may be required from remote peer.
+
+ This function is the server counterpart to the
+ InitializeSecurityContext API. The ContextAttributes is a bit mask
+ representing various context level functions viz. delegation, mutual
+ authentication, confidentiality, replay detection and sequence
+ detection. This API is used by the server side. When a request comes
+ in, the server uses the ContextReqFlags parameter to specify what
+ it requires of the session. In this fashion, a server can specify that
+ clients must be capable of using a confidential or integrity checked
+ session, and fail clients that can't meet that demand. Alternatively,
+ a server can require nothing, and whatever the client can provide or
+ requires is returned in the pfContextAttributes parameter. For a
+ package that supports 3 leg mutual authentication, the calling sequence
+ would be: Client provides a token, server calls Accept the first time,
+ generating a reply token. The client uses this in a second call to
+ InitializeSecurityContext, and generates a final token. This token is
+ then used in the final call to Accept to complete the session. Another
+ example would be the LAN Manager/NT authentication style. The client
+ connects to negotiate a protocol. The server calls Accept to set up a
+ context and generate a challenge to the client. The client calls
+ InitializeSecurityContext and creates a response. The server then
+ calls Accept the final time to allow the package to verify the response
+ is appropriate for the challenge.
+
+Arguments:
+
+ ClientConnection - Describes the client process.
+
+ Message - Message from the caller. Returns the response to be passed to
+ the caller. The message contains all of the following fields:
+
+ CredentialHandle - Handle to the credentials to be used to
+ create the context.
+
+
+ ContextHandle - On input, the handle to the partially formed context, if this is
+ a second call (see above) or NULL if this is the first call.
+
+ On output, new context handle. If this is a second call, this
+ can be the same as phContext.
+
+ InputTokenSize - Size of the input token, in bytes.
+
+ InputToken - Pointer to the input token. In the first call this
+ token can either be NULL or may contain security package specific
+ information.
+
+ ContextReqFlags - Requirements of the context, package specific.
+
+ #define ASC_REQ_DELEGATE 0x00000001
+ #define ASC_REQ_MUTUAL_AUTH 0x00000002
+ #define ASC_REQ_REPLAY_DETECT 0x00000004
+ #define ASC_REQ_SEQUENCE_DETECT 0x00000008
+ #define ASC_REQ_CONFIDENTIALITY 0x00000010
+ #define ASC_REQ_USE_SESSION_KEY 0x00000020
+ #define ASC_REQ_ALLOCATE_MEMORY 0x00000100
+ #define ASC_REQ_USE_DCE_STYLE 0x00000200
+
+ OutputTokenSize - Size of the output token, in bytes.
+
+ OutputToken - Buffer to receive the output token.
+
+ ContextAttributes -Attributes of the context established.
+
+ #define ASC_RET_DELEGATE 0x00000001
+ #define ASC_RET_MUTUAL_AUTH 0x00000002
+ #define ASC_RET_REPLAY_DETECT 0x00000004
+ #define ASC_RET_SEQUENCE_DETECT 0x00000008
+ #define ASC_RET_CONFIDENTIALITY 0x00000010
+ #define ASC_RET_USE_SESSION_KEY 0x00000020
+ #define ASC_RET_ALLOCATED_BUFFERS 0x00000100
+ #define ASC_RET_USED_DCE_STYLE 0x00000200
+
+ ExpirationTime - Expiration time of the context.
+
+ SessionKey - Session key to be used for this context.
+
+ NegotiateFlags - Flags negotiated for this context.
+
+Return Value:
+
+ STATUS_SUCCESS - Message handled
+ SEC_I_CALLBACK_NEEDED -- Caller should call again later
+
+ SEC_E_INVALID_TOKEN -- Token improperly formatted
+ SEC_E_INVALID_HANDLE -- Credential/Context Handle is invalid
+ SEC_E_BUFFER_TOO_SMALL -- Buffer for output token isn't big enough
+ SEC_E_LOGON_DENIED -- User is no allowed to logon to this server
+ SEC_E_INSUFFICIENT_MEMORY -- Not enough memory
+
+--*/
+
+{
+ SECURITY_STATUS SecStatus;
+ PSSP_ACCEPT_SECURITY_CONTEXT_ARGS Args;
+
+ //
+ // Simply call the worker routine.
+ //
+
+ SspPrint(( SSP_API, "SspApiAcceptSecurityContext Entered\n" ));
+ Args = &Message->Arguments.AcceptSecurityContextArgs;
+
+
+ //
+ // Handle the call differently depending on whether this is the first
+ // or second call.
+ //
+
+ if ( Args->ContextHandle.dwUpper == 0 && Args->ContextHandle.dwLower == 0 ){
+ SecStatus = SsprHandleNegotiateMessage(
+ ClientConnection,
+ &Args->CredentialHandle,
+ &Args->ContextHandle,
+ Args->ContextReqFlags,
+ Args->InputTokenSize,
+ Args->InputToken,
+ &Args->OutputTokenSize,
+ Args->OutputToken,
+ &Args->ContextAttributes,
+ &Args->ExpirationTime );
+ } else {
+ SecStatus = SsprHandleAuthenticateMessage(
+ ClientConnection,
+ &Args->CredentialHandle,
+ &Args->ContextHandle,
+ Args->ContextReqFlags,
+ Args->InputTokenSize,
+ Args->InputToken,
+ &Args->OutputTokenSize,
+ Args->OutputToken,
+ &Args->ContextAttributes,
+ &Args->ExpirationTime,
+ Args->SessionKey,
+ &Args->NegotiateFlags,
+ &Args->TokenHandle,
+ &Args->SubStatus,
+ Args->ContextNames,
+ &Args->PasswordExpiry );
+
+ //
+ // If that succeeded and we have a token handle, duplicate
+ // it into the client process.
+ //
+
+ if ((SecStatus == S_OK) && (Args->TokenHandle != NULL)) {
+ SecStatus = SspLpcDuplicateHandle(
+ ClientConnection,
+ FALSE, // not from client
+ TRUE, // close source
+ Args->TokenHandle, // input handle
+ &Args->TokenHandle // output handle
+ );
+
+
+ }
+ }
+
+ SspPrint(( SSP_API, "SspApiAcceptSecurityContext returns 0x%lx\n", SecStatus ));
+ return SecStatus;
+}
+
+
+
+SECURITY_STATUS
+SspApiQueryContextAttributes(
+ IN PSSP_CLIENT_CONNECTION ClientConnection,
+ IN OUT PSSP_API_MESSAGE Message
+ )
+
+/*++
+
+Routine Description:
+
+ This API allows a customer of the security services to determine
+ certain attributes of the context. These are: sizes, names, and
+ lifespan.
+
+Arguments:
+
+ ClientConnection - Describes the client process.
+
+ Message - Message from the caller. Returns the response to be passed to
+ the caller. The message contains all of the following fields:
+
+ ContextHandle - Handle to the context to query.
+
+ Attribute - Attribute to query.
+
+ #define SECPKG_ATTR_SIZES 0
+ #define SECPKG_ATTR_NAMES 1
+ #define SECPKG_ATTR_LIFESPAN 2
+
+ Buffer - Buffer to copy the data into. The buffer must be large enough
+ to fit the queried attribute.
+
+Return Value:
+
+ STATUS_SUCCESS - Call completed successfully
+
+ SEC_E_INVALID_HANDLE -- Credential/Context Handle is invalid
+ SEC_E_UNSUPPORTED_FUNCTION -- Function code is not supported
+
+--*/
+
+{
+ SECURITY_STATUS SecStatus;
+ PSSP_QUERY_CONTEXT_ATTRIBUTES_ARGS Args;
+
+ //
+ // Simply call the worker routine.
+ //
+
+ Args = &Message->Arguments.QueryContextAttributesArgs;
+
+
+ SecStatus = SsprQueryContextAttributes(
+ ClientConnection,
+ &Args->ContextHandle,
+ Args->Attribute,
+ Args->Buffer );
+
+ return SecStatus;
+
+}
+
+
+
+SECURITY_STATUS
+SspApiDeleteSecurityContext(
+ IN PSSP_CLIENT_CONNECTION ClientConnection,
+ IN OUT PSSP_API_MESSAGE Message
+ )
+
+/*++
+
+Routine Description:
+
+ Deletes the local data structures associated with the specified
+ security context and generates a token which is passed to a remote peer
+ so it too can remove the corresponding security context.
+
+ This API terminates a context on the local machine, and optionally
+ provides a token to be sent to the other machine. The OutputToken
+ generated by this call is to be sent to the remote peer (initiator or
+ acceptor). If the context was created with the I _REQ_ALLOCATE_MEMORY
+ flag, then the package will allocate a buffer for the output token.
+ Otherwise, it is the responsibility of the caller.
+
+Arguments:
+
+ ContextHandle - Handle to the context to delete
+
+ TokenLength - Size of the output token (if any) that should be sent to
+ the process at the other end of the session.
+
+ Token - Pointer to the token to send.
+
+Return Value:
+
+ STATUS_SUCCESS - Call completed successfully
+
+ SEC_E_NO_SPM -- Security Support Provider is not running
+ SEC_E_INVALID_HANDLE -- Credential/Context Handle is invalid
+
+--*/
+
+{
+ SECURITY_STATUS SecStatus;
+ PSSP_DELETE_SECURITY_CONTEXT_ARGS Args;
+
+ //
+ // Simply call the worker routine.
+ //
+
+ Args = &Message->Arguments.DeleteSecurityContextArgs;
+
+
+ SecStatus = SsprDeleteSecurityContext(
+ ClientConnection,
+ &Args->ContextHandle );
+
+ return SecStatus;
+
+}
+
+
+SECURITY_STATUS
+SspApiNoop(
+ IN PSSP_CLIENT_CONNECTION ClientConnection,
+ IN OUT PSSP_API_MESSAGE Message
+ )
+
+/*++
+
+Routine Description:
+
+ This is a no-operation dispatch procedure. It is passed to the LPC
+ thread when no action is required.
+
+ For instance, it is used during service shutdown to awaken the LPC thread
+ so it can recognize that it should exit.
+
+
+Arguments:
+
+Return Value:
+
+ Status of the operation.
+
+--*/
+
+{
+ NTSTATUS Status;
+ SspPrint(( SSP_API, "SspApiNoop Entered\n" ));
+ Status = STATUS_SUCCESS;
+ SspPrint(( SSP_API, "SspApiNoop returns 0x%lx\n", Status ));
+ return Status;
+ UNREFERENCED_PARAMETER( Message );
+ UNREFERENCED_PARAMETER( ClientConnection );
+}
+
+SECURITY_STATUS
+SspApiNtLmSspControl(
+ IN PSSP_CLIENT_CONNECTION ClientConnection,
+ IN OUT PSSP_API_MESSAGE Message
+ )
+
+/*++
+
+Routine Description:
+
+ This is a no-operation dispatch procedure. It is passed to the LPC
+ thread when no action is required.
+
+ For instance, it is used during service shutdown to awaken the LPC thread
+ so it can recognize that it should exit.
+
+
+Arguments:
+
+Return Value:
+
+ STATUS_SUCCESS -- Call completed successfully
+
+ SEC_E_UNSUPPORTED_FUNCTION -- Function code is not supported
+
+--*/
+
+{
+#if DBG
+ SECURITY_STATUS SecStatus;
+ PSSP_NTLMSSP_CONTROL_ARGS Args;
+
+ //
+ // Initialization
+ //
+
+ SspPrint(( SSP_API, "SspApiNtLmSspControl Entered\n" ));
+ Args = &Message->Arguments.NtLmSspControlArgs;
+
+
+ //
+ // Force a breakpoint
+ //
+
+ switch ( Args->FunctionCode ) {
+ case NTLMSSP_BREAKPOINT:
+ KdPrint(( "NtLmSsp Break Point\n"));
+ DbgBreakPoint();
+ break;
+
+ //
+ // Change the debug flags
+ //
+
+ case NTLMSSP_DBFLAG:
+ SspGlobalDbflag = Args->Data;
+ SspPrint((SSP_MISC,"SspGlobalDbflag is set to %lx\n", SspGlobalDbflag ));
+ break;
+
+ //
+ // Truncate the log file
+ //
+
+ case NTLMSSP_TRUNCATE:
+
+ SspOpenDebugFile( TRUE );
+ SspPrint((SSP_MISC, "TRUNCATE_LOG function received.\n" ));
+ break;
+
+
+ //
+ // All other function codes are invalid.
+ //
+
+ default:
+ SecStatus = SEC_E_UNSUPPORTED_FUNCTION;
+ goto Cleanup;
+ }
+
+
+ SecStatus = STATUS_SUCCESS;
+
+Cleanup:
+ SspPrint(( SSP_API, "SspApiNtLmSspControl returns 0x%lx\n", SecStatus ));
+ return SecStatus;
+#else // DBG
+ return SEC_E_UNSUPPORTED_FUNCTION;
+ UNREFERENCED_PARAMETER( Message );
+#endif // DBG
+ UNREFERENCED_PARAMETER( ClientConnection );
+}
diff --git a/private/net/svcdlls/ntlmssp/server/dirs b/private/net/svcdlls/ntlmssp/server/dirs
new file mode 100644
index 000000000..903636d5b
--- /dev/null
+++ b/private/net/svcdlls/ntlmssp/server/dirs
@@ -0,0 +1,27 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ dirs.
+
+Abstract:
+
+ This file specifies the subdirectories of the current directory that
+ contain component makefiles.
+
+
+Author:
+
+ Steve Wood (stevewo) 17-Apr-1990
+
+NOTE: Commented description of this file is in \nt\bak\bin\dirs.tpl
+
+!ENDIF
+
+DIRS= \
+ dom \
+ export
+
+
diff --git a/private/net/svcdlls/ntlmssp/server/error.c b/private/net/svcdlls/ntlmssp/server/error.c
new file mode 100644
index 000000000..ceab8280d
--- /dev/null
+++ b/private/net/svcdlls/ntlmssp/server/error.c
@@ -0,0 +1,1062 @@
+/*++
+
+Copyright (c) 1987-1993 Microsoft Corporation
+
+Module Name:
+
+ error.c
+
+Abstract:
+
+ Error/shutdown routines for NtLmSsp service
+
+Author:
+
+ Cliff Van Dyke (CliffV) 09-Jun-1993
+
+Environment:
+
+ User mode only.
+ Contains NT-specific code.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+--*/
+
+//
+// Common include files.
+//
+
+#include <ntlmssps.h> // Include files common to server side of service
+
+//
+// Include files specific to this .c file
+//
+
+#include <netlib.h> // SET_SERVICE_EXITCODE() ...
+#include <lmalert.h> // LAN Manager alert routines
+#include <stdio.h> // sprintf(), ...
+
+
+
+NET_API_STATUS
+SspCleanup(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ Cleanup all global resources.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ //
+ // Indicate we're no longer running.
+ //
+
+ if ( SspGlobalRunningEvent != NULL) {
+ if ( !ResetEvent( SspGlobalRunningEvent ) ) {
+ SspPrint((SSP_CRITICAL, "Cannot reset 'running' event error: %lu\n",
+ GetLastError() ));
+ }
+ (VOID) CloseHandle( SspGlobalRunningEvent );
+ SspGlobalRunningEvent = NULL;
+ }
+
+
+
+ //
+ // Stop the LPC (Wait for outstanding calls to complete)
+ //
+
+ if ( SspGlobalLpcInitialized ) {
+ SspLpcTerminate();
+ SspGlobalLpcInitialized = FALSE;
+ }
+
+ if ( SspGlobalCommonInitialized ) {
+ SspCommonShutdown();
+ SspGlobalCommonInitialized = FALSE;
+ }
+
+
+
+
+
+ //
+ // Set the service state to uninstalled, and tell the service controller.
+ //
+
+ SspGlobalServiceStatus.dwCurrentState = SERVICE_STOPPED;
+ SspGlobalServiceStatus.dwCheckPoint = 0;
+ SspGlobalServiceStatus.dwWaitHint = 0;
+
+ if( !SetServiceStatus( SspGlobalServiceHandle,
+ &SspGlobalServiceStatus ) ) {
+
+ SspPrint((SSP_CRITICAL, "SetServiceStatus error: %lu\n",
+ GetLastError() ));
+ }
+
+ //
+ // Close service handle, we need not to close this handle.
+ //
+
+#ifdef notdef
+ // This service handle can not be closed
+ CloseServiceHandle( SspGlobalServiceHandle );
+#endif // notdef
+
+
+ //
+ // Close the handle to the debug file.
+ //
+
+#if DBG
+ EnterCriticalSection( &SspGlobalLogFileCritSect );
+ if ( SspGlobalLogFile != INVALID_HANDLE_VALUE ) {
+ CloseHandle( SspGlobalLogFile );
+ SspGlobalLogFile = INVALID_HANDLE_VALUE;
+ }
+ LeaveCriticalSection( &SspGlobalLogFileCritSect );
+#endif // DBG
+
+ //
+ // Delete the Event used to ask NtLmSsp to exit.
+ //
+
+ if( SspGlobalTerminateEvent != NULL &&
+ !CloseHandle( SspGlobalTerminateEvent ) ) {
+ SspPrint((SSP_CRITICAL,
+ "CloseHandle SspGlobalTerminateEvent error: %lu\n",
+ GetLastError() ));
+ }
+
+
+
+ //
+ // Return an exit status to our caller.
+ //
+ return (NET_API_STATUS)
+ ((SspGlobalServiceStatus.dwWin32ExitCode == ERROR_SERVICE_SPECIFIC_ERROR) ?
+ SspGlobalServiceStatus.dwServiceSpecificExitCode :
+ SspGlobalServiceStatus.dwWin32ExitCode);
+
+}
+
+
+VOID
+SspWriteEventlog (
+ IN DWORD EventID,
+ IN DWORD EventType,
+ IN LPBYTE buffer OPTIONAL,
+ IN DWORD numbytes,
+ IN LPWSTR *msgbuf,
+ IN DWORD strcount
+ )
+/*++
+
+Routine Description:
+
+ Stub routine for calling Event Log.
+
+Arguments:
+
+ EventID - event log ID.
+
+ EventType - Type of event.
+
+ buffer - Data to be logged with the error.
+
+ numbyte - Size in bytes of "buffer"
+
+ msgbuf - array of null-terminated strings.
+
+ strcount - number of zero terminated strings in "msgbuf"
+
+Return Value:
+
+ None.
+
+--*/
+{
+ DWORD ErrorCode;
+ DWORD i;
+
+ IF_DEBUG( MISC ) {
+ SspPrint((SSP_MISC, "Event Log: EventID = %ld EventType = %ld\n",
+ EventID,
+ EventType ));
+
+ for (i = 0; i < strcount; i++ ) {
+
+ SspPrint((SSP_MISC, "\t" FORMAT_LPWSTR "\n", msgbuf[i] ));
+ }
+
+#if DBG
+ if( numbytes ) {
+
+ SspDumpBuffer( SSP_MISC, buffer, numbytes );
+ }
+
+#endif // DBG
+ }
+
+ //
+ // write event
+ //
+
+ ErrorCode = NetpWriteEventlog(
+ SERVICE_NTLMSSP,
+ EventID,
+ EventType,
+ strcount,
+ msgbuf,
+ numbytes,
+ buffer);
+
+
+ IF_DEBUG( MISC ) {
+ if( ErrorCode != NO_ERROR ) {
+ SspPrint((SSP_MISC,
+ "Error writing this event in the eventlog, Status = %ld\n",
+ ErrorCode ));
+ }
+ }
+
+ return;
+}
+
+
+
+
+VOID
+SspExit(
+ IN DWORD ServiceError,
+ IN DWORD Data,
+ IN BOOL LogError,
+ IN LPWSTR ErrorString
+ )
+/*++
+
+Routine Description:
+
+ Registers service as uninstalled with error code.
+
+Arguments:
+
+ ServiceError - Service specific error code
+
+ Data - a DWORD of data to be logged with the message.
+ No data is logged if this is zero.
+
+ LogError - TRUE if if error should be logged to the event log
+
+ ErrorString - Error string to log with message.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ IF_DEBUG( MISC ) {
+
+ SspPrint((SSP_MISC, "SspExit: NtLmSsp exiting %lu 0x%lx",
+ ServiceError,
+ ServiceError ));
+
+ if ( Data ) {
+ SspPrint((SSP_MISC, " Data: %lu 0x%lx", Data, Data ));
+ }
+
+ if( ErrorString != NULL ) {
+ SspPrint((SSP_MISC, " '" FORMAT_LPWSTR "'", ErrorString ));
+ }
+
+ SspPrint(( SSP_MISC, "\n"));
+
+ }
+
+ //
+ // Record our exit in the event log.
+ //
+
+ if ( LogError ) {
+ SspWriteEventlog( ServiceError,
+ EVENTLOG_ERROR_TYPE,
+ (Data) ? (LPBYTE) &Data : NULL,
+ (Data) ? sizeof(Data) : 0,
+ (ErrorString != NULL) ? &ErrorString : NULL,
+ (ErrorString != NULL) ? 1 : 0 );
+ }
+
+ //
+ // Set the service state to stop pending.
+ //
+
+ SspGlobalServiceStatus.dwCurrentState = SERVICE_STOP_PENDING;
+ SspGlobalServiceStatus.dwWaitHint = NTLMSSP_INSTALL_WAIT;
+ SspGlobalServiceStatus.dwCheckPoint = 0;
+
+ SET_SERVICE_EXITCODE(
+ ServiceError,
+ SspGlobalServiceStatus.dwWin32ExitCode,
+ SspGlobalServiceStatus.dwServiceSpecificExitCode
+ );
+
+ //
+ // Tell the service controller what our state is.
+ //
+
+ if( !SetServiceStatus( SspGlobalServiceHandle,
+ &SspGlobalServiceStatus ) ) {
+
+ SspPrint((SSP_CRITICAL, "SetServiceStatus error: %lu\n",
+ GetLastError() ));
+ }
+
+ //
+ // Indicate that all threads should exit.
+ //
+
+ SspGlobalTerminate = TRUE;
+
+ if ( SspGlobalTerminateEvent != NULL &&
+ !SetEvent( SspGlobalTerminateEvent ) ) {
+ SspPrint((SSP_CRITICAL, "Cannot set termination event: %lu\n",
+ GetLastError() ));
+ }
+
+}
+
+
+
+BOOL
+GiveInstallHints(
+ IN BOOL Started
+ )
+/*++
+
+Routine Description:
+
+ Give hints to the installer of the service that installation is progressing.
+
+Arguments:
+
+ Started -- Set true to tell the service controller that we're done starting.
+
+Return Value:
+
+ TRUE -- iff install hint was accepted.
+
+--*/
+{
+ DWORD WaitStatus;
+
+ //
+ // If we're not installing,
+ // we don't need this install hint.
+ //
+
+ if ( SspGlobalServiceStatus.dwCurrentState != SERVICE_START_PENDING ) {
+ return TRUE;
+ }
+
+
+ //
+ // If we've been asked to exit,
+ // return FALSE immediately asking the caller to exit.
+ //
+
+ WaitStatus = WaitForSingleObject( SspGlobalTerminateEvent, 0 );
+
+ if ( WaitStatus != WAIT_TIMEOUT ) {
+ return FALSE;
+ }
+
+
+ //
+ // Tell the service controller our current state.
+ //
+
+ if ( Started ) {
+ SspGlobalServiceStatus.dwCurrentState = SERVICE_RUNNING;
+ SspGlobalServiceStatus.dwCheckPoint = 0;
+ SspGlobalServiceStatus.dwWaitHint = 0;
+ } else {
+ SspGlobalServiceStatus.dwCheckPoint++;
+ }
+
+ if( !SetServiceStatus( SspGlobalServiceHandle, &SspGlobalServiceStatus ) ) {
+ SspExit( SERVICE_UIC_SYSTEM, GetLastError(), TRUE, NULL);
+ return FALSE;
+ }
+
+ return TRUE;
+
+}
+
+
+VOID
+SspControlHandler(
+ IN DWORD opcode
+ )
+/*++
+
+Routine Description:
+
+ Process and respond to a control signal from the service controller.
+
+Arguments:
+
+ opcode - Supplies a value which specifies the action for the NtLmSsp
+ service to perform.
+
+Return Value:
+
+ None.
+
+--*/
+{
+
+ SspPrint((SSP_MISC, "In control handler (Opcode: %ld)\n", opcode ));
+
+ //
+ // Handle an uninstall request.
+ //
+
+ switch (opcode) {
+ case SERVICE_CONTROL_STOP: /* Uninstall required */
+
+ //
+ // Request the service to exit.
+ //
+ // SspExit also sets the service status to UNINSTALL_PENDING
+ // and tells the service controller.
+ //
+
+ SspExit( NERR_Success, 0, FALSE, NULL);
+ return;
+
+ //
+ // Pause the service.
+ //
+
+ case SERVICE_CONTROL_PAUSE:
+
+ SspGlobalServiceStatus.dwCurrentState = SERVICE_PAUSED;
+ break;
+
+ //
+ // Continute the service.
+ //
+
+ case SERVICE_CONTROL_CONTINUE:
+
+ SspGlobalServiceStatus.dwCurrentState = SERVICE_RUNNING;
+ break;
+
+ //
+ // By default, just return the current status.
+ //
+
+ case SERVICE_CONTROL_INTERROGATE:
+ default:
+ break;
+ }
+
+ //
+ // Always respond with the current status.
+ //
+
+ if( !SetServiceStatus( SspGlobalServiceHandle,
+ &SspGlobalServiceStatus ) ) {
+
+ SspPrint((SSP_CRITICAL, "SetServiceStatus error: %lu\n",
+ GetLastError() ));
+ }
+
+ return;
+}
+
+
+VOID
+RaiseAlert(
+ IN DWORD alert_no,
+ IN LPWSTR *string_array
+ )
+/*++
+
+Routine Description:
+
+ Raise NtLmSsp specific Admin alerts.
+
+Arguments:
+
+ alert_no - The alert to be raised, text in alertmsg.h
+
+ string_array - array of strings terminated by NULL string.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ NET_API_STATUS NetStatus;
+ LPWSTR *SArray;
+ PCHAR Next;
+ PCHAR End;
+
+ char message[ALERTSZ + sizeof(ADMIN_OTHER_INFO)];
+ PADMIN_OTHER_INFO admin = (PADMIN_OTHER_INFO) message;
+
+ IF_DEBUG( MISC ) {
+ DWORD i;
+
+ SspPrint((SSP_MISC,"alert: %ld\n", alert_no ));
+
+ for( SArray = string_array, i = 0; *SArray != NULL; SArray++, i++ ) {
+ SspPrint((SSP_MISC,"String %ld: " FORMAT_LPWSTR "\n", i, *SArray ));
+ }
+ }
+
+ //
+ // Build the variable data
+ //
+ admin->alrtad_errcode = alert_no;
+ admin->alrtad_numstrings = 0;
+
+ Next = (PCHAR) ALERT_VAR_DATA(admin);
+ End = Next + ALERTSZ;
+
+ //
+ // now take care of (optional) char strings
+ //
+
+ for( SArray = string_array; *SArray != NULL; SArray++ ) {
+ DWORD StringLen;
+
+ StringLen = (wcslen(*SArray) + 1) * sizeof(WCHAR);
+
+ if( Next + StringLen < End ) {
+
+ //
+ // copy next string.
+ //
+
+ RtlCopyMemory(Next, *SArray, StringLen);
+ Next += StringLen;
+ admin->alrtad_numstrings++;
+ }
+ else {
+
+ SspPrint((SSP_CRITICAL,"Error raising alert, Can't fit all "
+ "message strings in the alert buffer \n" ));
+
+ return;
+ }
+ }
+
+ //
+ // Call alerter.
+ //
+
+ NetStatus = NetAlertRaiseEx(
+ ALERT_ADMIN_EVENT,
+ message,
+ (DWORD)((PCHAR)Next - (PCHAR)message),
+ SERVICE_NTLMSSP );
+
+ if ( NetStatus != NERR_Success ) {
+ SspPrint((SSP_CRITICAL,"Error raising alert %lu\n", NetStatus));
+ }
+
+ return;
+}
+
+
+#if DBG
+
+VOID
+SspDumpBuffer(
+ IN DWORD DebugFlag,
+ PVOID Buffer,
+ DWORD BufferSize
+ )
+/*++
+
+Routine Description:
+
+ Dumps the buffer content on to the debugger output.
+
+Arguments:
+
+ DebugFlag: Debug flag to pass on to SspPrintRoutine
+
+ Buffer: buffer pointer.
+
+ BufferSize: size of the buffer.
+
+Return Value:
+
+ none
+
+--*/
+{
+#define NUM_CHARS 16
+
+ DWORD i, limit;
+ CHAR TextBuffer[NUM_CHARS + 1];
+ LPBYTE BufferPtr = Buffer;
+
+ //
+ // If we aren't debugging this functionality, just return.
+ //
+ if ( (SspGlobalDbflag & DebugFlag) == 0 ) {
+ return;
+ }
+
+ SspPrint((0,"------------------------------------\n"));
+
+ //
+ // Hex dump of the bytes
+ //
+ limit = ((BufferSize - 1) / NUM_CHARS + 1) * NUM_CHARS;
+
+ for (i = 0; i < limit; i++) {
+
+ if (i < BufferSize) {
+
+ SspPrint((0,"%02x ", BufferPtr[i]));
+
+ if (BufferPtr[i] < 31 ) {
+ TextBuffer[i % NUM_CHARS] = '.';
+ } else if (BufferPtr[i] == '\0') {
+ TextBuffer[i % NUM_CHARS] = ' ';
+ } else {
+ TextBuffer[i % NUM_CHARS] = (CHAR) BufferPtr[i];
+ }
+
+ } else {
+
+ SspPrint((0," "));
+ TextBuffer[i % NUM_CHARS] = ' ';
+
+ }
+
+ if ((i + 1) % NUM_CHARS == 0) {
+ TextBuffer[NUM_CHARS] = 0;
+ SspPrint((0," %s\n", TextBuffer));
+ }
+
+ }
+
+ SspPrint((0,"------------------------------------\n"));
+}
+
+#endif // DBG
+
+
+
+#if DBG
+VOID
+SspOpenDebugFile(
+ IN BOOL ReopenFlag
+ )
+/*++
+
+Routine Description:
+
+ Opens or re-opens the debug file
+
+Arguments:
+
+ ReopenFlag - TRUE to indicate the debug file is to be closed, renamed,
+ and recreated.
+
+Return Value:
+
+ None
+
+--*/
+
+{
+ WCHAR LogFileName[500];
+ WCHAR BakFileName[500];
+ DWORD FileAttributes;
+ DWORD PathLength;
+ DWORD WinError;
+
+ //
+ // Close the handle to the debug file, if it is currently open
+ //
+
+ EnterCriticalSection( &SspGlobalLogFileCritSect );
+ if ( SspGlobalLogFile != INVALID_HANDLE_VALUE ) {
+ CloseHandle( SspGlobalLogFile );
+ SspGlobalLogFile = INVALID_HANDLE_VALUE;
+ }
+ LeaveCriticalSection( &SspGlobalLogFileCritSect );
+
+ //
+ // make debug directory path first, if it is not made before.
+ //
+ if( SspGlobalDebugSharePath == NULL ) {
+
+ if ( !GetWindowsDirectoryW(
+ LogFileName,
+ sizeof(LogFileName)/sizeof(WCHAR) ) ) {
+ SspPrint((SSP_CRITICAL, "Window Directory Path can't be "
+ "retrieved, %lu.\n", GetLastError() ));
+ return;
+ }
+
+ //
+ // check debug path length.
+ //
+
+ PathLength = wcslen(LogFileName) * sizeof(WCHAR) +
+ sizeof(DEBUG_DIR) + sizeof(WCHAR);
+
+ if( (PathLength + sizeof(DEBUG_FILE) > sizeof(LogFileName) ) ||
+ (PathLength + sizeof(DEBUG_BAK_FILE) > sizeof(BakFileName) ) ) {
+
+ SspPrint((SSP_CRITICAL, "Debug directory path (%ws) length is too long.\n",
+ LogFileName));
+ goto ErrorReturn;
+ }
+
+ wcscat(LogFileName, DEBUG_DIR);
+
+ //
+ // copy debug directory name to global var.
+ //
+
+ SspGlobalDebugSharePath =
+ LocalAlloc( 0, (wcslen(LogFileName) + 1) * sizeof(WCHAR) );
+
+ if( SspGlobalDebugSharePath == NULL ) {
+ SspPrint((SSP_CRITICAL, "Can't allocated memory for debug share "
+ "(%ws).\n", LogFileName));
+ goto ErrorReturn;
+ }
+
+ wcscpy(SspGlobalDebugSharePath, LogFileName);
+ }
+ else {
+ wcscpy(LogFileName, SspGlobalDebugSharePath);
+ }
+
+ //
+ // Check this path exists.
+ //
+
+ FileAttributes = GetFileAttributesW( LogFileName );
+
+ if( FileAttributes == 0xFFFFFFFF ) {
+
+ WinError = GetLastError();
+ if( WinError == ERROR_FILE_NOT_FOUND ) {
+
+ //
+ // Create debug directory.
+ //
+
+ if( !CreateDirectoryW( LogFileName, NULL) ) {
+ SspPrint((SSP_CRITICAL, "Can't create Debug directory (%ws), "
+ "%lu.\n", LogFileName, GetLastError() ));
+ goto ErrorReturn;
+ }
+
+ }
+ else {
+ SspPrint((SSP_CRITICAL, "Can't Get File attributes(%ws), "
+ "%lu.\n", LogFileName, WinError ));
+ goto ErrorReturn;
+ }
+ }
+ else {
+
+ //
+ // if this is not a directory.
+ //
+
+ if(!(FileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
+
+ SspPrint((SSP_CRITICAL, "Debug directory path (%ws) exists "
+ "as file.\n", LogFileName));
+ goto ErrorReturn;
+ }
+ }
+
+ //
+ // Create the name of the old and new log file names
+ //
+
+ (VOID) wcscpy( BakFileName, LogFileName );
+ (VOID) wcscat( LogFileName, DEBUG_FILE );
+ (VOID) wcscat( BakFileName, DEBUG_BAK_FILE );
+
+
+ //
+ // If this is a re-open,
+ // delete the backup file,
+ // rename the current file to the backup file.
+ //
+
+ if ( ReopenFlag ) {
+
+ if ( !DeleteFile( BakFileName ) ) {
+ WinError = GetLastError();
+ if ( WinError != ERROR_FILE_NOT_FOUND ) {
+ SspPrint((SSP_CRITICAL,
+ "Cannot delete " FORMAT_LPWSTR "(%ld)\n",
+ BakFileName,
+ WinError ));
+ SspPrint((SSP_CRITICAL, " Try to re-open the file.\n"));
+ ReopenFlag = FALSE; // Don't truncate the file
+ }
+ }
+ }
+
+ if ( ReopenFlag ) {
+ if ( !MoveFile( LogFileName, BakFileName ) ) {
+ SspPrint((SSP_CRITICAL,
+ "Cannot rename " FORMAT_LPWSTR " to " FORMAT_LPWSTR
+ " (%ld)\n",
+ LogFileName,
+ BakFileName,
+ GetLastError() ));
+ SspPrint((SSP_CRITICAL,
+ " Try to re-open the file.\n"));
+ ReopenFlag = FALSE; // Don't truncate the file
+ }
+ }
+
+ //
+ // Open the file.
+ //
+
+ EnterCriticalSection( &SspGlobalLogFileCritSect );
+ SspGlobalLogFile = CreateFileW( LogFileName,
+ GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL,
+ ReopenFlag ? CREATE_ALWAYS : OPEN_ALWAYS,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL );
+
+
+ if ( SspGlobalLogFile == INVALID_HANDLE_VALUE ) {
+ SspPrint((SSP_CRITICAL, "cannot open " FORMAT_LPWSTR ",\n",
+ LogFileName ));
+ LeaveCriticalSection( &SspGlobalLogFileCritSect );
+ goto ErrorReturn;
+ } else {
+ // Position the log file at the end
+ (VOID) SetFilePointer( SspGlobalLogFile,
+ 0,
+ NULL,
+ FILE_END );
+ }
+
+ LeaveCriticalSection( &SspGlobalLogFileCritSect );
+ return;
+
+ErrorReturn:
+ SspPrint((SSP_CRITICAL,
+ " Debug output will be written to debug terminal.\n"));
+ return;
+}
+
+
+#define MAX_PRINTF_LEN 1024 // Arbitrary.
+
+VOID
+SspPrintRoutine(
+ IN DWORD DebugFlag,
+ IN LPSTR Format,
+ ...
+ )
+
+{
+ va_list arglist;
+ char OutputBuffer[MAX_PRINTF_LEN];
+ ULONG length;
+ DWORD BytesWritten;
+ static BeginningOfLine = TRUE;
+ static LineCount = 0;
+ static TruncateLogFileInProgress = FALSE;
+
+ //
+ // If we aren't debugging this functionality, just return.
+ //
+ if ( DebugFlag != 0 && (SspGlobalDbflag & DebugFlag) == 0 ) {
+ return;
+ }
+
+ //
+ // vsprintf isn't multithreaded + we don't want to intermingle output
+ // from different threads.
+ //
+
+ EnterCriticalSection( &SspGlobalLogFileCritSect );
+ length = 0;
+
+ //
+ // Handle the beginning of a new line.
+ //
+ //
+
+ if ( BeginningOfLine ) {
+
+ //
+ // If the log file is getting huge,
+ // truncate it.
+ //
+
+ if ( SspGlobalLogFile != INVALID_HANDLE_VALUE &&
+ !TruncateLogFileInProgress ) {
+
+ //
+ // Only check every 50 lines,
+ //
+
+ LineCount++;
+ if ( LineCount >= 50 ) {
+ DWORD FileSize;
+ LineCount = 0;
+
+ //
+ // Is the log file too big?
+ //
+
+ FileSize = GetFileSize( SspGlobalLogFile, NULL );
+ if ( FileSize == 0xFFFFFFFF ) {
+ (void) DbgPrint( "[NTLMSSP] Cannot GetFileSize %ld\n",
+ GetLastError );
+ } else if ( FileSize > SspGlobalLogFileMaxSize ) {
+ TruncateLogFileInProgress = TRUE;
+ LeaveCriticalSection( &SspGlobalLogFileCritSect );
+ SspOpenDebugFile( TRUE );
+ SspPrint(( SSP_MISC,
+ "Logfile truncated because it was larger than %ld bytes\n",
+ SspGlobalLogFileMaxSize ));
+ EnterCriticalSection( &SspGlobalLogFileCritSect );
+ TruncateLogFileInProgress = FALSE;
+ }
+
+ }
+ }
+
+ //
+ // If we're writing to the debug terminal,
+ // indicate this is an NtLmSsp message.
+ //
+
+ if ( SspGlobalLogFile == INVALID_HANDLE_VALUE ) {
+ length += (ULONG) sprintf( &OutputBuffer[length], "[NtLmSsp] " );
+ }
+
+ //
+ // Put the timestamp at the begining of the line.
+ //
+ IF_DEBUG( TIMESTAMP ) {
+ SYSTEMTIME SystemTime;
+ GetLocalTime( &SystemTime );
+ length += (ULONG) sprintf( &OutputBuffer[length],
+ "%02u/%02u %02u:%02u:%02u ",
+ SystemTime.wMonth,
+ SystemTime.wDay,
+ SystemTime.wHour,
+ SystemTime.wMinute,
+ SystemTime.wSecond );
+ }
+
+ //
+ // Indicate the type of message on the line
+ //
+ {
+ char *Text;
+
+ switch (DebugFlag) {
+ case SSP_INIT:
+ Text = "INIT"; break;
+ case SSP_MISC:
+ Text = "MISC"; break;
+ case SSP_CRITICAL:
+ Text = "CRITICAL"; break;
+ case SSP_LPC:
+ case SSP_LPC_MORE:
+ Text = "LPC"; break;
+ case SSP_API:
+ case SSP_API_MORE:
+ Text = "API"; break;
+
+ default:
+ Text = "UNKNOWN"; break;
+
+ case 0:
+ Text = NULL;
+ }
+ if ( Text != NULL ) {
+ length += (ULONG) sprintf( &OutputBuffer[length], "[%s] ", Text );
+ }
+ }
+ }
+ //
+ // Put a the information requested by the caller onto the line
+ //
+
+ va_start(arglist, Format);
+
+ length += (ULONG) vsprintf(&OutputBuffer[length], Format, arglist);
+ BeginningOfLine = (length > 0 && OutputBuffer[length-1] == '\n' );
+
+ va_end(arglist);
+
+ ASSERT(length <= MAX_PRINTF_LEN);
+
+
+ //
+ // If the log file isn't open,
+ // just output to the debug terminal
+ //
+
+ if ( SspGlobalLogFile == INVALID_HANDLE_VALUE ) {
+ (void) DbgPrint( (PCH) OutputBuffer);
+
+ //
+ // Write the debug info to the log file.
+ //
+
+ } else {
+ if ( !WriteFile( SspGlobalLogFile,
+ OutputBuffer,
+ lstrlenA( OutputBuffer ),
+ &BytesWritten,
+ NULL ) ) {
+ (void) DbgPrint( (PCH) OutputBuffer);
+ }
+
+ }
+
+ LeaveCriticalSection( &SspGlobalLogFileCritSect );
+
+} // SspPrintRoutine
+
+#endif // DBG
diff --git a/private/net/svcdlls/ntlmssp/server/export/makefile b/private/net/svcdlls/ntlmssp/server/export/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/net/svcdlls/ntlmssp/server/export/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/net/svcdlls/ntlmssp/server/export/makefile.inc b/private/net/svcdlls/ntlmssp/server/export/makefile.inc
new file mode 100644
index 000000000..7117abd25
--- /dev/null
+++ b/private/net/svcdlls/ntlmssp/server/export/makefile.inc
@@ -0,0 +1,2 @@
+obj\$(TARGET_DIRECTORY)\ssptest.res: ..\ssptest.rc
+
diff --git a/private/net/svcdlls/ntlmssp/server/export/ntlmssps.def b/private/net/svcdlls/ntlmssp/server/export/ntlmssps.def
new file mode 100644
index 000000000..a04fa98b1
--- /dev/null
+++ b/private/net/svcdlls/ntlmssp/server/export/ntlmssps.def
@@ -0,0 +1,9 @@
+LIBRARY NTLMSSPS
+
+DESCRIPTION 'NTLMSSPS'
+
+EXPORTS
+
+ ServiceEntry
+
+DATA SINGLE SHARED
diff --git a/private/net/svcdlls/ntlmssp/server/export/sources b/private/net/svcdlls/ntlmssp/server/export/sources
new file mode 100644
index 000000000..0bd842592
--- /dev/null
+++ b/private/net/svcdlls/ntlmssp/server/export/sources
@@ -0,0 +1,9 @@
+!include ..\sources.inc
+
+C_DEFINES=$(C_DEFINES) -DEXPORT_BUILD
+
+TARGETLIBS = $(TARGETLIBS) \
+ ..\..\common\export\obj\*\ntlmcomn.lib
+
+TARGETNAME=ntlmssps
+
diff --git a/private/net/svcdlls/ntlmssp/server/init.c b/private/net/svcdlls/ntlmssp/server/init.c
new file mode 100644
index 000000000..e02044798
--- /dev/null
+++ b/private/net/svcdlls/ntlmssp/server/init.c
@@ -0,0 +1,532 @@
+/*--
+
+Copyright (c) 1987-1993 Microsoft Corporation
+
+Module Name:
+
+ init.c
+
+Abstract:
+
+ Entry point and main thread of NtLmSsp service.
+
+Author:
+
+ Cliff Van Dyke (CliffV) 02-Jun-1993
+
+Environment:
+
+ User mode only.
+ Contains NT-specific code.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+--*/
+
+//
+// Common include files.
+//
+
+#define NTLMSSPS_ALLOCATE // Allocate data from ntlmssps.h
+#define DEBUG_ALLOCATE // Allocate data from debug.h
+#include <ntlmssps.h> // Include files common to server side of service
+#undef DEBUG_ALLOCATE
+#undef NTLMSSPS_ALLOCATE
+
+//
+// Include files specific to this .c file
+//
+
+
+#include <netlib.h> // SET_SERVICE_EXITCODE() ...
+#include <config.h> // net config helpers.
+#include <secobj.h> // ACE_DATA ...
+
+ULONG SspCommonSecHandleValue = SEC_HANDLE_NTLMSSPS;
+ // Placed in lower DWORD of contexts and creds
+
+
+BOOL
+SspParse(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ Get parameters from registry.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ TRUE -- iff the parse was successful.
+
+--*/
+{
+#if DBG
+ NET_API_STATUS NetStatus;
+
+ DWORD Value;
+ LPWSTR ValueT = NULL;
+
+ //
+ // Variables for scanning the configuration data.
+ //
+
+ LPNET_CONFIG_HANDLE SectionHandle = NULL;
+
+
+ //
+ // Open the NTLMSSP configuration section.
+ //
+
+ NetStatus = NetpOpenConfigData(
+ &SectionHandle,
+ NULL, // no server name.
+ SERVICE_NTLMSSP,
+ TRUE ); // we only want readonly access
+
+ if ( NetStatus != NO_ERROR ) {
+
+ //
+ // Since the only parameters are debug parameters,
+ // don't require that they even exist.
+ //
+
+ if ( NetStatus == NERR_CfgCompNotFound ) {
+ goto Cleanup;
+ }
+
+ SspExit(SERVICE_UIC_BADPARMVAL, NetStatus, TRUE, NULL );
+ return FALSE;
+ }
+
+
+
+ //
+ // Get the "DBFLAG" configured parameter
+ //
+
+ NetStatus = NetpGetConfigDword (
+ SectionHandle,
+ NTLMSSP_KEYWORD_DBFLAG,
+ 0,
+ &Value );
+
+ if (NetStatus == NO_ERROR) {
+ SspGlobalDbflag = Value;
+
+ } else {
+ (VOID) NetpCloseConfigData( SectionHandle );
+ SspExit(SERVICE_UIC_BADPARMVAL, NetStatus, TRUE,
+ NTLMSSP_KEYWORD_DBFLAG );
+ return FALSE;
+ }
+
+
+
+
+ //
+ // Get the "MaximumLogFileSize" configured parameter
+ //
+
+ NetStatus = NetpGetConfigDword(
+ SectionHandle,
+ NTLMSSP_KEYWORD_MAXIMUMLOGFILESIZE, // keyword wanted
+ DEFAULT_MAXIMUM_LOGFILE_SIZE,
+ &Value );
+
+ if (NetStatus == NO_ERROR) {
+ SspGlobalLogFileMaxSize = Value;
+
+ } else {
+
+ (VOID) NetpCloseConfigData( SectionHandle );
+ SspExit(SERVICE_UIC_BADPARMVAL, NetStatus, TRUE,
+ NTLMSSP_KEYWORD_MAXIMUMLOGFILESIZE);
+ return FALSE;
+
+ }
+
+
+Cleanup:
+
+ //
+ // Open the debug file
+ //
+
+ SspOpenDebugFile( FALSE );
+
+
+ SspPrint((SSP_INIT,"Following are the effective values after parsing\n"));
+ SspPrint((SSP_INIT," DbFlag = %lx\n", SspGlobalDbflag));
+ SspPrint((SSP_INIT," MaximumLogFileSize = %ld\n",SspGlobalLogFileMaxSize ));
+#endif // DBG
+
+ return TRUE;
+}
+
+
+
+BOOLEAN
+SspInitialize(
+ IN PLMSVCS_GLOBAL_DATA LmsvcsGlobalData
+ )
+
+/*++
+
+Routine Description:
+
+ Initialize the service.
+
+Arguments:
+
+ LmsvcsGlobalData -- Global data passed by services.exe.
+
+Return Value:
+
+ TRUE - initialization was successful
+ FALSE - initialization failed.
+
+--*/
+{
+
+ NET_API_STATUS NetStatus;
+ NTSTATUS Status;
+
+ SECURITY_ATTRIBUTES SecurityAttributes;
+
+ //
+ // Everyone has access to synchronize using this event.
+ //
+ ACE_DATA AceData[1] = {
+ {ACCESS_ALLOWED_ACE_TYPE, 0, 0,
+ SYNCHRONIZE, &LmsvcsGlobalData->WorldSid}
+ };
+
+
+ //
+ // Set all global to their initial value.
+ //
+
+ SspGlobalTerminate = FALSE;
+ SspGlobalTerminateEvent = NULL;
+
+
+ SspGlobalLpcInitialized = FALSE;
+ SspGlobalCommonInitialized = FALSE;
+
+ SspGlobalServiceHandle = (SERVICE_STATUS_HANDLE) NULL;
+
+ SspGlobalServiceStatus.dwServiceType = SERVICE_WIN32;
+ SspGlobalServiceStatus.dwCurrentState = SERVICE_START_PENDING;
+ SspGlobalServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP |
+ SERVICE_ACCEPT_PAUSE_CONTINUE;
+ SspGlobalServiceStatus.dwCheckPoint = 0;
+ SspGlobalServiceStatus.dwWaitHint = NTLMSSP_INSTALL_WAIT;
+
+ SspGlobalRunningEvent = NULL;
+
+ SET_SERVICE_EXITCODE(
+ NO_ERROR,
+ SspGlobalServiceStatus.dwWin32ExitCode,
+ SspGlobalServiceStatus.dwServiceSpecificExitCode
+ );
+
+#if DBG
+ SspGlobalLogFile = INVALID_HANDLE_VALUE;
+ SspGlobalLogFileMaxSize = DEFAULT_MAXIMUM_LOGFILE_SIZE;
+ SspGlobalDebugSharePath = NULL;
+ InitializeCriticalSection( &SspGlobalLogFileCritSect );
+ SspGlobalDbflag = SSP_CRITICAL;
+#endif // DBG
+
+
+
+
+ //
+ // Tell the service controller we've started.
+ //
+
+ SspGlobalServiceHandle =
+ RegisterServiceCtrlHandler( SERVICE_NTLMSSP, SspControlHandler);
+
+ if (SspGlobalServiceHandle == (SERVICE_STATUS_HANDLE) NULL) {
+
+ NetStatus = GetLastError();
+
+ SspPrint((SSP_CRITICAL, "RegisterServiceCtrlHandler failed %lu\n",
+ NetStatus ));
+
+ SspExit( SERVICE_UIC_SYSTEM, NetStatus, TRUE, NULL );
+ return FALSE;
+
+ }
+
+
+ //
+ // Initialize the termination event.
+ //
+
+ SspGlobalTerminate = FALSE;
+
+ SspGlobalTerminateEvent = CreateEvent( NULL, // No security attributes
+ TRUE, // Must be manually reset
+ FALSE, // Initially not signaled
+ NULL ); // No name
+
+ if ( SspGlobalTerminateEvent == NULL ) {
+ NetStatus = GetLastError();
+ SspPrint((SSP_CRITICAL, "Cannot create termination Event %lu\n",
+ NetStatus ));
+ SspExit( SERVICE_UIC_SYSTEM, NetStatus, TRUE, NULL );
+ return FALSE;
+ }
+
+ if ( !GiveInstallHints( FALSE ) ) {
+ return FALSE;
+ }
+
+
+
+
+ //
+ // SspParse the command line (.ini) arguments
+ // it will set globals reflecting switch settings
+ //
+
+ if ( !SspParse() ) {
+ return FALSE;
+ }
+
+ SspPrint((SSP_INIT,"Command line parsed successfully ...\n"));
+
+
+
+
+ //
+ // Create an event for our callers to wait on.
+ //
+
+ Status = NetpCreateSecurityDescriptor(
+ AceData,
+ sizeof(AceData)/sizeof(AceData[0]),
+ NULL, // Default the owner Sid
+ NULL, // Default the primary group
+ &SecurityAttributes.lpSecurityDescriptor );
+
+ if ( !NT_SUCCESS(Status) ) {
+ SspPrint((SSP_CRITICAL,
+ "Cannot create security descriptor for 'running' event %lx\n",
+ Status ));
+ SspExit( SERVICE_UIC_SYSTEM, Status, TRUE, NULL );
+ return FALSE;
+ }
+
+ SecurityAttributes.nLength = sizeof( SECURITY_ATTRIBUTES );
+ SecurityAttributes.bInheritHandle = TRUE;
+
+ SspGlobalRunningEvent = CreateEvent( &SecurityAttributes,
+ TRUE, // Must be manually reset
+ FALSE, // Initially not signaled
+ NTLMSSP_RUNNING_EVENT );
+
+ NetpMemoryFree( SecurityAttributes.lpSecurityDescriptor );
+
+ if ( SspGlobalRunningEvent == NULL ) {
+ NetStatus = GetLastError();
+ SspPrint((SSP_CRITICAL, "Cannot create 'running' event %lu\n",
+ NetStatus ));
+ SspExit( SERVICE_UIC_SYSTEM, NetStatus, TRUE, NULL );
+ return FALSE;
+ }
+
+
+
+
+
+
+ //
+ // Initialize routines shared by Service and DLL.
+ //
+
+ Status = SspCommonInitialize();
+
+ if ( !NT_SUCCESS( Status) ) {
+ SspExit( SERVICE_UIC_SYSTEM, Status, TRUE, NULL );
+ return FALSE;
+ }
+
+ SspGlobalCommonInitialized = TRUE;
+
+
+
+
+
+ //
+ // Initialize LPC
+ //
+
+ Status = SspLpcInitialize( LmsvcsGlobalData );
+
+ if ( !NT_SUCCESS( Status) ) {
+ SspExit( SERVICE_UIC_SYSTEM, Status, TRUE, NULL );
+ return FALSE;
+ }
+
+ SspGlobalLpcInitialized = TRUE;
+
+
+
+ //
+ // We're done, this will be final hint
+ //
+
+ if ( !GiveInstallHints( TRUE ) ) {
+ return FALSE;
+ }
+
+
+ //
+ // Tell all clients that we've started
+ //
+
+ if ( !SetEvent( SspGlobalRunningEvent) ) {
+ NetStatus = GetLastError();
+ SspPrint((SSP_CRITICAL, "Cannot set 'running' event %lu\n",
+ NetStatus ));
+ SspExit( SERVICE_UIC_SYSTEM, NetStatus, TRUE, NULL );
+ return FALSE;
+ }
+
+ return TRUE;
+
+}
+
+
+
+int
+LMSVCS_ENTRY_POINT(
+ IN DWORD argc,
+ IN LPTSTR *argv,
+ IN PLMSVCS_GLOBAL_DATA LmsvcsGlobalData,
+ IN HANDLE SvcRefHandle
+ )
+
+/*++
+
+Routine Description:
+
+ Main routine for NtLmSsp service.
+
+ This routine initializes the NtLmSsp service. This thread becomes
+ the scavenger thread for the service.
+
+Arguments:
+
+ argc, argv - Command line arguments for the service.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ NET_API_STATUS NetStatus;
+ DWORD WaitStatus;
+
+
+ //
+ // Initialize the service
+ //
+
+ if ( !SspInitialize( LmsvcsGlobalData ) ) {
+ goto Cleanup;
+ }
+
+
+ //
+ // Wait till the service is to exit.
+ //
+
+ WaitStatus = WaitForSingleObject( SspGlobalTerminateEvent, INFINITE );
+
+ if ( WaitStatus == WAIT_FAILED ) {
+ NetStatus = GetLastError();
+ SspPrint((SSP_CRITICAL, "WaitForMultipleObjects failed %lu\n",
+ NetStatus ));
+ SspExit( SERVICE_UIC_SYSTEM, NetStatus, TRUE, NULL );
+ goto Cleanup;
+ }
+
+
+ //
+ // Cleanup and return to our caller.
+ //
+Cleanup:
+ return (int) SspCleanup();
+ UNREFERENCED_PARAMETER( argc );
+ UNREFERENCED_PARAMETER( argv );
+ UNREFERENCED_PARAMETER( LmsvcsGlobalData );
+ UNREFERENCED_PARAMETER( SvcRefHandle);
+
+
+}
+
+
+BOOLEAN
+SspDllInit(
+ IN PVOID DllHandle,
+ IN ULONG Reason,
+ IN PCONTEXT Context OPTIONAL
+ )
+
+/*++
+
+Routine Description:
+
+ This is the Dll initialization routine for ntlmssps.dll
+
+Arguments:
+
+ Standard.
+
+Return Status:
+
+ TRUE: iff initialization succeeded
+
+--*/
+
+{
+
+ //
+ // On process attach, disable thread library calls
+ //
+
+ switch (Reason) {
+ case DLL_PROCESS_ATTACH:
+
+ DisableThreadLibraryCalls( DllHandle );
+ break;
+
+ //
+ // Handle process detach.
+ //
+
+ case DLL_PROCESS_DETACH:
+
+
+ break;
+
+ }
+
+ return(TRUE);
+
+ UNREFERENCED_PARAMETER( Context );
+
+}
+
diff --git a/private/net/svcdlls/ntlmssp/server/lpc.c b/private/net/svcdlls/ntlmssp/server/lpc.c
new file mode 100644
index 000000000..6bd5df55b
--- /dev/null
+++ b/private/net/svcdlls/ntlmssp/server/lpc.c
@@ -0,0 +1,1591 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ lpc.c
+
+Abstract:
+
+ NtLmSsp Service LPC Port listener and API dispatcher.
+
+Author:
+
+ Cliff Van Dyke (CliffV) 8-Jun-1993
+
+Revision History:
+
+ Borrowed from lsa\server\auloop.c written by JimK
+
+--*/
+
+
+//
+// Common include files.
+//
+
+#include <ntlmssps.h> // Include files common to server side of service
+
+//
+// Include files specific to this .c file
+//
+
+#include <ntlpcapi.h> // LPC data and routines
+#include <netlib.h> // NetpMemoryFree()
+#include <netlibnt.h> // NetpApiStatusToNtStatus()
+#include <secobj.h> // ACE_DATA ...
+
+
+//
+// Global Variables used within this module
+//
+
+//
+// These variables are used to control the number of
+// threads used for processing Logon Process calls.
+// They have the following uses:
+//
+// SspLpcFreeServerThreadCount - When this is decremented to zero, we
+// will attempt to create another thread (bounded by
+// SspLpcMaximumServerThreadCount).
+//
+// SspLpcActiveServerThreadCount - Indicates how many threads are
+// currently active (both free and in-use) for processing
+// LPC calls.
+//
+// SspMimimumServerThreadCount - Indicates the minimum number of
+// threads to have available for processing Client calls.
+// SspLpcActiveServerThreadCount should never be decremented
+// below this value.
+//
+// SspLpcMaximumServerThreadCount - Indicates the maximum number of
+// threads to create for processing Client calls.
+// SspLpcActiveServerThreadCount should never be incremented
+// above this value.
+//
+// Note: The following conditions may cause SspLpcActiveServerThreadCount
+// to go outside the Minimum/Maximum bounds:
+//
+// 1) Initialization - We may start out with fewer threads
+// than the minimum.
+//
+// 2) Race conditions at thread exit - we are sloppy (but
+// fast) in dealing with decrementing the Active count.
+//
+// 3) Changes in max or min values - obviously may make the
+// active count outside the max/min range.
+//
+
+
+LONG SspLpcActiveServerThreadCount;
+LONG SspLpcFreeServerThreadCount;
+LONG SspLpcMinimumServerThreadCount;
+LONG SspLpcMaximumServerThreadCount;
+
+#define SSP_THREAD_LIFETIME (10 * 60 * 1000 ) // Ten minutes
+
+NTSTATUS
+SspLpcThread (
+ IN PVOID ThreadParameter
+ );
+
+//
+// Handle to wait on while LPC threads terminate.
+//
+
+HANDLE SspLpcTerminateEvent;
+BOOLEAN SspLpcTerminateRequested;
+
+//
+// LPC port
+//
+
+HANDLE SspLpcApiPort;
+
+//
+// Crit Sect to protect various globals in this module.
+//
+
+CRITICAL_SECTION SspLpcCritSect;
+
+
+LIST_ENTRY SspLpcConnectionList;
+
+
+//
+// API routine dispatch table
+//
+
+PSSP_API_DISPATCH SspLpcApiDispatch[SspLpcMaxApiNumber] = {
+ SspApiAcquireCredentialHandle,
+ SspApiFreeCredentialHandle,
+ SspApiInitializeSecurityContext,
+ SspApiAcceptSecurityContext,
+ SspApiQueryContextAttributes,
+ SspApiDeleteSecurityContext,
+ SspApiNtLmSspControl,
+ SspApiNoop
+ };
+
+
+
+
+BOOLEAN
+SspLpcReferenceClientConnection(
+ IN PSSP_CLIENT_CONNECTION Connection,
+ IN BOOLEAN RemoveConnection
+ )
+
+/*++
+
+Routine Description:
+
+ This routine checks to see if the Connection is from a currently
+ active client, and references the Connection if it is valid.
+
+ The caller may optionally request that the client's Connection be
+ removed from the list of valid Connections - preventing future
+ requests from finding this Connection.
+
+ For a client's Connection to be valid, the Connection value
+ must be on our list of active clients.
+
+ ?? How do we ensure this is the original process calling us?
+
+
+Arguments:
+
+ Connection - Points to the Connection is to be referenced.
+
+ RemoveConnection - This boolean value indicates whether the caller
+ wants the logon process's Connection to be removed from the list
+ of Connections. TRUE indicates the Connection is to be removed.
+ FALSE indicates the Connection is not to be removed.
+
+
+Return Value:
+
+ TRUE - the Connection was found and was referenced.
+
+ FALSE - the Connection was not found.
+
+--*/
+
+{
+ PLIST_ENTRY ListEntry;
+ PSSP_CLIENT_CONNECTION ClientConnection;
+
+
+ //
+ // Acquire exclusive access to the Connection list
+ //
+
+ EnterCriticalSection( &SspLpcCritSect );
+
+
+ //
+ // Now walk the list of Connections looking for a match.
+ //
+
+ for ( ListEntry = SspLpcConnectionList.Flink;
+ ListEntry != &SspLpcConnectionList;
+ ListEntry = ListEntry->Flink ) {
+
+ ClientConnection =
+ CONTAINING_RECORD( ListEntry, SSP_CLIENT_CONNECTION, Next );
+
+
+ //
+ // Found a match ... reference this Connection
+ // (if the Connection is being removed, we would increment
+ // and then decrement the reference, so don't bother doing
+ // either - since they cancel each other out).
+ //
+
+ if ( ClientConnection == Connection) {
+
+
+ if (!RemoveConnection) {
+ Connection->References += 1;
+ } else {
+
+ RemoveEntryList( &Connection->Next );
+ SspPrint(( SSP_LPC_MORE, "Delinked Client Connection 0x%lx\n",
+ Connection ));
+ }
+
+ LeaveCriticalSection( &SspLpcCritSect );
+ return TRUE;
+
+ }
+
+ }
+
+
+ //
+ // No match found
+ //
+ SspPrint(( SSP_LPC, "Call from unknown Client Connection 0x%lx\n",
+ Connection ));
+
+ LeaveCriticalSection( &SspLpcCritSect );
+ return FALSE;
+
+}
+
+
+VOID
+SspLpcDereferenceClientConnection(
+ PSSP_CLIENT_CONNECTION ClientConnection
+ )
+
+/*++
+
+Routine Description:
+
+ This routine decrements the specified Connection's reference count.
+ If the reference count drops to zero, then the Connection is deleted
+
+Arguments:
+
+ ClientConnection - Points to the Connection to be dereferenced.
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ ULONG References;
+
+
+ //
+ // Decrement the reference count
+ //
+
+ EnterCriticalSection( &SspLpcCritSect );
+ ASSERT( ClientConnection->References >= 1 );
+ References = -- ClientConnection->References;
+ LeaveCriticalSection( &SspLpcCritSect );
+
+ //
+ // If the count dropped to zero, then run-down the Connection
+ //
+
+ if ( References == 0) {
+
+ SspPrint(( SSP_LPC_MORE, "Deleting Client Connection 0x%lx\n",
+ ClientConnection ));
+
+ //
+ // Tell the credential manager that this client session went away.
+ //
+
+ SspCredentialClientConnectionDropped( ClientConnection );
+
+ //
+ // Tell the context manager that this client session went away.
+ //
+
+ SspContextClientConnectionDropped( ClientConnection );
+
+
+ //
+ // Close the comm port and the client process.
+ //
+
+ Status = NtClose( ClientConnection->CommPort );
+ ASSERT( NT_SUCCESS(Status) );
+
+ Status = NtClose( ClientConnection->ClientProcess );
+ ASSERT( NT_SUCCESS(Status) );
+
+ LocalFree( ClientConnection );
+
+ }
+
+ return;
+
+}
+
+
+NTSTATUS
+SspLpcHandleConnectionRequest(
+ IN PSSP_API_MESSAGE Message
+ )
+
+/*++
+
+Routine Description:
+
+ This routine adds a new client Connection to the list of
+ valid client Connections.
+
+ After adding a new Connection, that Connection has been referenced
+ to allow the caller to continue using it. Therefore, the
+ caller is expected to dereference the Connection before completing
+ the LPC call.
+
+ This routine will initialize the Links and References fields
+ of the client Connection.
+
+Arguments:
+
+ Message - The connect message from the client
+
+Return Value:
+
+ Status of the operation.
+
+--*/
+
+{
+ NTSTATUS Status;
+ NTSTATUS SavedStatus = STATUS_SUCCESS;
+
+ PSSP_CLIENT_CONNECTION ClientConnection = NULL;
+ HANDLE ClientProcess = NULL;
+
+ OBJECT_ATTRIBUTES NullAttributes;
+
+ //
+ // Open the client process.
+ //
+ // This is needed to access the client's virtual memory (to copy arguments),
+ //
+
+ InitializeObjectAttributes( &NullAttributes, NULL, 0, NULL, NULL );
+
+ Status = NtOpenProcess(
+ &ClientProcess,
+ PROCESS_VM_READ | // To read memory
+ PROCESS_VM_WRITE| // To write memory
+ PROCESS_DUP_HANDLE, // To duplicate a handle into
+ &NullAttributes,
+ &Message->PortMessage.ClientId );
+
+ if ( !NT_SUCCESS(Status) ) {
+ SavedStatus = Status;
+ SspPrint(( SSP_LPC, "Cannot NtOpenProcess 0x%lx\n", Status ));
+ }
+
+
+ //
+ // Allocate a connection block and initialize it.
+ //
+
+ if ( NT_SUCCESS(SavedStatus) ) {
+
+ ClientConnection = LocalAlloc( 0, sizeof(SSP_CLIENT_CONNECTION) );
+
+ if ( ClientConnection == NULL ) {
+ SavedStatus = STATUS_NO_MEMORY;
+ SspPrint(( SSP_LPC, "Cannot allocate client connection 0x%lx\n", Status ));
+
+ } else {
+
+ //
+ // The reference count is set to 2. 1 to indicate it is on the
+ // valid Connection list, and one for the caller.
+ //
+
+ ClientConnection->References = 2;
+
+ ClientConnection->ClientProcess = ClientProcess;
+ ClientConnection->CommPort = NULL;
+ InitializeListHead( &ClientConnection->CredentialHead );
+ InitializeListHead( &ClientConnection->ContextHead );
+
+ }
+
+ }
+
+
+ //
+ // Accept or reject the connection.
+ //
+
+ Message->ConnectionRequest.CompletionStatus = SavedStatus;
+
+ Status = NtAcceptConnectPort(
+ &ClientConnection->CommPort,
+ (PVOID) ClientConnection,
+ (PPORT_MESSAGE) Message,
+ (BOOLEAN) NT_SUCCESS(SavedStatus), // Accept or reject
+ NULL,
+ NULL );
+
+ if ( !NT_SUCCESS(Status) ) {
+ SavedStatus = Status;
+ SspPrint(( SSP_LPC, "Cannot NtAcceptConnectPort 0x%lx\n", Status ));
+ goto Cleanup;
+ }
+
+ if ( !NT_SUCCESS(SavedStatus) ) {
+ goto Cleanup;
+ }
+
+
+ //
+ // Add it to the list of valid client Connections.
+ //
+
+ EnterCriticalSection( &SspLpcCritSect );
+ InsertHeadList( &SspLpcConnectionList, &ClientConnection->Next );
+ LeaveCriticalSection( &SspLpcCritSect );
+
+ SspPrint(( SSP_LPC_MORE, "Added Client Connection 0x%lx\n",
+ ClientConnection ));
+
+
+ //
+ // And complete the connection.
+ //
+
+ Status = NtCompleteConnectPort(ClientConnection->CommPort);
+ if ( !NT_SUCCESS(Status) ) {
+ SavedStatus = Status;
+ SspPrint(( SSP_LPC, "Cannot NtCompleteConnectPort 0x%lx\n", Status ));
+
+ EnterCriticalSection( &SspLpcCritSect );
+ RemoveEntryList( &ClientConnection->Next );
+ LeaveCriticalSection( &SspLpcCritSect );
+
+ goto Cleanup;
+ }
+
+
+ //
+ // We don't need to access the Connection any more, so
+ // dereference it.
+ //
+
+ SspLpcDereferenceClientConnection( ClientConnection );
+
+ //
+ // Free and locally used resources.
+ //
+Cleanup:
+
+ if ( !NT_SUCCESS(SavedStatus) ) {
+
+ if ( ClientConnection != NULL ) {
+ if ( ClientConnection->CommPort != NULL ) {
+ Status = NtClose( ClientConnection->CommPort );
+ ASSERT( NT_SUCCESS(Status) );
+ }
+ LocalFree( ClientConnection );
+ }
+
+ if ( ClientProcess != NULL ) {
+ Status = NtClose( ClientProcess );
+ ASSERT( NT_SUCCESS(Status) );
+ }
+
+ }
+
+
+ return SavedStatus;
+
+}
+
+
+
+
+
+NTSTATUS
+SspLpcCreateThread(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ Create another thread to handle LPC calls
+
+Arguments:
+
+ None
+
+Return Value:
+
+ Status of the operation.
+
+--*/
+
+{
+ DWORD Ignore;
+ NTSTATUS Status;
+ BOOLEAN ExitWhenDone;
+ HANDLE Thread;
+ DWORD WinStatus;
+ ULONG ActiveThreadCount;
+
+ //
+ // Increment count of active threads.
+ //
+ // Only kick off another thread if we haven't maxed out.
+ //
+
+ EnterCriticalSection( &SspLpcCritSect );
+
+ if (SspLpcActiveServerThreadCount >= SspLpcMaximumServerThreadCount ||
+ SspLpcTerminateRequested ) {
+ LeaveCriticalSection( &SspLpcCritSect );
+ return STATUS_SUCCESS;
+ }
+
+ ActiveThreadCount = ++SspLpcActiveServerThreadCount;
+ if (SspLpcActiveServerThreadCount > SspLpcMinimumServerThreadCount) {
+ ExitWhenDone = TRUE;
+ } else {
+ ExitWhenDone = FALSE;
+ }
+
+ (VOID) InterlockedIncrement(&SspLpcFreeServerThreadCount);
+
+ LeaveCriticalSection( &SspLpcCritSect );
+
+
+
+ //
+ // Create a thread to process connects and requests to our LPC Port
+ //
+
+ Thread = CreateThread(
+ NULL,
+ 0L,
+ (LPTHREAD_START_ROUTINE)SspLpcThread,
+ (LPVOID)ExitWhenDone,
+ 0L,
+ &Ignore
+ );
+
+ if (Thread == NULL) {
+ WinStatus = GetLastError();
+ SspPrint(( SSP_LPC, "Cannot Create LPC Thread %ld\n", WinStatus ));
+ Status = NetpApiStatusToNtStatus( WinStatus );
+
+ EnterCriticalSection( &SspLpcCritSect );
+ SspLpcActiveServerThreadCount--;
+
+ if (SspLpcActiveServerThreadCount == 0) {
+ SspPrint(( SSP_LPC,
+ "SspLpcCreateThread: Thread count went to zero.\n" ));
+ if ( !SetEvent( SspLpcTerminateEvent ) ) {
+ SspPrint(( SSP_CRITICAL,
+ "SspLpcCreateThread: Cannot set termination event\n" ));
+
+ }
+ }
+
+ (VOID) InterlockedDecrement(&SspLpcFreeServerThreadCount);
+
+ LeaveCriticalSection( &SspLpcCritSect );
+
+ } else {
+ SspPrint(( SSP_LPC, "Created LPC thread # %ld\n", ActiveThreadCount ));
+ (VOID) CloseHandle( Thread );
+ Status = STATUS_SUCCESS;
+ }
+
+ return Status;
+}
+
+
+SECURITY_STATUS
+SspLpcCopyToClientBuffer (
+ IN PSSP_CLIENT_CONNECTION ClientConnection,
+ IN ULONG Size,
+ OUT PVOID ClientBufferAddress,
+ IN PVOID LocalBufferAddress
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is used to copy information into a client process's address
+ space.
+
+Arguments:
+
+ ClientConnection - Is a pointer to a data structure representing the
+ client process.
+
+ Size - Indicates the Size of the buffer (in bytes) to be
+ copied.
+
+ ClientBufferAddress - Is the address of the buffer to receive the
+ data. This address is the address of the buffer within the
+ client process, not in the current process.
+
+ LocalBufferAddress - Points to the local buffer whose contents are to
+ be copied into the client address space.
+
+Return Status:
+
+ STATUS_SUCCESS - Indicates the routine completed successfully.
+
+
+--*/
+
+{
+
+ NTSTATUS Status;
+
+ Status = NtWriteVirtualMemory(
+ ClientConnection->ClientProcess,
+ ClientBufferAddress,
+ LocalBufferAddress,
+ Size,
+ NULL );
+
+ return SspNtStatusToSecStatus( Status, SEC_E_NO_IMPERSONATION );
+}
+
+
+SECURITY_STATUS
+SspLpcCopyFromClientBuffer (
+ IN PSSP_CLIENT_CONNECTION ClientConnection,
+ IN ULONG Size,
+ OUT PVOID LocalBufferAddress,
+ IN PVOID ClientBufferAddress
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is used to copy information from a client process's address
+ space into a local buffer.
+
+Arguments:
+
+ ClientConnection - Is a pointer to a data structure representing the
+ client process.
+
+ Size - Indicates the Size of the buffer (in bytes) to be
+ copied.
+
+ LocalBufferAddress - Points to the local buffer into which the data is
+ to be copied.
+
+ ClientBufferAddress - Is the address of the client buffer whose
+ contents are to be copied. This address is the address of
+ the buffer within the client process, not in the current
+ process.
+
+Return Status:
+
+ STATUS_SUCCESS - Indicates the routine completed successfully.
+
+
+--*/
+
+{
+ NTSTATUS Status;
+
+ Status = NtReadVirtualMemory(
+ ClientConnection->ClientProcess,
+ ClientBufferAddress,
+ LocalBufferAddress,
+ Size,
+ NULL);
+
+ return SspNtStatusToSecStatus( Status, SEC_E_NO_IMPERSONATION );
+
+
+}
+
+
+
+
+SECURITY_STATUS
+SspLpcImpersonateTokenHandle(
+ IN PSSP_CLIENT_CONNECTION ClientConnection,
+ IN HANDLE TokenHandle,
+ IN PCLIENT_ID ClientId
+ )
+
+/*++
+
+Routine Description:
+
+ This routine causes the client thread to impersonate the specified
+ token handle.
+
+
+Arguments:
+
+ ClientConnection - Is a pointer to a data structure representing the
+ client process.
+
+ TokenHandle - TokenHandle in our address space.
+
+ ClientId - Pointer to the client ID of the client's thread.
+
+Return Status:
+
+ STATUS_SUCCESS - Indicates the routine completed successfully.
+
+
+--*/
+
+{
+ SECURITY_STATUS SecStatus;
+ NTSTATUS Status;
+ HANDLE ClientThreadHandle = NULL;
+
+ OBJECT_ATTRIBUTES NullAttributes;
+ SECURITY_QUALITY_OF_SERVICE SecurityQos;
+ HANDLE NullImpersonationToken = NULL;
+
+ //
+ // Open a handle to the client thread.
+ //
+
+ InitializeObjectAttributes( &NullAttributes, NULL, 0, NULL, NULL );
+
+ Status = NtOpenThread(
+ &ClientThreadHandle,
+ THREAD_IMPERSONATE, // Access to impersonate
+ &NullAttributes,
+ ClientId ); // ID of thread to impersonate
+
+ if ( !NT_SUCCESS(Status) ) {
+ SecStatus = SspNtStatusToSecStatus( Status, SEC_E_NO_IMPERSONATION );
+ goto Cleanup;
+ }
+
+
+ //
+ // Impersonate the token
+ //
+ // We impersonate the token ourselves then pass the token to the client
+ // thread via our thread handle. (Uck!)
+ //
+
+ Status = NtSetInformationThread( NtCurrentThread(),
+ ThreadImpersonationToken,
+ &TokenHandle,
+ sizeof(TokenHandle) );
+
+ if ( !NT_SUCCESS(Status) ) {
+ SecStatus = SspNtStatusToSecStatus( Status, SEC_E_NO_IMPERSONATION );
+ goto Cleanup;
+ }
+
+
+ //
+ // Pass the token to the client thread.
+ //
+ // Don't be fooled by the names of the parameters. The first parameter
+ // is the thread handle of the thread we're passing the token to. The
+ // second parameter is the thread handle of the thread we're passing the
+ // token from.
+ //
+
+ SecurityQos.Length = sizeof(SecurityQos);
+ SecurityQos.ImpersonationLevel = SecurityImpersonation;
+ SecurityQos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
+ SecurityQos.EffectiveOnly = FALSE;
+
+ Status = NtImpersonateThread(
+ ClientThreadHandle,
+ NtCurrentThread(),
+ &SecurityQos );
+
+ if ( !NT_SUCCESS(Status) ) {
+ SecStatus = SspNtStatusToSecStatus( Status, SEC_E_NO_IMPERSONATION );
+ goto Cleanup;
+ }
+
+ SecStatus = STATUS_SUCCESS;
+
+ //
+ // Release local resources
+ //
+Cleanup:
+ if ( ClientThreadHandle != NULL ) {
+ NtClose(ClientThreadHandle);
+ }
+
+ //
+ // Stop impersonating.
+ //
+
+ (VOID) NtSetInformationThread( NtCurrentThread(),
+ ThreadImpersonationToken,
+ &NullImpersonationToken,
+ sizeof(NullImpersonationToken) );
+
+ return SecStatus;
+ UNREFERENCED_PARAMETER( ClientConnection );
+}
+
+
+
+
+SECURITY_STATUS
+SspLpcGetLogonId (
+ IN PSSP_CLIENT_CONNECTION ClientConnection,
+ IN PSSP_API_MESSAGE Message,
+ OUT PLUID LogonId,
+ OUT PHANDLE ClientTokenHandle
+ )
+
+/*++
+
+Routine Description:
+
+ This routine gets the Logon Id of token of the calling thread.
+
+Arguments:
+
+ ClientConnection - Describes the client process.
+
+ Message - Message from the caller.
+
+ LogonId - Returns the Logon Id of the calling thread.
+
+ ClientTokenHandle - Returns a handle to an impersonation token for
+ the calling thread.
+
+Return Status:
+
+ STATUS_SUCCESS - Indicates the routine completed successfully.
+
+
+--*/
+
+{
+ SECURITY_STATUS SecStatus;
+ NTSTATUS Status;
+ HANDLE NullImpersonationToken = NULL;
+
+ //
+ // Impersonate the client while we check him out.
+ //
+
+ Status = NtImpersonateClientOfPort(
+ ClientConnection->CommPort,
+ &Message->PortMessage );
+
+ if ( !NT_SUCCESS(Status) ) {
+ return SspNtStatusToSecStatus( Status, SEC_E_NO_IMPERSONATION );
+ }
+
+
+ SecStatus = SspGetLogonId ( LogonId, ClientTokenHandle );
+
+
+ //
+ // Revert to self
+ //
+
+ Status = NtSetInformationThread( NtCurrentThread(),
+ ThreadImpersonationToken,
+ &NullImpersonationToken,
+ sizeof(HANDLE) );
+
+ ASSERT( NT_SUCCESS(Status) );
+
+ return SecStatus;
+}
+
+
+
+NTSTATUS
+SspLpcDuplicateHandle (
+ IN PSSP_CLIENT_CONNECTION ClientConnection,
+ IN BOOLEAN FromClient,
+ IN BOOLEAN CloseSource,
+ IN HANDLE SourceHandle,
+ OUT PHANDLE DestHandle
+ )
+
+/*++
+
+Routine Description:
+
+ This routine duplicates the handle into the client's process.
+
+Arguments:
+
+ LocalHandle - handle in this process to duplicate. This handle is
+ closed after the duplication succeeds.
+
+ FromClient - If true, duplicates from client process to server, if
+ false, from server to client.
+
+ CloseSource - If true, close source handle on duplicate.
+
+ ClientHandle - Receives a handle in the client's process
+
+ ClientId - ID of the client
+
+Return Value:
+
+ STATUS_SUCESS - the routine completed succsesfully
+
+--*/
+{
+ NTSTATUS Status;
+ ULONG Flags;
+ HANDLE SourceProcess;
+ HANDLE DestProcess;
+
+ if (FromClient) {
+ SourceProcess = ClientConnection->ClientProcess;
+ DestProcess = NtCurrentProcess();
+ } else {
+ SourceProcess = NtCurrentProcess();
+ DestProcess = ClientConnection->ClientProcess;
+ }
+
+ if (CloseSource) {
+ Flags = DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE;
+ } else {
+ Flags = DUPLICATE_SAME_ACCESS;
+
+ }
+
+ ASSERT(ClientConnection->ClientProcess != NULL);
+
+ Status = NtDuplicateObject(
+ SourceProcess,
+ SourceHandle,
+ DestProcess,
+ DestHandle,
+ 0, // no desired access
+ 0, // no attributes
+ Flags
+ );
+
+ if (!NT_SUCCESS(Status)) {
+ return(SspNtStatusToSecStatus( Status, SEC_E_NO_IMPERSONATION ));
+ }
+
+ return(SEC_E_OK);
+
+
+}
+
+
+NTSTATUS
+SspLpcThread (
+ IN PVOID ThreadParameter
+ )
+
+/*++
+
+Routine Description:
+
+ This is and instance of a thread used to listen for incoming LPC messages.
+ This routine dispatches the API calls to their dispatch routines.
+
+Arguments:
+
+ ThreadParameter - NULL if this is a permanent thread.
+
+Return Value:
+
+ Success - But no one ever checks the status
+
+--*/
+
+{
+ SSP_API_MESSAGE Request;
+ PSSP_API_MESSAGE Reply;
+ PSSP_CLIENT_CONNECTION ClientConnection;
+ NTSTATUS Status;
+ LONG FreeCount;
+ LONG ActiveThreadCount;
+
+ BOOLEAN ConnectionFound;
+
+ LARGE_INTEGER ThreadStartTime;
+ ULONG ThreadLifetime;
+
+ //
+ // Let the caller decide if we should load balance this thread.
+ //
+
+ if (ThreadParameter != NULL) {
+ (VOID) NtQuerySystemTime( &ThreadStartTime );
+ ThreadLifetime = SSP_THREAD_LIFETIME;
+ } else {
+ ThreadLifetime = INFINITE;
+ }
+
+
+ //
+ // First time through, there is no reply.
+ //
+
+ Reply = NULL;
+
+
+ //
+ // Now loop indefinitely, processing requests
+ //
+
+ for(;;) {
+
+ //
+ // Reply to the previous message and wait for a new message.
+ //
+
+ Status = NtReplyWaitReceivePort(
+ SspLpcApiPort,
+ (PVOID *) &ClientConnection,
+ (PPORT_MESSAGE) Reply,
+ (PPORT_MESSAGE) &Request
+ );
+
+
+
+ //
+ // Indicate this thread is processing a request.
+ // Start another LPC thread if we were the last free thread.
+ //
+
+ FreeCount = InterlockedDecrement(&SspLpcFreeServerThreadCount);
+ if ( FreeCount <= 0 ) {
+ (VOID) SspLpcCreateThread();
+ }
+
+
+
+
+
+
+ //
+ // Most failures should be simply ignored.
+ //
+
+
+ if ( !NT_SUCCESS( Status ) && Status != STATUS_INVALID_CID ) {
+
+ SspPrint(( SSP_LPC,
+ "NtReplyWaitReceivePort failed 0x%lx\n",
+ Status ));
+ Reply = NULL;
+
+
+
+ //
+ // Disconnect from the client
+ //
+ // These messages could be received in any of the following
+ // conditions:
+ //
+ // 1) A bug in the client side has inadvertantly closed
+ // the port handle.
+ //
+ // 2) The LsaDeregisterClient() call has called
+ // lsa, recevied completion status, and then beat
+ // lsa in closing the port (a race condition that
+ // can not be eliminated).
+ //
+ // 3) The client has died and the comm port is being
+ // rundown as part of process rundown.
+ //
+ // The first case is a bug, and the client is bound to find
+ // out about it real soon. In the second case, we will normally
+ // not have a client Connection left to reference, so looking for it
+ // won't hurt anything. So, the correct behaviour here is to
+ // try to reference and then delete the client's Connection.
+
+ } else if ( Status == STATUS_INVALID_CID ||
+ Request.PortMessage.u2.s2.Type == LPC_PORT_CLOSED ||
+ Request.PortMessage.u2.s2.Type == LPC_CLIENT_DIED ) {
+
+ SspPrint(( SSP_LPC,
+ "NtReplyWaitReceivePort port closed 0x%lx 0x%lx\n",
+ Status,
+ Request.PortMessage.u2.s2.Type ));
+
+ ConnectionFound = SspLpcReferenceClientConnection(
+ ClientConnection,
+ TRUE); // Remove Connection
+
+ if (ConnectionFound) {
+
+ //
+ // Dereferencing the Connection will cause it to be rundown.
+ //
+
+ SspLpcDereferenceClientConnection(ClientConnection);
+ }
+
+ Reply = NULL;
+
+
+
+ //
+ // Handle a Datagram request.
+ //
+
+ } else if (Request.PortMessage.u2.s2.Type == LPC_DATAGRAM) {
+
+ SspPrint(( SSP_LPC,
+ "NtReplyWaitReceivePort received datagram %ld\n",
+ Request.ApiNumber ));
+
+
+ //
+ // The only API supported in a datagram is No-op.
+ //
+
+ if ( Request.ApiNumber != SspLpcNoop ) {
+
+ SspPrint(( SSP_LPC,
+ "NtReplyWaitReceivePort invalid datagram ApiNumber %ld\n",
+ Request.ApiNumber ));
+
+ }
+
+ //
+ // Can't reply to a datagram.
+ //
+
+ Reply = NULL;
+
+ //
+ // Connect to a new client.
+ //
+
+ } else if (Request.PortMessage.u2.s2.Type == LPC_CONNECTION_REQUEST) {
+ Status = SspLpcHandleConnectionRequest( &Request );
+ Reply = NULL;
+
+
+
+
+ //
+ // Handle an API request.
+ //
+
+ } else if (Request.PortMessage.u2.s2.Type == LPC_REQUEST) {
+
+
+ //
+ // If the client passed a bogus API number,
+ // tell him so.
+ //
+
+ if (Request.ApiNumber >= SspLpcMaxApiNumber ) {
+
+ SspPrint(( SSP_LPC,
+ "NtReplyWaitReceivePort invalid ApiNumber %ld\n",
+ Request.ApiNumber ));
+
+
+ Reply = &Request;
+ Reply->ReturnedStatus = STATUS_INVALID_SYSTEM_SERVICE;
+
+
+ //
+ // Handle the valid API numbers
+ //
+
+ } else {
+
+
+ //
+ // Try to refrence the Connection. If one isn't found,
+ // then we must be deleting it in another thread.
+ //
+
+ ConnectionFound = SspLpcReferenceClientConnection(
+ ClientConnection,
+ FALSE );
+
+ if (!ConnectionFound) {
+
+ Reply = &Request;
+ Reply->ReturnedStatus = STATUS_INVALID_SYSTEM_SERVICE;
+
+ //
+ // Finally dispatch to the API routine.
+ //
+
+ } else {
+
+ //
+ // Valid API number - dispatch it
+ //
+
+ Request.ReturnedStatus =
+ (SspLpcApiDispatch[Request.ApiNumber])(
+ ClientConnection,
+ &Request );
+
+
+ Reply = &Request;
+
+
+ //
+ // Dereference the client connection.
+ //
+
+ SspLpcDereferenceClientConnection(ClientConnection);
+ }
+ }
+
+
+
+ //
+ // This is a totally unexpected situation, but we will
+ // cover our posterier just in case we come across an
+ // unexpected error.
+ //
+
+ } else {
+
+ SspPrint(( SSP_LPC,
+ "NtReplyWaitReceivePort invalid PortMessage %ld\n",
+ Request.PortMessage.u2.s2.Type ));
+ Reply = NULL;
+
+ }
+
+
+ //
+ // If this thread is to exit when done,
+ // and if this thread has outlived its usefulness,
+ // Exit now
+ //
+
+ if ( SspTimeHasElapsed( ThreadStartTime, ThreadLifetime) ||
+ SspLpcTerminateRequested ) {
+
+
+ EnterCriticalSection( &SspLpcCritSect );
+ ActiveThreadCount = --SspLpcActiveServerThreadCount;
+
+ if (SspLpcActiveServerThreadCount == 0) {
+ SspPrint(( SSP_LPC,
+ "SspLpcThread: Thread count went to zero.\n" ));
+ if ( !SetEvent( SspLpcTerminateEvent ) ) {
+ SspPrint(( SSP_CRITICAL,
+ "SspLpcThread: Cannot set termination event\n" ));
+ }
+ }
+ LeaveCriticalSection( &SspLpcCritSect );
+
+ //
+ // Send a reply if one is required
+ //
+
+ if ( Reply != NULL ) {
+
+ Status = NtReplyPort(
+ SspLpcApiPort,
+ (PPORT_MESSAGE) Reply
+ );
+
+ if (!NT_SUCCESS(Status)) {
+ SspPrint(( SSP_LPC,
+ "SspLpcThread: NtReplyPort failed 0x%lx.\n",
+ Status ));
+ }
+ }
+
+ SspPrint(( SSP_LPC, "Exit LPC thread. (%ld left)\n", ActiveThreadCount ));
+ return(STATUS_SUCCESS);
+ }
+
+
+ //
+ // Indicate we've freed up a thread.
+ //
+
+ (VOID) InterlockedIncrement(&SspLpcFreeServerThreadCount);
+
+ }
+
+ /* NOT REACHED */
+ ASSERT( FALSE );
+
+}
+
+
+
+NTSTATUS
+SspLpcInitialize(
+ IN PLMSVCS_GLOBAL_DATA LmsvcsGlobalData
+ )
+
+/*++
+
+Routine Description:
+
+ This function creates a port for communicating with the client.
+ It then creates threads to listen for connections
+ and to act upon calls from those processes.
+
+Arguments:
+
+ None.
+ LmsvcsGlobalData -- Global data passed by services.exe.
+
+Return Value:
+
+ Status of the operation.
+
+--*/
+
+{
+
+ NTSTATUS Status;
+ OBJECT_ATTRIBUTES PortObjectAttributes;
+ UNICODE_STRING PortName;
+ PSECURITY_DESCRIPTOR SecurityDescriptor;
+
+ //
+ // Everyone has access to read/write/connect to the LPC port
+ //
+
+ ACE_DATA AceData[1] = {
+ {ACCESS_ALLOWED_ACE_TYPE, 0, 0,
+ STANDARD_RIGHTS_READ | STANDARD_RIGHTS_WRITE | PORT_CONNECT,
+ &LmsvcsGlobalData->WorldSid}
+ };
+
+
+ //
+ // Create the LPC port
+ // (Allow everyone access
+ //
+
+ RtlInitUnicodeString( &PortName, NTLMSSP_LPC_PORT_NAME );
+
+ Status = NetpCreateSecurityDescriptor(
+ AceData,
+ sizeof(AceData)/sizeof(AceData[0]),
+ NULL, // Default the owner Sid
+ NULL, // Default the primary group
+ &SecurityDescriptor );
+
+ if ( !NT_SUCCESS(Status) ) {
+ SspPrint((SSP_CRITICAL,
+ "Cannot create security descriptor for lpc port 0x%lx\n",
+ Status ));
+ return Status;
+ }
+
+
+ InitializeObjectAttributes(
+ &PortObjectAttributes,
+ &PortName,
+ 0,
+ NULL,
+ SecurityDescriptor );
+
+ Status = NtCreatePort(
+ &SspLpcApiPort,
+ &PortObjectAttributes,
+ sizeof(SSP_REGISTER_CONNECT_INFO),
+ sizeof(SSP_API_MESSAGE),
+ sizeof(SSP_API_MESSAGE) * 32
+ );
+
+ NetpMemoryFree( SecurityDescriptor );
+
+ if ( !NT_SUCCESS(Status) ) {
+ SspPrint(( SSP_LPC, "Cannot NtCreatePort 0x%lx\n", Status ));
+ return Status;
+ }
+
+
+ InitializeCriticalSection(&SspLpcCritSect);
+
+ SspLpcActiveServerThreadCount = 0;
+ SspLpcFreeServerThreadCount = 0;
+ SspLpcMinimumServerThreadCount = 2;
+ SspLpcMaximumServerThreadCount = 16;
+
+ SspLpcTerminateRequested = FALSE;
+
+
+ //
+ // Initialize the connection list to be empty.
+ //
+
+ InitializeListHead( &SspLpcConnectionList );
+
+ //
+ // Create the termination event.
+ //
+ // This event is signalled when the last LPC thread exits.
+ //
+
+ SspLpcTerminateEvent = CreateEvent( NULL, // No security attributes
+ TRUE, // Must be manually reset
+ FALSE, // Initially not signaled
+ NULL ); // No name
+
+ if ( SspLpcTerminateEvent == NULL ) {
+ DWORD WinStatus;
+ WinStatus = GetLastError();
+ SspPrint(( SSP_LPC, "Cannot Create Terminate Event %ld\n", WinStatus ));
+ Status = NetpApiStatusToNtStatus( WinStatus );
+
+ SspLpcTerminate();
+ return Status;
+ }
+
+ //
+ // Create a thread to process connects and requests to our LPC Port
+ //
+
+ Status = SspLpcCreateThread();
+
+ if ( !NT_SUCCESS(Status) ) {
+ SspLpcTerminate();
+ }
+
+ return Status;
+
+}
+
+
+
+
+VOID
+SspLpcTerminate(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ This function closes the port for communicating with the client.
+ It then wait for all the LPC threads to terminate.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ Status of the operation.
+
+--*/
+
+{
+
+ NTSTATUS Status;
+ ULONG ActiveThreadCount;
+
+ //
+ // Let the threads know that they are supposed to exit.
+ //
+
+
+ EnterCriticalSection( &SspLpcCritSect );
+ SspLpcTerminateRequested = TRUE;
+ ActiveThreadCount = SspLpcActiveServerThreadCount;
+ LeaveCriticalSection( &SspLpcCritSect );
+
+ //
+ // Wait for Threads to exit
+ //
+
+ if ( ActiveThreadCount > 0 ) {
+ SSP_API_MESSAGE Message;
+ ULONG i;
+
+ //
+ // Send a no-op message (the threads will see the terminate flag)
+ //
+
+ Message.ApiNumber = SspLpcNoop;
+ Message.PortMessage.u1.s1.DataLength = sizeof(SSP_API_NUMBER) +
+ sizeof(SECURITY_STATUS);
+ Message.PortMessage.u1.s1.TotalLength = Message.PortMessage.u1.s1.DataLength +
+ sizeof(PORT_MESSAGE);
+ Message.PortMessage.u2.ZeroInit = 0;
+
+ //
+ // Send a message to each active thread asking it to leave.
+ //
+
+ for ( i=0; i<ActiveThreadCount ; i++ ) {
+
+ Status = NtRequestPort( SspLpcApiPort,
+ &Message.PortMessage );
+
+ ASSERT( NT_SUCCESS(Status) );
+
+ }
+
+ WaitForSingleObject( SspLpcTerminateEvent, INFINITE );
+
+ }
+
+ //
+ // Close the LPC terminate event.
+ //
+
+ if ( SspLpcTerminateEvent != NULL) {
+ (VOID) CloseHandle( SspLpcTerminateEvent );
+ SspLpcTerminateEvent = NULL;
+ }
+
+ //
+ // Close the LPC port
+ //
+
+ Status = NtClose( SspLpcApiPort );
+ if ( !NT_SUCCESS(Status) ) {
+ SspPrint(( SSP_LPC, "Cannot NtClose LPC Port 0x%lx\n", Status ));
+ }
+
+
+
+ //
+ // Drop any lingering connections
+ //
+
+ EnterCriticalSection( &SspLpcCritSect );
+ while ( !IsListEmpty( &SspLpcConnectionList ) ) {
+ BOOLEAN ConnectionFound;
+ PSSP_CLIENT_CONNECTION ClientConnection;
+
+ ClientConnection = CONTAINING_RECORD( SspLpcConnectionList.Flink,
+ SSP_CLIENT_CONNECTION,
+ Next );
+
+ LeaveCriticalSection( &SspLpcCritSect );
+
+ ConnectionFound = SspLpcReferenceClientConnection(
+ ClientConnection,
+ TRUE); // Remove Connection
+
+ if (ConnectionFound) {
+ SspLpcDereferenceClientConnection(ClientConnection);
+ }
+
+ EnterCriticalSection( &SspLpcCritSect );
+ }
+ LeaveCriticalSection( &SspLpcCritSect );
+
+ //
+ // Delete the critical section
+ //
+
+ DeleteCriticalSection(&SspLpcCritSect);
+
+ return;
+
+}
diff --git a/private/net/svcdlls/ntlmssp/server/ntlmssps.def b/private/net/svcdlls/ntlmssp/server/ntlmssps.def
new file mode 100644
index 000000000..a04fa98b1
--- /dev/null
+++ b/private/net/svcdlls/ntlmssp/server/ntlmssps.def
@@ -0,0 +1,9 @@
+LIBRARY NTLMSSPS
+
+DESCRIPTION 'NTLMSSPS'
+
+EXPORTS
+
+ ServiceEntry
+
+DATA SINGLE SHARED
diff --git a/private/net/svcdlls/ntlmssp/server/ntlmssps.h b/private/net/svcdlls/ntlmssp/server/ntlmssps.h
new file mode 100644
index 000000000..b0a73f911
--- /dev/null
+++ b/private/net/svcdlls/ntlmssp/server/ntlmssps.h
@@ -0,0 +1,255 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ ntlmssps.h
+
+Abstract:
+
+ Header file common to all of the NT Lanman Security Support Provider
+ (NtLmSsp) Service.
+
+Author:
+
+ Cliff Van Dyke (CliffV) 09-Jun-1993
+
+Revision History:
+
+--*/
+
+#ifndef _NTLMSSPS_INCLUDED_
+#define _NTLMSSPS_INCLUDED_
+
+////////////////////////////////////////////////////////////////////////////
+//
+// Common include files needed by ALL NtLmSsp Server files
+//
+////////////////////////////////////////////////////////////////////////////
+
+#include <ntlmcomn.h> // Common definitions for DLL and SERVICE
+
+#include <lmsvc.h> // SERVICE_UIC_*
+#include <lmerr.h> // NERR_*
+#include <debugfmt.h> // FORMAT_LPWSTR ...
+#include <services.h> // LMSVCS_GLOBAL_DATA
+
+
+//
+// init.c will #include this file with NTLMSSPS_ALLOCATE defined.
+// That will cause each of these variables to be allocated.
+//
+#ifdef NTLMSSPS_ALLOCATE
+#define EXTERN
+#else
+#define EXTERN extern
+#endif
+
+
+////////////////////////////////////////////////////////////////////////
+//
+// Global Definitions
+//
+////////////////////////////////////////////////////////////////////////
+
+//
+// Maximum amount of time between install hints.
+//
+#define NTLMSSP_INSTALL_WAIT 10000 // 10 seconds
+
+
+
+//
+// Generic interface to LPC dispatch routines
+//
+
+typedef SECURITY_STATUS
+(* PSSP_API_DISPATCH)(
+ IN PSSP_CLIENT_CONNECTION ClientConnection,
+ IN OUT PSSP_API_MESSAGE Message
+ );
+
+
+
+////////////////////////////////////////////////////////////////////////
+//
+// Global Variables
+//
+////////////////////////////////////////////////////////////////////////
+
+
+//
+// Flags indicating various modules have been started and must be stopped on exit.
+//
+
+EXTERN BOOLEAN SspGlobalLpcInitialized;
+EXTERN BOOLEAN SspGlobalCommonInitialized;
+
+
+//
+// Variables for communicating with the service controller.
+//
+
+EXTERN SERVICE_STATUS SspGlobalServiceStatus;
+EXTERN SERVICE_STATUS_HANDLE SspGlobalServiceHandle;
+
+//
+// Service Termination event.
+//
+
+EXTERN HANDLE SspGlobalTerminateEvent;
+EXTERN BOOLEAN SspGlobalTerminate;
+
+//
+// Service running event.
+//
+
+EXTERN HANDLE SspGlobalRunningEvent;
+
+
+////////////////////////////////////////////////////////////////////////
+//
+// Procedure Forwards
+//
+////////////////////////////////////////////////////////////////////////
+
+//
+// error.c
+//
+
+NET_API_STATUS
+SspCleanup(
+ VOID
+ );
+
+VOID
+SspExit(
+ IN DWORD ServiceError,
+ IN DWORD Data,
+ IN BOOL LogError,
+ IN LPWSTR ErrorString
+ );
+
+BOOL
+GiveInstallHints(
+ IN BOOL Started
+ );
+
+VOID
+SspControlHandler(
+ IN DWORD opcode
+ );
+
+VOID
+RaiseAlert(
+ IN DWORD alert_no,
+ IN LPWSTR *string_array
+ );
+
+
+
+//
+// Procedure forwards from api.c
+//
+
+SECURITY_STATUS
+SspApiAcquireCredentialHandle(
+ IN PSSP_CLIENT_CONNECTION ClientConnection,
+ IN OUT PSSP_API_MESSAGE Message
+ );
+
+SECURITY_STATUS
+SspApiFreeCredentialHandle(
+ IN PSSP_CLIENT_CONNECTION ClientConnection,
+ IN OUT PSSP_API_MESSAGE Message
+ );
+
+SECURITY_STATUS
+SspApiInitializeSecurityContext(
+ IN PSSP_CLIENT_CONNECTION ClientConnection,
+ IN OUT PSSP_API_MESSAGE Message
+ );
+
+SECURITY_STATUS
+SspApiAcceptSecurityContext(
+ IN PSSP_CLIENT_CONNECTION ClientConnection,
+ IN OUT PSSP_API_MESSAGE Message
+ );
+
+SECURITY_STATUS
+SspApiImpersonateSecurityContext(
+ IN PSSP_CLIENT_CONNECTION ClientConnection,
+ IN OUT PSSP_API_MESSAGE Message
+ );
+
+SECURITY_STATUS
+SspApiRevertSecurityContext(
+ IN PSSP_CLIENT_CONNECTION ClientConnection,
+ IN OUT PSSP_API_MESSAGE Message
+ );
+
+SECURITY_STATUS
+SspApiQueryContextAttributes(
+ IN PSSP_CLIENT_CONNECTION ClientConnection,
+ IN OUT PSSP_API_MESSAGE Message
+ );
+
+SECURITY_STATUS
+SspApiDeleteSecurityContext(
+ IN PSSP_CLIENT_CONNECTION ClientConnection,
+ IN OUT PSSP_API_MESSAGE Message
+ );
+
+SECURITY_STATUS
+SspApiNoop(
+ IN PSSP_CLIENT_CONNECTION ClientConnection,
+ IN OUT PSSP_API_MESSAGE Message
+ );
+
+SECURITY_STATUS
+SspApiNtLmSspControl(
+ IN PSSP_CLIENT_CONNECTION ClientConnection,
+ IN OUT PSSP_API_MESSAGE Message
+ );
+
+SECURITY_STATUS
+SspApiMapContextKeys(
+ IN PSSP_CLIENT_CONNECTION ClientConnection,
+ IN OUT PSSP_API_MESSAGE Message
+ );
+
+//
+// Procedure forwards from lpc.c
+//
+
+NTSTATUS
+SspLpcInitialize(
+ IN PLMSVCS_GLOBAL_DATA LmsvcsGlobalData
+ );
+
+VOID
+SspLpcTerminate(
+ VOID
+ );
+
+SECURITY_STATUS
+SspLpcGetLogonId(
+ IN PSSP_CLIENT_CONNECTION ClientConnection,
+ IN PSSP_API_MESSAGE Message,
+ OUT PLUID LogonId,
+ OUT PHANDLE ClientTokenHandle
+ );
+
+
+SECURITY_STATUS
+SspLpcDuplicateHandle(
+ IN PSSP_CLIENT_CONNECTION ClientConnection,
+ IN BOOLEAN FromClient,
+ IN BOOLEAN CloseSource,
+ IN HANDLE SourceHandle,
+ OUT PHANDLE DestHandle
+ );
+
+
+#endif // ifndef _NTLMSSPS_INCLUDED_
diff --git a/private/net/svcdlls/ntlmssp/server/ntlmssps.rc b/private/net/svcdlls/ntlmssp/server/ntlmssps.rc
new file mode 100644
index 000000000..fa60ff8cf
--- /dev/null
+++ b/private/net/svcdlls/ntlmssp/server/ntlmssps.rc
@@ -0,0 +1,17 @@
+#include <windows.h>
+
+#include <ntverp.h>
+
+#define EXPORT_CONTROLLED
+
+#ifdef EXPORT_BUILD
+#define EXPORT
+#endif
+
+#define VER_FILETYPE VFT_DLL
+#define VER_FILESUBTYPE VFT2_UNKNOWN
+#define VER_FILEDESCRIPTION_STR "NtLm Security Support Provider Service DLL"
+#define VER_INTERNALNAME_STR "NtLmSsps.DLL"
+#define VER_ORIGINALFILENAME_STR "NtLmSsps.DLL"
+
+#include "common.ver"
diff --git a/private/net/svcdlls/ntlmssp/server/sources.inc b/private/net/svcdlls/ntlmssp/server/sources.inc
new file mode 100644
index 000000000..09ec080ac
--- /dev/null
+++ b/private/net/svcdlls/ntlmssp/server/sources.inc
@@ -0,0 +1,74 @@
+!IF 0
+
+Copyright (c) 1989-92 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+Author:
+
+ Steve Wood (stevewo) 12-Apr-1990
+
+NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl
+
+!ENDIF
+
+#
+# The MAJORCOMP and MINORCOMP variables are defined
+# so that $(MAJORCOMP)$(MINORCOMP)filename can be used in
+# cross compiling to provide unique filenames in a flat namespace.
+#
+
+MAJORCOMP=net
+MINORCOMP=ntlmssp
+
+#TARGETNAME=ntlmssps
+TARGETPATH=$(BASEDIR)\public\sdk\lib
+TARGETTYPE=DYNLINK
+TARGETLIBS= \
+ $(BASEDIR)\public\sdk\lib\*\kernel32.lib \
+ $(BASEDIR)\public\sdk\lib\*\advapi32.lib \
+ $(BASEDIR)\public\sdk\lib\*\netlib.lib \
+ $(BASEDIR)\public\sdk\lib\*\netapi32.lib \
+ $(BASEDIR)\public\sdk\lib\*\lsadll.lib \
+ $(BASEDIR)\private\lsa\crypt\engine\obj\*\rc4c.obj \
+# $(BASEDIR)\public\sdk\lib\*\version.lib \
+
+DLLENTRY=SspDllInit
+
+INCLUDES=..;..\..;..\..\..\..\inc;..\..\..\..\..\inc
+
+C_DEFINES=-DSECURITY_WIN32
+
+!IFNDEF DISABLE_NET_UNICODE
+UNICODE=1
+NET_C_DEFINES=-DUNICODE
+!ENDIF
+
+SOURCES= \
+ ..\ntlmssps.rc \
+ ..\api.c \
+ ..\error.c \
+ ..\init.c \
+ ..\lpc.c
+
+USE_CRTDLL=1
+MSC_WARNING_LEVEL=/W3 /WX
+
+UMTYPE=console
+UMAPPL=ssptest
+UMRES=$(@R).res
+UMLIBS= \
+ $(BASEDIR)\public\sdk\lib\*\netapi32.lib \
+ $(BASEDIR)\public\sdk\lib\*\security.lib \
+ $(BASEDIR)\public\sdk\lib\*\ntdll.lib
+
+NTTARGETFILE1=obj\*\ssptest.res
diff --git a/private/net/svcdlls/ntlmssp/server/ssptest.c b/private/net/svcdlls/ntlmssp/server/ssptest.c
new file mode 100644
index 000000000..da7e6c60d
--- /dev/null
+++ b/private/net/svcdlls/ntlmssp/server/ssptest.c
@@ -0,0 +1,1577 @@
+/*--
+
+Copyright (c) 1987-1993 Microsoft Corporation
+
+Module Name:
+
+ ssptest.c
+
+Abstract:
+
+ Test program for the NtLmSsp service.
+
+Author:
+
+ 28-Jun-1993 (cliffv)
+
+Environment:
+
+ User mode only.
+ Contains NT-specific code.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+--*/
+
+
+//
+// Common include files.
+//
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windef.h>
+#include <winbase.h>
+#include <winsvc.h> // Needed for service controller APIs
+#include <lmcons.h>
+#include <lmerr.h>
+#include <lmsname.h>
+#include <rpc.h>
+#include <stdio.h> // printf
+#include <stdlib.h> // strtoul
+#include <tstring.h> // NetpAllocWStrFromWStr
+
+
+#include <security.h> // General definition of a Security Support Provider
+#include <ntmsv1_0.h>
+#include <ntlmsspd.h> // Common definitions between client and server
+#include <ntlmssp.h> // External definition of the NtLmSsp service
+#include <ntlmcomn.h>
+
+BOOLEAN QuietMode = FALSE; // Don't be verbose
+
+
+VOID
+DumpBuffer(
+ PVOID Buffer,
+ DWORD BufferSize
+ )
+/*++
+
+Routine Description:
+
+ Dumps the buffer content on to the debugger output.
+
+Arguments:
+
+ Buffer: buffer pointer.
+
+ BufferSize: size of the buffer.
+
+Return Value:
+
+ none
+
+--*/
+{
+#define NUM_CHARS 16
+
+ DWORD i, limit;
+ CHAR TextBuffer[NUM_CHARS + 1];
+ LPBYTE BufferPtr = Buffer;
+
+
+ printf("------------------------------------\n");
+
+ //
+ // Hex dump of the bytes
+ //
+ limit = ((BufferSize - 1) / NUM_CHARS + 1) * NUM_CHARS;
+
+ for (i = 0; i < limit; i++) {
+
+ if (i < BufferSize) {
+
+ printf("%02x ", BufferPtr[i]);
+
+ if (BufferPtr[i] < 31 ) {
+ TextBuffer[i % NUM_CHARS] = '.';
+ } else if (BufferPtr[i] == '\0') {
+ TextBuffer[i % NUM_CHARS] = ' ';
+ } else {
+ TextBuffer[i % NUM_CHARS] = (CHAR) BufferPtr[i];
+ }
+
+ } else {
+
+ printf(" ");
+ TextBuffer[i % NUM_CHARS] = ' ';
+
+ }
+
+ if ((i + 1) % NUM_CHARS == 0) {
+ TextBuffer[NUM_CHARS] = 0;
+ printf(" %s\n", TextBuffer);
+ }
+
+ }
+
+ printf("------------------------------------\n");
+}
+
+
+VOID
+PrintTime(
+ LPSTR Comment,
+ TimeStamp ConvertTime
+ )
+/*++
+
+Routine Description:
+
+ Print the specified time
+
+Arguments:
+
+ Comment - Comment to print in front of the time
+
+ Time - Local time to print
+
+Return Value:
+
+ None
+
+--*/
+{
+ LARGE_INTEGER LocalTime;
+
+ LocalTime.HighPart = ConvertTime.HighPart;
+ LocalTime.LowPart = ConvertTime.LowPart;
+
+ printf( "%s", Comment );
+
+ //
+ // If the time is infinite,
+ // just say so.
+ //
+
+ if ( LocalTime.HighPart == 0x7FFFFFFF && LocalTime.LowPart == 0xFFFFFFFF ) {
+ printf( "Infinite\n" );
+
+ //
+ // Otherwise print it more clearly
+ //
+
+ } else {
+
+ TIME_FIELDS TimeFields;
+
+ RtlTimeToTimeFields( &LocalTime, &TimeFields );
+
+ printf( "%ld/%ld/%ld %ld:%2.2ld:%2.2ld\n",
+ TimeFields.Month,
+ TimeFields.Day,
+ TimeFields.Year,
+ TimeFields.Hour,
+ TimeFields.Minute,
+ TimeFields.Second );
+ }
+
+}
+
+VOID
+PrintStatus(
+ NET_API_STATUS NetStatus
+ )
+/*++
+
+Routine Description:
+
+ Print a net status code.
+
+Arguments:
+
+ NetStatus - The net status code to print.
+
+Return Value:
+
+ None
+
+--*/
+{
+ printf( "Status = %lu 0x%lx", NetStatus, NetStatus );
+
+ switch (NetStatus) {
+ case NERR_Success:
+ printf( " NERR_Success" );
+ break;
+
+ case NERR_DCNotFound:
+ printf( " NERR_DCNotFound" );
+ break;
+
+ case ERROR_LOGON_FAILURE:
+ printf( " ERROR_LOGON_FAILURE" );
+ break;
+
+ case ERROR_ACCESS_DENIED:
+ printf( " ERROR_ACCESS_DENIED" );
+ break;
+
+ case ERROR_NOT_SUPPORTED:
+ printf( " ERROR_NOT_SUPPORTED" );
+ break;
+
+ case ERROR_NO_LOGON_SERVERS:
+ printf( " ERROR_NO_LOGON_SERVERS" );
+ break;
+
+ case ERROR_NO_SUCH_DOMAIN:
+ printf( " ERROR_NO_SUCH_DOMAIN" );
+ break;
+
+ case ERROR_NO_TRUST_LSA_SECRET:
+ printf( " ERROR_NO_TRUST_LSA_SECRET" );
+ break;
+
+ case ERROR_NO_TRUST_SAM_ACCOUNT:
+ printf( " ERROR_NO_TRUST_SAM_ACCOUNT" );
+ break;
+
+ case ERROR_DOMAIN_TRUST_INCONSISTENT:
+ printf( " ERROR_DOMAIN_TRUST_INCONSISTENT" );
+ break;
+
+ case ERROR_BAD_NETPATH:
+ printf( " ERROR_BAD_NETPATH" );
+ break;
+
+ case ERROR_FILE_NOT_FOUND:
+ printf( " ERROR_FILE_NOT_FOUND" );
+ break;
+
+ case NERR_NetNotStarted:
+ printf( " NERR_NetNotStarted" );
+ break;
+
+ case NERR_WkstaNotStarted:
+ printf( " NERR_WkstaNotStarted" );
+ break;
+
+ case NERR_ServerNotStarted:
+ printf( " NERR_ServerNotStarted" );
+ break;
+
+ case NERR_BrowserNotStarted:
+ printf( " NERR_BrowserNotStarted" );
+ break;
+
+ case NERR_ServiceNotInstalled:
+ printf( " NERR_ServiceNotInstalled" );
+ break;
+
+ case NERR_BadTransactConfig:
+ printf( " NERR_BadTransactConfig" );
+ break;
+
+ case SEC_E_NO_SPM:
+ printf( " SEC_E_NO_SPM" );
+ break;
+ case SEC_E_BAD_PKGID:
+ printf( " SEC_E_BAD_PKGID" ); break;
+ case SEC_E_NOT_OWNER:
+ printf( " SEC_E_NOT_OWNER" ); break;
+ case SEC_E_CANNOT_INSTALL:
+ printf( " SEC_E_CANNOT_INSTALL" ); break;
+ case SEC_E_INVALID_TOKEN:
+ printf( " SEC_E_INVALID_TOKEN" ); break;
+ case SEC_E_CANNOT_PACK:
+ printf( " SEC_E_CANNOT_PACK" ); break;
+ case SEC_E_QOP_NOT_SUPPORTED:
+ printf( " SEC_E_QOP_NOT_SUPPORTED" ); break;
+ case SEC_E_NO_IMPERSONATION:
+ printf( " SEC_E_NO_IMPERSONATION" ); break;
+ case SEC_E_LOGON_DENIED:
+ printf( " SEC_E_LOGON_DENIED" ); break;
+ case SEC_E_UNKNOWN_CREDENTIALS:
+ printf( " SEC_E_UNKNOWN_CREDENTIALS" ); break;
+ case SEC_E_NO_CREDENTIALS:
+ printf( " SEC_E_NO_CREDENTIALS" ); break;
+ case SEC_E_MESSAGE_ALTERED:
+ printf( " SEC_E_MESSAGE_ALTERED" ); break;
+ case SEC_E_OUT_OF_SEQUENCE:
+ printf( " SEC_E_OUT_OF_SEQUENCE" ); break;
+ case SEC_E_INSUFFICIENT_MEMORY:
+ printf( " SEC_E_INSUFFICIENT_MEMORY" ); break;
+ case SEC_E_INVALID_HANDLE:
+ printf( " SEC_E_INVALID_HANDLE" ); break;
+ case SEC_E_NOT_SUPPORTED:
+ printf( " SEC_E_NOT_SUPPORTED" ); break;
+
+ case SEC_I_CALLBACK_NEEDED:
+ printf( " SEC_I_CALLBACK_NEEDED" ); break;
+
+ }
+
+ printf( "\n" );
+}
+
+VOID
+ConfigureServiceRoutine(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ Configure the NtLmSsp Service
+
+Arguments:
+
+ None
+
+Return Value:
+
+ None
+
+--*/
+{
+ SC_HANDLE ScManagerHandle = NULL;
+ SC_HANDLE ServiceHandle = NULL;
+ WCHAR ServiceName[MAX_PATH];
+ DWORD WinStatus;
+
+
+ //
+ // Build the name of the NtLmSsp service.
+ //
+
+ if ( !GetWindowsDirectoryW(
+ ServiceName,
+ sizeof(ServiceName)/sizeof(WCHAR) ) ) {
+ printf( "GetWindowsDirectoryW failed:" );
+ PrintStatus( GetLastError() );
+ goto Cleanup;
+ }
+
+ wcscat( ServiceName, L"\\system32\\services.exe" );
+
+
+ //
+ // Open a handle to the Service Controller
+ //
+
+ ScManagerHandle = OpenSCManager(
+ NULL,
+ NULL,
+ SC_MANAGER_CREATE_SERVICE );
+
+ if (ScManagerHandle == NULL) {
+ printf( "OpenSCManager failed:" );
+ PrintStatus( GetLastError() );
+ goto Cleanup;
+ }
+
+ //
+ // If the service already exists,
+ // delete it and start afresh.
+ //
+
+ ServiceHandle = OpenService(
+ ScManagerHandle,
+ SERVICE_NTLMSSP,
+ DELETE );
+
+ if ( ServiceHandle == NULL ) {
+ WinStatus = GetLastError();
+ if ( WinStatus != ERROR_SERVICE_DOES_NOT_EXIST ) {
+ printf( "OpenService failed:" );
+ PrintStatus( WinStatus );
+ goto Cleanup;
+ }
+ } else {
+
+ if ( !DeleteService( ServiceHandle ) ) {
+ printf( "DeleteService failed:" );
+ PrintStatus( GetLastError() );
+ goto Cleanup;
+ }
+
+ (VOID) CloseServiceHandle(ServiceHandle);
+ }
+
+ //
+ // Create the service
+ //
+
+ ServiceHandle = CreateService(
+ ScManagerHandle,
+ SERVICE_NTLMSSP,
+ L"NT LM Security Support Provider",
+ SERVICE_QUERY_STATUS | SERVICE_QUERY_CONFIG,
+ SERVICE_WIN32_SHARE_PROCESS,
+ SERVICE_DEMAND_START,
+ SERVICE_ERROR_NORMAL,
+ ServiceName,
+ NULL, // No load order group
+ NULL, // No Tag Id required
+ NULL, // No dependencies
+ NULL, // Run as LocalSystem
+ NULL ); // No password
+
+
+
+ if ( ServiceHandle == NULL ) {
+ printf( "CreateService failed:" );
+ PrintStatus( GetLastError() );
+ goto Cleanup;
+ }
+
+
+Cleanup:
+ if ( ScManagerHandle != NULL ) {
+ (VOID) CloseServiceHandle(ScManagerHandle);
+ }
+ if ( ServiceHandle != NULL ) {
+ (VOID) CloseServiceHandle(ServiceHandle);
+ }
+ return;
+
+}
+
+VOID
+TestLpcRoutine(
+ LPWSTR DomainName,
+ LPWSTR UserName,
+ LPWSTR Password
+ )
+/*++
+
+Routine Description:
+
+ Test base LPC functionality
+
+Arguments:
+
+ None
+
+Return Value:
+
+ None
+
+--*/
+{
+ SECURITY_STATUS SecStatus;
+ CredHandle CredentialHandle1;
+ CredHandle CredentialHandle2;
+ CtxtHandle ClientContextHandle;
+ CtxtHandle ServerContextHandle;
+ TimeStamp Lifetime;
+ ULONG ContextAttributes;
+ ULONG PackageCount;
+ PSecPkgInfo PackageInfo = NULL;
+ HANDLE Token = NULL;
+
+ SEC_WINNT_AUTH_IDENTITY AuthIdentity;
+
+ SecBufferDesc NegotiateDesc;
+ SecBuffer NegotiateBuffer;
+
+ SecBufferDesc ChallengeDesc;
+ SecBuffer ChallengeBuffer;
+
+ SecBufferDesc AuthenticateDesc;
+ SecBuffer AuthenticateBuffer;
+
+ SecPkgContext_Sizes ContextSizes;
+ SecPkgContext_Lifespan ContextLifespan;
+ UCHAR ContextNamesBuffer[sizeof(SecPkgContext_Names)+UNLEN*sizeof(WCHAR)];
+ PSecPkgContext_Names ContextNames = (PSecPkgContext_Names) ContextNamesBuffer;
+
+ SecBufferDesc SignMessage;
+ SecBuffer SigBuffers[2];
+ BYTE bDataBuffer[20];
+ BYTE bSigBuffer[100];
+
+ NegotiateBuffer.pvBuffer = NULL;
+ ChallengeBuffer.pvBuffer = NULL;
+ AuthenticateBuffer.pvBuffer = NULL;
+
+ SigBuffers[1].pvBuffer = bSigBuffer;
+ SigBuffers[1].cbBuffer = sizeof(bSigBuffer);
+ SigBuffers[1].BufferType = SECBUFFER_TOKEN;
+
+ SigBuffers[0].pvBuffer = bDataBuffer;
+ SigBuffers[0].cbBuffer = sizeof(bDataBuffer);
+ SigBuffers[0].BufferType = SECBUFFER_DATA;
+ memset(bDataBuffer,0xeb,sizeof(bDataBuffer));
+
+ SignMessage.pBuffers = SigBuffers;
+ SignMessage.cBuffers = 2;
+ SignMessage.ulVersion = 0;
+
+ //
+ // Get info about the security packages.
+ //
+
+ SecStatus = EnumerateSecurityPackages( &PackageCount, &PackageInfo );
+
+ if ( SecStatus != STATUS_SUCCESS ) {
+ printf( "EnumerateSecurityPackages failed:" );
+ PrintStatus( SecStatus );
+ return;
+ }
+
+ if ( !QuietMode ) {
+ printf( "PackageCount: %ld\n", PackageCount );
+ printf( "Name: %ws Comment: %ws\n", PackageInfo->Name, PackageInfo->Comment );
+ printf( "Cap: %ld Version: %ld RPCid: %ld MaxToken: %ld\n\n",
+ PackageInfo->fCapabilities,
+ PackageInfo->wVersion,
+ PackageInfo->wRPCID,
+ PackageInfo->cbMaxToken );
+ }
+
+ //
+ // Get info about the security packages.
+ //
+
+ SecStatus = QuerySecurityPackageInfo( NTLMSP_NAME, &PackageInfo );
+
+ if ( SecStatus != STATUS_SUCCESS ) {
+ printf( "QuerySecurityPackageInfo failed:" );
+ PrintStatus( SecStatus );
+ return;
+ }
+
+ if ( !QuietMode ) {
+ printf( "Name: %ws Comment: %ws\n", PackageInfo->Name, PackageInfo->Comment );
+ printf( "Cap: %ld Version: %ld RPCid: %ld MaxToken: %ld\n\n",
+ PackageInfo->fCapabilities,
+ PackageInfo->wVersion,
+ PackageInfo->wRPCID,
+ PackageInfo->cbMaxToken );
+ }
+
+
+
+ //
+ // Acquire a credential handle for the server side
+ //
+
+ SecStatus = AcquireCredentialsHandle(
+ NULL, // New principal
+ NTLMSP_NAME, // Package Name
+ SECPKG_CRED_INBOUND,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ &CredentialHandle1,
+ &Lifetime );
+
+ if ( SecStatus != STATUS_SUCCESS ) {
+ printf( "AcquireCredentialsHandle failed: ");
+ PrintStatus( SecStatus );
+ return;
+ }
+
+ if ( !QuietMode ) {
+ printf( "CredentialHandle1: 0x%lx 0x%lx ",
+ CredentialHandle1.dwLower, CredentialHandle1.dwUpper );
+ PrintTime( "Lifetime: ", Lifetime );
+ }
+
+
+ //
+ // Acquire a credential handle for the client side
+ //
+
+
+ RtlZeroMemory( &AuthIdentity, sizeof(AuthIdentity) );
+// #define DO_OEM 1
+#ifndef DO_OEM
+ if ( DomainName != NULL ) {
+ AuthIdentity.Domain = DomainName;
+ AuthIdentity.DomainLength = wcslen(DomainName);
+ }
+ if ( UserName != NULL ) {
+ AuthIdentity.User = UserName;
+ AuthIdentity.UserLength = wcslen(UserName);
+ }
+ if ( Password != NULL ) {
+ AuthIdentity.Password = Password;
+ AuthIdentity.PasswordLength = wcslen(Password);
+ }
+ AuthIdentity.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
+#else
+ //
+ // BUGBUG: memory leak here
+ //
+
+ if ( DomainName != NULL ) {
+ AuthIdentity.Domain = (LPWSTR) NetpAllocStrFromWStr(DomainName);
+ AuthIdentity.DomainLength = wcslen(DomainName);
+ }
+ if ( UserName != NULL ) {
+ AuthIdentity.User = (LPWSTR) NetpAllocStrFromWStr(UserName);
+ AuthIdentity.UserLength = wcslen(UserName);
+ }
+ if ( Password != NULL ) {
+ AuthIdentity.Password = (LPWSTR) NetpAllocStrFromWStr(Password);
+ AuthIdentity.PasswordLength = wcslen(Password);
+ }
+ AuthIdentity.Flags = SEC_WINNT_AUTH_IDENTITY_ANSI;
+#endif
+
+ SecStatus = AcquireCredentialsHandle(
+ NULL, // New principal
+ NTLMSP_NAME, // Package Name
+ SECPKG_CRED_OUTBOUND,
+ NULL,
+ (DomainName == NULL && UserName == NULL && Password == NULL) ?
+ NULL : &AuthIdentity,
+ NULL,
+ NULL,
+ &CredentialHandle2,
+ &Lifetime );
+
+ if ( SecStatus != STATUS_SUCCESS ) {
+ printf( "AcquireCredentialsHandle failed: " );
+ PrintStatus( SecStatus );
+ return;
+ }
+
+
+ if ( !QuietMode ) {
+ printf( "CredentialHandle2: 0x%lx 0x%lx ",
+ CredentialHandle2.dwLower, CredentialHandle2.dwUpper );
+ PrintTime( "Lifetime: ", Lifetime );
+ }
+
+
+
+ //
+ // Get the NegotiateMessage (ClientSide)
+ //
+
+ NegotiateDesc.ulVersion = 0;
+ NegotiateDesc.cBuffers = 1;
+ NegotiateDesc.pBuffers = &NegotiateBuffer;
+
+ NegotiateBuffer.cbBuffer = PackageInfo->cbMaxToken;
+ NegotiateBuffer.BufferType = SECBUFFER_TOKEN;
+ NegotiateBuffer.pvBuffer = LocalAlloc( 0, NegotiateBuffer.cbBuffer );
+ if ( NegotiateBuffer.pvBuffer == NULL ) {
+ printf( "Allocate NegotiateMessage failed: 0x%ld\n", GetLastError() );
+ return;
+ }
+
+ SecStatus = InitializeSecurityContext(
+ &CredentialHandle2,
+ NULL, // No Client context yet
+ L"\\\\Frank\\IPC$", // Faked target name
+ ISC_REQ_SEQUENCE_DETECT,
+ 0, // Reserved 1
+ SECURITY_NATIVE_DREP,
+ NULL, // No initial input token
+ 0, // Reserved 2
+ &ClientContextHandle,
+ &NegotiateDesc,
+ &ContextAttributes,
+ &Lifetime );
+
+ if ( SecStatus != STATUS_SUCCESS ) {
+ if ( !QuietMode || !NT_SUCCESS(SecStatus) ) {
+ printf( "InitializeSecurityContext (negotiate): " );
+ PrintStatus( SecStatus );
+ }
+ if ( !NT_SUCCESS(SecStatus) ) {
+ return;
+ }
+ }
+
+ if ( !QuietMode ) {
+ printf( "\n\nNegotiate Message:\n" );
+
+ printf( "ClientContextHandle: 0x%lx 0x%lx Attributes: 0x%lx ",
+ ClientContextHandle.dwLower, ClientContextHandle.dwUpper,
+ ContextAttributes );
+ PrintTime( "Lifetime: ", Lifetime );
+
+ DumpBuffer( NegotiateBuffer.pvBuffer, NegotiateBuffer.cbBuffer );
+ }
+
+
+
+#if 0
+
+
+
+ //
+ // Query as many attributes as possible
+ //
+
+
+ SecStatus = QueryContextAttributes(
+ &ClientContextHandle,
+ SECPKG_ATTR_SIZES,
+ &ContextSizes );
+
+ if ( SecStatus != STATUS_SUCCESS ) {
+ printf( "QueryContextAttributes (sizes): " );
+ PrintStatus( SecStatus );
+ if ( !NT_SUCCESS(SecStatus) ) {
+ return;
+ }
+ }
+
+ if ( !QuietMode ) {
+ printf( "QuerySizes: %ld %ld %ld %ld\n",
+ ContextSizes.cbMaxToken,
+ ContextSizes.cbMaxSignature,
+ ContextSizes.cbBlockSize,
+ ContextSizes.cbSecurityTrailer );
+ }
+
+ SecStatus = QueryContextAttributes(
+ &ClientContextHandle,
+ SECPKG_ATTR_NAMES,
+ ContextNamesBuffer );
+
+ if ( SecStatus != STATUS_SUCCESS ) {
+ printf( "QueryContextAttributes (names): " );
+ PrintStatus( SecStatus );
+ if ( !NT_SUCCESS(SecStatus) ) {
+ return;
+ }
+ }
+
+ if ( !QuietMode ) {
+ printf( "QueryNames: %ws\n", ContextNames->sUserName );
+ }
+
+
+ SecStatus = QueryContextAttributes(
+ &ClientContextHandle,
+ SECPKG_ATTR_LIFESPAN,
+ &ContextLifespan );
+
+ if ( SecStatus != STATUS_SUCCESS ) {
+ printf( "QueryContextAttributes (lifespan): " );
+ PrintStatus( SecStatus );
+ if ( !NT_SUCCESS(SecStatus) ) {
+ return;
+ }
+ }
+
+ if ( !QuietMode ) {
+ PrintTime(" Start:", ContextLifespan.tsStart );
+ PrintTime(" Expiry:", ContextLifespan.tsExpiry );
+ }
+
+#endif
+
+
+
+ //
+ // Get the ChallengeMessage (ServerSide)
+ //
+
+ NegotiateBuffer.BufferType |= SECBUFFER_READONLY;
+ ChallengeDesc.ulVersion = 0;
+ ChallengeDesc.cBuffers = 1;
+ ChallengeDesc.pBuffers = &ChallengeBuffer;
+
+ ChallengeBuffer.cbBuffer = PackageInfo->cbMaxToken;
+ ChallengeBuffer.BufferType = SECBUFFER_TOKEN;
+ ChallengeBuffer.pvBuffer = LocalAlloc( 0, ChallengeBuffer.cbBuffer );
+ if ( ChallengeBuffer.pvBuffer == NULL ) {
+ printf( "Allocate ChallengeMessage failed: 0x%ld\n", GetLastError() );
+ return;
+ }
+
+ SecStatus = AcceptSecurityContext(
+ &CredentialHandle1,
+ NULL, // No Server context yet
+ &NegotiateDesc,
+ ISC_REQ_SEQUENCE_DETECT,
+ SECURITY_NATIVE_DREP,
+ &ServerContextHandle,
+ &ChallengeDesc,
+ &ContextAttributes,
+ &Lifetime );
+
+ if ( SecStatus != STATUS_SUCCESS ) {
+ if ( !QuietMode || !NT_SUCCESS(SecStatus) ) {
+ printf( "AcceptSecurityContext (Challenge): " );
+ PrintStatus( SecStatus );
+ }
+ if ( !NT_SUCCESS(SecStatus) ) {
+ return;
+ }
+ }
+
+ if ( !QuietMode ) {
+ printf( "\n\nChallenge Message:\n" );
+
+ printf( "ServerContextHandle: 0x%lx 0x%lx Attributes: 0x%lx ",
+ ServerContextHandle.dwLower, ServerContextHandle.dwUpper,
+ ContextAttributes );
+ PrintTime( "Lifetime: ", Lifetime );
+
+ DumpBuffer( ChallengeBuffer.pvBuffer, ChallengeBuffer.cbBuffer );
+ }
+
+
+
+
+
+
+ //
+ // Get the AuthenticateMessage (ClientSide)
+ //
+
+ ChallengeBuffer.BufferType |= SECBUFFER_READONLY;
+ AuthenticateDesc.ulVersion = 0;
+ AuthenticateDesc.cBuffers = 1;
+ AuthenticateDesc.pBuffers = &AuthenticateBuffer;
+
+ AuthenticateBuffer.cbBuffer = PackageInfo->cbMaxToken;
+ AuthenticateBuffer.BufferType = SECBUFFER_TOKEN;
+ AuthenticateBuffer.pvBuffer = LocalAlloc( 0, AuthenticateBuffer.cbBuffer );
+ if ( AuthenticateBuffer.pvBuffer == NULL ) {
+ printf( "Allocate AuthenticateMessage failed: 0x%ld\n", GetLastError() );
+ return;
+ }
+
+ SecStatus = InitializeSecurityContext(
+ NULL,
+ &ClientContextHandle,
+ L"\\\\Frank\\IPC$", // Faked target name
+ 0,
+ 0, // Reserved 1
+ SECURITY_NATIVE_DREP,
+ &ChallengeDesc,
+ 0, // Reserved 2
+ &ClientContextHandle,
+ &AuthenticateDesc,
+ &ContextAttributes,
+ &Lifetime );
+
+ if ( SecStatus != STATUS_SUCCESS ) {
+ printf( "InitializeSecurityContext (Authenticate): " );
+ PrintStatus( SecStatus );
+ if ( !NT_SUCCESS(SecStatus) ) {
+ return;
+ }
+ }
+
+ if ( !QuietMode ) {
+ printf( "\n\nAuthenticate Message:\n" );
+
+ printf( "ClientContextHandle: 0x%lx 0x%lx Attributes: 0x%lx ",
+ ClientContextHandle.dwLower, ClientContextHandle.dwUpper,
+ ContextAttributes );
+ PrintTime( "Lifetime: ", Lifetime );
+
+ DumpBuffer( AuthenticateBuffer.pvBuffer, AuthenticateBuffer.cbBuffer );
+ }
+
+
+
+ //
+ // Finally authenticate the user (ServerSide)
+ //
+
+ AuthenticateBuffer.BufferType |= SECBUFFER_READONLY;
+
+ SecStatus = AcceptSecurityContext(
+ NULL,
+ &ServerContextHandle,
+ &AuthenticateDesc,
+ 0,
+ SECURITY_NATIVE_DREP,
+ &ServerContextHandle,
+ NULL,
+ &ContextAttributes,
+ &Lifetime );
+
+ if (STATUS_SUCCESS == QueryContextAttributes(
+ &ClientContextHandle,
+ SECPKG_ATTR_NAMES,
+ ContextNamesBuffer )) {
+ printf( "QueryNames: %ws\n", ContextNames->sUserName );
+
+ }
+
+
+ if ( SecStatus != STATUS_SUCCESS ) {
+ printf( "AcceptSecurityContext (Challenge): " );
+ PrintStatus( SecStatus );
+ if ( !NT_SUCCESS(SecStatus) ) {
+ return;
+ }
+ }
+
+ if ( !QuietMode ) {
+ printf( "\n\nFinal Authentication:\n" );
+
+ printf( "ServerContextHandle: 0x%lx 0x%lx Attributes: 0x%lx ",
+ ServerContextHandle.dwLower, ServerContextHandle.dwUpper,
+ ContextAttributes );
+ PrintTime( "Lifetime: ", Lifetime );
+ printf(" \n" );
+ }
+
+
+ //
+ // Now make a third call to Initialize to check that RPC can
+ // reauthenticate.
+ //
+
+ AuthenticateBuffer.BufferType = SECBUFFER_TOKEN;
+
+
+ SecStatus = InitializeSecurityContext(
+ NULL,
+ &ClientContextHandle,
+ L"\\\\Frank\\IPC$", // Faked target name
+ 0,
+ 0, // Reserved 1
+ SECURITY_NATIVE_DREP,
+ NULL,
+ 0, // Reserved 2
+ &ClientContextHandle,
+ &AuthenticateDesc,
+ &ContextAttributes,
+ &Lifetime );
+
+ if ( SecStatus != STATUS_SUCCESS ) {
+ printf( "InitializeSecurityContext (Re-Authenticate): " );
+ PrintStatus( SecStatus );
+ if ( !NT_SUCCESS(SecStatus) ) {
+ return;
+ }
+ }
+
+
+
+ //
+ // Now try to re-authenticate the user (ServerSide)
+ //
+
+ AuthenticateBuffer.BufferType |= SECBUFFER_READONLY;
+
+ SecStatus = AcceptSecurityContext(
+ NULL,
+ &ServerContextHandle,
+ &AuthenticateDesc,
+ 0,
+ SECURITY_NATIVE_DREP,
+ &ServerContextHandle,
+ NULL,
+ &ContextAttributes,
+ &Lifetime );
+
+ if ( SecStatus != STATUS_SUCCESS ) {
+ printf( "AcceptSecurityContext (Re-authenticate): " );
+ PrintStatus( SecStatus );
+ if ( !NT_SUCCESS(SecStatus) ) {
+ return;
+ }
+ }
+
+
+ //
+ // Impersonate the client (ServerSide)
+ //
+
+ SecStatus = ImpersonateSecurityContext( &ServerContextHandle );
+
+ if ( SecStatus != STATUS_SUCCESS ) {
+ printf( "ImpersonateSecurityContext: " );
+ PrintStatus( SecStatus );
+ if ( !NT_SUCCESS(SecStatus) ) {
+ return;
+ }
+ }
+
+ //
+ // Do something while impersonating (Access the token)
+ //
+
+ {
+ NTSTATUS Status;
+ HANDLE TokenHandle = NULL;
+
+ //
+ // Open the token,
+ //
+
+ Status = NtOpenThreadToken(
+ NtCurrentThread(),
+ TOKEN_QUERY,
+ (BOOLEAN) TRUE, // Not really using the impersonation token
+ &TokenHandle );
+
+ if ( !NT_SUCCESS(Status) ) {
+ printf( "Access Thread token while impersonating: " );
+ PrintStatus( Status );
+ return;
+ } else {
+ (VOID) NtClose( TokenHandle );
+ }
+ }
+
+
+ //
+ // RevertToSelf (ServerSide)
+ //
+
+ SecStatus = RevertSecurityContext( &ServerContextHandle );
+
+ if ( SecStatus != STATUS_SUCCESS ) {
+ printf( "RevertSecurityContext: " );
+ PrintStatus( SecStatus );
+ if ( !NT_SUCCESS(SecStatus) ) {
+ return;
+ }
+ }
+
+
+ //
+ // Impersonate the client manually
+ //
+
+ SecStatus = QuerySecurityContextToken( &ServerContextHandle,&Token );
+
+ if ( SecStatus != STATUS_SUCCESS ) {
+ printf( "QuerySecurityContextToken: " );
+ PrintStatus( SecStatus );
+ if ( !NT_SUCCESS(SecStatus) ) {
+ return;
+ }
+ }
+
+ if (!ImpersonateLoggedOnUser(Token))
+ {
+ printf("Impersonate logged on user failed: %d\n",GetLastError());
+ return;
+ }
+ //
+ // Do something while impersonating (Access the token)
+ //
+
+ {
+ NTSTATUS Status;
+ HANDLE TokenHandle = NULL;
+ WCHAR UserName[100];
+ ULONG NameLength = 100;
+
+ //
+ // Open the token,
+ //
+
+ Status = NtOpenThreadToken(
+ NtCurrentThread(),
+ TOKEN_QUERY,
+ (BOOLEAN) TRUE, // Not really using the impersonation token
+ &TokenHandle );
+
+ if ( !NT_SUCCESS(Status) ) {
+ printf( "Access Thread token while impersonating: " );
+ PrintStatus( Status );
+ return;
+ } else {
+ (VOID) NtClose( TokenHandle );
+ }
+ if (!GetUserName(UserName, &NameLength))
+ {
+ printf("Failed to get username: %d\n",GetLastError());
+ return;
+ }
+ else
+ {
+ printf("Username = %ws\n",UserName);
+ }
+ }
+
+
+ //
+ // RevertToSelf (ServerSide)
+ //
+
+ if (!RevertToSelf())
+ {
+ printf( "RevertToSelf failed: %d\n ",GetLastError() );
+ return;
+ }
+ CloseHandle(Token);
+
+ //
+ // Check password expiry
+ //
+#ifdef notdef
+ SecStatus = SspQueryPasswordExpiry(
+ &ServerContextHandle,
+ &Lifetime
+ );
+ if (!NT_SUCCESS(SecStatus))
+ {
+ printf("Failed to query password expiry: 0x%x\n",SecStatus);
+ }
+ else
+ {
+ TIME_FIELDS TimeFields;
+
+ RtlTimeToTimeFields(
+ &Lifetime,
+ &TimeFields
+ );
+ printf("Password expires %d-%d-%d %d:%d:%d\n",
+ TimeFields.Day,
+ TimeFields.Month,
+ TimeFields.Year,
+ TimeFields.Hour,
+ TimeFields.Minute,
+ TimeFields.Second
+ );
+ }
+#endif
+ //
+ // Sign a message
+ //
+
+ SecStatus = MakeSignature(
+ &ClientContextHandle,
+ 0,
+ &SignMessage,
+ 0 );
+
+ if ( SecStatus != STATUS_SUCCESS ) {
+ printf( "MakeSignature: " );
+ PrintStatus( SecStatus );
+ if ( !NT_SUCCESS(SecStatus) ) {
+ return;
+ }
+ }
+
+ if ( !QuietMode ) {
+
+ printf("\n Signature: \n");
+ DumpBuffer(SigBuffers[1].pvBuffer,SigBuffers[1].cbBuffer);
+
+ }
+
+
+ //
+ // Verify the signature
+ //
+
+ SecStatus = VerifySignature(
+ &ServerContextHandle,
+ &SignMessage,
+ 0,
+ 0 );
+
+ if ( SecStatus != STATUS_SUCCESS ) {
+ printf( "VerifySignature: " );
+ PrintStatus( SecStatus );
+ if ( !NT_SUCCESS(SecStatus) ) {
+ return;
+ }
+ }
+
+
+
+ //
+ // Sign a message, this time to check if it can detect a change in the
+ // message
+ //
+
+ SecStatus = MakeSignature(
+ &ClientContextHandle,
+ 0,
+ &SignMessage,
+ 0 );
+
+ if ( SecStatus != STATUS_SUCCESS ) {
+ printf( "MakeSignature: " );
+ PrintStatus( SecStatus );
+ if ( !NT_SUCCESS(SecStatus) ) {
+ return;
+ }
+ }
+
+ if ( !QuietMode ) {
+
+ printf("\n Signature: \n");
+ DumpBuffer(SigBuffers[1].pvBuffer,SigBuffers[1].cbBuffer);
+
+ }
+
+ //
+ // Mess up the message to see if VerifySignature works
+ //
+
+ bDataBuffer[10] = 0xec;
+
+ //
+ // Verify the signature
+ //
+
+ SecStatus = VerifySignature(
+ &ServerContextHandle,
+ &SignMessage,
+ 0,
+ 0 );
+
+ if ( SecStatus != SEC_E_MESSAGE_ALTERED ) {
+ printf( "VerifySignature: " );
+ PrintStatus( SecStatus );
+ if ( !NT_SUCCESS(SecStatus) ) {
+ return;
+ }
+ }
+
+ //
+ // Delete both contexts.
+ //
+
+
+ SecStatus = DeleteSecurityContext( &ClientContextHandle );
+
+ if ( SecStatus != STATUS_SUCCESS ) {
+ printf( "DeleteSecurityContext failed: " );
+ PrintStatus( SecStatus );
+ return;
+ }
+
+ SecStatus = DeleteSecurityContext( &ServerContextHandle );
+
+ if ( SecStatus != STATUS_SUCCESS ) {
+ printf( "DeleteSecurityContext failed: " );
+ PrintStatus( SecStatus );
+ return;
+ }
+
+
+
+ //
+ // Free both credential handles
+ //
+
+ SecStatus = FreeCredentialsHandle( &CredentialHandle1 );
+
+ if ( SecStatus != STATUS_SUCCESS ) {
+ printf( "FreeCredentialsHandle failed: " );
+ PrintStatus( SecStatus );
+ return;
+ }
+
+ SecStatus = FreeCredentialsHandle( &CredentialHandle2 );
+
+ if ( SecStatus != STATUS_SUCCESS ) {
+ printf( "FreeCredentialsHandle failed: " );
+ PrintStatus( SecStatus );
+ return;
+ }
+
+
+ //
+ // Final Cleanup
+ //
+
+ if ( NegotiateBuffer.pvBuffer != NULL ) {
+ (VOID) LocalFree( NegotiateBuffer.pvBuffer );
+ }
+
+ if ( ChallengeBuffer.pvBuffer != NULL ) {
+ (VOID) LocalFree( ChallengeBuffer.pvBuffer );
+ }
+
+ if ( AuthenticateBuffer.pvBuffer != NULL ) {
+ (VOID) LocalFree( AuthenticateBuffer.pvBuffer );
+ }
+}
+
+#if DBG
+VOID
+ControlRoutine(
+ ULONG FunctionCode,
+ ULONG Data
+ )
+/*++
+
+Routine Description:
+
+ Control the NtLmSsp Service
+
+Arguments:
+
+ FunctionCode - Function code to pass to service
+
+ Data - Data to pass to service
+
+Return Value:
+
+ None
+
+--*/
+{
+ SECURITY_STATUS SecStatus;
+
+// SecStatus = NtLmSspControl( FunctionCode, Data );
+//
+// if ( SecStatus != STATUS_SUCCESS ) {
+// printf( "NtLmSspControl failed: " );
+// PrintStatus( SecStatus );
+// return;
+// }
+
+ UNREFERENCED_PARAMETER(FunctionCode);
+ UNREFERENCED_PARAMETER(Data);
+ return;
+}
+#endif // DBG
+
+
+
+int _CRTAPI1
+main(
+ IN int argc,
+ IN char ** argv
+ )
+/*++
+
+Routine Description:
+
+ Drive the NtLmSsp service
+
+Arguments:
+
+ argc - the number of command-line arguments.
+
+ argv - an array of pointers to the arguments.
+
+Return Value:
+
+ Exit status
+
+--*/
+{
+ LPSTR argument;
+ int i;
+ ULONG j;
+ ULONG Iterations;
+
+ LPWSTR DomainName = NULL;
+ LPWSTR UserName = NULL;
+ LPWSTR Password = NULL;
+
+#if DBG
+ ULONG FunctionCode;
+ ULONG Data;
+#endif // DBG
+
+ enum {
+ NoAction,
+ ConfigureService,
+#define CONFIG_PARAM "/ConfigureService"
+ TestLpc,
+#define TESTLPC_PARAM "/TestLpc"
+#define TESTLPC2_PARAM "/TestLpc:"
+#if DBG
+ Control,
+#define DBFLAG_PARAM "/Dbflag:"
+#define TRUNC_PARAM "/Trunc"
+#define BP_PARAM "/BP"
+#endif // DBG
+ } Action = NoAction;
+#define QUIET_PARAM "/Q"
+
+
+
+
+
+ //
+ // Loop through the arguments handle each in turn
+ //
+
+ for ( i=1; i<argc; i++ ) {
+
+ argument = argv[i];
+
+
+ //
+ // Handle /ConfigureService
+ //
+
+ if ( _stricmp( argument, CONFIG_PARAM ) == 0 ) {
+ if ( Action != NoAction ) {
+ goto Usage;
+ }
+
+ Action = ConfigureService;
+
+
+ //
+ // Handle /TestLpc
+ //
+
+ } else if ( _stricmp( argument, TESTLPC_PARAM ) == 0 ) {
+ if ( Action != NoAction ) {
+ goto Usage;
+ }
+
+ Action = TestLpc;
+ Iterations = 1;
+
+ //
+ // Handle /TestLpc:
+ //
+
+ } else if ( _strnicmp( argument,
+ TESTLPC2_PARAM,
+ sizeof(TESTLPC2_PARAM)-1 ) == 0 ){
+ char *end;
+ if ( Action != NoAction ) {
+ goto Usage;
+ }
+
+ Action = TestLpc;
+
+ Iterations = strtoul( &argument[sizeof(TESTLPC2_PARAM)-1], &end, 10 );
+
+ i++;
+ if ( i < argc ) {
+ argument = argv[i];
+ DomainName = NetpAllocWStrFromStr( argument );
+
+ i++;
+ if ( i < argc ) {
+ argument = argv[i];
+ UserName = NetpAllocWStrFromStr( argument );
+
+ i++;
+ if ( i < argc ) {
+ argument = argv[i];
+ Password = NetpAllocWStrFromStr( argument );
+ }
+ }
+ }
+
+
+#if DBG
+ //
+ // Handle /Dbflag
+ //
+
+ } else if (_strnicmp(argument,
+ DBFLAG_PARAM,
+ sizeof(DBFLAG_PARAM)-1 ) == 0 ){
+ char *end;
+ if ( Action != NoAction ) {
+ goto Usage;
+ }
+
+ Action = Control;
+
+ FunctionCode = NTLMSSP_DBFLAG;
+ Data = strtoul( &argument[sizeof(DBFLAG_PARAM)-1], &end, 16 );
+
+
+ //
+ // Handle /BP
+ //
+
+ } else if ( _stricmp( argument, BP_PARAM ) == 0 ) {
+ if ( Action != NoAction ) {
+ goto Usage;
+ }
+
+ Action = Control;
+
+ FunctionCode = NTLMSSP_BREAKPOINT;
+ Data = 0;
+
+
+ //
+ // Handle /TRUNC
+ //
+
+ } else if ( _stricmp( argument, TRUNC_PARAM ) == 0 ) {
+ if ( Action != NoAction ) {
+ goto Usage;
+ }
+
+ Action = Control;
+
+ FunctionCode = NTLMSSP_TRUNCATE;
+ Data = 0;
+
+
+ //
+ // Handle /Quiet
+ //
+
+ } else if ( _stricmp( argument, QUIET_PARAM ) == 0 ) {
+ QuietMode = TRUE;
+
+#endif // DBG
+
+
+ //
+ // Handle all other parameters
+ //
+
+ } else {
+Usage:
+ fprintf( stderr, "Usage: ssptest [/OPTIONS]\n\n" );
+
+ fprintf(
+ stderr,
+ "\n"
+ " " CONFIG_PARAM " - Configure NtLmSsp to run on this machine\n"
+ " " TESTLPC_PARAM "[:<iterations> <DomainName> <UserName> <Password>] - Test basic LPC to NtLmSsp service.\n"
+ " " QUIET_PARAM " - Don't be so verbose\n"
+ "\n"
+#if DBG
+ " " DBFLAG_PARAM "<HexFlags> - Set Dbflag in NtLmSsp service.\n"
+ " " TRUNC_PARAM " - Truncate log file in NtLmSsp service.\n"
+ " " BP_PARAM " - Breakpoint in NtLmSsp service.\n"
+#endif // DBG
+ "\n" );
+ return(1);
+ }
+ }
+
+ //
+ // Perform the action requested
+ //
+
+ switch ( Action ) {
+ case ConfigureService:
+ ConfigureServiceRoutine();
+ break;
+
+ case TestLpc: {
+ for ( j=0; j<Iterations ; j++ ) {
+ TestLpcRoutine( DomainName, UserName, Password );
+ }
+ break;
+ }
+#if DBG
+ case Control:
+ ControlRoutine( FunctionCode, Data );
+ break;
+#endif // DBG
+ }
+
+ return 0;
+
+}
+
diff --git a/private/net/svcdlls/ntlmssp/server/ssptest.rc b/private/net/svcdlls/ntlmssp/server/ssptest.rc
new file mode 100644
index 000000000..5b4b59079
--- /dev/null
+++ b/private/net/svcdlls/ntlmssp/server/ssptest.rc
@@ -0,0 +1,12 @@
+#include <windows.h>
+#include <ntverp.h>
+
+#define VER_FILETYPE VFT_APP
+#define VER_FILESUBTYPE VFT2_UNKNOWN
+#define VER_FILEDESCRIPTION_STR "Microsoft\256 Security Package Test Utility"
+
+#define VER_INTERNALNAME_STR "ssptest.exe"
+#define VER_ORIGINALFILENAME_STR "ssptest.exe"
+
+#include <common.ver>
+
diff --git a/private/net/svcdlls/nwsap/client/advapi.c b/private/net/svcdlls/nwsap/client/advapi.c
new file mode 100644
index 000000000..4420ef92e
--- /dev/null
+++ b/private/net/svcdlls/nwsap/client/advapi.c
@@ -0,0 +1,184 @@
+/*++
+
+Copyright (c) 1994 Microsoft Corporation
+Copyright (c) 1993 Micro Computer Systems, Inc.
+
+Module Name:
+
+ net\svcdlls\nwsap\client\advapi.c
+
+Abstract:
+
+ This routine handles the Advertise API for the SAP Agent
+
+Author:
+
+ Brian Walker (MCS) 06-15-1993
+
+Revision History:
+
+--*/
+
+#include "precomp.h"
+#pragma hdrstop
+
+
+/*++
+*******************************************************************
+ S a p A d d A d v e r t i s e
+
+Routine Description:
+
+ This routine adds an entry to the list of servers
+ that we advertise.
+
+Arguments:
+ ServerName = Ptr to AsciiZ server name
+ ServerType = USHORT of object type to add
+ ServerAddr = Ptr to 12 byte aerver address
+ RespondNearest = TRUE = Use me for respond nearest call
+ FALSE = Don't use me for respond nearest call
+
+Return Value:
+
+ SAPRETURN_SUCCESS - Added OK
+ SAPRETURN_NOMEMORY - Error allocating memory
+ SAPRETURN_EXISTS - Already exists in list
+ SAPRETURN_NOTINIT - SAP Agent is not running
+*******************************************************************
+--*/
+
+INT
+SapAddAdvertise(
+ IN PUCHAR ServerName,
+ IN USHORT ServerType,
+ IN PUCHAR ServerAddr,
+ IN BOOL RespondNearest)
+{
+ NTSTATUS status;
+ NWSAP_REQUEST_MESSAGE request;
+ NWSAP_REPLY_MESSAGE reply;
+
+ /** If not running - return error **/
+
+ if (!SapLibInitialized)
+ return SAPRETURN_NOTINIT;
+
+ /** Make sure name is not too long **/
+
+ if (strlen(ServerName) > NWSAP_MAXNAME_LENGTH) {
+ return SAPRETURN_INVALIDNAME;
+ }
+
+ /** Build the Add Advertise message **/
+
+ request.MessageType = NWSAP_LPCMSG_ADDADVERTISE;
+ request.PortMessage.u1.s1.DataLength = (USHORT)(sizeof(request) - sizeof(PORT_MESSAGE));
+ request.PortMessage.u1.s1.TotalLength = sizeof(request);
+ request.PortMessage.u2.ZeroInit = 0;
+
+ memset(request.Message.AdvApi.ServerName, 0, NWSAP_MAXNAME_LENGTH+1);
+ strcpy(request.Message.AdvApi.ServerName, ServerName);
+ memcpy(request.Message.AdvApi.ServerAddr, ServerAddr, 12);
+ request.Message.AdvApi.ServerType = ServerType;
+ request.Message.AdvApi.RespondNearest = RespondNearest;
+
+ /** Send it and get a response **/
+
+ status = NtRequestWaitReplyPort(
+ SapXsPortHandle,
+ (PPORT_MESSAGE)&request,
+ (PPORT_MESSAGE)&reply);
+
+ if (!NT_SUCCESS(status)) {
+ return status;
+ }
+
+ /** If we got a SAP error - return it **/
+
+ if (reply.Error)
+ return reply.Error;
+
+ /** Return the entry **/
+
+ memcpy(ServerAddr, reply.Message.AdvApi.ServerAddr, 12);
+
+ /** All Done OK **/
+
+ return SAPRETURN_SUCCESS;
+}
+
+
+/*++
+*******************************************************************
+ S a p R e m o v e A d v e r t i s e
+
+Routine Description:
+
+ This routine removes an entry to the list of servers
+ that we advertise.
+
+Arguments:
+ ServerName = Ptr to AsciiZ server name
+ ServerType = USHORT of object type to remove
+
+Return Value:
+
+ SAPRETURN_SUCCESS - Added OK
+ SAPRETURN_NOTEXIST - Entry does not exist in list
+ SAPRETURN_NOTINIT - SAP Agent is not running
+*******************************************************************
+--*/
+
+INT
+SapRemoveAdvertise(
+ IN PUCHAR ServerName,
+ IN USHORT ServerType)
+{
+ NTSTATUS status;
+ NWSAP_REQUEST_MESSAGE request;
+ NWSAP_REPLY_MESSAGE reply;
+
+ /** If not running - return error **/
+
+ if (!SapLibInitialized)
+ return SAPRETURN_NOTINIT;
+
+ /** Make sure name is not too long **/
+
+ if (strlen(ServerName) > NWSAP_MAXNAME_LENGTH) {
+ return SAPRETURN_INVALIDNAME;
+ }
+
+ /** Build the Add Advertise message **/
+
+ request.MessageType = NWSAP_LPCMSG_REMOVEADVERTISE;
+ request.PortMessage.u1.s1.DataLength = (USHORT)(sizeof(request) - sizeof(PORT_MESSAGE));
+ request.PortMessage.u1.s1.TotalLength = sizeof(request);
+ request.PortMessage.u2.ZeroInit = 0;
+
+ memset(request.Message.AdvApi.ServerName, 0, NWSAP_MAXNAME_LENGTH+1);
+ strcpy(request.Message.AdvApi.ServerName, ServerName);
+ request.Message.AdvApi.ServerType = ServerType;
+
+ /** Send it and get a response **/
+
+ status = NtRequestWaitReplyPort(
+ SapXsPortHandle,
+ (PPORT_MESSAGE)&request,
+ (PPORT_MESSAGE)&reply);
+
+ if (!NT_SUCCESS(status)) {
+ return status;
+ }
+
+ /** If we got a SAP error - return it **/
+
+ if (reply.Error)
+ return reply.Error;
+
+ /** All Done OK **/
+
+ return SAPRETURN_SUCCESS;
+}
+
diff --git a/private/net/svcdlls/nwsap/client/bindlib.c b/private/net/svcdlls/nwsap/client/bindlib.c
new file mode 100644
index 000000000..4fdd1f5ff
--- /dev/null
+++ b/private/net/svcdlls/nwsap/client/bindlib.c
@@ -0,0 +1,267 @@
+/*++
+
+Copyright (c) 1994 Microsoft Corporation
+Copyright (c) 1993 Micro Computer Systems, Inc.
+
+Module Name:
+
+ net\svcdlls\nwsap\client\bindlib.c
+
+Abstract:
+
+ This routine handles the BindLib API for the SAP Agent
+
+Author:
+
+ Brian Walker (MCS) 06-15-1993
+
+Revision History:
+
+--*/
+
+#include "precomp.h"
+#pragma hdrstop
+
+
+/*++
+*******************************************************************
+ S a p G e t O b j e c t N a m e
+
+Routine Description:
+
+ This routine converts an Object ID into an Object Name
+ and Type.
+
+Arguments:
+ ObjectID = Object ID to convert
+ ObjectName = Ptr to where to store 48 byte object name
+ ObjectType = Ptr to where to store the object type
+ ObjectAddr = Ptr to where to store NET_ADDRESS (12 bytes)
+
+ ObjectName, ObjectType, ObjectAddr can be NULL.
+
+Return Value:
+
+ SAPRETURN_SUCCESS = OK - name and type are filled in
+ SAPRETURN_NOTEXIST = Invalid object id.
+*******************************************************************
+--*/
+
+INT
+SapGetObjectName(
+ IN ULONG ObjectID,
+ IN PUCHAR ObjectName,
+ IN PUSHORT ObjectType,
+ IN PUCHAR ObjectAddr)
+{
+ NTSTATUS status;
+ NWSAP_REQUEST_MESSAGE request;
+ NWSAP_REPLY_MESSAGE reply;
+
+ /** If not initialized - return error **/
+
+ if (!SapLibInitialized)
+ return SAPRETURN_NOTINIT;
+
+ /** Build the Get Object Name message **/
+
+ request.MessageType = NWSAP_LPCMSG_GETOBJECTNAME;
+ request.PortMessage.u1.s1.DataLength = (USHORT)(sizeof(request) - sizeof(PORT_MESSAGE));
+ request.PortMessage.u1.s1.TotalLength = sizeof(request);
+ request.PortMessage.u2.ZeroInit = 0;
+ request.PortMessage.MessageId = 0;
+
+ request.Message.BindLibApi.ObjectID = ObjectID;
+
+ /** Send it and get a response **/
+
+ status = NtRequestWaitReplyPort(
+ SapXsPortHandle,
+ (PPORT_MESSAGE)&request,
+ (PPORT_MESSAGE)&reply);
+
+ if (!NT_SUCCESS(status)) {
+ return status;
+ }
+
+ /** If we got a SAP error - return it **/
+
+ if (reply.Error)
+ return reply.Error;
+
+ /** Return the entry **/
+
+ if (ObjectType)
+ *ObjectType = reply.Message.BindLibApi.ObjectType;
+
+ if (ObjectName)
+ memcpy(ObjectName, reply.Message.BindLibApi.ObjectName, NWSAP_MAXNAME_LENGTH+1);
+
+ if (ObjectAddr)
+ memcpy(ObjectAddr, reply.Message.BindLibApi.ObjectAddr, 12);
+
+ /** All Done OK **/
+
+ return SAPRETURN_SUCCESS;
+}
+
+
+/*++
+*******************************************************************
+ S a p G e t O b j e c t I D
+
+Routine Description:
+
+ This routine converts a name and type into an object ID.
+
+Arguments:
+ ObjectName = Ptr to AsciiZ object name (Must be uppercase)
+ ObjectType = Object type to look for
+ ObjectID = Ptr to where to store the object ID.
+
+Return Value:
+
+ SAPRETURN_SUCCESS = OK - Object ID is filled in
+ SAPRETURN_NOTEXIST = Name/Type not found
+*******************************************************************
+--*/
+
+INT
+SapGetObjectID(
+ IN PUCHAR ObjectName,
+ IN USHORT ObjectType,
+ IN PULONG ObjectID)
+{
+ NTSTATUS status;
+ NWSAP_REQUEST_MESSAGE request;
+ NWSAP_REPLY_MESSAGE reply;
+
+ /** If not initialized - return error **/
+
+ if (!SapLibInitialized)
+ return SAPRETURN_NOTINIT;
+
+ /** If the name is too long - error **/
+
+ if (strlen(ObjectName) > NWSAP_MAXNAME_LENGTH)
+ return SAPRETURN_INVALIDNAME;
+
+ /** Build the Get Object Name message **/
+
+ request.MessageType = NWSAP_LPCMSG_GETOBJECTID;
+ request.PortMessage.u1.s1.DataLength = (USHORT)(sizeof(request) - sizeof(PORT_MESSAGE));
+ request.PortMessage.u1.s1.TotalLength = sizeof(request);
+ request.PortMessage.u2.ZeroInit = 0;
+
+ memset(request.Message.BindLibApi.ObjectName, 0, NWSAP_MAXNAME_LENGTH+1);
+ strcpy(request.Message.BindLibApi.ObjectName, ObjectName);
+ request.Message.BindLibApi.ObjectType = ObjectType;
+
+ /** Send it and get a response **/
+
+ status = NtRequestWaitReplyPort(
+ SapXsPortHandle,
+ (PPORT_MESSAGE)&request,
+ (PPORT_MESSAGE)&reply);
+
+ if (!NT_SUCCESS(status)) {
+ return status;
+ }
+
+ /** If we got a SAP error - return it **/
+
+ if (reply.Error)
+ return reply.Error;
+
+ /** Return the entry **/
+
+ *ObjectID = reply.Message.BindLibApi.ObjectID;
+
+ /** All Done OK **/
+
+ return SAPRETURN_SUCCESS;
+}
+
+
+/*++
+*******************************************************************
+ S a p S c a n O b j e c t
+
+Routine Description:
+
+ This routine is used to scan thru the database list.
+
+Arguments:
+ ObjectID = Ptr to last Object ID we saw. On first call
+ this should point to a 0xFFFFFFFF.
+ ObjectName = Ptr to where to store 48 byte object name
+ ObjectType = Ptr to where to store the object type
+ ScanType = Object Type that we are scanning for
+ (0xFFFF = All)
+
+ ObjectName, ObjectType can be NULL.
+
+Return Value:
+
+ SAPRETURN_SUCCESS = OK - name and type are filled in
+ ObjectID has the object ID of this entry.
+ SAPRETURN_NOTEXIST = Invalid object id.
+*******************************************************************
+--*/
+
+INT
+SapScanObject(
+ IN PULONG ObjectID,
+ IN PUCHAR ObjectName,
+ IN PUSHORT ObjectType,
+ IN USHORT ScanType)
+{
+ NTSTATUS status;
+ NWSAP_REQUEST_MESSAGE request;
+ NWSAP_REPLY_MESSAGE reply;
+
+ /** If not initialized - return error **/
+
+ if (!SapLibInitialized)
+ return SAPRETURN_NOTINIT;
+
+ /** Build the Get Object Name message **/
+
+ request.MessageType = NWSAP_LPCMSG_SEARCH;
+ request.PortMessage.u1.s1.DataLength = (USHORT)(sizeof(request) - sizeof(PORT_MESSAGE));
+ request.PortMessage.u1.s1.TotalLength = sizeof(request);
+ request.PortMessage.u2.ZeroInit = 0;
+
+ request.Message.BindLibApi.ObjectID = *ObjectID;
+ request.Message.BindLibApi.ScanType = ScanType;
+
+ /** Send it and get a response **/
+
+ status = NtRequestWaitReplyPort(
+ SapXsPortHandle,
+ (PPORT_MESSAGE)&request,
+ (PPORT_MESSAGE)&reply);
+
+ if (!NT_SUCCESS(status)) {
+ return status;
+ }
+
+ /** If we got a SAP error - return it **/
+
+ if (reply.Error)
+ return reply.Error;
+
+ /** Return the entry **/
+
+ if (ObjectType)
+ *ObjectType = reply.Message.BindLibApi.ObjectType;
+
+ if (ObjectName)
+ memcpy(ObjectName, reply.Message.BindLibApi.ObjectName, NWSAP_MAXNAME_LENGTH+1);
+
+ *ObjectID = reply.Message.BindLibApi.ObjectID;
+
+ /** All Done OK **/
+
+ return SAPRETURN_SUCCESS;
+}
diff --git a/private/net/svcdlls/nwsap/client/init.c b/private/net/svcdlls/nwsap/client/init.c
new file mode 100644
index 000000000..f8d45d21c
--- /dev/null
+++ b/private/net/svcdlls/nwsap/client/init.c
@@ -0,0 +1,135 @@
+/*++
+
+Copyright (c) 1994 Microsoft Corporation
+Copyright (c) 1993 Micro Computer Systems, Inc.
+
+Module Name:
+
+ net\svcdlls\nwsap\client\init.c
+
+Abstract:
+
+ This routine initializes the SAP Library
+
+Author:
+
+ Brian Walker (MCS) 06-15-1993
+
+Revision History:
+
+--*/
+
+#include "precomp.h"
+#pragma hdrstop
+
+/** Global Variables **/
+
+INT SapLibInitialized = 0;
+HANDLE SapXsPortHandle;
+
+
+/*++
+*******************************************************************
+ S a p L i b I n i t
+
+Routine Description:
+
+ This routine initializes the SAP interface for a program
+
+Arguments:
+ None
+
+Return Value:
+
+ 0 = Ok
+ Else = Error
+*******************************************************************
+--*/
+
+DWORD
+SapLibInit(
+ VOID)
+{
+ UNICODE_STRING unistring;
+ NTSTATUS status;
+ SECURITY_QUALITY_OF_SERVICE qos;
+
+ /** If already initialized - return ok **/
+
+ if (SapLibInitialized) {
+ return 0;
+ }
+
+ /** Connect the port **/
+
+ /** Fill out the security quality of service **/
+
+ qos.Length = sizeof(qos);
+ qos.ImpersonationLevel = SecurityImpersonation;
+ qos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
+ qos.EffectiveOnly = TRUE;
+
+ /** Setup the unicode string of the port name **/
+
+ RtlInitUnicodeString(&unistring, NWSAP_BIND_PORT_NAME_W);
+
+ /** Do the connect **/
+
+ status = NtConnectPort(
+ &SapXsPortHandle, /* We get a handle back */
+ &unistring, /* Port name to connect to */
+ &qos, /* Quality of service */
+ NULL, /* Client View */
+ NULL, /* Server View */
+ NULL, /* MaxMessageLength */
+ NULL, /* ConnectionInformation */
+ NULL); /* ConnectionInformationLength */
+
+ /** If error - just return it **/
+
+ if (!NT_SUCCESS(status))
+ return status;
+
+ /** All Done **/
+
+ SapLibInitialized = 1;
+ return 0;
+}
+
+
+/*++
+*******************************************************************
+ S a p L i b S h u t d o w n
+
+Routine Description:
+
+ This routine shuts down the SAP interface for a program
+
+Arguments:
+ None
+
+Return Value:
+
+ 0 = Ok
+ Else = Error
+*******************************************************************
+--*/
+
+DWORD
+SapLibShutdown(
+ VOID)
+{
+ /** If not initialized - leave **/
+
+ if (!SapLibInitialized)
+ return 0;
+
+ /** Close the port **/
+
+ NtClose(SapXsPortHandle);
+
+ /** All Done **/
+
+ SapLibInitialized = 0;
+ return 0;
+}
diff --git a/private/net/svcdlls/nwsap/client/makefile b/private/net/svcdlls/nwsap/client/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/net/svcdlls/nwsap/client/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/net/svcdlls/nwsap/client/precomp.h b/private/net/svcdlls/nwsap/client/precomp.h
new file mode 100644
index 000000000..6299690e8
--- /dev/null
+++ b/private/net/svcdlls/nwsap/client/precomp.h
@@ -0,0 +1,46 @@
+/*++
+
+Copyright (c) 1994 Microsoft Corporation
+Copyright (c) 1993 Micro Computer Systems, Inc.
+
+Module Name:
+
+ net\svcdlls\nwsap\client\precomp.h
+
+Abstract:
+
+ Include files for the SAP Agent library
+
+Author:
+
+ Brian Walker (MCS) 13-Jun-1993
+
+Revision History:
+
+--*/
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+
+#include <windef.h>
+#include <winbase.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <nwsap.h>
+#include "..\saplpc.h"
+
+/** Global Variables **/
+
+extern INT SapLibInitialized;
+extern HANDLE SapXsPortHandle;
+
+#define NWSAP_MAXNAME_LENGTH 47
+
+/** Functions **/
+
+
+
+
+
diff --git a/private/net/svcdlls/nwsap/client/sources b/private/net/svcdlls/nwsap/client/sources
new file mode 100644
index 000000000..8e0c2f080
--- /dev/null
+++ b/private/net/svcdlls/nwsap/client/sources
@@ -0,0 +1,32 @@
+!IF 0
+
+Copyright (c) 1994 Microsoft Corporation
+Copyright (c) 1993 Micro Computer Systems, Inc.
+
+!ENDIF
+
+MAJORCOMP=net
+MINORCOMP=nwsaplib
+
+TARGETNAME=nwsaplib
+TARGETPATH=$(BASEDIR)\public\sdk\lib
+TARGETTYPE=LIBRARY
+
+USE_CRTDLL = 1
+
+INCLUDES=.;..\..\..\inc;..\..\..\..\inc
+
+UNICODE=1
+NET_C_DEFINES=-DUNICODE
+
+SOURCES= \
+ advapi.c \
+ bindlib.c \
+ init.c
+
+TARGETLIBS=$(BASEDIR)\public\sdk\lib\*\$(TARGETNAME).lib\
+ $(BASEDIR)\public\sdk\lib\*\kernel32.lib
+
+PRECOMPILED_INCLUDE=precomp.h
+PRECOMPILED_PCH=precomp.pch
+PRECOMPILED_OBJ=precomp.obj
diff --git a/private/net/svcdlls/nwsap/dirs b/private/net/svcdlls/nwsap/dirs
new file mode 100644
index 000000000..e63a0eefa
--- /dev/null
+++ b/private/net/svcdlls/nwsap/dirs
@@ -0,0 +1,26 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ dirs.
+
+Abstract:
+
+ This file specifies the subdirectories of the current directory that
+ contain component makefiles.
+
+
+Author:
+
+ Steve Wood (stevewo) 17-Apr-1990
+
+NOTE: Commented description of this file is in \nt\bak\bin\dirs.tpl
+
+!ENDIF
+
+
+DIRS= \
+ client \
+ server
diff --git a/private/net/svcdlls/nwsap/saplpc.h b/private/net/svcdlls/nwsap/saplpc.h
new file mode 100644
index 000000000..c0ea4bb46
--- /dev/null
+++ b/private/net/svcdlls/nwsap/saplpc.h
@@ -0,0 +1,101 @@
+/*++
+
+Copyright (c) 1994 Microsoft Corporation
+Copyright (c) 1993 Micro Computer Systems, Inc.
+
+Module Name:
+
+ net\svcdlls\nwsap\saplpc.h
+
+Abstract:
+
+Author:
+
+ Brian Walker (MCS) 06-30-1993
+
+Revision History:
+
+--*/
+
+#ifndef _NWSAP_LPC_
+#define _NWSAP_LCP_
+
+/**
+ Structure used to pass LPC messages between the client
+ library and the main server. Note the the PORT_MESSAGE is first
+ and that the request and reply structures are VERY similar.
+**/
+
+typedef struct _NWSAP_REQUEST_MESSAGE {
+
+ PORT_MESSAGE PortMessage;
+ ULONG MessageType;
+
+ union {
+
+ struct {
+ USHORT ServerType;
+ UCHAR ServerName[48];
+ UCHAR ServerAddr[12];
+ BOOL RespondNearest;
+ } AdvApi;
+
+ struct {
+ ULONG ObjectID;
+ UCHAR ObjectName[48];
+ USHORT ObjectType;
+ UCHAR ObjectAddr[12];
+ USHORT ScanType;
+ } BindLibApi;
+
+ } Message;
+
+} NWSAP_REQUEST_MESSAGE, *PNWSAP_REQUEST_MESSAGE;
+
+
+typedef struct _NWSAP_REPLY_MESSAGE {
+
+ PORT_MESSAGE PortMessage;
+ ULONG Error;
+
+ union {
+
+ struct {
+ USHORT ServerType;
+ UCHAR ServerName[48];
+ UCHAR ServerAddr[12];
+ BOOL RespondNearest;
+ } AdvApi;
+
+ struct {
+ ULONG ObjectID;
+ UCHAR ObjectName[48];
+ USHORT ObjectType;
+ UCHAR ObjectAddr[12];
+ USHORT ScanType;
+ } BindLibApi;
+
+ } Message;
+} NWSAP_REPLY_MESSAGE, *PNWSAP_REPLY_MESSAGE;
+
+/** Message Types **/
+
+#define NWSAP_LPCMSG_ADDADVERTISE 0
+#define NWSAP_LPCMSG_REMOVEADVERTISE 1
+#define NWSAP_LPCMSG_GETOBJECTID 2
+#define NWSAP_LPCMSG_GETOBJECTNAME 3
+#define NWSAP_LPCMSG_SEARCH 4
+
+/** Name of our port **/
+
+#define NWSAP_BIND_PORT_NAME_W L"\\BaseNamedObjects\\NwSapLpcPort"
+#define NWSAP_BIND_PORT_NAME_A "\\BaseNamedObjects\\NwSapLpcPort"
+
+/** Max message length we need **/
+
+#define NWSAP_BS_PORT_MAX_MESSAGE_LENGTH \
+ ( sizeof(NWSAP_REQUEST_MESSAGE) > sizeof(NWSAP_REPLY_MESSAGE) ? \
+ sizeof(NWSAP_REQUEST_MESSAGE) : sizeof(NWSAP_REPLY_MESSAGE) )
+
+#endif
+
diff --git a/private/net/svcdlls/nwsap/server/advapi.c b/private/net/svcdlls/nwsap/server/advapi.c
new file mode 100644
index 000000000..f3fdc435e
--- /dev/null
+++ b/private/net/svcdlls/nwsap/server/advapi.c
@@ -0,0 +1,331 @@
+/*++
+
+Copyright (c) 1994 Microsoft Corporation
+Copyright (c) 1993 Micro Computer Systems, Inc.
+
+Module Name:
+
+ net\svcdlls\nwsap\server\advapi.c
+
+Abstract:
+
+ This routine handles the Advertise API for the SAP Agent
+
+Author:
+
+ Brian Walker (MCS) 06-15-1993
+
+Revision History:
+
+--*/
+
+#include "precomp.h"
+#pragma hdrstop
+
+
+/*++
+*******************************************************************
+ S a p A d d A d v e r t i s e I n t e r n a l
+
+Routine Description:
+
+ This routine adds an entry to the list of servers
+ that we advertise.
+
+Arguments:
+ ServerName = Ptr to 48 byte server name
+ ServerType = USHORT of object type to add
+ ServerAddr = Ptr to 12 byte aerver address
+ RespondNearest = TRUE = Respond for me on get nearest server
+ FALSE = Do not respond for me on get nearest
+ ClientId = Client ID of client adding this.
+
+Return Value:
+
+ SAPRETURN_SUCCESS - Added OK
+ SAPRETURN_NOMEMORY - Error allocating memory
+ SAPRETURN_EXISTS - Already exists in list
+ SAPRETURN_NOTINIT - SAP Agent is not running
+*******************************************************************
+--*/
+
+INT
+SapAddAdvertiseInternal(
+ IN PUCHAR ServerName,
+ IN USHORT ServerType,
+ IN PUCHAR ServerAddr,
+ IN BOOL RespondNearest,
+ IN ULONG ClientId)
+{
+ PSAP_SERVER Servp;
+
+ /** If not running - return error **/
+
+ if (!SsInitialized)
+ return SAPRETURN_NOTINIT;
+
+ /** Get the lock on the send table **/
+
+ ACQUIRE_SENDTABLE_LOCK();
+
+ /** Uppercase the input string **/
+
+ _strupr(ServerName);
+
+ /** If network or node number is 0 - fill it in **/
+
+ if (!memcmp(ServerAddr, "\x00\x00\x00\x00", SAP_NET_LEN))
+ SAP_COPY_NETNUM(ServerAddr, SapNetNum);
+
+ if (!memcmp(ServerAddr+4, "\x00\x00\x00\x00\x00\x00", SAP_NODE_LEN))
+ SAP_COPY_NODENUM(ServerAddr+4, SapNodeNum);
+
+ /** Make sure not already in the table **/
+
+ Servp = SapServHead;
+ while (Servp) {
+
+ /** If already in our list - break out - return error **/
+
+ //
+ // If the address is on our local box, then this is a timing issue
+ // where we have yet to remove it from the last time this service
+ // was added... let the AddService continue.
+ //
+
+ if ((Servp->ServerType == htons(ServerType)) &&
+ (!SAP_NAMECMP(Servp->ServerName, ServerName)) &&
+ (memcmp(ServerAddr,SapNetNum,SAP_NET_LEN) != 0) &&
+ (memcmp(ServerAddr+4,SapNodeNum,SAP_NODE_LEN) != 0)) {
+
+ break;
+ }
+
+ /** Goto the next entry **/
+
+ Servp = Servp->Next;
+ }
+
+ /** If already here - return error **/
+
+ if (Servp) {
+ RELEASE_SENDTABLE_LOCK();
+ return SAPRETURN_EXISTS;
+ }
+
+ /**
+ If the name is in our big table - then return error.
+ **/
+
+ if (SapAllowDuplicateServers == 0) {
+ if (SdmdIsServerInTable(ServerName, ServerType)) {
+ RELEASE_SENDTABLE_LOCK();
+ return SAPRETURN_DUPLICATE;
+ }
+ }
+
+ /** Allocate the new entry **/
+
+ Servp = SAP_MALLOC(SAP_SERVER_SIZE, "AdvApi Add");
+ if (Servp == NULL) {
+ RELEASE_SENDTABLE_LOCK();
+ return SAPRETURN_NOMEMORY;
+ }
+
+ /** Fill in the entry **/
+
+ Servp->Next = NULL;
+ Servp->ServerType = htons(ServerType);
+ memset(Servp->ServerName, '\0', SAP_OBJNAME_LEN);
+ strcpy(Servp->ServerName, ServerName);
+ SAP_COPY_ADDRESS(Servp->Address, ServerAddr);
+ Servp->Hopcount = htons(1);
+ Servp->RespondNearest = RespondNearest;
+ Servp->ClientId = ClientId;
+
+ /** Add this entry to the list **/
+
+ if (SapServHead)
+ SapServTail->Next = Servp;
+ else
+ SapServHead = Servp;
+
+ SapServTail = Servp;
+
+ Servp->Changed = TRUE;
+ RELEASE_SENDTABLE_LOCK();
+
+ /**
+ Add this entry to our database NOW.
+ **/
+
+ SdmdUpdateEntry(
+ Servp->ServerName, /* Server name */
+ ntohs(Servp->ServerType), /* Server Type */
+ Servp->Address, /* Server Address */
+ ntohs(Servp->Hopcount), /* Server Hopcount */
+ CARDRET_MYSELF, /* Card number */
+ SapZeros, /* My address (don't care)*/
+ FALSE); /* Not a WAN card */
+
+ /** Cause the send thread to send another NOW **/
+
+ SapSendPackets(1);
+
+ /** All Done **/
+
+ return SAPRETURN_SUCCESS;
+}
+
+
+/*++
+*******************************************************************
+ S a p R e m o v e A d v e r t i s e I n t e r n a l
+
+Routine Description:
+
+ This routine removes an entry to the list of servers
+ that we advertise.
+
+Arguments:
+ ServerName = Ptr to 48 byte server name
+ ServerType = USHORT of object type to remove
+
+Return Value:
+
+ SAPRETURN_SUCCESS - Added OK
+ SAPRETURN_NOTEXIST - Entry does not exist in list
+ SAPRETURN_NOTINIT - SAP Agent is not running
+*******************************************************************
+--*/
+
+INT
+SapRemoveAdvertiseInternal(
+ IN PUCHAR ServerName,
+ IN USHORT ServerType)
+{
+ PSAP_SERVER Servp;
+
+ /** If not running - return error **/
+
+ if (!SsInitialized)
+ return SAPRETURN_NOTINIT;
+
+ /** Uppercase the input string **/
+
+ _strupr(ServerName);
+
+ /** Go find the entry **/
+
+ ACQUIRE_SENDTABLE_LOCK();
+ Servp = SapServHead;
+ while (Servp) {
+
+ /** If this is it - break out **/
+
+ if ((Servp->ServerType == htons(ServerType)) &&
+ (!SAP_NAMECMP(Servp->ServerName, ServerName))) {
+ break;
+ }
+
+ /** Goto the next entry **/
+
+ Servp = Servp->Next;
+ }
+
+ /** If not found - just leave **/
+
+ if (Servp == NULL) {
+ RELEASE_SENDTABLE_LOCK();
+ return SAPRETURN_NOTEXIST;
+ }
+
+ /** Mark the entry as going away - we will delete it later **/
+
+ Servp->Hopcount = htons(16);
+ Servp->Changed = TRUE;
+ RELEASE_SENDTABLE_LOCK();
+
+ /** Cause the send thread to send another NOW **/
+
+ SapSendPackets(1);
+
+ /** All Done **/
+
+ return SAPRETURN_SUCCESS;
+}
+
+
+/*++
+*******************************************************************
+ S a p C l i e n t D i s c o n n e c t e d
+
+Routine Description:
+
+ A client lpc thread has disconnected - delete any
+ entries that they might have had.
+
+Arguments:
+ ClientId = Client ID to delete for
+
+Return Value:
+
+ None
+*******************************************************************
+--*/
+
+VOID
+SapClientDisconnected(
+ ULONG ClientId)
+{
+ PSAP_SERVER Servp;
+ PSAP_SERVER NServp;
+ BOOL Found = FALSE;
+
+ /** Go find all entries that match **/
+
+ ACQUIRE_SENDTABLE_LOCK();
+ Servp = SapServHead;
+ while (Servp) {
+
+ /** Save ptr to the next entry **/
+
+ NServp = Servp->Next;
+
+ /** If this is it - break out **/
+
+ if (Servp->ClientId == ClientId) {
+
+ /** Mark the entry as going away **/
+
+ Servp->Hopcount = htons(16);
+ Servp->Changed = TRUE;
+
+ /** Mark we found at least one **/
+
+ Found = TRUE;
+ }
+
+ /** Goto the next entry **/
+
+ Servp = NServp;
+ }
+
+ /** Release the lock on the list **/
+
+ RELEASE_SENDTABLE_LOCK();
+
+ /**
+ Cause the send thread to send another NOW. Only if we
+ actually changed anything.
+ **/
+
+ if (Found) {
+ SapSendPackets(1);
+ }
+
+ /** All Done **/
+
+ return;
+}
+
diff --git a/private/net/svcdlls/nwsap/server/bindlib.c b/private/net/svcdlls/nwsap/server/bindlib.c
new file mode 100644
index 000000000..bcedf5195
--- /dev/null
+++ b/private/net/svcdlls/nwsap/server/bindlib.c
@@ -0,0 +1,402 @@
+/*++
+
+Copyright (c) 1994 Microsoft Corporation
+Copyright (c) 1993 Micro Computer Systems, Inc.
+
+Module Name:
+
+ net\svcdlls\nwsap\server\bindlib.c
+
+Abstract:
+
+ This routine handles the BindLib API for the SAP Agent
+
+Author:
+
+ Brian Walker (MCS) 06-15-1993
+
+Revision History:
+
+--*/
+
+#include "precomp.h"
+#pragma hdrstop
+
+#define BINDLIB_BITS 0xC0000000
+#define BINDLIB_MASK 0x1FFFFFFF
+
+
+/*++
+*******************************************************************
+ S a p G e t O b j e c t N a m e I n t e r n a l
+
+Routine Description:
+
+ This routine converts an Object ID into an Object Name
+ and Type.
+
+Arguments:
+ ObjectID = Object ID to convert
+ ObjectName = Ptr to where to store 48 byte object name
+ ObjectType = Ptr to where to store the object type
+ ObjectAddr = Ptr to where to store NET_ADDRESS (12 bytes)
+
+ ObjectName, ObjectType, ObjectAddr can be NULL.
+
+Return Value:
+
+ SAPRETURN_SUCCESS = OK - name and type are filled in
+ SAPRETURN_NOTEXIST = Invalid object id.
+*******************************************************************
+--*/
+
+INT
+SapGetObjectNameInternal(
+ IN ULONG ObjectID,
+ IN PUCHAR ObjectName,
+ IN PUSHORT ObjectType,
+ IN PUCHAR ObjectAddr)
+{
+ PSAP_RECORD Entry;
+
+ /** Make sure the object ID is valid **/
+
+ if ((ObjectID & BINDLIB_BITS) != BINDLIB_BITS)
+ return SAPRETURN_NOTEXIST;
+
+ /** Convert the Object ID into an index number **/
+
+ ObjectID &= BINDLIB_MASK;
+
+ /** Make sure the index number is valid **/
+
+ if (ObjectID >= (ULONG)SapNumArrayEntries)
+ return SAPRETURN_NOTEXIST;
+
+ /** Get a ptr to the entry **/
+
+ ACQUIRE_READERS_LOCK("SapGetObjectName");
+ Entry = GETPTRFROMINDEX(ObjectID);
+ if (Entry == NULL) {
+ RELEASE_READERS_LOCK("SapGetObjectName X1");
+ return SAPRETURN_NOTEXIST;
+ }
+
+ /** If in free list - return error **/
+
+ if ((Entry->ServType == 0xFFFF) ||
+ (Entry->HeadIndex == SDMD_ENDOFLIST) ||
+ (Entry->HopCount == 16)) {
+
+ RELEASE_READERS_LOCK("SapGetObjectName X2");
+ return SAPRETURN_NOTEXIST;
+ }
+
+ /** Return the entry **/
+
+ if (ObjectType)
+ *ObjectType = Entry->ServType;
+
+ if (ObjectName)
+ SAP_COPY_SERVNAME(ObjectName, Entry->ServName);
+
+ if (ObjectAddr)
+ SAP_COPY_ADDRESS(ObjectAddr, Entry->ServAddress);
+
+ /** All Done OK **/
+
+ RELEASE_READERS_LOCK("SapGetObjectName X3");
+ return SAPRETURN_SUCCESS;
+}
+
+
+/*++
+*******************************************************************
+ S a p G e t O b j e c t I D I n t e r n a l
+
+Routine Description:
+
+ This routine converts a name and type into an object ID.
+
+Arguments:
+ ObjectName = Ptr to 48 byte object name (Must be uppercase)
+ ObjectType = Object type to look for
+ ObjectID = Ptr to where to store the object ID.
+
+Return Value:
+
+ SAPRETURN_SUCCESS = OK - Object ID is filled in
+ SAPRETURN_NOTEXIST = Name/Type not found
+*******************************************************************
+--*/
+
+INT
+SapGetObjectIDInternal(
+ IN PUCHAR ObjectName,
+ IN USHORT ObjectType,
+ IN PULONG ObjectID)
+{
+ PSAP_RECORD Entry;
+ PSDMD_LIST_ENTRY ListHead;
+ INT HashIndex;
+ INT rc;
+
+ /** Cannot have wildcard **/
+
+ if (ObjectType == 0xFFFF)
+ return SAPRETURN_NOTEXIST;
+
+ /** Lock the database **/
+
+ ACQUIRE_READERS_LOCK("SapGetObjectId");
+
+ /** Get the name from the hash table **/
+
+ HashIndex = SdmdCalcHash(ObjectName);
+
+ /** Get ptr to the list head for the hash index **/
+
+ ListHead = SdmdNameHashTable + HashIndex;
+
+ /** Find the name in the list **/
+
+ Entry = GETPTRFROMINDEX(ListHead->Flink);
+ while (Entry) {
+
+ /**
+ Make sure the Object Type is correct and that this entry
+ is not marked for deletion.
+ **/
+
+ if ((Entry->ServType == ObjectType) && (Entry->HopCount != 16)) {
+
+ /** Check if this is the name **/
+
+ rc = SAP_NAMECMP(Entry->ServName, ObjectName);
+
+ /** If this is it - return it **/
+
+ if (!rc) {
+
+ /** Build the object ID and return it **/
+
+ *ObjectID = (ULONG)(Entry->Index | BINDLIB_BITS);
+ RELEASE_READERS_LOCK("SapGetObjectId X2");
+ return SAPRETURN_SUCCESS;
+ }
+
+ /**
+ Since the names are stored in alphabetical order, if we
+ get past the name we are looking for, then we can just
+ give up now instead of having to search the whole list.
+ **/
+
+ if (rc > 0)
+ break;
+ }
+
+ /** Goto the next entry **/
+
+ Entry = GETPTRFROMINDEX(Entry->Links[SAP_HASHLIST_INDEX].Flink);
+ }
+
+ /** There is not an entry for that name/type **/
+
+ RELEASE_READERS_LOCK("SapGetObjectId X3");
+ return SAPRETURN_NOTEXIST;
+}
+
+
+/*++
+*******************************************************************
+ S a p S c a n O b j e c t I n t e r n a l
+
+Routine Description:
+
+ This routine is used to scan thru the database list.
+
+Arguments:
+ ObjectID = Ptr to last Object ID we saw. On first call
+ this should point to a 0xFFFFFFFF.
+ ObjectName = Ptr to where to store 48 byte object name
+ ObjectType = Ptr to where to store the object type
+ ScanType = Object Type that we are scanning for
+ (0xFFFF = All)
+
+ ObjectName, ObjectType can be NULL.
+
+Return Value:
+
+ SAPRETURN_SUCCESS = OK - name and type are filled in
+ ObjectID has the object ID of this entry.
+ SAPRETURN_NOTEXIST = Invalid object id.
+*******************************************************************
+--*/
+
+INT
+SapScanObjectInternal(
+ IN PULONG ObjectID,
+ IN PUCHAR ObjectName,
+ IN PUSHORT ObjectType,
+ IN USHORT ScanType)
+{
+ PSAP_RECORD Entry;
+ PSAP_RECORD HeadEntry;
+ ULONG id;
+
+ /** Get the readers lock **/
+
+ ACQUIRE_READERS_LOCK("SapScanObject Entry");
+
+ /**
+ Get the ID and validate it. If the ID is 0xFFFFFFFF, then
+ we start with the first entry in the type list.
+
+ If it is not 0xFFFFFFFF - then we get that entry and
+ get it's fwd pointer in the type list as the next one to return.
+ **/
+
+ id = *ObjectID;
+ if (id == 0xFFFFFFFF) {
+
+ /** Get ptr to entry at head of the type list **/
+
+ HeadEntry = GETPTRFROMINDEX(SdmdLists[SAP_TYPELIST_INDEX].Flink);
+ if (ScanType != 0xFFFF) {
+ while (HeadEntry && (HeadEntry->ServType < ScanType)) {
+ HeadEntry = GETPTRFROMINDEX(HeadEntry->Links[SAP_TYPELIST_INDEX].Flink);
+ }
+ }
+
+ /** If no type found - return error **/
+
+ if ((HeadEntry == NULL) || (HeadEntry->ServType == 0xFFFF)) {
+ RELEASE_READERS_LOCK("SapScanObject X1");
+ return SAPRETURN_NOTEXIST;
+ }
+
+ /** If not wildcard - make sure it matches type we want **/
+
+ if (ScanType != 0xFFFF) {
+ if (HeadEntry->ServType != ScanType) {
+ RELEASE_READERS_LOCK("SapScanObject X2");
+ return SAPRETURN_NOTEXIST;
+ }
+ }
+
+ /** Get ptr to first entry in this sublist **/
+
+ Entry = GETPTRFROMINDEX(HeadEntry->Links[SAP_SUBLIST_INDEX].Flink);
+ }
+ else {
+
+ /**
+ This is a SCAN NEXT type call. Take the object ID and
+ get the forward pointer to it and verify that it is for
+ the same object type as before.
+ **/
+
+ if ((id & BINDLIB_BITS) != BINDLIB_BITS) {
+ RELEASE_READERS_LOCK("SapScanObject X4");
+ return SAPRETURN_NOTEXIST;
+ }
+ id &= BINDLIB_MASK; /* Convert to an index */
+
+ /** Make sure the index number is valid **/
+
+ if (id >= (ULONG)SapNumArrayEntries) {
+ RELEASE_READERS_LOCK("SapGetObjectName X5");
+ return SAPRETURN_NOTEXIST;
+ }
+
+ /**
+ Get ptr to the entry. If the ptr is bad or the entry
+ is on the free list - then we should return an error.
+ **/
+
+ Entry = GETPTRFROMINDEX(id);
+ if ((Entry == NULL) || (Entry->ServType == 0xFFFF) || (Entry->HeadIndex == SDMD_ENDOFLIST)) {
+ RELEASE_READERS_LOCK("SapGetObjectName X6");
+ return SAPRETURN_NOTEXIST;
+ }
+
+ /** Get the head entry for this entry **/
+
+ HeadEntry = GETPTRFROMINDEX(Entry->HeadIndex);
+ SS_ASSERT(HeadEntry);
+
+ /**
+ Now we get the ptr to the next entry in the sub list.
+ This is the entry we will return (If there is one).
+ **/
+
+ Entry = GETPTRFROMINDEX(Entry->Links[SAP_SUBLIST_INDEX].Flink);
+ }
+
+ /**
+ Entry is the entry we start with. This could be NULL.
+
+ Entry and HeadEntry are set here. HeadEntry must be non-NULL.
+ **/
+
+ while (1) {
+
+ /**
+ If we hit the end of this sublist - then we can go to the next
+ sublist if the scan type is for wildcard.
+ **/
+
+ if (Entry == NULL) {
+
+ /** If not wildcard - return error **/
+
+ if (ScanType != 0xFFFF) {
+ RELEASE_READERS_LOCK("SapGetObjectName X7");
+ return SAPRETURN_NOTEXIST;
+ }
+
+ /** This is for wildcard - get next HeadEntry **/
+
+ HeadEntry = GETPTRFROMINDEX(HeadEntry->Links[SAP_TYPELIST_INDEX].Flink);
+ if ((HeadEntry == NULL) || (HeadEntry->ServType == 0xFFFF)) {
+ RELEASE_READERS_LOCK("SapGetObjectName X8");
+ return SAPRETURN_NOTEXIST;
+ }
+
+ /** Get ptr to first entry in this list **/
+
+ Entry = GETPTRFROMINDEX(HeadEntry->Links[SAP_SUBLIST_INDEX].Flink);
+ }
+
+ /** If we got one - return it **/
+
+ if (Entry) {
+
+ /** If valid entry - return it **/
+
+ if (Entry->HopCount != 16)
+ break;
+
+ /** Get ptr to next entry in the list **/
+
+ Entry = GETPTRFROMINDEX(Entry->Links[SAP_SUBLIST_INDEX].Flink);
+ }
+
+ /** We go back up to try again **/
+ }
+
+ /** Return the entry **/
+
+ *ObjectID = (ULONG)(Entry->Index | BINDLIB_BITS);
+
+ if (ObjectType)
+ *ObjectType = Entry->ServType;
+
+ if (ObjectName)
+ SAP_COPY_SERVNAME(ObjectName, Entry->ServName);
+
+ /** All Done OK **/
+
+ RELEASE_READERS_LOCK("SapGetObjectName X9");
+ return SAPRETURN_SUCCESS;
+}
diff --git a/private/net/svcdlls/nwsap/server/dump.c b/private/net/svcdlls/nwsap/server/dump.c
new file mode 100644
index 000000000..44e80bdb7
--- /dev/null
+++ b/private/net/svcdlls/nwsap/server/dump.c
@@ -0,0 +1,206 @@
+/*++
+
+Copyright (c) 1994 Microsoft Corporation
+Copyright (c) 1993 Micro Computer Systems, Inc.
+
+Module Name:
+
+ net\svcdlls\nwsap\server\dump.c
+
+Abstract:
+
+ Memory dump functions.
+
+Author:
+
+ Brian Walker (MCS) 06-30-1993
+
+Revision History:
+
+--*/
+
+#include "precomp.h"
+#pragma hdrstop
+
+#if DBG
+
+
+/*++
+*******************************************************************
+ S a p D u m p M e m
+
+Routine Description:
+
+
+
+Arguments:
+
+ Address = Ptr to buffer to dump
+ Length = Length of data to dump
+ Comment = Comment to put in header line
+
+Return Value:
+
+ None.
+*******************************************************************
+--*/
+
+VOID
+SapDumpMem(
+ PUCHAR Address,
+ INT Length,
+ PCHAR Comment)
+{
+ PUCHAR Ptr;
+ INT Numdone;
+ INT Cnt;
+ INT i;
+ UCHAR ch;
+ CHAR Buildline[80];
+
+ /** Print hdr line if there is a comment **/
+
+ if (Comment) {
+ SS_PRINT(("%s Address = %x, Length = %d\n", Comment, Address, Length));
+ }
+
+ /** Truncate dump if it is too long **/
+
+ if (Length > 128)
+ Length = 128;
+
+ /** Dump out the memory **/
+
+ while (Length) {
+
+ /** Clear the buildline to spaces **/
+
+ memset(Buildline, ' ', 80);
+ *(Buildline + 79) = '\0';
+
+ /** Format the address on the left side **/
+
+ sprintf(Buildline, "%08lx", (ULONG)Address);
+ Buildline[8] = ' ';
+
+ /** Get ptrs to hex dump/ascii dump areas **/
+
+ Ptr = Buildline + 11; /* point at the build line */
+ Cnt = 61; /* Ptr+Cnt = bld ascii here */
+
+ /** Figure how many bytes there are **/
+
+ Numdone = 16; /* do 16 bytes */
+ if (Length < 16) /* or whatever is there */
+ Numdone = Length;
+ Length -= Numdone; /* adjust length for next time */
+
+ /** Build this line **/
+
+ for (i = 0 ; i < Numdone ; i++) {
+ ch = *Address++;
+ sprintf(Ptr, "%02x", (UCHAR)ch);
+ if ((ch < ' ') || (ch > '~'))
+ ch = '.';
+ Buildline[Cnt++] = ch;
+ Ptr += 2;
+ if (i == 7)
+ *Ptr++ = '-';
+ else
+ *Ptr++ = ' ';
+ }
+
+ /** Print the line **/
+
+ SS_PRINT(("%s\n", Buildline));
+ }
+
+ /** All Done **/
+
+ return;
+}
+
+
+/*++
+*******************************************************************
+ S a p D u m p M e m T o M e m o r y
+
+Routine Description:
+
+
+
+Arguments:
+
+Return Value:
+
+ None.
+
+*******************************************************************
+--*/
+
+VOID
+SapDumpMemToMemory(
+ PUCHAR Address,
+ INT Length,
+ PUCHAR Buffer)
+{
+ PUCHAR Ptr;
+ INT Numdone;
+ INT Cnt;
+ INT i;
+ UCHAR ch;
+ CHAR Buildline[80];
+
+ /** Dump out the memory **/
+
+ while (Length) {
+
+ /** Clear the buildline to spaces **/
+
+ memset(Buildline, ' ', 80);
+ *(Buildline + 79) = '\0';
+
+ /** Format the address on the left side **/
+
+ sprintf(Buildline, "%08lx", (ULONG)Address);
+ Buildline[8] = ' ';
+
+ /** Get ptrs to hex dump/ascii dump areas **/
+
+ Ptr = Buildline + 11; /* point at the build line */
+ Cnt = 61; /* Ptr+Cnt = bld ascii here */
+
+ /** Figure how many bytes there are **/
+
+ Numdone = 16; /* do 16 bytes */
+ if (Length < 16) /* or whatever is there */
+ Numdone = Length;
+ Length -= Numdone; /* adjust length for next time */
+
+ /** Build this line **/
+
+ for (i = 0 ; i < Numdone ; i++) {
+ ch = *Address++;
+ sprintf(Ptr, "%02x", (UCHAR)ch);
+ if ((ch < ' ') || (ch > '~'))
+ ch = '.';
+ Buildline[Cnt++] = ch;
+ Ptr += 2;
+ if (i == 7)
+ *Ptr++ = '-';
+ else
+ *Ptr++ = ' ';
+ }
+
+ /** Print the line **/
+
+ sprintf(Buffer, "%s\n", Buildline);
+ Buffer += strlen(Buffer);
+ }
+
+ /** All Done **/
+
+ return;
+}
+
+#endif // if DBG
diff --git a/private/net/svcdlls/nwsap/server/externs.h b/private/net/svcdlls/nwsap/server/externs.h
new file mode 100644
index 000000000..a188bfe1b
--- /dev/null
+++ b/private/net/svcdlls/nwsap/server/externs.h
@@ -0,0 +1,344 @@
+/*++
+
+Copyright (c) 1994 Microsoft Corporation
+Copyright (c) 1993 Micro Computer Systems, Inc.
+
+Module Name:
+
+ net\svcdlls\nwsap\server\externs.h
+
+Abstract:
+
+ These are the external variable and function prototypes for
+ the NT SAP Agent.
+
+Author:
+
+ Brian Walker (MCS) 06-29-1993
+
+Revision History:
+
+--*/
+
+/** Variables **/
+
+extern LIST_ENTRY SapRecvList;
+extern LIST_ENTRY SapFreeList;
+extern LIST_ENTRY SapWanRecvList;
+extern LIST_ENTRY SapWanFreeList;
+extern HANDLE SapRecvSem;
+extern HANDLE SapWanSem;
+extern HANDLE SapSendEvent;
+extern HANDLE SapWanEvent;
+extern SOCKET SapSocket;
+extern PSAP_CARD SapCardHead;
+extern PSAP_CARD SapCardTail;
+extern PSAP_SERVER SapServHead;
+extern PSAP_SERVER SapServTail;
+extern INT SapMaxFreeBufs;
+extern INT SapNumFreeBufs;
+extern INT SapCurWorkerThreads;
+extern INT SapCurReceiveThreads;
+extern INT SapCurBackup;
+extern INT SapMaxBackup;
+extern INT SapWanCurBackup;
+extern INT SapWanCurFree;
+extern INT SapWanMaxFree;
+extern INT SapMaxCardIndex;
+extern INT SapNumCards;
+extern INT SapCardInitDone;
+extern INT SapSendPacketsBusy;
+extern INT SapAgainFlag;
+extern INT SapDontHopLans;
+extern INT SapAllowDuplicateServers;
+extern INT SapWorkerThreadWaiting;
+extern UCHAR SapNetNum[];
+extern UCHAR SapNodeNum[];
+extern CRITICAL_SECTION SapRecvCriticalSection;
+extern CRITICAL_SECTION SapFreeCriticalSection;
+extern CRITICAL_SECTION SapSendCriticalSection;
+extern CRITICAL_SECTION SapSendBusyCriticalSection;
+extern CRITICAL_SECTION SapThreadCountCriticalSection;
+extern CRITICAL_SECTION SapLpcThreadCountCriticalSection;
+extern CRITICAL_SECTION SapLpcClientCriticalSection;
+extern CRITICAL_SECTION SapMemoryCriticalSection;
+extern CRITICAL_SECTION SdmdCriticalSection;
+extern CRITICAL_SECTION SapCardlistCriticalSection;
+extern CRITICAL_SECTION SapWanRecvCriticalSection;
+extern CRITICAL_SECTION SapWanFreeCriticalSection;
+extern HANDLE SdmdSynchEvent;
+extern ULONG SdmdLockCount;
+extern ULONG SapAllocCount;
+extern BOOL SapChanged;
+extern PHANDLE SapWanNotifyHandlesBuf;
+extern INT SapWorkerStarting;
+extern DWORD SapLastWorkerStartTime;
+extern UCHAR SapZeros[];
+extern ULONG SapCardlistLockCount;
+extern HANDLE SapCardlistSynchEvent;
+extern INT SapRecheckCount;
+
+extern ULONG SapThreadCount;
+extern HANDLE SapThreadEvent;
+
+extern DWORD SsDebug;
+extern BOOL SsInitialized;
+
+extern DWORD SapError;
+extern DWORD SapEventId;
+
+/** LPC Variables **/
+
+extern HANDLE SapXsLpcPortHandle;
+extern LIST_ENTRY SapLpcClientList;
+extern ULONG SapNumLpcClients;
+
+extern ULONG SapLpcNumWorkers;
+extern ULONG SapLpcMaxWorkers;
+extern HANDLE SapLpcThreadEvent;
+extern SAP_FILTERHDR SapNameFilterHashTable[SAP_NAMEFILTER_HASHSIZE];
+extern ULONG SapFilterCount;
+
+/** Configurable Global Variables **/
+
+extern INT SapMaxFreeBufs;
+extern INT SapNumRecvThreads;
+extern INT SapNumWorkerThreads;
+extern INT SapSendMinutes;
+extern INT SapNumArrayEntries;
+extern INT SapTimeoutInterval;
+extern INT SapMaxEverWorkerThreads;
+extern INT SapNewWorkerThreshhold;
+extern INT SapNewWorkerTimeout;
+extern INT SapHashTableSize;
+extern INT SapRespondForInternal;
+extern INT SapActiveFilter;
+extern ULONG SapWanFilter;
+extern INT SapNumWanNotifyThreads;
+extern INT SapRecheckAllCardsTime;
+extern INT SapRecvDelayOnMallocFail;
+extern INT SapRecvDelayOnNetError;
+extern INT SapDelayRespondToGeneral;
+
+
+/*****************************************************************
+ Function Declarations
+*****************************************************************/
+
+/** ADVAPI.c **/
+
+INT
+SapAddAdvertiseInternal(
+ IN PUCHAR ServerName,
+ IN USHORT ServerType,
+ IN PUCHAR ServerAddr,
+ IN BOOL RespondNearest,
+ IN ULONG ClientId);
+
+INT
+SapRemoveAdvertiseInternal(
+ IN PUCHAR ServerName,
+ IN USHORT ServerType);
+
+VOID
+SapClientDisconnected(
+ IN ULONG ClientId);
+
+/** BINDLIB.c **/
+
+INT
+SapGetObjectIDInternal(
+ IN PUCHAR ObjectName,
+ IN USHORT ObjectType,
+ IN PULONG ObjectID);
+
+INT
+SapGetObjectNameInternal(
+ IN ULONG ObjectID,
+ IN PUCHAR ObjectName,
+ IN PUSHORT ObjectType,
+ IN PUCHAR ObjectAddr);
+
+INT
+SapScanObjectInternal(
+ IN PULONG ObjectID,
+ IN PUCHAR ObjectName,
+ IN PUSHORT ObjectType,
+ IN USHORT ScanType);
+
+
+/** DUMP.c **/
+
+VOID
+SapDumpMem(
+ PUCHAR Address,
+ INT Length,
+ PCHAR Comment);
+
+VOID
+SapDumpMemToMemory(
+ PUCHAR Address,
+ INT Length,
+ PUCHAR Buffer);
+
+/** FILTER.c **/
+
+INT
+SapFilterInit(
+ VOID);
+
+VOID
+SapFilterShutdown(
+ VOID);
+
+INT
+SapAddFilter(
+ PUCHAR ServerName,
+ BOOL Flag);
+
+BOOL
+SapShouldIAdvertiseByName(
+ PUCHAR ServerName);
+
+BOOL
+SapShouldIAdvertiseByCard(
+ PSAP_CARD Cardptr,
+ BOOL EntryHasChanged);
+
+/** NETWORK.c **/
+
+INT
+SapNetworkInit(
+ VOID);
+
+VOID
+SapNetworkShutdown(
+ VOID);
+
+INT
+SapSendPackets(
+ INT Flag);
+
+INT
+SapSendForTypes(
+ USHORT ServerType,
+ PUCHAR RemoteAddress,
+ INT RemoteAddressLength,
+ INT Cardnum,
+ UCHAR Bcast,
+ BOOL WanFlag);
+
+INT
+SapSendGeneralRequest(
+ INT Flag,
+ PUCHAR NetNum);
+
+USHORT
+SapGetDelayTime(
+ PUCHAR Netnum);
+
+VOID
+SapCleanupDownedCard(
+ INT Cardnum);
+
+VOID
+SapUpdateCardNetworkNumbers(
+ VOID);
+
+/** NWSAP.c **/
+
+INT
+SapInit(
+ VOID);
+
+VOID
+SapShutdown(
+ VOID);
+
+DWORD WINAPI
+SapSendThread(
+ LPVOID Threadparm);
+
+INT
+SapStartWorkerThread(
+ VOID);
+
+BOOL
+SapCanIRespondNearest(
+ PUCHAR ServerName,
+ USHORT ServerType);
+
+/** REGISTRY.c **/
+
+INT
+SapReadRegistry(
+ VOID);
+
+/** SAPDEBUG.c **/
+
+#if DBG
+
+VOID
+SapDebugHandler(
+ VOID);
+
+PVOID
+SapDebugMalloc(
+ ULONG length,
+ PUCHAR string);
+
+VOID
+SapDebugFree(
+ PVOID ptr,
+ PUCHAR string);
+
+#endif
+
+/** SAPLPC.c **/
+
+DWORD
+SapXsInitialize(
+ VOID);
+
+VOID
+SapXsShutdown(
+ VOID);
+
+/** SSSUBS.c **/
+
+VOID
+SsAssert(
+ IN PVOID FailedAssertion,
+ IN PVOID FileName,
+ IN ULONG LineNumber);
+
+VOID
+SsLogEvent(
+ IN DWORD MessageId,
+ IN DWORD NumberOfSubStrings,
+ IN LPWSTR *SubStrings,
+ IN DWORD ErrorCode);
+
+VOID
+SsPrintf(
+ char *Format,
+ ...);
+
+/** WANCHECK.c **/
+
+INT
+SapWanInit(
+ VOID);
+
+VOID
+SapWanShutdown(
+ VOID);
+
+VOID
+SapRecheckAllCards(
+ VOID);
+
+VOID
+SapCheckSendGeneralRequest(
+ VOID);
+
diff --git a/private/net/svcdlls/nwsap/server/filter.c b/private/net/svcdlls/nwsap/server/filter.c
new file mode 100644
index 000000000..c2ef5c8f7
--- /dev/null
+++ b/private/net/svcdlls/nwsap/server/filter.c
@@ -0,0 +1,458 @@
+/*++
+
+Copyright (c) 1994 Microsoft Corporation
+Copyright (c) 1993 Micro Computer Systems, Inc.
+
+Module Name:
+
+ net\svcdlls\nwsap\server\filter.c
+
+Abstract:
+
+ These routines handle the filter functionality for the NT SAP Agent.
+
+Author:
+
+ Brian Walker (MCS) 06-15-1993
+
+Revision History:
+
+--*/
+
+#include "precomp.h"
+#pragma hdrstop
+
+/** Internal Function Prototypes **/
+
+INT
+SapFilterCalcHash(
+ PUCHAR ServerName);
+
+
+/*++
+*******************************************************************
+ S a p F i l t e r I n i t
+
+Routine Description:
+
+ This routine initializes the filter portion of the Sap Agent.
+
+Arguments:
+
+ None
+
+Return Value:
+
+ 0 = OK
+ Else = Error
+*******************************************************************
+--*/
+
+INT
+SapFilterInit(
+ VOID)
+{
+
+ /** Zero out the hash table **/
+
+ SapFilterCount = 0;
+ memset(SapNameFilterHashTable,
+ 0,
+ sizeof(SAP_FILTERHDR) * SAP_NAMEFILTER_HASHSIZE);
+
+ /** Init OK **/
+
+ return 0;
+}
+
+
+/*++
+*******************************************************************
+ S a p F i l t e r S h u t d o w n
+
+Routine Description:
+
+ When we are terminating, this routine will clean
+ up everything.
+
+Arguments:
+
+ None
+
+Return Value:
+
+ Nothing
+*******************************************************************
+--*/
+
+VOID
+SapFilterShutdown(
+ VOID)
+{
+ PSAP_NAMEFILTER Filterp;
+ PSAP_NAMEFILTER Nextp;
+ PSAP_FILTERHDR Hdrp;
+ INT Cnt;
+
+ /**
+ Free all the entries in the hash table.
+ **/
+
+ for (Cnt = 0 ; Cnt < SAP_NAMEFILTER_HASHSIZE ; Cnt++) {
+
+ /** Get ptr to this header **/
+
+ Hdrp = &SapNameFilterHashTable[Cnt];
+
+ /** Get ptr to first entry of this header **/
+
+ Filterp = Hdrp->FirstEntry;
+ Hdrp->FirstEntry = NULL;
+
+ /** Free all of these entries **/
+
+ while (Filterp) {
+ Nextp = Filterp->Next;
+ SAP_FREE(Filterp, "Free Pass Sap Filter Entry");
+ Filterp = Nextp;
+ }
+ }
+ SapFilterCount = 0;
+
+ /** All Done **/
+
+ return;
+}
+
+
+/*++
+*******************************************************************
+ S a p A d d F i l t e r
+
+Routine Description:
+
+ Add a server to the filter list.
+
+Arguments:
+
+ ServerName = Ptr to name of server to add to list
+ Flag = TRUE = Put in the PASS list
+ FALSE = Put in the NO-PASS list
+
+Return Value:
+
+ 0 = OK
+ Else = Error
+
+ NOTE: We do not LOCK around any of this because for now
+ we only ADD an entry during startup and only delete entries
+ at shutdown.
+*******************************************************************
+--*/
+
+INT
+SapAddFilter(
+ PUCHAR ServerName,
+ BOOL Flag)
+{
+ INT HashIndex;
+ INT rc;
+ PSAP_FILTERHDR Hdrp;
+ PSAP_NAMEFILTER Filterp;
+ PSAP_NAMEFILTER Testp;
+ PSAP_NAMEFILTER Bestp;
+
+ /** Uppercase the server name **/
+
+ _strupr(ServerName);
+
+ /** Get ptr to the header entry for this servername **/
+
+ HashIndex = SapFilterCalcHash(ServerName);
+ Hdrp = &SapNameFilterHashTable[HashIndex];
+
+ /** Allocate memory for this entry **/
+
+ Filterp = SAP_MALLOC(SAP_NAMEFILTER_SIZE, "Alloc Sap Filter Entry");
+ if (Filterp == NULL)
+ return ERROR_NOT_ENOUGH_MEMORY;
+
+ /** Fill in this entry **/
+
+ memcpy(Filterp->ServerName, ServerName, SAP_OBJNAME_LEN);
+
+ /** Add this entry to the list **/
+
+ Testp = Hdrp->FirstEntry;
+ Bestp = NULL;
+ while (Testp) {
+
+ /** Compare the names **/
+
+ rc = SAP_NAMECMP(Testp->ServerName, ServerName);
+
+ /** If duplicate entry - return it **/
+
+ if (rc == 0) {
+ SAP_FREE(Filterp, "Dup filter entry");
+ return ERROR_DUP_NAME;
+ }
+
+ /** If past the name - break out **/
+
+ if (rc > 0)
+ break;
+
+ /**
+ This name is less than, save it as best to add after.
+ **/
+
+ Bestp = Testp;
+ Testp = Testp->Next;
+ }
+
+ /** Add this entry to the list **/
+
+ if (Bestp) {
+ Filterp->Next = Bestp->Next;
+ Bestp->Next = Filterp;
+ }
+ else {
+ Filterp->Next = Hdrp->FirstEntry;
+ Hdrp->FirstEntry = Filterp;
+ }
+ SapFilterCount++;
+
+ /** All Done OK **/
+
+ return 0;
+
+ /** **/
+
+ UNREFERENCED_PARAMETER(Flag);
+}
+
+
+/*++
+*******************************************************************
+ S a p S h o u l d I A d v e r t i s e B y N a m e
+
+ Routine Description:
+
+ If filtering is on, this routine is called to see
+ if the given server should be advertised.
+
+ Arguments:
+
+ ServerName = Ptr to server name to check
+
+ Return Value:
+
+ TRUE = YES
+ FALSE = NO
+*******************************************************************
+--*/
+
+BOOL
+SapShouldIAdvertiseByName(
+ PUCHAR ServerName)
+{
+ INT HashIndex;
+ INT rc;
+ PSAP_FILTERHDR Hdrp;
+ PSAP_NAMEFILTER Filterp;
+
+ /**
+ If we are using the PASS list and the list
+ is empty - say OK to advertise.
+
+ If we are using the DONT PASS list and the list
+ is empty - say DO NOT advertise.
+ **/
+
+ if (SapActiveFilter == SAPFILTER_PASSLIST) {
+ if (SapFilterCount == 0)
+ return TRUE;
+ }
+ else {
+ if (SapFilterCount == 0)
+ return FALSE;
+ }
+
+ /** Find the name in the table **/
+
+ /** Get ptr to the header entry for this servername **/
+
+ HashIndex = SapFilterCalcHash(ServerName);
+ Hdrp = &SapNameFilterHashTable[HashIndex];
+
+ /** Add this entry to the list **/
+
+ Filterp = Hdrp->FirstEntry;
+ while (Filterp) {
+
+ /** Compare the names **/
+
+ rc = SAP_NAMECMP(Filterp->ServerName, ServerName);
+
+ /** If it matches - break out - we found it **/
+
+ if (rc == 0)
+ break;
+
+ /** If past the name - break out as not found **/
+
+ if (rc > 0) {
+ Filterp = NULL;
+ break;
+ }
+
+ /**
+ This name is less than, go try the next name.
+ **/
+
+ Filterp = Filterp->Next;
+ }
+
+ /**
+ If we Found the name - check the list we are in:
+
+ PASS LIST: It is OK to advertise it
+
+ DONT PASS LIST: It is NOT OK to advertise it
+ **/
+
+ if (Filterp) {
+ if (SapActiveFilter == SAPFILTER_PASSLIST)
+ return TRUE;
+
+ /**
+ We are in the DONT PASS LIST -
+ CANNOT ADVERTISE THIS SERVER.
+ **/
+
+ return FALSE;
+ }
+
+ /**
+ We did NOT FIND THE NAME.
+
+ If we are using the:
+
+ PASS LIST - We CANNOT advertise the name.
+
+ DONT PASS LIST - We CAN advertise the name.
+ **/
+
+ if (SapActiveFilter == SAPFILTER_PASSLIST)
+ return FALSE;
+
+ /** DONT PASS LIST - Advertise the name **/
+
+ return TRUE;
+}
+
+
+/*++
+*******************************************************************
+ S a p S h o u l d I A d v e r t i s e B y C a r d
+
+ Routine Description:
+
+ Check the WAN status of a card to see if we
+ should advertise this server.
+
+ NOTE: This routine should only be called if
+ Cardptr->Wanflag is TRUE.
+
+ Arguments:
+
+ Cardptr = Ptr to card we are wanting to send on
+ EntryHasChanged = TRUE = Entry has changed since last advertise
+ FALSE = Entry has NOT changed since last advertise
+ (Only valid if Cardptr != NULL)
+
+ Return Value:
+
+ TRUE = YES
+ FALSE = NO
+*******************************************************************
+--*/
+
+BOOL
+SapShouldIAdvertiseByCard(
+ PSAP_CARD Cardptr,
+ BOOL EntryHasChanged)
+{
+ /**
+ If the WAN flag ways not to send anything, then we
+ should just say NO now.
+ **/
+
+ if (SapWanFilter == SAPWAN_NOTHING)
+ return FALSE;
+
+ /**
+ If Changes ONLY - then if this entry has not changed
+ then we say NO. If it has changed, then we will
+ send it.
+ **/
+
+ if (SapWanFilter == SAPWAN_CHANGESONLY) {
+ if (!EntryHasChanged)
+ return FALSE;
+ else
+ return TRUE;
+ }
+
+ /**
+ Else - we send EVERYTHING across the WAN.
+ **/
+
+ /** It is OK to advertise this name **/
+
+ return TRUE;
+}
+
+
+/*++
+*******************************************************************
+ S a p F i l t e r C a l c H a s h
+
+Routine Description:
+
+ Calculate the hash index on a name for an entry.
+
+Arguments:
+
+ ServerName = Ptr to name to calculate hash index for
+
+Return Value:
+
+ The hash index is returned.
+*******************************************************************
+--*/
+
+INT
+SapFilterCalcHash(
+ PUCHAR ServerName)
+{
+ INT HashIndex;
+
+ /** Add up all the bytes in the name **/
+
+ HashIndex = 0;
+ while (*ServerName) {
+ HashIndex += (INT)*ServerName;
+ ServerName++;
+ }
+
+ /** Divide by the size of the hash table **/
+
+ HashIndex = HashIndex % SAP_NAMEFILTER_HASHSIZE;
+
+ /** Return the hash index **/
+
+ return HashIndex;
+}
+
diff --git a/private/net/svcdlls/nwsap/server/globals.c b/private/net/svcdlls/nwsap/server/globals.c
new file mode 100644
index 000000000..a1b1cff1a
--- /dev/null
+++ b/private/net/svcdlls/nwsap/server/globals.c
@@ -0,0 +1,126 @@
+/*++
+
+Copyright (c) 1994 Microsoft Corporation
+Copyright (c) 1993 Micro Computer Systems, Inc.
+
+Module Name:
+
+ net\svcdlls\nwsap\server\globals.c
+
+Abstract:
+
+ These are the global variables for the NT SAP Agent
+
+Author:
+
+ Brian Walker (MCS) 06-15-1993
+
+Revision History:
+
+--*/
+
+#include "precomp.h"
+#pragma hdrstop
+
+/** Global Variables **/
+
+LIST_ENTRY SapRecvList;
+LIST_ENTRY SapFreeList;
+LIST_ENTRY SapWanRecvList;
+LIST_ENTRY SapWanFreeList;
+HANDLE SapRecvSem = NULL;
+HANDLE SapWanSem = NULL;
+HANDLE SapSendEvent = NULL;
+HANDLE SapWanEvent = NULL;
+SOCKET SapSocket = INVALID_SOCKET;
+PSAP_CARD SapCardHead = NULL;
+PSAP_CARD SapCardTail = NULL;
+PSAP_SERVER SapServHead = NULL;
+PSAP_SERVER SapServTail = NULL;
+INT SapMaxFreeBufs;
+INT SapNumFreeBufs;
+INT SapCurWorkerThreads; /* Current number worker threads */
+INT SapCurReceiveThreads; /* Current number receive threads */
+INT SapCurBackup; /* Num entries in SapRecvList */
+INT SapMaxBackup; /* Maximum entries in SapRecvList */
+INT SapNumCards; /* Num cards NWLink has */
+INT SapWanCurBackup;
+INT SapWanCurFree;
+INT SapWanMaxFree;
+INT SapMaxCardIndex;
+INT SapCardInitDone;
+INT SapSendPacketsBusy;
+INT SapAgainFlag;
+INT SapWorkerThreadWaiting;
+INT SapDontHopLans;
+INT SapAllowDuplicateServers;
+UCHAR SapNetNum[4];
+UCHAR SapNodeNum[6];
+CRITICAL_SECTION SapRecvCriticalSection;
+CRITICAL_SECTION SapFreeCriticalSection;
+CRITICAL_SECTION SapSendCriticalSection;
+CRITICAL_SECTION SapSendBusyCriticalSection;
+CRITICAL_SECTION SapThreadCountCriticalSection;
+CRITICAL_SECTION SapLpcThreadCountCriticalSection;
+CRITICAL_SECTION SapLpcClientCriticalSection;
+CRITICAL_SECTION SapMemoryCriticalSection;
+CRITICAL_SECTION SdmdCriticalSection;
+CRITICAL_SECTION SapCardlistCriticalSection;
+CRITICAL_SECTION SapWanRecvCriticalSection;
+CRITICAL_SECTION SapWanFreeCriticalSection;
+HANDLE SdmdSynchEvent;
+PHANDLE SapWanNotifyHandlesBuf = NULL;
+ULONG SdmdLockCount;
+ULONG SapAllocCount;
+BOOL SapChanged;
+INT SapWorkerStarting;
+DWORD SapLastWorkerStartTime;
+UCHAR SapZeros[] = "\x00\x00\x00\x00\x00\x00";
+ULONG SapCardlistLockCount;
+HANDLE SapCardlistSynchEvent;
+
+ULONG SapThreadCount;
+HANDLE SapThreadEvent;
+
+DWORD SapError;
+DWORD SapEventId;
+
+/** LPC Variables **/
+
+HANDLE SapXsLpcPortHandle = NULL;
+LIST_ENTRY SapLpcClientList;
+ULONG SapNumLpcClients;
+
+ULONG SapLpcNumWorkers;
+ULONG SapLpcMaxWorkers;
+HANDLE SapLpcThreadEvent;
+SAP_FILTERHDR SapNameFilterHashTable[SAP_NAMEFILTER_HASHSIZE];
+ULONG SapFilterCount = 0;
+INT SapRecheckCount;
+
+/**
+ These are all configurable from the registry. They
+ are set in registry.c when the SAP Agent is started.
+**/
+
+INT SapMaxFreeBufs; /* Max bufs on free list at once */
+INT SapNumRecvThreads; /* Num receive threads to start */
+INT SapNumWorkerThreads; /* Num worker threads to start */
+INT SapSendMinutes; /* Num minutes between sends */
+INT SapNumArrayEntries; /* Initial size of SDMD array */
+INT SapTimeoutInterval; /* Num minutes before entries timeout */
+INT SapMaxEverWorkerThreads; /* Max ever worker threads we can have */
+INT SapNewWorkerThreshhold; /* Worker list max until start new thread */
+INT SapNewWorkerTimeout; /* After new worker - wait before start another */
+INT SapHashTableSize; /* Num entries in the HASH Table */
+INT SapRespondForInternal; /* Respond for other internal servers on this machine */
+INT SapActiveFilter;
+ULONG SapWanFilter;
+INT SapNumWanNotifyThreads;
+INT SapRecheckAllCardsTime;
+INT SapRecvDelayOnMallocFail; // seconds to delay after malloc fails
+INT SapRecvDelayOnNetError; // seconds to delay after recv error
+
+INT SapDelayRespondToGeneral; // milliseconds to delay before responding to
+ // general service request for specific type
+
diff --git a/private/net/svcdlls/nwsap/server/makefile b/private/net/svcdlls/nwsap/server/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/net/svcdlls/nwsap/server/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/net/svcdlls/nwsap/server/network.c b/private/net/svcdlls/nwsap/server/network.c
new file mode 100644
index 000000000..cb04131fb
--- /dev/null
+++ b/private/net/svcdlls/nwsap/server/network.c
@@ -0,0 +1,1615 @@
+/*++
+
+Copyright (c) 1994 Microsoft Corporation
+Copyright (c) 1993 Micro Computer Systems, Inc.
+
+Module Name:
+
+ net\svcdlls\nwsap\server\network.c
+
+Abstract:
+
+ These are the network interface routines for the NT SAP Agent
+
+Author:
+
+ Brian Walker (MCS) 06-15-1993
+
+Revision History:
+
+--*/
+
+#include "precomp.h"
+#pragma hdrstop
+
+/** This is the address we send to **/
+
+#define SAP_ADDRESS_LENGTH 15
+INT SapBroadcastAddressLength = SAP_ADDRESS_LENGTH;
+UCHAR SapBroadcastAddress[] = {
+ AF_IPX, 0, /* Address Family */
+ 0x00, 0x00, 0x00, 0x00, /* Dest. Net Number */
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* Dest. Node Number */
+ 0x04, 0x52, /* Dest. Socket */
+ 0x04 /* Packet type */
+};
+
+/** Internal Function Prototypes **/
+
+INT
+SapAddSendEntry(
+ PSAP_RECORD Entry,
+ PSAP_PKTLIST Plist,
+ USHORT HopInc);
+
+
+/*++
+*******************************************************************
+ S a p N e t w o r k I n i t
+
+Routine Description:
+
+ This routine initializes the network portion of the Sap Agent.
+
+Arguments:
+
+ None
+
+Return Value:
+
+ 0 = OK
+ Else = Error
+*******************************************************************
+--*/
+
+INT
+SapNetworkInit(
+ VOID)
+{
+ INT rc;
+ INT Length;
+ BOOL Value;
+ SOCKADDR_IPX Bindaddr;
+ PSAP_CARD Cardptr;
+ INT Cardnum;
+ IPX_ADDRESS_DATA Addrdata;
+
+ /** Open an IPX datagram socket **/
+
+ SapSocket = socket(AF_IPX, SOCK_DGRAM, NSPROTO_IPX);
+ if (SapSocket == INVALID_SOCKET) {
+ SapError = h_errno;
+ SapEventId = NWSAP_EVENT_SOCKET_FAILED;
+ return -1;
+ }
+
+ /** Enable sending of broadcasts **/
+
+ Value = TRUE;
+ rc = setsockopt(SapSocket, SOL_SOCKET, SO_BROADCAST, (PVOID)&Value, sizeof(INT));
+ if (rc == -1) {
+ SapError = h_errno;
+ SapEventId = NWSAP_EVENT_SETOPTBCAST_FAILED;
+ return -1;
+ }
+
+ /** Setup the address to bind to **/
+
+ memset(&Bindaddr, 0, sizeof(SOCKADDR_IPX));
+ Bindaddr.sa_family = AF_IPX;
+ Bindaddr.sa_socket = htons(NWSAP_SAP_SOCKET);
+
+ /** Bind to the socket **/
+
+ rc = bind(SapSocket, (PSOCKADDR)&Bindaddr, sizeof(SOCKADDR_IPX));
+ if (rc == -1) {
+ SapError = h_errno;
+ SapEventId = NWSAP_EVENT_BIND_FAILED;
+ IF_DEBUG(INITIALIZATION_ERRORS) {
+ SS_PRINT(("NWSAP: Error binding to SAP socket: error = %d\n", SapError));
+ }
+ return -1;
+ }
+
+ /** Get the bound address and save off the net/node numbers **/
+
+ Length = sizeof(SOCKADDR_IPX);
+ rc = getsockname(SapSocket, (PSOCKADDR)&Bindaddr, &Length);
+ if (rc == -1) {
+ SapError = h_errno;
+ SapEventId = NWSAP_EVENT_GETSOCKNAME_FAILED;
+ IF_DEBUG(INITIALIZATION_ERRORS) {
+ SS_PRINT(("NWSAP: Error getting socket name (address): error = %d\n", SapError));
+ }
+ return -1;
+ }
+
+ /** **/
+
+ IF_DEBUG(INITIALIZATION) {
+ SS_PRINT(("NWSAP: Bound Address is 0x%02x:0x%02x:0x%02x:0x%02x - 0x%02x:0x%02x:0x%02x:0x%02x:0x%02x:0x%02x - 0x%02x:0x%02x\n",
+ (UCHAR)Bindaddr.sa_netnum[0],
+ (UCHAR)Bindaddr.sa_netnum[1],
+ (UCHAR)Bindaddr.sa_netnum[2],
+ (UCHAR)Bindaddr.sa_netnum[3],
+ (UCHAR)Bindaddr.sa_nodenum[0],
+ (UCHAR)Bindaddr.sa_nodenum[1],
+ (UCHAR)Bindaddr.sa_nodenum[2],
+ (UCHAR)Bindaddr.sa_nodenum[3],
+ (UCHAR)Bindaddr.sa_nodenum[4],
+ (UCHAR)Bindaddr.sa_nodenum[5],
+ (UCHAR)*(PUCHAR)&Bindaddr.sa_socket,
+ (UCHAR)*((PUCHAR)&Bindaddr.sa_socket + 1)));
+ }
+
+ /** Save off the net/node in global variables **/
+
+ SAP_COPY_NETNUM(SapNetNum, Bindaddr.sa_netnum);
+ SAP_COPY_NODENUM(SapNodeNum, Bindaddr.sa_nodenum);
+
+ /** Set the extended address option **/
+
+ Length = 1;
+ rc = setsockopt(
+ SapSocket, /* Socket Handle */
+ NSPROTO_IPX, /* Option Level */
+ IPX_EXTENDED_ADDRESS, /* Option Name */
+ (PUCHAR)&Length, /* Ptr to on/off flag */
+ sizeof(INT)); /* Length of flag */
+
+ if (rc == -1) {
+ SapError = h_errno;
+ SapEventId = NWSAP_EVENT_OPTEXTENDEDADDR_FAILED;
+ IF_DEBUG(INITIALIZATION_ERRORS) {
+ SS_PRINT(("NWSAP: Error setting EXTENDED ADDRESS option: error = %d\n", SapError));
+ }
+ return -1;
+ }
+
+ /** Get the max number of cards there are **/
+
+ Length = sizeof(INT);
+ rc = getsockopt(
+ SapSocket, /* Socket Handle */
+ NSPROTO_IPX, /* Option Level */
+ IPX_MAX_ADAPTER_NUM, /* Option Name */
+ (PUCHAR)&SapMaxCardIndex, /* Ptr to on/off flag */
+ &Length); /* Length of flag */
+
+ if (rc == -1) {
+ SapError = h_errno;
+ SapEventId = NWSAP_EVENT_OPTMAXADAPTERNUM_ERROR;
+
+ IF_DEBUG(INITIALIZATION_ERRORS) {
+ SS_PRINT(("NWSAP: Error getting MAX_ADAPTER_NUM from transport: error = %d\n", SapError));
+ }
+ return -1;
+ }
+
+ /** **/
+
+ IF_DEBUG(INITIALIZATION) {
+ SS_PRINT(("NWSAP: Max number of cards from xport = %d\n", SapMaxCardIndex));
+ }
+
+ /** Get the list of cards we have **/
+
+ Cardnum = 0;
+ while (Cardnum != SapMaxCardIndex) {
+
+ /** Fill in this entry **/
+
+ Addrdata.adapternum = Cardnum;
+ Length = sizeof(IPX_ADDRESS_DATA);
+ rc = getsockopt(
+ SapSocket,
+ NSPROTO_IPX,
+ IPX_ADDRESS,
+ (PCHAR)&Addrdata,
+ &Length);
+
+ /** If error - just skip it **/
+
+ if (rc) {
+ IF_DEBUG(INITIALIZATION_ERRORS) {
+ SS_PRINT(("NWSAP: Error getting card info: h_errno = %d: Anum = %d\n", h_errno, Cardnum));
+ }
+ Cardnum++;
+ continue;
+ }
+
+ /** If this card not active - skip it **/
+
+ if (!Addrdata.status) {
+ IF_DEBUG(INITIALIZATION_ERRORS) {
+ SS_PRINT(("NWSAP: Netinit: Card info says card is down: h_errno = %d: Anum = %d\n", h_errno, Cardnum));
+ }
+ Cardnum++;
+ continue;
+ }
+
+ /** If this card already in the list - skip it **/
+
+ ACQUIRE_CARDLIST_WRITERS_LOCK(rc, "Cardinit");
+ Cardptr = SapCardHead;
+ while (Cardptr) {
+ if (Cardptr->Number == Addrdata.adapternum)
+ break;
+ Cardptr = Cardptr->Next;
+ }
+
+ /**
+ If we found the card, then it was added by the
+ WAN notify thread already. Just skip to the next
+ adapter.
+ **/
+
+ if (Cardptr) {
+ RELEASE_CARDLIST_WRITERS_LOCK("Cardinit X0");
+ Cardnum++;
+ continue;
+ }
+
+ /** Allocate an entry for this card **/
+
+ Cardptr = SAP_MALLOC(SAP_CARD_SIZE, "ALLOC CARD");
+ if (Cardptr == NULL) {
+ RELEASE_CARDLIST_WRITERS_LOCK("Cardinit X1");
+ SapError = 0;
+ SapEventId = NWSAP_EVENT_CARDMALLOC_FAILED;
+ return -1;
+ }
+
+ /** Fill in the values **/
+
+ SAP_COPY_NETNUM(Cardptr->Netnum, Addrdata.netnum);
+ SAP_COPY_NODENUM(Cardptr->Nodenum, Addrdata.nodenum);
+ Cardptr->Linkspeed = Addrdata.linkspeed;
+ Cardptr->Wanflag = Addrdata.wan;
+ Cardptr->Maxpkt = Addrdata.maxpkt;
+ Cardptr->Number = Cardnum;
+ Cardptr->ReqCount = 0;
+
+ /** For building with **/
+
+ Cardptr->Plist.Curnum = 0;
+ Cardptr->Plist.Curpkt = NULL;
+ Cardptr->Plist.Curptr = NULL;
+ Cardptr->Plist.PktHead = NULL;
+ Cardptr->Plist.PktTail = NULL;
+ Cardptr->Plist.NumPkts = 0;
+
+ /** **/
+
+#if DBG
+ IF_DEBUG(INITIALIZATION) {
+ SS_PRINT(("Card %d\n", Cardnum));
+ SapDumpMem(Cardptr->Netnum, 10, "NET/NODE");
+ }
+#endif
+
+ /** Put this entry on the end of the list **/
+
+ Cardptr->Next = NULL;
+ if (SapCardHead)
+ SapCardTail->Next = Cardptr;
+ else
+ SapCardHead = Cardptr;
+
+ SapCardTail = Cardptr;
+ SapNumCards++;
+ RELEASE_CARDLIST_WRITERS_LOCK("Cardinit X2");
+
+ /** Goto the next card number **/
+
+ Cardnum++;
+ }
+
+ /**
+ If there are no cards, that is OK. We allow that because
+ if all we have is a WAN adapter that is not up yet, then
+ we will come up later when the WAN adapter comes up.
+ **/
+
+ /** Initialization is OK **/
+
+ return 0;
+}
+
+
+/*++
+*******************************************************************
+ S a p N e t w o r k S h u t d o w n
+
+Routine Description:
+
+ When we are terminating, this routine will clean
+ up everything.
+
+Arguments:
+
+ None
+
+Return Value:
+
+ Nothing
+*******************************************************************
+--*/
+
+VOID
+SapNetworkShutdown(
+ VOID)
+{
+ PSAP_CARD Cardptr;
+ PSAP_CARD NCardptr;
+
+ /** Free the card list **/
+
+ Cardptr = SapCardHead;
+ while (Cardptr) {
+ NCardptr = Cardptr->Next;
+ SAP_FREE(Cardptr, "CARD NET SHUTDOWN");
+ Cardptr = NCardptr;
+ }
+
+ /** All Done **/
+
+ return;
+}
+
+
+/*++
+*******************************************************************
+ S a p S e n d P a c k e t s
+
+Routine Description:
+
+ This routine sends a SAP advertise for all servers
+ needing one.
+
+Arguments:
+
+ Flag = 0 - This is from SendThread
+ 1 - This is from SapAddAdvertise
+ 2 - Shutting down - send all with HOP = 16.
+
+Return Value:
+
+ 0 = OK
+ Else = Error
+*******************************************************************
+--*/
+
+INT
+SapSendPackets(
+ INT Flag)
+{
+ PSAP_RECORD Entry;
+ PSAP_RECORD NextEntry;
+ PSAP_RECORD HeadEntry;
+ PSAP_RECORD NextHeadEntry;
+ SAP_RECORD Myentry;
+ PSAP_SERVER Servp;
+ PSAP_SERVER Nextp;
+ PSAP_SERVER Backp;
+ PSAP_CARD Cardptr;
+ PSAP_PKTENTRY Pktp;
+ PSAP_PKTENTRY NPktp;
+ INT rc;
+ BOOL doit;
+ UCHAR DestAddr[SAP_ADDRESS_LENGTH];
+
+ /**
+ If we are already in here, then we need to mark that
+ we caught ourselves here and leave.
+
+ We will save who called us. If we already have saved
+ off a call from the main advertise thread or the
+ cleanup code, then we will not overwrite that.
+ **/
+
+ ACQUIRE_SENDBUSY_LOCK();
+ if (SapSendPacketsBusy) {
+ if (SapAgainFlag == -1)
+ SapAgainFlag = Flag; /* Save who called in */
+ else if (SapAgainFlag == 1)
+ SapAgainFlag= Flag;
+ RELEASE_SENDBUSY_LOCK();
+ return 0;
+ }
+ SapSendPacketsBusy = 1;
+ SapAgainFlag = -1;
+ RELEASE_SENDBUSY_LOCK();
+
+ /** **/
+
+send_again:
+ ACQUIRE_CARDLIST_READERS_LOCK("SapSendPackets");
+
+ /** Lock the server send table **/
+
+ ACQUIRE_SENDTABLE_LOCK();
+
+ /** Send all the packets we need to **/
+
+ Servp = SapServHead;
+
+ /**
+ Go thru the list and build all that we can.
+ We also update these in the database to make sure that
+ we don't timeout our internal entries.
+ **/
+
+ Backp = NULL;
+ while (Servp) {
+
+ /** If from shutdown set hop to 16 now **/
+
+ if (Flag == 2)
+ Servp->Hopcount = htons(16);
+
+ /** Go add this entry for all cards **/
+
+ Nextp = Servp->Next;
+
+ /** Figure whether to do this entry or not **/
+
+ if (Flag == 1) {
+ if (Servp->Changed)
+ doit = TRUE;
+ else
+ doit = FALSE;
+ }
+ else
+ doit = TRUE;
+
+ /**
+ If we should do it this entry, then add it to the list.
+ BUT check the filters first.
+ **/
+
+ if (doit) {
+
+ /** Go send it for all cards **/
+
+ Cardptr = SapCardHead;
+ while (Cardptr) {
+
+ /**
+ Check the filter here. If we should
+ advertise this entry or not. We do not
+ check the name since this is an internal server
+ and we must advertise for it. BUT we do check
+ for WAN filtering.
+ **/
+
+ if (Cardptr->Wanflag) {
+ doit = SapShouldIAdvertiseByCard(
+ Cardptr,
+ Servp->Changed);
+ }
+ else
+ doit = TRUE;
+
+ /** Copy this to an entry **/
+
+ if (doit) {
+ SAP_COPY_SERVNAME(Myentry.ServName, Servp->ServerName);
+ Myentry.ServType = ntohs(Servp->ServerType);
+ Myentry.HopCount = ntohs(Servp->Hopcount);
+ SAP_COPY_ADDRESS(Myentry.ServAddress, Servp->Address);
+
+ /** Add this entry to the send list **/
+
+ SapAddSendEntry(&Myentry, &Cardptr->Plist, 0);
+ }
+
+ /** Goto the next card entry **/
+
+ Cardptr = Cardptr->Next;
+ }
+ }
+
+ /** This entry not changed anymore **/
+
+ Servp->Changed = FALSE;
+
+ /** Update this entry in the database **/
+
+ SdmdUpdateEntry(
+ Servp->ServerName, /* Server name */
+ ntohs(Servp->ServerType), /* Server Type */
+ Servp->Address, /* Server Address */
+ ntohs(Servp->Hopcount), /* Server Hopcount */
+ CARDRET_MYSELF, /* Card number */
+ SapZeros, /* My address (don't care)*/
+ FALSE);
+
+ /**
+ If going away - delete the entry from the
+ advertise list.
+ **/
+
+ if (Servp->Hopcount == htons(16)) {
+
+ /** Take this entry out of the list **/
+
+ if (Backp)
+ Backp->Next = Nextp;
+ else
+ SapServHead = Nextp;
+
+ if (Servp == SapServTail)
+ SapServTail = Backp;
+
+ /** Free this entry **/
+
+ SAP_FREE(Servp, "Advertised deleted adv srv and deleted");
+ }
+ else {
+
+ /** Save ptr to this entry as back entry **/
+
+ Backp = Servp;
+ }
+
+ /** Goto the next entry **/
+
+ Servp = Nextp;
+ }
+
+ /** Release the server send table **/
+
+ RELEASE_SENDTABLE_LOCK();
+
+ /**
+ Now we need to add all the SAP's that are on different nets.
+ For each card we need to advertise SAP's that came in
+ on other cards.
+
+ We only send the actual packets if there is more then 1 card.
+ If there is only 1 card - we go thru so we can delete servers
+ that have gone away.
+ **/
+
+ /** Lock the database **/
+
+ ACQUIRE_WRITERS_LOCK(rc,"Send Packets");
+ if (rc) {
+
+ IF_DEBUG(ERRORS) {
+ SS_PRINT(("NWSAP: SendPackets: Error getting writers lock\n"));
+ }
+
+ goto send_the_packets; /* Error on get writers lock */
+ }
+
+ /** Get ptr to the head of the type list **/
+
+ HeadEntry = GETPTRFROMINDEX(SdmdLists[SAP_TYPELIST_INDEX].Flink);
+ while (HeadEntry && (HeadEntry->ServType != 0xFFFF)) {
+
+ /** Save ptr to next head entry **/
+
+ NextHeadEntry = GETPTRFROMINDEX(HeadEntry->Links[SAP_TYPELIST_INDEX].Flink);
+
+ /** Go thru this sublist **/
+
+ Entry = GETPTRFROMINDEX(HeadEntry->Links[SAP_SUBLIST_INDEX].Flink);
+ while (Entry) {
+
+ /** If from shutdown - set hopcount now to 16 **/
+
+ if (Flag == 2)
+ Entry->HopCount = 16;
+
+ /** If internal or only one card - skip it **/
+
+ if ((Entry->Internal) || (SapNumCards == 1))
+ goto do_next_entry;
+
+ /** Figure whether to do this entry or not **/
+
+ if (Flag == 1)
+ doit = Entry->Changed;
+ else
+ doit = TRUE;
+
+ /**
+ If we have decided to advertise this server, then
+ check the filter table to see if we can. We
+ check the WAN flags later on.
+ **/
+
+ if (doit) {
+ doit = SapShouldIAdvertiseByName(Entry->ServName);
+ }
+
+ /**
+ If this is from advertise or update, only send
+ ones that are changed.
+ **/
+
+ if (doit) {
+
+ /** Go add this entry for all cards **/
+
+ Cardptr = SapCardHead;
+ while (Cardptr) {
+
+ /**
+ Never advertise on same card as this
+ came to us from.
+ **/
+
+ if (Entry->CardNumber == Cardptr->Number) {
+ Cardptr = Cardptr->Next;
+ continue;
+ }
+
+ /**
+ If this is a WAN adapter - then check
+ the name to see if we should send advertisments
+ across the WAN or not.
+
+ Check the filter for this server as to
+ whether we should advertise this one or not.
+ We have already checked the name, we just
+ check the WAN status now.
+ **/
+
+ if (Cardptr->Wanflag) {
+ doit = SapShouldIAdvertiseByCard(
+ Cardptr,
+ Entry->Changed);
+ }
+ else {
+
+ /** This is a LAN card we are sending on **/
+
+ /**
+ We have 4 cases at advertise time:
+
+ 1. Advertise came on a LAN - Sending to a WAN
+ 2. Advertise came on a LAN - Sending to a LAN
+ 3. Advertise came on a WAN - Sending on a WAN
+ 4. Advertise came on a WAN - Sending on a LAN
+
+ Cases 1,3,4 are always OK. But case 2 is special
+ in that if the IPX layer is not a router here then
+ we might advertise a server across to a different
+ card that there is no route to.
+
+ SapDontHopLans is TRUE if we should not advertise
+ in the case 2.
+
+ If this is entry NOT from a wan and we don't
+ hop lan entries - do not advertise this entry.
+ **/
+
+ if ((!Entry->FromWan) && (SapDontHopLans))
+ doit = FALSE;
+ else
+ doit = TRUE;
+ }
+
+ /** If not for this card - add it in **/
+
+ if (doit) {
+ SapAddSendEntry(Entry, &Cardptr->Plist, 1);
+ }
+
+ /** Goto the next card entry **/
+
+ Cardptr = Cardptr->Next;
+ }
+ }
+
+ /** This entry is updated now **/
+
+do_next_entry:
+ Entry->Changed = FALSE;
+
+ /** Get ptr to the next entry **/
+
+ NextEntry = GETPTRFROMINDEX(Entry->Links[SAP_SUBLIST_INDEX].Flink);
+
+ /** If this entry is gone - toss it now **/
+
+ if (Entry->HopCount == 16)
+ SdmdFreeEntry(Entry);
+
+ /** Goto the next entry **/
+
+ Entry = NextEntry;
+ }
+
+ /** Goto the next type **/
+
+ HeadEntry = NextHeadEntry;
+ }
+
+ /** We can release the lock now **/
+
+ RELEASE_WRITERS_LOCK("Send Packets X1");
+
+ /**
+ Now we have built all the packets. We need to send all the
+ packets. Each card has a list of packets to send.
+ **/
+
+send_the_packets:
+ Cardptr = SapCardHead;
+ while (Cardptr) {
+
+ /** If we have a partial - finish it up **/
+
+ if (Cardptr->Plist.Curnum) {
+
+ /** Add this packet to the list **/
+
+ if (Cardptr->Plist.PktHead)
+ Cardptr->Plist.PktTail->Next = Cardptr->Plist.Curpkt;
+ else
+ Cardptr->Plist.PktHead = Cardptr->Plist.Curpkt;
+ Cardptr->Plist.PktTail = Cardptr->Plist.Curpkt;
+ Cardptr->Plist.NumPkts++;
+ }
+
+ /** Set the network number **/
+
+ memcpy(DestAddr, SapBroadcastAddress, SAP_ADDRESS_LENGTH);
+ SAP_COPY_NETNUM(DestAddr+2, Cardptr->Netnum);
+
+ /** Go send all the packets **/
+
+ Pktp = Cardptr->Plist.PktHead;
+
+ /** Reset all for next time **/
+
+ Cardptr->Plist.PktHead = NULL;
+ Cardptr->Plist.PktTail = NULL;
+ Cardptr->Plist.Curnum = 0;
+ Cardptr->Plist.Curptr = NULL;
+ Cardptr->Plist.Curpkt = NULL;
+
+ /** Send this packet **/
+
+ while (Pktp) {
+
+ /** Get ptr to next packet and get send length **/
+
+ NPktp = Pktp->Next;
+
+ /** Send the packet **/
+
+ rc = sendto(
+ SapSocket,
+ (PVOID)(Pktp->Buffer),
+ Pktp->SendLength,
+ 0,
+ (PSOCKADDR)DestAddr,
+ SapBroadcastAddressLength);
+
+ /** Check for errors **/
+
+ if (rc == -1) {
+ IF_DEBUG(ERRORS) {
+ SS_PRINT(("SENDPACKETS: Error sending SAP: h_errno = %d\n", h_errno));
+ }
+ }
+
+ /** Free this packet **/
+
+ SAP_FREE(Pktp, "Sent Packet for Card");
+
+ /**
+ If there are more - insert delay here. We do not
+ check for starting other worker threads, because we
+ are never called from a worker thread here.
+ **/
+
+ if (NPktp) {
+ NWSAP_SEND_DELAY();
+ }
+
+ /** Goto the next packet **/
+
+ Pktp = NPktp;
+ }
+
+ /** Goto the next card entry **/
+
+ Cardptr = Cardptr->Next;
+ }
+ RELEASE_CARDLIST_READERS_LOCK("SapSendPackets");
+
+ /**
+ Check if we were called while we were in here
+ **/
+
+ ACQUIRE_SENDBUSY_LOCK();
+ if (SapAgainFlag != -1) {
+ Flag = SapAgainFlag;
+ SapAgainFlag = -1; /* Mark not here again */
+
+ /** If from main send - go send again **/
+
+ if (Flag == 0) {
+ RELEASE_SENDBUSY_LOCK();
+ goto send_again;
+ }
+
+ /** If from advertise - just mark changed **/
+
+ if (Flag == 1)
+ SapChanged = 1;
+
+ /** Else it is OK to leave **/
+ }
+
+ /** We are OK to leave **/
+
+ SapSendPacketsBusy = 0;
+ RELEASE_SENDBUSY_LOCK();
+
+ /** All Done **/
+
+ return 0;
+}
+
+
+/*++
+*******************************************************************
+ S a p S e n d F o r T y p e s
+
+Routine Description:
+
+ This routine will find all servers of the given type (0xFFFF
+ means all types) and send the records for all matching servers
+ to the remote address given.
+
+Arguments:
+
+ ServerType = Type of server to send for (0xFFFF = all)
+ RemoteAddress = Address to send to
+ RemoteAddressLength = Length of the Remote Address
+ Cardnum = Card number request was received on
+ CARDRET_INTERNAL = 0xFF = We got this
+ request from an internal process. We
+ should only respond for services that are
+ local on this machine.
+ Bcast = 0 = Unicast request packet
+ Else = Broadcast request packet
+ WanFlag = TRUE = Card recv'd from is WAN
+ FALSE = Card recv'd from is LAN
+
+Return Value:
+
+ 0 = OK
+ Else = Terminate this worker thread
+*******************************************************************
+--*/
+
+INT
+SapSendForTypes(
+ USHORT ServerType,
+ PUCHAR RemoteAddress,
+ INT RemoteAddressLength,
+ INT Cardnum,
+ UCHAR Bcast,
+ BOOL WanFlag)
+{
+ INT rc;
+ PSAP_RECORD Entry;
+ PSAP_RECORD HeadEntry;
+ SAP_PKTLIST Plist;
+ PSAP_PKTENTRY Pktp;
+ PSAP_PKTENTRY NPktp;
+ BOOL doit;
+ BOOL started_new_worker;
+
+ /** Zero out the plist **/
+
+ memset(&Plist, 0, sizeof(SAP_PKTLIST));
+
+ /** Find the server type we want **/
+
+ ACQUIRE_READERS_LOCK("Send For Types A1");
+
+ /**
+ Get a pointer to the first entry in the typelist. If
+ this is for wildcards, then this is the entry we want.
+
+ If not for wildcards, then find the first type that this is for.
+ If not found - just leave.
+ **/
+
+ HeadEntry = GETPTRFROMINDEX(SdmdLists[SAP_TYPELIST_INDEX].Flink);
+
+ if (ServerType != 0xFFFF) {
+ while (HeadEntry && (HeadEntry->ServType < ServerType)) {
+ HeadEntry = GETPTRFROMINDEX(HeadEntry->Links[SAP_TYPELIST_INDEX].Flink);
+ }
+ }
+
+ /**
+ Go build all the entries we can.
+ **/
+
+ while (HeadEntry) {
+
+ /** If we are into the free entries - bail out **/
+
+ if (HeadEntry->ServType == 0xFFFF)
+ break;
+
+ /**
+ If not for wildcards and it is another kind of server
+ type - then bail out.
+ **/
+
+ if (ServerType != 0xFFFF) {
+ if (HeadEntry->ServType != ServerType)
+ break;
+ }
+
+ /**
+ We have an entry we might want to send back. If the
+ service for this entry is on the same network as the
+ machine that made this request - do not send it back.
+ It will respond for itself.
+
+ If this request was made by a process on this local machine,
+ then we should only respond for internal servers. But if the
+ request was unicast - then we need to send for ALL servers.
+
+ OTHERWISE - Build the entry into the packet.
+ **/
+
+ Entry = GETPTRFROMINDEX(HeadEntry->Links[SAP_SUBLIST_INDEX].Flink);
+ while (Entry) {
+
+ /** **/
+
+ doit = FALSE;
+ if (Cardnum == CARDRET_INTERNAL) {
+ if (!Bcast)
+ doit = TRUE;
+ else if (Entry->Internal)
+ doit = TRUE; /* Bcast = respond for internal srvs only */
+ }
+ else {
+
+ /**
+ If entry is an internal server - return it. If not
+ then we need only send back for services that are
+ on different cards from where the request came.
+
+ BUT if the request is unicast - always send back
+ everything.
+ **/
+
+ if (!Bcast)
+ doit = TRUE;
+ else if (Entry->Internal)
+ doit = TRUE;
+ else if (Entry->CardNumber != Cardnum)
+ doit = TRUE;
+
+ /**
+ If this server is NOT internal to us AND the
+ request is from a LAN card AND this entry is
+ a LAN entry AND we do not do LAN-LAN routing -
+ then do not send this entry if the request
+ came in on a different card then the server
+ advertised us on.
+ **/
+
+ if (SapDontHopLans &&
+ (!Entry->Internal) && /* Entry not internal */
+ (!Entry->FromWan) && /* Entry from LAN card */
+ (Entry->CardNumber != Cardnum) &&
+ (!WanFlag)) { /* Request from a LAN card */
+
+ doit = FALSE;
+ }
+ }
+
+ /** Add the entry to the list if we need to **/
+
+ if (doit) {
+
+ /**
+ Check the name to see if we can send this
+ name. The WAN status does not matter
+ because this is a response.
+ **/
+
+ doit = SapShouldIAdvertiseByName(Entry->ServName);
+
+ /** Add the entry **/
+
+ if (doit) {
+ if (SapAddSendEntry(Entry, &Plist, 0))
+ break;
+ }
+ }
+
+ /** Get ptr to the next entry **/
+
+ Entry = GETPTRFROMINDEX(Entry->Links[SAP_SUBLIST_INDEX].Flink);
+ }
+
+ /** Goto the next head entry **/
+
+ HeadEntry = GETPTRFROMINDEX(HeadEntry->Links[SAP_TYPELIST_INDEX].Flink);
+ }
+
+ /** We can release the lock now **/
+
+ RELEASE_READERS_LOCK("Send For Types X2");
+
+ /**
+ If we have a partial packet - finish it
+ up now.
+ **/
+
+ if (Plist.Curnum) {
+
+ /** Add this packet to the list **/
+
+ if (Plist.PktHead)
+ Plist.PktTail->Next = Plist.Curpkt;
+ else
+ Plist.PktHead = Plist.Curpkt;
+ Plist.PktTail = Plist.Curpkt;
+ Plist.NumPkts++;
+ }
+
+ /**
+ We are now ready to send packets to the client that requested
+ them. If there is more the one packet, then I am going to delay
+ 55 ms between each packet.
+
+ In order not to clog myself up, I check here to see
+ if there is someone else waiting on the worker queue. If not, then
+ I startup a new worker thread. When I get done, I will check to
+ see if I need to kill myself.
+ **/
+
+ started_new_worker = 0;
+ if (Plist.NumPkts > 1) {
+
+ /** If no other threads are waiting - start a new worker **/
+
+ if (SapWorkerThreadWaiting == 0) {
+ if (!SapStartWorkerThread()) {
+ started_new_worker = 1; /* We started a new one */
+ }
+ }
+ }
+
+ /** Go send all the packets **/
+
+ Pktp = Plist.PktHead;
+
+ /** Send this packet **/
+
+ //
+ // If we need to delay responding to General Service Request, do
+ // so here but only if the client is not asking for all services
+ // since this is probably a router coming up and asking us to dump
+ // our sap table.
+ //
+
+ if ( (Pktp != NULL) &&
+ (ServerType != 0xFFFF) &&
+ (SapDelayRespondToGeneral > 0) ) {
+
+ Sleep( SapDelayRespondToGeneral );
+ }
+
+ while (Pktp) {
+
+ /** Get ptr to next packet and get send length **/
+
+ NPktp = Pktp->Next;
+
+ /** Send the packet **/
+
+ rc = sendto(
+ SapSocket,
+ (PVOID)(Pktp->Buffer),
+ Pktp->SendLength,
+ 0,
+ (PSOCKADDR)RemoteAddress,
+ RemoteAddressLength);
+
+ /** Check for errors **/
+
+ if (rc == -1) {
+ IF_DEBUG(ERRORS) {
+ SS_PRINT(("NWSAP: SENDFORTYPES: Error sending SAP: h_errno = %d\n", h_errno));
+ }
+ }
+
+ /** Free this packet **/
+
+ SAP_FREE(Pktp, "SendforTypes pkt release");
+
+ /**
+ If there are more - insert delay here.
+ **/
+
+ if (NPktp) {
+ NWSAP_SEND_DELAY();
+ }
+
+ /** Goto the next packet **/
+
+ Pktp = NPktp;
+ }
+
+ /**
+ If we started a new worker, then check if any other
+ worker threads are waiting on the worker queue. If so, kill
+ my thread now. If not, then just return back to keep going.
+ **/
+
+ if (started_new_worker) {
+ if (SapWorkerThreadWaiting != 0) {
+ return -1; /* Terminate this thread */
+ }
+ }
+
+ /** Return nothing to send back **/
+
+ return 0;
+}
+
+
+/*++
+*******************************************************************
+ S a p S e n d G e n e r a l R e q u e s t
+
+Routine Description:
+
+ This routine send a general request to broadcast.
+
+Arguments:
+
+ Flag = 0 - This is from SendThread
+ 1 - This is from SapAddAdvertise
+ NetNum = Ptr to 4 byte network number to send to
+ (NULL = Send to all local nets)
+
+Return Value:
+
+ 0 = OK
+ Else = Error
+*******************************************************************
+--*/
+
+INT
+SapSendGeneralRequest(
+ INT Flag,
+ PUCHAR NetNum)
+{
+ INT rc;
+ INT SendLength;
+ SAP_REQUEST SendBuffer;
+ UCHAR DestAddr[SAP_ADDRESS_LENGTH];
+
+ /** Build the packet **/
+
+ SendBuffer.QueryType = htons(SAPTYPE_GENERAL_SERVICE_QUERY);
+ SendBuffer.ServerType = 0xFFFF; /* All types */
+ SendLength = SAP_REQUEST_SIZE;
+
+ /** Set the address to send to **/
+
+ memcpy(DestAddr, SapBroadcastAddress, SAP_ADDRESS_LENGTH);
+ if (NetNum) {
+ SAP_COPY_NETNUM(DestAddr+2, NetNum);
+ }
+
+ /**
+ Set the card init done to 1 here so that
+ as the WAN NOTIFY comes back, we will need to
+ process the calls.
+ **/
+
+ SapCardInitDone = 1;
+
+ /**
+ HACK for getting receive threads to stop. Send a packet
+ to myself.
+ **/
+
+ if (Flag) {
+ SAP_COPY_NETNUM(DestAddr+2, SapNetNum);
+ SAP_COPY_NODENUM(DestAddr+6, SapNodeNum);
+ }
+
+ /** Send this packet **/
+
+ rc = sendto(
+ SapSocket,
+ (PVOID)&SendBuffer,
+ SendLength,
+ 0,
+ (PSOCKADDR)DestAddr,
+ SapBroadcastAddressLength);
+
+ /** Check for errors **/
+
+ if (rc == -1) {
+ IF_DEBUG(ERRORS) {
+ SS_PRINT(("SENDGENERAL: Error sending packet: h_errno = %d\n", h_errno));
+ }
+ }
+
+ /** All Done **/
+
+ return rc;
+}
+
+
+/*++
+*******************************************************************
+ S a p A d d S e n d E n t r y
+
+Routine Description:
+
+ Add an entry to the send list of a card. This will
+ allocate a new buffer if necessary.
+
+Arguments:
+
+ Entry = Ptr to SAP_RECORD that shows entry to add
+ Plist = Ptr to packet list structure to use
+ HopInc = Amount to add to HopCount for this entry
+
+Return Value:
+
+ 0 = OK
+ Else = Error
+*******************************************************************
+--*/
+
+INT
+SapAddSendEntry(
+ PSAP_RECORD Entry,
+ PSAP_PKTLIST Plist,
+ USHORT HopInc)
+{
+ PSAP_HEADER Hdrp;
+ PSAP_PKTENTRY Pktp;
+
+ /**
+ If the max number of entries are in this packet, then
+ we need to add this packet to the list.
+ **/
+
+ if (Plist->Curnum == 7) {
+
+ /** Add this packet to the list **/
+
+ if (Plist->PktHead)
+ Plist->PktTail->Next = Plist->Curpkt;
+ else
+ Plist->PktHead = Plist->Curpkt;
+ Plist->PktTail = Plist->Curpkt;
+ Plist->NumPkts++; /* Count this one */
+
+ /** Reset current packet stuff **/
+
+ Plist->Curpkt = NULL;
+ Plist->Curnum = 0;
+ }
+
+ /** If no current buffer - make one **/
+
+ Pktp = Plist->Curpkt;
+ if (Pktp == NULL) {
+
+ /**
+ Allocate a new buffer. We add 8 bytes to the buffer
+ as a header. The first 4 bytes are a forward pointer
+ to link up the buffers. The next 4 bytes are a length
+ holder to tell how much data is in the packet.
+ **/
+
+ Pktp = (PSAP_PKTENTRY)SAP_MALLOC(SAP_PKTENTRY_SIZE+SAP_MAX_SENDLEN, "AddSendEntry");
+
+ if (Pktp == NULL) {
+ IF_DEBUG(ERRORS) {
+ SS_PRINT(("SAP: Alloc failed for %d bytes in AddSendEntry\n", SAP_MAX_SENDLEN + 8));
+ }
+ return 1;
+ }
+
+ /** Init the pointers **/
+
+ Pktp->Next = NULL;
+ Pktp->SendLength = 2; /* Length of service response cmd code */
+
+ Plist->Curpkt = Pktp;
+ Plist->Curnum = 0;
+ Plist->Curptr = Pktp->Buffer;
+
+ /** Set the command code = service response **/
+
+ *Plist->Curptr++ = 0x00;
+ *Plist->Curptr++ = SAPTYPE_GENERAL_SERVICE_RESPONSE;
+ }
+
+ /** Add this entry **/
+
+ Hdrp = (PSAP_HEADER)Plist->Curptr;
+ Hdrp->ServerType = htons(Entry->ServType);
+ if (Entry->HopCount == 16)
+ Hdrp->Hopcount = htons(Entry->HopCount);
+ else
+ Hdrp->Hopcount = htons((USHORT)(Entry->HopCount + HopInc));
+ SAP_COPY_ADDRESS(Hdrp->Address, Entry->ServAddress);
+ SAP_COPY_SERVNAME(Hdrp->ServerName, Entry->ServName);
+
+ /** Bump up the pointers **/
+
+ Plist->Curptr += SAP_HEADER_SIZE;
+ Plist->Curnum++;
+ Pktp->SendLength += SAP_HEADER_SIZE;
+
+ /** All OK **/
+
+ return 0;
+}
+
+
+/*++
+*******************************************************************
+ S a p G e t D e l a y T i m e
+
+Routine Description:
+
+ This routine gets the RIP delay time for a given network
+ from the NWLink driver.
+
+Arguments:
+
+ Netnum = Ptr to 4 byte network number to get delay time for
+
+Return Value:
+
+ The delay time
+ 0xFFFF = Invalid network number
+*******************************************************************
+--*/
+
+USHORT
+SapGetDelayTime(
+ PUCHAR Netnum)
+{
+ IPX_NETNUM_DATA Netdata;
+ INT rc;
+ INT Length;
+
+ /** Fill out to get the info on this netnum from NWLink **/
+
+ memcpy(Netdata.netnum, Netnum, 4);
+ Length = sizeof(IPX_NETNUM_DATA);
+
+ /** Get the info **/
+
+ rc = getsockopt(
+ SapSocket,
+ NSPROTO_IPX,
+ IPX_GETNETINFO_NORIP,
+ (PCHAR)&Netdata,
+ &Length);
+
+ /** If error - return it **/
+
+ if (rc)
+ return 0xFFFF;
+
+ /** Return the result **/
+
+ return Netdata.netdelay;
+}
+
+
+
+/*++
+*******************************************************************
+ S a p C l e a n u p D o w n e d C a r d
+
+Routine Description:
+
+ A certain card number has gone down. We need to cleanup
+ everything about that card from the list. The card has already
+ been deleted from the card list when we get here.
+
+Arguments:
+
+ Cardnum = Number of the card that died.
+
+Return Value:
+
+ Nothing
+*******************************************************************
+--*/
+
+VOID
+SapCleanupDownedCard(
+ INT Cardnum)
+{
+ PSAP_RECORD Entry;
+ PSAP_RECORD NextEntry;
+ PSAP_RECORD HeadEntry;
+ PSAP_RECORD NextHeadEntry;
+ INT rc;
+
+ /**
+ We need to hold the readers lock here so that as cards
+ come up and go down. If one goes down with a certain
+ adapter number and while we are cleaning it up, we have
+ another come up with the same number, we could get
+ confused. This will keep this from happening.
+ **/
+
+ ACQUIRE_CARDLIST_READERS_LOCK("SapCleanupDownedCard Entry");
+
+ /**
+ Now we need to add all the SAP's that are on different nets.
+ For each card we need to advertise SAP's that came in
+ on other cards.
+
+ We only send the actual packets if there is more then 1 card.
+ If there is only 1 card - we go thru so we can delete servers
+ that have gone away.
+ **/
+
+ /** Lock the database **/
+
+ ACQUIRE_WRITERS_LOCK(rc,"Cleanup Downed Card");
+ if (rc) {
+
+ RELEASE_CARDLIST_READERS_LOCK("SapCleanupDownedCard X1");
+
+ IF_DEBUG(ERRORS) {
+ SS_PRINT(("NWSAP: CleanupDownedCard: Error getting writers lock\n"));
+ }
+
+ return;
+ }
+
+ /** Get ptr to the head of the type list **/
+
+ HeadEntry = GETPTRFROMINDEX(SdmdLists[SAP_TYPELIST_INDEX].Flink);
+ while (HeadEntry && (HeadEntry->ServType != 0xFFFF)) {
+
+ /** Save ptr to next head entry **/
+
+ NextHeadEntry = GETPTRFROMINDEX(HeadEntry->Links[SAP_TYPELIST_INDEX].Flink);
+
+ /** Go thru this sublist **/
+
+ Entry = GETPTRFROMINDEX(HeadEntry->Links[SAP_SUBLIST_INDEX].Flink);
+ while (Entry) {
+
+ /** If for card that went down - mark it **/
+
+ if ((Entry->CardNumber == Cardnum) &&
+ (!Entry->Internal)) {
+
+ Entry->HopCount = 16;
+ Entry->Changed = TRUE;
+ SapChanged = TRUE;
+ }
+
+ /** Get ptr to the next entry **/
+
+ NextEntry = GETPTRFROMINDEX(Entry->Links[SAP_SUBLIST_INDEX].Flink);
+
+ /** Goto the next entry **/
+
+ Entry = NextEntry;
+ }
+
+ /** Goto the next type **/
+
+ HeadEntry = NextHeadEntry;
+ }
+
+ /** We can release the lock now **/
+
+ RELEASE_WRITERS_LOCK("Send Packets X1");
+ RELEASE_CARDLIST_READERS_LOCK("SapCleanupDownedCard X2");
+
+ /** All Done **/
+
+ return;
+}
+
+
+
+/*++
+*******************************************************************
+ S a p U p d a t e C a r d N e t w o r k N u m b e r s
+
+Routine Description:
+
+ The main thread will call here every minute ONLY if we
+ have adapters that have a network number of zero. This is
+ used to check if those adapters have detected a network number
+
+Arguments:
+
+ None
+
+Return Value:
+
+ Nothing
+*******************************************************************
+--*/
+
+VOID
+SapUpdateCardNetworkNumbers(
+ VOID)
+{
+ PSAP_CARD Cardptr;
+ INT Retcode;
+ INT Length;
+ IPX_ADDRESS_DATA Addrdata;
+
+ /** Lock the cardlist so we can check everything **/
+
+ ACQUIRE_CARDLIST_WRITERS_LOCK(Retcode, "SapUpdateCardNetworkNumbers");
+
+ /**
+ Go thru the list of adapters to see if any have a network number
+ of zero. If any of them do, then query IPX to see if it has changed
+ to non-zero.
+ **/
+
+ Cardptr = SapCardHead;
+ while (Cardptr) {
+
+ /** If this card has netnum = 0 - ask IPX **/
+
+ if (!memcmp(Cardptr->Netnum, SapZeros, SAP_NET_LEN)) {
+
+ /** Fill in this entry **/
+
+ Addrdata.adapternum = Cardptr->Number;
+ Length = sizeof(IPX_ADDRESS_DATA);
+ Retcode = getsockopt(
+ SapSocket,
+ NSPROTO_IPX,
+ IPX_ADDRESS,
+ (PCHAR)&Addrdata,
+ &Length);
+
+ /** If ok - set the new network number **/
+
+ if (Retcode == 0) {
+ SAP_COPY_NETNUM(Cardptr->Netnum, Addrdata.netnum);
+ }
+ }
+
+ /** Goto the next card **/
+
+ Cardptr = Cardptr->Next;
+ }
+
+ /** Release the lock on the list now **/
+
+ RELEASE_CARDLIST_WRITERS_LOCK("SapUpdateCardNetworkNumbers");
+
+ /** All Done **/
+
+ return;
+}
diff --git a/private/net/svcdlls/nwsap/server/nwsap.c b/private/net/svcdlls/nwsap/server/nwsap.c
new file mode 100644
index 000000000..206372b01
--- /dev/null
+++ b/private/net/svcdlls/nwsap/server/nwsap.c
@@ -0,0 +1,1789 @@
+/*++
+
+Copyright (c) 1994 Microsoft Corporation
+Copyright (c) 1993 Micro Computer Systems, Inc.
+
+Module Name:
+
+ net\svcdlls\nwsap\server\nwsap.c
+
+Abstract:
+
+ This is the main routine for the NT SAP Agent
+
+Author:
+
+ Brian Walker (MCS) 06-15-1993
+
+Revision History:
+
+--*/
+
+#include "precomp.h"
+#pragma hdrstop
+
+/** Internal Function Prototypes **/
+
+DWORD WINAPI
+SapWorkerThread(
+ LPVOID Threadparm);
+
+DWORD WINAPI
+SapRecvThread(
+ LPVOID Threadparm);
+
+INT
+SapHandleRequest(
+ PUCHAR Requestp,
+ INT Length,
+ PUCHAR Addrptr,
+ INT Addrlength);
+
+INT
+SapStartRecvThread(
+ VOID);
+
+INT
+SapGetNearestFromSendList(
+ PUCHAR Bufferp,
+ USHORT ServerType);
+
+INT
+SapGetCardFromAddress(
+ PUCHAR Addrptr,
+ BOOL *WanFlagp,
+ PUCHAR NetNump);
+
+
+/*++
+*******************************************************************
+ S a p I n i t
+
+Routine Description:
+
+ This routine initializes the Sap Agent.
+
+Arguments:
+
+ None
+
+Return Value:
+
+ 0 = OK
+ Else = Error
+*******************************************************************
+--*/
+
+INT
+SapInit(
+ VOID)
+{
+ INT rc;
+ INT i;
+
+ /**
+ These things need to be initialized before anything
+ else can happen.
+ **/
+
+ InitializeCriticalSection(&SapRecvCriticalSection);
+ InitializeCriticalSection(&SapFreeCriticalSection);
+ InitializeCriticalSection(&SapWanRecvCriticalSection);
+ InitializeCriticalSection(&SapWanFreeCriticalSection);
+ InitializeCriticalSection(&SapSendCriticalSection);
+ InitializeCriticalSection(&SapSendBusyCriticalSection);
+ InitializeCriticalSection(&SapThreadCountCriticalSection);
+ InitializeCriticalSection(&SapLpcThreadCountCriticalSection);
+ InitializeCriticalSection(&SapLpcClientCriticalSection);
+ InitializeCriticalSection(&SapMemoryCriticalSection);
+ InitializeCriticalSection(&SapCardlistCriticalSection);
+ InitializeCriticalSection(&SdmdCriticalSection);
+ SapCurBackup = 0;
+ SapThreadCount = 0;
+ SapLpcNumWorkers = 0;
+ SapWorkerThreadWaiting = 0;
+
+ /** Init the recv buffer list **/
+
+ InitializeListHead(&SapRecvList);
+ InitializeListHead(&SapFreeList);
+ InitializeListHead(&SapWanRecvList);
+ InitializeListHead(&SapWanFreeList);
+ InitializeListHead(&SapLpcClientList);
+ SapNumFreeBufs = 0;
+
+ /** Initialize the Critical Section we use for synch. **/
+
+ SapCardlistLockCount = 0;
+ SapCardlistSynchEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
+ if (SapCardlistSynchEvent == NULL) {
+ SapError = GetLastError();
+ SapEventId = NWSAP_EVENT_CARDLISTEVENT_FAIL;
+ return -1;
+ }
+
+ /** Go initialize the filter stuff **/
+
+ rc = SapFilterInit();
+ if (rc) {
+ return rc;
+ }
+
+ /** Go read the registry **/
+
+ rc = SapReadRegistry();
+ if (rc) {
+ return rc;
+ }
+
+ /** Go initialize the database routines **/
+
+ rc = SapInitSdmd();
+ if (rc) {
+ return rc;
+ }
+
+ /** Initialize the WAN checking interface **/
+
+ rc = SapWanInit();
+ if (rc)
+ return rc;
+
+ /** Initialize the sockets interface **/
+
+ rc = SapNetworkInit();
+ if (rc)
+ return rc;
+
+ /** Create an event for waiting for thread termination with **/
+
+ SapThreadEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+ if (SapThreadEvent == NULL) {
+ SapError = GetLastError();
+ SapEventId = NWSAP_EVENT_THREADEVENT_FAIL;
+ return -1;
+ }
+
+ /** Create the semaphore we use **/
+
+ SapRecvSem = CreateSemaphore(NULL, 0, 0x7FFFFFFF, NULL);
+ if (SapRecvSem == NULL) {
+ SapError = GetLastError();
+ SapEventId = NWSAP_EVENT_RECVSEM_FAIL;
+ return -1;
+ }
+
+ /** Create an event for the send thread **/
+
+ SapSendEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+ if (SapSendEvent == NULL) {
+ SapError = GetLastError();
+ SapEventId = NWSAP_EVENT_SENDEVENT_FAIL;
+ return -1;
+ }
+
+ /** Go init the LPC interface **/
+
+ if (SapXsInitialize()) {
+ return -1;
+ }
+
+ /** Start the worker threads **/
+
+ for (i = 0 ; i < SapNumWorkerThreads ; i++) {
+ rc = SapStartWorkerThread();
+ if (rc) {
+ SapEventId = 0; /* Already logged */
+ return -1;
+ }
+
+ /** Save time of last worker startup **/
+
+ SapLastWorkerStartTime = GetCurrentTime();
+ }
+
+ /** Start the receive threads **/
+
+ for (i = 0 ; i < SapNumRecvThreads ; i++) {
+ rc = SapStartRecvThread();
+ if (rc) {
+ SapEventId = 0; /* Already logged */
+ return -1;
+ }
+ }
+
+ /**
+ Send a general request so all SAPS on the network will
+ respond and we will get a list of available servers.
+ **/
+
+ SapSendGeneralRequest(0, NULL);
+
+ /** Initialization is OK **/
+
+ return 0;
+}
+
+
+/*++
+*******************************************************************
+ S a p S h u t d o w n
+
+Routine Description:
+
+ When we are terminating, this routine will clean
+ up everything.
+
+ The SsInitialized flag has been set to 0 before we
+ are called.
+
+Arguments:
+
+ None
+
+Return Value:
+
+ Nothing
+*******************************************************************
+--*/
+
+VOID
+SapShutdown(
+ VOID)
+{
+ PSAP_SERVER Servp;
+ PSAP_SERVER NServp;
+ PSAP_RECVBLOCK Recvbuf;
+ PLIST_ENTRY Listp;
+ INT i;
+ INT Max;
+ HANDLE Handle;
+
+ /**
+ The way the code runs is that we get here only after the
+ send thread has terminated. We need to wait for all receive
+ and worker threads to terminate. We will close the handles that
+ these threads wait on and then wait for all the threads to
+ terminate.
+
+ The receive threads wait on a recvfrom on SapSocket.
+ The worker threads wait on SapRecvSem.
+ **/
+
+ IF_DEBUG(TERMINATION) {
+ SS_PRINT(("NWSAP: Shutting down\n"));
+ }
+
+ /** Release so all threads can terminate **/
+
+ if (SapRecvSem != NULL) {
+ ReleaseSemaphore(SapRecvSem, SapCurWorkerThreads + 10, NULL);
+ }
+
+ /** If there is a WAN check thread - kill it **/
+
+ if (SapWanNotifyHandlesBuf) {
+ for (i = 0 ; i < SapNumWanNotifyThreads ; i++) {
+
+ /** Set the event for this handle **/
+
+ if (SapWanEvent) {
+ SetEvent(SapWanEvent);
+ }
+ else {
+
+ /** Take this handle out of the list **/
+
+ ACQUIRE_THREADCOUNT_LOCK();
+ Handle = SapWanNotifyHandlesBuf[i];
+ SapWanNotifyHandlesBuf[i] = NULL;
+ RELEASE_THREADCOUNT_LOCK();
+
+ if (Handle) {
+ TerminateThread(Handle, 0);
+ CloseHandle(Handle);
+ SAP_DEC_THREAD_COUNT("Wan Check Thread Termination", NULL);
+ }
+ }
+ }
+ }
+
+ /** Kill the WAN semaphore **/
+
+ if (SapWanSem != NULL) {
+ ReleaseSemaphore(SapWanSem, 1, NULL);
+ }
+
+ /** Kill the WAN Event **/
+
+ if (SapWanEvent != NULL) {
+ CloseHandle(SapWanEvent);
+ }
+
+ /**
+ We need to get the recv threads to stop. When we close
+ the SapSocket the recvform calls will NOT return with
+ an error. To make the threads stop - we will send a packet
+ to ourselves. Kind of a HACK - but the only way to do it.
+
+ Send a couple more then we need to just to be sure.
+ **/
+
+ if (SapSocket != INVALID_SOCKET) {
+
+ /** Send the packets **/
+
+ Max = SapCurReceiveThreads + 2;
+ for (i = 0 ; i < Max ; i++) {
+ SapSendGeneralRequest(1, NULL);
+ }
+
+ /** Close the socket **/
+
+ closesocket(SapSocket);
+ }
+
+ /** If we have a thread handle - wait for all threads to die **/
+
+ if (SapThreadEvent) {
+ IF_DEBUG(TERMINATION) {
+ SS_PRINT(("NWSAP: Waiting for all threads to terminate\n"));
+ }
+
+ /** Wait for all threads to die **/
+
+ WaitForSingleObjectEx(SapThreadEvent, INFINITE, TRUE);
+
+ IF_DEBUG(TERMINATION) {
+ SS_PRINT(("NWSAP: All threads are terminated\n"));
+ }
+ }
+
+ /** Free the buffer for holding the wan handles **/
+
+ if (SapWanNotifyHandlesBuf) {
+ ACQUIRE_THREADCOUNT_LOCK();
+ SAP_FREE(SapWanNotifyHandlesBuf, "Notify Table freed");
+ RELEASE_THREADCOUNT_LOCK();
+ }
+
+ /** Go shutdown the LPC interface **/
+
+ SapXsShutdown();
+
+ /**
+ At this point all threads are terminated. We just need to go
+ thru and free all memory and close all handles.
+ **/
+
+ /** Close handles **/
+
+ if (SapSendEvent != NULL)
+ CloseHandle(SapSendEvent);
+
+ if (SapThreadEvent != NULL)
+ CloseHandle(SapThreadEvent);
+
+ if (SapRecvSem != NULL)
+ CloseHandle(SapRecvSem);
+
+ /** Shutdown WAN Checking **/
+
+ SapWanShutdown();
+
+ /** Free the send list **/
+
+ Servp = SapServHead;
+ while (Servp) {
+ NServp = Servp->Next;
+ SAP_FREE(Servp, "SAP Shutdown 1");
+ Servp = NServp;
+ }
+
+ /** Free any buffers left in the worker queue **/
+
+ while (!IsListEmpty(&SapRecvList)) {
+ Listp = RemoveHeadList(&SapRecvList);
+ Recvbuf = CONTAINING_RECORD(Listp, SAP_RECVBLOCK, ListEntry);
+ SAP_FREE(Recvbuf, "SAP SHUTDOWN 2");
+ }
+
+ /** Free any buffers left in the recv buffer free list **/
+
+ while (!IsListEmpty(&SapFreeList)) {
+ Listp = RemoveHeadList(&SapFreeList);
+ Recvbuf = CONTAINING_RECORD(Listp, SAP_RECVBLOCK, ListEntry);
+ SAP_FREE(Recvbuf, "SAP SHUTDOWN 3");
+ }
+
+ /** Close the network interface **/
+
+ SapNetworkShutdown();
+
+ /** Cleanup the database **/
+
+ SapShutdownSdmd();
+
+ /** Cleanup the filter stuff **/
+
+ SapFilterShutdown();
+
+ /** Cleanup all critical sections **/
+
+ DeleteCriticalSection(&SapWanRecvCriticalSection);
+ DeleteCriticalSection(&SapWanFreeCriticalSection);
+ DeleteCriticalSection(&SdmdCriticalSection);
+ DeleteCriticalSection(&SapRecvCriticalSection);
+ DeleteCriticalSection(&SapFreeCriticalSection);
+ DeleteCriticalSection(&SapSendCriticalSection);
+ DeleteCriticalSection(&SapSendBusyCriticalSection);
+ DeleteCriticalSection(&SapMemoryCriticalSection);
+ DeleteCriticalSection(&SapThreadCountCriticalSection);
+ DeleteCriticalSection(&SapLpcThreadCountCriticalSection);
+ DeleteCriticalSection(&SapLpcClientCriticalSection);
+ DeleteCriticalSection(&SapCardlistCriticalSection);
+
+ /** All Done **/
+
+ return;
+}
+
+
+/*++
+*******************************************************************
+ S a p S e n d T h r e a d
+
+Routine Description:
+
+ This thread sends SAP announcements at the interval
+ specified in the registry (def = 60 seconds)
+
+Arguments:
+
+ Threadparm = Parameter from the CreateThread call
+
+Return Value:
+
+ Exit Code
+
+*******************************************************************
+--*/
+
+DWORD WINAPI
+SapSendThread(
+ LPVOID Threadparm)
+{
+ NTSTATUS Status;
+ INT MinutesLeft;
+ DWORD TimeWait;
+
+ /**
+ The first time thru we wait 10 seconds and
+ then advertise everybody so that we quickly
+ bring up new networks.
+ **/
+
+ TimeWait = 10 * 1000;
+
+ /** This is the send loop **/
+
+ SapRecheckCount = SapRecheckAllCardsTime;
+ MinutesLeft = SapSendMinutes;
+ while (SsInitialized) {
+
+ /** Wait for one minute **/
+
+ Status = WaitForSingleObjectEx(SapSendEvent, TimeWait, TRUE);
+
+ /** All other waits are one minute **/
+
+ TimeWait = 60 * 1000;
+
+ /** If stopping - leave **/
+
+ if (!SsInitialized) {
+ IF_DEBUG(TERMINATION) {
+ SS_PRINT(("NWSAP SEND THREAD: Exitting the thread\n"));
+ }
+ break;
+ }
+
+ /** Check for timeouts in the database **/
+
+ SdmdTimeoutCheck();
+
+ /**
+ In the case where our network number is zero, we must
+ recheck it here. When we started, we could have gotten
+ a network number of zero from IPX if IPX had not yet
+ determined its network number from the network.
+
+ In this case we keep checking every minute to see if
+ IPX learned its new network number.
+
+ We do this before we send the advertise packets to
+ that we do not have to wait another minute before
+ these show up on the wire.
+ **/
+
+ if (!memcmp(SapNetNum, SapZeros, SAP_NET_LEN)) {
+ INT Retcode;
+ INT AddressLength;
+ SOCKADDR Address;
+
+ /**
+ Ask IPX what it's current network number is.
+ If it changes to non-zero - we need to update.
+ **/
+
+ AddressLength = 16;
+ Retcode = getsockname(SapSocket, &Address, &AddressLength);
+
+ /** If the call succeeds - check the data **/
+
+ if (Retcode != SOCKET_ERROR) {
+
+ /** Set SapNetNum from the net number returned **/
+
+ memcpy(SapNetNum, Address.sa_data, SAP_NET_LEN);
+
+ /** If not zero - go update the advertise list **/
+
+ if (memcmp(SapNetNum, SapZeros, SAP_NET_LEN)) {
+ PSAP_SERVER Servp;
+
+ /** Lock the list so it doesn't change on us **/
+
+ ACQUIRE_SENDTABLE_LOCK();
+
+ /**
+ If any entries have a network number of zero
+ change them to have a network number of
+ the network number we just learned from
+ IPX.
+ **/
+
+ Servp = SapServHead;
+ while (Servp) {
+
+ /** **/
+
+ if (!memcmp(Servp->Address, SapZeros, SAP_NET_LEN)) {
+ memcpy(Servp->Address, SapNetNum, SAP_NET_LEN);
+ }
+
+ /** Goto the next entry **/
+
+ Servp = Servp->Next;
+ }
+
+ /** Release the list **/
+
+ RELEASE_SENDTABLE_LOCK();
+
+ /**
+ Now we need to update the network numbers
+ of all the cards we have.
+ **/
+
+ SapUpdateCardNetworkNumbers();
+ }
+ }
+ }
+
+ /**
+ If time is up - go send advertises for everything
+ we need. This handles advertising of routed SAPs
+ as well.
+
+ If there has been a change in the table - send now
+ **/
+
+ if (SapChanged)
+ MinutesLeft = 0; /* Send Now */
+ else
+ MinutesLeft--;
+
+ if (!MinutesLeft) {
+
+ /** Do the advertising **/
+
+ SapSendPackets(0);
+
+ /** Reset the number of minutes **/
+
+ MinutesLeft = SapSendMinutes;
+ }
+
+ /**
+ When a WAN card comes up, we send a general request.
+ We send the request multiple times in case it got
+ missed.
+ **/
+
+ SapCheckSendGeneralRequest();
+
+ /**
+ If we are suppossed to go check all cards again -
+ then we check that here.
+ **/
+
+ if (SapRecheckAllCardsTime) {
+ SapRecheckCount--;
+ if (SapRecheckCount == 0) {
+ SapRecheckAllCards();
+ SapRecheckCount = SapRecheckAllCardsTime;
+ }
+ }
+ }
+
+ /** Send out packets that tell networks about servers going down **/
+
+ SapSendPackets(2);
+
+ /** All Done **/
+
+ return 0;
+}
+
+
+
+
+/*++
+*******************************************************************
+ S a p W o r k e r T h r e a d
+
+Routine Description:
+
+ This thread takes recv'd SAP packets off of the
+ recv list and processes them.
+
+Arguments:
+
+ threadparm = Parameter from the CreateThread call
+
+Return Value:
+
+ Exit Code
+*******************************************************************
+--*/
+
+DWORD WINAPI
+SapWorkerThread(
+ LPVOID Threadparm)
+{
+ NTSTATUS Status;
+ PSAP_RECVBLOCK Recvbuf;
+ PLIST_ENTRY Listp;
+ INT SendLength;
+ INT rc;
+
+ /** **/
+
+ while (SsInitialized) {
+
+ /** Wait for a request to show up **/
+
+ INC_WORKER_THREAD_WAITING_COUNT();
+ Status = WaitForSingleObjectEx(SapRecvSem, INFINITE, TRUE);
+ DEC_WORKER_THREAD_WAITING_COUNT();
+
+ /** If stopping - just leave **/
+
+ if (!SsInitialized) {
+ IF_DEBUG(TERMINATION) {
+ SS_PRINT(("NWSAP: Worker Thread: Breaking out for stop\n"));
+ }
+ break;
+ }
+
+ /** If error - just ignore it **/
+
+ if (!NT_SUCCESS(Status)) {
+ IF_DEBUG(ERRORS) {
+ SS_PRINT(("NWSAP: Worker: Wait failed: status = 0x%lx\n", Status));
+ }
+ continue;
+ }
+
+ /** Get ownership of the worker list **/
+
+ ACQUIRE_RECVTABLE_LOCK();
+
+ /**
+ If we at in a high traffic time, we might need to start
+ another worker thread. Check this here. We do this here
+ to get the protection of the lock.
+ **/
+
+ if ((SapCurBackup > SapNewWorkerThreshhold) &&
+ (SapCurWorkerThreads < SapMaxEverWorkerThreads) &&
+ (!SapWorkerStarting)) {
+
+ LONG Diff;
+
+ /**
+ Make sure that enough time has elapsed. There is
+ a registry parameter that lets the user set
+ the min time between worker startups.
+
+ The diff line will give us the number of milliseconds
+ since the last time we tried to start a new worker.
+
+ If we have elapsed enough time - then we can start
+ a new one.
+ **/
+
+ Diff = abs((LONG)GetCurrentTime() - (LONG)SapLastWorkerStartTime);
+ if (Diff >= SapNewWorkerTimeout) {
+
+ /**
+ Mark that we are in the worker starting code so we
+ do not come in here again while we are starting up.
+
+ We release the lock so that other worker threads can
+ be working while we are starting a new one.
+ **/
+
+ SapWorkerStarting = 1;
+ RELEASE_RECVTABLE_LOCK();
+
+ /** Go start a new worker thread **/
+
+ rc = SapStartWorkerThread();
+
+ /**
+ If OK - set new time that a worker thread
+ was started at.
+ **/
+
+ if (!rc) {
+ SapLastWorkerStartTime = GetCurrentTime();
+ }
+
+ /**
+ Reacquire the recv table lock and go get
+ a packet to handle.
+
+ Mark that we are no longer in the worker thread
+ starting code.
+ **/
+
+ ACQUIRE_RECVTABLE_LOCK();
+ SapWorkerStarting = 0;
+ }
+ }
+
+ /** Get an entry from the list **/
+
+ if (!IsListEmpty(&SapRecvList)) {
+ Listp = RemoveHeadList(&SapRecvList);
+ Recvbuf = CONTAINING_RECORD(Listp, SAP_RECVBLOCK, ListEntry);
+ SapCurBackup--;
+ }
+ else
+ Recvbuf = NULL;
+
+ /** Release the lock on the list **/
+
+ RELEASE_RECVTABLE_LOCK();
+
+ /** If no packet - error **/
+
+ if (Recvbuf == NULL) {
+ IF_DEBUG(ERRORS) {
+ SS_PRINT(("NWSAP: WORKER: Wait came back but no block ready\n"));
+ }
+ continue;
+ }
+
+ /** Handle the packet here **/
+
+ SendLength = SapHandleRequest(
+ Recvbuf->Buffer,
+ Recvbuf->Datalen,
+ Recvbuf->Address,
+ Recvbuf->AddressLength);
+
+ /** If we need to send a response - do it **/
+
+ if (SendLength && (SendLength != -1)) {
+
+ /** Send the response back **/
+
+ rc = sendto(
+ SapSocket, /* Socket */
+ Recvbuf->Buffer, /* Ptr to send buffer */
+ SendLength, /* Length of data */
+ 0, /* Flags */
+ (PSOCKADDR)Recvbuf->Address,
+ Recvbuf->AddressLength);
+
+ /** If error - handle it **/
+
+ if (rc == -1) {
+ IF_DEBUG(ERRORS) {
+ SS_PRINT(("NWSAP: WORKER: Sendto failed: error = %d\n", h_errno));
+ }
+ }
+ }
+
+ /**
+ If there is room in the FREE list - then put
+ this entry in the free list, else just free
+ the memory back to heap.
+ **/
+
+ ACQUIRE_FREETABLE_LOCK();
+ if (SapNumFreeBufs < SapMaxFreeBufs) {
+ InsertTailList(&SapFreeList, &Recvbuf->ListEntry);
+ SapNumFreeBufs++;
+ }
+ else {
+ SAP_FREE(Recvbuf, "FREE RECVBUF");
+ }
+ RELEASE_FREETABLE_LOCK();
+
+ /** If we are to terminate - do it **/
+
+ if (SendLength == -1)
+ break;
+ }
+
+ /** We are terminating - uncount and set event if need to **/
+
+ SAP_DEC_THREAD_COUNT("Worker Terminate", &SapCurWorkerThreads);
+
+ /** All Done **/
+
+ return 0;
+}
+
+
+/*++
+*******************************************************************
+ S a p R e c v T h r e a d
+
+Routine Description:
+
+ This is the thread that recv SAPs and places them
+ on a list to be handled.
+
+Arguments:
+
+ Threadparm = Parameter from the CreateThread call
+
+Return Value:
+
+ Exit Code
+*******************************************************************
+--*/
+
+DWORD WINAPI
+SapRecvThread(
+ LPVOID Threadparm)
+{
+ PSAP_RECVBLOCK Recvbuf;
+ PLIST_ENTRY Listp;
+ DWORD lastNetError = 0;
+ HANDLE SapRecvEvent = NULL;
+ DWORD status;
+
+ SapRecvEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+
+ /** Sit in the loop and recv datagrams **/
+
+ while (SsInitialized) {
+
+ /** Get ownership of the list **/
+
+ ACQUIRE_FREETABLE_LOCK();
+ if (!IsListEmpty(&SapFreeList)) {
+
+ Listp = RemoveHeadList(&SapFreeList);
+ Recvbuf = CONTAINING_RECORD(Listp, SAP_RECVBLOCK, ListEntry);
+ SapNumFreeBufs--;
+
+ } else {
+
+ Recvbuf = SAP_MALLOC(SAP_RECVBLOCK_SIZE + SAP_MAX_RECVLEN, "Make RecvBuf");
+ if (Recvbuf == NULL) {
+ IF_DEBUG(ERRORS) {
+ SS_PRINT(("NWSAP: RECV: Error allocating recvbuf\n"));
+ }
+ RELEASE_FREETABLE_LOCK();
+
+ WaitForSingleObjectEx( SapRecvEvent,
+ (SapRecvDelayOnMallocFail * 1000),
+ TRUE);
+ continue;
+ }
+ }
+ RELEASE_FREETABLE_LOCK();
+
+ /** Hang a recv **/
+
+ Recvbuf->AddressLength = 16;
+ Recvbuf->Datalen = recvfrom(
+ SapSocket,
+ Recvbuf->Buffer,
+ SAP_MAX_RECVLEN,
+ 0,
+ (PSOCKADDR)Recvbuf->Address,
+ &Recvbuf->AddressLength);
+
+ /** If stopping - then just leave**/
+
+ if (!SsInitialized) {
+ SAP_FREE(Recvbuf, "RECV THREAD Terminate 1");
+ IF_DEBUG(TERMINATION) {
+ SS_PRINT(("NWSAP: RECV THREAD: Breaking out for stop\n"));
+ }
+ break;
+ }
+
+ /** If error - handle it **/
+
+ if (Recvbuf->Datalen == -1) {
+
+ lastNetError = h_errno;
+
+ IF_DEBUG(ERRORS) {
+ SS_PRINT(("NWSAP: RECV: Error recving sap: h_errno = %d\n",
+ lastNetError));
+ }
+
+ //
+ // Special case getting an invalid length for the incoming packet
+ // by not delaying.
+ //
+
+ if ( lastNetError != WSAEMSGSIZE &&
+ lastNetError != WSAEFAULT ) {
+
+ WaitForSingleObjectEx( SapRecvEvent,
+ (SapRecvDelayOnNetError * 1000),
+ TRUE);
+ }
+ /** We got an error on the recv **/
+
+ SAP_FREE(Recvbuf, "RECV THREAD Terminate 2");
+
+ continue;
+ }
+
+ /** Put the entry on the worker list **/
+
+ ACQUIRE_RECVTABLE_LOCK();
+ if (SapCurBackup < SapMaxBackup) {
+
+ InsertTailList(&SapRecvList, &Recvbuf->ListEntry);
+ SapCurBackup++;
+
+ } else {
+
+ SAP_FREE(Recvbuf, "RECV THREAD Dropped Request");
+ Recvbuf = NULL ;
+ RELEASE_RECVTABLE_LOCK();
+ continue;
+ }
+ RELEASE_RECVTABLE_LOCK();
+
+ /** Signal the worker thread there is a recv there **/
+
+ ReleaseSemaphore(SapRecvSem, 1, NULL);
+ }
+
+ /** We are terminating - uncount and set event if need to **/
+
+ SAP_DEC_THREAD_COUNT("Receive Terminate", &SapCurReceiveThreads);
+
+ if (SapRecvEvent != NULL) {
+ CloseHandle(SapRecvEvent);
+ }
+
+ /** All Done **/
+
+ return 0;
+}
+
+
+/*++
+*******************************************************************
+ S a p H a n d l e R e q u e s t
+
+Routine Description:
+
+ When we recv a sap packet - this function will handle
+ it and build a response if it needs to.
+
+Arguments:
+
+ Requestp = Ptr to the SAP packet recv'd
+ Length = Length of the SAP packet
+ Addrptr = Address of who sent the packet
+ Addrlength = Length of address at addrp
+
+Return Value:
+
+ Length of return packet to send back. If this is 0
+ then there is no packet to send back.
+
+ If -1, then we should terminate this worker thread immediately.
+*******************************************************************
+--*/
+
+INT
+SapHandleRequest(
+ PUCHAR Requestp,
+ INT Length,
+ PUCHAR Addrptr,
+ INT Addrlength)
+{
+ INT ReturnLength = 0;
+ INT Cardnum;
+ PSAP_HEADER Servp;
+ USHORT QueryType;
+ USHORT ServerType;
+ UCHAR BcastFlag;
+ UCHAR NetNum[SAP_NET_LEN];
+ BOOL WanFlag;
+ INT NumFound;
+ INT rc;
+
+ /**
+ Make sure we have a minimum length. The minimum length
+ is a USHORT of the query type and a USHORT of the
+ server type.
+ **/
+
+ if (Length < (sizeof(USHORT) + sizeof(USHORT)))
+ return 0;
+
+ /** Get the query type **/
+
+ QueryType = ntohs(*(PUSHORT)Requestp);
+
+ switch (QueryType) {
+
+ /** General Service Query **/
+
+ case SAPTYPE_GENERAL_SERVICE_QUERY:
+
+ /** Get the server type wanted **/
+
+ ServerType = ntohs(((PSAP_REQUEST)Requestp)->ServerType);
+
+ /**
+ Get flag telling us if this request is broadcast of
+ a unicast request.
+ **/
+
+ BcastFlag = (*(Addrptr + 15) & 1);
+
+ /**
+ Get the card number that this request came in on.
+ If the network cannot be found - OR - the request
+ came from the SAP Agent (ME), then we will not respond.
+
+ But if the request came from someone else in this machine
+ or an external person, then we will respond.
+ **/
+
+ Cardnum = SapGetCardFromAddress(Addrptr, &WanFlag, NULL);
+ if ((Cardnum == CARDRET_UNKNOWN) || (Cardnum == CARDRET_MYSELF))
+ return 0;
+
+ /**
+ Send all of that type. The return value will be 0 or -1.
+ If 0, then nothing is to be returned (we sent all the
+ packets from the routine). If -1, then this
+ thread should terminate.
+ **/
+
+ ReturnLength = SapSendForTypes(
+ ServerType,
+ Addrptr,
+ Addrlength,
+ Cardnum,
+ BcastFlag,
+ WanFlag);
+ break;
+
+ /** Nearest service query **/
+
+ case SAPTYPE_NEAREST_SERVICE_QUERY:
+
+ /** Verify we have a good server type **/
+
+ ServerType = ntohs(((PSAP_REQUEST)Requestp)->ServerType);
+ if (ServerType == 0xFFFF) /* Invalid */
+ break;
+
+ /**
+ Get the card that we received this on. If we get
+ that we cannot map the card net number, then
+ just ignore the packet.
+
+ retlen == 0 when we get here so we do not need to
+ set it.
+ **/
+
+ Cardnum = SapGetCardFromAddress(Addrptr, &WanFlag, NULL);
+ if (Cardnum == CARDRET_UNKNOWN)
+ break;
+
+ /**
+ Get flag telling us if this request is broadcast of
+ a unicast request.
+ **/
+
+ BcastFlag = (*(Addrptr + 15) & 1);;
+
+ /**
+ If this is NOT a WAN card, then we will first check
+ our send list for a server and then if one is not found
+ there, then we will check the database for another server.
+ **/
+
+ if (!WanFlag) {
+
+ /** Find one we are advertising **/
+
+ ReturnLength = SapGetNearestFromSendList(Requestp, ServerType);
+ if (ReturnLength) /* Found one - return it */
+ break;
+
+ /**
+ We did not find a nearest server in our advertise list,
+ so go find one in the database.
+ **/
+
+ ReturnLength = SdmdGetNearestServerLan(
+ Requestp, /* Ptr to request buffer */
+ ServerType, /* Server type they want */
+ Cardnum, /* Card req recv'd on */
+ BcastFlag); /* Req bcast/unicast flag */
+
+ /** Return this value (Calling ret will send response) **/
+
+ break;
+ }
+
+ /**
+ This request came over a WAN link. We will find up to
+ 4 servers to send back to the requestee.
+ **/
+
+ ReturnLength = SdmdGetNearestServerWan(
+ Requestp, /* Ptr to request buffer */
+ ServerType, /* Server type they want */
+ Cardnum, /* Card req recv'd on */
+ BcastFlag, /* Req bcast/unicast flag */
+ &NumFound); /* Num found */
+
+ /**
+ We found some responses. NumFound is set to the number
+ we have. We send them all back here.
+ **/
+
+ while (NumFound--) {
+
+ /** Send a packet **/
+
+ rc = sendto(
+ SapSocket, /* Socket */
+ Requestp, /* Ptr to send buffer */
+ ReturnLength, /* Length of data */
+ 0, /* Flags */
+ (PSOCKADDR)Addrptr, /* Ptr to address */
+ Addrlength); /* Length of address */
+
+ /** If error - handle it **/
+
+ if (rc == -1) {
+ IF_DEBUG(ERRORS) {
+ SS_PRINT(("NWSAP: GETNEAREST: Sendto failed: error = %d\n", h_errno));
+ }
+ }
+
+ /**
+ Goto next buffer to send.
+
+ Since this is on a WAN, we do not have to do the
+ 55ms delay.
+ **/
+
+ Requestp += ReturnLength;
+ }
+
+ /**
+ We have already sent the responses. So we return 0
+ here to signify that there is no reply to be sent.
+ **/
+
+ ReturnLength = 0;
+ break;
+
+ /** Advertise packets **/
+
+ case SAPTYPE_GENERAL_SERVICE_RESPONSE:
+
+ /** Get rid of the query type from the packet **/
+
+ Requestp += sizeof(USHORT);
+ Length -= sizeof(USHORT);
+
+ /**
+ Get the card number. If we only have one card
+ then we use that card for everything.
+ **/
+
+ Cardnum = SapGetCardFromAddress(Addrptr, &WanFlag, NetNum);
+
+ /** If error or from SAP Agent - ignore **/
+
+ if ((Cardnum == CARDRET_UNKNOWN) || (Cardnum == CARDRET_MYSELF))
+ return 0;
+
+ /**
+ Go thru the packet and update all the server entries
+ in there.
+ **/
+
+ while (Length >= SAP_HEADER_SIZE) {
+
+ /** **/
+
+ Servp = (PSAP_HEADER)Requestp;
+
+ /**
+ If the advertised network number is 0, replace it
+ with the network number of the card.
+ **/
+
+ if (!memcmp(Servp->Address, SapZeros, SAP_NET_LEN)) {
+ memcpy(Servp->Address, NetNum, SAP_NET_LEN);
+ }
+
+ /** Update this entry **/
+
+ SdmdUpdateEntry(
+ Servp->ServerName,
+ ntohs(Servp->ServerType),
+ Servp->Address,
+ ntohs(Servp->Hopcount),
+ Cardnum,
+ Addrptr + 6, /* Node addr of who sent the packet */
+ WanFlag);
+
+ /** Goto the next entry **/
+
+ Requestp += SAP_HEADER_SIZE;
+ Length -= SAP_HEADER_SIZE;
+ }
+
+ /** There is no return value here **/
+
+ ReturnLength = 0;
+ break;
+
+ /**
+ We should never get these. These are only valid as responses
+ to a NEAREST_SERVICE_QUERY packet. Just drop thru to default.
+ **/
+
+ case SAPTYPE_NEAREST_SERVICE_RESPONSE: /* DROP THRU TO DEFAULT */
+
+ /** Unknown - just ignore the packet **/
+
+ default:
+ ReturnLength = 0;
+ break;
+ }
+
+ /** Return the length of data to send back **/
+
+ return ReturnLength;
+}
+
+
+/*++
+*******************************************************************
+ S a p S t a r t R e c v T h r e a d
+
+Routine Description:
+
+ This routine starts a receive thread.
+
+Arguments:
+
+ Nothing
+
+Return Value:
+
+ 0 = OK
+ 1 = Error
+*******************************************************************
+--*/
+
+INT
+SapStartRecvThread(
+ VOID)
+{
+ HANDLE Handle;
+ DWORD Threadid;
+ DWORD Error;
+
+ /**
+ We count this thread as running before we start it
+ for synchronization. If the start fails - then we will
+ dec this count
+ **/
+
+ SAP_INC_THREAD_COUNT("Receive Start", &SapCurReceiveThreads);
+
+ /** Start the thread **/
+
+ Handle = CreateThread(
+ NULL, /* Security Ptr */
+ 0, /* Initial stack size */
+ SapRecvThread, /* Thread Function */
+ (LPVOID)SapSocket, /* Parm for the thread */
+ 0, /* Creation Flags */
+ &Threadid); /* Ptr to thread id */
+
+ if (Handle == NULL) {
+
+ /** **/
+
+ Error = GetLastError();
+ IF_DEBUG(ERRORS) {
+ SS_PRINT(("NWSAP: Error starting receive thread = %d\n", Error));
+ }
+
+ /** Dec the thread count **/
+
+ SAP_DEC_THREAD_COUNT("Receive Start Error", &SapCurReceiveThreads);
+
+ /** Log the error **/
+
+ SsLogEvent(
+ NWSAP_EVENT_STARTRECEIVE_ERROR,
+ 0,
+ NULL,
+ Error);
+
+ /** Return Error **/
+
+ return 1;
+ }
+
+ /** We can close this handle **/
+
+ CloseHandle(Handle);
+
+ /** Return OK **/
+
+ return 0;
+}
+
+
+/*++
+*******************************************************************
+ S a p S t a r t W o r k e r T h r e a d
+
+Routine Description:
+
+ This routine starts a worker thread.
+
+Arguments:
+
+ None
+
+Return Value:
+
+ 0 = OK
+ 1 = Error
+*******************************************************************
+--*/
+
+INT
+SapStartWorkerThread(
+ VOID)
+{
+ HANDLE Handle;
+ DWORD Threadid;
+ DWORD Error;
+
+ /**
+ We count this thread as running before we start it
+ for synchronization. If the start fails - then we will
+ dec this count
+ **/
+
+ SAP_INC_THREAD_COUNT("Worker Start", &SapCurWorkerThreads);
+
+ /** Start the thread **/
+
+ Handle = CreateThread(
+ NULL, /* Security Ptr */
+ 0, /* Initial stack size */
+ SapWorkerThread, /* Thread Function */
+ (LPVOID)SapSocket, /* Parm for the thread */
+ 0, /* Creation Flags */
+ &Threadid); /* Ptr to thread id */
+
+ /**
+ CreateThread failed - dec the count and return the error.
+ **/
+
+ if (Handle == NULL) {
+
+ /** **/
+
+ Error = GetLastError();
+ IF_DEBUG(ERRORS) {
+ SS_PRINT(("NWSAP: Error starting worker thread = %d\n", Error));
+ }
+
+ /** Uncount this thread running **/
+
+ SAP_DEC_THREAD_COUNT("Worker Start Error", &SapCurWorkerThreads);
+
+ /** Log the error to the event log **/
+
+ SsLogEvent(
+ NWSAP_EVENT_STARTWORKER_ERROR,
+ 0,
+ NULL,
+ Error);
+
+ /** Return Error **/
+
+ return 1;
+ }
+
+ /** We can close this handle **/
+
+ CloseHandle(Handle);
+
+ /** Return OK **/
+
+ return 0;
+}
+
+
+/*++
+*******************************************************************
+ S a p G e t N e a r e s t F r o m S e n d L i s t
+
+Routine Description:
+
+ When we get a Nearest Service Request from the network,
+ then first place we look is our advertise list. If
+ we find an entry here, we build the return packet.
+
+Arguments:
+
+ Bufferp = Ptr to buffer to build in
+ ServerType = Type of server being requested
+
+Return Value:
+
+ Length of the response.
+ 0 = There is no packet to send back. The server type
+ is not in our send list.
+*******************************************************************
+--*/
+
+INT
+SapGetNearestFromSendList(
+ PUCHAR Bufferp,
+ USHORT ServerType)
+{
+ PSAP_SERVER Servp;
+ PSAP_HEADER Hdrp;
+
+ /** Convert back to network order **/
+
+ ServerType = ntohs(ServerType);
+
+ /** Get access to the send list **/
+
+ ACQUIRE_SENDTABLE_LOCK();
+
+ /** Go find the server type in the list **/
+
+ Servp = SapServHead;
+ while (Servp) {
+
+ /** If entry being deleted - skip it **/
+
+ if (Servp->Hopcount == htons(16)) {
+ Servp = Servp->Next;
+ continue;
+ }
+
+ /** If server does not want to be used - skip it **/
+
+ if (Servp->RespondNearest == FALSE) {
+ Servp = Servp->Next;
+ continue;
+ }
+
+ /** If this is the entry we want - break out **/
+
+ if (Servp->ServerType == ServerType)
+ break;
+
+ /** Goto the next entry **/
+
+ Servp = Servp->Next;
+ }
+
+ /** If no entry found - return 0 **/
+
+ if (Servp == NULL) {
+ RELEASE_SENDTABLE_LOCK();
+ return 0;
+ }
+
+ /** Build the return entry in the buffer given **/
+
+ *(PUSHORT)Bufferp = htons(SAPTYPE_NEAREST_SERVICE_RESPONSE);
+ Bufferp += sizeof(USHORT);
+
+ Hdrp = (PSAP_HEADER)Bufferp;
+
+ Hdrp->ServerType = Servp->ServerType;
+ SAP_COPY_SERVNAME(Hdrp->ServerName, Servp->ServerName);
+ SAP_COPY_ADDRESS(Hdrp->Address, Servp->Address);
+ Hdrp->Hopcount = Servp->Hopcount;
+
+ /** Release the lock on the list **/
+
+ RELEASE_SENDTABLE_LOCK();
+
+ /** Return the length to send back **/
+
+ return SAP_HEADER_SIZE+sizeof(USHORT);
+}
+
+
+/*++
+*******************************************************************
+ S a p C a n I R e s p o n d N e a r e s t
+
+Routine Description:
+
+ For a given ServerName and SeverType see if it is allowed
+ to respond to a Find Nearest call for it. This is called
+ from the RespondNearestWan code to ask if an entry that is
+ in our table can be responded with.
+
+ It is very much assumed that this entry is in our advertise
+ table. If it is not found in the table we return NO.
+
+ NOTE: THE SENDTABLE LOCK MUST BE HELD WHEN WE GET HERE
+
+Arguments:
+
+ ServerName = Ptr to 48 byte name to look for
+ ServerType = Object type to look for
+
+Return Value:
+
+ TRUE = Yes - respond with it
+ FALSE = No - do not respond with it
+*******************************************************************
+--*/
+
+BOOL
+SapCanIRespondNearest(
+ PUCHAR ServerName,
+ USHORT ServerType)
+{
+ PSAP_SERVER Servp;
+
+ /** Convert back to network order **/
+
+ ServerType = ntohs(ServerType);
+
+ /** Go find the server type in the list **/
+
+ Servp = SapServHead;
+ while (Servp) {
+
+ /** If entry being deleted - skip it **/
+
+ if (Servp->Hopcount == htons(16)) {
+ Servp = Servp->Next;
+ continue;
+ }
+
+ /** If server does not want to be used - skip it **/
+
+ if (Servp->RespondNearest == FALSE) {
+ Servp = Servp->Next;
+ continue;
+ }
+
+ /**
+ If this is the right type and the name
+ matches - then break out of the loop to return
+ OK.
+ **/
+
+ if ((Servp->ServerType == htons(ServerType)) &&
+ (!SAP_NAMECMP(ServerName, Servp->ServerName))) {
+
+ break;
+ }
+
+
+ /** Goto the next entry **/
+
+ Servp = Servp->Next;
+ }
+
+ /** If no entry found - return cannot respond **/
+
+ if (Servp == NULL)
+ return FALSE;
+
+ /** Tell the caller he can respond with this server **/
+
+ return TRUE;
+}
+
+
+/*++
+*******************************************************************
+ S a p G e t C a r d F r o m A d d r e s s
+
+Routine Description:
+
+ When we receive a packet, we need to know which card we
+ received it on.
+
+Arguments:
+
+ Addrptr = Ptr to address that we received from
+ WanFlagp = Ptr to BOOL to store WAN status of the card
+ NetNump = Ptr to store network number of the card
+
+Return Value:
+
+ The card number that we received on
+
+ CARDRET_UNKNOWN = 0xFFFD = Cannot find card this is for.
+ CARDRET_MYSELF = 0xFFFE = It was from our SAP Agent (We sent it)
+ CARDRET_INTERNAL = 0xFFFF = It is from this machine but some other process.
+*******************************************************************
+--*/
+
+INT
+SapGetCardFromAddress(
+ PUCHAR Addrptr,
+ BOOL *WanFlagp,
+ PUCHAR NetNump)
+{
+ INT Cardnum;
+ PSAP_CARD Cardptr;
+
+ /** Get the lock on the card list **/
+
+ Cardnum = 0;
+ ACQUIRE_CARDLIST_READERS_LOCK("GetCardFromAddress");
+ Cardptr = SapCardHead;
+
+ /** Find the card this belongs to **/
+
+ while (Cardptr) {
+
+ /** If this is it - break out **/
+
+ if (!memcmp(Cardptr->Netnum, Addrptr+2, SAP_NET_LEN))
+ break;
+
+ /** Goto the next card **/
+
+ Cardnum++;
+ Cardptr = Cardptr->Next;
+ }
+
+ /** If not from any card - toss it **/
+
+ if (Cardptr == NULL) {
+
+ /**
+ If the netnum is zero and we have only one card then
+ we just use the first card.
+ **/
+
+ if (!memcmp(Addrptr + 2, SapZeros, SAP_NET_LEN)) {
+ if (SapNumCards == 1) {
+ Cardptr = SapCardHead;
+ goto GotCard;
+ }
+ }
+
+ /** Release the lock **/
+
+ RELEASE_CARDLIST_READERS_LOCK("GetCardFromAddress1");
+
+ /** Print the error **/
+
+#if DBG
+ IF_DEBUG(NOCARD) {
+ SS_PRINT(("NWSAP: HANREQ: no card for address\n"));
+ SapDumpMem(Addrptr, 15, "BAD ADDRESS");
+ }
+#endif
+
+ /** Return the error **/
+
+ return CARDRET_UNKNOWN;
+ }
+
+ /** Set the WAN Status and Netnum if user wanted **/
+
+GotCard:
+ *WanFlagp = Cardptr->Wanflag;
+ if (NetNump) {
+ SAP_COPY_NETNUM(NetNump, Cardptr->Netnum);
+ }
+
+ /**
+ If this is from my local machine - then we want
+ to check to see if we was sent by me (The SAP AGENT),
+ or by some other process.
+ **/
+
+ if (!memcmp(Cardptr->Nodenum, Addrptr+6, SAP_NODE_LEN)) {
+
+ USHORT Socket;
+
+ /** **/
+
+ Socket = ntohs(*(PUSHORT)(Addrptr + 12));
+
+ /** This is from SAP Agent - return it **/
+
+ if (Socket == NWSAP_SAP_SOCKET) {
+ RELEASE_CARDLIST_READERS_LOCK("GetCardFromAddress2");
+ return CARDRET_MYSELF;
+ }
+
+ /** We got one from some other in this machine **/
+
+ RELEASE_CARDLIST_READERS_LOCK("GetCardFromAddress3");
+ return CARDRET_INTERNAL;
+ }
+
+ /** Return the card number **/
+
+ RELEASE_CARDLIST_READERS_LOCK("GetCardFromAddress4");
+ return Cardnum;
+}
diff --git a/private/net/svcdlls/nwsap/server/nwsap.def b/private/net/svcdlls/nwsap/server/nwsap.def
new file mode 100644
index 000000000..2a53dbaff
--- /dev/null
+++ b/private/net/svcdlls/nwsap/server/nwsap.def
@@ -0,0 +1,6 @@
+NAME nwsap.dll
+
+DESCRIPTION 'NT NCP SAP Agent'
+
+EXPORTS
+ ServiceEntry
diff --git a/private/net/svcdlls/nwsap/server/nwsap.rc b/private/net/svcdlls/nwsap/server/nwsap.rc
new file mode 100644
index 000000000..de477b68d
--- /dev/null
+++ b/private/net/svcdlls/nwsap/server/nwsap.rc
@@ -0,0 +1,11 @@
+#include <windows.h>
+
+#include <ntverp.h>
+
+#define VER_FILETYPE VFT_DLL
+#define VER_FILESUBTYPE VFT2_UNKNOWN
+#define VER_FILEDESCRIPTION_STR "NW SAP Agent DLL"
+#define VER_INTERNALNAME_STR "NWSAP.DLL"
+
+#include "common.ver"
+
diff --git a/private/net/svcdlls/nwsap/server/nwsapp.h b/private/net/svcdlls/nwsap/server/nwsapp.h
new file mode 100644
index 000000000..62a6e9c5c
--- /dev/null
+++ b/private/net/svcdlls/nwsap/server/nwsapp.h
@@ -0,0 +1,487 @@
+/*++
+
+Copyright (c) 1994 Microsoft Corporation
+Copyright (c) 1993 Micro Computer Systems, Inc.
+
+Module Name:
+
+ net\svcdlls\nwsap\server\nwsapp.h
+
+Abstract:
+
+ This is the include file used by the NT SAP Agent files. It
+ is private for the SAP Agent code. All programs wanting to use
+ the API calls should use NWSAP.H.
+
+Author:
+
+ Brian Walker (MCS) 06-30-1993
+
+Revision History:
+
+--*/
+
+#ifndef _NWSAP_SAPP_
+#define _NWSAP_SAPP_
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+
+#include <windef.h>
+#include <winbase.h>
+#include <winsock.h>
+#include <wsipx.h>
+#include <wsnwlink.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <nwsap.h>
+#include "sdmd.h"
+
+#include "ssdebug.h"
+
+#include <netevent.h>
+
+/** **/
+
+#define SAP_OBJNAME_LEN 48 /* Length of a server name */
+#define SAP_ADDR_LEN 12 /* Length of IPX address (Net+Node+Socket) */
+
+#define SAP_NET_LEN 4 /* Length of a network number */
+#define SAP_NODE_LEN 6 /* Length of a node number */
+
+/** **/
+
+#define NWSAP_SAP_SOCKET 0x0452
+
+/**
+ We must define this Winsock option code here because it
+ is not in wsisn.h.
+**/
+
+#ifndef IPX_ADDRESS_NOTIFY
+#define IPX_ADDRESS_NOTIFY 0x400c
+#endif
+
+/** Flags for the SapWanFilter flag **/
+
+#define SAPWAN_NOTHING 0 /* Send nothing on the WAN except initial query */
+#define SAPWAN_CHANGESONLY 1 /* Send servers that have changed only */
+#define SAPWAN_REGULAR 2 /* Just handle it as a regular card */
+
+/** **/
+
+#define SAPFILTER_PASSLIST 0
+#define SAPFILTER_DONTPASSLIST 1
+
+/**
+ This macro is used to compare 2 names.
+
+ Arguments - n1 = First name to compare
+ n2 = Second name to compare
+
+ Returns - 0 = Names are the same
+ 1 = n1 > n2
+ -1 = n1 < n2
+**/
+
+#define SAP_NAMECMP(n1,n2) (strcmp(n1,n2))
+
+/** Macros to copy names and addresses around **/
+
+#define SAP_COPY_SERVNAME(dest,src) memcpy(dest,src,SAP_OBJNAME_LEN)
+#define SAP_COPY_ADDRESS(dest,src) memcpy(dest,src,SAP_ADDR_LEN)
+#define SAP_COPY_NETNUM(dest,src) memcpy(dest,src,SAP_NET_LEN)
+#define SAP_COPY_NODENUM(dest,src) memcpy(dest,src,SAP_NODE_LEN)
+
+/** **/
+
+#if DBG
+#ifdef SAP_MEMORY_TRACE
+#define SAP_MALLOC(a,b) SapDebugMalloc(a,b)
+#define SAP_FREE(a,b) SapDebugFree(a,b)
+#else
+#define SAP_MALLOC(a,b) malloc(a)
+#define SAP_FREE(a,b) free(a)
+#endif
+#else
+#define SAP_MALLOC(a,b) malloc(a)
+#define SAP_FREE(a,b) free(a)
+#endif
+
+/***
+ These define return codes from SapGetCardFromAddress. They
+ tell about special conditions.
+***/
+
+#define CARDRET_UNKNOWN 0xFFFD /* Network not found */
+#define CARDRET_MYSELF 0xFFFE /* I (SAP Agent) sent the packet */
+#define CARDRET_INTERNAL 0xFFFF /* Internal process other then myself */
+
+
+/********************************************************************
+ This is the receive buffer structure we use. This
+ is allocated by the receive thread to receive a
+ SAP from the network. This structure is then passed to
+ the worker thread for processing.
+*********************************************************************/
+typedef struct _SAP_RECVBLOCK {
+ LIST_ENTRY ListEntry; /* Used to link on worker list */
+ INT Datalen; /* Length of data in the buffer */
+ UCHAR Address[16]; /* Address of who sent the packet */
+ INT AddressLength; /* Length of the address */
+ UCHAR Buffer[1]; /* Buffer - must be last */
+} SAP_RECVBLOCK, *PSAP_RECVBLOCK;
+
+#define SAP_RECVBLOCK_SIZE FIELD_OFFSET(SAP_RECVBLOCK,Buffer)
+
+#define SAP_MAX_RECVLEN 512 /* Receive buffer size we use */
+#define SAP_MAX_SENDLEN 512 /* Send buffer size we use */
+
+/**
+ This is a packet entry that is kept by SAP_PKTLIST
+**/
+
+typedef struct _SAP_PKTENTRY {
+ struct _SAP_PKTENTRY *Next; /* Ptr to next PKTENTRY in list */
+ INT SendLength; /* Length of data in this buffer */
+ UCHAR Buffer[1]; /* Buffer we build into */
+} SAP_PKTENTRY, *PSAP_PKTENTRY;
+
+/** Length of SAP_PKTENTRY structure up to the buffer entry **/
+
+#define SAP_PKTENTRY_SIZE FIELD_OFFSET(SAP_PKTENTRY,Buffer)
+
+/**
+ This keeps track of a list of packets that are being
+ sent.
+**/
+
+typedef struct _SAP_PKTLIST {
+
+ /** Ptr to head/tail of send list **/
+
+ PSAP_PKTENTRY PktHead;
+ PSAP_PKTENTRY PktTail;
+ INT NumPkts;
+
+ /** Num entries in current packet **/
+
+ INT Curnum; /* Current num entries in list */
+ PUCHAR Curptr; /* Next build pointer */
+ PSAP_PKTENTRY Curpkt; /* Ptr to current packet entry */
+} SAP_PKTLIST, *PSAP_PKTLIST;
+
+/**
+ To handle SAP routing, we must know about each card that
+ NWLink is using. To handle this, we get a list of them
+ at init time. This structure holds one card entry.
+**/
+
+typedef struct _SAP_CARD {
+ struct _SAP_CARD *Next;
+ ULONG Linkspeed; /* Linkspeed of card in 100 b/sec */
+ INT Maxpkt; /* Max packet size can send on card */
+ BOOL Wanflag; /* TRUE = Yes */
+ UCHAR ReqCount; /* Count to send on gen requests */
+ UCHAR Number; /* Number assigned by transport */
+ UCHAR Netnum[4]; /* Network number of adapter */
+ UCHAR Nodenum[6]; /* Node address of adapter */
+ SAP_PKTLIST Plist; /* Ptr to list for advertising */
+} SAP_CARD, *PSAP_CARD;
+
+#define SAP_CARD_SIZE sizeof(SAP_CARD)
+
+
+/********************************************************************
+ Defines and structures that define the Service Advertising
+ Protocol packet that we transmit and receive.
+*********************************************************************/
+
+/** Query/Response types **/
+
+#define SAPTYPE_GENERAL_SERVICE_QUERY 1
+#define SAPTYPE_GENERAL_SERVICE_RESPONSE 2
+#define SAPTYPE_NEAREST_SERVICE_QUERY 3
+#define SAPTYPE_NEAREST_SERVICE_RESPONSE 4
+
+/**
+ A SAP Packet has a USHORT query type (above), plus 0 or more
+ of the SAP_HEADER structures defined here.
+**/
+
+/** Structure of entry in a SAP packet **/
+
+typedef struct _SAP_HEADER {
+ USHORT ServerType; /* Service type code */
+ UCHAR ServerName[SAP_OBJNAME_LEN];
+ UCHAR Address[SAP_ADDR_LEN];
+ USHORT Hopcount;
+} SAP_HEADER, *PSAP_HEADER;
+
+#define SAP_HEADER_SIZE sizeof(SAP_HEADER)
+
+/** General Service request structure **/
+
+typedef struct {
+ USHORT QueryType;
+ USHORT ServerType;
+} SAP_REQUEST, *PSAP_REQUEST;
+#define SAP_REQUEST_SIZE sizeof(SAP_REQUEST)
+
+
+/*********************************************************************
+ When sending more then 1 packet out at a time, we
+ have to delay 55 ms between the packets. This is defined
+ in the Novell docs about SAP. We do not want to overrun
+ routers.
+*********************************************************************/
+
+#define NWSAP_SEND_DELAY() Sleep(55)
+
+
+/*********************************************************************
+ This structure keeps track of an entry in our
+ advertise table.
+*********************************************************************/
+
+typedef struct _SAP_SERVER {
+ struct _SAP_SERVER *Next;
+ USHORT ServerType; /* Server Type */
+ UCHAR ServerName[SAP_OBJNAME_LEN]; /* Server Name */
+ UCHAR Address[SAP_ADDR_LEN]; /* Address of server*/
+ USHORT Hopcount; /* Hop Count */
+ BOOL Changed; /* TRUE = Yes */
+ BOOL RespondNearest;
+ ULONG ClientId; /* Client that added this */
+} SAP_SERVER, *PSAP_SERVER;
+
+#define SAP_SERVER_SIZE sizeof(SAP_SERVER)
+
+
+/*********************************************************************
+ This structure keeps track of an entry in our
+ server name filter table.
+*********************************************************************/
+
+/** Num entries in the hash table **/
+
+#define SAP_NAMEFILTER_HASHSIZE 255
+
+/** Structure to keep track of one entry **/
+
+typedef struct _SAP_NAMEFILTER {
+ struct _SAP_NAMEFILTER *Next;
+ UCHAR ServerName[SAP_OBJNAME_LEN];
+} SAP_NAMEFILTER, *PSAP_NAMEFILTER;
+#define SAP_NAMEFILTER_SIZE sizeof(SAP_NAMEFILTER)
+
+/** Structure to keep track of a hash entry **/
+
+typedef struct _SAP_FILTERHDR {
+ PSAP_NAMEFILTER FirstEntry;
+} SAP_FILTERHDR, *PSAP_FILTERHDR;
+
+
+
+/***********************************************************************
+
+***********************************************************************/
+
+#define ACQUIRE_RECVTABLE_LOCK() EnterCriticalSection(&SapRecvCriticalSection)
+#define ACQUIRE_SENDTABLE_LOCK() EnterCriticalSection(&SapSendCriticalSection)
+#define ACQUIRE_FREETABLE_LOCK() EnterCriticalSection(&SapFreeCriticalSection)
+#define ACQUIRE_THREADCOUNT_LOCK() EnterCriticalSection(&SapThreadCountCriticalSection)
+#define ACQUIRE_LPC_THREADCOUNT_LOCK() EnterCriticalSection(&SapLpcThreadCountCriticalSection)
+#define ACQUIRE_LPCCLIENT_LOCK() EnterCriticalSection(&SapLpcClientCriticalSection)
+#define ACQUIRE_WANRECVTABLE_LOCK() EnterCriticalSection(&SapWanRecvCriticalSection)
+#define ACQUIRE_WANFREETABLE_LOCK() EnterCriticalSection(&SapWanFreeCriticalSection)
+#define ACQUIRE_SENDBUSY_LOCK() EnterCriticalSection(&SapSendBusyCriticalSection)
+
+#define RELEASE_RECVTABLE_LOCK() LeaveCriticalSection(&SapRecvCriticalSection)
+#define RELEASE_SENDTABLE_LOCK() LeaveCriticalSection(&SapSendCriticalSection)
+#define RELEASE_FREETABLE_LOCK() LeaveCriticalSection(&SapFreeCriticalSection)
+#define RELEASE_THREADCOUNT_LOCK() LeaveCriticalSection(&SapThreadCountCriticalSection)
+#define RELEASE_LPC_THREADCOUNT_LOCK() LeaveCriticalSection(&SapLpcThreadCountCriticalSection)
+#define RELEASE_LPCCLIENT_LOCK() LeaveCriticalSection(&SapLpcClientCriticalSection)
+#define RELEASE_WANRECVTABLE_LOCK() LeaveCriticalSection(&SapWanRecvCriticalSection)
+#define RELEASE_WANFREETABLE_LOCK() LeaveCriticalSection(&SapWanFreeCriticalSection)
+#define RELEASE_SENDBUSY_LOCK() LeaveCriticalSection(&SapSendBusyCriticalSection)
+
+
+/*******************************************************************
+ These macros are used to check the number of worker
+ threads that are waiting for work to happen.
+*******************************************************************/
+
+#define INC_WORKER_THREAD_WAITING_COUNT() { \
+ ACQUIRE_RECVTABLE_LOCK(); \
+ SapWorkerThreadWaiting++; \
+ RELEASE_RECVTABLE_LOCK(); \
+}
+
+#define DEC_WORKER_THREAD_WAITING_COUNT() { \
+ ACQUIRE_RECVTABLE_LOCK(); \
+ SapWorkerThreadWaiting--; \
+ RELEASE_RECVTABLE_LOCK(); \
+}
+
+
+/**
+ These are used to keep track of how many receive and worker threads
+ we have. At terminate time we need to be able to wait for all
+ these to terminate.
+
+ The THREADCOUNT lock is held when these macros are called.
+
+ The "m" is a message.
+ lp is a pointer to a long to inc/dec
+**/
+
+#define SAP_INC_THREAD_COUNT(m,lp) { \
+ ACQUIRE_THREADCOUNT_LOCK(); \
+ SapThreadCount++; \
+ if (lp) { \
+ (*(PULONG)lp)++; \
+ } \
+ IF_DEBUG(THREADTRACE) { \
+ SS_PRINT(("INC THREAD COUNT from %s: new count = %d\n", m, SapThreadCount)); \
+ } \
+ ResetEvent(SapThreadEvent); \
+ RELEASE_THREADCOUNT_LOCK(); \
+ }
+
+#define SAP_DEC_THREAD_COUNT(m,lp) { \
+ ACQUIRE_THREADCOUNT_LOCK(); \
+ SapThreadCount--; \
+ if (lp) { \
+ (*(PULONG)lp)--; \
+ } \
+ IF_DEBUG(THREADTRACE) { \
+ SS_PRINT(("DEC THREAD COUNT from %s: new count = %d\n", m, SapThreadCount)); \
+ } \
+ if (SapThreadCount == 0) { \
+ IF_DEBUG(THREADTRACE) { \
+ SS_PRINT(("Setting event for Sap thread waiting\n")); \
+ } \
+ RELEASE_THREADCOUNT_LOCK(); \
+ SetEvent(SapThreadEvent); \
+ } \
+ else { \
+ RELEASE_THREADCOUNT_LOCK(); \
+ } \
+ }
+
+
+/**
+ These are used to keep track of how many LPC worker threads
+ we have. At terminate time we need to be able to wait for all
+ these to terminate.
+
+ The THREADCOUNT lock is held when these macros are called.
+
+ The "m" is a message.
+ lp is a pointer to a long to inc/dec
+**/
+
+#define SAP_INC_LPC_THREAD_COUNT(m) { \
+ ACQUIRE_LPC_THREADCOUNT_LOCK(); \
+ SapLpcNumWorkers++; \
+ IF_DEBUG(THREADTRACE) { \
+ SS_PRINT(("INC LPC THREAD COUNT from %s: new count = %d\n", m, SapLpcNumWorkers)); \
+ } \
+ ResetEvent(SapLpcThreadEvent); \
+ RELEASE_LPC_THREADCOUNT_LOCK(); \
+ }
+
+#define SAP_DEC_LPC_THREAD_COUNT(m) { \
+ ACQUIRE_LPC_THREADCOUNT_LOCK(); \
+ SapLpcNumWorkers--; \
+ IF_DEBUG(THREADTRACE) { \
+ SS_PRINT(("DEC LPC THREAD COUNT from %s: new count = %d\n", m, SapLpcNumWorkers)); \
+ } \
+ if (SapLpcNumWorkers == 0) { \
+ IF_DEBUG(THREADTRACE) { \
+ SS_PRINT(("Setting event for Sap LPC thread waiting\n")); \
+ } \
+ RELEASE_LPC_THREADCOUNT_LOCK(); \
+ SetEvent(SapLpcThreadEvent); \
+ } \
+ else { \
+ RELEASE_LPC_THREADCOUNT_LOCK(); \
+ } \
+ }
+
+
+/*******************************************************************
+ This is a readers/writers lock for the
+ card list.
+*******************************************************************/
+
+#define SAP_CARDLIST_LTO (60*1000) /* 1 Minute = writers lock timeout */
+
+#define ACQUIRE_CARDLIST_READERS_LOCK(m) { \
+ IF_DEBUG(LOCKS) { \
+ SS_PRINT(("SAP: ACLRL: ENT: From %s\n", m));\
+ } \
+ EnterCriticalSection(&SapCardlistCriticalSection);\
+ SapCardlistLockCount++; \
+ if (SapCardlistLockCount == 1) \
+ ResetEvent(SapCardlistSynchEvent); \
+ LeaveCriticalSection(&SapCardlistCriticalSection);\
+ IF_DEBUG(LOCKS) { \
+ SS_PRINT(("SAP: ACLRL: GOT: From %s\n", m));\
+ } \
+ }
+
+#define RELEASE_CARDLIST_READERS_LOCK(m) { \
+ IF_DEBUG(LOCKS) { \
+ SS_PRINT(("SAP: RCLRL: ENT: From %s\n", m));\
+ } \
+ EnterCriticalSection(&SapCardlistCriticalSection);\
+ IF_DEBUG(ERRORS) { \
+ if (SapCardlistLockCount == 0) { \
+ SS_PRINT(("NWSAP: RCLRL: Lock Count is 0\n"));\
+ } \
+ } \
+ SapCardlistLockCount--; \
+ if (SapCardlistLockCount == 0) \
+ SetEvent(SapCardlistSynchEvent); \
+ LeaveCriticalSection(&SapCardlistCriticalSection);\
+ IF_DEBUG(LOCKS) { \
+ SS_PRINT(("SAP: RCLRL: REL: From %s\n", m));\
+ } \
+ }
+
+#define ACQUIRE_CARDLIST_WRITERS_LOCK(statx,m) { \
+ IF_DEBUG(LOCKS) { \
+ SS_PRINT(("SAP: ACLWL: ENT: From %s\n", m)); \
+ } \
+ while (1) { \
+ EnterCriticalSection(&SapCardlistCriticalSection); \
+ if (SapCardlistLockCount != 0) { \
+ LeaveCriticalSection(&SapCardlistCriticalSection); \
+ statx = WaitForSingleObjectEx(SapCardlistSynchEvent,SAP_CARDLIST_LTO,TRUE); \
+ if (statx == WAIT_TIMEOUT) \
+ break; \
+ } \
+ else { \
+ statx = 0; \
+ break; \
+ } \
+ } \
+ IF_DEBUG(LOCKS) { \
+ SS_PRINT(("SAP: ACLWL: GOT: From %s\n", m)); \
+ } \
+ }
+
+#define RELEASE_CARDLIST_WRITERS_LOCK(m) { \
+ IF_DEBUG(LOCKS) { \
+ SS_PRINT(("SAP: RCLWL: ENT: From %s\n", m));\
+ } \
+ LeaveCriticalSection(&SapCardlistCriticalSection); \
+ }
+
+#endif
+
diff --git a/private/net/svcdlls/nwsap/server/precomp.h b/private/net/svcdlls/nwsap/server/precomp.h
new file mode 100644
index 000000000..0c6bb90c8
--- /dev/null
+++ b/private/net/svcdlls/nwsap/server/precomp.h
@@ -0,0 +1,6 @@
+
+#include "nwsapp.h"
+#include "sdmdp.h"
+#include "..\saplpc.h"
+#include "externs.h"
+
diff --git a/private/net/svcdlls/nwsap/server/registry.c b/private/net/svcdlls/nwsap/server/registry.c
new file mode 100644
index 000000000..76885e2dc
--- /dev/null
+++ b/private/net/svcdlls/nwsap/server/registry.c
@@ -0,0 +1,1110 @@
+/*++
+
+Copyright (c) 1994 Microsoft Corporation
+Copyright (c) 1993 Micro Computer Systems, Inc.
+
+Module Name:
+
+ net\svcdlls\nwsap\server\registry.c
+
+Abstract:
+
+ This file reads the registry for the NT SAP Agent.
+
+Author:
+
+ Brian Walker (MCS) 06-15-1993
+
+Revision History:
+
+--*/
+
+#include "precomp.h"
+#pragma hdrstop
+
+/** Internal Function Prototypes **/
+
+INT
+SapGetRegDword(
+ HKEY HKey,
+ LPTSTR KeyName,
+ PULONG ValuePtr);
+
+VOID
+SapReadFilterNames(
+ HANDLE Handle,
+ BOOL Flag);
+
+NTSTATUS
+SapAddFilterEntries(
+ PWSTR ValueName,
+ ULONG ValueType,
+ PVOID ValueData,
+ ULONG ValueLen,
+ PVOID Context,
+ PVOID Context2);
+
+NTSTATUS
+SapReadHopLans(
+ INT *pReadHopLans);
+
+/** **/
+
+TCHAR KeySendTime[] = TEXT("SendTime");
+TCHAR KeyEntryTimeout[] = TEXT("EntryTimeout");
+TCHAR KeyArraySize[] = TEXT("InitialArraySize");
+TCHAR KeyNumWorkers[] = TEXT("NumberWorkerThreads");
+TCHAR KeyNumRecvs[] = TEXT("NumberRecvThreads");
+TCHAR KeyMaxRecvBuf[] = TEXT("MaxRecvBufLookAhead");
+TCHAR KeyWorkerThreshhold[] = TEXT("WorkerThreshhold");
+TCHAR KeyMaxWorkers[] = TEXT("MaxWorkerThreads");
+TCHAR KeyWorkerDelay[] = TEXT("WorkerStartupDelay");
+TCHAR KeyDebug[] = TEXT("Debug");
+TCHAR KeyNameHashSize[] = TEXT("NameHashTableSize");
+TCHAR KeyRespondInternals[] = TEXT("RespondForInternalServers");
+TCHAR KeyMaxLpcClients[] = TEXT("NumberLpcThreads");
+TCHAR KeyFilterFlag[] = TEXT("ActivePassList");
+TCHAR KeyWanFilter[] = TEXT("WANFilter");
+TCHAR KeyWanUpdateTime[] = TEXT("WANUpdateTime");
+TCHAR KeyWanNotifyThreads[] = TEXT("WANNotifyThreads");
+TCHAR KeyAllowDuplicateServers[] = TEXT("AllowDuplicateServers");
+TCHAR KeyDelayOnMallocFail[] = TEXT("DelayOnMallocFail");
+TCHAR KeyDelayOnNetError[] = TEXT("DelayOnNetError");
+TCHAR KeyDelayRespondToGeneral[] = TEXT("DelayRespondToGeneral");
+
+TCHAR SapRegParmKey[] = TEXT("SYSTEM\\CurrentControlSet\\Services\\NwSapAgent\\Parameters");
+
+
+/** Defaults for all the registry parameters **/
+
+#define SAPDEF_SENDMIUTES 1 /* Minutes between advertises */
+#define SAPMIN_SENDMINUTES 1
+#define SAPMAX_SENDMINUTES 30
+
+#define SAPDEF_TIMEOUTINTERVAL 5 /* Minutes for entries to timeout */
+#define SAPMIN_TIMEOUTINTERVAL 3
+#define SAPMAX_TIMEOUTINTERVAL 30
+
+#define SAPDEF_NUMARRAYENTRIES 32 /* Initial num SDMD array entries */
+#define SAPMIN_NUMARRAYENTRIES 32
+#define SAPMAX_NUMARRAYENTRIES 10000
+
+#define SAPDEF_NUMWORKERTHREADS 1 /* Number of worker threads */
+#define SAPMIN_NUMWORKERTHREADS 1
+#define SAPMAX_NUMWORKERTHREADS 20
+
+#define SAPDEF_NUMRECVTHREADS 1 /* Number of receive threads */
+#define SAPMIN_NUMRECVTHREADS 1
+#define SAPMAX_NUMRECVTHREADS 10
+
+#define SAPDEF_MAXFREEBUFS 20
+#define SAPMIN_MAXFREEBUFS 1
+#define SAPMAX_MAXFREEBUFS 50
+
+#define SAPDEF_NEWWORKERTHRESHHOLD 50 /* Num recv bufs for starting new worker thread */
+#define SAPMIN_NEWWORKERTHRESHHOLD 5
+#define SAPMAX_NEWWORKERTHRESHHOLD 100
+#define SAPMAX_MAXENTRIES_RECVLIST 500
+
+#define SAPDEF_MAXEVERWORKERTHREADS 3 /* Max worker threads ever */
+#define SAPMIN_MAXEVERWORKERTHREADS 1
+#define SAPMAX_MAXEVERWORKERTHREADS 15
+
+#define SAPDEF_NEWWORKERTIMEOUT 1000 /* Milliseconds (1000 = 1 second) */
+#define SAPMIN_NEWWORKERTIMEOUT 1000
+#define SAPMAX_NEWWORKERTIMEOUT 9000
+
+#define SAPDEF_HASHTABLESIZE 255
+#define SAPMIN_HASHTABLESIZE 127
+#define SAPMAX_HASHTABLESIZE 999
+
+#define SAPDEF_DOINTERNALS 1 /* Default = Yes */
+#define SAPMIN_DOINTERNALS 0
+#define SAPMAX_DOINTERNALS 1
+
+#define SAPDEF_LPCCLIENTS 3
+#define SAPMIN_LPCCLIENTS 1
+#define SAPMAX_LPCCLIENTS 10
+
+#define SAPDEF_WANMAXFREE 2
+#define SAPMIN_WANMAXFREE 1
+#define SAPMAX_WANMAXFREE 10
+
+#define SAPDEF_WANCHECKTHREADS 1
+#define SAPMIN_WANCHECKTHREADS 1
+#define SAPMAX_WANCHECKTHREADS 10
+
+#define SAPDEF_WANUPDATETIME 15
+#define SAPMIN_WANUPDATETIME 5
+#define SAPMAX_WANUPDATETIME 60
+
+#define SAPDEF_DONTHOPLANS 1 /* Def = Don't hop lan-lan */
+#define SAPMIN_DONTHOPLANS 0 /* 0 = Do hop lan-lan */
+#define SAPMAX_DONTHOPLANS 1 /* 1 = Don't hop lan-lan */
+
+#define SAPDEF_ALLOWDUPS 0
+
+#define SAPDEF_WANFILTER SAPWAN_NOTHING
+
+#define SAPDEF_ACTIVEFILTER SAPFILTER_PASSLIST
+
+#define SAPDEF_DELAYONMALLOCFAIL 20
+#define SAPMIN_DELAYONMALLOCFAIL 1
+#define SAPMAX_DELAYONMALLOCFAIL 120
+
+#define SAPDEF_DELAYONNETERROR 3
+#define SAPMIN_DELAYONNETERROR 0
+#define SAPMAX_DELAYONNETERROR 60
+
+//
+// DelayRespondToGeneral specified in number of milliseconds
+//
+
+#define SAPDEF_DELAYRESPONDTOGENERAL 0
+#define SAPMIN_DELAYRESPONDTOGENERAL 0
+#define SAPMAX_DELAYRESPONDTOGENERAL 2000
+
+
+/*++
+*******************************************************************
+ S a p R e a d R e g i s t r y
+
+Routine Description:
+
+ This routine reads the registry during initialization
+ and sets all the configurable variables up.
+
+Arguments:
+
+ None
+
+Return Value:
+
+ 0 = OK
+ Else = Error
+*******************************************************************
+--*/
+
+INT
+SapReadRegistry(
+ VOID)
+{
+ HKEY handle;
+ INT error;
+ ULONG value;
+ DWORD eventid;
+ LPWSTR substr[1];
+
+ /** Set the defaults for everything **/
+
+ SapSendMinutes = SAPDEF_SENDMIUTES;
+ SapTimeoutInterval = SAPDEF_TIMEOUTINTERVAL;
+ SapNumArrayEntries = SAPDEF_NUMARRAYENTRIES;
+ SapNumWorkerThreads = SAPDEF_NUMWORKERTHREADS;
+ SapNumRecvThreads = SAPDEF_NUMRECVTHREADS;
+ SapMaxFreeBufs = SAPDEF_MAXFREEBUFS;
+ SapNewWorkerThreshhold = SAPDEF_NEWWORKERTHRESHHOLD;
+ SapMaxEverWorkerThreads = SAPDEF_MAXEVERWORKERTHREADS;
+ SapNewWorkerTimeout = SAPDEF_NEWWORKERTIMEOUT;
+ SapHashTableSize = SAPDEF_HASHTABLESIZE;
+ SapRespondForInternal = SAPDEF_DOINTERNALS;
+ SapLpcMaxWorkers = SAPDEF_LPCCLIENTS;
+ SapWanFilter = SAPDEF_WANFILTER;
+ SapActiveFilter = SAPDEF_ACTIVEFILTER;
+ SapWanMaxFree = SAPDEF_WANMAXFREE;
+ SapNumWanNotifyThreads = SAPDEF_WANCHECKTHREADS;
+ SapRecheckAllCardsTime = SAPDEF_WANUPDATETIME;
+ SapDontHopLans = SAPDEF_DONTHOPLANS;
+ SapMaxBackup = SAPMAX_MAXENTRIES_RECVLIST;
+ SapRecvDelayOnNetError = SAPDEF_DELAYONNETERROR;
+ SapAllowDuplicateServers = SAPDEF_ALLOWDUPS;
+ SapRecvDelayOnMallocFail = SAPDEF_DELAYONMALLOCFAIL;
+ SapDelayRespondToGeneral = SAPDEF_DELAYRESPONDTOGENERAL;
+
+ /** Open the registry key for the server (PARAMETERS) **/
+
+ error = RegOpenKey(HKEY_LOCAL_MACHINE, SapRegParmKey, &handle);
+
+ if (error) {
+
+ /** Debug stuff **/
+
+ IF_DEBUG(REGISTRY) {
+ SS_PRINT(("SAP: Error opening registry key: error = %d\n", error));
+ }
+
+ /** Log the event here **/
+
+ substr[0] = SapRegParmKey;
+ SsLogEvent(
+ NWSAP_EVENT_KEY_NOT_FOUND,
+ 1,
+ substr,
+ error);
+
+ /** Return the error **/
+
+ return error;
+ }
+
+ /** Get a new debug value (If there is one) **/
+
+ error = SapGetRegDword(handle, KeyDebug, &value);
+ if (!error) {
+ SsDebug = value;
+ }
+ IF_DEBUG(REGISTRY) {
+ SS_PRINT(("NWSAP: SapDebugValue = 0x%lx\n", SsDebug));
+ }
+
+ /** Get the Send Interval in minutes **/
+
+ error = SapGetRegDword(handle, KeySendTime, &value);
+ if (!error) {
+ if (value) {
+
+ /** Check for MIN/MAX **/
+
+ if (value < SAPMIN_SENDMINUTES)
+ value = SAPMIN_SENDMINUTES;
+
+ if (value > SAPMAX_SENDMINUTES)
+ value = SAPMAX_SENDMINUTES;
+
+ /** Set the new value **/
+
+ SapSendMinutes = (INT)value;
+ }
+ }
+
+ IF_DEBUG(REGISTRY) {
+ SS_PRINT(("NWSAP: SapSendMinutes = %d\n", SapSendMinutes));
+ }
+
+ /** Get the entry timeout in minutes **/
+
+ error = SapGetRegDword(handle, KeyEntryTimeout, &value);
+ if (!error) {
+ if (value) {
+
+ /** Check for MIN/MAX **/
+
+ if (value < SAPMIN_TIMEOUTINTERVAL)
+ value = SAPMIN_TIMEOUTINTERVAL;
+
+ if (value > SAPMAX_TIMEOUTINTERVAL)
+ value = SAPMAX_TIMEOUTINTERVAL;
+
+ /** Set the new value **/
+
+ SapTimeoutInterval = (INT)value;
+ }
+ }
+
+ IF_DEBUG(REGISTRY) {
+ SS_PRINT(("NWSAP: SapTimeoutInterval = %d\n", SapTimeoutInterval));
+ }
+
+ /** Get the initial array size (num entries) **/
+
+ error = SapGetRegDword(handle, KeyArraySize, &value);
+ if (!error) {
+ if (value) {
+
+ /** Check for MIN/MAX **/
+
+ if (value < SAPMIN_NUMARRAYENTRIES)
+ value = SAPMIN_NUMARRAYENTRIES;
+
+ if (value > SAPMAX_NUMARRAYENTRIES)
+ value = SAPMAX_NUMARRAYENTRIES;
+
+
+ /** Set the new value **/
+
+ SapNumArrayEntries = (INT)value;
+ }
+ }
+
+ IF_DEBUG(REGISTRY) {
+ SS_PRINT(("NWSAP: SapNumArrayEntries = %d\n", SapNumArrayEntries));
+ }
+
+ /** Get the initial number of worker threads **/
+
+ error = SapGetRegDword(handle, KeyNumWorkers, &value);
+ if (!error) {
+ if (value) {
+
+ /** Check for MIN/MAX **/
+
+ if (value < SAPMIN_NUMWORKERTHREADS)
+ value = SAPMIN_NUMWORKERTHREADS;
+
+ if (value > SAPMAX_NUMWORKERTHREADS)
+ value = SAPMAX_NUMWORKERTHREADS;
+
+ /** Set the new value **/
+
+ SapNumWorkerThreads = (INT)value;
+ }
+ }
+
+ IF_DEBUG(REGISTRY) {
+ SS_PRINT(("NWSAP: SapNumWorkerThreads = %d\n", SapNumWorkerThreads));
+ }
+
+ /** Get the initial number of receive threads **/
+
+ error = SapGetRegDword(handle, KeyNumRecvs, &value);
+ if (!error) {
+ if (value) {
+
+ /** Check for MIN/MAX **/
+
+ if (value < SAPMIN_NUMRECVTHREADS)
+ value = SAPMIN_NUMRECVTHREADS;
+
+ if (value > SAPMAX_NUMRECVTHREADS)
+ value = SAPMAX_NUMRECVTHREADS;
+
+ /** Set the new value **/
+
+ SapNumRecvThreads = (INT)value;
+ }
+ }
+
+ IF_DEBUG(REGISTRY) {
+ SS_PRINT(("NWSAP: SapNumRecvThreads = %d\n", SapNumRecvThreads));
+ }
+
+ /** Get the max recv bufs to hold in look ahead queue **/
+
+ error = SapGetRegDword(handle, KeyMaxRecvBuf, &value);
+ if (!error) {
+ if (value) {
+
+ /** Check for MIN/MAX **/
+
+ if (value < SAPMIN_MAXFREEBUFS)
+ value = SAPMIN_MAXFREEBUFS;
+
+ if (value > SAPMAX_MAXFREEBUFS)
+ value = SAPMAX_MAXFREEBUFS;
+
+ /** Set the new value **/
+
+ SapMaxFreeBufs = (INT)value;
+ }
+ }
+
+ IF_DEBUG(REGISTRY) {
+ SS_PRINT(("NWSAP: SapMaxFreeBufs = %d\n", SapMaxFreeBufs));
+ }
+
+ /** Get the num bufs in worker list to start new worker thread **/
+
+ error = SapGetRegDword(handle, KeyWorkerThreshhold, &value);
+ if (!error) {
+ if (value) {
+
+ /** Check for MIN/MAX **/
+
+ if (value < SAPMIN_NEWWORKERTHRESHHOLD)
+ value = SAPMIN_NEWWORKERTHRESHHOLD;
+
+ if (value > SAPMAX_NEWWORKERTHRESHHOLD)
+ value = SAPMAX_NEWWORKERTHRESHHOLD;
+
+ /** Set the new value **/
+
+ SapNewWorkerThreshhold = (INT)value;
+ }
+ }
+
+ IF_DEBUG(REGISTRY) {
+ SS_PRINT(("NWSAP: SapNewWorkerThreshhold = %d\n", SapNewWorkerThreshhold));
+ }
+
+ /** Get the max ever worker threads we can have **/
+
+ error = SapGetRegDword(handle, KeyMaxWorkers, &value);
+ if (!error) {
+ if (value) {
+
+ /** Check for MIN/MAX **/
+
+ if (value < SAPMIN_MAXEVERWORKERTHREADS)
+ value = SAPMIN_MAXEVERWORKERTHREADS;
+
+ if (value > SAPMAX_MAXEVERWORKERTHREADS)
+ value = SAPMAX_MAXEVERWORKERTHREADS;
+
+ /** Set the new value **/
+
+ SapMaxEverWorkerThreads = (INT)value;
+ }
+ }
+
+ IF_DEBUG(REGISTRY) {
+ SS_PRINT(("NWSAP: SapMaxEverWorkerThreads = %d\n", SapMaxEverWorkerThreads));
+ }
+
+ /** Get the delay after starting a worker thread until we can start another **/
+
+ error = SapGetRegDword(handle, KeyWorkerDelay, &value);
+ if (!error) {
+ if (value) {
+
+ /** Check for MIN/MAX (This is in millisecods) **/
+
+ if (value < SAPMIN_NEWWORKERTIMEOUT)
+ value = SAPMIN_NEWWORKERTIMEOUT;
+
+ if (value > SAPMAX_NEWWORKERTIMEOUT)
+ value = SAPMAX_NEWWORKERTIMEOUT;
+
+ /** Set the new value **/
+
+ SapNewWorkerTimeout = (INT)value;
+ }
+ }
+
+ IF_DEBUG(REGISTRY) {
+ SS_PRINT(("NWSAP: SapNewWorkerTimeout = %d\n", SapNewWorkerTimeout));
+ }
+
+ /** Read the hash table size **/
+
+ error = SapGetRegDword(handle, KeyNameHashSize, &value);
+ if (!error) {
+ if (value) {
+
+ /** Check for MIN/MAX (This is in millisecods) **/
+
+ if (value < SAPMIN_HASHTABLESIZE)
+ value = SAPMIN_HASHTABLESIZE;
+
+ if (value > SAPMAX_HASHTABLESIZE)
+ value = SAPMAX_HASHTABLESIZE;
+
+ /** Set the new value **/
+
+ SapHashTableSize = (INT)value;
+ }
+ }
+
+ IF_DEBUG(REGISTRY) {
+ SS_PRINT(("NWSAP: SapHashTableSize = %d\n", SapHashTableSize));
+ }
+
+ /** Read the flag for responding to internal servers **/
+
+ error = SapGetRegDword(handle, KeyRespondInternals, &value);
+ if (!error) {
+ if (value) {
+
+ /** Check for MIN/MAX (This is in millisecods) **/
+
+ if (value < SAPMIN_DOINTERNALS)
+ value = SAPMIN_DOINTERNALS;
+
+ if (value > SAPMAX_DOINTERNALS)
+ value = SAPMAX_DOINTERNALS;
+
+ /** Set the new value **/
+
+ SapRespondForInternal = (INT)value;
+ }
+ }
+
+ IF_DEBUG(REGISTRY) {
+ SS_PRINT(("NWSAP: SapRespondForInternal = %d\n", SapRespondForInternal));
+ }
+
+ /** Read the malloc error delay for the receive thread **/
+
+ error = SapGetRegDword(handle, KeyDelayOnNetError, &value);
+ if (!error) {
+
+ /** Check for MIN/MAX (This is in millisecods) **/
+
+ if (value < SAPMIN_DELAYONMALLOCFAIL)
+ value = SAPMIN_DELAYONMALLOCFAIL;
+
+ if (value > SAPMAX_DELAYONMALLOCFAIL)
+ value = SAPMAX_DELAYONMALLOCFAIL;
+
+ /** Set the new value **/
+
+ SapRecvDelayOnMallocFail = (INT)value;
+ }
+
+ IF_DEBUG(REGISTRY) {
+ SS_PRINT(("NWSAP: SapRecvDelayOnMallocFail = %d\n", SapRecvDelayOnMallocFail));
+ }
+
+ /** Read the net error delay **/
+
+ error = SapGetRegDword(handle, KeyDelayOnNetError, &value);
+ if (!error) {
+
+ /** Check for MIN/MAX (This is in millisecods) **/
+
+ if (value < SAPMIN_DELAYONNETERROR)
+ value = SAPMIN_DELAYONNETERROR;
+
+ if (value > SAPMAX_DELAYONNETERROR)
+ value = SAPMAX_DELAYONNETERROR;
+
+ /** Set the new value **/
+
+ SapRecvDelayOnNetError = (INT)value;
+ }
+
+ IF_DEBUG(REGISTRY) {
+ SS_PRINT(("NWSAP: SapRecvDelayOnNetError = %d\n", SapRecvDelayOnNetError));
+ }
+
+ /** Read the flag for routine LAN-LAN Saps **/
+
+ error = SapReadHopLans( &SapDontHopLans );
+
+ IF_DEBUG(REGISTRY) {
+ SS_PRINT(("NWSAP: SapDontHopLans = %d, error = 0x%x\n", SapDontHopLans, error));
+ }
+
+ /** Read the max number of LPC clients we support **/
+
+ error = SapGetRegDword(handle, KeyMaxLpcClients, &value);
+ if (!error) {
+ if (value) {
+
+ /** Check the MIN/MAX **/
+
+ if (value < SAPMIN_LPCCLIENTS)
+ value = SAPMIN_LPCCLIENTS;
+
+ if (value > SAPMAX_LPCCLIENTS)
+ value = SAPMAX_LPCCLIENTS;
+
+ /** Set the new value **/
+
+ SapLpcMaxWorkers = value;
+ }
+ }
+
+ IF_DEBUG(REGISTRY) {
+ SS_PRINT(("NWSAP: SapMaxLpcClients = %d\n", SapLpcMaxWorkers));
+ }
+
+ /** Read the WAN filter flag **/
+
+ error = SapGetRegDword(handle, KeyWanFilter, &value);
+ if (!error) {
+ if (value) {
+
+ /** If invalid value - report it **/
+
+ if ((value != SAPWAN_NOTHING) &&
+ (value != SAPWAN_CHANGESONLY) &&
+ (value != SAPWAN_REGULAR)) {
+
+ eventid = NWSAP_EVENT_BADWANFILTER_VALUE;
+ goto RegError;
+ }
+
+ /** Set the new value **/
+
+ SapWanFilter = value;
+ }
+ }
+
+ IF_DEBUG(REGISTRY) {
+ SS_PRINT(("NWSAP: SapWanFilter = %d\n", SapWanFilter));
+ }
+
+ /**
+ Read the number of WAN notify threads to have
+ **/
+
+ error = SapGetRegDword(handle, KeyWanNotifyThreads, &value);
+ if (!error) {
+ if (value) {
+
+ /** Check min/max **/
+
+ if (value < SAPMIN_WANCHECKTHREADS)
+ value = SAPMIN_WANCHECKTHREADS;
+
+ if (value > SAPMAX_WANCHECKTHREADS)
+ value = SAPMAX_WANCHECKTHREADS;
+
+ /** Set the new value **/
+
+ SapNumWanNotifyThreads = value;
+ }
+ }
+
+ IF_DEBUG(REGISTRY) {
+ SS_PRINT(("NWSAP: SapNumWanNotifyThreads = %d\n", SapNumWanNotifyThreads));
+ }
+
+ /**
+ Read the flag that tells us if we should allow an AddAdvertise
+ server to be the same name/type as a server already on the
+ network.
+ **/
+
+ error = SapGetRegDword(handle, KeyAllowDuplicateServers, &value);
+ if (!error) {
+
+ if (value)
+ SapAllowDuplicateServers = 1;
+ else
+ SapAllowDuplicateServers = 0;
+ }
+
+ IF_DEBUG(REGISTRY) {
+ SS_PRINT(("NWSAP: SapAllowDuplicateServers = %d\n", SapAllowDuplicateServers));
+ }
+
+ /** Read the update time for checking all cards **/
+
+ error = SapGetRegDword(handle, KeyWanUpdateTime, &value);
+ if (!error) {
+ if (value) {
+
+ /** Check min/max **/
+
+ if (value < SAPMIN_WANUPDATETIME)
+ value = SAPMIN_WANUPDATETIME;
+
+ if (value > SAPMAX_WANUPDATETIME)
+ value = SAPMAX_WANUPDATETIME;
+
+ /** Set the new value **/
+
+ }
+
+ /** 0 means dont update **/
+
+ SapRecheckAllCardsTime = value;
+ }
+
+ IF_DEBUG(REGISTRY) {
+ SS_PRINT(("NWSAP: SapRecheckAllCardsTime = %d\n", SapRecheckAllCardsTime));
+ }
+
+ /** Read the filter active flag **/
+
+ error = SapGetRegDword(handle, KeyFilterFlag, &value);
+ if (!error) {
+ if (value)
+ SapActiveFilter = SAPFILTER_DONTPASSLIST;
+ else
+ SapActiveFilter = SAPFILTER_PASSLIST;
+ }
+
+ IF_DEBUG(REGISTRY) {
+ SS_PRINT(("NWSAP: SapActiveFilter = %d\n", SapActiveFilter));
+ }
+
+ /**
+ If the filter is active - read the server names to pass.
+ If the filter is off - read the server names to NOT pass.
+ **/
+
+ if (SapActiveFilter == SAPFILTER_DONTPASSLIST)
+ SapReadFilterNames(handle, TRUE);
+ else
+ SapReadFilterNames(handle, FALSE);
+
+
+ /** Read the delay for responding to general requests **/
+
+ error = SapGetRegDword(handle, KeyDelayRespondToGeneral, &value);
+ if (!error) {
+ if (value) {
+
+ /** Check for MIN/MAX (This is in millisecods) **/
+
+ if (value < SAPMIN_DELAYRESPONDTOGENERAL)
+ value = SAPMIN_DELAYRESPONDTOGENERAL;
+
+ if (value > SAPMAX_DELAYRESPONDTOGENERAL)
+ value = SAPMAX_DELAYRESPONDTOGENERAL;
+
+ /** Set the new value **/
+
+ SapDelayRespondToGeneral = (INT)value;
+ }
+ }
+
+ IF_DEBUG(REGISTRY) {
+ SS_PRINT(("NWSAP: SapDelayRespondToGeneral = %d\n", SapDelayRespondToGeneral));
+ }
+
+ /** Close the key **/
+
+ RegCloseKey(handle);
+
+ /** Return OK **/
+
+ return 0;
+
+ /** **/
+
+RegError:
+
+ /** Close the registry handle **/
+
+ RegCloseKey(handle);
+
+ /** Log the error **/
+
+ SsLogEvent(
+ SapEventId,
+ 0,
+ NULL,
+ error);
+
+ /** Return error **/
+
+ return 1; /* Just reports an error - already logged */
+}
+
+
+/*++
+*******************************************************************
+ S a p G e t R e g D w o r d
+
+Routine Description:
+
+ Get a REG_DWORD value from the registry.
+
+Arguments:
+
+ hkey = Handle of registry to read value from
+ keyname = Name of key to read from
+ valuep = Ptr to where to store the number
+
+Return Value:
+
+ 0 = OK
+ Else = Error
+*******************************************************************
+--*/
+
+INT
+SapGetRegDword(
+ HKEY HKey,
+ LPTSTR KeyName,
+ PULONG ValuePtr)
+{
+ INT Error;
+ DWORD Type;
+ DWORD Length;
+
+ /** **/
+
+ IF_DEBUG(REGISTRY) {
+ SS_PRINT(("NWSAP: Reading registry value for %ws\n", KeyName));
+ }
+
+ /** Get the value **/
+
+ Length = sizeof(ULONG);
+ Error = RegQueryValueEx(HKey,
+ KeyName,
+ NULL,
+ &Type,
+ (LPBYTE)ValuePtr,
+ &Length);
+
+ /** If OK - check size - must be a REG_DWORD **/
+
+ if (Error == ERROR_SUCCESS) {
+
+ /** If bad type - set different error **/
+
+ if (Type != REG_DWORD) {
+ IF_DEBUG(REGISTRY) {
+ SS_PRINT(("NWSAP: SapGetRegDword: Bad type = %d\n", Type));
+ }
+ Error = ERROR_INVALID_DATA;
+ }
+ else if (Length != sizeof(ULONG)) {
+ IF_DEBUG(REGISTRY) {
+ SS_PRINT(("NWSAP: SapGetRegDword: Bad length = %d\n", Length));
+ }
+ Error = ERROR_INVALID_DATA;
+ }
+ }
+
+ /** **/
+
+ IF_DEBUG(REGISTRY) {
+ if (Error == 0) {
+ SS_PRINT(("NWSAP: Key %ws read: Value = %d\n", KeyName, *ValuePtr));
+ }
+ else {
+ SS_PRINT(("NWSAP: Key %ws error %d\n", KeyName, Error));
+ }
+ }
+
+ /** Return back to the caller **/
+
+ return Error;
+}
+
+
+/*++
+*******************************************************************
+ S a p R e a d F i l t e r N a m e s
+
+Routine Description:
+
+ Read the filter names from the registry
+
+Arguments:
+
+ Handle = SAP Registry parameters handle to read from
+ Flag = TRUE = Read the DONT PASS server names
+ FALSE = Read the PASS server names
+
+Return Value:
+
+ None.
+
+*******************************************************************
+--*/
+
+VOID
+SapReadFilterNames(
+ HANDLE Handle,
+ BOOL Flag)
+{
+ NTSTATUS Status;
+ RTL_QUERY_REGISTRY_TABLE QueryTable[2];
+ PWSTR KeynamePass = L"PassServers";
+ PWSTR KeynameDontPass = L"DontPassServers";
+
+ /** Setup the QUERY table **/
+
+ IF_DEBUG(REGISTRY) {
+ SS_PRINT(("NWSAP: ReadFilterName: Handle = 0x%lx, Flag = %d\n", Handle, Flag));
+ }
+
+ /** Count how many entries in the PassServers string **/
+
+ QueryTable[0].QueryRoutine = SapAddFilterEntries;
+ QueryTable[0].Flags = RTL_QUERY_REGISTRY_NOEXPAND;
+ QueryTable[0].DefaultType = REG_NONE;
+
+ if (!Flag) {
+ QueryTable[0].Name = KeynamePass;
+ QueryTable[0].EntryContext = (PVOID)TRUE; /* Pass list */
+ }
+ else {
+ QueryTable[0].Name = KeynameDontPass;
+ QueryTable[0].EntryContext = (PVOID)FALSE; /* No pass list */
+ }
+
+ /** Set ending entry **/
+
+ QueryTable[1].QueryRoutine = NULL;
+ QueryTable[1].Flags = 0;
+ QueryTable[1].Name = NULL;
+
+ /** Go read the entries **/
+
+ Status = RtlQueryRegistryValues(
+ RTL_REGISTRY_SERVICES,
+ L"NwSapAgent\\Parameters",
+ QueryTable,
+ (PVOID)NULL, /* Context to pass */
+ NULL);
+
+ /** **/
+
+ IF_DEBUG(REGISTRY) {
+ SS_PRINT(("NWSAP: ReadFilterName: RtlQueryRegisterValue status = 0x%lx\n", Status));
+ }
+
+ /** All Done **/
+
+ return;
+}
+
+/*page**************************************************************
+ S a p A d d F i l t e r E n t r i e s
+
+ This is a callback routine to get the list of
+ filter names.
+
+ Arguments - ValueName = The name of the key
+ ValueType = The type of value
+ ValueData = Ptr to the value data
+ ValueLen = Length of the value
+ Context = Context from query call
+ Context2 = Context from the query structure
+
+ Returns - Always STATUS_SUCCESS
+*******************************************************************/
+NTSTATUS
+SapAddFilterEntries(
+ PWSTR ValueName,
+ ULONG ValueType,
+ PVOID ValueData,
+ ULONG ValueLen,
+ PVOID Context,
+ PVOID Context2)
+{
+ BOOL Flag;
+ PWCHAR Ptr;
+ PWCHAR NamePtr;
+ INT Length;
+ INT Error;
+ UNICODE_STRING UniStr;
+ OEM_STRING OemStr;
+ UCHAR Buffer[SAP_OBJNAME_LEN];
+ LPWSTR SubStrings[2];
+
+ /** Get the flag to pass to Add List **/
+
+ Flag = (Context2) ? TRUE : FALSE;
+
+ /** Go thru each and add the entry **/
+
+ Ptr = ValueData;
+ while (*Ptr != (WCHAR)0) {
+
+ /** Save Ptr to this name **/
+
+ NamePtr = Ptr;
+
+ /** Skip to the next name (Counting Length as we go) **/
+
+ Length = 0;
+ while (*Ptr != (WCHAR)0) {
+ Length++;
+ Ptr++;
+ }
+
+ /** Count the 0 and skip past it **/
+
+ Length++;
+ Ptr++;
+
+ /**
+ If the length is too long - skip this name.
+ We include the 0 in the length, because
+ Novell names are 47 bytes + 1 byte of ending 0.
+ **/
+
+ if (Length > SAP_OBJNAME_LEN) {
+
+ /** Log the bad name as an event **/
+
+ SubStrings[0] = ValueName;
+ SubStrings[1] = NamePtr;
+
+ SsLogEvent(
+ NWSAP_EVENT_INVALID_FILTERNAME,
+ 2, /* Num SubString */
+ SubStrings,
+ 0);
+
+ /** And keep going **/
+
+ continue;
+ }
+
+ /** Convert the string to OEM **/
+
+ RtlInitUnicodeString(&UniStr, NamePtr);
+ OemStr.Length = SAP_OBJNAME_LEN - 1;
+ OemStr.MaximumLength = SAP_OBJNAME_LEN;
+ OemStr.Buffer = Buffer;
+
+ RtlUnicodeStringToOemString(
+ &OemStr,
+ &UniStr,
+ FALSE);
+
+ /** Add this string to the filter list **/
+
+ Error = SapAddFilter(Buffer, Flag);
+
+ /** Goto the next name **/
+ }
+
+ /** All done - return OK **/
+
+ return STATUS_SUCCESS;
+}
+
+/** **/
+
+TCHAR KeyHopLans[] = TEXT("EnableLanRouting");
+TCHAR RouterRegParmKey[] = TEXT("SYSTEM\\CurrentControlSet\\Services\\NwLnkRip\\Parameters");
+
+/*page**************************************************************
+ S a p R e a d H o p L a n s
+
+ Read the EnableLanRouting parameter from the IPX router
+ registry key to see if we should hop lan to lan traffic or not.
+
+ If the router is not routing between LANS - then we should
+ not hop the traffic.
+
+ Arguments - HopLans = Ptr to store result in
+ (Store 0 for no)
+ (Store 1 for yes)
+
+ Returns - Always returns 0
+********************************************************************/
+NTSTATUS
+SapReadHopLans(
+ INT *HopLans)
+{
+ HKEY handle;
+ INT error;
+ ULONG value;
+
+ /** Open the registry key for the server (PARAMETERS) **/
+
+ error = RegOpenKey(HKEY_LOCAL_MACHINE, RouterRegParmKey, &handle);
+
+ if (! error) {
+
+ error = SapGetRegDword(handle, KeyHopLans, &value);
+
+ if (!error) {
+
+ // This is a boolean valu of 0 or 1. 1 means we do lan to
+ // lan routing. 0 or not present means don't send lan-lan saps.
+
+ if (value < SAPMIN_DONTHOPLANS)
+ value = SAPMIN_DONTHOPLANS;
+
+ if (value > SAPMAX_DONTHOPLANS)
+ value = SAPMAX_DONTHOPLANS;
+
+ if ( value ) {
+
+ *HopLans = SAPMIN_DONTHOPLANS; /* 0 = Do hop lan-lan */
+
+ } else {
+
+ *HopLans = SAPMAX_DONTHOPLANS; /* 1 = Don't hop lan-lan */
+
+ }
+ }
+
+ RegCloseKey(handle);
+ }
+
+ return(0);
+}
diff --git a/private/net/svcdlls/nwsap/server/sapdebug.c b/private/net/svcdlls/nwsap/server/sapdebug.c
new file mode 100644
index 000000000..7365ea1ff
--- /dev/null
+++ b/private/net/svcdlls/nwsap/server/sapdebug.c
@@ -0,0 +1,518 @@
+/*++
+
+Copyright (c) 1994 Microsoft Corporation
+Copyright (c) 1993 Micro Computer Systems, Inc.
+
+Module Name:
+
+ net\svcdlls\nwsap\server\sapdebug.c
+
+Abstract:
+
+ This has some debug code in it.
+
+Author:
+
+ Brian Walker (MCS) 06-15-1993
+
+Revision History:
+
+--*/
+
+#include "precomp.h"
+#pragma hdrstop
+
+#if DBG
+
+/** File we output debug information to **/
+
+WCHAR SapDbgFile[] = L"c:\\tmp\\sap.dbg";
+UCHAR buffer[2048];
+
+/** **/
+
+#define WRITEIT(p) WriteFile(fd,p,strlen(p),&numwrote,NULL)
+
+
+/*++
+*******************************************************************
+ S a p D e b u g H a n d l e r
+
+Routine Description:
+
+Arguments:
+
+ Nothing
+
+Return Value:
+
+ Exit Code
+
+*******************************************************************
+--*/
+
+VOID
+SapDebugHandler(
+ VOID)
+{
+ HANDLE fd;
+ PSAP_RECORD Entry;
+ PSAP_CARD Cardptr;
+ PSAP_SERVER Servp;
+ DWORD numwrote;
+ PSDMD_LIST_ENTRY ListHead;
+ INT i;
+ PSAP_NAMEFILTER Filterp;
+ PSAP_FILTERHDR Hdrp;
+ INT Cnt;
+
+ /** **/
+
+ IF_DEBUG(ENABLEDUMP) {
+ SS_PRINT(("SAPDebugHandler ENTERED\n"));
+ }
+
+ /** Create the file **/
+
+ fd = CreateFile(
+ SapDbgFile,
+ GENERIC_READ | GENERIC_WRITE,
+ 0,
+ NULL,
+ CREATE_ALWAYS,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL);
+
+ if (fd == INVALID_HANDLE_VALUE) {
+ IF_DEBUG(ERRORS) {
+ SS_PRINT(("SAPDEBUG: Error creating file\n"));
+ }
+ return;
+ }
+
+ /** Lock the database **/
+
+ ACQUIRE_READERS_LOCK("SapDebug");
+
+ /** Write out info we want **/
+
+ sprintf(buffer, "Num entries in database = %d\n", SapNumArrayEntries);
+ WRITEIT(buffer);
+ sprintf(buffer, "Current time is = %d\n", SdmdCurrentTime);
+ WRITEIT(buffer);
+ sprintf(buffer, "READ LOCK COUNT = %d\n", SdmdLockCount);
+ WRITEIT(buffer);
+ sprintf(buffer, "Last Worker Start Time = %d: Cur Time = %d\n", SapLastWorkerStartTime, GetCurrentTime());
+ WRITEIT(buffer);
+ sprintf(buffer, "Hash Table ptr = 0x%lx, Num Hash entries = %d\n", SdmdNameHashTable, SapHashTableSize);
+ WRITEIT(buffer);
+ sprintf(buffer, "SapRecheckCount = %d\n", SapRecheckCount);
+ WRITEIT(buffer);
+ sprintf(buffer, "SapDontHopLans = %d\n", SapDontHopLans);
+ WRITEIT(buffer);
+
+ ACQUIRE_THREADCOUNT_LOCK();
+ sprintf(buffer, "Thread Count = %d, Worker = %d, Recv = %d\n",
+ SapThreadCount,
+ SapCurWorkerThreads,
+ SapCurReceiveThreads);
+ RELEASE_THREADCOUNT_LOCK();
+ WRITEIT(buffer);
+
+ sprintf(buffer, "Number of bufs in free queue = %d\n", SapNumFreeBufs);
+ WRITEIT(buffer);
+ sprintf(buffer, "Number of bufs in recv list = %d\n", SapCurBackup);
+ WRITEIT(buffer);
+ sprintf(buffer, "Current Allocation Count = %d\n", SapAllocCount);
+ WRITEIT(buffer);
+
+ /**
+ Do all the LPC information
+ **/
+
+ sprintf(buffer, "\n=============== LPC ==================\n\n");
+ WRITEIT(buffer);
+
+ sprintf(buffer, "Listen Port Handle = 0x%lx\n", SapXsLpcPortHandle);
+ WRITEIT(buffer);
+
+ ACQUIRE_LPC_THREADCOUNT_LOCK();
+ sprintf(buffer, "Total Workers Threads = %d\n", SapLpcNumWorkers);
+ RELEASE_LPC_THREADCOUNT_LOCK();
+ WRITEIT(buffer);
+
+ ACQUIRE_LPCCLIENT_LOCK();
+ sprintf(buffer, "Number Lpc Clients = %d\n", SapNumLpcClients);
+ RELEASE_LPCCLIENT_LOCK();
+ WRITEIT(buffer);
+
+ /**
+ Do the advertise list
+ **/
+
+ sprintf(buffer, "\n=========== ADVERTISE LIST ===============\n\n");
+ WRITEIT(buffer);
+
+ /** Get access to the send list **/
+
+ ACQUIRE_SENDTABLE_LOCK();
+
+ /** Go find the server type in the list **/
+
+ Servp = SapServHead;
+ i = 0;
+ while (Servp) {
+
+ sprintf(buffer, "%3d: Name = %s\n", i++, Servp->ServerName);
+ WRITEIT(buffer);
+
+ sprintf(buffer, " Object Type = 0x%x: HopCount = %d: Changed = %d\n",
+ Servp->ServerType, Servp->Hopcount, Servp->Changed);
+ WRITEIT(buffer);
+
+ sprintf(buffer, " RespondNearest = %d\n", Servp->RespondNearest);
+ WRITEIT(buffer);
+
+ sprintf(buffer, " ClientId = 0x%lx\n", Servp->ClientId);
+ WRITEIT(buffer);
+
+ sprintf(buffer, " Address = %02x:%02x:%02x:%02x - %02x:%02x:%02x:%02x:%02x:%02x - %02x:%02x\n\n",
+ (UCHAR)Servp->Address[0],
+ (UCHAR)Servp->Address[1],
+ (UCHAR)Servp->Address[2],
+ (UCHAR)Servp->Address[3],
+ (UCHAR)Servp->Address[4],
+ (UCHAR)Servp->Address[5],
+ (UCHAR)Servp->Address[6],
+ (UCHAR)Servp->Address[7],
+ (UCHAR)Servp->Address[8],
+ (UCHAR)Servp->Address[9],
+ (UCHAR)Servp->Address[10],
+ (UCHAR)Servp->Address[11]);
+ WRITEIT(buffer);
+
+ /** Goto the next entry **/
+
+ Servp = Servp->Next;
+ }
+
+ RELEASE_SENDTABLE_LOCK();
+
+ /**
+ Do the network
+ **/
+
+ sprintf(buffer, "\n=============== NETWORK ==================\n\n");
+ WRITEIT(buffer);
+
+ sprintf(buffer, "Internal Address = %02x:%02x:%02x:%02x - %02x:%02x:%02x:%02x:%02x:%02x\n",
+ (UCHAR)SapNetNum[0],
+ (UCHAR)SapNetNum[1],
+ (UCHAR)SapNetNum[2],
+ (UCHAR)SapNetNum[3],
+ (UCHAR)SapNodeNum[0],
+ (UCHAR)SapNodeNum[1],
+ (UCHAR)SapNodeNum[2],
+ (UCHAR)SapNodeNum[3],
+ (UCHAR)SapNodeNum[4],
+ (UCHAR)SapNodeNum[5]);
+ WRITEIT(buffer);
+ sprintf(buffer, "\nNumber of cards = %d\n", SapNumCards);
+ WRITEIT(buffer);
+
+ sprintf(buffer, "\nMax Number of cards = %d\n", SapMaxCardIndex);
+ WRITEIT(buffer);
+
+ ACQUIRE_CARDLIST_READERS_LOCK("SapDebug X");
+ Cardptr = SapCardHead;
+ while (Cardptr) {
+ sprintf(buffer, "Cardnum %d: Address = %02x:%02x:%02x:%02x - %02x:%02x:%02x:%02x:%02x:%02x\n",
+ Cardptr->Number,
+ (UCHAR)Cardptr->Netnum[0],
+ (UCHAR)Cardptr->Netnum[1],
+ (UCHAR)Cardptr->Netnum[2],
+ (UCHAR)Cardptr->Netnum[3],
+ (UCHAR)Cardptr->Nodenum[0],
+ (UCHAR)Cardptr->Nodenum[1],
+ (UCHAR)Cardptr->Nodenum[2],
+ (UCHAR)Cardptr->Nodenum[3],
+ (UCHAR)Cardptr->Nodenum[4],
+ (UCHAR)Cardptr->Nodenum[5]);
+ WRITEIT(buffer);
+
+ sprintf(buffer, " LinkSpeed = %d, Wanflag = %d, ReqCount = %d\n",
+ Cardptr->Linkspeed, Cardptr->Wanflag, Cardptr->ReqCount);
+ WRITEIT(buffer);
+
+ Cardptr = Cardptr->Next;
+ }
+ RELEASE_CARDLIST_READERS_LOCK("SapDebug X");
+
+ /**
+ Dump out registry information
+ **/
+
+ sprintf(buffer, "\n========== REG PARMS ============\n\n");
+ WRITEIT(buffer);
+ sprintf(buffer, "SapMaxFreeBufs = %d\n", SapMaxFreeBufs);
+ WRITEIT(buffer);
+ sprintf(buffer, "SapNumRecvThreads = %d\n", SapNumRecvThreads);
+ WRITEIT(buffer);
+ sprintf(buffer, "SapNumWorkerThreads = %d\n", SapNumWorkerThreads);
+ WRITEIT(buffer);
+ sprintf(buffer, "SapSendMinutes = %d\n", SapSendMinutes);
+ WRITEIT(buffer);
+ sprintf(buffer, "SapNumArrayEntries = %d\n", SapNumArrayEntries);
+ WRITEIT(buffer);
+ sprintf(buffer, "SapTimeoutInterval = %d\n", SapTimeoutInterval);
+ WRITEIT(buffer);
+ sprintf(buffer, "SapMaxEverWorkerThreads = %d\n", SapMaxEverWorkerThreads);
+ WRITEIT(buffer);
+ sprintf(buffer, "SapNewWorkerThreshhold = %d\n", SapNewWorkerThreshhold);
+ WRITEIT(buffer);
+ sprintf(buffer, "SapNewWorkerTimeout = %d\n", SapNewWorkerTimeout);
+ WRITEIT(buffer);
+ sprintf(buffer, "SapNumWanNotifyThreads = %d\n", SapNumWanNotifyThreads);
+ WRITEIT(buffer);
+ sprintf(buffer, "SapRecheckAllCardsTime = %d\n", SapRecheckAllCardsTime);
+ WRITEIT(buffer);
+
+ /** Database dumping **/
+
+ sprintf(buffer, "\n=============== DATABASE ==================\n\n");
+ WRITEIT(buffer);
+ sprintf(buffer, "TYPE LIST Head = %d, Tail = %d\n",
+ SdmdLists[SAP_TYPELIST_INDEX].Flink,
+ SdmdLists[SAP_TYPELIST_INDEX].Blink);
+ WRITEIT(buffer);
+ sprintf(buffer, "TIME LIST Head = %d, Tail = %d\n",
+ SdmdLists[SAP_TIMELIST_INDEX].Flink,
+ SdmdLists[SAP_TIMELIST_INDEX].Blink);
+ WRITEIT(buffer);
+
+ /** **/
+
+ Entry = SdmdTablePtr;
+ for (i = 0 ; i < SapNumArrayEntries ; i++,Entry++) {
+
+ sprintf(buffer, "\nINDEX = %d **********************\n", Entry->Index);
+ WRITEIT(buffer);
+
+ SapDumpMemToMemory(Entry->ServName, SAP_OBJNAME_LEN, buffer);
+ WRITEIT(buffer);
+
+ sprintf(buffer, " Server Type = 0x%x: HopCount = %d\n", Entry->ServType, Entry->HopCount);
+ WRITEIT(buffer);
+ sprintf(buffer, " Card Number = %d: Timeout = %d\n", Entry->CardNumber, Entry->Timeout);
+ WRITEIT(buffer);
+ sprintf(buffer, " Address = %02x:%02x:%02x:%02x - %02x:%02x:%02x:%02x:%02x:%02x - %02x:%02x\n",
+ (UCHAR)(Entry->ServAddress[0]),
+ (UCHAR)(Entry->ServAddress[1]),
+ (UCHAR)(Entry->ServAddress[2]),
+ (UCHAR)(Entry->ServAddress[3]),
+ (UCHAR)(Entry->ServAddress[4]),
+ (UCHAR)(Entry->ServAddress[5]),
+ (UCHAR)(Entry->ServAddress[6]),
+ (UCHAR)(Entry->ServAddress[7]),
+ (UCHAR)(Entry->ServAddress[8]),
+ (UCHAR)(Entry->ServAddress[9]),
+ (UCHAR)(Entry->ServAddress[10]),
+ (UCHAR)(Entry->ServAddress[11]));
+ WRITEIT(buffer);
+
+ sprintf(buffer, " Advertiser = %02x:%02x:%02x:%02x:%02x:%02x\n",
+ (UCHAR)(Entry->Advertiser[0]),
+ (UCHAR)(Entry->Advertiser[1]),
+ (UCHAR)(Entry->Advertiser[2]),
+ (UCHAR)(Entry->Advertiser[3]),
+ (UCHAR)(Entry->Advertiser[4]),
+ (UCHAR)(Entry->Advertiser[5]));
+ WRITEIT(buffer);
+
+ sprintf(buffer, " Head Index = %d, Hash Index = %d\n", Entry->HeadIndex, Entry->HashIndex);
+ WRITEIT(buffer);
+
+ sprintf(buffer, " TYPE FLink = %d, Blink = %d\n",
+ Entry->Links[SAP_TYPELIST_INDEX].Flink,
+ Entry->Links[SAP_TYPELIST_INDEX].Blink);
+ WRITEIT(buffer);
+
+ sprintf(buffer, " TIME FLink = %d, Blink = %d\n",
+ Entry->Links[SAP_TIMELIST_INDEX].Flink,
+ Entry->Links[SAP_TIMELIST_INDEX].Blink);
+ WRITEIT(buffer);
+
+ sprintf(buffer, " SUB FLink = %d, Blink = %d\n",
+ Entry->Links[SAP_SUBLIST_INDEX].Flink,
+ Entry->Links[SAP_SUBLIST_INDEX].Blink);
+ WRITEIT(buffer);
+
+ sprintf(buffer, " HASH FLink = %d, Blink = %d\n",
+ Entry->Links[SAP_HASHLIST_INDEX].Flink,
+ Entry->Links[SAP_HASHLIST_INDEX].Blink);
+ WRITEIT(buffer);
+
+ sprintf(buffer, " Internal = %d: Changed = %d\n", Entry->Internal, Entry->Changed);
+ WRITEIT(buffer);
+ }
+
+ /** Dump out the hash entries **/
+
+ sprintf(buffer, "\n============== HASH TABLE =================\n\n");
+ WRITEIT(buffer);
+
+ ListHead = SdmdNameHashTable;
+ for (i = 0 ; i < SapHashTableSize ; i++,ListHead++) {
+ sprintf(buffer, "HashIndex = %d: Flink = %d: Blink = %d\n",
+ ListHead->ListIndex, ListHead->Flink, ListHead->Blink);
+ WRITEIT(buffer);
+ }
+ RELEASE_READERS_LOCK("SapDebug X");
+
+ /** Dump out the filter table **/
+
+ sprintf(buffer, "\n=============== FILTER TABLE ==================\n\n");
+ WRITEIT(buffer);
+
+ sprintf(buffer, "WAN Filter = %d\n", SapWanFilter);
+ WRITEIT(buffer);
+ sprintf(buffer, "Active Filter = %d\n", SapActiveFilter);
+ WRITEIT(buffer);
+
+ sprintf(buffer, "\n------------------- PASS TABLE------------------\n\n");
+ WRITEIT(buffer);
+
+ for (Cnt = 0 ; Cnt < SAP_NAMEFILTER_HASHSIZE ; Cnt++) {
+
+ /** **/
+
+ sprintf(buffer, "HashIndex = %d\n", Cnt);
+ WRITEIT(buffer);
+
+ /** Get ptr to this header **/
+
+ Hdrp = &SapNameFilterHashTable[Cnt];
+ Filterp = Hdrp->FirstEntry;
+
+ /** Free all of these entries **/
+
+ while (Filterp) {
+ sprintf(buffer, " %s\n", Filterp->ServerName);
+ WRITEIT(buffer);
+ Filterp = Filterp->Next;
+ }
+ }
+
+ /** All Done **/
+
+ CloseHandle(fd);
+ return;
+}
+
+
+/*++
+*******************************************************************
+ S a p D e b u g M a l l o c
+
+Routine Description:
+
+ This is the debug version of malloc
+
+Arguments:
+
+ length = Number of bytes to malloc
+ string = Description string
+
+Return Value:
+
+ None.
+
+*******************************************************************
+--*/
+
+PVOID
+SapDebugMalloc(
+ ULONG length,
+ PUCHAR string)
+{
+ PVOID ptr;
+
+ /** Allocate the memory **/
+
+ ptr = malloc(length);
+
+ /** Do tracing **/
+
+ IF_DEBUG(MEMALLOC) {
+ SS_PRINT(("SAPALLOC: Ptr = 0x%lx: Length = %d: %s\n", ptr, length, string));
+ }
+
+ /** Count the allocation **/
+
+ if (ptr) {
+ EnterCriticalSection(&SapMemoryCriticalSection);
+ SapAllocCount++;
+ LeaveCriticalSection(&SapMemoryCriticalSection);
+ }
+
+ /** Return the pointer **/
+
+ return ptr;
+}
+
+
+
+/*++
+*******************************************************************
+ S a p D e b u g F r e e
+
+Routine Description:
+
+ This routine is the debug version of Free
+
+Arguments:
+
+ ptr = Ptr to free
+ string = Ptr to description string
+
+Return Value:
+
+ None.
+
+*******************************************************************
+--*/
+
+VOID
+SapDebugFree(
+ PVOID ptr,
+ PUCHAR string)
+{
+ /** **/
+
+ IF_DEBUG(MEMALLOC) {
+ SS_PRINT(("SAPFREE: Ptr = 0x%lx: %s\n", ptr, string));
+ }
+
+ /** Count the allocation **/
+
+ EnterCriticalSection(&SapMemoryCriticalSection);
+ if (SapAllocCount == 0) {
+ IF_DEBUG(ERRORS) {
+ SS_PRINT(("SAPFREE: Alloc count going to 0\n"));
+ }
+ }
+ SapAllocCount--;
+ LeaveCriticalSection(&SapMemoryCriticalSection);
+
+ /** Free the pointer **/
+
+ free(ptr);
+
+ /** All Done **/
+
+ return;
+}
+
+#endif // if DBG
+
+
diff --git a/private/net/svcdlls/nwsap/server/saplpc.c b/private/net/svcdlls/nwsap/server/saplpc.c
new file mode 100644
index 000000000..94dc585bf
--- /dev/null
+++ b/private/net/svcdlls/nwsap/server/saplpc.c
@@ -0,0 +1,768 @@
+/*++
+
+Copyright (c) 1994 Microsoft Corporation
+Copyright (c) 1993 Micro Computer Systems, Inc.
+
+Module Name:
+
+ net\svcdlls\nwsap\server\saplpc.c
+
+Abstract:
+
+ This routine handles the LPC interface that other programs use
+ to send requests to me for information.
+
+Author:
+
+ Brian Walker (MCS) 06-15-1993
+
+Revision History:
+
+--*/
+
+#include "precomp.h"
+#pragma hdrstop
+
+/**
+ Structure used to keep track of each LPC client
+ that attaches to our port.
+**/
+
+typedef struct {
+ HANDLE Handle; /* Port handle for the client */
+ PVOID Context; /* Context for the client */
+ LIST_ENTRY ListEntry; /* Used to link entries */
+} SAP_LPC_CLIENT, *PSAP_LPC_CLIENT;
+
+/** **/
+
+PHANDLE SapLpcWorkerHandles;
+
+/** Internal Function Prototypes **/
+
+DWORD WINAPI
+SapXsWorkerThread(
+ LPVOID Threadparm);
+
+VOID
+SapXsHandleConnect(
+ PPORT_MESSAGE Request);
+
+
+/*++
+*******************************************************************
+ S a p X s I n i t i a l i z e
+
+Routine Description:
+
+ This routine initializes the LPC interface used by NWSAP
+ clients to send requests to be handled.
+
+Arguments:
+
+ None
+
+Return Value:
+
+ 0 = OK
+ Else = Error code on return
+ The error is logged to the event log before returning
+*******************************************************************
+--*/
+
+DWORD
+SapXsInitialize(
+ VOID)
+{
+ NTSTATUS Status;
+ HANDLE Handle;
+ ULONG i;
+ DWORD Threadid;
+ UNICODE_STRING UnicodeName;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+
+ /** Get a buffer to hold worker thread handles **/
+
+ SapLpcWorkerHandles = SAP_MALLOC(SapLpcMaxWorkers * sizeof(HANDLE), "SapXsInit: Alloc worker handle buffer");
+ if (SapLpcWorkerHandles == NULL) {
+ SapError = 0;
+ SapEventId = NWSAP_EVENT_LPCHANDLEMEMORY_ERROR;
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+ memset(SapLpcWorkerHandles, 0, SapLpcMaxWorkers * sizeof(HANDLE));
+
+ /** Create a event that will be set by the last thread to exit. **/
+
+ IF_DEBUG(LPC) {
+ SS_PRINT(("NWSAP: XsInit: Creating XS Thread Event\n"));
+ }
+
+ Status = NtCreateEvent(
+ &SapLpcThreadEvent,
+ EVENT_ALL_ACCESS,
+ NULL,
+ NotificationEvent,
+ FALSE);
+
+ if (!NT_SUCCESS(Status)) {
+ IF_DEBUG(INITIALIZATION_ERRORS) {
+ SS_PRINT(("NWSAP: XsInit: NtCreateEvent failed: %X\n", Status));
+ }
+
+ /** Setup the error so it is reported **/
+
+ SapError = Status;
+ SapEventId = NWSAP_EVENT_CREATELPCEVENT_ERROR;
+
+ /** Return error **/
+
+ SapLpcThreadEvent = NULL;
+ return Status;
+ }
+
+ /**
+ Create the LPC Port here. This is the port that everyone else
+ uses to send requests to us.
+ **/
+
+ RtlInitUnicodeString(&UnicodeName, NWSAP_BIND_PORT_NAME_W);
+
+ InitializeObjectAttributes(
+ &ObjectAttributes,
+ &UnicodeName,
+ 0,
+ NULL,
+ NULL);
+
+ IF_DEBUG(LPC) {
+ SS_PRINT(("NWSAP: XsInit: Creating XS Port\n"));
+ }
+
+ Status = NtCreatePort(
+ &SapXsLpcPortHandle,
+ &ObjectAttributes,
+ 0,
+ NWSAP_BS_PORT_MAX_MESSAGE_LENGTH,
+ NWSAP_BS_PORT_MAX_MESSAGE_LENGTH * 32);
+
+ /** If create port fails - return error **/
+
+ if (!NT_SUCCESS(Status)) {
+
+ IF_DEBUG(INITIALIZATION_ERRORS) {
+ if (Status == STATUS_OBJECT_NAME_COLLISION) {
+ SS_PRINT(("NWSAP: XsInit: The LPC port already exists\n"));
+ }
+ else {
+ SS_PRINT(("NWSAP: XsInit: Failed to create port: %X\n", Status));
+ }
+ }
+ SapXsLpcPortHandle = NULL;
+
+ /** Setup to report the error **/
+
+ SapError = Status;
+ SapEventId = NWSAP_EVENT_CREATELPCPORT_ERROR;
+
+ /** Return error **/
+
+ return Status;
+ }
+
+ /**
+ Start the thread that listens for connections to
+ our port. If does not matter if someone did a
+ NtConnectPort before our NtListenPort, they will
+ just block.
+ **/
+
+ IF_DEBUG(LPC) {
+ SS_PRINT(("NWSAP: Creating XS Worker Threads\n"));
+ }
+ for (i = 0 ; i < SapLpcMaxWorkers ; i++) {
+
+ /** Count this new thread **/
+
+ SAP_INC_LPC_THREAD_COUNT("SapStartLpcWorkers");
+
+ /** Start the new thread **/
+
+ Handle = CreateThread(
+ NULL, /* Security Ptr */
+ 0, /* Initial stack size */
+ SapXsWorkerThread, /* Thread Function */
+ (LPVOID)NULL, /* Parm for the thread */
+ 0, /* Creation Flags */
+ &Threadid); /* Ptr to thread id */
+
+ /** If create fails - report error and return it **/
+
+ if (Handle == NULL) {
+
+ /** Set the error codes for return **/
+
+ SapError = GetLastError();
+ SapEventId = NWSAP_EVENT_STARTLPCWORKER_ERROR;
+
+ /** Uncount the thread **/
+
+ SAP_DEC_LPC_THREAD_COUNT("SapStartWorker Error");
+
+ IF_DEBUG(INITIALIZATION_ERRORS) {
+ SS_PRINT(("NWSAP: Error starting LPC Worker thread = %d\n", SapError));
+ }
+
+ /** Return Error **/
+
+ return SapError;
+ }
+
+ /** Save the handle for this thread **/
+
+ SapLpcWorkerHandles[i] = Handle;
+ }
+
+ /** Return OK **/
+
+ IF_DEBUG(LPC) {
+ SS_PRINT(("NWSAP: XsInit returning OK\n"));
+ }
+ return 0;
+}
+
+
+/*++
+*******************************************************************
+ S a p X s S h u t d o w n
+
+Routine Description:
+
+ This routine shuts down the LPC interface.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+*******************************************************************
+--*/
+
+VOID
+SapXsShutdown(
+ VOID)
+{
+ PSAP_LPC_CLIENT Clientp;
+ PLIST_ENTRY Listp;
+ PLIST_ENTRY NListp;
+ HANDLE Handle;
+ ULONG i;
+ BOOL Stopped;
+
+ /** **/
+
+ IF_DEBUG(LPC) {
+ SS_PRINT(("NWSAP: XsShutdown Entered\n"));
+ }
+
+ /** Nuke all the worker threads **/
+
+ if (SapLpcWorkerHandles) {
+ for (i = 0 ; i < SapLpcMaxWorkers ; i++) {
+ if (SapLpcWorkerHandles[i]) {
+
+ IF_DEBUG(LPC) {
+ SS_PRINT(("NWSAP: XsShutdown: Stopping thread %d\n",
+ SapLpcWorkerHandles[i]));
+ }
+
+ /** Stop the thread **/
+
+ Stopped = TerminateThread(SapLpcWorkerHandles[i], 0);
+
+ IF_DEBUG(LPC) {
+ SS_PRINT(("NWSAP: XsShutdown: Stop of thread %d = %d\n",
+ SapLpcWorkerHandles[i], Stopped));
+ }
+ }
+ }
+ }
+
+ IF_DEBUG(LPC) {
+ SS_PRINT(("NWSAP: XsShutdown: Closing LPC Port Handle 0x%lx\n",
+ SapXsLpcPortHandle));
+ }
+
+ /** Close the handle to all threads die **/
+
+ if (SapXsLpcPortHandle)
+ NtClose(SapXsLpcPortHandle);
+
+ IF_DEBUG(LPC) {
+ SS_PRINT(("NWSAP: XsShutdown: Cleaning up all clients\n"));
+ }
+
+ /** Get rid of all the clients **/
+
+ ACQUIRE_LPCCLIENT_LOCK();
+ Listp = SapLpcClientList.Flink;
+ InitializeListHead(&SapLpcClientList);
+ while (Listp != &SapLpcClientList) {
+
+ /** Save ptr to next entry **/
+
+ NListp = Listp->Flink;
+
+ /** Get ptr to the client and get his handle **/
+
+ Clientp = CONTAINING_RECORD(Listp, SAP_LPC_CLIENT, ListEntry);
+ Handle = Clientp->Handle;
+ Clientp->Handle = NULL;
+
+ /** Close the handle **/
+
+ if (Handle)
+ NtClose(Handle);
+
+ /** Free the entry **/
+
+ SAP_FREE(Clientp, "SapShutdown free Clientp structure\n");
+
+ /** Goto the next entry **/
+
+ Listp = NListp;
+ }
+ RELEASE_LPCCLIENT_LOCK();
+
+ /** Close the event handle **/
+
+ if (SapLpcThreadEvent != NULL)
+ CloseHandle(SapLpcThreadEvent);
+
+ /** **/
+
+ if (SapLpcWorkerHandles) {
+ SAP_FREE(SapLpcWorkerHandles, "SapXsShutdown: Worker Handles");
+ }
+
+ /** All Done **/
+
+ IF_DEBUG(LPC) {
+ SS_PRINT(("NWSAP: XsShutdown: DONE\n"));
+ }
+ return;
+}
+
+
+/*++
+*******************************************************************
+ S a p X s W o r k e r T h r e a d
+
+Routine Description:
+
+ This is the LPC worker thread used to handle requests
+ on our LPC port.
+
+Arguments:
+
+ Threadparm = Parm given in CreateThread
+ (We don't use it)
+
+Return Value:
+
+ Thread exit value.
+*******************************************************************
+--*/
+
+DWORD WINAPI
+SapXsWorkerThread(
+ LPVOID Threadparm)
+{
+ NTSTATUS Status;
+ NWSAP_REQUEST_MESSAGE Request;
+ NWSAP_REPLY_MESSAGE Reply;
+ PPORT_MESSAGE Myreply;
+ PSAP_LPC_CLIENT Clientp;
+
+ /** **/
+
+ IF_DEBUG(LPC) {
+ SS_PRINT(("NWSAP: XsWorker entered\n"));
+ }
+
+ /**
+ Listen for requests from clients. Handle connections
+ special.
+ **/
+
+ while (1) {
+
+ /**
+ Send the reply for the last message and get
+ another message to handle
+ **/
+
+ IF_DEBUG(LPC) {
+ SS_PRINT(("NWSAP: XsWorker: Calling ReplyWaitReceivePort: Handle = 0x%lx\n", SapXsLpcPortHandle));
+ }
+
+ Status = NtReplyWaitReceivePort(
+ SapXsLpcPortHandle, /* Handle */
+ &Clientp, /* Context */
+ NULL, /* Ptr to reply message */
+ (PPORT_MESSAGE)&Request); /* Ptr to request message */
+
+ /** Setup ptr to our reply message **/
+
+ IF_DEBUG(LPC) {
+ SS_PRINT(("NWSAP: XsWorker: Reply Wait Port: status = 0x%lx: Clientp = 0x%lx\n", Status, Clientp));
+ }
+
+ /** If shutting down - just leave **/
+
+ if (!SsInitialized)
+ break;
+
+ /** If we got an error - handle it **/
+
+ if (!NT_SUCCESS(Status)) {
+
+ IF_DEBUG(ERRORS) {
+ SS_PRINT(("NWSAP: XsWorker: NtReplyWaitReceivePort failed: %X\n", Status));
+ }
+
+ continue;
+ }
+
+ /** If this is a connection - go handle it **/
+
+ if (Request.PortMessage.u2.s2.Type == LPC_CONNECTION_REQUEST) {
+ SapXsHandleConnect((PPORT_MESSAGE)&Request);
+ continue;
+ }
+
+ /** Handle the message **/
+
+ IF_DEBUG(LPC) {
+ SS_PRINT(("NWSAP: XsWorker: Got message = %d, context = 0x%lx\n", Request.MessageType, Clientp));
+ }
+
+ Myreply = NULL;
+ switch (Request.PortMessage.u2.s2.Type) {
+ case LPC_REQUEST:
+
+ IF_DEBUG(LPC) {
+ SS_PRINT(("NWSAP: Worker: Got REQUEST: %d\n", Request.MessageType));
+ }
+
+ /** Handle the message **/
+
+ Myreply = (PPORT_MESSAGE)&Reply;
+ switch (Request.MessageType) {
+
+ case NWSAP_LPCMSG_ADDADVERTISE:
+ Reply.Error = SapAddAdvertiseInternal(
+ Request.Message.AdvApi.ServerName,
+ Request.Message.AdvApi.ServerType,
+ Request.Message.AdvApi.ServerAddr,
+ Request.Message.AdvApi.RespondNearest,
+ (ULONG)Clientp);
+
+ memcpy(
+ Reply.Message.AdvApi.ServerAddr,
+ Request.Message.AdvApi.ServerAddr,
+ 12);
+
+ break;
+
+ case NWSAP_LPCMSG_REMOVEADVERTISE:
+ Reply.Error = SapRemoveAdvertiseInternal(
+ Request.Message.AdvApi.ServerName,
+ Request.Message.AdvApi.ServerType);
+ break;
+
+ case NWSAP_LPCMSG_GETOBJECTID:
+ Reply.Error = SapGetObjectIDInternal(
+ Request.Message.BindLibApi.ObjectName,
+ Request.Message.BindLibApi.ObjectType,
+ &Reply.Message.BindLibApi.ObjectID);
+ break;
+
+ case NWSAP_LPCMSG_GETOBJECTNAME:
+ Reply.Error = SapGetObjectNameInternal(
+ Request.Message.BindLibApi.ObjectID,
+ Reply.Message.BindLibApi.ObjectName,
+ &Reply.Message.BindLibApi.ObjectType,
+ Reply.Message.BindLibApi.ObjectAddr);
+ break;
+
+ case NWSAP_LPCMSG_SEARCH:
+ Reply.Error = SapScanObjectInternal(
+ &Request.Message.BindLibApi.ObjectID,
+ Reply.Message.BindLibApi.ObjectName,
+ &Reply.Message.BindLibApi.ObjectType,
+ Request.Message.BindLibApi.ScanType);
+
+ Reply.Message.BindLibApi.ObjectID = Request.Message.BindLibApi.ObjectID;
+ break;
+
+ default:
+ IF_DEBUG(LPC) {
+ SS_PRINT(("NWSAP: XsWorker: Got unknown LPC SAP msg: %d\n", Request.MessageType));
+ }
+ Reply.Error = 1;
+ break;
+ }
+ break;
+
+ /** Client has died - clean up and go on **/
+
+ case LPC_PORT_CLOSED:
+ case LPC_CLIENT_DIED:
+
+ IF_DEBUG(LPC) {
+ SS_PRINT(("NWSAP: XsWorker: Client DIED: Clientp = 0x%lx\n", Clientp));
+ }
+
+ /** Take the client out of the list **/
+
+ ACQUIRE_LPCCLIENT_LOCK();
+ RemoveEntryList(&Clientp->ListEntry);
+ SapNumLpcClients--;
+ if (Clientp->Handle) {
+ NtClose(Clientp->Handle);
+ Clientp->Handle = NULL;
+ }
+ RELEASE_LPCCLIENT_LOCK();
+
+ /**
+ Cleanup all advertises the client had.
+
+ NOTE: Disabled now because we are not sure whether
+ we want to do it or not.
+ **/
+
+#if 0
+ SapClientDisconnected((ULONG)Clientp);
+#endif
+
+ /** Free the client memory **/
+
+ SAP_FREE(Clientp, "XsWorker: Leaving\n");
+
+ /** There is no reply for this **/
+
+ Myreply = NULL;
+ break;
+
+ /** All others just leave **/
+
+ default:
+ IF_DEBUG(LPC) {
+ SS_PRINT(("NWSAP: XsWorker: Got unknown LPC msg type %d\n", Request.PortMessage.u2.s2.Type));
+ }
+ Myreply = NULL; /* No reply */
+ break;
+ }
+
+ /** Setup for the reply and go back up to send it **/
+
+ if (Myreply) {
+
+ /** Fill out the reply **/
+
+ Reply.PortMessage.u1.s1.DataLength =
+ sizeof(Reply) - sizeof(PORT_MESSAGE);
+ Reply.PortMessage.u1.s1.TotalLength = sizeof(Reply);
+ Reply.PortMessage.u2.ZeroInit = 0;
+ Reply.PortMessage.ClientId = Request.PortMessage.ClientId;
+ Reply.PortMessage.MessageId = Request.PortMessage.MessageId;
+
+ /** Send the reply **/
+
+ Status = NtReplyPort(
+ Clientp->Handle,
+ (PPORT_MESSAGE)&Reply);
+
+ IF_DEBUG(LPC) {
+ SS_PRINT(("NWSAP: XsWorker: NtReplyPort status = 0x%lx\n", Status));
+ }
+ }
+
+ /** Go back to top to send reply and get another request **/
+ }
+
+ /** All Done - get out **/
+
+ SAP_DEC_LPC_THREAD_COUNT("LpcWorker: Terminating");
+ return NO_ERROR;
+}
+
+
+/*++
+*******************************************************************
+ S a p X s H a n d l e C o n n e c t
+
+Routine Description:
+
+ This routine is called by the SAP LPC worker threads when
+ a connect request is received. It will handle the
+ connect request completely here.
+
+Arguments:
+
+ Request = Ptr to the connection message received
+
+Return Value:
+
+ None.
+*******************************************************************
+--*/
+
+VOID
+SapXsHandleConnect(
+ PPORT_MESSAGE Request)
+{
+ NTSTATUS Status;
+ PSAP_LPC_CLIENT Clientp;
+ BOOLEAN Acceptit;
+
+ /** Allocate a block for this client **/
+
+ Clientp = SAP_MALLOC(sizeof(SAP_LPC_CLIENT), "Get Lpc Client Struct");
+ if (Clientp == NULL) {
+
+ IF_DEBUG(ERRORS) {
+ SS_PRINT(("NWSAP: XsWorker: Error allocating client structure\n"));
+ }
+
+ /** Log the error **/
+
+ SsLogEvent(
+ NWSAP_EVENT_LPCLISTENMEMORY_ERROR,
+ 0,
+ NULL,
+ 0);
+
+ /** Do not accept the connect **/
+
+ Acceptit = FALSE;
+ }
+ else {
+
+ /** We will accept the connection **/
+
+ Acceptit = TRUE;
+
+ /** Initialize this block **/
+
+ Clientp->Handle = NULL;
+ Clientp->Context = (PVOID)Clientp;
+ InitializeListHead(&Clientp->ListEntry);
+
+ /** Put this entry in our client list **/
+
+ ACQUIRE_LPCCLIENT_LOCK();
+ InsertHeadList(&SapLpcClientList, &Clientp->ListEntry);
+ SapNumLpcClients++;
+ RELEASE_LPCCLIENT_LOCK();
+ }
+
+ /** Accept/Refuse the connection according to 'acceptit' **/
+
+ IF_DEBUG(LPC) {
+ SS_PRINT(("NWSAP: XsWorker: Calling NtAcceptConnectPort: Clientp = 0x%lx\n", Clientp));
+ }
+
+ Status = NtAcceptConnectPort(
+ &Clientp->Handle, /* Handle for this client */
+ Clientp->Context, /* Port Context */
+ Request, /* Connection request */
+ Acceptit, /* AcceptConnection */
+ NULL, /* ServerView */
+ NULL); /* Client View */
+
+ IF_DEBUG(LPC) {
+ SS_PRINT(("NWSAP: XsWorker: NtAcceptConnectPort: status = 0x%lx, handle = 0x%lx\n",Status,Clientp->Handle));
+ }
+
+ /**
+ If it failed - cleanup and return
+ **/
+
+ if (!NT_SUCCESS(Status)) {
+
+ /** **/
+
+ IF_DEBUG(ERRORS) {
+ SS_PRINT(("NWSAP: XsWorker: NtAcceptConnectPort failed: %X\n", Status));
+ }
+
+ /** Take him out of our list **/
+
+ if (Clientp) {
+ ACQUIRE_LPCCLIENT_LOCK();
+ RemoveEntryList(&Clientp->ListEntry);
+ SapNumLpcClients--;
+ RELEASE_LPCCLIENT_LOCK();
+
+ /** Free the memory **/
+
+ SAP_FREE(Clientp, "NtAcceptConnectPort error");
+ }
+
+ return;
+ }
+
+ /** If no Clientp ptr we rejected the connection - we are done **/
+
+ if (Clientp == NULL)
+ return;
+
+ /**
+ Complete the connection to the port, thereby releasing
+ the clients NtConnectPort.
+ **/
+
+ IF_DEBUG(LPC) {
+ SS_PRINT(("NWSAP: XsWorker: Calling NtCompleteConnectPort\n"));
+ }
+
+ Status = NtCompleteConnectPort(Clientp->Handle);
+
+ IF_DEBUG(LPC) {
+ SS_PRINT(("NWSAP: XsWorker: NtCompleteConnectPort: status = 0x%lx\n",Status));
+ }
+
+ /** If it failed - cleanup and return **/
+
+ if (!NT_SUCCESS(Status)) {
+ IF_DEBUG(ERRORS) {
+ SS_PRINT(("NWSAP: XsWorker: NtCompleteConnectPort failed: %X\n", Status));
+ }
+
+ /** Take him out of our list **/
+
+ ACQUIRE_LPCCLIENT_LOCK();
+ RemoveEntryList(&Clientp->ListEntry);
+ SapNumLpcClients--;
+ RELEASE_LPCCLIENT_LOCK();
+
+ /** Close the handle **/
+
+ NtClose(Clientp->Handle);
+
+ /** Release the memory **/
+
+ SAP_FREE(Clientp, "NtCompleteConnectPort error");
+ return;
+ }
+
+ /** Everything finished OK **/
+
+ return;
+}
+
+
diff --git a/private/net/svcdlls/nwsap/server/sdmd.c b/private/net/svcdlls/nwsap/server/sdmd.c
new file mode 100644
index 000000000..306c35086
--- /dev/null
+++ b/private/net/svcdlls/nwsap/server/sdmd.c
@@ -0,0 +1,2136 @@
+/*++
+
+Copyright (c) 1994 Microsoft Corporation
+Copyright (c) 1993 Micro Computer Systems, Inc.
+
+Module Name:
+
+ net\svcdlls\nwsap\server\sdmd.c
+
+Abstract:
+
+ This file contains the database routines for storing and
+ accessing the SAP records.
+
+ SDMD = Specialized Dynamic Memory Database
+
+Author:
+
+ Brian Walker (MCS) 06-15-1993
+
+Revision History:
+
+--*/
+
+#include "precomp.h"
+#pragma hdrstop
+
+/** **/
+
+INT SdmdCurrentTime;
+PSAP_RECORD SdmdTablePtr;
+SDMD_LIST_ENTRY SdmdLists[SAP_NUM_LISTINDEX];
+PSDMD_LIST_ENTRY SdmdNameHashTable;
+
+/** Internal Function Prototypes **/
+
+PSAP_RECORD
+SdmdGetFreeEntry(
+ VOID);
+
+
+/*++
+*******************************************************************
+ S a p I n i t S d m d
+
+Routine Description:
+
+ This routine is called to initialize the database.
+
+Arguments:
+
+ None
+
+Return Value:
+
+ 0 = OK
+ Else = Error
+*******************************************************************
+--*/
+
+INT
+SapInitSdmd(
+ VOID)
+{
+ PSAP_RECORD Entry;
+ PSDMD_LIST_ENTRY ListHead;
+ PSAP_RECORD HeadEntry;
+ INT i;
+ INT j;
+
+ /** Set current time to start **/
+
+ SdmdCurrentTime = 0;
+
+ /** Initialize all the lists **/
+
+ for (i = 0 ; i < SAP_NUM_LISTINDEX ; i++)
+ SdmdInitializeListHead(i);
+
+ /** Initialize the Critical Section we use for synch. **/
+
+ SdmdSynchEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
+ if (SdmdSynchEvent == NULL) {
+ SapError = 0;
+ SapEventId = NWSAP_EVENT_SDMDEVENT_FAIL;
+ return -1;
+ }
+ SdmdLockCount = 0;
+
+ /** Allocate the buffer for the database table **/
+
+ SdmdTablePtr = (PSAP_RECORD)SAP_MALLOC(SapNumArrayEntries * SAP_RECORD_SIZE, "SdmdInit");
+ if (SdmdTablePtr == NULL) {
+ SapError = 0;
+ SapEventId = NWSAP_EVENT_TABLE_MALLOC_FAILED;
+ return -1;
+ }
+
+ /** Allocate the hash table **/
+
+ SdmdNameHashTable = (PSDMD_LIST_ENTRY)SAP_MALLOC(SapHashTableSize * sizeof(SDMD_LIST_ENTRY), "SdmdHashInit");
+ if (SdmdNameHashTable == NULL) {
+ SapError = 0;
+ SapEventId = NWSAP_EVENT_HASHTABLE_MALLOC_FAILED;
+ return -1;
+ }
+
+ /** Initialize the hash list **/
+
+ ListHead = SdmdNameHashTable;
+ for (i = 0 ; i < SapHashTableSize ; i++,ListHead++) {
+ ListHead->Flink = SDMD_ENDOFLIST;
+ ListHead->Blink = SDMD_ENDOFLIST;
+ ListHead->ListIndex = i;
+ }
+
+ /**
+ This list of free entries is kept on the Server Type list
+ with the type being set to 0xFFFF. This way to find a
+ free entry - just go look at the tail of the Server Type
+ list.
+ **/
+
+ Entry = SdmdTablePtr;
+ for (i = 0 ; i < SapNumArrayEntries ; i++,Entry++) {
+
+ /** Init the Index number **/
+
+ Entry->Index = i;
+
+ /** Init the links for this table **/
+
+ for (j = 0 ; j < SAP_NUM_LISTINDEX ; j++) {
+ Entry->Links[j].Flink = SDMD_ENDOFLIST;
+ Entry->Links[j].Blink = SDMD_ENDOFLIST;
+ Entry->Links[j].ListIndex = j;
+ }
+
+ /** Mark this as a free entry **/
+
+ Entry->ServType = 0xFFFF;
+ Entry->HashIndex = -1;
+
+ /**
+ Add this to the free list. The first entry goes on the
+ tail of the TYPE list. Each other entry goes under the
+ first entry.
+ **/
+
+ if (i == 0) {
+ SdmdInsertTailList(SAP_TYPELIST_INDEX, Entry);
+ HeadEntry = Entry;
+ HeadEntry->HeadIndex = SDMD_ENDOFLIST;
+ }
+ else {
+ SdmdInsertTailSubList(HeadEntry, SAP_SUBLIST_INDEX, Entry);
+ }
+ }
+
+ /** All Done OK **/
+
+ return 0;
+}
+
+
+/*++
+*******************************************************************
+ S a p S h u t d o w n S d m d
+
+Routine Description:
+
+ The SAP Agent is stopping - clean up the database. All threads
+ are stopped when we get here.
+
+Arguments:
+
+ None
+
+Return Value:
+
+ Nothing
+*******************************************************************
+--*/
+
+VOID
+SapShutdownSdmd(
+ VOID)
+{
+ /** Free the table **/
+
+ if (SdmdTablePtr) {
+ SAP_FREE(SdmdTablePtr, "SDMD Shutdown - SAP Table");
+ }
+
+ if (SdmdNameHashTable) {
+ SAP_FREE(SdmdNameHashTable, "SDMD Shutdown, HASH Table");
+ }
+
+ /**
+ Clean up the Synchronization. The critical section
+ is initialized after the SynchEvent is created. This
+ way if InitSdmd never got called, we don't die if we close down.
+ **/
+
+ if (SdmdSynchEvent) {
+ CloseHandle(SdmdSynchEvent);
+ }
+
+ /** All Done **/
+
+ return;
+}
+
+
+/*++
+*******************************************************************
+ S d m d G e t F r e e E n t r y
+
+Routine Description:
+
+ This routine gets an entry from the free list. If no
+ entry is available, it grows the table. The WRITERS
+ lock should be held when calling this routine
+
+Arguments:
+
+ None
+
+Return Value:
+
+ Pointer to an entry to use.
+ NULL = There are no free entries. We tried
+ to increase the size of the table, but the
+ allocation failed.
+
+
+ IMPORTANT: When calling this function, make sure that
+ any pointers you have into the database are updated after
+ the call. This call can move the database.
+*******************************************************************
+--*/
+
+PSAP_RECORD
+SdmdGetFreeEntry(
+ VOID)
+{
+ PSAP_RECORD Entry;
+ PSAP_RECORD HeadEntry;
+ PSAP_RECORD Newtable;
+ INT Newnum;
+ INT i;
+ INT j;
+
+ /**
+ Get ptr to the last entry in the type list. The
+ type list can never be empty so there will always
+ be something in here.
+ **/
+
+ HeadEntry = GETPTRFROMINDEX(SdmdLists[SAP_TYPELIST_INDEX].Blink);
+
+ /** If this is free (type == 0xFFFF) - then return the entry **/
+
+ if (HeadEntry->ServType == 0xFFFF) {
+
+ /**
+ If there are no entries in this list - then we need to go
+ make more entries.
+
+ Else - we take an entry out of the list of free ones.
+ **/
+
+ if (HeadEntry->Links[SAP_SUBLIST_INDEX].Flink != SDMD_ENDOFLIST) {
+ Entry = SdmdRemoveTailSubEntryList(HeadEntry, SAP_SUBLIST_INDEX);
+ }
+ else
+ Entry = NULL;
+
+ /** If we have an entry - return it **/
+
+ if (Entry) {
+ Entry->Links[SAP_SUBLIST_INDEX].Flink = SDMD_ENDOFLIST;
+ Entry->Links[SAP_SUBLIST_INDEX].Blink = SDMD_ENDOFLIST;
+ Entry->HeadIndex = SDMD_ENDOFLIST;
+ Entry->HashIndex = -1; /* Not in hash table */
+ return Entry;
+ }
+ }
+
+ /**
+ There are no free entries - we must grow the list.
+
+ We grow the list by 1 1/2 times its current size.
+ **/
+
+ /** Allocate the buffer for how many entries **/
+
+ Newnum = SapNumArrayEntries * 3 / 2;
+ IF_DEBUG(SDMD) {
+ SS_PRINT(("GROWING TABLE: oldnum = %d, Newnum = %d\n", SapNumArrayEntries, Newnum));
+ }
+
+ Newtable = (PSAP_RECORD)SAP_MALLOC(Newnum * SAP_RECORD_SIZE, "SdmdGrow");
+ if (Newtable == NULL) {
+ IF_DEBUG(ERRORS) {
+ SS_PRINT(("GROWING: Allocation failed\n"));
+ }
+ return NULL;
+ }
+
+ /** Copy the old table into the new place **/
+
+ memcpy(Newtable, SdmdTablePtr, SapNumArrayEntries * SAP_RECORD_SIZE);
+
+ /** Free the current table **/
+
+ SAP_FREE(SdmdTablePtr, "Free old table on grow");
+
+ /** Set the new pointer **/
+
+ SdmdTablePtr = Newtable;
+
+ /** Get new ptr to the head entry of the free list **/
+
+ HeadEntry = GETPTRFROMINDEX(SdmdLists[SAP_TYPELIST_INDEX].Blink);
+ SS_ASSERT(HeadEntry);
+
+ /** Link up all the new entries into the free list. **/
+
+ Entry = SdmdTablePtr + SapNumArrayEntries;
+ for (i = SapNumArrayEntries ; i < Newnum ; i++,Entry++) {
+
+ /** Init the Index number **/
+
+ Entry->Index = i;
+
+ /** Init the links for this table **/
+
+ for (j = 0 ; j < SAP_NUM_LISTINDEX ; j++) {
+ Entry->Links[j].Flink = SDMD_ENDOFLIST;
+ Entry->Links[j].Blink = SDMD_ENDOFLIST;
+ Entry->Links[j].ListIndex = j;
+ }
+
+ /** Add entry to free list **/
+
+ Entry->ServType = 0xFFFF;
+ Entry->HashIndex = -1;
+
+ /**
+ If this is the last entry in the list - do not add
+ it to the free list. We will return it as the
+ new entry to use.
+ **/
+
+ if (i == (Newnum - 1))
+ break;
+
+ /**
+ We already have a head entry for the free list. So we will
+ just add these entries to the end of the free sub list.
+ **/
+
+ SdmdInsertTailSubList(HeadEntry, SAP_SUBLIST_INDEX, Entry);
+ }
+
+ /** Set the new number **/
+
+ SapNumArrayEntries = Newnum;
+
+ /** Return ptr to the new entry **/
+
+ Entry->HeadIndex = SDMD_ENDOFLIST;
+ return Entry;
+}
+
+
+/*++
+*******************************************************************
+ S d m d F r e e E n t r y
+
+Routine Description:
+
+ Free the given record back to the free pool
+ The WRITERS lock must be held when calling here.
+
+Arguments:
+
+ Entry = Ptr to the entry to free
+
+Return Value:
+
+ Nothing
+*******************************************************************
+--*/
+
+VOID
+SdmdFreeEntry(
+ PSAP_RECORD Entry)
+{
+ PSAP_RECORD HeadEntry;
+ PSAP_RECORD FreeHeadEntry;
+
+ /** Save ptr to current head entry **/
+
+ HeadEntry = GETPTRFROMINDEX(Entry->HeadIndex);
+
+ /**
+ Remove the entry from all of it's lists. If we do not have
+ a head entry - then we have not been put in any lists.
+ **/
+
+ if (HeadEntry) {
+ SdmdRemoveEntryList(SAP_TIMELIST_INDEX, Entry);
+ SdmdRemoveSubEntryList(HeadEntry, SAP_SUBLIST_INDEX, Entry);
+ }
+
+ /** If in hash table - take it out **/
+
+ if (Entry->HashIndex != -1) {
+
+ PSDMD_LIST_ENTRY ListHead;
+
+ /** Make sure the hash index is good **/
+
+ SS_ASSERT(Entry->HashIndex < SapHashTableSize);
+
+ /** Get ptr to the list head for the hash index **/
+
+ ListHead = SdmdNameHashTable + Entry->HashIndex;
+
+ /** Remove the entry from the hash **/
+
+ SdmdRemoveEntryFromHash(ListHead, Entry);
+ }
+ else {
+ SS_PRINT(("SdmdFreeEntry: HashEntry for Entry 0x%lx is -1\n", Entry));
+ }
+
+ /** Change the type to free **/
+
+ Entry->ServType = 0xFFFF;
+ Entry->HashIndex = -1;
+
+ /**
+ Put this on the end of the type list. If there
+ are no free entries - make this the first one. If there
+ are, then just put this on the end of those.
+ **/
+
+ FreeHeadEntry = GETPTRFROMINDEX(SdmdLists[SAP_TYPELIST_INDEX].Blink);
+ SS_ASSERT(FreeHeadEntry); /* SHOULD NEVER BE NULL HERE */
+ SdmdInsertTailSubList(FreeHeadEntry, SAP_SUBLIST_INDEX, Entry);
+
+ /**
+ If this entry was in a sub list and the sublist is empty,
+ then we want to free the head entry.
+ **/
+
+ if (HeadEntry) {
+
+ if (HeadEntry->Links[SAP_SUBLIST_INDEX].Flink == SDMD_ENDOFLIST) {
+ IF_DEBUG(SDMD) {
+ SS_PRINT(("NWSAP: Freeing Head entry 0x%lx\n", HeadEntry));
+ }
+
+ /** Take it out of the list **/
+
+ SdmdRemoveEntryList(SAP_TYPELIST_INDEX, HeadEntry);
+
+ /** Put it on the free list **/
+
+ HeadEntry->ServType = 0xFFFF;
+ SdmdInsertTailSubList(FreeHeadEntry, SAP_SUBLIST_INDEX, HeadEntry);
+ }
+ }
+
+ /** All Done **/
+
+ return;
+}
+
+
+/*++
+*******************************************************************
+ S d m d U p d a t e E n t r y
+
+Routine Description:
+
+ This is called to update an entry in the sap table. If
+ the entry does not exists - then it will be created.
+
+Arguments:
+
+ ServerName = 48 byte name of the server
+ ServerType = Object type of the server (Host Order)
+ ServerAddr = 12 byte address of the server
+ ServerHopCount = Hop count to get to the server (Host Order)
+ CardNumber = Card Number this came in on
+ CARDRET_MYSELF = 0xFE = Internal Server advertised by us
+ CARDRET_INTERNAL = 0xFF = Internal Server not advertised by us.
+ SendersAddress = Node address of who sent the packet
+
+Return Value:
+
+ 0 = Entry was updated
+ 1 = Entry was created
+ 2 = Name is zero length or too long
+ 3 = Invalid Object type
+ 4 = IPX Address is our own but service is not internal
+ -1 = Entry could not be created
+ -2 = Lock error
+*******************************************************************
+--*/
+
+INT
+SdmdUpdateEntry(
+ IN PUCHAR ServerName,
+ IN USHORT ServerType,
+ IN PUCHAR ServerAddr,
+ IN USHORT ServerHopCount,
+ IN INT CardNumber,
+ IN PUCHAR SendersAddress,
+ IN BOOL WanFlag)
+{
+ PSAP_RECORD Entry;
+ PSAP_RECORD HeadEntry;
+ PSDMD_LIST_ENTRY ListHead;
+ PUCHAR Ptr;
+ INT HashIndex;
+ INT BackIndex;
+ INT HeadBackIndex;
+ INT EntryIndex;
+ INT Length;
+ INT rc;
+ UCHAR Internal;
+
+ /**
+ Cannot have an FFFF as an object type. If they pass an invalid
+ object type - then just bail out here.
+ **/
+
+ if (ServerType == 0xFFFF)
+ return 3;
+
+ /**
+ Check if this is internal or not. Set the
+ flag accordingly.
+ **/
+
+ if (CardNumber == CARDRET_INTERNAL) {
+ if (SapRespondForInternal)
+ Internal = 1;
+ else
+ Internal = 0;
+ CardNumber = 0;
+ }
+ else if (CardNumber == CARDRET_MYSELF) {
+ Internal = 2;
+ CardNumber = 0;
+ }
+ else {
+
+ Internal = 0;
+
+ //
+ // check if the address of the service is ours... if so, then a
+ // router has send in one of our own services since we rebooted.
+ //
+
+ if ( (memcmp( ServerAddr,
+ SapNetNum,
+ SAP_NET_LEN) == 0) &&
+ (memcmp( ServerAddr+SAP_NET_LEN,
+ SapNodeNum,
+ SAP_NODE_LEN) == 0) ) {
+
+ IF_DEBUG(SDMD) {
+ SS_PRINT(("NWSAP: SdmdUpdateEntry: entry from net is for our address!\n"));
+ }
+ return 4;
+ }
+ }
+
+ /**
+ If name starts with 0 - don't save it. The Novell docs
+ imply that all names should be ASCIIZ. There have been a couple
+ of cases of a SAP coming out that has a name of 0. A Novell 3.11
+ server does not save the name so we will not either.
+ **/
+
+ if (*ServerName == '\0')
+ return 2;
+
+ /**
+ Make sure the name is not too long. The name should be
+ a max of 47 chars long (48 including the ending 0). If
+ the name is too long, then it is tossed.
+
+ Also make sure to uppercase the name.
+
+ This is a combo strlen/strupr routine.
+ **/
+
+ Ptr = ServerName;
+ Length = SAP_OBJNAME_LEN;
+ while (Length) {
+
+ /** If at end - break **/
+
+ if (*Ptr == '\0') /* If found end - break */
+ break;
+
+ /** Uppercase this letter **/
+
+ *Ptr = (UCHAR)toupper(*Ptr);
+
+ /** If at end - break out **/
+
+ if (Length == 1) {
+ Length = 0;
+ break;
+ }
+
+ /** Goto the next byte **/
+
+ Ptr++;
+ Length--;
+ }
+
+ /** If we did not find the 0 - return error **/
+
+ if (!Length && (*Ptr != '\0'))
+ return 2;
+
+ /** Lock the database **/
+
+ ACQUIRE_WRITERS_LOCK(rc, "Update Entry");
+
+ /** If error on acquire lock - just leave **/
+
+ if (rc) {
+
+ IF_DEBUG(ERRORS) {
+ SS_PRINT(("NWSAP: SdmdUpdateEntry: Error acquiring writers lock\n"));
+ }
+
+ return -2;
+ }
+
+ /**
+ Find the name and type in the hash list.
+ **/
+
+ HashIndex = SdmdCalcHash(ServerName);
+
+ /** Get ptr to the list head for the hash index **/
+
+ ListHead = SdmdNameHashTable + HashIndex;
+
+ /** Find the name in the list **/
+
+ BackIndex = SDMD_ENDOFLIST;
+ Entry = GETPTRFROMINDEX(ListHead->Flink);
+ while (Entry) {
+
+ /** Make sure the Server Type is correct **/
+
+ if (Entry->ServType == ServerType) {
+
+ /** Check if this is the name **/
+
+ rc = SAP_NAMECMP(Entry->ServName, ServerName);
+
+ /** If this is it - break out to handle it **/
+
+ if (!rc)
+ break;
+
+ /**
+ Since the names are stored in alphabetical order, if we
+ get past the name we are looking for, then we can just
+ give up now instead of having to search the whole list.
+ **/
+
+ if (rc > 0) {
+ Entry = NULL;
+ break;
+ }
+ }
+
+ /** Goto the next entry **/
+
+ BackIndex = Entry->Index;
+ Entry = GETPTRFROMINDEX(Entry->Links[SAP_HASHLIST_INDEX].Flink);
+ }
+
+ /** If we found the entry - Update it **/
+
+ if (Entry) {
+
+ /**
+ If the hop count is 16, then this server is going
+ down. We set the hopcount to 16 so that the next
+ time we advertise - we will toss the entry after we
+ tell everyone about the fact that this server is down.
+ **/
+
+ if (ServerHopCount >= 16) {
+
+ /**
+ If from this same advertiser - then mark for deletion.
+ If this was from another advertiser, just ignore it
+ because we have kept the better route and are ignoring
+ all other routes to this server.
+ **/
+
+ if ((CardNumber == Entry->CardNumber) &&
+ (!memcmp(SendersAddress, Entry->Advertiser, SAP_NODE_LEN))) {
+
+ Entry->HopCount = 16;
+ Entry->Changed = TRUE;
+ SapChanged = TRUE;
+ }
+ RELEASE_WRITERS_LOCK("Update Entry X1");
+ return 0;
+ }
+
+ /**
+ If this is from the same advertiser, then update the
+ entry. We are keeping the best route. If we get
+ an advertise from someone else for the same name
+ (another router, we will check below if it is a better
+ router). If the hop count for this entry is 16, we update
+ regardless of who sent it since it a good route.
+ **/
+
+ if ( ((CardNumber == Entry->CardNumber) &&
+ (!memcmp(SendersAddress, Entry->Advertiser, SAP_NODE_LEN)))
+
+ ||
+
+ (Entry->HopCount == 16)
+
+
+ ) {
+
+ /** Set the new time on this entry **/
+
+ Entry->Timeout = SdmdCurrentTime + SapTimeoutInterval;
+
+ /**
+ If any information changed - then update the information
+ and set the changed flag. This includes if a service
+ was marked for deletion (16 hops) and now another route
+ has been found.
+ **/
+
+ if (Entry->HopCount == 16) {
+
+ /**
+ This is another route for a service we think
+ is going down. We change over to the new
+ advertiser here.
+ **/
+
+ SAP_COPY_ADDRESS(Entry->ServAddress, ServerAddr);
+ memcpy(Entry->Advertiser, SendersAddress, SAP_NODE_LEN);
+ Entry->HopCount = ServerHopCount;
+ Entry->CardNumber = CardNumber;
+ Entry->Internal = Internal;
+ Entry->FromWan = WanFlag;
+
+ Entry->Changed = TRUE;
+ SapChanged = TRUE;
+ }
+ else if ((Entry->HopCount != ServerHopCount) ||
+ (memcmp(Entry->ServAddress, ServerAddr, SAP_ADDR_LEN)) ||
+ (Entry->Internal != Internal)) {
+
+ /**
+ This is an advertise from a router who has changed
+ his route to a service. We update our table to
+ show the new change.
+ **/
+
+ Entry->HopCount = ServerHopCount;
+ SAP_COPY_ADDRESS(Entry->ServAddress, ServerAddr);
+ Entry->Internal = Internal;
+ Entry->FromWan = WanFlag;
+ Entry->Changed = TRUE;
+ SapChanged = TRUE;
+ }
+
+ /** Put this on the end of the timeout list **/
+
+ SdmdRemoveEntryList(SAP_TIMELIST_INDEX, Entry);
+ SdmdInsertTailList(SAP_TIMELIST_INDEX, Entry);
+ }
+ else {
+
+ /**
+ This is from somebody else advertising the same
+ name. If this is a better route then the one we
+ have then we should replace the current one with
+ this one. Since this is the same name and type, we
+ don't check the network delay time, only the hop count.
+
+ The reason for this is that it is to the same dest
+ address to the service. Therefore the address of the
+ service is the same, so the delay time will be the
+ same for RIP since RIP is in charge of getting the
+ best time for us later anyway.
+ **/
+
+ if (ServerHopCount < Entry->HopCount) {
+
+ /** Fill in the new entry **/
+
+ SAP_COPY_ADDRESS(Entry->ServAddress, ServerAddr);
+ memcpy(Entry->Advertiser, SendersAddress, SAP_NODE_LEN);
+ Entry->HopCount = ServerHopCount;
+ Entry->CardNumber = CardNumber;
+ Entry->Internal = Internal;
+ Entry->FromWan = WanFlag;
+
+ /** This entry has changed **/
+
+ Entry->Changed = TRUE;
+ SapChanged = TRUE;
+
+ /** Set the new time on this entry **/
+
+ Entry->Timeout = SdmdCurrentTime + SapTimeoutInterval;
+
+ /** Put this on the end of the timeout list **/
+
+ SdmdRemoveEntryList(SAP_TIMELIST_INDEX, Entry);
+ SdmdInsertTailList(SAP_TIMELIST_INDEX, Entry);
+ }
+ }
+
+ /** Release the lock and leave **/
+
+ RELEASE_WRITERS_LOCK("Update Entry X2");
+ return 0;
+ }
+
+ /**
+ The entry was not found in our list - so we need to
+ create it in the database now.
+ **/
+
+ /** If this is for a down server - just ignore it **/
+
+ if (ServerHopCount >= 16) {
+ RELEASE_WRITERS_LOCK("Update Entry X3");
+ return 0;
+ }
+
+ /** Get a free entry **/
+
+ Entry = SdmdGetFreeEntry();
+ if (Entry == NULL) {
+ RELEASE_WRITERS_LOCK("Update Entry X4");
+ return -1;
+ }
+
+ /**
+ Find the HeadEntry that this entry belongs in.
+ **/
+
+ HeadBackIndex = SDMD_ENDOFLIST;
+ HeadEntry = GETPTRFROMINDEX(SdmdLists[SAP_TYPELIST_INDEX].Flink);
+ while (HeadEntry && (HeadEntry->ServType < ServerType)) {
+ HeadBackIndex = HeadEntry->Index;
+ HeadEntry = GETPTRFROMINDEX(HeadEntry->Links[SAP_TYPELIST_INDEX].Flink);
+ }
+
+ /** If this type not found - then we need to create a HeadEntry **/
+
+ if ((HeadEntry == NULL) || (HeadEntry->ServType != ServerType)) {
+
+ /**
+ Get a new head entry - we must save the EntryIndex and
+ get a ptr back to after the call to GetFreeEntry because
+ the database could have moved in memory during the
+ GetFreeEntry call.
+ **/
+
+ EntryIndex = Entry->Index; /* Save entry index */
+ HeadEntry = SdmdGetFreeEntry();
+
+ /** Get ptr to entry back **/
+
+ Entry = GETPTRFROMINDEX(EntryIndex);
+ SS_ASSERT(Entry);
+
+ /** If no head entry alloc'd - return error **/
+
+ if (HeadEntry == NULL) {
+ SdmdFreeEntry(Entry);
+ RELEASE_WRITERS_LOCK("Update Entry X6");
+ return -1;
+ }
+
+ /** Insert this entry in the TYPE list **/
+
+ HeadEntry->ServType = ServerType;
+ HeadEntry->HeadIndex = SDMD_ENDOFLIST;
+ SdmdInsertList(SAP_TYPELIST_INDEX, HeadEntry, HeadBackIndex);
+ }
+
+ /** **/
+
+ SS_ASSERT(HeadEntry);
+
+ /** Fill in the entry **/
+
+ memset(Entry->ServName, '\0', SAP_OBJNAME_LEN);
+ strcpy(Entry->ServName, ServerName);
+ SAP_COPY_ADDRESS(Entry->ServAddress, ServerAddr);
+ memcpy(Entry->Advertiser, SendersAddress, SAP_NODE_LEN);
+ Entry->ServType = ServerType;
+ Entry->HopCount = ServerHopCount;
+ Entry->CardNumber = CardNumber;
+ Entry->Timeout = SdmdCurrentTime + SapTimeoutInterval;
+ Entry->Internal = Internal;
+ Entry->Changed = TRUE;
+ Entry->FromWan = WanFlag;
+
+ /** Mark that we have changed our database **/
+
+ SapChanged = TRUE;
+
+ /** Now put this entry onto the head entry **/
+
+ SdmdInsertTailSubList(HeadEntry, SAP_SUBLIST_INDEX, Entry);
+
+ /** Put this on the end of the timeout list **/
+
+ SdmdInsertTailList(SAP_TIMELIST_INDEX, Entry);
+
+ /** Add this entry to the hash list **/
+
+ SdmdInsertEntryInHash(ListHead, Entry, BackIndex);
+
+ /** Release the lock **/
+
+ RELEASE_WRITERS_LOCK("Update Entry X5");
+
+ /** All Done **/
+
+ return 1;
+}
+
+
+/*++
+*******************************************************************
+ S d m d T i m e o u t C h e c k
+
+Routine Description:
+
+ This routine is called once every minute to check if
+ any entries have timed out and if they have, then to
+ free them.
+
+Arguments:
+
+ None
+
+Return Value:
+
+ 0 = Nothing Timed out
+ 1 = Something Timed out
+*******************************************************************
+--*/
+
+INT
+SdmdTimeoutCheck(
+ VOID)
+{
+ PSAP_RECORD Entry;
+ INT rc;
+
+ /** Lock the database **/
+
+ ACQUIRE_WRITERS_LOCK(rc, "Timeout Check");
+
+ /** If error getting lock - return it **/
+
+ if (rc) {
+
+ /** **/
+
+ IF_DEBUG(ERRORS) {
+ SS_PRINT(("NWSAP: SdmdTimeoutCheck: Error getting writers lock\n"));
+ }
+
+ /** Nothing timed out **/
+
+ return 0;
+ }
+
+ /** Add to the current time **/
+
+ SdmdCurrentTime++;
+
+ /**
+ We check the head of the TIMEOUT list for entries that
+ are timing out. Since this list is ordered by time, we
+ can check if very easily.
+ **/
+
+ rc = 0;
+ Entry = GETPTRFROMINDEX(SdmdLists[SAP_TIMELIST_INDEX].Flink);
+ while (Entry) {
+
+ /** If time does not match - leave **/
+
+ if (Entry->Timeout != SdmdCurrentTime)
+ break;
+
+ /**
+ If this is from a WAN - just put it on the
+ end of the list.
+ **/
+
+ if (Entry->FromWan) {
+ Entry->Timeout = SdmdCurrentTime + SapTimeoutInterval;
+ SdmdRemoveEntryList(SAP_TIMELIST_INDEX, Entry);
+ SdmdInsertTailList(SAP_TIMELIST_INDEX, Entry);
+ }
+ else {
+
+ /** Time matches - toss this entry **/
+
+ IF_DEBUG(SDMD) {
+ SS_PRINT(("Timing out entry index %d\n", Entry->Index));
+ }
+ Entry->HopCount = 16; /* Entry no longer valid */
+ Entry->Changed = TRUE;
+ SapChanged = TRUE;
+ rc = 1;
+ }
+
+ /** Goto the next entry in the list **/
+
+ Entry = GETPTRFROMINDEX(Entry->Links[SAP_TIMELIST_INDEX].Flink);
+ }
+
+ /** Release the lock **/
+
+ RELEASE_WRITERS_LOCK("Timeout Check X");
+
+ /** All Done **/
+
+ return rc;
+}
+
+
+/*++
+*******************************************************************
+ S d m d G e t N e a r e s t S e r v e r L a n
+
+Routine Description:
+
+ When we get a GET NEAREST SERVICE QUERY and we are not
+ advertising for a service of that type. There could
+ be a service that is on this local machine that we need
+ to response for. If there is no internal server, then
+ we find one that is not too far away.
+
+Arguments:
+
+ Ptr = Ptr to the buffer to fill in with the response
+ ServType = Service type that the requestor wants
+ (Cannot be 0xFFFF).
+ Cardnum = Card number that request was received on.
+ Bcast = 0 - Unicast request received
+ Else = Broadcast request received
+
+Return Value:
+
+ 0 = No service found
+ Else = Size of the return buffer
+*******************************************************************
+--*/
+
+INT
+SdmdGetNearestServerLan(
+ PUCHAR Ptr,
+ USHORT ServType,
+ INT Cardnum,
+ UCHAR Bcast)
+{
+ PSAP_RECORD Entry;
+ PSAP_RECORD HeadEntry;
+ PSAP_RECORD NEntry;
+ PSAP_HEADER Hdrp;
+ USHORT HopInc;
+ USHORT DelayTime;
+ USHORT CurDelay;
+ UCHAR Local;
+ BOOL Flag;
+
+ /** Get the readers lock **/
+
+ ACQUIRE_READERS_LOCK("SdmdGetNearestServerLan");
+
+ /** Goto the server entries of the type given **/
+
+ HeadEntry = GETPTRFROMINDEX(SdmdLists[SAP_TYPELIST_INDEX].Flink);
+ while (HeadEntry && (HeadEntry->ServType < ServType)) {
+ HeadEntry = GETPTRFROMINDEX(HeadEntry->Links[SAP_TYPELIST_INDEX].Flink);
+ }
+
+ /** If not found - just return nothing **/
+
+ if ((HeadEntry == NULL) || (HeadEntry->ServType != ServType)) {
+ RELEASE_READERS_LOCK("SdmdGetNearestServerLan X1");
+ return 0;
+ }
+
+ /**
+ Search the entries to find the nearest server we can.
+ If there is not a nearest - just return 0.
+ **/
+
+ NEntry = NULL; /* No nearest entry yet */
+ Local = 0; /* No local net entry found */
+
+ /**
+ Go thru all the entries of this type to see if they
+ match.
+ **/
+
+ Entry = GETPTRFROMINDEX(HeadEntry->Links[SAP_SUBLIST_INDEX].Flink);
+ while (Entry) {
+
+ /**
+ If this entry is internal to me - skip it
+ **/
+
+ if (Entry->Internal == 2)
+ goto next_entry;
+
+ /** If Hopcount is 16 - skip this entry. It is going away **/
+
+ if (Entry->HopCount != 16) {
+
+ /**
+ If this is internal to our server - respond for it, then
+ we will check to see if we need to put it in our list to
+ respond for.
+ **/
+
+ if (Entry->Internal) {
+ NEntry = Entry;
+ HopInc = 0;
+ Local = 0; /* For Bcast/Local check to fail */
+ break; /* Send it back now */
+ }
+
+ /**
+ If this server is for the same card as the request
+ came in on, then we mark that we found a local server
+ so that we can check it later. We do not want to
+ respond with a remote server if there is one locally.
+
+ In the case that the request is a bcast, we will not
+ respond for the local server because that server will
+ respond for itself. If the request is not bcast, then
+ we must respond because we are the only machine that has
+ seen this request.
+ **/
+
+ if (Entry->CardNumber == Cardnum) {
+
+ /** Remember we found this local server **/
+
+ NEntry = Entry;
+ HopInc = 0;
+ Local = 1;
+ }
+ else if (Local == 0) {
+
+ /**
+ If this entry came from a LAN (which is not
+ the same card as the request came in on) AND
+ we are not routing from LAN to LAN - then skip
+ this entry.
+ **/
+
+ if ((!Entry->FromWan) && (SapDontHopLans))
+ Flag = FALSE;
+ else {
+
+ /**
+ See if we are allowed to send this server
+ across.
+ **/
+
+ Flag = SapShouldIAdvertiseByName(Entry->ServName);
+ }
+
+ /**
+ If we are allowed to return this entry -
+ then check to see if it is closer then any
+ other entry we have.
+ **/
+
+ if (Flag) {
+
+ /**
+ If we have no server on the net that the
+ request was recv'd on, find a remote server that
+ is as close as we can get. If we find a local server
+ later, we will use it instead of a remote server.
+ **/
+
+ if (NEntry) {
+
+ /**
+ If the travel time (reported from RIP) is shorter,
+ then use this entry, else if the travel
+ times are the same and the hopcount is
+ smaller, use this entry.
+
+ We don't check for 0xFFFF because it is always
+ greater then everything else being a USHORT.
+ **/
+
+ DelayTime = SapGetDelayTime(Entry->ServAddress);
+ if (DelayTime < CurDelay) {
+ NEntry = Entry;
+ HopInc = 1;
+ CurDelay = DelayTime;
+ }
+ else if (DelayTime == CurDelay) {
+ if (Entry->HopCount < NEntry->HopCount) {
+ NEntry = Entry;
+ HopInc = 1;
+ }
+ }
+ }
+ else {
+
+ /**
+ This is the first remote entry we have found. Remember
+ it because it is the closest one we know about so far.
+ **/
+
+ DelayTime = SapGetDelayTime(Entry->ServAddress);
+
+ NEntry = Entry;
+ HopInc = 1;
+ CurDelay = DelayTime;
+ }
+ }
+ }
+ }
+
+ /** Goto the next entry **/
+
+next_entry:
+ Entry = GETPTRFROMINDEX(Entry->Links[SAP_SUBLIST_INDEX].Flink);
+ }
+
+ /** If no server found - return it **/
+
+ if (NEntry == NULL) {
+ RELEASE_READERS_LOCK("SdmdGetNearestServerLan X2");
+ return 0;
+ }
+
+ /**
+ If the request was broadcast and we found a local server, then
+ we need to just ignore the request. This means that there
+ is a local server on the network that can respond. Local
+ being set means that we only found servers that were local
+ to the requestor. We have nothing to respond with.
+ **/
+
+ if (Bcast && Local) {
+ RELEASE_READERS_LOCK("SdmdGetNearestServerLan X2A");
+ return 0;
+ }
+
+ /** Build response code **/
+
+ *(PUSHORT)Ptr = htons(SAPTYPE_NEAREST_SERVICE_RESPONSE);
+ Ptr += sizeof(USHORT);
+
+ /** Build the data about the entry **/
+
+ Hdrp = (PSAP_HEADER)Ptr;
+ Hdrp->ServerType = htons(NEntry->ServType);
+ Hdrp->Hopcount = htons((USHORT)(NEntry->HopCount + HopInc));
+ SAP_COPY_SERVNAME(Hdrp->ServerName, NEntry->ServName);
+ SAP_COPY_ADDRESS(Hdrp->Address, NEntry->ServAddress);
+
+ /** Release the lock on the list **/
+
+ RELEASE_READERS_LOCK("SdmdGetNearestServerLan X3");
+
+ /** Return the size of the entry **/
+
+ return SAP_HEADER_SIZE + sizeof(USHORT);
+}
+
+
+/*++
+*******************************************************************
+ S d m d G e t N e a r e s t S e r v e r W a n
+
+Routine Description:
+
+ When we get a GET NEAREST SERVICE QUERY and we are not
+ advertising for a service of that type. There could
+ be a service that is on this local machine that we need
+ to response for. If there is no internal server, then
+ we find one that is not too far away.
+
+ For WAN - we support sending up to 4 responses. We
+ build them here. We assume the Maxnump will always hold
+ the numbers 1 or 4. This simplifies the code and makes
+ it where we don't have to write a bunch of extra code to
+ handle smaller numbers.
+
+Arguments:
+
+ Ptr = Ptr to the buffer to fill in with the response
+ ServType = Service type that the requestor wants
+ (Cannot be 0xFFFF).
+ Cardnum = Card number that request was received on.
+ Bcast = 0 - Unicast request received
+ Else = Broadcast request received
+ NumFoundp = Ptr to store number of return entries we built
+
+Return Value:
+
+ 0 = No services of that type are in the database
+ (*NumFoundp is also set to 0)
+ Else = Size of one return buffer
+ (*NumFoundp = Number of entries to return)
+
+ Do not depend on the return length to see if any were built.
+ Always check the NumFoundp value.
+*******************************************************************
+--*/
+
+INT
+SdmdGetNearestServerWan(
+ PUCHAR Ptr,
+ USHORT ServType,
+ INT Cardnum,
+ UCHAR Bcast,
+ INT *NumFoundp)
+{
+ INT Maxtodo;
+ INT i;
+ PSAP_RECORD Entry;
+ PSAP_RECORD HeadEntry;
+ PSAP_HEADER Hdrp;
+ PSAP_RECORD NEntry[4];
+ USHORT HopInc[4];
+ USHORT DelayTime[4];
+ USHORT CurDelay;
+ UCHAR Local;
+ BOOL Flag;
+
+ /** Start as none returned **/
+
+ *NumFoundp = 0;
+
+ /**
+ We must acquire this lock here to avoid a deadlock condition
+ where we would have a lock order problem.
+ **/
+
+ ACQUIRE_SENDTABLE_LOCK();
+
+ /** Get the readers lock **/
+
+ ACQUIRE_READERS_LOCK("SdmdGetNearestServerWan");
+
+ /** Goto the server entries of the type given **/
+
+ HeadEntry = GETPTRFROMINDEX(SdmdLists[SAP_TYPELIST_INDEX].Flink);
+ while (HeadEntry && (HeadEntry->ServType < ServType)) {
+ HeadEntry = GETPTRFROMINDEX(HeadEntry->Links[SAP_TYPELIST_INDEX].Flink);
+ }
+
+ /** If not found - just return nothing **/
+
+ if ((HeadEntry == NULL) || (HeadEntry->ServType != ServType)) {
+ RELEASE_READERS_LOCK("SdmdGetNearestServerWan X1");
+ RELEASE_SENDTABLE_LOCK();
+ return 0;
+ }
+
+ /**
+ Search the entries to find the nearest server we can.
+ If there is not a nearest - just return 0.
+ **/
+
+ NEntry[0] = NULL; /* No nearest entry yet */
+ NEntry[1] = NULL; /* No nearest entry yet */
+ NEntry[2] = NULL; /* No nearest entry yet */
+ NEntry[3] = NULL; /* No nearest entry yet */
+
+ /** Get the max we are to do **/
+
+ Maxtodo = 4;
+
+ /**
+ Go thru all the entries of this type to see if they
+ match.
+ **/
+
+ Entry = GETPTRFROMINDEX(HeadEntry->Links[SAP_SUBLIST_INDEX].Flink);
+ while (Entry) {
+
+ /** If Hopcount is 16 - skip this entry. It is going away **/
+
+ if (Entry->HopCount != 16) {
+
+ /**
+ If this is internal to our server - respond for it, then
+ we will check to see if we need to put it in our list to
+ respond for.
+ **/
+
+ if (Entry->Internal) {
+
+ /**
+ If this entry is being advertised by me, then
+ I need to see if I am allowed to respond for it
+ using the GetNearestServer call.
+ **/
+
+ if (Entry->Internal == 2) {
+
+ /** See if I am allowed **/
+
+ Flag = SapCanIRespondNearest(
+ Entry->ServName,
+ Entry->ServType);
+
+ /** If no - skip the entry **/
+
+ if (Flag == FALSE) {
+ goto next_entry;
+ }
+ }
+
+ /**
+ We have more than one to return, so we will
+ find one not assigned yet.
+ **/
+
+ for (i = 0 ; i < Maxtodo ; i++) {
+
+ /** If this one empty - set it up **/
+
+ if (NEntry[i] == NULL) {
+ NEntry[i] = Entry;
+ HopInc[i] = 0;
+ DelayTime[i] = 0;
+ break;
+ }
+ }
+
+ /**
+ If all slots have something in them now, then we
+ will find the one farthest away and replace it with
+ this new one.
+ **/
+
+ if (i == Maxtodo) {
+ INT Best = -1; /* Find Best one */
+
+ /** Go find one to replace with this one **/
+
+ for (i = 0 ; i < Maxtodo ; i++){
+
+ /**
+ If this entry is internal, then do not
+ replace it because the entry we have is
+ internal also and we do not need to
+ replace an internal with an internal.
+ **/
+
+ if (NEntry[i]->Internal)
+ continue;
+
+ /**
+ If this server is local to the network that
+ the request came from - then do not
+ overwrite with an internal one of mine.
+ **/
+
+ if (NEntry[i]->CardNumber == Cardnum)
+ continue;
+
+ /** If Best not set yet - set to replace this one **/
+
+ if (Best = -1) {
+ Best = i;
+ CurDelay = DelayTime[i];
+ continue;
+ }
+
+ /**
+ NEntry[Best] is not internal and
+ NEntry[i] is not internal.
+
+ If this current one (Index 'i') is closer
+ then the current best one (Index 'Best'), then
+ we should replace the current best one with
+ this one.
+ **/
+
+ if (DelayTime[i] < CurDelay) {
+ Best = i;
+ CurDelay = DelayTime[i];
+ }
+ else if (DelayTime[i] == CurDelay) {
+ if (NEntry[i]->HopCount < NEntry[Best]->HopCount) {
+ Best = i; /* CurDelay already set OK */
+ }
+ }
+ } /* For i = .. */
+
+ /**
+ If we found an entry to replace the best one -
+ replace it here.
+ **/
+
+ if (Best != -1) {
+ NEntry[Best] = Entry;
+ HopInc[Best] = 0;
+ DelayTime[Best] = CurDelay;
+ }
+ } /* If i == Maxtodo */
+
+ /** Skip to the next entry **/
+
+ goto next_entry;
+ } /* If Entry->Internal */
+
+ /**
+ If this server is on the same card as the request
+ came in on, then we have found a server on the
+ same network as the requestee. In this case it means
+ there is a server across the WAN link that the client
+ can use.
+
+ If this request is broadcast, then we will ignore
+ the request because that server will respond.
+
+ If the reqeust is NOT broadcast, then we will respond
+ for this server.
+ **/
+
+ if (Entry->CardNumber == Cardnum) {
+
+ /**
+ If the request was BCAST, then we do not respond
+ because there are servers local to the requestee
+ that can. We will just ignore the request.
+ **/
+
+ if (Bcast) {
+ RELEASE_READERS_LOCK("SdmdGetNearestServerWan X2");
+ RELEASE_SENDTABLE_LOCK();
+ return 0;
+ }
+
+ /**
+ The request was UNICAST - we will respond. We go
+ find the worst server there is and replace it with
+ this one.
+ **/
+
+ Local = 1; /* Local to the requestee */
+ Flag = 1; /* We will respond for this one */
+ }
+ else {
+ Local = 0; /* Not local to the requestee */
+
+ /** Check filter list if we should respond **/
+
+ Flag = SapShouldIAdvertiseByName(Entry->ServName);
+ }
+
+ /**
+ Now see if we should replace a server in our list with
+ this server.
+ **/
+
+ if (Flag) {
+
+ /**
+ If there is an empty slot - just put this
+ server in it.
+ **/
+
+ for (i = 0 ; i < Maxtodo ; i++) {
+
+ /**
+ If all the entries are not full yet, then we
+ just put this entry in a free slot.
+ **/
+
+ if (NEntry[i] == NULL) {
+ NEntry[i] = Entry;
+ if (Local) {
+ HopInc[i] = 0;
+ DelayTime[i] = 0;
+ }
+ else {
+ HopInc[i] = 1;
+ DelayTime[i] = SapGetDelayTime(Entry->ServAddress);
+ }
+ break;
+ }
+ }
+
+ /**
+ If no free entries found - we need to find
+ the worst entry there is and replace it if
+ it is worse then this new entry.
+ **/
+
+ if (i == Maxtodo) {
+ INT Worst = -1;
+
+ /**
+ Go thru the current list and see if there is
+ one in the current list that is not as good
+ as the one we are checking.
+
+ First we go thru the 4 entries and find the
+ worst one. Then when we get done, we
+ check it against the new entry and see if it
+ is better/worst. If new entry is better, then
+ we put it in place of the worst entry.
+ **/
+
+ for (i = 0 ; i < Maxtodo ; i++) {
+
+ /**
+ If this NEntry is local to the requestee,
+ do not replace it ever.
+ **/
+
+ if (NEntry[i]->CardNumber == Cardnum)
+ continue;
+
+ /**
+ If worst not set yet - use this one as the
+ worst.
+ **/
+
+ if (Worst == -1) {
+ Worst = i;
+ continue;
+ }
+
+ /**
+ If the worst one so far is internal, then
+ we check if the current one we are checking
+ is internal. If they are both internal, then
+ they are EQUAL and we just skip to the next
+ entry.
+
+ If the current one is not internal, then we
+ make it the worst one.
+ **/
+
+ if (NEntry[Worst]->Internal) {
+
+ /** If also internal - skip it **/
+
+ if (NEntry[i]->Internal)
+ continue;
+
+ /** Make this the worst one **/
+
+ Worst = i;
+ continue;
+ }
+
+ /**
+ The worst one is not local and not internal.
+ So we check here. If the one we are checking
+ is internal, then it will not be as bad as the
+ worst one we have - so we skip to the next entry.
+ **/
+
+ if (NEntry[i]->Internal)
+ continue;
+
+ /**
+ NEntry[Worst] and NEntry[i] are both not
+ local and not internal. So we check the
+ travel time and hops to decide which is worst.
+
+ If NEntry[i] is worse then NEntry[Worst], then
+ we replace Worst with entry i.
+
+ If the DelayTime for the current entry (i)
+ is greater then the DelayTime of the Worst
+ entry, then replace it.
+
+ If the DelayTimes are the same - then check
+ the HopCounts. If the HopCount for the
+ current entry is greater then the HopCount
+ for the Worst entry, then replace it.
+ **/
+
+ if (DelayTime[Worst] < DelayTime[i]) {
+ Worst = i;
+ }
+ else if (DelayTime[Worst] == DelayTime[i]) {
+ if (NEntry[Worst]->HopCount < NEntry[i]->HopCount) {
+ Worst = i;
+ }
+ }
+ }
+
+ /**
+ If we found a worst entry, then we need to
+ check it against the new entry. If the new entry
+ is better then the worst entry we found, then
+ we can replace the worst with this one.
+ **/
+
+ if (Worst != -1) {
+
+ /**
+ If this new entry is local to the
+ requestee, then we know that the Worst
+ is not local to the requestee and we
+ can just replace it.
+ **/
+
+ if (Local) {
+ NEntry[Worst] = Entry;
+ DelayTime[Worst] = 0;
+ HopInc[Worst] = 0;
+ }
+ else {
+
+ /**
+ New Entry is not local, so we have to
+ check internal.
+
+ If the new entry is internal and the Worst
+ entry is internal then they are equal,
+ Do not replace. If the Worst entry
+ is NOT internal, then replace it.
+ **/
+
+ if (Entry->Internal) {
+
+ if (NEntry[Worst]->Internal == 0) {
+ NEntry[Worst] = Entry;
+ DelayTime[Worst] = 0;
+ HopInc[Worst] = 0;
+ }
+ }
+ else {
+
+ /** Get the delay time for the new entry **/
+
+ CurDelay = SapGetDelayTime(Entry->ServAddress);
+
+ /**
+ Compare this entry to the Worst entry and
+ see if we can replace it.
+ **/
+
+ if (DelayTime[Worst] > CurDelay) {
+ NEntry[Worst] = Entry;
+ DelayTime[Worst] = CurDelay;
+ HopInc[Worst] = 1;
+ }
+ else if (DelayTime[Worst] == CurDelay) {
+ if (NEntry[Worst]->HopCount > Entry->HopCount) {
+ NEntry[Worst] = Entry;
+ HopInc[Worst] = 1;
+ }
+ }
+ } /* If Entry->Internal, else */
+ } /* If Local, else */
+ } /* If Worst != -1 */
+ } /* If i == Maxtodo */
+ } /* If Flag */
+ } /* If Entry->HopCount != 16 */
+
+ /** Goto the next entry **/
+
+next_entry:
+ Entry = GETPTRFROMINDEX(Entry->Links[SAP_SUBLIST_INDEX].Flink);
+ }
+
+ /**
+ We now have up to 4 entries to send back.
+
+ Now we need to sort the entries we have to get them in
+ order to send back. We want the best entry to be in slot 0,
+ then slot 1,....
+ **/
+
+ for (i = 0 ; i < Maxtodo - 1 ; i++) {
+ INT j;
+ INT Best;
+
+ /** If entry not there - skip it **/
+
+ if (NEntry[i] == NULL)
+ continue;
+
+ /** If this entry local - cannot be made to move down **/
+
+ if (NEntry[i]->CardNumber == Cardnum)
+ continue;
+
+ /**
+ Scan the rest of entries to find a better one. If a better
+ one is found - switch this one (i) and the better one.
+ **/
+
+ Best = i; /* Start with this as best one */
+ for (j = i + 1 ; j < Maxtodo ; j++) {
+
+ /** If entry not there - skip it **/
+
+ if (NEntry[j] == NULL)
+ continue;
+
+ /**
+ 1) If this entry is local - it is better.
+
+ 2) If this entry (j) is internal and the best entry
+ is not - then this entry (j) is better.
+
+ 3) This entry (j) is not local and is not internal.
+ If the best entry is internal, it is better, so we
+ do not switch.
+
+ 4) Compare the delay time/Hop count to see if entry
+ Best is better then this entry (j).
+ **/
+
+ if (NEntry[j]->CardNumber == Cardnum) /* 1 */
+ Best = j;
+ else if (NEntry[j]->Internal) { /* 2 */
+
+ if (NEntry[Best]->Internal == 0) /* 2 */
+ Best = j;
+ }
+ else {
+
+ /** If Best internal - it is better, skip to next **/
+
+ if (NEntry[Best]->Internal) /* 3 */
+ continue;
+
+ /**
+ Entry j is not internal or local. Entry Best
+ is not internal or local. Compare these to
+ find the best one.
+
+ Step 4.
+ **/
+
+ if (DelayTime[Best] > DelayTime[j]) {
+ Best = j;
+ }
+ else if (DelayTime[Best] == DelayTime[j]) {
+ if (NEntry[Best]->HopCount > NEntry[j]->HopCount) {
+ Best = j;
+ }
+ }
+ }
+ }
+
+ /**
+ If a found a better one - switch these entries.
+ **/
+
+ if (Best != i) {
+ PSAP_RECORD TEntry;
+ USHORT THopInc;
+ USHORT TDelay;
+
+ /** Save entry i **/
+
+ TEntry = NEntry[i];
+ TDelay = DelayTime[i];
+ THopInc = HopInc[i];
+
+ /** Copy over new entry **/
+
+ NEntry[i] = NEntry[Best];
+ DelayTime[i] = DelayTime[Best];
+ HopInc[i] = HopInc[Best];
+
+ /** Set the best entry with old stuff **/
+
+ NEntry[Best] = TEntry;
+ DelayTime[Best] = TDelay;
+ HopInc[Best] = THopInc;
+ }
+ } /* For i = 0 ... */
+
+ /**
+ Build the entries into the request buffer. We assume that
+ the buffer will hold all the entries we need.
+ **/
+
+ for (i = 0 ; i < Maxtodo ; i++) {
+
+ /** Build it only if there **/
+
+ if (NEntry[i] != NULL) {
+
+ /** Build response code **/
+
+ *(PUSHORT)Ptr = htons(SAPTYPE_NEAREST_SERVICE_RESPONSE);
+ Ptr += sizeof(USHORT);
+
+ /** Build the data about the entry **/
+
+ Hdrp = (PSAP_HEADER)Ptr;
+ Hdrp->ServerType = htons(NEntry[i]->ServType);
+ Hdrp->Hopcount = htons((USHORT)(NEntry[i]->HopCount + HopInc[i]));
+ SAP_COPY_SERVNAME(Hdrp->ServerName, NEntry[i]->ServName);
+ SAP_COPY_ADDRESS(Hdrp->Address, NEntry[i]->ServAddress);
+ Ptr += SAP_HEADER_SIZE;
+ *NumFoundp += 1;
+ }
+ }
+
+ /** Release the lock on the list **/
+
+ RELEASE_READERS_LOCK("SdmdGetNearestServerWan X3");
+ RELEASE_SENDTABLE_LOCK();
+
+ /** Return the size of one entry **/
+
+ return SAP_HEADER_SIZE + sizeof(USHORT);
+}
+
+
+/*++
+*******************************************************************
+ S d m d C a l c H a s h
+
+Routine Description:
+
+ Calculate the hash index on a name for an entry.
+
+Arguments:
+
+ ServerName = Ptr to name to calculate hash index for
+
+Return Value:
+
+ The hash index is returned.
+*******************************************************************
+--*/
+
+INT
+SdmdCalcHash(
+ PUCHAR ServerName)
+{
+ INT HashIndex;
+
+ /** Add up all the bytes in the name **/
+
+ HashIndex = 0;
+ while (*ServerName) {
+ HashIndex += (INT)*ServerName;
+ ServerName++;
+ }
+
+ /** Divide by the size of the hash table **/
+
+ HashIndex = HashIndex % SapHashTableSize;
+
+ /** Return the hash index **/
+
+ return HashIndex;
+}
+
+
+/*++
+*******************************************************************
+ S d m d I s S e r v e r I n T a b l e
+
+Routine Description:
+
+ Check to see if the given ServerName/ServerType is in
+ our table.
+
+Arguments:
+
+ ServerName = Ptr to name to calculate hash index for
+ ServerType = Object type to find
+
+Return Value:
+
+ TRUE = The name is in our table
+ FALSE = The name is not in our table
+
+ The hash index is returned.
+*******************************************************************
+--*/
+
+BOOL
+SdmdIsServerInTable(
+ PUCHAR ServerName,
+ USHORT ServerType)
+{
+ PSAP_RECORD Entry;
+ PSDMD_LIST_ENTRY ListHead;
+ INT Retcode;
+ INT HashIndex;
+
+ /** Lock the database **/
+
+ ACQUIRE_READERS_LOCK("SdmdIsServerInTable");
+
+ /**
+ Find the name and type in the hash list.
+ **/
+
+ HashIndex = SdmdCalcHash(ServerName);
+
+ /** Get ptr to the list head for the hash index **/
+
+ ListHead = SdmdNameHashTable + HashIndex;
+
+ /** Find the name in the list **/
+
+ Entry = GETPTRFROMINDEX(ListHead->Flink);
+ while (Entry) {
+
+ /** Make sure the Server Type is correct **/
+
+ if (Entry->ServType == ServerType) {
+
+ /** Check if this is the name **/
+
+ Retcode = SAP_NAMECMP(Entry->ServName, ServerName);
+
+ /**
+ If this is it - then we need to see if this is a
+ good or bad thing.
+
+ If the server is one we are advertising - then
+ return that we did not find it.
+
+ If not - then check the AllowDuplicateServers flag
+ to see if we are suppossed to return it as found or
+ just ignore it.
+ **/
+
+ if (Retcode == 0) {
+
+ /**
+ If advertised by me (Sap agent) - then return not
+ found - this will get replaced when the new AddAdvertise
+ starts getting advertised.
+ **/
+
+ if (Entry->Internal == 2) {
+ RELEASE_READERS_LOCK("SdmdIsServerInTable XGOOD 1");
+ return FALSE;
+ }
+
+ /**
+ If on the same machine - but advertised by the process
+ and not me - let that slide too. If a process is doing
+ both this will let them work (per andyhe).
+ **/
+
+ if (Entry->Internal == 1) {
+ RELEASE_READERS_LOCK("SdmdIsServerInTable XGOOD 2");
+ return FALSE;
+ }
+
+ /** Release the lock **/
+
+ RELEASE_READERS_LOCK("SdmdIsServerInTable XGOOD 3");
+
+ /**
+ If allow dups - then return not found, we do
+ not keep looking since their should be only
+ one server for a name/type pair.
+ **/
+
+ if (SapAllowDuplicateServers)
+ return FALSE;
+
+ /** Dups not allowed - return bad **/
+
+ return TRUE;
+ }
+
+ /**
+ Since the names are stored in alphabetical order, if we
+ get past the name we are looking for, then we can just
+ give up now instead of having to search the whole list.
+ **/
+
+ if (Retcode > 0) {
+ break;
+ }
+ }
+
+ /** Goto the next entry **/
+
+ Entry = GETPTRFROMINDEX(Entry->Links[SAP_HASHLIST_INDEX].Flink);
+ }
+
+ /** Not in the list - return it **/
+
+ RELEASE_READERS_LOCK("SdmdIsServerInTable XBAD");
+ return FALSE;
+}
diff --git a/private/net/svcdlls/nwsap/server/sdmd.h b/private/net/svcdlls/nwsap/server/sdmd.h
new file mode 100644
index 000000000..b8ac0fcc0
--- /dev/null
+++ b/private/net/svcdlls/nwsap/server/sdmd.h
@@ -0,0 +1,67 @@
+/*++
+
+Copyright (c) 1994 Microsoft Corporation
+Copyright (c) 1993 Micro Computer Systems, Inc.
+
+Module Name:
+
+ net\svcdlls\nwsap\server\sdmd.h
+
+Abstract:
+
+ This include file is for programs that want to use the
+ SDMD functions. This has the public function declarations and
+ defines.
+
+ SDMD = Specialized Dynamic Memory Database
+
+Author:
+
+ Brian Walker (MCS) 06-30-1993
+
+Revision History:
+
+--*/
+
+
+INT
+SapInitSdmd(
+ VOID);
+
+VOID
+SapShutdownSdmd(
+ VOID);
+
+INT
+SdmdTimeoutCheck(
+ VOID);
+
+INT
+SdmdUpdateEntry(
+ IN PUCHAR ServerName,
+ IN USHORT ServerType,
+ IN PUCHAR ServerAddr,
+ IN USHORT ServerHopCount,
+ IN INT CardNumber,
+ IN PUCHAR SendersAddress,
+ IN BOOL WanFlag);
+
+INT
+SdmdGetNearestServerLan(
+ PUCHAR Ptr,
+ USHORT ServType,
+ INT Cardnum,
+ UCHAR Bcast);
+
+INT
+SdmdGetNearestServerWan(
+ PUCHAR Ptr,
+ USHORT ServType,
+ INT Cardnum,
+ UCHAR Bcast,
+ INT *NumFoundp);
+
+BOOL
+SdmdIsServerInTable(
+ PUCHAR ServerName,
+ USHORT ServerType);
diff --git a/private/net/svcdlls/nwsap/server/sdmdp.h b/private/net/svcdlls/nwsap/server/sdmdp.h
new file mode 100644
index 000000000..54e7d9517
--- /dev/null
+++ b/private/net/svcdlls/nwsap/server/sdmdp.h
@@ -0,0 +1,288 @@
+/*++
+
+Copyright (c) 1994 Microsoft Corporation
+Copyright (c) 1993 Micro Computer Systems, Inc.
+
+Module Name:
+
+ net\svcdlls\nwsap\server\sdmdp.h
+
+Abstract:
+
+ This is the private include file for the files that handle
+ the SDMD. All other files should just include sdmd.h
+
+Author:
+
+ Brian Walker (MCS) 06-30-1993
+
+Revision History:
+
+--*/
+
+#ifndef _NWSAP_SDMDP_
+#define _NWSAP_SDMDP_
+
+/** **/
+
+typedef INT SDMD_INDEX;
+#define SDMD_ENDOFLIST (SDMD_INDEX)-1
+
+/** **/
+
+typedef struct _SDMD_LIST_ENTRY {
+ SDMD_INDEX Flink;
+ SDMD_INDEX Blink;
+ INT ListIndex;
+} SDMD_LIST_ENTRY, *PSDMD_LIST_ENTRY;
+
+/** Indexes of the number of sort lists we have **/
+
+#define SAP_TYPELIST_INDEX 0 /* Sorted by server type */
+#define SAP_TIMELIST_INDEX 1 /* Sorted by timeout time */
+#define SAP_SUBLIST_INDEX 2 /* Sublist by type */
+#define SAP_HASHLIST_INDEX 3 /* Hashed by name list */
+
+#define SAP_NUM_LISTINDEX 4 /* Num lists there are */
+
+/**
+ This is the structure that is kept in the database. Each entry
+ is linked into one or more lists depending on the status of the
+ entry.
+**/
+
+typedef struct _SAP_RECORD {
+
+ /**
+ These are the fields that we get from the SAP packet.
+ They are the information about the service that has
+ been advertised.
+ **/
+
+ USHORT ServType;
+ UCHAR ServName[SAP_OBJNAME_LEN];
+ UCHAR ServAddress[SAP_ADDR_LEN];
+ USHORT HopCount;
+
+ /**
+ This field set to 0 means that the service is on another
+ machine somewhere.
+
+ Set to 1 means that the server is on this local machine
+ but is not being advertised by me (The sap agent).
+
+ Set to 2 means that I (The SAP Agent) am advertising this
+ server.
+ **/
+
+ UCHAR Internal;
+
+ /**
+ If we have changed this entry but have not told the rest
+ of the world about it yet.
+ **/
+
+ BOOL Changed;
+
+ /** **/
+
+ INT CardNumber; /* Card this entry was received from*/
+ UCHAR Advertiser[SAP_NODE_LEN];/* Machine advertising we got */
+ INT Timeout; /* Time this entry times out */
+
+ /** My Index number in the array **/
+
+ SDMD_INDEX Index;
+ SDMD_INDEX HeadIndex; /* Index of head of list */
+ INT HashIndex; /* Hash Entry we are a member of */
+
+ /** Mark if this is from a wan or not **/
+
+ BOOL FromWan; /* TRUE = Yes, FALSE = NO */
+
+ /** Links into our database **/
+
+ SDMD_LIST_ENTRY Links[SAP_NUM_LISTINDEX];
+
+} SAP_RECORD, *PSAP_RECORD;
+#define SAP_RECORD_SIZE sizeof(SAP_RECORD)
+
+/** Convert from ptr to index (or reverse) **/
+
+#if 0
+#define GETPTRFROMINDEX(i) (PSAP_RECORD)((i == SDMD_ENDOFLIST) ? NULL : (SdmdTablePtr+i))
+#else
+#define GETPTRFROMINDEX(i) (PSAP_RECORD)(((i == SDMD_ENDOFLIST)||((ULONG)i >= (ULONG)SapNumArrayEntries)) ? NULL : (SdmdTablePtr+i))
+#endif
+
+#define GETINDEXFROMPTR(p) (p->Index)
+
+
+/***********************************************************************
+ Locking Macros.
+
+ There are 2 types of locks for the SDMD table.
+
+ READERS - This is used if you only want to READ the table.
+ Multiple threads can have this lock at once.
+
+ WRITERS = This is used is you want to change something.
+ Only 1 thread can have this and all READERS are
+ blocked while this is held.
+
+ statp is a vaiable (INT) to set to 0 if we got the lock
+ OK or non-zero if we timed out getting the lock.
+ The "m" parameter is a message for debugging to tell where we
+ got called from.
+************************************************************************/
+
+#define SDMD_LTO (60*1000) /* 1 Minute = writers lock timeout */
+
+#define ACQUIRE_WRITERS_LOCK(statx,m) { \
+ IF_DEBUG(LOCKS) { \
+ SS_PRINT(("SAP: AWL: ENT: From %s\n", m)); \
+ } \
+ while (1) { \
+ EnterCriticalSection(&SdmdCriticalSection); \
+ if (SdmdLockCount != 0) { \
+ LeaveCriticalSection(&SdmdCriticalSection); \
+ statx = WaitForSingleObjectEx(SdmdSynchEvent,SDMD_LTO,TRUE); \
+ if (statx == WAIT_TIMEOUT) \
+ break; \
+ } \
+ else { \
+ statx = 0; \
+ break; \
+ } \
+ } \
+ IF_DEBUG(LOCKS) { \
+ SS_PRINT(("SAP: AWL: GOT: From %s\n", m)); \
+ } \
+ }
+
+#define RELEASE_WRITERS_LOCK(m) { \
+ IF_DEBUG(LOCKS) { \
+ SS_PRINT(("SAP: RWL: ENT: From %s\n", m)); \
+ } \
+ LeaveCriticalSection(&SdmdCriticalSection); \
+ }
+
+#define ACQUIRE_READERS_LOCK(m) { \
+ IF_DEBUG(LOCKS) { \
+ SS_PRINT(("SAP: ARL: ENT: From %s\n", m)); \
+ } \
+ EnterCriticalSection(&SdmdCriticalSection); \
+ SdmdLockCount++; \
+ if (SdmdLockCount == 1) \
+ ResetEvent(SdmdSynchEvent); \
+ LeaveCriticalSection(&SdmdCriticalSection); \
+ IF_DEBUG(LOCKS) { \
+ SS_PRINT(("SAP: ARL: GOT: From %s\n", m)); \
+ } \
+ }
+
+#define RELEASE_READERS_LOCK(m) { \
+ IF_DEBUG(LOCKS) { \
+ SS_PRINT(("SAP: RRL: ENT: From %s\n", m)); \
+ } \
+ EnterCriticalSection(&SdmdCriticalSection); \
+ IF_DEBUG(ERRORS) { \
+ if (SdmdLockCount == 0) { \
+ SS_PRINT(("NWSAP: RRL: Lock Count is 0\n")); \
+ } \
+ } \
+ SdmdLockCount--; \
+ if (SdmdLockCount == 0) \
+ SetEvent(SdmdSynchEvent); \
+ LeaveCriticalSection(&SdmdCriticalSection); \
+ IF_DEBUG(LOCKS) { \
+ SS_PRINT(("SAP: RRL: REL: From %s\n", m)); \
+ } \
+ }
+
+/** Variables used by SDMD routines **/
+
+extern PSAP_RECORD SdmdTablePtr;
+extern SDMD_LIST_ENTRY SdmdLists[SAP_NUM_LISTINDEX];
+extern INT SdmdCurrentTime;
+extern PSDMD_LIST_ENTRY SdmdNameHashTable;
+
+
+/*******************************************************************
+ Function Prototypes
+********************************************************************/
+
+/** Routines from SDMD.c **/
+
+VOID
+SdmdFreeEntry(
+ PSAP_RECORD Entry);
+
+INT
+SdmdCalcHash(
+ PUCHAR ServerName);
+
+/** Routines from SDMDSUPP.c **/
+
+VOID
+SdmdInitializeListHead(
+ INT ListIndex);
+
+PSAP_RECORD
+SdmdRemoveHeadList(
+ INT ListIndex);
+
+PSAP_RECORD
+SdmdRemoveTailList(
+ INT ListIndex);
+
+VOID
+SdmdRemoveEntryList(
+ INT ListIndex,
+ PSAP_RECORD Entry);
+
+VOID
+SdmdInsertTailList(
+ INT ListIndex,
+ PSAP_RECORD Entry);
+
+VOID
+SdmdInsertHeadList(
+ INT ListIndex,
+ PSAP_RECORD Entry);
+
+VOID
+SdmdInsertList(
+ INT ListIndex,
+ PSAP_RECORD Entry,
+ INT AfterIndex);
+
+VOID
+SdmdInsertTailSubList(
+ PSAP_RECORD HeadEntry,
+ INT ListIndex,
+ PSAP_RECORD Entry);
+
+PSAP_RECORD
+SdmdRemoveTailSubEntryList(
+ PSAP_RECORD HeadEntry,
+ INT ListIndex);
+
+VOID
+SdmdRemoveSubEntryList(
+ PSAP_RECORD HeadEntry,
+ INT ListIndex,
+ PSAP_RECORD Entry);
+
+VOID
+SdmdRemoveEntryFromHash(
+ PSDMD_LIST_ENTRY ListHead,
+ PSAP_RECORD Entry);
+
+VOID
+SdmdInsertEntryInHash(
+ PSDMD_LIST_ENTRY ListHead,
+ PSAP_RECORD Entry,
+ INT AfterIndex);
+
+#endif
diff --git a/private/net/svcdlls/nwsap/server/sdmdsupp.c b/private/net/svcdlls/nwsap/server/sdmdsupp.c
new file mode 100644
index 000000000..f68641a79
--- /dev/null
+++ b/private/net/svcdlls/nwsap/server/sdmdsupp.c
@@ -0,0 +1,704 @@
+/*++
+
+Copyright (c) 1994 Microsoft Corporation
+Copyright (c) 1993 Micro Computer Systems, Inc.
+
+Module Name:
+
+ net\svcdlls\nwsap\server\sdmdsupp.c
+
+Abstract:
+
+ This file contains support routines for SDMD.c
+
+ SDMD = Specialized Dynamic Memory Database
+
+Author:
+
+ Brian Walker (MCS) 06-15-1993
+
+Revision History:
+
+--*/
+
+#include "precomp.h"
+#pragma hdrstop
+
+
+/*++
+*******************************************************************
+ S d m d I n i t i a l i z e L i s t H e a d
+
+Routine Description:
+
+ This routine initializes a list structure that
+ is used to keep track of a list with. It sets the
+ fwd and back links to point at nothing.
+
+Arguments:
+
+ ListIndex = Index of the list to initialize
+
+Return Value:
+
+ Nothing
+*******************************************************************
+--*/
+VOID
+SdmdInitializeListHead(
+ INT ListIndex)
+{
+ SdmdLists[ListIndex].Flink = SDMD_ENDOFLIST;
+ SdmdLists[ListIndex].Blink = SDMD_ENDOFLIST;
+ SdmdLists[ListIndex].ListIndex = ListIndex;
+ return;
+}
+
+
+/*++
+*******************************************************************
+ S d m d R e m o v e H e a d L i s t
+
+Routine Description:
+
+ This routine removes a record from the head of a list.
+
+Arguments:
+
+ ListIndex = List number to get entry from
+
+Return Value:
+
+ Ptr to SAP_RECORD structure of entry from head
+ (NULL = List is empty)
+*******************************************************************
+--*/
+
+PSAP_RECORD
+SdmdRemoveHeadList(
+ INT ListIndex)
+{
+ PSAP_RECORD Entry;
+ PSAP_RECORD NextEntry;
+
+ /** Get ptr to the entry - if none - error **/
+
+ Entry = GETPTRFROMINDEX(SdmdLists[ListIndex].Flink);
+ if (Entry == NULL)
+ return NULL;
+
+ /** Take this entry out of the list **/
+
+ NextEntry = GETPTRFROMINDEX(Entry->Links[ListIndex].Flink);
+ if (NextEntry) {
+ NextEntry->Links[ListIndex].Blink = SDMD_ENDOFLIST;
+ SdmdLists[ListIndex].Flink = NextEntry->Index;
+ }
+ else {
+ SdmdLists[ListIndex].Flink = SDMD_ENDOFLIST;
+ SdmdLists[ListIndex].Blink = SDMD_ENDOFLIST;
+ }
+
+ /** Return the pointer **/
+
+ return Entry;
+}
+
+
+/*++
+*******************************************************************
+ S d m d R e m o v e T a i l L i s t
+
+Routine Description:
+
+ This routine removes a record from the tail of a list.
+
+Arguments:
+
+ ListIndex = List number to get entry from
+
+Return Value:
+
+ Ptr to SAP_RECORD structure of entry from tail
+ (NULL = List is empty)
+*******************************************************************
+--*/
+
+#if 0 /* Current unused so we save the space */
+
+PSAP_RECORD
+SdmdRemoveTailList(
+ INT ListIndex)
+{
+ PSAP_RECORD Entry;
+ PSAP_RECORD BackEntry;
+
+ /** Get ptr to the entry - if none - error **/
+
+ Entry = GETPTRFROMINDEX(SdmdLists[ListIndex].Blink);
+ if (Entry == NULL)
+ return NULL;
+
+ /** Take this entry out of the list **/
+
+ BackEntry = GETPTRFROMINDEX(Entry->Links[ListIndex].Blink);
+ if (BackEntry) {
+ BackEntry->Links[ListIndex].Flink = SDMD_ENDOFLIST;
+ SdmdLists[ListIndex].Blink = BackEntry->Index;
+ }
+ else {
+ SdmdLists[ListIndex].Flink = SDMD_ENDOFLIST;
+ SdmdLists[ListIndex].Blink = SDMD_ENDOFLIST;
+ }
+
+ /** Return the pointer **/
+
+ return Entry;
+}
+
+#endif
+
+
+/*++
+*******************************************************************
+ S d m d R e m o v e E n t r y L i s t
+
+Routine Description:
+
+ This routine removes a specific entry from the given
+ list.
+
+Arguments:
+
+ ListIndex = Index of the list to remove it from
+ Entry = Ptr to the entry to remove from the list.
+
+Return Value:
+
+ Nothing
+*******************************************************************
+--*/
+
+VOID
+SdmdRemoveEntryList(
+ INT ListIndex,
+ PSAP_RECORD Entry)
+{
+ PSAP_RECORD BackEntry;
+ PSAP_RECORD NextEntry;
+
+ /** Handle the forward link **/
+
+ NextEntry = GETPTRFROMINDEX(Entry->Links[ListIndex].Flink);
+ if (NextEntry)
+ NextEntry->Links[ListIndex].Blink = Entry->Links[ListIndex].Blink;
+ else
+ SdmdLists[ListIndex].Blink = Entry->Links[ListIndex].Blink;
+
+ /** Handle the back link **/
+
+ BackEntry = GETPTRFROMINDEX(Entry->Links[ListIndex].Blink);
+ if (BackEntry)
+ BackEntry->Links[ListIndex].Flink = Entry->Links[ListIndex].Flink;
+ else
+ SdmdLists[ListIndex].Flink = Entry->Links[ListIndex].Flink;
+
+ /** All Done **/
+
+ return;
+}
+
+
+/*++
+*******************************************************************
+ S d m d I n s e r t T a i l L i s t
+
+Routine Description:
+
+ Add an entry to the end of a list.
+
+Arguments:
+
+ ListIndex = Index of the list to add this to.
+ Entry = Ptr to the entry to add
+
+Return Value:
+
+ Nothing
+*******************************************************************
+--*/
+
+VOID
+SdmdInsertTailList(
+ INT ListIndex,
+ PSAP_RECORD Entry)
+{
+ PSAP_RECORD TailEntry;
+
+ /** Get ptr to last record in list **/
+
+ TailEntry = GETPTRFROMINDEX(SdmdLists[ListIndex].Blink);
+
+ /**
+ If there is one - then point its forward link at my
+ new entry. If no entry - then point the list
+ head forward entry at my new entry.
+ **/
+
+ if (TailEntry)
+ TailEntry->Links[ListIndex].Flink = Entry->Index;
+ else
+ SdmdLists[ListIndex].Flink = Entry->Index;
+
+ /** Point new entries BACK LINK to current list tail **/
+
+ Entry->Links[ListIndex].Blink = SdmdLists[ListIndex].Blink;
+
+ /** Make my entry the new tail **/
+
+ SdmdLists[ListIndex].Blink = Entry->Index;
+
+ /** Make new entry have no forward link **/
+
+ Entry->Links[ListIndex].Flink = SDMD_ENDOFLIST;
+
+ /** All Done **/
+
+ return;
+}
+
+
+/*++
+*******************************************************************
+ S d m d I n s e r t H e a d L i s t
+
+Routine Description:
+
+ Add an entry to the head of a list.
+
+Arguments:
+
+ ListIndex = Index of the list to add this to.
+ Entry = Ptr to the entry to add
+
+Return Value:
+
+ Nothing
+*******************************************************************
+--*/
+
+VOID
+SdmdInsertHeadList(
+ INT ListIndex,
+ PSAP_RECORD Entry)
+{
+ PSAP_RECORD HeadEntry;
+
+ /** Get ptr to first record in list **/
+
+ HeadEntry = GETPTRFROMINDEX(SdmdLists[ListIndex].Flink);
+
+ /**
+ If there is one - then point its back link at my
+ new entry. If no entry - then point the list
+ head back entry at my new entry.
+ **/
+
+ if (HeadEntry)
+ HeadEntry->Links[ListIndex].Blink = Entry->Index;
+ else
+ SdmdLists[ListIndex].Blink = Entry->Index;
+
+ /** Point new entries Front LINK to current list head **/
+
+ Entry->Links[ListIndex].Flink = SdmdLists[ListIndex].Flink;
+
+ /** Make my entry the new head **/
+
+ SdmdLists[ListIndex].Flink = Entry->Index;
+
+ /** Make new entry have no back link **/
+
+ Entry->Links[ListIndex].Blink = SDMD_ENDOFLIST;
+
+ /** All Done **/
+
+ return;
+}
+
+
+/*++
+*******************************************************************
+ S d m d I n s e r t L i s t
+
+Routine Description:
+
+ This routine inserts an entry into a list at some point
+ in the middle of a list.
+
+Arguments:
+
+ ListIndex = Index of the list to add this to.
+ Entry = Ptr to the entry to add
+ AfterIndex = Index to put new entry after. -1 = At head
+
+Return Value:
+
+ Nothing
+*******************************************************************
+--*/
+
+VOID
+SdmdInsertList(
+ INT ListIndex,
+ PSAP_RECORD Entry,
+ INT AfterIndex)
+{
+ PSAP_RECORD NextEntry;
+ PSAP_RECORD BackEntry;
+
+ /** If head - do it **/
+
+ if (AfterIndex == SDMD_ENDOFLIST) {
+ SdmdInsertHeadList(ListIndex, Entry);
+ }
+ else {
+
+ /** Get ptr to entry that are inserting after **/
+
+ BackEntry = GETPTRFROMINDEX(AfterIndex);
+
+ /** Point new entry back at that entry **/
+
+ Entry->Links[ListIndex].Blink = AfterIndex;
+
+ /** Make new entry fwd ptr be back entry's fwd ptr **/
+
+ Entry->Links[ListIndex].Flink = BackEntry->Links[ListIndex].Flink;
+
+ /** Make back entry point fwd at new entry **/
+
+ BackEntry->Links[ListIndex].Flink = Entry->Index;
+
+ /** If we were at tail - make Entry the new tail **/
+
+ if (Entry->Links[ListIndex].Flink == SDMD_ENDOFLIST)
+ SdmdLists[ListIndex].Blink = Entry->Index;
+ else {
+
+ /** Make the entry after new one point back at us **/
+
+ NextEntry = GETPTRFROMINDEX(Entry->Links[ListIndex].Flink);
+ NextEntry->Links[ListIndex].Blink = Entry->Index;
+ }
+ }
+
+ /** All Done **/
+
+ return;
+}
+
+
+/*++
+*******************************************************************
+ S d m d I n s e r t T a i l S u b L i s t
+
+Routine Description:
+
+ Insert an entry at the tail of a sublist.
+
+Arguments:
+
+ HeadEntry = Ptr to the entry that is the head of the sub list
+ ListIndex = List index that the list uses
+ Entry = Ptr to entry to insert
+
+Return Value:
+
+ None.
+*******************************************************************
+--*/
+
+VOID
+SdmdInsertTailSubList(
+ PSAP_RECORD HeadEntry,
+ INT ListIndex,
+ PSAP_RECORD Entry)
+{
+ PSAP_RECORD TailEntry;
+
+ /** Get ptr to last record in list **/
+
+ TailEntry = GETPTRFROMINDEX(HeadEntry->Links[ListIndex].Blink);
+
+ /**
+ If there is one - then point its forward link at my
+ new entry. If no entry - then point the list
+ head forward entry at my new entry.
+ **/
+
+ if (TailEntry)
+ TailEntry->Links[ListIndex].Flink = Entry->Index;
+ else
+ HeadEntry->Links[ListIndex].Flink = Entry->Index;
+
+ /** Point new entries BACK LINK to current list tail **/
+
+ Entry->Links[ListIndex].Blink = HeadEntry->Links[ListIndex].Blink;
+
+ /** Make my entry the new tail **/
+
+ HeadEntry->Links[ListIndex].Blink = Entry->Index;
+
+ /** Make new entry have no forward link **/
+
+ Entry->Links[ListIndex].Flink = SDMD_ENDOFLIST;
+ Entry->HeadIndex = HeadEntry->Index;
+
+ /** All Done **/
+
+ return;
+}
+
+
+/*++
+*******************************************************************
+ S d m d R e m o v e T a i l S u b E n t r y L i s t
+
+Routine Description:
+
+ Remove an entry from the tail of a sublist.
+
+Arguments:
+
+ HeadEntry = Ptr to the head entry that has the list
+ ListIndex = The List index to get the entry from
+
+Return Value:
+
+ Ptr to the entry removed.
+ (NULL = Empty List).
+*******************************************************************
+--*/
+
+PSAP_RECORD
+SdmdRemoveTailSubEntryList(
+ PSAP_RECORD HeadEntry,
+ INT ListIndex)
+{
+ PSAP_RECORD Entry;
+ PSAP_RECORD BackEntry;
+
+ /** Get ptr to the entry - if none - error **/
+
+ Entry = GETPTRFROMINDEX(HeadEntry->Links[ListIndex].Blink);
+ if (Entry == NULL)
+ return NULL;
+
+ /** Take this entry out of the list **/
+
+ BackEntry = GETPTRFROMINDEX(Entry->Links[ListIndex].Blink);
+ if (BackEntry) {
+ BackEntry->Links[ListIndex].Flink = SDMD_ENDOFLIST;
+ HeadEntry->Links[ListIndex].Blink = BackEntry->Index;
+ }
+ else {
+ HeadEntry->Links[ListIndex].Flink = SDMD_ENDOFLIST;
+ HeadEntry->Links[ListIndex].Blink = SDMD_ENDOFLIST;
+ }
+
+ /** Return the pointer **/
+
+ return Entry;
+}
+
+
+/*++
+*******************************************************************
+ S d m d R e m o v e S u b E n t r y L i s t
+
+Routine Description:
+
+ This routine removes a specific entry from the given
+ list.
+
+Arguments:
+
+ ListIndex = Index of the list to remove it from
+ Entry = Ptr to the entry to remove from the list.
+
+Return Value:
+
+ Nothing
+*******************************************************************
+--*/
+
+VOID
+SdmdRemoveSubEntryList(
+ PSAP_RECORD HeadEntry,
+ INT ListIndex,
+ PSAP_RECORD Entry)
+{
+ PSAP_RECORD BackEntry;
+ PSAP_RECORD NextEntry;
+
+ /** Handle the forward link **/
+
+ NextEntry = GETPTRFROMINDEX(Entry->Links[ListIndex].Flink);
+ if (NextEntry)
+ NextEntry->Links[ListIndex].Blink = Entry->Links[ListIndex].Blink;
+ else
+ HeadEntry->Links[ListIndex].Blink = Entry->Links[ListIndex].Blink;
+
+ /** Handle the back link **/
+
+ BackEntry = GETPTRFROMINDEX(Entry->Links[ListIndex].Blink);
+ if (BackEntry)
+ BackEntry->Links[ListIndex].Flink = Entry->Links[ListIndex].Flink;
+ else
+ HeadEntry->Links[ListIndex].Flink = Entry->Links[ListIndex].Flink;
+
+ /** All Done **/
+
+ return;
+}
+
+
+/*++
+*******************************************************************
+ S d m d R e m o v e E n t r y F r o m H a s h
+
+Routine Description:
+
+ This routine removes a specific entry from the given
+ hash entry.
+
+Arguments:
+
+ ListHead = Ptr to the list head to remove from
+ Entry = Ptr to the entry to remove from the list.
+
+Return Value:
+
+ Nothing
+*******************************************************************
+--*/
+
+VOID
+SdmdRemoveEntryFromHash(
+ PSDMD_LIST_ENTRY ListHead,
+ PSAP_RECORD Entry)
+{
+ PSAP_RECORD BackEntry;
+ PSAP_RECORD NextEntry;
+
+ /** Handle the forward link **/
+
+ NextEntry = GETPTRFROMINDEX(Entry->Links[SAP_HASHLIST_INDEX].Flink);
+ if (NextEntry)
+ NextEntry->Links[SAP_HASHLIST_INDEX].Blink = Entry->Links[SAP_HASHLIST_INDEX].Blink;
+ else
+ ListHead->Blink = Entry->Links[SAP_HASHLIST_INDEX].Blink;
+
+ /** Handle the back link **/
+
+ BackEntry = GETPTRFROMINDEX(Entry->Links[SAP_HASHLIST_INDEX].Blink);
+ if (BackEntry)
+ BackEntry->Links[SAP_HASHLIST_INDEX].Flink = Entry->Links[SAP_HASHLIST_INDEX].Flink;
+ else
+ ListHead->Flink = Entry->Links[SAP_HASHLIST_INDEX].Flink;
+
+ /** All Done **/
+
+ return;
+}
+
+
+/*++
+*******************************************************************
+ S d m d I n s e r t E n t r y I n H a s h
+
+Routine Description:
+
+ This routine inserts an entry into a hash list.
+
+Arguments:
+
+ ListHead = Ptr to list head to insert this entry in
+ Entry = Ptr to the entry to add
+ AfterIndex = Index to put new entry after. -1 = At head
+
+Return Value:
+
+ Nothing
+*******************************************************************
+--*/
+
+VOID
+SdmdInsertEntryInHash(
+ PSDMD_LIST_ENTRY ListHead,
+ PSAP_RECORD Entry,
+ INT AfterIndex)
+{
+ PSAP_RECORD NextEntry;
+ PSAP_RECORD BackEntry;
+
+ /** Set the hash index for this entry **/
+
+ Entry->HashIndex = ListHead->ListIndex;
+
+ /** Get ptr to entry that are inserting after **/
+
+ BackEntry = GETPTRFROMINDEX(AfterIndex);
+
+ /** If NULL - We are insertting at the head **/
+
+ if (BackEntry == NULL) {
+
+ /** Give this entry index of the old head **/
+
+ Entry->Links[SAP_HASHLIST_INDEX].Blink = SDMD_ENDOFLIST;
+ Entry->Links[SAP_HASHLIST_INDEX].Flink = ListHead->Flink;
+
+ /** Make this entry the head of the list **/
+
+ ListHead->Flink = Entry->Index;
+ }
+ else {
+
+ /** Point new entry back at that entry **/
+
+ Entry->Links[SAP_HASHLIST_INDEX].Blink = AfterIndex;
+
+ /** Make new entry fwd ptr be back entry's fwd ptr **/
+
+ Entry->Links[SAP_HASHLIST_INDEX].Flink = BackEntry->Links[SAP_HASHLIST_INDEX].Flink;
+
+ /** Make back entry point fwd at new entry **/
+
+ BackEntry->Links[SAP_HASHLIST_INDEX].Flink = Entry->Index;
+ }
+
+ /** If we were at tail - make Entry the new tail **/
+
+ NextEntry = GETPTRFROMINDEX(Entry->Links[SAP_HASHLIST_INDEX].Flink);
+
+ if (NextEntry == NULL) {
+
+ /** Make this the new tail **/
+
+ ListHead->Blink = Entry->Index;
+ }
+ else {
+
+ /** Make the entry after new one point back at us **/
+
+ NextEntry->Links[SAP_HASHLIST_INDEX].Blink = Entry->Index;
+ }
+
+ /** All Done **/
+
+ return;
+}
+
+
diff --git a/private/net/svcdlls/nwsap/server/sources b/private/net/svcdlls/nwsap/server/sources
new file mode 100644
index 000000000..d5ee7038d
--- /dev/null
+++ b/private/net/svcdlls/nwsap/server/sources
@@ -0,0 +1,48 @@
+!IF 0
+
+Copyright (c) 1993 Micro Computer Systems, Inc.
+
+!ENDIF
+
+MAJORCOMP=net
+MINORCOMP=nwsap
+
+TARGETNAME=nwsap
+TARGETPATH=$(BASEDIR)\public\sdk\lib
+TARGETTYPE=DYNLINK
+
+USE_CRTDLL = 1
+
+C_DEFINES=$(C_DEFINES) -DUSE_DEBUGGER
+
+INCLUDES=.;..\..\..\inc;..\..\..\..\inc
+
+UNICODE=1
+NET_C_DEFINES=-DUNICODE
+
+SOURCES= \
+ advapi.c \
+ bindlib.c \
+ dump.c \
+ filter.c \
+ globals.c \
+ network.c \
+ nwsap.c \
+ registry.c \
+ sapdebug.c \
+ saplpc.c \
+ sdmd.c \
+ sdmdsupp.c \
+ sssubs.c \
+ svcctrl.c \
+ wancheck.c \
+ nwsap.rc
+
+TARGETLIBS=$(BASEDIR)\public\sdk\lib\*\$(TARGETNAME).lib\
+ $(BASEDIR)\public\sdk\lib\*\kernel32.lib \
+ $(BASEDIR)\public\sdk\lib\*\advapi32.lib \
+ $(BASEDIR)\public\sdk\lib\*\wsock32.lib
+
+PRECOMPILED_INCLUDE=precomp.h
+PRECOMPILED_PCH=precomp.pch
+PRECOMPILED_OBJ=precomp.obj
diff --git a/private/net/svcdlls/nwsap/server/ssdebug.h b/private/net/svcdlls/nwsap/server/ssdebug.h
new file mode 100644
index 000000000..0cbc51fe3
--- /dev/null
+++ b/private/net/svcdlls/nwsap/server/ssdebug.h
@@ -0,0 +1,101 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ net\svcdlls\nwsap\server\ssdebug.h
+
+Abstract:
+
+ Header file for various server service debugging aids.
+
+Author:
+
+ Brian Walker (MCS) 13-Jun-1993
+
+Revision History:
+
+--*/
+
+#ifndef _SSDEBUG_
+#define _SSDEBUG_
+
+
+#define DEBUG_INITIALIZATION 0x00000001
+#define DEBUG_INITIALIZATION_ERRORS 0x00000002
+#define DEBUG_TERMINATION 0x00000004
+#define DEBUG_TERMINATION_ERRORS 0x00000008
+
+#define DEBUG_LOCKS 0x00000010
+#define DEBUG_ANNOUNCE 0x00000020
+#define DEBUG_CONTROL_MESSAGES 0x00000040
+#define DEBUG_REGISTRY 0x00000080
+
+#define DEBUG_THREADTRACE 0x00000100
+#define DEBUG_SDMD 0x00000200
+#define DEBUG_MEMALLOC 0x00000400
+#define DEBUG_ERRORS 0x00000800
+
+#define DEBUG_LPC 0x00001000
+#define DEBUG_WAN 0x00002000
+#define DEBUG_INITIALIZATION_BREAKPOINT 0x00004000
+#define DEBUG_TERMINATION_BREAKPOINT 0x00008000
+
+#define DEBUG_17 0x00010000
+#define DEBUG_18 0x00020000
+#define DEBUG_NOCARD 0x00040000
+#define DEBUG_ENABLEDUMP 0x00080000
+
+extern DWORD SsDebug;
+#define IF_DEBUG(flag) if (SsDebug & (DEBUG_ ## flag))
+
+/** Default debug stuff **/
+
+#if DBG
+
+#define DEBUG_DEFAULT (DEBUG_INITIALIZATION_ERRORS | \
+ DEBUG_TERMINATION_ERRORS | \
+ DEBUG_ERRORS | \
+ DEBUG_ENABLEDUMP)
+
+#else
+
+#define DEBUG_DEFAULT (DEBUG_ENABLEDUMP)
+
+#endif
+
+#if DBG
+
+#ifdef USE_DEBUGGER
+#define SS_PRINT(args) DbgPrint args
+#else
+
+extern VOID SsPrintf(char *Format, ...);
+#define SS_PRINT(args) SsPrintf args
+
+#endif
+
+
+#ifdef USE_DEBUGGER
+#define SS_ASSERT(exp) ASSERT(exp)
+#else
+VOID
+SsAssert(
+ IN PVOID FailedAssertion,
+ IN PVOID FileName,
+ IN ULONG LineNumber
+ );
+#define SS_ASSERT(exp) if (!(exp)) SsAssert( #exp, __FILE__, __LINE__ )
+#endif
+
+#else /* DBG */
+
+#define SS_PRINT(args)
+#define SS_ASSERT(exp)
+
+#endif /* DBG */
+
+#endif // ndef _SSDEBUG_
+
+
diff --git a/private/net/svcdlls/nwsap/server/sssubs.c b/private/net/svcdlls/nwsap/server/sssubs.c
new file mode 100644
index 000000000..60184e502
--- /dev/null
+++ b/private/net/svcdlls/nwsap/server/sssubs.c
@@ -0,0 +1,203 @@
+/*++
+
+Copyright (c) 1994 Microsoft Corporation
+Copyright (c) 1993 Micro Computer Systems, Inc.
+
+Module Name:
+
+ net\svcdlls\nwsap\server\sssubs.c
+
+Abstract:
+
+ This module contains support routines for the NT server service.
+
+Author:
+
+ David Treadwell (davidtr) 10-Jan-1991
+
+Revision History:
+
+--*/
+
+#include "precomp.h"
+#pragma hdrstop
+
+#include <lmsname.h> // for SERVICE_NWSAP
+
+
+/*++
+*******************************************************************
+ S s A s s e r t
+
+Routine Description:
+
+Arguments:
+
+Return Value:
+
+ None.
+
+*******************************************************************
+--*/
+
+#if DBG
+
+VOID
+SsAssert(
+ IN PVOID FailedAssertion,
+ IN PVOID FileName,
+ IN ULONG LineNumber)
+{
+ BOOL Ok;
+ CHAR Choice[16];
+ DWORD Bytes;
+ DWORD Error;
+
+ /** **/
+
+ SS_PRINT(("\nNWSAP: Assertion failed: %s\n at line %ld of %s\n",
+ FailedAssertion, LineNumber, FileName));
+
+ do {
+ SS_PRINT(("Break or Ignore [bi]? "));
+ Bytes = sizeof(Choice);
+
+ Ok = ReadFile(
+ GetStdHandle(STD_INPUT_HANDLE),
+ &Choice,
+ Bytes,
+ &Bytes,
+ NULL);
+
+ if (Ok) {
+ if (toupper(Choice[0]) == 'I')
+ break;
+
+ if (toupper(Choice[0]) == 'B')
+ DbgUserBreakPoint();
+ }
+ else
+ Error = GetLastError();
+ } while(TRUE);
+
+ return;
+}
+#endif
+
+
+/*++
+*******************************************************************
+ S s L o g E v e n t
+
+Routine Description:
+
+Arguments:
+
+Return Value:
+
+ None.
+
+*******************************************************************
+--*/
+
+VOID
+SsLogEvent(
+ IN DWORD MessageId,
+ IN DWORD NumberOfSubStrings,
+ IN LPWSTR *SubStrings,
+ IN DWORD ErrorCode)
+{
+ HANDLE LogHandle;
+ DWORD DataSize = 0;
+ LPVOID RawData = NULL;
+
+ /** Open the error log **/
+
+ LogHandle = RegisterEventSource(
+ NULL,
+ SERVICE_NWSAP);
+
+ if (LogHandle == NULL) {
+ SS_PRINT(("NWSAP: RegisterEventSource failed: %lu\n", GetLastError()));
+ return;
+ }
+
+ /** If an error code was specified - set it **/
+
+ if (ErrorCode != 0) {
+ DataSize = sizeof(ErrorCode);
+ RawData = (LPVOID)&ErrorCode;
+ }
+
+ /** Log the error **/
+
+ if (!ReportEventW(
+ LogHandle,
+ EVENTLOG_ERROR_TYPE,
+ 0, /* event category */
+ MessageId,
+ NULL, /* user SID */
+ (WORD)NumberOfSubStrings,
+ DataSize,
+ SubStrings,
+ RawData)) {
+
+ SS_PRINT(("NWSAP: ReportEvent failed: %lu\n", GetLastError()));
+ }
+
+ if (!DeregisterEventSource(LogHandle)) {
+ SS_PRINT(("NWSAP: DeregisterEventSource failed: %lu\n",
+ GetLastError()));
+ }
+
+ /** All Done **/
+
+ return;
+}
+
+
+/*++
+*******************************************************************
+ S s P r i n t f
+
+Routine Description:
+
+ Do a printf to the debug console
+
+Arguments:
+
+Return Value:
+
+ None.
+
+*******************************************************************
+--*/
+
+#if DBG
+VOID
+SsPrintf (
+ char *Format,
+ ...)
+
+{
+ va_list Arglist;
+ char OutputBuffer[1024];
+ ULONG Length;
+
+ /** Sprintf the data into a buffer **/
+
+ va_start(Arglist, Format);
+ vsprintf(OutputBuffer, Format, Arglist);
+ va_end(Arglist);
+
+ /** Write the buffer to stdout **/
+
+ Length = strlen(OutputBuffer);
+ WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), (LPVOID)OutputBuffer, Length, &Length, NULL);
+
+ /** All Done **/
+
+ return;
+}
+#endif
+
diff --git a/private/net/svcdlls/nwsap/server/svcctrl.c b/private/net/svcdlls/nwsap/server/svcctrl.c
new file mode 100644
index 000000000..279ac5ed5
--- /dev/null
+++ b/private/net/svcdlls/nwsap/server/svcctrl.c
@@ -0,0 +1,532 @@
+/*++
+
+Copyright (c) 1994 Microsoft Corporation
+Copyright (c) 1993 Micro Computer Systems, Inc.
+
+Module Name:
+
+ net\svcdlls\nwsap\server\svcctrl.c
+
+Abstract:
+
+ This is the service controller interface for the NT SAP Agent
+
+Author:
+
+ Brian Walker (MCS) 06-15-1993
+
+Revision History:
+
+--*/
+
+#include "precomp.h"
+#pragma hdrstop
+
+#include <tstr.h>
+#include <wincon.h>
+#include <winsvc.h>
+
+#include <lmsname.h>
+#include <svcs.h>
+
+/** Globals we use **/
+
+DWORD SsDebug = DEBUG_DEFAULT;
+BOOL SsInitialized;
+
+SERVICE_STATUS SsServiceStatus;
+SERVICE_STATUS_HANDLE SsServiceStatusHandle;
+SVCS_GLOBAL_DATA SvcsGlobalData;
+
+/** Internal Function Prototypes **/
+
+VOID
+AnnounceServiceStatus(
+ VOID);
+
+VOID
+ControlResponse(
+ DWORD opCode);
+
+
+/*++
+*******************************************************************
+ S e r v i c e E n t r y
+
+Routine Description:
+
+ This is the main routine for the SAP Agent service. The
+ containing process will call this routine when we are
+ supposed to start up.
+
+Arguments:
+
+ argc = Number of arguments
+ argv = Ptr to array of arguments
+ pGlobalData =
+
+Return Value:
+
+ None.
+*******************************************************************
+--*/
+
+VOID
+SVCS_ENTRY_POINT( /* ServiceEntry */
+ IN DWORD argc,
+ IN LPWSTR argv[],
+ IN PSVCS_GLOBAL_DATA pGlobalData,
+ IN HANDLE SvcRefHandle)
+{
+ DWORD error;
+ DWORD terminationError;
+ BOOLEAN rpcServerStarted = FALSE;
+
+ UNREFERENCED_PARAMETER(SvcRefHandle);
+
+ /** Save the service global data for future use. **/
+
+ SvcsGlobalData = *pGlobalData;
+
+ /** Skip the service name in the argument list **/
+
+ if (argc > 0) {
+ argc--;
+ if (argc > 0) {
+ argv = &(argv[1]);
+ }
+ }
+
+ /**
+ Set up for debugging--the first command line argument may be
+ "/debug:X" where SsDebug gets set to X. (X is a hex number).
+ **/
+
+#if DBG
+ if (argc > 0 && STRNICMP(TEXT("/debug:"), (LPWSTR)argv[0], 7) == 0) {
+ UNICODE_STRING ustr;
+
+ /** Get the number from the command line **/
+
+ RtlInitUnicodeString(&ustr, (PWSTR)argv[0] + 7);
+ RtlUnicodeStringToInteger(&ustr, 16, &SsDebug);
+
+ /** Skip to the next cmd line argument **/
+
+ argc--;
+ if (argc > 0) {
+ argv = &(argv[1]);
+ }
+ }
+#endif
+
+#if DBG
+
+#ifndef USE_DEBUGGER
+
+ if (SsDebug != 0) {
+ CONSOLE_SCREEN_BUFFER_INFO csbi;
+ COORD coord;
+
+ (VOID)AllocConsole();
+ (VOID)GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi);
+ coord.X = (SHORT) (csbi.srWindow.Right - csbi.srWindow.Left + 1);
+ coord.Y = (SHORT)((csbi.srWindow.Bottom - csbi.srWindow.Top + 1) * 20);
+ (VOID)SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE), coord);
+ }
+#endif
+#endif /* DBG */
+
+ IF_DEBUG(INITIALIZATION) {
+ SS_PRINT(("NWSAP_main: server service starting.\n"));
+ }
+
+ /**
+ Initialize all the status fields so that subsequent calls to
+ SetServiceStatus need to only update fields that changed.
+ **/
+
+ SsServiceStatus.dwServiceType = SERVICE_WIN32;
+ SsServiceStatus.dwCurrentState = SERVICE_START_PENDING;
+#if DBG
+ SsServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP |
+ SERVICE_ACCEPT_SHUTDOWN |
+ SERVICE_ACCEPT_PAUSE_CONTINUE;
+#else
+ SsServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP |
+ SERVICE_ACCEPT_SHUTDOWN;
+#endif
+ SsServiceStatus.dwCheckPoint = 1;
+ SsServiceStatus.dwWaitHint = 30 * 1000; /* 30 seconds */
+ SsServiceStatus.dwWin32ExitCode = NO_ERROR;
+ SsServiceStatus.dwServiceSpecificExitCode = 0;
+
+ /**
+ Initialize server to receive service requests by registering the
+ control handler.
+ **/
+
+ SsServiceStatusHandle = RegisterServiceCtrlHandler(
+ SERVICE_NWSAP,
+ ControlResponse);
+
+ if (SsServiceStatusHandle == 0) {
+
+ error = GetLastError();
+
+ IF_DEBUG(INITIALIZATION_ERRORS) {
+ SS_PRINT(("NWSAP_main: RegisterServiceCtrlHandler failed: %ld", error));
+ }
+ goto exit;
+ }
+
+ IF_DEBUG(INITIALIZATION) {
+ SS_PRINT(("NWSAP_main: Control handler registered. Handle = 0x%lx\n", SsServiceStatusHandle));
+ }
+
+ /** Go initialize myself. **/
+
+ SapError = 0;
+ SapEventId = 0;
+
+ SsInitialized = TRUE;
+ error = (DWORD)SapInit();
+
+ /**
+ If we got an error - get need to log it and clean up
+ so we can exit with the error.
+ **/
+
+ if (error != NO_ERROR) {
+ SsInitialized = FALSE;
+
+ /** Log the error **/
+
+ if (SapEventId) {
+
+ /** Send it to the event log **/
+
+ SsLogEvent(
+ SapEventId,
+ 0,
+ NULL,
+ SapError);
+ }
+
+ /** Go clean up **/
+
+ goto exit;
+ }
+
+ /** Announce that we have successfully started. **/
+
+ SsServiceStatus.dwCurrentState = SERVICE_RUNNING;
+ SsServiceStatus.dwCheckPoint = 0;
+ SsServiceStatus.dwWaitHint = 0;
+
+ AnnounceServiceStatus();
+
+ IF_DEBUG(INITIALIZATION) {
+ SS_PRINT(("NWSAP_main: initialization successfully completed.\n"));
+ }
+
+ /**
+ Use this thread as the scavenger thread to send server
+ announcements and watch the registry for configuration changes.
+
+ This never returns until we are terminating.
+ **/
+
+ (VOID)SapSendThread(NULL);
+
+ /**
+ If we get an error during initialization, we come here to
+ exit.
+
+ If we started OK and the user told us to stop, then we fall
+ thru from the call the SapSendThread just above us.
+ **/
+
+exit:
+
+ IF_DEBUG(TERMINATION) {
+ SS_PRINT(("NWSAP_main: terminating.\n"));
+ }
+
+ /** Announce we are going down **/
+
+ if (error)
+ terminationError = ERROR_SERVICE_NOT_ACTIVE;
+ else
+ terminationError = NO_ERROR;
+
+ SsServiceStatus.dwCurrentState = SERVICE_STOP_PENDING;
+ SsServiceStatus.dwCheckPoint = 1;
+ SsServiceStatus.dwWaitHint = 20 * 1000; /* 20 seconds */
+ SsServiceStatus.dwWin32ExitCode = terminationError;
+ SsServiceStatus.dwServiceSpecificExitCode = 0;
+
+ /** Tell the service controller we are stopping **/
+
+ AnnounceServiceStatus();
+
+ /** Clean up everything **/
+
+ IF_DEBUG(TERMINATION) {
+ SS_PRINT(("NWSAP_main: cleaning up.\n" ));
+ }
+
+ SapShutdown();
+
+ /** Announce that we are down **/
+
+ SsServiceStatus.dwCurrentState = SERVICE_STOPPED;
+ SsServiceStatus.dwControlsAccepted = 0;
+ SsServiceStatus.dwCheckPoint = 0;
+ SsServiceStatus.dwWaitHint = 0;
+ SsServiceStatus.dwWin32ExitCode = terminationError;
+ SsServiceStatus.dwServiceSpecificExitCode = 0;
+
+ AnnounceServiceStatus();
+
+ /** **/
+
+ IF_DEBUG(TERMINATION) {
+ SS_PRINT(("NWSAP_main: the server service is terminated.\n"));
+ }
+
+ /** All Done **/
+
+ return;
+}
+
+
+/*++
+*******************************************************************
+ A n n o u n c e S e r v i c e S t a t u s
+
+Routine Description:
+
+ Announce the service's status to the service controller
+
+Arguments:
+
+Return Value:
+
+ None.
+
+*******************************************************************
+--*/
+
+VOID
+AnnounceServiceStatus(
+ VOID)
+{
+ /** If no handle - we cannot announce **/
+
+ if (SsServiceStatusHandle == 0) {
+ IF_DEBUG(ANNOUNCE) {
+ SS_PRINT(("NWSAP: AnnounceServiceStatus: Cannot call SetServiceStatus, "
+ "no status handle.\n"));
+ }
+ return;
+ }
+
+ /** If we are printing debug info - do it **/
+
+ IF_DEBUG(ANNOUNCE) {
+ SS_PRINT(("NWSAP: AnnounceServiceStatus: CurrentState %lx\n"
+ " ControlsAccepted %lx\n"
+ " Win32ExitCode %lu\n"
+ " ServiceSpecificExitCode %lu\n"
+ " CheckPoint %lu\n"
+ " WaitHint %lu\n",
+ SsServiceStatus.dwCurrentState,
+ SsServiceStatus.dwControlsAccepted,
+ SsServiceStatus.dwWin32ExitCode,
+ SsServiceStatus.dwServiceSpecificExitCode,
+ SsServiceStatus.dwCheckPoint,
+ SsServiceStatus.dwWaitHint));
+ }
+
+ /** Go set the status **/
+
+ SetServiceStatus(SsServiceStatusHandle, &SsServiceStatus);
+
+ /** All Done **/
+
+ return;
+}
+
+
+/*++
+*******************************************************************
+ C o n t r o l R e s p o n s e
+
+Routine Description:
+
+ This handles control requests from the service controller.
+
+Arguments:
+
+ opcode = Command code for us to perform
+
+Return Value:
+
+ None.
+
+*******************************************************************
+--*/
+
+VOID
+ControlResponse(
+ DWORD opcode)
+{
+ BOOL announce = TRUE;
+
+ /**
+ Determine the type of service control message
+ and modify the service status, if necessary.
+ **/
+
+ switch(opcode) {
+
+ /** Want us to stop the SAP Agent **/
+
+ case SERVICE_CONTROL_STOP:
+
+ IF_DEBUG(CONTROL_MESSAGES) {
+ SS_PRINT(("NWSAP: ControlResponse: STOP control received.\n"));
+ }
+
+ /** Set our initialization flag to off (We are stopping) **/
+
+ SsInitialized = FALSE;
+
+ /** Announce that we are stopping **/
+
+ SsServiceStatus.dwCurrentState = SERVICE_STOP_PENDING;
+ SsServiceStatus.dwCheckPoint = 1;
+ SsServiceStatus.dwWaitHint = 20 * 1000; /* 20 seconds */
+ SsServiceStatus.dwWin32ExitCode = 0;
+ SsServiceStatus.dwServiceSpecificExitCode = 0;
+
+ AnnounceServiceStatus();
+
+ /**
+ Wake up the send thread to tell him to kill
+ the server.
+ **/
+
+ if (!SetEvent(SapSendEvent)) {
+ IF_DEBUG(TERMINATION_ERRORS) {
+ SS_PRINT(("NWSAP: ControlResponse: SetEvent failed: %ld\n",
+ GetLastError()));
+ }
+ }
+
+ /** Let the main thread announce when the stop is done **/
+
+ announce = FALSE;
+ break;
+
+ /** NT is shutting down **/
+
+ case SERVICE_CONTROL_SHUTDOWN:
+
+ IF_DEBUG(CONTROL_MESSAGES) {
+ SS_PRINT(("NWSAP: ControlResponse: SHUTDOWN control received.\n"));
+ }
+
+ /** Set our initialization flag to off (We are stopping) **/
+
+ SsInitialized = FALSE;
+
+ /** Announce that we are stopping **/
+
+ SsServiceStatus.dwCurrentState = SERVICE_STOP_PENDING;
+ SsServiceStatus.dwCheckPoint = 1;
+ SsServiceStatus.dwWaitHint = 20 * 1000; /* 20 seconds */
+ SsServiceStatus.dwWin32ExitCode = 0;
+ SsServiceStatus.dwServiceSpecificExitCode = 0;
+
+ AnnounceServiceStatus();
+
+ /** Tell everyone we are going down **/
+
+ SapSendPackets(2);
+
+ /** Clean up everything **/
+
+ SetEvent(SapSendEvent);
+
+ /** Announce that we are down **/
+
+ SsServiceStatus.dwCurrentState = SERVICE_STOPPED;
+ SsServiceStatus.dwControlsAccepted = 0;
+ SsServiceStatus.dwCheckPoint = 0;
+ SsServiceStatus.dwWaitHint = 0;
+ SsServiceStatus.dwWin32ExitCode = 0;
+ SsServiceStatus.dwServiceSpecificExitCode = 0;
+
+ /** Go announce this below **/
+
+ break;
+
+ /** Pause = IGNORE - but return announce of current status **/
+
+ case SERVICE_CONTROL_PAUSE:
+
+ IF_DEBUG(CONTROL_MESSAGES) {
+ SS_PRINT(("NWSAP: ControlResponse: PAUSE control received.\n"));
+ }
+
+#if DBG
+ IF_DEBUG(ENABLEDUMP) {
+ SapDebugHandler();
+ }
+#endif
+ break;
+
+ /** Continue = IGNORE - but return announce of current status **/
+
+ case SERVICE_CONTROL_CONTINUE:
+
+ IF_DEBUG(CONTROL_MESSAGES) {
+ SS_PRINT(("NWSAP: ControlResponse: CONTINUE control received.\n"));
+ }
+
+ break;
+
+ /** Interrogate = return current status **/
+
+ case SERVICE_CONTROL_INTERROGATE:
+
+ IF_DEBUG(CONTROL_MESSAGES) {
+ SS_PRINT(("NWSAP: ControlResponse: INTERROGATE control received.\n"));
+ }
+
+ break;
+
+ /** Rest are ignored - but return current status **/
+
+ default:
+
+ IF_DEBUG(CONTROL_MESSAGES) {
+ SS_PRINT(("NWSAP: ControlResponse: unknown code received. Code = 0x%lx\n", opcode));
+ }
+
+ break;
+ }
+
+ /** Announce new status **/
+
+ if (announce) {
+ AnnounceServiceStatus();
+ }
+
+ /** All Done **/
+
+ return;
+}
+
diff --git a/private/net/svcdlls/nwsap/server/wancheck.c b/private/net/svcdlls/nwsap/server/wancheck.c
new file mode 100644
index 000000000..48a7be362
--- /dev/null
+++ b/private/net/svcdlls/nwsap/server/wancheck.c
@@ -0,0 +1,988 @@
+/*++
+
+Copyright (c) 1994 Microsoft Corporation
+Copyright (c) 1993 Micro Computer Systems, Inc.
+
+Module Name:
+
+ net\svcdlls\nwsap\server\wancheck.c
+
+Abstract:
+
+ These routines handle the WAN cards going up and down.
+
+Author:
+
+ Brian Walker (MCS) 06-15-1993
+
+Revision History:
+
+--*/
+
+#include "precomp.h"
+#pragma hdrstop
+
+/** Structure of data we pass to IPX for the ADDRESS_NOTIFY **/
+
+typedef struct {
+ UCHAR Reserved[20]; /* Reserved for IPX use */
+ IPX_ADDRESS_DATA IpxData; /* Information structure we get */
+ HANDLE EventHandle; /* Handle for IPX to signal */
+} IPX_ADDRESS_DATA_EXTENDED;
+
+/** **/
+
+typedef struct {
+ LIST_ENTRY ListEntry;
+ IPX_ADDRESS_DATA_EXTENDED Data;
+} SAP_WANTRACK, *PSAP_WANTRACK;
+#define SAP_WANTRACK_SIZE sizeof(SAP_WANTRACK)
+
+/** **/
+
+SOCKET SapWanSocket = INVALID_SOCKET;
+
+/** Internal Function Prototypes **/
+
+DWORD WINAPI
+SapWanCheckThread(
+ LPVOID Threadparm);
+
+DWORD WINAPI
+SapWanWorkerThread(
+ LPVOID Threadparm);
+
+VOID
+SapWanCardUp(
+ PIPX_ADDRESS_DATA IpxData);
+
+VOID
+SapWanCardDown(
+ PIPX_ADDRESS_DATA IpxData);
+
+
+/*++
+*******************************************************************
+ S a p W a n I n i t
+
+Routine Description:
+
+ This routine initializes the WAN monitoring portion of the Sap Agent.
+
+Arguments:
+
+ None
+
+Return Value:
+
+ 0 = OK
+ Else = Error
+*******************************************************************
+--*/
+
+INT
+SapWanInit(
+ VOID)
+{
+ HANDLE Handle;
+ DWORD Threadid;
+ DWORD Error;
+ INT rc;
+ INT i;
+ INT Length;
+ SOCKADDR_IPX Bindaddr;
+
+ /** Initialize the sockets interface **/
+
+ WSADATA wsadata;
+ rc = WSAStartup(0x0101, &wsadata);
+ if (rc) {
+ SapEventId = NWSAP_EVENT_WSASTARTUP_FAILED;
+ return rc;
+ }
+
+ /**
+ Create an event to use to wait with. We use this
+ with the ADDRESS_NOTIFY socket option.
+ **/
+
+ SapWanEvent = CreateEvent(
+ NULL, /* No security */
+ FALSE, /* Auto reset */
+ FALSE, /* Initial state = Not signalled */
+ NULL); /* No name */
+
+ if (SapWanEvent == NULL) {
+ SapError = GetLastError();
+ SapEventId = NWSAP_EVENT_WANEVENT_ERROR;
+ return -1;
+ }
+
+ /**
+ Allocate a block of memory for the
+ list of handles that we use to store the
+ NOTIFY threads with.
+ **/
+
+ Length = SapNumWanNotifyThreads * sizeof(HANDLE),
+ SapWanNotifyHandlesBuf = SAP_MALLOC(
+ Length,
+ "SapWanNoifyThreadHandles");
+
+ if (SapWanNotifyHandlesBuf == NULL) {
+ SapError = GetLastError();
+ SapEventId = NWSAP_EVENT_WANHANDLEMEMORY_ERROR;
+ return -1;
+ }
+ memset(SapWanNotifyHandlesBuf, 0, Length);
+
+ /** Create a semaphore for the threads to wait on **/
+
+ SapWanCurBackup = 0;
+ SapWanCurFree = 0;
+ SapWanSem = CreateSemaphore(NULL, 0, 0x7FFFFFFF, NULL);
+ if (SapWanSem == NULL) {
+ SapError = GetLastError();
+ SapEventId = NWSAP_EVENT_WANSEM_FAIL;
+ return -1;
+ }
+
+ /** Open a socket to do the getsockopt on **/
+
+ SapWanSocket = socket(AF_IPX, SOCK_DGRAM, NSPROTO_IPX);
+ if (SapWanSocket == INVALID_SOCKET) {
+ SapError = h_errno;
+ SapEventId = NWSAP_EVENT_WANSOCKET_FAILED;
+ return -1;
+ }
+
+ /** Bind to any old address **/
+
+ memset(&Bindaddr, 0, sizeof(SOCKADDR_IPX));
+ Bindaddr.sa_family = AF_IPX;
+
+ rc = bind(SapWanSocket, (PSOCKADDR)&Bindaddr, sizeof(SOCKADDR_IPX));
+ if (rc == -1) {
+ SapError = h_errno;
+ SapEventId = NWSAP_EVENT_WANBIND_FAILED;
+ return -1;
+ }
+
+ /**
+ Create the worker thread.
+
+ We count this thread as running before we start it
+ for synchronization. If the start fails - then we will
+ dec this count
+ **/
+
+ SAP_INC_THREAD_COUNT("Wan Start 1", NULL);
+
+ /** Start the thread **/
+
+ Handle = CreateThread(
+ NULL, /* Security Ptr */
+ 0, /* Initial stack size */
+ SapWanWorkerThread, /* Thread Function */
+ (LPVOID)NULL, /* Parm for the thread */
+ 0, /* Creation Flags */
+ &Threadid); /* Ptr to thread id */
+
+ if (Handle == NULL) {
+
+ /** **/
+
+ Error = GetLastError();
+ IF_DEBUG(INITIALIZATION_ERRORS) {
+ SS_PRINT(("NWSAP: Error starting WAN worker thread = %d\n", Error));
+ }
+
+ /** Dec the thread count **/
+
+ SAP_DEC_THREAD_COUNT("Wan Start 1 Error", NULL);
+
+ /** Log the error **/
+
+ SsLogEvent(NWSAP_EVENT_STARTWANWORKER_ERROR, 0, NULL, Error);
+
+ /** Return Error **/
+
+ return -1;
+ }
+
+ /** We can close this handle **/
+
+ CloseHandle(Handle);
+
+ /**
+ Create the wait thread.
+
+ We count this thread as running before we start it
+ for synchronization. If the start fails - then we will
+ dec this count
+ **/
+
+ for (i = 0 ; i < SapNumWanNotifyThreads ; i++) {
+
+ SAP_INC_THREAD_COUNT("Wan Start 2", NULL);
+
+ /** Start the thread **/
+
+ SapWanNotifyHandlesBuf[i] = CreateThread(
+ NULL, /* Security Ptr */
+ 0, /* Initial stack size */
+ SapWanCheckThread, /* Thread Function */
+ (LPVOID)i, /* Parm for the thread */
+ 0, /* Creation Flags */
+ &Threadid); /* Ptr to thread id */
+
+ if (SapWanNotifyHandlesBuf[i] == NULL) {
+
+ /** **/
+
+ Error = GetLastError();
+ IF_DEBUG(INITIALIZATION_ERRORS) {
+ SS_PRINT(("NWSAP: Error starting WAN check thread = %d\n", Error));
+ }
+
+ /** Dec the thread count **/
+
+ SAP_DEC_THREAD_COUNT("Wan Start 2 Error", NULL);
+
+ /** Log the error **/
+
+ SsLogEvent(NWSAP_EVENT_STARTWANCHECK_ERROR, 0, NULL, Error);
+
+ /** Return Error **/
+
+ return -1;
+ }
+ }
+
+ /** Init OK **/
+
+ return 0;
+}
+
+
+/*++
+*******************************************************************
+ S a p W a n S h u t d o w n
+
+Routine Description:
+
+ When we are terminating, this routine will clean
+ up everything.
+
+Arguments:
+
+ None
+
+Return Value:
+
+ Nothing
+*******************************************************************
+--*/
+
+VOID
+SapWanShutdown(
+ VOID)
+{
+ PLIST_ENTRY Listp;
+ PSAP_WANTRACK Wanp;
+
+ /** Close the semaphore handle **/
+
+ if (SapWanSem != NULL)
+ CloseHandle(SapWanSem);
+
+ /** Close the handle **/
+
+ if (SapWanSocket != INVALID_SOCKET)
+ closesocket(SapWanSocket);
+
+ /** Cleanup the free list **/
+
+ while (!IsListEmpty(&SapWanRecvList)) {
+ Listp = RemoveHeadList(&SapWanRecvList);
+ Wanp = CONTAINING_RECORD(Listp, SAP_WANTRACK, ListEntry);
+ SAP_FREE(Wanp, "Wan Shutdown Recv List");
+ }
+
+ while (!IsListEmpty(&SapWanFreeList)) {
+ Listp = RemoveHeadList(&SapWanFreeList);
+ Wanp = CONTAINING_RECORD(Listp, SAP_WANTRACK, ListEntry);
+ SAP_FREE(Wanp, "Wan Shutdown Free List");
+ }
+
+ /** All Done **/
+
+ return;
+}
+
+
+/*++
+*******************************************************************
+ S a p W a n C h e c k T h r e a d
+
+Routine Description:
+
+ This thread keeps a getsockopt down to the IPX driver
+ to look for WANs that are going up or down.
+
+Arguments:
+
+ Threadparm = Thread parameter from CreateThread
+
+Return Value:
+
+ 0 Always
+*******************************************************************
+--*/
+
+DWORD WINAPI
+SapWanCheckThread(
+ LPVOID Threadparm)
+{
+ PSAP_WANTRACK Trackp;
+ PLIST_ENTRY Listp;
+ INT Length;
+ INT rc;
+ DWORD Error;
+ INT Index;
+
+ /** **/
+
+ Index = (INT)Threadparm;
+
+ /** **/
+
+ while (1) {
+
+ /** Get a buffer to do this with **/
+
+ ACQUIRE_WANFREETABLE_LOCK();
+ if (SapWanCurFree) {
+ Listp = RemoveHeadList(&SapWanFreeList);
+ Trackp = CONTAINING_RECORD(Listp, SAP_WANTRACK, ListEntry);
+ SapWanCurFree--;
+ }
+ else {
+ Trackp = SAP_MALLOC(SAP_WANTRACK_SIZE, "Alloc Trackp");
+ }
+ RELEASE_WANFREETABLE_LOCK();
+
+ /** If no memory **/
+
+ if (Trackp == NULL) {
+ IF_DEBUG(ERRORS) {
+ SS_PRINT(("WanCheckThread: No memory\n"));
+ }
+ break;
+ }
+
+ /** Set the handle that we use for waiting on the event **/
+
+ Trackp->Data.EventHandle = SapWanEvent;
+
+ /** Issue the Getsockopt **/
+
+ Length = sizeof(IPX_ADDRESS_DATA_EXTENDED);
+
+ rc = getsockopt(
+ SapWanSocket,
+ NSPROTO_IPX,
+ IPX_ADDRESS_NOTIFY,
+ (PVOID)&Trackp->Data,
+ &Length);
+
+ /** If we get an error - just free the entry and bomb out **/
+
+ if (rc == -1) {
+ Error = h_errno;
+ IF_DEBUG(ERRORS) {
+ SS_PRINT(("WanCheckThread: terminate w/error 1 = %d\n", Error));
+ }
+ SAP_FREE(Trackp, "ADDR NOTIFY ERROR");
+ break;
+ }
+
+ /** Wait on the event to signal **/
+
+ Error = WaitForSingleObject(SapWanEvent, INFINITE);
+
+ /** If the wait failed - just free the entry and bomb out **/
+
+ if (Error == 0xFFFFFFFF) {
+ Error = GetLastError();
+ IF_DEBUG(ERRORS) {
+ SS_PRINT(("WanCheckThread: terminate w/error 2 = %d\n", Error));
+ }
+ SAP_FREE(Trackp, "ADDR WAIT ERROR");
+ break;
+ }
+
+ /**
+ If the server is going down - get out now
+ **/
+
+ if (!SsInitialized) {
+ SAP_FREE(Trackp, "Wan - Server down");
+ break;
+ }
+
+ /** Put the entry in the list **/
+
+ InitializeListHead(&Trackp->ListEntry);
+ ACQUIRE_WANRECVTABLE_LOCK();
+ InsertTailList(&SapWanRecvList, &Trackp->ListEntry);
+ SapWanCurBackup++;
+ RELEASE_WANRECVTABLE_LOCK();
+
+ /** Release the semaphore to run the worker thread **/
+
+ ReleaseSemaphore(SapWanSem, 1, NULL);
+ }
+
+ /**
+ Take us out of the table
+ **/
+
+ ACQUIRE_THREADCOUNT_LOCK();
+ if (SapWanNotifyHandlesBuf) {
+ if (SapWanNotifyHandlesBuf[Index]) {
+ SapWanNotifyHandlesBuf[Index] = NULL;
+ RELEASE_THREADCOUNT_LOCK();
+ SAP_DEC_THREAD_COUNT("Wan Check Thread Termination", NULL);
+ }
+ else {
+ RELEASE_THREADCOUNT_LOCK();
+ }
+ }
+ else {
+ RELEASE_THREADCOUNT_LOCK();
+ }
+
+ /** Just leave **/
+
+ return 0;
+}
+
+
+/*++
+*******************************************************************
+ S a p W a n W o r k e r T h r e a d
+
+Routine Description:
+
+ This thread takes blocks from the SapWanCheckThread
+ and processes them.
+
+Arguments:
+
+ Threadparm = Thread parameter from CreateThread
+
+Return Value:
+
+ 0 Always
+*******************************************************************
+--*/
+
+DWORD WINAPI
+SapWanWorkerThread(
+ LPVOID Threadparm)
+{
+ NTSTATUS Status;
+ PSAP_WANTRACK Trackp;
+ PLIST_ENTRY Listp;
+
+ /** **/
+
+ while (SsInitialized) {
+
+ /** Wait for a request to show up **/
+
+ Status = WaitForSingleObjectEx(SapWanSem, INFINITE, TRUE);
+
+ /** If stopping - just leave **/
+
+ if (!SsInitialized) {
+ IF_DEBUG(TERMINATION) {
+ SS_PRINT(("NWSAP: Wan Worker Thread: Breaking out for stop\n"));
+ }
+ break;
+ }
+
+ /** If error - just ignore it **/
+
+ if (!NT_SUCCESS(Status)) {
+ IF_DEBUG(ERRORS) {
+ SS_PRINT(("NWSAP: Wan Worker: Wait failed: Status = 0x%lx\n", Status));
+ }
+ continue;
+ }
+
+ /** Get ownership of the worker list **/
+
+ ACQUIRE_WANRECVTABLE_LOCK();
+
+ /** Get an entry from the list **/
+
+ if (!IsListEmpty(&SapWanRecvList)) {
+ SS_ASSERT(SapWanCurBackup != 0);
+ Listp = RemoveHeadList(&SapWanRecvList);
+ Trackp = CONTAINING_RECORD(Listp, SAP_WANTRACK, ListEntry);
+ SapWanCurBackup--;
+ }
+ else {
+ SS_ASSERT(SapWanCurBackup == 0);
+ Trackp = NULL;
+ }
+
+ /** Release the lock on the list **/
+
+ RELEASE_WANRECVTABLE_LOCK();
+
+ /** If no packet - error **/
+
+ if (Trackp == NULL) {
+ IF_DEBUG(ERRORS) {
+ SS_PRINT(("NWSAP: WAN WORKER: Wait came back but no block ready\n"));
+ }
+ continue;
+ }
+
+ /** Handle the data here **/
+
+ if (Trackp->Data.IpxData.status == TRUE)
+ SapWanCardUp(&Trackp->Data.IpxData);
+ else
+ SapWanCardDown(&Trackp->Data.IpxData);
+
+ /**
+ If there is room in the FREE list - then put
+ this entry in the free list, else just free
+ the memory back to heap.
+ **/
+
+ ACQUIRE_WANFREETABLE_LOCK();
+ if (SapWanCurFree < SapWanMaxFree) {
+ InsertTailList(&SapWanFreeList, &Trackp->ListEntry);
+ SapWanCurFree++;
+ }
+ else {
+ SAP_FREE(Trackp, "FREE WAN TRACK 1");
+ }
+ RELEASE_WANFREETABLE_LOCK();
+ }
+
+ /** We are terminating - uncount and set event if need to **/
+
+ SAP_DEC_THREAD_COUNT("Wan Worker Terminate", NULL);
+
+ /** All Done **/
+
+ return 0;
+}
+
+
+/*++
+*******************************************************************
+ S a p W a n C a r d U p
+
+Routine Description:
+
+ This routine is called when a new adapter is added. We
+ will handle setting up this new adapter.
+
+Arguments:
+
+ IpxData = Ptr to an IPX_ADDRESS_DATA structure that we
+ got from the transport
+
+Return Value:
+
+ Nothing
+
+*******************************************************************
+--*/
+
+VOID
+SapWanCardUp(
+ PIPX_ADDRESS_DATA IpxData)
+{
+ PSAP_CARD Cardptr;
+ INT Retcode;
+
+ /** Make sure that this card does not already exist **/
+
+ IF_DEBUG(WAN) {
+ SS_PRINT(("NWSAP: SapWanCardUp: Adapnum = %d\n", IpxData->adapternum));
+ }
+
+ ACQUIRE_CARDLIST_WRITERS_LOCK(Retcode, "Wancheck up 1");
+ if (Retcode) {
+ IF_DEBUG(ERRORS) {
+ SS_PRINT(("NWSAP: SapWanCardUp: Error getting card writers lock\n"));
+ }
+ return;
+ }
+
+ Cardptr = SapCardHead;
+ while (Cardptr) {
+ if (Cardptr->Number == IpxData->adapternum)
+ break;
+ Cardptr = Cardptr->Next;
+ }
+
+ /** If card already here - just leave **/
+
+ if (Cardptr) {
+ RELEASE_CARDLIST_WRITERS_LOCK("Wancheck up 1");
+ return;
+ }
+
+ /** Allocate an entry for this card **/
+
+ Cardptr = SAP_MALLOC(SAP_CARD_SIZE, "SapWanCardUp");
+ if (Cardptr == NULL) {
+
+ /** Release the lock **/
+
+ RELEASE_CARDLIST_WRITERS_LOCK("Wancheck up 2");
+
+ IF_DEBUG(ERRORS) {
+ SS_PRINT(("NWSAP: SapWanCardUp: Error allocating memory\n"));
+ }
+
+ /** Log the error **/
+
+ SsLogEvent(
+ NWSAP_EVENT_CARDMALLOC_FAILED,
+ 0,
+ NULL,
+ 0);
+
+ /** And Leave **/
+
+ return;
+ }
+
+ /** Fill in this card **/
+
+ Cardptr->Next = NULL;
+ SAP_COPY_NETNUM(Cardptr->Netnum, IpxData->netnum);
+ SAP_COPY_NODENUM(Cardptr->Nodenum, IpxData->nodenum);
+ Cardptr->Linkspeed = IpxData->linkspeed;
+ Cardptr->Wanflag = IpxData->wan;
+ Cardptr->Maxpkt = IpxData->maxpkt;
+ Cardptr->Number = (UCHAR)IpxData->adapternum;
+ Cardptr->ReqCount = 1; /* Send one extra general request */
+
+ /** For building with **/
+
+ Cardptr->Plist.Curnum = 0;
+ Cardptr->Plist.Curpkt = NULL;
+ Cardptr->Plist.Curptr = NULL;
+ Cardptr->Plist.PktHead = NULL;
+ Cardptr->Plist.PktTail = NULL;
+ Cardptr->Plist.NumPkts = 0;
+
+ /** Put this card in the list **/
+
+ if (SapCardHead)
+ SapCardTail->Next = Cardptr;
+ else
+ SapCardHead = Cardptr;
+ SapCardTail = Cardptr;
+ SapNumCards++; /* Count the card */
+
+ /**
+ If we are still starting up - then
+ we just leave. If not, then we send
+ a general request on this netnum.
+ **/
+
+ if (SapCardInitDone == 0) {
+ RELEASE_CARDLIST_WRITERS_LOCK("WanCardUp X1");
+ return;
+ }
+
+ /**
+ Send a general request on this network number to
+ get our table filled up for this network
+ **/
+
+ RELEASE_CARDLIST_WRITERS_LOCK("WanCardUp X2");
+ SapSendGeneralRequest(FALSE, IpxData->netnum);
+
+ /** All Done **/
+
+ return;
+}
+
+
+/*++
+*******************************************************************
+ S a p W a n C a r d D o w n
+
+Routine Description:
+
+ This routine is called when an adapter is removed from the
+ transports list. We cleanup everything that had to do with
+ this adapter.
+
+Arguments:
+
+ IpxData = Ptr to an IPX_ADDRESS_DATA structure that we
+ got from the transport
+
+Return Value:
+
+ Nothing
+
+*******************************************************************
+--*/
+
+VOID
+SapWanCardDown(
+ PIPX_ADDRESS_DATA IpxData)
+{
+ PSAP_CARD Cardptr;
+ PSAP_CARD BCardptr;
+ INT Status;
+ INT Cardnum;
+
+ /** **/
+
+ IF_DEBUG(WAN) {
+ SS_PRINT(("NWSAP: SapWanCardDown: Adapnum = %d\n", IpxData->adapternum));
+ }
+
+ /** Take the entry out of the card list **/
+
+ Cardnum = IpxData->adapternum;
+ ACQUIRE_CARDLIST_WRITERS_LOCK(Status, "WanCardDown");
+ if (Status) {
+ IF_DEBUG(ERRORS) {
+ SS_PRINT(("NWSAP: SapWanCardDown: Error getting cardlist writers lock\n"));
+ }
+ return;
+ }
+
+ /** **/
+
+ Cardptr = SapCardHead;
+ BCardptr = NULL;
+ while (Cardptr) {
+
+ /** If this is it - take it out of the list **/
+
+ if (Cardptr->Number == Cardnum) {
+
+ if (BCardptr)
+ BCardptr->Next = Cardptr->Next;
+ else
+ SapCardHead = Cardptr->Next;
+ if (Cardptr == SapCardTail)
+ SapCardTail = BCardptr;
+
+ SapNumCards--; /* Uncount the card */
+
+ break;
+ }
+
+ /** Goto the next entry **/
+
+ BCardptr = Cardptr;
+ Cardptr = Cardptr->Next;
+ }
+
+ /**
+ If we are still starting up - then
+ we just leave. If not, then we need to go
+ cleanup for this card.
+ **/
+
+ if (SapCardInitDone == 0) {
+ RELEASE_CARDLIST_WRITERS_LOCK("WanCardDown X1");
+ return;
+ }
+
+ /** Go shutdown the card now **/
+
+ RELEASE_CARDLIST_WRITERS_LOCK("WanCardDown X2");
+ if (Cardptr)
+ SapCleanupDownedCard(Cardnum);
+
+ /** All Done **/
+
+ return;
+}
+
+
+/*++
+*******************************************************************
+ S a p R e c h e c k A l l C a r d s
+
+Routine Description:
+
+ This routine goes thru and checks all cards occassionally
+ to make sure that they are still valid. This is called
+ from the send thread.
+
+Arguments:
+
+ None
+
+Return Value:
+
+ Nothing
+
+*******************************************************************
+--*/
+
+VOID
+SapRecheckAllCards(
+ VOID)
+{
+ INT Cardnum;
+ INT rc;
+ INT Length;
+ IPX_ADDRESS_DATA Addrdata;
+
+ /** Get the list of cards we have **/
+
+ Cardnum = 0;
+ while (Cardnum != SapMaxCardIndex) {
+
+ /** Fill in this entry **/
+
+ Addrdata.adapternum = Cardnum;
+ Length = sizeof(IPX_ADDRESS_DATA);
+ rc = getsockopt(
+ SapSocket,
+ NSPROTO_IPX,
+ IPX_ADDRESS,
+ (PCHAR)&Addrdata,
+ &Length);
+
+ /**
+ If this card is active - call to make sure it
+ is in our list.
+
+ If this card is inactive - call to make sure it
+ is not in our list.
+
+ Only check this if the getsockopt was OK.
+ **/
+
+ if (rc == 0) {
+ if (Addrdata.status == TRUE)
+ SapWanCardUp(&Addrdata);
+ else
+ SapWanCardDown(&Addrdata);
+ }
+
+ /** Goto the next card number **/
+
+ Cardnum++;
+ }
+
+ /** All Done **/
+
+ return;
+}
+
+
+/*++
+*******************************************************************
+ S a p C h e c k S e n d G e n e r a l R e q u e s t
+
+Routine Description:
+
+ This routine is called by the main send thread to check
+ if we need to send a GENERAL REQUEST out.
+
+ When a WAN card comes up, we send a general request.
+ We send the request multiple times in case it got
+ missed.
+
+Arguments:
+
+ None
+
+Return Value:
+
+ Nothing
+
+*******************************************************************
+--*/
+
+#define SAPMAX_GENREQBUF 40 /* Max we can do at once */
+
+VOID
+SapCheckSendGeneralRequest(
+ VOID)
+{
+ PSAP_CARD Cardptr;
+ PUCHAR NetPtr;
+ UCHAR NetBuf[SAPMAX_GENREQBUF * SAP_NET_LEN];
+ INT NumNets = 0;
+
+ /**
+ Even though we change the cards ReqCount entry
+ here, we get a READERS lock because No one else
+ is allowed to change this.
+ **/
+
+ ACQUIRE_CARDLIST_READERS_LOCK("CheckSendGenReq Entry");
+
+ /**
+ See if any cards need the advertise sent again. We
+ keep a count in the card structure for this.
+
+ If we find one - copy the network number to a
+ private buffer for us to send out of later.
+ **/
+
+ NetPtr = NetBuf;
+ Cardptr = SapCardHead;
+ while (Cardptr) {
+ if (Cardptr->ReqCount) {
+
+ /** Dec the send again count on this adapter **/
+
+ Cardptr->ReqCount--;
+
+ /** Save off the network number **/
+
+ SAP_COPY_NETNUM(NetPtr, Cardptr->Netnum);
+ NetPtr += SAP_NET_LEN;
+
+ /** If we hit max - just dump out **/
+
+ NumNets++;
+ if (NumNets == SAPMAX_GENREQBUF)
+ break;
+ }
+ Cardptr = Cardptr->Next;
+ }
+ RELEASE_CARDLIST_READERS_LOCK("CheckSendGenReq X1");
+
+ /**
+ Send a general request on this network number to
+ get our table filled up for this network
+ **/
+
+ NetPtr = NetBuf;
+ while (NumNets--) {
+ SapSendGeneralRequest(FALSE, NetPtr);
+ NetPtr += SAP_NET_LEN;
+ }
+
+ /** All Done **/
+
+ return;
+}
+
diff --git a/private/net/svcdlls/repl/client/expstub.c b/private/net/svcdlls/repl/client/expstub.c
new file mode 100644
index 000000000..5618189f1
--- /dev/null
+++ b/private/net/svcdlls/repl/client/expstub.c
@@ -0,0 +1,585 @@
+/*++
+
+Copyright (c) 1991-1993 Microsoft Corporation
+
+Module Name:
+
+ ExpStub.c
+
+Abstract:
+
+ Client stubs of the replicator service export directory APIs.
+
+Author:
+
+ John Rogers (JohnRo) 17-Dec-1991
+
+Environment:
+
+ User Mode - Win32
+
+Revision History:
+
+ 17-Dec-1991 JohnRo
+ Created dummy file.
+ 17-Dec-1991 JohnRo
+ Actually include my header file (LmRepl.h) so we can test against it.
+ 17-Jan-1992 JohnRo
+ Wrote stubs for first 3 RPCable APIs.
+ 20-Jan-1992 JohnRo
+ Added import APIs, config APIs, and rest of export APIs.
+ 27-Jan-1992 JohnRo
+ Split stubs into 3 files: ReplStub.c, ImpStub.c, and ExpStub.c.
+ Changed to use LPTSTR etc.
+ Added handling of getinfo and setinfo APIs when service isn't started.
+ Tell NetRpc.h macros that we need replicator service.
+ 30-Jan-1992 JohnRo
+ Made changes suggested by PC-LINT.
+ 03-Feb-1992 JohnRo
+ Corrected NetReplExportDirGetInfo's handling of level errors.
+ 13-Feb-1992 JohnRo
+ Implement NetReplExportDirDel() when svc is not running.
+ Added debug messages when processing APIs without service running.
+ 20-Feb-1992 JohnRo
+ Use ExportDirIsLevelValid() where possible.
+ Fixed enum when array is empty.
+ Fixed forgotten net handle closes in enum code.
+ Make NetRepl{Export,Import}Dir{Lock,Unlock} work w/o svc running.
+ Fixed usage of union/container.
+ 15-Mar-1992 JohnRo
+ Update registry with new values.
+ 23-Mar-1992 JohnRo
+ Fixed enum when service is running.
+ 06-Apr-1992 JohnRo
+ Fixed trivial MIPS compile problem.
+ 28-Apr-1992 JohnRo
+ Fixed another trivial MIPS compile problem.
+ 19-Jul-1992 JohnRo
+ RAID 10503: srv mgr: repl dialog doesn't come up.
+ Use PREFIX_ equates.
+ 27-Jul-1992 JohnRo
+ RAID 2274: repl svc should impersonate caller.
+ 26-Aug-1992 JohnRo
+ RAID 3602: NetReplExportDirSetInfo fails regardless of svc status.
+ 29-Sep-1992 JohnRo
+ RAID 7962: Repl APIs in wrong role kill svc.
+ Also fix remote repl admin.
+ 05-Apr-1993 JohnRo
+ Use NetpKdPrint() where possible.
+ Made changes suggested by PC-LINT 5.0
+ Removed some obsolete comments about retrying APIs.
+
+--*/
+
+
+// These must be included first:
+
+#include <windef.h>
+#include <lmcons.h> // NET_API_STATUS, etc.
+#include <netdebug.h> // Needed by <netrpc.h>; NetpKdPrint(), etc.
+#include <repldefs.h> // IF_DEBUG().
+#include <rpc.h> // Needed by <netrpc.h>.
+
+// These may be included in any order:
+
+#include <dirname.h> // ReplIsDirNameValid().
+#include <expdir.h> // ExportDirIsApiRecordValid(), etc.
+#include <lmrepl.h> // My prototypes, etc.
+#include <lmerr.h> // NERR_ and ERROR_ equates, NO_ERROR.
+#include <lmsname.h> // SERVICE_ name equates.
+#include <netlib.h> // NetpSetParmError(), etc.
+#include <netrpc.h> // NET_REMOTE macros.
+#include <prefix.h> // PREFIX_ equates.
+#include <repl.h> // MIDL-generated NetrRepl prototypes.
+#include <rpcutil.h> // GENERIC_INFO_CONTAINER, etc.
+
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetReplExportDirAdd (
+ IN LPCWSTR UncServerName OPTIONAL,
+ IN DWORD Level, // Must be 1.
+ IN const LPBYTE Buf,
+ OUT LPDWORD ParmError OPTIONAL
+ )
+{
+ NET_API_STATUS ApiStatus;
+
+ NET_REMOTE_TRY_RPC
+
+ //
+ // Try RPC (local or remote) version of API.
+ //
+ ApiStatus = NetrReplExportDirAdd(
+ (LPWSTR)UncServerName,
+ Level,
+ (LPEXPORT_CONTAINER) (LPVOID) &Buf,
+ ParmError);
+
+ NET_REMOTE_RPC_FAILED("NetReplExportDirAdd",
+ UncServerName,
+ ApiStatus,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_REPL )
+
+ //
+ // No downlevel version to call
+ //
+ ApiStatus = ERROR_NOT_SUPPORTED;
+
+ NET_REMOTE_END
+
+ //
+ // Handle API when service isn't running...
+ //
+ if (ApiStatus == NERR_ServiceNotInstalled) {
+ LPREPL_EDIR_INFO_1 ApiRecord;
+
+ IF_DEBUG(DLLSTUB) {
+ NetpKdPrint(( PREFIX_REPL
+ "NetReplExportDirAdd: processing w/o svc.\n" ));
+ }
+
+ //
+ // Check for caller errors.
+ //
+ NetpSetParmError( PARM_ERROR_UNKNOWN ); // Assume error until proven...
+ if (Buf == NULL) {
+ return (ERROR_INVALID_PARAMETER);
+ }
+ if (Level != 1) {
+ return (ERROR_INVALID_LEVEL);
+ }
+
+ ApiRecord = (LPVOID) Buf;
+ if ( ! ExportDirIsApiRecordValid (
+ Level,
+ ApiRecord,
+ ParmError) ) {
+
+ return (ERROR_INVALID_PARAMETER);
+ }
+
+ IF_DEBUG(DLLSTUB) {
+ NetpKdPrint(( PREFIX_REPL
+ "NetReplExportDirAdd: adding API record to " FORMAT_LPTSTR
+ "...\n",
+ (UncServerName!=NULL)
+ ? UncServerName
+ : (LPTSTR) TEXT("local") ));
+ NetpDbgDisplayReplExportDir( Level, Buf );
+ }
+
+ if (ExportDirConfigDataExists(
+ (LPWSTR)UncServerName,
+ ApiRecord->rped1_dirname )) {
+
+ // Oops, we got a duplicate.
+ return (ERROR_ALREADY_EXISTS); // BUGBUG: better error code?
+ }
+
+ // Write config data for this export directory.
+ ApiStatus = ExportDirWriteConfigData (
+ (LPWSTR)UncServerName,
+ ApiRecord->rped1_dirname,
+ ApiRecord->rped1_integrity,
+ ApiRecord->rped1_extent,
+ 0, // lock count
+ 0 ); // lock time (none) Seconds since 1970.
+
+ IF_DEBUG(DLLSTUB) {
+ NetpKdPrint(( PREFIX_REPL
+ "NetReplExportDirAdd: done adding API record, "
+ "API status is " FORMAT_API_STATUS ".\n",
+ ApiStatus ));
+ }
+
+ if (ApiStatus == NO_ERROR) {
+ //
+ // Everything went OK. Tell caller.
+ //
+ NetpSetParmError( PARM_ERROR_NONE );
+ }
+
+ }
+
+ return ApiStatus;
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetReplExportDirDel (
+ IN LPCWSTR UncServerName OPTIONAL,
+ IN LPCWSTR DirName
+ )
+{
+ NET_API_STATUS ApiStatus;
+
+ NET_REMOTE_TRY_RPC
+
+ //
+ // Try RPC (local or remote) version of API.
+ //
+ ApiStatus = NetrReplExportDirDel(
+ (LPWSTR)UncServerName,
+ (LPWSTR)DirName);
+
+ NET_REMOTE_RPC_FAILED("NetReplExportDirDel",
+ UncServerName,
+ ApiStatus,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_REPL )
+
+ //
+ // No downlevel version to call
+ //
+ ApiStatus = ERROR_NOT_SUPPORTED;
+
+ NET_REMOTE_END
+
+ //
+ // Handle API when service isn't running...
+ //
+ if (ApiStatus == NERR_ServiceNotInstalled) {
+
+ IF_DEBUG(DLLSTUB) {
+ NetpKdPrint(( PREFIX_REPL
+ "NetReplExportDirDel: processing w/o svc.\n" ));
+ }
+
+ //
+ // Check for caller errors.
+ //
+ if ( !ReplIsDirNameValid( (LPWSTR)DirName ) ) {
+ return (ERROR_INVALID_PARAMETER);
+ }
+
+ IF_DEBUG(DLLSTUB) {
+ NetpKdPrint(( PREFIX_REPL
+ "NetReplExportDirDel: deleting API record for '"
+ FORMAT_LPTSTR "'...\n", DirName ));
+ }
+
+ if (! ExportDirConfigDataExists( (LPWSTR)UncServerName, (LPWSTR)DirName )) {
+ return (NERR_UnknownDevDir);
+ }
+
+ //
+ // Write config data for this export directory.
+ //
+ ApiStatus = ExportDirDeleteConfigData( (LPWSTR)UncServerName, (LPWSTR)DirName );
+
+ IF_DEBUG(DLLSTUB) {
+ NetpKdPrint(( PREFIX_REPL
+ "NetReplExportDirDel: done deleting API record, "
+ "API status is " FORMAT_API_STATUS ".\n",
+ ApiStatus ));
+ }
+
+ }
+
+
+ return ApiStatus;
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetReplExportDirEnum (
+ IN LPCWSTR UncServerName OPTIONAL,
+ IN DWORD Level,
+ OUT LPBYTE * BufPtr,
+ IN DWORD PrefMaxSize,
+ OUT LPDWORD EntriesRead,
+ OUT LPDWORD TotalEntries,
+ IN OUT LPDWORD ResumeHandle OPTIONAL
+ )
+{
+ NET_API_STATUS ApiStatus;
+ GENERIC_INFO_CONTAINER GenericInfoContainer;
+ GENERIC_ENUM_STRUCT InfoStruct;
+
+ GenericInfoContainer.Buffer = NULL;
+ GenericInfoContainer.EntriesRead = 0;
+
+ InfoStruct.Container = &GenericInfoContainer;
+ InfoStruct.Level = Level;
+
+ *BufPtr = NULL; // Must be NULL so RPC knows to fill it in.
+
+ NET_REMOTE_TRY_RPC
+
+ //
+ // Try RPC (local or remote) version of API.
+ //
+ ApiStatus = NetrReplExportDirEnum(
+ (LPWSTR)UncServerName,
+ // Level,
+ // (LPEXPORT_CONTAINER) (LPVOID) BufPtr,
+ (LPEXPORT_ENUM_STRUCT) (LPVOID) &InfoStruct,
+ PrefMaxSize,
+ // EntriesRead,
+ TotalEntries,
+ ResumeHandle);
+ if ((ApiStatus == NERR_Success) || (ApiStatus == ERROR_MORE_DATA)) {
+ *BufPtr = (LPBYTE) GenericInfoContainer.Buffer;
+ *EntriesRead = GenericInfoContainer.EntriesRead;
+ }
+
+ NET_REMOTE_RPC_FAILED("NetReplExportDirEnum",
+ UncServerName,
+ ApiStatus,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_REPL )
+
+ //
+ // No downlevel version to call
+ //
+ ApiStatus = ERROR_NOT_SUPPORTED;
+
+ NET_REMOTE_END
+
+ //
+ // Handle service not running by reading config data directly.
+ //
+ if (ApiStatus == NERR_ServiceNotInstalled) {
+
+ IF_DEBUG(DLLSTUB) {
+ NetpKdPrint(( PREFIX_REPL
+ "NetReplExportDirEnum: processing w/o svc.\n" ));
+ }
+
+ NetpSetOptionalArg( ResumeHandle, 0 );
+
+ ApiStatus = ExportDirEnumApiRecords(
+ (LPWSTR)UncServerName,
+ Level,
+ BufPtr,
+ PrefMaxSize,
+ EntriesRead,
+ TotalEntries );
+
+ }
+
+ return ApiStatus;
+
+} // NetReplExportDirEnum
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetReplExportDirGetInfo (
+ IN LPCWSTR UncServerName OPTIONAL,
+ IN LPCWSTR DirName,
+ IN DWORD Level,
+ OUT LPBYTE * BufPtr
+ )
+{
+ NET_API_STATUS ApiStatus;
+
+ *BufPtr = NULL; // Must be NULL so RPC knows to fill it in.
+
+ NET_REMOTE_TRY_RPC
+
+ //
+ // Try RPC (local or remote) version of API.
+ //
+ ApiStatus = NetrReplExportDirGetInfo(
+ (LPWSTR)UncServerName,
+ (LPWSTR)DirName,
+ Level,
+ (LPEXPORT_CONTAINER) (LPVOID) BufPtr);
+
+ NET_REMOTE_RPC_FAILED("NetReplExportDirGetInfo",
+ UncServerName,
+ ApiStatus,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_REPL )
+
+ //
+ // No downlevel version to call
+ //
+ ApiStatus = ERROR_NOT_SUPPORTED;
+
+ NET_REMOTE_END
+
+ if (ApiStatus == NERR_ServiceNotInstalled) {
+
+ IF_DEBUG(DLLSTUB) {
+ NetpKdPrint(( PREFIX_REPL
+ "NetReplExportDirGetInfo: processing w/o svc.\n" ));
+ }
+
+ ApiStatus = ExportDirGetApiRecord (
+ (LPWSTR)UncServerName,
+ (LPWSTR)DirName,
+ Level,
+ BufPtr );
+
+ }
+
+ return ApiStatus;
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetReplExportDirLock (
+ IN LPCWSTR UncServerName OPTIONAL,
+ IN LPCWSTR DirName
+ )
+{
+ NET_API_STATUS ApiStatus;
+
+ NET_REMOTE_TRY_RPC
+
+ //
+ // Try RPC (local or remote) version of API.
+ //
+ ApiStatus = NetrReplExportDirLock(
+ (LPWSTR)UncServerName,
+ (LPWSTR)DirName);
+
+ NET_REMOTE_RPC_FAILED("NetReplExportDirLock",
+ UncServerName,
+ ApiStatus,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_REPL )
+
+ //
+ // No downlevel version to call
+ //
+ ApiStatus = ERROR_NOT_SUPPORTED;
+
+ NET_REMOTE_END
+
+ if (ApiStatus == NERR_ServiceNotInstalled) {
+
+ IF_DEBUG(DLLSTUB) {
+ NetpKdPrint(( PREFIX_REPL
+ "NetReplExportDirLock: processing w/o svc.\n" ));
+ }
+
+ ApiStatus = ExportDirLockInRegistry(
+ (LPWSTR)UncServerName,
+ (LPWSTR)DirName );
+
+ }
+ return ApiStatus;
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetReplExportDirSetInfo (
+ IN LPCWSTR UncServerName OPTIONAL,
+ IN LPCWSTR DirName,
+ IN DWORD Level,
+ IN const LPBYTE Buf,
+ OUT LPDWORD ParmError OPTIONAL
+ )
+{
+ NET_API_STATUS ApiStatus;
+ LPEXPORT_CONTAINER ApiUnion = (LPVOID) &Buf;
+ // LPEXPORT_CONTAINER ApiUnion = (LPVOID) Buf;
+
+ NET_REMOTE_TRY_RPC
+
+ IF_DEBUG( DLLSTUB ) {
+ NetpKdPrint(( PREFIX_REPL
+ "NetReplExportDirSetInfo: union at " FORMAT_LPVOID ".\n",
+ (LPVOID) Buf ));
+ }
+
+ //
+ // Try RPC (local or remote) version of API.
+ //
+ ApiStatus = NetrReplExportDirSetInfo(
+ (LPWSTR)UncServerName,
+ (LPWSTR)DirName,
+ Level,
+ ApiUnion,
+ ParmError);
+
+ NET_REMOTE_RPC_FAILED("NetReplExportDirSetInfo",
+ UncServerName,
+ ApiStatus,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_REPL )
+
+ //
+ // No downlevel version to call
+ //
+ ApiStatus = ERROR_NOT_SUPPORTED;
+
+ NET_REMOTE_END
+
+ if (ApiStatus == NERR_ServiceNotInstalled) {
+
+ IF_DEBUG(DLLSTUB) {
+ NetpKdPrint(( PREFIX_REPL
+ "NetReplExportDirSetInfo: processing w/o svc.\n" ));
+ }
+
+ ApiStatus = ExportDirConfigSetInfo (
+ (LPWSTR)UncServerName,
+ (LPWSTR)DirName,
+ Level,
+ Buf,
+ ParmError );
+
+ }
+
+ IF_DEBUG( DLLSTUB ) {
+ NetpKdPrint(( PREFIX_REPL
+ "NetReplExportDirSetInfo: returning status " FORMAT_API_STATUS
+ ".\n", ApiStatus ));
+ }
+
+
+ return ApiStatus;
+}
+
+NET_API_STATUS NET_API_FUNCTION
+NetReplExportDirUnlock (
+ IN LPCWSTR UncServerName OPTIONAL,
+ IN LPCWSTR DirName,
+ IN DWORD UnlockForce
+ )
+{
+ NET_API_STATUS ApiStatus;
+
+ NET_REMOTE_TRY_RPC
+
+ //
+ // Try RPC (local or remote) version of API.
+ //
+ ApiStatus = NetrReplExportDirUnlock(
+ (LPWSTR)UncServerName,
+ (LPWSTR)DirName,
+ UnlockForce);
+
+ NET_REMOTE_RPC_FAILED("NetReplExportDirUnlock",
+ UncServerName,
+ ApiStatus,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_REPL )
+
+ //
+ // No downlevel version to call
+ //
+ ApiStatus = ERROR_NOT_SUPPORTED;
+
+ NET_REMOTE_END
+
+ if (ApiStatus == NERR_ServiceNotInstalled) {
+
+ IF_DEBUG(DLLSTUB) {
+ NetpKdPrint(( PREFIX_REPL
+ "NetReplExportDirUnlock: processing w/o svc.\n" ));
+ }
+
+ ApiStatus = ExportDirUnlockInRegistry(
+ (LPWSTR)UncServerName,
+ (LPWSTR)DirName,
+ UnlockForce );
+
+ }
+ return ApiStatus;
+}
diff --git a/private/net/svcdlls/repl/client/impstub.c b/private/net/svcdlls/repl/client/impstub.c
new file mode 100644
index 000000000..6c8a8770f
--- /dev/null
+++ b/private/net/svcdlls/repl/client/impstub.c
@@ -0,0 +1,507 @@
+/*++
+
+Copyright (c) 1991-1993 Microsoft Corporation
+
+Module Name:
+
+ ImpStub.c
+
+Abstract:
+
+ Client stubs of the replicator service import directory APIs.
+
+Author:
+
+ John Rogers (JohnRo) 17-Dec-1991
+
+Environment:
+
+ User Mode - Win32
+
+Revision History:
+
+ 17-Dec-1991 JohnRo
+ Created dummy file.
+ 17-Dec-1991 JohnRo
+ Actually include my header file (LmRepl.h) so we can test against it.
+ 17-Jan-1992 JohnRo
+ Wrote stubs for first 3 RPCable APIs.
+ 20-Jan-1992 JohnRo
+ Added import APIs, config APIs, and rest of export APIs.
+ 27-Jan-1992 JohnRo
+ Split stubs into 3 files: ReplStub.c, ImpStub.c, and ExpStub.c.
+ Changed to use LPTSTR etc.
+ Added handling of getinfo and setinfo APIs when service isn't started.
+ Tell NetRpc.h macros that we need replicator service.
+ 05-Feb-1992 JohnRo
+ Added debug messages when service is not started.
+ 13-Feb-1992 JohnRo
+ Moved section name equates to ConfName.h.
+ 21-Feb-1992 JohnRo
+ Make NetReplImportDir{Del,Enum,Get,Lock,Unlock} work w/o svc running.
+ Fixed usage of union/container.
+ 21-Feb-1992 JohnRo
+ Changed ImportDirBuildApiRecord() so master name is not a UNC name.
+ 27-Feb-1992 JohnRo
+ Preserve state from last time service was running.
+ Changed state not started to state never replicated.
+ 15-Mar-1992 JohnRo
+ Update registry with new values.
+ 23-Mar-1992 JohnRo
+ Fixed enum when service is running.
+ 09-Jul-1992 JohnRo
+ RAID 10503: srv mgr: repl dialog doesn't come up.
+ Avoid compiler warnings.
+ Use PREFIX_ equates.
+ 27-Jul-1992 JohnRo
+ RAID 2274: repl svc should impersonate caller.
+ 09-Nov-1992 JohnRo
+ RAID 7962: Repl APIs in wrong role kill svc.
+ Fix remote repl admin.
+ 02-Apr-1993 JohnRo
+ Use NetpKdPrint() where possible.
+ Made changes suggested by PC-LINT 5.0
+ Removed some obsolete comments about retrying APIs.
+
+--*/
+
+
+// These must be included first:
+
+#include <windows.h> // IN, DWORD, etc.
+#include <lmcons.h> // NET_API_STATUS, etc.
+#include <netdebug.h> // Needed by <netrpc.h>.
+#include <repldefs.h> // Needed by <impdir.h>.
+#include <rpc.h> // Needed by <netrpc.h>.
+
+// These may be included in any order:
+
+#include <dirname.h> // ReplIsDirNameValid().
+#include <impdir.h> // ImportDirIsApiRecordValid(), etc.
+#include <lmrepl.h> // My prototypes, etc.
+#include <lmerr.h> // NERR_ and ERROR_ equates, NO_ERROR.
+#include <lmsname.h> // SERVICE_ name equates.
+#include <netlib.h> // NetpSetParmError() macro.
+#include <netrpc.h> // NET_REMOTE macros.
+#include <prefix.h> // PREFIX_ equates.
+#include <repl.h> // MIDL-generated NetrRepl prototypes.
+#include <rpcutil.h> // GENERIC_INFO_CONTAINER, etc.
+
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetReplImportDirAdd (
+ IN LPCWSTR UncServerName OPTIONAL,
+ IN DWORD Level,
+ IN const LPBYTE Buf,
+ OUT LPDWORD ParmError OPTIONAL // Set implicitly by NetpSetParmError().
+ )
+{
+ NET_API_STATUS ApiStatus;
+ LPIMPORT_CONTAINER ApiUnion = (LPVOID) &Buf;
+
+ NET_REMOTE_TRY_RPC
+
+ //
+ // Try RPC (local or remote) version of API.
+ //
+ ApiStatus = NetrReplImportDirAdd(
+ (LPWSTR)UncServerName,
+ Level,
+ ApiUnion,
+ ParmError);
+
+ NET_REMOTE_RPC_FAILED("NetReplImportDirAdd",
+ UncServerName,
+ ApiStatus,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_REPL )
+
+ //
+ // No downlevel version to call
+ //
+ ApiStatus = ERROR_NOT_SUPPORTED;
+
+ NET_REMOTE_END
+
+ if (ApiStatus == NERR_ServiceNotInstalled) {
+ LPREPL_IDIR_INFO_0 ApiRecord; // level 0 by definition
+
+ IF_DEBUG(DLLSTUB) {
+ NetpKdPrint(( PREFIX_REPL
+ "NetReplImportDirAdd: running w/o service.\n" ));
+ }
+
+ //
+ // Check for caller errors.
+ //
+ NetpSetParmError( PARM_ERROR_UNKNOWN ); // Assume error until proven...
+ if (Level != 0) {
+ return (ERROR_INVALID_LEVEL);
+ }
+ NetpAssert( ApiUnion != NULL );
+ if ((ApiUnion->Info0) == NULL) {
+ return (ERROR_INVALID_PARAMETER);
+ }
+ ApiRecord = ApiUnion->Info0; // level 0 by definition
+ NetpAssert( ApiRecord != NULL );
+ if ( !ReplIsDirNameValid(ApiRecord->rpid0_dirname) ) {
+ NetpSetParmError( 1 ); // Error in first field in ApiRecord.
+ return (ERROR_INVALID_PARAMETER);
+ }
+
+ // Make sure there is no existing record for this directory.
+ if (ImportDirConfigDataExists(
+ (LPWSTR)UncServerName,
+ ApiRecord->rpid0_dirname )) {
+
+ return (ERROR_ALREADY_EXISTS);
+ }
+
+ // Write the new or revised data.
+ ApiStatus = ImportDirWriteConfigData (
+ (LPWSTR)UncServerName,
+ ApiRecord->rpid0_dirname,
+ REPL_STATE_NEVER_REPLICATED,
+ NULL, // no master.
+ (DWORD) 0, // last update time, secs since 1970.
+ (DWORD) 0, // lock count
+ (DWORD) 0 ); // time of first lock, secs since 1970.
+
+ if (ApiStatus == NO_ERROR) {
+
+ //
+ // Everything went OK. Tell caller.
+ //
+ NetpSetParmError( PARM_ERROR_NONE );
+ }
+
+ }
+ return (ApiStatus);
+
+} // NetReplImportDirAdd
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetReplImportDirDel (
+ IN LPCWSTR UncServerName OPTIONAL,
+ IN LPCWSTR DirName
+ )
+{
+ NET_API_STATUS ApiStatus;
+
+ NET_REMOTE_TRY_RPC
+
+ //
+ // Try RPC (local or remote) version of API.
+ //
+ ApiStatus = NetrReplImportDirDel(
+ (LPWSTR)UncServerName,
+ (LPWSTR)DirName);
+
+ NET_REMOTE_RPC_FAILED("NetReplImportDirDel",
+ UncServerName,
+ ApiStatus,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_REPL )
+
+ //
+ // No downlevel version to call
+ //
+ ApiStatus = ERROR_NOT_SUPPORTED;
+
+ NET_REMOTE_END
+
+ //
+ // Handle API when service isn't running...
+ //
+ if (ApiStatus == NERR_ServiceNotInstalled) {
+
+ IF_DEBUG(DLLSTUB) {
+ NetpKdPrint(( PREFIX_REPL
+ "NetReplImportDirDel: processing w/o svc.\n" ));
+ }
+
+ //
+ // Check for caller errors.
+ //
+ if (DirName == NULL) {
+ return (ERROR_INVALID_PARAMETER);
+ }
+
+ IF_DEBUG(DLLSTUB) {
+ NetpKdPrint(( PREFIX_REPL
+ "NetReplImportDirDel: deleting API record for '"
+ FORMAT_LPTSTR "'...\n", DirName ));
+ }
+
+ if ( !ReplIsDirNameValid((LPWSTR)DirName) ) {
+ return (ERROR_INVALID_PARAMETER);
+ }
+
+ if (! ImportDirConfigDataExists( (LPWSTR)UncServerName, (LPWSTR)DirName )) {
+ return (NERR_UnknownDevDir);
+ }
+
+ //
+ // Write config data for this import directory.
+ //
+ ApiStatus = ImportDirDeleteConfigData( (LPWSTR)UncServerName, (LPWSTR)DirName );
+
+ IF_DEBUG(DLLSTUB) {
+ NetpKdPrint(( PREFIX_REPL
+ "NetReplImportDirDel: done deleting API record, "
+ "API status is " FORMAT_API_STATUS ".\n",
+ ApiStatus ));
+ }
+
+ }
+
+ return ApiStatus;
+
+} // NetReplImportDirDel
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetReplImportDirEnum (
+ IN LPCWSTR UncServerName OPTIONAL,
+ IN DWORD Level,
+ OUT LPBYTE * BufPtr,
+ IN DWORD PrefMaxSize,
+ OUT LPDWORD EntriesRead,
+ OUT LPDWORD TotalEntries,
+ IN OUT LPDWORD ResumeHandle OPTIONAL
+ )
+{
+ NET_API_STATUS ApiStatus;
+ GENERIC_INFO_CONTAINER GenericInfoContainer;
+ GENERIC_ENUM_STRUCT InfoStruct;
+
+ GenericInfoContainer.Buffer = NULL;
+ GenericInfoContainer.EntriesRead = 0;
+
+ InfoStruct.Container = &GenericInfoContainer;
+ InfoStruct.Level = Level;
+
+ *BufPtr = NULL; // Must be NULL so RPC knows to fill it in.
+
+ NET_REMOTE_TRY_RPC
+
+ //
+ // Try RPC (local or remote) version of API.
+ //
+ ApiStatus = NetrReplImportDirEnum(
+ (LPWSTR)UncServerName,
+ // Level,
+ (LPIMPORT_ENUM_STRUCT) (LPVOID) &InfoStruct,
+ PrefMaxSize,
+ // EntriesRead,
+ TotalEntries,
+ ResumeHandle);
+ if ((ApiStatus == NERR_Success) || (ApiStatus == ERROR_MORE_DATA)) {
+ *BufPtr = (LPBYTE) GenericInfoContainer.Buffer;
+ *EntriesRead = GenericInfoContainer.EntriesRead;
+ }
+
+ NET_REMOTE_RPC_FAILED("NetReplImportDirEnum",
+ UncServerName,
+ ApiStatus,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_REPL )
+
+ //
+ // No downlevel version to call
+ //
+ ApiStatus = ERROR_NOT_SUPPORTED;
+
+ NET_REMOTE_END
+
+ //
+ // Handle service not running by reading config data directly.
+ //
+ if (ApiStatus == NERR_ServiceNotInstalled) {
+
+ IF_DEBUG(DLLSTUB) {
+ NetpKdPrint(( PREFIX_REPL
+ "NetReplImportDirEnum: processing w/o svc.\n" ));
+ }
+
+ NetpSetOptionalArg( ResumeHandle, 0 );
+
+ ApiStatus = ImportDirEnumApiRecords(
+ (LPWSTR)UncServerName,
+ Level,
+ BufPtr,
+ PrefMaxSize,
+ EntriesRead,
+ TotalEntries
+ );
+
+ }
+
+ return ApiStatus;
+
+} // NetReplImportDirEnum
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetReplImportDirGetInfo (
+ IN LPCWSTR UncServerName OPTIONAL,
+ IN LPCWSTR DirName,
+ IN DWORD Level,
+ OUT LPBYTE * BufPtr
+ )
+{
+ NET_API_STATUS ApiStatus;
+
+ *BufPtr = NULL; // Must be NULL so RPC knows to fill it in.
+
+ NET_REMOTE_TRY_RPC
+
+ IF_DEBUG( DLLSTUB ) {
+ NetpKdPrint(( PREFIX_REPL
+ "NetReplImportDirGetInfo: union at " FORMAT_LPVOID ".\n",
+ (LPVOID) BufPtr ));
+ }
+
+ //
+ // Try RPC (local or remote) version of API.
+ //
+ ApiStatus = NetrReplImportDirGetInfo(
+ (LPWSTR)UncServerName,
+ (LPWSTR)DirName,
+ Level,
+ (LPIMPORT_CONTAINER) (LPVOID) BufPtr);
+
+ NET_REMOTE_RPC_FAILED("NetReplImportDirGetInfo",
+ UncServerName,
+ ApiStatus,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_REPL )
+
+ //
+ // No downlevel version to call
+ //
+ ApiStatus = ERROR_NOT_SUPPORTED;
+
+ NET_REMOTE_END
+
+
+ if (ApiStatus == NERR_ServiceNotInstalled) {
+
+ IF_DEBUG(DLLSTUB) {
+ NetpKdPrint(( PREFIX_REPL
+ "NetReplImportDirGetInfo: processing w/o svc.\n" ));
+ }
+
+ ApiStatus = ImportDirGetApiRecord(
+ (LPWSTR)UncServerName,
+ (LPWSTR)DirName,
+ Level,
+ BufPtr );
+
+
+ }
+
+ return ApiStatus;
+
+} // NetReplImportDirGetInfo
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetReplImportDirLock (
+ IN LPCWSTR UncServerName OPTIONAL,
+ IN LPCWSTR DirName
+ )
+{
+ NET_API_STATUS ApiStatus;
+
+ NET_REMOTE_TRY_RPC
+
+ //
+ // Try RPC (local or remote) version of API.
+ //
+ ApiStatus = NetrReplImportDirLock(
+ (LPWSTR)UncServerName,
+ (LPWSTR)DirName);
+
+ NET_REMOTE_RPC_FAILED("NetReplImportDirLock",
+ UncServerName,
+ ApiStatus,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_REPL )
+
+ //
+ // No downlevel version to call
+ //
+ ApiStatus = ERROR_NOT_SUPPORTED;
+
+ NET_REMOTE_END
+
+
+ if (ApiStatus == NERR_ServiceNotInstalled) {
+
+ IF_DEBUG(DLLSTUB) {
+ NetpKdPrint(( PREFIX_REPL
+ "NetReplImportDirLock: processing w/o svc.\n" ));
+ }
+
+ ApiStatus = ImportDirLockInRegistry(
+ (LPWSTR)UncServerName,
+ (LPWSTR)DirName );
+
+ }
+ return ApiStatus;
+
+} // NetReplImportDirLock
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetReplImportDirUnlock (
+ IN LPCWSTR UncServerName OPTIONAL,
+ IN LPCWSTR DirName,
+ IN DWORD UnlockForce
+ )
+{
+ NET_API_STATUS ApiStatus;
+
+ NET_REMOTE_TRY_RPC
+
+ //
+ // Try RPC (local or remote) version of API.
+ //
+ ApiStatus = NetrReplImportDirUnlock(
+ (LPWSTR)UncServerName,
+ (LPWSTR)DirName,
+ UnlockForce);
+
+ NET_REMOTE_RPC_FAILED("NetReplImportDirUnlock",
+ UncServerName,
+ ApiStatus,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_REPL )
+
+ //
+ // No downlevel version to call
+ //
+ ApiStatus = ERROR_NOT_SUPPORTED;
+
+ NET_REMOTE_END
+
+ if (ApiStatus == NERR_ServiceNotInstalled) {
+
+ IF_DEBUG(DLLSTUB) {
+ NetpKdPrint(( PREFIX_REPL
+ "NetReplImportDirUnlock: processing w/o svc.\n" ));
+ }
+
+ ApiStatus = ImportDirUnlockInRegistry(
+ (LPWSTR)UncServerName,
+ (LPWSTR)DirName,
+ UnlockForce );
+
+ }
+
+ return ApiStatus;
+
+} // NetReplImportDirUnlock
diff --git a/private/net/svcdlls/repl/client/makefile b/private/net/svcdlls/repl/client/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/net/svcdlls/repl/client/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/net/svcdlls/repl/client/replbind.c b/private/net/svcdlls/repl/client/replbind.c
new file mode 100644
index 000000000..5ee9acf9a
--- /dev/null
+++ b/private/net/svcdlls/repl/client/replbind.c
@@ -0,0 +1,275 @@
+/*++
+
+Copyright (c) 1991-1993 Microsoft Corporation
+
+Module Name:
+
+ ReplBind.c
+
+Abstract:
+
+ Routines which use RPC to bind and unbind the client to the replicator
+ service.
+
+Author:
+
+ John Rogers (JohnRo) 16-Jan-1992
+
+Environment:
+
+ User Mode -Win32
+
+Revision History:
+
+ 16-Jan-1992 JohnRo
+ Created the repl service RPC stuff from Rita's workstation RPC stuff.
+ I'm just going to use identify handles if possible.
+ 23-Jan-1992 JohnRo
+ Correct calls to NetpBindRpc.
+ 27-Jan-1992 JohnRo
+ Changed to use LPTSTR etc.
+ 30-Jan-1992 JohnRo
+ Made changes suggested by PC-LINT.
+ 05-Feb-1992 JohnRo
+ Changes required by new RPC.
+ 08-Apr-1992 JohnRo
+ Fixed UNICODE handling.
+ 08-Aug-1992 JohnRo
+ RAID 2252 (old 9963): repl should prevent export on Windows/NT.
+ 16-Dec-1992 JohnRo
+ Made changes suggested by PC-LINT 5.0
+ Added some IN and OUT keywords.
+ 30-Apr-1993 JohnRo
+ Use NetpKdPrint() where possible.
+ Use PREFIX_ equates.
+
+--*/
+
+
+// These must be included first:
+
+#include <windef.h> // IN, DWORD, etc.
+#include <rpc.h> // Needed by <repl.h>
+#include <lmcons.h> // Needed by <lmapibuf.h>
+
+// These may be included in any order:
+
+#include <netdebug.h> // NetpKdPrint(), FORMAT_ equates.
+#include <prefix.h> // PREFIX_ equates.
+#include <repl.h> // Generated by MIDL compiler.
+#include <repldefs.h> // IF_DEBUG(), etc.
+#include <replname.h> // REPLICATOR_INTERFACE_NAME.
+#include <rpcutil.h> // NetpBindRpc(), etc.
+
+
+// Define this for ifdef'ed code below.
+#define IMPERSONATE_FOR_ALL_APIS
+
+
+
+#if 0
+
+handle_t
+REPL_IMPERSONATE_HANDLE_bind(
+ IN REPL_IMPERSONATE_HANDLE ServerName
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is called from the replicator service client stubs when
+ it is necessary create an RPC binding to the server end with
+ impersonation level of impersonation.
+
+Arguments:
+
+ ServerName - A pointer to a string containing the name of the server
+ to bind with.
+
+Return Value:
+
+ The binding handle is returned to the stub routine. If the bind is
+ unsuccessful, a NULL will be returned.
+
+--*/
+{
+ handle_t BindHandle;
+ RPC_STATUS RpcStatus;
+
+ IF_DEBUG( DLLSTUB ) {
+ NetpKdPrint(( PREFIX_REPL
+ "REPL_IMPERSONATE_HANDLE_bind: server name " FORMAT_LPTSTR
+ ".\n", ServerName ? ServerName : (LPTSTR) TEXT("--NONE--") ));
+ }
+
+ RpcStatus = NetpBindRpc (
+ ServerName,
+ REPLICATOR_INTERFACE_NAME,
+ (LPTSTR) TEXT("Security=Impersonation Static True"),
+ &BindHandle
+ );
+
+ if (RpcStatus != RPC_S_OK) {
+ NetpKdPrint(( PREFIX_REPL
+ "REPL_IMPERSONATE_HANDLE_bind failed: " FORMAT_NTSTATUS "\n",
+ RpcStatus ));
+ }
+
+ return BindHandle;
+}
+
+#endif // 0
+
+
+handle_t
+REPL_IDENTIFY_HANDLE_bind(
+ IN REPL_IDENTIFY_HANDLE ServerName
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is called from the replicator service client stubs when
+ it is necessary create an RPC binding to the server end with
+ identification level of impersonation.
+
+Arguments:
+
+ ServerName - A pointer to a string containing the name of the server
+ to bind with.
+
+Return Value:
+
+ The binding handle is returned to the stub routine. If the bind is
+ unsuccessful, a NULL will be returned.
+
+--*/
+{
+ handle_t BindHandle;
+ RPC_STATUS RpcStatus;
+
+ IF_DEBUG( DLLSTUB ) {
+ LPTSTR DebugServerName;
+ LPSTR DebugText;
+
+ if (ServerName != NULL) {
+ DebugServerName = ServerName;
+ } else {
+ DebugServerName = (LPTSTR) TEXT("--NONE--");
+ }
+
+ DebugText = PREFIX_REPL "REPL_IDENTIFY_HANDLE_bind: "
+#ifndef IMPERSONATE_FOR_ALL_APIS
+ "(identify) "
+#else
+ "(really impersonate) "
+#endif
+ "server name " FORMAT_LPTSTR ".\n";
+
+ NetpKdPrint((
+ DebugText,
+ DebugServerName ));
+ }
+
+ RpcStatus = NetpBindRpc (
+ ServerName,
+ REPLICATOR_INTERFACE_NAME,
+#ifndef IMPERSONATE_FOR_ALL_APIS
+ (LPTSTR) TEXT("Security=Identification Static True"),
+#else
+ (LPTSTR) TEXT("Security=Impersonation Static True"),
+#endif
+ &BindHandle
+ );
+
+ if (RpcStatus != RPC_S_OK) {
+ NetpKdPrint(( PREFIX_REPL
+ "REPL_IDENTIFY_HANDLE_bind failed: " FORMAT_NTSTATUS "\n",
+ RpcStatus ));
+ }
+
+ return BindHandle;
+}
+
+
+
+#if 0
+
+void
+REPL_IMPERSONATE_HANDLE_unbind(
+ IN REPL_IMPERSONATE_HANDLE ServerName,
+ handle_t BindHandle
+ )
+
+/*++
+
+Routine Description:
+
+ This routine calls a common unbind routine that is shared by all services.
+ This routine is called from the replicator service client stubs when it is
+ necessary to unbind from the server end.
+
+Arguments:
+
+ ServerName - This is the name of the server from which to unbind.
+
+ BindingHandle - This is the binding handle that is to be closed.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ UNREFERENCED_PARAMETER(ServerName);
+
+ IF_DEBUG(RPCBIND) {
+ NetpKdPrint(( PREFIX_REPL
+ "REPL_IMPERSONATE_HANDLE_unbind: handle="
+ FORMAT_HEX_DWORD "\n", BindHandle ));
+ }
+
+ (void) NetpUnbindRpc(BindHandle);
+}
+
+#endif // 0
+
+
+void
+REPL_IDENTIFY_HANDLE_unbind(
+ IN REPL_IDENTIFY_HANDLE ServerName,
+ handle_t BindHandle
+ )
+
+/*++
+
+Routine Description:
+
+ This routine calls a common unbind routine that is shared by all services.
+ This routine is called from the server service client stubs when it is
+ necessary to unbind from a server.
+
+Arguments:
+
+ ServerName - This is the name of the server from which to unbind.
+
+ BindingHandle - This is the binding handle that is to be closed.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ UNREFERENCED_PARAMETER(ServerName);
+
+ IF_DEBUG(RPCBIND) {
+ NetpKdPrint(( PREFIX_REPL
+ "REPL_IDENTIFY_HANDLE_unbind: handle="
+ FORMAT_HEX_DWORD "\n", BindHandle ));
+ }
+
+ (void) NetpUnbindRpc(BindHandle);
+}
diff --git a/private/net/svcdlls/repl/client/replstub.c b/private/net/svcdlls/repl/client/replstub.c
new file mode 100644
index 000000000..1b766421c
--- /dev/null
+++ b/private/net/svcdlls/repl/client/replstub.c
@@ -0,0 +1,369 @@
+/*++
+
+Copyright (c) 1991-1993 Microsoft Corporation
+
+Module Name:
+
+ ReplStub.c
+
+Abstract:
+
+ Client stubs of the replicator service config APIs.
+
+Author:
+
+ John Rogers (JohnRo) 17-Dec-1991
+
+Environment:
+
+ User Mode - Win32
+
+Revision History:
+
+ 17-Dec-1991 JohnRo
+ Created dummy file.
+ 17-Dec-1991 JohnRo
+ Actually include my header file (LmRepl.h) so we can test against it.
+ 17-Jan-1992 JohnRo
+ Wrote stubs for first 3 RPCable APIs.
+ 20-Jan-1992 JohnRo
+ Added import APIs, config APIs, and rest of export APIs.
+ 27-Jan-1992 JohnRo
+ Split stubs into 3 files: ReplStub.c, ImpStub.c, and ExpStub.c.
+ Changed to use LPTSTR etc.
+ Added handling of getinfo and setinfo APIs when service isn't started.
+ Tell NetRpc.h macros that we need replicator service.
+ 05-Feb-1992 JohnRo
+ Added debug messages when service is not started.
+ 18-Feb-1992 JohnRo
+ NetReplGetInfo() does too much with bad info level.
+ Fixed usage of union/container.
+ Added code for NetReplSetInfo() when service is not started.
+ 15-Mar-1992 JohnRo
+ Added support for setinfo info levels in ReplConfigIsLevelValid().
+ 19-Jul-1992 JohnRo
+ RAID 10503: srv mgr: repl dialog doesn't come up.
+ Use PREFIX_ equates.
+ 20-Jul-1992 JohnRo
+ RAID 2252: repl should prevent export on Windows/NT.
+ 14-Aug-1992 JohnRo
+ RAID 3601: repl APIs should checked import & export lists.
+ Use PREFIX_NETAPI instead of PREFIX_REPL for repl API stubs.
+ 01-Dec-1992 JohnRo
+ RAID 3844: remote NetReplSetInfo uses local machine type.
+ 05-Jan-1993 JohnRo
+ Repl WAN support (get rid of repl name list limits).
+ Made changes suggested by PC-LINT 5.0
+ Corrected debug bit usage.
+ Removed some obsolete comments about retrying APIs.
+
+--*/
+
+
+// These must be included first:
+
+#include <windows.h>
+#include <lmcons.h> // NET_API_STATUS, etc.
+#include <netdebug.h> // NetpKdPrint(), needed by <netrpc.h>, etc.
+#include <rpc.h> // Needed by <netrpc.h>.
+
+// These may be included in any order:
+
+#include <lmrepl.h> // My prototypes, etc.
+#include <lmerr.h> // NERR_ and ERROR_ equates, NO_ERROR.
+#include <lmsname.h> // SERVICE_ name equates.
+#include <netlib.h> // NetpMemoryFree(), NetpSetParmError().
+#include <netrpc.h> // NET_REMOTE macros.
+#include <prefix.h> // PREFIX_ equates.
+#include <repl.h> // MIDL-generated NetrRepl prototypes.
+#include <replconf.h> // ReplConfig API workers.
+#include <repldefs.h> // IF_DEBUG(), etc.
+
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetReplGetInfo (
+ IN LPCWSTR UncServerName OPTIONAL,
+ IN DWORD Level,
+ OUT LPBYTE * BufPtr
+ )
+{
+ NET_API_STATUS ApiStatus;
+ LPTSTR ExportList = NULL;
+ LPTSTR ImportList = NULL;
+
+ *BufPtr = NULL; // Must be NULL so RPC knows to fill it in.
+
+ NET_REMOTE_TRY_RPC
+
+ //
+ // Try RPC (local or remote) version of API.
+ //
+ ApiStatus = NetrReplGetInfo(
+ (LPWSTR)UncServerName,
+ Level,
+ (LPCONFIG_CONTAINER) (LPVOID) BufPtr);
+
+ NET_REMOTE_RPC_FAILED("NetReplGetInfo",
+ (LPWSTR)UncServerName,
+ ApiStatus,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_REPL )
+
+ //
+ // No downlevel version to call
+ //
+ ApiStatus = ERROR_NOT_SUPPORTED;
+
+ NET_REMOTE_END
+
+ if (ApiStatus == NERR_ServiceNotInstalled) {
+ LPVOID ApiRecord = NULL;
+ DWORD Role, Interval, Pulse, GuardTime, Random;
+ TCHAR ExportPath[PATHLEN+1];
+ TCHAR ImportPath[PATHLEN+1];
+ TCHAR LogonUserName[UNLEN+1];
+
+ IF_DEBUG(DLLSTUB) {
+ NetpKdPrint(( PREFIX_NETAPI
+ "NetReplGetInfo: running w/o service.\n" ));
+ }
+
+ if ( !ReplConfigIsLevelValid( Level, FALSE /* not setinfo */ )) {
+ ApiStatus = ERROR_INVALID_LEVEL;
+ } else {
+ NetpAssert( *BufPtr == NULL );
+
+ // Read config data for the replicator.
+ ApiStatus = ReplConfigRead(
+ (LPWSTR)UncServerName,
+ & Role,
+ ExportPath,
+ &ExportList, // Alloc and set ptr.
+ ImportPath,
+ &ImportList, // Alloc and set ptr.
+ LogonUserName,
+ & Interval,
+ & Pulse,
+ & GuardTime,
+ & Random );
+ if (ApiStatus == NO_ERROR) {
+
+ ApiStatus = ReplConfigAllocAndBuildApiRecord (
+ Level,
+ Role,
+ ExportPath,
+ ExportList,
+ ImportPath,
+ ImportList,
+ LogonUserName,
+ Interval,
+ Pulse,
+ GuardTime,
+ Random,
+ (LPBYTE *) & ApiRecord ); // alloc and set ptr
+ if (ApiStatus == NO_ERROR) {
+ NetpAssert( ApiRecord != NULL );
+ NetpAssert(
+ ReplConfigIsApiRecordValid(
+ Level, ApiRecord, NULL ) );
+ }
+ }
+
+ }
+ *BufPtr = ApiRecord; // will be NULL on error.
+
+ }
+
+ if (ExportList != NULL) {
+ NetpMemoryFree( ExportList );
+ }
+ if (ImportList != NULL) {
+ NetpMemoryFree( ImportList );
+ }
+
+ return ApiStatus;
+
+} // NetReplGetInfo
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetReplSetInfo (
+ IN LPCWSTR UncServerName OPTIONAL,
+ IN DWORD Level,
+ IN const LPBYTE Buf,
+ OUT LPDWORD ParmError OPTIONAL
+ )
+{
+ NET_API_STATUS ApiStatus;
+ LPCONFIG_CONTAINER ApiUnion = (LPVOID) &Buf;
+ LPTSTR ExportList = NULL;
+ LPTSTR ImportList = NULL;
+
+ NET_REMOTE_TRY_RPC
+
+ //
+ // Try RPC (local or remote) version of API.
+ //
+ ApiStatus = NetrReplSetInfo(
+ (LPWSTR)UncServerName,
+ Level,
+ ApiUnion,
+ ParmError);
+
+ NET_REMOTE_RPC_FAILED("NetReplSetInfo",
+ UncServerName,
+ ApiStatus,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_REPL )
+
+ //
+ // No downlevel version to call
+ //
+ ApiStatus = ERROR_NOT_SUPPORTED;
+
+ NET_REMOTE_END
+
+ if (ApiStatus == NERR_ServiceNotInstalled) {
+
+ IF_DEBUG(DLLSTUB) {
+ NetpKdPrint(( PREFIX_NETAPI
+ "NetReplSetInfo: running w/o service.\n" ));
+ }
+
+ NetpSetParmError( PARM_ERROR_UNKNOWN );
+ NetpAssert( ApiUnion != NULL );
+ if ((ApiUnion->Info0) == NULL) {
+ return (ERROR_INVALID_PARAMETER);
+ }
+
+ if (Level == 0) {
+
+ LPREPL_INFO_0 ApiRecord = ApiUnion->Info0;
+ if ( !ReplConfigIsApiRecordValid( Level,ApiRecord,ParmError ) ) {
+ ApiStatus = ERROR_INVALID_PARAMETER;
+ } else if ( !ReplConfigIsRoleAllowed(
+ (LPWSTR)UncServerName,
+ ApiRecord->rp0_role ) ) {
+
+ ApiStatus = ERROR_INVALID_PARAMETER;
+
+ } else {
+
+ IF_DEBUG( DLLSTUB ) {
+ NetpKdPrint(( PREFIX_NETAPI
+ "NetReplSetInfo: got structure:\n" ));
+ NetpDbgDisplayRepl( Level, ApiRecord );
+ }
+
+ // Change config data for the replicator.
+ ApiStatus = ReplConfigWrite(
+ (LPWSTR)UncServerName,
+ ApiRecord->rp0_role,
+ ApiRecord->rp0_exportpath,
+ ApiRecord->rp0_exportlist,
+ ApiRecord->rp0_importpath,
+ ApiRecord->rp0_importlist,
+ ApiRecord->rp0_logonusername,
+ ApiRecord->rp0_interval,
+ ApiRecord->rp0_pulse,
+ ApiRecord->rp0_guardtime,
+ ApiRecord->rp0_random );
+ }
+
+ } else if ( (Level >= 1000) && (Level <= 1003) ) {
+
+ DWORD Role, Interval, Pulse, GuardTime, Random;
+ TCHAR ExportPath[PATHLEN+1];
+ TCHAR ImportPath[PATHLEN+1];
+ TCHAR LogonUserName[UNLEN+1];
+
+ // Read config data for the replicator.
+ ApiStatus = ReplConfigRead(
+ (LPWSTR)UncServerName,
+ & Role,
+ ExportPath,
+ &ExportList, // Alloc and set ptr.
+ ImportPath,
+ &ImportList, // Alloc and set ptr.
+ LogonUserName,
+ & Interval,
+ & Pulse,
+ & GuardTime,
+ & Random );
+ if (ApiStatus == NO_ERROR) {
+
+ if (Level == 1000) {
+ LPREPL_INFO_1000 SetInfoRecord = ApiUnion->Info1000;
+
+ if ( !ReplIsIntervalValid(SetInfoRecord->rp1000_interval)) {
+ NetpSetParmError( 1 ); // first field is in error.
+ return (ERROR_INVALID_PARAMETER);
+ }
+
+ Interval = SetInfoRecord->rp1000_interval;
+ } else if (Level == 1001) {
+ LPREPL_INFO_1001 SetInfoRecord = ApiUnion->Info1001;
+
+ if ( !ReplIsPulseValid(SetInfoRecord->rp1001_pulse)) {
+ NetpSetParmError( 1 ); // first field is in error.
+ return (ERROR_INVALID_PARAMETER);
+ }
+
+ Pulse = SetInfoRecord->rp1001_pulse;
+ } else if (Level == 1002) {
+ LPREPL_INFO_1002 SetInfoRecord = ApiUnion->Info1002;
+
+ if (!ReplIsGuardTimeValid(SetInfoRecord->rp1002_guardtime)){
+ NetpSetParmError( 1 ); // first field is in error.
+ return (ERROR_INVALID_PARAMETER);
+ }
+
+ GuardTime = SetInfoRecord->rp1002_guardtime;
+ } else {
+ LPREPL_INFO_1003 SetInfoRecord = ApiUnion->Info1003;
+ NetpAssert( Level == 1003 );
+ if ( !ReplIsRandomValid( SetInfoRecord->rp1003_random ) ) {
+ NetpSetParmError( 1 ); // first field is in error.
+ return (ERROR_INVALID_PARAMETER);
+ }
+
+ Random = SetInfoRecord->rp1003_random;
+ }
+
+ // Change config data for the replicator.
+ ApiStatus = ReplConfigWrite(
+ (LPWSTR)UncServerName,
+ Role,
+ ExportPath,
+ ExportList,
+ ImportPath,
+ ImportList,
+ LogonUserName,
+ Interval,
+ Pulse,
+ GuardTime,
+ Random );
+
+ } // read of old data was OK
+
+ } else {
+ ApiStatus = ERROR_INVALID_LEVEL;
+ }
+
+ if (ApiStatus == NO_ERROR) {
+ NetpSetParmError( PARM_ERROR_NONE );
+ }
+
+ }
+
+ if (ExportList != NULL) {
+ NetpMemoryFree( ExportList );
+ }
+ if (ImportList != NULL) {
+ NetpMemoryFree( ImportList );
+ }
+
+ return ApiStatus;
+
+} // NetReplSetInfo
diff --git a/private/net/svcdlls/repl/client/report.c b/private/net/svcdlls/repl/client/report.c
new file mode 100644
index 000000000..97a78af09
--- /dev/null
+++ b/private/net/svcdlls/repl/client/report.c
@@ -0,0 +1,101 @@
+/*++
+
+Copyright (c) 1992-1993 Microsoft Corporation
+
+Module Name:
+
+ Report.c
+
+Abstract:
+
+ This file contains the client-side version of
+ ReplConfigReportBadParmValue().
+
+Author:
+
+ John Rogers (JohnRo) 24-Jan-1992
+
+Environment:
+
+ User Mode - Win32
+ Portable to any flat, 32-bit environment. (Uses Win32 typedefs.)
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+ 24-Jan-1992 JohnRo
+ Created.
+ 13-Feb-1992 JohnRo
+ Make code reflect the fact that TheValue is optional.
+ 21-Sep-1992 JohnRo
+ RAID 6685: ReplConfigRead trashes Server Manager.
+ Use PREFIX_ equates.
+ 01-Dec-1992 JohnRo
+ RAID 3844: remote NetReplSetInfo uses local machine type.
+ 02-Apr-1993 JohnRo
+ Use NetpKdPrint() where possible.
+ Do real event logging on errors.
+ Made changes suggested by PC-LINT 5.0
+
+--*/
+
+
+#include <windef.h> // IN, VOID, DWORD, LPTSTR, etc.
+#include <lmcons.h> // Needed by <ReplConf.h>
+
+#include <lmerrlog.h> // NELOG_ equates.
+#include <netdebug.h> // NetpKdPrint(), etc.
+#include <prefix.h> // PREFIX_ equates.
+#include <replconf.h> // My prototypes.
+#include <repldefs.h> // ReplErrorLog().
+#include <tstr.h> // TCHAR_EOS.
+#include <winerror.h> // ERROR_, NO_ERROR equates.
+
+
+// Routine to report a bad parm value.
+// There are two different versions of this routine,
+// in client/report.c and server/parse.c
+// client/report.c is callable whether or not service is started.
+// server/parse.c only runs when service is starting; it talks to the service
+// controller.
+
+VOID
+ReplConfigReportBadParmValue(
+ IN LPTSTR UncServerName OPTIONAL,
+ IN LPTSTR SwitchName,
+ IN LPTSTR TheValue OPTIONAL
+ )
+{
+
+ NetpAssert( SwitchName != NULL );
+
+ if ( (TheValue == NULL) || ( (*TheValue) == TCHAR_EOS ) ) {
+
+ NetpKdPrint(( PREFIX_REPL "Bad value given for "
+ FORMAT_LPTSTR " switch on machine " FORMAT_LPTSTR ".\n",
+ SwitchName,
+ UncServerName ? UncServerName : (LPTSTR) TEXT("local") ));
+
+ } else {
+
+ NetpKdPrint(( PREFIX_REPL "Bad value '" FORMAT_LPTSTR "' given for "
+ FORMAT_LPTSTR " switch on machine " FORMAT_LPTSTR ".\n",
+ TheValue, SwitchName,
+ UncServerName ? UncServerName : (LPTSTR) TEXT("local") ));
+ }
+
+ // At this point the server-side calls ReplFinish. But we're running
+ // on the client-side of the RPC call, and the replicator isn't even
+ // up.
+
+ //
+ // Record an event for later use.
+ //
+ ReplErrorLog(
+ (LPCTSTR) UncServerName, // Where to log the error.
+ NELOG_ReplSysErr, // Log code (BUGBUG: better?)
+ ERROR_INVALID_DATA, // Data.
+ SwitchName, // BUGBUG: Invalid for this log code?
+ TheValue ); // BUGBUG: Invalid for this log code?
+
+} // ReplConfigReportBadParmValue
diff --git a/private/net/svcdlls/repl/client/sources b/private/net/svcdlls/repl/client/sources
new file mode 100644
index 000000000..a1e7f03f4
--- /dev/null
+++ b/private/net/svcdlls/repl/client/sources
@@ -0,0 +1,95 @@
+!IF 0
+
+Copyright (c) 1989-92 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+Author:
+
+ Steve Wood (stevewo) 12-Apr-1990
+
+NOTE: Commented description of this file is in \nt\public\oak\bin\sources.tpl
+
+!ENDIF
+
+#
+# The MAJORCOMP and MINORCOMP variables are defined
+# so that $(MAJORCOMP)$(MINORCOMP)filename can be used in
+# cross compiling to provide unique filenames in a flat namespace.
+#
+
+MAJORCOMP=repl
+MINORCOMP=cli
+
+
+NTPROFILEINPUT=YES
+
+#
+# The TARGETNAME variable is defined by the developer. It is the name of
+# the target (component) that is being built by this makefile. It
+# should NOT include any path or file extension information.
+#
+
+TARGETNAME=replcli
+
+#
+# The TARGETPATH and TARGETTYPE variables are defined by the developer.
+# The first specifies where the target is to be build. The second specifies
+# the type of target (either PROGRAM, DYNLINK, LIBRARY, UMAPPL_NOLIB or
+# BOOTPGM). UMAPPL_NOLIB is used when you're only building user-mode
+# apps and don't need to build a library.
+#
+
+TARGETPATH=obj
+
+# Pick one of the following and delete the others
+TARGETTYPE=LIBRARY
+
+#
+# The TARGETLIBS specifies additional libraries to link with you target
+# image. Each library path specification should contain an asterisk (*)
+# where the machine specific subdirectory name should go.
+#
+
+TARGETLIBS=
+
+#
+# The INCLUDES variable specifies any include paths that are specific to
+# this source directory. Separate multiple directory paths with single
+# semicolons. Relative path specifications are okay.
+#
+
+INCLUDES=..;..\common;..\..\..\inc;..\..\..\..\inc
+
+#
+# The SOURCES variable is defined by the developer. It is a list of all the
+# source files for this component. Each source file should be on a separate
+# line using the line continuation character. This will minimize merge
+# conflicts if two developers adding source files to the same component.
+#
+
+!IFNDEF DISABLE_NET_UNICODE
+UNICODE=1
+NET_C_DEFINES=-DUNICODE
+!ENDIF
+
+USE_CRTDLL=1
+
+SOURCES= \
+ ExpStub.c \
+ ImpStub.c \
+ ReplBind.c \
+ ReplStub.c \
+ repl_c.c \
+ Report.c
+
+C_DEFINES=-DRPC_NO_WINDOWS_H
diff --git a/private/net/svcdlls/repl/common/abspath.c b/private/net/svcdlls/repl/common/abspath.c
new file mode 100644
index 000000000..6f73022d0
--- /dev/null
+++ b/private/net/svcdlls/repl/common/abspath.c
@@ -0,0 +1,86 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ AbsPath.c
+
+Abstract:
+
+ This file contains ReplCheckAbsPathSyntax().
+
+Author:
+
+ John Rogers (JohnRo) 27-Jan-1992
+
+Environment:
+
+ User Mode - Win32
+ Portable to any flat, 32-bit environment. (Uses Win32 typedefs.)
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+ 27-Jan-1992 JohnRo
+ Created as part of replconf stuff..
+ 21-Feb-1992 JohnRo
+ Extracted this code for slightly more general use.
+ 19-Mar-1992 JohnRo
+ Richard Firth fixed a canon bug, which I was unintentionally
+ depending on here.
+ 20-Mar-1992 JohnRo
+ Include header file with my prototype, just to be on the safe side.
+ 17-Aug-1992 JohnRo
+ RAID 3174: NetReplSetInfo (and repl registry) allow bad paths.
+
+--*/
+
+
+// These must be included first:
+
+#include <windef.h> // MAX_PATH, etc.
+#include <lmcons.h> // LAN Manager common definitions
+
+// These can be in any order:
+
+#include <icanon.h> // I_NetPathCanonicalize(), ITYPE_ equates.
+#include <repldefs.h> // My prototype.
+#include <winerror.h> // ERROR_, NO_ERROR equates.
+
+
+
+
+NET_API_STATUS
+ReplCheckAbsPathSyntax (
+ IN LPTSTR AbsPath
+ )
+{
+ DWORD ActualType = 0; // 0 means we don't know yet.
+ NET_API_STATUS ApiStatus;
+ TCHAR CanonBuf[MAX_PATH];
+
+ ApiStatus = I_NetPathCanonicalize(
+ NULL,
+ AbsPath,
+ CanonBuf,
+ sizeof(CanonBuf),
+ NULL,
+ &ActualType,
+ 0);
+
+ //
+ // Check the type of the input.
+ //
+
+ if (ApiStatus != NO_ERROR) {
+ return (ApiStatus);
+ } else if (ActualType == ITYPE_PATH_ABSD) { // abs path with drive.
+ return (NO_ERROR);
+ } else {
+ return (ERROR_INVALID_DATA);
+ }
+
+ /*NOTREACHED*/
+
+} // ReplCheckAbsPathSyntax
diff --git a/private/net/svcdlls/repl/common/allowrol.c b/private/net/svcdlls/repl/common/allowrol.c
new file mode 100644
index 000000000..fa6bc12ae
--- /dev/null
+++ b/private/net/svcdlls/repl/common/allowrol.c
@@ -0,0 +1,130 @@
+/*++
+
+Copyright (c) 1992-1993 Microsoft Corporation
+
+Module Name:
+
+ AllowRol.c
+
+Abstract:
+
+ Just contains ReplConfigIsRoleAllowed().
+
+Author:
+
+ John Rogers (JohnRo) 07-Aug-1992
+
+Revision History:
+
+ 07-Aug-1992 JohnRo
+ Created for RAID 2252: repl should prevent export on Windows/NT.
+ 01-Dec-1992 JohnRo
+ RAID 3844: remote NetReplSetInfo uses local machine type.
+ 06-Apr-1993 JohnRo
+ RAID 1938: Replicator un-ACLs files when not given enough permission.
+ 30-Apr-1993 JohnRo
+ Use NetpKdPrint() where possible.
+
+--*/
+
+
+// These must be included first:
+
+#include <nt.h> // NT_PRODUCT_TYPE, etc.
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windows.h> // BOOL, etc.
+#include <lmcons.h> // NET_API_STATUS.
+
+// These may be included in any order:
+
+#include <netdebug.h> // NetpAssert(), NetpKdPrint(), FORMAT_ equates.
+#include <netlibnt.h> // NetpGetProductType().
+#include <prefix.h> // PREFIX_ equates.
+#include <replconf.h> // My prototype.
+#include <repldefs.h> // ReplRoleIncludesExport(), ReplRoleIsValid(), etc.
+#include <winerror.h> // ERROR_ and NO_ERROR equates.
+
+
+//
+// Return this value if we aren't sure whether or not to allow the role.
+//
+#define DEFAULT_ROLE_ALLOWED TRUE
+
+
+BOOL
+ReplConfigIsRoleAllowed(
+ IN LPTSTR UncServerName OPTIONAL,
+ IN DWORD Role
+ )
+
+/*++
+
+Routine Description:
+
+ Indicates whether the given role is allowed based on the current product
+ type. Callable whether or not service is started.
+
+Arguments:
+
+ Role - the desired replicator role.
+
+Return Value:
+
+ TRUE iff Role is valid and Role is consistent with the current product type.
+
+--*/
+
+{
+ NET_API_STATUS ApiStatus;
+ NT_PRODUCT_TYPE ProductType;
+
+ if ( !ReplIsRoleValid( Role ) ) {
+ return (FALSE);
+ }
+
+ //
+ // Ask NT what type of system we're running on.
+ //
+ ApiStatus = NetpGetProductType(
+ UncServerName,
+ &ProductType );
+
+ if (ApiStatus != NO_ERROR) {
+
+ NetpKdPrint(( PREFIX_REPL "ReplConfigIsRoleAllowed: unexpected failure "
+ "of NetpGetProductType(); assuming default.\n" ));
+
+ return (DEFAULT_ROLE_ALLOWED);
+ }
+
+ //
+ // Decide what to do based on the product type we got back.
+ //
+ if ((ProductType == NtProductLanManNt)
+ || (ProductType == NtProductServer) ) {
+
+ return (TRUE); // All roles are valid with NT AS.
+
+ } else if (ProductType == NtProductWinNt) {
+
+ if (ReplRoleIncludesExport( Role ) ) {
+ return (FALSE);
+ } else {
+ NetpAssert( Role == REPL_ROLE_IMPORT );
+ return (TRUE); // Import is allowed on all product types.
+ }
+
+ /*NOTREACHED*/
+
+ } else {
+ NetpKdPrint(( PREFIX_REPL "ReplConfigIsRoleAllowed: unexpected product "
+ "type " FORMAT_DWORD "\n", ProductType ));
+ NetpAssert( FALSE ); // Unexpected product type.
+
+ return (DEFAULT_ROLE_ALLOWED);
+ }
+
+ /*NOTREACHED*/
+
+}
diff --git a/private/net/svcdlls/repl/common/chnglock.c b/private/net/svcdlls/repl/common/chnglock.c
new file mode 100644
index 000000000..b559578b1
--- /dev/null
+++ b/private/net/svcdlls/repl/common/chnglock.c
@@ -0,0 +1,131 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ ChngLock.c
+
+Abstract:
+
+ This file contains:
+
+ ReplIncrLockFields
+ ReplDecrLockFields
+
+Author:
+
+ John Rogers (JohnRo) 07-Jan-1992
+
+Environment:
+
+ Runs under Windows NT.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+ 07-Jan-1992 JohnRo
+ Created.
+ 18-Feb-1992 JohnRo
+ Extracted these bits of code from server for use by DLL stubs too.
+ 22-Feb-1992 JohnRo
+ Made changes suggested by PC-LINT.
+ 26-Feb-1992 JohnRo
+ Check lock fields for validity.
+
+--*/
+
+
+// These must be included first:
+
+#include <windef.h> // IN, VOID, LPTSTR, etc.
+#include <lmcons.h> // NET_API_STATUS.
+
+// These can be in any order:
+
+#include <lmrepl.h> // REPL_UNLOCK_ equates.
+#include <netdebug.h> // NetpAssert(), etc.
+#include <repldefs.h> // My prototypes, etc.
+#include <replp.h> // NetpReplTimeNow().
+#include <winerror.h> // ERROR_ and NO_ERROR equates.
+
+
+NET_API_STATUS
+ReplIncrLockFields (
+ IN OUT LPDWORD LockCountPtr,
+ IN OUT LPDWORD LockTimePtr
+ )
+
+{
+ NetpAssert( LockCountPtr != NULL );
+ NetpAssert( LockTimePtr != NULL );
+
+ NetpAssert( ReplAreLockFieldsValid( *LockCountPtr, *LockTimePtr ) );
+
+ if ( *LockCountPtr == 0) {
+
+ *LockCountPtr = 1;
+ *LockTimePtr = NetpReplTimeNow();
+
+ } else {
+
+ // Not first time.
+ ++ (*LockCountPtr);
+
+ NetpAssert( (*LockCountPtr) > 0 );
+ NetpAssert( (*LockTimePtr) > 0 );
+
+ }
+
+ NetpAssert( ReplAreLockFieldsValid( *LockCountPtr, *LockTimePtr ) );
+
+ return (NO_ERROR);
+
+} // ReplIncrLockFields
+
+
+NET_API_STATUS
+ReplDecrLockFields (
+ IN OUT LPDWORD LockCountPtr,
+ IN OUT LPDWORD LockTimePtr,
+ IN DWORD UnlockForce
+ )
+
+{
+ NET_API_STATUS ApiStatus;
+
+ NetpAssert( LockCountPtr != NULL );
+ NetpAssert( LockTimePtr != NULL );
+
+ NetpAssert( ReplAreLockFieldsValid( *LockCountPtr, *LockTimePtr ) );
+
+ if ( !ReplIsForceLevelValid( UnlockForce ) ) {
+ return (ERROR_INVALID_PARAMETER);
+ }
+
+ if ((*LockCountPtr) == 0) { // not locked now.
+
+ ApiStatus = ERROR_INVALID_PARAMETER;
+
+ } else {
+
+ if (UnlockForce == REPL_UNLOCK_NOFORCE) {
+ --(*LockCountPtr);
+ } else {
+ (*LockCountPtr) = 0;
+ }
+
+ if ((*LockCountPtr) == 0) { // not locked any more.
+
+ *LockTimePtr = 0;
+
+ }
+
+ ApiStatus = NO_ERROR;
+ }
+
+ NetpAssert( ReplAreLockFieldsValid( *LockCountPtr, *LockTimePtr ) );
+
+ return (ApiStatus);
+
+} // ReplDecrLockFields
diff --git a/private/net/svcdlls/repl/common/chngnot.c b/private/net/svcdlls/repl/common/chngnot.c
new file mode 100644
index 000000000..31ddc8339
--- /dev/null
+++ b/private/net/svcdlls/repl/common/chngnot.c
@@ -0,0 +1,424 @@
+/*++
+
+Copyright (c) 1991-1993 Microsoft Corporation
+
+Module Name:
+
+ ChngNot.c
+
+Abstract:
+
+ This is a package of change notify routines:
+
+ ReplSetupChangeNotify
+ ReplEnableChangeNotify
+ ReplGetChangeNotifyStatus
+ ReplExtractChangeNotifyFirstDir
+ ReplCloseChangeNotify
+
+Author:
+
+ John Rogers (JohnRo) 28-Nov-1992
+
+Environment:
+
+ NT only.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+ 28-Nov-1992 JohnRo
+ Repl should use filesystem change notify. (Created these routines
+ from MadanA's test code in ReplTest/Watch.c)
+ 08-Dec-1992 JohnRo
+ Made changes suggested by PC-LINT 5.0
+ 14-Jan-1993 JohnRo
+ RAID 7053: locked trees added to pulse msg. (Actually fix all
+ kinds of remote lock handling.)
+ 26-Feb-1993 JohnRo
+ RAID 13126: Fix repl memory leak.
+ Made changes suggested by PC-LINT 5.0
+ Corrected copyright dates.
+ 30-Apr-1993 JohnRo
+ Use NetpKdPrint() where possible.
+
+--*/
+
+
+
+// These must be included first:
+
+#include <nt.h> // NT definitions
+#include <ntrtl.h> // NT runtime library definitions (needed by nturtl.h)
+#include <nturtl.h> // RtlDosPathNameToNtPathName_U().
+#include <windef.h> // LPVOID, etc.
+#include <lmcons.h> // NET_API_STATUS.
+
+// These may be included in any order:
+
+#include <chngnot.h> // My prototypes, REPL_CHANGE_NOTIFY_HANDLE, etc.
+#include <netdebug.h> // DBGSTATIC, NetpKdPrint(), FORMAT_ equates, etc.
+#include <netlib.h> // NetpMemoryAllocate(), etc.
+#include <netlibnt.h> // NetpNtStatusToApiStatus().
+#include <prefix.h> // PREFIX_ equates.
+#include <repldefs.h> // IF_DEBUG().
+#include <tstr.h> // TCHAR_EOS.
+#include <winerror.h> // NO_ERROR, ERROR_ equates.
+
+
+// Arbitrary amount for buffer which will be filled-in.
+// BUGBUG: We don't even use this buffer yet!
+#define BUFFER_SIZE (1024 * 10)
+
+
+//
+// Define which kinds of changes we want to actually get notified about.
+// We don't use access time in checksum, so skip FILE_NOTIFY_CHANGE_LAST_ACCESS.
+// Ditto for the creation time (FILE_NOTIFY_CHANGE_CREATION).
+// BUGBUG: we copy security info but don't checksum it yet!
+//
+
+#define MY_CHANGE_NOTIFY_FILTER \
+ ( \
+ FILE_NOTIFY_CHANGE_FILE_NAME | \
+ FILE_NOTIFY_CHANGE_DIR_NAME | \
+ FILE_NOTIFY_CHANGE_ATTRIBUTES | \
+ FILE_NOTIFY_CHANGE_SIZE | \
+ FILE_NOTIFY_CHANGE_LAST_WRITE | \
+ FILE_NOTIFY_CHANGE_EA | \
+ FILE_NOTIFY_CHANGE_SECURITY \
+ )
+
+
+DBGSTATIC BOOL
+ReplIsChangeNotifyHandleValid(
+ IN LPREPL_CHANGE_NOTIFY_HANDLE ReplHandle
+ )
+{
+ if (ReplHandle == NULL) {
+ return (FALSE);
+ } else if ( (ReplHandle->WaitableHandle) == (LPVOID) NULL ) {
+ return (FALSE);
+ } else if ( (ReplHandle->Buffer) == NULL ) {
+ return (FALSE);
+ } else if ( (ReplHandle->BufferSize) == 0 ) {
+ return (FALSE);
+ }
+ // BUGBUG: Check ReplHandle->NextElementInBuffer too.
+ // BUGBUG: Check ReplHandle->BufferBytesValid too.
+ return (TRUE);
+}
+
+
+NET_API_STATUS
+ReplSetupChangeNotify(
+ IN LPTSTR AbsPath,
+ OUT LPREPL_CHANGE_NOTIFY_HANDLE *ReplHandle
+ )
+{
+ NET_API_STATUS ApiStatus;
+ UNICODE_STRING DirName;
+ NTSTATUS NtStatus;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ BOOL PathAllocated = FALSE;
+ LPREPL_CHANGE_NOTIFY_HANDLE ReplStruct = NULL;
+
+
+ //
+ // Check for caller's errors.
+ //
+
+ NetpAssert( AbsPath != NULL );
+ NetpAssert( (*AbsPath) != TCHAR_EOS );
+ NetpAssert( ReplHandle != NULL );
+
+ //
+ // Allocate repl "handle" (structure).
+ //
+
+ ReplStruct = NetpMemoryAllocate( sizeof( REPL_CHANGE_NOTIFY_HANDLE ) );
+ if (ReplStruct == NULL) {
+ ApiStatus = ERROR_NOT_ENOUGH_MEMORY;
+ goto Cleanup;
+ }
+
+ //
+ // Fill-in fields in repl struct, so we don't confuse cleanup code.
+ //
+
+ ReplStruct->Buffer = NULL;
+ ReplStruct->BufferSize = 0;
+ ReplStruct->WaitableHandle = NULL;
+
+ //
+ // Allocate change buffer.
+ //
+
+ ReplStruct->Buffer = NetpMemoryAllocate( BUFFER_SIZE );
+ if (ReplStruct->Buffer == NULL) {
+ ApiStatus = ERROR_NOT_ENOUGH_MEMORY;
+ goto Cleanup;
+ }
+ ReplStruct->BufferSize = BUFFER_SIZE;
+
+ //
+ // Arrange NT-style stuff so we can open the directory.
+ //
+
+ if (! RtlDosPathNameToNtPathName_U(
+ AbsPath,
+ &DirName,
+ NULL,
+ NULL
+ )) {
+ ApiStatus = ERROR_INVALID_PARAMETER;
+ NetpKdPrint(( PREFIX_REPL "ReplSetupChangeNotify: "
+ "Could not convert DOS path to NT path; returning "
+ FORMAT_API_STATUS ".\n", ApiStatus ));
+ goto Cleanup;
+ }
+ PathAllocated = TRUE;
+
+ InitializeObjectAttributes(
+ &ObjectAttributes,
+ &DirName,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL
+ );
+
+ //
+ // Open the diretory.
+ //
+
+ NtStatus = NtOpenFile(
+ &(ReplStruct->WaitableHandle),
+ SYNCHRONIZE | FILE_LIST_DIRECTORY,
+ &ObjectAttributes,
+ &(ReplStruct->IoStatusBlock),
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ FILE_DIRECTORY_FILE
+ );
+
+ if (NT_SUCCESS(NtStatus)) {
+ NtStatus = ReplStruct->IoStatusBlock.Status;
+ }
+
+ if (! NT_SUCCESS(NtStatus)) {
+ ApiStatus = NetpNtStatusToApiStatus( NtStatus );
+ NetpKdPrint(( PREFIX_REPL "ReplSetupChangeNotify: "
+ "NtOpenFile " FORMAT_LPTSTR " failed: " FORMAT_NTSTATUS
+ "; returning " FORMAT_API_STATUS ".\n",
+ AbsPath, NtStatus, ApiStatus ));
+ ReplStruct->WaitableHandle = NULL; // BUGBUG: not necessary?
+ goto Cleanup;
+ }
+
+ IF_DEBUG( CHNGNOT ) {
+ NetpKdPrint(( PREFIX_REPL "ReplSetupChangeNotify: "
+ "Succeeded in opening dir.\n" ));
+ }
+
+ ApiStatus = NO_ERROR;
+
+Cleanup:
+ if (ApiStatus != NO_ERROR) {
+ if (ReplStruct->Buffer != NULL) {
+ NetpMemoryFree( ReplStruct->Buffer );
+ }
+ if (ReplStruct != NULL) {
+ NetpMemoryFree( ReplStruct );
+ }
+ *ReplHandle = NULL;
+ } else {
+ NetpAssert( ReplStruct != NULL );
+ *ReplHandle = ReplStruct;
+
+ NetpAssert( ReplIsChangeNotifyHandleValid( *ReplHandle ) );
+ }
+
+ IF_DEBUG( CHNGNOT ) {
+ NetpKdPrint(( PREFIX_REPL "ReplSetupChangeNotify: "
+ "returning " FORMAT_API_STATUS ".\n", ApiStatus ));
+ }
+
+ if (PathAllocated) {
+ (VOID) RtlFreeHeap( RtlProcessHeap(), 0, DirName.Buffer );
+ }
+
+
+ return (ApiStatus);
+
+} // ReplSetupChangeNotify
+
+
+NET_API_STATUS
+ReplEnableChangeNotify(
+ IN OUT LPREPL_CHANGE_NOTIFY_HANDLE ReplHandle
+ )
+{
+ NET_API_STATUS ApiStatus;
+ NTSTATUS NtStatus;
+
+ //
+ // Now tell the system that we're going to watch for changes.
+ //
+
+ NtStatus = NtNotifyChangeDirectoryFile(
+ ReplHandle->WaitableHandle,
+ NULL, // No event, wait on handle
+ NULL, // No APC routine
+ NULL, // No APC context
+ &(ReplHandle->IoStatusBlock),
+ ReplHandle->Buffer,
+ BUFFER_SIZE,
+ MY_CHANGE_NOTIFY_FILTER,
+ (BOOLEAN) TRUE // Watch for change in the entire tree
+ );
+
+
+ if ( !NT_SUCCESS( NtStatus ) ) {
+ ApiStatus = NetpNtStatusToApiStatus( NtStatus );
+
+ NetpKdPrint(( PREFIX_REPL "ReplEnableChangeNotify: "
+ "NtNotifyChangeDirectoryFile failed " FORMAT_NTSTATUS
+ "; returning " FORMAT_API_STATUS "\n",
+ NtStatus, ApiStatus ));
+ NetpAssert( ApiStatus != NO_ERROR )
+ goto Cleanup;
+ }
+
+ ApiStatus = NO_ERROR;
+
+Cleanup:
+ IF_DEBUG( CHNGNOT ) {
+ NetpKdPrint(( PREFIX_REPL "ReplEnableChangeNotify: "
+ "returning " FORMAT_API_STATUS ".\n", ApiStatus ));
+ }
+
+ return (ApiStatus);
+
+} // ReplEnableChangeNotify
+
+
+NET_API_STATUS
+ReplGetChangeNotifyStatus(
+ IN LPREPL_CHANGE_NOTIFY_HANDLE ReplHandle
+ )
+{
+ NET_API_STATUS ApiStatus;
+ NTSTATUS NtStatus;
+
+ NetpAssert( ReplIsChangeNotifyHandleValid( ReplHandle ) );
+
+ // BUGBUG: What status do we get if buffer overflowed?
+
+ NtStatus = ReplHandle->IoStatusBlock.Status;
+
+ ApiStatus = NetpNtStatusToApiStatus( NtStatus );
+
+ ReplHandle->BufferBytesValid = ReplHandle->IoStatusBlock.Information;
+ NetpAssert( ReplIsChangeNotifyHandleValid( ReplHandle ) );
+
+ IF_DEBUG( CHNGNOT ) {
+ NetpKdPrint(( PREFIX_REPL "ReplGetChangeNotifyStatus: "
+ "NT status is " FORMAT_NTSTATUS ", "
+ "num bytes valid is " FORMAT_DWORD ", "
+ "returning " FORMAT_API_STATUS ".\n",
+ NtStatus, ReplHandle->BufferBytesValid, ApiStatus ));
+ }
+
+ return (ApiStatus);
+
+} // ReplGetChangeNotifyStatus
+
+
+NET_API_STATUS
+ReplExtractChangeNotifyFirstDir(
+ IN OUT LPREPL_CHANGE_NOTIFY_HANDLE ReplHandle,
+ IN BOOL FirstTime,
+ OUT LPTSTR FirstLevelDirName
+ )
+{
+ LPVOID BufferValidEnd;
+ DWORD CharsLeft;
+ LPTSTR InCharPtr, OutCharPtr;
+ PFILE_NOTIFY_INFORMATION EntryPtr;
+
+ NetpAssert( ReplIsChangeNotifyHandleValid( ReplHandle ) );
+
+ if (FirstTime) {
+ ReplHandle->NextElementInBuffer = ReplHandle->Buffer;
+ }
+ EntryPtr = ReplHandle->NextElementInBuffer;
+
+ BufferValidEnd =
+ ((LPBYTE)(ReplHandle->Buffer))
+ + (ReplHandle->BufferBytesValid);
+ if ( BufferValidEnd >= ReplHandle->NextElementInBuffer ) {
+ return (ERROR_NO_MORE_ITEMS);
+ }
+
+ NetpAssert( EntryPtr->FileNameLength > 0 );
+ NetpAssert( (EntryPtr->FileNameLength % sizeof(WCHAR)) == 0 );
+
+ NetpAssert( sizeof(WCHAR) == sizeof(TCHAR) );
+ InCharPtr = (LPTSTR) (LPVOID) (EntryPtr->FileName);
+ OutCharPtr = FirstLevelDirName;
+ CharsLeft = (EntryPtr->FileNameLength) / sizeof(WCHAR);
+
+ /*lint -save -e716 */ // disable warnings for while(TRUE)
+ while (TRUE) {
+ if (CharsLeft == 0) {
+ break;
+ } else if ( IS_PATH_SEPARATOR( *InCharPtr ) ) {
+ break;
+ }
+
+ *OutCharPtr = *InCharPtr;
+
+ --CharsLeft;
+ ++InCharPtr;
+ ++OutCharPtr;
+ }
+ /*lint -restore */ // re-enable warnings for while(TRUE)
+
+ *OutCharPtr = TCHAR_EOS;
+
+ IF_DEBUG( CHNGNOT ) {
+ NetpKdPrint(( PREFIX_REPL
+ "ReplExtractChangeNotifyFirstDir: first level dir is '"
+ FORMAT_LPTSTR "'\n", FirstLevelDirName ));
+ }
+
+ ReplHandle->NextElementInBuffer =
+ ((LPBYTE)EntryPtr)
+ + EntryPtr->NextEntryOffset;
+
+ NetpAssert( ReplIsChangeNotifyHandleValid( ReplHandle ) );
+
+ return (NO_ERROR);
+
+} // ReplExtractChangeNotifyFirstDir
+
+
+NET_API_STATUS
+ReplCloseChangeNotify(
+ IN OUT LPREPL_CHANGE_NOTIFY_HANDLE ReplHandle
+ )
+{
+ NetpAssert( ReplIsChangeNotifyHandleValid( ReplHandle ) );
+
+ if (ReplHandle != NULL) {
+ if (ReplHandle->Buffer != NULL) {
+ NetpMemoryFree( ReplHandle->Buffer );
+ }
+ if ((ReplHandle->WaitableHandle) != NULL) {
+ (VOID) NtClose( ReplHandle->WaitableHandle );
+ }
+ NetpMemoryFree( ReplHandle );
+ }
+
+ return (NO_ERROR);
+
+} // ReplCloseChangeNotify
diff --git a/private/net/svcdlls/repl/common/chngnot.h b/private/net/svcdlls/repl/common/chngnot.h
new file mode 100644
index 000000000..5b585e404
--- /dev/null
+++ b/private/net/svcdlls/repl/common/chngnot.h
@@ -0,0 +1,85 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ ChngNot.h
+
+Abstract:
+
+ This module defines some change notify datatypes and routines.
+
+Author:
+
+ John Rogers (JohnRo) 25-Nov-1992
+
+Environment:
+
+ Runs under Windows NT.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+ 25-Nov-1992 JohnRo
+ Repl should use filesystem change notify.
+
+--*/
+
+
+#ifndef _CHNGNOT_
+#define _CHNGNOT_
+
+
+typedef struct _REPL_CHANGE_NOTIFY_HANDLE {
+
+ // Handle for use by callers.
+ HANDLE WaitableHandle;
+
+ // From here on are "implementation details"; callers should NOT use them.
+ LPVOID Buffer;
+ DWORD BufferSize;
+ DWORD BufferBytesValid;
+ IO_STATUS_BLOCK IoStatusBlock;
+ LPVOID NextElementInBuffer; // Points in Buffer, or is NULL.
+
+} REPL_CHANGE_NOTIFY_HANDLE;
+
+typedef REPL_CHANGE_NOTIFY_HANDLE * PREPL_CHANGE_NOTIFY_HANDLE;
+typedef REPL_CHANGE_NOTIFY_HANDLE * LPREPL_CHANGE_NOTIFY_HANDLE;
+
+
+NET_API_STATUS
+ReplSetupChangeNotify(
+ IN LPTSTR AbsPath,
+ OUT LPREPL_CHANGE_NOTIFY_HANDLE *ReplHandle
+ );
+
+
+NET_API_STATUS
+ReplEnableChangeNotify(
+ IN OUT LPREPL_CHANGE_NOTIFY_HANDLE ReplHandle
+ );
+
+
+NET_API_STATUS
+ReplGetChangeNotifyStatus(
+ IN LPREPL_CHANGE_NOTIFY_HANDLE ReplHandle
+ );
+
+
+NET_API_STATUS
+ReplExtractChangeNotifyFirstDir(
+ IN OUT LPREPL_CHANGE_NOTIFY_HANDLE ReplHandle,
+ IN BOOL FirstTime,
+ OUT LPTSTR FirstLevelDirName
+ );
+
+
+NET_API_STATUS
+ReplCloseChangeNotify(
+ IN OUT LPREPL_CHANGE_NOTIFY_HANDLE ReplHandle
+ );
+
+
+#endif // _CHNGNOT_
diff --git a/private/net/svcdlls/repl/common/data.c b/private/net/svcdlls/repl/common/data.c
new file mode 100644
index 000000000..c38b87824
--- /dev/null
+++ b/private/net/svcdlls/repl/common/data.c
@@ -0,0 +1,45 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ Data.c
+
+Abstract:
+
+ Global data for repl debug routines. (Debug only, so no security problems.)
+
+Author:
+
+ John Rogers (JohnRo) 14-Mar-1992
+
+Environment:
+
+ Portable to any flat, 32-bit environment. (Uses Win32 typedefs.)
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+ 14-Mar-1992 JohnRo
+ Created.
+--*/
+
+
+// These must be included first:
+
+#include <windef.h> // DWORD, etc.
+#include <lmcons.h> // Needed by repldefs.h/netlib.h.
+
+// These may be included in any order:
+
+#include <repldefs.h> // REPL_DEBUG_ALL.
+
+#if DBG
+
+//DWORD ReplGlobalTrace = 0;
+DWORD ReplGlobalTrace = 0; //REPL_DEBUG_ALL;
+
+#endif
+
+// That's all, folks!
diff --git a/private/net/svcdlls/repl/common/delfile.c b/private/net/svcdlls/repl/common/delfile.c
new file mode 100644
index 000000000..4d4600631
--- /dev/null
+++ b/private/net/svcdlls/repl/common/delfile.c
@@ -0,0 +1,262 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ DelFile.c
+
+Abstract:
+
+ This file just contains ReplDeleteFile.
+
+ This is callable even if the replicator service is not started.
+
+Author:
+
+ JR (John Rogers, JohnRo@Microsoft) 26-Apr-1993
+
+Environment:
+
+ User Mode - Win32
+ Portable to any flat, 32-bit environment. (Uses Win32 typedefs.)
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+ 26-Apr-1993 JohnRo
+ Created for RAID 7313: repl needs change permission to work on NTFS,
+ or we need to delete files differently.
+
+--*/
+
+
+// These must be included first:
+
+#include <nt.h> // NT_SUCCESS(), etc.
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windows.h> // IN, GetLastError(), LPCTSTR, OPTIONAL, etc.
+#include <lmcons.h> // NET_API_STATUS.
+
+// These may be included in any order:
+
+#include <lmerrlog.h> // NELOG_ equates.
+#include <netdebug.h> // NetpKdPrint(), FORMAT_ equates, etc.
+#include <netlibnt.h> // NetpNtStatusToApiStatus().
+#include <prefix.h> // PREFIX_ equates.
+#include <repldefs.h> // IF_DEBUG(), my prototype, USE_ equates, etc.
+#include <tstr.h> // TCHAR_EOS.
+#include <winerror.h> // ERROR_ and NO_ERROR equates.
+
+
+// BUGBUG: undo this!
+#undef USE_BACKUP_APIS
+
+
+NET_API_STATUS
+ReplDeleteFile(
+ IN LPCTSTR FileName
+ )
+{
+ NET_API_STATUS ApiStatus;
+#ifdef USE_BACKUP_APIS
+ HANDLE FileHandle = INVALID_HANDLE_VALUE;
+ IO_STATUS_BLOCK IoStatusBlock;
+
+ const ACCESS_MASK MyAccessDesired =
+ ( DELETE
+ | FILE_READ_ATTRIBUTES
+ | FILE_READ_DATA
+ | FILE_READ_EA
+ | FILE_TRAVERSE
+ | SYNCHRONIZE
+ );
+
+ const ULONG MyOpenOptions =
+ FILE_SYNCHRONOUS_IO_NONALERT
+ | FILE_DELETE_ON_CLOSE
+ | FILE_OPEN_FOR_BACKUP_INTENT
+ ;
+
+ const ULONG MyShareAccess =
+ FILE_SHARE_READ; // BUGBUG
+// FILE_SHARE_DELETE;
+
+ NTSTATUS NtStatus;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ BOOL PathAllocated = FALSE;
+ UNICODE_STRING UnicodePath;
+#endif
+
+ //
+ // Check for caller errors.
+ //
+
+ if ( (FileName==NULL) || ((*FileName)==TCHAR_EOS) ) {
+ ApiStatus = ERROR_INVALID_PARAMETER;
+ goto Cleanup;
+ }
+
+ //
+ // Tell the world what we're going to do.
+ //
+
+ IF_DEBUG( REPL ) {
+ NetpKdPrint(( PREFIX_REPL
+ "ReplDeleteFile: *** DELETING FILE *** '" FORMAT_LPTSTR "'.\n",
+ FileName ));
+ }
+
+ //
+ // return no error if the file does not exist.
+ //
+ if ( !ReplFileOrDirExists( FileName ) ) {
+ return( NO_ERROR );
+ }
+
+#ifndef USE_BACKUP_APIS
+
+ //
+ // If the file system ACL allows us to delete, we can just
+ // use the Win32 APIs for this.
+ //
+
+ if ( ! DeleteFile( (LPTSTR) FileName ) ) {
+ ApiStatus = (NET_API_STATUS) GetLastError();
+ } else {
+ ApiStatus = NO_ERROR;
+ }
+
+#else
+
+ //
+ // It turns out that "backup semantics" is very powerful. It allows
+ // us to create files in directories which have read-only ACLs.
+ // Unfortunately, there isn't a "backup semantics" flag for DeleteFile(),
+ // so we need to use the NT APIs to get the same effect.
+ //
+
+ //
+ // Convert file name to NT style.
+ //
+
+ RtlInitUnicodeString(
+ & UnicodePath, // output: struct
+ FileName ); // input: null terminated
+
+ if( !RtlDosPathNameToNtPathName_U(
+ FileName,
+ &UnicodePath,
+ NULL,
+ NULL) ) {
+
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplDeleteFile: RtlDosPathNameToNtPathname_U"
+ " of file '" FORMAT_LPTSTR "' failed.\n", FileName ));
+
+ // BUGBUG: this is just our best guess for an error code this.
+ ApiStatus = ERROR_INVALID_PARAMETER;
+ goto Cleanup;
+ }
+ NetpAssert( UnicodePath.Buffer != NULL );
+ PathAllocated = TRUE;
+
+ InitializeObjectAttributes(
+ &ObjectAttributes,
+ (LPVOID) &UnicodePath,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL );
+
+ //
+ // Open the file, with backup semantics and delete on close.
+ //
+
+ NtStatus = NtOpenFile(
+ & FileHandle,
+ MyAccessDesired,
+ &ObjectAttributes,
+ &IoStatusBlock,
+ MyShareAccess,
+ MyOpenOptions );
+
+ if ( !NT_SUCCESS( NtStatus ) ) {
+
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplDeleteFile: NtOpenFile of file '"
+ FORMAT_LPTSTR "' gave NT status " FORMAT_NTSTATUS ".\n",
+ FileName, NtStatus ));
+
+ ApiStatus = NetpNtStatusToApiStatus( NtStatus );
+ NetpAssert( ApiStatus != NO_ERROR );
+ goto Cleanup;
+ }
+ NetpAssert( NtStatus == STATUS_SUCCESS );
+ NetpAssert( FileHandle != INVALID_HANDLE_VALUE );
+
+ //
+ // Close the file, which will delete it since we gave the
+ // FILE_CLOSE_ON_DELETE flag.
+ //
+
+ NtStatus = NtClose( FileHandle );
+ FileHandle = INVALID_HANDLE_VALUE;
+
+ if ( !NT_SUCCESS( NtStatus ) ) {
+
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplDeleteFile: NtClose failed, "
+ " NT status is " FORMAT_NTSTATUS
+ ".\n", NtStatus ));
+
+ ApiStatus = NetpNtStatusToApiStatus( NtStatus );
+ NetpAssert( ApiStatus != NO_ERROR );
+ goto Cleanup;
+ }
+ NetpAssert( NtStatus == STATUS_SUCCESS );
+
+ (void) NtClose( FileHandle );
+ FileHandle = INVALID_HANDLE_VALUE;
+
+ (VOID) RtlFreeHeap( RtlProcessHeap(), 0, UnicodePath.Buffer );
+ PathAllocated = FALSE;
+
+ ApiStatus = NO_ERROR;
+
+#endif
+
+
+Cleanup:
+
+ if (ApiStatus != NO_ERROR) {
+
+ NetpKdPrint(( PREFIX_REPL
+ "ReplDeleteFile: ERROR " FORMAT_API_STATUS ".\n",
+ ApiStatus ));
+
+ //
+ // Log the error.
+ // BUGBUG: extract master server name and log there too.
+ //
+ ReplErrorLog(
+ NULL, // no server name (log locally)
+ NELOG_ReplSysErr, // log code
+ ApiStatus,
+ NULL, // optional str1
+ NULL); // optional str2
+ }
+
+#ifdef USE_BACKUP_APIS
+ if (FileHandle != INVALID_HANDLE_VALUE) {
+ (VOID) NtClose( FileHandle );
+ }
+
+ if (PathAllocated) {
+ (VOID) RtlFreeHeap( RtlProcessHeap(), 0, UnicodePath.Buffer );
+ }
+#endif
+
+ return (ApiStatus);
+
+}
diff --git a/private/net/svcdlls/repl/common/dirname.c b/private/net/svcdlls/repl/common/dirname.c
new file mode 100644
index 000000000..b172cc862
--- /dev/null
+++ b/private/net/svcdlls/repl/common/dirname.c
@@ -0,0 +1,112 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ DirName.c
+
+Abstract:
+
+ This module has some simple replicator directory name helpers.
+
+Author:
+
+ John Rogers (JohnRo) 07-Jan-1992
+
+Environment:
+
+ Runs under Windows NT.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+ 07-Jan-1992 JohnRo
+ Created.
+ 24-Jan-1992 JohnRo
+ Changed to use LPTSTR etc.
+ 26-Mar-1992 JohnRo
+ Added check to disallow "c:\stuff" form.
+
+--*/
+
+
+// These must be included first:
+
+#include <windef.h> // IN, VOID, LPTSTR, etc.
+#include <lmcons.h> // PATHLEN, etc.
+
+// These can be in any order:
+
+#include <dirname.h> // My prototypes.
+#include <tstr.h> // STRLEN().
+#include <winerror.h> // ERROR_ equates, NO_ERROR.
+
+
+BOOL
+ReplIsDirNameValid(
+ IN LPTSTR DirName
+ )
+
+/*++
+
+Routine Description:
+
+ ReplIsDirNameValid checks to see if a replicator directory name is
+ syntactically valid. No check is made to see if the directory exists.
+
+Arguments:
+
+ DirName points to a string containing an alleged replicator directory
+ name.
+
+Return Value:
+
+ BOOL - TRUE iff the directory name is valid.
+
+--*/
+
+{
+ TCHAR FirstChar;
+
+ if ( (DirName == NULL) || (*DirName == L'\0') ) {
+ return (FALSE);
+ }
+ FirstChar = DirName[0];
+
+
+ if (STRLEN( DirName ) > PATHLEN) {
+ return (FALSE);
+ }
+
+ if (ISALPHA( FirstChar )) {
+ if (STRLEN(DirName) >= 2) {
+
+ if (DirName[1] == TCHAR_COLON) { // Sneak in "c:\stuff"?
+ return (FALSE); // Name not valid!
+ }
+ }
+ }
+
+ //
+ // BUGBUG: This is just a quick partial hack. Eventually we should
+ // call the canon routines.
+ //
+
+ switch (FirstChar) {
+ case TCHAR_BACKSLASH:
+ // Name must be relative; no UNC or absolute paths allowed.
+ /*FALLTHROUGH*/
+ case TCHAR_FWDSLASH:
+ // Name must be relative; no UNC or absolute paths allowed.
+ /*FALLTHROUGH*/
+
+ return (FALSE); // name is not valid
+
+ default:
+ return (TRUE);
+ }
+
+ /*NOTREACHED*/
+
+} // ReplIsDirNameValid
diff --git a/private/net/svcdlls/repl/common/dirname.h b/private/net/svcdlls/repl/common/dirname.h
new file mode 100644
index 000000000..f201b63b7
--- /dev/null
+++ b/private/net/svcdlls/repl/common/dirname.h
@@ -0,0 +1,64 @@
+/*++
+
+Copyright (c) 1991-92 Microsoft Corporation
+
+Module Name:
+
+ DirName.h
+
+Abstract:
+
+ This module has some simple replicator directory name helpers.
+
+Author:
+
+ John Rogers (JohnRo) 31-Dec-1991
+
+Environment:
+
+ Runs under Windows NT.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+ 31-Dec-1991 JohnRo
+ Created.
+ 08-Jan-1992 JohnRo
+ Added ReplDirNamesMatch() macro.
+ 09-Jan-1992 JohnRo
+ Use _wcscmpi() instead of wcscmpi().
+ 24-Jan-1992 JohnRo
+ Changed to use LPTSTR etc.
+ 05-Dec-1992 JohnRo
+ Made changes suggested by PC-LINT 5.0
+
+--*/
+
+#ifndef _DIRNAME_
+#define _DIRNAME_
+
+
+// Don't complain about "unneeded" includes of these files:
+/*lint -efile(764,tstr.h) */
+#include <tstr.h> // STRICMP().
+
+
+// BOOL
+// ReplDirNamesMatch(
+// IN LPTSTR OneName,
+// IN LPTSTR TheOther
+// );
+//
+// BUGBUG: Should this canonicalize? (E.g. ".\a" == "a"?)
+//
+#define ReplDirNamesMatch(OneName,TheOther) \
+ ( ( (STRICMP( (OneName), (TheOther))) == 0 ) ? TRUE : FALSE )
+
+
+BOOL
+ReplIsDirNameValid(
+ IN LPTSTR DirName
+ );
+
+
+#endif // _DIRNAME_
diff --git a/private/net/svcdlls/repl/common/easize.c b/private/net/svcdlls/repl/common/easize.c
new file mode 100644
index 000000000..dbf292fb3
--- /dev/null
+++ b/private/net/svcdlls/repl/common/easize.c
@@ -0,0 +1,236 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ EaSize.c
+
+Abstract:
+
+ Compute size of full EA list (using OS/2 semantics) for a file or
+ directory. This must match OS/2 usage, down to the bit, as this
+ value is used in the replicator checksum. One thing in particular:
+ the "EA size" of a file with no EAs is 4, as it takes four bytes to
+ indicate empty full EA list.
+
+Author:
+
+ JR (John Rogers, JohnRo@Microsoft) 10-May-1993
+
+Revision History:
+
+ 10-May-1993 JohnRo
+ Created for RAID 3258: file not updated due to
+ ERROR_INVALID_USER_BUFFER (actually, massive rework of buggy
+ version in repl/server/filefind.c).
+
+--*/
+
+
+// These must be included first:
+
+#include <nt.h> // NT definitions
+#include <ntrtl.h> // NT runtime library definitions
+#include <nturtl.h>
+#include <windows.h>
+#include <lmcons.h> // NET_API_STATUS.
+
+// These may be included in any order:
+
+#include <lmerr.h> // NERR_ equates.
+#include <lmerrlog.h> // NELOG_ equates.
+#include <netdebug.h> // NetpAssert(), NetpKdPrint(), FORMAT_ equates.
+#include <netlibnt.h> // NetpNtStatusToApiStatus().
+#include <prefix.h> // PREFIX_ equates.
+#include <repldefs.h> // IF_DEBUG(), ReplErrorLog(), ReplGetEaSize(), etc.
+#include <tstr.h> // STRLEN(), etc.
+
+
+#define MY_ACCESS_DESIRED ( FILE_READ_DATA | FILE_READ_EA \
+ | FILE_TRAVERSE \
+ | SYNCHRONIZE | FILE_READ_ATTRIBUTES )
+
+
+DWORD
+ReplGetEaSize(
+ IN LPCTSTR Path
+ )
+
+/*++
+
+Routine Description:
+
+ Retrive EaSize of the given file and convert it to DosFindFirst2
+ EaSize.
+
+Arguments:
+
+ Path - file name. May refer to file or directory. May include drive
+ letter (e.g. "d:\import\dir\file.ext") or be UNC path (e.g.
+ "\\server\repl$\dir\dir2\file.ext").
+
+Return Value:
+
+ Return DosFindFirst2 EaSize. This value will be 4 if no EAs exist or
+ an error occurs.
+
+--*/
+
+{
+ NET_API_STATUS ApiStatus = NO_ERROR;
+ FILE_EA_INFORMATION EaInfo;
+ DWORD EaSize = EA_MIN_SIZE; // initially set to return on err
+ HANDLE FileHandle = INVALID_HANDLE_VALUE;
+ UNICODE_STRING FileName;
+ IO_STATUS_BLOCK IoStatusBlock;
+ NTSTATUS NtStatus = STATUS_SUCCESS;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ BOOL PathAllocated = FALSE;
+ DWORD PathLength;
+
+ NetpAssert( Path != NULL );
+ NetpAssert( (*Path) != TCHAR_EOS );
+
+ //
+ // Some systems don't like NtQueryEaFile with names like:
+ //
+ // \\server\REPL$\dir\.
+ // \\server\REPL$\dir\..
+ //
+ // so avoid them. (They won't be used in checksums anyway, so it
+ // doesn't matter if we lie about their EA sizes.)
+ //
+ PathLength = STRLEN(Path);
+ if (PathLength >= 2) {
+ LPCTSTR LastTwoChars = &Path[ PathLength-2 ];
+ NetpAssert( (*LastTwoChars) != TCHAR_EOS );
+ if (STRCMP( LastTwoChars, SLASH_DOT ) == 0) {
+ goto Cleanup;
+ }
+ }
+ if (PathLength >= 3) {
+ LPCTSTR LastThreeChars = &Path[ PathLength-3 ];
+ NetpAssert( (*LastThreeChars) != TCHAR_EOS );
+ if (STRCMP( LastThreeChars, SLASH_DOT_DOT ) == 0) {
+ goto Cleanup;
+ }
+ }
+
+#ifndef UNICODE
+#error Fix code below if UNICODE is not defined any more.
+#endif
+
+ if( !RtlDosPathNameToNtPathName_U(
+ Path,
+ &FileName,
+ NULL,
+ NULL
+ ) ) {
+
+ NetpKdPrint(( PREFIX_REPL
+ "ReplGetEaSize: "
+ "Could not convert DOS path '" FORMAT_LPTSTR "' "
+ "to NT path.\n", Path ));
+
+ ApiStatus = NERR_InternalError;
+ goto Cleanup;
+ }
+ PathAllocated = TRUE;
+
+ InitializeObjectAttributes(
+ &ObjectAttributes,
+ &FileName,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL );
+
+ NtStatus = NtOpenFile(
+ &FileHandle,
+ MY_ACCESS_DESIRED,
+ &ObjectAttributes,
+ &IoStatusBlock,
+ FILE_SHARE_READ,
+ FILE_SYNCHRONOUS_IO_NONALERT ); // open options
+
+ if (! NT_SUCCESS(NtStatus)) {
+ ApiStatus = NetpNtStatusToApiStatus( NtStatus );
+ NetpKdPrint(( PREFIX_REPL
+ "ReplGetEaSize: NtOpenFile " FORMAT_LPTSTR " failed: "
+ FORMAT_NTSTATUS "\n",
+ Path, NtStatus ));
+ NetpAssert( ApiStatus != NO_ERROR );
+ goto Cleanup;
+ }
+
+ IF_DEBUG( FILEFIND ) {
+ NetpKdPrint(( PREFIX_REPL
+ "ReplGetEaSize: Succeeded in opening dir.\n" ));
+ }
+
+ NtStatus = NtQueryInformationFile(
+ FileHandle,
+ &IoStatusBlock,
+ &EaInfo,
+ sizeof(FILE_EA_INFORMATION),
+ FileEaInformation ); // information class
+
+ if ( !NT_SUCCESS( NtStatus ) ) {
+
+ NetpKdPrint(( PREFIX_REPL
+ "ReplGetEaSize: NtQueryInformationFile for "
+ FORMAT_LPTSTR " FAILED, NtStatus="
+ FORMAT_NTSTATUS ", iosb.info=" FORMAT_ULONG "\n",
+ Path, NtStatus, IoStatusBlock.Information ));
+ ApiStatus = NetpNtStatusToApiStatus( NtStatus );
+ NetpAssert( ApiStatus != NO_ERROR );
+ goto Cleanup;
+ }
+
+ EaSize = EaInfo.EaSize;
+ if (EaSize == 0) {
+ EaSize = EA_MIN_SIZE;
+ }
+
+Cleanup:
+
+ //
+ // Take care of things and return EaSize to caller.
+ // Also use ApiStatus to decide whether or not to log anything.
+ //
+
+ if (ApiStatus != NO_ERROR) {
+
+ // BUGBUG: log this remotely too.
+ ReplErrorLog(
+ NULL, // no server name (local)
+ NELOG_ReplSysErr, // log code
+ ApiStatus, // error code we got
+ NULL, // no optional str 1
+ NULL ); // no optional str 2
+
+ NetpKdPrint(( PREFIX_REPL
+ "ReplGetEaSize: ERROR processing '" FORMAT_LPTSTR "', "
+ "final NT status " FORMAT_NTSTATUS ", "
+ "final API status " FORMAT_API_STATUS ".\n",
+ Path, NtStatus, ApiStatus ));
+ }
+
+ IF_DEBUG( FILEFIND ) {
+ NetpKdPrint(( PREFIX_REPL
+ "ReplGetEaSize: returning EA size " FORMAT_DWORD " "
+ "for " FORMAT_LPTSTR ", final NT status " FORMAT_NTSTATUS ", "
+ "final API status " FORMAT_API_STATUS ".\n",
+ EaSize, Path, NtStatus, ApiStatus ));
+ }
+
+ if (PathAllocated) {
+ (VOID) RtlFreeHeap( RtlProcessHeap(), 0, FileName.Buffer );
+ }
+
+ if (FileHandle != INVALID_HANDLE_VALUE) {
+ (void) NtClose(FileHandle);
+ }
+
+ return (EaSize);
+}
diff --git a/private/net/svcdlls/repl/common/expalloc.c b/private/net/svcdlls/repl/common/expalloc.c
new file mode 100644
index 000000000..da5fe7f1c
--- /dev/null
+++ b/private/net/svcdlls/repl/common/expalloc.c
@@ -0,0 +1,120 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ ExpAlloc.c
+
+Abstract:
+
+ This file contains ExportDirAllocApiRecords().
+
+Author:
+
+ John Rogers (JohnRo) 22-Jan-1992
+
+Environment:
+
+ Runs under Windows NT.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+ 22-Jan-1992 JohnRo
+ Created.
+ 28-Jan-1992 JohnRo
+ Changed ExportDirAllocApiRecords() to allow arrays.
+
+--*/
+
+
+// These must be included first:
+
+#include <windef.h> // IN, VOID, LPTSTR, etc.
+#include <lmcons.h> // NET_API_STATUS, PARM equates, etc.
+#include <rap.h> // Needed by <strucinf.h>.
+
+// These can be in any order:
+
+#include <expdir.h> // My prototype.
+#include <lmapibuf.h> // NetApiBufferAllocate().
+#include <netdebug.h> // NetpAssert().
+#include <netlib.h> // NetpPointerPlusSomeBytes().
+#include <strucinf.h> // Netp{various}StructureInfo().
+#include <winerror.h> // ERROR_* defines; NO_ERROR.
+
+
+NET_API_STATUS
+ExportDirAllocApiRecords (
+ IN DWORD Level,
+ IN DWORD EntryCount,
+ OUT LPBYTE * BufPtr,
+ IN OUT LPBYTE *StringLocation // Points just past top of data.
+ )
+
+{
+ LPBYTE FirstRecord = NULL;
+ NET_API_STATUS ApiStatus;
+ DWORD EntrySize;
+
+ //
+ // Check for caller errors.
+ //
+ if (BufPtr == NULL) {
+ return (ERROR_INVALID_PARAMETER);
+ }
+
+ * BufPtr = NULL; // Don't confuse caller about possible alloc'ed data.
+
+ if (EntryCount == 0) {
+ return (ERROR_INVALID_PARAMETER);
+ }
+
+ //
+ // Compute size of an entry (and check caller's Level too).
+ //
+ ApiStatus = NetpReplExportDirStructureInfo (
+ Level,
+ PARMNUM_ALL,
+ TRUE, // want native sizes
+ NULL, // don't need DataDesc16
+ NULL, // don't need DataDesc32
+ NULL, // don't need DataDescSmb
+ & EntrySize, // need max size of structure
+ NULL, // don't need FixedSize
+ NULL); // don't need StringSize
+ if (ApiStatus != NO_ERROR) {
+ return (ApiStatus);
+ }
+ NetpAssert( EntrySize > 0 );
+
+ //
+ // Allocate the output area.
+ //
+ ApiStatus = NetApiBufferAllocate(
+ EntrySize * EntryCount,
+ (LPVOID *) & FirstRecord);
+ if (ApiStatus != NO_ERROR) {
+
+ // ApiStatus is already set to return error to caller.
+
+ } else {
+ NetpAssert( FirstRecord != NULL );
+
+ //
+ // Tell caller where top of string area is.
+ //
+ * StringLocation = NetpPointerPlusSomeBytes(
+ FirstRecord,
+ EntrySize * EntryCount );
+
+ }
+
+ //
+ // Tell caller how everything went.
+ //
+ * BufPtr = FirstRecord;
+ return (ApiStatus);
+
+}
diff --git a/private/net/svcdlls/repl/common/expbuild.c b/private/net/svcdlls/repl/common/expbuild.c
new file mode 100644
index 000000000..690f23bff
--- /dev/null
+++ b/private/net/svcdlls/repl/common/expbuild.c
@@ -0,0 +1,160 @@
+/*++
+
+Copyright (c) 1992-1993 Microsoft Corporation
+
+Module Name:
+
+ ExpBuild.c
+
+Abstract:
+
+ This file contains ExportDirBuildApiRecord. This is used by
+ NetrReplExportDirGetInfo and NetrReplExportDirEnum.
+
+Author:
+
+ John Rogers (JohnRo) 08-Jan-1992
+
+Environment:
+
+ Runs under Windows NT.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Notes:
+
+ This code assumes that the export dir info levels are subsets of each other.
+ Also, this routine is callable whether or not the replicator service is
+ started.
+
+Revision History:
+
+ 08-Jan-1992 JohnRo
+ Created.
+ 08-Jan-1992 JohnRo
+ Fixed level 1 trashing bug.
+ 21-Jan-1992 JohnRo
+ Changed ExportDirBuildApiRecord's interface.
+ 24-Jan-1992 JohnRo
+ Changed to use LPTSTR etc.
+ 26-Feb-1992 JohnRo
+ API records now contain timestamps instead of elapsed times.
+ Check lock fields for validity.
+ Added assertion of valid record at end.
+ 15-Mar-1992 JohnRo
+ Improve setinfo info level support.
+ 28-Jul-1992 JohnRo
+ RAID 2274: repl svc should impersonate caller.
+ 08-Feb-1993 JohnRo
+ PC-LINT found a bug.
+ Use NetpKdPrint() where possible.
+
+--*/
+
+
+// These must be included first:
+
+#include <windef.h> // IN, VOID, LPTSTR, etc.
+#include <lmcons.h> // NET_API_STATUS, PARM equates, etc.
+#include <repldefs.h> // ReplIsIntegrityValid(), etc.
+
+// These can be in any order:
+
+#include <align.h> // POINTER_IS_ALIGNED(), ALIGN_TCHAR.
+#include <dirname.h> // ReplIsDirNameValid().
+#include <expdir.h> // My prototype, ExportDirIsLevelValid().
+#include <lmrepl.h> // LPREPL_EDIR_INFO_1, REPL_EXTENT_ stuff, etc.
+#include <netdebug.h> // NetpAssert(), NetpKdPrint(), etc.
+#include <prefix.h> // PREFIX_ equates.
+#include <tstr.h> // STRLEN(), etc.
+#include <winerror.h> // ERROR_ equates, NO_ERROR.
+
+
+NET_API_STATUS
+ExportDirBuildApiRecord (
+ IN DWORD Level,
+ IN LPTSTR DirName,
+ IN DWORD Integrity,
+ IN DWORD Extent,
+ IN DWORD LockCount,
+ IN DWORD TimeOfFirstLock, // Seconds since 1970.
+ OUT LPVOID Buffer,
+ IN OUT LPBYTE *StringLocation // Points just past top of data.
+ )
+
+{
+ LPREPL_EDIR_INFO_2 ApiRecord = Buffer; // superset info level
+ LPTSTR StringDest;
+ DWORD StringLength;
+
+ NetpAssert( StringLocation != NULL);
+ NetpAssert( *StringLocation != NULL);
+
+ IF_DEBUG( EXPAPI ) {
+ NetpKdPrint(( PREFIX_REPL
+ "ExportDirBuildApiRecord: building record at " FORMAT_LPVOID
+ ", *str loc is " FORMAT_LPVOID ".\n",
+ (LPVOID) Buffer, (LPVOID) *StringLocation ));
+ }
+
+ //
+ // Check for caller errors.
+ //
+ if (Buffer == NULL) {
+ return (ERROR_INVALID_PARAMETER);
+ }
+ NetpAssert( DirName != NULL);
+ if ( !ReplIsDirNameValid( DirName ) ) {
+ return (ERROR_INVALID_PARAMETER);
+ }
+ NetpAssert( Buffer != NULL);
+ if ( !ExportDirIsLevelValid( Level, FALSE ) ) { // don't allow setinfo.
+ return (ERROR_INVALID_LEVEL);
+ }
+ NetpAssert( ReplAreLockFieldsValid( LockCount, TimeOfFirstLock ) );
+
+ //
+ // First do subset common to all info levels.
+ //
+ StringLength = (DWORD) STRLEN( DirName );
+
+ NetpAssert( POINTER_IS_ALIGNED( *StringLocation, ALIGN_TCHAR ) );
+ StringDest = (LPTSTR) (LPVOID) (*StringLocation);
+ StringDest -= (StringLength + 1);
+
+ *StringLocation = (LPBYTE) (LPVOID) StringDest;
+
+ ApiRecord->rped2_dirname = StringDest;
+
+ (void) STRCPY(
+ StringDest, // dest
+ DirName); // src
+
+ //
+ // Next do stuff found in 1 and 2.
+ //
+ if (Level > 0) {
+ NetpAssert( ReplIsIntegrityValid( Integrity ) );
+ ApiRecord->rped2_integrity = Integrity;
+
+ NetpAssert( ReplIsExtentValid( Extent ) );
+ ApiRecord->rped2_extent = Extent;
+
+ //
+ // Now stuff only in level 2.
+ //
+ if (Level == 2) {
+ ApiRecord->rped2_lockcount = LockCount;
+
+ if (TimeOfFirstLock == 0) {
+ ApiRecord->rped2_locktime = 0;
+ } else {
+ ApiRecord->rped2_locktime = TimeOfFirstLock;
+ }
+ }
+ }
+
+ NetpAssert( ExportDirIsApiRecordValid( Level, ApiRecord, NULL ) );
+
+ return (NO_ERROR);
+
+}
diff --git a/private/net/svcdlls/repl/common/expconf.c b/private/net/svcdlls/repl/common/expconf.c
new file mode 100644
index 000000000..eadffc5a0
--- /dev/null
+++ b/private/net/svcdlls/repl/common/expconf.c
@@ -0,0 +1,694 @@
+/*++
+
+Copyright (c) 1992-1993 Microsoft Corporation
+
+Module Name:
+
+ ExpConf.c
+
+Abstract:
+
+ This file contains structures, function prototypes, and definitions
+ for the replicator export directory worker routines.
+
+Author:
+
+ John Rogers (JohnRo) 09-Jan-1992
+
+Environment:
+
+ User Mode - Win32
+ Portable to any flat, 32-bit environment. (Uses Win32 typedefs.)
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+ 09-Jan-1992 JohnRo
+ Created.
+ 23-Jan-1992 JohnRo
+ Clarify units for time parameters.
+ 28-Jan-1992 JohnRo
+ Added ExportDirConfigDataExists() and ExportDirDeleteConfigData().
+ ExportDirReadConfigData() should return NERR_UnknownDevDir.
+ Changed to use LPTSTR etc.
+ 03-Feb-1992 JohnRo
+ Corrected config _write where extent trashed integrity value.
+ Got rid of extra parse of comma.
+ Call ReplConfigReportBadParmValue() to inform user.
+ Corrected lengths used for integrity and extent.
+ 10-Feb-1992 JohnRo
+ ExportDirReadConfigData() should handle section not found.
+ 13-Feb-1992 JohnRo
+ Implement ExportDirDeleteConfigData().
+ Moved section name equates to ConfName.h.
+ 26-Feb-1992 JohnRo
+ API records now contain timestamps instead of elapsed times.
+ 23-Mar-1992 JohnRo
+ Get rid of old config helpers.
+ 10-Jul-1992 JohnRo
+ RAID 10503: srv mgr: repl dialog doesn't come up.
+ Use PREFIX_ equates.
+ 23-Jul-1992 JohnRo
+ RAID 2274: repl svc should impersonate caller.
+ 29-Sep-1992 JohnRo
+ Also fix remote repl admin.
+ 04-Dec-1992 JohnRo
+ RAID 3844: remote NetReplSetInfo uses local machine type.
+ Made changes suggested by PC-LINT 5.0
+ 21-Jan-1993 JohnRo
+ RAID 7717: Repl assert if not logged on correctly. (Also do event
+ logging for real.)
+ More changes suggested by PC-LINT 5.0
+ 25-Jan-1993 JohnRo
+ RAID 12914: avoid double close and free mem in ExportDirDeleteConfigData
+ 30-Apr-1993 JohnRo
+ Use NetpKdPrint() where possible.
+
+--*/
+
+
+#include <windef.h> // Win32 type definitions
+#include <lmcons.h> // LAN Manager common definitions
+#include <repldefs.h> // IF_DEBUG(), DWORDLEN.
+
+#include <config.h> // NetpConfig helpers.
+#include <confname.h> // SECT_NT_ equates.
+#include <dirname.h> // ReplIsDirNameValid().
+#include <expdir.h> // My prototypes.
+#include <lmapibuf.h> // NetApiBufferFree().
+#include <lmerr.h> // NERR_, ERROR_, NO_ERROR equates.
+#include <lmerrlog.h> // NELOG_ equates.
+#include <lmrepl.h> // REPL_INTEGRITY equates, etc.
+#include <netdebug.h> // NetpAssert(), etc.
+#include <netlib.h> // NetpPointerPlusSomeBytes().
+#include <prefix.h> // PREFIX_ equates.
+#include <replconf.h> // ReplConfigReportBadParmValue().
+#include <tstr.h> // STRCPY(), ATOL().
+
+
+#define STRING_FILE (LPVOID) TEXT("FILE")
+#define STRING_TREE (LPVOID) TEXT("TREE")
+
+#define MAX_INTEGRITY_LEN 4
+#define MAX_EXTENT_LEN 4
+
+#define EXPORT_VALUE_ARRAY_LEN \
+ (MAX_INTEGRITY_LEN /* integrity */ \
+ + 1 /* , */ \
+ + MAX_EXTENT_LEN /* extent */ \
+ + 1 /* , */ \
+ + DWORDLEN /* lockcount */ \
+ + 1 /* , */ \
+ + DWORDLEN ) /* locktime (seconds since 1970) */
+
+
+// Tells whether or not config data for this directory exists.
+// Callable even if the replicator service is not started.
+BOOL
+ExportDirConfigDataExists (
+ IN LPTSTR UncServerName OPTIONAL,
+ IN LPTSTR DirName // Caller must check dir name syntax.
+ )
+{
+ NET_API_STATUS ApiStatus;
+ LPNET_CONFIG_HANDLE Handle = NULL;
+ LPTSTR Value = NULL;
+
+ //
+ // Check for caller's errors.
+ //
+ NetpAssert( ReplIsDirNameValid( DirName ) );
+
+ //
+ // Open the right section of the config file/whatever.
+ //
+ ApiStatus = NetpOpenConfigDataEx(
+ & Handle,
+ UncServerName,
+ (LPTSTR) SECT_NT_REPLICATOR, // section
+ (LPTSTR) SECT_NT_REPLICATOR_EXPORTS, // area (instead of parameters)
+ TRUE); // read-only
+ if (ApiStatus != NO_ERROR) {
+ // Log error on server we were trying to access.
+ ReplErrorLog(
+ UncServerName,
+ NELOG_ReplSysErr,
+ ApiStatus,
+ NULL,
+ NULL );
+ return (FALSE); // section doesn't exist, so dir data doesn't.
+ }
+
+ //
+ // Read the value from the config file/whatever.
+ //
+ ApiStatus = NetpGetConfigValue(
+ Handle,
+ DirName, // keyword is dir name
+ & Value); // alloc and set ptr
+
+ //
+ // We're done with this, so close the config data and toss the buffer we
+ // got. But remember the status from NetpGetConfigValue!
+ //
+ if (Value != NULL) {
+ (void) NetApiBufferFree( Value );
+ }
+ (void) NetpCloseConfigData( Handle );
+
+ //
+ // Now check the status of the Get.
+ //
+ if (ApiStatus == NERR_CfgParamNotFound) {
+ return (FALSE); // doesn't exist
+ } else if (ApiStatus == NO_ERROR) {
+ return (TRUE); // exists
+ } else {
+ // Log error on server we were trying to access.
+ ReplErrorLog(
+ UncServerName,
+ NELOG_ReplSysErr,
+ ApiStatus,
+ NULL,
+ NULL );
+ return (FALSE); // Unexpected error, so assume it doesn't exist.
+ }
+
+ /*NOTREACHED*/
+
+} // ExportDirConfigDataExists
+
+
+// Delete config data for this directory.
+// Returns NERR_UnknownDevDir if config data doesn't exist for this dir.
+// Callable even if the replicator service is not started.
+NET_API_STATUS
+ExportDirDeleteConfigData (
+ IN LPTSTR UncServerName OPTIONAL,
+ IN LPTSTR DirName // Caller must check dir name syntax.
+ )
+{
+ NET_API_STATUS ApiStatus;
+ LPNET_CONFIG_HANDLE Handle = NULL;
+
+ //
+ // Check for caller's errors.
+ //
+ if ( ! ReplIsDirNameValid( DirName ) ) {
+ ApiStatus = ERROR_INVALID_PARAMETER;
+ goto Cleanup; // go log error
+ }
+
+ //
+ // Open the right section of the config file/whatever.
+ //
+ ApiStatus = NetpOpenConfigDataEx(
+ & Handle,
+ UncServerName,
+ (LPTSTR) SECT_NT_REPLICATOR, // section
+ (LPTSTR) SECT_NT_REPLICATOR_EXPORTS, // area (instead of parameters)
+ FALSE ); // not read-only
+ if (ApiStatus == NERR_CfgCompNotFound) {
+ ApiStatus = NERR_UnknownDevDir;
+ goto Cleanup; // go log error
+ } else if (ApiStatus != NO_ERROR) {
+ goto Cleanup; // go log error
+ }
+
+ //
+ // Delete this keyword from this section.
+ //
+ ApiStatus = NetpDeleteConfigKeyword(
+ Handle,
+ DirName ); // keyword is dir name
+
+ IF_DEBUG(EXPAPI) {
+ NetpKdPrint(( PREFIX_REPL
+ "ExportDirDeleteConfigData( " FORMAT_LPTSTR " ): conf del ret "
+ FORMAT_API_STATUS ".\n",
+ (UncServerName!=NULL) ? UncServerName : (LPTSTR) TEXT("local"),
+ ApiStatus ));
+ }
+
+ if (ApiStatus == NERR_CfgParamNotFound) {
+ ApiStatus = NERR_UnknownDevDir;
+ goto Cleanup; // go log error
+ } else if (ApiStatus != NO_ERROR) {
+ goto Cleanup; // go log error
+ }
+
+Cleanup:
+
+ //
+ // All done.
+ //
+ if ( Handle != NULL ) {
+ NET_API_STATUS CloseStatus;
+
+ CloseStatus = NetpCloseConfigData( Handle );
+
+ // Return this error iff it is first error we've gotten.
+ if ( (ApiStatus == NO_ERROR) && (CloseStatus != NO_ERROR) ) {
+ ApiStatus = CloseStatus;
+ }
+ }
+
+ if (ApiStatus != NO_ERROR) {
+
+ // Log error on server we were trying to access.
+ ReplErrorLog(
+ UncServerName,
+ NELOG_ReplSysErr,
+ ApiStatus,
+ NULL,
+ NULL );
+ }
+
+ return (ApiStatus);
+
+} // ExportDirDeleteConfigData
+
+
+// Parse config data for a single export directory. Callable whether or not
+// the replicator service is started. (This function is used in this file
+// and by the NetReplExportDirEnum routine.)
+NET_API_STATUS
+ExportDirParseConfigData (
+ IN LPTSTR UncServerName OPTIONAL,
+ IN LPTSTR ValueString,
+ OUT LPDWORD IntegrityPtr,
+ OUT LPDWORD ExtentPtr,
+ OUT LPDWORD LockCountPtr,
+ OUT LPDWORD LockTimePtr // Seconds since 1970.
+ )
+{
+ LPTSTR CurrentValuePtr = ValueString;
+
+ //
+ // Check for caller's errors.
+ //
+ NetpAssert( ValueString != NULL );
+ if (STRLEN( ValueString ) > EXPORT_VALUE_ARRAY_LEN) {
+ goto ReportBadConfigLine;
+ }
+
+ //
+ // Start parsing the value, which begins with a string containing the
+ // integrity.
+ //
+
+#define TRY_INTEGRITY( IntegrityEquate, IntegrityString ) \
+ { \
+ DWORD HopefulStringLength = STRLEN(IntegrityString); \
+ if (STRNCMP( CurrentValuePtr, (IntegrityString), HopefulStringLength) == 0) { \
+ *IntegrityPtr = (IntegrityEquate); \
+ CurrentValuePtr = (LPTSTR) (LPVOID) \
+ NetpPointerPlusSomeBytes( CurrentValuePtr, \
+ HopefulStringLength * sizeof(TCHAR) ); \
+ goto DoneIntegrity; \
+ } \
+ }
+
+ TRY_INTEGRITY( REPL_INTEGRITY_FILE, STRING_FILE )
+
+ TRY_INTEGRITY( REPL_INTEGRITY_TREE, STRING_TREE )
+
+ goto ReportBadConfigLine;
+
+DoneIntegrity:
+
+ //
+ // Parse the comma after the integrity string.
+ //
+
+#define PARSE_CHAR(AsciiChar) \
+ { \
+ if (*CurrentValuePtr == MAKE_TCHAR(AsciiChar)) { \
+ ++CurrentValuePtr; \
+ } else { \
+ goto ReportBadConfigLine; \
+ } \
+ }
+
+#define PARSE_COMMA( ) PARSE_CHAR(',')
+
+ PARSE_COMMA();
+
+ //
+ // Now do the extent string.
+ //
+
+#define TRY_EXTENT( ExtentEquate, ExtentString ) \
+ { \
+ DWORD HopefulStringLength = STRLEN(ExtentString); \
+ if (STRNCMP( CurrentValuePtr, (ExtentString), HopefulStringLength) == 0) { \
+ *ExtentPtr = (ExtentEquate); \
+ CurrentValuePtr = (LPTSTR) (LPVOID) \
+ NetpPointerPlusSomeBytes( CurrentValuePtr, \
+ HopefulStringLength * sizeof(TCHAR) ); \
+ goto DoneExtent; \
+ } \
+ }
+
+ TRY_EXTENT( REPL_EXTENT_FILE, STRING_FILE )
+
+ TRY_EXTENT( REPL_EXTENT_TREE, STRING_TREE )
+
+ goto ReportBadConfigLine;
+
+DoneExtent:
+
+ PARSE_COMMA();
+
+ //
+ // Parse the numbers on the rest of the line.
+ //
+
+#define PARSE_DWORD( NumberPtr ) \
+ { \
+ if ( ! ISDIGIT( *CurrentValuePtr ) ) { \
+ goto ReportBadConfigLine; \
+ } \
+ NetpAssert( NumberPtr != NULL ); \
+ * NumberPtr = (DWORD) ATOL( CurrentValuePtr ); \
+ while ( ISDIGIT( *CurrentValuePtr ) ) { \
+ ++CurrentValuePtr; \
+ } \
+ }
+
+ PARSE_DWORD( LockCountPtr );
+
+ PARSE_COMMA();
+
+ PARSE_DWORD( LockTimePtr );
+
+ IF_DEBUG(EXPAPI) {
+ NetpKdPrint(( PREFIX_REPL
+ "ExportDirParseConfigValue: Value = '" FORMAT_LPTSTR "',\n",
+ ValueString ));
+ NetpKdPrint((
+ " Integrity = " FORMAT_DWORD
+ ", Extent = " FORMAT_DWORD
+ ", lock count = " FORMAT_DWORD ".\n",
+ *IntegrityPtr, *ExtentPtr, *LockCountPtr ));
+ NetpDbgDisplayTimestamp( "lock time", *LockTimePtr );
+ }
+
+ if ( ! ReplIsIntegrityValid( *IntegrityPtr ) ) {
+ goto ReportBadConfigLine;
+ }
+ if ( ! ReplIsExtentValid( *ExtentPtr ) ) {
+ goto ReportBadConfigLine;
+ }
+
+ return (NO_ERROR);
+
+ReportBadConfigLine:
+
+ // BUGBUG: Sure would be nice if we could include dirname here.
+
+ ReplConfigReportBadParmValue(
+ UncServerName,
+ (LPTSTR) SECT_NT_REPLICATOR_EXPORTS,
+ ValueString );
+ return (ERROR_INVALID_DATA);
+
+} // ExportDirParseConfigData
+
+
+// Read config data for a single export directory. Callable whether or not
+// the replicator service is started. Returns NERR_UnknownDevDir if no
+// config data exists for this directory.
+NET_API_STATUS
+ExportDirReadConfigData (
+ IN LPTSTR UncServerName OPTIONAL,
+ IN LPTSTR DirName,
+ OUT LPDWORD IntegrityPtr,
+ OUT LPDWORD ExtentPtr,
+ OUT LPDWORD LockCountPtr,
+ OUT LPDWORD LockTimePtr // Seconds since 1970.
+ )
+{
+ NET_API_STATUS ApiStatus;
+ LPNET_CONFIG_HANDLE Handle = NULL;
+ LPTSTR Value = NULL;
+
+ //
+ // Check for caller's errors.
+ //
+ if ( ! ReplIsDirNameValid( DirName ) ) {
+ ApiStatus = ERROR_INVALID_PARAMETER;
+ goto Cleanup; // go log error
+ }
+
+ //
+ // Open the right section of the config file/whatever.
+ //
+ ApiStatus = NetpOpenConfigDataEx(
+ & Handle,
+ UncServerName,
+ (LPTSTR) SECT_NT_REPLICATOR, // section
+ (LPTSTR) SECT_NT_REPLICATOR_EXPORTS, // area (instead of parameters)
+ TRUE); // read-only
+ if (ApiStatus == NERR_CfgCompNotFound) {
+ ApiStatus = NERR_UnknownDevDir;
+ goto Cleanup; // go log error
+ } else if (ApiStatus != NO_ERROR) {
+ goto Cleanup; // go log error
+ }
+
+ //
+ // Read the value from the config file/whatever.
+ //
+ ApiStatus = NetpGetConfigValue(
+ Handle,
+ DirName, // keyword is dir name
+ & Value); // alloc and set ptr
+ if (ApiStatus == NERR_CfgParamNotFound) {
+ ApiStatus = NERR_UnknownDevDir;
+ goto Cleanup; // go log error
+ } else if (ApiStatus != NO_ERROR) {
+ goto Cleanup; // go log error
+ }
+ NetpAssert( Value != NULL );
+
+ IF_DEBUG(EXPAPI) {
+ NetpKdPrint(( PREFIX_REPL
+ "ExportDirReadConfigValue( " FORMAT_LPTSTR " ): '"
+ FORMAT_LPTSTR "' = '" FORMAT_LPTSTR "'.\n",
+ (UncServerName!=NULL) ? UncServerName : (LPTSTR) TEXT("local"),
+ DirName, Value ));
+ }
+
+ //
+ // Parse the value string...
+ //
+ ApiStatus = ExportDirParseConfigData (
+ UncServerName,
+ Value,
+ IntegrityPtr,
+ ExtentPtr,
+ LockCountPtr,
+ LockTimePtr); // Seconds since 1970.
+ // Fall through and log error if any.
+
+Cleanup:
+
+ //
+ // All done.
+ //
+ if ( Handle != NULL ) {
+ NET_API_STATUS CloseStatus;
+
+ CloseStatus = NetpCloseConfigData( Handle );
+
+ // Return this error iff it is first error we've gotten.
+ if ( (ApiStatus == NO_ERROR) && (CloseStatus != NO_ERROR) ) {
+ ApiStatus = CloseStatus;
+ }
+ }
+
+ (VOID) NetApiBufferFree( Value );
+
+ if (ApiStatus != NO_ERROR) {
+
+ // Log error on server we were trying to access.
+ ReplErrorLog(
+ UncServerName,
+ NELOG_ReplSysErr,
+ ApiStatus,
+ NULL,
+ NULL );
+ }
+ return (ApiStatus);
+
+} // ExportDirReadConfigData
+
+
+
+
+
+// Write config data for a single export directory. Callable whether or not
+// the replicator service is started.
+NET_API_STATUS
+ExportDirWriteConfigData (
+ IN LPTSTR UncServerName OPTIONAL,
+ IN LPTSTR DirName,
+ IN DWORD Integrity,
+ IN DWORD Extent,
+ IN DWORD LockCount,
+ IN DWORD LockTime // Seconds since 1970.
+ )
+{
+ NET_API_STATUS ApiStatus;
+ LPNET_CONFIG_HANDLE Handle = NULL;
+ TCHAR ValueArray[EXPORT_VALUE_ARRAY_LEN+1];
+
+ //
+ // Check for caller's errors.
+ //
+ if ( ! ReplIsDirNameValid( DirName ) ) {
+ ApiStatus = ERROR_INVALID_PARAMETER;
+ goto Cleanup; // go log error
+ }
+ if ( ! ReplIsIntegrityValid( Integrity ) ) {
+ ApiStatus = ERROR_INVALID_PARAMETER;
+ goto Cleanup; // go log error
+ }
+ if ( ! ReplIsExtentValid( Extent ) ) {
+ ApiStatus = ERROR_INVALID_PARAMETER;
+ goto Cleanup; // go log error
+ }
+
+ //
+ // Open the right section of the config file/whatever.
+ //
+ ApiStatus = NetpOpenConfigDataEx(
+ & Handle,
+ UncServerName,
+ (LPTSTR) SECT_NT_REPLICATOR, // section
+ (LPTSTR) SECT_NT_REPLICATOR_EXPORTS, // area (instead of parameters)
+ FALSE); // not read-only
+ if (ApiStatus != NO_ERROR) {
+ goto Cleanup; // go log error
+ }
+
+ //
+ // Start building the value, which begins with a string containing the
+ // integrity.
+ //
+ switch (Integrity) {
+ case REPL_INTEGRITY_FILE:
+
+ (void) STRCPY( ValueArray, STRING_FILE);
+ break;
+
+ case REPL_INTEGRITY_TREE:
+
+ (void) STRCPY( ValueArray, STRING_TREE);
+ break;
+
+ default:
+ NetpAssert( FALSE );
+ ApiStatus = ERROR_INVALID_PARAMETER;
+ goto Cleanup; // go log error
+ }
+
+ //
+ // Fields get seperated by commas.
+ //
+/*lint -save -e767 */ // Don't complain about different definitions
+#define WRITE_COMMA( ) \
+ (void) STRCAT( ValueArray, (LPVOID) TEXT(",") )
+/*lint -restore */ // Resume checking for different macro definitions
+
+ WRITE_COMMA();
+
+ //
+ // Next we do the extent.
+ //
+ switch (Extent) {
+ case REPL_EXTENT_FILE:
+
+ (void) STRCAT( ValueArray, STRING_FILE);
+ break;
+
+ case REPL_EXTENT_TREE:
+
+ (void) STRCAT( ValueArray, STRING_TREE);
+ break;
+
+ default:
+ NetpAssert( FALSE );
+ ApiStatus = ERROR_INVALID_PARAMETER;
+ goto Cleanup; // go log error
+ }
+
+ WRITE_COMMA();
+
+ //
+ // Now do the last two fields, with are both DWORDS.
+ //
+
+/*lint -save -e767 */ // Don't complain about different definitions
+#define WRITE_DWORD( Number ) \
+ { \
+ LPTSTR StrEnd = & ValueArray[ STRLEN( ValueArray ) ]; \
+ (void) ULTOA( (Number), StrEnd, /* radix */ 10 ); \
+ }
+/*lint -restore */ // Resume checking for different macro definitions
+
+ WRITE_DWORD( LockCount );
+
+ WRITE_COMMA();
+
+ WRITE_DWORD( LockTime ); // Seconds since 1970.
+
+ IF_DEBUG(EXPAPI) {
+ NetpKdPrint(( PREFIX_REPL
+ "ExportDirWriteConfigValue( " FORMAT_LPTSTR "): '"
+ FORMAT_LPTSTR "' = '" FORMAT_LPTSTR "'.\n",
+ (UncServerName!=NULL) ? UncServerName : (LPVOID) TEXT("local"),
+ DirName, ValueArray ));
+ }
+
+ //
+ // Write this value out to the config file/whatever.
+ //
+ ApiStatus = NetpSetConfigValue(
+ Handle,
+ DirName, // keyword is dir name
+ ValueArray);
+ // Faill through and log error if any.
+
+
+Cleanup:
+
+ //
+ // All done.
+ //
+ if ( Handle != NULL ) {
+ NET_API_STATUS CloseStatus;
+
+ CloseStatus = NetpCloseConfigData( Handle );
+
+ // Return this error iff it is first error we've gotten.
+ if ( (ApiStatus == NO_ERROR) && (CloseStatus != NO_ERROR) ) {
+ ApiStatus = CloseStatus;
+ }
+ }
+
+ if (ApiStatus != NO_ERROR) {
+
+ // Log error on server we were trying to access.
+ ReplErrorLog(
+ UncServerName,
+ NELOG_ReplSysErr,
+ ApiStatus,
+ NULL,
+ NULL );
+ }
+
+ return (ApiStatus);
+
+} // ExportDirWriteConfigData
diff --git a/private/net/svcdlls/repl/common/expdir.h b/private/net/svcdlls/repl/common/expdir.h
new file mode 100644
index 000000000..292b705fb
--- /dev/null
+++ b/private/net/svcdlls/repl/common/expdir.h
@@ -0,0 +1,265 @@
+/*++
+
+Copyright (c) 1992-1993 Microsoft Corporation
+
+Module Name:
+
+ ExpDir.h
+
+Abstract:
+
+ This file contains structures, function prototypes, and definitions
+ for the replicator export directory worker routines.
+
+Author:
+
+ John Rogers (JohnRo) 08-Jan-1992
+
+Environment:
+
+ User Mode - Win32
+ Portable to any flat, 32-bit environment. (Uses Win32 typedefs.)
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Notes:
+
+ You must include LmCons.h before this file.
+
+Revision History:
+
+ 08-Jan-1992 JohnRo
+ Created.
+ 09-Jan-1992 JohnRo
+ Added EXPORT_DIR_SECTION_NAME equate.
+ Added ExportDir{Read,Write}ConfigData.
+ 20-Jan-1992 JohnRo
+ Netr prototypes are now generated by MIDL and put in repl.h.
+ 23-Jan-1992 JohnRo
+ Clarify units for time parameters.
+ Changed EXPORT_DIR_SECTION_NAME.
+ Changed ExportDirBuildApiRecord's interface.
+ Added ExportDirIsApiRecordValid().
+ Added ExportDirAllocApiRecord().
+ 28-Jan-1992 JohnRo
+ Changed ExportDirAllocApiRecords() to allow arrays.
+ Added ExportDirConfigDataExists() and ExportDirDeleteConfigData().
+ Changed to use LPTSTR etc.
+ 09-Feb-1992 JohnRo
+ Added ExportDir{Start,Stop}Repl routines.
+ 13-Feb-1992 JohnRo
+ Moved section name equates to ConfName.h.
+ 15-Mar-1992 JohnRo
+ Update registry with new values.
+ Improve support for setinfo info levels.
+ 23-Mar-1992 JohnRo
+ Added ExportDirReadMasterList().
+ 30-Jul-1992 JohnRo
+ Help PC-LINT understand ExportDirIsLevelValid().
+ 29-Sep-1992 JohnRo
+ RAID 7962: Repl APIs in wrong role kill svc.
+ Also fix remote repl admin.
+ 01-Dec-1992 JohnRo
+ RAID 3844: remote NetReplSetInfo uses local machine type.
+ 13-Jan-1993 JohnRo
+ RAID 7053: locked trees added to pulse msg. (Actually fix all
+ kinds of remote lock handling.)
+ 13-Apr-1993 JohnRo
+ RAID 3107: locking directory over the net gives network path not found.
+
+--*/
+
+
+#ifndef _EXPDIR_
+#define _EXPDIR_
+
+
+#include <netlib.h> // IN_RANGE().
+
+
+//
+// Export dir helper routines and macros:
+//
+
+// Allocate one or more API records for an export directory. Callable whether
+// or not the replicator service is started. (Used in getinfo stub, getinfo
+// worker, and enum stub.)
+NET_API_STATUS
+ExportDirAllocApiRecords (
+ IN DWORD Level,
+ IN DWORD EntryCount,
+ OUT LPBYTE * BufPtr,
+ IN OUT LPBYTE *StringLocation // Points just past top of data.
+ );
+
+// Build API record for an export directory. Callable whether or not
+// the replicator service is started. (Used in getinfo and enum.)
+NET_API_STATUS
+ExportDirBuildApiRecord (
+ IN DWORD Level,
+ IN LPTSTR DirName,
+ IN DWORD Integrity,
+ IN DWORD Extent,
+ IN DWORD LockCount,
+ IN DWORD TimeOfFirstLock, // Seconds since 1970.
+ OUT LPVOID Buffer,
+ IN OUT LPBYTE *StringLocation // Points just past top of data.
+ );
+
+// Tells whether or not config data for this directory exists.
+// Callable even if the replicator service is not started.
+BOOL
+ExportDirConfigDataExists (
+ IN LPTSTR UncServerName OPTIONAL,
+ IN LPTSTR DirName // Caller must check dir name syntax.
+ );
+
+NET_API_STATUS
+ExportDirConfigSetInfo (
+ IN LPTSTR UncServerName OPTIONAL,
+ IN LPTSTR DirName,
+ IN DWORD Level,
+ IN LPVOID Buf,
+ OUT LPDWORD ParmError OPTIONAL
+ );
+
+// Delete config data for this directory.
+// Returns NERR_UnknownDevDir if config data doesn't exist for this dir.
+// Callable even if the replicator service is not started.
+NET_API_STATUS
+ExportDirDeleteConfigData (
+ IN LPTSTR UncServerName OPTIONAL,
+ IN LPTSTR DirName // Caller must check dir name syntax.
+ );
+
+// Callable even if the replicator service is not started.
+NET_API_STATUS
+ExportDirEnumApiRecords(
+ IN LPTSTR UncServerName OPTIONAL,
+ IN DWORD Level,
+ OUT LPBYTE * BufPtr,
+ IN DWORD PrefMaxSize,
+ OUT LPDWORD EntriesRead,
+ OUT LPDWORD TotalEntries
+ );
+
+// Callable even if the replicator service is not started.
+NET_API_STATUS
+ExportDirFixUserLockFiles(
+ IN LPCTSTR ExportPath, // Must include drive letter.
+ IN LPCTSTR DirName,
+ IN DWORD LockCount
+ );
+
+// Callable even if the replicator service is not started.
+NET_API_STATUS
+ExportDirGetApiRecord (
+ IN LPTSTR UncServerName OPTIONAL,
+ IN LPTSTR DirName,
+ IN DWORD Level,
+ OUT LPBYTE * BufPtr
+ );
+
+BOOL
+ExportDirIsApiRecordValid (
+ IN DWORD Level,
+ IN LPVOID ApiRecord,
+ OUT LPDWORD ParmError OPTIONAL
+ );
+
+// Callable whether or not service is started.
+// If service is running, assume caller has lock (any kind) on RMGlobalListLock.
+NET_API_STATUS
+ExportDirLockInRegistry(
+ IN LPTSTR UncServerName OPTIONAL,
+ IN LPTSTR DirName
+ );
+
+// Parse config data for a single export directory. Callable whether or not
+// the replicator service is started. (This function is used by routines
+// in Repl/Common/ExpConf.c and by the NetReplExportDirEnum routine.)
+NET_API_STATUS
+ExportDirParseConfigData (
+ IN LPTSTR UncServerName OPTIONAL,
+ IN LPTSTR ValueString,
+ OUT LPDWORD IntegrityPtr,
+ OUT LPDWORD ExtentPtr,
+ OUT LPDWORD LockCountPtr,
+ OUT LPDWORD LockTimePtr // Seconds since 1970.
+ );
+
+// Read config data for a single export directory. Callable whether or not
+// the replicator service is started.
+NET_API_STATUS
+ExportDirReadConfigData (
+ IN LPTSTR UncServerName OPTIONAL,
+ IN LPTSTR DirName,
+ OUT LPDWORD IntegrityPtr,
+ OUT LPDWORD ExtentPtr,
+ OUT LPDWORD LockCountPtr,
+ OUT LPDWORD LockTimePtr // Seconds since 1970.
+ );
+
+// Read export dirs into service's master list.
+// Only callable locally, when service is started.
+// This also fixes the USERLOCK.* file(s) to match the lock count in registry.
+NET_API_STATUS
+ExportDirReadMasterList(
+ VOID
+ );
+
+// Read specified export dir into service's master list.
+NET_API_STATUS
+ExportDirGetRegistryValues(
+ IN LPTSTR ServiceRegPath OPTIONAL,
+ IN LPTSTR TargetName
+ );
+
+// Start replicating (exporting).
+// Called when service starts or user does NetReplSetInfo() and changes role.
+NET_API_STATUS
+ExportDirStartRepl (
+ IN BOOL ServiceIsStarting
+ );
+
+// Stop replicating (exporting).
+// Called when service stops or user does NetReplSetInfo() and changes role.
+NET_API_STATUS
+ExportDirStopRepl (
+ VOID
+ );
+
+// Callable whether or not service is started.
+// If service is running, assume caller has lock (any kind) on RMGlobalListLock.
+NET_API_STATUS
+ExportDirUnlockInRegistry(
+ IN LPTSTR UncServerName OPTIONAL,
+ IN LPTSTR DirName,
+ IN DWORD UnlockForce
+ );
+
+// Write config data for a single export directory. Callable whether or not
+// the replicator service is started.
+NET_API_STATUS
+ExportDirWriteConfigData (
+ IN LPTSTR UncServerName OPTIONAL,
+ IN LPTSTR DirName,
+ IN DWORD Integrity,
+ IN DWORD Extent,
+ IN DWORD LockCount,
+ IN DWORD LockTime // Seconds since 1970.
+ );
+
+// BOOL
+// ExportDirIsLevelValid(
+// IN DWORD Level, // Info level
+// IN BOOL SetInfo // Are setinfo levels allowed?
+// );
+//
+#define ExportDirIsLevelValid(Level,SetInfo) \
+ /*lint -e506 */ /* don't complain about constant values here */ \
+ ( ( (Level) <= 2 ) \
+ || ( (SetInfo) && (IN_RANGE((Level), 1000, 1001)) ) ) \
+ /*lint +e506 */ \
+
+
+#endif // _EXPDIR_
diff --git a/private/net/svcdlls/repl/common/expenum.c b/private/net/svcdlls/repl/common/expenum.c
new file mode 100644
index 000000000..ffef39aa8
--- /dev/null
+++ b/private/net/svcdlls/repl/common/expenum.c
@@ -0,0 +1,298 @@
+/*++
+
+Copyright (c) 1991-1993 Microsoft Corporation
+
+Module Name:
+
+ ExpEnum.c
+
+Abstract:
+
+ ExportDirEnumApiRecords().
+
+Author:
+
+ John Rogers (JohnRo) 29-Sep-1992
+
+Environment:
+
+ User Mode - Win32
+
+Revision History:
+
+ 29-Sep-1992 JohnRo
+ RAID 7962: Repl APIs in wrong role kill svc. (Extracted this code from
+ NetExportDirEnum's DLL stub.)
+ 01-Dec-1992 JohnRo
+ RAID 3844: remote NetReplSetInfo uses local machine type.
+ 10-Mar-1993 JohnRo
+ RAID 12871: replication UI shows nothing (adding entry while enumerating
+ results in empty list).
+ Made changes suggested by PC-LINT 5.0
+
+--*/
+
+
+// These must be included first:
+
+
+#include <windows.h>
+#include <lmcons.h> // NET_API_STATUS, etc.
+#include <netdebug.h> // NetpAssert().
+
+// These may be included in any order:
+
+#include <config.h> // LPNET_CONFIG_HANDLE, Netp config routines.
+#include <confname.h> // SECT_NT_ equates.
+#include <dirname.h> // ReplIsDirNameValid().
+#include <expdir.h> // ExportDirIsApiRecordValid(), my prototype, etc.
+#include <lmerr.h> // NERR_ and ERROR_ equates, NO_ERROR.
+#include <lmerrlog.h> // NELOG_ equates.
+#include <netlib.h> // NetpSetParmError(), etc.
+#include <prefix.h> // PREFIX_ equates.
+#include <repldefs.h> // IF_DEBUG(), ReplErrorLog(), etc.
+#include <strucinf.h> // NetpReplExportDirStructureInfo().
+
+
+// Callable even if the replicator service is not started.
+NET_API_STATUS
+ExportDirEnumApiRecords(
+ IN LPTSTR UncServerName OPTIONAL,
+ IN DWORD Level,
+ OUT LPBYTE * BufPtr,
+ IN DWORD PrefMaxSize,
+ OUT LPDWORD EntriesRead,
+ OUT LPDWORD TotalEntries
+ )
+{
+ NET_API_STATUS ApiStatus;
+ LPVOID ArrayEntry;
+ LPVOID ArrayStart = NULL;
+ DWORD EntriesAllocated = 0;
+ DWORD EntriesFound = 0;
+ BOOL FirstTime;
+ DWORD FixedEntrySize;
+ LPNET_CONFIG_HANDLE Handle = NULL;
+ LPBYTE StringLocation;
+
+ UNREFERENCED_PARAMETER( PrefMaxSize );
+
+ if ( !ExportDirIsLevelValid( Level, FALSE ) ) {
+ ApiStatus = ERROR_INVALID_LEVEL;
+ goto Cleanup;
+ } else if (BufPtr == NULL) {
+ ApiStatus = ERROR_INVALID_PARAMETER;
+ goto Cleanup;
+ } else if (EntriesRead == NULL) {
+ ApiStatus = ERROR_INVALID_PARAMETER;
+ goto Cleanup;
+ } else if (TotalEntries == NULL) {
+ ApiStatus = ERROR_INVALID_PARAMETER;
+ goto Cleanup;
+ }
+
+ //
+ // Set outputs in case we run into an error.
+ //
+
+ * BufPtr = NULL;
+ * EntriesRead = 0;
+ * TotalEntries = 0;
+
+ //
+ // Figure-out the size of a fixed entry for this info level.
+ //
+ ApiStatus = NetpReplExportDirStructureInfo (
+ Level,
+ PARMNUM_ALL,
+ TRUE, // want native sizes
+ NULL, // don't need data desc 16
+ NULL, // don't need data desc 32
+ NULL, // don't need data desc SMB
+ NULL, // don't need max size
+ & FixedEntrySize,
+ NULL ); // don't need string size
+
+ NetpAssert( ApiStatus == NO_ERROR ); // Already checked args.
+ NetpAssert( FixedEntrySize > 0 );
+
+ //
+ // Open the right section of the config file/whatever.
+ //
+ ApiStatus = NetpOpenConfigDataEx(
+ & Handle,
+ UncServerName,
+ (LPTSTR) SECT_NT_REPLICATOR, // service name
+ (LPTSTR) SECT_NT_REPLICATOR_EXPORTS, // area under service
+ TRUE); // read-only
+ if (ApiStatus != NO_ERROR) {
+
+ // Handle section not found as empty enum array (not error).
+ if (ApiStatus == NERR_CfgCompNotFound) {
+ ApiStatus = NO_ERROR;
+ goto Cleanup;
+ }
+
+ goto Cleanup;
+ }
+
+ //
+ // Loop, expanding buffer if necessary, until we get it large enough.
+ //
+
+ do {
+
+ //
+ // Count entries in config data.
+ //
+ ApiStatus = NetpNumberOfConfigKeywords (
+ Handle,
+ & EntriesAllocated );
+
+ if (ApiStatus != NO_ERROR) {
+ goto Cleanup;
+ } else if (EntriesAllocated == 0) {
+ goto Cleanup;
+ }
+
+ //
+ // Alloc the array...
+ //
+ ApiStatus = ExportDirAllocApiRecords (
+ Level,
+ EntriesAllocated,
+ (LPBYTE *) & ArrayStart,
+ & StringLocation ); // Points just past top of data.
+ if (ApiStatus == NO_ERROR) {
+ NetpAssert( ArrayStart != NULL );
+ } else {
+ goto Cleanup;
+ }
+
+ //
+ // Go back and reread, filling-in the config data as we go.
+ //
+
+ ArrayEntry = ArrayStart;
+ FirstTime = TRUE;
+
+ while (ApiStatus == NO_ERROR) {
+ LPTSTR DirName;
+ DWORD Integrity, Extent, LockCount, LockTime;
+ LPTSTR ValueString;
+
+ ApiStatus = NetpEnumConfigSectionValues (
+ Handle,
+ & DirName, // Keyword - alloc and set ptr.
+ & ValueString, // Must be freed by NetApiBufferFree().
+ FirstTime );
+
+ FirstTime = FALSE;
+
+ if (ApiStatus == NO_ERROR) {
+ NetpAssert( DirName != NULL );
+ NetpAssert( ValueString != NULL );
+
+ ++EntriesFound;
+ if (EntriesFound > EntriesAllocated) {
+ EntriesFound = 0;
+ NetpMemoryFree( ArrayStart );
+ ArrayStart = NULL;
+ ApiStatus = ERROR_MORE_DATA;
+ break; // exit per-entry loop and try again
+ }
+
+ if ( !ReplIsDirNameValid( DirName ) ) {
+ // BUGBUG: perhaps delete entry, log event, and continue?
+ ApiStatus = ERROR_INVALID_DATA;
+ NetpMemoryFree( DirName );
+ NetpMemoryFree( ValueString );
+ goto Cleanup;
+ }
+
+ //
+ // Parse the value string...
+ //
+ ApiStatus = ExportDirParseConfigData (
+ UncServerName,
+ ValueString,
+ & Integrity,
+ & Extent,
+ & LockCount,
+ & LockTime); // Seconds since 1970.
+
+ NetpMemoryFree( ValueString );
+
+ if (ApiStatus == NO_ERROR) {
+ //
+ // Build API record for this entry.
+ //
+ ApiStatus = ExportDirBuildApiRecord (
+ Level,
+ DirName,
+ Integrity,
+ Extent,
+ LockCount,
+ LockTime, // Seconds since 1970.
+ ArrayEntry,
+ & StringLocation );
+ if (ApiStatus != NO_ERROR) {
+ NetpKdPrint(( PREFIX_REPL
+ "ExportDirEnumApiRecords: error "
+ FORMAT_API_STATUS " from exp build.\n",
+ ApiStatus ));
+ NetpAssert( FALSE );
+ goto Cleanup;
+ }
+
+ ArrayEntry = NetpPointerPlusSomeBytes(
+ ArrayEntry, FixedEntrySize );
+ }
+ NetpMemoryFree( DirName );
+
+ }
+
+ } // while not error (may be NERR_CfgParamNotFound at end).
+
+ if (ApiStatus == NERR_CfgParamNotFound) {
+ ApiStatus = NO_ERROR;
+ } else {
+ NetpAssert( ApiStatus != NO_ERROR );
+ goto Cleanup;
+ }
+
+ } while (ApiStatus == ERROR_MORE_DATA);
+
+ //
+ // All done.
+ //
+
+Cleanup:
+
+ if (Handle != NULL) {
+ (VOID) NetpCloseConfigData( Handle );
+ }
+
+ NetpAssert( ApiStatus != ERROR_MORE_DATA );
+ if (ApiStatus == NO_ERROR) {
+ * BufPtr = ArrayStart;
+ * EntriesRead = EntriesFound;
+ * TotalEntries = EntriesFound;
+ } else {
+ NetpKdPrint(( PREFIX_REPL
+ "ExportDirEnumApiRecords: returning status " FORMAT_API_STATUS
+ ".\n", ApiStatus ));
+
+ // Log the error.
+ ReplErrorLog(
+ UncServerName, // log here and there.
+ NELOG_ReplSysErr,
+ ApiStatus,
+ NULL, // no str1
+ NULL ); // no str2
+
+ }
+
+ return ApiStatus;
+
+}
diff --git a/private/net/svcdlls/repl/common/expget.c b/private/net/svcdlls/repl/common/expget.c
new file mode 100644
index 000000000..c6a4bbaf9
--- /dev/null
+++ b/private/net/svcdlls/repl/common/expget.c
@@ -0,0 +1,110 @@
+/*++
+
+Copyright (c) 1991-1993 Microsoft Corporation
+
+Module Name:
+
+ ExpGet.c
+
+Abstract:
+
+ ExportDirGetApiRecord().
+
+Author:
+
+ John Rogers (JohnRo) 13-Nov-1992
+
+Environment:
+
+ User Mode - Win32
+
+Revision History:
+
+ 13-Nov-1992 JohnRo
+ RAID 1537: Repl APIs in wrong role kill svc. (Extracted from DLL
+ stubs.)
+ 04-Jan-1993 JohnRo
+ Made changes suggested by PC-LINT 5.0
+
+--*/
+
+
+// These must be included first:
+
+#include <windows.h>
+#include <lmcons.h> // NET_API_STATUS, etc.
+
+// These may be included in any order:
+
+#include <dirname.h> // ReplIsDirNameValid().
+#include <expdir.h> // My prototype, ExportDirIsApiRecordValid(), etc.
+#include <netdebug.h> // NetpAssert().
+
+
+// Callable even if the replicator service is not started.
+NET_API_STATUS
+ExportDirGetApiRecord (
+ IN LPTSTR UncServerName OPTIONAL,
+ IN LPTSTR DirName,
+ IN DWORD Level,
+ OUT LPBYTE * BufPtr
+ )
+{
+ NET_API_STATUS ApiStatus;
+
+ LPVOID ApiRecord = NULL;
+ DWORD Integrity, Extent, LockCount;
+ DWORD TimeOfFirstLock; // Seconds since 1970.
+ LPBYTE StringLocation; // Points just past top of data.
+
+ ApiStatus = NO_ERROR; // Innocent until proven guilty.
+
+ if (! ReplIsDirNameValid(DirName)) {
+ ApiStatus = ERROR_INVALID_PARAMETER;
+ } else if ( !ExportDirIsLevelValid( Level, FALSE ) ) {
+ ApiStatus = ERROR_INVALID_LEVEL;
+ } else if (BufPtr == NULL) {
+ ApiStatus = ERROR_INVALID_PARAMETER;
+ }
+
+ if (ApiStatus == NO_ERROR) {
+ // Read config data for a single export directory.
+ ApiStatus = ExportDirReadConfigData (
+ UncServerName,
+ DirName,
+ & Integrity,
+ & Extent,
+ & LockCount,
+ & TimeOfFirstLock ); // Seconds since 1970.
+ }
+ if (ApiStatus == NO_ERROR) {
+
+ ApiStatus = ExportDirAllocApiRecords (
+ Level,
+ 1, // only 1 record.
+ (LPBYTE *) & ApiRecord, // alloc and set ptr
+ (LPBYTE *) & StringLocation ); // Points just past top of data.
+ if (ApiStatus == NO_ERROR) {
+ NetpAssert( ApiRecord != NULL );
+ ApiStatus = ExportDirBuildApiRecord (
+ Level,
+ DirName,
+ Integrity,
+ Extent,
+ LockCount,
+ TimeOfFirstLock, // Seconds since 1970.
+ ApiRecord,
+ (LPBYTE *) (LPVOID) & StringLocation);
+ NetpAssert( ApiStatus == NO_ERROR ); // We checked all parms.
+ }
+
+ }
+
+ if (ApiStatus == NO_ERROR) {
+ NetpAssert( ApiRecord != NULL );
+ NetpAssert( ExportDirIsApiRecordValid( Level, ApiRecord, NULL ) );
+ }
+ *BufPtr = ApiRecord; // will be NULL on error.
+
+ return ApiStatus;
+}
diff --git a/private/net/svcdlls/repl/common/explock.c b/private/net/svcdlls/repl/common/explock.c
new file mode 100644
index 000000000..e1abace61
--- /dev/null
+++ b/private/net/svcdlls/repl/common/explock.c
@@ -0,0 +1,162 @@
+/*++
+
+Copyright (c) 1992-1993 Microsoft Corporation
+
+Module Name:
+
+ ExpLock.c
+
+Abstract:
+
+ Common registry lock/unlock routines for export dirs.
+
+Author:
+
+ John Rogers (JohnRo) 15-Mar-1992
+
+Environment:
+
+ User Mode - Win32
+
+Notes:
+
+ This file is extremely similar to ImpLock.c. If you fix any bugs here,
+ make sure they're reflected there, and vice versa.
+
+Revision History:
+
+ 15-Mar-1992 JohnRo
+ Created these routines.
+ 23-Jul-1992 JohnRo
+ RAID 2274: repl svc should impersonate caller.
+ 29-Sep-1992 JohnRo
+ Also fix remote repl admin.
+ 13-Apr-1993 JohnRo
+ RAID 3107: locking directory over the net gives network path not found.
+ Made changes suggested by PC-LINT 5.0
+ 30-Apr-1993 JohnRo
+ Use NetpKdPrint() where possible.
+
+--*/
+
+
+// These must be included first:
+
+#include <windef.h>
+#include <lmcons.h> // NET_API_STATUS, etc.
+
+// These may be included in any order:
+
+#include <dirname.h> // ReplIsDirNameValid().
+#include <expdir.h> // My prototypes, etc.
+#include <lmrepl.h> // REPL_UNLOCK_FORCE equates.
+#include <netdebug.h> // NetpKdPrint(), etc.
+#include <repldefs.h> // IF_DEBUG().
+#include <winerror.h> // NO_ERROR, ERROR_ equates.
+
+
+// Callable whether or not service is started.
+// If service is running, assume caller has lock (any kind) on RMGlobalListLock.
+NET_API_STATUS
+ExportDirLockInRegistry(
+ IN LPTSTR UncServerName OPTIONAL,
+ IN LPTSTR DirName
+ )
+{
+ NET_API_STATUS ApiStatus;
+ DWORD Integrity, Extent, LockCount;
+ DWORD TimeOfFirstLock; // Seconds since 1970.
+
+ IF_DEBUG(REPL) {
+ NetpKdPrint(( "ExportDirLockInRegistry( " FORMAT_LPTSTR
+ "): beginning...\n",
+ (UncServerName!=NULL) ? UncServerName : (LPTSTR) TEXT("local") ));
+ }
+
+ if ( !ReplIsDirNameValid(DirName)) {
+ return (ERROR_INVALID_PARAMETER);
+ }
+
+ // Read config data for a single export directory.
+ ApiStatus = ExportDirReadConfigData (
+ UncServerName,
+ DirName,
+ & Integrity,
+ & Extent,
+ & LockCount,
+ & TimeOfFirstLock ); // Seconds since 1970.
+ if (ApiStatus == NO_ERROR) {
+
+ ApiStatus = ReplIncrLockFields(
+ & LockCount,
+ & TimeOfFirstLock );
+ }
+ if (ApiStatus == NO_ERROR) {
+ ApiStatus = ExportDirWriteConfigData (
+ UncServerName,
+ DirName,
+ Integrity,
+ Extent,
+ LockCount,
+ TimeOfFirstLock ); // Seconds since 1970.
+ }
+
+ return (ApiStatus);
+
+} // ExportDirLockInRegistry
+
+
+// Callable whether or not service is started.
+// If service is running, assume caller has lock (any kind) on RMGlobalListLock.
+NET_API_STATUS
+ExportDirUnlockInRegistry(
+ IN LPTSTR UncServerName OPTIONAL,
+ IN LPTSTR DirName,
+ IN DWORD UnlockForce
+ )
+{
+ NET_API_STATUS ApiStatus;
+ DWORD Integrity, Extent, LockCount;
+ DWORD TimeOfFirstLock; // Seconds since 1970.
+
+ IF_DEBUG(REPL) {
+ NetpKdPrint(( "ExportDirUnlockInRegistry( " FORMAT_LPTSTR
+ "): beginning...\n",
+ (UncServerName!=NULL) ? UncServerName : (LPTSTR) TEXT("local") ));
+ }
+
+ if ( !ReplIsDirNameValid(DirName)) {
+ return (ERROR_INVALID_PARAMETER);
+ } else if ( !ReplIsForceLevelValid( UnlockForce ) ) {
+ return (ERROR_INVALID_PARAMETER);
+ }
+
+ // Read config data for a single export directory.
+ ApiStatus = ExportDirReadConfigData (
+ UncServerName,
+ DirName,
+ & Integrity,
+ & Extent,
+ & LockCount,
+ & TimeOfFirstLock ); // Seconds since 1970.
+
+ if (ApiStatus == NO_ERROR) {
+
+ ApiStatus = ReplDecrLockFields(
+ & LockCount,
+ & TimeOfFirstLock,
+ UnlockForce );
+ }
+ if (ApiStatus == NO_ERROR) {
+ ApiStatus = ExportDirWriteConfigData (
+ UncServerName,
+ DirName,
+ Integrity,
+ Extent,
+ LockCount,
+ TimeOfFirstLock ); // Seconds since 1970.
+ }
+
+ return (ApiStatus);
+
+} // ExportDirUnlockInRegistry
diff --git a/private/net/svcdlls/repl/common/expset.c b/private/net/svcdlls/repl/common/expset.c
new file mode 100644
index 000000000..9744f9c2c
--- /dev/null
+++ b/private/net/svcdlls/repl/common/expset.c
@@ -0,0 +1,195 @@
+/*++
+
+Copyright (c) 1991-1993 Microsoft Corporation
+
+Module Name:
+
+ ExpSet.c
+
+Abstract:
+
+ ExportDirSetInfo().
+
+Author:
+
+ John Rogers (JohnRo) 29-Sep-1992
+
+Environment:
+
+ User Mode - Win32
+
+Revision History:
+
+ 29-Sep-1992 JohnRo
+ RAID 7962: Repl APIs in wrong role kill svc. (Extracted code from DLL
+ stubs.)
+ 30-Dec-1992 JohnRo
+ Corrected debug bit usage.
+ 30-Apr-1993 JohnRo
+ Use NetpKdPrint() where possible.
+
+--*/
+
+
+// These must be included first:
+
+#include <windows.h>
+#include <lmcons.h> // NET_API_STATUS, etc.
+#include <repldefs.h> // IF_DEBUG().
+
+// These may be included in any order:
+
+#include <dirname.h> // ReplIsDirNameValid().
+#include <expdir.h> // My prototype, ExportDirIsApiRecordValid(), etc.
+#include <lmrepl.h> // LPREPL_EDIR_INFO_1, etc.
+#include <netdebug.h> // NetpKdPrint(), etc.
+#include <prefix.h> // PREFIX_ equates.
+
+
+NET_API_STATUS
+ExportDirConfigSetInfo (
+ IN LPTSTR UncServerName OPTIONAL,
+ IN LPTSTR DirName,
+ IN DWORD Level,
+ IN LPVOID Buffer,
+ OUT LPDWORD ParmError OPTIONAL
+ )
+{
+ NET_API_STATUS ApiStatus;
+
+ DWORD Integrity, Extent, LockCount;
+ DWORD TimeOfFirstLock; // Seconds since 1970.
+
+ ApiStatus = NO_ERROR; // Innocent until proven guilty.
+
+ NetpSetParmError( PARM_ERROR_UNKNOWN ); // Assume error until proven...
+ if (! ReplIsDirNameValid(DirName)) {
+ ApiStatus = ERROR_INVALID_PARAMETER;
+
+ IF_DEBUG( EXPAPI ) {
+ NetpKdPrint(( PREFIX_REPL
+ "NetReplExportDirSetInfo: Invalid dir name parm.\n" ));
+ }
+ }
+ if (Buffer == NULL) {
+
+ IF_DEBUG( EXPAPI ) {
+ NetpKdPrint(( PREFIX_REPL
+ "NetReplExportDirSetInfo: null buffer pointer.\n" ));
+ }
+
+ return (ERROR_INVALID_PARAMETER);
+ }
+
+ // BUGBUG: ParmError is not set in many paths through here!
+
+ if (ApiStatus == NO_ERROR) {
+
+ // Read config data for a single export directory.
+ // We have to do this 'cos the set info struct (level 1) is
+ // a subset of the info we need to do a write.
+ ApiStatus = ExportDirReadConfigData (
+ UncServerName,
+ DirName,
+ & Integrity,
+ & Extent,
+ & LockCount,
+ & TimeOfFirstLock ); // Seconds since 1970.
+ }
+
+ if (ApiStatus == NO_ERROR) {
+
+ if (Level == 1) {
+ LPREPL_EDIR_INFO_1 ApiRecord = Buffer;
+
+ if ( !ExportDirIsApiRecordValid(
+ Level, ApiRecord, ParmError ) ) {
+
+ IF_DEBUG( EXPAPI ) {
+ NetpKdPrint(( PREFIX_REPL
+ "NetReplExportDirSetInfo: Invalid level 1 record, "
+ "ParmError is " FORMAT_DWORD "\n",
+ (ParmError!=NULL)
+ ? (*ParmError)
+ : PARM_ERROR_UNKNOWN ));
+ }
+
+ ApiStatus = ERROR_INVALID_PARAMETER;
+
+ } else {
+
+ // BUGBUG: Match DirName and ApiRecord->rped1_dirname.
+
+ // Write config data for this export directory.
+ ApiStatus = ExportDirWriteConfigData (
+ UncServerName,
+ DirName, // new dir name (same)
+ ApiRecord->rped1_integrity, // new integrity
+ ApiRecord->rped1_extent, // new extent
+ LockCount, // old lock count
+ TimeOfFirstLock ); // old lock time
+ }
+
+ } else if ( (Level==1000) || (Level==1001) ) {
+
+ if (ApiStatus == NO_ERROR) {
+ if (Level==1000) {
+ LPREPL_EDIR_INFO_1000 ApiRecord = Buffer;
+ Integrity = ApiRecord->rped1000_integrity;
+
+ if ( !ReplIsIntegrityValid( Integrity ) ) {
+ NetpSetParmError( 1 ); // error in first field.
+ ApiStatus = ERROR_INVALID_PARAMETER;
+
+ IF_DEBUG( EXPAPI ) {
+ NetpKdPrint(( PREFIX_REPL
+ "NetReplExportDirSetInfo: "
+ "Bad integrity value.\n" ));
+ }
+
+ } else {
+ // Write the revised value below.
+
+ NetpSetParmError( PARM_ERROR_NONE );
+ }
+ } else {
+ LPREPL_EDIR_INFO_1001 ApiRecord = Buffer;
+ NetpAssert( Level == 1001 );
+ Extent = ApiRecord->rped1001_extent;
+
+ if ( !ReplIsExtentValid( Extent ) ) {
+ NetpSetParmError( 1 ); // error in first field.
+ ApiStatus = ERROR_INVALID_PARAMETER;
+
+ IF_DEBUG( EXPAPI ) {
+ NetpKdPrint(( PREFIX_REPL
+ "NetReplExportDirSetInfo: "
+ "Bad extent value.\n" ));
+ }
+
+ } else {
+ // Write the revised value below.
+ NetpSetParmError( PARM_ERROR_NONE );
+ ApiStatus = NO_ERROR;
+ }
+ }
+
+ if (ApiStatus == NO_ERROR) {
+ // Write config data for this export directory.
+ ApiStatus = ExportDirWriteConfigData (
+ UncServerName,
+ DirName,
+ Integrity,
+ Extent,
+ LockCount,
+ TimeOfFirstLock );
+ NetpSetParmError( PARM_ERROR_NONE );
+ }
+ }
+ } else {
+ ApiStatus = ERROR_INVALID_LEVEL;
+ }
+ }
+
+ return ApiStatus;
+}
diff --git a/private/net/svcdlls/repl/common/expvalid.c b/private/net/svcdlls/repl/common/expvalid.c
new file mode 100644
index 000000000..0fe5c5bf4
--- /dev/null
+++ b/private/net/svcdlls/repl/common/expvalid.c
@@ -0,0 +1,177 @@
+/*++
+
+Copyright (c) 1992-1993 Microsoft Corporation
+
+Module Name:
+
+ ExpValid.c
+
+Abstract:
+
+ This file contains ExportDirIsApiRecordValid().
+
+Author:
+
+ John Rogers (JohnRo) 22-Jan-1992
+
+Environment:
+
+ Runs under Windows NT.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Notes:
+
+ This code assumes that the info levels are subsets of each other.
+
+Revision History:
+
+ 22-Jan-1992 JohnRo
+ Created ExportDirIsApiRecordValid() from code in NetrReplExportDirAdd().
+ 28-Jan-1992 JohnRo
+ Oops, this code accidently said level 1 was invalid.
+ 30-Jan-1992 JohnRo
+ Made changes suggested by PC-LINT.
+ 18-Feb-1992 JohnRo
+ Handle level 2 as well.
+ 27-Feb-1992 JohnRo
+ Really allow level 2.
+ Check lock fields for validity.
+ 15-Mar-1992 JohnRo
+ Improve setinfo info level support.
+ 23-Mar-1993 JohnRo
+ Added debug output.
+
+--*/
+
+
+// These must be included first:
+
+#include <windef.h> // IN, VOID, LPTSTR, etc.
+#include <lmcons.h> // NET_API_STATUS, PARM equates, etc.
+#include <repldefs.h> // ReplIsIntegrityValid(), etc.
+
+// These can be in any order:
+
+#include <dirname.h> // ReplIsDirNameValid().
+#include <expdir.h> // My prototype.
+#include <lmrepl.h> // LPREPL_EDIR_INFO_1, REPL_EXTENT_ stuff, etc.
+#include <netdebug.h> // NetpAssert(), NetpKdPrint().
+#include <netlib.h> // NetpSetParmError().
+#include <prefix.h> // PREFIX_ equates.
+
+#define INDICATE_ERROR( text ) \
+ { \
+ NetpKdPrint(( PREFIX_REPL \
+ "ExportDirIsApiRecordValid: " text ".\n" )); \
+ /* NetpDbgHexDump( Buf, REASONABLE_DUMP_SIZE ); */ \
+ }
+
+
+BOOL
+ExportDirIsApiRecordValid (
+ IN DWORD Level,
+ IN LPVOID Buf,
+ OUT LPDWORD ParmError OPTIONAL
+ )
+
+{
+
+ //
+ // First, make sure level is valid and there's actually a struct there.
+ //
+ NetpSetParmError( PARM_ERROR_UNKNOWN ); // Assume error until proven...
+ if ( !ExportDirIsLevelValid( Level, TRUE) ) {
+
+ NetpKdPrint(( PREFIX_REPL
+ "ExportDirIsApiRecordValid: bad info level.\n" ));
+
+ return (FALSE); // No, it's not valid.
+ }
+ if (Buf == NULL) {
+
+ NetpKdPrint(( PREFIX_REPL
+ "ExportDirIsApiRecordValid: null buf ptr.\n" ));
+
+ return (FALSE); // No, it's not valid.
+ }
+
+ if (Level < PARMNUM_BASE_INFOLEVEL) {
+ LPREPL_EDIR_INFO_2 ApiRecord; // Superset info level.
+ ApiRecord = Buf;
+
+ //
+ // Check item(s) common to all levels.
+ //
+ if ( !ReplIsDirNameValid(ApiRecord->rped2_dirname) ) {
+ NetpSetParmError( 1 ); // Error in first field in ApiRecord.
+
+ INDICATE_ERROR(
+ "ExportDirIsApiRecordValid: bad dir name.\n" );
+
+ return (FALSE); // No, it's not valid.
+ }
+
+ //
+ // Check items unique to level 1 and 2.
+ //
+ if (Level > 0) {
+ if ( !ReplIsIntegrityValid(ApiRecord->rped2_integrity) ) {
+ NetpSetParmError( 2 ); // Error in second field in ApiRecord.
+
+ INDICATE_ERROR( "bad integrity (struct)" );
+
+ return (FALSE); // No, it's not valid.
+ }
+ if ( !ReplIsExtentValid(ApiRecord->rped2_extent) ) {
+ NetpSetParmError( 3 ); // Error in third field in ApiRecord.
+
+ INDICATE_ERROR( "bad extent (struct)" );
+
+ return (FALSE); // No, it's not valid.
+ }
+
+ if (Level >= 2) {
+ if ( !ReplAreLockFieldsValid( ApiRecord->rped2_lockcount,
+ ApiRecord->rped2_locktime ) ) {
+ NetpSetParmError( 4 ); // Err in fourth and/or fifth field.
+
+ INDICATE_ERROR( "bad lock fields" );
+
+ return (FALSE); // No, it's not valid.
+ }
+ }
+ }
+
+ } else if (Level == REPL_EXPORT_INTEGRITY_INFOLEVEL) {
+
+ DWORD NewIntegrity = * (LPDWORD) (LPVOID) Buf;
+
+ if ( !ReplIsIntegrityValid( NewIntegrity ) ) {
+ NetpSetParmError( 1 ); // Error in first field in ApiRecord.
+
+ INDICATE_ERROR( "bad integrity (field)" );
+
+ return (FALSE); // No, it's not valid.
+ }
+
+ } else {
+
+ DWORD NewExtent = * (LPDWORD) (LPVOID) Buf;
+ NetpAssert( Level == REPL_EXPORT_EXTENT_INFOLEVEL );
+ if ( !ReplIsExtentValid( NewExtent ) ) {
+ NetpSetParmError( 1 ); // Error in first field in ApiRecord.
+
+ INDICATE_ERROR( "bad extent (field)" );
+
+ return (FALSE); // No, it's not valid.
+ }
+
+ }
+
+ //
+ // Everything went OK. Tell caller.
+ //
+ NetpSetParmError( PARM_ERROR_NONE );
+ return (TRUE); // Yes, it's valid.
+
+}
diff --git a/private/net/svcdlls/repl/common/fixlocks.c b/private/net/svcdlls/repl/common/fixlocks.c
new file mode 100644
index 000000000..6cb44c389
--- /dev/null
+++ b/private/net/svcdlls/repl/common/fixlocks.c
@@ -0,0 +1,194 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ FixLocks.c
+
+Abstract:
+
+ This routine (ExportDirFixUserLockFiles) makes sure that the local
+ disk's UserLock.* file(s) are in agreement with the lock count from
+ the registry. The file(s) are deleted and/or created as necessary.
+
+ This is callable even if the replicator service is not started.
+
+
+Author:
+
+ JR (John Rogers, JohnRo@Microsoft) 14-Jan-1993
+
+Environment:
+
+ User Mode - Win32
+ Portable to any flat, 32-bit environment. (Uses Win32 typedefs.)
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+ 14-Jan-1993 JohnRo
+ Created for RAID 7053: locked trees added to pulse msg. (Actually
+ fix all kinds of remote lock handling.)
+ 26-Apr-1993 JohnRo
+ RAID 7313: repl needs change permission to work on NTFS, or we need
+ to delete files differently.
+
+--*/
+
+
+// These must be included first:
+
+#include <windows.h> // IN, GetLastError(), LPCTSTR, OPTIONAL, etc.
+#include <lmcons.h> // NET_API_STATUS.
+
+// These may be included in any order:
+
+
+#include <dirname.h> // ReplIsDirNameValid().
+#include <expdir.h> // My prototype.
+#include <lmerrlog.h> // NELOG_ equates.
+#include <netdebug.h> // NetpAssert(), NetpKdPrint(), FORMAT_ equates, etc.
+#include <prefix.h> // PREFIX_ equates.
+#include <repldefs.h> // SLASH, ReplCheckAbsPathSyntax(), etc.
+#include <tstr.h> // STRCAT(), STRCPY(), STRLEN().
+#include <winerror.h> // ERROR_, NO_ERROR equates.
+
+
+NET_API_STATUS
+ExportDirFixUserLockFiles(
+ IN LPCTSTR ExportPath, // Must include drive letter.
+ IN LPCTSTR DirName,
+ IN DWORD LockCount
+ )
+{
+ NET_API_STATUS ApiStatus;
+ HANDLE FileHandle = NULL;
+ TCHAR LockFileName[MAX_PATH+1];
+ BOOL NtUserLockExists;
+
+ //
+ // Check for caller errors.
+ //
+ if ( !ReplIsDirNameValid( (LPTSTR) DirName ) ) {
+ ApiStatus = ERROR_INVALID_PARAMETER;
+ goto Cleanup;
+ }
+ ApiStatus = ReplCheckAbsPathSyntax( (LPTSTR) ExportPath );
+ if (ApiStatus != NO_ERROR) {
+ goto Cleanup;
+ }
+
+ //
+ // Build NT version of lock file name.
+ //
+ (VOID) STRCPY( LockFileName, ExportPath );
+ (VOID) STRCAT( LockFileName, SLASH );
+ (VOID) STRCAT( LockFileName, DirName );
+ (VOID) STRCAT( LockFileName, SLASH );
+ (VOID) STRCAT( LockFileName, USERLOCK_NT );
+
+ NetpAssert( STRLEN( LockFileName ) <= MAX_PATH );
+
+ //
+ // See if the NT version of the user lock file exists.
+ //
+ NtUserLockExists = ReplFileOrDirExists( LockFileName );
+
+ if ( NtUserLockExists && (LockCount==0) ) {
+
+ //
+ // Delete extraneous NT lock file.
+ //
+ NetpKdPrint(( PREFIX_REPL
+ "ExportDirFixUserLockFiles: "
+ "deleting extraneous user lock file '" FORMAT_LPTSTR "'...\n",
+ LockFileName ));
+ ApiStatus = ReplDeleteFile( LockFileName );
+ if (ApiStatus != NO_ERROR) {
+ goto Cleanup;
+ }
+
+ } else if ( (!NtUserLockExists) && (LockCount>0) ) {
+
+ //
+ // Create a lock file if we can. (The directory might not exist yet,
+ // so we might not be able to do this. No problem.)
+ //
+ FileHandle = CreateFile(
+ (LPCTSTR) LockFileName, // name of file to create.
+ GENERIC_WRITE, // desired access.
+ FILE_SHARE_WRITE, // share mode: we don't care.
+ NULL, // default security attr.
+ CREATE_ALWAYS, // create disposition.
+ FILE_FLAG_WRITE_THROUGH, // flags&attr: don't cache writes.
+ NULL ); // no template file.
+ if (FileHandle == INVALID_HANDLE_VALUE) {
+ ApiStatus = (NET_API_STATUS) GetLastError();
+ NetpAssert( ApiStatus != NO_ERROR );
+
+ if (ApiStatus == ERROR_PATH_NOT_FOUND) {
+ // First-level dir doesn't exist yet. That's OK, as pulser
+ // will correct for this when it gets created.
+ ApiStatus = NO_ERROR;
+ } else if (ApiStatus == ERROR_WRITE_PROTECT) {
+ // Perhaps just CD-ROM? OK, as registry lock count is correct,
+ // and ReplCheckExportLocks() checks registry before file
+ // system.
+
+ ReplErrorLog(
+ NULL, // no server name (log locally)
+ NELOG_ReplAccessDenied, // log code
+ ApiStatus,
+ NULL, // optional str1
+ NULL); // optional str2
+
+ // Don't prevent repl from exporting CD-ROM.
+ ApiStatus = NO_ERROR;
+ } else if (ApiStatus == ERROR_ACCESS_DENIED) {
+
+ // Log in case service is running in wrong account, or
+ // an ACL is wrong somewhere.
+ ReplErrorLog(
+ NULL, // no server name (log locally)
+ NELOG_ReplAccessDenied, // log code
+ ApiStatus,
+ NULL, // optional str1
+ NULL); // optional str2
+
+ // Don't prevent repl from exporting CD-ROM.
+ ApiStatus = NO_ERROR;
+ }
+ goto Cleanup; // go log error.
+ }
+ // Fall through to close the file in cleanup code.
+ }
+
+ ApiStatus = NO_ERROR;
+
+Cleanup:
+
+ if (FileHandle != NULL) {
+ (VOID) CloseHandle( FileHandle );
+ }
+
+ if (ApiStatus != NO_ERROR) {
+
+ NetpKdPrint(( PREFIX_REPL
+ "ExportDirFixUserLockFiles: ERROR " FORMAT_API_STATUS ".\n",
+ ApiStatus ));
+
+ //
+ // Log the error.
+ // BUGBUG: extract master server name and log there too.
+ //
+ ReplErrorLog(
+ NULL, // no server name (log locally)
+ NELOG_ReplSysErr, // log code
+ ApiStatus,
+ NULL, // optional str1
+ NULL); // optional str2
+ }
+ return (ApiStatus);
+
+}
diff --git a/private/net/svcdlls/repl/common/fsresolu.c b/private/net/svcdlls/repl/common/fsresolu.c
new file mode 100644
index 000000000..092bf946f
--- /dev/null
+++ b/private/net/svcdlls/repl/common/fsresolu.c
@@ -0,0 +1,220 @@
+/*++
+
+Copyright (c) 1992-1993 Microsoft Corporation
+
+Module Name:
+
+ FsResolu.c
+
+Abstract:
+
+ Module only contains ReplGetFsTimeResolutionSecs().
+
+Author:
+
+ JR (John Rogers, JohnRo@Microsoft)
+
+Environment:
+
+ User mode only.
+ Contains NT-specific code.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+ 17-Dec-1992 JohnRo
+ Created for RAID 1513: Repl does not maintain ACLs. (Also fix
+ HPFS->FAT timestamp.)
+ 26-Feb-1993 JohnRo
+ RAID 13126: Fix repl memory leak.
+
+--*/
+
+// These must be included first:
+
+#include <nt.h> // NtOpenFile(), ULONG, etc.
+#include <ntrtl.h> // PLARGE_INTEGER, TIME_FIELDS, etc.
+#include <nturtl.h> // Needed for ntrtl.h and windows.h to co-exist.
+
+#include <windows.h> // GetLastError(), etc.
+#include <lmcons.h>
+
+// These may be included in any order:
+
+#include <netdebug.h> // DBGSTATIC, NetpKdPrint(), FORMAT_ equates, etc.
+#include <netlib.h> // NetpMemoryAllocate(), etc.
+#include <prefix.h> // PREFIX_ equates.
+#include <repldefs.h> // My prototype, UNKNOWN_FS_RESOLUTION, etc.
+#include <tstring.h> // NetpNCopyWStrToTStr().
+#include <winerror.h> // NO_ERROR.
+
+
+DWORD
+ReplGetFsTimeResolutionSecs(
+ IN LPCTSTR AbsPath
+ )
+{
+ NET_API_STATUS ApiStatus;
+ DWORD ResolutionSecs = UNKNOWN_FS_RESOLUTION;
+ HANDLE FileHandle = NULL;
+ IO_STATUS_BLOCK IoStatusBlock;
+ NTSTATUS NtStatus;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+
+ const ULONG OpenOptions =
+ FILE_SYNCHRONOUS_IO_NONALERT
+#if 0
+ | ( IsDirectory ? FILE_DIRECTORY_FILE : 0 )
+#endif
+ ;
+
+ TCHAR FsName[MAXIMUM_FILENAME_LENGTH+1];
+ DWORD FsNameLen; // char count in FsName, w/o nul char.
+ BOOL PathAllocated = FALSE;
+ UNICODE_STRING UnicodePath;
+ PFILE_FS_ATTRIBUTE_INFORMATION VolumeInfo = NULL;
+ ULONG VolumeInfoSize;
+
+ NetpAssert( AbsPath != NULL );
+ ApiStatus = ReplCheckAbsPathSyntax( (LPTSTR) AbsPath );
+ NetpAssert( ApiStatus == NO_ERROR );
+
+ RtlInitUnicodeString(
+ & UnicodePath, // output: struct
+ AbsPath ); // input: null terminated
+
+ if( !RtlDosPathNameToNtPathName_U(
+ AbsPath,
+ &UnicodePath,
+ NULL,
+ NULL) ) {
+
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplGetFsTimeResolutionSecs: RtlDosPathNameToNtPathname_U"
+ " of source '" FORMAT_LPTSTR "' failed.\n", AbsPath ));
+
+ // BUGBUG: log error
+ goto Cleanup;
+ }
+ PathAllocated = TRUE;
+
+ InitializeObjectAttributes(
+ &ObjectAttributes,
+ (LPVOID) &UnicodePath,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL );
+ NtStatus = NtOpenFile(
+ & FileHandle,
+ SYNCHRONIZE | // desired ...
+ FILE_READ_ATTRIBUTES, // ... access
+ &ObjectAttributes,
+ &IoStatusBlock,
+ FILE_SHARE_READ, // share access
+ OpenOptions );
+
+ if ( !NT_SUCCESS( NtStatus ) ) {
+
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplGetFsTimeResolutionSecs: NtOpenFile of source '"
+ FORMAT_LPTSTR "' gave NT status " FORMAT_NTSTATUS ".\n",
+ AbsPath, NtStatus ));
+
+ // BUGBUG: log error
+ goto Cleanup;
+ }
+
+ //
+ // Alloc space for volume fs attr info (including various strings
+ // after the structure).
+ //
+ VolumeInfoSize =
+ sizeof(FILE_FS_ATTRIBUTE_INFORMATION)
+ + ( (MAXIMUM_FILENAME_LENGTH+1) * sizeof(WCHAR) );
+ VolumeInfo = NetpMemoryAllocate( VolumeInfoSize );
+ if (VolumeInfo == NULL) {
+ // BUGBUG: log the error!
+ goto Cleanup;
+ }
+
+ NtStatus = NtQueryVolumeInformationFile(
+ FileHandle,
+ &IoStatusBlock,
+ (PVOID) VolumeInfo,
+ VolumeInfoSize,
+ FileFsAttributeInformation ); // FS info class
+
+ if ( !NT_SUCCESS( NtStatus ) ) {
+
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplGetFsTimeResolutionSecs: NtQueryVolumeInformationFile "
+ "FAILED, NT status is " FORMAT_NTSTATUS ".\n",
+ NtStatus ));
+
+ // BUGBUG: log error
+ goto Cleanup;
+ }
+
+ IF_DEBUG( REPL ) {
+ NetpKdPrint(( PREFIX_REPL
+ "ReplGetFsTimeResolutionSecs: got volume info, "
+ "name len is " FORMAT_DWORD ", name[0] is " FORMAT_WCHAR ".\n",
+ (DWORD) (VolumeInfo->FileSystemNameLength),
+ VolumeInfo->FileSystemName[0] ));
+ }
+ NetpAssert( ((VolumeInfo->FileSystemNameLength) % sizeof(WCHAR)) == 0 );
+
+ FsNameLen = (VolumeInfo->FileSystemNameLength) / sizeof(WCHAR);
+ // len w/o nul
+
+ ApiStatus = NetpNCopyWStrToTStr(
+ FsName, // dest
+ VolumeInfo->FileSystemName, // src
+ FsNameLen); // chars
+ NetpAssert( ApiStatus == NO_ERROR );
+ FsName[ FsNameLen ] = L'\0';
+
+ IF_DEBUG( REPL ) {
+ NetpKdPrint(( PREFIX_REPL
+ "ReplGetFsTimeResolutionSecs: got fs name '" FORMAT_LPTSTR
+ "'.\n", FsName ));
+ }
+
+ //
+ // Compute ResolutionSecs using known file system names.
+ //
+ if (STRICMP( FsName, (LPTSTR) TEXT("FAT") ) == 0) {
+ ResolutionSecs = 2;
+ goto Cleanup;
+ }
+ if (STRICMP( FsName, (LPTSTR) TEXT("HPFS") ) == 0) {
+ ResolutionSecs = 1;
+ goto Cleanup;
+ }
+ if (STRICMP( FsName, (LPTSTR) TEXT("NTFS") ) == 0) {
+ ResolutionSecs = 1;
+ goto Cleanup;
+ }
+
+ NetpKdPrint(( PREFIX_REPL
+ "ReplGetFsTimeResolutionSecs: UNKNOWN fs name of '"
+ "'.\n", FsName ));
+ // BUGBUG: log this error!
+ ResolutionSecs = UNKNOWN_FS_RESOLUTION;
+
+Cleanup:
+
+ if (FileHandle != NULL) {
+ (VOID) NtClose( FileHandle );
+ }
+
+ if (VolumeInfo != NULL) {
+ NetpMemoryFree( VolumeInfo );
+ }
+
+ if (PathAllocated) {
+ (VOID) RtlFreeHeap( RtlProcessHeap(), 0, UnicodePath.Buffer );
+ }
+
+ return (ResolutionSecs);
+}
diff --git a/private/net/svcdlls/repl/common/ignorenm.c b/private/net/svcdlls/repl/common/ignorenm.c
new file mode 100644
index 000000000..335655e7d
--- /dev/null
+++ b/private/net/svcdlls/repl/common/ignorenm.c
@@ -0,0 +1,100 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ IgnoreNm.c
+
+Abstract:
+
+ This file contains ReplIgnoreDirOrFileName().
+
+Author:
+
+ John Rogers (JohnRo) 25-Feb-1992
+
+Environment:
+
+ User Mode - Win32
+ Portable to any flat, 32-bit environment. (Uses Win32 typedefs.)
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+ 25-Feb-1992 JohnRo
+ Created this routine.
+ 19-Aug-1992 JohnRo
+ RAID 3603: import tree (TMPREE.RP$) generated at startup.
+ 08-Dec-1992 JohnRo
+ Made changes suggested by PC-LINT 5.0
+
+--*/
+
+
+// These must be included first:
+
+#include <windef.h> // MAX_PATH, etc.
+#include <lmcons.h> // LAN Manager common definitions
+
+// These can be in any order:
+
+#include <client.h> // RP, TMP_TREE, etc.
+#include <dirname.h> // ReplIsDirNameValid().
+#include <netdebug.h> // NetpAssert().
+#include <repldefs.h> // My prototype, DOT, DOT_DOT.
+#include <tstr.h> // TCHAR_ equates, STRLEN(), etc.
+#include <winerror.h> // ERROR_, NO_ERROR equates.
+
+
+
+
+BOOL
+ReplIgnoreDirOrFileName (
+ IN LPTSTR Name
+ )
+{
+ INT len;
+
+ NetpAssert( Name != NULL );
+ NetpAssert( (*Name) != TCHAR_EOS );
+ NetpAssert( ReplIsDirNameValid( Name ) ); // Not abs path, UNC, "c:x".
+
+ //
+ // Definitely ignore "." and "..".
+ //
+ if ( (STRCMP(Name, DOT)==0) || (STRCMP(Name, DOT_DOT)==0) ) {
+ return (TRUE); // yes, ignore this one.
+ }
+
+ //
+ // Ignore "*.RP$". This also gets TMPTREE.RP$, TMPTREEX.RP$.
+ //
+ len = ((INT) STRLEN( Name )) - ((INT)STRLEN( RP ));
+
+ if ( (len > 0) && (STRICMP(&Name[len], RP) == 0) ) {
+ return (TRUE); // yes, ignore this one.
+ }
+ NetpAssert( STRICMP(Name, TMP_TREE) != 0 );
+ NetpAssert( STRICMP(Name, TMP_TREEX) != 0 );
+
+ //
+ // Ignore "REPL.INI".
+ //
+ if (STRICMP(Name, REPL_INI) == 0) {
+ return (TRUE); // yes, ignore this one.
+ }
+
+ //
+ // Ignore "USERLOCK.*".
+ //
+ if (STRNICMP(Name, ULOCK_PREFIX, STRLEN(ULOCK_PREFIX)) == 0) {
+ return (TRUE); // yes, ignore this one.
+ }
+
+ //
+ // None of the above.
+ //
+ return (FALSE); // No, don't ignore this one.
+
+} // ReplIgnoreDirOrFileName
diff --git a/private/net/svcdlls/repl/common/impalloc.c b/private/net/svcdlls/repl/common/impalloc.c
new file mode 100644
index 000000000..e60e6cbce
--- /dev/null
+++ b/private/net/svcdlls/repl/common/impalloc.c
@@ -0,0 +1,118 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ ImpAlloc.c
+
+Abstract:
+
+ This file contains ImportDirAllocApiRecords().
+
+Author:
+
+ John Rogers (JohnRo) 19-Feb-1992
+
+Environment:
+
+ Runs under Windows NT.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+ 19-Feb-1992 JohnRo
+ Created this routine by cloning ExportDirAllocApiRecords().
+
+--*/
+
+
+// These must be included first:
+
+#include <windef.h> // IN, VOID, LPTSTR, etc.
+#include <lmcons.h> // NET_API_STATUS, PARM equates, etc.
+#include <rap.h> // Needed by <strucinf.h>.
+
+// These can be in any order:
+
+#include <impdir.h> // My prototype.
+#include <lmapibuf.h> // NetApiBufferAllocate().
+#include <netdebug.h> // NetpAssert().
+#include <netlib.h> // NetpPointerPlusSomeBytes().
+#include <strucinf.h> // Netp{various}StructureInfo().
+#include <winerror.h> // ERROR_* defines; NO_ERROR.
+
+
+NET_API_STATUS
+ImportDirAllocApiRecords (
+ IN DWORD Level,
+ IN DWORD EntryCount,
+ OUT LPBYTE * BufPtr,
+ IN OUT LPBYTE *StringLocation // Points just past top of data.
+ )
+
+{
+ LPBYTE FirstRecord = NULL;
+ NET_API_STATUS ApiStatus;
+ DWORD EntrySize;
+
+ //
+ // Check for caller errors.
+ //
+ if (BufPtr == NULL) {
+ return (ERROR_INVALID_PARAMETER);
+ }
+
+ * BufPtr = NULL; // Don't confuse caller about possible alloc'ed data.
+
+ if (EntryCount == 0) {
+ return (ERROR_INVALID_PARAMETER);
+ }
+
+ //
+ // Compute size of an entry (and check caller's Level too).
+ //
+ ApiStatus = NetpReplImportDirStructureInfo (
+ Level,
+ PARMNUM_ALL,
+ TRUE, // want native sizes
+ NULL, // don't need DataDesc16
+ NULL, // don't need DataDesc32
+ NULL, // don't need DataDescSmb
+ & EntrySize, // need max size of structure
+ NULL, // don't need FixedSize
+ NULL); // don't need StringSize
+ if (ApiStatus != NO_ERROR) {
+ return (ApiStatus);
+ }
+ NetpAssert( EntrySize > 0 );
+
+ //
+ // Allocate the output area.
+ //
+ ApiStatus = NetApiBufferAllocate(
+ EntrySize * EntryCount,
+ (LPVOID *) & FirstRecord);
+ if (ApiStatus != NO_ERROR) {
+
+ // ApiStatus is already set to return error to caller.
+
+ } else {
+ NetpAssert( FirstRecord != NULL );
+
+ //
+ // Tell caller where top of string area is.
+ //
+ * StringLocation = NetpPointerPlusSomeBytes(
+ FirstRecord,
+ EntrySize * EntryCount );
+
+ }
+
+ //
+ // Tell caller how everything went.
+ //
+ * BufPtr = FirstRecord;
+ return (ApiStatus);
+
+}
diff --git a/private/net/svcdlls/repl/common/impbuild.c b/private/net/svcdlls/repl/common/impbuild.c
new file mode 100644
index 000000000..8043fd8fc
--- /dev/null
+++ b/private/net/svcdlls/repl/common/impbuild.c
@@ -0,0 +1,203 @@
+/*++
+
+Copyright (c) 1992-1993 Microsoft Corporation
+
+Module Name:
+
+ ImpBuild.c
+
+Abstract:
+
+ This file contains ImportDirBuildApiRecord. This is used by
+ NetrReplImportDirGetInfo and NetrReplImportDirEnum.
+
+Author:
+
+ John Rogers (JohnRo) 08-Jan-1992
+
+Environment:
+
+ Portable to any flat, 32-bit environment. (Uses Win32 typedefs.)
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Notes:
+
+ This code assumes that the import dir info levels are subsets of each other.
+
+Revision History:
+
+ 08-Jan-1992 JohnRo
+ Created.
+ 24-Jan-1992 JohnRo
+ Changed to use LPTSTR etc.
+ 27-Jan-1992 JohnRo
+ Changed interface to allow use when service is not running.
+ 21-Feb-1992 JohnRo
+ Fixed bug checking state parameter.
+ UncMaster parm is optional.
+ Added check of UncMaster validity.
+ Undid redundant checks of Buffer (3 of them!)
+ 21-Feb-1992 JohnRo
+ Changed ImportDirBuildApiRecord() so master name is not a UNC name.
+ 22-Feb-1992 JohnRo
+ Made changes suggested by PC-LINT.
+ 26-Feb-1992 JohnRo
+ Check lock fields for validity.
+ API records now contain timestamps instead of elapsed times.
+ Added assertion of valid record at end.
+ 25-Mar-1992 JohnRo
+ Avoid obsolete state values.
+ 27-Mar-1992 JohnRo
+ Allow MasterName to point to a null char.
+ 28-Jul-1992 JohnRo
+ RAID 2274: repl svc should impersonate caller.
+ Added debug output of structure after we build it.
+ 30-Apr-1993 JohnRo
+ Use NetpKdPrint() where possible.
+
+--*/
+
+
+// These must be included first:
+
+#include <windef.h> // IN, VOID, LPTSTR, etc.
+#include <lmcons.h> // NET_API_STATUS.
+#include <repldefs.h> // ReplIsIntegrityValid(), IF_DEBUG(), SLASH_SLASH, etc.
+
+// These can be in any order:
+
+#include <align.h> // POINTER_IS_ALIGNED(), ALIGN_TCHAR.
+#include <dirname.h> // ReplIsDirNameValid().
+#include <impdir.h> // My prototype, ImportDirIsLevelValid().
+#include <lmrepl.h> // LPREPL_IDIR_INFO_1, REPL_EXTENT_ stuff, etc.
+#include <names.h> // NetpIsComputerNameValid().
+#include <netdebug.h> // NetpAssert(), NetpKdPrint(), etc.
+#include <prefix.h> // PREFIX_ equates.
+#include <tstr.h> // STRLEN(), TCHAR_EOS, etc.
+#include <winerror.h> // ERROR_ equates, NO_ERROR.
+
+
+NET_API_STATUS
+ImportDirBuildApiRecord (
+ IN DWORD Level,
+ IN LPTSTR DirName,
+ IN DWORD State,
+ IN LPTSTR MasterName OPTIONAL, // computer name (not UNC).
+ IN DWORD TimeOfLastUpdate, // Seconds since 1970.
+ IN DWORD LockCount,
+ IN DWORD TimeOfFirstLock, // Seconds since 1970.
+ OUT LPVOID Buffer,
+ IN OUT LPBYTE *StringLocation // Points just past top of data.
+ )
+
+{
+ LPREPL_IDIR_INFO_1 ApiRecord = Buffer; // superset info level
+ LPTSTR StringDest;
+ DWORD StringLength;
+
+ NetpAssert( StringLocation != NULL);
+ NetpAssert( *StringLocation != NULL);
+
+ IF_DEBUG( IMPAPI ) {
+ NetpKdPrint(( PREFIX_REPL
+ "ImportDirBuildApiRecord: building record at " FORMAT_LPVOID
+ ", *str loc is " FORMAT_LPVOID ".\n",
+ (LPVOID) Buffer, (LPVOID) *StringLocation ));
+ }
+
+ if ( (MasterName != NULL) && ((*MasterName) == TCHAR_EOS) ) {
+ MasterName = NULL;
+ }
+
+ //
+ // Check for caller errors.
+ //
+ if ( ! ReplIsDirNameValid( DirName ) ) {
+ return (ERROR_INVALID_DATA);
+ } else if (Buffer == NULL) {
+ return (ERROR_INVALID_PARAMETER);
+ } else if ( !ReplIsStateValid( State ) ) {
+ return (ERROR_INVALID_DATA);
+ } else if ( !ImportDirIsLevelValid( Level ) ) {
+ return (ERROR_INVALID_LEVEL);
+ } else if ((MasterName!=NULL) && !NetpIsComputerNameValid(MasterName)) {
+ return (ERROR_INVALID_PARAMETER);
+ } else if ( !ReplAreLockFieldsValid( LockCount, TimeOfFirstLock ) ) {
+ return (ERROR_INVALID_PARAMETER);
+ }
+
+ //
+ // First do subset common to both info levels.
+ //
+ StringLength = (DWORD) STRLEN( DirName );
+
+ NetpAssert( POINTER_IS_ALIGNED( *StringLocation, ALIGN_TCHAR ) );
+ StringDest = (LPTSTR) (LPVOID) (*StringLocation);
+ StringDest -= (StringLength + 1);
+
+ *StringLocation = (LPBYTE) (LPVOID) StringDest;
+
+ ApiRecord->rpid1_dirname = StringDest;
+
+ (void) STRCPY(
+ StringDest, // dest
+ DirName); // src
+
+ //
+ // Next do stuff only found in level 1.
+ //
+ if (Level > 0) {
+
+ //
+ // Do master name (only other string)...
+ //
+ if (MasterName != NULL) {
+ StringLength = (DWORD) STRLEN( MasterName ) + 2; // Ch to UNC.
+
+ NetpAssert( POINTER_IS_ALIGNED( *StringLocation, ALIGN_TCHAR ) );
+ StringDest = (LPTSTR) (LPVOID) (*StringLocation);
+ StringDest -= (StringLength + 1);
+
+ *StringLocation = (LPBYTE) (LPVOID) StringDest;
+
+ ApiRecord->rpid1_mastername = StringDest;
+
+ (void) STRCPY(
+ StringDest, // dest
+ SLASH_SLASH ); // src
+ (void) STRCAT(
+ StringDest, // dest
+ MasterName); // src
+ } else {
+ ApiRecord->rpid1_mastername = NULL;
+ }
+
+ //
+ // Now do simple stuff...
+ //
+ {
+ ApiRecord->rpid1_state = State;
+ }
+
+ ApiRecord->rpid1_last_update_time = TimeOfLastUpdate;
+
+ ApiRecord->rpid1_lockcount = LockCount;
+
+ if (TimeOfFirstLock == 0) {
+ ApiRecord->rpid1_locktime = 0;
+ } else {
+ ApiRecord->rpid1_locktime = TimeOfFirstLock;
+ }
+ }
+
+ IF_DEBUG( IMPAPI ) {
+ NetpKdPrint(( PREFIX_REPL
+ "ImportDirBuildApiRecord: built structure:\n" ));
+ NetpDbgDisplayReplImportDir( Level, Buffer );
+ }
+
+ NetpAssert( ImportDirIsApiRecordValid( Level, ApiRecord, NULL ) );
+
+ return (NO_ERROR);
+
+}
diff --git a/private/net/svcdlls/repl/common/impconf.c b/private/net/svcdlls/repl/common/impconf.c
new file mode 100644
index 000000000..127812f2a
--- /dev/null
+++ b/private/net/svcdlls/repl/common/impconf.c
@@ -0,0 +1,720 @@
+/*++
+
+Copyright (c) 1992-1993 Microsoft Corporation
+
+Module Name:
+
+ ImpConf.c
+
+Abstract:
+
+ This file contains structures, function prototypes, and definitions
+ for the replicator import directory worker routines.
+
+Author:
+
+ John Rogers (JohnRo) 09-Jan-1992
+
+Environment:
+
+ User Mode - Win32
+ Portable to any flat, 32-bit environment. (Uses Win32 typedefs.)
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+ 09-Jan-1992 JohnRo
+ Created.
+ 23-Jan-1992 JohnRo
+ Clarify units for time parameters.
+ 27-Jan-1992 JohnRo
+ ImportDirReadConfigData() should return NERR_UnknownDevDir.
+ Changed to use LPTSTR etc.
+ 10-Feb-1992 JohnRo
+ ImportDirReadConfigData() should handle section not found.
+ 13-Feb-1992 JohnRo
+ Moved section name equates to ConfName.h.
+ 21-Feb-1992 JohnRo
+ Fixed bugs handling UNC master.
+ Added support for REPL_STATE_NOT_STARTED.
+ Added ImportDirDeleteConfigData() and ImportDirConfigDataExists().
+ 27-Feb-1992 JohnRo
+ Changed state not started to state never replicated.
+ 25-Mar-1992 JohnRo
+ Avoid obsolete state values.
+ Get rid of old config helpers.
+ 26-Mar-1992 JohnRo
+ Fixed bug parsing UncMaster.
+ 10-Jul-1992 JohnRo
+ RAID 10503: srv mgr: repl dialog doesn't come up.
+ Use PREFIX_ equates.
+ 23-Jul-1992 JohnRo
+ RAID 2274: repl svc should impersonate caller.
+ 29-Sep-1992 JohnRo
+ Fix remote repl admin.
+ 01-Dec-1992 JohnRo
+ RAID 3844: remote NetReplSetInfo uses local machine type.
+ 21-Jan-1993 JohnRo
+ RAID 7717: Repl assert if not logged on correctly. (Also do event
+ logging for real.)
+ Made changes suggested by PC-LINT 5.0
+ 30-Apr-1993 JohnRo
+ Use NetpKdPrint() where possible.
+
+--*/
+
+
+// These must be included first:
+
+#include <windef.h> // Win32 type definitions
+#include <lmcons.h> // LAN Manager common definitions
+
+// These may be included in any order:
+
+#include <config.h> // NetpConfig helpers.
+#include <confname.h> // SECT_NT_ equates.
+#include <dirname.h> // ReplIsDirNameValid().
+#include <impdir.h> // My prototypes.
+#include <lmapibuf.h> // NetApiBufferFree().
+#include <lmerr.h> // NO_ERROR, ERROR_, and NERR_ equates.
+#include <lmerrlog.h> // NELOG_ equates.
+#include <lmrepl.h> // REPL_STATE_ equates.
+#include <names.h> // NetpIsUncComputerNameValid().
+#include <netdebug.h> // NetpAssert(), etc.
+#include <netlib.h> // NetpPointerPlusSomeBytes().
+#include <prefix.h> // PREFIX_ equates.
+#include <replconf.h> // ReplConfigReportBadParmValue().
+#include <repldefs.h> // IF_DEBUG(), DWORDLEN, ReplErrorLog().
+#include <tstr.h> // STRCPY(), ATOL().
+
+
+#define IMPORT_VALUE_ARRAY_LEN \
+ (STATE_LEN /* state */ \
+ + 1 /* , */ \
+ + UNCLEN /* master */ \
+ + 1 /* , */ \
+ + DWORDLEN /* last_update_time (seconds since 1970) */ \
+ + 1 /* , */ \
+ + DWORDLEN /* lockcount */ \
+ + 1 /* , */ \
+ + DWORDLEN ) /* locktime (seconds since 1970) */
+
+#define OPTIONAL_LPTSTR( tstr ) \
+ ( (tstr) ? (tstr) : (LPTSTR) TEXT("<noname>") )
+
+
+// Tells whether or not config data for this directory exists.
+// Callable even if the replicator service is not started.
+BOOL
+ImportDirConfigDataExists (
+ IN LPTSTR UncServerName OPTIONAL,
+ IN LPTSTR DirName // Caller must check dir name syntax.
+ )
+{
+ NET_API_STATUS ApiStatus;
+ LPNET_CONFIG_HANDLE Handle;
+ LPTSTR Value;
+
+ //
+ // Check for caller's errors.
+ //
+ NetpAssert( ReplIsDirNameValid( DirName ) );
+
+ //
+ // Open the right section of the config file/whatever.
+ //
+ ApiStatus = NetpOpenConfigDataEx(
+ & Handle,
+ UncServerName,
+ (LPTSTR) SECT_NT_REPLICATOR, // section
+ (LPTSTR) SECT_NT_REPLICATOR_IMPORTS, // area (instead of parameters)
+ TRUE); // read-only
+
+ if (ApiStatus != NO_ERROR) {
+
+ // Log error on server we were trying to access.
+ ReplErrorLog(
+ UncServerName,
+ NELOG_ReplSysErr,
+ ApiStatus,
+ NULL,
+ NULL );
+
+ return (FALSE); // section doesn't exist, so dir data doesn't.
+ }
+
+ //
+ // Read the value from the config file/whatever.
+ //
+ ApiStatus = NetpGetConfigValue(
+ Handle,
+ DirName, // keyword is dir name
+ & Value); // alloc and set ptr
+
+ //
+ // We're done with this, so close the config data and toss the buffer we
+ // got. But remember the status from NetpGetConfigValue!
+ //
+ if (Value != NULL) {
+ (void) NetApiBufferFree( Value );
+ }
+ (void) NetpCloseConfigData( Handle );
+
+ //
+ // Now check the status of the Get.
+ //
+ if (ApiStatus == NERR_CfgParamNotFound) {
+ return (FALSE); // doesn't exist
+ } else if (ApiStatus == NO_ERROR) {
+ return (TRUE); // exists
+ } else {
+ // Log error on server we were trying to access.
+ ReplErrorLog(
+ UncServerName,
+ NELOG_ReplSysErr,
+ ApiStatus,
+ NULL,
+ NULL );
+
+ return (FALSE); // Say it doesn't exist, since we don't know.
+ }
+
+ /*NOTREACHED*/
+
+} // ImportDirConfigDataExists
+
+
+// Delete config data for this directory.
+// Returns NERR_UnknownDevDir if config data doesn't exist for this dir.
+// Callable even if the replicator service is not started.
+NET_API_STATUS
+ImportDirDeleteConfigData (
+ IN LPTSTR UncServerName OPTIONAL,
+ IN LPTSTR DirName // Caller must check dir name syntax.
+ )
+{
+ NET_API_STATUS ApiStatus;
+ LPNET_CONFIG_HANDLE Handle = NULL;
+
+ //
+ // Check for caller's errors.
+ //
+ if ( ! ReplIsDirNameValid( DirName ) ) {
+ ApiStatus = ERROR_INVALID_PARAMETER;
+ goto Cleanup; // Log error below.
+ }
+
+ //
+ // Open the right section of the config file/whatever.
+ //
+ ApiStatus = NetpOpenConfigDataEx(
+ & Handle,
+ UncServerName,
+ (LPTSTR) SECT_NT_REPLICATOR, // section
+ (LPTSTR) SECT_NT_REPLICATOR_IMPORTS, // area (instead of parameters)
+ FALSE ); // not read-only
+ if (ApiStatus == NERR_CfgCompNotFound) {
+ ApiStatus = NERR_UnknownDevDir;
+ goto Cleanup; // Log error below.
+ } else if (ApiStatus != NO_ERROR) {
+ goto Cleanup; // Log error below.
+ }
+
+ //
+ // Delete this keyword from this section.
+ //
+ ApiStatus = NetpDeleteConfigKeyword(
+ Handle,
+ DirName ); // keyword is dir name
+
+ IF_DEBUG(EXPAPI) {
+ NetpKdPrint(( PREFIX_REPL
+ "ImportDirDeleteConfigData: conf del ret " FORMAT_API_STATUS
+ ".\n", ApiStatus ));
+ }
+
+ if (ApiStatus == NERR_CfgParamNotFound) {
+ ApiStatus = NERR_UnknownDevDir;
+ goto Cleanup; // Log error below.
+ } else if (ApiStatus != NO_ERROR) {
+ goto Cleanup; // Log error below.
+ }
+
+ //
+ // All done.
+ //
+Cleanup:
+
+ if ( Handle != NULL ) {
+ NET_API_STATUS CloseStatus;
+
+ CloseStatus = NetpCloseConfigData( Handle );
+
+ // Return this error iff it is first error we've gotten.
+ if ( (ApiStatus == NO_ERROR) && (CloseStatus != NO_ERROR) ) {
+ ApiStatus = CloseStatus;
+ }
+ }
+
+ if (ApiStatus != NO_ERROR) {
+ // Log error on server we were trying to access.
+ ReplErrorLog(
+ UncServerName,
+ NELOG_ReplSysErr,
+ ApiStatus,
+ NULL,
+ NULL );
+ }
+
+
+ return (ApiStatus);
+
+} // ImportDirDeleteConfigData
+
+
+// Parse config data for a single import directory. Callable whether or not
+// the replicator service is started.
+NET_API_STATUS
+ImportDirParseConfigData (
+ IN LPTSTR UncServerName OPTIONAL,
+ IN LPTSTR ValueString,
+ OUT LPDWORD StatePtr,
+ OUT LPTSTR UncMasterPtr,
+ OUT LPDWORD LastUpdateTimePtr, // Seconds since 1970.
+ OUT LPDWORD LockCountPtr,
+ OUT LPDWORD LockTimePtr // Seconds since 1970.
+ )
+{
+ LPTSTR CurrentValuePtr = ValueString;
+
+ //
+ // Check for caller's errors.
+ //
+ NetpAssert( ValueString != NULL);
+ if (STRLEN( ValueString ) > IMPORT_VALUE_ARRAY_LEN) {
+ goto ReportBadConfigLine;
+ }
+
+ //
+ // Start parsing the value, which begins with a string containing the
+ // state.
+ //
+
+#define TRY_STATE( StateEquate, StateString ) \
+ { \
+ DWORD HopefulStringLength = STRLEN(StateString); \
+ if (STRNCMP( CurrentValuePtr, (StateString), HopefulStringLength) == 0) { \
+ *StatePtr = (StateEquate); \
+ CurrentValuePtr = (LPTSTR) (LPVOID) \
+ NetpPointerPlusSomeBytes( CurrentValuePtr, \
+ HopefulStringLength * sizeof(TCHAR) ); \
+ goto DoneState; \
+ } \
+ }
+
+ TRY_STATE( REPL_STATE_OK, OK_RP )
+
+ TRY_STATE( REPL_STATE_NO_SYNC, NO_SYNC_RP )
+
+ TRY_STATE( REPL_STATE_NO_MASTER, NO_MASTER_RP )
+
+ TRY_STATE( REPL_STATE_NEVER_REPLICATED, NEVER_REPLICATED_RP )
+
+ goto ReportBadConfigLine;
+
+DoneState:
+
+ //
+ // Parse the rest of the value string.
+ //
+
+#define PARSE_CHAR(AsciiChar) \
+ { \
+ if (*CurrentValuePtr == MAKE_TCHAR(AsciiChar)) { \
+ ++CurrentValuePtr; \
+ } else { \
+ goto ReportBadConfigLine; \
+ } \
+ }
+
+#define PARSE_COMMA( ) PARSE_CHAR(',')
+
+ PARSE_COMMA();
+
+ //
+ // Parse the (optional) UNC master name.
+ //
+ if (*CurrentValuePtr == MAKE_TCHAR('\\')) {
+ LPTSTR StringAfterMasterName;
+
+ StringAfterMasterName = STRCHR( CurrentValuePtr, MAKE_TCHAR(',') );
+ if (StringAfterMasterName == NULL) {
+ goto ReportBadConfigLine;
+ }
+
+ *StringAfterMasterName = TCHAR_EOS; // changes master to its own str.
+ if ( !NetpIsUncComputerNameValid( CurrentValuePtr ) ) {
+ goto ReportBadConfigLine;
+ }
+
+ (void) STRCPY( UncMasterPtr, CurrentValuePtr );
+ *StringAfterMasterName = MAKE_TCHAR(','); // put line back as is was
+ CurrentValuePtr = StringAfterMasterName; // skip to the comma.
+ } else if (*CurrentValuePtr == MAKE_TCHAR(',')) {
+ if (UncMasterPtr != NULL) {
+ *UncMasterPtr = TCHAR_EOS;
+ }
+ } else {
+ goto ReportBadConfigLine;
+ }
+
+ PARSE_COMMA();
+
+ //
+ // Parse the numbers on the rest of the line.
+ //
+
+#define PARSE_DWORD( NumberPtr ) \
+ { \
+ if ( ! ISDIGIT( *CurrentValuePtr ) ) { \
+ goto ReportBadConfigLine; \
+ } \
+ NetpAssert( NumberPtr != NULL ); \
+ * NumberPtr = (DWORD) ATOL( CurrentValuePtr ); \
+ while ( ISDIGIT( *CurrentValuePtr ) ) { \
+ ++CurrentValuePtr; \
+ } \
+ }
+
+ PARSE_DWORD( LastUpdateTimePtr ); // Seconds since 1970.
+
+ PARSE_COMMA();
+
+ PARSE_DWORD( LockCountPtr );
+
+ PARSE_COMMA();
+
+ PARSE_DWORD( LockTimePtr ); // Seconds since 1970.
+
+ IF_DEBUG(IMPAPI) {
+ NetpKdPrint(( PREFIX_REPL
+ "ImportDirParseConfigValue: Value = '" FORMAT_LPTSTR "',\n",
+ ValueString ));
+ NetpKdPrint((
+ " State = " FORMAT_DWORD
+ ", UncMaster = '" FORMAT_LPTSTR "'.\n",
+ *StatePtr, OPTIONAL_LPTSTR(UncMasterPtr) ));
+ NetpKdPrint((
+ " last update time (secs since 1970) = " FORMAT_DWORD
+ ", lock count = " FORMAT_DWORD
+ ", lock time (secs since 1970) = " FORMAT_DWORD ".\n",
+ *LastUpdateTimePtr, *LockCountPtr, *LockTimePtr ));
+ }
+
+ if ( ! ReplIsStateValid( *StatePtr ) ) {
+ goto ReportBadConfigLine;
+ }
+
+ return (NO_ERROR);
+
+ReportBadConfigLine:
+
+ // BUGBUG: Sure would be nice if we could include dirname here.
+
+ ReplConfigReportBadParmValue(
+ UncServerName,
+ (LPTSTR) SECT_NT_REPLICATOR_IMPORTS,
+ ValueString );
+
+ return (ERROR_INVALID_DATA);
+
+} // ImportDirParseConfigData
+
+
+// Read config data for a single import directory. Callable whether or not
+// the replicator service is started. Returns NERR_UnknownDevDir if there
+// is no config data for this directory.
+NET_API_STATUS
+ImportDirReadConfigData (
+ IN LPTSTR UncServerName OPTIONAL,
+ IN LPTSTR DirName,
+ OUT LPDWORD StatePtr,
+ OUT LPTSTR UncMasterPtr,
+ OUT LPDWORD LastUpdateTimePtr, // Seconds since 1970.
+ OUT LPDWORD LockCountPtr,
+ OUT LPDWORD LockTimePtr // Seconds since 1970.
+ )
+{
+ NET_API_STATUS ApiStatus;
+ LPNET_CONFIG_HANDLE Handle = NULL;
+ LPTSTR Value = NULL;
+
+ //
+ // Check for caller's errors.
+ //
+ if ( ! ReplIsDirNameValid( DirName ) ) {
+ ApiStatus = ERROR_INVALID_PARAMETER;
+ goto Cleanup; // go log error
+ }
+
+ //
+ // Open the right section of the config file/whatever.
+ //
+ ApiStatus = NetpOpenConfigDataEx(
+ & Handle,
+ UncServerName,
+ (LPTSTR) SECT_NT_REPLICATOR, // section
+ (LPTSTR) SECT_NT_REPLICATOR_IMPORTS, // area (instead of parameters)
+ TRUE); // read-only
+ if (ApiStatus == NERR_CfgCompNotFound) {
+ ApiStatus = NERR_UnknownDevDir;
+ goto Cleanup; // go log error
+ } else if (ApiStatus != NO_ERROR) {
+ goto Cleanup; // go log error
+ }
+
+ //
+ // Read the value from the config file/whatever.
+ //
+ ApiStatus = NetpGetConfigValue(
+ Handle,
+ DirName, // keyword is dir name
+ & Value); // alloc and set ptr
+ if (ApiStatus == NERR_CfgParamNotFound) {
+ ApiStatus = NERR_UnknownDevDir;
+ goto Cleanup; // go log error
+ } else if (ApiStatus != NO_ERROR) {
+ goto Cleanup; // go log error
+ }
+ NetpAssert( Value != NULL );
+
+ IF_DEBUG(IMPAPI) {
+ NetpKdPrint(( PREFIX_REPL
+ "ImportDirReadConfigValue( " FORMAT_LPTSTR "): '"
+ FORMAT_LPTSTR "' = '" FORMAT_LPTSTR "'.\n",
+ (UncServerName!=NULL) ? UncServerName : (LPTSTR) TEXT("local"),
+ DirName, Value ));
+ }
+
+ //
+ // Parse the value string...
+ //
+ ApiStatus = ImportDirParseConfigData (
+ UncServerName,
+ Value,
+ StatePtr,
+ UncMasterPtr,
+ LastUpdateTimePtr, // Seconds since 1970.
+ LockCountPtr,
+ LockTimePtr); // Seconds since 1970.
+ // Fall through and log error if this failed.
+
+Cleanup:
+ //
+ // All done.
+ //
+ if ( Handle != NULL ) {
+ NET_API_STATUS CloseStatus;
+
+ CloseStatus = NetpCloseConfigData( Handle );
+
+ // Return this error iff it is first error we've gotten.
+ if ( (ApiStatus == NO_ERROR) && (CloseStatus != NO_ERROR) ) {
+ ApiStatus = CloseStatus;
+ }
+ }
+
+ if (Value != NULL) {
+ (VOID) NetApiBufferFree( Value );
+ }
+
+ if (ApiStatus != NO_ERROR) {
+ // Log error on server we were trying to access.
+ ReplErrorLog(
+ UncServerName,
+ NELOG_ReplSysErr,
+ ApiStatus,
+ NULL,
+ NULL );
+ }
+
+ return (ApiStatus);
+
+} // ImportDirReadConfigData
+
+
+
+
+
+// Write config data for a single import directory. Callable whether or not
+// the replicator service is started.
+NET_API_STATUS
+ImportDirWriteConfigData (
+ IN LPTSTR UncServerName OPTIONAL,
+ IN LPTSTR DirName,
+ IN DWORD State,
+ IN LPTSTR UncMaster OPTIONAL,
+ IN DWORD LastUpdateTime, // Seconds since 1970.
+ IN DWORD LockCount,
+ IN DWORD LockTime // Seconds since 1970.
+ )
+{
+ NET_API_STATUS ApiStatus;
+ LPNET_CONFIG_HANDLE Handle = NULL;
+ TCHAR ValueArray[IMPORT_VALUE_ARRAY_LEN+1];
+
+ IF_DEBUG(IMPAPI) {
+ NetpKdPrint(( PREFIX_REPL
+ "ImportDirWriteConfigValue:( " FORMAT_LPTSTR " ): DirName = "
+ FORMAT_LPTSTR ".\n",
+ (UncServerName!=NULL) ? UncServerName : (LPTSTR) TEXT("local"),
+ DirName ));
+ NetpKdPrint((
+ " State = " FORMAT_DWORD
+ ", UncMaster = '" FORMAT_LPTSTR "'.\n",
+ State, OPTIONAL_LPTSTR(UncMaster) ));
+ NetpKdPrint((
+ " last update time (secs since 1970) = " FORMAT_DWORD
+ ", lock count = " FORMAT_DWORD
+ ", lock time (secs since 1970) = " FORMAT_DWORD ".\n",
+ LastUpdateTime, LockCount, LockTime ));
+ }
+ //
+ // Check for caller's errors.
+ //
+ if ( ! ReplIsDirNameValid( DirName ) ) {
+ ApiStatus = ERROR_INVALID_PARAMETER;
+ goto Cleanup; // go log error
+ }
+ if ( ! ReplIsStateValid( State ) ) {
+ ApiStatus = ERROR_INVALID_PARAMETER;
+ goto Cleanup; // go log error
+ }
+ if (UncMaster != NULL) {
+ if ( ! NetpIsUncComputerNameValid( UncMaster ) ) {
+ ApiStatus = ERROR_INVALID_PARAMETER;
+ goto Cleanup; // go log error
+ }
+ }
+
+ //
+ // Open the right section of the config file/whatever.
+ //
+ ApiStatus = NetpOpenConfigDataEx(
+ & Handle,
+ UncServerName,
+ (LPTSTR) SECT_NT_REPLICATOR, // section
+ (LPTSTR) SECT_NT_REPLICATOR_IMPORTS, // area (instead of parameters)
+ FALSE); // not read-only
+ if (ApiStatus != NO_ERROR) {
+ goto Cleanup; // go log error
+ }
+
+ //
+ // Start building the value, which begins with a string containing the
+ // state.
+ //
+ switch (State) {
+ case REPL_STATE_OK:
+
+ (void) STRCPY( ValueArray, OK_RP);
+ break;
+
+ case REPL_STATE_NO_SYNC:
+
+ (void) STRCPY( ValueArray, NO_SYNC_RP);
+ break;
+
+ case REPL_STATE_NO_MASTER:
+
+ (void) STRCPY( ValueArray, NO_MASTER_RP);
+ break;
+
+ case REPL_STATE_NEVER_REPLICATED:
+
+ (void) STRCPY( ValueArray, NEVER_REPLICATED_RP);
+ break;
+
+ default:
+ NetpAssert( FALSE );
+ ApiStatus = ERROR_INVALID_PARAMETER;
+ goto Cleanup; // go log error
+ }
+
+ //
+ // Build the rest of the value string.
+ //
+ (void) STRCAT( ValueArray, (LPTSTR) TEXT(",") );
+
+ if (UncMaster != NULL) {
+ (void) STRCAT( ValueArray, UncMaster );
+ }
+
+/*lint -save -e767 */ // Don't complain about different definitions
+#define WRITE_COMMA( ) \
+ (void) STRCAT( ValueArray, (LPTSTR) TEXT(",") )
+/*lint -restore */ // Resume checking for different macro definitions
+
+/*lint -save -e767 */ // Don't complain about different definitions
+#define WRITE_DWORD( Number ) \
+ { \
+ LPTSTR StrEnd = & ValueArray[ STRLEN( ValueArray ) ]; \
+ (void) ULTOA( (Number), StrEnd, /* radix */ 10 ); \
+ }
+/*lint -restore */ // Resume checking for different macro definitions
+
+ WRITE_COMMA();
+
+ WRITE_DWORD( LastUpdateTime ); // Seconds since 1970.
+
+ WRITE_COMMA();
+
+ WRITE_DWORD( LockCount );
+
+ WRITE_COMMA();
+
+ WRITE_DWORD( LockTime ); // Seconds since 1970.
+
+ IF_DEBUG(IMPAPI) {
+ NetpKdPrint(( PREFIX_REPL
+ "ImportDirWriteConfigValue: '" FORMAT_LPTSTR "' = '"
+ FORMAT_LPTSTR "'.\n", DirName, ValueArray ));
+ }
+
+ //
+ // Write this value out to the config file/whatever.
+ //
+ ApiStatus = NetpSetConfigValue(
+ Handle,
+ DirName, // keyword is dir name
+ ValueArray);
+ // Fall through and log error if one happened.
+
+Cleanup:
+ //
+ // All done.
+ //
+ if ( Handle != NULL ) {
+ NET_API_STATUS CloseStatus;
+
+ CloseStatus = NetpCloseConfigData( Handle );
+
+ // Return this error iff it is first error we've gotten.
+ if ( (ApiStatus == NO_ERROR) && (CloseStatus != NO_ERROR) ) {
+ ApiStatus = CloseStatus;
+ }
+ }
+
+ if (ApiStatus != NO_ERROR) {
+ // Log error on server we were trying to access.
+ ReplErrorLog(
+ UncServerName,
+ NELOG_ReplSysErr,
+ ApiStatus,
+ NULL,
+ NULL );
+ }
+
+ return (ApiStatus);
+
+} // ImportDirWriteConfigData
diff --git a/private/net/svcdlls/repl/common/impdir.h b/private/net/svcdlls/repl/common/impdir.h
new file mode 100644
index 000000000..d38228c36
--- /dev/null
+++ b/private/net/svcdlls/repl/common/impdir.h
@@ -0,0 +1,259 @@
+/*++
+
+Copyright (c) 1992-1993 Microsoft Corporation
+
+Module Name:
+
+ ImpDir.h
+
+Abstract:
+
+ This file contains structures, function prototypes, and definitions
+ for the replicator import directory worker routines.
+
+Author:
+
+ John Rogers (JohnRo) 07-Jan-1992
+
+Environment:
+
+ User Mode - Win32
+ Portable to any flat, 32-bit environment. (Uses Win32 typedefs.)
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Notes:
+
+ You must include LmCons.h and ReplDefs.h before this file.
+
+Revision History:
+
+ 07-Jan-1992 JohnRo
+ Created.
+ 09-Jan-1992 JohnRo
+ Added ImportDirBuildApiRecord().
+ Added IMPORT_DIR_SECTION_NAME equate.
+ Added ImportDir{Read,Write}ConfigData.
+ 16-Jan-1992 JohnRo
+ Corrected "Notes" comment above.
+ 20-Jan-1992 JohnRo
+ Netr prototypes are now generated by MIDL and put in repl.h.
+ 23-Jan-1992 JohnRo
+ Clarify units for time parameters.
+ Changed IMPORT_DIR_SECTION_NAME.
+ 27-Jan-1992 JohnRo
+ Added ImportDirSetState().
+ Changed to use LPTSTR etc.
+ Changed to avoid use of client-specific data structure.
+ 09-Feb-1992 JohnRo
+ Added ImportDir{Start,Stop}Repl routines.
+ 13-Feb-1992 JohnRo
+ Moved section name equates to ConfName.h.
+ 19-Feb-1992 JohnRo
+ Added ImportDirIsApiRecordValid() and various other routines.
+ 21-Feb-1992 JohnRo
+ UncMaster parm is optional to ImportDirBuildApiRecord.
+ 21-Feb-1992 JohnRo
+ Changed ImportDirBuildApiRecord() so master name is not a UNC name.
+ 15-Mar-1992 JohnRo
+ Update registry with new values.
+ 23-Mar-1992 JohnRo
+ Added ImportDirReadClientList().
+ 24-Mar-1992 JohnRo
+ UncMaster parm is optional to ImportDirWriteConfigData().
+ 30-Jul-1992 JohnRo
+ Help PC-LINT understand ImportDirIsLevelValid().
+ 25-Sep-1992 JohnRo
+ RAID 5494: repl svc does not maintain time stamp on import startup.
+ 29-Sep-1992 JohnRo
+ RAID 7962: Repl APIs in wrong role kill svc.
+ Also fix remote repl admin.
+ 01-Dec-1992 JohnRo
+ RAID 3844: remote NetReplSetInfo uses local machine type.
+ 13-Apr-1993 JohnRo
+ RAID 3107: locking directory over the net gives network path not found.
+
+--*/
+
+
+#ifndef _IMPDIR_
+#define _IMPDIR_
+
+
+//
+// Import dir helper routines and macros:
+//
+
+
+// Allocate one or more API records for an import directory. Callable whether
+// or not the replicator service is started. (Used in getinfo stub, getinfo
+// worker, and enum stub.)
+NET_API_STATUS
+ImportDirAllocApiRecords (
+ IN DWORD Level,
+ IN DWORD EntryCount,
+ OUT LPBYTE * BufPtr,
+ IN OUT LPBYTE *StringLocation // Points just past top of data.
+ );
+
+NET_API_STATUS
+ImportDirBuildApiRecord (
+ IN DWORD Level,
+ IN LPTSTR DirName,
+ IN DWORD State,
+ IN LPTSTR MasterName OPTIONAL, // computer name (not UNC).
+ IN DWORD TimeOfLastUpdate, // Seconds since 1970.
+ IN DWORD LockCount,
+ IN DWORD TimeOfFirstLock, // Seconds since 1970.
+ OUT LPVOID Buffer,
+ IN OUT LPBYTE *StringLocation // Points just past top of data.
+ );
+
+// Tells whether or not config data for this directory exists.
+// Callable even if the replicator service is not started.
+BOOL
+ImportDirConfigDataExists (
+ IN LPTSTR UncServerName OPTIONAL,
+ IN LPTSTR DirName // Caller must check dir name syntax.
+ );
+
+NET_API_STATUS
+ImportDirConfigSetInfo (
+ IN LPTSTR UncServerName OPTIONAL,
+ IN LPTSTR DirName,
+ IN DWORD Level,
+ IN LPVOID Buf,
+ OUT LPDWORD ParmError OPTIONAL
+ );
+
+// Delete config data for this directory.
+// Returns NERR_UnknownDevDir if config data doesn't exist for this dir.
+// Callable even if the replicator service is not started.
+NET_API_STATUS
+ImportDirDeleteConfigData (
+ IN LPTSTR UncServerName OPTIONAL,
+ IN LPTSTR DirName // Caller must check dir name syntax.
+ );
+
+// Callable even if the replicator service is not started.
+NET_API_STATUS
+ImportDirEnumApiRecords(
+ IN LPTSTR UncServerName OPTIONAL,
+ IN DWORD Level,
+ OUT LPBYTE * BufPtr,
+ IN DWORD PrefMaxSize,
+ OUT LPDWORD EntriesRead,
+ OUT LPDWORD TotalEntries
+ );
+
+// Callable even if the replicator service is not started.
+NET_API_STATUS
+ImportDirGetApiRecord (
+ IN LPTSTR UncServerName OPTIONAL,
+ IN LPTSTR DirName,
+ IN DWORD Level,
+ OUT LPBYTE * BufPtr
+ );
+
+BOOL
+ImportDirIsApiRecordValid (
+ IN DWORD Level,
+ IN LPVOID ApiRecord,
+ OUT LPDWORD ParmError OPTIONAL
+ );
+
+// BOOL
+// ImportDirIsLevelValid(
+// IN DWORD Level
+// );
+//
+#define ImportDirIsLevelValid(Level) \
+ /*lint -e506 */ /* don't complain about constant values here */ \
+ ( ((Level) <= 1 ) ? TRUE : FALSE ) \
+ /*lint +e506 */ \
+
+// Callable whether or not service is started.
+// If service is running, assume caller has lock (any kind) on RCGlobalListLock.
+NET_API_STATUS
+ImportDirLockInRegistry(
+ IN LPTSTR UncServerName OPTIONAL,
+ IN LPTSTR DirName
+ );
+
+// Parse config data for a single import directory. Callable whether or not
+// the replicator service is started.
+NET_API_STATUS
+ImportDirParseConfigData (
+ IN LPTSTR UncServerName OPTIONAL,
+ IN LPTSTR KeywordValue,
+ OUT LPDWORD StatePtr,
+ OUT LPTSTR MasterPtr,
+ OUT LPDWORD LastUpdateTimePtr, // Seconds since 1970.
+ OUT LPDWORD LockCountPtr,
+ OUT LPDWORD LockTimePtr // Seconds since 1970.
+ );
+
+// Read import dirs into service's client list.
+// Only callable as part of the service itself.
+NET_API_STATUS
+ImportDirReadClientList(
+ VOID
+ );
+
+// Read config data for a single import directory. Callable whether or not
+// the replicator service is started.
+NET_API_STATUS
+ImportDirReadConfigData (
+ IN LPTSTR UncServerName OPTIONAL,
+ IN LPTSTR DirName,
+ OUT LPDWORD StatePtr,
+ OUT LPTSTR MasterPtr,
+ OUT LPDWORD LastUpdateTimePtr, // Seconds since 1970.
+ OUT LPDWORD LockCountPtr,
+ OUT LPDWORD LockTimePtr // Seconds since 1970.
+ );
+
+// Change the state for a single import directory.
+NET_API_STATUS
+ImportDirSetState (
+ IN LPTSTR DirName,
+ IN DWORD State // Must be REPL_STATE_ value.
+ );
+
+// Start replicating (importing).
+// Called when service starts or user does NetReplSetInfo() and changes role.
+NET_API_STATUS
+ImportDirStartRepl (
+ IN BOOL ServiceIsStarting
+ );
+
+// Stop replicating (importing).
+// Called when service stops or user does NetReplSetInfo() and changes role.
+NET_API_STATUS
+ImportDirStopRepl (
+ VOID
+ );
+
+// Callable whether or not service is started.
+// If service is running, assume caller has lock (any kind) on RCGlobalListLock.
+NET_API_STATUS
+ImportDirUnlockInRegistry(
+ IN LPTSTR UncServerName OPTIONAL,
+ IN LPTSTR DirName,
+ IN DWORD UnlockForce
+ );
+
+// Write config data for a single import directory. Callable whether or not
+// the replicator service is started.
+NET_API_STATUS
+ImportDirWriteConfigData (
+ IN LPTSTR UncServerName OPTIONAL,
+ IN LPTSTR DirName,
+ IN DWORD State,
+ IN LPTSTR UncMaster OPTIONAL,
+ IN DWORD LastUpdateTime, // Seconds since 1970.
+ IN DWORD LockCount,
+ IN DWORD LockTime // Seconds since 1970.
+ );
+
+
+#endif // _IMPDIR_
diff --git a/private/net/svcdlls/repl/common/impenum.c b/private/net/svcdlls/repl/common/impenum.c
new file mode 100644
index 000000000..0d03aba40
--- /dev/null
+++ b/private/net/svcdlls/repl/common/impenum.c
@@ -0,0 +1,321 @@
+/*++
+
+Copyright (c) 1991-1993 Microsoft Corporation
+
+Module Name:
+
+ ImpEnum.c
+
+Abstract:
+
+ ImporDirEnumApiRecords.
+
+Author:
+
+ John Rogers (JohnRo) 17-Dec-1991
+
+Environment:
+
+ User Mode - Win32
+
+Revision History:
+
+ 16-Nov-1992 JohnRo
+ RAID 1537: Repl APIs in wrong role kill svc. (Extracted from DLL stub.)
+ 01-Dec-1992 JohnRo
+ RAID 3844: remote NetReplSetInfo uses local machine type.
+ 10-Mar-1993 JohnRo
+ RAID 12871: replication UI shows nothing (adding entry while enumerating
+ results in empty list).
+ Made changes suggested by PC-LINT 5.0
+ Use NetpKdPrint() where possible.
+
+--*/
+
+
+// These must be included first:
+
+#include <windows.h> // IN, DWORD, etc.
+#include <lmcons.h> // NET_API_STATUS, etc.
+
+// These may be included in any order:
+
+#include <config.h> // LPNET_CONFIG_HANDLE, Netp config routines.
+#include <confname.h> // SECT_NT_ equates.
+#include <dirname.h> // ReplIsDirNameValid().
+#include <impdir.h> // My prototype.
+#include <lmerr.h> // NERR_ and ERROR_ equates, NO_ERROR.
+#include <lmerrlog.h> // NELOG_ equates.
+#include <netdebug.h> // NetpAssert(), NetpKdPrint(), etc.
+#include <prefix.h> // PREFIX_ equates.
+#include <repldefs.h> // IF_DEBUG(), ReplErrorLog(), etc.
+#include <strucinf.h> // NetpReplImportDirStructureInfo().
+
+
+
+// Callable even if the replicator service is not started.
+NET_API_STATUS
+ImportDirEnumApiRecords(
+ IN LPTSTR UncServerName OPTIONAL,
+ IN DWORD Level,
+ OUT LPBYTE * BufPtr,
+ IN DWORD PrefMaxSize,
+ OUT LPDWORD EntriesRead,
+ OUT LPDWORD TotalEntries
+ )
+
+{
+ NET_API_STATUS ApiStatus;
+
+ LPVOID ArrayEntry;
+ LPVOID ArrayStart = NULL;
+ DWORD EntriesAllocated = 0;
+ DWORD EntriesFound = 0;
+ BOOL FirstTime;
+ DWORD FixedEntrySize;
+ LPNET_CONFIG_HANDLE Handle = NULL;
+ LPBYTE StringLocation;
+
+ UNREFERENCED_PARAMETER( PrefMaxSize );
+
+ IF_DEBUG( IMPAPI ) {
+ NetpKdPrint(( PREFIX_REPL
+ "ImportDirEnumApiRecords( " FORMAT_LPTSTR
+ " ): doing parameter checks...\n",
+ (UncServerName!=NULL) ? UncServerName : (LPTSTR) TEXT("local")
+ ));
+ }
+
+ if ( ! ImportDirIsLevelValid( Level ) ) {
+ ApiStatus = ERROR_INVALID_LEVEL;
+ goto Cleanup;
+ } else if (BufPtr == NULL) {
+ ApiStatus = ERROR_INVALID_PARAMETER;
+ goto Cleanup;
+ } else if (EntriesRead == NULL) {
+ ApiStatus = ERROR_INVALID_PARAMETER;
+ goto Cleanup;
+ } else if (TotalEntries == NULL) {
+ ApiStatus = ERROR_INVALID_PARAMETER;
+ goto Cleanup;
+ }
+
+ //
+ // Set outputs in case we run into an error.
+ //
+
+ * BufPtr = NULL;
+ * EntriesRead = 0;
+ * TotalEntries = 0;
+
+ //
+ // Figure-out the size of a fixed entry for this info level.
+ //
+ ApiStatus = NetpReplImportDirStructureInfo (
+ Level,
+ PARMNUM_ALL,
+ TRUE, // want native sizes
+ NULL, // don't need data desc 16
+ NULL, // don't need data desc 32
+ NULL, // don't need data desc SMB
+ NULL, // don't need max size
+ & FixedEntrySize,
+ NULL ); // don't need string size
+
+ NetpAssert( ApiStatus == NO_ERROR ); // Already checked args.
+ NetpAssert( FixedEntrySize > 0 );
+
+ //
+ // Open the right section of the config file/whatever.
+ //
+ ApiStatus = NetpOpenConfigDataEx(
+ & Handle,
+ UncServerName,
+ (LPTSTR) SECT_NT_REPLICATOR, // service name
+ (LPTSTR) SECT_NT_REPLICATOR_IMPORTS, // area under service
+ TRUE); // read-only
+ if (ApiStatus != NO_ERROR) {
+
+ // Handle section not found as empty enum array (not error).
+ if (ApiStatus == NERR_CfgCompNotFound) {
+ ApiStatus = NO_ERROR;
+ goto Cleanup;
+ }
+
+ goto Cleanup;
+ }
+
+ //
+ // Loop, expanding buffer if necessary, until we get it large enough.
+ //
+
+ do {
+
+ //
+ // Count entries in config data.
+ //
+ ApiStatus = NetpNumberOfConfigKeywords (
+ Handle,
+ & EntriesAllocated );
+
+ if (ApiStatus != NO_ERROR) {
+ goto Cleanup;
+ } else if (EntriesAllocated == 0) {
+ goto Cleanup;
+ }
+
+ //
+ // Alloc and/or expand the array...
+ //
+ ApiStatus = ImportDirAllocApiRecords (
+ Level,
+ EntriesAllocated,
+ (LPBYTE *) (LPVOID) & ArrayStart,
+ & StringLocation ); // Points just past top of data.
+ if (ApiStatus == NO_ERROR) {
+ NetpAssert( ArrayStart != NULL );
+ } else {
+ goto Cleanup;
+ }
+
+ //
+ // Go back and reread, filling-in the config data as we go.
+ //
+
+ ArrayEntry = ArrayStart;
+ FirstTime = TRUE;
+
+ while (ApiStatus == NO_ERROR) {
+ LPTSTR DirName;
+ DWORD LockCount;
+ DWORD State;
+ DWORD TimeOfFirstLock; // Seconds since 1970.
+ DWORD TimeOfLastUpdate; // Seconds since 1970.
+ TCHAR UncMaster[UNCLEN+1];
+ LPTSTR ValueString;
+
+ ApiStatus = NetpEnumConfigSectionValues (
+ Handle,
+ & DirName, // Keyword - alloc and set ptr.
+ & ValueString, // Must be freed by NetApiBufferFree().
+ FirstTime );
+
+ FirstTime = FALSE;
+
+ if (ApiStatus == NO_ERROR) {
+ NetpAssert( DirName != NULL );
+ NetpAssert( ValueString != NULL );
+
+ ++EntriesFound;
+ if (EntriesFound > EntriesAllocated) {
+ EntriesFound = 0;
+ NetpMemoryFree( ArrayStart );
+ ArrayStart = NULL;
+ ApiStatus = ERROR_MORE_DATA;
+ break; // exit per-entry loop and try again
+ }
+
+ if ( !ReplIsDirNameValid( DirName ) ) {
+ // BUGBUG: perhaps delete entry, log event, and continue?
+ ApiStatus = ERROR_INVALID_DATA;
+ NetpMemoryFree( DirName );
+ NetpMemoryFree( ValueString );
+ goto Cleanup;
+ }
+
+ //
+ // Parse the value string...
+ //
+ ApiStatus = ImportDirParseConfigData (
+ UncServerName,
+ ValueString,
+ & State,
+ UncMaster,
+ & TimeOfLastUpdate, // Seconds since 1970.
+ & LockCount,
+ & TimeOfFirstLock ); // Seconds since 1970.
+
+ NetpMemoryFree( ValueString );
+
+ if (ApiStatus == NO_ERROR) {
+ LPTSTR MasterName;
+ NetpAssert( ArrayEntry != NULL );
+
+ if (*UncMaster) {
+ MasterName = UncMaster + 2; // Chg from UNC to non.
+ } else {
+ MasterName = NULL;
+ }
+
+ //
+ // Build API record for this entry.
+ //
+ ApiStatus = ImportDirBuildApiRecord (
+ Level,
+ DirName,
+ State, // State when svc was running.
+ MasterName,
+ TimeOfLastUpdate, // Seconds since 1970.
+ LockCount,
+ TimeOfFirstLock, // Seconds since 1970.
+ ArrayEntry,
+ & StringLocation );
+ if (ApiStatus != NO_ERROR) {
+ NetpKdPrint(( PREFIX_REPL
+ "ImportDirEnumApiRecords: error "
+ FORMAT_API_STATUS " from imp build.\n",
+ ApiStatus ));
+ NetpAssert( FALSE );
+ goto Cleanup;
+ }
+
+ ArrayEntry = NetpPointerPlusSomeBytes(
+ ArrayEntry, FixedEntrySize );
+ }
+ NetpMemoryFree( DirName );
+
+ }
+
+ } // while not error (may be NERR_CfgParamNotFound at end).
+
+ if (ApiStatus == NERR_CfgParamNotFound) {
+ ApiStatus = NO_ERROR;
+ } else {
+ NetpAssert( ApiStatus != NO_ERROR );
+ goto Cleanup;
+ }
+
+ } while (ApiStatus == ERROR_MORE_DATA);
+
+ //
+ // All done.
+ //
+
+Cleanup:
+
+ if (Handle != NULL) {
+ (VOID) NetpCloseConfigData( Handle );
+ }
+
+ NetpAssert( ApiStatus != ERROR_MORE_DATA );
+ if (ApiStatus == NO_ERROR) {
+ * BufPtr = ArrayStart;
+ * EntriesRead = EntriesFound;
+ * TotalEntries = EntriesFound;
+ } else {
+ NetpKdPrint(( PREFIX_REPL
+ "ImportDirEnumApiRecords: returning status " FORMAT_API_STATUS
+ ".\n", ApiStatus ));
+
+ // Log the error.
+ ReplErrorLog(
+ UncServerName, // log here and there.
+ NELOG_ReplSysErr,
+ ApiStatus,
+ NULL, // no str1
+ NULL ); // no str2
+ }
+
+ return ApiStatus;
+
+}
diff --git a/private/net/svcdlls/repl/common/impget.c b/private/net/svcdlls/repl/common/impget.c
new file mode 100644
index 000000000..54a9e0513
--- /dev/null
+++ b/private/net/svcdlls/repl/common/impget.c
@@ -0,0 +1,149 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ ImpGet.c
+
+Abstract:
+
+ Read an API record with import directory info from the registry.
+ Callable even if the replicator service is not started.
+
+Author:
+
+ John Rogers (JohnRo) 09-Nov-1992
+
+Environment:
+
+ User Mode - Win32
+
+Revision History:
+
+ 09-Nov-1992 JohnRo
+ Created for RAID 7962: Repl APIs in wrong role kill svc.
+
+--*/
+
+
+// These must be included first:
+
+//#include <nt.h> // Needed by <config.h> temporarily.
+//#include <ntrtl.h> // Needed by <config.h> temporarily.
+#include <windef.h> // IN, DWORD, etc.
+
+#include <lmcons.h> // NET_API_STATUS, etc.
+//#include <rap.h> // Needed by <strucinf.h>.
+//#include <repldefs.h> // Needed by <impdir.h>.
+//#include <rpc.h> // Needed by <netrpc.h>.
+
+// These may be included in any order:
+
+//#include <config.h> // LPNET_CONFIG_HANDLE, Netp config routines.
+//#include <confname.h> // SECT_NT_ equates.
+#include <dirname.h> // ReplIsDirNameValid().
+#include <impdir.h> // My ptototype, ImportDirIsApiRecordValid(), etc.
+#include <lmapibuf.h> // NetApiBufferFree().
+//#include <lmrepl.h> // My prototypes, etc.
+//#include <lmsvc.h> // SERVICE_ equates.
+#include <netdebug.h> // NetpAssert().
+//#include <netlib.h> // NetpSetParmError() macro.
+//#include <netrpc.h> // NET_REMOTE macros.
+//#include <prefix.h> // PREFIX_ equates.
+//#include <repl.h> // MIDL-generated NetrRepl prototypes.
+//#include <rpcutil.h> // GENERIC_INFO_CONTAINER, etc.
+//#include <strucinf.h> // NetpReplImportDirStructureInfo().
+#include <winerror.h> // ERROR_ equates, NO_ERROR.
+
+
+
+NET_API_STATUS
+ImportDirGetApiRecord (
+ IN LPTSTR UncServerName OPTIONAL,
+ IN LPTSTR DirName,
+ IN DWORD Level,
+ OUT LPBYTE * BufPtr
+ )
+{
+ NET_API_STATUS ApiStatus;
+ LPVOID ApiRecord = NULL;
+ DWORD LockCount;
+ LPTSTR MasterName;
+ DWORD State;
+ LPBYTE StringLocation; // Points just past top of data.
+ DWORD TimeOfFirstLock; // Seconds since 1970.
+ DWORD TimeOfLastUpdate; // Seconds since 1970.
+ TCHAR UncMaster[UNCLEN+1];
+
+ if ( !ReplIsDirNameValid( DirName )) {
+ ApiStatus = ERROR_INVALID_PARAMETER;
+ goto Cleanup;
+ } else if ( !ImportDirIsLevelValid( Level ) ) {
+ ApiStatus = ERROR_INVALID_LEVEL;
+ goto Cleanup;
+ } else if (BufPtr == NULL) {
+ ApiStatus = ERROR_INVALID_PARAMETER;
+ goto Cleanup;
+ }
+
+ //
+ // Read config data for a single import directory.
+ //
+ ApiStatus = ImportDirReadConfigData (
+ UncServerName,
+ DirName,
+ & State,
+ UncMaster,
+ & TimeOfLastUpdate, // Seconds since 1970.
+ & LockCount,
+ & TimeOfFirstLock ); // Seconds since 1970.
+
+ if (ApiStatus != NO_ERROR) {
+ goto Cleanup;
+ }
+
+ ApiStatus = ImportDirAllocApiRecords (
+ Level,
+ 1, // only 1 record.
+ (LPBYTE *) & ApiRecord, // alloc and set ptr
+ (LPBYTE *) & StringLocation ); // Points just past top of data.
+ if (ApiStatus != NO_ERROR) {
+ goto Cleanup;
+ }
+ NetpAssert( ApiRecord != NULL );
+
+ if (*UncMaster) {
+ MasterName = UncMaster + 2; // Chg from UNC to non.
+ } else {
+ MasterName = NULL;
+ }
+
+ ApiStatus = ImportDirBuildApiRecord (
+ Level,
+ DirName,
+ State,
+ MasterName,
+ TimeOfLastUpdate, // Seconds since 1970
+ LockCount,
+ TimeOfFirstLock, // Seconds since 1970.
+ ApiRecord,
+ (LPBYTE *) (LPVOID) & StringLocation);
+ NetpAssert( ApiStatus == NO_ERROR ); // We checked all parms.
+
+
+Cleanup:
+
+ if (ApiStatus == NO_ERROR) {
+ NetpAssert( ApiRecord != NULL );
+ *BufPtr = ApiRecord;
+ } else {
+ *BufPtr = NULL;
+ if (ApiRecord != NULL) {
+ (VOID) NetApiBufferFree( ApiRecord );
+ }
+ }
+
+ return ApiStatus;
+
+}
diff --git a/private/net/svcdlls/repl/common/implock.c b/private/net/svcdlls/repl/common/implock.c
new file mode 100644
index 000000000..c94a70f05
--- /dev/null
+++ b/private/net/svcdlls/repl/common/implock.c
@@ -0,0 +1,172 @@
+/*++
+
+Copyright (c) 1992-1993 Microsoft Corporation
+
+Module Name:
+
+ ImpLock.c
+
+Abstract:
+
+ Routines to update lock fields in registry (for import dirs).
+
+Author:
+
+ John Rogers (JohnRo) 15-Mar-1992
+
+Environment:
+
+ User Mode - Win32
+
+Notes:
+
+ This file is extremely similar to ExpLock.c. If you fix any bugs here,
+ make sure they're reflected there, and vice versa.
+
+Revision History:
+
+ 15-Mar-1992 JohnRo
+ Update registry with new values.
+ 29-Sep-1992 JohnRo
+ Also fix remote repl admin.
+ 13-Apr-1993 JohnRo
+ RAID 3107: locking directory over the net gives network path not found.
+ Made changes suggested by PC-LINT 5.0
+ 30-Apr-1993 JohnRo
+ Use NetpKdPrint() where possible.
+
+--*/
+
+
+// These must be included first:
+
+#include <windef.h> // IN, DWORD, etc.
+#include <lmcons.h> // NET_API_STATUS, etc.
+
+// These may be included in any order:
+
+#include <dirname.h> // ReplIsDirNameValid().
+#include <impdir.h> // My prototypes, etc.
+#include <lmrepl.h> // REPL_FORCE_ equates.
+#include <netdebug.h> // NetpKdPrint().
+#include <repldefs.h> // Stuff.
+#include <winerror.h> // NO_ERROR, ERROR_ equates.
+
+
+
+// Callable whether or not service is started.
+// If service is running, assume caller has lock (any kind) on RCGlobalListLock.
+NET_API_STATUS
+ImportDirLockInRegistry(
+ IN LPTSTR UncServerName OPTIONAL,
+ IN LPTSTR DirName
+ )
+{
+ NET_API_STATUS ApiStatus;
+ DWORD LockCount;
+ DWORD State;
+ TCHAR UncMaster[UNCLEN+1];
+ DWORD TimeOfFirstLock; // Seconds since 1970.
+ DWORD TimeOfLastUpdate; // Seconds since 1970.
+
+ IF_DEBUG(REPL) {
+ NetpKdPrint(( "ImportDirLockInRegistry( " FORMAT_LPTSTR
+ "): beginnning...\n",
+ (UncServerName!=NULL) ? UncServerName : (LPTSTR) TEXT("local") ));
+ }
+
+ if ( !ReplIsDirNameValid( DirName )) {
+ return (ERROR_INVALID_PARAMETER);
+ }
+
+ // Read config data for a single import directory.
+ ApiStatus = ImportDirReadConfigData (
+ UncServerName,
+ DirName,
+ & State,
+ UncMaster,
+ & TimeOfLastUpdate, // Seconds since 1970.
+ & LockCount,
+ & TimeOfFirstLock ); // Seconds since 1970.
+
+ if (ApiStatus == NO_ERROR) {
+
+ ApiStatus = ReplIncrLockFields(
+ & LockCount,
+ & TimeOfFirstLock );
+ }
+ if (ApiStatus == NO_ERROR) {
+ ApiStatus = ImportDirWriteConfigData (
+ UncServerName,
+ DirName,
+ State,
+ (*UncMaster) ? UncMaster : NULL,
+ TimeOfLastUpdate, // Seconds since 1970.
+ LockCount,
+ TimeOfFirstLock ); // Seconds since 1970.
+ }
+
+ return ApiStatus;
+
+} // ImportDirLockInRegistry
+
+
+// Callable whether or not service is started.
+// If service is running, assume caller has lock (any kind) on RCGlobalListLock.
+NET_API_STATUS
+ImportDirUnlockInRegistry(
+ IN LPTSTR UncServerName OPTIONAL,
+ IN LPTSTR DirName,
+ IN DWORD UnlockForce
+ )
+{
+ NET_API_STATUS ApiStatus;
+ DWORD LockCount;
+ DWORD State;
+ TCHAR UncMaster[UNCLEN+1];
+ DWORD TimeOfFirstLock; // Seconds since 1970.
+ DWORD TimeOfLastUpdate; // Seconds since 1970.
+
+ IF_DEBUG(REPL) {
+ NetpKdPrint(( "ImportDirUnlockInRegistry( " FORMAT_LPTSTR
+ "): beginnning...\n",
+ (UncServerName!=NULL) ? UncServerName : (LPTSTR) TEXT("local") ));
+ }
+
+ if ( !ReplIsDirNameValid( DirName )) {
+ return (ERROR_INVALID_PARAMETER);
+ }
+
+ // Read config data for a single import directory.
+ ApiStatus = ImportDirReadConfigData (
+ UncServerName,
+ DirName,
+ & State,
+ UncMaster,
+ & TimeOfLastUpdate, // Seconds since 1970.
+ & LockCount,
+ & TimeOfFirstLock ); // Seconds since 1970.
+
+ if (ApiStatus == NO_ERROR) {
+
+ ApiStatus = ReplDecrLockFields(
+ & LockCount,
+ & TimeOfFirstLock,
+ UnlockForce );
+ }
+ if (ApiStatus == NO_ERROR) {
+ // Write the new or revised data.
+ ApiStatus = ImportDirWriteConfigData (
+ UncServerName,
+ DirName,
+ State,
+ (*UncMaster) ? UncMaster : NULL,
+ TimeOfLastUpdate, // Seconds since 1970.
+ LockCount,
+ TimeOfFirstLock ); // Seconds since 1970.
+
+ }
+
+ return ApiStatus;
+
+} // ImportDirUnlockInRegistry
diff --git a/private/net/svcdlls/repl/common/imports.h b/private/net/svcdlls/repl/common/imports.h
new file mode 100644
index 000000000..5a1f5577c
--- /dev/null
+++ b/private/net/svcdlls/repl/common/imports.h
@@ -0,0 +1,58 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ imports.h
+
+Abstract:
+
+ This file allows us to include standard system header files in the
+ .idl file. The main .idl file imports a file called import.idl.
+ This allows the .idl file to use the types defined in these header
+ files. It also causes the following line to be added in the
+ MIDL generated header file:
+
+ #include "imports.h"
+
+ Thus these types are available to the RPC stub routines as well.
+
+Author:
+
+ John Rogers (JohnRo) 17-Jan-1992
+
+Revision History:
+
+ 17-Jan-1992 JohnRo
+ Created the repl service RPC stuff from Rita's workstation RPC stuff.
+ 24-Jan-1992 JohnRo
+ Changed to use LPTSTR etc.
+ 16-Feb-1992 JohnRo
+ Corrected use of LPTSTR.
+
+--*/
+
+
+#include <windef.h>
+#include <lmcons.h>
+
+#ifdef MIDL_PASS
+
+#ifdef UNICODE
+#define LPTSTR [string] wchar_t *
+#else
+#define LPTSTR [string] LPTSTR
+#endif
+
+#define LPSTR [string] char_t *
+#define LPCSTR [string] char_t *
+#define LPWSTR [string] wchar_t *
+#define LPCWSTR [string] wchar_t *
+
+//#define LPSTR [string] LPSTR
+#define BOOL DWORD
+
+#endif
+
+#include <lmrepl.h>
diff --git a/private/net/svcdlls/repl/common/impstate.c b/private/net/svcdlls/repl/common/impstate.c
new file mode 100644
index 000000000..ab467aa2e
--- /dev/null
+++ b/private/net/svcdlls/repl/common/impstate.c
@@ -0,0 +1,124 @@
+/*++
+
+Copyright (c) 1992-1993 Microsoft Corporation
+
+Module Name:
+
+ ImpState.c
+
+Abstract:
+
+ This file contains ImportDirSetState().
+
+Author:
+
+ John Rogers (JohnRo) 27-Jan-1992
+
+Environment:
+
+ User Mode - Win32
+ Portable to any flat, 32-bit environment. (Uses Win32 typedefs.)
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+ 27-Jan-1992 JohnRo
+ Created.
+ 26-Mar-1992 JohnRo
+ Added more argument checking.
+ 26-Mar-1992 JohnRo
+ Don't pass ImportDirWriteConfigData() UncMaster as ptr to null char.
+ 29-Sep-1992 JohnRo
+ Fix remote repl admin.
+ 04-Dec-1992 JohnRo
+ Handle unexpected errors better.
+ Use PREFIX_ equates.
+ Made changes suggested by PC-LINT 5.0
+ 30-Apr-1993 JohnRo
+ Use NetpKdPrint() where possible.
+
+--*/
+
+
+#include <windef.h> // Win32 type definitions
+#include <lmcons.h> // NET_API_STATUS, UNCLEN.
+
+#include <dirname.h> // ReplIsDirNameValid().
+#include <impdir.h> // My prototypes, IMPORT_DIR_SECTION_NAME.
+#include <lmerr.h> // ERROR_, NERR_, NO_ERROR equates.
+#include <netdebug.h> // NetpAssert().
+#include <prefix.h> // PREFIX_ equates.
+#include <repldefs.h> // IF_DEBUG(), ReplIsStateValid(), etc.
+#include <tstr.h> // TCHAR_EOS.
+
+
+// Change the state for a single import directory.
+// Creates an entry with defaults if necessary.
+NET_API_STATUS
+ImportDirSetState (
+ IN LPTSTR DirName,
+ IN DWORD State
+ )
+{
+ NET_API_STATUS ApiStatus;
+ DWORD LastUpdateTime; // Seconds since 1970.
+ DWORD LockCount;
+ DWORD LockTime; // Seconds since 1970.
+ DWORD OldState;
+ TCHAR UncMaster[UNCLEN+1];
+
+ NetpAssert( ReplIsDirNameValid( DirName ) );
+ NetpAssert( ReplIsStateValid( State ) );
+
+ // Read config data for a this import directory.
+ ApiStatus = ImportDirReadConfigData (
+ NULL, // no server name
+ DirName,
+ & OldState,
+ UncMaster,
+ & LastUpdateTime, // Seconds since 1970.
+ & LockCount,
+ & LockTime); // Seconds since 1970.
+
+ if (ApiStatus == NERR_UnknownDevDir) {
+
+ // Set defaults.
+ LastUpdateTime = 0;
+ LockCount = 0;
+ LockTime = 0;
+ UncMaster[0] = TCHAR_EOS;
+
+ } else if (ApiStatus != NO_ERROR) {
+
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ImportDirSetState: unexpected ret "
+ FORMAT_API_STATUS " from ImportDirReadConfigData("
+ FORMAT_LPTSTR ").\n",
+ ApiStatus, DirName ));
+ goto Cleanup;
+
+ }
+
+ // Write revised config data for this directory.
+ ApiStatus = ImportDirWriteConfigData (
+ NULL, // no server name
+ DirName,
+ State,
+ (*UncMaster) ? UncMaster : NULL,
+ LastUpdateTime, // Seconds since 1970.
+ LockCount,
+ LockTime ); // Seconds since 1970.
+
+ if (ApiStatus != NO_ERROR) {
+
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ImportDirSetState: unexpected ret "
+ FORMAT_API_STATUS " from ImportDirWriteConfigData("
+ FORMAT_LPTSTR ").\n",
+ ApiStatus, DirName ));
+ }
+
+Cleanup:
+ return (ApiStatus);
+
+} // ImportDirSetState
diff --git a/private/net/svcdlls/repl/common/impvalid.c b/private/net/svcdlls/repl/common/impvalid.c
new file mode 100644
index 000000000..2fc7eeb6d
--- /dev/null
+++ b/private/net/svcdlls/repl/common/impvalid.c
@@ -0,0 +1,118 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ ImpValid.c
+
+Abstract:
+
+ This file contains ImportDirIsApiRecordValid().
+
+Author:
+
+ John Rogers (JohnRo) 22-Jan-1992
+
+Environment:
+
+ Runs under Windows NT.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Notes:
+
+ This code assumes that the info levels are subsets of each other.
+
+Revision History:
+
+ 18-Feb-1992 JohnRo
+ Added ImportDirIsApiRecordValid().
+ 21-Feb-1992 JohnRo
+ rpid1_mastername is a UNC name.
+ 27-Feb-1992 JohnRo
+ Check lock fields for validity.
+ rpid1_mastername is optional.
+
+--*/
+
+
+// These must be included first:
+
+#include <windef.h> // IN, VOID, LPTSTR, etc.
+#include <lmcons.h> // NET_API_STATUS, PARM equates, etc.
+#include <repldefs.h> // IF_DEBUG(), ReplIsIntegrityValid(), etc.
+
+// These can be in any order:
+
+#include <dirname.h> // ReplIsDirNameValid().
+#include <impdir.h> // My prototype.
+#include <lmrepl.h> // LPREPL_EDIR_INFO_1, REPL_EXTENT_ stuff, etc.
+#include <names.h> // NetpIsUncComputerNameValid().
+#include <netlib.h> // NetpSetParmError().
+
+
+BOOL
+ImportDirIsApiRecordValid (
+ IN DWORD Level,
+ IN LPVOID Buf,
+ OUT LPDWORD ParmError OPTIONAL
+ )
+
+{
+ LPREPL_IDIR_INFO_1 ApiRecord; // Superset info level.
+
+ //
+ // First, make sure level is valid and there's actually a struct there.
+ //
+ NetpSetParmError( PARM_ERROR_UNKNOWN ); // Assume error until proven...
+ if (Level > 1) {
+ return (FALSE); // No, it's not valid.
+ }
+ if (Buf == NULL) {
+ return (FALSE); // No, it's not valid.
+ }
+ ApiRecord = Buf;
+
+ //
+ // Check item(s) common to all levels.
+ //
+ if ( ! ReplIsDirNameValid(ApiRecord->rpid1_dirname) ) {
+ NetpSetParmError( 1 ); // Error in first field in ApiRecord.
+ return (FALSE); // No, it's not valid.
+ }
+
+ //
+ // Check items unique to level 1.
+ //
+ if (Level > 0) {
+ if ( ! ReplIsStateValid(ApiRecord->rpid1_state) ) {
+ NetpSetParmError( 2 ); // Error in second field in ApiRecord.
+ return (FALSE); // No, it's not valid.
+ }
+
+ if ( ApiRecord->rpid1_mastername != NULL ) {
+ if ( ! NetpIsUncComputerNameValid(ApiRecord->rpid1_mastername) ) {
+ NetpSetParmError( 3 ); // Error in third field in ApiRecord.
+ return (FALSE); // No, it's not valid.
+ }
+ }
+
+
+ // BUGBUG: No check possible for the following field?
+ // 4 (last_update_time)
+
+ if ( !ReplAreLockFieldsValid( ApiRecord->rpid1_lockcount,
+ ApiRecord->rpid1_locktime) ) {
+ NetpSetParmError( 5 ); // Error in fifth/sixth field.
+ return (FALSE); // No, it's not valid.
+ }
+
+ }
+
+ //
+ // Everything went OK. Tell caller.
+ //
+ NetpSetParmError( PARM_ERROR_NONE );
+ return (TRUE); // Yes, it's valid.
+
+}
diff --git a/private/net/svcdlls/repl/common/iniparm.h b/private/net/svcdlls/repl/common/iniparm.h
new file mode 100644
index 000000000..861f8f0bf
--- /dev/null
+++ b/private/net/svcdlls/repl/common/iniparm.h
@@ -0,0 +1,87 @@
+/*++
+
+Copyright (c) 1987-1992 Microsoft Corporation
+
+Module Name:
+ iniparm.h
+
+Abstract:
+ Function prototypes and some global data
+
+Author:
+ Ported from Lan Man 2.x
+
+Environment:
+ Contains NT-specific code.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+ 10/23/91 (madana)
+ ported to NT. Converted to NT style.
+ 20-Jan-1992 JohnRo
+ More changes suggested by PC-LINT.
+ Removed support for lanman dir switch and/or config keyword.
+ The tryuser variable should be treated as a BOOL.
+ 24-Jan-1992 JohnRo
+ Added KEYWORD_ equates (for config use) and got rid of SW_ equates.
+ Added default equates for integrity and extent.
+ Changed BOTH_SW etc to be TCHARs.
+ Moved current role from P_repl_sw to ReplGlobalRole.
+ Use REPL_ROLE_ equates just like the APIs do.
+ 13-Feb-1992 JohnRo
+ Renamed KEYWORD_ equates to REPL_KEYWORD_ and moved to <confname.h>.
+ Got rid of DEFAULT_REPL equate. Ditto DEFAULT_EXPORTPATH etc.
+ 29-Jul-1992 JohnRo
+ RAID 2650: repl svc should handle new subdirs.
+
+
+--*/
+
+
+#ifndef _INIPARM_
+#define _INIPARM_
+
+
+// default parameters
+
+//#define DEFAULT_REPL NULL
+
+//#define DEFAULT_EXPPATH NULL
+//#define DEFAULT_IMPPATH NULL
+
+#define DEFAULT_EXPLIST NULL
+#define DEFAULT_IMPLIST NULL
+
+#define DEFAULT_TRYUSER TRUE
+
+#define DEFAULT_INTEGRITY REPL_INTEGRITY_FILE
+#define DEFAULT_EXTENT REPL_EXTENT_TREE
+
+#define DEFAULT_STATE REPL_STATE_NEVER_REPLICATED
+
+
+// parameter range
+
+#define DEFAULT_LOGON NULL
+#define DEFAULT_PASSWD NULL
+#define DEFAULT_SYNC 5
+#define MAX_SYNC 60
+#define MIN_SYNC 1
+#define DEFAULT_PULSE 3
+#define MAX_PULSE 10
+#define MIN_PULSE 1
+#define DEFAULT_GUARD 2
+#define MAX_GUARD 30
+#define MIN_GUARD 0
+#define DEFAULT_RANDOM 60
+#define MAX_RANDOM 120
+#define MIN_RANDOM 1
+
+
+//
+// (Global config variables used to be declared here.
+// They are now declared in ReplGbl.h --JohnRo, 24-Jan-1992.)
+//
+
+
+#endif // _INIPARM_
diff --git a/private/net/svcdlls/repl/common/lstvalid.c b/private/net/svcdlls/repl/common/lstvalid.c
new file mode 100644
index 000000000..20e853a06
--- /dev/null
+++ b/private/net/svcdlls/repl/common/lstvalid.c
@@ -0,0 +1,162 @@
+/*++
+
+Copyright (c) 1987-1993 Microsoft Corporation
+
+Module Name:
+
+ LstValid.c
+
+Abstract:
+
+ ReplConfigIsListValid() is used to validate the syntax of export lists
+ and import lists. No existence checking is done.
+
+Author:
+
+ John Rogers, using code ported from Lan Man 2.x
+
+Environment:
+
+ Requires ANSI C extensions: slash-slash comments, long external names.
+ Tab size is set to 4.
+
+Revision History:
+
+ 14-Aug-1992 JohnRo
+ RAID 3601: repl APIs should checked import & export lists.
+ (Extracted this code from repl/server/master.c to help track down bogus
+ import/export lists.)
+ 27-Aug-1992 JohnRo
+ RAID 4611: repl: fix import/export lists.
+ 05-Jan-1993 JohnRo
+ Repl WAN support.
+ Made changes suggested by PC-LINT 5.0
+ 30-Apr-1993 JohnRo
+ Use NetpKdPrint() where possible.
+
+
+--*/
+
+// These must be included first:
+
+#include <windows.h>
+#include <lmcons.h>
+
+// These may be included in any order:
+
+#include <netdebug.h> // NetpKdPrint(), FORMAT_ equates, etc.
+#include <netlib.h> // NetpMemoryAllocate(), NetpIsServerStarted().
+#include <icanon.h>
+#include <prefix.h> // PREFIX_ equates.
+#include <repldefs.h> // IF_DEBUG(), etc.
+#include <replconf.h> // My prototypes.
+#include <tstr.h> // STRSIZE(), TCHAR_EOS.
+
+
+BOOL
+ReplConfigIsListValid(
+ IN LPTSTR UncanonList OPTIONAL
+ )
+
+/*++
+
+Routine Description:
+
+ Makes sure the synax of List is valid.
+ Callable whether or not service is started.
+
+Arguments:
+
+ UncanonList - optionally points to a semicolon-separated list of domain
+ names (e.g. TEXT("MyDomain")) and server names (e.g.
+ TEXT("SomeServer")).
+
+Return Value:
+
+ BOOL - TRUE iff List has valid syntax.
+
+--*/
+{
+ NET_API_STATUS ApiStatus;
+ DWORD EntryCount;
+ LPTSTR CanonList;
+ DWORD CanonListSize;
+
+ //
+ // Handle simple cases first.
+ //
+
+ if (UncanonList == NULL) {
+ return (TRUE); // yes, syntax is valid.
+ }
+ if (*UncanonList == TCHAR_EOS) {
+ return (TRUE); // yes, syntax is valid.
+ }
+
+ //
+ // Allocate temp space for parsed list.
+ //
+
+ CanonListSize = STRSIZE( UncanonList );
+
+ CanonList = NetpMemoryAllocate( CanonListSize );
+
+ if (CanonList == NULL) {
+
+ NetpKdPrint(( PREFIX_REPL
+ "ReplConfigIsListValid() can't allocate memory for "
+ "CanonList.\n" ));
+
+ return (FALSE); // can't tell if syntax is valid, so assume not.
+ }
+
+ IF_DEBUG( REPL ) {
+ NetpKdPrint(( PREFIX_REPL
+ "ReplConfigIsListValid before canon:\n" ));
+ NetpDbgDisplayReplList(
+ "uncanon list",
+ UncanonList );
+ }
+
+ //
+ // Do the actual parse.
+ //
+
+ ApiStatus = I_NetListCanonicalize(NULL,
+ UncanonList,
+ (LPTSTR) LIST_DELIMITER_STR_UI,
+ CanonList,
+ CanonListSize,
+ &EntryCount,
+ NULL, // PathTypes
+ 0, // PathTypesLen
+ (NAMETYPE_COMPUTER |
+ OUTLIST_TYPE_API |
+ INLC_FLAGS_MULTIPLE_DELIMITERS |
+ INLC_FLAGS_CANONICALIZE
+ ));
+
+ if (ApiStatus != NO_ERROR) {
+
+ NetpKdPrint(( PREFIX_REPL
+ "ReplConfigIsListValid() is in trouble calling "
+ "I_NetListCanonicalize, ApiStatus is " FORMAT_API_STATUS
+ "\n", ApiStatus ));
+
+ NetpMemoryFree(CanonList);
+
+ return (FALSE); // syntax is not valid, so say so.
+ }
+
+ IF_DEBUG( REPL ) {
+ NetpKdPrint(( PREFIX_REPL
+ "ReplConfigIsListValid after canon:\n" ));
+ NetpDbgDisplayReplList(
+ "canon list",
+ CanonList );
+ }
+
+ NetpMemoryFree( CanonList );
+
+ return (TRUE); // yes, syntax is valid.
+}
diff --git a/private/net/svcdlls/repl/common/makefile b/private/net/svcdlls/repl/common/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/net/svcdlls/repl/common/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/net/svcdlls/repl/common/replbld.c b/private/net/svcdlls/repl/common/replbld.c
new file mode 100644
index 000000000..b20694427
--- /dev/null
+++ b/private/net/svcdlls/repl/common/replbld.c
@@ -0,0 +1,190 @@
+/*++
+
+Copyright (c) 1992-1993 Microsoft Corporation
+
+Module Name:
+
+ ReplBld.c
+
+Abstract:
+
+ This file contains ReplConfigAllocAndBuildApiRecord().
+
+Author:
+
+ John Rogers (JohnRo) 23-Jan-1992
+
+Environment:
+
+ User Mode - Win32
+ Portable to any flat, 32-bit environment. (Uses Win32 typedefs.)
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+ 23-Jan-1992 JohnRo
+ Created.
+ 24-Jan-1992 JohnRo
+ Changed to use LPTSTR etc.
+ 18-Feb-1992 JohnRo
+ Validate more of caller's parms.
+ Added assertion check of built record.
+ 11-Mar-1992 JohnRo
+ Fixed an error code.
+ 30-Apr-1993 JohnRo
+ Use NetpKdPrint() where possible.
+ Use PREFIX_ equates.
+
+--*/
+
+
+#include <windef.h> // IN, DWORD, etc.
+#include <lmcons.h> // LAN Manager common definitions
+#include <repldefs.h> // IF_DEBUG().
+
+#include <align.h> // ALIGN_ equates.
+#include <lmapibuf.h> // NetApiBufferAllocate().
+#include <lmrepl.h> // LMREPL_INFO_0.
+#include <netdebug.h> // NetpKdPrint(), etc.
+#include <netlib.h> // NetpCopyDataToBuffer(), etc.
+#include <prefix.h> // PREFIX_ equates.
+#include <replconf.h> // My prototype.
+#include <tstr.h> // STRLEN().
+#include <winerror.h> // ERROR_ equates, NO_ERROR.
+
+
+#define POSSIBLE_STRSIZE(s) ((s) ? STRSIZE(s) : 0)
+
+
+// Allocate and build a repl config API record. Callable whether or not
+// the replicator service is started.
+NET_API_STATUS
+ReplConfigAllocAndBuildApiRecord (
+ IN DWORD Level,
+ IN DWORD Role,
+ IN LPTSTR ExportPath OPTIONAL,
+ IN LPTSTR ExportList OPTIONAL,
+ IN LPTSTR ImportPath OPTIONAL,
+ IN LPTSTR ImportList OPTIONAL,
+ IN LPTSTR LogonUserName OPTIONAL,
+ IN DWORD Interval,
+ IN DWORD Pulse,
+ IN DWORD GuardTime,
+ IN DWORD Random,
+ OUT LPBYTE * BufPtr // Alloc and set pointer.
+ )
+
+
+{
+ NET_API_STATUS ApiStatus;
+ LPREPL_INFO_0 Buffer;
+ LPVOID OutFixedDataEnd;
+ DWORD SizeNeeded;
+ LPBYTE StringLocation;
+
+ //
+ // Check for caller's errors.
+ //
+ if ( Level != 0 ) {
+ return (ERROR_INVALID_LEVEL);
+ } else if (BufPtr == NULL) {
+ return (ERROR_INVALID_PARAMETER);
+ }
+ * BufPtr = NULL; // Test for memory access fault.
+ if ( !ReplIsIntervalValid(Interval) ) {
+ return (ERROR_INVALID_PARAMETER);
+ } else if ( !ReplIsPulseValid( Pulse ) ) {
+ return (ERROR_INVALID_PARAMETER);
+ } else if ( !ReplIsGuardTimeValid( GuardTime ) ) {
+ return (ERROR_INVALID_PARAMETER);
+ } else if ( !ReplIsRandomValid( Random ) ) {
+ return (ERROR_INVALID_PARAMETER);
+ }
+
+ ApiStatus = ReplCheckAbsPathSyntax( ExportPath );
+ if (ApiStatus != NO_ERROR) {
+ return (ERROR_INVALID_PARAMETER);
+ }
+
+ ApiStatus = ReplCheckAbsPathSyntax( ImportPath );
+ if (ApiStatus != NO_ERROR) {
+ return (ERROR_INVALID_PARAMETER);
+ }
+
+ // BUGBUG: Check ImportList and ExportList.
+ // BUGBUG: Check LogonUserName.
+
+ //
+ // Compute how much space we'll need and allocate it.
+ //
+ SizeNeeded = sizeof(REPL_INFO_0)
+ + POSSIBLE_STRSIZE(ExportPath)
+ + POSSIBLE_STRSIZE(ExportList)
+ + POSSIBLE_STRSIZE(ImportPath)
+ + POSSIBLE_STRSIZE(ImportList)
+ + POSSIBLE_STRSIZE(LogonUserName);
+
+ ApiStatus = NetApiBufferAllocate(
+ SizeNeeded,
+ (LPVOID *) & Buffer);
+ if (ApiStatus != NO_ERROR) {
+ return (ApiStatus);
+ }
+ NetpAssert( Buffer != NULL );
+
+ //
+ // Fill in the numbers in the structure.
+ //
+
+ Buffer->rp0_role = Role;
+ Buffer->rp0_interval = Interval;
+ Buffer->rp0_pulse = Pulse;
+ Buffer->rp0_guardtime = GuardTime;
+ Buffer->rp0_random = Random;
+
+ //
+ // Do the strings...
+ //
+
+ OutFixedDataEnd = NetpPointerPlusSomeBytes( Buffer, sizeof(REPL_INFO_0) );
+ StringLocation = NetpPointerPlusSomeBytes( Buffer, SizeNeeded );
+
+#define COPY_TSTRING_FIELD( destField, srcVar ) \
+ { \
+ if (srcVar != NULL) { \
+ BOOL CopyOK; \
+ CopyOK = NetpCopyDataToBuffer( \
+ (LPVOID) srcVar, \
+ STRSIZE(srcVar), \
+ OutFixedDataEnd, \
+ & StringLocation, \
+ (LPVOID) & Buffer->rp0_ ## destField, \
+ ALIGN_TCHAR); \
+ NetpAssert(CopyOK); \
+ } else { \
+ Buffer->rp0_ ## destField = NULL; \
+ } \
+ }
+
+ COPY_TSTRING_FIELD( exportpath, ExportPath );
+ COPY_TSTRING_FIELD( exportlist, ExportList );
+ COPY_TSTRING_FIELD( importpath, ImportPath );
+ COPY_TSTRING_FIELD( importlist, ImportList );
+ COPY_TSTRING_FIELD( logonusername, LogonUserName );
+
+ //
+ // All done.
+ //
+
+ IF_DEBUG(REPLAPI) {
+ NetpKdPrint(( PREFIX_REPL
+ "ReplConfigAllocAndBuildApiRecord: built structure...\n" ));
+ NetpDbgDisplayRepl( Level, Buffer );
+ }
+
+ NetpAssert( ReplConfigIsApiRecordValid( Level, Buffer, NULL ) );
+
+ * BufPtr = (LPVOID) Buffer;
+ return (NO_ERROR);
+
+} // ReplConfigAllocAndBuildApiRecord
diff --git a/private/net/svcdlls/repl/common/replconf.c b/private/net/svcdlls/repl/common/replconf.c
new file mode 100644
index 000000000..5b40f032f
--- /dev/null
+++ b/private/net/svcdlls/repl/common/replconf.c
@@ -0,0 +1,666 @@
+/*++
+
+Copyright (c) 1992-1993 Microsoft Corporation
+
+Module Name:
+
+ ReplConf.c
+
+Abstract:
+
+ This file contains structures, function prototypes, and definitions
+ for the replicator export directory worker routines.
+
+Author:
+
+ John Rogers (JohnRo) 27-Jan-1992
+
+Environment:
+
+ User Mode - Win32
+ Portable to any flat, 32-bit environment. (Uses Win32 typedefs.)
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+ 27-Jan-1992 JohnRo
+ Created.
+ 04-Feb-1992 JohnRo
+ Fixed bug handling return code from ReplIsAbsPathValid().
+ Improved handling of default strings (which may be NULL pointers).
+ 13-Feb-1992 JohnRo
+ Do some handling of errors for missing parameters.
+ Moved section name and keyword equates to ConfName.h.
+ 18-Feb-1992 JohnRo
+ Handle optional keywords in _write routine.
+ Fixed some overlooked cleanup in error paths in _read routine.
+ Moved ReplCheckAbsPathSyntax() out for general use.
+ Fixed occasional wrong keyword in error messages.
+ 24-Feb-1992 JohnRo
+ Interval is obsolete for NT: don't keep in registry.
+ (Keep in APIs just in case we implement repl APIs for OS/2, etc.)
+ 26-Feb-1992 JohnRo
+ Fixed bug in defaulting the interval.
+ 11-Mar-1992 JohnRo
+ Added some error checking for export path and import path on write.
+ Improved some error handling after calling NetpOpenConfigData().
+ 23-Mar-1992 JohnRo
+ Get rid of old config helpers.
+ 27-Aug-1992 JohnRo
+ RAID 4611: validate ImportList and ExportList as read from registry.
+ 21-Sep-1992 JohnRo
+ RAID 6685: ReplConfigRead trashes Server Manager.
+ 04-Dec-1992 JohnRo
+ RAID 3844: remote NetReplSetInfo uses local machine type.
+ Improve range check of guard time field.
+ Also, the interval (sync) field is not obsolete after all.
+ 05-Jan-1993 JohnRo
+ Repl WAN support (get rid of repl name list limits).
+ Made changes suggested by PC-LINT 5.0
+ 26-Jan-1993 JohnRo
+ RAID 7717: Repl assert if not logged on correctly. (Also do event
+ logging for real.)
+
+--*/
+
+
+// These must be included first:
+
+#include <windef.h> // MAX_PATH, etc.
+#include <lmcons.h> // LAN Manager common definitions
+
+// These may be included in any order:
+
+#include <config.h> // NetpConfig helpers, LPNET_CONFIG_HANDLE.
+#include <confname.h> // SECT_NT_ equates, REPL_KEYWORD_ too.
+#include <lmapibuf.h> // NetApiBufferFree().
+#include <lmerr.h> // NERR_, ERROR_, NO_ERROR equates.
+#include <lmerrlog.h> // NELOG_ equates.
+#include <lmrepl.h> // REPL_ROLE equates, etc.
+#include <netdebug.h> // NetpAssert(), FORMAT_ equates, etc.
+#include <replconf.h> // My prototypes.
+#include <repldefs.h> // IF_DEBUG(), ReplCheckAbsPathSyntax().
+#include <tstr.h> // STRICMP(), TCHAR_EOS, etc.
+
+
+//
+// Macro to set an output from a default string value (which may be NULL).
+//
+#define SET_STRING_TO_DEFAULT( output, default ) \
+ { \
+ /*lint -save -e506 */ /* don't complain about constant values here */ \
+ if (default != NULL) { \
+ (void) STRCPY( output, default ); \
+ } else { \
+ *output = TCHAR_EOS; \
+ } \
+ /*lint -restore */ \
+ }
+
+
+
+// Callable whether or not service is started.
+// All outputs are undefined if return value is not NO_ERROR.
+NET_API_STATUS
+ReplConfigRead(
+ IN LPTSTR UncServerName OPTIONAL,
+ OUT LPDWORD Role,
+ OUT LPTSTR ExportPath,
+ OUT LPTSTR *ExportList, // alloc and set ptr
+ OUT LPTSTR ImportPath,
+ OUT LPTSTR *ImportList, // alloc and set ptr
+ OUT LPTSTR LogonUserName,
+ OUT LPDWORD Interval,
+ OUT LPDWORD Pulse,
+ OUT LPDWORD GuardTime,
+ OUT LPDWORD Random
+ )
+{
+ NET_API_STATUS ApiStatus;
+ DWORD DwordValue;
+ LPNET_CONFIG_HANDLE Handle = NULL;
+ LPTSTR StringValue = NULL;
+
+ //
+ // Open the right section of the config file/whatever.
+ //
+ ApiStatus = NetpOpenConfigData(
+ & Handle,
+ UncServerName,
+ (LPTSTR) SECT_NT_REPLICATOR,
+ TRUE); // read-only
+ if (ApiStatus != NO_ERROR) {
+
+ goto Cleanup; // go log error
+ }
+
+ //
+ // Read role (REPLICATOR switch)...
+ // This value is required.
+ // For historical reasons, this can be a string ("Both" etc) or a DWORD
+ // (REPL_ROLE_BOTH etc).
+ //
+ ApiStatus = NetpGetConfigValue(
+ Handle,
+ (LPTSTR) REPL_KEYWORD_ROLE, // KeyWanted
+ & StringValue); // alloc and set ptr
+ if (ApiStatus == NERR_CfgParamNotFound) {
+ ReplConfigReportBadParmValue(
+ UncServerName,
+ (LPTSTR) REPL_KEYWORD_ROLE,
+ NULL );
+
+ goto Cleanup; // go log error
+
+ } else if (ApiStatus == NO_ERROR) { // Read of string value OK.
+
+ DWORD CharCount;
+ NetpAssert( StringValue != NULL );
+ CharCount = STRLEN( StringValue );
+
+ if ( STRSPN( StringValue, (LPTSTR) TEXT("0123456789") ) == CharCount ) {
+ //
+ // Convert the string to a numeric value.
+ //
+ *Role = (DWORD) ATOL( StringValue );
+
+ if ( !ReplIsRoleValid( *Role ) ) {
+ ReplConfigReportBadParmValue(
+ UncServerName,
+ (LPTSTR) REPL_KEYWORD_ROLE,
+ StringValue );
+ ApiStatus = ERROR_INVALID_DATA;
+ goto Cleanup;
+ }
+ } else if (STRICMP(StringValue, (LPTSTR) REPL_KEYWORD_ROLE_BOTH) == 0) {
+ *Role = REPL_ROLE_BOTH;
+ } else if (STRICMP(StringValue, (LPTSTR) REPL_KEYWORD_ROLE_EXPORT) == 0) {
+ *Role = REPL_ROLE_EXPORT;
+ } else if (STRICMP(StringValue, (LPTSTR) REPL_KEYWORD_ROLE_IMPORT) == 0) {
+ *Role = REPL_ROLE_IMPORT;
+ } else {
+
+ ReplConfigReportBadParmValue(
+ UncServerName,
+ (LPTSTR) REPL_KEYWORD_ROLE,
+ StringValue );
+ ApiStatus = ERROR_INVALID_DATA;
+ goto Cleanup;
+
+ }
+ (void) NetApiBufferFree( StringValue );
+ StringValue = NULL;
+
+ } else if (ApiStatus == ERROR_INVALID_DATA) { // Probably REG_DWORD...
+
+ ApiStatus = NetpGetConfigDword (
+ Handle,
+ (LPTSTR) REPL_KEYWORD_ROLE, // Key wanted
+ (DWORD) -1, // default value (none!)
+ Role ); // set *Role with default or value read
+ if (ApiStatus != NO_ERROR) {
+ ReplConfigReportBadParmValue(
+ UncServerName,
+ (LPTSTR) REPL_KEYWORD_ROLE,
+ NULL );
+ goto Cleanup; // go log error
+ } else if ( !ReplIsRoleValid( *Role ) ) {
+ ReplConfigReportBadParmValue(
+ UncServerName,
+ (LPTSTR) REPL_KEYWORD_ROLE,
+ NULL );
+ ApiStatus = ERROR_INVALID_DATA;
+ goto Cleanup;
+ }
+
+ } else {
+ ReplConfigReportBadParmValue(
+ UncServerName,
+ (LPTSTR) REPL_KEYWORD_ROLE,
+ NULL );
+ goto Cleanup; // go log error
+ }
+
+ //
+ // Read the export and import paths.
+ // Unlike LM2.x, these values do not have defaults under NT.
+ //
+#define READ_PATH( keyword, output ) \
+ { \
+ ApiStatus = NetpGetConfigValue( \
+ Handle, \
+ (LPTSTR) keyword, \
+ & StringValue); \
+ if (ApiStatus == NERR_CfgParamNotFound) { \
+ ReplConfigReportBadParmValue( \
+ UncServerName, (LPTSTR) keyword, NULL ); \
+ goto Cleanup; \
+ } else if (ApiStatus == NO_ERROR) { \
+ NetpAssert( StringValue != NULL ); \
+ ApiStatus = ReplCheckAbsPathSyntax( StringValue ); \
+ if (ApiStatus == NO_ERROR) { \
+ (void) STRCPY( output, StringValue ); \
+ } else if (ApiStatus == ERROR_INVALID_DATA) { \
+ ReplConfigReportBadParmValue( \
+ UncServerName, (LPTSTR) keyword, StringValue ); \
+ goto Cleanup; \
+ } else { \
+ ReplConfigReportBadParmValue( \
+ UncServerName, (LPTSTR) keyword, StringValue ); \
+ ApiStatus = ERROR_INVALID_DATA; \
+ goto Cleanup; \
+ } \
+ (void) NetApiBufferFree( StringValue ); \
+ StringValue = NULL; \
+ } else { \
+ goto Cleanup; /* go log error */ \
+ } \
+ }
+
+ READ_PATH( REPL_KEYWORD_EXPPATH, ExportPath );
+ READ_PATH( REPL_KEYWORD_IMPPATH, ImportPath );
+
+ //
+ // Read the export and import lists.
+ //
+#define READ_LIST( keyword, default, output ) \
+ { \
+ ApiStatus = NetpGetConfigValue( \
+ Handle, \
+ (LPTSTR) keyword, \
+ & StringValue); \
+ if (ApiStatus == NERR_CfgParamNotFound) { \
+ *output = NULL; \
+ } else if (ApiStatus == NO_ERROR) { \
+ NetpAssert( output != NULL ); \
+ NetpAssert( StringValue != NULL ); \
+ if ( !ReplConfigIsListValid( StringValue ) ) { \
+ ReplConfigReportBadParmValue( \
+ UncServerName, (LPTSTR) keyword, StringValue ); \
+ ApiStatus = ERROR_INVALID_DATA; \
+ goto Cleanup; \
+ } \
+ *output = StringValue; \
+ StringValue = NULL; /* don't confuse cleanup code */ \
+ } else { \
+ goto Cleanup; /* go log error */ \
+ } \
+ }
+
+ READ_LIST( REPL_KEYWORD_EXPLIST, DEFAULT_EXPLIST, ExportList );
+ READ_LIST( REPL_KEYWORD_IMPLIST, DEFAULT_IMPLIST, ImportList );
+
+ //
+ // Read logon user name.
+ //
+ ApiStatus = NetpGetConfigValue(
+ Handle,
+ (LPTSTR) REPL_KEYWORD_LOGON,
+ & StringValue);
+ if (ApiStatus == NERR_CfgParamNotFound) {
+ SET_STRING_TO_DEFAULT( LogonUserName, DEFAULT_LOGON );
+ } else if (ApiStatus == NO_ERROR) {
+ NetpAssert( StringValue != NULL );
+ /* BUGBUG: add error check on value! */
+ (void) STRCPY( LogonUserName, StringValue );
+ (void) NetApiBufferFree( StringValue );
+ StringValue = NULL;
+ } else {
+ goto Cleanup; // go log error
+ }
+
+ //
+ // Read interval (also called SYNC)...
+ //
+ ApiStatus = NetpGetConfigDword(
+ Handle,
+ (LPTSTR) REPL_KEYWORD_INTERVAL,
+ DEFAULT_SYNC,
+ & DwordValue );
+ if (ApiStatus != NO_ERROR) {
+ ReplConfigReportBadParmValue(
+ UncServerName,
+ (LPTSTR) REPL_KEYWORD_INTERVAL,
+ NULL );
+ goto Cleanup; // go log error
+ }
+
+ if ( !ReplIsIntervalValid( DwordValue ) ) {
+ ReplConfigReportBadParmValue(
+ UncServerName,
+ (LPTSTR) REPL_KEYWORD_INTERVAL,
+ NULL );
+ ApiStatus = ERROR_INVALID_DATA;
+ goto Cleanup; // go log error
+ } else {
+ * Interval = DwordValue;
+ }
+
+ //
+ // Read pulse...
+ //
+ ApiStatus = NetpGetConfigDword(
+ Handle,
+ (LPTSTR) REPL_KEYWORD_PULSE,
+ DEFAULT_PULSE,
+ & DwordValue );
+ if (ApiStatus != NO_ERROR) {
+ ReplConfigReportBadParmValue(
+ UncServerName,
+ (LPTSTR) REPL_KEYWORD_PULSE,
+ NULL );
+ goto Cleanup; // go log error
+ }
+
+ if ( !ReplIsPulseValid( DwordValue ) ) {
+ ReplConfigReportBadParmValue(
+ UncServerName,
+ (LPTSTR) REPL_KEYWORD_PULSE,
+ NULL );
+ ApiStatus = ERROR_INVALID_DATA;
+ goto Cleanup; // go log error
+ } else {
+ * Pulse = DwordValue;
+ }
+
+ //
+ // Read guardtime...
+ //
+ ApiStatus = NetpGetConfigDword(
+ Handle,
+ (LPTSTR) REPL_KEYWORD_GUARD,
+ DEFAULT_GUARD,
+ & DwordValue );
+ if (ApiStatus != NO_ERROR) {
+ ReplConfigReportBadParmValue(
+ UncServerName,
+ (LPTSTR) REPL_KEYWORD_GUARD,
+ NULL );
+ goto Cleanup; // go log error
+ }
+ if ( !ReplIsGuardTimeValid( DwordValue ) ) {
+ ReplConfigReportBadParmValue(
+ UncServerName,
+ (LPTSTR) REPL_KEYWORD_GUARD,
+ NULL );
+
+ ApiStatus = ERROR_INVALID_DATA;
+ goto Cleanup; // go log error
+ } else if ( DwordValue > ( (*Interval) / 2 ) ) {
+ ReplConfigReportBadParmValue(
+ UncServerName,
+ (LPTSTR) REPL_KEYWORD_GUARD,
+ NULL );
+
+ ApiStatus = ERROR_INVALID_DATA;
+ goto Cleanup; // go log error
+ } else {
+ * GuardTime = DwordValue;
+ }
+
+ //
+ // Read random...
+ //
+ ApiStatus = NetpGetConfigDword(
+ Handle,
+ (LPTSTR) REPL_KEYWORD_RANDOM,
+ DEFAULT_RANDOM,
+ & DwordValue );
+ if (ApiStatus != NO_ERROR) {
+ ReplConfigReportBadParmValue(
+ UncServerName,
+ (LPTSTR) REPL_KEYWORD_RANDOM,
+ NULL );
+ goto Cleanup; // go log error
+ }
+ if ( !ReplIsRandomValid( DwordValue ) ) {
+ ReplConfigReportBadParmValue(
+ UncServerName,
+ (LPTSTR) REPL_KEYWORD_RANDOM,
+ NULL );
+ ApiStatus = ERROR_INVALID_DATA;
+ goto Cleanup; // go log error
+ } else {
+ * Random = DwordValue;
+ }
+
+ ApiStatus = NO_ERROR;
+
+ //
+ // All done.
+ //
+
+Cleanup:
+
+ if ( Handle != NULL ) {
+ NET_API_STATUS CloseStatus;
+
+ CloseStatus = NetpCloseConfigData( Handle );
+
+ // Return this error iff it is first error we've gotten.
+ if ( (ApiStatus == NO_ERROR) && (CloseStatus != NO_ERROR) ) {
+ ApiStatus = CloseStatus;
+ }
+ }
+
+ if (StringValue != NULL) {
+ (VOID) NetApiBufferFree( StringValue );
+ }
+
+ if (ApiStatus != NO_ERROR) {
+
+ // Log error on server we were trying to access.
+ ReplErrorLog(
+ UncServerName,
+ NELOG_ReplSysErr,
+ ApiStatus,
+ NULL,
+ NULL );
+ }
+
+ return (ApiStatus);
+
+} // ReplConfigRead
+
+
+
+
+
+// Write config data for the replicator.
+// Callable whether or not service is started.
+NET_API_STATUS
+ReplConfigWrite(
+ IN LPTSTR UncServerName OPTIONAL,
+ IN DWORD Role,
+ IN LPTSTR ExportPath OPTIONAL,
+ IN LPTSTR ExportList OPTIONAL,
+ IN LPTSTR ImportPath OPTIONAL,
+ IN LPTSTR ImportList OPTIONAL,
+ IN LPTSTR LogonUserName OPTIONAL,
+ IN DWORD Interval,
+ IN DWORD Pulse,
+ IN DWORD GuardTime,
+ IN DWORD Random
+ )
+{
+ NET_API_STATUS ApiStatus;
+ LPNET_CONFIG_HANDLE Handle = NULL;
+
+ //
+ // Check for caller's errors.
+ //
+ if ( !ReplIsRoleValid( Role ) ) {
+ ApiStatus = ERROR_INVALID_PARAMETER;
+ goto Cleanup; // go log error
+ }
+ if ( !ReplConfigIsRoleAllowed( UncServerName, Role ) ) {
+ ApiStatus = ERROR_INVALID_PARAMETER;
+ goto Cleanup; // go log error
+ }
+ if ( !ReplConfigIsListValid( ExportList ) ) {
+ ApiStatus = ERROR_INVALID_PARAMETER;
+ goto Cleanup; // go log error
+ }
+ if ( !ReplConfigIsListValid( ImportList ) ) {
+ ApiStatus = ERROR_INVALID_PARAMETER;
+ goto Cleanup; // go log error
+ }
+ if ( !ReplIsIntervalValid( Interval ) ) {
+ ApiStatus = ERROR_INVALID_PARAMETER;
+ goto Cleanup; // go log error
+ }
+ if ( !ReplIsPulseValid( Pulse ) ) {
+ ApiStatus = ERROR_INVALID_PARAMETER;
+ goto Cleanup; // go log error
+ }
+ if ( !ReplIsGuardTimeValid( GuardTime ) ) {
+ ApiStatus = ERROR_INVALID_PARAMETER;
+ goto Cleanup; // go log error
+ }
+ if ( !ReplIsRandomValid( Random ) ) {
+ ApiStatus = ERROR_INVALID_PARAMETER;
+ goto Cleanup; // go log error
+ }
+
+ ApiStatus = ReplCheckAbsPathSyntax( ExportPath );
+ if (ApiStatus != NO_ERROR) {
+ goto Cleanup; // go log error
+ }
+
+ ApiStatus = ReplCheckAbsPathSyntax( ImportPath );
+ if (ApiStatus != NO_ERROR) {
+ goto Cleanup; // go log error
+ }
+
+ // BUGBUG: Error check LogonUserName.
+
+ //
+ // Open the right section of the config file/whatever.
+ //
+ ApiStatus = NetpOpenConfigData(
+ & Handle,
+ UncServerName,
+ (LPTSTR) SECT_NT_REPLICATOR,
+ FALSE); // not read-only
+ if (ApiStatus != NO_ERROR) {
+ goto Cleanup; // go log error
+ }
+
+ //
+ // Write role (replicate switch).
+ // This used to be a string (e.g. "Both"), but is now a number.
+ //
+
+ ApiStatus = NetpSetConfigDword(
+ Handle,
+ (LPTSTR) REPL_KEYWORD_ROLE,
+ Role );
+ if (ApiStatus != NO_ERROR) {
+ goto Cleanup; // go log error
+ }
+
+#define WRITE_OPTIONAL_STRING( keyword, value ) \
+ { \
+ if ( ( (value)!=NULL) && ( (*(value)) != TCHAR_EOS ) ) { \
+ ApiStatus = NetpSetConfigValue( \
+ Handle, \
+ (LPTSTR) keyword, \
+ value ); \
+ if (ApiStatus != NO_ERROR) { \
+ goto Cleanup; /* go log error */ \
+ } \
+ } else { \
+ ApiStatus = NetpDeleteConfigKeyword( \
+ Handle, \
+ (LPTSTR) keyword); \
+ if ( (ApiStatus!=NO_ERROR) \
+ && (ApiStatus!=NERR_CfgParamNotFound) ) { \
+ goto Cleanup; /* go log error */ \
+ } \
+ } \
+ }
+
+ //
+ // Write export and import paths.
+ //
+
+#define WRITE_PATH( keyword, value ) \
+ WRITE_OPTIONAL_STRING( keyword, value )
+
+ WRITE_PATH( REPL_KEYWORD_EXPPATH, ExportPath );
+
+ WRITE_PATH( REPL_KEYWORD_IMPPATH, ImportPath );
+
+ //
+ // Write export and import lists.
+ //
+
+#define WRITE_LIST( keyword, value ) \
+ WRITE_OPTIONAL_STRING( keyword, value )
+
+ WRITE_LIST( REPL_KEYWORD_EXPLIST, ExportList );
+
+ WRITE_LIST( REPL_KEYWORD_IMPLIST, ImportList );
+
+ //
+ // Write logon user name.
+ //
+ WRITE_OPTIONAL_STRING( REPL_KEYWORD_LOGON, LogonUserName )
+
+ //
+ // Write DWORDs (interval, pulse, guardtime, random).
+ //
+
+/*lint -save -e767 */ // Don't complain about different definitions
+#define WRITE_DWORD( keyword, num ) \
+ { \
+ ApiStatus = NetpSetConfigDword( \
+ Handle, \
+ (LPTSTR) keyword, \
+ num ); \
+ if (ApiStatus != NO_ERROR) { \
+ goto Cleanup; /* go log error */ \
+ } \
+ }
+/*lint -restore */ // Resume checking for different macro definitions
+
+ WRITE_DWORD( REPL_KEYWORD_INTERVAL, Interval );
+
+ WRITE_DWORD( REPL_KEYWORD_PULSE, Pulse );
+
+ WRITE_DWORD( REPL_KEYWORD_GUARD, GuardTime );
+
+ WRITE_DWORD( REPL_KEYWORD_RANDOM, Random );
+
+ ApiStatus = NO_ERROR;
+
+Cleanup:
+
+ //
+ // All done.
+ //
+ if ( Handle != NULL ) {
+ NET_API_STATUS CloseStatus;
+
+ CloseStatus = NetpCloseConfigData( Handle );
+
+ // Return this error iff it is first error we've gotten.
+ if ( (ApiStatus == NO_ERROR) && (CloseStatus != NO_ERROR) ) {
+ ApiStatus = CloseStatus;
+ }
+ }
+
+ if (ApiStatus != NO_ERROR) {
+
+ // Log error on server we were trying to access.
+ ReplErrorLog(
+ UncServerName,
+ NELOG_ReplSysErr,
+ ApiStatus,
+ NULL,
+ NULL );
+ }
+
+ return (ApiStatus);
+
+} // ReplConfigWrite
diff --git a/private/net/svcdlls/repl/common/replconf.h b/private/net/svcdlls/repl/common/replconf.h
new file mode 100644
index 000000000..ec81336c6
--- /dev/null
+++ b/private/net/svcdlls/repl/common/replconf.h
@@ -0,0 +1,167 @@
+/*++
+
+Copyright (c) 1992-1993 Microsoft Corporation
+
+Module Name:
+
+ ReplConf.h
+
+Abstract:
+
+ This file contains structures, function prototypes, and definitions
+ for the replicator "config API" workers.
+
+Author:
+
+ John Rogers (JohnRo) 15-Jan-1992
+
+Environment:
+
+ User Mode - Win32
+ Portable to any flat, 32-bit environment. (Uses Win32 typedefs.)
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Notes:
+
+ You must include LmCons.h before this file.
+
+Revision History:
+
+ 15-Jan-1992 JohnRo
+ Created.
+ 20-Jan-1992 JohnRo
+ Netr prototypes are now generated by MIDL and put in repl.h.
+ 23-Jan-1992 JohnRo
+ Added ReplConfigIsLevelValid() and some prototypes.
+ 13-Feb-1992 JohnRo
+ Added ReplConfigIsApiRecordValid().
+ Added ReplConfigReportBadParmValue().
+ Clarify that certain functions can be called if service is not started.
+ 11-Mar-1992 JohnRo
+ Added support for setinfo info levels in ReplConfigIsLevelValid().
+ 24-Mar-1992 JohnRo
+ Renamed many ReplGlobal vars to ReplConfig vars.
+ 20-Jul-1992 JohnRo
+ RAID 2252: repl should prevent export on Windows/NT.
+ 14-Aug-1992 JohnRo
+ RAID 3601: repl APIs should checked import & export lists.
+ 01-Dec-1992 JohnRo
+ RAID 3844: remote NetReplSetInfo uses local machine type.
+ 05-Jan-1993 JohnRo
+ Repl WAN support (get rid of repl name list limits).
+ Made changes suggested by PC-LINT 5.0
+
+--*/
+
+
+#ifndef _REPLCONF_
+#define _REPLCONF_
+
+
+#include <netlib.h> // IN_RANGE().
+
+
+BOOL
+ReplConfigIsApiRecordValid (
+ IN DWORD Level,
+ IN LPVOID ApiRecord,
+ OUT LPDWORD ParmError OPTIONAL
+ );
+
+// BOOL
+// ReplConfigIsLevelValid(
+// IN DWORD Level, // Info level
+// IN BOOL SetInfo // Are setinfo levels allowed?
+// );
+//
+#define ReplConfigIsLevelValid(Level,SetInfo) \
+ ( \
+ /*lint -e506 */ /* don't complain about constant values here */ \
+ ( (Level) == 0 ) \
+ || ( (SetInfo) && (IN_RANGE((Level), 1000, 1003)) ) \
+ /*lint +e506 */ \
+ )
+
+
+// Indicates whether or not the syntax of a list (export or import) is
+// valid. Does no checking for the existence of items in the list.
+// Callable whether or not service is started.
+BOOL
+ReplConfigIsListValid(
+ IN LPTSTR List OPTIONAL
+ );
+
+
+// Indicates whether the given role is allowed based on the current product
+// type. Callable whether or not service is started.
+BOOL
+ReplConfigIsRoleAllowed(
+ IN LPTSTR UncServerName OPTIONAL,
+ IN DWORD Role
+ );
+
+// Callable whether or not service is started.
+NET_API_STATUS
+ReplConfigAllocAndBuildApiRecord (
+ IN DWORD Level,
+ IN DWORD Role,
+ IN LPTSTR ExportPath OPTIONAL,
+ IN LPTSTR ExportList OPTIONAL,
+ IN LPTSTR ImportPath OPTIONAL,
+ IN LPTSTR ImportList OPTIONAL,
+ IN LPTSTR LogonUserName OPTIONAL,
+ IN DWORD Interval,
+ IN DWORD Pulse,
+ IN DWORD GuardTime,
+ IN DWORD Random,
+ OUT LPBYTE * BufPtr // Alloc and set pointer.
+ );
+
+
+// Callable whether or not service is started.
+NET_API_STATUS
+ReplConfigRead(
+ IN LPTSTR UncServerName OPTIONAL,
+ OUT LPDWORD Role,
+ OUT LPTSTR ExportPath,
+ OUT LPTSTR *ExportList, // Alloc and set pointer.
+ OUT LPTSTR ImportPath,
+ OUT LPTSTR *ImportList, // Alloc and set pointer.
+ OUT LPTSTR LogonUserName,
+ OUT LPDWORD Interval,
+ OUT LPDWORD Pulse,
+ OUT LPDWORD GuardTime,
+ OUT LPDWORD Random
+ );
+
+// Routine to report a bad parm value.
+// There are two different versions of this routine,
+// in client/report.c and server/error.c
+// client/report.c is callable whether or not service is started.
+// server/error.c only runs when service is starting; it talks to the service
+// controller.
+VOID
+ReplConfigReportBadParmValue(
+ IN LPTSTR UncServerName OPTIONAL,
+ IN LPTSTR SwitchName,
+ IN LPTSTR TheValue OPTIONAL
+ );
+
+// Callable whether or not service is started.
+NET_API_STATUS
+ReplConfigWrite(
+ IN LPTSTR UncServerName OPTIONAL,
+ IN DWORD Role,
+ IN LPTSTR ExportPath OPTIONAL,
+ IN LPTSTR ExportList OPTIONAL,
+ IN LPTSTR ImportPath OPTIONAL,
+ IN LPTSTR ImportList OPTIONAL,
+ IN LPTSTR LogonUserName OPTIONAL,
+ IN DWORD Interval,
+ IN DWORD Pulse,
+ IN DWORD GuardTime,
+ IN DWORD Random
+ );
+
+
+#endif // _REPLCONF_
diff --git a/private/net/svcdlls/repl/common/repldefs.h b/private/net/svcdlls/repl/common/repldefs.h
new file mode 100644
index 000000000..8a58fd41f
--- /dev/null
+++ b/private/net/svcdlls/repl/common/repldefs.h
@@ -0,0 +1,831 @@
+/*++
+
+Copyright (c) 1987-1993 Microsoft Corporation
+
+Module Name:
+ repldefs.h
+
+Abstract:
+ Function prototypes and some global data
+
+Author:
+ Ported from Lan Man 2.x
+
+Environment:
+ Portable to any flat, 32-bit environment. (Uses Win32 typedefs.)
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+ 10/07/91 (madana)
+ ported to NT. Converted to NT style.
+ 17-Dec-1991 JohnRo
+ Use BOOL (Win32) rather than BOOLEAN (NT) where possible..
+ Replaced tabs in source file.
+ 26-Dec-1991 JohnRo
+ Added ReplIsIntegrityValid() and ReplIsExtentValid().
+ 08-Jan-1992 JohnRo
+ Added ReplIsForceLevelValid().
+ Added and revised trace bits.
+ 20-Jan-1992 JohnRo
+ Avoid lint warnings about IF_DEBUG stuff.
+ Changed ReplMain() prototype to match svc controller's needs.
+ ReportStatus() should add thread ID to status being reported.
+ Added DWORDLEN for use in config routines.
+ Temporarily enable all debug outputs.
+ Delete REPL_SECTION (use SECT_NT_REPLICATOR in config.h instead).
+ 22-Jan-1992 JohnRo
+ Added ReplRoleIncludes{Client,Master} macros.
+ Added REPL_ROLE_STOPPED (private) equate.
+ 27-Jan-1992 JohnRo
+ Added debug trace flags for DLL stubs.
+ Moved private REPL_STATE_ equates and ReplIsStateValid() to
+ <repldefs.h>. Ditto for the _RP equates and STATE_LEN.
+ Changed to use LPTSTR etc.
+ Added ReplIsRoleValid() (checks for everything but stopped).
+ Changed macro names to ReplRoleIncludes{Export,Import}.
+ 09-Feb-1992 JohnRo
+ Set up to dynamically change role.
+ 20-Feb-1992 JohnRo
+ Added support for REPL_STATE_NOT_STARTED.
+ Added Repl{Incr,Decr}LockFields().
+ Added ReplCheckAbsPathSyntax().
+ Added ReplIs{Interval,GuardTime,Pulse,Random}Valid().
+ 22-Feb-1992 JohnRo
+ Clarify that text parm to ReplFinish() is optional.
+ 26-Feb-1992 JohnRo
+ Added ReplLockFieldsAreValid() macro.
+ Created ReplIgnoreDirOrFileName().
+ 27-Feb-1992 JohnRo
+ Changed state not started to state never replicated.
+ 05-Mar-1992 JohnRo
+ Changed ReplMain's interface to match new service controller.
+ Cranked-down max tree depth for testing only.
+ 13-Mar-1992 JohnRo
+ Fixed bug handling tree depth.
+ Wait hint is in milliseconds for new service controller.
+ 15-Mar-1992 JohnRo
+ Put back IF_DEBUG() the way it belongs.
+ 24-Mar-1992 JohnRo
+ Got rid of useless master and client termination codes.
+ Use integrity and extent equates in <lmrepl.h>.
+ Modify REPL$ share handling.
+ Put tree depth back to normal max value.
+ 25-Mar-1992 JohnRo
+ Avoid obsolete state values.
+ 09-Apr-1992 JohnRo
+ Prepare for WCHAR.H (_wcsicmp vs _wcscmpi, etc).
+ 15-Jul-1992 JohnRo
+ RAID 10503: srv mgr: repl dialog doesn't come up.
+ Avoid compiler warnings.
+ 11-Aug-1992 JohnRo
+ RAID 3288: repl svc should preserve ACLs on copy.
+ 01-Sep-1992 JohnRo
+ RAID 3721: repl svc does not stop with shutdown msg.
+ 04-Dec-1992 JohnRo
+ Repl should use filesystem change notify.
+ Added trace bits for file find routines and checksum routines.
+ Made changes suggested by PC-LINT 5.0
+ 08-Dec-1992 JohnRo
+ RAID 3316: access violation while stopping the replicator
+ 17-Dec-1992 JohnRo
+ RAID 1513: Repl does not maintain ACLs. (Also fix HPFS->FAT timestamp.)
+ Made changes suggested by PC-LINT 5.0
+ 05-Jan-1993 JohnRo
+ Repl WAN support (get rid of repl name list limits).
+ 13-Jan-1993 JohnRo
+ RAID 7053: locked trees added to pulse msg. (Actually fix all
+ kinds of remote lock handling.)
+ 15-Jan-1993 JohnRo
+ RAID 7717: Repl assert if not logged on correctly. (Also do event
+ logging for real.)
+ 22-Jan-1993 JohnRo
+ RAID 7983: Repl svc needs to change process token to really copy ACLs.
+ 17-Feb-1993 JohnRo
+ RAID 11365: Fixed various repl mailslot problems.
+ Also get rid of obsolete default import/export path equates.
+ Made PC-LINT changes for free build.
+ 25-Jan-1993 JohnRo
+ RAID 12237: replicator tree depth exceeded.
+ 30-Mar-1993 JohnRo
+ Reduce checksum frequency by using change notify on import tree.
+ 06-Apr-1993 JohnRo
+ RAID 1938: Replicator un-ACLs files when not given enough permission.
+ 21-Apr-1993 JohnRo
+ RAID 7313: repl needs change permission to work on NTFS, or we need
+ to delete files differently.
+ 07-May-1993 JohnRo
+ RAID 3258: file not updated due to ERROR_INVALID_USER_BUFFER.
+ 21-May-1993 JohnRo
+ RAID 11103: repl svc doesn't handle time being set back.
+ 10-Jun-1993 JohnRo
+ RAID 13080: Allow repl between different timezones.
+ 24-Aug-1993 JohnRo
+ RAID 788 (was 16419): Repl: spaces in computer names do not work again.
+
+--*/
+
+
+#ifndef _REPLDEFS_
+#define _REPLDEFS_
+
+
+// Don't complain about "unneeded" includes of these files:
+/*lint -efile(764,iniparm.h,lmrepl.h,netlib.h,windows.h) */
+/*lint -efile(766,iniparm.h,lmrepl.h,netlib.h,windows.h) */
+#include <windows.h> // PSECURITY_ATTRIBUTES.
+#include <iniparm.h> // MIN_SYNC, etc.
+#include <lmrepl.h> // REPL_EXTENT_FILE, etc.
+#include <netlib.h> // IN_RANGE().
+
+
+//
+// Conditional compilation options:
+//
+
+#define USE_BACKUP_APIS
+
+// Uncomment this out when GetFileSecurity() stops returning
+// ERROR_BAD_DESCRIPTOR_FORMAT for UNC names.
+#define USE_UNC_GETFILESEC
+
+
+
+//
+// Mailslot names and related lengths.
+//
+
+#define MASTER_SLOT_NAME TEXT("\\MAILSLOT\\NET\\REPL_MAS")
+#define CLIENT_SLOT_NAME TEXT("\\MAILSLOT\\NET\\REPL_CLI")
+
+// Length of CLIENT_SLOT_NAME in TCHARs, including the null char.
+#define CLIENT_SLOT_NAME_LEN ( sizeof(CLIENT_SLOT_NAME) / sizeof(TCHAR) )
+
+#define MAX_2_MSLOT_SIZE (441 - (CLIENT_SLOT_NAME_LEN + CNLEN + 1))
+
+// Length of full client slot name (including null char) in TCHARs.
+#define FULL_SLOT_NAME_SIZE (CLIENT_SLOT_NAME_LEN + CNLEN + 1 + 2)
+
+// Minimum size of DosFindFirst2 EaSize, for cbList field.
+#define EA_MIN_SIZE 4
+
+
+//
+// Various other constants.
+//
+
+#define RADIX 10
+
+#define SLASH (LPTSTR) TEXT("\\")
+#define SLASH_DOT (LPTSTR) TEXT("\\.")
+#define SLASH_DOT_DOT (LPTSTR) TEXT("\\..")
+#define SLASH_SLASH (LPTSTR) TEXT("\\\\")
+#define STAR_DOT_STAR (LPTSTR) TEXT("*.*")
+#define DOT (LPTSTR) TEXT(".")
+#define DOT_DOT (LPTSTR) TEXT("..")
+
+
+// List of separator chars we allow between names in import and export lists.
+#define REPL_LIST_DELIMITER_STR ((LPTSTR) TEXT("\t;,"))
+
+
+// Define a value for unknown file system time resolution.
+#define UNKNOWN_FS_RESOLUTION ( (DWORD) 0 )
+
+
+// DWORDLEN: DWORD takes this many decimal digits to store.
+// BUGBUG This assumes that DWORD is 32-bits or less.
+#define DWORDLEN 10
+
+
+//
+// Service controller wait hints.
+// These values are in milliseconds (formerly tenths of a second under LM 2.x).
+// Max usable wait hint is 255 tenths of a second (25.5 seconds).
+// NET.EXE's use of NetService APIs means we are stuck with this limit for now.
+//
+#define MAX_USABLE_WAIT_HINT (25500)
+
+// Service controller status wait hint. We use this for starting and
+// stopping.
+#define REPL_WAIT_HINT MAX_USABLE_WAIT_HINT
+
+
+//
+// Time might go backwards (because of syncing the clock or somebody changing
+// the date and time), so define how much of this we can ignore. This value
+// is in seconds. Since interval times are given in minutes, this value should
+// be over a minute. 110 seconds is a first guess (almost 2 minutes).
+//
+#define BACKWARDS_TIME_CHNG_ALLOWED_SECS 110
+
+
+//
+// AlertLogExit codes.
+//
+#define EXIT 1
+#define NO_EXIT 0
+
+//
+// Message types.
+//
+
+// Sync messages
+#define SYNC_MSG 1
+#define GUARD_MSG 2
+#define PULSE_MSG 3
+
+// Query messages
+#define IS_DIR_SUPPORTED 11
+#define IS_MASTER 12
+#define DIR_NOT_SUPPORTED 13
+#define DIR_SUPPORTED 14
+#define NOT_MASTER_DIR 15
+#define MASTER_DIR 16
+#define DIR_COLLIDE 17
+
+// Internal pseudo-messages
+#define GUARD_TIMEOUT 20
+#define DUPL_TIMEOUT 21
+#define PULSE_1_TIMEOUT 22
+#define PULSE_2_TIMEOUT 23
+#define PULSE_3_TIMEOUT 24
+#define NEXT_AVAILABLE_MESSAGE_NUMBER 25
+
+
+// Update opcodes.
+#define START 1
+#define UPDATE 2
+#define END 3
+#define PULSE 4
+#define GUARD 5
+
+
+// Security information to copy with the files and directories.
+// BUGBUG: Add SACL to this? Add other items when NT supports them!
+#define REPL_SECURITY_TO_COPY \
+ ( OWNER_SECURITY_INFORMATION \
+ | GROUP_SECURITY_INFORMATION \
+ | DACL_SECURITY_INFORMATION )
+
+
+
+#define REPL_SHARE (LPTSTR) TEXT("REPL$")
+
+#define REPL_INI (LPTSTR) TEXT("REPL.INI")
+
+// Unlike downlevel, we represent one or more user locks by just one file.
+// The lock count is kept in the registry.
+#define USERLOCK_NT (LPTSTR) TEXT("USERLOCK.NT$")
+
+
+#define NT_MSG_TOKEN 0xFFFFFFFF
+
+
+//
+// These former "signal file" names are now used as state strings in the
+// config data for each directory.
+//
+#define OK_RP (LPTSTR) TEXT("OK.RP$")
+#define NO_MASTER_RP (LPTSTR) TEXT("NO_MASTR.RP$")
+#define NO_SYNC_RP (LPTSTR) TEXT("NO_SYNC.RP$")
+#define NEVER_REPLICATED_RP (LPTSTR) TEXT("NEVERREP.RP$") /* Added for APIs. */
+
+//
+// Max len of the above strings. Since they were FAT file names (8.3), then
+// the max len (in chars) is 8 + 1 (dot) + 3 (extension).
+//
+#define STATE_LEN (8+1+3)
+
+
+//
+// Debug Definititions
+//
+
+#define REPL_DEBUG_REPL 0x00000001 // General debugging
+#define REPL_DEBUG_MAJOR 0x00000002 // Major events
+
+#define REPL_DEBUG_CLIENT 0x00000010 // Debug the Client
+#define REPL_DEBUG_WATCHD 0x00000020 // Debug the Watchdog thread
+#define REPL_DEBUG_SYNC 0x00000040 // Debug the Syncer thread
+
+#define REPL_DEBUG_MASTER 0x00000100 // Debug the Master
+#define REPL_DEBUG_PULSER 0x00000200 // Debug the Master
+#define REPL_DEBUG_COPYTIME 0x00000800 // Debug the time copy routines.
+
+#define REPL_DEBUG_EXPAPI 0x00010000 // Debug export API routines
+#define REPL_DEBUG_IMPAPI 0x00020000 // Debug import API routines
+#define REPL_DEBUG_REPLAPI 0x00040000 // Debug repl API routines
+
+#define REPL_DEBUG_DIRNAME 0x01000000 // Debug dirname helpers
+#define REPL_DEBUG_DLLSTUB 0x02000000 // DLL stubs.
+#define REPL_DEBUG_RPCBIND 0x04000000 // RPC bind/unbind routines.
+#define REPL_DEBUG_SVCCTRL 0x08000000 // Service controller stuff.
+#define REPL_DEBUG_FILEFIND 0x10000000 // File find routines.
+#define REPL_DEBUG_CHECKSUM 0x20000000 // Checksum routines.
+#define REPL_DEBUG_CHNGNOT 0x40000000 // Change notify routines.
+
+#define REPL_DEBUG_STALLER 0x80000000 // Staller thread stuff.
+#define REPL_DEBUG_STOPPER REPL_DEBUG_STALLER // Stopper thread too.
+
+#define REPL_DEBUG_ALL ((DWORD) -1) // All possible debug bits.
+
+
+
+// Private role (in addition to REPL_ROLE_ equates in LmRepl.h).
+#define REPL_ROLE_STOPPED ( (DWORD) 'S' ) // arbitrary value
+
+
+#undef IF_DEBUG
+
+#if DBG
+
+extern DWORD ReplGlobalTrace;
+
+#define IF_DEBUG(Function) if (ReplGlobalTrace & REPL_DEBUG_ ## Function)
+
+#else
+
+#define IF_DEBUG(Function) \
+ /*lint -save -e506 */ /* don't complain about constant values here */ \
+ if (FALSE) \
+ /*lint -restore */
+
+#endif // DBG
+
+//
+// ReplNetNameCompare
+//
+
+#ifdef UNICODE
+
+#define ReplNetNameCompare(_a, _b, _c, _e, _f ) \
+ I_NetNameCompare((_a), (_b), (_c), (_e), (_f) )
+
+#define ReplNetPathCompare(_a, _b, _c, _e, _f ) \
+ I_NetPathCompare((_a), (_b), (_c), (_e), (_f) )
+
+#else // UNICODE
+
+#define ReplNetNameCompare(_a, _b, _c, _e, _f ) \
+ stricmp( (_b), (_c) )
+
+#define ReplNetPathCompare(_a, _b, _c, _e, _f ) \
+ stricmp( (_b), (_c) )
+
+#endif // UNICODE
+
+//
+// S T R U C T U R E S
+//
+
+
+//
+// Definition of the checksum of a particular directory tree.
+//
+
+struct checksum_rec {
+ DWORD checksum;
+ DWORD count;
+};
+
+typedef struct checksum_rec CHECKSUM_REC;
+typedef struct checksum_rec * PCHECKSUM_REC;
+typedef struct checksum_rec * LPCHECKSUM_REC;
+
+// MESSAGE STRUCTURES.
+
+struct msg_header {
+ DWORD msg_type;
+ TCHAR sender[CNLEN+1];
+ TCHAR senders_domain[DNLEN+1];
+};
+
+typedef struct msg_header MSG_HEADER;
+typedef struct msg_header *PMSG_HEADER;
+typedef struct msg_header *LPMSG_HEADER;
+
+struct status_rec {
+ TCHAR dir_name[PATHLEN];
+ DWORD opcode; // 1 - start, 2 - update, 3 - end.
+ DWORD checksum;
+ DWORD count;
+ DWORD integrity;
+ DWORD extent;
+};
+
+typedef struct status_rec STATUS_REC;
+typedef struct status_rec *PSTATUS_REC;
+typedef struct status_rec *LPSTATUS_REC;
+
+struct msg_status_rec {
+ DWORD dir_name_offset; // offset where name string is
+ // from msg buffer start
+
+ DWORD opcode; // 1 - start, 2 - update, 3 - end.
+ DWORD checksum;
+ DWORD count;
+ DWORD integrity;
+ DWORD extent;
+
+};
+
+typedef struct msg_status_rec MSG_STATUS_REC;
+typedef struct msg_status_rec *PMSG_STATUS_REC;
+typedef struct msg_status_rec *LPMSG_STATUS_REC;
+
+struct repl_info {
+ DWORD random;
+ DWORD sync_rate;
+ DWORD pulse_rate;
+ DWORD guard_time;
+};
+
+typedef struct repl_info REPL_INFO;
+typedef struct repl_info *PREPL_INFO;
+typedef struct repl_info *LPREPL_INFO;
+
+struct query_msg {
+ MSG_HEADER header;
+ TCHAR dir_name[PATHLEN]; // ASCIIZ dir/tree name.
+};
+
+typedef struct query_msg QUERY_MSG;
+typedef struct query_msg *PQUERY_MSG;
+typedef struct query_msg *LPQUERY_MSG;
+
+struct sync_msg {
+ MSG_HEADER header;
+ REPL_INFO info;
+ DWORD update_count;
+
+ //
+ // here come update_count msg_status_rec records, the file name strings
+ // stuffed at the end of the buffer by NetPackStr, dir_name is the
+ // offset from the start of the message.
+ //
+
+};
+
+typedef struct sync_msg SYNCMSG;
+typedef struct sync_msg *PSYNCMSG;
+typedef struct sync_msg *LPSYNCMSG;
+
+//
+// Global procedure definitions
+//
+
+VOID
+AlertLogExit(
+ IN NET_API_STATUS alert_code,
+ IN NET_API_STATUS errlog_code,
+ IN NET_API_STATUS sys_code,
+ IN LPTSTR str1,
+ IN LPTSTR str2,
+ IN BOOL exit_flag
+ );
+
+VOID
+ReportStatus(
+ IN DWORD State,
+ IN NET_API_STATUS ApiStatus,
+ IN DWORD WaitHint,
+ IN DWORD CheckPoint
+ );
+
+VOID
+ReplFinish (
+ IN NET_API_STATUS ApiStatus
+ );
+
+VOID
+ScanTree(
+ IN LONG MasterTimeZoneOffsetSecs, // exporter offset from GMT
+ IN OUT LPTSTR Path, // path plus scratch space (to PATHLEN+1)
+ OUT PCHECKSUM_REC ScanRec
+ );
+
+VOID
+ScanDir(
+ IN LONG MasterTimeZoneOffsetSecs, // exporter offset from GMT
+ IN OUT LPTSTR Path, // path plus scratch space (to PATHLEN+1)
+ OUT PCHECKSUM_REC ScanRec,
+ IN DWORD Flag
+ );
+
+NET_API_STATUS
+Parse(
+ IN DWORD argc,
+ IN LPTSTR argv[]
+ );
+
+VOID
+ReplMain(
+ IN DWORD dwNumServicesArgs,
+ IN LPTSTR *lpServiceArgVectors
+ );
+
+VOID
+InitParseData(
+ VOID
+ );
+
+VOID
+FreeParseData(
+ VOID
+ );
+
+DWORD
+ReplMasterMain(
+ IN LPVOID parm // NULL if service is starting; non-NULL if role is changing
+ );
+
+// BOOL
+// ReplAreLockFieldsValid (
+// IN DWORD LockCount,
+// IN DWORD LockTime
+// );
+//
+#define ReplAreLockFieldsValid(LockCount,LockTime) \
+ ( ( ((LockCount)!=0) && ((LockTime)!=0) ) \
+ || ( ((LockCount)==0) && ((LockTime)==0) ) )
+
+NET_API_STATUS
+ReplChangeRole(
+ IN DWORD NewRole
+ );
+
+NET_API_STATUS
+ReplCheckAbsPathSyntax (
+ IN LPTSTR AbsPath
+ );
+
+DWORD
+ReplClientMain(
+ IN LPVOID parm // NULL if service is starting; non-NULL if role is changing
+ );
+
+NET_API_STATUS
+ReplCopyDirectoryItself(
+ IN LPCTSTR SourcePath,
+ IN LPCTSTR DestPath,
+ IN BOOL bFailIfExists
+ );
+
+NET_API_STATUS
+ReplCopyFile(
+ IN LPCTSTR SourcePath,
+ IN LPCTSTR DestPath,
+ IN BOOL bFailIfExists
+ );
+
+VOID
+ReplCopyJustDateTime(
+ IN LPCTSTR SourcePath,
+ IN LPCTSTR DestPath,
+ IN BOOL IsDirectory
+ );
+
+NET_API_STATUS
+ReplCopySecurity(
+ IN LPCTSTR SourcePath,
+ IN LPCTSTR DestPath
+ );
+
+NET_API_STATUS
+ReplDecrLockFields (
+ IN OUT LPDWORD LockCountPtr,
+ IN OUT LPDWORD LockTimePtr,
+ IN DWORD UnlockForce // Use REPL_UNLOCK_FORCE or REPL_UNLOCK_NOFORCE.
+ );
+
+NET_API_STATUS
+ReplDeleteFile(
+ IN LPCTSTR FileName
+ );
+
+NET_API_STATUS
+ReplDisableBackupPermission(
+ VOID
+ );
+
+// Callable even if the replicator service is not started.
+NET_API_STATUS
+ReplDoUserLockFilesExist(
+ IN LPCTSTR Path, // May be absolute path (with drive) or UNC path.
+ OUT LPBOOL IsLockedPtr
+ );
+
+NET_API_STATUS
+ReplEnableBackupPermission(
+ VOID
+ );
+
+VOID
+ReplErrorLog(
+ IN LPCTSTR UncServerName OPTIONAL,
+ IN NET_API_STATUS l_code,
+ IN NET_API_STATUS s_code,
+ IN LPTSTR str1 OPTIONAL,
+ IN LPTSTR str2 OPTIONAL
+ );
+
+BOOL
+ReplFileOrDirExists(
+ IN LPCTSTR FileName
+ );
+
+DWORD
+ReplGetEaSize(
+ IN LPCTSTR Path
+ );
+
+BOOL
+ReplIsFileTimeCloseEnough(
+ IN LPVOID MasterFileTime, // Points to a FILETIME value.
+ IN LPVOID ClientFileTime // Points to a FILETIME value.
+ );
+
+// Returns UNKNOWN_FS_RESOLUTION on error.
+DWORD
+ReplGetFsTimeResolutionSecs(
+ IN LPCTSTR AbsPath
+ );
+
+BOOL
+ReplIgnoreDirOrFileName (
+ IN LPTSTR Name
+ );
+
+NET_API_STATUS
+ReplIncrLockFields (
+ IN OUT LPDWORD LockCountPtr,
+ IN OUT LPDWORD LockTimePtr
+ );
+
+NET_API_STATUS
+ReplInitAnyList(
+ IN LPCTSTR UncanonList OPTIONAL,
+ IN OUT LPTSTR ** NameListPtr, // Allocated by this routine (or set to NULL).
+ IN LPCTSTR ConfigKeywordName,
+ OUT LPDWORD EntryCount
+ );
+
+NET_API_STATUS
+ReplInitBackupPermission(
+ VOID
+ );
+
+// BOOL
+// ReplIsExtentValid(
+// IN DWORD parm
+// );
+//
+#define ReplIsExtentValid(parm) \
+ ( ((parm) == REPL_EXTENT_FILE) || ((parm) == REPL_EXTENT_TREE) )
+
+// BOOL
+// ReplIsForceLevelValid(
+// IN DWORD Force
+// );
+//
+#define ReplIsForceLevelValid(Force) \
+ ( ((Force) == REPL_UNLOCK_FORCE) || ((Force) == REPL_UNLOCK_NOFORCE) )
+
+// BOOL
+// ReplIsGuardTimeValid(
+// IN DWORD parm
+// );
+//
+#if MIN_GUARD // low bound > 0
+#define ReplIsGuardTimeValid(parm) \
+ ( IN_RANGE( (parm), MIN_GUARD, MAX_GUARD ) )
+#else // MIN_GUARD is zero.
+#define ReplIsGuardTimeValid(parm) \
+ ( (parm) <= MAX_GUARD )
+#endif
+
+// BOOL
+// ReplIsIntegrityValid(
+// IN DWORD parm
+// );
+//
+#define ReplIsIntegrityValid(parm) \
+ ( ((parm) == REPL_INTEGRITY_FILE) || ((parm) == REPL_INTEGRITY_TREE) )
+
+// BOOL
+// ReplIsIntervalValid(
+// IN DWORD parm
+// );
+//
+#define ReplIsIntervalValid(parm) \
+ ( IN_RANGE((parm), MIN_SYNC, MAX_SYNC) )
+
+// BOOL
+// ReplIsPulseValid(
+// IN DWORD parm
+// );
+//
+#define ReplIsPulseValid(parm) \
+ ( IN_RANGE(parm, MIN_PULSE, MAX_PULSE) )
+
+// BOOL
+// ReplIsRandomValid(
+// IN DWORD parm
+// );
+//
+#define ReplIsRandomValid(parm) \
+ ( IN_RANGE((parm), MIN_RANDOM, MAX_RANDOM) )
+
+// BOOL
+// ReplIsRoleValid(
+// IN DWORD role
+// );
+//
+// Check for valid roles (not including stopped, which is for internal use).
+//
+#define ReplIsRoleValid(role) \
+ ( ((role) == REPL_ROLE_BOTH) \
+ || ((role) == REPL_ROLE_EXPORT) \
+ || ((role) == REPL_ROLE_IMPORT) )
+
+
+// Check for valid state. This includes the "private" state values.
+#define ReplIsStateValid( State ) \
+ ( ((State)==REPL_STATE_OK) \
+ || ((State)==REPL_STATE_NO_MASTER) \
+ || ((State)==REPL_STATE_NO_SYNC) \
+ || ((State)==REPL_STATE_NEVER_REPLICATED) )
+
+NET_API_STATUS
+ReplMakeSecurityAttributes(
+ IN LPCTSTR SourcePath,
+ OUT PSECURITY_ATTRIBUTES * DestSecurityAttrPtr // alloc and set ptr
+ );
+
+NET_API_STATUS
+ReplMarshallQueryMsg(
+ IN PBYTE InBuf,
+ OUT PBYTE OutBuf,
+ IN LPDWORD BytesRead
+ );
+
+
+// BOOL
+// ReplRoleIncludesExport(role)
+// IN DWORD Role
+// );
+#define ReplRoleIncludesExport(role) \
+ ( ((role) == REPL_ROLE_EXPORT) || ((role) == REPL_ROLE_BOTH) )
+
+
+// BOOL
+// ReplRoleIncludesImport(role)
+// IN DWORD Role
+// );
+#define ReplRoleIncludesImport(role) \
+ ( ((role) == REPL_ROLE_IMPORT) || ((role) == REPL_ROLE_BOTH) )
+
+DWORD
+ReplStaller(
+ IN LPVOID Parm
+ );
+
+DWORD
+ReplStopper(
+ IN LPVOID Parm
+ );
+
+VOID
+ReplStopService(
+ VOID
+ );
+
+NET_API_STATUS
+ReplUnmarshallMessage(
+ IN PBYTE InBuf,
+ IN DWORD InBufLen,
+ OUT PBYTE OutBuf,
+ IN DWORD OutBufLen,
+ OUT LPDWORD BytesRead
+ );
+
+NET_API_STATUS
+ReplUnmarshallSyncMsg(
+ IN PBYTE InBuf,
+ IN DWORD InBufLen,
+ OUT PBYTE OutBuf,
+ IN DWORD OutBufLen,
+ OUT LPDWORD BytesRead
+ );
+
+NET_API_STATUS
+ReplUnmarshallQueryMsg(
+ IN PBYTE InBuf,
+ IN DWORD InBufLen,
+ OUT PBYTE OutBuf,
+ IN DWORD OutBufLen,
+ OUT LPDWORD BytesRead
+ );
+
+
+#endif // _REPLDEFS_
diff --git a/private/net/svcdlls/repl/common/replerr.c b/private/net/svcdlls/repl/common/replerr.c
new file mode 100644
index 000000000..510fe5161
--- /dev/null
+++ b/private/net/svcdlls/repl/common/replerr.c
@@ -0,0 +1,298 @@
+/*++
+
+Copyright (c) 1987-1993 Microsoft Corporation
+
+Module Name:
+
+ ReplErr.c
+
+Abstract:
+
+ Contains one routine for error handling: ReplErrorLog().
+
+ This routine is shared by the APIs (client side) and the service itself.
+
+Author:
+
+ JR (John Rogers, JohnRo@Microsoft) 21-Jan-1993
+ (Actually, just massive rework of LM 2.x code via MadanA and RitaW.)
+
+Environment:
+
+ User mode only.
+ Uses Win32 stuff: ReportEvent(), etc.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+ 21-Jan-1993 JohnRo
+ Created for RAID 7717: Repl assert if not logged on correctly. (Also
+ do event logging for real.)
+ 15-Feb-1993 JohnRo
+ RAID 10685: user name not in repl event log.
+ 13-May-1993 JohnRo
+ RAID 9741: Replicator logs same event over and over.
+ Changed debug bit to MAJOR for this.
+ Changed debug output on errors to be unconditional.
+
+--*/
+
+
+// These must be included first:
+
+#define NOMINMAX // Let stdlib.h define min() and max()
+#include <windows.h> // LocalFree(), ReportEvent(), etc.
+#include <lmcons.h>
+
+// These may be included in any order:
+
+#include <icanon.h> // NetpIsRemote(), ISREMOTE, etc.
+#include <lmsname.h> // SERVICE_REPL.
+#include <netdebug.h> // DBGSTATIC, NetpKdPrint(), FORMAT_ equates, etc.
+#include <netlib.h> // NetpGetUserSid().
+#include <prefix.h> // PREFIX_ equates.
+#include <repldefs.h> // My prototypes.
+#include <tstr.h> // ULTOA(). (includes stdlib.h)
+
+
+//
+// Static values which we use to prevent logging the same error code over
+// and over.
+// BUGBUG: Add server name, str1, str2 static variables someday.
+//
+
+DBGSTATIC NET_API_STATUS ReplLastLogCode = NO_ERROR;
+DBGSTATIC NET_API_STATUS ReplLastApiStatus = NO_ERROR;
+
+
+VOID
+ReplErrorLog(
+ IN LPCTSTR UncServerName OPTIONAL,
+ IN NET_API_STATUS LogCode,
+ IN NET_API_STATUS StatusCode,
+ IN LPTSTR str1 OPTIONAL,
+ IN LPTSTR str2 OPTIONAL
+ )
+/*++
+
+Routine Description:
+
+ Writes entry into event log.
+
+ This is a recursive routine. It writes a message remotely and then
+ recursively calls itself to copy the message locally.
+
+Arguments:
+
+ UncServerName - Name of server on which to log this error.
+
+ LogCode - Error log msg code (must appear in lmerrlog.h).
+
+ StatusCode - In case of system / network error, the code. Should be zero
+ if not needed for message.
+
+ str1 - Merge string for msg. Should be NULL if not needed for message.
+
+ str2 - Merge string for msg. Should be NULL if not needed for message.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ NET_API_STATUS ApiStatus;
+ TCHAR ErrCodeString[DWORDLEN+1]; // Long enough for any status.
+ HANDLE LogHandle = NULL;
+ LPTSTR StringArray[3]; // Space for ptrs to err code, str1, str2.
+ WORD StringCount = 0; // Num of ptrs in StringArray (so far).
+ BOOL SuccessFlag;
+ PSID UserSid = NULL;
+
+
+ IF_DEBUG( MAJOR ) {
+ NetpKdPrint(( PREFIX_REPL
+ "Error log message: " FORMAT_API_STATUS, LogCode ));
+
+ }
+
+ //
+ // Is this a redundant event log?
+ //
+ // To make life easier on ourselves,
+ // we only avoid redundant logs of local events with no optional strings.
+ // That covers the common cases well (like NELOG_ReplSysErr).
+ // And we don't have to worry about allocating/deallocating the strings
+ // either.
+ //
+
+ if ( (LogCode==ReplLastLogCode)
+ && (StatusCode==ReplLastApiStatus)
+ && (UncServerName==NULL)
+ && (str1==NULL)
+ && (str2==NULL) ) {
+
+ goto Cleanup; // Yes, this is redundant.
+ }
+
+ ReplLastLogCode = LogCode;
+ ReplLastApiStatus = StatusCode;
+
+ //
+ // Is sys/net error specified? If so, convert to string and store
+ // pointer in StringArray.
+ //
+
+ if (StatusCode != 0) {
+
+ (VOID) ULTOA(StatusCode, ErrCodeString, RADIX);
+
+ StringArray[StringCount] = ErrCodeString;
+ ++StringCount;
+
+ IF_DEBUG( MAJOR ) {
+ NetpKdPrint(( " " FORMAT_API_STATUS, StatusCode ));
+ }
+ }
+
+ //
+ // Now take care of (optional) char strings.
+ //
+
+ if (str1 != NULL) {
+ StringArray[StringCount] = str1;
+ ++StringCount;
+
+ IF_DEBUG( MAJOR ) {
+ NetpKdPrint(( " " FORMAT_LPTSTR, str1 ));
+ }
+ }
+ if (str2 != NULL) {
+ StringArray[StringCount] = str2;
+ ++StringCount;
+
+ IF_DEBUG( MAJOR ) {
+ NetpKdPrint(( " " FORMAT_LPTSTR, str2 ));
+ }
+ }
+
+ IF_DEBUG( MAJOR ) {
+ NetpKdPrint(( "\n" ));
+ }
+
+ NetpAssert( StringCount <= 3 ); // Update StringArray size if not.
+
+ //
+ // Get event log handle using out service name as source.
+ //
+
+ LogHandle = RegisterEventSource(
+ (LPTSTR) UncServerName,
+ (LPTSTR) SERVICE_REPL );
+
+ if (LogHandle == NULL) {
+ NetpKdPrint(( PREFIX_REPL "RegisterEventSource FAILED! status: "
+ FORMAT_API_STATUS "\n",
+ (NET_API_STATUS) GetLastError() ));
+ // Nothing to do; we can't log the fact that we can't log an error!
+ goto Cleanup;
+ }
+
+ //
+ // Get SID of user which invoked this thread, if applicable.
+ //
+
+ ApiStatus = NetpGetUserSid(
+ &UserSid ); // alloc and set ptr (free with LocalFree).
+
+ if (ApiStatus != NO_ERROR) {
+ NetpKdPrint(( PREFIX_REPL
+ "ReplErrorLog: Can't get SID for user, status="
+ FORMAT_API_STATUS "...\n", ApiStatus ));
+
+ // Log without user ID is better than none, so continue...
+ UserSid = NULL;
+ }
+
+ //
+ // Log the error code specified.
+ //
+
+ SuccessFlag = ReportEvent(
+ LogHandle,
+ EVENTLOG_ERROR_TYPE,
+ 0, // Event category.
+ LogCode, // Message ID.
+ UserSid,
+ StringCount,
+ 0, // Zero bytes of raw data.
+ StringArray, // Pointer to array of ptrs to insert strings.
+ (PVOID) NULL ); // No pointer to raw data.
+
+ if ( !SuccessFlag ) {
+
+ ApiStatus = (NET_API_STATUS) GetLastError();
+ NetpAssert( ApiStatus != NO_ERROR );
+
+ NetpKdPrint(( PREFIX_REPL
+ "FAILED ReportEvent call, status " FORMAT_API_STATUS ".\n",
+ ApiStatus ));
+
+ // Not much else we can do but continue...
+ }
+
+ //
+ // We're done with the event log handle.
+ //
+
+ SuccessFlag = DeregisterEventSource(LogHandle);
+
+ if ( !SuccessFlag ) {
+
+ ApiStatus = (NET_API_STATUS) GetLastError();
+ NetpAssert( ApiStatus != NO_ERROR );
+
+ NetpKdPrint(( PREFIX_REPL
+ "FAILED DeregisterEventSource call, status "
+ FORMAT_API_STATUS ".\n",
+ ApiStatus ));
+
+ // Not much else we can do but continue...
+ }
+
+ //
+ // If we were doing a remote message, then log a copy locally.
+ //
+ if ( (UncServerName!=NULL) && (UncServerName[0]!=TCHAR_EOS) ) {
+
+ DWORD Location;
+
+ ApiStatus = NetpIsRemote(
+ (LPTSTR) UncServerName, // input: uncanon name
+ & Location, // output: local or remote flag
+ NULL, // don't need canon name output
+ 0); // flags: normal
+
+ if ( (ApiStatus!=NO_ERROR) || (Location==(DWORD)ISREMOTE) ) {
+
+ // RECURSIVE CALL: Log error locally too, just in case.
+ ReplErrorLog(
+ NULL, // No server name this time.
+ LogCode,
+ StatusCode,
+ str1,
+ str2 );
+ }
+ }
+
+ //
+ // Clean up...
+ //
+
+Cleanup:
+
+ if (UserSid != NULL) {
+ (VOID) LocalFree( UserSid );
+ }
+
+}
diff --git a/private/net/svcdlls/repl/common/replgbl.h b/private/net/svcdlls/repl/common/replgbl.h
new file mode 100644
index 000000000..44d99c8bd
--- /dev/null
+++ b/private/net/svcdlls/repl/common/replgbl.h
@@ -0,0 +1,139 @@
+/*++
+
+Copyright (c) 1987-1993 Microsoft Corporation
+
+Module Name:
+
+ ReplGbl.h
+
+Abstract:
+
+ Constants and some global data definition.
+
+Author:
+
+ Ported from Lan Man 2.x
+
+Environment:
+
+ Contains NT-specific code.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Notes:
+
+ The caller must include <lmcons.h> before this file.
+
+Revision History:
+
+ 10/28/91 (madana)
+ ported to NT. Converted to NT style.
+ 20-Jan-1992 JohnRo
+ Changed file name from repl.h to replgbl.h to avoid MIDL conflict.
+ More changes suggested by PC-LINT.
+ 22-Jan-1992 JohnRo
+ Moved current role from P_repl_sw to ReplGlobalRole.
+ 24-Jan-1992 JohnRo
+ Changed to use LPTSTR etc.
+ Added ReplGlobal variables corresponding to the old P_ variables.
+ 09-Feb-1992 JohnRo
+ Set up to dynamically change role.
+ 05-Mar-1992 JohnRo
+ Changed ReplMain's interface to match new service controller.
+ 06-Mar-1992 JohnRo
+ Avoid starting RPC server too soon.
+ 24-Mar-1992 JohnRo
+ Renamed many ReplGlobal vars to ReplConfig vars.
+ 11-Aug-1992 JohnRo
+ RAID 2115: repl svc should wait while stopping or changing roles.
+ 08-Dec-1992 JohnRo
+ RAID 3316: access violation while stopping the replicator
+ 05-Jan-1993 JohnRo
+ Repl WAN support (get rid of repl name list limits).
+ 24-May-1993 JohnRo
+ RAID 10587: repl could deadlock with changed NetpStopRpcServer(), so
+ just call ExitProcess() instead.
+
+--*/
+
+
+#ifndef _REPLGBL_
+#define _REPLGBL_
+
+
+#include <netlock.h> // LPNET_LOCK.
+//#include <repldefs.h> // MAX_NAME_BUF.
+#include <winsvc.h> // SERVICE_STATUS_HANDLE, etc.
+
+
+//
+// "config" variables (read at startup and settable by NetReplSetInfo).
+//
+
+extern LPNET_LOCK ReplConfigLock; // decl and init in repl.c
+extern DWORD ReplConfigRole; // Locked by ReplConfigLock.
+extern TCHAR ReplConfigExportPath[PATHLEN+1]; // Ditto.
+extern LPTSTR ReplConfigExportList; // Ditto.
+extern TCHAR ReplConfigImportPath[PATHLEN+1]; // Ditto.
+extern LPTSTR ReplConfigImportList; // Ditto.
+extern TCHAR ReplConfigLogonUserName[UNLEN+1]; // Ditto.
+extern DWORD ReplConfigInterval; // Ditto.
+extern DWORD ReplConfigPulse; // Ditto.
+extern DWORD ReplConfigGuardTime; // Ditto.
+extern DWORD ReplConfigRandom; // Ditto.
+
+//
+// Variables to control termination of the service.
+//
+
+extern HANDLE ReplGlobalClientTerminateEvent;
+extern HANDLE ReplGlobalMasterTerminateEvent;
+
+//
+// Variables to control service startup.
+//
+
+extern HANDLE ReplGlobalExportStartupEvent;
+extern HANDLE ReplGlobalImportStartupEvent;
+
+//
+// Variables to control service stop.
+//
+
+extern BOOL ReplGlobalIsServiceStopping;
+extern DWORD ReplGlobalCheckpoint;
+
+//
+// client thread handle
+//
+extern HANDLE ReplGlobalClientThreadHandle;
+
+
+//
+// master thread handle
+//
+extern HANDLE ReplGlobalMasterThreadHandle;
+
+
+//
+// Variables to control service error report.
+//
+
+extern SERVICE_STATUS_HANDLE ReplGlobalServiceHandle;
+extern DWORD ReplGlobalUninstallUicCode;
+
+//
+// We talk to both downlevel and NT clients, who want ANSI and Unicode
+// strings respectively. So, let's maintain copies of this (presumably
+// constant) data in both forms:
+//
+extern WCHAR ReplGlobalUnicodeComputerName[CNLEN+1];
+extern WCHAR ReplGlobalUnicodeDomainName[DNLEN+1];
+
+extern CHAR ReplGlobalAnsiComputerName[CNLEN+1];
+extern CHAR ReplGlobalAnsiDomainName[DNLEN+1];
+
+extern LPTSTR ReplGlobalComputerName; // points to one of the above.
+extern LPTSTR ReplGlobalDomainName; // points to one of the above.
+
+
+#endif // _REPLGBL_
diff --git a/private/net/svcdlls/repl/common/replname.h b/private/net/svcdlls/repl/common/replname.h
new file mode 100644
index 000000000..36afcecc2
--- /dev/null
+++ b/private/net/svcdlls/repl/common/replname.h
@@ -0,0 +1,35 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ ReplName.h
+
+Abstract:
+
+ Private header file which defines the replicator's interface name.
+
+Author:
+
+ John Rogers (JohnRo) 16-Jan-1992
+
+Revision History:
+
+ 16-Jan-1992 JohnRo
+ Created the repl service RPC stuff from Rita's workstation RPC stuff.
+ 16-Dec-1992 JohnRo
+ Made changes suggested by PC-LINT 5.0
+ Made this file include-able multiple times.
+
+--*/
+
+
+#ifndef _REPLNAME_
+#define _REPLNAME_
+
+
+#define REPLICATOR_INTERFACE_NAME (LPTSTR) TEXT("repl")
+
+
+#endif // _REPLNAME_
diff --git a/private/net/svcdlls/repl/common/replp.c b/private/net/svcdlls/repl/common/replp.c
new file mode 100644
index 000000000..f474e28aa
--- /dev/null
+++ b/private/net/svcdlls/repl/common/replp.c
@@ -0,0 +1,271 @@
+/*++
+
+Copyright (c) 1987-92 Microsoft Corporation
+
+Module Name:
+
+ replp.c
+
+Abstract:
+
+ contains library functions that may be moved to netlib later.
+
+Author:
+
+ 10/24/91 madana
+
+Environment:
+
+ Contains NT-specific code.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+ 16-Jan-1992 JohnRo
+ Avoid using private logon functions.
+ Fixed bug regarding returned value from NetpReplWriteMail functions.
+ Fixed mistake in NetpKdPrint format string by using equate.
+ 23-Jan-1992 JohnRo
+ Indicate that NetpReplTimeNow(()) can be called even if service is
+ 30-Jan-1992 JohnRo
+ Made changes suggested by PC-LINT.
+ 17-Feb-1992 JohnRo
+ Added a little checking of mailslot name.
+ 21-Feb-1992 JohnRo
+ Minor changes to mailslot name handling.
+ 27-Feb-1992 JohnRo
+ Added debug code regarding NetpReplTimeNow().
+ 11-Nov-1992 JohnRo
+ Fixed handle leak if WriteFile() fails.
+ More debug output of time.
+ Use PREFIX_ equates.
+
+--*/
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windef.h>
+#include <winbase.h>
+
+#include <lmcons.h>
+#include <netdebug.h> // NetpKdPrint(()), FORMAT_ equates, etc.
+#include <netlib.h> // NetpMemoryFree().
+#include <prefix.h> // PREFIX_ equates.
+#include <repldefs.h> // IF_DEBUG().
+#include <tstring.h> // NetpAlloc{type}From{type}(), etc.
+#include <winerror.h> // NO_ERROR, ERROR_ equates.
+
+
+DWORD
+NetpReplTimeNow(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ Returns the current time in terms of number of seconds since
+ Jan 1, 1970.
+
+ Note that this can be called even if service is not started.
+
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ Returns the current time in terms of number of seconds since
+ Jan 1, 1970.
+
+--*/
+{
+ BOOL ConvOk;
+ NTSTATUS NtStatus;
+ LARGE_INTEGER TimeNow;
+ DWORD Time1970;
+
+#define SOME_BOGUS_VALUE 12345
+
+ NtStatus = NtQuerySystemTime( &TimeNow );
+ NetpAssert( NT_SUCCESS( NtStatus ) );
+
+ Time1970 = SOME_BOGUS_VALUE;
+ ConvOk = RtlTimeToSecondsSince1970( &TimeNow, (PULONG) &Time1970 );
+
+ IF_DEBUG( REPL ) {
+ NetpKdPrint(( PREFIX_REPL
+ "NetpReplTimeNow: RTL says it is " FORMAT_DWORD ".\n",
+ Time1970 ));
+ NetpDbgDisplayTimestamp( " (formatted)", Time1970 );
+ }
+ NetpAssert( ConvOk );
+ NetpAssert( Time1970 != SOME_BOGUS_VALUE );
+
+ return Time1970;
+
+} // NetpReplTimeNow
+
+
+
+
+NET_API_STATUS
+NetpReplWriteMailW(
+ IN LPWSTR MailslotName,
+ IN LPBYTE Message,
+ IN DWORD MessageLength
+ )
+/*++
+
+Routine Description :
+
+ Writes a mail message to the specified mailslot.
+
+Arguments :
+
+ MailslotName : Name of the mailslot where to write the mail
+ Message : mail message
+ Messagelength : mail message length
+
+Return Value :
+
+ NO_ERROR : if the mail is successfully written to the mailslot
+ (other) : otherwise error code from CreateFileW or WriteFile.
+
+Threads:
+
+ Only called by master, pulser, and syncer threads.
+
+--*/
+{
+
+ NET_API_STATUS ApiStatus;
+ HANDLE FileHandle;
+ DWORD NumberOfBytesWritten;
+
+ NetpAssert( MailslotName != NULL );
+ NetpAssert( MailslotName[0] == L'\\' );
+ NetpAssert( MailslotName[1] == L'\\' );
+
+ NetpAssert( Message != NULL );
+ NetpAssert( MessageLength != 0 );
+
+ //
+ // Open the mailslot to write.
+ //
+
+ FileHandle = CreateFileW(
+ MailslotName,
+ GENERIC_WRITE,
+ FILE_SHARE_WRITE | FILE_SHARE_READ,
+ (LPSECURITY_ATTRIBUTES) NULL,
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL );
+
+ if ( FileHandle == (HANDLE) (-1)) {
+ ApiStatus = GetLastError();
+ NetpKdPrint(( PREFIX_REPL
+ "Problem with opening mailslot '" FORMAT_LPWSTR
+ "', error " FORMAT_API_STATUS ".\n",
+ MailslotName, ApiStatus));
+
+ NetpAssert( ApiStatus != NO_ERROR );
+ NetpAssert( ApiStatus != ERROR_INVALID_FUNCTION );
+
+ return (ApiStatus);
+ }
+
+
+
+ //
+ // Write message to mailslot
+ //
+
+ if ( !WriteFile(
+ FileHandle,
+ Message,
+ MessageLength,
+ &NumberOfBytesWritten,
+ NULL
+ ) ) {
+
+ ApiStatus = GetLastError();
+
+ NetpKdPrint(( PREFIX_REPL
+ "Error writing to " FORMAT_LPWSTR
+ " mailslot, error " FORMAT_API_STATUS ".\n",
+ MailslotName, ApiStatus));
+
+ NetpAssert( ApiStatus != NO_ERROR );
+ NetpAssert( ApiStatus != ERROR_INVALID_FUNCTION );
+
+ (VOID) CloseHandle(FileHandle);
+ return (ApiStatus);
+ }
+
+ (VOID) CloseHandle(FileHandle);
+
+ return (NO_ERROR);
+
+} // NetpReplWriteMailW
+
+
+
+
+NET_API_STATUS
+NetpReplWriteMailA(
+ IN LPSTR MailslotName,
+ IN LPBYTE Message,
+ IN DWORD MessageLength
+ )
+/*++
+
+Routine Description :
+
+ Same as NetpReplWriteMailW, but the mailslot name is specified as
+ ANSI string.
+
+ Writes a mail message to the specified mailslot.
+
+Arguments :
+
+ MailslotName : Name of the mailslot where to write the mail
+ Message : mail message
+ Messagelength : mail message length
+
+Return Value :
+
+ NO_ERROR : if the mail is successfully written to the mailslot
+ (other) : otherwise error code from CreateFileW or WriteFile.
+
+--*/
+{
+
+ NET_API_STATUS ApiStatus;
+ LPWSTR UnicodeMailslotName;
+
+ NetpAssert( MailslotName != NULL );
+ NetpAssert( MailslotName[0] == '\\' );
+ NetpAssert( MailslotName[1] == '\\' );
+
+ NetpAssert( Message != NULL );
+ NetpAssert( MessageLength != 0 );
+
+ UnicodeMailslotName = NetpAllocWStrFromStr( MailslotName );
+
+ if( UnicodeMailslotName == NULL) {
+
+ return (ERROR_NOT_ENOUGH_MEMORY);
+
+ }
+
+ ApiStatus = NetpReplWriteMailW(UnicodeMailslotName, Message, MessageLength);
+
+ NetpMemoryFree( UnicodeMailslotName );
+
+ return (ApiStatus);
+
+} // NetpReplWriteMailA
diff --git a/private/net/svcdlls/repl/common/replp.h b/private/net/svcdlls/repl/common/replp.h
new file mode 100644
index 000000000..fcdbee408
--- /dev/null
+++ b/private/net/svcdlls/repl/common/replp.h
@@ -0,0 +1,70 @@
+/*++
+
+Copyright (c) 1987-92 Microsoft Corporation
+
+Module Name:
+
+ replp.h
+
+Abstract:
+
+ contains library functions that may be moved to netlib later.
+
+Author:
+
+ 10/24/91 madana
+
+Environment:
+
+ Contains NT-specific code.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+ 05-Jan-1992 JohnRo
+ Fixed bug regarding returned value from NetpReplWriteMail functions.
+ 23-Jan-1992 JohnRo
+ Indicate that NetpReplTimeNow() can be called even if service is
+ not started.
+
+--*/
+
+
+#ifndef _REPLP_
+#define _REPLP_
+
+
+#ifdef UNICODE
+
+#define NetpReplWriteMail NetpReplWriteMailW
+
+#else // UNICODE
+
+#define NetpReplWriteMail NetpReplWriteMailA
+
+#endif // UNICODE
+
+
+NET_API_STATUS
+NetpReplWriteMailW(
+ IN LPWSTR,
+ IN LPBYTE,
+ IN DWORD
+ );
+
+NET_API_STATUS
+NetpReplWriteMailA(
+ IN LPSTR,
+ IN LPBYTE,
+ IN DWORD
+ );
+
+// Return number of seconds since 1970.
+// This can be called even if service is not started.
+DWORD
+NetpReplTimeNow(
+ VOID
+ );
+
+
+#endif // _REPLP_
diff --git a/private/net/svcdlls/repl/common/repvalid.c b/private/net/svcdlls/repl/common/repvalid.c
new file mode 100644
index 000000000..428b891b6
--- /dev/null
+++ b/private/net/svcdlls/repl/common/repvalid.c
@@ -0,0 +1,198 @@
+/*++
+
+Copyright (c) 1992-1993 Microsoft Corporation
+
+Module Name:
+
+ RepValid.c
+
+Abstract:
+
+ This file contains ReplConfigIsApiRecordValid().
+
+Author:
+
+ John Rogers (JohnRo) 14-Feb-1992
+
+Environment:
+
+ User Mode - Win32
+ Portable to any flat, 32-bit environment. (Uses Win32 typedefs.)
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+ 14-Feb-1992 JohnRo
+ Created.
+ 18-Feb-1992 JohnRo
+ Use ReplCheckAbsPathSyntax().
+ Use ReplIs{Interval,GuardTime,Pulse,Random}Valid() macros.
+ 10-Mar-1992 JohnRo
+ Added support for setinfo info levels.
+ 14-Aug-1992 JohnRo
+ Help track down bogus import/export lists.
+ 04-Jan-1993 JohnRo
+ Made changes suggested by PC-LINT 5.0
+
+--*/
+
+
+#include <windef.h> // IN, DWORD, etc.
+#include <lmcons.h> // LAN Manager common definitions
+#include <rpc.h> // Needed by <repl.h>.
+
+#include <iniparm.h> // MIN_ and MAX_ equates.
+#include <lmrepl.h> // LPREPL_INFO_0.
+#include <netlib.h> // IN_RANGE().
+#include <replconf.h> // ReplConfigIsListValid().
+#include <repldefs.h> // ReplIsRoleValid().
+#include <winerror.h> // ERROR_ and NO_ERROR equates.
+
+
+BOOL
+ReplConfigIsApiRecordValid (
+ IN DWORD Level, // Level must not be a setinfo-only level.
+ IN LPVOID ApiRecord,
+ OUT LPDWORD ParmError OPTIONAL // Set implicitly by some macros.
+ )
+
+{
+
+ if (ApiRecord == NULL) {
+ NetpSetParmError( PARM_ERROR_UNKNOWN );
+ return (FALSE); // No, it is not valid.
+ }
+
+ //
+ // Check the actual data, based on the level.
+ //
+
+ switch (Level) {
+ case 0 :
+ {
+ LPREPL_INFO_0 Buffer = ApiRecord;
+
+
+#define CHECK_LIST( fieldName, parm_error ) \
+ { \
+ if (Buffer->rp0_ ## fieldName) { \
+ if ( !ReplConfigIsListValid( Buffer->rp0_ ## fieldName ) ) { \
+ NetpSetParmError( parm_error ); \
+ return (FALSE); /* No, it is not valid. */ \
+ } \
+ } \
+ }
+
+
+#define CHECK_PATH( fieldName, parm_error ) \
+ { \
+ DWORD ApiStatus; \
+ if (Buffer->rp0_ ## fieldName) { \
+ ApiStatus = ReplCheckAbsPathSyntax( Buffer->rp0_ ## fieldName ); \
+ if (ApiStatus != NO_ERROR) { \
+ NetpSetParmError( parm_error ); \
+ return (FALSE); /* No, it is not valid. */ \
+ } \
+ } \
+ }
+
+
+ if ( ! ReplIsRoleValid( Buffer->rp0_role ) ) {
+ NetpSetParmError( 1 ); // first field in structure
+ return (FALSE); // No, it is not valid.
+ }
+
+ CHECK_PATH( exportpath, 2 ); // second...
+
+ CHECK_LIST( exportlist, 3 ); // third...
+
+ CHECK_PATH( importpath, 4 ); // fourth...
+
+ CHECK_LIST( importlist, 5 ); // fifth...
+
+ // BUGBUG: Handle logonusername!!! // sixth...
+
+ if ( !ReplIsIntervalValid( Buffer->rp0_interval ) ) {
+ NetpSetParmError( 7 ); // seventh...
+ return (FALSE); // No, it is not valid.
+ }
+
+ if ( !ReplIsPulseValid( Buffer->rp0_pulse ) ) {
+ NetpSetParmError( 8 ); // eighth...
+ return (FALSE); // No, it is not valid.
+ }
+
+ if ( !ReplIsGuardTimeValid( Buffer->rp0_guardtime ) ) {
+ NetpSetParmError( 9 ); // ninth...
+ return (FALSE); // No, it is not valid.
+ }
+
+ if ( !ReplIsRandomValid( Buffer->rp0_random ) ) {
+ NetpSetParmError( 10 ); // tenth...
+ return (FALSE); // No, it is not valid.
+ }
+
+
+ }
+ return (TRUE); // yes, it's valid.
+
+ case 1000 :
+ {
+ LPREPL_INFO_1000 Buffer = (LPVOID) ApiRecord;
+ DWORD NewValue = Buffer->rp1000_interval;
+
+ if ( !ReplIsIntervalValid( NewValue ) ) {
+ NetpSetParmError( 1 ); // first field in structure.
+ return (FALSE); // No, it is not valid.
+ }
+
+ }
+ return (TRUE); // yes, it's valid.
+
+ case 1001 :
+ {
+ LPREPL_INFO_1001 Buffer = (LPVOID) ApiRecord;
+ DWORD NewValue = Buffer->rp1001_pulse;
+
+ if ( !ReplIsPulseValid( NewValue ) ) {
+ NetpSetParmError( 1 ); // first field in structure.
+ return (FALSE); // No, it is not valid.
+ }
+
+ }
+ return (TRUE); // yes, it's valid.
+
+ case 1002 :
+ {
+ LPREPL_INFO_1002 Buffer = (LPVOID) ApiRecord;
+ DWORD NewValue = Buffer->rp1002_guardtime;
+
+ if ( !ReplIsGuardTimeValid( NewValue ) ) {
+ NetpSetParmError( 1 ); // first field in structure.
+ return (FALSE); // No, it is not valid.
+ }
+
+ }
+ return (TRUE); // yes, it's valid.
+
+ case 1003 :
+ {
+ LPREPL_INFO_1003 Buffer = (LPVOID) ApiRecord;
+ DWORD NewValue = Buffer->rp1003_random;
+
+ if ( !ReplIsRandomValid( NewValue ) ) {
+ NetpSetParmError( 1 ); // first field in structure.
+ return (FALSE); // No, it is not valid.
+ }
+
+ }
+ return (TRUE); // yes, it's valid.
+
+ default :
+ NetpSetParmError( PARM_ERROR_UNKNOWN );
+ return (FALSE); // Don't know level, so say record is not valid.
+ }
+
+ /*NOTREACHED*/
+
+} // ReplConfigIsApiRecordValid
diff --git a/private/net/svcdlls/repl/common/sources b/private/net/svcdlls/repl/common/sources
new file mode 100644
index 000000000..9dbfb0d69
--- /dev/null
+++ b/private/net/svcdlls/repl/common/sources
@@ -0,0 +1,133 @@
+!IF 0
+
+Copyright (c) 1989-1993 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+Author:
+
+ Steve Wood (stevewo) 12-Apr-1990
+
+NOTE: Commented description of this file is in \nt\public\oak\bin\sources.tpl
+
+!ENDIF
+
+#
+# The MAJORCOMP and MINORCOMP variables are defined
+# so that $(MAJORCOMP)$(MINORCOMP)filename can be used in
+# cross compiling to provide unique filenames in a flat namespace.
+#
+
+MAJORCOMP=net
+MINORCOMP=repl
+
+
+NTPROFILEINPUT=YES
+
+#
+# The TARGETNAME variable is defined by the developer. It is the name of
+# the target (component) that is being built by this makefile. It
+# should NOT include any path or file extension information.
+#
+
+TARGETNAME=replcom
+
+#
+# The TARGETPATH and TARGETTYPE variables are defined by the developer.
+# The first specifies where the target is to be build. The second specifies
+# the type of target (either PROGRAM, DYNLINK, LIBRARY, UMAPPL_NOLIB or
+# BOOTPGM). UMAPPL_NOLIB is used when you're only building user-mode
+# apps and don't need to build a library.
+#
+
+TARGETPATH=obj
+
+# Pick one of the following and delete the others
+TARGETTYPE=LIBRARY
+
+#
+# The TARGETLIBS specifies additional libraries to link with you target
+# image. Each library path specification should contain an asterisk (*)
+# where the machine specific subdirectory name should go.
+#
+
+TARGETLIBS=
+
+#
+# The INCLUDES variable specifies any include paths that are specific to
+# this source directory. Separate multiple directory paths with single
+# semicolons. Relative path specifications are okay.
+#
+
+# ..\server is where IgnoreNm.c gets Client.h until we can move some
+# equates from Client.h to ReplDefs.h.
+INCLUDES=.;..\..\..\inc;..\..\..\api;..\..\..\..\inc;..\server
+
+#
+# The SOURCES variable is defined by the developer. It is a list of all the
+# source files for this component. Each source file should be on a separate
+# line using the line continuation character. This will minimize merge
+# conflicts if two developers adding source files to the same component.
+#
+
+!IFNDEF DISABLE_NET_UNICODE
+UNICODE=1
+NET_C_DEFINES=-DUNICODE
+!ENDIF
+
+USE_CRTDLL=1
+
+SOURCES= \
+ AbsPath.c \
+ AllowRol.c \
+ ChngLock.c \
+ ChngNot.c \
+ Data.c \
+ DelFile.c \
+ DirName.c \
+ EaSize.c \
+ ExpAlloc.c \
+ ExpBuild.c \
+ ExpConf.c \
+ ExpEnum.c \
+ ExpGet.c \
+ ExpLock.c \
+ ExpSet.c \
+ ExpValid.c \
+ FixLocks.c \
+ FsResolu.c \
+ IgnoreNm.c \
+ ImpAlloc.c \
+ ImpBuild.c \
+ ImpConf.c \
+ ImpGet.c \
+ ImpLock.c \
+ ImpState.c \
+ ImpEnum.c \
+ ImpValid.c \
+ LstValid.c \
+ ReplBld.c \
+ ReplConf.c \
+ ReplErr.c \
+ ReplP.c \
+ RepValid.c \
+ UserLock.c
+
+#
+# Next specify options for the compiler.
+#
+
+# C_DEFINES=-DTRACE -DTRACE2 -DDEBUG
+
+C_DEFINES=-DRPC_NO_WINDOWS_H
+WARNING_LEVEL=-W4
+
diff --git a/private/net/svcdlls/repl/common/userlock.c b/private/net/svcdlls/repl/common/userlock.c
new file mode 100644
index 000000000..c142993ad
--- /dev/null
+++ b/private/net/svcdlls/repl/common/userlock.c
@@ -0,0 +1,158 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ UserLock.c
+
+Abstract:
+
+ ReplDoUserLockFilesExist().
+
+ This routine checks for UserLock.* files in the directory given.
+ The directory may be a UNC path name.
+
+ This is callable even if the replicator service is not started.
+
+Author:
+
+ JR (John Rogers, JohnRo@Microsoft)) 13-Jan-1993
+
+Environment:
+
+ User Mode - Win32
+ Portable to any flat, 32-bit environment. (Uses Win32 typedefs.)
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+ 13-Jan-1993 JohnRo
+ Created for RAID 7053: locked trees added to pulse msg. (Actually
+ fix all kinds of remote lock handling.)
+ 02-Feb-1993 JohnRo
+ RAID 8355: Downlevel lock file check causes assert in repl importer.
+
+--*/
+
+
+// These must be included first:
+
+#include <windows.h> // FindFirstFile(), IN, LPCTSTR, OPTIONAL, etc.
+#include <lmcons.h> // NET_API_STATUS.
+
+// These may be included in any order:
+
+
+#include <client.h> // USER_LOCK.
+#include <lmerrlog.h> // NELOG_ equates.
+#include <netdebug.h> // NetpAssert(), NetpKdPrint(), FORMAT_ equates, etc.
+#include <prefix.h> // PREFIX_ equates.
+#include <repldefs.h> // My prototype, ReplErrorLog(), SLASH, etc.
+#include <tstr.h> // IS_PATH_SEPARATOR(), STRCAT(), STRCPY(), STRLEN().
+#include <winerror.h> // ERROR_, NO_ERROR equates.
+
+
+// Callable even if the replicator service is not started.
+NET_API_STATUS
+ReplDoUserLockFilesExist(
+ IN LPCTSTR Path, // May be absolute path (with drive) or UNC path.
+ OUT LPBOOL IsLockedPtr
+ )
+{
+ NET_API_STATUS ApiStatus;
+ BOOL DirLocked = FALSE; // Default is not locked.
+ WIN32_FIND_DATA FindData;
+ HANDLE FindHandle = INVALID_HANDLE_VALUE;
+ TCHAR SearchPattern[MAX_PATH+1];
+
+ //
+ // Check for caller errors.
+ //
+ NetpAssert( IsLockedPtr != NULL );
+
+ if ( IS_PATH_SEPARATOR(Path[0]) ) {
+ // BUGBUG: Syntax check somehow.
+ // NetpAssert( NetpIsRemotePathValid( Path ) );
+ if ( ! IS_PATH_SEPARATOR(Path[1]) ) {
+ ApiStatus = ERROR_INVALID_PARAMETER;
+ goto Cleanup; // Don't forget to unlock stuff.
+ }
+ } else {
+ ApiStatus = ReplCheckAbsPathSyntax( (LPTSTR) Path );
+ if (ApiStatus != NO_ERROR) {
+ goto Cleanup; // Don't forget to unlock stuff.
+ }
+ }
+
+ //
+ // Build a search pattern. This will look like one of the following:
+ // c:\dir1\dir2\dir3\USERLOCK.*
+ // \\server\REPL$\dirname\USERLOCK.*
+ //
+
+ (VOID) STRCPY( SearchPattern, (LPTSTR) Path );
+ (VOID) STRCAT( SearchPattern, SLASH );
+ (VOID) STRCAT( SearchPattern, USER_LOCK ); // "USERLOCK.*".
+
+ NetpAssert( STRLEN( SearchPattern ) <= MAX_PATH );
+
+ // NetpAssert( NetpIsRemotePathValid( SearchPattern ) );
+
+ //
+ // Check if any USERLOCK.* exists.
+ //
+ FindHandle = FindFirstFile(
+ SearchPattern,
+ &FindData );
+ if (FindHandle != INVALID_HANDLE_VALUE) {
+ // Yes, there is a lock file.
+ DirLocked = TRUE;
+ ApiStatus = NO_ERROR;
+ } else {
+ ApiStatus = (NET_API_STATUS) GetLastError();
+ IF_DEBUG( FILEFIND ) {
+ NetpKdPrint(( PREFIX_REPL
+ "ReplDoUserLockFilesExist: got err " FORMAT_API_STATUS
+ " from FindFirstFile.\n",
+ ApiStatus ));
+ }
+ NetpAssert( ApiStatus != NO_ERROR );
+ if (ApiStatus == ERROR_FILE_NOT_FOUND) {
+ // No lock file.
+ DirLocked = FALSE;
+ ApiStatus = NO_ERROR;
+ }
+ // Unexpected error will be logged below.
+ }
+
+
+Cleanup:
+
+ if (ApiStatus != NO_ERROR) {
+
+ ReplErrorLog(
+ NULL, // local (no server name)
+ NELOG_ReplSysErr, // log code
+ ApiStatus, // the unexpected error code
+ NULL, // no optional str1
+ NULL ); // no optional str2
+
+ // BUGBUG: log unexpected error on remote machine too, if UNC path.
+
+ NetpKdPrint(( PREFIX_REPL
+ "ReplDoUserLockFilesExist: ERROR " FORMAT_API_STATUS ".\n",
+ ApiStatus ));
+ }
+
+ if (FindHandle != INVALID_HANDLE_VALUE) {
+ (VOID) FindClose( FindHandle );
+ }
+
+ if (IsLockedPtr != NULL) {
+ *IsLockedPtr = DirLocked;
+ }
+
+ return (ApiStatus);
+
+}
diff --git a/private/net/svcdlls/repl/dirs b/private/net/svcdlls/repl/dirs
new file mode 100644
index 000000000..b187a0a3b
--- /dev/null
+++ b/private/net/svcdlls/repl/dirs
@@ -0,0 +1,49 @@
+!IF 0
+
+Copyright (c) 1989-92 Microsoft Corporation
+
+Module Name:
+
+ dirs.
+
+Abstract:
+
+ This file specifies the subdirectories of the current directory that
+ contain component makefiles.
+
+
+Author:
+
+ Steve Wood (stevewo) 17-Apr-1990
+
+NOTE: Commented description of this file is in \nt\public\oak\bin\dirs.tpl
+
+!ENDIF
+
+#
+# This macro is defined by the developer. It is a list of all subdirectories
+# that build required components. Each subdirectory should be on a separate
+# line using the line continuation character. This will minimize merge
+# conflicts if two developers adding source files to the same component.
+# The order of the directories is the order that they will be built when
+# doing a build.
+#
+
+DIRS= \
+ Client \
+ Common \
+ Server
+
+
+#
+# This macro is defined by the developer. It is a list of all subdirectories
+# that build optional components. Each subdirectory should be on a separate
+# line using the line continuation character. This will minimize merge
+# conflicts if two developers adding source files to the same component.
+# The order of the directories is the order that they will be built when
+# doing a build.
+#
+
+OPTIONAL_DIRS= \
+ ReplTest \
+ TestDLL
diff --git a/private/net/svcdlls/repl/imports.idl b/private/net/svcdlls/repl/imports.idl
new file mode 100644
index 000000000..d8e25cba2
--- /dev/null
+++ b/private/net/svcdlls/repl/imports.idl
@@ -0,0 +1,67 @@
+/*++
+
+Copyright (c) 1991-92 Microsoft Corporation
+
+Module Name:
+
+ imports.idl
+
+Abstract:
+
+ This file is useful for creating RPC interfaces that require the use
+ of windef types. The .idl file for the RPC product should contain a
+ line in the interface body that imports this file. For example:
+
+ import "imports.h";
+
+ Doing this causes the MIDL generated header file to contain the
+ following line:
+
+ #include "imports.h"
+
+ If this technique is not used, and instead the .idl file for the RPC
+ product simply contains #include <importsf.h>, then the contents of
+ imports.h will be expanded in the MIDL generated header file. This
+ can lead to duplicate definition problems later when the RPC client
+ or RPC server code needs to include both the MIDL generated header file
+ and a file that is included in imports.h.
+
+Author:
+
+ Dan Lafferty (danl) 20-Mar-1991
+
+Environment:
+
+ User Mode - Win32 - for use with the MIDL compiler
+
+
+Revision History:
+
+ 03-Apr-1991 danl
+ created
+
+--*/
+
+[
+ uuid(12345678-1234-ABCD-EF00-9948756789AB),
+#ifdef __midl
+ ms_union,
+#endif // __midl
+ version(1.0)
+]
+interface imports
+
+{
+#define MIDL_PASS
+#include "imports.h"
+
+//
+// All .idl files need to contain at least one function prototype
+//
+
+DWORD
+Dummy(
+ [in] DWORD DummyParm);
+
+
+}
diff --git a/private/net/svcdlls/repl/makefil0 b/private/net/svcdlls/repl/makefil0
new file mode 100644
index 000000000..70f4b74ba
--- /dev/null
+++ b/private/net/svcdlls/repl/makefil0
@@ -0,0 +1,63 @@
+#
+# This is the MIDL compile phase of the build process.
+#
+# The following is where you put the name of your .idl file without
+# the .idl extension:
+#
+
+!INCLUDE $(NTMAKEENV)\makefile.plt
+
+IDL_NAME = repl
+
+!IFNDEF DISABLE_NET_UNICODE
+UNICODE=1
+NET_C_DEFINES=-DUNICODE
+!ENDIF
+
+SDKINC = $(BASEDIR)\public\sdk\inc
+NETINC = ..\..\inc
+SDKCRTINC = $(BASEDIR)\public\sdk\inc\crt
+PRIVINC = ..\..\..\inc
+COMMONINC = .\common
+
+INCS = -I$(SDKINC) -I$(SDKCRTINC) -I$(PRIVINC) -I$(NETINC) -I$(COMMONINC)
+
+CLIENT_TARGETS = client\$(IDL_NAME)_c.c \
+ $(COMMONINC)\$(IDL_NAME).h
+
+SERVER_TARGETS = server\$(IDL_NAME)_s.c
+
+EXTRN_DEPENDS = $(SDKINC)\lmcons.h \
+ $(SDKINC)\windef.h \
+ $(SDKINC)\lmrepl.h \
+ $(COMMONINC)\imports.h
+
+CPP = -cpp_cmd "$(MIDL_CPP)" $(MIDL_FLAGS) $(C_DEFINES) $(NET_C_DEFINES)
+
+#
+# Define Products and Dependencies
+#
+
+all: $(CLIENT_TARGETS) $(SERVER_TARGETS) $(EXTRN_DEPENDS)
+!IF "$(BUILDMSG)" != ""
+ @ech ; $(BUILDMSG) ;
+!ENDIF
+
+clean: delsrc all
+
+delsrc:
+ erase $(CLIENT_TARGETS) $(SERVER_TARGETS)
+
+#
+# MIDL COMPILE
+#
+
+$(CLIENT_TARGETS) : .\$(IDL_NAME).idl .\imports.idl $(EXTRN_DEPENDS) .\$(IDL_NAME).acf
+ midl -Oi -server none -oldnames -error allocation -error ref -ms_ext -c_ext $(CPP) .\$(IDL_NAME).idl $(INCS)
+ IF EXIST $(IDL_NAME).h copy $(IDL_NAME).h .\$(COMMONINC) & del $(IDL_NAME).h
+ IF EXIST $(IDL_NAME)_c.c copy $(IDL_NAME)_c.c .\client & del $(IDL_NAME)_c.c
+
+$(SERVER_TARGETS) : .\$(IDL_NAME).idl .\imports.idl $(EXTRN_DEPENDS) .\$(IDL_NAME).acf
+ midl -client none -oldnames -error stub_data -error allocation -error ref -ms_ext -c_ext $(CPP) .\$(IDL_NAME).idl $(INCS)
+ IF EXIST $(IDL_NAME).h copy $(IDL_NAME).h .\$(COMMONINC) & del $(IDL_NAME).h
+ IF EXIST $(IDL_NAME)_s.c copy $(IDL_NAME)_s.c .\server & del $(IDL_NAME)_s.c
diff --git a/private/net/svcdlls/repl/repl.acf b/private/net/svcdlls/repl/repl.acf
new file mode 100644
index 000000000..a09b3b023
--- /dev/null
+++ b/private/net/svcdlls/repl/repl.acf
@@ -0,0 +1,18 @@
+[ implicit_handle( handle_t repl_bhandle )
+] interface repl
+{
+typedef [allocate(all_nodes)] LPREPL_INFO_0;
+typedef [allocate(all_nodes)] LPREPL_INFO_1000;
+typedef [allocate(all_nodes)] LPREPL_INFO_1001;
+typedef [allocate(all_nodes)] LPREPL_INFO_1002;
+typedef [allocate(all_nodes)] LPREPL_INFO_1003;
+
+typedef [allocate(all_nodes)] LPREPL_EDIR_INFO_0;
+typedef [allocate(all_nodes)] LPREPL_EDIR_INFO_1;
+typedef [allocate(all_nodes)] LPREPL_EDIR_INFO_2;
+typedef [allocate(all_nodes)] LPREPL_EDIR_INFO_1000;
+typedef [allocate(all_nodes)] LPREPL_EDIR_INFO_1001;
+
+typedef [allocate(all_nodes)] LPREPL_IDIR_INFO_0;
+typedef [allocate(all_nodes)] LPREPL_IDIR_INFO_1;
+}
diff --git a/private/net/svcdlls/repl/repl.idl b/private/net/svcdlls/repl/repl.idl
new file mode 100644
index 000000000..7ead45851
--- /dev/null
+++ b/private/net/svcdlls/repl/repl.idl
@@ -0,0 +1,307 @@
+/*++
+
+Copyright (c) 1991-92 Microsoft Corporation
+
+Module Name:
+
+ Repl.idl
+
+Abstract:
+
+ Contains the Netr (Net Remote) RPC interface specification for the APIs
+ associated with the replicator service. Also contains the RPC specific
+ data structures for these APIs.
+
+Author:
+
+ John Rogers (JohnRo) 20-Jan-1992
+
+Environment:
+
+ User Mode - Win32 - MIDL
+
+Revision History:
+
+ 20-Jan-1992 JohnRo
+ Created the repl service RPC stuff from RitaW's workstation RPC stuff.
+ 20-Jan-1992 JohnRo
+ Added import APIs, config APIs, and rest of export APIs.
+ 24-Jan-1992 JohnRo
+ Changed to use LPTSTR etc.
+ 17-Feb-1992 JohnRo
+ Need to repeat somethings in this .idl as well as imports.idl.
+--*/
+
+
+//
+// Interface Attributes
+//
+
+[
+ uuid(6BFFD098-0206-0936-4859-199201201157),
+ version(1.0),
+#ifdef __midl
+ ms_union,
+#endif // __midl
+ pointer_default(unique)
+]
+
+
+//
+// Interface Keyword
+//
+
+interface repl
+
+
+//
+// Interface Body
+//
+
+{
+
+import "imports.idl"; // lmrepl.h is included here.
+#include <lmcons.h>
+
+//
+// BUGBUG - take this definition out when midl understands LPWSTR etc
+//
+
+#ifdef UNICODE
+#define LPTSTR wchar_t *
+#endif
+
+
+typedef [handle] LPTSTR REPL_IDENTIFY_HANDLE;
+
+//
+// Replicator Configuration APIs
+//
+
+typedef [switch_type(unsigned long)] union _CONFIG_CONTAINER {
+ [case(0)]
+ LPREPL_INFO_0 Info0;
+ [case(1000)]
+ LPREPL_INFO_1000 Info1000;
+ [case(1001)]
+ LPREPL_INFO_1001 Info1001;
+ [case(1002)]
+ LPREPL_INFO_1002 Info1002;
+ [case(1003)]
+ LPREPL_INFO_1003 Info1003;
+ [default]
+ ;
+} CONFIG_CONTAINER, *PCONFIG_CONTAINER, *LPCONFIG_CONTAINER;
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetrReplGetInfo (
+ [in,string,unique] REPL_IDENTIFY_HANDLE UncServerName,
+ [in] DWORD Level,
+ [out,switch_is(Level)] LPCONFIG_CONTAINER BufPtr
+ );
+
+NET_API_STATUS NET_API_FUNCTION
+NetrReplSetInfo (
+ [in,string,unique] REPL_IDENTIFY_HANDLE UncServerName,
+ [in] DWORD Level,
+ [in,switch_is(Level)] LPCONFIG_CONTAINER BufPtr,
+ [in,out,unique] LPDWORD ParmError
+ );
+
+
+//
+// Replicator Export Directory APIs
+//
+
+// Union for general-purpose use.
+typedef [switch_type(unsigned long)] union _EXPORT_CONTAINER {
+ [case(0)]
+ LPREPL_EDIR_INFO_0 Info0;
+ [case(1)]
+ LPREPL_EDIR_INFO_1 Info1;
+ [case(2)]
+ LPREPL_EDIR_INFO_2 Info2;
+ [case(1000)]
+ LPREPL_EDIR_INFO_1000 Info1000;
+ [case(1001)]
+ LPREPL_EDIR_INFO_1001 Info1001;
+ [default]
+ ;
+} EXPORT_CONTAINER, *PEXPORT_CONTAINER, *LPEXPORT_CONTAINER;
+
+// Stuff for enum-only use.
+typedef struct _EXPORT_INFO_0_CONTAINER {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] LPREPL_EDIR_INFO_0 Buffer;
+} EXPORT_INFO_0_CONTAINER, *PEXPORT_INFO_0_CONTAINER,
+ *LPEXPORT_INFO_0_CONTAINER;
+
+typedef struct _EXPORT_INFO_1_CONTAINER {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] LPREPL_EDIR_INFO_1 Buffer;
+} EXPORT_INFO_1_CONTAINER, *PEXPORT_INFO_1_CONTAINER,
+ *LPEXPORT_INFO_1_CONTAINER;
+
+typedef struct _EXPORT_INFO_2_CONTAINER {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] LPREPL_EDIR_INFO_2 Buffer;
+} EXPORT_INFO_2_CONTAINER, *PEXPORT_INFO_2_CONTAINER,
+ *LPEXPORT_INFO_2_CONTAINER;
+
+typedef struct _EXPORT_ENUM_STRUCT {
+ DWORD Level;
+ [switch_is(Level)] union _EXPORT_ENUM_UNION {
+ [case(0)]
+ LPEXPORT_INFO_0_CONTAINER Level0;
+ [case(1)]
+ LPEXPORT_INFO_1_CONTAINER Level1;
+ [case(2)]
+ LPEXPORT_INFO_2_CONTAINER Level2;
+ [default]
+ ;
+ } ExportInfo;
+} EXPORT_ENUM_STRUCT, *PEXPORT_ENUM_STRUCT, *LPEXPORT_ENUM_STRUCT;
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetrReplExportDirAdd (
+ [in,string,unique] REPL_IDENTIFY_HANDLE UncServerName,
+ [in] DWORD Level,
+ [in,switch_is(Level)] LPEXPORT_CONTAINER Buf,
+ [in,out,unique] LPDWORD ParmError
+ );
+
+NET_API_STATUS NET_API_FUNCTION
+NetrReplExportDirDel (
+ [in,string,unique] REPL_IDENTIFY_HANDLE UncServerName,
+ [in,string,unique] LPTSTR DirName
+ );
+
+NET_API_STATUS NET_API_FUNCTION
+NetrReplExportDirEnum (
+ [in,string,unique] REPL_IDENTIFY_HANDLE UncServerName,
+ [in,out] LPEXPORT_ENUM_STRUCT BufPtr,
+ [in] DWORD PrefMaxSize,
+ [out] LPDWORD TotalEntries,
+ [in,out,unique] LPDWORD ResumeHandle
+ );
+
+NET_API_STATUS NET_API_FUNCTION
+NetrReplExportDirGetInfo (
+ [in,string,unique] REPL_IDENTIFY_HANDLE UncServerName,
+ [in,string,unique] LPTSTR DirName,
+ [in] DWORD Level,
+ [out,switch_is(Level)] LPEXPORT_CONTAINER BufPtr
+ );
+
+NET_API_STATUS NET_API_FUNCTION
+NetrReplExportDirLock (
+ [in,string,unique] REPL_IDENTIFY_HANDLE UncServerName,
+ [in,string,unique] LPTSTR DirName
+ );
+
+NET_API_STATUS NET_API_FUNCTION
+NetrReplExportDirSetInfo (
+ [in,string,unique] REPL_IDENTIFY_HANDLE UncServerName,
+ [in,string,unique] LPTSTR DirName,
+ [in] DWORD Level,
+ [in,switch_is(Level)] LPEXPORT_CONTAINER BufPtr,
+ [in,out,unique] LPDWORD ParmError
+ );
+
+NET_API_STATUS NET_API_FUNCTION
+NetrReplExportDirUnlock (
+ [in,string,unique] REPL_IDENTIFY_HANDLE UncServerName,
+ [in,string,unique] LPTSTR DirName,
+ [in] DWORD UnlockForce
+ );
+
+
+//
+// Replicator Import Directory APIs
+//
+
+// General-purpose container:
+typedef [switch_type(unsigned long)] union _IMPORT_CONTAINER {
+ [case(0)]
+ LPREPL_IDIR_INFO_0 Info0;
+ [case(1)]
+ LPREPL_IDIR_INFO_1 Info1;
+ [default]
+ ;
+} IMPORT_CONTAINER, *PIMPORT_CONTAINER, *LPIMPORT_CONTAINER;
+
+// Enum-only containers:
+typedef struct _IMPORT_INFO_0_CONTAINER {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] LPREPL_IDIR_INFO_0 Buffer;
+} IMPORT_INFO_0_CONTAINER, *PIMPORT_INFO_0_CONTAINER,
+ *LPIMPORT_INFO_0_CONTAINER;
+
+typedef struct _IMPORT_INFO_1_CONTAINER {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] LPREPL_IDIR_INFO_1 Buffer;
+} IMPORT_INFO_1_CONTAINER, *PIMPORT_INFO_1_CONTAINER,
+ *LPIMPORT_INFO_1_CONTAINER;
+
+typedef struct _IMPORT_ENUM_STRUCT {
+ DWORD Level;
+ [switch_is(Level)] union _IMPORT_ENUM_UNION {
+ [case(0)]
+ LPIMPORT_INFO_0_CONTAINER Level0;
+ [case(1)]
+ LPIMPORT_INFO_1_CONTAINER Level1;
+ [default]
+ ;
+ } ImportInfo;
+} IMPORT_ENUM_STRUCT, *PIMPORT_ENUM_STRUCT, *LPIMPORT_ENUM_STRUCT;
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetrReplImportDirAdd (
+ [in,string,unique] REPL_IDENTIFY_HANDLE UncServerName,
+ [in] DWORD Level,
+ [in,switch_is(Level)] LPIMPORT_CONTAINER Buf,
+ [in,out,unique] LPDWORD ParmError
+ );
+
+NET_API_STATUS NET_API_FUNCTION
+NetrReplImportDirDel (
+ [in,string,unique] REPL_IDENTIFY_HANDLE UncServerName,
+ [in,string,unique] LPTSTR DirName
+ );
+
+NET_API_STATUS NET_API_FUNCTION
+NetrReplImportDirEnum (
+ [in,string,unique] REPL_IDENTIFY_HANDLE UncServerName,
+ [in,out] LPIMPORT_ENUM_STRUCT BufPtr,
+ [in] DWORD PrefMaxSize,
+ [out] LPDWORD TotalEntries,
+ [in,out,unique] LPDWORD ResumeHandle
+ );
+
+NET_API_STATUS NET_API_FUNCTION
+NetrReplImportDirGetInfo (
+ [in,string,unique] REPL_IDENTIFY_HANDLE UncServerName,
+ [in,string,unique] LPTSTR DirName,
+ [in] DWORD Level,
+ [out,switch_is(Level)] LPIMPORT_CONTAINER BufPtr
+ );
+
+NET_API_STATUS NET_API_FUNCTION
+NetrReplImportDirLock (
+ [in,string,unique] REPL_IDENTIFY_HANDLE UncServerName,
+ [in,string,unique] LPTSTR DirName
+ );
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetrReplImportDirUnlock (
+ [in,string,unique] REPL_IDENTIFY_HANDLE UncServerName,
+ [in,string,unique] LPTSTR DirName,
+ [in] DWORD UnlockForce
+ );
+
+
+} // That's all, folks! --JR
diff --git a/private/net/svcdlls/repl/repltest/cli_pars.c b/private/net/svcdlls/repl/repltest/cli_pars.c
new file mode 100644
index 000000000..9481cf614
--- /dev/null
+++ b/private/net/svcdlls/repl/repltest/cli_pars.c
@@ -0,0 +1,281 @@
+/********************************************************************/
+/** Microsoft LAN Manager **/
+/** Copyright(c) Microsoft Corp., 1987-1991 **/
+/********************************************************************/
+
+
+/* This file conatins Client's Signal Handler and Installer
+* and function ClientParse, which is a
+* a watered down copy of function Parse (file parse.c), it uses
+* the same mechanisem for argument parsing (Cmdargs4), but does not
+* perform any checks since these had already been performed and
+* passed succesfully (otherwise we wouldnt get here). Some additional
+* processing is performed on lists to make them easier to access.
+*
+**********************************************************************
+
+
+#define INCL_DOSPROCESS
+#define INCL_DOSSIGNALS
+#define INCL_DOSSEMAPHORES
+
+#define INCL_DOSERRORS
+#define INCL_DOSINFOSEG
+#include <os2.h>
+//
+//#include <limits.h>
+//
+#include <netcons.h>
+#include <neterr.h>
+#include <netlib.h>
+#include <cmdargs.h>
+#include <netbios.h>
+#include <service.h>
+#include <errlog.h>
+#include <icanon.h>
+#include <apiutil.h>
+#include <swtchtxt.h>
+#include <replini.h>
+
+#include "repldefs.h"
+#include "iniparm.h"
+#include "client.h"
+
+//
+// OUTNAME is a macro which provides the output name (the name we
+// display) of a switch argument. For the current macros this is
+// the text, after skipping over the slash (one byte).
+//
+
+#define OUTNAME(x) (char *)(x+1)
+
+#define TRUE 1
+
+
+static const char rep_REPL[] = SW_REPL_REPL;
+static const char rep_EXPPATH[] = SW_REPL_EXPPATH;
+static const char rep_IMPPATH[] = SW_REPL_IMPPATH;
+static const char rep_EXPLIST[] = SW_REPL_EXPLIST;
+static const char rep_IMPLIST[] = SW_REPL_IMPLIST;
+static const char rep_TRYUSER[] = SW_REPL_TRYUSER;
+static const char rep_LOGON[] = SW_REPL_LOGON;
+static const char rep_PASSWD[] = SW_REPL_PASSWD;
+static const char rep_SYNC[] = SW_REPL_SYNCH;
+static const char rep_PULSE[] = SW_REPL_PULSE;
+static const char rep_GUARD[] = SW_REPL_GUARD;
+static const char rep_RANDOM[] = SW_REPL_RANDOM;
+
+
+char *P_repl = DEFAULT_REPL;
+char *P_exppath = DEFAULT_EXPPATH;
+char *P_imppath = DEFAULT_IMPPATH;
+char *P_explist = DEFAULT_EXPLIST;
+char *P_implist = DEFAULT_IMPLIST;
+int P_tryuser = DEFAULT_TRYUSER;
+char *P_logon = DEFAULT_LOGON;
+char *P_passwd = DEFAULT_PASSWD;
+int P_sync = DEFAULT_SYNC;
+int P_pulse = DEFAULT_PULSE;
+int P_guard = DEFAULT_GUARD;
+int P_random = DEFAULT_RANDOM;
+
+
+struct cmdfmt argfmt[] = {
+ { (char * )rep_REPL, CMDARG_TYPE_STRING, (char * ) & (P_repl), sizeof(rep_REPL) + 1, 0 },
+ { (char * )rep_EXPPATH, CMDARG_TYPE_STRING, (char * ) & (P_exppath), sizeof(rep_EXPPATH) + 1, 0 },
+ { (char * )rep_IMPPATH, CMDARG_TYPE_STRING, (char * ) & (P_imppath), sizeof(rep_IMPPATH) + 1, 0 },
+ { (char * )rep_EXPLIST, CMDARG_TYPE_STRING, (char * ) & (P_explist), sizeof(rep_EXPLIST) + 1, 0 },
+ { (char * )rep_IMPLIST, CMDARG_TYPE_STRING, (char * ) & (P_implist), sizeof(rep_IMPLIST) + 1, 0 },
+ { (char * )rep_TRYUSER, CMDARG_TYPE_YN, (char * ) & (P_tryuser), sizeof(rep_TRYUSER) + 1, 0 },
+ { (char * )rep_LOGON, CMDARG_TYPE_STRING, (char * ) & (P_logon), sizeof(rep_LOGON) + 1, 0 },
+ { (char * )rep_PASSWD, CMDARG_TYPE_STRING, (char * ) & (P_passwd), sizeof(rep_PASSWD) + 1, 0 },
+ { (char * )rep_SYNC, CMDARG_TYPE_INT, (char * ) & (P_sync), sizeof(rep_SYNC) + 1, 0 },
+ { (char * )rep_PULSE, CMDARG_TYPE_INT, (char * ) & (P_pulse), sizeof(rep_PULSE) + 1, 0 },
+ { (char * )rep_GUARD, CMDARG_TYPE_INT, (char * ) & (P_guard), sizeof(rep_GUARD) + 1, 0 },
+ { (char * )rep_RANDOM, CMDARG_TYPE_INT, (char * ) & (P_random), sizeof(rep_RANDOM) + 1, 0 }
+};
+
+
+#define FCOUNT (sizeof(argfmt) / sizeof(argfmt[0]))
+
+char * master_list[32]; /* each enrty points to a string conataining
+ a computer/domain name */
+unsigned short masters_count;
+
+
+void
+ClientParse(argc, argv)
+/*******************
+*
+* Parse -- parse command line arguments fro client
+*
+*************************************************************************/
+int argc;
+char **argv;
+{
+ char *lastarg;
+
+ char big_buf[MAXPATHLEN * 2]; /* space for master and client name list and
+ for export/import paths
+ (currently about 0.5K) */
+ char *work_buf;
+ unsigned len;
+
+
+
+ GetCmdArgs4(argc, argv, 0, FCOUNT, argfmt, &lastarg);
+
+ work_buf = (char * )big_buf;
+
+ if (P_implist != NULL) {
+ int i;
+ char *p;
+
+ //
+ // Canonicalize the master's name list
+ // Really just making sure no major errors here,
+ // master process will do actual work anyway
+ //
+
+ I_NetListCanonicalize(NULL, P_implist, LIST_DELIMITER_STR_UI,
+ work_buf, sizeof(big_buf), &masters_count, NULL, 0,
+ (NAMETYPE_COMPUTER | OUTLIST_TYPE_API |
+ INLC_FLAGS_MULTIPLE_DELIMITERS));
+
+ //
+ // The ouptut of I_NetListCanon - masters_buf pointed by
+ // work_buf, has the list of masters computer names separated
+ // by spaces. For easier access each entry in pointer array
+ // master_list is set to one such master name, and each name
+ // is turned to ASCIIZ from.
+ //
+
+ p = strcpyf(P_implist, work_buf);
+
+ for (i = 0; i < masters_count; i++) {
+ master_list[i] = p;
+ if ((p = strchrf(p, ' ')) == NULL)
+ break;
+ *p = '\0';
+ p++;
+ }
+
+ } // P_implist != NULL
+ else
+ masters_count = 0;
+
+
+ if (P_imppath != NULL) {
+
+ unsigned long type = 0;
+
+ I_NetPathCanonicalize(NULL, P_imppath, work_buf, sizeof(big_buf),
+ NULL, &type, 0L);
+
+ if (type == ITYPE_PATH_RELND) {
+
+ //
+ // it is relative to Lanman root - must turn it to Absolute path
+ //
+
+ make_lanman_filename(work_buf, import_path);
+ } else
+ strcpyf(import_path, work_buf);
+
+ } // P_exppath != NULL
+ else /* take default (realtive to lanroot) */ {
+ make_lanman_filename((char * )DEFAULT_IMPORT_PATH, import_path);
+ }
+
+ //
+ // a path of "c:\" is an excepction - it ends with a slash,
+ // since anywhere it is being used a slash is appended to it
+ // we may simply "del" the slash upfront //
+ //
+
+
+ len = strlenf(import_path);
+
+ if ((import_path[len - 1] == '\\') || (import_path[len - 1] == '/'))
+ import_path[len - 1] = '\0';
+
+
+ P_imppath = (char * ) import_path;
+
+
+ if (P_passwd != NULL)
+ struprf(P_passwd);
+
+
+} // end of Parse
+
+
+
+
+
+// InstClientSigHand - Install the signal handling routine
+// for Client process.
+//
+
+void pascal
+InstClientSigHand(void)
+{
+ PFNSIGHANDLER prevaddr;
+ unsigned short prev;
+
+ //
+ // disable all other signals
+ //
+
+ DosSetSigHandler(NULL, &prevaddr, &prev, SIGA_IGNORE, SIG_CTRLC);
+ DosSetSigHandler(NULL, &prevaddr, &prev, SIGA_IGNORE, SIG_BROKENPIPE);
+ DosSetSigHandler(NULL, &prevaddr, &prev, SIGA_IGNORE, SIG_KILLPROCESS);
+ DosSetSigHandler(NULL, &prevaddr, &prev, SIGA_IGNORE, SIG_CTRLBREAK);
+ DosSetSigHandler(NULL, &prevaddr, &prev, SIGA_ERROR, SIG_PFLG_B);
+ DosSetSigHandler(NULL, &prevaddr, &prev, SIGA_ERROR, SIG_PFLG_C);
+
+ //
+ // install the Client's signal handler
+ //
+
+ DosSetSigHandler( (PFNSIGHANDLER) ClientSigHand, &prevaddr, &prev,
+ SIGA_ACCEPT, SERVICE_FLAG);
+ return;
+}
+
+
+
+
+//
+// ClientSigHand - Master's signal handling routine
+//
+// Parms:
+// opcode -- low byte
+// signum -- better be SIGA_FLG_A
+//
+
+void pascal
+ClientSigHand( opcode, signum)
+unsigned opcode;
+unsigned signum;
+{
+ PFNSIGHANDLER prevaddr;
+ unsigned short prev;
+
+ opcode &= 0xff;
+
+ if ((opcode == WONDER_ARG) || (signum = SIG_KILLPROCESS))
+
+ ClientExit(); /* gently remove itself and all desecendents from
+ the world - FIN */
+
+ //
+ // Reenable the signal handler
+ // if the signal is not uninstall
+ //
+
+ (void) DosSetSigHandler((PFNSIGHANDLER) ClientSigHand,
+ &prevaddr, &prev, SIGA_ACKNOWLEDGE, SERVICE_FLAG );
+}
+
+
diff --git a/private/net/svcdlls/repl/repltest/dispmail.c b/private/net/svcdlls/repl/repltest/dispmail.c
new file mode 100644
index 000000000..f3888bc0f
--- /dev/null
+++ b/private/net/svcdlls/repl/repltest/dispmail.c
@@ -0,0 +1,159 @@
+/*++
+
+******************************************************************
+* Microsoft LAN Manager *
+* Copyright(c) Microsoft Corp., 1987-1993 *
+******************************************************************
+
+
+Module : DisplMail.c (extracted from LM 2.x test2.c)
+
+BUGBUG: Assumes little-ending machine!
+
+--*/
+
+
+// These must be included first:
+
+#include <windows.h> // DWORD, etc.
+#include <lmcons.h> // NET_API_STATUS.
+
+// These may be included in any order:
+
+#include <netdebug.h> // NetpKdPrint(), FORMAT_ equates.
+#include "repldefs.h" // DIR_NOT_SUPPORTED, etc.
+#include <replpack.h> // LPPACK_MSG_HEADER, etc.
+#include <repltest.h> // DisplayPackedReplMailslotMsg().
+#include <stdio.h> // printf().
+
+
+VOID
+DisplayMessageType(
+ IN DWORD MessageType
+ )
+{
+#define TRY_TYPE( equate ) \
+ { \
+ if (MessageType == equate) { \
+ (VOID) printf( #equate ); \
+ goto Cleanup; \
+ } \
+ }
+
+ // Update messages:
+ TRY_TYPE( SYNC_MSG )
+ TRY_TYPE( GUARD_MSG )
+ TRY_TYPE( PULSE_MSG )
+
+ // Query messages:
+ TRY_TYPE( DIR_NOT_SUPPORTED )
+ TRY_TYPE( DIR_SUPPORTED )
+ TRY_TYPE( NOT_MASTER_DIR )
+ TRY_TYPE( MASTER_DIR )
+ TRY_TYPE( IS_DIR_SUPPORTED )
+ TRY_TYPE( IS_MASTER )
+ TRY_TYPE( DIR_COLLIDE )
+
+ // None of the above:
+ (VOID) printf( "UNEXPECTED MSG TYPE (" FORMAT_DWORD ")",
+ MessageType );
+
+Cleanup:
+ return;
+
+}
+
+
+VOID
+DisplayPackedReplMailslotMsg(
+ IN LPVOID Message,
+ IN DWORD MessageSize,
+ IN BOOL Verbose
+ )
+{
+ LPPACK_MSG_HEADER hmsg = (LPVOID) Message;
+ DWORD i;
+ WORD MessageType;
+ LPBYTE MessageByteArray = (LPVOID) Message;
+ LPPACK_QUERY_MSG qmsg;
+ LPPACK_SYNCMSG smsg;
+ LPPACK_MSG_STATUS_REC upd;
+
+ if (Verbose) {
+ (VOID) printf(
+ "Got message, size is " FORMAT_DWORD ".\n", MessageSize );
+ }
+
+ MessageType = hmsg->msg_type; // BUGBUG: Assumes little-endian!
+
+ (VOID) printf( "MSG type: " );
+ DisplayMessageType( MessageType );
+
+ switch (MessageType) {
+ case SYNC_MSG: /*FALLTHROUGH*/
+ case GUARD_MSG: /*FALLTHROUGH*/
+ case PULSE_MSG:
+
+ smsg = (LPVOID) Message;
+
+ (VOID) printf(
+ ", from: '" FORMAT_LPSTR "' in domain '" FORMAT_LPSTR "'",
+ smsg->header.sender, smsg->header.senders_domain );
+ if (Verbose) {
+ (VOID) printf(
+ ", COUNT: " FORMAT_DWORD ", rand: " FORMAT_DWORD
+ ", sync: " FORMAT_DWORD ", pulse: " FORMAT_DWORD
+ ", guard: " FORMAT_DWORD,
+ smsg->update_count, smsg->info.random,
+ smsg->info.sync_rate, smsg->info.pulse_rate,
+ smsg->info.guard_time);
+ }
+ (VOID) printf( "\n" );
+
+
+ upd = (LPVOID) (MessageByteArray + sizeof(*smsg));
+
+ for (i = 0; i < smsg->update_count; i++) {
+
+ LPSTR fnp =
+ (LPVOID) (MessageByteArray + (upd + i)->dir_name_offset);
+
+ (VOID) printf(
+ "DIR: " FORMAT_LPSTR ", code: " FORMAT_DWORD
+ ", cksum: " FORMAT_HEX_DWORD ", count: " FORMAT_DWORD
+ ", integ: " FORMAT_DWORD ", ext: " FORMAT_DWORD "\n",
+ fnp, (upd + i)->opcode,
+ (upd + i)->checksum, (upd + i)->count,
+ (upd + i)->integrity, (upd + i)->extent );
+ }
+
+ break;
+
+ case IS_DIR_SUPPORTED: /*FALLTHROUGH*/
+ case IS_MASTER: /*FALLTHROUGH*/
+ case DIR_NOT_SUPPORTED: /*FALLTHROUGH*/
+ case DIR_SUPPORTED: /*FALLTHROUGH*/
+ case NOT_MASTER_DIR: /*FALLTHROUGH*/
+ case MASTER_DIR: /*FALLTHROUGH*/
+ case DIR_COLLIDE:
+
+ qmsg = (LPVOID) Message;
+
+ (VOID) printf(
+ ", from: " FORMAT_LPSTR
+ ", DIR: " FORMAT_LPSTR "\n",
+ qmsg->header.sender,
+ qmsg->dir_name);
+
+ break;
+
+ default:
+
+ (VOID) printf( "UNEXPECTED MSG TYPE: " FORMAT_DWORD ".\n",
+ hmsg->msg_type );
+
+ break;
+
+ } // switch
+
+}
diff --git a/private/net/svcdlls/repl/repltest/fakefind.c b/private/net/svcdlls/repl/repltest/fakefind.c
new file mode 100644
index 000000000..8b9d16ae2
--- /dev/null
+++ b/private/net/svcdlls/repl/repltest/fakefind.c
@@ -0,0 +1,125 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ FakeFind.c
+
+Abstract:
+
+ BUGBUG
+
+Author:
+
+ JR (John Rogers, JohnRo@Microsoft) 06-Apr-1993
+
+Environment:
+
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+ 06-Apr-1993 JohnRo
+ Created.
+ 16-Apr-1993 JohnRo
+ Vastly improved handling of single files.
+ Added print of EA size.
+ 16-Apr-1993 JohnRo
+ Added prints of various time fields.
+ 20-Apr-1993 JohnRo
+ Display time in milliseconds too, as CompareFileTime has problems.
+ 07-May-1993 JohnRo
+ RAID 3258: file not updated due to ERROR_INVALID_USER_BUFFER.
+ 13-Jun-1993 JohnRo
+ RAID 13080: Allow repl between different timezones.
+ 15-Jun-1993 JohnRo
+ Extracted FakeFindData() for use by multiple test apps.
+
+--*/
+
+
+// These must be included first:
+
+#include <windows.h> // IN, LPTSTR, TRUE, etc.
+#include <lmcons.h> // NET_API_STATUS.
+
+// These may be included in any order:
+
+#include <assert.h> // assert().
+#include <filefind.h> // LPREPL_FIND_HANDLE, etc.
+#include <netdebug.h> // FORMAT_ equates.
+#include <repldefs.h> // ReplGetEaSize().
+#include <repltest.h> // My prototype, ShowTime().
+#include <stdio.h> // printf().
+#include <tstr.h> // STRCPY(), TCHAR_EOS, etc.
+
+
+VOID
+FakeFindData(
+ IN LPCTSTR FileName,
+ IN BOOL Verbose,
+ OUT LPREPL_WIN32_FIND_DATA FindData
+ )
+{
+ HANDLE FindHandle = INVALID_HANDLE_VALUE;
+ LPCTSTR LastPathSep;
+ LPCTSTR PartOfNameToCopy;
+
+ assert( FindData != NULL );
+
+ FindHandle = FindFirstFile(
+ (LPTSTR) FileName,
+ (LPWIN32_FIND_DATA) (LPVOID) FindData );
+
+ if (FindHandle == INVALID_HANDLE_VALUE) {
+ assert( FALSE ); // BUGBUG
+ goto Cleanup;
+ }
+
+ // Strip off dir names and backslashes.
+ LastPathSep = STRRCHR( FileName, TCHAR_BACKSLASH );
+ if (LastPathSep != NULL) {
+ PartOfNameToCopy = LastPathSep + 1;
+ } else {
+ PartOfNameToCopy = FileName;
+ }
+
+ // Strip off D: if that's there.
+ if (STRLEN( PartOfNameToCopy ) >= 2) {
+ if (PartOfNameToCopy[1] == TCHAR_COLON) {
+ PartOfNameToCopy = PartOfNameToCopy+2;
+ }
+ }
+
+ if (Verbose) {
+ (VOID) printf(
+ "File name is '" FORMAT_LPTSTR "'.\n", PartOfNameToCopy );
+
+ ShowTime( "access time (from dir)",
+ &(FindData->fdFound.ftLastAccessTime) );
+
+ ShowTime( "create time (from dir)",
+ &(FindData->fdFound.ftCreationTime) );
+
+ ShowTime( "write time (from dir)",
+ &(FindData->fdFound.ftLastWriteTime) );
+ }
+
+ (VOID) STRCPY(
+ FindData->fdFound.cFileName, // dest
+ PartOfNameToCopy ); // src
+
+ FindData->nEaSize = ReplGetEaSize( FileName );
+
+ if (Verbose) {
+ (VOID) printf( "EA size is " FORMAT_DWORD ".\n", FindData->nEaSize );
+ }
+
+Cleanup:
+
+ if (FindHandle != INVALID_HANDLE_VALUE) {
+ (VOID) FindClose( FindHandle );
+ }
+
+} // FakeFindData
diff --git a/private/net/svcdlls/repl/repltest/makefile b/private/net/svcdlls/repl/repltest/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/net/svcdlls/repl/repltest/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/net/svcdlls/repl/repltest/mas_pars.c b/private/net/svcdlls/repl/repltest/mas_pars.c
new file mode 100644
index 000000000..7289103b8
--- /dev/null
+++ b/private/net/svcdlls/repl/repltest/mas_pars.c
@@ -0,0 +1,278 @@
+/********************************************************************/
+/** Microsoft LAN Manager **/
+/** Copyright(c) Microsoft Corp., 1987-1991 **/
+/********************************************************************/
+
+/* This file conatins Master's Signal Handler and Installer
+* and function MasterParse, which is a
+* a watered down copy of function Parse (file parse.c), it uses
+* the same mechanisem for argument parsing (Cmdargs4), but does not
+* perform any checks since these had already been performed and
+* passed succesfully (otherwise we wouldnt get here). Some additional
+* processing is performed on lists to make them easier to access.
+*
+***********************************************************************/
+
+
+
+#define INCL_DOSPROCESS
+#define INCL_DOSSEMAPHORES
+#define INCL_DOSSIGNALS
+
+#define INCL_DOSERRORS
+#define INCL_DOSINFOSEG
+#include <os2.h>
+/*
+#include <limits.h>
+*/
+#include <netcons.h>
+#include <neterr.h>
+#include <netlib.h>
+#include <cmdargs.h>
+#include <netbios.h>
+#include <service.h>
+#include <errlog.h>
+#include <icanon.h>
+#include <apiutil.h>
+#include <swtchtxt.h>
+#include <replini.h>
+
+#include "repldefs.h"
+#include "iniparm.h"
+#include "master.h"
+
+//
+// OUTNAME is a macro which provides the output name (the name we
+// display) of a switch argument. For the current macros this is
+// the text, after skipping over the slash (one byte).
+//
+
+#define OUTNAME(x) (char *)(x+1)
+
+#define TRUE 1
+
+static const char rep_REPL[] = SW_REPL_REPL;
+static const char rep_EXPPATH[] = SW_REPL_EXPPATH;
+static const char rep_IMPPATH[] = SW_REPL_IMPPATH;
+static const char rep_EXPLIST[] = SW_REPL_EXPLIST;
+static const char rep_IMPLIST[] = SW_REPL_IMPLIST;
+static const char rep_TRYUSER[] = SW_REPL_TRYUSER;
+static const char rep_LOGON[] = SW_REPL_LOGON;
+static const char rep_PASSWD[] = SW_REPL_PASSWD;
+static const char rep_SYNC[] = SW_REPL_SYNCH;
+static const char rep_PULSE[] = SW_REPL_PULSE;
+static const char rep_GUARD[] = SW_REPL_GUARD;
+static const char rep_RANDOM[] = SW_REPL_RANDOM;
+
+
+char *P_repl = DEFAULT_REPL;
+char *P_exppath = DEFAULT_EXPPATH;
+char *P_imppath = DEFAULT_IMPPATH;
+char *P_explist = DEFAULT_EXPLIST;
+char *P_implist = DEFAULT_IMPLIST;
+int P_tryuser = DEFAULT_TRYUSER;
+char *P_logon = DEFAULT_LOGON;
+char *P_passwd = DEFAULT_PASSWD;
+int P_sync = DEFAULT_SYNC;
+int P_pulse = DEFAULT_PULSE;
+int P_guard = DEFAULT_GUARD;
+int P_random = DEFAULT_RANDOM;
+
+
+struct cmdfmt argfmt[] = {
+ { (char * )rep_REPL, CMDARG_TYPE_STRING, (char * ) & (P_repl), sizeof(rep_REPL) + 1, 0 },
+ { (char * )rep_EXPPATH, CMDARG_TYPE_STRING, (char * ) & (P_exppath), sizeof(rep_EXPPATH) + 1, 0 },
+ { (char * )rep_IMPPATH, CMDARG_TYPE_STRING, (char * ) & (P_imppath), sizeof(rep_IMPPATH) + 1, 0 },
+ { (char * )rep_EXPLIST, CMDARG_TYPE_STRING, (char * ) & (P_explist), sizeof(rep_EXPLIST) + 1, 0 },
+ { (char * )rep_IMPLIST, CMDARG_TYPE_STRING, (char * ) & (P_implist), sizeof(rep_IMPLIST) + 1, 0 },
+ { (char * )rep_TRYUSER, CMDARG_TYPE_YN, (char * ) & (P_tryuser), sizeof(rep_TRYUSER) + 1, 0 },
+ { (char * )rep_LOGON, CMDARG_TYPE_STRING, (char * ) & (P_logon), sizeof(rep_LOGON) + 1, 0 },
+ { (char * )rep_PASSWD, CMDARG_TYPE_STRING, (char * ) & (P_passwd), sizeof(rep_PASSWD) + 1, 0 },
+ { (char * )rep_SYNC, CMDARG_TYPE_INT, (char * ) & (P_sync), sizeof(rep_SYNC) + 1, 0 },
+ { (char * )rep_PULSE, CMDARG_TYPE_INT, (char * ) & (P_pulse), sizeof(rep_PULSE) + 1, 0 },
+ { (char * )rep_GUARD, CMDARG_TYPE_INT, (char * ) & (P_guard), sizeof(rep_GUARD) + 1, 0 },
+ { (char * )rep_RANDOM, CMDARG_TYPE_INT, (char * ) & (P_random), sizeof(rep_RANDOM) + 1, 0 }
+};
+
+
+
+#define FCOUNT (sizeof(argfmt) / sizeof(argfmt[0]))
+
+char *client_list[32]; /* each enrty points to a string conataining
+ a computer/domain name */
+unsigned short client_count;
+
+
+
+void
+MasterParse(argc, argv)
+/*********************
+*
+* Parse -- parse master's command line arguments
+**************************************************************************/
+
+
+int argc;
+char **argv;
+{
+ char *lastarg;
+
+
+ char big_buf[MAXPATHLEN*2]; /* space for master and client name list and
+ for export/import paths
+ (currently about 0.5K) */
+
+ char *work_buf;
+
+ //
+ // no need to check for errors this time round
+ //
+
+ GetCmdArgs4(argc, argv, 0, FCOUNT, argfmt, &lastarg);
+
+ work_buf = (char * )big_buf;
+
+ if (P_explist != NULL) {
+ int i;
+ char *p;
+ char t = ' ';
+
+ //
+ // Canonicalize the master's name list
+ //
+
+
+ I_NetListCanonicalize(NULL, P_explist, LIST_DELIMITER_STR_UI,
+ work_buf, sizeof(big_buf), &client_count,
+ NULL, 0, (NAMETYPE_COMPUTER | OUTLIST_TYPE_API |
+ INLC_FLAGS_MULTIPLE_DELIMITERS));
+
+ //
+ // The ouptut of I_NetListCanon - big_buf pointed by
+ //
+
+ // work_buf, has the list of clients computer names separated
+ // by spaces. To sav memory big_buf is copied back to P_explist
+ // (which is really part of argv and is stack space).
+ // The string copied back MUST be shorter as Cmdargs4 swallowed
+ // all leading switches (i.e "/MASTER = jojo" is now just "JOJO").
+
+ // For easier access, each entry in pointer array
+ // client_list is set to one such client name, and each name
+ // is turned to ASCIIZ from.
+
+
+ strcpyf(P_explist, work_buf);
+
+ p = P_explist;
+ for (i = 0; i < client_count; i++) {
+ client_list[i] = p;
+ if ((p = strchrf(p, t)) == NULL)
+ break;
+ *p = '\0';
+ p++;
+
+ }
+
+ } // P_explist == NULL
+ else
+ client_count = 0;
+
+ if (P_exppath != NULL) {
+ unsigned long type = 0;
+
+ I_NetPathCanonicalize(NULL, P_exppath, work_buf, sizeof(big_buf),
+ NULL, &type, 0L);
+
+ //
+ // again to save memory the export path is returned to P_exppath
+ //
+
+ if (type == ITYPE_PATH_RELND) {
+
+ //
+ // it is relative to Lanman root - must turn it to Absolute path
+ //
+
+ make_lanman_filename(work_buf, (char * ) export_path);
+ } else
+ strcpyf(export_path, work_buf);
+
+ } // P_exppath != NULL
+ else /* take default (realtive to lanroot) */ {
+ make_lanman_filename((char * )DEFAULT_EXPORT_PATH, export_path);
+ }
+
+ P_exppath = (char * ) export_path;
+
+} // end of Parse
+
+
+
+
+
+// InstMasterSigHand - Install the signal handling routine
+// for Master process.
+//
+
+void pascal
+InstMasterSigHand()
+{
+ PFNSIGHANDLER prevaddr;
+ unsigned short prev;
+
+ //
+ // disable all other signals
+ //
+
+ DosSetSigHandler(NULL, &prevaddr, &prev, SIGA_IGNORE, SIG_CTRLC);
+ DosSetSigHandler(NULL, &prevaddr, &prev, SIGA_IGNORE, SIG_BROKENPIPE);
+ DosSetSigHandler(NULL, &prevaddr, &prev, SIGA_IGNORE, SIG_KILLPROCESS);
+ DosSetSigHandler(NULL, &prevaddr, &prev, SIGA_IGNORE, SIG_CTRLBREAK);
+ DosSetSigHandler(NULL, &prevaddr, &prev, SIGA_ERROR, SIG_PFLG_B);
+ DosSetSigHandler(NULL, &prevaddr, &prev, SIGA_ERROR, SIG_PFLG_C);
+
+ //
+ // install the Master's signal handler
+ //
+
+ (void) DosSetSigHandler( (PFNSIGHANDLER) MasterSigHand, &prevaddr, &prev,
+ SIGA_ACCEPT, SERVICE_FLAG);
+ return;
+}
+
+
+
+//
+// MasterSigHand - Master's signal handling routine
+//
+// Parms:
+// opcode -- low byte
+// signum -- better be SIGA_FLG_A
+//
+
+void pascal
+MasterSigHand( opcode, signum)
+unsigned opcode;
+unsigned signum;
+{
+ PFNSIGHANDLER prevaddr;
+ unsigned short prev;
+
+ opcode &= 0xff;
+ if ((opcode == WONDER_ARG) || (signum = SIG_KILLPROCESS))
+
+ MasterExit(); /* gently remove itself and all desecendents from
+ the world - FIN */
+
+ //
+ // Reenable the signal handler
+ // if the signal is not uninstall
+ //
+
+ (void) DosSetSigHandler((PFNSIGHANDLER) MasterSigHand,
+ &prevaddr, &prev, SIGA_ACKNOWLEDGE, SERVICE_FLAG );
+
+}
+
+
diff --git a/private/net/svcdlls/repl/repltest/mtest1.c b/private/net/svcdlls/repl/repltest/mtest1.c
new file mode 100644
index 000000000..b9502702a
--- /dev/null
+++ b/private/net/svcdlls/repl/repltest/mtest1.c
@@ -0,0 +1,119 @@
+///*****************************************************************
+/// Microsoft LAN Manager *
+/// Copyright(c) Microsoft Corp., 1987-1990 *
+///*****************************************************************
+
+
+// Module : mtest1.c
+*
+*
+***************************************************************************//
+#define INCL_DOSPROCESS
+#define INCL_DOSERRORS
+#define INCL_DOSINFOSEG
+#include <os2.h>
+
+
+#include <netcons.h>
+#include <netlib.h>
+#include <mailslot.h>
+#include "repldefs.h"
+#include "test.h"
+
+#define FOREVER 1
+
+char slot_name[] = MASTER_SLOT_NAME;
+
+void main(int, char*[]);
+
+char msg_buf[sizeof(struct query_msg)];
+
+void
+main(argc, argv)
+//*************
+*
+*
+*************************************************************************//
+
+int argc;
+char *argv[];
+{
+ struct sync_msg * msg;
+
+ struct query_msg *msgp;
+ char destin[FULL_SLOT_NAME_SIZE];
+ char * dest_p;
+ int err;
+
+
+ dest_p = (char * )destin;
+
+ //
+ // stick in leading double slashes fro computer name
+ //
+
+ strcpyf(dest_p, (char * ) "\\\\YUVALN1");
+ strcpyf((char * ) (dest_p + strlenf(dest_p)),
+ (char * ) MASTER_SLOT_NAME);
+
+ msgp = (struct query_msg *) msg_buf;
+
+ strcpyf((char * )msgp->header.sender, (char * ) "YUVALN1");
+
+
+
+ //
+ // 1
+ //
+
+ strcpyf((char * )msgp->dir_name, (char * ) "SERVICES");
+ msgp->header.msg_type = IS_DIR_SUPPORTED;
+
+
+ if (err = DosWriteMailslot((char * ) dest_p, (char * ) msg_buf,
+ sizeof (msg_buf), HI_PRIO, SECOND_CLASS, MAIL_WRITE_WAIT))
+ nprintf("MTEST1 ERR after DosWriteMailslot, err = %d\n", err);
+
+ //
+ // 2
+ //
+
+
+
+ msgp->header.msg_type = IS_MASTER;
+ if (err = DosWriteMailslot((char * ) dest_p, (char * ) msg_buf,
+ sizeof (msg_buf), HI_PRIO, SECOND_CLASS, MAIL_WRITE_WAIT))
+ nprintf("MTEST1 ERR after DosWriteMailslot, err = %d\n", err);
+
+
+
+ //
+ // 3
+ //
+
+
+ msgp->header.msg_type = IS_DIR_SUPPORTED;
+ strcpyf((char * )msgp->dir_name, (char * ) "aaaaaaaa");
+
+ if (err = DosWriteMailslot((char * ) dest_p, (char * ) msg_buf,
+ sizeof (msg_buf), HI_PRIO, SECOND_CLASS, MAIL_WRITE_WAIT))
+ nprintf("MTEST1 ERR after DosWriteMailslot, err = %d\n", err);
+
+ //
+ // 4
+ //
+
+
+ msgp->header.msg_type = IS_MASTER;
+ if (err = DosWriteMailslot((char * ) dest_p, (char * ) msg_buf,
+ sizeof (msg_buf), HI_PRIO, SECOND_CLASS, MAIL_WRITE_WAIT))
+
+ nprintf("MTEST1 ERR after DosWriteMailslot, err = %d\n", err);
+
+
+ argc = argc;
+ ++ * argv;
+
+}
+
+
diff --git a/private/net/svcdlls/repl/repltest/mtest2.c b/private/net/svcdlls/repl/repltest/mtest2.c
new file mode 100644
index 000000000..85d027d89
--- /dev/null
+++ b/private/net/svcdlls/repl/repltest/mtest2.c
@@ -0,0 +1,253 @@
+///*****************************************************************
+/// Microsoft LAN Manager *
+/// Copyright(c) Microsoft Corp., 1987-1990 *
+///*****************************************************************
+
+
+// Module : mtest2.c
+*
+*
+***************************************************************************//
+#define INCL_DOSMEMMGR
+#define INCL_DOSFILEMGR
+#define INCL_DOSERRORS
+#define INCL_DOSINFOSEG
+#include <os2.h>
+
+
+#include <netcons.h>
+#include <netlib.h>
+#include <srvparam.h>
+#include "repldefs.h"
+#include "test.h"
+
+// DosFindFirst attributes
+#define FILE_NORMAL 0x0000
+#define FILE_READONLY 0x0001
+#define FILE_HIDDEN 0x0002
+#define FILE_SYSTEM 0x0004
+#define FILE_DIR 0x0010
+#define FILE_ARCHIVE 0x0020
+
+#define FILE_ALL (FILE_NORMAL | FILE_READONLY | FILE_HIDDEN | FILE_SYSTEM | FILE_DIR | FILE_ARCHIVE)
+
+#define FN_OFFSET (sizeof(FILEFINDBUF) - CCHMAXPATHCOMP)
+
+#define STAR_DOT_C "*.c"
+#define SLASH "\\"
+
+#define MAX_FILES_IN_DIR 200
+
+#define PATH "c:\\lanman12\\services\\tmp\\*.*"
+
+void CreateSortArray(unsigned[], unsigned, char *);
+void ReplSort(unsigned[], unsigned, char *);
+int compfn(unsigned, unsigned, char *);
+
+void main(int, char*[]);
+
+void
+main(argc, argv)
+//*************
+*
+*
+*************************************************************************//
+
+int argc;
+char *argv[];
+{
+ FILEFINDBUF search_buf;
+ char path_buf[MAXPATHLEN];
+ unsigned shan = -1;
+ int scnt = 1;
+ unsigned retval = 0;
+ char *import_path;
+ unsigned offset_array[MAX_FILES_IN_DIR];
+ unsigned search_flag;
+ unsigned client_dir_cnt;
+ unsigned client_sel;
+ char * client_seg;
+ int err, i;
+
+
+ import_path = (char * )path_buf;
+ strcpyf(import_path, (char * )PATH);
+
+ search_flag = FILE_NORMAL | FILE_READONLY | FILE_HIDDEN | FILE_SYSTEM;
+
+ //
+ // search_flag = FILE_ALL;
+ //
+
+ if (err = DosFindFirst(import_path, (unsigned short *)&shan,
+ search_flag, (PFILEFINDBUF)&search_buf, sizeof(search_buf),
+ (unsigned short *)&client_dir_cnt, 0L))
+
+ {
+
+
+ AlertLogExit(ALERT_ReplSysErr, NELOG_ReplSysErr, err, NULL, NULL, EXIT);
+ return 1;
+
+ nprintf("DosFinfFirst error - %d\n", err);
+
+ }
+
+
+ if (client_dir_cnt > MAX_FILES_IN_DIR)
+ {
+ AlertLogExit(ALERT_ReplTooManyFiles, NERLOG_ReplTooManyFiles, 0,
+ (char *)import_path, NULL, NO_EXIT);
+
+ nprintf("TOO MANY files - %d\n", client_dir_cnt);
+ }
+//
+
+ client_dir_cnt = MAX_FILES_IN_DIR;
+
+ if (err = DosAllocSeg ((client_dir_cnt * sizeof(FILEFINDBUF)),
+ (PSEL) & client_sel, 0))
+
+ //
+
+ AlertLogExit(ALERT_ReplSysErr, NELOG_ReplSysErr, err, NULL, NULL, EXIT);
+
+ //
+
+ nprintf("DosAllocSeg error - %d\n", err);
+ else
+ client_seg = (char * )SOTOFAR (client_sel, 0);
+
+ DosFindClose(shan);
+
+
+ shan = -1;
+
+
+ if (err = DosFindFirst(import_path, (unsigned short * ) & shan,
+ search_flag, (PFILEFINDBUF)client_seg,
+ (client_dir_cnt * sizeof(FILEFINDBUF)),
+ (unsigned short * ) & client_dir_cnt, 0L)) {
+
+ //
+
+ AlertLogExit(ALERT_ReplSysErr, NELOG_ReplSysErr, err, NULL, NULL, EXIT);
+ return 1;
+
+ //
+
+ nprintf("DosFinfFirst error - %d\n", err);
+ }
+
+
+
+ DosFindClose(shan);
+
+
+ CreateSortArray(offset_array, client_dir_cnt, client_seg);
+
+ for (i = 0; i < client_dir_cnt ; i++)
+ nprintf("%Fs\n", (client_seg + offset_array[i]));
+
+ argc = argc;
+ ++ * argv;
+
+}
+
+
+
+
+
+void CreateSortArray(array, num, buf)
+//********************************
+*
+* Creates an offset array where each entry has the offset within buf
+* of the next FILEFINDBUF enrty. It then sorts the array offset according
+* to file name, using ReplSort.
+*
+* FDATE fdateCreation; unsigned short ++ Create date
+* FTIME ftimeCreation; unsigned short ++ Create time
+* FDATE fdateLastAccess; unsigned short _access date
+* FTIME ftimeLastAccess; unsigned short _access time
+* FDATE fdateLastWrite; unsigned short ++ _write date
+* FTIME ftimeLastWrite; unsigned short ++ _write time
+* ULONG cbFile; long ++ file size
+* ULONG cbFileAlloc; long allocated size
+* USHORT attrFile; unsigned short ++ file attributes
+* UCHAR cchName; unsigned char name len
+* CHAR achName[CCHMAXPATHCOMP]; unsigned char ++ file name
+*
+* EXIT: retuns Checksum which*
+*
+* *
+**********************************************************************//
+unsigned array[];
+unsigned num;
+char * buf;
+{
+ unsigned offset;
+ char *buf_p;
+ int i;
+ PFILEFINDBUF filebuf_p;
+
+ buf_p = buf;
+
+ for (i = 0; i < num ; i++) {
+ array[i] = buf_p - buf + FN_OFFSET;
+
+ filebuf_p = (PFILEFINDBUF)buf_p;
+ offset = sizeof(FILEFINDBUF) - CCHMAXPATHCOMP
+ + filebuf_p->cchName + 1;
+ buf_p = buf_p + offset;
+ }
+
+ ReplSort(array, num, buf);
+}
+
+
+
+
+void ReplSort (base, num, buf_p)
+//*******************************
+* ShellSort -- qsort interface but with ptr
+*
+**********************************************//
+
+unsigned base[]; // array of offsets within buf_p
+unsigned num; // # of entries in buf_p
+char *buf_p; // where actual data is
+{
+ unsigned inc; // diminishing increment
+
+ //
+ // use Knuth formula h(k-1) = 2*h(k) +1
+ //
+
+ unsigned temp; // holds current value
+
+ int i, j;
+
+
+ //
+ // compute starting inc
+ //
+
+ inc = 1;
+ while (inc < num)
+ inc = 2 * inc + 1;
+ inc = (inc - 1) / 2;
+
+ for (; inc != 0 ; inc = (inc - 1) / 2) {
+ for (i = inc; i < num; i++) {
+ temp = base[i];
+ j = i - inc;
+ while (j >= 0 && strcmpf((buf_p + temp), (buf_p + base[j])) < 0 ) {
+ base[j+inc] = base[j];
+ j -= inc;
+ }
+ base[j+inc] = temp;
+ }
+ }
+}
+
+
diff --git a/private/net/svcdlls/repl/repltest/queryea.c b/private/net/svcdlls/repl/repltest/queryea.c
new file mode 100644
index 000000000..bd045d559
--- /dev/null
+++ b/private/net/svcdlls/repl/repltest/queryea.c
@@ -0,0 +1,372 @@
+/*++
+
+Copyright (c) 1991-1993 Microsoft Corporation
+
+Module Name:
+
+ QueryEA.c
+
+Abstract:
+
+ Test NT EA handling.
+
+Author:
+
+ MadanA 16-Mar-1991
+
+Revision History:
+
+ 12-Dec-1991 MadanA
+ Created.
+ 06-Jan-1993 JohnRo
+ Massive surgery; work with _stdcall; added this block of comments.
+ 22-Feb-1993 JohnRo
+ Fix infinite loop on two or more EAs.
+ Dump entire array in the EA buffer, and compute EA size too.
+ 07-May-1993 JohnRo
+ RAID 3258: file not updated due to ERROR_INVALID_USER_BUFFER.
+ Corrected DumpEaBuffer's computation of size.
+
+--*/
+
+
+// These must be included first:
+
+#include <nt.h> // NT definitions
+#include <ntrtl.h> // NT runtime library definitions
+#include <nturtl.h>
+#include <windows.h>
+#include <lmcons.h> // Needed by NetDebug.h, etc.
+
+// These may be included in any order:
+
+#include <assert.h> // assert().
+#include <netdebug.h> // NetpAssert(), FORMAT_ equates.
+#include <repldefs.h> // ReplGetEaSize(), ReplGlobalTrace, REPL_DEBUG_ALL.
+#include <stdio.h> // printf().
+#include <stdlib.h> // EXIT_FAILURE, EXIT_SUCCESS, _CRTAPI1.
+#include <string.h> // _stricmp().
+#include <tstring.h> // NetpAlloc{type}From{type}, etc.
+
+
+#define BUFFER_SIZE 1024
+
+
+#define MY_ACCESS_DESIRED ( FILE_READ_DATA | FILE_READ_EA \
+ | FILE_TRAVERSE \
+ | SYNCHRONIZE | FILE_READ_ATTRIBUTES )
+
+DBGSTATIC VOID
+Usage(
+ VOID
+ )
+{
+ (VOID) printf("Usage: QueryEA [-v] filename\n");
+}
+
+
+DBGSTATIC VOID
+DumpEaBuffer(
+ IN PBYTE EaBuffer
+ )
+{
+ PFILE_FULL_EA_INFORMATION Ea;
+ DWORD DosEaSize = 0;
+
+ Ea = (PFILE_FULL_EA_INFORMATION) EaBuffer;
+ NetpAssert( Ea != NULL );
+
+ while( Ea != NULL ) {
+
+ (VOID) printf( "EA name length = " FORMAT_ULONG ".\n", Ea->EaNameLength );
+ (VOID) printf( "EA value length = " FORMAT_ULONG ".\n", Ea->EaValueLength );
+ DosEaSize += EA_MIN_SIZE; // fixed overhead for DOS FEA structure.
+
+ DosEaSize += Ea->EaNameLength + 1;
+ DosEaSize += Ea->EaValueLength;
+
+ if( Ea->NextEntryOffset == 0 ) {
+
+ // no more EA
+
+ Ea = NULL;
+
+ } else {
+
+ // to next EA structure
+
+ Ea = (PVOID) (((PBYTE) (PVOID) Ea) + Ea->NextEntryOffset);
+
+ }
+
+ }
+
+ DosEaSize += EA_MIN_SIZE; // Overhead for offset of 0 (marks end of list).
+
+
+ (VOID) printf( "Final EA size is " FORMAT_DWORD ".\n", DosEaSize );
+
+} // DumpEaBuffer
+
+DBGSTATIC DWORD
+EaSizeAccordingToNt(
+ IN LPCWSTR UnicodeFileName
+ )
+{
+
+ FILE_EA_INFORMATION EaInfo;
+ DWORD EaSize = EA_MIN_SIZE; // initial value to return on err
+ HANDLE FileHandle = INVALID_HANDLE_VALUE;
+ UNICODE_STRING FileName;
+ IO_STATUS_BLOCK IoStatusBlock;
+ NTSTATUS NtStatus;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+
+ assert( UnicodeFileName != NULL );
+ assert( (*UnicodeFileName) != L'\0' );
+
+ if( !RtlDosPathNameToNtPathName_U(
+ UnicodeFileName,
+ &FileName,
+ NULL,
+ NULL
+ ) ) {
+
+ (VOID) printf("EaSizeAccordingToNt: Could not convert DOS path to NT path\n");
+ goto Cleanup;
+ }
+ // BUGBUG: memory leak.
+
+ InitializeObjectAttributes(
+ &ObjectAttributes,
+ &FileName,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL );
+
+ NtStatus = NtOpenFile(
+ &FileHandle,
+ MY_ACCESS_DESIRED,
+ &ObjectAttributes,
+ &IoStatusBlock,
+ FILE_SHARE_READ,
+ FILE_SYNCHRONOUS_IO_NONALERT ); // open options
+
+ if (! NT_SUCCESS(NtStatus)) {
+ (VOID) printf(
+ "EaSizeAccordingToNt: NtOpenFile " FORMAT_LPWSTR " failed: "
+ FORMAT_NTSTATUS "\n",
+ UnicodeFileName, NtStatus);
+ goto Cleanup;
+ } else {
+ (VOID) printf("EaSizeAccordingToNt: Succeeded in opening dir.\n");
+ }
+
+ NtStatus = NtQueryInformationFile(
+ FileHandle,
+ &IoStatusBlock,
+ &EaInfo,
+ sizeof(FILE_EA_INFORMATION),
+ FileEaInformation ); // information class
+
+ if ( !NT_SUCCESS( NtStatus ) ) {
+
+ (VOID) printf(
+ "EaSizeAccordingToNt: NtQueryInformationFile for "
+ FORMAT_LPWSTR " FAILED, NtStatus="
+ FORMAT_NTSTATUS ", iosb.info=" FORMAT_ULONG "\n",
+ UnicodeFileName, NtStatus, IoStatusBlock.Information );
+ goto Cleanup;
+ }
+
+ EaSize = EaInfo.EaSize;
+
+Cleanup:
+
+ (VOID) printf(
+ "EaSizeAccordingToNt: returning EA size " FORMAT_DWORD
+ " for " FORMAT_LPWSTR ", final status " FORMAT_NTSTATUS "\n",
+ EaSize, UnicodeFileName, NtStatus);
+
+ if (FileHandle != INVALID_HANDLE_VALUE) {
+ (void) NtClose(FileHandle);
+ }
+
+ return (EaSize);
+
+} // EaSizeAccordingToNt
+
+
+int _CRTAPI1
+main(
+ IN int argc,
+ IN char * argv[]
+ )
+{
+
+ NTSTATUS ntstatus;
+
+ HANDLE FileHandle;
+ LPSTR FileNameA = NULL;
+ UNICODE_STRING FileName;
+
+ IO_STATUS_BLOCK IoStatusBlock;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+
+ BYTE RawEaBuffer[BUFFER_SIZE];
+ LPWSTR UnicodeFileName;
+
+
+ //
+ // Parse command-line arguments.
+ //
+
+ if ( (argc < 2) || (argc > 3) ) {
+ Usage();
+ return (EXIT_FAILURE);
+ } else if (argc == 3) {
+ if ( (_stricmp(argv[1], "-v")==0) || (_stricmp(argv[1], "/v")==0) ) {
+ ReplGlobalTrace = REPL_DEBUG_ALL;
+ FileNameA = argv[2];
+ } else {
+ Usage();
+ return (EXIT_FAILURE);
+ }
+ } else {
+ FileNameA = argv[1];
+ }
+ assert( FileNameA != NULL );
+
+ //
+ // Do some output and call various new routines to get EA size.
+ //
+
+ (VOID) printf("FileName = " FORMAT_LPSTR " \n", FileNameA );
+
+ UnicodeFileName = NetpAllocWStrFromStr( FileNameA );
+ NetpAssert( UnicodeFileName != NULL );
+
+ (VOID) printf(
+ "EA size according to NT API: " FORMAT_DWORD ".\n\n",
+ EaSizeAccordingToNt( UnicodeFileName ) );
+
+ (VOID) printf(
+ "EA size according to repl routine: " FORMAT_DWORD ".\n\n",
+ ReplGetEaSize( UnicodeFileName ) );
+
+ //
+ // OK, now compute the same thing the old way.
+ //
+
+ if( !RtlDosPathNameToNtPathName_U(
+ UnicodeFileName,
+ &FileName,
+ NULL,
+ NULL
+ ) ) {
+
+ (VOID) printf("Could not convert DOS path to NT path\n");
+ return (EXIT_FAILURE);
+ }
+ // BUGBUG: memory leak.
+
+ InitializeObjectAttributes(
+ &ObjectAttributes,
+ &FileName,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL );
+
+ ntstatus = NtOpenFile(
+ &FileHandle,
+ MY_ACCESS_DESIRED,
+ &ObjectAttributes,
+ &IoStatusBlock,
+ FILE_SHARE_READ,
+ FILE_SYNCHRONOUS_IO_NONALERT ); // open options
+
+ if (! NT_SUCCESS(ntstatus)) {
+ (VOID) printf(
+ "NtOpenFile " FORMAT_LPSTR " failed: " FORMAT_NTSTATUS "\n",
+ FileNameA, ntstatus);
+ return (EXIT_FAILURE);
+ } else {
+ (VOID) printf("Succeeded in opening dir.\n");
+ }
+
+
+
+ if ( NT_SUCCESS( ntstatus = NtQueryEaFile(FileHandle,
+ &IoStatusBlock,
+ RawEaBuffer,
+ BUFFER_SIZE,
+ FALSE,
+ NULL,
+ 0,
+ NULL,
+ TRUE
+ ) ) ) {
+
+ (VOID) printf(
+ "NtQueryEaFile for " FORMAT_LPSTR " succeeded, ntstatus="
+ FORMAT_NTSTATUS ", iosb.info=" FORMAT_ULONG "\n",
+ FileNameA, ntstatus, IoStatusBlock.Information );
+ NetpAssert( IoStatusBlock.Information != 0 );
+
+// NetpDbgHexDump( RawEaBuffer, BUFFER_SIZE );
+
+ DumpEaBuffer( (LPVOID) RawEaBuffer );
+
+ while( NT_SUCCESS( ntstatus = NtQueryEaFile(FileHandle,
+ &IoStatusBlock,
+ RawEaBuffer,
+ BUFFER_SIZE,
+ FALSE,
+ NULL,
+ 0,
+ NULL,
+ FALSE
+ ) ) ) {
+
+
+ (VOID) printf(
+ "NtQueryEaFile for " FORMAT_LPSTR " succeeded "
+ FORMAT_NTSTATUS "\n",
+ FileNameA, ntstatus);
+
+// NetpDbgHexDump( RawEaBuffer, BUFFER_SIZE );
+
+ // Check for redir's way of indicating no more EAs.
+ if (IoStatusBlock.Information == 0) {
+ break;
+ }
+
+ DumpEaBuffer( (LPVOID) RawEaBuffer );
+
+ }
+
+ if( ( ntstatus != STATUS_NO_EAS_ON_FILE )
+ && ( ntstatus != STATUS_NO_MORE_EAS )
+ && ( ntstatus != STATUS_SUCCESS ) ) {
+
+ (VOID) printf(
+ "NtQueryEaFile for " FORMAT_LPSTR " FAILED, NT status="
+ FORMAT_NTSTATUS "\n", FileNameA, ntstatus );
+ }
+
+ }
+
+ if (ntstatus == STATUS_NO_EAS_ON_FILE) {
+ (VOID) printf(
+ "No EAs for file, so EA size would be " FORMAT_DWORD ".\n",
+ EA_MIN_SIZE );
+ }
+
+ (VOID) printf(
+ "NtQueryEaFile for " FORMAT_LPSTR " final status " FORMAT_NTSTATUS
+ "\n", FileNameA, ntstatus);
+
+ (void) NtClose(FileHandle);
+ return (EXIT_SUCCESS);
+
+} // main
diff --git a/private/net/svcdlls/repl/repltest/repldel.c b/private/net/svcdlls/repl/repltest/repldel.c
new file mode 100644
index 000000000..da1434cb1
--- /dev/null
+++ b/private/net/svcdlls/repl/repltest/repldel.c
@@ -0,0 +1,165 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ ReplDel.c
+
+Abstract:
+
+ Test app - tests ReplDeleteFile().
+
+Author:
+
+ JR (John Rogers, JohnRo@Microsoft) 22-Apr-1993
+
+Environment:
+
+ User Mode - Win32
+ Portable to any flat, 32-bit environment. (Uses Win32 typedefs.)
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+ 22-Apr-1993 JohnRo
+ Created for RAID 7313: repl needs change permission to work on NTFS,
+ or we need to delete files differently.
+ 22-Apr-1993 JohnRo
+ Added -v (verbose) command-line argument.
+
+--*/
+
+
+// These must be included first:
+
+#include <windows.h> // IN, GetLastError(), LPCTSTR, OPTIONAL, etc.
+#include <lmcons.h> // NET_API_STATUS.
+
+// These may be included in any order:
+
+#include <assert.h> // assert().
+#include <netdebug.h> // DBGSTATIC.
+#include <netlib.h> // NetpMemoryFree(), etc
+#include <repldefs.h> // ReplDeleteFile().
+#include <stdio.h> // printf(), etc.
+#include <stdlib.h> // EXIT_FAILURE, EXIT_SUCCESS, _CRTAPI1.
+#include <tstring.h> // NetpAllocWStrFromStr(), TCHAR_EOS.
+#include <winerror.h> // ERROR_, NO_ERROR equates.
+
+
+DBGSTATIC VOID
+Usage(
+ IN char * ProgName
+ )
+{
+ (VOID) printf(
+ "This program deletes a file, using replicator routines.\n\n"
+ "Author: JR (John Rogers, JohnRo@Microsoft)\n\n"
+ "Usage: %s [opts] targetFile\n\n"
+ "Where opts are:\n"
+// " -d delete a directory\n",
+ " -v verbose\n",
+ ProgName);
+}
+
+int _CRTAPI1
+main(
+ IN int argc,
+ IN char *argv[]
+ )
+{
+ NET_API_STATUS ApiStatus;
+ int ArgNumber;
+ BOOL DoingFiles = TRUE;
+ BOOL GotPermission = FALSE;
+ LPWSTR PathName = NULL;
+
+ //
+ // Process command-line arguments.
+ //
+
+ for (ArgNumber = 1; ArgNumber < argc; ArgNumber++) {
+ if ((*argv[ArgNumber] == '-') || (*argv[ArgNumber] == '/')) {
+ switch (tolower(*(argv[ArgNumber]+1))) // Process switches
+ {
+
+ case 'd' : // Delete directory.
+ DoingFiles = FALSE;
+ break;
+
+ case 'v' :
+ ReplGlobalTrace = REPL_DEBUG_ALL;
+ break;
+
+ default :
+ Usage( argv[0] );
+ ApiStatus = ERROR_INVALID_PARAMETER;
+ goto Cleanup;
+ }
+ } else { // Must be a file name.
+ if (PathName == NULL) {
+ PathName = NetpAllocWStrFromStr(argv[ArgNumber]);
+ assert( PathName != NULL );
+ } else {
+ Usage( argv[0] ); // Too many file names.
+ ApiStatus = ERROR_INVALID_PARAMETER;
+ goto Cleanup;
+ }
+ }
+ }
+
+ if (PathName == NULL) { // not enough file names
+ Usage( argv[0] );
+ ApiStatus = ERROR_INVALID_PARAMETER;
+ goto Cleanup;
+ }
+
+ assert( PathName != NULL );
+ assert( (*PathName) != TCHAR_EOS );
+
+ //
+ // Init and enable the backup/restore permissions in our process token.
+ //
+
+ ApiStatus = ReplInitBackupPermission();
+ (VOID) printf( "Back from ReplInitBackupPermission, status "
+ FORMAT_API_STATUS ".\n", ApiStatus );
+
+ ApiStatus = ReplEnableBackupPermission();
+ (VOID) printf( "Back from ReplEnableBackupPermission, status "
+ FORMAT_API_STATUS ".\n", ApiStatus );
+
+ GotPermission = TRUE;
+
+ //
+ // Delete the file or directory.
+ //
+
+ if (DoingFiles) {
+ ApiStatus = ReplDeleteFile( PathName );
+#if 0
+ } else {
+ ApiStatus = ReplDeleteDirectoryItself( PathName );
+#endif
+ }
+
+ (VOID) printf(
+ "Removing " FORMAT_LPWSTR " gives " FORMAT_API_STATUS ".\n",
+ PathName, ApiStatus );
+
+
+Cleanup:
+ if (GotPermission) {
+ (VOID) ReplDisableBackupPermission();
+ }
+ if (PathName != NULL) {
+ NetpMemoryFree( PathName );
+ }
+
+ if (ApiStatus == NO_ERROR) {
+ return (EXIT_SUCCESS);
+ } else {
+ return (EXIT_FAILURE);
+ }
+}
diff --git a/private/net/svcdlls/repl/repltest/repldir.c b/private/net/svcdlls/repl/repltest/repldir.c
new file mode 100644
index 000000000..d21bd5104
--- /dev/null
+++ b/private/net/svcdlls/repl/repltest/repldir.c
@@ -0,0 +1,69 @@
+/* Copyright (c) 1992-1993 Microsoft Corporation */
+/* Author: JR (John Rogers, JohnRo@Microsoft) */
+
+#include <assert.h> /* assert(). */
+#include <sys/stat.h> /* _stat(), struct _stat. */
+#include <stdio.h> /* printf(). */
+#include <stdlib.h> /* errno, EXIT_FAILURE, EXIT_SUCCESS, NULL */
+#include <time.h> /* ctime(). */
+
+#ifdef _CRTAPI1
+// NT:
+#define MAINTYPE _CRTAPI1
+#else
+// OS/2, UNIX:
+#define MAINTYPE
+#endif
+
+
+void
+ShowTime(
+ char * Comment,
+ time_t Time
+ )
+{
+ char * TimeStringPtr;
+
+ TimeStringPtr = ctime( &Time );
+ if (TimeStringPtr == NULL) {
+ // 1234567890123456789012345
+ TimeStringPtr = "*********INVALID********\n";
+ }
+ // TimeStringPtr points to str ending with "\n\0".
+
+ (void) printf( "%s: %s", Comment, TimeStringPtr );
+
+} // ShowTime
+
+
+int MAINTYPE
+main (
+ int argc,
+ char * argv[]
+ )
+
+{
+ int ErrorNumber = 0;
+ char * FileName = argv[1];
+ struct _stat StatBuffer;
+
+ assert( FileName != NULL );
+
+ if ( _stat( FileName, &StatBuffer ) ) {
+ ErrorNumber = errno;
+ assert( ErrorNumber != 0 );
+ (void) printf( "stat func failed %d\n", ErrorNumber );
+ goto Cleanup;
+ }
+
+ ShowTime( "mod time", StatBuffer.st_mtime );
+ ShowTime( "chg time", StatBuffer.st_ctime );
+
+Cleanup:
+ if (ErrorNumber == 0) {
+ return (EXIT_SUCCESS);
+ } else {
+ return (EXIT_FAILURE);
+ }
+
+} // main
diff --git a/private/net/svcdlls/repl/repltest/replmain.c b/private/net/svcdlls/repl/repltest/replmain.c
new file mode 100644
index 000000000..db14989b2
--- /dev/null
+++ b/private/net/svcdlls/repl/repltest/replmain.c
@@ -0,0 +1,106 @@
+/*++
+
+Copyright (c) 1991-92 Microsoft Corporation
+
+Module Name:
+
+ ReplMain.c
+
+Abstract:
+
+ Startup program to test repl service.
+
+Author:
+
+ 11/19/91 madana
+
+Revision History:
+
+ 12-Jan-1992 JohnRo
+ Added debug output; try direct call to ReplMain().
+ Deleted tabs in source file.
+ Get ReplMain's prototype from a header file.
+ 13-Jan-1992 JohnRo
+ Fix parsing error.
+ 27-Jan-1992 JohnRo
+ Arg passing still wasn't right.
+ 17-Feb-1992 JohnRo
+ Initialize the RPC server.
+ 04-Mar-1992 JohnRo
+ Changed ReplMain's interface to match new service controller.
+ 19-Mar-1992 JohnRo
+ Fixed bug where RPC stuff was being stopped too soon.
+ 31-Mar-1992 JohnRo
+ Service controller changed parameter list format.
+ 05-May-1992 JohnRo
+ Avoid internal compiler error (initializing static struct).
+
+--*/
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windef.h>
+#include <winbase.h>
+
+#include <lmcons.h>
+#include <rpc.h> // Needed by <rpcutil.h>.
+
+#include <lmsname.h> // SERVICE_REPL.
+#include <netdebug.h> // NetpKdPrint(()), FORMAT_ equates
+#include <repl.h> // repl_ServerIfHandle.
+#include <repldefs.h> // ReplMain().
+#include <rpcutil.h> // NetpInitRpcServer().
+#include <winsvc.h> // SERVICE_TABLE_ENTRY.
+
+
+#if 0
+SERVICE_TABLE_ENTRY ReplDispatchTable[] = {
+ { SERVICE_REPL, ReplMain },
+ { NULL, NULL }
+ };
+#endif
+
+
+void
+main (
+ void
+ )
+
+/*++
+
+Routine Description:
+
+ This is a temporary main routine for the replicator service. It is
+ separated from the workstation service for now so that the NT test
+ machine can be updated without rebooting because the workstation
+ service will be running and it cannot be terminated.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ NET_API_STATUS ApiStatus;
+ NetpKdPrint(( "[replmain/main] beginning execution.\n" ));
+
+ NetpKdPrint(( "Calling NetpInitRpcServer...\n" ));
+ ApiStatus = NetpInitRpcServer();
+ NetpAssert( ApiStatus == NO_ERROR );
+
+ NetpKdPrint(( "[replmain/main] calling ReplMain.\n" ));
+
+ ReplMain( 0, NULL );
+
+ NetpKdPrint(( "[replmain/main] Back from ReplMain.\n" ));
+
+ NetpKdPrint(( "[replmain] Stopping RPC server (if any).\n" ));
+ (void) NetpStopRpcServer( repl_ServerIfHandle );
+
+ NetpKdPrint(( "[replmain/main] done execution.\n" ));
+}
diff --git a/private/net/svcdlls/repl/repltest/replsum.c b/private/net/svcdlls/repl/repltest/replsum.c
new file mode 100644
index 000000000..c2b454489
--- /dev/null
+++ b/private/net/svcdlls/repl/repltest/replsum.c
@@ -0,0 +1,221 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ ReplSum.c
+
+Abstract:
+
+ Test program - computes checksum of a single file (or tree)
+
+Author:
+
+ JR (John Rogers, JohnRo@Microsoft) 06-Apr-1993
+
+Environment:
+
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+ 06-Apr-1993 JohnRo
+ Created.
+ 16-Apr-1993 JohnRo
+ Vastly improved handling of single files.
+ Added print of EA size.
+ 16-Apr-1993 JohnRo
+ Added prints of various time fields.
+ 20-Apr-1993 JohnRo
+ Display time in milliseconds too, as CompareFileTime has problems.
+ 07-May-1993 JohnRo
+ RAID 3258: file not updated due to ERROR_INVALID_USER_BUFFER.
+ 13-Jun-1993 JohnRo
+ RAID 13080: Allow repl between different timezones.
+ 15-Jun-1993 JohnRo
+ Extracted ShowFileTimes() and ShowTime() for use by multiple test apps.
+ Ditto for FakeFindData().
+
+--*/
+
+
+// These must be included first:
+
+//#include <nt.h> // NtOpenFile(), ULONG, etc.
+//#include <ntrtl.h> // PLARGE_INTEGER, TIME_FIELDS, etc.
+//#include <nturtl.h> // Needed for ntrtl.h and windows.h to co-exist.
+#include <windows.h> // IN, LPTSTR, TRUE, etc.
+#include <lmcons.h> // NET_API_STATUS.
+
+// These may be included in any order:
+
+#include <assert.h> // assert().
+#include <checksum.h> // FORMAT_CHECKSUM, SingleChecksum().
+#include <client.h> // ReplGetTimeCacheValue().
+#include <filefind.h> // LPREPL_FIND_HANDLE, etc.
+#include <netdebug.h> // DBGSTATIC, FORMAT_ equates.
+#include <repldefs.h> // CHECKSUM_REC, ReplGetEaSize(), ScanTree(), etc.
+#include <repltest.h> // FakeFindData(), ShowFileTimes(), ShowTime().
+#include <stdio.h> // printf().
+#include <stdlib.h> // EXIT_FAILURE, EXIT_SUCCESS, _CRTAPI1.
+#include <tstring.h> // NetpCopyTStrFromStr(), STRCPY(), TCHAR_EOS, etc.
+
+
+DBGSTATIC VOID
+Usage(
+ IN char * ProgName
+ )
+{
+ (VOID) printf(
+ "Repl checksum program...\n"
+ "Author: JR (John Rogers, JohnRo@Microsoft)\n"
+ "Usage: %s [options] srcPath\n"
+ "Options:\n"
+ " -d checksum dir "
+ "(default is to checksum a file)\n"
+ " -s uncServerName server name to use local time from\n"
+ " -v verbose\n",
+ ProgName);
+}
+
+DBGSTATIC VOID
+DumpChecksum(
+ IN LPCHECKSUM_REC Record
+ )
+{
+ assert( Record != NULL );
+ (VOID) printf( "checksum record:\n" );
+ (VOID) printf( " checksum: " FORMAT_CHECKSUM ".\n", Record->checksum );
+ (VOID) printf( " count: " FORMAT_DWORD ".\n", Record->count );
+
+} // DumpChecksum
+
+
+int _CRTAPI1
+main(
+ IN int argc,
+ IN char *argv[]
+ )
+{
+ NET_API_STATUS ApiStatus;
+ int ArgNumber;
+ BOOL DoingFiles = TRUE;
+ REPL_WIN32_FIND_DATA FindData;
+ BOOL GotPath = FALSE;
+ LONG MasterTimeZoneOffsetSecs; // exporter offset from GMT
+ CHECKSUM_REC Record;
+ TCHAR Source[ PATHLEN+1 ]; // area after L'\0' is scratch
+ LPTSTR UncServerName = NULL; // server where files are.
+ BOOL Verbose = FALSE;
+
+
+ //
+ // Process command-line arguments.
+ //
+ for (ArgNumber = 1; ArgNumber < argc; ArgNumber++) {
+ if ((*argv[ArgNumber] == '-') || (*argv[ArgNumber] == '/')) {
+ switch (tolower(*(argv[ArgNumber]+1))) // Process switches
+ {
+
+ case 'd' :
+ DoingFiles = FALSE;
+ break;
+
+ case 's' :
+ if (UncServerName != NULL) {
+ Usage( argv[0] );
+ return (EXIT_FAILURE);
+ }
+ UncServerName
+ = NetpAllocTStrFromStr( (LPSTR) argv[++ArgNumber]);
+ NetpAssert( UncServerName != NULL );
+ break;
+
+ case 'v' :
+ ReplGlobalTrace = REPL_DEBUG_ALL;
+ Verbose = TRUE;
+ break;
+
+ default :
+ Usage( argv[0] );
+ return (EXIT_FAILURE);
+
+ } // switch
+
+ } else { // not an argument
+
+ if (GotPath) {
+ Usage( argv[0] );
+ return (EXIT_FAILURE);
+ }
+ GotPath = TRUE;
+ (VOID) NetpCopyStrToTStr(Source, argv[ArgNumber]);
+ }
+ }
+
+ if ( !GotPath ) {
+ Usage( argv[0] );
+ return (EXIT_FAILURE);
+ }
+
+ assert( Source[0] != TCHAR_EOS );
+
+ //
+ // Checksum is based on master's timezone, so get it.
+ //
+ ApiStatus = ReplGetTimeCacheValue(
+ UncServerName,
+ &MasterTimeZoneOffsetSecs ); // offset (+ for West of GMT, etc).
+ if (ApiStatus != NO_ERROR) {
+ (VOID) printf(
+ "ReplGetTimeCacheValue FAILED, status " FORMAT_API_STATUS ".\n",
+ ApiStatus );
+ goto Cleanup;
+ }
+ if (Verbose) {
+ (VOID) printf(
+ "Master time zone offset (seconds) is " FORMAT_LONG ".\n",
+ MasterTimeZoneOffsetSecs );
+ }
+ assert( MasterTimeZoneOffsetSecs != -1 );
+
+ //
+ // Checksum the file or directory.
+ //
+ if (DoingFiles) {
+
+ ShowFileTimes( Source );
+ FakeFindData(
+ Source, // file name
+ Verbose,
+ &FindData );
+
+ Record.checksum = SingleChecksum(
+ MasterTimeZoneOffsetSecs, // exporter offset from GMT
+ &FindData );
+ Record.count = 1;
+
+ } else {
+ ScanTree(
+ MasterTimeZoneOffsetSecs, // exporter offset from GMT
+ Source, // dir name (and scratch space!)
+ &Record );
+ }
+
+ (VOID) printf( "checksum for '" FORMAT_LPTSTR "' is:\n",
+ Source );
+ DumpChecksum( &Record );
+ ApiStatus = NO_ERROR;
+
+
+Cleanup:
+
+ // BUGBUG: memory leaks, but who cares?
+ if (ApiStatus == NO_ERROR) {
+ return (EXIT_SUCCESS);
+ } else {
+ return (EXIT_FAILURE);
+ }
+
+} // main
diff --git a/private/net/svcdlls/repl/repltest/replsvc.c b/private/net/svcdlls/repl/repltest/replsvc.c
new file mode 100644
index 000000000..12b498efe
--- /dev/null
+++ b/private/net/svcdlls/repl/repltest/replsvc.c
@@ -0,0 +1,84 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ ReplSvc.c
+
+Abstract:
+
+ This is part of ReplTest emulates the service controller APIs.
+
+Author:
+
+ John Rogers (JohnRo) 13-Jan-1992
+
+Environment:
+
+ Portable to any flat, 32-bit environment. (Uses Win32 typedefs.)
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+ 14-Jan-1992 JohnRo
+ Created.
+ 05-Mar-1992 JohnRo
+ Changed interface to match new service controller.
+ 31-Mar-1992 JohnRo
+ SC_HANDLE changed to SERVICE_STATUS_HANDLE.
+ 22-May-1992 JohnRo
+ RAID 9829: winsvc.h and related file cleanup.
+
+--*/
+
+
+// These must be included first:
+
+#include <windows.h> // DWORD, etc.
+#include <lmcons.h> // IN, etc.
+
+// These may be included in any order:
+
+#include <netdebug.h> // NetpAssert(), NetpDbg routines, FORMAT_ equates.
+#include <prefix.h> // PREFIX_ equates.
+#include <winsvc.h> // My prototypes, etc.
+
+
+BOOL
+WINAPI
+SetServiceStatus(
+ IN SERVICE_STATUS_HANDLE hServiceStatus,
+ IN LPSERVICE_STATUS ss
+ )
+{
+ UNREFERENCED_PARAMETER( hServiceStatus );
+
+ NetpKdPrint(( PREFIX_REPL
+ "(fake) SetServiceStatus: pretending to accept status:\n" ));
+ NetpAssert( ss != NULL );
+ NetpKdPrint(( " state=" FORMAT_DWORD " controls=" FORMAT_HEX_DWORD
+ " Win32 status=" FORMAT_API_STATUS " net status=" FORMAT_API_STATUS
+ " checkpoint=" FORMAT_DWORD " wait hint=" FORMAT_DWORD "\n",
+ ss->dwCurrentState, ss->dwControlsAccepted, ss->dwWin32ExitCode,
+ ss->dwServiceSpecificExitCode, ss->dwCheckPoint, ss->dwWaitHint ));
+
+ return (TRUE); // OK
+}
+
+
+SERVICE_STATUS_HANDLE
+WINAPI
+RegisterServiceCtrlHandler(
+ IN LPCTSTR lpServiceName,
+ IN LPHANDLER_FUNCTION lpHandlerProc
+ )
+{
+ UNREFERENCED_PARAMETER( lpServiceName );
+ UNREFERENCED_PARAMETER( lpHandlerProc );
+ NetpKdPrint(( PREFIX_REPL
+ "(fake) ServiceRegisterCtrlHandler: pretending to do "
+ FORMAT_LPTSTR ".\n", lpServiceName ));
+
+ return ( (SERVICE_STATUS_HANDLE) lpHandlerProc ); // Anything but -1. (OK)
+}
diff --git a/private/net/svcdlls/repl/repltest/repltest.c b/private/net/svcdlls/repl/repltest/repltest.c
new file mode 100644
index 000000000..042b9132f
--- /dev/null
+++ b/private/net/svcdlls/repl/repltest/repltest.c
@@ -0,0 +1,234 @@
+/*++
+
+Copyright (c) 1991-92 Microsoft Corporation
+
+Module Name:
+
+ ReplTest.c
+
+Abstract:
+
+ Separate program to test repl service APIs.
+
+Author:
+
+ 11/19/91 madana
+
+Revision History:
+
+ 12-Jan-1992 JohnRo
+ Added debug output; try direct call to ReplMain().
+ Deleted tabs in source file.
+ Get ReplMain's prototype from a header file.
+ 13-Jan-1992 JohnRo
+ Fix parsing error.
+ 15-Jan-1992 JohnRo
+ Added call to a thread which does API tests.
+ 12-Feb-1992 JohnRo
+ Changed to allow Win32 registry stuff.
+ Changed file name from repl.h to replgbl.h to avoid MIDL conflict.
+ Really fixed parsing error.
+ 15-Feb-1992 JohnRo
+ Added call to NetpInitRpcServer().
+ 19-Feb-1992 JohnRo
+ Added tests of repl config and import APIs.
+ 22-Feb-1992 JohnRo
+ Added multi-thread workaround to EXIT_A_TEST(), etc.
+ 04-Mar-1992 JohnRo
+ Changed ReplMain's interface to match new service controller.
+ 13-Mar-1992 JohnRo
+ Removed temporary code which pretended to run the service.
+ 20-Mar-1992 JohnRo
+ Net config stuff doesn't need to init Win32 stuff anymore.
+ 22-Sep-1992 JohnRo
+ Work with stdcall.
+ Main should wait for service start (if necessary), not Test routines.
+
+--*/
+
+
+// These must be included first:
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windows.h> // DWORD, IN, CreateThread API, etc.
+#include <lmcons.h>
+#ifdef REPL_TEST_STANDALONE
+#include <rpc.h> // Needed by <rpcutil.h>.
+#endif
+
+// These may be included in any order:
+
+#include <configp.h>
+#include <netdebug.h> // NetpKdPrint(()), FORMAT_ equates
+#ifdef REPL_TEST_STANDALONE
+#include <repldefs.h> // ReplMain().
+#include <replgbl.h> // ReplGlobalTerminateEvent.
+#endif
+#include <repltest.h> // My prototypes.
+#ifdef REPL_TEST_STANDALONE
+#include <rpcutil.h> // NetpInitRpcServer().
+#endif
+#include <stdlib.h> // EXIT_FAILURE, EXIT_SUCCESS, _CRTAPI1.
+#include <thread.h> // FORMAT_NET_THREAD_ID, NetpCurrentThread().
+
+
+//#define ARBITRARY_STACK_SIZE 16000
+// BUGBUG: Maybe I'm running out of stack space 'cos WinReg insists that
+// maxes are 64KB each. So lets try something ridiculous:
+#define ARBITRARY_STACK_SIZE (500 * 1024)
+
+
+BOOL TestIsMultithread = TRUE; // Tell EXIT_A_TEST() what to do.
+
+#ifdef REPL_TEST_STANDALONE
+DWORD
+StartReplService(
+ IN LPVOID Parm OPTIONAL
+ );
+#endif
+
+int _CRTAPI1
+main(
+ IN int argc,
+ IN char *argv[]
+ )
+
+/*++
+
+Routine Description:
+
+ This is a temporary main routine for the replicator service.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+{
+#ifdef REPL_TEST_STANDALONE
+ NET_API_STATUS ApiStatus;
+ HANDLE StartHandle;
+#endif
+ DWORD ChildThreadId;
+ HANDLE TestHandle;
+
+#if defined(FAKE_PER_PROCESS_RW_CONFIG)
+
+ NetpInitFakeConfigData();
+
+#endif // FAKE_PER_PROCESS_RW_CONFIG
+
+#ifdef REPL_TEST_STANDALONE
+ NetpKdPrint(( "Calling NetpInitRpcServer...\n" ));
+ ApiStatus = NetpInitRpcServer();
+ NetpAssert( ApiStatus == NO_ERROR );
+#endif
+
+ NetpKdPrint(( "Starting everyone with stack size of " FORMAT_DWORD
+ " bytes.\n", ARBITRARY_STACK_SIZE ));
+
+#ifdef REPL_TEST_STANDALONE
+ //
+ // Create a thread which will become first thread of repl service.
+ //
+ StartHandle = CreateThread(
+ NULL,
+ ARBITRARY_STACK_SIZE,
+ (LPTHREAD_START_ROUTINE) StartReplService,
+ NULL, // no parameter
+ 0, // no creation flags
+ & ChildThreadId);
+ NetpAssert( StartHandle != (HANDLE) 0 );
+
+ WaitForMasterThreadInit();
+
+ WaitForClientThreadInit();
+
+#endif
+
+ //
+ // Create a thread to test the export dir APIs.
+ //
+ TestHandle = CreateThread(
+ NULL,
+ ARBITRARY_STACK_SIZE,
+ (LPTHREAD_START_ROUTINE) TestExportDirApis,
+ NULL, // no parameter
+ 0, // no creation flags
+ & ChildThreadId);
+ NetpAssert( TestHandle != (HANDLE) 0 );
+
+ //
+ // Create a thread to test the repl config APIs.
+ //
+ TestHandle = CreateThread(
+ NULL,
+ ARBITRARY_STACK_SIZE,
+ (LPTHREAD_START_ROUTINE) TestReplApis,
+ NULL, // no parameter
+ 0, // no creation flags
+ & ChildThreadId);
+ NetpAssert( TestHandle != (HANDLE) 0 );
+
+ //
+ // Create a thread to test the import dir APIs.
+ //
+ TestHandle = CreateThread(
+ NULL,
+ ARBITRARY_STACK_SIZE,
+ (LPTHREAD_START_ROUTINE) TestImportDirApis,
+ NULL, // no parameter
+ 0, // no creation flags
+ & ChildThreadId);
+ NetpAssert( TestHandle != (HANDLE) 0 );
+
+#ifdef REPL_TEST_STANDALONE
+ //
+ // Wait for the child threads here...
+ //
+ WaitForever();
+#endif
+
+ return (EXIT_SUCCESS);
+
+} // main()
+
+
+#ifdef REPL_TEST_STANDALONE
+DWORD
+StartReplService(
+ IN LPVOID Parm OPTIONAL
+ )
+
+{
+ UNREFERENCED_PARAMETER( Parm );
+
+ NetpKdPrint(( "StartReplService: thread ID is "
+ FORMAT_NET_THREAD_ID ".\n", NetpCurrentThread() ));
+
+ NetpKdPrint(( "StartReplService: calling ReplMain.\n" ));
+ ReplMain( 0, NULL, NULL );
+
+ NetpKdPrint(( "StartReplService: Back from ReplMain.\n" ));
+
+ //
+ // Call NetServiceStartCtrlDispatcher to set up the control interface.
+ // The API won't return until all services have been terminated. At that
+ // point, we just exit.
+ //
+ // NetServiceStartCtrlDispatcher (
+ // ReplDispatchTable
+ // );
+
+ NetpKdPrint(( "StartReplService: done execution.\n" ));
+
+ return (NO_ERROR);
+
+} // StartReplService
+#endif
diff --git a/private/net/svcdlls/repl/repltest/repltest.h b/private/net/svcdlls/repl/repltest/repltest.h
new file mode 100644
index 000000000..a4813201e
--- /dev/null
+++ b/private/net/svcdlls/repl/repltest/repltest.h
@@ -0,0 +1,107 @@
+/*++
+
+Copyright (c) 1991-1993 Microsoft Corporation
+
+Module Name:
+
+ ReplTest.h
+
+Abstract:
+
+ Prototypes for replicator test program.
+
+Author:
+
+ John Rogers (JohnRo) 14-Jan-1992
+
+Revision History:
+
+ 14-Jan-1992 JohnRo
+ Created.
+ 17-Feb-1992 JohnRo
+ Added EXIT_A_TEST() macro.
+ Added test of repl config APIs.
+ 22-Feb-1992 JohnRo
+ Added multi-thread workaround to EXIT_A_TEST(), etc.
+ 28-Jul-1992 JohnRo
+ RAID 2274: repl svc should impersonate caller.
+ 03-Dec-1992 JohnRo
+ Repl tests for remote registry. Undo old thread junk.
+ 28-May-1993 JohnRo
+ Added DisplayPackedReplMailslotMsg() and DisplayMessageType().
+ 15-Jun-1993 JohnRo
+ Added ShowFileTimes() and ShowTime() for use by multiple test apps.
+ 23-Jul-1993 JohnRo
+ Added Display(). Added Verbose flag to some routines.
+
+--*/
+
+#ifndef _REPLTEST_
+#define _REPLTEST_
+
+
+#include <filefind.h> // LPREPL_FIND_HANDLE, etc.
+#include <netdebug.h> // NetpKdPrint().
+#include <stdio.h> // printf().
+
+
+#define OPTIONAL_LPTSTR( tstr ) \
+ ( (tstr) ? (tstr) : TEXT("<noname>") )
+
+//#define Display NetpDbgPrint
+#define Display printf
+
+VOID
+DisplayMessageType(
+ IN DWORD MessageType
+ );
+
+VOID
+DisplayPackedReplMailslotMsg(
+ IN LPVOID Message,
+ IN DWORD MessageSize,
+ IN BOOL Verbose
+ );
+
+VOID
+FakeFindData(
+ IN LPCTSTR FileName,
+ IN BOOL Verbose,
+ OUT LPREPL_WIN32_FIND_DATA FindData
+ );
+
+VOID
+ShowFileTimes(
+ IN LPCTSTR FileName
+ );
+
+VOID
+ShowTime(
+ IN LPCSTR Tag,
+ IN LPFILETIME GmtFileTime
+ );
+
+VOID
+TestExportDirApis(
+ IN LPTSTR UncServerName OPTIONAL,
+ IN BOOL ImportOnly,
+ IN BOOL OrdinaryUserOnly,
+ IN BOOL Verbose
+ );
+
+VOID
+TestImportDirApis(
+ IN LPTSTR UncServerName OPTIONAL,
+ IN BOOL OrdinaryUserOnly,
+ IN BOOL Verbose
+ );
+
+VOID
+TestReplApis(
+ IN LPTSTR UncServerName OPTIONAL,
+ IN BOOL ImportOnly,
+ IN BOOL OrdinaryUserOnly
+ );
+
+
+#endif // _REPLTEST_
diff --git a/private/net/svcdlls/repl/repltest/setea.c b/private/net/svcdlls/repl/repltest/setea.c
new file mode 100644
index 000000000..5d4025a93
--- /dev/null
+++ b/private/net/svcdlls/repl/repltest/setea.c
@@ -0,0 +1,130 @@
+
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <nt.h> // NT definitions
+#include <ntrtl.h> // NT runtime library definitions
+#include <nturtl.h>
+
+#include <windef.h>
+#include <winbase.h>
+#include <logonp.h>
+
+
+#define BUFFER_SIZE 1024 * 10
+
+void main(
+ int argc,
+ char *argv[]
+ )
+{
+
+ NTSTATUS ntstatus;
+
+ HANDLE FileHandle;
+ UNICODE_STRING FileName;
+
+ IO_STATUS_BLOCK IoStatusBlock;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+
+ CHAR EaBuffer[BUFFER_SIZE];
+
+ LPBYTE TempPtr;
+
+ PFILE_FULL_EA_INFORMATION EaBufferPtr;
+ DWORD EaBufferLength;
+ LPWSTR UnicodeFileName;
+
+
+ if( argc < 4 ) {
+ printf("Usage : setea filename eaname eavalue \n");
+
+ return;
+ }
+
+ printf("FileName = %s \n", argv[1]);
+ printf("EaName = %s \n", argv[2]);
+ printf("EaValue = %s \n", argv[3]);
+
+ UnicodeFileName = NetpLogonOemToUnicode(argv[1]);
+
+ if( !RtlDosPathNameToNtPathName_U(
+ UnicodeFileName,
+ &FileName,
+ NULL,
+ NULL
+ ) ) {
+
+ printf("Could not convert DOS path to NT path\n");
+ return;
+ }
+
+ InitializeObjectAttributes(
+ &ObjectAttributes,
+ &FileName,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL
+ );
+
+ ntstatus = NtOpenFile(
+ &FileHandle,
+ FILE_READ_DATA | FILE_READ_EA |
+ FILE_WRITE_DATA | FILE_WRITE_EA,
+ &ObjectAttributes,
+ &IoStatusBlock,
+ 0,
+ 0
+ );
+
+ if (! NT_SUCCESS(ntstatus)) {
+ printf("NtOpenFile %s failed: 0x%08lx\n", argv[1], ntstatus);
+ return;
+ }
+ else {
+ printf("Succeed in opening dir\n");
+ }
+
+
+ // prepare EA
+
+ EaBufferPtr = (PFILE_FULL_EA_INFORMATION)EaBuffer;
+
+ EaBufferPtr->NextEntryOffset = 0;
+ EaBufferPtr->Flags = 0;
+ EaBufferPtr->EaNameLength = (UCHAR)strlen(argv[2]);
+ EaBufferPtr->EaValueLength = (USHORT)strlen(argv[3]);
+
+ TempPtr = &(EaBufferPtr->EaName);
+
+ // copy EaName
+ strcpy(TempPtr, argv[2]);
+
+ // copyEaValue
+
+ TempPtr += ( EaBufferPtr->EaNameLength + 1 );
+ strcpy(TempPtr, argv[3]);
+
+ TempPtr += ( EaBufferPtr->EaValueLength + 1 );
+
+ EaBufferLength = TempPtr - EaBuffer;
+
+ ntstatus = NtSetEaFile(FileHandle,
+ &IoStatusBlock,
+ EaBuffer,
+ EaBufferLength
+ );
+
+
+ if (NT_SUCCESS(ntstatus)) {
+
+ printf("NtSetEaFile for %s succeeded %08lx\n", argv[1], ntstatus);
+
+ }
+ else {
+ printf("NtSetEaFile for %s failed %08lx\n", argv[1], ntstatus);
+ }
+
+ (void) NtClose(FileHandle);
+}
diff --git a/private/net/svcdlls/repl/repltest/showftim.c b/private/net/svcdlls/repl/repltest/showftim.c
new file mode 100644
index 000000000..2c56bd18c
--- /dev/null
+++ b/private/net/svcdlls/repl/repltest/showftim.c
@@ -0,0 +1,81 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ ShowFTim.c
+
+Abstract:
+
+ BUGBUG
+
+Author:
+
+ JR (John Rogers, JohnRo@Microsoft) 06-Apr-1993
+
+Environment:
+
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+ 15-Jun-1993 JohnRo
+ Extracted ShowFileTimes() and ShowTime() for use by multiple test apps.
+
+--*/
+
+
+// These must be included first:
+
+#include <windows.h> // CreateFile(), IN, LPTSTR, TRUE, etc.
+
+// These may be included in any order:
+
+#include <assert.h> // assert().
+#include <repltest.h> // My prototype, ShowTime().
+
+
+VOID
+ShowFileTimes(
+ IN LPCTSTR FileName
+ )
+{
+ HANDLE FileHandle = INVALID_HANDLE_VALUE;
+ BY_HANDLE_FILE_INFORMATION FileInformation;
+
+ FileHandle = CreateFile(
+ FileName,
+ GENERIC_READ, // desired access
+ FILE_SHARE_READ, // share mode
+ NULL, // no security attributes
+ OPEN_EXISTING, // open if exists; fail if not.
+ 0, // flags
+ (HANDLE) NULL // no template
+ );
+ if (FileHandle == INVALID_HANDLE_VALUE) {
+ assert( FALSE ); // BUGBUG
+ goto Cleanup;
+ }
+
+ if ( !GetFileInformationByHandle(
+ FileHandle,
+ &FileInformation ) ) {
+
+ assert( FALSE ); // BUGBUG
+ goto Cleanup;
+ }
+
+ ShowTime( "access time (from file)", &(FileInformation.ftLastAccessTime) );
+
+ ShowTime( "create time (from file)", &(FileInformation.ftCreationTime) );
+
+ ShowTime( "write time (from file)", &(FileInformation.ftLastWriteTime) );
+
+Cleanup:
+
+ if (FileHandle != INVALID_HANDLE_VALUE) {
+ (VOID) CloseHandle( FileHandle );
+ }
+
+} // ShowFileTimes
diff --git a/private/net/svcdlls/repl/repltest/showtime.c b/private/net/svcdlls/repl/repltest/showtime.c
new file mode 100644
index 000000000..1174b2ba6
--- /dev/null
+++ b/private/net/svcdlls/repl/repltest/showtime.c
@@ -0,0 +1,105 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ ReplSum.c
+
+Abstract:
+
+ Test program - computes checksum of a single file (or tree)
+
+Author:
+
+ JR (John Rogers, JohnRo@Microsoft) 06-Apr-1993
+
+Environment:
+
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+ 06-Apr-1993 JohnRo
+ Created.
+ 16-Apr-1993 JohnRo
+ Vastly improved handling of single files.
+ Added print of EA size.
+ 16-Apr-1993 JohnRo
+ Added prints of various time fields.
+ 20-Apr-1993 JohnRo
+ Display time in milliseconds too, as CompareFileTime has problems.
+ 07-May-1993 JohnRo
+ RAID 3258: file not updated due to ERROR_INVALID_USER_BUFFER.
+ 13-Jun-1993 JohnRo
+ RAID 13080: Allow repl between different timezones.
+ 15-Jun-1993 JohnRo
+ Extracted ShowFileTimes() and ShowTime() for use by multiple test apps.
+
+--*/
+
+
+// These must be included first:
+
+#include <nt.h> // NtOpenFile(), ULONG, etc.
+#include <ntrtl.h> // PLARGE_INTEGER, TIME_FIELDS, etc.
+#include <nturtl.h> // Needed for ntrtl.h and windows.h to co-exist.
+#include <windows.h> // IN, LPTSTR, TRUE, etc.
+
+// These may be included in any order:
+
+#include <assert.h> // assert().
+#include <repltest.h> // My prototype.
+#include <stdio.h> // printf().
+
+
+VOID
+ShowTime(
+ IN LPCSTR Tag,
+ IN LPFILETIME GmtFileTime
+ )
+{
+ LARGE_INTEGER GmtLargeIntegerTime;
+ LARGE_INTEGER LocalFileTime;
+ NTSTATUS NtStatus;
+ TIME_FIELDS TimeFields;
+
+ assert( Tag != NULL );
+
+ //
+ // BUGBUG: This assumes that FILETIME and LARGE_INTEGER have same
+ // precision. Is this guaranteed?
+ //
+
+ GmtLargeIntegerTime.HighPart = GmtFileTime->dwHighDateTime;
+
+ GmtLargeIntegerTime.LowPart = GmtFileTime->dwLowDateTime;
+
+
+ //
+ // Convert from GMT to local time zone.
+ //
+ NtStatus = RtlSystemTimeToLocalTime (
+ &GmtLargeIntegerTime, // src
+ &LocalFileTime ); // dest
+ assert( NtStatus == STATUS_SUCCESS );
+
+ //
+ // Convert from 64-bit to individual fields, so we can display.
+ //
+ RtlTimeToTimeFields(
+ &LocalFileTime, // src
+ &TimeFields ); // dest
+
+ (VOID) printf(
+ FORMAT_LPSTR ": %04d-%02d-%02d %02d:%02d:%02d.%03d\n",
+ Tag,
+ TimeFields.Year,
+ TimeFields.Month,
+ TimeFields.Day,
+ TimeFields.Hour,
+ TimeFields.Minute,
+ TimeFields.Second,
+ TimeFields.Milliseconds );
+
+} // ShowTime
diff --git a/private/net/svcdlls/repl/repltest/sources b/private/net/svcdlls/repl/repltest/sources
new file mode 100644
index 000000000..e50cbe56f
--- /dev/null
+++ b/private/net/svcdlls/repl/repltest/sources
@@ -0,0 +1,121 @@
+!IF 0
+
+Copyright (c) 1989-1993 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+Author:
+
+ John Rogers (JohnRo) 17-Dec-1991
+
+NOTE: Commented description of this file is in \nt\public\oak\bin\sources.tpl
+
+!ENDIF
+
+#
+# The MAJORCOMP and MINORCOMP variables are defined
+# so that $(MAJORCOMP)$(MINORCOMP)filename can be used in
+# cross compiling to provide unique filenames in a flat namespace.
+#
+
+MAJORCOMP=repl
+MINORCOMP=test
+
+#
+# The TARGETNAME variable is defined by the developer. It is the name of
+# the target (component) that is being built by this makefile. It
+# should NOT include any path or file extension information.
+#
+
+TARGETNAME=ReplTest
+
+#
+# The TARGETPATH and TARGETTYPE variables are defined by the developer.
+# The first specifies where the target is to be build. The second specifies
+# the type of target (either PROGRAM, DYNLINK, LIBRARY, UMAPPL_NOLIB or
+# BOOTPGM). UMAPPL_NOLIB is used when you're only building user-mode
+# apps and don't need to build a library.
+#
+
+TARGETPATH=obj
+
+# Pick one of the following and delete the others
+TARGETTYPE=LIBRARY
+
+#
+# The TARGETLIBS specifies additional libraries to link with you target
+# image. Each library path specification should contain an asterisk (*)
+# where the machine specific subdirectory name should go.
+#
+
+TARGETLIBS=
+
+#
+# The INCLUDES variable specifies any include paths that are specific to
+# this source directory. Separate multiple directory paths with single
+# semicolons. Relative path specifications are okay.
+#
+
+INCLUDES=.;..\common;..\..\..\inc;..\..\..\..\inc;..\server
+
+#
+# The SOURCES variable is defined by the developer. It is a list of all the
+# source files for this component. Each source file should be on a separate
+# line using the line continuation character. This will minimize merge
+# conflicts if two developers adding source files to the same component.
+#
+
+# Most .c files in this directory aren't compiled; they're OS/2 test programs.
+# Maybe someday we'll convert them over.
+
+!IFNDEF DISABLE_NET_UNICODE
+UNICODE=1
+NET_C_DEFINES=-DUNICODE
+!ENDIF
+
+USE_CRTDLL=1
+
+# ReplSvc.c
+# Wait.c
+SOURCES= \
+ DispMail.c \
+ FakeFind.c \
+ ShowFTim.c \
+ ShowTime.c \
+ TestExp.c \
+ TestImp.c \
+ TestRepl.c
+
+#
+# Next specify one or more user mode test programs and their type
+# UMTEST is used for optional test programs. UMAPPL is used for
+# programs that always get built when the directory is built.
+#
+
+UMTYPE=console
+
+# UMAPPL=queryea*replmain*repltest*testexp0*testimp0*testrep0*watch
+UMAPPL=repldel*replsum*test2*testaft*testcopy*testexp0*testimp0*testrep0
+UMTEST=repldir*queryea*replmain*repltest*watch*watchl
+
+UMLIBS= \
+ obj\*\ReplTest.lib \
+ $(BASEDIR)\public\sdk\lib\*\netapi32.lib \
+ $(BASEDIR)\Public\Sdk\Lib\*\netlib.lib \
+ $(BASEDIR)\public\sdk\lib\*\netrap.lib \
+ ..\server\obj\*\repl.lib \
+ ..\common\obj\*\replcom.lib \
+ $(BASEDIR)\public\sdk\lib\*\RpcNdr.lib \
+ $(BASEDIR)\public\sdk\lib\*\RpcRT4.lib \
+ $(BASEDIR)\public\sdk\lib\*\AdvApi32.lib
+
+C_DEFINES=-DRPC_NO_WINDOWS_H
diff --git a/private/net/svcdlls/repl/repltest/test.h b/private/net/svcdlls/repl/repltest/test.h
new file mode 100644
index 000000000..c960f6d9e
--- /dev/null
+++ b/private/net/svcdlls/repl/repltest/test.h
@@ -0,0 +1,84 @@
+///*****************************************************************
+/// Microsoft LAN Manager *
+/// Copyright(c) Microsoft Corp., 1987-1990 *
+///*****************************************************************
+
+//
+// @(#)test/test
+// Function prototypes and some global data
+//
+//
+
+
+//
+ C O N S T A N T S - DEFINES
+ ***************** *******
+//
+
+#define CLIENT_SLOT_NAME "\\MAILSLOT\\NET\\REPL_CLI"
+#define MASTER_SLOT_NAME "\\MAILSLOT\\NET\\REPL_MAS"
+
+
+#ifndef MAX_2_MSLOT_SIZE
+#define MAX_2_MSLOT_SIZE (441 - (sizeof(CLIENT_SLOT_NAME) + CNLEN + 1))
+#endif
+
+
+#define FULL_SLOT_NAME_SIZE (sizeof(CLIENT_SLOT_NAME) + CNLEN + 1 + 2)
+
+#define MILLI_IN_MINUTE 60*1000L
+#define MAX_UPDATES 32
+
+// message types
+#define SYNC_MSG 1
+#define GUARD_MSG 2
+#define PULSE_MSG 3
+#define IS_DIR_SUPPORTED 11
+#define IS_MASTER 12
+#define DIR_NOT_SUPPORTED 13
+#define DIR_SUPPORTED 14
+#define NOT_MASTER_DIR 15
+#define MASTER_DIR 16
+
+// Message opcodes
+#define START 1
+#define UPDATE 2
+#define END 3
+#define PULSE 4
+
+// Scan modes
+#define SYNC_SCAN 1
+#define GUARD_SCAN 2
+
+// Extent modes
+#define DIRECTORY 1
+#define TREE 2
+
+// Integrity mode
+#define FILE_INTG 1
+#define TREE_INTG 2
+
+// AlertLogExit codes
+#define EXIT 1
+#define NO_EXIT 0
+
+
+#define HI_PRIO 9 // mailsot write priority
+#define SECOND_CLASS 2
+#define MAIL_WRITE_WAIT 500L
+
+// DosFindFirst attributes
+#define FILE_NORMAL 0x0000
+#define FILE_READONLY 0x0001
+#define FILE_HIDDEN 0x0002
+#define FILE_SYSTEM 0x0004
+#define FILE_DIR 0x0010
+#define FILE_ARCHIVE 0x0020
+
+
+//
+ F U N C T I O N S
+ *****************
+//
+
+VOID AlertLogExit(unsigned, unsigned, unsigned, char *, char *, unsigned);
diff --git a/private/net/svcdlls/repl/repltest/test1.c b/private/net/svcdlls/repl/repltest/test1.c
new file mode 100644
index 000000000..e110a2669
--- /dev/null
+++ b/private/net/svcdlls/repl/repltest/test1.c
@@ -0,0 +1,76 @@
+///*****************************************************************
+/// Microsoft LAN Manager *
+/// Copyright(c) Microsoft Corp., 1987-1990 *
+///*****************************************************************
+
+
+// Module : test1.c
+*
+*
+***************************************************************************//
+
+
+#include <netcons.h>
+#include <netlib.h>
+#include <service.h>
+
+#include "repldefs.h"
+#include "repl.h"
+
+
+VOID main(int, char*[]);
+
+char service[] = SERVICE_NAME;
+
+char info1[sizeof(struct service_info_2)];
+char info2[sizeof(struct service_status) * 4];
+
+VOID
+main(argc, argv)
+//*************
+*
+*
+*************************************************************************//
+
+int argc;
+char *argv[];
+{
+ unsigned short level;
+ unsigned char opcode;
+ unsigned char dumm = 0;
+ NET_API_STATUS NetStatus;
+ unsigned short *avail;
+ struct service_info_2 *buf1;
+ struct service_info_2 *buf2;
+
+ //
+
+ buf1 = (struct service_info_2 *)info1;
+
+ level = 2;
+ if (NetStatus = NetServiceGetInfo(NULL, (const char *)service, level,
+ (char *)buf1, sizeof(info1), avail))
+
+ NetpKdPrint(("NetServiceGetInfo error = %d\n", NetStatus));
+
+ else
+ NetpKdPrint(("NetServiceGetInfo: name-%s, status-%u, code-%U, pid-%u, text-%s\n",
+ buf1->svci2_name, buf1->svci2_status, buf1->svci2_code, buf1->svci2_pid, buf1->svci2_text));
+//
+
+ buf2 = (struct service_info_2 * )info2;
+ opcode = SERVICE_CTRL_INTERROGATE;
+
+
+ if (NetStatus = NetServiceControl(NULL, (const char * )service, opcode, dumm,
+ (char * )buf2, sizeof(info2)))
+
+ NetpKdPrint(("NetServiceControl error = %d\n", NetStatus));
+
+ else
+ NetpKdPrint(("NetServiceCONTROL: name-%s, status-%u, code-%lu, pid-%u, text-%s\n",
+ buf2->svci2_name, buf2->svci2_status, buf2->svci2_code, buf2->svci2_pid, buf2->svci2_text));
+
+}
+
+
diff --git a/private/net/svcdlls/repl/repltest/test2.c b/private/net/svcdlls/repl/repltest/test2.c
new file mode 100644
index 000000000..61d948655
--- /dev/null
+++ b/private/net/svcdlls/repl/repltest/test2.c
@@ -0,0 +1,184 @@
+/*++
+
+******************************************************************
+* Microsoft LAN Manager *
+* Copyright(c) Microsoft Corp., 1987-1993 *
+******************************************************************
+
+
+Module : test2.c
+
+--*/
+
+
+// These must be included first:
+
+#include <windows.h> // DWORD, etc.
+#include <lmcons.h> // NET_API_STATUS.
+
+// These may be included in any order:
+
+#include <assert.h> // assert().
+#include <master.h> // MAX_MSG.
+#include <netdebug.h> // DBGSTATIC, NetpKdPrint(), FORMAT_ equates, etc.
+#include <netlib.h>
+#include "repldefs.h"
+#include <repltest.h> // DisplayPackedReplMailslotMsg().
+#include <stdio.h> // printf().
+#include <stdlib.h> // EXIT_FAILURE, EXIT_SUCCESS, _CRTAPI1.
+#include <time.h> // ctime(), time(), etc.
+#include <tstr.h> // STRCPY(), etc.
+
+
+DBGSTATIC BYTE msg_buf[MAX_2_MSLOT_SIZE];
+
+DBGSTATIC VOID
+Usage (
+ IN char * ProgName
+ )
+{
+ (void) printf(
+ "Usage: %s [-h] [-m] [-v]\n"
+ "\n"
+ "flags:\n"
+ " -h hex dump each msg\n"
+ " -m use master mailslot instead of client\n"
+ " -v indicates verbose (debug) mode\n"
+ "\n"
+ "Example: %s -v\n",
+ ProgName, ProgName );
+}
+
+int _CRTAPI1
+main(
+ int argc,
+ char * argv[]
+ )
+{
+ NET_API_STATUS ApiStatus = NO_ERROR;
+ int ArgNumber;
+ DWORD bytes_read;
+ BOOL DoHexDump = FALSE;
+ TCHAR MailslotName[FULL_SLOT_NAME_SIZE+1];
+ BOOL Master = FALSE;
+ DWORD MaxMailslotSize;
+ time_t Now;
+ HANDLE test_mailslot_handle = (HANDLE)(-1);
+ BOOL Verbose = FALSE;
+
+ //
+ // Process command-line arguments for real.
+ //
+ for (ArgNumber = 1; ArgNumber < argc; ArgNumber++) {
+ if ((*argv[ArgNumber] == '-') || (*argv[ArgNumber] == '/')) {
+ switch (tolower(*(argv[ArgNumber]+1))) // Process switches
+ {
+
+ case 'h' :
+ DoHexDump = TRUE;
+ break;
+
+ case 'm' :
+ Master = TRUE;
+ break;
+
+ case 'v' :
+ Verbose = TRUE;
+ break;
+
+ default:
+
+ Usage( argv[0] );
+ return (EXIT_FAILURE);
+ }
+ } else {
+ Usage( argv[0] );
+ return (EXIT_FAILURE);
+ }
+ }
+
+ (void) STRCPY( MailslotName, SLASH_SLASH );
+ (void) STRCAT( MailslotName, DOT );
+ if (!Master) {
+ (void) STRCAT( MailslotName, (LPTSTR) CLIENT_SLOT_NAME );
+ MaxMailslotSize = MAX_2_MSLOT_SIZE;
+ } else {
+ (void) STRCAT( MailslotName, (LPTSTR) MASTER_SLOT_NAME );
+ MaxMailslotSize = MAX_MSG;
+ }
+
+ (VOID) printf(
+ "Creating mailslot '" FORMAT_LPTSTR "' of size "
+ FORMAT_DWORD "...\n",
+ MailslotName, MaxMailslotSize );
+
+ test_mailslot_handle = CreateMailslot(
+ MailslotName,
+ MaxMailslotSize,
+ (DWORD) MAILSLOT_WAIT_FOREVER, // readtimeout
+ NULL ); // security attributes
+ if ( test_mailslot_handle == (HANDLE)(-1) ) {
+ ApiStatus = (NET_API_STATUS) GetLastError();
+ assert( ApiStatus != NO_ERROR );
+
+ (VOID) printf(
+ "CreateMailslot error: " FORMAT_API_STATUS "\n", ApiStatus );
+ goto Cleanup;
+ }
+
+ Now = time( NULL );
+ (VOID) printf( ctime( &Now ) );
+
+ while (TRUE) {
+
+ if (Verbose) {
+ (VOID) printf( "Waiting for next mailslot message...\n\n" );
+ }
+
+ if ( !ReadFile( test_mailslot_handle,
+ msg_buf,
+ sizeof( msg_buf ),
+ &bytes_read,
+ NULL )) { // No overlapped I/O
+
+ ApiStatus = (NET_API_STATUS) GetLastError();
+ assert( ApiStatus != NO_ERROR );
+
+ (VOID) printf(
+ "ReadFile error: " FORMAT_API_STATUS "\n", ApiStatus );
+ goto Cleanup; // don't forget to close...
+ }
+
+ (VOID) Beep(1000, 500);
+ (VOID) Beep(1500, 700);
+
+ Now = time( NULL );
+ (VOID) printf( ctime( &Now ) );
+
+ if (DoHexDump) {
+ NetpDbgHexDump(
+ (LPVOID) msg_buf, // start of dump
+ bytes_read); // size in bytes
+ }
+
+ DisplayPackedReplMailslotMsg(
+ msg_buf, // start of msg
+ bytes_read, // msg size in bytes
+ Verbose );
+
+ } // while loop
+
+
+Cleanup:
+
+ if ( test_mailslot_handle != (HANDLE)(-1) ) {
+ (VOID) CloseHandle( test_mailslot_handle );
+ }
+
+ if (ApiStatus == NO_ERROR) {
+ return (EXIT_SUCCESS);
+ } else {
+ return (EXIT_FAILURE);
+ }
+
+} // main()
diff --git a/private/net/svcdlls/repl/repltest/test3.c b/private/net/svcdlls/repl/repltest/test3.c
new file mode 100644
index 000000000..2c24eb87f
--- /dev/null
+++ b/private/net/svcdlls/repl/repltest/test3.c
@@ -0,0 +1,87 @@
+
+///*****************************************************************
+/// Microsoft LAN Manager *
+/// Copyright(c) Microsoft Corp., 1987-1990 *
+///*****************************************************************
+
+
+// Module : test2.c
+*
+*
+***************************************************************************//
+
+#define INCL_DOSFILEMGR
+#define INCL_DOSERRORS
+#define INCL_DOSINFOSEG
+#include <os2.h>
+
+
+#include <netcons.h>
+#include <netlib.h>
+#include "repldefs.h"
+#include "test.h"
+
+
+VOID main(int, char*[]);
+
+//
+* FDATE fdateCreation; unsigned short ++ Create date
+* FTIME ftimeCreation; unsigned short ++ Create time
+* FDATE fdateLastAccess; unsigned short _access date
+* FTIME ftimeLastAccess; unsigned short _access time
+* FDATE fdateLastWrite; unsigned short ++ _write date
+* FTIME ftimeLastWrite; unsigned short ++ _write time
+* ULONG cbFile; long ++ file size
+* ULONG cbFileAlloc; long allocated size
+* USHORT attrFile; unsigned short ++ file attributes
+* ULONG cbList; unsigned short ++ EA size
+* UCHAR cchName; unsigned char name len
+* CHAR achName[CCHMAXPATHCOMP]; unsigned char ++ file name
+//
+
+
+
+VOID
+main(argc, argv)
+//*************
+*
+*
+*************************************************************************//
+
+int argc;
+char *argv[];
+{
+ unsigned shan = -1;
+ int scnt = 1;
+ FILEFINDBUF2 sbuf;
+ int i = 0;
+ int NetStatus;
+
+ if (NetStatus = DosChDir((char * )"C:\\LANMAN12\\NETPROG", 0L))
+ NetpKdPrint(("TEST - DosChdir failed\n"));
+
+ else
+ {
+
+ if (!DosFindFirst2((char * )"*.*", (unsigned short * ) & shan,
+ (FILE_NORMAL | FILE_READONLY | FILE_HIDDEN | FILE_SYSTEM),
+ (PVOID) & sbuf, sizeof(sbuf), (unsigned short * ) & scnt, 0x2, 0L))
+
+ do
+ {
+
+ NetpKdPrint(("name= %Fs, namelen:%d, cdate:%d, ctime:%d, wdate:%d, wtime:%d, fsize:%ld, fa:%u, easize:%d\n",
+ (char * )sbuf.achName, sbuf.cchName,
+ sbuf.fdateCreation, sbuf.ftimeCreation,
+ sbuf.fdateLastWrite, sbuf.ftimeLastWrite, sbuf.cbFile, sbuf.attrFile,
+ sbuf.cbList));
+ }
+ while (DosFindNext(shan, (PVOID) & sbuf, sizeof(sbuf),
+ (unsigned short * ) & scnt) == 0);
+
+ DosFindClose(shan);
+
+ }
+}
+
+
diff --git a/private/net/svcdlls/repl/repltest/test4.c b/private/net/svcdlls/repl/repltest/test4.c
new file mode 100644
index 000000000..438b3bf74
--- /dev/null
+++ b/private/net/svcdlls/repl/repltest/test4.c
@@ -0,0 +1,85 @@
+
+///*****************************************************************
+/// Microsoft LAN Manager *
+/// Copyright(c) Microsoft Corp., 1987-1990 *
+///*****************************************************************
+
+
+// Module : test2.c
+*
+*
+***************************************************************************//
+
+#define INCL_DOSFILEMGR
+#define INCL_DOSERRORS
+#define INCL_DOSINFOSEG
+#include <os2.h>
+
+
+#include <netcons.h>
+#include <netlib.h>
+#include "repldefs.h"
+#include "test.h"
+
+
+VOID main(int, char*[]);
+
+//
+* FDATE fdateCreation; unsigned short ++ Create date
+* FTIME ftimeCreation; unsigned short ++ Create time
+* FDATE fdateLastAccess; unsigned short _access date
+* FTIME ftimeLastAccess; unsigned short _access time
+* FDATE fdateLastWrite; unsigned short ++ _write date
+* FTIME ftimeLastWrite; unsigned short ++ _write time
+* ULONG cbFile; long ++ file size
+* ULONG cbFileAlloc; long allocated size
+* USHORT attrFile; unsigned short ++ file attributes
+* ULONG cbList; unsigned short ++ EA size
+* UCHAR cchName; unsigned char name len
+* CHAR achName[CCHMAXPATHCOMP]; unsigned char ++ file name
+//
+
+
+
+VOID
+main(argc, argv)
+//*************
+*
+*
+*************************************************************************//
+
+int argc;
+char *argv[];
+{
+ unsigned shan = -1;
+ int scnt = 1;
+ FILEFINDBUF sbuf;
+ int i = 0;
+ int NetStatus;
+
+ if (NetStatus = DosChDir((char * )"C:\\LANMAN12\\NETPROG", 0L))
+ NetpKdPrint(("TEST - DosChdir failed\n"));
+
+ else
+ {
+
+ if (!DosFindFirst((char * )"*.*", (unsigned short * ) & shan,
+ (FILE_NORMAL | FILE_READONLY | FILE_HIDDEN | FILE_SYSTEM),
+ (PVOID) & sbuf, sizeof(sbuf), (unsigned short * ) & scnt, 0L)) {
+
+ do
+ {
+
+ NetpKdPrint(("name= %s, namelen:%d, cdate:%d, ctime:%d, wdate:%d, wtime:%d, fsize:%ld, fa:%u\n",
+ sbuf.achName, sbuf.cchName, sbuf.fdateCreation, sbuf.ftimeCreation,
+ sbuf.fdateLastWrite, sbuf.ftimeLastWrite, sbuf.cbFile, sbuf.attrFile));
+ }
+ while (DosFindNext(shan, (PVOID) & sbuf, sizeof(sbuf),
+ (unsigned short * ) & scnt) == 0);
+ }
+ DosFindClose(shan);
+
+ }
+}
+
+
diff --git a/private/net/svcdlls/repl/repltest/test5.c b/private/net/svcdlls/repl/repltest/test5.c
new file mode 100644
index 000000000..8d583e28f
--- /dev/null
+++ b/private/net/svcdlls/repl/repltest/test5.c
@@ -0,0 +1,190 @@
+///*****************************************************************
+/// Microsoft LAN Manager *
+/// Copyright(c) Microsoft Corp., 1987-1990 *
+///*****************************************************************
+
+
+// Module : test5.c
+*
+*
+***************************************************************************//
+#define INCL_DOSPROCESS
+#define INCL_DOSFILEMGR
+#define INCL_DOSERRORS
+#define INCL_DOSINFOSEG
+#include <os2.h>
+
+#include <sysbits.h>
+#include <netcons.h>
+#include <netlib.h>
+#include "repldefs.h"
+#include "client.h"
+
+VOID main(int, char*[]);
+char P_import[] = "c:\\lanman12";
+char tmp_buf[MAXPATHLEN];
+char dir_buf[10];
+
+VOID
+main(argc, argv)
+//*************
+*
+*
+*************************************************************************//
+int argc;
+char *argv[];
+{
+ char *dir_name;
+
+ dir_name = (char * )dir_buf;
+
+ strcpyf(dir_name, (char * ) "SERVICES");
+ ReplSetSignalFile(NO_MASTER_SIGNAL, dir_name);
+ DosBeep(1000, 500);
+ DosBeep(1500, 700);
+ DosSleep(30000L);
+
+ strcpyf(dir_name, (char * ) "SERVICES");
+ ReplSetSignalFile(NO_SYNC_SIGNAL, dir_name);
+ DosBeep(1000, 500);
+ DosBeep(1500, 700);
+ DosSleep(30000L);
+
+ strcpyf(dir_name, (char * ) "SERVICES");
+ ReplSetSignalFile(NO_PERM_SIGNAL, dir_name);
+ DosBeep(1000, 500);
+ DosBeep(1500, 700);
+ DosSleep(30000L);
+
+ strcpyf(dir_name, (char * ) "SERVICES");
+ ReplSetSignalFile(OK_SIGNAL, dir_name);
+ DosBeep(1000, 500);
+ DosBeep(1500, 700);
+ DosSleep(30000L);
+
+
+ argc = argc;
+ ++ * argv;
+
+}
+
+
+
+
+
+
+VOID ReplSetSignalFile(signal, dir_name)
+//*********************************
+*
+* writes signal file in dir.
+***********************************************************************//
+unsigned signal;
+char *dir_name;
+{
+ unsigned short act, fhand, bytes_written;
+ char *buf_p;
+ char *file_p;
+ NET_API_STATUS NetStatus;
+
+ //
+
+ buf_p = (char *)ReplClientGetPoolEntry( QUESML_POOL );
+
+ //
+
+ buf_p = (char * )tmp_buf;
+
+
+ strcpyf(buf_p, P_import);
+ strcpyf((char * )(buf_p + strlenf(buf_p)), "\\\0");
+ strcpyf((char * )(buf_p + strlenf(buf_p)), dir_name);
+ file_p = buf_p + strlenf(buf_p) + 1;
+ strcpyf((char * )(buf_p + strlenf(buf_p)), "\\\0");
+
+
+ //
+ // delete old signal file - must make sure, so we delete all of them
+ //
+
+ strcpyf(file_p, (char * ) OK_RP$);
+ NetStatus = DosDelete(buf_p, 0L);
+
+ strcpyf(file_p, (char * ) NO_SYNC_RP$);
+ NetStatus = DosDelete(buf_p, 0L);
+
+ strcpyf(file_p, (char * ) NO_MASTER_RP$);
+ NetStatus = DosDelete(buf_p, 0L);
+
+ strcpyf(file_p, (char * ) NO_PERM_RP$);
+ NetStatus = DosDelete(buf_p, 0L);
+
+
+ switch (signal) {
+ case OK_SIGNAL:
+
+ //
+ ///*******
+ //
+
+ strcpyf(file_p, (char * ) OK_RP$);
+ break;
+
+ case NO_SYNC_SIGNAL:
+
+ //
+ ///***********
+ //
+
+ strcpyf(file_p, (char * ) NO_SYNC_RP$);
+ break;
+
+ case NO_MASTER_SIGNAL:
+
+ //
+ ///*************
+ //
+
+ strcpyf(file_p, (char * ) NO_MASTER_RP$);
+ break;
+
+ case NO_PERM_SIGNAL:
+
+ //
+ ///*************
+ //
+
+ strcpyf(file_p, (char * ) NO_PERM_RP$);
+ break;
+ }
+
+ if ((NetStatus = DosOpen(buf_p, (unsigned short * ) & fhand,
+ (unsigned short * ) & act, 0L, 0,
+ OF_CREATE_FILE ,
+ (OM_SYNC | OM_FAIL_RC | OM_DENY_WRITE | OM_READ_WRITE), 0L))
+
+ || (act != ACTION_CREATED)) {
+
+ //
+
+ AlertLogExit(ALERT_ReplSignalFileErr, NERLOG_ReplSignalFileErr, NetStatus,
+ dir_name, NULL, EXIT);
+
+ //
+
+ NetpKdPrint(("DosOpen Error, NetStatus = %d, act = %d\n", NetStatus, act));
+ } else
+ {
+
+ if (NetStatus = DosWrite(fhand, buf_p, (strlenf(buf_p) + 1),
+ (PUSHORT) & bytes_written))
+
+ NetpKdPrint(("DosOpen Error, NetStatus = %d, bytewritten = %d\n", NetStatus, bytes_written));
+
+ else
+ DosClose(fhand);
+ }
+
+
+}
+
+
diff --git a/private/net/svcdlls/repl/repltest/test6.c b/private/net/svcdlls/repl/repltest/test6.c
new file mode 100644
index 000000000..5cfb1464b
--- /dev/null
+++ b/private/net/svcdlls/repl/repltest/test6.c
@@ -0,0 +1,140 @@
+///*****************************************************************
+/// Microsoft LAN Manager *
+/// Copyright(c) Microsoft Corp., 1987-1990 *
+///*****************************************************************
+
+
+// Module : test6.c
+*
+*
+***************************************************************************//
+#define INCL_DOSPROCESS
+#define INCL_DOSFILEMGR
+#define INCL_NOCOMMON
+#define INCL_DOSERRORS
+#define INCL_DOSINFOSEG
+#include <os2.h>
+
+#include <sysbits.h>
+#include <netcons.h>
+#include <neterr.h>
+#include <netlib.h>
+#include <apiutil.h>
+
+
+#define REPL_INI "REPL.INI"
+
+// Extent modes
+#define FILE_EXT 1
+#define TREE_EXT 2
+
+// Integrity mode
+#define FILE_INTG 1
+#define TREE_INTG 2
+
+#define INTEGRITY_SW "INTEGRITY"
+#define EXTENT_SW "EXTENT"
+#define FILE_SW "FILE"
+#define TREE_SW "TREE"
+
+
+char repl_file[] = REPL_INI;
+
+NET_API_STATUS GetParm(const char *, char *, unsigned short,
+unsigned short *, unsigned short);
+
+VOID GetReplIni(unsigned *, unsigned *);
+
+VOID main(int, char*[]);
+
+
+VOID
+main(argc, argv)
+//*************
+*
+*
+*************************************************************************//
+int argc;
+char *argv[];
+{
+ unsigned integ;
+ unsigned ext;
+
+ GetReplIni(&integ, &ext);
+ NetpKdPrint(("GetReplIni: integrity= %d, extent = %d\n", integ, ext));
+ argc = argc;
+ ++ * argv;
+
+}
+
+
+VOID GetReplIni(integrity, extent)
+//********************************
+*
+* Attempts to _read file REPL.INI in the current directory, if successfull
+* reads the INTEGRITY and EXTENT values and returns them, otherwise
+* plugs defaults.
+*
+* EXIT integrity - pointer to INTEGRITY value.
+* extent - pointer to EXTENT value.
+*
+****************************************************************************//
+unsigned // integrity;
+unsigned // extent;
+{
+ unsigned short act, fhand, parmlen;
+ NET_API_STATUS NetStatus;
+ char buf[15];
+ long seekback;
+
+ //
+ // plug defaults
+ //
+
+ *integrity = FILE_INTG;
+ *extent = TREE_EXT;
+
+
+ if (NetStatus = DosOpen((char * )repl_file, (unsigned short * ) & fhand,
+ (unsigned short * ) & act, 0L, 0,
+ OF_EXISTING_FILE , OM_DENY_WRITE, 0L)) {
+
+
+ if (NetStatus == ERROR_OPEN_FAILED) // makes more sense
+ NetStatus = ERROR_FILE_NOT_FOUND;
+
+ if ((NetStatus == ERROR_FILE_NOT_FOUND) || (NetStatus == ERROR_DRIVE_LOCKED)
+ || (NetStatus == ERROR_ACCESS_DENIED))
+
+ NetpKdPrint (("DOSOPEN NetStatus = %d\n", NetStatus));
+ } else
+ {
+
+ if ((NetStatus = GetParm((const char * ) INTEGRITY_SW, (char * )buf,
+ sizeof(buf), (unsigned short * ) & parmlen, fhand)) == 0)
+
+ if (parmlen >= 4)
+ if (strnicmpf((char * )buf, (char * ) TREE_SW,
+ sizeof(TREE_SW)) == 0)
+ *integrity = TREE_INTG;
+
+ //
+ // reset file pointer to read the next parm
+ //
+
+ if (NetStatus = DosChgFilePtr(fhand, 0L, 0, (PULONG) & seekback))
+ NetpKdPrint (("DOSChgFilePtr NetStatus = %d\n", NetStatus));
+
+ if ((NetStatus = GetParm((const char * ) EXTENT_SW, (char * )buf,
+ sizeof(buf), &parmlen, fhand)) == 0)
+ if (parmlen >= 4)
+ if (strnicmpf((char * )buf, (char * ) FILE_SW,
+ sizeof(FILE_SW)) == 0)
+ *extent = FILE_EXT;
+
+ DosClose(fhand);
+
+ }
+}
+
+
diff --git a/private/net/svcdlls/repl/repltest/test7.c b/private/net/svcdlls/repl/repltest/test7.c
new file mode 100644
index 000000000..e20e3c740
--- /dev/null
+++ b/private/net/svcdlls/repl/repltest/test7.c
@@ -0,0 +1,59 @@
+
+///*****************************************************************
+/// Microsoft LAN Manager *
+/// Copyright(c) Microsoft Corp., 1987-1990 *
+///*****************************************************************
+
+
+// Module : test6.c
+*
+*
+***************************************************************************//
+#define INCL_DOSPROCESS
+#define INCL_DOSFILEMGR
+#define INCL_NOCOMMON
+#define INCL_DOSERRORS
+#define INCL_DOSINFOSEG
+#include <os2.h>
+
+#include <sysbits.h>
+#include <netcons.h>
+#include <neterr.h>
+#include <netlib.h>
+#include <apiutil.h>
+
+#define CLI_PATH "C:\\IMP\\AA"
+#define TMP_PATH "C:\\IMP\\BB"
+
+
+VOID main(int, char*[]);
+
+
+VOID
+main(argc, argv)
+//*************
+*
+*
+*************************************************************************//
+int argc;
+char *argv[];
+{
+ int NetStatus;
+ char *client_path;
+ char *tmp_path;
+
+ client_path = (char * ) CLI_PATH;
+ tmp_path = (char * ) TMP_PATH;
+
+ NetpKdPrint(("DosMove, source: %Fs, dest:%Fs\n", client_path, tmp_path));
+
+ NetStatus = DosMove(client_path, tmp_path, 0L);
+
+ NetpKdPrint(("DosMove error = %d\n", NetStatus));
+
+ argc = argc;
+ ++ * argv;
+
+}
+
+
diff --git a/private/net/svcdlls/repl/repltest/testaft.c b/private/net/svcdlls/repl/repltest/testaft.c
new file mode 100644
index 000000000..47d77ff78
--- /dev/null
+++ b/private/net/svcdlls/repl/repltest/testaft.c
@@ -0,0 +1,90 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ TestAft.c
+
+Abstract:
+
+ This code tests the repl config APIs immediately after the service
+ has stopped.
+
+Author:
+
+ John Rogers (JohnRo) 15-Dec-1992
+
+Environment:
+
+ Portable to any flat, 32-bit environment. (Uses Win32 typedefs.)
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+ 15-Dec-1992 JohnRo
+ Created.
+
+--*/
+
+// These must be included first:
+
+#include <windows.h> // IN, DWORD, needed by <repltest.h>.
+#include <lmcons.h> // NET_API_STATUS.
+
+// These may be included in any order:
+
+#include <lmapibuf.h> // NetApiBufferFree().
+#include <lmerr.h> // NERR_, ERROR_, and NO_ERROR equates.
+#include <lmrepl.h> // NetRepl APIs, REPL_INFO_0, etc.
+#include <lmsvc.h> // NetService APIs, SERVICE_ equates, etc.
+#include <netdebug.h> // NetpKdPrint(()), NetpAssert(), etc.
+#include <netlib.h> // NetpIsServiceStarted().
+#include <stdlib.h> // EXIT_FAILURE, EXIT_SUCCESS, _CRTAPI1.
+
+
+int _CRTAPI1
+main(
+ IN int argc,
+ IN char *argv[]
+ )
+{
+ NET_API_STATUS ApiStatus;
+ LPVOID Info = NULL;
+ LPVOID StatBuf = NULL;
+
+ NetpKdPrint(( "TestAft: starting up...\n" ));
+
+ // BUGBUG; // make sure service is working
+ NetpAssert( NetpIsServiceStarted( SERVICE_REPL ) );
+
+ ApiStatus = NetServiceControl(
+ NULL, // server name (local)
+ SERVICE_REPL, // Servicename
+ SERVICE_CTRL_UNINSTALL, // Opcode
+ 0, // Service-specific args
+ (LPBYTE *) (LPVOID) &StatBuf); // Alloc return buffer
+ NetpAssert( ApiStatus == NO_ERROR );
+ NetpAssert( StatBuf != NULL );
+
+ NetpAssert( !NetpIsServiceStarted( SERVICE_REPL ) );
+
+ //
+ // Now (quickly) try an API...
+ //
+
+ ApiStatus = NetReplGetInfo(
+ NULL, // no server name
+ 0, // info level
+ (LPBYTE *) (LPVOID) &Info ); // alloc and set ptr;
+ NetpAssert( ApiStatus == NO_ERROR );
+ NetpAssert( Info != NULL );
+
+
+ NetpKdPrint(( "TestAft: done!\n" ));
+
+ // BUGBUG: memory leak!
+
+ return (EXIT_SUCCESS);
+
+} // main
diff --git a/private/net/svcdlls/repl/repltest/testcopy.c b/private/net/svcdlls/repl/repltest/testcopy.c
new file mode 100644
index 000000000..23929922a
--- /dev/null
+++ b/private/net/svcdlls/repl/repltest/testcopy.c
@@ -0,0 +1,268 @@
+
+
+// These must be included first:
+
+#include <windef.h> // LPWSTR, TRUE, etc.
+#include <lmcons.h> // NET_API_STATUS.
+
+// These may be included in any order:
+
+#include <assert.h> // assert().
+
+#include <client.h> // RCGlobalFsTimeResultionMs.
+#undef IF_DEBUG
+
+#include <debuglib.h> // NetlibpTrace, NETLIB_DEBUG_ equates.
+#undef IF_DEBUG
+
+#include <filefind.h> // LPREPL_FIND_HANDLE, etc.
+#include <netdebug.h> // FORMAT_ equates.
+#include <netlib.h> // NetpMemoryAllocate(), etc
+#include <repldefs.h> // ReplCopyFile(), UNKNOWN_FS_RESOLUTION, etc.
+#include <repltest.h> // FakeFindData(), ShowFileTimes(), ShowTime().
+#include <stdio.h>
+#include <stdlib.h> // EXIT_FAILURE, EXIT_SUCCESS, _CRTAPI1.
+#include <tstring.h> // NetpAllocWStrFromStr(), STRCPY(), etc.
+
+
+enum { UNKNOWN, JUST_FILE, DIR_ITSELF, TREE } WHAT_TO_COPY;
+
+
+VOID
+Usage(
+ IN char * ProgName
+ )
+{
+ (VOID) printf(
+ "This program copies a file or a directory, testing replicator APIs.\n"
+ "Author: JR (John Rogers, JohnRo@Microsoft)\n\n"
+ "Usage: %s [opts] src dest\n\n"
+ "\nWhere opts are:\n"
+ " -d copy directory all by itself\n"
+ " -f force copy over existing dest\n"
+ " -p pretend to copy but don't\n"
+ " -r copy recurvely (dirs and files)\n"
+ " -v verbose\n", ProgName);
+}
+
+int _CRTAPI1
+main(
+ IN int argc,
+ IN char *argv[]
+ )
+{
+ NET_API_STATUS ApiStatus;
+ int ArgNumber;
+ LPWSTR Dest = NULL;
+ LPTSTR DestParent = NULL;
+ BOOL FailIfExists = TRUE;
+ BOOL GotPermission = FALSE;
+ BOOL Pretend = FALSE;
+ LPWSTR Source = NULL;
+ enum WHAT_TO_COPY CopyType = UNKNOWN;
+ BOOL Verbose = FALSE;
+
+
+ //
+ // Process command-line arguments.
+ //
+ for (ArgNumber = 1; ArgNumber < argc; ArgNumber++) {
+ if ((*argv[ArgNumber] == '-') || (*argv[ArgNumber] == '/')) {
+ switch (tolower(*(argv[ArgNumber]+1))) // Process switches
+ {
+
+ case 'd' : // Copy directory.
+ if (CopyType != UNKNOWN) {
+ ApiStatus = ERROR_INVALID_PARAMETER;
+ Usage( argv[0] );
+ goto Cleanup;
+ }
+ CopyType = DIR_ITSELF;
+ break;
+
+ case 'f' : // Force delete of existing dest.
+ FailIfExists = FALSE;
+ break;
+
+ case 'p' : // Pretend to copy but don't (to test other stuff)
+ Pretend = TRUE;
+ break;
+
+ case 'r' : // Recursive copy.
+ if (CopyType != UNKNOWN) {
+ ApiStatus = ERROR_INVALID_PARAMETER;
+ Usage( argv[0] );
+ goto Cleanup;
+ }
+ CopyType = TREE;
+ break;
+
+ case 'v' :
+ NetlibpTrace = NETLIB_DEBUG_ALL;
+ ReplGlobalTrace = REPL_DEBUG_ALL;
+ Verbose = TRUE;
+ break;
+
+ default :
+ Usage( argv[0] );
+ ApiStatus = ERROR_INVALID_PARAMETER;
+ goto Cleanup;
+ }
+ } else { // Must be a file name.
+ if (Source == NULL) {
+ Source = NetpAllocWStrFromStr(argv[ArgNumber]);
+ assert( Source != NULL );
+ } else if (Dest == NULL) {
+ Dest = NetpAllocWStrFromStr(argv[ArgNumber]);
+ assert( Dest != NULL );
+ } else {
+ Usage( argv[0] ); // Too many file names.
+ ApiStatus = ERROR_INVALID_PARAMETER;
+ goto Cleanup;
+ }
+ }
+ }
+
+ if (CopyType == UNKNOWN) {
+ CopyType = JUST_FILE;
+ }
+
+ if ( (Source==NULL) || (Dest==NULL) || (CopyType==UNKNOWN) ) {
+ Usage( argv[0] );
+ ApiStatus = ERROR_INVALID_PARAMETER;
+ goto Cleanup;
+ }
+
+ //
+ // Get fs resolution using some absolute directory name on import side.
+ //
+ DestParent = NetpMemoryAllocate(
+ STRSIZE(Dest) + STRSIZE( (LPTSTR) TEXT("\\..") ) );
+ assert( DestParent != NULL );
+ if (Dest[1] == TCHAR_COLON) {
+ DestParent[0] = Dest[0]; // drive letter.
+ DestParent[1] = TCHAR_COLON;
+ DestParent[2] = TCHAR_BACKSLASH;
+ DestParent[3] = TCHAR_EOS;
+ } else {
+ (VOID) STRCPY(
+ DestParent, // dest
+ Dest ); // src
+ (VOID) STRCAT(
+ DestParent, // dest
+ (LPTSTR) TEXT("\\..") ); // src
+ }
+ RCGlobalFsTimeResolutionSecs =
+ ReplGetFsTimeResolutionSecs( DestParent );
+
+ assert( RCGlobalFsTimeResolutionSecs != UNKNOWN_FS_RESOLUTION );
+
+ //
+ // Init and enable the backup/restore permissions in our process token.
+ //
+ ApiStatus = ReplInitBackupPermission();
+ (VOID) printf( "Back from ReplInitBackupPermission, status "
+ FORMAT_API_STATUS ".\n", ApiStatus );
+
+ ApiStatus = ReplEnableBackupPermission();
+ (VOID) printf( "Back from ReplEnableBackupPermission, status "
+ FORMAT_API_STATUS ".\n", ApiStatus );
+
+ GotPermission = TRUE;
+
+ //
+ // Copy the file, directory, or tree (unless we're pretending to copy).
+ //
+ if ( !Pretend ) {
+ if (CopyType==JUST_FILE) {
+ ApiStatus = ReplCopyFile(
+ Source,
+ Dest,
+ FailIfExists );
+ } else if (CopyType==DIR_ITSELF) {
+ ApiStatus = ReplCopyDirectoryItself(
+ Source,
+ Dest,
+ FailIfExists );
+ } else {
+ assert( CopyType==TREE );
+ ApiStatus = ReplCopyTree(
+ Source,
+ Dest );
+ }
+
+ (VOID) printf(
+ FORMAT_LPWSTR " => " FORMAT_LPWSTR ": " FORMAT_API_STATUS ".\n",
+ Source, Dest, ApiStatus );
+
+ } else {
+ (VOID) printf(
+ "PRETENDING TO COPY "
+ FORMAT_LPWSTR " => " FORMAT_LPWSTR ".\n",
+ Source, Dest );
+
+ // Pretend to get good status from copy.
+ ApiStatus = NO_ERROR;
+ }
+
+ if (CopyType==JUST_FILE) {
+ REPL_WIN32_FIND_DATA DestFindData;
+ REPL_WIN32_FIND_DATA SourceFindData;
+
+ //
+ // Print resulting times.
+ //
+ FakeFindData(
+ Dest, // file name
+ Verbose,
+ &DestFindData
+ );
+ FakeFindData(
+ Source, // file name
+ Verbose,
+ &SourceFindData
+ );
+ if (Verbose) {
+ ShowTime(
+ "Source times",
+ &( SourceFindData.fdFound.ftLastWriteTime ) );
+
+ ShowTime(
+ "Dest times ",
+ &( DestFindData.fdFound.ftLastWriteTime ) );
+ }
+
+ //
+ // Make sure file time is close enough.
+ //
+ if ( !ReplIsFileTimeCloseEnough(
+ &SourceFindData.fdFound.ftLastWriteTime,
+ &DestFindData.fdFound.ftLastWriteTime) ) {
+
+ (VOID) printf("***** FILE TIMES (AFTER COPY) NOT CLOSE ENOUGH!\n");
+ }
+ }
+
+ // BUGBUG; // make sure checksums match.
+
+
+Cleanup:
+ if (Dest != NULL) {
+ NetpMemoryFree( Dest );
+ }
+ if (DestParent != NULL) {
+ NetpMemoryFree( DestParent );
+ }
+ if (GotPermission) {
+ (VOID) ReplDisableBackupPermission();
+ }
+ if (Source != NULL) {
+ NetpMemoryFree( Source );
+ }
+
+ if (ApiStatus == NO_ERROR) {
+ return (EXIT_SUCCESS);
+ } else {
+ return (EXIT_FAILURE);
+ }
+}
diff --git a/private/net/svcdlls/repl/repltest/testexp.c b/private/net/svcdlls/repl/repltest/testexp.c
new file mode 100644
index 000000000..f7a7aa0e0
--- /dev/null
+++ b/private/net/svcdlls/repl/repltest/testexp.c
@@ -0,0 +1,631 @@
+/*++
+
+Copyright (c) 1992-1993 Microsoft Corporation
+
+Module Name:
+
+ TestExp.c
+
+Abstract:
+
+ This code tests the repl export dir APIs.
+
+Author:
+
+ John Rogers (JohnRo) 15-Jan-1992
+
+Environment:
+
+ Portable to any flat, 32-bit environment. (Uses Win32 typedefs.)
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Notes:
+
+ This code assumes that the info levels are subsets of each other.
+
+Revision History:
+
+ 15-Jan-1992 JohnRo
+ Created.
+ 17-Jan-1992 JohnRo
+ Avoid a MIPS compile-time warning.
+ 17-Jan-1992 JohnRo
+ Changed to call real APIs.
+ 24-Jan-1992 JohnRo
+ Changed to use LPTSTR etc.
+ 29-Jan-1992 JohnRo
+ Added test of get info.
+ Added display of ParmError.
+ 13-Feb-1992 JohnRo
+ Use NetReplExportDirDel() to allow use with persistent config.
+ 19-Feb-1992 JohnRo
+ Added tests of set info, del, lock, and unlock.
+ Use the EXIT_A_TEST() macro.
+ 27-Feb-1992 JohnRo
+ Avoid assert because of trying invalid level test of add.
+ 14-Mar-1992 JohnRo
+ Minor debug output cleanup.
+ 20-Mar-1992 JohnRo
+ Caller should wait for service start (if necessary), not us.
+ 28-Jul-1992 JohnRo
+ RAID 2274: repl svc should impersonate caller.
+ 13-Nov-1992 JohnRo
+ Detect get info returning NULL buffer pointer.
+ 03-Dec-1992 JohnRo
+ Repl tests for remote registry. Undo old thread junk.
+ 23-Jul-1993 JohnRo
+ RAID 16685: NT repl should ignore LPTn to protect downlevel.
+
+--*/
+
+// These must be included first:
+
+#include <windows.h> // IN, DWORD, needed by <repltest.h>.
+#include <lmcons.h> // NET_API_STATUS.
+
+// These may be included in any order:
+
+#include <expdir.h> // ExportDirIsApiRecordValid().
+#include <lmapibuf.h> // NetApiBufferFree().
+#include <lmerr.h> // NERR_, ERROR_, and NO_ERROR equates.
+#include <lmrepl.h> // NetRepl APIs, REPL_EDIR_INFO_0, etc.
+#include <netdebug.h> // NetpDbgDisplay routines.
+#include <repltest.h> // TestExportDirApis(), etc.
+
+DBGSTATIC void
+TestExportDirAdd(
+ IN LPTSTR UncServerName OPTIONAL,
+ IN LPTSTR DirName,
+ IN DWORD Level,
+ IN BOOL ImportOnly,
+ IN BOOL OrdinaryUserOnly,
+ IN NET_API_STATUS ExpectedStatus
+ );
+
+DBGSTATIC void
+TestExportDirDel(
+ IN LPTSTR UncServerName OPTIONAL,
+ IN LPTSTR DirName,
+ IN BOOL ImportOnly,
+ IN BOOL OrdinaryUserOnly,
+ IN NET_API_STATUS ExpectedStatus
+ );
+
+DBGSTATIC void
+TestExportDirEnum(
+ IN LPTSTR UncServerName OPTIONAL,
+ IN BOOL ImportOnly
+ );
+
+DBGSTATIC void
+TestExportDirGet(
+ IN LPTSTR UncServerName OPTIONAL,
+ IN LPTSTR DirName,
+ IN DWORD Level,
+ IN BOOL ImportOnly,
+ IN NET_API_STATUS ExpectedStatus
+ );
+
+DBGSTATIC void
+TestExportDirLock(
+ IN LPTSTR UncServerName OPTIONAL,
+ IN LPTSTR DirName,
+ IN BOOL ImportOnly,
+ IN BOOL OrdinaryUserOnly,
+ IN NET_API_STATUS ExpectedStatus
+ );
+
+DBGSTATIC void
+TestExportDirSet(
+ IN LPTSTR UncServerName OPTIONAL,
+ IN LPTSTR DirName,
+ IN DWORD Level,
+ IN DWORD Integrity,
+ IN DWORD Extent,
+ IN BOOL ImportOnly,
+ IN BOOL OrdinaryUserOnly,
+ IN NET_API_STATUS ExpectedStatus
+ );
+
+DBGSTATIC void
+TestExportDirUnlock(
+ IN LPTSTR UncServerName OPTIONAL,
+ IN LPTSTR DirName,
+ IN DWORD Force,
+ IN BOOL ImportOnly,
+ IN BOOL OrdinaryUserOnly,
+ IN NET_API_STATUS ExpectedStatus
+ );
+
+
+VOID
+TestExportDirApis(
+ IN LPTSTR UncServerName OPTIONAL,
+ IN BOOL ImportOnly,
+ IN BOOL OrdinaryUserOnly,
+ IN BOOL Verbose
+ )
+{
+
+ NetpKdPrint(( "TestExportDirApis: starting...\n" ));
+
+ TestExportDirEnum( UncServerName,
+ ImportOnly );
+
+#define EXISTING_NAME TEXT("flarp") /* created by me by hand. --JR */
+#define NONEXISTENT_NAME TEXT("notthere")
+
+ // Some tests depend on NONEXISTENT_NAME not being in repl config data.
+ // This may or may not be the first time, so don't check error code.
+ (void) NetReplExportDirDel( UncServerName, EXISTING_NAME );
+ (void) NetReplExportDirDel( UncServerName, NONEXISTENT_NAME );
+
+ TestExportDirLock( UncServerName,
+ NULL,
+ ImportOnly,
+ OrdinaryUserOnly,
+ ERROR_INVALID_PARAMETER );
+ TestExportDirLock( UncServerName,
+ EXISTING_NAME,
+ ImportOnly,
+ OrdinaryUserOnly,
+ NERR_UnknownDevDir );
+ TestExportDirLock( UncServerName,
+ NONEXISTENT_NAME,
+ ImportOnly,
+ OrdinaryUserOnly,
+ NERR_UnknownDevDir );
+
+ TestExportDirUnlock( UncServerName,
+ EXISTING_NAME,
+ 135, // bad force level
+ ImportOnly,
+ OrdinaryUserOnly,
+ ERROR_INVALID_PARAMETER );
+
+ TestExportDirGet( UncServerName,
+ NONEXISTENT_NAME,
+ 3,
+ ImportOnly,
+ ERROR_INVALID_LEVEL );
+ TestExportDirGet( UncServerName,
+ NONEXISTENT_NAME,
+ 0,
+ ImportOnly,
+ NERR_UnknownDevDir );
+ TestExportDirGet( UncServerName,
+ NONEXISTENT_NAME,
+ 1,
+ ImportOnly,
+ NERR_UnknownDevDir );
+ TestExportDirGet( UncServerName,
+ NONEXISTENT_NAME,
+ 2,
+ ImportOnly,
+ NERR_UnknownDevDir );
+
+ TestExportDirAdd( UncServerName,
+ EXISTING_NAME,
+ 0,
+ ImportOnly,
+ OrdinaryUserOnly,
+ ERROR_INVALID_LEVEL );
+ TestExportDirAdd( UncServerName,
+ EXISTING_NAME,
+ 1,
+ ImportOnly,
+ OrdinaryUserOnly,
+ NO_ERROR );
+ TestExportDirAdd( UncServerName,
+ EXISTING_NAME,
+ 1,
+ ImportOnly,
+ OrdinaryUserOnly,
+ ERROR_ALREADY_EXISTS );
+ TestExportDirAdd( UncServerName,
+ NONEXISTENT_NAME,
+ 1,
+ ImportOnly,
+ OrdinaryUserOnly,
+ NO_ERROR ); // create
+ TestExportDirAdd( UncServerName,
+ NONEXISTENT_NAME,
+ 1,
+ ImportOnly,
+ OrdinaryUserOnly,
+ ERROR_ALREADY_EXISTS ); // check that create really worked
+ TestExportDirAdd( UncServerName,
+ EXISTING_NAME,
+ 2,
+ ImportOnly,
+ OrdinaryUserOnly,
+ ERROR_INVALID_LEVEL );
+
+ TestExportDirGet( UncServerName,
+ EXISTING_NAME,
+ 2,
+ ImportOnly,
+ NO_ERROR ); // check create
+
+ TestExportDirLock( UncServerName,
+ EXISTING_NAME,
+ ImportOnly,
+ OrdinaryUserOnly,
+ NO_ERROR );
+ TestExportDirLock( UncServerName,
+ NONEXISTENT_NAME,
+ ImportOnly,
+ OrdinaryUserOnly,
+ NO_ERROR );
+
+ TestExportDirEnum( UncServerName,
+ ImportOnly ); // check locks
+
+ TestExportDirUnlock( UncServerName,
+ EXISTING_NAME,
+ REPL_UNLOCK_NOFORCE,
+ ImportOnly,
+ OrdinaryUserOnly,
+ NO_ERROR );
+ TestExportDirUnlock( UncServerName,
+ NONEXISTENT_NAME,
+ REPL_UNLOCK_NOFORCE,
+ ImportOnly,
+ OrdinaryUserOnly,
+ NO_ERROR );
+
+ TestExportDirEnum( UncServerName,
+ ImportOnly ); // check unlocks
+
+ TestExportDirLock( UncServerName,
+ EXISTING_NAME,
+ ImportOnly,
+ OrdinaryUserOnly,
+ NO_ERROR ); // lock=1
+ TestExportDirLock( UncServerName,
+ EXISTING_NAME,
+ ImportOnly,
+ OrdinaryUserOnly,
+ NO_ERROR ); // lock=2
+ TestExportDirUnlock( UncServerName,
+ EXISTING_NAME,
+ REPL_UNLOCK_FORCE,
+ ImportOnly,
+ OrdinaryUserOnly,
+ NO_ERROR ); // lock=0
+
+ TestExportDirSet(
+ UncServerName,
+ EXISTING_NAME,
+ 12345, // level
+ REPL_INTEGRITY_FILE,
+ REPL_EXTENT_FILE,
+ ImportOnly,
+ OrdinaryUserOnly,
+ ERROR_INVALID_LEVEL );
+
+ TestExportDirSet(
+ UncServerName,
+ EXISTING_NAME,
+ 1, // level
+ REPL_INTEGRITY_FILE,
+ REPL_EXTENT_FILE,
+ ImportOnly,
+ OrdinaryUserOnly,
+ NO_ERROR );
+
+ TestExportDirSet(
+ UncServerName,
+ EXISTING_NAME,
+ 1, // level
+ 12355, // integrity (bad)
+ REPL_EXTENT_FILE,
+ ImportOnly,
+ OrdinaryUserOnly,
+ ERROR_INVALID_PARAMETER );
+
+ // OK, now lets delete everything and see how enum works with empty list.
+ TestExportDirDel( UncServerName,
+ EXISTING_NAME,
+ ImportOnly,
+ OrdinaryUserOnly,
+ NO_ERROR );
+ TestExportDirDel( UncServerName,
+ NONEXISTENT_NAME,
+ ImportOnly,
+ OrdinaryUserOnly,
+ NO_ERROR );
+ TestExportDirDel( UncServerName,
+ NONEXISTENT_NAME,
+ ImportOnly,
+ OrdinaryUserOnly,
+ NERR_UnknownDevDir );
+ TestExportDirEnum( UncServerName, ImportOnly );
+
+ NetpKdPrint(( "TestExportDirApis: returning...\n" ));
+
+} // TestExportDirApis
+
+
+DBGSTATIC void
+TestExportDirAdd(
+ IN LPTSTR UncServerName OPTIONAL,
+ IN LPTSTR DirName,
+ IN DWORD Level,
+ IN BOOL ImportOnly,
+ IN BOOL OrdinaryUserOnly,
+ IN NET_API_STATUS ExpectedStatus
+ )
+{
+ NET_API_STATUS ApiStatus;
+ REPL_EDIR_INFO_1 Info;
+ DWORD ParmError;
+
+ Info.rped1_dirname = DirName;
+
+ Info.rped1_integrity = REPL_INTEGRITY_FILE;
+ Info.rped1_extent = REPL_EXTENT_FILE;
+
+ if (Level <= 1) {
+ NetpKdPrint(( "TestExportDirAdd: structure we'll try to add:\n" ));
+ NetpDbgDisplayReplExportDir( Level, & Info );
+ }
+
+ NetpKdPrint(( "TestExportDirAdd: trying add level " FORMAT_DWORD
+ ".\n", Level ));
+ ApiStatus = NetReplExportDirAdd (
+ UncServerName,
+ Level,
+ (LPBYTE) (LPVOID) & Info,
+ & ParmError );
+
+ NetpKdPrint(( "TestExportDirAdd: back from add, status="
+ FORMAT_API_STATUS ", parm err=" FORMAT_LONG ".\n" ,
+ ApiStatus, (LONG) ParmError ));
+
+ if ( !OrdinaryUserOnly) {
+ NetpAssert( ApiStatus == ExpectedStatus );
+ }
+
+} // TestExportDirAdd
+
+
+DBGSTATIC void
+TestExportDirDel(
+ IN LPTSTR UncServerName OPTIONAL,
+ IN LPTSTR DirName,
+ IN BOOL ImportOnly,
+ IN BOOL OrdinaryUserOnly,
+ IN NET_API_STATUS ExpectedStatus
+ )
+{
+ NET_API_STATUS ApiStatus;
+
+ NetpKdPrint(( "TestExportDirDel: trying to del '" FORMAT_LPTSTR "'.\n",
+ DirName ));
+
+ ApiStatus = NetReplExportDirDel (
+ UncServerName,
+ DirName );
+
+ NetpKdPrint(( "TestExportDirDel: back from del, status="
+ FORMAT_API_STATUS ".\n", ApiStatus ));
+
+ if ( !OrdinaryUserOnly) {
+ NetpAssert( ApiStatus == ExpectedStatus );
+ }
+
+} // TestExportDirDel
+
+
+DBGSTATIC void
+TestExportDirEnum(
+ IN LPTSTR UncServerName OPTIONAL,
+ IN BOOL ImportOnly
+ )
+{
+ NET_API_STATUS ApiStatus;
+ LPVOID BufPtr;
+ DWORD EntriesRead;
+ DWORD Level;
+ DWORD TotalEntries;
+
+ for (Level=0; Level<=2; ++Level) {
+
+ NetpKdPrint(( "TestExportDirEnum: trying enum level " FORMAT_DWORD
+ ".\n", Level ));
+ ApiStatus = NetReplExportDirEnum (
+ UncServerName,
+ Level,
+ (LPBYTE *) & BufPtr,
+ 1, // pref max size
+ & EntriesRead,
+ & TotalEntries,
+ NULL ); // no resume handle
+
+ NetpKdPrint(( "TestExportDirEnum: back from enum, status="
+ FORMAT_API_STATUS ".\n" , ApiStatus ));
+ NetpKdPrint(( " alloc'ed buffer at " FORMAT_LPVOID ".\n",
+ (LPVOID) BufPtr ));
+ NetpKdPrint(( " entries read=" FORMAT_DWORD
+ ", total=" FORMAT_DWORD "\n", EntriesRead, TotalEntries ));
+
+ NetpAssert( ApiStatus == NO_ERROR );
+
+ if (BufPtr != NULL) {
+ NetpDbgDisplayReplExportDirArray( Level, BufPtr, EntriesRead );
+
+ ApiStatus = NetApiBufferFree( BufPtr );
+ NetpAssert( ApiStatus == NO_ERROR );
+ }
+ }
+
+} // TestExportDirEnum
+
+
+DBGSTATIC void
+TestExportDirGet(
+ IN LPTSTR UncServerName OPTIONAL,
+ IN LPTSTR DirName,
+ IN DWORD Level,
+ IN BOOL ImportOnly,
+ IN NET_API_STATUS ExpectedStatus
+ )
+{
+ NET_API_STATUS ApiStatus;
+ LPVOID Info = NULL;
+
+ NetpKdPrint(( "TestExportDirGet: trying level " FORMAT_DWORD " for '"
+ FORMAT_LPTSTR "'.\n", Level, DirName ));
+
+ ApiStatus = NetReplExportDirGetInfo (
+ UncServerName,
+ DirName,
+ Level,
+ (LPBYTE *) (LPVOID) & Info ); // alloc and set pointer
+
+ NetpKdPrint(( "TestExportDirGet: back from get info, status="
+ FORMAT_API_STATUS ".\n", ApiStatus ));
+
+ NetpAssert( ApiStatus == ExpectedStatus );
+ if (ApiStatus == NO_ERROR) {
+ NetpKdPrint(( "TestExportDirGet: structure we got back:\n" ));
+ NetpAssert( Info != NULL );
+ NetpDbgDisplayReplExportDir( Level, Info );
+
+ ApiStatus = NetApiBufferFree( Info );
+ NetpAssert( ApiStatus == NO_ERROR );
+ }
+
+} // TestExportDirGet
+
+
+DBGSTATIC void
+TestExportDirLock(
+ IN LPTSTR UncServerName OPTIONAL,
+ IN LPTSTR DirName,
+ IN BOOL ImportOnly,
+ IN BOOL OrdinaryUserOnly,
+ IN NET_API_STATUS ExpectedStatus
+ )
+{
+ NET_API_STATUS ApiStatus;
+
+ NetpKdPrint(( "TestExportDirLock: trying lock on '" FORMAT_LPTSTR "'.\n",
+ OPTIONAL_LPTSTR( DirName ) ));
+
+ ApiStatus = NetReplExportDirLock (
+ UncServerName,
+ DirName );
+
+ NetpKdPrint(( "TestExportDirLock: back from API, status="
+ FORMAT_API_STATUS ".\n", ApiStatus ));
+
+ if ( !OrdinaryUserOnly) {
+ NetpAssert( ApiStatus == ExpectedStatus );
+ }
+
+} // TestExportDirLock
+
+
+DBGSTATIC void
+TestExportDirSet(
+ IN LPTSTR UncServerName OPTIONAL,
+ IN LPTSTR DirName,
+ IN DWORD Level,
+ IN DWORD Integrity,
+ IN DWORD Extent,
+ IN BOOL ImportOnly,
+ IN BOOL OrdinaryUserOnly,
+ IN NET_API_STATUS ExpectedStatus
+ )
+{
+ LPREPL_EDIR_INFO_1 ApiRecord;
+ NET_API_STATUS ApiStatus;
+ const DWORD GetInfoLevel = 1;
+ LPVOID Info;
+ DWORD ParmError;
+
+ NetpKdPrint(( "TestExportDirSet: getting level " FORMAT_DWORD ".\n",
+ Level ));
+
+ ApiStatus = NetReplExportDirGetInfo (
+ UncServerName,
+ DirName,
+ GetInfoLevel, // guaranteed good level
+ (LPBYTE *) (LPVOID) & Info ); // alloc and set pointer
+
+ NetpKdPrint(( "TestExportDirSet: back from get info(" FORMAT_DWORD
+ "), status=" FORMAT_API_STATUS ".\n", GetInfoLevel, ApiStatus ));
+
+ NetpAssert( ApiStatus == NO_ERROR );
+ if (ApiStatus != NO_ERROR) { // Can't continue if we couldn't get.
+ return;
+ }
+
+ NetpKdPrint(( "TestExportDirSet: old structure we got back:\n" ));
+ NetpDbgDisplayReplExportDir( GetInfoLevel, Info );
+
+ NetpAssert( ExportDirIsApiRecordValid( GetInfoLevel, Info, NULL ) );
+
+ ApiRecord = Info;
+ ApiRecord->rped1_integrity = Integrity;
+ ApiRecord->rped1_extent = Extent;
+
+ if (ExpectedStatus != ERROR_INVALID_LEVEL) {
+ NetpKdPrint(( "TestExportDirSet: structure we'll try to Set:\n" ));
+ NetpDbgDisplayReplExportDir( Level, Info );
+ }
+
+ NetpKdPrint(( "TestExportDirSet: trying Set level " FORMAT_DWORD
+ ".\n", Level ));
+ ApiStatus = NetReplExportDirSetInfo (
+ UncServerName,
+ DirName,
+ Level,
+ (LPBYTE) (LPVOID) Info,
+ & ParmError );
+
+ NetpKdPrint(( "TestExportDirSet: back from Set, status="
+ FORMAT_API_STATUS ", parm err=" FORMAT_LONG ".\n" ,
+ ApiStatus, (LONG) ParmError ));
+
+ if ( !OrdinaryUserOnly) {
+ NetpAssert( ApiStatus == ExpectedStatus );
+ }
+
+ ApiStatus = NetApiBufferFree( Info );
+ NetpAssert( ApiStatus == NO_ERROR );
+
+} // TestExportDirSet
+
+
+DBGSTATIC void
+TestExportDirUnlock(
+ IN LPTSTR UncServerName OPTIONAL,
+ IN LPTSTR DirName,
+ IN DWORD Force,
+ IN BOOL ImportOnly,
+ IN BOOL OrdinaryUserOnly,
+ IN NET_API_STATUS ExpectedStatus
+ )
+{
+ NET_API_STATUS ApiStatus;
+
+ NetpKdPrint(( "TestExportDirUnlock: trying unlock on '" FORMAT_LPTSTR
+ "', force=" FORMAT_DWORD ".\n",
+ OPTIONAL_LPTSTR( DirName ), Force ));
+
+ ApiStatus = NetReplExportDirUnlock (
+ UncServerName,
+ DirName,
+ Force );
+
+ NetpKdPrint(( "TestExportDirUnlock: back from API, status="
+ FORMAT_API_STATUS ".\n", ApiStatus ));
+
+ if ( !OrdinaryUserOnly) {
+ NetpAssert( ApiStatus == ExpectedStatus );
+ }
+
+} // TestExportDirUnlock
diff --git a/private/net/svcdlls/repl/repltest/testexp0.c b/private/net/svcdlls/repl/repltest/testexp0.c
new file mode 100644
index 000000000..500627153
--- /dev/null
+++ b/private/net/svcdlls/repl/repltest/testexp0.c
@@ -0,0 +1,120 @@
+/*++
+
+Copyright (c) 1992-1993 Microsoft Corporation
+
+Module Name:
+
+ TestExp0.c
+
+Abstract:
+
+ Test only export APIs, in a simple process (no threads).
+
+Author:
+
+ John Rogers (JohnRo) 16-Mar-1991
+
+Revision History:
+
+ 16-Mar-1992 JohnRo
+ Created.
+ 20-Mar-1992 JohnRo
+ Main should wait for service start (if necessary), not Test routines.
+ 27-Jul-1992 JohnRo
+ RAID 2274: repl svc should impersonate caller.
+ 22-Sep-1992 JohnRo
+ Work with stdcall.
+ 03-Dec-1992 JohnRo
+ Repl tests for remote registry. Undo old thread junk.
+ 23-Jul-1993 JohnRo
+ RAID 16685: NT repl should ignore LPTn to protect downlevel.
+
+--*/
+
+
+// These must be included first:
+
+#include <windows.h> // DWORD, IN, API, etc.
+#include <lmcons.h>
+
+// These may be included in any order:
+
+#include <netdebug.h> // NetpKdPrint(()), FORMAT_ equates
+#include <repltest.h> // TestReplApis(), etc.
+#include <stdio.h> // printf(), etc.
+#include <stdlib.h> // EXIT_FAILURE, EXIT_SUCCESS, _CRTAPI1.
+#include <tstring.h> // NetpAllocTStrFromStr().
+
+
+DBGSTATIC VOID
+Usage (
+ VOID
+ )
+{
+ (void) printf(
+ "Usage: TestExp0 [-i] [-u] [-v] [-s \\\\server_name]\n\n"
+ "flags:\n"
+ " -i target is import-only\n"
+ " -s server_name server to remove APIs to "
+ "(default is local system)\n"
+ " -u only does ordinary user tests "
+ "(default include admin tests)\n"
+ " -v verbose\n"
+ "\n"
+ "Example: TestExp0 -s \\\\somebody\n");
+}
+
+
+int _CRTAPI1
+main(
+ IN int argc,
+ IN char *argv[]
+ )
+
+{
+ int ArgNumber;
+ BOOL ImportOnly = FALSE;
+ BOOL OrdinaryUserOnly = FALSE;
+ LPTSTR UncServerName = NULL;
+ BOOL Verbose = FALSE;
+
+ for (ArgNumber = 1; ArgNumber < argc; ArgNumber++) {
+ if ((*argv[ArgNumber] == '-') || (*argv[ArgNumber] == '/')) {
+ switch (tolower(*(argv[ArgNumber]+1))) // Process switches
+ {
+ case 'i' :
+ ImportOnly = TRUE;
+ break;
+ case 'u' :
+ OrdinaryUserOnly = TRUE;
+ break;
+ case 's' :
+ UncServerName
+ = NetpAllocTStrFromStr( (LPSTR) argv[++ArgNumber]);
+ NetpAssert( UncServerName != NULL );
+ break;
+ case 'v' :
+ Verbose = TRUE;
+ break;
+ default :
+ Usage();
+ return (EXIT_FAILURE);
+ }
+ } else {
+ Usage(); // Bad flag char.
+ return (EXIT_FAILURE);
+ }
+ }
+
+ NetpKdPrint(( "TestExp0: starting up ...\n" ));
+ TestExportDirApis(
+ UncServerName,
+ ImportOnly,
+ OrdinaryUserOnly,
+ Verbose );
+
+ NetpKdPrint(( "TestExp0: done!\n" ));
+
+ return (EXIT_SUCCESS);
+
+} // main
diff --git a/private/net/svcdlls/repl/repltest/testimp.c b/private/net/svcdlls/repl/repltest/testimp.c
new file mode 100644
index 000000000..d4e46cc4d
--- /dev/null
+++ b/private/net/svcdlls/repl/repltest/testimp.c
@@ -0,0 +1,399 @@
+/*++
+
+Copyright (c) 1992-1993 Microsoft Corporation
+
+Module Name:
+
+ TestImp.c
+
+Abstract:
+
+ This code tests the repl import dir APIs.
+
+Author:
+
+ John Rogers (JohnRo) 20-Feb-1992
+
+Environment:
+
+ Portable to any flat, 32-bit environment. (Uses Win32 typedefs.)
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Notes:
+
+ This code assumes that the info levels are subsets of each other.
+
+Revision History:
+
+ 20-Feb-1992 JohnRo
+ Created the import tests from the export tests.
+ 20-Mar-1992 JohnRo
+ Caller should wait for service start (if necessary), not us.
+ 28-Jul-1992 JohnRo
+ RAID 2274: repl svc should impersonate caller.
+ 13-Nov-1992 JohnRo
+ Detect get info returning NULL buffer pointer.
+ 03-Dec-1992 JohnRo
+ Repl tests for remote registry. Undo old thread junk.
+ 23-Jul-1993 JohnRo
+ RAID 16685: NT repl should ignore LPTn to protect downlevel.
+ 09-Aug-1993 JohnRo
+ Improved debug output for different info levels.
+
+--*/
+
+// These must be included first:
+
+#include <windows.h> // IN, DWORD, needed by <repltest.h>.
+#include <lmcons.h> // NET_API_STATUS.
+
+// These may be included in any order:
+
+#include <impdir.h> // ImportDirIsApiRecordValid(), etc.
+#include <lmapibuf.h> // NetApiBufferFree().
+#include <lmerr.h> // NERR_, ERROR_, and NO_ERROR equates.
+#include <lmrepl.h> // NetRepl APIs, REPL_IDIR_INFO_0, etc.
+#include <netdebug.h> // NetpDbgDisplay routines.
+#include <repldefs.h> // DOT_DOT.
+#include <repltest.h> // TestReplApis(), Display(), etc.
+
+
+#define DIR_NAME_LPT1 ((LPCTSTR) TEXT("LPT1"))
+
+
+DBGSTATIC void
+TestImportDirAdd(
+ IN LPTSTR UncServerName OPTIONAL,
+ IN LPTSTR DirName,
+ IN DWORD Level,
+ IN BOOL OrdinaryUserOnly,
+ IN NET_API_STATUS ExpectedStatus
+ );
+
+DBGSTATIC void
+TestImportDirDel(
+ IN LPTSTR UncServerName OPTIONAL,
+ IN LPTSTR DirName,
+ IN BOOL OrdinaryUserOnly,
+ IN NET_API_STATUS ExpectedStatus
+ );
+
+DBGSTATIC void
+TestImportDirEnum(
+ IN LPTSTR UncServerName OPTIONAL
+ );
+
+DBGSTATIC void
+TestImportDirGet(
+ IN LPTSTR UncServerName OPTIONAL,
+ IN LPTSTR DirName,
+ IN DWORD Level,
+ IN NET_API_STATUS ExpectedStatus
+ );
+
+DBGSTATIC void
+TestImportDirLock(
+ IN LPTSTR UncServerName OPTIONAL,
+ IN LPTSTR DirName,
+ IN BOOL OrdinaryUserOnly,
+ IN NET_API_STATUS ExpectedStatus
+ );
+
+DBGSTATIC void
+TestImportDirUnlock(
+ IN LPTSTR UncServerName OPTIONAL,
+ IN LPTSTR DirName,
+ IN DWORD Force,
+ IN BOOL OrdinaryUserOnly,
+ IN NET_API_STATUS ExpectedStatus
+ );
+
+
+VOID
+TestImportDirApis(
+ IN LPTSTR UncServerName OPTIONAL,
+ IN BOOL OrdinaryUserOnly,
+ IN BOOL Verbose
+ )
+{
+
+ if (Verbose) {
+ Display( "TestImportDirApis: starting...\n" );
+ }
+
+ TestImportDirEnum( UncServerName );
+
+#define EXISTING_NAME TEXT("flarp") /* created by me by hand. --JR */
+#define NONEXISTENT_NAME TEXT("notthere")
+
+ // Some tests depend on NONEXISTENT_NAME not being in repl config data.
+ // This may or may not be the first time, so don't check error code.
+ (void) NetReplImportDirDel( UncServerName, EXISTING_NAME );
+ (void) NetReplImportDirDel( UncServerName, NONEXISTENT_NAME );
+
+ TestImportDirLock( UncServerName, NULL, OrdinaryUserOnly, ERROR_INVALID_PARAMETER );
+ TestImportDirLock( UncServerName, EXISTING_NAME, OrdinaryUserOnly, NERR_UnknownDevDir );
+ TestImportDirLock( UncServerName, NONEXISTENT_NAME, OrdinaryUserOnly, NERR_UnknownDevDir );
+
+ TestImportDirGet( UncServerName, NONEXISTENT_NAME, 2, ERROR_INVALID_LEVEL );
+ TestImportDirGet( UncServerName, NONEXISTENT_NAME, 0, NERR_UnknownDevDir );
+ TestImportDirGet( UncServerName, NONEXISTENT_NAME, 1, NERR_UnknownDevDir );
+
+ TestImportDirAdd( UncServerName, EXISTING_NAME, 0, OrdinaryUserOnly, NO_ERROR );
+ TestImportDirAdd( UncServerName, EXISTING_NAME, 1, OrdinaryUserOnly, ERROR_INVALID_LEVEL );
+ TestImportDirAdd( UncServerName, EXISTING_NAME, 0, OrdinaryUserOnly, ERROR_ALREADY_EXISTS );
+ TestImportDirAdd( UncServerName, NONEXISTENT_NAME, 0, OrdinaryUserOnly, NO_ERROR ); // create
+ TestImportDirAdd( UncServerName, NONEXISTENT_NAME, 0, OrdinaryUserOnly, ERROR_ALREADY_EXISTS ); // ck create
+ TestImportDirAdd( UncServerName, EXISTING_NAME, 1, OrdinaryUserOnly, ERROR_INVALID_LEVEL );
+
+ TestImportDirAdd(
+ UncServerName,
+ DOT_DOT,
+ 1,
+ OrdinaryUserOnly,
+ ERROR_INVALID_PARAMETER );
+
+ TestImportDirAdd(
+ UncServerName,
+ (LPTSTR) DIR_NAME_LPT1,
+ 1,
+ OrdinaryUserOnly,
+ ERROR_INVALID_PARAMETER );
+
+ TestImportDirGet( UncServerName, EXISTING_NAME, 1, NO_ERROR ); // check create
+
+ TestImportDirLock( UncServerName, EXISTING_NAME, OrdinaryUserOnly, NO_ERROR );
+ TestImportDirLock( UncServerName, NONEXISTENT_NAME, OrdinaryUserOnly, NO_ERROR );
+
+ TestImportDirUnlock( UncServerName, EXISTING_NAME,
+ 135, // bad force level
+ OrdinaryUserOnly, ERROR_INVALID_PARAMETER );
+
+ TestImportDirEnum( UncServerName ); // check locks
+
+ TestImportDirUnlock( UncServerName, EXISTING_NAME, REPL_UNLOCK_NOFORCE, OrdinaryUserOnly, NO_ERROR );
+ TestImportDirUnlock( UncServerName, NONEXISTENT_NAME, REPL_UNLOCK_NOFORCE, OrdinaryUserOnly, NO_ERROR );
+
+ TestImportDirEnum( UncServerName ); // check unlocks
+
+ TestImportDirLock( UncServerName, EXISTING_NAME, OrdinaryUserOnly, NO_ERROR ); // lock=1
+ TestImportDirLock( UncServerName, EXISTING_NAME, OrdinaryUserOnly, NO_ERROR ); // lock=2
+ TestImportDirUnlock( UncServerName, EXISTING_NAME, REPL_UNLOCK_NOFORCE, OrdinaryUserOnly, NO_ERROR ); // lk=0
+
+ // OK, now lets delete everything and see how enum works with empty list.
+ TestImportDirDel( UncServerName, EXISTING_NAME, OrdinaryUserOnly, NO_ERROR );
+ TestImportDirDel( UncServerName, NONEXISTENT_NAME, OrdinaryUserOnly, NO_ERROR );
+ TestImportDirDel( UncServerName, NONEXISTENT_NAME, OrdinaryUserOnly, NERR_UnknownDevDir );
+ TestImportDirEnum( UncServerName );
+
+ if (Verbose) {
+ Display( "TestImportDirApis: returning/exiting...\n" );
+ }
+
+} // TestImportDirApis
+
+
+DBGSTATIC void
+TestImportDirAdd(
+ IN LPTSTR UncServerName OPTIONAL,
+ IN LPTSTR DirName,
+ IN DWORD Level,
+ IN BOOL OrdinaryUserOnly,
+ IN NET_API_STATUS ExpectedStatus
+ )
+{
+ NET_API_STATUS ApiStatus;
+ REPL_IDIR_INFO_1 Info; // Superset (includes only add info level).
+ DWORD ParmError;
+
+ Info.rpid1_dirname = DirName;
+ if (Level == 1) {
+ Info.rpid1_mastername = NULL; // Prevent spurious GP faults.
+ }
+
+ if (Level <= 1) { // Only display valid levels.
+ Display( "TestImportDirAdd: structure we'll try to add:\n" );
+ NetpDbgDisplayReplImportDir( Level, & Info );
+ }
+
+ Display( "TestImportDirAdd: trying add level " FORMAT_DWORD
+ ".\n", Level );
+ ApiStatus = NetReplImportDirAdd (
+ UncServerName,
+ Level,
+ (LPBYTE) (LPVOID) & Info,
+ & ParmError );
+
+ Display( "TestImportDirAdd: back from add, status="
+ FORMAT_API_STATUS ", parm err=" FORMAT_LONG ".\n" ,
+ ApiStatus, (LONG) ParmError );
+
+ if ( !OrdinaryUserOnly) {
+ NetpAssert( ApiStatus == ExpectedStatus );
+ }
+
+} // TestImportDirAdd
+
+
+DBGSTATIC void
+TestImportDirDel(
+ IN LPTSTR UncServerName OPTIONAL,
+ IN LPTSTR DirName,
+ IN BOOL OrdinaryUserOnly,
+ IN NET_API_STATUS ExpectedStatus
+ )
+{
+ NET_API_STATUS ApiStatus;
+
+ Display( "TestImportDirDel: trying to del '" FORMAT_LPTSTR "'.\n",
+ DirName );
+
+ ApiStatus = NetReplImportDirDel (
+ UncServerName,
+ DirName );
+
+ Display( "TestImportDirDel: back from del, status="
+ FORMAT_API_STATUS ".\n", ApiStatus );
+
+ if ( !OrdinaryUserOnly) {
+ NetpAssert( ApiStatus == ExpectedStatus );
+ }
+
+} // TestImportDirDel
+
+
+DBGSTATIC void
+TestImportDirEnum(
+ IN LPTSTR UncServerName OPTIONAL
+ )
+{
+ NET_API_STATUS ApiStatus;
+ LPVOID BufPtr;
+ DWORD EntriesRead;
+ DWORD Level;
+ DWORD TotalEntries;
+
+ for (Level=0; Level<=1; ++Level) {
+
+ Display( "TestImportDirEnum: trying enum level " FORMAT_DWORD
+ ".\n", Level );
+ ApiStatus = NetReplImportDirEnum (
+ UncServerName,
+ Level,
+ (LPBYTE *) & BufPtr,
+ 1, // pref max size
+ & EntriesRead,
+ & TotalEntries,
+ NULL ); // no resume handle
+
+ Display( "TestImportDirEnum: back from enum, status="
+ FORMAT_API_STATUS ".\n" , ApiStatus );
+ Display( " alloc'ed buffer at " FORMAT_LPVOID ".\n",
+ (LPVOID) BufPtr );
+ Display( " entries read=" FORMAT_DWORD
+ ", total=" FORMAT_DWORD "\n", EntriesRead, TotalEntries );
+
+ NetpAssert( ApiStatus == NO_ERROR );
+
+ if (BufPtr != NULL) {
+ NetpDbgDisplayReplImportDirArray( Level, BufPtr, EntriesRead );
+
+ ApiStatus = NetApiBufferFree( BufPtr );
+ NetpAssert( ApiStatus == NO_ERROR );
+ }
+ }
+
+} // TestImportDirEnum
+
+
+DBGSTATIC void
+TestImportDirGet(
+ IN LPTSTR UncServerName OPTIONAL,
+ IN LPTSTR DirName,
+ IN DWORD Level,
+ IN NET_API_STATUS ExpectedStatus
+ )
+{
+ NET_API_STATUS ApiStatus;
+ LPVOID Info = NULL;
+
+ Display( "TestImportDirGet: trying level " FORMAT_DWORD " for '"
+ FORMAT_LPTSTR "'.\n", Level, DirName );
+
+ ApiStatus = NetReplImportDirGetInfo (
+ UncServerName,
+ DirName,
+ Level,
+ (LPBYTE *) (LPVOID) & Info ); // alloc and set pointer
+
+ Display( "TestImportDirGet: back from get info, status="
+ FORMAT_API_STATUS ".\n", ApiStatus );
+
+ NetpAssert( ApiStatus == ExpectedStatus );
+ if (ApiStatus == NO_ERROR) {
+ Display( "TestImportDirGet: structure we got back:\n" );
+ NetpAssert( Info != NULL );
+ NetpDbgDisplayReplImportDir( Level, Info );
+
+ ApiStatus = NetApiBufferFree( Info );
+ NetpAssert( ApiStatus == NO_ERROR );
+ }
+
+} // TestImportDirGet
+
+
+DBGSTATIC void
+TestImportDirLock(
+ IN LPTSTR UncServerName OPTIONAL,
+ IN LPTSTR DirName,
+ IN BOOL OrdinaryUserOnly,
+ IN NET_API_STATUS ExpectedStatus
+ )
+{
+ NET_API_STATUS ApiStatus;
+
+ Display( "TestImportDirLock: trying lock on '" FORMAT_LPTSTR "'.\n",
+ OPTIONAL_LPTSTR( DirName ) );
+
+ ApiStatus = NetReplImportDirLock (
+ UncServerName,
+ DirName );
+
+ Display( "TestImportDirLock: back from API, status="
+ FORMAT_API_STATUS ".\n", ApiStatus );
+
+ if ( !OrdinaryUserOnly) {
+ NetpAssert( ApiStatus == ExpectedStatus );
+ }
+
+} // TestImportDirLock
+
+
+DBGSTATIC void
+TestImportDirUnlock(
+ IN LPTSTR UncServerName OPTIONAL,
+ IN LPTSTR DirName,
+ IN DWORD Force,
+ IN BOOL OrdinaryUserOnly,
+ IN NET_API_STATUS ExpectedStatus
+ )
+{
+ NET_API_STATUS ApiStatus;
+
+ Display( "TestImportDirUnlock: trying unlock on '" FORMAT_LPTSTR
+ "', force=" FORMAT_DWORD ".\n",
+ OPTIONAL_LPTSTR( DirName ), Force );
+
+ ApiStatus = NetReplImportDirUnlock (
+ UncServerName,
+ DirName,
+ Force );
+
+ Display( "TestImportDirUnlock: back from API, status="
+ FORMAT_API_STATUS ".\n", ApiStatus );
+
+ if ( !OrdinaryUserOnly) {
+ NetpAssert( ApiStatus == ExpectedStatus );
+ }
+
+} // TestImportDirUnlock
diff --git a/private/net/svcdlls/repl/repltest/testimp0.c b/private/net/svcdlls/repl/repltest/testimp0.c
new file mode 100644
index 000000000..1115d2293
--- /dev/null
+++ b/private/net/svcdlls/repl/repltest/testimp0.c
@@ -0,0 +1,115 @@
+/*++
+
+Copyright (c) 1992-1993 Microsoft Corporation
+
+Module Name:
+
+ TestImp0.c
+
+Abstract:
+
+ Test only import APIs, in a simple process (no threads).
+
+Author:
+
+ John Rogers (JohnRo) 23-Mar-1991
+
+Revision History:
+
+ 23-Mar-1992 JohnRo
+ Created.
+ 27-Jul-1992 JohnRo
+ RAID 2274: repl svc should impersonate caller.
+ 22-Sep-1992 JohnRo
+ Work with stdcall.
+ 03-Dec-1992 JohnRo
+ Repl tests for remote registry. Undo old thread junk.
+ 23-Jul-1993 JohnRo
+ RAID 16685: NT repl should ignore LPTn to protect downlevel.
+
+--*/
+
+
+// These must be included first:
+
+#include <windows.h> // DWORD, IN, CreateThread API, etc.
+#include <lmcons.h>
+
+// These may be included in any order:
+
+#include <repltest.h> // My prototypes, Display().
+#include <stdio.h> // printf(), etc.
+#include <stdlib.h> // EXIT_FAILURE, EXIT_SUCCESS, _CRTAPI1.
+#include <tstring.h> // NetpAllocTStrFromStr().
+
+
+DBGSTATIC VOID
+Usage (
+ VOID
+ )
+{
+ (void) printf(
+ "Author: JR (John Rogers, JohnRo@Microsoft)\n"
+ "Usage: TestImp0 [-u] [-s \\\\server_name] [-v]\n\n"
+ "flags:\n"
+ " -s server_name server to remove APIs to "
+ "(default is local system)\n"
+ " -u only does ordinary user tests "
+ "(default include admin tests)\n"
+ " -v verbose\n"
+ "\n"
+ "Example: TestImp0 -s \\\\somebody\n");
+}
+
+int _CRTAPI1
+main(
+ IN int argc,
+ IN char *argv[]
+ )
+{
+ int ArgNumber;
+ BOOL OrdinaryUserOnly = FALSE;
+ LPTSTR UncServerName = NULL;
+ BOOL Verbose = FALSE;
+
+ for (ArgNumber = 1; ArgNumber < argc; ArgNumber++) {
+ if ((*argv[ArgNumber] == '-') || (*argv[ArgNumber] == '/')) {
+ switch (tolower(*(argv[ArgNumber]+1))) // Process switches
+ {
+ case 's' :
+ UncServerName
+ = NetpAllocTStrFromStr( (LPSTR) argv[++ArgNumber]);
+ NetpAssert( UncServerName != NULL );
+ break;
+ case 'u' :
+ OrdinaryUserOnly = TRUE;
+ break;
+ case 'v' :
+ Verbose = TRUE;
+ break;
+ default :
+ Usage();
+ return (EXIT_FAILURE);
+ }
+ } else {
+ Usage(); // Bad flag char.
+ return (EXIT_FAILURE);
+ }
+ }
+
+ if (Verbose) {
+ Display( "TestImp0: starting up...\n" );
+ }
+
+ TestImportDirApis(
+ UncServerName,
+ OrdinaryUserOnly,
+ Verbose );
+
+ if (Verbose) {
+ Display( "TestImp0: done!\n" );
+ }
+
+ return (EXIT_SUCCESS);
+
+} // main
diff --git a/private/net/svcdlls/repl/repltest/testrep0.c b/private/net/svcdlls/repl/repltest/testrep0.c
new file mode 100644
index 000000000..21cc8e070
--- /dev/null
+++ b/private/net/svcdlls/repl/repltest/testrep0.c
@@ -0,0 +1,119 @@
+/*++
+
+Copyright (c) 1992-1993 Microsoft Corporation
+
+Module Name:
+
+ TestRep0.c
+
+Abstract:
+
+ Test only repl config APIs, in a simple process (no threads).
+
+Author:
+
+ John Rogers (JohnRo) 16-Mar-1991
+
+Revision History:
+
+ 27-Mar-1992 JohnRo
+ Created.
+ 28-Jul-1992 JohnRo
+ RAID 2274: repl svc should impersonate caller.
+ Add explicit server name test.
+ 22-Sep-1992 JohnRo
+ Work with stdcall.
+ 01-Dec-1992 JohnRo
+ Corrected example in usage msg. Give defaults there too.
+ 03-Dec-1992 JohnRo
+ Repl tests for remote registry. Undo old thread junk.
+ 02-Aug-1993 JohnRo
+ Improved usage message.
+ Use Display().
+
+--*/
+
+
+// These must be included first:
+
+#include <windows.h> // DWORD, IN, CreateThread API, etc.
+#include <lmcons.h>
+
+// These may be included in any order:
+
+#include <netdebug.h> // NetpAssert(), FORMAT_ equates
+#include <repltest.h> // My prototypes, Display().
+#include <stdio.h> // printf(), etc.
+#include <stdlib.h> // EXIT_FAILURE, EXIT_SUCCESS, _CRTAPI1.
+#include <tstring.h> // NetpAllocTStrFromStr().
+
+
+
+DBGSTATIC VOID
+Usage (
+ VOID
+ )
+{
+ (void) printf(
+ "This program tests the NetRepl{Get,Set}Info APIs.\n"
+ "Author: JR (John Rogers, JohnRo@Microsoft)\n\n"
+ "Usage: TestRep0 [-i] [-s \\\\server_name] [-u]\n\n"
+ "flags:\n"
+ " -i target is import-only\n"
+ " -s server_name server to remove APIs to "
+ "(default is local system)\n"
+ " -u only does ordinary user tests "
+ "(default tests admin too)\n"
+ "\n"
+ "Example: TestRep0 -s \\\\somebody\n");
+}
+
+
+int _CRTAPI1
+main(
+ IN int argc,
+ IN char *argv[]
+ )
+{
+ int ArgNumber;
+ BOOL ImportOnly = FALSE;
+ BOOL OrdinaryUserOnly = FALSE; // default: do admin tests.
+ LPTSTR UncServerName = NULL;
+
+ for (ArgNumber = 1; ArgNumber < argc; ArgNumber++) {
+ if ((*argv[ArgNumber] == '-') || (*argv[ArgNumber] == '/')) {
+ switch (tolower(*(argv[ArgNumber]+1))) // Process switches
+ {
+ case 'i' :
+ ImportOnly = TRUE;
+ break;
+ case 'u' :
+ OrdinaryUserOnly = TRUE;
+ break;
+ case 's' :
+ UncServerName
+ = NetpAllocTStrFromStr( (LPSTR) argv[++ArgNumber]);
+ NetpAssert( UncServerName != NULL );
+ break;
+ default :
+ Usage();
+ return (EXIT_FAILURE);
+ }
+ } else {
+ Usage(); // Bad flag char.
+ return (EXIT_FAILURE);
+ }
+ }
+
+
+ Display( "TestRep0: starting up...\n" );
+ TestReplApis(
+ UncServerName,
+ ImportOnly,
+ OrdinaryUserOnly );
+
+ Display( "TestRep0: done!\n" );
+
+ return (EXIT_SUCCESS);
+
+} // main
diff --git a/private/net/svcdlls/repl/repltest/testrepl.c b/private/net/svcdlls/repl/repltest/testrepl.c
new file mode 100644
index 000000000..e909bee52
--- /dev/null
+++ b/private/net/svcdlls/repl/repltest/testrepl.c
@@ -0,0 +1,379 @@
+/*++
+
+Copyright (c) 1992-1993 Microsoft Corporation
+
+Module Name:
+
+ TestRepl.c
+
+Abstract:
+
+ This code tests the repl config APIs.
+
+Author:
+
+ John Rogers (JohnRo) 19-Feb-1992
+
+Environment:
+
+ Portable to any flat, 32-bit environment. (Uses Win32 typedefs.)
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+ 19-Feb-1992 JohnRo
+ Created.
+ 29-Jul-1992 JohnRo
+ RAID 2274: repl svc should impersonate caller.
+ Add explicit server name test.
+ Fixed NetReplSetInfo API test.
+ 06-Aug-1992 JohnRo
+ RAID 2252 (old 9963): repl should prevent export on Windows/NT.
+ 16-Nov-1992 JohnRo
+ Display expected status a little more often.
+ 03-Dec-1992 JohnRo
+ Repl tests for remote registry. Undo old thread junk.
+ 30-Dec-1992 JohnRo
+ Fixed expected error handling in set-info test.
+ 06-Jan-1993 JohnRo
+ Repl WAN support (get rid of repl name list limits).
+ 13-May-1993 JohnRo
+ Allow blank in computer name.
+
+--*/
+
+// These must be included first:
+
+#include <windows.h> // IN, DWORD, needed by <repltest.h>.
+#include <lmcons.h> // NET_API_STATUS.
+
+// These may be included in any order:
+
+#include <lmapibuf.h> // NetApiBufferFree().
+#include <lmrepl.h> // NetRepl APIs, REPL_INFO_0, etc.
+#include <netdebug.h> // NetpDbgDisplay routines.
+#include <netlib.h> // NetpMemoryFree(), etc.
+#include <replconf.h> // ReplConfigIsApiRecordValid(), etc.
+#include <repldefs.h> // DWORDLEN.
+#include <repltest.h> // TestReplApis(), etc.
+#include <tstr.h> // STRCAT(), ULTOA(), etc.
+#include <winerror.h> // ERROR_ and NO_ERROR equates.
+
+
+DBGSTATIC LPTSTR
+BuildLongNameList(
+ IN LPTSTR NamePart,
+ IN DWORD EntryCount
+ )
+{
+ LPTSTR EndPtr;
+ DWORD EntryNum;
+ LPTSTR NameList;
+ DWORD NameListSize;
+ DWORD NamePartLen;
+
+ NetpAssert( EntryCount > 0 );
+ NetpAssert( NamePart != NULL );
+
+ NamePartLen = STRLEN( NamePart );
+ NetpAssert( NamePartLen > 0 );
+
+ NameListSize =
+ EntryCount
+ * (NamePartLen + DWORDLEN + 1) // name part, number, semi or null
+ * sizeof(TCHAR);
+ NetpAssert( NameListSize > 0 );
+ NetpAssert( NameListSize > EntryCount );
+
+ NameList = NetpMemoryAllocate( NameListSize );
+ NetpAssert( NameList != NULL );
+
+ EndPtr = NameList;
+ for (EntryNum=1; EntryNum <= EntryCount; ++EntryNum) {
+
+ (VOID) STRCPY(
+ EndPtr,
+ NamePart );
+ EndPtr += NamePartLen;
+
+ (VOID) ULTOA(
+ EntryNum, // number to convert
+ EndPtr, // dest
+ 10 ); // base
+ EndPtr = STRCHR( EndPtr, TEXT('\0') );
+ NetpAssert( EndPtr != NULL );
+
+ (VOID) STRCAT( NameList, (LPTSTR) TEXT(";") );
+ ++EndPtr;
+ }
+
+ --EndPtr; // Bump back over extra semicolon at end.
+ NetpAssert( (*EndPtr) == TEXT(';') );
+ *EndPtr = TEXT('\0');
+
+ NetpDbgDisplayReplList( "built name list", NameList );
+
+ NetpAssert( ReplConfigIsListValid( NameList ) );
+
+ return (NameList);
+}
+
+DBGSTATIC void
+TestReplGet(
+ IN LPTSTR UncServerName OPTIONAL,
+ IN DWORD Level,
+ IN NET_API_STATUS ExpectedStatus
+ );
+
+DBGSTATIC void
+TestReplSet(
+ IN LPTSTR UncServerName OPTIONAL,
+ IN DWORD Role,
+ IN LPTSTR ExportList OPTIONAL,
+ IN LPTSTR ExportPath,
+ IN LPTSTR ImportList OPTIONAL,
+ IN DWORD Level,
+ IN BOOL ImportOnly,
+ IN NET_API_STATUS ExpectedStatus
+ );
+
+VOID
+TestReplApis(
+ IN LPTSTR UncServerName OPTIONAL,
+ IN BOOL ImportOnly,
+ IN BOOL OrdinaryUserOnly
+ )
+{
+ NET_API_STATUS ExpectedAdminStatus;
+
+ // Build lots of names of the form "EXPORT nn" (with blank in the name).
+ LPTSTR LongExportNameList =
+ BuildLongNameList( (LPTSTR) TEXT("EXPORT "), 50 );
+ LPTSTR LongImportNameList =
+ BuildLongNameList( (LPTSTR) TEXT("IMPORT "), 50 );
+
+ LPTSTR OriginalExportNameList = NULL;
+ LPTSTR OriginalImportNameList = NULL;
+
+ NetpAssert( LongExportNameList != NULL );
+ NetpAssert( LongImportNameList != NULL );
+
+ if (OrdinaryUserOnly) {
+ ExpectedAdminStatus = ERROR_ACCESS_DENIED;
+ } else {
+ ExpectedAdminStatus = NO_ERROR;
+ }
+
+ if (UncServerName != NULL) {
+ NetpKdPrint(( "TestReplApis: testing server '" FORMAT_LPTSTR "'.\n",
+ UncServerName ));
+ }
+
+#define EXISTING_NAME TEXT("flarp") /* created by me by hand. --JR */
+#define NONEXISTENT_NAME TEXT("notthere")
+
+ //
+ // Preserve orginal import and export lists.
+ //
+ {
+ LPREPL_INFO_0 ApiRecord;
+ NET_API_STATUS ApiStatus;
+
+ NetpKdPrint(( "TestReplApis: getting original lists...\n" ));
+
+ ApiStatus = NetReplGetInfo (
+ UncServerName,
+ 0, // level
+ (LPBYTE *) (LPVOID) & ApiRecord ); // alloc and set pointer
+
+ NetpAssert( ApiStatus == NO_ERROR );
+ OriginalExportNameList = ApiRecord->rp0_exportlist;
+ OriginalImportNameList = ApiRecord->rp0_importlist;
+ // BUGBUG: memory leak of rest of structure and strings, but who cares?
+
+ }
+
+ TestReplGet( UncServerName, 1, ERROR_INVALID_LEVEL );
+ TestReplGet( UncServerName, 0, NO_ERROR );
+
+#define GOOD_EXPORT_PATH TEXT("d:\\myrepl\\exp")
+#define MISSING_DRIVE_PATH TEXT("\\lanman.nt\\myrepl\\exp")
+
+#define BOGUS_EXPORT_PATH TEXT("relative\\bogus\\\\path")
+
+ TestReplSet( UncServerName,
+ REPL_ROLE_EXPORT,
+ NULL, // export list
+ GOOD_EXPORT_PATH,
+ NULL, // import list
+ 7,
+ ImportOnly,
+ ERROR_INVALID_LEVEL );
+ TestReplSet( UncServerName,
+ REPL_ROLE_EXPORT,
+ NULL, // export list
+ BOGUS_EXPORT_PATH,
+ NULL, // import list
+ 0,
+ ImportOnly,
+ ERROR_INVALID_PARAMETER );
+ TestReplSet( UncServerName,
+ REPL_ROLE_EXPORT,
+ NULL, // export list
+ MISSING_DRIVE_PATH,
+ NULL, // import list
+ 0,
+ ImportOnly,
+ ERROR_INVALID_PARAMETER );
+
+ TestReplSet( UncServerName,
+ REPL_ROLE_EXPORT,
+ NULL, // export list
+ GOOD_EXPORT_PATH,
+ NULL, // import list
+ 0,
+ ImportOnly,
+ ExpectedAdminStatus );
+
+ TestReplSet( UncServerName,
+ REPL_ROLE_IMPORT,
+ NULL, // export list
+ GOOD_EXPORT_PATH,
+ NULL, // import list
+ 0,
+ ImportOnly,
+ NO_ERROR );
+
+ TestReplSet( UncServerName,
+ REPL_ROLE_IMPORT,
+ LongExportNameList, // export list
+ GOOD_EXPORT_PATH,
+ LongImportNameList, // import list
+ 0,
+ ImportOnly,
+ NO_ERROR );
+
+ // Put lists back the way they were.
+ TestReplSet( UncServerName,
+ REPL_ROLE_IMPORT,
+ OriginalExportNameList, // export list
+ GOOD_EXPORT_PATH,
+ OriginalImportNameList, // import list
+ 0,
+ ImportOnly,
+ NO_ERROR );
+
+ NetpKdPrint(( "TestReplApis: returning/exiting...\n" ));
+
+} // TestReplApis
+
+
+DBGSTATIC void
+TestReplGet(
+ IN LPTSTR UncServerName OPTIONAL,
+ IN DWORD Level,
+ IN NET_API_STATUS ExpectedStatus
+ )
+{
+ NET_API_STATUS ApiStatus;
+ LPVOID Info;
+
+ NetpKdPrint(( "TestReplGet: trying level " FORMAT_DWORD ".\n", Level ));
+
+ ApiStatus = NetReplGetInfo (
+ UncServerName,
+ Level,
+ (LPBYTE *) (LPVOID) & Info ); // alloc and set pointer
+
+ NetpKdPrint(( "TestReplGet: back from get info, status="
+ FORMAT_API_STATUS ".\n", ApiStatus ));
+
+ NetpAssert( ApiStatus == ExpectedStatus );
+ if (ApiStatus == NO_ERROR) {
+ NetpKdPrint(( "TestReplGet: structure we got back:\n" ));
+ NetpDbgDisplayRepl( Level, Info );
+
+ NetpAssert( ReplConfigIsApiRecordValid( Level, Info, NULL ) );
+
+ ApiStatus = NetApiBufferFree( Info );
+ NetpAssert( ApiStatus == NO_ERROR );
+ }
+
+} // TestReplGet
+
+
+DBGSTATIC void
+TestReplSet(
+ IN LPTSTR UncServerName OPTIONAL,
+ IN DWORD Role,
+ IN LPTSTR ExportList OPTIONAL,
+ IN LPTSTR ExportPath,
+ IN LPTSTR ImportList OPTIONAL,
+ IN DWORD Level,
+ IN BOOL ImportOnly,
+ IN NET_API_STATUS ExpectedStatus
+ )
+{
+ LPREPL_INFO_0 ApiRecord;
+ NET_API_STATUS ApiStatus;
+ const DWORD GetInfoLevel = 0;
+ LPVOID Info;
+ DWORD ParmError;
+
+ NetpKdPrint((
+ "TestReplSet: getting level " FORMAT_DWORD
+ ", expecting status " FORMAT_API_STATUS ".\n",
+ Level, ExpectedStatus ));
+
+ ApiStatus = NetReplGetInfo (
+ UncServerName,
+ GetInfoLevel, // guaranteed good level
+ (LPBYTE *) (LPVOID) & Info ); // alloc and set pointer
+
+ NetpKdPrint(( "TestReplSet: back from get info(0), status="
+ FORMAT_API_STATUS ".\n", ApiStatus ));
+
+ NetpAssert( ApiStatus == NO_ERROR );
+ if (ApiStatus != NO_ERROR) { // Can't continue if we couldn't get.
+ return;
+ }
+
+ NetpKdPrint(( "TestReplSet: old structure we got back:\n" ));
+ NetpDbgDisplayRepl( GetInfoLevel, Info );
+
+ NetpAssert( ReplConfigIsApiRecordValid( GetInfoLevel, Info, NULL ) );
+
+ ApiRecord = (LPREPL_INFO_0) Info;
+ ApiRecord->rp0_role = Role;
+ ApiRecord->rp0_exportlist = ExportList;
+ ApiRecord->rp0_exportpath = ExportPath;
+ ApiRecord->rp0_importlist = ImportList;
+
+ if (ExpectedStatus != ERROR_INVALID_LEVEL) {
+ NetpKdPrint(( "TestReplSet: structure we'll try to Set:\n" ));
+ NetpDbgDisplayRepl( Level, Info );
+ }
+
+ NetpKdPrint(( "TestReplSet: trying Set level " FORMAT_DWORD
+ ".\n", Level ));
+ ApiStatus = NetReplSetInfo (
+ UncServerName,
+ Level,
+ (LPVOID) Info,
+ & ParmError );
+
+ NetpKdPrint(( "TestReplSet: back from Set, status="
+ FORMAT_API_STATUS ", parm err=" FORMAT_LONG ".\n" ,
+ ApiStatus, (LONG) ParmError ));
+
+ if (ImportOnly && (Role!=REPL_ROLE_IMPORT) ) {
+ NetpAssert( ApiStatus==ERROR_INVALID_PARAMETER );
+ return;
+ }
+ NetpAssert( ApiStatus == ExpectedStatus );
+
+ ApiStatus = NetApiBufferFree( Info );
+ NetpAssert( ApiStatus == NO_ERROR );
+
+
+} // TestReplSet
diff --git a/private/net/svcdlls/repl/repltest/wait.c b/private/net/svcdlls/repl/repltest/wait.c
new file mode 100644
index 000000000..603bf0436
--- /dev/null
+++ b/private/net/svcdlls/repl/repltest/wait.c
@@ -0,0 +1,161 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ Wait.c
+
+Abstract:
+
+ Various wait routines for repl test program.
+
+Author:
+
+ John Rogers (JohnRo) 14-Jan-1992
+
+Revision History:
+
+ 14-Jan-1992 JohnRo
+ Created.
+ 23-Jan-1992 JohnRo
+ Changed file name from repl.h to replgbl.h to avoid MIDL conflict.
+ 10-Feb-1992 JohnRo
+ Changed to allow dynamic role changes.
+ 16-Feb-1992 JohnRo
+ Fixed bugs in WaitFor{Master,Client}ThreadInit.
+ 13-Mar-1992 JohnRo
+ Added more checking of WaitForMultipleObjects().
+
+--*/
+
+
+// These must be included first:
+
+#include <windows.h> // WaitForSingleObject(), etc.
+#include <lmcons.h> // (Needed by repldefs.h)
+#include <repldefs.h> // (Needed by client.h and master.h)
+
+// These may be included in any order:
+
+#include <client.h> // RCGlobalClientThreadInit flag.
+#include <netdebug.h> // NetpKdPrint(()), FORMAT_ equates
+#include <master.h> // RMGlobalMasterThreadInit flag.
+#include <replgbl.h> // ReplGlobalClientTerminateEvent, etc.
+#include <repltest.h> // My prototypes.
+
+
+#define ARBITRARY_SLEEP_TIME 10000 // 10 seconds in milliseconds.
+
+
+#define STOPPED_FOR_TIMEOUT 1
+#define STOPPED_FOR_TERMINATE 2
+
+
+DBGSTATIC DWORD // Returns STOPPED_FOR_TIMEOUT or STOPPED_FOR_TERMINATE.
+SleepAWhile(
+ IN DWORD DelayMilliseconds
+ )
+{
+ DWORD WaitStatus;
+ HANDLE Handles[2];
+
+ Handles[0] = ReplGlobalClientTerminateEvent;
+ Handles[1] = ReplGlobalMasterTerminateEvent;
+
+ WaitStatus = WaitForMultipleObjects(
+ 2, // number of handles
+ Handles,
+ FALSE, // don't wait for all
+ DelayMilliseconds);
+
+ // Check if this thread should exit.
+
+ if ( ( WaitStatus == 0 ) || (WaitStatus == 1) ) {
+ NetpKdPrint(( "SleepAWhile: terminating!\n" ));
+ return (STOPPED_FOR_TERMINATE);
+ } else if (WaitStatus == WAIT_TIMEOUT) {
+ return (STOPPED_FOR_TIMEOUT);
+ } else {
+ NetpKdPrint(( "SleepAWhile: unexpected wait status " FORMAT_DWORD
+ ", last error " FORMAT_DWORD ".\n",
+ WaitStatus, GetLastError() ));
+ NetpAssert( FALSE );
+ }
+
+} // SleepAWhile
+
+
+void
+WaitForever(
+ void
+ )
+{
+ DWORD Reason;
+
+ while (1) {
+
+ NetpKdPrint(( "WaitForever: waiting...\n" ));
+
+ Reason = SleepAWhile( ARBITRARY_SLEEP_TIME );
+
+ if (Reason == STOPPED_FOR_TERMINATE) {
+ break;
+ }
+ }
+
+} // WaitForever
+
+
+void
+WaitForClientThreadInit(
+ void
+ )
+{
+ DWORD Reason;
+
+ while (RCGlobalClientThreadInit == FALSE) {
+
+ NetpKdPrint(( "WaitForClientThreadInit... waiting.\n" ));
+
+ Reason = SleepAWhile( ARBITRARY_SLEEP_TIME );
+
+ if (Reason == STOPPED_FOR_TERMINATE) {
+
+ break;
+ }
+
+ if ( RCGlobalClientThreadInit ) {
+ break;
+ }
+ }
+
+} // WaitForClientThreadInit
+
+
+
+void
+WaitForMasterThreadInit(
+ void
+ )
+{
+ DWORD Reason;
+
+ while (RMGlobalMasterThreadInit == FALSE) {
+
+ NetpKdPrint(( "WaitForMasterThreadInit... waiting.\n" ));
+
+ Reason = SleepAWhile( ARBITRARY_SLEEP_TIME );
+
+ if (Reason == STOPPED_FOR_TERMINATE) {
+
+ break;
+ }
+
+ if ( RMGlobalMasterThreadInit ) {
+ break;
+ }
+
+ }
+
+} // WaitForMasterThreadInit
diff --git a/private/net/svcdlls/repl/repltest/watch.c b/private/net/svcdlls/repl/repltest/watch.c
new file mode 100644
index 000000000..1d536559d
--- /dev/null
+++ b/private/net/svcdlls/repl/repltest/watch.c
@@ -0,0 +1,231 @@
+/*++
+
+Copyright (c) 1991-1993 Microsoft Corporation
+
+Module Name:
+
+ Watch.c
+
+Abstract:
+
+ This is a stand-alone test program. It tries the
+ NtNotifyChangeDirectoryFile API.
+
+Author:
+
+ Madan Appiah sometime in 1991
+
+Environment:
+
+ Portable to any flat, 32-bit environment. (Uses Win32 typedefs.)
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+ ??-???-1991 MadanA
+ Created.
+ 28-Nov-1992 JohnRo
+ Repl should use filesystem change notify.
+ Work with stdcall.
+ Added this block of comments.
+ Random changes.
+ 28-Jun-1993 JohnRo
+ Allow dir name to be command-line option.
+ Work better in free builds.
+ 28-Jun-1993 JohnRo
+ Use WaitForMultipleObjects(), to be just like the replicator service.
+ 10-Aug-1993 JohnRo
+ RAID 17010: Implement per-first-level-directory change notify.
+
+--*/
+
+
+
+// These must be included first:
+
+#include <windows.h> // LPVOID, etc.
+#include <lmcons.h> // NET_API_STATUS.
+
+// These may be included in any order:
+
+#include <assert.h> // assert().
+#include <chngnot.h> // ReplSetupChangeNotify, REPL_CHANGE_NOTIFY_HANDLE, etc
+#include <lmapibuf.h> // NetApiBufferFree().
+#include <netdebug.h> // DBGSTATIC, FORMAT_ equates, etc.
+#include <repldefs.h> // ReplGlobalTrace, REPL_DEBUG_ flags.
+#include <stdio.h> // printf(), etc.
+#include <stdlib.h> // EXIT_FAILURE, EXIT_SUCCESS, _CRTAPI1.
+#include <tstring.h> // NetpAlloc{type}from{type} functions.
+#include <winerror.h> // NO_ERROR, ERROR_ equates.
+
+
+DBGSTATIC VOID
+Usage(
+ IN char * ProgName
+ )
+{
+ assert( ProgName != NULL );
+ assert( (*ProgName) != '\0' );
+ assert( (*ProgName) != '\n' );
+ (VOID) printf(
+ "Repl change notify test program...\n"
+ "Authors: JR (John Rogers, JohnRo@Microsoft) (modifications)\n"
+ " Madan Appiah (MadanA@Microsoft) (original)\n\n"
+ "Usage: %s [options] srcPath\n\n"
+ "Options:\n"
+ " -r recursive (tree extent)\n"
+ " -v verbose\n",
+ ProgName);
+
+} // Usage()
+
+
+int _CRTAPI1
+main(
+ IN int argc,
+ IN char *argv[]
+ )
+{
+ NET_API_STATUS ApiStatus;
+ int ArgNumber;
+ LPWSTR DirName = NULL;
+ BOOL DoTree = FALSE;
+ LPREPL_CHANGE_NOTIFY_HANDLE ReplHandle = NULL;
+ BOOL Verbose = FALSE;
+ HANDLE WaitHandles[1];
+ DWORD WaitStatus;
+
+
+ //
+ // Process command-line arguments.
+ //
+ for (ArgNumber = 1; ArgNumber < argc; ArgNumber++) {
+ if ((*argv[ArgNumber] == '-') || (*argv[ArgNumber] == '/')) {
+ switch (tolower(*(argv[ArgNumber]+1))) // Process switches
+ {
+
+ case 'r' :
+ DoTree = TRUE;
+ break;
+
+ case 'v' :
+ ReplGlobalTrace = REPL_DEBUG_ALL;
+ Verbose = TRUE;
+ break;
+
+ default :
+ Usage( argv[0] );
+ ApiStatus = ERROR_INVALID_PARAMETER;
+ goto Cleanup;
+
+ } // switch
+
+ } else { // not an argument
+
+ if (DirName != NULL) {
+ Usage( argv[0] );
+ ApiStatus = ERROR_INVALID_PARAMETER;
+ goto Cleanup;
+ }
+ DirName = NetpAllocWStrFromStr( argv[ArgNumber] );
+ assert( DirName != NULL );
+ }
+ }
+ if (DirName == NULL) {
+ Usage( argv[0] );
+ ApiStatus = ERROR_INVALID_PARAMETER;
+ goto Cleanup;
+ }
+
+
+ //
+ // Get ready to watch changes.
+ //
+
+ ApiStatus = ReplSetupChangeNotify(
+ (LPCTSTR) DirName,
+ DoTree, // TRUE for entire tree
+ & ReplHandle );
+ (VOID) printf( "watch: got " FORMAT_API_STATUS
+ " from ReplSetupChangeNotify.\n", ApiStatus );
+ assert( ApiStatus == NO_ERROR );
+
+ WaitHandles[0] = ReplGetWaitableChangeNotifyHandle( ReplHandle );
+ assert( WaitHandles[0] != INVALID_HANDLE_VALUE );
+
+ //
+ // Tell user what's up and how to get out of it.
+ //
+
+ (VOID) printf(
+ "This program will loop forever, being notified about each change in\n"
+ "'" FORMAT_LPWSTR "'. Press ^C (control-C) to cancel this program.\n",
+ DirName );
+
+ //
+ // Loop until we get control-C.
+ //
+
+ while (TRUE) {
+
+ //
+ // Enable this pass...
+ //
+
+ ApiStatus = ReplEnableChangeNotify( ReplHandle );
+ (VOID) printf( "watch: got " FORMAT_API_STATUS
+ " from ReplEnableChangeNotify.\n", ApiStatus );
+ assert( ApiStatus == NO_ERROR );
+
+
+ //
+ // OK, now wait for other changes.
+ //
+
+ (VOID) printf( "watch: waiting...\n" );
+
+ WaitStatus = WaitForMultipleObjects(
+ 1, // handle count
+ WaitHandles,
+ FALSE, // don't wait for all handles
+ (DWORD) -1); // wait forever...
+
+ if (Verbose) {
+ (VOID) printf( "watch: back from wait.\n" );
+ }
+
+ if (WaitStatus != 0) { // anything but 0 (index of first) is wrong!
+
+ ApiStatus = GetLastError();
+ (VOID) printf(
+ "WaitForMultipleObjects failed, returned " FORMAT_DWORD
+ ", error code is " FORMAT_API_STATUS ".\n",
+ WaitStatus, ApiStatus );
+ assert( ApiStatus != NO_ERROR );
+ goto Cleanup;
+ }
+
+ ApiStatus = ReplGetChangeNotifyStatus( ReplHandle );
+ (VOID) printf(
+ "watch: after wait, got status " FORMAT_API_STATUS ".\n",
+ ApiStatus );
+
+ } // forever...
+
+Cleanup:
+
+ if (DirName != NULL) {
+ (VOID) NetApiBufferFree( DirName );
+ }
+
+ if (ReplHandle != NULL) {
+ (VOID) ReplCloseChangeNotify( ReplHandle );
+ }
+
+ if (ApiStatus == NO_ERROR) {
+ return (EXIT_SUCCESS);
+ } else {
+ return (EXIT_FAILURE);
+ }
+
+} // main()
diff --git a/private/net/svcdlls/repl/repltest/watchl.c b/private/net/svcdlls/repl/repltest/watchl.c
new file mode 100644
index 000000000..0a2ec1602
--- /dev/null
+++ b/private/net/svcdlls/repl/repltest/watchl.c
@@ -0,0 +1,205 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ WatchL.c
+
+Abstract:
+
+ This program tests the replicator change notify list routines.
+
+Author:
+
+ JR (John Rogers, JohnRo@Microsoft) 10-Aug-1993
+
+Environment:
+
+ Portable to any flat, 32-bit environment. (Uses Win32 typedefs.)
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+ 10-Aug-1993 JohnRo
+ Created for RAID 17010: Implement per-first-level-directory change
+ notify.
+
+--*/
+
+
+
+// These must be included first:
+
+#include <windows.h> // LPVOID, etc.
+#include <lmcons.h> // NET_API_STATUS.
+
+// These may be included in any order:
+
+#include <assert.h> // assert().
+#include <chngnotl.h> // ReplInitChangeNotifyList(), etc.
+#include <lmapibuf.h> // NetApiBufferFree().
+#include <netdebug.h> // DBGSTATIC, FORMAT_ equates, etc.
+#include <repldefs.h> // ReplGlobalTrace, REPL_DEBUG_ flags.
+#include <stdio.h> // printf(), etc.
+#include <stdlib.h> // EXIT_FAILURE, EXIT_SUCCESS, _CRTAPI1.
+#include <tstring.h> // NetpAlloc{type}from{type} functions.
+#include <winerror.h> // NO_ERROR, ERROR_ equates.
+
+
+DBGSTATIC VOID
+Usage(
+ IN char * ProgName
+ )
+{
+ assert( ProgName != NULL );
+ assert( (*ProgName) != '\0' );
+ assert( (*ProgName) != '\n' );
+ (VOID) printf(
+ "Repl change notify list test program (WatchL)...\n"
+ "Author: JR (John Rogers, JohnRo@Microsoft)\n\n"
+ "Usage: %s [options] srcPath\n\n"
+ "Options:\n"
+ " -r recursive (tree extent)\n"
+ " -v verbose\n",
+ ProgName);
+
+} // Usage()
+
+
+int _CRTAPI1
+main(
+ IN int argc,
+ IN char *argv[]
+ )
+{
+ NET_API_STATUS ApiStatus;
+ int ArgNumber;
+ LPWSTR DirName = NULL;
+ BOOL DoTree = FALSE;
+ DWORD EventTriggered;
+ LPCHANGE_NOTIFY_LIST ReplListHandle = NULL;
+ BOOL Verbose = FALSE;
+
+ //
+ // Process command-line arguments.
+ //
+
+ for (ArgNumber = 1; ArgNumber < argc; ArgNumber++) {
+ if ((*argv[ArgNumber] == '-') || (*argv[ArgNumber] == '/')) {
+ switch (tolower(*(argv[ArgNumber]+1))) // Process switches
+ {
+
+ case 'r' :
+ DoTree = TRUE;
+ break;
+
+ case 'v' :
+ ReplGlobalTrace = REPL_DEBUG_ALL;
+ Verbose = TRUE;
+ break;
+
+ default :
+ Usage( argv[0] );
+ ApiStatus = ERROR_INVALID_PARAMETER;
+ goto Cleanup;
+
+ } // switch
+
+ } else { // not an argument
+
+ if (DirName != NULL) {
+ Usage( argv[0] );
+ ApiStatus = ERROR_INVALID_PARAMETER;
+ goto Cleanup;
+ }
+ DirName = NetpAllocWStrFromStr( argv[ArgNumber] );
+ assert( DirName != NULL );
+ }
+ }
+ if (DirName == NULL) {
+ Usage( argv[0] );
+ ApiStatus = ERROR_INVALID_PARAMETER;
+ goto Cleanup;
+ }
+
+ //
+ // Get ready to watch changes.
+ //
+
+ ApiStatus = ReplInitChangeNotifyList(
+ &ReplListHandle, // alloc and set ptr
+ 0 ); // no fixed entries
+ (VOID) printf( "WatchL: got " FORMAT_API_STATUS
+ " from ReplInitpChangeNotifyList.\n", ApiStatus );
+ assert( ApiStatus == NO_ERROR );
+ assert( ReplListHandle != NULL );
+
+ ApiStatus = ReplAddVarToChangeNotifyList(
+ ReplListHandle, // list to add to
+ (LPCTSTR) DirName, // absolute path
+ NULL, // don't need time of last change
+ DoTree, // TRUE iff tree extent
+ &DirName ); // context (any junk will do)
+ (VOID) printf( "WatchL: got " FORMAT_API_STATUS
+ " from ReplAddVarToChangeNotifyList.\n", ApiStatus );
+ assert( ApiStatus == NO_ERROR );
+
+ //
+ // Tell user what's up and how to get out of it.
+ //
+
+ (VOID) printf(
+ "This program will loop forever, being notified about each change in\n"
+ "'" FORMAT_LPWSTR "'. Press ^C (control-C) to cancel this program.\n",
+ DirName );
+
+ //
+ // Loop until we get control-C.
+ //
+
+ while (TRUE) {
+
+ (VOID) printf( "WatchL: waiting...\n" );
+
+ //
+ // OK, now wait for other changes.
+ //
+
+ ApiStatus = ReplWaitForNextChangeNotifyList(
+ ReplListHandle, // list to wait for
+ (DWORD) (-1), // infinite timeout
+ &EventTriggered ); // index of event triggered
+ assert( ApiStatus == NO_ERROR );
+
+ if (Verbose) {
+ (VOID) printf( "WatchL: back from wait.\n" );
+ }
+
+ if (EventTriggered != 0) { // anything but 0 (index of first) is wrong!
+ (VOID) printf(
+ "ReplWaitForNextChangeNotifyList failed, returned "
+ FORMAT_DWORD ", error code is " FORMAT_API_STATUS ".\n",
+ EventTriggered, ApiStatus );
+ goto Cleanup;
+ }
+
+ } // forever...
+
+Cleanup:
+
+ if (DirName != NULL) {
+ (VOID) NetApiBufferFree( DirName );
+ }
+
+ if (ReplListHandle != NULL) {
+ (VOID) ReplCloseChangeNotifyList( ReplListHandle );
+ }
+
+ if (ApiStatus == NO_ERROR) {
+ return (EXIT_SUCCESS);
+ } else {
+ return (EXIT_FAILURE);
+ }
+
+} // main()
diff --git a/private/net/svcdlls/repl/server/cachetim.c b/private/net/svcdlls/repl/server/cachetim.c
new file mode 100644
index 000000000..02eef2a01
--- /dev/null
+++ b/private/net/svcdlls/repl/server/cachetim.c
@@ -0,0 +1,113 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ CacheTim.c
+
+Abstract:
+
+ Contains timezone cache functions.
+
+Author:
+
+ JR (John Rogers, JohnRo@Microsoft)
+
+Environment:
+
+ User mode only.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+ 10-Jun-1993 JohnRo
+ Created for RAID 13080: Allow repl between different timezones.
+
+--*/
+
+
+// These must be included first:
+
+#include <windows.h>
+#include <lmcons.h>
+
+// These may be included in any order:
+
+#include <client.h> // My prototypes.
+#include <lmapibuf.h> // NetApiBufferFree().
+#include <lmremutl.h> // NetRemoteTOD(), LPTIME_OF_DAY_INFO.
+#include <netdebug.h> // NetpAssert(), etc.
+#include <prefix.h> // PREFIX_ equates.
+#include <timelib.h> // NetpLocalTimeZoneOffset().
+#include <tstr.h> // TCHAR_EOS.
+
+
+NET_API_STATUS
+ReplFreeTimeCache(
+ VOID
+ )
+{
+ // BUGBUG write code
+ return (NO_ERROR);
+}
+
+
+NET_API_STATUS
+ReplGetTimeCacheValue(
+ IN LPCTSTR UncServerName OPTIONAL, // Must be NULL on exporter
+ OUT LPLONG TimeZoneOffsetSecs // offset (+ for West of GMT, etc).
+ )
+{
+ NET_API_STATUS ApiStatus;
+ LPTIME_OF_DAY_INFO TimeBuf = NULL;
+
+ NetpAssert( TimeZoneOffsetSecs != NULL );
+ if ( (UncServerName==NULL) || ((*UncServerName)==TCHAR_EOS) ) {
+
+ //
+ // Handle easy case: local machine.
+ //
+
+ *TimeZoneOffsetSecs = NetpLocalTimeZoneOffset();
+ ApiStatus = NO_ERROR;
+ goto Cleanup;
+ }
+
+
+ ApiStatus = NetRemoteTOD(
+ (LPTSTR) UncServerName,
+ (LPVOID) &TimeBuf ); // Data returned here (alloc & set ptr)
+ if (ApiStatus != NO_ERROR) {
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplGetTimeCacheValue: NetRemoteTOD FAILED, status is "
+ FORMAT_API_STATUS ".\n",
+ ApiStatus ));
+ // BUGBUG: log this
+ goto Cleanup;
+ }
+ NetpAssert( TimeBuf != NULL );
+ if (TimeBuf->tod_timezone == -1) {
+ *TimeZoneOffsetSecs = NetpLocalTimeZoneOffset();
+ } else {
+ *TimeZoneOffsetSecs = TimeBuf->tod_timezone * 60; // minutes to seconds
+ }
+ NetpAssert( *TimeZoneOffsetSecs != -1 );
+
+Cleanup:
+
+ if (TimeBuf != NULL) {
+ (VOID) NetApiBufferFree( TimeBuf );
+ }
+ return (ApiStatus);
+}
+
+
+NET_API_STATUS
+ReplInitTimeCache(
+ VOID
+ )
+{
+ // BUGBUG write code
+ return (NO_ERROR);
+}
diff --git a/private/net/svcdlls/repl/server/checksum.c b/private/net/svcdlls/repl/server/checksum.c
new file mode 100644
index 000000000..0b47e5e41
--- /dev/null
+++ b/private/net/svcdlls/repl/server/checksum.c
@@ -0,0 +1,671 @@
+/*++
+
+Copyright (c) 1987-1993 Microsoft Corporation
+
+Module Name:
+ checksum.c
+
+Abstract:
+ Contains checksum functions that scan a dir/tree checksum all entries
+ and counts them.
+
+Author:
+ Ported from Lan Man 2.x
+
+Environment:
+ User mode only.
+ Contains NT-specific code.
+ Requires ANSI C extensions: slash-slash comments, long external names,
+ _strupr() function.
+ Tab size is set to 4.
+
+Revision History:
+ 04/11/89 (yuv)
+ initial coding
+
+ 02/15/90 (YN)
+ Improved checksuming
+
+ 10/24/91 (madana)
+ ported to NT. Converted to NT style.
+
+ 11-Dec-1991 JohnRo
+ Avoid unnamed structure fields to allow MIPS builds.
+
+ 13-Dec-1991 JohnRo
+ Avoid nonstandard dollar sign in C source code.
+
+ 18-Dec-1991 JohnRo
+ Made changes suggested by PC-LINT.
+ 16-Jan-1992 JohnRo
+ Avoid using private logon functions.
+ 20-Jan-1992 JohnRo
+ More changes suggested by PC-LINT.
+ 27-Jan-1992 JohnRo
+ Changed to use LPTSTR etc.
+ 13-Mar-1992 JohnRo
+ Fixed bug handling tree depth.
+ 25-Mar-1992 JohnRo
+ Added debug output of checksum.
+ Use FORMAT_ equates where possible.
+ 26-Mar-1992 JohnRo
+ Fixed bug where DoScanTree() was looking in wrong directory.
+ 26-Mar-1992 JohnRo
+ New ReplFind routines interface.
+ 19-Aug-1992 JohnRo
+ RAID 3603: import tree (TMPREE.RP$) generated at startup.
+ Use PREFIX_ equates.
+ 27-Aug-1992 JohnRo
+ RAID 4660: repl svc computes checksum wrong (NT vs. OS/2 client).
+ SingleChecksum doesn't need to update its input structure.
+ Fixed remaining missing use of PREFIX_ equates.
+ 23-Dec-1992 JohnRo
+ RAID 5996: repl should override FILE_ATTRIBUTE_NORMAL.
+ Trying to track down time off by two seconds on FAT.
+ Made changes suggested by PC-LINT 5.0
+ 25-Feb-1993 JohnRo
+ RAID 12237: replicator tree depth exceeded.
+ Use NetpKdPrint() where possible.
+ 06-Apr-1993 JohnRo
+ Support ReplSum test app.
+ 30-Apr-1993 JohnRo
+ Repl should stop quicker during checksum.
+ 11-Jun-1993 JohnRo
+ RAID 13080: Allow repl between different timezones.
+ 08-Jul-1993 JohnRo
+ RAID 15736: OS/2 time stamps are broken again (try rounding down).
+ Made changes suggested by PC-LINT 5.0
+
+--*/
+
+// These must be included first:
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windef.h>
+#include <winbase.h>
+
+#include <lmcons.h>
+
+// These may be included in any order:
+
+#include <netlib.h>
+#include <netdebug.h> // NetpKdPrint(), FORMAT_ equates, etc.
+#include <prefix.h> // PREFIX_ equates.
+#include <replgbl.h> // ReplGlobal variables.
+#include <string.h> // _strupr().
+#include <tstring.h> // NetpAlloc{type}From{type}(), etc.
+#include <stdlib.h>
+
+#include <repldefs.h> // IF_DEBUG(), ReplIgnoreDirOrFile()Name, etc.
+#include <filefind.h>
+#include <checksum.h> // FORMAT_CHECKSUM, my prototypes, etc.
+#include <timelib.h> // NetpLocalTimeZoneOffset(), etc.
+#include <tstr.h> // STRLEN(), etc.
+
+
+#define ISEVEN(n) ( ( (n) & 0x01 ) == 0 )
+
+
+VOID
+ScanTree(
+ IN LONG MasterTimeZoneOffsetSecs, // exporter offset from GMT
+ IN OUT LPTSTR path, // path plus scratch space (to PATHLEN+1)
+ OUT PCHECKSUM_REC scan_rec
+ )
+
+/*++
+
+Routine Description:
+
+ Scans recursivly thru an entire sub-tree Xoring checksums and
+
+Arguments:
+ path - path of dir to be searched. Must be alloc'ed as TCHAR[PATHLEN+1], as
+ this routine uses the space at the end for temporary stuff.
+
+Return Value:
+ scan_rec - holds subtree Xored checksum amd file count
+ counting files
+
+NOTE:
+
+ Memory usage - Recursion goes as deep as the longest path in the dir tree!!
+ so memory required = a great deal. But NT gracefully expands the
+ stack, so we're not going to worry about it anymore.
+
+Threads:
+ Called by syncer and pulser threads.
+
+--*/
+{
+ LPREPL_FIND_HANDLE shan = INVALID_REPL_HANDLE;
+ CHECKSUM_REC local_rec;
+ REPL_WIN32_FIND_DATA search_buf;
+ DWORD path_index;
+
+ NetpAssert( path != NULL );
+ NetpAssert( scan_rec != NULL );
+
+ IF_DEBUG(CHECKSUM) {
+
+ NetpKdPrint(( PREFIX_REPL "ScanTree() is calling ScanDir() for "
+ FORMAT_LPTSTR ".\n", path ));
+
+ }
+
+ ScanDir(MasterTimeZoneOffsetSecs, path, scan_rec, TRUE);
+
+ // Remember where this directory name ends, as we modify path...
+ path_index = STRLEN(path);
+
+ // Update path to scan for directories.
+ (void) STRCAT( path, (LPVOID) SLASH );
+ (void) STRCAT( path, (LPVOID) STAR_DOT_STAR );
+
+ // Scan for directories.
+ shan = ReplFindFirstFile( path, &search_buf);
+
+ if (shan != INVALID_REPL_HANDLE) {
+
+ do {
+ if (search_buf.fdFound.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
+ if (ReplIgnoreDirOrFileName(search_buf.fdFound.cFileName)) {
+ continue;
+ }
+
+ // Quit if service is stopping.
+ if (ReplGlobalIsServiceStopping) {
+ goto Cleanup;
+ }
+
+ // append "\\"
+ *(path + path_index) = TCHAR_BACKSLASH;
+
+ // append sub-dir name
+ (void) STRCPY(
+ path + path_index + 1,
+ search_buf.fdFound.cFileName);
+
+
+ IF_DEBUG(CHECKSUM) {
+
+ NetpKdPrint(( PREFIX_REPL "ScanTree() is calling "
+ "ScanTree() for " FORMAT_LPTSTR ".\n",
+ path ));
+
+ }
+
+ ScanTree( MasterTimeZoneOffsetSecs, path, &local_rec );
+
+ // add checksum and count to prev values.
+ scan_rec->checksum ^= local_rec.checksum;
+ scan_rec->count += local_rec.count;
+ }
+ } while (ReplFindNextFile(shan, &search_buf));
+
+
+ } else {
+
+ NetpKdPrint(( PREFIX_REPL "ScanTree() is in error calling"
+ " FindFirstFile(), " FORMAT_API_STATUS ".\n",
+ (NET_API_STATUS) GetLastError() ));
+
+ }
+
+Cleanup:
+
+ // reset path name
+ *(path + path_index) = TCHAR_EOS;
+
+ if (shan != INVALID_REPL_HANDLE) {
+ if( !ReplFindClose(shan) ) {
+
+ NetpKdPrint(( PREFIX_REPL
+ "ScanTree() is in error when "
+ "calling ReplFindClose(), " FORMAT_API_STATUS ".\n",
+ (NET_API_STATUS) GetLastError() ));
+
+ }
+ }
+
+ IF_DEBUG(CHECKSUM) {
+
+ NetpKdPrint(( PREFIX_REPL "ScanTree() is done " FORMAT_LPTSTR
+ " successfully.\n", path ));
+
+ }
+
+} // ScanTree
+
+
+VOID
+ScanDir(
+ IN LONG MasterTimeZoneOffsetSecs, // exporter offset from GMT
+ IN OUT LPTSTR path, // path plus scratch space (to PATHLEN+1)
+ OUT PCHECKSUM_REC scan_rec,
+ IN DWORD flag
+ )
+/*++
+
+Routine Description :
+ Scans thru a directory xoring and counting for each file in the directory.
+
+Arguments :
+ path - path of dir to be scanned. Must be alloc'ed as TCHAR[PATHLEN+1], as
+ this routine uses the space at the end for temporary stuff.
+ flag - FALSE : scan only file entries , TRUE : scan sub_dir entries too.
+
+Return Value :
+ scan_rec holds the Xored checksum for entire dir and file count
+
+Threads:
+ Called by syncer and pulser threads.
+
+--*/
+{
+ DWORD path_index;
+ LPREPL_FIND_HANDLE shan = INVALID_REPL_HANDLE;
+ REPL_WIN32_FIND_DATA search_buf;
+
+ NetpAssert( path != NULL );
+ NetpAssert( scan_rec != NULL );
+
+ scan_rec->checksum = 0;
+ scan_rec->count = 0;
+
+ // Remember where this directory name ends, as we modify path...
+ path_index = STRLEN(path);
+
+#if 0
+ // set current directory
+ if(!SetCurrentDirectory(path)) {
+
+ NetpKdPrint(( PREFIX_REPL
+ "ScanDir can't CD to " FORMAT_LPTSTR "", path ));
+
+ return;
+ }
+
+
+ IF_DEBUG(CHECKSUM) {
+
+ NetpKdPrint(( PREFIX_REPL
+ "ScanDir() set CD to " FORMAT_LPTSTR " \n", path));
+
+ }
+#endif // 0
+
+ // Update path to scan for directories.
+ (void) STRCAT( path, (LPVOID) SLASH );
+ (void) STRCAT( path, (LPVOID) STAR_DOT_STAR );
+
+ shan = ReplFindFirstFile( path, &search_buf);
+
+ if (shan != INVALID_REPL_HANDLE) {
+
+ do {
+ if (search_buf.fdFound.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
+ if(!flag)
+ // no directory search
+ continue;
+
+ if (ReplIgnoreDirOrFileName(search_buf.fdFound.cFileName)) {
+ continue;
+ }
+ } else {
+ if (ReplIgnoreDirOrFileName(search_buf.fdFound.cFileName)) {
+ continue;
+ }
+ }
+
+ // Quit if service is stopping.
+ if (ReplGlobalIsServiceStopping) {
+ goto Cleanup;
+ }
+
+ IF_DEBUG(CHECKSUM) {
+
+ NetpKdPrint(( PREFIX_REPL "ScanDir() computes single checksum "
+ " of file " FORMAT_LPTSTR ".\n",
+ search_buf.fdFound.cFileName ));
+
+ }
+
+ scan_rec->checksum ^= SingleChecksum(MasterTimeZoneOffsetSecs, &search_buf);
+ ++(scan_rec->count);
+
+ } while (ReplFindNextFile(shan, &search_buf));
+
+
+
+ } else {
+ // BUGBUG: ignore errors from find first?
+ }
+
+Cleanup:
+
+ // reset path name
+ *(path + path_index) = TCHAR_EOS;
+
+ if (shan != INVALID_REPL_HANDLE) {
+ if( !ReplFindClose(shan) ) {
+
+ NetpKdPrint(( PREFIX_REPL
+ "ScanDir() is in error when "
+ "calling ReplFindClose(), " FORMAT_API_STATUS ".\n",
+ (NET_API_STATUS) GetLastError() ));
+
+ }
+ }
+
+ IF_DEBUG(CHECKSUM) {
+ NetpKdPrint(( PREFIX_REPL "ScanDir() is done " FORMAT_LPTSTR
+ " successfully.\n", path ));
+ }
+
+} // end ScanDir
+
+
+DWORD
+SingleChecksum(
+ IN LONG MasterTimeZoneOffsetSecs, // offset from GMT
+ IN LPREPL_WIN32_FIND_DATA info
+ )
+/*++
+
+Routine Description :
+
+ Computes a checksum from the following file info.
+
+ DWORD dwFileAttributes; ++
+ FILETIME ftCreationTime;
+ FILETIME ftLastAccessTime;
+ FILETIME ftLastWriteTime; ++
+ DWORD nFileSizeHigh;
+ DWORD nFileSizeLow; ++
+ DWORD nEaSize; ++
+ CHAR cFileName[ MAX_PATH ]; ++
+
+Arguments :
+ info - FindFirst info buffer, ++ are used for checksum
+
+Return Value :
+ if no error:
+ returns checksum which is a wierd combination of Shifted Xor's
+ if error:
+ returns REPL_UNKNOWN_CHECKSUM
+
+--*/
+{
+
+ WORD FatDate, FatTime;
+ WORD modus;
+ DWORD tmp;
+ LPSTR AnsiFileName;
+ LPBYTE char_p;
+ WORD FileAttributes;
+ DWORD GmtSecondsSince1970;
+ FILETIME LocalFileTime;
+ DWORD MasterSecondsSince1970;
+ DWORD result;
+ DWORD i;
+ DWORD len;
+ DWORD quad_len;
+
+ result = 0;
+
+ NetpFileTimeToSecondsSince1970(
+ & (info->fdFound.ftLastWriteTime), // input: GMT (FILETIME)
+ & GmtSecondsSince1970 ); // output: GMT (secs since '70)
+
+ if (MasterTimeZoneOffsetSecs >= 0) {
+ MasterSecondsSince1970 =
+ GmtSecondsSince1970 - (DWORD) MasterTimeZoneOffsetSecs;
+ } else {
+ MasterSecondsSince1970 =
+ GmtSecondsSince1970 + (DWORD) (-MasterTimeZoneOffsetSecs);
+ }
+
+ // Everybody else rounds up to 2 second interval, so now we have to.
+ if ( !ISEVEN( MasterSecondsSince1970 ) ) {
+ ++MasterSecondsSince1970;
+ }
+ NetpAssert( ISEVEN( MasterSecondsSince1970 ) );
+
+ NetpSecondsSince1970ToFileTime(
+ MasterSecondsSince1970, // input: master's TZ, secs...
+ & LocalFileTime ); // output: master's TZ, filetime
+
+ if ( !FileTimeToDosDateTime(&LocalFileTime, &FatDate, &FatTime) ) {
+
+ NetpKdPrint(( PREFIX_REPL
+ "SingleChecksum can't convert date/time to Dos format\n" ));
+
+ return (REPL_UNKNOWN_CHECKSUM);
+ }
+
+ IF_DEBUG( CHECKSUM ) {
+ NetpKdPrint(( PREFIX_REPL
+ "SingleChecksum: FatDate=" FORMAT_HEX_WORD
+ ", FatTime=" FORMAT_HEX_WORD
+ ", GMT seconds since 1970=" FORMAT_DWORD
+ ", master time (secs since 1970)=" FORMAT_DWORD
+ ", master timezone offset secs=" FORMAT_LONG
+ ", filetime(low)=" FORMAT_HEX_DWORD
+ ", filetime(high)=" FORMAT_HEX_DWORD ".\n",
+ FatDate, FatTime,
+ GmtSecondsSince1970,
+ MasterSecondsSince1970,
+ MasterTimeZoneOffsetSecs,
+ (DWORD) LocalFileTime.dwLowDateTime,
+ (DWORD) LocalFileTime.dwHighDateTime ));
+ }
+
+ modus = (WORD)((FatDate + FatTime) % 31);
+
+ tmp = ShortsToLong(FatDate, FatTime);
+
+ result ^= LeRotate(tmp, modus);
+
+ IF_DEBUG( CHECKSUM ) {
+ NetpKdPrint(( PREFIX_REPL
+ "SingleChecksum: modus=" FORMAT_HEX_WORD
+ ", tmp=" FORMAT_HEX_DWORD
+ ", result (so far)=" FORMAT_HEX_DWORD ".\n",
+ modus, tmp, result ));
+ }
+
+ //
+ // Get other numbers: file size, attributes, EA size.
+ //
+
+ result ^= RiRotate(info->fdFound.nFileSizeLow, 23);
+
+ FileAttributes =
+ ( (WORD) (info->fdFound.dwFileAttributes) )
+ & (~FILE_ATTRIBUTE_NORMAL);
+ tmp = ShortsToLong( FileAttributes,
+ (WORD) (info->nEaSize) );
+
+ result ^= LeRotate(tmp, 13);
+
+ IF_DEBUG( CHECKSUM ) {
+ NetpKdPrint(( PREFIX_REPL
+ "SingleChecksum: file size (low)=" FORMAT_HEX_DWORD
+ ", file attr (original)=" FORMAT_HEX_WORD
+ ", EA size=" FORMAT_HEX_WORD
+ ", tmp=" FORMAT_HEX_DWORD
+ ", result (so far)=" FORMAT_HEX_DWORD ".\n",
+ (DWORD) (info->fdFound.nFileSizeLow),
+ (WORD) (info->fdFound.dwFileAttributes),
+ (WORD) (info->nEaSize),
+ tmp, result ));
+ }
+
+ //
+ // Last but not least, do the file name.
+ // We need an upper-case, ANSI string version of this.
+ //
+
+ AnsiFileName = NetpAllocStrFromTStr(info->fdFound.cFileName);
+
+ if(AnsiFileName == NULL) {
+
+ NetpKdPrint(( PREFIX_REPL
+ "SingleChecksum in ERROR_NOT_ENOUGH_MEMORY error.\n" ));
+
+ return (REPL_UNKNOWN_CHECKSUM);
+
+ }
+ (void) _strupr( AnsiFileName );
+
+ // take care of complete multiples of 4.
+ len = strlen(AnsiFileName);
+ quad_len = len / 4;
+ char_p = (LPVOID) AnsiFileName;
+
+ for (i = 0, modus = 0; i < len; i++)
+ modus += (WORD) (*(char_p + i) * ((len - i) & 0x0007));
+
+ modus = (WORD) ((modus % 13) + (modus % 17));
+
+ for (i = 0; i < quad_len; i++) {
+ tmp = CharsToLong(char_p);
+ result ^= LeRotate(tmp, modus);
+ char_p += 4;
+ }
+
+ //
+ // take care of remaining bytes.
+ //
+
+ if ((quad_len = (len % 4)) > 0) {
+ tmp = 0;
+ for (i = 0; i < quad_len ; i++, char_p++)
+ tmp = (tmp << 8) + (*char_p);
+ }
+
+ result ^= RiRotate(tmp, modus);
+
+ IF_DEBUG(CHECKSUM) {
+ NetpKdPrint(( PREFIX_REPL
+ "SingleChecksum: '" FORMAT_LPSTR "' = " FORMAT_CHECKSUM
+ ".\n", AnsiFileName, result ));
+ }
+
+ NetpMemoryFree(AnsiFileName);
+
+ return result;
+
+} // end SingleChecksum
+
+
+DWORD
+RiRotate(
+ IN DWORD val,
+ IN WORD shift
+ )
+/*++
+
+Routine Description :
+ Rotates val right by rotate
+
+Arguments :
+ val : value to shift
+ shift : num of bits to rotate
+
+Return Value :
+ result of rotate
+
+--*/
+{
+ DWORD tmp;
+
+ tmp = val << (sizeof(DWORD) * 8 - shift);
+ val = val >> shift;
+ return (val | tmp);
+}
+
+
+
+DWORD
+LeRotate(
+ IN DWORD val,
+ IN WORD shift
+ )
+/*++
+
+Routine Description :
+ Rotates val left by shift
+
+Arguments :
+ val : value to shift
+ shift : num of bits to rotate
+
+Return Value :
+ result of rotate
+
+--*/
+{
+ unsigned long tmp;
+
+ tmp = val >> (sizeof(DWORD) * 8 - shift);
+ val = val << shift;
+ return (val | tmp);
+}
+
+DWORD
+CharsToLong(
+ IN LPBYTE s
+ )
+/*++
+
+Routine Description :
+ converts a first 4 chars in srting to long.
+
+Arguments :
+ s : string from where the charecters are packed.
+
+Return Value :
+ returns packed DWORD.
+
+--*/
+{
+ DWORD result = 0;
+ DWORD i;
+
+ for (i = 0; i < 4; i++, s++)
+ result = (result << 8) + (*s);
+
+ return result;
+}
+
+
+DWORD
+ShortsToLong(
+ IN WORD s1,
+ IN WORD s2
+ )
+/*++
+
+Routine Description :
+ converts 2 shorts into 1 long.
+
+Arguments :
+ s1, s2 : two shorts
+
+Return Value :
+ returns packed DWORD.
+
+--*/
+{
+ DWORD result;
+
+ result = s1;
+ result = (result << 16) + s2;
+ return result;
+}
+
+
diff --git a/private/net/svcdlls/repl/server/checksum.h b/private/net/svcdlls/repl/server/checksum.h
new file mode 100644
index 000000000..7bd5ebc2a
--- /dev/null
+++ b/private/net/svcdlls/repl/server/checksum.h
@@ -0,0 +1,93 @@
+/*++
+
+Copyright (c) 1987-1993 Microsoft Corporation
+
+Module Name:
+ checksum.h
+
+Abstract:
+ contains the constant, typedefs and function protos for checksum.c source.
+
+Author:
+ Ported from Lan Man 2.x
+
+Environment:
+ User mode only.
+ Contains NT-specific code.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+ Tab size is set to 4.
+
+Revision History:
+ 10/24/91 (madana)
+ ported to NT. Converted to NT style.
+ 13-Dec-1991 JohnRo
+ Avoid nonstandard dollar sign in C source code.
+ 20-Jan-1992 JohnRo
+ More changes suggested by PC-LINT.
+ 24-Jan-1992 JohnRo
+ Changed to use LPTSTR etc.
+ 26-Mar-1992 JohnRo
+ Get rid of a redundant definition of "*.*".
+ 27-Aug-1992 JohnRo
+ RAID 4660: repl svc computes checksum wrong (NT vs. OS/2 client).
+ 19-Apr-1993 JohnRo
+ Support ReplSum test app.
+ Include other header files as necessary.
+ Define FORMAT_CHECKSUM for general use.
+ Made changes suggested by PC-LINT 5.0
+ 10-Jun-1993 JohnRo
+ RAID 13080: Allow repl between different timezones.
+
+--*/
+
+
+#ifndef _CHECKSUM_
+#define _CHECKSUM_
+
+
+#include <debugfmt.h> // FORMAT_HEX_DWORD.
+#include <filefind.h> // LPREPL_WIN32_FIND_DATA, etc.
+
+
+#ifndef FORMAT_CHECKSUM
+#define FORMAT_CHECKSUM FORMAT_HEX_DWORD
+#endif
+
+#define REPL_UNKNOWN_CHECKSUM ((DWORD) -1)
+
+
+#define SLASH (LPTSTR) TEXT("\\")
+#define RP (LPTSTR) TEXT(".RP$")
+
+
+DWORD
+SingleChecksum(
+ IN LONG MasterTimeZoneOffsetSecs, // offset from GMT
+ IN LPREPL_WIN32_FIND_DATA Info
+ );
+
+DWORD
+RiRotate(
+ IN DWORD,
+ IN WORD
+ );
+
+DWORD
+LeRotate(
+ IN DWORD,
+ IN WORD
+ );
+
+DWORD
+CharsToLong(
+ IN LPBYTE
+ );
+
+DWORD
+ShortsToLong(
+ IN WORD,
+ IN WORD
+ );
+
+
+#endif // _CHECKSUM_
diff --git a/private/net/svcdlls/repl/server/chklocks.c b/private/net/svcdlls/repl/server/chklocks.c
new file mode 100644
index 000000000..05f855813
--- /dev/null
+++ b/private/net/svcdlls/repl/server/chklocks.c
@@ -0,0 +1,249 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ ChkLocks.c
+
+Abstract:
+
+ ReplCheckExportLocks().
+
+ This routine:
+
+ if local _access and local exporter is started:
+ check for locks in local master list
+ else if registry exists for given export dir:
+ (this handles local exporter not started or remote NT)
+ check for locks in (local or remote) registry
+ else:
+ (must be downlevel master)
+ check for UserLock.* file on master
+
+Author:
+
+ JR (John Rogers, JohnRo@Microsoft)) 17-Jan-1993
+
+Environment:
+
+ User Mode - Win32
+ Portable to any flat, 32-bit environment. (Uses Win32 typedefs.)
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+ 17-Jan-1993 JohnRo
+ Created for RAID 7053: locked trees added to pulse msg. (Actually
+ fix all kinds of remote lock handling.)
+ 11-Feb-1993 JohnRo
+ RAID 8355: Downlevel lock file check causes assert in repl importer.
+ Made changes suggested by PC-LINT 5.0
+
+--*/
+
+
+// These must be included first:
+
+#include <windows.h> // IN, LPCTSTR, OPTIONAL, etc.
+#include <lmcons.h> // NET_API_STATUS.
+
+// These may be included in any order:
+
+
+#include <dirname.h> // ReplIsDirNameValid().
+#include <expdir.h> // ExportDirReadConfigData().
+#include <icanon.h> // NetpIsRemote(), ISLOCAL equate.
+#include <lmerrlog.h> // NELOG_* defines
+#include <master.h> // PMASTER_LIST_REC, RMGlobalListLock.
+#include <masproto.h> // My prototype, GetMasterRec().
+#include <netdebug.h> // NetpAssert(), NetpKdPrint(), FORMAT_ equates, etc.
+#include <prefix.h> // PREFIX_ equates.
+#include <repldefs.h> // SLASH, REPL_SHARE, etc.
+#include <replgbl.h> // ReplConfigLock, ReplConfigRole.
+#include <tstr.h> // STRLEN(), TCHAR_EOS, etc.
+#include <winerror.h> // ERROR_, RPC_S_, NO_ERROR equates.
+
+
+/*++
+
+Threads:
+
+ Only called by syncer thread.
+
+--*/
+
+
+NET_API_STATUS
+ReplCheckExportLocks(
+ IN LPCTSTR UncServerName OPTIONAL,
+ IN LPCTSTR DirName,
+ OUT LPBOOL IsLockedPtr
+ )
+{
+ NET_API_STATUS ApiStatus;
+ BOOL ConfigLocked = FALSE;
+ BOOL DirLocked = FALSE; // Default is not locked.
+ DWORD Integrity;
+ DWORD LocalOrRemote; // Will be set to ISLOCAL or ISREMOTE.
+ DWORD Extent;
+ BOOL ListLocked = FALSE;
+ DWORD LockCount;
+ DWORD LockTime; // Seconds since 1970.
+ PMASTER_LIST_REC MasterRec;
+
+ //
+ // Check for caller errors (except server name, which NetpIsRemote will
+ // check for us).
+ //
+ if ( !ReplIsDirNameValid( (LPTSTR) DirName ) ) {
+ ApiStatus = ERROR_INVALID_PARAMETER;
+ goto Cleanup;
+ }
+ NetpAssert( IsLockedPtr != NULL );
+
+ //
+ // Find out if server name was given, if it is valid, and if it is local
+ // or remote.
+ //
+ if ( (UncServerName!=NULL) && ((*UncServerName)!=TCHAR_EOS) ) {
+ //
+ // Name was given. Canonicalize it and check if it's remote.
+ //
+ ApiStatus = NetpIsRemote(
+ (LPTSTR) UncServerName, // input: uncanon name
+ & LocalOrRemote, // output: local or remote flag
+ NULL, // don't need canon name output
+ 0 ); // flags: normal
+ if (ApiStatus != NO_ERROR) {
+ goto Cleanup; // Don't forget to unlock stuff.
+ }
+ } else {
+ LocalOrRemote = ISLOCAL; // No server name given, so must be local.
+ }
+
+ //
+ // If this is a local request we should try the fastest way and
+ // just look in the master list. Of course, first we need to find
+ // out if the exporter is running right now.
+ //
+ if (LocalOrRemote == ISLOCAL) {
+
+ ACQUIRE_LOCK_SHARED( ReplConfigLock );
+ ConfigLocked = TRUE;
+
+ if (ReplRoleIncludesExport( ReplConfigRole ) ) {
+
+ ACQUIRE_LOCK_SHARED( RMGlobalListLock );
+ ListLocked = TRUE;
+
+ MasterRec = GetMasterRec( (LPTSTR) DirName );
+ if (MasterRec == NULL) {
+ // Hmmm... This exporter doesn't know about this directory.
+ // We could give an error code here, but that would just be
+ // noise. Let's just say it isn't locked.
+ ApiStatus = NO_ERROR;
+ goto Cleanup; // Don't forget to unlock stuff.
+ }
+
+ if ( (MasterRec->lockcount) > 0 ) {
+ DirLocked = TRUE;
+ }
+ ApiStatus = NO_ERROR;
+ goto Cleanup; // Don't forget to unlock stuff.
+
+ } // local exporter is running
+
+ } // local
+
+ //
+ // Attempt to read registry. This might be locally (if we're import only)
+ // on remote exporter (assuming it is running Windows/NT or better).
+ //
+ ApiStatus = ExportDirReadConfigData(
+ (LPTSTR) UncServerName,
+ (LPTSTR) DirName,
+ & Integrity,
+ & Extent,
+ & LockCount,
+ & LockTime );
+ if (ApiStatus == NO_ERROR) {
+ if (LockCount > 0) {
+ DirLocked = TRUE;
+ }
+ goto Cleanup;
+
+ } else if (ApiStatus == RPC_S_SERVER_UNAVAILABLE) {
+
+ TCHAR RemotePath[MAX_PATH+1];
+
+ //
+ // Downlevel exporter. OK, we'll just do what a downlevel importer
+ // would do: look for USERLOCK.* files using a UNC name.
+ // So, let's build a search path:
+ // "\\server\REPL$\dirname"
+ // "\\.\REPL$\dirname"
+ //
+
+ if ( (UncServerName!=NULL) && ((*UncServerName)!=TCHAR_EOS) ) {
+ (VOID) STRCPY( RemotePath, (LPTSTR) UncServerName );
+ } else {
+ (VOID) STRCPY( RemotePath, (LPTSTR) TEXT("\\\\.") );
+ }
+ (VOID) STRCAT( RemotePath, SLASH );
+ (VOID) STRCAT( RemotePath, REPL_SHARE );
+ (VOID) STRCAT( RemotePath, SLASH );
+ (VOID) STRCAT( RemotePath, DirName );
+
+ NetpAssert( STRLEN( RemotePath ) <= MAX_PATH );
+
+ // NetpAssert( NetpIsRemotePathValid( RemotePath ) );
+
+
+ //
+ // Check if USERLOCK.* exists on master.
+ //
+ ApiStatus = ReplDoUserLockFilesExist(
+ RemotePath,
+ & DirLocked );
+ if (ApiStatus != NO_ERROR) {
+ goto Cleanup; // Don't forget to unlock stuff.
+ }
+ ApiStatus = NO_ERROR;
+
+ } else {
+ // Unexpected error will be logged below.
+ }
+
+Cleanup:
+
+ if (ApiStatus != NO_ERROR) {
+
+ // Log error remotely (if possible) and locally.
+ ReplErrorLog(
+ UncServerName, // server to log (local too)
+ NELOG_ReplSysErr, // log code
+ ApiStatus, // the unexpected error code
+ NULL, // no optional str1
+ NULL ); // no optional str2
+
+ NetpKdPrint(( PREFIX_REPL
+ "ReplCheckExportLocks: ERROR " FORMAT_API_STATUS ".\n",
+ ApiStatus ));
+ }
+
+ if (ConfigLocked) {
+ RELEASE_LOCK( ReplConfigLock );
+ }
+
+ if (ListLocked) {
+ RELEASE_LOCK( RMGlobalListLock );
+ }
+
+ if (IsLockedPtr != NULL) {
+ *IsLockedPtr = DirLocked;
+ }
+
+ return (ApiStatus);
+
+}
diff --git a/private/net/svcdlls/repl/server/chngrole.c b/private/net/svcdlls/repl/server/chngrole.c
new file mode 100644
index 000000000..cdda533ce
--- /dev/null
+++ b/private/net/svcdlls/repl/server/chngrole.c
@@ -0,0 +1,296 @@
+/*++
+
+Copyright (c) 1992-1993 Microsoft Corporation
+
+Module Name:
+
+ ChngRole.c
+
+Abstract:
+
+ Contains ReplChangeRole().
+
+Author:
+
+ John Rogers (JohnRo) 10-Feb-1992
+
+Environment:
+
+ User mode only.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+ Tab size is set to 4.
+
+Revision History:
+
+ 10-Feb-1992 JohnRo
+ Created this routine to allow dynamic role changes.
+ 15-Feb-1992 JohnRo
+ Make sure ReplGlobalRole is locked when we _read it.
+ Start and stop the RPC server if applicable.
+ Added some debug output.
+ 06-Mar-1992 JohnRo
+ Avoid starting RPC server too soon.
+ 13-Mar-1992 JohnRo
+ Make sure service controller status gets updated after service starts.
+ 22-Mar-1992 JohnRo
+ Added more debug output.
+ 24-Mar-1992 JohnRo
+ Renamed many ReplGlobal vars to ReplConfig vars.
+ 26-Mar-1992 JohnRo
+ Display status after calling NetpStopRpcServer().
+ 01-Apr-1992 JohnRo
+ Handle shutdown while trying to start service.
+ 28-Apr-1992 JohnRo
+ Fixed trivial MIPS compile problem.
+ 29-Jul-1992 JohnRo
+ RAID 2679: Fixed bogus assert when switching between active roles.
+ 19-Aug-1992 JohnRo
+ RAID 2115: repl svc should wait while stopping or changing role.
+ Use PREFIX_ equates where necessary.
+ 13-Nov-1992 JohnRo
+ RAID 1357: Repl APIs in wrong role kill svc.
+ 11-Dec-1992 JohnRo
+ RAID 3316: _access violation while stopping the replicator
+ Minor debug output enhancement.
+ Made changes suggested by PC-LINT 5.0
+ 02-Apr-1993 JohnRo
+ Use NetpKdPrint() where possible.
+ 24-May-1993 JohnRo
+ RAID 10587: repl could deadlock with changed NetpStopRpcServer(), so
+ just call ExitProcess() instead.
+
+--*/
+
+
+// These must be included first:
+
+#include <windef.h>
+#include <lmcons.h> // NET_API_STATUS, etc.
+#include <rpc.h> // Needed by <rpcutil.h>.
+
+// These can be included in any order:
+
+#include <expdir.h> // ExportDir{Start,Stop}Repl routines.
+#include <impdir.h> // ImportDir{Start,Stop}Repl routines.
+#include <lmrepl.h> // REPL_ROLE_ equates.
+#include <netdebug.h> // DBGSTATIC, NetpKdPrint(), FORMAT_ equates.
+#include <netlock.h> // ACQUIRE_LOCK(), etc.
+#include <prefix.h> // PREFIX_ equates.
+#include <repl.h> // repl_ServerIfHandle.
+#include <repldefs.h> // IF_DEBUG(), etc.
+#include <replgbl.h> // ReplConfigRole, ReplGlobal variables, etc.
+#include <replname.h> // REPLICATOR_INTERFACE_NAME.
+#include <rpcutil.h> // NetpStartRpcServer(), etc.
+#include <thread.h> // NetpCurrentThread(), FORMAT_NET_THREAD_ID.
+#include <winerror.h> // NO_ERROR.
+#include <winsvc.h> // SERVICE_RUNNING, etc.
+
+
+NET_API_STATUS
+ReplChangeRole (
+ IN DWORD NewRole
+ )
+
+/*++
+
+Routine Description:
+
+ This routine can be called:
+
+ (a) when the service is starting,
+ (b) when the service is up and a new role is given via NetReplSetInfo,
+ (c) when the service is stopped by NetServiceStop,
+
+ (d) when an error is detected while attempting to start the service.
+ BUGBUG NOT ANY MORE! NOW WE JUST ExitProcess().
+
+ ReplChangeRole assumes caller has exclusive lock on ReplConfigLock.
+
+Arguments:
+
+ NewRole - desired new role (e.g. REPL_ROLE_IMPORT, REPL_ROLE_STOPPED).
+
+Return Value:
+
+ NET_API_STATUS
+ (Or, does not return at all if NewRole is REPL_ROLE_STOPPED).
+
+Threads:
+
+ This can be called by the main thread, an API thread, or a service
+ controller thread doing a ControlService() API.
+--*/
+
+{
+ NET_API_STATUS ApiStatus = NO_ERROR;
+ NET_API_STATUS FirstStatus;
+ DWORD OldRole;
+ BOOL ServiceIsStarting;
+
+ // NOTE: ReplChangeRole assumes caller has exclusive lock on ReplConfigLock.
+ OldRole = ReplConfigRole;
+
+ IF_DEBUG(REPL) {
+ NetpKdPrint(( PREFIX_REPL
+ "ReplChangeRole: changing role from " FORMAT_DWORD
+ " to " FORMAT_DWORD " for thread " FORMAT_NET_THREAD_ID ".\n",
+ OldRole, NewRole, NetpCurrentThread() ));
+ }
+
+ if (OldRole == NewRole) {
+ return (NO_ERROR);
+ }
+
+ if (OldRole == REPL_ROLE_STOPPED) {
+ ServiceIsStarting = TRUE;
+ } else {
+ ServiceIsStarting = FALSE;
+ }
+
+#define IS_EXPORT (ReplRoleIncludesExport(NewRole))
+#define WAS_EXPORT (ReplRoleIncludesExport(OldRole))
+#define IS_IMPORT (ReplRoleIncludesImport(NewRole))
+#define WAS_IMPORT (ReplRoleIncludesImport(OldRole))
+
+#define SET_GLOBAL_ROLE( newValue ) \
+ { \
+ ReplConfigRole = newValue; \
+ }
+
+ SET_GLOBAL_ROLE( NewRole );
+
+ CONVERT_EXCLUSIVE_LOCK_TO_SHARED( ReplConfigLock );
+
+#define BACK_FROM( routineName ) \
+ IF_DEBUG(REPL) { \
+ NetpKdPrint(( PREFIX_REPL \
+ "ReplChangeRole: back from " routineName ", api stat " \
+ FORMAT_API_STATUS ".\n", ApiStatus )); \
+ }
+
+ //
+ // Stop exporting and/or importing, if applicable.
+ // Note that errors in shutting down are handled differently. We make
+ // sure that we try to stop both parts even if we get an error in the first.
+ // That way this routine is useful in aborting starting the repl service
+ // if we've partially started.
+ //
+ FirstStatus = NO_ERROR;
+ if (WAS_EXPORT && ( ! IS_EXPORT ) ) {
+ ApiStatus = ExportDirStopRepl( );
+ BACK_FROM( "ExportDirStopRepl" );
+ if (ApiStatus != NO_ERROR) {
+ FirstStatus = ApiStatus;
+ }
+ }
+ if (WAS_IMPORT && ( ! IS_IMPORT ) ) {
+ ApiStatus = ImportDirStopRepl( );
+ BACK_FROM( "ImportDirStopRepl" );
+ if ( (ApiStatus != NO_ERROR) && (FirstStatus == NO_ERROR) ) {
+ FirstStatus = ApiStatus;
+ }
+ }
+ if (FirstStatus != NO_ERROR) {
+ NetpKdPrint(( PREFIX_REPL
+ "ReplChangeRole: ABORTING SERVICE.\n" ));
+ goto CleanUp;
+ }
+
+ //
+ // Start exporting and/or importing, if applicable.
+ // Note that the export code should be started first, to avoid "backward"
+ // jumps in the checkpoint numbers we give the service controller.
+ //
+ if ( (!WAS_EXPORT) && IS_EXPORT ) {
+ // Start export threads and wait for them to init OK.
+ ApiStatus = ExportDirStartRepl( ServiceIsStarting );
+ BACK_FROM( "ExportDirStartRepl" );
+ if (ApiStatus != NO_ERROR) {
+ goto CleanUp;
+ }
+ }
+ if ( (!WAS_IMPORT) && IS_IMPORT ) {
+ // Start import threads and wait for them to init OK.
+ ApiStatus = ImportDirStartRepl( ServiceIsStarting );
+ BACK_FROM( "ImportDirStartRepl" );
+ if (ApiStatus != NO_ERROR) {
+ goto CleanUp;
+ }
+ }
+
+ // BUGBUG; // lost ApiStatus here?
+ if (ApiStatus == NO_ERROR) {
+
+ if (ServiceIsStarting) {
+ NetpAssert( NewRole != REPL_ROLE_STOPPED );
+
+ IF_DEBUG(REPL) {
+ NetpKdPrint(( PREFIX_REPL
+ "ReplChangeRole: starting RPC server...\n" ));
+ }
+
+ //
+ // Start RPC server. Note that API threads can start when we do
+ // this call, so we have to make sure the global data is valid.
+ //
+ ApiStatus = NetpStartRpcServer(
+ REPLICATOR_INTERFACE_NAME,
+ repl_ServerIfHandle);
+
+ if (ApiStatus != NO_ERROR) {
+
+ SET_GLOBAL_ROLE( REPL_ROLE_STOPPED );
+
+ NetpKdPrint(( PREFIX_REPL
+ "ReplChangeRole: NetpStartRpcServer failed, "
+ "status = " FORMAT_API_STATUS ".\n", ApiStatus ));
+
+ goto CleanUp;
+ }
+
+ ReportStatus(
+ SERVICE_RUNNING,
+ NO_ERROR,
+ 0, // wait hint
+ 0 ); // checkpoint
+
+ } else if (NewRole == REPL_ROLE_STOPPED ) {
+
+CleanUp:
+ //
+ // Stop accepting RPC (API) calls.
+ // Also tell export and import threads (if any) to stop.
+ // Also set global role to stopped.
+ //
+ IF_DEBUG(REPL) {
+ NetpKdPrint(( PREFIX_REPL
+ "ReplChangeRole: stopping entire service...\n" ));
+ }
+
+ ReplStopService( );
+ /*NOTREACHED*/
+
+ } else {
+ // Just changing between import, export, and both.
+ }
+
+ }
+
+ // In the event of multiple errors, tell about first one.
+ if (FirstStatus != NO_ERROR) {
+ ApiStatus = FirstStatus;
+ }
+
+ IF_DEBUG(REPL) {
+ NetpKdPrint(( PREFIX_REPL
+ "ReplChangeRole: returning status of " FORMAT_API_STATUS ".\n",
+ ApiStatus ));
+ NetpKdPrint(( PREFIX_REPL
+ "**************************** NEW ROLE: " FORMAT_DWORD " "
+ "****************************\n", NewRole ));
+
+ }
+
+ return (ApiStatus);
+
+} // ReplChangeRole
diff --git a/private/net/svcdlls/repl/server/cli_dupl.c b/private/net/svcdlls/repl/server/cli_dupl.c
new file mode 100644
index 000000000..70e70858d
--- /dev/null
+++ b/private/net/svcdlls/repl/server/cli_dupl.c
@@ -0,0 +1,497 @@
+/*++
+
+Copyright (c) 1987-1993 Microsoft Corporation
+
+Module Name:
+
+ cli_dupl.c
+
+Abstract:
+
+ Contains functions that implement the client's multi-master mechanism.
+
+Author:
+
+ Ported from Lan Man 2.1
+
+Environment:
+
+ User mode only.
+ Contains NT-specific code.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+ 11-Apr-1989 (yuv)
+ Initial Coding.
+
+ 03-Oct-1991 (cliffv)
+ Ported to NT. Converted to NT style.
+ 20-Jan-1992 JohnRo
+ Changed file name from repl.h to replgbl.h to avoid MIDL conflict.
+ Fixed bug regarding returned value from NetpReplWriteMail functions.
+ Changed to use NetLock.h (allow shared locks, for one thing).
+ Use REPL_STATE_ equates for client_list_rec.state values.
+ Made changes suggested by PC-LINT.
+ 28-Jan-1992 JohnRo
+ Changed to use LPTSTR etc.
+ 24-Mar-1992 JohnRo
+ Added/corrected some lock handling.
+ 11-Mar-1993 JohnRo
+ RAID 14144: avoid very long hourglass in repl UI.
+ Don't remove the client list record if master is dead.
+ Made changes suggested by PC-LINT 5.0
+ Added some mailslot msg debug code.
+ 26-Mar-1993 JohnRo
+ RAID 4267: Replicator has problems when work queue gets large.
+
+--*/
+
+
+#include <windows.h> // IN, DWORD, OPTIONAL, etc.
+#include <lmcons.h>
+
+#include <lmerrlog.h> // NELOG_* defines
+#include <lmrepl.h> // REPL_STATE_ equates.
+#include <netdebug.h> // DBGSTATIC ..
+#include <netlock.h> // Lock data types, functions, and macros.
+#include <tstr.h> // TCHAR_EOS, etc.
+#include <stdlib.h> // wcscpy().
+#include <winerror.h> // NO_ERROR.
+
+#include <replgbl.h> // ReplGlobal variables.
+#include <repldefs.h>
+#include <client.h>
+#include <replp.h>
+
+// G L O B A L S
+
+
+//
+//
+// Multiple masters mechanism
+//
+// In order to establish the existence and keep track of "registered
+// duplicates" (i.e. masters for whom we have ascertained they are multiple) so
+// their updates for the particular directories may be safely ignored, the
+// following data structure is used:
+//
+//
+// list_header --> dir_name ---> dir_name ....
+// master master
+// | |
+// | |
+// V V
+// masterII masterII
+// | |
+// | |
+// V V
+// . .
+// . .
+// . .
+//
+
+
+
+
+DBGSTATIC PDUPL_MASTERS_REC
+ReplGetDuplRecord(
+ IN PCLIENT_LIST_REC dir_rec,
+ IN LPTSTR dupl_master
+ )
+/*++
+
+Routine Description:
+
+ Searchs for the dupl entry, if doesn't exist creates new, and returns
+ NULL pointer.
+
+ BUGBUG do we have to lock this client list while comparing.
+
+Arguments:
+
+ dir_rec - Specifies which directory is affected.
+
+ dupl_master - Specifies the computername of the duplicate master.
+
+Return Value:
+
+ Returns pointer to dupl_rec or NULL.
+
+--*/
+{
+ PDUPL_MASTERS_REC dupl;
+
+ for ( dupl = dir_rec->dupl.next_p; dupl != NULL; dupl = dupl->next_p ) {
+ if (STRICMP(dupl->master, dupl_master) == 0) {
+ break;
+ }
+ }
+ return dupl;
+}
+
+
+
+VOID
+ReplMultiMaster(
+ IN DWORD mode,
+ IN PCLIENT_LIST_REC dir_rec,
+ IN LPTSTR dupl_master
+ )
+/*++
+
+Routine Description:
+
+ Manages all multiple master cases.
+
+ Assumes that caller has a lock (any kind) on RCGlobalClientListLock.
+
+Arguments:
+
+ mode - Specifies the actiion to take for this multiple master.
+
+ dir_rec - Specifies which directory is affected.
+
+ dupl_master - Specifies the computername of the duplicate master.
+
+Return Value:
+
+ None.
+
+Threads:
+
+ Called by client and syncer threads.
+
+--*/
+{
+ PDUPL_MASTERS_REC dupl;
+ PDUPL_MASTERS_REC temp;
+
+ switch (mode) {
+
+ //
+ // Handle the case where a machine claims to be the master for a directory
+ // but we're currently being served by another master.
+ //
+
+ case DUPL_MASTER_UPDATE:
+
+
+ //
+ // If we've not already registered this duplicate,
+ // see if the old master is still the master for this directory.
+ //
+ // (If we've already noticed this duplciate, just ignore this event.)
+ //
+
+ if ((dupl = ReplGetDuplRecord(dir_rec, dupl_master)) == NULL) {
+
+ //
+ // ask old master if still supporting dir
+ //
+
+ ReplClientSendMessage(IS_MASTER, dir_rec->master, dir_rec->dir_name);
+
+
+ //
+ // Allocate a duplicate master entry and describe this duplicate
+ // master.
+ //
+
+ dupl = ReplClientGetPoolEntry( DUPLMA_POOL );
+
+ if ( dupl == NULL ) {
+ return;
+ }
+
+ dupl->next_p = NULL;
+ (void) STRCPY( dupl->master, dupl_master );
+ dupl->count = 1;
+ if ((dupl->sleep_time = dir_rec->pulse_time / 10) == 0 ) {
+ dupl->sleep_time = 1;
+ }
+
+
+ //
+ // Link this entry onto the end of dupl masters chain
+ //
+
+ ACQUIRE_LOCK( RCGlobalDuplListLock );
+
+ if ((temp = dir_rec->dupl.next_p) != NULL) {
+ while (temp->next_p != NULL)
+ temp = temp->next_p;
+
+ temp->next_p = dupl;
+ } else {
+ dir_rec->dupl.next_p = dupl;
+ }
+
+ RELEASE_LOCK( RCGlobalDuplListLock );
+ }
+ break;
+
+
+
+ //
+ // Handle the case where the old master doesn't respond to the IS_MASTER
+ // query in a timely manner.
+ //
+
+ case DUPL_MASTER_TIMEOUT:
+
+ //
+ // Get the Duplicate Master record.
+ //
+ // (This can't really fail since we added it above).
+ //
+
+ if ((dupl = ReplGetDuplRecord(dir_rec, dupl_master)) != NULL) {
+
+
+ //
+ // We are getting a query timeout, but we already got
+ // a reply that the old master is active and registered
+ // the new one as a duplicate.
+ //
+
+ if (dupl->count == 0) {
+ break;
+ }
+
+
+ //
+ // If we've already queried the old master enough times,
+ // note that the old master is dead.
+ //
+
+ if ((dupl->count)++ >= DUPL_QUERY_RETRY) {
+
+ //
+ // old master's dead
+ //
+
+ // Note: ReplMasterDead needs lock (any kind) on
+ // RCGlobalClientListLock.
+ ReplMasterDead(dir_rec);
+
+ //
+ // If we've not queried the old master enough times,
+ // Query it again.
+ //
+
+ } else {
+
+ ReplClientSendMessage(IS_MASTER, dir_rec->master, dir_rec->dir_name);
+
+ //
+ // set sleep (timeout) record to be used by WatchDog
+ //
+
+ if ( (dupl->sleep_time = dir_rec->pulse_time / 10) == 0 ) {
+ dupl->sleep_time = 1;
+ }
+ }
+ }
+ break;
+
+ //
+ // Old master is active so register all unregistered dupls as
+ // "official duplicates", nothing more is needed (the timeout
+ // for the query message will be ignored)
+ //
+
+ case OLD_MASTER_ACTIVE:
+
+ dupl = dir_rec->dupl.next_p;
+ while (dupl != NULL) {
+ dupl->count = 0;
+ dupl->sleep_time = 0;
+
+ //
+ // Let the Duplicate Master know we think it is a duplicate.
+ //
+
+ ReplClientSendMessage(DIR_COLLIDE, dupl->master, dir_rec->dir_name);
+ dupl = dupl->next_p;
+ }
+ break;
+
+
+ //
+ // Handle when the old master denies being the master of this directory.
+ //
+
+ case OLD_MASTER_DONE:
+
+ // Note: ReplMasterDead needs lock (any kind) on RCGlobalClientListLock.
+ ReplMasterDead(dir_rec);
+ break;
+
+ default:
+ break;
+
+ }
+
+} // ReplMultiMaster
+
+
+
+VOID
+ReplMasterDead(
+ IN PCLIENT_LIST_REC dir_rec
+ )
+/*++
+
+Routine Description:
+
+ Old master is gone ! Set signal to NO_MASTER and remove client_list entry.
+ BUGBUG: Let's leave the entry, as that's where signal is!
+
+ We don't install a new master for this directory. We'll discover the
+ new master on the next pulse from that master.
+
+ Assumes that caller has a lock (any kind) on RCGlobalClientListLock.
+
+Arguments:
+
+ dir_rec - Supplies a description of the directory whose master is dead.
+
+Return Value:
+
+ None.
+
+Threads:
+
+ Called by client and syncer threads.
+
+--*/
+{
+
+ //
+ // Mark the directory that there is no master.
+ //
+
+ // Note: ReplSetSignalFile needs lock (any kind) on RCGlobalClientListLock.
+ ReplSetSignalFile(dir_rec, REPL_STATE_NO_MASTER);
+
+#if 0
+ //
+ // Delete the record for this client.
+ //
+
+ // Note: ReplRemoveClientRec needs exclusive RCGlobalClientListLock.
+ // BUGBUG: THIS IS STUPID!
+ ReplRemoveClientRec(dir_rec);
+#endif
+
+}
+
+
+
+VOID
+ReplClientSendMessage(
+ IN DWORD type,
+ IN LPTSTR master,
+ IN LPTSTR dir_name
+ )
+
+/*++
+
+Routine Description:
+
+ Constructs a QUERY_MSG record and sends writes to master's mailslot.
+
+Arguments:
+
+ type - Type of query message message to send.
+
+ master - Computername of the master to send to.
+
+ dir_name - Name of the directory to query.
+
+Return Value:
+
+ NONE.
+
+Threads:
+
+ Called by client and syncer threads.
+
+--*/
+{
+ PSMLBUF_REC que_p;
+ PQUERY_MSG msg_p;
+ NET_API_STATUS NetStatus;
+ TCHAR MailslotName[FULL_SLOT_NAME_SIZE];
+ BYTE MarshalledQueryMessage[ 2 * sizeof(QUERY_MSG) ];
+ DWORD MarshalledMessageLen = 0;
+ DWORD WinError;
+
+
+ //
+ // Allocate a buffer for the query message.
+ //
+
+ if ((que_p = ReplClientGetPoolEntry( QUESML_POOL )) == NULL) {
+ return;
+ }
+
+ msg_p = (PQUERY_MSG )(que_p->data);
+
+ //
+ // Build the message
+ //
+
+ msg_p->header.msg_type = type;
+ (void) STRCPY( msg_p->header.sender, ReplGlobalComputerName );
+ (void) STRCPY( msg_p->header.senders_domain, ReplGlobalDomainName );
+
+ (void) STRCPY( msg_p->dir_name, dir_name);
+
+
+ //
+ // Build the destination mailslot name: \\master\MAILSLOT\NET\REPL_MAS
+ //
+ NetpAssert( master != NULL );
+ NetpAssert( (*master) != TCHAR_EOS );
+
+ (void) STRCPY( MailslotName, SLASH_SLASH );
+ (void) STRCAT( MailslotName, master );
+ (void) STRCAT( MailslotName, (LPTSTR) MASTER_SLOT_NAME );
+
+ //
+ // marshall query message
+ //
+
+ if( ( WinError = ReplMarshallQueryMsg( (LPBYTE) msg_p,
+ MarshalledQueryMessage,
+ &MarshalledMessageLen ) ) != NO_ERROR) {
+
+ // the marshall routine could fail only iff the system doesn't
+ // have sufficient memory
+
+ AlertLogExit(0,
+ NELOG_ReplNetErr,
+ WinError,
+ NULL,
+ NULL,
+ NO_EXIT);
+ }
+ NetpAssert( MarshalledMessageLen != 0 );
+
+ //
+ // Write the message to the mailslot
+ //
+
+ NetStatus = NetpReplWriteMail(
+ MailslotName,
+ (LPBYTE) MarshalledQueryMessage,
+ MarshalledMessageLen );
+
+ if ( NetStatus != NO_ERROR ) {
+ AlertLogExit(0, NELOG_ReplNetErr, NetStatus, NULL, NULL, NO_EXIT);
+ }
+
+ ReplClientFreePoolEntry( QUESML_POOL, que_p);
+}
diff --git a/private/net/svcdlls/repl/server/cli_list.c b/private/net/svcdlls/repl/server/cli_list.c
new file mode 100644
index 000000000..b369dcb78
--- /dev/null
+++ b/private/net/svcdlls/repl/server/cli_list.c
@@ -0,0 +1,850 @@
+/*++
+
+Copyright (c) 1987-1993 Microsoft Corporation
+
+Module Name:
+
+ cli_list.c
+
+Abstract:
+
+ Contains function that manage all client lists.
+
+Author:
+
+ Ported from Lan Man 2.1
+
+Environment:
+
+ User mode only.
+ Contains NT-specific code.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+ 11-Apr-1989 (yuv)
+ Initial Coding
+
+ 03-Oct-1991 (cliffv)
+ Ported to NT. Converted to NT style.
+ 14-Jan-1992 JohnRo
+ Made changes suggested by PC-LINT.
+ Changed to use NetLock.h (allow shared locks, for one thing).
+ Added lockcount and time_of_first_lock fields to client list record.
+ Use REPL_STATE_ equates for client_list_rec.state values.
+ Added RemoveClientRecForDirName() for use by NetrReplImportDirDel().
+ Maintain RCGlobalClientListCount for use by NetrReplImportDirEnum().
+ Added some more debug code.
+ 28-Jan-1992 JohnRo
+ ReplInitSetSignalFile() is obsolete.
+ P_ globals are now named ReplGlobal (and declared in replgbl.h).
+ Changed to use LPTSTR etc.
+ 04-Mar-1992 JohnRo
+ Changed ReplMain's interface to match new service controller.
+ 15-Mar-1992 JohnRo
+ Update registry with new values.
+ 24-Mar-1992 JohnRo
+ Renamed many ReplGlobal vars to ReplConfig vars.
+ Added/corrected some lock handling.
+ 25-Mar-1992 JohnRo
+ Avoid obsolete state values.
+ 26-Mar-1992 JohnRo
+ Fixed call to ImportDirSetState() in ReplClientInitLists().
+ Added more assertion checking in ReplAddClientRec().
+ 27-Mar-1992 JohnRo
+ Allow use of ReplAddClientRec() without some parameters.
+ RCGlobalClientListCount is sometimes trashed by
+ ReplRemoveClientRecForDirName().
+ 19-Aug-1992 JohnRo
+ RAID 3603: import tree (TMPREE.RP$) generated at startup.
+ 25-Sep-1992 JohnRo
+ RAID 5494: repl svc does not maintain time stamp on import startup.
+ 06-Nov-1992 JohnRo
+ RAID 5496: old fields not maintained in registry.
+ Minor debug and comment enhancements.
+ 11-Jan-1993 JohnRo
+ RAID 6710: repl cannot manage dir with 2048 files.
+ Made changes suggested by PC-LINT 5.0
+ 15-Jan-1993 JohnRo
+ RAID 7717: Repl assert if not logged on correctly. (Also do event
+ logging for real.)
+ 02-Apr-1993 JohnRo
+ Use NetpKdPrint() where possible.
+ Added more comments about which thread is which.
+
+--*/
+
+
+#include <windows.h> // IN, DWORD, etc.
+#include <lmcons.h>
+
+#include <alertmsg.h> // ALERT_* defines
+#include <align.h> // ALIGN_* defines
+#include <dirname.h> // ReplIsDirNameValid().
+#include <icanon.h> // I_NetPathCompare
+#include <impdir.h> // ImportDirSetState().
+#include <iniparm.h> // DEFAULT_ equates.
+#include <lmerr.h> // NERR_* defines
+#include <lmerrlog.h> // NELOG_* defines
+#include <lmrepl.h> // REPL_STATE_ equates.
+#include <names.h> // NetpIsComputerNameValid().
+#include <netdebug.h> // DBGSTATIC ..
+#include <netlib.h> // NetpMemoryAllocate
+#include <netlock.h> // Lock data types, functions, and macros.
+#include <prefix.h> // PREFIX_ equates.
+#include <tstr.h> // STRLEN(), etc.
+
+//
+// Local include files
+//
+#include <repldefs.h>
+#include <client.h>
+#include <replgbl.h> // ReplGlobal and ReplConfig variables.
+#include <replp.h>
+
+//
+// G L O B A L S:
+//
+
+
+
+DBGSTATIC VOID
+ReplClientInitPool(
+ IN DWORD PoolNumber,
+ IN DWORD EntryCount,
+ IN DWORD EntrySize
+ )
+/*++
+
+Routine Description:
+
+ Allocates initial (guessed) memory for the Big Buffer pool.
+ If needed, other entries will be allocated dynamically.
+
+ Guess is = MAX(count, POOL_MIN_ENTRY_COUNT )
+
+ From here the linked list memory is managed as follows:
+
+ If an entry is deleted (FREE)
+ add record to free_list.
+
+ If a new entry is required (ALLOC)
+ if NOTEMPTY(free_list)
+ grab a record from free_list
+ else
+ Allocate new entry from the heap.
+
+Arguments:
+
+ PoolNumber - Which pool to initialize
+
+ EntryCount - Initial guess of number of entries to allocate.
+
+ EntrySize - Size in byte of each entry in the pool.
+
+Return Value:
+
+ None.
+
+--*/
+{
+#ifdef notdef
+ DWORD i;
+ NET_API_STATUS NetStatus;
+#endif // notdef
+
+ //
+ // Ensure the EntrySize is a multiple of pointer size to ensure
+ // each entry allocated here is pointer size aligned.
+ //
+
+ EntrySize = ROUND_UP_COUNT( EntrySize, ALIGN_LPBYTE );
+
+ RCGlobalPoolEntrySize[PoolNumber] = EntrySize;
+ RCGlobalPoolHeader[PoolNumber] = NULL;
+
+
+#ifdef notdef
+
+ //
+ // Always allocate a minimum number of entries.
+ //
+
+ if (EntryCount < POOL_MIN_ENTRY_COUNT ) {
+ EntryCount = POOL_MIN_ENTRY_COUNT ;
+ }
+
+ //
+ // Allocate all the appropiate entries.
+ //
+ // It is better to detect a lack of resources at service initialization
+ // rather than later. That is why the pool is primed, otherwise we
+ // could just let the pools prime themselves.
+ //
+
+ for (i = 0; i < (EntryCount - 1); i++) {
+ PUCHAR rec;
+
+ rec = NetpMemoryAllocate( RCGlobalPoolEntrySize[PoolNumber] );
+
+ if ( rec == NULL ) {
+ NetStatus = ERROR_NOT_ENOUGH_MEMORY;
+ AlertLogExit( ALERT_ReplSysErr,
+ NELOG_ReplSysErr,
+ NetStatus,
+ NULL,
+ NULL,
+ NO_EXIT);
+ BUGBUG; // Hey, this will cause ReplClientFreePoolEntry to
+ // fault on a NULL pointer! --JR
+ }
+
+ ReplClientFreePoolEntry( PoolNumber, rec );
+
+ }
+#endif // notdef
+
+ DBG_UNREFERENCED_PARAMETER (EntryCount);
+}
+
+VOID
+ReplClientFreePools(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ free up all memory that consumed in pool
+
+Arguments:
+
+ none
+
+Return Value:
+
+ none
+
+--*/
+{
+
+ DWORD i;
+ PUCHAR Rec, NextRec;
+
+ for(i = 0; i < POOL_COUNT; i++) {
+
+ NextRec = RCGlobalPoolHeader[i];
+
+ while(NextRec != NULL) {
+
+ Rec = NextRec;
+ NextRec = *((PUCHAR *)Rec);
+
+ NetpMemoryFree((LPVOID) Rec);
+
+ }
+
+ }
+}
+
+
+
+PVOID
+ReplClientGetPoolEntry(
+ IN DWORD PoolNumber
+ )
+/*++
+
+Routine Description:
+
+ Returns a pointer to an entry from the appropriate pool
+
+Arguments:
+
+ PoolNumber - Which pool to allocate from
+
+Return Value:
+
+ Pointer to a buffer of the appropriate size.
+ This will be NULL is the memory is not available.
+
+Threads:
+
+ Called by API, client, and syncer threads.
+
+--*/
+{
+ PUCHAR rec;
+ NET_API_STATUS NetStatus;
+
+ //
+ // Take first record on free_list, make header point to next one.
+ //
+
+ ACQUIRE_LOCK( RCGlobalPoolLock );
+
+ if ( RCGlobalPoolHeader[PoolNumber] != NULL) {
+
+ rec = RCGlobalPoolHeader[PoolNumber];
+ RCGlobalPoolHeader[PoolNumber] =
+ *( (PUCHAR *) RCGlobalPoolHeader[PoolNumber]);
+
+ RELEASE_LOCK( RCGlobalPoolLock );
+ return rec;
+ }
+
+ RELEASE_LOCK( RCGlobalPoolLock );
+
+
+ //
+ // If pool is empty, try to allocate an entry.
+ //
+
+ rec = NetpMemoryAllocate( RCGlobalPoolEntrySize[PoolNumber] );
+
+ if ( rec == NULL ) {
+ NetStatus = ERROR_NOT_ENOUGH_MEMORY;
+ AlertLogExit( ALERT_ReplSysErr,
+ NELOG_ReplSysErr,
+ NetStatus,
+ NULL,
+ NULL,
+ NO_EXIT);
+ }
+
+ return rec;
+}
+
+
+
+VOID
+ReplClientFreePoolEntry(
+ IN DWORD PoolNumber,
+ IN PVOID Buffer
+ )
+/*++
+
+Routine Description:
+
+ Frees an entry back to the appropriate pool
+
+Arguments:
+
+ PoolNumber - Which pool to free to.
+
+ Buffer - Pointer to the buffer to free.
+
+Return Value:
+
+ NONE.
+
+Threads:
+
+ Called by API, client, and syncer threads.
+
+--*/
+{
+ NetpAssert( Buffer != NULL );
+
+ //
+ // Link this buffer at the front of the pool.
+ //
+
+ ACQUIRE_LOCK( RCGlobalPoolLock );
+
+ * ((PUCHAR *)(Buffer)) = RCGlobalPoolHeader[PoolNumber];
+ RCGlobalPoolHeader[PoolNumber] = (PUCHAR) Buffer;
+
+ RELEASE_LOCK( RCGlobalPoolLock );
+}
+
+
+
+
+NET_API_STATUS
+ReplClientInitLists(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ Counts initial # of directories under IMPORT path and allocates
+ memory for client_list. Reads config data into the client list.
+ Also initializes various pools.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NET_API_STATUS.
+
+Threads:
+
+ Only called by client thread.
+
+--*/
+{
+ NET_API_STATUS ApiStatus;
+ DWORD dir_count = 0;
+
+ TCHAR PathName[MAX_PATH];
+ DWORD PathIndex;
+ WIN32_FIND_DATA FindData;
+ HANDLE FindHandle;
+
+ //
+ // Build the path name <ImportPath>\*.*
+ //
+
+ NetpAssert( ReplConfigImportPath != NULL );
+ (void) STRCPY(PathName, ReplConfigImportPath);
+ PathIndex = STRLEN(PathName);
+ (void) STRCPY(PathName + PathIndex++, SLASH);
+ (void) STRCPY(PathName + PathIndex, STAR_DOT_STAR);
+
+ //
+ // Go thru all SubDirectories counting them and Setting the signal file.
+ //
+
+ FindHandle = FindFirstFile( PathName, &FindData );
+
+ if ( FindHandle != INVALID_HANDLE_VALUE ) {
+ do {
+
+ if ( FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) {
+
+ //
+ // Make sure it's not ".", "..", "TMPTREE.RP$", etc.
+ //
+
+ if (ReplIgnoreDirOrFileName(FindData.cFileName)) {
+ continue;
+ }
+
+ //
+ // Set the state to indicate there is no master.
+ //
+
+ (void) STRCPY( PathName + PathIndex, FindData.cFileName );
+
+ ApiStatus = ImportDirSetState(
+ FindData.cFileName,
+ REPL_STATE_NO_MASTER );
+ if (ApiStatus != NO_ERROR) {
+ //
+ // One very likely reason for that to fail is if we're not
+ // running in the Replicator Local Group, so we would get
+ // access denied when trying to write to the registry.
+ // Log this and continue.
+ //
+ ReplErrorLog(
+ NULL, // local (no server name)
+ NELOG_ReplSysErr,
+ ApiStatus,
+ NULL,
+ NULL );
+ }
+
+ ++dir_count;
+ }
+ } while ( FindNextFile( FindHandle, &FindData ) );
+
+ (VOID) FindClose( FindHandle );
+ }
+
+
+
+ //
+ // Initialize each of the memory pools
+ //
+
+ ReplClientInitPool( CLIENT_LIST_POOL, dir_count, sizeof( CLIENT_LIST_REC ) );
+ ReplClientInitPool( QUEBIG_POOL, dir_count, sizeof( BIGBUF_REC ) );
+ ReplClientInitPool( QUESML_POOL, dir_count, sizeof( SMLBUF_REC ) );
+ ReplClientInitPool( DUPLMA_POOL, dir_count, sizeof( DUPL_MASTERS_REC ) );
+
+ //
+ // Initialize values in client list to reflect registry updates above.
+ // BUGBUG: if dir exists in registry but not on disk, state is not
+ // reset to REPL_STATE_NO_MASTER.
+ //
+ ApiStatus = ImportDirReadClientList( );
+
+ if (ApiStatus != NO_ERROR) {
+ NetpKdPrint(( PREFIX_REPL
+ "ReplClientInitLists: ImportDirReadClientList failed, status "
+ FORMAT_API_STATUS ".\n", ApiStatus ));
+ }
+
+ return (ApiStatus);
+
+} // ReplClientInitLists
+
+
+
+
+PCLIENT_LIST_REC
+ReplAddClientRec(
+ IN LPTSTR dir_name,
+ IN PSYNCMSG master_info OPTIONAL,
+ IN PMSG_STATUS_REC dir_info OPTIONAL
+ )
+/*++
+
+Routine Description:
+
+ Allocated a new client list entry, fills it in, and links it onto
+ the front of the client list.
+
+Arguments:
+
+ dir_name - new dir name
+
+ master_info - pointer to sync_msg_structure with master's info
+
+ dir_info - pointer to sync_msg_structure with dir's info
+
+Return Value:
+
+ Returns pointer to newly allocated entry. Returns NULL if not enough
+ memory for new entry.
+
+Threads:
+
+ Only called by API and syncer threads.
+
+--*/
+{
+ PCLIENT_LIST_REC rec;
+
+ //
+ // Check for errors by caller.
+ //
+
+ NetpAssert( ReplIsDirNameValid( dir_name ) );
+ if (master_info != NULL) {
+ // BUGBUG: Is this UNC name? What about null name?
+ NetpAssert( NetpIsComputerNameValid( master_info->header.sender ) );
+ }
+ if (dir_info != NULL) {
+ NetpAssert( ReplIsIntegrityValid( dir_info->integrity ) );
+ NetpAssert( ReplIsExtentValid( dir_info->extent ) );
+ // BUGBUG: check other fields while we're at it?
+ }
+
+ //
+ // Get an entry from the pool.
+ //
+
+ rec = ReplClientGetPoolEntry( CLIENT_LIST_POOL );
+
+ if ( rec == NULL ) {
+ return NULL;
+ }
+
+ //
+ // put values in.
+ //
+
+ (void) STRCPY( rec->dir_name, dir_name);
+ if (master_info != NULL) {
+ (void) STRCPY( rec->master, master_info->header.sender);
+ } else {
+ (rec->master)[0] = TCHAR_EOS;
+ }
+ if (dir_info != NULL) {
+ rec->integrity = dir_info->integrity;
+ rec->extent = dir_info->extent;
+ } else {
+ rec->integrity = DEFAULT_INTEGRITY;
+ rec->extent = DEFAULT_EXTENT;
+ }
+
+ //
+ // Convert times to seconds.
+ //
+
+ if (master_info != NULL) {
+ rec->sync_time = master_info->info.sync_rate * 60;
+ rec->pulse_time = master_info->info.pulse_rate * 60;
+ rec->guard_time = master_info->info.guard_time * 60;
+ rec->rand_time = master_info->info.random;
+ } else {
+
+ // BUGBUG; // should acquire shared ReplConfigLock here!
+ rec->sync_time = ReplConfigInterval * 60;
+ rec->pulse_time = ReplConfigPulse * 60;
+ rec->guard_time = ReplConfigGuardTime * 60;
+ rec->rand_time = ReplConfigRandom; // already in seconds.
+ // BUGBUG; // should free ReplConfigLock here!
+ }
+
+ //
+ // Set other values to defaults.
+ //
+ rec->checksum = 0;
+ rec->count = (DWORD) -1;
+ rec->est_max_dir_entry_count = 1; // Estimated max entries (so far): 1.
+ rec->timestamp = NetpReplTimeNow();
+ rec->alerts = 0;
+ rec->state = REPL_STATE_NEVER_REPLICATED;
+
+ rec->lockcount = 0;
+ rec->time_of_first_lock = 0;
+
+ rec->timer.timeout = 0;
+ rec->timer.type = 0;
+ rec->timer.grd_timeout = 0;
+ rec->timer.grd_checksum = 0;
+ rec->timer.grd_count = (DWORD) -1;
+
+ rec->dupl.master[0] = 0;
+ rec->dupl.count = 0;
+ rec->dupl.sleep_time = 0;
+ rec->dupl.next_p = NULL;
+
+
+ //
+ // Chain record into the front of the client_list.
+ //
+
+ ACQUIRE_LOCK( RCGlobalClientListLock );
+
+ rec->next_p = RCGlobalClientListHeader;
+ rec->prev_p = NULL;
+ RCGlobalClientListHeader = rec;
+
+ //
+ // When inserting the very first record, there is no next.
+ //
+
+ if (rec->next_p != NULL)
+ (rec->next_p)->prev_p = rec;
+
+ ++RCGlobalClientListCount;
+
+ RELEASE_LOCK( RCGlobalClientListLock );
+
+ //
+ // Return a pointer to the newly initialized entry.
+ //
+
+ return rec;
+
+}
+
+
+
+VOID
+ReplRemoveClientRec(
+ IN OUT PCLIENT_LIST_REC rec
+ )
+/*++
+
+Routine Description:
+
+ Removes record from client_list (doubly linked) and puts record
+ on free_list (singly linked)
+
+ Assumes that caller has an exclusive lock on RCGlobalClientListLock.
+ This prevents this from being called while another thread has a
+ pointer to this entry.
+
+Arguments:
+
+ rec - Pointer to record to delink.
+
+Return Value:
+
+ None.
+
+Threads:
+
+ Only called by syncer thread.
+
+--*/
+{
+ PDUPL_MASTERS_REC dupl;
+ PDUPL_MASTERS_REC next;
+
+ NetpAssert( rec != NULL );
+
+ //
+ // First free all elements in dupl list.
+ //
+
+ dupl = rec->dupl.next_p;
+
+ while (dupl != NULL) {
+ next = dupl->next_p;
+ ReplClientFreePoolEntry( DUPLMA_POOL, dupl);
+ dupl = next;
+ }
+
+ //
+ // Delink the record from the client list.
+ //
+
+ if (rec->prev_p != NULL) {
+ (rec->prev_p)->next_p = rec->next_p;
+ } else { // it's the first record.
+ RCGlobalClientListHeader = rec->next_p;
+ }
+
+ if (rec->next_p != NULL) { // if it's not the last record.
+ (rec->next_p)->prev_p = rec->prev_p;
+ }
+
+ //
+ // Free the record into the pool.
+ //
+
+ ReplClientFreePoolEntry( CLIENT_LIST_POOL, rec );
+
+ --RCGlobalClientListCount;
+
+}
+
+
+NET_API_STATUS
+ReplRemoveClientRecForDirName (
+ IN LPTSTR DirName
+ )
+/*++
+
+Routine Description:
+
+ Removes record from client_list (doubly linked) and puts record
+ on free_list (singly linked)
+
+ Assumes that caller has an exclusive lock on RCGlobalClientListLock.
+
+Arguments:
+
+ DirName - name of entry to search for and delete.
+
+Return Value:
+
+ None.
+
+Threads:
+
+ API threads only.
+
+--*/
+{
+ NET_API_STATUS ApiStatus;
+ PCLIENT_LIST_REC rec;
+
+ NetpAssert( DirName != NULL );
+
+ //
+ // Scan the list for a match on this DirName...
+ // BUGBUG: Should we check MasterName too?
+ //
+ for ( rec = RCGlobalClientListHeader; rec != NULL; rec = rec->next_p ) {
+ if ( (STRICMP( rec->dir_name, DirName ) == 0) ) {
+ // BUGBUG: Should we check MasterName too?
+ break;
+ }
+ }
+
+ if (rec == NULL) {
+ ApiStatus = NERR_UnknownDevDir; // entry not found
+ } else {
+ PDUPL_MASTERS_REC dupl;
+ PDUPL_MASTERS_REC next;
+
+ ApiStatus = NO_ERROR; // entry found, so let's delete it...
+
+ //
+ // First free all elements in dupl list.
+ //
+
+ dupl = rec->dupl.next_p;
+
+ while (dupl != NULL) {
+ next = dupl->next_p;
+ ReplClientFreePoolEntry( DUPLMA_POOL, dupl);
+ dupl = next;
+ }
+
+ //
+ // Delink the record from the client list.
+ //
+
+ if (rec->prev_p != NULL) {
+ (rec->prev_p)->next_p = rec->next_p;
+ } else { // it's the first record.
+ RCGlobalClientListHeader = rec->next_p;
+ }
+
+ if (rec->next_p != NULL) { // if it's not the last record.
+ (rec->next_p)->prev_p = rec->prev_p;
+ }
+
+ //
+ // Free the record into the pool.
+ //
+
+ ReplClientFreePoolEntry( CLIENT_LIST_POOL, rec );
+
+ NetpAssert( RCGlobalClientListCount >= 1 );
+ --RCGlobalClientListCount;
+
+ }
+
+ return (ApiStatus);
+
+} // ReplRemoveClientRecForDirName
+
+
+
+PCLIENT_LIST_REC
+ReplGetClientRec(
+ IN LPTSTR dir_name,
+ IN LPTSTR MasterName OPTIONAL
+ )
+/*++
+
+Routine Description:
+
+ Searches client_list for record with dir_name.
+
+ Assumes that caller has a shared lock on RCGlobalClientListLock.
+
+Arguments:
+
+ dir_name - The directory name to look for.
+
+ MasterName - If not NULL, specifies the name of the master server for
+ this directory. If specified, this name must match the one in the
+ client record, otherwise this routine merely indicates that the
+ record was not found.
+
+Return Value:
+
+ Returns a pointer to the required record or NULL if not found
+
+--*/
+{
+ PCLIENT_LIST_REC rec;
+
+ for ( rec = RCGlobalClientListHeader; rec != NULL; rec = rec->next_p ) {
+ if ( (STRICMP( rec->dir_name, dir_name ) == 0) ) {
+ if ( MasterName == NULL ||
+ ReplNetNameCompare( NULL,
+ rec->master,
+ MasterName,
+ NAMETYPE_COMPUTER,
+ 0 ) == 0 ) {
+ break;
+ }
+ }
+ }
+
+ return rec;
+}
diff --git a/private/net/svcdlls/repl/server/client.c b/private/net/svcdlls/repl/server/client.c
new file mode 100644
index 000000000..297b3cc30
--- /dev/null
+++ b/private/net/svcdlls/repl/server/client.c
@@ -0,0 +1,1200 @@
+/*++
+
+Copyright (c) 1987-1993 Microsoft Corporation
+
+Module Name:
+
+ client.c
+
+Abstract:
+
+ Contains main module of the REPL Client.
+
+Author:
+
+ Ported from Lan Man 2.1
+
+Environment:
+
+ User mode only.
+ Contains NT-specific code.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+ 03-Apr-1989 (yuv)
+ Initial Coding.
+
+ 03-Oct-1991 (cliffv)
+ Ported to NT. Converted to NT style.
+ 20-Jan-1992 JohnRo
+ Changed file name from repl.h to replgbl.h to avoid MIDL conflict.
+ Added global flag for ReplTest use.
+ Changed to use NetLock.h (allow shared locks, for one thing).
+ ReportStatus() should add thread ID to status being reported.
+ Added RCGlobalClientListCount for use by NetrReplImportDirEnum().
+ Made changes suggested by PC-LINT.
+ 27-Jan-1992 JohnRo
+ Changed to use LPTSTR etc.
+ 09-Feb-1992 JohnRo
+ Set up to dynamically change role.
+ Use FORMAT equates.
+ 15-Feb-1992 JohnRo
+ Added a little debug output.
+ 22-Feb-1992 JohnRo
+ Mention mailslot name when _open of it fails.
+ PC-LINT found a bug.
+ Made other changes suggested by PC-LINT.
+ 05-Mar-1992 JohnRo
+ Changed ReplMain's interface to match new service controller.
+ 06-Mar-1992 JohnRo
+ Avoid starting RPC server too soon.
+ 24-Mar-1992 JohnRo
+ Renamed many ReplGlobal vars to ReplConfig vars.
+ Added more comments about which thread is which.
+ Fixed bug where startup event wasn't being set.
+ Got rid of useless master and client termination codes.
+ 01-Apr-1992 JohnRo
+ Avoid assertion if import startup fails.
+ Improve error code if wksta not started.
+ CliffV told me about overlapped I/O vs. termination events.
+ Got rid of last use of NT-specific stuff (RtlZeroMemory).
+ 21-Aug-1992 JohnRo
+ RAID 3607: REPLLOCK.RP$ is being created during tree copy.
+ Use PREFIX_ equates.
+ 25-Sep-1992 JohnRo
+ RAID 5494: repl svc does not maintain time stamp on import startup.
+ 22-Oct-1992 jimkel
+ Add ClientInitImpList() to init and canon the list of machines
+ and domains that the importer will accept replications for.
+ ClinetInitImpList() is called from ReplClientInitialze().
+ Also, added a shared lock in NameOfValidMaster()
+ 25-Oct-1992 jimkel
+ Rename masters_count and master_list[] to RCGlobalExportCount
+ and RCGlobalExportList[]
+ 18-Nov-1992 JohnRo
+ RAID 1537: repl APIs in wrong role kill service.
+ More debug code...
+ 03-Dec-1992 JohnRo
+ RAID 4526: Repl svc has rare memory leak (changing role).
+ 16-Dec-1992 JohnRo
+ RAID 1513: Repl does not maintain ACLs (also fix timestamps).
+ Made changes suggested by PC-LINT 5.0
+ 05-Jan-1993 JohnRo
+ RAID 6763: Repl WAN support (get rid of repl name list limits).
+ 17-Feb-1993 JohnRo
+ RAID 11365: Fixed various mailslot size problems.
+ Minor debug output changes here and there.
+ One more change suggested by PC-LINT 5.0
+ 26-Mar-1993 JohnRo
+ RAID 4267: Replicator has problems when work queue gets large.
+ Actually _close the mailslot handle on exit.
+ Added yet another assert.
+ 30-Mar-1993 JohnRo
+ Reduce checksum frequency by using change notify on import tree.
+ 11-May-1993 JohnRo
+ RAID 895: Repl error 298 (ERROR_TOO_MANY_POSTS).
+ 24-May-1993 JohnRo
+ RAID 10587: repl could deadlock with changed NetpStopRpcServer(), so
+ just call ExitProcess() instead.
+ Made changes suggested by PC-LINT 5.0
+ 22-Nov-1994 JonN
+ RAID 27287: Memory leak in Replicator (LMREPL.EXE)
+ ReplEnableChangeNotify is being called even though there is still
+ an outstanding notify request on ChangeHandle, this causes multiple
+ requests to build up and leaks paged pool. Added boolean
+ NotifyPending to keep this at only a single request at a time.
+
+--*/
+
+
+#include <nt.h> // NT definitions (needed by chngnot.h)
+#include <ntrtl.h> // NT runtime library definitions (needed by nturtl.h)
+#include <nturtl.h> // Needed to have windows.h and nt.h co-exist.
+#define NOMINMAX // Let stdlib.h define min and max
+#include <windows.h> // DWORD, ReadFile(), etc.
+#include <lmcons.h> // NET_API_STATUS, IN, OUT, etc.
+
+#include <alertmsg.h> // ALERT_* defines
+#include <chngnot.h> // ReplSetupChangeNotify, REPL_CHANGE_NOTIFY_HANDLE, etc
+#include <confname.h> // REPL_KEYWORD_ equates.
+#include <icanon.h> // I_NetPathCompare
+#include <limits.h> // LONG_MAX.
+#include <lmerr.h> // NO_ERROR, ERROR_, NERR_ equates.
+#include <lmerrlog.h> // NELOG_* defines
+#include <lmsname.h> // SERVICE_WORKSTATION.
+#include <netdebug.h> // DBGSTATIC ..
+#include <netlib.h> // NetpMemoryAllocate(), NetpIsServerStarted().
+#include <netlock.h> // Lock data types, functions, and macros.
+#include <prefix.h> // PREFIX_ equates.
+#include <replp.h> // NetpReplTimeNow().
+#include <stdlib.h> // rand()
+#include <string.h> // memset().
+#include <thread.h> // FORMAT_NET_THREAD_ID, NetpCurrentThread().
+#include <tstr.h> // STRLEN(), etc.
+#include <winsvc.h> // SERVICE_ equates, etc.
+
+//
+// Local include files
+//
+#include <repldefs.h> // IF_DEBUG(), etc.
+#include <replgbl.h> // ReplGlobal and ReplConfig variables.
+#include <repllock.h> // LOCK_LEVEL equates.
+
+#define CLIENT_ALLOCATE // Allocate storage for all client global variables
+
+#include <client.h>
+
+//
+// Global Data Declarations:
+//
+
+DBGSTATIC HANDLE ReplGlobalClientMailslotHandle = (HANDLE)(-1);
+
+
+
+DBGSTATIC VOID
+ReplClientCleanup(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ Takes care of graceful termination.
+ Gets rid of all resources owned.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ HANDLE WaitHandles[2] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE};
+ DWORD HandleCount = 0;
+
+ //
+ // Tell the syncer and watchd threads that they should stop.
+ //
+
+ (VOID) SetEvent( ReplGlobalClientTerminateEvent );
+
+ //
+ // Wait for all our child threads to complete.
+ //
+
+ if ( RCGlobalSyncerThread != NULL ) {
+
+ WaitHandles[HandleCount++] = RCGlobalSyncerThread;
+
+ }
+
+ if ( RCGlobalWatchdThread != NULL ) {
+
+ WaitHandles[HandleCount++] = RCGlobalWatchdThread;
+
+ }
+
+ if ( HandleCount != 0 ) {
+
+ (VOID) WaitForMultipleObjects( HandleCount, WaitHandles, TRUE,
+ (DWORD) -1 );
+
+ }
+
+
+ // close thread handles
+
+ if ( RCGlobalSyncerThread != NULL ) {
+
+ (void) CloseHandle( RCGlobalSyncerThread );
+
+ }
+
+ if ( RCGlobalWatchdThread != NULL ) {
+
+ (void) CloseHandle( RCGlobalWatchdThread );
+
+ }
+
+ //
+ // Close the mailslot handle
+ //
+
+ if ( ReplGlobalClientMailslotHandle != (HANDLE)(-1) ) {
+
+ (VOID) CloseHandle( ReplGlobalClientMailslotHandle );
+
+ }
+
+
+ if ( RCGlobalWorkQueueSemaphore != INVALID_HANDLE_VALUE ) {
+
+ (VOID) CloseHandle( RCGlobalWorkQueueSemaphore );
+
+ }
+
+ // clean export list memory
+
+ if ( RCGlobalExportCount != 0 ) {
+
+ NetpAssert( (RCGlobalExportList[0]) != NULL );
+ NetpMemoryFree( RCGlobalExportList[0] );
+
+ }
+
+ if( RCGlobalLockInitialized ) {
+
+ NetpDeleteLock( RCGlobalWorkQueueLock );
+ NetpDeleteLock( RCGlobalPoolLock );
+ NetpDeleteLock( RCGlobalDelayListLock );
+
+ }
+
+ // free pool resource
+ ReplClientFreePools();
+
+}
+
+
+
+DBGSTATIC DWORD
+Randomize(
+ IN DWORD limit
+ )
+/*++
+
+Routine Description:
+
+ Returns a random # in the range 0 - limit.
+
+Arguments:
+
+ limit - maximum value to return
+
+Return Value:
+
+ Returns a random # in the range 0 - limit.
+
+
+--*/
+{
+ DWORD j;
+
+ if ((limit) && (j = ( ((DWORD)rand()) % limit))) {
+ return j;
+ } else {
+ return 0;
+ }
+}
+
+
+
+DBGSTATIC BOOL
+NameOfValidMaster(
+ IN LPTSTR sender,
+ IN LPTSTR domain
+ )
+/*++
+
+Routine Description:
+
+ Checks if incoming message should be accepted.
+
+Arguments:
+
+ sender - sender's computer name
+
+ domain - sender's domain name.
+
+Return Value:
+
+ TRUE - Message should be accepted.
+
+ FALSE - Message should not be accepted.
+
+Threads:
+
+ Only called by client thread.
+
+--*/
+{
+ DWORD i;
+
+ //
+ // If a list of masters was configured,
+ // allow this master if either the sender name or domain name is on
+ // the list.
+ //
+
+ ACQUIRE_LOCK_SHARED( ReplConfigLock ); // get shared lock on list
+
+ if ( RCGlobalExportCount != 0 ) {
+
+ for (i = 0; i < RCGlobalExportCount; i++) {
+
+ if ( ReplNetNameCompare(NULL,
+ sender,
+ RCGlobalExportList[i],
+ NAMETYPE_COMPUTER,
+ 0L
+ ) == 0 ||
+ ReplNetNameCompare(NULL,
+ domain,
+ RCGlobalExportList[i],
+ NAMETYPE_DOMAIN,
+ 0L) == 0) {
+
+ // Name found, so release lock and exit
+ RELEASE_LOCK( ReplConfigLock );
+ return TRUE;
+ }
+
+ }
+
+ //
+ // If no list was configured, just allow masters from our primary domain.
+ //
+
+ } else if ( ReplNetNameCompare(NULL,
+ ReplGlobalDomainName,
+ domain,
+ NAMETYPE_DOMAIN,
+ 0L
+ ) == 0 ) {
+
+ // name found release lock and exit
+ RELEASE_LOCK( ReplConfigLock );
+ return TRUE;
+ }
+
+ RELEASE_LOCK( ReplConfigLock ); // name not found release lock and exit
+ return FALSE;
+
+}
+
+
+DBGSTATIC NET_API_STATUS
+ReplClientInitialize(
+ IN BOOL ServiceIsStarting
+ )
+/*++
+
+Routine Description:
+
+ Initialization for the REPL service client.
+
+Arguments:
+
+ ServiceIsStarting - TRUE iff we're still starting the service.
+
+Return Value:
+
+ return NO_ERROR if initialization is successful.
+ error code otherwise
+
+Threads:
+
+ Only called by client thread.
+
+--*/
+{
+ SYSTEMTIME time;
+ DWORD ThreadId;
+
+ NET_API_STATUS NetStatus;
+ DWORD State;
+ TCHAR MailslotName[FULL_SLOT_NAME_SIZE];
+
+ IF_DEBUG( REPL ) {
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplClientInitialize: thread ID is "
+ FORMAT_NET_THREAD_ID ".\n", NetpCurrentThread() ));
+ }
+
+ if (ServiceIsStarting) {
+ State = SERVICE_START_PENDING;
+ } else {
+ State = SERVICE_RUNNING;
+ }
+
+ //
+ // Initialize all globals to their initial value.
+ // This must be done at runtime since this address space can
+ // be shared by other services.
+ //
+
+ RCGlobalClientListHeader = NULL;
+ RCGlobalClientListCount = 0;
+ RCGlobalWorkQueueSemaphore = NULL;
+ RCGlobalWorkQueueHead = NULL;
+ RCGlobalWorkQueueTail = NULL;
+ RCGlobalSyncerThread = NULL;
+ RCGlobalWatchdThread = NULL;
+ RCGlobalDelayListHeader = NULL;
+ RCGlobalTimeOfLastChangeNotify = NetpReplTimeNow();
+
+ RCGlobalLockInitialized = FALSE;
+
+ RCGlobalExportCount = 0;
+
+ //
+ // Initialize the state of the service.
+ //
+
+ ReportStatus(
+ State,
+ NO_ERROR, // exit code
+ REPL_WAIT_HINT,
+ 21 ); // check point
+
+ //
+ // Creates Client mailslot - where Client receives Masters messages.
+ //
+ // Creates mailslot \MAILSLOT\NET\REPL_CLI,
+ //
+ // If a one exists with the same name
+ // or any other system NetStatus, Exits.
+ //
+
+ (void) STRCPY( MailslotName, SLASH_SLASH );
+ (void) STRCAT( MailslotName, DOT );
+ (void) STRCAT( MailslotName, (LPTSTR) CLIENT_SLOT_NAME );
+
+ IF_DEBUG( CLIENT ) {
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplClientInitialize: opening " FORMAT_LPTSTR ".\n",
+ MailslotName ));
+ }
+
+
+ ReplGlobalClientMailslotHandle = CreateMailslot(
+ MailslotName,
+ MAX_2_MSLOT_SIZE,
+ (DWORD) MAILSLOT_WAIT_FOREVER, // readtimeout
+ NULL ); // security attributes
+
+ if ( ReplGlobalClientMailslotHandle == (HANDLE)(-1) ) {
+
+ // Find out why mailslot failed. Perhaps wksta not started?
+ if ( !NetpIsServiceStarted( (LPTSTR) SERVICE_WORKSTATION ) ) {
+ NetStatus = NERR_WkstaNotStarted;
+ } else {
+ NetStatus = (NET_API_STATUS) GetLastError();
+ }
+ NetpAssert( NetStatus != NO_ERROR );
+ ReplFinish( NetStatus );
+
+ return NetStatus;
+ }
+
+ ReportStatus(
+ State,
+ NO_ERROR,
+ REPL_WAIT_HINT,
+ 22 ); // checkpoint
+
+
+ //
+ // Creates Work Queue - written by ClientMain and Watchd, read by Syncer.
+ //
+
+ // Initialize all locks at one place. See ReplLock.h and NetLock.h for
+ // details.
+
+ RCGlobalWorkQueueLock = NetpCreateLock(
+ WORK_QUEUE_LOCK_LEVEL,
+ (LPTSTR) TEXT("work queue") );
+ NetpAssert( RCGlobalWorkQueueLock != NULL );
+
+ RCGlobalPoolLock = NetpCreateLock(
+ POOL_LOCK_LEVEL,
+ (LPTSTR) TEXT("pool") );
+ NetpAssert( RCGlobalPoolLock != NULL );
+
+ RCGlobalDelayListLock = NetpCreateLock(
+ DELAY_LIST_LOCK_LEVEL,
+ (LPTSTR) TEXT("delay list") );
+ NetpAssert( RCGlobalDelayListLock != NULL );
+
+ RCGlobalLockInitialized = TRUE;
+
+ RCGlobalWorkQueueSemaphore = CreateSemaphoreW(
+ NULL, // No security attributes
+ 0, // Initially no entries in queue
+ LONG_MAX, // Allow queue to be big enough
+ NULL ); // No name
+
+ if ( RCGlobalWorkQueueSemaphore == NULL ) {
+ NetStatus = GetLastError();
+ NetpAssert( NetStatus != NO_ERROR );
+ ReplFinish( NetStatus );
+ return NetStatus;
+ }
+
+ ReportStatus(
+ State,
+ NO_ERROR,
+ REPL_WAIT_HINT,
+ 23 ); // checkpoint
+
+
+ //
+ // Initialize the Delay List
+ //
+
+ RCGlobalDelayListHeader = NULL;
+
+ ReportStatus(
+ State,
+ NO_ERROR,
+ REPL_WAIT_HINT,
+ 24 ); // checkpoint
+
+ //
+ // Initialize the client importer list. The list of
+ // machines and domains that the client will accept import
+ // messages from.
+ //
+
+
+ // NOTE: ReplInitAnyList() assumes caller has config data locked.
+ ACQUIRE_LOCK_SHARED( ReplConfigLock );
+ NetStatus = ReplInitAnyList(
+ (LPCTSTR) ReplConfigImportList, // uncanon
+ & RCGlobalExportList, // canon list: alloc and set ptr
+ (LPCTSTR) REPL_KEYWORD_IMPLIST, // config keyword name
+ & RCGlobalExportCount ); // set entry count too
+ RELEASE_LOCK( ReplConfigLock );
+
+ if (NetStatus != NO_ERROR) {
+
+ return (NetStatus);
+
+ }
+
+ //
+ // Init random number generator and file system resolution.
+ //
+
+ GetSystemTime( &time );
+ srand( time.wMilliseconds );
+
+ RCGlobalFsTimeResolutionSecs =
+ ReplGetFsTimeResolutionSecs( ReplConfigImportPath );
+
+ ReportStatus(
+ State,
+ NO_ERROR,
+ REPL_WAIT_HINT,
+ 25 ); // checkpoint
+
+
+ //
+ // Clean up following crash in the middle of a sync.
+ //
+
+ NetStatus = ReplCrashRecovery();
+ if ( NetStatus != NO_ERROR ) {
+ // ReplCrashRecovery has already called ReplFinish.
+ return (NetStatus);
+ }
+
+ ReportStatus(
+ State,
+ NO_ERROR,
+ REPL_WAIT_HINT,
+ 26 ); // checkpoint
+
+
+ //
+ // ReplClientInitLists allocates buffers and sets state to NO MASTER
+ // for each dir under IMPORT. This must be done after calling
+ // ReplCrashRecovery, so we get state for the recovered directory
+ // intead of TMPTREE[X].RP$
+ //
+
+ NetStatus = ReplClientInitLists();
+ if (NetStatus != NO_ERROR) {
+ ReplFinish( NetStatus );
+ return (NetStatus);
+ }
+
+ ReportStatus(
+ State,
+ NO_ERROR,
+ REPL_WAIT_HINT,
+ 27 ); // checkpoint
+
+
+ //
+ // Creates Syncer thread.
+ //
+
+ RCGlobalSyncerThread = CreateThread( NULL, // No Security
+ CLIENT_SYNCER_STACK_SIZE,
+ ReplSyncerThread,
+ NULL,
+ 0, // No creation flags
+ &ThreadId );
+
+ if ( RCGlobalSyncerThread == NULL ) {
+
+ NetStatus = GetLastError();
+ NetpAssert( NetStatus != NO_ERROR );
+ ReplFinish( NetStatus );
+ return NetStatus;
+ }
+
+ ReportStatus(
+ State,
+ NO_ERROR,
+ REPL_WAIT_HINT,
+ 28 ); // checkpoint
+
+
+
+ //
+ // Creates Watchdog thread.
+ //
+
+ RCGlobalWatchdThread = CreateThread( NULL, // No Security
+ CLIENT_WATCHD_STACK_SIZE,
+ ReplWatchdThread,
+ NULL,
+ 0, // No creation flags
+ &ThreadId );
+
+ if ( RCGlobalWatchdThread == NULL ) {
+
+ NetStatus = GetLastError();
+ NetpAssert( NetStatus != NO_ERROR );
+ ReplFinish( NetStatus );
+ return NetStatus;
+ }
+
+ ReportStatus(
+ State,
+ NO_ERROR,
+ REPL_WAIT_HINT,
+ 29 ); // checkpoint
+
+
+ return NO_ERROR;
+}
+
+
+
+DWORD
+ReplClientMain(
+ IN LPVOID parm
+ )
+/*++
+
+Routine Description:
+
+ Main entry point for the Replicator Client.
+
+Arguments:
+
+ parm - NULL iff service is starting (non-NULL if role is changing).
+
+Return Value:
+
+ exit error.
+
+Threads:
+
+ Only called by client thread.
+
+--*/
+{
+ NET_API_STATUS ApiStatus;
+ LPREPL_CHANGE_NOTIFY_HANDLE ChangeHandle = NULL;
+ BOOL NotifyPending = FALSE;
+ BOOL ReadPending = FALSE;
+ DWORD EventTriggered;
+ BOOL ServiceIsStarting;
+ HANDLE WaitHandles[3];
+
+ ServiceIsStarting = (parm == NULL);
+
+ //
+ // Perform all Client side initialization.
+ //
+
+ ApiStatus = ReplClientInitialize(ServiceIsStarting);
+ if (ApiStatus != NO_ERROR) {
+
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplClientMain: ReplClientInitialize failed, stat="
+ FORMAT_API_STATUS ".\n", ApiStatus ));
+
+ goto Cleanup; // Don't forget to close handles.
+ }
+
+ //
+ // Arrange to use change notify on import path.
+ //
+ ApiStatus = ReplSetupChangeNotify(
+ ReplConfigImportPath,
+ &ChangeHandle );
+ if (ApiStatus != NO_ERROR) {
+ goto Cleanup;
+ }
+ NetpAssert( ChangeHandle != NULL );
+
+
+#if DBG
+ RCGlobalClientThreadInit = TRUE; // only used by ReplTest stuff
+#endif
+
+ // Tell ReplChangeRole/ImportDirStartRepl that the client is done with setup
+
+ IF_DEBUG( CLIENT ) {
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplClientMain----------> setting startup event\n" ));
+ }
+
+ if( !SetEvent(ReplGlobalImportStartupEvent) ) {
+
+ ApiStatus = (NET_API_STATUS) GetLastError();
+ NetpAssert( ApiStatus != NO_ERROR );
+ NetpAssert( ApiStatus != ERROR_INVALID_FUNCTION );
+ AlertLogExit(ALERT_ReplSysErr,
+ NELOG_ReplSysErr,
+ ApiStatus,
+ NULL,
+ NULL,
+ EXIT);
+
+ goto Cleanup; // Don't forget to close handles.
+ }
+
+
+ WaitHandles[0] = ReplGlobalClientTerminateEvent ;
+ WaitHandles[1] = ReplGlobalClientMailslotHandle;
+ WaitHandles[2] = ChangeHandle->WaitableHandle;
+
+ //
+ // Loop forever reading the ClientMailslot and waiting terminate event.
+ //
+
+ for (;;) {
+
+ DWORD bytes_read;
+ DWORD bytes_read2;
+ BYTE msg_buf[ MAX_2_MSLOT_SIZE ];
+ DWORD msg_buf2[
+ (MAX_REPL_MAILSLOT_EXPANSION * MAX_2_MSLOT_SIZE / sizeof(DWORD))
+ + 1
+ ];
+ // align to DWORD boundary
+
+ PSYNCMSG msg;
+
+ PBIGBUF_REC que_buf;
+ OVERLAPPED OverLapped;
+
+ // Enable this pass of change notify...
+
+ if (!NotifyPending) {
+ ApiStatus = ReplEnableChangeNotify( ChangeHandle );
+ if (ApiStatus != NO_ERROR) {
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplClientMain got " FORMAT_API_STATUS
+ " from ReplEnableChangeNotify.\n", ApiStatus ));
+ NetpAssert( FALSE ); // enable can't fail!
+ goto Cleanup;
+ }
+ NotifyPending = TRUE;
+ }
+
+ // make an asynchronous read request.
+
+ bytes_read = bytes_read2 = 0;
+
+ (VOID) memset( &OverLapped, '\0', sizeof(OverLapped) );
+ OverLapped.Offset = 0;
+
+ if ( (!ReadPending) &&
+ !ReadFile( ReplGlobalClientMailslotHandle,
+ msg_buf,
+ sizeof( msg_buf ),
+ &bytes_read,
+ &OverLapped )) { // Overlapped I/O
+
+ ApiStatus = (NET_API_STATUS) GetLastError();
+
+ if( ApiStatus != ERROR_IO_PENDING ) {
+
+ NetpAssert( ApiStatus != NO_ERROR );
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplClientMain: ReadFile (mailslot) error "
+ FORMAT_API_STATUS ".\n", ApiStatus ));
+
+ AlertLogExit( ALERT_ReplNetErr,
+ NELOG_ReplNetErr,
+ ApiStatus,
+ NULL,
+ NULL,
+ EXIT);
+
+ break;
+
+ }
+ else
+ {
+ ReadPending = TRUE;
+ }
+
+ }
+
+
+ // wait on multiple events ..
+ EventTriggered = WaitForMultipleObjects(
+ 3, // number of entries in array
+ WaitHandles, // array of handles
+ FALSE,
+ (DWORD) -1 );
+
+ if(EventTriggered == 0) { // ReplGlobalClientTerminateEvent
+ // terminate event
+
+ ApiStatus = NO_ERROR;
+ goto Cleanup; // Don't forget to close handles.
+
+ } else if(EventTriggered == 1) { // ReplGlobalClientMailslotHandle
+ // mailslot event
+
+ // get bytes_read info
+
+ ReadPending = FALSE; // GetOverlappedResult will complete IRP
+ if( !GetOverlappedResult( ReplGlobalClientMailslotHandle,
+ &OverLapped,
+ &bytes_read,
+ TRUE ) ) {
+
+ ApiStatus = GetLastError();
+
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "Repl client got error "
+ FORMAT_API_STATUS " reading mailslot, "
+ "buf size=" FORMAT_DWORD ".\n",
+ ApiStatus, MAX_2_MSLOT_SIZE ));
+
+ NetpAssert( ApiStatus != NO_ERROR );
+
+ // error read mailslot
+ AlertLogExit( ALERT_ReplNetErr,
+ NELOG_ReplNetErr,
+ ApiStatus,
+ NULL,
+ NULL,
+ NO_EXIT);
+ continue;
+ }
+
+ if(bytes_read == 0) {
+
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "Received an empty mail.\n" ));
+
+ continue;
+ }
+
+ IF_DEBUG( CLIENT ) {
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "Received mailslot msg, size=" FORMAT_DWORD ":\n",
+ bytes_read ));
+ NetpDbgHexDump( msg_buf, NetpDbgReasonable( bytes_read ));
+ }
+
+
+ //
+ // Unmarshall message to make it internal format
+ //
+
+ if( (ApiStatus = ReplUnmarshallMessage(msg_buf,
+ bytes_read,
+ (LPBYTE) msg_buf2,
+ MAX_REPL_MAILSLOT_EXPANSION * MAX_2_MSLOT_SIZE,
+ &bytes_read2)) != NO_ERROR ) {
+
+ // the unmarshall routine will fail only iff the system doesn't
+ // have sufficient memory or the message is bogus ...
+
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "Invalid mailslot message received, stat="
+ FORMAT_API_STATUS "\n", ApiStatus ));
+
+ AlertLogExit( ALERT_ReplNetErr,
+ NELOG_ReplNetErr,
+ ApiStatus,
+ NULL,
+ NULL,
+ NO_EXIT);
+
+ continue;
+ }
+
+ msg = (PSYNCMSG) (LPVOID) msg_buf2;
+
+ // successful mailslot read
+
+ //
+ // check if it is an expected sender.
+ //
+
+ if (!NameOfValidMaster(msg->header.sender,
+ msg->header.senders_domain)){
+ IF_DEBUG(CLIENT) { // debug code
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "Ignoring message received, "
+ "unexpected sender or domain:\n"
+ " sender-> " FORMAT_LPTSTR "\n"
+ " domain-> " FORMAT_LPTSTR "\n",
+ msg->header.sender,
+ msg->header.senders_domain ));
+ //NetpDbgHexDump( msg_buf, NetpDbgReasonable( bytes_read ));
+ }
+ continue;
+
+
+ }
+
+ IF_DEBUG( MAJOR ) { // debug code
+
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "Message received, type: "
+ FORMAT_DWORD ", from: " FORMAT_LPTSTR "\n",
+ msg->header.msg_type,
+ msg->header.sender ));
+ }
+
+
+ //
+
+ switch (msg->header.msg_type) {
+
+ case DIR_SUPPORTED: /*FALLTHROUGH*/
+ case DIR_NOT_SUPPORTED: /*FALLTHROUGH*/
+ case MASTER_DIR: /*FALLTHROUGH*/
+ case NOT_MASTER_DIR:
+
+ //
+ // Handle this query (handshake) message right now.
+ // This might involve updating structures, adding another
+ // timeout message, and/or sending a message to one or more
+ // masters.
+ //
+ // Note: This code used to pass the handshake messages off
+ // to the syncer thread. As we began to use the replicator
+ // for over a gigabyte, this introduced delays which made it
+ // seem like the master was timed-out when it was not. So,
+ // we process handshake messages right away instead.
+ //
+
+ ReplClientRespondToQueryMsg( (LPVOID) msg );
+
+ break;
+
+ //
+ // Handle the message type which simply gets posted to the
+ // syncer thread. No delay is needed for these messages.
+ //
+ // See the ReplSyncerThread procedure for comments on where these
+ // message types came from.
+ //
+ case PULSE_MSG:
+
+ //
+ // Copy msg into a buffer so it can be put in que and msg_buf
+ // freed for next message.
+ //
+
+ if ((que_buf = ReplClientGetPoolEntry( QUEBIG_POOL )) == NULL) {
+
+ //
+ // out of memory
+ //
+
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "Can't get client pool entry, "
+ "pool out-of-memory, message type " FORMAT_HEX_DWORD
+ "\n", msg->header.msg_type ));
+
+ break;
+ }
+
+ NetpMoveMemory( que_buf->data, msg_buf2, bytes_read2 );
+
+ //
+ // Insert the message into the Work Queue.
+ //
+
+ ReplInsertWorkQueue( que_buf );
+
+ IF_DEBUG(CLIENT) {
+
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "Message is queued in work queue\n" ));
+
+ }
+
+ break;
+
+
+
+ //
+ // Master's update message,
+ //
+ //
+ // To avoid having all clients send requests (consequence of an
+ // update) to Master at the same time, each client waits a random
+ // time (in the range [0..RANDOM]) before handling the update.
+ // Delay time is stored in delay field of the mesaage and put on
+ // the special delay list, managed by the WatchDog
+ // thread. When time's up WatchDog gets the message out of delay
+ // list and writes it into the Work Queue for Syncer thread to do
+ // the actual work (update).
+ //
+
+ case SYNC_MSG:
+ case GUARD_MSG:
+
+ //
+ // Copy into own buffer -> free mailsot buffer.
+ //
+
+ if ((que_buf = ReplClientGetPoolEntry( QUEBIG_POOL )) == NULL) {
+
+ //
+ // out of memory.
+ //
+
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "Can't get client pool entry, "
+ "pool out-of-memory, message type "
+ FORMAT_HEX_DWORD "\n", msg->header.msg_type ));
+
+ break;
+ }
+
+ NetpMoveMemory( que_buf->data, msg_buf2, bytes_read2 );
+
+ //
+ // Get random delay (in units of 10 seconds).
+ //
+
+ if (!(que_buf->delay = Randomize(msg->info.random) / 10)) {
+ que_buf->delay = 1;
+ }
+
+ //
+ // Put on delay linked list.
+ //
+
+ ACQUIRE_LOCK( RCGlobalDelayListLock );
+
+ que_buf->next_p = RCGlobalDelayListHeader;
+ RCGlobalDelayListHeader = que_buf;
+
+ RELEASE_LOCK( RCGlobalDelayListLock );
+
+ IF_DEBUG(CLIENT) {
+
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "Message is queued in delay queue\n" ));
+
+ }
+
+ break;
+
+
+ //
+ // All other message types are ignored.
+ //
+
+ default:
+
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "Bad Message, Type = " FORMAT_HEX_DWORD
+ "\n", msg->header.msg_type ));
+
+ AlertLogExit(0, NELOG_ReplBadMsg, 0, NULL, NULL, NO_EXIT);
+
+ }
+
+ } else if (EventTriggered == 2) { // ChangeHandle->WaitableHandle
+
+ NotifyPending = FALSE;
+
+ ACQUIRE_LOCK( RCGlobalClientListLock );
+
+ IF_DEBUG( SYNC ) {
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "Change of import tree detected...\n" ));
+ }
+
+ RCGlobalTimeOfLastChangeNotify = NetpReplTimeNow();
+
+ RELEASE_LOCK( RCGlobalClientListLock );
+
+ } else {
+
+ // error on WaitForMultipleObjects
+
+ ApiStatus = (NET_API_STATUS) GetLastError();
+ NetpAssert( ApiStatus != NO_ERROR );
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "WaitForMultipleObjects() in error, "
+ "EventTriggered = " FORMAT_DWORD ", ",
+ "ApiStatus = " FORMAT_API_STATUS ".\n",
+ EventTriggered, ApiStatus ));
+
+ AlertLogExit( ALERT_ReplNetErr,
+ NELOG_ReplNetErr,
+ ApiStatus,
+ NULL,
+ NULL,
+ EXIT);
+
+ goto Cleanup; // Don't forget to close handles.
+ }
+
+ }
+
+Cleanup:
+
+ if (ChangeHandle != NULL) {
+ (VOID) ReplCloseChangeNotify( ChangeHandle );
+ }
+
+ if ( ReplGlobalClientMailslotHandle != (HANDLE)(-1) ) {
+
+ // Prevent stale use of handle (overlapped use of ex-stack space).
+ (VOID) CloseHandle( ReplGlobalClientMailslotHandle );
+
+ ReplGlobalClientMailslotHandle = (HANDLE)(-1);
+ }
+
+ ReplClientCleanup();
+
+ if (ApiStatus != NO_ERROR) {
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplClientMain: exiting, status " FORMAT_API_STATUS
+ ", thread ID " FORMAT_NET_THREAD_ID ".\n",
+ ApiStatus, NetpCurrentThread() ));
+
+ ReplFinish( ApiStatus );
+ /*NOTREACHED*/
+ }
+
+ return ((DWORD) ApiStatus);
+
+} // ReplClientMain
diff --git a/private/net/svcdlls/repl/server/client.h b/private/net/svcdlls/repl/server/client.h
new file mode 100644
index 000000000..ed68b3392
--- /dev/null
+++ b/private/net/svcdlls/repl/server/client.h
@@ -0,0 +1,604 @@
+/*++
+
+Copyright (c) 1987-1993 Microsoft Corporation
+
+Module Name:
+
+ client.h
+
+Abstract:
+
+ Constants, structures, and globals for the client side.
+
+Author:
+
+ Ported from Lan Man 2.1
+
+Environment:
+
+ User mode only.
+ Contains NT-specific code.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+ 23-Oct-1991 (cliffv)
+ Ported to NT. Converted to NT style.
+ 13-Dec-1991 JohnRo
+ Avoid nonstandard dollar sign in C source code.
+ 20-Jan-1992 JohnRo
+ More changes suggested by PC-LINT.
+ Added global flag for ReplTest use.
+ Changed to use NetLock.h (allow shared locks, for one thing).
+ Added lockcount and time_of_first_lock fields to client record.
+ Use REPL_STATE_ equates for client_list_rec.state values.
+ Added STATE_LEN to make config stuff easier.
+ Added ReplStateIsValid() macro.
+ Changed _RP equates to be LPTSTR instead of LPWSTR type.
+ Added ReplRemoveClientRecForDirName() for use by NetrReplImportDirDel().
+ Added RCGlobalClientListCount for use by NetrReplImportDirEnum().
+ 27-Jan-1992 JohnRo
+ Moved private REPL_STATE_ equates and ReplIsStateValid() to
+ <repldefs.h>. Ditto the _RP equates and STATE_LEN.
+ ReplInitSetSignalFile() is obsolete.
+ Changed to use LPTSTR etc.
+ 25-Mar-1992 JohnRo
+ Added ReplCopyTree().
+ Removed order dependencies in includes.
+ 27-Mar-1992 JohnRo
+ Clarify ReplAddClientRec() parameters.
+ 18-Aug-1992 JohnRo
+ RAID 2115: repl svc should wait while stopping or changing role.
+ RAID 3607: REPLLOCK.RP$ is being created during tree copy.
+ 25-Sep-1992 JohnRo
+ RAID 5494: repl svc does not maintain time stamp on import startup.
+ 25-Oct-1992 jimkel
+ rename master_list[] and masters_count to RCGlobalExportList and
+ RCGlobalExportCount
+ 16-Dec-1992 JohnRo
+ RAID 1513: Repl does not maintain ACLs (also fix timestamps).
+ Made changes suggested by PC-LINT 5.0
+ Added some IN and OUT keywords.
+ 05-Jan-1993 JohnRo
+ RAID 6763: Repl WAN support (get rid of repl name list limits).
+ 11-Jan-1993 JohnRo
+ RAID 6710: repl cannot manage dir with 2048 files.
+ 15-Feb-1993 JohnRo
+ RAID 11365: Fixed various mailslot size problems.
+ 11-Mar-1993 JohnRo
+ RAID 14144: avoid very long hourglass in repl UI.
+ 24-Mar-1993 JohnRo
+ RAID 4267: Replicator has problems when work queue gets large.
+ 30-Mar-1993 JohnRo
+ Reduce checksum frequency by using change notify on import tree.
+ 10-Jun-1993 JohnRo
+ RAID 13080: Allow repl between different timezones.
+
+--*/
+
+#ifndef _CLIENT_
+#define _CLIENT_
+
+
+// Don't complain about "unneeded" includes of these files:
+/*lint -efile(764,align.h) */
+/*lint -efile(766,align.h) */
+#include <align.h> // ALIGN_WORST.
+#include <netlock.h> // LPNET_LOCK.
+#include <repldefs.h> // MAX_2_MSLOT_SIZE, PSYNCMSG, etc.
+
+
+//
+// C O N S T A N T S - DEFINES
+//
+//
+// Client specific defines
+//
+
+
+#define CLIENT_SYNCER_STACK_SIZE 12268 // 12K
+#define CLIENT_WATCHD_STACK_SIZE 4096 // 4K
+
+
+//
+// Downlevel mailslot stuff expands from MAX_2_MSLOT_SIZE (packed) to
+// that times MAX_REPL_MAILSLOT_EXPANSION in native format.
+// This should be largest of:
+//
+// CHAR to TCHAR expansion
+// compiler structure member alignment
+//
+// Be pesimistic (but safe) and assume WORST_CASE alignment on everything.
+//
+#define MAX_REPL_MAILSLOT_EXPANSION ALIGN_WORST
+
+//
+// Watchdog ticks every 10 seconds.
+//
+#define WATCHD_TIMER_PERIOD 10000L
+
+
+#define RP (LPTSTR) TEXT(".RP$")
+#define STAR_RP (LPTSTR) TEXT("*.RP$")
+
+#define TMP_TREE (LPTSTR) TEXT("TMPTREE.RP$")
+#define TMP_TREEX (LPTSTR) TEXT("TMPTREEX.RP$")
+#define TMP_FILE (LPTSTR) TEXT("TMPFILE.RP$")
+#define USER_LOCK (LPTSTR) TEXT("USERLOCK.*")
+#define ULOCK_PREFIX (LPTSTR) TEXT("USERLOCK.")
+#define REPL_LOCK (LPTSTR) TEXT("REPLLOCK.RP$")
+
+
+#define ONE_MINUTE 59U // 1 second less than 1 minute - for boundary cases.
+
+
+//
+// Define PoolNumbers
+//
+#define CLIENT_LIST_POOL 0
+#define QUEBIG_POOL 1
+#define QUESML_POOL 2
+#define DUPLMA_POOL 3
+#define POOL_COUNT 4
+
+#define POOL_MIN_ENTRY_COUNT 2
+
+// CLIENT_LIST_REC -> alerts flags:
+#define SIGNAL_ERROR_ALERT 0x0001
+#define LOST_MASTER_ALERT 0x0002
+#define UPDATE_ERROR_ALERT 0x0004
+#define LOGON_FAILED_ALERT 0x0008
+#define DUPL_MASTER_ALERT 0x0010
+#define ACCESS_DENIED_ALERT 0x0020
+#define USER_LOGGED_ALERT 0x0040
+#define BAD_IMPORT_ALERT 0x0080
+#define USER_CURDIR_ALERT 0x0100
+#define MAX_FILES_ALERT 0x0200
+
+// ReplMultiMaster codes:
+#define DUPL_MASTER_UPDATE 1
+#define DUPL_MASTER_TIMEOUT 2
+#define OLD_MASTER_DONE 3
+#define OLD_MASTER_ACTIVE 4
+
+//
+// The number of times to re-ask our master if we should let a duplicate
+// master be our master.
+//
+#define DUPL_QUERY_RETRY 3
+
+
+
+//
+// CLIENT STRUCTURES:
+//
+
+typedef struct _TIMER_REC {
+ DWORD timeout; // pulse timeout.
+ DWORD type; // types: PULSE_1_TIMEOUT or PULSE_2_TIMEOUT
+ DWORD grd_timeout; // required wait for guard
+ DWORD grd_checksum; // checksum to be rechecked after guard.
+ DWORD grd_count; // file count " " " " "
+} TIMER_REC, *PTIMER_REC;
+
+
+typedef struct _DUPL_MASTERS_REC {
+ TCHAR master[CNLEN+1]; // duplicate master name
+ DWORD count; // count inquiries to original master
+
+ //
+ // to resolve who's the real master
+ //
+
+ DWORD sleep_time; // required sleep time (between queries).
+ struct _DUPL_MASTERS_REC *next_p; // next (dupl) master for this dir.
+} DUPL_MASTERS_REC, *PDUPL_MASTERS_REC;
+
+
+//
+// The REPL client maintains a CLIENT_LIST_REC structure for each replicated
+// directory. They are maintained in a doubly linked list headed at
+// RCGlobalClientListHeader. The linked list is protected by
+// RCGlobalClientListLock.
+//
+typedef struct _CLIENT_LIST_REC {
+ TCHAR dir_name[PATHLEN]; // ASCIIZ dir/tree name.
+ TCHAR master[CNLEN+1]; // master name (without leading backslashes).
+ DWORD integrity; // Integrity: FILE / TREE.
+ DWORD extent; // Extent: TREE / DIRECTORY.
+ DWORD sync_time; // Sync time in seconds.
+ DWORD pulse_time; // Pulse time in seconds.
+ DWORD guard_time; // Guard time in seconds.
+ DWORD rand_time; // The rand limit for updates.
+ DWORD checksum; // f(name, timestamp).
+ DWORD count; // # of files in dir/tree.
+ DWORD timestamp; // date+time of last update.
+ DWORD alerts; // Bit map of alerts raised,
+ // used to prevent repitition of alerts
+
+ DWORD state; // State of tree: REPL_STATE_OK, etc.
+ TIMER_REC timer; // sleep details for WatchdThread.
+ DUPL_MASTERS_REC dupl; // dupl masters details.
+
+ DWORD lockcount; // Number of outstanding locks.
+ DWORD time_of_first_lock; // Time (secs since 1970) of first lock.
+
+ struct _CLIENT_LIST_REC *next_p;
+ struct _CLIENT_LIST_REC *prev_p;
+
+ DWORD est_max_dir_entry_count; // Estimated (may be high) max entries
+ // for any dir in this tree.
+
+} CLIENT_LIST_REC, *PCLIENT_LIST_REC, *LPCLIENT_LIST_REC;
+
+// PUSER STRUCTURES:
+
+typedef struct _BIGBUF_REC {
+
+ struct _BIGBUF_REC *next_p; // Must be the first field
+
+ BYTE data[MAX_REPL_MAILSLOT_EXPANSION * MAX_2_MSLOT_SIZE];
+ // BUGBUG: data must be DWORD aligned; is it?
+ // Downlevel Ansi string message buffer may
+ // expand after unmarshall, to fit unicode
+ // strings. so that data buffer size is
+ // expanded here.
+
+ DWORD delay; // used only for delay_list.
+
+} BIGBUF_REC, *PBIGBUF_REC;
+
+//
+//
+typedef struct _SMLBUF_REC {
+ struct _SMLBUF_REC *next_p; // Must be the first field
+ BYTE data[sizeof(QUERY_MSG)];
+} SMLBUF_REC, *PSMLBUF_REC;
+
+//
+// F U N C T I O N S
+//
+
+
+
+//
+// CacheTim.c
+//
+
+NET_API_STATUS
+ReplFreeTimeCache(
+ VOID
+ );
+
+NET_API_STATUS
+ReplGetTimeCacheValue(
+ IN LPCTSTR UncServerName OPTIONAL, // Must be NULL on exporter
+ OUT LPLONG TimeZoneOffsetSecs // offset (+ for West of GMT, etc).
+ );
+
+NET_API_STATUS
+ReplInitTimeCache(
+ VOID
+ );
+
+
+//
+// client.c
+//
+
+NET_API_STATUS
+InitClientImpList(
+ VOID
+ );
+
+//
+// cli_dupl.c
+//
+
+VOID
+ReplMultiMaster(
+ IN DWORD mode,
+ IN PCLIENT_LIST_REC dir_rec,
+ IN LPTSTR dupl_master
+ );
+
+VOID
+ReplMasterDead(
+ IN PCLIENT_LIST_REC dir_rec
+ );
+
+VOID
+ReplClientSendMessage(
+ IN DWORD type,
+ IN LPTSTR master,
+ IN LPTSTR dir_name
+ );
+
+//
+// cli_list.c
+//
+
+PVOID
+ReplClientGetPoolEntry(
+ IN DWORD PoolNumber
+ );
+
+VOID
+ReplClientFreePoolEntry(
+ IN DWORD PoolNumber,
+ IN PVOID Buffer
+ );
+
+NET_API_STATUS
+ReplClientInitLists(
+ VOID
+ );
+
+PCLIENT_LIST_REC
+ReplAddClientRec(
+ IN LPTSTR DirName,
+ IN PSYNCMSG MasterInfo OPTIONAL,
+ IN PMSG_STATUS_REC DirInfo OPTIONAL
+ );
+
+VOID
+ReplRemoveClientRec(
+ IN OUT PCLIENT_LIST_REC rec
+ );
+
+NET_API_STATUS
+ReplRemoveClientRecForDirName (
+ IN LPTSTR DirName
+ );
+
+PCLIENT_LIST_REC
+ReplGetClientRec(
+ IN LPTSTR dir_name,
+ IN LPTSTR MasterName OPTIONAL
+ );
+
+//
+// CliQuery.c
+//
+
+VOID
+ReplClientRespondToQueryMsg(
+ IN PQUERY_MSG QueryMsg
+ );
+
+//
+// CopyTree.c
+//
+
+NET_API_STATUS
+ReplCopyTree(
+ IN LPTSTR SourcePath,
+ IN LPTSTR DestPath
+ );
+
+//
+// ScanQs.c
+//
+
+BOOL
+ReplScanQueuesForMoreRecentMsg(
+ IN LPCTSTR DirName
+ );
+
+//
+// syncer.c
+//
+
+DWORD
+ReplSyncerThread(
+ IN LPVOID Parameter
+ );
+
+VOID
+ReplSetTimeOut(
+ IN DWORD timeout_type,
+ IN PCLIENT_LIST_REC tree_rec
+ );
+
+//
+// synctree.c
+//
+
+VOID
+ReplSyncTree(
+ IN PMSG_STATUS_REC upd_rec,
+ IN OUT PCLIENT_LIST_REC tree_rec
+ );
+
+BOOL
+ChecksumEqual(
+ IN LPCTSTR UncMasterName,
+ IN LPTSTR TmpPath,
+ IN PMSG_STATUS_REC UpdRec,
+ OUT PCHECKSUM_REC CheckRec
+ );
+
+NET_API_STATUS
+ReplCrashRecovery(
+ VOID
+ );
+
+
+NET_API_STATUS
+ReplDeleteCrashDirRecord(
+ VOID
+ );
+
+//
+// syncmisc.c
+//
+
+VOID
+ReplSetSignalFile(
+ IN OUT PCLIENT_LIST_REC tree_rec,
+ IN DWORD signal
+ );
+
+NET_API_STATUS
+ReplSyncCopy(
+ IN LPTSTR source_path,
+ IN LPTSTR dest_path,
+ IN PCLIENT_LIST_REC tree_rec
+ );
+
+NET_API_STATUS
+ReplFileIntegrityCopy(
+ IN LPTSTR source_path,
+ IN LPTSTR dest_path,
+ IN LPTSTR tmp_path,
+ IN PCLIENT_LIST_REC tree_rec,
+ IN DWORD src_attr,
+ IN DWORD dest_attr
+ );
+
+NET_API_STATUS
+ReplFileIntegrityDel(
+ IN LPTSTR path,
+ IN DWORD attrib,
+ IN PCLIENT_LIST_REC tree_rec
+ );
+
+NET_API_STATUS
+ReplCreateTempDir(
+ IN LPTSTR tmp_path,
+ IN PCLIENT_LIST_REC tree_rec
+ );
+
+NET_API_STATUS
+ReplCreateReplLock(
+ IN OUT PCLIENT_LIST_REC tree_rec
+ );
+
+BOOL
+ReplAnyRemoteFilesOpen(
+ IN LPTSTR path
+ );
+
+NET_API_STATUS
+ReplTreeDelete(
+ IN LPTSTR dir
+ );
+
+VOID
+ReplInsertWorkQueue(
+ IN PBIGBUF_REC Buffer
+ );
+
+PBIGBUF_REC
+ReplGetWorkQueue(
+ OUT PBOOL ClientTerminating
+ );
+
+VOID
+ReplClientFreePools(
+ VOID
+ );
+
+//
+// watchd.c
+//
+
+DWORD
+ReplWatchdThread(
+ IN LPVOID Parameter
+ );
+
+
+
+
+
+
+//
+// E X T E R N A L S
+// *****************
+//
+
+//
+// client.c will #include this file with CLIENT_ALLOCATE defined.
+// That will cause each of these variables to be allocated.
+//
+#ifdef CLIENT_ALLOCATE
+#define EXTERN
+#else
+#define EXTERN extern
+#endif
+
+//
+// List of all directories to be replicated.
+//
+EXTERN LPNET_LOCK RCGlobalClientListLock;
+EXTERN LPNET_LOCK RCGlobalDuplListLock;
+EXTERN PCLIENT_LIST_REC RCGlobalClientListHeader;
+EXTERN DWORD RCGlobalClientListCount;
+
+//
+// Work Queue for the syncer thread.
+//
+EXTERN LPNET_LOCK RCGlobalWorkQueueLock;
+EXTERN HANDLE RCGlobalWorkQueueSemaphore;
+EXTERN PBIGBUF_REC RCGlobalWorkQueueHead;
+EXTERN PBIGBUF_REC RCGlobalWorkQueueTail;
+
+//
+// Generic Pool Globals
+//
+// These globals are only referenced/modified while holding RCGlobalPoolLock.
+//
+// Each pool header is a singly linked list of 'EntrySize' entries. The
+// first few bytes of each entry is used as a pointer to the next entry.
+//
+EXTERN LPNET_LOCK RCGlobalPoolLock;
+EXTERN PUCHAR RCGlobalPoolHeader[POOL_COUNT];
+EXTERN DWORD RCGlobalPoolEntrySize[POOL_COUNT];
+
+// Flag to make sure the locks are initialized before they are deleted.
+EXTERN BOOL RCGlobalLockInitialized;
+
+//
+// Thread handles for the various client threads.
+//
+EXTERN HANDLE RCGlobalSyncerThread;
+EXTERN HANDLE RCGlobalWatchdThread;
+
+//
+// The delay list contains a list of messages which aren't to be processed
+// immediately.
+//
+EXTERN LPNET_LOCK RCGlobalDelayListLock;
+EXTERN PBIGBUF_REC RCGlobalDelayListHeader;
+
+
+EXTERN TCHAR import_path[MAX_PATH];
+EXTERN TCHAR local_username[UNLEN+1];
+
+//
+// A list of computer/domain name of masters we will replicate from.
+//
+EXTERN LPTSTR *RCGlobalExportList; // locked by ReplConfigLock
+EXTERN DWORD RCGlobalExportCount;
+
+#if DBG
+EXTERN BOOL RCGlobalClientThreadInit; // only used by ReplTest stuff
+#endif
+
+//
+// Import file system resolution in seconds. This is rounded up to the
+// next second, and is UNKNOWN_FS_RESOLUTION if the value is unknown.
+//
+EXTERN DWORD RCGlobalFsTimeResolutionSecs; // Locked by ReplConfigLock.
+
+//
+// Time of last change notify on the import tree (in seconds since 1970, GMT).
+//
+EXTERN DWORD RCGlobalTimeOfLastChangeNotify; // Locked by RCGlobalClientListLock
+
+
+#undef EXTERN
+
+
+#endif // _CLIENT_
diff --git a/private/net/svcdlls/repl/server/cliquery.c b/private/net/svcdlls/repl/server/cliquery.c
new file mode 100644
index 000000000..0c12e2122
--- /dev/null
+++ b/private/net/svcdlls/repl/server/cliquery.c
@@ -0,0 +1,217 @@
+/*++
+
+Copyright (c) 1987-1993 Microsoft Corporation
+
+Module Name:
+
+ CliQuery.c
+
+Abstract:
+
+ Contains ReplClientRespondToQueryMsg().
+
+Author:
+
+ Ported from Lan Man 2.1
+
+Environment:
+
+ User mode only.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+ 26-Mar-1993 JohnRo
+ RAID 4267: Replicator has problems when work queue gets large.
+ (Extracted this code from ReplSyncerThread, now called by ClientThread.)
+
+--*/
+
+
+// These must be included first:
+
+#include <windows.h> // DWORD, etc.
+#include <lmcons.h> // NET_API_STATUS (needed by other header files).
+
+// These may be included in any order:
+
+#include <client.h> // My prototype, PCLIENT_LIST_REC, ReplSetTimeOut().
+#include <netdebug.h> // NetpKdPrint(), FORMAT_ equates, etc.
+#include <prefix.h> // PREFIX_ equates.
+#include <repldefs.h> // IF_DEBUG(), PQUERY_MSG, etc.
+
+
+VOID
+ReplClientRespondToQueryMsg(
+ IN PQUERY_MSG QueryMsg
+ )
+/*++
+
+Routine Description:
+
+ ReplClientRespondToQueryMsg is given a query message which the client
+ thread has received. This routine takes care of the message, which
+ may involve updating data structures and/or sending mailslot messages.
+
+Arguments:
+
+ QueryMsg - points to native format query message (DIR_SUPPORTED, etc).
+
+Return Value:
+
+ NONE.
+
+Threads:
+
+ Only called by client thread.
+
+--*/
+{
+ PCLIENT_LIST_REC ClientRecord;
+ DWORD MsgType;
+
+
+ NetpAssert( QueryMsg != NULL );
+
+ MsgType = QueryMsg->header.msg_type;
+
+
+
+ //
+ // Handle DIR_SUPPORTED
+ //
+ // Master still supports directory. This is a reply to IS_SUPPORTED
+ // query, sent after more than pulse * 2 time has passed
+ // without sync/pulse.
+ //
+
+ IF_DEBUG(CLIENT) {
+
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "client received a query message "
+ "(type = " FORMAT_DWORD ").\n", MsgType ));
+
+ }
+
+ switch (MsgType) {
+ case DIR_SUPPORTED:
+
+ //
+ // If we still support this directory from this master,
+ // This puts us back to square 2, since no pulse/update
+ // have been received, status is not OK still
+ //
+
+ ACQUIRE_LOCK_SHARED( RCGlobalClientListLock );
+ if ((ClientRecord = ReplGetClientRec(QueryMsg->dir_name,
+ QueryMsg->header.sender )) != NULL) {
+
+ // Note: ReplSetTimeOut needs shared RCGlobalClientListLock.
+ ReplSetTimeOut(PULSE_1_TIMEOUT, ClientRecord);
+ }
+
+ RELEASE_LOCK( RCGlobalClientListLock );
+ break;
+
+
+ //
+ // Handle DIR-NOT-SUPPORTED
+ //
+ // As above but negative, so assume there was an END_REPL
+ // for this dir but it was missed
+ //
+
+ case DIR_NOT_SUPPORTED:
+
+ //
+ // Make sure repl record exists.
+ //
+
+
+ //
+ // If we still support this directory from this master,
+ // set signal to NO_MASTER.
+ //
+
+ ACQUIRE_LOCK_SHARED( RCGlobalClientListLock );
+ if ((ClientRecord = ReplGetClientRec(QueryMsg->dir_name,
+ QueryMsg->header.sender )) != NULL) {
+
+ // Note: ReplMasterDead needs lock (any kind) on
+ // RCGlobalClientListLock.
+ ReplMasterDead(ClientRecord);
+ }
+ RELEASE_LOCK( RCGlobalClientListLock );
+
+ break;
+
+
+ //
+ // Handle MASTER_DIR.
+ //
+ // A duplicate master sent a pulse for this directory and we queried
+ // our master to see if it is still our master. Our master replied
+ // that it is still the master.
+ //
+
+ case MASTER_DIR:
+
+ //
+ // If we still support this directory from this master,
+ // tell all other potential masters that they are duplicates,
+ // reset the timer to expect a pulse from the master.
+ //
+
+ ACQUIRE_LOCK_SHARED( RCGlobalClientListLock );
+ if ((ClientRecord = ReplGetClientRec(QueryMsg->dir_name,
+ QueryMsg->header.sender )) != NULL) {
+
+ // Note: ReplMultiMaster needs lock (any kind) on
+ // RCGlobalClientListLock.
+ ReplMultiMaster(OLD_MASTER_ACTIVE, ClientRecord,
+ QueryMsg->header.sender);
+
+ // Note: ReplSetTimeOut needs shared RCGlobalClientListLock.
+ ReplSetTimeOut(PULSE_1_TIMEOUT, ClientRecord);
+
+ }
+ RELEASE_LOCK( RCGlobalClientListLock );
+ break;
+
+
+ //
+ // Handle NOT_MASTER_DIR.
+ //
+ // Same as above but master replies it is no longer the master.
+ //
+
+ case NOT_MASTER_DIR:
+
+ //
+ // If we still support this directory from this master,
+ // set signal to NO_MASTER and remove dir from client_list.
+ //
+
+ ACQUIRE_LOCK_SHARED( RCGlobalClientListLock );
+ if ((ClientRecord = ReplGetClientRec(QueryMsg->dir_name,
+ QueryMsg->header.sender )) != NULL) {
+
+ // Note: ReplMultiMaster needs lock (any kind) on
+ // RCGlobalClientListLock.
+ ReplMultiMaster(OLD_MASTER_DONE, ClientRecord,
+ QueryMsg->header.sender);
+ }
+ RELEASE_LOCK( RCGlobalClientListLock );
+
+ break;
+
+
+ default:
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplClientRespondToQueryMsg: got invalid query message type "
+ FORMAT_DWORD ".\n", MsgType ));
+ break;
+
+ }
+
+}
diff --git a/private/net/svcdlls/repl/server/copydir.c b/private/net/svcdlls/repl/server/copydir.c
new file mode 100644
index 000000000..ad89fc907
--- /dev/null
+++ b/private/net/svcdlls/repl/server/copydir.c
@@ -0,0 +1,542 @@
+/*++
+
+Copyright (c) 1992-1993 Microsoft Corporation
+
+Module Name:
+
+ CopyDir.c
+
+Abstract:
+
+ ReplCopyDirectoryItself() copies all of the following for a directory:
+
+ - the directory name (created if doesn't already exist)
+ - the attributes (normal, readonly, etc)
+ - the EAs
+ - the security (including ACLs, owner, group)
+ - the alternate data stream (for NTFS only)
+ - the timestamps (creation, last access, last write)
+
+ Here's how those things are copied, depending on conditional compilation:
+
+ what USE_BACKUP_APIS without backup APIs
+ ---------- -------------------- --------------------
+ name CreateDirectory CreateDirectory
+ attributes SetFileAttributes SetFileAttributes
+ EAs Backup APIs BUGBUG: NOT COPIED!
+ security Backup APIs SetFileSecurity
+ alt.data Backup APIs BUGBUG: NOT COPIED!
+ timestamps ReplCopyJustDateTime ReplCopyJustDateTime
+
+ The _access required for the import tree and export tree are given below.
+ CHANGE=add+_read+_write+traverse.
+ READ=_read+traverse.
+ ALL=add+_read+_write+traverse+setperm+takeowner.
+
+ what USE_BACKUP_APIS without backup APIs
+ ---------- -------------------- --------------------
+ dest needs CHANGE _access ALL _access
+ src needs CHANGE _access CHANGE _access
+
+ NOTE: We need CHANGE _access on src dirs, as we copy those ACLs to dest
+ dirs, which we need to be able to update next time!
+
+Author:
+
+ John Rogers (JohnRo) 11-Aug-1992
+
+Environment:
+
+ User mode only. Uses Win32 APIs.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+ Tab size is set to 4.
+
+Revision History:
+
+ 11-Aug-1992 JohnRo
+ Created for RAID 3288: repl svc should preserve ACLs on copy.
+ 13-Aug-1992 JohnRo
+ Disable GetFileSecurity() until it works for UNC names.
+ 15-Dec-1992 JohnRo
+ RAID 1513: Repl does not maintain ACLs. (Also fix HPFS->FAT timestamp.)
+ 08-Jan-1993 JohnRo
+ RAID 6961: ReplCopyDirectoryItself does not respect bFailIfExists.
+ 06-Apr-1993 JohnRo
+ RAID 1938: Replicator un-ACLs files when not given enough permission.
+ Use NetpKdPrint() where possible.
+ Do some debug output when setting attributes.
+ Prepare for >32 bits someday.
+ 27-Apr-1993 JohnRo
+ RAID 7157: fix setting ACLs, etc., on dirs themselves. Also copy
+ alternate data streams for dirs themselves.
+ 04-Jun-1993 JohnRo
+ RAID 12473: Changed to handle ReplMakeFileSecurity not supported on
+ downlevel exporter.
+
+--*/
+
+
+// These must be included first:
+
+#include <windows.h> // IN, LPTSTR, CreateDirectory(), etc.
+#include <lmcons.h> // NET_API_STATUS, PATHLEN, etc.
+
+// These may be included in any order:
+
+#include <lmerr.h> // NERR_InternalError, NO_ERROR.
+#include <lmerrlog.h> // NELOG_ equates.
+#include <netdebug.h> // NetpKdPrint(), FORMAT_ equates, etc.
+#include <netlib.h> // NetpMemoryAllocate(), etc.
+#include <prefix.h> // PREFIX_ equates.
+#include <repldefs.h> // IF_DEBUG(), my prototype, etc.
+#include <tstr.h> // TCHAR_EOS, STRLEN().
+
+
+#define MY_BACKUP_API_BUFFER_SIZE (4 * 1024)
+
+
+// Yes, we want backup APIs to copy security for us.
+#define MY_PROCESS_SECURITY TRUE
+
+
+NET_API_STATUS
+ReplCopyDirectoryItself(
+ IN LPCTSTR SourcePath,
+ IN LPCTSTR DestPath,
+ IN BOOL bFailIfExists
+ )
+
+/*++
+
+Routine Description:
+
+ ReplCopyDirectoryItself copies everthing contained in a directory,
+ except subdirectories and files. "Everything" includes extended
+ attributes, attributes, timestamps, and security info.
+
+Arguments:
+
+ SourcePath - Points to source directory path, which might be a UNC name.
+
+ DestPath - Points to destination directory path name.
+
+ bFailIfExists - a boolean which is TRUE iff this routine should fail
+ if the destination already exists.
+
+Return Value:
+
+ NET_API_STATUS
+
+Threads:
+
+ Used by client and syncer threads.
+
+--*/
+
+{
+ NET_API_STATUS ApiStatus;
+ BOOL DestExists;
+ PSECURITY_ATTRIBUTES DestSecurityAttr = NULL;
+ DWORD SourceAttributes;
+
+#ifdef USE_BACKUP_APIS
+ DWORD ActualBufferSizeRead = 0;
+ DWORD ActualBufferSizeWritten;
+ LPVOID BackupBuffer = NULL;
+ LPVOID DestContext = NULL;
+ HANDLE DestHandle = INVALID_HANDLE_VALUE;
+ LPVOID SourceContext = NULL;
+ HANDLE SourceHandle = INVALID_HANDLE_VALUE;
+#endif
+
+ //
+ // Check for caller errors.
+ //
+
+ NetpAssert( SourcePath != NULL );
+ NetpAssert( (*SourcePath) != TCHAR_EOS );
+ NetpAssert( STRLEN(SourcePath) <= PATHLEN );
+ NetpAssert( DestPath != NULL );
+ NetpAssert( (*DestPath) != TCHAR_EOS );
+ NetpAssert( STRLEN(DestPath) <= PATHLEN );
+
+ DestExists = ReplFileOrDirExists( DestPath );
+
+ if ( bFailIfExists && DestExists ) {
+
+ ApiStatus = ERROR_ALREADY_EXISTS;
+ goto Cleanup;
+ }
+
+ SourceAttributes = GetFileAttributes( (LPTSTR) SourcePath );
+
+ if ( SourceAttributes == (DWORD) -1 ) {
+
+ //
+ // File doesn't exist, bad syntax, or something along those lines.
+ //
+ ApiStatus = (NET_API_STATUS) GetLastError();
+ NetpAssert( ApiStatus != NO_ERROR );
+ goto Cleanup;
+
+ } else if ((SourceAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) {
+
+ //
+ // Source is really a file. We don't handle this!
+ //
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplCopyDirectoryItself: source is not directory! path: '"
+ FORMAT_LPTSTR "', src attr " FORMAT_HEX_DWORD ".\n",
+ SourcePath, SourceAttributes ));
+ ApiStatus = NERR_InternalError; // bug in caller.
+ goto Cleanup;
+
+ } else {
+
+ //
+ // OK, source is really a directory.
+ // Build security attributes so that directory we create will
+ // have the right security (ACLs, owner, group).
+ //
+ ApiStatus = ReplMakeSecurityAttributes(
+ SourcePath,
+ &DestSecurityAttr ); // alloc and set ptr
+ if (ApiStatus==ERROR_NOT_SUPPORTED) {
+
+ // Just downlevel master, so set default security and continue.
+ DestSecurityAttr = NULL;
+
+ } else if (ApiStatus != NO_ERROR) {
+ goto Cleanup;
+ } else {
+ NetpAssert( ApiStatus == NO_ERROR );
+ NetpAssert( DestSecurityAttr != NULL );
+ }
+
+ if ( !DestExists ) {
+
+ //
+ // At last, we can create the new directory.
+ //
+
+ IF_DEBUG( MAJOR ) {
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplCopyDirectoryItself: creating dir '"
+ FORMAT_LPTSTR "'...\n", DestPath ));
+ }
+
+ if ( !CreateDirectory(
+ (LPTSTR) DestPath,
+ DestSecurityAttr) ) {
+
+ // Create failed.
+ ApiStatus = (NET_API_STATUS) GetLastError();
+ NetpAssert( ApiStatus != NO_ERROR );
+
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplCopyDirectoryItself: CreateDirectory failed "
+ FORMAT_API_STATUS ".\n", ApiStatus ));
+
+ goto Cleanup;
+ }
+
+#ifndef USE_BACKUP_APIS
+
+ } else {
+
+ //
+ // Directory already exists. Use it, and update security for it.
+ //
+
+ LPVOID SecurityDescriptor;
+ if (DestSecurityAttr != NULL) {
+ SecurityDescriptor = DestSecurityAttr->lpSecurityDescriptor;
+ } else {
+ SecurityDescriptor = NULL;
+ }
+
+ if ( !SetFileSecurity(
+ (LPTSTR) DestPath,
+ REPL_SECURITY_TO_COPY, // security info (flags)
+ SecurityDescriptor) ) {
+
+ ApiStatus = (NET_API_STATUS) GetLastError();
+ NetpAssert( ApiStatus != NO_ERROR );
+
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplCopyDirectoryItself: unexpected ret code from "
+ "SetFileSecurity('" FORMAT_LPTSTR "'): "
+ FORMAT_API_STATUS ".\n",
+ DestPath, ApiStatus ));
+
+ goto Cleanup;
+ }
+#endif
+ }
+
+#ifdef USE_BACKUP_APIS
+ //
+ // Open source directory.
+ //
+ SourceHandle = CreateFile(
+ SourcePath,
+ GENERIC_READ, // desired access
+ FILE_SHARE_READ, // share mode
+ NULL, // no security attributes
+ OPEN_EXISTING, // open if exists; fail if not.
+ FILE_ATTRIBUTE_NORMAL| FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_SEQUENTIAL_SCAN, // flags
+ (HANDLE) NULL // no template
+ );
+
+ if (SourceHandle == INVALID_HANDLE_VALUE) {
+ ApiStatus = (NET_API_STATUS) GetLastError();
+
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplCopyDirectoryItself: open of source '" FORMAT_LPTSTR
+ "' gave status " FORMAT_API_STATUS ".\n",
+ SourcePath, ApiStatus ));
+
+ NetpAssert( ApiStatus != NO_ERROR );
+ goto Cleanup;
+ }
+
+ //
+ // Override current dest attributes, particularly read-only,
+ // until we're done.
+ //
+ if ( !SetFileAttributes(
+ (LPTSTR) DestPath,
+ FILE_ATTRIBUTE_NORMAL) ) {
+
+ ApiStatus = (NET_API_STATUS) GetLastError();
+ NetpAssert( ApiStatus != NO_ERROR );
+
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplCopyDirectoryItself: unexpected ret code from temp "
+ "SetFileAttributes(" FORMAT_LPTSTR ", " FORMAT_HEX_DWORD
+ "): " FORMAT_API_STATUS ".\n",
+ DestPath, SourceAttributes, ApiStatus ));
+
+ goto Cleanup;
+ }
+
+ //
+ // Open dest directory, which must exist by now.
+ //
+ DestHandle = CreateFile(
+ DestPath,
+ GENERIC_WRITE | WRITE_DAC | WRITE_OWNER, // desired access
+ FILE_SHARE_WRITE, // share mode
+ NULL, // no security attributes
+ OPEN_EXISTING, // open if exists; fail if not.
+ FILE_ATTRIBUTE_NORMAL| FILE_FLAG_BACKUP_SEMANTICS, // flags
+ (HANDLE) NULL // no template
+ );
+
+ if (DestHandle == INVALID_HANDLE_VALUE) {
+ ApiStatus = (NET_API_STATUS) GetLastError();
+
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplCopyDirectoryItself: open of dest '" FORMAT_LPTSTR
+ "' gave status " FORMAT_API_STATUS ".\n",
+ SourcePath, ApiStatus ));
+ NetpAssert( ApiStatus != NO_ERROR );
+ goto Cleanup;
+ }
+
+ BackupBuffer = NetpMemoryAllocate( MY_BACKUP_API_BUFFER_SIZE );
+ if (BackupBuffer == NULL) {
+ ApiStatus = ERROR_NOT_ENOUGH_MEMORY;
+ goto Cleanup;
+ }
+
+ IF_DEBUG( SYNC ) {
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplCopyDirectoryItself: copying" ));
+ }
+
+ for (;;) { // until end of file...
+
+ IF_DEBUG( SYNC ) {
+ NetpKdPrint(( "<" ));
+ }
+
+ if ( !BackupRead(
+ SourceHandle,
+ BackupBuffer,
+ MY_BACKUP_API_BUFFER_SIZE,
+ & ActualBufferSizeRead,
+ FALSE, // don't abort yet
+ MY_PROCESS_SECURITY,
+ & SourceContext
+ ) ) {
+
+ // Process read error.
+ ApiStatus = (NET_API_STATUS) GetLastError();
+
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplCopyDirectoryItself: BackupRead"
+ " gave status " FORMAT_API_STATUS ".\n",
+ ApiStatus ));
+ NetpAssert( ApiStatus != NO_ERROR );
+ goto Cleanup;
+ }
+
+ // No error on read, how about EOF?
+ if (ActualBufferSizeRead == 0) { // normal end of file.
+ ApiStatus = NO_ERROR;
+
+ IF_DEBUG( SYNC ) {
+ NetpKdPrint(( "DONE(OK)\n" ));
+ }
+
+ break;
+ }
+
+ IF_DEBUG( SYNC ) {
+ NetpKdPrint(( ">" ));
+ }
+
+ if ( !BackupWrite(
+ DestHandle,
+ BackupBuffer,
+ ActualBufferSizeRead,
+ & ActualBufferSizeWritten,
+ FALSE, // don't abort yet
+ MY_PROCESS_SECURITY,
+ & DestContext
+ ) ) {
+
+ ApiStatus = (NET_API_STATUS) GetLastError();
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplCopyDirectoryItself: BackupWrite"
+ " gave status " FORMAT_API_STATUS ".\n",
+ ApiStatus ));
+ NetpAssert( ApiStatus != NO_ERROR );
+ goto Cleanup;
+ }
+
+ } // until end of file
+
+ ApiStatus = NO_ERROR;
+
+#endif
+
+ NetpAssert( ReplFileOrDirExists( DestPath ) );
+
+ //
+ // Set the attributes (hidden, system, archive, etc) for the dir.
+ //
+ IF_DEBUG( SYNC ) {
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "Setting client's dir attributes to "
+ FORMAT_HEX_DWORD " for '" FORMAT_LPTSTR "'.\n",
+ SourceAttributes, DestPath ));
+ }
+
+ if ( !SetFileAttributes(
+ (LPTSTR) DestPath,
+ SourceAttributes) ) {
+
+ ApiStatus = (NET_API_STATUS) GetLastError();
+ NetpAssert( ApiStatus != NO_ERROR );
+
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplCopyDirectoryItself: unexpected ret code from final "
+ "SetFileAttributes(" FORMAT_LPTSTR ", " FORMAT_HEX_DWORD
+ "): " FORMAT_API_STATUS ".\n",
+ DestPath, SourceAttributes, ApiStatus ));
+
+ goto Cleanup;
+ }
+
+ ApiStatus = NO_ERROR;
+
+ } // yes, source is a directory
+
+Cleanup:
+
+#ifdef USE_BACKUP_APIS
+
+ if (DestHandle != INVALID_HANDLE_VALUE) {
+ if (DestContext) {
+ (VOID) BackupWrite(
+ DestHandle,
+ BackupBuffer,
+ ActualBufferSizeRead,
+ & ActualBufferSizeWritten,
+ TRUE, // yes, it is time to abort.
+ MY_PROCESS_SECURITY,
+ & DestContext
+ );
+ }
+ (VOID) CloseHandle( DestHandle );
+ } else {
+ NetpAssert( DestContext == NULL );
+ }
+
+ if (SourceHandle != INVALID_HANDLE_VALUE) {
+ if (SourceContext != NULL) {
+ (VOID) BackupRead(
+ SourceHandle,
+ BackupBuffer,
+ MY_BACKUP_API_BUFFER_SIZE,
+ & ActualBufferSizeRead,
+ TRUE, // yes, it is time to abort.
+ MY_PROCESS_SECURITY,
+ & SourceContext
+ );
+ }
+ (VOID) CloseHandle( SourceHandle );
+ } else {
+ NetpAssert( SourceContext == NULL );
+ }
+
+ if (BackupBuffer != NULL) {
+ NetpMemoryFree( BackupBuffer );
+ }
+
+#endif
+
+ if (DestSecurityAttr != NULL) {
+ NetpMemoryFree( DestSecurityAttr );
+ }
+
+ //
+ // Now that directory is closed, we can copy timestamps...
+ //
+ if (ApiStatus == NO_ERROR) {
+ ReplCopyJustDateTime(
+ SourcePath,
+ DestPath,
+ TRUE ); // yes, we're copying directories.
+ } else {
+
+ // Log this!
+ ReplErrorLog(
+ NULL, // local (no server name)
+ NELOG_ReplSysErr, // log code
+ ApiStatus,
+ NULL, // no optional str1
+ NULL ); // no optional str2
+
+ // BUGBUG: Log on remote server too if we got UNC name.
+
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplCopyDirectoryItself: ERROR " FORMAT_API_STATUS
+ " while copying to '" FORMAT_LPTSTR "'.\n",
+ ApiStatus, DestPath ));
+
+ }
+
+ IF_DEBUG( MAJOR ) {
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplCopyDirectoryItself: directory create/copy of '"
+ FORMAT_LPTSTR "' to '"
+ FORMAT_LPTSTR "' gave status " FORMAT_API_STATUS ".\n",
+ SourcePath, DestPath, ApiStatus ));
+ }
+
+ return (ApiStatus);
+
+}
diff --git a/private/net/svcdlls/repl/server/copyfile.c b/private/net/svcdlls/repl/server/copyfile.c
new file mode 100644
index 000000000..f4a15f8ad
--- /dev/null
+++ b/private/net/svcdlls/repl/server/copyfile.c
@@ -0,0 +1,553 @@
+/*++
+
+Copyright (c) 1992-1993 Microsoft Corporation
+
+Module Name:
+
+ CopyFile.c
+
+Abstract:
+
+ ReplCopyFile() copies all of the following for a file:
+
+ - the data
+ - the attributes (normal, readonly, etc)
+ - the EAs
+ - the security (including ACLs, owner, group)
+ - the alternate data stream (for NTFS only)
+ - the timestamps (creation, last _access, last _write)
+
+ Here's how those things are copied, depending on conditional compilation:
+
+ USE_BACKUP_APIS and
+ what USE_UNC_GETFILESEC USE_BACKUP_APIS neither
+ ---------- -------------------- -------------------- --------------------
+ data Backup APIs Backup APIs CopyFile API
+ attributes SetFileAttributes SetFileAttributes SetFileAttributes
+ EAs Backup APIs Backup APIs BUGBUG: NOT COPIED!
+ security CreateFile CreateFile CreateFile
+ alt.data Backup APIs Backup APIs BUGBUG: NOT COPIED!
+ timestamps ReplCopyJustDateTime ReplCopyJustDateTime ReplCopyJustDateTime
+
+ The access required for the import tree and export tree are given below.
+ CHANGE=add+read+write+traverse.
+ READ=read+traverse.
+ ALL=add+read+write+traverse+setperm+takeowner.
+
+ USE_BACKUP_APIS and
+ what USE_UNC_GETFILESEC USE_BACKUP_APIS neither
+ ---------- -------------------- -------------------- --------------------
+ dest needs CHANGE access CHANGE access ALL access
+ src needs CHANGE access CHANGE access CHANGE access
+
+ NOTE: We need CHANGE access on src files, as we copy those ACLs to dest
+ files, which we need to be able to update next time!
+
+Author:
+
+ John Rogers (JohnRo) 11-Aug-1992
+
+Environment:
+
+ User mode only. Uses Win32 APIs.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+ Tab size is set to 4.
+
+Revision History:
+
+ 11-Aug-1992 JohnRo
+ Created for RAID 3288: repl svc should preserve ACLs on copy.
+ 30-Dec-1992 JohnRo
+ RAID 1513: repl does not maintain ACLs. (Really enable Backup APIs.)
+ Make sure timestamp gets rounded in right direction.
+ Use NetpKdPrint where possible.
+ 08-Jan-1993 JohnRo
+ RAID 6961: ReplCopyDirectoryItself does not respect bFailIfExists.
+ 18-Jan-1993 JohnRo
+ RAID 7983: Repl svc needs to change process token to really copy ACLs.
+ 11-Feb-1993 JohnRo
+ RAID 10716: msg timing and checksum problems (also downlevel msg bug).
+ 26-Feb-1993 JohnRo
+ RAID 12986: Workaround bug where HPFS leaves archive bit on.
+ 06-Apr-1993 JohnRo
+ RAID 1938: Replicator un-ACLs files when not given enough permission.
+ Do some debug output when setting attributes.
+ Prepare for >32 bits someday.
+ 23-Apr-1993 JohnRo
+ RAID 7313: repl needs change permission to work on NTFS,
+ or we need to delete files differently.
+ 04-Jun-1993 JohnRo
+ RAID 12473: Changed to handle ReplMakeFileSecurity not supported on
+ downlevel exporter.
+
+--*/
+
+
+// These must be included first:
+
+#include <windows.h> // IN, LPTSTR, CopyFile(), WRITE_DAC, etc.
+#include <lmcons.h> // NET_API_STATUS, PATHLEN, etc.
+
+// These may be included in any order:
+
+#include <lmerr.h> // NERR_InternalError, NO_ERROR.
+#include <lmerrlog.h> // NELOG_ equates.
+#include <netdebug.h> // NetpKdPrint(), FORMAT_ equates, etc.
+#include <prefix.h> // PREFIX_ equates.
+#include <repldefs.h> // IF_DEBUG(), my prototype, USE_ equates, etc.
+#include <tstr.h> // TCHAR_EOS, STRLEN().
+
+
+
+// Arbitrary backup API buffer size.
+#define REPL_BACKUP_BUFFER_SIZE (16*1024)
+
+// No, we don't want to put security info in the stream.
+// (We're using CreateFile's security attributes parameter for this.)
+#define REPL_PROCESS_SECURITY FALSE
+
+
+NET_API_STATUS
+ReplCopyFile(
+ IN LPCTSTR SourcePath,
+ IN LPCTSTR DestPath,
+ IN BOOL bFailIfExists
+ )
+
+/*++
+
+Routine Description:
+
+ ReplCopyFile copies everthing contained in a file. ("Everything" includes
+ data, alternate data, extended attributes, attributes, timestamps, and
+ security info.)
+
+Arguments:
+
+ SourcePath - Points to source file path, which might be a UNC name.
+
+ DestPath - Points to destination file path name.
+
+ bFailIfExists - a boolean which is TRUE iff this routine should fail
+ if the destination already exists.
+
+Return Value:
+
+ NET_API_STATUS
+
+Threads:
+
+ Used by client and syncer threads.
+
+--*/
+
+{
+ NET_API_STATUS ApiStatus;
+ BOOL DestExists;
+ HANDLE DestHandle = INVALID_HANDLE_VALUE;
+ PSECURITY_ATTRIBUTES DestSecurityAttr = NULL;
+ DWORD SourceAttributes = (DWORD) (-1);
+
+#ifdef USE_BACKUP_APIS
+ DWORD ActualBufferSizeRead = 0;
+ DWORD ActualBufferSizeWritten;
+ LPVOID BackupBuffer = NULL;
+ LPVOID DestContext = NULL;
+ LPVOID SourceContext = NULL;
+ HANDLE SourceHandle = INVALID_HANDLE_VALUE;
+#endif
+
+
+ //
+ // Check for caller errors.
+ //
+
+ NetpAssert( SourcePath != NULL );
+ NetpAssert( (*SourcePath) != TCHAR_EOS );
+ NetpAssert( STRLEN(SourcePath) <= PATHLEN );
+ NetpAssert( DestPath != NULL );
+ NetpAssert( (*DestPath) != TCHAR_EOS );
+ NetpAssert( STRLEN(DestPath) <= PATHLEN );
+
+ DestExists = ReplFileOrDirExists( DestPath );
+ if (bFailIfExists && DestExists) {
+ // DON'T GOTO CLEANUP FROM HERE, AS IT WOULD DELETE FILE!!!
+ return (ERROR_ALREADY_EXISTS);
+ }
+
+ //
+ // What kind of source (file/directory) is it?
+ //
+
+ SourceAttributes = GetFileAttributes( (LPTSTR) SourcePath );
+
+ if ( SourceAttributes == (DWORD) -1 ) {
+
+ //
+ // File doesn't exist, bad syntax, or something along those lines.
+ //
+ ApiStatus = (NET_API_STATUS) GetLastError();
+ NetpAssert( ApiStatus != NO_ERROR );
+ // DON'T GOTO CLEANUP FROM HERE, AS IT WOULD DELETE FILE!!!
+ return (ApiStatus);
+
+ } else if ((SourceAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) {
+
+ //
+ // Source is a directory tree. We don't handle this!
+ //
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplCopyFile: source is directory! path: '"
+ FORMAT_LPTSTR "', src attr " FORMAT_HEX_DWORD ".\n",
+ SourcePath, SourceAttributes ));
+ ApiStatus = NERR_InternalError;
+ // DON'T GOTO CLEANUP FROM HERE, AS IT WOULD DELETE FILE!!!
+ return (ApiStatus);
+
+ } else {
+
+ //
+ // Simple case: source exists and is just a single file.
+ // Start off by setting desired destination security attributes.
+ //
+
+ ApiStatus = ReplMakeSecurityAttributes(
+ SourcePath,
+ &DestSecurityAttr ); // alloc and set ptr
+ if (ApiStatus==ERROR_NOT_SUPPORTED) {
+
+ // Just downlevel master, so set default security and continue.
+ DestSecurityAttr = NULL;
+
+ } else if (ApiStatus != NO_ERROR) {
+ // DON'T GOTO CLEANUP FROM HERE, AS IT WOULD DELETE FILE!!!
+ return (ApiStatus);
+ } else {
+ NetpAssert( ApiStatus == NO_ERROR );
+ NetpAssert( DestSecurityAttr != NULL );
+ }
+
+#ifdef USE_BACKUP_APIS
+ //
+ // Open source file.
+ //
+ SourceHandle = CreateFile(
+ SourcePath,
+ GENERIC_READ, // desired access
+ FILE_SHARE_READ, // share mode
+ NULL, // no security attributes
+ OPEN_EXISTING, // open if exists; fail if not.
+ FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_SEQUENTIAL_SCAN, // flags
+ (HANDLE) NULL // no template
+ );
+
+ if (SourceHandle == INVALID_HANDLE_VALUE) {
+ ApiStatus = (NET_API_STATUS) GetLastError();
+
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplCopyFile: open of source '" FORMAT_LPTSTR
+ "' gave status " FORMAT_API_STATUS ".\n",
+ SourcePath, ApiStatus ));
+
+ NetpAssert( ApiStatus != NO_ERROR );
+
+ if (DestSecurityAttr != NULL) {
+ NetpMemoryFree( DestSecurityAttr );
+ }
+
+ // DON'T GOTO CLEANUP FROM HERE, AS IT WOULD DELETE FILE!!!
+ return (ApiStatus);
+ }
+#endif
+
+ //
+ // Delete the destination file. Why? Because...
+ // We're going to set the security on destination file using
+ // the CreateFile API. This will ignore setting the security if the
+ // file already exists. So, we need to delete it to force this to
+ // happen.
+ //
+
+ if (DestExists) {
+ ApiStatus = ReplDeleteFile( DestPath );
+ if (ApiStatus != NO_ERROR) {
+ goto Cleanup;
+ }
+ }
+ NetpAssert( !ReplFileOrDirExists( DestPath ) );
+
+ //
+ // Open dest file. Since we already deleted any existing one, we know
+ // this will create the file from scratch. This is important, because
+ // we're depending on CreateFile's use of DestSecurityAttr to set
+ // security on the file.
+ //
+
+ DestHandle = CreateFile(
+ DestPath,
+ GENERIC_WRITE // desired...
+ | WRITE_DAC // ...
+ | WRITE_OWNER, // access
+ FILE_SHARE_WRITE, // share mode: none
+ DestSecurityAttr, // desired security attributes
+ CREATE_NEW, // disposition create new (fail exist)
+#ifdef USE_BACKUP_APIS
+ FILE_FLAG_BACKUP_SEMANTICS, // flags
+#else
+ 0, // flags
+#endif
+ (HANDLE) NULL // no template
+ );
+
+ if (DestHandle == INVALID_HANDLE_VALUE) {
+ ApiStatus = (NET_API_STATUS) GetLastError();
+
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplCopyFile: open of dest '" FORMAT_LPTSTR
+ "' gave status " FORMAT_API_STATUS ".\n",
+ DestPath, ApiStatus ));
+
+ NetpAssert( ApiStatus != NO_ERROR );
+ goto Cleanup;
+ }
+
+#ifndef USE_BACKUP_APIS
+
+ //
+ // Close the dest file and copy the data.
+ // (In this case, we're using CopyFile API, which is not handle-based.)
+ //
+
+ NetpAssert( DestHandle != INVALID_HANDLE_VALUE );
+ (VOID) CloseHandle( DestHandle );
+ DestHandle = INVALID_HANDLE_VALUE;
+
+ if ( !CopyFile(
+ (LPTSTR) SourcePath,
+ (LPTSTR) DestPath,
+ FALSE /* don't fail if exists */ ) ) {
+
+ ApiStatus = (NET_API_STATUS) GetLastError();
+ NetpAssert( ApiStatus != NO_ERROR );
+ goto Cleanup;
+
+ }
+
+ NetpAssert( ReplFileOrDirExists( DestPath ) );
+
+ // Timestamps and attributes will be copied below.
+ // BUGBUG no way to copy EAs, alt data.
+ ApiStatus = NO_ERROR;
+
+
+#else // defined(USE_BACKUP_APIS)
+
+ BackupBuffer = NetpMemoryAllocate( REPL_BACKUP_BUFFER_SIZE );
+ if (BackupBuffer == NULL) {
+ ApiStatus = ERROR_NOT_ENOUGH_MEMORY;
+ goto Cleanup;
+ }
+
+
+ IF_DEBUG( SYNC ) {
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplCopyFile: copying" ));
+ }
+
+ for (;;) { // until end of file...
+
+ IF_DEBUG( SYNC ) {
+ NetpKdPrint(( "<" ));
+ }
+
+ if ( !BackupRead(
+ SourceHandle,
+ BackupBuffer,
+ REPL_BACKUP_BUFFER_SIZE,
+ & ActualBufferSizeRead,
+ FALSE, // don't abort yet
+ REPL_PROCESS_SECURITY,
+ & SourceContext
+ ) ) {
+
+ // Process read error.
+ ApiStatus = (NET_API_STATUS) GetLastError();
+
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplCopyFile: BackupRead"
+ " gave status " FORMAT_API_STATUS ".\n",
+ ApiStatus ));
+ NetpAssert( ApiStatus != NO_ERROR );
+ goto Cleanup;
+ }
+
+ // No error on read, how about EOF?
+ if (ActualBufferSizeRead == 0) { // normal end of file.
+ ApiStatus = NO_ERROR;
+
+ IF_DEBUG( SYNC ) {
+ NetpKdPrint(( "DONE(OK)\n" ));
+ }
+
+ break;
+ }
+
+ IF_DEBUG( SYNC ) {
+ NetpKdPrint(( ">" ));
+ }
+
+ if ( !BackupWrite(
+ DestHandle,
+ BackupBuffer,
+ ActualBufferSizeRead,
+ & ActualBufferSizeWritten,
+ FALSE, // don't abort yet
+ REPL_PROCESS_SECURITY,
+ & DestContext
+ ) ) {
+
+ ApiStatus = (NET_API_STATUS) GetLastError();
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplCopyFile: BackupWrite"
+ " gave status " FORMAT_API_STATUS ".\n",
+ ApiStatus ));
+ NetpAssert( ApiStatus != NO_ERROR );
+ goto Cleanup;
+ }
+
+ } // until end of file
+ ApiStatus = NO_ERROR;
+
+
+#endif
+
+
+ } // else (just single file)
+
+
+Cleanup:
+
+#ifdef USE_BACKUP_APIS
+
+ if (DestHandle != INVALID_HANDLE_VALUE) {
+ if (DestContext) {
+ (VOID) BackupWrite(
+ DestHandle,
+ BackupBuffer,
+ ActualBufferSizeRead,
+ & ActualBufferSizeWritten,
+ TRUE, // yes, it is time to abort.
+ REPL_PROCESS_SECURITY,
+ & DestContext
+ );
+ }
+ (VOID) CloseHandle( DestHandle );
+ } else {
+ NetpAssert( DestContext == NULL );
+ }
+
+ if (SourceHandle != INVALID_HANDLE_VALUE) {
+ if (SourceContext != NULL) {
+ (VOID) BackupRead(
+ SourceHandle,
+ BackupBuffer,
+ REPL_BACKUP_BUFFER_SIZE,
+ & ActualBufferSizeRead,
+ TRUE, // yes, it is time to abort.
+ REPL_PROCESS_SECURITY,
+ & SourceContext
+ );
+ }
+ (VOID) CloseHandle( SourceHandle );
+ } else {
+ NetpAssert( SourceContext == NULL );
+ }
+
+ if (BackupBuffer != NULL) {
+ NetpMemoryFree( BackupBuffer );
+ }
+
+#endif
+
+ //
+ // Copy the timestamps, and make sure we round them in a manner consistent
+ // with our checksum. CopyFile and GetFileTime/SetFileTime all round
+ // in the opposite direction.
+ //
+ if (ApiStatus == NO_ERROR) {
+ ReplCopyJustDateTime(
+ SourcePath,
+ DestPath,
+ FALSE ); // no, we're not copying directories.
+ }
+
+ if ( (ApiStatus==NO_ERROR) && (SourceAttributes != (DWORD)-1) ) {
+
+ //
+ // Set the attributes (hidden, system, archive, etc) for the dir.
+ //
+ IF_DEBUG( SYNC ) {
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "Setting client's file attributes to "
+ FORMAT_HEX_DWORD " for '" FORMAT_LPTSTR "'.\n",
+ SourceAttributes, DestPath ));
+ }
+
+ if ( !SetFileAttributes(
+ (LPTSTR) DestPath,
+ SourceAttributes) ) {
+
+ ApiStatus = (NET_API_STATUS) GetLastError();
+
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplCopyFile: unexpected ret code from "
+ "SetFileAttributes(" FORMAT_LPTSTR ", " FORMAT_HEX_DWORD
+ "): " FORMAT_API_STATUS ".\n",
+ DestPath, SourceAttributes, ApiStatus ));
+ NetpAssert( ApiStatus != NO_ERROR );
+
+ // Already cleaning up, so just continue...
+ }
+ }
+
+ if (DestSecurityAttr != NULL) {
+ NetpMemoryFree( DestSecurityAttr );
+ }
+
+ //
+ // Last, but not least: the BackupWrite API has a problem where if
+ // we don't have permission to set the ACLs, it writes the file without
+ // them. At least it gives us a return code in this case.
+ // So, if the copy wasn't perfect, get rid of anything which might
+ // be a security problem.
+ //
+ if (ApiStatus != NO_ERROR) {
+
+ // Log this!
+ ReplErrorLog(
+ NULL, // local (no server name)
+ NELOG_ReplSysErr, // log code
+ ApiStatus,
+ NULL, // no optional str1
+ NULL ); // no optional str2
+
+ // BUGBUG: Log on remote server too if we got UNC name.
+
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplCopyFile: making sure target file '" FORMAT_LPTSTR
+ "' is gone due to error " FORMAT_API_STATUS
+ " while copying.\n", DestPath, ApiStatus ));
+
+ (VOID) ReplDeleteFile( DestPath );
+ }
+
+ IF_DEBUG( SYNC ) {
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplCopyFile: file (etc) copy of " FORMAT_LPTSTR " to "
+ FORMAT_LPTSTR " gave status " FORMAT_API_STATUS ".\n",
+ SourcePath, DestPath, ApiStatus ));
+ }
+
+ return (ApiStatus);
+
+}
diff --git a/private/net/svcdlls/repl/server/copytime.c b/private/net/svcdlls/repl/server/copytime.c
new file mode 100644
index 000000000..647b24599
--- /dev/null
+++ b/private/net/svcdlls/repl/server/copytime.c
@@ -0,0 +1,527 @@
+/*++
+
+Copyright (c) 1987-1993 Microsoft Corporation
+
+Module Name:
+
+ CopyTime.c
+
+Abstract:
+
+ This module contains:
+
+ ReplCopyJustDateTime
+ ReplIsFileTimeCloseEnough
+ ReplMungeFileTime
+
+Author:
+
+ JR (John Rogers, JohnRo@Microsoft)
+
+Environment:
+
+ User mode only.
+ Contains NT-specific code.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+ 16-Dec-1992 JohnRo
+ RAID 1513: Repl does not maintain ACLs. (Also fix HPFS->FAT timestamp.)
+ Extracted ReplCopyJustDateTime to its own source file (from syncer.c).
+ 10-Mar-1993 JohnRo
+ RAID 13126: Fix repl memory leak.
+ Log errors in ReplCopyJustDateTime() and ReplMungeFileTime().
+ Added debug output to catch dir timestamp being set wrong.
+ 16-Jun-1993 JohnRo
+ RAID 13080: Allow repl between different timezones.
+ (Actually fix "off by one" problem when munging NTFS to FAT time.)
+ 09-Jul-1993 JohnRo
+ RAID 15736: OS/2 time stamps are broken again (try rounding down).
+
+--*/
+
+// These must be included first:
+
+#include <nt.h> // NtOpenFile(), ULONG, etc.
+#include <ntrtl.h> // PLARGE_INTEGER, TIME_FIELDS, etc.
+#include <nturtl.h> // Needed for ntrtl.h and windows.h to co-exist.
+
+#include <windows.h> // GetLastError(), LPFILETIME, CompareFileTime(), etc.
+#include <lmcons.h>
+
+// These may be included in any order:
+
+#include <client.h> // RCGlobalFsTimeResultionSecs.
+#include <lmerr.h> // NO_ERROR, NERR_InternalError.
+#include <lmerrlog.h> // NELOG_* defines
+#include <netdebug.h> // DBGSTATIC, NetpKdPrint(), FORMAT_ equates, etc.
+#include <netlibnt.h> // NetpNtStatusToApiStatus().
+#include <prefix.h> // PREFIX_ equates.
+#include <repldefs.h> // ReplCopyJustDateTime(), UNKNOWN_FS_RESOLUTION, etc.
+#include <timelib.h> // NetpFileTimeToSecondsSince1970().
+
+
+#define ISEVEN(n) ( ( (n) & 0x01 ) == 0 )
+
+
+#ifndef REPL_DEBUG_COPYTIME
+#define REPL_DEBUG_COPYTIME 0x00000800 // Debug the time copy routines.
+#endif
+
+
+DBGSTATIC NET_API_STATUS
+ReplMungeFileTime(
+ IN PLARGE_INTEGER OriginalFileTime,
+ IN DWORD ImportFsTimeResolutionSecs,
+ OUT PLARGE_INTEGER MungedFileTime
+ );
+
+
+VOID
+ReplCopyJustDateTime(
+ IN LPCTSTR SourcePath,
+ IN LPCTSTR DestPath,
+ IN BOOL IsDirectory
+ )
+/*++
+
+Routine Description:
+
+ Set the date and time stamps of the destination path to that of the
+ source path.
+
+ This function can be called in different contexts for different types
+ of paths:
+
+ (1) for files (called by ReplCopyFile).
+
+ (2) for first-level directories (called by ReplFileIntegritySync and
+ ReplTreeIntegritySync).
+
+ (3) for non-first-level directories (called by ReplCopyDirectoryItself).
+ In this case, ReplCopyJustDateTime is typically called on a pair of
+ directories to restore the date and time after files have been
+ added to the directory (changing the timestamps in the first place).
+
+Arguments:
+
+ SourcePath - Specifies the path (of the file or directory) which
+ currently has the date and time stamps. This must already exist.
+
+ DestPath - Specifies the path (of the file or directory) which is to
+ receive the date and time stamps. This must already exist.
+
+ IsDirectory - true iff this is a directory copy; that is,
+ SourcePath and DestPath are both directories.
+
+Return Value:
+
+ NONE.
+
+--*/
+{
+ NET_API_STATUS ApiStatus;
+ HANDLE FileHandle = INVALID_HANDLE_VALUE; // Src file, then dest file.
+ FILE_BASIC_INFORMATION DestInfo;
+ IO_STATUS_BLOCK IoStatusBlock;
+ LARGE_INTEGER MungedLastWriteTime;
+ NTSTATUS NtStatus;
+ OBJECT_ATTRIBUTES ObjectAttributes; // Src obj attr, then dest obj attr.
+ const ULONG OpenOptions = // Src or dest open options.
+ FILE_SYNCHRONOUS_IO_NONALERT
+ | ( IsDirectory ? FILE_DIRECTORY_FILE : 0 );
+ BOOL PathAllocated = FALSE;
+ FILE_BASIC_INFORMATION SourceInfo;
+ UNICODE_STRING UnicodePath;
+
+ NetpAssert( SourcePath != NULL );
+ NetpAssert( DestPath != NULL );
+
+ //
+ // Determine the timestamps on the source path.
+ //
+
+ RtlInitUnicodeString(
+ & UnicodePath, // output: struct
+ SourcePath ); // input: null terminated
+
+ if( !RtlDosPathNameToNtPathName_U(
+ SourcePath,
+ &UnicodePath,
+ NULL,
+ NULL) ) {
+
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplCopyJustDateTime: RtlDosPathNameToNtPathname_U"
+ " of source '" FORMAT_LPTSTR "' failed.\n", SourcePath ));
+
+ // BUGBUG: this is just our best guess for an error code for this.
+ ApiStatus = NERR_InternalError;
+ goto Cleanup;
+ }
+ PathAllocated = TRUE;
+
+ InitializeObjectAttributes(
+ &ObjectAttributes,
+ (LPVOID) &UnicodePath,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL );
+ //
+ // Open source file, so we can get the timestamps.
+ // BUGBUG: This used to ask for SYNCHRONIZE | FILE_READ_ATTRIBUTES,
+ // But that ran into a "feature" in the NT redir where it "optimizes"
+ // and only gives time in 2 second resolution.
+ //
+ NtStatus = NtOpenFile(
+ & FileHandle,
+ SYNCHRONIZE | // desired ...
+ FILE_READ_ATTRIBUTES | // ... (See BUGBUG above.)
+ FILE_READ_DATA, // ... access
+ &ObjectAttributes,
+ &IoStatusBlock,
+ FILE_SHARE_READ, // share access
+ OpenOptions );
+
+ if ( !NT_SUCCESS( NtStatus ) ) {
+
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplCopyJustDateTime: NtOpenFile of source '"
+ FORMAT_LPTSTR "' gave NT status " FORMAT_NTSTATUS ".\n",
+ SourcePath, NtStatus ));
+
+ ApiStatus = NetpNtStatusToApiStatus( NtStatus );
+ NetpAssert( ApiStatus != NO_ERROR );
+ goto Cleanup;
+ }
+ NetpAssert( NtStatus == STATUS_SUCCESS );
+
+ NtStatus = NtQueryInformationFile(
+ FileHandle,
+ &IoStatusBlock,
+ (PVOID) &SourceInfo,
+ (ULONG) sizeof(SourceInfo),
+ FileBasicInformation ); // info class
+
+ if ( !NT_SUCCESS( NtStatus ) ) {
+
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplCopyJustDateTime: NtQueryInformationFile (src) failed, "
+ " NT status is " FORMAT_NTSTATUS
+ ".\n", NtStatus ));
+
+ ApiStatus = NetpNtStatusToApiStatus( NtStatus );
+ NetpAssert( ApiStatus != NO_ERROR );
+ goto Cleanup;
+ }
+ NetpAssert( NtStatus == STATUS_SUCCESS );
+
+ IF_DEBUG( COPYTIME ) {
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "initial master time for " FORMAT_LPTSTR " is:\n ",
+ SourcePath ));
+ NetpDbgDisplayLargeIntegerTime( &(SourceInfo.LastWriteTime) );
+ NetpKdPrint((
+ ", filetime(low)=" FORMAT_HEX_DWORD ", "
+ "filetime(high)=" FORMAT_HEX_DWORD ".\n",
+ (DWORD) SourceInfo.LastWriteTime.LowPart,
+ (DWORD) SourceInfo.LastWriteTime.HighPart ));
+ }
+
+ (void) NtClose( FileHandle );
+ FileHandle = INVALID_HANDLE_VALUE;
+
+ (VOID) RtlFreeHeap( RtlProcessHeap(), 0, UnicodePath.Buffer );
+ PathAllocated = FALSE;
+
+ //
+ // OK. We need to muck with a timestamp here. The situation involves
+ // different precisions for different filesystems. HPFS (under NT and OS/2)
+ // has 1-second precision. FAT (under any O.S.) has 2-second precision.
+ // The replicator (e.g. under OS/2) computes checksums using the FAT-format
+ // of the timestamp. In this case, OS/2 seems to be truncating the time.
+ // Under NT, there is a different policy: round the time up, to make
+ // life easy for tree-copy-by-time (TC /t). So, in order to interoperate
+ // with OS/2 systems, we have to munge them overselves.
+ //
+ // Note that only the last write time is involved in the checksum. If
+ // the other times were also involved, then we would have to munge them
+ // here too.
+ //
+ ApiStatus = ReplMungeFileTime(
+ & (SourceInfo.LastWriteTime), // input: original time
+ RCGlobalFsTimeResolutionSecs, // input: import filesys res.
+ & MungedLastWriteTime ); // output: munged time
+ if (ApiStatus != NO_ERROR) {
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplCopyJustDateTime: ReplMungeFileTime failed, status "
+ FORMAT_API_STATUS ".\n", ApiStatus ));
+ goto Cleanup;
+ }
+
+ //
+ // Set the timestamps on the dest path.
+ //
+
+ RtlInitUnicodeString(
+ & UnicodePath, // output: struct
+ DestPath ); // input: null terminated
+
+ if( !RtlDosPathNameToNtPathName_U(
+ DestPath,
+ &UnicodePath,
+ NULL,
+ NULL) ) {
+
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplCopyJustDateTime: RtlDosPathNameToNtPathname_U"
+ " of dest '" FORMAT_LPTSTR "' failed.\n", DestPath ));
+
+ ApiStatus = NERR_InternalError; // BUGBUG: better error code?
+ goto Cleanup;
+ }
+ PathAllocated = TRUE;
+
+ InitializeObjectAttributes(
+ &ObjectAttributes,
+ (LPVOID) &UnicodePath,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL );
+
+ NtStatus = NtOpenFile(
+ & FileHandle,
+ SYNCHRONIZE | // desired ...
+ FILE_READ_ATTRIBUTES | // ...
+ FILE_WRITE_ATTRIBUTES, // ... access
+ &ObjectAttributes,
+ &IoStatusBlock,
+ 0, // share access (none)
+ OpenOptions );
+
+ if ( !NT_SUCCESS( NtStatus ) ) {
+
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplCopyJustDateTime: NtOpenFile of dest '"
+ FORMAT_LPTSTR "' gave NT status " FORMAT_NTSTATUS ".\n",
+ DestPath, NtStatus ));
+
+ ApiStatus = NetpNtStatusToApiStatus( NtStatus );
+ NetpAssert( ApiStatus != NO_ERROR );
+ goto Cleanup;
+ }
+ NetpAssert( NtStatus == STATUS_SUCCESS );
+
+ NtStatus = NtQueryInformationFile(
+ FileHandle,
+ &IoStatusBlock,
+ (PVOID) &DestInfo,
+ (ULONG) sizeof(DestInfo),
+ FileBasicInformation ); // info class
+
+ if ( !NT_SUCCESS( NtStatus ) ) {
+
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplCopyJustDateTime: NtQueryInformationFile (dest) failed, "
+ " NT status is " FORMAT_NTSTATUS ".\n", NtStatus ));
+
+ ApiStatus = NetpNtStatusToApiStatus( NtStatus );
+ NetpAssert( ApiStatus != NO_ERROR );
+ goto Cleanup;
+ }
+ NetpAssert( NtStatus == STATUS_SUCCESS );
+
+ //
+ // We only checksum the write time, but let's update them all
+ // just to be kosher. Note that last write time must be the munged
+ // version, or client's checksum will never match NT server's.
+ //
+ DestInfo.ChangeTime = SourceInfo.ChangeTime;
+ DestInfo.CreationTime = SourceInfo.CreationTime;
+ DestInfo.LastAccessTime = SourceInfo.LastAccessTime;
+ DestInfo.LastWriteTime = MungedLastWriteTime;
+
+ NtStatus = NtSetInformationFile(
+ FileHandle,
+ &IoStatusBlock,
+ (PVOID) &DestInfo,
+ sizeof(DestInfo),
+ FileBasicInformation ); // info class
+
+ if ( !NT_SUCCESS( NtStatus ) ) {
+
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplCopyJustDateTime: NtSetInformationFile failed "
+ FORMAT_NTSTATUS ".\n", NtStatus ));
+
+ ApiStatus = NetpNtStatusToApiStatus( NtStatus );
+ NetpAssert( ApiStatus != NO_ERROR );
+ goto Cleanup;
+ }
+ NetpAssert( NtStatus == STATUS_SUCCESS );
+
+ IF_DEBUG( COPYTIME ) {
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "Changed time for " FORMAT_LPTSTR " to ",
+ DestPath ));
+ NetpDbgDisplayLargeIntegerTime( &(DestInfo.LastWriteTime) );
+ NetpKdPrint(( "\n" ));
+ }
+
+ ApiStatus = NO_ERROR;
+
+
+Cleanup:
+
+ if (ApiStatus != NO_ERROR) {
+
+ //
+ // Log the error.
+ // BUGBUG: extract master server name and log there too.
+ //
+ ReplErrorLog(
+ NULL, // no server name (log locally)
+ NELOG_ReplSysErr, // log code
+ ApiStatus,
+ NULL, // optional str1
+ NULL); // optional str2
+ }
+
+ if (FileHandle != INVALID_HANDLE_VALUE) {
+ (VOID) NtClose( FileHandle );
+ }
+
+ if (PathAllocated) {
+ (VOID) RtlFreeHeap( RtlProcessHeap(), 0, UnicodePath.Buffer );
+ }
+
+ return;
+
+} // ReplCopyJustDateTime
+
+
+DBGSTATIC NET_API_STATUS
+ReplMungeFileTime(
+ IN PLARGE_INTEGER OriginalFileTime,
+ IN DWORD ImportFsTimeResolutionSecs,
+ OUT PLARGE_INTEGER MungedFileTime
+ )
+{
+ ULONG MungedSecondsSince1970;
+ ULONG SecondsSince1970;
+ FILETIME TempFileTime;
+
+ UNREFERENCED_PARAMETER( ImportFsTimeResolutionSecs );
+
+ NetpAssert( OriginalFileTime != NULL );
+ NetpAssert( MungedFileTime != NULL );
+
+ //
+ // Convert from 64-bit to seconds since 1970.
+ // Round up to next second here.
+ //
+ TempFileTime.dwLowDateTime = OriginalFileTime->LowPart;
+ TempFileTime.dwHighDateTime = OriginalFileTime->HighPart;
+
+ NetpFileTimeToSecondsSince1970 (
+ &TempFileTime, // input (64-bit)
+ &SecondsSince1970 ); // output (secs) (round up to next sec)
+
+ //
+ // Round up to 2 second inteval.
+ //
+ if ( !ISEVEN( SecondsSince1970 ) ) {
+ MungedSecondsSince1970 = SecondsSince1970 + 1;
+ } else {
+ MungedSecondsSince1970 = SecondsSince1970;
+ }
+ NetpAssert( ISEVEN( MungedSecondsSince1970 ) );
+
+ //
+ // Convert back to 64-bit format.
+ //
+ (VOID) RtlSecondsSince1970ToTime(
+ MungedSecondsSince1970, // input: secs (rounded to 2 secs)
+ MungedFileTime ); // output: 64-bit
+
+ return (NO_ERROR);
+
+} // ReplMungeFileTime
+
+
+BOOL
+ReplIsFileTimeCloseEnough(
+ IN LPVOID MasterFileTime, // Points to a FILETIME value.
+ IN LPVOID ClientFileTime // Points to a FILETIME value.
+ )
+{
+ LPFILETIME ClientBigTime = ClientFileTime;
+ LONG CompareResult; // -1, 0, or 1
+ LPFILETIME MasterBigTime = MasterFileTime;
+
+ NetpAssert( MasterBigTime != NULL );
+ NetpAssert( ClientBigTime != NULL );
+
+ CompareResult = CompareFileTime(
+ MasterBigTime,
+ ClientBigTime );
+
+ if (CompareResult == 0) {
+
+ // Times match exactly.
+ IF_DEBUG( COPYTIME ) {
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplIsFileTimeCloseEnough: "
+ "CompareFileTime says files match.\n" ));
+ }
+ return (TRUE);
+
+ } else if (CompareResult == 1) {
+
+ // Client time too low. Not close enough.
+ return (FALSE);
+
+ } else {
+
+ DWORD MasterSecondsSince1970;
+ DWORD ClientSecondsSince1970;
+ DWORD TimeDiffSecs;
+
+ // Master time too low. Because of diff file system resolution
+ // or something else?
+ NetpAssert( CompareResult == -1 );
+
+ NetpFileTimeToSecondsSince1970(
+ MasterBigTime, // in: 64-bit
+ &MasterSecondsSince1970 ); // out: seconds since 1970
+
+ NetpFileTimeToSecondsSince1970(
+ ClientBigTime, // in: 64-bit
+ &ClientSecondsSince1970 ); // out: seconds since 1970
+
+ IF_DEBUG( COPYTIME ) {
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplIsFileTimeCloseEnough: "
+ "mast secs since 1970 is " FORMAT_DWORD ", "
+ "client secs since 1970 is " FORMAT_DWORD ".\n",
+ MasterSecondsSince1970, ClientSecondsSince1970 ));
+ }
+
+ NetpAssert( MasterSecondsSince1970 <= ClientSecondsSince1970 );
+ TimeDiffSecs = ClientSecondsSince1970 - MasterSecondsSince1970;
+ // NetpAssert( TimeDiffSecs != 0 );
+
+ // BUGBUG: This used to use filesys resolution
+ if (TimeDiffSecs < 2) {
+ // if (TimeDiffSecs < RCGlobalFsTimeResolutionSecs) {
+ return (TRUE); // Close enough.
+ } else {
+ return (FALSE); // Not within file system resolution.
+ }
+
+ }
+
+ /*NOTREACHED*/
+
+} // ReplIsFileTimeCloseEnough
diff --git a/private/net/svcdlls/repl/server/copytree.c b/private/net/svcdlls/repl/server/copytree.c
new file mode 100644
index 000000000..32408051d
--- /dev/null
+++ b/private/net/svcdlls/repl/server/copytree.c
@@ -0,0 +1,409 @@
+/*++
+
+Copyright (c) 1992-1993 Microsoft Corporation
+
+Module Name:
+
+ CopyTree.c
+
+Abstract:
+
+ ReplCopyTree() does a recursive file/directory copy.
+
+Author:
+
+ John Rogers (JohnRo) 25-Mar-1992
+
+Environment:
+
+ User mode only. Uses Win32 APIs.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+ Tab size is set to 4.
+
+Revision History:
+
+ 25-Mar-1992 JohnRo
+ Created.
+ 26-Mar-1992 JohnRo
+ Removed assumption about using the current directory.
+ New ReplFind routines interface.
+ 27-Mar-1992 JohnRo
+ Create dest directories. Copy single file if asked to.
+ 11-Aug-1992 JohnRo
+ RAID 3288: repl svc should preserve ACLs on copy.
+ 25-Feb-1993 JohnRo
+ RAID 12237: replicator tree depth exceeded.
+ Improve thread documentation.
+ Use NetpKdPrint() where possible.
+ Use PREFIX_ equates.
+ 22-Apr-1993 JohnRo
+ RAID 7157: replicator does not stop while in recursive tree copy.
+ Make sure attributes, EAs, etc. are updated for each directory.
+ Added error logging.
+ Random cleanup.
+ Prepare for >32 bits someday.
+
+--*/
+
+
+#include <windows.h> // IN, LPTSTR, etc.
+#include <lmcons.h> // NET_API_STATUS, PATHLEN, etc.
+
+#include <client.h> // ReplCopyTree().
+#include <filefind.h> // REPL_WIN32_FIND_DATA.
+#include <lmerrlog.h> // NELOG_ equates.
+#include <netdebug.h> // NetpKdPrint(), FORMAT_ equates, etc.
+#include <prefix.h> // PREFIX_ equates.
+#include <repldefs.h> // IF_DEBUG(), STAR_DOT_STAR, etc.
+#include <replgbl.h> // ReplGlobal variables.
+#include <tstr.h> // STRLEN(), TCHAR_EOS, etc.
+
+
+DBGSTATIC NET_API_STATUS
+ReplCopyRestOfTree(
+ IN LPTSTR SourcePath,
+ IN LPTSTR DestPath
+ );
+
+
+NET_API_STATUS
+ReplCopyTree(
+ IN LPTSTR SourcePath,
+ IN LPTSTR DestPath
+ )
+
+/*++
+
+Routine Description:
+
+ Front-end for ReplCopyRestOfTree(). See ReplCopyRestOfTree().
+
+Arguments:
+
+ See ReplCopyRestOfTree().
+
+Return Value:
+
+ NET_API_STATUS - NO_ERROR (copy complete).
+ - ERROR_OPERATION_ABORTED (service is stopping).
+ - other errors.
+
+Threads:
+
+ Used by client and syncer threads.
+
+--*/
+
+{
+ NET_API_STATUS ApiStatus;
+ DWORD Attributes;
+ TCHAR FullSourceBuffer[PATHLEN+1];
+ TCHAR FullDestBuffer[PATHLEN+1];
+
+ NetpAssert( SourcePath != NULL );
+ NetpAssert( (*SourcePath) != TCHAR_EOS );
+ NetpAssert( STRLEN(SourcePath) <= PATHLEN );
+ NetpAssert( DestPath != NULL );
+ NetpAssert( (*DestPath) != TCHAR_EOS );
+ NetpAssert( STRLEN(DestPath) <= PATHLEN );
+
+ Attributes = GetFileAttributes( SourcePath );
+
+ if ( Attributes == (DWORD) -1 ) {
+
+ //
+ // Source doesn't exist, bad syntax, or something along those lines.
+ //
+ ApiStatus = (NET_API_STATUS) GetLastError();
+ NetpAssert( ApiStatus != NO_ERROR );
+
+ // Log this!
+ ReplErrorLog(
+ NULL, // local (no server name)
+ NELOG_ReplUpdateError, // log code
+ ApiStatus,
+ DestPath,
+ SourcePath );
+
+ // BUGBUG: Log on remote server too if we got UNC name.
+
+ } else {
+ if ((Attributes & FILE_ATTRIBUTE_DIRECTORY) == 0) {
+
+ //
+ // Simple case: just a single file.
+ // ReplCopyFile() will copy data, attributes, everything!
+ //
+ ApiStatus = ReplCopyFile(
+ SourcePath,
+ DestPath,
+ FALSE ); // Don't fail if exists.
+
+ // (Error already logged by ReplCopyFile.)
+
+ } else {
+
+ //
+ // It's a directory tree.
+ // Set up large buffers and call the worker to handle this.
+ //
+ (void) STRCPY( FullSourceBuffer, SourcePath );
+ (void) STRCPY( FullDestBuffer, DestPath );
+
+ ApiStatus = ReplCopyRestOfTree(
+ FullSourceBuffer,
+ FullDestBuffer );
+
+ // (Error already logged by ReplCopyRestOfTree.)
+
+ }
+ }
+
+ IF_DEBUG( SYNC ) {
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplCopyTree: tree copy of " FORMAT_LPTSTR " to "
+ FORMAT_LPTSTR " gave status " FORMAT_API_STATUS ".\n",
+ SourcePath, DestPath, ApiStatus ));
+ }
+
+ // No need to log error here, as it has already been done.
+
+ return (ApiStatus);
+
+} // ReplCopyTree.
+
+
+DBGSTATIC NET_API_STATUS
+ReplCopyRestOfTree(
+ IN LPTSTR SourcePath,
+ IN LPTSTR DestPath
+ )
+
+/*++
+
+Routine Description:
+
+ Scans recursivly thru an entire sub-tree copying files and directories.
+
+ Uses a depth-first algorithm.
+
+Arguments:
+
+ BUGBUG
+ Note - path of dir to be scanned. Must be alloc'ed as TCHAR[PATHLEN+1], as
+ this routine uses the space at the end for temporary stuff.
+
+Return Value:
+
+ NET_API_STATUS
+
+Threads:
+
+ Used by client and syncer threads.
+
+--*/
+
+{
+ NET_API_STATUS ApiStatus;
+ DWORD DestAttributes;
+ DWORD DestPathIndex;
+ REPL_WIN32_FIND_DATA SearchBuf;
+ LPREPL_FIND_HANDLE SearchHandle = INVALID_REPL_HANDLE;
+ DWORD SourcePathIndex;
+
+#define UNEXPECTED( apiName ) \
+ { \
+ NetpKdPrint(( PREFIX_REPL_CLIENT \
+ "ReplCopyRestOfTree: Unexpected status from " \
+ apiName " (" FORMAT_DWORD ").\n", ApiStatus )); \
+ }
+
+ SourcePathIndex = STRLEN(SourcePath);
+ NetpAssert( SourcePathIndex < PATHLEN );
+ DestPathIndex = STRLEN(DestPath);
+ NetpAssert( DestPathIndex < PATHLEN );
+
+ //
+ // Prevent trashing a file with this directory.
+ //
+
+ DestAttributes = GetFileAttributes( DestPath );
+
+ if ((DestAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) {
+
+ ApiStatus = ERROR_ALREADY_EXISTS;
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplCopyRestOfTree: *ERROR* "
+ "Copying dir to file - invalid.\n" ));
+ goto Cleanup;
+ }
+
+ IF_DEBUG( MAJOR ) {
+ if (DestAttributes == (DWORD)-1) {
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplCopyRestOfTree: Creating directory "
+ FORMAT_LPTSTR "...\n", DestPath ));
+ } else {
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplCopyRestOfTree: Updating directory "
+ FORMAT_LPTSTR "...\n", DestPath ));
+ }
+ }
+
+ //
+ // Make sure dest directory exists, with right attributes, EAs, etc.
+ //
+
+ ApiStatus = ReplCopyDirectoryItself(
+ SourcePath, // place to copy security, timestamp, etc from.
+ DestPath, // Name of the new directory.
+ FALSE); // Don't fail if it already exists.
+
+ if (ApiStatus != NO_ERROR) {
+ UNEXPECTED( "ReplCopyDirectoryItself" );
+ goto Cleanup;
+ }
+
+
+ //
+ // Setup to scan this directory for files and directories.
+ //
+ (void) STRCAT( SourcePath, SLASH );
+ (void) STRCAT( SourcePath, STAR_DOT_STAR );
+ IF_DEBUG( SYNC ) {
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplCopyRestOfTree: Processing source " FORMAT_LPTSTR
+ ", dest " FORMAT_LPTSTR ".\n", SourcePath, DestPath ));
+ }
+
+ SearchHandle = ReplFindFirstFile( SourcePath, &SearchBuf);
+ SourcePath[SourcePathIndex] = TCHAR_EOS;
+
+ if (SearchHandle == INVALID_REPL_HANDLE) {
+
+ ApiStatus = (NET_API_STATUS) GetLastError();
+ UNEXPECTED( "ReplFindFirstFile" );
+ goto Cleanup;
+ }
+
+ //
+ // Loop for each files and directory in this directory.
+ //
+ do {
+ //
+ // Quit if service is stopping.
+ //
+
+ if (ReplGlobalIsServiceStopping) {
+ ApiStatus = ERROR_OPERATION_ABORTED;
+ goto Cleanup;
+ }
+
+ //
+ // Ignore REPL.INI, USERLOCK.*, ., .., and so on.
+ //
+
+ if ( ReplIgnoreDirOrFileName( SearchBuf.fdFound.cFileName ) ) {
+ continue; // Skip to next one.
+ }
+
+ // Append "\".
+ SourcePath[SourcePathIndex] = TCHAR_BACKSLASH;
+ DestPath[DestPathIndex] = TCHAR_BACKSLASH;
+
+ // Append sub-dir or file name name.
+ (void) STRCPY(
+ SourcePath + SourcePathIndex + 1,
+ SearchBuf.fdFound.cFileName);
+ NetpAssert( STRLEN( SourcePath ) <= PATHLEN );
+
+ (void) STRCPY(
+ DestPath + DestPathIndex + 1,
+ SearchBuf.fdFound.cFileName);
+ NetpAssert( STRLEN( DestPath ) <= PATHLEN );
+
+ if (SearchBuf.fdFound.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
+
+ //
+ // Recursively copy this subdirectory.
+ //
+
+ ApiStatus = ReplCopyRestOfTree( SourcePath, DestPath );
+ if (ApiStatus != NO_ERROR) {
+ UNEXPECTED( "ReplCopyRestOfTree" );
+ goto Cleanup;
+ }
+
+ } else {
+
+ IF_DEBUG( SYNC ) {
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplCopyRestOfTree: Copying file "
+ FORMAT_LPTSTR " to " FORMAT_LPTSTR ".\n",
+ SourcePath, DestPath ));
+ }
+
+ //
+ // Copy this individual file; don't fail if it already exists.
+ // ReplCopyFile() will copy data, attributes, everything.
+ //
+
+ ApiStatus = ReplCopyFile(SourcePath, DestPath, FALSE);
+ if (ApiStatus != NO_ERROR) {
+ UNEXPECTED( "ReplCopyFile" );
+ goto Cleanup;
+ }
+
+ }
+
+ // Reset path names for next pass through the loop.
+ SourcePath[SourcePathIndex] = TCHAR_EOS;
+ DestPath[DestPathIndex] = TCHAR_EOS;
+
+
+ } while (ReplFindNextFile(SearchHandle, &SearchBuf));
+
+ ApiStatus = NO_ERROR;
+
+Cleanup:
+ IF_DEBUG( SYNC ) {
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplCopyRestOfTree() is done, stat="
+ FORMAT_API_STATUS ".\n", ApiStatus ));
+ }
+
+ if (SearchHandle != INVALID_REPL_HANDLE) {
+ if( !ReplFindClose(SearchHandle) ) {
+
+ ApiStatus = (NET_API_STATUS) GetLastError();
+ UNEXPECTED( "ReplFindClose" );
+ NetpAssert( ApiStatus != NO_ERROR );
+
+ }
+ }
+
+ //
+ // Log error.
+ //
+ if (ApiStatus != NO_ERROR) {
+
+ // Log this locally.
+ ReplErrorLog(
+ NULL, // local (no server name)
+ NELOG_ReplUpdateError, // log code
+ ApiStatus,
+ DestPath,
+ SourcePath );
+
+ // BUGBUG: Log error on remote server too, if we got UNC name.
+ }
+
+ //
+ // Reset path names, now that we've logged them.
+ //
+ SourcePath[SourcePathIndex] = TCHAR_EOS;
+ DestPath[DestPathIndex] = TCHAR_EOS;
+
+ return (ApiStatus);
+
+} // ReplCopyRestOfTree
diff --git a/private/net/svcdlls/repl/server/entcount.c b/private/net/svcdlls/repl/server/entcount.c
new file mode 100644
index 000000000..27d92c34a
--- /dev/null
+++ b/private/net/svcdlls/repl/server/entcount.c
@@ -0,0 +1,241 @@
+/*++
+
+Copyright (c) 1987-1993 Microsoft Corporation
+
+Module Name:
+
+ EntCount.c
+
+Abstract:
+
+ ReplCountDirectoryEntries().
+
+Author:
+
+ JR (John Rogers, JohnRo@Microsoft) 11-Jan-1993
+
+Environment:
+
+ User mode only.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+ 11-Jan-1993 JohnRo
+ Created for RAID 6710: repl cannot manage dir with 2048 files.
+ (Actually, did massive cut and paste from ReplDirSort2.)
+ 19-Apr-1993 JohnRo
+ RAID 829: replication giving system error 38 (ERROR_HANDLE_EOF).
+ Use NetpKdPrint() where possible.
+
+--*/
+
+
+// These must be included first:
+
+#include <windows.h> // IN, DWORD, etc.
+#include <lmcons.h>
+
+// These can be in any order:
+
+#include <filefind.h> // REPL_WIN32_FIND_DATA, my prototype.
+#include <netdebug.h> // NetpAssert(), etc.
+#include <prefix.h> // PREFIX_ equates.
+#include <repldefs.h> // IF_DEBUG(), DOT, etc.
+#include <tstr.h> // STRCPY(), TCHAR_EOS, etc.
+
+
+NET_API_STATUS
+ReplCountDirectoryEntries(
+ IN LPCTSTR FullDirPath,
+ OUT LPDWORD EntryCountPtr
+ )
+
+/*++
+
+Routine Description:
+
+ This just counts the entries (dirs and files) in the given directory.
+ Skips "." and ".." but counts all others.
+
+Arguments:
+
+ FullDirPath - Gives full dir name of directory. This must be a UNC name
+ or an absolute path (including drive).
+
+ EntryCountPtr - Returns the number of directory entries used.
+
+Return Value:
+
+ NET_API_STATUS.
+
+Threads:
+
+ Only called by syncer thread.
+
+--*/
+
+{
+ NET_API_STATUS ApiStatus;
+ DWORD ActualCount = 0;
+ REPL_WIN32_FIND_DATA FindBuffer;
+ LPREPL_FIND_HANDLE FindHandle = INVALID_REPL_HANDLE;
+ TCHAR SearchPattern[PATHLEN+4+1]; // "name\*.*"
+
+
+ //
+ // Check for caller's errors.
+ //
+ NetpAssert( FullDirPath != NULL );
+ NetpAssert( (*FullDirPath) != TCHAR_EOS );
+
+ //
+ // Append \*.* to the path
+ //
+
+ (VOID) STRCPY( SearchPattern, FullDirPath );
+ (void) STRCAT( SearchPattern, SLASH );
+ (void) STRCAT( SearchPattern, STAR_DOT_STAR );
+
+ IF_DEBUG( FILEFIND ) {
+ NetpKdPrint(( PREFIX_REPL
+ "ReplCountDirectoryEntries: searching '" FORMAT_LPTSTR "'.\n",
+ FullDirPath ));
+ }
+
+ //
+ // Find the first file.
+ //
+
+ FindHandle = ReplFindFirstFile(
+ (LPTSTR) SearchPattern,
+ &FindBuffer );
+
+ if ( FindHandle == INVALID_REPL_HANDLE ) {
+
+ ApiStatus = (NET_API_STATUS) GetLastError();
+
+
+ //
+ // This error means there was a connection to the REPL$ share
+ // on the srever that went away (the server went down and has
+ // come up again, or more likely the REPL on the server was stopped
+ // and restarted) so we must retry to establish the connection again.
+ //
+
+ if ( ApiStatus == ERROR_NETNAME_DELETED ) {
+ FindHandle = ReplFindFirstFile(
+ (LPTSTR) SearchPattern,
+ &FindBuffer );
+
+ if ( FindHandle == INVALID_REPL_HANDLE ) {
+ ApiStatus = (NET_API_STATUS) GetLastError();
+ } else {
+ ApiStatus = NO_ERROR;
+ }
+ }
+
+ //
+ // Now a Hack - the server maps access denied to one of the
+ // following error codes, to verify whether it is really an access
+ // denied error we need a different api.
+ //
+
+ if ( ApiStatus == ERROR_NO_MORE_FILES ||
+ ApiStatus == ERROR_PATH_NOT_FOUND ) {
+
+ //
+ // If there really are no files in the directory,
+ // just indicate so.
+ //
+
+ if ( ReplFileOrDirExists( FullDirPath ) ) {
+ NetpAssert( ActualCount == 0 );
+ ApiStatus = NO_ERROR;
+ goto Cleanup;
+ }
+
+
+ //
+ // The LM2.1 code didn't generate an alert for ERROR_PATH_NOT_FOUND.
+ // I'll just join the common error code.
+ //
+
+ ApiStatus = (NET_API_STATUS) GetLastError();
+ goto Cleanup;
+
+ } else if ( ApiStatus != NO_ERROR ) {
+ goto Cleanup;
+ }
+ }
+
+ //
+ // do FindNext until exhausted or count reached.
+ //
+
+ do {
+
+ NetpAssert(
+ (FindBuffer.fdFound.dwFileAttributes)
+ != ( (DWORD)-1 ) );
+
+
+ //
+ // Skip over "." and ".." once and for all.
+ //
+
+ if ( STRCMP( FindBuffer.fdFound.cFileName, DOT) == 0 ||
+ STRCMP( FindBuffer.fdFound.cFileName, DOT_DOT) == 0 ) {
+
+ // Don't count these.
+ continue;
+ }
+
+
+ ++ActualCount;
+
+ } while ( ReplFindNextFile( FindHandle, &FindBuffer ));
+
+ //
+ // FindNext failed (perhaps with ERROR_NO_MORE_FILES).
+ //
+
+ ApiStatus = (NET_API_STATUS) GetLastError();
+ if (ApiStatus == ERROR_NO_MORE_FILES) {
+ ApiStatus = NO_ERROR;
+ }
+
+ //
+ // Clean up
+ //
+
+Cleanup:
+
+ //
+ // Free any locally used resources.
+ //
+
+ if ( FindHandle != INVALID_REPL_HANDLE ) {
+ (void) ReplFindClose( FindHandle );
+ }
+
+ if (EntryCountPtr != NULL) {
+ *EntryCountPtr = ActualCount;
+ }
+
+ IF_DEBUG( FILEFIND ) {
+ NetpKdPrint(( PREFIX_REPL
+ "ReplCountDirectoryEntries: returning " FORMAT_DWORD
+ " entries and status " FORMAT_API_STATUS ".\n",
+ ActualCount, ApiStatus ));
+ }
+
+ if (ApiStatus == ERROR_HANDLE_EOF) {
+ // BUGBUG: quiet this debug output eventually.
+ NetpKdPrint(( PREFIX_REPL
+ "ReplCountDirectoryEntries: GOT HANDLE EOF!\n" ));
+ }
+
+ return ApiStatus;
+
+}
diff --git a/private/net/svcdlls/repl/server/error.c b/private/net/svcdlls/repl/server/error.c
new file mode 100644
index 000000000..94916d7a9
--- /dev/null
+++ b/private/net/svcdlls/repl/server/error.c
@@ -0,0 +1,566 @@
+/*++
+
+Copyright (c) 1987-1993 Microsoft Corporation
+
+Module Name:
+
+ error.c
+
+Abstract:
+
+ Contains routines for error handling.
+
+ These routines are shared by the client and master.
+
+Author:
+
+ Ported from cli_eror.c and mas_eror.c from Lan Man 2.1
+
+Environment:
+
+ User mode only.
+ Contains NT-specific code.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+ 15-Apr-1989 (yuv)
+ Initial Coding.
+
+ 09-Oct-1991 (cliffv)
+ Ported to NT. Converted to NT style.
+
+ 17-Dec-1991 JohnRo
+ Use BOOL (Win32) rather than BOOLEAN (NT) where possible..
+ 16-Jan-1992 JohnRo
+ Avoid using private logon functions.
+ Changed file name from repl.h to replgbl.h to avoid MIDL conflict.
+ Fixed bug regarding returned value from NetpReplWriteMail functions.
+ ReportStatus() should add thread ID to status being reported.
+ 24-Jan-1992 JohnRo
+ Changed to use LPTSTR etc.
+ Added ReplConfigReportBadParmValue() (RPC server-side version).
+ 13-Feb-1992 JohnRo
+ Set up to dynamically change role.
+ Use FORMAT equates.
+ 16-Feb-1992 JohnRo
+ Added debug output to ReportStatus().
+ 22-Feb-1992 JohnRo
+ Minor debug output enhancements.
+ 05-Mar-1992 JohnRo
+ Changed interface to match new service controller.
+ 22-Mar-1992 JohnRo
+ Minor debug and comment changes.
+ 24-Mar-1992 JohnRo
+ Control more debug output by new trace bits.
+ 04-Apr-1992 JohnRo
+ Added NetAlertRaise() and NetAlertRaiseEx() APIs.
+ 13-Jul-1992 JohnRo
+ RAID 10503: srv mgr: repl dialog doesn't come up.
+ Report error coming back from SetServiceStatus().
+ Use PREFIX_ equates.
+ 18-Aug-1992 JohnRo
+ RAID 2115: repl svc should wait while stopping or changing roles.
+ Fixed UNICODE bugs in MergeStrings() and RaiseAlert().
+ 24-Sep-1992 JohnRo
+ RAID 1091 (set wait hint > 0).
+ 05-Dec-1992 JohnRo
+ RAID 3844: remote NetReplSetInfo uses local machine type.
+ 08-Dec-1992 JohnRo
+ RAID 3316: access violation while stopping the replicator
+ 15-Jan-1993 JohnRo
+ RAID 7717: Repl assert if not logged on correctly. (Also do event
+ logging for real.)
+ Extracted ErrorLog (now ReplErrorLog) for common use.
+ Made some changes suggested by PC-LINT 5.0
+ Use NetpKdPrint() where possible.
+ 26-Apr-1993 JohnRo
+ Set global uninstall code in AlertLogExit just in case.
+ Added more debug output if ReplFinish gets called.
+ 24-May-1993 JohnRo
+ RAID 10587: repl could deadlock with changed NetpStopRpcServer(), so
+ just call ExitProcess() instead.
+
+--*/
+
+
+// These must be included first:
+
+#define NOMINMAX // Let stdlib.h define min() and max()
+#include <windows.h> // IN, DWORD, etc.
+#include <lmcons.h>
+
+// These may be included in any order:
+
+#include <lmalert.h> // NetAlertRaiseEx(), ALERT_*_EVENT equates, etc.
+#include <lmerr.h> // (Needed by SET_SERVICE_STATUS macro.)
+#include <lmerrlog.h> // NELOG_* defines.
+#include <lmsname.h> // SERVICE_REPL.
+#include <netdebug.h> // DBGSTATIC, NetpKdPrint(), FORMAT_ equates, etc.
+#include <netlib.h> // NetpMemoryAllocate().
+#include <prefix.h> // PREFIX_ equates.
+#include <repldefs.h>
+#include <replgbl.h> // ReplGlobal variables.
+#include <tstr.h> // STRSIZE(), etc.
+#include <winsvc.h> // SERVICE_STATUS(), etc.
+
+
+
+DBGSTATIC LPTSTR
+MergeStrings(
+ IN LPTSTR str_p,
+ IN LPTSTR buf_start,
+ IN LPTSTR next_p
+ )
+/*++
+
+Routine Description:
+
+
+ Copies string into alert buffer. Doesn't allow buffer overflow
+ excess simply discarded
+
+
+Arguments:
+
+ str_p - String to concatenate onto end of the alert buffer.
+
+ buf_start - Beginning of alert buffer. This is assumed to have enough
+ room for ALERTSZ TCHARs.
+
+ next_p - Pointer to next available byte in alert buffer.
+
+Return Value:
+
+ Returns pointer to next empty character in alert buffer.
+ Returns NULL if there is no more room.
+
+--*/
+{
+ DWORD CharCount;
+
+ if ( next_p == NULL ) {
+ return NULL;
+ }
+
+ CharCount = (DWORD) (STRLEN(str_p) + 1);
+ if ((next_p + CharCount - buf_start) < ALERTSZ) {
+ (VOID) STRCPY( next_p, str_p );
+ return (next_p + CharCount);
+ }
+
+ return NULL;
+}
+
+
+
+DBGSTATIC VOID
+RaiseAlert(
+ IN DWORD alert_no,
+ IN NET_API_STATUS syserr_no,
+ IN LPTSTR str1 OPTIONAL,
+ IN LPTSTR str2 OPTIONAL
+ )
+/*++
+
+Routine Description:
+
+ Raise REPL service specific Admin alerts.
+
+Arguments:
+
+ alert_no - The alert to be raised, text in alertmsg.h
+
+ syserr_no - The system / net error code the caused this condition, zero
+ (== 0) if none.
+
+ str1 str2 - optional merger strings for the alert, should be
+ NULL when not used
+
+Return Value:
+
+ None.
+
+--*/
+{
+
+ NET_API_STATUS ApiStatus;
+ LPTSTR start;
+ LPTSTR nexts;
+
+ // BYTE message[ALERTSZ + sizeof(STD_ALERT) + sizeof(ADMIN_OTHER_INFO)];
+ TCHAR message[ALERTSZ + sizeof(ADMIN_OTHER_INFO)];
+ // PSTD_ALERT alert = (PSTD_ALERT) message;
+ PADMIN_OTHER_INFO admin = (LPVOID) message;
+
+ IF_DEBUG( REPL ) {
+ NetpKdPrint(( PREFIX_REPL "alert: '" FORMAT_DWORD "'", alert_no ));
+ }
+
+ //
+ // setup fixed portion.
+ //
+
+ // alert->alrt_timestamp = NetpReplTimeNow();
+
+ // (VOID) STRCPY ( alert->alrt_servicename, SERVICE_REPL );
+ // (VOID) STRCPY(alert->alrt_eventname, ALERT_ADMIN_EVENT);
+
+ admin->alrtad_errcode = alert_no;
+ admin->alrtad_numstrings = 0;
+
+ start = (LPTSTR) ALERT_VAR_DATA(admin);
+ nexts = start;
+
+ //
+ // If a system/net error is specified,
+ // Convert it to text and put it in the buffer.
+ //
+
+ if (syserr_no != 0) {
+ TCHAR strtmp[DWORDLEN]; // long enough for any sys/net NetStatus
+
+ (VOID) ULTOA(syserr_no, strtmp, RADIX);
+
+ nexts = MergeStrings(strtmp, start, nexts);
+ admin->alrtad_numstrings += 1;
+
+ IF_DEBUG( REPL ) {
+ NetpKdPrint(( " " FORMAT_API_STATUS, syserr_no ));
+ }
+ }
+
+ //
+ // now take care of (optional) char strings.
+ //
+
+ if (str1 != NULL ) {
+ nexts = MergeStrings(str1, start, nexts);
+ admin->alrtad_numstrings += 1;
+
+ IF_DEBUG( REPL ) {
+ NetpKdPrint(( " '" FORMAT_LPTSTR "'", str1 ));
+ }
+ }
+
+ if (str2 != NULL ) {
+ nexts = MergeStrings(str2, start, nexts);
+ admin->alrtad_numstrings += 1;
+
+ IF_DEBUG( REPL ) {
+ NetpKdPrint(( " '" FORMAT_LPTSTR "'", str2 ));
+ }
+ }
+
+ IF_DEBUG( REPL ) {
+ NetpKdPrint(( "\n" ));
+ }
+
+
+ ApiStatus = NetAlertRaiseEx(
+ (LPTSTR) ALERT_ADMIN_EVENT, // alert name
+ message, // variable part of alert
+ (DWORD) ((PCHAR)nexts - (PCHAR)message), // variable size
+ (LPTSTR) SERVICE_REPL ); // my service name.
+
+ if (ApiStatus != NO_ERROR) {
+ NetpKdPrint(( PREFIX_REPL "Error calling NetAlertRaiseEx "
+ FORMAT_API_STATUS "\n", ApiStatus ));
+ }
+
+}
+
+
+
+VOID
+AlertLogExit(
+ IN NET_API_STATUS alert_code,
+ IN NET_API_STATUS errlog_code,
+ IN NET_API_STATUS sys_code,
+ IN LPTSTR str1,
+ IN LPTSTR str2,
+ IN BOOL exit_flag
+ )
+/*++
+
+Routine Description:
+
+ Reports to error log, raises alert, and forces the service to exit.
+
+Arguments:
+
+ alert_code - Alert code. Text is in alertmsg.h. If 0, no alert is
+ generated.
+
+ errlog_code - Net error log code. Text is in lmerrlog.h. If 0, no error
+ log entry is generated.
+
+ sys_code - The system / net error code the caused this condition,
+ zero if none.
+
+ str1-str2 - optional merger strings for the alert, should be NULL
+ when not used.
+
+ exit_flag - True if the condition is fatal and the Replicator service
+ should exit.
+
+Return Value:
+
+ None.
+
+--*/
+{
+
+
+ if (errlog_code) {
+ ReplErrorLog(
+ NULL, // no server name
+ errlog_code,
+ sys_code,
+ str1,
+ str2);
+ }
+
+ if (alert_code) {
+ RaiseAlert(alert_code, sys_code, str1, str2);
+ }
+
+ if (exit_flag) {
+
+ // Save error code so this can be reported to service controller
+ // when the service uninstall completes.
+
+ if (sys_code != NO_ERROR) {
+ ReplGlobalUninstallUicCode = sys_code;
+ }
+
+ //
+ // Tell the entire service to exit. ReplStopService() will tell
+ // the service controller and kill the process.
+ //
+ // Note: we can't call ReplFinish() here, because of a possible
+ // infinite loop. (ReplFinish->ReportStatus->AlertLogExit.)
+ //
+ // We also can't call ReplChangeRole(), because of another infinite
+ // loop: ReplChangeRole->ExportDirStartRepl->ReplFinish->
+ // ReportStatus->AlertLogExit.
+ //
+ ReplStopService( );
+ /*NOTREACHED*/
+
+ }
+
+} // AlertLogExit
+
+
+
+// Routine to report a bad parm value.
+// There are two different versions of this routine,
+// in client/report.c and server/error.c
+// client/report.c is callable whether or not service is started.
+// server/error.c only runs when service is starting; it talks to the service
+// controller.
+
+VOID
+ReplConfigReportBadParmValue(
+ IN LPTSTR UncServerName OPTIONAL, // Must be local, might be explicit.
+ IN LPTSTR SwitchName,
+ IN LPTSTR TheValue OPTIONAL
+ )
+{
+ UNREFERENCED_PARAMETER( SwitchName );
+ UNREFERENCED_PARAMETER( UncServerName );
+
+ NetpAssert( SwitchName != NULL );
+
+ IF_DEBUG(REPL) {
+ NetpKdPrint(( PREFIX_REPL "Bad value to '" FORMAT_LPTSTR "' switch.\n",
+ SwitchName ));
+ if (TheValue != NULL) {
+ NetpKdPrint(( PREFIX_REPL "Value given was '" FORMAT_LPTSTR "'.\n",
+ TheValue ));
+ }
+ }
+ ReplFinish( ERROR_INVALID_DATA );
+
+} // ReplConfigReportBadParmValue
+
+
+VOID
+ReplFinish (
+ IN NET_API_STATUS ApiStatus
+ )
+/*++
+
+Routine Description:
+
+ Report a fatal initialization error. (parm out of range, missing
+ or system errors). Reports stop pending condition to service status.
+
+ This routine then cleans up and exits the process.
+
+Arguments:
+
+ ApiStatus - Caller's NetStatus code returned by a faulty system / API call.
+
+Return Value:
+
+ DOES NOT RETURN.
+
+--*/
+{
+ if (ApiStatus != NO_ERROR) {
+ NetpKdPrint(( PREFIX_REPL "TERMINATION api status " FORMAT_API_STATUS
+ "\n", ApiStatus ));
+ ReplErrorLog(
+ NULL, // no server name
+ NELOG_ReplSysErr,
+ ApiStatus,
+ NULL,
+ NULL);
+ }
+ IF_DEBUG( SVCCTRL ) {
+ NetpKdPrint(( PREFIX_REPL
+ "TERMINATION about to set Status to stop pending\n" ));
+ }
+
+
+ // Save error code so this can be reported to service controller
+ // when the service uninstall completes.
+
+ ReplGlobalUninstallUicCode = ApiStatus;
+
+ // report uninstall started ..
+
+ ReportStatus(
+ SERVICE_STOP_PENDING,
+ NO_ERROR, // exit code (we'll tell about error later)
+ REPL_WAIT_HINT,
+ 0 ); // checkpoint
+
+ //
+ // Tell the entire service to exit. ReplStopService() will tell
+ // the service controller and kill the process.
+ //
+ // Note: We can't call ReplChangeRole(), because of an infinite
+ // loop: ReplChangeRole->ExportDirStartRepl->ReplFinish.
+ //
+ ReplStopService();
+ /*NOTREACHED*/
+
+} // ReplFinish
+
+
+
+
+
+
+VOID
+ReportStatus(
+ IN DWORD State,
+ IN NET_API_STATUS ApiStatus,
+ IN DWORD WaitHint,
+ IN DWORD CheckPoint
+ )
+/*++
+
+Routine Description:
+
+ Report the current status to the service controller and update the global
+ service status (in case we're polled later).
+
+Arguments:
+
+ State - Current state of the service.
+
+ ApiStatus - Code for particular state.
+
+ WaitHint - BUGBUG.
+
+ CheckPoint - BUGBUG.
+
+
+Return Value:
+
+ None.
+
+--*/
+{
+ NET_API_STATUS NetStatus;
+ SERVICE_STATUS ServiceStatus;
+
+ IF_DEBUG( SVCCTRL ) {
+ NetpKdPrint(( PREFIX_REPL
+ "ReportStatus: state " FORMAT_DWORD ", api status "
+ FORMAT_API_STATUS ", wait hint " FORMAT_DWORD
+ ", checkpoint " FORMAT_DWORD ".\n",
+ State, ApiStatus, WaitHint, CheckPoint ));
+ }
+
+#if DBG
+ if (State == SERVICE_STOPPED) {
+ NetpAssert( WaitHint == 0 );
+ NetpAssert( CheckPoint == 0 );
+ } else if (State == SERVICE_STOP_PENDING) {
+ NetpAssert( WaitHint != 0 );
+ // BUGBUG: How about an assert for CheckPoint here?
+ } else {
+ NetpAssert( ApiStatus == NO_ERROR );
+ }
+#endif
+
+ // Prevent ERROR_INVALID_DATA from SetServiceStatus...
+ if (State == SERVICE_RUNNING) {
+ WaitHint = 0;
+ CheckPoint = 0;
+ }
+
+ //
+ // initialize service status:
+ //
+
+ ServiceStatus.dwServiceType = SERVICE_WIN32;
+ ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
+ ServiceStatus.dwCurrentState = State;
+ ServiceStatus.dwWaitHint = WaitHint;
+ ServiceStatus.dwCheckPoint = CheckPoint;
+
+ SET_SERVICE_EXITCODE(
+ ApiStatus,
+ ServiceStatus.dwWin32ExitCode,
+ ServiceStatus.dwServiceSpecificExitCode
+ );
+
+ //
+ // Tell service controller what's up.
+ //
+ if ( !SetServiceStatus(ReplGlobalServiceHandle, &ServiceStatus)) {
+ NetStatus = (NET_API_STATUS) GetLastError();
+
+ NetpKdPrint(( PREFIX_REPL "ReportStatus: "
+ "Unexpected return code " FORMAT_API_STATUS
+ " from SetServiceStatus().", NetStatus ));
+
+#if DBG
+ {
+ LPSERVICE_STATUS ss = &ServiceStatus;
+
+ NetpAssert( sizeof(SERVICE_STATUS_HANDLE) == sizeof(DWORD) );
+ NetpKdPrint(( PREFIX_REPL "Service handle contents: "
+ FORMAT_HEX_DWORD ".\n", (DWORD) ReplGlobalServiceHandle ));
+
+ NetpKdPrint(( PREFIX_REPL "Service status contents:\n" ));
+ NetpAssert( ss != NULL );
+ NetpKdPrint(( " state=" FORMAT_DWORD " controls=" FORMAT_HEX_DWORD
+ " Win32 status=" FORMAT_API_STATUS " net status=" FORMAT_API_STATUS
+ " checkpoint=" FORMAT_DWORD " wait hint=" FORMAT_DWORD "\n",
+ ss->dwCurrentState, ss->dwControlsAccepted, ss->dwWin32ExitCode,
+ ss->dwServiceSpecificExitCode, ss->dwCheckPoint, ss->dwWaitHint ));
+
+ }
+#endif
+
+ AlertLogExit(0, NELOG_ReplNetErr, NetStatus, NULL, NULL, EXIT);
+ }
+
+} // ReportStatus
diff --git a/private/net/svcdlls/repl/server/expadd.c b/private/net/svcdlls/repl/server/expadd.c
new file mode 100644
index 000000000..66d587182
--- /dev/null
+++ b/private/net/svcdlls/repl/server/expadd.c
@@ -0,0 +1,254 @@
+/*++
+
+Copyright (c) 1992-1993 Microsoft Corporation
+
+Module Name:
+
+ ExpAdd.c
+
+Abstract:
+
+ This file contains NetrReplExportDirAdd.
+
+Author:
+
+ John Rogers (JohnRo) 08-Jan-1992
+
+Environment:
+
+ Runs under Windows NT.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+ 08-Jan-1992 JohnRo
+ Created.
+ 20-Jan-1992 JohnRo
+ Avoid possible duplicate entry.
+ 20-Jan-1992 JohnRo
+ Changed prototype to match MIDL requirements.
+ Netr prototypes are now generated by MIDL and put in repl.h.
+ 20-Jan-1992 JohnRo
+ Call ExportDirIsApiRecordValid() to do some checking.
+ 24-Jan-1992 JohnRo
+ Changed to use LPTSTR etc.
+ 15-Mar-1992 JohnRo
+ Update registry with new values.
+ 22-Mar-1992 JohnRo
+ Fixed bug handling union in debug output.
+ 08-May-1992 JohnRo
+ Fixed link problem in free (nondebug) build.
+ 22-Jul-1992 JohnRo
+ RAID 2274: repl svc should impersonate caller.
+ 02-Nov-1992 JohnRo
+ RAID 7962: Repl APIs in wrong role kill svc.
+ 02-Apr-1993 JohnRo
+ Use NetpKdPrint() where possible.
+
+--*/
+
+
+// These must be included first:
+
+#include <windef.h> // IN, VOID, LPTSTR, etc.
+#include <lmcons.h> // NET_API_STATUS, PARM equates, etc.
+#include <repldefs.h> // IF_DEBUG(), etc.
+#include <master.h> // PMASTER_LIST_REC, RMGlobalListLock.
+#include <rpc.h> // Needed by <repl.h>.
+
+// These can be in any order:
+
+#include <expdir.h> // ExportDirIsApiRecordValid(), etc.
+#include <lmrepl.h> // LPREPL_EDIR_INFO_1, REPL_EXTENT_ stuff, etc.
+#include <masproto.h> // GetMasterRecord(), NewMasterRecord().
+#include <netdebug.h> // NetpAssert(), NetpKdPrint(), etc.
+#include <netlib.h> // NetpSetParmError().
+#include <netlock.h> // ACQUIRE_LOCK(), etc.
+#include <repl.h> // My prototype (in MIDL-generated .h file).
+#include <replgbl.h> // ReplConfigLock, ReplConfigRole.
+#include <rpcutil.h> // NetpImpersonateClient(), NetpRevertToSelf().
+#include <winerror.h> // ERROR_ equates, NO_ERROR.
+
+
+NET_API_STATUS
+NetrReplExportDirAdd (
+ IN LPTSTR UncServerName OPTIONAL,
+ IN DWORD Level,
+ IN LPEXPORT_CONTAINER Buf, // RPC container (union)
+ OUT LPDWORD ParmError OPTIONAL // name used by NetpSetParmError() macro.
+ )
+
+/*++
+
+Routine Description:
+
+ Same as NetReplExportDirAdd.
+
+Arguments:
+
+ Same as NetReplExportDirAdd.
+
+Return Value:
+
+ Same as NetReplExportDirAdd.
+
+--*/
+
+{
+ LPREPL_EDIR_INFO_1 ApiRecord;
+ NET_API_STATUS ApiStatus;
+ BOOL ConfigLocked = FALSE;
+ BOOL Impersonated = FALSE;
+ PMASTER_LIST_REC InternalRecord;
+ BOOL ListLocked = FALSE;
+
+ //
+ // Check for caller errors.
+ //
+ NetpSetParmError( PARM_ERROR_UNKNOWN ); // Assume error until proven...
+ if (Buf == NULL) {
+ return (ERROR_INVALID_PARAMETER);
+ }
+ if (Level != 1) {
+ return (ERROR_INVALID_LEVEL);
+ }
+
+ if ( ! ExportDirIsApiRecordValid (
+ Level,
+ Buf->Info1,
+ ParmError) ) {
+
+ return (ERROR_INVALID_PARAMETER);
+ }
+
+ ApiRecord = Buf->Info1;
+ NetpAssert( ApiRecord != NULL );
+
+ IF_DEBUG(EXPAPI) {
+ NetpKdPrint(( "NetrReplExportDirAdd: adding API record...\n" ));
+ NetpDbgDisplayReplExportDir( Level, Buf->Info1 );
+ }
+
+ //
+ // Impersonate caller, so security check (write to registry) reflects
+ // the client's process, not the repl service process.
+ //
+ ApiStatus = NetpImpersonateClient();
+ if (ApiStatus != NO_ERROR) {
+ goto Cleanup;
+ }
+ Impersonated = TRUE;
+
+ //
+ // Get a shared lock on config data so we can see if role includes export.
+ //
+ ACQUIRE_LOCK_SHARED( ReplConfigLock );
+ ConfigLocked = TRUE;
+
+ //
+ // Check for duplicate record, one way or the other.
+ //
+ if (ReplRoleIncludesExport( ReplConfigRole ) ) {
+
+ //
+ // Get a lock on the global list which we want to add to.
+ //
+ if (ReplRoleIncludesExport( ReplConfigRole ) ) {
+ ACQUIRE_LOCK( RMGlobalListLock ); // Get excl lock.
+ ListLocked = TRUE;
+ }
+
+ // We're exporting, so just use export half's global data.
+ if (GetMasterRec( ApiRecord->rped1_dirname ) != NULL) {
+ // Oops, we got a duplicate.
+ ApiStatus = ERROR_ALREADY_EXISTS;
+ goto Cleanup; // Don't forget to release lock...
+ }
+
+ } else {
+
+ // Service is running but not exporting, so we can only use registry.
+ if (ExportDirConfigDataExists(
+ NULL, // no server name
+ ApiRecord->rped1_dirname) ) {
+
+ ApiStatus = ERROR_ALREADY_EXISTS;
+ goto Cleanup; // Don't forget to release lock...
+ }
+
+ }
+
+ //
+ // Write config data for this export directory.
+ // This has the side-effect of doing a security check.
+ //
+ ApiStatus = ExportDirWriteConfigData (
+ UncServerName,
+ ApiRecord->rped1_dirname,
+ ApiRecord->rped1_integrity,
+ ApiRecord->rped1_extent,
+ 0, // lock count
+ 0 ); // lock time (none) Seconds since 1970.
+ if (ApiStatus != NO_ERROR) {
+ goto Cleanup; // Don't forget to release lock...
+ }
+
+ if ( !ReplRoleIncludesExport( ReplConfigRole ) ) {
+ goto CleanupOK;
+ }
+
+ //
+ // Create a new master record and add it to the appropriate list(s).
+ // NewMasterRecord() will do any necessary locking and unlocking.
+ //
+ InternalRecord = NewMasterRecord(
+ ApiRecord->rped1_dirname,
+ ApiRecord->rped1_integrity,
+ ApiRecord->rped1_extent);
+
+ IF_DEBUG(EXPAPI) {
+ NetpKdPrint(( "NetrReplExportDirAdd: done adding API record, "
+ "got master record at " FORMAT_LPVOID ".\n",
+ (LPVOID) InternalRecord ));
+ }
+
+ //
+ // Assumption: NewMasterRecord can only fail if it can't allocate memory.
+ //
+ if (InternalRecord == NULL) {
+ // BUGBUG: Registry updated but not service? Oh well, we tried.
+ ApiStatus = ERROR_NOT_ENOUGH_MEMORY;
+ goto Cleanup; // Don't forget to release lock...
+ }
+
+ //
+ // I (JohnRo) was thinking about putting some assertion checking on
+ // InternalRecord here. However, I'm not sure we can depend on the
+ // pointer. What if some other process just did a ReplExportDirDel
+ // on the same name? We have a pointer to a record that's gone.
+ // So, never mind the assertion check.
+ //
+
+CleanupOK:
+ //
+ // Everything went OK. Tell caller.
+ //
+ NetpSetParmError( PARM_ERROR_NONE );
+ ApiStatus = NO_ERROR;
+
+Cleanup:
+ if (ListLocked) {
+ RELEASE_LOCK( RMGlobalListLock );
+ }
+
+ if (ConfigLocked) {
+ RELEASE_LOCK( ReplConfigLock );
+ }
+
+ if (Impersonated) {
+ (VOID) NetpRevertToSelf();
+ }
+
+ return (ApiStatus);
+
+}
diff --git a/private/net/svcdlls/repl/server/expdel.c b/private/net/svcdlls/repl/server/expdel.c
new file mode 100644
index 000000000..c2f204d5b
--- /dev/null
+++ b/private/net/svcdlls/repl/server/expdel.c
@@ -0,0 +1,154 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ ExpDel.c
+
+Abstract:
+
+ NetrReplExportDirDel is the local worker for the NetReplExportDirDel API.
+
+Author:
+
+ John Rogers (JohnRo) 07-Jan-1992
+
+Environment:
+
+ Runs under Windows NT.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+ 07-Jan-1992 JohnRo
+ Created.
+ 20-Jan-1992 JohnRo
+ Netr prototypes are now generated by MIDL and put in repl.h.
+ 24-Jan-1992 JohnRo
+ Changed to use LPTSTR etc.
+ 15-Mar-1992 JohnRo
+ Update registry with new values.
+ 22-Jul-1992 JohnRo
+ RAID 2274: repl svc should impersonate caller.
+ 13-Nov-1992 JohnRo
+ RAID 1537: Repl APIs in wrong role kill svc.
+
+--*/
+
+
+// These must be included first:
+
+#include <windef.h> // IN, VOID, LPTSTR, etc.
+#include <lmcons.h> // NET_API_STATUS, PARM equates, etc.
+#include <repldefs.h> // ReplIsIntegrityValid(), etc.
+#include <master.h> // PMASTER_LIST_REC.
+#include <rpc.h> // Needed by <repl.h>.
+
+// These can be in any order:
+
+#include <dirname.h> // ReplIsDirNameValid().
+#include <expdir.h> // ExportDirDeleteConfigData().
+#include <lmrepl.h> // LPREPL_EDIR_INFO_1, REPL_EXTENT_ stuff, etc.
+#include <masproto.h> // RemoveMasterRecForDirName().
+#include <netlock.h> // ACQUIRE_LOCK(), etc.
+#include <repl.h> // My prototype (in MIDL-generated .h file).
+#include <replgbl.h> // ReplConfigLock, ReplConfigRole.
+#include <rpcutil.h> // NetpImpersonateClient(), NetpRevertToSelf().
+#include <winerror.h> // ERROR_ equates, NO_ERROR.
+
+
+NET_API_STATUS
+NetrReplExportDirDel (
+ IN LPTSTR UncServerName OPTIONAL,
+ IN LPTSTR DirName
+ )
+
+/*++
+
+Routine Description:
+
+ Same as NetReplExportDirDel.
+
+Arguments:
+
+ Same as NetReplExportDirDel.
+
+Return Value:
+
+ Same as NetReplExportDirDel.
+
+--*/
+
+{
+ NET_API_STATUS ApiStatus;
+ BOOL ConfigLocked = FALSE;
+ BOOL Impersonated = FALSE;
+ BOOL ListLocked = FALSE;
+
+ if (ReplIsDirNameValid( DirName ) == FALSE) {
+ return (ERROR_INVALID_PARAMETER);
+ }
+
+ //
+ // Impersonate caller, so security check (write to registry) reflects
+ // the client's process, not the repl service process.
+ //
+ ApiStatus = NetpImpersonateClient();
+ if (ApiStatus != NO_ERROR) {
+ goto Cleanup;
+ }
+ Impersonated = TRUE;
+
+ //
+ // We'll need shared lock on config data, so we can find out if export half
+ // is actually running.
+ //
+ ACQUIRE_LOCK_SHARED( ReplConfigLock );
+ ConfigLocked = TRUE;
+
+ //
+ // Call somebody to delete from registry.
+ // This has the side-effect of doing a security check.
+ //
+ ApiStatus = ExportDirDeleteConfigData( UncServerName, DirName );
+ if (ApiStatus != NO_ERROR) {
+ goto Cleanup; // Don't forget to release lock...
+ }
+
+ if ( ReplRoleIncludesExport( ReplConfigRole ) ) {
+ //
+ // Get lock on global list (RemoveMasterRecForDirName needs this).
+ // This same lock protects export data in the registry.
+ //
+
+ ACQUIRE_LOCK( RMGlobalListLock );
+ ListLocked = TRUE;
+
+ //
+ // Remove from in-memory data being used by service.
+ //
+ ApiStatus = RemoveMasterRecForDirName( DirName );
+ // BUGBUG: registry and service out-of-sync if ApiStatus != NO_ERROR.
+ }
+
+ // BUGBUG: Is just removing the record from list enough or do we need
+ // to notify anybody?
+
+Cleanup:
+
+ if (Impersonated) {
+ (VOID) NetpRevertToSelf();
+ }
+
+ if (ListLocked) {
+ RELEASE_LOCK( RMGlobalListLock );
+ }
+
+ if (ConfigLocked) {
+ RELEASE_LOCK( ReplConfigLock );
+ }
+
+ return (ApiStatus);
+
+}
diff --git a/private/net/svcdlls/repl/server/expenum.c b/private/net/svcdlls/repl/server/expenum.c
new file mode 100644
index 000000000..f4547f36f
--- /dev/null
+++ b/private/net/svcdlls/repl/server/expenum.c
@@ -0,0 +1,311 @@
+/*++
+
+Copyright (c) 1992-1993 Microsoft Corporation
+
+Module Name:
+
+ ExpEnum.c
+
+Abstract:
+
+ This file contains NetrReplExportDirEnum.
+
+Author:
+
+ John Rogers (JohnRo) 08-Jan-1992
+
+Environment:
+
+ Runs under Windows NT.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+ 08-Jan-1992 JohnRo
+ Created.
+ 14-Jan-1992 JohnRo
+ Handle empty array correctly.
+ 20-Jan-1992 JohnRo
+ Changed prototype to match MIDL requirements.
+ Netr prototypes are now generated by MIDL and put in repl.h.
+ 24-Jan-1992 JohnRo
+ Changed to use LPTSTR etc.
+ Clarify units for time parameters.
+ Changed ExportDirBuildApiRecord's interface.
+ 23-Mar-1992 JohnRo
+ Fixed enum when service is running.
+ 12-Nov-1992 JohnRo
+ RAID 1537: repl APIs in wrong role kill service.
+ 10-Mar-1993 JohnRo
+ Use NetpKdPrint() where possible.
+ Made changes suggested by PC-LINT 5.0
+ Use PREFIX_ equates.
+ 11-Jul-1994 Danl
+ Fix assert that gets hit when an invalid info level is passed in.
+ I moved the code that checks the info level up, so that it gets
+ checked first.
+
+--*/
+
+
+// These must be included first:
+
+#include <windef.h> // IN, VOID, LPTSTR, etc.
+#include <lmcons.h> // NET_API_STATUS, PARM equates, etc.
+#include <rap.h> // Needed by <strucinf.h>.
+#include <repldefs.h> // IF_DEBUG(), ReplIsIntegrityValid(), etc.
+#include <master.h> // LPMASTER_LIST_REC.
+#include <rpc.h> // Needed by <repl.h>.
+
+// These can be in any order:
+
+#include <expdir.h> // ExportDirBuildApiRecord().
+#include <lmapibuf.h> // NetApiBufferAllocate().
+#include <master.h> // RMGlobal variables.
+#include <netdebug.h> // NetpAssert(), NetpKdPrint(), etc.
+#include <netlib.h> // NetpPointerPlusSomeBytes().
+#include <netlock.h> // ACQUIRE_LOCK_SHARED(), RELEASE_LOCK().
+#include <prefix.h> // PREFIX_ equates.
+#include <repl.h> // My prototype (in MIDL-generated .h file).
+#include <replgbl.h> // ReplConfigLock, ReplConfigRole.
+#include <strucinf.h> // Netp{various}StructureInfo().
+#include <winerror.h> // ERROR_ equates, NO_ERROR.
+
+
+NET_API_STATUS
+NetrReplExportDirEnum (
+ IN LPTSTR UncServerName OPTIONAL,
+ // IN DWORD Level,
+ IN OUT LPEXPORT_ENUM_STRUCT EnumContainer,
+ // OUT LPEXPORT_CONTAINER BufPtr, // RPC container (union)
+ IN DWORD PrefMaxSize,
+ // OUT LPDWORD EntriesRead,
+ OUT LPDWORD TotalEntries,
+ IN OUT LPDWORD ResumeHandle OPTIONAL
+ )
+
+/*++
+
+Routine Description:
+
+ Same as NetReplExportDirEnum.
+
+Arguments:
+
+ Same as NetReplExportDirEnum.
+
+Return Value:
+
+ Same as NetReplExportDirEnum.
+
+--*/
+
+{
+ LPVOID ApiArray;
+ LPVOID ApiEntry;
+ NET_API_STATUS ApiStatus;
+ BOOL ConfigLocked = FALSE;
+ DWORD EntryCount;
+ DWORD FixedSize;
+ DWORD Level;
+ BOOL ListLocked = FALSE;
+ LPMASTER_LIST_REC MasterRecord;
+ DWORD OutputSize;
+ LPVOID StringLocation;
+
+#define SET_ENTRIES_READ( value ) \
+ { \
+ /* Pretend level 0, to make life easy. */ \
+ EnumContainer->ExportInfo.Level0->EntriesRead = (value); \
+ }
+
+#define SET_BUFFER_POINTER( value ) \
+ { \
+ /* Pretend level 0, to make life easy. */ \
+ EnumContainer->ExportInfo.Level0->Buffer = (value); \
+ }
+ UNREFERENCED_PARAMETER(PrefMaxSize);
+
+ //
+ // Check for caller errors.
+ //
+ Level = EnumContainer->Level;
+
+ //
+ // Compute size of output area (and check caller's Level too).
+ //
+ ApiStatus = NetpReplExportDirStructureInfo (
+ Level,
+ PARMNUM_ALL,
+ TRUE, // want native sizes
+ NULL, // don't need DataDesc16
+ NULL, // don't need DataDesc32
+ NULL, // don't need DataDescSmb
+ & OutputSize, // need max size of structure
+ & FixedSize,
+ NULL); // don't need StringSize
+ if (ApiStatus != NO_ERROR) {
+ return (ApiStatus);
+ }
+
+ NetpAssert( OutputSize > sizeof( REPL_EDIR_INFO_0 ) );
+ NetpAssert( OutputSize > FixedSize );
+ NetpAssert( FixedSize != 0 );
+
+ NetpAssert( EnumContainer != NULL );
+ NetpAssert( EnumContainer->ExportInfo.Level0 != NULL );
+
+ // Don't confuse caller about possible alloc'ed data.
+ SET_BUFFER_POINTER( NULL );
+
+ if (TotalEntries == NULL) {
+ return (ERROR_INVALID_PARAMETER);
+ }
+
+ // Test for memory faults before we lock anything.
+ SET_ENTRIES_READ( 0 );
+ *TotalEntries = 0;
+
+ // This version only supports 1 call to enumerate, so resume handle
+ // should never be set to nonzero. But let's check, so caller finds out
+ // they have a buggy program.
+ if (ResumeHandle != NULL) {
+ if (*ResumeHandle != 0) {
+ return (ERROR_INVALID_PARAMETER);
+ }
+ }
+
+ //
+ // We'll need shared lock on config data, so we can find out if export half
+ // is actually running.
+ //
+ ACQUIRE_LOCK_SHARED( ReplConfigLock );
+ ConfigLocked = TRUE;
+
+ //
+ // Handle call using registry only, if export half is not running now.
+ //
+ if ( !ReplRoleIncludesExport( ReplConfigRole ) ) {
+ ApiStatus = ExportDirEnumApiRecords(
+ UncServerName,
+ Level,
+ (LPBYTE *) (LPVOID) &ApiArray,
+ PrefMaxSize,
+ & EntryCount,
+ TotalEntries );
+ goto Cleanup;
+ }
+
+ //
+ // Get read-only lock on master list.
+ //
+ ACQUIRE_LOCK_SHARED( RMGlobalListLock );
+ ListLocked = TRUE;
+
+ //
+ // Find out how many entries there are.
+ //
+ EntryCount = RMGlobalMasterListCount;
+
+ if (EntryCount > 0) {
+
+ NetpAssert( RMGlobalMasterListHeader != NULL );
+
+ //
+ // Allocate the output area.
+ //
+ ApiStatus = NetApiBufferAllocate(
+ OutputSize * EntryCount,
+ (LPVOID *) & ApiArray);
+ if (ApiStatus != NO_ERROR) {
+
+ // ApiStatus is already set to return error to caller.
+ // Don't forget to release lock...
+
+ } else {
+#if DBG
+ DWORD EntriesFound = 0;
+#endif
+
+ IF_DEBUG( EXPAPI ) {
+ NetpKdPrint(( PREFIX_REPL_MASTER
+ "NetrReplExportDirEnum: alloc'ed " FORMAT_DWORD
+ " bytes at " FORMAT_LPVOID ".\n",
+ OutputSize * EntryCount, (LPVOID) ApiArray ));
+ }
+ NetpAssert( ApiArray != NULL );
+
+ //
+ // Loop for each entry in master list.
+ //
+ ApiEntry = ApiArray;
+ MasterRecord = RMGlobalMasterListHeader;
+ StringLocation = NetpPointerPlusSomeBytes(
+ ApiArray,
+ OutputSize * EntryCount );
+
+ while (MasterRecord != NULL) {
+
+
+ //
+ // Build an array entry for this master record.
+ //
+ ApiStatus = ExportDirBuildApiRecord (
+ Level,
+ MasterRecord->dir_name,
+ MasterRecord->integrity,
+ MasterRecord->extent,
+ MasterRecord->lockcount,
+ MasterRecord->time_of_first_lock, // secs since 1970
+ ApiEntry,
+ (LPBYTE *) (LPVOID) & StringLocation);
+ NetpAssert( ApiStatus == NO_ERROR ); // We checked all parms.
+
+ MasterRecord = MasterRecord->next_p;
+ ApiEntry = NetpPointerPlusSomeBytes( ApiEntry, FixedSize );
+#if DBG
+ ++EntriesFound;
+#endif
+
+ }
+#if DBG
+ NetpAssert( EntriesFound == EntryCount );
+#endif
+
+ }
+
+ // Don't forget to release lock...
+
+ } else {
+
+ // No entries...
+ NetpAssert( RMGlobalMasterListHeader == NULL );
+
+ ApiArray = NULL;
+ ApiStatus = NO_ERROR;
+
+ // Don't forget to release lock...
+ }
+
+
+ //
+ // Release locks and tell caller how everything went.
+ //
+Cleanup:
+ if (ListLocked) {
+ RELEASE_LOCK( RMGlobalListLock );
+ }
+
+ if (ConfigLocked) {
+ RELEASE_LOCK( ReplConfigLock ); // Locked first, must be unlocked last.
+ }
+
+ SET_BUFFER_POINTER( (LPVOID) ApiArray );
+
+ SET_ENTRIES_READ( EntryCount );
+
+ *TotalEntries = EntryCount;
+
+ return (ApiStatus);
+
+}
diff --git a/private/net/svcdlls/repl/server/expget.c b/private/net/svcdlls/repl/server/expget.c
new file mode 100644
index 000000000..62ad37bda
--- /dev/null
+++ b/private/net/svcdlls/repl/server/expget.c
@@ -0,0 +1,213 @@
+/*++
+
+Copyright (c) 1992-1993 Microsoft Corporation
+
+Module Name:
+
+ ExpGet.c
+
+Abstract:
+
+ This file contains NetrReplExportDirGetInfo.
+
+Author:
+
+ John Rogers (JohnRo) 08-Jan-1992
+
+Environment:
+
+ Runs under Windows NT.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+ 08-Jan-1992 JohnRo
+ Created.
+ 15-Jan-1992 JohnRo
+ Corrected name of this routine.
+ 20-Jan-1992 JohnRo
+ Netr prototypes are now generated by MIDL and put in repl.h.
+ 20-Jan-1992 JohnRo
+ Changed prototype to match MIDL requirements.
+ 28-Jan-1992 JohnRo
+ Changed ExportDirAllocApiRecords() to allow arrays.
+ Changed to use LPTSTR etc.
+ Changed ExportDirBuildApiRecord's interface.
+ 23-Mar-1992 JohnRo
+ Do a little more checking before we lock things.
+ 28-Jul-1992 JohnRo
+ Added more debug output.
+ Avoid compiler warnings.
+ 13-Nov-1992 JohnRo
+ RAID 1537: Repl APIs in wrong role kill svc.
+ 02-Apr-1993 JohnRo
+ Use NetpKdPrint() where possible.
+
+--*/
+
+
+// These must be included first:
+
+#include <windef.h> // IN, VOID, LPTSTR, etc.
+#include <lmcons.h> // NET_API_STATUS.
+#include <repldefs.h> // Needed by <master.h>.
+#include <master.h> // PMASTER_LIST_REC.
+#include <rpc.h> // Needed by <repl.h>.
+
+// These can be in any order:
+
+#include <dirname.h> // ReplIsDirNameValid().
+#include <expdir.h> // ExportDirBuildApiRecord(), etc.
+#include <lmerr.h> // ERROR_ and NERR_ equates; NO_ERROR.
+#include <masproto.h> // GetMasterRec().
+#include <netdebug.h> // NetpAssert().
+#include <netlock.h> // ACQUIRE_LOCK_SHARED(), RELEASE_LOCK().
+#include <prefix.h> // PREFIX_ equates.
+#include <repl.h> // My prototype (in MIDL-generated .h file).
+#include <replgbl.h> // ReplConfigLock, ReplConfigRole.
+
+
+NET_API_STATUS
+NetrReplExportDirGetInfo (
+ IN LPTSTR UncServerName OPTIONAL,
+ IN LPTSTR DirName,
+ IN DWORD Level,
+ OUT LPEXPORT_CONTAINER BufPtr // RPC container (union)
+ )
+
+/*++
+
+Routine Description:
+
+ Same as NetReplExportDirGetInfo.
+
+Arguments:
+
+ Same as NetReplExportDirGetInfo.
+
+Return Value:
+
+ Same as NetReplExportDirGetInfo.
+
+--*/
+
+{
+ LPVOID ApiRecord = NULL;
+ NET_API_STATUS ApiStatus;
+ BOOL ConfigLocked = FALSE;
+ BOOL ListLocked = FALSE;
+ PMASTER_LIST_REC MasterRecord;
+ LPVOID StringLocation;
+
+ IF_DEBUG( EXPAPI ) {
+ NetpKdPrint(( PREFIX_REPL
+ "NetrReplExportDirGetInfo: union at " FORMAT_LPVOID ".\n",
+ (LPVOID) BufPtr ));
+ }
+
+ //
+ // Check for caller errors.
+ //
+ if (BufPtr == NULL) {
+ return (ERROR_INVALID_PARAMETER);
+ }
+
+ BufPtr->Info0 = NULL; // Don't confuse caller about possible alloc'ed data.
+
+ if (ReplIsDirNameValid( DirName ) == FALSE) {
+ return (ERROR_INVALID_PARAMETER);
+ } else if ( !ExportDirIsLevelValid( Level, FALSE ) ) { // no setinfo levels
+ return (ERROR_INVALID_LEVEL);
+ }
+
+ //
+ // Get a shared lock on config data so we can see if role includes export.
+ //
+ ACQUIRE_LOCK_SHARED( ReplConfigLock );
+ ConfigLocked = TRUE;
+
+ if ( !ReplRoleIncludesExport( ReplConfigRole ) ) {
+ ApiStatus = ExportDirGetApiRecord (
+ UncServerName,
+ DirName,
+ Level,
+ (LPBYTE *) (LPVOID) & ApiRecord );
+ goto Cleanup;
+ }
+
+ //
+ // Get read-only lock on master list.
+ //
+ ACQUIRE_LOCK_SHARED( RMGlobalListLock );
+ ListLocked = TRUE;
+
+ //
+ // Find the master record for this dir name.
+ //
+ MasterRecord = GetMasterRec( DirName );
+ if (MasterRecord == NULL) { // Not found.
+
+ ApiStatus = NERR_UnknownDevDir;
+ // Don't forget to release lock...
+
+ } else { // Found.
+
+ //
+ // Allocate the output area.
+ // Compute size of output area (and check caller's Level too).
+ //
+ ApiStatus = ExportDirAllocApiRecords (
+ Level,
+ 1, // only need one record.
+ (LPBYTE *) & ApiRecord,
+ (LPBYTE *) (LPVOID) & StringLocation ); // Points past data end.
+ if (ApiStatus != NO_ERROR) {
+
+ // ApiStatus is already set to return error to caller.
+ // Don't forget to release lock...
+
+ } else {
+ NetpAssert( ApiRecord != NULL );
+
+ //
+ // Build ApiRecord!
+ //
+ ApiStatus = ExportDirBuildApiRecord (
+ Level,
+ DirName,
+ MasterRecord->integrity,
+ MasterRecord->extent,
+ MasterRecord->lockcount,
+ MasterRecord->time_of_first_lock, // Secs since 1970.
+ ApiRecord,
+ (LPBYTE *) (LPVOID) & StringLocation);
+ NetpAssert( ApiStatus == NO_ERROR ); // We checked all parms.
+
+ // Don't forget to release lock...
+
+ }
+
+ }
+
+Cleanup:
+
+ //
+ // Release locks and tell caller how everything went.
+ //
+
+ if (ListLocked) {
+ RELEASE_LOCK( RMGlobalListLock );
+ }
+
+ if (ConfigLocked) {
+ RELEASE_LOCK( ReplConfigLock );
+ }
+
+ if (ApiStatus == NO_ERROR) {
+ NetpAssert( ApiRecord != NULL );
+ NetpAssert( ExportDirIsApiRecordValid( Level, ApiRecord, NULL ) );
+ }
+ BufPtr->Info2 = (LPVOID) ApiRecord;
+ return (ApiStatus);
+
+}
diff --git a/private/net/svcdlls/repl/server/explock.c b/private/net/svcdlls/repl/server/explock.c
new file mode 100644
index 000000000..6d8419f68
--- /dev/null
+++ b/private/net/svcdlls/repl/server/explock.c
@@ -0,0 +1,333 @@
+/*++
+
+Copyright (c) 1992-1993 Microsoft Corporation
+
+Module Name:
+
+ ExpLock.c
+
+Abstract:
+
+ This file contains:
+
+ NetrReplExportDirLock
+ NetrReplExportDirUnlock
+
+Author:
+
+ John Rogers (JohnRo) 07-Jan-1992
+
+Environment:
+
+ Runs under Windows NT.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Notes:
+
+ This file is extremely similar to ImpLock.c. If you fix any bugs here,
+ make sure they're reflected there, and vice versa.
+
+Revision History:
+
+ 07-Jan-1992 JohnRo
+ Created.
+ 20-Jan-1992 JohnRo
+ Netr prototypes are now generated by MIDL and put in repl.h.
+ 24-Jan-1992 JohnRo
+ Changed to use LPTSTR etc.
+ 18-Feb-1992 JohnRo
+ Use Repl{Incr,Decr}LockFields().
+ 15-Mar-1992 JohnRo
+ Update registry with new values.
+ 22-Jul-1992 JohnRo
+ RAID 2274: repl svc should impersonate caller.
+ 17-Nov-1992 JohnRo
+ RAID 1537: repl APIs in wrong role kill service.
+ 12-Jan-1993 JohnRo
+ RAID 7064: replicator exporter skips new data (change notify while
+ dir locked is lost).
+ Made changes suggested by PC-LINT 5.0
+ 13-Jan-1993 JohnRo
+ RAID 7053: locked trees added to pulse msg. (Actually fix all
+ kinds of remote lock handling.)
+ 04-Mar-1993 JohnRo
+ RAID 7988: downlevel repl importer might not see lock file
+ for new first-level dir.
+ 13-Apr-1993 JohnRo
+ RAID 3107: locking directory over the net gives network path not found.
+
+--*/
+
+
+// These must be included first:
+
+#include <windef.h> // IN, VOID, LPTSTR, etc.
+#include <lmcons.h> // NET_API_STATUS, PARM equates, etc.
+#include <repldefs.h> // (Required by masproto.h)
+#include <master.h> // LPMASTER_LIST_REC, RMGlobalListLock.
+#include <rpc.h> // Needed by <repl.h>.
+
+// These can be in any order:
+
+#include <dirname.h> // ReplIsDirNameValid().
+#include <expdir.h> // ExportDirLockInRegistry(), etc.
+#include <lmerr.h> // NERR_ equates, NO_ERROR.
+#include <lmrepl.h> // REPL_UNLOCK_ equates.
+#include <masproto.h> // GetMasterRec().
+#include <netdebug.h> // NetpAssert().
+#include <netlock.h> // ACQUIRE_LOCK(), etc.
+#include <pulser.h> // PulserTimeOfLastNotifyOrUnlock, etc.
+#include <repl.h> // My prototype (in MIDL-generated .h file).
+#include <replgbl.h> // ReplConfigLock, ReplConfigRole.
+#include <replp.h> // NetpReplTimeNow().
+#include <rpcutil.h> // NetpImpersonateClient(), NetpRevertToSelf().
+
+
+NET_API_STATUS
+NetrReplExportDirLock (
+ IN LPTSTR UncServerName OPTIONAL,
+ IN LPTSTR DirName
+ )
+
+/*++
+
+Routine Description:
+
+ Same as NetReplExportDirLock.
+
+Arguments:
+
+ Same as NetReplExportDirLock.
+
+Return Value:
+
+ Same as NetReplExportDirLock.
+
+--*/
+
+{
+ NET_API_STATUS ApiStatus;
+ BOOL ConfigLocked = FALSE;
+ BOOL Impersonated = FALSE;
+ BOOL ListLocked = FALSE;
+ LPMASTER_LIST_REC MasterRecord;
+
+ if ( !ReplIsDirNameValid( DirName ) ) {
+ return (ERROR_INVALID_PARAMETER);
+ }
+
+ //
+ // Impersonate caller, so security check (write to registry) reflects
+ // the client's process, not the repl service process.
+ //
+ ApiStatus = NetpImpersonateClient();
+ if (ApiStatus != NO_ERROR) {
+ goto Cleanup;
+ }
+ Impersonated = TRUE;
+
+ //
+ // Get a shared lock on config data so we can see if role includes export.
+ //
+ ACQUIRE_LOCK_SHARED( ReplConfigLock );
+ ConfigLocked = TRUE;
+
+ //
+ // Lock global list and matching registry data.
+ // We need this lock even if this side of service is not running,
+ // because the XxxDirLockInRegistry routines are not atomic.
+ //
+ ACQUIRE_LOCK_SHARED( RMGlobalListLock );
+ ListLocked = TRUE;
+
+ //
+ // Update lock values in registry first.
+ // This has the side-effect of doing a security check.
+ //
+ ApiStatus = ExportDirLockInRegistry(
+ UncServerName,
+ DirName
+ );
+ if ( (ApiStatus == NO_ERROR) && (ReplRoleIncludesExport(ReplConfigRole)) ) {
+ MasterRecord = GetMasterRec( DirName );
+
+ if (MasterRecord == NULL) {
+
+ ApiStatus = NERR_UnknownDevDir;
+ goto Cleanup; // Don't forget to release lock...
+
+ } else {
+
+ //
+ // Update lock values in service.
+ //
+ ApiStatus = ReplIncrLockFields(
+ & (MasterRecord->lockcount),
+ & (MasterRecord->time_of_first_lock) );
+ NetpAssert( ApiStatus == NO_ERROR ); // BUGBUG
+
+ //
+ // Make sure we have a userlock file, so downlevel clients
+ // know about lock.
+ //
+ ApiStatus = ExportDirFixUserLockFiles(
+ (LPCTSTR) ReplConfigExportPath,
+ (LPCTSTR) DirName,
+ MasterRecord->lockcount );
+ NetpAssert( ApiStatus == NO_ERROR ); // BUGBUG
+ if (ApiStatus != NO_ERROR) {
+ goto Cleanup;
+ }
+
+ MasterRecord->locks_fixed = TRUE;
+
+ }
+ }
+
+Cleanup:
+
+ if (Impersonated) {
+ (VOID) NetpRevertToSelf();
+ }
+
+ if (ListLocked) {
+ RELEASE_LOCK( RMGlobalListLock );
+ }
+
+ if (ConfigLocked) {
+ RELEASE_LOCK( ReplConfigLock );
+ }
+
+ return (ApiStatus);
+
+} // NetrReplExportDirLock
+
+
+NET_API_STATUS
+NetrReplExportDirUnlock (
+ IN LPTSTR UncServerName OPTIONAL,
+ IN LPTSTR DirName,
+ IN DWORD UnlockForce
+ )
+
+/*++
+
+Routine Description:
+
+ Same as NetReplExportDirUnlock.
+
+Arguments:
+
+ Same as NetReplExportDirUnlock.
+
+Return Value:
+
+ Same as NetReplExportDirUnlock.
+
+--*/
+
+{
+ NET_API_STATUS ApiStatus;
+ BOOL ConfigLocked = FALSE;
+ BOOL Impersonated = FALSE;
+ BOOL ListLocked = FALSE;
+ LPMASTER_LIST_REC MasterRecord;
+
+ if ( !ReplIsDirNameValid( DirName ) ) {
+ return (ERROR_INVALID_PARAMETER);
+ } else if ( !ReplIsForceLevelValid( UnlockForce ) ) {
+ return (ERROR_INVALID_PARAMETER);
+ }
+
+ //
+ // Impersonate caller, so security check (write to registry) reflects
+ // the client's process, not the repl service process.
+ //
+ ApiStatus = NetpImpersonateClient();
+ if (ApiStatus != NO_ERROR) {
+ goto Cleanup;
+ }
+ Impersonated = TRUE;
+
+ //
+ // Get a shared lock on config data so we can see if role includes export.
+ //
+ ACQUIRE_LOCK_SHARED( ReplConfigLock );
+ ConfigLocked = TRUE;
+
+ //
+ // Lock global list and matching registry data.
+ // We need this lock even if this side of service is not running,
+ // because the XxxDirLockInRegistry routines are not atomic.
+ //
+ ACQUIRE_LOCK_SHARED( RMGlobalListLock );
+ ListLocked = TRUE;
+
+ //
+ // Update lock values in registry first.
+ // This has the side-effect of doing a security check.
+ //
+ ApiStatus = ExportDirUnlockInRegistry(
+ UncServerName,
+ DirName,
+ UnlockForce );
+ if ( (ApiStatus == NO_ERROR) && (ReplRoleIncludesExport(ReplConfigRole)) ) {
+ MasterRecord = GetMasterRec( DirName );
+
+ if (MasterRecord == NULL) {
+
+ ApiStatus = NERR_UnknownDevDir;
+ // Don't forget to release lock...
+
+ } else {
+
+ //
+ // Update lock values in service.
+ //
+ ApiStatus = ReplDecrLockFields(
+ & (MasterRecord->lockcount),
+ & (MasterRecord->time_of_first_lock),
+ UnlockForce );
+ NetpAssert( ApiStatus == NO_ERROR ); // BUGBUG
+
+ //
+ // Update time so pulser knows to do another checksum.
+ //
+ PulserTimeOfLastNotifyOrUnlock = NetpReplTimeNow();
+
+ //
+ // Consider removing userlock file, so downlevel clients
+ // know we don't have lock anymore.
+ //
+ ApiStatus = ExportDirFixUserLockFiles(
+ (LPCTSTR) ReplConfigExportPath,
+ (LPCTSTR) DirName,
+ MasterRecord->lockcount );
+ NetpAssert( ApiStatus == NO_ERROR ); // BUGBUG
+
+ if (ApiStatus != NO_ERROR) {
+ goto Cleanup; // Don't forget to release lock...
+ }
+
+ MasterRecord->locks_fixed = TRUE;
+
+ }
+ }
+
+Cleanup:
+
+ if (Impersonated) {
+ (VOID) NetpRevertToSelf();
+ }
+
+ if (ListLocked) {
+ RELEASE_LOCK( RMGlobalListLock );
+ }
+
+ if (ConfigLocked) {
+ RELEASE_LOCK( ReplConfigLock );
+ }
+
+ return (ApiStatus);
+
+} // NetrReplExportDirUnlock
diff --git a/private/net/svcdlls/repl/server/expread.c b/private/net/svcdlls/repl/server/expread.c
new file mode 100644
index 000000000..299a18e70
--- /dev/null
+++ b/private/net/svcdlls/repl/server/expread.c
@@ -0,0 +1,423 @@
+/*++
+
+Copyright (c) 1992-1993 Microsoft Corporation
+
+Module Name:
+
+ ExpRead.c
+
+Abstract:
+
+ ExportDirReadMasterList().
+ ExportDirGetRegistryValues().
+
+Author:
+
+ John Rogers (JohnRo) 23-Mar-1992
+
+Environment:
+
+ User Mode - Win32
+
+Revision History:
+
+ 23-Mar-1992 JohnRo
+ Created.
+ 09-Jul-1992 JohnRo
+ RAID 10503: srv mgr: repl dialog doesn't come up.
+ Use PREFIX_ equates.
+ 25-Sep-1992 JohnRo
+ RAID 5494: repl svc does not maintain time stamp on import startup.
+ (Fixed lock type.)
+ 02-Dec-1992 JohnRo
+ RAID 3844: remote NetReplSetInfo uses local machine type.
+ Quiet normal debug output.
+ 13-Jan-1993 JohnRo
+ RAID 7053: locked trees added to pulse msg. (Actually fix all
+ kinds of remote lock handling.)
+ Made some changes suggested by PC-LINT 5.0
+ 04-Mar-1993 JohnRo
+ RAID 7988: downlevel repl importer might not see lock file
+ for new first-level dir.
+ Made more changes suggested by PC-LINT 5.0
+ 26-Apr-1993 JohnRo
+ RAID 7313: Fix user locks flag value for obscure permissions (e.g.
+ read-only file-system).
+ Use NetpKdPrint() where possible.
+
+--*/
+
+
+// These must be included first:
+
+#include <windef.h> // IN, DWORD, etc.
+#include <lmcons.h> // NET_API_STATUS, etc.
+
+// These may be included in any order:
+
+#include <alertmsg.h>
+#include <config.h> // LPNET_CONFIG_HANDLE, Netp config routines.
+#include <confname.h> // SECT_NT_ equates.
+#include <expdir.h> // My prototype.
+#include <lmerr.h> // NERR_ and ERROR_ equates, NO_ERROR.
+#include <lmerrlog.h>
+#include <master.h> // RMGlobalListLock, etc.
+#include <masproto.h> // NewMasterRecord(), etc.
+#include <netdebug.h> // NetpAssert(), etc.
+#include <netlock.h> // ACQUIRE_LOCK(), etc.
+#include <prefix.h> // PREFIX_ equates.
+#include <repldefs.h> // IF_DEBUG().
+#include <replgbl.h> // ReplConfig variables.
+#include <tstr.h> // STRCMP(), ... functions
+
+
+// This also fixes the USERLOCK.* file(s) to match the lock count in registry.
+NET_API_STATUS
+ExportDirReadMasterList(
+ VOID
+ )
+{
+ NET_API_STATUS ApiStatus;
+ BOOL ConfigLocked = FALSE;
+ LPTSTR DirName = NULL;
+ DWORD EntryCount = 0;
+ BOOL FirstTime;
+ LPNET_CONFIG_HANDLE Handle = NULL;
+ BOOL ListLocked = FALSE;
+
+ IF_DEBUG(MASTER) {
+ NetpKdPrint(( PREFIX_REPL_MASTER
+ "ExportDirReadMasterList: beginning...\n" ));
+ }
+
+ ACQUIRE_LOCK_SHARED( ReplConfigLock );
+ ConfigLocked = TRUE;
+
+ ACQUIRE_LOCK( RMGlobalListLock );
+ ListLocked = TRUE;
+
+ //
+ // Open the right section of the config file/whatever.
+ //
+ ApiStatus = NetpOpenConfigDataEx(
+ & Handle,
+ NULL, // local (no server name)
+ (LPTSTR) SECT_NT_REPLICATOR, // service name
+ (LPTSTR) SECT_NT_REPLICATOR_EXPORTS, // area under service
+ TRUE); // read-only
+ if (ApiStatus != NO_ERROR) {
+ goto CleanUp;
+ }
+
+ //
+ // Count entries in config data.
+ //
+ ApiStatus = NetpNumberOfConfigKeywords (
+ Handle,
+ & EntryCount );
+
+ if (ApiStatus != NO_ERROR) {
+ NetpAssert( FALSE ); // BUGBUG handle unexpected better.
+ goto CleanUp;
+ } else if (EntryCount == 0) {
+ goto CleanUp;
+ }
+
+ //
+ // Loop for each keyword (relative directory path) in this section.
+ //
+
+ FirstTime = TRUE;
+
+ /*lint -save -e716 */ // disable warnings for while(TRUE)
+ while (TRUE) {
+ DWORD Integrity, Extent, LockCount, LockTime;
+ LPTSTR ValueString;
+
+ ApiStatus = NetpEnumConfigSectionValues (
+ Handle,
+ & DirName, // Keyword - alloc and set ptr.
+ & ValueString, // Must be freed by NetApiBufferFree().
+ FirstTime );
+
+ FirstTime = FALSE;
+
+ if (ApiStatus != NO_ERROR) {
+ goto CleanUp; // Handle end of list or an actual error.
+ }
+
+ NetpAssert( DirName != NULL );
+ NetpAssert( ValueString != NULL );
+
+ //
+ // Parse the value string...
+ //
+ ApiStatus = ExportDirParseConfigData (
+ NULL, // no server name
+ ValueString,
+ & Integrity,
+ & Extent,
+ & LockCount,
+ & LockTime); // Seconds since 1970.
+
+ NetpMemoryFree( ValueString );
+
+ if (ApiStatus == NO_ERROR) {
+ PMASTER_LIST_REC rec;
+
+ rec = NewMasterRecord( DirName, Integrity, Extent );
+
+ if (rec == NULL) {
+ ApiStatus = ERROR_NOT_ENOUGH_MEMORY;
+ goto CleanUp;
+ }
+
+ // Override lock info set by NewMasterRecord().
+ rec->locks_fixed = FALSE; // just in case read-only filesystem.
+ rec->lockcount = LockCount;
+ rec->time_of_first_lock = LockTime;
+
+ // Fix UserLock file(s) to match lock count.
+ ApiStatus = ExportDirFixUserLockFiles(
+ (LPCTSTR) ReplConfigExportPath,
+ (LPCTSTR) DirName,
+ LockCount );
+ if (ApiStatus != NO_ERROR) {
+ // BUGBUG: is leaving loop a good idea?
+
+ goto CleanUp; // go log error, etc.
+ }
+
+ NetpAssert( DirName != NULL );
+ NetpMemoryFree( DirName );
+ DirName = NULL;
+
+ rec->locks_fixed = TRUE;
+
+ } else {
+ goto CleanUp; // go log error, etc.
+ }
+
+ } // while TRUE (exit on error or end of list)
+ /*lint -restore */ // re-enable warnings for while(TRUE)
+
+
+ //
+ // All done (error or otherwise).
+ //
+
+CleanUp:
+
+ if (ApiStatus == NERR_CfgParamNotFound) { // Normal end of list.
+ ApiStatus = NO_ERROR;
+ }
+
+ IF_DEBUG(MASTER) {
+ NetpKdPrint(( PREFIX_REPL_MASTER
+ "ExportDirReadMasterList: done, status="
+ FORMAT_API_STATUS ".\n", ApiStatus ));
+ }
+
+ if (ApiStatus != NO_ERROR) {
+
+ AlertLogExit(ALERT_ReplSysErr,
+ NELOG_ReplSysErr,
+ ApiStatus,
+ NULL,
+ NULL,
+ EXIT); // BUGBUG: kill service?
+ }
+
+
+ if (ConfigLocked) {
+ RELEASE_LOCK( ReplConfigLock );
+ }
+
+ if (DirName != NULL) {
+ NetpMemoryFree( DirName );
+ }
+
+ if (Handle != NULL) {
+ (void) NetpCloseConfigData( Handle );
+ }
+
+ if (ListLocked) {
+ RELEASE_LOCK( RMGlobalListLock );
+ }
+
+ return ApiStatus;
+
+} // ExportDirReadMasterList
+
+NET_API_STATUS
+ExportDirGetRegistryValues(
+ IN LPTSTR ServiceRegPath OPTIONAL,
+ IN LPTSTR TargetName
+ )
+{
+ NET_API_STATUS ApiStatus;
+ DWORD EntryCount = 0;
+ BOOL FirstTime;
+ LPNET_CONFIG_HANDLE Handle = NULL;
+ BOOL LockDone = FALSE;
+
+ UNREFERENCED_PARAMETER( ServiceRegPath );
+
+ IF_DEBUG(MASTER) {
+ NetpKdPrint(( PREFIX_REPL_MASTER
+ "ExportDirGetRegistryValues: beginning...\n" ));
+ }
+
+ ACQUIRE_LOCK( RMGlobalListLock );
+ LockDone = TRUE;
+
+ //
+ // Open the right section of the config file/whatever.
+ //
+ ApiStatus = NetpOpenConfigDataEx(
+ & Handle,
+ NULL, // local (no server name)
+ (LPTSTR) SECT_NT_REPLICATOR, // service name
+ (LPTSTR) SECT_NT_REPLICATOR_EXPORTS, // area under service
+ TRUE); // read-only
+ if (ApiStatus != NO_ERROR) {
+ goto Exit;
+ }
+
+ //
+ // Count entries in config data.
+ //
+ ApiStatus = NetpNumberOfConfigKeywords (
+ Handle,
+ & EntryCount );
+
+ if (ApiStatus != NO_ERROR) {
+ NetpAssert( FALSE ); // BUGBUG handle unexpected better.
+ goto Exit;
+ } else if (EntryCount == 0) {
+ ApiStatus = NERR_CfgCompNotFound; // no enteries in list
+ goto Exit; // probable a new export dir
+ }
+
+ //
+ // Loop for each keyword (relative directory path) in this section.
+ //
+
+ FirstTime = TRUE;
+
+ /*lint -save -e716 */ // disable warnings for while(TRUE)
+ while (TRUE) { // look for the TargetName
+
+ LPTSTR DirName;
+ DWORD Integrity, Extent, LockCount, LockTime;
+ LPTSTR ValueString;
+
+ ApiStatus = NetpEnumConfigSectionValues (
+ Handle,
+ & DirName, // Keyword - alloc and set ptr.
+ & ValueString, // Must be freed by NetApiBufferFree().
+ FirstTime );
+
+ FirstTime = FALSE;
+
+ if (ApiStatus != NO_ERROR) {
+ goto Exit; // Handle end of list or an actual error.
+ }
+
+ NetpAssert( DirName != NULL );
+ NetpAssert( ValueString != NULL );
+
+ IF_DEBUG( MASTER ) {
+ NetpKdPrint(( PREFIX_REPL_MASTER
+ "Registry Item Found: '" FORMAT_LPTSTR "'.\n",
+ DirName ));
+ }
+
+
+ if ( (STRICMP( DirName, TargetName ) == 0) ) {
+
+ //
+ // Parse the value string...
+ //
+ IF_DEBUG( MASTER ) {
+ NetpKdPrint(( PREFIX_REPL_MASTER
+ "Registry Match made: '" FORMAT_LPTSTR "'~=' "
+ FORMAT_LPTSTR "'.\n",
+ DirName, TargetName ));
+ }
+
+
+ ApiStatus = ExportDirParseConfigData (
+ NULL, // no server name
+ ValueString,
+ & Integrity,
+ & Extent,
+ & LockCount,
+ & LockTime); // Seconds since 1970.
+
+ NetpMemoryFree( ValueString );
+
+ if (ApiStatus == NO_ERROR) {
+ PMASTER_LIST_REC rec;
+
+ rec = NewMasterRecord( DirName, Integrity, Extent );
+
+ NetpMemoryFree( DirName );
+
+ if (rec == NULL) {
+ ApiStatus = ERROR_NOT_ENOUGH_MEMORY;
+ goto Exit;
+ }
+
+ // Override lock info set by NewMasterRecord().
+
+ rec->lockcount = LockCount;
+ rec->time_of_first_lock = LockTime;
+ goto Exit;
+
+ } else {
+ // BUGBUG: Log the error!
+ IF_DEBUG(MASTER) {
+ NetpKdPrint(( PREFIX_REPL_MASTER
+ "ExportDirParseConfigData retured error: "
+ FORMAT_API_STATUS ".\n", ApiStatus ));
+ }
+ goto Exit;
+ }
+
+ } else {
+ NetpMemoryFree( DirName );
+ NetpMemoryFree( ValueString );
+ }
+ }
+ /*lint -restore */ // re-enable warnings for while(TRUE)
+
+
+ //
+ // All done (error or otherwise).
+ //
+
+Exit:
+
+ //
+ // if an error occurs pass that value back
+ //
+
+
+ IF_DEBUG(MASTER) {
+ NetpKdPrint(( PREFIX_REPL_MASTER
+ "ExportDirGetRegistryValues: done, status="
+ FORMAT_API_STATUS ".\n", ApiStatus ));
+ }
+
+ if (Handle != NULL) {
+ (void) NetpCloseConfigData( Handle );
+ }
+
+ if (LockDone) {
+ RELEASE_LOCK( RMGlobalListLock );
+ }
+
+ return ApiStatus;
+
+} // ExportDirGetRegistryValues
diff --git a/private/net/svcdlls/repl/server/expset.c b/private/net/svcdlls/repl/server/expset.c
new file mode 100644
index 000000000..4defa2137
--- /dev/null
+++ b/private/net/svcdlls/repl/server/expset.c
@@ -0,0 +1,323 @@
+/*++
+
+Copyright (c) 1992-1993 Microsoft Corporation
+
+Module Name:
+
+ ExpSet.c
+
+Abstract:
+
+ This file contains NetrReplExportDirSetInfo.
+
+Author:
+
+ John Rogers (JohnRo) 08-Jan-1992
+
+Environment:
+
+ Runs under Windows NT.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+ 08-Jan-1992 JohnRo
+ Created.
+ 09-Jan-1992 JohnRo
+ PC-LINT found a bug.
+ 20-Jan-1992 JohnRo
+ Changed prototype to match MIDL requirements.
+ 24-Jan-1992 JohnRo
+ Changed to use LPTSTR etc.
+ 15-Mar-1992 JohnRo
+ Update registry with new values.
+ Use improved setinfo support from ExpDir.h.
+ 22-Jul-1992 JohnRo
+ RAID 2252: repl should prevent export on Windows/NT.
+ Added debug output.
+ 26-Aug-1992 JohnRo
+ RAID 3602: NetReplExportDirSetInfo fails regardless of svc status.
+ 29-Sep-1992 JohnRo
+ RAID 7962: Repl APIs in wrong role kill svc.
+ Fix remote repl admin.
+ 30-Dec-1992 JohnRo
+ RAID 5340: NetReplExportDirSetInfo is confused (when role=import).
+ 05-Apr-1993 JohnRo
+ Use NetpKdPrint() where possible.
+ Made some changes suggested by PC-LINT 5.0
+
+--*/
+
+
+// These must be included first:
+
+#include <windef.h> // IN, VOID, LPTSTR, etc.
+#include <lmcons.h> // NET_API_STATUS, PARM equates, etc.
+#include <repldefs.h> // IF_DEBUG(), etc.
+#include <master.h> // LPMASTER_LIST_REC.
+#include <rpc.h> // Needed by <repl.h>.
+
+// These can be in any order:
+
+#include <dirname.h> // ReplDirNamesMatch(), ReplIsDirNameValid().
+#include <expdir.h> // ExportDirIsLevelValid(), etc.
+#include <lmerr.h> // ERROR_ and NERR_ equates; NO_ERROR.
+#include <lmrepl.h> // LPREPL_EDIR_INFO_1, REPL_EXTENT_ stuff, etc.
+#include <masproto.h> // GetMasterRec().
+#include <netdebug.h> // NetpKdPrint(), FORMAT_ equates, etc.
+#include <netlib.h> // NetpSetParmError().
+#include <netlock.h> // ACQUIRE_LOCK_SHARED(), RELEASE_LOCK().
+#include <prefix.h> // PREFIX_ equates.
+#include <repl.h> // My prototype (in MIDL-generated .h file).
+#include <replgbl.h> // ReplConfigLock, ReplConfigRole.
+#include <rpcutil.h> // NetpImpersonateClient(), NetpRevertToSelf().
+
+
+NET_API_STATUS
+NetrReplExportDirSetInfo (
+ IN LPTSTR UncServerName OPTIONAL,
+ IN LPTSTR DirName,
+ IN DWORD Level,
+ IN LPEXPORT_CONTAINER Buf, // RPC container (union)
+ OUT LPDWORD ParmError OPTIONAL // name used by NetpSetParmError() macro.
+ )
+
+/*++
+
+Routine Description:
+
+ Same as NetReplExportDirSetInfo.
+
+Arguments:
+
+ Same as NetReplExportDirSetInfo.
+
+Return Value:
+
+ Same as NetReplExportDirSetInfo.
+
+--*/
+
+{
+ NET_API_STATUS ApiStatus;
+ BOOL ConfigLocked = FALSE;
+ BOOL Impersonated = FALSE;
+ BOOL ListLocked = FALSE;
+ LPMASTER_LIST_REC MasterRecord;
+ LPVOID PossibleRec;
+
+ IF_DEBUG( EXPAPI ) {
+ NetpKdPrint(( PREFIX_REPL
+ "NetrReplExportDirSetInfo: union at " FORMAT_LPVOID ".\n",
+ (LPVOID) Buf ));
+ }
+
+ //
+ // Check for caller errors.
+ //
+ NetpSetParmError( PARM_ERROR_UNKNOWN ); // Assume error until proven...
+ if (Buf == NULL) {
+
+ NetpKdPrint(( PREFIX_REPL
+ "NetrReplExportDirSetInfo: null buffer pointer.\n" ));
+
+ return (ERROR_INVALID_PARAMETER);
+ }
+ PossibleRec = (LPVOID) (Buf->Info0); // BUGBUG: nonportable?
+
+ if (ReplIsDirNameValid( DirName ) == FALSE) {
+
+ NetpKdPrint(( PREFIX_REPL
+ "NetrReplExportDirSetInfo: dir name parm invalid.\n" ));
+
+ return (ERROR_INVALID_PARAMETER);
+
+ } else if ( !ExportDirIsLevelValid( Level, TRUE ) ) {
+
+ NetpKdPrint(( PREFIX_REPL
+ "NetrReplExportDirSetInfo: "
+ "bad info level (before switch).\n" ));
+
+ return (ERROR_INVALID_LEVEL);
+
+ } else if ( !ExportDirIsApiRecordValid( Level, PossibleRec, ParmError ) ) {
+
+ NetpKdPrint(( PREFIX_REPL
+ "NetrReplExportDirSetInfo: api record invalid (parm error "
+ FORMAT_DWORD ".\n",
+ (ParmError!=NULL) ? (*ParmError) : PARM_ERROR_UNKNOWN ));
+
+ return (ERROR_INVALID_PARAMETER);
+ }
+
+ //
+ // Impersonate caller, so security check (write to registry) reflects
+ // the client's process, not the repl service process.
+ //
+ ApiStatus = NetpImpersonateClient();
+ if (ApiStatus != NO_ERROR) {
+ goto Cleanup;
+ }
+ Impersonated = TRUE;
+
+ //
+ // Get a shared lock on config data so we can see if role includes export.
+ //
+ ACQUIRE_LOCK_SHARED( ReplConfigLock );
+ ConfigLocked = TRUE;
+
+ //
+ // Handle entire API if this half isn't running.
+ //
+ if ( !ReplRoleIncludesExport( ReplConfigRole ) ) {
+ ApiStatus = ExportDirConfigSetInfo (
+ UncServerName,
+ DirName,
+ Level,
+ PossibleRec,
+ ParmError );
+ goto Cleanup;
+ }
+
+ //
+ // Get read-write lock on master list.
+ //
+ ACQUIRE_LOCK( RMGlobalListLock );
+ ListLocked = TRUE;
+
+ //
+ // Find the master record for this dir name.
+ //
+ MasterRecord = GetMasterRec( DirName );
+ if (MasterRecord == NULL) { // Not found.
+
+ ApiStatus = NERR_UnknownDevDir;
+ goto Cleanup; // Don't forget to release lock...
+
+ } else { // Found.
+
+ switch (Level) {
+
+ case 1 :
+ {
+ LPREPL_EDIR_INFO_1 ApiRecord = PossibleRec;
+
+ if ( !ReplDirNamesMatch(ApiRecord->rped1_dirname, DirName) ) {
+ // dir name argument must match one in ApiRecord.
+ NetpSetParmError( 1 ); // Error in first field.
+ ApiStatus = ERROR_INVALID_PARAMETER;
+
+ NetpKdPrint(( PREFIX_REPL
+ "NetrReplExportDirSetInfo: "
+ "dir names do not match.\n" ));
+
+ } else {
+
+ //
+ // Write to registry and do security check.
+ //
+ ApiStatus = ExportDirWriteConfigData (
+ UncServerName,
+ ApiRecord->rped1_dirname,
+ ApiRecord->rped1_integrity,
+ ApiRecord->rped1_extent,
+ MasterRecord->lockcount,
+ MasterRecord->time_of_first_lock );
+ if (ApiStatus == NO_ERROR) {
+
+ MasterRecord->integrity = ApiRecord->rped1_integrity;
+ MasterRecord->extent = ApiRecord->rped1_extent;
+
+ NetpSetParmError( PARM_ERROR_NONE );
+ }
+ }
+ }
+ goto Cleanup; // Don't forget to release lock...
+
+ case REPL_EXPORT_INTEGRITY_INFOLEVEL :
+ {
+ DWORD NewIntegrity = * (LPDWORD) (LPVOID) PossibleRec;
+
+ //
+ // Write to registry and do security check.
+ //
+ ApiStatus = ExportDirWriteConfigData (
+ UncServerName,
+ MasterRecord->dir_name,
+ NewIntegrity,
+ MasterRecord->extent,
+ MasterRecord->lockcount,
+ MasterRecord->time_of_first_lock );
+ if (ApiStatus == NO_ERROR) {
+
+ MasterRecord->integrity = NewIntegrity;
+
+ NetpSetParmError( PARM_ERROR_NONE );
+ }
+
+ }
+ goto Cleanup; // Don't forget to release lock...
+
+ case REPL_EXPORT_EXTENT_INFOLEVEL :
+ {
+ DWORD NewExtent = * (LPDWORD) (LPVOID) PossibleRec;
+
+ //
+ // Write to registry and do security check.
+ //
+ ApiStatus = ExportDirWriteConfigData (
+ UncServerName,
+ MasterRecord->dir_name,
+ MasterRecord->integrity,
+ NewExtent,
+ MasterRecord->lockcount,
+ MasterRecord->time_of_first_lock );
+ if (ApiStatus == NO_ERROR) {
+
+ MasterRecord->extent = NewExtent;
+
+ NetpSetParmError( PARM_ERROR_NONE );
+ }
+
+ }
+ goto Cleanup; // Don't forget to release lock...
+
+ default :
+
+ ApiStatus = ERROR_INVALID_LEVEL;
+
+ NetpKdPrint(( PREFIX_REPL
+ "NetrReplExportDirSetInfo: invalid level (switch).\n" ));
+
+ goto Cleanup; // Don't forget to release lock...
+ }
+
+ /*NOTREACHED*/
+
+ }
+
+Cleanup:
+
+ //
+ // Release locks and tell caller how everything went.
+ //
+ if (ListLocked) {
+ RELEASE_LOCK( RMGlobalListLock );
+ }
+
+ if (ConfigLocked) {
+ RELEASE_LOCK( ReplConfigLock );
+ }
+
+ if (Impersonated) {
+ (VOID) NetpRevertToSelf();
+ }
+ IF_DEBUG( EXPAPI ) {
+ NetpKdPrint(( PREFIX_REPL
+ "NetrReplExportDirSetInfo: exiting, status: "
+ FORMAT_API_STATUS ".\n", ApiStatus ));
+ }
+
+ return (ApiStatus);
+
+}
diff --git a/private/net/svcdlls/repl/server/expstart.c b/private/net/svcdlls/repl/server/expstart.c
new file mode 100644
index 000000000..f04ddcc16
--- /dev/null
+++ b/private/net/svcdlls/repl/server/expstart.c
@@ -0,0 +1,197 @@
+/*++
+
+Copyright (c) 1992-1993 Microsoft Corporation
+
+Module Name:
+
+ ExpStart.c
+
+Abstract:
+
+ Contains ExportDirStartRepl().
+
+Author:
+
+ John Rogers (JohnRo) 10-Feb-1992
+
+Environment:
+
+ User mode only.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+ Tab size is set to 4.
+
+Revision History:
+
+ 09-Feb-1992 JohnRo
+ Created this routine to allow dynamic role changes.
+ 05-Mar-1992 JohnRo
+ Changed interface to match new service controller.
+ 06-Mar-1992 JohnRo
+ Avoid starting RPC server too soon.
+ 22-Mar-1992 JohnRo
+ Added more debug output.
+ 01-Apr-1992 JohnRo
+ Up the stack size, just to be on the safe side.
+ Avoid assertion if export startup fails.
+ 09-Jul-1992 JohnRo
+ RAID 10503: srv mgr: repl dialog doesn't come up.
+ Use PREFIX_ equates.
+ 19-Aug-1992 JohnRo
+ RAID 2115: repl svc should wait while stopping or changing role.
+ 11-Mar-1993 JohnRo
+ RAID 12100: stopping repl sometimes goes into infinite loop.
+ 02-Apr-1993 JohnRo
+ Use NetpKdPrint() where possible.
+ 24-May-1993 JohnRo
+ RAID 10587: repl could deadlock with changed NetpStopRpcServer(), so
+ just call ExitProcess() instead.
+
+--*/
+
+
+// These must be included first:
+
+#include <windows.h> // CreateThread(), DWORD, etc.
+#include <lmcons.h> // IN, NET_API_STATUS, etc.
+
+// These may be included in any order:
+
+#include <expdir.h> // My prototype.
+#include <netdebug.h> // DBGSTATIC, NetpKdPrint(), FORMAT_ equates.
+#include <prefix.h> // PREFIX_ equates.
+#include <repldefs.h> // IF_DEBUG().
+#include <replgbl.h> // ReplGlobalMasterThreadHandle.
+#include <winerror.h> // ERROR_ and NO_ERROR equates.
+
+
+#define MASTER_STACK_SIZE (24 * 1024) /* BUGBUG: Wild guess! */
+
+
+// Start replicating (exporting). Wait for master thread to startup.
+// Called when service starts or user does NetReplSetInfo() and changes role.
+NET_API_STATUS
+ExportDirStartRepl (
+ IN BOOL ServiceIsStarting
+ )
+
+{
+ NET_API_STATUS ApiStatus;
+ DWORD EventTriggered;
+ DWORD ThreadID;
+ LPVOID ThreadParm; // NULL if service is starting;
+ // non-NULL if role is changing.
+ HANDLE WaitHandles[3];
+
+ IF_DEBUG( MASTER ) {
+ NetpKdPrint(( PREFIX_REPL_MASTER
+ "ExportDirStartRepl: beginning...\n" ));
+ }
+
+ // Reset events in case we've ever been running before.
+ (VOID) ResetEvent( ReplGlobalExportStartupEvent );
+ (VOID) ResetEvent( ReplGlobalMasterTerminateEvent );
+
+ if (ServiceIsStarting) {
+ ThreadParm = NULL;
+ } else {
+ ThreadParm = (LPVOID) & ApiStatus; // any non-NULL addr will do.
+ }
+
+ //
+ // Start up master thread.
+ //
+
+ ReplGlobalMasterThreadHandle = CreateThread(
+ NULL,
+ MASTER_STACK_SIZE,
+ ReplMasterMain,
+ ThreadParm,
+ 0,
+ &ThreadID);
+
+ //
+ // Handle error starting thread.
+ //
+
+ if (ReplGlobalMasterThreadHandle == NULL) {
+
+ ApiStatus = (NET_API_STATUS) GetLastError();
+
+ NetpAssert( ApiStatus != NO_ERROR );
+
+ ReplFinish( ApiStatus );
+
+ return (ApiStatus);
+ }
+
+ //
+ // Wait for master to setup.
+ //
+
+ WaitHandles[0] = ReplGlobalMasterTerminateEvent; // terminate
+ WaitHandles[1] = ReplGlobalExportStartupEvent; // master success
+ WaitHandles[2] = ReplGlobalMasterThreadHandle; // master died
+
+#define DUMP_WAIT_HANDLES( array, total ) \
+ { \
+ DWORD index; \
+ for (index=0; index<total; ++index) { \
+ NetpKdPrint(( " " #array "[" FORMAT_DWORD "] is " \
+ FORMAT_HEX_DWORD ".\n", index, (DWORD) array[index] )); \
+ } \
+ }
+
+ IF_DEBUG( MASTER ) {
+ NetpKdPrint(( PREFIX_REPL_MASTER
+ "ExportDirStartRepl: wait........................\n" ));
+ DUMP_WAIT_HANDLES( WaitHandles, 3 );
+ }
+ EventTriggered = WaitForMultipleObjects( 3, WaitHandles, FALSE,
+ (DWORD) -1 );
+
+ IF_DEBUG( MASTER ) {
+ NetpKdPrint(( PREFIX_REPL_MASTER
+ "ExportDirStartRepl: EventTriggered = " FORMAT_DWORD
+ ".\n", EventTriggered ));
+ }
+
+ if (EventTriggered == 0) { // terminate during startup.
+
+ ApiStatus = ReplGlobalUninstallUicCode;
+ NetpAssert( ApiStatus != NO_ERROR ); // Should be set by master thread.
+ goto Cleanup;
+
+ } else if ( (EventTriggered != 1) && (EventTriggered != 2) ) {
+
+ ApiStatus = GetLastError();
+ NetpAssert( ApiStatus != NO_ERROR ); // BUGBUG
+ goto Cleanup;
+
+ }
+
+ NetpAssert( ReplGlobalMasterThreadHandle != NULL );
+
+ return (NO_ERROR);
+
+
+Cleanup: // Cleanup for abnormal exits only!
+
+ NetpKdPrint(( PREFIX_REPL_MASTER
+ "ExportDirStartRepl: (error) cleanup, ApiStatus="
+ FORMAT_API_STATUS ".\n", ApiStatus ));
+
+ NetpAssert( ApiStatus != NO_ERROR );
+
+ if (ReplGlobalMasterThreadHandle != NULL) {
+
+ // wait for master thread to complete
+ (VOID) WaitForSingleObject( ReplGlobalMasterThreadHandle, (DWORD) -1 );
+
+ // Avoid confusing anyone else.
+ ReplGlobalMasterThreadHandle = NULL;
+
+ }
+
+ return (ApiStatus);
+
+} // ExportDirStartRepl
diff --git a/private/net/svcdlls/repl/server/expstop.c b/private/net/svcdlls/repl/server/expstop.c
new file mode 100644
index 000000000..18c96987d
--- /dev/null
+++ b/private/net/svcdlls/repl/server/expstop.c
@@ -0,0 +1,126 @@
+/*++
+
+Copyright (c) 1992-1993 Microsoft Corporation
+
+Module Name:
+
+ ExpStop.c
+
+Abstract:
+
+ Contains ExportDirStopRepl().
+
+Author:
+
+ John Rogers (JohnRo) 09-Feb-1992
+
+Environment:
+
+ User mode only.
+ Uses Win32 APIs.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+ 09-Feb-1992 JohnRo
+ Created this routine to allow dynamically changing role.
+ 22-Mar-1992 JohnRo
+ Oops, this routine was setting the wrong event.
+ 18-Aug-1992 JohnRo
+ RAID 2115: repl svc should wait while stopping or changing role.
+ Use PREFIX_ equates.
+ 11-Mar-1993 JohnRo
+ RAID 12100: stopping repl sometimes goes into infinite loop.
+ 02-Apr-1993 JohnRo
+ Use NetpKdPrint() where possible.
+
+--*/
+
+
+// These must be included first:
+
+#include <windows.h> // IN, SetEvent(), DWORD, etc.
+#include <lmcons.h> // NET_API_STATUS.
+
+// These may be included in any order:
+
+#include <expdir.h> // My prototype.
+#include <netdebug.h> // NetpKdPrint(), FORMAT_ equates.
+#include <prefix.h> // PREFIX_ equates.
+#include <repldefs.h> // IF_DEBUG().
+#include <replgbl.h> // ReplGlobal variables.
+#include <winerror.h> // NO_ERROR.
+
+
+NET_API_STATUS
+ExportDirStopRepl (
+ VOID
+ )
+{
+ NET_API_STATUS ApiStatus;
+ DWORD WaitStatus;
+
+ IF_DEBUG(REPL) {
+ NetpKdPrint(( PREFIX_REPL_MASTER
+ "ExportDirStopRepl: informing export thread...\n" ));
+ }
+
+ //
+ // Tell the export (master) thread that it should stop.
+ //
+ if( !SetEvent( ReplGlobalMasterTerminateEvent ) ) {
+
+ ApiStatus = GetLastError();
+
+ NetpKdPrint(( PREFIX_REPL_MASTER
+ "ExportDirStopRepl: SetEvent Failed with error "
+ FORMAT_API_STATUS "\n", ApiStatus ));
+
+ NetpAssert( ApiStatus != NO_ERROR );
+ return (ApiStatus);
+
+ }
+
+ if (ReplGlobalMasterThreadHandle != NULL) {
+
+ //
+ // Wait for the thread to end, if it hasn't already.
+ //
+
+ IF_DEBUG(REPL) {
+ NetpKdPrint(( PREFIX_REPL_MASTER
+ "ExportDirStopRepl: waiting...\n" ));
+ }
+
+ WaitStatus = WaitForSingleObject(
+ ReplGlobalMasterThreadHandle,
+ (DWORD) -1); // no timeout. BUGBUG!
+
+ // close the thread handle (BUGBUG: not necessary?)
+ (VOID) CloseHandle( ReplGlobalMasterThreadHandle );
+
+ if ( WaitStatus != 0 ) {
+ ApiStatus = (NET_API_STATUS) GetLastError();
+
+ NetpKdPrint(( PREFIX_REPL_MASTER
+ "ExportDirStopRepl: unexpected wait status "
+ FORMAT_DWORD ", api status " FORMAT_API_STATUS ".\n",
+ WaitStatus, ApiStatus ));
+
+ NetpAssert( ApiStatus != NO_ERROR );
+
+ } else {
+
+ ApiStatus = NO_ERROR;
+
+ }
+
+ } else {
+ ApiStatus = NO_ERROR;
+ }
+
+ // Avoid confusing anyone else.
+ ReplGlobalMasterThreadHandle = NULL;
+
+ return (ApiStatus);
+}
diff --git a/private/net/svcdlls/repl/server/filefind.c b/private/net/svcdlls/repl/server/filefind.c
new file mode 100644
index 000000000..5b2b9a7b6
--- /dev/null
+++ b/private/net/svcdlls/repl/server/filefind.c
@@ -0,0 +1,359 @@
+/*++
+
+Copyright (c) 1991-1993 Microsoft Corporation
+
+Module Name:
+
+ filefind.c
+
+Abstract:
+
+ This module implements replicator FindFirst/FindNext.
+
+ It is identical to the WIN32 FindFirst/FindNext except it returns
+ the DOS EA length.
+
+Author:
+
+ 4-Dec-1991 (madana)
+
+Revision History:
+
+ 11-Dec-1991 JohnRo
+ Avoid unnamed structure fields to allow MIPS builds.
+ 27-Jan-1992 JohnRo
+ Changed to use LPTSTR etc.
+ Need more permission to do NtFileOpen of ".".
+ Use FORMAT_ equates.
+ Made changes suggested by PC-LINT.
+ 25-Feb-1992 JohnRo
+ Try different NtOpenFile parms.
+ 26-Mar-1992 JohnRo
+ New ReplFind routines interface.
+ More work on NtOpenFile problems.
+ Added debug output to the ReplFind{First,Next}File() routines.
+ 16-Jul-1992 JohnRo
+ RAID 10503: srv mgr: repl dialog doesn't come up.
+ Avoid compiler warnings.
+ Use PREFIX_ equates.
+ 06-Jan-1993 JohnRo
+ RAID 6701: repl svc should include EA size in checksum.
+ Minor cleanup of code.
+ Be a little more paranoid in free build in ReplFindClose().
+ Added debug output.
+ 22-Feb-1993 JohnRo
+ Fix infinite loop on two or more EAs.
+ RAID 9739: replication giving system error 38 (ERROR_HANDLE_EOF).
+ 01-Apr-1993 JohnRo
+ NtQueryEaFile() sometimes fails with STATUS_INVALID_PARAMETER.
+ 19-Apr-1993 JohnRo
+ RAID 829: replication giving system error 38 (ERROR_HANDLE_EOF).
+ Support ReplSum test app.
+ Made changes suggested by PC-LINT 5.0.
+ 28-Apr-1993 JohnRo
+ Use NetpKdPrint() where possible.
+ 07-May-1993 JohnRo
+ RAID 3258: file not updated due to ERROR_INVALID_USER_BUFFER.
+
+--*/
+
+
+#include <windows.h>
+#include <lmcons.h>
+
+#include <filefind.h> // External Declarations for this file.
+#include <lmapibuf.h> // NetApiBufferAllocate(), etc.
+#include <lmerrlog.h> // NELOG_ equates.
+#include <netdebug.h> // NetpKdPrint(), FORMAT_ equates.
+#include <netlib.h> // IN_RANGE().
+#include <prefix.h> // PREFIX_ equates.
+#include <repldefs.h> // IF_DEBUG(), ReplErrorLog(), etc.
+#include <tstring.h> // NetpCopyWStrFromTStr(), TCHAR_ equates, etc.
+
+
+DBGSTATIC VOID
+ReplBuildFullPathInFindBuffer(
+ IN LPTSTR lpDirWeAreSearching OPTIONAL,
+ IN OUT LPREPL_FIND_HANDLE lpReplHandle,
+ IN LPREPL_WIN32_FIND_DATA lpFindFileData
+ )
+{
+ LPTSTR lpLastPathSep;
+ NetpAssert( lpFindFileData != NULL );
+ NetpAssert( lpReplHandle != INVALID_REPL_HANDLE );
+ NetpAssert( lpReplHandle != NULL );
+
+ if (lpDirWeAreSearching != NULL) { // First time.
+
+ IF_DEBUG( FILEFIND ) {
+ NetpKdPrint(( PREFIX_REPL
+ "ReplBuildFullPathInFindBuffer: first call with '"
+ FORMAT_LPTSTR "'.\n", lpDirWeAreSearching ));
+ }
+
+ //
+ // Copy whole thing (e.g. c:\very\long\path\*.*" or
+ // "\\server\share\path\*.*".
+ //
+ (void) STRCPY( lpReplHandle->tchFullPath, lpDirWeAreSearching );
+
+ // Get rid of trailing "\*.*" from our copy.
+ lpLastPathSep = STRRCHR( lpReplHandle->tchFullPath, TCHAR_BACKSLASH );
+ NetpAssert( lpLastPathSep != NULL );
+ NetpAssert( STRCMP( lpLastPathSep+1, STAR_DOT_STAR ) == 0 );
+ *lpLastPathSep = TCHAR_EOS;
+
+ lpReplHandle->dwDirNameLen = STRLEN( lpReplHandle->tchFullPath );
+ NetpAssert( IN_RANGE( lpReplHandle->dwDirNameLen, 1, PATHLEN ) );
+
+ (void) STRCAT( lpReplHandle->tchFullPath, SLASH ); // cpy backslash.
+ (void) STRCAT( lpReplHandle->tchFullPath,
+ lpFindFileData->fdFound.cFileName );
+
+
+ } else { // Not first time.
+
+ IF_DEBUG( FILEFIND ) {
+ NetpKdPrint(( PREFIX_REPL
+ "ReplBuildFullPathInFindBuffer: next call with '"
+ FORMAT_LPTSTR "'.\n", lpReplHandle->tchFullPath ));
+ }
+
+ NetpAssert( IN_RANGE( lpReplHandle->dwDirNameLen, 1, PATHLEN ) );
+ NetpAssert(
+ lpReplHandle->tchFullPath[ lpReplHandle->dwDirNameLen ]
+ == TCHAR_BACKSLASH );
+ lpReplHandle->tchFullPath[ lpReplHandle->dwDirNameLen ] = TCHAR_EOS;
+ (void) STRCAT( lpReplHandle->tchFullPath, SLASH );
+ (void) STRCAT( lpReplHandle->tchFullPath,
+ lpFindFileData->fdFound.cFileName );
+ }
+
+ NetpAssert( STRLEN(lpReplHandle->tchFullPath) <= PATHLEN );
+
+ IF_DEBUG( FILEFIND ) {
+ NetpKdPrint(( PREFIX_REPL "ReplBuildFullPathInFindBuffer: built '"
+ FORMAT_LPTSTR "'.\n", lpReplHandle->tchFullPath ));
+ }
+
+} // ReplBuildFullPathInFindBuffer
+
+
+LPREPL_FIND_HANDLE
+ReplFindFirstFile(
+ IN LPTSTR lpFileName,
+ OUT LPREPL_WIN32_FIND_DATA lpFindFileData
+ )
+
+/*++
+
+Routine Description:
+
+ Same as FindFirstFile and in addition this function will return
+ nEaSize that will match to the EaSize returned by DosFindFirst2.
+
+Arguments:
+
+ lpFileName : directory name.
+ lpFindFileData : return data buffer.
+
+Return Value:
+
+ same as FindFirstFile return value.
+
+--*/
+
+{
+ NET_API_STATUS ApiStatus;
+ LPREPL_FIND_HANDLE lpReplHandle;
+
+ NetpAssert( lpFileName != NULL );
+ NetpAssert( lpFindFileData != NULL );
+
+ ApiStatus = NetApiBufferAllocate(
+ sizeof(REPL_FIND_HANDLE),
+ (LPVOID *) (LPVOID) & lpReplHandle );
+ if (ApiStatus != NO_ERROR) {
+ return (INVALID_REPL_HANDLE);
+ }
+ NetpAssert( lpReplHandle != NULL );
+
+ lpReplHandle->hWindows = FindFirstFile( lpFileName,
+ (LPWIN32_FIND_DATA) (LPVOID) lpFindFileData );
+
+ if ( lpReplHandle->hWindows == INVALID_HANDLE_VALUE ) {
+ (void) NetApiBufferFree( lpReplHandle );
+
+ ApiStatus = (NET_API_STATUS) GetLastError();
+
+ NetpKdPrint(( PREFIX_REPL
+ "ReplFindFirstFile: FindFirstFile failed, api status "
+ FORMAT_API_STATUS ".\n", ApiStatus ));
+
+ NetpAssert( ApiStatus != NO_ERROR );
+
+ if (ApiStatus == ERROR_HANDLE_EOF) {
+ // BUGBUG: quiet this debug output eventually.
+ NetpKdPrint(( PREFIX_REPL
+ "ReplFindFirstFile: GOT HANDLE EOF!\n" ));
+ }
+
+ ReplErrorLog(
+ NULL, // no server name (local)
+ NELOG_ReplSysErr, // log code
+ ApiStatus, // error code we got
+ NULL, // no optional str 1
+ NULL ); // no optional str 2
+
+ return (INVALID_REPL_HANDLE);
+ }
+
+ ReplBuildFullPathInFindBuffer( lpFileName, lpReplHandle, lpFindFileData );
+
+#if 0
+ lpReplHandle->dwDirNameLen = STRLEN( lpFileName );
+ NetpAssert( IN_RANGE( lpReplHandle->dwDirNameLen, 1, PATHLEN ) );
+
+ (void) STRCPY( lpReplHandle->tchFullPath, lpFileName );
+ (void) STRCAT( lpReplHandle->tchFullPath, SLASH );
+ (void) STRCAT( lpReplHandle->tchFullPath,
+ lpFindFileData->fdFound.cFileName );
+#endif // 0
+
+ IF_DEBUG( FILEFIND ) {
+ NetpKdPrint(( PREFIX_REPL "ReplFindFirstFile: found " FORMAT_LPTSTR
+ " with attr " FORMAT_HEX_DWORD ".\n",
+ lpReplHandle->tchFullPath,
+ lpFindFileData->fdFound.dwFileAttributes ));
+ }
+
+ lpFindFileData->nEaSize =
+ ReplGetEaSize( lpReplHandle->tchFullPath );
+
+ return (lpReplHandle);
+
+}
+
+BOOL
+ReplFindNextFile(
+ IN LPREPL_FIND_HANDLE lpReplHandle,
+ IN OUT LPREPL_WIN32_FIND_DATA lpFindFileData
+ )
+
+/*++
+
+Routine Description:
+
+ Same as FindFirstFile and in addition this function will return
+ nEaSize that will match to the EaSize returned by DosFindFirst2.
+
+Arguments:
+
+ hFindFile : directory handle.
+ lpFindFileData : return data buffer.
+
+Return Value:
+
+ same as FindFirstFile return value.
+
+--*/
+{
+ NET_API_STATUS ApiStatus;
+ BOOL ReturnValue;
+
+ NetpAssert( lpFindFileData != NULL );
+ NetpAssert( lpReplHandle != INVALID_REPL_HANDLE );
+ NetpAssert( lpReplHandle != NULL );
+
+ ReturnValue = FindNextFile( lpReplHandle->hWindows,
+ (LPWIN32_FIND_DATA) (LPVOID) lpFindFileData );
+
+ if ( !ReturnValue ) {
+
+ ApiStatus = (NET_API_STATUS) GetLastError();
+ NetpAssert( ApiStatus != NO_ERROR );
+ if (ApiStatus != ERROR_NO_MORE_FILES) {
+ NetpKdPrint(( PREFIX_REPL
+ "ReplFindNextFirst: FindNextFile failed, api status "
+ FORMAT_API_STATUS ".\n", ApiStatus ));
+
+ if (ApiStatus == ERROR_HANDLE_EOF) {
+ // BUGBUG: quiet this debug output eventually.
+ NetpKdPrint(( PREFIX_REPL
+ "ReplFindNextFile: GOT HANDLE EOF!\n" ));
+ }
+
+ // Log this.
+ ReplErrorLog(
+ NULL, // no server name (local)
+ NELOG_ReplSysErr, // log code
+ ApiStatus, // error code we got
+ NULL, // no optional str 1
+ NULL ); // no optional str 2
+ }
+
+ return ReturnValue;
+
+ }
+
+ ReplBuildFullPathInFindBuffer( NULL, lpReplHandle, lpFindFileData );
+
+#if 0
+ NetpAssert( IN_RANGE( lpReplHandle->dwDirNameLen, 1, PATHLEN ) );
+ NetpAssert(
+ lpReplHandle->tchFullPath[ lpReplHandle->dwDirNameLen ]
+ == TCHAR_BACKSLASH );
+ lpReplHandle->tchFullPath[ lpReplHandle->dwDirNameLen ] = TCHAR_EOS;
+ (void) STRCAT( lpReplHandle->tchFullPath, SLASH );
+ (void) STRCAT( lpReplHandle->tchFullPath,
+ lpFindFileData->fdFound.cFileName );
+#endif // 0
+
+ IF_DEBUG( FILEFIND ) {
+ NetpKdPrint(( PREFIX_REPL "ReplFindNextFile: found " FORMAT_LPTSTR
+ " with attr " FORMAT_HEX_DWORD ".\n",
+ lpReplHandle->tchFullPath,
+ lpFindFileData->fdFound.dwFileAttributes ));
+ }
+
+ lpFindFileData->nEaSize =
+ ReplGetEaSize( lpReplHandle->tchFullPath );
+
+ return ReturnValue;
+
+}
+
+BOOL
+ReplFindClose(
+ IN LPREPL_FIND_HANDLE lpReplHandle
+ )
+
+/*++
+
+Routine Description:
+
+ Same as FindClose.
+
+Arguments:
+
+ hFindFile : directory handle.
+
+Return Value:
+
+ same as FindClose.
+
+--*/
+
+{
+
+ BOOL Result;
+
+ if ( (lpReplHandle==NULL) || (lpReplHandle==INVALID_REPL_HANDLE) ) {
+ return (FALSE); // error.
+ }
+
+ Result = FindClose( lpReplHandle->hWindows );
+ (void) NetApiBufferFree( lpReplHandle );
+ return (Result);
+
+}
diff --git a/private/net/svcdlls/repl/server/filefind.h b/private/net/svcdlls/repl/server/filefind.h
new file mode 100644
index 000000000..30b7265d6
--- /dev/null
+++ b/private/net/svcdlls/repl/server/filefind.h
@@ -0,0 +1,97 @@
+/*++
+
+Copyright (c) 1987-1993 Microsoft Corporation
+
+Module Name:
+
+ filefind.h
+
+Abstract:
+
+ Contains structures and function prototypes for a win32-like find
+ file which also returns the EA size for the files.
+
+Author:
+
+ 17-Oct-1991 (cliffv)
+ Merged from winbase.h
+
+Environment:
+
+ User mode only.
+ Contains NT-specific code.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+ 04-Dec-1991 (madana)
+ redefined _REPL_WIN32_FIND_DATAW structures for better alignment and
+ removed ANSI related defs.
+ 11-Dec-1991 JohnRo
+ Avoid unnamed structure fields to allow MIPS builds.
+ Delete tabs in source file.
+ 24-Jan-1992 JohnRo
+ Changed to use LPTSTR etc.
+ 26-Mar-1992 JohnRo
+ Added tchFullPath field to REPL_WIN32_FIND_DATA structure.
+ 11-Jan-1993 JohnRo
+ RAID 6710: repl cannot manage dir with 2048 files.
+ 06-Apr-1993 JohnRo
+ Support ReplSum test app.
+ 07-May-1993 JohnRo
+ RAID 3258: file not updated due to ERROR_INVALID_USER_BUFFER.
+
+
+--*/
+
+
+#ifndef _FILEFIND_
+#define _FILEFIND_
+
+
+#include <lmcons.h> // NET_API_STATUS, PATHLEN.
+
+
+#define INVALID_REPL_HANDLE NULL /* Was (HANDLE)(-1) for Win32. */
+
+
+typedef struct _REPL_FIND_HANDLE {
+ HANDLE hWindows;
+ TCHAR tchFullPath[PATHLEN+1]; // Full path of this file.
+ DWORD dwDirNameLen; // Number of chars (not incl last "\file").
+} REPL_FIND_HANDLE, *PREPL_FIND_HANDLE, *LPREPL_FIND_HANDLE;
+
+typedef struct _REPL_WIN32_FIND_DATA {
+ WIN32_FIND_DATA fdFound;
+ DWORD nEaSize;
+} REPL_WIN32_FIND_DATA, *PREPL_WIN32_FIND_DATA, *LPREPL_WIN32_FIND_DATA;
+
+//
+// Function prototypes.
+//
+
+NET_API_STATUS
+ReplCountDirectoryEntries(
+ IN LPCTSTR FullDirPath,
+ OUT LPDWORD EntryCountPtr
+ );
+
+LPREPL_FIND_HANDLE
+ReplFindFirstFile(
+ IN LPTSTR lpFileName,
+ OUT LPREPL_WIN32_FIND_DATA lpFindFileData
+ );
+
+BOOL
+ReplFindNextFile(
+ IN OUT LPREPL_FIND_HANDLE hFindFile,
+ IN OUT LPREPL_WIN32_FIND_DATA lpFindFileData
+ );
+
+BOOL
+ReplFindClose(
+ IN LPREPL_FIND_HANDLE hFindFile
+ );
+
+
+#endif // _FILEFIND_
diff --git a/private/net/svcdlls/repl/server/getparm.c b/private/net/svcdlls/repl/server/getparm.c
new file mode 100644
index 000000000..3b1f35df2
--- /dev/null
+++ b/private/net/svcdlls/repl/server/getparm.c
@@ -0,0 +1,506 @@
+#if 0 // entire file is obsolete --JR, 30-Jan-1992
+
+
+/*++
+
+Copyright (c) 1987-1992 Microsoft Corporation
+
+Module Name:
+ getparm.c
+
+Abstract:
+ Deals with reading REPL.INI for INTEGRITY and EXTENT parametrs.
+ Basically scavanged from the GetConfig api
+
+Author:
+ Ported from Lan Man 2.x
+
+Environment:
+ Contains NT-specific code.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+ 10/07/91 (madana)
+ ported to NT. Converted to NT style.
+ 07-Jan-1992 JohnRo
+ Changed local isspace() to iswspace() to avoid ctype.h conflicts.
+ 16-Jan-1992 JohnRo
+ Avoid using private logon functions.
+
+
+--*/
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windef.h>
+#include <winbase.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <lmcons.h>
+#include <lmerr.h>
+#include <netlib.h>
+#include <netdebug.h> // NetpKdPrint(()), format_pointer
+#include <tstring.h> // NetpAlloc{type}From{type}(), etc.
+
+// repl headers
+
+#include <repldefs.h>
+#include <iniparm.h>
+#include <master.h>
+#include <pulser.h>
+#include <masproto.h>
+#include <wcslocal.h>
+
+// Local (static) data and functions
+
+DBGSTATIC DWORD GlobalFileOffset = 0;
+
+BOOL
+iswspace(
+ IN WCHAR c
+ );
+
+VOID
+GetReplIni(
+ OUT LPDWORD integrity,
+ OUT LPDWORD extent
+ )
+/*++
+
+Routine Description :
+ Attempts to read file REPL.INI in the current directory, if successfull
+ reads the INTEGRITY and EXTENT values and returns them, otherwise
+ plugs defaults.
+
+Arguments :
+
+Return Value :
+ integrity - pointer to INTEGRITY value.
+ extent - pointer to EXTENT value.
+
+--*/
+{
+ int fhand;
+ DWORD BytesRead, parmlen;
+ BYTE buf[30];
+ CHAR Dummy;
+ OVERLAPPED Overlapped;
+ OFSTRUCT ReOpenBuff;
+
+ //
+ // plug defaults
+ //
+
+ *integrity = FILE_INTG;
+ *extent = TREE_EXT;
+
+ fhand = OpenFile(REPL_INI_A,
+ (LPOFSTRUCT)&ReOpenBuff,
+ OF_READWRITE | OF_SHARE_DENY_WRITE);
+ if(fhand == -1) {
+ return; // with defaults
+ }
+
+ GlobalFileOffset = 0;
+
+ if ((GetParm(INTEGRITY_SW,
+ (LPBYTE)buf,
+ sizeof(buf),
+ (LPDWORD) &parmlen,
+ fhand)) == 0)
+ if (parmlen >= (DWORD)wcslen(TREE_SW))
+ if (wcsncmpi((LPWSTR)buf, TREE_SW, (DWORD)wcslen(TREE_SW)) == 0)
+ *integrity = TREE_INTG;
+
+ //
+ // reset file pointer to read the next parm
+ //
+
+ RtlZeroMemory( &Overlapped, sizeof(Overlapped) );
+ Overlapped.Offset = 0;
+ GlobalFileOffset = 0;
+
+ //
+ // make a dummy read
+ //
+
+ if(ReadFile((HANDLE)fhand, &Dummy, 0, &BytesRead, &Overlapped)) {
+ if (GetParm(EXTENT_SW,
+ (LPBYTE)buf,
+ sizeof(buf),
+ (LPDWORD)&parmlen,
+ fhand)) {
+ if (parmlen >= (DWORD)wcslen(FILE_SW)) {
+ if (wcsncmpi((LPWSTR)buf,
+ FILE_SW,
+ (DWORD)wcslen(FILE_SW)) == 0) {
+ *extent = FILE_EXT;
+ }
+ }
+ }
+ }
+
+ CloseHandle((HANDLE)fhand);
+}
+
+BOOL
+GetParm(
+ IN LPWSTR parameter,
+ OUT LPBYTE buf,
+ IN WORD buflen,
+ OUT PDWORD parmlen,
+ IN int fh
+ )
+/*++
+
+Routine Description :
+ return the value of the parameter specified.
+
+Arguments :
+ parameter : name of the parameter whose value to be retrieved
+ buf : buffer for return value
+ buflen : length of the given storage
+ parmlen : place to return the length of the parameter retrieved.
+ fh : file handle
+
+Return Value :
+ return TRUE if successful
+ FALSE otherwise
+
+--*/
+{
+ WCHAR tmpbuf[PATHLEN+1];
+ DWORD tbuflen = sizeof(tmpbuf);
+ LPWSTR tptr;
+
+ while ((next_parameter(fh, (LPBYTE)tmpbuf, tbuflen))) {
+
+ if (pwcscmp(tmpbuf, parameter)) {
+
+ //
+ // Assuming that the string we have obtained has an
+ // '=' in it, we will return the text to the right
+ // of the '='. Other wise, we return a nul-string.
+ //
+
+ if ((tptr = wcschr(tmpbuf, L'=')) == NULL)
+ return FALSE;
+ else
+ tptr++;
+
+ if((*parmlen = (DWORD)wcslen(tptr)) >= buflen) {
+ return FALSE;
+ }
+ else {
+ wcscpy((LPWSTR)buf, tptr);
+ return TRUE;
+ }
+ }
+ }
+
+ return FALSE;
+
+}
+
+BOOL
+next_parameter(
+ IN int fh,
+ OUT LPBYTE buf,
+ IN DWORD buflen
+ )
+/*++
+
+Routine Description :
+ get next parameter for this component
+
+Arguments :
+ fh : file handle
+ buf : place holder
+ buflen : buffer length
+
+Return Value :
+ return TRUE if successful
+ FALSE otherwise
+
+ NOTE :
+
+ buffer will contain the parameter line, stripped of
+ leading and trailing whitespace, as well as any
+ whitespace around the first '='.
+
+--*/
+{
+ LPWSTR p, endp, tempp;
+ DWORD count;
+
+ while (get_conf_line(fh, buf, buflen)) {
+
+ IF_DEBUG(MASTER) {
+ NetpKdPrint(("[REPL] Next_param ... buf is %ws\n", buf));
+ }
+
+ p = (LPWSTR)buf;
+ while (iswspace(*p))
+ p++; // Skip over leading space
+
+ IF_DEBUG(MASTER) {
+ NetpKdPrint(("[REPL] Next_param ... stripped buf is %ws\n", p));
+ }
+
+ if (*p == L'[')
+ return FALSE; // Stop if reached next component
+
+ if (*p != L';' && *p != 0) // Make sure not comment or empty
+ {
+
+ //
+ // We now have the following task: given the text
+ // pointed to by p, which is in the form XXX=YYY or
+ // just XXX, move it to the front of the buffer and
+ // also strip out spaces leading, trailing, and surrounding
+ // the '='.
+ //
+
+ endp = wcschr(p, L'=');
+ if (endp != NULL) {
+ tempp = endp - 1;
+ while (iswspace(*tempp))
+ tempp--;
+ count = (tempp - p) + 1;
+ wcsncpy((LPWSTR)buf, p, count);
+ buf[count++] = L'=';
+ p = endp + 1;
+ while (iswspace(*p))
+ p++;
+ } else
+ count = 0;
+
+ endp = p + (DWORD)wcslen(p);
+ while (iswspace(*--endp))
+ *endp = 0;
+
+ wcscpy((LPWSTR)&(buf[count]), p);
+
+ IF_DEBUG(MASTER) {
+ NetpKdPrint(("[REPL] Next_param ... return buf is %ws\n", buf));
+ }
+
+ return TRUE;
+ }
+ }
+
+ IF_DEBUG(MASTER) {
+ NetpKdPrint(("[REPL] Next_param ... Hit EOF or next component\n"));
+ }
+ return FALSE;
+}
+
+BOOL
+get_conf_line(
+ IN int fh,
+ OUT LPBYTE buf,
+ IN DWORD buflen
+ )
+/*++
+
+Routine Description :
+ get a line from the given file.
+
+Arguments :
+ fh : file handle
+ buf : place to return line
+ buflen : place length
+
+Return Value :
+ return TRUE if successful
+ FALSE otherwise
+
+--*/
+{
+ DWORD nread;
+ LPBYTE eol;
+ DWORD seekback;
+ OVERLAPPED Overlapped;
+ DWORD AnsiBufLen;
+ LPBYTE AnsiBuffer;
+ LPWSTR UnicodeBuffer;
+ CHAR Dummy;
+
+ AnsiBufLen = buflen / sizeof(WCHAR);
+ AnsiBuffer = NetpMemoryAllocate(AnsiBufLen);
+
+ if(AnsiBuffer == NULL)
+ return FALSE;
+
+ if (!ReadFile((HANDLE)fh, AnsiBuffer, AnsiBufLen - 1, &nread, NULL))
+ return FALSE;
+
+ IF_DEBUG(MASTER) {
+ NetpKdPrint(("[REPL] get_conf_line ... read 0x%lx bytes\n", nread));
+ }
+
+ if (nread == 0) {
+ NetpMemoryFree(AnsiBuffer);
+ return FALSE;
+ }
+
+ IF_DEBUG(MASTER) {
+ NetpKdPrint(("[REPL] get_conf_line ... buf %s, len 0x%lx\n",
+ AnsiBuffer,
+ AnsiBufLen));
+ }
+
+ //
+ // adjust file pointer.
+ //
+
+ GlobalFileOffset += nread;
+
+ AnsiBuffer[nread] = 0;
+ eol = strchr(AnsiBuffer, '\r');
+ if (eol == NULL) {
+ if (nread == (AnsiBufLen - 1)) {
+
+ //
+ // Above takes into account the fact that the
+ // last line may not end in a carriage return
+ //
+
+ IF_DEBUG(MASTER) {
+ NetpKdPrint(("[REPL] get_conf_line ... line to long\n"));
+ }
+ NetpMemoryFree(AnsiBuffer);
+ return FALSE;
+ }
+ }
+ else {
+
+ //
+ // Below calculation takes into account the CR/LF
+ // (the "-2" does it). Wonders of wonders, it even
+ // works if the CR is in the buffer but the LF isn't
+ //
+
+ *eol = 0;
+ seekback = nread - (eol - (LPSTR)AnsiBuffer) - 2;
+
+ IF_DEBUG(MASTER) {
+ NetpKdPrint(("[REPL] get_conf_line ... line is <%s>\n",
+ (LPSTR)AnsiBuffer));
+ NetpKdPrint(("[REPL] get_conf_line ... seeking back 0x%lx\n",
+ seekback));
+ }
+
+ //
+ // reset file pointer to read the next parm
+ //
+
+ RtlZeroMemory( &Overlapped, sizeof(Overlapped) );
+ GlobalFileOffset -= seekback;
+ Overlapped.Offset = GlobalFileOffset;
+
+ //
+ // make a dummy read
+ //
+
+ if(!ReadFile((HANDLE)fh, &Dummy, 0, &nread, &Overlapped)) {
+ NetpMemoryFree(AnsiBuffer);
+ return FALSE;
+ }
+
+ IF_DEBUG(MASTER) {
+ NetpKdPrint(("[REPL] get_conf_line ... new pos is 0x%lx\n",
+ GlobalFileOffset));
+ }
+ }
+
+ UnicodeBuffer = NetpAllocWStrFromStr((LPSTR)AnsiBuffer);
+
+ if(UnicodeBuffer == NULL) {
+
+ NetpKdPrint(("[REPL] get_conf_line out of memory \n"));
+
+ NetpMemoryFree(AnsiBuffer);
+
+ return FALSE;
+ }
+
+ wcscpy((LPWSTR)buf, UnicodeBuffer);
+
+ NetpMemoryFree(UnicodeBuffer);
+ NetpMemoryFree(AnsiBuffer);
+
+ return TRUE;
+}
+
+BOOL
+pwcscmp(
+ IN LPWSTR parameter,
+ IN LPWSTR template
+ )
+/*++
+
+Routine Description :
+ compare two strings , parameter string is delimited with '='.
+
+Arguments :
+ parameter : string of the format LPART=RPART
+ template : string that should be comapared with LPART.
+
+Return Value :
+ return TRUE if the comparition is successful.
+ FALSE otherwise.
+
+--*/
+{
+ LPWSTR end;
+ DWORD limit;
+
+ IF_DEBUG(MASTER) {
+ NetpKdPrint(("[REPL] pstrcmp ... comparing %ws and %ws\n",
+ parameter, template));
+ }
+
+ end = wcschr(parameter, L'=');
+ if (end == NULL) {
+ if(_wcsicmp(parameter, template) == 0) {
+ return TRUE;
+ }
+ }
+ else {
+ limit = end - parameter;
+ if ((DWORD)wcslen(template) == limit) {
+ if(wcsncmpi(parameter, template, limit) == 0) {
+ return TRUE;
+ }
+ }
+ }
+ return FALSE;
+}
+
+
+BOOL
+iswspace(
+ IN WCHAR c
+ )
+/*++
+
+Routine Description :
+ is the given char is a white space.
+
+Arguments :
+ c : chat to be tested.
+
+Return Value :
+ return TRUE if it a white space
+ FALSE otherwise.
+
+--*/
+{
+ return (c == L' ' || c == L'\t');
+}
+
+
+#endif // 0 // entire file is obsolete --JR, 30-Jan-1992
diff --git a/private/net/svcdlls/repl/server/impadd.c b/private/net/svcdlls/repl/server/impadd.c
new file mode 100644
index 000000000..ca902410b
--- /dev/null
+++ b/private/net/svcdlls/repl/server/impadd.c
@@ -0,0 +1,245 @@
+/*++
+
+Copyright (c) 1992-1993 Microsoft Corporation
+
+Module Name:
+
+ ImpAdd.c
+
+Abstract:
+
+ This file contains NetrReplImportDirAdd.
+
+Author:
+
+ John Rogers (JohnRo) 09-Jan-1992
+
+Environment:
+
+ Runs under Windows NT.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+ 09-Jan-1992 JohnRo
+ Created.
+ 20-Jan-1992 JohnRo
+ Netr prototypes are now generated by MIDL and put in repl.h.
+ 20-Jan-1992 JohnRo
+ Changed prototype to match MIDL requirements.
+ 24-Jan-1992 JohnRo
+ Changed to use LPTSTR etc.
+ 15-Mar-1992 JohnRo
+ Update registry with new values.
+ 27-Mar-1992 JohnRo
+ NetrImportDirAdd() sometimes forgot to release a lock.
+ 27-Mar-1992 JohnRo
+ Try fixing bug where bad name gets put in registry.
+ 08-May-1992 JohnRo
+ Fixed link problem in free (nondebug) build.
+ 22-Jul-1992 JohnRo
+ RAID 2274: repl svc should impersonate caller.
+ 09-Nov-1992 JohnRo
+ RAID 7962: Repl APIs in wrong role kill svc.
+ Fix remote repl admin.
+ 02-Apr-1993 JohnRo
+ Use NetpKdPrint() where possible.
+
+--*/
+
+
+// These must be included first:
+
+#include <windef.h> // IN, VOID, LPTSTR, etc.
+#include <lmcons.h> // NET_API_STATUS, PARM equates, etc.
+#include <repldefs.h> // IF_DEBUG(), needed by <client.h>.
+#include <rpc.h> // Needed by <repl.h>.
+
+// These can be in any order:
+
+#include <client.h> // LPCLIENT_LIST_REC, etc.
+#include <dirname.h> // ReplIsDirNameValid().
+#include <impdir.h> // ImportDirWriteConfigData(), etc.
+#include <lmrepl.h> // LPREPL_IDIR_INFO_1, REPL_EXTENT_ stuff, etc.
+#include <netdebug.h> // NetpAssert(), NetpKdPrint(), etc.
+#include <netlib.h> // NetpSetParmError().
+#include <repl.h> // My prototype (in MIDL-generated .h file).
+#include <replgbl.h> // ReplConfigLock, ReplConfigRole.
+#include <winerror.h> // ERROR_ equates, NO_ERROR.
+#include <rpcutil.h> // NetpImpersonateClient(), NetpRevertToSelf().
+
+
+NET_API_STATUS
+NetrReplImportDirAdd (
+ IN LPTSTR UncServerName OPTIONAL,
+ IN DWORD Level,
+ IN LPIMPORT_CONTAINER Buf, // RPC container (union)
+ OUT LPDWORD ParmError OPTIONAL // name used by NetpSetParmError() macro.
+ )
+
+/*++
+
+Routine Description:
+
+ Same as NetReplImportDirAdd.
+
+Arguments:
+
+ Same as NetReplImportDirAdd.
+
+Return Value:
+
+ Same as NetReplImportDirAdd.
+
+--*/
+
+{
+ LPREPL_IDIR_INFO_0 ApiRecord; // level 0 by definition
+ NET_API_STATUS ApiStatus;
+ BOOL Impersonated = FALSE;
+ LPCLIENT_LIST_REC InternalRecord;
+ BOOL LockedClientList = FALSE;
+ BOOL LockedConfigData = FALSE;
+
+ //
+ // Check for caller errors.
+ //
+ NetpSetParmError( PARM_ERROR_UNKNOWN ); // Assume error until proven...
+ if (Level != 0) {
+ return (ERROR_INVALID_LEVEL);
+ }
+ if (Buf == NULL) {
+ return (ERROR_INVALID_PARAMETER);
+ }
+ ApiRecord = Buf->Info0; // level 0 by definition
+ NetpAssert( ApiRecord != NULL );
+ if (ReplIsDirNameValid(ApiRecord->rpid0_dirname) == FALSE) {
+ NetpSetParmError( 1 ); // Error in first field in ApiRecord.
+ return (ERROR_INVALID_PARAMETER);
+ }
+
+ //
+ // Impersonate caller, so security check (write to registry) reflects
+ // the client's process, not the repl service process.
+ //
+ ApiStatus = NetpImpersonateClient();
+ if (ApiStatus != NO_ERROR) {
+ goto Cleanup;
+ }
+ Impersonated = TRUE;
+
+ //
+ // Lock config data so role doesn't change while we're executing.
+ //
+ ACQUIRE_LOCK_SHARED( ReplConfigLock );
+ LockedConfigData = TRUE;
+
+ if ( !ReplRoleIncludesImport( ReplConfigRole ) ) {
+
+ // Make sure there is no existing record for this directory.
+ if (ImportDirConfigDataExists(
+ UncServerName,
+ ApiRecord->rpid0_dirname )) {
+
+ ApiStatus = ERROR_ALREADY_EXISTS;
+ goto Cleanup;
+ }
+
+ // Write the new or revised data.
+ ApiStatus = ImportDirWriteConfigData (
+ UncServerName,
+ ApiRecord->rpid0_dirname,
+ REPL_STATE_NEVER_REPLICATED,
+ NULL, // no master.
+ (DWORD) 0, // last update time, secs since 1970.
+ (DWORD) 0, // lock count
+ (DWORD) 0 ); // time of first lock, secs since 1970.
+
+ goto Cleanup;
+ }
+
+ //
+ // Import-side of service is running...
+ // Get exclusive lock on client list, so we don't get confused by
+ // another API thread. This also locks the matching registry data.
+ //
+ ACQUIRE_LOCK( RCGlobalClientListLock );
+ LockedClientList = TRUE;
+
+ //
+ // Find record in client list.
+ //
+ InternalRecord = ReplGetClientRec(
+ ApiRecord->rpid0_dirname,
+ NULL); // don't care which master
+ if (InternalRecord != NULL) {
+ ApiStatus = ERROR_ALREADY_EXISTS; // BUGBUG: better error code?
+ // Don't forget to release lock...
+ } else {
+
+ IF_DEBUG(IMPAPI) {
+ NetpKdPrint(( "NetrReplImportDirAdd: adding API record...\n" ));
+ NetpDbgDisplayReplImportDir( Level, (LPVOID) ApiRecord );
+ }
+
+ //
+ // Call somebody to add to the registry.
+ // This has the side-effect of doing a security check.
+ //
+ ApiStatus = ImportDirWriteConfigData (
+ UncServerName,
+ ApiRecord->rpid0_dirname,
+ REPL_STATE_NEVER_REPLICATED,
+ NULL, // no master name
+ 0, // no last update time
+ 0, // no locks
+ 0 ); // no lock time
+
+ if (ApiStatus == NO_ERROR) {
+ //
+ // Create a new client record and add it to the appropriate list(s).
+ //
+ InternalRecord = ReplAddClientRec(
+ ApiRecord->rpid0_dirname,
+ NULL, // BUGBUG: need PSYNCMSG here
+ NULL); // BUGBUG: need PMSG_STATUS_REC here
+
+ IF_DEBUG(IMPAPI) {
+ NetpKdPrint(( "NetrReplImportDirAdd: done adding API record, "
+ "got client record at " FORMAT_LPVOID ".\n",
+ (LPVOID) InternalRecord ));
+ }
+
+ //
+ // Assumption: ReplAddClientRec can only fail if it can't allocate
+ // memory.
+ //
+ if (InternalRecord == NULL) {
+ ApiStatus = ERROR_NOT_ENOUGH_MEMORY;
+ // Don't forget to release lock...
+ } else {
+ ApiStatus = NO_ERROR;
+ }
+ }
+ }
+
+Cleanup:
+
+ if (Impersonated) {
+ (VOID) NetpRevertToSelf();
+ }
+
+ if (LockedClientList) {
+ RELEASE_LOCK( RCGlobalClientListLock );
+ }
+
+ if (LockedConfigData) {
+ RELEASE_LOCK( ReplConfigLock );
+ }
+
+ if (ApiStatus == NO_ERROR) {
+ NetpSetParmError( PARM_ERROR_NONE );
+ }
+ return (ApiStatus);
+
+}
diff --git a/private/net/svcdlls/repl/server/impdel.c b/private/net/svcdlls/repl/server/impdel.c
new file mode 100644
index 000000000..736e95944
--- /dev/null
+++ b/private/net/svcdlls/repl/server/impdel.c
@@ -0,0 +1,160 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ ImpDel.c
+
+Abstract:
+
+ NetrReplImportDirDel is the local worker for the NetReplImportDirDel API.
+
+Author:
+
+ John Rogers (JohnRo) 09-Jan-1992
+
+Environment:
+
+ Runs under Windows NT.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+ 09-Jan-1992 JohnRo
+ Created.
+ 20-Jan-1992 JohnRo
+ Netr prototypes are now generated by MIDL and put in repl.h.
+ 27-Jan-1992 JohnRo
+ Changed to use LPTSTR etc.
+ 15-Mar-1992 JohnRo
+ Update registry with new values.
+ 22-Jul-1992 JohnRo
+ RAID 2274: repl svc should impersonate caller.
+ 12-Nov-1992 JohnRo
+ RAID 1537: repl APIs in wrong role kill service.
+ Also fix remote repl admin.
+
+--*/
+
+
+// These must be included first:
+
+#include <windef.h> // IN, VOID, LPTSTR, etc.
+#include <lmcons.h> // NET_API_STATUS, PARM equates, etc.
+#include <repldefs.h> // (Needed by client.h)
+#include <rpc.h> // Needed by <repl.h>.
+
+// These can be in any order:
+
+#include <client.h> // ReplRemoveClientRecForDirName().
+#include <dirname.h> // ReplIsDirNameValid().
+#include <impdir.h> // ImportDirDeleteConfigData().
+#include <lmerr.h> // NERR_, ERROR_ equates, NO_ERROR.
+#include <netlock.h> // ACQUIRE_LOCK(), etc.
+#include <repl.h> // My prototype (in MIDL-generated .h file).
+#include <replgbl.h> // ReplConfigLock, ReplConfigRole.
+#include <rpcutil.h> // NetpImpersonateClient(), NetpRevertToSelf().
+
+
+NET_API_STATUS
+NetrReplImportDirDel (
+ IN LPTSTR UncServerName OPTIONAL,
+ IN LPTSTR DirName
+ )
+
+/*++
+
+Routine Description:
+
+ Same as NetReplImportDirDel.
+
+Arguments:
+
+ Same as NetReplImportDirDel.
+
+Return Value:
+
+ Same as NetReplImportDirDel.
+
+--*/
+
+{
+ NET_API_STATUS ApiStatus;
+ BOOL Impersonated = FALSE;
+ BOOL LockedClientList = FALSE;
+ BOOL LockedConfigData = FALSE;
+
+ if (ReplIsDirNameValid( DirName ) == FALSE) {
+ return (ERROR_INVALID_PARAMETER);
+ }
+
+ //
+ // Impersonate caller, so security check (write to registry) reflects
+ // the client's process, not the repl service process.
+ //
+ ApiStatus = NetpImpersonateClient();
+ if (ApiStatus != NO_ERROR) {
+ goto Cleanup;
+ }
+ Impersonated = TRUE;
+
+ //
+ // Check role and handle call if import half of service is not running.
+ //
+ ACQUIRE_LOCK_SHARED( ReplConfigLock );
+ LockedConfigData = TRUE;
+
+ if ( !ReplRoleIncludesImport( ReplConfigRole ) ) {
+
+ if (! ImportDirConfigDataExists( UncServerName, DirName )) {
+ ApiStatus = NERR_UnknownDevDir;
+ goto Cleanup;
+ }
+
+ ApiStatus = ImportDirDeleteConfigData( UncServerName, DirName );
+ goto Cleanup;
+ }
+
+ //
+ // Import side of service is running...
+ // Get exclusive lock on client list, so we don't get confused by
+ // another API thread. This also locks the matching registry data.
+ //
+ ACQUIRE_LOCK( RCGlobalClientListLock );
+ LockedClientList = TRUE;
+
+ //
+ // Call somebody to delete from registry.
+ // This has the side-effect of doing a security check.
+ //
+ ApiStatus = ImportDirDeleteConfigData( UncServerName, DirName );
+ if (ApiStatus != NO_ERROR) {
+ goto Cleanup; // Don't forget to release lock...
+ }
+
+ // BUGBUG: Is just removing the record from list enough or do we need
+ // to notify anybody? What if there's something for this one in the
+ // delay list or work queue?
+
+ ApiStatus = ReplRemoveClientRecForDirName( DirName );
+
+ // BUGBUG: Are registry and service out-of-sync if ApiStatus != NO_ERROR?
+
+Cleanup:
+
+ if (Impersonated) {
+ (VOID) NetpRevertToSelf();
+ }
+
+ if (LockedClientList) {
+ RELEASE_LOCK( RCGlobalClientListLock );
+ }
+
+ if (LockedConfigData) {
+ RELEASE_LOCK( ReplConfigLock );
+ }
+
+ return (ApiStatus);
+
+}
diff --git a/private/net/svcdlls/repl/server/impenum.c b/private/net/svcdlls/repl/server/impenum.c
new file mode 100644
index 000000000..9e3d2378c
--- /dev/null
+++ b/private/net/svcdlls/repl/server/impenum.c
@@ -0,0 +1,305 @@
+/*++
+
+Copyright (c) 1992-1993 Microsoft Corporation
+
+Module Name:
+
+ ImpEnum.c
+
+Abstract:
+
+ This file contains NetrReplImportDirEnum.
+
+Author:
+
+ John Rogers (JohnRo) 14-Jan-1992
+
+Environment:
+
+ Runs under Windows NT.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+ 14-Jan-1992 JohnRo
+ Created.
+ 20-Jan-1992 JohnRo
+ Netr prototypes are now generated by MIDL and put in repl.h.
+ 20-Jan-1992 JohnRo
+ Changed prototype to match MIDL requirements.
+ 27-Jan-1992 JohnRo
+ Changed to use LPTSTR etc.
+ Changed interface of ImportDirBuildApiRecord to work w/o service.
+ 23-Mar-1992 JohnRo
+ Fixed enum when service is running.
+ 27-Mar-1992 JohnRo
+ More debug output.
+ 17-Nov-1992 JohnRo
+ RAID 1537: repl APIs in wrong role kill service.
+ 10-Mar-1993 JohnRo
+ Fixed various lock/unlock mistakes.
+ PC-LINT 5.0 noticed that EntriesRead may not be set.
+ Use NetpKdPrint() where possible.
+
+--*/
+
+
+// These must be included first:
+
+#include <windef.h> // IN, VOID, LPTSTR, etc.
+#include <lmcons.h> // NET_API_STATUS, PARM equates, etc.
+#include <rap.h> // Needed by <strucinf.h>.
+#include <repldefs.h> // ReplIsIntegrityValid(), etc.
+#include <rpc.h> // Needed by <repl.h>.
+
+// These can be in any order:
+
+#include <client.h> // RC globals, etc.
+#include <impdir.h> // ImportDirBuildApiRecord().
+#include <lmapibuf.h> // NetApiBufferAllocate().
+#include <netdebug.h> // NetpAssert(), NetpKdPrint(), etc.
+#include <netlib.h> // NetpPointerPlusSomeBytes().
+#include <netlock.h> // ACQUIRE_LOCK_SHARED(), RELEASE_LOCK().
+#include <repl.h> // My prototype (in MIDL-generated .h file).
+#include <replgbl.h> // ReplConfigLock, ReplConfigRole.
+#include <strucinf.h> // Netp{various}StructureInfo().
+#include <winerror.h> // ERROR_ equates, NO_ERROR.
+
+
+NET_API_STATUS
+NetrReplImportDirEnum (
+ IN LPTSTR UncServerName OPTIONAL,
+ // IN DWORD Level,
+ IN OUT LPIMPORT_ENUM_STRUCT EnumContainer,
+ // OUT LPIMPORT_CONTAINER BufPtr, // RPC container (union)
+ IN DWORD PrefMaxSize,
+ // OUT LPDWORD EntriesRead,
+ OUT LPDWORD TotalEntries,
+ IN OUT LPDWORD ResumeHandle OPTIONAL
+ )
+
+/*++
+
+Routine Description:
+
+ Same as NetReplImportDirEnum.
+
+Arguments:
+
+ Same as NetReplImportDirEnum.
+
+Return Value:
+
+ Same as NetReplImportDirEnum.
+
+--*/
+
+{
+ LPVOID ApiArray = NULL;
+ LPVOID ApiEntry;
+ NET_API_STATUS ApiStatus;
+ LPCLIENT_LIST_REC ClientRecord;
+ DWORD EntryCount = 0;
+ DWORD FixedSize;
+ DWORD Level;
+ BOOL LockedClientList = FALSE;
+ BOOL LockedConfigData = FALSE;
+ DWORD OutputSize;
+ LPVOID StringLocation;
+
+ UNREFERENCED_PARAMETER(PrefMaxSize);
+ UNREFERENCED_PARAMETER(UncServerName);
+
+#define SET_ENTRIES_READ( value ) \
+ { \
+ /* Pretend level 0, to make life easy. */ \
+ EnumContainer->ImportInfo.Level0->EntriesRead = (value); \
+ }
+
+#define SET_BUFFER_POINTER( value ) \
+ { \
+ /* Pretend level 0, to make life easy. */ \
+ EnumContainer->ImportInfo.Level0->Buffer = (value); \
+ }
+
+ //
+ // Check for caller errors.
+ //
+ NetpAssert( EnumContainer != NULL );
+ NetpAssert( EnumContainer->ImportInfo.Level0 != NULL );
+ Level = EnumContainer->Level;
+
+ SET_BUFFER_POINTER( NULL ); // Don't confuse caller about possible alloc.
+
+ if (TotalEntries == NULL) {
+ return (ERROR_INVALID_PARAMETER);
+ }
+
+ // Test for memory faults before we lock anything.
+ SET_ENTRIES_READ( 0 );
+ * TotalEntries = 0;
+
+ // This version only supports 1 call to enumerate, so resume handle
+ // should never be set to nonzero. But let's check, so caller finds out
+ // they have a buggy program.
+ if (ResumeHandle != NULL) {
+ if (*ResumeHandle != 0) {
+ return (ERROR_INVALID_PARAMETER);
+ }
+ }
+
+ //
+ // Check role and handle call if import half of service is not running.
+ //
+ ACQUIRE_LOCK_SHARED( ReplConfigLock );
+ LockedConfigData = TRUE;
+
+ if ( !ReplRoleIncludesImport( ReplConfigRole ) ) {
+
+ ApiStatus = ImportDirEnumApiRecords(
+ UncServerName,
+ Level,
+ (LPBYTE *) (LPVOID) &ApiArray,
+ PrefMaxSize,
+ & EntryCount,
+ TotalEntries );
+ goto Cleanup;
+ }
+
+ //
+ // Import side of service is running...
+ // Compute size of output area (and check caller's Level too).
+ //
+ ApiStatus = NetpReplImportDirStructureInfo (
+ Level,
+ PARMNUM_ALL,
+ TRUE, // want native sizes
+ NULL, // don't need DataDesc16
+ NULL, // don't need DataDesc32
+ NULL, // don't need DataDescSmb
+ & OutputSize, // need max size of structure
+ & FixedSize,
+ NULL); // don't need StringSize
+ if (ApiStatus != NO_ERROR) {
+ goto Cleanup; // don't forget to unlock...
+ }
+
+ //
+ // Get read-only lock on client list.
+ //
+ ACQUIRE_LOCK_SHARED( RCGlobalClientListLock );
+ LockedClientList = TRUE;
+
+ //
+ // Find out how many entries there are.
+ //
+ EntryCount = RCGlobalClientListCount;
+
+ IF_DEBUG( IMPAPI ) {
+ NetpKdPrint(( "NetrReplImportDirEnum: there are " FORMAT_DWORD
+ " client records.\n", EntryCount ));
+ }
+
+ if (EntryCount > 0) {
+
+ //
+ // Allocate the output area.
+ //
+ ApiStatus = NetApiBufferAllocate(
+ OutputSize * EntryCount,
+ (LPVOID *) & ApiArray);
+ if (ApiStatus != NO_ERROR) {
+
+ // ApiStatus is already set to return error to caller.
+ // Don't forget to release lock...
+
+ } else {
+
+ NetpAssert( ApiArray != NULL );
+
+ //
+ // Loop for each entry in client list.
+ //
+ ApiEntry = ApiArray;
+ ClientRecord = RCGlobalClientListHeader;
+ StringLocation = NetpPointerPlusSomeBytes(
+ ApiArray,
+ OutputSize * EntryCount );
+
+ while (ClientRecord != NULL) {
+
+ //
+ // Build an array entry for this client record.
+ //
+ ApiStatus = ImportDirBuildApiRecord (
+ Level,
+ ClientRecord->dir_name,
+ ClientRecord->state,
+ ClientRecord->master,
+ ClientRecord->timestamp, // Last update, secs since '70.
+ ClientRecord->lockcount,
+ ClientRecord->time_of_first_lock, // Seconds since 1970.
+ ApiEntry,
+ (LPBYTE *) (LPVOID) & StringLocation);
+ IF_DEBUG( IMPAPI ) {
+ NetpKdPrint(( "NetrReplImportDirEnum: build status is "
+ FORMAT_API_STATUS ".\n", ApiStatus ));
+ }
+ NetpAssert( ApiStatus == NO_ERROR ); // We checked all parms.
+
+ ClientRecord = ClientRecord->next_p;
+ ApiEntry = NetpPointerPlusSomeBytes( ApiEntry, FixedSize );
+
+ }
+
+ }
+
+ // Don't forget to release lock...
+
+ } else {
+
+ // No entries...
+ ApiArray = NULL;
+ ApiStatus = NO_ERROR;
+
+ // Don't forget to release lock...
+ }
+
+ //
+ // Release lock and tell caller how everything went.
+ //
+Cleanup:
+
+ if (LockedClientList) {
+ RELEASE_LOCK( RCGlobalClientListLock );
+ }
+
+ if (LockedConfigData) {
+ RELEASE_LOCK( ReplConfigLock );
+ }
+
+
+ SET_BUFFER_POINTER( (LPVOID) ApiArray );
+ SET_ENTRIES_READ( EntryCount );
+ * TotalEntries = EntryCount;
+
+
+ if (ApiStatus==NO_ERROR) {
+ IF_DEBUG( IMPAPI ) {
+ NetpKdPrint(( "NetrReplImportDirEnum: at end, ApiArray at "
+ FORMAT_LPVOID " and EntryCount=" FORMAT_DWORD ".\n",
+ (LPVOID) ApiArray, EntryCount ));
+ }
+ if (ApiArray != NULL) {
+ NetpAssert( EntryCount > 0 );
+
+ IF_DEBUG( IMPAPI ) {
+ NetpDbgDisplayReplImportDirArray( Level, ApiArray, EntryCount );
+ }
+ } else {
+ NetpAssert( EntryCount == 0 );
+ }
+ }
+ return (ApiStatus);
+
+}
diff --git a/private/net/svcdlls/repl/server/impget.c b/private/net/svcdlls/repl/server/impget.c
new file mode 100644
index 000000000..3176a9bf0
--- /dev/null
+++ b/private/net/svcdlls/repl/server/impget.c
@@ -0,0 +1,235 @@
+/*++
+
+Copyright (c) 1992-1993 Microsoft Corporation
+
+Module Name:
+
+ ImpGet.c
+
+Abstract:
+
+ This file contains NetrReplImportDirGetInfo.
+
+Author:
+
+ John Rogers (JohnRo) 12-Jan-1992
+
+Environment:
+
+ Runs under Windows NT.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+ 12-Jan-1992 JohnRo
+ Created.
+ 20-Jan-1992 JohnRo
+ Netr prototypes are now generated by MIDL and put in repl.h.
+ 20-Jan-1992 JohnRo
+ Corrected name of this routine.
+ Changed prototype to match MIDL requirements.
+ 27-Jan-1992 JohnRo
+ Changed to use LPTSTR etc.
+ 28-Jul-1992 JohnRo
+ Fixed an error path where bad record pointer was being used.
+ Corrected type of ApiRecord.
+ Added error checking.
+ 12-Nov-1992 JohnRo
+ RAID 1537: repl APIs in wrong role kill service.
+ 02-Apr-1993 JohnRo
+ Use NetpKdPrint() where possible.
+
+--*/
+
+
+// These must be included first:
+
+#include <windef.h> // IN, VOID, LPTSTR, etc.
+#include <lmcons.h> // NET_API_STATUS, PARM equates, etc.
+#include <rap.h> // Needed by <strucinf.h>.
+#include <repldefs.h> // ReplIsIntegrityValid(), etc.
+#include <rpc.h> // Needed by <repl.h>.
+
+// These can be in any order:
+
+#include <client.h> // RC globals, ReplGetClientRecord(), etc.
+#include <dirname.h> // ReplIsDirNameValid().
+#include <impdir.h> // ImportDirBuildApiRecord(), etc.
+#include <lmapibuf.h> // NetApiBufferAllocate().
+#include <lmerr.h> // ERROR_ and NERR_ equates; NO_ERROR.
+#include <lmrepl.h> // LPREPL_IDIR_INFO_1, REPL_EXTENT_ stuff, etc.
+#include <netdebug.h> // NetpAssert(), NetpKdPrint(), etc.
+#include <netlib.h> // NetpPointerPlusSomeBytes().
+#include <netlock.h> // ACQUIRE_LOCK_SHARED(), RELEASE_LOCK().
+#include <prefix.h> // PREFIX_ equates.
+#include <repl.h> // My prototype (in MIDL-generated .h file).
+#include <replgbl.h> // ReplConfigLock, ReplConfigRole.
+#include <strucinf.h> // Netp{various}StructureInfo().
+
+
+NET_API_STATUS
+NetrReplImportDirGetInfo (
+ IN LPTSTR UncServerName OPTIONAL,
+ IN LPTSTR DirName,
+ IN DWORD Level,
+ OUT LPIMPORT_CONTAINER BufPtr // RPC container (union)
+ )
+
+/*++
+
+Routine Description:
+
+ Same as NetReplImportDirGetInfo.
+
+Arguments:
+
+ Same as NetReplImportDirGetInfo.
+
+Return Value:
+
+ Same as NetReplImportDirGetInfo.
+
+--*/
+
+{
+ LPREPL_IDIR_INFO_1 ApiRecord = NULL;
+ NET_API_STATUS ApiStatus;
+ LPCLIENT_LIST_REC ClientRecord;
+ BOOL LockedClientList = FALSE;
+ BOOL LockedConfigData = FALSE;
+ DWORD OutputSize;
+ LPVOID StringLocation;
+
+ UNREFERENCED_PARAMETER(UncServerName);
+
+ IF_DEBUG( IMPAPI ) {
+ NetpKdPrint(( PREFIX_REPL
+ "NetrReplImportDirGetInfo: union at " FORMAT_LPVOID ".\n",
+ (LPVOID) BufPtr ));
+ }
+
+ //
+ // Check for caller errors.
+ //
+ if (BufPtr == NULL) {
+ return (ERROR_INVALID_PARAMETER);
+ }
+
+ BufPtr->Info0 = NULL; // Don't confuse caller about possible alloc'ed data.
+
+ if (ReplIsDirNameValid( DirName ) == FALSE) {
+ return (ERROR_INVALID_PARAMETER);
+ }
+
+ //
+ // Check role and handle call if import half of service is not running.
+ //
+ ACQUIRE_LOCK_SHARED( ReplConfigLock );
+ LockedConfigData = TRUE;
+
+ if ( !ReplRoleIncludesImport( ReplConfigRole ) ) {
+
+ ApiStatus = ImportDirGetApiRecord(
+ UncServerName,
+ DirName,
+ Level,
+ (LPBYTE *) (LPVOID) &ApiRecord );
+ goto Cleanup;
+ }
+
+ //
+ //
+ // Compute size of output area (and check caller's Level too).
+ //
+ ApiStatus = NetpReplImportDirStructureInfo (
+ Level,
+ PARMNUM_ALL,
+ TRUE, // want native sizes
+ NULL, // don't need DataDesc16
+ NULL, // don't need DataDesc32
+ NULL, // don't need DataDescSmb
+ & OutputSize, // need max size of structure
+ NULL, // don't need FixedSize
+ NULL); // don't need StringSize
+ if (ApiStatus != NO_ERROR) {
+ // Don't forget to release lock...
+ goto Cleanup;
+ }
+ NetpAssert( OutputSize > 0 );
+
+ //
+ // Get read-only lock on client list.
+ //
+ ACQUIRE_LOCK_SHARED( RCGlobalClientListLock );
+ LockedClientList = TRUE;
+
+ //
+ // Find the client record for this dir name.
+ //
+ ClientRecord = (LPCLIENT_LIST_REC) ReplGetClientRec(
+ DirName,
+ NULL); // BUGBUG: no master name?
+
+ if (ClientRecord == NULL) { // Not found.
+
+ ApiStatus = NERR_UnknownDevDir;
+ // Don't forget to release lock...
+
+ } else { // Found.
+
+ //
+ // Allocate the output area.
+ //
+ ApiStatus = NetApiBufferAllocate(
+ OutputSize,
+ (LPVOID *) & ApiRecord);
+ if (ApiStatus != NO_ERROR) {
+
+ // ApiStatus is already set to return error to caller.
+ // Don't forget to release lock...
+ NetpAssert( ApiRecord == NULL );
+
+ } else {
+ NetpAssert( ApiRecord != NULL );
+
+ //
+ // Build ApiRecord!
+ //
+ StringLocation = NetpPointerPlusSomeBytes( ApiRecord, OutputSize );
+ ApiStatus = ImportDirBuildApiRecord (
+ Level,
+ ClientRecord->dir_name,
+ ClientRecord->state,
+ ClientRecord->master,
+ ClientRecord->timestamp, // Last update, secs since '70.
+ ClientRecord->lockcount,
+ ClientRecord->time_of_first_lock, // Seconds since 1970.
+ ApiRecord,
+ (LPBYTE *) (LPVOID) & StringLocation);
+ NetpAssert( ApiStatus == NO_ERROR ); // We checked all parms.
+ NetpAssert( ImportDirIsApiRecordValid (Level, ApiRecord, NULL ) );
+
+ // Don't forget to release lock...
+
+ }
+
+ }
+
+ //
+ // Release lock and tell caller how everything went.
+ //
+
+Cleanup:
+
+ if (LockedClientList) {
+ RELEASE_LOCK( RCGlobalClientListLock );
+ }
+
+ if (LockedConfigData) {
+ RELEASE_LOCK( ReplConfigLock );
+ }
+
+ BufPtr->Info0 = (LPVOID) ApiRecord; // Pretend info level 0.
+ return (ApiStatus);
+
+}
diff --git a/private/net/svcdlls/repl/server/implock.c b/private/net/svcdlls/repl/server/implock.c
new file mode 100644
index 000000000..0aa344067
--- /dev/null
+++ b/private/net/svcdlls/repl/server/implock.c
@@ -0,0 +1,303 @@
+/*++
+
+Copyright (c) 1992-1993 Microsoft Corporation
+
+Module Name:
+
+ ImpLock.c
+
+Abstract:
+
+ This file contains:
+
+ NetrReplImportDirLock
+ NetrReplImportDirUnlock
+
+Author:
+
+ John Rogers (JohnRo) 08-Jan-1992
+
+Environment:
+
+ Runs under Windows NT.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Notes:
+
+ This file is extremely similar to ExpLock.c. If you fix any bugs here,
+ make sure they're reflected there, and vice versa.
+
+Revision History:
+
+ 08-Jan-1992 JohnRo
+ Created.
+ 27-Jan-1992 JohnRo
+ Netr prototypes are now generated by MIDL and put in repl.h.
+ Changed to use LPTSTR etc.
+ 18-Feb-1992 JohnRo
+ Use Repl{Incr,Decr}LockFields().
+ 15-Mar-1992 JohnRo
+ Update registry with new values.
+ 22-Jul-1992 JohnRo
+ RAID 2274: repl svc should impersonate caller.
+ 17-Nov-1992 JohnRo
+ RAID 1537: repl APIs in wrong role kill service.
+ 13-Apr-1993 JohnRo
+ RAID 3107: locking directory over the net gives network path not found.
+ Made some changes suggested by PC-LINT 5.0
+
+--*/
+
+
+// These must be included first:
+
+#include <windef.h> // IN, VOID, LPTSTR, etc.
+#include <lmcons.h> // NET_API_STATUS, PARM equates, etc.
+#include <repldefs.h> // (Required by client.h)
+#include <rpc.h> // Needed by <repl.h>.
+
+// These can be in any order:
+
+#include <client.h> // LPCLIENT_LIST_REC, etc.
+#include <dirname.h> // ReplIsDirNameValid().
+#include <lmerr.h> // NERR_ equates, NO_ERROR.
+#include <impdir.h> // ImportDirLockInRegistry(), etc.
+#include <lmrepl.h> // REPL_UNLOCK_ equates.
+#include <netlock.h> // ACQUIRE_LOCK(), etc.
+#include <repl.h> // My prototypes (in MIDL-generated .h file).
+#include <replgbl.h> // ReplConfigLock, ReplConfigRole.
+#include <rpcutil.h> // NetpImpersonateClient(), NetpRevertToSelf().
+
+
+NET_API_STATUS
+NetrReplImportDirLock (
+ IN LPTSTR UncServerName OPTIONAL,
+ IN LPTSTR DirName
+ )
+
+/*++
+
+Routine Description:
+
+ Same as NetReplImportDirLock.
+
+Arguments:
+
+ Same as NetReplImportDirLock.
+
+Return Value:
+
+ Same as NetReplImportDirLock.
+
+--*/
+
+{
+ NET_API_STATUS ApiStatus;
+ LPCLIENT_LIST_REC ClientRecord;
+ BOOL Impersonated = FALSE;
+ BOOL LockedClientList = FALSE;
+ BOOL LockedConfigData = FALSE;
+
+ if (ReplIsDirNameValid( DirName ) == FALSE) {
+ return (ERROR_INVALID_PARAMETER);
+ }
+
+ //
+ // Get lock on role; we'll need that below.
+ //
+ ACQUIRE_LOCK_SHARED( ReplConfigLock );
+ LockedConfigData = TRUE;
+
+ //
+ //
+ // Impersonate caller, so security check (write to registry) reflects
+ // the client's process, not the repl service process.
+ //
+ ApiStatus = NetpImpersonateClient();
+ if (ApiStatus != NO_ERROR) {
+ goto Cleanup;
+ }
+ Impersonated = TRUE;
+
+ //
+ // Get a lock on client list, so we don't get confused by
+ // another API thread. This also locks the matching registry data.
+ // We need this lock even if this side of service is not running,
+ // because the XxxDirLockInRegistry routines are not atomic.
+ //
+ ACQUIRE_LOCK_SHARED( RCGlobalClientListLock );
+ LockedClientList = TRUE;
+
+ //
+ // Update lock values in registry first.
+ // This has the side-effect of doing a security check.
+ //
+ ApiStatus = ImportDirLockInRegistry(
+ UncServerName,
+ DirName );
+ if (ApiStatus != NO_ERROR) {
+ goto Cleanup;
+ }
+
+ //
+ // Check role and update globals if import half of service is running.
+ //
+ if ( ReplRoleIncludesImport( ReplConfigRole ) ) {
+ ClientRecord = ReplGetClientRec(
+ DirName,
+ NULL ); // No particular master name
+
+ if (ClientRecord == NULL) {
+
+ ApiStatus = NERR_UnknownDevDir;
+ // Don't forget to release lock...
+
+ } else {
+
+ //
+ // Update lock values in service.
+ //
+ ApiStatus = ReplIncrLockFields(
+ & (ClientRecord->lockcount),
+ & (ClientRecord->time_of_first_lock) );
+ // Don't forget to release lock...
+ }
+ }
+
+Cleanup:
+
+ if (Impersonated) {
+ (VOID) NetpRevertToSelf();
+ }
+
+ if (LockedClientList) {
+ RELEASE_LOCK( RCGlobalClientListLock );
+ }
+
+ if (LockedConfigData) {
+ RELEASE_LOCK( ReplConfigLock );
+ }
+
+ return (ApiStatus);
+
+} // NetrReplImportDirLock
+
+
+NET_API_STATUS
+NetrReplImportDirUnlock (
+ IN LPTSTR UncServerName OPTIONAL,
+ IN LPTSTR DirName,
+ IN DWORD UnlockForce
+ )
+
+/*++
+
+Routine Description:
+
+ Same as NetReplImportDirUnlock.
+
+Arguments:
+
+ Same as NetReplImportDirUnlock.
+
+Return Value:
+
+ Same as NetReplImportDirUnlock.
+
+--*/
+
+{
+ NET_API_STATUS ApiStatus;
+ LPCLIENT_LIST_REC ClientRecord;
+ BOOL Impersonated = FALSE;
+ BOOL LockedClientList = FALSE;
+ BOOL LockedConfigData = FALSE;
+
+ if (ReplIsDirNameValid( DirName ) == FALSE) {
+ return (ERROR_INVALID_PARAMETER);
+ } else if ( ! ReplIsForceLevelValid( UnlockForce ) ) {
+ return (ERROR_INVALID_PARAMETER);
+ }
+
+ //
+ // Get lock on role; we'll need that below.
+ //
+ ACQUIRE_LOCK_SHARED( ReplConfigLock );
+ LockedConfigData = TRUE;
+
+ //
+ //
+ // Impersonate caller, so security check (write to registry) reflects
+ // the client's process, not the repl service process.
+ //
+ ApiStatus = NetpImpersonateClient();
+ if (ApiStatus != NO_ERROR) {
+ goto Cleanup;
+ }
+ Impersonated = TRUE;
+
+ //
+ // Get a lock on client list, so we don't get confused by
+ // another API thread. This also locks the matching registry data.
+ // We need this lock even if this side of service is not running,
+ // because the XxxDirLockInRegistry routines are not atomic.
+ //
+ ACQUIRE_LOCK_SHARED( RCGlobalClientListLock );
+ LockedClientList = TRUE;
+
+ //
+ // Update lock values in registry first.
+ // This has the side-effect of doing a security check.
+ //
+ ApiStatus = ImportDirUnlockInRegistry(
+ UncServerName,
+ DirName,
+ UnlockForce );
+ if (ApiStatus != NO_ERROR) {
+ goto Cleanup;
+ }
+
+ //
+ // Check role and update globals if import half of service is running.
+ //
+ if ( ReplRoleIncludesImport( ReplConfigRole ) ) {
+ ClientRecord = ReplGetClientRec(
+ DirName,
+ NULL ); // No particular master name
+
+ if (ClientRecord == NULL) {
+
+ ApiStatus = NERR_UnknownDevDir;
+ // Don't forget to release lock...
+
+ } else {
+
+ //
+ // Update lock values in service.
+ //
+ ApiStatus = ReplDecrLockFields(
+ & (ClientRecord->lockcount),
+ & (ClientRecord->time_of_first_lock),
+ UnlockForce );
+ // Don't forget to release lock...
+
+ }
+ }
+
+Cleanup:
+
+ if (Impersonated) {
+ (VOID) NetpRevertToSelf();
+ }
+
+ if (LockedClientList) {
+ RELEASE_LOCK( RCGlobalClientListLock );
+ }
+
+ if (LockedConfigData) {
+ RELEASE_LOCK( ReplConfigLock );
+ }
+
+ return (ApiStatus);
+
+} // NetrReplImportDirUnlock
diff --git a/private/net/svcdlls/repl/server/impread.c b/private/net/svcdlls/repl/server/impread.c
new file mode 100644
index 000000000..0ec8c3d8d
--- /dev/null
+++ b/private/net/svcdlls/repl/server/impread.c
@@ -0,0 +1,223 @@
+/*++
+
+Copyright (c) 1992-1993 Microsoft Corporation
+
+Module Name:
+
+ ImpRead.c
+
+Abstract:
+
+ ImportDirReadClientList().
+ Only callable as part of the service itself.
+
+Author:
+
+ John Rogers (JohnRo) 23-Sep-1992
+
+Environment:
+
+ User Mode - Win32
+
+Revision History:
+
+ 25-Sep-1992 JohnRo
+ Created as part of RAID 5494: repl svc does not maintain time stamp
+ on import startup.
+ 02-Dec-1992 JohnRo
+ RAID 3844: remote NetReplSetInfo uses local machine type.
+ 02-Apr-1993 JohnRo
+ Use NetpKdPrint() where possible.
+ Made changes suggested by PC-LINT 5.0
+ 15-Jun-1993 JohnRo
+ Multi-masters are ignored.
+
+--*/
+
+
+// These must be included first:
+
+#include <windef.h> // IN, DWORD, etc.
+#include <lmcons.h> // NET_API_STATUS, etc.
+
+// These may be included in any order:
+
+#include <config.h> // LPNET_CONFIG_HANDLE, Netp config routines.
+#include <confname.h> // SECT_NT_ equates.
+#include <impdir.h> // My prototype.
+#include <lmerr.h> // NERR_ and ERROR_ equates, NO_ERROR.
+#include <client.h> // RCGlobalClientListLock, etc.
+#include <netdebug.h> // NetpAssert(), etc.
+#include <netlock.h> // ACQUIRE_LOCK(), etc.
+#include <prefix.h> // PREFIX_ equates.
+#include <repldefs.h> // IF_DEBUG().
+#include <tstr.h> // STRCPY(), TCHAR_BACKSLASH.
+
+
+
+NET_API_STATUS
+ImportDirReadClientList(
+ VOID
+ )
+{
+ NET_API_STATUS ApiStatus;
+ LPTSTR DirName = NULL;
+ DWORD EntryCount = 0;
+ BOOL FirstTime;
+ LPNET_CONFIG_HANDLE Handle = NULL;
+ BOOL LockDone = FALSE;
+
+ IF_DEBUG(CLIENT) {
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ImportDirReadClientList: beginning...\n" ));
+ }
+
+ ACQUIRE_LOCK( RCGlobalClientListLock );
+ LockDone = TRUE;
+
+ //
+ // Open the right section of the config file/whatever.
+ //
+ ApiStatus = NetpOpenConfigDataEx(
+ & Handle,
+ NULL, // local (no server name)
+ (LPTSTR) SECT_NT_REPLICATOR, // service name
+ (LPTSTR) SECT_NT_REPLICATOR_IMPORTS, // area under service
+ TRUE); // read-only
+ if (ApiStatus != NO_ERROR) {
+ goto CleanUp;
+ }
+
+ //
+ // Count entries in config data.
+ //
+ ApiStatus = NetpNumberOfConfigKeywords (
+ Handle,
+ & EntryCount );
+
+ if (ApiStatus != NO_ERROR) {
+ NetpAssert( FALSE ); // BUGBUG handle unexpected better.
+ goto CleanUp;
+ } else if (EntryCount == 0) {
+ goto CleanUp;
+ }
+
+ //
+ // Loop for each keyword (relative directory path) in this section.
+ //
+
+ FirstTime = TRUE;
+
+ /*lint -save -e716 */ // disable warnings for while(TRUE)
+ while (TRUE) {
+ DWORD State, LockCount;
+ DWORD LastUpdateTime, LockTime; // Seconds since 1970 (GMT).
+ LPTSTR ValueString;
+ TCHAR UncMaster[UNCLEN+1];
+
+ ApiStatus = NetpEnumConfigSectionValues (
+ Handle,
+ & DirName, // Keyword - alloc and set ptr.
+ & ValueString, // Must be freed by NetApiBufferFree().
+ FirstTime );
+
+ FirstTime = FALSE;
+
+ if (ApiStatus != NO_ERROR) {
+ goto CleanUp; // Handle end of list or an actual error.
+ }
+
+ NetpAssert( DirName != NULL );
+ NetpAssert( ValueString != NULL );
+
+ //
+ // Parse the value string...
+ //
+ ApiStatus = ImportDirParseConfigData (
+ NULL, // no server name
+ ValueString,
+ & State,
+ UncMaster,
+ & LastUpdateTime,
+ & LockCount,
+ & LockTime); // Seconds since 1970.
+
+ NetpMemoryFree( ValueString );
+
+ if (ApiStatus == NO_ERROR) {
+ PCLIENT_LIST_REC rec;
+
+ rec = ReplAddClientRec(
+ DirName,
+ NULL, // no master info record
+ NULL ); // no dir info
+
+ NetpMemoryFree( DirName );
+ DirName = NULL;
+
+ if (rec == NULL) {
+ ApiStatus = ERROR_NOT_ENOUGH_MEMORY;
+ goto CleanUp;
+ }
+
+ // Override info set by ReplAddClientRecord().
+ rec->lockcount = LockCount;
+ rec->time_of_first_lock = LockTime;
+ rec->timestamp = LastUpdateTime;
+
+ rec->state = State;
+
+#if 0
+ // BUGBUG: This used to set the master field in the client
+ // record. However, this prevents other machines from becoming
+ // masters. So, ignore it...
+ if (UncMaster[0] != TCHAR_EOS) {
+ NetpAssert( UncMaster[0] == TCHAR_BACKSLASH );
+ NetpAssert( UncMaster[1] == TCHAR_BACKSLASH );
+
+ (VOID) STRCPY(
+ rec->master, // dest
+ UncMaster+2 ); // src (not UNC name)
+ }
+#endif
+
+ } else {
+ // BUGBUG: Log the error!
+ goto CleanUp;
+ }
+
+ } // while TRUE (exit on error or end of list)
+ /*lint -restore */ // re-enable warnings for while(TRUE)
+
+
+ //
+ // All done (error or otherwise).
+ //
+
+CleanUp:
+
+ if (ApiStatus == NERR_CfgParamNotFound) { // Normal end of list.
+ ApiStatus = NO_ERROR;
+ }
+
+ IF_DEBUG(CLIENT) {
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ImportDirReadClientList: done, status="
+ FORMAT_API_STATUS ".\n", ApiStatus ));
+ }
+
+ if (DirName != NULL) {
+ NetpMemoryFree( DirName );
+ }
+
+ if (Handle != NULL) {
+ (void) NetpCloseConfigData( Handle );
+ }
+
+ if (LockDone) {
+ RELEASE_LOCK( RCGlobalClientListLock );
+ }
+
+ return ApiStatus;
+
+} // ImportDirReadClientList
diff --git a/private/net/svcdlls/repl/server/impstart.c b/private/net/svcdlls/repl/server/impstart.c
new file mode 100644
index 000000000..f771fa200
--- /dev/null
+++ b/private/net/svcdlls/repl/server/impstart.c
@@ -0,0 +1,227 @@
+/*++
+
+Copyright (c) 1987-1993 Microsoft Corporation
+
+Module Name:
+
+ ImpStart.c
+
+Abstract:
+
+ Contains ImportDirStartRepl().
+
+Author:
+
+ 10/31/91 madana
+ initial coding
+
+Environment:
+
+ User mode only.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+ Tab size is set to 4.
+
+Revision History:
+
+ 09-Feb-1992 JohnRo
+ Created this routine from code written by MadanA.
+ 14-Feb-1992 JohnRo
+ ApiStatus is sometimes wrong.
+ 05-Mar-1992 JohnRo
+ Changed interface to match new service controller.
+ 06-Mar-1992 JohnRo
+ Avoid starting RPC server too soon.
+ 23-Mar-1992 JohnRo
+ Got rid of useless master and client termination codes.
+ 01-Apr-1992 JohnRo
+ Avoid assertion if import startup fails.
+ Up the stack size, just to be on the safe side.
+ 19-Aug-1992 JohnRo
+ RAID 2115: repl svc should wait while stopping or changing role.
+ 11-Mar-1993 JohnRo
+ RAID 12100: stopping repl sometimes goes into infinite loop.
+ Use PREFIX_ equates.
+ 02-Apr-1993 JohnRo
+ Use NetpKdPrint() where possible.
+ 24-May-1993 JohnRo
+ RAID 10587: repl could deadlock with changed NetpStopRpcServer(), so
+ just call ExitProcess() instead.
+
+--*/
+
+
+// These must be included first:
+
+#include <windows.h> // CreateThread(), DWORD, etc.
+#include <lmcons.h> // IN, NET_API_STATUS, etc.
+
+// These can be included in any order:
+
+#include <impdir.h> // ImportDir{Start,Stop}Repl routines.
+#include <lmrepl.h> // REPL_ROLE_ equates.
+#include <netdebug.h> // DBGSTATIC, NetpKdPrint(), FORMAT_ equates.
+#include <prefix.h> // PREFIX_ equates.
+#include <repldefs.h> // IF_DEBUG().
+#include <replgbl.h> // ReplGlobal variables.
+#include <winerror.h> // ERROR_ and NO_ERROR equates.
+
+
+#define CLIENT_STACK_SIZE (24 * 1024) /* BUGBUG: Wild guess! */
+
+
+DBGSTATIC NET_API_STATUS
+CreateClientThread(
+ IN BOOL ServiceIsStarting
+ );
+
+
+// Start replicating (importing). Wait for client thread to startup.
+// Called when service starts or user does NetReplSetInfo() and changes role.
+NET_API_STATUS
+ImportDirStartRepl (
+ IN BOOL ServiceIsStarting
+ )
+{
+ NET_API_STATUS ApiStatus;
+ DWORD EventTriggered;
+ HANDLE WaitHandles[3];
+
+ IF_DEBUG(REPL) {
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ImportDirStartRepl: Beginning...\n" ));
+ }
+
+ // Reset events in case we've ever been running before.
+ (VOID) ResetEvent( ReplGlobalImportStartupEvent );
+ (VOID) ResetEvent( ReplGlobalClientTerminateEvent );
+
+ // start up client
+
+ ApiStatus = CreateClientThread( ServiceIsStarting );
+ if (ApiStatus != NO_ERROR) {
+ goto Cleanup;
+ }
+
+ NetpAssert( ReplGlobalClientThreadHandle != NULL );
+
+#define DUMP_WAIT_HANDLES( array, total ) \
+ { \
+ DWORD index; \
+ for (index=0; index<total; ++index) { \
+ NetpKdPrint(( " " #array "[" FORMAT_DWORD "] is " \
+ FORMAT_HEX_DWORD ".\n", index, (DWORD) array[index] )); \
+ } \
+ }
+
+ // wait for client to setup
+
+ WaitHandles[0] = ReplGlobalClientTerminateEvent; // terminate
+ WaitHandles[1] = ReplGlobalImportStartupEvent; // client success
+ WaitHandles[2] = ReplGlobalClientThreadHandle; // client died
+
+ IF_DEBUG( CLIENT ) {
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ImportDirStartRepl: wait........................\n" ));
+ DUMP_WAIT_HANDLES( WaitHandles, 3 );
+ }
+
+ EventTriggered = WaitForMultipleObjects( 3, WaitHandles, FALSE,
+ (DWORD) -1 );
+
+ IF_DEBUG( CLIENT ) {
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ImportDirStartRepl: EventTriggered = " FORMAT_DWORD
+ ".\n", EventTriggered ));
+ }
+
+ if (EventTriggered == 0) { // terminate during startup
+
+ ApiStatus = ReplGlobalUninstallUicCode;
+ NetpAssert( ApiStatus != NO_ERROR ); // Should be set by client thread.
+ goto Cleanup;
+
+ } else if (EventTriggered != 1) {
+
+ ApiStatus = GetLastError();
+ NetpAssert( ApiStatus != NO_ERROR );
+ goto Cleanup;
+
+ }
+ return (NO_ERROR);
+
+
+Cleanup: // Cleanup for abnormal exits only!
+
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ImportDirStartRepl: *ERROR*, cleaning up, stat="
+ FORMAT_API_STATUS ".\n", ApiStatus ));
+
+ NetpAssert( ApiStatus != NO_ERROR );
+
+ if( ReplGlobalClientThreadHandle != NULL ) {
+
+ // wait for client thread to complete
+ (VOID) WaitForSingleObject( ReplGlobalClientThreadHandle, (DWORD) -1 );
+
+ // Avoid confusing anyone else.
+ ReplGlobalClientThreadHandle = NULL;
+ }
+
+ return (ApiStatus);
+
+} // ImportDirStartRepl
+
+
+DBGSTATIC NET_API_STATUS
+CreateClientThread(
+ IN BOOL ServiceIsStarting
+ )
+/*++
+
+Routine Description:
+
+ Creates Client thread.
+
+ stack size CLIENT_STACK_SIZE.
+
+Arguments:
+
+ ServiceIsStarting - TRUE iff we're still starting the service.
+
+Return Value:
+
+ status code
+
+--*/
+{
+ DWORD ThreadID;
+ LPVOID ThreadParm;
+
+ if (ServiceIsStarting) {
+ ThreadParm = NULL;
+ } else {
+ ThreadParm = (LPVOID) & ThreadID; // any non-NULL addr will do.
+ }
+
+ ReplGlobalClientThreadHandle = CreateThread(
+ NULL,
+ CLIENT_STACK_SIZE,
+ ReplClientMain,
+ ThreadParm,
+ 0,
+ &ThreadID);
+
+ if (ReplGlobalClientThreadHandle == NULL) {
+
+ NET_API_STATUS ApiStatus = (NET_API_STATUS) GetLastError();
+
+ NetpAssert( ApiStatus != NO_ERROR );
+
+ ReplFinish( ApiStatus );
+
+ return (ApiStatus);
+ }
+
+ return (NO_ERROR);
+
+} // CreateClientThread
diff --git a/private/net/svcdlls/repl/server/impstop.c b/private/net/svcdlls/repl/server/impstop.c
new file mode 100644
index 000000000..c28625d82
--- /dev/null
+++ b/private/net/svcdlls/repl/server/impstop.c
@@ -0,0 +1,125 @@
+/*++
+
+Copyright (c) 1992-1993 Microsoft Corporation
+
+Module Name:
+
+ ImpStop.c
+
+Abstract:
+
+ Contains ImportDirStopRepl().
+
+Author:
+
+ John Rogers (JohnRo) 09-Feb-1992
+
+Environment:
+
+ User mode only.
+ Uses Win32 APIs.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+ 09-Feb-1992 JohnRo
+ Created this routine to allow dynamically changing role.
+ 18-Aug-1992 JohnRo
+ RAID 2115: repl svc should wait while stopping or changing role.
+ Use PREFIX_ equates.
+ 11-Mar-1993 JohnRo
+ RAID 12100: stopping repl sometimes goes into infinite loop.
+ 02-Apr-1993 JohnRo
+ Use NetpKdPrint() where possible.
+
+--*/
+
+
+// These must be included first:
+
+#include <windows.h> // IN, SetEvent(), DWORD, etc.
+#include <lmcons.h> // NET_API_STATUS.
+
+// These may be included in any order:
+
+#include <impdir.h> // My prototype.
+#include <netdebug.h> // NetpKdPrint(), FORMAT_ equates.
+#include <prefix.h> // PREFIX_ equates.
+#include <repldefs.h> // IF_DEBUG().
+#include <replgbl.h> // ReplGlobal variables.
+#include <winerror.h> // NO_ERROR.
+
+
+NET_API_STATUS
+ImportDirStopRepl (
+ VOID
+ )
+{
+ NET_API_STATUS ApiStatus;
+ DWORD WaitStatus;
+
+ IF_DEBUG(REPL) {
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ImportDirStopRepl: informing import thread...\n" ));
+ }
+
+ //
+ // Tell the import (client) thread that it should stop.
+ //
+
+ if( !SetEvent( ReplGlobalClientTerminateEvent ) ) {
+
+ ApiStatus = GetLastError();
+
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ImportDirStopRepl: SetEvent Failed with error "
+ FORMAT_API_STATUS "\n", ApiStatus ));
+
+ NetpAssert( ApiStatus != NO_ERROR );
+ return (ApiStatus);
+
+ }
+
+ //
+ // Wait for the thread to end, if it hasn't already.
+ //
+
+ if (ReplGlobalClientThreadHandle != NULL) {
+
+ IF_DEBUG(REPL) {
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ImportDirStopRepl: waiting...\n" ));
+ }
+
+ WaitStatus = WaitForSingleObject(
+ ReplGlobalClientThreadHandle,
+ (DWORD) -1); // no timeout. BUGBUG
+
+ // close the thread handle (BUGBUG: not necessary?)
+ (VOID) CloseHandle( ReplGlobalClientThreadHandle );
+
+ if ( WaitStatus != 0 ) {
+ ApiStatus = (NET_API_STATUS) GetLastError();
+
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ImportDirStopRepl: unexpected wait status "
+ FORMAT_DWORD ", api status " FORMAT_API_STATUS ".\n",
+ WaitStatus, ApiStatus ));
+
+ NetpAssert( ApiStatus != NO_ERROR );
+
+ } else {
+
+ ApiStatus = NO_ERROR;
+
+ }
+
+ } else {
+ ApiStatus = NO_ERROR;
+ }
+
+ // Avoid confusing anyone else.
+ ReplGlobalClientThreadHandle = NULL;
+
+ return (ApiStatus);
+}
diff --git a/private/net/svcdlls/repl/server/initany.c b/private/net/svcdlls/repl/server/initany.c
new file mode 100644
index 000000000..5d4d8d187
--- /dev/null
+++ b/private/net/svcdlls/repl/server/initany.c
@@ -0,0 +1,235 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ InitAny.c
+
+Abstract:
+
+ Contains ReplInitAnyList().
+
+Author:
+
+ JR (John Rogers, JohnRo@Microsoft) 06-Jan-1993
+
+Environment:
+
+ User mode only.
+ Portable to any flat, 32-bit environment. (Uses Win32 typedefs.)
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+ 06-Jan-1993 JohnRo
+ Repl WAN support: created this function from InitClientList()
+ and InitClientImpList().
+ 30-Apr-1993 JohnRo
+ Prepare for spaces in computer names.
+ 24-Aug-1993 JohnRo
+ RAID 16419: Repl: spaces in computer names do not work again.
+ Made changes suggested by PC-LINT 5.0
+
+--*/
+
+
+// These must be included first:
+
+#include <windows.h> // DWORD, LPTSTR, etc.
+#include <lmcons.h> // NET_API_STATUS, IN, OUT, etc.
+
+// These may be included in any order:
+
+#include <icanon.h> // I_NetPath functions, LIST_DELIMITER_ equates, etc.
+#include <netdebug.h> // NetpAssert(), NetpKdPrint(), FORMAT_ equates, etc.
+#include <netlib.h> // NetpMemoryAllocate(), etc.
+#include <prefix.h> // PREFIX_ equates.
+#include <repldefs.h> // IF_DEBUG(), my prototype, REPL_LIST_DELIMITER_STR...
+#include <tstr.h> // STRCHR(), STRSIZE(), etc.
+#include <winerror.h> // NO_ERROR, ERROR_ equates.
+
+
+NET_API_STATUS
+ReplInitAnyList(
+ IN LPCTSTR UncanonList OPTIONAL,
+ IN OUT LPTSTR ** NameListPtr, // Allocated by this routine (or set to NULL).
+ IN LPCTSTR ConfigKeywordName,
+ OUT LPDWORD EntryCount
+ )
+
+{
+ DWORD ActualCount = 0;
+ NET_API_STATUS ApiStatus;
+ LPTSTR CanonListEntry;
+ LPTSTR CanonListStart = NULL;
+ DWORD CanonListSize;
+ DWORD Index;
+ LPTSTR * NameList = NULL;
+
+ UNREFERENCED_PARAMETER( ConfigKeywordName ); // BUGBUG: log this?
+
+ //
+ // Check for caller errors.
+ //
+ NetpAssert( NameListPtr != NULL );
+ NetpAssert( ConfigKeywordName != NULL );
+ NetpAssert( EntryCount != NULL );
+
+ //
+ // Handle empty list gracefully...
+ //
+ if ( (UncanonList == NULL) || (UncanonList[0] == TCHAR_EOS) ) {
+
+ ApiStatus = NO_ERROR;
+ goto Cleanup; // go set *NameListPtr and *EntryCount.
+ }
+
+ //
+ // Allocate memory for canon list.
+ //
+ CanonListSize = STRSIZE( UncanonList );
+
+ CanonListStart = NetpMemoryAllocate( CanonListSize );
+
+ if (CanonListStart == NULL) {
+
+ NetpKdPrint(( PREFIX_REPL
+ "ReplInitAnyList: "
+ "can't allocate memory for CanonListStart.\n" ));
+
+ ApiStatus = ERROR_NOT_ENOUGH_MEMORY;
+ goto Cleanup;
+ }
+
+ //
+ // Canonicalize the names.
+ // Note that this depends on computer names (without leading backslashes)
+ // being canonicalized the same as domain names.
+ //
+ IF_DEBUG( REPL ) {
+ NetpKdPrint(( PREFIX_REPL
+ "ReplInitAnyList before canon:\n" ));
+ NetpDbgDisplayReplList(
+ "uncanon list",
+ UncanonList );
+ }
+
+ ApiStatus = I_NetListCanonicalize(
+ NULL,
+ (LPTSTR) UncanonList,
+ (LPTSTR) REPL_LIST_DELIMITER_STR,
+ CanonListStart,
+ CanonListSize,
+ &ActualCount,
+ NULL, // PathTypes
+ 0, // PathTypesLen
+ (NAMETYPE_COMPUTER |
+ OUTLIST_TYPE_API |
+ INLC_FLAGS_MULTIPLE_DELIMITERS |
+ INLC_FLAGS_CANONICALIZE
+ ) );
+
+ if (ApiStatus != NO_ERROR) {
+
+ NetpKdPrint(( PREFIX_REPL
+ "ReplInitAnyList() is in trouble calling "
+ "I_NetListCanonicalize, ApiStatus is " FORMAT_API_STATUS
+ "\n", ApiStatus ));
+
+ ActualCount = 0;
+ goto Cleanup;
+ }
+
+ IF_DEBUG( REPL ) {
+ NetpKdPrint(( PREFIX_REPL
+ "ReplInitAnyList after canon:\n" ));
+ NetpDbgDisplayReplList(
+ "canon list",
+ CanonListStart );
+ }
+
+ //
+ // Handle empty list gracefully.
+ //
+ if (ActualCount == 0) {
+ NetpAssert( ApiStatus == NO_ERROR );
+ goto Cleanup; // go set *NameListPtr and *EntryCount.
+ }
+
+ //
+ // Allocate space for the name list (an array of pointers).
+ //
+ NameList = NetpMemoryAllocate( ActualCount * sizeof( LPTSTR ) );
+ if (NameList == NULL) {
+
+ NetpKdPrint(( PREFIX_REPL
+ "ReplInitAnyList: can't allocate memory for NameList.\n" ));
+
+ ApiStatus = ERROR_NOT_ENOUGH_MEMORY;
+ goto Cleanup;
+ }
+
+ //
+ // CanonListStart has list of computer and domain names...
+ // Scan this list and set array of pointers to names.
+ //
+ CanonListEntry = CanonListStart;
+ for ( Index = 0; Index < ActualCount; ++Index ) {
+
+ NameList[Index] = CanonListEntry;
+
+ //
+ // Move to end of name.
+ //
+ CanonListEntry = STRCHR(CanonListEntry, LIST_DELIMITER_CHAR_API);
+
+ if (CanonListEntry == NULL) {
+ break;
+ }
+
+ //
+ // Terminate previous string and move to next string.
+ //
+ *CanonListEntry++ = TCHAR_EOS;
+
+ }
+
+ IF_DEBUG( REPL ) {
+
+ NetpKdPrint(( PREFIX_REPL "Initial list count (for "
+ FORMAT_LPTSTR ") is "
+ FORMAT_DWORD ".\n",
+ (LPTSTR) ConfigKeywordName,
+ ActualCount ));
+
+ }
+
+ ApiStatus = NO_ERROR;
+
+Cleanup:
+
+ //
+ // Free stuff if we aren't giving it back to caller.
+ //
+ if ( (ApiStatus != NO_ERROR) && (CanonListStart != NULL) ) {
+ NetpMemoryFree( CanonListStart );
+ }
+
+ if ( (ApiStatus != NO_ERROR) && (NameList != NULL) ) {
+ NetpMemoryFree( NameList );
+ }
+
+ //
+ // Set outputs for caller.
+ //
+ *EntryCount = ActualCount; // May be zero.
+
+ if (ApiStatus == NO_ERROR) {
+ *NameListPtr = NameList; // May be NULL.
+ } else {
+ *NameListPtr = NULL;
+ }
+
+ return (ApiStatus);
+}
diff --git a/private/net/svcdlls/repl/server/makefile b/private/net/svcdlls/repl/server/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/net/svcdlls/repl/server/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/net/svcdlls/repl/server/marshall.c b/private/net/svcdlls/repl/server/marshall.c
new file mode 100644
index 000000000..fb89fca44
--- /dev/null
+++ b/private/net/svcdlls/repl/server/marshall.c
@@ -0,0 +1,817 @@
+/*++
+
+Copyright (c) 1987-1993 Microsoft Corporation
+
+Module Name:
+
+ marshall.c
+
+Abstract:
+
+ Contains the functions that are required to marshall/unmarshall
+ message buffer.
+
+Author:
+
+ 11/08/91 (madana)
+ initial coding.
+
+Environment:
+
+ Contains NT-specific code.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+ 19-Dec-1991 JohnRo
+ Made changes suggested by PC-LINT.
+ 16-Jan-1992 JohnRo
+ Avoid using private logon functions.
+ 23-Jan-1992 JohnRo
+ Avoid compiler warnings if MIN_GUARD is zero.
+ 28-Jan-1992 JohnRo
+ Changed to use LPTSTR etc. Also moved RANGECHECK to NetLib.h and
+ renamed it IN_RANGE().
+ 05-Mar-1992 JohnRo
+ A few more changes suggested by PC-LINT.
+ 24-Mar-1992 JohnRo
+ Fixed bug dealing with pulse value in sync msg.
+ Added more debug output.
+ Correct some character types.
+ Use integrity and extent equates in <lmrepl.h>.
+ 08-Apr-1992 JohnRo
+ Fixed UNICODE handling.
+ 11-Feb-1993 JohnRo
+ RAID 10716: msg timing and checksum problems (also downlevel msg bug).
+ Use PREFIX_ equates.
+
+--*/
+
+#include <windows.h>
+
+#include <align.h>
+#include <lmcons.h>
+#include <netdebug.h> // DBGSTATIC, NetDbgHexDump(), etc.
+#include <netlib.h> // IN_RANGE(), etc.
+#include <prefix.h> // PREFIX_ equates.
+#include <smbgtpt.h>
+#include <string.h> // strcpy(), strlen().
+#include <tstring.h> // NetpCopy{type}ToUnalignedWStr(), etc.
+#include <stdlib.h>
+
+
+// repl headers
+
+#include <repldefs.h> // IF_DEBUG(), etc.
+#include <replpack.h>
+#include <iniparm.h>
+
+#define NOT_NET_VERSION 0
+#define NT_VERSION 1
+
+
+#ifdef UNICODE
+
+DBGSTATIC VOID
+ReplGetUnicodeName(
+ OUT LPWSTR WhereDst,
+ IN LPBYTE WhereSrc
+ );
+
+#define ReplGetNeutralName ReplGetUnicodeName
+
+#else // ndef UNICODE
+
+DBGSTATIC VOID
+ReplGetNeutralName(
+ OUT LPTSTR WhereDst,
+ IN LPBYTE WhereSrc
+ );
+
+#endif // ndef UNICODE
+
+
+#define REPORT_BAD_INCOMING_MSG( bufStart, bufSize, explanation ) \
+ { \
+ NetpKdPrint(( PREFIX_REPL_CLIENT "BAD INCOMING MSG (" \
+ explanation ")\n" )); \
+ NetpDbgHexDump( bufStart, bufSize ); \
+ }
+
+
+NET_API_STATUS
+ReplMarshallQueryMsg(
+ IN PBYTE InBuf,
+ OUT PBYTE OutBuf,
+ IN LPDWORD BytesRead
+ )
+/*++
+
+Routine Description:
+
+ Marshall QUERY type message. The marshalled data buffer will look like as
+ follows:
+
+ PACK_QUERY_MSG
+ PACK_HEADER
+ WORD msg_type;
+ CHAR sender[LM20_CNLEN+1];
+ CHAR senders_domain[LM20_DNLEN+1];
+ CHAR dir_name[LM20_PATHLEN]; // ASCIIZ dir/tree name.
+ WCHAR unicode_sender[];
+ WCHAR unicode_senders_domain[];
+ WCHAR unicode_dir_name[];
+ DWORD NT_TOKEN; // unaligned
+
+Arguments:
+
+ InBuf : input buffer that contains QUERY_MSG data structure.
+ OutBuf : output buffer that will have the marshalled data.
+ BytesRead : length of the marshalled data
+
+Return Value:
+
+ NERR_Success : if successfully unmarshall/validate the message.
+ error code : otherwise
+
+ NOTE : OutBuf is assumed big enough to fit the entire marshalled data.
+
+--*/
+{
+
+ PQUERY_MSG QueryMsg;
+ PPACK_QUERY_MSG PackQueryMsg;
+ DWORD Len, Token, TotalSize;
+ LPBYTE Where;
+
+ NetpAssert( InBuf != NULL );
+ NetpAssert( OutBuf != NULL );
+
+ QueryMsg = (PQUERY_MSG) InBuf;
+ PackQueryMsg = (PPACK_QUERY_MSG) OutBuf;
+
+ // copy header info.
+
+ SmbPutUshort( (LPWORD) &(PackQueryMsg->header.msg_type),
+ (WORD) QueryMsg->header.msg_type );
+
+#if defined(DBCS) && defined(UNICODE) // ReplMarshallQueryMsg()
+ NetpCopyWStrToStrDBCS(
+ PackQueryMsg->header.sender, // dest
+ QueryMsg->header.sender ); // src
+
+ NetpCopyWStrToStrDBCS(
+ PackQueryMsg->header.senders_domain, // dest
+ QueryMsg->header.senders_domain ); // src
+
+ NetpCopyWStrToStrDBCS(
+ PackQueryMsg->dir_name, // dest
+ QueryMsg->dir_name ); // src
+#else
+ NetpCopyTStrToStr(
+ PackQueryMsg->header.sender, // dest
+ QueryMsg->header.sender ); // src
+
+ NetpCopyTStrToStr(
+ PackQueryMsg->header.senders_domain, // dest
+ QueryMsg->header.senders_domain ); // src
+
+ NetpCopyTStrToStr(
+ PackQueryMsg->dir_name, // dest
+ QueryMsg->dir_name ); // src
+#endif // DBCS
+
+
+ // copy unicode names
+
+ Where = OutBuf + sizeof(PACK_QUERY_MSG);
+
+ Len = ( STRLEN( QueryMsg->header.sender ) + 1 ) * sizeof( WCHAR );
+
+ NetpCopyTStrToUnalignedWStr( Where, QueryMsg->header.sender );
+
+ Where += Len;
+
+ Len = ( STRLEN( QueryMsg->header.senders_domain ) + 1 ) * sizeof( WCHAR );
+
+ NetpCopyTStrToUnalignedWStr( Where, QueryMsg->header.senders_domain );
+
+ Where += Len;
+
+ Len = ( STRLEN( QueryMsg->dir_name ) + 1 ) * sizeof( WCHAR );
+
+ NetpCopyTStrToUnalignedWStr( Where, QueryMsg->dir_name );
+
+ Where += Len;
+
+ // add token
+
+ Token = NT_MSG_TOKEN;
+
+ NetpMoveMemory( Where, &Token, sizeof( DWORD ));
+
+ Where += sizeof( DWORD );
+
+ TotalSize = (DWORD) (Where - OutBuf);
+ NetpAssert( TotalSize != 0 );
+ *BytesRead = TotalSize;
+
+ return NO_ERROR;
+}
+
+
+NET_API_STATUS
+ReplUnmarshallMessage(
+ IN PBYTE InBuf,
+ IN DWORD InBufLen,
+ OUT PBYTE OutBuf,
+ IN DWORD OutBufLen,
+ OUT LPDWORD BytesRead
+ )
+/*++
+
+Routine Description:
+
+ Unmarshall a message buffer. This function determines the message version
+ (NT/DOWNLEVEL), message type and unmarshalls it accordingly. This
+ function also validates the messsage fields.
+
+Arguments:
+
+ InBuf : incoming message buffer that contains marshalled data.
+ InBufLen : length of input buffer.
+ OutBuf : unmarshalled message buffer.
+ OutBufLen : unmarshall buffer length.
+ BytesRead : unmarshalled message length.
+
+Return Value:
+
+ NO_ERROR : if successfully unmarshall/validate the message.
+ error code : otherwise
+
+--*/
+{
+
+ DWORD MsgType;
+
+ // get message type.
+
+ MsgType = (DWORD) SmbGetUshort( (LPWORD)InBuf );
+
+ switch ( MsgType ) {
+
+ case SYNC_MSG : /*FALLTHROUGH*/
+ case GUARD_MSG : /*FALLTHROUGH*/
+ case PULSE_MSG :
+
+ return( ReplUnmarshallSyncMsg(InBuf,
+ InBufLen,
+ OutBuf,
+ OutBufLen,
+ BytesRead) );
+
+ case IS_DIR_SUPPORTED : /*FALLTHROUGH*/
+ case IS_MASTER : /*FALLTHROUGH*/
+ case DIR_NOT_SUPPORTED : /*FALLTHROUGH*/
+ case DIR_SUPPORTED : /*FALLTHROUGH*/
+ case NOT_MASTER_DIR : /*FALLTHROUGH*/
+ case MASTER_DIR : /*FALLTHROUGH*/
+ case DIR_COLLIDE :
+
+ return( ReplUnmarshallQueryMsg(InBuf,
+ InBufLen,
+ OutBuf,
+ OutBufLen,
+ BytesRead) );
+
+ default :
+
+ REPORT_BAD_INCOMING_MSG( InBuf, InBufLen, "bad msg type" );
+ return ERROR_INVALID_DATA;
+
+ }
+ /*NOTREACHED*/
+
+}
+
+NET_API_STATUS
+ReplUnmarshallSyncMsg(
+ IN PBYTE InBuf,
+ IN DWORD InBufLen,
+ OUT PBYTE OutBuf,
+ IN DWORD OutBufLen,
+ OUT LPDWORD BytesRead
+ )
+/*++
+
+Routine Description:
+
+ Unmarshall SYNC/PULSE/GUARD type message buffer. The input buffer contains
+ packed PACK_SYNGMSG and PACK_MSG_STATUS_REC as defined in replpack.h and
+ as marshalled in puls_msg.c.
+
+Arguments:
+
+ InBuf : incoming message buffer that contains marshalled data.
+ InBufLen : length of input buffer.
+ OutBuf : unmarshalled message buffer.
+ OutBufLen : unmarshall buffer length.
+ BytesRead : unmarshalled message length.
+
+Return Value:
+
+ NO_ERROR : if successfully unmarshall/validate the message.
+ error code : otherwise
+
+--*/
+{
+ DWORD MsgToken;
+ DWORD Version;
+ DWORD i;
+
+ PSYNCMSG SyncMsg;
+ PPACK_SYNCMSG PackSyncMsg;
+
+ LPWSTR UnicodeName;
+ DWORD UpdateCount;
+
+ PMSG_STATUS_REC UpdateRec;
+ PPACK_MSG_STATUS_REC PackUpdateRec;
+
+ PBYTE WhereSrc;
+ PBYTE WhereDst;
+
+ DWORD Len;
+
+ *BytesRead = 0;
+
+ MsgToken = SmbGetUlong( (LPBYTE) (InBuf + InBufLen - sizeof(DWORD)) );
+
+ Version = (MsgToken == NT_MSG_TOKEN) ? NT_VERSION : NOT_NET_VERSION;
+
+ // copy header info.
+
+ SyncMsg = (PSYNCMSG) OutBuf;
+
+ PackSyncMsg = (PPACK_SYNCMSG) InBuf;
+
+ SyncMsg->header.msg_type =
+ (DWORD) SmbGetUshort( (LPBYTE) &(PackSyncMsg->header.msg_type) );
+
+ // msg_type is checked already in the main unmarshall routine
+
+ UpdateCount = SyncMsg->update_count =
+ (DWORD) SmbGetUshort( (LPWORD) &(PackSyncMsg->update_count));
+
+ // do sanity check on UpdateCount
+
+ if( InBufLen < ( sizeof( PACK_SYNCMSG ) +
+ UpdateCount * sizeof( PACK_MSG_STATUS_REC ) ) ) {
+
+ // bogus message, message size is not sufficient even to fit
+ // fix portion.
+
+ REPORT_BAD_INCOMING_MSG( InBuf, InBufLen, "sync msg too small" );
+ return ERROR_INVALID_DATA;
+
+ }
+
+ if( OutBufLen < ( sizeof(SYNCMSG) ) ) {
+
+ // output buffer is too small
+
+ REPORT_BAD_INCOMING_MSG( InBuf, InBufLen,
+ "sync buffer too small for fixed" );
+ return ERROR_INSUFFICIENT_BUFFER;
+
+ }
+
+ if( Version != NT_VERSION ) {
+
+ // sanity check on ANSI names
+
+ if( ( strlen((LPSTR) PackSyncMsg->header.sender) > LM20_CNLEN ) ||
+ ( strlen( (LPSTR)PackSyncMsg->header.senders_domain ) >
+ LM20_DNLEN ) ) {
+
+ REPORT_BAD_INCOMING_MSG( InBuf, InBufLen,
+ "sync msg ANSI names too long" );
+ return ERROR_INVALID_DATA;
+
+ }
+
+ // copy neutral names from ansi strings
+
+ NetpCopyStrToTStr (
+ SyncMsg->header.sender, // dest
+ PackSyncMsg->header.sender ); // src
+
+ NetpCopyStrToTStr (
+ SyncMsg->header.senders_domain, // dest
+ PackSyncMsg->header.senders_domain ); // src
+
+ } else {
+
+ // copy neutral names from unicode strings
+
+ WhereSrc = (PBYTE) (InBuf +
+ sizeof(PACK_SYNCMSG) +
+ UpdateCount * sizeof(PACK_MSG_STATUS_REC)) ;
+
+ ReplGetNeutralName(SyncMsg->header.sender, WhereSrc);
+
+ WhereSrc += ( ( STRLEN(SyncMsg->header.sender) + 1 ) * sizeof(WCHAR) );
+
+ ReplGetNeutralName(SyncMsg->header.senders_domain, WhereSrc);
+
+ // sanity check on UNICODE names
+
+ if( ( STRLEN( SyncMsg->header.sender ) > CNLEN ) ||
+ ( STRLEN( SyncMsg->header.senders_domain ) > DNLEN ) ) {
+
+ REPORT_BAD_INCOMING_MSG( InBuf, InBufLen,
+ "sync msg UNICODE names too long" );
+ return ERROR_INVALID_DATA;
+
+ }
+
+ }
+
+
+ SyncMsg->info.random =
+ (DWORD) SmbGetUshort( (LPWORD) &(PackSyncMsg->info.random));
+
+ if( !ReplIsRandomValid( SyncMsg->info.random ) ) {
+
+ REPORT_BAD_INCOMING_MSG( InBuf, InBufLen, "sync msg bad RANDOM value" );
+ return ERROR_INVALID_DATA;
+ }
+
+ {
+ DWORD SyncRate;
+
+ SyncRate =
+ (DWORD) SmbGetUshort( (LPWORD) &(PackSyncMsg->info.sync_rate));
+ SyncMsg->info.sync_rate = SyncRate;
+
+ if ( !ReplIsIntervalValid( SyncRate ) ) {
+
+ REPORT_BAD_INCOMING_MSG( InBuf, InBufLen,
+ "sync msg bad SYNC value" );
+ return ERROR_INVALID_DATA;
+ }
+
+ // Get pulse rate (pulse * sync time):
+ SyncMsg->info.pulse_rate =
+ (DWORD) SmbGetUshort( (LPWORD) &(PackSyncMsg->info.pulse_rate));
+
+ if ( !IN_RANGE( SyncMsg->info.pulse_rate,
+ (MIN_PULSE*SyncRate),
+ (MAX_PULSE*SyncRate) ) ) {
+
+ REPORT_BAD_INCOMING_MSG( InBuf, InBufLen,
+ "sync msg bad PULSE value" );
+ return ERROR_INVALID_DATA;
+ }
+ }
+
+ SyncMsg->info.guard_time =
+ (DWORD) SmbGetUshort( (LPWORD) &(PackSyncMsg->info.guard_time));
+
+ if( !ReplIsGuardTimeValid( SyncMsg->info.guard_time ) ) {
+
+ REPORT_BAD_INCOMING_MSG( InBuf, InBufLen, "sync msg bad GUARD value" );
+ return ERROR_INVALID_DATA;
+ }
+
+ UpdateRec = (PMSG_STATUS_REC) (LPVOID) (SyncMsg + 1);
+
+ PackUpdateRec = (PPACK_MSG_STATUS_REC) (LPVOID) (PackSyncMsg + 1);
+
+ WhereDst = (PBYTE) ( OutBuf +
+ sizeof(SYNCMSG) +
+ UpdateCount * sizeof(MSG_STATUS_REC) );
+
+ // align to WCHAR boundary
+
+ WhereDst = ROUND_UP_POINTER( WhereDst, ALIGN_WCHAR );
+
+ for( i = 0;
+ i < UpdateCount;
+ i++, UpdateRec++, PackUpdateRec++ ) {
+
+ UpdateRec->dir_name_offset = (DWORD) (WhereDst - OutBuf);
+ if (UpdateRec->dir_name_offset > OutBufLen) {
+
+ // output buffer is too small
+
+ REPORT_BAD_INCOMING_MSG( InBuf, InBufLen,
+ "sync name offset too large" );
+ return ERROR_INSUFFICIENT_BUFFER;
+
+ }
+
+
+ WhereSrc = InBuf +
+ (DWORD) SmbGetUshort( (LPWORD) &(PackUpdateRec->dir_name_offset));
+
+ if( Version == NT_VERSION ) {
+
+ // skip ansi dir name.
+
+ WhereSrc += (DWORD) ( strlen( (LPSTR) WhereSrc) + 1 );
+
+ ReplGetNeutralName( (LPTSTR) WhereDst, WhereSrc );
+
+ if( ( Len = wcslen((LPWSTR) WhereDst) ) > PATHLEN ) {
+
+ REPORT_BAD_INCOMING_MSG( InBuf, InBufLen,
+ "sync NT name too long" );
+ return ERROR_INVALID_DATA;
+
+ }
+ } else {
+
+ // copy unicode dir name from ansi string
+
+ UnicodeName = NetpAllocWStrFromStr( (LPSTR) WhereSrc );
+
+ if( UnicodeName == NULL ) {
+
+ // no memory.
+
+ return ERROR_NOT_ENOUGH_MEMORY;
+
+ }
+
+ (void) wcscpy( (LPWSTR) WhereDst, UnicodeName );
+
+ NetpMemoryFree( UnicodeName );
+
+ if( ( Len = wcslen((LPWSTR) WhereDst) ) > LM20_PATHLEN ) {
+
+ REPORT_BAD_INCOMING_MSG( InBuf, InBufLen,
+ "sync non-NT name too long" );
+ return ERROR_INVALID_DATA;
+
+ }
+ }
+
+ WhereDst += ( ( Len + 1 ) * sizeof(WCHAR) );
+
+ UpdateRec->opcode =
+ (DWORD) SmbGetUshort( (LPWORD) &(PackUpdateRec->opcode));
+
+ UpdateRec->checksum =
+ SmbGetUlong( (LPDWORD) &(PackUpdateRec->checksum));
+
+ UpdateRec->count =
+ (DWORD) SmbGetUshort( (LPWORD) &(PackUpdateRec->count));
+
+ UpdateRec->integrity =
+ (DWORD) SmbGetUshort( (LPWORD) &(PackUpdateRec->integrity));
+
+ UpdateRec->extent =
+ (DWORD) SmbGetUshort( (LPWORD) &(PackUpdateRec->extent));
+
+ if( !IN_RANGE( UpdateRec->opcode, START, GUARD ) ||
+ !ReplIsIntegrityValid( UpdateRec->integrity ) ||
+ !ReplIsExtentValid( UpdateRec->extent ) ) {
+
+ REPORT_BAD_INCOMING_MSG( InBuf, InBufLen,
+ "sync RANGE/INTEGRITY/EXTENT invalid" );
+ return ERROR_INVALID_DATA;
+
+ }
+
+
+ }
+
+ // set total bytes read in the output buffer.
+
+ *BytesRead = (DWORD) (WhereDst - OutBuf);
+
+ return NO_ERROR;
+
+}
+
+NET_API_STATUS
+ReplUnmarshallQueryMsg(
+ IN PBYTE InBuf,
+ IN DWORD InBufLen,
+ OUT PBYTE OutBuf,
+ IN DWORD OutBufLen,
+ OUT LPDWORD BytesRead
+ )
+/*++
+
+Routine Description:
+
+ Unmarshall QUERY type message buffer.
+
+Arguments:
+
+ InBuf : incoming message buffer that contains marshalled data.
+ InBufLen : length of input buffer.
+ OutBuf : unmarshalled message buffer.
+ OutBufLen : unmarshall buffer length.
+ BytesRead : unmarshalled message length.
+
+Return Value:
+
+ NO_ERROR : if successfully unmarshall/validate the message.
+ error code : otherwise
+
+--*/
+{
+ DWORD MsgToken;
+ DWORD Version;
+
+ PQUERY_MSG QueryMsg;
+ PPACK_QUERY_MSG PackQueryMsg;
+
+ PBYTE WhereSrc;
+
+ *BytesRead = 0;
+
+ // sanity check input message length
+
+ if( InBufLen < sizeof(PACK_QUERY_MSG) ) {
+
+ REPORT_BAD_INCOMING_MSG( InBuf, InBufLen, "query msg too small" );
+ return ERROR_INVALID_DATA;
+
+ }
+
+ MsgToken = SmbGetUlong( (LPBYTE) (InBuf + InBufLen - sizeof(DWORD)));
+
+ if( (MsgToken == NT_MSG_TOKEN) && (InBufLen != sizeof(PACK_QUERY_MSG)) ) {
+
+ Version = NT_VERSION;
+
+ } else {
+
+ Version = NOT_NET_VERSION;
+
+ }
+
+ if( OutBufLen < sizeof(QUERY_MSG) ) {
+
+ // output buffer is too small
+
+ REPORT_BAD_INCOMING_MSG( InBuf, InBufLen,
+ "query output buffer too small" );
+ return ERROR_INSUFFICIENT_BUFFER;
+
+ }
+
+ // copy header info.
+
+ QueryMsg = (PQUERY_MSG) OutBuf;
+
+ PackQueryMsg = (PPACK_QUERY_MSG) InBuf;
+
+ QueryMsg->header.msg_type =
+ (DWORD) SmbGetUshort( (LPBYTE) &(PackQueryMsg->header.msg_type) );
+
+ // msg_type value is already validated in the main unmarshall routine ..
+
+ if( Version != NT_VERSION ) {
+
+ // validate ansi names ..
+
+ if( ( strlen( (LPSTR) PackQueryMsg->header.sender ) > LM20_CNLEN ) ||
+ ( strlen( (LPSTR)PackQueryMsg->header.senders_domain) >
+ LM20_DNLEN )) {
+
+ REPORT_BAD_INCOMING_MSG( InBuf, InBufLen,
+ "query ANSI names too long" );
+ return ERROR_INVALID_DATA;
+
+ }
+
+ // copy neutral names from ansi strings
+
+ NetpCopyStrToTStr (
+ QueryMsg->header.sender, // dest
+ (LPSTR) PackQueryMsg->header.sender ); // src
+
+
+ NetpCopyStrToTStr (
+ QueryMsg->header.senders_domain, // dest
+ (LPSTR)PackQueryMsg->header.senders_domain ); // src
+
+ } else {
+
+ // copy unicode names from unicode strings
+
+ WhereSrc = (PBYTE) (InBuf + sizeof(PACK_QUERY_MSG) );
+
+ ReplGetNeutralName(QueryMsg->header.sender, WhereSrc);
+
+ WhereSrc += ( ( STRLEN(QueryMsg->header.sender) + 1 ) * sizeof(WCHAR) );
+
+ ReplGetNeutralName(QueryMsg->header.senders_domain, WhereSrc);
+
+ // validate unicode names ..
+
+ if( ( STRLEN( QueryMsg->header.sender ) > CNLEN ) ||
+ ( STRLEN( QueryMsg->header.senders_domain) > DNLEN )) {
+
+ REPORT_BAD_INCOMING_MSG( InBuf, InBufLen,
+ "query UNICODE names too long" );
+ return ERROR_INVALID_DATA;
+
+ }
+
+ }
+
+ *BytesRead = sizeof(QUERY_MSG);
+
+ return NO_ERROR;
+}
+
+#ifndef UNICODE
+
+DBGSTATIC VOID
+ReplGetNeutralName(
+ OUT LPTSTR NeutralName,
+ IN LPBYTE UnicodeRaw
+ )
+/*++
+
+Routine Description:
+
+ Copy an unaligned Unicode string to aligned buffer.
+
+Arguments:
+
+ NeutralName : TCHAR aligned buffer.
+ UnicodeRaw : unaligned buffer with unicode name in it.
+
+Return Value:
+
+ none
+
+--*/
+{
+
+ TCHAR NeutralChar;
+ WCHAR UnicodeChar;
+
+ do {
+
+ UnicodeChar = (WCHAR) SmbGetUshort ( UnicodeRaw );
+
+ NeutralChar = (TCHAR) UnicodeChar; // BUGBUG: Wrong!!
+
+ *NeutralName++ = NeutralChar;
+
+ UnicodeRaw += sizeof(WCHAR);
+
+ } while (UnicodeChar != L'\0' );
+
+} // ReplGetNeutralName
+
+
+#else // def UNICODE
+
+
+DBGSTATIC VOID
+ReplGetUnicodeName(
+ OUT LPWSTR UnicodeName,
+ IN LPBYTE UnicodeRaw
+ )
+/*++
+
+Routine Description:
+
+ Copy a unaligned Unicode string to aligned buffer.
+
+Arguments:
+
+ UnicodeName : WCHAR aligned buffer.
+ UnicodeRaw : unaligned buffer with unicode name in it.
+
+Return Value:
+
+ none
+
+--*/
+{
+
+ WCHAR UnicodeChar;
+
+ do {
+
+ UnicodeChar = (WCHAR) SmbGetUshort ( UnicodeRaw );
+
+ *UnicodeName++ = UnicodeChar;
+
+ UnicodeRaw += sizeof(WCHAR);
+
+ } while (UnicodeChar != L'\0' );
+
+} // ReplGetUnicodeName
+
+#endif // def UNICODE
diff --git a/private/net/svcdlls/repl/server/masproto.h b/private/net/svcdlls/repl/server/masproto.h
new file mode 100644
index 000000000..cf54d8885
--- /dev/null
+++ b/private/net/svcdlls/repl/server/masproto.h
@@ -0,0 +1,234 @@
+/*++
+
+Copyright (c) 1987-1993 Microsoft Corporation
+
+Module Name:
+ masproto.h
+
+Abstract:
+ Function prototypes for repl master.
+
+Author:
+ Ported from Lan Man 2.x
+
+Environment:
+ Contains NT-specific code.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+ 10/24/91 (madana)
+ created new file
+ 07-Jan-1992 JohnRo
+ Added RemoveMasterRecForDirName() for use by NetrReplExportDirDel().
+ Delete bogus isspace() prototype to avoid conflicts with <ctype.h>.
+ 24-Jan-1992 JohnRo
+ Changed to use LPTSTR etc.
+ Use ReplConfigRead() instead of GetReplIni().
+ 09-Feb-1992 JohnRo
+ Set up to dynamically change role.
+ 23-Mar-1992 JohnRo
+ Got rid of useless master and client termination codes.
+ 13-Apr-1992 JohnRo
+ Improve error handling if pulser thread can't start.
+ 05-Jan-1993 JohnRo
+ Repl WAN support.
+ Added some parameter names to some prototypes here.
+ 13-Jan-1993 JohnRo
+ RAID 7053: locked trees added to pulse msg. (Actually fix all
+ kinds of remote lock handling.)
+ 09-Apr-1993 JohnRo
+ RAID 5959: export side gets long hourglass too.
+
+
+--*/
+
+
+#ifndef _MASPROTO_
+#define _MASPROTO_
+
+
+#include <repldefs.h> // PQUERY_MSG, etc.
+#include <master.h> // PMASTER_LIST_REC, etc.
+
+
+// F U N C T I O N S
+
+NET_API_STATUS
+InitMaster(
+ IN BOOL ServiceIsStarting
+ );
+
+BOOL
+CreateReplShare(
+ IN LPTSTR ExportPath
+ );
+
+NET_API_STATUS
+CreatePulserThread(
+ VOID
+ );
+
+NET_API_STATUS
+ReplCheckExportLocks(
+ IN LPCTSTR UncServerName OPTIONAL,
+ IN LPCTSTR DirName,
+ OUT LPBOOL IsLockedPtr
+ );
+
+VOID
+ReplMasterCleanup(
+ VOID
+ );
+
+BOOL
+ReplMasterNameCheck(
+ IN LPTSTR Sender,
+ IN LPTSTR Domain
+ );
+
+VOID
+SendMasterMsg(
+ IN DWORD MsgType,
+ IN PQUERY_MSG MsgRcvd
+ );
+
+VOID
+SyncUpdate(
+ VOID
+ );
+
+VOID
+GuardUpdate(
+ VOID
+ );
+
+VOID
+SendPulse(
+ VOID
+ );
+
+VOID
+ScanObject(
+ IN LPTSTR DirName,
+ IN DWORD Mode,
+ IN BOOL CallerHasExclLock
+ );
+
+VOID
+CheckForDelDirs(
+ VOID
+ );
+
+VOID
+SetForDelDirs(
+ VOID
+ );
+
+VOID
+UpdateRecAndMsg(
+ IN OUT PMASTER_LIST_REC DirRec,
+ IN PCHECKSUM_REC NewCheck,
+ IN DWORD MsgOpcode
+ );
+
+PMASTER_LIST_REC
+NewMasterRecord(
+ IN LPTSTR DirName,
+ IN DWORD Integrity,
+ IN DWORD Extent
+ );
+
+PMASTER_LIST_REC
+GetMasterRec(
+ IN LPTSTR DirName
+ );
+
+VOID
+RemoveFromList(
+ IN PMASTER_LIST_REC Rec
+ );
+
+NET_API_STATUS
+RemoveMasterRecForDirName (
+ IN LPTSTR DirName
+ );
+
+VOID
+InitLists(
+ VOID
+ );
+
+BOOL
+InitMsgBuf(
+ VOID
+ );
+
+VOID
+InitMsgSend(
+ IN DWORD MsgType
+ );
+
+VOID
+AddToMsg(
+ IN PMASTER_LIST_REC Rec,
+ IN DWORD Opcode
+ );
+
+VOID
+MsgSend(
+ VOID
+ );
+
+DWORD
+PulserThread(
+ IN LPVOID parm
+ );
+
+VOID
+PulserExit(
+ VOID
+ );
+
+BOOL
+GetParm(
+ IN LPTSTR parameter,
+ OUT LPBYTE buf,
+ IN WORD buflen,
+ OUT PDWORD parmlen,
+ IN int fh
+ );
+
+BOOL
+next_parameter(
+ IN int fh,
+ OUT LPBYTE buf,
+ IN DWORD buflen
+ );
+
+BOOL
+get_conf_line(
+ IN int fh,
+ OUT LPBYTE buf,
+ IN DWORD buflen
+ );
+
+#if 0
+BOOL
+pwcscmp(
+ IN LPTSTR parameter,
+ IN LPTSTR template
+ );
+#endif // 0
+
+VOID
+ReplMasterFreeList(
+ VOID
+ );
+
+VOID
+ReplMasterFreeMsgBuf(
+ VOID
+ );
+
+
+#endif // _MASPROTO_
diff --git a/private/net/svcdlls/repl/server/master.c b/private/net/svcdlls/repl/server/master.c
new file mode 100644
index 000000000..7788d14bd
--- /dev/null
+++ b/private/net/svcdlls/repl/server/master.c
@@ -0,0 +1,1043 @@
+/*++
+
+Copyright (c) 1987-1993 Microsoft Corporation
+
+Module Name:
+ master.c
+
+Abstract:
+ Entry point and main thread of REPL master service.
+
+Author:
+ Ported from Lan Man 2.x
+
+Environment:
+ User mode only.
+ Contains NT-specific code.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+ Tab size is set to 4.
+
+Revision History:
+ 04/03/89 (yuv)
+ initial coding
+ 10/07/91 (madana)
+ ported to NT. Converted to NT style.
+ 20-Jan-1992 JohnRo
+ Avoid using private logon functions.
+ Changed file name from repl.h to replgbl.h to avoid MIDL conflict.
+ Added global flag for ReplTest use.
+ Added debug print of thread ID.
+ ReportStatus() should add thread ID to status being reported.
+ Fixed bug regarding returned value from NetpReplWriteMail functions.
+ Changed to use NetLock.h (allow shared locks, for one thing).
+ PC-LINT found bug in UNICODE version.
+ Made other changes suggested by PC-LINT.
+ 24-Jan-1992 JohnRo
+ Changed to use LPTSTR etc.
+ 10-Feb-1992 JohnRo
+ Added lock for master's client list (RMGlobalClientList).
+ Set up to dynamically change role.
+ Made changes suggested by PC-LINT.
+ Use FORMAT equates.
+ 14-Feb-1992 JohnRo
+ Master's client list lock was created too late.
+ 14-Feb-1992 JohnRo
+ Added debug message when thread ends.
+ 22-Feb-1992 JohnRo
+ Minor changes to mailslot name handling.
+ PC-LINT found a bug.
+ Made other changes suggested by PC-LINT.
+ 05-Mar-1992 JohnRo
+ Changed interface to match new service controller.
+ 11-Mar-1992 JohnRo
+ Use SHI_USES_UNLIMITED equate.
+ 24-Mar-1992 JohnRo
+ Added/corrected some lock handling.
+ Renamed many ReplGlobal vars to ReplConfig vars.
+ Read export dir data at startup.
+ Set event that ExportDirStartRepl() is waiting on.
+ Don't tell service controller whole service is started too soon.
+ Modify REPL$ share handling.
+ 25-Mar-1992 JohnRo
+ A little more debug message cleanup.
+ 01-Apr-1992 JohnRo
+ CliffV told me about overlapped I/O vs. termination events.
+ Improve error code if wksta not started.
+ 13-Apr-1992 JohnRo
+ Improve error handling if pulser thread can't start.
+ 09-Jul-1992 JohnRo
+ RAID 10503: srv mgr: repl dialog doesn't come up.
+ Use PREFIX_ equates.
+ Made other changes suggested by PC-LINT.
+ 19-Aug-1992 JohnRo
+ RAID 2115: repl svc should wait while stopping or changing role.
+ Help track down bogus import/export lists.
+ 27-Aug-1992 JohnRo
+ RAID 4611: repl: fix import/export lists.
+ 25-Oct-1992 jimkel
+ changed lock structure. Removed RMGlobalClientListLock
+ renamed RMGlobalClientList to RMGlobalImportList
+ RMGlobalClientCount to RMGlobalImportCount
+ now locked by ReplConfigLock
+ 18-Nov-1992 JohnRo
+ RAID 1537: repl APIs in wrong role kill service.
+ Allow "infinite" wait for pulser thread when cleaning up.
+ Added some mailslot msg debug checks.
+ Undo the canon list workaround.
+ 23-Nov-1992 JohnRo
+ RAID 3571: Repl svc generates bogus "system error 1" popups
+ 05-Jan-1993 JohnRo
+ Repl WAN support (get rid of repl name list limits).
+ Added a little debug output.
+ Made changes suggested by PC-LINT 5.0
+ 13-Jan-1993 JohnRo
+ RAID 7053: locked trees added to pulse msg. (Actually fix all
+ kinds of remote lock handling.)
+ More changes suggested by PC-LINT 5.0
+ 30-Mar-1993 JohnRo
+ Minor debug output changes here and there.
+ Still more changes suggested by PC-LINT 5.0
+
+--*/
+
+// These must be included first:
+
+#include <windows.h>
+#include <lmcons.h>
+
+// These may be included in any order:
+
+#include <alertmsg.h>
+#include <confname.h> // REPL_KEYWORD_ equates.
+#include <expdir.h> // ExportDirReadMasterList().
+#include <icanon.h> // NAMETYPE_ stuff; needed by ReplNetNameCompare().
+#include <lmerr.h> // NERR_ and ERROR_ equates; NO_ERROR.
+#include <lmerrlog.h>
+#ifdef OLD_SHARE_STUFF
+#include <lmserver.h>
+#include <lmshare.h> // SHARE_INFO_2, NetShareAdd(), SHI_, etc.
+#endif
+#include <lmsname.h> // SERVICE_WORKSTATION.
+#include <netdebug.h> // NetpKdPrint(), FORMAT_ equates.
+#include <netlib.h> // NetpMemoryAllocate(), NetpIsServerStarted().
+#include <netlock.h> // Lock data types, functions, and macros.
+#include <prefix.h> // PREFIX_ equates.
+#include <string.h> // memset().
+#include <thread.h> // FORMAT_NET_THREAD_ID, NetpCurrentThread().
+#include <tstr.h> // STRCPY(), TCHAR_EOS, etc.
+
+// repl headers
+
+#include <repldefs.h> // IF_DEBUG(), etc.
+#include <replgbl.h> // ReplGlobal and ReplConfig variables.
+#include <iniparm.h>
+#include <master.h>
+#include <masproto.h>
+#include <replp.h>
+
+
+// Global Data Declarations:
+
+DWORD RMGlobalCompatibilityMode = TRUE;
+
+DWORD RMGlobalPulserTID; // PulserThread tid.
+HANDLE RMGlobalPulserThreadHandle;
+
+DBGSTATIC HANDLE RMGlobalMstMSlotHandle;
+
+LPNET_LOCK RMGlobalListLock;
+
+#ifdef OLD_SHARE_STUFF
+LPTSTR RMGlobalReplShare = REPL_SHARE;
+DBGSTATIC LPTSTR RMGlobalShareComment = SHARE_COMMENT;
+
+DBGSTATIC BOOL RMGlobalShareCreated = FALSE;
+#endif
+
+DBGSTATIC BOOL RMGlobalMailslotCreated = FALSE;
+DBGSTATIC BOOL RMGlobalPulserThreadCreated = FALSE;
+
+LPTSTR *RMGlobalImportList = NULL;
+DWORD RMGlobalImportCount = 0;
+
+#if DBG
+BOOL RMGlobalMasterThreadInit = FALSE; // only used by ReplTest stuff
+#endif
+
+
+DWORD
+ReplMasterMain(
+ IN LPVOID parm
+ )
+/*++
+
+Routine Description:
+ main master thread.
+
+Arguments:
+ NULL iff service is starting (non-NULL if role is changing)
+
+Return Value:
+ return error value
+
+Threads:
+ Only called by master thread.
+
+--*/
+{
+ NET_API_STATUS ApiStatus;
+ HANDLE WaitHandles[2];
+ DWORD EventTriggered;
+ BOOL ServiceIsStarting;
+
+
+ ServiceIsStarting = (parm == NULL);
+
+ //
+ // init master thread.
+ //
+
+ ApiStatus = InitMaster( ServiceIsStarting );
+ if (ApiStatus != NO_ERROR) {
+
+ ReplMasterCleanup();
+
+ return ((DWORD) ApiStatus);
+ }
+
+ // Note: The code here used to tell the service controller that the
+ // service is running. However, the client half may still be starting,
+ // so it's too soon for that. Let's leave it up to ReplChangeRole().
+
+
+#if DBG
+ RMGlobalMasterThreadInit = TRUE; // only used by ReplTest stuff
+#endif
+
+ // init event array
+ WaitHandles[0] = ReplGlobalMasterTerminateEvent;
+ WaitHandles[1] = RMGlobalMstMSlotHandle;
+
+ for (;;) { // forever
+
+ DWORD BytesRead;
+ DWORD BytesRead2;
+ MASTER_LIST_REC *MasterRec;
+ BYTE Message[ 2 * sizeof(QUERY_MSG) ];
+ QUERY_MSG QueryMsg;
+ OVERLAPPED OverLapped;
+ DWORD type;
+
+
+ BytesRead = BytesRead2 = 0;
+ (void) memset( &OverLapped, '\0', sizeof(OverLapped) );
+ OverLapped.Offset = 0;
+
+ //
+ // read mails from master mail slot
+ //
+
+ if ( !ReadFile(RMGlobalMstMSlotHandle,
+ Message,
+ sizeof(Message),
+ &BytesRead,
+ &OverLapped)) {
+
+ ApiStatus = (NET_API_STATUS) GetLastError();
+
+ if (ApiStatus != ERROR_IO_PENDING) {
+
+ // error in reading mails.
+
+ NetpKdPrint(( PREFIX_REPL_MASTER
+ "Error reading mailslot message, "
+ "stat=" FORMAT_API_STATUS "\n", ApiStatus ));
+
+ AlertLogExit(ALERT_ReplNetErr,
+ NELOG_ReplNetErr,
+ ApiStatus,
+ NULL,
+ NULL,
+ EXIT);
+
+ // terminate master thread
+ break;
+
+ }
+
+ }
+
+ // wait on multiple events ..
+ EventTriggered = WaitForMultipleObjects( 2, WaitHandles, FALSE,
+ (DWORD) -1 );
+
+ if(EventTriggered == 0) {
+ // terminate event
+
+ // Prevent stale use of handle (overlapped use of ex-stack space).
+ (VOID) CloseHandle( RMGlobalMstMSlotHandle );
+ RMGlobalMstMSlotHandle = (HANDLE)-1;
+ RMGlobalMailslotCreated = FALSE;
+
+ break;
+ } else if(EventTriggered == 1) {
+ // mailslot event
+
+ // get BytesRead info
+
+ if( !GetOverlappedResult( RMGlobalMstMSlotHandle,
+ &OverLapped,
+ &BytesRead,
+ TRUE ) ) {
+
+ ApiStatus = (NET_API_STATUS) GetLastError();
+
+ NetpKdPrint(( PREFIX_REPL_MASTER
+ "Repl master is in error "
+ " (" FORMAT_API_STATUS ") reading mailslot \n",
+ ApiStatus ));
+
+ // error read mailslot
+ AlertLogExit( ALERT_ReplNetErr,
+ NELOG_ReplNetErr,
+ ApiStatus,
+ NULL,
+ NULL,
+ NO_EXIT);
+ continue;
+ }
+
+ if(BytesRead == 0) {
+
+ NetpKdPrint(( PREFIX_REPL_MASTER
+ "Received an empty mail.\n" ));
+
+ continue;
+ }
+
+
+ //
+ // Unmarshall message to make it internal format
+ //
+
+ ApiStatus = ReplUnmarshallMessage(
+ Message,
+ BytesRead,
+ (LPBYTE) &QueryMsg,
+ sizeof(QUERY_MSG),
+ &BytesRead2);
+ if (ApiStatus != NO_ERROR) {
+
+ // the unmarshall routine will fail only if the system doesn't
+ // have sufficient memory or the message is bogus...
+
+ NetpKdPrint(( PREFIX_REPL_MASTER
+ "Invalid mailslot message received, "
+ " status=" FORMAT_API_STATUS ".\n", ApiStatus ));
+
+ AlertLogExit( ALERT_ReplNetErr,
+ NELOG_ReplNetErr,
+ ApiStatus,
+ NULL,
+ NULL,
+ NO_EXIT);
+
+ continue;
+ }
+
+
+ //
+ // check if it is an expected sender.
+ //
+
+ if (ReplMasterNameCheck(QueryMsg.header.sender,
+ QueryMsg.header.senders_domain)) {
+ continue;
+ }
+
+ IF_DEBUG(MASTER) { // debug code.
+
+ NetpKdPrint(( PREFIX_REPL_MASTER
+ "Message received, "
+ "type: " FORMAT_DWORD ", from: " FORMAT_LPTSTR
+ ", dir: " FORMAT_LPTSTR "\n",
+ QueryMsg.header.msg_type,
+ QueryMsg.header.sender,
+ QueryMsg.dir_name ));
+ }
+
+
+ switch (QueryMsg.header.msg_type) {
+
+ case IS_DIR_SUPPORTED:
+ case IS_MASTER:
+
+ //
+ // Identical really, both request to know if the reply is
+ // either yes, or no + the last pulser for the directory
+ //
+ // need shared (read) lock to prevent other thread (pulser) from
+ // changing this linked list. Thus in Pulser thread only list
+ // updates grab the semaphore.
+ //
+
+ ACQUIRE_LOCK_SHARED( RMGlobalListLock );
+
+ MasterRec = GetMasterRec(QueryMsg.dir_name);
+
+ RELEASE_LOCK( RMGlobalListLock );
+
+ type = (QueryMsg.header.msg_type == IS_DIR_SUPPORTED) ?
+
+ ( (MasterRec == NULL) ? DIR_NOT_SUPPORTED : DIR_SUPPORTED ) :
+
+ ( (MasterRec == NULL) ? NOT_MASTER_DIR : MASTER_DIR );
+
+ SendMasterMsg(type, &QueryMsg);
+
+ break;
+
+ case DIR_COLLIDE:
+
+ AlertLogExit(ALERT_ReplCannotMasterDir,
+ NELOG_ReplCannotMasterDir,
+ 0,
+ (LPTSTR)QueryMsg.dir_name,
+ (LPTSTR) QueryMsg.header.sender,
+ NO_EXIT);
+
+ break;
+
+
+ default:
+
+ AlertLogExit(ALERT_ReplSysErr,
+ NELOG_ReplBadMsg,
+ 0,
+ NULL,
+ NULL,
+ NO_EXIT);
+
+ } // end switch.
+
+ } else {
+ // error on WaitForMultipleObjects
+
+ NetpKdPrint(( PREFIX_REPL_MASTER
+ "WaitForMultipleObjects() in error, "
+ "EventTriggered = " FORMAT_DWORD "\n", EventTriggered ));
+
+ AlertLogExit( ALERT_ReplNetErr,
+ NELOG_ReplNetErr,
+ EventTriggered,
+ NULL,
+ NULL,
+ EXIT);
+
+ // terminate master thread
+
+ // Prevent stale use of handle (overlapped use of ex-stack space).
+ (VOID) CloseHandle( RMGlobalMstMSlotHandle );
+ RMGlobalMstMSlotHandle = (HANDLE)-1;
+ RMGlobalMailslotCreated = FALSE;
+
+ break;
+ }
+
+ } // end while (FOREVER)
+
+ ReplMasterCleanup();
+
+ IF_DEBUG( REPL ) {
+ NetpKdPrint(( PREFIX_REPL_MASTER
+ "ReplMasterMain: exiting thread "
+ FORMAT_NET_THREAD_ID ".\n", NetpCurrentThread() ));
+ }
+
+ return (NO_ERROR); // BUGBUG: or ApiStatus?
+
+} // ReplMasterMain
+
+
+NET_API_STATUS
+InitMaster(
+ IN BOOL ServiceIsStarting
+ )
+/*++
+
+Routine Description:
+ Initialize master thread.
+
+Arguments:
+ ServiceIsStarting - TRUE iff we're still starting the service.
+
+Return Value:
+
+ NO_ERROR : if successfully initialize master setup.
+ error code : otherwise.
+
+Threads:
+ Only called by master thread.
+
+--*/
+{
+ NET_API_STATUS ApiStatus;
+ DWORD ServiceState;
+ TCHAR MailslotName[FULL_SLOT_NAME_SIZE];
+
+ IF_DEBUG( REPL ) {
+ NetpKdPrint(( PREFIX_REPL_MASTER "InitMaster: thread ID is "
+ FORMAT_NET_THREAD_ID ".\n", NetpCurrentThread() ));
+ }
+
+ RMGlobalCompatibilityMode = TRUE;
+
+ if (ServiceIsStarting) {
+ ServiceState = SERVICE_START_PENDING;
+ } else {
+ ServiceState = SERVICE_RUNNING;
+ }
+
+ ReportStatus(
+ ServiceState,
+ NO_ERROR,
+ REPL_WAIT_HINT,
+ 11 ); // checkpoint
+
+
+ // RMGlobalListLock is now created in main thread, regardless of role.
+
+
+ // NOTE: ReplInitAnyList() assumes caller has config data locked.
+ ACQUIRE_LOCK_SHARED( ReplConfigLock );
+ ApiStatus = ReplInitAnyList(
+ (LPCTSTR) ReplConfigExportList, // uncanon
+ & RMGlobalImportList, // canon list: alloc and set ptr
+ (LPCTSTR) REPL_KEYWORD_EXPLIST, // config keyword name
+ & RMGlobalImportCount ); // set entry count too
+ RELEASE_LOCK( ReplConfigLock );
+ if (ApiStatus != NO_ERROR) {
+
+ return (ApiStatus);
+
+ }
+
+ ReportStatus(
+ ServiceState,
+ NO_ERROR,
+ REPL_WAIT_HINT,
+ 12 ); // checkpoint
+
+#ifdef OLD_SHARE_STUFF
+
+ ACQUIRE_LOCK_SHARED( ReplConfigLock );
+ if( !CreateReplShare(ReplConfigExportPath) ) {
+
+ RELEASE_LOCK( ReplConfigLock );
+ return NERR_InternalError; // BUGBUG: better error code here?
+
+ }
+ RELEASE_LOCK( ReplConfigLock );
+
+ RMGlobalShareCreated = TRUE;
+#endif
+
+ ReportStatus(
+ ServiceState,
+ NO_ERROR,
+ REPL_WAIT_HINT,
+ 13 ); // checkpoint
+
+ //
+ // Creates Master mailsot - where master receives clients inqueries.
+ // Creates mailslot \\.\MAILSLOT\NET\REPL_MAS, if a one exists with
+ // the same name or any other system err, Exits.
+ //
+
+ (void) STRCPY( MailslotName, SLASH_SLASH );
+ (void) STRCAT( MailslotName, DOT );
+ (void) STRCAT( MailslotName, (LPTSTR) MASTER_SLOT_NAME );
+
+ IF_DEBUG( MASTER ) {
+ NetpKdPrint(( PREFIX_REPL_MASTER
+ "InitMaster: creating mailslot " FORMAT_LPTSTR ".\n",
+ MailslotName ));
+ }
+
+ RMGlobalMstMSlotHandle = CreateMailslot(MailslotName,
+ MAX_MSG,
+ (DWORD) MAILSLOT_WAIT_FOREVER, // readtimeout
+ NULL);
+
+ if (RMGlobalMstMSlotHandle == (HANDLE)(-1)) {
+
+ // Find out why mailslot failed. Perhaps wksta not started?
+ if ( !NetpIsServiceStarted( (LPTSTR) SERVICE_WORKSTATION ) ) {
+ ApiStatus = NERR_WkstaNotStarted;
+ } else {
+ ApiStatus = (NET_API_STATUS) GetLastError();
+ }
+ NetpAssert( ApiStatus != NO_ERROR );
+
+ NetpKdPrint(( PREFIX_REPL_MASTER
+ "InitMaster: failed creating mailslot, stat="
+ FORMAT_API_STATUS ".\n", ApiStatus ));
+
+ ReplFinish( ApiStatus );
+ return (ApiStatus);
+ }
+
+ RMGlobalMailslotCreated = TRUE;
+
+
+ ReportStatus(
+ ServiceState,
+ NO_ERROR,
+ REPL_WAIT_HINT,
+ 14 ); // checkpoint
+
+ // init upd_buf that collects update records and
+
+ if ( !InitMsgBuf() ) {
+ return (NERR_InternalError); // BUGBUG: better error value?
+ }
+
+ ReportStatus(
+ ServiceState,
+ NO_ERROR,
+ REPL_WAIT_HINT,
+ 15 ); // checkpoint
+
+ // This also fixes the USERLOCK.* file(s) to match the lock count in
+ // registry.
+ ApiStatus = ExportDirReadMasterList( );
+ if (ApiStatus != NO_ERROR) {
+ return (ApiStatus);
+ }
+
+ ReportStatus(
+ ServiceState,
+ NO_ERROR,
+ REPL_WAIT_HINT,
+ 16 ); // checkpoint
+
+ ApiStatus = CreatePulserThread();
+ if (ApiStatus != NO_ERROR) {
+ return (ApiStatus);
+ }
+
+ RMGlobalPulserThreadCreated = TRUE;
+
+ ReportStatus(
+ ServiceState,
+ NO_ERROR,
+ REPL_WAIT_HINT,
+ 17 ); // checkpoint
+
+ (void) ResumeThread(RMGlobalPulserThreadHandle);
+
+ // And, absolutely last, tell ReplChangeRole (via ExportDirStartRepl)
+ // that we've initialized OK.
+
+ IF_DEBUG( MASTER ) {
+ NetpKdPrint(( PREFIX_REPL_MASTER
+ "InitMaster----------> setting startup event\n" ));
+ }
+
+ if (!SetEvent(ReplGlobalExportStartupEvent)) {
+
+ ApiStatus = (NET_API_STATUS) GetLastError();
+ NetpAssert( ApiStatus != NO_ERROR );
+ NetpAssert( ApiStatus != ERROR_INVALID_FUNCTION );
+ AlertLogExit(ALERT_ReplSysErr,
+ NELOG_ReplSysErr,
+ ApiStatus,
+ NULL,
+ NULL,
+ EXIT);
+
+ return (ApiStatus);
+ }
+
+ // That's all, folks!
+ return (NO_ERROR);
+
+}
+
+
+#ifdef OLD_SHARE_STUFF
+BOOL
+CreateReplShare(
+ IN LPTSTR ExportPath
+ )
+/*++
+
+Routine Description:
+
+ Takes care of Master's export path.
+
+Arguments:
+
+ ExportPath - user specified (or lanman.ini) EXPORT path.
+
+Return Value:
+
+ TRUE : if successfully create master repl share
+ FALSE : otherwise.
+
+Threads:
+
+ Only called by master thread.
+
+--*/
+{
+ SHARE_INFO_2 ShareInfo2;
+ NET_API_STATUS NetStatus;
+
+ //
+ // Do the NetShareAdd.
+ //
+
+ ShareInfo2.shi2_netname = RMGlobalReplShare;
+ ShareInfo2.shi2_path = ExportPath;
+ ShareInfo2.shi2_remark = RMGlobalShareComment;
+
+ ShareInfo2.shi2_type = STYPE_DISKTREE;
+ ShareInfo2.shi2_permissions = ACCESS_READ | ACCESS_ATRIB;
+ ShareInfo2.shi2_max_uses = (DWORD) SHI_USES_UNLIMITED;
+ ShareInfo2.shi2_current_uses = 0; // N/A
+ ShareInfo2.shi2_passwd = NULL; // N/A
+
+ NetStatus = NetShareAdd(NULL, 2, (LPBYTE)&ShareInfo2, NULL);
+
+ IF_DEBUG(MASTER) {
+ NetpKdPrint(( PREFIX_REPL_MASTER
+ "CreateReplShare: NetShareAdd returned "
+ FORMAT_API_STATUS ".\n", NetStatus ));
+ }
+
+ if (NetStatus != NO_ERROR) {
+ if (NetStatus != NERR_DuplicateShare) {
+
+ //
+ // if can't create share just shutdown.
+ //
+
+ ReplFinish( NetStatus );
+
+ return FALSE;
+
+ } else {
+
+ //
+ // if can't create because share exists delete and try again.
+ //
+
+ NetStatus = NetShareDel(NULL, RMGlobalReplShare, 0L);
+ if(NetStatus) {
+ ReplFinish( NetStatus );
+
+ return FALSE;
+
+ } else if (NetStatus = NetShareAdd(NULL,
+ 2,
+ (LPBYTE)&ShareInfo2,
+ NULL)) {
+
+ NetpKdPrint(( PREFIX_REPL_MASTER
+ "CreateReplShare is in trouble "
+ "calling NetShareAdd, NetStatus = " FORMAT_API_STATUS
+ "\n", NetStatus ));
+ ReplFinish( NetStatus );
+
+ return FALSE;
+ }
+ }
+
+ }
+
+ return TRUE;
+
+} // end of CreateReplShare
+#endif
+
+
+
+NET_API_STATUS
+CreatePulserThread(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ Creates Pulser thread.
+
+ stack size MASTER_PULSER_STACK_SIZE.
+
+Arguments:
+
+ none
+
+Return Value:
+
+ NO_ERROR - succesful; RMGlobalPulserThreadHandle is set.
+ error code otherwise.
+
+Threads:
+ Only called by master thread.
+
+--*/
+{
+ RMGlobalPulserThreadHandle = CreateThread(NULL,
+ MASTER_PULSER_STACK_SIZE,
+ PulserThread,
+ NULL,
+ CREATE_SUSPENDED,
+ &RMGlobalPulserTID);
+
+ if (RMGlobalPulserThreadHandle == NULL) {
+
+ NET_API_STATUS ApiStatus = (NET_API_STATUS) GetLastError();
+
+ NetpKdPrint(( PREFIX_REPL_MASTER
+ "Repl master can't create PULSER thread,"
+ " api status " FORMAT_API_STATUS ".\n", ApiStatus ));
+
+ NetpAssert( ApiStatus != NO_ERROR );
+ ReplFinish( ApiStatus );
+
+ return (ApiStatus);
+ }
+
+ return (NO_ERROR);
+}
+
+
+
+VOID
+ReplMasterCleanup(
+ VOID
+ )
+/*++
+
+Routine Description:
+ gets rid of all resources owned master thread
+ called from master thread just before returning from main master thread
+ procedure.
+
+Arguments:
+ none
+
+Return Value:
+ none
+
+--*/
+{
+ IF_DEBUG( MASTER ) {
+ NetpKdPrint(( PREFIX_REPL_MASTER
+ "ReplMasterCleanup: cleaning up...\n" ));
+ }
+
+ if( RMGlobalPulserThreadCreated ) {
+
+ //
+ // wait for pulser thread to terminate
+ //
+
+ // BUGBUG: This was GRACE_TIMEOUT. Why was this handled differently
+ // than the other thread? Also, I think this caused a problem in
+ // debug build where pulser was scanning large directory structure
+ // with lots of debug output. (GRACE_TIMEOUT has a value which gives
+ // 10 seconds of delay.) --JR
+ (VOID) WaitForSingleObject( RMGlobalPulserThreadHandle, INFINITE );
+
+ // close pulser thread handle
+ (VOID) CloseHandle(RMGlobalPulserThreadHandle);
+
+ RMGlobalPulserThreadCreated = FALSE;
+ }
+
+
+ if (RMGlobalMailslotCreated) {
+ (VOID) CloseHandle(RMGlobalMstMSlotHandle);
+ RMGlobalMstMSlotHandle = (HANDLE)-1;
+ RMGlobalMailslotCreated = FALSE;
+ }
+
+#ifdef OLD_SHARE_STUFF
+ if (RMGlobalShareCreated) {
+ NetShareDel(NULL, RMGlobalReplShare, 0);
+ }
+#endif
+
+ // clean client list memory
+
+ if( RMGlobalImportCount != 0 ) {
+
+ NetpMemoryFree( RMGlobalImportList[0] );
+
+ }
+
+}
+
+
+
+VOID
+SendMasterMsg(
+ IN DWORD msg_type,
+ IN PQUERY_MSG msg_rcvd
+ )
+/*++
+
+Routine Description:
+ Constructs and sends a reply message.
+
+Arguments:
+ msg_type- Goes in msg header and determines msg contents.
+ msg - msg replying to ( has destintaion and dir_name).
+
+Return Value:
+ none. Always return even if MailSlotWrite failed.
+
+Threads:
+ Only called by master thread.
+
+--*/
+{
+
+ NET_API_STATUS ApiStatus;
+ CHAR MessageBuf[sizeof(QUERY_MSG)];
+ CHAR MarshalledMsgBuf[2 * sizeof(QUERY_MSG)];
+ DWORD MarshalledMsgBufSize = 0;
+
+ PQUERY_MSG OurReplyMessage;
+ TCHAR DestSlotName[FULL_SLOT_NAME_SIZE];
+
+ OurReplyMessage = (PVOID) MessageBuf;
+
+ OurReplyMessage->header.msg_type = msg_type;
+ (void) STRCPY(
+ OurReplyMessage->header.sender,
+ ReplGlobalComputerName);
+ (void) STRCPY(
+ OurReplyMessage->header.senders_domain,
+ ReplGlobalDomainName);
+
+ (void) STRCPY(OurReplyMessage->dir_name, msg_rcvd->dir_name);
+
+ //
+ // stick in leading double slashes for computer name.
+ //
+ NetpAssert( (msg_rcvd->header.sender) != NULL );
+ NetpAssert( (*(msg_rcvd->header.sender)) != TCHAR_EOS );
+
+ (void) STRCPY(DestSlotName, SLASH_SLASH);
+ (void) STRCAT(DestSlotName, msg_rcvd->header.sender);
+ (void) STRCAT(DestSlotName, (LPTSTR) CLIENT_SLOT_NAME);
+
+ ApiStatus = ReplMarshallQueryMsg(
+ MessageBuf,
+ MarshalledMsgBuf,
+ &MarshalledMsgBufSize );
+ if (ApiStatus != NO_ERROR) {
+
+ NetpAssert( ApiStatus != ERROR_INVALID_FUNCTION );
+
+ // the marshall routine will fail only iff the system doesn't
+ // have sufficient memory
+
+ AlertLogExit(ALERT_ReplSysErr,
+ NELOG_ReplNetErr,
+ ApiStatus,
+ NULL,
+ NULL,
+ NO_EXIT);
+ }
+ NetpAssert( MarshalledMsgBufSize != 0 );
+
+ ApiStatus = NetpReplWriteMail( DestSlotName,
+ MarshalledMsgBuf,
+ MarshalledMsgBufSize );
+
+ if (ApiStatus != NO_ERROR) {
+
+ NetpKdPrint(( PREFIX_REPL_MASTER
+ "SendMasterMsg() can't successfully write "
+ "mailslot message to " FORMAT_LPTSTR ", status "
+ FORMAT_API_STATUS "\n", DestSlotName, ApiStatus ));
+
+ NetpAssert( ApiStatus != NO_ERROR );
+ NetpAssert( ApiStatus != ERROR_INVALID_FUNCTION );
+
+ AlertLogExit(ALERT_ReplSysErr,
+ NELOG_ReplNetErr,
+ ApiStatus,
+ NULL,
+ NULL,
+ NO_EXIT);
+ }
+
+} // end of SendMasterMsg
+
+
+BOOL
+ReplMasterNameCheck(
+ IN LPTSTR Sender,
+ IN LPTSTR Domain
+ )
+/*++
+
+Routine Description:
+ Check to determine that the incoming message should be accepted.
+
+Arguments:
+ Sender - sender's machine name.
+ Domain - sender's (primary) domain name.
+
+Return Value:
+ RETURNS FALSE - if valid, TRUE - otherwise.
+
+Threads:
+ Only called by master thread.
+
+--*/
+{
+
+ DWORD i;
+ BOOL Result = FALSE;
+
+ ACQUIRE_LOCK_SHARED( ReplConfigLock );
+
+ if (RMGlobalImportCount != 0) {
+ for (i = 0; i < RMGlobalImportCount; i++) {
+ if (!ReplNetNameCompare(
+ NULL,
+ Sender,
+ RMGlobalImportList[i],
+ NAMETYPE_COMPUTER,
+ 0L) ||
+ !ReplNetNameCompare(
+ NULL,
+ Domain,
+ RMGlobalImportList[i],
+ NAMETYPE_DOMAIN,
+ 0L))
+ {
+ Result = TRUE;
+ break;
+ }
+ }
+ } else if (ReplNetNameCompare(
+ NULL,
+ ReplGlobalDomainName,
+ Domain,
+ NAMETYPE_DOMAIN,
+ 0L)) { // client_count == 0 - send to domain name
+ Result = TRUE;
+ }
+
+ RELEASE_LOCK( ReplConfigLock );
+ return Result;
+
+} // ReplMasterNameCheck
diff --git a/private/net/svcdlls/repl/server/master.h b/private/net/svcdlls/repl/server/master.h
new file mode 100644
index 000000000..63492afdf
--- /dev/null
+++ b/private/net/svcdlls/repl/server/master.h
@@ -0,0 +1,133 @@
+/*++
+
+Copyright (c) 1987-1993 Microsoft Corporation
+
+Module Name:
+ master.h
+
+Abstract:
+ Constants and some global data definition.
+
+Author:
+ Ported from Lan Man 2.x
+
+Environment:
+ Contains NT-specific code.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+ 10/07/91 (madana)
+ ported to NT. Converted to NT style.
+ 19-Dec-1991 JohnRo
+ Deleted unused (and incorrect) MAX_SHARE equate.
+ 26-Dec-1991 JohnRo
+ Added equate for unguarded value of master_list_rec.grd_count.
+ 20-Jan-1992 JohnRo
+ More changes suggested by PC-LINT.
+ Added global flag for ReplTest use.
+ Changed to use NetLock.h (allow shared locks, for one thing).
+ Added lockcount and time_of_first_lock fields.
+ Added RMGlobalMasterListHeader (was master_list_header) and
+ RMGlobalMasterListCount.
+ 24-Jan-1992 JohnRo
+ Changed to use LPTSTR etc.
+ 10-Feb-1992 JohnRo
+ Added lock for master's client list (RMGlobalClientList).
+ 24-Mar-1992 JohnRo
+ Renamed many ReplGlobal vars to ReplConfig vars.
+ 25-Oct_1992 jimkel
+ renamed RMGlobalClient* to RMGlobalImport*
+ removed RMGlobalClientListLock now using ReplConfigLock
+ 05-Jan-1993 JohnRo
+ Repl WAN support (get rid of repl name list limits).
+ 04-Mar-1993 JohnRo
+ RAID 7988: downlevel repl importer might not see lock file
+ for new first-level dir.
+
+--*/
+
+
+#ifndef _MASTER_
+#define _MASTER_
+
+
+#include <netlock.h> // LPNET_LOCK.
+
+
+// C O N S T A N T S - DEFINES
+
+#define MAX_MSG sizeof(QUERY_MSG)
+#define MASTER_SLOT_SIZE (MAX_MSG * 6)
+
+#define SHARE_COMMENT TEXT("REPL MASTER")
+
+#define MASTER_PULSER_STACK_SIZE 12268 // 12K
+
+#define MASTER_GUARD_NOT_NEEDED ( (DWORD) -1 )
+
+
+// S T R U C T U R E S
+
+// MASTER LIST:
+
+struct master_list_rec {
+
+ TCHAR dir_name[PATHLEN]; // TCHAR dir/tree name.
+ DWORD integrity; // Integrity: FILE / TREE.
+ DWORD extent; // Extent: TREE / DIRECTORY.
+ DWORD timestamp; // date+time of last update.
+ DWORD checksum; // f(ForEachFile(name, timestamp))
+ DWORD count; // # of files in dir/tree.
+ DWORD grd_checksum; // guarded checksum.
+
+ DWORD grd_count; // Guarded count, plays the role of guard
+ // flag, when == MASTER_GUARD_NOT_NEEDED
+ // no guard is needed.
+
+ BOOL exists; // used to check for deleted dirs.
+
+ BOOL locks_fixed; // Have user locks been fixed for this?
+
+ DWORD lockcount; // Number of locks for this dir.
+ DWORD time_of_first_lock; // First lock time (seconds since 1970).
+
+ struct master_list_rec *next_p;
+ struct master_list_rec *prev_p;
+};
+
+typedef struct master_list_rec MASTER_LIST_REC;
+typedef struct master_list_rec * PMASTER_LIST_REC;
+typedef struct master_list_rec * LPMASTER_LIST_REC;
+
+// E X T E R N A L S
+
+extern DWORD RMGlobalCompatibilityMode;
+
+extern LPMASTER_LIST_REC RMGlobalMasterListHeader; // locked by RMGlobalListLock
+extern DWORD RMGlobalMasterListCount; // locked by RMGlobalListLock
+extern LPNET_LOCK RMGlobalListLock;
+
+extern LPTSTR *RMGlobalImportList; // Locked by ReplConfigLock
+extern DWORD RMGlobalImportCount; // (ditto)
+
+extern DWORD RMGlobalPulserTID; // PulserThread tid.
+extern HANDLE RMGlobalPulserThreadHandle;
+
+#if DBG
+extern BOOL RMGlobalMasterThreadInit; // only used by ReplTest stuff
+#endif
+
+
+
+// F U N C T I O N S
+
+//
+// master.c
+//
+
+NET_API_STATUS
+InitClientList(
+ VOID
+ );
+
+#endif // _MASTER_
diff --git a/private/net/svcdlls/repl/server/mksecatt.c b/private/net/svcdlls/repl/server/mksecatt.c
new file mode 100644
index 000000000..303799fd6
--- /dev/null
+++ b/private/net/svcdlls/repl/server/mksecatt.c
@@ -0,0 +1,258 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ MkSecAtt.c
+
+Abstract:
+
+ ReplMakeSecurityAttributes() creates a security descriptor containing:
+
+ - the security (including ACLs, owner, group)
+
+ This is used by ReplCopyFile() and ReplCopyDirectoryItself().
+
+Author:
+
+ John Rogers (JohnRo) 08-Apr-1993
+
+Environment:
+
+ User mode only. Uses Win32 APIs.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+ Tab size is set to 4.
+
+Revision History:
+
+ 08-Apr-1993 JohnRo
+ Created for RAID 1938: Replicator un-ACLs files when not given
+ enough permission.
+ 04-Jun-1993 JohnRo
+ RAID 12473: Changed to handle ReplMakeFileSecurity not supported on
+ downlevel exporter.
+ 10-May-1995 JonN
+ RAID 11829: Clients of ReplMakeSecurityAttributes free DestSecurityAttr
+ but not DestSecurityAttr->lpSecurityDescriptor. Changed allocations
+ to place lpSecurityDescriptor in the same memory block as
+ DestSecurityAttr.
+
+--*/
+
+
+// These must be included first:
+
+#include <windows.h> // IN, LPTSTR, PSECURITY_ATTRIBUTES, etc.
+#include <lmcons.h> // NET_API_STATUS, PATHLEN, etc.
+
+// These may be included in any order:
+
+#include <lmerr.h> // NERR_InternalError, NO_ERROR.
+#include <netdebug.h> // NetpKdPrint(), FORMAT_ equates, etc.
+#include <netlib.h> // NetpMemoryAllocate(), etc.
+#include <prefix.h> // PREFIX_ equates.
+#include <repldefs.h> // IF_DEBUG(), my prototype, USE_ equates, etc.
+#include <tstr.h> // TCHAR_EOS, STRLEN().
+
+#define REPL_ROUND_UP(x) (sizeof(DWORD) * (((x) + sizeof(DWORD) - 1) / sizeof(DWORD)))
+#define BASE_SECURITY_ATTR_SIZE REPL_ROUND_UP(sizeof( SECURITY_ATTRIBUTES ))
+
+
+
+NET_API_STATUS
+ReplMakeSecurityAttributes(
+ IN LPCTSTR SourcePath,
+ OUT PSECURITY_ATTRIBUTES * DestSecurityAttrPtr // alloc and set ptr
+ )
+
+/*++
+
+Routine Description:
+
+ ReplMakeSecurityAttributes builds an security descriptor in memory.
+ It is make to be a clone of the source file or directory.
+
+Arguments:
+
+ SourcePath - Points to the path of a file or directory to be used as
+ the original security info (ACL, owner, group). This path is
+ typically a UNC name, for instance:
+
+ \\server\REPL$\dir\file
+
+ This routine assumes that the source file or directory exists.
+
+ DestSecurityAttrPtr - Points to a pointer which be filled-in by this
+ routine. (This routine will allocate memory; the caller must free
+ with NetpMemoryFree or equivalent.) On error, this will be set to
+ NULL.
+
+Return Value:
+
+ NET_API_STATUS
+
+ Note that ERROR_NOT_SUPPORTED is a reasonable value to return for
+ downlevel exporters.
+
+Threads:
+
+ Used by client and syncer threads.
+
+--*/
+
+{
+ NET_API_STATUS ApiStatus;
+ PSECURITY_ATTRIBUTES DestSecurityAttr = NULL;
+
+#ifdef USE_UNC_GETFILESEC
+ PSECURITY_DESCRIPTOR SourceSecurityDesc = NULL;
+ DWORD SourceSecurityDescSize;
+#endif
+
+ //
+ // Check for caller errors.
+ //
+
+ if (SourcePath == NULL) {
+ ApiStatus = ERROR_INVALID_PARAMETER;
+ goto Cleanup;
+ }
+ if ((*SourcePath) == TCHAR_EOS) {
+ ApiStatus = ERROR_INVALID_PARAMETER;
+ goto Cleanup;
+ }
+ if (STRLEN(SourcePath) > PATHLEN) {
+ ApiStatus = ERROR_INVALID_PARAMETER;
+ goto Cleanup;
+ }
+
+#ifdef USE_UNC_GETFILESEC
+
+ //
+ // Find out how much memory we need for source security attributes.
+ //
+
+ if (!GetFileSecurity(
+ (LPTSTR) SourcePath,
+ REPL_SECURITY_TO_COPY, // requested info flags
+ NULL, // don't have a buffer yet
+ 0, // size of sec desc buffer so far
+ & SourceSecurityDescSize // size needed
+ )) {
+
+ // GetFileSecurity failed. Why?
+ ApiStatus = (NET_API_STATUS) GetLastError();
+ NetpAssert( ApiStatus != NO_ERROR );
+
+ if (ApiStatus == ERROR_INSUFFICIENT_BUFFER) {
+ NetpAssert(
+ SourceSecurityDescSize > sizeof(SECURITY_DESCRIPTOR) );
+ } else if ( (ApiStatus==ERROR_NOT_SUPPORTED)
+ || (ApiStatus==ERROR_INVALID_PARAMETER) ) {
+
+ // Just downlevel master, so set default security and continue.
+ ApiStatus = ERROR_NOT_SUPPORTED;
+ goto Cleanup;
+
+ } else {
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplMakeSecurityAttributes: GetFileSecurity(1st) failed "
+ FORMAT_API_STATUS ".\n", ApiStatus ));
+ goto Cleanup;
+ }
+ } else {
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplMakeSecurityAttributes: GetFileSecurity claims to have "
+ "worked with 0 size and NULL pointer!\n" ));
+ ApiStatus = NERR_InternalError;
+ goto Cleanup;
+ }
+
+ //
+ // Allocate memory for both the destination and source security attributes
+ // structures. The source security structure is at the end of the same
+ // memory block.
+ //
+
+ DestSecurityAttr = NetpMemoryAllocate(
+ BASE_SECURITY_ATTR_SIZE + SourceSecurityDescSize );
+ if (DestSecurityAttr == NULL) {
+ ApiStatus = ERROR_NOT_ENOUGH_MEMORY;
+ goto Cleanup;
+ }
+
+ SourceSecurityDesc = (PSECURITY_DESCRIPTOR)
+ (((ULONG)DestSecurityAttr)+BASE_SECURITY_ATTR_SIZE);
+ DestSecurityAttr->lpSecurityDescriptor = SourceSecurityDesc;
+
+ if (!GetFileSecurity(
+ (LPTSTR) SourcePath,
+ REPL_SECURITY_TO_COPY, // requested info flags
+ SourceSecurityDesc,
+ SourceSecurityDescSize, // size of sec desc buffer
+ & SourceSecurityDescSize // size needed
+ )) {
+
+ // GetFileSecurity failed again! Why?
+ ApiStatus = (NET_API_STATUS) GetLastError();
+ NetpAssert( ApiStatus != NO_ERROR );
+
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplMakeSecurityAttributes: GetFileSecurity(2nd) failed "
+ FORMAT_API_STATUS ".\n", ApiStatus ));
+
+ goto Cleanup;
+ }
+
+ //
+ // Build security attributes so that directory we create will
+ // have the right security (ACLs, owner, group).
+ //
+
+ DestSecurityAttr->nLength = sizeof(SECURITY_ATTRIBUTES);
+ DestSecurityAttr->bInheritHandle = FALSE;
+
+
+ ApiStatus = NO_ERROR;
+
+#else // not USE_UNC_GETFILESEC
+
+ BUGBUG; // how do we do this without UNC GetFileSecurity?
+ ApiStatus = ERROR_NOT_SUPPORTED;
+
+#endif // not USE_UNC_GETFILESEC
+
+
+
+Cleanup:
+
+ //
+ // Set vars for caller.
+ //
+
+ if (ApiStatus == NO_ERROR) {
+ *DestSecurityAttrPtr = DestSecurityAttr;
+ } else {
+ *DestSecurityAttrPtr = NULL;
+
+ if (DestSecurityAttr != NULL) {
+ NetpMemoryFree( DestSecurityAttr );
+ }
+
+ } // error occurred.
+
+ // Note that ERROR_NOT_SUPPORTED is a reasonable value to return for
+ // downlevel exporters.
+
+ IF_DEBUG( SYNC ) {
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplMakeSecurityAttributes: returning status of "
+ FORMAT_API_STATUS " reading security from '"
+ FORMAT_LPTSTR "'.\n",
+ ApiStatus, SourcePath ));
+ }
+
+ return (ApiStatus);
+
+}
diff --git a/private/net/svcdlls/repl/server/parse.c b/private/net/svcdlls/repl/server/parse.c
new file mode 100644
index 000000000..debc630bd
--- /dev/null
+++ b/private/net/svcdlls/repl/server/parse.c
@@ -0,0 +1,1360 @@
+#if 0 // Entire file is obsolete. --JR, 28-Jan-1992
+
+/*++
+
+Copyright (c) 1987-92 Microsoft Corporation
+
+Module Name:
+ parse.c
+
+Abstract:
+ Contains function Parse, that parses and validates all service parms.
+
+Author:
+ Ported from Lan Man 2.x
+
+Environment:
+ User mode only.
+ Contains NT-specific code.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+ Tab size is set to 4.
+
+Revision History:
+ 03/22/89 (yuv)
+ initila coding
+
+ 10/28/91 (madana)
+ ported to NT. Converted to NT style.
+ 20-Jan-1992 JohnRo
+ Avoid using private logon functions.
+ Added config list lock.
+ Removed support for lanman dir switch and/or config keyword.
+ The tryuser variable should be treated as a BOOL.
+ Changed to use my config.h helpers.
+ Renamed wcstol() to wtol().
+ Added some debug output.
+ Use SECT_NT_REPLICATOR in config.h instead of REPL_SECTION.
+ Made changes suggested by PC-LINT.
+ 23-Jan-1992 JohnRo
+ Added ReplConfigReportBadParmValue().
+ We're updating in-memory copy of config data, so we need exclusive
+ lock on ReplGlobalConfigLock.
+ Moved current role from P_repl_sw to ReplGlobalRole.
+ Use REPL_ROLE_ equates just like the APIs do.
+ Made just about everything DBGSTATIC.
+
+
+--*/
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windef.h> // MAX_PATH, etc.
+#include <winbase.h>
+
+#include <lmcons.h>
+#include <netdebug.h> // NetpKdPrint(()), etc. (Needed by config.h)
+
+#include <config.h> // NetpConfig routines, SECT_NT_REPLICATOR, etc.
+#include <netlib.h>
+#include <netlock.h> // ACQUIRE_LOCK(), etc.
+#include <lmerr.h> // NO_ERROR, ERROR_ and NERR_ equates.
+#include <lmerrlog.h>
+#include <lmrepl.h> // REPL_ROLE_ equates.
+#include <lmsvc.h>
+#include <icanon.h>
+#include <tstring.h> // NetpAlloc{type}From{type}(), wtol(), etc.
+#include <stdlib.h>
+
+#include <replconf.h> // ReplGlobalConfigLock, ReplConfigReportBadParmValue().
+#include <repldefs.h>
+#include <iniparm.h>
+#include <wcslocal.h>
+
+DBGSTATIC LPTSTR rep_REPL = SW_REPL_REPL;
+DBGSTATIC LPTSTR rep_EXPPATH = SW_REPL_EXPPATH;
+DBGSTATIC LPTSTR rep_IMPPATH = SW_REPL_IMPPATH;
+DBGSTATIC LPTSTR rep_EXPLIST = SW_REPL_EXPLIST;
+DBGSTATIC LPTSTR rep_IMPLIST = SW_REPL_IMPLIST;
+DBGSTATIC LPTSTR rep_TRYUSER = SW_REPL_TRYUSER;
+DBGSTATIC LPTSTR rep_LOGON = SW_REPL_LOGON;
+DBGSTATIC LPTSTR rep_PASSWD = SW_REPL_PASSWD;
+DBGSTATIC LPTSTR rep_SYNC = SW_REPL_SYNCH;
+DBGSTATIC LPTSTR rep_PULSE = SW_REPL_PULSE;
+DBGSTATIC LPTSTR rep_GUARD = SW_REPL_GUARD;
+DBGSTATIC LPTSTR rep_RANDOM = SW_REPL_RANDOM;
+
+DBGSTATIC LPWSTR def_P_repl = DEFAULT_REPL;
+DBGSTATIC LPWSTR def_P_exppath = DEFAULT_EXPPATH;
+DBGSTATIC LPWSTR def_P_imppath = DEFAULT_IMPPATH;
+DBGSTATIC LPWSTR def_P_explist = DEFAULT_EXPLIST;
+DBGSTATIC LPWSTR def_P_implist = DEFAULT_IMPLIST;
+DBGSTATIC LPWSTR def_P_logon = DEFAULT_LOGON;
+DBGSTATIC LPWSTR def_P_passwd = DEFAULT_PASSWD;
+
+//
+// The following items are initialized at program start.
+// They are also locked by ReplGlobalConfigLock.
+//
+
+LPWSTR P_repl = DEFAULT_REPL;
+LPWSTR P_exppath = DEFAULT_EXPPATH;
+LPWSTR P_imppath = DEFAULT_IMPPATH;
+LPWSTR P_explist = DEFAULT_EXPLIST;
+LPWSTR P_implist = DEFAULT_IMPLIST;
+LPWSTR P_logon = DEFAULT_LOGON;
+LPWSTR P_passwd = DEFAULT_PASSWD;
+
+BOOL P_tryuser = DEFAULT_TRYUSER;
+DWORD P_sync = DEFAULT_SYNC;
+DWORD P_pulse = DEFAULT_PULSE;
+DWORD P_guard = DEFAULT_GUARD;
+DWORD P_random = DEFAULT_RANDOM;
+
+DWORD ReplGlobalRole = REPL_ROLE_IMPORT;
+
+// End of items locked by ReplGlobalConfigLock.
+
+
+// prototypes
+
+DBGSTATIC NET_API_STATUS
+Parse2(
+ IN DWORD argc,
+ IN LPTSTR argv[],
+ IN LPNET_CONFIG_HANDLE ConfigHandle
+ );
+
+DBGSTATIC NET_API_STATUS
+GetParameter(
+ IN DWORD argc,
+ IN LPTSTR argv[],
+ IN LPNET_CONFIG_HANDLE ConfigHandle,
+ IN LPTSTR SwitchName,
+ IN OUT LPWSTR *ParmValue
+ );
+
+DBGSTATIC NET_API_STATUS
+GetCmdValue(
+ IN DWORD argc,
+ IN LPTSTR argv[],
+ IN LPTSTR SwitchName,
+ IN OUT LPWSTR *ParmValue
+ );
+
+DBGSTATIC NET_API_STATUS
+PathCheck(
+ IN OUT LPWSTR * Path,
+ IN DWORD TypeWanted,
+ IN LPTSTR SwitchName
+ );
+
+DBGSTATIC NET_API_STATUS
+ListCheck(
+ IN LPWSTR list,
+ IN LPTSTR SwitchName
+ );
+
+DBGSTATIC NET_API_STATUS
+DwordCheck(
+ IN LPWSTR StrValue OPTIONAL,
+ IN DWORD Min,
+ IN DWORD Max,
+ IN LPTSTR SwitchName,
+ IN OUT LPDWORD DwordValue
+ );
+
+DBGSTATIC NET_API_STATUS
+YesNoCheck(
+ IN LPWSTR StrValue OPTIONAL,
+ IN LPTSTR SwitchName,
+ IN OUT LPBOOL Value
+ );
+
+DBGSTATIC NET_API_STATUS
+NameCheck(
+ IN OUT LPWSTR * Name,
+ IN DWORD Type,
+ IN LPTSTR SwitchName
+ );
+
+NET_API_STATUS
+Parse(
+ IN DWORD argc,
+ IN LPTSTR argv[]
+ )
+/*++
+
+Routine Description :
+
+ Wrapper function of the real worker Parse2 function.
+
+Arguments :
+ argc : argument count
+ argv : argument string array pointer.
+
+Return Value :
+ return NO_ERROR if successfully parse parameter
+ ERROR_INVALID_PARAMETER, otherwise
+
+--*/
+{
+ NET_API_STATUS ApiStatus;
+ LPNET_CONFIG_HANDLE ConfigHandle;
+
+ // Get read-write lock for config data. (The lock is for the in-memory
+ // version of the data, which we're going to set.)
+ ACQUIRE_LOCK( ReplGlobalConfigLock );
+
+ // Open the config file (nt.cfg)
+ ApiStatus = NetpOpenConfigData(
+ & ConfigHandle,
+ NULL, // local machine
+ SECT_NT_REPLICATOR, // section name
+ TRUE); // read-only
+
+
+ if (ApiStatus != NO_ERROR) {
+ NetpKdPrint(( "[REPL] Parse() can't open config section.\n" ));
+ ReplFinish(
+ SERVICE_UIC_CODE(
+ SERVICE_UIC_SYSTEM,
+ ApiStatus),
+ NULL);
+
+ RELEASE_LOCK( ReplGlobalConfigLock );
+ return( ApiStatus );
+ }
+
+
+ ApiStatus = Parse2(argc, argv, ConfigHandle);
+
+ // close config file ..
+
+ (void) NetpCloseConfigData( ConfigHandle );
+
+ RELEASE_LOCK( ReplGlobalConfigLock );
+
+ return( ApiStatus );
+}
+
+DBGSTATIC NET_API_STATUS
+Parse2(
+ IN DWORD argc,
+ IN LPTSTR argv[],
+ IN LPNET_CONFIG_HANDLE ConfigHandle
+ )
+/*++
+
+Routine Description :
+ parse command line arguments, range-check them and set global
+ variables.
+
+ parameter units range/value default
+ --------- ----- ----------- -------
+
+ /REPLICATE - import/export/both REPL_ROLE_IMPORT
+ /EXPORTPATH pathname - repl\export
+ /IMPORTPATH pathname - repl\import
+ /EXPORTLIST names 0-32 0
+ /IMPORTLIST srvnames 0-32 0
+ /TRYUSER - yes/no YES
+ /LOGON username - -
+ /PASSWORD password - -
+ /INTERVAL minutes 1-60 5
+ /PULSE integer 1-10 3
+ /GUARDTIME minutes 0 to (interval/2) 2
+ /RANDOM seconds 1-120 60
+
+Arguments :
+ argc : argument count
+ argv : argument string array pointer.
+ ConfigHandle : config handle for replicator section.
+
+Return Value :
+ return NO_ERROR if successfully parse parameter
+ ERROR_INVALID_PARAMETER, on syntax error
+ and so on...
+
+--*/
+{
+
+ NET_API_STATUS ApiStatus;
+ LPWSTR ParmStrValue;
+
+ // get and check common parameter to master and client.
+
+ // /REPL switch
+
+ ApiStatus = GetParameter(argc,
+ argv,
+ ConfigHandle,
+ rep_REPL,
+ &P_repl );
+ if (ApiStatus != NO_ERROR) {
+ return( ApiStatus );
+ }
+
+ if(P_repl[0] == L'\0') {
+
+ ReplConfigReportBadParmValue( rep_REPL, NULL );
+
+ return( ERROR_INVALID_PARAMETER );
+ }
+
+ if(_wcsicmp(P_repl, BOTH_SW) == 0) {
+ ReplGlobalRole = REPL_ROLE_BOTH;
+ }
+ else if(_wcsicmp(P_repl, EXPORT_SW) == 0) {
+ ReplGlobalRole = REPL_ROLE_EXPORT;
+ }
+ else if(_wcsicmp(P_repl, IMPORT_SW) == 0) {
+ ReplGlobalRole = REPL_ROLE_IMPORT;
+ }
+ else {
+
+ ReplConfigReportBadParmValue( rep_REPL, NULL );
+
+ return( ERROR_INVALID_PARAMETER );
+ }
+
+ IF_DEBUG(REPL) { // debug code
+
+ NetpKdPrint(( "[Repl] Repl parameter \n"));
+ NetpKdPrint((" REPL = %ws \n", P_repl));
+
+ }
+
+ // get and check repl master parameters
+
+ if (ReplRoleIncludesMaster( ReplGlobalRole )) {
+
+ IF_DEBUG(REPL) { // debug code
+
+ NetpKdPrint(( "[Repl] Repl master parameters \n"));
+
+ }
+
+ // EXPORTPATH
+ ApiStatus = GetParameter(argc,
+ argv,
+ ConfigHandle,
+ rep_EXPPATH,
+ &P_exppath );
+ if (ApiStatus != NO_ERROR) {
+ return( ApiStatus );
+ }
+
+ ApiStatus = PathCheck(&P_exppath, ITYPE_PATH_ABSD, rep_EXPPATH);
+ if (ApiStatus != NO_ERROR) {
+ return( ApiStatus );
+ }
+
+ IF_DEBUG(REPL) { // debug code
+
+ NetpKdPrint((" EXPORT PATH = %ws \n", P_exppath));
+
+ }
+
+ // EXPORTLIST
+ ApiStatus = GetParameter(argc,
+ argv,
+ ConfigHandle,
+ rep_EXPLIST,
+ &P_explist );
+ if (ApiStatus != NO_ERROR) {
+ return( ApiStatus );
+ }
+
+ ApiStatus = ListCheck(P_explist, rep_EXPLIST);
+ if (ApiStatus != NO_ERROR) {
+ return( ApiStatus );
+ }
+
+ IF_DEBUG(REPL) { // debug code
+
+ NetpKdPrint((" EXPORT LIST = %ws \n", P_explist));
+
+ }
+
+ // INTERVAL
+ ApiStatus = GetParameter(argc,
+ argv,
+ ConfigHandle,
+ rep_SYNC,
+ &ParmStrValue );
+ if (ApiStatus != NO_ERROR) {
+ return( ApiStatus );
+ }
+
+ ApiStatus = DwordCheck(ParmStrValue,
+ MIN_SYNC, MAX_SYNC, rep_SYNC, &P_sync );
+ if (ApiStatus != NO_ERROR) {
+ return( ApiStatus );
+ }
+
+ IF_DEBUG(REPL) { // debug code
+
+ NetpKdPrint((" INTERVAL = 0x%lx \n", P_sync));
+
+ }
+
+ // PULSE
+ ApiStatus = GetParameter(argc,
+ argv,
+ ConfigHandle,
+ rep_PULSE,
+ &ParmStrValue );
+ if (ApiStatus != NO_ERROR) {
+ return( ApiStatus );
+ }
+
+ ApiStatus = DwordCheck(ParmStrValue,
+ MIN_PULSE, MAX_PULSE, rep_PULSE, &P_pulse );
+ if (ApiStatus != NO_ERROR) {
+ return( ApiStatus );
+ }
+
+ IF_DEBUG(REPL) { // debug code
+
+ NetpKdPrint((" PULSE = 0x%lx \n", P_pulse));
+
+ }
+
+ // GUARD
+ ApiStatus = GetParameter(argc,
+ argv,
+ ConfigHandle,
+ rep_GUARD,
+ &ParmStrValue );
+ if (ApiStatus != NO_ERROR) {
+ return( ApiStatus );
+ }
+
+ ApiStatus = DwordCheck(ParmStrValue,
+ MIN_GUARD, MAX_GUARD, rep_GUARD, &P_guard );
+ if (ApiStatus != NO_ERROR) {
+ return( ApiStatus );
+ }
+
+ IF_DEBUG(REPL) { // debug code
+
+ NetpKdPrint((" GUARD = 0x%lx \n", P_guard));
+
+ }
+
+ if(P_guard > (P_sync / 2)) {
+ NetpKdPrint(( "[REPL-MASTER] guard and sync parms conflict.\n"));
+ ReplFinish(
+ SERVICE_UIC_CODE( SERVICE_UIC_CONFLPARM, 0),
+ NULL);
+
+
+ return( ERROR_INVALID_PARAMETER );
+ }
+
+
+ }
+
+ if (ReplRoleIncludesClient( ReplGlobalRole )) {
+
+ IF_DEBUG(REPL) { // debug code
+
+ NetpKdPrint(( "[Repl] Repl client parameters \n"));
+
+ }
+ // IMPORTPATH
+ ApiStatus = GetParameter(argc,
+ argv,
+ ConfigHandle,
+ rep_IMPPATH,
+ &P_imppath );
+ if (ApiStatus != NO_ERROR) {
+ return( ApiStatus );
+ }
+
+ ApiStatus = PathCheck(&P_imppath, ITYPE_PATH_ABSD, rep_IMPPATH);
+ if (ApiStatus != NO_ERROR) {
+ return( ApiStatus );
+ }
+
+ IF_DEBUG(REPL) { // debug code
+
+ NetpKdPrint((" IMPORT PATH = %ws \n", P_imppath));
+
+ }
+
+ // IMPORTLIST
+ ApiStatus = GetParameter(argc,
+ argv,
+ ConfigHandle,
+ rep_IMPLIST,
+ &P_implist );
+ if (ApiStatus != NO_ERROR) {
+ return( ApiStatus );
+ }
+
+ ApiStatus = ListCheck(P_implist, rep_IMPLIST);
+ if (ApiStatus != NO_ERROR) {
+ return( ApiStatus );
+ }
+
+ IF_DEBUG(REPL) { // debug code
+
+ NetpKdPrint((" IMPORT LIST = %ws \n", P_implist));
+ }
+
+ // TRY USER
+ ApiStatus = GetParameter(argc,
+ argv,
+ ConfigHandle,
+ rep_TRYUSER,
+ &ParmStrValue );
+ if (ApiStatus != NO_ERROR) {
+ return( ApiStatus );
+ }
+
+ ApiStatus = YesNoCheck(ParmStrValue, rep_TRYUSER, &P_tryuser );
+ if (ApiStatus != NO_ERROR) {
+ return( ApiStatus );
+ }
+
+ IF_DEBUG(REPL) { // debug code
+
+ NetpKdPrint((" TRYUSER = " FORMAT_DWORD "\n", (DWORD) P_tryuser));
+
+ }
+
+ // LOGON
+ ApiStatus = GetParameter(argc,
+ argv,
+ ConfigHandle,
+ rep_LOGON,
+ &P_logon );
+ if (ApiStatus != NO_ERROR) {
+ return( ApiStatus );
+ }
+
+ ApiStatus = NameCheck(&P_logon, NAMETYPE_USER, rep_LOGON);
+ if (ApiStatus != NO_ERROR) {
+ return( ApiStatus );
+ }
+
+ IF_DEBUG(REPL) { // debug code
+
+ NetpKdPrint((" LOGON = %ws \n", P_logon));
+
+ }
+
+ // PASSWORD
+ ApiStatus = GetParameter(argc,
+ argv,
+ ConfigHandle,
+ rep_PASSWD,
+ &P_passwd );
+ if (ApiStatus != NO_ERROR) {
+ return( ApiStatus );
+ }
+
+ ApiStatus = NameCheck(&P_passwd, NAMETYPE_PASSWORD, rep_PASSWD);
+ if (ApiStatus != NO_ERROR) {
+ return( ApiStatus );
+ }
+
+ IF_DEBUG(REPL) { // debug code
+
+ NetpKdPrint((" PASSWORD = %ws \n", P_passwd));
+
+ }
+
+ // RANDOM
+ ApiStatus = GetParameter(argc,
+ argv,
+ ConfigHandle,
+ rep_RANDOM,
+ &ParmStrValue );
+ if (ApiStatus != NO_ERROR) {
+ return( ApiStatus );
+ }
+
+ ApiStatus = DwordCheck(ParmStrValue,
+ MIN_RANDOM, MAX_RANDOM, rep_RANDOM, &P_random);
+ if (ApiStatus != NO_ERROR) {
+ return( ApiStatus );
+ }
+
+ IF_DEBUG(REPL) { // debug code
+
+ NetpKdPrint((" RANDOM = 0x%lx \n", P_random));
+
+ }
+
+ }
+
+
+ return( NO_ERROR );
+}
+
+DBGSTATIC NET_API_STATUS
+GetParameter(
+ IN DWORD argc,
+ IN LPTSTR argv[],
+ IN LPNET_CONFIG_HANDLE ConfigHandle,
+ IN LPTSTR SwitchName,
+ IN OUT LPWSTR * ParmValue
+ )
+/*++
+
+Routine Description :
+ Read a parameter from nt.cfg and from command line and return
+ appropriate value.
+
+Arguments :
+ argc, argv : command line
+ ConfigHandle : handle to our section of config data
+ SwitchName : name of the paremeter
+
+
+Return Value :
+ return NO_ERROR if successfully retrive the requested parameter and
+ ParmValue is set appropriately.
+
+ return other values if it can't retrive the parameter.
+ ReplFinish is called to terminate service.
+
+--*/
+{
+ NET_API_STATUS ApiStatus;
+ LPTSTR KeywordT;
+ LPTSTR ValueT;
+ LPWSTR ValueW;
+
+ ApiStatus = GetCmdValue(argc, argv, SwitchName, &ValueW );
+ if (ApiStatus != NO_ERROR) {
+ return( ApiStatus );
+ }
+
+ if (ValueW != NULL) {
+
+ *ParmValue = ValueW;
+ return( NO_ERROR );
+ }
+
+ //
+ // No value given in command line so read config data.
+ //
+ NetpAssert( SwitchName[0] == TCHAR_FWDSLASH );
+ KeywordT = & SwitchName[1];
+
+ ApiStatus = NetpGetConfigValue(
+ ConfigHandle,
+ KeywordT,
+ & ValueT); // Must be freed by NetApiBufferFree().
+
+ if (ApiStatus != NO_ERROR) {
+
+ //
+ // this parameter does not have an entry in nt.cfg and it is
+ // not defined in command line. However we return NO_ERROR and
+ // leaving the parameter value to default value the program
+ // assumed.
+ //
+
+ NetpAssert( ValueT == NULL );
+ return( NO_ERROR );
+
+ }
+ NetpAssert( ValueT != NULL );
+
+ //
+ // Give caller a copy of the value we found in config data.
+ //
+
+ ValueW = NetpAllocWStrFromTStr( ValueT ); // will check result below
+ NetpMemoryFree( ValueT );
+
+ if (ValueW == NULL) {
+
+ NetpKdPrint(( "[REPL] GetParameter: out of memory.\n" ));
+ ReplFinish(
+ SERVICE_UIC_CODE( SERVICE_UIC_RESOURCE, SERVICE_UIC_M_MEMORY),
+ NULL);
+
+ return( ERROR_NOT_ENOUGH_MEMORY );
+
+ }
+
+ *ParmValue = ValueW;
+
+ return( NO_ERROR );
+
+} // GetParameter
+
+
+DBGSTATIC NET_API_STATUS
+GetCmdValue(
+ IN DWORD argc,
+ IN LPTSTR argv[],
+ IN LPTSTR SwitchName,
+ IN OUT LPWSTR * ParmValue
+ )
+/*++
+
+Routine Description :
+ read a parameter from command line
+
+Arguments :
+ argc, argv : command line parameters
+ SwitchName : parameter name
+
+Return Value :
+ return NO_ERROR if successfully retrive the requested parameter and
+ ParmValue is set appropriately.
+
+ return ERROR_INVALID_PARAMETER if it can't retrive the parameter.
+ ReplFinish is called to terminate service.
+
+--*/
+{
+ NET_API_STATUS ApiStatus = NO_ERROR; // innocent until proven guilty
+ LPTSTR SepaPtr;
+ DWORD i;
+
+ *ParmValue = NULL;
+
+ NetpAssert( SwitchName[0] == TCHAR_FWDSLASH );
+
+ for(i = 0; i < argc; i++) {
+ SepaPtr = STRCHR(argv[i], (TCHAR) ':');
+
+ if (SepaPtr == NULL) {
+
+ ReplConfigReportBadParmValue( SwitchName, NULL );
+
+ ApiStatus = ERROR_INVALID_PARAMETER;
+
+ break;
+ }
+
+ if (STRNICMP(argv[i], SwitchName, (SepaPtr - argv[i])) == 0) {
+
+ *ParmValue = NetpAllocWStrFromTStr( SepaPtr + 1 ); // skip colon.
+
+ if(*ParmValue == NULL) {
+
+ NetpKdPrint(( "[REPL] GetCmdValue: out of memory.\n" ));
+ ReplFinish(
+ SERVICE_UIC_CODE(
+ SERVICE_UIC_RESOURCE,
+ SERVICE_UIC_M_MEMORY),
+ NULL);
+
+ ApiStatus = ERROR_NOT_ENOUGH_MEMORY;
+
+ break;
+ }
+
+ break;
+ }
+ }
+
+ IF_DEBUG(REPL) {
+ NetpKdPrint(( "GetCmdValue: returning " FORMAT_API_STATUS
+ " while searching for '" FORMAT_LPTSTR "',\n",
+ ApiStatus, SwitchName ));
+ NetpKdPrint(( " *ParmValue = " FORMAT_LPVOID ".\n",
+ (LPVOID) *ParmValue));
+ }
+ return( ApiStatus );
+
+}
+
+DBGSTATIC NET_API_STATUS
+PathCheck(
+ IN OUT LPWSTR * Path,
+ IN DWORD TypeWanted,
+ IN LPTSTR SwitchName
+ )
+/*++
+
+Routine Description :
+ check the syntax of the given path name and existance of that path.
+
+Arguments :
+ Path : path name to test. (This will be freed by this routine, and set to
+ point to a new alloc'ed canonicalized path.)
+ TypeWanted : path type to test
+ SwitchName : name of switch associated with this path.
+
+Return Value :
+ NET_API_STATUS (e.g. ERROR_INVALID_PARAMETER if error in *Path)
+
+ NOTE : it frees the original path buffer.
+
+ NOTE: This routine also changes the current directory.
+
+--*/
+
+{
+ NET_API_STATUS ApiStatus;
+ DWORD ActualType = 0;
+ TCHAR CanonPathT[MAX_PATH]; // BUGBUG do we want +1 here?
+ LPWSTR CanonPathW;
+ LPTSTR UncanonPathT; // Input path as TCHARs.
+ LPWSTR UncanonPathW;
+
+ NetpAssert( Path != NULL );
+ NetpAssert( *Path != NULL );
+
+ UncanonPathW = *Path;
+ UncanonPathT = NetpAllocTStrFromWStr( UncanonPathW );
+
+ if (UncanonPathT == NULL) {
+
+ ReplFinish(
+ SERVICE_UIC_CODE( SERVICE_UIC_RESOURCE, SERVICE_UIC_M_MEMORY),
+ NULL);
+
+ return( ERROR_NOT_ENOUGH_MEMORY );
+ }
+
+ ApiStatus = I_NetPathCanonicalize(
+ NULL,
+ UncanonPathT,
+ CanonPathT,
+ sizeof(CanonPathT),
+ NULL,
+ &ActualType,
+ 0);
+
+ IF_DEBUG(REPL) {
+ NetpKdPrint(( "[Repl] PathCheck: TypeWanted is " FORMAT_DWORD " for "
+ FORMAT_LPWSTR " and real type is " FORMAT_DWORD ".\n",
+ TypeWanted, *Path, ActualType ));
+ NetpKdPrint(( "[REPL] PathCheck: ApiStatus is " FORMAT_API_STATUS
+ ".\n", ApiStatus ));
+ }
+ NetpMemoryFree( UncanonPathT );
+
+ //
+ // Check the type of the input.
+ //
+
+ if (TypeWanted == ITYPE_PATH_ABSD) {
+
+ if ((ApiStatus != NO_ERROR) || (ActualType != ITYPE_PATH_ABSD)) {
+
+ ReplConfigReportBadParmValue( SwitchName, *Path );
+
+ if (ApiStatus != NO_ERROR) {
+ return( ApiStatus );
+ } else {
+ return( ERROR_INVALID_PARAMETER );
+ }
+ }
+
+ } else {
+
+ NetpAssert( TypeWanted == ITYPE_PATH_RELND );
+
+ if ((ApiStatus != NO_ERROR) ||
+ ((ActualType != ITYPE_PATH_ABSD) &&
+ (ActualType != ITYPE_PATH_RELND))) {
+
+ ReplConfigReportBadParmValue( SwitchName, *Path );
+
+ if (ApiStatus != NO_ERROR) {
+ return( ApiStatus );
+ } else {
+ return( ERROR_INVALID_PARAMETER );
+ }
+ }
+ }
+
+ //
+ // Arrange to pass back the canonicalized version of input path.
+ //
+ NetpMemoryFree( UncanonPathW );
+
+ if (ActualType == ITYPE_PATH_ABSD) {
+
+ CanonPathW = NetpAllocWStrFromTStr( CanonPathT );
+
+ if (CanonPathW == NULL) {
+
+ ReplFinish(
+ SERVICE_UIC_CODE( SERVICE_UIC_RESOURCE, SERVICE_UIC_M_MEMORY),
+ NULL);
+
+
+ return( ERROR_NOT_ENOUGH_MEMORY );
+ }
+
+ } else {
+
+ //
+ // BUGBUG: Can't get canon relative path, 'cos bug in
+ // I_NetPathCanonicalize insists on add current directory to it.
+ // But we don't need this for the time being (or ever?).
+ //
+ NetpAssert( FALSE );
+ }
+
+ NetpAssert( CanonPathW != NULL );
+ *Path = CanonPathW;
+
+ //
+ // Make sure the path is valid path and it exists.
+ //
+ if (!SetCurrentDirectoryW(CanonPathW)) {
+
+ ReplFinish( SERVICE_UIC_CODE( SERVICE_UIC_SYSTEM, 0), NULL);
+
+ return( ERROR_DIRECTORY ); // BUGBUG: Is there a better error code?
+ }
+
+ return( NO_ERROR );
+
+} // PathCheck
+
+
+DBGSTATIC NET_API_STATUS
+ListCheck(
+ IN LPWSTR list,
+ IN LPTSTR SwitchName
+ )
+/*++
+
+Routine Description :
+ check a name list.
+
+Arguments :
+ list : name list
+ SwitchName : name of switch associated with this parameter.
+
+Return Value :
+ return NO_ERROR if the list is OK
+ CmdValue is set appropriately.
+
+ return error code if not, ReplFinish is called to terminate service.
+
+ Note :
+
+ 1. does return from this routine if error
+
+ 2. Canonicalize the export name list, Really just making sure
+ no major errors here, master process will do actual work anyway.
+
+--*/
+{
+
+#if UNICODE
+ WCHAR WorkBuf[MAX_PATH * MAX_REPL_NAMELIST];
+#else // UNICODE
+ CHAR WorkBuf[MAX_PATH * MAX_REPL_NAMELIST];
+ LPSTR AnsiName;
+#endif
+
+ DWORD list_count;
+ NET_API_STATUS NetStatus;
+
+#ifdef UNICODE
+ NetStatus = I_NetListCanonicalize(NULL,
+ list,
+ LIST_DELIMITER_STR_UI,
+ WorkBuf,
+ sizeof(WorkBuf) ,
+ &list_count,
+ NULL,
+ 0,
+ (NAMETYPE_COMPUTER |
+ OUTLIST_TYPE_API |
+ INLC_FLAGS_MULTIPLE_DELIMITERS |
+ INLC_FLAGS_CANONICALIZE
+ ));
+
+ if((NetStatus != NO_ERROR) ||
+ (list_count > MAX_REPL_NAMELIST)) {
+
+ ReplConfigReportBadParmValue( SwitchName, list );
+
+ if (NetStatus != NO_ERROR) {
+ return( NetStatus );
+ } else {
+ return( ERROR_INVALID_PARAMETER );
+ }
+ }
+
+#else // UNICODE
+
+ AnsiName = NetpAllocStrFromWStr(list);
+
+ if(AnsiName == NULL) {
+
+ ReplFinish(
+ SERVICE_UIC_CODE( SERVICE_UIC_RESOURCE, SERVICE_UIC_M_MEMORY),
+ NULL);
+
+
+ return( ERROR_NOT_ENOUGH_MEMORY );
+ }
+
+ NetStatus = I_NetListCanonicalize(NULL,
+ AnsiName,
+ LIST_DELIMITER_STR_UI,
+ WorkBuf,
+ sizeof(WorkBuf) ,
+ &list_count,
+ NULL,
+ 0,
+ (NAMETYPE_COMPUTER |
+ OUTLIST_TYPE_API |
+ INLC_FLAGS_MULTIPLE_DELIMITERS |
+ INLC_FLAGS_CANONICALIZE
+ ));
+
+ NetpMemoryFree(AnsiName);
+
+ if((NetStatus != NO_ERROR) ||
+ (list_count > MAX_REPL_NAMELIST)) {
+
+ ReplConfigReportBadParmValue( SwitchName, list );
+
+ if (NetStatus != NO_ERROR) {
+ return( NetStatus );
+ } else {
+ return( ERROR_INVALID_PARAMETER );
+ }
+ }
+
+
+#endif // UNICODE
+
+ return( NO_ERROR );
+}
+
+DBGSTATIC NET_API_STATUS
+DwordCheck(
+ IN LPWSTR StrValue OPTIONAL,
+ IN DWORD Min,
+ IN DWORD Max,
+ IN LPTSTR SwitchName,
+ IN OUT LPDWORD DwordValue
+ )
+/*++
+
+Routine Description :
+ convert a string parameter to DWORD parameter. Also check the range
+ of parameter.
+
+Arguments :
+ StrValue : WCHAR string (or NULL if no value given)
+ Min : minimum value of the parameter.
+ Max : maximum value of the parameter.
+ SwitchName : name of switch associated with this parameter.
+
+Return Value :
+ return NO_ERROR if successfully convert and set dword value.
+
+ return error code if not, ReplFinish is called to terminate service.
+
+ NOTE : it frees the storage of the parameter buffer.
+
+--*/
+{
+ DWORD value;
+
+ if (StrValue != NULL) {
+ *DwordValue = 0;
+
+ value = (DWORD) wtol(StrValue);
+
+ NetpMemoryFree(StrValue);
+ } else {
+ value = *DwordValue;
+ }
+
+ if((value < Min) || (value > Max)) {
+
+ ReplConfigReportBadParmValue( SwitchName, StrValue );
+
+ return( ERROR_INVALID_PARAMETER );
+ }
+
+ *DwordValue = value;
+
+ return( NO_ERROR );
+}
+
+DBGSTATIC NET_API_STATUS
+YesNoCheck(
+ IN LPWSTR StrValue OPTIONAL,
+ IN LPTSTR SwitchName,
+ IN OUT LPBOOL Value
+ )
+/*++
+
+Routine Description :
+ check YES/NO type parameter.
+
+Arguments :
+ StrValue : parameter value.
+ SwitchName : name of switch associated with this parameter.
+
+Return Value :
+ return NO_ERROR if successfully set yes/no value.
+
+ return error code if not, ReplFinish is called to terminate service.
+
+--*/
+{
+ if (StrValue != NULL) {
+ if(wcsncmpi(StrValue, L"YES", wcslen(StrValue)) == 0) {
+ NetpMemoryFree(StrValue);
+ *Value = TRUE;
+ return (NO_ERROR);
+ }
+
+ if(wcsncmpi(StrValue, L"NO", wcslen(StrValue)) == 0) {
+ NetpMemoryFree(StrValue);
+ *Value = FALSE;
+ return (NO_ERROR);
+ }
+
+ ReplConfigReportBadParmValue( SwitchName, StrValue );
+
+ return (ERROR_INVALID_PARAMETER);
+ } else {
+ return (NO_ERROR); // Leave default set.
+ }
+ /*NOTREACHED*/
+
+}
+
+DBGSTATIC NET_API_STATUS
+NameCheck(
+ IN OUT LPWSTR * Name,
+ IN DWORD Type,
+ IN LPTSTR SwitchName
+ )
+/*++
+
+Routine Description :
+ check NAME type parameter.
+
+Arguments :
+ Name : name value
+ Type : name type
+ SwitchName : name of switch associated with this parameter.
+
+Return Value :
+ return NO_ERROR if successfully checks the name value.
+
+ return error code if not, ReplFinish is called to terminate service.
+
+--*/
+{
+#ifdef UNICODE
+ LPWSTR CanonName;
+#else // UNICODE
+ LPSTR CanonName;
+ LPSTR AnsiName;
+#endif
+
+ NET_API_STATUS NetStatus;
+
+ if(*Name != NULL) {
+
+ if((Type == NAMETYPE_USER) && ((*Name)[0] == L'\0')) {
+
+ ReplConfigReportBadParmValue( SwitchName, *Name );
+
+
+ return( ERROR_INVALID_PARAMETER );
+ }
+
+#ifdef UNICODE
+ CanonName = NetpMemoryAllocate((UNLEN + 1) * sizeof(WCHAR));
+
+ if(CanonName == NULL) {
+
+ ReplFinish(
+ SERVICE_UIC_CODE( SERVICE_UIC_RESOURCE, SERVICE_UIC_M_MEMORY),
+ NULL);
+
+
+ return( ERROR_NOT_ENOUGH_MEMORY );
+ }
+
+ NetStatus = I_NetNameCanonicalize(NULL,
+ *Name,
+ CanonName,
+ (UNLEN + 1) * sizeof(WCHAR),
+ Type,
+ 0);
+
+ if(NetStatus != NO_ERROR) {
+ NetpMemoryFree(CanonName);
+
+ ReplConfigReportBadParmValue( SwitchName, *Name );
+
+
+ return( NetStatus );
+ }
+
+ NetpMemoryFree(*Name);
+ *Name = CanonName;
+
+#else //UNICODE
+
+ AnsiName = NetpAllocStrFromWStr(*Name);
+ CanonName = NetpMemoryAllocate(UNLEN + 1);
+
+ if((AnsiName == NULL) || (CanonName == NULL)) {
+
+ ReplFinish(
+ SERVICE_UIC_CODE( SERVICE_UIC_RESOURCE, SERVICE_UIC_M_MEMORY),
+ NULL);
+
+
+ return( ERROR_NOT_ENOUGH_MEMORY );
+ }
+
+
+ NetStatus = I_NetNameCanonicalize(NULL,
+ AnsiName,
+ CanonName,
+ (UNLEN + 1),
+ Type,
+ 0);
+
+ NetpMemoryFree(AnsiName);
+
+ if(NetStatus != NO_ERROR) {
+ NetpMemoryFree(CanonName);
+
+ ReplConfigReportBadParmValue( SwitchName, *Name );
+
+
+ return( NetStatus );
+ }
+
+ NetpMemoryFree(*Name);
+ *Name = NetpAllocWStrFromStr(CanonName);
+
+ NetpMemoryFree(CanonName);
+
+ if(*Name == NULL) {
+
+ ReplFinish(
+ SERVICE_UIC_CODE( SERVICE_UIC_RESOURCE, SERVICE_UIC_M_MEMORY),
+ NULL);
+
+
+ return( NetStatus );
+ }
+
+#endif
+ }
+
+ return( NO_ERROR );
+}
+
+
+// Routine to report a bad parm value.
+// There are two different versions of this routine,
+// in client/report.c and server/parse.c [actually now server/error.c]
+// client/report.c is callable whether or not service is started.
+// server/parse.c only runs when service is starting; it talks to the service
+// controller.
+
+VOID
+ReplConfigReportBadParmValue(
+ IN LPTSTR SwitchName,
+ IN LPWSTR TheValue OPTIONAL
+ )
+{
+ UNREFERENCED_PARAMETER( SwitchName );
+
+ NetpAssert( SwitchName != NULL );
+ IF_DEBUG(REPL) {
+ NetpKdPrint(( "[REPL] Bad value to " FORMAT_LPTSTR " switch.\n",
+ SwitchName ));
+ }
+ ReplFinish(
+ SERVICE_UIC_CODE( SERVICE_UIC_BADPARMVAL, 0),
+ TheValue);
+
+} // ReplConfigReportBadParmValue
+
+
+VOID
+InitParseData(
+ VOID
+ )
+{
+ def_P_repl = DEFAULT_REPL;
+ def_P_exppath = DEFAULT_EXPPATH;
+ def_P_imppath = DEFAULT_IMPPATH;
+ def_P_explist = DEFAULT_EXPLIST;
+ def_P_implist = DEFAULT_IMPLIST;
+ def_P_logon = DEFAULT_LOGON;
+ def_P_passwd = DEFAULT_PASSWD;
+
+
+ P_repl = def_P_repl;
+ P_exppath = def_P_exppath;
+ P_imppath = def_P_imppath;
+ P_explist = def_P_explist;
+ P_implist = def_P_implist;
+ P_logon = def_P_logon;
+ P_passwd = def_P_passwd;
+
+ P_tryuser = DEFAULT_TRYUSER;
+ P_sync = DEFAULT_SYNC;
+ P_pulse = DEFAULT_PULSE;
+ P_guard = DEFAULT_GUARD;
+ P_random = DEFAULT_RANDOM;
+
+ ReplGlobalRole = REPL_ROLE_IMPORT;
+
+}
+
+VOID
+FreeParseData(
+ VOID
+ )
+{
+ // free up the heap memory
+
+ if( P_repl != def_P_repl) {
+ NetpMemoryFree(P_repl);
+ }
+
+ if( P_exppath != def_P_exppath ) {
+ NetpMemoryFree(P_exppath);
+ }
+
+ if( P_imppath != def_P_imppath ) {
+ NetpMemoryFree(P_imppath);
+ }
+
+ if( P_explist != def_P_explist ) {
+ NetpMemoryFree(P_explist);
+ }
+
+ if( P_implist != def_P_implist ) {
+ NetpMemoryFree(P_implist);
+ }
+
+ if( P_logon != def_P_logon ) {
+ NetpMemoryFree(P_logon);
+ }
+
+ if( P_passwd != def_P_passwd ) {
+ NetpMemoryFree(P_passwd);
+ }
+
+}
+
+#endif // 0 - entire file is obsolete.
diff --git a/private/net/svcdlls/repl/server/permit.c b/private/net/svcdlls/repl/server/permit.c
new file mode 100644
index 000000000..a1315c1b6
--- /dev/null
+++ b/private/net/svcdlls/repl/server/permit.c
@@ -0,0 +1,218 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+Copyright (c) Maynard Electronics, Inc. 1984-93
+
+Module Name:
+
+ Permit.c
+
+Abstract:
+
+ This module contains the following functions:
+
+ ReplInitBackupPermission
+ ReplEnableBackupPermission (BUGBUG: just a stub)
+ ReplDisableBackupPermission (BUGBUG: just a stub)
+
+ BUGBUG: right now this could be less of a security risk:
+
+ (1) Instead of changing process token, we could just do thread token.
+
+ (2) We could enable and disable this on the fly (by impersonate
+ and revert to self).
+
+Author:
+
+ John Rogers (JohnRo) 01-Feb-1993
+
+Environment:
+
+ User mode only. Uses Win32 APIs.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+ Tab size is set to 4.
+
+Revision History:
+
+ 01-Feb-1993 JohnRo
+ Created Microsoft repl code from Maynard's NTFSRegy.c (from Steve
+ DeVos at Maynard).
+
+--*/
+
+
+#include <windows.h> // IN, LPTSTR, CreateDirectory(), SE_BACKUP_ stuff, etc.
+#include <lmcons.h> // NET_API_STATUS, PATHLEN, etc.
+
+#include <lmerrlog.h> // NELOG_ equates.
+#include <netdebug.h> // NetpKdPrint(), FORMAT_ equates, etc.
+#include <netlib.h> // NetpMemoryAllocate(), etc.
+#include <prefix.h> // PREFIX_ equates.
+#include <repldefs.h> // IF_DEBUG(), my prototype, etc.
+#include <tstr.h> // TCHAR_EOS, STRLEN().
+#include <winerror.h> // NO_ERROR, ERROR_ equates.
+
+
+
+NET_API_STATUS
+ReplInitBackupPermission(
+ VOID
+ )
+{
+ NET_API_STATUS ApiStatus;
+ LUID BackupValue;
+ const DWORD DesiredAccess = TOKEN_ADJUST_PRIVILEGES;
+ TOKEN_PRIVILEGES NewState;
+ HANDLE ProcessHandle;
+ LUID RestoreValue;
+ HANDLE TokenHandle = NULL;
+
+
+ // get process handle
+
+ ProcessHandle = GetCurrentProcess();
+
+ // open process token
+
+
+ if ( ! OpenProcessToken( ProcessHandle, DesiredAccess, &TokenHandle ) ) {
+ ApiStatus = (NET_API_STATUS) GetLastError();
+
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplInitBackupPermission: OpenProcessToken FAILED, status "
+ FORMAT_API_STATUS "\n", ApiStatus ));
+
+ NetpAssert( ApiStatus != NO_ERROR );
+ goto Cleanup; // Go log the error.
+ }
+ NetpAssert( TokenHandle != NULL );
+
+ // adjust backup token privileges
+
+ if ( ! LookupPrivilegeValue( NULL,
+ (LPTSTR) SE_BACKUP_NAME,
+ &BackupValue ) ) {
+
+ ApiStatus = (NET_API_STATUS) GetLastError();
+
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplInitBackupPermission: LookupPrividegeValue(1st) FAILED, "
+ "status " FORMAT_API_STATUS "\n", ApiStatus ));
+
+ NetpAssert( ApiStatus != NO_ERROR );
+ goto Cleanup;
+ }
+
+ // Enable backup privilege for this process
+
+ NewState.PrivilegeCount = 1;
+ NewState.Privileges[0].Luid = BackupValue;
+ NewState.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
+
+ if ( ! AdjustTokenPrivileges( TokenHandle, FALSE, &NewState, (DWORD)0, NULL, NULL ) ) {
+ ApiStatus = (NET_API_STATUS) GetLastError();
+
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplInitBackupPermission: AdjustPrividegeValue(1st) FAILED, "
+ "status " FORMAT_API_STATUS "\n", ApiStatus ));
+
+ NetpAssert( ApiStatus != NO_ERROR );
+ goto Cleanup;
+ }
+
+ // AdjustTokenPriv always returns SUCCESS, call GetLast to see if it worked.
+
+ ApiStatus = (NET_API_STATUS) GetLastError();
+ if ( ApiStatus != NO_ERROR ) {
+
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplInitBackupPermission: AdjustPrividegeValue(1st) FAILED, "
+ "status " FORMAT_API_STATUS "\n", ApiStatus ));
+
+ goto Cleanup;
+ }
+
+ // adjust restore token privileges
+
+ if ( ! LookupPrivilegeValue( NULL, (LPTSTR) SE_RESTORE_NAME, &RestoreValue ) ) {
+ ApiStatus = (NET_API_STATUS) GetLastError();
+
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplInitBackupPermission: LookupPrividegeValue(2nd) FAILED, "
+ "status " FORMAT_API_STATUS "\n", ApiStatus ));
+
+ NetpAssert( ApiStatus != NO_ERROR );
+ goto Cleanup;
+ }
+
+ // Enable backup privilege for this process
+
+ NewState.PrivilegeCount = 1;
+ NewState.Privileges[0].Luid = RestoreValue;
+ NewState.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
+
+ if ( ! AdjustTokenPrivileges( TokenHandle, FALSE, &NewState, (DWORD)0, NULL, NULL ) ) {
+ ApiStatus = (NET_API_STATUS) GetLastError();
+
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplInitBackupPermission: AdjustPrividegeValue(2nd) FAILED, "
+ "status " FORMAT_API_STATUS "\n", ApiStatus ));
+
+ NetpAssert( ApiStatus != NO_ERROR );
+ goto Cleanup;
+ }
+
+ if ( GetLastError() != NO_ERROR ) {
+ ApiStatus = (NET_API_STATUS) GetLastError();
+
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplInitBackupPermission: AdjustPrividegeValue(2nd) FAILED, "
+ "status " FORMAT_API_STATUS "\n", ApiStatus ));
+
+ NetpAssert( ApiStatus != NO_ERROR );
+ goto Cleanup;
+ }
+
+Cleanup:
+
+ if (TokenHandle != NULL) {
+ // close process token
+
+ (VOID) CloseHandle( TokenHandle );
+ }
+
+ if (ApiStatus != NO_ERROR) {
+ NetpKdPrint(( PREFIX_REPL
+ "ReplInitBackupPermission: FAILED, status " FORMAT_API_STATUS
+ ".\n", ApiStatus ));
+
+ ReplErrorLog(
+ NULL, // no server name (local)
+ NELOG_ReplSysErr, // log code
+ ApiStatus, // error code we got
+ NULL, // no optional str 1
+ NULL ); // no optional str 2
+ }
+ return( ApiStatus );
+
+} // ReplInitBackupPermission
+
+
+NET_API_STATUS
+ReplEnableBackupPermission(
+ VOID
+ )
+{
+ return (NO_ERROR); // BUGBUG: finish this!
+
+} // ReplEnableBackupPermission
+
+
+NET_API_STATUS
+ReplDisableBackupPermission(
+ VOID
+ )
+{
+ return (NO_ERROR); // BUGBUG: finish this!
+
+} // ReplDisableBackupPermission
diff --git a/private/net/svcdlls/repl/server/puls_msg.c b/private/net/svcdlls/repl/server/puls_msg.c
new file mode 100644
index 000000000..ecb4eebbd
--- /dev/null
+++ b/private/net/svcdlls/repl/server/puls_msg.c
@@ -0,0 +1,1367 @@
+/*++
+
+Copyright (c) 1987-1993 Microsoft Corporation
+
+Module Name:
+ puls_msg.c
+
+Abstract:
+ Contains routines that handle all update messages.
+
+Author:
+ Ported from Lan Man 2.x
+
+Environment:
+ Contains NT-specific code.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+ 04/15/89 (yuv)
+ initial coding
+
+ 10/07/91 (madana)
+ ported to NT. Converted to NT style.
+ 16-Jan-1992 JohnRo
+ Avoid using private logon functions.
+ Changed file name from repl.h to replgbl.h to avoid MIDL conflict.
+ Fixed bug regarding returned value from NetpReplWriteMail functions.
+ 30-Jan-1992 JohnRo
+ Changed to use LPTSTR etc.
+ 10-Feb-1992 JohnRo
+ Changed to use FORMAT_ equates.
+ 20-Feb-1992 JohnRo
+ Fix mailslot name when no export list is specified.
+ AddToMsg's caller should get a shared lock on master list.
+ InitMsgBuf() should get a shared lock on config data.
+ 22-Feb-1992 JohnRo
+ Minor changes to mailslot name handling.
+ 24-Mar-1992 JohnRo
+ Renamed many ReplGlobal vars to ReplConfig vars.
+ Clarify value of pulse_rate field.
+ Added more debug output.
+ Make sure some things are aligned.
+ 27-Mar-1992 JohnRo
+ Some UNICODE strings in packet should be unaligned.
+ 25-Oct-1992 jimkel
+ rename RMGlobalClient* to RMGlobalImport*
+ now locked by ReplConfigLock
+ 15-Feb-1993 JohnRo
+ RAID 11365: Fixed various mailslot size problems.
+ Corrected some message format comments.
+ Use NetpKdPrint() where possible.
+ Made changes suggested by PC-LINT 5.0
+ Use PREFIX_ equates.
+
+--*/
+
+
+// These must be included first:
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windows.h>
+#include <lmcons.h>
+
+// These may be included in any order:
+
+#include <lmerrlog.h>
+#include <alertmsg.h>
+#include <netdebug.h> // DBGSTATIC, NetDbgHexDump(), FORMAT_ equates, etc.
+#include <prefix.h> // PREFIX_ equates.
+#include <smbgtpt.h>
+#include <netlib.h>
+#include <netlock.h> // ACQUIRE_LOCK_SHARED(), etc.
+#include <string.h> // strcpy(), strlen().
+#include <tstring.h> // NetpAlloc{type}From{type}(), etc.
+#include <stdlib.h> // wcscpy(), etc.
+
+
+// repl headers
+
+#include <repldefs.h> // IF_DEBUG(), etc.
+#include <replgbl.h> // ReplGlobal and ReplConfig variables.
+#include <replpack.h>
+#include <master.h>
+#include <replp.h>
+
+/*++
+
+DATA STRUCTURES:
+
+ 1. MsgBuf[max mailsot message size] - used for actual send - WriteMailsot.
+
+ This buffer is formatted as follows:
+
+ In compatibility mode: (will hold ANSI and UNICODE strings)
+
+ Fixed part:
+
+ 1. MSG_HEADER header;
+
+ WORD msg_type;
+ CHAR sender[CNLEN+1];
+ CHAR senders_domain[DNLEN+1];
+
+ 2. REPL_INFO info;
+
+ WORD random;
+ WORD sync_rate;
+ WORD pulse_rate; (pulse time * sync rate)
+ WORD guard_time;
+
+ 3. WORD update_count;
+
+ 4. MSG_STATUS_REC - update_count records
+
+ WORD dir_name_offset;
+ WORD opcode;
+ DWORD checksum; (old style checksum)
+ WORD count;
+ WORD integrity;
+ WORD extent;
+
+ 5. UNICODE_ANSI_MSG_HEADER
+
+ WCHAR sender[CNLEN+1]; (or shorter; unaligned)
+ WCHAR senders_domain[DNLEN+1]; (or shorter; unaligned)
+
+ Variable Part:
+
+ 6. DIR_NAMEs - update_count name pairs
+
+ CHAR AnsiDirName[PATHLEN+1]; (or shorter)
+ WCHAR UnicodeDirName[PATHLEN+1]; (or shorter; unaligned)
+
+ 7. NT MESSAGE SIGNATURE
+
+ DWORD Signature (unaligned)
+
+ In NT only mode: (with UNICODE strings only)
+
+ 1. UNICODE_MSG_HEADER header;
+
+ WORD msg_type;
+ CHAR sender[CNLEN+1]; // NULL string
+ CHAR senders_domain[DNLEN+1]; // NULL string
+
+ 2. REPL_INFO info;
+
+ WORD random;
+ WORD sync_rate;
+ WORD pulse_rate; (pulse time * sync rate)
+ WORD guard_time;
+
+ 3. WORD update_count;
+
+ 4. MSG_STATUS_REC - update count records
+
+ WORD dir_name_offset;
+ WORD opcode;
+ DWORD checksum;
+ WORD count;
+ WORD integrity;
+ WORD extent;
+
+ 5. UNICODE_ANSI_MSG_HEADER
+
+ WCHAR sender[CNLEN+1];
+ WCHAR senders_domain[DNLEN+1];
+
+ Variable Part:
+
+ 6. DIR_NAMEs - update_count name pairs
+
+ CHAR Null String;
+ WCHAR UnicodeDirName;
+
+ 7. NT MESSAGE SIGNATURE
+
+ DWORD Signature
+
+ 2. UpdateList: list of update records.
+
+ UpdateList sentinel will have the following fields:
+
+ 1. count of update records in the list.
+
+ 2. Pointer to head of the list.
+
+ 3. Pointer to tail of the list.
+
+ Each entry in the list will have the following fields.
+
+ 1. Pointer to next record.
+
+ 2. Pointer to prev record.
+
+ 3. STATUS_REC structure.
+
+ 3. FreeList: list of free record space.
+
+ Initially the list will be empty list, as update records come in
+ we will allote record space and when update records are freed, the
+ free record space is saved in this list. However this free list will
+ save only maximum of MAX_FREE_RECORDS.
+
+FUNCTIONS:
+
+1. InitMsgBuf()
+
+ - initializes MsgBuf with MSG_HEADER and REPL_INFO,
+ and UpdateList to NULL list.
+
+2. InitMsgSend(type)
+
+ - Plugs msg_type in header and reset UpdateList.
+
+3. AddToMsg(upd_rec)
+
+ - adds upd_rec to upd_buf to UpdateList.
+
+4. MsgSend()
+
+ - Sends the message. uses as many mailslot writes as
+ needed to send the entire UpdateList.
+
+--*/
+
+#define MAX_FREE_RECORDS 10
+
+#define NO_SAVE 0
+#define SAVE 1
+
+// type defs
+
+struct update_rec {
+ struct update_rec *NextRec;
+ struct update_rec *PrevRec;
+ STATUS_REC UpdateEntry;
+};
+
+typedef struct update_rec UPDATE_REC;
+typedef struct update_rec * PUPDATE_REC;
+//typedef struct update_rec * LPUPDATE_REC;
+
+struct list_sentinel {
+ DWORD count;
+ PUPDATE_REC Head;
+ PUPDATE_REC Tail;
+};
+
+typedef struct list_sentinel LIST_SENTINEL;
+
+// G L O B A L S:
+
+DBGSTATIC LIST_SENTINEL UpdateList; // update list header
+
+DBGSTATIC LIST_SENTINEL FreeList; // free list header
+// Note: FreeList is a singly linked list. so FreeList.tail field and
+// the PrevRec field in FreeRecs are unused.
+
+
+DBGSTATIC CHAR MsgBuf[MAX_2_MSLOT_SIZE];
+
+DBGSTATIC VOID
+SendToAll(
+ IN LPBYTE MsgBuffer,
+ IN DWORD MsgSize
+ );
+
+DBGSTATIC VOID
+AddUpdateRec(
+ IN OUT PUPDATE_REC UpdateRec
+ );
+
+DBGSTATIC VOID
+DeleteUpdateRec(
+ IN PUPDATE_REC UpdateRec
+ );
+
+DBGSTATIC PUPDATE_REC
+GetFreeUpdateRec(
+ VOID
+ );
+
+DBGSTATIC VOID
+FreeUpdateRec(
+ IN PUPDATE_REC UpdateRec,
+ IN DWORD SaveFlag
+ );
+
+DBGSTATIC VOID
+FreeUpdateList(
+ IN DWORD SaveFlag
+ );
+
+DBGSTATIC VOID
+FreeFreeList(
+ VOID
+ );
+
+DBGSTATIC DWORD
+GetNumRecFitIn(
+ IN DWORD BufSize,
+ OUT LPDWORD FitSize
+ );
+
+
+BOOL
+InitMsgBuf(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ Called by InitMsg ( called at Master start ).
+ Initializes MsgBuf and UpdateList;
+
+Arguments:
+
+ none
+
+Return Value:
+
+ TRUE iff no error occurred.
+
+Threads:
+
+ Only called by pulser thread.
+
+--*/
+{
+ PPACK_SYNCMSG SyncMsg;
+
+ //
+ // initialize UpdateList
+ //
+
+ UpdateList.Head = UpdateList.Tail = NULL;
+ UpdateList.count = 0;
+
+ //
+ // initialize FreeList
+ //
+
+ FreeList.Head = FreeList.Tail = NULL;
+ FreeList.count = 0;
+
+ //
+ // initialize MsgBuf.
+ //
+
+ SyncMsg = (PPACK_SYNCMSG)MsgBuf;
+
+ if( RMGlobalCompatibilityMode ) {
+
+ // copy Ansi names in compatibility mode messaging
+
+ (void) strcpy( SyncMsg->header.sender,
+ ReplGlobalAnsiComputerName);
+
+ } else {
+
+ // insert NULL string
+
+ SyncMsg->header.sender[0] = '\0';
+
+ }
+
+ if( RMGlobalCompatibilityMode ) {
+
+ (void) strcpy( SyncMsg->header.senders_domain,
+ ReplGlobalAnsiDomainName);
+
+ } else {
+
+ // insert NULL string
+
+ SyncMsg->header.senders_domain[0] = '\0';
+
+ }
+
+ // copy other header info.
+
+ ACQUIRE_LOCK_SHARED( ReplConfigLock );
+
+ NetpAssert( ReplIsRandomValid( ReplConfigRandom ) );
+ SmbPutUshort( (LPWORD) ( &(SyncMsg->info.random) ),
+ (WORD) ReplConfigRandom);
+
+ NetpAssert( ReplIsIntervalValid( ReplConfigInterval ) );
+ SmbPutUshort( (LPWORD) ( &(SyncMsg->info.sync_rate) ),
+ (WORD) ReplConfigInterval);
+
+ NetpAssert( ReplIsPulseValid( ReplConfigPulse ) );
+ SmbPutUshort( (LPWORD) ( &(SyncMsg->info.pulse_rate) ),
+ (WORD) (ReplConfigPulse * ReplConfigInterval));
+
+ NetpAssert( ReplIsGuardTimeValid( ReplConfigGuardTime ) );
+ SmbPutUshort( (LPWORD) ( &(SyncMsg->info.guard_time) ),
+ (WORD) ReplConfigGuardTime);
+
+ SmbPutUshort( (LPWORD) ( &(SyncMsg->update_count) ), 0);
+
+ RELEASE_LOCK( ReplConfigLock );
+
+ return TRUE;
+
+} // end of InitMsgBuf
+
+
+VOID
+InitMsgSend(
+ IN DWORD msg_type
+ )
+/*++
+
+Routine Description:
+ Plugs msg_type into MsgBuf, and initialize UpdateList;
+
+Arguments:
+ msg_type: SYNC / GUARD / PULSE.
+
+Return Value:
+ none
+
+--*/
+{
+ //
+ // set message type in buffer
+ //
+
+ SmbPutUshort( (LPWORD)MsgBuf, (WORD) msg_type);
+
+ //
+ // initialize UpdateList
+ //
+
+ if(UpdateList.count != 0) {
+
+ FreeUpdateList(SAVE);
+
+ }
+
+}
+
+VOID
+ReplMasterFreeMsgBuf(
+ VOID
+ )
+/*++
+
+Routine Description:
+ free update buffers. called from pulser thread main routine
+
+Arguments:
+ none
+
+Return Value:
+ none
+
+--*/
+{
+ //
+ // Free update records.
+ //
+
+ if(UpdateList.count != 0) {
+
+ FreeUpdateList(NO_SAVE);
+
+ }
+
+ //
+ // Free FreeList record memory
+ //
+
+ if(FreeList.count != 0) {
+
+ FreeFreeList();
+
+ }
+
+}
+
+VOID
+AddToMsg(
+ IN PMASTER_LIST_REC rec,
+ IN DWORD opcode
+ )
+/*++
+
+Routine Description:
+
+ Add Update record to UpdateList.
+
+Arguments:
+
+ rec - points to master_list_rec which holds dir name and status
+ variables. AddToMsg() assumes that the caller has at least a
+ read-only lock on this.
+
+ opcode - START / UPDATE / END / PULSE.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PUPDATE_REC UpdateRec;
+
+
+ UpdateRec = GetFreeUpdateRec();
+
+ if( UpdateRec == NULL ) {
+
+ NetpKdPrint(( PREFIX_REPL_MASTER
+ "ERROR_NOT_ENOUGH_MEMORY error in AddToMsg Function.\n" ));
+
+ // BUGBUG: Log this?
+ return;
+
+ }
+
+ UpdateRec->NextRec = NULL;
+ UpdateRec->PrevRec = NULL;
+
+ UpdateRec->UpdateEntry.opcode = opcode;
+ UpdateRec->UpdateEntry.checksum = rec->checksum;
+ UpdateRec->UpdateEntry.count = rec->count;
+ UpdateRec->UpdateEntry.integrity = rec->integrity;
+ UpdateRec->UpdateEntry.extent = rec->extent;
+
+ (void) STRCPY(UpdateRec->UpdateEntry.dir_name, rec->dir_name);
+
+
+ IF_DEBUG(PULSER) {
+
+ NetpKdPrint(( PREFIX_REPL_MASTER
+ "AddToMsg() is calling AddUpdateRec() "
+ "to add record for dir: " FORMAT_LPTSTR "\n", rec->dir_name ));
+
+ }
+
+ AddUpdateRec( UpdateRec );
+ NetpAssert( UpdateList.count > 0 );
+
+} // end of AddToMsg
+
+
+VOID
+MsgSend(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ Sends all update info in UpdateList to clients, via second class mail slot
+ to clients. Since slot size is limited (440 - sizeof(mailslot_name)),
+ must use multiple messages.
+
+ REQUIRES
+
+ 1. MAX_2_MSLOT_SIZE defines effective max msg size.
+ 2. UpdateList has data organized as described above.
+ 3. MsgBuf has header info initialized by InitMsgBuf.
+
+ ENSURES:
+
+ 1. All data is sent in (multiple) second class mailslot messages.
+
+ MODIFIES: None.
+
+Arguments:
+
+ none
+
+Return Value:
+
+ none
+
+Threads:
+
+ Only called by pulser thread.
+
+--*/
+{
+ DWORD i;
+ DWORD NumRecs, FitSize;
+ PUPDATE_REC UpdateRec;
+ PUPDATE_REC NextRec;
+ PPACK_SYNCMSG SyncMsg;
+ LPBYTE Where;
+ DWORD Len;
+
+
+ PPACK_MSG_STATUS_REC StartMsgRec;
+ PPACK_MSG_STATUS_REC MsgRec;
+
+ IF_DEBUG( PULSER ) {
+ NetpKdPrint(( PREFIX_REPL_MASTER
+ "MsgSend: initial count is " FORMAT_DWORD ".\n",
+ UpdateList.count ));
+ }
+
+ while(UpdateList.count != 0) {
+
+ // determine the number of records that can fit in the MsgBuf
+
+ NumRecs = GetNumRecFitIn( sizeof(MsgBuf) , &FitSize);
+ NetpAssert( FitSize <= sizeof(MsgBuf) );
+ NetpAssert( FitSize <= MAX_2_MSLOT_SIZE );
+ if (NumRecs > 0) {
+ NetpAssert( FitSize > 0 );
+ }
+
+
+ SyncMsg = (PPACK_SYNCMSG) MsgBuf;
+
+ SmbPutUshort( (LPWORD) ( &(SyncMsg->update_count) ), (WORD) NumRecs);
+
+ StartMsgRec = (PPACK_MSG_STATUS_REC)
+ (MsgBuf + sizeof(PACK_SYNCMSG));
+
+ // copy fixed portion of status recs.
+
+ for(i = 0, MsgRec = StartMsgRec, UpdateRec = UpdateList.Head;
+ ( i < NumRecs );
+ i++, MsgRec++, UpdateRec = UpdateRec->NextRec ) {
+
+ SmbPutUshort( (LPWORD) ( &(MsgRec->dir_name_offset) ), 0 );
+ // offset field set to zero initially
+
+ SmbPutUshort( (LPWORD) ( &(MsgRec->opcode) ),
+ (WORD) UpdateRec->UpdateEntry.opcode );
+
+ SmbPutUlong( (LPDWORD) ( &(MsgRec->checksum) ),
+ UpdateRec->UpdateEntry.checksum );
+
+ SmbPutUshort( (LPWORD) ( &(MsgRec->count) ),
+ (WORD) UpdateRec->UpdateEntry.count );
+
+ SmbPutUshort( (LPWORD) ( &(MsgRec->integrity) ),
+ (WORD) UpdateRec->UpdateEntry.integrity );
+
+ SmbPutUshort( (LPWORD) ( &(MsgRec->extent) ),
+ (WORD) UpdateRec->UpdateEntry.extent );
+
+ }
+
+ Where = (LPBYTE)( StartMsgRec + NumRecs );
+
+ // copy Unicode sender name
+
+ Len = ( wcslen( ReplGlobalUnicodeComputerName ) + 1) * sizeof(WCHAR);
+
+ NetpMoveMemory(Where, (LPBYTE) ReplGlobalUnicodeComputerName, Len);
+
+ Where += Len;
+
+ // copy Unicode domain name.
+
+ Len = ( wcslen( ReplGlobalUnicodeDomainName ) + 1) * sizeof(WCHAR);
+
+ NetpMoveMemory(Where, (LPBYTE) ReplGlobalUnicodeDomainName, Len);
+
+ Where += Len;
+
+ // copy variables portions ..
+
+ for(i = 0, MsgRec = StartMsgRec, UpdateRec = UpdateList.Head;
+ ( i < NumRecs );
+ i++, MsgRec++, UpdateRec = UpdateRec->NextRec ) {
+
+ SmbPutUshort( (LPWORD) ( &(MsgRec->dir_name_offset) ),
+ (WORD) ( (LPBYTE)Where - (LPBYTE)MsgBuf) );
+
+ if( RMGlobalCompatibilityMode ) {
+
+#if defined(DBCS) && defined(UNICODE) // MsgSend()
+ NetpCopyWStrToStrDBCS(
+ Where, // dest
+ UpdateRec->UpdateEntry.dir_name ); // src
+#else
+ NetpCopyTStrToStr(
+ Where, // dest
+ UpdateRec->UpdateEntry.dir_name ); // src
+#endif // defined(DBCS)
+
+ Len = strlen( (LPSTR) Where) + 1;
+
+ Where += Len;
+
+ } else {
+
+ *(PCHAR)Where = '\0';
+
+ Where += sizeof(CHAR);
+ }
+
+ // copy Unicode name
+
+ Len = ( STRLEN( UpdateRec->UpdateEntry.dir_name ) + 1 ) *
+ sizeof(WCHAR);
+
+ NetpCopyTStrToUnalignedWStr(
+ Where, // dest
+ UpdateRec->UpdateEntry.dir_name ); // src
+
+ Where += Len;
+
+ }
+
+ // add NT message token at the end ..
+
+ SmbPutUlong( (LPDWORD)Where, NT_MSG_TOKEN );
+
+ SendToAll((LPBYTE)MsgBuf, FitSize);
+
+
+ IF_DEBUG(PULSER) {
+
+ NetpKdPrint(( PREFIX_REPL_MASTER
+ "MsgSend() sent the following "
+ FORMAT_DWORD " record(s):\n", NumRecs ));
+
+ }
+
+ // delete Update records
+ for(i = 0, UpdateRec = UpdateList.Head;
+ ( i < NumRecs ) && ( UpdateRec != NULL );
+ i++, UpdateRec = NextRec ) {
+
+ IF_DEBUG(PULSER) {
+
+ NetpKdPrint(( PREFIX_REPL_MASTER
+ " " FORMAT_LPTSTR "\n",
+ UpdateRec->UpdateEntry.dir_name ));
+
+ }
+
+ NextRec = UpdateRec->NextRec;
+ DeleteUpdateRec(UpdateRec);
+
+ }
+ }
+
+} // end MsgSend
+
+
+DBGSTATIC VOID
+SendToAll(
+ IN LPBYTE MsgBuffer,
+ IN DWORD MsgSize
+ )
+/*++
+
+Routine Description:
+
+ Sends message to all clients listed in client_list.
+
+ ENTRY: pointer to message buffer.
+
+ REQUIRES:
+ 1. MsgBuf has all header entries filled in.
+ 2. RMGlobalImportList has RMGlobalImportCount valid entries.
+
+ ENSURES: 1. Message sent all clients in client list.
+
+ MODIFIES: None.
+
+Arguments:
+
+ msgp - pointer to message buffer
+
+Return Value:
+
+ none
+
+Threads:
+
+ Only called by pulser thread.
+
+--*/
+{
+ NET_API_STATUS ApiStatus;
+ TCHAR destin[FULL_SLOT_NAME_SIZE];
+ DWORD i;
+
+ NetpAssert( MsgBuffer != NULL );
+ NetpAssert( MsgSize != 0 );
+
+ IF_DEBUG( PULSER ) {
+ NetpKdPrint(( PREFIX_REPL_MASTER
+ "(Pulser) outgoing message follows:\n" ));
+ NetpDbgHexDump( MsgBuffer, MsgSize );
+ }
+
+ ACQUIRE_LOCK_SHARED( ReplConfigLock ); // get read-only lock here.
+
+ //
+ // stick in leading double slashes for computer name.
+ //
+
+
+ if (RMGlobalImportCount != 0) {
+
+ for (i = 0; i < RMGlobalImportCount; i++) {
+
+ NetpAssert( RMGlobalImportList[i] != NULL );
+ NetpAssert( (*(RMGlobalImportList[i])) != TCHAR_EOS );
+
+ (void) STRCPY(destin, SLASH_SLASH);
+ (void) STRCAT(destin, RMGlobalImportList[i]);
+ (void) STRCAT(destin, (LPTSTR) CLIENT_SLOT_NAME);
+
+ ApiStatus = NetpReplWriteMail(destin, MsgBuffer, MsgSize);
+ if (ApiStatus != NO_ERROR) {
+
+ NetpAssert( ApiStatus != ERROR_INVALID_FUNCTION );
+
+ AlertLogExit(ALERT_ReplSysErr,
+ NELOG_ReplNetErr,
+ ApiStatus,
+ NULL,
+ NULL,
+ NO_EXIT);
+
+ // Don't forget to release lock...
+ }
+
+ IF_DEBUG(PULSER) {
+
+ NetpKdPrint(( PREFIX_REPL_MASTER
+ "SendToall() sent a mailslot message of size "
+ FORMAT_DWORD " to " FORMAT_LPTSTR " successfully.\n",
+ MsgSize, destin ));
+
+ }
+
+ }
+
+ } else { // RMGlobalImportCount == 0 - send to domain name
+
+ NetpAssert( ReplGlobalDomainName != NULL );
+ NetpAssert( (*ReplGlobalDomainName) != TCHAR_EOS );
+
+ (void) STRCPY(destin, SLASH_SLASH);
+ (void) STRCAT(destin, ReplGlobalDomainName);
+ (void) STRCAT(destin, (LPTSTR) CLIENT_SLOT_NAME);
+
+ ApiStatus = NetpReplWriteMail(destin, MsgBuffer, MsgSize);
+ if (ApiStatus != NO_ERROR) {
+ NetpAssert( ApiStatus != ERROR_INVALID_FUNCTION );
+ AlertLogExit(ALERT_ReplSysErr,
+ NELOG_ReplNetErr,
+ ApiStatus,
+ NULL,
+ NULL,
+ NO_EXIT);
+
+ // Don't forget to release lock...
+
+ }
+
+ IF_DEBUG(PULSER) {
+
+ NetpKdPrint(( PREFIX_REPL_MASTER
+ "SendToall() sent a mailslot message of size " FORMAT_DWORD
+ " to " FORMAT_LPTSTR " successfully.\n",
+ MsgSize, destin ));
+
+ }
+
+ }
+
+ RELEASE_LOCK( ReplConfigLock );
+
+}
+
+DBGSTATIC VOID
+AddUpdateRec(
+ IN OUT PUPDATE_REC UpdateRec
+ )
+/*++
+
+Routine Description:
+
+ Add a update record to UpdateList.
+
+Arguments:
+
+ UpdateRec - pointer to new update rec.
+
+
+Return Value:
+
+ none
+
+ ASSUME:
+
+ NextRec and PrevRec fields are initialized to NULL before
+ calling this function.
+
+--*/
+{
+
+ // special case, if list is empty
+ if( UpdateList.count == 0) {
+
+ // add new record
+
+ UpdateList.Head = UpdateRec;
+ UpdateList.Tail = UpdateRec;
+
+ // set counter
+
+ UpdateList.count = 1;
+
+ } else {
+
+ // add new record to end of list
+
+ // adjust new record pointer
+
+ UpdateRec->PrevRec = UpdateList.Tail;
+
+ // adjust tail record pointer
+
+ UpdateList.Tail->NextRec = UpdateRec;
+
+ // adjust UpdateList rec pointer
+
+ UpdateList.Tail = UpdateRec;
+
+ // adjust counter
+
+ UpdateList.count++;
+ }
+
+ NetpAssert( UpdateList.count > 0 );
+}
+
+DBGSTATIC VOID
+DeleteUpdateRec(
+ IN PUPDATE_REC UpdateRec
+ )
+/*++
+
+Routine Description:
+
+ Delete a record from UpdateList
+
+Arguments:
+
+ UpdateRec - pointer to the record to be removed from list
+
+Return Value:
+
+ none
+
+ ASSUME: the given record is assumed to be in the UpdateList.
+
+--*/
+{
+ // handle special cases first
+
+ if( ( UpdateRec->NextRec == NULL ) &&
+ ( UpdateRec->PrevRec == NULL ) ) {
+
+ // last record removed from the list
+
+ if( UpdateList.count != 1 ) {
+
+ // assertion failed
+
+ NetpKdPrint(( PREFIX_REPL_MASTER
+ "Assertion failed in DeleteUpdateRec function, "
+ "expected update list count 1, it is "
+ FORMAT_HEX_DWORD ".\n", UpdateList.count ));
+
+ return;
+ }
+
+ if( ( UpdateList.Head != UpdateRec ) ||
+ ( UpdateList.Tail != UpdateRec ) ) {
+
+ // assertion failed
+
+ NetpKdPrint(( PREFIX_REPL_MASTER
+ "Assertion failed in DeleteUpdateRec function, "
+ "bogus UpdateRec pointer.\n" ));
+
+ return;
+ }
+
+ // adjust pointers
+
+ UpdateList.Head = NULL;
+ UpdateList.Tail = NULL;
+ UpdateList.count = 0;
+
+ // free update record
+ FreeUpdateRec( UpdateRec, SAVE );
+
+ return;
+ }
+
+ if( UpdateRec->NextRec == NULL ) {
+
+ // record removed from tail
+
+ if( ( UpdateList.Tail != UpdateRec ) ) {
+
+ // assertion failed
+
+ NetpKdPrint(( PREFIX_REPL_MASTER
+ "Assertion failed in DeleteUpdateRec function, "
+ "bogus UpdateRec pointer.\n" ));
+
+ return;
+ }
+
+ // adjust pointers
+
+ UpdateList.Tail = UpdateRec->PrevRec;
+ UpdateRec->PrevRec->NextRec = NULL;
+ UpdateList.count--;
+
+ // free update record
+ FreeUpdateRec( UpdateRec, SAVE );
+
+ return;
+
+ }
+
+ if( UpdateRec->PrevRec == NULL ) {
+
+ // record removed from head
+
+ if( ( UpdateList.Head != UpdateRec ) ) {
+
+ // assertion failed
+
+ NetpKdPrint(( PREFIX_REPL_MASTER
+ "Assertion failed in DeleteUpdateRec function, "
+ "bogus UpdateRec pointer.\n" ));
+
+ return;
+ }
+
+ // set UpdateList sentinel
+
+ UpdateList.Head = UpdateRec->NextRec;
+ UpdateRec->NextRec->PrevRec = NULL;
+ UpdateList.count--;
+
+ // free update record
+ FreeUpdateRec( UpdateRec, SAVE );
+
+ return;
+
+ }
+
+ // record removed from middle
+
+ if( ( UpdateRec->NextRec->PrevRec != UpdateRec ) ||
+ ( UpdateRec->PrevRec->NextRec != UpdateRec ) ) {
+
+ // assertion failed
+
+ NetpKdPrint(( PREFIX_REPL_MASTER
+ "Assertion failed in DeleteUpdateRec function, "
+ "bogus UpdateRec pointer.\n" ));
+
+ return;
+ }
+
+ UpdateRec->NextRec->PrevRec = UpdateRec->PrevRec;
+ UpdateRec->PrevRec->NextRec = UpdateRec->NextRec;
+
+ UpdateList.count--;
+
+ // free update record
+ FreeUpdateRec( UpdateRec, SAVE );
+
+ return;
+
+}
+
+DBGSTATIC PUPDATE_REC
+GetFreeUpdateRec(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ Get a free update rec buffer either from free list or allocate new memory.
+
+Arguments:
+
+ none
+
+
+Return Value:
+
+ return a pointer to new update record, or NULL if out of memory.
+
+ Note - FreeList is a singlely linked list. so FreeList.Tail field and
+ the PrevRec field in FreeRecs are unused.
+
+--*/
+{
+ PUPDATE_REC FreeRecord;
+
+ if( FreeList.count > 0 ) {
+
+ // get a record from free list
+
+ FreeRecord = FreeList.Head;
+ NetpAssert( FreeRecord != NULL );
+
+ FreeList.Head = FreeRecord->NextRec;
+
+ FreeList.count--;
+
+ return FreeRecord;
+ }
+
+ // allote new block of memory
+
+ FreeRecord = (PUPDATE_REC) NetpMemoryAllocate( sizeof(UPDATE_REC) );
+
+ return FreeRecord;
+
+}
+
+DBGSTATIC VOID
+FreeUpdateRec(
+ IN PUPDATE_REC UpdateRec,
+ IN DWORD SaveFlag
+ )
+/*++
+
+Routine Description:
+
+ Free an update record, this record will either be put in FreeList or
+ the memory is freed back to system.
+
+Arguments:
+
+ UpdateRec - pointer to the record that should be freed
+
+Return Value:
+
+ none
+
+ Note - FreeList is a singlely linked list. so FreeList.tail field and
+ the PrevRec field in FreeRecs are unused.
+
+--*/
+{
+ if( (SaveFlag == NO_SAVE) || ( FreeList.count >= MAX_FREE_RECORDS ) ) {
+
+ // free this update record block memory to system.
+
+ NetpMemoryFree(UpdateRec);
+
+ return;
+ }
+
+ // add this block to free list
+
+ UpdateRec->NextRec = FreeList.Head;
+ UpdateRec->PrevRec = NULL; // unused
+ FreeList.Head = UpdateRec;
+
+ FreeList.count++;
+
+ return;
+}
+
+DBGSTATIC VOID
+FreeUpdateList(
+ IN DWORD SaveFlag
+ )
+/*++
+
+Routine Description:
+
+ Free up the all update records from UpdateList
+
+Arguments:
+
+
+Return Value:
+
+ none
+
+--*/
+{
+
+ PUPDATE_REC UpdateRec, NextRec;
+
+ UpdateRec = UpdateList.Head;
+
+ while(UpdateRec != NULL) {
+
+ NextRec = UpdateRec->NextRec;
+
+ FreeUpdateRec( UpdateRec, SaveFlag );
+
+ UpdateRec = NextRec;
+ }
+
+ UpdateList.Head = NULL;
+ UpdateList.Tail = NULL;
+ UpdateList.count = 0;
+
+}
+
+
+DBGSTATIC VOID
+FreeFreeList(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ Free up the memory blocks in FreeList.
+
+Arguments:
+
+ none
+
+Return Value:
+
+ none
+
+ Note - FreeList is a singlely linked list. so FreeList.tail field and
+ the PrevRec field in FreeRecs are unused.
+
+--*/
+{
+
+ PUPDATE_REC FreeRec, NextRec;
+
+ FreeRec = FreeList.Head;
+
+ while( FreeRec != NULL ) {
+
+ NextRec = FreeRec->NextRec;
+
+ NetpMemoryFree( FreeRec );
+
+ FreeRec = NextRec;
+ }
+
+ FreeList.Head = NULL;
+ FreeList.Tail = NULL;
+ FreeList.count = 0;
+
+}
+
+DBGSTATIC DWORD
+GetNumRecFitIn(
+ IN DWORD BufSize,
+ OUT LPDWORD FitSize
+ )
+/*++
+
+Routine Description:
+
+ Compute the number of update records that will fit in the buffer. If there
+ are fewer update records in UpdateList than the number of records that the
+ buffer will fit in, it will return that count.
+
+Arguments:
+
+ BufSize - buffer size
+
+ FitSize - the exact size of the buffer required to fill in number records
+ that is returned.
+
+Return Value:
+
+ return number of records that can be filled in the buffer.
+
+
+--*/
+{
+
+ DWORD LocalFitSize;
+ DWORD NumMsgs;
+ PUPDATE_REC UpdateRec;
+ DWORD NextRecSize;
+
+ NetpAssert( BufSize <= MAX_2_MSLOT_SIZE );
+
+ // space required for header portion of data
+
+ LocalFitSize = sizeof(PACK_SYNCMSG) +
+ ( wcslen( ReplGlobalUnicodeComputerName ) + 1 +
+ wcslen( ReplGlobalUnicodeDomainName ) + 1
+ ) * sizeof(WCHAR) +
+ sizeof(NT_MSG_TOKEN);
+
+ if( BufSize < LocalFitSize ) {
+
+ // insufficient buffer size
+
+ NetpKdPrint(( PREFIX_REPL_MASTER
+ " Insufficient message buffer size " FORMAT_DWORD ".\n",
+ BufSize ));
+
+ *FitSize = 0;
+
+ return ( 0 );
+ }
+
+ UpdateRec = UpdateList.Head;
+
+ NumMsgs = 0;
+
+ while( UpdateRec != NULL ) {
+
+ // Fixed portion + variable portion.
+ // ( Unicode string + Ansi string )
+
+ NextRecSize = ( sizeof(PACK_MSG_STATUS_REC) ) +
+ ( ( STRLEN(UpdateRec->UpdateEntry.dir_name) + 1 ) *
+ (sizeof(WCHAR) + sizeof(CHAR) ) );
+
+ if( BufSize < ( LocalFitSize + NextRecSize ) ) {
+
+ if( NumMsgs == 0) {
+
+ // insufficient buffer size
+
+ NetpKdPrint(( PREFIX_REPL_MASTER
+ "GetNumRecFitIn: "
+ "Insufficient message buffer size " FORMAT_DWORD
+ " for single update \n",
+ BufSize ));
+
+ }
+
+ break;
+ }
+
+ LocalFitSize += NextRecSize;
+
+ NumMsgs++;
+
+ UpdateRec = UpdateRec->NextRec;
+ }
+
+ *FitSize = LocalFitSize;
+
+ return ( NumMsgs );
+
+}
diff --git a/private/net/svcdlls/repl/server/pulser.c b/private/net/svcdlls/repl/server/pulser.c
new file mode 100644
index 000000000..61696d030
--- /dev/null
+++ b/private/net/svcdlls/repl/server/pulser.c
@@ -0,0 +1,1556 @@
+/*++
+
+Copyright (c) 1987-1993 Microsoft Corporation
+
+Module Name:
+ pulser.c
+
+Abstract:
+ Contains Pulse thread - does actual work for REPL Master.
+
+Author:
+ Ported from Lan Man 2.x
+
+Environment:
+ Contains NT-specific code.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+ 04/11/89 (yuv)
+ initial coding
+
+ 10/07/91 (madana)
+ ported to NT. Converted to NT style.
+ 26-Dec-1991 JohnRo
+ Added equate for unguarded value of master_list_rec.grd_count.
+ 20-Jan-1992 JohnRo
+ Changed file name from repl.h to replgbl.h to avoid MIDL conflict.
+ Changed which routines lock master list to allow duplicate checks.
+ Added debug print of thread ID.
+ Changed to use NetLock.h (allow shared locks, for one thing).
+ Added RemoveMasterRecForDirName() for use by NetrReplExportDirDel().
+ Added locktime and time_of_first_lock fields to master record.
+ Added RMGlobalMasterListHeader (was master_list_header) and
+ RMGlobalMasterListCount.
+ Made changes suggested by PC-LINT.
+ 24-Jan-1992 JohnRo
+ Use ReplConfigRead() instead of GetReplIni().
+ P_ globals are now called ReplGlobal variables in replgbl.h.
+ Changed to use LPTSTR etc.
+ 09-Feb-1992 JohnRo
+ Set up to dynamically change role.
+ Use FORMAT equates.
+ 17-Feb-1992 JohnRo
+ CheckForDelDirs() should get a shared lock on master list.
+ Added some debug messages.
+ 05-Mar-1992 JohnRo
+ Fix locking of master list.
+ Lock global copy of config data.
+ Minor source cleanup.
+ 15-Mar-1992 JohnRo
+ Change RemoveMasterRecForDirName()'s lock requirements to allow updating
+ registry with new values.
+ 23-Mar-1992 JohnRo
+ Renamed many ReplGlobal vars to ReplConfig vars.
+ Avoid RemoveFromList() vs. CheckForDelDirs() lock conflict.
+ Use integrity and extent equates in <lmrepl.h>.
+ 19-Aug-1992 JohnRo
+ RAID 2115: repl svc should wait while stopping or changing role.
+ Use PREFIX_ equates.
+ 20-Nov-1992 JohnRo
+ RAID 1537: Repl APIs in wrong role kill svc.
+ Fix remote repl admin.
+ Added some debug checks.
+ 28-Nov-1992 JohnRo
+ Repl should use filesystem change notify.
+ 12-Jan-1993 JohnRo
+ RAID 7064: replicator exporter skips new data (change notify while
+ dir locked is lost).
+ 14-Jan-1993 JohnRo
+ RAID 7053: locked trees added to pulse msg. (Actually fix all
+ kinds of remote lock handling.)
+ 12-Feb-1993 JohnRo
+ RAID 10716: msg timing and checksum problems (also downlevel msg bug).
+ RAID 7988: Downlevel importer might not see lock file.
+ Made changes suggested by PC-LINT 5.0
+ 25-Jan-1993 JohnRo
+ RAID 12237: replicator tree depth exceeded.
+ 08-Mar-1993 JohnRo
+ RAID 7988: downlevel repl importer might not see lock file
+ for new first-level dir.
+ 25-Mar-1993 JohnRo
+ RAID 4267: Replicator has problems when work queue gets large.
+ 09-Apr-1993 JohnRo
+ RAID 5959: export side gets long hourglass too.
+ Use NetpKdPrint() where possible.
+ 26-Apr-1993 JimKel
+ RAID 7298: repl stops when it didn't get _access to an export dir.
+ 27-May-1993 JohnRo
+ RAID 11103: repl svc doesn't handle time being set back.
+ 10-Jun-1993 JohnRo
+ RAID 13080: Allow repl between different timezones.
+ 18-Jun-1993 JohnRo
+ RAID 13594: repl exporter does not handle timezone changes (must
+ recompute all checksums).
+ 22-Nov-1994 JonN
+ RAID 27287: Memory leak in Replicator (LMREPL.EXE)
+ ReplEnableChangeNotify is being called even though there is still
+ an outstanding notify request on ChangeHandle, this causes multiple
+ requests to build up and leaks paged pool. Added boolean
+ NotifyPending to keep this at only a single request at a time.
+
+
+--*/
+
+#include <nt.h> // NT definitions (needed by chngnot.h)
+#include <ntrtl.h> // NT runtime library definitions (needed by nturtl.h)
+#include <nturtl.h> // Needed to have windows.h and nt.h co-exist.
+#include <windows.h> // DWORD, IN, File APIs, etc.
+
+#include <lmcons.h>
+#include <lmerr.h>
+#include <netlib.h>
+#include <lmerrlog.h>
+#include <alertmsg.h>
+#include <lmrepl.h> // REPL_EXTENT_FILE, etc.
+#include <netlib.h>
+#include <netdebug.h> // NetpKdPrint(), FORMAT_ equates.
+#include <netlock.h> // Lock data types, functions, and macros.
+#include <prefix.h> // PREFIX_ equates.
+#include <thread.h> // FORMAT_NET_THREAD_ID, NetpCurrentThread().
+#include <timelib.h> // NetpLocalTimeZoneOffset().
+#include <tstr.h> // TCHAR_FWDSLASH, etc.
+
+// repl headers
+
+#include <chngnot.h> // ReplSetupChangeNotify, REPL_CHANGE_NOTIFY_HANDLE, etc
+#include <expdir.h> // ExportDirWriteConfigData(), etc.
+#include <repldefs.h> // IF_DEBUG(), etc.
+#include <replgbl.h> // ReplGlobal and ReplConfig variables.
+#include <iniparm.h> // DEFAULT_INTEGRITY, etc.
+#include <master.h>
+#include <pulser.h> // PulserTimeOfLastNotifyOrUnlock, etc.
+#include <masproto.h>
+#include <replp.h>
+
+#define FOREVER 1
+
+// G L O B A L S:
+
+// lists initially empty
+LPMASTER_LIST_REC RMGlobalMasterListHeader = NULL; // Locked by RMGlobalListLock
+DWORD RMGlobalMasterListCount = 0; // Locked by RMGlobalListLock.
+
+//
+// Time (in secs since 1970) of last export tree chng or unlock.
+// (We can't checksum a tree while it is locked.)
+// This is set and used by PulserThread(), and set by NetrReplExportDirUnlock().
+//
+DWORD PulserTimeOfLastNotifyOrUnlock = 0; // Locked by RMGlobalListLock.
+
+
+DBGSTATIC BOOL guard_needed;
+DBGSTATIC DWORD PulserTimeOfSyncStart;
+DBGSTATIC DWORD sync_time;
+DBGSTATIC DWORD guard_time;
+DBGSTATIC TCHAR path_buf[PATHLEN+1];
+
+
+#define WAIT_N_SECONDS_AND_CATCH_CHANGES( totalSecondsToWait ) \
+ { \
+ DWORD CycleStart = NetpReplTimeNow(); \
+ DWORD CycleEnd = CycleStart + totalSecondsToWait; \
+ DWORD SecondsLeftToWait = totalSecondsToWait; \
+ NetpAssert( totalSecondsToWait > 0 ); \
+ NetpAssert( CycleEnd > CycleStart ); \
+ /*lint -save -e716 */ /* disable warnings for while(TRUE) */ \
+ while (TRUE) { \
+ DWORD TimeWaitEnded; \
+ \
+ if (!NotifyPending) { \
+ /* Enable this pass of change notify... */ \
+ \
+ ApiStatus = ReplEnableChangeNotify( ChangeHandle ); \
+ if (ApiStatus != NO_ERROR) { \
+ NetpKdPrint(( PREFIX_REPL_MASTER \
+ "PulserThread got " FORMAT_API_STATUS \
+ " from ReplEnableChangeNotify.\n", ApiStatus )); \
+ goto Cleanup; \
+ } \
+ NotifyPending = TRUE; \
+ } \
+ \
+ NetpAssert( SecondsLeftToWait > 0 ); \
+ WaitStatus = WaitForMultipleObjects( \
+ 2, /* handle count */ \
+ WaitHandles, \
+ FALSE, /* don't wait for all handles */ \
+ SecondsLeftToWait * 1000); /* timeout left (millisecs) */ \
+ \
+ /* Check if this thread should exit. */ \
+ \
+ TimeWaitEnded = NetpReplTimeNow(); \
+ if ( WaitStatus == 0 ) { /* termination handle */ \
+ Terminating = TRUE; \
+ break; /* exit do-while timeout loop */ \
+ } else if (WaitStatus == WAIT_TIMEOUT) { \
+ if (TimeWaitEnded < \
+ (CycleStart-BACKWARDS_TIME_CHNG_ALLOWED_SECS) ) { \
+ /* Time moved back a bunch, maybe a month. */ \
+ /* Cause first-time resync, reset checksum times, etc */ \
+ FirstTime = TRUE; \
+ LastTimeZoneOffsetSecs = NetpLocalTimeZoneOffset(); \
+ goto RestartCycle; \
+ } \
+ break; /* exit do-while timeout loop */ \
+ } \
+ NetpAssert( WaitStatus == 1 ); /* change notify handle */ \
+ NotifyPending = FALSE; \
+ ACQUIRE_LOCK( RMGlobalListLock ); \
+ /* BUGBUG: set all locks_fixed to FALSE? */ \
+ PulserTimeOfLastNotifyOrUnlock = TimeWaitEnded; \
+ RELEASE_LOCK( RMGlobalListLock ); \
+ \
+ if (TimeWaitEnded >= CycleEnd) { \
+ break; /* waited too long or clock moved forward */ \
+ } else if (TimeWaitEnded < \
+ (CycleStart-BACKWARDS_TIME_CHNG_ALLOWED_SECS) ) { \
+ /* Time moved back a bunch, maybe a month. */ \
+ /* Cause first-time resync, reset checksum times, etc */ \
+ FirstTime = TRUE; \
+ LastTimeZoneOffsetSecs = NetpLocalTimeZoneOffset(); \
+ goto RestartCycle; \
+ } \
+ SecondsLeftToWait = TimeWaitEnded - CycleStart; \
+ NetpAssert( SecondsLeftToWait > 0 ); \
+ /* do it again */ \
+ \
+ } /* while TRUE */ \
+ /*lint -restore */ /* re-enable warnings for while(TRUE) */ \
+ }
+
+
+DWORD
+PulserThread(
+ IN LPVOID parm
+ )
+/*++
+
+Routine Description:
+ Spawned by Master process, takes care of all replication master's timing.
+
+Arguments:
+ parm : never used here.
+
+Return Value:
+ does not return at all.
+
+Threads:
+ Only called by pulser thread.
+
+--*/
+{
+
+ NET_API_STATUS ApiStatus;
+ LPREPL_CHANGE_NOTIFY_HANDLE ChangeHandle = NULL;
+ BOOL ConfigLocked = FALSE;
+ BOOL FirstTime = TRUE;
+ BOOL NotifyPending = FALSE;
+ LONG LastTimeZoneOffsetSecs = NetpLocalTimeZoneOffset();
+ DWORD pulse_count = 0;
+ LPTSTR path_p;
+ DWORD path_len;
+ DWORD SyncTimeUsed;
+ BOOL Terminating = FALSE;
+ DWORD TimeOfLastChecksum = 0;// Time (secs since 1970) when we summed tree
+ HANDLE WaitHandles[2];
+
+ UNREFERENCED_PARAMETER(parm);
+
+ ACQUIRE_LOCK_SHARED( ReplConfigLock );
+ ConfigLocked = TRUE;
+
+ IF_DEBUG( PULSER ) {
+ NetpKdPrint(( PREFIX_REPL_MASTER
+ "PulserThread: thread ID is "
+ FORMAT_NET_THREAD_ID ", max pulse is " FORMAT_DWORD ".\n",
+ NetpCurrentThread(), ReplConfigPulse ));
+ }
+ NetpAssert( ReplIsPulseValid( ReplConfigPulse ) );
+
+ //
+ // Arrange to use change notify on export path.
+ //
+ ApiStatus = ReplSetupChangeNotify(
+ ReplConfigExportPath,
+ &ChangeHandle );
+ if (ApiStatus != NO_ERROR) {
+ goto Cleanup;
+ }
+
+ //
+ // converts times from minutes to seconds.
+ //
+
+ guard_time = ReplConfigGuardTime * 60;
+ sync_time = ReplConfigInterval * 60;
+
+ path_p = path_buf;
+ (void) STRCPY(path_p, ReplConfigExportPath);
+
+ RELEASE_LOCK( ReplConfigLock );
+ ConfigLocked = FALSE;
+
+ //
+ // append a slash except for the case of "c:\" or "c:/".
+ //
+
+ path_len = STRLEN(path_p);
+ if ((path_p[path_len - 1] != TCHAR_BACKSLASH)
+ && (path_p[path_len - 1] != TCHAR_FWDSLASH))
+
+ (void) STRCPY(path_p + path_len, SLASH);
+
+ WaitHandles[0] = ReplGlobalMasterTerminateEvent;
+ WaitHandles[1] = ChangeHandle->WaitableHandle;
+
+ /*lint -save -e716 */ // disable warnings for while(TRUE)
+ while (FOREVER) {
+
+ DWORD CurrentTime;
+ DWORD WaitStatus;
+
+RestartCycle:
+
+ IF_DEBUG(PULSER) {
+
+ NetpKdPrint(( PREFIX_REPL_MASTER
+ "Pulser is starting its cycle..., "
+ "pulse_count is " FORMAT_DWORD "\n",
+ pulse_count ));
+
+ }
+
+ guard_needed = FALSE;
+
+ PulserTimeOfSyncStart = NetpReplTimeNow();
+
+ if (FirstTime) {
+ TimeOfLastChecksum = PulserTimeOfSyncStart;
+ }
+
+ ACQUIRE_LOCK_SHARED( RMGlobalListLock );
+ if ( (TimeOfLastChecksum <= PulserTimeOfLastNotifyOrUnlock)
+ || (PulserTimeOfLastNotifyOrUnlock > PulserTimeOfSyncStart)
+ || FirstTime ) {
+
+ RELEASE_LOCK( RMGlobalListLock );
+ TimeOfLastChecksum = PulserTimeOfSyncStart;
+ SyncUpdate(); // Checks for changes, if guard needed raises
+ // guard_needed
+
+ IF_DEBUG(PULSER) {
+
+ NetpKdPrint(( PREFIX_REPL_MASTER
+ "Pulser completed SyncUpdate.\n" ));
+
+ }
+ } else {
+ RELEASE_LOCK( RMGlobalListLock );
+ }
+
+ if (FirstTime) {
+ FirstTime = FALSE;
+ }
+
+ ACQUIRE_LOCK_SHARED( ReplConfigLock );
+ ConfigLocked = TRUE;
+ if (( ReplConfigPulse != 0) && ( ++pulse_count == ReplConfigPulse)) {
+ pulse_count = 0;
+ SendPulse(); // Sends Pulse.
+
+ IF_DEBUG(PULSER) {
+
+ NetpKdPrint(( PREFIX_REPL_MASTER
+ "Pulser sent pulse.\n" ));
+
+ }
+
+ }
+ RELEASE_LOCK( ReplConfigLock );
+ ConfigLocked = FALSE;
+
+
+ //
+ // Wait for guard stuff to become stable, or another change to occur.
+ //
+
+ if ((guard_needed) && (guard_time != 0)) {
+
+ WAIT_N_SECONDS_AND_CATCH_CHANGES( guard_time );
+
+ // Check if this thread should exit.
+ if (Terminating) {
+ break;
+ }
+
+ //
+ // We need to re-scan the tree, even if we didn't get a change
+ // notify this time around. (Actually,
+ // We need to send a guard message if no changes occurred.
+ // GuardUpdate will do another checksum and decide whether or
+ // not to do the guard msg for each directory. BUGBUG: We could
+ // optimize this if no change notify occurred. We would still
+ // need to send the message, but the extra checksum is unneeded.
+ //
+
+ TimeOfLastChecksum = NetpReplTimeNow();
+ GuardUpdate(); // Check if those INTEGRITY snobs have
+ // stopped fingering their files.
+ // We checksum entire tree to find out.
+
+ IF_DEBUG(PULSER) {
+
+ NetpKdPrint(( PREFIX_REPL_MASTER
+ "Pulser completed GuardUpdate.\n" ));
+
+ }
+
+ }
+
+ //
+ // Check for reasons to restart cycle:
+ // time (or date) set back
+ // timezone changed (e.g. to daylight savings time)
+ //
+
+ CurrentTime = NetpReplTimeNow();
+ if (CurrentTime < PulserTimeOfSyncStart) { // was time set back?
+ // Cause first-time resync, reset checksum times, etc.
+ FirstTime = TRUE;
+ LastTimeZoneOffsetSecs = NetpLocalTimeZoneOffset();
+ goto RestartCycle;
+ }
+
+ {
+ LONG NewTimeZoneOffsetSecs = NetpLocalTimeZoneOffset();
+ if (NewTimeZoneOffsetSecs != LastTimeZoneOffsetSecs) {
+ FirstTime = TRUE;
+ LastTimeZoneOffsetSecs = NewTimeZoneOffsetSecs;
+ goto RestartCycle;
+ }
+ }
+
+ //
+ // Check if sync_time gone, or we need some sleeping.
+ //
+
+ SyncTimeUsed = CurrentTime - PulserTimeOfSyncStart;
+ if (SyncTimeUsed < sync_time) {
+
+ WAIT_N_SECONDS_AND_CATCH_CHANGES( sync_time - SyncTimeUsed );
+
+ // Check if this thread should exit.
+
+ if ( Terminating ) {
+ break;
+ }
+ }
+
+ IF_DEBUG(PULSER) {
+
+ NetpKdPrint(( PREFIX_REPL_MASTER
+ "Pulser completed one cycle.\n" ));
+
+ }
+
+ } // while
+ /*lint -restore */ // re-enable warnings for while(TRUE)
+
+Cleanup:
+
+ if (ConfigLocked) {
+ RELEASE_LOCK( ReplConfigLock );
+ }
+
+ if (ChangeHandle != NULL) {
+ (VOID) ReplCloseChangeNotify( ChangeHandle );
+ }
+
+ // get rid of master_rec_list
+
+ ReplMasterFreeList();
+
+ // get rid of message buffer
+
+ ReplMasterFreeMsgBuf();
+
+ IF_DEBUG( PULSER ) {
+ NetpKdPrint(( PREFIX_REPL_MASTER
+ "PulserThread: exiting thread " FORMAT_NET_THREAD_ID ".\n",
+ NetpCurrentThread() ));
+
+ }
+
+ return (0);
+
+} // PulserThread
+
+
+VOID
+SyncUpdate(
+ VOID
+ )
+/*++
+
+Routine Description :
+ Does a complete scan of participating directories, creates the update msg
+ and if needed sets up the guard list
+
+Arguments :
+ none
+
+Return Value :
+ none
+
+Threads:
+ Only called by pulser thread.
+
+--*/
+{
+ NET_API_STATUS ApiStatus = NO_ERROR, TmpStatus = NO_ERROR;
+ BOOL ConfigLocked = FALSE,
+ Update = FALSE;
+ HANDLE shan = INVALID_HANDLE_VALUE;
+ WIN32_FIND_DATA search_buf;
+
+ ACQUIRE_LOCK_SHARED( ReplConfigLock );
+ ConfigLocked = TRUE;
+
+
+ //
+ // Change to Export directory - note - it might be locked by
+ // another process
+ //
+
+
+ if (!SetCurrentDirectory(ReplConfigExportPath)) {
+
+ ApiStatus = (NET_API_STATUS) GetLastError();
+ NetpAssert( ApiStatus != NO_ERROR );
+ NetpAssert( ApiStatus != ERROR_INVALID_FUNCTION );
+ goto Cleanup;
+
+ }
+
+ IF_DEBUG(PULSER) {
+
+ NetpKdPrint(( PREFIX_REPL_MASTER
+ "SyncUpdate() set current directory to " FORMAT_LPTSTR "\n",
+ ReplConfigExportPath ));
+
+ }
+
+ //
+ // reset exists for every entry in master list,
+ // so when we complete this sync we can know
+ // which dirs have been deleted
+ //
+
+ SetForDelDirs();
+
+ InitMsgSend(SYNC_MSG); // creates message buffer, header etc to be used
+ // for subsequent update
+
+ //
+ // Go thru all SubDirectories
+ //
+
+ shan = FindFirstFile(STAR_DOT_STAR, & search_buf);
+
+
+ if(shan != INVALID_HANDLE_VALUE) {
+
+ //
+ // on valid handle
+ //
+
+ do
+ {
+
+ //
+ // make sure it's a dir and not '.' or '..'
+ //
+
+ if ((search_buf.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
+ (STRCMP(search_buf.cFileName , DOT)) &&
+ (STRCMP(search_buf.cFileName , DOT_DOT))) {
+
+ if (!SetCurrentDirectory(search_buf.cFileName)) {
+ ApiStatus = (NET_API_STATUS) GetLastError();
+ NetpAssert( ApiStatus != NO_ERROR );
+ NetpAssert( ApiStatus != ERROR_INVALID_FUNCTION );
+
+ //
+ // only keep an error for the first dir that failed
+ // so that we don't fill up the error log or message buffers
+ //
+ if ( ApiStatus == NO_ERROR )
+ ApiStatus = TmpStatus;
+
+ //
+ // for the contunue statement we want to get the
+ // next dir in the export path and try that one.
+ // We are assuming that the last SetCurrentDirectory
+ // call which failed left us at the ReplConfigExportPath
+ //
+ continue;
+ }
+
+ //
+ // does the work
+ //
+
+ ACQUIRE_LOCK( RMGlobalListLock );
+
+ // Note: ScanObject needs shared lock on ReplConfigLock and
+ // any lock on RMGlobalListLock.
+ ScanObject(
+ search_buf.cFileName,
+ SYNC_SCAN,
+ TRUE ); // yes, caller has excl lock
+ // (ScanObject converted excl lock to shared.)
+
+ RELEASE_LOCK( RMGlobalListLock );
+ Update = TRUE; // possible Update to this dir
+
+ if (!SetCurrentDirectory(DOT_DOT)) {
+ ApiStatus = (NET_API_STATUS) GetLastError();
+ NetpAssert( ApiStatus != NO_ERROR );
+ NetpAssert( ApiStatus != ERROR_INVALID_FUNCTION );
+
+ //
+ // Can't get back to parent dir, something is messed up.
+ // Go log error and return.
+ // this is a more serious error and indicates that we could
+ // not continue with the next find next call so exit and
+ // send what we have insead of continuing.
+ //
+ goto Cleanup;
+
+ }
+ }
+ } while (FindNextFile(shan, &search_buf));
+
+ } else {
+
+ ApiStatus = (NET_API_STATUS) GetLastError();
+ NetpAssert( ApiStatus != NO_ERROR );
+
+ IF_DEBUG(PULSER) {
+
+ NetpKdPrint(( PREFIX_REPL_MASTER
+ "SyncUpdate() is in error "
+ "when calling FindFirstFile(), " FORMAT_DWORD "\n",
+ ApiStatus ));
+
+ }
+ goto Cleanup;
+
+ }
+
+
+Cleanup:
+
+ //
+ // See if an existing directory (i.e it has a record on the Master List)
+ // has been deleted then send a message for the rest
+ //
+ if ( Update )
+ {
+ CheckForDelDirs();
+ MsgSend();
+ }
+
+ if (ConfigLocked) {
+ RELEASE_LOCK( ReplConfigLock );
+ }
+
+ if (shan != INVALID_HANDLE_VALUE) {
+ (VOID) FindClose(shan);
+ }
+
+ if (ApiStatus != NO_ERROR) { // log the first error that occured if any
+ AlertLogExit(ALERT_ReplSysErr,
+ NELOG_ReplSysErr,
+ ApiStatus,
+ NULL,
+ NULL,
+ NO_EXIT);
+ }
+
+
+} // SyncUpdate
+
+
+
+VOID
+GuardUpdate(
+ VOID
+ )
+/*++
+
+Routine Description :
+ Scans TREE INTEGRITY trees found unstable on last update. For those
+ stable since then, sends update to clients and updates Master List
+
+Arguments :
+ none
+
+Return Value :
+ none
+
+Threads:
+ Only called by pulser thread.
+
+--*/
+{
+ NET_API_STATUS ApiStatus = NO_ERROR, TmpStatus = NO_ERROR;
+ BOOL ConfigLocked = FALSE,
+ Update = FALSE;
+ PMASTER_LIST_REC cur_rec;
+ BOOL ListLocked = FALSE;
+
+ ACQUIRE_LOCK_SHARED( ReplConfigLock );
+ ConfigLocked = TRUE;
+
+ //
+ // Change to Export directory - note - it might be locked by
+ // another process
+ //
+
+ if (!SetCurrentDirectory(ReplConfigExportPath)) {
+ ApiStatus = (NET_API_STATUS) GetLastError();
+ NetpAssert( ApiStatus != NO_ERROR );
+ NetpAssert( ApiStatus != ERROR_INVALID_FUNCTION );
+ goto Cleanup;
+
+ }
+
+ InitMsgSend(GUARD_MSG); // creates message buffer, header
+ // etc to be used for update
+
+ //
+ // Go thru all SubDirectories in Guard list
+ //
+
+ ACQUIRE_LOCK_SHARED( RMGlobalListLock ); // ScanObject() needs any lock.
+ ListLocked = TRUE;
+
+ cur_rec = RMGlobalMasterListHeader;
+
+ while (cur_rec != NULL) {
+ if (cur_rec->grd_count != MASTER_GUARD_NOT_NEEDED) {
+
+ if (!SetCurrentDirectory(cur_rec->dir_name)) {
+ ApiStatus = (NET_API_STATUS) GetLastError();
+ NetpAssert( ApiStatus != NO_ERROR );
+ NetpAssert( ApiStatus != ERROR_INVALID_FUNCTION );
+ //
+ // only keep an error for the first dir that failed
+ // so that we don't fill up the error log or message buffers
+ //
+ if ( ApiStatus == NO_ERROR )
+ ApiStatus = TmpStatus;
+
+ //
+ // we want to get the next dir record and try that one.
+ // We are assuming that the last SetCurrentDirectory
+ // call which failed left us at the ReplConfigExportPath
+ // therefor skip the else and pick up the next record
+ //
+ }
+ else {
+
+ //
+ // does the work
+ // Note: ScanObject needs shared lock on ReplConfigLock and
+ // excl lock on RMGlobalListLock.
+ //
+
+ ScanObject( cur_rec->dir_name,
+ GUARD_SCAN,
+ FALSE
+ ); // no, caller does not have excl lock
+
+ Update = TRUE; // possible Update to this dir
+
+ if (!SetCurrentDirectory(DOT_DOT)) {
+ ApiStatus = (NET_API_STATUS) GetLastError();
+ NetpAssert( ApiStatus != NO_ERROR );
+ NetpAssert( ApiStatus != ERROR_INVALID_FUNCTION );
+
+ //
+ // Can't get back to parent dir, something is messed up.
+ // Go log error and return.
+ // this is a more serious error and indicates that we could
+ // not continue with the next find next call so exit and
+ // send what we have insead of continuing.
+ //
+ goto Cleanup;
+ }
+ }
+ }
+
+ cur_rec = cur_rec->next_p;
+ }
+
+Cleanup:
+
+ if (ListLocked) { // MsgSend gets into trouble if master
+ RELEASE_LOCK( RMGlobalListLock ); // list is locked, so avoid it.
+ ListLocked = FALSE;
+ }
+
+ if ( Update )
+ {
+ MsgSend(); // Let the world know the difference ..
+ }
+
+ if (ConfigLocked) {
+ RELEASE_LOCK( ReplConfigLock );
+ }
+
+ if (ApiStatus != NO_ERROR) { // log the first error that is found if any
+ AlertLogExit(ALERT_ReplSysErr,
+ NELOG_ReplSysErr,
+ ApiStatus,
+ NULL,
+ NULL,
+ NO_EXIT);
+ }
+
+} // GuardUpdate
+
+
+VOID
+SendPulse(
+ VOID
+ )
+/*++
+
+Routine Description :
+ Sends all entries in Master List that have a timestamp earlier then
+ previous sync update
+
+Arguments :
+
+Return Value :
+
+Threads:
+ Only called by pulser thread.
+
+--*/
+{
+ DWORD CurrentTime = NetpReplTimeNow();
+ PMASTER_LIST_REC cur_rec;
+
+ InitMsgSend(PULSE_MSG); // creates message buffer, header etc
+ // to be used for subsequent update
+
+ ACQUIRE_LOCK_SHARED( RMGlobalListLock );
+
+ cur_rec = RMGlobalMasterListHeader;
+
+ while (cur_rec != NULL) {
+ IF_DEBUG( PULSER ) {
+ NetpKdPrint(( PREFIX_REPL_MASTER
+ "SendPulse: found '" FORMAT_LPTSTR "', timestamp "
+ FORMAT_DWORD ".\n",
+ cur_rec->dir_name, cur_rec->timestamp ));
+ }
+ if ((cur_rec->timestamp < (PulserTimeOfSyncStart - sync_time))
+ && (cur_rec->count != (DWORD) -1)) {
+
+ IF_DEBUG(PULSER) {
+ NetpKdPrint(( PREFIX_REPL_MASTER
+ "SendPulse: calling AddToMsg with dir name '"
+ FORMAT_LPTSTR "'.\n", cur_rec->dir_name ));
+ }
+
+ AddToMsg(cur_rec, PULSE);
+
+ } else if (CurrentTime < (cur_rec->timestamp)) { // clock set back?
+
+ IF_DEBUG(PULSER) {
+ NetpKdPrint(( PREFIX_REPL_MASTER
+ "SendPulse: calling AddToMsg with dir name '"
+ FORMAT_LPTSTR "' (TIME WENT BACK).\n",
+ cur_rec->dir_name ));
+ }
+
+ AddToMsg(cur_rec, PULSE);
+ }
+
+ cur_rec = cur_rec->next_p;
+
+ }
+ RELEASE_LOCK( RMGlobalListLock );
+
+ MsgSend();
+
+}
+
+
+
+VOID
+ScanObject(
+ IN LPTSTR dir_name,
+ IN DWORD mode,
+ IN BOOL CallerHasExclLock
+ )
+/*++
+
+Routine Description:
+ Scans a complete object (TREE or DIRECTORY) counting files and checksumming
+ and updates Master List and adds entries to msg_buf according to mode,
+ change detected and guard / passed status.
+
+ Note that the caller must have a lock (any kind) on RMGlobalListLock.
+ This routine will convert it to a shared lock if needed.
+
+ Caller must also have lock (shared will do) on ReplConfigLock.
+
+Arguments:
+ dir_name - Name of dir (tree) to be scanned.
+
+ mode - scan mode: SYNC_SCAN or GUARD_SCAN.
+
+ CallerHasExclLock - TRUE iff caller has exclusive lock on RMGlobaListLock,
+ in which case this routine will convert to shared lock.
+
+Return Value:
+ If encounters and error simply returns - doing nothing.
+
+Threads:
+ Only called by pulser thread.
+
+--*/
+{
+ NET_API_STATUS ApiStatus;
+ CHECKSUM_REC tmp;
+ PCHECKSUM_REC tmp_rec;
+ PMASTER_LIST_REC cur_rec;
+ LONG MasterTimeZoneOffsetSecs; // exporter offset from GMT
+ DWORD msg_code, path_index;
+ LPTSTR path;
+
+ IF_DEBUG(PULSER) {
+ NetpKdPrint(( PREFIX_REPL_MASTER
+ "ScanObject: called with dir name '" FORMAT_LPTSTR
+ "', mode " FORMAT_DWORD ".\n", dir_name, mode ));
+ }
+
+ path = (LPTSTR)path_buf;
+ path_index = STRLEN(path);
+
+ tmp_rec = &tmp;
+
+ msg_code = UPDATE;
+
+ cur_rec = GetMasterRec(dir_name); // Gets cur record for dir, returns
+ // NULL if none exists.
+
+ // it's not in the current list maby its in the regestry?
+
+ if (cur_rec == NULL) {
+ ApiStatus = ExportDirGetRegistryValues( NULL, dir_name );
+ if ( ApiStatus == NO_ERROR ) {
+
+ // name found in registry and placed in the MasterList
+ // so now get the record
+
+ cur_rec = GetMasterRec(dir_name);
+ NetpAssert( cur_rec != NULL );
+ } else {
+
+ //
+ // no record in registry or could not access or other error so
+ // make new entry and continue.
+ //
+
+ cur_rec = NewMasterRecord(
+ dir_name,
+ DEFAULT_INTEGRITY,
+ DEFAULT_EXTENT);
+ if (cur_rec == NULL) {
+
+ IF_DEBUG(PULSER) {
+ NetpKdPrint(( PREFIX_REPL_MASTER
+ "ScanObject() can't create a new directory record.\n"
+ ));
+ }
+
+ ApiStatus = ERROR_NOT_ENOUGH_MEMORY;
+ goto Cleanup;
+ }
+
+ msg_code = START;
+
+ // Write new entry to registry...
+ ApiStatus = ExportDirWriteConfigData (
+ NULL, // server name
+ dir_name,
+ DEFAULT_INTEGRITY,
+ DEFAULT_EXTENT,
+ 0, // lock count
+ 0 ); // Seconds since 1970.
+ if (ApiStatus != NO_ERROR) {
+ goto Cleanup;
+ }
+ }
+ }
+
+ // At this point we know we've got a record in the client list.
+ NetpAssert( cur_rec != NULL );
+
+ if (CallerHasExclLock) {
+ //
+ // Let's change our exclusive lock to a shared one, as we may be in here
+ // for quite a long time, maybe even hours.
+ //
+ CONVERT_EXCLUSIVE_LOCK_TO_SHARED( RMGlobalListLock );
+ }
+
+ //
+ // Fix user lock files if necessary. (We would have to do this if
+ // this is a new first-level directory. Fixing the lock files may
+ // create a lock file for the replicator, which a downlevel importer would
+ // need to see.)
+ //
+ if ( ! (cur_rec->locks_fixed) ) {
+ IF_DEBUG( PULSER ) {
+ NetpKdPrint(( PREFIX_REPL_MASTER
+ "ScanObject: fixing user locks for new first-level '"
+ FORMAT_LPTSTR "'\n", dir_name ));
+ }
+
+ // Fix UserLock file(s) to match lock count.
+ ApiStatus = ExportDirFixUserLockFiles(
+ (LPCTSTR) ReplConfigExportPath,
+ (LPCTSTR) dir_name,
+ cur_rec->lockcount );
+ // BUGBUG: unfriendly on CD-ROM!
+ if (ApiStatus != NO_ERROR) {
+ goto Cleanup;
+ }
+
+ cur_rec->locks_fixed = TRUE;
+ }
+
+ //
+ // The checksums we send will exporter's local time, which may different
+ // than importer's, so compute offset to use for the checksums.
+ //
+ MasterTimeZoneOffsetSecs = NetpLocalTimeZoneOffset();
+
+ //
+ // Done fixing user locks. Continue with normal stuff.
+ //
+
+ cur_rec->exists = TRUE; // to enable detection of dir deletion.
+
+ (void) STRCPY(path + path_index, dir_name);
+
+ if (cur_rec->extent == REPL_EXTENT_FILE) {
+
+ IF_DEBUG(PULSER) {
+
+ NetpKdPrint(( PREFIX_REPL_MASTER
+ "ScanObject() called ScanDir.\n" ));
+
+ }
+
+ // scans dir, checksums and counts files
+ ScanDir(
+ MasterTimeZoneOffsetSecs, // exporter offset from GMT
+ path,
+ tmp_rec,
+ 0);
+ }
+ else {
+
+ IF_DEBUG(PULSER) {
+
+ NetpKdPrint(( PREFIX_REPL_MASTER
+ "ScanObject() called ScanTree.\n" ));
+
+ }
+
+ // as above but on a tree
+ ScanTree(
+ MasterTimeZoneOffsetSecs, // exporter offset from GMT
+ path,
+ tmp_rec);
+ }
+
+ *(path + path_index) = '\0';
+
+ if (tmp_rec->count == (DWORD)-1) {
+ NetpKdPrint(( PREFIX_REPL_MASTER
+ "ScanObject ERROR! GOT COUNT -1 FROM ScanTree/ScanDir!!!\n" ));
+ // BUGBUG: log this?
+ return;
+ }
+
+ if (mode == SYNC_SCAN) {
+
+ //
+ // Change ?
+ //
+
+ BOOL NeedMsg;
+ NeedMsg = ( (tmp_rec->count != cur_rec->count)
+ || (tmp_rec->checksum != cur_rec->checksum) );
+ if (cur_rec->timestamp > NetpReplTimeNow()) {
+ // Time went backwards, e.g. a month, so force message to get sent
+ // for this dir even though checksums match.
+ NeedMsg = TRUE;
+ }
+
+ if (NeedMsg) {
+ if ((cur_rec->integrity == REPL_INTEGRITY_TREE) && (guard_time != 0)) {
+
+ //
+ // wait GUARDTIME
+ //
+
+ guard_needed = TRUE; // raise flag
+
+ cur_rec->grd_count = tmp_rec->count;
+ cur_rec->grd_checksum = tmp_rec->checksum;
+ } else // integrity = FILE
+ UpdateRecAndMsg(cur_rec, tmp_rec, msg_code);
+
+ }
+
+ } // mode = SYNC_SCAN
+
+ if (mode == GUARD_SCAN) {
+
+ //
+ // Change during GUARDTIME ?
+ //
+
+ if ((tmp_rec->count == cur_rec->grd_count)
+ && (tmp_rec->checksum == cur_rec->grd_checksum)) {
+
+ //
+ // tree is stable so just update
+ //
+
+ if (cur_rec->count == (DWORD) -1) // see if we havent just started?
+
+ msg_code = START;
+
+ UpdateRecAndMsg(cur_rec, tmp_rec, msg_code);
+ }
+ }
+
+ ApiStatus = NO_ERROR;
+
+Cleanup:
+
+ if (ApiStatus != NO_ERROR) {
+ AlertLogExit(ALERT_ReplSysErr,
+ NELOG_ReplSysErr,
+ ApiStatus,
+ NULL,
+ NULL,
+ NO_EXIT);
+ }
+
+ IF_DEBUG(PULSER) {
+
+ NetpKdPrint(( PREFIX_REPL_MASTER
+ "ScanObject() is done.\n" ));
+
+ }
+
+
+} // End of ScanObject
+
+
+VOID
+SetForDelDirs(
+ VOID
+ )
+/*++
+
+Routine Description:
+ Inits Checks for directory under EXPORT that have been deleted.
+ Before SYNC for each entry in the master list the exists flag is reset,
+ it is set when the directory is scanned ( a good sign for it's existence).
+ CheckForDelDirs below goes again thru master list for those entries not set.
+
+Arguments:
+ none
+
+Return Value:
+ none
+
+Threads:
+ Only called by pulser thread.
+
+--*/
+{
+ PMASTER_LIST_REC cur_rec;
+
+ ACQUIRE_LOCK( RMGlobalListLock );
+
+ cur_rec = RMGlobalMasterListHeader;
+
+ while (cur_rec != NULL) {
+ cur_rec->exists = FALSE;
+ cur_rec = cur_rec->next_p;
+ }
+
+ RELEASE_LOCK( RMGlobalListLock );
+
+}
+
+
+
+void
+CheckForDelDirs(
+ VOID
+ )
+/*++
+
+Routine Description:
+ Checks for directory under EXPORT that have been deleted.
+ The check is done by going thru entire list for entries whose exists flag
+ is not set.
+
+ For every deleted directory, it sends an END update message and removes
+ the dir's entry in the master list.
+
+Arguments:
+
+Return Value:
+
+Threads:
+ Only called by pulser thread.
+
+--*/
+{
+ PMASTER_LIST_REC cur_rec;
+ PMASTER_LIST_REC next_rec;
+
+ ACQUIRE_LOCK( RMGlobalListLock ); // RemoveFromList() needs excl. lock.
+ cur_rec = RMGlobalMasterListHeader;
+
+ while (cur_rec != NULL) {
+ next_rec = cur_rec->next_p;
+ if ( ! (cur_rec->exists) ) {
+
+ IF_DEBUG(PULSER) {
+ NetpKdPrint(( PREFIX_REPL_MASTER
+ "CheckForDelDirs: calling AddToMsg "
+ "with dir name '" FORMAT_LPTSTR
+ "'.\n", cur_rec->dir_name ));
+ }
+
+ AddToMsg(cur_rec, END); // Send an EndOfRepl msg
+
+ RemoveFromList(cur_rec); // Remove from MasterList
+ }
+ cur_rec = next_rec;
+ }
+ RELEASE_LOCK( RMGlobalListLock );
+}
+
+
+VOID
+UpdateRecAndMsg(
+ IN OUT PMASTER_LIST_REC dir_rec,
+ IN PCHECKSUM_REC new_check,
+ IN DWORD msg_opcode)
+/*++
+
+Routine Description :
+ Updates entry in Master List and adds to msg_buf
+
+Arguments :
+ dir_rec tree's record in master list. Must be excl locked by caller.
+ new_check New checksum and count for tree.
+ msg_opcode code for update msg: START or UPDATE
+
+Return Value :
+
+Threads:
+ Only called by pulser thread.
+
+--*/
+{
+ NetpAssert( new_check != NULL );
+
+ dir_rec->count = new_check->count; // update old record
+ dir_rec->checksum = new_check->checksum;
+ dir_rec->timestamp = NetpReplTimeNow(); // .. and timestamp it
+ dir_rec->grd_count = MASTER_GUARD_NOT_NEEDED; // mark it as unguarded
+
+ IF_DEBUG(PULSER) {
+ NetpKdPrint(( PREFIX_REPL_MASTER
+ "UpdateRecAndMsg: calling AddToMsg with dir name '"
+ FORMAT_LPTSTR "'.\n", dir_rec->dir_name ));
+ }
+
+ AddToMsg(dir_rec, msg_opcode);
+
+}
+
+
+PMASTER_LIST_REC
+NewMasterRecord(
+ IN LPTSTR dir_name,
+ IN DWORD integrity,
+ IN DWORD extent
+ )
+/*++
+
+Routine Description :
+ Allocates new record links it to master list, and inits the members.
+ Caller must have exclusive lock for the master list before calling
+ NewMasterRecord.
+
+Arguments :
+ dir_name - new dir name
+ integrity - new dir's integrity
+ extent - bew dir's extent
+
+Return Value :
+ returns - pointer to new record.
+ - NULL if fails.
+
+--*/
+{
+ PMASTER_LIST_REC rec;
+
+ rec = (PMASTER_LIST_REC)NetpMemoryAllocate(sizeof(MASTER_LIST_REC));
+
+ if(rec == NULL) {
+
+ AlertLogExit(ALERT_ReplSysErr,
+ NELOG_ReplSysErr,
+ ERROR_NOT_ENOUGH_MEMORY,
+ NULL,
+ NULL,
+ NO_EXIT);
+ return(NULL);
+ }
+
+ //
+ // put values in
+ //
+
+ rec->extent = extent;
+ rec->integrity = integrity;
+ rec->checksum = 0;
+ rec->count = (DWORD) -1;
+ rec->grd_checksum = 0;
+ rec->grd_count = MASTER_GUARD_NOT_NEEDED;
+ rec->lockcount = 0;
+ rec->locks_fixed = FALSE;
+ rec->time_of_first_lock = 0;
+ (void) STRCPY(rec->dir_name, dir_name);
+
+ //
+ // Chain record into master list.
+ // Caller already has exclusive lock for master list, so it's OK.
+ //
+
+ rec->next_p = RMGlobalMasterListHeader;
+ rec->prev_p = NULL;
+ RMGlobalMasterListHeader = rec;
+
+ if (rec->next_p != NULL) // when inserting the very first record,
+ // there is no next
+
+ (rec->next_p)->prev_p = rec;
+
+ ++RMGlobalMasterListCount;
+
+ return rec;
+
+}
+
+
+
+VOID
+RemoveFromList(
+ IN PMASTER_LIST_REC rec
+ )
+/*++
+
+Routine Description:
+ Removes record from master list (doubly linked) and
+ free the block of memory.
+
+ Note that the caller must have an exclusive lock on RMGlobalListLock.
+
+Arguments:
+ rec : pointer to the record that to be removed.
+
+Return Value:
+ none
+
+Threads:
+ Only called by pulser thread.
+
+--*/
+{
+
+ NetpAssert( rec != NULL );
+
+ NetpAssert( RMGlobalMasterListCount > 0 );
+
+ if (rec->prev_p != NULL)
+ (rec->prev_p)->next_p = rec->next_p;
+ else // it's the first record
+ RMGlobalMasterListHeader = rec->next_p;
+
+ if (rec->next_p != NULL) // if it's not the last record
+ (rec->next_p)->prev_p = rec->prev_p;
+
+ --RMGlobalMasterListCount;
+
+ NetpMemoryFree( rec );
+}
+
+
+NET_API_STATUS
+RemoveMasterRecForDirName(
+ IN LPTSTR DirName
+ )
+/*++
+
+Routine Description:
+ Removes record from master list (doubly linked) and
+ free the block of memory.
+
+ The caller must have an exclusive lock on RMGlobalListLock.
+
+Arguments:
+ DirName : Name of directory whose master record is to be deleted.
+
+Return Value:
+ NET_API_STATUS (NO_ERROR or NERR_UnknownDevDir)
+
+Threads:
+ Only called by API threads.
+--*/
+{
+ NET_API_STATUS ApiStatus = NERR_UnknownDevDir; // assume guilty until...
+ PMASTER_LIST_REC Rec;
+
+ Rec = RMGlobalMasterListHeader;
+
+ while (Rec != NULL) {
+ if (STRICMP(Rec->dir_name, DirName))
+ Rec = Rec->next_p;
+ else {
+
+ NetpAssert( RMGlobalMasterListCount > 0 );
+
+ if (Rec->prev_p != NULL)
+ (Rec->prev_p)->next_p = Rec->next_p;
+ else // it's the first record
+ RMGlobalMasterListHeader = Rec->next_p;
+
+ if (Rec->next_p != NULL) // if it's not the last record
+ (Rec->next_p)->prev_p = Rec->prev_p;
+
+ --RMGlobalMasterListCount;
+
+ NetpMemoryFree( Rec );
+ ApiStatus = NO_ERROR;
+ break;
+ }
+ }
+
+ return (ApiStatus);
+
+} // RemoveMasterRecForDirName
+
+
+PMASTER_LIST_REC
+GetMasterRec(
+ IN LPTSTR dir_name
+ )
+/*++
+
+Routine Description :
+ Searches master list for record with dir_name.
+ Caller must lock the master list before calling GetMasterRec.
+
+Arguments :
+ dir_name - the required dir_name
+
+Return Value :
+ returns a pointer to the required record or NULL if not found
+
+--*/
+{
+ PMASTER_LIST_REC Rec;
+
+ Rec = RMGlobalMasterListHeader;
+
+ while (Rec != NULL) {
+ if (STRICMP(Rec->dir_name, dir_name))
+ Rec = Rec->next_p;
+ else
+ break;
+ }
+
+ return Rec;
+}
+
+VOID
+ReplMasterFreeList(
+ VOID
+ )
+/*++
+
+Routine Description:
+ Free RMGlobalMasterListHeader. called from pulser thread main routine.
+
+Arguments:
+ none
+
+Return Value:
+ none
+
+Threads:
+ Only called by pulser thread.
+
+--*/
+
+{
+ PMASTER_LIST_REC Rec, NextRec;
+
+ ACQUIRE_LOCK( RMGlobalListLock );
+
+ Rec = RMGlobalMasterListHeader;
+
+ while (Rec != NULL) {
+ NextRec = Rec->next_p;
+ NetpMemoryFree( Rec );
+ Rec = NextRec;
+ }
+
+ RMGlobalMasterListHeader = NULL;
+ RMGlobalMasterListCount = 0;
+
+ RELEASE_LOCK( RMGlobalListLock );
+}
diff --git a/private/net/svcdlls/repl/server/pulser.h b/private/net/svcdlls/repl/server/pulser.h
new file mode 100644
index 000000000..830032abe
--- /dev/null
+++ b/private/net/svcdlls/repl/server/pulser.h
@@ -0,0 +1,69 @@
+/*++
+
+Copyright (c) 1987-1993 Microsoft Corporation
+
+Module Name:
+ pulser.h
+
+Abstract:
+ constants and global data definitions
+
+Author:
+ Ported from Lan Man 2.x
+
+Environment:
+ Contains NT-specific code.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+ 10/07/91 (madana)
+ ported to NT. Converted to NT style.
+ 20-Jan-1992 JohnRo
+ More changes suggested by PC-LINT.
+ 24-Jan-1992 JohnRo
+ Changed to use LPTSTR etc.
+ 22-Mar-1992 JohnRo
+ Use integrity and extent equates in <lmrepl.h>.
+ 12-Jan-1993 JohnRo
+ RAID 7064: replicator exporter skips new data (change notify while
+ dir locked is lost).
+
+
+--*/
+
+
+#ifndef _PULSER_
+#define _PULSER_
+
+
+// C O N S T A N T S - DEFINES
+
+#define INTEGRITY_SW TEXT("INTEGRITY")
+#define EXTENT_SW TEXT("EXTENT")
+#define FILE_SW TEXT("FILE")
+#define TREE_SW TEXT("TREE")
+
+#define MASTER_INITIAL_GUESS 1
+
+#define UPD_INITIAL_SIZE 1024
+#define UPD_INCR_SIZE 1024
+
+#define MILLI_IN_MINUTE 60*1000L
+
+// Scan modes:
+#define SYNC_SCAN 1
+#define GUARD_SCAN 2
+
+
+
+
+// E X T E R N A L S
+
+//
+// Time (in secs since 1970) of last export tree chng or unlock.
+// (We can't checksum a tree while it is locked.)
+//
+extern DWORD PulserTimeOfLastNotifyOrUnlock; // Locked by RMGlobalListLock.
+
+
+#endif // _PULSER_
diff --git a/private/net/svcdlls/repl/server/repl.c b/private/net/svcdlls/repl/server/repl.c
new file mode 100644
index 000000000..a8e777fa5
--- /dev/null
+++ b/private/net/svcdlls/repl/server/repl.c
@@ -0,0 +1,925 @@
+/*++
+
+Copyright (c) 1987-1993 Microsoft Corporation
+
+Module Name:
+
+ repl.c
+
+Abstract:
+
+ Contains ReplMain thread which will be started by the service manager.
+
+Author:
+
+ 10/31/91 madana
+ initial coding
+
+Environment:
+
+ User mode only.
+ Contains NT-specific code.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+ Tab size is set to 4.
+
+Revision History:
+
+ 16-Jan-1992 JohnRo
+ Avoid using private logon functions.
+ Changed file name from repl.h to replgbl.h to avoid MIDL conflict.
+ Added config list lock.
+ ReportStatus() should add thread ID to status being reported.
+ Changed ReplMain() prototype to match svc controller's needs.
+ PC-LINT found a bug calling ReplFinish().
+ Made other changes suggested by PC-LINT.
+ Use NetpGet{ComputerName,DomainName} to make our life easier.
+ 24-Jan-1992 JohnRo
+ Changed to use LPTSTR etc.
+ Call ReplConfigRead() instead of Parse().
+ Moved current role from P_repl_sw to ReplGlobalRole.
+ Use REPL_ROLE_ equates just like the APIs do.
+ 04-Feb-1992 JohnRo
+ Made changes suggested by PC-LINT.
+ 09-Feb-1992 JohnRo
+ Use ReplChangeRole(), which allows dynamic role changes.
+ More changes suggested by PC-LINT.
+ Use FORMAT equates.
+ 15-Feb-1992 JohnRo
+ ReplMain() must wait while service is running.
+ Added more debug output.
+ 05-Mar-1992 JohnRo
+ Changed ReplMain's interface to match new service controller.
+ 06-Mar-1992 JohnRo
+ Service controller sets handles to NULL on error, not -1.
+ 06-Mar-1992 JohnRo
+ Avoid starting RPC server too soon.
+ 14-Mar-1992 JohnRo
+ Moved trace bits from here into common/data.c
+ 20-Mar-1992 JohnRo
+ Make sure UIC code is set for service controller.
+ 24-Mar-1992 JohnRo
+ Tell service controller that we're pending stop, then stop.
+ Workaround a service controller bug (broken pipe on service stop).
+ Stopping service should be done via ReplChangeRole().
+ Fixed names to check for in ReplControlRoutine().
+ 01-Apr-1992 JohnRo
+ When start fails, call ReplChangeRole() so svc ctrl stat gets updated.
+ More service controller workaround attempts.
+ 20-Jul-1992 JohnRo
+ RAID 2252: repl should prevent export on Windows/NT.
+ Use PREFIX_ equates.
+ 19-Aug-1992 JohnRo
+ RAID 2115: repl svc should wait while stopping or changing roles.
+ 23-Sep-1992 JohnRo
+ RAID 1091: net start replicator causes breakpoint due to bad heap
+ address. Also set wait hint > 0.
+ 18-Nov-1992 JohnRo
+ RAID 1537: Repl APIs in wrong role kill svc.
+ Various debug msgs cleaned-up.
+ 04-Dec-1992 JohnRo
+ RAID 3844: remote NetReplSetInfo uses local machine type.
+ RAID 3316: Fix hang at every svc stop (was lock conflict between
+ ReplChangeRole and ReplStopService).
+ 08-Dec-1992 JohnRo
+ RAID 3316: access violation while stopping the replicator.
+ 05-Jan-1993 JohnRo
+ Repl WAN support (get rid of repl name list limits).
+ Made changes suggested by PC-LINT 5.0
+ 29-Jan-1993 JohnRo
+ RAID 8913: repl svc should avoid hard-error popups.
+ 04-Feb-1993 JohnRo
+ RAID 9914: internal error stopping repl svc.
+ 11-Mar-1993 JohnRo
+ RAID 12100: stopping repl sometimes goes into infinite loop.
+ 24-Mar-1993 JohnRo
+ RAID 4267: Replicator has problems when work queue gets large.
+ 30-Mar-1993 JohnRo
+ Repl svc should use DBFlag in registry.
+ 06-Apr-1993 JohnRo
+ More changes suggested by PC-LINT 5.0
+ 28-Apr-1993 JohnRo
+ Use NetpKdPrint() where possible.
+ 24-May-1993 JohnRo
+ RAID 10587: repl could deadlock with changed NetpStopRpcServer(), so
+ just call ExitProcess() instead.
+
+--*/
+
+
+// These must be included first:
+
+#include <windows.h> // DWORD, CreateThread(), WaitForMultipleObjects(), etc.
+#include <lmcons.h> // IN, NET_API_STATUS.
+#include <rpc.h> // Needed by <rpcutil.h>.
+
+// These may be included in any order:
+
+#include <client.h> // RCGlobalClientListLock.
+#include <config.h> // LPNET_CONFIG_HANDLE, etc.
+#include <confname.h> // SECT_NT_REPLICATOR, REPL_KEYWORD_ equates.
+#include <expdir.h> // ExportDir{Start,Stop}Repl routines.
+#include <impdir.h> // ImportDir{Start,Stop}Repl routines.
+#include <lmapibuf.h>
+#include <lmerr.h> // NO_ERROR, NERR_ equates.
+#include <lmrepl.h> // REPL_ROLE_ equates.
+#include <lmsname.h> // SERVICE_REPL.
+#include <master.h> // RMGlobalListLock.
+#include <netdebug.h> // NetpKdPrint(), FORMAT_ equates.
+#include <netlib.h>
+#include <netlock.h> // NetpCreateLock(), etc.
+#include <prefix.h> // PREFIX_ equates.
+#include <replconf.h> // ReplConfig routines.
+#include <repldefs.h> // IF_DEBUG(), needed by <replgbl.h>, etc.
+#include <replgbl.h> // ReplGlobal and ReplConfig variables.
+#include <repllock.h> // CONFIG_DATA_LOCK_LEVEL.
+#include <rpcutil.h> // NetpStartRpcServer(), etc.
+#include <tstring.h> // NetpAlloc{type}From{type}(), etc.
+#include <winsvc.h> // SERVICE_STATUS_HANDLE, etc.
+
+
+// global "config" variables
+
+LPNET_LOCK ReplConfigLock = NULL;
+DWORD ReplConfigRole = REPL_ROLE_STOPPED; // Locked by ReplConfigLock.
+TCHAR ReplConfigExportPath[PATHLEN+1]; // Ditto.
+LPTSTR ReplConfigExportList = NULL; // Ditto.
+TCHAR ReplConfigImportPath[PATHLEN+1]; // Ditto.
+LPTSTR ReplConfigImportList = NULL; // Ditto.
+TCHAR ReplConfigLogonUserName[UNLEN+1]; // Ditto.
+DWORD ReplConfigInterval; // Ditto.
+DWORD ReplConfigPulse; // Ditto.
+DWORD ReplConfigGuardTime; // Ditto.
+DWORD ReplConfigRandom; // Ditto.
+
+HANDLE ReplGlobalClientTerminateEvent = NULL;
+HANDLE ReplGlobalMasterTerminateEvent = NULL;
+
+
+//
+// Variables to control service startup.
+//
+
+HANDLE ReplGlobalExportStartupEvent = NULL;
+HANDLE ReplGlobalImportStartupEvent = NULL;
+
+//
+// Variables to control service stop.
+//
+
+BOOL ReplGlobalIsServiceStopping = FALSE;
+DWORD ReplGlobalCheckpoint = 1;
+
+//
+// client and master thread handles
+//
+HANDLE ReplGlobalClientThreadHandle = NULL;
+HANDLE ReplGlobalMasterThreadHandle = NULL;
+
+
+//
+// Variables to control service error report.
+//
+SERVICE_STATUS_HANDLE ReplGlobalServiceHandle = (SERVICE_STATUS_HANDLE) NULL;
+DWORD ReplGlobalUninstallUicCode = 0;
+
+//
+// We talk to both downlevel and NT clients, who want ANSI and Unicode
+// strings respectively. So, let's maintain copies of this (presumably
+// constant) data in both forms:
+//
+WCHAR ReplGlobalUnicodeComputerName[CNLEN+1];
+WCHAR ReplGlobalUnicodeDomainName[DNLEN+1];
+
+CHAR ReplGlobalAnsiComputerName[CNLEN+1];
+CHAR ReplGlobalAnsiDomainName[DNLEN+1];
+
+LPTSTR ReplGlobalComputerName; // points to one of the above.
+LPTSTR ReplGlobalDomainName; // points to one of the above.
+
+
+//
+// proto defs
+//
+
+DBGSTATIC NET_API_STATUS
+ReplInit(
+ VOID
+ );
+
+DBGSTATIC VOID
+ReplControlRoutine(
+ IN DWORD dwControl
+ );
+
+DBGSTATIC VOID
+ReplCleanup(
+ VOID
+ );
+
+DBGSTATIC NET_API_STATUS
+GetLocalInfo(
+ VOID
+ );
+
+//
+// main code start
+//
+
+VOID
+ReplMain(
+ IN DWORD dwNumServicesArgs,
+ IN LPTSTR *lpServiceArgVectors
+ )
+/*++
+
+Routine Description:
+
+ Talk to service controller, read registry, and change role (which
+ starts import and/or export parts).
+
+Arguments:
+
+ Ignored. (Required by service controller interface.)
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NET_API_STATUS ApiStatus;
+ BOOL LockedConfigData = FALSE;
+ DWORD NewRole;
+
+ UNREFERENCED_PARAMETER( dwNumServicesArgs );
+ UNREFERENCED_PARAMETER( lpServiceArgVectors );
+
+ IF_DEBUG( MAJOR ) {
+ NetpKdPrint(( PREFIX_REPL
+ "**************************** STARTING REPL "
+ "****************************\n" ));
+ }
+
+ // DbgBreakPoint();
+
+
+ // Register control routine.
+ // This must be done before any calls to ReportStatus().
+
+ ReplGlobalServiceHandle = RegisterServiceCtrlHandler(
+ (LPTSTR) SERVICE_REPL,
+ ReplControlRoutine );
+
+ if (ReplGlobalServiceHandle == (SERVICE_STATUS_HANDLE) NULL) {
+
+ // can't start service
+
+ NetpKdPrint(( PREFIX_REPL "can't start repl service, "
+ "RegisterServiceCtrlHandler is in error.\n" ));
+
+ // Haven't done anything to clean up, so don't.
+ return;
+
+ }
+
+ ReportStatus(
+ SERVICE_START_PENDING,
+ NO_ERROR,
+ REPL_WAIT_HINT,
+ 1 ); // checkpoint
+
+
+ // initialize global data
+
+ ApiStatus = ReplInit();
+ if (ApiStatus != NO_ERROR) {
+
+ goto Cleanup;
+
+ }
+
+ ReportStatus(
+ SERVICE_START_PENDING,
+ NO_ERROR,
+ REPL_WAIT_HINT,
+ 2 ); // checkpoint
+
+
+ // Read config data from registry. We just read the stuff controlling the
+ // service as a whole here; the client and master threads each read their
+ // own config data as well.
+
+ ApiStatus = ReplConfigRead(
+ NULL, // no server name
+ & NewRole,
+ ReplConfigExportPath,
+ &ReplConfigExportList, // Alloc and set ptr.
+ ReplConfigImportPath,
+ &ReplConfigImportList, // Alloc and set ptr.
+ ReplConfigLogonUserName,
+ & ReplConfigInterval,
+ & ReplConfigPulse,
+ & ReplConfigGuardTime,
+ & ReplConfigRandom );
+ if (ApiStatus != NO_ERROR) {
+
+ goto Cleanup;
+
+ }
+
+#if DBG
+ {
+ LPNET_CONFIG_HANDLE SectionHandle = NULL;
+ LPWSTR ValueT = NULL;
+
+ ApiStatus = NetpOpenConfigData(
+ &SectionHandle,
+ NULL, // no server name.
+ (LPTSTR) SECT_NT_REPLICATOR, // section name
+ TRUE ); // we only want readonly access
+ NetpAssert( ApiStatus == NO_ERROR );
+
+ ApiStatus = NetpGetConfigValue (
+ SectionHandle,
+ (LPTSTR) REPL_KEYWORD_DBFLAG,
+ &ValueT );
+ (VOID) NetpCloseConfigData( SectionHandle );
+ if (ApiStatus == NO_ERROR) {
+
+ NetpAssert( ValueT != NULL );
+ ReplGlobalTrace = NetpAtoX( ValueT );
+ (VOID) NetApiBufferFree( ValueT );
+
+ } else if (ApiStatus == NERR_CfgParamNotFound) {
+
+ //
+ // copy default value in here.
+ //
+
+ // ReplGlobalTrace = 0;
+ } else {
+ NetpKdPrint(( PREFIX_REPL
+ "ReplMain: unexpected error " FORMAT_API_STATUS
+ " from NetpGetConfigValue.\n", ApiStatus ));
+ // Probably harmless, so let's continue...
+ }
+ }
+#endif // DBG
+
+ //
+ // Make sure role in registry is allowed with current product type.
+ //
+ if ( !ReplConfigIsRoleAllowed( NULL, NewRole ) ) {
+ // BUGBUG: Log this!
+ NetpKdPrint(( PREFIX_REPL "Role inconsistent with product type; "
+ " assuming role of " FORMAT_DWORD ".\n", REPL_ROLE_IMPORT ));
+
+ NewRole = REPL_ROLE_IMPORT;
+
+ // Update registry so we don't get this every time.
+ ApiStatus = ReplConfigWrite(
+ NULL, // no server name
+ NewRole,
+ ReplConfigExportPath,
+ ReplConfigExportList,
+ ReplConfigImportPath,
+ ReplConfigImportList,
+ ReplConfigLogonUserName,
+ ReplConfigInterval,
+ ReplConfigPulse,
+ ReplConfigGuardTime,
+ ReplConfigRandom );
+ if (ApiStatus != NO_ERROR) {
+ goto Cleanup;
+ }
+ }
+
+
+ ReportStatus(
+ SERVICE_START_PENDING,
+ NO_ERROR,
+ REPL_WAIT_HINT,
+ 3 ); // checkpoint
+
+ // ReplChangeRole assumes caller has exclusive lock on ReplConfigLock.
+ ACQUIRE_LOCK( ReplConfigLock );
+ LockedConfigData = TRUE;
+
+ //
+ // Change the role to the one. This is where most of the work gets done,
+ // like starting other threads and so on. This is also where ReplConfigRole
+ // gets set. Also, the RPC server will be started or stopped as necessary.
+ // Last and not least, ReplChngRole() will inform the service controller.
+ // NOTE: ReplChangeRole assumes caller has exclusive lock on ReplConfigLock.
+ //
+
+ ApiStatus = ReplChangeRole( NewRole );
+
+ if (ApiStatus != NO_ERROR) {
+ goto Cleanup;
+ }
+
+ IF_DEBUG(REPL) {
+ NetpKdPrint(( PREFIX_REPL "ReplMain: changed role OK.\n" ));
+ }
+
+ if (LockedConfigData) {
+ RELEASE_LOCK( ReplConfigLock );
+ LockedConfigData = FALSE;
+ }
+
+ IF_DEBUG( REPL ) {
+ NetpKdPrint(( PREFIX_REPL "ReplMain: exiting thread!!!!!!!!!!!!!!\n" ));
+ }
+
+ //
+ // That's all, folks. When somebody decides to stop the service,
+ // ReplControlRoutine will create the Stopper and Staller threads to do it.
+ //
+
+ return;
+
+
+Cleanup :
+
+ NetpKdPrint(( PREFIX_REPL
+ "ReplMain: forced to cleanup; shutting down svc...; status "
+ FORMAT_API_STATUS "\n", ApiStatus ));
+
+ ReplGlobalUninstallUicCode = ApiStatus; // So service ctrl gets told.
+
+ if ( !LockedConfigData ) {
+ // Get lock for ReplChangeRole().
+ ACQUIRE_LOCK( ReplConfigLock );
+ LockedConfigData = TRUE;
+ }
+
+ //
+ // Make sure nothing is left running...
+ //
+ // Change role back to stopped, and do all other cleanup.
+ // NOTE: ReplChangeRole assumes caller has exclusive lock on ReplConfigLock.
+ (VOID) ReplChangeRole( REPL_ROLE_STOPPED );
+
+ if (LockedConfigData) {
+ RELEASE_LOCK( ReplConfigLock );
+ }
+
+ ReplCleanup();
+
+ // Report status. This needs ReplGlobalServiceHandle one last time.
+
+ ReportStatus(
+ SERVICE_STOPPED,
+ ReplGlobalUninstallUicCode,
+ 0, // wait hint
+ 0 ); // checkpoint
+
+ // BUGBUG: Close ReplGlobalServiceHandle here someday?
+
+ IF_DEBUG( MAJOR ) {
+ NetpKdPrint(( PREFIX_REPL
+ "**************************** ENDING REPL "
+ "****************************\n" ));
+ }
+
+ return;
+
+} // ReplMain
+
+
+DBGSTATIC NET_API_STATUS
+ReplInit(
+ VOID
+ )
+{
+ NET_API_STATUS NetStatus;
+
+ // init global variable ..
+
+ // this needs to be done because the service can be stopped and started;
+ // still the service .exe running and the global data live forever.
+
+ // BUGBUG: memory leak of old values?
+ ReplConfigExportList = NULL;
+ ReplConfigImportList = NULL;
+
+ ReplGlobalClientTerminateEvent = NULL;
+ ReplGlobalMasterTerminateEvent = NULL;
+
+ ReplGlobalExportStartupEvent = NULL;
+ ReplGlobalImportStartupEvent = NULL;
+
+ ReplGlobalClientThreadHandle = NULL;
+ ReplGlobalMasterThreadHandle = NULL;
+
+ ReplGlobalUninstallUicCode = 0;
+
+ ReplGlobalIsServiceStopping = FALSE;
+ ReplGlobalCheckpoint = 1;
+
+ // Init lock for config data.
+ ReplConfigLock = NetpCreateLock(
+ CONFIG_DATA_LOCK_LEVEL, (LPTSTR) TEXT("config data") );
+ NetpAssert( ReplConfigLock != NULL );
+
+ // Init client list lock (needed by import lock/unlock APIs even if
+ // import side not running). Ditto for master list lock.
+ RCGlobalClientListLock = NetpCreateLock(
+ CLIENT_LIST_LOCK_LEVEL,
+ (LPTSTR) TEXT("client list") );
+ NetpAssert( RCGlobalClientListLock != NULL );
+
+ RCGlobalDuplListLock = NetpCreateLock(
+ DUPL_LIST_LOCK_LEVEL,
+ (LPTSTR) TEXT("dupl list") );
+ NetpAssert( RCGlobalDuplListLock != NULL );
+
+ RMGlobalListLock = NetpCreateLock(
+ MASTER_LIST_LOCK_LEVEL,
+ (LPTSTR) TEXT("master list") );
+ NetpAssert( RMGlobalListLock != NULL); // BUGBUG: out of memory?
+
+ // Create startup events.
+
+ ReplGlobalExportStartupEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+
+ if( ReplGlobalExportStartupEvent == NULL) {
+
+ return (GetLastError());
+
+ }
+
+ ReplGlobalImportStartupEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+
+ if( ReplGlobalImportStartupEvent == NULL) {
+
+ return (GetLastError());
+
+ }
+
+
+ //
+ // Create termination events.
+ //
+
+ ReplGlobalClientTerminateEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+
+ if( ReplGlobalClientTerminateEvent == NULL) {
+
+ return (GetLastError());
+
+ }
+
+ ReplGlobalMasterTerminateEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+
+ if (ReplGlobalMasterTerminateEvent == NULL) {
+
+ return (GetLastError());
+
+ }
+
+ //
+ // Disable the dreaded "net name deleted" popup (and all other hard
+ // error popups).
+ //
+ (VOID) SetErrorMode( SEM_FAILCRITICALERRORS );
+
+ //
+ // Get local domain name, computer name, etc.
+ //
+
+ NetStatus = GetLocalInfo();
+ if (NetStatus != NO_ERROR) {
+ return (NetStatus);
+ }
+
+ return (NO_ERROR);
+
+}
+
+
+DBGSTATIC NET_API_STATUS
+GetLocalInfo(
+ VOID
+ )
+/*++
+
+Routine Description :
+ Retrieves (via net config helpers) local info and stores
+ it in a global variable. This info includes the computer name and the
+ domain name.
+
+Arguments :
+
+Return Value :
+
+--*/
+{
+ LPTSTR TempTStr;
+ NET_API_STATUS NetStatus;
+
+
+ //
+ // First get computer name...
+ //
+ NetStatus = NetpGetComputerName( & TempTStr ); // alloc and set ptr.
+ if (NetStatus != NO_ERROR) {
+ NetpKdPrint(( PREFIX_REPL_MASTER "GetLocalInfo() is in trouble calling "
+ "NetpGetComputerName(), NetStatus is "
+ FORMAT_API_STATUS "\n", NetStatus ));
+
+ ReplFinish( NetStatus );
+
+ return NetStatus;
+ }
+ NetpAssert( TempTStr != NULL );
+
+ NetpCopyTStrToWStr( ReplGlobalUnicodeComputerName, TempTStr );
+
+#if defined(DBCS) && defined(UNICODE) // GetLocalInfo()
+ NetpCopyWStrToStrDBCS( ReplGlobalAnsiComputerName, TempTStr );
+#else
+ NetpCopyTStrToStr( ReplGlobalAnsiComputerName, TempTStr );
+#endif // defined(DBCS) && defined(UNICODE)
+
+ (void) NetApiBufferFree( TempTStr );
+
+
+ //
+ // Domain name...
+ //
+ NetStatus = NetpGetDomainName( & TempTStr ); // alloc and set ptr.
+ if (NetStatus != NO_ERROR) {
+ NetpKdPrint(( PREFIX_REPL_MASTER "GetLocalInfo() is in trouble calling "
+ "NetpGetDomainName(), NetStatus is " FORMAT_API_STATUS "\n",
+ NetStatus ));
+ ReplFinish( NetStatus );
+
+ // BUGBUG: memory leak here? Not a problem 'cos we're dying?
+ return NetStatus;
+ }
+ NetpAssert( TempTStr != NULL );
+
+ NetpCopyTStrToWStr( ReplGlobalUnicodeDomainName, TempTStr );
+
+#if defined(DBCS) && defined(UNICODE) // GetLocalInfo()
+ NetpCopyWStrToStrDBCS( ReplGlobalAnsiDomainName, TempTStr );
+#else
+ NetpCopyTStrToStr( ReplGlobalAnsiDomainName, TempTStr );
+#endif // defined(DBCS) && defined(UNICODE)
+
+ (void) NetApiBufferFree( TempTStr );
+
+ //
+ // Let's do a TCHAR version of each name, to make life easy.
+ //
+#ifdef UNICODE
+ ReplGlobalComputerName = ReplGlobalUnicodeComputerName;
+ ReplGlobalDomainName = ReplGlobalUnicodeDomainName;
+#else
+ ReplGlobalComputerName = ReplGlobalAnsiComputerName;
+ ReplGlobalDomainName = ReplGlobalAnsiDomainName;
+#endif
+
+ return (NO_ERROR);
+}
+
+
+
+DBGSTATIC VOID
+ReplControlRoutine(
+ IN DWORD Control
+ )
+{
+ NET_API_STATUS ApiStatus = NO_ERROR;
+ DWORD State = SERVICE_RUNNING;
+ HANDLE StallerThreadHandle = NULL;
+ HANDLE StopperThreadHandle = NULL;
+ DWORD ThreadID;
+
+ IF_DEBUG( SVCCTRL ) {
+ NetpKdPrint(( PREFIX_REPL "ReplControlRoutine: got control of "
+ FORMAT_DWORD ".\n", Control ));
+ }
+
+ switch (Control) {
+
+ case SERVICE_CONTROL_STOP:
+
+ State = SERVICE_STOP_PENDING;
+
+ //
+ // Tell service controller quickly what's going on...
+ //
+
+ ReportStatus(
+ State,
+ NO_ERROR,
+ REPL_WAIT_HINT,
+ 1 ); // checkpoint
+
+ //
+ // Set global vars for stopper and staller threads.
+ //
+
+ ReplGlobalIsServiceStopping = TRUE;
+ ReplGlobalCheckpoint = 2; // Already used 1 above.
+
+ //
+ // Create stopper thread, which will do the real work.
+ // Do this BEFORE creating the staller thread, in case we don't have
+ // enough memory or whatever. (Otherwise, we'd have a staller which
+ // would run forever.)
+ //
+ IF_DEBUG( SVCCTRL ) {
+ NetpKdPrint(( PREFIX_REPL
+ "ReplControlRoutine: creating stopper thread...\n" ));
+ }
+ StopperThreadHandle = CreateThread(
+ NULL, // no security attributes
+ (20 * 1024), // stack size in bytes (wild guess)
+ ReplStopper, // routine to call
+ NULL, // no thread parm
+ 0, // creation flags: normal
+ &ThreadID);
+ if (StopperThreadHandle == NULL) {
+ ApiStatus = (NET_API_STATUS) GetLastError();
+ NetpAssert( ApiStatus != NO_ERROR );
+ NetpKdPrint(( PREFIX_REPL
+ "ReplControlRoutine: unable to create stopper thread!, "
+ "API status " FORMAT_API_STATUS "\n", ApiStatus ));
+ goto Cleanup;
+ }
+
+ //
+ // Create staller thread.
+ //
+ IF_DEBUG( SVCCTRL ) {
+ NetpKdPrint(( PREFIX_REPL
+ "ReplControlRoutine: creating staller thread...\n" ));
+ }
+
+ StallerThreadHandle = CreateThread(
+ NULL, // no security attributes
+ (10 * 1024), // stack size in bytes (wild guess)
+ ReplStaller, // routine to call
+ NULL, // no thread parm
+ 0, // creation flags: normal
+ &ThreadID);
+ if (StallerThreadHandle == NULL) {
+ ApiStatus = (NET_API_STATUS) GetLastError();
+ NetpAssert( ApiStatus != NO_ERROR );
+ NetpKdPrint(( PREFIX_REPL
+ "ReplControlRoutine: unable to create staller thread!, "
+ "API status " FORMAT_API_STATUS "\n", ApiStatus ));
+ goto Cleanup;
+ }
+
+ goto Cleanup;
+
+ case SERVICE_CONTROL_INTERROGATE:
+
+ //
+ // Report service installed
+ //
+ // Note: This depends on the service controller never doing an
+ // interrogate during a start_pending or stop_pending state.
+ // RitaW assures me (JohnRo) that this is OK.
+ //
+ // BUGBUG: Lock ReplCOnfigRole here?
+ if (ReplConfigRole == REPL_ROLE_STOPPED) {
+ State = SERVICE_STOPPED;
+ } else {
+ State = SERVICE_RUNNING;
+ }
+
+ ApiStatus = NO_ERROR;
+ goto ReportStatusAndCleanup;
+
+ default:
+
+ goto Cleanup;
+ }
+
+ReportStatusAndCleanup:
+
+ ReportStatus(
+ State,
+ ApiStatus,
+ 0, // wait hint
+ 0 ); // checkpoint
+
+Cleanup:
+
+ if (StallerThreadHandle != NULL) {
+ (VOID) CloseHandle( StallerThreadHandle );
+ }
+
+ if (StopperThreadHandle != NULL) {
+ (VOID) CloseHandle( StopperThreadHandle );
+ }
+
+ IF_DEBUG( SVCCTRL ) {
+ NetpKdPrint(( PREFIX_REPL "ReplControlRoutine: returning.\n" ));
+ }
+
+}
+
+DBGSTATIC VOID
+ReplCleanup(
+ VOID
+ )
+{
+
+ // Close startup event handles.
+
+ if (ReplGlobalExportStartupEvent != NULL) {
+
+ (void) CloseHandle( ReplGlobalExportStartupEvent );
+
+ }
+
+ if (ReplGlobalImportStartupEvent != NULL) {
+
+ (void) CloseHandle( ReplGlobalImportStartupEvent );
+
+ }
+
+
+ // Close termination event handles.
+
+ if (ReplGlobalClientTerminateEvent != NULL) {
+
+ (void) CloseHandle( ReplGlobalClientTerminateEvent );
+
+ }
+
+ if (ReplGlobalMasterTerminateEvent != NULL) {
+
+ (void) CloseHandle( ReplGlobalMasterTerminateEvent );
+
+ }
+
+} // ReplCleanup
+
+
+VOID // Never returns!
+ReplStopService (
+ VOID
+ )
+{
+ IF_DEBUG(REPL) {
+ NetpKdPrint(( PREFIX_REPL "ReplStopService: beginning...\n" ));
+ }
+
+ //
+ // We used to try to stop accepting RPC (API) calls here, by calling
+ // NetpStopRpcServer(). But that could introduce a deadlock, since
+ // NetpStopRpcServer() now waits for pending RPC calls to end, and one
+ // of the pending calls could be causing the service to stop!
+ //
+ // So, we now take advantage of the fact that we're the only service
+ // in this process. Why not just exit the process?
+ //
+
+ //
+ // I don't know of any code that cares, but just in case...
+ //
+
+ ReplGlobalIsServiceStopping = TRUE;
+
+ //
+ // Make sure service controller gets: stop pending and stopped.
+ //
+
+ ReportStatus(
+ SERVICE_STOP_PENDING,
+ NO_ERROR,
+ REPL_WAIT_HINT,
+ 1 ); // checkpoint
+
+ ReportStatus(
+ SERVICE_STOPPED,
+ ReplGlobalUninstallUicCode,
+ 0, // wait hint
+ 0 ); // checkpoint
+
+ IF_DEBUG( MAJOR ) {
+ NetpKdPrint(( PREFIX_REPL
+ "**************************** ENDING REPL (STATUS="
+ FORMAT_API_STATUS ")"
+ "****************************\n",
+ ReplGlobalUninstallUicCode
+ ));
+ }
+
+ //
+ // Ka-boom!
+ //
+ ExitProcess( (UINT) ReplGlobalUninstallUicCode );
+
+ /*NOTREACHED*/
+
+} // ReplStopService
diff --git a/private/net/svcdlls/repl/server/replget.c b/private/net/svcdlls/repl/server/replget.c
new file mode 100644
index 000000000..fbb535eb2
--- /dev/null
+++ b/private/net/svcdlls/repl/server/replget.c
@@ -0,0 +1,111 @@
+/*++
+
+Copyright (c) 1992-1993 Microsoft Corporation
+
+Module Name:
+
+ ReplGet.c
+
+Abstract:
+
+ This file contains NetrReplGetInfo().
+
+Author:
+
+ John Rogers (JohnRo) 20-Jan-1992
+
+Environment:
+
+ User Mode - Win32
+ Portable to any flat, 32-bit environment. (Uses Win32 typedefs.)
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+ 20-Jan-1992 JohnRo
+ Created.
+ 24-Jan-1992 JohnRo
+ Changed to use LPTSTR etc.
+ P_ globals are now called ReplGlobal variables in replgbl.h.
+ Moved some code from here to ReplConfigAllocAndBuildApiRecord().
+ This routine can use a shared lock for the config data.
+ 31-Jan-1992 JohnRo
+ Fixed an error code.
+ 24-Mar-1992 JohnRo
+ Renamed many ReplGlobal vars to ReplConfig vars.
+ 13-Jan-1993 JohnRo
+ Made some changes suggested by PC-LINT 5.0
+
+--*/
+
+
+#include <windef.h> // IN, DWORD, etc.
+#include <lmcons.h> // LAN Manager common definitions
+#include <rpc.h> // Needed by <repl.h>.
+
+#include <lmrepl.h> // LPREPL_INFO_0.
+#include <netlib.h> // NetpCopyDataToBuffer().
+#include <netlock.h> // ACQUIRE_LOCK_SHARED(), etc.
+#include <repl.h> // My prototype (in MIDL-generated .h file).
+#include <replconf.h> // ReplConfig routines.
+#include <replgbl.h> // ReplGlobal and ReplConfig variables.
+#include <winerror.h> // ERROR_ equates, NO_ERROR.
+
+
+// Read config data for the replicator. Callable whether or not
+// the replicator service is started.
+NET_API_STATUS
+NetrReplGetInfo (
+ IN LPTSTR UncServerName OPTIONAL,
+ IN DWORD Level,
+ OUT LPCONFIG_CONTAINER BufPtr // RPC container (union)
+ )
+{
+ NET_API_STATUS ApiStatus;
+ LPREPL_INFO_0 Buffer;
+
+ UNREFERENCED_PARAMETER( UncServerName );
+
+ //
+ // Check for caller's errors.
+ //
+ if ( Level != 0 ) {
+ return (ERROR_INVALID_LEVEL);
+ } else if (BufPtr == NULL) {
+ return (ERROR_INVALID_PARAMETER);
+ }
+ BufPtr->Info0 = NULL; // Test for memory access fault.
+
+ //
+ // Get a shared lock on the config data, so we get a consistent snapshot.
+ //
+ ACQUIRE_LOCK_SHARED( ReplConfigLock );
+
+ //
+ // Compute how much space we'll need and allocate it.
+ // Build the final API record while we're at it.
+ //
+ ApiStatus = ReplConfigAllocAndBuildApiRecord (
+ Level,
+ ReplConfigRole,
+ ReplConfigExportPath,
+ ReplConfigExportList,
+ ReplConfigImportPath,
+ ReplConfigImportList,
+ ReplConfigLogonUserName, // logon user name
+ ReplConfigInterval, // interval
+ ReplConfigPulse,
+ ReplConfigGuardTime,
+ ReplConfigRandom,
+ (LPBYTE *) (LPVOID) &Buffer ); // Alloc and set pointer.
+
+ //
+ // All done.
+ //
+
+ RELEASE_LOCK( ReplConfigLock );
+
+ BufPtr->Info0 = Buffer; // (Note: Buffer may be NULL.)
+ return (ApiStatus);
+
+} // NetpReplGetInfo
diff --git a/private/net/svcdlls/repl/server/repllock.h b/private/net/svcdlls/repl/server/repllock.h
new file mode 100644
index 000000000..a1ab1c303
--- /dev/null
+++ b/private/net/svcdlls/repl/server/repllock.h
@@ -0,0 +1,87 @@
+/*++
+
+Copyright (c) 1991-1993 Microsoft Corporation
+
+Module Name:
+
+ ReplLock.h
+
+Abstract:
+
+ This module defines lock equates for use with net/inc/netlock.h.
+
+ For more information, see the comments in that file.
+ See also public/spec/network/repllock.txt.
+
+Author:
+
+ John Rogers (JohnRo) 30-Dec-1991
+
+Environment:
+
+ Portable to any flat, 32-bit environment. (Uses Win32 typedefs.)
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+ 30-Dec-1991 JohnRo
+ Created.
+ 02-Jan-1992 JohnRo
+ Added support for FAKE_PER_PROCESS_RW_CONFIG handling.
+ 09-Jan-1992 JohnRo
+ Client list lock and pool lock can be held at same time.
+ 15-Jan-1992 JohnRo
+ Added config list lock level.
+ 10-Feb-1992 JohnRo
+ Added lock for master's client list (RMGlobalClientList).
+ 25-Mar-1992 JohnRo
+ Revised/corrected lock level numbers.
+ 09-Nov-1992 JohnRo
+ RAID 7962: Repl APIs in wrong role kill svc.
+ 25-Mar-1993 JohnRo
+ RAID 4267: Replicator has problems when work queue gets large.
+
+--*/
+
+#ifndef _REPLLOCK_
+#define _REPLLOCK_
+
+
+//
+// Define levels for locks used by the replicator service. These levels
+// are checked during debugging. Locks which shouldn't be held at the same
+// time can have the same level. Make sure there are no conflicts with the
+// reserved lock level(s) in netlock.h.
+//
+// Config data lock can be held while getting client list lock.
+// For instance, this happens in NetrImportDirAdd.
+//
+// Config data lock is held while getting a master list lock in
+// NetrReplImportDirAdd, GuardUpdate, etc.
+//
+// Client list lock can be held while getting lock on delay list.
+// This happens when ReplDoUpdate calls ReplScanQueuesForMoreRecentMsg.
+//
+// The client list lock can be held while getting a pool lock. This
+// happens when ReplRemoveClientRecForDirName calls ReplClientFreePoolEntry.
+// Also ReplCheckTimeList calls ReplQuePut, which calls ReplClientGetPoolEntry.
+//
+// Client list lock can be held while getting lock on work queue.
+//
+// Client list lock can be held while getting lock on duplicate list.
+//
+// The delay list lock can be held while getting a lock
+// for the work queue.
+//
+
+
+#define CONFIG_DATA_LOCK_LEVEL ((DWORD) 0x00000100)
+#define CLIENT_LIST_LOCK_LEVEL ((DWORD) 0x00000200)
+#define DELAY_LIST_LOCK_LEVEL ((DWORD) 0x00000300)
+#define POOL_LOCK_LEVEL ((DWORD) 0x00000400)
+#define WORK_QUEUE_LOCK_LEVEL ((DWORD) 0x00000500)
+#define MASTER_LIST_LOCK_LEVEL ((DWORD) 0x00000600)
+#define DUPL_LIST_LOCK_LEVEL ((DWORD) 0x00000700)
+
+
+#endif // ndef _REPLLOCK_
diff --git a/private/net/svcdlls/repl/server/replpack.h b/private/net/svcdlls/repl/server/replpack.h
new file mode 100644
index 000000000..d0cc5d62a
--- /dev/null
+++ b/private/net/svcdlls/repl/server/replpack.h
@@ -0,0 +1,99 @@
+/*++
+
+Copyright (c) 1987-92 Microsoft Corporation
+
+Module Name:
+
+ replpack.h
+
+Abstract:
+ Contains the structure definitions that are required to marshall/unmarshall
+ message buffers. All structures that are defined here are tightly
+ packed, so no field should be accessed directly. These structure
+ definitions are strictly used to get the address of different fields
+ and the size of the structures. These structure fields will be filled
+ and retrieved using SmbPutU* and SmbGetU* functions.
+
+Author:
+
+ 11/07/91 (madana)
+ initial coding.
+
+Environment:
+
+ Contains NT-specific code.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+ 24-Mar-1992 JohnRo
+ Clarify value of pulse_rate field.
+
+--*/
+
+#include <packon.h>
+
+struct pack_msg_header {
+ WORD msg_type;
+ CHAR sender[LM20_CNLEN+1];
+ CHAR senders_domain[LM20_DNLEN+1];
+};
+
+typedef struct pack_msg_header PACK_MSG_HEADER;
+typedef struct pack_msg_header *PPACK_MSG_HEADER;
+typedef struct pack_msg_header *LPPACK_MSG_HEADER;
+
+struct pack_msg_status_rec {
+ WORD dir_name_offset; // offset where name string is
+ // from msg buffer start
+
+ WORD opcode; // 1 - start, 2 - update, 3 - end.
+ DWORD checksum;
+ WORD count;
+ WORD integrity;
+ WORD extent;
+
+};
+
+typedef struct pack_msg_status_rec PACK_MSG_STATUS_REC;
+typedef struct pack_msg_status_rec *PPACK_MSG_STATUS_REC;
+typedef struct pack_msg_status_rec *LPPACK_MSG_STATUS_REC;
+
+struct pack_repl_info {
+ WORD random;
+ WORD sync_rate;
+ WORD pulse_rate; // pulse time * sync rate
+ WORD guard_time;
+};
+
+typedef struct pack_repl_info PACK_REPL_INFO;
+typedef struct pack_repl_info *PPACK_REPL_INFO;
+typedef struct pack_repl_info *LPPACK_REPL_INFO;
+
+struct pack_sync_msg {
+ PACK_MSG_HEADER header;
+ PACK_REPL_INFO info;
+ WORD update_count;
+
+ //
+ // here come update_count msg_status_rec records, the file name strings
+ // stuffed at the end of the buffer by NetPackStr, dir_name is the
+ // offset from the start of the message.
+ //
+
+};
+
+typedef struct pack_sync_msg PACK_SYNCMSG;
+typedef struct pack_sync_msg *PPACK_SYNCMSG;
+typedef struct pack_sync_msg *LPPACK_SYNCMSG;
+
+struct pack_query_msg {
+ PACK_MSG_HEADER header;
+ CHAR dir_name[LM20_PATHLEN]; // ASCIIZ dir/tree name.
+};
+
+typedef struct pack_query_msg PACK_QUERY_MSG;
+typedef struct pack_query_msg *PPACK_QUERY_MSG;
+typedef struct pack_query_msg *LPPACK_QUERY_MSG;
+
+#include <packoff.h>
diff --git a/private/net/svcdlls/repl/server/replset.c b/private/net/svcdlls/repl/server/replset.c
new file mode 100644
index 000000000..471039756
--- /dev/null
+++ b/private/net/svcdlls/repl/server/replset.c
@@ -0,0 +1,516 @@
+/*++
+
+Copyright (c) 1992-1993 Microsoft Corporation
+
+Module Name:
+
+ ReplSet.c
+
+Abstract:
+
+ This file contains NetrReplSetInfo().
+
+Author:
+
+ John Rogers (JohnRo) 10-Feb-1992
+
+Environment:
+
+ User Mode - Win32
+ Portable to any flat, 32-bit environment. (Uses Win32 typedefs.)
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+ 10-Feb-1992 JohnRo
+ Created.
+ 16-Feb-1992 JohnRo
+ Use ReplIs{Interval,GuardTime,Pulse,Random}Valid() macros.
+ 12-Mar-1992 JohnRo
+ Update registry with new values.
+ Added support for setinfo info levels in ReplIsApiRecordValid().
+ 24-Mar-1992 JohnRo
+ Renamed many ReplGlobal vars to ReplConfig vars.
+ 22-Jul-1992 JohnRo
+ RAID 2274: repl svc should impersonate caller.
+ 06-Aug-1992 JohnRo
+ RAID 2252: repl should prevent export on Windows/NT.
+ 27-Aug-1992 JohnRo
+ RAID 4611: Set ParmError if we get an invalid info level (e.g. on
+ a bad import/export list).
+ 22-Oct-92 jimkel
+ Added a call to InitClientList() <- in master.c
+ Added a call to InitClientImpList() <- in client.c
+ These calls cannonicalize the import and export lists and
+ store their cannonicalized forms for internal replicator use.
+ 16-Nov-1992 JohnRo
+ RAID 1537: repl APIs in wrong role kill service.
+ 01-Dec-1992 JohnRo
+ RAID 3844: remote NetReplSetInfo uses local machine type.
+ 16-Dec-1992 JohnRo
+ RAID 1513: Repl does not maintain ACLs (also fix timestamps).
+ 06-Jan-1993 JohnRo
+ Repl WAN support (get rid of repl name list limits).
+ 01-Apr-1993 JohnRo
+ Made changes suggested by PC-LINT 5.0
+ Use NetpKdPrint() where possible.
+--*/
+
+
+// These must be included first:
+
+#include <windef.h> // IN, DWORD, etc.
+#include <lmcons.h> // LAN Manager common definitions
+#include <rpc.h> // Needed by <repl.h>.
+
+// These may be included in any order:
+
+#include <client.h> // RCGlobalExportList, etc.
+#include <confname.h> // REPL_KEYWORD_ equates.
+#include <lmrepl.h> // LPREPL_INFO_0.
+#include <master.h> // RMGlobalImportList, etc.
+#include <netdebug.h> // NetpAssert().
+#include <netlib.h> // NetpMemoryFree(), NetpSetParmError().
+#include <netlock.h> // ACQUIRE_LOCK(), etc.
+#include <prefix.h> // PREFIX_ equates.
+#include <repl.h> // My prototype (in MIDL-generated .h file).
+#include <replconf.h> // ReplConfig routines.
+#include <repldefs.h> // ReplIsRoleValid(), etc.
+#include <replgbl.h> // ReplGlobal and ReplConfig variables.
+#include <rpcutil.h> // NetpImpersonateClient(), NetpRevertToSelf().
+#include <tstring.h> // NetpAlloc{type}From{type}, STRCPY(), TCHAR_EOS, etc.
+#include <winerror.h> // ERROR_ equates, NO_ERROR.
+
+
+//
+// Set config data for the replicator. Only callable when
+// the replicator service is started.
+//
+
+NET_API_STATUS
+NetrReplSetInfo (
+ IN LPTSTR UncServerName OPTIONAL,
+ IN DWORD Level,
+ IN LPCONFIG_CONTAINER Buf,
+ OUT LPDWORD ParmError OPTIONAL
+ )
+{
+ NET_API_STATUS ApiStatus;
+ DWORD OldRole;
+ BOOL Impersonated = FALSE;
+ BOOL Locked = FALSE;
+
+ UNREFERENCED_PARAMETER( UncServerName );
+
+ //
+ // Check for caller's errors.
+ //
+ NetpSetParmError( PARM_ERROR_UNKNOWN ); // Set in case we get bad level.
+
+#define RETURN_BAD_PARM( parm_error ) \
+ { \
+ NetpSetParmError( parm_error ); \
+ ApiStatus = ERROR_INVALID_PARAMETER; \
+ goto Cleanup; /* Don't forget to release lock... */ \
+ }
+
+ if (Buf == NULL) {
+ RETURN_BAD_PARM( PARM_ERROR_UNKNOWN )
+ /*NOTREACHED*/
+ }
+
+ if ( !ReplConfigIsLevelValid( Level, TRUE ) ) { // Yes, allow setinfo lvl
+ ApiStatus = ERROR_INVALID_LEVEL;
+ goto Cleanup;
+ }
+
+ //
+ // We need to check all fields before we set any.
+ //
+
+ if ( !ReplConfigIsApiRecordValid (
+ Level,
+ (LPVOID) (Buf->Info0), // BUGBUG: nonportable?
+ ParmError ) ) {
+
+ ApiStatus = ERROR_INVALID_PARAMETER; // ParmError already set.
+ goto Cleanup;
+ }
+
+ //
+ // Impersonate caller, so security check (write to registry) reflects
+ // the client's process, not the repl service process.
+ //
+ ApiStatus = NetpImpersonateClient();
+ if (ApiStatus != NO_ERROR) {
+ goto Cleanup;
+ }
+ Impersonated = TRUE;
+
+ //
+ // Get exclusive lock on config variables, so we don't get confused by
+ // another API thread. This also locks the matching registry data.
+ //
+ ACQUIRE_LOCK( ReplConfigLock );
+ Locked = TRUE;
+
+ IF_DEBUG( REPLAPI ) {
+ NetpKdPrint(( PREFIX_REPL
+ "NetrReplSetInfo(server side): got config lock OK.\n" ));
+ }
+
+ //
+ // Based on info level, do additional checking and updates.
+ //
+
+ switch (Level) {
+ case 0 :
+ {
+ LPREPL_INFO_0 Buffer = (LPVOID) (Buf->Info0);
+
+ if ( !ReplConfigIsRoleAllowed( NULL, Buffer->rp0_role ) ) {
+ RETURN_BAD_PARM( 1 ) // Role is first field in struct.
+ /*NOTREACHED*/
+ }
+
+ //
+ // Change permanent copy of registry data for the replicator.
+ // This acts as our security check, too.
+ //
+ ApiStatus = ReplConfigWrite(
+ NULL, // no server name
+ Buffer->rp0_role,
+ Buffer->rp0_exportpath,
+ Buffer->rp0_exportlist,
+ Buffer->rp0_importpath,
+ Buffer->rp0_importlist,
+ Buffer->rp0_logonusername,
+ Buffer->rp0_interval,
+ Buffer->rp0_pulse,
+ Buffer->rp0_guardtime,
+ Buffer->rp0_random );
+ if (ApiStatus != NO_ERROR) {
+ goto Cleanup; // Don't forget to release lock...
+ }
+
+ //
+ // Change in-memory copy of registry data for the replicator.
+ //
+#define SET_LIST( globalName, fieldName ) \
+ { \
+ LPTSTR Source = Buffer->rp0_ ## fieldName; \
+ if ( (Source!=NULL) && ( (*Source) != TCHAR_EOS ) ) { \
+ if (globalName != NULL) { \
+ if (STRICMP(globalName, Source) == 0) { \
+ /* OK as is; nothing to do. */ \
+ } else { \
+ /* Old and new are different. Free old and alloc new. */ \
+ NetpMemoryFree( globalName ); \
+ globalName = NetpAllocTStrFromTStr( Source ); \
+ if (globalName == NULL) { \
+ ApiStatus = ERROR_NOT_ENOUGH_MEMORY; \
+ goto Cleanup; /* Don't forget to release lock... */ \
+ } \
+ } \
+ } else { \
+ /* New name but no old name. Alloc new. */ \
+ globalName = NetpAllocTStrFromTStr( Source ); \
+ if (globalName == NULL) { \
+ ApiStatus = ERROR_NOT_ENOUGH_MEMORY; \
+ goto Cleanup; /* Don't forget to release lock... */ \
+ } \
+ } \
+ } else if (globalName != NULL) { \
+ /* Old list but no new list. Free old. */ \
+ NetpMemoryFree( globalName ); \
+ globalName = NULL; \
+ } \
+ }
+
+
+#define SET_PATH( globalName, fieldName ) \
+ { \
+ LPTSTR Source = Buffer->rp0_ ## fieldName; \
+ NetpAssert( globalName != NULL ); \
+ if ( (Source!=NULL) && ( (*Source) != TCHAR_EOS ) ) { \
+ (void) STRCPY( globalName, Source ); \
+ } else { \
+ globalName[0] = TCHAR_EOS; \
+ } \
+ }
+
+ // Note: because of possible shock to other code, set role last.
+
+ SET_PATH( ReplConfigExportPath, exportpath );
+
+ // The UI will update REPL$ share to reflect new export path.
+ // Repl svc may not be running as admin so can't do NetShareAdd?
+ // BUGBUG: Hey, we impersonated, so maybe we can!
+
+ SET_LIST( ReplConfigExportList, exportlist );
+
+ SET_PATH( ReplConfigImportPath, importpath );
+ RCGlobalFsTimeResolutionSecs =
+ ReplGetFsTimeResolutionSecs( ReplConfigImportPath );
+
+
+ SET_LIST( ReplConfigImportList, importlist );
+
+ // BUGBUG: Set logonusername!!!
+
+ ReplConfigInterval = Buffer->rp0_interval;
+ ReplConfigPulse = Buffer->rp0_pulse;
+ ReplConfigGuardTime = Buffer->rp0_guardtime;
+ ReplConfigRandom = Buffer->rp0_random;
+
+ //
+ // If the role is not changing make sure the internal
+ // variables are updated.
+ //
+
+ OldRole = ReplConfigRole;
+
+ IF_DEBUG( REPLAPI ) {
+ NetpKdPrint(( PREFIX_REPL
+ "NetrReplSetInfo(server side): initing lists.\n" ));
+ }
+
+ if ( ReplRoleIncludesExport(Buffer->rp0_role)
+ && ReplRoleIncludesExport(OldRole) ) {
+
+ if (RMGlobalImportList != NULL) {
+ NetpMemoryFree( RMGlobalImportList );
+ RMGlobalImportList = NULL;
+ }
+
+ // NOTE: ReplInitAnyList() assumes caller has config data lock.
+ ApiStatus = ReplInitAnyList(
+ (LPCTSTR) ReplConfigExportList, // uncanon
+ & RMGlobalImportList, // canon list: alloc and set ptr
+ (LPCTSTR) REPL_KEYWORD_EXPLIST, // config keyword name
+ & RMGlobalImportCount ); // set entry count too
+
+ if ( ApiStatus != NO_ERROR )
+ goto Cleanup;
+ }
+
+ if ( ReplRoleIncludesImport(Buffer->rp0_role)
+ && ReplRoleIncludesImport(OldRole) ) {
+
+ if (RCGlobalExportList != NULL) {
+ NetpMemoryFree( RCGlobalExportList );
+ RCGlobalExportList = NULL;
+ }
+
+ // NOTE: ReplInitAnyList() assumes caller has config data lock.
+ ApiStatus = ReplInitAnyList(
+ (LPCTSTR) ReplConfigImportList, // uncanon
+ & RCGlobalExportList, // canon list: alloc and set ptr
+ (LPCTSTR) REPL_KEYWORD_IMPLIST, // config keyword name
+ & RCGlobalExportCount ); // set entry count too
+ if ( ApiStatus != NO_ERROR )
+ goto Cleanup;
+ }
+
+ //
+ // Change the role, including all the side-effects (like
+ // starting and stopping threads). Also set ReplConfigRole.
+ // NOTE: ReplChangeRole assumes caller has exclusive lock on
+ // ReplConfigLock.
+ //
+
+ IF_DEBUG( REPLAPI ) {
+ NetpKdPrint(( PREFIX_REPL
+ "NetrReplSetInfo(server side): changing role.\n" ));
+ }
+
+ ApiStatus = ReplChangeRole( Buffer->rp0_role );
+ NetpAssert( ApiStatus == NO_ERROR );
+
+ IF_DEBUG( REPLAPI ) {
+ NetpKdPrint(( PREFIX_REPL
+ "NetrReplSetInfo(server side): "
+ "done changing role.\n" ));
+ }
+
+
+ // Don't forget to unimpersonate.
+ }
+ break;
+
+ case 1000 :
+ {
+ LPREPL_INFO_1000 Buffer = (LPVOID) (Buf->Info1000);
+ DWORD NewValue = Buffer->rp1000_interval;
+
+ if ( !ReplIsIntervalValid( NewValue ) ) {
+ RETURN_BAD_PARM( 1 ) // first field in structure.
+ /*NOTREACHED*/
+ }
+
+ //
+ // Change permanent copy of registry data for the replicator.
+ // This acts as our security check, too.
+ //
+ ApiStatus = ReplConfigWrite(
+ NULL, // no server name
+ ReplConfigRole,
+ ReplConfigExportPath,
+ ReplConfigExportList,
+ ReplConfigImportPath,
+ ReplConfigImportList,
+ ReplConfigLogonUserName,
+ NewValue, // new interval
+ ReplConfigPulse,
+ ReplConfigGuardTime,
+ ReplConfigRandom );
+ if (ApiStatus != NO_ERROR) {
+ goto Cleanup; // Don't forget to release lock...
+ }
+
+ ReplConfigInterval = NewValue;
+
+ // Don't forget to unlock and unimpersonate.
+
+ // BUGBUG: Notify anybody?
+ }
+ break;
+
+ case 1001 :
+ {
+ LPREPL_INFO_1001 Buffer = (LPVOID) (Buf->Info1001);
+ DWORD NewValue = Buffer->rp1001_pulse;
+
+ if ( !ReplIsPulseValid( NewValue ) ) {
+ RETURN_BAD_PARM( 1 ) // first field in structure.
+ /*NOTREACHED*/
+ }
+
+ //
+ // Change permanent copy of registry data for the replicator.
+ // This acts as our security check, too.
+ //
+ ApiStatus = ReplConfigWrite(
+ NULL, // no server name
+ ReplConfigRole,
+ ReplConfigExportPath,
+ ReplConfigExportList,
+ ReplConfigImportPath,
+ ReplConfigImportList,
+ ReplConfigLogonUserName,
+ ReplConfigInterval,
+ NewValue, // new pulse
+ ReplConfigGuardTime,
+ ReplConfigRandom );
+ if (ApiStatus != NO_ERROR) {
+ goto Cleanup; // Don't forget to release lock...
+ }
+
+ ReplConfigPulse = NewValue;
+
+ // Don't forget to unlock and unimpersonate.
+
+ // BUGBUG: Notify anybody?
+ }
+ break;
+
+ case 1002 :
+ {
+ LPREPL_INFO_1002 Buffer = (LPVOID) (Buf->Info1002);
+ DWORD NewValue = Buffer->rp1002_guardtime;
+
+ if ( !ReplIsGuardTimeValid( NewValue ) ) {
+ RETURN_BAD_PARM( 1 ) // first field in structure.
+ /*NOTREACHED*/
+ }
+
+ //
+ // Change permanent copy of registry data for the replicator.
+ // This acts as our security check, too.
+ //
+ ApiStatus = ReplConfigWrite(
+ NULL, // no server name
+ ReplConfigRole,
+ ReplConfigExportPath,
+ ReplConfigExportList,
+ ReplConfigImportPath,
+ ReplConfigImportList,
+ ReplConfigLogonUserName,
+ ReplConfigInterval,
+ ReplConfigPulse,
+ NewValue, // new guard time
+ ReplConfigRandom );
+ if (ApiStatus != NO_ERROR) {
+ goto Cleanup; // Don't forget to release lock...
+ }
+
+ ReplConfigGuardTime = NewValue;
+
+ // Don't forget to unlock and unimpersonate.
+
+ // BUGBUG: Notify anybody?
+ }
+ break;
+
+ case 1003 :
+ {
+ LPREPL_INFO_1003 Buffer = (LPVOID) (Buf->Info1003);
+ DWORD NewValue = Buffer->rp1003_random;
+
+ if ( !ReplIsRandomValid( NewValue ) ) {
+ RETURN_BAD_PARM( 1 ) // first field in structure.
+ /*NOTREACHED*/
+ }
+
+ //
+ // Change permanent copy of registry data for the replicator.
+ // This acts as our security check, too.
+ //
+ ApiStatus = ReplConfigWrite(
+ NULL, // no server name
+ ReplConfigRole,
+ ReplConfigExportPath,
+ ReplConfigExportList,
+ ReplConfigImportPath,
+ ReplConfigImportList,
+ ReplConfigLogonUserName,
+ ReplConfigInterval,
+ ReplConfigPulse,
+ ReplConfigGuardTime,
+ NewValue ); // new random time
+ if (ApiStatus != NO_ERROR) {
+ goto Cleanup; // Don't forget to release lock...
+ }
+
+ ReplConfigRandom = NewValue;
+
+ // Don't forget to unlock and unimpersonate.
+
+ }
+ break;
+
+ default :
+ NetpAssert( FALSE ); // Can't get here.
+ }
+
+ //
+ // All done.
+ //
+
+Cleanup:
+
+ if (Impersonated) {
+ (VOID) NetpRevertToSelf();
+ }
+
+ if (Locked) {
+ RELEASE_LOCK( ReplConfigLock );
+ }
+
+ if (ApiStatus == NO_ERROR) {
+ NetpSetParmError( PARM_ERROR_NONE );
+ }
+
+ return (ApiStatus);
+
+} // NetpReplSetInfo
diff --git a/private/net/svcdlls/repl/server/replstal.c b/private/net/svcdlls/repl/server/replstal.c
new file mode 100644
index 000000000..6c01d1434
--- /dev/null
+++ b/private/net/svcdlls/repl/server/replstal.c
@@ -0,0 +1,137 @@
+/*++
+
+Copyright (c) 1992-1993 Microsoft Corporation
+
+Module Name:
+
+ ReplStal.c
+
+Abstract:
+
+ Contains staller thread. This is just used to keep the NET command happy
+ while we are stopping.
+
+Author:
+
+ JR (John Rogers, JohnRo@Microsoft) 11-Dec-1992
+
+Environment:
+
+ User mode only.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+ Tab size is set to 4.
+
+Revision History:
+
+ 11-Dec-1992 JohnRo
+ Created for RAID 3316: _access violation while stopping the replicator
+ 02-Apr-1993 JohnRo
+ Use NetpKdPrint() where possible.
+
+--*/
+
+
+// These must be included first:
+
+#include <windows.h> // IN, DWORD, Sleep(), NO_ERROR, etc.
+#include <lmcons.h> // (Needed by repldefs.h/lmrepl.h)
+
+// These may be included in any order:
+
+#include <netdebug.h> // NetpAssert(), NetpKdPrint(), FORMAT_ equates.
+#include <prefix.h> // PREFIX_ equates.
+#include <repldefs.h> // IF_DEBUG(), my prototype.
+#include <replgbl.h> // ReplGlobal and ReplConfig variables.
+#include <thread.h> // FORMAT_NET_THREAD_ID, NetpCurrentThread().
+#include <winsvc.h> // SERVICE_STATUS_HANDLE, etc.
+
+
+
+DWORD
+ReplStaller(
+ IN LPVOID Parm
+ )
+
+/*++
+
+Called by CreateThread.
+
+Threads:
+
+ Only called by staller thread.
+
+--*/
+
+
+{
+ DWORD SleepMilliseconds;
+
+ UNREFERENCED_PARAMETER( Parm );
+
+ IF_DEBUG( STALLER ) {
+ NetpKdPrint(( PREFIX_REPL
+ "ReplStaller: thread ID is "
+ FORMAT_NET_THREAD_ID ".\n", NetpCurrentThread() ));
+ }
+
+ NetpAssert( ReplGlobalIsServiceStopping );
+
+ //
+ // Compute sleep time. This must be less than NET.EXE's amount, so
+ // it doesn't decide to give up on us.
+ //
+ SleepMilliseconds = MAX_USABLE_WAIT_HINT / 2;
+ NetpAssert( SleepMilliseconds > 1000 );
+ NetpAssert( SleepMilliseconds < MAX_USABLE_WAIT_HINT );
+
+
+ //
+ // Loop, stalling, until the stopper thread is done.
+ //
+ while (ReplGlobalIsServiceStopping) {
+
+ //
+ // Sleep
+ //
+ Sleep( SleepMilliseconds );
+
+ IF_DEBUG( STALLER ) {
+ NetpKdPrint(( PREFIX_REPL
+ "ReplStaller: reporting checkpoint " FORMAT_DWORD ".\n",
+ ReplGlobalCheckpoint ));
+ }
+
+ //
+ // Tell service controller that we're working on it.
+ //
+ ReportStatus(
+ SERVICE_STOP_PENDING, // new state
+ NO_ERROR,
+ REPL_WAIT_HINT,
+ ReplGlobalCheckpoint );
+
+ ++ReplGlobalCheckpoint;
+
+ }
+
+ //
+ // Tell service controller that we're all done.
+ //
+ IF_DEBUG(REPL) {
+ NetpKdPrint(( PREFIX_REPL
+ "ReplChangeRole: setting stopped state...\n" ));
+ }
+ ReportStatus(
+ SERVICE_STOPPED,
+ ReplGlobalUninstallUicCode,
+ 0, // wait hint
+ 0 ); // checkpoint
+
+ IF_DEBUG( STALLER ) {
+ NetpKdPrint(( PREFIX_REPL
+ "ReplStaller: exiting thread.\n" ));
+ }
+
+ return ( (DWORD) NO_ERROR );
+
+}
diff --git a/private/net/svcdlls/repl/server/replstop.c b/private/net/svcdlls/repl/server/replstop.c
new file mode 100644
index 000000000..fa3880366
--- /dev/null
+++ b/private/net/svcdlls/repl/server/replstop.c
@@ -0,0 +1,121 @@
+/*++
+
+Copyright (c) 1992-1993 Microsoft Corporation
+
+Module Name:
+
+ ReplStop.c
+
+Abstract:
+
+ Contains stopper thread. This is created when we want to stop the service.
+
+Author:
+
+ JR (John Rogers, JohnRo@Microsoft) 08-Dec-1992
+
+Environment:
+
+ User mode only.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+ Tab size is set to 4.
+
+Revision History:
+
+ 08-Dec-1992 JohnRo
+ Created for RAID 3316: _access violation while stopping the replicator
+ 28-Apr-1993 JohnRo
+ Use NetpKdPrint() where possible.
+
+--*/
+
+
+// These must be included first:
+
+#include <windows.h> // IN, DWORD, NO_ERROR, etc.
+#include <lmcons.h> // (Needed by repldefs.h/lmrepl.h)
+
+// These may be included in any order:
+
+#include <netdebug.h> // NetpAssert(), NetpKdPrint(), FORMAT_ equates.
+#include <prefix.h> // PREFIX_ equates.
+#include <repldefs.h> // IF_DEBUG(), my prototype.
+#include <replgbl.h> // ReplGlobal and ReplConfig variables.
+#include <thread.h> // FORMAT_NET_THREAD_ID, NetpCurrentThread().
+
+
+
+DWORD
+ReplStopper(
+ IN LPVOID Parm
+ )
+
+/*++
+
+Called by CreateThread.
+
+Threads:
+
+ Only called by Stopper thread.
+
+--*/
+
+
+{
+ NET_API_STATUS ApiStatus;
+ BOOL LockedConfigData = FALSE;
+
+ UNREFERENCED_PARAMETER( Parm );
+
+ IF_DEBUG( STOPPER ) {
+ NetpKdPrint(( PREFIX_REPL
+ "ReplStopper: thread ID is "
+ FORMAT_NET_THREAD_ID ".\n", NetpCurrentThread() ));
+ }
+
+ NetpAssert( ReplGlobalIsServiceStopping );
+
+ ACQUIRE_LOCK( ReplConfigLock );
+ LockedConfigData = TRUE;
+
+ NetpAssert( ReplConfigRole != REPL_ROLE_STOPPED );
+
+ IF_DEBUG( STOPPER ) {
+ NetpKdPrint(( PREFIX_REPL
+ "ReplStopper: changing role...\n" ));
+ }
+
+ //
+ // Change the role to stopped. This will tell the exporting and
+ // importing threads and the RPC server. It will also set the
+ // service controller's status to stopped.
+ // NOTE: ReplChangeRole assumes caller has exclusive lock on
+ // ReplConfigLock.
+ //
+ NetpAssert( LockedConfigData );
+ ApiStatus = ReplChangeRole( REPL_ROLE_STOPPED );
+ NetpAssert( ApiStatus == NO_ERROR );
+
+ IF_DEBUG( STOPPER ) {
+ NetpKdPrint(( PREFIX_REPL
+ "ReplStopper: done changing role.\n" ));
+ }
+
+ if (LockedConfigData) {
+ RELEASE_LOCK( ReplConfigLock );
+ }
+
+ //
+ // The staller thread has been keeping NET.EXE busy for us. Tell
+ // the staller that it can go away.
+ //
+ ReplGlobalIsServiceStopping = FALSE;
+
+ IF_DEBUG( STOPPER ) {
+ NetpKdPrint(( PREFIX_REPL
+ "ReplStopper: exiting thread.\n" ));
+ }
+
+ return ( (DWORD) NO_ERROR );
+
+}
diff --git a/private/net/svcdlls/repl/server/scanqs.c b/private/net/svcdlls/repl/server/scanqs.c
new file mode 100644
index 000000000..f22b14e30
--- /dev/null
+++ b/private/net/svcdlls/repl/server/scanqs.c
@@ -0,0 +1,252 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ ScanQs.c
+
+Abstract:
+
+ Contains:
+ ReplScanMessageForMatchingDirName
+ ReplScanQueuesForMoreRecentMsg
+
+ BUGBUG Should this stuff also match on the right master?
+
+Author:
+
+ JR (John Rogers, JohnRo@Microsoft)
+
+Environment:
+
+ User mode only.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+ 30-Mar-1993 JohnRo
+ Created for RAID 4267: Replicator has problems when work queue gets
+ large.
+ 31-Mar-1993 JohnRo
+ Repl watchdog should scan queues too.
+
+--*/
+
+
+// These must be included first:
+
+#include <windows.h>
+#include <lmcons.h>
+
+// These may be included in any order:
+
+#include <client.h> // My prototype, PBIGBUF_REC, RCGlobalDelayQueue, etc.
+#include <netdebug.h> // NetpAssert(), NetpKdPrint(), FORMAT_ equates, etc.
+#include <netlock.h> // Lock data types, functions, and macros.
+#include <prefix.h> // PREFIX_ equates.
+#include <repldefs.h> // IF_DEBUG().
+#include <tstr.h> // STRICMP(), TCHAR_EOS.
+
+
+DBGSTATIC BOOL
+ReplScanMessageForMatchingDirName(
+ IN LPCTSTR DirNameWanted,
+ IN PSYNCMSG UpdateMessage
+ )
+{
+ DWORD DirCount;
+ LPTSTR DirNameFound;
+ BOOL Found = FALSE;
+ PMSG_STATUS_REC StatusRecord;
+
+ NetpAssert( DirNameWanted != NULL );
+ NetpAssert( (*DirNameWanted) != TCHAR_EOS );
+ NetpAssert( UpdateMessage != NULL );
+
+ //
+ // Look at the dirs in this message.
+ //
+
+ StatusRecord = (PMSG_STATUS_REC) (LPVOID) (UpdateMessage + 1);
+
+ //
+ // Loop for each directory in this update message.
+ //
+
+ DirCount = UpdateMessage->update_count;
+ NetpAssert( DirCount > 0 );
+
+ while (DirCount--) {
+
+ DirNameFound = (LPTSTR)
+ (((PCHAR)UpdateMessage) + StatusRecord->dir_name_offset);
+
+ if (STRICMP( DirNameWanted, DirNameFound ) == 0 ) {
+
+ Found = TRUE;
+
+ goto Cleanup; // don't forget to unlock...
+ }
+
+ ++StatusRecord; // increment status pointer.
+
+ } // for each dir in this message...
+
+
+ //
+ // No match.
+ //
+ NetpAssert( Found == FALSE );
+
+Cleanup:
+
+ IF_DEBUG( SYNC ) {
+
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplScanMessageForMatchingDirName: " FORMAT_LPSTR
+ " find a match for '" FORMAT_LPTSTR "' in update message at "
+ FORMAT_LPVOID ".\n",
+ Found ? "did" : "did not",
+ DirNameWanted, (LPVOID) UpdateMessage ));
+
+ }
+
+ return (Found);
+
+} // ReplScanMessageForMatchingDirName
+
+
+BOOL
+ReplScanQueuesForMoreRecentMsg(
+ IN LPCTSTR DirNameWanted
+ )
+/*++
+
+Routine Description:
+
+ ReplScanQueuesForMoreRecentMsg scans the work queue and the delay list
+ for messages matching a given directory name.
+
+Arguments:
+
+ DirNameWanted - First level directory name to search for.
+
+Return Value:
+
+ TRUE iff a message matches DirNameWanted.
+
+Threads:
+
+ Called by syncer and watchd threads.
+
+--*/
+{
+ PBIGBUF_REC CurrentDelayRecord;
+ PBIGBUF_REC * DelayRecord;
+ BOOL Found = FALSE;
+ BOOL DelayListLocked = FALSE;
+ DWORD MessageType;
+ PSYNCMSG UpdateMessage;
+ PBIGBUF_REC WorkQueueRecord;
+ BOOL WorkQueueLocked = FALSE;
+
+
+ //
+ // Scan the work queue for matches for this dir name.
+ //
+
+ ACQUIRE_LOCK_SHARED( RCGlobalWorkQueueLock );
+ WorkQueueLocked = TRUE;
+
+ WorkQueueRecord = RCGlobalWorkQueueHead;
+
+ while ( WorkQueueRecord != NULL ) {
+
+ UpdateMessage = (LPVOID) (WorkQueueRecord->data);
+
+ MessageType = UpdateMessage->header.msg_type;
+
+ if ( (MessageType==SYNC_MSG)
+ || (MessageType==GUARD_MSG)
+ || (MessageType==PULSE_MSG) ) {
+
+ if (ReplScanMessageForMatchingDirName(
+ DirNameWanted,
+ UpdateMessage)) {
+
+ Found = TRUE;
+ goto Cleanup; // don't forget to unlock...
+ }
+ }
+ WorkQueueRecord = WorkQueueRecord->next_p;
+
+ } // while more records in work queue
+
+ RELEASE_LOCK( RCGlobalWorkQueueLock );
+ WorkQueueLocked = FALSE;
+
+
+
+ //
+ // Set things up so we can walk the delay list.
+ // Make sure no one's updating the list.
+ //
+
+ ACQUIRE_LOCK_SHARED( RCGlobalDelayListLock );
+ DelayListLocked = TRUE;
+
+ DelayRecord = &RCGlobalDelayListHeader;
+
+ while ( *DelayRecord != NULL ) {
+
+ CurrentDelayRecord = *DelayRecord;
+
+ //
+ // Look at the dirs in this message.
+ //
+ UpdateMessage = (LPVOID) (CurrentDelayRecord->data);
+
+ MessageType = UpdateMessage->header.msg_type;
+ NetpAssert( (MessageType==SYNC_MSG) || (MessageType==GUARD_MSG) );
+
+ if (ReplScanMessageForMatchingDirName( DirNameWanted, UpdateMessage)) {
+
+ Found = TRUE;
+
+ goto Cleanup; // don't forget to unlock...
+ }
+
+ DelayRecord = &CurrentDelayRecord->next_p;
+
+ } // while more messages in delay list...
+
+
+ //
+ // No match. Don't forget to unlock list.
+ //
+ NetpAssert( Found == FALSE );
+
+Cleanup:
+
+ IF_DEBUG( SYNC ) {
+
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplScanQueuesForMoreRecentMsg: " FORMAT_LPSTR
+ " find a match for '" FORMAT_LPTSTR "'.\n",
+ Found ? "did" : "did not",
+ DirNameWanted ));
+
+ }
+
+ if (DelayListLocked) {
+ RELEASE_LOCK( RCGlobalDelayListLock );
+ }
+
+ if (WorkQueueLocked) {
+ RELEASE_LOCK( RCGlobalWorkQueueLock );
+ }
+
+ return (Found);
+
+} // ReplScanQueuesForMoreRecentMsg
diff --git a/private/net/svcdlls/repl/server/sources b/private/net/svcdlls/repl/server/sources
new file mode 100644
index 000000000..bf489dfbd
--- /dev/null
+++ b/private/net/svcdlls/repl/server/sources
@@ -0,0 +1,176 @@
+!IF 0
+
+Copyright (c) 1989-1993 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+Author:
+
+ Steve Wood (stevewo) 12-Apr-1990
+
+NOTE: Commented description of this file is in \nt\public\oak\bin\sources.tpl
+
+!ENDIF
+
+#
+# The MAJORCOMP and MINORCOMP variables are defined
+# so that $(MAJORCOMP)$(MINORCOMP)filename can be used in
+# cross compiling to provide unique filenames in a flat namespace.
+#
+
+MAJORCOMP=net
+MINORCOMP=repl
+
+#
+# The TARGETNAME variable is defined by the developer. It is the name of
+# the target (component) that is being built by this makefile. It
+# should NOT include any path or file extension information.
+#
+
+TARGETNAME=repl
+
+#
+# The TARGETPATH and TARGETTYPE variables are defined by the developer.
+# The first specifies where the target is to be build. The second specifies
+# the type of target (either PROGRAM, DYNLINK, LIBRARY, UMAPPL_NOLIB or
+# BOOTPGM). UMAPPL_NOLIB is used when you're only building user-mode
+# apps and don't need to build a library.
+#
+
+TARGETPATH=obj
+
+# Pick one of the following and delete the others
+TARGETTYPE=LIBRARY
+
+#
+# The TARGETLIBS specifies additional libraries to link with you target
+# image. Each library path specification should contain an asterisk (*)
+# where the machine specific subdirectory name should go.
+#
+
+TARGETLIBS=
+
+#
+# The INCLUDES variable specifies any include paths that are specific to
+# this source directory. Separate multiple directory paths with single
+# semicolons. Relative path specifications are okay.
+#
+
+INCLUDES=.;..\common;..\..\..\inc;..\..\..\api;..\..\..\..\inc;
+
+#
+# The SOURCES variable is defined by the developer. It is a list of all the
+# source files for this component. Each source file should be on a separate
+# line using the line continuation character. This will minimize merge
+# conflicts if two developers adding source files to the same component.
+#
+
+!IFNDEF DISABLE_NET_UNICODE
+UNICODE=1
+NET_C_DEFINES=-DUNICODE
+!ENDIF
+
+USE_CRTDLL=1
+
+MSC_WARNING_LEVEL=/W3 /WX
+
+SOURCES= \
+ CacheTim.c \
+ checksum.c \
+ ChkLocks.c \
+ ChngRole.c \
+ cli_dupl.c \
+ cli_list.c \
+ client.c \
+ CliQuery.c \
+ CopyDir.c \
+ CopyFile.c \
+ CopyTime.c \
+ CopyTree.c \
+ error.c \
+ EntCount.c \
+ ExpAdd.c \
+ ExpDel.c \
+ ExpEnum.c \
+ ExpGet.c \
+ ExpLock.c \
+ ExpRead.c \
+ ExpSet.c \
+ ExpStart.c \
+ ExpStop.c \
+ filefind.c \
+ ImpAdd.c \
+ ImpDel.c \
+ ImpEnum.c \
+ ImpGet.c \
+ ImpLock.c \
+ ImpRead.c \
+ ImpStart.c \
+ ImpStop.c \
+ InitAny.c \
+ marshall.c \
+ master.c \
+ MkSecAtt.c \
+ Permit.c \
+ puls_msg.c \
+ pulser.c \
+ repl.c \
+ ReplGet.c \
+ ReplSet.c \
+ ReplStal.c \
+ ReplStop.c \
+ repl_s.c \
+ ScanQs.c \
+ syncer.c \
+ syncmisc.c \
+ synctree.c \
+ watchd.c \
+ wcslocal.c
+
+# i386_SOURCES=i386\source1.asm
+
+# MIPS_SOURCES=mips\source1.s
+
+#
+# Next specify options for the compiler.
+#
+
+# C_DEFINES=-DTRACE -DTRACE2 -DDEBUG
+C_DEFINES=-DRPC_NO_WINDOWS_H
+WARNING_LEVEL=-W4
+
+#
+# Next specify one or more user mode test programs and their type
+# UMTEST is used for optional test programs. UMAPPL is used for
+# programs that always get built when the directory is built.
+#
+
+# UMTYPE=console
+# UMAPPL=cliff
+# UMLIBS= obj\*\netlogon.lib \
+# $(BASEDIR)\Public\Sdk\Lib\*\netlib.lib \
+# $(BASEDIR)\public\sdk\lib\*\netapi32.lib \
+# $(BASEDIR)\public\sdk\lib\*\winrpc.lib \
+# $(BASEDIR)\public\sdk\lib\*\ndrlib.lib
+
+#
+# Defining either (or both) the variables NTTARGETFILE0 and/or NTTARGETFILES
+# will cause MAKEFILE.DEF to include .\makefile.inc immediately after it
+# specifies the top level targets (all, clean and loc) and their dependencies.
+# MAKEFILE.DEF also expands NTTARGETFILE0 as the first dependent for the
+# "all" target and NTTARGETFILES as the last dependent for the "all" target.
+# Useful for specifying additional targets and dependencies that don't fit the
+# general case covered by MAKEFILE.DEF
+#
+
+# NTTARGETFILE0=
+# NTTARGETFILES=
diff --git a/private/net/svcdlls/repl/server/syncer.c b/private/net/svcdlls/repl/server/syncer.c
new file mode 100644
index 000000000..86b476a53
--- /dev/null
+++ b/private/net/svcdlls/repl/server/syncer.c
@@ -0,0 +1,1664 @@
+/*++
+
+Copyright (c) 1987-1993 Microsoft Corporation
+
+Module Name:
+
+ syncer.c
+
+Abstract:
+
+ Contains Syncer thread - does actual work for REPL client.
+
+Author:
+
+ Ported from Lan Man 2.1
+
+Environment:
+
+ User mode only.
+ Contains NT-specific code.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+ 11-Apr-1989 (yuv)
+ Initial Coding.
+
+ 21-Oct-1991 (cliffv)
+ Ported to NT. Converted to NT style.
+
+ 30-Dec-1991 JohnRo
+ Made some changes suggested by PC-LINT.
+ 16-Jan-1992 JohnRo
+ Changed file name from repl.h to replgbl.h to avoid MIDL conflict.
+ Added debug print of thread ID.
+ Changed to use NetLock.h (allow shared locks, for one thing).
+ Use REPL_STATE_ equates for client_list_rec.state values.
+ 24-Jan-1992 JohnRo
+ P_ globals are now called ReplGlobal variables in replgbl.h.
+ Changed to use LPTSTR etc.
+ 24-Mar-1992 JohnRo
+ Renamed many ReplGlobal vars to ReplConfig vars.
+ Added/corrected some lock handling.
+ Added a little more debug output.
+ 25-Mar-1992 JohnRo
+ Added some thread comments.
+ 27-Mar-1992 JohnRo
+ Just a debug output adjustment.
+ 29-Jul-1992 JohnRo
+ RAID 2650: repl svc should handle new subdirs.
+ 19-Aug-1992 JohnRo
+ RAID 2115: repl svc should wait while stopping or changing role.
+ Use PREFIX_ equates.
+ 05-Nov-1992 JohnRo
+ RAID 5496: old fields not maintained in registry.
+ Added some debug output when setting no sync state.
+ 17-Jan-1993 JohnRo
+ RAID 7053: locked trees added to pulse msg. (Actually fix all
+ kinds of remote lock handling.)
+ Made changes suggested by PC-LINT 5.0
+ Added some debug checks.
+ 01-Feb-1993 JohnRo
+ RAID 7983: Repl svc needs to change process token to really copy ACLs.
+ 15-Feb-1993 JohnRo
+ RAID 8355: setting repl lock when registry doesn't exist causes assert.
+ 11-Mar-1993 JohnRo
+ RAID 14144: avoid very long hourglass in repl UI.
+ 26-Mar-1993 JohnRo
+ RAID 4267: Replicator has problems when work queue gets large.
+ We didn't really need excl lock on client list when syncing with tree
+ integrity.
+ Also, prepare for >32 bits someday.
+ 01-Apr-1993 JohnRo
+ Reduce checksum frequency by using change notify on import tree.
+ Don't generate repl sys err popup for access denied for every cycle.
+ 05-Apr-1993 JohnRo
+ RAID 5091: directory state not reflected accurately (in registry during
+ sync).
+ 21-Apr-1993 JohnRo
+ RAID 7298: repl stops when it didn't get _access to an import dir.
+ 23-Apr-1993 JohnRo
+ RAID 7157: fix setting ACLs, etc., on dirs themselves.
+ 28-Apr-1993 JohnRo
+ Use NetpKdPrint() where possible.
+ 25-May-1993 JohnRo
+ RAID 11103: repl svc doesn't handle time being set back.
+ 10-Jun-1993 JohnRo
+ RAID 13080: Allow repl between different timezones.
+
+--*/
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+
+#include <windef.h>
+#include <winbase.h>
+#include <lmcons.h>
+
+#include <alertmsg.h> // ALERT_* defines
+#include <icanon.h> // I_NetPathCompare
+#include <impdir.h> // ImportDirWriteConfigData().
+#include <lmerr.h> // NERR_* defines
+#include <lmerrlog.h> // NELOG_* defines
+#include <lmrepl.h> // REPL_STATE_ equates.
+#include <masproto.h> // ReplCheckExportLocks().
+#include <netdebug.h> // DBGSTATIC, NetDbgHexDump(), FORMAT_ equates, etc.
+#include <netlock.h> // Lock data types, functions, and macros.
+#include <prefix.h> // PREFIX_ equates.
+#include <thread.h> // FORMAT_NET_THREAD_ID, NetpCurrentThread().
+#include <time.h> // ctime(), time(), etc.
+#include <tstr.h> // STRLEN(), TCHAR_EOS, etc.
+
+//
+// Local include files
+//
+#include <repldefs.h> // IF_DEBUG(), etc.
+#include <replgbl.h> // ReplGlobal and ReplConfig variables.
+#include <client.h>
+#include <replp.h>
+
+// G L O B A L S:
+#if DBG
+DBGSTATIC int RCGlobalSyncReceived[NEXT_AVAILABLE_MESSAGE_NUMBER] = {
+ 0};
+#endif
+
+//
+// Global variable indicating whether logon is needed.
+//
+//
+// logged_on is TRUE iff "someone" is logged onto the workstation.
+// (i.e. after checked someone is logged on)
+//
+
+#ifdef LOGON_DEFINED
+
+DBGSTATIC BOOL logged_on = FALSE;
+
+//
+// need_logoff is TRUE iff we have explictly logged on and must logoff.
+//
+DBGSTATIC BOOL need_logoff = FALSE;
+
+#endif // LOGON_DEFINED
+
+
+
+DBGSTATIC NET_API_STATUS
+ReplLogon(
+ IN PCLIENT_LIST_REC cur_rec
+ )
+/*++
+
+Routine Description:
+
+ Takes care of logging on - Client repl can't _access master when no logon.
+
+ Assumes that caller has a lock (any kind) on RCGlobalClientListLock.
+
+ if (USER LOGGED ON)
+ if(ReplConfigTryUser = NO)
+ STOP (client service cannot proceed).
+ else
+ OK (just take advantage of the user's logon)
+
+ else (USER NOT LOGGED ON)
+ if (ReplConfigLogonUserName != NULL)
+ TRY LOGGING ON USING ReplConfigLogonUserName and P_passwd.....
+
+ else (ReplConfigLogonUserName == NULL)
+ TRY LOGGONG ON USING the local COMPUTERNAME and a NULL password.
+ if fails try establishing a new password.
+
+Arguments:
+
+ cur_rec - The client tree record for the directory being replicated.
+ (Used only to determine who the master is).
+
+Return Value:
+
+ 0 - if succesfull.
+
+Threads:
+
+ Only called by syncer thread.
+
+--*/
+{
+
+#ifdef LOGON_DEFINED
+
+ //
+ // If we've already successfully logged on, don't do it again.
+ //
+
+ if ( logged_on ) {
+ return (NO_ERROR);
+ }
+ char buf[MAX_WKSTA_INFO_SIZE_10];
+ struct wksta_info_10 *info;
+ unsigned short avail;
+ NET_API_STATUS NetStatus;
+
+ info = (struct wksta_info_10 * )buf;
+
+ if (NetStatus = NetWkstaGetInfo(NULL, 10, info,
+ MAX_WKSTA_INFO_SIZE_10, (unsigned short * ) & avail)) {
+ AlertLogExit(ALERT_ReplNetErr, NELOG_ReplNetErr, NetStatus,
+ NULL, NULL, NO_EXIT);
+ return (NetStatus);
+ }
+
+ //
+ // a user is logged on.
+ //
+
+ ACQUIRE_LOCK_SHARED( ReplConfigLock );
+ if (info->wki10_username[0] != 0) {
+ if (ReplConfigTryUser) {
+
+ //
+ // all is well a user is logged on and client is permitted
+ // to piggyback.
+ //
+
+ RELEASE_LOCK( ReplConfigLock );
+ return (NO_ERROR);
+ } else {
+
+ IF_DEBUG( SYNC ) {
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplLogon: setting no sync (user logged on)\n" ));
+ }
+
+ // Note: ReplSetSignalFile needs lock (any kind) on
+ // RCGlobalClientListLock.
+ ReplSetSignalFile(cur_rec, REPL_STATE_NO_SYNC);
+ if ((cur_rec->alerts & USER_LOGGED_ALERT) == 0) {
+ AlertLogExit(ALERT_ReplUserLoged, NELOG_ReplUserLoged, 0,
+ NULL, NULL, NO_EXIT);
+ cur_rec->alerts |= USER_LOGGED_ALERT;
+ }
+ RELEASE_LOCK( ReplConfigLock );
+ return (BUGBUG);
+ }
+ }
+
+ //
+ // user is not logged on.
+ //
+
+ //
+ // is ReplConfigLogonUserName specified.
+ //
+
+ if (ReplConfigLogonUserName != NULL) {
+
+ //
+ // Try logon using ReplConfigLogonUserName and P_asswd.
+ //
+ // NERR_UnableToAddName_W error means messanger couldn't add the
+ // username which is always the case when loggong on with the
+ // computername
+ //
+
+ NetStatus = NetWkstaSetUID(NULL,
+ ReplConfigLogonUserName, P_passwd, NULL, 0);
+ if ((NetStatus != NO_ERROR) && (NetStatus != NERR_UnableToAddName_W)) {
+
+ if ((cur_rec->alerts & LOGON_FAILED_ALERT) == 0) {
+ AlertLogExit(
+ ALERT_ReplLogonFailed,
+ NELOG_ReplLogonFailed,
+ NetStatus,
+ ReplConfigLogonUserName,
+ ReplGlobalUnicodeComputerName,
+ NO_EXIT);
+ cur_rec->alerts |= LOGON_FAILED_ALERT;
+ }
+ RELEASE_LOCK( ReplConfigLock );
+ return (NetStatus);
+ } else {
+
+ //
+ // succeeded in loging on.
+ //
+
+ need_logoff = TRUE;
+ RELEASE_LOCK( ReplConfigLock );
+ return (NO_ERROR);
+ }
+ }
+ RELEASE_LOCK( ReplConfigLock );
+
+ //
+ // user is not logged on and ReplConfigLogonUserName specified.
+ //
+
+ //
+ // try logging on using the computername and a NULL password.
+ //
+
+ NetStatus = NetWkstaSetUID(
+ NULL, ReplGlobalUnicodeComputerName,
+ "", NULL, 0);
+ if ( (NetStatus != NO_ERROR) && (NetStatus != NERR_UnableToAddName_W)) {
+ // This error means messanger couldn't add the username
+ // which is always the case when logging on with the computername.
+
+ if ((cur_rec->alerts & LOGON_FAILED_ALERT) == 0) {
+ AlertLogExit(ALERT_ReplLogonFailed, NELOG_ReplLogonFailed,
+ NetStatus,
+ ReplGlobalUnicodeComputerName,
+ ReplGlobalUnicodeComputerName,
+ NO_EXIT);
+ cur_rec->alerts |= LOGON_FAILED_ALERT;
+ }
+
+ return (NetStatus);
+ } else {
+
+ //
+ // succeeded in logging on.
+ //
+
+ need_logoff = TRUE;
+ return (NO_ERROR);
+ }
+#else
+
+ return (NO_ERROR);
+
+#endif // LOGON_DEFINED
+
+ DBG_UNREFERENCED_PARAMETER(cur_rec);
+ /*NOTREACHED*/
+}
+
+
+
+DBGSTATIC VOID
+ReplLogoff(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ Logs off from the current master.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+Threads:
+
+ Only called by syncer thread.
+
+--*/
+{
+
+#ifdef LOGON_DEFINED
+
+ //
+ // Only physically logoff if it is needed.
+ //
+
+ if ( need_logoff ) {
+ NetWkstaSetUID(NULL, NULL, NULL, NULL, WKSTA_MAX_FORCE);
+ need_logoff = FALSE;
+ }
+
+ //
+ // Always indicate that we've logged off.
+ //
+
+ logged_on = FALSE;
+
+#endif // LOGON_DEFINED
+}
+
+
+
+
+
+DBGSTATIC VOID
+ReplDoUpdate(
+ IN LPTSTR dir,
+ IN PSYNCMSG master_info,
+ IN PMSG_STATUS_REC status_rec
+ )
+/*++
+
+Routine Description:
+
+ Does a normal update:
+ 1. Checks if dir is in client_list (i.e. is it a new dir) and
+ adds it if not.
+
+ 2. Checks if update is from dir's master, if not call
+ ReplMultiMaster function.
+
+ 3. Checks for change (i.e. has status vars changed), if so call
+ ReplSyncTree.
+
+Arguments:
+
+ dir - directory name.
+
+ master_info - master name, and some other parms.
+
+ status_rec - new checksum, count and timestamp.
+
+Return Value:
+
+ NONE.
+
+Threads:
+
+ Only called by syncer thread.
+
+--*/
+{
+ TCHAR master_path[MAX_PATH];
+ TCHAR client_path[MAX_PATH];
+ DWORD Master_Attributes, Client_Attributes, ApiStatus;
+
+ CHECKSUM_REC ck_rec;
+ PCLIENT_LIST_REC cur_rec;
+ BOOL ListLocked = FALSE;
+ NET_API_STATUS NetStatus = NERR_Success;
+ BOOL PermissionEnabled = FALSE;
+ BOOL SyncNeeded = FALSE;
+ TCHAR UncMaster[UNCLEN+1];
+
+
+ NetpAssert( dir != NULL );
+ NetpAssert( (*dir) != TCHAR_EOS );
+ NetpAssert( master_info != NULL );
+ NetpAssert( status_rec != NULL );
+
+ IF_DEBUG( MAJOR ) {
+ time_t Now = (time_t) NetpReplTimeNow();
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "%24s "
+ "Syncer processing msg with '" FORMAT_LPTSTR "'\n",
+ (LPSTR) ctime( &Now ),
+ dir ));
+ }
+
+ //
+ // Form master's and client's path UNC.
+ //
+
+ ACQUIRE_LOCK_SHARED( ReplConfigLock );
+
+ (void) STRCPY(master_path, SLASH_SLASH);
+ (void) STRCAT(master_path, master_info->header.sender);
+ (void) STRCAT(master_path, SLASH);
+ (void) STRCAT(master_path, REPL_SHARE);
+ (void) STRCAT(master_path, SLASH);
+ (void) STRCAT(master_path, dir);
+
+ (void) STRCPY(client_path, ReplConfigImportPath);
+ (void) STRCAT(client_path, SLASH);
+ (void) STRCAT(client_path, dir);
+
+ RELEASE_LOCK( ReplConfigLock );
+
+ //
+ // We'll also need the UNC name of the master computer itself.
+ //
+
+ if (STRLEN( master_info->header.sender ) > CNLEN) {
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplDoUpdate: sender name in message is too long.\n" ));
+ // BUGBUG: log this.
+ goto Cleanup;
+ }
+
+ (VOID) STRCPY( UncMaster, (LPTSTR) SLASH_SLASH );
+ (VOID) STRCAT( UncMaster, master_info->header.sender );
+ NetpAssert( STRLEN( UncMaster ) <= UNCLEN );
+
+ //
+ // We're going to need backup and restore permissions to do stuff,
+ // so enable them here.
+ //
+ NetStatus = ReplEnableBackupPermission( ); // Enable both.
+ if (NetStatus != NO_ERROR) {
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplDoUpdate: unexpected status " FORMAT_API_STATUS
+ " from enable permission...\n", NetStatus ));
+ AlertLogExit( ALERT_ReplSysErr,
+ NELOG_ReplSysErr,
+ NetStatus,
+ NULL,
+ NULL,
+ NO_EXIT);
+ // Continue... Maybe we'll get lucky and still be able to do work.
+ } else {
+ PermissionEnabled = TRUE;
+ }
+
+ //
+ // Find or create client record first.
+ //
+
+ ACQUIRE_LOCK( RCGlobalClientListLock ); // Need excl in case we add below.
+ ListLocked = TRUE;
+
+ cur_rec = ReplGetClientRec(dir, NULL );
+
+ if (cur_rec == NULL) {
+
+ //
+ // add to client list.
+ //
+
+ if ((cur_rec = ReplAddClientRec(dir, master_info, status_rec)) == NULL) {
+
+ //
+ // might fail due to memory problem.
+ //
+
+ goto Cleanup; // Don't forget to unlock stuff.
+ } else {
+ // Add equivalent info to registry, so ReplCreateReplLock doesn't
+ // get confused.
+ ApiStatus = ImportDirWriteConfigData(
+ NULL, // no server name (local).
+ dir, // dir name
+ REPL_STATE_NEVER_REPLICATED,
+ UncMaster, // master computer name
+ 0, // no last update time
+ 0, // no locks
+ 0 ); // no lock time
+ if (ApiStatus != NO_ERROR) {
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplDoUpdate: unexpected ret code from "
+ "ImportDirWriteConfigData: " FORMAT_API_STATUS ".\n",
+ ApiStatus ));
+ ReplErrorLog(
+ NULL, // local (no server name)
+ NELOG_ReplSysErr, // log code
+ ApiStatus, // the unexpected error code
+ NULL, // no optional str1
+ NULL ); // no optional str2
+ goto Cleanup; // Don't forget to unlock stuff.
+ }
+ SyncNeeded = TRUE; // don't even try ChecksumEqual below.
+ }
+ } else {
+
+ //
+ // When the current record is created before a message is sent
+ // from the exporter, the master name is set to null. This causes
+ // ReplNetNameCompare to fail and forces a Duplicate master situation
+ // To prevent this, fill in the master name at this time since we
+ // know it.
+ //
+
+ if ( *(cur_rec->master) == '\0' ) {
+ (VOID) STRCPY( cur_rec->master, master_info->header.sender );
+ }
+ }
+
+ //
+ // At this point we know we've got a record in the client list.
+ // Let's change our exclusive lock to a shared one, as we may be in here
+ // for quite a long time, maybe even hours.
+ //
+ NetpAssert( cur_rec != NULL );
+ NetpAssert( ListLocked );
+ CONVERT_EXCLUSIVE_LOCK_TO_SHARED( RCGlobalClientListLock );
+
+ //
+ // Check if dir exists on client.
+ //
+ //
+ // The next chunk of code queries the client's first level directory.
+ // If one exists its attributes are compared against the masters. If
+ // one does not exist a directory is created with matching atributes.
+ // If both directories exist but the attributes do not match, the client's
+ // attributes are set to the master's.
+ //
+ // BUGBUG: What is not compared and updated is the security attributes
+ // of the client directory. Could the master directory contain acls that
+ // would be bad for the client to have?
+ //
+
+
+ Client_Attributes = GetFileAttributes( client_path );
+ IF_DEBUG( SYNC ) {
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplDoUpdate: attr for '" FORMAT_LPTSTR "' is "
+ FORMAT_HEX_DWORD ".\n", client_path, Client_Attributes ));
+ }
+
+ if ( Client_Attributes == (DWORD) -1 ) {
+
+ NetStatus = (NET_API_STATUS) GetLastError();
+ NetpAssert( NetStatus != NO_ERROR );
+
+ //
+ // If the import path cannot be found,
+ // alert someone.
+ //
+
+ if (NetStatus == ERROR_PATH_NOT_FOUND) {
+
+ AlertLogExit( ALERT_ReplBadImport,
+ NELOG_ReplBadImport,
+ 0,
+ client_path,
+ NULL,
+ NO_EXIT);
+ goto Cleanup; // Don't forget to unlock stuff.
+
+ //
+ // If access denied, don't exit; try next dir.
+ //
+
+ } else if ((NetStatus == ERROR_ACCESS_DENIED)
+ || (NetStatus == ERROR_NETWORK_ACCESS_DENIED)) {
+
+ if ((cur_rec->alerts & ACCESS_DENIED_ALERT) == 0) {
+ AlertLogExit( ALERT_ReplAccessDenied,
+ NELOG_ReplAccessDenied,
+ NetStatus,
+ client_path,
+ UncMaster, // need my own server name here.
+ NO_EXIT);
+ cur_rec->alerts |= ACCESS_DENIED_ALERT;
+ }
+ goto Cleanup; // Don't forget to unlock stuff.
+
+ //
+ // If this is any error other than the directory not existing,
+ // alert someone and continue.
+ //
+
+ } else if (NetStatus != ERROR_FILE_NOT_FOUND) {
+
+ AlertLogExit( ALERT_ReplSysErr,
+ NELOG_ReplSysErr,
+ NetStatus,
+ NULL,
+ NULL,
+ NO_EXIT);
+ goto Cleanup; // Don't forget to unlock stuff.
+ }
+
+ //
+ // If the dir does not exist at client, create it now.
+ // Make sure it has the right ACL, attributes, etc.
+ //
+
+ NetStatus = ReplCopyDirectoryItself(
+ master_path, // src
+ client_path, // dest
+ FALSE ); // don't fail if already exists.
+ if (NetStatus != NO_ERROR) {
+
+ AlertLogExit( ALERT_ReplSysErr,
+ NELOG_ReplSysErr,
+ NetStatus,
+ NULL,
+ NULL,
+ NO_EXIT);
+ goto Cleanup; // Don't forget to unlock stuff.
+ }
+
+ Client_Attributes = FILE_ATTRIBUTE_DIRECTORY;
+ }
+
+ //
+ // If the name exists at client as a file - no replication takes place.
+ //
+
+ if ( (Client_Attributes & FILE_ATTRIBUTE_DIRECTORY) == 0 ) {
+ goto Cleanup; // Don't forget to unlock stuff.
+ }
+
+ //
+ // Get Attributes at master
+ //
+
+ Master_Attributes = GetFileAttributes( master_path );
+ IF_DEBUG( SYNC ) {
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplDoUpdate: attr for '" FORMAT_LPTSTR "' is "
+ FORMAT_HEX_DWORD ".\n", master_path, Master_Attributes ));
+ }
+
+ if ( Master_Attributes == (DWORD) -1 ) {
+
+ NetStatus = (NET_API_STATUS) GetLastError();
+ NetpAssert( NetStatus != NO_ERROR );
+ NetpAssert( NetStatus != ERROR_INVALID_FUNCTION );
+
+ //
+ // If the export path cannot be found,
+ // alert someone.
+ //
+
+ if (NetStatus == ERROR_PATH_NOT_FOUND) {
+
+ AlertLogExit( ALERT_ReplBadExport,
+ NELOG_ReplBadExport,
+ 0,
+ master_path,
+ NULL,
+ NO_EXIT);
+ goto Cleanup; // Don't forget to unlock stuff.
+
+ } else if ((NetStatus == ERROR_ACCESS_DENIED)
+ || (NetStatus == ERROR_NETWORK_ACCESS_DENIED)) {
+
+ if ((cur_rec->alerts & ACCESS_DENIED_ALERT) == 0) {
+ AlertLogExit( ALERT_ReplAccessDenied,
+ NELOG_ReplAccessDenied,
+ NetStatus,
+ master_path,
+ cur_rec->master,
+ NO_EXIT);
+ cur_rec->alerts |= ACCESS_DENIED_ALERT;
+ }
+ goto Cleanup; // Don't forget to unlock stuff.
+
+ } else {
+
+ AlertLogExit( ALERT_ReplSysErr,
+ NELOG_ReplSysErr,
+ NetStatus,
+ NULL,
+ NULL,
+ NO_EXIT);
+ goto Cleanup;
+ }
+ }
+
+ //
+ // If the user has locked us from replication,
+ // don't sync now.
+ //
+
+ if (cur_rec->lockcount > 0) {
+ IF_DEBUG( SYNC ) {
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplDoUpdate: Tree Integrity setting no sync"
+ " (locked)\n" ));
+ }
+
+ // Note: ReplSetSignalFile needs lock (any kind) on
+ // RCGlobalClientListLock.
+ ReplSetSignalFile(cur_rec, REPL_STATE_NO_SYNC);
+ goto Cleanup;
+ }
+
+ //
+ // Is it our true master ?
+ //
+
+ if (ReplNetNameCompare( NULL,
+ cur_rec->master,
+ master_info->header.sender,
+ NAMETYPE_COMPUTER,
+ 0L ) == 0) {
+
+ //
+ // Is there a more recent message for this directory?
+ //
+
+ if ( ReplScanQueuesForMoreRecentMsg( dir ) ) {
+ IF_DEBUG( MAJOR ) {
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplDoUpdate: skipping message for '" FORMAT_LPTSTR
+ "' in favor of more recent message.\n", dir ));
+ }
+ goto Cleanup; // don't forget to unlock...
+ }
+
+ //
+ // Always update integrity and extent - in case
+ // changed at master. Same goes for the timing variables.
+ //
+
+ cur_rec->integrity = status_rec->integrity;
+ cur_rec->extent = status_rec->extent;
+ cur_rec->pulse_time = master_info->info.pulse_rate * 60;
+ cur_rec->guard_time = master_info->info.guard_time * 60;
+ cur_rec->rand_time = master_info->info.random;
+
+ //
+ // If client dir attributes are not the same as the master's
+ // assign the master attributes to the client
+ //
+
+ if ( Client_Attributes != Master_Attributes ) {
+
+ IF_DEBUG( SYNC ) {
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "Setting client's attributes (first level) to "
+ FORMAT_HEX_DWORD " for '" FORMAT_LPTSTR "'.\n",
+ Master_Attributes, client_path ));
+ }
+
+ if ( !SetFileAttributes(
+ (LPTSTR) client_path,
+ Master_Attributes ) ) {
+
+ ApiStatus = (NET_API_STATUS) GetLastError();
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplDoUpdate: unexpected ret code from "
+ "SetFileAttributes(" FORMAT_LPTSTR
+ ", " FORMAT_HEX_DWORD
+ "): " FORMAT_API_STATUS ".\n",
+ client_path, Master_Attributes, ApiStatus ));
+ NetpAssert( ApiStatus != NO_ERROR );
+ goto Cleanup; // Don't forget to unlock stuff.
+ }
+ }
+
+
+ if ( !SyncNeeded ) { // not first time we've seen this dir...
+
+ if ((cur_rec->checksum != status_rec->checksum)
+ || (((DWORD)cur_rec->count) != status_rec->count)) {
+
+ //
+ // Walk import tree and compute. Could be first time, in
+ // which case we just have old checksum.
+ //
+ if ( !ChecksumEqual(
+ UncMaster, client_path, status_rec, &ck_rec) ) {
+
+ //
+ // Yes, the checksum changed on master, so sync it.
+ //
+ SyncNeeded = TRUE; // must do sync and checksum again.
+
+ } else {
+
+ //
+ // Was probably just first time. Update checksum in
+ // client rec. Also update timestamp, so we don't keep
+ // recomputing this checksum.
+ //
+ cur_rec->checksum = ck_rec.checksum;
+ cur_rec->count = ck_rec.count;
+ cur_rec->timestamp = NetpReplTimeNow();
+
+ }
+ }
+
+ }
+
+ if ( !SyncNeeded ) {
+
+ //
+ // Have we gotten a change notify, which might mean we just need to
+ // do another checksum?
+ //
+ if (RCGlobalTimeOfLastChangeNotify >= (cur_rec->timestamp)) {
+
+ //
+ // Got change notify since last checksum.
+ //
+ if ( !ChecksumEqual(
+ UncMaster, client_path, status_rec, &ck_rec) ) {
+
+ SyncNeeded = TRUE; // must do sync and checksum again.
+
+ } else {
+
+ //
+ // There was a change notify but the checksum still
+ // matches. Must have been another tree that changed.
+ // Update timestamp in client rec, so we don't keep
+ // recomputing this checksum.
+ //
+ cur_rec->timestamp = NetpReplTimeNow();
+
+ }
+ } else if ( NetpReplTimeNow()
+ < ((cur_rec->timestamp) - BACKWARDS_TIME_CHNG_ALLOWED_SECS)
+ ) {
+
+ //
+ // Time went backwards, perhaps a month. Can't trust time of
+ // last change notify or timestamp in record.
+ //
+
+ if ( !ChecksumEqual(
+ UncMaster, client_path, status_rec, &ck_rec) ) {
+
+ SyncNeeded = TRUE;
+ } else {
+ cur_rec->timestamp = NetpReplTimeNow();
+
+ }
+ }
+ }
+
+ //
+ // if out of Sync ?
+ //
+
+ if (SyncNeeded) {
+
+ //
+ // This sync may take a while, and somebody may look at status
+ // during the sync, so set status to no sync in the meantime.
+ //
+
+ IF_DEBUG( SYNC ) {
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplDoUpdate: setting no sync"
+ " (about to begin update)\n" ));
+ }
+
+ // Note: ReplSetSignalFile needs lock (any kind) on
+ // RCGlobalClientListLock.
+ ReplSetSignalFile(cur_rec, REPL_STATE_NO_SYNC);
+
+
+ //
+ // If we can logon, sync the tree.
+ //
+
+ // Note: ReplLogon needs lock (any kind) on RCGlobalClientListLock.
+ if ((ReplLogon(cur_rec)) == NO_ERROR) {
+
+ //
+ // S Y N C T H E T R E E (at last!)
+ //
+ // This will sync the specified directory, do another checksum,
+ // and update state and checksum in client record.
+ //
+ // Note: ReplSyncTree needs lock (any kind) on
+ // RCGlobalClientListLock.
+ ReplSyncTree(status_rec, cur_rec);
+ }
+
+ } else {
+
+ BOOL ExportLocked;
+
+ //
+ // See if exporter is locked; set no sync if it is.
+ //
+ ApiStatus = ReplCheckExportLocks(
+ UncMaster,
+ (LPCTSTR) dir,
+ &ExportLocked );
+
+ if ( ( !ExportLocked ) && (ApiStatus == NO_ERROR) ) {
+ //
+ // tree in sync so update signal file.
+ //
+
+ IF_DEBUG( SYNC ) {
+ if (SyncNeeded) {
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplDoUpdate: setting in sync (sync anyway)\n" ));
+ } else {
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplDoUpdate: setting in sync (not sync anyway)\n"
+ ));
+ }
+ }
+ // Note: ReplSetSignalFile needs lock (any kind) on
+ // RCGlobalClientListLock.
+ ReplSetSignalFile(cur_rec, REPL_STATE_OK);
+ } else {
+
+ IF_DEBUG( SYNC ) {
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplDoUpdate: setting NO sync "
+ "(exporter locked or down)\n" ));
+ }
+ // Note: ReplSetSignalFile needs lock (any kind) on
+ // RCGlobalClientListLock.
+ ReplSetSignalFile(cur_rec, REPL_STATE_NO_SYNC);
+ }
+ }
+
+ // Note: ReplSetTimeOut needs shared RCGlobalClientListLock.
+ ReplSetTimeOut(PULSE_1_TIMEOUT, cur_rec); // reset timeout anyway.
+
+ //
+ // If this pulser is not our current master,
+ // handle it.
+ //
+
+ } else {
+
+ // Note: ReplMultiMaster needs lock (any kind) on
+ // RCGlobalClientListLock.
+ ReplMultiMaster( DUPL_MASTER_UPDATE,
+ cur_rec,
+ master_info->header.sender);
+ }
+
+Cleanup:
+
+ if (ListLocked) {
+ RELEASE_LOCK( RCGlobalClientListLock );
+ }
+ if (PermissionEnabled) {
+ (VOID) ReplDisableBackupPermission();
+ }
+}
+
+
+
+
+DBGSTATIC VOID
+ReplGuardCheck(
+ IN PCLIENT_LIST_REC tree_rec
+ )
+/*++
+
+Routine Description:
+
+ Called after GUARD_TIMEOUT - tries to sync the tree again. Uses
+ ReplSyncTree.
+
+ Fakes a MSG_STATUS_REC record, so ReplSyncTree
+ can't tell the difference. The Guard checksum and count appear as the
+ status_rec, i.e. as though it is part of an update message.
+
+ Assumes that caller has lock (any kind) on RCGlobalClientListLock.
+
+Arguments:
+
+ tree_rec - The client record for the directory to check.
+
+Return Value:
+
+ None.
+
+Threads:
+
+ Only called by syncer thread.
+
+--*/
+{
+ MSG_STATUS_REC status_rec;
+
+
+ //
+ // Setup the Faked status_rec.
+ //
+
+ status_rec.opcode = GUARD;
+ status_rec.checksum = tree_rec->timer.grd_checksum;
+ status_rec.count = tree_rec->timer.grd_count;
+ status_rec.integrity = tree_rec->integrity;
+ status_rec.extent = tree_rec->extent;
+
+ //
+ // Leave guard status vars on (i.e grd_count != -1 so ReplSyncTree knows
+ // this is a guard check, and does not setup another guard timeout
+ //
+
+ //
+ // This sync may take a while, and somebody may look at status
+ // during the sync, so set status to no sync in the meantime.
+ //
+
+ IF_DEBUG( SYNC ) {
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplGuardCheck: setting no sync"
+ " (about to begin update)\n" ));
+ }
+
+ // Note: ReplSetSignalFile needs lock (any kind) on
+ // RCGlobalClientListLock.
+ ReplSetSignalFile(tree_rec, REPL_STATE_NO_SYNC);
+
+
+ //
+ // If we can logon, sync the tree.
+ //
+
+ // Note: ReplLogon needs lock (any kind) on RCGlobalClientListLock.
+ if ((ReplLogon(tree_rec)) == NO_ERROR) {
+
+ // Note: ReplSyncTree needs lock (any kind) on RCGlobalClientListLock.
+ ReplSyncTree((PMSG_STATUS_REC ) & status_rec, tree_rec);
+ }
+
+}
+
+
+
+
+DBGSTATIC DWORD
+ReplTimeSince(
+ DWORD check_time
+ )
+/*++
+
+Routine Description:
+
+ Returns the time difference between check_time and current time, in seconds.
+
+Arguments:
+
+ check_time - The time to compare against (in seconds since Jan 1, 1970).
+
+Return Value:
+
+ Returns the number of seconds that have elapsed since check_time.
+
+--*/
+{
+ DWORD tmp;
+
+ tmp = NetpReplTimeNow();
+ if (tmp > check_time) {
+ return (tmp - check_time);
+ } else {
+ return 0;
+ }
+}
+
+
+
+DWORD
+ReplSyncerThread(
+ LPVOID Parameter
+ )
+/*++
+
+Routine Description:
+
+ Spawned by Client process, takes care of all replication client's timing.
+
+ BUGBUG Comment is bad.
+
+Arguments:
+
+ Parameter - Not used.
+
+Return Value:
+
+ NONE.
+
+Threads:
+
+ Only called by syncer thread.
+
+--*/
+{
+ NET_API_STATUS ApiStatus;
+ DWORD MessageType;
+ PSYNCMSG sync_p;
+ PQUERY_MSG query_p;
+ PMSG_STATUS_REC status_p;
+ PCLIENT_LIST_REC cur_rec;
+ PBIGBUF_REC que_buf;
+
+ UNREFERENCED_PARAMETER( Parameter );
+
+ IF_DEBUG( REPL ) {
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplSyncerThread: thread ID is "
+ FORMAT_NET_THREAD_ID ".\n", NetpCurrentThread() ));
+ }
+
+ //
+ // Init backup and restore privs so we can copy ACLs.
+ // Don't enable them yet, though.
+ //
+ ApiStatus = ReplInitBackupPermission();
+ if (ApiStatus != NO_ERROR) {
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplSyncerThread: FAILED init backup permission, status "
+ FORMAT_API_STATUS ".\n", ApiStatus ));
+
+ ReplErrorLog(
+ NULL, // no server name (local)
+ NELOG_ReplSysErr, // error log code
+ ApiStatus, // status to log
+ NULL, // no optional str1
+ NULL ); // no optional str2
+
+ // Continue, maybe we can still get some work done.
+ }
+
+ //
+ // Loop forever taking entries off the Work Queue and doing them.
+ //
+
+ for (;;) {
+ BOOL ClientTerminating;
+
+ //
+ // Get the next message from the queue.
+ //
+ // Also, awaken if the repl service is to be terminated.
+ //
+
+ que_buf = ReplGetWorkQueue( &ClientTerminating );
+
+ if ( ClientTerminating ) {
+ IF_DEBUG(SYNC) {
+
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplSyncerThread: TERMINATING...\n" ));
+ }
+ break; // exit.
+ }
+
+ if ( que_buf == NULL ) {
+ continue;
+ }
+
+ query_p = (PQUERY_MSG ) (que_buf->data);
+ MessageType = query_p->header.msg_type;
+
+#if DBG
+ {
+ if (MessageType < NEXT_AVAILABLE_MESSAGE_NUMBER) {
+ RCGlobalSyncReceived[ MessageType ] ++;
+ }
+ }
+#endif
+
+
+ IF_DEBUG(SYNC) {
+
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "worker queue received a message "
+ "(type = " FORMAT_DWORD ").\n", MessageType ));
+
+ }
+
+ switch (MessageType) {
+
+ case DIR_SUPPORTED: /*FALLTHROUGH*/
+ case DIR_NOT_SUPPORTED: /*FALLTHROUGH*/
+ case MASTER_DIR: /*FALLTHROUGH*/
+ case NOT_MASTER_DIR:
+
+ //
+ // Handshake messages are now handled in client thread!
+ //
+
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplSyncerThread got handshake message in queue!\n" ));
+ NetpAssert( FALSE );
+
+ ReplClientFreePoolEntry( QUEBIG_POOL, que_buf);
+
+ break;
+
+
+ //
+ // Handle a PULSE_MSG from the master - check if we have all synced.
+ //
+
+ case PULSE_MSG:
+
+ sync_p = (PSYNCMSG) (LPVOID) query_p;
+ status_p = (PMSG_STATUS_REC) (LPVOID) (sync_p + 1);
+
+ //
+ // Loop for each directory in this pulse message.
+ //
+
+ while ((sync_p->update_count)--) {
+ LPTSTR dir_name;
+
+ dir_name = (LPTSTR)
+ (((PCHAR)sync_p) + status_p->dir_name_offset);
+
+ ReplDoUpdate(dir_name, sync_p, status_p);
+
+ ++status_p; // increment status pointer.
+ }
+
+
+ //
+ // Log off from this master.
+ //
+
+ ReplLogoff();
+
+
+ ReplClientFreePoolEntry( QUEBIG_POOL, que_buf);
+ break;
+
+
+ //
+ // Handle a SYNC_MSG or GUARD_MSG from the master.
+ //
+
+ case SYNC_MSG:
+ case GUARD_MSG:
+
+ sync_p = (PSYNCMSG) (LPVOID) query_p;
+ status_p = (PMSG_STATUS_REC) (LPVOID) (sync_p + 1);
+
+ //
+ // Loop for each directory in the message.
+ //
+
+ while ((sync_p->update_count)--) {
+ LPTSTR dir_name;
+ dir_name = (LPTSTR)
+ (((PCHAR)sync_p) + status_p->dir_name_offset);
+
+ switch (status_p->opcode) {
+
+ case START:
+ case UPDATE:
+
+ ReplDoUpdate(dir_name, sync_p, status_p);
+ break;
+
+ //
+ // If the master is asking us to stop syncing this directory,
+ // do so.
+ //
+
+ case END:
+
+ //
+ // If we still support this directory from this master,
+ // set signal to NO_MASTER and remove dir from client_list.
+ //
+
+ ACQUIRE_LOCK_SHARED( RCGlobalClientListLock );
+ if ((cur_rec = ReplGetClientRec(
+ dir_name,
+ sync_p->header.sender )) != NULL) {
+
+ // Note: ReplMasterDead needs lock (any kind) on
+ // RCGlobalClientListLock.
+ ReplMasterDead(cur_rec);
+ }
+ RELEASE_LOCK( RCGlobalClientListLock );
+ break;
+
+ default:
+ break;
+
+ }
+
+
+ ++status_p; // increment status pointer.
+
+ }
+
+
+ //
+ // Log off from this master.
+ //
+
+ ReplLogoff();
+
+ ReplClientFreePoolEntry( QUEBIG_POOL, que_buf);
+ break;
+
+
+
+ //
+ // Handle GUARD_TIMEOUT:
+ //
+ // A directory changed while we were syncing. Since then, the
+ // guard time on the directory has elapsed, so we will try again.
+ //
+
+ case GUARD_TIMEOUT:
+
+ //
+ // make sure it wasn't deleted while guarding.
+ //
+
+
+ //
+ // If we still support this directory from this master,
+ // try to sync again.
+ //
+
+ ACQUIRE_LOCK_SHARED( RCGlobalClientListLock );
+ if ((cur_rec = ReplGetClientRec(query_p->dir_name,
+ query_p->header.sender )) != NULL) {
+
+ //
+ // make sure no update was received while this timeout was
+ // on it's way
+ //
+
+ if (ReplTimeSince(cur_rec->timestamp) > cur_rec->guard_time) {
+
+ // Note: ReplGuardCheck needs any RCGlobalClientListLock.
+ ReplGuardCheck(cur_rec);
+
+ }
+ }
+ RELEASE_LOCK( RCGlobalClientListLock );
+
+ ReplClientFreePoolEntry( QUESML_POOL, que_buf);
+ break;
+
+
+ //
+ // We asked our master to send us a MASTER_DIR or a NOT_MASTER_DIR.
+ // Our master did not respond.
+ //
+
+ case DUPL_TIMEOUT:
+
+ ACQUIRE_LOCK_SHARED( RCGlobalClientListLock );
+ if ((cur_rec = ReplGetClientRec(query_p->dir_name, NULL )) != NULL) {
+
+ //
+ // the timeout sender field is used to store the dupl_master.
+ //
+
+ // Note: ReplMultiMaster needs lock (any kind) on
+ // RCGlobalClientListLock.
+ ReplMultiMaster( DUPL_MASTER_TIMEOUT,
+ cur_rec,
+ query_p->header.sender);
+ }
+ RELEASE_LOCK( RCGlobalClientListLock );
+
+ ReplClientFreePoolEntry( QUESML_POOL, que_buf);
+ break;
+
+
+
+ //
+ // Handle when our master has not sent us a pulse in
+ // pulse time + 1 minute.
+ //
+
+ case PULSE_1_TIMEOUT:
+
+
+ //
+ // If we still support this directory from this master,
+ // indicate that we have no sync signal from the master.
+ // prepare to timeout again.
+ //
+
+ ACQUIRE_LOCK_SHARED( RCGlobalClientListLock );
+ if ((cur_rec = ReplGetClientRec(query_p->dir_name,
+ query_p->header.sender )) != NULL) {
+
+ //
+ // make sure no update was received while this timeout was
+ // on it's way
+ //
+
+ if (ReplTimeSince(cur_rec->timestamp) >=
+ (cur_rec->pulse_time + ONE_MINUTE)) {
+
+ IF_DEBUG( SYNC ) {
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplSyncerThread: setting no sync"
+ " (pulse 1 timeout)\n" ));
+ }
+
+ // Note: ReplSetSignalFile needs lock (any kind) on
+ // RCGlobalClientListLock.
+ ReplSetSignalFile(cur_rec, REPL_STATE_NO_SYNC);
+
+ // Note: ReplSetTimeOut needs shared RCGlobalClientListLock.
+ ReplSetTimeOut(PULSE_2_TIMEOUT, cur_rec);
+ }
+ }
+ RELEASE_LOCK( RCGlobalClientListLock );
+
+ ReplClientFreePoolEntry( QUESML_POOL, que_buf);
+ break;
+
+
+ //
+ // Handle when our master has not sent us a pulse in
+ // two times the pulse time + one minute.
+ //
+
+ case PULSE_2_TIMEOUT:
+
+ //
+ // If we still support this directory from this master,
+ // ask the master if it still supports this directory.
+ //
+
+ ACQUIRE_LOCK_SHARED( RCGlobalClientListLock );
+ if ((cur_rec = ReplGetClientRec(query_p->dir_name,
+ query_p->header.sender )) != NULL) {
+
+ //
+ // make sure tree is in right state.
+ //
+
+ if (cur_rec->state == REPL_STATE_NO_SYNC) {
+
+ ReplClientSendMessage( IS_DIR_SUPPORTED,
+ cur_rec->master,
+ cur_rec->dir_name);
+
+ // Note: ReplSetTimeOut needs shared RCGlobalClientListLock.
+ ReplSetTimeOut(PULSE_3_TIMEOUT, cur_rec);
+ }
+ }
+ RELEASE_LOCK( RCGlobalClientListLock );
+
+ ReplClientFreePoolEntry( QUESML_POOL, que_buf);
+ break;
+
+
+ //
+ // Handle when our master has not sent us a pulse in
+ // three times the pulse time + one minute.
+ //
+ // The master should have at least replied to our IS_DIRSUPPORTED query.
+ //
+
+ case PULSE_3_TIMEOUT:
+
+ //
+ // If we still support this directory from this master,
+ // alert that the master is really dead.
+ //
+
+ ACQUIRE_LOCK_SHARED( RCGlobalClientListLock );
+ if ((cur_rec = ReplGetClientRec(query_p->dir_name,
+ query_p->header.sender )) != NULL) {
+
+ //
+ // make sure tree is in right state.
+ //
+
+ if (cur_rec->state == REPL_STATE_NO_SYNC) {
+
+ AlertLogExit(ALERT_ReplLostMaster,
+ NELOG_ReplLostMaster,
+ 0,
+ cur_rec->master,
+ cur_rec->dir_name,
+ NO_EXIT);
+
+
+ //
+ // Set signal to NO_MASTER and remove dir from client_list.
+ //
+
+ // Note: ReplMasterDead needs lock (any kind) on
+ // RCGlobalClientListLock.
+ ReplMasterDead(cur_rec);
+ }
+ }
+ RELEASE_LOCK( RCGlobalClientListLock );
+
+ ReplClientFreePoolEntry( QUESML_POOL, que_buf);
+ break;
+
+ default:
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplSyncerThread: UNEXPECTED MESSAGE TYPE IN QUEUE\n" ));
+
+ NetpAssert( FALSE );
+
+ ReplClientFreePoolEntry( QUEBIG_POOL, que_buf);
+ break;
+
+ }
+
+
+ }
+
+ //
+ // Exit the syncer thread.
+ //
+
+ IF_DEBUG( REPL ) {
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplSyncerThread: exiting thread "
+ FORMAT_NET_THREAD_ID ".\n", NetpCurrentThread() ));
+ }
+
+ return 0;
+
+}
+
+
+
+
+
+VOID
+ReplSetTimeOut(
+ IN DWORD timeout_type,
+ IN PCLIENT_LIST_REC tree_rec
+ )
+/*++
+
+Routine Description:
+
+ Set up a timeout for this particular directory.
+
+ Note: all delays are in units of 10 seconds, rounded up.
+
+ Assumes that caller has shared lock on RCGlobalClientListLock.
+
+Arguments:
+
+ timeout_type - type of timeout.
+
+ tree_rec - The client tree record for this directory.
+
+Return Value:
+
+ NONE.
+
+Threads:
+
+ Called by client and syncer threads.
+
+--*/
+{
+
+ switch (timeout_type) {
+ case PULSE_1_TIMEOUT:
+
+ tree_rec->timer.timeout = (tree_rec->pulse_time + ONE_MINUTE) / 10 + 1;
+ tree_rec->timer.type = PULSE_1_TIMEOUT;
+ break;
+
+ case PULSE_2_TIMEOUT:
+
+ tree_rec->timer.timeout = tree_rec->pulse_time / 10 + 1;
+ tree_rec->timer.type = PULSE_2_TIMEOUT;
+ break;
+
+ case PULSE_3_TIMEOUT:
+
+ tree_rec->timer.timeout = tree_rec->pulse_time / 10 + 1;
+ tree_rec->timer.type = PULSE_3_TIMEOUT;
+ break;
+
+ case GUARD_TIMEOUT:
+
+ tree_rec->timer.grd_timeout = tree_rec->guard_time / 10 + 1;
+ break;
+ default:
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplSetTimeOut: got INVALID timeout type!\n" ));
+ }
+
+}
diff --git a/private/net/svcdlls/repl/server/syncmisc.c b/private/net/svcdlls/repl/server/syncmisc.c
new file mode 100644
index 000000000..f43310505
--- /dev/null
+++ b/private/net/svcdlls/repl/server/syncmisc.c
@@ -0,0 +1,1214 @@
+/*++
+
+Copyright (c) 1987-1993 Microsoft Corporation
+
+Module Name:
+
+ SyncMisc.c
+
+Abstract:
+
+ Contains miscellaneous sync functions.
+
+Author:
+
+ Ported from Lan Man 2.1
+
+Environment:
+
+ User mode only.
+ Contains NT-specific code.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+ 09-May-1989 (yuv)
+ Initial Coding.
+ 11-Oct-1991 (cliffv)
+ Ported to NT. Converted to NT style.
+ 13-Dec-1991 JohnRo
+ Avoid nonstandard dollar sign in C source code.
+ 16-Jan-1992 JohnRo
+ Avoid using private logon functions.
+ Changed file name from repl.h to replgbl.h to avoid MIDL conflict.
+ Changed to use NetLock.h (allow shared locks, for one thing).
+ Use REPL_STATE_ equates for client_list_rec.state values.
+ Changed _RP equates to be LPTSTR instead of LPWSTR type.
+ 27-Jan-1992 JohnRo
+ Use config data instead of state files.
+ P_ globals are now called ReplGlobal variables in replgbl.h.
+ Changed to use LPTSTR etc.
+ 09-Feb-1992 JohnRo
+ Set up to dynamically change role.
+ Use FORMAT equates.
+ 25-Mar-1992 JohnRo
+ Avoid obsolete state values.
+ Added/corrected some lock handling.
+ Fixed bug in ReplSetSignalFile.
+ Added more debug output.
+ 25-Mar-1992 JohnRo
+ Win32 CopyFile() API doesn't copy directories, dammit!
+ Warn about files being deleted.
+ 26-Mar-1992 JohnRo
+ Fixed bug handling empty directory in ReplTreeDelete().
+ Added more debug output and assertion checking.
+ 11-Aug-1992 JohnRo
+ RAID 3288: repl svc should preserve ACLs on copy.
+ Avoid compiler warnings.
+ Use PREFIX_ equates.
+ 17-Aug-1992 JohnRo
+ RAID 3607: REPLLOCK.RP$ is being created during tree copy.
+ 27-Oct-1992 jimkel
+ Changed the error message to read correctly. when file not copyied.
+ 11-Nov-1992 JohnRo
+ Fix remote repl admin.
+ Added some debug checks.
+ 15-Jan-1993 JohnRo
+ RAID 7717: Repl assert if not logged on correctly. (Also do event
+ logging for real.)
+ Made some changes suggested by PC-LINT 5.0
+ Added some IN and OUT keywords.
+ 11-Mar-1993 JohnRo
+ RAID 14144: avoid very long hourglass in repl UI.
+ Make sure handle is closed in ReplTreeDelete().
+ 26-Mar-1993 JohnRo
+ RAID 4267: Replicator has problems when work queue gets large.
+ 13-Apr-1993 JohnRo
+ RAID 3107: locking directory over the net gives network path not found.
+ Lots of debug changes.
+ 19-Apr-1993 JohnRo
+ RAID 829: replication giving system error 38 (ERROR_HANDLE_EOF).
+ 26-Apr-1993 JohnRo
+ RAID 7313: repl needs change permission to work on NTFS,
+ or we need to delete files differently.
+
+--*/
+
+#include <windows.h> // IN, DWORD, etc.
+#include <lmcons.h>
+
+#include <alertmsg.h> // ALERT_* defines
+#include <config.h> // NetpOpenConfigData(), LPNET_CONFIG_HANDLE, etc.
+#include <confname.h> // SECT_ and REPL_KEYWORD_ equates.
+#include <dirname.h> // ReplIsDirNameValid().
+#include <impdir.h> // ImportDirWriteConfigData().
+#include <lmapibuf.h> // NetApiBufferFree()
+#include <lmerrlog.h> // NELOG_* defines
+#include <lmrepl.h> // REPL_STATE_ equates.
+#include <lmshare.h> // NetFileEnum
+#include <names.h> // NetpIsUncComputerNameValid(), etc.
+#include <netdebug.h> // DBGSTATIC, NetpDbgDisplayReplState(), etc.
+#include <netlib.h> // NetpMemoryAllocate
+#include <prefix.h> // PREFIX_ equates.
+#include <tstr.h> // TCHAR_EOS, etc.
+#include <winerror.h> // NO_ERROR and ERROR_ equates.
+
+//
+// Local include files
+//
+#include <repldefs.h> // IF_DEBUG(), etc.
+#include <replgbl.h> // ReplGlobal variables.
+#include <client.h>
+
+
+
+VOID
+ReplSetSignalFile(
+ IN OUT PCLIENT_LIST_REC tree_rec,
+ IN DWORD signal
+ )
+/*++
+
+Routine Description:
+
+ This routine used to write a signal file in a directory.
+ Nowadays, it really writes state in the registry.
+
+ Doesn't do anything if requested signal is same as current.
+
+ Assumes that caller has an lock (of any kind) on RCGlobalClientListLock.
+
+Arguments:
+
+ tree_rec - Pointer to the directory's client list record.
+
+ signal - Indicates which file to create.
+
+Return Value:
+
+ None.
+
+Threads:
+
+ Called by client and syncer threads.
+
+--*/
+{
+ NetpAssert( tree_rec != NULL );
+ NetpAssert( ReplIsStateValid( signal ) );
+ if (tree_rec->master[0] != TCHAR_EOS) {
+ NetpAssert( NetpIsComputerNameValid( tree_rec->master ) );
+ }
+
+ //
+ // If the state requested is same as the existing one then just return.
+ //
+
+ IF_DEBUG( MAJOR ) {
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "Setting new state for " FORMAT_LPTSTR ":\n",
+ tree_rec->dir_name ));
+ NetpDbgDisplayReplState( signal );
+ }
+
+ if ((tree_rec->state != signal) || (signal == REPL_STATE_OK)) {
+ NET_API_STATUS ApiStatus;
+ TCHAR UncMasterBuffer[UNCLEN+1];
+ LPTSTR UncMaster;
+
+ NetpAssert( ReplIsDirNameValid( tree_rec->dir_name ) );
+
+ //
+ // Build a UNC master name if necessary.
+ //
+ if (tree_rec->master[0] != TCHAR_EOS) {
+ (void) STRCPY( UncMasterBuffer, SLASH_SLASH );
+ (void) STRCAT( UncMasterBuffer, tree_rec->master );
+ NetpAssert( NetpIsUncComputerNameValid( UncMasterBuffer ) );
+ UncMaster = &UncMasterBuffer[0];
+ } else {
+ UncMaster = NULL;
+ }
+
+ //
+ // Write config data for a single import directory.
+ //
+
+ tree_rec->state = signal;
+
+ ApiStatus = ImportDirWriteConfigData (
+ NULL, // server name
+ tree_rec->dir_name,
+ tree_rec->state,
+ UncMaster,
+ tree_rec->timestamp, // Last update time, secs since 1970.
+ tree_rec->lockcount,
+ tree_rec->time_of_first_lock); // Seconds since 1970.
+
+ if (ApiStatus != NO_ERROR) {
+
+ // Log error. Maybe we're running as wrong user...
+ AlertLogExit( ALERT_ReplSysErr,
+ NELOG_ReplSysErr,
+ ApiStatus,
+ NULL,
+ NULL,
+ NO_EXIT);
+ }
+ }
+
+}
+
+
+NET_API_STATUS
+ReplSyncCopy(
+ IN LPTSTR source_path,
+ IN LPTSTR dest_path,
+ IN OUT PCLIENT_LIST_REC tree_rec
+ )
+/*++
+
+Routine Description:
+
+ Copies source_path to dest_path and handles error conditions.
+
+Arguments:
+
+ source_path - UNC source path.
+
+ dest_path - UNC destination path.
+
+ tree_rec - pointer to CLIENT_LIST_REC for current dir.
+
+Return Value:
+
+ Net status code of the copy.
+
+Threads:
+
+ Only called by syncer thread.
+
+--*/
+{
+ NET_API_STATUS ApiStatus;
+
+ //
+ // Copy the file/tree and just return if there is no error.
+ //
+
+ IF_DEBUG( MAJOR ) {
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplSyncCopy: ***** copying file/tree '"
+ FORMAT_LPTSTR "' to '" FORMAT_LPTSTR "'.\n",
+ source_path, dest_path ));
+ }
+
+ ApiStatus = ReplCopyTree( source_path, dest_path );
+ if (ApiStatus == NO_ERROR) {
+ return NO_ERROR;
+ }
+
+ //
+ // If access is denied, raise that alert.
+ //
+
+ NetpKdPrint(( PREFIX_REPL_CLIENT "ReplSyncCopy: Copy failed, status="
+ FORMAT_API_STATUS ".\n", ApiStatus ));
+
+ if ((ApiStatus == ERROR_ACCESS_DENIED)
+ || (ApiStatus == ERROR_NETWORK_ACCESS_DENIED)) {
+
+ if ((tree_rec->alerts & ACCESS_DENIED_ALERT) == 0) {
+ AlertLogExit( ALERT_ReplAccessDenied,
+ NELOG_ReplAccessDenied,
+ ApiStatus,
+ source_path,
+ tree_rec->master,
+ NO_EXIT);
+ tree_rec->alerts |= ACCESS_DENIED_ALERT;
+ }
+
+
+ //
+ // Otherwise, raise a less specific alert.
+ //
+
+ } else if ((tree_rec->alerts & UPDATE_ERROR_ALERT) == 0) {
+ AlertLogExit( ALERT_ReplUpdateError,
+ NELOG_ReplUpdateError,
+ ApiStatus,
+ dest_path, // changed from source_path,
+ tree_rec->master,
+ NO_EXIT);
+ tree_rec->alerts |= UPDATE_ERROR_ALERT;
+ }
+
+ return (ApiStatus);
+
+}
+
+
+
+
+NET_API_STATUS
+ReplFileIntegrityDel(
+ IN LPTSTR path,
+ IN DWORD attrib,
+ IN OUT PCLIENT_LIST_REC tree_rec
+ )
+/*++
+
+Routine Description:
+
+ Deletes the file or directory specfied by path.
+
+Arguments:
+
+ path - UNC file or directory to delete. If a directory is specified,
+ the entire directory tree is deleted.
+
+ attrib - The file attributes of path.
+
+ tree_rec - Tree record for the directory containing path.
+
+Return Value:
+
+ Status of the operation.
+
+Threads:
+
+ Only called by syncer thread.
+
+--*/
+{
+ NET_API_STATUS NetStatus;
+
+ //
+ // Avoid -1, which means "file not found".
+ //
+
+ NetpAssert( attrib != (DWORD)(-1) );
+
+ //
+ // If 'path' is a directory, delete the entire tree.
+ //
+
+ if (attrib & FILE_ATTRIBUTE_DIRECTORY) {
+
+ IF_DEBUG( MAJOR ) {
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplFileIntegrityDel: *** deleting dir/tree *** '"
+ FORMAT_LPTSTR "'.\n", path ));
+ }
+ NetStatus = ReplTreeDelete(path);
+ if ( NetStatus == NO_ERROR || NetStatus == ERROR_FILE_NOT_FOUND ) {
+ return NO_ERROR;
+ }
+
+ //
+ // If 'path' is a file, force the file to be deleted.
+ //
+
+ } else {
+
+ //
+ // Ensure the file is not read-only so we can delete it.
+ //
+
+ (VOID) SetFileAttributes( path, FILE_ATTRIBUTE_NORMAL );
+
+ IF_DEBUG( SYNC ) {
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplFileIntegrityDel: *** deleting file *** '"
+ FORMAT_LPTSTR "'.\n", path ));
+ }
+
+ NetStatus = ReplDeleteFile( path );
+ if ( (NetStatus==NO_ERROR) || (NetStatus==ERROR_FILE_NOT_FOUND) ) {
+ return NO_ERROR;
+ }
+
+ }
+
+ //
+ // Log the error.
+ //
+ // No AlertLogging for sharing violation ? BUGBUG
+ //
+
+ if ((tree_rec->alerts & UPDATE_ERROR_ALERT) == 0) {
+ AlertLogExit( ALERT_ReplUpdateError,
+ NELOG_ReplUpdateError,
+ NetStatus,
+ path,
+ tree_rec->master,
+ NO_EXIT);
+
+ tree_rec->alerts |= UPDATE_ERROR_ALERT;
+ }
+
+ return NetStatus;
+}
+
+
+
+
+
+NET_API_STATUS
+ReplFileIntegrityCopy(
+ IN LPTSTR source_path,
+ IN LPTSTR dest_path,
+ IN LPTSTR tmp_path,
+ IN OUT PCLIENT_LIST_REC tree_rec,
+ IN DWORD src_attr,
+ IN DWORD dest_attr
+ )
+/*++
+
+Routine Description:
+
+ Copies source_path to temp_path and destination_path. Cleans up after
+ itself upon failure. Handles error conditions.
+
+Arguments:
+
+ source_path - UNC source path.
+
+ dest_path - UNC destination path.
+
+ tmp_path - UNC path to IMPORT\TMPFILE.RP$.
+
+ tree_rec - pointer to CLIENT_LIST_REC for current dir.
+
+ src_attr - Attributes of the source path.
+
+ dest_attr - Attributes of the destination path.
+
+Return Value:
+
+ Status of the operation.
+
+Threads:
+
+ Only called by syncer thread.
+
+--*/
+{
+ NET_API_STATUS NetStatus = NO_ERROR;
+
+ IF_DEBUG( MAJOR ) {
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplFileIntegrityCopy: ***** copying dir/file '"
+ FORMAT_LPTSTR "' to (temp) '" FORMAT_LPTSTR "'.\n",
+ source_path, tmp_path ));
+ }
+
+ //
+ // If we're copying a file, first copy the source to tmp_path.
+ //
+
+ if ((src_attr & FILE_ATTRIBUTE_DIRECTORY) == 0) {
+ NetStatus = ReplCopyFile( source_path, tmp_path, FALSE );
+ }
+
+
+ //
+ // Continue if we're successful so far.
+ //
+
+ if ( NetStatus == NO_ERROR ) {
+
+ //
+ // Try to delete existing file - if succeeds it must mean
+ // exclusive access (otherwise del would fail)
+ //
+ // if fails, error reporting was already done so may just return.
+ //
+
+ NetStatus = ReplFileIntegrityDel(dest_path, dest_attr, tree_rec);
+ if ( NetStatus != NO_ERROR ) {
+ (VOID) ReplFileIntegrityDel(tmp_path, src_attr, tree_rec);
+ return NetStatus;
+ }
+
+ //
+ // If source is a file, simply copy tmp_path to dest_path.
+ //
+
+ if ((src_attr & FILE_ATTRIBUTE_DIRECTORY) == 0) {
+ IF_DEBUG( MAJOR ) {
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplFileIntegrityCopy: ***** copying file '"
+ FORMAT_LPTSTR "' to file '" FORMAT_LPTSTR "'.\n",
+ source_path, tmp_path ));
+ }
+
+ NetStatus = ReplCopyFile( tmp_path, dest_path, FALSE );
+ if (NetStatus == NO_ERROR) {
+ return NO_ERROR;
+ }
+
+ //
+ // If the source is a directory,
+ // create a new dir and make sure attributes are same.
+ //
+ // Errors are reported by ReplSyncCopy
+ //
+
+ } else {
+
+ return (ReplSyncCopy(source_path, dest_path, tree_rec));
+ }
+ }
+
+ //
+ // Failure, delete any file we've created and report the error.
+ //
+
+ (VOID) ReplFileIntegrityDel(tmp_path, src_attr, tree_rec);
+
+ //
+ // Take care of error reporting.
+ //
+ // Sharing violations are simply ignored BUGBUG
+ //
+
+ if ((NetStatus == ERROR_ACCESS_DENIED)
+ || (NetStatus == ERROR_NETWORK_ACCESS_DENIED)) {
+
+ if ((tree_rec->alerts & ACCESS_DENIED_ALERT) == 0) {
+ AlertLogExit( ALERT_ReplAccessDenied,
+ NELOG_ReplAccessDenied,
+ NetStatus,
+ source_path,
+ tree_rec->master,
+ NO_EXIT);
+ tree_rec->alerts |= ACCESS_DENIED_ALERT;
+ }
+
+
+ //
+ // Otherwise, raise a less specific alert.
+ //
+
+ } else if ((tree_rec->alerts & UPDATE_ERROR_ALERT) == 0) {
+ AlertLogExit( ALERT_ReplUpdateError,
+ NELOG_ReplUpdateError,
+ NetStatus,
+ dest_path, // changed from source_path,
+ tree_rec->master,
+ NO_EXIT);
+ tree_rec->alerts |= UPDATE_ERROR_ALERT;
+ }
+
+ return NetStatus;
+}
+
+
+
+
+NET_API_STATUS
+ReplCreateTempDir(
+ IN LPTSTR tmp_path,
+ IN PCLIENT_LIST_REC tree_rec
+ )
+/*++
+
+Routine Description:
+
+ Creates dir TMPTREE.RP$ under client's IMPORT path.
+
+Arguments:
+
+ tmp_path - Name of the directory to create.
+
+ tree_rec - Tree record of the directory being replicated.
+
+Return Value:
+
+ Status of the operation.
+
+Threads:
+
+ Only called by syncer thread.
+
+--*/
+{
+ NET_API_STATUS NetStatus;
+
+ //
+ // Try to simply create the directory.
+ //
+ // BUGBUG Better security descriptor
+ //
+
+ if ( CreateDirectory( tmp_path, NULL ) ) {
+ return NO_ERROR;
+ }
+ NetStatus = (NET_API_STATUS) GetLastError();
+ NetpAssert( NetStatus != NO_ERROR );
+
+ //
+ // If dir exists - remove it and try again.
+ //
+
+ if ( ReplTreeDelete(tmp_path) == NO_ERROR ) {
+ if ( CreateDirectory( tmp_path, NULL ) ) {
+ return NO_ERROR;
+ }
+ NetStatus = (NET_API_STATUS) GetLastError();
+ }
+
+ if ((tree_rec->alerts & UPDATE_ERROR_ALERT) == 0) {
+ AlertLogExit( ALERT_ReplUpdateError,
+ NELOG_ReplUpdateError,
+ NetStatus,
+ tmp_path,
+ NULL,
+ NO_EXIT);
+
+ tree_rec->alerts |= UPDATE_ERROR_ALERT;
+ }
+
+ return NetStatus;
+}
+
+
+
+NET_API_STATUS
+ReplCreateReplLock(
+ IN OUT PCLIENT_LIST_REC tree_rec
+ )
+/*++
+
+Routine Description:
+
+ Creates a REPLLOCK.RP$ file in Pathname. If Succesful, checks for
+ presence of USERLOCKs, if present surrenders REPLLOCK and fails.
+
+ Assumes that caller has a lock (any kind) on RCGlobalClientListLock.
+
+Arguments:
+
+ tree_rec - Tree record of the directory being replicated.
+ The lock count will be updated by this routine on exit.
+
+
+Return Value:
+
+ NO_ERROR - everything worked.
+ ERROR_LOCKED - dir is already locked (e.g. by an app).
+ other status codes - some API failed.
+
+Threads:
+
+ Only called by syncer thread.
+
+--*/
+{
+ NET_API_STATUS ApiStatus;
+ LPNET_CONFIG_HANDLE ConfigHandle = NULL;
+
+ NetpAssert( tree_rec != NULL );
+ NetpAssert( ReplIsDirNameValid( tree_rec->dir_name ) );
+
+ //
+ // Are there are user locks?
+ //
+ if (tree_rec->lockcount > 0) {
+ return (ERROR_LOCKED);
+ }
+
+ //
+ // Record our lock in client list. (We've got a lock on list.)
+ //
+ ApiStatus = ReplIncrLockFields(
+ & (tree_rec->lockcount),
+ & (tree_rec->time_of_first_lock) );
+ NetpAssert( ApiStatus == NO_ERROR );
+ NetpAssert( tree_rec->lockcount == 1 );
+
+ //
+ // Open the right section of the config file/whatever.
+ //
+ ApiStatus = NetpOpenConfigData(
+ & ConfigHandle,
+ NULL, // local (no server name)
+ (LPTSTR) SECT_NT_REPLICATOR,
+ FALSE); // not read-only (we'll delete later)
+
+ if (ApiStatus != NO_ERROR) {
+
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplCreateReplLock: UNEXPECTED ERROR " FORMAT_API_STATUS
+ " from NetpOpenConfigData.\n", ApiStatus ));
+ goto Cleanup; // go log error
+ }
+
+ //
+ // Write the dir name out to registry (as CrashDir).
+ //
+ ApiStatus = NetpSetConfigValue(
+ ConfigHandle,
+ (LPTSTR) REPL_KEYWORD_CRASHDIR,
+ tree_rec->dir_name );
+ if (ApiStatus != NO_ERROR) {
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplCreateReplLock: UNEXPECTED ERROR " FORMAT_API_STATUS
+ " from NetpSetConfigValue.\n", ApiStatus ));
+ goto Cleanup; // go log error
+ }
+
+ //
+ // Increment number of locks in registry too.
+ //
+ ApiStatus = ImportDirLockInRegistry(
+ NULL, // no server name
+ tree_rec->dir_name );
+ if (ApiStatus != NO_ERROR) {
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplCreateReplLock: UNEXPECTED ERROR " FORMAT_API_STATUS
+ " from ImportDirLockInRegistry.\n", ApiStatus ));
+ }
+
+Cleanup:
+
+ if (ConfigHandle != NULL) {
+ (VOID) NetpCloseConfigData( ConfigHandle );
+ }
+ if (ApiStatus != NO_ERROR) {
+
+ //
+ // Undo client list update.
+ //
+ NetpAssert( tree_rec->lockcount == 1 );
+ ApiStatus = ReplDecrLockFields(
+ & (tree_rec->lockcount),
+ & (tree_rec->time_of_first_lock),
+ REPL_UNLOCK_NOFORCE );
+ NetpAssert( tree_rec->lockcount == 0 );
+
+ }
+ if (ApiStatus != NO_ERROR) {
+
+ AlertLogExit(
+ ALERT_ReplSysErr,
+ NELOG_ReplSysErr,
+ ApiStatus,
+ NULL,
+ NULL,
+ NO_EXIT );
+ }
+
+ IF_DEBUG( SYNC ) {
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplCreateReplLock: returning " FORMAT_API_STATUS ".\n",
+ ApiStatus ));
+ }
+ return (ApiStatus);
+
+}
+
+
+
+BOOL
+ReplAnyRemoteFilesOpen(
+ IN LPTSTR path
+ )
+/*++
+
+Routine Description:
+
+ Checks if a remote user is accessing a file in dir we are working on.
+
+Arguments:
+
+ path - A local path to check.
+
+Return Value:
+
+ Returns FALSE if no files are opened, or if server not started.
+
+Threads:
+
+ Only called by syncer thread.
+
+--*/
+{
+ NET_API_STATUS NetStatus;
+ PFILE_INFO_2 FileInfo2;
+ DWORD EntriesRead;
+ DWORD TotalEntries;
+ BOOL ret;
+ LPTSTR Pathname;
+
+#ifdef UNICODE
+ Pathname = path;
+#else // UNICODE
+
+ Pathname = NetpAllocStrFromTStr( path );
+
+ if ( Pathname == NULL ) {
+ return FALSE;
+ }
+#endif // UNICODE
+
+
+ //
+ // Ask server if any remote system is accessing any file in this tree.
+ //
+
+ NetStatus = NetFileEnum( NULL, // This machine
+ Pathname,
+ NULL, // No user name
+ 2, // Level
+ (LPBYTE *) (LPVOID) &FileInfo2,
+ 2*sizeof(FILE_INFO_2), // PrefMaxLen
+ &EntriesRead,
+ &TotalEntries,
+ NULL );
+
+#ifndef UNICODE
+ NetpMemoryFree( Pathname );
+#endif // UNICODE
+
+
+ //
+ // On success, just indicate whether any file were enumerated.
+ //
+
+ if ( NetStatus == NO_ERROR || NetStatus == ERROR_MORE_DATA ) {
+ if ( TotalEntries == 0 ) {
+ ret = FALSE;
+ IF_DEBUG( SYNC ) {
+ NetpKdPrint(( PREFIX_REPL_CLIENT "No remote files open.\n" ));
+ }
+ } else {
+ ret = TRUE;
+ IF_DEBUG( SYNC ) {
+ NetpKdPrint(( PREFIX_REPL_CLIENT "Some remote files open.\n" ));
+ }
+ }
+
+ (VOID) NetApiBufferFree( FileInfo2 );
+
+ //
+ // If we can't contact the server, obviously no system has files open.
+ //
+
+ } else {
+ ret = FALSE;
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "Can't determine if remote files open "
+ FORMAT_API_STATUS ".\n", NetStatus ));
+ // BUGBUG: log this?
+ }
+
+ return ret;
+}
+
+
+
+NET_API_STATUS
+ReplTreeDelete(
+ IN LPTSTR dir
+ )
+/*++
+
+Routine Description:
+
+
+ Delete a directory tree with extreme prejudice
+
+
+ ReplTreeDelete deletes the directory specified by <dir>, and if
+ <dir> is not empty, it removes its contents. <dir> cannot
+ be the root directory of a device.
+
+ ReplTreeDelete calls itself recursively.
+
+ Since ReplTreeDelete fails as soon as one of the functions
+ which it invokes fails, a failure will leave some but
+ not all of the tree deleted.
+
+
+Arguments:
+
+ dir - Specifies the root of the tree to be deleted (a UNC name).
+ This buffer is modified by ReplTreeDelete and although it is restored
+ to its original value by ReplTreeDelete before it returns, it
+ nevertheless must point to a buffer large enough to hold any file name.
+
+Return Value:
+
+Threads:
+
+ Called by client and syncer threads.
+
+--*/
+{
+ NET_API_STATUS ApiStatus = NO_ERROR;
+ HANDLE FindHandle = INVALID_HANDLE_VALUE;
+ WIN32_FIND_DATA FindData;
+
+ DWORD dir_index;
+
+ IF_DEBUG( MAJOR ) {
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplTreeDelete: *** deleting tree *** '"
+ FORMAT_LPTSTR "'.\n", dir ));
+ }
+
+ dir_index = STRLEN(dir);
+ (void) STRCPY(dir + dir_index++, SLASH);
+ (void) STRCPY(dir + dir_index, STAR_DOT_STAR);
+
+ //
+ // Enumerate all files/directories within the current directory
+ //
+
+ FindHandle = FindFirstFile( dir, &FindData );
+
+ if ( FindHandle != INVALID_HANDLE_VALUE ) {
+ do {
+
+ //
+ // Build the full pathname of the file/directory.
+ //
+
+ (void) STRCPY(dir + dir_index, FindData.cFileName );
+
+ //
+ // Recursively call ReplTreeDelete on all directories.
+ //
+
+ if ( FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) {
+
+ //
+ // make sure it's not '.' or '..'.
+ //
+
+ if ( STRCMP(FindData.cFileName , DOT) == 0) {
+ continue;
+ }
+ if ( STRCMP(FindData.cFileName , DOT_DOT) == 0) {
+ continue;
+ }
+
+
+ if ((ApiStatus = ReplTreeDelete(dir)) != NO_ERROR ) {
+ break;
+ }
+
+
+ //
+ // Simply force files to be deleted.
+ //
+
+ } else {
+
+ //
+ // change the file's mode so it can be deleted.
+ //
+
+ if ( !SetFileAttributes( dir, FILE_ATTRIBUTE_NORMAL ) ) {
+ ApiStatus = (NET_API_STATUS) GetLastError();
+ NetpAssert( ApiStatus != NO_ERROR );
+
+ if (ApiStatus == ERROR_HANDLE_EOF) {
+ // BUGBUG: quiet this debug output eventually.
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplTreeDelete (after SetFileAttributes): "
+ "GOT HANDLE EOF!\n" ));
+ }
+
+ break;
+ }
+
+ //
+ // Remove the file.
+ //
+
+ IF_DEBUG( MAJOR ) {
+ NetpKdPrint(( PREFIX_REPL_CLIENT "ReplTreeDelete: deleting "
+ FORMAT_LPTSTR ".\n", dir ));
+ }
+ ApiStatus = ReplDeleteFile( dir );
+ if (ApiStatus != NO_ERROR) {
+ // BUGBUG; // log and debug dump this!
+ NetpKdPrint(( "ReplTreeDelete: ReplDeleteFile failed"
+ ", status " FORMAT_API_STATUS "\n", ApiStatus ));
+ break;
+ }
+
+ }
+ } while ( FindNextFile( FindHandle, &FindData ) );
+
+ }
+
+ // BUGBUG: log error if FindFirstFile failed.
+
+ if (FindHandle != INVALID_HANDLE_VALUE) {
+ (VOID) FindClose( FindHandle );
+ FindHandle = INVALID_HANDLE_VALUE;
+ }
+
+
+ //
+ // restore old directory path.
+ //
+
+ *(dir + --dir_index) = TCHAR_EOS;
+
+ if (ApiStatus) {
+ goto LogErrorAndExit;
+ }
+
+ //
+ // Allow the directory itself to be deleted
+ //
+
+ if ( !SetFileAttributes( dir, FILE_ATTRIBUTE_NORMAL ) ) {
+ ApiStatus = (NET_API_STATUS) GetLastError();
+ goto LogErrorAndExit;
+ }
+
+ //
+ // Remove the directory itself.
+ //
+
+ IF_DEBUG( MAJOR ) {
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplTreeDelete: ***deleting directory*** "
+ FORMAT_LPTSTR ".\n", dir ));
+ }
+ if ( !RemoveDirectory( dir ) ) {
+ ApiStatus = (NET_API_STATUS) GetLastError();
+ goto LogErrorAndExit;
+ }
+
+ return NO_ERROR;
+
+LogErrorAndExit:
+
+ NetpAssert( ApiStatus != NO_ERROR );
+
+ if (ApiStatus == ERROR_HANDLE_EOF) {
+ // BUGBUG: quiet this debug output eventually.
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplTreeDelete(at end): GOT HANDLE EOF!\n" ));
+ }
+
+ if (FindHandle != INVALID_HANDLE_VALUE) {
+ (VOID) FindClose( FindHandle );
+ }
+
+ ReplErrorLog(
+ NULL, // no server name (local)
+ NELOG_ReplSysErr, // log code
+ ApiStatus, // error code we got
+ NULL, // no optional str 1
+ NULL ); // no optional str 2
+ return ApiStatus;
+
+}
+
+
+VOID
+ReplInsertWorkQueue(
+ IN PBIGBUF_REC Buffer
+ )
+/*++
+
+Routine Description:
+
+ Inserts the specified buffer at the tail of the work queue.
+
+Arguments:
+
+ Buffer -- Address of buffer to insert. The first four bytes of the
+ buffer will be overwritten.
+
+Return Value:
+
+ None.
+
+Threads:
+
+ Only called by client and watchd threads.
+
+--*/
+{
+
+ //
+ // Serialize access to the global list head and tail.
+ //
+
+ ACQUIRE_LOCK( RCGlobalWorkQueueLock );
+
+ //
+ // Put the buffer at the tail of the list.
+ //
+
+ Buffer->next_p = NULL;
+ if ( RCGlobalWorkQueueHead == NULL ) {
+ RCGlobalWorkQueueHead = Buffer;
+ } else {
+ RCGlobalWorkQueueTail->next_p = Buffer;
+ }
+ RCGlobalWorkQueueTail = Buffer;
+
+ //
+ // Allow others to access the list.
+ //
+
+ RELEASE_LOCK( RCGlobalWorkQueueLock );
+
+ //
+ // Indicate that there is one more entry on the list.
+ //
+
+ if ( !ReleaseSemaphore( RCGlobalWorkQueueSemaphore, 1, NULL ) ) {
+ NET_API_STATUS NetStatus;
+
+ NetStatus = (NET_API_STATUS) GetLastError();
+ NetpAssert( NetStatus != NO_ERROR );
+ NetpAssert( NetStatus != ERROR_INVALID_FUNCTION );
+
+ AlertLogExit( ALERT_ReplSysErr,
+ NELOG_ReplSysErr,
+ NetStatus,
+ NULL,
+ NULL,
+ NO_EXIT);
+
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "Insert Work Queue couldn't release "
+ "sem " FORMAT_API_STATUS "\n", NetStatus ));
+ }
+
+}
+
+
+PBIGBUF_REC
+ReplGetWorkQueue(
+ OUT PBOOL ClientTerminating
+ )
+/*++
+
+Routine Description:
+
+ Removes an entry from the head of the work queue. Waits until there
+ is a buffer to remove or until the REPL service should terminate.
+
+ Note that the RCGlobalWorkQueueSemaphore is a counting semaphore which
+ is signalled as many times as there are items in the work queue. It is
+ important to read the semaphore every time something is removed
+ from the queue, otherwise the two will become out of sync.
+
+Arguments:
+
+ ClientTerminating - set to TRUE (on output) if client is terminating;
+ set to FALSE otherwise.
+
+Return Value:
+
+ Returns the address of a buffer from the head of the queue.
+ Return NULL if awoken and there are no entries at the head of the queue.
+
+Threads:
+
+ Only called by syncer thread.
+
+--*/
+{
+ PBIGBUF_REC Buffer;
+ HANDLE WaitHandles[2];
+ DWORD WaitStatus;
+
+ //
+ // Wait for an entry to be place into the work queue.
+ // (Or for the REPL service to be terminated).
+ //
+
+ WaitHandles[0] = ReplGlobalClientTerminateEvent;
+ WaitHandles[1] = RCGlobalWorkQueueSemaphore;
+
+ WaitStatus = WaitForMultipleObjects( 2, WaitHandles, FALSE, (DWORD) -1 );
+ if (WaitStatus == 0) {
+ *ClientTerminating = TRUE;
+ return (NULL);
+ } else {
+ *ClientTerminating = FALSE;
+ }
+
+ //
+ // Remove an entry from the queue if there is one.
+ //
+
+ ACQUIRE_LOCK( RCGlobalWorkQueueLock );
+
+ Buffer = RCGlobalWorkQueueHead;
+
+ if ( Buffer != NULL ) {
+ RCGlobalWorkQueueHead = Buffer->next_p;
+ if ( RCGlobalWorkQueueTail == Buffer ) {
+ RCGlobalWorkQueueTail = NULL;
+ }
+ }
+
+ RELEASE_LOCK( RCGlobalWorkQueueLock );
+
+ return Buffer;
+
+}
diff --git a/private/net/svcdlls/repl/server/synctree.c b/private/net/svcdlls/repl/server/synctree.c
new file mode 100644
index 000000000..1b02167d9
--- /dev/null
+++ b/private/net/svcdlls/repl/server/synctree.c
@@ -0,0 +1,2842 @@
+/*++
+
+Copyright (c) 1987-1993 Microsoft Corporation
+
+Module Name:
+
+ synctree.c
+
+Abstract:
+
+ Contains the functions which actually sync with the master.
+
+Author:
+
+ Ported from Lan Man 2.1
+
+Environment:
+
+ User mode only.
+ Contains NT-specific code.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+ 09-May-1989 (yuv)
+ Initial Coding.
+
+ 17-Oct-1991 (cliffv)
+ Ported to NT. Converted to NT style.
+
+ 11-Dec-1991 JohnRo
+ Avoid unnamed structure fields to allow MIPS builds.
+
+ 13-Dec-1991 JohnRo
+ Avoid nonstandard dollar sign in C source code.
+
+ 18-Dec-1991 JohnRo
+ Propagate LM2.1 bug fixes for LM bugs 3505 and 3423.
+
+ 08-Jan-1992 JohnRo
+ Use REPL_STATE_ equates for client_list_rec.state values.
+ 21-Jan-1992 JohnRo
+ Changed tree_depth_exception to a real BOOL.
+ Made other changes suggested by PC-LINT.
+ 24-Jan-1992 JohnRo
+ P_ globals are now called ReplGlobal variables in replgbl.h.
+ Changed to use LPTSTR etc.
+ 26-Feb-1992 JohnRo
+ Renamed tree depth fields to avoid conflicts between syncer and pulser.
+ 05-Mar-1992 JohnRo
+ Changed interface to match new service controller.
+ 24-Mar-1992 JohnRo
+ Added more debug output.
+ Use integrity and extent equates in <lmrepl.h>.
+ Modify REPL$ share handling.
+ 24-Mar-1992 JohnRo
+ Renamed many ReplGlobal vars to ReplConfig vars.
+ Added/corrected some lock handling.
+ Still more debug output.
+ 25-Mar-1992 JohnRo
+ New ReplFind routines interface.
+ Fixed bug where wrong master path name was being built in ReplSyncTree.
+ Fixed a max depth problem in ReplFileIntegritySync().
+ 17-Jul-1992 JohnRo
+ RAID 10503: srv mgr: repl dialog doesn't come up.
+ Use PREFIX_ equates.
+ 29-Jul-1992 JohnRo
+ RAID 2650: repl svc should handle new subdirs. Corrected ReplAllocBuf()
+ arg types.
+ 11-Aug-1992 JohnRo
+ RAID 2115: repl svc should wait while stopping or changing roles.
+ 17-Aug-1992 JohnRo
+ RAID 3607: REPLLOCK.RP$ is being created during tree copy.
+ 10-Sep-1992 JohnRo
+ RAID 3608: repl time stamp not updated.
+ 04-Nov-1992 JohnRo
+ RAID 10615: fix rare memory leak in ReplSyncTree.
+ Also added some debug output when setting no sync state.
+ 18-Nov-1992 JohnRo
+ RAID 3638: repl cannot get more than 64K for buffer space.
+ Avoid compiler warnings (const vs. volatile).
+ 17-Dec-1992 JohnRo
+ RAID 1513: Repl does not maintain ACLs. (Also fix HPFS->FAT timestamp.)
+ 06-Jan-1993 JohnRo
+ RAID 6727: some "only at client" files are never deleted.
+ Made some changes suggested by PC-LINT 5.0
+ 11-Jan-1993 JohnRo
+ RAID 6710: repl cannot manage dir with 2048 files.
+ 17-Jan-1993 JohnRo
+ RAID 7053: locked trees added to pulse msg. (Actually fix all
+ kinds of remote lock handling.)
+ 03-Mar-1993 JohnRo
+ RAID 12392: if dir timestamp changes, don't delnode entire tree.
+ 04-Mar-1993 JohnRo
+ RAID 12237: replicator tree depth exceeded.
+ RAID 8355: Downlevel lock file check causes assert in repl importer.
+ PC-LINT found a bug in ReplTreeIntegritySync.
+ Added debug output to ChecksumEqual().
+ 11-Mar-1993 JohnRo
+ RAID 14144: avoid very long hourglass in repl UI.
+ 26-Mar-1993 JohnRo
+ RAID 4267: Replicator has problems when work queue gets large.
+ Prepare for >32 bits someday.
+ 31-Mar-1993 JohnRo
+ Repl svc stop should be quicker.
+ Also fix some HANDLE vs. LPREPL_FIND_HANDLE problems.
+ Also various debug output changes.
+ 13-Apr-1993 JohnRo
+ RAID 3107: locking directory over the net gives network path not found.
+ 21-Apr-1993 JohnRo
+ RAID 7313: repl needs change permission to work on NTFS,
+ or we need to delete files differently.
+ 26-Apr-1993 JohnRo
+ RAID 7157: fix setting ACLs, etc., on dirs themselves.
+ 28-Apr-1993 JohnRo
+ RAID 7984: repl sometimes forgets to update empty dir timestamp, etc.
+ Use NetpKdPrint() where possible.
+ 27-May-1993 JimKel and JohnRo
+ RAID 11682: Fixed tree integrity first level dir (ACLs, etc) after copy.
+ Made changes suggested by PC-LINT 5.0.
+ 10-Jun-1993 JohnRo
+ RAID 13080: Allow repl between different timezones.
+ Updated some comments.
+ More changes suggested by PC-LINT 5.0
+
+--*/
+
+
+// These must be included first:
+
+#include <windows.h> // IN, DWORD, etc.
+#include <lmcons.h>
+
+// These may be included in any order:
+
+#include <alertmsg.h> // ALERT_* defines
+#include <align.h> // ALIGN_* defines
+#include <checksum.h> // FORMAT_CHECKSUM.
+#include <client.h>
+#include <config.h> // NetpOpenConfigData(), LPNET_CONFIG_HANDLE, etc.
+#include <confname.h> // SECT_ and REPL_KEYWORD_ equates.
+#include <dirname.h> // ReplIsDirNameValid().
+#include <filefind.h> // REPL_WIN32_FIND_DATA, ReplCountDirectoryEntries, etc.
+#include <impdir.h> // ImportDirUnlockInRegistry().
+#include <lmapibuf.h> // NetApiBufferFree().
+#include <lmerr.h> // NO_ERROR, ERROR_ and NERR_ equates.
+#include <lmerrlog.h> // NELOG_* defines
+#include <lmrepl.h> // REPL_STATE_ equates, etc.
+#include <masproto.h> // ReplCheckExportLocks().
+#if DBG
+#include <names.h> // NetpIsUncComputerNameValid().
+#endif
+#include <netdebug.h> // DBGSTATIC, NetKdPrint(), etc.
+#include <netlib.h> // NetpMemoryAllocate
+#include <prefix.h> // PREFIX_ equates.
+#include <repldefs.h> // REPL_SHARE, etc.
+#include <replgbl.h> // ReplGlobal and ReplConfig variables.
+#include <replp.h>
+#include <tstr.h> // STRLEN(), etc.
+
+
+//
+// Return values for ReplFileCompare
+//
+#define COPY_FROM_MASTER 1
+#define COPY_FROM_CLIENT 2
+
+//
+// Locally used definitions
+//
+#define ONLY_AT_CLIENT 1
+#define ONLY_AT_MASTER -1
+#define AT_BOTH 0
+
+
+//
+// The VAR_BUF structure defines a buffer to contain several FIND_DATA
+// structures and an array of pointers to the structure. The buffer is
+// allocated, enlarged and deallocated by the ReplAllocBuf routine.
+//
+typedef struct _VAR_BUF {
+ DWORD size; // Numbers of bytes alloced for *buf
+ PUCHAR buf;
+ DWORD ArraySize; // Number of pointers alloced for *array
+ LPREPL_WIN32_FIND_DATA *array;
+} VAR_BUF, *PVAR_BUF;
+
+
+//
+// Allocation flags to ReplAllocBuf
+//
+#define ALLOC_BUF 0
+#define REALLOC_BUF 1
+#define FREE_BUF 2
+
+
+DBGSTATIC VOID
+ReplDisplayFileFindArray(
+ IN LPTSTR Tag,
+ IN LPREPL_WIN32_FIND_DATA Array,
+ IN DWORD EntryCount
+ )
+{
+#if 0
+ LPREPL_WIN32_FIND_DATA Entry = Array;
+ DWORD Index = 0;
+
+ NetpKdPrint(( FORMAT_LPTSTR ":\n", Tag ));
+ // BUGBUG: This seems to be broken (only displays first entry right).
+ while (Index < EntryCount) {
+ if (Entry->fdFound.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
+ LPTSTR Name = Entry->fdFound.cFileName;
+ NetpKdPrint(( " " FORMAT_DWORD ": " FORMAT_LPTSTR "\n",
+ Index, Name ));
+ }
+ ++Index;
+ ++Entry;
+ }
+#else
+ UNREFERENCED_PARAMETER( Tag );
+ UNREFERENCED_PARAMETER( Array );
+ UNREFERENCED_PARAMETER( EntryCount );
+#endif
+
+} // ReplDisplayFileFindArray
+
+
+BOOL
+ReplFileOrDirExists(
+ IN LPCTSTR FileName
+ )
+{
+ if ( (FileName==NULL) || ( (*FileName) == TCHAR_EOS ) ) {
+ return (FALSE); // no, it does not exist.
+ } else if ( GetFileAttributes( (LPTSTR) FileName ) != ((DWORD)-1) ) {
+ return (TRUE); // yes, it exists.
+ } else {
+ // GetFileAttributes returned -1. Does not exist or some other error.
+ return (FALSE); // no, it does not exist.
+ }
+
+ /*NOTREACHED*/
+
+} // ReplFileOrDirExists
+
+
+
+DBGSTATIC NET_API_STATUS
+ReplAllocBuf(
+ IN OUT PCLIENT_LIST_REC tree_rec,
+ IN OUT PVAR_BUF alloc,
+ IN DWORD alloc_flag
+ )
+/*++
+
+Routine Description:
+
+ ReplAllocBuf handles file find buffer alloc/realloc/free operations.
+ This routine allocates a buffer for the structures plus another array
+ of pointers to those structures. The VAR_BUF structure contains the
+ data which is controlled by this routine. The est_max_dir_entry_count
+ field in the client record is used to tell this routine how large to
+ allocate things.
+
+ Note that the old data is NOT copied if a reallocation is done.
+
+ Also note that this is a recursive routine. Be careful.
+
+Arguments:
+
+ tree_rec - identifies the directory this buffer is being allocated for.
+ This may have alert bits changed in it if ReplAllocBuf fails.
+
+ alloc - specifies the buffer descriptor (VAR_BUF) for this buffer.
+
+ alloc_flag - ALLOC_BUF if this is an allocation.
+ REALLOC_BUF if this is a reallocation.
+ FREE_BUF if this is to free allocated buffer.
+
+Return Value:
+
+ Status of operation.
+
+Threads:
+
+ Only called by syncer thread.
+
+--*/
+{
+ DWORD EntrySize;
+ NET_API_STATUS NetStatus;
+ DWORD NewEntryCount;
+
+ EntrySize = ROUND_UP_COUNT(
+ sizeof(REPL_WIN32_FIND_DATA),
+ ALIGN_WORST );
+ NetpAssert( EntrySize > 0 );
+
+ //
+ // Check for caller errors.
+ //
+ NetpAssert(
+ (alloc_flag==ALLOC_BUF)
+ || (alloc_flag==REALLOC_BUF)
+ || (alloc_flag==FREE_BUF) );
+
+ NetpAssert( alloc != NULL );
+ NetpAssert( tree_rec != NULL );
+ NewEntryCount = tree_rec->est_max_dir_entry_count;
+
+ //
+ // Make sure we have room for at least one entry. We'll always
+ // find "." and "..", so we need a buffer. But we'll skip leaving those
+ // entries in the buffer.
+ //
+ // Also, make sure that there is space for an entire extra entry at the end
+ // of the array. That way, if ReplFindNext tries to write something while
+ // there being called at end of file, we won't trash the heap.
+ //
+ ++NewEntryCount;
+ NetpAssert( NewEntryCount > 0 );
+
+ IF_DEBUG( SYNC ) {
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplAllocBuf: operation " FORMAT_DWORD " for VAR_BUF at "
+ FORMAT_LPVOID ", changing to " FORMAT_DWORD " entries.\n",
+ alloc_flag, (LPVOID) alloc, NewEntryCount ));
+ }
+
+ //
+ // Free the old buffer if necessary.
+ //
+ if ( alloc_flag != ALLOC_BUF ) {
+ if ( alloc->buf != NULL ) {
+ NetpMemoryFree( alloc->buf );
+ alloc->buf = NULL;
+ }
+ if ( alloc->array != NULL ) {
+ NetpMemoryFree( alloc->array );
+ alloc->array = NULL;
+ }
+ if (alloc_flag==FREE_BUF) {
+ return NO_ERROR;
+ }
+ // Fall through to alloc code for realloc case.
+ }
+
+ //
+ // Avoid confusing callers if we can only allocate one of these things...
+ //
+ alloc->array = NULL;
+ alloc->buf = NULL;
+
+ //
+ // Compute size of array of pointers and allocate it.
+ //
+ alloc->ArraySize = NewEntryCount;
+
+ alloc->array = NetpMemoryAllocate(
+ NewEntryCount * sizeof(LPREPL_WIN32_FIND_DATA) );
+
+ if (alloc->array == NULL) {
+ NetStatus = ERROR_NOT_ENOUGH_MEMORY;
+
+ if ((tree_rec->alerts & UPDATE_ERROR_ALERT) == 0) {
+ AlertLogExit( ALERT_ReplUpdateError,
+ NELOG_ReplUpdateError,
+ NetStatus,
+ tree_rec->dir_name,
+ tree_rec->master,
+ NO_EXIT);
+ tree_rec->alerts |= UPDATE_ERROR_ALERT;
+ }
+ return NetStatus;
+ }
+
+ //
+ // Compute size of find buffer and allocate it.
+ //
+ alloc->size = NewEntryCount * EntrySize;
+
+ alloc->buf = NetpMemoryAllocate( alloc->size );
+
+ if ( alloc->buf == NULL || alloc->array == NULL ) {
+
+ (VOID) ReplAllocBuf( tree_rec, alloc, FREE_BUF );
+ NetStatus = ERROR_NOT_ENOUGH_MEMORY;
+
+ if ((tree_rec->alerts & UPDATE_ERROR_ALERT) == 0) {
+ AlertLogExit( ALERT_ReplUpdateError,
+ NELOG_ReplUpdateError,
+ NetStatus,
+ tree_rec->dir_name,
+ tree_rec->master,
+ NO_EXIT);
+ tree_rec->alerts |= UPDATE_ERROR_ALERT;
+ }
+ return NetStatus;
+ }
+
+ return NO_ERROR;
+}
+
+
+DBGSTATIC VOID
+ReplSort(
+ LPREPL_WIN32_FIND_DATA base[],
+ DWORD num
+ )
+/*++
+
+Routine Description:
+
+ Shell sort to FIND_DATA structures by file name.
+
+Arguments:
+
+ base - Array of pointer to FIND_DATA structures to sort.
+
+ num - Number of entries in the array.
+
+Return Value:
+
+ NONE.
+
+--*/
+{
+ INT inc; // diminishing increment.
+
+ //
+ // use Knuth formula h(k-1) = 2*h(k) +1
+ //
+
+ LPREPL_WIN32_FIND_DATA temp;
+ INT i, j;
+
+ //
+ // compute starting inc.
+ //
+
+ inc = 1;
+ while (inc < (INT)num)
+ inc = 2 * inc + 1;
+ inc = (inc - 1) / 2;
+
+ for (; inc != 0 ; inc = (inc - 1) / 2) {
+ for (i = inc; i < (INT)num; i++) {
+ temp = base[i];
+ j = i - inc;
+ while ((j >= 0) &&
+ (STRICMP(temp->fdFound.cFileName, base[j]->fdFound.cFileName ) < 0 )) {
+ base[j+inc] = base[j];
+ j -= inc;
+ }
+ base[j+inc] = temp;
+ }
+ }
+}
+
+
+
+
+DBGSTATIC NET_API_STATUS
+ReplReadSortDir2(
+ IN LPTSTR path,
+ OUT LPDWORD dir_count,
+ IN OUT PVAR_BUF out,
+ IN OUT PCLIENT_LIST_REC tree_rec
+ )
+/*++
+
+Routine Description:
+
+ Reads all entries (up to dir_count) in the dir specified by dir, and sorts
+ acording to file name. The sort is not directly on buffer, but rather
+ the offsets into the buffer are arranged in array.
+
+Arguments:
+
+ path - Specifies UNC pathname of the directory. This buffer will be
+ modified and returned to its original value.
+
+ dir_count - Returns the number of directory entries used.
+
+ out - Specifies the size and address of the buffer to return the FIND_DATA
+ in.
+
+ tree_rec - Pointer to dir's CLIENT_LIST_REC.
+
+Return Value:
+
+ Status code.
+
+ ERROR_BUFFER_OVERFLOW : means that the buffer size needs to be extended.
+
+Threads:
+
+ Only called by syncer thread.
+
+--*/
+{
+ NET_API_STATUS NetStatus;
+ LPREPL_WIN32_FIND_DATA buf_p;
+ LPREPL_FIND_HANDLE FindHandle = INVALID_REPL_HANDLE;
+
+ DWORD path_index;
+ DWORD count = 0;
+
+ //
+ // Read the first file into the front of the buffer.
+ //
+
+ buf_p = (LPREPL_WIN32_FIND_DATA) out->buf;
+ if ( out->size < sizeof( *buf_p ) ) {
+ return ERROR_BUFFER_OVERFLOW ;
+ }
+
+ //
+ // Append \*.* to the path
+ //
+
+ path_index = STRLEN(path);
+ (void) STRCPY(path + path_index, SLASH);
+ (void) STRCPY(path + path_index + 1, STAR_DOT_STAR);
+
+ IF_DEBUG( SYNC ) {
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplReadSortDir2: searching '" FORMAT_LPTSTR "'.\n",
+ path ));
+ }
+
+ //
+ // Find the first file.
+ //
+
+ FindHandle = ReplFindFirstFile( path, buf_p );
+
+ if ( FindHandle == INVALID_REPL_HANDLE ) {
+
+ NetStatus = GetLastError();
+
+
+ //
+ // This error means there was a connection to the REPL$ share
+ // on the srever that went away (the server went down and has
+ // come up again, or more likely the REPL on the server was stopped
+ // and restarted) so we must retry to establish the connection again.
+ //
+
+ if ( NetStatus == ERROR_NETNAME_DELETED ) {
+ FindHandle = ReplFindFirstFile( path, buf_p );
+
+ if ( FindHandle == INVALID_REPL_HANDLE ) {
+ NetStatus = GetLastError();
+ } else {
+ NetStatus = NO_ERROR;
+ }
+ }
+
+ //
+ // Now a Hack - the server maps access denied to one of the
+ // following error codes, to verify whether it is really an access
+ // denied error we need a different api.
+ //
+
+ if ( NetStatus == ERROR_NO_MORE_FILES ||
+ NetStatus == ERROR_PATH_NOT_FOUND ) {
+
+ *(path + path_index) = L'\0';
+
+ //
+ // If there really are no files in the directory,
+ // just indicate so.
+ //
+
+ if ( ReplFileOrDirExists( path ) ) {
+ *dir_count = 0;
+ return NO_ERROR;
+ }
+
+
+ //
+ // The LM2.1 code didn't generate an alert for ERROR_PATH_NOT_FOUND.
+ // I'll just join the common error code.
+ //
+
+ NetStatus = GetLastError();
+ goto Cleanup;
+ }
+
+ if ( NetStatus != NO_ERROR ) {
+ goto Cleanup;
+ }
+ }
+
+ //
+ // do FindNext until exhausted or count reached.
+ //
+
+ do {
+
+ //
+ // Handle subdirectories.
+ //
+
+ if (buf_p->fdFound.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) {
+
+ //
+ // If only first level files should be replicated,
+ // ignore directories.
+ //
+
+ if (tree_rec->extent != REPL_EXTENT_TREE ) {
+ continue;
+ }
+
+ //
+ // Skip over "." and ".." once and for all.
+ //
+
+ if ( STRCMP( buf_p->fdFound.cFileName, DOT) == 0 ||
+ STRCMP( buf_p->fdFound.cFileName, DOT_DOT) == 0 ) {
+
+ continue;
+ }
+
+ }
+
+ //
+ // Check for end of array. Note that we can't fill in the last entry,
+ // as we need to leave space for the last find next call.
+ //
+
+ if ( count >= ((out->ArraySize)-1) ) {
+ NetStatus = ERROR_BUFFER_OVERFLOW;
+ goto Cleanup;
+ }
+
+ //
+ // Keep a pointer to this entry.
+ //
+ out->array[count] = buf_p;
+ count++;
+
+ //
+ // Find out where next entry should go.
+ //
+
+ ++buf_p;
+
+ buf_p = ROUND_UP_POINTER( buf_p, ALIGN_WORST );
+
+#if 0
+ // BUGBUG: we now allocate entire entries only, so this check is
+ // useless.
+ if ( out->size - ((PUCHAR)buf_p - (PUCHAR)out->buf) < sizeof( *buf_p )){
+ NetStatus = ERROR_BUFFER_OVERFLOW;
+ goto Cleanup;
+ }
+#endif
+
+ } while ( ReplFindNextFile( FindHandle, buf_p ));
+
+ //
+ // FindNext failed (perhaps with ERROR_NO_MORE_FILES).
+ //
+
+ NetStatus = GetLastError();
+
+
+ //
+ // Clean up
+ //
+
+Cleanup:
+
+ //
+ // Free any locally used resources.
+ //
+
+ if ( FindHandle != INVALID_REPL_HANDLE ) {
+ (void) ReplFindClose( FindHandle );
+ }
+ *(path + path_index) = '\0';
+
+
+
+ //
+ // If we simply enumerated all the files,
+ // sort the array and return them.
+ //
+
+ if ( NetStatus == ERROR_NO_MORE_FILES ) {
+ ReplSort( out->array, count );
+ *dir_count = count;
+ return NO_ERROR;
+ }
+
+ return NetStatus;
+
+}
+
+
+
+
+
+
+DBGSTATIC NET_API_STATUS
+ReplReadSortDir(
+ IN LPTSTR path,
+ OUT LPDWORD dir_count,
+ IN OUT PVAR_BUF out,
+ IN OUT PCLIENT_LIST_REC tree_rec
+ )
+/*++
+
+Routine Description:
+
+ Just a wrapper for ReplReadSortDir2 which does the actual work.
+
+ Reads all entries (up to out->ArraySize) in the dir specified by dir,
+ and sorts acording to file name. The sort is not directly on buffer,
+ but rather the pointers into the buffer are arranged in out->array.
+
+Arguments:
+
+ path - Specifies UNC pathname of the directory. This buffer will be
+ modified and returned to its original value.
+
+ dir_count - Returns the number of directory entries used.
+
+ out - Specifies the size and address of the buffer to return the find data
+ in.
+
+ tree_rec - Pointer to dir's CLIENT_LIST_REC.
+
+Return Value:
+
+ Status code.
+
+Threads:
+
+ Only called by syncer thread.
+
+--*/
+{
+ NET_API_STATUS NetStatus;
+
+ //
+ // Loop calling ReplReadSortDir2 growing the buffer until it is big enough.
+ //
+
+ for (;;) {
+
+ NetStatus = ReplReadSortDir2(path, dir_count, out, tree_rec);
+
+ if ( NetStatus != ERROR_BUFFER_OVERFLOW ) {
+ break;
+ }
+
+ //
+ // Need to grow buffer. Figure out how big it should be.
+ //
+ NetStatus = ReplCountDirectoryEntries(
+ path,
+ & (tree_rec->est_max_dir_entry_count) );
+ if (NetStatus != NO_ERROR) {
+ NetpAssert( NetStatus != ERROR_BUFFER_OVERFLOW );
+ goto Cleanup;
+ }
+
+ //
+ // Expand the buffer.
+ //
+ if ((NetStatus = ReplAllocBuf(tree_rec, out, REALLOC_BUF)) != NO_ERROR){
+
+ //
+ // ReplAllocBuf already reported the error.
+ //
+
+ return NetStatus;
+ }
+
+ }
+
+Cleanup:
+
+ if ( NetStatus == NO_ERROR ) {
+
+ return NetStatus;
+
+ }
+
+ //
+ // Report any errors.
+ //
+
+ if ( NetStatus == ERROR_BUFFER_OVERFLOW ) {
+
+ //
+ // max_file limit has been exceeded.
+ //
+
+ if ((tree_rec->alerts & MAX_FILES_ALERT) == 0) {
+
+ //
+ // BUGBUG Shouldn't path be reported
+ //
+
+ AlertLogExit( ALERT_ReplMaxFiles,
+ NELOG_ReplMaxFiles,
+ 0,
+ NULL,
+ NULL,
+ NO_EXIT);
+ tree_rec->alerts |= MAX_FILES_ALERT;
+ }
+
+ } else if ( NetStatus == ERROR_NETWORK_ACCESS_DENIED ||
+ NetStatus == ERROR_ACCESS_DENIED ) {
+
+ if ((tree_rec->alerts & ACCESS_DENIED_ALERT) == 0) {
+ AlertLogExit( ALERT_ReplAccessDenied,
+ NELOG_ReplAccessDenied,
+ NetStatus,
+ path,
+ tree_rec->master,
+ NO_EXIT);
+ tree_rec->alerts |= ACCESS_DENIED_ALERT;
+ }
+
+ } else {
+
+ if ((tree_rec->alerts & UPDATE_ERROR_ALERT) == 0) {
+ AlertLogExit( ALERT_ReplUpdateError,
+ NELOG_ReplUpdateError,
+ NetStatus,
+ path,
+ tree_rec->master,
+ NO_EXIT);
+ tree_rec->alerts |= UPDATE_ERROR_ALERT;
+ }
+ }
+
+ return NetStatus;
+}
+
+
+
+DBGSTATIC DWORD
+ReplFileCompare(
+ LPREPL_WIN32_FIND_DATA buf1,
+ LPREPL_WIN32_FIND_DATA buf2
+ )
+/*++
+
+Routine Description:
+
+ Compares the attributes of both entries and returns as follows:
+
+ Only the following attributes are compared (NOTE: assumes filenames are
+ identical).: LastWrittenTime, FileSize, FileAttributes, EA size.
+ These are the same fields used to compute the checksum.
+
+Arguments:
+
+ buf1 - the FIND_DATA structure for the first file.
+
+ buf_2 - the FIND_DATA structure for the second file.
+
+Return Value:
+
+ COPY_FROM_CLIENT: All attributes are the same.
+
+ COPY_FROM_MASTER: At least one attribute is different.
+
+Threads:
+
+ Only called by syncer thread.
+
+--*/
+{
+
+#define PRINT_COMPARE( msgText ) \
+ { \
+ IF_DEBUG( SYNC ) { \
+ NetpKdPrint(( PREFIX_REPL_CLIENT \
+ "ReplFindCompare: comparing " FORMAT_LPTSTR " to " \
+ FORMAT_LPTSTR ": " msgText ".\n", \
+ buf1->fdFound.cFileName, \
+ buf2->fdFound.cFileName )); \
+ } \
+ }
+
+ NetpAssert(STRICMP( buf1->fdFound.cFileName, buf2->fdFound.cFileName)==0);
+
+ // Does file time match (within resolution of file system)?
+ if ( !ReplIsFileTimeCloseEnough( &buf1->fdFound.ftLastWriteTime, &buf2->fdFound.ftLastWriteTime) ) {
+
+ PRINT_COMPARE( "different write times (beyond resolution)" );
+ return COPY_FROM_MASTER;
+ }
+
+ if (buf1->fdFound.nFileSizeHigh != buf2->fdFound.nFileSizeHigh) {
+ PRINT_COMPARE( "different file size (high)" );
+ return COPY_FROM_MASTER;
+ }
+
+ if (buf1->fdFound.nFileSizeLow != buf2->fdFound.nFileSizeLow) {
+ PRINT_COMPARE( "different file size (low)" );
+ return COPY_FROM_MASTER;
+ }
+
+ if (buf1->fdFound.dwFileAttributes != buf2->fdFound.dwFileAttributes) {
+ PRINT_COMPARE( "different attributes" );
+ return COPY_FROM_MASTER;
+ }
+
+ if (buf1->nEaSize != buf2->nEaSize) {
+ PRINT_COMPARE( "different EA sizes" );
+ return COPY_FROM_MASTER;
+ }
+
+ //
+ // BUGBUG: We should compare other times here, like modification ("change")
+ // time and create time. Comparing the access time would probably cause
+ // other problems. Perhaps the change time will reflect an alternate-data-
+ // stream update, which is currently now reflected in the file's write time.
+ // Don't ask me why, I only work here.
+ //
+
+ //
+ // files are identical.
+ //
+
+ PRINT_COMPARE( "identical" );
+ return COPY_FROM_CLIENT;
+
+} // ReplFileCompare
+
+
+
+DBGSTATIC NET_API_STATUS
+ReplTreeIntegritySync(
+ IN OUT PVAR_BUF MasterBuffer,
+ IN OUT PVAR_BUF ClientBuffer,
+ IN LPTSTR master_path,
+ IN LPTSTR client_path OPTIONAL,
+ IN LPTSTR tmp_path,
+ IN PCLIENT_LIST_REC tree_rec
+ )
+/*++
+
+Routine Description:
+
+ Does a complete tree integrity sync for 1 dir, recursively.
+
+ Called by ReplSyncTree and by itself recursively.
+
+Arguments:
+
+ MasterBuffer - Buffer to be used to contain FIND_DATA structures of
+ the master directory.
+
+ ClientBuffer - Buffer to be used to contain FIND_DATA structures of
+ the client directory.
+
+ client_path - UNC to client's dir. This buffer is modified during
+ the execution of this routine but is returned to its original
+ value on completion. This path can be NULL in the case that this
+ subdirectory doesn't currently exist on the client.
+
+ master_path - UNC to master's dir. This buffer is modified during
+ the execution of this routine but is returned to its original
+ value on completion.
+
+ tmp_path - UNC to import\TMPTREE.RP$ temporary tree.
+
+ tree_rec - pointer to dir's CLIENT_LIST_REC.
+
+Return Value:
+
+ NET_API_STATUS.
+
+Threads:
+
+ Only called by syncer thread.
+
+--*/
+{
+ LPREPL_FIND_HANDLE FindHandle = INVALID_REPL_HANDLE;
+ NET_API_STATUS NetStatus;
+
+ DWORD cli_dir_cnt;
+ DWORD mas_dir_cnt;
+ DWORD master_index;
+ DWORD client_index = 0;
+ DWORD tmp_index;
+ DWORD i, j;
+
+ IF_DEBUG( SYNC ) {
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplTreeIntegritySync: master path='" FORMAT_LPTSTR
+ "', client path=" FORMAT_LPTSTR "'.\n", master_path,
+ client_path ? client_path : (LPTSTR) TEXT("<NONE>") ));
+ }
+
+ //
+ // Get the file names of the master and client directories.
+ //
+
+ master_index = STRLEN(master_path);
+ tmp_index = STRLEN(tmp_path);
+ if ( client_path != NULL ) {
+ client_index = STRLEN(client_path);
+ }
+
+ NetStatus = ReplReadSortDir( master_path, &mas_dir_cnt, MasterBuffer, tree_rec);
+ if ( NetStatus != NO_ERROR ) {
+ goto Cleanup;
+ }
+
+ if ( client_path != NULL ) {
+ NetStatus = ReplReadSortDir( client_path,
+ &cli_dir_cnt,
+ ClientBuffer,
+ tree_rec);
+ if ( NetStatus != NO_ERROR ) {
+ goto Cleanup;
+ }
+ } else {
+ client_index = 0;
+ cli_dir_cnt = 0;
+ }
+
+ IF_DEBUG( SYNC ) {
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplTreeIntegritySync: " FORMAT_DWORD
+ " master dirs/files and " FORMAT_DWORD " on client.\n",
+ mas_dir_cnt, cli_dir_cnt ));
+ }
+
+
+ //
+ // see if dirs are empty.
+ //
+
+ if ((mas_dir_cnt == 0) && (cli_dir_cnt == 0)) {
+ NetStatus = NO_ERROR;
+ goto Cleanup; // Make sure we update timestamp for empty dir.
+ }
+
+ //
+ // now the works.
+ //
+
+ i = j = 0;
+
+ while ((i < mas_dir_cnt) || (j < cli_dir_cnt)) {
+ INT State;
+
+ //
+ // Determine if the file/dir exists as master/client/both.
+ //
+
+ if ((i < mas_dir_cnt) && (j < cli_dir_cnt)) {
+ State = STRICMP( MasterBuffer->array[i]->fdFound.cFileName,
+ ClientBuffer->array[j]->fdFound.cFileName );
+
+ if ( State < 0 ) {
+ State = ONLY_AT_MASTER;
+ } else if ( State > 0 ) {
+ State = ONLY_AT_CLIENT;
+ } else {
+ State = AT_BOTH;
+ }
+
+ //
+ // either i or j is exhausted.
+ //
+
+ } else {
+ if (i < mas_dir_cnt) {
+ State = ONLY_AT_MASTER;
+ } else {
+ State = ONLY_AT_CLIENT;
+ }
+ }
+
+
+ //
+ // File not present at Master ==> deleted - so don't copy to temp.
+ //
+
+ switch (State) {
+ case ONLY_AT_CLIENT:
+
+ j++; // advance client index.
+ break;
+
+ //
+ // Files present only at Master ==> added - must copy to tmp dir.
+ //
+
+ case ONLY_AT_MASTER:
+
+
+ //
+ // Don't copy REPL.INI or userlock files.
+ //
+
+ if ( !ReplIgnoreDirOrFileName(
+ MasterBuffer->array[i]->fdFound.cFileName ) ) {
+
+ (void) STRCPY(master_path + master_index, SLASH);
+ (void) STRCPY(master_path + master_index + 1,
+ MasterBuffer->array[i]->fdFound.cFileName);
+
+ (void) STRCPY(tmp_path + tmp_index, SLASH);
+ (void) STRCPY(tmp_path + tmp_index + 1,
+ MasterBuffer->array[i]->fdFound.cFileName);
+
+ NetStatus = ReplSyncCopy( master_path, tmp_path, tree_rec );
+
+ if ( NetStatus != NO_ERROR ) {
+ goto Cleanup;
+ }
+
+ *(master_path + master_index) = '\0';
+ *(tmp_path + tmp_index) = '\0';
+ }
+
+ i++; // advance master index.
+
+ break;
+
+
+ //
+ // File exists at both master and client.
+ //
+
+ case AT_BOTH:
+
+ (void) STRCPY(tmp_path + tmp_index, SLASH);
+ (void) STRCPY(tmp_path + tmp_index + 1, MasterBuffer->array[i]->fdFound.cFileName);
+
+ //
+ // If the file at the master is different than at the client,
+ // copy the file from the master into the temp directory.
+ //
+
+ if(ReplFileCompare(MasterBuffer->array[i],
+ ClientBuffer->array[j])==COPY_FROM_MASTER){
+
+ (void) STRCPY(master_path + master_index, SLASH);
+ (void) STRCPY(master_path + master_index + 1,
+ MasterBuffer->array[i]->fdFound.cFileName);
+
+ NetStatus = ReplSyncCopy( master_path, tmp_path, tree_rec );
+
+ if ( NetStatus != NO_ERROR ) {
+ goto Cleanup;
+ }
+
+ *(master_path + master_index) = '\0';
+ *(tmp_path + tmp_index) = '\0';
+
+ //
+ // If the file at the master is same as that at the client,
+ // copy the file from the client into the temp directory to
+ // avoid network traffic.
+ //
+
+ } else {
+
+ (void) STRCPY(client_path + client_index, SLASH);
+ (void) STRCPY(client_path + client_index + 1,
+ ClientBuffer->array[j]->fdFound.cFileName);
+
+ //
+ // make shure that the client_path does not equal the
+ // tmp_path, if equal, a sharing violation occurs.
+ //
+ if (STRICMP( client_path, tmp_path ))
+ {
+ NetStatus = ReplSyncCopy( client_path,
+ tmp_path,
+ tree_rec );
+
+ if ( NetStatus != NO_ERROR ) {
+ goto Cleanup;
+ }
+ }
+
+ *(client_path + client_index) = '\0';
+ *(tmp_path + tmp_index) = '\0';
+ }
+ i++;
+ j++;
+
+ break;
+
+ default:
+
+ NetpAssert( FALSE ); // invalid state!
+ NetStatus = NERR_InternalError;
+ goto Cleanup;
+
+ } // switch
+
+ //
+ // Quit if service is stopping.
+ //
+ if (ReplGlobalIsServiceStopping) {
+ NetStatus = NO_ERROR;
+ goto Cleanup;
+ }
+
+ } // big while loop
+
+ //
+ // NOTE!!!!
+ //
+ //
+ // This gets complicated in the case of a new dir at master: the dir has already
+ // been created under tmp_path, but does NOT appear under client_path.
+ // Work around:
+ //
+ // Go thru master sub-dirs
+ // if same sub-dir exists under client_path
+ // recurse normally
+ // else
+ // recurse with client_path == tmp_path!!!!
+ //
+
+
+ //
+ // Now we are left with the sub-directories that are present at both sides
+ // we have taken care of the cases where a dir is missing at either side.
+ //
+
+ if (tree_rec->extent == REPL_EXTENT_TREE) {
+
+ REPL_WIN32_FIND_DATA FindData;
+
+ //
+ // Walk the temp tree finding sub-directories to copy from the master.
+ // We don't walk the MasterBuffer (or ClientBuffer) list since we
+ // want to re-use that same buffer for the recursion.
+ //
+
+ (void) STRCPY(tmp_path + tmp_index, SLASH);
+ (void) STRCPY(tmp_path + tmp_index + 1, STAR_DOT_STAR);
+
+ FindHandle = ReplFindFirstFile( tmp_path, &FindData );
+
+ if ( FindHandle != INVALID_REPL_HANDLE ) {
+
+ do {
+ if (FindData.fdFound.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
+
+ //
+ // make sure it's not '.' or '..'.
+ //
+
+ if (STRCMP(FindData.fdFound.cFileName, DOT) == 0) {
+ continue;
+ }
+ if (STRCMP(FindData.fdFound.cFileName, DOT_DOT) == 0) {
+ continue;
+ }
+
+ //
+ // Build the path names.
+ //
+
+ (void) STRCPY(tmp_path + tmp_index + 1, FindData.fdFound.cFileName);
+
+ (void) STRCPY(master_path + master_index, SLASH);
+ (void) STRCPY(master_path + master_index + 1, FindData.fdFound.cFileName);
+
+ if ( client_path != NULL ) {
+ (void) STRCPY(client_path + client_index, SLASH);
+ (void) STRCPY(client_path + client_index + 1,
+ FindData.fdFound.cFileName);
+ }
+
+ //
+ // If the client path exists for this subdirectory,
+ // use it.
+ //
+
+ if ( ReplFileOrDirExists( client_path ) ) {
+
+ //
+ // RECURSIVE CALL HERE.
+ //
+
+ NetStatus = ReplTreeIntegritySync( MasterBuffer,
+ ClientBuffer,
+ master_path,
+ client_path,
+ tmp_path,
+ tree_rec);
+
+ //
+ // If the client path doesn't exist,
+ // just use null.
+ //
+
+ } else {
+
+ //
+ // RECURSIVE CALL HERE.
+ //
+
+ NetStatus = ReplTreeIntegritySync( MasterBuffer,
+ ClientBuffer,
+ master_path,
+ tmp_path, // BUGBUG?
+ tmp_path,
+ tree_rec);
+ }
+
+ //
+ // Quit immediately on any error.
+ //
+
+ if (NetStatus) {
+ goto Cleanup;
+ }
+
+ //
+ // Also quit if service is stopping.
+ //
+ if (ReplGlobalIsServiceStopping) {
+ NetStatus = NO_ERROR;
+ goto Cleanup;
+ }
+ }
+ } while ( ReplFindNextFile( FindHandle, &FindData ));
+
+ }
+
+ } // end TREE extent.
+
+ NetStatus = NO_ERROR;
+
+
+ //
+ // Cleanup
+ //
+
+Cleanup:
+ *(master_path + master_index) = '\0';
+ if ( client_path != NULL ) {
+ *(client_path + client_index) = '\0';
+ }
+ *(tmp_path + tmp_index) = '\0';
+
+ if (FindHandle != INVALID_REPL_HANDLE) {
+ (VOID) ReplFindClose( FindHandle );
+ }
+
+ if ( NetStatus == NO_ERROR ) {
+
+ //
+ // Update attributes, times, ACL, etc.
+ //
+
+ NetStatus = ReplCopyDirectoryItself(
+ master_path, // src
+ tmp_path, // dest (temp tree, which caller will rename)
+ FALSE ); // don't fail if already exists.
+
+ if (NetStatus != NO_ERROR) {
+
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplTreeIntegritySync: got status " FORMAT_API_STATUS
+ " from ReplCopyDirectoryItself\n", NetStatus ));
+ }
+ }
+
+ if (NetStatus != NO_ERROR) {
+
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplTreeIntegritySync: returning status " FORMAT_API_STATUS
+ ".\n", NetStatus ));
+
+ if ((tree_rec->alerts & UPDATE_ERROR_ALERT) == 0) {
+ AlertLogExit( ALERT_ReplUpdateError,
+ NELOG_ReplUpdateError,
+ NetStatus,
+ tree_rec->dir_name,
+ tree_rec->master,
+ NO_EXIT);
+ tree_rec->alerts |= UPDATE_ERROR_ALERT;
+ }
+ }
+
+ return NetStatus;
+
+}
+
+
+
+
+
+DBGSTATIC VOID
+ReplFileIntegritySync(
+ IN OUT PVAR_BUF MasterBuffer,
+ IN OUT PVAR_BUF ClientBuffer,
+ IN LPTSTR master_path,
+ IN LPTSTR client_path,
+ IN LPTSTR tmp_path,
+ IN PCLIENT_LIST_REC tree_rec
+ )
+/*++
+
+Routine Description:
+
+ Does a complete file integrity sync for 1 dir, recursively.
+
+ Called by ReplSyncTree and by itself recursively.
+
+Arguments:
+
+ MasterBuffer - Buffer to be used to contain FIND_DATA structures of
+ the master directory.
+
+ ClientBuffer - Buffer to be used to contain FIND_DATA structures of
+ the client directory.
+
+ client_path - UNC to client's dir. This buffer is modified during
+ the execution of this routine but is returned to its original
+ value on completion.
+
+ master_path - UNC to master's dir. This buffer is modified during
+ the execution of this routine but is returned to its original
+ value on completion.
+
+ tmp_path - UNC to import\TMPFILE.RP$
+
+ tree_rec - pointer to dir's CLIENT_LIST_REC.
+
+Return Value:
+
+ None.
+
+Threads:
+
+ Only called by syncer thread.
+
+--*/
+{
+ NET_API_STATUS ApiStatus;
+ DWORD cli_dir_cnt;
+ LPREPL_FIND_HANDLE FindHandle = INVALID_REPL_HANDLE;
+ DWORD mas_dir_cnt;
+ DWORD master_index;
+ DWORD client_index;
+ DWORD i, j;
+
+
+ IF_DEBUG( MAJOR ) {
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "scanning '" FORMAT_LPTSTR "'...\n", client_path ));
+ }
+
+
+ IF_DEBUG( SYNC ) {
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplFileIntegritySync: master path='" FORMAT_LPTSTR
+ "', client path=" FORMAT_LPTSTR "'.\n", master_path,
+ client_path ));
+ }
+
+ //
+ // Get the file names of the master and client directories.
+ //
+
+ master_index = STRLEN(master_path);
+ client_index = STRLEN(client_path);
+
+ if ( ReplReadSortDir( master_path, &mas_dir_cnt, MasterBuffer, tree_rec) !=
+ NO_ERROR ) {
+ return;
+ }
+
+ if ( ReplReadSortDir( client_path, &cli_dir_cnt, ClientBuffer, tree_rec) !=
+ NO_ERROR ) {
+ return;
+ }
+
+ IF_DEBUG( SYNC ) {
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplFileIntegritySync: " FORMAT_DWORD
+ " master dirs/files and " FORMAT_DWORD " on client.\n",
+ mas_dir_cnt, cli_dir_cnt ));
+ ReplDisplayFileFindArray(
+ (LPTSTR) TEXT("***master***"),
+ (LPVOID) MasterBuffer->buf,
+ mas_dir_cnt );
+ ReplDisplayFileFindArray(
+ (LPTSTR) TEXT("***client***"),
+ (LPVOID) ClientBuffer->buf,
+ cli_dir_cnt );
+
+ }
+
+ //
+ // see if dirs are empty.
+ //
+
+ if ((mas_dir_cnt == 0) && (cli_dir_cnt == 0)) {
+ ApiStatus = NO_ERROR;
+ goto Cleanup; // Make sure we update timestamp for empty dir.
+ }
+
+ //
+ // now the works.
+ //
+
+ i = j = 0;
+
+ while ((i < mas_dir_cnt) || (j < cli_dir_cnt)) {
+ INT State;
+
+ //
+ // Determine if the file/dir exists as master/client/both.
+ //
+
+ if ((i < mas_dir_cnt) && (j < cli_dir_cnt)) {
+ State = STRICMP( MasterBuffer->array[i]->fdFound.cFileName,
+ ClientBuffer->array[j]->fdFound.cFileName );
+
+ if ( State < 0 ) {
+ State = ONLY_AT_MASTER;
+ } else if ( State > 0 ) {
+ State = ONLY_AT_CLIENT;
+ } else {
+ State = AT_BOTH;
+ }
+
+ //
+ // either i or j is exhausted.
+ //
+
+ } else {
+ if (i < mas_dir_cnt) {
+ State = ONLY_AT_MASTER;
+ } else {
+ State = ONLY_AT_CLIENT;
+ }
+ }
+
+
+ //
+ // File not present at Master ==> so delete at client.
+ //
+
+ switch (State) {
+
+ case ONLY_AT_CLIENT: {
+
+ //
+ // Don't delete any signal files.
+ //
+
+ if ( !ReplIgnoreDirOrFileName(
+ ClientBuffer->array[j]->fdFound.cFileName ) ) {
+
+ (void) STRCPY(client_path + client_index, SLASH);
+ (void) STRCPY(client_path + client_index + 1,
+ ClientBuffer->array[j]->fdFound.cFileName);
+
+ IF_DEBUG( SYNC ) {
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplFileIntegritySync: deleting "
+ FORMAT_LPTSTR " (only at client).\n",
+ client_path ));
+ }
+
+ (VOID) ReplFileIntegrityDel(
+ client_path,
+ ClientBuffer->array[j]->fdFound.dwFileAttributes,
+ tree_rec);
+
+ *(client_path + client_index) = '\0';
+ }
+
+ j++; // advance client index.
+
+ break;
+ }
+
+ //
+ // Files present only at Master ==> added - must copy to client.
+ //
+
+ case ONLY_AT_MASTER:
+
+ //
+ // Don't copy REPL.INI or userlock files from master.
+ //
+
+ if ( !ReplIgnoreDirOrFileName(
+ MasterBuffer->array[i]->fdFound.cFileName ) ) {
+
+ (void) STRCPY(master_path + master_index, SLASH);
+ (void) STRCPY(master_path + master_index + 1,
+ MasterBuffer->array[i]->fdFound.cFileName);
+
+ (void) STRCPY(client_path + client_index, SLASH);
+ (void) STRCPY(client_path + client_index + 1,
+ MasterBuffer->array[i]->fdFound.cFileName);
+
+
+ (VOID) ReplSyncCopy( master_path, client_path, tree_rec );
+
+ *(master_path + master_index) = '\0';
+ *(client_path + client_index) = '\0';
+ }
+
+ i++; // advance master index.
+
+ break;
+
+
+
+ //
+ // File exists at both master and client.
+ //
+
+ case AT_BOTH:
+
+ //
+ // Only copy the file if it is actually different at the master.
+ //
+
+ if(ReplFileCompare(MasterBuffer->array[i],
+ ClientBuffer->array[j]) ==COPY_FROM_MASTER){
+
+ DWORD MasterAttributes
+ = MasterBuffer->array[i]->fdFound.dwFileAttributes;
+
+ //
+ // If it is a file, nuke it and copy the new one.
+ //
+ if ( (MasterAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0 ) {
+
+ (void) STRCPY(master_path + master_index, SLASH);
+ (void) STRCPY(master_path + master_index + 1,
+ MasterBuffer->array[i]->fdFound.cFileName);
+
+ (void) STRCPY(client_path + client_index, SLASH);
+ (void) STRCPY(client_path + client_index + 1,
+ MasterBuffer->array[i]->fdFound.cFileName);
+
+ // Copy this file. (Dir will be done below.)
+ (VOID) ReplFileIntegrityCopy(
+ master_path,
+ client_path,
+ tmp_path,
+ tree_rec,
+ MasterAttributes,
+ ClientBuffer->array[j]->fdFound.dwFileAttributes);
+ // Errors ignored? Yes. The checksum later will find
+ // the different, and the error has already been logged.
+
+ *(master_path + master_index) = '\0';
+ *(client_path + client_index) = '\0';
+
+ } else {
+
+ //
+ // Directory timestamp will be fixed below.
+ // EAs, attributes, ACL, etc., are also fixed up below.
+ //
+
+ IF_DEBUG( SYNC ) {
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "MISMATCH(master, entry " FORMAT_DWORD ") '"
+ FORMAT_LPTSTR "':",
+ i,
+ MasterBuffer->array[i]->fdFound.cFileName ));
+ NetpDbgDisplayFileTime(
+ &(MasterBuffer->array[i]->fdFound.ftLastWriteTime));
+ NetpKdPrint(( "\n" ));
+
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "MISMATCH(client, entry " FORMAT_DWORD ") '"
+ FORMAT_LPTSTR "':",
+ j,
+ ClientBuffer->array[j]->fdFound.cFileName ));
+ NetpDbgDisplayFileTime(
+ &(ClientBuffer->array[j]->fdFound.ftLastWriteTime));
+ NetpKdPrint(( "\n" ));
+
+ }
+
+ }
+
+
+ }
+
+ i++;
+ j++;
+
+ break;
+
+ default:
+
+ NetpAssert( FALSE ); // invalid state!
+ ApiStatus = NERR_InternalError;
+ goto Cleanup;
+
+ } // switch
+
+ //
+ // Quit if service is stopping.
+ //
+ if (ReplGlobalIsServiceStopping) {
+ goto Cleanup;
+ }
+
+ } // big while loop
+
+
+ //
+ // Now we are left with the sub-directories that are present at both sides
+ // we have taken care of the cases where a dir is missing at either side.
+ //
+
+ if (tree_rec->extent == REPL_EXTENT_TREE) {
+
+ REPL_WIN32_FIND_DATA FindData;
+
+ //
+ // Walk the client tree finding sub-directories to copy from the master.
+ // We don't walk the MasterBuffer (or ClientBuffer) list since we
+ // want to re-use that same buffer for the recursion.
+ //
+
+ (void) STRCPY(client_path + client_index, SLASH);
+ (void) STRCPY(client_path + client_index + 1, STAR_DOT_STAR);
+
+ FindHandle = ReplFindFirstFile( client_path, &FindData );
+
+ if ( FindHandle != INVALID_REPL_HANDLE ) {
+
+ do {
+ if (FindData.fdFound.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
+
+ //
+ // make sure it's not '.' or '..'.
+ //
+
+ if (STRCMP(FindData.fdFound.cFileName, DOT) == 0) {
+ continue;
+ }
+ if (STRCMP(FindData.fdFound.cFileName, DOT_DOT) == 0) {
+ continue;
+ }
+
+ //
+ // Only recurse if this directory exists at the master.
+ //
+
+ (void) STRCPY(master_path + master_index, SLASH);
+ (void) STRCPY(master_path + master_index + 1, FindData.fdFound.cFileName);
+
+ if ( ReplFileOrDirExists( master_path )) {
+
+ //
+ // RECURSIVE CALL HERE.
+ //
+
+ (void) STRCPY( client_path + client_index + 1,
+ FindData.fdFound.cFileName);
+
+ ReplFileIntegritySync( MasterBuffer,
+ ClientBuffer,
+ master_path,
+ client_path,
+ tmp_path,
+ tree_rec);
+ }
+ }
+
+ //
+ // Also quit if service is stopping.
+ //
+ if (ReplGlobalIsServiceStopping) {
+ goto Cleanup;
+ }
+
+ } while ( ReplFindNextFile( FindHandle, &FindData ));
+
+ }
+
+ } // end TREE extent
+
+Cleanup:
+
+ *(master_path + master_index) = '\0';
+ *(client_path + client_index) = '\0';
+
+ if (FindHandle != INVALID_REPL_HANDLE) {
+ (VOID) ReplFindClose( FindHandle );
+ }
+
+ //
+ // Update times, ACL, etc.
+ //
+
+ ApiStatus = ReplCopyDirectoryItself(
+ master_path, // src
+ client_path, // dest
+ FALSE ); // don't fail if already exists.
+
+ if (ApiStatus != NO_ERROR) {
+
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplFileIntegritySync: got status " FORMAT_API_STATUS
+ " from ReplCopyDirectoryItself\n", ApiStatus ));
+
+ if ((tree_rec->alerts & UPDATE_ERROR_ALERT) == 0) {
+ AlertLogExit( ALERT_ReplUpdateError,
+ NELOG_ReplUpdateError,
+ ApiStatus,
+ tree_rec->dir_name,
+ tree_rec->master,
+ NO_EXIT);
+ tree_rec->alerts |= UPDATE_ERROR_ALERT;
+ }
+ }
+ return;
+
+}
+
+
+
+VOID
+ReplSyncTree(
+ IN PMSG_STATUS_REC upd_rec,
+ IN OUT PCLIENT_LIST_REC tree_rec
+ )
+/*++
+
+Routine Description:
+
+ ReplSyncTree will sync the specified directory, do another checksum,
+ and update state and checksum in client record.
+
+ Assumes that caller has a lock (any kind) on RCGlobalClientListLock.
+
+Arguments:
+
+ upd_rec - Specifies the desired checksum and file count from the master.
+
+ tree_rec - points to client_rec struct for updated dir/tree.
+
+Return Value:
+
+ None.
+
+Threads:
+
+ Only called by syncer thread.
+
+--*/
+{
+ NET_API_STATUS ApiStatus;
+ BOOL ExportLocked;
+ TCHAR UncMasterName[UNCLEN+1];
+
+ TCHAR master_path[MAX_PATH];
+ TCHAR client_path[MAX_PATH];
+ TCHAR tmp_path[MAX_PATH];
+
+ VAR_BUF MasterBuffer, ClientBuffer;
+ DWORD import_index;
+
+ CHECKSUM_REC ck_rec;
+
+#define WARN_ABOUT_DELETE( fileName ) \
+ { \
+ IF_DEBUG( SYNC ) { \
+ NetpKdPrint(( PREFIX_REPL_CLIENT \
+ "ReplSyncTree: ***DELETING*** " FORMAT_LPTSTR ".\n", \
+ fileName )); \
+ } \
+ }
+#define WARN_ABOUT_MOVEFILE( oldName, newName ) \
+ { \
+ IF_DEBUG( MAJOR ) { \
+ NetpKdPrint(( PREFIX_REPL_CLIENT \
+ "ReplSyncTree: ***MOVEFILE*** " FORMAT_LPTSTR " to " \
+ FORMAT_LPTSTR ".\n", oldName, newName )); \
+ } \
+ }
+
+ NetpAssert( tree_rec != NULL );
+ NetpAssert( upd_rec != NULL );
+
+ //
+ // form master's path UNC.
+ //
+
+ (void) STRCPY(master_path, SLASH_SLASH);
+ (void) STRCAT(master_path, tree_rec->master);
+ (void) STRCAT(master_path, SLASH);
+ (void) STRCAT(master_path, REPL_SHARE);
+ (void) STRCAT(master_path, SLASH);
+ (void) STRCAT(master_path, tree_rec->dir_name);
+
+ //
+ // form client's path UNC.
+ //
+
+ ACQUIRE_LOCK_SHARED( ReplConfigLock );
+ import_index = STRLEN(ReplConfigImportPath);
+ (void) STRCPY(client_path, ReplConfigImportPath);
+ (void) STRCAT(client_path, SLASH);
+ (void) STRCAT(client_path, tree_rec->dir_name);
+ RELEASE_LOCK( ReplConfigLock );
+
+ //
+ // Form master's server name UNC.
+ //
+ (VOID) STRCPY( UncMasterName, SLASH_SLASH );
+ (VOID) STRCAT( UncMasterName, tree_rec->master );
+ NetpAssert( NetpIsUncComputerNameValid( UncMasterName ) );
+
+ IF_DEBUG( SYNC ) {
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplSyncTree: processing opcode " FORMAT_DWORD
+ " for master '" FORMAT_LPTSTR "'"
+ ", dir '" FORMAT_LPTSTR "',\n"
+ " master path '" FORMAT_LPTSTR "', client_path '"
+ FORMAT_LPTSTR "'.\n",
+ upd_rec->opcode,
+ UncMasterName,
+ tree_rec->dir_name, master_path, client_path ));
+ }
+
+ //
+ // Allocate 2 buffers to compare 2 dirs.
+ //
+
+ if ( ReplAllocBuf(tree_rec, &MasterBuffer, ALLOC_BUF) != NO_ERROR ) {
+ return;
+ }
+ if ( ReplAllocBuf(tree_rec, &ClientBuffer, ALLOC_BUF) != NO_ERROR ) {
+ (VOID) ReplAllocBuf(tree_rec, &MasterBuffer, FREE_BUF);
+ return;
+ }
+
+ //
+ // Handle Tree Integrity
+ //
+
+ if (tree_rec->integrity == REPL_INTEGRITY_TREE) {
+
+ //
+ // Form tmp path UNC.
+ //
+
+ ACQUIRE_LOCK_SHARED( ReplConfigLock );
+ (void) STRCPY(tmp_path, ReplConfigImportPath);
+ (void) STRCAT(tmp_path, SLASH);
+ (void) STRCAT(tmp_path, TMP_TREE);
+ RELEASE_LOCK( ReplConfigLock );
+
+
+ //
+ // If the user has locked us from replication,
+ // don't sync now.
+ //
+
+ if (tree_rec->lockcount > 0) {
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplSyncTree: setting no sync"
+ " (tree integrity: import locked)\n" ));
+
+ // Note: ReplSetSignalFile needs lock (any kind) on
+ // RCGlobalClientListLock.
+ ReplSetSignalFile(tree_rec, REPL_STATE_NO_SYNC);
+ goto Cleanup;
+ }
+
+ ApiStatus = ReplCheckExportLocks(
+ UncMasterName, // server name to check locks on
+ (LPCTSTR) tree_rec->dir_name,
+ &ExportLocked );
+ if ( ExportLocked || (ApiStatus!=NO_ERROR) ) {
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplSyncTree: setting no sync"
+ " (tree integrity: export locked or down)\n" ));
+
+ // Note: ReplSetSignalFile needs lock (any kind) on
+ // RCGlobalClientListLock.
+ ReplSetSignalFile(tree_rec, REPL_STATE_NO_SYNC);
+ goto Cleanup;
+ }
+
+ //
+ // If we can't create the temporary directory,
+ // don't sync now.
+ //
+
+ if (ReplCreateTempDir(tmp_path, tree_rec)) {
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplSyncTree: setting no sync"
+ " (tree integrity: can't create temp dir)\n" ));
+
+ // Note: ReplSetSignalFile needs lock (any kind) on
+ // RCGlobalClientListLock.
+ ReplSetSignalFile(tree_rec, REPL_STATE_NO_SYNC);
+ goto Cleanup;
+ }
+
+ //
+ // Sync master tree into temp tree.
+ //
+
+ ApiStatus = ReplTreeIntegritySync( &MasterBuffer,
+ &ClientBuffer,
+ master_path,
+ client_path,
+ tmp_path,
+ tree_rec);
+
+
+ if ( ApiStatus != NO_ERROR ) {
+
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplSyncTree: setting no sync"
+ " (tree integrity: sync failed)\n" ));
+
+ // Note: ReplSetSignalFile needs lock (any kind) on
+ // RCGlobalClientListLock.
+ ReplSetSignalFile(tree_rec, REPL_STATE_NO_SYNC);
+ goto CLEANUP_1;
+ }
+
+
+
+ //
+ // If the new directory doesn't match the master copy,
+ // try again after the guard time has elapsed.
+ //
+ // Notice that the master only sends us a pulse for a guarded
+ // tree integrity directory if and only if it thinks the tree
+ // has been stable for the guard time. Therefore, if we're
+ // able to duplicate a tree that the master has told us to
+ // duplicate, we know a priori that the guard time has already been
+ // statisfied.
+ //
+
+ if (!ChecksumEqual(UncMasterName, tmp_path, upd_rec, &ck_rec)) {
+
+ //
+ // If there is no guard time,
+ // don't do anything here.
+ //
+
+ if (tree_rec->guard_time != 0) {
+
+ //
+ // If this is the guard check itself,
+ // don't both doing another guard check.
+ //
+
+ if (tree_rec->timer.grd_count != (DWORD) -1) {
+ tree_rec->timer.grd_count = (DWORD) -1;
+
+ //
+ // If this call was the result of the initial pulse from
+ // the master, set up a guard request to try again later.
+ //
+
+ } else {
+ tree_rec->timer.grd_checksum = ck_rec.checksum;
+ tree_rec->timer.grd_count = ck_rec.count;
+
+ // Note: ReplSetTimeOut needs shared RCGlobalClientListLock.
+ ReplSetTimeOut(GUARD_TIMEOUT, tree_rec);
+ }
+ }
+
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplSyncTree: setting no sync"
+ " (tree integrity: checksum mismatch)\n" ));
+
+ // Note: ReplSetSignalFile needs lock (any kind) on
+ // RCGlobalClientListLock.
+ ReplSetSignalFile(tree_rec, REPL_STATE_NO_SYNC);
+ goto CLEANUP_1;
+ }
+
+ //
+ // On success, always clear the guard request.
+ //
+
+ tree_rec->timer.grd_count = (DWORD) -1;
+
+ //
+ // Check for user locks. If none...
+ // Create our own lock, so we can lock out apps.
+ // ReplCreateReplLock will also put dir name in CrashDir in the
+ // registry, so we can recover if we die while copying this stuff.
+ //
+
+ // Note: ReplCreateReplLock any lock on RCGlobalClientListLock.
+ ApiStatus = ReplCreateReplLock( tree_rec );
+ if (ApiStatus != NO_ERROR) {
+
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplSyncTree: setting no sync"
+ " (tree integrity: ReplCreateReplLock failed).\n" ));
+
+ // Note: ReplSetSignalFile needs lock (any kind) on
+ // RCGlobalClientListLock.
+ ReplSetSignalFile(tree_rec, REPL_STATE_NO_SYNC);
+ goto CLEANUP_1;
+ }
+
+ //
+ // Check for any open files.
+ //
+
+ if ( ReplAnyRemoteFilesOpen(client_path)) {
+
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplSyncTree: setting no sync"
+ " (tree integrity: remote files open)\n" ));
+
+ // Note: ReplSetSignalFile needs lock (any kind) on
+ // RCGlobalClientListLock.
+ ReplSetSignalFile(tree_rec, REPL_STATE_NO_SYNC);
+ goto CLEANUP_2;
+ }
+
+ //
+ // rename old client's dir to TMPTREEX.RP$.
+ //
+
+ (void) STRCPY(tmp_path + import_index + 1, TMP_TREEX);
+
+ WARN_ABOUT_MOVEFILE( client_path, tmp_path );
+ if ( !MoveFile( client_path, tmp_path ) ) {
+
+ ApiStatus = GetLastError();
+
+ //
+ // Might fail if it is current dir for some user process.
+ //
+
+ if (ApiStatus == ERROR_SHARING_VIOLATION) {
+ if ((tree_rec->alerts & USER_CURDIR_ALERT) == 0) {
+ AlertLogExit( ALERT_ReplUserCurDir,
+ NELOG_ReplUserCurDir,
+ 0,
+ client_path,
+ NULL,
+ NO_EXIT);
+
+ tree_rec->alerts |= USER_CURDIR_ALERT;
+ }
+ }
+
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplSyncTree: setting no sync"
+ " (tree integrity: "
+ "move file to TMPTREEX.RP$ failed)\n" ));
+
+ // Note: ReplSetSignalFile needs lock (any kind) on
+ // RCGlobalClientListLock.
+ ReplSetSignalFile(tree_rec, REPL_STATE_NO_SYNC);
+ goto CLEANUP_3;
+ }
+
+ //
+ // Rename new synced TMPTREE.RP$ dir to client's dir.
+ //
+
+ (void) STRCPY(tmp_path + import_index + 1, TMP_TREE);
+
+ WARN_ABOUT_MOVEFILE( tmp_path, client_path );
+ if ( !MoveFile( tmp_path, client_path )) {
+
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplSyncTree: setting no sync"
+ " (tree integrity: "
+ "move file TMPTREE.RP$ to destination failed)\n" ));
+
+ // Note: ReplSetSignalFile needs lock (any kind) on
+ // RCGlobalClientListLock.
+ ReplSetSignalFile(tree_rec, REPL_STATE_NO_SYNC);
+ goto CLEANUP_3;
+ }
+
+ //
+ // That's it sync done - now update all status variables.
+ //
+
+ tree_rec->checksum = ck_rec.checksum;
+ tree_rec->count = ck_rec.count;
+ tree_rec->timer.grd_timeout = 0;
+ tree_rec->timestamp = NetpReplTimeNow();
+ tree_rec->alerts = 0;
+
+ // Note: ReplSetTimeOut and ReplSetSignalFile need lock (any kind) on
+ // RCGlobalClientListLock.
+ ReplSetTimeOut(PULSE_1_TIMEOUT, tree_rec);
+
+ IF_DEBUG( SYNC ) {
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplSyncTree: setting state OK (tree integrity).\n" ));
+ }
+
+ ReplSetSignalFile(tree_rec, REPL_STATE_OK);
+
+ AlertLogExit( ALERT_ReplDataChanged,
+ 0,
+ 0,
+ tree_rec->dir_name,
+ NULL,
+ NO_EXIT);
+
+ //
+ // Delete the old client tree.
+ //
+
+ (void) STRCPY(tmp_path + import_index + 1, TMP_TREEX);
+ (VOID) ReplTreeDelete(tmp_path);
+ goto CLEANUP_2;
+
+
+ //
+ // Cleanup to restore the old client tree and the correct tree.
+ //
+
+CLEANUP_3:
+ (void) STRCPY(tmp_path + import_index + 1, TMP_TREEX);
+ WARN_ABOUT_MOVEFILE( tmp_path + import_index + 1, TMP_TREEX );
+ (VOID) MoveFile(tmp_path, client_path);
+
+ //
+ // Cleanup to remove the lock (if any).
+ //
+
+CLEANUP_2:
+
+ //
+ // Undo client list update.
+ //
+ NetpAssert( tree_rec->lockcount == 1 );
+ ApiStatus = ReplDecrLockFields(
+ & (tree_rec->lockcount),
+ & (tree_rec->time_of_first_lock),
+ REPL_UNLOCK_NOFORCE );
+ NetpAssert( ApiStatus == NO_ERROR );
+ NetpAssert( tree_rec->lockcount == 0 );
+
+ //
+ // Update registry import entry (decr lock count).
+ //
+ ApiStatus = ImportDirUnlockInRegistry(
+ NULL, // no server name.
+ tree_rec->dir_name,
+ REPL_UNLOCK_NOFORCE );
+ NetpAssert( ApiStatus == NO_ERROR );
+
+ //
+ // Delete CrashDir from registry.
+ //
+ ApiStatus = ReplDeleteCrashDirRecord();
+ NetpAssert( ApiStatus == NO_ERROR );
+
+ //
+ // Just delete the new temporary tree (if any).
+ //
+
+CLEANUP_1:
+ (void) STRCPY( tmp_path + import_index, SLASH);
+ (void) STRCPY( tmp_path + import_index + 1, TMP_TREE);
+ if (ReplFileOrDirExists(tmp_path)) { // Avoid warning if not needed.
+ (VOID) ReplTreeDelete(tmp_path);
+ }
+
+ //
+ // Handle file integrity.
+ //
+
+ } else {
+
+ //
+ // Form tmp file UNC.
+ //
+
+ ACQUIRE_LOCK_SHARED( ReplConfigLock );
+ (void) STRCPY(tmp_path, ReplConfigImportPath);
+ (void) STRCAT(tmp_path, SLASH);
+ (void) STRCAT(tmp_path, TMP_FILE);
+ RELEASE_LOCK( ReplConfigLock );
+
+ //
+ // If the user has locked us from replication,
+ // don't sync now.
+ //
+
+ if (tree_rec->lockcount > 0) {
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplSyncTree: setting no sync"
+ " (file integrity: import locked)\n" ));
+
+ // Note: ReplSetSignalFile needs lock (any kind) on
+ // RCGlobalClientListLock.
+ ReplSetSignalFile(tree_rec, REPL_STATE_NO_SYNC);
+ goto Cleanup;
+ }
+
+ ApiStatus = ReplCheckExportLocks(
+ UncMasterName, // server name to check locks on
+ (LPCTSTR) tree_rec->dir_name,
+ &ExportLocked );
+ if ( ExportLocked || (ApiStatus!=NO_ERROR) ) {
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplSyncTree: setting no sync"
+ " (file integrity: export locked or down)\n" ));
+
+ // Note: ReplSetSignalFile needs lock (any kind) on
+ // RCGlobalClientListLock.
+ ReplSetSignalFile(tree_rec, REPL_STATE_NO_SYNC);
+ goto Cleanup;
+ }
+
+
+ //
+ //
+ // Sync master tree into Client tree.
+ //
+
+ ReplFileIntegritySync( &MasterBuffer,
+ &ClientBuffer,
+ master_path,
+ client_path,
+ tmp_path,
+ tree_rec);
+
+ //
+ // Note we could do just a partial update, so in any case we need
+ // to update dir's checksum and count to actual value. If it was
+ // not complete - I.E ChecksumEqual will fail we do not update
+ // any other status variables because we know we are in some way
+ // out of date
+ //
+
+
+ //
+ // If our checksum doesn't match the master's,
+ // just remember our checksum,
+ // and mark the directory as needing syncing.
+ //
+
+ if (!ChecksumEqual(UncMasterName, client_path, upd_rec, &ck_rec)) {
+ tree_rec->checksum = ck_rec.checksum;
+ tree_rec->count = ck_rec.count;
+
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplSyncTree: setting no sync"
+ " (file integrity: checksum mismatch).\n" ));
+
+ // Note: ReplSetSignalFile needs lock (any kind) on
+ // RCGlobalClientListLock.
+ ReplSetSignalFile(tree_rec, REPL_STATE_NO_SYNC);
+
+ //
+ // If our checksum does match the master,
+ // Indicate so everywhere.
+ //
+
+ } else {
+
+ tree_rec->checksum = ck_rec.checksum;
+ tree_rec->count = ck_rec.count;
+ tree_rec->timer.grd_timeout = 0;
+ tree_rec->timestamp = NetpReplTimeNow();
+ tree_rec->alerts = 0;
+
+ IF_DEBUG( SYNC ) {
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplSyncTree: setting state OK (file integrity).\n" ));
+ }
+
+ // Note: ReplSetTimeOut and ReplSetSignalFile need lock (any kind)
+ // on RCGlobalClientListLock.
+ ReplSetTimeOut(PULSE_1_TIMEOUT, tree_rec);
+
+ ReplSetSignalFile(tree_rec, REPL_STATE_OK);
+
+ AlertLogExit( ALERT_ReplDataChanged,
+ 0,
+ 0,
+ tree_rec->dir_name,
+ NULL,
+ NO_EXIT);
+
+ }
+
+ //
+ // Delete our temporary file.
+ //
+
+ WARN_ABOUT_DELETE( tmp_path );
+ (VOID) ReplDeleteFile( tmp_path );
+ }
+
+ //
+ // Free the two allocated buffers
+ //
+
+Cleanup:
+ (VOID) ReplAllocBuf(tree_rec, &MasterBuffer, FREE_BUF);
+ (VOID) ReplAllocBuf(tree_rec, &ClientBuffer, FREE_BUF);
+
+ return;
+
+
+}
+
+
+
+
+BOOL
+ChecksumEqual(
+ IN LPCTSTR UncMasterName,
+ IN LPTSTR tmp_path,
+ IN PMSG_STATUS_REC upd_rec,
+ OUT PCHECKSUM_REC check_rec
+ )
+/*++
+
+Routine Description:
+
+ Compute the checksum on the tree / dir (according to extent) under
+ tmp_path and compare to those stored in upd_rec.
+
+Arguments:
+
+ tmp_path - Path to combine the checksum on.
+
+ upd_rec - the desired checksum to compare with.
+
+ check_rec - Returns the computed checksum.
+
+Return Value:
+
+ TRUE: The checksum for tmp_path and upd_rec are the same.
+ FALSE: Otherwise
+
+Threads:
+
+ Only called by syncer thread.
+
+--*/
+{
+ NET_API_STATUS ApiStatus;
+ LONG MasterTimeZoneOffsetSecs; // exporter offset from GMT
+
+ IF_DEBUG( MAJOR ) {
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "Doing checksum for tree '" FORMAT_LPTSTR "', master '"
+ FORMAT_LPTSTR "'\n",
+ tmp_path, UncMasterName ));
+ }
+
+ ApiStatus = ReplGetTimeCacheValue(
+ UncMasterName,
+ &MasterTimeZoneOffsetSecs ); // offset (+ for West of GMT, etc).
+ if (ApiStatus != NO_ERROR) {
+
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "CompareChecksum FAILED CALLING ReplGetTimeCacheValue, "
+ "status is " FORMAT_API_STATUS ".\n",
+ ApiStatus ));
+
+ AlertLogExit(
+ ALERT_ReplSysErr,
+ NELOG_ReplSysErr,
+ ApiStatus,
+ NULL,
+ NULL,
+ NO_EXIT);
+
+ return (FALSE); // closest we can get to telling caller.
+ }
+
+ //
+ // Compute the checksum for the passed-in tree.
+ //
+
+ if (upd_rec->extent == REPL_EXTENT_FILE) {
+ ScanDir(MasterTimeZoneOffsetSecs, tmp_path, check_rec, 0);
+ } else {
+ ScanTree(MasterTimeZoneOffsetSecs, tmp_path, check_rec);
+ }
+
+ //
+ // see if scan tree completed ok.
+ //
+
+ if (check_rec->count == (DWORD)-1) {
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ChecksumEqual: ERROR! COUNT IS -1!!!\n" ));
+ // BUGBUG: log this?
+ return FALSE;
+ }
+
+
+ //
+ // see if checksum and count are same as those in update.
+ //
+
+ if ((upd_rec->checksum == check_rec->checksum)
+ && (((DWORD)upd_rec->count) == check_rec->count)) {
+ return TRUE;
+
+ } else {
+ IF_DEBUG( SYNC ) {
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ChecksumEqual: DID NOT MATCH...\n" ));
+#define SHOW_ONE( name, rec_ptr ) \
+ NetpKdPrint(( \
+ " " name " checksum=" FORMAT_CHECKSUM ", count=" \
+ FORMAT_DWORD ",\n", rec_ptr->checksum, rec_ptr->count ))
+
+ SHOW_ONE( "master", upd_rec );
+ SHOW_ONE( "client actual", check_rec );
+ }
+
+ return FALSE;
+ }
+}
+
+
+
+
+
+NET_API_STATUS
+ReplCrashRecovery(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ Checks for any crashed dir - i.e. crashed while syncing it.
+
+ if (dir TMPTREE.RP$ exists) and (dir TMPTREEX.RP$ exists)
+ assume sync was complete and all that was missing was to copy it
+ TMPTREEX.RP$ dir back into client's dir under IMPORT. Retrieves
+ the client's dir name from the CrashDir field in the registry and
+ copies the dir there.
+
+ otherwise if either TMP dirs exist simply removes them.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NET_API_STATUS
+
+Threads:
+
+ Only called by client thread.
+
+--*/
+{
+ NET_API_STATUS ApiStatus;
+ LPNET_CONFIG_HANDLE ConfigHandle = NULL;
+ LPTSTR CrashDir = NULL;
+
+ TCHAR client_path[MAX_PATH];
+ TCHAR tmp_path[MAX_PATH];
+
+ BOOL tmptree_exists = FALSE;
+ BOOL tmptreex_exists = FALSE;
+ DWORD tmp_index;
+#if DBG
+ DWORD tmptreex_index;
+#endif
+ DWORD Attributes;
+
+
+ //
+ // Determine if TMPTREE exists.
+ //
+
+ ACQUIRE_LOCK_SHARED( ReplConfigLock );
+ (void) STRCPY(tmp_path, ReplConfigImportPath);
+ RELEASE_LOCK( ReplConfigLock );
+
+ tmp_index = STRLEN(tmp_path);
+ (void) STRCPY(tmp_path + tmp_index++, SLASH);
+ (void) STRCPY(tmp_path + tmp_index, TMP_TREE);
+
+ Attributes = GetFileAttributes( tmp_path );
+
+ if ( Attributes != (DWORD) -1 && (Attributes & FILE_ATTRIBUTE_DIRECTORY) ) {
+ tmptree_exists = TRUE;
+ }
+
+
+ //
+ // Determine if TMPTREEX exists.
+ //
+
+ (void) STRCPY(tmp_path + tmp_index, TMP_TREEX);
+#if DBG
+ tmptreex_index = STRLEN(tmp_path);
+#endif
+
+ Attributes = GetFileAttributes( tmp_path );
+
+ if ( Attributes != (DWORD) -1 && (Attributes & FILE_ATTRIBUTE_DIRECTORY) ) {
+ tmptreex_exists = TRUE;
+ }
+
+
+ //
+ // If both TMPTREE.RP$ and TMPTREEX.RP$ exists,
+ // assume sync was complete and all that was missing was to copy it
+ // TMPTREE.RP$ dir back into client's dir under IMPORT. Retrieves
+ // the client's dir name from the CrashDir field in the registry and
+ // copies the dir there. Deletes CrashDir field so we don't get confused.
+ //
+
+ if ( tmptree_exists && tmptreex_exists ) {
+
+ //
+ // Open the right section of the config file/whatever.
+ //
+ ApiStatus = NetpOpenConfigData(
+ & ConfigHandle,
+ NULL, // local (no server name)
+ (LPTSTR) SECT_NT_REPLICATOR,
+ TRUE); // read-only.
+ if (ApiStatus != NO_ERROR) {
+ // BUGBUG: Alert/event for this!
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplCrashRecovery: UNEXPECTED ERROR " FORMAT_API_STATUS
+ " from NetpOpenConfigData.\n", ApiStatus ));
+ goto Cleanup;
+ }
+
+ //
+ // Read the pathname from the registry.
+ // Assume the pathname is correct.
+ //
+
+ ApiStatus = NetpGetConfigValue(
+ ConfigHandle,
+ (LPTSTR) REPL_KEYWORD_CRASHDIR,
+ & CrashDir ); // alloc and set pointer.
+ if (ApiStatus != NO_ERROR) {
+ // BUGBUG: Alert/event for this!
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplCrashRecovery: UNEXPECTED ERROR " FORMAT_API_STATUS
+ " from NetpGetConfigValue.\n", ApiStatus ));
+ goto Cleanup;
+ }
+ NetpAssert( CrashDir != NULL );
+
+ //
+ // Make sure CrashDir syntax is valid.
+ //
+ if ( !ReplIsDirNameValid( CrashDir ) ) {
+ // BUGBUG: Alert/event for this!
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplCrashRecovery: BAD CRASH DIR '" FORMAT_LPTSTR
+ "' in registry.\n", CrashDir ));
+ ApiStatus = ERROR_INVALID_DATA;
+ goto Cleanup;
+ }
+
+
+
+ //
+ // tmp path should be just tmptreex.
+ //
+
+#if DBG
+ NetpAssert( *(tmp_path + tmptreex_index - 1) == TCHAR_EOS );
+#endif
+
+ //
+ // Build path name of final client tree.
+ //
+ ACQUIRE_LOCK_SHARED( ReplConfigLock );
+ (VOID) STRCPY( client_path, ReplConfigImportPath );
+ RELEASE_LOCK( ReplConfigLock );
+ (VOID) STRCAT( client_path, SLASH );
+ (VOID) STRCAT( client_path, CrashDir );
+
+ //
+ // Fail if the original name already exists.
+ //
+ if (ReplFileOrDirExists( client_path ) ) {
+ ApiStatus = NERR_InternalError; // BUGBUG: better error code?
+ // BUGBUG: Alert/event for this!
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplCrashRecovery: UNEXPECTED ERROR: '" FORMAT_LPTSTR
+ " exists but should not!\n", client_path ));
+ goto Cleanup;
+ }
+
+ //
+ // Copy the TMPTREEX directory to the original name.
+ //
+
+
+ ApiStatus = ReplCopyTree( tmp_path, client_path );
+ if (ApiStatus != NO_ERROR) {
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplCrashRecovery: UNEXPECTED ERROR " FORMAT_API_STATUS
+ " from ReplCopyTree.\n", ApiStatus ));
+ goto Cleanup;
+ }
+
+ //
+ // Decrement lock count for client dir.
+ //
+ ApiStatus = ImportDirUnlockInRegistry(
+ NULL,
+ CrashDir,
+ REPL_UNLOCK_NOFORCE );
+ if (ApiStatus != NO_ERROR) {
+ goto Cleanup;
+ }
+
+ }
+ ApiStatus = NO_ERROR;
+
+Cleanup:
+
+ //
+ // Delete CrashDir so we don't do this again next time.
+ //
+ (VOID) ReplDeleteCrashDirRecord();
+
+ //
+ // Clean up the temp directories.
+ //
+
+ if (tmptreex_exists) {
+ (void) STRCPY(tmp_path + tmp_index, TMP_TREEX);
+ (VOID) ReplTreeDelete(tmp_path);
+ }
+
+ if (tmptree_exists) {
+ (void) STRCPY(tmp_path + tmp_index, TMP_TREE);
+ (VOID) ReplTreeDelete(tmp_path);
+ }
+
+ if (ApiStatus != NO_ERROR) {
+ ReplFinish( ApiStatus );
+ }
+ if (CrashDir != NULL) {
+ (VOID) NetApiBufferFree( CrashDir );
+ }
+ if (ConfigHandle != NULL) {
+ (VOID) NetpCloseConfigData( ConfigHandle );
+ }
+
+ return (ApiStatus);
+
+} // ReplCrashRecovery
+
+
+
+NET_API_STATUS
+ReplDeleteCrashDirRecord(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ Deletes registry entry for CrashDir.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NET_API_STATUS
+
+Threads:
+
+ Only called by client thread.
+
+--*/
+{
+ NET_API_STATUS ApiStatus;
+ LPNET_CONFIG_HANDLE ConfigHandle = NULL;
+
+ //
+ // Open the right section of the config file/whatever.
+ //
+ ApiStatus = NetpOpenConfigData(
+ & ConfigHandle,
+ NULL, // local (no server name)
+ (LPTSTR) SECT_NT_REPLICATOR,
+ FALSE); // not read-only
+ if (ApiStatus != NO_ERROR) {
+ // BUGBUG: Alert/event for this!
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplDeleteCrashDirRecord: UNEXPECTED ERROR " FORMAT_API_STATUS
+ " from NetpOpenConfigData.\n", ApiStatus ));
+ goto Cleanup;
+ }
+
+ ApiStatus = NetpDeleteConfigKeyword(
+ ConfigHandle,
+ (LPTSTR) REPL_KEYWORD_CRASHDIR );
+
+Cleanup:
+
+ if (ConfigHandle != NULL) {
+ (VOID) NetpCloseConfigData( ConfigHandle );
+ }
+
+ return (ApiStatus);
+
+} // ReplDeleteCrashDirRecord
diff --git a/private/net/svcdlls/repl/server/watchd.c b/private/net/svcdlls/repl/server/watchd.c
new file mode 100644
index 000000000..5e0d22d55
--- /dev/null
+++ b/private/net/svcdlls/repl/server/watchd.c
@@ -0,0 +1,461 @@
+/*++
+
+Copyright (c) 1987-1993 Microsoft Corporation
+
+Module Name:
+
+ watchd.c
+
+Abstract:
+
+ Contains watchdog thread - does all timing work for REPL client.
+
+Author:
+
+ Ported from Lan Man 2.1
+
+Environment:
+
+ User mode only.
+ Contains NT-specific code.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+ 11-Apr-1989 (yuv)
+ Initial Coding.
+ 21-Oct-1991 (cliffv)
+ Ported to NT. Converted to NT style.
+ 11-Dec-1991 JohnRo
+ Changed to allow MIPS builds.
+ 16-Jan-1992 JohnRo
+ Changed file name from repl.h to replgbl.h to avoid MIDL conflict.
+ Added debug print of thread ID.
+ Changed to use NetLock.h (allow shared locks, for one thing).
+ 24-Jan-1992 JohnRo
+ Changed to use LPTSTR etc.
+ 10-Feb-1992 JohnRo
+ Set up to dynamically change role.
+ Use FORMAT equates.
+ 15-Feb-1992 JohnRo
+ Added debug message when thread ends.
+ 24-Mar-1992 JohnRo
+ Added comments about which thread(s) call various routines.
+ 19-Aug-1992 JohnRo
+ RAID 2115: repl svc should wait while stopping or changing role.
+ Use PREFIX_ equates.
+ 24-Mar-1993 JohnRo
+ RAID 4267: Replicator has problems when work queue gets large.
+ Always do debug output if unexpected value from WaitForSingleObject.
+ Made some changes suggested by PC-LINT 5.0
+ 31-Mar-1993 JohnRo
+ Repl watchdog should scan queues too.
+ Minor debug output changes.
+
+--*/
+
+#include <windows.h>
+#include <lmcons.h>
+
+#include <alertmsg.h> // ALERT_* defines
+#include <lmerrlog.h> // NELOG_* defines
+#include <netdebug.h> // DBGSTATIC ..
+#include <netlock.h> // Lock data types, functions, and macros.
+#include <prefix.h> // PREFIX_ equates.
+#include <thread.h> // FORMAT_NET_THREAD_ID, NetpCurrentThread().
+#include <tstr.h> // STRCPY(), etc.
+
+//
+// Local include files
+//
+#include <client.h> // ReplWatchdThread().
+#include <repldefs.h> // IF_DEBUG().
+#include <replgbl.h> // ReplGlobal variables.
+
+
+
+DBGSTATIC VOID
+ReplQuePut(
+ IN DWORD timeout_type,
+ IN LPTSTR master,
+ IN LPTSTR dir_name
+ )
+/*++
+
+Routine Description:
+
+ Puts a timeout message onto work_que for syncer to act upon.
+
+Arguments:
+
+ timeout_type - The type of this timeout. One of the *_TIMEOUT defines
+ from repldefs.h.
+
+ master - master's name.
+
+ dir_name - dir's name.
+
+Return Value:
+
+ None.
+
+Threads:
+
+ Only called by watchd thread.
+
+--*/
+{
+ PSMLBUF_REC que_buf;
+ PQUERY_MSG msg_buf;
+
+ //
+ // Allocate a small queue entry.
+ // Ignore errors, they are reported by the called function.
+ //
+
+ que_buf = ReplClientGetPoolEntry( QUESML_POOL );
+
+ if ( que_buf == NULL) {
+ return;
+ }
+
+
+ //
+ // Build the message.
+ //
+
+ msg_buf = (PQUERY_MSG )(que_buf->data);
+ msg_buf->header.msg_type = timeout_type;
+ (void) STRCPY(msg_buf->header.sender, master);
+ (void) STRCPY(msg_buf->dir_name, dir_name);
+
+ //
+ // Put the message in the Work Queue.
+ //
+
+ ReplInsertWorkQueue( (LPVOID) que_buf );
+
+ IF_DEBUG(WATCHD) {
+
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "Watch dog fired a timer event "
+ "(message type = " FORMAT_DWORD ").\n", timeout_type ));
+
+ }
+
+}
+
+
+DBGSTATIC VOID
+ReplCheckTimerList(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ Goes thru each dir entry in client list and checks for PULSE_TIMEOUT,
+ GUARD_TIMEOUT and then goes thru list of dupl_masters checking for
+ DUPL_TIMEOUT.
+
+ A timer is active if != 0, a timer fires when it becomes 0.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+Threads:
+
+ Only called by watchd thread.
+
+--*/
+{
+ PCLIENT_LIST_REC cur_rec;
+ LPTSTR DirName;
+ PDUPL_MASTERS_REC dupl;
+
+
+ //
+ // Make sure no one is touching the list.
+ //
+
+
+ ACQUIRE_LOCK_SHARED( RCGlobalClientListLock );
+
+
+ //
+ // Loop through each directory entry in the client list.
+ //
+
+ for ( cur_rec = RCGlobalClientListHeader;
+ cur_rec != NULL;
+ cur_rec = cur_rec->next_p ) {
+
+ DirName = cur_rec->dir_name;
+
+ //
+ // If we've got a message in the queue for this, that's good enough.
+ // (Scan delay list and work queue.)
+ //
+ if (ReplScanQueuesForMoreRecentMsg( DirName )) {
+ continue;
+ }
+ //
+ // If the timer is running on this directory entry,
+ // decrement the timer.
+ // If the time is then up,
+ // Let the syncer know.
+ //
+
+ if (cur_rec->timer.timeout != 0) {
+ if (--(cur_rec->timer.timeout) == 0) {
+ ReplQuePut( cur_rec->timer.type,
+ cur_rec->master,
+ DirName );
+ }
+ }
+
+ //
+ // If the guard timer is running on this directory entry,
+ // decrement the timer.
+ // If the time is then up,
+ // let the syncer know.
+ //
+
+ if (cur_rec->timer.grd_timeout != 0) {
+ if (--(cur_rec->timer.grd_timeout) == 0) {
+ ReplQuePut(
+ GUARD_TIMEOUT,
+ cur_rec->master,
+ DirName );
+ }
+ }
+
+
+ //
+ // Loop for each duplicate master for this directory entry.
+ //
+
+ ACQUIRE_LOCK_SHARED( RCGlobalDuplListLock );
+ for ( dupl = cur_rec->dupl.next_p;
+ dupl != NULL;
+ dupl = dupl->next_p ) {
+
+ //
+ // If this duplicate master is waiting for something,
+ // decrement the timer.
+ // If the time is then up,
+ // let the syncer know.
+ //
+
+ if (dupl->sleep_time != 0) {
+ if (--(dupl->sleep_time) == 0) {
+ ReplQuePut(
+ DUPL_TIMEOUT,
+ dupl->master,
+ DirName );
+ }
+ }
+ }
+ RELEASE_LOCK( RCGlobalDuplListLock );
+ }
+
+
+ RELEASE_LOCK( RCGlobalClientListLock );
+
+}
+
+
+
+
+DBGSTATIC VOID
+ReplCheckDelayQueue(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ Goes thru delay list, if any message's time is up, puts it into work_que
+ for syncer thread to do work, and releases it from delay_list.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+Threads:
+
+ Only called by watchd thread.
+
+--*/
+{
+ PBIGBUF_REC *delay_rec;
+ PBIGBUF_REC curr_rec;
+
+ //
+ // Make sure no one's touching the list
+ //
+
+ ACQUIRE_LOCK( RCGlobalDelayListLock );
+
+
+ delay_rec = &RCGlobalDelayListHeader;
+
+ while ( *delay_rec != NULL) {
+
+ curr_rec = *delay_rec;
+
+ //
+ // Check if the time is up for this entry.
+ //
+
+ if (--(curr_rec->delay) == 0) {
+
+ //
+ // get it off delay list
+ //
+
+ *delay_rec = curr_rec->next_p;
+
+ //
+ // Put on work_que for syncer to take care of
+ //
+
+ ReplInsertWorkQueue( curr_rec );
+
+ IF_DEBUG(WATCHD) {
+
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "Watch dog fired a delay event.\n" ));
+
+ }
+
+
+ } else {
+
+ delay_rec = &curr_rec->next_p;
+ }
+ }
+
+ RELEASE_LOCK( RCGlobalDelayListLock );
+
+}
+
+
+
+
+
+DWORD
+ReplWatchdThread(
+ LPVOID Parameter
+ )
+/*++
+
+Routine Description:
+
+ This is the thread procedure for the Watchdog Thread. It
+ takes care of all replication client's timing.
+
+Arguments:
+
+ None. (Parameter is required to match WIN32 CreateThread routine,
+ but is ignored here.)
+
+Return Value:
+
+ None.
+
+Threads:
+
+ Only called by watchd thread.
+
+--*/
+{
+ NET_API_STATUS ApiStatus;
+ DWORD WaitStatus;
+
+ UNREFERENCED_PARAMETER(Parameter);
+
+ IF_DEBUG( REPL ) {
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplWatchdThread: thread ID is "
+ FORMAT_NET_THREAD_ID ".\n", NetpCurrentThread() ));
+ }
+
+ //
+ // Loop forever doing timing functions.
+ //
+
+ for ( ;; ) {
+
+ //
+ // Wait on the termination event.
+ //
+ // Time out every WATCHD_TIMER_PERIOD to supply the pulse.
+ //
+ // Notice that this algorithm doesn't take into account time spent
+ // within this routine, so the period is actually a little longer
+ // than 10 seconds. Considering the events currently being timed,
+ // this is insignificant.
+ //
+
+ WaitStatus = WaitForSingleObject( ReplGlobalClientTerminateEvent,
+ WATCHD_TIMER_PERIOD );
+
+
+ //
+ // Check if this thread should exit.
+ //
+
+ if ( WaitStatus == 0 ) {
+
+ break;
+
+ //
+ // Do the timing functions.
+ //
+
+ } else if (WaitStatus == WAIT_TIMEOUT ) {
+ ReplCheckTimerList();
+ ReplCheckDelayQueue();
+
+ } else {
+
+ ApiStatus = (NET_API_STATUS) GetLastError();
+ NetpAssert( ApiStatus != NO_ERROR );
+
+ // Log this error.
+ AlertLogExit(
+ ALERT_ReplSysErr,
+ NELOG_ReplSysErr,
+ ApiStatus,
+ NULL,
+ NULL,
+ NO_EXIT);
+
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "Watchd: WaitForSingleObject returned " FORMAT_HEX_DWORD
+ ", api status is " FORMAT_API_STATUS ".\n",
+ WaitStatus, ApiStatus ));
+ }
+
+ }
+
+ IF_DEBUG( REPL ) {
+ NetpKdPrint(( PREFIX_REPL_CLIENT
+ "ReplWatchdThread: exiting thread " FORMAT_NET_THREAD_ID ".\n",
+ NetpCurrentThread() ));
+
+ }
+
+ return 0;
+
+} // ReplWatchdThread
diff --git a/private/net/svcdlls/repl/server/wcslocal.c b/private/net/svcdlls/repl/server/wcslocal.c
new file mode 100644
index 000000000..55dd4deab
--- /dev/null
+++ b/private/net/svcdlls/repl/server/wcslocal.c
@@ -0,0 +1,152 @@
+#if 0 // entire file is obsolete
+
+/*++
+
+Copyright (c) 1987-1993 Microsoft Corporation
+
+Module Name:
+ WcsLocal.c
+
+Abstract:
+ Contains wcs string functions that are not available in wcstr.h
+
+Author:
+ 10/29/91 madana
+ temp code.
+
+Environment:
+ User mode only.
+ Contains NT-specific code.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+ Tab size is set to 4.
+
+Revision History:
+
+ 09-Jan-1992 JohnRo
+ Fixed bug where wcsupr() wasn't doing anything!
+ Moved wcstol() from here to NetLib and TString.h (now wtol()).
+ Changed to use ANSI types instead of Win32 typedefs.
+ Use wcsicmp() from NetLib (what the heck).
+ 09-Apr-1992 JohnRo
+ Prepare for WCHAR.H (_wcsicmp vs _wcscmpi, etc).
+ 21-Apr-1993 JohnRo
+ All routines in this file are historical.
+--*/
+
+
+#include <windef.h> // IN, etc.
+
+#include <wcslocal.h> // My prototypes.
+
+
+#if 0
+int
+_wcsicmp(
+ IN wchar_t * string1,
+ IN wchar_t * string2
+ )
+/*++
+Routine Description :
+ case insensitive comparition of two wide charecter strings.
+
+Arguments :
+ string1 : string to be compared
+ string2 : string to be compared
+
+Return Value :
+ return = if string1 == string2
+ < 1 if string1 < string2
+ > 1 if string1 > string2
+
+--*/
+{
+ int ret = 0 ;
+
+ while( !(ret = (int)wcsupper(*string1) - (int)wcsupper(*string2)) && *string2) {
+ ++string1, ++string2;
+ }
+
+ if ( ret < 0 )
+ ret = -1 ;
+ else if ( ret > 0 )
+ ret = 1 ;
+
+ return ret;
+}
+#endif // 0
+
+
+#if 0
+int
+wcsncmpi(
+ IN const wchar_t * string1,
+ IN const wchar_t * string2,
+ IN size_t n
+ )
+/*++
+Routine Description :
+ case insensitive comparition of two wide charecter strings of length upto
+ 'n' wide-charecters.
+
+Arguments :
+ string1 : string to be compared
+ string2 : string to be compared
+
+Return Value :
+ return = if string1 == string2
+ < 1 if string1 < string2
+ > 1 if string1 > string2
+
+--*/
+{
+ int ret = 0 ;
+
+ while(((ret = ((int)wcsupper(*string1) - (int)wcsupper(*string2))) == 0) &&
+ ((int)*string2 != 0) &&
+ (n != 0)) {
+ ++string1, ++string2, n--;
+ }
+
+ if ( ret < 0 )
+ ret = -1 ;
+ else if ( ret > 0 )
+ ret = 1 ;
+
+ return ret;
+}
+#endif // 0
+
+#if 0
+wchar_t *
+_wcsupr(
+ IN wchar_t * string
+ )
+{
+ return (_wcsupr(string));
+} // _wcsupr
+#endif // 0
+
+wchar_t
+wcsupper(
+ IN wchar_t inchar
+ )
+/*++
+Routine Description :
+ convert a given charecter to upper case.
+
+Arguments :
+ inchar : input charecter.
+
+Return Value :
+ return upper case of the input charecter.
+
+--*/
+{
+ if((inchar >= L'a') && (inchar <= L'z')) {
+ inchar = (wchar_t)((inchar - L'a') + L'A');
+ }
+
+ return inchar;
+}
+
+#endif // entire file is obsolete
diff --git a/private/net/svcdlls/repl/server/wcslocal.h b/private/net/svcdlls/repl/server/wcslocal.h
new file mode 100644
index 000000000..30ecfa710
--- /dev/null
+++ b/private/net/svcdlls/repl/server/wcslocal.h
@@ -0,0 +1,67 @@
+/*++
+
+Copyright (c) 1987-1992 Microsoft Corporation
+
+Module Name:
+ wcslocal.h
+
+Abstract:
+ Contains prototypes for wcs string functions that are
+ not available in wcstr.h
+
+Author:
+ 10/29/91 madana
+ temp code.
+
+Environment:
+ User mode only.
+ Contains NT-specific code.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+ Tab size is set to 4.
+
+Revision History:
+
+ 05-Jan-1992 JohnRo
+ wcsupr() changes its input parameter!
+ Changed to use ANSI types instead of Win32 typedefs.
+ 09-Jan-1992 JohnRo
+ Moved wcstol() from here to NetLib/TString.h's wtol().
+ 09-Apr-1992 JohnRo
+ Prepare for WCHAR.H (_wcsicmp vs _wcscmpi, etc).
+--*/
+
+
+#ifndef _WCSLOCAL_
+#define _WCSLOCAL_
+
+
+#include <stdlib.h> // Get wchar_t from an official place.
+
+
+// (Prototype is in wchar.h or wcstr.h)
+// int
+// wcsicmp(
+// IN const wchar_t * string1,
+// IN const wchar_t * string2
+// );
+
+// (Prototype is in wchar.h or wcstr.h)
+// int
+// wcsnicmp(
+// IN const wchar_t * string1,
+// IN const wchar_t * string2,
+// IN size_t n
+// );
+
+wchar_t
+wcsupper(
+ IN wchar_t inchar
+ );
+
+wchar_t
+wcsupper(
+ IN wchar_t inchar
+ );
+
+
+#endif // _WCSLOCAL_
diff --git a/private/net/svcdlls/repl/testdll/fakestub.c b/private/net/svcdlls/repl/testdll/fakestub.c
new file mode 100644
index 000000000..71b6765c5
--- /dev/null
+++ b/private/net/svcdlls/repl/testdll/fakestub.c
@@ -0,0 +1,166 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ FakeStub.c
+
+Abstract:
+
+ Test of DLL stubs handling of replicator APIs when service is not
+ started.
+
+Author:
+
+ John Rogers (JohnRo) 27-Jan-1992
+
+Revision History:
+
+ 27-Jan-1992 JohnRo
+ Created.
+ 23-Mar-1992 JohnRo
+ Fixed enum when service is running.
+--*/
+
+// These must be included first:
+
+#include <windef.h> // IN, VOID, LPTSTR, etc.
+#include <lmcons.h> // NET_API_STATUS, PARM equates, etc.
+#include <rpc.h> // Needed by <repl.h>.
+
+// These can be in any order:
+
+#include <lmerr.h> // NERR_ equates.
+#include <netdebug.h> // NetpAssert(), NetpKdPrint(()), etc.
+#include <repl.h> // Netr routine prototypes (MIDL-generated .h).
+
+
+#define FAKE_DLL_STUB \
+ { \
+ UNREFERENCED_PARAMETER(UncServerName); \
+ return (NERR_ServiceNotInstalled); \
+ }
+
+
+
+
+
+DWORD NetrReplGetInfo(
+ REPL_IDENTIFY_HANDLE UncServerName,
+ DWORD Level,
+ LPCONFIG_CONTAINER BufPtr)
+
+ FAKE_DLL_STUB
+
+DWORD NetrReplSetInfo(
+ REPL_IDENTIFY_HANDLE UncServerName,
+ DWORD Level,
+ LPCONFIG_CONTAINER BufPtr,
+ LPDWORD ParmError)
+
+ FAKE_DLL_STUB
+
+DWORD NetrReplExportDirAdd(
+ REPL_IDENTIFY_HANDLE UncServerName,
+ DWORD Level,
+ LPEXPORT_CONTAINER Buf,
+ LPDWORD ParmError)
+
+ FAKE_DLL_STUB
+
+DWORD NetrReplExportDirDel(
+ REPL_IDENTIFY_HANDLE UncServerName,
+ LPTSTR DirName)
+
+ FAKE_DLL_STUB
+
+DWORD NetrReplExportDirEnum(
+ REPL_IDENTIFY_HANDLE UncServerName,
+ // DWORD Level,
+ IN OUT LPEXPORT_ENUM_STRUCT EnumContainer,
+ // LPEXPORT_CONTAINER BufPtr,
+ DWORD PrefMaxSize,
+ // LPDWORD EntriesRead,
+ LPDWORD TotalEntries,
+ LPDWORD ResumeHandle)
+
+ FAKE_DLL_STUB
+
+DWORD NetrReplExportDirGetInfo(
+ REPL_IDENTIFY_HANDLE UncServerName,
+ LPTSTR DirName,
+ DWORD Level,
+ LPEXPORT_CONTAINER BufPtr)
+
+ FAKE_DLL_STUB
+
+DWORD NetrReplExportDirLock(
+ REPL_IDENTIFY_HANDLE UncServerName,
+ LPTSTR DirName)
+
+ FAKE_DLL_STUB
+
+DWORD NetrReplExportDirSetInfo(
+ REPL_IDENTIFY_HANDLE UncServerName,
+ LPTSTR DirName,
+ DWORD Level,
+ LPEXPORT_CONTAINER BufPtr,
+ LPDWORD ParmError)
+
+ FAKE_DLL_STUB
+
+DWORD NetrReplExportDirUnlock(
+ REPL_IDENTIFY_HANDLE UncServerName,
+ LPTSTR DirName,
+ DWORD UnlockForce)
+
+ FAKE_DLL_STUB
+
+DWORD NetrReplImportDirAdd(
+ REPL_IDENTIFY_HANDLE UncServerName,
+ DWORD Level,
+ LPIMPORT_CONTAINER Buf,
+ LPDWORD ParmError)
+
+ FAKE_DLL_STUB
+
+DWORD NetrReplImportDirDel(
+ REPL_IDENTIFY_HANDLE UncServerName,
+ LPTSTR DirName)
+
+ FAKE_DLL_STUB
+
+DWORD NetrReplImportDirEnum(
+ REPL_IDENTIFY_HANDLE UncServerName,
+ // DWORD Level,
+ IN OUT LPIMPORT_ENUM_STRUCT EnumContainer,
+ // LPIMPORT_CONTAINER BufPtr,
+ DWORD PrefMaxSize,
+ // LPDWORD EntriesRead,
+ LPDWORD TotalEntries,
+ LPDWORD ResumeHandle)
+
+ FAKE_DLL_STUB
+
+DWORD NetrReplImportDirGetInfo(
+ REPL_IDENTIFY_HANDLE UncServerName,
+ LPTSTR DirName,
+ DWORD Level,
+ LPIMPORT_CONTAINER BufPtr)
+
+ FAKE_DLL_STUB
+
+DWORD NetrReplImportDirLock(
+ REPL_IDENTIFY_HANDLE UncServerName,
+ LPTSTR DirName)
+
+ FAKE_DLL_STUB
+
+DWORD NetrReplImportDirUnlock(
+ REPL_IDENTIFY_HANDLE UncServerName,
+ LPTSTR DirName,
+ DWORD UnlockForce)
+
+ FAKE_DLL_STUB
+
diff --git a/private/net/svcdlls/repl/testdll/makefile b/private/net/svcdlls/repl/testdll/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/net/svcdlls/repl/testdll/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/net/svcdlls/repl/testdll/sources b/private/net/svcdlls/repl/testdll/sources
new file mode 100644
index 000000000..be98309a9
--- /dev/null
+++ b/private/net/svcdlls/repl/testdll/sources
@@ -0,0 +1,110 @@
+!IF 0
+
+Copyright (c) 1989-92 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+Author:
+
+ John Rogers (JohnRo) 17-Dec-1991
+
+NOTE: Commented description of this file is in \nt\public\oak\bin\sources.tpl
+
+!ENDIF
+
+#
+# The MAJORCOMP and MINORCOMP variables are defined
+# so that $(MAJORCOMP)$(MINORCOMP)filename can be used in
+# cross compiling to provide unique filenames in a flat namespace.
+#
+
+MAJORCOMP=Test
+MINORCOMP=DLL
+
+#
+# The TARGETNAME variable is defined by the developer. It is the name of
+# the target (component) that is being built by this makefile. It
+# should NOT include any path or file extension information.
+#
+
+TARGETNAME=TestDLL
+
+#
+# The TARGETPATH and TARGETTYPE variables are defined by the developer.
+# The first specifies where the target is to be build. The second specifies
+# the type of target (either PROGRAM, DYNLINK, LIBRARY, UMAPPL_NOLIB or
+# BOOTPGM). UMAPPL_NOLIB is used when you're only building user-mode
+# apps and don't need to build a library.
+#
+
+TARGETPATH=obj
+
+# Pick one of the following and delete the others
+TARGETTYPE=LIBRARY
+
+#
+# The TARGETLIBS specifies additional libraries to link with you target
+# image. Each library path specification should contain an asterisk (*)
+# where the machine specific subdirectory name should go.
+#
+
+TARGETLIBS=
+
+#
+# The INCLUDES variable specifies any include paths that are specific to
+# this source directory. Separate multiple directory paths with single
+# semicolons. Relative path specifications are okay.
+#
+
+INCLUDES=.;..\common;..\repltest;..\..\..\inc;..\..\..\..\inc;..\server
+
+#
+# The SOURCES variable is defined by the developer. It is a list of all the
+# source files for this component. Each source file should be on a separate
+# line using the line continuation character. This will minimize merge
+# conflicts if two developers adding source files to the same component.
+#
+
+# Most .c files in this directory aren't compiled; they're OS/2 test programs.
+# Maybe someday we'll convert them over.
+
+!IFNDEF DISABLE_NET_UNICODE
+UNICODE=1
+NET_C_DEFINES=-DUNICODE
+!ENDIF
+
+USE_CRTDLL=1
+
+SOURCES=FakeStub.c
+
+#
+# Next specify one or more user mode test programs and their type
+# UMTEST is used for optional test programs. UMAPPL is used for
+# programs that always get built when the directory is built.
+#
+
+UMTYPE=console
+UMAPPL=TestDLL
+UMLIBS= \
+ $(BASEDIR)\public\sdk\lib\*\netapi32.lib \
+ $(BASEDIR)\Public\Sdk\Lib\*\netlib.lib \
+ $(BASEDIR)\public\sdk\lib\*\NetRap.lib \
+ ..\Server\obj\*\Repl.lib \
+ ..\Client\obj\*\ReplCli.lib \
+ ..\Common\obj\*\ReplCom.lib \
+ ..\ReplTest\obj\*\ReplTest.lib \
+ $(BASEDIR)\public\sdk\lib\*\RpcNdr.lib \
+ $(BASEDIR)\public\sdk\lib\*\RpcRT4.lib \
+ $(BASEDIR)\public\sdk\lib\*\SvcCtrl.lib \
+ $(BASEDIR)\public\sdk\lib\*\advapi32.lib
+
+C_DEFINES=-DRPC_NO_WINDOWS_H
diff --git a/private/net/svcdlls/repl/testdll/testdll.c b/private/net/svcdlls/repl/testdll/testdll.c
new file mode 100644
index 000000000..a51a1a823
--- /dev/null
+++ b/private/net/svcdlls/repl/testdll/testdll.c
@@ -0,0 +1,75 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ TestDLL.c
+
+Abstract:
+
+ Test of DLL stubs handling of replicator APIs when service is not
+ started.
+
+Author:
+
+ John Rogers (JohnRo) 27-Jan-1992
+
+Revision History:
+
+ 27-Jan-1992 JohnRo
+ Created.
+ 19-Feb-1992 JohnRo
+ Added tests of repl config and import APIs.
+ 22-Feb-1992 JohnRo
+ Added multi-thread workaround to EXIT_A_TEST(), etc.
+ 27-Feb-1992 JohnRo
+ Added display of current time.
+ 20-Mar-1992 JohnRo
+ Caller should wait for service start (if necessary), not Test routines.
+
+--*/
+
+// These must be included first:
+
+#include <windef.h> // IN, VOID, LPTSTR, etc.
+#include <lmcons.h> // NET_API_STATUS, PARM equates, etc.
+
+// These can be in any order:
+
+#include <netdebug.h> // NetpAssert(), NetpKdPrint(()), etc.
+#include <replp.h> // NetpReplTimeNow().
+#include <repltest.h> // Test routine prototypes.
+#include <stdlib.h> // EXIT_SUCCESS.
+#include <time.h> // time().
+
+
+BOOL TestIsMultithread = FALSE; // Tell EXIT_A_TEST() how to work.
+
+
+int
+main(
+ void
+ )
+{
+ DWORD Time;
+ NetpKdPrint(( "main(TestDLL version)...\n" ));
+
+ Time = (DWORD) time( NULL );
+ NetpDbgDisplayTimestamp( "time()", Time );
+ Time = NetpReplTimeNow( );
+ NetpDbgDisplayTimestamp( "NetpReplTimeNow()", Time );
+
+ NetpKdPrint(( "TestDLL: Calling TestReplApis...\n" ));
+ TestReplApis( /* ignored */ NULL );
+
+ NetpKdPrint(( "TestDLL: Calling TestExportDirApis...\n" ));
+ TestExportDirApis( /* ignored */ NULL );
+
+ NetpKdPrint(( "TestDLL: Calling TestImportDirApis...\n" ));
+ TestImportDirApis( /* ignored */ NULL );
+
+ NetpKdPrint(( "main(TestDLL version)...\n" ));
+
+ return (EXIT_SUCCESS);
+}
diff --git a/private/net/svcdlls/rpl/client/makefile b/private/net/svcdlls/rpl/client/makefile
new file mode 100644
index 000000000..f0db8e4a7
--- /dev/null
+++ b/private/net/svcdlls/rpl/client/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS LINE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/net/svcdlls/rpl/client/rplapi.c b/private/net/svcdlls/rpl/client/rplapi.c
new file mode 100644
index 000000000..881161ea2
--- /dev/null
+++ b/private/net/svcdlls/rpl/client/rplapi.c
@@ -0,0 +1,1893 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ rpl.c
+
+Abstract:
+
+ This file contains program to test RPL APIs.
+
+Author:
+
+ Vladimiv Z. Vulovic (vladimv) 19-November-1993
+
+Environment:
+
+ User Mode - Win32
+
+Revision History:
+
+--*/
+
+//
+// INCLUDES
+//
+
+#include <nt.h> // DbgPrint prototype
+#include <ntrtl.h> // DbgPrint prototype
+#include <nturtl.h> // Needed by winbase.h
+
+#include <windows.h> // DWORD, IN, File APIs, etc.
+
+#include <lmcons.h> // NET_API_STATUS
+#include <lmerr.h> // NetError codes
+#include <lmrpl.h> // RPL_HANDLE
+#include <i_lmrpl.h> // RPL_CONFIG_INFO_1
+#include <lmapibuf.h> // NetApiBufferFree()
+#include <stdlib.h> // exit()
+#include <stdio.h> // printf
+#include <ctype.h> // toupper - bombs if I use it
+
+#define RPL_BUFFER_GET_ALL ((DWORD)-1)
+
+#define Call( fn ) \
+ { \
+ int _ApiError = fn; \
+ if ( _ApiError != NO_ERROR) { \
+ printf( "Line = %d, _ApiError = %d\n", __LINE__, _ApiError); \
+ DbgPrint( "[RplApi] Line = %d, _ApiError = %d\n", __LINE__, _ApiError); \
+ DbgUserBreakPoint(); \
+ } \
+ }
+
+#define RPL_ASSERT( condition) \
+ { \
+ if ( !( condition)) { \
+ printf( "File %s, Line %d\n", __FILE__, __LINE__); \
+ DbgPrint( "[RplApi] File %s, Line %d\n", __FILE__, __LINE__); \
+ DbgUserBreakPoint(); \
+ } \
+ }
+
+PWCHAR G_ServerName; // GLOBAL
+RPL_HANDLE G_ServerHandle; // GLOBAL
+
+
+BOOL ReadString(
+ IN PCHAR StringName,
+ OUT PWCHAR * pString,
+ IN BOOL MustHaveInput
+ )
+{
+ BYTE Line[ 300];
+ WCHAR WcharBuffer[ 300];
+ DWORD Length; // includes terminal null wchar
+
+ printf( "%s=", StringName);
+
+ *pString = NULL;
+
+ if ( gets( Line) == NULL) {
+ return( FALSE);
+ }
+ if ( *Line == 0) {
+ //
+ // Zero length input is OK only when input is optional (and in that
+ // case pointer is assumed to be NULL).
+ //
+ if ( MustHaveInput == FALSE) {
+ return( TRUE);
+ } else {
+ printf( "%s must be supplied\n", StringName);
+ return( FALSE);
+ }
+ }
+ Length = MultiByteToWideChar( CP_OEMCP, MB_PRECOMPOSED, Line,
+ -1, WcharBuffer, sizeof( WcharBuffer));
+ if ( Length == 0) {
+ printf( "Invalid string = %s\n, Line");
+ return( FALSE);
+ }
+ *pString = LocalAlloc( GMEM_FIXED, Length * sizeof(WCHAR));
+ if ( *pString == NULL) {
+ printf( "LocalAlloc failed");
+ return( FALSE);
+ }
+ wcscpy( *pString, WcharBuffer);
+ return( TRUE);
+}
+
+
+BOOL ReadInt(
+ IN PCHAR FieldName,
+ OUT int * pInt,
+ IN BOOL MustHaveInput
+ )
+{
+ BYTE Line[ 300];
+ printf( "%s=", FieldName);
+ if ( gets( Line) == NULL) {
+ return( FALSE);
+ }
+ if ( sscanf( Line, "%d", pInt) != 1) {
+ if ( MustHaveInput == FALSE) {
+ return( TRUE);
+ } else {
+ printf( "%s must be supplied\n", FieldName);
+ return( FALSE);
+ }
+ }
+ return( TRUE);
+}
+
+
+BOOL ReadWchar(
+ IN PCHAR FieldName,
+ OUT PWCHAR pWchar,
+ IN BOOL MustHaveInput
+ )
+{
+ BYTE Line[ 300];
+ CHAR Char;
+
+ printf( "%s=", FieldName);
+ if ( gets( Line) == NULL) {
+ return( FALSE);
+ }
+ //
+ // Note that "%1s" instead of "%1c" would nicely overwrite stack
+ // (due to terminating NULL added).
+ //
+ if ( sscanf( Line, "%1c", &Char) != 1) {
+ if ( MustHaveInput == FALSE) {
+ return( TRUE);
+ } else {
+ printf( "%s must be supplied\n", FieldName);
+ return( FALSE);
+ }
+ }
+ *pWchar = Char;
+ return( TRUE);
+}
+
+
+VOID TestConnect( VOID)
+{
+ RPL_HANDLE ServerHandle;
+ Call( NetRplOpen( G_ServerName, &ServerHandle);)
+ printf( "ServerHandle = 0x%x\n", ServerHandle);
+ Call( NetRplClose( ServerHandle);)
+}
+
+
+DWORD ConfigDisplayInfo(
+ IN DWORD Level,
+ OUT LPVOID Buffer
+ )
+{
+ LPRPL_CONFIG_INFO_2 Info = Buffer;
+
+ if ( Level > 2) {
+ return( ERROR_INVALID_LEVEL);
+ }
+ printf( "ConfigName=%ws\n\tConfigComment=%ws\n", Info->ConfigName, Info->ConfigComment);
+ if ( Level == 0) {
+ return( NO_ERROR);
+ }
+ printf( "\tFlags = 0x%x\n", Info->Flags);
+ if ( Level == 1) {
+ return( NO_ERROR);
+ }
+ printf( "\tBootName=%ws\n\tDirName=%ws\n\tDirname2=%ws\n\t"
+ "Dirname3=%ws\n\tDirName4=%ws\n\tFitShared=%ws\n\tFitPersonal=%ws\n",
+ Info->BootName, Info->DirName, Info->DirName2,
+ Info->DirName3, Info->DirName4, Info->FitShared, Info->FitPersonal);
+ return( NO_ERROR);
+}
+
+
+
+VOID TestConfigEnum(
+ IN DWORD Level,
+ IN PWCHAR AdapterName,
+ IN DWORD PrefMaxLength,
+ IN PDWORD pResumeHandle
+ )
+{
+ LPBYTE Buffer;
+ DWORD EntriesRead;
+ DWORD TotalEntries;
+ DWORD CoreSize;
+ DWORD index;
+ DWORD Error;
+
+ switch( Level) {
+ case 0:
+ CoreSize = sizeof( RPL_CONFIG_INFO_0);
+ break;
+ case 1:
+ CoreSize = sizeof( RPL_CONFIG_INFO_1);
+ break;
+ case 2:
+ CoreSize = sizeof( RPL_CONFIG_INFO_2);
+ break;
+ default:
+ printf( "\nTestConfigEnum: invalid Level=%d", Level);
+ return;
+ break;
+ }
+
+ printf( "\nTestConfigEnum: Level=%d", Level);
+ if ( PrefMaxLength != RPL_BUFFER_GET_ALL) {
+ printf( ", PrefMaxLength=%ld", PrefMaxLength);
+ } else {
+ printf( ", unlimited buffer size");
+ }
+ if ( AdapterName != NULL) {
+ printf( ", AdapterName=%ws", AdapterName);
+ }
+ if ( pResumeHandle != NULL) {
+ printf( ", ResumeHandle=0x%x\n\n", *pResumeHandle);
+ } else {
+ printf( ", not resumable.\n\n");
+ }
+
+ for ( ; ; ) {
+ Error = NetRplConfigEnum( G_ServerHandle, AdapterName, Level, &Buffer,
+ PrefMaxLength, &EntriesRead, &TotalEntries, pResumeHandle);
+
+ if ( Error != NO_ERROR && Error != ERROR_MORE_DATA) {
+ printf( "Error = %d\n", Error);
+ break;
+ }
+
+ printf( "Buffer = 0x%x, EntriesRead = %d, TotalEntries = %d", Buffer,
+ EntriesRead, TotalEntries);
+ if ( pResumeHandle != NULL) {
+ printf( ", ResumeHandle = 0x%x\n", *pResumeHandle);
+ } else {
+ printf("\n");
+ }
+
+ for ( index = 0; index < EntriesRead; index++) {
+ ConfigDisplayInfo( Level, Buffer + index * CoreSize);
+ }
+ NetApiBufferFree( Buffer); // =~ MIDL_user_free()
+
+ if ( pResumeHandle == NULL) {
+ break;
+ }
+ if ( *pResumeHandle == 0) {
+ RPL_ASSERT( Error == NO_ERROR);
+ break;
+ }
+ RPL_ASSERT( Error == ERROR_MORE_DATA);
+ }
+}
+
+
+DWORD ProfileDisplayInfo(
+ IN DWORD Level,
+ OUT LPVOID Buffer
+ )
+{
+ LPRPL_PROFILE_INFO_2 Info = Buffer;
+
+ if ( Level > 2) {
+ return( ERROR_INVALID_LEVEL);
+ }
+ printf( "ProfileName=%ws\n\tProfileComment=%ws\n", Info->ProfileName, Info->ProfileComment);
+ if ( Level == 0) {
+ return( NO_ERROR);
+ }
+ printf( "\tFlags = 0x%x\n", Info->Flags);
+ if ( Level == 1) {
+ return( NO_ERROR);
+ }
+ printf( "\tConfigName=%ws\n\tBootName=%ws\n\tFitShared=%ws\n\tFitPersonal=%ws\n",
+ Info->ConfigName, Info->BootName, Info->FitShared, Info->FitPersonal);
+ return( NO_ERROR);
+}
+
+
+
+VOID TestProfileEnum(
+ IN DWORD Level,
+ IN PWCHAR AdapterName,
+ IN DWORD PrefMaxLength,
+ IN PDWORD pResumeHandle
+ )
+{
+ LPBYTE Buffer;
+ DWORD EntriesRead;
+ DWORD TotalEntries;
+ DWORD CoreSize;
+ DWORD index;
+ DWORD Error;
+
+ switch( Level) {
+ case 0:
+ CoreSize = sizeof( RPL_PROFILE_INFO_0);
+ break;
+ case 1:
+ CoreSize = sizeof( RPL_PROFILE_INFO_1);
+ break;
+ case 2:
+ CoreSize = sizeof( RPL_PROFILE_INFO_2);
+ break;
+ default:
+ printf( "\nTestProfileEnum: invalid Level=%d", Level);
+ return;
+ break;
+ }
+
+ printf( "\nTestProfileEnum: Level=%d", Level);
+ if ( PrefMaxLength != RPL_BUFFER_GET_ALL) {
+ printf( ", PrefMaxLength=%ld", PrefMaxLength);
+ } else {
+ printf( ", unlimited buffer size");
+ }
+ if ( AdapterName != NULL) {
+ printf( ", AdapterName=%ws", AdapterName);
+ }
+ if ( pResumeHandle != NULL) {
+ printf( ", ResumeHandle=0x%x\n\n", *pResumeHandle);
+ } else {
+ printf( ", not resumable.\n\n");
+ }
+
+ for ( ; ; ) {
+ Error = NetRplProfileEnum( G_ServerHandle, AdapterName, Level, &Buffer,
+ PrefMaxLength, &EntriesRead, &TotalEntries, pResumeHandle);
+
+ if ( Error != NO_ERROR && Error != ERROR_MORE_DATA) {
+ printf( "Error = %d\n", Error);
+ break;
+ }
+
+ printf( "Buffer = 0x%x, EntriesRead = %d, TotalEntries = %d", Buffer,
+ EntriesRead, TotalEntries);
+ if ( pResumeHandle != NULL) {
+ printf( ", ResumeHandle = 0x%x\n", *pResumeHandle);
+ } else {
+ printf("\n");
+ }
+
+ for ( index = 0; index < EntriesRead; index++) {
+ ProfileDisplayInfo( Level, Buffer + index * CoreSize);
+ }
+ NetApiBufferFree( Buffer); // =~ MIDL_user_free()
+
+ if ( pResumeHandle == NULL) {
+ break;
+ }
+ if ( *pResumeHandle == 0) {
+ RPL_ASSERT( Error == NO_ERROR);
+ break;
+ }
+ RPL_ASSERT( Error == ERROR_MORE_DATA);
+ }
+}
+
+
+DWORD ServiceDisplayInfo(
+ IN DWORD Level,
+ OUT LPVOID Buffer
+ )
+{
+ LPRPL_INFO_0 Info = Buffer;
+
+ if ( Level > 0) {
+ return( ERROR_INVALID_LEVEL);
+ }
+ printf( "Flags = 0x%x\n", Info->Flags);
+ return( NO_ERROR);
+}
+
+
+
+DWORD WkstaDisplayInfo(
+ IN DWORD Level,
+ OUT LPVOID Buffer
+ )
+{
+ LPRPL_WKSTA_INFO_2 Info = Buffer;
+
+ if ( Level > 2) {
+ return( ERROR_INVALID_LEVEL);
+ }
+ printf( "WkstaName=%ws\n\tWkstaComment=%ws\n", Info->WkstaName, Info->WkstaComment);
+ if ( Level == 0) {
+ return( NO_ERROR);
+ }
+ printf( "\tFlags=0x%x\n\tProfileName=%ws\n", Info->Flags, Info->ProfileName);
+ if ( Level == 1) {
+ return( NO_ERROR);
+ }
+ printf( "\tBootName=%ws\n", Info->BootName);
+ printf( "\tFitFile=%ws\n", Info->FitFile);
+ printf( "\tAdapterName=%ws\n", Info->AdapterName);
+ printf( "\tTcpIpAddress=0x%x\n", Info->TcpIpAddress);
+ printf( "\tTcpIpSubnet=0x%x\n", Info->TcpIpSubnet);
+ printf( "\tTcpIpGateway=0x%x\n", Info->TcpIpGateway);
+ return( NO_ERROR);
+}
+
+
+
+VOID TestWkstaEnum(
+ IN DWORD Level,
+ IN PWCHAR ProfileName,
+ IN DWORD PrefMaxLength,
+ IN PDWORD pResumeHandle
+ )
+{
+ LPBYTE Buffer;
+ DWORD EntriesRead;
+ DWORD TotalEntries;
+ DWORD CoreSize;
+ DWORD index;
+ DWORD Error;
+
+ switch( Level) {
+ case 0:
+ CoreSize = sizeof( RPL_WKSTA_INFO_0);
+ break;
+ case 1:
+ CoreSize = sizeof( RPL_WKSTA_INFO_1);
+ break;
+ case 2:
+ CoreSize = sizeof( RPL_WKSTA_INFO_2);
+ break;
+ default:
+ printf( "\nTestWkstaEnum: invalid Level=%d", Level);
+ return;
+ break;
+ }
+
+ printf( "\nTestWkstaEnum: Level=%d", Level);
+ if ( PrefMaxLength != RPL_BUFFER_GET_ALL) {
+ printf( ", PrefMaxLength=%ld", PrefMaxLength);
+ } else {
+ printf( ", unlimited buffer size");
+ }
+ if ( ProfileName != NULL) {
+ printf( ", ProfileName=%ws", ProfileName);
+ }
+ if ( pResumeHandle != NULL) {
+ printf( ", ResumeHandle=0x%x\n\n", *pResumeHandle);
+ } else {
+ printf( ", not resumable.\n\n");
+ }
+
+ for ( ; ; ) {
+ Error = NetRplWkstaEnum( G_ServerHandle, ProfileName, Level, &Buffer,
+ PrefMaxLength, &EntriesRead, &TotalEntries, pResumeHandle);
+
+ if ( Error != NO_ERROR && Error != ERROR_MORE_DATA) {
+ printf( "Error = %d\n", Error);
+ break;
+ }
+
+ printf( "Buffer = 0x%x, EntriesRead = %d, TotalEntries = %d", Buffer,
+ EntriesRead, TotalEntries);
+ if ( pResumeHandle != NULL) {
+ printf( ", ResumeHandle = 0x%x\n", *pResumeHandle);
+ } else {
+ printf("\n");
+ }
+
+ for ( index = 0; index < EntriesRead; index++) {
+ WkstaDisplayInfo( Level, Buffer + index * CoreSize);
+ }
+ NetApiBufferFree( Buffer); // =~ MIDL_user_free()
+
+ if ( pResumeHandle == NULL) {
+ break;
+ }
+ if ( *pResumeHandle == 0) {
+ RPL_ASSERT( Error == NO_ERROR);
+ break;
+ }
+ RPL_ASSERT( Error == ERROR_MORE_DATA);
+ }
+}
+
+
+DWORD VendorDisplayInfo(
+ IN DWORD Level,
+ OUT LPVOID Buffer
+ )
+{
+ LPRPL_VENDOR_INFO_1 Info = Buffer;
+
+ if ( Level > 1) {
+ return( ERROR_INVALID_LEVEL);
+ }
+ printf( "VendorName=%ws\n\tVendorComment=%ws\n", Info->VendorName, Info->VendorComment);
+ if ( Level == 0) {
+ return( NO_ERROR);
+ }
+ printf( "\tFlags=0x%x\n", Info->Flags);
+ return( NO_ERROR);
+}
+
+
+
+DWORD BootDisplayInfo(
+ IN DWORD Level,
+ OUT LPVOID Buffer
+ )
+{
+ LPRPL_BOOT_INFO_2 Info = Buffer;
+
+ if ( Level > 2) {
+ return( ERROR_INVALID_LEVEL);
+ }
+ printf( "BootName=%ws\n\tBootComment=%ws\n", Info->BootName, Info->BootComment);
+ if ( Level == 0) {
+ return( NO_ERROR);
+ }
+ printf( "\tFlags=0x%x\n\tVendorName=%ws\n", Info->Flags, Info->VendorName);
+ if ( Level == 1) {
+ return( NO_ERROR);
+ }
+ printf( "\tBbcFile=%ws\n", Info->BbcFile);
+ printf( "\tWindowSize=0x%x\n", Info->WindowSize);
+ return( NO_ERROR);
+}
+
+
+
+DWORD AdapterDisplayInfo(
+ IN DWORD Level,
+ OUT LPVOID Buffer
+ )
+{
+ LPRPL_ADAPTER_INFO_1 Info = Buffer;
+
+ if ( Level > 1) {
+ return( ERROR_INVALID_LEVEL);
+ }
+ printf( "AdapterName=%ws\n\tAdapterComment=%ws\n", Info->AdapterName, Info->AdapterComment);
+ if ( Level == 0) {
+ return( NO_ERROR);
+ }
+ printf( "\tFlags=0x%x\n", Info->Flags);
+ return( NO_ERROR);
+}
+
+
+
+VOID TestAdapterEnum(
+ IN DWORD Level,
+ IN DWORD PrefMaxLength,
+ IN PDWORD pResumeHandle
+ )
+{
+ LPBYTE Buffer;
+ DWORD EntriesRead;
+ DWORD TotalEntries;
+ DWORD CoreSize;
+ DWORD index;
+ DWORD Error;
+
+ switch( Level) {
+ case 0:
+ CoreSize = sizeof( RPL_ADAPTER_INFO_0);
+ break;
+ case 1:
+ CoreSize = sizeof( RPL_ADAPTER_INFO_1);
+ break;
+ default:
+ printf( "\nTestAdapterEnum: invalid Level=%d", Level);
+ return;
+ break;
+ }
+
+ printf( "\nTestAdapterEnum: Level=%d", Level);
+ if ( PrefMaxLength != RPL_BUFFER_GET_ALL) {
+ printf( ", PrefMaxLength=%ld", PrefMaxLength);
+ } else {
+ printf( ", unlimited buffer size");
+ }
+ if ( pResumeHandle != NULL) {
+ printf( ", ResumeHandle=0x%x\n\n", *pResumeHandle);
+ } else {
+ printf( ", not resumable.\n\n");
+ }
+
+ for ( ; ; ) {
+ Error = NetRplAdapterEnum( G_ServerHandle, Level, &Buffer,
+ PrefMaxLength, &EntriesRead, &TotalEntries, pResumeHandle);
+
+ if ( Error != NO_ERROR && Error != ERROR_MORE_DATA) {
+ printf( "Error = %d\n", Error);
+ break;
+ }
+
+ printf( "Buffer = 0x%x, EntriesRead = %d, TotalEntries = %d", Buffer,
+ EntriesRead, TotalEntries);
+ if ( pResumeHandle != NULL) {
+ printf( ", ResumeHandle = 0x%x\n", *pResumeHandle);
+ } else {
+ printf("\n");
+ }
+
+ for ( index = 0; index < EntriesRead; index++) {
+ AdapterDisplayInfo( Level, Buffer + index * CoreSize);
+ }
+ NetApiBufferFree( Buffer); // =~ MIDL_user_free()
+
+ if ( pResumeHandle == NULL) {
+ break;
+ }
+ if ( *pResumeHandle == 0) {
+ RPL_ASSERT( Error == NO_ERROR);
+ break;
+ }
+ RPL_ASSERT( Error == ERROR_MORE_DATA);
+ }
+}
+
+
+VOID TestAdapterAdd( IN LPVOID Buffer)
+{
+ DWORD Error;
+ DWORD ErrorParameter;
+
+ printf( "\nTestAdapterAdd\n");
+ AdapterDisplayInfo( 1, Buffer);
+ Error = NetRplAdapterAdd( G_ServerHandle, 1, Buffer, &ErrorParameter);
+ if ( Error != NO_ERROR) {
+ printf( "Error = %d, ErrorParameter = %d\n", Error, ErrorParameter);
+ return;
+ }
+}
+
+
+VOID AdapterAdd( VOID)
+{
+ RPL_ADAPTER_INFO_1 Info;
+
+ if ( !ReadString( "AdapterName", &Info.AdapterName, TRUE)) {
+ return;
+ }
+ Info.Flags = 0;
+ printf( "\tAll other parameters are optional\n");
+ if ( !ReadString( "AdapterComment", &Info.AdapterComment, FALSE)) {
+ return;
+ }
+ TestAdapterAdd( &Info);
+}
+
+
+VOID TestBootAdd( IN LPVOID Buffer)
+{
+ DWORD Error;
+ DWORD ErrorParameter;
+
+ printf( "\nTestBootAdd\n");
+ BootDisplayInfo( 2, Buffer);
+ Error = NetRplBootAdd( G_ServerHandle, 2, Buffer, &ErrorParameter);
+ if ( Error != NO_ERROR) {
+ printf( "Error = %d, ErrorParameter = %d\n", Error, ErrorParameter);
+ return;
+ }
+}
+
+
+VOID BootAdd( VOID)
+{
+ RPL_BOOT_INFO_2 Info;
+
+ if ( !ReadString( "BootName", &Info.BootName, TRUE)) {
+ return;
+ }
+ if ( !ReadString( "VendorName", &Info.VendorName, TRUE)) {
+ return;
+ }
+ if ( !ReadString( "ConfigName", &Info.BbcFile, TRUE)) {
+ return;
+ }
+ Info.Flags = 0;
+ printf( "\tAll other parameters are optional\n");
+ if ( !ReadString( "BootComment", &Info.BootComment, FALSE)) {
+ return;
+ }
+ Info.WindowSize = 1;
+ if ( !ReadInt( "WindowSize", &Info.WindowSize, FALSE)) {
+ return;
+ }
+ TestBootAdd( &Info);
+}
+
+
+VOID BootDel( VOID)
+{
+ PWCHAR BootName;
+ PWCHAR VendorName;
+ DWORD Error;
+
+ if ( !ReadString( "BootName", &BootName, TRUE)) {
+ return;
+ }
+ if ( !ReadString( "VendorName", &VendorName, TRUE)) {
+ return;
+ }
+ Error = NetRplBootDel( G_ServerHandle, BootName, VendorName);
+ if ( Error != NO_ERROR) {
+ printf( "Error = %d\n", Error);
+ return;
+ }
+}
+
+
+VOID TestBootEnum(
+ IN DWORD Level,
+ IN DWORD PrefMaxLength,
+ IN PDWORD pResumeHandle
+ )
+{
+ LPBYTE Buffer;
+ DWORD EntriesRead;
+ DWORD TotalEntries;
+ DWORD CoreSize;
+ DWORD index;
+ DWORD Error;
+
+ switch( Level) {
+ case 0:
+ CoreSize = sizeof( RPL_BOOT_INFO_0);
+ break;
+ case 1:
+ CoreSize = sizeof( RPL_BOOT_INFO_1);
+ break;
+ case 2:
+ CoreSize = sizeof( RPL_BOOT_INFO_2);
+ break;
+ default:
+ printf( "\nTestBootEnum: invalid Level=%d", Level);
+ return;
+ break;
+ }
+
+ printf( "\nTestBootEnum: Level=%d", Level);
+ if ( PrefMaxLength != RPL_BUFFER_GET_ALL) {
+ printf( ", PrefMaxLength=%ld", PrefMaxLength);
+ } else {
+ printf( ", unlimited buffer size");
+ }
+ if ( pResumeHandle != NULL) {
+ printf( ", ResumeHandle=0x%x\n\n", *pResumeHandle);
+ } else {
+ printf( ", not resumable.\n\n");
+ }
+
+ for ( ; ; ) {
+ Error = NetRplBootEnum( G_ServerHandle, Level, &Buffer,
+ PrefMaxLength, &EntriesRead, &TotalEntries, pResumeHandle);
+
+ if ( Error != NO_ERROR && Error != ERROR_MORE_DATA) {
+ printf( "Error = %d\n", Error);
+ break;
+ }
+
+ printf( "Buffer = 0x%x, EntriesRead = %d, TotalEntries = %d", Buffer,
+ EntriesRead, TotalEntries);
+ if ( pResumeHandle != NULL) {
+ printf( ", ResumeHandle = 0x%x\n", *pResumeHandle);
+ } else {
+ printf("\n");
+ }
+
+ for ( index = 0; index < EntriesRead; index++) {
+ BootDisplayInfo( Level, Buffer + index * CoreSize);
+ }
+ NetApiBufferFree( Buffer); // =~ MIDL_user_free()
+
+ if ( pResumeHandle == NULL) {
+ break;
+ }
+ if ( *pResumeHandle == 0) {
+ RPL_ASSERT( Error == NO_ERROR);
+ break;
+ }
+ RPL_ASSERT( Error == ERROR_MORE_DATA);
+ }
+}
+
+
+VOID BootEnum( VOID)
+{
+ BYTE Line[ 300];
+ DWORD Count;
+ DWORD Level;
+ DWORD PrefMaxLength;
+ DWORD ResumeHandle;
+
+ printf( "Input: Level & PrefMaxLength\n");
+ if ( gets( Line) == NULL) {
+ return;
+ }
+ Count = sscanf( Line, "%d %d", &Level, &PrefMaxLength);
+ if ( Count != 2) {
+ printf( "Bad number of arguments.\n");
+ return;
+ }
+ if ( PrefMaxLength != RPL_BUFFER_GET_ALL) {
+ ResumeHandle = 0, TestBootEnum( Level, PrefMaxLength, &ResumeHandle);
+ } else {
+ TestBootEnum( Level, RPL_BUFFER_GET_ALL, NULL);
+ }
+}
+
+
+VOID TestConfigAdd( IN LPVOID Buffer)
+{
+ DWORD Error;
+ DWORD ErrorParameter;
+
+ printf( "\nTestConfigAdd\n");
+ ConfigDisplayInfo( 2, Buffer);
+ Error = NetRplConfigAdd( G_ServerHandle, 2, Buffer, &ErrorParameter);
+ if ( Error != NO_ERROR) {
+ printf( "Error = %d, ErrorParameter = %d\n", Error, ErrorParameter);
+ return;
+ }
+}
+
+
+VOID ConfigAdd( VOID)
+{
+ RPL_CONFIG_INFO_2 Info;
+
+ if ( !ReadString( "ConfigName", &Info.ConfigName, TRUE)) {
+ return;
+ }
+ if ( !ReadString( "BootName", &Info.BootName, TRUE)) {
+ return;
+ }
+ if ( !ReadString( "DirName", &Info.DirName, TRUE)) {
+ return;
+ }
+ if ( !ReadString( "FitShared", &Info.FitShared, TRUE)) {
+ return;
+ }
+ if ( !ReadString( "FitPersonal", &Info.FitPersonal, TRUE)) {
+ return;
+ }
+ Info.Flags = 0;
+ printf( "\tAll other parameters are optional\n");
+ if ( !ReadString( "ConfigComment", &Info.ConfigComment, FALSE)) {
+ return;
+ }
+ if ( !ReadString( "DirName2", &Info.DirName2, TRUE)) {
+ return;
+ }
+ if ( !ReadString( "DirName3", &Info.DirName3, TRUE)) {
+ return;
+ }
+ if ( !ReadString( "DirName4", &Info.DirName4, TRUE)) {
+ return;
+ }
+ TestConfigAdd( &Info);
+}
+
+
+VOID ConfigDel( VOID)
+{
+ PWCHAR ConfigName;
+ DWORD Error;
+
+ if ( !ReadString( "ConfigName", &ConfigName, FALSE)) {
+ return;
+ }
+ Error = NetRplConfigDel( G_ServerHandle, ConfigName);
+ if ( Error != 0) {
+ printf( "Failed to delete ConfigName=%ws, Error = %d\n", ConfigName, Error);
+ }
+}
+
+
+VOID TestVendorAdd( IN LPVOID Buffer)
+{
+ DWORD Error;
+ DWORD ErrorParameter;
+
+ printf( "\nTestVendorAdd\n");
+ VendorDisplayInfo( 1, Buffer);
+ Error = NetRplVendorAdd( G_ServerHandle, 1, Buffer, &ErrorParameter);
+ if ( Error != NO_ERROR) {
+ printf( "Error = %d, ErrorParameter = %d\n", Error, ErrorParameter);
+ return;
+ }
+}
+
+
+VOID VendorAdd( VOID)
+{
+ RPL_VENDOR_INFO_1 Info;
+
+ if ( !ReadString( "VendorName", &Info.VendorName, TRUE)) {
+ return;
+ }
+ Info.Flags = 0;
+ printf( "\tAll other parameters are optional\n");
+ if ( !ReadString( "VendorComment", &Info.VendorComment, FALSE)) {
+ return;
+ }
+ TestVendorAdd( &Info);
+}
+
+
+VOID VendorDel( VOID)
+{
+ PWCHAR VendorName;
+ DWORD Error;
+
+ if ( !ReadString( "VendorName", &VendorName, FALSE)) {
+ return;
+ }
+ Error = NetRplVendorDel( G_ServerHandle, VendorName);
+ if ( Error != 0) {
+ printf( "Failed to delete VendorName=%ws, Error = %d\n", VendorName, Error);
+ }
+}
+
+
+VOID TestVendorEnum(
+ IN DWORD Level,
+ IN DWORD PrefMaxLength,
+ IN PDWORD pResumeHandle
+ )
+{
+ LPBYTE Buffer;
+ DWORD EntriesRead;
+ DWORD TotalEntries;
+ DWORD CoreSize;
+ DWORD index;
+ DWORD Error;
+
+ switch( Level) {
+ case 0:
+ CoreSize = sizeof( RPL_VENDOR_INFO_0);
+ break;
+ case 1:
+ CoreSize = sizeof( RPL_VENDOR_INFO_1);
+ break;
+ default:
+ printf( "\nTestVendorEnum: invalid Level=%d", Level);
+ return;
+ break;
+ }
+
+ printf( "\nTestVendorEnum: Level=%d", Level);
+ if ( PrefMaxLength != RPL_BUFFER_GET_ALL) {
+ printf( ", PrefMaxLength=%ld", PrefMaxLength);
+ } else {
+ printf( ", unlimited buffer size");
+ }
+ if ( pResumeHandle != NULL) {
+ printf( ", ResumeHandle=0x%x\n\n", *pResumeHandle);
+ } else {
+ printf( ", not resumable.\n\n");
+ }
+
+ for ( ; ; ) {
+ Error = NetRplVendorEnum( G_ServerHandle, Level, &Buffer,
+ PrefMaxLength, &EntriesRead, &TotalEntries, pResumeHandle);
+
+ if ( Error != NO_ERROR && Error != ERROR_MORE_DATA) {
+ printf( "Error = %d\n", Error);
+ break;
+ }
+
+ printf( "Buffer = 0x%x, EntriesRead = %d, TotalEntries = %d", Buffer,
+ EntriesRead, TotalEntries);
+ if ( pResumeHandle != NULL) {
+ printf( ", ResumeHandle = 0x%x\n", *pResumeHandle);
+ } else {
+ printf("\n");
+ }
+
+ for ( index = 0; index < EntriesRead; index++) {
+ VendorDisplayInfo( Level, Buffer + index * CoreSize);
+ }
+ NetApiBufferFree( Buffer); // =~ MIDL_user_free()
+
+ if ( pResumeHandle == NULL) {
+ break;
+ }
+ if ( *pResumeHandle == 0) {
+ RPL_ASSERT( Error == NO_ERROR);
+ break;
+ }
+ RPL_ASSERT( Error == ERROR_MORE_DATA);
+ }
+}
+
+
+VOID VendorEnum( VOID)
+{
+ BYTE Line[ 300];
+ DWORD Count;
+ DWORD Level;
+ DWORD PrefMaxLength;
+ DWORD ResumeHandle;
+
+ printf( "Input: Level & PrefMaxLength\n");
+ if ( gets( Line) == NULL) {
+ return;
+ }
+ Count = sscanf( Line, "%d %d", &Level, &PrefMaxLength);
+ if ( Count != 2) {
+ printf( "Bad number of arguments.\n");
+ return;
+ }
+ if ( PrefMaxLength != RPL_BUFFER_GET_ALL) {
+ ResumeHandle = 0, TestVendorEnum( Level, PrefMaxLength, &ResumeHandle);
+ } else {
+ TestVendorEnum( Level, RPL_BUFFER_GET_ALL, NULL);
+ }
+}
+
+
+VOID TestProfileAdd( IN LPVOID Buffer)
+{
+ DWORD Error;
+ DWORD ErrorParameter;
+
+ printf( "\nTestProfileAdd");
+ ProfileDisplayInfo( 2, Buffer);
+ Error = NetRplProfileAdd( G_ServerHandle, 2, Buffer, &ErrorParameter);
+ if ( Error != NO_ERROR) {
+ printf( "Error = %d, ErrorParameter = %d\n", Error, ErrorParameter);
+ return;
+ }
+}
+
+
+VOID TestProfileGetInfo(
+ IN DWORD Level,
+ IN PWCHAR ProfileName
+ )
+{
+ LPBYTE Buffer;
+ DWORD Error;
+ printf( "\nTestProfileGetInfo: Level=%d, ProfileName=%ws\n", Level, ProfileName);
+ Error = NetRplProfileGetInfo( G_ServerHandle, ProfileName, Level, &Buffer);
+ if ( Error != NO_ERROR) {
+ printf( "Error = %d\n", Error);
+ return;
+ }
+ ProfileDisplayInfo( Level, Buffer);
+ NetApiBufferFree( Buffer); // =~ MIDL_user_free()
+}
+
+
+VOID TestProfileSetInfo(
+ IN DWORD Level,
+ IN PWCHAR ProfileName,
+ IN LPVOID Buffer
+ )
+{
+ DWORD Error;
+ DWORD ErrorParameter;
+
+ printf( "\nTestProfileSetInfo: Level=%d, ProfileName=%ws\n", Level, ProfileName);
+ ProfileDisplayInfo( Level, Buffer);
+ Error = NetRplProfileSetInfo( G_ServerHandle, ProfileName, Level, Buffer, &ErrorParameter);
+ if ( Error != NO_ERROR) {
+ printf( "Error = %d, ErrorParameter = %d\n", Error, ErrorParameter);
+ return;
+ }
+}
+
+
+VOID TestWkstaAdd( IN LPVOID Buffer)
+{
+ DWORD Error;
+ DWORD ErrorParameter;
+
+ printf( "\nTestWkstaAdd\n");
+ WkstaDisplayInfo( 2, Buffer);
+ Error = NetRplWkstaAdd( G_ServerHandle, 2, Buffer, &ErrorParameter);
+ if ( Error != NO_ERROR) {
+ printf( "Error = %d, ErrorParameter = %d\n", Error, ErrorParameter);
+ return;
+ }
+}
+
+
+VOID TestWkstaGetInfo(
+ IN DWORD Level,
+ IN PWCHAR WkstaName
+ )
+{
+ LPBYTE Buffer;
+ DWORD Error;
+ printf( "\nTestWkstaGetInfo: Level=%d, WkstaName=%ws\n", Level, WkstaName);
+ Error = NetRplWkstaGetInfo( G_ServerHandle, WkstaName, Level, &Buffer);
+ if ( Error != NO_ERROR) {
+ printf( "Error = %d\n", Error);
+ return;
+ }
+ WkstaDisplayInfo( Level, Buffer);
+ NetApiBufferFree( Buffer); // =~ MIDL_user_free()
+}
+
+
+VOID TestWkstaSetInfo(
+ IN DWORD Level,
+ IN PWCHAR WkstaName,
+ IN LPVOID Buffer
+ )
+{
+ DWORD Error;
+ DWORD ErrorParameter;
+
+ printf( "\nTestWkstaSetInfo: Level=%d, WkstaName=%ws\n", Level, WkstaName);
+ WkstaDisplayInfo( Level, Buffer);
+ Error = NetRplWkstaSetInfo( G_ServerHandle, WkstaName, Level, Buffer, &ErrorParameter);
+ if ( Error != NO_ERROR) {
+ printf( "Error = %d, ErrorParameter = %d\n", Error, ErrorParameter);
+ return;
+ }
+}
+
+
+VOID AdapterDel( VOID)
+{
+ PWCHAR AdapterName;
+ DWORD Error;
+
+ if ( !ReadString( "AdapterName", &AdapterName, FALSE)) {
+ return;
+ }
+ if ( AdapterName == NULL) {
+ printf( "You requested to delete all adapters!\n");
+ }
+
+ Error = NetRplAdapterDel( G_ServerHandle, AdapterName);
+ if ( Error != 0) {
+ printf( "Failed to delete AdapterName=%ws, Error = %d\n", AdapterName, Error);
+ }
+}
+
+
+VOID AdapterEnum( VOID)
+{
+ BYTE Line[ 300];
+ DWORD Count;
+ DWORD Level;
+ DWORD PrefMaxLength;
+ DWORD ResumeHandle;
+
+ printf( "Input: Level & PrefMaxLength\n");
+ if ( gets( Line) == NULL) {
+ return;
+ }
+ Count = sscanf( Line, "%d %d", &Level, &PrefMaxLength);
+ if ( Count != 2) {
+ printf( "Bad number of arguments.\n");
+ return;
+ }
+ if ( PrefMaxLength != RPL_BUFFER_GET_ALL) {
+ ResumeHandle = 0, TestAdapterEnum( Level, PrefMaxLength, &ResumeHandle);
+ } else {
+ TestAdapterEnum( Level, RPL_BUFFER_GET_ALL, NULL);
+ }
+}
+
+
+VOID ConfigEnum( VOID)
+{
+ BYTE Line[ 300];
+ DWORD Count;
+ DWORD Level;
+ DWORD PrefMaxLength;
+ DWORD ResumeHandle;
+ PWCHAR AdapterName;
+
+ printf( "Input: Level & PrefMaxLength\n");
+ if ( gets( Line) == NULL) {
+ return;
+ }
+ Count = sscanf( Line, "%d %d", &Level, &PrefMaxLength);
+ if ( Count != 2) {
+ printf( "Bad number of arguments.\n");
+ return;
+ }
+ if( !ReadString( "[filter] AdapterName", &AdapterName, FALSE)) {
+ return;
+ }
+ if ( PrefMaxLength != RPL_BUFFER_GET_ALL) {
+ ResumeHandle = 0, TestConfigEnum( Level, AdapterName, PrefMaxLength, &ResumeHandle);
+ } else {
+ TestConfigEnum( Level, AdapterName, RPL_BUFFER_GET_ALL, NULL);
+ }
+}
+
+
+VOID ProfileAdd( VOID)
+{
+ RPL_PROFILE_INFO_2 Info;
+
+ if ( !ReadString( "ProfileName", &Info.ProfileName, TRUE)) {
+ return;
+ }
+ if ( !ReadString( "ConfigName", &Info.ConfigName, TRUE)) {
+ return;
+ }
+ Info.Flags = 0;
+ printf( "\tAll other parameters are optional\n");
+ if ( !ReadString( "ProfileComment", &Info.ProfileComment, FALSE)) {
+ return;
+ }
+ if ( !ReadString( "BootName", &Info.BootName, FALSE)) {
+ return;
+ }
+ if ( !ReadString( "FitShared", &Info.FitShared, FALSE)) {
+ return;
+ }
+ if ( !ReadString( "FitPersonal", &Info.FitPersonal, FALSE)) {
+ return;
+ }
+ TestProfileAdd( &Info);
+}
+
+
+VOID ProfileClone( VOID)
+{
+ PWCHAR SourceProfileName;
+ PWCHAR TargetProfileName;
+ PWCHAR TargetProfileComment;
+ DWORD Error;
+
+ if ( !ReadString( "SourceProfileName", &SourceProfileName, TRUE)) {
+ return;
+ }
+ if ( !ReadString( "TargetProfileName", &TargetProfileName, TRUE)) {
+ return;
+ }
+ if ( !ReadString( "TargetProfileComment", &TargetProfileComment, FALSE)) {
+ return;
+ }
+ Error = NetRplProfileClone( G_ServerHandle, SourceProfileName,
+ TargetProfileName, TargetProfileComment);
+ if ( Error != NO_ERROR) {
+ printf( "Error = %d\n", Error);
+ return;
+ }
+}
+
+
+VOID ProfileDel( VOID)
+{
+ PWCHAR ProfileName;
+ DWORD Error;
+
+ if ( !ReadString( "ProfileName", &ProfileName, TRUE)) {
+ return;
+ }
+ Error = NetRplProfileDel( G_ServerHandle, ProfileName);
+ if ( Error != NO_ERROR) {
+ printf( "Error = %d\n", Error);
+ return;
+ }
+}
+
+
+VOID ProfileEnum( VOID)
+{
+ BYTE Line[ 300];
+ DWORD Count;
+ DWORD Level;
+ DWORD PrefMaxLength;
+ DWORD ResumeHandle;
+ PWCHAR AdapterName;
+
+ printf( "Input: Level & PrefMaxLength\n");
+ if ( gets( Line) == NULL) {
+ return;
+ }
+ Count = sscanf( Line, "%d %d", &Level, &PrefMaxLength);
+ if ( Count != 2) {
+ printf( "Bad number of arguments.\n");
+ return;
+ }
+ if( !ReadString( "[filter] AdapterName", &AdapterName, FALSE)) {
+ return;
+ }
+ if ( PrefMaxLength != RPL_BUFFER_GET_ALL) {
+ ResumeHandle = 0, TestProfileEnum( Level, AdapterName, PrefMaxLength, &ResumeHandle);
+ } else {
+ TestProfileEnum( Level, AdapterName, RPL_BUFFER_GET_ALL, NULL);
+ }
+}
+
+
+VOID ProfileGetInfo( VOID)
+{
+ BYTE Line[ 300];
+ WCHAR ProfileName[ 20];
+ CHAR ProfileNameA[ 20];
+ DWORD Length;
+ DWORD Count;
+ DWORD Level;
+
+ printf( "Input: Level & ProfileName\n");
+ if ( gets( Line) == NULL) {
+ return;
+ }
+ Count = sscanf( Line, "%d %s", &Level, ProfileNameA);
+ if ( Count != 2) {
+ printf( "Bad number of arguments.\n");
+ return;
+ }
+ Length = MultiByteToWideChar( CP_OEMCP, MB_PRECOMPOSED, ProfileNameA, -1,
+ ProfileName, sizeof( ProfileName));
+ if ( Length == 0) {
+ printf( "Invalid ProfileName = %s\n, ProfileNameA");
+ return;
+ }
+ TestProfileGetInfo( Level, ProfileName);
+}
+
+
+VOID ProfileSetInfo( VOID)
+{
+ PWCHAR ProfileName;
+ DWORD Level;
+ RPL_PROFILE_INFO_2 Info;
+ LPVOID Buffer;
+
+ Buffer = &Info;
+
+ if ( !ReadInt( "Level", &Level, TRUE) || Level > 2) {
+ return;
+ }
+ if ( !ReadString( "ProfileName", &ProfileName, TRUE)) {
+ return;
+ }
+ Info.ProfileName = NULL;
+ // ReadString( "ProfileName", &Info.ProfileName);
+ if ( !ReadString( "ProfileComment", &Info.ProfileComment, FALSE)) {
+ return;
+ }
+ if ( Level == 0) {
+ goto testit;
+ }
+ if ( !ReadInt( "Flags", &Info.Flags, TRUE)) {
+ return;
+ }
+ if ( Level == 1) {
+ goto testit;
+ }
+ Info.BootName = NULL;
+ // ReadString( "BootName", &Info.ProfileName);
+ if ( !ReadString( "FitShared", &Info.ProfileName, FALSE)) {
+ return;
+ }
+ if ( !ReadString( "FitPersonal", &Info.ProfileName, FALSE)) {
+ return;
+ }
+testit:
+ TestProfileSetInfo( Level, ProfileName, Buffer);
+}
+
+
+VOID ServiceClose( VOID)
+{
+ Call( NetRplClose( G_ServerHandle);)
+}
+
+
+VOID ServiceGetInfo( VOID)
+{
+ LPBYTE Buffer;
+ DWORD Error;
+ Error = NetRplGetInfo( G_ServerHandle, 0, &Buffer);
+ if ( Error != NO_ERROR) {
+ printf( "Error = %d\n", Error);
+ return;
+ }
+ ServiceDisplayInfo( 0, Buffer);
+ NetApiBufferFree( Buffer); // =~ MIDL_user_free()
+}
+
+
+VOID ServiceOpen( VOID)
+{
+ Call( NetRplOpen( G_ServerName, &G_ServerHandle);)
+}
+
+
+VOID ServiceSetInfo( VOID)
+{
+ DWORD Level;
+ LPVOID Buffer;
+ DWORD Error;
+ DWORD ErrorParameter;
+
+ if ( !ReadInt( "Level", &Level, FALSE)) {
+ return;
+ }
+
+ switch( Level) {
+ case 0: {
+ RPL_INFO_0 Info;
+ Buffer = &Info;
+ if ( !ReadInt( "Flags", &Info.Flags, TRUE)) {
+ return;
+ }
+ break;
+ }
+ default:
+ return;
+ break;
+ }
+ Error = NetRplSetInfo( G_ServerHandle, 0, Buffer, &ErrorParameter);
+ if ( Error != NO_ERROR) {
+ printf( "Error = %d, ErrorParameter = %d\n", Error, ErrorParameter);
+ return;
+ }
+}
+
+
+VOID WkstaAdd( VOID)
+{
+ RPL_WKSTA_INFO_2 Info;
+
+ if ( !ReadString( "WkstaName", &Info.WkstaName, TRUE)) {
+ return;
+ }
+ if ( !ReadString( "ProfileName", &Info.ProfileName, TRUE)) {
+ return;
+ }
+ if ( !ReadString( "AdapterName", &Info.AdapterName, TRUE)) {
+ return;
+ }
+ if ( !ReadInt( "Flags", &Info.Flags, TRUE)) {
+ return;
+ }
+ Info.Flags = 0;
+ printf( "\tAll other parameters are optional\n");
+ if ( !ReadString( "WkstaComment", &Info.WkstaComment, FALSE)) {
+ return;
+ }
+ if ( !ReadString( "BootName", &Info.BootName, FALSE)) {
+ return;
+ }
+ if ( !ReadString( "FitFile", &Info.FitFile, FALSE)) {
+ return;
+ }
+ Info.TcpIpAddress = (DWORD)-1;
+ if ( !ReadInt( "TcpIpAddress", &Info.TcpIpAddress, FALSE)) {
+ return;
+ }
+ Info.TcpIpSubnet = (DWORD)-1;
+ if ( !ReadInt( "TcpIpSubnet", &Info.TcpIpSubnet, FALSE)) {
+ return;
+ }
+ Info.TcpIpGateway = (DWORD)-1;
+ if ( !ReadInt( "TcpIpGateway", &Info.TcpIpGateway, FALSE)) {
+ return;
+ }
+ TestWkstaAdd( &Info);
+}
+
+
+VOID WkstaClone( VOID)
+{
+ PWCHAR SourceWkstaName;
+ PWCHAR TargetWkstaName;
+ PWCHAR TargetWkstaComment;
+ PWCHAR TargetAdapterName;
+ DWORD TargetTcpIpAddress;
+ DWORD Error;
+
+ if ( !ReadString( "SourceWkstaName", &SourceWkstaName, TRUE)) {
+ return;
+ }
+ if ( !ReadString( "TargetWkstaName", &TargetWkstaName, TRUE)) {
+ return;
+ }
+ if ( !ReadString( "TargetAdapterName", &TargetAdapterName, TRUE)) {
+ return;
+ }
+ printf( "\tAll other parameters are optional\n");
+ if ( !ReadString( "TargetWkstaComment", &TargetWkstaComment, FALSE)) {
+ return;
+ }
+ TargetTcpIpAddress = (DWORD)-1;
+ if( !ReadInt( "TcpIpAddress", &TargetTcpIpAddress, FALSE)) {
+ return;
+ }
+ Error = NetRplWkstaClone( G_ServerHandle, SourceWkstaName,
+ TargetWkstaName, TargetWkstaComment, TargetAdapterName, TargetTcpIpAddress);
+ if ( Error != NO_ERROR) {
+ printf( "Error = %d\n", Error);
+ return;
+ }
+}
+
+
+VOID WkstaDel( VOID)
+{
+ PWCHAR WkstaName;
+ DWORD Error;
+
+ if ( !ReadString( "WkstaName", &WkstaName, TRUE)) {
+ return;
+ }
+ Error = NetRplWkstaDel( G_ServerHandle, WkstaName);
+ if ( Error != NO_ERROR) {
+ printf( "Error = %d\n", Error);
+ return;
+ }
+}
+
+
+VOID WkstaEnum( VOID)
+{
+ BYTE Line[ 300];
+ DWORD Count;
+ DWORD Level;
+ DWORD PrefMaxLength;
+ DWORD ResumeHandle;
+ PWCHAR ProfileName;
+
+ printf( "Input: Level, PrefMaxLength\n");
+ if ( gets( Line) == NULL) {
+ return;
+ }
+ Count = sscanf( Line, "%d %d", &Level, &PrefMaxLength);
+ if ( Count != 2) {
+ printf( "Bad number of arguments.\n");
+ return;
+ }
+ if( !ReadString( "[filter] ProfileName", &ProfileName, FALSE)) {
+ return;
+ }
+ ResumeHandle = 0, TestWkstaEnum( Level, ProfileName, PrefMaxLength, &ResumeHandle);
+}
+
+
+VOID WkstaGetInfo( VOID)
+{
+ BYTE Line[ 300];
+ WCHAR WkstaName[ 20];
+ CHAR WkstaNameA[ 20];
+ DWORD Length;
+ DWORD Count;
+ DWORD Level;
+
+ printf( "Input: Level & WkstaName\n");
+ if ( gets( Line) == NULL) {
+ return;
+ }
+ Count = sscanf( Line, "%d %s", &Level, WkstaNameA);
+ if ( Count != 2) {
+ printf( "Bad number of arguments.\n");
+ return;
+ }
+ Length = MultiByteToWideChar( CP_OEMCP, MB_PRECOMPOSED, WkstaNameA, -1,
+ WkstaName, sizeof( WkstaName));
+ if ( Length == 0) {
+ printf( "Invalid WkstaName = %s\n, WkstaNameA");
+ return;
+ }
+ TestWkstaGetInfo( Level, WkstaName);
+}
+
+
+VOID WkstaSetInfo( VOID)
+{
+ PWCHAR WkstaName;
+ DWORD Level;
+ LPVOID Buffer;
+
+ if ( !ReadInt( "Level", &Level, FALSE)) {
+ return;
+ }
+ if ( !ReadString( "WkstaName", &WkstaName, FALSE)) {
+ return;
+ }
+
+ switch( Level) {
+ case 0: {
+ RPL_WKSTA_INFO_0 Info;
+ Buffer = &Info;
+ Info.WkstaName = NULL;
+ // ReadString( "WkstaName", &Info.WkstaName);
+ if( !ReadString( "WkstaComment", &Info.WkstaComment, FALSE)) {
+ return;
+ }
+ break;
+ }
+ case 1: {
+ RPL_WKSTA_INFO_1 Info;
+ Buffer = &Info;
+ Info.WkstaName = NULL;
+ // ReadString( "WkstaName", &Info.WkstaName);
+ if( !ReadString( "WkstaComment", &Info.WkstaComment, FALSE)) {
+ return;
+ }
+ if( !ReadString( "ProfileName", &Info.ProfileName, FALSE)) {
+ return;
+ }
+ break;
+ }
+ case 2: {
+ RPL_WKSTA_INFO_2 Info;
+ Buffer = &Info;
+ Info.WkstaName = NULL;
+ // ReadString( "WkstaName", &Info.WkstaName);
+ if( !ReadString( "WkstaComment", &Info.WkstaComment, FALSE)) {
+ return;
+ }
+ if( !ReadString( "ProfileName", &Info.ProfileName, FALSE)) {
+ return;
+ }
+ break;
+ }
+ default:
+ return;
+ break;
+ }
+ TestWkstaSetInfo( Level, WkstaName, Buffer);
+}
+
+
+VOID Worker( VOID)
+{
+ BYTE Line[ 300];
+ CHAR response;
+
+ for ( ; ;) {
+ printf("Adapter Boot Config Profile Service Vendor Wksta [Quit]: ");
+ if ( gets( Line) == NULL) {
+ return;
+ }
+ sscanf( Line, " %1c", &response);
+ switch( toupper(response)) {
+ case 'A':
+ printf( " Add Del Enum: ");
+ if ( gets( Line) == NULL) {
+ return;
+ }
+ sscanf( Line, " %1c", &response);
+ switch( toupper(response)) {
+ case 'A':
+ AdapterAdd();
+ break;
+ case 'D':
+ AdapterDel();
+ break;
+ case 'E':
+ AdapterEnum();
+ break;
+ }
+ break;
+ case 'B':
+ printf( " Add Del Enum: ");
+ if ( gets( Line) == NULL) {
+ return;
+ }
+ sscanf( Line, " %1c", &response);
+ switch( toupper(response)) {
+ case 'A':
+ BootAdd();
+ break;
+ case 'D':
+ BootDel();
+ break;
+ case 'E':
+ BootEnum();
+ break;
+ }
+ break;
+ case 'C':
+ printf( " Add Del Enum: ");
+ if ( gets( Line) == NULL) {
+ return;
+ }
+ sscanf( Line, " %1c", &response);
+ switch( toupper(response)) {
+ case 'A':
+ ConfigAdd();
+ break;
+ case 'D':
+ ConfigDel();
+ break;
+ case 'E':
+ ConfigEnum();
+ break;
+ }
+ break;
+ case 'P':
+ printf( " Add Clone Del Enum GetInfo SetInfo: ");
+ if ( gets( Line) == NULL) {
+ return;
+ }
+ sscanf( Line, " %1c", &response);
+ switch( toupper(response)) {
+ case 'A':
+ ProfileAdd();
+ break;
+ case 'C':
+ ProfileClone();
+ break;
+ case 'D':
+ ProfileDel();
+ break;
+ case 'E':
+ ProfileEnum();
+ break;
+ case 'G':
+ ProfileGetInfo();
+ break;
+ case 'S':
+ ProfileSetInfo();
+ break;
+ }
+ break;
+ case 'S':
+ printf( " Close GetInfo Open SetInfo: ");
+ if ( gets( Line) == NULL) {
+ return;
+ }
+ sscanf( Line, " %1c", &response);
+ switch( toupper(response)) {
+ case 'C':
+ ServiceClose();
+ break;
+ case 'G':
+ ServiceGetInfo();
+ break;
+ case 'O':
+ ServiceOpen();
+ break;
+ case 'S':
+ ServiceSetInfo();
+ break;
+ }
+ break;
+ case 'V':
+ printf( " Add Del Enum: ");
+ if ( gets( Line) == NULL) {
+ return;
+ }
+ sscanf( Line, " %1c", &response);
+ switch( toupper(response)) {
+ case 'A':
+ VendorAdd();
+ break;
+ case 'D':
+ VendorDel();
+ break;
+ case 'E':
+ VendorEnum();
+ break;
+ }
+ break;
+ case 'W':
+ printf( " Add Clone Del Enum GetInfo SetInfo: ");
+ if ( gets( Line) == NULL) {
+ return;
+ }
+ sscanf( Line, " %1c", &response);
+ switch( toupper(response)) {
+ case 'A':
+ WkstaAdd();
+ break;
+ case 'C':
+ WkstaClone();
+ break;
+ case 'D':
+ WkstaDel();
+ break;
+ case 'E':
+ WkstaEnum();
+ break;
+ case 'G':
+ WkstaGetInfo();
+ break;
+ case 'S':
+ WkstaSetInfo();
+ break;
+ }
+ break;
+ case 'Q':
+ return;
+ break;
+ default:
+ printf( "Your input '%1c' is invalid. Try again.\n", response);
+ break;
+ }
+ }
+}
+
+
+DWORD _CRTAPI1 main( int argc, char **argv)
+{
+ DWORD ResumeHandle;
+
+ if ( argc == 1) {
+ G_ServerName = NULL;
+ } else if ( argc == 2) {
+ WCHAR Buffer[ CNLEN + 1];
+ DWORD Length;
+ Length = MultiByteToWideChar(
+ CP_OEMCP,
+ MB_PRECOMPOSED,
+ argv[ 1],
+ -1,
+ Buffer,
+ sizeof( Buffer)
+ );
+ if ( Length == 0) {
+ printf( "Invalid ServerName\n");
+ return( 1);
+ }
+ G_ServerName = Buffer;
+ printf( "ServerName = %ws\n", G_ServerName);
+ } else {
+ printf( "Usage: RplApi [ServerName]\n");
+ return( 1);
+ }
+
+ TestConnect();
+
+ Call( NetRplOpen( G_ServerName, &G_ServerHandle);)
+ printf( "G_ServerHandle = 0x%x\n", G_ServerHandle);
+
+// #define NOT_YET
+#ifdef NOT_YET
+ for ( ; ; ) {
+ TestConfigEnum( 0, NULL, RPL_BUFFER_GET_ALL, NULL);
+ ResumeHandle = 0, NULL, TestConfigEnum( 1, NULL, 400, &ResumeHandle);
+ ResumeHandle = 0, NULL, TestConfigEnum( 2, NULL, 10, &ResumeHandle);
+
+ TestConfigEnum( 2, NULL, RPL_BUFFER_GET_ALL, NULL);
+ TestConfigEnum( 1, NULL, 500, NULL);
+ ResumeHandle = 0, TestConfigEnum( 0, NULL, 500, &ResumeHandle);
+
+ TestProfileEnum( 2, NULL, RPL_BUFFER_GET_ALL, NULL);
+ ResumeHandle = 0, TestProfileEnum( 0, NULL, 10, &ResumeHandle);
+ ResumeHandle = 0, TestProfileEnum( 1, NULL, 30, &ResumeHandle);
+ ResumeHandle = 0, TestProfileEnum( 2, NULL, 50, &ResumeHandle);
+
+ TestWkstaEnum( 2, NULL, RPL_BUFFER_GET_ALL, NULL);
+ ResumeHandle = 0, TestWkstaEnum( 0, NULL, 10, &ResumeHandle);
+ ResumeHandle = 0, TestWkstaEnum( 1, NULL, 30, &ResumeHandle);
+ ResumeHandle = 0, TestWkstaEnum( 2, L"elnk2", 30, &ResumeHandle);
+ ResumeHandle = 0, TestWkstaEnum( 1, L"xxx", 30, &ResumeHandle);
+
+ ResumeHandle = 0, TestAdapterEnum( 0, 30, &ResumeHandle);
+
+ TestWkstaGetInfo( 0, L"elnk2_");
+ TestWkstaGetInfo( 0, L"3sta_");
+ }
+#endif
+
+ Worker();
+
+ Call( NetRplClose( G_ServerHandle);)
+ return(0);
+}
diff --git a/private/net/svcdlls/rpl/client/rplbind.c b/private/net/svcdlls/rpl/client/rplbind.c
new file mode 100644
index 000000000..9a89ee7b4
--- /dev/null
+++ b/private/net/svcdlls/rpl/client/rplbind.c
@@ -0,0 +1,119 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ rplbind.c
+
+Abstract:
+
+ Contains the RPC bind and un-bind routines for the Remote (Initial)
+ Program Load service.
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 27 - July - 1993
+
+Environment:
+
+ User Mode -Win32
+
+Revision History:
+
+
+--*/
+
+//
+// INCLUDES
+//
+#include <nt.h> // DbgPrint prototype
+#include <rpc.h> // DataTypes and runtime APIs
+#include <rplsvc_c.h> // generated by the MIDL complier
+#include <rpcutil.h> // NetRpc utils
+#include <netlib.h> // UNUSED macro
+#include <rplnames.h> // RPL_INTERFACE_NAME
+
+
+
+/****************************************************************************/
+handle_t
+RPL_NAME_bind (
+ RPL_NAME ServerName
+ )
+/*++
+
+Routine Description:
+ This routine calls a common bind routine that is shared by all services.
+ This routine is called from the remote boot service client stubs when
+ it is necessary to bind to a server.
+
+Arguments:
+
+ ServerName - A pointer to a string containing the name of the server
+ to bind with.
+
+Return Value:
+
+ The binding handle is returned to the stub routine. If the
+ binding is unsuccessful, a NULL will be returned.
+
+--*/
+{
+ handle_t bindingHandle;
+ RPC_STATUS status;
+
+ status = NetpBindRpc (
+ ServerName,
+ RPL_INTERFACE_NAME,
+ 0,
+ &bindingHandle);
+
+#ifdef DEBUG
+ DbgPrint("RPLSVC_HANDLE_bind:NetpBindRpc status=%d\n",status);
+ DbgPrint("RPLSVC_HANDLE_bind: handle=%d\n",bindingHandle);
+#endif
+
+ return( bindingHandle);
+}
+
+
+
+/****************************************************************************/
+void
+RPL_NAME_unbind (
+ RPL_NAME ServerName,
+ handle_t BindingHandle
+ )
+/*++
+
+Routine Description:
+
+ This routine calls a common unbind routine that is shared by
+ all services.
+ This routine is called from the remote boot service client stubs when
+ it is necessary to unbind to a server.
+
+
+Arguments:
+
+ ServerName - This is the name of the server from which to unbind.
+
+ BindingHandle - This is the binding handle that is to be closed.
+
+Return Value:
+
+ none.
+
+--*/
+{
+ UNUSED(ServerName); // This parameter is not used
+
+#ifdef DEBUG
+ DbgPrint("RPLSVC_HANDLE_unbind: handle=%d\n",BindingHandle);
+#endif
+
+ NetpUnbindRpc ( BindingHandle);
+ return;
+}
+
diff --git a/private/net/svcdlls/rpl/client/rplstub.c b/private/net/svcdlls/rpl/client/rplstub.c
new file mode 100644
index 000000000..89d91f3de
--- /dev/null
+++ b/private/net/svcdlls/rpl/client/rplstub.c
@@ -0,0 +1,1279 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ rplstub.c
+
+Abstract:
+
+ These are the Remote (Initial) Program Load Service API RPC client stubs.
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 27 - July - 1993
+
+Environment:
+
+ User Mode - Win32
+
+Revision History:
+
+ 27-Jul-1993 vladimv
+ Created
+
+--*/
+
+//
+// INCLUDES
+//
+
+#include <nt.h> // DbgPrint prototype
+#include <ntrtl.h> // DbgPrint prototype
+#include <nturtl.h> // Needed by winbase.h
+
+#include <windef.h> // DWORD
+#include <winbase.h> // EXCEPTION_STATUS_VIOLATION
+
+#include <rpc.h> // DataTypes and runtime APIs
+#include <rpcutil.h> // NetRpc utils
+
+#include <lmsvc.h>
+#include <lmcons.h> // NET_API_STATUS
+#include <lmerr.h> // NetError codes
+
+#include <netlib.h> // NetpServiceIsStarted() (needed by netrpc.h).
+#include <netdebug.h> // needed for netrpc.h
+#include <netrpc.h> // NET_REMOTE macros.
+
+#include <rplsvc_c.h> // generated by the MIDL complier
+
+DWORD RplMapRpcError(
+ IN DWORD RpcError,
+ IN DWORD BadContextError
+ );
+
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetRplOpen(
+ IN LPTSTR ServerName,
+ OUT LPRPL_HANDLE ServerHandle
+ )
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+{
+ NET_API_STATUS status = ERROR_SUCCESS;
+
+ RpcTryExcept {
+ *ServerHandle = NULL; // needed for RPC to create new context handle
+ status = NetrRplOpen( ServerName, (LPRPL_RPC_HANDLE)ServerHandle);
+ }
+ RpcExcept( EXCEPTION_EXECUTE_HANDLER) {
+ status = RplMapRpcError( RpcExceptionCode(), ERROR_INVALID_HANDLE);
+ }
+ RpcEndExcept
+
+ if (status != NO_ERROR){
+ ServerHandle = NULL;
+ }
+
+ return( status);
+}
+
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetRplClose(
+ IN RPL_HANDLE ServerHandle
+ )
+/*++
+
+Routine Description:
+
+Arguments:
+
+Return Value:
+
+--*/
+{
+ NET_API_STATUS status;
+
+ RpcTryExcept {
+ status = NetrRplClose( (LPRPL_RPC_HANDLE)&ServerHandle);
+ }
+ RpcExcept( EXCEPTION_EXECUTE_HANDLER) {
+ status = RplMapRpcError( RpcExceptionCode(), ERROR_INVALID_HANDLE);
+ }
+ RpcEndExcept
+
+ return( status);
+}
+
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetRplGetInfo(
+ IN RPL_HANDLE ServerHandle,
+ IN DWORD InfoLevel,
+ OUT LPBYTE * PointerToBuffer
+ )
+/*++
+
+Routine Description:
+
+ This is the DLL entrypoint for NetRplGetInfo.
+
+Arguments:
+
+ ServerHandle - Handle obtained through NetRplOpen().
+
+ InfoLevel - This indicates the level of information that is desired.
+
+ PointerToBuffer - Pointer to a Location where the pointer to the returned
+ information structure is to be placed.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NET_API_STATUS status;
+
+ RpcTryExcept {
+ *PointerToBuffer = NULL; // Must be NULL so RPC knows to fill it in.
+ status = NetrRplGetInfo(
+ (RPL_RPC_HANDLE)ServerHandle,
+ InfoLevel,
+ (LPRPL_INFO_STRUCT)PointerToBuffer
+ );
+ }
+ RpcExcept( EXCEPTION_EXECUTE_HANDLER) {
+ status = RplMapRpcError( RpcExceptionCode(), ERROR_INVALID_HANDLE);
+ }
+ RpcEndExcept
+
+ return( status);
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetRplSetInfo(
+ IN RPL_HANDLE ServerHandle,
+ IN DWORD InfoLevel,
+ IN LPBYTE Buffer,
+ OUT LPDWORD ErrorParameter OPTIONAL
+ )
+/*++
+
+Routine Description:
+
+ This is the DLL entrypoint for NetRplSetInfo.
+
+Arguments:
+
+ ServerHandle - Handle obtained through NetRplOpen().
+
+ InfoLevel - This indicates the level of information that is desired.
+
+ Buffer - Pointer to the information structure to be set. InfoLevel describes
+ the structure in this buffer.
+
+ ErrorParameter - - Returns the identifier to the invalid parameter in Buffer if this
+ function returns ERROR_INVALID_PARAMETER.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NET_API_STATUS status;
+
+ RpcTryExcept {
+ status = NetrRplSetInfo(
+ (RPL_RPC_HANDLE)ServerHandle,
+ InfoLevel,
+ (LPRPL_INFO_STRUCT)&Buffer,
+ ErrorParameter
+ );
+ }
+ RpcExcept( EXCEPTION_EXECUTE_HANDLER) {
+ status = RplMapRpcError( RpcExceptionCode(), ERROR_INVALID_HANDLE);
+ }
+ RpcEndExcept
+
+ return( status);
+}
+
+
+//
+// BOOT block apis
+//
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetRplBootAdd(
+ IN RPL_HANDLE ServerHandle,
+ IN DWORD InfoLevel,
+ IN LPBYTE Buffer,
+ OUT LPDWORD ErrorParameter OPTIONAL
+ )
+/*++
+
+Routine Description:
+
+Arguments:
+
+Return Value:
+
+--*/
+{
+ NET_API_STATUS status;
+
+ RpcTryExcept {
+ status = NetrRplBootAdd(
+ (RPL_RPC_HANDLE)ServerHandle,
+ InfoLevel,
+ (LPRPL_BOOT_INFO_STRUCT)&Buffer,
+ ErrorParameter
+ );
+ }
+ RpcExcept( EXCEPTION_EXECUTE_HANDLER) {
+ status = RplMapRpcError( RpcExceptionCode(), ERROR_INVALID_HANDLE);
+ }
+ RpcEndExcept
+
+ return( status);
+}
+
+//
+// NetRplBootDel: if VendorName is NULL, then all boot block records with
+// given BootName will be deleted.
+//
+
+NET_API_STATUS NET_API_FUNCTION
+NetRplBootDel(
+ IN RPL_HANDLE ServerHandle,
+ IN LPTSTR BootName,
+ IN LPTSTR VendorName
+ )
+/*++
+
+Routine Description:
+
+Arguments:
+
+Return Value:
+
+--*/
+{
+ NET_API_STATUS status;
+
+ RpcTryExcept {
+ status = NetrRplBootDel(
+ (RPL_RPC_HANDLE)ServerHandle,
+ BootName,
+ VendorName
+ );
+ }
+ RpcExcept( EXCEPTION_EXECUTE_HANDLER) {
+ status = RplMapRpcError( RpcExceptionCode(), ERROR_INVALID_HANDLE);
+ }
+ RpcEndExcept
+
+ return( status);
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetRplBootEnum(
+ IN RPL_HANDLE ServerHandle,
+ IN DWORD InfoLevel,
+ OUT LPBYTE * PointerToBuffer,
+ IN DWORD PrefMaxLength,
+ OUT LPDWORD EntriesRead,
+ OUT LPDWORD TotalEntries,
+ OUT LPDWORD ResumeHandle
+ )
+/*++
+
+Routine Description:
+
+Arguments:
+
+Return Value:
+
+--*/
+{
+ NET_API_STATUS status;
+ GENERIC_ENUM_STRUCT InfoStruct;
+ GENERIC_INFO_CONTAINER GenericInfoContainer;
+
+ GenericInfoContainer.Buffer = NULL;
+ GenericInfoContainer.EntriesRead = 0;
+
+ InfoStruct.Container = &GenericInfoContainer;
+ InfoStruct.Level = InfoLevel;
+
+ RpcTryExcept {
+ status = NetrRplBootEnum(
+ (RPL_RPC_HANDLE)ServerHandle,
+ (LPRPL_BOOT_ENUM)&InfoStruct,
+ PrefMaxLength,
+ TotalEntries,
+ ResumeHandle
+ );
+ if ( status == NERR_Success || status == ERROR_MORE_DATA) {
+ *PointerToBuffer = (LPBYTE)GenericInfoContainer.Buffer;
+ *EntriesRead = GenericInfoContainer.EntriesRead;
+ }
+ }
+ RpcExcept( EXCEPTION_EXECUTE_HANDLER) {
+ status = RplMapRpcError( RpcExceptionCode(), ERROR_INVALID_HANDLE);
+ }
+ RpcEndExcept
+
+ return( status);
+}
+
+//
+// CONFIG apis
+//
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetRplConfigAdd(
+ IN RPL_HANDLE ServerHandle,
+ IN DWORD InfoLevel,
+ IN LPBYTE Buffer,
+ OUT LPDWORD ErrorParameter OPTIONAL
+ )
+/*++
+
+Routine Description:
+
+Arguments:
+
+Return Value:
+
+--*/
+{
+ NET_API_STATUS status;
+
+ RpcTryExcept {
+ status = NetrRplConfigAdd(
+ (RPL_RPC_HANDLE)ServerHandle,
+ InfoLevel,
+ (LPRPL_CONFIG_INFO_STRUCT)&Buffer,
+ ErrorParameter
+ );
+ }
+ RpcExcept( EXCEPTION_EXECUTE_HANDLER) {
+ status = RplMapRpcError( RpcExceptionCode(), ERROR_INVALID_HANDLE);
+ }
+ RpcEndExcept
+
+ return( status);
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetRplConfigDel(
+ IN RPL_HANDLE ServerHandle,
+ IN LPTSTR ConfigName
+ )
+/*++
+
+Routine Description:
+
+Arguments:
+
+Return Value:
+
+--*/
+{
+ NET_API_STATUS status;
+
+ RpcTryExcept {
+ status = NetrRplConfigDel(
+ (RPL_RPC_HANDLE)ServerHandle,
+ ConfigName
+ );
+ }
+ RpcExcept( EXCEPTION_EXECUTE_HANDLER) {
+ status = RplMapRpcError( RpcExceptionCode(), ERROR_INVALID_HANDLE);
+ }
+ RpcEndExcept
+
+ return( status);
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetRplConfigEnum(
+ IN RPL_HANDLE ServerHandle,
+ IN LPTSTR AdapterName,
+ IN DWORD InfoLevel,
+ OUT LPBYTE * PointerToBuffer,
+ IN DWORD PrefMaxLength,
+ OUT LPDWORD EntriesRead,
+ OUT LPDWORD TotalEntries,
+ OUT LPDWORD ResumeHandle
+ )
+/*++
+
+Routine Description:
+
+Arguments:
+
+Return Value:
+
+--*/
+{
+ NET_API_STATUS status;
+ GENERIC_ENUM_STRUCT InfoStruct;
+ GENERIC_INFO_CONTAINER GenericInfoContainer;
+
+ GenericInfoContainer.Buffer = NULL;
+ GenericInfoContainer.EntriesRead = 0;
+
+ InfoStruct.Container = &GenericInfoContainer;
+ InfoStruct.Level = InfoLevel;
+
+ RpcTryExcept {
+ status = NetrRplConfigEnum(
+ (RPL_RPC_HANDLE)ServerHandle,
+ AdapterName,
+ (LPRPL_CONFIG_ENUM)&InfoStruct,
+ PrefMaxLength,
+ TotalEntries,
+ ResumeHandle
+ );
+ if ( status == NERR_Success || status == ERROR_MORE_DATA) {
+ *PointerToBuffer = (LPBYTE)GenericInfoContainer.Buffer;
+ *EntriesRead = GenericInfoContainer.EntriesRead;
+ }
+ }
+ RpcExcept( EXCEPTION_EXECUTE_HANDLER) {
+ status = RplMapRpcError( RpcExceptionCode(), ERROR_INVALID_HANDLE);
+ }
+ RpcEndExcept
+
+ return( status);
+}
+
+
+//
+// PROFILE apis
+//
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetRplProfileAdd(
+ IN RPL_HANDLE ServerHandle,
+ IN DWORD InfoLevel,
+ IN LPBYTE Buffer,
+ OUT LPDWORD ErrorParameter OPTIONAL
+ )
+/*++
+
+Routine Description:
+
+Arguments:
+
+Return Value:
+
+--*/
+{
+ NET_API_STATUS status;
+
+ RpcTryExcept {
+ status = NetrRplProfileAdd(
+ (RPL_RPC_HANDLE)ServerHandle,
+ InfoLevel,
+ (LPRPL_PROFILE_INFO_STRUCT)&Buffer,
+ ErrorParameter
+ );
+ }
+ RpcExcept( EXCEPTION_EXECUTE_HANDLER) {
+ status = RplMapRpcError( RpcExceptionCode(), ERROR_INVALID_HANDLE);
+ }
+ RpcEndExcept
+
+ return( status);
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetRplProfileClone(
+ IN RPL_HANDLE ServerHandle,
+ IN LPTSTR SourceProfileName,
+ IN LPTSTR TargetProfileName,
+ IN LPTSTR TargetProfileComment OPTIONAL
+ )
+/*++
+
+Routine Description:
+
+Arguments:
+
+Return Value:
+
+--*/
+{
+ NET_API_STATUS status;
+
+ RpcTryExcept {
+ status = NetrRplProfileClone(
+ (RPL_RPC_HANDLE)ServerHandle,
+ SourceProfileName,
+ TargetProfileName,
+ TargetProfileComment
+ );
+ }
+ RpcExcept( EXCEPTION_EXECUTE_HANDLER) {
+ status = RplMapRpcError( RpcExceptionCode(), ERROR_INVALID_HANDLE);
+ }
+ RpcEndExcept
+
+ return( status);
+}
+
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetRplProfileDel(
+ IN RPL_HANDLE ServerHandle,
+ IN LPTSTR ProfileName
+ )
+/*++
+
+Routine Description:
+
+Arguments:
+
+Return Value:
+
+--*/
+{
+ NET_API_STATUS status;
+
+ RpcTryExcept {
+ status = NetrRplProfileDel(
+ (RPL_RPC_HANDLE)ServerHandle,
+ ProfileName
+ );
+ }
+ RpcExcept( EXCEPTION_EXECUTE_HANDLER) {
+ status = RplMapRpcError( RpcExceptionCode(), ERROR_INVALID_HANDLE);
+ }
+ RpcEndExcept
+
+ return( status);
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetRplProfileEnum(
+ IN RPL_HANDLE ServerHandle,
+ IN LPTSTR AdapterName,
+ IN DWORD InfoLevel,
+ OUT LPBYTE * PointerToBuffer,
+ IN DWORD PrefMaxLength,
+ OUT LPDWORD EntriesRead,
+ OUT LPDWORD TotalEntries,
+ OUT LPDWORD ResumeHandle
+ )
+/*++
+
+Routine Description:
+
+Arguments:
+
+Return Value:
+
+--*/
+{
+ NET_API_STATUS status;
+ GENERIC_ENUM_STRUCT InfoStruct;
+ GENERIC_INFO_CONTAINER GenericInfoContainer;
+
+ GenericInfoContainer.Buffer = NULL;
+ GenericInfoContainer.EntriesRead = 0;
+
+ InfoStruct.Container = &GenericInfoContainer;
+ InfoStruct.Level = InfoLevel;
+
+ RpcTryExcept {
+ status = NetrRplProfileEnum(
+ (RPL_RPC_HANDLE)ServerHandle,
+ AdapterName,
+ (LPRPL_PROFILE_ENUM)&InfoStruct,
+ PrefMaxLength,
+ TotalEntries,
+ ResumeHandle
+ );
+ if ( status == NERR_Success || status == ERROR_MORE_DATA) {
+ *PointerToBuffer = (LPBYTE)GenericInfoContainer.Buffer;
+ *EntriesRead = GenericInfoContainer.EntriesRead;
+ }
+ }
+ RpcExcept( EXCEPTION_EXECUTE_HANDLER) {
+ status = RplMapRpcError( RpcExceptionCode(), ERROR_INVALID_HANDLE);
+ }
+ RpcEndExcept
+
+ return( status);
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetRplProfileGetInfo(
+ IN RPL_HANDLE ServerHandle,
+ IN LPTSTR ProfileName,
+ IN DWORD InfoLevel,
+ OUT LPBYTE * PointerToBuffer
+ )
+/*++
+
+Routine Description:
+
+Arguments:
+
+Return Value:
+
+--*/
+{
+ NET_API_STATUS status;
+
+ RpcTryExcept {
+ *PointerToBuffer = NULL; // Must be NULL so RPC knows to fill it in.
+ status = NetrRplProfileGetInfo(
+ (RPL_RPC_HANDLE)ServerHandle,
+ ProfileName,
+ InfoLevel,
+ (LPRPL_PROFILE_INFO_STRUCT)PointerToBuffer
+ );
+ }
+ RpcExcept( EXCEPTION_EXECUTE_HANDLER) {
+ status = RplMapRpcError( RpcExceptionCode(), ERROR_INVALID_HANDLE);
+ }
+ RpcEndExcept
+
+ return( status);
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetRplProfileSetInfo(
+ IN RPL_HANDLE ServerHandle,
+ IN LPTSTR ProfileName,
+ IN DWORD InfoLevel,
+ IN LPBYTE Buffer,
+ OUT LPDWORD ErrorParameter OPTIONAL
+ )
+/*++
+
+Routine Description:
+
+Arguments:
+
+Return Value:
+
+--*/
+{
+ NET_API_STATUS status;
+
+ RpcTryExcept {
+ status = NetrRplProfileSetInfo(
+ (RPL_RPC_HANDLE)ServerHandle,
+ ProfileName,
+ InfoLevel,
+ (LPRPL_PROFILE_INFO_STRUCT)&Buffer,
+ ErrorParameter
+ );
+ }
+ RpcExcept( EXCEPTION_EXECUTE_HANDLER) {
+ status = RplMapRpcError( RpcExceptionCode(), ERROR_INVALID_HANDLE);
+ }
+ RpcEndExcept
+
+ return( status);
+}
+
+//
+// VENDOR apis
+//
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetRplVendorAdd(
+ IN RPL_HANDLE ServerHandle,
+ IN DWORD InfoLevel,
+ IN LPBYTE Buffer,
+ OUT LPDWORD ErrorParameter OPTIONAL
+ )
+{
+ NET_API_STATUS status;
+
+ RpcTryExcept {
+ status = NetrRplVendorAdd(
+ (RPL_RPC_HANDLE)ServerHandle,
+ InfoLevel,
+ (LPRPL_VENDOR_INFO_STRUCT)&Buffer,
+ ErrorParameter
+ );
+ }
+ RpcExcept( EXCEPTION_EXECUTE_HANDLER) {
+ status = RplMapRpcError( RpcExceptionCode(), ERROR_INVALID_HANDLE);
+ }
+ RpcEndExcept
+
+ return( status);
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetRplVendorDel(
+ IN RPL_HANDLE ServerHandle,
+ IN LPTSTR VendorName
+ )
+/*++
+
+Routine Description:
+
+Arguments:
+
+Return Value:
+
+--*/
+{
+ NET_API_STATUS status;
+
+ RpcTryExcept {
+ status = NetrRplVendorDel(
+ (RPL_RPC_HANDLE)ServerHandle,
+ VendorName
+ );
+ }
+ RpcExcept( EXCEPTION_EXECUTE_HANDLER) {
+ status = RplMapRpcError( RpcExceptionCode(), ERROR_INVALID_HANDLE);
+ }
+ RpcEndExcept
+
+ return( status);
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetRplVendorEnum(
+ IN RPL_HANDLE ServerHandle,
+ IN DWORD InfoLevel,
+ OUT LPBYTE * PointerToBuffer,
+ IN DWORD PrefMaxLength,
+ OUT LPDWORD EntriesRead,
+ OUT LPDWORD TotalEntries,
+ OUT LPDWORD ResumeHandle
+ )
+/*++
+
+Routine Description:
+
+Arguments:
+
+Return Value:
+
+--*/
+{
+ NET_API_STATUS status;
+ GENERIC_ENUM_STRUCT InfoStruct;
+ GENERIC_INFO_CONTAINER GenericInfoContainer;
+
+ GenericInfoContainer.Buffer = NULL;
+ GenericInfoContainer.EntriesRead = 0;
+
+ InfoStruct.Container = &GenericInfoContainer;
+ InfoStruct.Level = InfoLevel;
+
+ RpcTryExcept {
+ status = NetrRplVendorEnum(
+ (RPL_RPC_HANDLE)ServerHandle,
+ (LPRPL_VENDOR_ENUM)&InfoStruct,
+ PrefMaxLength,
+ TotalEntries,
+ ResumeHandle
+ );
+ if ( status == NERR_Success || status == ERROR_MORE_DATA) {
+ *PointerToBuffer = (LPBYTE)GenericInfoContainer.Buffer;
+ *EntriesRead = GenericInfoContainer.EntriesRead;
+ }
+ }
+ RpcExcept( EXCEPTION_EXECUTE_HANDLER) {
+ status = RplMapRpcError( RpcExceptionCode(), ERROR_INVALID_HANDLE);
+ }
+ RpcEndExcept
+
+ return( status);
+}
+
+//
+// ADAPTER apis
+//
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetRplAdapterAdd(
+ IN RPL_HANDLE ServerHandle,
+ IN DWORD InfoLevel,
+ IN LPBYTE Buffer,
+ OUT LPDWORD ErrorParameter OPTIONAL
+ )
+/*++
+
+Routine Description:
+
+Arguments:
+
+Return Value:
+
+--*/
+{
+ NET_API_STATUS status;
+
+ RpcTryExcept {
+ status = NetrRplAdapterAdd(
+ (RPL_RPC_HANDLE)ServerHandle,
+ InfoLevel,
+ (LPRPL_ADAPTER_INFO_STRUCT)&Buffer,
+ ErrorParameter
+ );
+ }
+ RpcExcept( EXCEPTION_EXECUTE_HANDLER) {
+ status = RplMapRpcError( RpcExceptionCode(), ERROR_INVALID_HANDLE);
+ }
+ RpcEndExcept
+
+ return( status);
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetRplAdapterDel(
+ IN RPL_HANDLE ServerHandle,
+ IN LPTSTR AdapterName OPTIONAL
+ )
+/*++
+
+Routine Description:
+
+Arguments:
+
+Return Value:
+
+--*/
+{
+ NET_API_STATUS status;
+
+ RpcTryExcept {
+ status = NetrRplAdapterDel(
+ (RPL_RPC_HANDLE)ServerHandle,
+ AdapterName
+ );
+ }
+ RpcExcept( EXCEPTION_EXECUTE_HANDLER) {
+ status = RplMapRpcError( RpcExceptionCode(), ERROR_INVALID_HANDLE);
+ }
+ RpcEndExcept
+
+ return( status);
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetRplAdapterEnum(
+ IN RPL_HANDLE ServerHandle,
+ IN DWORD InfoLevel,
+ OUT LPBYTE * PointerToBuffer,
+ IN DWORD PrefMaxLength,
+ OUT LPDWORD EntriesRead,
+ OUT LPDWORD TotalEntries,
+ OUT LPDWORD ResumeHandle
+ )
+/*++
+
+Routine Description:
+
+Arguments:
+
+Return Value:
+
+--*/
+{
+ NET_API_STATUS status;
+ GENERIC_ENUM_STRUCT InfoStruct;
+ GENERIC_INFO_CONTAINER GenericInfoContainer;
+
+ GenericInfoContainer.Buffer = NULL;
+ GenericInfoContainer.EntriesRead = 0;
+
+ InfoStruct.Container = &GenericInfoContainer;
+ InfoStruct.Level = InfoLevel;
+
+ RpcTryExcept {
+ status = NetrRplAdapterEnum(
+ (RPL_RPC_HANDLE)ServerHandle,
+ (LPRPL_ADAPTER_ENUM)&InfoStruct,
+ PrefMaxLength,
+ TotalEntries,
+ ResumeHandle
+ );
+ if ( status == NERR_Success || status == ERROR_MORE_DATA) {
+ *PointerToBuffer = (LPBYTE)GenericInfoContainer.Buffer;
+ *EntriesRead = GenericInfoContainer.EntriesRead;
+ }
+ }
+ RpcExcept( EXCEPTION_EXECUTE_HANDLER) {
+ status = RplMapRpcError( RpcExceptionCode(), ERROR_INVALID_HANDLE);
+ }
+ RpcEndExcept
+
+ return( status);
+}
+
+
+//
+// WKSTA apis
+//
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetRplWkstaAdd(
+ IN RPL_HANDLE ServerHandle,
+ IN DWORD InfoLevel,
+ IN LPBYTE Buffer,
+ OUT LPDWORD ErrorParameter OPTIONAL
+ )
+/*++
+
+Routine Description:
+
+Arguments:
+
+Return Value:
+
+--*/
+{
+ NET_API_STATUS status;
+
+ RpcTryExcept {
+ status = NetrRplWkstaAdd(
+ (RPL_RPC_HANDLE)ServerHandle,
+ InfoLevel,
+ (LPRPL_WKSTA_INFO_STRUCT)&Buffer,
+ ErrorParameter
+ );
+ }
+ RpcExcept( EXCEPTION_EXECUTE_HANDLER) {
+ status = RplMapRpcError( RpcExceptionCode(), ERROR_INVALID_HANDLE);
+ }
+ RpcEndExcept
+
+ return( status);
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetRplWkstaClone(
+ IN RPL_HANDLE ServerHandle,
+ IN LPTSTR SourceWkstaName,
+ IN LPTSTR TargetWkstaName,
+ IN LPTSTR TargetWkstaComment OPTIONAL,
+ IN LPTSTR TargetAdapterName,
+ IN DWORD TargetWkstaIpAddress
+ )
+/*++
+
+Routine Description:
+
+Arguments:
+
+Return Value:
+
+--*/
+{
+ NET_API_STATUS status;
+
+ RpcTryExcept {
+ status = NetrRplWkstaClone(
+ (RPL_RPC_HANDLE)ServerHandle,
+ SourceWkstaName,
+ TargetWkstaName,
+ TargetWkstaComment,
+ TargetAdapterName,
+ TargetWkstaIpAddress
+ );
+ }
+ RpcExcept( EXCEPTION_EXECUTE_HANDLER) {
+ status = RplMapRpcError( RpcExceptionCode(), ERROR_INVALID_HANDLE);
+ }
+ RpcEndExcept
+
+ return( status);
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetRplWkstaDel(
+ IN RPL_HANDLE ServerHandle,
+ IN LPTSTR WkstaName
+ )
+/*++
+
+Routine Description:
+
+Arguments:
+
+Return Value:
+
+--*/
+{
+ NET_API_STATUS status;
+
+ RpcTryExcept {
+ status = NetrRplWkstaDel(
+ (RPL_RPC_HANDLE)ServerHandle,
+ WkstaName
+ );
+ }
+ RpcExcept( EXCEPTION_EXECUTE_HANDLER) {
+ status = RplMapRpcError( RpcExceptionCode(), ERROR_INVALID_HANDLE);
+ }
+ RpcEndExcept
+
+ return( status);
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetRplWkstaEnum(
+ IN RPL_HANDLE ServerHandle,
+ IN LPTSTR ProfileName,
+ IN DWORD InfoLevel,
+ OUT LPBYTE * PointerToBuffer,
+ IN DWORD PrefMaxLength,
+ OUT LPDWORD EntriesRead,
+ OUT LPDWORD TotalEntries,
+ OUT LPDWORD ResumeHandle
+ )
+/*++
+
+Routine Description:
+
+Arguments:
+
+Return Value:
+
+--*/
+{
+ NET_API_STATUS status;
+ GENERIC_ENUM_STRUCT InfoStruct;
+ GENERIC_INFO_CONTAINER GenericInfoContainer;
+
+ GenericInfoContainer.Buffer = NULL;
+ GenericInfoContainer.EntriesRead = 0;
+
+ InfoStruct.Container = &GenericInfoContainer;
+ InfoStruct.Level = InfoLevel;
+
+ RpcTryExcept {
+ status = NetrRplWkstaEnum(
+ (RPL_RPC_HANDLE)ServerHandle,
+ ProfileName,
+ (LPRPL_WKSTA_ENUM)&InfoStruct,
+ PrefMaxLength,
+ TotalEntries,
+ ResumeHandle
+ );
+ if ( status == NERR_Success || status == ERROR_MORE_DATA) {
+ *PointerToBuffer = (LPBYTE)GenericInfoContainer.Buffer;
+ *EntriesRead = GenericInfoContainer.EntriesRead;
+ }
+ }
+ RpcExcept( EXCEPTION_EXECUTE_HANDLER) {
+ status = RplMapRpcError( RpcExceptionCode(), ERROR_INVALID_HANDLE);
+ }
+ RpcEndExcept
+
+ return( status);
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetRplWkstaGetInfo(
+ IN RPL_HANDLE ServerHandle,
+ IN LPTSTR WkstaName,
+ IN DWORD InfoLevel,
+ OUT LPBYTE * PointerToBuffer
+ )
+/*++
+
+Routine Description:
+
+Arguments:
+
+Return Value:
+
+--*/
+{
+ NET_API_STATUS status;
+
+ RpcTryExcept {
+ *PointerToBuffer = NULL; // Must be NULL so RPC knows to fill it in.
+ status = NetrRplWkstaGetInfo(
+ (RPL_RPC_HANDLE)ServerHandle,
+ WkstaName,
+ InfoLevel,
+ (LPRPL_WKSTA_INFO_STRUCT)PointerToBuffer
+ );
+ }
+ RpcExcept( EXCEPTION_EXECUTE_HANDLER) {
+ status = RplMapRpcError( RpcExceptionCode(), ERROR_INVALID_HANDLE);
+ }
+ RpcEndExcept
+
+ return( status);
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetRplWkstaSetInfo(
+ IN RPL_HANDLE ServerHandle,
+ IN LPTSTR WkstaName,
+ IN DWORD InfoLevel,
+ IN LPBYTE Buffer,
+ OUT LPDWORD ErrorParameter OPTIONAL
+ )
+/*++
+
+Routine Description:
+
+Arguments:
+
+Return Value:
+
+--*/
+{
+ NET_API_STATUS status;
+
+ RpcTryExcept {
+ status = NetrRplWkstaSetInfo(
+ (RPL_RPC_HANDLE)ServerHandle,
+ WkstaName,
+ InfoLevel,
+ (LPRPL_WKSTA_INFO_STRUCT)&Buffer,
+ ErrorParameter
+ );
+ }
+ RpcExcept( EXCEPTION_EXECUTE_HANDLER) {
+ status = RplMapRpcError( RpcExceptionCode(), ERROR_INVALID_HANDLE);
+ }
+ RpcEndExcept
+
+ return( status);
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetRplSetSecurity(
+ IN RPL_HANDLE ServerHandle,
+ IN LPTSTR WkstaName OPTIONAL,
+ IN DWORD WkstaRid,
+ IN DWORD RplUserRid
+ )
+/*++
+
+Routine Description:
+
+Arguments:
+
+Return Value:
+
+--*/
+{
+ NET_API_STATUS status;
+
+ RpcTryExcept {
+ status = NetrRplSetSecurity(
+ (RPL_RPC_HANDLE)ServerHandle,
+ WkstaName,
+ WkstaRid,
+ RplUserRid
+ );
+ }
+ RpcExcept( EXCEPTION_EXECUTE_HANDLER) {
+ status = RplMapRpcError( RpcExceptionCode(), ERROR_INVALID_HANDLE);
+ }
+ RpcEndExcept
+
+ return( status);
+}
+
+
+DWORD RplMapRpcError(
+ IN DWORD RpcError,
+ IN DWORD BadContextError
+ )
+/*++
+
+Routine Description:
+
+ This routine maps the RPC error into a more meaningful error
+ for the caller.
+
+Arguments:
+
+ RpcError - Supplies the exception error raised by RPC
+
+ BadContextError - Supplies the error code to return whenever an error
+ which indicates invalid context is received. In some cases, this
+ value is ERROR_INVALID_HANDLE in others, it is ERROR_INVALID_SERVICE_LOCK.
+
+Return Value:
+
+ Returns the mapped error.
+
+--*/
+{
+ switch ( RpcError) {
+
+ case RPC_S_INVALID_BINDING:
+ case RPC_X_SS_IN_NULL_CONTEXT:
+ case RPC_X_SS_CONTEXT_DAMAGED:
+ case RPC_X_SS_HANDLES_MISMATCH:
+ case ERROR_INVALID_HANDLE:
+ return( BadContextError);
+
+ case RPC_X_NULL_REF_POINTER:
+ return( ERROR_INVALID_PARAMETER);
+
+ case EXCEPTION_ACCESS_VIOLATION:
+ return( ERROR_INVALID_ADDRESS);
+
+ default:
+ return( RpcError);
+ }
+}
+
diff --git a/private/net/svcdlls/rpl/client/sources b/private/net/svcdlls/rpl/client/sources
new file mode 100644
index 000000000..6227f5162
--- /dev/null
+++ b/private/net/svcdlls/rpl/client/sources
@@ -0,0 +1,62 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 27 - July - 1993
+
+
+Revision History:
+
+!ENDIF
+
+MAJORCOMP = net
+MINORCOMP = services
+TARGETNAME= rplsvc
+
+
+NTPROFILEINPUT=YES
+
+TARGETPATH=obj
+TARGETTYPE=LIBRARY
+TARGETLIBS=
+
+INCLUDES=.;..\inc;..\..\..\inc;..\..\..\..\inc
+
+!IFNDEF DISABLE_NET_UNICODE
+UNICODE=1
+NET_C_DEFINES=-DUNICODE
+!ENDIF
+
+MSC_WARNING_LEVEL=/W3 /WX
+
+SOURCES=rplstub.c \
+ rplbind.c \
+ rplsvc_c.c
+
+
+UMTYPE= console
+!IF 0
+UMAPPL= rplapi
+!ELSE
+UMTEST= rplapi
+!ENDIF
+UMLIBS= \
+ $(BASEDIR)\public\sdk\lib\*\rpcrt4.lib \
+ $(BASEDIR)\public\sdk\lib\*\netapi32.lib \
+ $(BASEDIR)\Public\Sdk\Lib\*\netlib.lib
+
+C_DEFINES=-DRPC_NO_WINDOWS_H
diff --git a/private/net/svcdlls/rpl/command/makefile b/private/net/svcdlls/rpl/command/makefile
new file mode 100644
index 000000000..f0db8e4a7
--- /dev/null
+++ b/private/net/svcdlls/rpl/command/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS LINE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/net/svcdlls/rpl/command/makefile.inc b/private/net/svcdlls/rpl/command/makefile.inc
new file mode 100644
index 000000000..803916b39
--- /dev/null
+++ b/private/net/svcdlls/rpl/command/makefile.inc
@@ -0,0 +1,4 @@
+rplmsg.rc: msg00001.bin
+
+rplmsg.h msg00001.bin: rplmsg.mc
+ mc -v rplmsg.mc
diff --git a/private/net/svcdlls/rpl/command/rplcmd.c b/private/net/svcdlls/rpl/command/rplcmd.c
new file mode 100644
index 000000000..b5f2b32e9
--- /dev/null
+++ b/private/net/svcdlls/rpl/command/rplcmd.c
@@ -0,0 +1,2408 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ rplcmd.c
+
+Abstract:
+
+ This file contains program for RemoteBoot command program.
+ This program originated from rplapi.c in ..\client directory.
+
+Author:
+
+ Vladimiv Z. Vulovic (vladimv) 19-November-1993
+
+Environment:
+
+ User Mode - Win32
+
+Revision History:
+
+--*/
+
+//
+// INCLUDES
+//
+
+#include <nt.h> // DbgPrint prototype
+#include <ntrtl.h> // DbgPrint prototype
+#include <nturtl.h> // Needed by winbase.h
+
+#include <windows.h> // DWORD, IN, File APIs, etc.
+
+#include <lmcons.h> // NET_API_STATUS
+#include <lmerr.h> // NetError codes
+#include <lmrpl.h> // RPL_HANDLE
+#include <i_lmrpl.h> // RPL_CONFIG_INFO_1
+#include <lmapibuf.h> // NetApiBufferFree()
+#include <stdlib.h> // exit()
+#include <stdio.h> // printf
+#include <ctype.h> // toupper - bombs if I use it
+#include "jet.h" // JetError (for rpllib.h)
+
+#include "rplmsg.h" // private RPL command error codes & messages
+#include "rpllib.h" // RplPrintf
+
+#define RPL_BUFFER_GET_ALL ((DWORD)-1)
+
+#if DBG
+#define RPL_DEBUG
+#endif // DBG
+
+#ifdef RPL_DEBUG
+#define RplDbgPrint( _x_) printf _x_
+#define RPL_ASSERT( condition) \
+ { \
+ if ( !( condition)) { \
+ RplDbgPrint( "File %s, Line %d\n", __FILE__, __LINE__); \
+ DbgPrint( "[RplApi] File %s, Line %d\n", __FILE__, __LINE__); \
+ DbgUserBreakPoint(); \
+ } \
+ }
+#else
+#define RplDbgPrint( _x_)
+#define RPL_ASSERT( condition)
+#endif
+
+#define QUESTION_SW L"/?"
+#define QUESTION_SW_TOO L"-?"
+#define RPL_GENERIC_ERROR 1
+
+RPL_HANDLE GlobalServerHandle;
+BOOL GlobalHaveServerHandle = FALSE;
+PWCHAR GlobalServerName;
+HANDLE GlobalMessageHandle;
+
+
+int FileIsConsole( int fh)
+{
+ unsigned htype ;
+
+ htype = GetFileType(GetStdHandle(fh));
+ htype &= ~FILE_TYPE_REMOTE;
+ return htype == FILE_TYPE_CHAR;
+}
+
+
+
+DWORD ConsolePrint(
+ LPWSTR pch,
+ int cch
+ )
+{
+ int cchOut;
+ BOOL Success;
+ CHAR *pchOemBuffer;
+
+ if ( cch == 0) {
+ return( 0);
+ }
+
+ if ( FileIsConsole(STD_OUTPUT_HANDLE)) {
+ Success = WriteConsole(
+ GetStdHandle(STD_OUTPUT_HANDLE),
+ pch, cch, &cchOut, NULL);
+ if ( Success) {
+ return( cchOut);
+ }
+ }
+
+ cchOut = WideCharToMultiByte( CP_OEMCP, 0, pch, cch, NULL, 0, NULL,NULL);
+ if (cchOut == 0) {
+ return 0;
+ }
+
+ if ((pchOemBuffer = (CHAR *)malloc(cchOut)) != NULL) {
+ WideCharToMultiByte(CP_OEMCP, 0, pch, cch,
+ pchOemBuffer, cchOut, NULL, NULL);
+ WriteFile( GetStdHandle(STD_OUTPUT_HANDLE),
+ pchOemBuffer, cchOut, &cch, NULL);
+ free(pchOemBuffer);
+ }
+
+ return cchOut;
+}
+
+
+
+DWORD MessagePrint(
+ IN DWORD MessageId,
+ ...
+ )
+/*++
+
+Routine Description:
+
+ Finds the unicode message corresponding to the supplied message id,
+ merges it with caller supplied string(s), and prints the resulting
+ string.
+
+Arguments:
+
+ MessageId - message id
+
+Return Value:
+
+ Count of characters, not counting the terminating null character,
+ printed by this routine. Zero return value indicates failure.
+
+--*/
+{
+ va_list arglist;
+ WCHAR * buffer = NULL;
+ DWORD length;
+ LPVOID lpSource;
+ DWORD dwFlags = FORMAT_MESSAGE_ALLOCATE_BUFFER;
+
+ if ( MessageId == NO_ERROR) {
+ return( 0);
+ }
+
+ va_start( arglist, MessageId);
+
+ if ( MessageId < NERR_BASE) {
+ //
+ // Get message from system.
+ //
+ lpSource = NULL; // redundant step according to FormatMessage() spec
+ dwFlags |= FORMAT_MESSAGE_FROM_SYSTEM;
+
+ } else if ( MessageId >= IDS_LOAD_LIBRARY_FAILURE
+ && MessageId <= IDS_RPLSVC_LOCAL_COMPUTER ) {
+ //
+ // Get message from this module.
+ //
+ lpSource = NULL;
+ dwFlags |= FORMAT_MESSAGE_FROM_HMODULE;
+ } else {
+ //
+ // Get message from netmsg.dll.
+ //
+ lpSource = GlobalMessageHandle;
+ dwFlags |= FORMAT_MESSAGE_FROM_HMODULE;
+ }
+
+ length = FormatMessage(
+ dwFlags, // dwFlags
+ lpSource, // lpSource
+ MessageId, // MessageId
+ 0L, // dwLanguageId
+ (LPTSTR)&buffer, // lpBuffer
+ 0, // size
+ &arglist // lpArguments
+ );
+
+ length = ConsolePrint(buffer, length);
+
+ LocalFree(buffer);
+
+ return( length);
+
+} // MessagePrint()
+
+
+
+BOOL ReadString(
+ IN DWORD MessageId,
+ OUT PWCHAR * pString,
+ IN BOOL MustHaveInput
+ )
+{
+ BYTE Line[ 300];
+ WCHAR WcharBuffer[ 300];
+ DWORD Length; // includes terminal null wchar
+
+ RplPrintf0( MessageId );
+
+ *pString = NULL;
+
+ if ( gets( Line) == NULL) {
+ return( FALSE);
+ }
+ if ( *Line == 0) {
+ //
+ // Zero length input is OK only when input is optional (and in that
+ // case pointer is assumed to be NULL).
+ //
+ if ( MustHaveInput == FALSE) {
+ return( TRUE);
+ } else {
+ RplPrintfID( IDS_NOTSUPPLIED, MessageId );
+ return( FALSE);
+ }
+ }
+ Length = MultiByteToWideChar( CP_OEMCP, MB_PRECOMPOSED, Line,
+ -1, WcharBuffer, sizeof( WcharBuffer));
+ if ( Length == 0) {
+ RplPrintf1( IDS_INVALIDSTRING_CHARSTRINGA, (PWCHAR)Line );
+ return( FALSE);
+ }
+ *pString = LocalAlloc( GMEM_FIXED, Length * sizeof(WCHAR));
+ if ( *pString == NULL) {
+ RplDbgPrint(( "LocalAlloc failed"));
+ return( FALSE);
+ }
+ wcscpy( *pString, WcharBuffer);
+ return( TRUE);
+}
+
+
+//
+// returns -1 on error, position of selection (starting from 0) otherwise
+//
+int ReadFromMenu(
+ IN DWORD MenuMessageId,
+ IN DWORD FilterMessageId
+ )
+{
+ int ReturnValue = -1;
+ WCHAR Char = L'\0';
+ PWCHAR InputBuffer = NULL;
+ PWCHAR FilterBuffer = NULL;
+ int pos;
+
+ RplSPrintfN( FilterMessageId, NULL, 0, &FilterBuffer );
+ if (FilterBuffer == NULL) {
+ // this should never happen
+ goto nomsg_cleanup;
+ }
+
+ if ( !ReadString( MenuMessageId, &InputBuffer, FALSE ) ) {
+ goto nomsg_cleanup;
+ }
+
+ if ( InputBuffer == NULL
+ || (*InputBuffer) == L'\0'
+ || !CharUpperBuff( InputBuffer, 1 ) ) {
+ goto cleanup;
+ }
+ Char = InputBuffer[0];
+
+ pos = 0;
+ while (FilterBuffer[pos] != L'\0') {
+ if (Char == FilterBuffer[pos]) {
+ break;
+ }
+ pos++;
+ }
+
+ if (FilterBuffer[pos] == L'\0') {
+ goto cleanup;
+ }
+
+ ReturnValue = pos;
+
+
+cleanup:
+ if ( ReturnValue == -1 ) {
+ if (InputBuffer == NULL) {
+ RplPrintf2( IDS_BADMENUSEL, L"", FilterBuffer);
+ } else {
+ if (InputBuffer[0] != L'\0') {
+ InputBuffer[1] = L'\0';
+ }
+ RplPrintf2( IDS_BADMENUSEL, InputBuffer, FilterBuffer);
+ }
+ }
+
+nomsg_cleanup:
+
+ if (FilterBuffer != NULL) {
+ LocalFree( FilterBuffer );
+ }
+ if (InputBuffer != NULL) {
+ LocalFree( InputBuffer );
+ }
+
+ return( ReturnValue);
+}
+
+
+BOOL ReadInt(
+ IN DWORD MessageId,
+ OUT int * pInt,
+ IN BOOL MustHaveInput
+ )
+{
+ BYTE Line[ 300];
+ RplPrintf0( MessageId );
+ if ( gets( Line) == NULL) {
+ return( FALSE);
+ }
+ if ( sscanf( Line, "%d", pInt) != 1) {
+ if ( MustHaveInput == FALSE) {
+ return( TRUE);
+ } else {
+ RplPrintfID( IDS_NOTSUPPLIED, MessageId );
+ return( FALSE);
+ }
+ }
+ return( TRUE);
+}
+
+
+BOOL ReadWkstaFlags(
+ OUT PDWORD pFlags
+ )
+{
+ *pFlags = 0;
+
+ switch( ReadFromMenu( IDS_PROMPT_MENU_WKSTA_LOGON_INPUT,
+ IDS_PROMPT_FILTER_WKSTA_LOGON_INPUT )) {
+ case 0:
+ *pFlags |= WKSTA_FLAGS_LOGON_INPUT_REQUIRED;
+ break;
+ case 1:
+ *pFlags |= WKSTA_FLAGS_LOGON_INPUT_OPTIONAL;
+ break;
+ case 2:
+ *pFlags |= WKSTA_FLAGS_LOGON_INPUT_IMPOSSIBLE;
+ break;
+ default:
+ // this really shouldn't happen; fall through
+ case -1:
+ return( FALSE);
+ }
+
+ switch( ReadFromMenu( IDS_PROMPT_MENU_WKSTA_SHAREDORPRIVATE,
+ IDS_PROMPT_FILTER_WKSTA_SHAREDORPRIVATE )) {
+ case 0:
+ *pFlags |= WKSTA_FLAGS_SHARING_TRUE;
+ break;
+ case 1:
+ *pFlags |= WKSTA_FLAGS_SHARING_FALSE;
+ break;
+ default:
+ // this really shouldn't happen; fall through
+ case -1:
+ return( FALSE);
+ }
+
+ switch( ReadFromMenu( IDS_PROMPT_MENU_WKSTA_DHCP,
+ IDS_PROMPT_FILTER_YN)) {
+ case 0:
+ *pFlags |= WKSTA_FLAGS_DHCP_TRUE;
+ break;
+ case 1:
+ *pFlags |= WKSTA_FLAGS_DHCP_FALSE;
+ break;
+ default:
+ // this really shouldn't happen; fall through
+ case -1:
+ return( FALSE);
+ }
+
+ switch( ReadFromMenu( IDS_PROMPT_MENU_WKSTA_DELETE,
+ IDS_PROMPT_FILTER_YN)) {
+ case 0:
+ *pFlags |= WKSTA_FLAGS_DELETE_TRUE;
+ break;
+ case 1:
+ *pFlags |= WKSTA_FLAGS_DELETE_FALSE;
+ break;
+ default:
+ // this really shouldn't happen; fall through
+ case -1:
+ return( FALSE);
+ }
+
+ return( TRUE);
+}
+
+
+
+DWORD ConfigDisplayInfo(
+ IN DWORD Level,
+ OUT LPVOID Buffer
+ )
+{
+ LPRPL_CONFIG_INFO_2 Info = Buffer;
+
+ if ( Level > 2) {
+ return( ERROR_INVALID_LEVEL);
+ }
+ RplPrintf1( IDS_CONFIGNAME_NOTAB, Info->ConfigName );
+ RplPrintf1( IDS_CONFIGCOMMENT, Info->ConfigComment );
+ if ( Level == 0) {
+ return( NO_ERROR);
+ }
+ switch( Info->Flags & CONFIG_FLAGS_MASK_ENABLED) {
+ case CONFIG_FLAGS_ENABLED_TRUE:
+ RplPrintf0( IDS_CONFIGENABLED );
+ break;
+ case CONFIG_FLAGS_ENABLED_FALSE:
+ RplPrintf0( IDS_CONFIGDISABLED );
+ break;
+ default:
+ RplPrintf1( IDS_CONFIGBADFLAGS, (PWSTR)Info->Flags );
+ break;
+ }
+ if ( Level == 1) {
+ return( NO_ERROR);
+ }
+ RplPrintf1( IDS_BOOTNAME, Info->BootName );
+ RplPrintf1( IDS_DIRNAME, Info->DirName );
+ RplPrintf1( IDS_DIRNAME2, Info->DirName2 );
+ RplPrintf1( IDS_DIRNAME3, Info->DirName3 );
+ RplPrintf1( IDS_DIRNAME4, Info->DirName4 );
+ RplPrintf1( IDS_FITSHARED, Info->FitShared );
+ RplPrintf1( IDS_FITPERSONAL, Info->FitPersonal );
+ return( NO_ERROR);
+}
+
+
+
+VOID TestConfigEnum(
+ IN DWORD Level,
+ IN PWCHAR AdapterName,
+ IN DWORD PrefMaxLength,
+ IN PDWORD pResumeHandle
+ )
+{
+ LPBYTE Buffer;
+ DWORD EntriesRead;
+ DWORD TotalEntries;
+ DWORD CoreSize;
+ DWORD index;
+ DWORD Error;
+
+ switch( Level) {
+ case 0:
+ CoreSize = sizeof( RPL_CONFIG_INFO_0);
+ break;
+ case 1:
+ CoreSize = sizeof( RPL_CONFIG_INFO_1);
+ break;
+ case 2:
+ CoreSize = sizeof( RPL_CONFIG_INFO_2);
+ break;
+ default:
+ return;
+ break;
+ }
+
+#ifdef RPL_DEBUG
+ printf( "\nConfigEnum: Level=%d", Level);
+ if ( PrefMaxLength != RPL_BUFFER_GET_ALL) {
+ printf( ", PrefMaxLength=%ld", PrefMaxLength);
+ } else {
+ RplDbgPrint(( ", unlimited buffer size"));
+ }
+ if ( AdapterName != NULL) {
+ RplDbgPrint(( ", AdapterName=%ws", AdapterName));
+ }
+ if ( pResumeHandle != NULL) {
+ RplDbgPrint(( ", ResumeHandle=0x%x\n\n", *pResumeHandle));
+ } else {
+ RplDbgPrint(( ", not resumable.\n\n"));
+ }
+#endif
+
+ for ( ; ; ) {
+ Error = NetRplConfigEnum( GlobalServerHandle, AdapterName, Level, &Buffer,
+ PrefMaxLength, &EntriesRead, &TotalEntries, pResumeHandle);
+
+ if ( Error != NO_ERROR && Error != ERROR_MORE_DATA) {
+ MessagePrint( Error);
+ break;
+ }
+
+#ifdef RPL_DEBUG
+ RplDbgPrint(( "Buffer = 0x%x, EntriesRead = %d, TotalEntries = %d", Buffer,
+ EntriesRead, TotalEntries));
+ if ( pResumeHandle != NULL) {
+ RplDbgPrint(( ", ResumeHandle = 0x%x\n", *pResumeHandle));
+ } else {
+ RplDbgPrint(("\n"));
+ }
+#endif
+
+ for ( index = 0; index < EntriesRead; index++) {
+ ConfigDisplayInfo( Level, Buffer + index * CoreSize);
+ }
+ NetApiBufferFree( Buffer); // =~ MIDL_user_free()
+
+ if ( pResumeHandle == NULL) {
+ break;
+ }
+ if ( *pResumeHandle == 0) {
+ if ( Error != NO_ERROR) {
+ MessagePrint( Error);
+ }
+ break;
+ }
+ if ( Error != ERROR_MORE_DATA) {
+ MessagePrint( Error);
+ MessagePrint( IDS_UNEXPECTED_RETURN_CODE, Error);
+ break;
+ }
+ }
+}
+
+#define PROFILE_FLAGS_DISK_PRESENT_TRUE ((DWORD)0x00000001)
+#define PROFILE_FLAGS_DISK_PRESENT_FALSE ((DWORD)0x00000002)
+#define PROFILE_FLAGS_MASK_DISK_PRESENT \
+ ( PROFILE_FLAGS_DISK_PRESENT_FALSE | \
+ PROFILE_FLAGS_DISK_PRESENT_TRUE )
+
+typedef enum _DISPLAY_ACTION {
+ DisplayActionSetInfo = 0,
+ DisplayActionGetInfo
+} DISPLAY_ACTION;
+
+
+DWORD ProfileDisplayInfo(
+ IN BOOL SetInfo,
+ IN DWORD Level,
+ OUT LPVOID Buffer
+ )
+{
+ LPRPL_PROFILE_INFO_2 Info = Buffer;
+
+ if ( Level > 2) {
+ return( ERROR_INVALID_LEVEL);
+ }
+ RplPrintf1(IDS_PROFILENAME_NOTAB, Info->ProfileName );
+ RplPrintf1(IDS_PROFILECOMMENT, Info->ProfileComment );
+ if ( Level == 0) {
+ return( NO_ERROR);
+ }
+ switch( Info->Flags & PROFILE_FLAGS_MASK_DISK_PRESENT) {
+ case PROFILE_FLAGS_DISK_PRESENT_TRUE:
+ break; // do not report anything if all is good
+ case PROFILE_FLAGS_DISK_PRESENT_FALSE:
+ RplPrintf0( IDS_PROFILENODISKTREE );
+ break;
+ case 0:
+ //
+ // 0 is OK value for SetInfo, but not for Add & GetInfo
+ //
+ if ( SetInfo == TRUE) {
+ break;
+ }
+ default:
+ RplPrintf1( IDS_PROFILEBADFLAGS, (LPWSTR)Info->Flags);
+ break;
+ }
+ if ( Level == 1) {
+ return( NO_ERROR);
+ }
+ RplPrintf1( IDS_CONFIGNAME, Info->ConfigName );
+ RplPrintf1( IDS_BOOTNAME, Info->BootName );
+ RplPrintf1( IDS_FITSHARED, Info->FitShared );
+ RplPrintf1( IDS_FITPERSONAL, Info->FitPersonal );
+ return( NO_ERROR);
+}
+
+
+
+VOID TestProfileEnum(
+ IN DWORD Level,
+ IN PWCHAR AdapterName,
+ IN DWORD PrefMaxLength,
+ IN PDWORD pResumeHandle
+ )
+{
+ LPBYTE Buffer;
+ DWORD EntriesRead;
+ DWORD TotalEntries;
+ DWORD CoreSize;
+ DWORD index;
+ DWORD Error;
+
+ switch( Level) {
+ case 0:
+ CoreSize = sizeof( RPL_PROFILE_INFO_0);
+ break;
+ case 1:
+ CoreSize = sizeof( RPL_PROFILE_INFO_1);
+ break;
+ case 2:
+ CoreSize = sizeof( RPL_PROFILE_INFO_2);
+ break;
+ default:
+ return;
+ break;
+ }
+
+#ifdef RPL_DEBUG
+ RplDbgPrint(( "\nProfileEnum: Level=%d", Level));
+ if ( PrefMaxLength != RPL_BUFFER_GET_ALL) {
+ RplDbgPrint(( ", PrefMaxLength=%ld", PrefMaxLength));
+ } else {
+ RplDbgPrint(( ", unlimited buffer size"));
+ }
+ if ( AdapterName != NULL) {
+ RplDbgPrint(( ", AdapterName=%ws", AdapterName));
+ }
+ if ( pResumeHandle != NULL) {
+ RplDbgPrint(( ", ResumeHandle=0x%x\n\n", *pResumeHandle));
+ } else {
+ RplDbgPrint(( ", not resumable.\n\n"));
+ }
+#endif
+
+ for ( ; ; ) {
+ Error = NetRplProfileEnum( GlobalServerHandle, AdapterName, Level, &Buffer,
+ PrefMaxLength, &EntriesRead, &TotalEntries, pResumeHandle);
+
+ if ( Error != NO_ERROR && Error != ERROR_MORE_DATA) {
+ MessagePrint( Error);
+ break;
+ }
+
+#ifdef RPL_DEBUG
+ RplDbgPrint(( "Buffer = 0x%x, EntriesRead = %d, TotalEntries = %d", Buffer,
+ EntriesRead, TotalEntries));
+ if ( pResumeHandle != NULL) {
+ RplDbgPrint(( ", ResumeHandle = 0x%x\n", *pResumeHandle));
+ } else {
+ RplDbgPrint(("\n"));
+ }
+#endif
+
+ for ( index = 0; index < EntriesRead; index++) {
+ ProfileDisplayInfo( FALSE, Level, Buffer + index * CoreSize);
+ }
+ NetApiBufferFree( Buffer); // =~ MIDL_user_free()
+
+ if ( pResumeHandle == NULL) {
+ break;
+ }
+ if ( *pResumeHandle == 0) {
+ if ( Error != NO_ERROR) {
+ MessagePrint( Error);
+ }
+ break;
+ }
+ if ( Error != ERROR_MORE_DATA) {
+ MessagePrint( Error);
+ MessagePrint( IDS_UNEXPECTED_RETURN_CODE, Error);
+ break;
+ }
+ }
+}
+
+
+DWORD ServiceDisplayInfo(
+ IN DWORD Level,
+ OUT LPVOID Buffer
+ )
+{
+ LPRPL_INFO_0 Info = Buffer;
+
+ if ( Level > 0) {
+ return( ERROR_INVALID_LEVEL);
+ }
+ switch( Info->Flags) {
+ case 0:
+ break;
+ default:
+ RplPrintf1( IDS_SERVICEBADFLAGS, (PWCHAR)Info->Flags);
+ }
+ return( NO_ERROR);
+}
+
+
+
+DWORD WkstaDisplayInfo(
+ IN DWORD Level,
+ OUT LPVOID Buffer
+ )
+{
+ LPRPL_WKSTA_INFO_2 Info = Buffer;
+
+ if ( Level > 2) {
+ return( ERROR_INVALID_LEVEL);
+ }
+ RplPrintf1( IDS_WKSTANAME_NOTAB, Info->WkstaName );
+ RplPrintf1( IDS_WKSTACOMMENT, Info->WkstaComment );
+ if ( Level == 0) {
+ return( NO_ERROR);
+ }
+ switch( Info->Flags & WKSTA_FLAGS_MASK_LOGON_INPUT) {
+ case WKSTA_FLAGS_LOGON_INPUT_REQUIRED:
+ RplPrintf0( IDS_WKSTALOGONREQD );
+ break;
+ case WKSTA_FLAGS_LOGON_INPUT_OPTIONAL:
+ RplPrintf0( IDS_WKSTALOGONOPT );
+ break;
+ case WKSTA_FLAGS_LOGON_INPUT_IMPOSSIBLE:
+ RplPrintf0( IDS_WKSTALOGONNONE );
+ break;
+ default:
+ RplPrintf1( IDS_WKSTALOGONBADFLAGS, (LPWSTR)Info->Flags );
+ break;
+ }
+ switch( Info->Flags & WKSTA_FLAGS_MASK_SHARING) {
+ case WKSTA_FLAGS_SHARING_TRUE:
+ RplPrintf0( IDS_WKSTASHARINGTRUE );
+ break;
+ case WKSTA_FLAGS_SHARING_FALSE:
+ RplPrintf0( IDS_WKSTASHARINGFALSE );
+ break;
+ default:
+ RplPrintf1( IDS_WKSTASHARINGBADFLAGS, (LPWSTR)Info->Flags );
+ break;
+ }
+ switch( Info->Flags & WKSTA_FLAGS_MASK_DHCP) {
+ case WKSTA_FLAGS_DHCP_TRUE:
+ RplPrintf0( IDS_WKSTA_DHCP_TRUE);
+ break;
+ case WKSTA_FLAGS_DHCP_FALSE:
+ RplPrintf0( IDS_WKSTA_DHCP_FALSE);
+ break;
+ default:
+ RplPrintf1( IDS_WKSTA_DHCP_BAD_FLAGS, (LPWSTR)Info->Flags );
+ break;
+ }
+ switch( Info->Flags & WKSTA_FLAGS_MASK_DELETE) {
+ case WKSTA_FLAGS_DELETE_TRUE:
+ RplPrintf0( IDS_WKSTA_DELETE_TRUE);
+ break;
+ case WKSTA_FLAGS_DELETE_FALSE:
+ RplPrintf0( IDS_WKSTA_DELETE_FALSE);
+ break;
+ default:
+ RplPrintf1( IDS_WKSTA_DELETE_BAD_FLAGS, (LPWSTR)Info->Flags );
+ break;
+ }
+ RplPrintf1( IDS_WKSTAINPROFILE, Info->ProfileName);
+ if ( Level == 1) {
+ return( NO_ERROR);
+ }
+ RplPrintf1( IDS_BOOTNAME, Info->BootName);
+ RplPrintf1( IDS_FITFILE, Info->FitFile);
+ RplPrintf1( IDS_WKSTAADAPTER, Info->AdapterName);
+ RplPrintf1( IDS_TCPIPADDRESS, (LPWSTR)Info->TcpIpAddress);
+ RplPrintf1( IDS_TCPIPSUBNET, (LPWSTR)Info->TcpIpSubnet);
+ RplPrintf1( IDS_TCPIPGATEWAY, (LPWSTR)Info->TcpIpGateway);
+ return( NO_ERROR);
+}
+
+
+
+VOID TestWkstaEnum(
+ IN DWORD Level,
+ IN PWCHAR ProfileName,
+ IN DWORD PrefMaxLength,
+ IN PDWORD pResumeHandle
+ )
+{
+ LPBYTE Buffer;
+ DWORD EntriesRead;
+ DWORD TotalEntries;
+ DWORD CoreSize;
+ DWORD index;
+ DWORD Error;
+
+ switch( Level) {
+ case 0:
+ CoreSize = sizeof( RPL_WKSTA_INFO_0);
+ break;
+ case 1:
+ CoreSize = sizeof( RPL_WKSTA_INFO_1);
+ break;
+ case 2:
+ CoreSize = sizeof( RPL_WKSTA_INFO_2);
+ break;
+ default:
+ return;
+ break;
+ }
+
+#ifdef RPL_DEBUG
+ RplDbgPrint(( "\nWkstaEnum: Level=%d", Level));
+ if ( PrefMaxLength != RPL_BUFFER_GET_ALL) {
+ RplDbgPrint(( ", PrefMaxLength=%ld", PrefMaxLength));
+ } else {
+ RplDbgPrint(( ", unlimited buffer size"));
+ }
+ if ( ProfileName != NULL) {
+ RplDbgPrint(( ", ProfileName=%ws", ProfileName));
+ }
+ if ( pResumeHandle != NULL) {
+ RplDbgPrint(( ", ResumeHandle=0x%x\n\n", *pResumeHandle));
+ } else {
+ RplDbgPrint(( ", not resumable.\n\n"));
+ }
+#endif
+
+ for ( ; ; ) {
+ Error = NetRplWkstaEnum( GlobalServerHandle, ProfileName, Level, &Buffer,
+ PrefMaxLength, &EntriesRead, &TotalEntries, pResumeHandle);
+
+ if ( Error != NO_ERROR && Error != ERROR_MORE_DATA) {
+ MessagePrint( Error);
+ break;
+ }
+
+#ifdef RPL_DEBUG
+ RplDbgPrint(( "Buffer = 0x%x, EntriesRead = %d, TotalEntries = %d", Buffer,
+ EntriesRead, TotalEntries));
+ if ( pResumeHandle != NULL) {
+ RplDbgPrint(( ", ResumeHandle = 0x%x\n", *pResumeHandle));
+ } else {
+ RplDbgPrint(("\n"));
+ }
+#endif
+
+ for ( index = 0; index < EntriesRead; index++) {
+ WkstaDisplayInfo( Level, Buffer + index * CoreSize);
+ }
+ NetApiBufferFree( Buffer); // =~ MIDL_user_free()
+
+ if ( pResumeHandle == NULL) {
+ break;
+ }
+ if ( *pResumeHandle == 0) {
+ if ( Error != NO_ERROR) {
+ MessagePrint( Error);
+ }
+ break;
+ }
+ if ( Error != ERROR_MORE_DATA) {
+ MessagePrint( Error);
+ MessagePrint( IDS_UNEXPECTED_RETURN_CODE, Error);
+ break;
+ }
+ }
+}
+
+
+DWORD VendorDisplayInfo(
+ IN DWORD Level,
+ OUT LPVOID Buffer
+ )
+{
+ LPRPL_VENDOR_INFO_1 Info = Buffer;
+
+ if ( Level > 1) {
+ return( ERROR_INVALID_LEVEL);
+ }
+ RplPrintf1( IDS_VENDORNAME_NOTAB, Info->VendorName );
+ RplPrintf1( IDS_VENDORCOMMENT, Info->VendorComment );
+ if ( Level == 0) {
+ return( NO_ERROR);
+ }
+ switch( Info->Flags) {
+ case 0:
+ break;
+ default:
+ RplPrintf1( IDS_VENDORBADFLAGS, (LPWSTR)Info->Flags);
+ }
+ return( NO_ERROR);
+}
+
+
+
+DWORD BootDisplayInfo(
+ IN DWORD Level,
+ OUT LPVOID Buffer
+ )
+{
+ LPRPL_BOOT_INFO_2 Info = Buffer;
+
+ if ( Level > 2) {
+ return( ERROR_INVALID_LEVEL);
+ }
+ RplPrintf1( IDS_BOOTNAME_NOTAB, Info->BootName);
+ RplPrintf1( IDS_BOOTCOMMENT, Info->BootComment);
+ if ( Level == 0) {
+ return( NO_ERROR);
+ }
+ switch( Info->Flags & BOOT_FLAGS_MASK_FINAL_ACKNOWLEDGMENT) {
+ case BOOT_FLAGS_FINAL_ACKNOWLEDGMENT_TRUE:
+ RplPrintf0( IDS_BOOTACK_TRUE );
+ break;
+ case BOOT_FLAGS_FINAL_ACKNOWLEDGMENT_FALSE:
+ RplPrintf0( IDS_BOOTACK_FALSE );
+ break;
+ default:
+ RplPrintf1( IDS_BOOTBADFLAGS, (LPWSTR)Info->Flags);
+ break;
+ }
+ RplPrintf1( IDS_VENDORNAME, Info->VendorName);
+ if ( Level == 1) {
+ return( NO_ERROR);
+ }
+ RplPrintf1( IDS_BBCFILE, Info->BbcFile);
+ RplPrintf1( IDS_WINDOWSIZE, (LPWSTR)Info->WindowSize);
+ return( NO_ERROR);
+}
+
+
+
+DWORD AdapterDisplayInfo(
+ IN DWORD Level,
+ OUT LPVOID Buffer
+ )
+{
+ LPRPL_ADAPTER_INFO_1 Info = Buffer;
+
+ if ( Level > 1) {
+ return( ERROR_INVALID_LEVEL);
+ }
+ RplPrintf1( IDS_ADAPTERNAME_NOTAB, Info->AdapterName);
+ RplPrintf1( IDS_ADAPTERCOMMENT, Info->AdapterComment);
+ if ( Level == 0) {
+ return( NO_ERROR);
+ }
+ switch( Info->Flags) {
+ case 0:
+ break;
+ default:
+ RplPrintf1( IDS_ADAPTERBADFLAGS, (LPWSTR)Info->Flags);
+ }
+ return( NO_ERROR);
+}
+
+
+
+VOID TestAdapterEnum(
+ IN DWORD Level,
+ IN DWORD PrefMaxLength,
+ IN PDWORD pResumeHandle
+ )
+{
+ LPBYTE Buffer;
+ DWORD EntriesRead;
+ DWORD TotalEntries;
+ DWORD CoreSize;
+ DWORD index;
+ DWORD Error;
+
+ switch( Level) {
+ case 0:
+ CoreSize = sizeof( RPL_ADAPTER_INFO_0);
+ break;
+ case 1:
+ CoreSize = sizeof( RPL_ADAPTER_INFO_1);
+ break;
+ default:
+ return;
+ break;
+ }
+
+#ifdef RPL_DEBUG
+ RplDbgPrint(( "\nAdapterEnum: Level=%d", Level));
+ if ( PrefMaxLength != RPL_BUFFER_GET_ALL) {
+ RplDbgPrint(( ", PrefMaxLength=%ld", PrefMaxLength));
+ } else {
+ RplDbgPrint(( ", unlimited buffer size"));
+ }
+ if ( pResumeHandle != NULL) {
+ RplDbgPrint(( ", ResumeHandle=0x%x\n\n", *pResumeHandle));
+ } else {
+ RplDbgPrint(( ", not resumable.\n\n"));
+ }
+#endif
+
+ for ( ; ; ) {
+ Error = NetRplAdapterEnum( GlobalServerHandle, Level, &Buffer,
+ PrefMaxLength, &EntriesRead, &TotalEntries, pResumeHandle);
+
+ if ( Error != NO_ERROR && Error != ERROR_MORE_DATA) {
+ MessagePrint( Error);
+ break;
+ }
+
+#ifdef RPL_DEBUG
+ RplDbgPrint(( "Buffer = 0x%x, EntriesRead = %d, TotalEntries = %d", Buffer,
+ EntriesRead, TotalEntries));
+ if ( pResumeHandle != NULL) {
+ RplDbgPrint(( ", ResumeHandle = 0x%x\n", *pResumeHandle));
+ } else {
+ RplDbgPrint(("\n"));
+ }
+#endif
+
+ for ( index = 0; index < EntriesRead; index++) {
+ AdapterDisplayInfo( Level, Buffer + index * CoreSize);
+ }
+ NetApiBufferFree( Buffer); // =~ MIDL_user_free()
+
+ if ( pResumeHandle == NULL) {
+ break;
+ }
+ if ( *pResumeHandle == 0) {
+ if ( Error != NO_ERROR) {
+ MessagePrint( Error);
+ }
+ break;
+ }
+ if ( Error != ERROR_MORE_DATA) {
+ MessagePrint( Error);
+ MessagePrint( IDS_UNEXPECTED_RETURN_CODE, Error);
+ break;
+ }
+ }
+}
+
+
+VOID TestAdapterAdd( IN LPVOID Buffer)
+{
+ DWORD Error;
+ DWORD ErrorParameter;
+
+#ifdef RPL_DEBUG
+ RplDbgPrint(( "\nTestAdapterAdd\n"));
+ AdapterDisplayInfo( 1, Buffer);
+#endif
+ Error = NetRplAdapterAdd( GlobalServerHandle, 1, Buffer, &ErrorParameter);
+ if ( Error != NO_ERROR) {
+ MessagePrint( Error);
+ return;
+ }
+}
+
+
+VOID AdapterAdd( VOID)
+{
+ RPL_ADAPTER_INFO_1 Info;
+
+ if ( !ReadString( IDS_PROMPT_ADAPTERNAME, &Info.AdapterName, TRUE)) {
+ return;
+ }
+ Info.Flags = 0;
+ RplPrintf0( IDS_PARAMETERSOPTIONAL );
+ if ( !ReadString( IDS_PROMPT_ADAPTERCOMMENT, &Info.AdapterComment, FALSE)) {
+ return;
+ }
+ TestAdapterAdd( &Info);
+}
+
+
+VOID TestBootAdd( IN LPVOID Buffer)
+{
+ DWORD Error;
+ DWORD ErrorParameter;
+
+#ifdef RPL_DEBUG
+ RplDbgPrint(( "\nTestBootAdd\n"));
+ BootDisplayInfo( 2, Buffer);
+#endif
+ Error = NetRplBootAdd( GlobalServerHandle, 2, Buffer, &ErrorParameter);
+ if ( Error != NO_ERROR) {
+ MessagePrint( Error);
+ return;
+ }
+}
+
+
+VOID BootAdd( VOID)
+{
+ RPL_BOOT_INFO_2 Info;
+
+ if ( !ReadString( IDS_PROMPT_BOOTNAME, &Info.BootName, TRUE)) {
+ return;
+ }
+ if ( !ReadString( IDS_PROMPT_VENDORNAME, &Info.VendorName, TRUE)) {
+ return;
+ }
+ if ( !ReadString( IDS_PROMPT_BBCFILE, &Info.BbcFile, TRUE)) {
+ return;
+ }
+ Info.Flags = BOOT_FLAGS_FINAL_ACKNOWLEDGMENT_TRUE;
+ RplPrintf0( IDS_PARAMETERSOPTIONAL );
+ if ( !ReadString( IDS_PROMPT_BOOTCOMMENT, &Info.BootComment, FALSE)) {
+ return;
+ }
+ Info.WindowSize = 0;
+ if ( !ReadInt( IDS_PROMPT_WINDOWSIZE, &Info.WindowSize, FALSE)) {
+ return;
+ }
+ TestBootAdd( &Info);
+}
+
+
+VOID BootDel( VOID)
+{
+ PWCHAR BootName;
+ PWCHAR VendorName;
+ DWORD Error;
+
+ if ( !ReadString( IDS_PROMPT_BOOTNAME, &BootName, TRUE)) {
+ return;
+ }
+ if ( !ReadString( IDS_PROMPT_VENDORNAME, &VendorName, TRUE)) {
+ return;
+ }
+ Error = NetRplBootDel( GlobalServerHandle, BootName, VendorName);
+ if ( Error != NO_ERROR) {
+ MessagePrint( Error);
+ return;
+ }
+}
+
+
+VOID TestBootEnum(
+ IN DWORD Level,
+ IN DWORD PrefMaxLength,
+ IN PDWORD pResumeHandle
+ )
+{
+ LPBYTE Buffer;
+ DWORD EntriesRead;
+ DWORD TotalEntries;
+ DWORD CoreSize;
+ DWORD index;
+ DWORD Error;
+
+ switch( Level) {
+ case 0:
+ CoreSize = sizeof( RPL_BOOT_INFO_0);
+ break;
+ case 1:
+ CoreSize = sizeof( RPL_BOOT_INFO_1);
+ break;
+ case 2:
+ CoreSize = sizeof( RPL_BOOT_INFO_2);
+ break;
+ default:
+ return;
+ break;
+ }
+
+#ifdef RPL_DEBUG
+ RplDbgPrint(( "\nTestBootEnum: Level=%d", Level));
+ if ( PrefMaxLength != RPL_BUFFER_GET_ALL) {
+ RplDbgPrint(( ", PrefMaxLength=%ld", PrefMaxLength));
+ } else {
+ RplDbgPrint(( ", unlimited buffer size"));
+ }
+ if ( pResumeHandle != NULL) {
+ RplDbgPrint(( ", ResumeHandle=0x%x\n\n", *pResumeHandle));
+ } else {
+ RplDbgPrint(( ", not resumable.\n\n"));
+ }
+#endif
+
+ for ( ; ; ) {
+ Error = NetRplBootEnum( GlobalServerHandle, Level, &Buffer,
+ PrefMaxLength, &EntriesRead, &TotalEntries, pResumeHandle);
+
+ if ( Error != NO_ERROR && Error != ERROR_MORE_DATA) {
+ MessagePrint( Error);
+ break;
+ }
+
+#ifdef RPL_DEBUG
+ RplDbgPrint(( "Buffer = 0x%x, EntriesRead = %d, TotalEntries = %d", Buffer,
+ EntriesRead, TotalEntries));
+ if ( pResumeHandle != NULL) {
+ RplDbgPrint(( ", ResumeHandle = 0x%x\n", *pResumeHandle));
+ } else {
+ RplDbgPrint(("\n"));
+ }
+#endif
+
+ for ( index = 0; index < EntriesRead; index++) {
+ BootDisplayInfo( Level, Buffer + index * CoreSize);
+ }
+ NetApiBufferFree( Buffer); // =~ MIDL_user_free()
+
+ if ( pResumeHandle == NULL) {
+ break;
+ }
+ if ( *pResumeHandle == 0) {
+ if ( Error != NO_ERROR) {
+ MessagePrint( Error);
+ }
+ break;
+ }
+ if ( Error != ERROR_MORE_DATA) {
+ MessagePrint( Error);
+ MessagePrint( IDS_UNEXPECTED_RETURN_CODE, Error);
+ break;
+ }
+ }
+}
+
+
+VOID BootEnum( VOID)
+{
+ DWORD Level;
+ DWORD PrefMaxLength;
+ DWORD ResumeHandle;
+
+ if ( !ReadInt( IDS_PROMPT_LEVEL012, &Level, TRUE) || Level > 2) {
+ return;
+ }
+#ifdef RPL_DEBUG
+ if ( !ReadInt( IDS_PROMPT_PREFMAXLENGTH_DEBUG, &PrefMaxLength, TRUE)) {
+ return;
+ }
+#else
+ PrefMaxLength = RPL_BUFFER_GET_ALL;
+#endif
+ if ( PrefMaxLength != RPL_BUFFER_GET_ALL) {
+ ResumeHandle = 0, TestBootEnum( Level, PrefMaxLength, &ResumeHandle);
+ } else {
+ TestBootEnum( Level, RPL_BUFFER_GET_ALL, NULL);
+ }
+}
+
+
+VOID TestConfigAdd( IN LPVOID Buffer)
+{
+ DWORD Error;
+ DWORD ErrorParameter;
+
+#ifdef RPL_DEBUG
+ RplDbgPrint(( "\nTestConfigAdd\n"));
+ ConfigDisplayInfo( 2, Buffer);
+#endif
+ Error = NetRplConfigAdd( GlobalServerHandle, 2, Buffer, &ErrorParameter);
+ if ( Error != NO_ERROR) {
+ MessagePrint( Error);
+ RplDbgPrint(( "ErrorParameter = %d\n", ErrorParameter));
+ return;
+ }
+}
+
+
+VOID ConfigAdd( VOID)
+{
+ RPL_CONFIG_INFO_2 Info;
+
+ if ( !ReadString( IDS_PROMPT_CONFIGNAME, &Info.ConfigName, TRUE)) {
+ return;
+ }
+ if ( !ReadString( IDS_PROMPT_BOOTNAME, &Info.BootName, TRUE)) {
+ return;
+ }
+ if ( !ReadString( IDS_PROMPT_DIRNAME, &Info.DirName, TRUE)) {
+ return;
+ }
+ if ( !ReadString( IDS_PROMPT_DIRNAME2, &Info.DirName2, TRUE)) {
+ return;
+ }
+ if ( !ReadString( IDS_PROMPT_FITSHARED, &Info.FitShared, TRUE)) {
+ return;
+ }
+ if ( !ReadString( IDS_PROMPT_FITPERSONAL, &Info.FitPersonal, TRUE)) {
+ return;
+ }
+ Info.Flags = 0;
+ RplPrintf0( IDS_PARAMETERSOPTIONAL );
+ if ( !ReadString( IDS_PROMPT_CONFIGCOMMENT, &Info.ConfigComment, FALSE)) {
+ return;
+ }
+ if ( !ReadString( IDS_PROMPT_DIRNAME3, &Info.DirName3, FALSE)) {
+ return;
+ }
+ if ( !ReadString( IDS_PROMPT_DIRNAME4, &Info.DirName4, FALSE)) {
+ return;
+ }
+ TestConfigAdd( &Info);
+}
+
+
+VOID ConfigDel( VOID)
+{
+ PWCHAR ConfigName;
+ DWORD Error;
+
+ if ( !ReadString( IDS_PROMPT_CONFIGNAME, &ConfigName, FALSE)) {
+ return;
+ }
+ Error = NetRplConfigDel( GlobalServerHandle, ConfigName);
+ if ( Error != 0) {
+ MessagePrint( Error);
+ }
+}
+
+
+VOID TestVendorAdd( IN LPVOID Buffer)
+{
+ DWORD Error;
+ DWORD ErrorParameter;
+
+#ifdef RPL_DEBUG
+ RplDbgPrint(( "\nTestVendorAdd\n"));
+ VendorDisplayInfo( 1, Buffer);
+#endif
+ Error = NetRplVendorAdd( GlobalServerHandle, 1, Buffer, &ErrorParameter);
+ if ( Error != NO_ERROR) {
+ MessagePrint( Error);
+ RplDbgPrint(( "ErrorParameter = %d\n", ErrorParameter));
+ return;
+ }
+}
+
+
+VOID VendorAdd( VOID)
+{
+ RPL_VENDOR_INFO_1 Info;
+
+ if ( !ReadString( IDS_PROMPT_VENDORNAME, &Info.VendorName, TRUE)) {
+ return;
+ }
+ Info.Flags = 0;
+ RplPrintf0( IDS_PARAMETERSOPTIONAL );
+ if ( !ReadString( IDS_PROMPT_VENDORCOMMENT, &Info.VendorComment, FALSE)) {
+ return;
+ }
+ TestVendorAdd( &Info);
+}
+
+
+VOID VendorDel( VOID)
+{
+ PWCHAR VendorName;
+ DWORD Error;
+
+ if ( !ReadString( IDS_PROMPT_VENDORNAME, &VendorName, FALSE)) {
+ return;
+ }
+ Error = NetRplVendorDel( GlobalServerHandle, VendorName);
+ if ( Error != 0) {
+ MessagePrint( Error);
+ }
+}
+
+
+VOID TestVendorEnum(
+ IN DWORD Level,
+ IN DWORD PrefMaxLength,
+ IN PDWORD pResumeHandle
+ )
+{
+ LPBYTE Buffer;
+ DWORD EntriesRead;
+ DWORD TotalEntries;
+ DWORD CoreSize;
+ DWORD index;
+ DWORD Error;
+
+ switch( Level) {
+ case 0:
+ CoreSize = sizeof( RPL_VENDOR_INFO_0);
+ break;
+ case 1:
+ CoreSize = sizeof( RPL_VENDOR_INFO_1);
+ break;
+ default:
+ return;
+ break;
+ }
+
+#ifdef RPL_DEBUG
+ RplDbgPrint(( "\nTestVendorEnum: Level=%d", Level));
+ if ( PrefMaxLength != RPL_BUFFER_GET_ALL) {
+ RplDbgPrint(( ", PrefMaxLength=%ld", PrefMaxLength));
+ } else {
+ RplDbgPrint(( ", unlimited buffer size"));
+ }
+ if ( pResumeHandle != NULL) {
+ RplDbgPrint(( ", ResumeHandle=0x%x\n\n", *pResumeHandle));
+ } else {
+ RplDbgPrint(( ", not resumable.\n\n"));
+ }
+#endif
+
+ for ( ; ; ) {
+ Error = NetRplVendorEnum( GlobalServerHandle, Level, &Buffer,
+ PrefMaxLength, &EntriesRead, &TotalEntries, pResumeHandle);
+
+ if ( Error != NO_ERROR && Error != ERROR_MORE_DATA) {
+ MessagePrint( Error);
+ break;
+ }
+
+#ifdef RPL_DEBUG
+ RplDbgPrint(( "Buffer = 0x%x, EntriesRead = %d, TotalEntries = %d", Buffer,
+ EntriesRead, TotalEntries));
+ if ( pResumeHandle != NULL) {
+ RplDbgPrint(( ", ResumeHandle = 0x%x\n", *pResumeHandle));
+ } else {
+ RplDbgPrint(("\n"));
+ }
+#endif
+
+ for ( index = 0; index < EntriesRead; index++) {
+ VendorDisplayInfo( Level, Buffer + index * CoreSize);
+ }
+ NetApiBufferFree( Buffer); // =~ MIDL_user_free()
+
+ if ( pResumeHandle == NULL) {
+ break;
+ }
+ if ( *pResumeHandle == 0) {
+ if ( Error != NO_ERROR) {
+ MessagePrint( Error);
+ }
+ break;
+ }
+ if ( Error != ERROR_MORE_DATA) {
+ MessagePrint( Error);
+ MessagePrint( IDS_UNEXPECTED_RETURN_CODE, Error);
+ break;
+ }
+ }
+}
+
+
+VOID VendorEnum( VOID)
+{
+ DWORD Level;
+ DWORD PrefMaxLength;
+ DWORD ResumeHandle;
+
+#ifdef RPL_DEBUG
+ if ( !ReadInt( IDS_PROMPT_LEVEL01, &Level, TRUE) || Level > 1) {
+ return;
+ }
+ if ( !ReadInt( IDS_PROMPT_PREFMAXLENGTH_DEBUG, &PrefMaxLength, TRUE)) {
+ return;
+ }
+#else
+ Level = 0;
+ PrefMaxLength = RPL_BUFFER_GET_ALL;
+#endif
+ if ( PrefMaxLength != RPL_BUFFER_GET_ALL) {
+ ResumeHandle = 0, TestVendorEnum( Level, PrefMaxLength, &ResumeHandle);
+ } else {
+ TestVendorEnum( Level, RPL_BUFFER_GET_ALL, NULL);
+ }
+}
+
+
+VOID TestProfileAdd( IN LPVOID Buffer)
+{
+ DWORD Error;
+ DWORD ErrorParameter;
+
+#ifdef RPL_DEBUG
+ RplDbgPrint(( "\nTestProfileAdd\n"));
+ ProfileDisplayInfo( FALSE, 2, Buffer);
+#endif
+ Error = NetRplProfileAdd( GlobalServerHandle, 2, Buffer, &ErrorParameter);
+ if ( Error != NO_ERROR) {
+ MessagePrint( Error);
+ RplDbgPrint(( "ErrorParameter = %d\n", ErrorParameter));
+ return;
+ }
+}
+
+
+VOID TestProfileGetInfo(
+ IN DWORD Level,
+ IN PWCHAR ProfileName
+ )
+{
+ LPBYTE Buffer;
+ DWORD Error;
+ Error = NetRplProfileGetInfo( GlobalServerHandle, ProfileName, Level, &Buffer);
+ if ( Error != NO_ERROR) {
+ MessagePrint( Error);
+ return;
+ }
+ ProfileDisplayInfo( FALSE, Level, Buffer);
+ NetApiBufferFree( Buffer); // =~ MIDL_user_free()
+}
+
+
+VOID TestProfileSetInfo(
+ IN DWORD Level,
+ IN PWCHAR ProfileName,
+ IN LPVOID Buffer
+ )
+{
+ DWORD Error;
+ DWORD ErrorParameter;
+#ifdef RPL_DEBUG
+ RplDbgPrint(( "\nTestProfileSetInfo: Level=%d, ProfileName=%ws\n", Level, ProfileName));
+ ProfileDisplayInfo( TRUE, Level, Buffer);
+#endif
+ Error = NetRplProfileSetInfo( GlobalServerHandle, ProfileName, Level, Buffer, &ErrorParameter);
+ if ( Error != NO_ERROR) {
+ MessagePrint( Error);
+ RplDbgPrint(( "ErrorParameter = %d\n", ErrorParameter));
+ return;
+ }
+}
+
+
+VOID TestWkstaAdd( IN LPVOID Buffer)
+{
+ DWORD Error;
+ DWORD ErrorParameter;
+
+#ifdef RPL_DEBUG
+ RplDbgPrint(( "\nTestWkstaAdd\n"));
+ WkstaDisplayInfo( 2, Buffer);
+#endif
+ Error = NetRplWkstaAdd( GlobalServerHandle, 2, Buffer, &ErrorParameter);
+ if ( Error != NO_ERROR) {
+ MessagePrint( Error);
+ RplDbgPrint(( "ErrorParameter = %d\n", ErrorParameter));
+ return;
+ }
+}
+
+
+VOID TestWkstaGetInfo(
+ IN DWORD Level,
+ IN PWCHAR WkstaName
+ )
+{
+ LPBYTE Buffer;
+ DWORD Error;
+
+#ifdef RPL_DEBUG
+ RplDbgPrint(( "\nWkstaGetInfo: Level=%d, WkstaName=%ws\n", Level, WkstaName));
+#endif
+ Error = NetRplWkstaGetInfo( GlobalServerHandle, WkstaName, Level, &Buffer);
+ if ( Error != NO_ERROR) {
+ MessagePrint( Error);
+ return;
+ }
+ WkstaDisplayInfo( Level, Buffer);
+ NetApiBufferFree( Buffer); // =~ MIDL_user_free()
+}
+
+
+VOID TestWkstaSetInfo(
+ IN DWORD Level,
+ IN PWCHAR WkstaName,
+ IN LPVOID Buffer
+ )
+{
+ DWORD Error;
+ DWORD ErrorParameter;
+
+#ifdef RPL_DEBUG
+ RplDbgPrint(( "\nWkstaSetInfo: Level=%d, WkstaName=%ws\n", Level, WkstaName));
+ WkstaDisplayInfo( Level, Buffer);
+#endif
+ Error = NetRplWkstaSetInfo( GlobalServerHandle, WkstaName, Level, Buffer, &ErrorParameter);
+ if ( Error != NO_ERROR) {
+ MessagePrint( Error);
+ RplDbgPrint(( "ErrorParameter = %d\n", ErrorParameter));
+ return;
+ }
+}
+
+
+VOID AdapterDel( VOID)
+{
+ PWCHAR AdapterName;
+ DWORD Error;
+
+ if ( !ReadString( IDS_PROMPT_ADAPTERNAME, &AdapterName, FALSE)) {
+ return;
+ }
+ if ( AdapterName == NULL) {
+ switch ( ReadFromMenu( IDS_PROMPT_MENU_ADAPTERDELETEALL,
+ IDS_PROMPT_FILTER_YN) ) {
+ case 0:
+ break;
+ case 1:
+ default:
+ return;
+ }
+ }
+
+ Error = NetRplAdapterDel( GlobalServerHandle, AdapterName);
+ if ( Error != 0) {
+ MessagePrint( Error);
+ }
+}
+
+
+VOID AdapterEnum( VOID)
+{
+ DWORD Level;
+ DWORD PrefMaxLength;
+ DWORD ResumeHandle;
+
+#ifdef RPL_DEBUG
+ if ( !ReadInt( IDS_PROMPT_LEVEL01, &Level, TRUE) || Level > 1) {
+ return;
+ }
+ if ( !ReadInt( IDS_PROMPT_PREFMAXLENGTH_DEBUG, &PrefMaxLength, TRUE)) {
+ return;
+ }
+#else
+ Level = 0;
+ PrefMaxLength = RPL_BUFFER_GET_ALL;
+#endif
+ if ( PrefMaxLength != RPL_BUFFER_GET_ALL) {
+ ResumeHandle = 0, TestAdapterEnum( Level, PrefMaxLength, &ResumeHandle);
+ } else {
+ TestAdapterEnum( Level, RPL_BUFFER_GET_ALL, NULL);
+ }
+}
+
+
+VOID ConfigEnum( VOID)
+{
+ DWORD Level;
+ DWORD PrefMaxLength;
+ DWORD ResumeHandle;
+ PWCHAR AdapterName;
+
+ if ( !ReadInt( IDS_PROMPT_LEVEL012, &Level, TRUE) || Level > 2) {
+ return;
+ }
+#ifdef RPL_DEBUG
+ if ( !ReadInt( IDS_PROMPT_PREFMAXLENGTH_DEBUG, &PrefMaxLength, TRUE)) {
+ return;
+ }
+#else
+ PrefMaxLength = RPL_BUFFER_GET_ALL;
+#endif
+ if( !ReadString( IDS_PROMPT_ADAPTERNAME_COMPATCONFIG, &AdapterName, FALSE)) {
+ return;
+ }
+ if ( PrefMaxLength != RPL_BUFFER_GET_ALL) {
+ ResumeHandle = 0, TestConfigEnum( Level, AdapterName, PrefMaxLength, &ResumeHandle);
+ } else {
+ TestConfigEnum( Level, AdapterName, RPL_BUFFER_GET_ALL, NULL);
+ }
+}
+
+
+VOID ProfileAdd( VOID)
+{
+ RPL_PROFILE_INFO_2 Info;
+
+ if ( !ReadString( IDS_PROMPT_PROFILENAME, &Info.ProfileName, TRUE)) {
+ return;
+ }
+ if ( !ReadString( IDS_PROMPT_CONFIGNAME, &Info.ConfigName, TRUE)) {
+ return;
+ }
+ Info.Flags = 0;
+ RplPrintf0( IDS_PARAMETERSOPTIONAL );
+ if ( !ReadString( IDS_PROMPT_PROFILECOMMENT, &Info.ProfileComment, FALSE)) {
+ return;
+ }
+ if ( !ReadString( IDS_PROMPT_BOOTNAME, &Info.BootName, FALSE)) {
+ return;
+ }
+ if ( !ReadString( IDS_PROMPT_FITSHARED, &Info.FitShared, FALSE)) {
+ return;
+ }
+ if ( !ReadString( IDS_PROMPT_FITPERSONAL, &Info.FitPersonal, FALSE)) {
+ return;
+ }
+ TestProfileAdd( &Info);
+}
+
+
+VOID ProfileClone( VOID)
+{
+ PWCHAR SourceProfileName;
+ PWCHAR TargetProfileName;
+ PWCHAR TargetProfileComment;
+ DWORD Error;
+
+ if ( !ReadString( IDS_PROMPT_PROFILENAME_SOURCE, &SourceProfileName, TRUE)) {
+ return;
+ }
+ if ( !ReadString( IDS_PROMPT_PROFILENAME_TARGET, &TargetProfileName, TRUE)) {
+ return;
+ }
+ if ( !ReadString( IDS_PROMPT_PROFILECOMMENT_TARGET, &TargetProfileComment, FALSE)) {
+ return;
+ }
+ Error = NetRplProfileClone( GlobalServerHandle, SourceProfileName,
+ TargetProfileName, TargetProfileComment);
+ if ( Error != NO_ERROR) {
+ MessagePrint( Error);
+ return;
+ }
+}
+
+
+VOID ProfileDel( VOID)
+{
+ PWCHAR ProfileName;
+ DWORD Error;
+
+ if ( !ReadString( IDS_PROMPT_PROFILENAME, &ProfileName, TRUE)) {
+ return;
+ }
+ Error = NetRplProfileDel( GlobalServerHandle, ProfileName);
+ if ( Error != NO_ERROR) {
+ MessagePrint( Error);
+ return;
+ }
+}
+
+
+VOID ProfileEnum( VOID)
+{
+ DWORD Level;
+ DWORD PrefMaxLength;
+ DWORD ResumeHandle;
+ PWCHAR AdapterName;
+
+ if ( !ReadInt( IDS_PROMPT_LEVEL012, &Level, TRUE) || Level > 2) {
+ return;
+ }
+#ifdef RPL_DEBUG
+ if ( !ReadInt( IDS_PROMPT_PREFMAXLENGTH_DEBUG, &PrefMaxLength, TRUE)) {
+ return;
+ }
+#else
+ PrefMaxLength = RPL_BUFFER_GET_ALL;
+#endif
+ if( !ReadString( IDS_PROMPT_ADAPTERNAME_COMPATPROF, &AdapterName, FALSE)) {
+ return;
+ }
+ if ( PrefMaxLength != RPL_BUFFER_GET_ALL) {
+ ResumeHandle = 0, TestProfileEnum( Level, AdapterName, PrefMaxLength, &ResumeHandle);
+ } else {
+ TestProfileEnum( Level, AdapterName, RPL_BUFFER_GET_ALL, NULL);
+ }
+}
+
+
+VOID ProfileGetInfo( VOID)
+{
+ BYTE Line[ 300];
+ WCHAR ProfileName[ 20];
+ CHAR ProfileNameA[ 20];
+ DWORD Length;
+ DWORD Count;
+ DWORD Level;
+
+ RplPrintf0( IDS_PROMPT_PROFILENAME_INFOLEVEL );
+ if ( gets( Line) == NULL) {
+ return;
+ }
+ Count = sscanf( Line, "%d %s", &Level, ProfileNameA);
+ if ( Count != 2) {
+ RplPrintf0( IDS_BADARGCOUNT );
+ return;
+ }
+ Length = MultiByteToWideChar( CP_OEMCP, MB_PRECOMPOSED, ProfileNameA, -1,
+ ProfileName, sizeof( ProfileName));
+ if ( Length == 0) {
+ RplPrintf1( IDS_INVALIDSTRING_CHARSTRINGA, (LPWSTR)ProfileNameA);
+ return;
+ }
+ TestProfileGetInfo( Level, ProfileName);
+}
+
+
+VOID ProfileSetInfo( VOID)
+{
+ PWCHAR ProfileName;
+ DWORD Level;
+ RPL_PROFILE_INFO_2 Info;
+ LPVOID Buffer;
+
+ Buffer = &Info;
+ if ( !ReadInt( IDS_PROMPT_LEVEL012, &Level, TRUE) || Level > 2) {
+ return;
+ }
+ if ( !ReadString( IDS_PROMPT_PROFILENAME_INPUT, &ProfileName, TRUE)) {
+ return;
+ }
+ RplPrintf0( IDS_INPUTPROFILEPROPERTIES );
+ Info.ProfileName = NULL;
+ // ReadString( "ProfileName", &Info.ProfileName);
+ if ( !ReadString( IDS_PROMPT_PROFILECOMMENT, &Info.ProfileComment, FALSE)) {
+ return;
+ }
+ if ( Level == 0) {
+ goto testit;
+ }
+ Info.Flags = 0;
+ if ( Level == 1) {
+ goto testit;
+ }
+ Info.ConfigName = NULL;
+ // ReadString( IDS_PROMPT_CONFIGNAME, &Info.ConfigName, FALSE)) {
+ if ( !ReadString( IDS_PROMPT_BOOTNAME, &Info.BootName, FALSE)) {
+ return;
+ }
+ if ( !ReadString( IDS_PROMPT_FITSHARED, &Info.FitShared, FALSE)) {
+ return;
+ }
+ if ( !ReadString( IDS_PROMPT_FITPERSONAL, &Info.FitPersonal, FALSE)) {
+ return;
+ }
+testit:
+ TestProfileSetInfo( Level, ProfileName, Buffer);
+}
+
+
+VOID ServiceClose( VOID)
+{
+ DWORD Error;
+
+ if ( GlobalHaveServerHandle == FALSE) {
+ RplPrintf0( IDS_NOSERVICEHANDLE );
+ return;
+ }
+ Error = NetRplClose( GlobalServerHandle);
+ if ( Error != NO_ERROR) {
+ MessagePrint( Error);
+ return;
+ }
+ LocalFree( GlobalServerName); // OK if NULL pointer
+ GlobalHaveServerHandle = FALSE;
+}
+
+
+VOID ServiceGetInfo( VOID)
+{
+ LPBYTE Buffer;
+ DWORD Error;
+ Error = NetRplGetInfo( GlobalServerHandle, 0, &Buffer);
+ if ( Error != NO_ERROR) {
+ MessagePrint( Error);
+ return;
+ }
+ if ( GlobalServerName == NULL) {
+ RplPrintf0( IDS_RPLSVC_LOCAL_COMPUTER);
+ } else {
+ RplPrintf1( IDS_RPLSVC_COMPUTER, GlobalServerName);
+ }
+ ServiceDisplayInfo( 0, Buffer);
+ NetApiBufferFree( Buffer); // =~ MIDL_user_free()
+}
+
+
+VOID ServiceOpen( VOID)
+{
+ DWORD Error;
+
+ if ( GlobalHaveServerHandle == TRUE) {
+ RplPrintf0( IDS_MUSTCLOSEHANDLE );
+ return;
+ }
+ if ( !ReadString( IDS_PROMPT_SERVERNAME, &GlobalServerName, FALSE)) {
+ return;
+ }
+ Error = NetRplOpen( GlobalServerName, &GlobalServerHandle);
+ if ( Error != NO_ERROR) {
+ MessagePrint( Error);
+ return;
+ }
+ GlobalHaveServerHandle = TRUE;
+}
+
+
+BOOL ReadServiceFlags(
+ OUT PDWORD pFlags
+ )
+{
+ *pFlags = 0;
+
+ switch( ReadFromMenu( IDS_PROMPT_MENU_SERVICE_SECURITY,
+ IDS_PROMPT_FILTER_YN)) {
+ case 0:
+ *pFlags |= RPL_CHECK_SECURITY;
+ break;
+ case 1:
+ break;
+ default:
+ // this really shouldn't happen; fall through
+ case -1:
+ return( FALSE);
+ }
+
+ switch( ReadFromMenu( IDS_PROMPT_MENU_SERVICE_CONFIGS,
+ IDS_PROMPT_FILTER_YN)) {
+ case 0:
+ *pFlags |= RPL_CHECK_CONFIGS;
+ break;
+ case 1:
+ break;
+ default:
+ // this really shouldn't happen; fall through
+ case -1:
+ return( FALSE);
+ }
+
+#ifdef NOT_YET
+ switch( ReadFromMenu( IDS_PROMPT_MENU_SERVICE_RPLDISK,
+ IDS_PROMPT_FILTER_SERVICE_RPLDISK )) {
+ case 0:
+ *pFlags |= RPL_REPLACE_RPLDISK;
+ break;
+ case 1:
+ break;
+ default:
+ // this really shouldn't happen; fall through
+ case -1:
+ return( FALSE);
+ }
+#endif
+
+ switch( ReadFromMenu( IDS_PROMPT_MENU_SERVICE_BACKUP,
+ IDS_PROMPT_FILTER_YN)) {
+ case 0:
+ *pFlags |= RPL_BACKUP_DATABASE;
+ break;
+ case 1:
+ break;
+ default:
+ // this really shouldn't happen; fall through
+ case -1:
+ return( FALSE);
+ }
+
+ return( TRUE);
+}
+
+
+
+VOID ServiceSetInfo( VOID)
+{
+ DWORD Level;
+ LPVOID Buffer;
+ DWORD Error;
+ DWORD ErrorParameter;
+
+ Level = 0;
+
+#ifdef RPL_DEBUG
+ if ( !ReadInt( IDS_PROMPT_LEVEL_DEBUG, &Level, FALSE)) {
+ return;
+ }
+#endif
+
+ switch( Level) {
+ case 0: {
+ RPL_INFO_0 Info;
+ Buffer = &Info;
+ if ( !ReadServiceFlags( &Info.Flags)) {
+ return;
+ }
+ break;
+ }
+ default:
+ return;
+ break;
+ }
+ Error = NetRplSetInfo( GlobalServerHandle, 0, Buffer, &ErrorParameter);
+ if ( Error != NO_ERROR) {
+ MessagePrint( Error);
+ RplDbgPrint(( "ErrorParameter = %d\n", ErrorParameter));
+ return;
+ }
+}
+
+
+VOID WkstaAdd( VOID)
+{
+ RPL_WKSTA_INFO_2 Info;
+
+ if ( !ReadString( IDS_PROMPT_WKSTANAME, &Info.WkstaName, TRUE)) {
+ return;
+ }
+ if ( !ReadString( IDS_PROMPT_PROFILENAME, &Info.ProfileName, TRUE)) {
+ return;
+ }
+ if ( !ReadString( IDS_PROMPT_ADAPTERNAME, &Info.AdapterName, TRUE)) {
+ return;
+ }
+ if ( !ReadWkstaFlags( &Info.Flags)) {
+ return;
+ }
+ RplPrintf0( IDS_PARAMETERSOPTIONAL );
+ if ( !ReadString( IDS_PROMPT_WKSTACOMMENT, &Info.WkstaComment, FALSE)) {
+ return;
+ }
+ if ( !ReadString( IDS_PROMPT_BOOTNAME, &Info.BootName, FALSE)) {
+ return;
+ }
+ if ( !ReadString( IDS_PROMPT_FITFILE, &Info.FitFile, FALSE)) {
+ return;
+ }
+ Info.TcpIpAddress = (DWORD)-1;
+ if ( !ReadInt( IDS_PROMPT_TCPIPADDRESS, &Info.TcpIpAddress, FALSE)) {
+ return;
+ }
+ Info.TcpIpSubnet = (DWORD)-1;
+ if ( !ReadInt( IDS_PROMPT_TCPIPSUBNET, &Info.TcpIpSubnet, FALSE)) {
+ return;
+ }
+ Info.TcpIpGateway = (DWORD)-1;
+ if ( !ReadInt( IDS_PROMPT_TCPIPGATEWAY, &Info.TcpIpGateway, FALSE)) {
+ return;
+ }
+ TestWkstaAdd( &Info);
+}
+
+
+VOID WkstaClone( VOID)
+{
+ PWCHAR SourceWkstaName;
+ PWCHAR TargetWkstaName;
+ PWCHAR TargetWkstaComment;
+ PWCHAR TargetAdapterName;
+ DWORD TargetTcpIpAddress;
+ DWORD Error;
+
+ if ( !ReadString( IDS_PROMPT_WKSTANAME_SOURCE, &SourceWkstaName, TRUE)) {
+ return;
+ }
+ if ( !ReadString( IDS_PROMPT_WKSTANAME_TARGET, &TargetWkstaName, TRUE)) {
+ return;
+ }
+ if ( !ReadString( IDS_PROMPT_ADAPTERNAME_TARGET, &TargetAdapterName, TRUE)) {
+ return;
+ }
+ RplPrintf0( IDS_PARAMETERSOPTIONAL );
+ if ( !ReadString( IDS_PROMPT_WKSTACOMMENT_TARGET, &TargetWkstaComment, FALSE)) {
+ return;
+ }
+ TargetTcpIpAddress = (DWORD)-1;
+ if( !ReadInt( IDS_PROMPT_TCPIPADDRESS, &TargetTcpIpAddress, FALSE)) {
+ return;
+ }
+ Error = NetRplWkstaClone( GlobalServerHandle, SourceWkstaName,
+ TargetWkstaName, TargetWkstaComment, TargetAdapterName, TargetTcpIpAddress);
+ if ( Error != NO_ERROR) {
+ MessagePrint( Error);
+ return;
+ }
+}
+
+
+VOID WkstaDel( VOID)
+{
+ PWCHAR WkstaName;
+ DWORD Error;
+
+ if ( !ReadString( IDS_PROMPT_WKSTANAME, &WkstaName, TRUE)) {
+ return;
+ }
+ Error = NetRplWkstaDel( GlobalServerHandle, WkstaName);
+ if ( Error != NO_ERROR) {
+ MessagePrint( Error);
+ return;
+ }
+}
+
+
+VOID WkstaEnum( VOID)
+{
+ DWORD Level;
+ DWORD PrefMaxLength;
+ DWORD ResumeHandle;
+ PWCHAR ProfileName;
+
+ if ( !ReadInt( IDS_PROMPT_LEVEL012, &Level, TRUE) || Level > 2) {
+ return;
+ }
+#ifdef RPL_DEBUG
+ if ( !ReadInt( IDS_PROMPT_PREFMAXLENGTH_DEBUG, &PrefMaxLength, TRUE)) {
+ return;
+ }
+#else
+ PrefMaxLength = RPL_BUFFER_GET_ALL;
+#endif
+ if( !ReadString( IDS_PROMPT_PROFILENAME_COMPATWKSTA, &ProfileName, FALSE)) {
+ return;
+ }
+ ResumeHandle = 0, TestWkstaEnum( Level, ProfileName, PrefMaxLength, &ResumeHandle);
+}
+
+
+VOID WkstaGetInfo( VOID)
+{
+ BYTE Line[ 300];
+ WCHAR WkstaName[ 20];
+ CHAR WkstaNameA[ 20];
+ DWORD Length;
+ DWORD Count;
+ DWORD Level;
+
+ RplPrintf0( IDS_PROMPT_WKSTANAME_INFOLEVEL );
+ if ( gets( Line) == NULL) {
+ return;
+ }
+ Count = sscanf( Line, "%d %s", &Level, WkstaNameA);
+ if ( Count != 2) {
+ RplPrintf0( IDS_BADARGCOUNT );
+ return;
+ }
+ Length = MultiByteToWideChar( CP_OEMCP, MB_PRECOMPOSED, WkstaNameA, -1,
+ WkstaName, sizeof( WkstaName));
+ if ( Length == 0) {
+ RplPrintf1( IDS_INVALIDSTRING_CHARSTRINGA, (LPWSTR)WkstaNameA);
+ return;
+ }
+ TestWkstaGetInfo( Level, WkstaName);
+}
+
+
+VOID WkstaSetInfo( VOID)
+{
+ PWCHAR WkstaName;
+ DWORD Level;
+ LPVOID Buffer;
+ RPL_WKSTA_INFO_2 Info;
+
+ Buffer = &Info;
+ if ( !ReadInt( IDS_PROMPT_LEVEL012, &Level, TRUE) || Level > 2) {
+ return;
+ }
+ if ( !ReadString( IDS_PROMPT_WKSTANAME_INPUT, &WkstaName, FALSE)) {
+ return;
+ }
+ RplPrintf0( IDS_INPUTWKSTAPROPERTIES );
+ if( !ReadString( IDS_PROMPT_WKSTANAME, &Info.WkstaName, FALSE)) {
+ return;
+ }
+ if( !ReadString( IDS_PROMPT_WKSTACOMMENT, &Info.WkstaComment, FALSE)) {
+ return;
+ }
+ if ( Level == 0) {
+ goto testit;
+ }
+ if ( !ReadWkstaFlags( &Info.Flags)) {
+ return;
+ }
+ if( !ReadString( IDS_PROMPT_PROFILENAME, &Info.ProfileName, FALSE)) {
+ return;
+ }
+ if ( Level == 1) {
+ goto testit;
+ }
+ if ( !ReadString( IDS_PROMPT_BOOTNAME, &Info.BootName, FALSE)) {
+ return;
+ }
+ if ( !ReadString( IDS_PROMPT_FITFILE, &Info.FitFile, FALSE)) {
+ return;
+ }
+ if ( !ReadString( IDS_PROMPT_ADAPTERNAME, &Info.AdapterName, FALSE)) {
+ return;
+ }
+ Info.TcpIpAddress = (DWORD)-1;
+ if ( !ReadInt( IDS_PROMPT_TCPIPADDRESS, &Info.TcpIpAddress, FALSE)) {
+ return;
+ }
+ Info.TcpIpSubnet = (DWORD)-1;
+ if ( !ReadInt( IDS_PROMPT_TCPIPSUBNET, &Info.TcpIpSubnet, FALSE)) {
+ return;
+ }
+ Info.TcpIpGateway = (DWORD)-1;
+ if ( !ReadInt( IDS_PROMPT_TCPIPGATEWAY, &Info.TcpIpGateway, FALSE)) {
+ return;
+ }
+testit:
+ TestWkstaSetInfo( Level, WkstaName, Buffer);
+}
+
+//
+// BUGBUG INTL: still must deal with char constants
+//
+
+
+VOID Worker( VOID)
+{
+ for ( ; ;) {
+ switch( ReadFromMenu( IDS_PROMPT_MENU_MAIN_ABCPSVWQ,
+ IDS_PROMPT_FILTER_MAIN_ABCPSVWQ )) {
+ case 0:
+ switch( ReadFromMenu( IDS_PROMPT_MENU_ADAPTER_ADE,
+ IDS_PROMPT_FILTER_ADAPTER_ADE )) {
+ case 0:
+ AdapterAdd();
+ break;
+ case 1:
+ AdapterDel();
+ break;
+ case 2:
+ AdapterEnum();
+ break;
+ }
+ break;
+ case 1:
+ switch( ReadFromMenu( IDS_PROMPT_MENU_BOOT_ADE,
+ IDS_PROMPT_FILTER_BOOT_ADE )) {
+ case 0:
+ BootAdd();
+ break;
+ case 1:
+ BootDel();
+ break;
+ case 2:
+ BootEnum();
+ break;
+ }
+ break;
+ case 2:
+ switch( ReadFromMenu( IDS_PROMPT_MENU_CONFIG_ADE,
+ IDS_PROMPT_FILTER_CONFIG_ADE )) {
+ case 0:
+ ConfigAdd();
+ break;
+ case 1:
+ ConfigDel();
+ break;
+ case 2:
+ ConfigEnum();
+ break;
+ }
+ break;
+ case 3:
+ switch( ReadFromMenu( IDS_PROMPT_MENU_PROFILE_ACDEGS,
+ IDS_PROMPT_FILTER_PROFILE_ACDEGS )) {
+ case 0:
+ ProfileAdd();
+ break;
+ case 1:
+ ProfileClone();
+ break;
+ case 2:
+ ProfileDel();
+ break;
+ case 3:
+ ProfileEnum();
+ break;
+ case 4:
+ ProfileGetInfo();
+ break;
+ case 5:
+ ProfileSetInfo();
+ break;
+ }
+ break;
+ case 4:
+ switch( ReadFromMenu( IDS_PROMPT_MENU_SERVICE_CGOS,
+ IDS_PROMPT_FILTER_SERVICE_CGOS )) {
+ case 0:
+ ServiceClose();
+ break;
+ case 1:
+ ServiceGetInfo();
+ break;
+ case 2:
+ ServiceOpen();
+ break;
+ case 3:
+ ServiceSetInfo();
+ break;
+ }
+ break;
+ case 5:
+ switch( ReadFromMenu( IDS_PROMPT_MENU_VENDOR_ADE,
+ IDS_PROMPT_FILTER_VENDOR_ADE )) {
+ case 0:
+ VendorAdd();
+ break;
+ case 1:
+ VendorDel();
+ break;
+ case 2:
+ VendorEnum();
+ break;
+ }
+ break;
+ case 6:
+ switch( ReadFromMenu( IDS_PROMPT_MENU_WKSTA_ACDEGS,
+ IDS_PROMPT_FILTER_WKSTA_ACDEGS )) {
+ case 0:
+ WkstaAdd();
+ break;
+ case 1:
+ WkstaClone();
+ break;
+ case 2:
+ WkstaDel();
+ break;
+ case 3:
+ WkstaEnum();
+ break;
+ case 4:
+ WkstaGetInfo();
+ break;
+ case 5:
+ WkstaSetInfo();
+ break;
+ }
+ break;
+ case 7:
+ return;
+ break;
+ default:
+ // go back to start of loop
+ break;
+ }
+ }
+}
+
+
+DWORD _CRTAPI1 main( int argc, char **argv)
+{
+ DWORD Error;
+ DWORD Length;
+
+ GlobalMessageHandle = LoadLibrary( L"netmsg.dll");
+ if ( GlobalMessageHandle == NULL) {
+ MessagePrint( IDS_LOAD_LIBRARY_FAILURE, GetLastError());
+ goto ErrorExit;
+ }
+
+ if ( argc == 1) {
+ GlobalServerName = NULL;
+ } else if ( argc == 2) {
+ Length = (MAX_PATH+1+2) * sizeof(WCHAR); // terminal null + two backslashes
+ GlobalServerName = LocalAlloc( GMEM_FIXED, Length);
+ if ( GlobalServerName == NULL) {
+ RplDbgPrint(( "LocalAlloc failed"));
+ goto ErrorExit;
+ }
+ Length = MultiByteToWideChar(
+ CP_OEMCP,
+ MB_PRECOMPOSED,
+ argv[ 1],
+ -1,
+ GlobalServerName,
+ Length
+ );
+ if ( Length == 0 || !_wcsicmp( GlobalServerName, QUESTION_SW)
+ || !_wcsicmp( GlobalServerName, QUESTION_SW_TOO)) {
+ goto ErrorExit;
+ }
+#ifdef RPL_DEBUG
+ RplPrintf1( IDS_TARGETSERVER, GlobalServerName);
+#endif
+ } else {
+ goto ErrorExit;
+ }
+
+ Error = NetRplOpen( GlobalServerName, &GlobalServerHandle);
+ if ( Error != NO_ERROR) {
+ MessagePrint( Error);
+ MessagePrint( IDS_OPEN_SERVICE_HANDLE);
+ goto ErrorExit;
+ }
+ GlobalHaveServerHandle = TRUE;
+
+ Worker();
+
+ if ( GlobalHaveServerHandle == FALSE) {
+ return(0);
+ }
+
+ Error = NetRplClose( GlobalServerHandle);
+ if ( Error != NO_ERROR) {
+ MessagePrint( Error);
+ MessagePrint( IDS_CLOSE_SERVICE_HANDLE);
+ goto ErrorExit;
+ }
+ GlobalHaveServerHandle = FALSE; // in case one adds code below
+ return(0);
+
+ErrorExit:
+
+ RplPrintf0( IDS_USAGE );
+ return(1);
+}
+
diff --git a/private/net/svcdlls/rpl/command/rplmsg.mc b/private/net/svcdlls/rpl/command/rplmsg.mc
new file mode 100644
index 000000000..b2c8d526b
--- /dev/null
+++ b/private/net/svcdlls/rpl/command/rplmsg.mc
@@ -0,0 +1,610 @@
+;/*++
+;
+;Copyright (c) 1992 Microsoft Corporation
+;
+;Module Name:
+;
+; rplmsg.h
+;
+;Abstract:
+;
+; Definitions for Remoteboot command private errors.
+;
+;Author:
+;
+; Vladimir Z. Vulovic (vladimv) 06 - November - 1992
+;
+;Revision History:
+; Jon Newman April - 28 - 1994
+; moved & cleaned up bunch of messages
+;
+;
+;Notes:
+;
+; This file is generated by the MC tool from the rplmsg.mc file.
+;
+;
+;--*/
+
+MessageId=10000 SymbolicName=IDS_LOAD_LIBRARY_FAILURE
+Language=English
+LoadLibrary() fails with winError = %1!d!%n%0
+.
+MessageId=+1 SymbolicName=IDS_UNEXPECTED_RETURN_CODE
+Language=English
+Remote boot server returned an unexpected error = %1!d!%n%0
+.
+MessageId=+1 SymbolicName=IDS_OPEN_SERVICE_HANDLE
+Language=English
+Failed to open remote boot service handle.%n%0
+.
+MessageId=+1 SymbolicName=IDS_CLOSE_SERVICE_HANDLE
+Language=English
+Failed to close remote boot service handle.%n%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_ADAPTERCOMMENT
+Language=English
+AdapterComment=%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_ADAPTERNAME
+Language=English
+AdapterName=%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_ADAPTERNAME_COMPATCONFIG
+Language=English
+To list only configurations compatible with a given network card, input AdapterName=%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_ADAPTERNAME_COMPATPROF
+Language=English
+To list only profiles compatible with a given network card, input AdapterName=%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_ADAPTERNAME_TARGET
+Language=English
+TargetAdapterName=%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_BOOTCOMMENT
+Language=English
+BootComment=%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_BOOTNAME
+Language=English
+BootName=%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_CONFIGCOMMENT
+Language=English
+ConfigComment=%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_CONFIGNAME
+Language=English
+ConfigName=%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_DIRNAME
+Language=English
+DirName=%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_DIRNAME2
+Language=English
+DirName2=%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_DIRNAME3
+Language=English
+DirName3=%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_DIRNAME4
+Language=English
+DirName4=%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_FITFILE
+Language=English
+FitFile=%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_FITPERSONAL
+Language=English
+FitPersonal=%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_FITSHARED
+Language=English
+FitShared=%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_LEVEL_DEBUG
+Language=English
+Information Level=%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_LEVEL01
+Language=English
+Information Level (0 or 1)=%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_LEVEL012
+Language=English
+Information Level (0, 1 or 2)=%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_MENU_ADAPTER_ADE
+Language=English
+Add Del Enum: %0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_FILTER_ADAPTER_ADE
+Language=English
+ADE%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_MENU_ADAPTER_DE
+Language=English
+Del Enum: %0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_MENU_ADAPTERDELETEALL
+Language=English
+Are you sure you wish to delete all adapters? (Y/N)=%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_MENU_BOOT_ADE
+Language=English
+Add Del Enum: %0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_FILTER_BOOT_ADE
+Language=English
+ADE%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_MENU_CONFIG_ADE
+Language=English
+Add Del Enum: %0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_FILTER_CONFIG_ADE
+Language=English
+ADE%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_MENU_MAIN_ABCPSVWQ
+Language=English
+Adapter Boot Config Profile Service Vendor Wksta [Quit]: %0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_FILTER_MAIN_ABCPSVWQ
+Language=English
+ABCPSVWQ%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_MENU_PROFILE_ACDEGS
+Language=English
+Add Clone Del Enum GetInfo SetInfo: %0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_FILTER_PROFILE_ACDEGS
+Language=English
+ACDEGS%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_MENU_SERVICE_CGOS
+Language=English
+Close GetInfo Open SetInfo: %0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_FILTER_SERVICE_CGOS
+Language=English
+CGOS%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_MENU_SERVICE_CONFIGS
+Language=English
+Would you like to check for installed configurations [Y,N]=%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_MENU_SERVICE_RPLDISK
+Language=English
+Would you like to update client boot files [Y,N]=%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_MENU_SERVICE_BACKUP
+Language=English
+Would you backup service database [Y,N]=%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_FILTER_YN
+Language=English
+YN%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_MENU_SERVICE_SECURITY
+Language=English
+Would you check logon domain for remoteboot workstations [Y,N]=%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_MENU_VENDOR_ADE
+Language=English
+Add Del Enum: %0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_FILTER_VENDOR_ADE
+Language=English
+ADE%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_MENU_WKSTA_ACDEGS
+Language=English
+Add Clone Del Enum GetInfo SetInfo: %0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_FILTER_WKSTA_ACDEGS
+Language=English
+ACDEGS%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_MENU_WKSTA_DELETE
+Language=English
+Delete corresponding user account when workstation is deleted by Remoteboot Manager [Y,N]=%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_MENU_WKSTA_DHCP
+Language=English
+Does RemoteBoot workstation use DHCP [Y,N]=%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_MENU_WKSTA_LOGON_INPUT
+Language=English
+Is Remoteboot password Required, Optional or Not solicited [R,O,N]=%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_FILTER_WKSTA_LOGON_INPUT
+Language=English
+RON%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_MENU_WKSTA_SHAREDORPRIVATE
+Language=English
+Does RemoteBoot workstation uses shared or private home directory [S,P]=%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_FILTER_WKSTA_SHAREDORPRIVATE
+Language=English
+SP%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_PREFMAXLENGTH_DEBUG
+Language=English
+PrefMaxLength (-1 for all data)=%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_PROFILECOMMENT
+Language=English
+ProfileComment=%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_PROFILECOMMENT_TARGET
+Language=English
+TargetProfileComment=%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_PROFILENAME
+Language=English
+ProfileName=%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_PROFILENAME_COMPATWKSTA
+Language=English
+To list only workstations using a given profile, input ProfileName=%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_PROFILENAME_INFOLEVEL
+Language=English
+Input Information Level (0, 1 or 2) and ProfileName%n%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_PROFILENAME_INPUT
+Language=English
+Input ProfileName=%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_PROFILENAME_SOURCE
+Language=English
+SourceProfileName=%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_PROFILENAME_TARGET
+Language=English
+TargetProfileName=%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_SERVERNAME
+Language=English
+ServerName=%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_TCPIPADDRESS
+Language=English
+TcpIpAddress (when DHCP is disabled)=%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_TCPIPGATEWAY
+Language=English
+TcpIpGateway (when DHCP is disabled)=%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_TCPIPSUBNET
+Language=English
+TcpIpSubnet (when DHCP is disabled)=%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_VENDORCOMMENT
+Language=English
+VendorComment=%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_VENDORNAME
+Language=English
+VendorName=%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_BBCFILE
+Language=English
+BbcFile=%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_WINDOWSIZE
+Language=English
+WindowSize=%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_WKSTACOMMENT
+Language=English
+WkstaComment=%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_WKSTACOMMENT_TARGET
+Language=English
+TargetWkstaComment=%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_WKSTANAME
+Language=English
+WkstaName=%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_WKSTANAME_INFOLEVEL
+Language=English
+Input Information Level (0, 1 or 2) and WkstaName%n%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_WKSTANAME_INPUT
+Language=English
+Input WkstaName=%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_WKSTANAME_SOURCE
+Language=English
+SourceWkstaName=%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_WKSTANAME_TARGET
+Language=English
+TargetWkstaName=%0
+.
+MessageId=+1 SymbolicName=IDS_BADARGCOUNT
+Language=English
+Bad number of arguments.%n%0
+.
+MessageId=+1 SymbolicName=IDS_BADMENUSEL
+Language=English
+Your input "%1!ws!" is invalid. One of "%2!ws!" was expected.%n%0
+.
+MessageId=+1 SymbolicName=IDS_INPUTPROFILEPROPERTIES
+Language=English
+Input new profile properties.%n%0
+.
+MessageId=+1 SymbolicName=IDS_INPUTWKSTAPROPERTIES
+Language=English
+Input new workstation properties.%n%0
+.
+MessageId=+1 SymbolicName=IDS_INVALIDSTRING_CHARSTRINGA
+Language=English
+Invalid string "%1!s!"%n%0
+.
+MessageId=+1 SymbolicName=IDS_MUSTCLOSEHANDLE
+Language=English
+Must close existing RemoteBoot service handle
+before opening new RemoteBoot service handle.%n%0
+.
+MessageId=+1 SymbolicName=IDS_NOSERVICEHANDLE
+Language=English
+You do not have an open RemoteBoot service handle.%n%0
+.
+MessageId=+1 SymbolicName=IDS_NOTSUPPLIED
+Language=English
+"%1!ws!" must be supplied%n%0
+.
+MessageId=+1 SymbolicName=IDS_INVALID_INPUT
+Language=English
+No valid input supplied%n%0
+.
+MessageId=+1 SymbolicName=IDS_TARGETSERVER
+Language=English
+ServerName = %1!ws!%n%0
+.
+MessageId=+1 SymbolicName=IDS_USAGE
+Language=English
+The RPLCMD utility can be used to view and update the Remoteboot service
+database. The Remoteboot service must already be running.%n
+%n
+RPLCMD [\\computername]%n
+%n
+\\computername Specifies a remote computer where the Remoteboot service%n
+ is running. If this parameter is omitted, RPLCMD manages%n
+ the Remoteboot service on the local computer.%n
+.
+MessageId=+1 SymbolicName=IDS_PARAMETERSOPTIONAL
+Language=English
+%tAll other parameters are optional%n%0
+.
+MessageId=+1 SymbolicName=IDS_ADAPTERBADFLAGS
+Language=English
+%tBad Adapter flags, Flags=0x%1!x!%n%0
+.
+MessageId=+1 SymbolicName=IDS_ADAPTERCOMMENT
+Language=English
+%tAdapterComment=%1!ws!%n%0
+.
+MessageId=+1 SymbolicName=IDS_BBCFILE
+Language=English
+%tBbcFile=%1!ws!%n%0
+.
+MessageId=+1 SymbolicName=IDS_BOOTACK_FALSE
+Language=English
+%tAcknowledgment of the last RemoteBoot frame is not requested.%n%0
+.
+MessageId=+1 SymbolicName=IDS_BOOTACK_TRUE
+Language=English
+%tAcknowledgment of the last RemoteBoot frame is requested.%n%0
+.
+MessageId=+1 SymbolicName=IDS_BOOTBADFLAGS
+Language=English
+%tBad Boot flags, Flags=0x%1!x!%n%0
+.
+MessageId=+1 SymbolicName=IDS_BOOTCOMMENT
+Language=English
+%tBootComment=%1!ws!%n%0
+.
+MessageId=+1 SymbolicName=IDS_BOOTNAME
+Language=English
+%tBootName=%1!ws!%n%0
+.
+MessageId=+1 SymbolicName=IDS_CONFIGBADFLAGS
+Language=English
+%tBad Configuration flags, Flags=0x%1!x!%n%0
+.
+MessageId=+1 SymbolicName=IDS_CONFIGCOMMENT
+Language=English
+%tConfigComment=%1!ws!%n%0
+.
+MessageId=+1 SymbolicName=IDS_CONFIGDISABLED
+Language=English
+%tConfiguration is disabled.%n%0
+.
+MessageId=+1 SymbolicName=IDS_CONFIGENABLED
+Language=English
+%tConfiguration is enabled.%n%0
+.
+MessageId=+1 SymbolicName=IDS_CONFIGNAME
+Language=English
+%tConfigName=%1!ws!%n%0
+.
+MessageId=+1 SymbolicName=IDS_DIRNAME
+Language=English
+%tDirName=%1!ws!%n%0
+.
+MessageId=+1 SymbolicName=IDS_DIRNAME2
+Language=English
+%tDirName2=%1!ws!%n%0
+.
+MessageId=+1 SymbolicName=IDS_DIRNAME3
+Language=English
+%tDirName3=%1!ws!%n%0
+.
+MessageId=+1 SymbolicName=IDS_DIRNAME4
+Language=English
+%tDirName4=%1!ws!%n%0
+.
+MessageId=+1 SymbolicName=IDS_FITFILE
+Language=English
+%tFitFile=%1!ws!%n%0
+.
+MessageId=+1 SymbolicName=IDS_FITPERSONAL
+Language=English
+%tFitPersonal=%1!ws!%n%0
+.
+MessageId=+1 SymbolicName=IDS_FITSHARED
+Language=English
+%tFitShared=%1!ws!%n%0
+.
+MessageId=+1 SymbolicName=IDS_PROFILEBADFLAGS
+Language=English
+%tBad Profile flags, Flags = 0x%1!x!%n%0
+.
+MessageId=+1 SymbolicName=IDS_PROFILECOMMENT
+Language=English
+%tProfileComment=%1!ws!%n%0
+.
+MessageId=+1 SymbolicName=IDS_PROFILENODISKTREE
+Language=English
+%tDisk tree for this profile may be absent.%n%0
+.
+MessageId=+1 SymbolicName=IDS_TCPIPADDRESS
+Language=English
+%tTcpIpAddress=0x%1!x!%n%0
+.
+MessageId=+1 SymbolicName=IDS_TCPIPGATEWAY
+Language=English
+%tTcpIpGateway=0x%1!x!%n%0
+.
+MessageId=+1 SymbolicName=IDS_TCPIPSUBNET
+Language=English
+%tTcpIpSubnet=0x%1!x!%n%0
+.
+MessageId=+1 SymbolicName=IDS_SERVICEBADFLAGS
+Language=English
+%tBad Service flags, Flags=0x%1!x!%n%0
+.
+MessageId=+1 SymbolicName=IDS_VENDORBADFLAGS
+Language=English
+%tBad Vendor flags, Flags = 0x%1!x!%n%0
+.
+MessageId=+1 SymbolicName=IDS_VENDORCOMMENT
+Language=English
+%tVendorComment=%1!ws!%n%0
+.
+MessageId=+1 SymbolicName=IDS_VENDORNAME
+Language=English
+%tVendorName=%1!ws!%n%0
+.
+MessageId=+1 SymbolicName=IDS_WINDOWSIZE
+Language=English
+%tWindowSize=0x%1!x!%n%0
+.
+MessageId=+1 SymbolicName=IDS_WKSTAADAPTER
+Language=English
+%tAdapterName=%1!ws!%n%0
+.
+MessageId=+1 SymbolicName=IDS_WKSTACOMMENT
+Language=English
+%tWkstaComment=%1!ws!%n%0
+.
+MessageId=+1 SymbolicName=IDS_WKSTAINPROFILE
+Language=English
+%tProfileName=%1!ws!%n%0
+.
+MessageId=+1 SymbolicName=IDS_WKSTALOGONBADFLAGS
+Language=English
+%tBad Remoteboot password flags, Flags = 0x%1!x!%n%0
+.
+MessageId=+1 SymbolicName=IDS_WKSTALOGONNONE
+Language=English
+%tRemoteBoot password is not solicited%n%0
+.
+MessageId=+1 SymbolicName=IDS_WKSTALOGONOPT
+Language=English
+%tRemoteBoot password is optional%n%0
+.
+MessageId=+1 SymbolicName=IDS_WKSTALOGONREQD
+Language=English
+%tRemoteBoot password is requred%n%0
+.
+MessageId=+1 SymbolicName=IDS_WKSTASHARINGBADFLAGS
+Language=English
+%tBad Remoteboot home directory flags, Flags = 0x%1!x!%n%0
+.
+MessageId=+1 SymbolicName=IDS_WKSTASHARINGFALSE
+Language=English
+%tUses private home directory%n%0
+.
+MessageId=+1 SymbolicName=IDS_WKSTASHARINGTRUE
+Language=English
+%tUses shared home directory%n%0
+.
+MessageId=+1 SymbolicName=IDS_WKSTA_DELETE_TRUE
+Language=English
+%tUser will be deleted when workstation is deleted by Remoteboot Manager%n%0
+.
+MessageId=+1 SymbolicName=IDS_WKSTA_DELETE_FALSE
+Language=English
+%tUser will be not deleted when workstation is deleted by Remoteboot Manager%n%0
+.
+MessageId=+1 SymbolicName=IDS_WKSTA_DELETE_BAD_FLAGS
+Language=English
+%tBad DELETE flags, Flags = 0x%1!x!%n%0
+.
+MessageId=+1 SymbolicName=IDS_WKSTA_DHCP_TRUE
+Language=English
+%tUses DHCP (when TCP/IP is enabled)%n%0
+.
+MessageId=+1 SymbolicName=IDS_WKSTA_DHCP_FALSE
+Language=English
+%tDoes not use DHCP (when TCP/IP is enabled)%n%0
+.
+MessageId=+1 SymbolicName=IDS_WKSTA_DHCP_BAD_FLAGS
+Language=English
+%tBad DHCP flags, Flags = 0x%1!x!%n%0
+.
+MessageId=+1 SymbolicName=IDS_PROFILENAME_NOTAB
+Language=English
+ProfileName=%1!ws!%n%0
+.
+MessageId=+1 SymbolicName=IDS_ADAPTERNAME_NOTAB
+Language=English
+AdapterName=%1!ws!%n%0
+.
+MessageId=+1 SymbolicName=IDS_BOOTNAME_NOTAB
+Language=English
+BootName=%1!ws!%n%0
+.
+MessageId=+1 SymbolicName=IDS_CONFIGNAME_NOTAB
+Language=English
+ConfigName=%1!ws!%n%0
+.
+MessageId=+1 SymbolicName=IDS_VENDORNAME_NOTAB
+Language=English
+VendorName=%1!ws!%n%0
+.
+MessageId=+1 SymbolicName=IDS_WKSTANAME_NOTAB
+Language=English
+WkstaName=%1!ws!%n%0
+.
+MessageId=+1 SymbolicName=IDS_RPLSVC_COMPUTER
+Language=English
+Managing remoteboot service at computer %1!ws!.%n%0
+.
+MessageId=+1 SymbolicName=IDS_RPLSVC_LOCAL_COMPUTER
+Language=English
+Managing remoteboot service at local computer.%n%0
+.
+
+ \ No newline at end of file
diff --git a/private/net/svcdlls/rpl/command/rplver.rc b/private/net/svcdlls/rpl/command/rplver.rc
new file mode 100644
index 000000000..52c98ef82
--- /dev/null
+++ b/private/net/svcdlls/rpl/command/rplver.rc
@@ -0,0 +1,12 @@
+#include <windows.h>
+
+#include <ntverp.h>
+
+#define VER_FILETYPE VFT_APP
+#define VER_FILESUBTYPE VFT2_UNKNOWN
+#define VER_FILEDESCRIPTION_STR "RemoteBoot Command-line Utility"
+#define VER_INTERNALNAME_STR "RPLCMD.EXE"
+#define VER_ORIGINALFILENAME_STR "RPLCMD.EXE"
+
+#include "common.ver"
+#include "rplmsg.rc"
diff --git a/private/net/svcdlls/rpl/command/sources b/private/net/svcdlls/rpl/command/sources
new file mode 100644
index 000000000..c546b3668
--- /dev/null
+++ b/private/net/svcdlls/rpl/command/sources
@@ -0,0 +1,65 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 08 - April - 1994
+
+
+Revision History:
+
+!ENDIF
+
+MAJORCOMP = net
+MINORCOMP = rpl
+TARGETNAME= rplcmd
+
+
+
+TARGETPATH=obj
+TARGETTYPE=PROGRAM
+TARGETLIBS= \
+ ..\lib\obj\*\rpllib.lib \
+
+INCLUDES=.;..\inc;..\..\..\inc;..\..\..\..\inc
+
+!IFNDEF DISABLE_NET_UNICODE
+UNICODE=1
+NET_C_DEFINES=-DUNICODE
+!ENDIF
+
+MSC_WARNING_LEVEL=/W3 /WX
+
+SOURCES= \
+ rplcmd.c \
+ rplver.rc
+
+
+UMTYPE= console
+UMLIBS= \
+ $(BASEDIR)\Public\Sdk\Lib\*\netlib.lib \
+ $(BASEDIR)\public\sdk\lib\*\netapi32.lib \
+ $(BASEDIR)\Public\Sdk\Lib\*\shell32.lib \
+ $(BASEDIR)\public\sdk\lib\*\user32.lib
+
+C_DEFINES=-DRPC_NO_WINDOWS_H
+
+
+NTTARGETFILE0= \
+ rplmsg.h \
+ rplmsg.mc
+
+
diff --git a/private/net/svcdlls/rpl/convert/adapter.c b/private/net/svcdlls/rpl/convert/adapter.c
new file mode 100644
index 000000000..738dbb67c
--- /dev/null
+++ b/private/net/svcdlls/rpl/convert/adapter.c
@@ -0,0 +1,71 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ adapter.c
+
+Abstract:
+
+ Creates adapter table to be used with NT rpl service.
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 19 - November - 1993
+
+Revision History:
+
+--*/
+
+#include "local.h"
+#define RPLADAPTER_ALLOCATE
+#include "adapter.h"
+#undef RPLADAPTER_ALLOCATE
+
+
+DWORD AdapterCreateTable( VOID)
+{
+ JET_COLUMNDEF ColumnDef;
+ JET_ERR JetError;
+ DWORD index;
+ DWORD Offset;
+ CHAR IndexKey[ 255];
+
+ JetError = JetCreateTable( SesId, DbId, ADAPTER_TABLE_NAME,
+ ADAPTER_TABLE_PAGE_COUNT, ADAPTER_TABLE_DENSITY, &AdapterTableId);
+
+ //
+ // Create columns. First initalize fields that do not change between
+ // addition of columns.
+ //
+ ColumnDef.cbStruct = sizeof(ColumnDef);
+ ColumnDef.columnid = 0;
+ ColumnDef.wCountry = 1;
+ ColumnDef.langid = 0x0409; // USA english
+ ColumnDef.cp = 1200; // UNICODE codepage
+ ColumnDef.wCollate = 0;
+ ColumnDef.cbMax = 0;
+ ColumnDef.grbit = 0; // variable length binary and text data.
+
+ for ( index = 0; index < ADAPTER_TABLE_LENGTH; index++) {
+
+ ColumnDef.coltyp = AdapterTable[ index].ColumnType;
+
+ CallM( JetAddColumn( SesId, AdapterTableId,
+ AdapterTable[ index].ColumnName, &ColumnDef,
+ NULL, 0, &AdapterTable[ index].ColumnId));
+ }
+
+ Offset = AddKey( IndexKey, '+', AdapterTable[ ADAPTER_AdapterName].ColumnName);
+ IndexKey[ Offset++] = '\0';
+ JetError = JetCreateIndex( SesId, AdapterTableId, ADAPTER_INDEX_AdapterName,
+ JET_bitIndexPrimary, IndexKey, Offset, 50);
+ if ( JetError != JET_errSuccess) {
+ RplAssert( TRUE, ("CreateIndex failed err=%d", JetError));
+ return( MapJetError( JetError));
+ }
+
+ return( ERROR_SUCCESS);
+}
+
diff --git a/private/net/svcdlls/rpl/convert/boot.c b/private/net/svcdlls/rpl/convert/boot.c
new file mode 100644
index 000000000..e0dacb4ec
--- /dev/null
+++ b/private/net/svcdlls/rpl/convert/boot.c
@@ -0,0 +1,306 @@
+/*++
+
+Copyright (c) 1987-1993 Microsoft Corporation
+
+Module Name:
+
+ boot.c
+
+Abstract:
+
+ Creates boot block table (server record table). Parses old style
+ RPL.MAP server records and creates corresponding entries in jet
+ database table.
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 19 - November - 1993
+
+Revision History:
+
+--*/
+
+#include "local.h"
+#define RPLBOOT_ALLOCATE
+#include "boot.h"
+#undef RPLBOOT_ALLOCATE
+
+
+DWORD BootCreateTable( VOID)
+{
+ JET_COLUMNDEF ColumnDef;
+ JET_ERR JetError;
+ DWORD index;
+ DWORD Offset;
+ CHAR IndexKey[ 255];
+
+ JetError = JetCreateTable( SesId, DbId, BOOT_TABLE_NAME,
+ BOOT_TABLE_PAGE_COUNT, BOOT_TABLE_DENSITY, &BootTableId);
+ if ( JetError != JET_errSuccess) {
+ RplAssert( TRUE, ("CreateTable failed err=%d", JetError));
+ return( MapJetError( JetError));
+ }
+
+ //
+ // Create columns. First initalize fields that do not change between
+ // addition of columns.
+ //
+ ColumnDef.cbStruct = sizeof(ColumnDef);
+ ColumnDef.columnid = 0;
+ ColumnDef.wCountry = 1;
+ ColumnDef.langid = 0x0409; // USA english
+ ColumnDef.cp = 1200; // UNICODE codepage
+ ColumnDef.wCollate = 0;
+ ColumnDef.cbMax = 0;
+ ColumnDef.grbit = 0; // variable length binary and text data.
+
+ for ( index = 0; index < BOOT_TABLE_LENGTH; index++ ) {
+
+ ColumnDef.coltyp = BootTable[ index].ColumnType;
+
+ JetError = JetAddColumn( SesId, BootTableId,
+ BootTable[ index].ColumnName, &ColumnDef,
+ NULL, 0, &BootTable[ index].ColumnId);
+ if ( JetError != JET_errSuccess) {
+ RplAssert( TRUE, ("AddColumn( %s) failed err=%d", BootTable[ index].ColumnName, JetError));
+ return( MapJetError( JetError));
+ }
+ }
+
+ //
+ // Boot names of DOS boot blocks may be shared by several adapter id
+ // classes (vendor codes). Therefore, server name (== boot name)
+ // cannot be a unique index nor a primary index. But you can construct
+ // a primary index from VendorId & BootName.
+ // +VendorId+BootName index is used to enumerate all boot names for a
+ // given vendor id - which is then used to enumerate all profiles & all
+ // configs compatible with a given vendor id.
+ //
+ Offset = AddKey( IndexKey, '+', BootTable[ BOOT_VendorId].ColumnName);
+ Offset += AddKey( IndexKey + Offset, '+', BootTable[ BOOT_BootName].ColumnName);
+ IndexKey[ Offset++] = '\0';
+ JetError = JetCreateIndex( SesId, BootTableId, BOOT_INDEX_VendorIdBootName,
+ JET_bitIndexPrimary, IndexKey, Offset, 50);
+ if ( JetError != JET_errSuccess) {
+ RplAssert( TRUE, ("CreateIndex failed err=%d", JetError));
+ return( MapJetError( JetError));
+ }
+
+ Offset = AddKey( IndexKey, '+', BootTable[ BOOT_BootName].ColumnName);
+ IndexKey[ Offset++] = '\0';
+ JetError = JetCreateIndex( SesId, BootTableId, BOOT_INDEX_BootName,
+ 0, IndexKey, Offset, 50);
+ if ( JetError != JET_errSuccess) {
+ RplAssert( TRUE, ("CreateIndex failed err=%d", JetError));
+ return( MapJetError( JetError));
+ }
+
+ //
+ // Make boot name a current index - used to validate configs.
+ //
+ JetError = JetSetCurrentIndex( SesId, BootTableId, BOOT_INDEX_BootName);
+ if ( JetError != JET_errSuccess) {
+ RplAssert( TRUE, ("SetCurrentIndex failed err=%d", JetError));
+ return( MapJetError( JetError));
+ }
+
+ return( ERROR_SUCCESS);
+}
+
+
+VOID ProcessBoot( PWCHAR * Fields)
+{
+//
+// Boot block record (server record) defines.
+//
+#define BOOT_BBCFILE_INDEX 1 // boot block configuration file
+#define BOOT_RETRYCOUNT_INDEX 2 // retry count (used with default boot)
+#define BOOT_TIMEOUT_INDEX 3 // retry period (used with default boot)
+#define BOOT_ACKSTATUS_INDEX 4 // used with acknowledgments
+#define BOOT_COMMENT_INDEX 6 // boot block comment
+#define BOOT_VENDOR_INDEX 7 // common adapter id digits
+#define BOOT_BOOTNAME_INDEX 13 // boot block identifier
+
+#define BOOT_DEFAULT_RETRYCOUNT 3
+#define BOOT_DEFAULT_TIMEOUT 10
+#define USE_ACK_CHAR L'A'
+#define FINAL_ACK_ONLY_CHAR L'F'
+#define PARTIAL_ACK_CHAR L'P'
+#define NO_ACK_CHAR L'N'
+#define ADAPT_WIN_CHAR L'W'
+
+ PWCHAR VendorName; // common adapter id digits
+ DWORD VendorId; // common adapter id digits
+ PWCHAR BootName; // id of boot block record
+ PWCHAR BootComment;
+ PWCHAR BbcFile; // boot block configuration file relative path
+#ifdef RPL_OBSOLETE
+ DWORD RetryCount;
+ DWORD RetryPeriod;
+#endif
+ DWORD WindowSize; // used with acknowledgments
+ //
+ // SendFileRequest is of type JET_coltypBit, which is of UCHAR size.
+ // Thus it is declared as BOOLEAN == UCHAR, instead of BOOL == int.
+ //
+ BOOLEAN FinalAck; // used with acknowledgments
+ DWORD Flags;
+
+#ifdef RPL_OBSOLETE
+ //
+ // Initialize retry count & retry period (else use defaults).
+ //
+ if ( iswdigit( *Fields[ BOOT_TIMEOUT_INDEX]) &&
+ iswdigit( *Fields[ BOOT_RETRYCOUNT_INDEX])) {
+ RetryPeriod = wcstoul( Fields[ BOOT_TIMEOUT_INDEX], NULL, 0);
+ RetryCount = wcstoul( Fields[ BOOT_RETRYCOUNT_INDEX], NULL, 0);
+ } else {
+ RetryPeriod = BOOT_DEFAULT_TIMEOUT;
+ RetryCount = BOOT_DEFAULT_RETRYCOUNT;
+ }
+#endif
+
+ switch( toupper( *Fields[ BOOT_ACKSTATUS_INDEX])) {
+ case FINAL_ACK_ONLY_CHAR:
+ WindowSize = MAXWORD; // don't ack any (non-final) packet
+ FinalAck = TRUE; // ack the final packet
+ break;
+ case PARTIAL_ACK_CHAR:
+ WindowSize = 0; // ack every (non-final) packet
+ FinalAck = FALSE; // don't ack the final packet
+ break;
+ case ADAPT_WIN_CHAR:
+ if ( Fields[ BOOT_ACKSTATUS_INDEX][ 1] != EQUALS_CHAR) {
+ WindowSize = 0; // the default is to ack every (non-final) packet
+ } else {
+ WindowSize = wcstoul( Fields[BOOT_ACKSTATUS_INDEX] + 2, NULL, 0);
+ //
+ // Algorithm adds an extra packet to window, decrement counter
+ // by one. (Thus window size of 1 is same as USE_ACK_CHAR.)
+ //
+ if ( WindowSize) { // avoid underflow
+ WindowSize--;
+ }
+ } // ack every WindowSize-th (non-final) packet
+ FinalAck = TRUE; // ack the final packet
+ break;
+ case NO_ACK_CHAR:
+ case 0:
+ WindowSize = MAXWORD; // don't ack any (non-final) packet
+ FinalAck = FALSE; // don't ack the final packet
+ break;
+ default:
+ DbgUserBreakPoint();
+ NOTHING; // fall through to most common case
+ case USE_ACK_CHAR:
+ WindowSize = 0; // ack every (non-final) packet
+ FinalAck = TRUE; // ack the final packet
+ break;
+ }
+ WindowSize = 0; // BUGBUG temporary workaround for RPLSVC bug
+
+ if ( FinalAck == TRUE) {
+ Flags = BOOT_FLAGS_FINAL_ACKNOWLEDGMENT_TRUE;
+ } else {
+ Flags = BOOT_FLAGS_FINAL_ACKNOWLEDGMENT_FALSE;
+ }
+
+ BootComment = Fields[ BOOT_COMMENT_INDEX];
+ if ( RPL_STRING_TOO_LONG( BootComment)) {
+ BootComment[ RPL_MAX_STRING_LENGTH] = 0; // silently truncate it
+ }
+
+
+ //
+ // Note that server identifier has leading 'R' (enabled) or 'D'
+ // (disabled) in wksta record but it always has leading 'R' in server
+ // record. We do not want to save leading 'R' in server record.
+ //
+ BootName = Fields[ BOOT_BOOTNAME_INDEX] + 1; // add 1 to skip leading 'R'
+ if ( !ValidName( BootName, RPL_MAX_BOOT_NAME_LENGTH, TRUE)) {
+ RplPrintf1( RPLI_CVT_BootNameTooLong, BootName);
+ return;
+ }
+
+ BbcFile = Fields[ BOOT_BBCFILE_INDEX];
+ if ( !ValidName( BbcFile, RPL_MAX_STRING_LENGTH, TRUE)) {
+ RplPrintf2( RPLI_CVT_BbcFileTooLong, BootName, BbcFile);
+ return;
+ }
+
+ //
+ // LM2.2 introduced multiple vendor codes in the vendor field of a
+ // boot block record. For each of the vendor codes we create a
+ // separate jet record.
+ //
+ // Store common adapter id digits both as a hex string (VendorName)
+ // and as a dword (VendorId).
+ //
+ for ( VendorName = wcstok( Fields[ BOOT_VENDOR_INDEX], L"|");
+ VendorName != NULL; VendorName = wcstok( NULL, L"|")) {
+
+ if ( !ValidHexName( VendorName, RPL_VENDOR_NAME_LENGTH, TRUE)) {
+ RplPrintf2( RPLI_CVT_BadVendorName, BootName, VendorName);
+ continue;
+ }
+
+ VendorId = wcstoul( VendorName, NULL, 16); // since VendorName must be hex
+
+ Call( JetPrepareUpdate( SesId, BootTableId, JET_prepInsert));
+
+ Call( JetSetColumn( SesId, BootTableId,
+ BootTable[ BOOT_BootName].ColumnId,
+ BootName, (wcslen( BootName) + 1) * sizeof(WCHAR), 0, NULL));
+
+ Call( JetSetColumn( SesId, BootTableId,
+ BootTable[ BOOT_BootComment].ColumnId,
+ BootComment, (wcslen( BootComment) + 1) * sizeof(WCHAR), 0, NULL));
+
+ Call( JetSetColumn( SesId, BootTableId,
+ BootTable[ BOOT_Flags].ColumnId,
+ &Flags, sizeof( Flags), 0, NULL));
+
+ Call( JetSetColumn( SesId, BootTableId,
+ BootTable[ BOOT_VendorName].ColumnId,
+ VendorName, (wcslen( VendorName) + 1) * sizeof(WCHAR), 0, NULL));
+
+ Call( JetSetColumn( SesId, BootTableId,
+ BootTable[ BOOT_BbcFile].ColumnId,
+ BbcFile, (wcslen( BbcFile) + 1) * sizeof(WCHAR), 0, NULL));
+
+ Call( JetSetColumn( SesId, BootTableId,
+ BootTable[ BOOT_WindowSize].ColumnId,
+ &WindowSize, sizeof( WindowSize), 0, NULL));
+
+ Call( JetSetColumn( SesId, BootTableId,
+ BootTable[ BOOT_VendorId].ColumnId,
+ &VendorId, sizeof( VendorId), 0, NULL));
+
+#ifdef RPL_OBSOLETE
+ Call( JetSetColumn( SesId, BootTableId,
+ BootTable[ BOOT_RetryCount].ColumnId,
+ &RetryCount, sizeof( RetryCount), 0, NULL));
+
+ Call( JetSetColumn( SesId, BootTableId,
+ BootTable[ BOOT_RetryPeriod].ColumnId,
+ &RetryPeriod, sizeof( RetryPeriod), 0, NULL));
+#endif
+
+ Call( JetUpdate( SesId, BootTableId, NULL, 0, NULL));
+ }
+}
+
+
+BOOL FindBoot( IN PWCHAR BootName)
+{
+ return( Find( BootTableId, BootName));
+}
+
+
+VOID BootListTable( VOID)
+{
+ ListTable( BOOT_TABLE_NAME, BootTable[ BOOT_BootName].ColumnName,
+ BOOT_INDEX_BootName);
+}
+
+
diff --git a/private/net/svcdlls/rpl/convert/config.c b/private/net/svcdlls/rpl/convert/config.c
new file mode 100644
index 000000000..99215c4f5
--- /dev/null
+++ b/private/net/svcdlls/rpl/convert/config.c
@@ -0,0 +1,527 @@
+/*++
+
+Copyright (c) 1987-1993 Microsoft Corporation
+
+Module Name:
+
+ config.c
+
+Abstract:
+
+ Creates config table. Parses old style RPLMGR.INI config records
+ and creates corresponding entries in jet database table.
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 19 - November - 1993
+
+Revision History:
+
+--*/
+
+#include "local.h"
+#define RPLCONFIG_ALLOCATE
+#include "config.h"
+#undef RPLCONFIG_ALLOCATE
+
+PWCHAR RplmgrIniFile = L"rplmgr.ini";
+
+#define MAX_RPLMGR_INI_FILE_SIZE 0xFFFF // BUGBUG some arbitrary value
+
+#define NO_SCRIPT_FIELDS 13
+
+#define SECTION_STARTMARK L'['
+#define CONFIG_BEGIN L"[configuration]"
+
+#define CARRIAGE_RETURN_CHAR L'\r' // 0xD or \015
+
+// #define RPL_DEBUG_ALL
+
+
+BOOL GetConfigFieldValue(
+ IN PWCHAR Cursor,
+ IN DWORD Index
+ )
+/*++
+ Fields with multiple field values, such as AdapterName, are
+ not properly parsed here. Howerever, we do not care about
+ the values of these fields.
+--*/
+{
+ PWCHAR End;
+ DWORD Length;
+
+ //
+ // By default the field is empty.
+ //
+ ConfigParseTable[ Index].Value = NULL;
+ ConfigParseTable[ Index].Size = 0;
+
+ //
+ // Read to EQUALS_CHAR
+ //
+ while ( *Cursor != EQUALS_CHAR && *Cursor != COMMENT_CHAR &&
+ *Cursor != CARRIAGE_RETURN_CHAR) {
+ Cursor++;
+ }
+
+ if ( *Cursor == COMMENT_CHAR || *Cursor == CARRIAGE_RETURN_CHAR) {
+ return( TRUE);
+ }
+
+ Cursor++; // Skip EQUALS_CHAR
+
+ //
+ // Read to the beginning of field value.
+ //
+ while( *Cursor != CARRIAGE_RETURN_CHAR && iswspace(*Cursor) &&
+ *Cursor != COMMENT_CHAR) {
+ Cursor++;
+ }
+
+ if ( *Cursor == COMMENT_CHAR || *Cursor == CARRIAGE_RETURN_CHAR) {
+ return( TRUE);
+ }
+
+ //
+ // Make sure that boot block reference is enabled.
+ //
+ if ( Index == CONFIG_BootName) {
+ if ( *Cursor != L'R') {
+ RplAssert( TRUE, ("Bad boot block id: %.40ws", Cursor));
+ return( FALSE);
+ }
+ Cursor++; // skip leading 'R' in boot block name
+ }
+
+ //
+ // Find the end point of this field. Since comments may contain spaces,
+ // space is not used as a separator for CONFIG_ConfigComment field.
+ //
+
+ End = wcspbrk( Cursor, Index == CONFIG_ConfigComment ? L"\f\n\r\t\v" : L" \f\n\r\t\v");
+ if ( End != NULL) {
+ Length = End - Cursor;
+ } else {
+ Length = wcslen( Cursor);
+ }
+
+ ConfigParseTable[ Index].Value = GlobalAlloc( GMEM_FIXED, (Length+1)*sizeof(WCHAR));
+ if ( ConfigParseTable[ Index].Value == NULL) {
+ DWORD Error = GetLastError();
+ RplAssert( TRUE, ("GlobalAlloc: Error = %d", Error));
+ return( FALSE);
+ }
+ ConfigParseTable[ Index].Size = (Length+1)*sizeof(WCHAR);
+ memcpy( ConfigParseTable[ Index].Value, Cursor, Length * sizeof(WCHAR));
+ ((PWCHAR)ConfigParseTable[ Index].Value)[ Length] = 0;
+ return( TRUE);
+}
+
+
+BOOL StringISpaceEqual( IN PWCHAR str1, IN PWCHAR str2 )
+/*++
+
+Routine Description:
+
+ Case insensitive version of StringsSpaceEqual.
+
+Arguments:
+
+ str1 - first string
+ str2 - second string
+
+Return Value:
+
+ TRUE if strings are equal, FALSE otherwise.
+
+--*/
+{
+ WCHAR ch1;
+
+#ifdef NOT_YET
+ if ( str1 == NULL || str2 == NULL) {
+ str1 = (str1 != NULL) ? str1 : str2;
+ if ( str2 == NULL) {
+ return( TRUE); // both strings are NULL
+ }
+ while ( iswspace( *str1++)) {
+ NOTHING; // check if not NULL string contains spaces only
+ }
+ return( *str1 == 0);
+ }
+#endif // NOT_YET
+
+ //
+ // Compare strings until the first space or NULL character
+ // (in the first string) or until we find the first different character.
+ //
+ while ( (ch1 = toupper(*str1)) && !iswspace(ch1) && ch1 == toupper(*str2)) {
+ str1++, str2++;
+ }
+
+ //
+ // For strings to be equal, both characters must be NULL or space.
+ //
+ if ( (!ch1 || iswspace(ch1)) && (!(ch1 = *str2) || iswspace(ch1)))
+ {
+ return( TRUE);
+ }
+
+ return( FALSE);
+}
+
+
+#ifdef RPL_DEBUG_ALL
+VOID ProcessConfigDisplay( VOID)
+{
+ DWORD Index;
+
+ printf( "\tCONFIGURATION\n");
+ for ( Index = 0; Index < CONFIG_TABLE_LENGTH; Index++) {
+ if ( ConfigParseTable[ Index].Name == NULL) {
+ continue;
+ }
+ printf( "%ws %ws 0x%x\n", ConfigParseTable[ Index].Name,
+ ConfigParseTable[ Index].Value, ConfigParseTable[ Index].Size);
+ }
+}
+#endif
+
+
+PWCHAR ProcessConfig( IN PWCHAR Cursor)
+/*++
+
+Routine Description:
+
+ We read all the fields in the Config[] array, then use jet apis
+ to store this config in the database.
+
+Arguments:
+
+ Cursor - at entry this points to the beginning of the config section
+
+Return Value:
+
+ Cursor value after we finished processing the current config section.
+
+--*/
+{
+#define INVALID_ERROR_PARAMETER ((DWORD)(-1))
+ DWORD Index;
+ JET_ERR JetError;
+ DWORD Flags;
+ DWORD Offset;
+ DWORD ErrorParameter;
+
+ for ( Index = 0; Index < CONFIG_TABLE_LENGTH; Index++) {
+ if ( ConfigParseTable[ Index].Value != NULL) {
+ GlobalFree( ConfigParseTable[ Index].Value);
+ }
+ ConfigParseTable[ Index].Value = NULL;
+ }
+ if ( OffsetAfterComment( Cursor) == 0) {
+ Flags = CONFIG_FLAGS_ENABLED_TRUE;
+ } else {
+ Flags = CONFIG_FLAGS_ENABLED_FALSE;
+ }
+
+ for ( Cursor=GetNextLine(Cursor), ErrorParameter = INVALID_ERROR_PARAMETER;
+ *Cursor!=0; Cursor=GetNextLine(Cursor)) {
+
+ Offset = OffsetAfterComment( Cursor);
+ if ( *(Cursor+Offset) == SECTION_STARTMARK) {
+ break; // start of a new section, write the current section
+ }
+ Cursor += Offset;
+
+ //
+ // Find value for this name.
+ //
+ for ( Index = 0; Index < CONFIG_TABLE_LENGTH; Index++) {
+ if ( ConfigParseTable[ Index].Name == NULL) {
+ continue; // skip this one, not a RPLMGR.INI keyword
+ }
+ if ( !StringISpaceEqual( Cursor, ConfigParseTable[ Index].Name)) {
+ continue; // no match with this keyword
+ }
+ if ( !GetConfigFieldValue( Cursor, Index)) {
+ if ( ErrorParameter == INVALID_ERROR_PARAMETER) {
+ ErrorParameter = Index;
+ }
+ } else if ( Index == CONFIG_FitShared || Index == CONFIG_FitPersonal) {
+ PWCHAR Value;
+ Value = AddFileExtension( ConfigParseTable[ Index].Value,
+ L".FIT", TRUE);
+ if ( Value == NULL) {
+ if ( ErrorParameter == INVALID_ERROR_PARAMETER) {
+ ErrorParameter = Index;
+ }
+ } else if ( Value != ConfigParseTable[ Index].Value) {
+ GlobalFree( ConfigParseTable[ Index].Value);
+ ConfigParseTable[ Index].Value = Value;
+ ConfigParseTable[ Index].Size = (wcslen(Value)+1)*sizeof(WCHAR);
+ }
+ }
+ break;
+ }
+ }
+#ifdef RPL_DEBUG_ALL
+ ProcessConfigDisplay();
+#endif
+
+ if ( ConfigParseTable[ CONFIG_ConfigName].Value == NULL) {
+ RplAssert( TRUE, ("Bad config"));
+ return( Cursor);
+ }
+
+ //
+ // Perform same checks as in NetrRplConfigAdd.
+ //
+ if ( ErrorParameter != INVALID_ERROR_PARAMETER) {
+ NOTHING; // do not overwrite the first error
+ } else if ( !ValidName( ConfigParseTable[ CONFIG_FitPersonal].Value, RPL_MAX_STRING_LENGTH, TRUE)) {
+ ErrorParameter = CONFIG_FitPersonal;
+ } else if ( !ValidName( ConfigParseTable[ CONFIG_FitShared].Value, RPL_MAX_STRING_LENGTH, TRUE)) {
+ ErrorParameter = CONFIG_FitShared;
+ } else if ( !ValidName( ConfigParseTable[ CONFIG_DirName4].Value, RPL_MAX_STRING_LENGTH, FALSE)) {
+ ErrorParameter = CONFIG_DirName4;
+ } else if ( !ValidName( ConfigParseTable[ CONFIG_DirName3].Value, RPL_MAX_STRING_LENGTH, FALSE)) {
+ ErrorParameter = CONFIG_DirName3;
+ } else if ( !ValidName( ConfigParseTable[ CONFIG_DirName2].Value, RPL_MAX_STRING_LENGTH, TRUE)) {
+ ErrorParameter = CONFIG_DirName2;
+ } else if ( !ValidName( ConfigParseTable[ CONFIG_DirName].Value, RPL_MAX_STRING_LENGTH, TRUE)) {
+ ErrorParameter = CONFIG_DirName;
+ } else if ( !ValidName( ConfigParseTable[ CONFIG_BootName].Value, RPL_MAX_BOOT_NAME_LENGTH, TRUE)) {
+ ErrorParameter = CONFIG_BootName;
+ } else if ( !ValidName( ConfigParseTable[ CONFIG_ConfigName].Value, RPL_MAX_CONFIG_NAME_LENGTH, TRUE)) {
+ ErrorParameter = CONFIG_ConfigName;
+ }
+
+ if ( ErrorParameter < CONFIG_TABLE_LENGTH) {
+ RplPrintf2( RPLI_CVT_ConfigInvalid, ConfigParseTable[ CONFIG_ConfigName].Value, ConfigParseTable[ ErrorParameter].Name);
+ return( Cursor);
+ }
+
+ if ( ConfigParseTable[ CONFIG_FitPersonal].Value == NULL) {
+ //
+ // Ignore default boot configurations.
+ //
+ RplPrintf1( RPLI_CVT_ConfigNoPersonalFIT, ConfigParseTable[ CONFIG_ConfigName].Value);
+ return( Cursor);
+ }
+
+ if ( !_wcsicmp( L"OS2", ConfigParseTable[ CONFIG_DirName].Value )) {
+ //
+ // Ignore OS/2 configurations.
+ //
+ RplPrintf1( RPLI_CVT_ConfigNoOS2, ConfigParseTable[ CONFIG_ConfigName].Value);
+ return( Cursor);
+ }
+
+ if ( RPL_STRING_TOO_LONG( ConfigParseTable[ CONFIG_ConfigComment].Value)) {
+ //
+ // Silently truncate comments that are too long.
+ //
+ ((PWCHAR)ConfigParseTable[ CONFIG_ConfigComment].Value)[ RPL_MAX_STRING_LENGTH] = 0;
+ ConfigParseTable[ CONFIG_ConfigComment].Size = (RPL_MAX_STRING_LENGTH+1)*sizeof(WCHAR);
+ }
+
+ _wcsupr( (PWCHAR)ConfigParseTable[ CONFIG_BootName].Value);
+ _wcsupr( (PWCHAR)ConfigParseTable[ CONFIG_ConfigName].Value);
+
+ Call( JetPrepareUpdate( SesId, ConfigTableId, JET_prepInsert));
+
+
+ for ( Index = 0; Index < CONFIG_TABLE_LENGTH; Index++ ) {
+ if ( ConfigParseTable[ Index].Name == NULL) {
+ continue;
+ }
+ JetError = JetSetColumn( SesId, ConfigTableId,
+ ConfigTable[ Index].ColumnId, ConfigParseTable[ Index].Value,
+ ConfigParseTable[ Index].Size, 0, NULL);
+ if ( JetError != JET_errSuccess) {
+ RplAssert( TRUE, ("Index=%d(dec) JetError=%d", Index, JetError));
+ }
+ }
+ Call( JetSetColumn( SesId, ConfigTableId,
+ ConfigTable[ CONFIG_Flags].ColumnId, &Flags, sizeof(Flags), 0, NULL));
+
+ Call( JetUpdate( SesId, ConfigTableId, NULL, 0, NULL));
+ return( Cursor);
+}
+
+
+
+DWORD ProcessRplmgrIni( IN LPWSTR FilePath)
+{
+ PWCHAR Cursor;
+ PWCHAR String;
+ JET_COLUMNDEF ColumnDef;
+ JET_ERR JetError;
+ DWORD Index;
+ DWORD Offset;
+ CHAR IndexKey[ 255];
+
+ JetError = JetCreateTable( SesId, DbId, "config", CONFIG_TABLE_PAGE_COUNT,
+ CONFIG_TABLE_DENSITY, &ConfigTableId);
+
+ ColumnDef.cbStruct = sizeof( ColumnDef);
+ ColumnDef.columnid = 0;
+ ColumnDef.coltyp = JET_coltypLong;
+ ColumnDef.cbMax = 0;
+ ColumnDef.grbit = 0;
+
+ //
+ // Create columns. First initialize fields that do not change between
+ // addition of columns.
+ //
+ ColumnDef.cbStruct = sizeof(ColumnDef);
+ ColumnDef.columnid = 0;
+ ColumnDef.wCountry = 1;
+ ColumnDef.langid = 0x0409; // USA english
+ ColumnDef.cp = 1200; // UNICODE codepage
+ ColumnDef.wCollate = 0;
+ ColumnDef.cbMax = 0;
+ ColumnDef.grbit = 0; // variable length binary and text data.
+
+ for ( Index = 0; Index < CONFIG_TABLE_LENGTH; Index++ ) {
+
+ ColumnDef.coltyp = ConfigTable[ Index].ColumnType;
+ JetError = JetAddColumn( SesId, ConfigTableId,
+ ConfigTable[ Index].ColumnName, &ColumnDef,
+ NULL, 0, &ConfigTable[ Index].ColumnId);
+ if( JetError != ERROR_SUCCESS ) {
+ return( MapJetError( JetError));
+ }
+ }
+
+ Offset = AddKey( IndexKey, '+', ConfigTable[ CONFIG_ConfigName].ColumnName);
+ IndexKey[ Offset++] = '\0';
+ JetError = JetCreateIndex( SesId, ConfigTableId, CONFIG_INDEX_ConfigName,
+ JET_bitIndexPrimary, IndexKey, Offset, 50);
+ if ( JetError != JET_errSuccess) {
+ RplAssert( TRUE, ("CreateIndex failed err=%d", JetError));
+ return( MapJetError( JetError));
+ }
+
+ //
+ // +BootName+ConfigName index is used to enumerate all configurations for
+ // a given VendorId.
+ //
+ Offset = AddKey( IndexKey, '+', ConfigTable[ CONFIG_BootName].ColumnName);
+ Offset += AddKey( IndexKey + Offset, '+', ConfigTable[ CONFIG_ConfigName].ColumnName);
+ IndexKey[ Offset++] = '\0';
+ JetError = JetCreateIndex( SesId, ConfigTableId, CONFIG_INDEX_BootNameConfigName,
+ JET_bitIndexUnique, IndexKey, Offset, 50);
+ if ( JetError != JET_errSuccess) {
+ RplAssert( TRUE, ("CreateIndex failed err=%d", JetError));
+ return( MapJetError( JetError));
+ }
+
+ //
+ // Make config name a current index - used to validate profiles.
+ //
+ JetError = JetSetCurrentIndex( SesId, ConfigTableId, CONFIG_INDEX_ConfigName);
+ if ( JetError != JET_errSuccess) {
+ RplAssert( TRUE, ("SetCurrentIndex failed err=%d", JetError));
+ return( MapJetError( JetError));
+ }
+
+ String = ReadTextFile( FilePath, RplmgrIniFile, MAX_RPLMGR_INI_FILE_SIZE);
+ if ( String == NULL) {
+ return( GetLastError());
+ }
+
+ //
+ // If current line is the beginning of configuration, we process
+ // that configuration (note that this processing has the side effect of
+ // advancing the cursor), else we just advance the cursor.
+ //
+ for ( Cursor = GetFirstLine( String); *Cursor != 0; NOTHING) {
+ Offset = OffsetAfterComment( Cursor);
+ if ( StringISpaceEqual( Cursor+Offset, CONFIG_BEGIN)) {
+ Cursor = ProcessConfig( Cursor);
+ } else {
+ Cursor = GetNextLine( Cursor);
+
+ }
+ }
+
+ if ( GlobalFree( String) != NULL) {
+ RplAssert( TRUE, ("GlobalFree: Error=%d", GetLastError()));
+ }
+
+ return( ERROR_SUCCESS);
+}
+
+
+VOID ConfigPruneTable( VOID)
+/*++
+ Eliminate config records that do not have a corresponding
+ boot block record defined.
+--*/
+{
+ WCHAR Name[ 20];
+ DWORD NameSize;
+ JET_ERR ForJetError;
+ JET_ERR JetError;
+
+ for ( ForJetError = JetMove( SesId, ConfigTableId, JET_MoveFirst, 0);
+ ForJetError == JET_errSuccess;
+ ForJetError = JetMove( SesId, ConfigTableId, JET_MoveNext, 0)) {
+
+ JetError = JetRetrieveColumn( SesId, ConfigTableId,
+ ConfigTable[ CONFIG_BootName].ColumnId, Name,
+ sizeof( Name), &NameSize, 0, NULL);
+ if ( JetError != JET_errSuccess) {
+ RplAssert( TRUE, ("RetriveColumn failed err=%d", JetError));
+ Call( JetDelete( SesId, ConfigTableId));
+ continue;
+ }
+ if ( !FindBoot( Name)) {
+#ifdef RPL_DEBUG_ALL
+ WCHAR Value[ 20];
+ JetRetrieveColumn( SesId, ConfigTableId,
+ ConfigTable[ CONFIG_ConfigName].ColumnId, Value,
+ sizeof( Value), &NameSize, 0, NULL);
+ printf("Deleting config %ws since boot %ws was not found\n",
+ Value, Name);
+#endif
+ Call( JetDelete( SesId, ConfigTableId));
+ continue;
+ }
+ }
+}
+
+
+BOOL FindConfig( IN PWCHAR Name)
+{
+ JET_ERR JetError;
+
+ JetError = JetMove( SesId, ConfigTableId, JET_MoveFirst, 0);
+ if ( JetError != JET_errSuccess) {
+ RplAssert( TRUE, ("JetMove failed err=%d", JetError));
+ return( FALSE);
+ }
+ JetError = JetMakeKey( SesId, ConfigTableId, Name,
+ ( wcslen( Name) + 1) * sizeof(WCHAR), JET_bitNewKey);
+ if ( JetError != JET_errSuccess) {
+ RplAssert( TRUE, ("MakeKey failed err=%d", JetError));
+ return( FALSE);
+ }
+ JetError = JetSeek( SesId, ConfigTableId, JET_bitSeekEQ);
+ if ( JetError != JET_errSuccess) {
+ RplAssert( TRUE, ("JetSeek for %ws failed err=%d", Name, JetError));
+ return( FALSE);
+ }
+ return( TRUE);
+}
+
+
+VOID ConfigListTable( VOID)
+{
+ ListTable( CONFIG_TABLE_NAME, ConfigTable[ CONFIG_ConfigName].ColumnName,
+ CONFIG_INDEX_ConfigName);
+}
+
diff --git a/private/net/svcdlls/rpl/convert/convert.rc b/private/net/svcdlls/rpl/convert/convert.rc
new file mode 100644
index 000000000..bec8ec87c
--- /dev/null
+++ b/private/net/svcdlls/rpl/convert/convert.rc
@@ -0,0 +1,12 @@
+#include <windows.h>
+#include <ntverp.h>
+
+#define VER_FILETYPE VFT_APP
+#define VER_FILESUBTYPE VFT2_UNKNOWN
+#define VER_FILEDESCRIPTION_STR "RemoteBoot Conversion Utility"
+#define VER_INTERNALNAME_STR "rplcnv.exe"
+#define VER_ORIGINALFILENAME_STR "rplcnv.exe"
+
+#include "common.ver"
+
+1 11 MSG00001.bin
diff --git a/private/net/svcdlls/rpl/convert/debug.c b/private/net/svcdlls/rpl/convert/debug.c
new file mode 100644
index 000000000..918c93752
--- /dev/null
+++ b/private/net/svcdlls/rpl/convert/debug.c
@@ -0,0 +1,76 @@
+/*++
+
+Copyright (c) 1991-1993 Microsoft Corporation
+
+Module Name:
+
+ debug.c
+
+Abstract:
+
+ Debugging module.
+
+Author:
+
+ Jon Newman 26 - April - 1994
+
+Environment:
+
+ User mode
+
+Revision History :
+
+ Built from server\debug.c
+
+--*/
+
+#include "local.h"
+#include "rpldebug.h"
+
+#ifdef RPL_DEBUG
+
+#define RPL_PROMPT "[Rpl] "
+
+// int RG_DebugLevel = -1; // max debugging
+// int RG_DebugLevel = 0; // no debugging, for public use
+
+int RG_DebugLevel; // needed by other modules
+int RG_Assert; // needed by other modules
+
+char RG_DebugPublicBuffer[ 120];
+
+#define RPL_DEFAULT_DEBUG_LEVEL ( RPL_DEBUG_FLOW | \
+ RPL_DEBUG_SERVER | \
+ RPL_DEBUG_MAJOR )
+
+#define RPL_MAXIMUM_DEBUG_LEVEL (-1L)
+
+
+
+VOID _CRTAPI1 RplDebugPrint( CONST CHAR * format, ...)
+{
+ va_list arglist;
+
+ va_start( arglist, format);
+
+ strcpy( RG_DebugPublicBuffer, RPL_PROMPT );
+ vsprintf( RG_DebugPublicBuffer + sizeof( RPL_PROMPT) - 1, format, arglist);
+ DbgPrint( "%s\n", RG_DebugPublicBuffer); // for kernel debugger
+ printf( "%s\n", RG_DebugPublicBuffer); // for user debugger
+
+ if ( RG_Assert != 0) {
+ ASSERT( FALSE); // break for checked build
+ DbgPrint( "[RplSvc] Running checked version of service on a free build.\n");
+ printf( "[RplSvc] Running checked version of service on a free build.\n");
+ DbgUserBreakPoint();
+ }
+
+ va_end( arglist);
+
+} // RplDebugPrint
+
+
+#endif // RPL_DEBUG
+
+
+
diff --git a/private/net/svcdlls/rpl/convert/library.c b/private/net/svcdlls/rpl/convert/library.c
new file mode 100644
index 000000000..1014cbef2
--- /dev/null
+++ b/private/net/svcdlls/rpl/convert/library.c
@@ -0,0 +1,330 @@
+/*++
+
+Copyright (c) 1987-1993 Microsoft Corporation
+
+Module Name:
+
+ library.c
+
+Abstract:
+
+ Common routines used in CONVERT code.
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 19 - November - 1993
+
+Revision History:
+
+ Jon Newman (jonn) 06 - June - 1994
+ Convert file from OEM code page
+
+--*/
+
+#include "local.h"
+
+
+LPWSTR ReadTextFile(
+ IN LPWSTR FilePath,
+ IN LPWSTR FileName,
+ IN DWORD MaxFileSize
+ )
+/*++
+
+Routine Description:
+
+ Reads text file, converts its content from dbcs to unicode, and returns
+ a pointer to newly allocate unicode buffer.
+
+Arguments:
+
+Return Value:
+
+ Pointer to unicode buffer table if successful, NULL otherwise.
+
+--*/
+{
+ PBYTE DbcsString = NULL;
+ DWORD DbcsSize;
+ PWCHAR UnicodeString = NULL;
+ DWORD UnicodeSize;
+ int UnicodeStringLength;
+ HANDLE FileHandle;
+ DWORD BytesRead;
+ BOOL success = FALSE;
+ PWCHAR pWchar;
+ WCHAR CompleteFilePath[ MAX_PATH+1];
+ PWCHAR UseFilePath = FileName;
+
+ CompleteFilePath[0] = L'\0';
+ if ( FilePath != NULL && lstrlenW(FilePath) > 0) {
+ lstrcpyW( CompleteFilePath, FilePath);
+ if ( CompleteFilePath[ lstrlenW(CompleteFilePath)-1 ] != L'\\') {
+ lstrcatW( CompleteFilePath, L"\\");
+ }
+ if ( FileName != NULL) {
+ lstrcatW( CompleteFilePath, FileName );
+ }
+ UseFilePath = CompleteFilePath;
+ }
+
+ FileHandle = CreateFile( UseFilePath, GENERIC_READ, FILE_SHARE_READ,
+ NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0L);
+ if ( FileHandle == INVALID_HANDLE_VALUE) {
+ //
+ // Specifying a bad path (path with missing OS/2 rpl init files)
+ // is a common user error. Print out something for the user.
+ //
+ RplAssert( TRUE, ("CreateFile: Error = %d", GetLastError()));
+ RplPrintf1( RPLI_CVT_CannotOpenFile, UseFilePath);
+ goto exit;
+ }
+ DbcsSize = GetFileSize( FileHandle, NULL); // does not include 0x1A at the file end
+ if ( DbcsSize == INVALID_FILE_SIZE || DbcsSize > MaxFileSize) {
+ RplAssert( TRUE, ("DbcsSize = %d", DbcsSize));
+ goto exit;
+ }
+ DbcsString = GlobalAlloc( GMEM_FIXED, DbcsSize);
+ if ( DbcsString == NULL) {
+ RplAssert( TRUE, ("GlobalAlloc: Error = %d", GetLastError()));
+ goto exit;
+ }
+
+ UnicodeSize = ( DbcsSize + 1) * sizeof(WCHAR); // extra 1 for terminating NULL
+ UnicodeString = GlobalAlloc( GMEM_FIXED, UnicodeSize);
+ if ( UnicodeString == NULL) {
+ RplAssert( TRUE, ("GlobalAlloc: Error = %d", GetLastError()));
+ goto exit;
+ }
+
+ if ( !ReadFile( FileHandle, DbcsString, DbcsSize, &BytesRead, NULL)) {
+ RplAssert( TRUE, ("ReadFile: Error = %d", GetLastError()));
+ goto exit;
+ }
+
+ if ( BytesRead != DbcsSize) {
+ RplAssert( TRUE, ("BytesRead = %d, DbcsSize", BytesRead, DbcsSize));
+ goto exit;
+ }
+
+ UnicodeStringLength = MultiByteToWideChar(
+ CP_OEMCP, // file is in OEM codepage
+ MB_PRECOMPOSED, DbcsString, DbcsSize, UnicodeString, UnicodeSize);
+ if ( UnicodeStringLength == 0) {
+ RplAssert( TRUE, ("MultiByte...: Error = %d", GetLastError()));
+ goto exit;
+ }
+
+ //
+ // If file has END_OF_TEXT_FILE_CHAR, truncate the text there.
+ //
+ pWchar = wcschr( UnicodeString, END_OF_TEXT_FILE_CHAR);
+ if ( pWchar != NULL) {
+ *pWchar = 0;
+ }
+
+ success = TRUE;
+
+exit:
+
+ if ( FileHandle != INVALID_HANDLE_VALUE) {
+ (VOID)CloseHandle( FileHandle);
+ }
+ if ( DbcsString != NULL) {
+ GlobalFree( DbcsString);
+ }
+
+ if ( success != TRUE && UnicodeString != NULL) {
+ GlobalFree( UnicodeString);
+ UnicodeString = NULL;
+ }
+
+ return( UnicodeString);
+
+} // ReadTextFile
+
+
+PWCHAR GetFirstLine( PWCHAR Cursor)
+/*++
+ Skips all white characters.
+--*/
+//
+{
+ // Read empty chars if there's some.
+ while( *Cursor != 0 && iswspace(*Cursor)) {
+ Cursor++;
+ }
+
+ return( Cursor);
+}
+
+
+
+PWCHAR GetNextLine( PWCHAR Cursor)
+/*++
+ Skips to the end of the current line.
+ Then skips all white characters.
+--*/
+//
+{
+ // Read to end of line.
+ do {
+ Cursor++;
+ } while ( *Cursor != 0 && *Cursor != NEW_LINE_CHAR);
+
+ // Read empty chars if there's some.
+ while( *Cursor != 0 && iswspace(*Cursor)) {
+ Cursor++;
+ }
+
+ return( Cursor);
+}
+
+
+DWORD OffsetAfterComment( IN PWCHAR Cursor)
+/*++
+ Returns the offset of the first non-white non-newline character
+ after an optional RPL_COMMENT. This routine does not & should
+ not cross line boundaries (this is reserved for GetNewLine).
+--*/
+{
+#define RPL_COMMENT L";*;"
+#define RPL_COMMENT_LENGTH (sizeof(RPL_COMMENT)/sizeof(WCHAR) - 1)
+
+ PWCHAR End;
+
+ if ( wcsncmp( Cursor, RPL_COMMENT, RPL_COMMENT_LENGTH)) {
+ return( 0); // there is no RPL_COMMENT
+ }
+ for ( End = Cursor + RPL_COMMENT_LENGTH;
+ *End != 0 && *End != NEW_LINE_CHAR && iswspace(*End);
+ End++) {
+ NOTHING;
+ }
+ if ( *End == NEW_LINE_CHAR) {
+ //
+ // We went to far. Must decrement end pointer or
+ // GetNewLine() will advance to the second next line.
+ //
+ End--;
+ }
+ return( (DWORD)(End - Cursor));
+}
+
+
+BOOL Find( IN JET_TABLEID TableId, IN PWCHAR Name)
+{
+ JET_ERR JetError;
+
+ JetError = JetMove( SesId, TableId, JET_MoveFirst, 0);
+ if ( JetError != JET_errSuccess) {
+ RplAssert( TRUE, ("JetMove failed err=%d", JetError));
+ return( FALSE);
+ }
+ JetError = JetMakeKey( SesId, TableId, Name,
+ ( wcslen( Name) + 1) * sizeof(WCHAR), JET_bitNewKey);
+ if ( JetError != JET_errSuccess) {
+ RplAssert( TRUE, ("MakeKey failed err=%d", JetError));
+ return( FALSE);
+ }
+ JetError = JetSeek( SesId, TableId, JET_bitSeekEQ);
+ if ( JetError != JET_errSuccess) {
+ if ( JetError == JET_errRecordNotFound) {
+ //
+ // This is an expected error => no break for this.
+ //
+ RplDbgPrint(("JetSeek for %ws failed with error = %d.\n", Name, JetError));
+ } else {
+ RplAssert( TRUE, ("JetSeek failed err=%d", JetError));
+ }
+ return( FALSE);
+ }
+ return( TRUE);
+}
+
+
+VOID Enum( IN JET_TABLEID TableId, IN JET_COLUMNID ColumnId, IN PCHAR IndexName)
+{
+ WCHAR Name[ 20];
+ DWORD NameSize;
+ JET_ERR ForJetError;
+ JET_ERR JetError;
+
+ Call( JetSetCurrentIndex( SesId, TableId, IndexName));
+
+ for ( ForJetError = JetMove( SesId, TableId, JET_MoveFirst, 0);
+ ForJetError == JET_errSuccess;
+ ForJetError = JetMove( SesId, TableId, JET_MoveNext, 0)) {
+
+ JetError = JetRetrieveColumn( SesId, TableId,
+ ColumnId, Name, sizeof( Name), &NameSize, 0, NULL);
+ if ( JetError != JET_errSuccess) {
+ RplAssert( TRUE, ("RetriveColumn failed err=%d", JetError));
+ continue;
+ }
+ RplDbgPrint(( "%ws\n", Name));
+ }
+}
+
+
+VOID ListTable( IN PCHAR TableName, IN PCHAR ColumnName, IN PCHAR IndexName)
+{
+ JET_TABLEID TableId;
+ JET_COLUMNDEF ColumnDef;
+
+ Call( JetOpenTable( SesId, DbId, TableName, NULL, 0,
+ JET_bitTableDenyWrite, &TableId));
+
+ Call( JetGetTableColumnInfo( SesId, TableId, ColumnName, &ColumnDef,
+ sizeof( ColumnDef), JET_ColInfo));
+
+ RplDbgPrint(( "\tTable: %s\n", TableName));
+
+ Enum( TableId, ColumnDef.columnid, IndexName);
+
+ Call( JetCloseTable( SesId, TableId));
+}
+
+
+PWCHAR AddFileExtension(
+ IN PWCHAR FilePath,
+ IN PWCHAR FileExtension,
+ IN BOOLEAN ExtensionOK
+ )
+{
+#define DOT_CHAR L'.'
+#define BACK_SLASH_CHAR L'\\'
+ PWCHAR FilePathEx;
+ PWCHAR pDot;
+ DWORD Length;
+ DWORD Error;
+
+ if ( FilePath == NULL) {
+ RplAssert( TRUE, ("FilePath is NULL"));
+ return( NULL);
+ }
+
+ pDot = wcsrchr( FilePath, DOT_CHAR);
+ if ( pDot != NULL) {
+ //
+ // Found a DOT. FilePath may have an extension.
+ //
+ if ( wcschr( pDot, BACK_SLASH_CHAR) == NULL) {
+ //
+ // There is no backslash after the DOT. FilePath has an extension.
+ // Return NULL if caller insists that file should have no extension.
+ //
+ return( ExtensionOK ? FilePath : NULL);
+ }
+ }
+ Length = wcslen( FilePath) + wcslen( FileExtension);
+ FilePathEx = GlobalAlloc( GMEM_FIXED, (Length + 1) * sizeof(WCHAR));
+ if ( FilePathEx == NULL) {
+ Error = GetLastError();
+ RplAssert( TRUE, ("GlobalAlloc: Error = %d", Error));
+ return( NULL);
+ }
+ wcscpy( FilePathEx, FilePath);
+ wcscat( FilePathEx, FileExtension);
+ return( FilePathEx);
+}
diff --git a/private/net/svcdlls/rpl/convert/local.h b/private/net/svcdlls/rpl/convert/local.h
new file mode 100644
index 000000000..658da46bf
--- /dev/null
+++ b/private/net/svcdlls/rpl/convert/local.h
@@ -0,0 +1,227 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ local.h
+
+Abstract:
+
+ Main include file for Remote initial Program Load CONVERT program.
+ This program converts from RPL.MAP & RPLMGR.INI, used in OS/2
+ lm2.0 and up, to NT database.
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 19 - November - 1993
+
+Revision History:
+
+--*/
+
+#include <nt.h> // ntexapi.h\NtQuerySystemTime
+#include <ntrtl.h> // RtlTimeToSecondsSince1970
+#include <nturtl.h>
+
+#include <windows.h> // DWORD, IN, File APIs, etc.
+#include <stdlib.h>
+#include <lmcons.h>
+
+#include <stdio.h> // vsprintf
+#include <ctype.h> // isspace
+
+#include <lmerr.h> // NERR_RplBootStartFailed - used to be here
+
+//
+#include <lmapibuf.h> // NetApiBufferFree
+#include <netlib.h>
+
+#include <lmsvc.h>
+
+#include <lmalert.h> // STD_ALERT ALERT_OTHER_INFO
+
+#include <lmerrlog.h>
+#include <alertmsg.h>
+#include <lmserver.h>
+#include <netlib.h>
+#include <netlock.h> // Lock data types, functions, and macros.
+#include <thread.h> // FORMAT_NET_THREAD_ID, NetpCurrentThread().
+
+#include <lmshare.h> // PSHARE_INFO_2
+#include <lmaccess.h> // NetGroupGetInfo
+#include <lmconfig.h> // NetConfigGet
+#include <nb30.h> // NCB
+
+#include <lmrpl.h>
+
+//
+// Global types and constants (used in all RPL server files).
+//
+
+#include <riplcons.h> // includes __JET500 flag
+#include <jet.h>
+#include <rpllib.h> // AddKey(), MapJetError(), FillTcpIpString()
+
+#include <rpldb.h>
+#include "nlstxt.h" // RPLI_CVT manifests
+
+#define EQUALS_CHAR L'='
+
+#define RPL_BUGBUG_ERROR 10999 // need to get rid of these
+
+#if DBG
+#define RPL_DEBUG // used in rpldata.h
+#endif
+
+//
+// Declare/Define all global variables.
+//
+
+#include "rpldata.h" // defines/declares global data structures
+
+//
+// jet call macros:
+// Call - jet call ignore error
+// CallR - jet call RETURN on error
+// CallB - jet call return BOOLEAN (false) on error
+// CallM - jet call return MAPPED error
+//
+
+#ifdef RPL_DEBUG
+
+#define RplDbgPrint( _x_) printf( "[DebugPrint] "); printf _x_
+
+#define RplBreakPoint() if ( DebugLevel) DbgUserBreakPoint()
+
+#define RplAssert( DBG_CONDITION, DBG_PRINT_ARGUMENTS) \
+ { \
+ if ( DBG_CONDITION) { \
+ RplDbgPrint( DBG_PRINT_ARGUMENTS); \
+ RplDbgPrint((" File %s, Line %d\n", __FILE__, __LINE__)); \
+ RplBreakPoint(); \
+ } \
+ }
+#define Call( fn ) \
+ { \
+ int _JetError = fn; \
+ if ( _JetError != JET_errSuccess) { \
+ printf("File = %s, Line = %d, _JetError = %d\n", __FILE__, __LINE__, _JetError); \
+ if ( _JetError < 0) { \
+ RplBreakPoint(); \
+ } \
+ } \
+ }
+#define CallR( fn ) \
+ { \
+ int _JetError = fn; \
+ if ( _JetError != JET_errSuccess) { \
+ printf("File = %s, Line = %d, _JetError = %d\n", __FILE__, __LINE__, _JetError); \
+ if ( _JetError < 0) { \
+ RplBreakPoint(); \
+ return; \
+ } \
+ } \
+ }
+
+#define CallB( fn ) \
+ { \
+ int _JetError = fn; \
+ if ( _JetError != JET_errSuccess) { \
+ printf("File = %s, Line = %d, _JetError = %d\n", __FILE__, __LINE__, _JetError); \
+ if ( _JetError < 0) { \
+ RplBreakPoint(); \
+ return( FALSE); \
+ } \
+ } \
+ }
+#define CallM( fn ) \
+ { \
+ int _JetError = fn; \
+ if ( _JetError != JET_errSuccess) { \
+ printf("File = %s, Line = %d, _JetError = %d\n", __FILE__, __LINE__, _JetError); \
+ if ( _JetError < 0) { \
+ RplBreakPoint(); \
+ return( NERR_RplInternal); \
+ } \
+ } \
+ }
+#define CallJ( fn ) \
+ { \
+ int _JetError = fn; \
+ if ( _JetError != JET_errSuccess) { \
+ printf("File = %s, Line = %d, _JetError = %d\n", __FILE__, __LINE__, _JetError); \
+ if ( _JetError < 0) { \
+ RplBreakPoint(); \
+ Error = NERR_RplInternal; \
+ goto cleanup; \
+ } \
+ } \
+ }
+
+#else
+#define RplDbgPrint( _x_)
+#define RplBreakPoint()
+#define RplAssert( DBG_CONDITION, DBG_PRINT_ARGUMENTS)
+#define Call( fn ) { if ( fn < 0) { NOTHING;} }
+#define CallR( fn ) { if ( fn < 0) { return;} }
+#define CallB( fn ) { if ( fn < 0) { return( FALSE);} }
+#define CallM( fn ) \
+ { \
+ int _JetError = fn; \
+ if ( _JetError < 0) { \
+ return( NERR_RplInternal); \
+ } \
+ }
+#define CallJ( fn ) \
+ { \
+ int _JetError = fn; \
+ if ( _JetError < 0) { \
+ Error = NERR_RplInternal; \
+ goto cleanup; \
+ } \
+ }
+#endif
+
+
+LPWSTR ReadTextFile( IN LPWSTR FilePath, IN LPWSTR FileName, IN DWORD MaxFileSize);
+PWCHAR GetFirstLine( PWCHAR Cursor);
+PWCHAR GetNextLine( PWCHAR Cursor);
+DWORD OffsetAfterComment( IN PWCHAR Cursor);
+BOOL Find( IN JET_TABLEID TableId, IN PWCHAR Name);
+VOID Enum( IN JET_TABLEID TableId, IN JET_COLUMNID ColumnId, IN PCHAR ndexName);
+VOID ListTable( IN PCHAR TableName, IN PCHAR ColumnName, IN PCHAR IndexName);
+PWCHAR AddFileExtension(
+ IN PWCHAR FilePath,
+ IN PWCHAR FileExtension,
+ IN BOOLEAN ExtensionOK
+ );
+
+DWORD BootCreateTable( VOID);
+VOID ProcessBoot( PWCHAR * Fields);
+VOID BootListTable( VOID);
+BOOL FindBoot( IN PWCHAR BootName);
+
+DWORD ProcessRplmgrIni( IN LPWSTR FilePath );
+BOOL FindConfig( IN PWCHAR ConfigName);
+VOID ConfigPruneTable( VOID);
+VOID ConfigListTable( VOID);
+DWORD CloseConfigTable( VOID);
+
+DWORD ProfileCreateTable( VOID);
+BOOL FindProfile( IN PWCHAR ProfileName);
+VOID ProfileListTable( VOID);
+VOID ProcessProfile( PWCHAR * Fields);
+VOID ProfilePruneTable( VOID);
+
+DWORD WkstaCreateTable( VOID);
+VOID ProcessWksta( PWCHAR * Fields);
+VOID WkstaPruneTable( VOID);
+VOID WkstaListTable( VOID);
+BOOL ListWkstaInfo( IN PWCHAR AdapterName);
+
+DWORD AdapterCreateTable( VOID);
+
+DWORD VendorCreateTable( VOID);
+
+
diff --git a/private/net/svcdlls/rpl/convert/makefile b/private/net/svcdlls/rpl/convert/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/net/svcdlls/rpl/convert/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/net/svcdlls/rpl/convert/makefile.inc b/private/net/svcdlls/rpl/convert/makefile.inc
new file mode 100644
index 000000000..f28149e83
--- /dev/null
+++ b/private/net/svcdlls/rpl/convert/makefile.inc
@@ -0,0 +1,4 @@
+nlstxt.rc: msg00001.bin
+
+nlstxt.h msg00001.bin: nlstxt.mc
+ mc -v nlstxt.mc
diff --git a/private/net/svcdlls/rpl/convert/nlstxt.mc b/private/net/svcdlls/rpl/convert/nlstxt.mc
new file mode 100644
index 000000000..8b8e47f8e
--- /dev/null
+++ b/private/net/svcdlls/rpl/convert/nlstxt.mc
@@ -0,0 +1,214 @@
+;/*++
+;
+;Copyright (c) 1992 Microsoft Corporation
+;
+;Module Name:
+;
+; rplimsg.h
+;
+;Abstract:
+;
+; Definitions for RPLCNV command private errors.
+;
+;Author:
+;
+; Jon Newman (jonn) 21 - April - 1994
+;
+;Revision History:
+;
+; 21-Apr-1994 jonn
+; templated from AT command
+;
+;Notes:
+;
+; This file is generated by the MC tool from the lmatmsg.mc file.
+;
+;--*/
+
+MessageId=11000 SymbolicName=RPLI_CVT_NoMDB
+Language=English
+File system.mdb is absent.%n
+.
+
+MessageId=+1 SymbolicName=RPLI_CVT_StopService
+Language=English
+Remote boot service must be stopped for this program to run.%n
+.
+
+MessageId=+1 SymbolicName=RPLI_CVT_InitError
+Language=English
+Unexpected initialization error.%n
+.
+
+MessageId=+1 SymbolicName=RPLI_CVT_CannotOpenFile
+Language=English
+Cannot open %1!ws!%n
+.
+
+MessageId=+1 SymbolicName=RPLI_CVT_ProfileInvalid
+Language=English
+Not converting %1!ws! profile because its name is invalid.%n
+.
+
+MessageId=+1 SymbolicName=RPLI_CVT_ProfileInvalidConfig
+Language=English
+Not converting %1!ws! profile because its configuration %2!ws!
+is invalid.%n
+.
+
+MessageId=+1 SymbolicName=RPLI_CVT_ProfileInvalidFit
+Language=English
+Not converting %1!ws! profile because its fit file %2!ws!
+is invalid.%n
+.
+
+MessageId=+1 SymbolicName=RPLI_CVT_ProfileInvalidBoot
+Language=English
+Not converting %1!ws! profile because its boot block name %2!ws!
+is invalid.%n
+.
+
+MessageId=+1 SymbolicName=RPLI_CVT_USAGE
+Language=English
+The RPLCNV utility (RemoteBoot conversion program) converts
+rpl.map and rplmgr.ini used by OS/2 LANMAN Remoteboot service into system.mdb
+and rplsvc.mdb files used by NT Remoteboot service. Install the Remoteboot
+service before running the Remoteboot conversion program, and
+make sure a copy of the original system.mdb file is in
+the root of the RPL file tree as specified during RPL installation.%n
+%n
+RPLCNV [Path]%n
+%n
+Path Specifies a fully qualified path to the directory containing the
+rpl.map and rplmgr.ini files used by OS/2 LANMAN RemoteBoot service.
+If this parameter is omitted it is assumed that these files are in the
+current directory or the root of the RPL file tree.
+.
+
+MessageId=+1 SymbolicName=RPLI_CVT_NoMAP_INI
+Language=English
+Files rpl.map and/or rplmgr.ini are absent.%n
+.
+
+MessageId=+1 SymbolicName=RPLI_CVT_NoSystemMdb
+Language=English
+File system.mdb is absent from the root of the RPL file tree.
+RPLCNV cannot convert rpl.map and rplmgr.ini unless a copy
+of the original system.mdb file is in the root of the RPL file tree.%n
+.
+
+MessageId=+1 SymbolicName=RPLI_CVT_RplsvcMdbExists
+Language=English
+File rplsvc.mdb already exists in the root of the RPL file tree.
+RPLCNV will not create a new one until the existing one is moved,
+renamed or deleted.%n
+.
+
+MessageId=+1 SymbolicName=RPLI_CVT_OldSystemMdb
+Language=English
+File system.mdb exists in the root of the RPL file tree but is not
+a copy of the original system.mdb file. RPLCNV cannot convert rpl.map
+and rplmgr.ini unless a copy of the original system.mdb file is in the
+root of the RPL file tree.%n
+.
+
+MessageId=+1 SymbolicName=RPLI_CVT_BootNameTooLong
+Language=English
+Boot block record with name %1!ws! is not converted because
+its name is too long or invalid.%n
+.
+
+MessageId=+1 SymbolicName=RPLI_CVT_BbcFileTooLong
+Language=English
+Boot block record with name %1!ws! is not converted because
+path to boot block configuration file %2!ws! is too long or
+invalid.%n
+.
+
+MessageId=+1 SymbolicName=RPLI_CVT_BadVendorName
+Language=English
+Boot block record with name %1!ws! is not converted because
+vendor name %2!ws! is invalid.%n
+.
+
+MessageId=+1 SymbolicName=RPLI_CVT_WkstaInvalid
+Language=English
+Workstation record with name %1!ws! is not converted because
+its name is too long or invalid.%n
+.
+
+MessageId=+1 SymbolicName=RPLI_CVT_WkstaInvalidAdapter
+Language=English
+Workstation record with name %1!ws! is not converted because
+adapter id %2!ws! is invalid.%n
+.
+
+MessageId=+1 SymbolicName=RPLI_CVT_WkstaInvalidLogon
+Language=English
+Workstation record with name %1!ws! is not converted because
+remoteboot username/password prompting field %2!ws! is invalid.%n
+.
+
+MessageId=+1 SymbolicName=RPLI_CVT_WkstaInvalidFit
+Language=English
+Workstation record with name %1!ws! is not converted because
+fit name %2!ws! is too long or invalid.%n
+.
+
+MessageId=+1 SymbolicName=RPLI_CVT_WkstaInvalidSharing
+Language=English
+Workstation record with name %1!ws! is not converted because
+sharing type %2!ws! is invalid.%n
+.
+
+MessageId=+1 SymbolicName=RPLI_CVT_WkstaDisabledBoot
+Language=English
+Workstation record with name %1!ws! is not converted since its
+reference to block record %2!ws! is disabled.%n
+.
+
+MessageId=+1 SymbolicName=RPLI_CVT_WkstaInvalidBoot
+Language=English
+Workstation record with name %1!ws! is not converted because
+boot block name %2!ws! is invalid.%n
+.
+
+MessageId=+1 SymbolicName=RPLI_CVT_WkstaInvalidProfile
+Language=English
+Workstation record with name %1!ws! is not converted because
+profile name %2!ws! is invalid.%n
+.
+
+MessageId=+1 SymbolicName=RPLI_CVT_WkstaDuplicateName
+Language=English
+Workstation record is not converted because it uses a duplicate computer name
+(%1!ws!) or a duplicate adapter id (%2!ws!).%n
+.
+
+MessageId=+1 SymbolicName=RPLI_CVT_WkstaDefaultProfile
+Language=English
+Workstation record with computer name %1!ws! is not converted
+because it uses the obsolete DEFAULT profile.%n
+.
+
+MessageId=+1 SymbolicName=RPLI_CVT_ConfigNoPersonalFIT
+Language=English
+Not converting %1!ws! configuration because it does not have
+a fit file for personal profiles.%n
+.
+
+MessageId=+1 SymbolicName=RPLI_CVT_ConfigNoOS2
+Language=English
+Not converting %1!ws! configuration because it is an OS/2
+client configuration.%n
+.
+
+MessageId=+1 SymbolicName=RPLI_CVT_ConfigInvalid
+Language=English
+Not converting %1!ws! configuration because the value of
+keyword %2!ws! is absent or invalid.%n
+.
+
+
+
+ \ No newline at end of file
diff --git a/private/net/svcdlls/rpl/convert/profile.c b/private/net/svcdlls/rpl/convert/profile.c
new file mode 100644
index 000000000..f8f0c14a8
--- /dev/null
+++ b/private/net/svcdlls/rpl/convert/profile.c
@@ -0,0 +1,270 @@
+/*++
+
+Copyright (c) 1987-1993 Microsoft Corporation
+
+Module Name:
+
+ profile.c
+
+Abstract:
+
+ Creates profile table. Parses old style RPL.MAP profile records and
+ creates corresponding entries in jet database table.
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 19 - November - 1993
+
+Revision History:
+
+--*/
+
+#include "local.h"
+#define RPLPROFILE_ALLOCATE
+#include "profile.h"
+#undef RPLPROFILE_ALLOCATE
+
+
+DWORD ProfileCreateTable( VOID)
+{
+ JET_COLUMNDEF ColumnDef;
+ JET_ERR JetError;
+ DWORD index;
+ DWORD Offset;
+ CHAR IndexKey[ 255];
+
+ JetError = JetCreateTable( SesId, DbId, PROFILE_TABLE_NAME,
+ PROFILE_TABLE_PAGE_COUNT, PROFILE_TABLE_DENSITY, &ProfileTableId);
+
+ //
+ // Create columns. First initalize fields that do not change between
+ // addition of columns.
+ //
+ ColumnDef.cbStruct = sizeof(ColumnDef);
+ ColumnDef.columnid = 0;
+ ColumnDef.wCountry = 1;
+ ColumnDef.langid = 0x0409; // USA english
+ ColumnDef.cp = 1200; // UNICODE codepage
+ ColumnDef.wCollate = 0;
+ ColumnDef.cbMax = 0;
+ ColumnDef.grbit = 0; // variable length binary and text data.
+
+ for ( index = 0; index < PROFILE_TABLE_LENGTH; index++ ) {
+
+ ColumnDef.coltyp = ProfileTable[ index].ColumnType;
+
+ Call( JetAddColumn( SesId, ProfileTableId,
+ ProfileTable[ index].ColumnName, &ColumnDef,
+ NULL, 0, &ProfileTable[ index].ColumnId));
+ }
+
+ Offset = AddKey( IndexKey, '+', ProfileTable[ PROFILE_ProfileName].ColumnName);
+ IndexKey[ Offset++] = '\0';
+ JetError = JetCreateIndex( SesId, ProfileTableId, PROFILE_INDEX_ProfileName,
+ JET_bitIndexPrimary, IndexKey, Offset, 50);
+ if ( JetError != JET_errSuccess) {
+ RplAssert( TRUE, ("CreateIndex failed err=%d", JetError));
+ return( MapJetError( JetError));
+ }
+
+ //
+ // +BootName+ProfileName index is used to enumerate all profiles for
+ // a given VendorId.
+ //
+ Offset = AddKey( IndexKey, '+', ProfileTable[ PROFILE_BootName].ColumnName);
+ Offset += AddKey( IndexKey + Offset, '+', ProfileTable[ PROFILE_ProfileName].ColumnName);
+ IndexKey[ Offset++] = '\0';
+ JetError = JetCreateIndex( SesId, ProfileTableId, PROFILE_INDEX_BootNameProfileName,
+ JET_bitIndexUnique, IndexKey, Offset, 50);
+ if ( JetError != JET_errSuccess) {
+ RplAssert( TRUE, ("CreateIndex failed err=%d", JetError));
+ return( MapJetError( JetError));
+ }
+
+ //
+ // +ConfigName+ProfileName index is used to find if a there is a
+ // profile record for a given configuration record
+ //
+ Offset = AddKey( IndexKey, '+', ProfileTable[ PROFILE_ConfigName].ColumnName);
+ Offset += AddKey( IndexKey + Offset, '+', ProfileTable[ PROFILE_ProfileName].ColumnName);
+ IndexKey[ Offset++] = '\0';
+ JetError = JetCreateIndex( SesId, ProfileTableId, PROFILE_INDEX_ConfigNameProfileName,
+ JET_bitIndexUnique, IndexKey, Offset, 50);
+ if ( JetError != JET_errSuccess) {
+ RplAssert( TRUE, ("CreateIndex failed err=%d", JetError));
+ return( MapJetError( JetError));
+ }
+
+ //
+ // Make profile name a current index - used to validate wkstas.
+ //
+ JetError = JetSetCurrentIndex( SesId, ProfileTableId, PROFILE_INDEX_ProfileName);
+ if ( JetError != JET_errSuccess) {
+ RplAssert( TRUE, ("SetCurrentIndex failed err=%d", JetError));
+ return( MapJetError( JetError));
+ }
+
+ return( ERROR_SUCCESS);
+}
+
+
+VOID ProcessProfile( PWCHAR * Fields)
+{
+//
+// PROFILE_COMMENT_INDEX would be equal to 15 if we were to count fields
+// from 1 (not 0) and count ",,," as a single field (not 3 fields).
+//
+#define PROFILE_FITFILE_INDEX 3 // common fit file name
+#define PROFILE_CONFIGNAME_INDEX 12 // configuration name
+#define PROFILE_BOOTNAME_INDEX 13 // boot block identifier
+#define PROFILE_PROFILENAME_INDEX 15 // profile name
+#define PROFILE_COMMENT_INDEX 16 // profile comment
+
+ PWCHAR ProfileComment;
+ PWCHAR ProfileName;
+ PWCHAR BootName;
+ PWCHAR ConfigName;
+ PWCHAR FitShared;
+ PWCHAR FitPersonal;
+ DWORD Flags;
+
+ ProfileName = Fields[ PROFILE_PROFILENAME_INDEX];
+ if ( !ValidName( ProfileName, RPL_MAX_PROFILE_NAME_LENGTH, TRUE)) {
+ RplPrintf1( RPLI_CVT_ProfileInvalid, ProfileName);
+ RplAssert( TRUE, ("Bad profile name %ws", ProfileName));
+ return;
+ }
+ _wcsupr( ProfileName);
+
+ FitShared = AddFileExtension( Fields[ PROFILE_FITFILE_INDEX], L".FIT", FALSE);
+ FitPersonal = AddFileExtension( Fields[ PROFILE_FITFILE_INDEX], L"P.FIT", FALSE);
+ if ( !ValidName( FitShared, RPL_MAX_STRING_LENGTH, TRUE) ||
+ !ValidName( FitPersonal, RPL_MAX_STRING_LENGTH, TRUE)) {
+ RplPrintf2( RPLI_CVT_ProfileInvalidFit, ProfileName, Fields[ PROFILE_FITFILE_INDEX]);
+ RplAssert( TRUE, ("Bad fit name %ws", Fields[ PROFILE_FITFILE_INDEX]));
+ return;
+ }
+
+ ConfigName = Fields[ PROFILE_CONFIGNAME_INDEX];
+ if ( !ValidName( ConfigName, RPL_MAX_CONFIG_NAME_LENGTH, TRUE)) {
+ RplPrintf2( RPLI_CVT_ProfileInvalidConfig, ProfileName, ConfigName);
+ RplAssert( TRUE, ("Bad config name %ws", ConfigName));
+ return;
+ }
+ _wcsupr( ConfigName);
+
+ BootName = Fields[ PROFILE_BOOTNAME_INDEX];
+ RplAssert( *BootName != L'R', ("BootBlockId=%ws", BootName));
+ BootName++; // skip leading 'R' in server name
+ if ( !ValidName( BootName, RPL_MAX_BOOT_NAME_LENGTH, TRUE)) {
+ RplPrintf2( RPLI_CVT_ProfileInvalidBoot, ProfileName, Fields[ PROFILE_BOOTNAME_INDEX]);
+ RplAssert( TRUE, ("Bad boot name %ws", Fields[ PROFILE_BOOTNAME_INDEX]));
+ return;
+ }
+ _wcsupr( BootName);
+
+ ProfileComment = Fields[ PROFILE_COMMENT_INDEX];
+ if ( RPL_STRING_TOO_LONG( ProfileComment)) {
+ ProfileComment[ RPL_MAX_STRING_LENGTH] = 0; // silently truncate it
+ }
+
+ Call( JetPrepareUpdate( SesId, ProfileTableId, JET_prepInsert));
+
+ Call( JetSetColumn( SesId, ProfileTableId,
+ ProfileTable[ PROFILE_ProfileName].ColumnId, ProfileName,
+ (wcslen( ProfileName) + 1) * sizeof(WCHAR), 0, NULL));
+
+ Call( JetSetColumn( SesId, ProfileTableId,
+ ProfileTable[ PROFILE_ProfileComment].ColumnId, ProfileComment,
+ (wcslen( ProfileComment) + 1) * sizeof(WCHAR), 0, NULL));
+
+ Call( JetSetColumn( SesId, ProfileTableId,
+ ProfileTable[ PROFILE_ConfigName].ColumnId, ConfigName,
+ (wcslen( ConfigName) + 1) * sizeof(WCHAR), 0, NULL));
+
+ Call( JetSetColumn( SesId, ProfileTableId,
+ ProfileTable[ PROFILE_BootName].ColumnId, BootName,
+ (wcslen( BootName) + 1) * sizeof(WCHAR), 0, NULL));
+
+ Call( JetSetColumn( SesId, ProfileTableId,
+ ProfileTable[ PROFILE_FitShared].ColumnId, FitShared,
+ (wcslen( FitShared) + 1) * sizeof(WCHAR), 0, NULL));
+
+ Call( JetSetColumn( SesId, ProfileTableId,
+ ProfileTable[ PROFILE_FitPersonal].ColumnId, FitPersonal,
+ (wcslen( FitPersonal) + 1) * sizeof(WCHAR), 0, NULL));
+
+ Flags = 0;
+ Call( JetSetColumn( SesId, ProfileTableId,
+ ProfileTable[ PROFILE_Flags].ColumnId, &Flags, sizeof(Flags), 0, NULL));
+
+
+ Call( JetUpdate( SesId, ProfileTableId, NULL, 0, NULL));
+}
+
+
+VOID ProfilePruneTable( VOID)
+/*++
+ Eliminate profile records that do not have a corresponding config record
+ defined.
+--*/
+{
+ WCHAR Name[ 20];
+ DWORD NameSize;
+ JET_ERR ForJetError;
+ JET_ERR JetError;
+
+ for ( ForJetError = JetMove( SesId, ProfileTableId, JET_MoveFirst, 0);
+ ForJetError == JET_errSuccess;
+ ForJetError = JetMove( SesId, ProfileTableId, JET_MoveNext, 0)) {
+
+ JetError = JetRetrieveColumn( SesId, ProfileTableId,
+ ProfileTable[ PROFILE_BootName].ColumnId, Name,
+ sizeof( Name), &NameSize, 0, NULL);
+ if ( JetError != JET_errSuccess) {
+ RplAssert( TRUE, ("RetriveColumn failed err=%d", JetError));
+ Call( JetDelete( SesId, ProfileTableId));
+ continue;
+ }
+ if ( !FindBoot( Name)) {
+ Call( JetDelete( SesId, ProfileTableId));
+ continue;
+ }
+
+ JetError = JetRetrieveColumn( SesId, ProfileTableId,
+ ProfileTable[ PROFILE_ConfigName].ColumnId, Name,
+ sizeof( Name), &NameSize, 0, NULL);
+ if ( JetError != JET_errSuccess) {
+ RplAssert( TRUE, ("RetriveColumn failed err=%d", JetError));
+ Call( JetDelete( SesId, ProfileTableId));
+ continue;
+ }
+ if ( !FindConfig( Name)) {
+ WCHAR ProfileName[ 20];
+ DWORD ProfileNameSize;
+ JetError = JetRetrieveColumn( SesId, ProfileTableId,
+ ProfileTable[ PROFILE_ProfileName].ColumnId, ProfileName,
+ sizeof( ProfileName), &ProfileNameSize, 0, NULL);
+ if ( JetError == JET_errSuccess && ProfileNameSize <= sizeof( ProfileName) ) {
+ RplPrintf2( RPLI_CVT_ProfileInvalidConfig, ProfileName, Name);
+ }
+ Call( JetDelete( SesId, ProfileTableId));
+ continue;
+ }
+ }
+}
+
+
+
+BOOL FindProfile( IN PWCHAR ProfileName)
+{
+ return( Find( ProfileTableId, ProfileName));
+}
+
+
+
+VOID ProfileListTable( VOID)
+{
+ ListTable( PROFILE_TABLE_NAME, ProfileTable[ PROFILE_ProfileName].ColumnName,
+ PROFILE_INDEX_ProfileName);
+}
diff --git a/private/net/svcdlls/rpl/convert/rpldata.h b/private/net/svcdlls/rpl/convert/rpldata.h
new file mode 100644
index 000000000..d62c9b2e5
--- /dev/null
+++ b/private/net/svcdlls/rpl/convert/rpldata.h
@@ -0,0 +1,47 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ rpldata.h
+
+Abstract:
+
+ RPL covert global variables (declarations & definitions).
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 19 - November - 1993
+
+Environment:
+
+ User mode
+
+Revision History :
+
+--*/
+
+//
+// rplmain.c will #include this file with RPLDATA_ALLOCATE defined.
+// That will cause each of these variables to be allocated.
+//
+#ifdef RPLDATA_ALLOCATE
+#define EXTERN
+#define INIT( _x) = _x
+#else
+#define EXTERN extern
+#define INIT( _x)
+#endif
+
+EXTERN JET_SESID SesId;
+EXTERN JET_DBID DbId;
+EXTERN JET_INSTANCE JetInstance;
+
+EXTERN CHAR G_ServiceDatabaseA[ MAX_PATH ];
+EXTERN WCHAR G_ServiceDirectoryW[ MAX_PATH ];
+
+#ifdef RPL_DEBUG
+EXTERN int DebugLevel;
+#endif
+
diff --git a/private/net/svcdlls/rpl/convert/rplmain.c b/private/net/svcdlls/rpl/convert/rplmain.c
new file mode 100644
index 000000000..a2643e0b6
--- /dev/null
+++ b/private/net/svcdlls/rpl/convert/rplmain.c
@@ -0,0 +1,740 @@
+/*++
+
+Copyright (c) 1987-1993 Microsoft Corporation
+
+Module Name:
+
+ rplmain.c
+
+Abstract:
+
+ Converts old style (OS/2) files RPL.MAP & RPLMGR.INI
+ into a new style database to be used with NT rpl server.
+
+ BUGBUG Should polish and make WIN32 application out of this.
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 19 - November - 1993
+
+Environment:
+
+ User mode
+
+Revision History :
+
+--*/
+
+#define RPLDATA_ALLOCATE
+#include "local.h"
+#undef RPLDATA_ALLOCATE
+#include <shellapi.h>
+#include "boot.h"
+#include "config.h"
+#include "profile.h"
+#include "wksta.h"
+#include "adapter.h"
+#include "vendor.h"
+
+#define COMMA_CHAR L','
+PWCHAR RplMapFile = L"rpl.map";
+WCHAR RG_NulWchar = 0; // used for empty entries in rpl.map
+//
+// RPL.MAP defines
+//
+#define MAX_RPL_MAP_FILE_SIZE 0xFFFE // BUGBUG some arbitrary value
+typedef enum _MAP_RECORD_TYPE {
+ RecordTypeComment = 0, // comment line in PRL.MAP file
+ RecordTypeBoot, // boot block records, documented in os/2 lm2.0 & up
+ RecordTypePcDosBoot, // boot block records, undocumented but supported
+ RecordTypeProfile, // profile record
+ RecordTypeUniqueAdapter,// wksta record containing unique adapter id
+ RecordTypeWildAdapter, // wksta record containing wildcards in adapter id
+ RecordTypeUnknown // unknown record
+} MAP_RECORD_TYPE;
+
+#define EXTENDED_BOOT_ENTRIES L"yyyyyyyyyyyy"
+#define EXTENDED_BOOT_ENTRIES2 L"RPL~Header~~"
+#define PCDOS_BOOT_ENTRIES L"xxxxxxxxxxxx"
+#define PCDOS_BOOT_ENTRIES2 L"PCDOS~Header"
+#define PROFILE_ADAPTER_ID L"000000000000" // first field of profile record
+
+
+
+#define MAX_RPL_FIELD 20 // max number of entries on rpl.map line
+
+
+
+
+BOOL StringsSpaceEqual( IN PWCHAR str1, IN PWCHAR str2 )
+/*++
+
+Routine Description:
+
+ Compare strings up to the first space or NULL character in each
+ string, returning TRUE if strings are equal, FALSE otherwise.
+
+Arguments:
+
+ str1 - first string
+ str2 - second string
+
+Return Value:
+
+ TRUE if strings are equal, FALSE otherwise.
+
+--*/
+{
+ WCHAR ch1;
+
+#ifdef NOT_YET
+ if ( str1 == NULL || str2 == NULL) {
+ str1 = (str1 != NULL) ? str1 : str2;
+ if ( str2 == NULL) {
+ return( TRUE); // both strings are NULL
+ }
+ while ( iswspace( *str1++)) {
+ NOTHING; // check if not NULL string contains spaces only
+ }
+ return( *str1 == 0);
+ }
+#endif // NOT_YET
+
+ //
+ // Compare strings until the first space or NULL character
+ // (in the first string) or until we find the first different character.
+ //
+ while ( (ch1 = *str1) && !iswspace(ch1) && ch1 == *str2) {
+ str1++, str2++;
+ }
+
+ //
+ // For strings to be equal, both characters must be NULL or space.
+ //
+ if ( (!ch1 || iswspace(ch1)) && (!(ch1 = *str2) || iswspace(ch1)))
+ {
+ return( TRUE);
+ }
+
+ return( FALSE);
+}
+
+
+BOOL IsWildAdapterName( IN PWCHAR Cursor)
+/*++
+
+Routine Description:
+
+ Returns TRUE if the input string may be the adapter id of a wksta group.
+
+ Adapter id must be terminated with space AND must have exactly
+ RPL_ADAPTER_NAME_LENGTH hex digits.
+
+Arguments:
+
+ Cursor - pointer to adapter id string
+
+Return Value:
+ Returns TRUE if input string may be a unique adapter id of a client,
+ else FALSE.
+
+--*/
+{
+ DWORD count = 0;
+ WCHAR ch;
+
+ for ( count = 0; (ch = *Cursor) == '?' || iswxdigit(ch); count++) {
+ Cursor++;
+ }
+
+ if ( !iswspace(ch) || count != RPL_ADAPTER_NAME_LENGTH) {
+ return( FALSE);
+ }
+
+ return( TRUE);
+}
+
+
+BOOL IsAdapterName( IN PWCHAR Cursor)
+/*++
+
+Routine Description:
+
+ Returns TRUE if the input string may be the adapter id of a wksta.
+
+ Adapter id must be terminated with space AND must have
+ exactly RPL_ADAPTER_NAME_LENGTH hex digits
+
+Arguments:
+
+ Cursor - pointer to adapter id string
+
+Return Value:
+ Returns TRUE if input string may be a unique adapter id of a client,
+ else FALSE.
+
+--*/
+{
+ DWORD count;
+
+ for ( count = 0; iswxdigit(*Cursor); count++) {
+ Cursor++;
+ }
+
+ if ( !iswspace(*Cursor) || count != RPL_ADAPTER_NAME_LENGTH) {
+ return( FALSE);
+ }
+ return( TRUE);
+}
+
+
+MAP_RECORD_TYPE RplGetRecordType( IN PWCHAR Cursor)
+/*++
+
+Routine Description:
+
+ Returns the type of a line in RPL.MAP.
+
+Arguments:
+ Cursor - pointer to line from RPL.map file
+
+Return Value:
+ Returns type of a line in RPL.map file
+
+--*/
+{
+ // strings can be either nul or space terminated
+
+ if ( *Cursor == COMMENT_CHAR) {
+
+ return( RecordTypeComment);
+ }
+
+ if ( StringsSpaceEqual( Cursor, EXTENDED_BOOT_ENTRIES) ||
+ StringsSpaceEqual( Cursor, EXTENDED_BOOT_ENTRIES2)) {
+
+ return( RecordTypeBoot);
+ }
+
+ if ( StringsSpaceEqual( Cursor, PCDOS_BOOT_ENTRIES) ||
+ StringsSpaceEqual( Cursor, PCDOS_BOOT_ENTRIES2)) {
+
+ return( RecordTypePcDosBoot);
+ }
+
+ if ( StringsSpaceEqual( Cursor, PROFILE_ADAPTER_ID)) {
+
+ return( RecordTypeProfile);
+ }
+
+ if ( IsAdapterName( Cursor)) {
+ return( RecordTypeUniqueAdapter);
+ }
+
+ if ( IsWildAdapterName( Cursor)) {
+ return( RecordTypeWildAdapter);
+ }
+
+ return( RecordTypeUnknown);
+}
+
+
+
+PWCHAR ScanChar(
+ IN PWCHAR String,
+ IN WCHAR ch
+ )
+/*++
+
+Routine Description:
+
+ Searches for the first desired character in the string. The string
+ should be NULL terminated.
+
+Arguments:
+ String - string which is NULL terminated
+ ch - character we are searching for
+
+Return Value:
+ Pointer to the first desired character (if there is such character),
+ else pointer to the last character before terminating NULL.
+
+--*/
+{
+ while ( *String && *String != ch) {
+ String++;
+ }
+ return( String);
+}
+
+
+PWCHAR ScanNonSpace( IN PWCHAR String)
+/*++
+
+Routine Description:
+ Returns pointer to the first non space character in the string.
+ This may be pointer to NULL if string has no space char.
+
+Arguments:
+ string - NULL terminated string to search in
+
+Return Value:
+ Pointer of the first non space character in the string.
+
+--*/
+{
+ while( iswspace(*String)) { // iswspace(0) is 0
+ String++;
+ }
+ return( String);
+}
+
+
+
+PWCHAR ScanSpace( IN PWCHAR String)
+/*++
+
+Routine Description:
+
+ Returns pointer to the first space character in the string if it
+ can find a space character in the string. Else, it returns pointer
+ to zero character ('\0').
+
+Arguments:
+ String - where we are searching for space character
+
+Return Value:
+ Pointer to the first space character (if there is a space char) or
+ pointer to zero.
+
+--*/
+{
+ while ( *String && !iswspace(*String)) {
+ String++;
+ }
+ return( String);
+}
+
+
+BOOL LineToWords(
+ IN PWCHAR Cursor,
+ IN PWCHAR * Fields,
+ IN DWORD FieldsLength
+ )
+/*++
+
+Routine Description:
+ Breaks the line the words and returns the pointer to a word (string) table.
+
+ Note that we do special line parsing when current table index is 9, 10
+ or 11. E.g. field ",,," of RPL.map line will result in three table
+ entries instead of just one.
+
+Arguments:
+ Cursor - ptr to line string
+ Fields - pointer to string table describing fields in RPL.MAP line
+ FieldsLength - max number of entries in fields string table,
+ not counting the terminal NULL entry
+Return Value:
+ None.
+
+--*/
+{
+#define FIRST_EXTRA_MEM 9
+#define LAST_EXTRA_MEM 11
+ PWCHAR str;
+ DWORD index;
+ DWORD length;
+
+
+ for ( index = 0; index < FieldsLength; index++) {
+
+ if (*Cursor) {
+
+ //
+ // check if extra mem field(s)
+ //
+
+ if ( index >= FIRST_EXTRA_MEM && index <= LAST_EXTRA_MEM) {
+ length = ScanChar( Cursor, COMMA_CHAR) - Cursor;
+ if ( Cursor[ length] == COMMA_CHAR) {
+ length++;
+ }
+ } else {
+ length = ScanSpace( Cursor) - Cursor;
+ }
+
+ if (length == 0 || (length == 1 && *Cursor == TILDE_CHAR)) {
+
+ Fields[ index] = &RG_NulWchar;
+
+ } else {
+
+ str = Fields[ index] = GlobalAlloc( GMEM_FIXED, (length + 1) * sizeof( WCHAR));
+ if ( str == NULL) {
+ return( FALSE);
+ }
+ memcpy( str, Cursor, length * sizeof( WCHAR));
+ str[ length] = 0;
+
+ // replace all TILDE_CHAR-s in the string
+ while (*(str = ScanChar( str, TILDE_CHAR )) == TILDE_CHAR) {
+ *str = SPACE_CHAR;
+ }
+ }
+ Cursor += length;
+
+ } else {
+ Fields[ index] = &RG_NulWchar;
+ }
+
+ //
+ // jump over spaces
+ //
+
+ Cursor = ScanNonSpace( Cursor);
+ }
+
+ Fields[ index] = NULL; // terminate the string table
+ return( TRUE);
+}
+
+
+DWORD ProcessRplMap( IN LPWSTR FilePath)
+{
+ PWCHAR Cursor;
+ PWCHAR String;
+ DWORD Error;
+ LPWSTR Fields[ MAX_RPL_FIELD + 1]; // add 1 for NULL terminator
+
+ Error = BootCreateTable();
+ if ( Error != ERROR_SUCCESS) {
+ return( Error);
+ }
+
+ Error = ProfileCreateTable();
+ if ( Error != ERROR_SUCCESS) {
+ return( Error);
+ }
+
+ Error = WkstaCreateTable();
+ if ( Error != ERROR_SUCCESS) {
+ return( Error);
+ }
+
+ Error = AdapterCreateTable();
+ if ( Error != ERROR_SUCCESS) {
+ return( Error);
+ }
+
+ String = ReadTextFile( FilePath, RplMapFile, MAX_RPL_MAP_FILE_SIZE);
+ if ( String == NULL) {
+ return( GetLastError());
+ }
+
+ for ( Cursor = GetFirstLine( String);
+ *Cursor != 0;
+ Cursor = GetNextLine( Cursor)) {
+
+ Cursor += OffsetAfterComment( Cursor);
+ switch( RplGetRecordType( Cursor)) {
+ case RecordTypePcDosBoot:
+ NOTHING;
+ break;
+ case RecordTypeBoot: // os/2 lm2.0 & higher, boot block record
+ LineToWords( Cursor, Fields, MAX_RPL_FIELD);
+ ProcessBoot( Fields);
+ break;
+ case RecordTypeProfile:
+ LineToWords( Cursor, Fields, MAX_RPL_FIELD);
+ ProcessProfile( Fields);
+ break;
+ case RecordTypeUniqueAdapter:
+ LineToWords( Cursor, Fields, MAX_RPL_FIELD);
+ ProcessWksta( Fields);
+ break;
+ case RecordTypeWildAdapter:
+ NOTHING;
+ break;
+ case RecordTypeComment:
+ NOTHING;
+ break;
+ default: // unexpected line
+ DbgUserBreakPoint();
+ break;
+ }
+ }
+
+ if ( GlobalFree( String) != NULL) {
+ RplAssert( TRUE, ("GlobalFree: Error=%d", GetLastError()));
+ }
+
+ return( ERROR_SUCCESS);
+}
+
+VOID GlobalInit()
+{
+#ifdef RPL_DEBUG
+ DebugLevel = 0;
+#endif
+ SesId = 0;
+ DbId = 0;
+ JetInstance = 0;
+ BootTableId = 0;
+ ConfigTableId = 0;
+ ProfileTableId = 0;
+ WkstaTableId = 0;
+ AdapterTableId = 0;
+}
+
+
+VOID DbCleanup()
+{
+ if ( VendorTableId != 0) {
+ Call( JetCloseTable( SesId, VendorTableId));
+ }
+ if ( BootTableId != 0) {
+ Call( JetCloseTable( SesId, BootTableId));
+ }
+ if ( ConfigTableId != 0) {
+ Call( JetCloseTable( SesId, ConfigTableId));
+ }
+ if ( ProfileTableId != 0) {
+ Call( JetCloseTable( SesId, ProfileTableId));
+ }
+ if ( WkstaTableId != 0) {
+ Call( JetCloseTable( SesId, WkstaTableId));
+ }
+ if ( AdapterTableId != 0) {
+ Call( JetCloseTable( SesId, AdapterTableId));
+ }
+ if ( DbId != 0) {
+ Call( JetCloseDatabase( SesId, DbId, 0));
+ Call( JetDetachDatabase( SesId, G_ServiceDatabaseA));
+ }
+ if ( SesId != 0)
+ Call( JetEndSession( SesId, 0));
+ if ( JetInstance != 0) {
+ Call( JetTerm2( JetInstance, JET_bitTermComplete));
+ }
+}
+
+
+DWORD SetSystemParameters( VOID)
+{
+ DWORD DirectoryLengthW = sizeof(G_ServiceDirectoryW)/sizeof(WCHAR);
+ DWORD DirectoryLengthA;
+ DWORD Error = RplRegRead( NULL, NULL, NULL, G_ServiceDirectoryW, &DirectoryLengthW );
+ if (Error != NO_ERROR) {
+ return( Error);
+ }
+ if ( !WideCharToMultiByte( CP_OEMCP,
+ 0,
+ G_ServiceDirectoryW,
+ DirectoryLengthW,
+ G_ServiceDatabaseA,
+ sizeof(G_ServiceDatabaseA)/sizeof(CHAR),
+ NULL,
+ NULL ) ) {
+ return( GetLastError() );
+ }
+ DirectoryLengthA = strlen(G_ServiceDatabaseA);
+ if ( DirectoryLengthA + wcslen(RPL_SERVICE_DATABASE_W) > MAX_PATH
+ || DirectoryLengthA + strlen("temp.tmp") > MAX_PATH
+ || DirectoryLengthA + wcslen(RPL_SYSTEM_DATABASE_W) > MAX_PATH) {
+ return( NERR_RplBadRegistry);
+ }
+
+#ifdef __JET500
+ CallM( JetSetSystemParameter( &JetInstance, 0, JET_paramSystemPath, 0, G_ServiceDatabaseA));
+#else
+ strcpy( G_ServiceDatabaseA + DirectoryLengthA, RPL_SYSTEM_DATABASE );
+ CallM( JetSetSystemParameter( &JetInstance, 0, JET_paramSysDbPath, 0, G_ServiceDatabaseA));
+#endif
+
+ strcpy( G_ServiceDatabaseA + DirectoryLengthA, "temp.tmp" );
+ CallM( JetSetSystemParameter( &JetInstance, 0, JET_paramTempPath, 0, G_ServiceDatabaseA));
+
+ strcpy( G_ServiceDatabaseA + DirectoryLengthA, RPL_SERVICE_DATABASE );
+
+ CallM( JetSetSystemParameter( &JetInstance, 0, JET_paramMaxBuffers, 250, NULL));
+ CallM( JetSetSystemParameter( &JetInstance, 0, JET_paramBfThrshldLowPrcnt, 0, NULL));
+ CallM( JetSetSystemParameter( &JetInstance, 0, JET_paramBfThrshldHighPrcnt, 100, NULL));
+ CallM( JetSetSystemParameter( &JetInstance, 0, JET_paramMaxOpenTables, 30, NULL));
+ CallM( JetSetSystemParameter( &JetInstance, 0, JET_paramMaxOpenTableIndexes, 105, NULL))
+ CallM( JetSetSystemParameter( &JetInstance, 0, JET_paramMaxCursors, 100, NULL));
+ CallM( JetSetSystemParameter( &JetInstance, 0, JET_paramMaxSessions, 10, NULL));
+ CallM( JetSetSystemParameter( &JetInstance, 0, JET_paramMaxVerPages, 64, NULL));
+ CallM( JetSetSystemParameter( &JetInstance, 0, JET_paramMaxTemporaryTables, 5, NULL));
+ CallM( JetSetSystemParameter( &JetInstance, 0, JET_paramLogFilePath, 0, "."));
+ CallM( JetSetSystemParameter( &JetInstance, 0, JET_paramLogBuffers, 41, NULL));
+#ifdef __JET500
+ CallM( JetSetSystemParameter( &JetInstance, 0, JET_paramLogFileSize, 1000, NULL));
+ CallM( JetSetSystemParameter( &JetInstance, 0, JET_paramBaseName, 0, "j50"));
+#else
+ CallM( JetSetSystemParameter( &JetInstance, 0, JET_paramLogFileSectors, 1000, NULL));
+#endif
+ CallM( JetSetSystemParameter( &JetInstance, 0, JET_paramLogFlushThreshold, 10, NULL));
+ return( NO_ERROR);
+}
+
+
+DWORD CheckFile(
+ PWCHAR FilePath,
+ PWCHAR FileName
+ )
+{
+ DWORD Error = NO_ERROR;
+ WCHAR CompleteFilePath[ MAX_PATH+1];
+ PWCHAR UseFilePath = FileName;
+ HANDLE FindHandle;
+ WIN32_FIND_DATA FindData;
+
+ CompleteFilePath[0] = L'\0';
+ if ( FilePath != NULL && lstrlenW(FilePath) > 0) {
+ lstrcpyW( CompleteFilePath, FilePath);
+ if ( CompleteFilePath[ lstrlenW(CompleteFilePath)-1 ] != L'\\') {
+ lstrcatW( CompleteFilePath, L"\\");
+ }
+ if ( FileName != NULL) {
+ lstrcatW( CompleteFilePath, FileName );
+ }
+ UseFilePath = CompleteFilePath;
+ }
+ FindHandle = FindFirstFile( UseFilePath, &FindData);
+ if (FindHandle == INVALID_HANDLE_VALUE) {
+ Error = GetLastError();
+ }
+ else
+ {
+ FindClose( FindHandle );
+ }
+
+ return(Error);
+}
+
+
+DWORD _CRTAPI1 main(
+ int argc,
+ CHAR ** charArgv
+ )
+{
+ DWORD Error;
+ JET_ERR JetError;
+ WCHAR ** argv;
+ PWCHAR SourceFilePath;
+ BOOL SpecificDirectory = FALSE;
+ PWCHAR ThisDirectory = L".\\";
+ BOOL RplsvcMdbExists = FALSE;
+
+ GlobalInit();
+ Error = SetSystemParameters();
+ if ( Error != NO_ERROR) {
+ goto SkipCleanup;
+ }
+
+ // Find files rpl.map and rplmgr.ini
+ argv = CommandLineToArgvW( GetCommandLineW(), &argc);
+ if ( argv != NULL && argc > 1) {
+ SourceFilePath = argv[ 1];
+ SpecificDirectory = TRUE;
+ } else {
+ SourceFilePath = G_ServiceDirectoryW;
+ }
+ if ( (Error = CheckFile(SourceFilePath, RplMapFile)) != NO_ERROR
+ || (Error = CheckFile(SourceFilePath, L"rplmgr.ini")) != NO_ERROR )
+ {
+ if ( (!SpecificDirectory)
+ && (Error = CheckFile(ThisDirectory, RplMapFile)) == NO_ERROR
+ && (Error = CheckFile(ThisDirectory, L"rplmgr.ini")) == NO_ERROR )
+ {
+ SourceFilePath = ThisDirectory;
+ }
+ }
+ if (Error != NO_ERROR) {
+ RplPrintf0( RPLI_CVT_NoMAP_INI);
+ goto SkipCleanup;
+ }
+
+#ifndef __JET500
+ // find file system.mdb
+ if ( (Error = CheckFile(G_ServiceDirectoryW, RPL_SYSTEM_DATABASE_W))
+ != NO_ERROR) {
+ RplPrintf0( RPLI_CVT_NoSystemMdb);
+ goto SkipCleanup;
+ }
+#endif
+
+ // check for file rplsvc.mdb
+ RplsvcMdbExists = ((Error = CheckFile(G_ServiceDirectoryW,
+ RPL_SERVICE_DATABASE_W))
+ == NO_ERROR);
+ if (RplsvcMdbExists) {
+ RplPrintf0( RPLI_CVT_RplsvcMdbExists);
+#ifndef RPL_DEBUG
+ goto SkipCleanup;
+#endif
+ }
+
+ JetError = JetInit( &JetInstance);
+ if ( JetError == JET_errInvalidPath) {
+ RplPrintf0( RPLI_CVT_NoMDB);
+ Error = MapJetError( Error);
+ goto SkipCleanup;
+ } else if ( JetError == JET_errFileNotFound) {
+ RplPrintf0( RPLI_CVT_StopService);
+ Error = MapJetError( Error);
+ goto SkipCleanup;
+ } else if ( JetError < 0) {
+ RplPrintf0( RPLI_CVT_InitError);
+ Error = MapJetError( Error);
+ goto SkipCleanup;
+ }
+ CallJ( JetBeginSession( JetInstance, &SesId, "admin", ""));
+ JetError = JetCreateDatabase( SesId, G_ServiceDatabaseA, NULL, &DbId, JET_bitDbSingleExclusive);
+ if ( JetError == JET_errDatabaseDuplicate) {
+#ifndef RPL_DEBUG
+ RplPrintf0( RPLI_CVT_OldSystemMdb);
+#endif
+ Error = MapJetError( Error);
+#ifdef RPL_DEBUG
+ if (RplsvcMdbExists) {
+ CallJ( JetAttachDatabase( SesId, G_ServiceDatabaseA, 0));
+ CallJ( JetOpenDatabase( SesId, G_ServiceDatabaseA, NULL, &DbId, JET_bitDbExclusive));
+ BootListTable();
+ ConfigListTable();
+ ProfileListTable();
+ WkstaListTable();
+ }
+#endif
+ } else if ( JetError == JET_errSuccess) {
+ Error = VendorCreateTable();
+ if ( Error != ERROR_SUCCESS) {
+ goto cleanup;
+ }
+ Error = ProcessRplMap( SourceFilePath);
+ if ( Error != ERROR_SUCCESS) {
+ goto cleanup;
+ }
+ Error = ProcessRplmgrIni( SourceFilePath);
+ if ( Error != ERROR_SUCCESS) {
+ goto cleanup;
+ }
+ ConfigPruneTable();
+ ProfilePruneTable();
+ WkstaPruneTable();
+ } else {
+ RplAssert( TRUE, ("CreateDatabase: JetError=%d", JetError));
+ Error = MapJetError( JetError);
+ }
+
+cleanup:
+ DbCleanup();
+
+SkipCleanup:
+ if ( Error != NO_ERROR) {
+ RplPrintf0( RPLI_CVT_USAGE);
+ }
+ return( Error);
+}
+
diff --git a/private/net/svcdlls/rpl/convert/sources b/private/net/svcdlls/rpl/convert/sources
new file mode 100644
index 000000000..2c565a1ea
--- /dev/null
+++ b/private/net/svcdlls/rpl/convert/sources
@@ -0,0 +1,51 @@
+MAJORCOMP=net
+MINORCOMP=rplcnv
+
+TARGETPATH=obj
+TARGETNAME=rplcnv
+TARGETTYPE=PROGRAM
+NTTARGETFILE0=nlstxt.h msg00001.bin
+
+USE_CRTDLL=1
+
+TARGETLIBS= \
+ ..\lib\obj\*\rpllib.lib \
+ $(BASEDIR)\public\sdk\lib\*\netlib.lib \
+ $(BASEDIR)\public\sdk\lib\*\kernel32.lib \
+ $(BASEDIR)\public\sdk\lib\*\advapi32.lib \
+ $(BASEDIR)\public\sdk\lib\*\user32.lib \
+ $(BASEDIR)\public\sdk\lib\*\gdi32.lib \
+ $(BASEDIR)\public\sdk\lib\*\comdlg32.lib \
+ $(BASEDIR)\public\sdk\lib\*\samlib.lib \
+ $(BASEDIR)\public\sdk\lib\*\netapi32.lib \
+ $(BASEDIR)\public\sdk\lib\*\wsock32.lib \
+ $(BASEDIR)\public\sdk\lib\*\shell32.lib \
+ $(BASEDIR)\public\sdk\lib\*\jet500.lib \
+ $(BASEDIR)\public\sdk\lib\*\ntdll.lib
+
+!IFNDEF DISABLE_NET_UNICODE
+UNICODE=1
+NET_C_DEFINES=-DUNICODE
+!ENDIF
+
+INCLUDES=.;..\inc;..\..\..\inc;..\..\..\api;..\..\..\..\inc;
+WARNING_LEVEL=-W4
+
+MSC_WARNING_LEVEL=/W3 /WX
+
+SOURCES= \
+ rplmain.c \
+ vendor.c \
+ adapter.c \
+ boot.c \
+ config.c \
+ profile.c \
+ wksta.c \
+ library.c \
+ debug.c \
+ convert.rc
+
+
+C_DEFINES= -DINCL_32= -DNT -DRPC_NO_WINDOWS_H -DWIN32 -DRPL_RPLCNV
+
+UMTYPE=console
diff --git a/private/net/svcdlls/rpl/convert/vendor.c b/private/net/svcdlls/rpl/convert/vendor.c
new file mode 100644
index 000000000..7e83f4491
--- /dev/null
+++ b/private/net/svcdlls/rpl/convert/vendor.c
@@ -0,0 +1,139 @@
+/*++
+
+Copyright (c) 1987-1994 Microsoft Corporation
+
+Module Name:
+
+ vendor.c
+
+Abstract:
+
+ Creates boot block table (server record table). Parses old style
+ RPL.MAP server records and creates corresponding entries in jet
+ database table.
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 16 - March - 1994
+
+Revision History:
+
+--*/
+
+#include "local.h"
+#define RPLVENDOR_ALLOCATE
+#include "vendor.h"
+#undef RPLVENDOR_ALLOCATE
+
+RPL_VENDOR_INFO_0 VendorInfoTable[]
+ = {
+ { L"00001B", L"Novell (NE1000, NE2000)"},
+ { L"00004B", L"Nokia/ICL (EtherTeam 16)"},
+ { L"000062", L"3Com 3Station"},
+ { L"0000C0", L"Western Digital/SMC (Ethernet)"},
+ { L"0000F6", L"Madge Smart Ringnodes"},
+ { L"0020AF", L"3Com (Elnk II, Elnk III, Tokenlink III)"},
+ { L"004033", L"NE2000-compatible"},
+ { L"004095", L"NE2000-compatible"},
+ { L"00608C", L"3Com (Elnk 16, Elnk II, Elnk/MC, Elnk III)"},
+ { L"00AA00", L"Intel (EtherExpress 16, EtherExpress PRO)"},
+ { L"020701", L"Racal Interlan (NI6510, NI5210)"},
+ { L"02608C", L"3Com (3Station, Elnk, Elnk II, Elnk Plus, Elnk/MC)"},
+ { L"080009", L"HP (EtherTwist, AM2100)"},
+ { L"08005A", L"IBM (Token Ring, Ethernet)"},
+ { L"10005A", L"IBM (Token Ring, Ethernet)"},
+ { L"42608C", L"3Com (Tokenlink)"}
+};
+#define VENDOR_INFO_TABLE_LENGTH (sizeof(VendorInfoTable)/sizeof(VendorInfoTable[0]))
+
+
+VOID ProcessVendor(
+ IN PWCHAR VendorName,
+ IN PWCHAR VendorComment
+ )
+{
+ DWORD Flags;
+
+ if ( !ValidHexName( VendorName, RPL_VENDOR_NAME_LENGTH, TRUE)) {
+ RplAssert( TRUE, ("Invalid VendorName = %ws", VendorName));
+ return;
+ }
+
+ Call( JetPrepareUpdate( SesId, VendorTableId, JET_prepInsert));
+
+ Call( JetSetColumn( SesId, VendorTableId,
+ VendorTable[ VENDOR_VendorName].ColumnId, VendorName,
+ (wcslen( VendorName) + 1) * sizeof(WCHAR), 0, NULL));
+
+ Call( JetSetColumn( SesId, VendorTableId,
+ VendorTable[ VENDOR_VendorComment].ColumnId, VendorComment,
+ (wcslen( VendorComment) + 1) * sizeof(WCHAR), 0, NULL));
+
+ Flags = 0;
+ Call( JetSetColumn( SesId, VendorTableId,
+ VendorTable[ VENDOR_Flags].ColumnId, &Flags,
+ sizeof( Flags), 0, NULL));
+
+ Call( JetUpdate( SesId, VendorTableId, NULL, 0, NULL));
+}
+
+
+
+DWORD VendorCreateTable( VOID)
+{
+ JET_COLUMNDEF ColumnDef;
+ JET_ERR JetError;
+ DWORD index;
+ DWORD Offset;
+ CHAR IndexKey[ 255];
+
+ JetError = JetCreateTable( SesId, DbId, VENDOR_TABLE_NAME,
+ VENDOR_TABLE_PAGE_COUNT, VENDOR_TABLE_DENSITY, &VendorTableId);
+ if ( JetError != JET_errSuccess) {
+ RplAssert( TRUE, ("CreateTable failed err=%d", JetError));
+ return( MapJetError( JetError));
+ }
+
+ //
+ // Create columns. First initalize fields that do not change between
+ // addition of columns.
+ //
+ ColumnDef.cbStruct = sizeof(ColumnDef);
+ ColumnDef.columnid = 0;
+ ColumnDef.wCountry = 1;
+ ColumnDef.langid = 0x0409; // USA english
+ ColumnDef.cp = 1200; // UNICODE codepage
+ ColumnDef.wCollate = 0;
+ ColumnDef.cbMax = 0;
+ ColumnDef.grbit = 0; // variable length binary and text data.
+
+ for ( index = 0; index < VENDOR_TABLE_LENGTH; index++ ) {
+
+ ColumnDef.coltyp = VendorTable[ index].ColumnType;
+
+ JetError = JetAddColumn( SesId, VendorTableId,
+ VendorTable[ index].ColumnName, &ColumnDef,
+ NULL, 0, &VendorTable[ index].ColumnId);
+ if ( JetError != JET_errSuccess) {
+ RplAssert( TRUE, ("AddColumn( %s) failed err=%d", VendorTable[ index].ColumnName, JetError));
+ return( MapJetError( JetError));
+ }
+ }
+
+ Offset = AddKey( IndexKey, '+', VendorTable[ VENDOR_VendorName].ColumnName);
+ IndexKey[ Offset++] = '\0';
+ JetError = JetCreateIndex( SesId, VendorTableId, VENDOR_INDEX_VendorName,
+ JET_bitIndexPrimary, IndexKey, Offset, 50);
+ if ( JetError != JET_errSuccess) {
+ RplAssert( TRUE, ("CreateIndex failed err=%d", JetError));
+ return( MapJetError( JetError));
+ }
+
+ for ( index = 0; index < VENDOR_INFO_TABLE_LENGTH; index++) {
+ ProcessVendor( VendorInfoTable[ index].VendorName,
+ VendorInfoTable[ index].VendorComment);
+ }
+
+ return( ERROR_SUCCESS);
+}
+
diff --git a/private/net/svcdlls/rpl/convert/wksta.c b/private/net/svcdlls/rpl/convert/wksta.c
new file mode 100644
index 000000000..96e45683a
--- /dev/null
+++ b/private/net/svcdlls/rpl/convert/wksta.c
@@ -0,0 +1,565 @@
+/*++
+
+Copyright (c) 1987-1993 Microsoft Corporation
+
+Module Name:
+
+ wksta.c
+
+Abstract:
+
+ Creates wksta table. Parses old style RPL.MAP unique adapter id
+ workstation records and creates corresponding entries in jet
+ database table.
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 19 - November - 1993
+
+Revision History:
+
+--*/
+
+#include "local.h"
+#include "winsock.h" // for inet_addr()
+#define RPLWKSTA_ALLOCATE
+#include "wksta.h"
+#undef RPLWKSTA_ALLOCATE
+
+
+DWORD WkstaCreateTable( VOID)
+{
+ JET_COLUMNDEF ColumnDef;
+ JET_ERR JetError;
+ DWORD index;
+ DWORD Offset;
+ CHAR IndexKey[ 255];
+
+ JetError = JetCreateTable( SesId, DbId, WKSTA_TABLE_NAME,
+ WKSTA_TABLE_PAGE_COUNT, WKSTA_TABLE_DENSITY, &WkstaTableId);
+
+ //
+ // Create columns. First initalize fields that do not change between
+ // addition of columns.
+ //
+ ColumnDef.cbStruct = sizeof(ColumnDef);
+ ColumnDef.columnid = 0;
+ ColumnDef.wCountry = 1;
+ ColumnDef.langid = 0x0409; // USA english
+ ColumnDef.cp = 1200; // UNICODE codepage
+ ColumnDef.wCollate = 0;
+ ColumnDef.cbMax = 0;
+ ColumnDef.grbit = 0; // variable length binary and text data.
+
+ for ( index = 0; index < WKSTA_TABLE_LENGTH; index++) {
+
+ ColumnDef.coltyp = WkstaTable[ index].ColumnType;
+
+ CallM( JetAddColumn( SesId, WkstaTableId,
+ WkstaTable[ index].ColumnName, &ColumnDef,
+ NULL, 0, &WkstaTable[ index].ColumnId));
+ }
+
+ //
+ // For now, the only reason we define these indices is to make sure
+ // wksta records have different AdapterName-s and different WkstaName-s.
+ // BUGBUG We could perhaps do this for TCP/IP address field as well.
+ //
+ Offset = AddKey( IndexKey, '+', WkstaTable[ WKSTA_AdapterName].ColumnName);
+ IndexKey[ Offset++] = '\0';
+ JetError = JetCreateIndex( SesId, WkstaTableId, WKSTA_INDEX_AdapterName,
+ JET_bitIndexPrimary, IndexKey, Offset, 50);
+ if ( JetError != JET_errSuccess) {
+ RplAssert( TRUE, ("CreateIndex failed err=%d", JetError));
+ return( MapJetError( JetError));
+ }
+
+ Offset = AddKey( IndexKey, '+', WkstaTable[ WKSTA_WkstaName].ColumnName);
+ IndexKey[ Offset++] = '\0';
+ JetError = JetCreateIndex( SesId, WkstaTableId, WKSTA_INDEX_WkstaName,
+ JET_bitIndexUnique, IndexKey, Offset, 50);
+ if ( JetError != JET_errSuccess) {
+ RplAssert( TRUE, ("CreateIndex failed err=%d", JetError));
+ return( MapJetError( JetError));
+ }
+
+ //
+ // +ProfileName+WkstaName index is used to enumerate all wkstas attached
+ // to a given profile.
+ //
+ Offset = AddKey( IndexKey, '+', WkstaTable[ WKSTA_ProfileName].ColumnName);
+ Offset += AddKey( IndexKey + Offset, '+', WkstaTable[ WKSTA_WkstaName].ColumnName);
+ IndexKey[ Offset++] = '\0';
+ JetError = JetCreateIndex( SesId, WkstaTableId, WKSTA_INDEX_ProfileNameWkstaName,
+ JET_bitIndexUnique, IndexKey, Offset, 50);
+ if ( JetError != JET_errSuccess) {
+ RplAssert( TRUE, ("CreateIndex failed err=%d", JetError));
+ return( MapJetError( JetError));
+ }
+
+ //
+ // +BootName+WkstaName index is used to find if a there is a
+ // wksta record for a given boot record.
+ //
+ Offset = AddKey( IndexKey, '+', WkstaTable[ WKSTA_BootName].ColumnName);
+ Offset += AddKey( IndexKey + Offset, '+', WkstaTable[ WKSTA_WkstaName].ColumnName);
+ IndexKey[ Offset++] = '\0';
+ JetError = JetCreateIndex( SesId, WkstaTableId, WKSTA_INDEX_BootNameWkstaName,
+ JET_bitIndexUnique, IndexKey, Offset, 50);
+ if ( JetError != JET_errSuccess) {
+ RplAssert( TRUE, ("CreateIndex failed err=%d", JetError));
+ return( MapJetError( JetError));
+ }
+
+ return( ERROR_SUCCESS);
+}
+
+
+DWORD TcpIpAddressToDword( IN PWCHAR UnicodeString)
+{
+ CHAR DbcsString[ 20];
+ DWORD ByteCount;
+
+ if ( *UnicodeString == 0) {
+ //
+ // WideCharToMultiByte() would convert this string successfully,
+ // returning ByteCount equal to 1 and DbcsString[ 0] equal to 0.
+ // And inet_addr() would then return 0, a valid TCP/IP address.
+ //
+ return( INADDR_NONE);
+ }
+ ByteCount = WideCharToMultiByte( // counts the terminating null byte
+ CP_OEMCP, // RonaldM confirms inet_addr() wants OEM string
+ 0,
+ UnicodeString,
+ -1,
+ DbcsString, // dbcs string
+ sizeof( DbcsString),
+ NULL, // no default character
+ NULL // no default character flag
+ );
+ if ( ByteCount == 0) {
+ return( INADDR_NONE); // failed to convert to DBCS
+ }
+ //
+ // Convert the string to network byte order, then to host byte order.
+ //
+ return( (DWORD)ntohl( inet_addr( DbcsString)));
+}
+
+
+VOID ProcessWksta( PWCHAR * Fields)
+/*++
+ We need to get from here whether wksta record is PERSONAL or SHARED.
+--*/
+{
+//
+// Wksta record (computer record) defines.
+//
+#define WKSTA_DISABLED_CH L'D'
+#define WKSTA_ENABLED_CH L'R'
+
+//
+// WKSTA_SERVER_INDEX is an index of a boot block record identifier in a string
+// table corresponding to a wksta record. This index is 0-based. Note
+// that wksta record is parsed so that field ",,," is counted as three
+// entries not as one.
+//
+#define WKSTA_AdapterName_INDEX 0 // wksta adapter id
+#define WKSTA_WKSTANAME_INDEX 1 // wksta name
+#define WKSTA_LOGONINPUT_INDEX 2 // username/password prompting policy
+#define WKSTA_FITFILE_INDEX 3 // fit file name
+#define WKSTA_SHARING_INDEX 5 // shared or personal profile
+#define WKSTA_BOOTNAME_INDEX 13 // boot block identifier
+#define WKSTA_PROFILENAME_INDEX 15 // profile name
+#define WKSTA_COMMENT_INDEX 16 // wksta comment
+#define WKSTA_TCPIPADDRESS_INDEX 17 // wksta tcpip address
+#define WKSTA_TCPIPSUBNET_INDEX 18 // wksta subnet mask
+#define WKSTA_TCPIPGATEWAY_INDEX 19 // wksta tcpip gateway address
+
+ PWCHAR WkstaComment;
+ PWCHAR WkstaName;
+ PWCHAR AdapterName;
+ PWCHAR ProfileName;
+ DWORD TcpIpAddress;
+ DWORD TcpIpSubnet;
+ DWORD TcpIpGateway;
+ PWCHAR Fit;
+ PWCHAR BootName;
+ JET_ERR JetError;
+ DWORD Flags;
+
+ Flags = 0;
+
+ WkstaName = Fields[ WKSTA_WKSTANAME_INDEX];
+ if ( !ValidName( WkstaName, RPL_MAX_WKSTA_NAME_LENGTH, TRUE)) {
+ RplPrintf1( RPLI_CVT_WkstaInvalid, WkstaName);
+ RplAssert( TRUE, ("Bad wksta name"));
+ return;
+ }
+ _wcsupr( WkstaName);
+
+ AdapterName = Fields[ WKSTA_AdapterName_INDEX];
+ if ( !ValidHexName( AdapterName, RPL_ADAPTER_NAME_LENGTH, TRUE)) {
+ RplPrintf2( RPLI_CVT_WkstaInvalidAdapter, WkstaName, AdapterName);
+ RplAssert( TRUE, ("Bad adapter id"));
+ return;
+ }
+ _wcsupr( AdapterName);
+
+ if ( Fields[ WKSTA_LOGONINPUT_INDEX] [1] != 0) {
+ RplPrintf2( RPLI_CVT_WkstaInvalidLogon, WkstaName, Fields[ WKSTA_LOGONINPUT_INDEX]);
+ RplAssert( TRUE, ("Bad username/password prompting field."));
+ return;
+ }
+ switch( Fields[ WKSTA_LOGONINPUT_INDEX] [0]) {
+ case WKSTA_LOGON_INPUT_REQUIRED:
+ Flags |= WKSTA_FLAGS_LOGON_INPUT_REQUIRED;
+ break;
+ case WKSTA_LOGON_INPUT_OPTIONAL:
+ Flags |= WKSTA_FLAGS_LOGON_INPUT_OPTIONAL;
+ break;
+ case WKSTA_LOGON_INPUT_IMPOSSIBLE:
+ Flags |= WKSTA_FLAGS_LOGON_INPUT_IMPOSSIBLE;
+ break;
+ default:
+ RplPrintf2( RPLI_CVT_WkstaInvalidLogon, WkstaName, Fields[ WKSTA_LOGONINPUT_INDEX]);
+ RplAssert( TRUE, ("Bad username/password prompting field."));
+ return;
+ break;
+ }
+
+ Fit = AddFileExtension( Fields[ WKSTA_FITFILE_INDEX], L".FIT", TRUE);
+ if ( !ValidName( Fit, RPL_MAX_STRING_LENGTH, TRUE)) {
+ RplPrintf2( RPLI_CVT_WkstaInvalidFit, WkstaName, Fields[ WKSTA_FITFILE_INDEX]);
+ RplAssert( TRUE, ("Bad fit file field %ws.", Fields[ WKSTA_FITFILE_INDEX]));
+ return;
+ }
+
+ //
+ // Find if wksta record is enabled or disabled.
+ //
+ switch( Fields[ WKSTA_SHARING_INDEX] [0]) {
+ case WKSTA_SHARING_TRUE:
+ Flags |= WKSTA_FLAGS_SHARING_TRUE;
+ break;
+ case WKSTA_SHARING_FALSE:
+ Flags |= WKSTA_FLAGS_SHARING_FALSE;
+ break;
+ default:
+ RplPrintf2( RPLI_CVT_WkstaInvalidSharing, WkstaName, Fields[ WKSTA_SHARING_INDEX]);
+ RplAssert( TRUE, ("Data sharing not properly set."));
+ return;
+ break;
+ }
+
+ //
+ // Find if wksta record is enabled or disabled.
+ //
+ switch( *Fields[ WKSTA_BOOTNAME_INDEX]) {
+ case WKSTA_ENABLED_CH:
+ break;
+ case WKSTA_DISABLED_CH:
+ RplPrintf2( RPLI_CVT_WkstaDisabledBoot, WkstaName, Fields[ WKSTA_BOOTNAME_INDEX]);
+ return;
+ break;
+ default:
+ RplPrintf2( RPLI_CVT_WkstaInvalidBoot, WkstaName, Fields[ WKSTA_BOOTNAME_INDEX]);
+ RplAssert( TRUE, ("Bad switch"));
+ return;
+ break;
+ }
+
+ BootName = Fields[ WKSTA_BOOTNAME_INDEX] + 1; // skip leading char
+ if ( !ValidName( BootName, RPL_MAX_STRING_LENGTH, TRUE)) {
+ RplPrintf2( RPLI_CVT_WkstaInvalidBoot, WkstaName, Fields[ WKSTA_BOOTNAME_INDEX]);
+ RplAssert( TRUE, ("Bad boot name"));
+ return;
+ }
+
+ ProfileName = Fields[ WKSTA_PROFILENAME_INDEX];
+ if ( !ValidName( ProfileName, RPL_MAX_PROFILE_NAME_LENGTH, TRUE)) {
+ RplPrintf2( RPLI_CVT_WkstaInvalidProfile, WkstaName, ProfileName);
+ RplAssert( TRUE, ("Bad profile name"));
+ return;
+ }
+ _wcsupr( ProfileName);
+
+ WkstaComment = Fields[ WKSTA_COMMENT_INDEX];
+ if ( RPL_STRING_TOO_LONG( WkstaComment)) {
+ WkstaComment[ RPL_MAX_STRING_LENGTH] = 0; // silently truncate it
+ }
+
+ TcpIpAddress = TcpIpAddressToDword( Fields[ WKSTA_TCPIPADDRESS_INDEX]);
+ TcpIpSubnet = TcpIpAddressToDword( Fields[ WKSTA_TCPIPSUBNET_INDEX]);
+ TcpIpGateway = TcpIpAddressToDword( Fields[ WKSTA_TCPIPGATEWAY_INDEX]);
+
+ //
+ // If all addresses are valid assumes this (old style) client does not
+ // want DHCP to be enabled. In other words, if any addresses is bogus
+ // this client does not loose anything by trying out DHCP.
+ //
+ if ( TcpIpAddress != INADDR_NONE && TcpIpSubnet != INADDR_NONE
+ && TcpIpGateway != -1) {
+ Flags |= WKSTA_FLAGS_DHCP_FALSE;
+ } else {
+ Flags |= WKSTA_FLAGS_DHCP_TRUE;
+ }
+
+ //
+ // Play it safe; assume user accounts are not to be deleted
+ //
+ Flags |= WKSTA_FLAGS_DELETE_FALSE;
+
+ Call( JetPrepareUpdate( SesId, WkstaTableId, JET_prepInsert));
+
+ Call( JetSetColumn( SesId, WkstaTableId,
+ WkstaTable[ WKSTA_WkstaName].ColumnId, WkstaName,
+ (wcslen( WkstaName) + 1) * sizeof(WCHAR), 0, NULL));
+
+ Call( JetSetColumn( SesId, WkstaTableId,
+ WkstaTable[ WKSTA_WkstaComment].ColumnId, WkstaComment,
+ (wcslen( WkstaComment) + 1) * sizeof(WCHAR), 0, NULL));
+
+ Call( JetSetColumn( SesId, WkstaTableId,
+ WkstaTable[ WKSTA_ProfileName].ColumnId, ProfileName,
+ (wcslen( ProfileName) + 1) * sizeof(WCHAR), 0, NULL));
+
+ Call( JetSetColumn( SesId, WkstaTableId,
+ WkstaTable[ WKSTA_BootName].ColumnId, BootName,
+ (wcslen( BootName) + 1) * sizeof(WCHAR), 0, NULL));
+
+ Call( JetSetColumn( SesId, WkstaTableId,
+ WkstaTable[ WKSTA_FitFile].ColumnId, Fit,
+ (wcslen( Fit) + 1) * sizeof(WCHAR), 0, NULL));
+
+ Call( JetSetColumn( SesId, WkstaTableId,
+ WkstaTable[ WKSTA_AdapterName].ColumnId, AdapterName,
+ (wcslen( AdapterName) + 1) * sizeof(WCHAR), 0, NULL));
+
+ Call( JetSetColumn( SesId, WkstaTableId,
+ WkstaTable[ WKSTA_TcpIpAddress].ColumnId, &TcpIpAddress,
+ sizeof( TcpIpAddress), 0, NULL));
+
+ Call( JetSetColumn( SesId, WkstaTableId,
+ WkstaTable[ WKSTA_TcpIpSubnet].ColumnId, &TcpIpSubnet,
+ sizeof( TcpIpSubnet), 0, NULL));
+
+ Call( JetSetColumn( SesId, WkstaTableId,
+ WkstaTable[ WKSTA_TcpIpGateway].ColumnId, &TcpIpGateway,
+ sizeof( TcpIpGateway), 0, NULL));
+
+ Call( JetSetColumn( SesId, WkstaTableId,
+ WkstaTable[ WKSTA_Flags].ColumnId, &Flags,
+ sizeof( Flags), 0, NULL));
+
+ JetError = JetUpdate( SesId, WkstaTableId, NULL, 0, NULL);
+ if ( JetError == JET_errKeyDuplicate) {
+ RplPrintf2( RPLI_CVT_WkstaDuplicateName, WkstaName, AdapterName);
+ } else if (JetError != JET_errSuccess) {
+ RplAssert( TRUE,("JetUpdate failed error = %d", JetError));
+ }
+}
+
+
+VOID WkstaPruneTable( VOID)
+/*++
+ Eliminate wksta records that do not have a corresponding profile record
+ defined or do not have a corresponding server record defined.
+--*/
+{
+
+ WCHAR Name[ 20]; // BUGBUG arbitrary size
+ DWORD NameSize;
+ JET_ERR ForJetError;
+ JET_ERR JetError;
+
+ for ( ForJetError = JetMove( SesId, WkstaTableId, JET_MoveFirst, 0);
+ ForJetError == JET_errSuccess;
+ ForJetError = JetMove( SesId, WkstaTableId, JET_MoveNext, 0)) {
+
+ JetError = JetRetrieveColumn( SesId, WkstaTableId,
+ WkstaTable[ WKSTA_BootName].ColumnId, Name,
+ sizeof( Name), &NameSize, 0, NULL);
+ if ( JetError != JET_errSuccess) {
+ RplAssert( TRUE, ("RetriveColumn failed err=%d", JetError));
+ Call( JetDelete( SesId, WkstaTableId));
+ continue;
+ }
+ if ( !FindBoot( Name)) {
+ RplAssert( TRUE, ("FindBoot failed."));
+ Call( JetDelete( SesId, WkstaTableId));
+ continue;
+ }
+
+ JetError = JetRetrieveColumn( SesId, WkstaTableId,
+ WkstaTable[ WKSTA_ProfileName].ColumnId, Name,
+ sizeof( Name), &NameSize, 0, NULL);
+ if ( JetError != JET_errSuccess) {
+ RplAssert( TRUE, ("RetriveColumn failed err=%d", JetError));
+ Call( JetDelete( SesId, WkstaTableId));
+ continue;
+ }
+ //
+ // This will eliminate workstations joined to the DEFAULT profile
+ // since the DEFAULT profile does not have its profile record in
+ // RPL.MAP. Note that default boot is not supported under NT.
+ // Instead of just deleting these workstations, we could also
+ // add the corresponding AdapterName record to the AdapterName table.
+ // This may not be worth the effort though.
+ //
+ if ( !FindProfile( Name)) {
+ if ( _wcsicmp( Name, L"DEFAULT") == 0) {
+ JetError = JetRetrieveColumn( SesId, WkstaTableId,
+ WkstaTable[ WKSTA_WkstaName].ColumnId, Name,
+ sizeof( Name), &NameSize, 0, NULL);
+ if ( JetError != JET_errSuccess) {
+ RplAssert( TRUE, ("RetriveColumn failed err=%d", JetError));
+ } else {
+ RplPrintf1( RPLI_CVT_WkstaDefaultProfile, Name);
+ }
+ } else {
+ RplAssert( TRUE, ("FindProfile failed."));
+ }
+ Call( JetDelete( SesId, WkstaTableId));
+ continue;
+ }
+ }
+
+ //
+ // The error below is the only expected error (end of table error).
+ //
+ if ( ForJetError != JET_errNoCurrentRecord) {
+ RplAssert( TRUE, ("ForJetError = %d", ForJetError));
+ }
+}
+
+
+VOID WkstaListTable( VOID)
+{
+ ListTable( WKSTA_TABLE_NAME, WkstaTable[ WKSTA_AdapterName].ColumnName,
+ WKSTA_INDEX_WkstaName);
+}
+
+
+
+BOOL RplDbInitTable( IN PCHAR TableName, IN OUT PRPL_COLUMN_INFO Table,
+ IN DWORD TableLength, IN OUT JET_TABLEID * pTableId)
+{
+ JET_COLUMNDEF ColumnDef;
+ DWORD index;
+
+ CallB( JetOpenTable( SesId, DbId, TableName, NULL, 0,
+ JET_bitTableDenyWrite, pTableId));
+
+ for ( index = 0; index < TableLength; index++) {
+ CallB( JetGetTableColumnInfo( SesId, *pTableId, Table[ index].ColumnName,
+ &ColumnDef, sizeof( ColumnDef), JET_ColInfo));
+ Table[ index].ColumnId = ColumnDef.columnid;
+ Table[ index].ColumnType = ColumnDef.coltyp;
+ }
+ return( TRUE);
+}
+
+
+BOOL FindWksta( IN LPWSTR AdapterName)
+/*++
+ Return TRUE if it finds wksta record for input AdapterName.
+ Returns FALSE otherwise.
+
+ BUGBUG This code is inefficient. I should really make AdapterName a
+ jet currency data, but even now it is kind of stupid taking wcslen of
+ AdapterName since it is a fixed length string.
+--*/
+{
+ JET_ERR JetError;
+
+ JetError = JetSetCurrentIndex( SesId, WkstaTableId, WKSTA_INDEX_AdapterName);
+ if ( JetError != JET_errSuccess) {
+ RplAssert( TRUE, ("SetCurrentIndex failed err=%d", JetError));
+ return( FALSE);
+ }
+ JetError = JetMakeKey( SesId, WkstaTableId, AdapterName,
+ ( wcslen( AdapterName) + 1) * sizeof(WCHAR), JET_bitNewKey);
+ if ( JetError != JET_errSuccess) {
+ RplAssert( TRUE, ("MakeKey failed err=%d", JetError));
+ return( FALSE);
+ }
+ JetError = JetSeek( SesId, WkstaTableId, JET_bitSeekEQ);
+ if ( JetError != JET_errSuccess) {
+ if ( JetError == JET_errRecordNotFound) {
+ //
+ // This is an expected error, do not break for this.
+ //
+ RplAssert( TRUE, ( "FindWksta( %ws) failed", AdapterName));
+ } else {
+ RplAssert( TRUE, ("JetSeek failed err=%d", JetError));
+ }
+ return( FALSE);
+ }
+ return( TRUE);
+}
+
+#ifdef NOT_YET
+
+VOID Display( IN DWORD index)
+{
+ BYTE Buffer[ 120];
+ DWORD DataSize;
+ DWORD AddressDword;
+
+ Call( JetRetrieveColumn( SesId, WkstaTableId,
+ WkstaTable[ index].ColumnId, Buffer,
+ sizeof( Buffer), &DataSize, 0, NULL));
+
+ RplDbgPrint(( "%s = ", WkstaTable[ index].ColumnName));
+
+ switch( index) {
+ case WKSTA_WkstaName:
+ case WKSTA_WkstaComment:
+ case WKSTA_ProfileName:
+ case WKSTA_FitFile:
+ case WKSTA_BootName:
+ RplDbgPrint(( "%ws\n", Buffer));
+ break;
+ case WKSTA_TcpIpAddress:
+ case WKSTA_TcpIpSubnet:
+ case WKSTA_TcpIpGateway:
+ AddressDword = *(PDWORD)Buffer;
+ FillTcpIpString( Buffer, AddressDword);
+ RplDbgPrint(( "%s\n", Buffer));
+ break;
+ case WKSTA_Flags:
+ RplDbgPrint(( "0x%x\n", *(PDWORD)Buffer));
+ break;
+ }
+}
+
+
+BOOL ListWkstaInfo( IN PWCHAR AdapterName)
+/*++
+ Returns TRUE if it can find all the information needed to boot the client.
+ Returns FALSE otherwise.
+--*/
+{
+ if ( !RplDbInitTable( WKSTA_TABLE_NAME, WkstaTable, WKSTA_TABLE_LENGTH,
+ &WkstaTableId)) {
+ return( FALSE);
+ }
+ if ( !FindWksta( AdapterName)) {
+ RplAssert( TRUE, ("FindWksta( %ws) failed", AdapterName));
+ return( FALSE);
+ }
+ Display( WKSTA_WkstaName);
+ Display( WKSTA_WkstaComment);
+ Display( WKSTA_Flags);
+ Display( WKSTA_ProfileName);
+ Display( WKSTA_FitFile);
+ Display( WKSTA_BootName);
+ Display( WKSTA_TcpIpAddress);
+ Display( WKSTA_TcpIpSubnet);
+ Display( WKSTA_TcpIpGateway);
+ return( TRUE);
+}
+#endif
+
diff --git a/private/net/svcdlls/rpl/dirs b/private/net/svcdlls/rpl/dirs
new file mode 100644
index 000000000..4d6610aed
--- /dev/null
+++ b/private/net/svcdlls/rpl/dirs
@@ -0,0 +1,33 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ dirs.
+
+Abstract:
+
+ This file specifies the subdirectories of the current directory that
+ contain component makefiles.
+
+
+Author:
+
+ Steve Wood (stevewo) 17-Apr-1990
+
+NOTE: Commented description of this file is in \nt\bak\bin\dirs.tpl
+
+!ENDIF
+
+
+DIRS= \
+ client \
+ lib \
+ dll \
+ server \
+ convert \
+ command
+
+
+OPTIONAL_DIRS=rplinst
diff --git a/private/net/svcdlls/rpl/dll/buffer.c b/private/net/svcdlls/rpl/dll/buffer.c
new file mode 100644
index 000000000..0d7336dcb
--- /dev/null
+++ b/private/net/svcdlls/rpl/dll/buffer.c
@@ -0,0 +1,124 @@
+/*++
+
+Copyright (c) 1987-93 Microsoft Corporation
+
+Module Name:
+
+ buffer.c (renamed from rpl2_bfr.c)
+
+Abstract:
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 03 - February - 1993
+
+Revision History:
+
+ 03-Feb-1993 vladimv
+ Ported to NT
+
+--*/
+
+#include "local.h"
+
+
+BOOL RplDlcBufferFree(
+ BYTE free_type,
+ PADAPTER_INFO pAdapterInfo
+ )
+/*++
+
+Routine Description:
+ Frees the DLC buffer. Can be called from more than one thread.
+
+Arguments:
+ free_type - do we free SFR or FIND receive buffer
+ pAdapterInfo - pointer to adapter specific info
+
+Return Value:
+ TRUE if success, FALSE otherwise.
+--*/
+
+{
+ DWORD status;
+ PLLC_CCB pBadCcb; // pointer for AcsLan
+ PLLC_CCB pCcb;
+ PLLC_BUFFER_FREE_PARMS pBufferFreeParms;
+ HANDLE Event;
+ WORD usStationId;
+ PLLC_BUFFER pBuffer;
+
+ if (free_type == FIND_FREE) {
+ pCcb= &pAdapterInfo->find_bf_ccb;
+ pBufferFreeParms = &pAdapterInfo->find_bf_ccbpt;
+ pBuffer = pAdapterInfo->u1.r1.pFirstBuffer;
+ usStationId = pAdapterInfo->u1.r1.usStationId;
+ Event = pAdapterInfo->findsem;
+ } else {
+ pCcb= &pAdapterInfo->sfr_bf_ccb;
+ pBufferFreeParms = &pAdapterInfo->sfr_bf_ccbpt;
+ pBuffer = pAdapterInfo->u3.r1.Type.Event.pReceivedFrame;
+ usStationId = pAdapterInfo->u3.r1.usStationId;
+ Event = pAdapterInfo->getdata_sem;
+ }
+
+ //
+ // clear the blocks first
+ //
+ memset( (PVOID)pCcb, '\0', sizeof(LLC_CCB));
+ memset( (PVOID)pBufferFreeParms, '\0', sizeof(LLC_BUFFER_FREE_PARMS));
+
+ pCcb->uchAdapterNumber = pAdapterInfo->adapter_number;
+ pCcb->uchDlcCommand = LLC_BUFFER_FREE;
+ pCcb->hCompletionEvent = Event;
+ pCcb->u.pParameterTable = (PLLC_PARMS)pBufferFreeParms;
+
+ pBufferFreeParms->pFirstBuffer = (PLLC_XMIT_BUFFER)pBuffer;
+ if ( pBuffer == NULL) {
+ RplDump( ++RG_Assert, ("pBuffer=0x%x", pBuffer));
+ return( TRUE); // a workaround for free build until the bug is found BUGBUG
+ }
+
+ if ( ResetEvent( Event) == FALSE) {
+ if ( pAdapterInfo->Closing == TRUE) {
+ return( FALSE);
+ }
+ status = GetLastError();
+ RplDump( ++RG_Assert, ( "status=0x%x", status));
+ RplDlcReportEvent( status, SEM_SET);
+ return( FALSE);
+ }
+
+ status = AcsLan( pCcb, &pBadCcb);
+ if ( pAdapterInfo->Closing == TRUE) {
+ (VOID)SetEvent( Event);
+ return( FALSE);
+ }
+
+ if ( status != ACSLAN_STATUS_COMMAND_ACCEPTED) {
+ RplDump( ++RG_Assert, ( "status=0x%x", status));
+ RplDlcReportEvent( status, LLC_BUFFER_FREE);
+ (VOID)SetEvent( Event);
+ return( FALSE);
+ }
+
+ status = WaitForSingleObject( Event, 3000L);
+ if ( pAdapterInfo->Closing == TRUE) {
+ return( FALSE);
+ }
+
+ if ( (status != WAIT_OBJECT_0) || (pCcb->uchDlcStatus)) {
+ if (status != WAIT_OBJECT_0) {
+ if ( status == -1) {
+ status = GetLastError();
+ }
+ RplDlcReportEvent( status, SEM_WAIT);
+ } else {
+ RplDlcReportEvent( pCcb->uchDlcStatus, LLC_BUFFER_FREE);
+ }
+ RplDump( ++RG_Assert, ( "status=%d, pCcb=0x%x, DlcStatus=0x%x",
+ status, pCcb, pCcb->uchDlcStatus));
+ return( FALSE);
+ }
+ return( TRUE);
+}
diff --git a/private/net/svcdlls/rpl/dll/emulator.c b/private/net/svcdlls/rpl/dll/emulator.c
new file mode 100644
index 000000000..6f0110037
--- /dev/null
+++ b/private/net/svcdlls/rpl/dll/emulator.c
@@ -0,0 +1,110 @@
+/*++
+
+Copyright (c) 1987-93 Microsoft Corporation
+
+Module Name:
+
+ emulator.c (renamed from rplemul.c)
+
+Abstract:
+
+ Contains RPL emulator for ETHERSTART clients.
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 03 - February - 1993
+
+Revision History:
+
+ 03-Feb-1993 vladimv
+ Ported to NT
+ 08-Jun-1994 vladimv
+ Updated with lm2.2 fixes for 3Com Etherlink card.
+
+--*/
+
+#include "local.h"
+
+//
+// ripl_rom[] is the x86 code we send to ETHERSTART clients. The source
+// code is on \\deficit\lm\src\ripl\rpldll\rplemul.asm.
+//
+// BUGBUG We should really check in & compile the actual x86 assembly code
+// BUGBUG rather than collecting sniff traces of what MS Lan Manager server
+// BUGBUG sends to the client.
+//
+BYTE ripl_rom[] = {
+ 0xFA ,0xB8 ,0x90 ,0x00
+,0x8E ,0xD0 ,0xBC ,0x00 ,0x02 ,0xCD ,0x12 ,0xB1 ,0x06 ,0xD3 ,0xE0 ,0x8D ,0x0E ,0x53 ,0x0B ,0x83
+,0xC1 ,0x0F ,0x51 ,0xD1 ,0xE9 ,0xD1 ,0xE9 ,0xD1 ,0xE9 ,0xD1 ,0xE9 ,0x2B ,0xC1 ,0x59 ,0x8E ,0xC0
+,0x8D ,0x36 ,0x35 ,0x01 ,0x8B ,0xFE ,0x2B ,0xCE ,0xE8 ,0x56 ,0x02 ,0x50 ,0xB8 ,0x35 ,0x01 ,0x50
+,0xCB ,0x0E ,0x1F ,0xB4 ,0xFB ,0xB2 ,0x80 ,0x8D ,0x1E ,0x76 ,0x04 ,0xCD ,0x13 ,0x8B ,0xF3 ,0x8D
+,0x3E ,0xA1 ,0x04 ,0xA5 ,0xA5 ,0xA5 ,0x8B ,0xF3 ,0x8D ,0x3E ,0xDE ,0x04 ,0xA5 ,0xA5 ,0xA5 ,0x8B
+,0xF3 ,0x8D ,0x3E ,0x09 ,0x05 ,0xA5 ,0xA5 ,0xA5 ,0xB4 ,0x03 ,0x32 ,0xFF ,0xCD ,0x10 ,0x32 ,0xD2
+,0x01 ,0x16 ,0x04 ,0x04 ,0x01 ,0x16 ,0x39 ,0x04 ,0x01 ,0x16 ,0x6E ,0x04 ,0xBE ,0xDA ,0x03 ,0x8B
+,0x16 ,0x04 ,0x04 ,0xBB ,0x24 ,0x00 ,0x90 ,0xE8 ,0x26 ,0x02 ,0xA1 ,0x04 ,0x04 ,0x05 ,0x24 ,0x00
+,0x90 ,0xA3 ,0x04 ,0x04 ,0xBE ,0xFE ,0x03 ,0x8B ,0x16 ,0x04 ,0x04 ,0xBB ,0x06 ,0x00 ,0x90 ,0xE8
+,0x0E ,0x02 ,0xBE ,0x68 ,0x00 ,0x90 ,0x8D ,0x1E ,0x70 ,0x04 ,0xE8 ,0xF5 ,0x01 ,0xBE ,0xBE ,0x05
+,0x8D ,0x1E ,0x42 ,0x05 ,0xE8 ,0xF2 ,0x01 ,0x73 ,0x13 ,0x0B ,0xC0 ,0x74 ,0x03 ,0xE9 ,0x8D ,0x00
+,0xBE ,0xFE ,0x03 ,0xBB ,0x06 ,0x00 ,0x90 ,0xE8 ,0xFE ,0x01 ,0xEB ,0xC8 ,0x8D ,0x36 ,0x72 ,0x05
+,0x8D ,0x3E ,0xD8 ,0x04 ,0xA5 ,0xA5 ,0xA5 ,0xBE ,0x06 ,0x04 ,0x8B ,0x16 ,0x39 ,0x04 ,0xBB ,0x2F
+,0x00 ,0x90 ,0xE8 ,0xCB ,0x01 ,0xBE ,0x3B ,0x04 ,0x8B ,0x16 ,0x6E ,0x04 ,0xBB ,0x2D ,0x00 ,0x90
+,0xE8 ,0xBD ,0x01 ,0xA1 ,0x39 ,0x04 ,0x05 ,0x2F ,0x00 ,0x90 ,0xA3 ,0x39 ,0x04 ,0xA1 ,0x6E ,0x04
+,0x05 ,0x2D ,0x00 ,0x90 ,0xA3 ,0x6E ,0x04 ,0xB9 ,0x50 ,0x00 ,0xBE ,0x35 ,0x04 ,0x8B ,0x16 ,0x39
+,0x04 ,0xBB ,0x04 ,0x00 ,0x90 ,0xE8 ,0x98 ,0x01 ,0xBE ,0x68 ,0x00 ,0x90 ,0x8D ,0x1E ,0xD8 ,0x04
+,0xE8 ,0x7F ,0x01 ,0xBE ,0x68 ,0x04 ,0x8B ,0x16 ,0x6E ,0x04 ,0xBB ,0x06 ,0x00 ,0x90 ,0xE8 ,0x7F
+,0x01 ,0xBE ,0xBE ,0x05 ,0x8D ,0x1E ,0x8D ,0x05 ,0xE8 ,0x6E ,0x01 ,0x73 ,0x28 ,0x0B ,0xC0 ,0x75
+,0x0C ,0xBE ,0x35 ,0x04 ,0xBB ,0x04 ,0x00 ,0x90 ,0xE8 ,0x7D ,0x01 ,0xE2 ,0xBD ,0xE8 ,0xF6 ,0x00
+,0xB8 ,0x40 ,0x00 ,0x8E ,0xD8 ,0xC7 ,0x06 ,0x72 ,0x00 ,0x34 ,0x12 ,0xEA ,0x00 ,0x00 ,0xFF ,0xFF
+,0xB9 ,0x50 ,0x00 ,0xEB ,0xBE ,0x80 ,0x3E ,0x9B ,0x05 ,0xFC ,0x75 ,0xF4 ,0x81 ,0x3E ,0xA0 ,0x05
+,0x00 ,0x20 ,0x75 ,0xEC ,0xA1 ,0xF3 ,0x04 ,0x39 ,0x06 ,0xA8 ,0x05 ,0x74 ,0x02 ,0xEB ,0x88 ,0x8B
+,0x16 ,0xF1 ,0x04 ,0x39 ,0x16 ,0xA6 ,0x05 ,0x74 ,0x03 ,0xE9 ,0x7B ,0xFF ,0x86 ,0xC4 ,0x86 ,0xD6
+,0x40 ,0x83 ,0xD2 ,0x00 ,0x86 ,0xC4 ,0x86 ,0xD6 ,0xA3 ,0xF3 ,0x04 ,0x89 ,0x16 ,0xF1 ,0x04 ,0xBE
+,0x68 ,0x04 ,0xBB ,0x06 ,0x00 ,0x90 ,0xE8 ,0x1F ,0x01 ,0x8A ,0x1E ,0xB6 ,0x05 ,0xF6 ,0xC3 ,0x10
+,0x74 ,0x0D ,0x53 ,0xBE ,0x68 ,0x00 ,0x90 ,0x8D ,0x1E ,0xD8 ,0x04 ,0xE8 ,0xE4 ,0x00 ,0x5B ,0xF6
+,0xC3 ,0x20 ,0x74 ,0x10 ,0xA1 ,0xB0 ,0x05 ,0x86 ,0xE0 ,0xA3 ,0x4B ,0x0B ,0xA1 ,0xAE ,0x05 ,0x86
+,0xE0 ,0xA3 ,0x4D ,0x0B ,0x8B ,0x0E ,0xB7 ,0x05 ,0x86 ,0xCD ,0x83 ,0xE9 ,0x04 ,0x0B ,0xC9 ,0x74
+,0x37 ,0x33 ,0xD2 ,0xA1 ,0x4B ,0x0B ,0xBF ,0x10 ,0x00 ,0xF7 ,0xF7 ,0x8B ,0xFA ,0x03 ,0x06 ,0x4D
+,0x0B ,0x75 ,0x09 ,0x81 ,0xFA ,0x00 ,0x0C ,0x73 ,0x03 ,0xE9 ,0x51 ,0xFF ,0x03 ,0xD1 ,0x89 ,0x16
+,0x4B ,0x0B ,0xA3 ,0x4D ,0x0B ,0x3D ,0x00 ,0xA0 ,0x72 ,0x03 ,0xE9 ,0x40
+ ,0xFF ,0x06 ,0x8E ,0xC0
+,0x8D ,0x36 ,0xBB ,0x05 ,0xE8 ,0x7A ,0x00 ,0x07 ,0xF6 ,0xC3 ,0x80 ,0x75 ,0x03 ,0xE9 ,0x40 ,0xFF
+,0xA1 ,0x4F ,0x0B ,0x8B ,0x0E ,0x51 ,0x0B ,0xF6 ,0xC3 ,0x40 ,0x74 ,0x0B ,0xA1 ,0xB4 ,0x05 ,0x86
+,0xC4 ,0x8B ,0x0E ,0xB2 ,0x05 ,0x86 ,0xCD ,0x33 ,0xD2 ,0xBB ,0x10 ,0x00 ,0xF7 ,0xF3 ,0x03 ,0xC1
+,0x50 ,0x52 ,0xE8 ,0x01 ,0x00 ,0xCB ,0x06 ,0x1E ,0x33 ,0xC0 ,0x8E ,0xC0 ,0xB8 ,0x70 ,0x00 ,0x8E
+,0xD8 ,0xA1 ,0x02 ,0x00 ,0x0B ,0x06 ,0x04 ,0x00 ,0x75 ,0x12 ,0x26 ,0xA1 ,0x00 ,0x01 ,0x26 ,0xA3
+,0x4C ,0x00 ,0x26 ,0xA1 ,0x02 ,0x01 ,0x26 ,0xA3 ,0x4E ,0x00 ,0xEB ,0x0E ,0xA1 ,0x02 ,0x00 ,0x26
+,0xA3 ,0x4C ,0x00 ,0xA1 ,0x04 ,0x00 ,0x26 ,0xA3 ,0x4E ,0x00 ,0xA1 ,0x06 ,0x00 ,0x26 ,0xA3 ,0x64
+,0x00 ,0xA1 ,0x08 ,0x00 ,0x26 ,0xA3 ,0x66 ,0x00 ,0x26 ,0xC6 ,0x06 ,0xD0 ,0x04 ,0x01 ,0x1F ,0x07
+,0xC3 ,0xF7 ,0xC6 ,0x01 ,0x00 ,0x74 ,0x01 ,0xA4 ,0xD1 ,0xE9 ,0x9C ,0xF3 ,0xA5 ,0x9D ,0x73 ,0x01
+,0xA4 ,0xC3 ,0xB4 ,0xFE ,0xB2 ,0x80 ,0xCD ,0x13 ,0xC3 ,0xB4 ,0xFF ,0xB2 ,0x80 ,0xCD ,0x13 ,0xC3
+,0x50 ,0x51 ,0x53 ,0x8B ,0xCB ,0x32 ,0xFF ,0xB4 ,0x02 ,0xCD ,0x10 ,0xAC ,0x33 ,0xDB ,0xB4 ,0x0E
+,0xCD ,0x10 ,0xE2 ,0xF7 ,0x5B ,0x59 ,0x58 ,0xC3 ,0x50 ,0x51 ,0x8D ,0x70 ,0xFF ,0x8B ,0xCB ,0x8A
+,0x04 ,0x3C ,0x39 ,0x7C ,0x0A ,0xB0 ,0x30 ,0x88 ,0x04 ,0x4E ,0xE2 ,0xF3 ,0xEB ,0x05 ,0x90 ,0xFE
+,0xC0 ,0x88 ,0x04 ,0x59 ,0x58 ,0xC3 ,0x53 ,0x65 ,0x61 ,0x72 ,0x63 ,0x68 ,0x69 ,0x6E ,0x67 ,0x20
+,0x66 ,0x6F ,0x72 ,0x20 ,0x52 ,0x50 ,0x4C ,0x20 ,0x53 ,0x65 ,0x72 ,0x76 ,0x65 ,0x72 ,0x2C ,0x20
+,0x52 ,0x65 ,0x74 ,0x72 ,0x69 ,0x65 ,0x73 ,0x20 ,0x3D ,0x20 ,0x30 ,0x30 ,0x30 ,0x30 ,0x30 ,0x30
+,0x00 ,0x01 ,0x53 ,0x65 ,0x6E ,0x64 ,0x69 ,0x6E ,0x67 ,0x20 ,0x46 ,0x69 ,0x6C ,0x65 ,0x20 ,0x52
+,0x65 ,0x71 ,0x75 ,0x65 ,0x73 ,0x74 ,0x73 ,0x20 ,0x74 ,0x6F ,0x20 ,0x52 ,0x50 ,0x4C ,0x20 ,0x53
+,0x65 ,0x72 ,0x76 ,0x65 ,0x72 ,0x2C ,0x20 ,0x52 ,0x65 ,0x74 ,0x72 ,0x69 ,0x65 ,0x73 ,0x20 ,0x3D
+,0x20 ,0x30 ,0x30 ,0x30 ,0x30 ,0x00 ,0x03 ,0x57 ,0x61 ,0x69 ,0x74 ,0x69 ,0x6E ,0x67 ,0x20 ,0x66
+,0x6F ,0x72 ,0x20 ,0x44 ,0x61 ,0x74 ,0x61 ,0x20 ,0x46 ,0x72 ,0x61 ,0x6D ,0x65 ,0x20 ,0x77 ,0x69
+,0x74 ,0x68 ,0x20 ,0x53 ,0x65 ,0x71 ,0x75 ,0x65 ,0x6E ,0x63 ,0x65 ,0x20 ,0x4E ,0x75 ,0x6D ,0x62
+,0x65 ,0x72 ,0x3A ,0x20 ,0x30 ,0x30 ,0x30 ,0x30 ,0x30 ,0x30 ,0x00 ,0x04 ,0x03 ,0x00 ,0x02 ,0x00
+,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x5A ,0xFC ,0xFC ,0x03 ,0x00 ,0x57 ,0x00
+,0x01 ,0x00 ,0x08 ,0x40 ,0x03 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x10 ,0x00 ,0x08 ,0x00 ,0x06 ,0x40
+,0x09 ,0x05 ,0xBE ,0x00 ,0x06 ,0x40 ,0x0A ,0x00 ,0x01 ,0x00 ,0x0A ,0x40 ,0x06 ,0x00 ,0x00 ,0x00
+,0x00 ,0x00 ,0x00 ,0x00 ,0x05 ,0x40 ,0x07 ,0xFC ,0x00 ,0x2C ,0x00 ,0x04 ,0x00 ,0x24 ,0xC0 ,0x05
+,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00
+,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00
+,0x00 ,0x04 ,0x40 ,0x13 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00
+,0x00 ,0x5A ,0xF8 ,0xFC ,0x03 ,0x00 ,0x57 ,0x00 ,0x10 ,0x00 ,0x08 ,0x40 ,0x11 ,0x00 ,0x00 ,0x00
+,0x00 ,0x00 ,0x10 ,0x00 ,0x08 ,0x00 ,0x06 ,0x40 ,0x09 ,0x05 ,0xBE ,0x00
+ ,0x06 ,0x40 ,0x0A ,0x00
+,0x01 ,0x00 ,0x0A ,0x40 ,0x06 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x05 ,0x40 ,0x07 ,0xFC
+,0x00 ,0x2C ,0x00 ,0x04 ,0x00 ,0x24 ,0xC0 ,0x05 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00
+,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00
+,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x04 ,0x40 ,0x13
+};
+WORD sizeof_ripl_rom = sizeof( ripl_rom);
+
diff --git a/private/net/svcdlls/rpl/dll/ether.c b/private/net/svcdlls/rpl/dll/ether.c
new file mode 100644
index 000000000..bd8cb05f7
--- /dev/null
+++ b/private/net/svcdlls/rpl/dll/ether.c
@@ -0,0 +1,498 @@
+/*++
+
+Copyright (c) 1987-93 Microsoft Corporation
+
+Module Name:
+
+ ether.c
+
+Abstract:
+
+ Contains EtherStartThread()
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 03 - February - 1993
+
+Revision History:
+
+ 03-Feb-1993 vladimv
+ Ported to NT
+ 16-Aug-1995 jonn
+ Extensions to allow EtherStart clients bridged to FDDI
+ to boot from FDDI adapters. FDDI adapters use a SAP station
+ rather than a DIRECT station.
+
+--*/
+
+#include "local.h"
+#include <jet.h> // need to include because rpllib.h depends on JET
+#include <rpllib.h> // RplReportEvent()
+
+//
+// The following are XNS packet definitions used to crack EtherStart
+// packets.
+//
+
+#include <packon.h> // pack EtherStart structures
+
+struct sockaddr_ns {
+ DWORD net;
+ BYTE host[ NODE_ADDRESS_LENGTH];
+ WORD socket;
+};
+
+#define ETHERSTART_SOCKET 0x0104 // After swapping bytes
+
+#define SNAP_SAP 0xAA; // SAP used for SNAP packets
+
+struct _IDP {
+ WORD chksum;
+ WORD len;
+ BYTE xport_cntl;
+ BYTE packet_type;
+ struct sockaddr_ns dest;
+ struct sockaddr_ns src;
+};
+
+#define XNS_NO_CHKSUM 0xffff // XNS checksum
+#define PEX_TYPE 0x4 // packet exchange type
+
+struct _PEX {
+ DWORD pex_id;
+ WORD pex_client;
+};
+
+#define ETHERSERIES_CLIENT 0x0080 // After swapping bytes
+
+struct _ETHERSTART {
+ WORD ethershare_ver;
+ BYTE unit;
+ BYTE fill;
+ WORD block;
+ BYTE func;
+ BYTE error;
+ WORD bytes;
+};
+
+#define ETHERSHARE_VER 0
+#define FUNC_RESPONSE 0x80
+#define FUNC_READFILEREQ 0x20
+#define FUNC_READFILERESP (FUNC_READFILEREQ | FUNC_RESPONSE)
+
+typedef struct _ETHERSTART_REQ { // EtherStart Read File Request
+ struct _IDP idp;
+ struct _PEX pex;
+ struct _ETHERSTART es;
+ BYTE filename[64];
+ WORD start;
+ WORD count;
+} ETHERSTART_REQ;
+
+typedef struct _ETHERSTART_RESP { // EtherStart Read File Response
+ struct _IDP idp;
+ struct _PEX pex;
+ struct _ETHERSTART es;
+ BYTE data[0x200];
+} ETHERSTART_RESP;
+
+typedef struct _SNAP_FRAME { // SNAP packet
+ struct {
+ BYTE orgcode[3];
+ BYTE etype[2];
+ } header;
+ union {
+ ETHERSTART_REQ request;
+ ETHERSTART_RESP response;
+ } data;
+} SNAP_FRAME;
+
+//
+// RECEIVE case: DLC oddity in MS-unique DIRECT extension.
+// In case of a direct open auchLanHeader[] returned by DLC contains
+// LAN_HEADER structure without physical control fields. (I.e.
+// it contains LAN_HEADER_TOO structure defined below.). Therefore
+// 3Com 3Station address begins at offset 6 instead of at offset 8.
+//
+#define RPLDLC_DIRECT_SOURCE_OFFSET 6 // WARNING not 8
+#define RPLDLC_SAP_SOURCE_OFFSET 8
+
+//
+// TRANSMIT case: possible DLC bug
+// If LAN_HEADER is used then DLC sends only the first 4 bytes of
+// destination address ( and the first byte of destination address is
+// at offset 2 instead of 0). But if we use LAN_HEADER_TOO then this
+// problem goes away.
+//
+typedef struct _LAN_HEADER_TOO {
+ BYTE dest_addr[ NODE_ADDRESS_LENGTH]; // Destination address
+ BYTE source_addr[ NODE_ADDRESS_LENGTH]; // Source address
+ BYTE routing_info_header[2]; // Routing information hdr
+} LAN_HEADER_TOO; // BUGBUG not LAN_HEADER due to a DLC bug
+
+#include <packoff.h> // restore default packing (done with EtherStart structures)
+
+
+
+#ifdef RPL_DEBUG
+VOID RplCopy( PVOID destination, PVOID source, DWORD length) {
+ memcpy( destination, source, length);
+}
+#else
+#define RplCopy( _a0, _a1, _a2) memcpy( _a0, _a1, _a2)
+#endif
+
+
+BOOL EtherAcsLan(
+ PADAPTER_INFO pAdapterInfo,
+ PLLC_CCB pCcb,
+ BYTE Command
+ )
+{
+ PLLC_CCB pBadCcb;
+ DWORD status;
+ DWORD retry_count;
+
+ pCcb->uchDlcCommand = Command;
+ retry_count = 0;
+
+transmit_retry:
+ status = AcsLan( pCcb, &pBadCcb);
+ if ( pAdapterInfo->Closing == TRUE) {
+ return( FALSE);
+ }
+
+ if ( status != ACSLAN_STATUS_COMMAND_ACCEPTED) {
+ RplDump( ++RG_Assert,( "status=0x%x", status));
+ RplDlcReportEvent( status, Command);
+ return( FALSE);
+ }
+
+ status = WaitForSingleObject( pCcb->hCompletionEvent, INFINITE);
+ if ( pAdapterInfo->Closing == TRUE) {
+ return( FALSE);
+ }
+
+ if ( status != WAIT_OBJECT_0 || pCcb->uchDlcStatus) {
+ if ( status == WAIT_FAILED) {
+ status = GetLastError();
+ RplDlcReportEvent( status, SEM_WAIT);
+ }
+ if ( retry_count++ < MAXRETRY) {
+ RplDump( RG_DebugLevel & RPL_DEBUG_MISC,(
+ "EtherAcslan(0x%x): retry_count=%d, status=%d, DlcStatus=0x%x",
+ Command, retry_count, status, pCcb->uchDlcStatus));
+ Sleep( 100L * retry_count); // wait for NETWORK to recover
+ goto transmit_retry;
+ }
+ RplDump( ++RG_Assert,( "pBadCcb=0x%x status=%d DlcStatus=0x%x",
+ pBadCcb, status, pCcb->uchDlcStatus));
+ if ( status) {
+ RplDlcReportEvent( status, SEM_WAIT);
+ } else {
+ RplDlcReportEvent( pCcb->uchDlcStatus, Command);
+ }
+ return( FALSE);
+ }
+
+ //
+ // Due to a DLC bug "pNext" field is trashed even on success
+ // code path. Until the bug is fix we must reinstate NULL.
+ //
+ pCcb->pNext = NULL;
+ return( TRUE);
+}
+
+
+//
+// RG_EtherFileName[] contains names of all legal boot request types.
+// We will do a case insensitive search since file name may arrive
+// either in uppercase or lowercase depending on 3Station.
+// RG_EtherFileName[] should be initialized during dll init time. BUGBUG
+//
+
+PCHAR RG_EtherFileName[] = { // names of all legal boot request types
+ "BOOTPC.COM",
+ "BOOTSTAT.COM",
+ NULL
+ };
+
+BOOL RplCrack( ETHERSTART_REQ * pEtherstartReq)
+{
+ PCHAR pFileName;
+ DWORD index;
+
+ if ( pEtherstartReq->idp.packet_type != PEX_TYPE ||
+ pEtherstartReq->pex.pex_client != ETHERSERIES_CLIENT ||
+ pEtherstartReq->es.func != FUNC_READFILEREQ) {
+ return( FALSE);
+ }
+
+ //
+ // Make sure this is a legal file request.
+ //
+ for ( index = 0, pFileName = RG_EtherFileName[ index];
+ pFileName != NULL;
+ pFileName = RG_EtherFileName[ ++index] ) {
+ if ( _stricmp( pEtherstartReq->filename, pFileName)) {
+ return( TRUE);
+ }
+ }
+ return( FALSE);
+}
+
+
+VOID EtherStartThread( POPEN_INFO pOpenInfo)
+{
+ PADAPTER_INFO pAdapterInfo;
+ LLC_CCB ccb;
+ BOOL BufferFree;
+ ETHERSTART_REQ * pEtherstartReq;
+ PRCVBUF pRcvbuf;
+ struct {
+ XMIT_BUFFER XmitBuffer; // see comment for XMIT_BUFFER
+ LAN_HEADER_TOO LanHeader;
+ } XmitQueue; // first send buffer.
+ SNAP_FRAME SnapResp; // second & last send buffer
+ union {
+ LLC_DIR_OPEN_DIRECT_PARMS DirOpenDirectParms;
+ LLC_DLC_OPEN_SAP_PARMS DlcOpenSapParms;
+ LLC_RECEIVE_PARMS ReceiveParms;
+ LLC_TRANSMIT_PARMS TransmitParms;
+ LLC_BUFFER_FREE_PARMS BufferFreeParms;
+ } Parms;
+ BOOL fIsFDDI = FALSE;
+ USHORT usFDDIStationId = 0;
+ INT source_offset = RPLDLC_DIRECT_SOURCE_OFFSET;
+
+
+ RplDump( RG_DebugLevel & RPL_DEBUG_ETHER,(
+ "++EtherStartThread: pOpenInfo=0x%x", pOpenInfo));
+
+ pAdapterInfo = (PADAPTER_INFO)pOpenInfo->adapt_info_ptr;
+ BufferFree = FALSE;
+
+ if ( pAdapterInfo->network_type == RPL_ADAPTER_FDDI ) {
+ fIsFDDI = TRUE;
+ source_offset = RPLDLC_SAP_SOURCE_OFFSET;
+ RplDump( RG_DebugLevel & RPL_DEBUG_ETHER,(
+ "EtherStartThread: fIsFDDI==TRUE"));
+ }
+ //
+
+ //
+ // Initialize fixed fields in ccb.
+ //
+ memset( &ccb, '\0', sizeof(ccb));
+ ccb.hCompletionEvent = CreateEvent(
+ NULL, // no security attributes
+ FALSE, // automatic reset
+ FALSE, // initial state is NOT signalled
+ NULL // no name
+ );
+ if ( ccb.hCompletionEvent == NULL) {
+ DWORD status = GetLastError();
+ RplDump( ++RG_Assert, ( "error=%d", status));
+ RplDlcReportEvent( status, SEM_CREATE);
+ RplReportEvent( NELOG_RplXnsBoot, NULL, 0, NULL);
+ return;
+ }
+ ccb.uchAdapterNumber = pAdapterInfo->adapter_number;
+ ccb.u.pParameterTable = (PLLC_PARMS)&Parms;
+
+ //
+ // Initialize fixed fields in XmitQueue.
+ //
+ memset( (PVOID)&XmitQueue, '\0', sizeof( XmitQueue.XmitBuffer));
+ XmitQueue.XmitBuffer.cbBuffer = sizeof( XmitQueue.LanHeader);
+ RplCopy( XmitQueue.LanHeader.source_addr, pOpenInfo->NodeAddress, NODE_ADDRESS_LENGTH);
+ //
+ // Routing info header fields should be important for token ring already.
+ // However DLC may want to use these fields to set the XNS identifier.
+ // For now, 0x0600 word is hardcoded in DLCAPI.DLL // BUGBUG
+ //
+ XmitQueue.LanHeader.routing_info_header[0] = 0x06;
+ XmitQueue.LanHeader.routing_info_header[1] = 0x00;
+
+ //
+ // Initialize fixed fields in SnapResp.data.response.
+ //
+ memset( &SnapResp.data.response, '\0', sizeof(SnapResp.data.response));
+ SnapResp.data.response.idp.chksum = XNS_NO_CHKSUM;
+ SnapResp.data.response.idp.packet_type = PEX_TYPE;
+ SnapResp.data.response.idp.dest.socket = ETHERSTART_SOCKET;
+ RplCopy( SnapResp.data.response.idp.src.host, pOpenInfo->NodeAddress, NODE_ADDRESS_LENGTH);
+ SnapResp.data.response.idp.src.socket = ETHERSTART_SOCKET;
+ SnapResp.data.response.pex.pex_client = ETHERSERIES_CLIENT;
+ SnapResp.data.response.es.func = FUNC_READFILERESP;
+
+ memset( &Parms, '\0', sizeof(Parms));
+ if (fIsFDDI) {
+ //
+ // Prepare for DLC_OPEN_SAP.
+ //
+ // Parms.DlcOpenSapParms.usStationId=
+ // Parms.DlcOpenSapParms.usUserStatValue=
+ // Parms.DlcOpenSapParms.uchT1=
+ // Parms.DlcOpenSapParms.uchT2=
+ // Parms.DlcOpenSapParms.uchTi=
+ // Parms.DlcOpenSapParms.uchMaxOut=
+ // Parms.DlcOpenSapParms.uchMaxIn=
+ // Parms.DlcOpenSapParms.uchMaxOutIncr=
+ // Parms.DlcOpenSapParms.uchMaxRetryCnt=
+ // Parms.DlcOpenSapParms.uchMaxMembers=
+ // Parms.DlcOpenSapParms.usMaxI_Field=
+ Parms.DlcOpenSapParms.uchSapValue=SNAP_SAP;
+ Parms.DlcOpenSapParms.uchOptionsPriority=LLC_INDIVIDUAL_SAP;
+ Parms.DlcOpenSapParms.uchcStationCount=1;
+ // Parms.DlcOpenSapParms.uchReserved2[2]=
+ // Parms.DlcOpenSapParms.cGroupCount=
+ // Parms.DlcOpenSapParms.pGroupList=
+ // Parms.DlcOpenSapParms.DlcStatusFlags=
+ // Parms.DlcOpenSapParms.uchReserved3[8]=
+ // Parms.DlcOpenSapParms.cLinkStationsAvail=
+ } else {
+ //
+ // Prepare for DIR_OPEN_DIRECT.
+ //
+ Parms.DirOpenDirectParms.usOpenOptions = LLC_DIRECT_OPTIONS_ALL_MACS;
+ Parms.DirOpenDirectParms.usEthernetType = 0x0600; // XNS identifier
+ Parms.DirOpenDirectParms.ulProtocolTypeMask = 0xFFFFFFFF;
+ Parms.DirOpenDirectParms.ulProtocolTypeMatch = 0x7200FFFF;
+ Parms.DirOpenDirectParms.usProtocolTypeOffset = 14; // where FFFF0072 of client begins
+ }
+ if ( !EtherAcsLan( pAdapterInfo,
+ &ccb,
+ (BYTE)((fIsFDDI) ? LLC_DLC_OPEN_SAP
+ : LLC_DIR_OPEN_DIRECT ))) {
+ RplReportEvent( NELOG_RplXnsBoot, NULL, 0, NULL);
+ CloseHandle( ccb.hCompletionEvent);
+ return;
+ }
+ if (fIsFDDI) {
+ usFDDIStationId = Parms.DlcOpenSapParms.usStationId;
+ }
+
+ for (;;) {
+ extern BYTE ripl_rom[];
+ extern WORD sizeof_ripl_rom;
+ WORD Temp;
+ SNAP_FRAME * psnapreq;
+
+ if ( BufferFree == TRUE) {
+ BufferFree = FALSE;
+ memset( &Parms, '\0', sizeof( Parms));
+ Parms.BufferFreeParms.pFirstBuffer = (PLLC_XMIT_BUFFER)pRcvbuf;
+ if ( !EtherAcsLan( pAdapterInfo, &ccb, LLC_BUFFER_FREE)) {
+ break;
+ }
+ }
+
+ //
+ // Prepare for RECEIVE.
+ //
+ // ReceiveParms.usStationId==0 means receive MAC & non-MAC frames.
+ //
+ memset( &Parms, '\0', sizeof(Parms));
+ if (fIsFDDI) {
+ Parms.ReceiveParms.usStationId = usFDDIStationId;
+ }
+ if ( !EtherAcsLan( pAdapterInfo, &ccb, LLC_RECEIVE)) {
+ break;
+ }
+ BufferFree = TRUE;
+
+ pRcvbuf = (PRCVBUF)Parms.ReceiveParms.pFirstBuffer;
+ if ( fIsFDDI ) {
+ psnapreq = (SNAP_FRAME *)&pRcvbuf->u;
+ if ( psnapreq->header.etype[0] != 0x06
+ || psnapreq->header.etype[1] != 0x00 ) {
+ continue;
+ }
+ pEtherstartReq = &(psnapreq->data.request);
+ }
+ else
+ {
+ pEtherstartReq = (ETHERSTART_REQ *)&pRcvbuf->u;
+ }
+
+ //
+ // Make sure this request is for us.
+ //
+ if ( !RplCrack( pEtherstartReq)) {
+ continue; // this request is not for us, ignore it
+ }
+
+ //
+ // Prepare for TRANSMIT_DIR_FRAME.
+ //
+ memset( &Parms, '\0', sizeof(Parms));
+ Parms.TransmitParms.uchXmitReadOption = DLC_DO_NOT_CHAIN_XMIT;
+ Parms.TransmitParms.pXmitQueue1 = (LLC_XMIT_BUFFER *)&XmitQueue;
+ Parms.TransmitParms.pBuffer1 = (fIsFDDI)
+ ? (PVOID)&SnapResp
+ : (PVOID)&SnapResp.data.response;
+
+ //
+ // Initialize variable fields in XmitQueue.
+ //
+ // We don't want to reverse bits here.
+ // CODEWORK The +2 here is probably made necessary by a DLC bug.
+ //
+ RplCopy( ((PBYTE)XmitQueue.LanHeader.dest_addr)
+ + ((fIsFDDI) ? 2 : 0),
+ &pRcvbuf->b.auchLanHeader[ source_offset],
+ NODE_ADDRESS_LENGTH);
+
+ //
+ // Initialize variable fields in SnapResp.data.response.
+ // For FDDI we use all of SnapResp.
+ //
+ Temp = min( pEtherstartReq->count,
+ (WORD)(sizeof_ripl_rom - pEtherstartReq->start));
+ Parms.TransmitParms.cbBuffer1 = (WORD)( sizeof(SnapResp.data.response) -
+ sizeof( SnapResp.data.response.data) + Temp );
+ if (fIsFDDI) {
+ Parms.TransmitParms.cbBuffer1 += sizeof(SnapResp.header);
+ Parms.TransmitParms.usStationId = usFDDIStationId;
+ Parms.TransmitParms.uchRemoteSap=SNAP_SAP;
+ RplCopy( &SnapResp.header,
+ &psnapreq->header,
+ sizeof(SnapResp.header) );
+ }
+ SnapResp.data.response.idp.len = HILO( Parms.TransmitParms.cbBuffer1);
+ RplCopy( SnapResp.data.response.idp.dest.host,
+ &pRcvbuf->b.auchLanHeader[ source_offset],
+ NODE_ADDRESS_LENGTH);
+ if (fIsFDDI) {
+ ReverseBits( SnapResp.data.response.idp.dest.host );
+ }
+ SnapResp.data.response.pex.pex_id = pEtherstartReq->pex.pex_id;
+ SnapResp.data.response.es.bytes = Temp; // stays intel ordered
+ // CODEWORK why do we need to copy every time?
+ RplCopy( SnapResp.data.response.data, &ripl_rom[ pEtherstartReq->start], Temp);
+
+ if ( !EtherAcsLan( pAdapterInfo, &ccb,
+ (BYTE)((fIsFDDI) ? LLC_TRANSMIT_UI_FRAME
+ : LLC_TRANSMIT_DIR_FRAME ))) {
+ break;
+ }
+ }
+
+ if ( pAdapterInfo->Closing == FALSE) {
+ RplDump(++RG_Assert, (
+ "pInfo=0x%x &ccb=0x%x pReq=0x%x pRcvbuf=0x%x pXmitQueue=0x%x pResp=0x%x &Parms=0x%x",
+ pAdapterInfo, &ccb, pEtherstartReq, pRcvbuf, &XmitQueue,
+ (fIsFDDI) ? (PVOID)&SnapResp : (PVOID)&SnapResp.data.response, &Parms));
+ RplReportEvent( NELOG_RplXnsBoot, NULL, 0, NULL);
+ }
+ if ( BufferFree == TRUE) {
+ memset( &Parms, '\0', sizeof( Parms));
+ Parms.BufferFreeParms.pFirstBuffer = (PLLC_XMIT_BUFFER)pRcvbuf;
+ (VOID)EtherAcsLan( pAdapterInfo, &ccb, LLC_BUFFER_FREE);
+ }
+ CloseHandle( ccb.hCompletionEvent);
+
+ RplDump( RG_DebugLevel & RPL_DEBUG_ETHER,(
+ "--EtherStartThread: pOpenInfo=0x%x", pOpenInfo));
+}
diff --git a/private/net/svcdlls/rpl/dll/fdr.c b/private/net/svcdlls/rpl/dll/fdr.c
new file mode 100644
index 000000000..dc7f53078
--- /dev/null
+++ b/private/net/svcdlls/rpl/dll/fdr.c
@@ -0,0 +1,181 @@
+/*++
+
+Copyright (c) 1987-93 Microsoft Corporation
+
+Module Name:
+
+ fdr.c
+
+Abstract:
+
+ Contains RpldFileDataResponse() entry point.
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 03 - February - 1993
+
+Revision History:
+
+ 03-Feb-1993 vladimv
+ Ported to NT
+
+--*/
+
+#include "local.h"
+
+BOOL RplDlcFdr(
+ POPEN_INFO pOpenInfo,
+ PRCB pRcb,
+ PBYTE data_ptr,
+ WORD length,
+ DWORD locate_addr,
+ DWORD start_addr
+ )
+/*++
+
+Routine Description:
+ This function sends FILE.DATA.RESPONSE frames to the workstation.
+
+Arguments:
+ pOpenInfo pointer to the OPEN_INFO structure
+ pRcb pointer to the RESOURCE CONTROL BLOCK structure
+ data_ptr pointer to the data to be send
+ length length of the data
+ xfer_addr addr to place data on workstation
+ start_addr addr to start execution at
+
+Return Value:
+ ERROR_SUCCESS if success, else failure.
+
+--*/
+{
+ DWORD status;
+ PFDR pFrame;
+ PLLC_CCB pBadCcb;
+ PLAN_RESOURCE pLanResource;
+ PADAPTER_INFO pAdapterInfo;
+
+ pAdapterInfo = (PADAPTER_INFO)pOpenInfo->adapt_info_ptr;
+ pLanResource = (PLAN_RESOURCE)pRcb->lan_rcb_ptr;
+
+ pFrame = (PFDR)pLanResource->frame;
+
+ pLanResource->xmit_error_cnt = 0; // Zero consecutive xmit error counter
+ pLanResource->retry_count = 0; // allow retries
+
+transmit_retry:
+ (void)memset( (LPVOID)&pLanResource->ccb, '\0', sizeof( pLanResource->ccb));
+
+ pLanResource->TransmitParms.cbBuffer2 = length;
+ pLanResource->TransmitParms.pBuffer2 = data_ptr;
+
+ // len = 0x1d + len data bit
+ pFrame->program_length = HILO(length + (sizeof (*pFrame)));
+
+ pFrame->data_hdr = HILO(length + 4); // set len part of data hdr
+// #define RPL_ELNK
+#ifdef RPL_ELNK
+ if ( length < 0x544) {
+ pFrame->data_hdr |= 0xFFFF0000;
+ }
+#endif
+ pFrame->seq_num = HILO2(pRcb->fdr_seq_number);
+ pFrame->flags = *((PBYTE)&pRcb->send_flags);
+ pFrame->locate_addr = HILO2(locate_addr);
+ pFrame->xfer_addr = HILO2(start_addr);
+
+
+ if ( !ResetEvent( pRcb->txsemhdl)) {
+ status = GetLastError();
+ RplDump( ++RG_Assert,( "status=%d", status));
+ RplDlcReportEvent( status, SEM_SET);
+ return( FALSE);
+ }
+
+ pLanResource->ccb.hCompletionEvent = pRcb->txsemhdl;
+ pLanResource->ccb.uchAdapterNumber = pAdapterInfo->adapter_number;
+ pLanResource->ccb.uchDlcCommand = LLC_TRANSMIT_UI_FRAME;
+ pLanResource->ccb.u.pParameterTable = (PLLC_PARMS)&pLanResource->TransmitParms;
+
+ if ( !ResetEvent( pRcb->SF_wakeup)) {
+ status = GetLastError();
+ RplDump( ++RG_Assert,( "status=%d", status));
+ RplDlcReportEvent( status, SEM_SET);
+ pRcb->RcbIsBad = TRUE;
+ return( FALSE);
+ }
+
+ status = AcsLan( &pLanResource->ccb, &pBadCcb);
+ if ( pAdapterInfo->Closing == TRUE) {
+ (VOID)SetEvent( pRcb->txsemhdl);
+ return( FALSE);
+ }
+
+ if ( status != ACSLAN_STATUS_COMMAND_ACCEPTED) {
+ RplDump( ++RG_Assert,( "pCcb=0x%x pBadCcb=0x%x status=%d",
+ &pLanResource->ccb, pBadCcb, status));
+ RplDlcReportEvent( status, LLC_TRANSMIT_UI_FRAME);
+ (VOID)SetEvent( pRcb->txsemhdl);
+ return( FALSE);
+ }
+
+ status = WaitForSingleObject( pRcb->txsemhdl, INFINITE);
+ if ( pAdapterInfo->Closing == TRUE) {
+ return( FALSE);
+ }
+
+ if ((status != WAIT_OBJECT_0) || (pLanResource->ccb.uchDlcStatus)) {
+ if ( status != WAIT_OBJECT_0) {
+ if ( status == WAIT_FAILED) {
+ status = GetLastError();
+ }
+ RplDlcReportEvent( status, SEM_WAIT);
+ }
+ if ( pLanResource->retry_count++ < MAXRETRY) {
+ RplDump( RG_DebugLevel & RPL_DEBUG_MISC,(
+ "FDR(%ws): retry_count=%d, status=%d, DlcStatus=0x%x",
+ pRcb->AdapterName, pLanResource->retry_count, status,
+ pLanResource->ccb.uchDlcStatus));
+ Sleep( 100L * pLanResource->retry_count); // for NETWORK to recover
+ goto transmit_retry;
+ }
+ RplDump( ++RG_Assert,(
+ "pCcb=0x%x pBadCcb=0x%x AdapterName=%ws status=%d DlcStatus=0x%x",
+ &pLanResource->ccb, pBadCcb, pRcb->AdapterName, status, pLanResource->ccb.uchDlcStatus));
+
+ pLanResource->retry_count = 0; // no more attempts, reset retry count
+
+ if ( status != ERROR_SUCCESS) {
+ RplDlcReportEvent( status, SEM_WAIT);
+ (void)SetEvent( pRcb->txsemhdl);
+ return( FALSE);
+
+ }
+
+ if ( pLanResource->ccb.uchDlcStatus == LLC_STATUS_TRANSMIT_ERROR_FS
+ ||
+ pLanResource->ccb.uchDlcStatus == LLC_STATUS_TRANSMIT_ERROR) {
+ //
+ // Workstation is not receiving anymore. ?? Rebooted
+ // during boot, power failure or etc. OR real transmit
+ // failure ??
+
+ if ( pLanResource->xmit_error_cnt++ <= MAX_CONSECUTIVE_ERROR) {
+ return( FALSE);
+ }
+
+ //
+ // Too many consecutive transmit errors. Tell that there is a
+ // problem with transmitting.
+ //
+
+ RplDlcReportEvent( pLanResource->ccb.uchDlcStatus, LLC_TRANSMIT_UI_FRAME);
+ return( FALSE);
+ }
+
+ RplDlcReportEvent( pLanResource->ccb.uchDlcStatus, LLC_TRANSMIT_UI_FRAME);
+ return( FALSE);
+ }
+
+ return( TRUE);
+}
diff --git a/private/net/svcdlls/rpl/dll/find.c b/private/net/svcdlls/rpl/dll/find.c
new file mode 100644
index 000000000..0527aa668
--- /dev/null
+++ b/private/net/svcdlls/rpl/dll/find.c
@@ -0,0 +1,312 @@
+/*++
+
+Copyright (c) 1987-93 Microsoft Corporation
+
+Module Name:
+
+ find.c
+
+Abstract:
+
+ Contains RplDlcFind() entry point.
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 03 - February - 1993
+
+Revision History:
+
+ 03-Feb-1993 vladimv
+ Ported to NT
+
+Notes:
+
+ June 21, 1990 Olli Visamo
+ If RplDlcFind() gets error LLC_STATUS_LOST_DATA_NO_BUFFERS or
+ LLC_STATUS_LOST_DATA_INADEQUATE_SPACE from LLC_RECEIVE, go back to
+ receive loop since these errors are recoverable.
+
+ July 30, 1990 Olli Visamo
+ If there is VolumeId in FIND frame, it is copied to RCB structure.
+--*/
+
+#include "local.h"
+
+DBGSTATIC VOID BinaryToUnicode(
+ OUT LPWSTR UnicodeString,
+ IN PBYTE BinaryArray,
+ IN DWORD BinaryArrayLength
+ )
+{
+ BYTE byte;
+ DWORD index;
+
+ for ( index=0; index < BinaryArrayLength; index++) {
+ byte = BinaryArray[ index];
+ UnicodeString[ 2*index] = L"0123456789ABCDEF"[ byte / 0x10];
+ UnicodeString[ 2*index+1] = L"0123456789ABCDEF"[ byte & 0x0F];
+ }
+ UnicodeString[ 2*index] = 0;
+}
+
+
+BOOL RplDlcFind(
+ POPEN_INFO pOpenInfo,
+ PRCB pRcb
+ )
+/*++
+Routine Description:
+
+ Activates DLC receive to pick up FIND frames. FIND frames arrive on
+ FIND_SAP sid.
+
+ We do not log any events if we encounter errors while service is in
+ the closing state. But we have to return FALSE even in that case
+ else the caller would keep calling us in a tight loop.
+
+Arguments:
+ pOpenInfo pointer to the OPEN_INFO structure
+ pRcb pointer to the RESOURCE CONTROL BLOCK structure
+
+Return Value:
+ TRUE if a valid FIND request was received
+ FALSE otherwise
+
+--*/
+{
+ DWORD status;
+ PLLC_CCB pBadCcb;
+ PRCVBUF pRcvbuf;
+ PADAPTER_INFO pAdapterInfo;
+ PLAN_RESOURCE pLanResource;
+ PFOUND_FRAME pFoundFrame;
+ WORD frame_size;
+ WORD volid_len;
+ WORD prog_len;
+ WORD i;
+
+
+ pAdapterInfo = (PADAPTER_INFO)pOpenInfo->adapt_info_ptr;
+
+ memset( (PVOID)&pAdapterInfo->u1, '\0', sizeof(pAdapterInfo->u1));
+ pAdapterInfo->u1.r.uchAdapterNumber = pAdapterInfo->adapter_number;
+ pAdapterInfo->u1.r.uchDlcCommand = LLC_RECEIVE;
+ pAdapterInfo->u1.r.hCompletionEvent = pAdapterInfo->findsem;
+ pAdapterInfo->u1.r.u.pParameterTable = (PLLC_PARMS)&pAdapterInfo->u1.r1;
+
+ pAdapterInfo->u1.r1.usStationId = pAdapterInfo->findsid;
+
+rcvloop:
+ if ( !ResetEvent( pAdapterInfo->findsem)) {
+ if ( pAdapterInfo->Closing == TRUE) {
+ return( FALSE);
+ }
+ status = GetLastError();
+ RplDump( ++RG_Assert,( "pAdapterInfo=0x%x, status=%d", pAdapterInfo, status));
+ RplDlcReportEvent( status, SEM_SET);
+ return( FALSE);
+ }
+
+ status = AcsLan( &pAdapterInfo->u1.r, &pBadCcb);
+ if ( pAdapterInfo->Closing == TRUE) {
+ return( FALSE);
+ }
+
+ if ( status != ACSLAN_STATUS_COMMAND_ACCEPTED) {
+ RplDump( ++RG_Assert,( "pAdapterInfo=0x%x, status=%d", pAdapterInfo, status));
+ RplDlcReportEvent( status, LLC_RECEIVE);
+ return( FALSE);
+ }
+
+ status = WaitForSingleObject( pAdapterInfo->findsem, INFINITE);
+ if ( pAdapterInfo->Closing == TRUE) {
+ return( FALSE);
+ }
+
+ if ( (status != WAIT_OBJECT_0) || (pAdapterInfo->u1.r.uchDlcStatus)) {
+ if ( status != WAIT_OBJECT_0) {
+ if ( status == -1) {
+ status = GetLastError();
+ }
+ RplDlcReportEvent( status, SEM_WAIT);
+ } else if ( pAdapterInfo->u1.r.uchDlcStatus
+ == LLC_STATUS_LOST_DATA_NO_BUFFERS) {
+ goto rcvloop; // recoverable error
+ } else if ( pAdapterInfo->u1.r.uchDlcStatus
+ == LLC_STATUS_LOST_DATA_INADEQUATE_SPACE) {
+ //
+ // Release the buffer first. We do not expect an error here.
+ //
+ if ( !RplDlcBufferFree( FIND_FREE, pAdapterInfo)) {
+ return( FALSE);
+ }
+ goto rcvloop;
+ } else {
+ RplDlcReportEvent( pAdapterInfo->u1.r.uchDlcStatus, LLC_RECEIVE);
+ }
+ RplDump( ++RG_Assert,( "status=%d, DlcStatus=0x%x",
+ status, pAdapterInfo->u1.r.uchDlcStatus));
+ return( FALSE);
+ }
+
+ pRcvbuf = (PRCVBUF) (pAdapterInfo->u1.r1.pFirstBuffer);
+
+ if ( pRcvbuf->u.Find.program_command != FIND_CMD) {
+ //
+ // NOT a FIND frame, ignore it. And of course, release the buffer.
+ //
+ if ( !RplDlcBufferFree( FIND_FREE, pAdapterInfo)) {
+ return( FALSE);
+ }
+ goto rcvloop; // Issue new receive
+ }
+
+ //
+ // Save burned in address first in binary then in hex-ascii representation.
+ //
+ memcpy( pRcb->NodeAddress, pRcvbuf->u.Find.source_addr, NODE_ADDRESS_LENGTH);
+ BinaryToUnicode( pRcb->AdapterName, pRcb->NodeAddress, NODE_ADDRESS_LENGTH);
+
+ // Volume id option and default boot bit are not necessarily used
+ pRcb->volume_id[0] ='\0';
+ pRcb->flags.defboot = 0;
+
+ // Check if volumeid is passed (Nokia option)
+ prog_len = HILO(pRcvbuf->u.Find.program_length);
+ if (prog_len > FIND_LEN) {
+ //
+ // To get volume id length, we subtract 6 (4 for FILE_HDR length
+ // plus 2 for identifier bytes)
+ //
+ volid_len = (WORD)((HIBYTE((LOWORD(pRcvbuf->u.Find.file_hdr)))) - 6);
+
+ if (volid_len <= MAX_VOLID_LEN && volid_len >= 0) {
+ if (pRcvbuf->u.Find.file_name[0] == VOLID_FIELD1 &&
+ pRcvbuf->u.Find.file_name[1] == VOLID_FIELD2) {
+ // IT IS VOLUME ID
+ for (i = 0; i < volid_len; i++) {
+ pRcb->volume_id[i] = pRcvbuf->u.Find.file_name[i+2];
+ }
+ }
+ }
+ }
+
+ //
+ // PREPARE LAN HEADER AND TRANSMIT BUFFERS IN LAN RCB FOR
+ // FOUND FRAME TRANSMISSION
+ //
+
+ pLanResource = (PLAN_RESOURCE)pRcb->lan_rcb_ptr;
+
+ memcpy( (PVOID)pLanResource->LanHeader.dest_addr,
+ (PBYTE)&pRcvbuf->b.auchLanHeader[ 8], NODE_ADDRESS_LENGTH);
+
+ //
+ // Set routing stuff for TokenRing only.
+ //
+ if ( pAdapterInfo->network_type == RPL_ADAPTER_TOKEN_RING) {
+ //
+ // Clear msb in first byte to avoid GROUP address
+ //
+ pLanResource->LanHeader.dest_addr[0] &= 0x7F;
+ //
+ // Send it using limited broadcast.
+ //
+ pLanResource->LanHeader.routing_info_header[0] = BRDCST;
+ pLanResource->LanHeader.routing_info_header[1] = UNSPEC_FRM_SIZE;
+ //
+ // Set routing info indication bit
+ //
+ pLanResource->LanHeader.source_addr[0] |= 0x80;
+ }
+
+ //
+ // Setup LLC_TRANSMIT_PARMS (transmit parameter table).
+ //
+ pLanResource->TransmitParms.usStationId = pAdapterInfo->findsid; // our, send Sid
+ pLanResource->TransmitParms.uchTransmitFs = 0;
+ pLanResource->TransmitParms.uchRemoteSap = FIND_SAP; // their, receive SAP
+ pLanResource->TransmitParms.pXmitQueue1 =
+ (PLLC_XMIT_BUFFER)&pLanResource->XmitBuffer;
+ pLanResource->TransmitParms.pXmitQueue2 = NULL;
+ //
+ // Buffer 1 is FOUND_FRAME. There is no buffer 2.
+ //
+ pLanResource->TransmitParms.cbBuffer1 = sizeof( FOUND_FRAME);
+ pLanResource->TransmitParms.pBuffer1 = (PVOID)pLanResource->frame;
+ pLanResource->TransmitParms.cbBuffer2 = 0;
+ pLanResource->TransmitParms.pBuffer2 = NULL;
+ //
+ // Do not chain on completion.
+ //
+ pLanResource->TransmitParms.uchXmitReadOption = DLC_DO_NOT_CHAIN_XMIT;
+
+
+ //
+ // Setup LLC_XMIT_BUFFER used for "pXmitQueue1" above. Note that in
+ // LAN_RESOURCE data structure LanHeader field is right behind
+ // XmitBuffer field. Alignment should cause no problems (for now)
+ // since TransmitParms size is 0xc.
+ //
+ pLanResource->XmitBuffer.pNext = NULL;
+ pLanResource->XmitBuffer.cbBuffer = sizeof( pLanResource->LanHeader);
+ pLanResource->XmitBuffer.cbUserData = 0;
+
+ // Prepare FOUND frame, there is space for it in LAN RCB
+
+ //
+ // Choose frame_size as smaller of our frame size & client's
+ // frame size.
+ //
+
+ frame_size = HILO(pRcvbuf->u.Find.max_frame); // in Intel format
+
+ if ( frame_size > pAdapterInfo->max_xmit_buff_size) {
+ //
+ // Our frame size is smaller than client's frame size.
+ //
+ frame_size = pAdapterInfo->max_xmit_buff_size;
+ }
+
+ pFoundFrame = (PFOUND_FRAME)pLanResource->frame; // frame area of rcb
+ pFoundFrame->max_frame = HILO( frame_size); // in LAN format
+ pFoundFrame->program_length = FL; // 00 3A
+ pFoundFrame->program_command = FOUND_CMD; // 00 02
+ pFoundFrame->corr_header = CORR_HDR; // 00 08 40 03
+ pFoundFrame->correlator = 0; // 00 00 00 00
+ pFoundFrame->resp_hdr = RESP_HDR; // 00 05 40 0B
+ pFoundFrame->resp_code = 0; // 00
+ pFoundFrame->dest_hdr = DEST_HDR; // 00 0a 40 0c
+ pFoundFrame->source_hdr = SOURCE_HDR; // 00 0a 40 06
+ pFoundFrame->info_hdr = INFO_HDR; // 00 10 00 08
+ pFoundFrame->frame_hdr = FRAME_HDR; // 00 06 40 09
+ pFoundFrame->class_hdr = CLASS_HDR; // 00 06 40 0a
+ pFoundFrame->conn_class = CLASS1; // 00 01
+ pFoundFrame->lsap_hdr = LSAP_HDR; // 00 05 40 07
+ pFoundFrame->rsap = LOAD_SAP; // F8
+
+ //
+ // Put our card-ID in frame we're transmitting. Remote boot client
+ // will use this address as a destination address for sending its
+ // SEND.FILE.REQUEST frames.
+ //
+ memcpy( (PVOID)pFoundFrame->source_addr,
+ pOpenInfo->NodeAddress, NODE_ADDRESS_LENGTH);
+
+ //
+ // put wrksta-id in frame they're RECEIVING
+ //
+ memcpy( (PVOID)pFoundFrame->dest_addr,
+ pRcvbuf->u.Find.source_addr, NODE_ADDRESS_LENGTH);
+
+ //
+ // release the buffer
+ //
+ if ( !RplDlcBufferFree( FIND_FREE, pAdapterInfo)) {
+ return( FALSE);
+ }
+ return( TRUE);
+}
+
+
+
diff --git a/private/net/svcdlls/rpl/dll/found.c b/private/net/svcdlls/rpl/dll/found.c
new file mode 100644
index 000000000..d9e4469a8
--- /dev/null
+++ b/private/net/svcdlls/rpl/dll/found.c
@@ -0,0 +1,159 @@
+/*++
+
+Copyright (c) 1987-93 Microsoft Corporation
+
+Module Name:
+
+ found.c
+
+Abstract:
+
+ Contains RpldFound() entry point.
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 03 - February - 1993
+
+Revision History:
+
+ 03-Feb-1993 vladimv
+ Ported to NT
+
+--*/
+
+#include "local.h"
+
+
+
+BOOL RplDlcFound(
+ POPEN_INFO pOpenInfo,
+ PRCB pRcb
+ )
+/*++
+
+Routine Description:
+ This function sends FOUND frame to the workstation.
+
+Arguments:
+ pOpenInfo pointer to the OPEN_INFO structure
+ pRcb pointer to the RESOURCE CONTROL BLOCK structure
+
+Return Value:
+ ERROR_SUCCESS if success, else failure.
+
+--*/
+{
+ DWORD status;
+ PLLC_CCB pBadCcb;
+ PLAN_RESOURCE pLanResource;
+ PADAPTER_INFO pAdapterInfo;
+
+ pAdapterInfo = (PADAPTER_INFO)pOpenInfo->adapt_info_ptr;
+ pLanResource = (PLAN_RESOURCE)pRcb->lan_rcb_ptr;
+
+ pLanResource->xmit_error_cnt = 0; // Zero consecutive xmit error counter
+ pLanResource->retry_count = 0; // allow retries
+
+transmit_retry:
+ (void)memset( (LPVOID)&pLanResource->ccb, '\0', sizeof( pLanResource->ccb));
+ //
+ // Most of other FOUND_FRAME data have been already set in RpldFind() code.
+ //
+ pLanResource->TransmitParms.cbBuffer2 = 0;
+ pLanResource->TransmitParms.pBuffer2 = NULL;
+
+ if ( !ResetEvent( pRcb->txsemhdl)) {
+ status = GetLastError();
+ RplDump( ++RG_Assert,( "status=%d", status));
+ RplDlcReportEvent( status, SEM_SET);
+ return( FALSE);
+ }
+
+ pLanResource->ccb.hCompletionEvent = pRcb->txsemhdl;
+ pLanResource->ccb.uchAdapterNumber = pAdapterInfo->adapter_number;
+ pLanResource->ccb.uchDlcCommand = LLC_TRANSMIT_UI_FRAME;
+ pLanResource->ccb.u.pParameterTable = (PLLC_PARMS)&pLanResource->TransmitParms;
+
+ if ( !ResetEvent( pRcb->SF_wakeup)) {
+ status = GetLastError();
+ RplDump( ++RG_Assert,( "status=%d", status));
+ RplDlcReportEvent( status, SEM_SET);
+ pRcb->RcbIsBad = TRUE;
+ return( FALSE);
+ }
+
+ status = AcsLan( &pLanResource->ccb, &pBadCcb);
+ if ( pAdapterInfo->Closing == TRUE) {
+ (VOID)SetEvent( pRcb->txsemhdl);
+ return( FALSE);
+ }
+
+ if ( status != ACSLAN_STATUS_COMMAND_ACCEPTED) {
+ RplDump( ++RG_Assert,( "pCcb=0x%x pBadCcb=0x%x status=%d",
+ &pLanResource->ccb, pBadCcb, status));
+ RplDlcReportEvent( status, LLC_TRANSMIT_UI_FRAME);
+ (VOID)SetEvent( pRcb->txsemhdl);
+ return( FALSE);
+ }
+
+ status = WaitForSingleObject( pRcb->txsemhdl, INFINITE);
+ if ( pAdapterInfo->Closing == TRUE) {
+ return( FALSE);
+ }
+
+ if ( (status != WAIT_OBJECT_0) || (pLanResource->ccb.uchDlcStatus)) {
+ if ( status != WAIT_OBJECT_0) {
+ if ( status == WAIT_FAILED) {
+ status = GetLastError();
+ }
+ RplDlcReportEvent( status, SEM_WAIT);
+ }
+ if ( pLanResource->retry_count++ < MAXRETRY) {
+ RplDump( RG_DebugLevel & RPL_DEBUG_FOUND,(
+ "FDR(%ws): retry_count=%d, status=%d, DlcStatus=0x%x",
+ pRcb->AdapterName, pLanResource->retry_count, status,
+ pLanResource->ccb.uchDlcStatus));
+ Sleep( 100L * pLanResource->retry_count); // for NETWORK to recover
+ goto transmit_retry;
+ }
+ RplDump( ++RG_Assert,(
+ "pCcb=0x%x pBadCcb=0x%x AdapterName=%ws status=%d DlcStatus=0x%x",
+ &pLanResource->ccb, pBadCcb, pRcb->AdapterName, status, pLanResource->ccb.uchDlcStatus));
+
+ pLanResource->retry_count = 0; // no more attempts, reset retry count
+
+ if ( status != ERROR_SUCCESS) {
+ RplDlcReportEvent( status, SEM_WAIT);
+ (VOID)SetEvent( pRcb->txsemhdl);
+ return( FALSE);
+
+ }
+
+ if ( pLanResource->ccb.uchDlcStatus == LLC_STATUS_TRANSMIT_ERROR_FS
+ ||
+ pLanResource->ccb.uchDlcStatus == LLC_STATUS_TRANSMIT_ERROR) {
+ //
+ // Workstation is not receiving anymore. ?? Rebooted
+ // during boot, power failure or etc. OR real transmit
+ // failure ??
+
+ if ( pLanResource->xmit_error_cnt++ <= MAX_CONSECUTIVE_ERROR) {
+ return( FALSE);
+ }
+
+ //
+ // Too many consecutive transmit errors. Tell that there is a
+ // problem with transmitting.
+ //
+
+ RplDlcReportEvent( pLanResource->ccb.uchDlcStatus, LLC_TRANSMIT_UI_FRAME);
+ return( FALSE);
+ }
+
+ RplDlcReportEvent( pLanResource->ccb.uchDlcStatus, LLC_TRANSMIT_UI_FRAME);
+ return( FALSE);
+ }
+
+ return( TRUE);
+}
+
diff --git a/private/net/svcdlls/rpl/dll/init.c b/private/net/svcdlls/rpl/dll/init.c
new file mode 100644
index 000000000..0550b158f
--- /dev/null
+++ b/private/net/svcdlls/rpl/dll/init.c
@@ -0,0 +1,545 @@
+/*++
+
+Copyright (c) 1987-93 Microsoft Corporation
+
+Module Name:
+
+ init.c
+
+Abstract:
+
+ Contains RplDlcInit() and RplDlcTerm() entry points.
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 03 - February - 1993
+
+Revision History:
+
+ 03-Feb-1993 vladimv
+ Ported to NT
+
+--*/
+
+#include "local.h"
+
+//
+// Routines imported from ether.c
+//
+VOID EtherStartThread( POPEN_INFO pOpenInfo);
+
+// Some definitions for EtherStart
+#define ETHERSTART_STACK_SIZE 0x1400 // 5k
+
+BYTE Swap[256] =
+{
+0x00,0x80,0x40,0xc0,0x20,0xa0,0x60,0xe0,0x10,0x90,0x50,0xd0,0x30,0xb0,0x70,0xf0,
+0x08,0x88,0x48,0xc8,0x28,0xa8,0x68,0xe8,0x18,0x98,0x58,0xd8,0x38,0xb8,0x78,0xf8,
+0x04,0x84,0x44,0xc4,0x24,0xa4,0x64,0xe4,0x14,0x94,0x54,0xd4,0x34,0xb4,0x74,0xf4,
+0x0c,0x8c,0x4c,0xcc,0x2c,0xac,0x6c,0xec,0x1c,0x9c,0x5c,0xdc,0x3c,0xbc,0x7c,0xfc,
+0x02,0x82,0x42,0xc2,0x22,0xa2,0x62,0xe2,0x12,0x92,0x52,0xd2,0x32,0xb2,0x72,0xf2,
+0x0a,0x8a,0x4a,0xca,0x2a,0xaa,0x6a,0xea,0x1a,0x9a,0x5a,0xda,0x3a,0xba,0x7a,0xfa,
+0x06,0x86,0x46,0xc6,0x26,0xa6,0x66,0xe6,0x16,0x96,0x56,0xd6,0x36,0xb6,0x76,0xf6,
+0x0e,0x8e,0x4e,0xce,0x2e,0xae,0x6e,0xee,0x1e,0x9e,0x5e,0xde,0x3e,0xbe,0x7e,0xfe,
+0x01,0x81,0x41,0xc1,0x21,0xa1,0x61,0xe1,0x11,0x91,0x51,0xd1,0x31,0xb1,0x71,0xf1,
+0x09,0x89,0x49,0xc9,0x29,0xa9,0x69,0xe9,0x19,0x99,0x59,0xd9,0x39,0xb9,0x79,0xf9,
+0x05,0x85,0x45,0xc5,0x25,0xa5,0x65,0xe5,0x15,0x95,0x55,0xd5,0x35,0xb5,0x75,0xf5,
+0x0d,0x8d,0x4d,0xcd,0x2d,0xad,0x6d,0xed,0x1d,0x9d,0x5d,0xdd,0x3d,0xbd,0x7d,0xfd,
+0x03,0x83,0x43,0xc3,0x23,0xa3,0x63,0xe3,0x13,0x93,0x53,0xd3,0x33,0xb3,0x73,0xf3,
+0x0b,0x8b,0x4b,0xcb,0x2b,0xab,0x6b,0xeb,0x1b,0x9b,0x5b,0xdb,0x3b,0xbb,0x7b,0xfb,
+0x07,0x87,0x47,0xc7,0x27,0xa7,0x67,0xe7,0x17,0x97,0x57,0xd7,0x37,0xb7,0x77,0xf7,
+0x0f,0x8f,0x4f,0xcf,0x2f,0xaf,0x6f,0xef,0x1f,0x9f,0x5f,0xdf,0x3f,0xbf,0x7f,0xff
+};
+
+VOID ReverseBits( PBYTE NodeAddress) {
+ DWORD i;
+ for ( i = 0; i < NODE_ADDRESS_LENGTH; i++) {
+ NodeAddress[ i] = Swap[ NodeAddress[ i] ];
+ }
+}
+
+DBGSTATIC BOOL AdapterInit(
+ POPEN_INFO pOpenInfo,
+ BYTE command,
+ BYTE sapval
+ )
+/*++
+Routine Description:
+
+ Contains functions used to prepare network adapter to serve remote boot.
+ Following DLC calls (ACSLAN) are used
+
+ - DIR.OPEN.ADAPTER
+ - DIR.STATUS
+ - DLC.OPEN.SAP
+ - DIR.SET.FUNCTIONAL.ADDRESS
+
+ Setup parameters for command (CCB and parameter tables).
+ We do not use an application-ID to lock our DLC resources.
+ Call ACSLAN.
+ Check immediate return code, quit on error, no retry.
+ Wait on the semaphore for command completion. check status.
+ Parse result of command when necessary.
+ - OPEN_ADAPTER
+ Record the Appl-ID value for future DLC calls.
+ Get max. transmit buffer size.
+ - STATUS
+ Place adapter-ID in the openinfo structure.
+ Place adapter-type in the openinfo structure.
+ - OPEN_SAP
+ Place station ids in the adapter_info structure
+ - SET_FUNCTIONAL_ADDRESS (TokenRing) or
+ SET_GROUP_ADDRESS (Ethernet)
+
+Return Value:
+ TRUE if success, FALSE otherwise.
+--*/
+{
+ DWORD status;
+ PLLC_CCB pBadCcb; // unused pointer for ACSLAN
+ PADAPTER_INFO pAdapterInfo;
+
+ pAdapterInfo = (PADAPTER_INFO)pOpenInfo->adapt_info_ptr;
+
+ if ( !ResetEvent( pAdapterInfo->gen_sem)) {
+ status = GetLastError();
+ RplDump( ++RG_Assert,( "status=%d", status));
+ RplDlcReportEvent( status, SEM_SET);
+ return( FALSE);
+ }
+
+ //
+ // An early check - compare dwords - do not subtract - to avoid wrapping.
+ //
+ if ( pOpenInfo->adapt_info_size <= sizeof( ADAPTER_INFO)) {
+ RplDump( ++RG_Assert,( "adapt_info_size=%d", pOpenInfo->adapt_info_size));
+ return( FALSE); // no space for DLC buffer pool
+ }
+
+ //
+ // Zero parameter blocks (this will zero "pNext" field in LLC_CCB)
+ // and do the common parameter initializion.
+ //
+ (VOID)memset((PVOID)&pAdapterInfo->s,'\0',sizeof(pAdapterInfo->s));
+ pAdapterInfo->s.ccb.uchAdapterNumber = pAdapterInfo->adapter_number;
+ pAdapterInfo->s.ccb.uchDlcCommand = command;
+ pAdapterInfo->s.ccb.hCompletionEvent = pAdapterInfo->gen_sem;
+
+ //
+ // Initialize command specific parameters in the input LLC_CCB.
+ //
+ switch( command) {
+ case LLC_BUFFER_CREATE:
+ pAdapterInfo->s.ccb.u.pParameterTable =
+ (PLLC_PARMS)&pAdapterInfo->s.u.s0.bcp;
+ pAdapterInfo->s.u.s0.bcp.pBuffer =
+ pOpenInfo->adapt_info_ptr + sizeof( ADAPTER_INFO);
+ pAdapterInfo->s.u.s0.bcp.cbBufferSize =
+ pOpenInfo->adapt_info_size - sizeof( ADAPTER_INFO);
+ pAdapterInfo->s.u.s0.bcp.cbMinimumSizeThreshold = 0x2000;
+ break;
+
+ case LLC_DIR_CLOSE_ADAPTER:
+ NOTHING; // there is nothing to initialize here
+ break;
+ case LLC_DIR_OPEN_ADAPTER:
+ pAdapterInfo->s.ccb.u.pParameterTable =
+ (PLLC_PARMS)&pAdapterInfo->s.u.s1.cpo;
+ pAdapterInfo->s.u.s1.cpo.pAdapterParms = &pAdapterInfo->s.u.s1.oap;
+ pAdapterInfo->s.u.s1.cpo.pExtendedParms = &pAdapterInfo->s.u.s1.oep;
+ pAdapterInfo->s.u.s1.oep.pSecurityDescriptor = NULL;
+ //
+ // With LLC_ETHERNET_TYPE_DEFAULT rpl server on build 392 did not
+ // detect an 802.3 client FIND frame.
+ //
+#ifdef NOT_YET
+ // Antti's email
+ pAdapterInfo->s.u.s1.oep.LlcEthernetType = LLC_ETHERNET_TYPE_DIX;
+#else
+ // Old stuff
+ pAdapterInfo->s.u.s1.oep.LlcEthernetType = LLC_ETHERNET_TYPE_802_3;
+#endif
+ pAdapterInfo->s.u.s1.oep.hBufferPool = NULL; // for first open
+ pAdapterInfo->s.u.s1.cpo.pDlcParms = &pAdapterInfo->s.u.s1.odp;
+ break;
+
+ case LLC_DIR_STATUS:
+ pAdapterInfo->s.ccb.u.pParameterTable =
+ (PLLC_PARMS)&pAdapterInfo->s.u.s2.dsp;
+ break;
+
+ case LLC_DLC_OPEN_SAP:
+ pAdapterInfo->s.ccb.u.pParameterTable =
+ (PLLC_PARMS)&pAdapterInfo->s.u.s3.cpo;
+ pAdapterInfo->s.u.s3.cpo.uchOptionsPriority = (BYTE ) 0x04; // individual sap
+ pAdapterInfo->s.u.s3.cpo.uchSapValue = sapval; // the SAP number to open
+ break;
+
+ case LLC_DIR_SET_FUNCTIONAL_ADDRESS: // TokenRing
+ //
+ // This works correctly for Ethernet medium as well. I.e. in Ethernet
+ // case bits within every byte are inverted by DLC. We keep the code
+ // for SET_GROUP_ADDRESS in case DLC gets changed not to support this.
+ //
+ pAdapterInfo->s.ccb.u.ulParameter = 0x00000040; // accept FIND frames
+ break;
+
+ case LLC_DIR_SET_GROUP_ADDRESS: // Ethernet
+ //
+ // Use TokenRing FUNCTIONAL_ADDRESS given above, but with bits
+ // inverted in every single byte (order of bytes is unchanged).
+ //
+ pAdapterInfo->s.ccb.u.ulParameter = 0x00000002; // accept FIND frames
+ break;
+ default:
+ RplDump( ++RG_Assert, ("command=0x%x", command));
+ return( FALSE);
+ break;
+ }
+
+ status = AcsLan( &pAdapterInfo->s.ccb, &pBadCcb);
+ RplDump( RG_DebugLevel & RPL_DEBUG_INIT,( "Adapter: AcsLan(0x%x), status=%d", command, status));
+
+ if ( status != ACSLAN_STATUS_COMMAND_ACCEPTED) {
+
+ //
+ // We do not write an event log if we fail to open an adapter.
+ // We assume we may be trying to open an adapter that does not even exist.
+ //
+ if ( command == LLC_DIR_OPEN_ADAPTER) {
+ RplDump( RG_DebugLevel & RPL_DEBUG_INIT,(
+ "Adapter: AcsLan( DIR_OPEN_ADAPTER) fails, status=0x%x, adapterNumber=%d",
+ status, pAdapterInfo->adapter_number));
+ } else {
+ // Look up ACSLAN_STATUS errors.
+ RplDump( ++RG_Assert,( "AcsLan: command=0x%x, status=0x%x, adapterNumber=%d",
+ command, status, pAdapterInfo->adapter_number));
+ RplDlcReportEvent( status, command);
+ }
+ (VOID)SetEvent( pAdapterInfo->gen_sem);
+ return( FALSE);
+ }
+
+ //
+ // await command completion for few seconds
+ //
+ status = WaitForSingleObject( pAdapterInfo->gen_sem, 3000L);
+
+ if (status != WAIT_OBJECT_0) {
+ if ( status == -1) {
+ status = GetLastError();
+ }
+ RplDump( ++RG_Assert,( "Adapter: Wait(), status=%d", status));
+ RplDlcReportEvent( status, SEM_WAIT);
+ (VOID)SetEvent( pAdapterInfo->gen_sem);
+ return( FALSE);
+ }
+
+ RplDump( RG_DebugLevel & RPL_DEBUG_INIT,(
+ "Adapter: AcsLan( command=0x%x), DlcStatus=0x%x",
+ command, pAdapterInfo->s.ccb.uchDlcStatus));
+
+ if ( pAdapterInfo->s.ccb.uchDlcStatus) {
+ RplDump( ++RG_Assert,( "Adapter: DlcStatus=0x%x", pAdapterInfo->s.ccb.uchDlcStatus));
+ RplDlcReportEvent( pAdapterInfo->s.ccb.uchDlcStatus, command);
+ return( FALSE);
+ }
+
+ switch( command) {
+ case LLC_BUFFER_CREATE:
+ pAdapterInfo->hBufferPool = pAdapterInfo->s.u.s0.bcp.hBufferPool;
+ RPL_ASSERT( pAdapterInfo->hBufferPool != NULL);
+ break;
+
+ case LLC_DIR_OPEN_ADAPTER:
+ //
+ // Save our maximum frame size for transmit buffers.
+ //
+ pAdapterInfo->max_xmit_buff_size =
+ pAdapterInfo->s.u.s1.oap.usMaxFrameSize;
+
+ //
+ // OS/2 dlc uses smaller size, thus if uncomment the definition
+ // of OS2_MAX_FRAME_SIZE below we can repro OS/2 RPL behavior
+ // byte by byte. Useful for debugging.
+ //
+#define OS2_MAX_FRAME_SIZE 1508
+
+#ifdef OS2_MAX_FRAME_SIZE
+ if (pAdapterInfo->max_xmit_buff_size > OS2_MAX_FRAME_SIZE) {
+ pAdapterInfo->max_xmit_buff_size = OS2_MAX_FRAME_SIZE;
+ }
+#endif
+
+ if ( pAdapterInfo->max_xmit_buff_size < OVRHD + 100) {
+ RplDump( ++RG_Assert,( "Adapter: max_xmit_buff_size=0x%x",
+ pAdapterInfo->max_xmit_buff_size));
+ RplDlcReportEvent( TOO_SMALL_XMIT_BUFF, RPLDLL_NO_ERROR);
+ return( FALSE);
+ }
+
+ // Tell the size of LAN_RCB, server allocates space for it.
+ pOpenInfo->lan_rcb_size = sizeof(LAN_RESOURCE);
+ break;
+
+ case LLC_DIR_STATUS:
+ pAdapterInfo->network_type = pAdapterInfo->s.u.s2.dsp.usAdapterType;
+ if ( pAdapterInfo->network_type != RPL_ADAPTER_ETHERNET &&
+ pAdapterInfo->network_type != RPL_ADAPTER_FDDI &&
+ pAdapterInfo->network_type != RPL_ADAPTER_TOKEN_RING) {
+ RplDump( ++RG_Assert,( "Adapter: network_type=0x%x",
+ pAdapterInfo->network_type));
+ return( FALSE); // unknown adapter type
+ }
+
+ memcpy( pOpenInfo->NodeAddress,
+ pAdapterInfo->s.u.s2.dsp.auchNodeAddress, NODE_ADDRESS_LENGTH);
+#ifndef NOT_YET
+ //
+ // In Ethernet (FDDI) case "auchNodeAddress" returned by DLC has
+ // wrong order of bits within a byte. Until DLC gets fixed
+ // we need to reverse the bit order. Without this the source
+ // address in FOUND frame is wrong, thus remote boot client
+ // uses wrong address in LAN_HEADER of its SEND_FILE_REQUEST.
+ //
+
+ if ( pAdapterInfo->network_type != RPL_ADAPTER_TOKEN_RING) {
+ ReverseBits( pOpenInfo->NodeAddress);
+ }
+#endif
+ break;
+
+ case LLC_DLC_OPEN_SAP:
+ //
+ // Save station ids for future use
+ //
+ if (sapval == LOAD_SAP) {
+ pAdapterInfo->loadsid = pAdapterInfo->s.u.s3.cpo.usStationId;
+ RplDump( RG_DebugLevel & RPL_DEBUG_INIT,(
+ "Adapter: loadsid = 0x%x", pAdapterInfo->loadsid));
+ } else {
+ pAdapterInfo->findsid = pAdapterInfo->s.u.s3.cpo.usStationId;
+ RplDump( RG_DebugLevel & RPL_DEBUG_INIT,(
+ "Adapter: findsid = 0x%x", pAdapterInfo->findsid));
+ }
+ break;
+
+ case LLC_DIR_CLOSE_ADAPTER:
+ //
+ // Close events that are no longer needed.
+ //
+ (VOID)CloseHandle( pAdapterInfo->gen_sem);
+ (VOID)CloseHandle( pAdapterInfo->sfrsem);
+ (VOID)CloseHandle( pAdapterInfo->findsem);
+ (VOID)CloseHandle( pAdapterInfo->getdata_sem);
+ break;
+ }
+
+ return( TRUE);
+}
+
+BOOL RplDlcInit(
+ POPEN_INFO pOpenInfo,
+ DWORD rpl_adapter
+ )
+/*++
+
+Routine Description:
+
+ Prepares DLL and adapter to serve Remote Boot clients.
+
+ Create system events, open adapter logically and get adapter status,
+ open architected SAPs (F8 and FC), then set architected functional address.
+ This last operation succeeds in case of Tokenring. It fails in case of
+ Ethernet, so there we set group address.
+
+ In case of errors we write an event log and return.
+
+Arguments:
+ pOpenInfo pointer to the OPEN_INFO structure
+ rpl_adapter adapter number
+
+Return Value:
+ TRUE if success, FALSE otherwise.
+
+--*/
+{
+ DWORD status;
+ PADAPTER_INFO pAdapterInfo;
+ DWORD index;
+ HANDLE Handle;
+
+ //
+ // Memory for pAdapterInfo was allocated in rpl server code.
+ //
+ pAdapterInfo = (PADAPTER_INFO)pOpenInfo->adapt_info_ptr;
+
+ //
+ // Initialize ADAPTER_INFO structure.
+ //
+
+ //
+ // Service is just starting => Closing is FALSE.
+ //
+ pAdapterInfo->Closing = FALSE;
+
+ //
+ // Typecast below reflects mismatch between RplDlc* & DLC interface.
+ //
+ pAdapterInfo->adapter_number = (BYTE)rpl_adapter;
+
+ //
+ // SFR receive thread is not yet created, init status variables
+ //
+ pAdapterInfo->SfrReceiveThreadHandle = NULL;
+ pAdapterInfo->SfrReturn = FALSE;
+
+ for ( status = ERROR_SUCCESS, index=0; index<4; index++) {
+ BOOL ManualReset;
+ BOOL InitialStateSignalled;
+ //
+ // There are four events to create and save handles to proper places
+ // for future use. Note that "sfrsem" is different.
+ //
+ if ( index != 1) {
+ ManualReset = TRUE;
+ InitialStateSignalled = TRUE;
+ } else {
+ ManualReset = FALSE;
+ InitialStateSignalled = FALSE;
+ }
+ Handle = CreateEvent( NULL, ManualReset, InitialStateSignalled, NULL);
+ if ( Handle == NULL) {
+ status = GetLastError();
+ RplDump( ++RG_Assert,( "status=%d", status));
+ break;
+ }
+ switch( index) {
+ case 0:
+ pAdapterInfo->gen_sem = Handle;
+ break;
+ case 1:
+ pAdapterInfo->sfrsem = Handle;
+ break;
+ case 2:
+ pAdapterInfo->findsem = Handle;
+ break;
+ case 3:
+ pAdapterInfo->getdata_sem = Handle;
+ break;
+ }
+ }
+
+ if ( status != ERROR_SUCCESS) { // failed to create events
+ RplDlcReportEvent( status, SEM_CREATE);
+ return( FALSE);
+ }
+ if ( !AdapterInit( pOpenInfo, LLC_DIR_OPEN_ADAPTER, 0)) {
+ return( FALSE);
+ }
+ if ( !AdapterInit( pOpenInfo, LLC_BUFFER_CREATE, 0)) {
+ return( FALSE);
+ }
+ if ( !AdapterInit( pOpenInfo, LLC_DIR_STATUS, 0)) {
+ return( FALSE);
+ }
+ if ( !AdapterInit( pOpenInfo, LLC_DLC_OPEN_SAP, LOAD_SAP)) {
+ return( FALSE);
+ }
+ if ( !AdapterInit( pOpenInfo, LLC_DLC_OPEN_SAP, FIND_SAP)) {
+ return( FALSE);
+ }
+
+ //
+ // For now LLC_DIR_SET_FUNCTIONAL_ADDRESS does the correct thing both
+ // in case of Ethernet & TokenRing. If due to unforeseen changes in
+ // DLC this code stops working on Ethernet case, we should call
+ // LLC_DIR_SET_GROUP_ADDRESS in Ethernet case below.
+ //
+ if ( !AdapterInit(
+ pOpenInfo,
+#ifdef NOT_YET
+ pAdapterInfo->network_type == RPL_ADAPTER_TOKEN_RING
+ ? LLC_DIR_SET_FUNCTIONAL_ADDRESS
+ || LLC_DIR_SET_GROUP_ADDRESS,
+#else
+ LLC_DIR_SET_FUNCTIONAL_ADDRESS,
+#endif
+ 0)) {
+
+ return( FALSE);
+ }
+
+ if ( pAdapterInfo->network_type == RPL_ADAPTER_ETHERNET
+ || pAdapterInfo->network_type == RPL_ADAPTER_FDDI ) {
+
+ DWORD tid;
+
+ pAdapterInfo->EtherStartThreadHandle = CreateThread(
+ NULL, // lpThreadAttribute
+ 0, // dwStackSize - same as first thread
+ (LPTHREAD_START_ROUTINE)EtherStartThread, // lpStartAddress
+ (LPVOID)pOpenInfo, // lpParameter
+ 0, // dwCreationFlags
+ &tid // lpThreadId
+ );
+ if ( pAdapterInfo->EtherStartThreadHandle == NULL) {
+ status = GetLastError();
+ RplDump( ++RG_Assert,( "CreateThread(), status=%d", status));
+ RplDlcReportEvent( status, THREAD_CREATE);
+ return( FALSE);
+ }
+ }
+
+ return( TRUE);
+}
+
+BOOL RplDlcTerm( POPEN_INFO pOpenInfo)
+/*++
+
+Routine Description:
+ Releases adapter and other resources that have been allocated for RPLDLL.
+
+ This thread must wait for internally created RPLDLL threads to die.
+ RPLSRV does not know about this threads. If we do not wait here, then
+ RPLSRV may clean up all resources (e.g. debug buffer) before RPLDLL
+ internal threads manage to die (=>access violations).
+
+Arguments
+ pOpenInfo pointer to the OPEN_INFO structure
+
+--*/
+{
+ DWORD status;
+ PADAPTER_INFO pAdapterInfo;
+
+ pAdapterInfo = (PADAPTER_INFO )pOpenInfo->adapt_info_ptr;
+ pAdapterInfo->Closing = TRUE;
+
+ //
+ // Close adapter, release resources
+ //
+ if ( !AdapterInit( pOpenInfo, LLC_DIR_CLOSE_ADAPTER, 0)) {
+ return( FALSE);
+ }
+
+ if ( pAdapterInfo->SfrReceiveThreadHandle != NULL) {
+ RplDump( RG_DebugLevel & RPL_DEBUG_INIT,( "RplDlcTerm: Wait( Sfr)"));
+ status = WaitForSingleObject( pAdapterInfo->SfrReceiveThreadHandle, INFINITE);
+ if ( status != WAIT_OBJECT_0) {
+ RplDump( ++RG_Assert,( "status=%d", status==WAIT_FAILED ? GetLastError() : status));
+ }
+ }
+
+ if ( pAdapterInfo->EtherStartThreadHandle != NULL) {
+ RplDump( RG_DebugLevel & RPL_DEBUG_INIT,( "RplDlcTerm: Wait( Ether)"));
+ status = WaitForSingleObject( pAdapterInfo->EtherStartThreadHandle, INFINITE);
+ if ( status != WAIT_OBJECT_0) {
+ RplDump( ++RG_Assert,( "status=%d", status==WAIT_FAILED ? GetLastError() : status));
+ }
+ }
+
+ return( TRUE);
+}
+
+
diff --git a/private/net/svcdlls/rpl/dll/local.h b/private/net/svcdlls/rpl/dll/local.h
new file mode 100644
index 000000000..48bfb1539
--- /dev/null
+++ b/private/net/svcdlls/rpl/dll/local.h
@@ -0,0 +1,498 @@
+/*++
+
+Copyright (c) 1987-93 Microsoft Corporation
+
+Module Name:
+
+ local.h
+
+Abstract:
+
+ Main include file used by rpldll & rploem code.
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 03 - February - 1993
+
+Revision History:
+
+ 03-Feb-1993 vladimv
+ Ported to NT
+ 19-Nov-1993 vladimv
+ rpldll.h -> local.h + cleanup & face-lift
+
+--*/
+
+#define NOTHING // same def in ntdef.h which is not included here
+
+#include <nt.h> // NT definitions
+#include <ntrtl.h> // NT runtime library definitions
+#include <nturtl.h>
+
+#include <windows.h> // DWORD, IN, File APIs, etc.
+#include <stdlib.h>
+#include <lmcons.h>
+
+#include <stdio.h> // vsprintf
+#include <ctype.h> // isspace
+
+
+//#include <lmcons.h> // LAN Manager common definitions
+#include <lmerr.h> // LAN Manager network error definitions
+#include <lmsname.h> // LAN Manager service names
+#include <lmapibuf.h> // NetApiBufferFree
+
+#include <netlib.h> // LAN Man utility routines
+#include <netlibnt.h> // NetpNtStatusToApiStatus
+#include <netdebug.h> // NetpDbgPrint
+#include <tstring.h> // Transitional string functions
+#include <icanon.h> // I_Net canonicalize functions
+#include <align.h> // ROUND_UP_COUNT macro
+
+#include <services.h> // LMSVCS_GLOBAL_DATA
+#include <apperr.h> // APE_AT_ID_NOT_FOUND
+
+#include <dlcapi.h> // - LLC_CCB
+#include <dlcio.h> // - DLC_DO_NOT_CHAIN_XMIT
+#include <lmerrlog.h> // - ERRLOG_BASE NELOG_OEM_Code
+
+//
+// Global types and constants (used in all RPL server files).
+//
+
+#include <rpldebug.h>
+#include <rpldll.h> // rplnet.dll entry points & return codes
+#include <riplcons.h>
+#include <ripltyps.h>
+
+
+//
+// BUGBUG For Ethernet, DLC driver returns 0x100 while LLC_ADAPTER_ETHERNET
+// BUGBUG is 0x10. One or the other should be changed. The following
+// BUGBUG defines & "<<4" are introduced as a workaround.
+//
+#define RPL_ADAPTER_ETHERNET (LLC_ADAPTER_ETHERNET<<4)
+#define RPL_ADAPTER_TOKEN_RING LLC_ADAPTER_TOKEN_RING
+//
+// BUGBUG -- DLC uses a "currently free value" to indicate FDDI.
+//
+#define RPL_ADAPTER_FDDI 0x0200
+
+
+#define LOAD_SAP 0xF8 // Receive SAP for SEND.FILE.REQUEST frames
+#define FIND_SAP 0xFC // Receice SAP for FIND frames
+
+//
+// Error handler definitions and return codes to server
+//
+
+
+//
+// Non DLC causing error. Note that these do NOT and should NOT overlap
+// with dlcapi.h command codes.
+//
+#define RPLDLL_NO_ERROR (0xFF)
+#define SEM_SET (RPLDLL_NO_ERROR - 1) // ResetEvent
+#define SEM_WAIT (RPLDLL_NO_ERROR - 2) // WaitForSingle
+#define SEM_CREATE (RPLDLL_NO_ERROR - 3) // CreateEvent
+#define THREAD_CREATE (RPLDLL_NO_ERROR - 5) // CreateThread
+
+// Message number definitions. See RMTDLL.MSG file
+
+// SEM_ , ACSLAN_, MEMORY_, THREAD_ERROR
+#define FAIL_MSG 110 // RMT0110I: RMTNET2.DLL: %1 failed.
+
+// SEM_ , MEMORY_, and THREAD_ERROR
+#define DOS_ERROR 111 // RMT0111I: The error code is the data.
+
+#define TOO_SMALL_XMIT_BUFF 120
+
+// RMTNET1.DLL errors
+
+#define CANNOT_LOAD_MOD 125
+#define PROC_NOT_FOUND 126
+#define VERSION_MISMATCH 127
+
+
+#define MSG_FILENAME "RPLDLL.MSG"
+
+
+// BYTE => BYTE
+// INT => SHORT
+// UINT => WORD
+
+//
+// COMPLETE_BY_READ is used for LLL_RECEIVE in "ulReceivedFlag".
+// Apparently, any other non-zero value would do equally well.
+//
+#define COMPLETE_BY_READ 1
+
+//
+// Version number returned to server ==
+// Version number returned by RPL2_Init
+//
+#define VERSION2_00 0x0200
+
+//
+// Helper Macros
+//
+
+// Swap bytes of word: (B1-B0) -> (B0-B1)
+//
+#define HILO(w) ((WORD)(((w<<8) | (w>>8))))
+
+
+// Swap bytes and words of dword: (B3-B2-B1-B0) -> (B0-B1-B2-B3)
+//
+#define HILO2(_x) ((((DWORD)HILO(HIWORD(_x))) | (((DWORD)HILO(LOWORD(_x)))<<16)))
+
+
+// SAP receive buffer pool values
+#define SAP_BUFFER_SIZE 240
+#define FIND_POOL_SIZE SAP_BUFFER_SIZE
+#define LOAD_POOL_SIZE (20*SAP_BUFFER_SIZE) // 20 rcv buffers for load SAP
+#define PARAGRAPH 16
+#define LOAD_POOL_SIZE_PARA (LOAD_POOL_SIZE/PARAGRAPH)
+#define FIND_POOL_SIZE_PARA (FIND_POOL_SIZE/PARAGRAPH)
+
+// Type of buffer for BUFFER.FREE
+#define FIND_FREE 0
+#define SFR_FREE 1
+
+// RMT2 internal data structures and RIPL frame formats
+// Size of ADAPTER_INFO structure MUST NOT exceed 2 kbytes
+
+
+typedef struct _ADAPTER_INFO {
+ BYTE Closing; // TRUE if adapter has been closed
+ BYTE adapter_number; // number of adapter to be used 0,1...
+ WORD network_type; // TokenRing or Ethernet
+ WORD loadsid; // Station ID for LOAD SAP
+ WORD findsid; // Station ID for FIND SAP
+ WORD max_xmit_buff_size; // Upper limit for buffers to xmit
+ HANDLE gen_sem; // event for DLC general work
+ HANDLE findsem; // event for FIND_SAP RECEIVE and BUFFER.FREE
+ HANDLE sfrsem; // event for LOAD_SAP RECEIVE
+ HANDLE getdata_sem;// event for LOAD_SAP READ and BUFFER.FREE
+
+ HANDLE hBufferPool;// DLC handle used for buffer supplied to DLC
+
+ //
+ // Following data is not actual adapter info,
+ // but this is only place to hide it to make
+ // these DLLs HANDLE-CALLABLE ie. RPL_Init must be made to
+ // every network adapter that is used. Adapter_info is
+ // so called handle, that must be used on subsequent calls
+ // to that adapter.
+ //
+
+ //
+ // SfrReceiveThread is used to keep receive posted for SFR.
+ // SfrReceiveThreadHandle is NULL if & only this thread exists.
+ //
+ HANDLE SfrReceiveThreadHandle;
+
+ //
+ // EtherStartThread is used to send DLC to 3Com 3Station clients.
+ // EtherStartThreadHandle is non-NULL if & only this thread exists.
+ //
+ HANDLE EtherStartThreadHandle;
+
+ //
+ // Used by SfrReceiveThread to let DlcSfr thread it is time to exit.
+ //
+ BOOL SfrReturn;
+
+ //
+ // Command control block and parameter tables for open adapter, open sap,
+ // set functional address, get status and close adapter commands.
+ //
+
+ // Note: re-used data space
+ struct {
+ LLC_CCB ccb; // the base ccb
+ union {
+ struct {
+ LLC_BUFFER_CREATE_PARMS bcp; // Buffer Create Parms
+ } s0;
+ struct {
+ LLC_DIR_OPEN_ADAPTER_PARMS cpo; // Ccb Parms Open
+ LLC_ADAPTER_OPEN_PARMS oap; // Open Adapter Parms
+ LLC_EXTENDED_ADAPTER_PARMS oep; // Open Extended Parms
+ LLC_DLC_PARMS odp; // Open Dlc Parms
+ } s1;
+ struct {
+ LLC_DIR_STATUS_PARMS dsp; // Dir Status Parms
+ } s2;
+ struct {
+ LLC_DLC_OPEN_SAP_PARMS cpo; // Ccb ???
+ } s3;
+ } u;
+ } s;
+ // CCBs and parameter tables for RECEIVEs, READ and BUFFER_FREE
+ struct {
+ LLC_CCB r; // RECEIVE ccb for find
+ LLC_RECEIVE_PARMS r1; // parameter table 17 bytes
+ BYTE pad; // alignment
+ } u1;
+
+ struct {
+ LLC_CCB r; // RECEIVE ccb for sfr
+ LLC_RECEIVE_PARMS r1; // parameter table 17 bytes*/
+ BYTE pad; // alignment
+ } u2;
+
+ struct {
+ LLC_CCB r; // READ ccb for sfr
+ LLC_READ_PARMS r1; // parameter table
+ } u3;
+
+ //
+ // BUFFER.FREE CCB and parameter table for freeing received buffer,
+ // SEND.FILE.REQUEST frames
+ //
+ LLC_CCB sfr_bf_ccb;
+ LLC_BUFFER_FREE_PARMS sfr_bf_ccbpt;
+
+ //
+ // BUFFER.FREE CCB and parameter table for freeing received buffer,
+ // FIND frames
+ //
+ LLC_CCB find_bf_ccb;
+ LLC_BUFFER_FREE_PARMS find_bf_ccbpt;
+
+} ADAPTER_INFO, *PADAPTER_INFO;
+
+
+typedef struct _LAN_HEADER {
+ BYTE pcf0; // Physical control field 0
+ BYTE pcf1; // Physical control field 1
+ BYTE dest_addr[ NODE_ADDRESS_LENGTH]; // Destination address
+ BYTE source_addr[ NODE_ADDRESS_LENGTH]; // Source address
+ BYTE routing_info_header[2]; // Routing information hdr
+} LAN_HEADER;
+
+//
+// In LAN_RESOURCE structure below it is essential that LanHeader field
+// is right behind XmitBuffer field. LLC_XMIT_BUFFER structure used to have
+// a trailing auchData[1] which would give a wrong offset for LAN_RESOURCE.
+// Now it has a trailing auchData[0] so compiler complains if it is not the
+// the last element in a tructure. Both problems are solved by defining
+// XMIT_BUFFER which has no auchData[] field.
+//
+
+typedef struct _XMIT_BUFFER {
+ struct _XMIT_BUFFER * pNext; // next buffer (or NULL)
+ WORD usReserved1; //
+ WORD cbBuffer; // length of transmitted data
+ WORD usReserved2; //
+ WORD cbUserData; // length of optional header
+} XMIT_BUFFER, *PXMIT_BUFFER;
+
+typedef struct _LAN_RESOURCE {
+ BYTE xmit_error_cnt; // consecutive xmit error count
+ BYTE retry_count; // xmit retries on current frame
+ LLC_CCB ccb; // for RECEIVE ,TRANSMIT frames
+ LLC_TRANSMIT_PARMS TransmitParms; // parameter block for TRANSMIT
+ XMIT_BUFFER XmitBuffer; // buffer one header
+ LAN_HEADER LanHeader; // for TRANSMIT.UI.FRAME
+ BYTE rest_of_routing[16]; // lan hdr defines 2 bytes routing
+ BYTE frame[110]; // the frame itself
+} LAN_RESOURCE, *PLAN_RESOURCE;
+
+
+//
+// Remote boot structures cannot use default packing. Thus:
+//
+
+#include <packon.h> // Pack structures: #pragma pack(1)
+
+// The find frame structure is defined below.
+
+typedef struct _FIND_FRAME {
+ WORD program_length; // 0x53 + value in first 2 bytes of file_hdr
+ WORD program_command; // must be FIND_CMD
+ DWORD corr_hdr;
+ DWORD correlator;
+ DWORD info_hdr;
+ DWORD frame_hdr;
+ WORD max_frame;
+ DWORD class_hdr;
+ WORD conn_class;
+ DWORD source_hdr;
+ BYTE source_addr[ NODE_ADDRESS_LENGTH];
+ DWORD lsap_hdr;
+ BYTE rsap;
+ DWORD search_hdr;
+ DWORD loader_hdr;
+ BYTE mach_conf[8];
+ WORD equip_flags;
+ WORD memory_size;
+ BYTE rpl_ec[8];
+ WORD adapter_id;
+ BYTE adapter_ec[10];
+ DWORD file_hdr;
+ BYTE file_name[1];
+} FIND_FRAME, *PFIND_FRAME;
+
+#define FIND_LEN 0x53
+
+//
+// The following structure defines the FOUND frame,
+// RMT2 transmits the FOUND frame in response to a FIND
+// frame from a workstation already configured to RIPL.
+//
+
+typedef struct _FOUND_FRAME {
+ WORD program_length;
+ WORD program_command;
+ DWORD corr_header;
+ DWORD correlator;
+ DWORD resp_hdr;
+ BYTE resp_code;
+ DWORD dest_hdr; // NOTE 4 bytes! not 6
+ BYTE dest_addr[ NODE_ADDRESS_LENGTH];
+ DWORD source_hdr;
+ BYTE source_addr[ NODE_ADDRESS_LENGTH];
+ DWORD info_hdr;
+ DWORD frame_hdr;
+ WORD max_frame;
+ DWORD class_hdr;
+ WORD conn_class;
+ DWORD lsap_hdr;
+ BYTE rsap;
+} FOUND_FRAME, *PFOUND_FRAME;
+
+
+//
+//
+// The fdr structure defines the FILE.DATA.RESPONSE frame transmitted by RMT2
+// in response to a validated SEND.FILE.DATA frame already received by RMT2.
+//
+
+
+typedef struct _FDR { // FILE.DATA.RESPONSE
+ WORD program_length;
+ WORD program_command;
+ DWORD seq_hdr;
+ DWORD seq_num;
+ DWORD loader_hdr;
+ DWORD locate_addr;
+ DWORD xfer_addr;
+ BYTE flags;
+ DWORD data_hdr; // the data follows this
+} FDR, *PFDR;
+
+
+//
+// The sfr structure defines the SEND.FILE.REQUEST frame transmitted by
+// the remote workstation after reception of the FOUND frame.
+//
+
+typedef struct _SFR { // SEND.FILE.REQUEST
+ WORD program_length;
+ WORD program_command;
+ DWORD seq_header; // sequence number vector
+ DWORD seq_num; // starting sequence number
+ DWORD info_hdr;
+ DWORD frame_hdr;
+ WORD max_frame;
+ DWORD class_hdr;
+ WORD conn_class;
+ DWORD source_hdr;
+ BYTE source_addr[ NODE_ADDRESS_LENGTH];
+ DWORD lsap_hdr;
+ BYTE rsap;
+ DWORD search_hdr;
+ DWORD loader_hdr;
+ BYTE mach_conf[8];
+ WORD equip_flags;
+} SFR, *PSFR;
+
+#include <packoff.h> // Restore default packing, #pragma pack()
+
+//
+// The following structure defines the Receive frame from a requesting
+// device seeking a RIPL server. The find_frame occupies the second
+// half of the structure (or the SEND.FILE.REQUEST frame, or ALERT frame).
+// The only field used by RMT2 to determine the
+// frame type is the "program_command" field of the frame.
+//
+
+
+typedef struct _RCVBUF {
+ LLC_NOT_CONTIGUOUS_BUFFER b; // buffer one header
+ union {
+ FIND_FRAME Find; // the data (find frame)
+ SFR SendFileRequest; // or a SEND.FILE.REQUEST
+ } u;
+} RCVBUF, *PRCVBUF;
+
+
+#define RCB_ALERT 0xff // rcb was alerted
+#define RCB_ALERTED 0x1 // bit set for ALERT frame
+#define RCB_ERROR 0x1 // bit for tx-rx error
+
+//
+// The following #defines relate to the values of the program_command
+// field in the frame structures above
+//
+
+#define FIND_CMD 0x0100 // FIND frame
+#define SEND_CMD 0x1000 // SEND command
+#define FDR_CMD 0x2000 // FILE.DATA.RESPONSE cmd
+#define ALERT_CMD 0x3000 // ALERT frame
+#define FOUND_CMD 0x0200 // FOUND frame
+
+//
+// The following #defines relate the the values of various fields in
+// the FOUND frame structure.
+//
+
+#define OVRHD 123 // overhead on a FOUND frame
+#define CLASS1 0x100 // class 1 provided by RJS
+#define FL 0x3A00 // sizeof found frame
+#define CORR_HDR 0x03400800 // NOT intel format
+#define RESP_HDR 0x0b400500 // ditto
+#define DEST_HDR 0x0c400a00 // ditto
+#define SOURCE_HDR 0x06400a00 // ditto
+#define INFO_HDR 0x08001000 // NOT intel format
+#define FRAME_HDR 0x09400600 // LAN format
+#define CLASS_HDR 0x0a400600 // LAN format
+#define LSAP_HDR 0x07400500 // LAN format
+#define BRDCST 0x82 // routing for broadcast
+#define UNSPEC_FRM_SIZE 0x70 // (DFFF rrrr) FFF frm size bits ON
+#define SEQ_HDR 0x11400800
+#define LDR_HDR 0x14c00d00
+#define DATA_HDR 0x18400000
+#define MAXRETRY 5 // retry TRANSMIT this much
+
+//
+// We do not write to Net error log until there is at least
+// MAX_CONSECUTIVE_ERROR transmit errors.
+//
+#define MAX_CONSECUTIVE_ERROR 3
+
+// Definitions for volume_id handling
+#define MAX_VOLID_LEN 17
+#define VOLID_FIELD1 0xFF
+#define VOLID_FIELD2 0xB0
+
+
+//
+// Routines exported by report.c
+//
+VOID RplDlcReportEvent(
+ DWORD ErrorCode,
+ DWORD Command
+ );
+
+//
+// Routines exported by buffer.c
+//
+BOOL RplDlcBufferFree(
+ BYTE free_type,
+ PADAPTER_INFO pAdapterInfo
+ );
+
+VOID ReverseBits( PBYTE NodeAddress );
diff --git a/private/net/svcdlls/rpl/dll/makefile b/private/net/svcdlls/rpl/dll/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/net/svcdlls/rpl/dll/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/net/svcdlls/rpl/dll/report.c b/private/net/svcdlls/rpl/dll/report.c
new file mode 100644
index 000000000..3eb7520e2
--- /dev/null
+++ b/private/net/svcdlls/rpl/dll/report.c
@@ -0,0 +1,60 @@
+/*++
+
+Copyright (c) 1987-93 Microsoft Corporation
+
+Module Name:
+
+ report.c
+
+Abstract:
+
+ Event log writing function used in rpl "dll".
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 03 - February - 1993
+
+Revision History:
+
+ 03-Feb-1993 vladimv
+ Ported to NT
+
+--*/
+
+#include "local.h"
+#include <jet.h> // need to include because rpllib.h depends on JET
+#include <rpllib.h> // RplReportEvent()
+
+VOID RplDlcReportEvent(
+ DWORD ErrorCode,
+ DWORD Command
+ )
+/*++
+
+Routine Description:
+ Composes an error message and writes it to a LAN Manager error log file.
+
+Arguments:
+ ErrorCode actual error code
+ command failed Dos- or ACSLAN command
+
+Return Value:
+ None.
+
+--*/
+{
+ DWORD RawDataBuffer[ 2];
+
+ RawDataBuffer[ 0] = Command; // dlc command or internal rpl command
+ RawDataBuffer[ 1] = ErrorCode; // dlc error code or win32 error code
+
+ RplReportEvent(
+ NELOG_System_Error,
+ NULL,
+ sizeof( RawDataBuffer),
+ (PBYTE)RawDataBuffer
+ );
+}
+
+
+
diff --git a/private/net/svcdlls/rpl/dll/sfr.c b/private/net/svcdlls/rpl/dll/sfr.c
new file mode 100644
index 000000000..0084f297c
--- /dev/null
+++ b/private/net/svcdlls/rpl/dll/sfr.c
@@ -0,0 +1,499 @@
+/*++
+
+Copyright (c) 1987-93 Microsoft Corporation
+
+Module Name:
+
+ sfr.c
+
+Abstract:
+
+ Contains RplDlcSfr() entry point.
+ Contains RplFindRcb() function which is also used by server\request.c\RequestThread().
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 03 - February - 1993
+
+Revision History:
+
+ 03-Feb-1993 vladimv
+ Ported to NT
+
+--*/
+
+#include "local.h"
+
+
+VOID SfrReceiveThread( PADAPTER_INFO pAdapterInfo)
+/*++
+
+Routine Description:
+
+ Created from RplDlcSfr(). Keeps RECEIVE posted on LOAD_SAP.
+ Received data is read by RpldSendFileRequest() function using AcsLan
+ READ command. The RECEIVE call will complete if ...
+
+--*/
+{
+ PLLC_CCB pBadCcb;
+ DWORD status;
+ BYTE command; // failed command
+
+ RplDump( RG_DebugLevel & RPL_DEBUG_SFR, (
+ "++SfrReceiveThread: pAdapterInfo=0x%x", pAdapterInfo));
+
+ memset( (PVOID)&pAdapterInfo->u2, '\0', sizeof(pAdapterInfo->u2));
+ pAdapterInfo->u2.r.uchAdapterNumber = pAdapterInfo->adapter_number;
+ pAdapterInfo->u2.r.uchDlcCommand = LLC_RECEIVE;
+ pAdapterInfo->u2.r.hCompletionEvent = pAdapterInfo->sfrsem;
+ pAdapterInfo->u2.r.u.pParameterTable = (PLLC_PARMS)&pAdapterInfo->u2.r1;
+ pAdapterInfo->u2.r1.ulReceiveFlag = COMPLETE_BY_READ;
+ pAdapterInfo->u2.r1.usStationId = pAdapterInfo->loadsid;
+
+ for ( ; ;) {
+ status = AcsLan(&pAdapterInfo->u2.r, &pBadCcb);
+ if (status != ACSLAN_STATUS_COMMAND_ACCEPTED) {
+ command = LLC_RECEIVE;
+ break;
+ }
+ //
+ // Waiting for SEND.FILE.REQUEST
+ //
+ status = WaitForSingleObject( pAdapterInfo->sfrsem, INFINITE);
+ if ( status != WAIT_OBJECT_0) {
+ if ( status == WAIT_FAILED) {
+ status = GetLastError();
+ }
+ command = SEM_WAIT;
+ break;
+ }
+ if ( pAdapterInfo->u2.r.uchDlcStatus
+ != LLC_STATUS_LOST_DATA_NO_BUFFERS
+ && pAdapterInfo->u2.r.uchDlcStatus
+ != LLC_STATUS_LOST_DATA_INADEQUATE_SPACE) {
+ status = pAdapterInfo->u2.r.uchDlcStatus;
+ command = LLC_RECEIVE;
+ break;
+ }
+ }
+
+ if ( pAdapterInfo->Closing == TRUE) {
+ RplDump( RG_DebugLevel & RPL_DEBUG_SFR, (
+ "--SfrReceiveThread: pAdapterInfo=0x%x", pAdapterInfo));
+ return;
+ }
+
+ RplDump( ++RG_Assert,( "status=%d, command=0x%x", status, command));
+
+ //
+ // We come here in case of unrecoverable errors. RPL service will
+ // be terminated. We also set getdata_sem event to unblock
+ // RpldSendFileRequest() thread.
+ //
+ RplDlcReportEvent( status, command);
+ pAdapterInfo->SfrReturn = TRUE;
+ (VOID)SetEvent( pAdapterInfo->getdata_sem);
+}
+
+
+VOID set_fdr_fixed(
+ PLAN_RESOURCE pLanResource,
+ WORD usStationId
+ )
+/*++
+Routine Description:
+ set_fdr_fixed() initializes the fixed parts of the FILE.DATA.RESPONSE
+ frame and the TRANSMIT.UI parameter block. It takes a pointer to the
+ LAN RCB containing the frame and transmit parameters to be initialized
+ and usStationId for architected FIND SAP.
+--*/
+
+{
+ PFDR pFdr; // point to frame
+ LLC_TRANSMIT_PARMS *tpp; // point to xmit parms
+
+ pFdr = (PFDR)pLanResource->frame; // generate frame here
+ tpp = &pLanResource->TransmitParms; // locate xmit parms
+ // setup fixed part of frame
+ // program length varies
+ pFdr->program_command = FDR_CMD; // 00 20
+ pFdr->seq_hdr = SEQ_HDR; // 00 08 40 11
+ // seq num varies
+ pFdr->loader_hdr = LDR_HDR; // 00 0d c0 14
+ pFdr->locate_addr = 0; // 00 00 00 00
+ pFdr->xfer_addr = 0; // 00 00 00 00
+ pFdr->flags = 0; // flags zero = no FDR ack
+ // data_hdr varies
+ pFdr->data_hdr = DATA_HDR; // 00 ?? 40 18
+ // setup transmit parm block
+ tpp->usStationId = usStationId;
+ tpp->uchTransmitFs = 0; // 00
+ tpp->uchRemoteSap = FIND_SAP; // FC
+ // Queue #1 is 1st buffer
+ tpp->pXmitQueue1 = (PLLC_XMIT_BUFFER)&pLanResource->XmitBuffer;
+ tpp->pXmitQueue2 = NULL; // 00 00 00 00
+ tpp->cbBuffer1 = (sizeof *pFdr); // 00 1D
+ tpp->pBuffer1 = (PCH)pLanResource->frame;
+ tpp->uchXmitReadOption = DLC_DO_NOT_CHAIN_XMIT; // don't chain on completion
+}
+
+
+VOID ProcessSfr(
+ PRCB pRcb,
+ PADAPTER_INFO pAdapterInfo,
+ PRCVBUF pRcvbuf
+ )
+{
+
+ PLAN_RESOURCE pLanResource;
+ WORD tempword;
+
+ pLanResource = (PLAN_RESOURCE)pRcb->lan_rcb_ptr;
+ pLanResource->retry_count = 0;
+ tempword = HILO(pRcvbuf->u.SendFileRequest.max_frame);
+ //
+ // Both protocol data and boot block data must fit in
+ // transmit buffer frame (OVRHD == size of protocol data).
+ //
+ if (tempword <= pAdapterInfo->max_xmit_buff_size) {
+ tempword -= OVRHD;
+ } else {
+ tempword = (WORD)( pAdapterInfo->max_xmit_buff_size - OVRHD);
+ }
+
+ //
+ // "max_frame" is used by rpl server to determine the size of boot
+ // block data frames.
+ //
+ pRcb->max_frame = tempword;
+
+ memcpy( pLanResource->LanHeader.dest_addr,
+ &(pRcvbuf->b.auchLanHeader[ 8]), NODE_ADDRESS_LENGTH);
+
+ // mask off routing info indication bit
+ pLanResource->LanHeader.dest_addr[0] &= 0x7F;
+ // note: byte to word xlate
+ pLanResource->XmitBuffer.cbBuffer = pRcvbuf->b.cbLanHeader;
+
+
+ if (pAdapterInfo->network_type == RPL_ADAPTER_TOKEN_RING) {
+ if ( pRcvbuf->b.cbLanHeader > 14) {
+ //
+ // There is routing info, we have TokenRing case. Inherit
+ // the routing from received frame. Note memcpy length! Also,
+ // we "and" with 0x1f for "non broadcast" and "xor" with 0x80
+ // for "direction interpreted from right to left".
+ //
+ memcpy( pLanResource->LanHeader.routing_info_header,
+ &pRcvbuf->b.auchLanHeader[14], pRcvbuf->b.cbLanHeader -14);
+ pLanResource->LanHeader.routing_info_header[0] &= 0x1f;
+ pLanResource->LanHeader.routing_info_header[1] ^= 0x80;
+ } else {
+ //
+ // There is no routing info, we have Etherner case. We just
+ // clear the indication bit.
+ //
+ pLanResource->LanHeader.source_addr[0] &= 0x7f;
+ }
+ }
+
+ // set FILE.DATA.RESPONSE
+ set_fdr_fixed( pLanResource, pAdapterInfo->findsid);
+}
+
+
+PRCB RplFindRcb(
+ PPRCB HeadRcbList,
+ PBYTE NodeAddress
+ )
+/*++
+Routine Description:
+
+ Finds matching rcb from rcbs in use chain.
+
+Arguments:
+ HeadRcbList pointer to the variable, which contains pointer to the
+ first RCB in chain
+ NodeAddress pointer to the hex adapter-id to scan for
+
+Return Value:
+ Pointer to matching RCB if success, NULL otherwise.
+
+--*/
+{
+ PRCB pRcb;
+
+ DebugCheckList( HeadRcbList, NULL, 0);
+
+ for ( pRcb = *HeadRcbList; pRcb != NULL; pRcb = pRcb->next_rcb) {
+ if ( !memcmp( pRcb->NodeAddress, NodeAddress,
+ sizeof( pRcb->NodeAddress) )) {
+ break;
+ }
+ }
+ return( pRcb);
+
+}
+
+
+BOOL RplDlcSfr(
+ POPEN_INFO pOpenInfo,
+ PPRCB HeadRcbList,
+ PCRITICAL_SECTION ProtectRcbList
+ )
+/*++
+
+Routine Description:
+ Receives SEND.FILE.REQUEST frames. Creates a thread to keep a receive
+ command outstanding on the opened LOAD_SAP in order to pick up
+ SEND.FILE.REQUEST frames from workstations. Uses AcsLan READ-function
+ to get notification for received data.
+
+ It validates the frame and places the requested sequence number,
+ lan header, transmit buffer header and parameters into the RCB
+ associated with the workstation and clears RAM semaphore (in RCB)
+ to wake up the service thread for the workstation.
+ It then frees the buffer containing the frame and waits for the next one.
+
+Arguments:
+ pOpenInfo pointer to the OPEN_INFO structure
+ HeadRcbList pointer to the variable, which contains pointer to the
+ first RCB in chain. The chain contains RCB-s of all clients this
+ server is interested in getting SEND.FILE.REQUEST frames from.
+ ProtectRcbList for serialising access to rcb list
+
+Return Value:
+ ERROR_SUCCESS if success, non-zero otherwise
+--*/
+{
+ DWORD status;
+ PLLC_CCB pBadCcb;
+ PRCB pRcb;
+ PRCVBUF pRcvbuf;
+ DWORD sfr_seq_number; // in the incoming SFR frame
+ DWORD fdr_seq_number; // of what worker already sent
+ PADAPTER_INFO pAdapterInfo;
+ DWORD ThreadId;
+
+ pAdapterInfo = (PADAPTER_INFO)pOpenInfo->adapt_info_ptr;
+
+ RplDump( RG_DebugLevel & RPL_DEBUG_SFR, (
+ "++SendFileRequest: pAdapterInfo=0x%x", pAdapterInfo));
+
+ //
+ // Create thread to keep receive posted.
+ //
+ pAdapterInfo->SfrReceiveThreadHandle = CreateThread(
+ NULL, // lpThreadAttribute
+ 0, // dwStackSize - same as first thread
+ (LPTHREAD_START_ROUTINE)SfrReceiveThread, // lpStartAddress
+ (LPVOID)pAdapterInfo, // lpParameter
+ 0, // dwCreationFlags
+ &ThreadId // lpThreadId
+ );
+ if ( pAdapterInfo->SfrReceiveThreadHandle == NULL) {
+ status = GetLastError();
+ RplDump( ++RG_Assert,( "pAdapterInfo=0x%x, status=%d", pAdapterInfo, status));
+ RplDlcReportEvent( status, THREAD_CREATE);
+ return( FALSE);
+ }
+
+ //
+ // Setup CCB for LLC_READ, then setup LLC_READ_PARMS to wake up
+ // only when data arrives.
+ //
+ memset( (PVOID)&pAdapterInfo->u3, '\0', sizeof(pAdapterInfo->u3));
+ pAdapterInfo->u3.r.uchAdapterNumber = pAdapterInfo->adapter_number;
+ pAdapterInfo->u3.r.uchDlcCommand = LLC_READ;
+ pAdapterInfo->u3.r.hCompletionEvent = pAdapterInfo->getdata_sem;
+ pAdapterInfo->u3.r.u.pParameterTable = (PLLC_PARMS)&pAdapterInfo->u3.r1;
+
+ pAdapterInfo->u3.r1.usStationId = pAdapterInfo->loadsid;
+ pAdapterInfo->u3.r1.uchOptionIndicator = LLC_OPTION_READ_SAP;
+ pAdapterInfo->u3.r1.uchEventSet = LLC_EVENT_RECEIVE_DATA;
+
+ //
+ // Loop for ever processing SendFileRequest frames arriving to this
+ // adapter.
+ //
+ for ( ; ;) {
+
+ //
+ // Reset event to be cleared later on by AcsLan.
+ //
+ if ( !ResetEvent( pAdapterInfo->getdata_sem)) {
+ if ( pAdapterInfo->Closing == TRUE) {
+ return( FALSE);
+ }
+ status = GetLastError();
+ RplDump( ++RG_Assert,( "pAdapterInfo=0x%x, status=%d", pAdapterInfo, status));
+ RplDlcReportEvent( status, SEM_SET);
+ return( FALSE);
+ }
+
+ status = AcsLan( &pAdapterInfo->u3.r, &pBadCcb);
+ if ( pAdapterInfo->Closing == TRUE) {
+ (VOID)SetEvent( pAdapterInfo->getdata_sem);
+ return( FALSE);
+ }
+
+ if (status != ACSLAN_STATUS_COMMAND_ACCEPTED) {
+ RplDump( ++RG_Assert,( " r=0x%x, status=%d", &pAdapterInfo->u3.r, status));
+ RplDlcReportEvent( status, LLC_READ); // failed to READ
+ (VOID)SetEvent( pAdapterInfo->getdata_sem);
+ return( FALSE);
+ }
+
+ status = WaitForSingleObject( pAdapterInfo->getdata_sem, INFINITE);
+ if ( pAdapterInfo->Closing == TRUE) {
+ return( FALSE);
+ }
+
+ if ( ( status != WAIT_OBJECT_0) || (pAdapterInfo->u3.r.uchDlcStatus)) {
+ if ( status != WAIT_OBJECT_0) {
+ if ( status == WAIT_FAILED) {
+ status = GetLastError();
+ }
+ RplDlcReportEvent( status, SEM_WAIT);
+ } else {
+ RplDlcReportEvent( pAdapterInfo->u3.r.uchDlcStatus, LLC_READ);
+ }
+ RplDump( ++RG_Assert,( " pAdapterInfo=0x%x, status=%d, DlcStatus=0x%x",
+ pAdapterInfo, status, pAdapterInfo->u3.r.uchDlcStatus));
+ return( FALSE);
+ }
+
+ if ( pAdapterInfo->SfrReturn) { // Check sfr receive
+ //
+ // SfrReceiveThreadHandle is being waited in DlcTerm() call.
+ //
+ return( TRUE); // event was written by sfr receive
+ }
+
+ //
+ // Point to receive buffer.
+ //
+ pRcvbuf = (PRCVBUF)(pAdapterInfo->u3.r1.Type.Event.pReceivedFrame);
+ RplDump( TRUE, ("pRcvbuf=0x%x", pRcvbuf));
+
+
+ //
+ // Whenever we find a matching RCB we set SFR flag so that worker
+ // thread does not move this RCB while we are working on it. Note
+ // that this means that SFR must be cleared once we are done with
+ // this RCB.
+ //
+
+ EnterCriticalSection( ProtectRcbList);
+
+ pRcb = RplFindRcb( HeadRcbList, (PBYTE)pRcvbuf->u.SendFileRequest.source_addr);
+ if ( pRcb != NULL) {
+ if ( pRcb->Pending == TRUE) {
+ pRcb = NULL; // pretend we did not see this RCB
+ } else {
+ pRcb->SFR = TRUE; // hold this RCB
+ }
+ }
+
+ LeaveCriticalSection( ProtectRcbList);
+
+ if ( pRcb == NULL
+ || pRcvbuf->u.SendFileRequest.program_command == ALERT_CMD
+ || pRcvbuf->u.SendFileRequest.program_command != SEND_CMD
+ || pRcvbuf->u.SendFileRequest.conn_class != CLASS1) {
+ //
+ // Calling adapter was not found, or we received an alert frame,
+ // or we received an unknown frame, or request class was wrong.
+ //
+
+ if ( pRcb != NULL && status == 0
+ && pRcvbuf->u.SendFileRequest.program_command == ALERT_CMD) {
+ //
+ // Got an alert frame on LOAD_SAP and RCB is found.
+ //
+ pRcb->flags.alerted = RCB_ALERTED;// yes: signal
+ //
+ // Instead of ACK or rexmit request, we have Alert frame.
+ // Wake up worker.
+ //
+ if ( !SetEvent( pRcb->SF_wakeup)) {
+ RplDump( ++RG_Assert,("pRcb=0x%x error=%d", pRcb, GetLastError()));
+ }
+ }
+ if ( pRcb != NULL) {
+ pRcb->SFR = FALSE; // release RCB
+ }
+ if ( !RplDlcBufferFree( SFR_FREE, pAdapterInfo)) {
+ return( FALSE);
+ }
+ continue;
+ }
+
+ //
+ // Get SFR sequence number (in intel format) & validate it.
+ //
+ sfr_seq_number = HILO2(pRcvbuf->u.SendFileRequest.seq_num);
+ fdr_seq_number = pRcb->fdr_seq_number;
+ if ( sfr_seq_number > fdr_seq_number + 1) {
+ RplDump( ++RG_Assert, (
+ "pRcb=0x%x, sfr_seq_number=%d, fdr_seq_number=%d",
+ pRcb, sfr_seq_number, fdr_seq_number));
+ pRcb->SFR = FALSE; // release RCB
+ if ( !RplDlcBufferFree( SFR_FREE, pAdapterInfo)) {
+ return( FALSE);
+ }
+ continue;
+ }
+
+ //
+ // We received a meaningful SFR frame. Initialize data if this is
+ // the first SFR frame. Then update clients sequence number & unblock
+ // worker thread.
+ //
+
+ if ( pRcb->ReceivedSfr == FALSE) {
+ pRcb->ReceivedSfr = TRUE;
+ ProcessSfr( pRcb, pAdapterInfo, pRcvbuf);
+ }
+ pRcb->sfr_seq_number = sfr_seq_number;
+
+ if ( !SetEvent( pRcb->SF_wakeup)) {
+ RplDump( ++RG_Assert,( "pRcb=0x%x error=%d", pRcb, GetLastError()));
+ }
+
+ pRcb->SFR = FALSE; // release RCB
+
+ if ( !RplDlcBufferFree( SFR_FREE, pAdapterInfo)) {
+ return( FALSE);
+ }
+ } // end of big loop
+}
+
+
+#ifdef RPL_DEBUG
+VOID DebugCheckList( PPRCB HeadList, PRCB pInputRcb, DWORD Operation)
+{
+ PRCB pRcb;
+ DWORD count;
+ DWORD found;
+
+ for ( pRcb = *HeadList, count = 0, found = FALSE; pRcb != NULL;
+ pRcb = pRcb->next_rcb, count++) {
+ if ( count > 20) {
+ RplDump( ++RG_Assert,("Circular list"));
+ }
+ if ( pRcb == pInputRcb) {
+ found = TRUE;
+ }
+ }
+ if ( pInputRcb != NULL) {
+ if ( Operation == RPL_MUST_FIND && found == FALSE) {
+ RplDump( ++RG_Assert,("Element not present"));
+ } else if ( Operation == RPL_MUST_NOT_FIND && found == TRUE) {
+ RplDump( ++RG_Assert,("Element already present"));
+ }
+ }
+}
+#endif
+
diff --git a/private/net/svcdlls/rpl/dll/sizeof.c b/private/net/svcdlls/rpl/dll/sizeof.c
new file mode 100644
index 000000000..501dba2cf
--- /dev/null
+++ b/private/net/svcdlls/rpl/dll/sizeof.c
@@ -0,0 +1,174 @@
+/*++
+
+Copyright (c) 1987-93 Microsoft Corporation
+
+Module Name:
+
+ sizeof.c
+
+Abstract:
+
+ Prints offsets of more commonly used fields in RPL data structures.
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 03 - February - 1993
+
+Revision History:
+
+ 03-Feb-1993 vladimv
+ Created
+
+--*/
+
+#include "local.h"
+#include <ntcsrsrv.h>
+#include <stdio.h>
+
+typedef struct _HEAP_ENTRY {
+ //
+ // This field gives the size of the current block in allocation
+ // granularity units. (i.e. Size << HEAP_GRANULARITY_SHIFT
+ // equals the size in bytes).
+ //
+
+ USHORT Size;
+
+ //
+ // This field gives the size of the previous block in allocation
+ // granularity units. (i.e. PreviousSize << HEAP_GRANULARITY_SHIFT
+ // equals the size of the previous block in bytes).
+ //
+
+ USHORT PreviousSize;
+
+ //
+ // This field contains the index into the segment that controls
+ // the memory for this block.
+ //
+
+ UCHAR SegmentIndex;
+
+ //
+ // This field contains various flag bits associated with this block.
+ // Currently these are:
+ //
+ // 0x01 - block is busy
+ // 0x02 - allocated with VirtualAlloc
+ // 0x04 - busy block contains tail fill/free block contains free fill
+ // 0x08 - last block before uncommitted memory
+ // 0x10 - caller settable
+ // 0x20 - caller settable
+ // 0x40 - caller settable
+ // 0x80 - caller settable
+ //
+ // The high order 4 bits are settable by higher level heap interfaces.
+ // For example, the Win32 heap calls remember the DDESHARE and DISCARDABLE
+ // flags here. The pool manager remembers the pool type here.
+ //
+
+ UCHAR Flags;
+
+ //
+ // This field is for debugging purposes. It will normally contain a
+ // stack back trace index of the allocator for x86 systems.
+ //
+
+ union {
+ USHORT AllocatorBackTraceIndex;
+ struct {
+ UCHAR Index;
+ UCHAR Mask;
+ } FreeListBitIndex;
+ } u0;
+
+ //
+ // Format of the remaining fields depends on whether this is an
+ // busy or free block.
+ //
+
+ union {
+ struct {
+ //
+ // This field contains the number of unused bytes at the end of this
+ // block that were not actually allocated. Used to compute exact
+ // size requested prior to rounding requested size to allocation
+ // granularity. Also used for tail checking purposes.
+ //
+
+ ULONG UnusedBytes : 8;
+
+ //
+ // This field is currently unused, but is intended for storing
+ // any encoded value that will give the that gives the type of object
+ // allocated.
+ //
+
+ ULONG Type : 24;
+
+ //
+ // This field is a 32-bit settable value that a higher level heap package
+ // can use. The Win32 heap manager stores handle values in this field.
+ //
+
+ ULONG Settable;
+ } BusyBlock;
+
+ struct {
+ //
+ // Free blocks use these two words for linking together free blocks
+ // of the same size on a doubly linked list.
+ //
+ LIST_ENTRY FreeList;
+ } FreeBlock;
+ } u;
+
+} HEAP_ENTRY, *PHEAP_ENTRY;
+
+void _CRTAPI1 main( void) {
+ printf("FIELD_OFFSET( RCVBUF, u.SendFileRequest.seq_num) == %d == 0x%x\n",
+ FIELD_OFFSET( RCVBUF, u.SendFileRequest.seq_num),
+ FIELD_OFFSET( RCVBUF, u.SendFileRequest.seq_num));
+
+ printf("FIELD_OFFSET( OPEN_INFO, adapt_info_ptr) == %d == 0x%x\n",
+ FIELD_OFFSET( OPEN_INFO, adapt_info_ptr),
+ FIELD_OFFSET( OPEN_INFO, adapt_info_ptr));
+
+ printf("sizeof( ADAPTER_INFO) == %d == 0x%x\n",
+ sizeof( ADAPTER_INFO),
+ sizeof( ADAPTER_INFO));
+ printf("FIELD_OFFSET( ADAPTER_INFO, u1.r1.pFirstBuffer) == %d == 0x%x\n",
+ FIELD_OFFSET( ADAPTER_INFO, u1.r1.pFirstBuffer),
+ FIELD_OFFSET( ADAPTER_INFO, u1.r1.pFirstBuffer));
+ printf("FIELD_OFFSET( ADAPTER_INFO, u3.r1.Type.Event.pReceivedFrame) == %d == 0x%x\n",
+ FIELD_OFFSET( ADAPTER_INFO, u3.r1.Type.Event.pReceivedFrame),
+ FIELD_OFFSET( ADAPTER_INFO, u3.r1.Type.Event.pReceivedFrame));
+
+
+ printf("FIELD_OFFSET( FIND_FRAME, source_addr) == %d == 0x%x\n",
+ FIELD_OFFSET( FIND_FRAME, source_addr),
+ FIELD_OFFSET( FIND_FRAME, source_addr));
+
+ printf("FIELD_OFFSET( RCB, AdapterName) == %d == 0x%x\n",
+ FIELD_OFFSET( RCB, AdapterName),
+ FIELD_OFFSET( RCB, AdapterName));
+ printf("FIELD_OFFSET( RCB, lan_rcb_ptr) == %d == 0x%x\n",
+ FIELD_OFFSET( RCB, lan_rcb_ptr),
+ FIELD_OFFSET( RCB, lan_rcb_ptr));
+
+ printf("FIELD_OFFSET( LLC_NOT_CONTIGUOUS_BUFFER, auchLanHeader) == %d == 0x%x\n",
+ FIELD_OFFSET( LLC_NOT_CONTIGUOUS_BUFFER, auchLanHeader),
+ FIELD_OFFSET( LLC_NOT_CONTIGUOUS_BUFFER, auchLanHeader));
+
+ printf("FIELD_OFFSET( LLC_CCB, u.pParameterTable) == %d == 0x%x\n",
+ FIELD_OFFSET( LLC_CCB, u.pParameterTable),
+ FIELD_OFFSET( LLC_CCB, u.pParameterTable));
+
+ printf("FIELD_OFFSET( CSR_PROCESS, ProcessHandle) == %d == 0x%x\n",
+ FIELD_OFFSET( CSR_PROCESS, ProcessHandle),
+ FIELD_OFFSET( CSR_PROCESS, ProcessHandle));
+
+ printf("sizeof( HEAP_ENTRY) == %d == 0x%x\n",
+ sizeof( HEAP_ENTRY),
+ sizeof( HEAP_ENTRY));
+}
diff --git a/private/net/svcdlls/rpl/dll/sources b/private/net/svcdlls/rpl/dll/sources
new file mode 100644
index 000000000..f79e2d95d
--- /dev/null
+++ b/private/net/svcdlls/rpl/dll/sources
@@ -0,0 +1,37 @@
+MAJORCOMP=net
+MINORCOMP=rplnet
+
+TARGETPATH=obj
+TARGETNAME=rplnet
+TARGETTYPE=LIBRARY
+
+TARGETLIBS= \
+ $(BASEDIR)\Public\Sdk\Lib\*\netlib.lib \
+ $(BASEDIR)\public\sdk\lib\*\kernel32.lib \
+ $(BASEDIR)\public\sdk\lib\*\advapi32.lib \
+ $(BASEDIR)\public\sdk\lib\*\rpcndr.lib \
+ $(BASEDIR)\public\sdk\lib\*\rpcrt4.lib
+
+INCLUDES=.;..\inc;..\..\..\inc;..\..\..\api;..\..\..\..\inc;
+
+MSC_WARNING_LEVEL=/W3 /WX
+
+SOURCES= \
+ init.c \
+ ether.c \
+ emulator.c \
+ buffer.c \
+ fdr.c \
+ find.c \
+ found.c \
+ sfr.c \
+ report.c
+
+USE_CRTDLL=1
+C_DEFINES= -DINCL_32= -DNT -DRPC_NO_WINDOWS_H -DWIN32
+
+UMTYPE=console
+UMTEST=sizeof
+UMLIBS= \
+ $(BASEDIR)\Public\Sdk\Lib\*\dlcapi.lib
+
diff --git a/private/net/svcdlls/rpl/dll/xns.c b/private/net/svcdlls/rpl/dll/xns.c
new file mode 100644
index 000000000..e0b878e20
--- /dev/null
+++ b/private/net/svcdlls/rpl/dll/xns.c
@@ -0,0 +1,520 @@
+/*++
+
+Copyright (c) 1987-93 Microsoft Corporation
+
+Module Name:
+
+ xns.c
+
+Abstract:
+
+ Test program to help debug DIR_OPEN_DIRECT path in DLC driver & RPL service.
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 03 - February - 1993
+
+Revision History:
+
+ 03-Feb-1993 vladimv
+ Ported to NT
+
+--*/
+
+#include "local.h"
+#define PRINTF( _args) printf _args
+
+//
+// ripl_rom[] is the x86 code we send to ETHERSTART clients. Currently it
+// is exactly 0x429 bytes long.
+//
+BYTE ripl_rom[] = {
+0xFA,0xB8,0x90,0x00,0xBC,0x00,0x02,0x8E,0xD0,0xCD,0x12,0x86,0xC4,0x86,0xC4,0xB1,
+0x06,0xD3,0xE0,0x8D,0x0E,0x3C,0x0B,0x83,0xC1,0x0F,0x51,0xD1,0xE9,0xD1,0xE9,0xD1,
+0xE9,0xD1,0xE9,0x2B,0xC1,0x59,0x8E,0xC0,0x8D,0x36,0x39,0x01,0x8B,0xFE,0x2B,0xCE,
+0xE8,0x3B,0x02,0x50,0xB8,0x39,0x01,0x50,0xCB,0x0E,0x1F,0xB4,0xFB,0xB2,0x80,0x8D,
+0x1E,0x5F,0x04,0xCD,0x13,0x8B,0xF3,0x8D,0x3E,0x8A,0x04,0xA5,0xA5,0xA5,0x8B,0xF3,
+0x8D,0x3E,0xC7,0x04,0xA5,0xA5,0xA5,0x8B,0xF3,0x8D,0x3E,0xF2,0x04,0xA5,0xA5,0xA5,
+0xB4,0x03,0x32,0xFF,0xCD,0x10,0x32,0xD2,0x01,0x16,0xED,0x03,0x01,0x16,0x22,0x04,
+0x01,0x16,0x57,0x04,0xBE,0xC3,0x03,0x8B,0x16,0xED,0x03,0xBB,0x24,0x00,0x90,0xE8,
+0x0B,0x02,0xA1,0xED,0x03,0x05,0x24,0x00,0x90,0xA3,0xED,0x03,0xBE,0xE7,0x03,0x8B,
+0x16,0xED,0x03,0xBB,0x06,0x00,0x90,0xE8,0xF3,0x01,0xBE,0x68,0x00,0x90,0x8D,0x1E,
+0x59,0x04,0xE8,0xDA,0x01,0xBE,0xBE,0x05,0x8D,0x1E,0x2B,0x05,0xE8,0xD7,0x01,0x73,
+0x13,0x0B,0xC0,0x74,0x03,0xE9,0x8D,0x00,0xBE,0xE7,0x03,0xBB,0x06,0x00,0x90,0xE8,
+0xE3,0x01,0xEB,0xC8,0x8D,0x36,0x5B,0x05,0x8D,0x3E,0xC1,0x04,0xA5,0xA5,0xA5,0xBE,
+0xEF,0x03,0x8B,0x16,0x22,0x04,0xBB,0x2F,0x00,0x90,0xE8,0xB0,0x01,0xBE,0x24,0x04,
+0x8B,0x16,0x57,0x04,0xBB,0x2D,0x00,0x90,0xE8,0xA2,0x01,0xA1,0x22,0x04,0x05,0x2F,
+0x00,0x90,0xA3,0x22,0x04,0xA1,0x57,0x04,0x05,0x2D,0x00,0x90,0xA3,0x57,0x04,0xB9,
+0x50,0x00,0xBE,0x1E,0x04,0x8B,0x16,0x22,0x04,0xBB,0x04,0x00,0x90,0xE8,0x7D,0x01,
+0xBE,0x68,0x00,0x90,0x8D,0x1E,0xC1,0x04,0xE8,0x64,0x01,0xBE,0x51,0x04,0x8B,0x16,
+0x57,0x04,0xBB,0x06,0x00,0x90,0xE8,0x64,0x01,0xBE,0xBE,0x05,0x8D,0x1E,0x76,0x05,
+0xE8,0x53,0x01,0x73,0x28,0x0B,0xC0,0x75,0x0C,0xBE,0x1E,0x04,0xBB,0x04,0x00,0x90,
+0xE8,0x62,0x01,0xE2,0xBD,0xE8,0xF6,0x00,0xB8,0x40,0x00,0x8E,0xD8,0xC7,0x06,0x72,
+0x00,0x34,0x12,0xEA,0x00,0x00,0xFF,0xFF,0xB9,0x50,0x00,0xEB,0xBE,0x80,0x3E,0x84,
+0x05,0xFC,0x75,0xF4,0x81,0x3E,0x89,0x05,0x00,0x20,0x75,0xEC,0xA1,0xDC,0x04,0x39,
+0x06,0x91,0x05,0x74,0x02,0xEB,0x88,0x8B,0x16,0xDA,0x04,0x39,0x16,0x8F,0x05,0x74,
+0x03,0xE9,0x7B,0xFF,0x86,0xC4,0x86,0xD6,0x40,0x83,0xD2,0x00,0x86,0xC4,0x86,0xD6,
+0xA3,0xDC,0x04,0x89,0x16,0xDA,0x04,0xBE,0x51,0x04,0xBB,0x06,0x00,0x90,0xE8,0x04,
+0x01,0x8A,0x1E,0x9F,0x05,0xF6,0xC3,0x10,0x74,0x0D,0x53,0xBE,0x68,0x00,0x90,0x8D,
+0x1E,0xC1,0x04,0xE8,0xC9,0x00,0x5B,0xF6,0xC3,0x20,0x74,0x10,0xA1,0x99,0x05,0x86,
+0xE0,0xA3,0x34,0x0B,0xA1,0x97,0x05,0x86,0xE0,0xA3,0x36,0x0B,0x8B,0x0E,0xA0,0x05,
+0x86,0xCD,0x83,0xE9,0x04,0x0B,0xC9,0x74,0x37,0x33,0xD2,0xA1,0x34,0x0B,0xBF,0x10,
+0x00,0xF7,0xF7,0x8B,0xFA,0x03,0x06,0x36,0x0B,0x75,0x09,0x81,0xFA,0x00,0x0C,0x73,
+0x03,0xE9,0x51,0xFF,0x03,0xD1,0x89,0x16,0x34,0x0B,0xA3,0x36,0x0B,0x3D,0x00,0xA0,
+0x72,0x03,0xE9,0x40,0xFF,0x06,0x8E,0xC0,0x8D,0x36,0xA4,0x05,0xE8,0x5F,0x00,0x07,
+0xF6,0xC3,0x80,0x75,0x03,0xE9,0x40,0xFF,0xA1,0x38,0x0B,0x8B,0x0E,0x3A,0x0B,0xF6,
+0xC3,0x40,0x74,0x0B,0xA1,0x9D,0x05,0x86,0xC4,0x8B,0x0E,0x9B,0x05,0x86,0xCD,0x33,
+0xD2,0xBB,0x10,0x00,0xF7,0xF3,0x03,0xC1,0x50,0x52,0xE8,0x01,0x00,0xCB,0x06,0x1E,
+0x33,0xC0,0x8E,0xC0,0xB8,0x70,0x00,0x8E,0xD8,0xA1,0x02,0x00,0x26,0xA3,0x4C,0x00,
+0xA1,0x04,0x00,0x26,0xA3,0x4E,0x00,0xA1,0x06,0x00,0x26,0xA3,0x64,0x00,0xA1,0x08,
+0x00,0x26,0xA3,0x66,0x00,0x26,0xC6,0x06,0xD0,0x04,0x01,0x1F,0x07,0xC3,0xF7,0xC6,
+0x01,0x00,0x74,0x01,0xA4,0xD1,0xE9,0x9C,0xF3,0xA5,0x9D,0x73,0x01,0xA4,0xC3,0xB4,
+0xFE,0xB2,0x80,0xCD,0x13,0xC3,0xB4,0xFF,0xB2,0x80,0xCD,0x13,0xC3,0x50,0x51,0x53,
+0x8B,0xCB,0x32,0xFF,0xB4,0x02,0xCD,0x10,0xAC,0x33,0xDB,0xB4,0x0E,0xCD,0x10,0xE2,
+0xF7,0x5B,0x59,0x58,0xC3,0x50,0x51,0x8D,0x70,0xFF,0x8B,0xCB,0x8A,0x04,0x3C,0x39,
+0x7C,0x0A,0xB0,0x30,0x88,0x04,0x4E,0xE2,0xF3,0xEB,0x05,0x90,0xFE,0xC0,0x88,0x04,
+0x59,0x58,0xC3,0x53,0x65,0x61,0x72,0x63,0x68,0x69,0x6E,0x67,0x20,0x66,0x6F,0x72,
+0x20,0x52,0x50,0x4C,0x20,0x53,0x65,0x72,0x76,0x65,0x72,0x2C,0x20,0x52,0x65,0x74,
+0x72,0x69,0x65,0x73,0x20,0x3D,0x20,0x30,0x30,0x30,0x30,0x30,0x30,0x00,0x01,0x53,
+0x65,0x6E,0x64,0x69,0x6E,0x67,0x20,0x46,0x69,0x6C,0x65,0x20,0x52,0x65,0x71,0x75,
+0x65,0x73,0x74,0x73,0x20,0x74,0x6F,0x20,0x52,0x50,0x4C,0x20,0x53,0x65,0x72,0x76,
+0x65,0x72,0x2C,0x20,0x52,0x65,0x74,0x72,0x69,0x65,0x73,0x20,0x3D,0x20,0x30,0x30,
+0x30,0x30,0x00,0x03,0x57,0x61,0x69,0x74,0x69,0x6E,0x67,0x20,0x66,0x6F,0x72,0x20,
+0x44,0x61,0x74,0x61,0x20,0x46,0x72,0x61,0x6D,0x65,0x20,0x77,0x69,0x74,0x68,0x20,
+0x53,0x65,0x71,0x75,0x65,0x6E,0x63,0x65,0x20,0x4E,0x75,0x6D,0x62,0x65,0x72,0x3A,
+0x20,0x30,0x30,0x30,0x30,0x30,0x30,0x00,0x04,0x03,0x00,0x02,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x5A,0xFC,0xFC,0x03,0x00,0x57,0x00,0x01,0x00,0x08,
+0x40,0x03,0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x08,0x00,0x06,0x40,0x09,0x05,0xBE,
+0x00,0x06,0x40,0x0A,0x00,0x01,0x00,0x0A,0x40,0x06,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x05,0x40,0x07,0xFC,0x00,0x2C,0x00,0x04,0x00,0x24,0xC0,0x05,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x40,
+0x13,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x5A,0xF8,
+0xFC,0x03,0x00,0x57,0x00,0x10,0x00,0x08,0x40,0x11,0x00,0x00,0x00,0x00,0x00,0x10,
+0x00,0x08,0x00,0x06,0x40,0x09,0x05,0xBE,0x00,0x06,0x40,0x0A,0x00,0x01,0x00,0x0A,
+0x40,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x40,0x07,0xFC,0x00,0x2C,0x00,
+0x04,0x00,0x24,0xC0,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x40,0x13};
+WORD sizeof_ripl_rom = sizeof( ripl_rom); // equal to 0x429
+
+BYTE Swap[256] =
+{
+0x00,0x80,0x40,0xc0,0x20,0xa0,0x60,0xe0,0x10,0x90,0x50,0xd0,0x30,0xb0,0x70,0xf0,
+0x08,0x88,0x48,0xc8,0x28,0xa8,0x68,0xe8,0x18,0x98,0x58,0xd8,0x38,0xb8,0x78,0xf8,
+0x04,0x84,0x44,0xc4,0x24,0xa4,0x64,0xe4,0x14,0x94,0x54,0xd4,0x34,0xb4,0x74,0xf4,
+0x0c,0x8c,0x4c,0xcc,0x2c,0xac,0x6c,0xec,0x1c,0x9c,0x5c,0xdc,0x3c,0xbc,0x7c,0xfc,
+0x02,0x82,0x42,0xc2,0x22,0xa2,0x62,0xe2,0x12,0x92,0x52,0xd2,0x32,0xb2,0x72,0xf2,
+0x0a,0x8a,0x4a,0xca,0x2a,0xaa,0x6a,0xea,0x1a,0x9a,0x5a,0xda,0x3a,0xba,0x7a,0xfa,
+0x06,0x86,0x46,0xc6,0x26,0xa6,0x66,0xe6,0x16,0x96,0x56,0xd6,0x36,0xb6,0x76,0xf6,
+0x0e,0x8e,0x4e,0xce,0x2e,0xae,0x6e,0xee,0x1e,0x9e,0x5e,0xde,0x3e,0xbe,0x7e,0xfe,
+0x01,0x81,0x41,0xc1,0x21,0xa1,0x61,0xe1,0x11,0x91,0x51,0xd1,0x31,0xb1,0x71,0xf1,
+0x09,0x89,0x49,0xc9,0x29,0xa9,0x69,0xe9,0x19,0x99,0x59,0xd9,0x39,0xb9,0x79,0xf9,
+0x05,0x85,0x45,0xc5,0x25,0xa5,0x65,0xe5,0x15,0x95,0x55,0xd5,0x35,0xb5,0x75,0xf5,
+0x0d,0x8d,0x4d,0xcd,0x2d,0xad,0x6d,0xed,0x1d,0x9d,0x5d,0xdd,0x3d,0xbd,0x7d,0xfd,
+0x03,0x83,0x43,0xc3,0x23,0xa3,0x63,0xe3,0x13,0x93,0x53,0xd3,0x33,0xb3,0x73,0xf3,
+0x0b,0x8b,0x4b,0xcb,0x2b,0xab,0x6b,0xeb,0x1b,0x9b,0x5b,0xdb,0x3b,0xbb,0x7b,0xfb,
+0x07,0x87,0x47,0xc7,0x27,0xa7,0x67,0xe7,0x17,0x97,0x57,0xd7,0x37,0xb7,0x77,0xf7,
+0x0f,0x8f,0x4f,0xcf,0x2f,0xaf,0x6f,0xef,0x1f,0x9f,0x5f,0xdf,0x3f,0xbf,0x7f,0xff
+};
+
+void ReverseBits( PBYTE NodeAddress) {
+ DWORD i;
+ static CHAR Hex[] = "0123456789ABCDEF";
+ for ( i = 0; i < NODE_ADDRESS_LENGTH; i++) {
+ NodeAddress[ i] = Swap[ NodeAddress[ i] ];
+ PRINTF(( "%c", Hex[ NodeAddress[i]>>4]));
+ PRINTF(( "%c", Hex[ NodeAddress[i] & 0x0F]));
+ }
+ PRINTF(( "\n"));
+}
+
+HANDLE hCompletionEvent;
+LLC_CCB Ccb;
+PLLC_CCB pBadCcb;
+PLLC_CCB pCcb;
+
+LLC_DIR_OPEN_ADAPTER_PARMS DirOpenAdapterParms;
+LLC_ADAPTER_OPEN_PARMS AdapterOpenParms;
+LLC_EXTENDED_ADAPTER_PARMS ExtendedAdapterParms;
+LLC_DLC_PARMS DlcParms;
+
+LLC_BUFFER_CREATE_PARMS BufferCreate;
+PVOID pBuffer;
+DWORD cbBufferSize;
+
+LLC_DIR_STATUS_PARMS DirStatusParms;
+LLC_DIR_OPEN_DIRECT_PARMS DirOpenDirectParms;
+LLC_RECEIVE_PARMS ReceiveParms;
+
+LLC_TRANSMIT_PARMS TransmitParms;
+
+BOOL EtherAcsLan( VOID)
+{
+ DWORD Status;
+
+ pCcb->hCompletionEvent = hCompletionEvent;
+ if ( !ResetEvent( pCcb->hCompletionEvent)) {
+ PRINTF(("EtherAcsLan: Reset(), Status=%d\n", GetLastError()));
+ return( FALSE);
+ }
+ Status = AcsLan( pCcb, &pBadCcb);
+ if ( Status != ACSLAN_STATUS_COMMAND_ACCEPTED) {
+ PRINTF(("EtherAcsLan: Acslan, Status=0x%x\n", Status));
+ return( FALSE);
+ }
+ Status = WaitForSingleObject( pCcb->hCompletionEvent, INFINITE);
+ PRINTF(("EtherAcsLan: DlcCommand=0x%x\n", Ccb.uchDlcCommand));
+ if ( Status != WAIT_OBJECT_0 || pCcb->uchDlcStatus) {
+ if ( Status == -1) {
+ Status = GetLastError();
+ }
+ PRINTF(("EtherAcsLan: Acslan, Status=0x%x, DlcStatus=0x%x\n", Status, pCcb->uchDlcStatus));
+ return( FALSE);
+ }
+
+ //
+ // Due to a DLC bug "pNext" field is trashed even on success
+ // code path. Until the bug is fix we must reinstate NULL.
+ //
+ pCcb->pNext = NULL;
+ return( TRUE);
+}
+
+BOOL RplDirCloseAdapter( VOID) {
+ (VOID)memset((PVOID)&Ccb, '\0', sizeof(Ccb));
+ Ccb.uchDlcCommand = LLC_DIR_CLOSE_ADAPTER;
+ return( EtherAcsLan());
+}
+
+BOOL RplDirOpenAdapter( VOID) {
+ (VOID)memset((PVOID)&Ccb, '\0', sizeof(Ccb));
+ Ccb.uchDlcCommand = LLC_DIR_OPEN_ADAPTER;
+ Ccb.u.pParameterTable = (PLLC_PARMS)&DirOpenAdapterParms;
+ DirOpenAdapterParms.pAdapterParms = &AdapterOpenParms;
+ DirOpenAdapterParms.pExtendedParms = &ExtendedAdapterParms;
+ ExtendedAdapterParms.pSecurityDescriptor = NULL;
+ //
+ // With LLC_ETHERNET_TYPE_DEFAULT rpl server on build 392 did not
+ // detect an 802.3 client FIND frame.
+ //
+#define OLD_STUFF
+#ifdef OLD_STUFF
+ // Old stuff
+ ExtendedAdapterParms.LlcEthernetType = LLC_ETHERNET_TYPE_802_3;
+#else
+ // Antti's email
+ ExtendedAdapterParms.LlcEthernetType = LLC_ETHERNET_TYPE_DIX;
+#endif
+ ExtendedAdapterParms.hBufferPool = NULL; // for first open
+ DirOpenAdapterParms.pDlcParms = &DlcParms;
+ return( EtherAcsLan());
+}
+
+BOOL RplBufferCreate( VOID) {
+ (VOID)memset((PVOID)&Ccb, '\0', sizeof(Ccb));
+ Ccb.uchDlcCommand = LLC_BUFFER_CREATE;
+ Ccb.u.pParameterTable = (PLLC_PARMS)&BufferCreate;
+ BufferCreate.pBuffer = pBuffer;
+ BufferCreate.cbBufferSize = cbBufferSize;
+ BufferCreate.cbMinimumSizeThreshold = 0x2000;
+ return( EtherAcsLan());
+}
+
+BOOL RplDirStatus( VOID) {
+ USHORT network_type;
+ (VOID)memset((PVOID)&Ccb, '\0', sizeof(Ccb));
+ Ccb.uchDlcCommand = LLC_DIR_STATUS;
+ Ccb.u.pParameterTable = (PLLC_PARMS)&DirStatusParms;
+ if ( !EtherAcsLan()) {
+ return( FALSE);
+ }
+ network_type = DirStatusParms.usAdapterType;
+ if ( network_type != RPL_ADAPTER_ETHERNET) {
+ PRINTF(( "network_type=0x%x\n", network_type));
+ return( FALSE);
+ }
+ ReverseBits( DirStatusParms.auchNodeAddress);
+ return( TRUE);
+}
+
+BOOL RplDirOpenDirect( VOID) {
+ (VOID)memset((PVOID)&Ccb, '\0', sizeof(Ccb));
+ Ccb.uchDlcCommand = LLC_DIR_OPEN_DIRECT;
+ Ccb.uchAdapterNumber = 0;
+ Ccb.u.pParameterTable = (PLLC_PARMS)&DirOpenDirectParms;
+ memset((PCH)&DirOpenDirectParms, '\0', sizeof(DirOpenDirectParms));
+ DirOpenDirectParms.usOpenOptions = LLC_DIRECT_OPTIONS_ALL_MACS; // BUGBUG ??
+ DirOpenDirectParms.usEthernetType = 0x0600; // XNS identifier
+ DirOpenDirectParms.ulProtocolTypeMask = 0xFFFFFFFF;
+ DirOpenDirectParms.ulProtocolTypeMatch = 0x7200FFFF;
+ DirOpenDirectParms.usProtocolTypeOffset = 14; // where FFFF0072 of client begins
+ return( EtherAcsLan());
+}
+
+BOOL RplReceive( VOID) {
+ (VOID)memset( (PVOID)&Ccb, '\0', sizeof(Ccb));
+ (VOID)memset( (PVOID)&ReceiveParms, '\0', sizeof(ReceiveParms));
+ Ccb.uchDlcCommand = LLC_RECEIVE;
+ Ccb.u.pParameterTable = (PLLC_PARMS)&ReceiveParms;
+ ReceiveParms.usStationId = 0; // receive MAC & non-MAC frames
+ return( EtherAcsLan());
+}
+
+
+//
+// The following are XNS packet definitions used to crack EtherStart
+// packets.
+//
+
+#include <packon.h> // pack EtherStart structures
+
+struct sockaddr_ns {
+ DWORD net;
+ BYTE host[ NODE_ADDRESS_LENGTH];
+ WORD socket;
+};
+
+#define ETHERSTART_SOCKET 0x0104 // After swapping bytes
+
+struct _IDP {
+ WORD chksum;
+ WORD len;
+ BYTE xport_cntl;
+ BYTE packet_type;
+ struct sockaddr_ns dest;
+ struct sockaddr_ns src;
+};
+
+#define XNS_NO_CHKSUM 0xffff // XNS checksum
+#define PEX_TYPE 0x4 // packet exchange type
+
+struct _PEX {
+ DWORD pex_id;
+ WORD pex_client;
+};
+
+#define ETHERSERIES_CLIENT 0x0080 // After swapping bytes
+
+struct _ETHERSTART {
+ WORD ethershare_ver;
+ BYTE unit;
+ BYTE fill;
+ WORD block;
+ BYTE func;
+ BYTE error;
+ WORD bytes;
+};
+
+#define ETHERSHARE_VER 0
+#define FUNC_RESPONSE 0x80
+#define FUNC_READFILEREQ 0x20
+#define FUNC_READFILERESP (FUNC_READFILEREQ | FUNC_RESPONSE)
+
+typedef struct _ETHERSTART_REQ { // EtherStart Read File Request
+ struct _IDP idp;
+ struct _PEX pex;
+ struct _ETHERSTART es;
+ BYTE filename[64];
+ WORD start;
+ WORD count;
+} ETHERSTART_REQ;
+
+typedef struct _ETHERSTART_RESP { // EtherStart Read File Response
+ struct _IDP idp;
+ struct _PEX pex;
+ struct _ETHERSTART es;
+ BYTE data[0x200];
+} ETHERSTART_RESP;
+
+
+PRCVBUF pRcvbuf;
+ETHERSTART_REQ * EtherstartReq;
+ETHERSTART_RESP EtherstartResp;
+//
+// When we use LAN_HEADER then destination address begins at offset
+// 14 of XmitData, while source address begins at offset 20.
+// However, DLC scrambles the data so that destination address begins
+// at offset 0 (instead of 2) - while source address still begins at
+// offset 6 - which is correct. LAN_HEADER_TOO is meant as yet another
+// DLC workaround.
+//
+typedef struct _LAN_HEADER_TOO {
+ BYTE dest_addr[ NODE_ADDRESS_LENGTH]; // Destination address
+ BYTE source_addr[ NODE_ADDRESS_LENGTH]; // Source address
+ BYTE routing_info_header[2]; // Routing information hdr
+} LAN_HEADER_TOO;
+
+struct {
+ XMIT_BUFFER XmitBuffer; // see comment for XMIT_BUFFER
+ LAN_HEADER_TOO LanHeader; // BUGBUG replaces LAN_HEADER
+} XmitQueue;
+
+#include <packoff.h> // restore default packing (done with EtherStart structures)
+
+VOID RplCopy( PVOID destination, PVOID source, DWORD length) {
+ memcpy( destination, source, length);
+}
+
+
+BOOL RplSend( VOID)
+{
+ WORD Temp;
+
+ (VOID)memset( (PVOID)&Ccb, '\0', sizeof(Ccb));
+ Ccb.uchDlcCommand = LLC_TRANSMIT_DIR_FRAME;
+
+ Ccb.u.pParameterTable = (PLLC_PARMS)&TransmitParms;
+ memset( (PVOID)&TransmitParms, '\0', sizeof(TransmitParms));
+ TransmitParms.uchXmitReadOption = DLC_DO_NOT_CHAIN_XMIT;
+
+ //
+ // Initialize XmitQueue which contains the first send buffer.
+ //
+ TransmitParms.pXmitQueue1 = (LLC_XMIT_BUFFER *)&XmitQueue;
+ memset( (PVOID)&XmitQueue, '\0', sizeof( XmitQueue.XmitBuffer));
+ XmitQueue.XmitBuffer.cbBuffer = sizeof( XmitQueue.LanHeader);
+ //
+ // In case of a direct open auchLanHeader[] returned by DLC contains
+ // LAN_HEADER structure without physical control fields. For now
+ // we work around this DLC bug by using offset 6 instead of 8 as a
+ // source address offset in auchLanHeader[].
+ //
+#define RPLDLC_SOURCE_OFFSET 6 // BUGBUG not 8 due to dlc bug
+ RplCopy( XmitQueue.LanHeader.dest_addr,
+ (PBYTE)&pRcvbuf->b.auchLanHeader[ RPLDLC_SOURCE_OFFSET],
+ NODE_ADDRESS_LENGTH);
+ RplCopy( XmitQueue.LanHeader.source_addr,
+ DirStatusParms.auchNodeAddress,
+ NODE_ADDRESS_LENGTH);
+ //
+ // These two are important for token ring already.
+ // I was hoping they will set XNS identifier field itself.
+ //
+ XmitQueue.LanHeader.routing_info_header[0] = 0x06;
+ XmitQueue.LanHeader.routing_info_header[1] = 0x00;
+
+ //
+ // Initialize EtherstartResp which contains the second & the last
+ // send buffer.
+ //
+ TransmitParms.pBuffer1 = (PVOID)&EtherstartResp;
+ Temp = min( EtherstartReq->count,
+ (WORD)(sizeof_ripl_rom - EtherstartReq->start));
+ TransmitParms.cbBuffer1 = (WORD)( sizeof(EtherstartResp) -
+ sizeof( EtherstartResp.data) + Temp);
+ EtherstartResp.idp.chksum = XNS_NO_CHKSUM;
+ EtherstartResp.idp.len = HILO( TransmitParms.cbBuffer1);
+ EtherstartResp.idp.packet_type = PEX_TYPE;
+ RplCopy( EtherstartResp.idp.dest.host,
+ (PBYTE)&pRcvbuf->b.auchLanHeader[ RPLDLC_SOURCE_OFFSET],
+ NODE_ADDRESS_LENGTH);
+ EtherstartResp.idp.dest.socket = ETHERSTART_SOCKET;
+ RplCopy( EtherstartResp.idp.src.host, DirStatusParms.auchNodeAddress,
+ NODE_ADDRESS_LENGTH);
+ EtherstartResp.idp.src.socket = ETHERSTART_SOCKET;
+ EtherstartResp.pex.pex_id = EtherstartReq->pex.pex_id;
+ EtherstartResp.pex.pex_client = ETHERSERIES_CLIENT;
+ EtherstartResp.es.func = FUNC_READFILERESP;
+ EtherstartResp.es.bytes = Temp; // stays intel ordered
+ RplCopy( EtherstartResp.data, &ripl_rom[ EtherstartReq->start], Temp);
+ return( EtherAcsLan());
+}
+
+PCHAR RG_EtherFileName[] = { // names of all legal boot request types
+ "BOOTPC.COM",
+ "BOOTSTAT.COM",
+ NULL
+ };
+int RG_EtherFileNameLength[] = { // strlen of the above strings
+ 10,
+ 12,
+ 0
+ };
+
+BOOL RplCrack( VOID)
+{
+ DWORD index;
+ PCHAR pFileName;
+
+ pRcvbuf = (PRCVBUF)ReceiveParms.pFirstBuffer;
+ EtherstartReq = (ETHERSTART_REQ *)&pRcvbuf->u;
+ //
+ // Make sure this request is for us.
+ //
+ if ( EtherstartReq->idp.packet_type != PEX_TYPE ||
+ EtherstartReq->pex.pex_client != ETHERSERIES_CLIENT ||
+ EtherstartReq->es.func != FUNC_READFILEREQ) {
+ return( FALSE); // this request is not for us
+ }
+
+ //
+ // Make sure this is a legal file request.
+ //
+ for ( index = 0, pFileName = RG_EtherFileName[ index];
+ pFileName != NULL;
+ pFileName = RG_EtherFileName[ ++index] ) {
+ if ( !memcmp( EtherstartReq->filename, pFileName,
+ RG_EtherFileNameLength[ index])) {
+ break;
+ }
+ }
+ if ( pFileName == NULL) {
+ return( FALSE); // this is not a legal file name request
+ }
+ return( TRUE);
+}
+
+BOOL Worker( VOID) {
+ if ( !RplBufferCreate()) {
+ return( FALSE);
+ }
+ if ( !RplDirStatus()) {
+ return( FALSE);
+ }
+ if ( !RplDirOpenDirect()) {
+ return( FALSE);
+ }
+ for ( ; ; ) {
+ if ( !RplReceive()) {
+ return( FALSE);
+ }
+ if ( !RplCrack()) {
+ return( FALSE);
+ }
+ if ( !RplSend()) {
+ return( FALSE);
+ }
+ }
+ return( TRUE);
+}
+
+
+BOOL _CRTAPI1 main( void) {
+ BOOL success;
+ pCcb = &Ccb;
+ hCompletionEvent = CreateEvent(
+ NULL, // no security attributes
+ TRUE, // use manual reset
+ TRUE, // initial state is signalled
+ NULL // no name
+ );
+ if ( hCompletionEvent == NULL) {
+ PRINTF(( "CreateEvent() error\n", GetLastError()));
+ return( FALSE);
+ }
+ cbBufferSize = 0xA000;
+ pBuffer = GlobalAlloc( GMEM_FIXED, cbBufferSize);
+ if ( pBuffer == NULL) {
+ PRINTF(( "GlobalAlloc() error\n", GetLastError()));
+ return( FALSE);
+ }
+ if ( !RplDirOpenAdapter()) {
+ return( FALSE);
+ }
+ success = Worker();
+ RplDirCloseAdapter();
+ return( success);
+}
+
+
diff --git a/private/net/svcdlls/rpl/doc/lm21ddk.doc b/private/net/svcdlls/rpl/doc/lm21ddk.doc
new file mode 100644
index 000000000..1a7bb04e4
--- /dev/null
+++ b/private/net/svcdlls/rpl/doc/lm21ddk.doc
Binary files differ
diff --git a/private/net/svcdlls/rpl/doc/rpl.doc b/private/net/svcdlls/rpl/doc/rpl.doc
new file mode 100644
index 000000000..cb3be7592
--- /dev/null
+++ b/private/net/svcdlls/rpl/doc/rpl.doc
Binary files differ
diff --git a/private/net/svcdlls/rpl/exe/makefile b/private/net/svcdlls/rpl/exe/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/net/svcdlls/rpl/exe/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/net/svcdlls/rpl/exe/rplsvc.c b/private/net/svcdlls/rpl/exe/rplsvc.c
new file mode 100644
index 000000000..953cd8a8e
--- /dev/null
+++ b/private/net/svcdlls/rpl/exe/rplsvc.c
@@ -0,0 +1,146 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ rplsvc.c
+
+Abstract:
+
+ This is the main routine for the Remote Program Load Service.
+
+Notes:
+
+ BUGBUG For now rplsvc.exe is built in ..\server directory
+ since building it in ..\server is more convenient.
+ The code here may be out of date compared to ..\server\rplsvc.c.
+
+ You must modify ..\server to build rplsvc.lib (instead of rplsvc.exe)
+ in order for this code to link.
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 10 - Februrary - 1993
+
+Environment:
+
+ User Mode - Win32
+
+Revision History:
+
+ 10-Feb-1993 vladimv
+ created
+
+--*/
+
+//
+// INCLUDES
+//
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windows.h>
+#include <winsvc.h> // Service control APIs
+
+#include <lmcons.h>
+#include <lmerr.h> // NERR_ and ERROR_ equates.
+#include <lmsname.h>
+
+#include <rpcutil.h> // NetpInitRpcServer()
+#include <netdebug.h> // NetpKdPrint(())
+#include <secobj.h> // NetpCreateWellKnownSids()
+
+
+//
+// Function Prototypes.
+//
+
+VOID
+RPL_main(
+ DWORD argc,
+ LPWSTR * argv
+ );
+
+//
+// Dispatch table for all services. Passed to NetServiceStartCtrlDispatcher.
+//
+// Add new service entries here.
+//
+
+SERVICE_TABLE_ENTRY RPLServiceDispatchTable[] = {
+ { SERVICE_RIPL, RPL_main },
+ { NULL, NULL }
+};
+
+
+
+VOID _CRTAPI1
+main (
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ This is a main routine for the LANMan Remote Program Load Services.
+
+ It basically sets up the ControlDispatcher and, on return, exits from
+ this main thread. The call to NetServiceStartCtrlDispatcher does
+ not return until all services have terminated, and this process can
+ go away.
+
+ It will be up to the ControlDispatcher thread to start/stop/pause/continue
+ any services. If a service is to be started, it will create a thread
+ and then call the main routine of that service.
+
+
+Arguments:
+
+ Anything passed in from the "command line". Currently, NOTHING.
+
+Return Value:
+
+ NONE
+
+Note:
+
+
+--*/
+{
+ NTSTATUS ntstatus;
+
+ //
+ // Create well-known SIDs
+ //
+ if (! NT_SUCCESS (ntstatus = NetpCreateWellKnownSids(NULL))) {
+ NetpKdPrint(("[RplSvc] Failed to create well-known SIDs %08lx\n",
+ ntstatus));
+ return;
+ }
+
+
+ //
+ // Initialize the RpcServer Locks.
+ //
+
+ NetpInitRpcServer();
+
+ //
+ // Call StartServiceCtrlDispatcher to set up the control interface.
+ // The API won't return until all services have been terminated. At that
+ // point, we just exit.
+ //
+
+ if (! StartServiceCtrlDispatcher ( RPLServiceDispatchTable)) {
+ //
+ // BUGBUG: Log an event for failing to start control dispatcher
+ //
+ NetpKdPrint(("[RplSvc] Failed to start control dispatcher %lu\n",
+ GetLastError()));
+ }
+
+ ExitProcess(0);
+}
diff --git a/private/net/svcdlls/rpl/exe/rplsvc.rc b/private/net/svcdlls/rpl/exe/rplsvc.rc
new file mode 100644
index 000000000..b2fc29e3c
--- /dev/null
+++ b/private/net/svcdlls/rpl/exe/rplsvc.rc
@@ -0,0 +1,11 @@
+#include <windows.h>
+
+#include <ntverp.h>
+
+#define VER_FILETYPE VFT_APP
+#define VER_FILESUBTYPE VFT2_UNKNOWN
+#define VER_FILEDESCRIPTION_STR "Net Remote Program Load Service App"
+#define VER_INTERNALNAME_STR "atrpl.exe"
+
+#include "common.ver"
+
diff --git a/private/net/svcdlls/rpl/exe/sources b/private/net/svcdlls/rpl/exe/sources
new file mode 100644
index 000000000..eaac46918
--- /dev/null
+++ b/private/net/svcdlls/rpl/exe/sources
@@ -0,0 +1,97 @@
+!IF 0
+
+Copyright (c) 1989-92 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+
+Author:
+
+ Steve Wood (stevewo) 12-Apr-1989
+
+
+Revision History:
+
+!ENDIF
+
+#
+# The TARGETNAME variable is defined by the developer. It is the name of
+# the target (component) that is being built by this makefile. It
+# should NOT include any path or file extension information.
+#
+
+MAJORCOMP =net
+MINORCOMP =rplsvc
+TARGETNAME=rplsvc
+
+#
+# The TARGETPATH and TARGETTYPE varialbes are defined by the developer.
+# The first specifies where the target is to be build. The second specifies
+# the type of target (either PROGRAM, DYNLINK or LIBRARY)
+#
+
+TARGETPATH=obj
+
+TARGETTYPE=PROGRAM
+
+TARGETLIBS= \
+ ..\server\obj\*\rplsvc.lib \
+ ..\dll\obj\*\rplnet.lib \
+ $(BASEDIR)\Public\Sdk\Lib\*\dlcapi.lib \
+ $(BASEDIR)\public\sdk\lib\*\netlib.lib \
+ $(BASEDIR)\public\sdk\lib\*\netapi32.lib \
+ $(BASEDIR)\public\sdk\lib\*\rpcrt4.lib \
+ $(BASEDIR)\public\sdk\lib\*\rpcutil.lib \
+ $(BASEDIR)\public\sdk\lib\*\rpcndr.lib
+
+#
+# The INCLUDES variable specifies any include paths that are specific to
+# this source directory. Separate multiple directory paths with single
+# semicolons. Relative path specifications are okay.
+#
+
+INCLUDES=..\..\..\inc;..\..\..\..\inc;$(BASEDIR)\public\sdk\inc
+
+#
+# The SOURCES variable is defined by the developer. It is a list of all the
+# source files for this component. Each source file should be on a separate
+# line using the line continuation character. This will minimize merge
+# conflicts if two developers adding source files to the same component.
+#
+
+!IFNDEF DISABLE_NET_UNICODE
+UNICODE=1
+NET_C_DEFINES=-DUNICODE
+!ENDIF
+
+#
+# BUGBUG Messages are not bound properly with RplSvc.exe if it
+# BUGBUG built from within the exe directory.
+#
+
+MSC_WARNING_LEVEL=/W3 /WX
+
+SOURCES= rplsvc.c rplsvc.rc
+
+UMTYPE=console
+UMLIBS=$(BASEDIR)\public\sdk\lib\*\netapi32.lib
+
+
+#
+# Defining the NTTARGETFILES variable causes MAKEFILE.DEF to attempt to
+# include .\makefile.inc immediately after it specifies the top
+# level targets (all, clean and loc) and their dependencies. MAKEFILE.DEF
+# also expands the value of the NTTARGETFILES variable at the end of the
+# list of dependencies for the all target. Useful for specifying additional
+# targets and dependencies that don't fit the general case covered by
+# MAKEFILE.DEF
+#
diff --git a/private/net/svcdlls/rpl/imports.idl b/private/net/svcdlls/rpl/imports.idl
new file mode 100644
index 000000000..3cca04e23
--- /dev/null
+++ b/private/net/svcdlls/rpl/imports.idl
@@ -0,0 +1,63 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ imports.idl
+
+Abstract:
+
+ This file is useful for creating RPC interfaces that require the use
+ of windef types. The .idl file for the RPC product should contain a
+ line in the interface body that imports this file. For example:
+
+ import "imports.h";
+
+ Doing this causes the MIDL generated header file to contain the
+ following line:
+
+ #include "imports.h"
+
+ If this technique is not used, and instead the .idl file for the RPC
+ product simply contains #include <importsf.h>, then the contents of
+ imports.h will be expanded in the MIDL generated header file. This
+ can lead to duplicate definition problems later when the RPC client
+ or RPC server code needs to include both the MIDL generated header file
+ and a file that is included in imports.h.
+
+Author:
+
+ Dan Lafferty (danl) 20-Mar-1991
+
+Environment:
+
+ User Mode - Win32 - for use with the MIDL compiler
+
+
+Revision History:
+
+ 03-Apr-1991 danl
+ created
+
+--*/
+
+[
+ uuid(12345678-1234-ABCD-EF00-9948756789AB),
+#ifdef __midl
+ ms_union,
+#endif // __midl
+ version(1.0)
+]
+interface imports
+
+{
+#define MIDL_PASS
+#include "imports.h"
+
+//
+// All .idl files need to contain at least one function prototype
+//
+
+DWORD Dummy( [in] DWORD DummyParm);
+}
diff --git a/private/net/svcdlls/rpl/inc/adapter.h b/private/net/svcdlls/rpl/inc/adapter.h
new file mode 100644
index 000000000..744173cad
--- /dev/null
+++ b/private/net/svcdlls/rpl/inc/adapter.h
@@ -0,0 +1,52 @@
+/*++
+
+Module Name:
+
+ adapter.h
+
+Abstract:
+
+ Describes layout of JET database table used for ADAPTER structures.
+
+--*/
+
+#ifdef RPLADAPTER_ALLOCATE
+#define EXTERN_ADAPTER
+#define INIT_ADAPTER( _x) = _x
+#else
+#define EXTERN_ADAPTER extern
+#define INIT_ADAPTER( _x)
+#endif
+
+//
+// Indices of entries in AdapterTable[] - server column array.
+//
+#define ADAPTER_AdapterName 0
+#define ADAPTER_AdapterComment 1
+#define ADAPTER_Flags 2
+#define ADAPTER_TABLE_LENGTH 3
+
+RPL_COLUMN_INFO AdapterTable[]
+#ifdef RPLADAPTER_ALLOCATE
+ = {
+ { "AdapterName", JET_coltypBinary, 0}, // address of unknown rpl client
+ { "AdapterComment", JET_coltypBinary, 0}, // default comment
+ { "Flags", JET_coltypLong, 0}
+}
+#endif // RPLADAPTER_ALLOCATE
+;
+
+//
+// This definition gives wrong result when RPLADAPTER_ALLOCATE is not defined
+//#define ADAPTER_TABLE_LENGTH (sizeof(AdapterTable)/sizeof(AdapterTable[0]))
+//
+
+#define ADAPTER_INDEX_AdapterName "foo" // + AdapterName
+
+#define ADAPTER_TABLE_NAME "Adapter"
+#define ADAPTER_TABLE_PAGE_COUNT 5 // initial number of 4K pages
+#define ADAPTER_TABLE_DENSITY 100 // initial density
+
+#ifdef RPL_RPLCNV
+EXTERN_ADAPTER JET_TABLEID AdapterTableId;
+#endif // RPL_RPLCNV
diff --git a/private/net/svcdlls/rpl/inc/boot.h b/private/net/svcdlls/rpl/inc/boot.h
new file mode 100644
index 000000000..2abb4babb
--- /dev/null
+++ b/private/net/svcdlls/rpl/inc/boot.h
@@ -0,0 +1,59 @@
+/*++
+
+Module Name:
+
+ boot.h
+
+Abstract:
+
+ Describes layout of JET database table used for BOOT structures.
+
+--*/
+
+#ifdef RPLBOOT_ALLOCATE
+#define EXTERN_BOOT
+#define INIT_BOOT( _x) = _x
+#else
+#define EXTERN_BOOT extern
+#define INIT_BOOT( _x)
+#endif
+
+//
+// Indices of entries in BootTable[] - server column array.
+//
+#define BOOT_BootName 0
+#define BOOT_BootComment 1
+#define BOOT_Flags 2
+#define BOOT_VendorName 3
+#define BOOT_BbcFile 4
+#define BOOT_WindowSize 5
+#define BOOT_VendorId 6
+#define BOOT_TABLE_LENGTH 7
+
+RPL_COLUMN_INFO BootTable[]
+#ifdef RPLBOOT_ALLOCATE
+ = {
+ { "BootName", JET_coltypBinary, 0}, // id of boot block record
+ { "BootComment", JET_coltypBinary, 0}, // comment for boot block record
+ { "Flags", JET_coltypLong, 0},
+ { "VendorName", JET_coltypBinary, 0}, // VendorId as hex unicode string
+ { "BbcFile", JET_coltypBinary, 0}, // BBC file name
+ { "WindowSize", JET_coltypLong, 0}, // used with acknowledgments
+ { "VendorId", JET_coltypLong, 0} // common adapter id digits
+}
+#endif // RPLBOOT_ALLOCATE
+;
+
+//
+// This definition gives wrong result when RPLBOOT_ALLOCATE is not defined
+//#define BOOT_TABLE_LENGTH (sizeof(BootTable)/sizeof(BootTable[0]))
+//
+
+#define BOOT_INDEX_VendorIdBootName "foo" // + VendorId + BootName
+#define BOOT_INDEX_BootName "goo" // + BootName
+
+#define BOOT_TABLE_NAME "Boot"
+#define BOOT_TABLE_PAGE_COUNT 5 // initial number of 4K pages
+#define BOOT_TABLE_DENSITY 100 // initial density
+
+EXTERN_BOOT JET_TABLEID BootTableId;
diff --git a/private/net/svcdlls/rpl/inc/config.h b/private/net/svcdlls/rpl/inc/config.h
new file mode 100644
index 000000000..6cb135433
--- /dev/null
+++ b/private/net/svcdlls/rpl/inc/config.h
@@ -0,0 +1,95 @@
+/*++
+
+Module Name:
+
+ config.h
+
+Abstract:
+
+ Describes layout of JET database table used for CONFIG structures.
+
+--*/
+
+#ifdef RPLCONFIG_ALLOCATE
+#define EXTERN_CONFIG
+#define INIT_CONFIG( _x) = _x
+#else
+#define EXTERN_CONFIG extern
+#define INIT_CONFIG( _x)
+#endif
+
+#define CONFIG_ConfigName 0
+#define CONFIG_ConfigComment 1
+#define CONFIG_Flags 2
+#define CONFIG_BootName 3
+#define CONFIG_DirName 4
+#define CONFIG_DirName2 5
+#define CONFIG_DirName3 6
+#define CONFIG_DirName4 7
+#define CONFIG_FitShared 8
+#define CONFIG_FitPersonal 9
+#define CONFIG_TABLE_LENGTH 10
+
+EXTERN_CONFIG RPL_COLUMN_INFO ConfigTable[ CONFIG_TABLE_LENGTH]
+#ifdef RPLCONFIG_ALLOCATE
+ = {
+ { "ConfigName", JET_coltypBinary, 0},
+ { "ConfigComment", JET_coltypBinary, 0},
+ { "Flags", JET_coltypLong, 0},
+ { "BootName", JET_coltypBinary, 0},
+ { "DirName", JET_coltypBinary, 0},
+ { "DirName2", JET_coltypBinary, 0},
+ { "DirName3", JET_coltypBinary, 0},
+ { "DirName4", JET_coltypBinary, 0},
+ { "FitShared", JET_coltypBinary, 0},
+ { "FitPersonal", JET_coltypBinary, 0}
+}
+#endif // RPLCONFIG_ALLOCATE
+;
+
+//
+// The block below is needed by RPLCNV.EXE only. We include it here
+// because it is essential that order of elements and lengths of
+// ConfigTable and ConfigParseTable arrays are kept in sync.
+//
+#ifdef RPL_RPLCNV
+typedef struct _CONFIG_PARSE_INFO {
+ PWCHAR Name; // RPLMGR.INI name - NULL for new entries
+ PVOID Value; // RPLMGR.INI value
+ DWORD Size; // size in bytes of the above value
+} CONFIG_PARSE_INFO, *PCONFIG_PARSE_INFO;
+EXTERN_CONFIG CONFIG_PARSE_INFO ConfigParseTable[ CONFIG_TABLE_LENGTH]
+#ifdef RPLCONFIG_ALLOCATE
+ = {
+ { L"name", NULL, 0},
+ { L"comment", NULL, 0},
+ { NULL, NULL, 0}, // a placeholder for Flags fields
+ { L"bblink", NULL, 0},
+ { L"dirname", NULL, 0},
+ { L"dirname2", NULL, 0},
+ { L"dirname3", NULL, 0},
+ { L"dirname4", NULL, 0},
+ { L"fitfileshared", NULL, 0},
+ { L"fitfilepersonal", NULL, 0}
+}
+#endif // RPLCONFIG_ALLOCATE
+;
+#endif // RPL_RPLCNV
+
+
+//
+// This definition gives wrong result when RPLCONFIG_ALLOCATE is not defined
+//#define CONFIG_TABLE_LENGTH (sizeof(ConfigTable)/sizeof(ConfigTable[0]))
+//
+
+#define CONFIG_INDEX_ConfigName "foo" // +ConfigName
+#define CONFIG_INDEX_BootNameConfigName "goo" // +BootName+ConfigName
+
+#define CONFIG_TABLE_NAME "Config"
+#define CONFIG_TABLE_PAGE_COUNT 10 // initial number of 4K pages
+#define CONFIG_TABLE_DENSITY 80 // initial density
+
+#ifdef RPL_RPLCNV
+EXTERN_CONFIG JET_TABLEID ConfigTableId;
+#endif // RPL_RPLCNV
+
diff --git a/private/net/svcdlls/rpl/inc/i_lmrpl.h b/private/net/svcdlls/rpl/inc/i_lmrpl.h
new file mode 100644
index 000000000..1cbde89b6
--- /dev/null
+++ b/private/net/svcdlls/rpl/inc/i_lmrpl.h
@@ -0,0 +1,26 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ i_lmrpl.h
+
+Abstract:
+
+ This file desribes hidden structures used with RPL service apis.
+ It is a private complement of lmrpl.h public file.
+
+ Currently structures defined in this file are mostly used as
+ placeholders for future extensions, and to suppress MIDL warnings
+ and to make sure code is already set to support different levels.
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 18-Nov-1993
+
+--*/
+
+typedef struct _RPL_INFO_1 {
+ DWORD AdapterPolicy; // bitmask
+} RPL_INFO_1, *PRPL_INFO_1, *LPRPL_INFO_1;
diff --git a/private/net/svcdlls/rpl/inc/imports.h b/private/net/svcdlls/rpl/inc/imports.h
new file mode 100644
index 000000000..5b2991ab0
--- /dev/null
+++ b/private/net/svcdlls/rpl/inc/imports.h
@@ -0,0 +1,45 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ imports.h
+
+Abstract:
+
+ This file allows us to include standard system header files in the
+ .idl file. The main .idl file imports a file called import.idl.
+ This allows the .idl file to use the types defined in these header
+ files. It also causes the following line to be added in the
+ MIDL generated header file:
+
+ #include "imports.h"
+
+ Thus these types are available to the RPC stub routines as well.
+
+Author:
+
+ Dan Lafferty (danl) 07-May-1991
+
+Revision History:
+
+
+--*/
+
+
+#include <windef.h>
+#include <lmcons.h>
+
+#ifdef MIDL_PASS
+#ifdef UNICODE
+#define LPTSTR [string] wchar_t*
+#else
+#define LPTSTR [string] LPTSTR
+#endif
+#define LPSTR [string] LPSTR
+#define BOOL DWORD
+#endif
+
+#include <lmrpl.h> // public definitions
+#include "i_lmrpl.h" // internal definitions
diff --git a/private/net/svcdlls/rpl/inc/profile.h b/private/net/svcdlls/rpl/inc/profile.h
new file mode 100644
index 000000000..a012ec86c
--- /dev/null
+++ b/private/net/svcdlls/rpl/inc/profile.h
@@ -0,0 +1,62 @@
+/*++
+
+Module Name:
+
+ profile.h
+
+Abstract:
+
+ Describes layout of JET database table used for PROFILE structures.
+
+--*/
+
+#ifdef RPLPROFILE_ALLOCATE
+#define EXTERN_PROFILE
+#define INIT_PROFILE( _x) = _x
+#else
+#define EXTERN_PROFILE extern
+#define INIT_PROFILE( _x)
+#endif
+
+//
+// Indices of entries in ProfileTable[] - profile column array.
+//
+#define PROFILE_ProfileName 0
+#define PROFILE_ProfileComment 1
+#define PROFILE_ConfigName 2
+#define PROFILE_BootName 3
+#define PROFILE_FitShared 4
+#define PROFILE_FitPersonal 5
+#define PROFILE_Flags 6
+#define PROFILE_TABLE_LENGTH 7
+
+EXTERN_PROFILE RPL_COLUMN_INFO ProfileTable[ PROFILE_TABLE_LENGTH]
+#ifdef RPLPROFILE_ALLOCATE
+ = {
+ { "ProfileName", JET_coltypBinary, 0}, // profile name
+ { "ProfileComment", JET_coltypBinary, 0}, // profile comment
+ { "ConfigName", JET_coltypBinary, 0}, // configuration name
+ { "BootName", JET_coltypBinary, 0}, // boot block id
+ { "FitShared", JET_coltypBinary, 0}, // fit file for wksta sharing
+ { "FitPersonal", JET_coltypBinary, 0}, // fit file for wksta exclusive
+ { "Flags", JET_coltypLong, 0}
+}
+#endif // RPLPROFILE_ALLOCATE
+;
+//
+// This definition gives wrong result when RPLPROFILE_ALLOCATE is not defined
+//#define PROFILE_TABLE_LENGTH (sizeof(ProfileTable)/sizeof(ProfileTable[0]))
+//
+
+#define PROFILE_INDEX_ProfileName "foo" // +ProfileName
+#define PROFILE_INDEX_BootNameProfileName "goo" // +BootName+ProfileName
+#define PROFILE_INDEX_ConfigNameProfileName "hoo" // +ConfigName+ProfileName
+
+#define PROFILE_TABLE_NAME "Profile"
+#define PROFILE_TABLE_PAGE_COUNT 5 // initial number of 4K pages
+#define PROFILE_TABLE_DENSITY 100 // initial density
+
+#ifdef RPL_RPLCNV
+EXTERN_PROFILE JET_TABLEID ProfileTableId;
+#endif // RPL_RPLCNV
+
diff --git a/private/net/svcdlls/rpl/inc/riplcons.h b/private/net/svcdlls/rpl/inc/riplcons.h
new file mode 100644
index 000000000..cfa6674cf
--- /dev/null
+++ b/private/net/svcdlls/rpl/inc/riplcons.h
@@ -0,0 +1,130 @@
+/*++
+
+Module Name:
+
+ riplcons.h
+
+Abstract:
+
+ RPL constants
+
+--*/
+
+#define RPL_SUCCESS NO_ERROR // defined to be ZERO (LONG).
+
+#define INVALID_FILE_OFFSET ((DWORD)(-1L)) // for use with SetFilePointer()
+
+#define BITSET( i, mask) ( (mask & (1<<i)) != 0)
+
+
+//
+// Bits and values of type file in file map
+// Bits 0 - 3 are reserved for the types of data files and
+// special cases. Bits 4 - 7 are boolean bits of executable and sys
+// files. When bits 0 - 3 are used, bits 4 - 7 bust be reset.
+
+// IS_EXE_SYS bit is set for exe-s and device drivers that have exe-format
+
+#define IS_BIN_SYS 0x0000
+#define IS_EXE_SYS 0x0100 // set if loadable file has exe-format
+#define IS_COM_FILE 0x0200
+#define IS_EXE_FILE 0x0300
+#define IS_MOVEABLE 0x0400
+#define EXEC_IN_LOW_MEM 0x0800
+#define EXEC_IN_FREE_MEM 0x1000
+#define OTHER_FILES_MASK 0x00FF
+#define BINARY_LOADER 15
+#define DATA_FILE 14
+#define BINARY_LOADER_DATA 13
+#define LOADER_PARAM 12
+#define RPL_BOOT_TYPE 11
+#define IS_EXE_FORMAT 0x0100
+#define IS_EXECUTABLE_FILE 0x0200
+
+#define DRV_TYPE 0x0000
+#define EXE_TYPE 0x0200
+#define ORG_TYPE 1
+#define BASE_TYPE 6
+#define UNKNOWN_CONFIG_TYPE 7
+
+//
+// work station types of returned in parameter wksta_type
+//
+
+#define PCDOS_WKSTA 0
+#define OS2_WKSTA 1
+
+//
+// The patched offsets of RPLBOOT.SYS
+//
+
+#define BBLOCK_BASE_INDEX 8
+#define MAX_NLS_NUM 5
+#define NLS_BASE_INDEX 10
+#define NLS_INCREMENT 8
+#define NO_CHKSUM_USED 1963
+
+//
+// Misc conts
+//
+
+#define LM20_WKSTA_LINE 257 // max rpl.map wksta line len supported by rplmfsd.sys (of lm 2.0?)
+
+
+//
+// these constants are used only rplservr, not in rplboot.asm, sorry
+//
+
+#define OFFSET_RPLBOOT_HDR 16
+#define NLS_VERSION_10 1 // 1st version number of NLS
+#define BBVERSION_10 3 // Boot block format V2.1(Free type)
+
+
+//
+// Parameter substitution strings for TCPIP protocol.ini parameters
+// used by RPLBOOT.SYS
+//
+
+#define TEMPLATESIZE 15
+#define TCPIP_PREFIX "(TCPIP"
+#define TCPIP_ADDRESS "(TCPIP_ADDRESS)"
+#define TCPIP_SUBMASK "(TCPIP_SUBMASK)"
+#define TCPIP_GATEWAY "(TCPIP_GATEWAY)"
+
+#define TILDE_CHAR L'~'
+#define SPACE_CHAR L' '
+#define COMMENT_CHAR L';'
+#define LINE_FEED_CHAR L'\n' // 0xA or \012
+#define NEW_LINE_CHAR LINE_FEED_CHAR
+
+#define MAXWORD ((WORD)-1) // it is not safe to compare WORDs with -1
+
+#define END_OF_TEXT_FILE_CHAR ((WCHAR)0x1a) // ctrl-Z == 'Z' - 0x40
+
+#define RPL_MAX_PROFILE_NAME_SIZE ((RPL_MAX_PROFILE_NAME_LENGTH + 1) * sizeof(WCHAR))
+#define RPL_MAX_WKSTA_NAME_SIZE ((RPL_MAX_WKSTA_NAME_LENGTH + 1) * sizeof(WCHAR))
+#define RPL_MAX_CONFIG_NAME_SIZE ((RPL_MAX_CONFIG_NAME_LENGTH + 1) * sizeof(WCHAR))
+#define RPL_MAX_BOOT_NAME_SIZE ((RPL_MAX_BOOT_NAME_LENGTH + 1) * sizeof(WCHAR))
+
+#define RPL_MAX_ADAPTER_NAME_LENGTH RPL_ADAPTER_NAME_LENGTH
+#define RPL_MAX_ADAPTER_NAME_SIZE ((RPL_MAX_ADAPTER_NAME_LENGTH + 1) * sizeof(WCHAR))
+
+#define RPL_MAX_VENDOR_NAME_LENGTH RPL_VENDOR_NAME_LENGTH
+#define RPL_MAX_VENDOR_NAME_SIZE ((RPL_MAX_VENDOR_NAME_LENGTH + 1) * sizeof(WCHAR))
+
+
+
+//
+// RPL_EVENTLOG_NAME is the name of registry key (under EventLog service)
+// used to interpret events for RPL service.
+//
+
+#define RPL_EVENTLOG_NAME TEXT( "RemoteBoot")
+
+
+//
+// Define this flag to move to JET500
+// Note that the "sources" files must be updated seperately to refer to
+// JET.LIB or JET500.LIB.
+//
+#define __JET500 1
diff --git a/private/net/svcdlls/rpl/inc/ripltyps.h b/private/net/svcdlls/rpl/inc/ripltyps.h
new file mode 100644
index 000000000..bd62bf9e9
--- /dev/null
+++ b/private/net/svcdlls/rpl/inc/ripltyps.h
@@ -0,0 +1,137 @@
+/*++
+
+Module Name: ripltyps.h
+
+--*/
+
+struct _PARAGRAPH {
+ union {
+ DWORD dwords[4];
+ WORD words[8];
+ BYTE bytes[16];
+ } t;
+} PARAGRAPH, *PPARAGRAPH;
+
+//****************************************************************
+//
+// The boot block structure is totally redefined in LM 2.1, because
+// the extending of the LM 2.0 generated too complicated code.
+//
+// All these data structures are in the boot block header. Thus
+// they are common for RPLSERVR and RPLBOOT. The boot data is the
+// parameter block of RPL MiniFSD and the resource table is for
+// OS/2 1.2 Loader. There must be something other for OS/2 2.0 because
+// paragraph addresses can't be used (kernel > 640 kB).
+//
+// Boot block is a file list including a header. There
+// are also the standard dos parameters and some special RIPL data
+// stuctures in the header.
+//
+// The boot block root is actually the header of RPLBOOT.SYS file
+// (that can be anywhere in the boot block).
+// Remote IPL ROM is requested to jump to there. RPLBOOT finds
+// the boot block base address in its header (patched by RPLSERVR).
+// All pointers (except the file 32 bit physical pointers) in boot
+// block headers are offsets relative to the boot block base address.
+//
+//****************************************************************
+
+// data structure used in RPLBOOT offset 16,
+typedef struct rplboot_header {
+ BYTE achIdStamp[3]; // "RPL" string for identification
+ BYTE bNul; // terminator
+ BYTE bBbVersion; // version number of boot block format
+ BYTE bNlsVersion; // version number of NLS patches (1 = make)
+ WORD usNlsPatchOff; // offset from file start of NLS patch files
+ DWORD phBootBlockHeader; // 32 bits physical address
+} RPLBOOT_HEADER, *PRPLBOOT_HEADER;
+
+//
+// The segment of parameter and name pointers is the segment of
+// file list table. The parameter block of executable files
+// will be copied to the PSP offset 80H (Program Segment Prefix).
+// The handle I/O and the error messages of rplboot requires
+// file names. The check sum is not calculated, if the chk_sum
+// field is 1963 ('magic number').
+//
+
+//
+// It may not be required to pack structures below, but for testing
+// purposes it is useful since this should give us 1-1 correspondence
+// with OS/2 rpl server.
+//
+// FILE_DATA packed size is 0x16 => unpacked is 0x18
+// BOOT_BLOCK_HDR packed size is 0x12 => unpacked is 0x14
+//
+#include <packon.h> // pack structures in boot block
+
+typedef struct _FILE_DATA {
+ DWORD file_addr; // 32 bit physical address of file
+ DWORD file_len; // file length (bytes)
+ DWORD extra_mem; // extra memory (bytes)
+ WORD name_offset; // offset of name string
+ WORD param_offset; // offset of parameter block
+ WORD chk_sum; // 16 bit add checksum of the file
+ WORD file_type; // type of file
+ BYTE param_len; // length of params block
+ BYTE pad1; // pad this to next even byte
+} FILE_DATA, *PFILE_DATA;
+
+// new boot block header structure used in LAN Manager 2.1 (and later)
+// the offsets are relative from the boot block base address
+
+typedef struct _BOOT_BLOCK_HDR {
+ WORD id_stamp; // if 0x18cd (int 18H) => extended structure
+ WORD usInfoLevel; // info level of this structure
+ WORD cbSize; // size of boot block header struct (and data)
+ WORD file_list_len; // length of file list table
+ WORD offRplWkstaLine; // offset ASCIZ rpl.map wksta line (any length)
+ WORD offResourceTbl; // pointer of IFS resource table
+ WORD cbResourceTbl; // size of resource table struct
+ WORD offBootData; // offset of LM 2.0 level boot structure
+ WORD offMBootData; // offset of the buffer used in the multi boot
+ FILE_DATA aFileData[1]; // dynamic array for file data
+} BOOT_BLOCK_HDR, *PBOOT_BLOCK_HDR;
+
+// DON't MODIFY, the structures fixed in 1.2 LDR spec
+
+typedef struct _RESOURCE {
+ WORD pos_in_paras; // paragraph of the file in PC memory
+ DWORD file_len; // length of file in bytes
+} RESOURCE;
+
+typedef struct _RESOURCE_TABLE {
+ WORD entries; // number of entries 4 (or 3 if no BootData)
+ RESOURCE file_tbl[4];
+} RESOURCE_TBL, *PRESOURCE_TBL;
+
+
+//
+// BootData structure is fixed in RPL MFSD,
+// negotiate with Randy before any changes
+// All offsets are relative from the beginning of the structure.
+//
+
+typedef struct _BOOT_DATA {
+ WORD cbSize; // size of the whole structure
+ DWORD int_5c; // netbios interrupt saved by rplboot.sys
+ WORD cbWkstaLine; // size of wksta line
+ WORD offWkstaLine; // offset of ASCIIZ wksta line
+ WORD cbFit; // size of the FIT file
+ WORD offFit; // offset of ASCIIZ FIT file
+ WORD cbCodePage; // size of NLS Code Page
+ WORD offCodePage; // offset of Code Page Table Dynamic data area for the buffers
+} BOOT_DATA, *PBOOT_DATA;
+
+//
+// Defines the structure of the element in a variable len multiboot
+// info table
+//
+typedef struct _MBOOTDATA {
+ WORD cbSize; // size of the dynamic structure
+ BYTE achProfile[16]; // profile name
+ BYTE achProfileComment[1]; // variable len comment
+} MBOOTDATA, *PMBOOTDATA;
+
+#include <packoff.h> // restore default packing (done with boot block)
+
diff --git a/private/net/svcdlls/rpl/inc/rpldb.h b/private/net/svcdlls/rpl/inc/rpldb.h
new file mode 100644
index 000000000..3990ced31
--- /dev/null
+++ b/private/net/svcdlls/rpl/inc/rpldb.h
@@ -0,0 +1,30 @@
+/*++
+
+Module Name:
+
+ rpldb.h
+
+Abstract:
+
+ Main include file for JET database portion of RPL service & RPL convert
+ program.
+
+
+ Auxiliary include files (adapter.h , boot.h, config.h, profile.h &
+ wksta.h) are JET table specific.
+
+--*/
+
+#define RPL_SERVICE_DATABASE "rplsvc.mdb"
+#define RPL_SERVICE_DATABASE_W L"rplsvc.mdb"
+#define RPL_SYSTEM_DATABASE "system.mdb"
+#define RPL_SYSTEM_DATABASE_W L"system.mdb"
+#define RPL_TEMP_DATABASE "temp.mdb"
+#define RPL_TEMP_DATABASE_W L"temp.mdb"
+
+typedef struct _RPL_COLUMN_INFO {
+ CHAR * ColumnName;
+ JET_COLTYP ColumnType;
+ JET_COLUMNID ColumnId;
+} RPL_COLUMN_INFO, *PRPL_COLUMN_INFO;
+
diff --git a/private/net/svcdlls/rpl/inc/rpldebug.h b/private/net/svcdlls/rpl/inc/rpldebug.h
new file mode 100644
index 000000000..d1a0a6ec7
--- /dev/null
+++ b/private/net/svcdlls/rpl/inc/rpldebug.h
@@ -0,0 +1,88 @@
+/*++
+
+Module:
+
+ rpldebug.h
+
+Abstract:
+
+ Definitions of debug data structures.
+
+--*/
+
+#if DBG
+#define RPL_DEBUG
+#endif // DBG
+
+#define NODE_ADDRESS_LENGTH 6
+
+#ifdef RPL_DEBUG
+
+extern int RG_DebugLevel; // needed by other modules
+extern int RG_Assert; // needed by other modules
+
+VOID _CRTAPI1 RplDebugPrint( CONST CHAR * format, ...);
+
+#define RplDump( DBG_CONDITION, DBG_PRINT_ARGUMENTS) \
+ { \
+ if ( DBG_CONDITION) { \
+ if ( RG_Assert!= 0) { \
+ RplDebugPrint( "File %s, Line %d\n", __FILE__, __LINE__); \
+ } \
+ RplDebugPrint DBG_PRINT_ARGUMENTS; \
+ } \
+ }
+
+#define RPL_RETURN( arg) RplDump(++RG_Assert,("File %s, Line %d\n", __FILE__, __LINE__)); return( arg)
+
+//
+// We do not define
+//
+// #define RPL_ASSERT( condition) ASSERT( condition)
+//
+// because we want to break even on a free build and in user mode
+// (when rplsvc runs under ntsd).
+//
+#define RPL_ASSERT( condition) \
+ { \
+ if ( !( condition)) { \
+ ++RG_Assert; \
+ RplDebugPrint( "File %s, Line %d\n", __FILE__, __LINE__); \
+ } \
+ }
+
+#define RPL_DEBUG_MEMALLOC ((DWORD)0x00000001) // memory allocations
+#define RPL_DEBUG_INIT ((DWORD)0x00000002)
+#define RPL_DEBUG_FDR ((DWORD)0x00000004) // file data response
+#define RPL_DEBUG_FIND ((DWORD)0x00000008) // find
+#define RPL_DEBUG_FOUND ((DWORD)0x00000010) // found
+#define RPL_DEBUG_SFR ((DWORD)0x00000020) // send file request
+#define RPL_DEBUG_MISC ((DWORD)0x00000040) // miscelaneous
+#define RPL_DEBUG_REQUEST ((DWORD)0x00000080) // request thread
+#define RPL_DEBUG_SERVICE ((DWORD)0x00000100)
+#define RPL_DEBUG_WORKER ((DWORD)0x00000200) // worker thread
+#define RPL_DEBUG_MEMORY ((DWORD)0x00000400) // other memory
+#define RPL_DEBUG_FLOW ((DWORD)0x00000800) // other flow
+#define RPL_DEBUG_FLOWINIT ((DWORD)0x00001000) // init flow
+#define RPL_DEBUG_ETHER ((DWORD)0x00002000) // EtherStart
+#define RPL_DEBUG_DB ((DWORD)0x00004000) // misc database
+#define RPL_DEBUG_CONFIG ((DWORD)0X00008000) // Config apis
+#define RPL_DEBUG_RESUME ((DWORD)0X00010000) // Resume key usage
+#define RPL_DEBUG_PROFILE ((DWORD)0X00020000) // Profile apis
+#define RPL_DEBUG_WKSTA ((DWORD)0X00040000) // Wksta apis
+#define RPL_DEBUG_ADAPTER ((DWORD)0X00080000) // Adapter apis
+#define RPL_DEBUG_BOOT ((DWORD)0X00100000) // Boot record usage
+#define RPL_DEBUG_VENDOR ((DWORD)0X00200000) // Vendor record usage
+#define RPL_DEBUG_SPECIAL ((DWORD)0X00400000) // for one time bugs
+
+#else // RPL_DEBUG
+
+#define RPL_RETURN( arg) return( arg)
+#define RPL_RETURN( arg) return( arg)
+#define RPL_ASSERT( condition)
+
+#define RplDump( DBG_CONDITION, DBG_PRINT_ARGUMENTS) \
+ do {NOTHING;} while (0)
+
+#endif // RPL_DEBUG
+
diff --git a/private/net/svcdlls/rpl/inc/rpldll.h b/private/net/svcdlls/rpl/inc/rpldll.h
new file mode 100644
index 000000000..4a606f487
--- /dev/null
+++ b/private/net/svcdlls/rpl/inc/rpldll.h
@@ -0,0 +1,144 @@
+/*++
+
+Module:
+
+ rpldll.h
+
+Abstract:
+
+ DLL interface: return codes, data structures & function declarations.
+
+--*/
+
+//
+// Data structures used in the DLL function declarations.
+//
+
+typedef struct _OPEN_INFO {
+ //
+ // Adapter info buffer should be large enough to hold private adapter
+ // info and the maximum amount of memory needed for DLC buffer pool.
+ //
+ PBYTE adapt_info_ptr; // Buffer for adapter private information.
+ DWORD adapt_info_size; // Size of above buffer.
+ BYTE NodeAddress[ NODE_ADDRESS_LENGTH]; // burned-in adapter num
+ DWORD lan_rcb_size; // size of the network specific RCB
+} OPEN_INFO, *POPEN_INFO;
+
+typedef struct _RCB { // Resource Control Block
+ struct _RCB * next_rcb; // the next rcb
+ PBYTE lan_rcb_ptr; // ptr to the network specific resource block
+ DWORD sfr_seq_number; // number of requested packet, set by RPL ROM
+ DWORD fdr_seq_number; // number of the next packet to be sent
+
+ DWORD max_frame; // size of data block
+
+ BYTE ReceivedSfr; // have we received an SFR's from this client
+
+ struct { // bitfield
+
+ BYTE alerted: 1; // SAP F8 got an ALERT frame
+ BYTE spframe: 2; // special frame is last one
+ BYTE rcberr:1; // RECEIVE on SAP F8 got an error
+ BYTE defboot:1; // If DLL supports default boot, DLL sets this
+ // to ask server to use default boot
+ } flags;
+
+ //
+ // "send_flags" is used for FLAGS field in FileDataResponse frame.
+ // In OS/2 days RPL service pretended not to know about the DLC layer,
+ // but even then it manipulated "send_flags" defined here.
+ //
+ struct { // bitfield
+ BYTE reserved :4;
+ BYTE ack_request :1;
+ BYTE locate_enable :1;
+ BYTE xfer_enable :1;
+ BYTE end_of_file :1;
+ } send_flags;
+
+ //
+ // SF_wakeup is an EVENT created in RplGetRcbFromList.
+ //
+ HANDLE SF_wakeup; // event for wakeup
+
+ //
+ // thxsemhdl is an EVENT created in RplWorkerThread.
+ //
+ HANDLE txsemhdl; // event used for transmitting data
+
+ BYTE NodeAddress[ NODE_ADDRESS_LENGTH]; // of remote client
+ WCHAR AdapterName[ 2 * NODE_ADDRESS_LENGTH + 1]; // above in hex UNICODE
+
+ //
+ // volume_id is ASCIIZ string of chosen boot. It is set by the DLL
+ // if protocol supports it.
+ //
+ CHAR volume_id[ 17];
+
+ BOOL SFR; // is SFR thread working on this RCB
+ BOOL Pending; // is RCB in a transient state
+ BOOL RcbIsBad; // if we should get rid of this RCB
+} RCB, *PRCB, **PPRCB;
+
+
+//
+// Functions exported by ..\dll\init.c
+//
+BOOL RplDlcInit(
+ POPEN_INFO pOpenInfo,
+ DWORD rpl_adapter
+ );
+BOOL RplDlcTerm( POPEN_INFO pOpenInfo);
+
+//
+// Functions exported by ..\dll\find.c
+//
+BOOL RplDlcFind(
+ POPEN_INFO pOpenInfo,
+ PRCB pRcb
+ );
+
+//
+// Functions exported by ..\dll\found.c
+//
+BOOL RplDlcFound(
+ POPEN_INFO pOpenInfo,
+ PRCB pRcb
+ );
+
+//
+// Functions exported by ..\dll\fdr.c
+//
+BOOL RplDlcFdr(
+ POPEN_INFO pOpenInfo,
+ PRCB pRcb,
+ PBYTE data_ptr,
+ WORD length,
+ DWORD locate_addr,
+ DWORD start_addr
+ );
+
+
+//
+// Functions exported by ..\dll\sfr.c
+//
+BOOL RplDlcSfr(
+ POPEN_INFO pOpenInfo,
+ PPRCB HeadRcbList,
+ PCRITICAL_SECTION ProtectRcbList
+ );
+PRCB RplFindRcb(
+ PPRCB HeadRcbList,
+ PBYTE NodeAddress
+ );
+#ifdef RPL_DEBUG
+#define RPL_MUST_FIND 1
+#define RPL_MUST_NOT_FIND 2
+VOID DebugCheckList( PPRCB HeadList, PRCB pInputRcb, DWORD Operation);
+#else
+#define DebugCheckList( HeadList, pInputRcb, Operation)
+#endif
+
+
+
diff --git a/private/net/svcdlls/rpl/inc/rpllib.h b/private/net/svcdlls/rpl/inc/rpllib.h
new file mode 100644
index 000000000..b3361878b
--- /dev/null
+++ b/private/net/svcdlls/rpl/inc/rpllib.h
@@ -0,0 +1,94 @@
+/*++
+
+Module Name:
+
+ rpllib.h
+
+Abstract:
+
+ Desribes exports from lib subirectory.
+
+ Also used for macros used by several RPL subdirectories - because
+ all of them include this file.
+
+--*/
+
+#define RPL_STRING_TOO_LONG(_x_) (_x_!=NULL && wcslen(_x_)>RPL_MAX_STRING_LENGTH)
+
+//
+// Exports from ..\lib\adapter.c
+//
+BOOL ValidHexName( IN PWCHAR Name, IN DWORD NameLength, IN BOOL MustHaveInput);
+BOOL ValidName( IN PWCHAR Name, IN DWORD MaxNameLength, IN BOOL MustHaveInput);
+
+//
+// Exports from ..\lib\tcpip.c
+//
+DWORD FillTcpIpString( OUT PCHAR Buffer, IN DWORD AddressDword);
+
+//
+// Exports from ..\lib\addkey.c
+//
+DWORD AddKey( OUT PCHAR Target, IN CHAR Prefix, IN PCHAR Source);
+
+//
+// Exports from ..\lib\jeterror.c
+//
+DWORD MapJetError( IN JET_ERR JetError);
+
+//
+// Exports from ..\lib\report.c
+//
+VOID RplReportEvent(
+ IN DWORD MessageId,
+ IN LPWSTR InsertionString,
+ IN DWORD RawDataBufferLength OPTIONAL,
+ IN LPVOID RawDataBuffer
+ );
+
+//
+// Exports from ..\lib\cmdline.c
+//
+
+VOID RplPrintf0(
+ IN DWORD MessageId
+ );
+VOID RplPrintf1(
+ IN DWORD MessageId,
+ IN PWCHAR InsertionString
+ );
+VOID RplPrintf2(
+ IN DWORD MessageId,
+ IN PWCHAR InsertionString1,
+ IN PWCHAR InsertionString2
+ );
+VOID RplPrintfN(
+ IN DWORD MessageId,
+ IN PWCHAR * Parameters,
+ IN DWORD NumParameters
+ );
+VOID RplPrintfID(
+ IN DWORD MessageId,
+ IN DWORD MessageIdInsertion
+ );
+VOID RplSPrintfN(
+ IN DWORD MessageId,
+ IN PWCHAR * Parameters,
+ IN DWORD NumParameters,
+ OUT PWCHAR * MessageStringPtr
+ );
+
+//
+// Exports from ..\lib\reg.c
+//
+
+DWORD RplRegRead(
+ OUT DWORD * pAdapterPolicy,
+ OUT DWORD * pBackupInterval,
+ OUT DWORD * pMaxThreadCount,
+ OUT PWCHAR DirectoryBuffer,
+ IN OUT DWORD * pDirectoryBufferSize
+ );
+DWORD RplRegSetPolicy(
+ IN DWORD NewAdapterPolicy
+ );
diff --git a/private/net/svcdlls/rpl/inc/rplnames.h b/private/net/svcdlls/rpl/inc/rplnames.h
new file mode 100644
index 000000000..454ccf6a4
--- /dev/null
+++ b/private/net/svcdlls/rpl/inc/rplnames.h
@@ -0,0 +1,24 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ rplnames.h
+
+Abstract:
+
+ Private header file which defines the RPL service names.
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 22 - November - 1993
+
+Revision History:
+
+ 22-Nov-1993 vladimv
+ Created
+
+--*/
+
+#define RPL_INTERFACE_NAME TEXT("rplsvc")
diff --git a/private/net/svcdlls/rpl/inc/vendor.h b/private/net/svcdlls/rpl/inc/vendor.h
new file mode 100644
index 000000000..39516f572
--- /dev/null
+++ b/private/net/svcdlls/rpl/inc/vendor.h
@@ -0,0 +1,53 @@
+/*++
+
+Module Name:
+
+ vendor.h
+
+Abstract:
+
+ Describes layout of JET database table used for VENDOR structures.
+
+--*/
+
+#ifdef RPLVENDOR_ALLOCATE
+#define EXTERN_VENDOR
+#define INIT_VENDOR( _x) = _x
+#else
+#define EXTERN_VENDOR extern
+#define INIT_VENDOR( _x)
+#endif
+
+//
+// Indices of entries in VendorTable[] - used to describe vendor codes
+//
+#define VENDOR_VendorName 0
+#define VENDOR_VendorComment 1
+#define VENDOR_Flags 2
+#define VENDOR_TABLE_LENGTH 3
+
+RPL_COLUMN_INFO VendorTable[]
+#ifdef RPLVENDOR_ALLOCATE
+ = {
+ { "VendorName", JET_coltypBinary, 0}, // first 6 digits of adapter id
+ { "VendorComment", JET_coltypBinary, 0}, // vendor comment
+ { "Flags", JET_coltypLong, 0}
+}
+#endif // RPLVENDOR_ALLOCATE
+;
+
+//
+// This definition gives wrong result when RPLVENDOR_ALLOCATE is not defined
+//#define VENDOR_TABLE_LENGTH (sizeof(VendorTable)/sizeof(VendorTable[0]))
+//
+
+#define VENDOR_INDEX_VendorName "foo" // + VendorName
+
+#define VENDOR_TABLE_NAME "Vendor"
+#define VENDOR_TABLE_PAGE_COUNT 5 // initial number of 4K pages
+#define VENDOR_TABLE_DENSITY 100 // initial density
+
+#ifdef RPL_RPLCNV
+EXTERN_VENDOR JET_TABLEID VendorTableId;
+#endif // RPL_RPLCNV
+
diff --git a/private/net/svcdlls/rpl/inc/wksta.h b/private/net/svcdlls/rpl/inc/wksta.h
new file mode 100644
index 000000000..49f58401b
--- /dev/null
+++ b/private/net/svcdlls/rpl/inc/wksta.h
@@ -0,0 +1,105 @@
+/*++
+
+Module Name:
+
+ wksta.h
+
+Abstract:
+
+ Describes layout of JET database table used for WKSTA structures.
+
+--*/
+
+#ifdef RPLWKSTA_ALLOCATE
+#define EXTERN_WKSTA
+#define INIT_WKSTA( _x) = _x
+#else
+#define EXTERN_WKSTA extern
+#define INIT_WKSTA( _x)
+#endif
+
+//
+// WKSTA_LOGON_INPUT_* describe username/password policy during rpl logon
+// on the client side. Depending on the value of this field, user input for
+// username/password during RPL logon will be:
+//
+#define WKSTA_LOGON_INPUT_REQUIRED L'P' // user input is required
+#define WKSTA_LOGON_INPUT_OPTIONAL L'N' // user input is optional
+#define WKSTA_LOGON_INPUT_IMPOSSIBLE L'D' // user input is not solicited
+//
+// WKSTA_SHARING_* describe whether workstation shares or does not share its
+// remote boot disk (i.e. "does it have shared or personal profile").
+//
+#define WKSTA_SHARING_TRUE L'S' // shares remote boot disk
+#define WKSTA_SHARING_FALSE L'P' // does not share remote boot disk
+
+//
+// WKSTA_SHARING_* describe whether workstation shares or does not share its
+// remote boot disk (i.e. "does it have shared or personal profile").
+//
+#define WKSTA_SHARING_TRUE L'S' // shares remote boot disk
+#define WKSTA_SHARING_FALSE L'P' // does not share remote boot disk
+
+//
+// WKSTA_DISABLE_DHCP_* describe whether workstation uses DHCP or not. Note
+// that these flags are relevant only if TCP/IP itself is enabled (i.e. changes
+// to boot block configuration file, config.sys & autoexec.bat have been made).
+//
+
+#define WKSTA_DISABLE_DHCP_FALSE '0' // use DHCP
+#define WKSTA_DISABLE_DHCP_TRUE '1' // do not use DHCP
+
+//
+// Indices of entries in WkstaTable[] - wksta column array.
+//
+#define WKSTA_WkstaName 0
+#define WKSTA_WkstaComment 1
+#define WKSTA_ProfileName 2
+#define WKSTA_BootName 3
+#define WKSTA_FitFile 4
+#define WKSTA_AdapterName 5
+#define WKSTA_TcpIpAddress 6
+#define WKSTA_TcpIpSubnet 7
+#define WKSTA_TcpIpGateway 8
+#define WKSTA_Flags 9
+#define WKSTA_TABLE_LENGTH 10
+
+//
+// We could speed things up by defining AdapterName as JET_coltypCurrency
+//
+
+EXTERN_WKSTA RPL_COLUMN_INFO WkstaTable[ WKSTA_TABLE_LENGTH]
+#ifdef RPLWKSTA_ALLOCATE
+ = {
+ { "WkstaName", JET_coltypBinary, 0}, // wksta name
+ { "WkstaComment", JET_coltypBinary, 0}, // wksta comment
+ { "ProfileName", JET_coltypBinary, 0}, // profile name
+ { "BootName", JET_coltypBinary, 0}, // boot block id - if NULL => consult profile
+ { "FitFile", JET_coltypBinary, 0}, // fit file - if NULL => consult profile
+ { "AdapterName", JET_coltypBinary, 0}, // network address, hex string
+ { "TcpIpAddress", JET_coltypLong, 0}, // tcp/ip address
+ { "TcpIpSubnet", JET_coltypLong, 0}, // subnet tcp/ip address
+ { "TcpIpGateway", JET_coltypLong, 0}, // gateway tcp/ip address
+ { "Flags", JET_coltypLong, 0}
+}
+#endif // RPLWKSTA_ALLOCATE
+;
+//
+// This definition gives wrong result when RPLWKSTA_ALLOCATE is not defined
+//#define WKSTA_TABLE_LENGTH (sizeof(WkstaTable)/sizeof(WkstaTable[0]))
+//
+
+#define WKSTA_INDEX_AdapterName "foo" // + AdapterName
+#define WKSTA_INDEX_WkstaName "goo" // + WkstaName
+#define WKSTA_INDEX_ProfileNameWkstaName "hoo" // + ProfileName + WkstaName
+#define WKSTA_INDEX_BootNameWkstaName "joo" // + BootName + WkstaName
+
+#define WKSTA_TABLE_NAME "Wksta"
+#define WKSTA_TABLE_PAGE_COUNT 5 // initial number of 4K pages
+#define WKSTA_TABLE_DENSITY 100 // initial density
+
+#ifdef RPL_RPLCNV
+EXTERN_WKSTA JET_TABLEID WkstaTableId;
+#endif // RPL_RPLCNV
+
+
diff --git a/private/net/svcdlls/rpl/lib/adapter.c b/private/net/svcdlls/rpl/lib/adapter.c
new file mode 100644
index 000000000..ff83481eb
--- /dev/null
+++ b/private/net/svcdlls/rpl/lib/adapter.c
@@ -0,0 +1,120 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ adapter.c
+
+Abstract:
+
+ Contains:
+ BOOL ValidHexName( IN PWCHAR Name, IN DWORD Length)
+ BOOL ValidName( IN PWCHAR Name, IN DWORD MaxNameLength)
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 19 - November - 1993
+
+Revision History:
+
+--*/
+
+#include "local.h"
+#include "rpllib.h"
+#include "icanon.h" // I_NetPathType
+
+
+
+BOOL ValidHexName(
+ IN PWCHAR Name,
+ IN DWORD NameLength,
+ IN BOOL MustHaveInput
+ )
+/*++
+
+Routine Description:
+
+ Returns TRUE if the input string is a hex string of a
+ desired length.
+
+Arguments:
+
+ Name - NULL or NULL terminated hexadecimal string
+ NameLength - meaningfull only if Name is provided
+ MustHaveInput - TRUE if null Name input is not allowed
+
+Return Value:
+ TRUE if success, FALSE otherwise
+
+--*/
+{
+ DWORD count;
+
+ if ( Name == NULL) {
+ return( !MustHaveInput); // null Name is OK only if MustHaveInput is FALSE
+ }
+ for ( count = 0; iswxdigit(*Name); count++) {
+ Name++;
+ }
+ if ( *Name != 0 || count != NameLength) {
+ return( FALSE);
+ }
+ return( TRUE);
+}
+
+
+
+BOOL ValidName(
+ IN PWCHAR Name,
+ IN DWORD MaxNameLength,
+ IN BOOL MustHaveInput
+ )
+/*++
+
+Routine Description:
+
+ Returns TRUE if Name is valid. When Name is not NULL,
+ then is must be a valid path component for RPL purposes.
+
+Arguments:
+
+ Name - NULL or NULL terminated RPL path string
+ MaxNameLength - meaningfull only if Name is provided
+ MustHaveInput - TRUE if null Name input is not allowed
+
+Return Value:
+ TRUE if success, FALSE otherwise
+
+--*/
+{
+ DWORD Length;
+ DWORD PathType = 0;
+
+ if ( Name == NULL) {
+ return( !MustHaveInput); // null Name is OK only if MustHaveInput is FALSE
+ }
+
+ Length = wcslen( Name);
+
+ if ( Length == 0 || Length > MaxNameLength) {
+ return( FALSE);
+ }
+ //
+ // Do not allow leading spaces nor "." or ".." (according to old rplmgr
+ // code these are all very bad).
+ //
+ if ( iswspace( *Name) || *Name==L'.') {
+ return( FALSE);
+ }
+ if ( wcsspn( Name, L"\f\n\r\t\v\\ ") != 0) {
+ return( FALSE);
+ }
+ if ( I_NetPathType( NULL, Name, &PathType, 0) != NO_ERROR
+ || PathType != ITYPE_PATH_RELND ) {
+ return( FALSE);
+ }
+ return( TRUE);
+}
+
+
diff --git a/private/net/svcdlls/rpl/lib/addkey.c b/private/net/svcdlls/rpl/lib/addkey.c
new file mode 100644
index 000000000..681be066e
--- /dev/null
+++ b/private/net/svcdlls/rpl/lib/addkey.c
@@ -0,0 +1,31 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ addkey.c
+
+Abstract:
+
+ Contains:
+ DWORD AddKey( OUT PCHAR Target, IN CHAR Prefix, IN PCHAR Source)
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 19 - November - 1993
+
+Revision History:
+
+--*/
+
+#include "local.h"
+
+DWORD AddKey( OUT PCHAR Target, IN CHAR Prefix, IN PCHAR Source)
+{
+ DWORD Size;
+ Target[ 0] = Prefix;
+ Size = 1 + strlen( Source); // copy terminating null
+ memcpy( Target + sizeof( Prefix), Source, Size);
+ return( Size + sizeof( Prefix));
+}
diff --git a/private/net/svcdlls/rpl/lib/cmdline.c b/private/net/svcdlls/rpl/lib/cmdline.c
new file mode 100644
index 000000000..875883bcb
--- /dev/null
+++ b/private/net/svcdlls/rpl/lib/cmdline.c
@@ -0,0 +1,249 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ cmdline.c
+
+Abstract:
+
+ Contains:
+ VOID RplPrintfXXX()
+
+Author:
+
+ Jon Newman (jonn) 20 - April - 1994
+
+Revision History:
+
+--*/
+
+#include "local.h"
+
+//
+// Must fit within FORMAT_MESSAGE_MAX_WIDTH_MASK
+//
+#define MAX_LINE_WIDTH 80
+
+
+VOID RplPrintf0(
+ IN DWORD MessageId
+ )
+/*++
+
+Routine Description:
+
+ Writes a message to STDOUT.
+
+Arguments:
+
+ MessageId - Message ID
+
+Return Value:
+
+ None.
+
+--*/
+{
+ RplPrintfN( MessageId, NULL, 0 );
+}
+
+VOID RplPrintf1(
+ IN DWORD MessageId,
+ IN PWCHAR InsertionString // note: Unicode
+ )
+/*++
+
+Routine Description:
+
+ Writes a message to STDOUT.
+
+Arguments:
+
+ MessageId - Message ID
+ InsertionString - asciiz string (may be NULL)
+
+Return Value:
+
+ None.
+
+--*/
+{
+ RplPrintfN( MessageId, &InsertionString, 1 );
+}
+
+
+VOID RplPrintf2(
+ IN DWORD MessageId,
+ IN PWCHAR InsertionString1, // note: Unicode
+ IN PWCHAR InsertionString2 // note: Unicode
+ )
+/*++
+
+Routine Description:
+
+ Writes a message to STDOUT.
+
+Arguments:
+
+ MessageId - Message ID
+
+ InsertionString1 - asciiz string (may be NULL)
+ InsertionString2 - asciiz string (may be NULL)
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PWCHAR ParamArray[2];
+ ParamArray[0] = InsertionString1;
+ ParamArray[1] = InsertionString2;
+ RplPrintfN( MessageId, ParamArray, 2 );
+}
+
+
+VOID RplPrintfN(
+ IN DWORD MessageId,
+ IN PWCHAR * Parameters, // note: Unicode
+ IN DWORD NumParameters
+ )
+/*++
+
+Routine Description:
+
+ Writes a message to STDOUT.
+
+Arguments:
+
+ MessageId - Message ID
+
+ Parameters - array of pointers to parameters
+
+ NumParameters - number of parameters in array
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PWCHAR MessageString = NULL;
+ DWORD Error = NO_ERROR;
+
+ (void) RplSPrintfN( MessageId,
+ Parameters,
+ NumParameters,
+ &MessageString );
+
+ if (MessageString != NULL) {
+ wprintf( MessageString );
+ }
+
+ if (MessageString != NULL) {
+ LocalFree( MessageString );
+ MessageString = NULL;
+ }
+}
+
+
+VOID RplPrintfID(
+ IN DWORD MessageId,
+ IN DWORD MessageIdInsertion
+ )
+/*++
+
+Routine Description:
+
+ Writes a message to STDOUT.
+
+Arguments:
+
+ MessageId - Message ID
+
+ MessageIdInsertion - Message ID to be inserted as %1 into MessageId
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PWCHAR MessageString = NULL;
+
+ RplSPrintfN( MessageIdInsertion, NULL, 0, &MessageString );
+
+ if (MessageString != NULL) {
+ RplPrintf1( MessageId, MessageString );
+ }
+
+ if (MessageString != NULL) {
+ LocalFree( MessageString );
+ MessageString = NULL;
+ }
+}
+
+
+VOID RplSPrintfN(
+ IN DWORD MessageId,
+ IN PWCHAR * Parameters, // note: Unicode
+ IN DWORD NumParameters,
+ OUT PWCHAR * MessageStringPtr
+ )
+/*++
+
+Routine Description:
+
+ Retrieves a message
+
+Arguments:
+
+ MessageId - Message ID
+
+ Parameters - array of pointers to parameters
+
+ NumParameters - number of parameters in array
+
+ MessageStringPtr - message returned, must be LocalFree'd by caller
+
+Return Value:
+
+ None.
+
+--*/
+{
+ DWORD Error = NO_ERROR;
+
+ if (MessageStringPtr == NULL) {
+#ifdef RPL_DEBUG
+ wprintf( L"RplSPrintfN: bad parameter\n" );
+#endif
+ return;
+ }
+
+ if ( 0 == FormatMessageW( FORMAT_MESSAGE_ALLOCATE_BUFFER
+ | FORMAT_MESSAGE_FROM_HMODULE
+ | FORMAT_MESSAGE_ARGUMENT_ARRAY
+ | MAX_LINE_WIDTH,
+ NULL,
+ MessageId,
+ 0,
+ (LPTSTR)(MessageStringPtr), // ALLOCATE_BUFFER
+ NumParameters,
+ (va_list *)Parameters) ) { // ARGUMENT_ARRAY
+#ifdef RPL_DEBUG
+ Error = GetLastError();
+ wprintf( L"RplSPrintfN: FormatMessage error %d\n", Error );
+#endif
+ goto cleanup;
+ }
+
+cleanup:
+
+ if (Error != NO_ERROR && *MessageStringPtr != NULL) {
+ LocalFree( *MessageStringPtr );
+ *MessageStringPtr = NULL;
+ }
+}
diff --git a/private/net/svcdlls/rpl/lib/jeterror.c b/private/net/svcdlls/rpl/lib/jeterror.c
new file mode 100644
index 000000000..70bb8f1a1
--- /dev/null
+++ b/private/net/svcdlls/rpl/lib/jeterror.c
@@ -0,0 +1,66 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ jeterror.c
+
+Abstract:
+
+ Contains:
+ DWORD MapJetError( IN JET_ERR JetError)
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 19 - November - 1993
+
+Revision History:
+
+--*/
+
+#include "local.h"
+
+//
+// BUGBUG ERROR_RPL_JET_ERROR should be a part of winerr.h
+// Although RPL apis follow NET convention, they really use WINDOWS error
+// codes. This is somewhat inconsistent.
+//
+#define ERROR_RPL_JET_ERROR 0x7777 // BUGBUG arbitrary value for now
+
+
+DWORD MapJetError( IN JET_ERR JetError)
+/*++
+
+Routine Description:
+
+ This function maps the Jet database errors to Windows error.
+
+Arguments:
+
+ JetError - an error JET function call.
+
+Return Value:
+
+ Windows Error.
+
+--*/
+{
+ if( JetError == JET_errSuccess ) {
+ return( ERROR_SUCCESS);
+ } else if ( JetError < 0) {
+ DWORD Error;
+ switch( JetError ) {
+ case JET_errNoCurrentRecord:
+ Error = ERROR_NO_MORE_ITEMS;
+ break;
+
+ case JET_errRecordNotFound: // record not found
+ default:
+ Error = ERROR_RPL_JET_ERROR;
+ }
+ return(Error);
+ } else {
+ return( ERROR_SUCCESS);
+ }
+}
diff --git a/private/net/svcdlls/rpl/lib/local.h b/private/net/svcdlls/rpl/lib/local.h
new file mode 100644
index 000000000..927f8d163
--- /dev/null
+++ b/private/net/svcdlls/rpl/lib/local.h
@@ -0,0 +1,54 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ local.h
+
+Abstract:
+
+ Main include file for Remote initial Program Load library.
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 19 - November - 1993
+
+Revision History:
+
+--*/
+
+#include <nt.h> // ntexapi.h\NtQuerySystemTime
+#include <ntrtl.h> // RtlTimeToSecondsSince1970
+#include <nturtl.h>
+
+#include <windows.h> // DWORD, IN, File APIs, etc.
+#include <stdlib.h>
+#include <lmcons.h>
+
+#include <stdio.h> // vsprintf
+#include <ctype.h> // isspace
+
+#include <lmerr.h> // NERR_RplBootStartFailed - used to be here
+
+#include <lmsvc.h>
+
+#include <lmalert.h> // STD_ALERT ALERT_OTHER_INFO
+
+#include <lmerrlog.h>
+#include <alertmsg.h>
+#include <lmserver.h>
+#include <netlib.h>
+#include <netlock.h> // Lock data types, functions, and macros.
+#include <thread.h> // FORMAT_NET_THREAD_ID, NetpCurrentThread().
+
+#include <lmshare.h> // PSHARE_lsINFO_2
+#include <lmaccess.h> // NetGroupGetInfo
+#include <lmconfig.h> // NetConfigGet
+#include <nb30.h> // NCB
+
+#include <riplcons.h> // includes __JET500 flag
+#include <jet.h> // JET_ERR
+
+#include "rpldebug.h"
+#include "rpllib.h"
diff --git a/private/net/svcdlls/rpl/lib/makefile b/private/net/svcdlls/rpl/lib/makefile
new file mode 100644
index 000000000..f0db8e4a7
--- /dev/null
+++ b/private/net/svcdlls/rpl/lib/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS LINE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/net/svcdlls/rpl/lib/reg.c b/private/net/svcdlls/rpl/lib/reg.c
new file mode 100644
index 000000000..181fd207b
--- /dev/null
+++ b/private/net/svcdlls/rpl/lib/reg.c
@@ -0,0 +1,271 @@
+/*++
+
+Copyright (c) 1987-1993 Microsoft Corporation
+
+Module Name:
+
+ reg.c
+
+Abstract:
+
+ Registry _access routines for RPL server and related components
+
+Author:
+
+ Vladimir Z. Vulovic 27 - July - 1993
+
+Environment:
+
+ User mode
+
+Revision History :
+
+ Moved from server by Jon Newman 26 - April - 1994
+
+--*/
+
+#include "local.h"
+
+#define RPL_REGISTRY_PARAMETERS L"System\\CurrentControlSet\\Services\\RemoteBoot\\Parameters"
+#define RPL_REGISTRY_DIRECTORY L"Directory"
+#define RPL_REGISTRY_STARTUP L"Startup"
+#define RPL_DEFAULT_DIRECTORY L"%SystemRoot%\\rpl\\"
+#define RPL_DEFAULT_STARTUP 0
+#define RPL_DEFAULT_BACKUP_INTERVAL 24
+#define RPL_DEFAULT_MAX_WORKER_COUNT 10
+#define RPL_REGISTRY_BACKUP_INTERVAL L"BackupInterval"
+#define RPL_REGISTRY_THREAD_COUNT L"maxthreads"
+
+
+DWORD RplRegReadValue(
+ IN HKEY Key,
+ IN DWORD DefaultValue,
+ IN PWCHAR Name,
+ OUT DWORD * pValue
+ )
+{
+ DWORD Size;
+ DWORD Type;
+ DWORD Error;
+
+ Size = sizeof( DWORD);
+ Error = RegQueryValueEx( Key, Name, NULL,
+ &Type, (PBYTE)pValue, &Size);
+ if ( Error == NO_ERROR) {
+ if ( Type != REG_DWORD || Size != sizeof( DWORD)) {
+ Error = NERR_RplBadRegistry;
+ RplDump( ++RG_Assert, ("Type = 0x%x, Value = 0x%x, Size=%d",
+ Type, *pValue, Size));
+ }
+ } else if (Error == ERROR_FILE_NOT_FOUND) {
+ Error = NO_ERROR;
+ *pValue = DefaultValue;
+ } else {
+ RplDump( ++RG_Assert, ("Error = %d", Error));
+ }
+ return( Error);
+}
+
+
+
+DWORD RplRegReadDirectory(
+ IN HKEY Key,
+ IN PWCHAR DefaultValue,
+ OUT PWCHAR DirectoryBuffer,
+ IN OUT DWORD * pDirectoryBufferSize
+ )
+{
+ WCHAR Buffer[ MAX_PATH];
+ PWCHAR Value;
+ DWORD Length;
+ DWORD Error;
+ DWORD Size;
+ DWORD Type;
+
+ Size = sizeof( Buffer);
+ Error = RegQueryValueEx( Key, RPL_REGISTRY_DIRECTORY, NULL,
+ &Type, (PBYTE)Buffer, &Size);
+ if ( Error == NO_ERROR) {
+ //
+ // We have the data. First validate the data, then append the
+ // backslash if needed.
+ //
+ Value = Buffer;
+ if ( Type != REG_EXPAND_SZ || (Size&1) != 0 || Size < sizeof(L"c:")) {
+ Error = NERR_RplBadRegistry;
+ RplDump( ++RG_Assert, ("Type2 = 0x%x, Value = 0x%x, Size=%d",
+ Type, Value, Size));
+ return( Error);
+ }
+ Length = Size / sizeof(WCHAR) - 1;
+ if ( Value[ Length] != 0 || Length != wcslen(Value)) {
+ Error = NERR_RplBadRegistry;
+ RplDump( ++RG_Assert, ("Length = %d, Value = 0x%x, Size=%d",
+ Length, Value, Size));
+ return( Error);
+ }
+ if ( Value[ Length - 1] != L'\\') {
+ //
+ // Need to append the backslash.
+ //
+ if ( ( Length + 1) >= MAX_PATH) {
+ Error = NERR_RplBadRegistry;
+ RplDump( ++RG_Assert, ("Length = %d, Value = 0x%x, Size=%d",
+ Length, Value, Size));
+ return( Error); // not enough space
+ }
+ Value[ Length] = L'\\';
+ Value[ ++Length] = 0;
+ }
+ } else if ( Error == ERROR_FILE_NOT_FOUND ) {
+ Value = DefaultValue;
+ } else {
+ RplDump( ++RG_Assert, ("Error = %d", Error));
+ return( Error);
+ }
+
+ //
+ // Length returned by ExpandEnvironmentalStrings includes terminating
+ // NULL byte.
+ //
+ Length = ExpandEnvironmentStrings( Value, DirectoryBuffer, *pDirectoryBufferSize);
+ if ( Length == 0) {
+ Error = GetLastError();
+ RplDump( ++RG_Assert, ("Error = %d", Error));
+ return( Error);
+ }
+ if ( Length > (*pDirectoryBufferSize)
+ || Length > MAX_PATH || Length != wcslen(DirectoryBuffer) + 1) {
+ Error = NERR_RplBadRegistry;
+ RplDump( ++RG_Assert, ("Length = %d", Length));
+ return( Error);
+ }
+ *pDirectoryBufferSize = (DWORD)(Length - 1);
+ return( NO_ERROR);
+}
+
+
+
+DWORD RplRegRead(
+ OUT DWORD * pStartup,
+ OUT DWORD * pBackupInterval,
+ OUT DWORD * pMaxThreadCount,
+ OUT PWCHAR DirectoryBuffer,
+ IN OUT DWORD * pDirectoryBufferSize
+ )
+{
+ DWORD Error;
+ HKEY Key;
+ BOOL HaveKey = FALSE;
+
+ //
+ // First open the RPL service parameters registry tree.
+ //
+ // This will fail if the RPL registry tree is absent, that is,
+ // if RPL is not installed.
+ //
+ Error = RegOpenKeyEx( HKEY_LOCAL_MACHINE, RPL_REGISTRY_PARAMETERS,
+ 0, KEY_READ, &Key);
+ if ( Error != NO_ERROR) {
+ RplDump( ++RG_Assert, ("Error = %d", Error));
+ goto cleanup;
+ }
+ HaveKey = TRUE;
+
+ //
+ // It is OK if values in parameters subtree are absent, but present
+ // values should be valid. This is why each read routine receives
+ // default value as the second parameter.
+ //
+
+ if ( pStartup != NULL) {
+ Error = RplRegReadValue( Key, RPL_DEFAULT_STARTUP, RPL_REGISTRY_STARTUP,
+ pStartup);
+ if ( Error != NO_ERROR) {
+ goto cleanup;
+ }
+ }
+
+ if ( pBackupInterval != NULL) {
+ Error = RplRegReadValue( Key, RPL_DEFAULT_BACKUP_INTERVAL,
+ RPL_REGISTRY_BACKUP_INTERVAL, pBackupInterval);
+ if ( Error != NO_ERROR) {
+ goto cleanup;
+ }
+ //
+ // Registry contains backup interval in hours, convert it to
+ //
+ //
+ }
+
+ if ( pMaxThreadCount != NULL) {
+ Error = RplRegReadValue( Key, RPL_DEFAULT_MAX_WORKER_COUNT,
+ RPL_REGISTRY_THREAD_COUNT, pMaxThreadCount);
+ if ( Error != NO_ERROR) {
+ goto cleanup;
+ }
+ }
+
+ if ( DirectoryBuffer != NULL && pDirectoryBufferSize != NULL
+ && (*pDirectoryBufferSize) != 0 ) {
+ Error = RplRegReadDirectory( Key, RPL_DEFAULT_DIRECTORY,
+ DirectoryBuffer, pDirectoryBufferSize);
+ if ( Error != NO_ERROR) {
+ goto cleanup;
+ }
+ }
+
+cleanup:
+ if ( HaveKey == TRUE) {
+ (VOID)RegCloseKey( Key);
+ }
+ if ( Error != NO_ERROR) {
+ RplReportEvent( NELOG_RplRegistry, NULL, sizeof(DWORD), &Error );
+ }
+ return( Error);
+}
+
+
+DWORD RplRegSetPolicy( DWORD NewStartup)
+{
+ DWORD Error;
+ HKEY Key;
+ BOOL HaveKey = FALSE;
+
+ //
+ // First open the RPL service parameters registry tree.
+ //
+ // This will fail if the RPL registry tree is absent, that is,
+ // if RPL is not installed.
+ //
+ Error = RegOpenKeyEx( HKEY_LOCAL_MACHINE, RPL_REGISTRY_PARAMETERS,
+ 0, KEY_SET_VALUE, &Key);
+ if ( Error != NO_ERROR){
+ RplDump( ++RG_Assert, ("Error = %d", Error));
+ goto cleanup;
+ }
+ HaveKey = TRUE;
+
+ //
+ // Now set the Startup value
+ //
+ Error = RegSetValueEx( Key, RPL_REGISTRY_STARTUP, 0,
+ REG_DWORD, (PBYTE)&NewStartup, sizeof(NewStartup));
+ if ( Error != NO_ERROR) {
+ RplDump( ++RG_Assert, ("Error = %d", Error));
+ goto cleanup;
+ }
+
+cleanup:
+ if ( HaveKey == TRUE) {
+ (VOID)RegCloseKey( Key);
+ }
+
+ if ( Error != NO_ERROR) {
+ RplReportEvent( NELOG_RplRegistry, NULL, sizeof(DWORD), &Error );
+ }
+
+ return( Error);
+}
+
+
diff --git a/private/net/svcdlls/rpl/lib/report.c b/private/net/svcdlls/rpl/lib/report.c
new file mode 100644
index 000000000..2364ed2e0
--- /dev/null
+++ b/private/net/svcdlls/rpl/lib/report.c
@@ -0,0 +1,111 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ report.c
+
+Abstract:
+
+ Contains:
+ VOID RplReportEvent(
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 19 - November - 1993
+
+Revision History:
+
+--*/
+
+#include "local.h"
+
+
+VOID RplReportEvent(
+ IN DWORD MessageId,
+ IN LPWSTR InsertionString,
+ IN DWORD RawDataBufferLength OPTIONAL,
+ IN LPVOID RawDataBuffer
+ )
+/*++
+
+Routine Description:
+
+ Writes an error message and ascii string to the error log. Also,
+ writes the data in the data buf if there are any.
+
+Arguments:
+
+ MessageId - Message ID
+
+ InsertionString - at most one asciiz string (may be NULL)
+
+ RawDataBufferLength - size of data to be printed from the auxiliary data
+ buffer. May be zero, in which case the actual value depends on
+ "RawDataBuffer". It is 0 if "RawDataBuffer" is NULL, else it is
+ "sizeof( wchar) * wcslen( RawDataBuffer)".
+
+ RawDataBuffer - data buffer containing secondary error code & some
+ other useful info. Must be NULL terminated string or NULL if
+ "RawDataBufferLength" is zero.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ WORD NumberOfStrings;
+ LPWSTR InsertionStringArray[ 1];
+ LPWSTR * PointerToStrings;
+ HANDLE logHandle;
+
+ logHandle = RegisterEventSource( NULL, RPL_EVENTLOG_NAME);
+
+ // If the event log cannot be opened, just return.
+
+ if ( logHandle == NULL) {
+#ifdef NOT_YET
+ RplDump( RG_DebugLevel & RPL_DEBUG_MISC,(
+ "ReportEvent: RegisterEventSource() failed with error %d",
+ GetLastError()));
+#endif // NOT_YET
+ return;
+ }
+
+ if ( InsertionString == NULL) {
+ PointerToStrings = NULL;
+ NumberOfStrings = 0;
+ } else {
+ InsertionStringArray[ 0] = InsertionString;
+ PointerToStrings = InsertionStringArray;
+ NumberOfStrings = 1;
+ }
+
+ //
+ // Use default for RawDataBufferLength if caller requested us so.
+ //
+ if ( RawDataBufferLength == 0 && RawDataBuffer != NULL) {
+ RawDataBufferLength = sizeof( TCHAR) * wcslen( (LPWSTR)RawDataBuffer);
+ }
+
+ if ( !ReportEvent(
+ logHandle,
+ EVENTLOG_ERROR_TYPE,
+ 0, // event category
+ MessageId, // event id
+ NULL, // user SID. We're local system - uninteresting
+ NumberOfStrings, // number of strings
+ RawDataBufferLength, // raw data size
+ PointerToStrings, // string array
+ RawDataBuffer // raw data buffer
+ )) {
+#ifdef NOT_YET
+ RplDump( RG_DebugLevel & RPL_DEBUG_MISC,(
+ "ReportEvent: fails with error %d", GetLastError()));
+#endif // NOT_YET
+ }
+
+ DeregisterEventSource( logHandle);
+}
diff --git a/private/net/svcdlls/rpl/lib/sources b/private/net/svcdlls/rpl/lib/sources
new file mode 100644
index 000000000..2be59e4ef
--- /dev/null
+++ b/private/net/svcdlls/rpl/lib/sources
@@ -0,0 +1,36 @@
+MAJORCOMP=net
+MINORCOMP=rpllib
+
+TARGETPATH=obj
+TARGETNAME=rpllib
+TARGETTYPE=LIBRARY
+
+TARGETLIBS= \
+ $(BASEDIR)\Public\Sdk\Lib\*\netlib.lib \
+ $(BASEDIR)\public\sdk\lib\*\netapi32.lib \
+ $(BASEDIR)\public\sdk\lib\*\wsock32.lib
+
+!IFNDEF DISABLE_NET_UNICODE
+UNICODE=1
+NET_C_DEFINES=-DUNICODE
+!ENDIF
+
+INCLUDES=.;..\inc;..\..\..\inc;..\..\..\api;..\..\..\..\inc;
+
+MSC_WARNING_LEVEL=/W3 /WX
+
+SOURCES= \
+ report.c \
+ cmdline.c \
+ adapter.c \
+ tcpip.c \
+ jeterror.c \
+ reg.c \
+ addkey.c
+
+
+C_DEFINES= -DINCL_32= -DNT -DRPC_NO_WINDOWS_H -DWIN32
+
+UMTYPE=console
+
+
diff --git a/private/net/svcdlls/rpl/lib/tcpip.c b/private/net/svcdlls/rpl/lib/tcpip.c
new file mode 100644
index 000000000..b45b2f8d3
--- /dev/null
+++ b/private/net/svcdlls/rpl/lib/tcpip.c
@@ -0,0 +1,68 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ tcpip.c
+
+Abstract:
+
+ Contains:
+ DWORD FillTcpIpString( OUT PCHAR Buffer, IN DWORD AddressDword)
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 19 - November - 1993
+
+Revision History:
+
+--*/
+
+#include "local.h"
+#include "rpllib.h"
+#include "winsock.h" // for INADDR_NONE
+
+DWORD FillTcpIpString(
+ OUT PCHAR Buffer,
+ IN DWORD AddressDword
+ )
+/*++
+ AddressDword is in a host byte order. E.g. dword 0x11223344
+ corresponds to 11.22.33.44 string.
+--*/
+{
+ if ( AddressDword == INADDR_NONE) {
+ strcpy( Buffer, "~ ");
+ } else {
+#ifdef NOT_YET
+ PBYTE p = (PBYTE)&AddressDword;
+ //
+ // For dword 0x11223344 we have p[0] = 44, etc.
+ //
+ // We do not do:
+ //
+ // ipaddr.s_addr = htonl( AddressDword)
+ // string = inet_ntoa( ipaddr)
+ // strcpy( Buffer, string)
+ //
+ // because inet_ntoa() allocates buffer for string.
+ //
+ sprintf( Buffer, "%d.%d.%d.%d ", p[3], p[2], p[1], p[0]);
+#else
+ struct in_addr InAddr; // Internet address structure
+ PCHAR String;
+
+ // Convert the host address to network byte order
+ InAddr.s_addr = htonl( AddressDword);
+
+ // Convert the IP address value to a string
+ String = inet_ntoa( InAddr) ;
+
+ // Copy the string to our buffer & append a space.
+ strcpy( Buffer, String);
+ strcat( Buffer, " ");
+#endif
+ }
+ return( strlen( Buffer));
+}
diff --git a/private/net/svcdlls/rpl/makefil0 b/private/net/svcdlls/rpl/makefil0
new file mode 100644
index 000000000..fc6bb29d7
--- /dev/null
+++ b/private/net/svcdlls/rpl/makefil0
@@ -0,0 +1,73 @@
+#
+# This is the MIDL compile phase of the build process.
+#
+# The following is where you put the name of your .idl file without
+# the .idl extension:
+#
+
+!INCLUDE $(NTMAKEENV)\makefile.plt
+
+IDL_NAME = rplsvc
+CLIENT_H = rplsvc_c.h
+SERVER_H = rplsvc_s.h
+CLIENT_ACF = rplsvc_c.acf
+SERVER_ACF = rplsvc_s.acf
+
+!IFNDEF BASEDIR
+BASEDIR=\nt
+!ENDIF
+
+!IFNDEF DISABLE_NET_UNICODE
+UNICODE=1
+NET_C_DEFINES=-DUNICODE
+!ENDIF
+
+SDKINC = $(BASEDIR)\public\sdk\inc
+NETINC = $(BASEDIR)\private\net\inc
+SDKCRTINC = $(BASEDIR)\public\sdk\inc\crt
+PRIVINC = $(BASEDIR)\private\inc
+RPLINC = inc
+INCS = -I$(SDKINC) -I$(SDKCRTINC) -I$(PRIVINC) -I$(NETINC) -I$(RPLINC)
+
+CLIENT_TARGETS = client\$(IDL_NAME)_c.c client\$(CLIENT_H)
+SERVER_TARGETS = server\$(IDL_NAME)_s.c server\$(SERVER_H)
+TARGETS = $(CLIENT_TARGETS) $(SERVER_TARGETS)
+
+EXTRN_DEPENDS = $(SDKINC)\lmcons.h \
+ $(SDKINC)\windef.h \
+ $(SDKINC)\lmrpl.h \
+ inc\i_lmrpl.h \
+ inc\imports.h
+
+CLIENT_FLAGS = -acf $(CLIENT_ACF) -header $(CLIENT_H) -oldnames
+SERVER_FLAGS = -acf $(SERVER_ACF) -header $(SERVER_H) -oldnames
+
+CPP = -cpp_cmd "$(MIDL_CPP)" $(MIDL_FLAGS) $(C_DEFINES) $(NET_C_DEFINES)
+
+#
+# Define Products and Dependencies
+#
+
+all: $(TARGETS) $(EXTRN_DEPENDS)
+!IF "$(BUILDMSG)" != ""
+ @ech ; $(BUILDMSG) ;
+!ENDIF
+
+clean: delsrc all
+
+delsrc:
+ erase $(CLIENT_TARGETS) $(SERVER_TARGETS)
+
+#
+# MIDL COMPILE
+#
+
+$(CLIENT_TARGETS) : .\$(IDL_NAME).idl $(EXTRN_DEPENDS) .\$(CLIENT_ACF)
+ midl -Oi -server none -error allocation -error ref -ms_ext -c_ext $(CPP) $(CLIENT_FLAGS) .\$(IDL_NAME).idl $(INCS)
+ IF EXIST $(IDL_NAME)_c.c copy $(IDL_NAME)_c.c .\client & del $(IDL_NAME)_c.c
+ IF EXIST $(CLIENT_H) copy $(CLIENT_H) .\client & del $(CLIENT_H)
+
+$(SERVER_TARGETS) : .\$(IDL_NAME).idl $(EXTRN_DEPENDS) .\$(SERVER_ACF)
+ midl -client none -error stub_data -error allocation -error ref -ms_ext -c_ext $(CPP) $(SERVER_FLAGS) .\$(IDL_NAME).idl $(INCS)
+ IF EXIST $(IDL_NAME)_s.c copy $(IDL_NAME)_s.c .\server & del $(IDL_NAME)_s.c
+ IF EXIST $(SERVER_H) copy $(SERVER_H) .\server & del $(SERVER_H)
diff --git a/private/net/svcdlls/rpl/makefile b/private/net/svcdlls/rpl/makefile
new file mode 100644
index 000000000..d30e64184
--- /dev/null
+++ b/private/net/svcdlls/rpl/makefile
@@ -0,0 +1,12 @@
+# BUGBUG This makefile is needed so that build.exe would not report an
+# BUGBUG a bogus error. There is probably a better way to achieve this.
+# ( The bogus error is reported when linking rpl directory:
+# NMAKE U1064: MAKEFILE not found & no target specified )
+#
+
+all:
+!IF "$(BUILDMSG)" != ""
+ @ech ; $(BUILDMSG) ;
+!ENDIF
+
+clean: all
diff --git a/private/net/svcdlls/rpl/rplcnv/local.h b/private/net/svcdlls/rpl/rplcnv/local.h
new file mode 100644
index 000000000..b5e032c06
--- /dev/null
+++ b/private/net/svcdlls/rpl/rplcnv/local.h
@@ -0,0 +1,64 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ local.h
+
+Abstract:
+
+ Main include file for RPLSTART
+
+Author:
+
+ Jon Newman (jonn) 12 - January - 1994
+
+Revision History:
+
+--*/
+
+#include <nt.h> // ntexapi.h\NtQuerySystemTime
+#include <ntrtl.h> // RtlTimeToSecondsSince1970
+#include <nturtl.h>
+
+#include <windows.h> // DWORD, IN, File APIs, etc.
+#include <stdlib.h>
+#include <lmcons.h>
+
+#include <stdio.h> // vsprintf
+#include <ctype.h> // isspace
+
+#include <lmerr.h> // NERR_RplBootStartFailed - used to be here
+
+//
+#include <lmapibuf.h> // NetApiBufferFree
+#include <netlib.h>
+
+#include <lmsvc.h>
+
+#include <lmalert.h> // STD_ALERT ALERT_OTHER_INFO
+
+#include <lmerrlog.h>
+#include <alertmsg.h>
+#include <lmserver.h>
+#include <netlib.h>
+#include <netlock.h> // Lock data types, functions, and macros.
+#include <thread.h> // FORMAT_NET_THREAD_ID, NetpCurrentThread().
+
+#include <lmshare.h> // PSHARE_INFO_2
+#include <lmaccess.h> // NetGroupGetInfo
+#include <lmconfig.h> // NetConfigGet
+#include <nb30.h> // NCB
+
+//
+// Global types and constants (used in all RPL server files).
+//
+
+#include <riplcons.h> // includes __JET500 flag
+#include <jet.h>
+#include <rpllib.h> // AddKey(), MapJetError(), FillTcpIpString()
+
+#include <rpldb.h>
+
+#include "rplmgrd.h"
diff --git a/private/net/svcdlls/rpl/rplcnv/makefile b/private/net/svcdlls/rpl/rplcnv/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/net/svcdlls/rpl/rplcnv/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/net/svcdlls/rpl/rplcnv/rplcnv.c b/private/net/svcdlls/rpl/rplcnv/rplcnv.c
new file mode 100644
index 000000000..218f90581
--- /dev/null
+++ b/private/net/svcdlls/rpl/rplcnv/rplcnv.c
@@ -0,0 +1,37 @@
+/*++
+
+Copyright (c) 1987-1993 Microsoft Corporation
+
+Module Name:
+
+ rplcnv.c
+
+Abstract:
+
+ Converts old style (OS/2) files RPL.MAP & RPLMGR.INI
+ into a new style database to be used with NT rpl server.
+
+Author:
+
+ Jon Newman (jonn) 02 - February - 1993
+
+Environment:
+
+ User mode
+
+Revision History :
+
+--*/
+
+#define RPLDATA_ALLOCATE
+#include "local.h"
+#undef RPLDATA_ALLOCATE
+
+
+DWORD _CRTAPI1 main ( VOID)
+{
+ I_NetRplCmd_ConvertDatabase( NULL);
+
+ return( ERROR_SUCCESS);
+}
+
diff --git a/private/net/svcdlls/rpl/rplcnv/sources b/private/net/svcdlls/rpl/rplcnv/sources
new file mode 100644
index 000000000..c298a8272
--- /dev/null
+++ b/private/net/svcdlls/rpl/rplcnv/sources
@@ -0,0 +1,27 @@
+MAJORCOMP=net
+MINORCOMP=rplcnv
+
+TARGETPATH=obj
+TARGETNAME=rplcnv
+TARGETTYPE=PROGRAM
+
+TARGETLIBS= \
+ ..\convert\obj\*\rplmgrd.lib
+
+!IFNDEF DISABLE_NET_UNICODE
+UNICODE=1
+NET_C_DEFINES=-DUNICODE
+!ENDIF
+
+INCLUDES=.;..\inc;..\..\..\inc;..\..\..\api;..\..\..\..\inc;
+
+MSC_WARNING_LEVEL=/W3 /WX
+
+SOURCES= \
+ rplcnv.c
+
+
+C_DEFINES= -DINCL_32= -DNT -DRPC_NO_WINDOWS_H -DWIN32 -DRPL_RPLCNV
+
+UMTYPE=console
+
diff --git a/private/net/svcdlls/rpl/rplinst/debug.c b/private/net/svcdlls/rpl/rplinst/debug.c
new file mode 100644
index 000000000..468b04969
--- /dev/null
+++ b/private/net/svcdlls/rpl/rplinst/debug.c
@@ -0,0 +1,137 @@
+/*++
+
+Copyright (c) 1991-1993 Microsoft Corporation
+
+Module Name:
+
+ debug.c
+
+Abstract:
+
+ Debugging module. RpldDebugPrint() was adapted from TelnetDebugPrint()
+ in net\sockets\tcpcmd\telnet\debug.c.
+
+Author:
+
+ Vladimir Z. Vulovic 27 - July - 1993
+
+Environment:
+
+ User mode
+
+Revision History :
+
+--*/
+
+#include "local.h"
+#include "debug.h"
+
+#ifdef RPL_DEBUG
+
+#define RPL_PROMPT "[Rpl] "
+#define DEBUG_RECORD_SIZE 0x80
+#define DEBUG_BUFFER_SIZE 0x20000 // 2*64k, must be a power of 2
+#define DEBUG_BUFFER_MASK (DEBUG_BUFFER_SIZE - 1)
+
+// int RG_DebugLevel = -1; // max debugging
+// int RG_DebugLevel = 0; // no debugging, for public use
+
+int RG_DebugLevel; // needed by other modules
+int RG_Assert; // needed by other modules
+
+PCHAR RG_DebugBuffer;
+CRITICAL_SECTION RG_ProtectDebug;
+DWORD RG_DebugOffset;
+//
+// This buffer is used even when RG_DebugBuffer is not NULL.
+//
+char RG_DebugPublicBuffer[ 120];
+
+
+VOID RplDebugDelete( VOID)
+{
+ if ( RG_DebugBuffer != NULL) {
+ (VOID)GlobalFree( RG_DebugBuffer);
+ }
+ DeleteCriticalSection( &RG_ProtectDebug);
+}
+
+#define RPL_DEFAULT_DEBUG_LEVEL ( RPL_DEBUG_FLOW | \
+ RPL_DEBUG_SERVER | \
+ RPL_DEBUG_MAJOR )
+
+#define RPL_MAXIMUM_DEBUG_LEVEL (-1L)
+
+
+
+VOID RplDebugInitialize( VOID)
+{
+ //
+ // Set RG_Assert to 0. Without this the very first use of
+ // RplDump() will lead to an assert.
+ //
+ RG_Assert = 0;
+
+// RG_DebugLevel = RPL_DEFAULT_DEBUG_LEVEL;
+ RG_DebugLevel = RPL_MAXIMUM_DEBUG_LEVEL & ~RPL_DEBUG_MEMALLOC
+ & ~RPL_DEBUG_FLOWINIT & ~RPL_DEBUG_FDR & ~RPL_DEBUG_SFR;
+// RG_DebugLevel = RPL_MAXIMUM_DEBUG_LEVEL;
+
+
+ RG_DebugBuffer = (PCHAR)GlobalAlloc(
+ GMEM_FIXED,
+ DEBUG_BUFFER_SIZE + DEBUG_RECORD_SIZE
+ );
+ if ( RG_DebugBuffer != NULL) {
+ RtlFillMemory(
+ RG_DebugBuffer,
+ DEBUG_BUFFER_SIZE + DEBUG_RECORD_SIZE,
+ '*'
+ );
+ RG_DebugOffset = 0;
+ }
+ memcpy( RG_DebugPublicBuffer, RPL_PROMPT, sizeof( RPL_PROMPT) -1);
+
+ InitializeCriticalSection( &RG_ProtectDebug);
+}
+
+
+VOID _CRTAPI1 RplDebugPrint( CONST CHAR * format, ...)
+{
+ va_list arglist;
+
+ va_start( arglist, format);
+
+ EnterCriticalSection( &RG_ProtectDebug);
+
+ if ( RG_Assert == 0 && RG_DebugBuffer != NULL) {
+ RtlZeroMemory( RG_DebugBuffer + RG_DebugOffset, DEBUG_RECORD_SIZE);
+ vsprintf( RG_DebugBuffer + RG_DebugOffset, format, arglist);
+ RG_DebugOffset += DEBUG_RECORD_SIZE;
+ RG_DebugOffset &= DEBUG_BUFFER_MASK;
+ } else {
+ vsprintf( RG_DebugPublicBuffer + sizeof( RPL_PROMPT) - 1, format, arglist);
+ DbgPrint( "%s\n", RG_DebugPublicBuffer);
+ }
+
+ if ( RG_Assert != 0) {
+ ASSERT( FALSE); // break for checked build
+ KdPrint(( "[RplSvc] Running checked version of service on a free build.\n"));
+ }
+ while ( RG_Assert != 0) {
+ KdPrint(( "[RplSvc] Debug, then reset RG_Assert to 0.\n"));
+ DbgUserBreakPoint();
+ Sleep( RG_Assert * 30000); // sleep for 30 seconds
+ }
+
+ LeaveCriticalSection( &RG_ProtectDebug);
+
+ va_end( arglist);
+
+} // RplDebugPrint
+
+
+#endif // RPL_DEBUG
+
+
+
diff --git a/private/net/svcdlls/rpl/rplinst/debug.h b/private/net/svcdlls/rpl/rplinst/debug.h
new file mode 100644
index 000000000..31706c1fe
--- /dev/null
+++ b/private/net/svcdlls/rpl/rplinst/debug.h
@@ -0,0 +1,28 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ debug.h
+
+Abstract:
+
+ Exports from debug.c
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 19 - November - 1993
+
+Environment:
+
+ User mode
+
+Revision History :
+
+--*/
+
+#ifdef RPL_DEBUG
+VOID RplDebugInitialize( VOID);
+VOID RplDebugDelete( VOID);
+#endif // RPL_DEBUG
diff --git a/private/net/svcdlls/rpl/rplinst/local.h b/private/net/svcdlls/rpl/rplinst/local.h
new file mode 100644
index 000000000..ebdc7068b
--- /dev/null
+++ b/private/net/svcdlls/rpl/rplinst/local.h
@@ -0,0 +1,77 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ local.h
+
+Abstract:
+
+ Main include file for RPLINST
+
+Author:
+
+ Jon Newman (jonn) 12 - January - 1994
+
+Revision History:
+
+--*/
+
+#include <nt.h> // ntexapi.h\NtQuerySystemTime
+#include <ntrtl.h> // RtlTimeToSecondsSince1970
+#include <nturtl.h>
+
+#include <windows.h> // DWORD, IN, File APIs, etc.
+#include <stdlib.h>
+#include <lmcons.h>
+
+#include <stdio.h> // vsprintf
+#include <ctype.h> // isspace
+
+#include <lmerr.h> // NERR_RplBootStartFailed - used to be here
+
+//
+#include <lmapibuf.h> // NetApiBufferFree
+#include <netlib.h>
+
+#include <lmsvc.h>
+
+#include <lmalert.h> // STD_ALERT ALERT_OTHER_INFO
+
+#include <lmerrlog.h>
+#include <alertmsg.h>
+#include <lmserver.h>
+#include <netlib.h>
+#include <netlock.h> // Lock data types, functions, and macros.
+#include <thread.h> // FORMAT_NET_THREAD_ID, NetpCurrentThread().
+
+#include <lmshare.h> // PSHARE_INFO_2
+#include <lmaccess.h> // NetGroupGetInfo
+#include <lmconfig.h> // NetConfigGet
+#include <nb30.h> // NCB
+
+//
+// Global types and constants (used in all RPL server files).
+//
+
+#include <riplcons.h> // includes __JET500 flag
+#include <jet.h>
+#include <rpllib.h> // AddKey(), MapJetError(), FillTcpIpString()
+
+#include <rpldb.h>
+#include <rpldebug.h>
+
+#include "rplmgrd.h"
+#include "nlstxt.h"
+
+#include <lmshare.h>
+
+#define RPLFILES_STRING L"RPLFILES"
+#define RPL_SERVICE_NAME L"RemoteBoot"
+#define RPL_REMARK L"RemoteBoot service share"
+
+#define RPL_BUGBUG_ERROR 10999 // need to get rid of these
+
+WCHAR RG_Directory[ PATHLEN+1]; // path to rpl service disk data
+DWORD RG_DirectoryLength; // length of the above path
diff --git a/private/net/svcdlls/rpl/rplinst/makefile b/private/net/svcdlls/rpl/rplinst/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/net/svcdlls/rpl/rplinst/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/net/svcdlls/rpl/rplinst/makefile.inc b/private/net/svcdlls/rpl/rplinst/makefile.inc
new file mode 100644
index 000000000..f1d8b42fd
--- /dev/null
+++ b/private/net/svcdlls/rpl/rplinst/makefile.inc
@@ -0,0 +1,8 @@
+# templated from portuas JonN 1/14/94
+nlstxt.mc: rplimsg.h
+ mapmsg -p RPLI RPLI_BASE rplimsg.h > nlstxt.mc
+
+rplinst.rc: nlstxt.rc msg00001.bin
+
+nlstxt.h nlstxt.rc msg00001.bin: nlstxt.mc
+ mc -v nlstxt.mc
diff --git a/private/net/svcdlls/rpl/rplinst/querydir.c b/private/net/svcdlls/rpl/rplinst/querydir.c
new file mode 100644
index 000000000..64e9596c1
--- /dev/null
+++ b/private/net/svcdlls/rpl/rplinst/querydir.c
@@ -0,0 +1,136 @@
+/*++
+
+Copyright (c) 1987-1993 Microsoft Corporation
+
+Module Name:
+
+ querydir.c
+
+Abstract:
+
+ Contains I_NetRpl_QueryDirectory
+
+Author:
+
+ Jon Newman 13 - January - 1994
+
+Environment:
+
+ User mode
+
+Revision History :
+
+--*/
+
+#define RPLDATA_ALLOCATE
+#include "local.h"
+#undef RPLDATA_ALLOCATE
+
+
+#define RPL_REGISTRY_PARAMETERS L"System\\CurrentControlSet\\Services\\RemoteBoot\\Parameters"
+#define RPL_REGISTRY_DIRECTORY L"Directory"
+#define RPL_DEFAULT_DIRECTORY L"%SystemRoot%\\rpl\\"
+
+// BUGBUG server\rplmain.c should call this routine
+
+
+DWORD I_NetRpl_QueryDirectory( OUT LPWSTR pchFilePath,
+ OUT LPDWORD pcchFilePathLength)
+{
+ DWORD Error;
+ BYTE Buffer[ (PATHLEN + 1) * sizeof(WCHAR)];
+ PWCHAR Value;
+ HKEY Key;
+ DWORD Length;
+ DWORD Type;
+ DWORD Size;
+
+ // RPL_ASSERT( pchFilePath != NULL);
+
+ //
+ // First open the RPL service parameters registry tree.
+ //
+ Error = RegOpenKeyEx(
+ HKEY_LOCAL_MACHINE,
+ RPL_REGISTRY_PARAMETERS,
+ 0,
+ KEY_READ,
+ &Key
+ );
+ if (Error != ERROR_SUCCESS){
+ // RplDump( ++RG_Assert, ("Error = %d", Error));
+ return( Error);
+ }
+ // Length = sizeof( Buffer); This is totally wrong! Has Rl Server been
+ // using the default value this whole time?
+ // Length = sizeof( Buffer);
+ Size = sizeof( Buffer);
+ Error = RegQueryValueEx(
+ Key,
+ RPL_REGISTRY_DIRECTORY,
+ NULL,
+ &Type,
+ Buffer,
+ &Size
+ );
+ (VOID)RegCloseKey( Key);
+ if ( Error == NO_ERROR) {
+ //
+ // We have the data. First validate the data, then append the
+ // backslash if needed.
+ //
+ Value = (PWCHAR)Buffer;
+ if ( Type != REG_EXPAND_SZ || (Size&1) != 0 || Size < sizeof(L"c:")) {
+ // RplDump( ++RG_Assert, ("Type = 0x%x, Value = 0x%x, Size=%d",
+ // Type, Value, Size));
+ return( RPL_BUGBUG_ERROR);
+ }
+ Length = Size / sizeof(WCHAR) - 1;
+ if ( Value[ Length] != 0 || Length != wcslen(Value)) {
+ // RplDump( ++RG_Assert, ("Length = %d, Value = 0x%x, Size=%d",
+ // Length, Value, Size));
+ return( RPL_BUGBUG_ERROR);
+ }
+ if ( Value[ Length - 1] != L'\\') {
+ //
+ // Need to append the backslash.
+ //
+ if ( Length >= PATHLEN) {
+ // RplDump( ++RG_Assert, ("Length = %d, Value = 0x%x, Size=%d",
+ // Length, Value, Size));
+ return( RPL_BUGBUG_ERROR); // not enough space
+ }
+ Value[ Length] = L'\\';
+ Value[ ++Length] = 0;
+ }
+ } else {
+ Value = RPL_DEFAULT_DIRECTORY;
+ }
+
+ //
+ // Length returned by ExpandEnvironmentalStrings includes terminating
+ // NULL byte.
+ //
+ // This code was wrong!! The third parameter to ExpandEnvironmentStrings
+ // should be a character count, not a byte count.
+ //
+ // Length = ExpandEnvironmentStrings( Value, RG_Directory, sizeof( RG_Directory));
+ Length = ExpandEnvironmentStrings( Value, pchFilePath, PATHLEN+1);
+ if ( Length == 0) {
+ Error = GetLastError();
+ // RplDump( ++RG_Assert, ("Error = %d", Error));
+ return( Error);
+ }
+ if ( Length > PATHLEN+1) {
+ // RplDump( ++RG_Assert, ("Length = %d", Length));
+ return( RPL_BUGBUG_ERROR);
+ }
+ if ( Length != wcslen( pchFilePath) + 1) {
+ // RplDump( ++RG_Assert, ("Length = %d", Length));
+ return( RPL_BUGBUG_ERROR);
+ }
+ if (pcchFilePathLength != NULL) {
+ *pcchFilePathLength = (DWORD)(Length - 1);
+ }
+ return( NO_ERROR);
+}
diff --git a/private/net/svcdlls/rpl/rplinst/rplimsg.h b/private/net/svcdlls/rpl/rplinst/rplimsg.h
new file mode 100644
index 000000000..f66f2c940
--- /dev/null
+++ b/private/net/svcdlls/rpl/rplinst/rplimsg.h
@@ -0,0 +1,55 @@
+#define RPLI_BASE 11000 /* RPLI Messages start here */
+#define RPLI_MAX 11999 /* RPLI Messages end here */
+
+#define RPLI_CreatingShare (RPLI_BASE + 0)
+ /*
+ *Creating RPLFILES share...
+ */
+#define RPLI_ErrorCreatingShare (RPLI_BASE + 1)
+ /*
+ *Error creating RPLFILES share:%n%1!ls!
+ */
+#define RPLI_ConfiguringService (RPLI_BASE + 2)
+ /*
+ *Making RPL service autostart...
+ */
+#define RPLI_ErrorConfiguringService (RPLI_BASE + 3)
+ /*
+ *Error making RPL service autostart:%n%1!ls!
+ */
+#define RPLI_RPLINSTcomplete (RPLI_BASE + 4)
+ /*
+ *RPLINST completed successfully!
+ */
+#define RPLI_ErrorReadingRPLDirectory (RPLI_BASE + 5)
+ /*
+ *The registry key RPL\\Parameters value Directory could not be read due to the following error:%n%1!ls!
+ */
+#define RPLI_ErrorReadingString (RPLI_BASE + 6)
+ /*
+ *Could not read string
+ */
+#define RPLI_SettingPermissions (RPLI_BASE + 7)
+ /*
+ *Making RPL service autostart...
+ */
+#define RPLI_ErrorSettingPermissions (RPLI_BASE + 8)
+ /*
+ *The file permissions on the RPL file tree could not be set due to the following error:%n%1!ls!
+ */
+#define RPLI_CreatingRPLUSER (RPLI_BASE + 9)
+ /*
+ *Creating the RPLUSER group...
+ */
+#define RPLI_ErrorCreatingRPLUSER (RPLI_BASE + 10)
+ /*
+ *The RPLUSER group could not be created due to the following error:%n%1!ls!
+ */
+#define RPLI_CheckingWkstaAccts (RPLI_BASE + 11)
+ /*
+ *Checking the workstation accounts...
+ */
+#define RPLI_ErrorCheckingWkstaAccts (RPLI_BASE + 12)
+ /*
+ *The following error occurred checking the workstation accounts:%n%1!ls!
+ */
diff --git a/private/net/svcdlls/rpl/rplinst/rplinst.c b/private/net/svcdlls/rpl/rplinst/rplinst.c
new file mode 100644
index 000000000..38c018a30
--- /dev/null
+++ b/private/net/svcdlls/rpl/rplinst/rplinst.c
@@ -0,0 +1,137 @@
+/*++
+
+Copyright (c) 1987-1993 Microsoft Corporation
+
+Module Name:
+
+ rplinst.c
+
+Abstract:
+
+ Secondary setup activity, including
+
+ Establish RPLFILES share
+
+ Set up permissions on RPLFILES tree
+
+ BUGBUG Should polish and make WIN32 application out of this.
+
+Author:
+
+ Jon Newman (jonn) 13 - January - 1994
+
+Environment:
+
+ User mode
+
+Revision History :
+
+--*/
+
+#define RPLDATA_ALLOCATE
+#include "local.h"
+#undef RPLDATA_ALLOCATE
+
+#include "security.h"
+
+WCHAR RG_Directory[ PATHLEN+1];
+DWORD RG_DirectoryLength;
+
+
+DWORD _CRTAPI1 main ( VOID)
+{
+ DWORD dwErr = NO_ERROR;
+ RPL_HANDLE hRpl = NULL;
+ WCHAR awchBase[ MAX_PATH ];
+
+ dwErr = I_NetRpl_QueryDirectory( RG_Directory, &RG_DirectoryLength);
+ if ( dwErr != NO_ERROR) {
+ I_NetRplCmd_WriteError( STDERR, RPLI_ErrorReadingRPLDirectory, dwErr);
+ goto cleanup;
+ }
+
+ I_NetRplCmd_WriteMessage( STDOUT, RPLI_CreatingRPLUSER);
+
+ dwErr = RplAddRPLUSERGroup();
+ if (dwErr != NO_ERROR) {
+ I_NetRplCmd_WriteError( STDERR, RPLI_ErrorCreatingRPLUSER, dwErr);
+ goto cleanup;
+ }
+
+ dwErr = NetRplOpen( NULL, &hRpl );
+ if (dwErr != NO_ERROR) {
+ I_NetRplCmd_WriteError( STDERR, RPLI_ErrorCheckingWkstaAccts, dwErr);
+ goto cleanup;
+ }
+
+ I_NetRplCmd_WriteMessage( STDOUT, RPLI_CheckingWkstaAccts);
+
+ dwErr = RplCheckWkstaAccounts( hRpl );
+ if (dwErr != NO_ERROR) {
+ I_NetRplCmd_WriteError( STDERR, RPLI_ErrorCheckingWkstaAccts, dwErr);
+ goto cleanup;
+ }
+
+ I_NetRplCmd_WriteMessage( STDOUT, RPLI_SettingPermissions);
+
+ ASSERT( RG_DirectoryLength < MAX_PATH );
+ ASSERT( RG_DirectoryLength == lstrlenW(RG_DirectoryLength) );
+ ASSERT( RG_Directory[ RG_DirectoryLength-1 ] == L'\\' );
+ lstrcpyW( awchBase, RG_Directory );
+ awchBase[ RG_DirectoryLength-1 ] = L'\0';
+
+ dwErr = RplSetPermsOnTree( awchBase );
+ if (dwErr != NO_ERROR) {
+ I_NetRplCmd_WriteError( STDERR, RPLI_ErrorSettingPermissions, dwErr);
+ goto cleanup;
+ }
+
+ I_NetRplCmd_WriteMessage( STDOUT, RPLI_RPLINSTcomplete);
+
+cleanup:
+
+ if (hRpl != NULL) {
+ NetRplClose( hRpl );
+ }
+
+ return( dwErr);
+}
+
+
+//
+// allows us to use original security.c code
+//
+NET_API_STATUS NET_API_FUNCTION
+MyNetrRplWkstaEnum(
+ IN RPL_HANDLE ServerHandle,
+ IN LPWSTR ProfileName,
+ IN OUT LPRPL_WKSTA_ENUM WkstaEnum,
+ IN DWORD PrefMaxLength,
+ OUT LPDWORD TotalEntries,
+ OUT LPDWORD pResumeHandle OPTIONAL
+ )
+/*++
+ For more extensive comments see related code in NetrConfigEnum.
+--*/
+{
+ DWORD EntriesRead = 0;
+ LPBYTE Buffer = NULL;
+ DWORD Error = NO_ERROR;
+ // Error = NetApiBufferAllocate( 4096, &Buffer);
+
+ Error = NetRplWkstaEnum( ServerHandle,
+ ProfileName,
+ WkstaEnum->Level,
+ (LPBYTE *)&(WkstaEnum->WkstaInfo.Level0->Buffer),
+ PrefMaxLength,
+ &(WkstaEnum->WkstaInfo.Level0->EntriesRead),
+ TotalEntries,
+ pResumeHandle );
+
+ return( Error);
+}
+
+void __RPC_API MyMIDL_user_free( void __RPC_FAR * pv )
+{
+ NetApiBufferFree( pv );
+}
diff --git a/private/net/svcdlls/rpl/rplinst/rplinst.rc b/private/net/svcdlls/rpl/rplinst/rplinst.rc
new file mode 100644
index 000000000..dbaf0b6f0
--- /dev/null
+++ b/private/net/svcdlls/rpl/rplinst/rplinst.rc
@@ -0,0 +1,14 @@
+/* templated from portuas JonN 1/14/94 */
+#include <windows.h>
+
+#include <ntverp.h>
+
+#define VER_FILETYPE VFT_APP
+#define VER_FILESUBTYPE VFT2_UNKNOWN
+#define VER_FILEDESCRIPTION_STR "RPLINST Utility"
+#define VER_INTERNALNAME_STR "rplinst.exe"
+#define VER_ORIGINALFILENAME_STR "rplinst.exe"
+
+#include "common.ver"
+
+1 11 MSG00001.bin
diff --git a/private/net/svcdlls/rpl/rplinst/security.c b/private/net/svcdlls/rpl/rplinst/security.c
new file mode 100644
index 000000000..a064309f9
--- /dev/null
+++ b/private/net/svcdlls/rpl/rplinst/security.c
@@ -0,0 +1,1567 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ security.c
+
+Abstract:
+
+ Security addons to the tree-manipulation module
+ for the RPL service.
+
+Author:
+
+ Jon Newman (jonn) 16 - February - 1994
+
+Revision History:
+
+ Jon Newman (jonn) 16 - February - 1994
+ Added RplGrant*Perms primitives
+
+ CODEWORK: Should have more sophisticated error handling
+
+--*/
+
+
+#include "local.h"
+#include "tree.h"
+#include "treei.h"
+#include "security.h"
+#include <ntseapi.h>
+#include <ntrtl.h> // RtlAllocateAndInitializeSid
+#include <ntlsa.h>
+#include <ntsam.h>
+#include <nturtl.h> // see IsNTFS
+
+
+typedef enum _RPL_SD_BLOCK_TYPE {
+
+ RPL_SD_BLOCK_TYPE_ADMINONLY = 0,// Only RPLADMIN gets access
+
+ RPL_SD_BLOCK_TYPE_ALLUSERS, // RPLADMIN gets full access, RPLUSER
+ // gets RX access
+
+ RPL_SD_BLOCK_TYPE_ONEUSER, // RPLADMIN gets full access, one
+ // wksta account gets RWXCDA access
+
+ RPL_SD_BLOCK_TYPE_BADUSER // Skip setting permissions on this user
+} RPL_SD_BLOCK_TYPE;
+
+/*
+ * This is the block of information which is retained between the recursive
+ * calls in a single RplDoTree run. It remembers the SECURITY_DESCRIPTOR
+ * which was assigned to the last file/directory, so if the next
+ * file/directory wants the same permissions (as is usually the case)
+ * we do not have to fetch everything all over again. It also contains
+ * some information about what kind of SECURITY_DESCRIPTOR is currently
+ * stored, plus items needed to get a different SECURITY_DESCRIPTOR.
+ */
+typedef struct _RPL_SD_BLOCK {
+ PSECURITY_DESCRIPTOR psd;
+ RPL_SD_BLOCK_TYPE nChangeableAceType;
+ PSID psidSystem;
+ PSID psidAdministrators;
+ PSID psidSystemOperators;
+ PSID psidRPLUSER;
+ PSID psidGrantTo; // SID of account with name pwszGrantToName
+ PWCHAR pwszGrantToName;
+ // Name of account for BLOCK_TYPE_ONEUSER
+ POLICY_ACCOUNT_DOMAIN_INFO * pinfoAccountDomain;
+ SAM_HANDLE hsamAccountDomain;
+} RPL_SD_BLOCK, *PRPL_SD_BLOCK;
+
+//
+// The DACL we assign to files has 3 or 4 ACEs.
+//
+#define RPL_INDEX_SYSTEM_ACE 0
+#define RPL_INDEX_ADMINS_ACE 1
+#define RPL_INDEX_RPLADMIN_ACE 2
+#define RPL_INDEX_CHANGEABLE_ACE 3
+
+
+//
+// Forward declarations
+//
+
+DWORD SetRplPerms(
+ IN PWCHAR pwszPath,
+ IN OUT PVOID * ppv);
+
+DWORD GetSdForFile(
+ OUT PSECURITY_DESCRIPTOR * ppsd,
+ IN OUT RPL_SD_BLOCK ** ppsdblock,
+ IN PWCHAR pwszFile,
+ OUT BOOL * pfSkipPermsOnFile
+ );
+
+DWORD GetSd(
+ OUT PSECURITY_DESCRIPTOR * ppsd,
+ IN OUT RPL_SD_BLOCK ** ppsdblock,
+ IN RPL_SD_BLOCK_TYPE nChangeableAceType,
+ IN PWCHAR pwszGrantTo
+ );
+
+DWORD AddInheritableAce(
+ IN OUT ACL * pacl,
+ IN ACCESS_MASK mask,
+ IN SID * psid );
+
+DWORD InitSdBlock(
+ OUT RPL_SD_BLOCK ** ppsdblock
+ );
+
+VOID FreeSdBlock(
+ IN RPL_SD_BLOCK ** ppsdblock
+ );
+
+DWORD GetAccountDomain(
+ OUT SAM_HANDLE * phsamAccountDomain,
+ OUT POLICY_ACCOUNT_DOMAIN_INFO ** ppinfoAccountDomain,
+ IN ACCESS_MASK DesiredAccess
+ );
+
+DWORD GetAccountSid(
+ IN SAM_HANDLE hsamAccountDomain,
+ IN POLICY_ACCOUNT_DOMAIN_INFO * pinfoAccountDomain,
+ IN WCHAR * pwszAccountName,
+ OUT PSID * ppsidAccountSid,
+ IN BOOL fIsLocalgroup,
+ OUT DWORD * pulRid OPTIONAL
+ );
+
+DWORD IsNTFS(
+ IN WCHAR * pszResource,
+ OUT BOOL * pfIsNTFS );
+
+DWORD RplAddWkstaAccount( LPWSTR pszWkstaName,
+ SAM_HANDLE hsamAccountDomain,
+ POLICY_ACCOUNT_DOMAIN_INFO * pinfoAccountDomain,
+ SAM_HANDLE hsamRPLUSER );
+
+VOID FillUnicodeString( PUNICODE_STRING punistr,
+ LPWSTR lpwsz );
+
+DWORD MyBuildSid( OUT PSID * ppsidAccountSid,
+ IN POLICY_ACCOUNT_DOMAIN_INFO * pinfoAccountDomain,
+ IN ULONG ulRid );
+
+VOID MyFreeSid( IN OUT PSID * ppsid );
+
+
+/*
+ * Like RplTreeCopy, but also sets permissions on target files/directories.
+ * Target must be in %RplRoot% subtree.
+ *
+ * CODEWORK do something with error buffer
+ */
+DWORD RplTreeCopyAndSetPerms(
+ IN PWCHAR pwszSource,
+ IN PWCHAR pwszTarget )
+{
+ WCHAR Buffer[ 300];
+ DWORD Error = NO_ERROR;
+ PVOID pv = NULL;
+ DWORD Flags = RPL_TREE_COPY | RPL_TREE_AUXILIARY;
+ BOOL fIsNTFS = TRUE;
+
+ //
+ // Check if volume is NTFS, if so never mind about ACLs
+ //
+
+ Error = IsNTFS( pwszTarget, &fIsNTFS );
+ if (Error != NO_ERROR) {
+ TREE_ASSERT( ( "Error=%d", Error));
+ goto cleanup;
+ } else if (!fIsNTFS) {
+ Flags &= ~RPL_TREE_AUXILIARY;
+ }
+
+ Buffer[ 0] = 0;
+ Error = RplDoTree( pwszSource, pwszTarget,
+ Flags,
+ Buffer, sizeof(Buffer),
+ SetRplPerms, &pv);
+ if ( Error != NO_ERROR) {
+ TREE_ASSERT( ( "Error=%d", Error));
+ goto cleanup;
+ }
+
+cleanup:
+
+ if ( pv != NULL) {
+ FreeSdBlock( (RPL_SD_BLOCK **)(&pv) );
+ }
+
+ return( Error);
+}
+
+
+/*
+ * Sets permissions on a file tree.
+ * Target must be in %RplRoot% subtree.
+ *
+ * CODEWORK do something with error buffer
+ */
+DWORD RplSetPermsOnTree(
+ IN PWCHAR pwszSource )
+{
+ WCHAR Buffer[ 300];
+ DWORD Error = NO_ERROR;
+ PVOID pv = NULL;
+ BOOL fIsNTFS = TRUE;
+
+ //
+ // Check if volume is NTFS, if so do nothing
+ //
+
+ Error = IsNTFS( pwszSource, &fIsNTFS );
+ if (Error != NO_ERROR) {
+ TREE_ASSERT( ( "Error=%d", Error));
+ goto cleanup;
+ } else if (!fIsNTFS) {
+ goto cleanup;
+ }
+
+ Buffer[ 0] = 0;
+ Error = RplDoTree( pwszSource, NULL,
+ RPL_TREE_AUXILIARY,
+ Buffer, sizeof(Buffer),
+ SetRplPerms, &pv);
+ if ( Error != NO_ERROR) {
+ TREE_ASSERT( ( "Error=%d", Error));
+ goto cleanup;
+ }
+
+cleanup:
+
+ if ( pv != NULL) {
+ FreeSdBlock( (RPL_SD_BLOCK **)(&pv) );
+ }
+
+ return( Error);
+}
+
+
+/*
+ * This is the callback routine called by RplDoTree to set permissions
+ * on a specific file/directory.
+ */
+DWORD SetRplPerms(
+ IN PWCHAR pwszPath,
+ IN OUT PVOID * ppv)
+{
+ DWORD Error = NO_ERROR;
+ RPL_SD_BLOCK ** ppsdblock = (RPL_SD_BLOCK **)ppv;
+ PSECURITY_DESCRIPTOR psd = NULL;
+ BOOL fSkipPermsOnFile = FALSE;
+
+ RPL_ASSERT( pwszPath != NULL );
+ RPL_ASSERT( ppv != NULL );
+
+ Error = GetSdForFile( &psd, ppsdblock, pwszPath, &fSkipPermsOnFile );
+ if (Error != NO_ERROR || fSkipPermsOnFile) {
+ goto cleanup;
+ }
+
+ RPL_ASSERT( psd != NULL );
+
+ //
+ // Set the actual file security. Note that this call will succeed
+ // even if the volume is not NTFS, thus we must check for
+ // partition type elsewhere.
+ //
+
+ if ( !SetFileSecurity( pwszPath, DACL_SECURITY_INFORMATION, psd)) {
+ Error = GetLastError();
+ TREE_ASSERT( ( "Error=%d", Error));
+ goto cleanup;
+ }
+
+cleanup:
+
+ return( Error);
+}
+
+
+/*
+ * Get the SECURITY_DESCRIPTOR which is appropriate for a file/directory
+ * at this position in %RplRoot%. First we determine what kind of
+ * SECURITY_DESCRIPTOR we want, then we call GetSd() to build it.
+ */
+DWORD GetSdForFile(
+ OUT PSECURITY_DESCRIPTOR * ppsd,
+ IN OUT RPL_SD_BLOCK ** ppsdblock,
+ IN PWCHAR pwszFile,
+ OUT BOOL * pfSkipPermsOnFile
+ )
+{
+ DWORD Error = NO_ERROR;
+ PWCHAR pwszRelativeFile = NULL;
+ PWCHAR pwszWkstaNameStart = NULL;
+ PWCHAR pwszWkstaNameEnd = NULL;
+ RPL_SD_BLOCK_TYPE blocktype = RPL_SD_BLOCK_TYPE_ADMINONLY;
+ WCHAR awchFile[MAX_PATH];
+ WCHAR wchSave, wchSave2;
+ DWORD cch;
+
+ RPL_ASSERT( ppsd != NULL );
+ RPL_ASSERT( ppsdblock != NULL );
+ RPL_ASSERT( pwszFile != NULL );
+ RPL_ASSERT( pfSkipPermsOnFile != NULL );
+ RPL_ASSERT( lstrlenW(pwszFile) >= (INT)RG_DirectoryLength );
+ RPL_ASSERT( lstrlenW(pwszFile) < MAX_PATH );
+ RPL_ASSERT( RG_DirectoryLength > 0 && RG_DirectoryLength < MAX_PATH );
+
+ *pfSkipPermsOnFile = FALSE;
+
+ //
+ // Make working copy of pwszFile
+ //
+ if (NULL == lstrcpy(awchFile,pwszFile)) {
+ Error = GetLastError();
+ TREE_ASSERT( ( "Error=%d", Error));
+ goto cleanup;
+ }
+
+ //
+ // Ensure that awchFile starts with RG_Directory
+ //
+ // RG_Directory always ends with L'\\'. awchFile might be equal to
+ // RG_Directory without the L'\\', we must handle this.
+ //
+ wchSave = awchFile[RG_DirectoryLength];
+ wchSave2 = awchFile[RG_DirectoryLength-1];
+ if (lstrlenW(awchFile) == (int)RG_DirectoryLength-1) {
+ awchFile[RG_DirectoryLength-1] = L'\\';
+ }
+ awchFile[RG_DirectoryLength] = L'\0';
+ if ( 0 != lstrcmpi( awchFile,
+ RG_Directory )) {
+ RPL_ASSERT( FALSE );
+ Error = ERROR_INVALID_PARAMETER;
+ TREE_ASSERT( ( "Error=%d", Error));
+ goto cleanup;
+ }
+ awchFile[RG_DirectoryLength] = wchSave;
+ awchFile[RG_DirectoryLength-1] = wchSave2;
+
+ pwszRelativeFile = awchFile + RG_DirectoryLength;
+
+ //
+ // If the path is in rplfiles\binfiles or rplfiles\profiles,
+ // set the changeable ACE to RPLUSER
+ //
+
+ cch = lstrlenW(L"RPLFILES\\BINFILES");
+ wchSave = pwszRelativeFile[ cch ];
+ pwszRelativeFile[ cch ] = L'\0';
+ if ( 0 == lstrcmpi( pwszRelativeFile, L"RPLFILES\\BINFILES") ) {
+ blocktype = RPL_SD_BLOCK_TYPE_ALLUSERS;
+ goto cleanup;
+ }
+ pwszRelativeFile[ cch ] = wchSave;
+
+ cch = lstrlenW(L"RPLFILES\\PROFILES");
+ wchSave = pwszRelativeFile[ cch ];
+ pwszRelativeFile[ cch ] = L'\0';
+ if ( 0 == lstrcmpi( pwszRelativeFile, L"RPLFILES\\PROFILES") ) {
+ blocktype = RPL_SD_BLOCK_TYPE_ALLUSERS;
+ goto cleanup;
+ }
+ pwszRelativeFile[ cch ] = wchSave;
+
+ //
+ // If the path is in rplfiles\machines\* or rplfiles\tmpfiles\*,
+ // set the changeable ACE to the individual workstation account
+ //
+ cch = lstrlenW(L"RPLFILES\\MACHINES\\");
+ wchSave = pwszRelativeFile[ cch ];
+ pwszRelativeFile[ cch ] = L'\0';
+ if ( 0 == lstrcmpi( pwszRelativeFile, L"RPLFILES\\MACHINES\\") ) {
+ pwszRelativeFile[ cch ] = wchSave;
+ blocktype = RPL_SD_BLOCK_TYPE_ONEUSER;
+ pwszWkstaNameStart = pwszWkstaNameEnd = pwszRelativeFile + cch;
+ while (*pwszWkstaNameEnd != L'\\' && *pwszWkstaNameEnd != L'\0') {
+ pwszWkstaNameEnd++;
+ }
+ *pwszWkstaNameEnd = L'\0';
+ goto cleanup;
+ }
+ pwszRelativeFile[ cch ] = wchSave;
+
+ cch = lstrlenW(L"RPLFILES\\TMPFILES\\");
+ wchSave = pwszRelativeFile[ cch ];
+ pwszRelativeFile[ cch ] = L'\0';
+ if ( 0 == lstrcmpi( pwszRelativeFile, L"RPLFILES\\TMPFILES\\") ) {
+ pwszRelativeFile[ cch ] = wchSave;
+ blocktype = RPL_SD_BLOCK_TYPE_ONEUSER;
+ pwszWkstaNameStart = pwszWkstaNameEnd = pwszRelativeFile + cch;
+ while (*pwszWkstaNameEnd != L'\\' && *pwszWkstaNameEnd != L'\0') {
+ pwszWkstaNameEnd++;
+ }
+ *pwszWkstaNameEnd = L'\0';
+ goto cleanup;
+ }
+ pwszRelativeFile[ cch ] = wchSave;
+
+cleanup:
+
+ //
+ // We now know what kind of SECURITY_DESCRIPTOR we want, so get it.
+ //
+ if (Error == NO_ERROR && !(*pfSkipPermsOnFile)) {
+ Error = GetSd( ppsd, ppsdblock, blocktype, pwszWkstaNameStart );
+ }
+
+ return( Error);
+}
+
+
+/*
+ * This call gets an actual security descriptor which corresponds to
+ * nChangeableAceType and pwszGrantTo. The security descriptor
+ * is only valid until next call to GetSd().
+ *
+ * The caller has to free *ppsdblock eventually, but should not free
+ * *ppsd.
+ */
+DWORD GetSd(
+ OUT PSECURITY_DESCRIPTOR * ppsd,
+ IN OUT RPL_SD_BLOCK ** ppsdblock,
+ IN RPL_SD_BLOCK_TYPE nChangeableAceType,
+ IN PWCHAR pwszGrantTo
+ )
+{
+ DWORD Error = NO_ERROR;
+ PACL paclDacl = NULL;
+ BOOL fDaclPresent = FALSE;
+ BOOL fDaclDefaulted = FALSE;
+
+ RPL_ASSERT( ppsd != NULL);
+ RPL_ASSERT( ppsdblock != NULL);
+
+ //
+ // Create the RPL_SD_BLOCK if it does not yet exist
+ //
+ if ( *ppsdblock == NULL )
+ {
+ Error = InitSdBlock( ppsdblock );
+ if ( Error != NO_ERROR) {
+ TREE_ASSERT( ( "Error=%d", Error));
+ goto cleanup;
+ }
+ }
+ RPL_ASSERT( *ppsdblock != NULL );
+
+ //
+ // If the RPL_SD_BLOCK is already configured correctly, return success
+ //
+ if ( ( (*ppsdblock)->nChangeableAceType == nChangeableAceType )
+ && ( (*ppsdblock)->nChangeableAceType != RPL_SD_BLOCK_TYPE_ONEUSER
+ || (0 == lstrcmpW((*ppsdblock)->pwszGrantToName, pwszGrantTo)) )
+ && ( (*ppsdblock)->nChangeableAceType != RPL_SD_BLOCK_TYPE_BADUSER
+ || (0 == lstrcmpW((*ppsdblock)->pwszGrantToName, pwszGrantTo)) )
+ )
+ {
+ goto cleanup;
+ }
+ //
+ // Extract the DACL
+ //
+ if ( !GetSecurityDescriptorDacl( (*ppsdblock)->psd,
+ &fDaclPresent,
+ &paclDacl,
+ &fDaclDefaulted
+ )) {
+ Error = GetLastError();
+ TREE_ASSERT( ( "Error=%d", Error));
+ goto cleanup;
+ }
+
+ RPL_ASSERT( fDaclPresent && !fDaclDefaulted );
+
+ //
+ // Clear the changeable ACE if present
+ //
+ if ( (*ppsdblock)->nChangeableAceType != RPL_SD_BLOCK_TYPE_ADMINONLY
+ && (*ppsdblock)->nChangeableAceType != RPL_SD_BLOCK_TYPE_BADUSER )
+ {
+ if ( !DeleteAce( paclDacl, RPL_INDEX_CHANGEABLE_ACE )) {
+ Error = GetLastError();
+ TREE_ASSERT( ( "Error=%d", Error));
+ goto cleanup;
+ }
+ // CODEWORK do we need to decrement ACE count here?
+ (*ppsdblock)->nChangeableAceType = RPL_SD_BLOCK_TYPE_ADMINONLY;
+ }
+
+ //
+ // Clear and rewrite psidGrantTo if necessary
+ //
+ if ( nChangeableAceType == RPL_SD_BLOCK_TYPE_ONEUSER
+ || nChangeableAceType == RPL_SD_BLOCK_TYPE_BADUSER )
+ {
+ if ( (*ppsdblock)->psidGrantTo != NULL) {
+ MyFreeSid( &((*ppsdblock)->psidGrantTo) );
+ TREE_FREE( (*ppsdblock)->pwszGrantToName );
+ (*ppsdblock)->pwszGrantToName = NULL;
+ }
+ if (lstrlenW(pwszGrantTo) > RPL_MAX_WKSTA_NAME_LENGTH)
+ {
+ TREE_ASSERT( ( "Bad user %ws", pwszGrantTo));
+ nChangeableAceType = RPL_SD_BLOCK_TYPE_BADUSER;
+ }
+ else
+ {
+ Error = GetAccountSid( (*ppsdblock)->hsamAccountDomain,
+ (*ppsdblock)->pinfoAccountDomain,
+ pwszGrantTo,
+ &((*ppsdblock)->psidGrantTo),
+ FALSE,
+ NULL );
+ // CODEWORK trap error here?
+ if ( Error != NO_ERROR) {
+ TREE_ASSERT( ( "Error=%d", Error));
+ goto cleanup;
+ }
+ }
+
+ (*ppsdblock)->pwszGrantToName =
+ TREE_ALLOC( (lstrlenW(pwszGrantTo)+1) * sizeof(WCHAR) );
+ if ( (*ppsdblock)->pwszGrantToName == NULL )
+ {
+ Error = ERROR_NOT_ENOUGH_MEMORY;
+ TREE_ASSERT( ( "Error=%d", Error));
+ goto cleanup;
+ }
+ lstrcpyW( (*ppsdblock)->pwszGrantToName, pwszGrantTo );
+ }
+
+ //
+ // Add changeable ACE if desired
+ //
+ RPL_ASSERT( (*ppsdblock)->nChangeableAceType = RPL_SD_BLOCK_TYPE_ADMINONLY );
+ switch (nChangeableAceType) {
+ case RPL_SD_BLOCK_TYPE_ADMINONLY:
+ case RPL_SD_BLOCK_TYPE_BADUSER:
+ break;
+ case RPL_SD_BLOCK_TYPE_ALLUSERS:
+ Error = AddInheritableAce( paclDacl,
+ GENERIC_READ | GENERIC_EXECUTE,
+ (*ppsdblock)->psidRPLUSER );
+ break;
+ case RPL_SD_BLOCK_TYPE_ONEUSER:
+ Error = AddInheritableAce( paclDacl,
+ GENERIC_READ
+ | GENERIC_WRITE
+ | GENERIC_EXECUTE
+ | DELETE,
+ (*ppsdblock)->psidGrantTo );
+ break;
+ default:
+ ASSERT(FALSE);
+ break;
+ }
+ if ( Error != NO_ERROR) {
+ TREE_ASSERT( ( "Error=%d", Error));
+ goto cleanup;
+ }
+ (*ppsdblock)->nChangeableAceType = nChangeableAceType;
+
+ //
+ // Save the upgraded ACL into the security descriptor
+ //
+ // CODEWORK is this needed?
+ //
+ if ( !SetSecurityDescriptorDacl( (*ppsdblock)->psd,
+ TRUE,
+ paclDacl,
+ FALSE
+ )) {
+ Error = GetLastError();
+ TREE_ASSERT( ( "Error=%d", Error));
+ goto cleanup;
+ }
+
+cleanup:
+
+ if (Error == NO_ERROR) {
+ *ppsd = (*ppsdblock)->psd;
+ }
+
+ return( Error);
+}
+
+
+SID_IDENTIFIER_AUTHORITY IDAuthorityNT = SECURITY_NT_AUTHORITY;
+
+/*
+ * Create a new RPL_SD_BLOCK
+ */
+DWORD InitSdBlock(
+ OUT RPL_SD_BLOCK ** ppsdblock
+ )
+{
+ DWORD Error = 0;
+ DWORD cbNewDacl = 0;
+ PACL paclDacl = NULL;
+
+ RPL_ASSERT( ppsdblock != NULL );
+ RPL_ASSERT( *ppsdblock == NULL );
+
+ //
+ // Allocate memory for the RPL_SD_BLOCK
+ //
+ *ppsdblock = TREE_ALLOC( sizeof(RPL_SD_BLOCK) );
+ if ( *ppsdblock == NULL) {
+ Error = ERROR_NOT_ENOUGH_MEMORY;
+ goto cleanup;
+ }
+ ZeroMemory( (*ppsdblock), sizeof(RPL_SD_BLOCK) );
+
+ //
+ // Get System SID
+ //
+ Error = RtlAllocateAndInitializeSid(
+ &IDAuthorityNT,
+ 1,
+ SECURITY_LOCAL_SYSTEM_RID,
+ 0, 0, 0, 0, 0, 0, 0,
+ &((*ppsdblock)->psidSystem)
+ );
+ if (Error != NO_ERROR) {
+ goto cleanup;
+ }
+
+ //
+ // Get Administrators SID
+ //
+ Error = RtlAllocateAndInitializeSid(
+ &IDAuthorityNT,
+ 2,
+ SECURITY_BUILTIN_DOMAIN_RID,
+ DOMAIN_ALIAS_RID_ADMINS,
+ 0, 0, 0, 0, 0, 0,
+ &((*ppsdblock)->psidAdministrators)
+ );
+ if (Error != NO_ERROR) {
+ goto cleanup;
+ }
+
+ //
+ // Get System Operators SID
+ //
+ Error = RtlAllocateAndInitializeSid(
+ &IDAuthorityNT,
+ 2,
+ SECURITY_BUILTIN_DOMAIN_RID,
+ DOMAIN_ALIAS_RID_SYSTEM_OPS,
+ 0, 0, 0, 0, 0, 0,
+ &((*ppsdblock)->psidSystemOperators)
+ );
+ if ( Error != NO_ERROR) {
+ TREE_ASSERT( ( "Error=%d", Error));
+ goto cleanup;
+ }
+
+ //
+ // Get information about account domain
+ //
+ Error = GetAccountDomain( &((*ppsdblock)->hsamAccountDomain),
+ &((*ppsdblock)->pinfoAccountDomain),
+ GENERIC_EXECUTE );
+ if ( Error != NO_ERROR) {
+ TREE_ASSERT( ( "Error=%d", Error));
+ goto cleanup;
+ }
+
+ //
+ // Get RPLUSER SID
+ //
+ Error = GetAccountSid( (*ppsdblock)->hsamAccountDomain,
+ (*ppsdblock)->pinfoAccountDomain,
+ RPL_GROUP_RPLUSER,
+ &((*ppsdblock)->psidRPLUSER),
+ TRUE,
+ NULL );
+ if ( Error != NO_ERROR) {
+ TREE_ASSERT( ( "Error=%d", Error));
+ goto cleanup;
+ }
+
+ //
+ // Start assembling DACL
+ //
+ cbNewDacl = sizeof(ACL) + (3*sizeof(ACCESS_ALLOWED_ACE))
+ + (3*GetSidLengthRequired(SID_MAX_SUB_AUTHORITIES))
+ - sizeof(DWORD);
+ paclDacl = (PACL) TREE_ALLOC( cbNewDacl);
+ if ( paclDacl == NULL) {
+ Error = ERROR_NOT_ENOUGH_MEMORY;
+ goto cleanup;
+ }
+ if ( !InitializeAcl( paclDacl, cbNewDacl, ACL_REVISION )) {
+ Error = GetLastError();
+ TREE_ASSERT( ( "Error=%d", Error));
+ goto cleanup;
+ }
+
+ //
+ // Add System ACE
+ //
+ Error = AddInheritableAce( paclDacl,
+ GENERIC_ALL,
+ (*ppsdblock)->psidSystem );
+ if (Error != NO_ERROR) {
+ TREE_ASSERT( ( "Error=%d", Error));
+ goto cleanup;
+ }
+ // CODEWORK do we still want to free psidSystem?
+
+ //
+ // Add Administrators ACE
+ //
+ Error = AddInheritableAce( paclDacl,
+ GENERIC_ALL,
+ (*ppsdblock)->psidAdministrators );
+ if (Error != NO_ERROR) {
+ TREE_ASSERT( ( "Error=%d", Error));
+ goto cleanup;
+ }
+ // CODEWORK do we still want to free psidAdministrators?
+
+ //
+ // Add System Operators ACE
+ //
+ Error = AddInheritableAce( paclDacl,
+ GENERIC_ALL,
+ (*ppsdblock)->psidSystemOperators );
+ if (Error != NO_ERROR) {
+ TREE_ASSERT( ( "Error=%d", Error));
+ goto cleanup;
+ }
+ // BUGBUG shouldn't add this for WINNT machines!
+ // CODEWORK do we still want to free psidSystemOperators?
+
+ //
+ // Build security descriptor
+ //
+ (*ppsdblock)->psd = TREE_ALLOC( sizeof(SECURITY_DESCRIPTOR) );
+ if ( (*ppsdblock)->psd == NULL) {
+ Error = ERROR_NOT_ENOUGH_MEMORY;
+ goto cleanup;
+ }
+ if ( !InitializeSecurityDescriptor( (*ppsdblock)->psd,
+ SECURITY_DESCRIPTOR_REVISION )) {
+ Error = GetLastError();
+ TREE_ASSERT( ( "Error=%d", Error));
+ goto cleanup;
+ }
+ if ( !SetSecurityDescriptorDacl( (*ppsdblock)->psd,
+ TRUE,
+ paclDacl,
+ FALSE )) {
+ Error = GetLastError();
+ TREE_ASSERT( ( "Error=%d", Error));
+ goto cleanup;
+ }
+
+ //
+ // Set nChangeableAceType in RPL_SD_BLOCK
+ //
+ (*ppsdblock)->nChangeableAceType = RPL_SD_BLOCK_TYPE_ADMINONLY;
+
+cleanup:
+
+ if (Error != NO_ERROR)
+ {
+ FreeSdBlock( ppsdblock );
+
+ if (paclDacl != NULL) {
+ TREE_FREE( paclDacl );
+ paclDacl = NULL;
+ }
+ }
+
+ return(Error);
+}
+
+/*
+ * Add an ACCESS_ALLOWED_ACE to the ACL, with all inheritance flags set
+ */
+DWORD AddInheritableAce(
+ IN OUT ACL * pacl,
+ IN ACCESS_MASK mask,
+ IN SID * psid )
+{
+ DWORD Error = NO_ERROR;
+ ACCESS_ALLOWED_ACE * pace = NULL;
+
+ if ( !AddAccessAllowedAce( pacl,
+ ACL_REVISION,
+ mask,
+ psid )) {
+ Error = GetLastError();
+ TREE_ASSERT( ( "Error=%d", Error));
+ goto cleanup;
+ }
+
+ //
+ // new ACE should be the last ACE
+ //
+ if ( !GetAce( pacl, (pacl->AceCount) - 1, &pace )) {
+ Error = GetLastError();
+ TREE_ASSERT( ( "Error=%d", Error));
+ goto cleanup;
+ }
+
+ RPL_ASSERT( EqualSid( psid, &(pace->SidStart)) );
+
+ (pace->Header.AceFlags) |= (OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE);
+
+cleanup:
+
+ return( Error);
+}
+
+
+/*
+ * The RplDoTree run is over, free the RPL_SD_BLOCK
+ */
+VOID FreeSdBlock(
+ IN RPL_SD_BLOCK ** ppsdblock
+ )
+{
+ RPL_ASSERT( ppsdblock != NULL );
+
+ if (*ppsdblock != NULL)
+ {
+ if ((*ppsdblock)->psd != NULL) {
+ TREE_FREE( (*ppsdblock)->psd );
+ }
+ if ((*ppsdblock)->pwszGrantToName != NULL) {
+ TREE_FREE( (*ppsdblock)->pwszGrantToName );
+ }
+ if ((*ppsdblock)->psidSystem != NULL) {
+ RtlFreeSid( (*ppsdblock)->psidSystem );
+ }
+ if ((*ppsdblock)->psidAdministrators != NULL) {
+ RtlFreeSid( (*ppsdblock)->psidAdministrators );
+ }
+ if ((*ppsdblock)->psidSystemOperators != NULL) {
+ RtlFreeSid( (*ppsdblock)->psidSystemOperators);
+ }
+ MyFreeSid( &((*ppsdblock)->psidRPLUSER) );
+ MyFreeSid( &((*ppsdblock)->psidGrantTo) );
+ if ( (*ppsdblock)->pinfoAccountDomain != NULL) {
+ LsaFreeMemory( (*ppsdblock)->pinfoAccountDomain);
+ }
+ if ((*ppsdblock)->hsamAccountDomain != NULL) {
+ SamCloseHandle( (*ppsdblock)->hsamAccountDomain);
+ }
+ }
+ TREE_FREE( *ppsdblock );
+ *ppsdblock = NULL;
+}
+
+
+/*
+ * Get a SAM_HANDLE to the account domain, and other information about
+ * the account domain. This routine uses SAM and LSA directly.
+ */
+DWORD GetAccountDomain(
+ OUT SAM_HANDLE * phsamAccountDomain,
+ OUT POLICY_ACCOUNT_DOMAIN_INFO ** ppinfoAccountDomain,
+ IN ACCESS_MASK DesiredAccess
+ )
+{
+ DWORD Error = NO_ERROR;
+ SAM_HANDLE hsamServer = NULL;
+ LSA_HANDLE hlsa = NULL;
+ OBJECT_ATTRIBUTES oa;
+ POLICY_ACCOUNT_DOMAIN_INFO * pacctdominfo = NULL;
+ SECURITY_QUALITY_OF_SERVICE sqos;
+
+ RPL_ASSERT( phsamAccountDomain != NULL );
+ RPL_ASSERT( ppinfoAccountDomain != NULL );
+
+ //
+ // Get server handle
+ //
+
+ Error = SamConnect( NULL,
+ &hsamServer,
+ SAM_SERVER_LOOKUP_DOMAIN,
+ NULL );
+ if ( Error != NERR_Success) {
+ TREE_ASSERT( ( "Error=%d", Error));
+ goto cleanup;
+ }
+
+ //
+ // Set up object attributes (borrowed from uintlsa.cxx)
+ //
+
+ sqos.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
+ sqos.ImpersonationLevel = SecurityImpersonation;
+ sqos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
+ sqos.EffectiveOnly = FALSE;
+
+ InitializeObjectAttributes( &oa, NULL, 0L, NULL, NULL );
+ oa.SecurityQualityOfService = &sqos;
+
+ //
+ // Get LSA handle
+ //
+
+ Error = LsaOpenPolicy( NULL,
+ &oa,
+ GENERIC_EXECUTE,
+ &hlsa );
+ if ( Error != NERR_Success) {
+ TREE_ASSERT( ( "Error=%d", Error));
+ goto cleanup;
+ }
+
+ //
+ // Get primary domain information
+ //
+
+ Error = LsaQueryInformationPolicy( hlsa,
+ PolicyAccountDomainInformation,
+ ppinfoAccountDomain );
+ if ( Error != NERR_Success) {
+ TREE_ASSERT( ( "Error=%d", Error));
+ goto cleanup;
+ }
+
+ //
+ // Open account domain handle
+ //
+
+ Error = SamOpenDomain( hsamServer,
+ DesiredAccess,
+ (*ppinfoAccountDomain)->DomainSid,
+ phsamAccountDomain );
+ if ( Error != NERR_Success) {
+ TREE_ASSERT( ( "Error=%d", Error));
+ goto cleanup;
+ }
+
+cleanup:
+
+ if ( hsamServer != NULL) {
+ SamCloseHandle( hsamServer);
+ }
+
+ if ( hlsa != NULL) {
+ LsaClose( hlsa);
+ }
+
+ if ( Error != NO_ERROR && (*ppinfoAccountDomain) != NULL) {
+ LsaFreeMemory( (*ppinfoAccountDomain));
+ *ppinfoAccountDomain = NULL;
+ }
+
+ if ( Error != NO_ERROR && phsamAccountDomain != NULL) {
+ SamCloseHandle( *phsamAccountDomain);
+ *phsamAccountDomain = NULL;
+ }
+
+ return( Error);
+}
+
+
+/*
+ * Get the SID for an account in the account domain. This method
+ * uses SAM directly to do the lookup, this is why we need to keep
+ * around a SAM domain handle.
+ *
+ * Returns NERR_RplWkstaNeedsUserAcct if the account is not found and
+ * !fIsLocalgroup, NERR_RplNeedsRPLUSERAcct if fIsLocalgroup
+ *
+ * Use MyFreeSid to free the SID returned in **ppsidAccountSid
+ */
+DWORD GetAccountSid(
+ IN SAM_HANDLE hsamAccountDomain,
+ IN POLICY_ACCOUNT_DOMAIN_INFO * pinfoAccountDomain,
+ IN WCHAR * pwszAccountName,
+ OUT PSID * ppsidAccountSid,
+ IN BOOL fIsLocalgroup,
+ OUT DWORD * pulRid OPTIONAL
+ )
+{
+ DWORD Error = NO_ERROR;
+ UNICODE_STRING unistr;
+ PULONG RelativeIds = NULL;
+ PSID_NAME_USE Use = NULL;
+ DWORD cbNewSid = 0;
+ PUCHAR pcSubAuthorityCount = NULL;
+ PDWORD pdwLastSubAuthority = NULL;
+
+ RPL_ASSERT( hsamAccountDomain != NULL );
+ RPL_ASSERT( pinfoAccountDomain != NULL );
+ RPL_ASSERT( pwszAccountName != NULL );
+ RPL_ASSERT( ppsidAccountSid != NULL && *ppsidAccountSid == NULL );
+
+ //
+ // Lookup name
+ //
+
+ FillUnicodeString( &unistr, pwszAccountName );
+
+ Error = SamLookupNamesInDomain( hsamAccountDomain,
+ 1,
+ &unistr,
+ &RelativeIds,
+ &Use );
+ if (Error != NO_ERROR) {
+ if (Error == STATUS_NONE_MAPPED) {
+ Error = (fIsLocalgroup) ? NERR_RplNeedsRPLUSERAcct
+ : NERR_RplWkstaNeedsUserAcct;
+ }
+ // TREE_ASSERT( ( "Error=%d", Error));
+ goto cleanup;
+ }
+
+ RPL_ASSERT( RelativeIds != NULL && Use != NULL );
+
+ if ( Use[0] != ((fIsLocalgroup) ? SidTypeAlias : SidTypeUser) ) {
+ Error = (fIsLocalgroup) ? NERR_RplNeedsRPLUSERAcct
+ : NERR_RplWkstaNeedsUserAcct;
+ goto cleanup;
+ }
+
+ //
+ // Construct account SID from RID and domain SID
+ //
+
+ Error = MyBuildSid( ppsidAccountSid, pinfoAccountDomain, RelativeIds[0] );
+ if (Error != NO_ERROR) {
+ TREE_ASSERT( ( "Error=%d", Error));
+ goto cleanup;
+ }
+
+cleanup:
+
+ if ( Error == NO_ERROR && pulRid != NULL) {
+ *pulRid = RelativeIds[0];
+ }
+
+ if (RelativeIds != NULL) {
+ SamFreeMemory( RelativeIds);
+ }
+ if (Use != NULL) {
+ SamFreeMemory( Use);
+ }
+ if ( Error != NO_ERROR ) {
+ MyFreeSid( ppsidAccountSid );
+ }
+
+ return( Error);
+}
+
+
+/*******************************************************************
+
+ NAME: IsNTFS
+
+ SYNOPSIS: This function checks the given file resource and attempts to
+ determine if it is on an NTFS partition.
+
+ ENTRY: pwszResource - Pointer to file/directory name in the form
+ "X:\aaa\bbb\ccc
+ pfIsNTFS - Pointer to BOOL that will receive the results
+
+ RETURNS: NO_ERROR if successful, error code otherwise
+
+ NOTES:
+
+ HISTORY:
+ JonN 23-Feb-1994 Copied from acledit\ntfsacl.cxx
+
+********************************************************************/
+
+DWORD IsNTFS(
+ IN WCHAR * pwszResource,
+ OUT BOOL * pfIsNTFS )
+{
+ DWORD Error = NO_ERROR ;
+ HANDLE hDrive = NULL ;
+ WCHAR awchDosDriveName[4];
+ WCHAR awchNtDriveName[40];
+ UNICODE_STRING unistrNtDriveName;
+ BYTE buffFsInfo[ sizeof(FILE_FS_ATTRIBUTE_INFORMATION) + 200 ] ;
+ PFILE_FS_ATTRIBUTE_INFORMATION FsInfo =
+ (PFILE_FS_ATTRIBUTE_INFORMATION)buffFsInfo ;
+ OBJECT_ATTRIBUTES oa ;
+ IO_STATUS_BLOCK StatusBlock ;
+
+ RPL_ASSERT( pwszResource != NULL
+ && lstrlen(pwszResource) >= 3
+ && pfIsNTFS != NULL ) ;
+
+ *pfIsNTFS = FALSE ;
+
+ //
+ // Determine the NT drive name
+ //
+
+ awchDosDriveName[0] = pwszResource[0];
+ awchDosDriveName[1] = pwszResource[1];
+ awchDosDriveName[2] = pwszResource[2];
+ awchDosDriveName[3] = L'\0';
+ RPL_ASSERT( awchDosDriveName[1] == L':'
+ && awchDosDriveName[2] == L'\\' );
+
+ unistrNtDriveName.Buffer = awchNtDriveName;
+ unistrNtDriveName.Length = sizeof(awchNtDriveName);
+ unistrNtDriveName.MaximumLength = sizeof(awchNtDriveName);
+
+ if (!RtlDosPathNameToNtPathName_U( awchDosDriveName,
+ &unistrNtDriveName,
+ NULL,
+ NULL))
+ {
+ RPL_ASSERT( FALSE ) ;
+ Error = ERROR_NOT_ENOUGH_MEMORY ;
+ goto cleanup;
+ }
+ RPL_ASSERT( unistrNtDriveName.Length < unistrNtDriveName.MaximumLength );
+ unistrNtDriveName.Buffer[unistrNtDriveName.Length/sizeof(WCHAR)] = L'\0';
+
+ //
+ // Open the NT volume and query volume information
+ //
+
+ InitializeObjectAttributes( &oa,
+ &unistrNtDriveName,
+ OBJ_CASE_INSENSITIVE,
+ 0,
+ 0 );
+ Error = NtOpenFile( &hDrive,
+ SYNCHRONIZE | FILE_READ_DATA,
+ &oa,
+ &StatusBlock,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ FILE_SYNCHRONOUS_IO_ALERT);
+ if (Error != NO_ERROR) {
+ TREE_ASSERT( ( "Error=%d", Error));
+ goto cleanup;
+ }
+ Error = NtQueryVolumeInformationFile(
+ hDrive,
+ &StatusBlock,
+ (PVOID) FsInfo,
+ sizeof(buffFsInfo),
+ FileFsAttributeInformation );
+ if (Error != NO_ERROR) {
+ if (Error == ERROR_ACCESS_DENIED) {
+ TREE_ASSERT( (
+"IsNTFS - Unable to determine volume information (access denied) assuming the file system is NTFS"
+ ) ) ;
+ Error = NO_ERROR ;
+ *pfIsNTFS = TRUE ;
+ } else {
+ TREE_ASSERT( ( "Error=%d", Error));
+ }
+ goto cleanup;
+ }
+
+ if ( FsInfo->FileSystemAttributes & FILE_PERSISTENT_ACLS )
+ {
+ *pfIsNTFS = TRUE ;
+ }
+
+cleanup:
+
+ /* Close the volume if we ever opened it
+ */
+ if ( hDrive != NULL )
+ {
+ NtClose( hDrive );
+ }
+
+ return( Error) ;
+}
+
+
+/*******************************************************************
+
+ NAME: RplCreateWkstaAccounts
+
+ SYNOPSIS: This function checks whether a user account exists for
+ each RPL workstation, and creates one if not. It also
+ adds all of these accounts to the RPLUSER local group.
+
+ RETURNS: NO_ERROR if successful, error code otherwise
+
+ NOTES:
+
+ HISTORY:
+ JonN 04-Mar-1994 Created
+
+********************************************************************/
+
+DWORD RplCheckWkstaAccounts( RPL_RPC_HANDLE ServerHandle )
+{
+ DWORD Error = 0;
+ POLICY_ACCOUNT_DOMAIN_INFO * pinfoAccountDomain = NULL;
+ SAM_HANDLE hsamAccountDomain = NULL;
+ PSID psidRPLUSER = NULL;
+ ULONG ulRidRPLUSER = 0;
+ SAM_HANDLE hsamRPLUSER = NULL;
+ DWORD ErrorEnum = NO_ERROR;
+ RPL_WKSTA_ENUM rplwkstaenum;
+ RPL_WKSTA_INFO_0_CONTAINER rplwksta0container;
+ DWORD cEntries = 0;
+ DWORD hResumeHandle = 0;
+ DWORD i;
+
+ rplwkstaenum.Level = 0;
+ rplwkstaenum.WkstaInfo.Level0 = &rplwksta0container;
+ rplwkstaenum.WkstaInfo.Level0->EntriesRead = 0;
+ rplwkstaenum.WkstaInfo.Level0->Buffer = NULL;
+
+ Error = GetAccountDomain( &hsamAccountDomain,
+ &pinfoAccountDomain,
+ GENERIC_EXECUTE | DOMAIN_CREATE_USER );
+ if ( Error != NO_ERROR) {
+ TREE_ASSERT( ( "Error=%d", Error));
+ goto cleanup;
+ }
+
+ Error = GetAccountSid( hsamAccountDomain,
+ pinfoAccountDomain,
+ RPL_GROUP_RPLUSER,
+ &psidRPLUSER,
+ TRUE,
+ &ulRidRPLUSER );
+ if ( Error != NO_ERROR) {
+ TREE_ASSERT( ( "Error=%d", Error));
+ goto cleanup;
+ }
+
+ Error = SamOpenAlias( hsamAccountDomain,
+ ALIAS_ADD_MEMBER,
+ ulRidRPLUSER,
+ &hsamRPLUSER );
+ if ( Error != NO_ERROR) {
+ TREE_ASSERT( ( "Error=%d", Error));
+ goto cleanup;
+ }
+
+ do {
+ ErrorEnum = NetrRplWkstaEnum( ServerHandle,
+ NULL,
+ &rplwkstaenum,
+ (ULONG)-1,
+ &cEntries,
+ &hResumeHandle );
+ switch (ErrorEnum) {
+ case STATUS_MORE_ENTRIES:
+ case ERROR_MORE_DATA:
+ case NO_ERROR:
+ case STATUS_NO_MORE_ENTRIES:
+
+ if (rplwkstaenum.WkstaInfo.Level0->Buffer == NULL) {
+ // no workstations
+ break;
+ }
+ for ( i = 0;
+ (Error == NO_ERROR)
+ && (i < rplwkstaenum.WkstaInfo.Level0->EntriesRead);
+ i++ ) {
+ Error = RplAddWkstaAccount(
+ rplwkstaenum.WkstaInfo.Level0->Buffer[i].WkstaName,
+ hsamAccountDomain,
+ pinfoAccountDomain,
+ hsamRPLUSER );
+ }
+
+ MIDL_user_free( rplwkstaenum.WkstaInfo.Level0->Buffer );
+ rplwkstaenum.WkstaInfo.Level0->Buffer = NULL;
+ break;
+
+ default:
+ Error = ErrorEnum;
+ TREE_ASSERT( ( "Error=%d", Error));
+ goto cleanup;
+ }
+ } while ( Error == STATUS_MORE_ENTRIES || Error == ERROR_MORE_DATA );
+
+cleanup:
+
+ /*
+ * We have no way to free the resume handle
+ */
+
+ if ( rplwkstaenum.WkstaInfo.Level0->Buffer != NULL ) {
+ MIDL_user_free( rplwkstaenum.WkstaInfo.Level0->Buffer );
+ }
+
+ if ( pinfoAccountDomain != NULL) {
+ LsaFreeMemory( pinfoAccountDomain);
+ }
+ if (hsamAccountDomain != NULL) {
+ SamCloseHandle( hsamAccountDomain);
+ }
+
+ if (hsamRPLUSER != NULL) {
+ SamCloseHandle( hsamRPLUSER);
+ }
+
+ if (hsamRPLUSER != NULL) {
+ SamCloseHandle( hsamRPLUSER);
+ }
+
+ MyFreeSid( &psidRPLUSER );
+
+ return( Error);
+}
+
+
+/*******************************************************************
+
+ NAME: RplAddWkstaAccount
+
+ SYNOPSIS: This function checks whether a user account exists for
+ a single workstation, and creates one if not. It also
+ adds the account to the RPLUSER local group.
+
+ RETURNS: NO_ERROR if successful, error code otherwise
+
+ NOTES:
+
+ HISTORY:
+ JonN 04-Mar-1994 Created
+
+********************************************************************/
+
+DWORD RplAddWkstaAccount( LPWSTR pszWkstaName,
+ SAM_HANDLE hsamAccountDomain,
+ POLICY_ACCOUNT_DOMAIN_INFO * pinfoAccountDomain,
+ SAM_HANDLE hsamRPLUSER )
+{
+ DWORD Error = 0;
+ UNICODE_STRING unistr;
+ PSID psidUser = NULL;
+ ULONG ulRidUser = 0;
+ SAM_HANDLE hsamUser = NULL;
+
+ RPL_ASSERT( pszWkstaName != NULL
+ && hsamAccountDomain != NULL
+ && pinfoAccountDomain != NULL
+ && hsamRPLUSER != NULL );
+
+ /*
+ * Get SID of existing account. It would be more efficient to look
+ * up all the accounts at once.
+ */
+
+ Error = GetAccountSid( hsamAccountDomain,
+ pinfoAccountDomain,
+ pszWkstaName,
+ &psidUser,
+ FALSE,
+ &ulRidUser );
+ if ( Error == NERR_RplWkstaNeedsUserAcct )
+ {
+ /*
+ * Account doesn't exist, add it
+ *
+ * Note that this will fail unless user is administrator or
+ * at least account operator
+ */
+
+ FillUnicodeString( &unistr, pszWkstaName );
+
+ Error = SamCreateUserInDomain( hsamAccountDomain,
+ &unistr,
+ 0x0,
+ &hsamUser,
+ &ulRidUser );
+ if (Error != NO_ERROR) {
+ TREE_ASSERT( ( "Error=%d", Error));
+ goto cleanup;
+ }
+
+ Error = MyBuildSid( &psidUser, pinfoAccountDomain, ulRidUser );
+ if (Error != NO_ERROR) {
+ TREE_ASSERT( ( "Error=%d", Error));
+ goto cleanup;
+ }
+
+ }
+
+ /*
+ * Add account to RPLUSER alias
+ *
+ * Note that this will fail unless user is administrator or
+ * at least account operator
+ */
+
+ Error = SamAddMemberToAlias( hsamRPLUSER, psidUser );
+ switch (Error) {
+ case STATUS_MEMBER_IN_ALIAS:
+ Error = NO_ERROR;
+ // fall through
+ case NO_ERROR:
+ break;
+ default:
+ TREE_ASSERT( ( "Error=%d", Error));
+ goto cleanup;
+ }
+
+
+cleanup:
+
+ MyFreeSid( &psidUser );
+
+ if (hsamUser != NULL) {
+ SamCloseHandle( hsamUser);
+ }
+
+ return( Error);
+}
+
+
+/*******************************************************************
+
+ NAME: RplAddRPLUSERGroup
+
+ SYNOPSIS: This function checks whether the RPLUSER local group
+ exists, and creates it if it doesn't.
+
+ RETURNS: NO_ERROR if successful, error code otherwise
+
+ NOTES:
+
+ HISTORY:
+ JonN 03-Mar-1994 Created
+
+********************************************************************/
+
+DWORD RplAddRPLUSERGroup( VOID )
+{
+ DWORD Error = 0;
+ POLICY_ACCOUNT_DOMAIN_INFO * pinfoAccountDomain = NULL;
+ SAM_HANDLE hsamAccountDomain = NULL;
+ PSID psidRPLUSER = NULL;
+ UNICODE_STRING unistr;
+ SAM_HANDLE hsamRPLUSER = NULL;
+ ULONG ulRidRPLUSER = 0;
+
+ Error = GetAccountDomain( &hsamAccountDomain,
+ &pinfoAccountDomain,
+ GENERIC_EXECUTE | DOMAIN_CREATE_ALIAS );
+ if ( Error != NO_ERROR) {
+ TREE_ASSERT( ( "Error=%d", Error));
+ goto cleanup;
+ }
+
+ Error = GetAccountSid( hsamAccountDomain,
+ pinfoAccountDomain,
+ RPL_GROUP_RPLUSER,
+ &psidRPLUSER,
+ TRUE,
+ NULL );
+ if ( Error == NO_ERROR) {
+ // RPLUSER already exists
+ goto cleanup;
+ } else if (Error != NERR_RplNeedsRPLUSERAcct)
+ {
+ TREE_ASSERT( ( "Error=%d", Error));
+ goto cleanup;
+ }
+
+ FillUnicodeString( &unistr, RPL_GROUP_RPLUSER );
+
+ Error = SamCreateAliasInDomain( hsamAccountDomain,
+ &unistr,
+ 0x0,
+ &hsamRPLUSER,
+ &ulRidRPLUSER );
+ if ( Error != NO_ERROR) {
+ TREE_ASSERT( ( "Error=%d", Error));
+ goto cleanup;
+ }
+
+cleanup:
+
+ if ( pinfoAccountDomain != NULL) {
+ LsaFreeMemory( pinfoAccountDomain);
+ }
+ if (hsamAccountDomain != NULL) {
+ SamCloseHandle( hsamAccountDomain);
+ }
+
+ if (hsamRPLUSER != NULL) {
+ SamCloseHandle( hsamRPLUSER);
+ }
+
+ MyFreeSid( &psidRPLUSER );
+
+ return( Error);
+}
+
+VOID FillUnicodeString( PUNICODE_STRING punistr,
+ LPWSTR lpwsz )
+{
+ ASSERT( punistr != NULL && lpwsz == NULL );
+ punistr->Buffer = lpwsz;
+ punistr->Length = lstrlenW(lpwsz) * sizeof(WCHAR);
+ punistr->MaximumLength = punistr->Length + sizeof(WCHAR);
+}
+
+DWORD MyBuildSid( OUT PSID * ppsidAccountSid,
+ IN POLICY_ACCOUNT_DOMAIN_INFO * pinfoAccountDomain,
+ IN ULONG ulRid )
+{
+ DWORD Error = 0;
+ DWORD cbNewSid = 0;
+ PUCHAR pcSubAuthorityCount = NULL;
+ PDWORD pdwLastSubAuthority = NULL;
+
+ ASSERT( ppsidAccountSid != NULL && pinfoAccountDomain != NULL && ulRid != 0 );
+
+ cbNewSid = GetLengthSid(pinfoAccountDomain->DomainSid) + sizeof(DWORD);
+ *ppsidAccountSid = TREE_ALLOC( cbNewSid );
+ if ( *ppsidAccountSid == NULL) {
+ Error = ERROR_NOT_ENOUGH_MEMORY;
+ goto cleanup;
+ }
+
+ if ( !CopySid( cbNewSid, *ppsidAccountSid, pinfoAccountDomain->DomainSid)) {
+ Error = GetLastError();
+ goto cleanup;
+ }
+
+ pcSubAuthorityCount = GetSidSubAuthorityCount( *ppsidAccountSid );
+ RPL_ASSERT( pcSubAuthorityCount != NULL );
+ (*pcSubAuthorityCount)++;
+ pdwLastSubAuthority = GetSidSubAuthority(
+ *ppsidAccountSid, (DWORD)((*pcSubAuthorityCount)-1) );
+ RPL_ASSERT( pdwLastSubAuthority != NULL );
+ *pdwLastSubAuthority = ulRid;
+
+cleanup:
+
+ if (Error != NO_ERROR) {
+ MyFreeSid( ppsidAccountSid );
+ }
+
+ return( Error);
+}
+
+VOID MyFreeSid( IN OUT PSID * ppsid )
+{
+ RPL_ASSERT( ppsid != NULL );
+
+ if ( *ppsid != NULL) {
+ TREE_FREE( *ppsid );
+ *ppsid = NULL;
+ }
+}
diff --git a/private/net/svcdlls/rpl/rplinst/security.h b/private/net/svcdlls/rpl/rplinst/security.h
new file mode 100644
index 000000000..403aa7fd1
--- /dev/null
+++ b/private/net/svcdlls/rpl/rplinst/security.h
@@ -0,0 +1,17 @@
+//
+// #define RPL_RPC_HANDLE RPL_HANDLE
+//
+
+#define NetrRplWkstaEnum MyNetrRplWkstaEnum
+#define MIDL_user_free MyMIDL_user_free
+
+#include <rplsvc_s.h> // RPL_WKSTA_ENUM
+
+#include "..\server\security.h"
+
+#undef TREE_ALLOC
+#undef TREE_FREE
+#undef TREE_ASSERT
+#define TREE_ALLOC(x) (malloc( (x) ))
+#define TREE_FREE(x) (free( (x) ))
+#define TREE_ASSERT( x ) ASSERT(FALSE)
diff --git a/private/net/svcdlls/rpl/rplinst/sources b/private/net/svcdlls/rpl/rplinst/sources
new file mode 100644
index 000000000..60cf3e52f
--- /dev/null
+++ b/private/net/svcdlls/rpl/rplinst/sources
@@ -0,0 +1,38 @@
+MAJORCOMP=net
+MINORCOMP=rplinst
+
+TARGETPATH=obj
+TARGETNAME=rplinst
+TARGETTYPE=PROGRAM
+NTTARGETFILE0=nlstxt.h nlstxt.mc nlstxt.rc
+
+TARGETLIBS= \
+ ..\lib\obj\*\rpllib.lib \
+ $(BASEDIR)\Public\Sdk\Lib\*\netlib.lib \
+ $(BASEDIR)\public\sdk\lib\*\netapi32.lib \
+ $(BASEDIR)\public\sdk\lib\*\samlib.lib \
+ $(BASEDIR)\public\sdk\lib\*\wsock32.lib \
+ $(BASEDIR)\public\sdk\lib\*\jet500.lib
+
+!IFNDEF DISABLE_NET_UNICODE
+UNICODE=1
+NET_C_DEFINES=-DUNICODE
+!ENDIF
+
+INCLUDES=.;..\inc;..\server;..\..\..\inc;..\..\..\api;..\..\..\..\inc;
+
+MSC_WARNING_LEVEL=/W3 /WX
+
+SOURCES= \
+ rplinst.rc \
+ rplinst.c \
+ security.c \
+ tree.c \
+ debug.c \
+ querydir.c
+
+
+C_DEFINES= -DINCL_32= -DNT -DRPC_NO_WINDOWS_H -DWIN32 -DRPL_RPLCNV
+
+UMTYPE=console
+
diff --git a/private/net/svcdlls/rpl/rplinst/tree.c b/private/net/svcdlls/rpl/rplinst/tree.c
new file mode 100644
index 000000000..08b3a942c
--- /dev/null
+++ b/private/net/svcdlls/rpl/rplinst/tree.c
@@ -0,0 +1,2 @@
+
+#include "..\server\tree.c"
diff --git a/private/net/svcdlls/rpl/rplinst/tree.h b/private/net/svcdlls/rpl/rplinst/tree.h
new file mode 100644
index 000000000..d68d34917
--- /dev/null
+++ b/private/net/svcdlls/rpl/rplinst/tree.h
@@ -0,0 +1,28 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ tree.h
+
+Abstract:
+
+ Exports from tree.c
+
+Author:
+
+ Jon Newman (jonn) 23 - November - 1993
+
+Revision History:
+
+ Vladimir Z. Vulovic (vladimv) 02 - December - 1993
+ Integrated with the rest of RPL service code.
+
+--*/
+
+DWORD RplTreeCopy( IN PWCHAR Source, IN PWCHAR Target);
+DWORD RplTreeDelete( IN PWCHAR Target);
+DWORD RplMakeDir( IN PWCHAR Target);
+
+
diff --git a/private/net/svcdlls/rpl/rplinst/treei.h b/private/net/svcdlls/rpl/rplinst/treei.h
new file mode 100644
index 000000000..3489cd759
--- /dev/null
+++ b/private/net/svcdlls/rpl/rplinst/treei.h
@@ -0,0 +1,51 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ tree.h
+
+Abstract:
+
+ Internal definitions from tree.c
+
+Author:
+
+ Jon Newman (jonn) 16 - February - 1994
+
+Revision History:
+
+ Jon Newman (jonn) 16 - February - 1994
+ Added RplGrant*Perms primitives
+
+--*/
+
+typedef DWORD (RPL_TREE_CALLBACK)( PWCHAR pszPath, PVOID * ppv);
+
+#define RPL_TREE_COPY_DACL 0x1 // not yet implemented
+#define RPL_TREE_COPY_SACL 0x2 // not yet implemented
+#define RPL_TREE_AUXILIARY 0x2000 // perform callback action
+#define RPL_TREE_COPY 0x4000
+#define RPL_TREE_DELETE 0x8000
+
+//
+// Forward declarations
+//
+
+DWORD RplDoTree(
+ PWCHAR Source,
+ PWCHAR Target,
+ DWORD Flags,
+ PWCHAR Buffer,
+ DWORD BufferSize,
+ RPL_TREE_CALLBACK AuxiliaryCallback,
+ PVOID AuxiliaryBlock
+ );
+
+VOID LoadError(
+ PWCHAR FilePath,
+ LPVOID Buffer,
+ DWORD BufferSize
+ );
+
diff --git a/private/net/svcdlls/rpl/rplstart/local.h b/private/net/svcdlls/rpl/rplstart/local.h
new file mode 100644
index 000000000..85977e516
--- /dev/null
+++ b/private/net/svcdlls/rpl/rplstart/local.h
@@ -0,0 +1,66 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ local.h
+
+Abstract:
+
+ Main include file for RPLSTART
+
+Author:
+
+ Jon Newman (jonn) 12 - January - 1994
+
+Revision History:
+
+--*/
+
+#include <nt.h> // ntexapi.h\NtQuerySystemTime
+#include <ntrtl.h> // RtlTimeToSecondsSince1970
+#include <nturtl.h>
+
+#include <windows.h> // DWORD, IN, File APIs, etc.
+#include <stdlib.h>
+#include <lmcons.h>
+
+#include <stdio.h> // vsprintf
+#include <ctype.h> // isspace
+
+#include <lmerr.h> // NERR_RplBootStartFailed - used to be here
+
+//
+#include <lmapibuf.h> // NetApiBufferFree
+#include <netlib.h>
+
+#include <lmsvc.h>
+
+#include <lmalert.h> // STD_ALERT ALERT_OTHER_INFO
+
+#include <lmerrlog.h>
+#include <alertmsg.h>
+#include <lmserver.h>
+#include <netlib.h>
+#include <netlock.h> // Lock data types, functions, and macros.
+#include <thread.h> // FORMAT_NET_THREAD_ID, NetpCurrentThread().
+
+#include <lmshare.h> // PSHARE_INFO_2
+#include <lmaccess.h> // NetGroupGetInfo
+#include <lmconfig.h> // NetConfigGet
+#include <nb30.h> // NCB
+
+//
+// Global types and constants (used in all RPL server files).
+//
+
+#include <riplcons.h> // includes __JET500 flag
+#include <jet.h>
+#include <rpllib.h> // AddKey(), MapJetError(), FillTcpIpString()
+
+#include <rpldb.h>
+
+#include "rplmgrd.h"
+
+#define RPL_BUGBUG_ERROR ((DWORD)-1) // need to get rid of these
diff --git a/private/net/svcdlls/rpl/rplstart/makefile b/private/net/svcdlls/rpl/rplstart/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/net/svcdlls/rpl/rplstart/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/net/svcdlls/rpl/rplstart/rplstart.c b/private/net/svcdlls/rpl/rplstart/rplstart.c
new file mode 100644
index 000000000..1903c25a5
--- /dev/null
+++ b/private/net/svcdlls/rpl/rplstart/rplstart.c
@@ -0,0 +1,139 @@
+/*++
+
+Copyright (c) 1987-1993 Microsoft Corporation
+
+Module Name:
+
+ rplstart.c
+
+Abstract:
+
+ Copies fresh RPL database and files.
+
+ BUGBUG Should polish and make WIN32 application out of this.
+
+Author:
+
+ Jon Newman (jonn) 12 - January - 1994
+
+Environment:
+
+ User mode
+
+Revision History :
+
+--*/
+
+#define RPLDATA_ALLOCATE
+#include "local.h"
+#undef RPLDATA_ALLOCATE
+
+#define RPL_INSTALL_FILES L"\\INSTALL"
+
+
+DWORD _CRTAPI1 main ( VOID)
+{
+
+ WCHAR awchConvertPath[ PATHLEN+1];
+ DWORD dwErr;
+
+ dwErr = I_NetRpl_QueryDirectory( awchConvertPath, NULL);
+ if ( dwErr != NO_ERROR) {
+ return( dwErr);
+ }
+
+ if ( lstrlenW(awchConvertPath) + lstrlenW(RPL_INSTALL_FILES) > PATHLEN) {
+ return( RPL_BUGBUG_ERROR);
+ }
+
+ (void) lstrcatW( awchConvertPath, RPL_INSTALL_FILES);
+
+ dwErr = I_NetRplCmd_ConvertDatabase( awchConvertPath);
+
+// BUGBUG proper error reporting
+// BUGBUG print final message
+
+ return( dwErr);
+
+#if 0
+
+ DWORD Error;
+ JET_ERR JetError;
+
+ Assert = 0;
+#ifdef __JET500
+ CallM( JetSetSystemParameter( 0, JET_paramSystemPath, 0, "."));
+#else
+ CallM( JetSetSystemParameter( 0, JET_paramSysDbPath, 0, "system.mdb"));
+#endif
+ CallM( JetSetSystemParameter( 0, JET_paramTempPath, 0, "temp.tmp"));
+ CallM( JetSetSystemParameter( 0, JET_paramMaxBuffers, 250, NULL));
+ CallM( JetSetSystemParameter( 0, JET_paramBfThrshldLowPrcnt, 0, NULL));
+ CallM( JetSetSystemParameter( 0, JET_paramBfThrshldHighPrcnt, 100, NULL));
+ CallM( JetSetSystemParameter( 0, JET_paramMaxOpenTables, 30, NULL));
+ CallM( JetSetSystemParameter( 0, JET_paramMaxOpenTableIndexes, 105, NULL))
+ CallM( JetSetSystemParameter( 0, JET_paramMaxCursors, 100, NULL));
+ CallM( JetSetSystemParameter( 0, JET_paramMaxSessions, 10, NULL));
+ CallM( JetSetSystemParameter( 0, JET_paramMaxVerPages, 64, NULL));
+ CallM( JetSetSystemParameter( 0, JET_paramMaxTemporaryTables, 5, NULL));
+ CallM( JetSetSystemParameter( 0, JET_paramLogFilePath, 0, "."));
+ CallM( JetSetSystemParameter( 0, JET_paramLogBuffers, 41, NULL));
+#ifdef __JET500
+ CallM( JetSetSystemParameter( 0, JET_paramLogFileSize, 1000, NULL));
+ CallM( JetSetSystemParameter( 0, JET_paramBaseName, "j50", NULL));
+#else
+ CallM( JetSetSystemParameter( 0, JET_paramLogFileSectors, 1000, NULL));
+#endif
+ CallM( JetSetSystemParameter( 0, JET_paramLogFlushThreshold, 10, NULL));
+
+ CallM( JetInit());
+ CallM( JetBeginSession( &SesId, "admin", ""));
+ JetError = JetCreateDatabase( SesId, RPL_SERVICE_DATABASE, NULL, &DbId, JET_bitDbSingleExclusive);
+
+ if ( JetError == JET_errDatabaseDuplicate) {
+ CallM( JetAttachDatabase( SesId, RPL_SERVICE_DATABASE, 0));
+ CallM( JetOpenDatabase( SesId, RPL_SERVICE_DATABASE, NULL, &DbId, JET_bitDbExclusive));
+ BootListTable();
+ ConfigListTable();
+ ProfileListTable();
+ WkstaListTable();
+ ListWkstaInfo( L"02608C1B87A5");
+ } else if ( JetError == JET_errSuccess) {
+ Error = ProcessRplMap();
+ if ( Error != ERROR_SUCCESS) {
+ return( Error);
+ }
+ Error = ProcessRplmgrIni();
+ if ( Error != ERROR_SUCCESS) {
+ return( Error);
+ }
+ ConfigPruneTable();
+ ProfilePruneTable();
+ WkstaPruneTable();
+ //
+ // If we were to return here, database would be left in an unusable
+ // state and any further app calling JetInit() for this database
+ // would fail.
+ //
+ BootCloseTable();
+ ConfigCloseTable();
+ ProfileCloseTable();
+ WkstaCloseTable();
+ AdapterCloseTable();
+ } else {
+ RplDump( ++Assert, ("CreateDatabase: JetError=%d", JetError));
+ CallM( JetEndSession( SesId, 0));
+ CallM( JetTerm2( BUGBUG, JET_bitTermComplete));
+ return( MapJetError( JetError));
+ }
+
+ CallM( JetCloseDatabase( SesId, DbId, 0));
+ CallM( JetDetachDatabase( SesId, RPL_SERVICE_DATABASE));
+ CallM( JetEndSession( SesId, 0));
+ CallM( JetTerm2( BUGBUG, JET_bitTermComplete));
+
+#endif
+
+ return( ERROR_SUCCESS);
+}
+
diff --git a/private/net/svcdlls/rpl/rplstart/sources b/private/net/svcdlls/rpl/rplstart/sources
new file mode 100644
index 000000000..5f4748061
--- /dev/null
+++ b/private/net/svcdlls/rpl/rplstart/sources
@@ -0,0 +1,32 @@
+MAJORCOMP=net
+MINORCOMP=rplstart
+
+TARGETPATH=obj
+TARGETNAME=rplstart
+TARGETTYPE=PROGRAM
+
+TARGETLIBS= \
+ ..\obj\*\jet.lib \
+ ..\lib\obj\*\rpllib.lib \
+ ..\convert\obj\*\rplmgrd.lib \
+ $(BASEDIR)\Public\Sdk\Lib\*\netlib.lib \
+ $(BASEDIR)\public\sdk\lib\*\netapi32.lib \
+ $(BASEDIR)\public\sdk\lib\*\wsock32.lib
+
+!IFNDEF DISABLE_NET_UNICODE
+UNICODE=1
+NET_C_DEFINES=-DUNICODE
+!ENDIF
+
+INCLUDES=.;..\inc;..\..\..\inc;..\..\..\api;..\..\..\..\inc;
+
+MSC_WARNING_LEVEL=/W3 /WX
+
+SOURCES= \
+ rplstart.c
+
+
+C_DEFINES= -DINCL_32= -DNT -DRPC_NO_WINDOWS_H -DWIN32 -DRPL_RPLCNV
+
+UMTYPE=console
+
diff --git a/private/net/svcdlls/rpl/rplsvc.idl b/private/net/svcdlls/rpl/rplsvc.idl
new file mode 100644
index 000000000..aa8b41d8b
--- /dev/null
+++ b/private/net/svcdlls/rpl/rplsvc.idl
@@ -0,0 +1,543 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ rplsvc.idl
+
+Abstract:
+
+ This is the IDL file that describes the RPC interface for the
+ remotable NetRpl APIs that reside in the messenger service.
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 27-July-1993
+
+Environment:
+
+ User Mode -Win32
+
+Revision History:
+
+ 27-July-1993 vladimv
+ Created
+
+--*/
+
+//
+// Interface Attributes
+//
+
+[
+ uuid(28607FF1-15A0-8E03-D670-B89EEC8EB047),
+ version(1.0),
+#ifdef __midl
+ ms_union,
+#endif // __midl
+ pointer_default(unique)
+]
+
+//
+// Interface Keyword
+//
+
+interface rplsvc
+
+//
+// Interface Body
+//
+
+{
+
+import "imports.idl";
+#include <lmcons.h>
+
+//
+// BUGBUG - take this definition out when midl understands LPWSTR etc
+//
+
+#ifdef UNICODE
+#define LPTSTR wchar_t*
+#endif
+
+//
+// Generic Handle used to bind from client to server.
+//
+
+typedef [handle] LPTSTR RPL_NAME;
+typedef RPL_NAME * PRPL_NAME;
+typedef PRPL_NAME LPRPL_NAME;
+
+//
+// Context Handle (internal definition)
+//
+
+typedef [context_handle] PVOID RPL_RPC_HANDLE;
+typedef RPL_RPC_HANDLE * PRPL_RPC_HANDLE;
+typedef PRPL_RPC_HANDLE LPRPL_RPC_HANDLE;
+
+
+//
+// Data Structures
+//
+
+//
+// SERVICE data - used by Get & Set
+//
+
+typedef [switch_type(DWORD)] union _RPL_INFO_STRUCT {
+ [case(0)] LPRPL_INFO_0 RplInfo0;
+ [case(1)] LPRPL_INFO_1 RplInfo1;
+} RPL_INFO_STRUCT, *PRPL_INFO_STRUCT, *LPRPL_INFO_STRUCT;
+
+
+//
+// BOOT data - used by Enum
+//
+
+typedef struct _RPL_BOOT_INFO_0_CONTAINER {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] LPRPL_BOOT_INFO_0 Buffer;
+} RPL_BOOT_INFO_0_CONTAINER, *PRPL_BOOT_INFO_0_CONTAINER, *LPRPL_BOOT_INFO_0_CONTAINER;
+
+typedef struct _RPL_BOOT_INFO_1_CONTAINER {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] LPRPL_BOOT_INFO_1 Buffer;
+} RPL_BOOT_INFO_1_CONTAINER, *PRPL_BOOT_INFO_1_CONTAINER, *LPRPL_BOOT_INFO_1_CONTAINER;
+
+typedef struct _RPL_BOOT_INFO_2_CONTAINER {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] LPRPL_BOOT_INFO_2 Buffer;
+} RPL_BOOT_INFO_2_CONTAINER, *PRPL_BOOT_INFO_2_CONTAINER, *LPRPL_BOOT_INFO_2_CONTAINER;
+
+typedef struct _RPL_BOOT_ENUM {
+ DWORD Level;
+ [switch_is(Level)] union _RPL_BOOT_ENUM_UNION {
+ [case(0)] LPRPL_BOOT_INFO_0_CONTAINER Level0;
+ [case(1)] LPRPL_BOOT_INFO_1_CONTAINER Level1;
+ [case(2)] LPRPL_BOOT_INFO_2_CONTAINER Level2;
+ } BootInfo;
+} RPL_BOOT_ENUM, *PRPL_BOOT_ENUM, *LPRPL_BOOT_ENUM;
+
+typedef [switch_type(DWORD)] union _RPL_BOOT_INFO_STRUCT {
+ [case(0)] LPRPL_BOOT_INFO_0 BootInfo0;
+ [case(1)] LPRPL_BOOT_INFO_1 BootInfo1;
+ [case(2)] LPRPL_BOOT_INFO_2 BootInfo2;
+} RPL_BOOT_INFO_STRUCT, *PRPL_BOOT_INFO_STRUCT, *LPRPL_BOOT_INFO_STRUCT;
+
+
+
+//
+// CONFIG data - used by Enum
+//
+
+typedef struct _RPL_CONFIG_INFO_0_CONTAINER {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] LPRPL_CONFIG_INFO_0 Buffer;
+} RPL_CONFIG_INFO_0_CONTAINER, *PRPL_CONFIG_INFO_0_CONTAINER, *LPRPL_CONFIG_INFO_0_CONTAINER;
+
+typedef struct _RPL_CONFIG_INFO_1_CONTAINER {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] LPRPL_CONFIG_INFO_1 Buffer;
+} RPL_CONFIG_INFO_1_CONTAINER, *PRPL_CONFIG_INFO_1_CONTAINER, *LPRPL_CONFIG_INFO_1_CONTAINER;
+
+typedef struct _RPL_CONFIG_INFO_2_CONTAINER {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] LPRPL_CONFIG_INFO_2 Buffer;
+} RPL_CONFIG_INFO_2_CONTAINER, *PRPL_CONFIG_INFO_2_CONTAINER, *LPRPL_CONFIG_INFO_2_CONTAINER;
+
+typedef struct _RPL_CONFIG_ENUM {
+ DWORD Level;
+ [switch_is(Level)] union _RPL_CONFIG_ENUM_UNION {
+ [case(0)] LPRPL_CONFIG_INFO_0_CONTAINER Level0;
+ [case(1)] LPRPL_CONFIG_INFO_1_CONTAINER Level1;
+ [case(2)] LPRPL_CONFIG_INFO_2_CONTAINER Level2;
+ } ConfigInfo;
+} RPL_CONFIG_ENUM, *PRPL_CONFIG_ENUM, *LPRPL_CONFIG_ENUM;
+
+typedef [switch_type(DWORD)] union _RPL_CONFIG_INFO_STRUCT {
+ [case(0)] LPRPL_CONFIG_INFO_0 ConfigInfo0;
+ [case(1)] LPRPL_CONFIG_INFO_1 ConfigInfo1;
+ [case(2)] LPRPL_CONFIG_INFO_2 ConfigInfo2;
+} RPL_CONFIG_INFO_STRUCT, *PRPL_CONFIG_INFO_STRUCT, *LPRPL_CONFIG_INFO_STRUCT;
+
+
+
+//
+// PROFILE data - used by Enum, Get, Set & Add
+//
+
+typedef struct _RPL_PROFILE_INFO_0_CONTAINER {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] LPRPL_PROFILE_INFO_0 Buffer;
+} RPL_PROFILE_INFO_0_CONTAINER, *PRPL_PROFILE_INFO_0_CONTAINER, *LPRPL_PROFILE_INFO_0_CONTAINER;
+
+typedef struct _RPL_PROFILE_INFO_1_CONTAINER {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] LPRPL_PROFILE_INFO_1 Buffer;
+} RPL_PROFILE_INFO_1_CONTAINER, *PRPL_PROFILE_INFO_1_CONTAINER, *LPRPL_PROFILE_INFO_1_CONTAINER;
+
+typedef struct _RPL_PROFILE_INFO_2_CONTAINER {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] LPRPL_PROFILE_INFO_2 Buffer;
+} RPL_PROFILE_INFO_2_CONTAINER, *PRPL_PROFILE_INFO_2_CONTAINER, *LPRPL_PROFILE_INFO_2_CONTAINER;
+
+
+typedef struct _RPL_PROFILE_ENUM {
+ DWORD Level;
+ [switch_is(Level)] union _RPL_PROFILE_ENUM_UNION {
+ [case(0)] LPRPL_PROFILE_INFO_0_CONTAINER Level0;
+ [case(1)] LPRPL_PROFILE_INFO_1_CONTAINER Level1;
+ [case(2)] LPRPL_PROFILE_INFO_2_CONTAINER Level2;
+ } ProfileInfo;
+} RPL_PROFILE_ENUM, *PRPL_PROFILE_ENUM, *LPRPL_PROFILE_ENUM;
+
+
+typedef [switch_type(DWORD)] union _RPL_PROFILE_INFO_STRUCT {
+ [case(0)] LPRPL_PROFILE_INFO_0 ProfileInfo0;
+ [case(1)] LPRPL_PROFILE_INFO_1 ProfileInfo1;
+ [case(2)] LPRPL_PROFILE_INFO_2 ProfileInfo2;
+} RPL_PROFILE_INFO_STRUCT, *PRPL_PROFILE_INFO_STRUCT, *LPRPL_PROFILE_INFO_STRUCT, PRPL_PROFILE_INFO, LPRPL_PROFILE_INFO;
+
+
+//
+// VENDOR data - used by Enum
+//
+
+typedef struct _RPL_VENDOR_INFO_0_CONTAINER {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] LPRPL_VENDOR_INFO_0 Buffer;
+} RPL_VENDOR_INFO_0_CONTAINER, *PRPL_VENDOR_INFO_0_CONTAINER, *LPRPL_VENDOR_INFO_0_CONTAINER;
+
+typedef struct _RPL_VENDOR_INFO_1_CONTAINER {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] LPRPL_VENDOR_INFO_1 Buffer;
+} RPL_VENDOR_INFO_1_CONTAINER, *PRPL_VENDOR_INFO_1_CONTAINER, *LPRPL_VENDOR_INFO_1_CONTAINER;
+
+typedef struct _RPL_VENDOR_ENUM {
+ DWORD Level;
+ [switch_is(Level)] union _RPL_VENDOR_ENUM_UNION {
+ [case(0)] LPRPL_VENDOR_INFO_0_CONTAINER Level0;
+ [case(1)] LPRPL_VENDOR_INFO_1_CONTAINER Level1;
+ } VendorInfo;
+} RPL_VENDOR_ENUM, *PRPL_VENDOR_ENUM, *LPRPL_VENDOR_ENUM;
+
+typedef [switch_type(DWORD)] union _RPL_VENDOR_INFO_STRUCT {
+ [case(0)] LPRPL_VENDOR_INFO_0 VendorInfo0;
+ [case(1)] LPRPL_VENDOR_INFO_1 VendorInfo1;
+} RPL_VENDOR_INFO_STRUCT, *PRPL_VENDOR_INFO_STRUCT, *LPRPL_VENDOR_INFO_STRUCT;
+
+
+//
+// ADAPTER data - used by Enum
+//
+
+typedef struct _RPL_ADAPTER_INFO_0_CONTAINER {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] LPRPL_ADAPTER_INFO_0 Buffer;
+} RPL_ADAPTER_INFO_0_CONTAINER, *PRPL_ADAPTER_INFO_0_CONTAINER, *LPRPL_ADAPTER_INFO_0_CONTAINER;
+
+typedef struct _RPL_ADAPTER_INFO_1_CONTAINER {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] LPRPL_ADAPTER_INFO_1 Buffer;
+} RPL_ADAPTER_INFO_1_CONTAINER, *PRPL_ADAPTER_INFO_1_CONTAINER, *LPRPL_ADAPTER_INFO_1_CONTAINER;
+
+typedef struct _RPL_ADAPTER_ENUM {
+ DWORD Level;
+ [switch_is(Level)] union _RPL_ADAPTER_ENUM_UNION {
+ [case(0)] LPRPL_ADAPTER_INFO_0_CONTAINER Level0;
+ [case(1)] LPRPL_ADAPTER_INFO_1_CONTAINER Level1;
+ } AdapterInfo;
+} RPL_ADAPTER_ENUM, *PRPL_ADAPTER_ENUM, *LPRPL_ADAPTER_ENUM;
+
+typedef [switch_type(DWORD)] union _RPL_ADAPTER_INFO_STRUCT {
+ [case(0)] LPRPL_ADAPTER_INFO_0 AdapterInfo0;
+ [case(1)] LPRPL_ADAPTER_INFO_1 AdapterInfo1;
+} RPL_ADAPTER_INFO_STRUCT, *PRPL_ADAPTER_INFO_STRUCT, *LPRPL_ADAPTER_INFO_STRUCT;
+
+
+//
+// WKSTA data - used by Enum, Get, Set & Add
+//
+
+typedef struct _RPL_WKSTA_INFO_0_CONTAINER {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] LPRPL_WKSTA_INFO_0 Buffer;
+} RPL_WKSTA_INFO_0_CONTAINER, *PRPL_WKSTA_INFO_0_CONTAINER, *LPRPL_WKSTA_INFO_0_CONTAINER;
+
+typedef struct _RPL_WKSTA_INFO_1_CONTAINER {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] LPRPL_WKSTA_INFO_1 Buffer;
+} RPL_WKSTA_INFO_1_CONTAINER, *PRPL_WKSTA_INFO_1_CONTAINER, *LPRPL_WKSTA_INFO_1_CONTAINER;
+
+typedef struct _RPL_WKSTA_INFO_2_CONTAINER {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] LPRPL_WKSTA_INFO_2 Buffer;
+} RPL_WKSTA_INFO_2_CONTAINER, *PRPL_WKSTA_INFO_2_CONTAINER, *LPRPL_WKSTA_INFO_2_CONTAINER;
+
+
+typedef struct _RPL_WKSTA_ENUM {
+ DWORD Level;
+ [switch_is(Level)] union _RPL_WKSTA_ENUM_UNION {
+ [case(0)] LPRPL_WKSTA_INFO_0_CONTAINER Level0;
+ [case(1)] LPRPL_WKSTA_INFO_1_CONTAINER Level1;
+ [case(2)] LPRPL_WKSTA_INFO_2_CONTAINER Level2;
+ } WkstaInfo;
+} RPL_WKSTA_ENUM, *PRPL_WKSTA_ENUM, *LPRPL_WKSTA_ENUM;
+
+
+typedef [switch_type(DWORD)] union _RPL_WKSTA_INFO_STRUCT {
+ [case(0)] LPRPL_WKSTA_INFO_0 WkstaInfo0;
+ [case(1)] LPRPL_WKSTA_INFO_1 WkstaInfo1;
+ [case(2)] LPRPL_WKSTA_INFO_2 WkstaInfo2;
+} RPL_WKSTA_INFO_STRUCT, *PRPL_WKSTA_INFO_STRUCT, *LPRPL_WKSTA_INFO_STRUCT;
+
+//
+// Function Prototypes
+//
+
+//
+// Service apis
+//
+
+NET_API_STATUS NET_API_FUNCTION
+NetrRplOpen(
+ [in,string,unique] RPL_NAME ServerName,
+ [out] LPRPL_RPC_HANDLE ServerHandle
+ );
+NET_API_STATUS NET_API_FUNCTION
+NetrRplClose(
+ [in,out] LPRPL_RPC_HANDLE ServerHandle
+ );
+NET_API_STATUS NET_API_FUNCTION
+NetrRplGetInfo(
+ [in] RPL_RPC_HANDLE ServerHandle,
+ [in] DWORD Level,
+ [out, switch_is(Level)] LPRPL_INFO_STRUCT InfoStruct
+ );
+NET_API_STATUS NET_API_FUNCTION
+NetrRplSetInfo(
+ [in] RPL_RPC_HANDLE ServerHandle,
+ [in] DWORD Level,
+ [in, switch_is(Level)] LPRPL_INFO_STRUCT InfoStruct,
+ [in,out,unique] LPDWORD ErrorParameter
+ );
+
+//
+// ADAPTER apis
+//
+
+NET_API_STATUS NET_API_FUNCTION
+NetrRplAdapterAdd(
+ [in] RPL_RPC_HANDLE ServerHandle,
+ [in] DWORD Level,
+ [in, switch_is(Level)] LPRPL_ADAPTER_INFO_STRUCT AdapterInfoStruct,
+ [in,out,unique] LPDWORD ErrorParameter
+ );
+NET_API_STATUS NET_API_FUNCTION
+NetrRplAdapterDel(
+ [in] RPL_RPC_HANDLE ServerHandle,
+ [in,string,unique] LPTSTR AdapterName
+ );
+NET_API_STATUS NET_API_FUNCTION
+NetrRplAdapterEnum(
+ [in] RPL_RPC_HANDLE ServerHandle,
+ [in,out] LPRPL_ADAPTER_ENUM AdapterEnum,
+ [in] DWORD PrefMaxLength,
+ [out] LPDWORD TotalEntries,
+ [in,out,unique] LPDWORD ResumeHandle
+ );
+
+
+//
+// BOOT apis
+//
+
+NET_API_STATUS NET_API_FUNCTION
+NetrRplBootAdd(
+ [in] RPL_RPC_HANDLE ServerHandle,
+ [in] DWORD Level,
+ [in, switch_is(Level)] LPRPL_BOOT_INFO_STRUCT BootInfoStruct,
+ [in,out,unique] LPDWORD ErrorParameter
+ );
+NET_API_STATUS NET_API_FUNCTION
+NetrRplBootDel(
+ [in] RPL_RPC_HANDLE ServerHandle,
+ [in,string] LPTSTR BootName,
+ [in,string] LPTSTR VendorName
+ );
+NET_API_STATUS NET_API_FUNCTION
+NetrRplBootEnum(
+ [in] RPL_RPC_HANDLE ServerHandle,
+ [in,out] LPRPL_BOOT_ENUM BootEnum,
+ [in] DWORD PrefMaxLength,
+ [out] LPDWORD TotalEntries,
+ [in,out,unique] LPDWORD ResumeHandle
+ );
+
+//
+// CONFIG apis
+//
+
+NET_API_STATUS NET_API_FUNCTION
+NetrRplConfigAdd(
+ [in] RPL_RPC_HANDLE ServerHandle,
+ [in] DWORD Level,
+ [in, switch_is(Level)] LPRPL_CONFIG_INFO_STRUCT ConfigInfoStruct,
+ [in,out,unique] LPDWORD ErrorParameter
+ );
+NET_API_STATUS NET_API_FUNCTION
+NetrRplConfigDel(
+ [in] RPL_RPC_HANDLE ServerHandle,
+ [in,string] LPTSTR ConfigName
+ );
+NET_API_STATUS NET_API_FUNCTION
+NetrRplConfigEnum(
+ [in] RPL_RPC_HANDLE ServerHandle,
+ [in,string,unique] LPTSTR AdapterName,
+ [in,out] LPRPL_CONFIG_ENUM ConfigEnum,
+ [in] DWORD PrefMaxLength,
+ [out] LPDWORD TotalEntries,
+ [in,out,unique] LPDWORD ResumeHandle
+ );
+
+//
+// PROFILE apis
+//
+
+NET_API_STATUS NET_API_FUNCTION
+NetrRplProfileAdd(
+ [in] RPL_RPC_HANDLE ServerHandle,
+ [in] DWORD Level,
+ [in, switch_is(Level)] LPRPL_PROFILE_INFO_STRUCT ProfileInfoStruct,
+ [in,out,unique] LPDWORD ErrorParameter
+ );
+NET_API_STATUS NET_API_FUNCTION
+NetrRplProfileClone(
+ [in] RPL_RPC_HANDLE ServerHandle,
+ [in,string] LPTSTR SourceProfileName,
+ [in,string] LPTSTR TargetProfileName,
+ [in,string,unique] LPTSTR TargetProfileComment
+ );
+NET_API_STATUS NET_API_FUNCTION
+NetrRplProfileDel(
+ [in] RPL_RPC_HANDLE ServerHandle,
+ [in,string] LPTSTR ProfileName
+ );
+NET_API_STATUS NET_API_FUNCTION
+NetrRplProfileEnum(
+ [in] RPL_RPC_HANDLE ServerHandle,
+ [in,string,unique] LPTSTR AdapterName,
+ [in,out] LPRPL_PROFILE_ENUM ProfileEnum,
+ [in] DWORD PrefMaxLength,
+ [out] LPDWORD TotalEntries,
+ [in,out,unique] LPDWORD ResumeHandle
+ );
+NET_API_STATUS NET_API_FUNCTION
+NetrRplProfileGetInfo(
+ [in] RPL_RPC_HANDLE ServerHandle,
+ [in,string] LPTSTR ProfileName,
+ [in] DWORD Level,
+ [out, switch_is(Level)] LPRPL_PROFILE_INFO_STRUCT ProfileInfoStruct
+ );
+NET_API_STATUS NET_API_FUNCTION
+NetrRplProfileSetInfo(
+ [in] RPL_RPC_HANDLE ServerHandle,
+ [in,string] LPTSTR ProfileName,
+ [in] DWORD Level,
+ [in, switch_is(Level)] LPRPL_PROFILE_INFO_STRUCT ProfileInfoStruct,
+ [in,out,unique] LPDWORD ErrorParameter
+ );
+
+//
+// VENDOR apis
+//
+
+NET_API_STATUS NET_API_FUNCTION
+NetrRplVendorAdd(
+ [in] RPL_RPC_HANDLE ServerHandle,
+ [in] DWORD Level,
+ [in, switch_is(Level)] LPRPL_VENDOR_INFO_STRUCT VendorInfoStruct,
+ [in,out,unique] LPDWORD ErrorParameter
+ );
+NET_API_STATUS NET_API_FUNCTION
+NetrRplVendorDel(
+ [in] RPL_RPC_HANDLE ServerHandle,
+ [in,string] LPTSTR VendorName
+ );
+NET_API_STATUS NET_API_FUNCTION
+NetrRplVendorEnum(
+ [in] RPL_RPC_HANDLE ServerHandle,
+ [in,out] LPRPL_VENDOR_ENUM VendorEnum,
+ [in] DWORD PrefMaxLength,
+ [out] LPDWORD TotalEntries,
+ [in,out,unique] LPDWORD ResumeHandle
+ );
+
+//
+// WKSTA apis
+//
+
+NET_API_STATUS NET_API_FUNCTION
+NetrRplWkstaAdd(
+ [in] RPL_RPC_HANDLE ServerHandle,
+ [in] DWORD Level,
+ [in, switch_is(Level)] LPRPL_WKSTA_INFO_STRUCT WkstaInfo,
+ [in,out,unique] LPDWORD ErrorParameter
+ );
+NET_API_STATUS NET_API_FUNCTION
+NetrRplWkstaClone(
+ [in] RPL_RPC_HANDLE ServerHandle,
+ [in,string] LPTSTR SourceWkstaName,
+ [in,string] LPTSTR TargetWkstaName,
+ [in,string,unique] LPTSTR TargetWkstaComment,
+ [in,string] LPTSTR TargetAdapterName,
+ [in] DWORD TargetWkstaIpAddress
+ );
+NET_API_STATUS NET_API_FUNCTION
+NetrRplWkstaDel(
+ [in] RPL_RPC_HANDLE ServerHandle,
+ [in,string] LPTSTR WkstaName
+ );
+NET_API_STATUS NET_API_FUNCTION
+NetrRplWkstaEnum(
+ [in] RPL_RPC_HANDLE ServerHandle,
+ [in,string,unique] LPTSTR ProfileName,
+ [in,out] LPRPL_WKSTA_ENUM WkstaEnum,
+ [in] DWORD PrefMaxLength,
+ [out] LPDWORD TotalEntries,
+ [in,out,unique] LPDWORD ResumeHandle
+ );
+NET_API_STATUS NET_API_FUNCTION
+NetrRplWkstaGetInfo(
+ [in] RPL_RPC_HANDLE ServerHandle,
+ [in,string] LPTSTR WkstaName,
+ [in] DWORD Level,
+ [out, switch_is(Level)] LPRPL_WKSTA_INFO_STRUCT WkstaInfoStruct
+ );
+NET_API_STATUS NET_API_FUNCTION
+NetrRplWkstaSetInfo(
+ [in] RPL_RPC_HANDLE ServerHandle,
+ [in,string] LPTSTR WkstaName,
+ [in] DWORD Level,
+ [in, switch_is(Level)] LPRPL_WKSTA_INFO_STRUCT WkstaInfoStruct,
+ [in,out,unique] LPDWORD ErrorParameter
+ );
+
+//
+// SECURITY api
+//
+NET_API_STATUS NET_API_FUNCTION
+NetrRplSetSecurity(
+ [in] RPL_RPC_HANDLE ServerHandle,
+ [in,string,unique] LPTSTR WkstaName,
+ [in] DWORD WkstaRid,
+ [in] DWORD RplUserRid
+ );
+
+
+}
+
+ \ No newline at end of file
diff --git a/private/net/svcdlls/rpl/rplsvc_c.acf b/private/net/svcdlls/rpl/rplsvc_c.acf
new file mode 100644
index 000000000..a1e493fb0
--- /dev/null
+++ b/private/net/svcdlls/rpl/rplsvc_c.acf
@@ -0,0 +1,33 @@
+[implicit_handle (handle_t rplsvc_handle)]
+
+interface rplsvc
+
+{
+
+typedef [allocate(all_nodes)] LPRPL_INFO_0;
+typedef [allocate(all_nodes)] LPRPL_INFO_1;
+
+typedef [allocate(all_nodes)] LPRPL_BOOT_INFO_0;
+typedef [allocate(all_nodes)] LPRPL_BOOT_INFO_1;
+typedef [allocate(all_nodes)] LPRPL_BOOT_INFO_2;
+
+typedef [allocate(all_nodes)] LPRPL_CONFIG_INFO_0;
+typedef [allocate(all_nodes)] LPRPL_CONFIG_INFO_1;
+typedef [allocate(all_nodes)] LPRPL_CONFIG_INFO_2;
+
+typedef [allocate(all_nodes)] LPRPL_PROFILE_INFO_0;
+typedef [allocate(all_nodes)] LPRPL_PROFILE_INFO_1;
+typedef [allocate(all_nodes)] LPRPL_PROFILE_INFO_2;
+
+typedef [allocate(all_nodes)] LPRPL_VENDOR_INFO_0;
+typedef [allocate(all_nodes)] LPRPL_VENDOR_INFO_1;
+
+typedef [allocate(all_nodes)] LPRPL_ADAPTER_INFO_0;
+typedef [allocate(all_nodes)] LPRPL_ADAPTER_INFO_1;
+
+typedef [allocate(all_nodes)] LPRPL_WKSTA_INFO_0;
+typedef [allocate(all_nodes)] LPRPL_WKSTA_INFO_1;
+typedef [allocate(all_nodes)] LPRPL_WKSTA_INFO_2;
+
+}
+ \ No newline at end of file
diff --git a/private/net/svcdlls/rpl/rplsvc_s.acf b/private/net/svcdlls/rpl/rplsvc_s.acf
new file mode 100644
index 000000000..157ff2912
--- /dev/null
+++ b/private/net/svcdlls/rpl/rplsvc_s.acf
@@ -0,0 +1,7 @@
+[implicit_handle (handle_t rplsvc_handle)]
+
+interface rplsvc
+
+{
+}
+ \ No newline at end of file
diff --git a/private/net/svcdlls/rpl/server/adapter.c b/private/net/svcdlls/rpl/server/adapter.c
new file mode 100644
index 000000000..95756c78c
--- /dev/null
+++ b/private/net/svcdlls/rpl/server/adapter.c
@@ -0,0 +1,502 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ adapter.c
+
+Abstract:
+
+ Adapter APIs.
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 19 - November - 1993
+
+Revision History:
+
+--*/
+
+#include "local.h"
+#include "rpldb.h"
+#include "db.h"
+#include "dblib.h"
+#define RPLADAPTER_ALLOCATE
+#include "adapter.h"
+#undef RPLADAPTER_ALLOCATE
+#include "database.h"
+
+
+
+DWORD AdapterGetField(
+ IN PRPL_SESSION pSession,
+ IN DWORD FieldIndex,
+ OUT LPVOID * pData,
+ IN OUT LPINT pSpaceLeft
+ )
+{
+ BYTE LocalBuffer[ 300];
+ PBYTE Buffer;
+ DWORD DataSize;
+ DWORD BufferSize;
+ JET_ERR JetError;
+
+ switch( FieldIndex) {
+ case ADAPTER_Flags:
+ Buffer = (PBYTE)pData;
+ BufferSize = sizeof( DWORD);
+ break;
+ default:
+ Buffer = LocalBuffer;
+ BufferSize = sizeof( LocalBuffer);
+ break;
+ }
+ JetError = JetRetrieveColumn( pSession->SesId, pSession->AdapterTableId,
+ AdapterTable[ FieldIndex].ColumnId, LocalBuffer,
+ sizeof( LocalBuffer), &DataSize, 0, NULL);
+ if ( JetError < 0) {
+ RplDump( ++RG_Assert, ("JetError=%d", JetError));
+ return( NERR_RplAdapterInfoCorrupted);
+ }
+ if ( Buffer != LocalBuffer) {
+ if ( BufferSize == DataSize) {
+ return( NO_ERROR);
+ } else {
+ RplDump( ++RG_Assert, ("Bad DataSize=0x%x", DataSize));
+ return( NERR_RplAdapterInfoCorrupted);
+ }
+ }
+ //
+ // We have done with fixed data. From here on we deal with unicode
+ // strings only.
+ //
+ if ( DataSize > sizeof( LocalBuffer)) {
+ RplDump( ++RG_Assert, ( "Too big DataSize=0x%x", DataSize));
+ return( NERR_RplAdapterInfoCorrupted);
+ }
+ if ( DataSize == 0) {
+ if ( JetError != JET_wrnColumnNull) {
+ RplDump( ++RG_Assert, ( "JetError=%d", JetError));
+ return( NERR_RplAdapterInfoCorrupted);
+ } else {
+ *pData = NULL; // so RPC rpcrt4!_tree_size_ndr() does not bomb here
+ return( NO_ERROR);
+ }
+ }
+ if ( DataSize & 1 != 0 || wcslen((PWCHAR)LocalBuffer) + 1 != DataSize/2) {
+ RplDump( ++RG_Assert, ("LocalBuffer=0x%x, DataSize=0x%x", LocalBuffer, DataSize));
+ return( NERR_RplAdapterInfoCorrupted);
+ }
+ *pData = MIDL_user_allocate( DataSize);
+ if ( *pData == NULL) {
+ RplDump( ++RG_Assert, ( "Error=%d", GetLastError()));
+ return( ERROR_NOT_ENOUGH_MEMORY);
+ }
+ memcpy( *pData, LocalBuffer, DataSize);
+ *pSpaceLeft -= DataSize;
+ return( NO_ERROR);
+}
+
+
+DWORD AdapterGetInfo(
+ IN PRPL_SESSION pSession,
+ IN LPWSTR AdapterName,
+ IN DWORD Level,
+ OUT LPVOID Buffer,
+ OUT PINT pSpaceLeft
+ )
+{
+ DWORD Error;
+ LPRPL_ADAPTER_INFO_1 Info = Buffer;
+
+ switch( Level) {
+ case 1:
+ Error = AdapterGetField( pSession, ADAPTER_Flags, (LPVOID *)&Info->Flags, pSpaceLeft);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ NOTHING; // fall through
+ case 0:
+ Error = AdapterGetField( pSession, ADAPTER_AdapterComment, &Info->AdapterComment, pSpaceLeft);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ if ( AdapterName == NULL) {
+ Error = AdapterGetField( pSession, ADAPTER_AdapterName, &Info->AdapterName, pSpaceLeft);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ } else {
+ DWORD DataSize = (wcslen( AdapterName) + 1) * sizeof(WCHAR);
+ Info->AdapterName = MIDL_user_allocate( DataSize);
+ if ( Info->AdapterName == NULL) {
+ return( ERROR_NOT_ENOUGH_MEMORY);
+ }
+ RplDump( RG_DebugLevel & RPL_DEBUG_ADAPTER, ( "AdapterName=0x%x", Info->AdapterName));
+ memcpy( Info->AdapterName, AdapterName, DataSize);
+ *pSpaceLeft -= DataSize;
+ }
+ break;
+ default:
+ return( ERROR_INVALID_LEVEL);
+ break;
+ }
+ return( NO_ERROR);
+}
+
+
+
+VOID AdapterGetInfoCleanup(
+ IN DWORD Level,
+ IN OUT LPVOID Buffer
+ )
+{
+ switch( Level) {
+ case 0: {
+ LPRPL_ADAPTER_INFO_0 Info = Buffer;
+ if ( Info->AdapterName != NULL) {
+ MIDL_user_free( Info->AdapterName);
+ }
+ if ( Info->AdapterComment != NULL) {
+ MIDL_user_free( Info->AdapterComment);
+ }
+ break;
+ }
+ }
+}
+
+
+DWORD AdapterSetInfo(
+ IN PRPL_SESSION pSession,
+ IN DWORD Level,
+ OUT LPVOID Buffer,
+ OUT LPDWORD pErrorParameter
+ )
+{
+ LPRPL_ADAPTER_INFO_1 Info = Buffer;
+ switch( Level) {
+ case 1:
+ //
+ // Must initialize Flags - or will trap in GetField.
+ //
+ {
+ *pErrorParameter = ADAPTER_Flags;
+ CallM( JetSetColumn( pSession->SesId, pSession->AdapterTableId,
+ AdapterTable[ ADAPTER_Flags].ColumnId,
+ &Info->Flags, sizeof( Info->Flags), 0, NULL));
+ }
+ NOTHING; // fall through
+ case 0:
+ if ( Info->AdapterComment != NULL) {
+ *pErrorParameter = ADAPTER_AdapterComment;
+ CallM( JetSetColumn( pSession->SesId, pSession->AdapterTableId,
+ AdapterTable[ ADAPTER_AdapterComment].ColumnId,
+ Info->AdapterComment,
+ ( wcslen( Info->AdapterComment) + 1) * sizeof(WCHAR),
+ 0, NULL));
+ }
+ if ( Info->AdapterName != NULL) {
+ *pErrorParameter = ADAPTER_AdapterName;
+ CallM( JetSetColumn( pSession->SesId, pSession->AdapterTableId,
+ AdapterTable[ ADAPTER_AdapterName].ColumnId,
+ Info->AdapterName,
+ ( wcslen( Info->AdapterName) + 1) * sizeof(WCHAR),
+ 0, NULL));
+ }
+ break;
+ }
+ return( NO_ERROR);
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetrRplAdapterAdd(
+ IN RPL_HANDLE ServerHandle,
+ IN DWORD Level,
+ IN LPRPL_ADAPTER_INFO_STRUCT AdapterInfoStruct,
+ OUT LPDWORD pErrorParameter OPTIONAL
+ )
+{
+ LPRPL_ADAPTER_INFO_1 Info;
+ LPVOID Buffer;
+ DWORD Error;
+ DWORD ErrorParameter;
+ PRPL_SESSION pSession = &RG_ApiSession;
+
+ ErrorParameter = INVALID_ERROR_PARAMETER;
+
+ Buffer = Info = AdapterInfoStruct->AdapterInfo1;
+ switch( Level) {
+ case 1:
+ if ( Info->Flags != 0) {
+ ErrorParameter = ADAPTER_Flags;
+ break;
+ }
+ if ( RPL_STRING_TOO_LONG( Info->AdapterComment)) {
+ ErrorParameter = ADAPTER_AdapterComment;
+ break;
+ }
+ if ( !ValidHexName( Info->AdapterName, RPL_ADAPTER_NAME_LENGTH, TRUE)) {
+ ErrorParameter = ADAPTER_AdapterName;
+ break;
+ }
+ _wcsupr( Info->AdapterName);
+ break;
+ default:
+ return( ERROR_INVALID_LEVEL);
+ break;
+ }
+
+ if ( ErrorParameter != INVALID_ERROR_PARAMETER) {
+ if ( ARGUMENT_PRESENT( pErrorParameter)) {
+ *pErrorParameter = ErrorParameter;
+ }
+ return( ERROR_INVALID_PARAMETER);
+ }
+
+ EnterCriticalSection( &RG_ProtectDatabase);
+ Call( JetBeginTransaction( pSession->SesId));
+
+ //
+ // Verify that AdapterName is available in the database.
+ //
+ if ( RplFind( pSession, ADAPTER_TABLE_TAG, Info->AdapterName)) {
+ Error = NERR_RplAdapterNameUnavailable;
+ goto cleanup;
+ }
+
+ CallJ( JetPrepareUpdate( pSession->SesId, pSession->AdapterTableId, JET_prepInsert));
+
+ Error = AdapterSetInfo( pSession, Level, Buffer, &ErrorParameter);
+ if ( Error == ERROR_SUCCESS) {
+ ErrorParameter = 0;
+ CallJ( JetUpdate( pSession->SesId, pSession->AdapterTableId, NULL, 0, NULL));
+ }
+
+cleanup:
+ if ( Error == NO_ERROR) {
+ Call( JetCommitTransaction( pSession->SesId, JET_bitCommitFlush));
+ } else {
+ Call( JetRollback( pSession->SesId, JET_bitRollbackAll));
+ }
+ LeaveCriticalSection( &RG_ProtectDatabase);
+
+ if ( Error != ERROR_SUCCESS) {
+ if ( ARGUMENT_PRESENT( pErrorParameter)) {
+ *pErrorParameter = ErrorParameter;
+ }
+ }
+ return( Error);
+}
+
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetrRplAdapterDel(
+ IN RPL_HANDLE ServerHandle,
+ IN LPWSTR AdapterName
+ )
+/*++
+ If AdapterName is provided then delete matching record, else delete all
+ adapter records.
+--*/
+{
+ JET_ERR JetError;
+ DWORD Error = NO_ERROR;
+ PRPL_SESSION pSession = &RG_ApiSession;
+
+ if ( !ValidHexName( AdapterName, RPL_ADAPTER_NAME_LENGTH, FALSE)) {
+ return( ERROR_INVALID_PARAMETER);
+ }
+ if ( AdapterName != NULL) {
+ _wcsupr( AdapterName);
+ }
+
+ EnterCriticalSection( &RG_ProtectDatabase);
+ Call( JetBeginTransaction( pSession->SesId));
+
+ if ( AdapterName != NULL) {
+ if ( !RplFind( pSession, ADAPTER_TABLE_TAG, AdapterName)) {
+ Error = NERR_RplAdapterNotFound;
+ goto cleanup;
+ }
+ CallJ( JetDelete( pSession->SesId, pSession->AdapterTableId));
+ } else {
+ CallJ( JetSetCurrentIndex( pSession->SesId, pSession->AdapterTableId, ADAPTER_INDEX_AdapterName));
+ //
+ // The call to move to the beginning of the table is not redundant.
+ // E.g. JET_errNoCurrentRecord will be returned in case of empty table.
+ //
+ JetError = JetMove( pSession->SesId, pSession->AdapterTableId, JET_MoveFirst, 0);
+ if ( JetError < 0) {
+ if ( JetError != JET_errRecordNotFound
+ && JetError != JET_errNoCurrentRecord) {
+ RplDump( ++RG_Assert, ("JetError=%d", JetError));
+ Error = NERR_RplInternal;
+ }
+ goto cleanup;
+ }
+ do {
+ CallJ( JetDelete( pSession->SesId, pSession->AdapterTableId));
+ JetError = JetMove( pSession->SesId, pSession->AdapterTableId, JET_MoveNext, 0);
+ } while ( JetError == JET_errSuccess);
+ }
+
+cleanup:
+ if ( Error == NO_ERROR) {
+ Call( JetCommitTransaction( pSession->SesId, JET_bitCommitFlush));
+ } else {
+ Call( JetRollback( pSession->SesId, JET_bitRollbackAll));
+ }
+ LeaveCriticalSection( &RG_ProtectDatabase);
+ return( Error);
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetrRplAdapterEnum(
+ IN RPL_HANDLE ServerHandle,
+ IN OUT LPRPL_ADAPTER_ENUM AdapterEnum,
+ IN DWORD PrefMaxLength,
+ OUT LPDWORD TotalEntries,
+ OUT LPDWORD pResumeHandle OPTIONAL
+ )
+/*++
+ For more extensive comments see related code in NetrAdapterEnum.
+--*/
+{
+ LPBYTE Buffer;
+ DWORD TypicalSize;
+ DWORD CoreSize;
+ DWORD Error;
+ INT SpaceLeft;
+ DWORD ArrayLength;
+ DWORD EntriesRead;
+ JET_ERR JetError;
+ BOOL InfoError;
+ BOOL TableEnd;
+ PRPL_SESSION pSession = &RG_ApiSession;
+
+ //
+ // For now we here we disallow all levels but 0 level. All other code
+ // would work for levels 0 and 1.
+ //
+ switch( AdapterEnum->Level) {
+ case 1:
+ TypicalSize = CoreSize = sizeof( RPL_ADAPTER_INFO_1);
+ NOTHING; // fall through
+ case 0:
+ if ( AdapterEnum->Level == 0) {
+ TypicalSize = CoreSize = sizeof( RPL_ADAPTER_INFO_0);
+ }
+ TypicalSize += 8 * sizeof( WCHAR); // typical size of AdapterName
+ TypicalSize += 20 * sizeof( WCHAR); // typical size of AdapterComment
+ break;
+ default:
+ return( ERROR_INVALID_LEVEL);
+ break;
+ }
+
+ if ( PrefMaxLength == -1) {
+ SpaceLeft = DEFAULT_BUFFER_SIZE;
+ } else {
+ SpaceLeft = PrefMaxLength;
+ }
+
+ ArrayLength = SpaceLeft / TypicalSize;
+ if ( ArrayLength == 0) {
+ ArrayLength = 1; // try to return at least one element
+ }
+
+ Buffer = MIDL_user_allocate( ArrayLength * CoreSize);
+ if ( Buffer == NULL) {
+ return( ERROR_NOT_ENOUGH_MEMORY);
+ }
+ RplDump( RG_DebugLevel & RPL_DEBUG_ADAPTER, (
+ "AdapterEnum: Buffer=0x%x, ArrayLength=0x%x", Buffer, ArrayLength));
+
+ AdapterEnum->AdapterInfo.Level0->Buffer = (LPRPL_ADAPTER_INFO_0)Buffer;
+
+ EntriesRead = 0;
+ InfoError = FALSE;
+ Error = NO_ERROR;
+
+ EnterCriticalSection( &RG_ProtectDatabase);
+ Call( JetBeginTransaction( pSession->SesId));
+
+ if ( !RplFilterFirst( pSession, ADAPTER_TABLE_TAG, NULL, pResumeHandle, &TableEnd)) {
+ Error = NERR_RplCannotEnum;
+ goto cleanup;
+ }
+ if ( TableEnd == TRUE) {
+ goto cleanup;
+ }
+ for ( ; ; ) {
+ memset( Buffer, 0, CoreSize); // for cleanup to work properly
+ Error = AdapterGetInfo( pSession, NULL, AdapterEnum->Level, Buffer, &SpaceLeft);
+ if ( Error != NO_ERROR) {
+ InfoError = TRUE; // clean things up without holding crit sec
+ break;
+ }
+ EntriesRead++;
+ Buffer += CoreSize;
+ SpaceLeft -= CoreSize;
+ JetError = JetMove( pSession->SesId, pSession->AdapterTableId, JET_MoveNext, 0);
+ if ( JetError != JET_errSuccess) {
+ break; // assume end of table
+ }
+ if ( SpaceLeft <= 0) {
+ Error = ERROR_MORE_DATA;
+ break;
+ }
+ if ( EntriesRead >= ArrayLength) {
+ Error = ERROR_MORE_DATA;
+ break;
+ }
+ }
+cleanup:
+ Call( JetCommitTransaction( pSession->SesId, 0));
+ LeaveCriticalSection( &RG_ProtectDatabase);
+ if ( InfoError == TRUE) {
+ AdapterGetInfoCleanup( AdapterEnum->Level, Buffer);
+ }
+ if ( Error == NO_ERROR) {
+ *TotalEntries = EntriesRead;
+ } else if ( Error == ERROR_MORE_DATA) {
+ *TotalEntries = EntriesRead * 2; // we cheat here
+ } else {
+ //
+ // Cleanup in case of "bad" errors.
+ //
+ while ( EntriesRead > 0) {
+ EntriesRead--;
+ Buffer -= CoreSize;
+ AdapterGetInfoCleanup( AdapterEnum->Level, Buffer);
+ }
+ MIDL_user_free( Buffer);
+ }
+
+ RplDump( RG_DebugLevel & RPL_DEBUG_ADAPTER, ("AdapterEnum: EntriesRead = 0x%x", EntriesRead));
+
+ AdapterEnum->AdapterInfo.Level0->EntriesRead = EntriesRead;
+ if ( EntriesRead == 0) {
+ AdapterEnum->AdapterInfo.Level0->Buffer = NULL;
+ }
+
+ if ( ARGUMENT_PRESENT( pResumeHandle)) {
+ if ( Error == ERROR_MORE_DATA && EntriesRead > 0) {
+ EnterCriticalSection( &RG_ProtectDatabase);
+ Call( JetBeginTransaction( pSession->SesId));
+ RplFilterSave( pSession, (DWORD)ServerHandle, NULL,
+ ((LPRPL_ADAPTER_INFO_0)(Buffer-CoreSize))->AdapterName,
+ pResumeHandle);
+ Call( JetCommitTransaction( pSession->SesId, JET_bitCommitFlush));
+ LeaveCriticalSection( &RG_ProtectDatabase);
+ } else {
+ *pResumeHandle = 0; // resume from beginning
+ }
+ }
+ return( Error);
+}
+
diff --git a/private/net/svcdlls/rpl/server/apisec.c b/private/net/svcdlls/rpl/server/apisec.c
new file mode 100644
index 000000000..7fbd8184d
--- /dev/null
+++ b/private/net/svcdlls/rpl/server/apisec.c
@@ -0,0 +1,124 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ rplsec.c
+
+Abstract:
+
+ This module contains the remote boot service support routines
+ that create security objects and enforce security _access checking.
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 06 - November - 1992
+
+Revision History:
+
+ 06-Nov-1992 vladimv
+ Created
+
+--*/
+
+#include "local.h"
+#include "apisec.h"
+#include <netlibnt.h> // NetpNtStatusToApiStatus
+#include <secobj.h> // ACE_DATA
+
+//
+// Structure that describes the mapping of Generic access rights to
+// object specific access rights for the remote boot service security object.
+//
+GENERIC_MAPPING RG_SecurityMapping = {
+ STANDARD_RIGHTS_READ | // Generic read
+ RPL_RECORD_ENUM |
+ RPL_RECORD_GET_INFO,
+ STANDARD_RIGHTS_WRITE | // Generic write
+ RPL_RECORD_ADD |
+ RPL_RECORD_SET_INFO |
+ RPL_RECORD_DEL,
+ STANDARD_RIGHTS_EXECUTE, // Generic execute
+ RPL_RECORD_ALL_ACCESS | // Generic all
+ RPL_RECORD_CLONE
+ };
+
+PSECURITY_DESCRIPTOR RG_SecurityDescriptor;
+
+
+NET_API_STATUS RplCreateSecurityObject( VOID)
+/*++
+
+Routine Description:
+
+ This function creates the remote bootr user-mode configuration
+ information object which is represented by a security descriptors.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NET_API_STATUS code
+
+--*/
+{
+ NTSTATUS status;
+
+ //
+ // Order matters! These ACEs are inserted into the DACL in the
+ // following order. Security access is granted or denied based on
+ // the order of the ACEs in the DACL.
+ //
+ // LocalGroupAdmins are fow now allowed to perform all remote boot
+ // Service operations. Everybody else is denied.
+ //
+
+ ACE_DATA aceData[] = {
+ {ACCESS_ALLOWED_ACE_TYPE, 0, 0, GENERIC_ALL, &AliasAdminsSid}
+ };
+
+
+ status = NetpCreateSecurityObject(
+ aceData, // pAceData
+ sizeof(aceData) / sizeof(aceData[0]), // countAceData
+ NULL, // OwnerSid
+ NULL, // PrimaryGroupSid
+ &RG_SecurityMapping, // GenericToSpecificMapping
+ &RG_SecurityDescriptor // ppNewDescriptor
+ );
+
+ if ( ! NT_SUCCESS (status)) {
+ RplDump( ++RG_Assert, ( "status = 0x%x", status));
+ return NetpNtStatusToApiStatus( status);
+ }
+
+ return( NO_ERROR);
+}
+
+
+
+DWORD RplDeleteSecurityObject( VOID)
+/*++
+
+Routine Description:
+
+ This function destroys the remote boot service user-mode configuration
+ information object which is represented by a security descriptors.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NET_API_STATUS code
+
+--*/
+{
+ return( NetpDeleteSecurityObject( &RG_SecurityDescriptor));
+}
+
+
diff --git a/private/net/svcdlls/rpl/server/apisec.h b/private/net/svcdlls/rpl/server/apisec.h
new file mode 100644
index 000000000..4393620e3
--- /dev/null
+++ b/private/net/svcdlls/rpl/server/apisec.h
@@ -0,0 +1,52 @@
+/*++
+
+Copyright (c) 1987-94 Microsoft Corporation
+
+Module Name:
+
+ rplsec.h
+
+Abstract:
+
+ Functions exported by rplsec.c
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 05 - April - 1994
+
+Revision History:
+
+ 05-Apr-1994 vladimv
+ Created
+
+--*/
+
+NET_API_STATUS RplCheckSecurity( ACCESS_MASK DesiredAccess);
+NET_API_STATUS RplCreateSecurityObject( VOID);
+NET_API_STATUS RplDeleteSecurityObject( VOID);
+
+//
+// Object specific access masks
+//
+
+#define RPL_RECORD_ADD 0x0001
+#define RPL_RECORD_CLONE 0x0002
+#define RPL_RECORD_DEL 0x0004
+#define RPL_RECORD_ENUM 0x0008
+#define RPL_RECORD_GET_INFO 0x0010
+#define RPL_RECORD_SET_INFO 0x0020
+
+#define RPL_RECORD_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | \
+ RPL_RECORD_ADD | \
+ RPL_RECORD_DEL | \
+ RPL_RECORD_ENUM | \
+ RPL_RECORD_CLONE | \
+ RPL_RECORD_SET_INFO | \
+ RPL_RECORD_GET_INFO )
+
+
+#define SECURITY_OBJECT L"RplSecurityObject"
+
+extern PSECURITY_DESCRIPTOR RG_SecurityDescriptor;
+extern GENERIC_MAPPING RG_SecurityMapping;
+
diff --git a/private/net/svcdlls/rpl/server/bbcfile.c b/private/net/svcdlls/rpl/server/bbcfile.c
new file mode 100644
index 000000000..6344ceb93
--- /dev/null
+++ b/private/net/svcdlls/rpl/server/bbcfile.c
@@ -0,0 +1,1059 @@
+/*++
+
+Copyright (c) 1987-1993 Microsoft Corporation
+
+Module Name:
+
+ bbcfile.c
+
+Abstract:
+
+ Processes boot block configuration file.
+
+ Provides bbcfile functionality similar to that contained in init.c
+ & patch.c of LANMAN 2.1 code.
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 19 - November - 1993
+
+Environment:
+
+ User mode
+
+Revision History :
+
+--*/
+
+
+
+#include "local.h"
+#include "bbcfile.h"
+
+//
+// Checksum buffer size is chosen to be a multiple of 8 * sizeof( DWORD).
+// to speed up checksum algorithm below. This is not a requirement though.
+//
+#define CHK_SUM_BUF_SIZE ( 8 * sizeof( DWORD) * 256) // 8K
+
+#define NO_PATCH_OFFSET ((DWORD)-1)
+
+#define PARAM_INDEX 2 // index of parameters in sys/com/exe line
+//
+// PARAM_INDEX string when expressed in DBCS should not exceed 0xFF bytes.
+//
+#define MAX_SIZE_DBCS_PARAMS (0xFF+1)
+
+#define MEM_INDEX 3 // index of extra memory in sys/com/exe line
+#define MOVEABLE_INDEX 4 // set if driver can be moved after init
+
+//
+// MOVABLE_INDEX string may be take one of the following two values.
+//
+#define MOVEABLE_SWITCH L'M'
+#define EXEC_IN_LOW_MEM_SWITCH L'L' // undocumented switch value
+
+#define MAX_FLIST_LEN 255
+
+//
+// Indices used for parsing of lines in boot block configuration files.
+//
+
+#define CONF_LINE_ID 0 // index of line type
+#define CONF_LINE_FILE 1 // index of file (rel path) in config line
+#define CONF_LINE_BASE_ADDRESS 1 // index of file (rel path) in config line
+#define FIRST_FILE_NAME 3 // index of the first file name in LDR line
+
+#define MAX_CONFIG_FIELDS 8 // maximum number of fields in config line
+
+#define MIN_BBLOCK_BASE_SEG 0xc0
+#define TILDE_STRING L"~"
+#define SPACE_STRING L" "
+
+
+
+DWORD StringToDword( IN PWCHAR String)
+/*++
+ We would like to use generic base (0) but it does not work for
+ strings like "D0H". That is the reason why we first check if
+ the last character is 'H' or 'h'.
+--*/
+{
+ DWORD Length;
+
+ Length = wcslen( String);
+ if ( Length == 0) {
+ return( 0);
+ }
+ if ( String[ Length-1] == L'H' || String[ Length-1] == L'h') {
+ return( wcstoul( String, NULL, 16));
+ } else {
+ return( wcstoul( String, NULL, 0));
+ }
+}
+
+
+BOOL RplInitExeOrSys(
+ IN PRPL_WORKER_DATA pWorkerData,
+ IN OUT PFLIST_TBL pFlist
+ )
+/*++
+
+Routine Description:
+ Retreives the file type of a binary (exe/com/sys) file.
+
+Arguments:
+ pFlist - pointer to FLIST_TBL element
+
+Return Value:
+ TRUE if successful, else FALSE.
+
+--*/
+{
+ BYTE id_tbl[2];
+ HANDLE FileHandle;
+ DWORD bytes_read;
+ DWORD DbcsSize;
+ LPSTR DbcsString;
+ PWCHAR * WordTable;
+
+ FileHandle = RplFileOpen( pFlist->path_name);
+ if ( FileHandle == INVALID_HANDLE_VALUE) {
+ RplDump( ++RG_Assert, ( "path_name=%ws", pFlist->path_name));
+ pWorkerData->EventStrings[ 0] = pWorkerData->WkstaName;
+ pWorkerData->EventStrings[ 1] = pFlist->path_name;
+ pWorkerData->EventId = NELOG_RplWkstaFileOpen;
+ return( FALSE);
+ }
+
+ if ( !ReadFile( FileHandle, id_tbl, 2, &bytes_read, NULL)) {
+ RplDump( ++RG_Assert, ( "Error = %d", GetLastError()));
+ pWorkerData->EventStrings[ 0] = pWorkerData->WkstaName;
+ pWorkerData->EventStrings[ 1] = pFlist->path_name;
+ pWorkerData->EventId = NELOG_RplWkstaFileRead;
+ (VOID)CloseHandle( FileHandle);
+ return( FALSE);
+ }
+ (VOID)CloseHandle( FileHandle);
+
+ if ( bytes_read == 2 && id_tbl[0] == 0x4d && id_tbl[1] == 0x5a) {
+ //
+ // Driver or executable file is in exe-format, set the bit in
+ // the type field. This info will be used on the client side.
+ //
+ pFlist->FileData.file_type |= IS_EXE_SYS;
+ }
+
+ WordTable = pWorkerData->WordTable;
+
+ if ( *WordTable[ PARAM_INDEX] == 0) {
+ pFlist->FileData.param_len = 0;
+ pFlist->param_list = (LPSTR)&RG_Null;
+ } else {
+ DbcsSize = RplUnicodeToDbcs(
+ pWorkerData->MemoryHandle,
+ WordTable[ PARAM_INDEX],
+ -1, // UNICODE string length not available
+ MAX_SIZE_DBCS_PARAMS,
+ &DbcsString
+ );
+ if ( DbcsSize == 0) {
+ RplDump( ++RG_Assert, ("WordTable=0x%x, string=%ws",
+ WordTable, WordTable[ PARAM_INDEX]));
+ pWorkerData->EventStrings[ 0] = pWorkerData->WkstaName;
+ pWorkerData->EventStrings[ 1] = pFlist->path_name;
+ pWorkerData->EventId = NELOG_RplWkstaFileSize;
+ return( FALSE);
+ }
+ pFlist->FileData.param_len = (BYTE)(DbcsSize - 1);
+ pFlist->param_list = DbcsString;
+ }
+
+ pFlist->FileData.extra_mem = StringToDword( WordTable[ MEM_INDEX]);
+
+ if ( *WordTable[ MOVEABLE_INDEX] == MOVEABLE_SWITCH) {
+ pFlist->FileData.file_type |= IS_MOVEABLE;
+ } else if ( *WordTable[ MOVEABLE_INDEX] == EXEC_IN_LOW_MEM_SWITCH) {
+ pFlist->FileData.file_type |= EXEC_IN_LOW_MEM;
+ }
+ return( TRUE);
+}
+
+
+BOOL RplChecksum(
+ IN OUT PRPL_WORKER_DATA pWorkerData,
+ IN HANDLE FileHandle,
+ OUT PWORD pChecksum
+ )
+/*++
+Routine Description:
+ If successful returns sum of all words in a file.
+
+Arguments:
+ FileHandle - handle of file to checksum
+ pChecksum - pointer to calculated checksum
+
+Return Value:
+ TRUE if success, FALSE otherwise
+--*/
+{
+ DWORD Checksum;
+ DWORD BytesRead;
+ PDWORD pDword;
+ DWORD Length;
+
+ if ( pWorkerData->ChecksumBuffer == NULL) {
+ pWorkerData->ChecksumBuffer = RplMemAlloc( pWorkerData->MemoryHandle,
+ CHK_SUM_BUF_SIZE);
+ if ( pWorkerData->ChecksumBuffer == NULL) {
+ return( FALSE);
+ }
+ }
+
+ Checksum = 0;
+
+ do {
+ //
+ // These are usually binary files so there are no DBCS/UNICODE
+ // issues. But even if we had a text file here, we should read
+ // it & checksum it raw (i.e. no conversion from DBCS to UNICODE)
+ // since it is the raw data that gets shipped to the client.
+ //
+ if ( !ReadFile( FileHandle,
+ pWorkerData->ChecksumBuffer,
+ CHK_SUM_BUF_SIZE,
+ &BytesRead,
+ NULL)) {
+ RplDump( ++RG_Assert, ( "Error = %d", GetLastError()));
+ return( FALSE);
+ }
+
+ //
+ // Round up to make it divisible by 4 == sizeof( *pDword).
+ // Note that extra bytes are set zero in the 'boot' block,
+ // the files are always on the boundary of paragraph.
+ //
+ if ( BytesRead & 1) { // make it divisible by 2
+ pWorkerData->ChecksumBuffer[ BytesRead++] = 0;
+ }
+ if ( BytesRead & 2) { // make it divisible by 4
+ pWorkerData->ChecksumBuffer[ BytesRead++] = 0;
+ pWorkerData->ChecksumBuffer[ BytesRead++] = 0;
+ }
+
+ //
+ // Checksum in chunks of 8, for speed.
+ //
+ for ( Length = BytesRead / 4,
+ pDword = (PDWORD)pWorkerData->ChecksumBuffer + Length;
+ Length > 8; Length -= 8) {
+ pDword -= 8;
+ Checksum += pDword[ 0];
+ Checksum += pDword[ 1];
+ Checksum += pDword[ 2];
+ Checksum += pDword[ 3];
+ Checksum += pDword[ 4];
+ Checksum += pDword[ 5];
+ Checksum += pDword[ 6];
+ Checksum += pDword[ 7];
+ }
+ // Then finish the checksum.
+ //
+ for ( ; Length > 0; Length--) {
+ Checksum += *(--pDword);
+ }
+ } while (BytesRead == CHK_SUM_BUF_SIZE); // exit read loop when all read
+
+ *pChecksum = LOWORD( Checksum) + HIWORD( Checksum);
+ return( TRUE);
+}
+
+
+BOOL RplInitFileList(
+ IN PRPL_WORKER_DATA pWorkerData,
+ IN LPWSTR rel_path,
+ IN OUT PFLIST_TBL pFlist,
+ IN OUT PDWORD cur_para_ptr,
+ IN DWORD file_type
+ )
+/*++
+
+Routine Description:
+
+ Opens a file and initalizes the parameter item of the file list table.
+ It reads the file only if checksum is required and file is not of
+ RPL_BOOT_TYPE.
+
+Arguments:
+
+ rel_path - relative path name of file
+ pFlist - pointer to current element in file list table
+ cur_para_ptr - current paragraph
+ file_type - file type
+
+Return Value:
+ TRUE if success, FALSE otherwise.
+
+--*/
+{
+ HANDLE FileHandle;
+ LPSTR DbcsFileName;
+ DWORD DbcsFileNameSize;
+ DWORD Size;
+ BOOL Success;
+
+ Success = FALSE;
+ FileHandle = INVALID_HANDLE_VALUE;
+
+ RplDump( RG_DebugLevel & RPL_DEBUG_FLOW,( "++InitFileList(0x%x)", pWorkerData));
+
+ //
+ // get the full path name and the actual file name
+ //
+
+ Size = ( RG_DirectoryLength + wcslen( rel_path) + 1) * sizeof(WCHAR);
+ pFlist->path_name = RplMemAlloc( pWorkerData->MemoryHandle, Size);
+ if ( pFlist->path_name == NULL) {
+ pWorkerData->EventStrings[ 0] = pWorkerData->WkstaName;
+ pWorkerData->EventId = NELOG_RplWkstaMemory;
+ goto cleanup;
+ }
+ wcscpy( pFlist->path_name, RG_Directory);
+ wcscat( pFlist->path_name, rel_path);
+
+ //
+ // Open the BBC file and get its length.
+ //
+
+ FileHandle = RplFileOpen( pFlist->path_name);
+ if ( FileHandle == INVALID_HANDLE_VALUE) {
+ RplDump( ++RG_Assert, ("path_name=%ws", pFlist->path_name));
+ pWorkerData->EventStrings[ 0] = pWorkerData->WkstaName;
+ pWorkerData->EventStrings[ 1] = pFlist->path_name;
+ pWorkerData->EventId = NELOG_RplWkstaFileOpen;
+ goto cleanup;
+ }
+ Size = GetFileSize( FileHandle, NULL);
+ if ( Size == INVALID_FILE_SIZE) {
+ RplDump( ++RG_Assert, ( "Error=%d, path_name=%ws",
+ GetLastError(), pFlist->path_name));
+ pWorkerData->EventStrings[ 0] = pWorkerData->WkstaName;
+ pWorkerData->EventStrings[ 1] = pFlist->path_name;
+ pWorkerData->EventId = NELOG_RplWkstaFileSize;
+ goto cleanup;
+ }
+ pFlist->FileData.file_len = Size;
+
+ //
+ // Calculate checksum for the file, if cheksums are done. Note that
+ // RPLBOOT.SYS is never checksummed because it modifies its own code
+ // before the checking the checksum.
+ //
+
+ if ( RG_ReadChecksum && file_type != RPL_BOOT_TYPE) {
+ if ( !RplChecksum( pWorkerData, FileHandle, &pFlist->FileData.chk_sum)) {
+ pFlist->FileData.chk_sum = NO_CHKSUM_USED; // for RPLBOOT to ingore checksum
+ pWorkerData->EventStrings[ 0] = pWorkerData->WkstaName;
+ pWorkerData->EventStrings[ 1] = pFlist->path_name;
+ pWorkerData->EventId = NELOG_RplWkstaFileChecksum;
+ }
+ } else {
+ pFlist->FileData.chk_sum = NO_CHKSUM_USED; // RPLBOOT is never checksummed
+ }
+
+ //
+ // Note that FileName is UNICODE, while client needs to receive a DBCS
+ // version of this name
+ //
+ pFlist->FileName = RplGetLastPathComponent( pFlist->path_name);
+
+ DbcsFileNameSize = RplUnicodeToDbcs(
+ pWorkerData->MemoryHandle,
+ pFlist->FileName, // UNICODE string to convert
+ -1, // UNICODE string length not available
+ MAX_SIZEOF_DBCS_PATH,
+ &DbcsFileName
+ );
+ if ( DbcsFileNameSize == 0) {
+ RplDump( ++RG_Assert, ("FileName=%ws", pFlist->FileName));
+ pWorkerData->EventStrings[ 0] = pWorkerData->WkstaName;
+ pWorkerData->EventStrings[ 1] = pFlist->path_name;
+ pWorkerData->EventId = NELOG_RplWkstaFileSize;
+ goto cleanup;
+ }
+
+ pFlist->DbcsFileName = DbcsFileName;
+ pFlist->DbcsFileNameSize = DbcsFileNameSize;
+
+ //
+ // Length of file in paragraphs, rounded up to the next paragraph.
+ // The maximum length could be checked, but let it be.
+ // Set the relative position of file from the start of file block
+ //
+ pFlist->FileData.file_len = (pFlist->FileData.file_len + 15) & 0xfffffff0L;
+
+ pFlist->FileData.file_type = (WORD)file_type;
+ pFlist->FileData.param_len = 0;
+ pFlist->FileData.extra_mem = 0;
+ pFlist->FileData.param_offset = 0;
+ pFlist->FileData.name_offset = 0;
+ pFlist->param_list = NULL;
+
+ //
+ // There may be several instances of the same file in the boot block.
+ // For example, Nokia NetStation memory extender LOADHI.SYS can load
+ // device drivers to memory between C000 - FFFF. PROTMAN.SYS, NDIS and
+ // NETBEUI may all be loaded to there. These boot block lines
+ // load the whole DOS NDIS stack to the memory above C000
+ // DAT NETBEUI.DOS
+ // DRV LOADHI.SYS NETBEUI.DOS ~
+ // DAT IBMTOK.DOS
+ // DRV LOADHI.SYS IBMTOK.DOS ~
+ // DAT PROTMAN.DOS
+ // DRV LOADHI.SYS PROTMAN.DOS~I:\ ~
+ //
+ // The code to send LOADHI.SYS once (instead of three times) in the boot
+ // block, is not worth of effort, since LOADHI.SYS is small (~4kB).
+ //
+
+ pFlist->FileData.file_addr = *cur_para_ptr * 16;
+ *cur_para_ptr += (pFlist->FileData.file_len / 16);
+ RplDump( RG_DebugLevel & RPL_DEBUG_FLOW,( "--InitFileList(0x%x)", pWorkerData));
+ Success = TRUE;
+
+cleanup:
+ if ( FileHandle != INVALID_HANDLE_VALUE) {
+ (VOID)CloseHandle( FileHandle);
+ }
+ return( Success);
+}
+
+
+LPWSTR * RplBbcFileToTable( IN OUT PRPL_WORKER_DATA pWorkerData)
+/*++
+
+Routine Description:
+ Reads boot block file to a buffer. Skips comment lines and copies
+ non-comment lines to the string buffer. Returns pointer to the array
+ of pointers to lines, and the length of table.
+
+Arguments:
+
+Return Value:
+ Pointer to line table if successful, NULL otherwise.
+
+--*/
+{
+#define RPL_LINE_END L"\n\r"
+ DWORD index;
+ LPWSTR * Table;
+ PWCHAR UnicodeString;
+ PWCHAR pWchar;
+
+ RplDump( RG_DebugLevel & RPL_DEBUG_FLOW,( "++BbcFileToTable(0x%x)", pWorkerData));
+
+ UnicodeString = RplReadTextFile( pWorkerData->MemoryHandle,
+ pWorkerData->BbcFile, MAX_BBC_FILE_SIZE);
+ if ( UnicodeString == NULL) {
+ RplDump( ++RG_Assert, ( "BbcFile=%ws", pWorkerData->BbcFile));
+ pWorkerData->EventStrings[ 0] = pWorkerData->WkstaName;
+ pWorkerData->EventStrings[ 1] = pWorkerData->BbcFile;
+ pWorkerData->EventId = NELOG_RplWkstaFileRead;
+ return( NULL);
+ }
+
+ Table = RplMemAlloc( pWorkerData->MemoryHandle, (MAX_FLIST_LEN+1)* sizeof( LPWSTR));
+ if ( Table == NULL) {
+ pWorkerData->EventStrings[ 0] = pWorkerData->WkstaName;
+ pWorkerData->EventId = NELOG_RplWkstaMemory;
+ return( NULL);
+ }
+
+ for ( pWchar = wcstok( UnicodeString, RPL_LINE_END), index = 0;
+ pWchar != NULL && index < MAX_FLIST_LEN;
+ pWchar = wcstok( NULL, RPL_LINE_END)) {
+
+ while( iswspace( *pWchar)) { // skip empty chars at the beginning
+ pWchar++;
+ }
+ if ( *pWchar == 0 || *pWchar == L';') {
+ continue; // don't save blank or comment lines
+ }
+
+ Table[ index++] = pWchar;
+ }
+
+ if ( pWchar != NULL) {
+ //
+ // Error case. Too many lines in the file.
+ //
+ RplDump( ++RG_Assert, ( "pWchar=0x%x", pWchar));
+ pWorkerData->EventStrings[ 0] = pWorkerData->WkstaName;
+ pWorkerData->EventStrings[ 1] = pWorkerData->BbcFile;
+ pWorkerData->EventId = NELOG_RplWkstaFileLineCount;
+ Table = NULL;
+ } else {
+ Table[ index] = NULL; // terminate the table
+ }
+ RplDump( RG_DebugLevel & RPL_DEBUG_FLOW,( "--BbcFileToTable(0x%x)", pWorkerData));
+ return( Table);
+
+} // RplBbcFileToTable()
+
+
+
+BOOL RplBbcLineToTable(
+ IN OUT PRPL_WORKER_DATA pWorkerData,
+ IN LPWSTR Line
+ )
+/*++
+
+Routine Description:
+
+ Copies a line into a buffer. Breaks the copy into "words" (substrings).
+ Makes an array of pointers to "words". The original line string is kept
+ unchanged because it is needed for error reporting in the caller.
+
+Arguments:
+ Line - ptr to line string
+
+Return Value:
+ TRUE if success, FALSE otherwise.
+ FALSE is returned if we ran out of memory, or if line has too many words.
+
+--*/
+{
+ DWORD index;
+ DWORD BufferSize;
+ LPWSTR Cursor;
+
+ RplDump( RG_DebugLevel & RPL_DEBUG_FLOW,( "++BbcLineToTable(0x%x):%ws", pWorkerData, Line));
+
+ BufferSize = (wcslen( Line) + 1) * sizeof( WCHAR);
+
+ if ( pWorkerData->LineBuffer == NULL) {
+
+ pWorkerData->LineBuffer = RplMemAlloc( pWorkerData->MemoryHandle, BufferSize);
+ if ( pWorkerData->LineBuffer == NULL) {
+ pWorkerData->EventStrings[ 0] = pWorkerData->WkstaName;
+ pWorkerData->EventId = NELOG_RplWkstaMemory;
+ return( FALSE);
+ }
+ pWorkerData->LineBufferSize = BufferSize;
+
+ } else if ( BufferSize > pWorkerData->LineBufferSize) {
+
+ RplMemFree( pWorkerData->MemoryHandle, pWorkerData->LineBuffer);
+ pWorkerData->LineBuffer = RplMemAlloc( pWorkerData->MemoryHandle, BufferSize);
+ if ( pWorkerData->LineBuffer == NULL) {
+ pWorkerData->EventStrings[ 0] = pWorkerData->WkstaName;
+ pWorkerData->EventId = NELOG_RplWkstaMemory;
+ return( FALSE);
+ }
+ pWorkerData->LineBufferSize = BufferSize;
+ }
+
+ if ( pWorkerData->WordTable == NULL) {
+ pWorkerData->WordTable = RplMemAlloc(
+ pWorkerData->MemoryHandle,
+ (MAX_CONFIG_FIELDS + 1) * sizeof(LPWSTR)
+ );
+ if ( pWorkerData->WordTable == NULL) {
+ pWorkerData->EventStrings[ 0] = pWorkerData->WkstaName;
+ pWorkerData->EventId = NELOG_RplWkstaMemory;
+ return( FALSE);
+ }
+ }
+
+ memcpy( pWorkerData->LineBuffer, Line, BufferSize);
+
+ for ( index = 0, Cursor = wcstok( pWorkerData->LineBuffer, SPACE_STRING);
+ index < MAX_CONFIG_FIELDS && Cursor != NULL;
+ index++, Cursor = wcstok( NULL, SPACE_STRING)) {
+
+ if ( wcscmp( Cursor, TILDE_STRING) == 0) {
+ //
+ // Single tilda is just an empty placeholder.
+ //
+ pWorkerData->WordTable[ index] = (LPWSTR)&RG_Null;
+
+ } else {
+ pWorkerData->WordTable[ index] = Cursor;
+ //
+ // In unlikely case there are tildas embedded with a filename
+ // (e.g. driver with its parameters), replace them tildas with spaces.
+ //
+ while ( (Cursor = wcsrchr( Cursor, TILDE_CHAR)) != NULL) {
+ *Cursor++ = SPACE_CHAR;
+ }
+ }
+ }
+
+ if ( Cursor != NULL) {
+ //
+ // Boot block config line has too many items.
+ //
+ RplDump( ++RG_Assert, ( "Cursor=0x%x", Cursor));
+ pWorkerData->EventStrings[ 0] = pWorkerData->WkstaName;
+ pWorkerData->EventStrings[ 1] = pWorkerData->BbcFile;
+ pWorkerData->EventStrings[ 2] = Line;
+ pWorkerData->EventId = NELOG_Invalid_Config_Line;
+ return( FALSE);
+ }
+
+ while ( index < MAX_CONFIG_FIELDS) {
+ pWorkerData->WordTable[ index++] = (LPWSTR)&RG_Null; // fill in the rest
+ }
+ pWorkerData->WordTable[ index] = NULL; // then null terminate
+ RplDump( RG_DebugLevel & RPL_DEBUG_FLOW,( "--BbcLineToTable(0x%x):%ws", pWorkerData, Line));
+ return( TRUE);
+}
+
+
+CONFIG_TYPE ConfigTypeTable[] = {
+ { L"RPL", RPL_BOOT_TYPE},
+ { L"ORG", ORG_TYPE},
+ { L"DAT", DATA_FILE},
+ { L"LDR", BINARY_LOADER},
+ { L"DRV", DRV_TYPE},
+ { L"EXE", EXE_TYPE},
+ { L"BASE", BASE_TYPE},
+ { NULL, UNKNOWN_CONFIG_TYPE}
+};
+
+DWORD RplConfigLineType( IN LPWSTR ConfigLineId)
+/*++
+
+Routine Description:
+
+ Returns the type of the current line in boot block config file.
+
+Arguments:
+
+ Pointer to identifier for the current line.
+
+Return Value:
+
+ Config line type.
+
+--*/
+{
+ DWORD Type;
+ PCONFIG_TYPE pConfigType;
+
+ for ( Type = UNKNOWN_CONFIG_TYPE, pConfigType = ConfigTypeTable;
+ pConfigType->id != 0;
+ pConfigType++) {
+ if ( !wcscmp( ConfigLineId, pConfigType->id )) {
+ Type = pConfigType->type;
+ break;
+ }
+ }
+ return( Type);
+}
+
+
+BOOL RplCheckBootHeader(
+ IN PRPL_WORKER_DATA pWorkerData,
+ IN LPWSTR FilePath,
+ OUT PDWORD pFirstOffset
+ )
+/*++
+
+Routine Description:
+ Reads patch offsets from the start of DLCLOADR.COM file.
+
+Arguments:
+ FilePath - path name of DLCLOADR.COM
+ pFirstOffset - ptr to first NLS patch offset
+
+Return Value:
+ TRUE if successful, FALSE otherwise.
+
+--*/
+{
+ HANDLE FileHandle;
+ DWORD read_len;
+ WORD offset_buf[ OFFSET_BUF_LEN];
+ PRPLBOOT_HEADER pRplbootHdr;
+ BOOL Success;
+
+ Success = FALSE;
+
+ FileHandle = RplFileOpen( FilePath);
+ if ( FileHandle == INVALID_HANDLE_VALUE) {
+ RplDump( ++RG_Assert, ( "FilePath=%ws", FilePath));
+ pWorkerData->EventStrings[ 0] = pWorkerData->WkstaName;
+ pWorkerData->EventStrings[ 1] = FilePath;
+ pWorkerData->EventId = NELOG_RplWkstaFileOpen;
+ goto cleanup;
+ }
+
+ if ( !ReadFile( FileHandle, (PBYTE)offset_buf,
+ OFFSET_BUF_LEN * sizeof(WORD), &read_len, NULL)) {
+ RplDump( ++RG_Assert, ( "Error=%d", GetLastError()));
+ pWorkerData->EventStrings[ 0] = pWorkerData->WkstaName;
+ pWorkerData->EventStrings[ 1] = FilePath;
+ pWorkerData->EventId = NELOG_RplWkstaFileRead;
+ goto cleanup;
+ }
+
+ pRplbootHdr = (PRPLBOOT_HEADER)((PBYTE)offset_buf + OFFSET_RPLBOOT_HDR);
+
+ if ( !strcmp( pRplbootHdr->achIdStamp, "RPL" ) && // BUGBUG hardcoded constants
+ pRplbootHdr->bBbVersion == BBVERSION_10 ) {
+ //
+ // check that rplboot.sys nls patch version match or
+ // it does not require NLS patching at all
+ //
+ if (pRplbootHdr->bNlsVersion == NLS_VERSION_10) {
+ //
+ // get the offset of NLS patches in RPLBOOT.SYS
+ //
+ *pFirstOffset = pRplbootHdr->usNlsPatchOff;
+ } else if (pRplbootHdr->bNlsVersion == 0) {
+ //
+ // No NLS patching, if version number is 0, that's OK.
+ //
+ *pFirstOffset = NO_PATCH_OFFSET;
+ } else {
+ //
+ // Configuration error: RPLBOOT.SYS expects to get a
+ // different NLS patching from one that RPLSERVR can provide,
+ // the versions are incompatible.
+ //
+ RplDump( ++RG_Assert, ( "FilePath=%ws", FilePath));
+ pWorkerData->EventStrings[ 0] = pWorkerData->WkstaName;
+ pWorkerData->EventStrings[ 1] = FilePath;
+ pWorkerData->EventId = NELOG_RplWkstaWrongVersion;
+ goto cleanup;
+ }
+ } else {
+ //
+ // Configuration error: the header is missing, this is an old
+ // (or wrong) RPLBOOT.sys.
+ //
+ RplDump( ++RG_Assert, ( "FilePath=%ws", FilePath));
+ pWorkerData->EventStrings[ 0] = pWorkerData->WkstaName;
+ pWorkerData->EventStrings[ 1] = FilePath;
+ pWorkerData->EventId = NELOG_RplWkstaWrongVersion;
+ goto cleanup;
+ }
+ Success = TRUE;
+
+cleanup:
+ if ( FileHandle != INVALID_HANDLE_VALUE) {
+ (VOID)CloseHandle( FileHandle);
+ }
+ return( Success);
+}
+
+
+BOOL RplBbcFile( IN OUT PRPL_WORKER_DATA pWorkerData)
+/*++
+
+Routine Description:
+
+ Processes the boot block configuration file. Initializes file list table.
+
+Arguments:
+
+Return Value:
+ TRUE if success, FALSE otherwise
+
+--*/
+{
+ LPWSTR * LineTable; // ptr to array of line-strings for BBC file
+ DWORD LoaderLineIndex; // index of a loader line in BBC file
+ LPWSTR * LoaderWordTable; // ptr to array of word-strings for loader line
+ LPWSTR LoaderBuffer; // buffer for LoaderWordTable
+ PRESOURCE_TBL resource_tbl;
+ DWORD resource_tbl_size;
+ DWORD resource_tbl_len;
+ LPWSTR String;
+ DWORD Index;
+ DWORD org_addr;
+ DWORD cur_para; // current length of boot block image in paragraphs
+ DWORD fblock_base;
+ DWORD bblock_base; // no default base address
+ PFLIST_TBL flist_tbl; // file list array
+ DWORD flist_tbl_len; // # of elements in flist_tbl[]
+ DWORD flist_tbl_size; // size in bytes of flist_tbl[]
+ DWORD FileIndex; // index entries in flist_tbl[]
+ BOOL org_is_set;
+ DWORD line_type;
+ DWORD PatchOffset;
+
+ RplDump( RG_DebugLevel & RPL_DEBUG_FLOW,( "++BbcFile(0x%x)", pWorkerData));
+
+ pWorkerData->loader_i = pWorkerData->rplboot_i = MAXWORD;
+ fblock_base = bblock_base = 0;
+ resource_tbl_size = resource_tbl_len = 0;
+ org_is_set = FALSE;
+ cur_para = 0;
+
+ //
+ // Read boot block configuration file to a UNICODE string table, where
+ // each string from this table contains a single line from BBC file.
+ // This table is null terminated.
+ //
+ LineTable = RplBbcFileToTable( pWorkerData);
+ if ( LineTable == NULL) {
+ return( FALSE);
+ }
+
+ flist_tbl = RplMemAlloc( pWorkerData->MemoryHandle, sizeof(FLIST_TBL) * MAX_FLIST_LEN);
+ if ( flist_tbl == NULL) {
+ pWorkerData->EventStrings[ 0] = pWorkerData->WkstaName;
+ pWorkerData->EventId = NELOG_RplWkstaMemory;
+ return( FALSE);
+ }
+
+ //
+ // Process lines in the boot block configuration file and
+ // insert data to file list
+ //
+ for ( Index = FileIndex = 0; (String = LineTable[ Index]) != NULL; Index++) {
+ //
+ // Break up single line from boot block config file into word table.
+ //
+ if ( !RplBbcLineToTable( pWorkerData, String)) {
+ return( FALSE);
+ }
+
+ line_type = RplConfigLineType( pWorkerData->WordTable[ CONF_LINE_ID]);
+
+ switch ( line_type) {
+
+ case RPL_BOOT_TYPE: // There must be a single entry like this.
+ if ( pWorkerData->rplboot_i != MAXWORD) {
+ RplDump( ++RG_Assert, ( "BbcFile=%ws, String=%ws", pWorkerData->BbcFile, String));
+ pWorkerData->EventStrings[ 0] = pWorkerData->WkstaName;
+ pWorkerData->EventStrings[ 1] = pWorkerData->BbcFile;
+ pWorkerData->EventStrings[ 2] = String;
+ pWorkerData->EventId = NELOG_Invalid_Config_Line;
+ return( FALSE);
+ }
+ pWorkerData->rplboot_i = FileIndex; // fall through !!
+
+ case BINARY_LOADER: // There must be a single entry like this.
+ if (line_type == BINARY_LOADER) {
+ if ( pWorkerData->loader_i != MAXWORD) {
+ RplDump( ++RG_Assert, ( "BbcFile=%ws, String=%ws, loader_i=0x%x",
+ pWorkerData->BbcFile, String, pWorkerData->loader_i));
+ pWorkerData->EventStrings[ 0] = pWorkerData->WkstaName;
+ pWorkerData->EventStrings[ 1] = pWorkerData->BbcFile;
+ pWorkerData->EventStrings[ 2] = String;
+ pWorkerData->EventId = NELOG_Invalid_Config_Line;
+ return( FALSE);
+ }
+ pWorkerData->loader_i = FileIndex;
+ } // fall through !!
+
+ case EXE_TYPE:
+ case DRV_TYPE:
+ case DATA_FILE:
+ if ( !RplInitFileList( pWorkerData,
+ pWorkerData->WordTable[ CONF_LINE_FILE],
+ &flist_tbl[ FileIndex], &cur_para, line_type)) {
+ return( FALSE);
+ }
+ if ( line_type == DRV_TYPE || line_type == EXE_TYPE) {
+ if ( !RplInitExeOrSys( pWorkerData, &flist_tbl[ FileIndex])) {
+ return( FALSE );
+ }
+ }
+
+ //
+ // Update the minimum size of header.
+ // Reserve space for 0xa 0xd in the end of param list
+ //
+ if ( flist_tbl[ FileIndex].FileData.param_len) {
+ pWorkerData->min_wksta_buf += (flist_tbl[ FileIndex].FileData.param_len + 2);
+ }
+ pWorkerData->min_wksta_buf += flist_tbl[ FileIndex].DbcsFileNameSize;
+ FileIndex++;
+ break;
+
+ case ORG_TYPE:
+ org_addr = StringToDword( pWorkerData->WordTable[ CONF_LINE_FILE]);
+ //
+ // file block base must be above 0C0 (paras)
+ //
+ fblock_base = org_addr - cur_para - bblock_base;
+ org_is_set = TRUE;
+ if ( cur_para + bblock_base > org_addr) {
+ RplDump( ++RG_Assert, ( "BbcFile=%ws, cur_para=0x%x,"
+ " bblock_base=0x%x, org_addr=0x%x",
+ pWorkerData->BbcFile, cur_para, bblock_base, org_addr));
+ pWorkerData->EventStrings[ 0] = pWorkerData->WkstaName;
+ pWorkerData->EventStrings[ 1] = pWorkerData->BbcFile;
+ pWorkerData->EventId = NELOG_Files_Dont_Fit;
+ return( FALSE);
+ }
+ break;
+
+ case BASE_TYPE:
+ bblock_base = StringToDword( pWorkerData->WordTable[ CONF_LINE_BASE_ADDRESS]);
+ if ( org_is_set || bblock_base < MIN_BBLOCK_BASE_SEG) {
+ //
+ // Origin has been set to a wrong address or
+ // boot block base address is too low.
+ //
+ RplDump( ++RG_Assert, ( "BbcFile=%ws, org_is_set=%d, bblock_base=0x%x",
+ pWorkerData->BbcFile, org_is_set, bblock_base));
+ pWorkerData->EventStrings[ 0] = pWorkerData->WkstaName;
+ pWorkerData->EventStrings[ 1] = pWorkerData->BbcFile;
+ pWorkerData->EventId = NELOG_Files_Dont_Fit;
+ return( FALSE);
+ }
+ break;
+
+ default:
+ RplDump( ++RG_Assert, ( "BbcFile=%ws, String=%ws", pWorkerData->BbcFile, String));
+ pWorkerData->EventStrings[ 0] = pWorkerData->WkstaName;
+ pWorkerData->EventStrings[ 1] = pWorkerData->BbcFile;
+ pWorkerData->EventStrings[ 2] = String;
+ pWorkerData->EventId = NELOG_Invalid_Config_Line;
+ return( FALSE);
+ } // switch( line_type)
+
+ if ( line_type == BINARY_LOADER) {
+ //
+ // We have to save loader line data for processing below.
+ //
+ LoaderWordTable = pWorkerData->WordTable;
+ LoaderBuffer = pWorkerData->LineBuffer;
+ pWorkerData->LineBuffer = NULL;
+ pWorkerData->WordTable = NULL;
+ LoaderLineIndex = Index;
+ }
+ }
+
+ //
+ // Boot block configuration file must contain a boot line (RPLBOOT.SYS)
+ // and a loader line (RPLSTART.COM or OS2LDR).
+ //
+
+ if ( pWorkerData->rplboot_i == MAXWORD || pWorkerData->loader_i == MAXWORD) {
+ RplDump( ++RG_Assert, ( "BbcFile=%ws, rplboot_i=0x%x, loader_i=0x%x\n",
+ pWorkerData->BbcFile, pWorkerData->rplboot_i, pWorkerData->loader_i));
+ pWorkerData->EventStrings[ 0] = pWorkerData->WkstaName;
+ pWorkerData->EventStrings[ 1] = pWorkerData->BbcFile;
+ pWorkerData->EventId = NELOG_RplWkstaBbcFile;
+ return( FALSE);
+ }
+
+ //
+ // Do not bother to resize file list table - it will be freed shortly
+ // anyway, just save the relevant size of table for later use.
+ //
+ flist_tbl_len = FileIndex;
+ flist_tbl_size = flist_tbl_len * sizeof(FLIST_TBL);
+
+ //
+ // Check the loader parameters. This code does real work for OS/2 only.
+ // For DOS it just fills the null table entry.
+ //
+
+ //
+ // Count number of items in the resource table.
+ //
+ for ( resource_tbl_len = 0;
+ *LoaderWordTable[ FIRST_FILE_NAME + resource_tbl_len]; resource_tbl_len++) {
+ NOTHING;
+ }
+ resource_tbl_len++; // leave space for terminating null
+ resource_tbl_size = resource_tbl_len * sizeof( RESOURCE) + sizeof( WORD);
+ resource_tbl = RplMemAlloc( pWorkerData->MemoryHandle, resource_tbl_size);
+ if ( resource_tbl == NULL) {
+ pWorkerData->EventStrings[ 0] = pWorkerData->WkstaName;
+ pWorkerData->EventId = NELOG_RplWkstaMemory;
+ return( FALSE);
+ }
+ if ( resource_tbl_len > MAXWORD) {
+ RplDump( ++RG_Assert, ( "resource_tbl_len=%d", resource_tbl_len));
+ return( FALSE);
+ }
+ resource_tbl->entries = (WORD)resource_tbl_len;
+
+ //
+ // get the file names in the reource table of OS2LDR
+ //
+ for ( Index = 0; *(String = LoaderWordTable[ FIRST_FILE_NAME + Index]) != 0; Index++) {
+
+ String = RplGetLastPathComponent( String); // convert file path to file name
+
+ //
+ // Verify that resource file is a part of a boot block.
+ //
+ for ( FileIndex = 0;
+ FileIndex < flist_tbl_len
+ && _wcsicmp( String, flist_tbl[ FileIndex].FileName);
+ FileIndex++) {
+ NOTHING;
+ }
+ if ( FileIndex == flist_tbl_len) {
+ //
+ // resource file is not a part of a boot block
+ //
+ RplDump( ++RG_Assert, ( "BbcFile=%ws, Line=%ws",
+ pWorkerData->BbcFile, LineTable[ LoaderLineIndex]));
+ pWorkerData->EventStrings[ 0] = pWorkerData->WkstaName;
+ pWorkerData->EventStrings[ 1] = pWorkerData->BbcFile;
+ pWorkerData->EventStrings[ 2] = LineTable[ LoaderLineIndex];
+ pWorkerData->EventId = NELOG_Invalid_Config_Line;
+ return( FALSE);
+ }
+ //
+ // initialize the next item in resource table
+ //
+ resource_tbl->file_tbl[ Index].pos_in_paras = (WORD)( flist_tbl[ FileIndex].FileData.file_addr >> 4);
+ resource_tbl->file_tbl[ Index].file_len = flist_tbl[ FileIndex].FileData.file_len;
+ }
+ resource_tbl->file_tbl[ Index].pos_in_paras = 0;
+ resource_tbl->file_tbl[ Index].file_len = 0L;
+
+ //
+ // Clean up.
+ //
+ RplMemFree( pWorkerData->MemoryHandle, LoaderWordTable);
+ RplMemFree( pWorkerData->MemoryHandle, LoaderBuffer);
+
+ //
+ // Get the minimum size of workstation specific data
+ //
+ pWorkerData->min_wksta_buf += flist_tbl_size + resource_tbl_size;
+
+ pWorkerData->file_block_base = fblock_base << 4;
+ pWorkerData->boot_block_base = bblock_base << 4;
+ pWorkerData->flist_tbl = flist_tbl;
+ pWorkerData->flist_tbl_len = flist_tbl_len;
+ pWorkerData->resource_tbl = resource_tbl;
+ pWorkerData->resource_tbl_size = resource_tbl_size;
+
+ //
+ // Check header of RPLBOOT.SYS.
+ //
+ if( !RplCheckBootHeader( pWorkerData, flist_tbl->path_name, &PatchOffset)) {
+ return( FALSE);
+ }
+
+ //
+ // RPLBOOT.SYS has a good header, see if it requires patching.
+ //
+ if ( PatchOffset == NO_PATCH_OFFSET) {
+ pWorkerData->MakePatch = FALSE; // RPLBOOT.SYS requires no patching.
+ } else {
+ pWorkerData->MakePatch = TRUE;
+ pWorkerData->PatchOffset = flist_tbl[ pWorkerData->rplboot_i].FileData.file_addr + PatchOffset;
+ flist_tbl[ pWorkerData->rplboot_i].FileData.chk_sum = NO_CHKSUM_USED;
+ }
+
+ RplDump( RG_DebugLevel & RPL_DEBUG_FLOW,( "--BbcFile(0x%x)", pWorkerData));
+ return( TRUE);
+
+} // RplBbcFile()
+
+
+
diff --git a/private/net/svcdlls/rpl/server/bbcfile.h b/private/net/svcdlls/rpl/server/bbcfile.h
new file mode 100644
index 000000000..2c3486709
--- /dev/null
+++ b/private/net/svcdlls/rpl/server/bbcfile.h
@@ -0,0 +1,28 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ bbcfile.h
+
+Abstract:
+
+ Exports from bbcfile.c
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 19 - November - 1993
+
+Environment:
+
+ User mode
+
+Revision History :
+
+--*/
+
+BOOL RplBbcFile( IN OUT PRPL_WORKER_DATA pWorkerData);
+
+
+
diff --git a/private/net/svcdlls/rpl/server/boot.c b/private/net/svcdlls/rpl/server/boot.c
new file mode 100644
index 000000000..3c8bed16b
--- /dev/null
+++ b/private/net/svcdlls/rpl/server/boot.c
@@ -0,0 +1,832 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ boot.c
+
+Abstract:
+
+ This module contains RPL boot apis: - internal routines only.
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 10 - November - 1993
+
+Revision History:
+
+ 10-Nov-1993 vladimv
+ Created
+
+--*/
+
+#include "local.h"
+#include "rpldb.h"
+#include "db.h"
+#include "dblib.h"
+#include "config.h"
+#include "profile.h"
+#include "wksta.h"
+#define RPLBOOT_ALLOCATE
+#include "boot.h"
+#undef RPLBOOT_ALLOCATE
+#include "database.h"
+
+
+
+DWORD BootGetField(
+ IN PRPL_SESSION pSession,
+ IN DWORD FieldIndex,
+ OUT LPVOID * pData,
+ IN OUT LPINT pSpaceLeft
+ )
+{
+ BYTE LocalBuffer[ 300];
+ PBYTE Buffer;
+ DWORD DataSize;
+ DWORD BufferSize;
+ JET_ERR JetError;
+
+ switch( FieldIndex) {
+ case BOOT_WindowSize:
+ case BOOT_Flags:
+ case BOOT_VendorId:
+ Buffer = (PBYTE)pData;
+ BufferSize = sizeof( DWORD);
+ break;
+ default:
+ Buffer = LocalBuffer;
+ BufferSize = sizeof( LocalBuffer);
+ break;
+ }
+ JetError = JetRetrieveColumn( pSession->SesId, pSession->BootTableId,
+ BootTable[ FieldIndex].ColumnId, Buffer,
+ BufferSize, &DataSize, 0, NULL);
+ if ( JetError < 0) {
+ RplDump( ++RG_Assert, ("JetError=%d", JetError));
+ return( NERR_RplBootInfoCorrupted);
+ }
+ if ( Buffer != LocalBuffer) {
+ if ( BufferSize == DataSize) {
+ return( NO_ERROR);
+ } else {
+ RplDump( ++RG_Assert, ("Bad DataSize=0x%x", DataSize));
+ return( NERR_RplBootInfoCorrupted);
+ }
+ }
+ //
+ // We have done with fixed data. From here on we deal with unicode
+ // strings only.
+ //
+ if ( DataSize > sizeof( LocalBuffer)) {
+ RplDump( ++RG_Assert, ( "Too big DataSize=0x%x", DataSize));
+ return( NERR_RplBootInfoCorrupted);
+ }
+ if ( DataSize == 0) {
+ if ( JetError != JET_wrnColumnNull) {
+ RplDump( ++RG_Assert, ( "JetError=%d", JetError));
+ return( NERR_RplBootInfoCorrupted);
+ } else {
+ *pData = NULL; // so RPC rpcrt4!_tree_size_ndr() does not bomb here
+ return( NO_ERROR);
+ }
+ }
+ if ( DataSize & 1 != 0 || wcslen((PWCHAR)LocalBuffer) + 1 != DataSize/2) {
+ RplDump( ++RG_Assert, ("LocalBuffer=0x%x, DataSize=0x%x", LocalBuffer, DataSize));
+ return( NERR_RplBootInfoCorrupted);
+ }
+ *pData = MIDL_user_allocate( DataSize);
+ if ( *pData == NULL) {
+ RplDump( ++RG_Assert, ( "Error=%d", GetLastError()));
+ RPL_RETURN( ERROR_NOT_ENOUGH_MEMORY);
+ }
+ memcpy( *pData, LocalBuffer, DataSize);
+ *pSpaceLeft -= DataSize;
+ return( NO_ERROR);
+}
+
+
+BOOL BootResumeFirst(
+ IN PRPL_SESSION pSession,
+ IN LPDWORD pResumeHandle,
+ OUT PBOOL pTableEnd
+ )
+/*++
+ Set currency to the first boot record following the boot record
+ described by the resume handle.
+--*/
+{
+ BYTE ResumeValue[ RPL_MAX_VENDOR_NAME_SIZE + RPL_MAX_WKSTA_NAME_SIZE];
+ PWCHAR BootName;
+ DWORD BootNameSize;
+ DWORD ResumeSize;
+ JET_ERR JetError;
+
+ *pTableEnd = FALSE;
+
+ CallB( JetSetCurrentIndex( pSession->SesId, pSession->BootTableId, BOOT_INDEX_VendorIdBootName));
+ //
+ // The call to move to the beginning of the table is not redundant.
+ // E.g. JET_errNoCurrentRecord will be returned in case of empty table.
+ //
+ JetError = JetMove( pSession->SesId, pSession->BootTableId, JET_MoveFirst, 0);
+ if ( JetError < 0) {
+ if ( JetError == JET_errRecordNotFound
+ || JetError == JET_errNoCurrentRecord) {
+ *pTableEnd = TRUE;
+ return( TRUE);
+ } else {
+ RplDump( ++RG_Assert, ("JetError=%d", JetError));
+ return( FALSE);
+ }
+ }
+ if ( !(ARGUMENT_PRESENT( pResumeHandle)) || *pResumeHandle == 0) {
+ return( TRUE); // not resuming, all done
+ }
+ ResumeSize = sizeof( ResumeValue);
+ if ( !ResumeKeyGet( pSession, *pResumeHandle, ResumeValue, &ResumeSize)) {
+ return( FALSE);
+ }
+ CallB( JetMakeKey( pSession->SesId, pSession->BootTableId, ResumeValue, sizeof( DWORD), JET_bitNewKey));
+ BootName = (PWCHAR)( ResumeValue + sizeof(DWORD));
+ BootNameSize = (DWORD)( ResumeSize - sizeof(DWORD));
+ if ( BootNameSize != (wcslen( BootName) + 1) * sizeof( WCHAR)) {
+ RplDump( ++RG_Assert, ("ResumeValue=0x%x, ResumeSize=0x%x", ResumeValue, ResumeSize));
+ return( FALSE);
+ }
+ CallB( JetMakeKey( pSession->SesId, pSession->BootTableId, BootName, BootNameSize, 0));
+ CallB( JetSeek( pSession->SesId, pSession->BootTableId, JET_bitSeekGT));
+ return( TRUE);
+}
+
+
+VOID BootResumeSave(
+ IN PRPL_SESSION pSession,
+ IN DWORD ServerHandle,
+ IN DWORD VendorId,
+ IN PWCHAR BootName,
+ IN PDWORD pResumeHandle
+ )
+/*++
+ This all other other functions that save resume keys is void, because there
+ is no documented way of returning error cannot resume to the client.
+--*/
+{
+ BYTE ResumeBuffer[ 30 * sizeof(WCHAR)];
+ DWORD BootSize;
+
+ memcpy( ResumeBuffer, &VendorId, sizeof( VendorId));
+ BootSize = ( wcslen( BootName) + 1) * sizeof(WCHAR);
+ memcpy( ResumeBuffer + sizeof(VendorId), BootName, BootSize);
+ (VOID)ResumeKeySet( pSession, (DWORD)ServerHandle, ResumeBuffer, BootSize+sizeof(VendorId), pResumeHandle);
+}
+
+
+DWORD BootFilterFind(
+ IN PRPL_SESSION pSession,
+ IN OUT PRPL_FILTER pFilter,
+ IN OUT PBOOL pTableEnd
+ )
+/*++
+ Returns name of the the first/next BOOT structure supporting a given
+ VendorId.
+ The "first" BOOT does not have to be absolutely first, but first behind
+ the BOOT specified by RPL_FILTER.
+ In case such BOOT structure cannot be found, returns some kind of error.
+
+Arguments:
+
+ pFilter - pointer to RPL_FILTER structure with the following fields
+ FindFirst : find first (TRUE) or find next (FALSE)
+ VendorId : vendor id to be suppored (input only)
+ BootName : buffer large enough to hold the longest
+ BootName string. When input value of
+ BootNameSize is non-zero, this contains the
+ previous value of BootName string.
+ output value is NOT valid if error or end of table
+ BootNameSize : at input this contains the size of the previous
+ BootName string. If this size is zero/non-zero,
+ it means we are looking for the first/next
+ BOOT structure.
+ at output this cotains the size of BootName string
+ output value is NOT valid if error or end of table
+
+ pTableEnd - pointer to boolean (input value is FALSE) which will be set
+ to TRUE if we reach end of boot table
+-**/
+{
+ DWORD CheckVendorId;
+ DWORD DataSize;
+ JET_ERR JetError;
+
+ if ( pFilter->FindFirst == FALSE) {
+ JetError = JetMove( pSession->SesId, pSession->BootTableId, JET_MoveNext, 0);
+ if ( JetError != JET_errSuccess) {
+ if ( JetError == JET_errNoCurrentRecord) {
+ RplDump( RG_DebugLevel & RPL_DEBUG_BOOT, ("JetError=%d", JetError));
+ } else {
+ RplDump( ++RG_Assert, ("JetError=%d", JetError));
+ }
+ goto cleanup;
+ }
+ } else {
+ pFilter->FindFirst = FALSE;
+ CallM( JetSetCurrentIndex( pSession->SesId, pSession->BootTableId, BOOT_INDEX_VendorIdBootName));
+ CallM( JetMakeKey( pSession->SesId, pSession->BootTableId, &pFilter->VendorId, sizeof( pFilter->VendorId), JET_bitNewKey));
+ if ( pFilter->BootNameSize != 0) {
+ //
+ // We are continuing the search from the previous BOOT structure.
+ //
+ CallM( JetMakeKey( pSession->SesId, pSession->BootTableId, pFilter->BootName, pFilter->BootNameSize, 0));
+ }
+ //
+ // In case of seek errors assume we have reached end of table.
+ //
+ JetError = JetSeek( pSession->SesId, pSession->BootTableId, JET_bitSeekGT);
+ if ( JetError != JET_errSuccess) {
+ RplDump( RG_DebugLevel & RPL_DEBUG_BOOT, ("BootFilterFind: Seek(0x%x) => JetError=%d",
+ pFilter->VendorId, JetError));
+ goto cleanup;
+ }
+ //
+ // Verify that element we seeked to has the proper vendor id.
+ //
+ CallM( JetMakeKey( pSession->SesId, pSession->BootTableId, &pFilter->VendorId, sizeof( pFilter->VendorId), JET_bitNewKey));
+ JetError = JetSetIndexRange( pSession->SesId, pSession->BootTableId,
+ JET_bitRangeInclusive | JET_bitRangeUpperLimit);
+ if ( JetError != JET_errSuccess) {
+ RplDump( RG_DebugLevel & RPL_DEBUG_BOOT, ("BootFilterFind: SetIndexRange(0x%x) => JetError=%d",
+ pFilter->VendorId, JetError));
+ goto cleanup;
+ }
+ }
+#ifdef RPL_DEBUG
+ CallM( JetRetrieveColumn( pSession->SesId, pSession->BootTableId,
+ BootTable[ BOOT_VendorId].ColumnId, &CheckVendorId,
+ sizeof( CheckVendorId), &DataSize, 0, NULL));
+ if ( CheckVendorId != pFilter->VendorId) {
+ RplDump( ++RG_Assert, ( "CheckVendorId=0x%x"));
+ }
+#endif // RPL_DEBUG
+ CallM( JetRetrieveColumn( pSession->SesId, pSession->BootTableId,
+ BootTable[ BOOT_BootName].ColumnId, pFilter->BootName,
+ RPL_MAX_BOOT_NAME_SIZE, &pFilter->BootNameSize, 0, NULL));
+cleanup:
+ if ( JetError < 0) {
+ *pTableEnd = TRUE; // pretend it is end of table
+ }
+ return( NO_ERROR);
+}
+
+
+DWORD BootSetInfo(
+ IN PRPL_SESSION pSession,
+ IN DWORD Level,
+ IN LPVOID Buffer,
+ OUT LPDWORD pErrorParameter
+ )
+{
+ LPRPL_BOOT_INFO_2 Info = Buffer;
+ switch( Level) {
+ case 2:
+ if ( Info->WindowSize != -1) {
+ *pErrorParameter = BOOT_WindowSize;
+ CallM( JetSetColumn( pSession->SesId, pSession->BootTableId,
+ BootTable[ BOOT_WindowSize].ColumnId,
+ &Info->WindowSize, sizeof( Info->WindowSize), 0, NULL));
+ }
+ if ( Info->BbcFile != NULL) {
+ *pErrorParameter = BOOT_BbcFile;
+ CallM( JetSetColumn( pSession->SesId, pSession->BootTableId,
+ BootTable[ BOOT_BbcFile].ColumnId,
+ Info->BbcFile,
+ ( wcslen( Info->BbcFile) + 1) * sizeof(WCHAR),
+ 0, NULL));
+ }
+ NOTHING; // fall through
+ case 1:
+ if ( Info->VendorName != NULL) {
+ DWORD VendorId;
+ *pErrorParameter = BOOT_VendorName;
+ CallM( JetSetColumn( pSession->SesId, pSession->BootTableId,
+ BootTable[ BOOT_VendorName].ColumnId,
+ Info->VendorName,
+ ( wcslen( Info->VendorName) + 1) * sizeof(WCHAR),
+ 0, NULL));
+ VendorId = wcstoul( Info->VendorName, NULL, 16);
+ CallM( JetSetColumn( pSession->SesId, pSession->BootTableId,
+ BootTable[ BOOT_VendorId].ColumnId,
+ &VendorId, sizeof( VendorId), 0, NULL));
+ }
+ if ( Info->Flags != 0) {
+ *pErrorParameter = BOOT_Flags;
+ CallM( JetSetColumn( pSession->SesId, pSession->BootTableId,
+ BootTable[ BOOT_Flags].ColumnId,
+ &Info->Flags, sizeof( Info->Flags), 0, NULL));
+ }
+ NOTHING; // fall through
+ case 0:
+ if ( Info->BootComment != NULL) {
+ *pErrorParameter = BOOT_BootComment;
+ CallM( JetSetColumn( pSession->SesId, pSession->BootTableId,
+ BootTable[ BOOT_BootComment].ColumnId,
+ Info->BootComment,
+ ( wcslen( Info->BootComment) + 1) * sizeof(WCHAR),
+ 0, NULL));
+ }
+ if ( Info->BootName != NULL) {
+ *pErrorParameter = BOOT_BootName;
+ CallM( JetSetColumn( pSession->SesId, pSession->BootTableId,
+ BootTable[ BOOT_BootName].ColumnId,
+ Info->BootName,
+ ( wcslen( Info->BootName) + 1) * sizeof(WCHAR),
+ 0, NULL));
+ }
+ break;
+ }
+ return( NO_ERROR);
+}
+
+
+DWORD BootGetInfo(
+ IN PRPL_SESSION pSession,
+ OUT PDWORD pVendorId,
+ IN DWORD Level,
+ OUT LPVOID Buffer,
+ IN OUT PINT pSpaceLeft
+ )
+{
+ DWORD Error;
+ DWORD WhoCares;
+ LPRPL_BOOT_INFO_2 Info = Buffer;
+
+ switch( Level) {
+ case 2:
+ Error = BootGetField( pSession, BOOT_WindowSize, (LPVOID *)&Info->WindowSize, pSpaceLeft);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ Error = BootGetField( pSession, BOOT_BbcFile, &Info->BbcFile, pSpaceLeft);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ NOTHING; // fall through
+ case 1:
+ Error = BootGetField( pSession, BOOT_Flags, (LPVOID *)&Info->Flags, pSpaceLeft);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ Error = BootGetField( pSession, BOOT_VendorName, &Info->VendorName, pSpaceLeft);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ NOTHING; // fall through
+ case 0:
+ Error = BootGetField( pSession, BOOT_BootComment, &Info->BootComment, pSpaceLeft);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ Error = BootGetField( pSession, BOOT_BootName, &Info->BootName, pSpaceLeft);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ Error = BootGetField( pSession, BOOT_VendorId, (LPVOID *)pVendorId, &WhoCares);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ break;
+ default:
+ return( ERROR_INVALID_LEVEL);
+ break;
+ }
+ return( NO_ERROR);
+}
+
+
+
+VOID BootGetInfoCleanup(
+ IN DWORD Level,
+ IN OUT LPVOID Buffer
+ )
+{
+ LPRPL_BOOT_INFO_2 Info = Buffer;
+
+ switch( Level) {
+ case 2:
+ if ( Info->BbcFile != NULL) {
+ MIDL_user_free( Info->BbcFile);
+ }
+ NOTHING; // fall through
+ case 1:
+ if ( Info->VendorName != NULL) {
+ MIDL_user_free( Info->VendorName);
+ }
+ NOTHING; // fall through
+ case 0:
+ if ( Info->BootComment != NULL) {
+ MIDL_user_free( Info->BootComment);
+ }
+ if ( Info->BootName != NULL) {
+ MIDL_user_free( Info->BootName);
+ }
+ break;
+ }
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetrRplBootAdd(
+ IN RPL_HANDLE ServerHandle,
+ IN DWORD Level,
+ OUT LPRPL_BOOT_INFO_STRUCT BootInfoStruct,
+ OUT LPDWORD pErrorParameter OPTIONAL
+ )
+{
+ LPRPL_BOOT_INFO_2 Info;
+ LPVOID Buffer;
+ DWORD Error;
+ DWORD ErrorParameter;
+ DWORD VendorId;
+ PRPL_SESSION pSession = &RG_ApiSession;
+
+ ErrorParameter = INVALID_ERROR_PARAMETER;
+ Buffer = Info = BootInfoStruct->BootInfo2;
+
+ switch( Level) {
+ case 2:
+ if ( Info->BbcFile == NULL || RPL_STRING_TOO_LONG( Info->BootComment)) {
+ ErrorParameter = BOOT_BbcFile;
+ break;
+ }
+ if ( !ValidHexName( Info->VendorName, RPL_VENDOR_NAME_LENGTH, TRUE)) {
+ ErrorParameter = BOOT_VendorName;
+ break;
+ }
+ _wcsupr( Info->VendorName);
+ VendorId = wcstoul( Info->VendorName, NULL, 16);
+ switch( Info->Flags) {
+ case BOOT_FLAGS_FINAL_ACKNOWLEDGMENT_TRUE:
+ case BOOT_FLAGS_FINAL_ACKNOWLEDGMENT_FALSE:
+ break;
+ default:
+ ErrorParameter = BOOT_Flags;
+ break;
+ }
+ if ( ErrorParameter != INVALID_ERROR_PARAMETER) {
+ break;
+ }
+ if ( RPL_STRING_TOO_LONG( Info->BootComment)) {
+ ErrorParameter = BOOT_BootComment;
+ break;
+ }
+ if ( !ValidName( Info->BootName, RPL_MAX_BOOT_NAME_LENGTH, TRUE)) {
+ ErrorParameter = BOOT_BootName;
+ break;
+ }
+ _wcsupr( Info->BootName);
+ break;
+ default:
+ return( ERROR_INVALID_LEVEL);
+ break;
+ }
+
+ if ( ErrorParameter != INVALID_ERROR_PARAMETER) {
+ if ( ARGUMENT_PRESENT( pErrorParameter)) {
+ *pErrorParameter = ErrorParameter;
+ }
+ return( ERROR_INVALID_PARAMETER);
+ }
+
+ EnterCriticalSection( &RG_ProtectDatabase);
+ Call( JetBeginTransaction( pSession->SesId));
+
+ //
+ // Verify that BootName + VendorId is available in the database.
+ //
+ if ( BootFind( pSession, Info->BootName, VendorId)) {
+ Error = NERR_RplBootNameUnavailable;
+ goto cleanup;
+ }
+
+ CallJ( JetPrepareUpdate( pSession->SesId, pSession->BootTableId, JET_prepInsert));
+
+ Error = BootSetInfo( pSession, Level, Buffer, &ErrorParameter);
+ if ( Error == ERROR_SUCCESS) {
+ ErrorParameter = 0;
+ CallJ( JetUpdate( pSession->SesId, pSession->BootTableId, NULL, 0, NULL));
+ }
+
+cleanup:
+ if ( Error == NO_ERROR) {
+ Call( JetCommitTransaction( pSession->SesId, JET_bitCommitFlush));
+ } else {
+ Call( JetRollback( pSession->SesId, JET_bitRollbackAll));
+ }
+ LeaveCriticalSection( &RG_ProtectDatabase);
+
+ if ( Error != ERROR_SUCCESS) {
+ if ( ARGUMENT_PRESENT( pErrorParameter)) {
+ *pErrorParameter = ErrorParameter;
+ }
+ }
+ return( Error);
+}
+
+
+
+BOOL BootFind(
+ IN PRPL_SESSION pSession,
+ IN PWCHAR BootName,
+ IN DWORD VendorId
+ )
+{
+ JET_ERR JetError;
+ CallB( JetSetCurrentIndex( pSession->SesId, pSession->BootTableId, BOOT_INDEX_VendorIdBootName));
+ CallB( JetMakeKey( pSession->SesId, pSession->BootTableId, &VendorId, sizeof(VendorId), JET_bitNewKey));
+ CallB( JetMakeKey( pSession->SesId, pSession->BootTableId, BootName, ( wcslen( BootName) + 1) * sizeof(WCHAR), 0));
+ JetError = JetSeek( pSession->SesId, pSession->BootTableId, JET_bitSeekEQ);
+ if ( JetError < 0) {
+#ifdef RPL_DEBUG
+ //
+ // JET_errNoCurrentRecord will be returned in case of empty table.
+ //
+ if ( JetError != JET_errRecordNotFound
+ && JetError != JET_errNoCurrentRecord) {
+ RplDump( ++RG_Assert, ("JetError=%d", JetError));
+ }
+#endif
+ return( FALSE);
+ }
+ return( TRUE);
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetrRplBootDel(
+ IN RPL_HANDLE ServerHandle,
+ IN LPWSTR BootName,
+ IN LPWSTR VendorName
+ )
+/*++
+ If VendorName is NULL we should delete all records with input BootName.
+ But for now we insist on valid input for VendorName.
+--*/
+{
+ DWORD Error;
+ DWORD VendorId;
+ PRPL_SESSION pSession = &RG_ApiSession;
+
+ if ( !ValidName( BootName, RPL_MAX_BOOT_NAME_LENGTH, TRUE)) {
+ return( ERROR_INVALID_PARAMETER);
+ }
+ _wcsupr( BootName);
+ if ( !ValidHexName( VendorName, RPL_VENDOR_NAME_LENGTH, TRUE)) {
+ return( ERROR_INVALID_PARAMETER);
+ }
+ VendorId = wcstoul( VendorName, NULL, 16);
+
+ EnterCriticalSection( &RG_ProtectDatabase);
+ Call( JetBeginTransaction( pSession->SesId));
+
+ if ( RplFindByField( pSession, CONFIG_TABLE_TAG,
+ CONFIG_INDEX_BootNameConfigName, BootName)) {
+ //
+ // We found a CONFIG record which uses this BOOT.
+ //
+ Error = NERR_RplBootInUse;
+ goto cleanup;
+ }
+ if ( RplFindByField( pSession, PROFILE_TABLE_TAG,
+ PROFILE_INDEX_BootNameProfileName, BootName)) {
+ //
+ // We found a PROFILE record which uses this BOOT.
+ //
+ Error = NERR_RplBootInUse;
+ goto cleanup;
+ }
+ if ( RplFindByField( pSession, WKSTA_TABLE_TAG,
+ WKSTA_INDEX_BootNameWkstaName, BootName)) {
+ //
+ // We found a WKSTA record which uses this BOOT.
+ //
+ Error = NERR_RplBootInUse;
+ goto cleanup;
+ }
+ if ( !BootFind( pSession, BootName, VendorId)) {
+ Error = NERR_RplBootNotFound;
+ goto cleanup;
+ }
+ CallJ( JetDelete( pSession->SesId, pSession->BootTableId));
+ Error = NO_ERROR;
+
+cleanup:
+ if ( Error == NO_ERROR) {
+ Call( JetCommitTransaction( pSession->SesId, JET_bitCommitFlush));
+ } else {
+ Call( JetRollback( pSession->SesId, JET_bitRollbackAll));
+ }
+ LeaveCriticalSection( &RG_ProtectDatabase);
+ return( Error);
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetrRplBootEnum(
+ IN RPL_RPC_HANDLE ServerHandle,
+ IN OUT LPRPL_BOOT_ENUM BootEnum,
+ IN DWORD PrefMaxLength,
+ OUT LPDWORD TotalEntries,
+ IN OUT LPDWORD pResumeHandle OPTIONAL
+ )
+/*++
+
+Routine Description:
+
+Arguments:
+
+ pServerHandle - ptr to RPL_HANDLE
+
+ InfoStruct - Pointer to a structure that contains the information that
+ RPC needs about the returned data. This structure contains the
+ following information:
+ Level - The desired information level - indicates how to
+ interpret the structure of the returned buffer.
+ EntriesRead - Indicates how many elements are returned in the
+ array of structures that are returned.
+ BufferPointer - Location for the pointer to the array of
+ structures that are being returned.
+
+ PrefMaxLen - Indicates a maximum size limit that the caller will allow
+ for (the return buffer.
+
+ TotalEntries - Pointer to a value that upon return indicates the total
+ number of entries in the "active" database.
+
+ pResumeHandle - Inidcates where to restart the enumeration. This is an
+ optional parameter and can be NULL.
+
+Return Value:
+ NO_ERROR if success.
+
+--*/
+{
+ LPBYTE Buffer;
+ DWORD TypicalSize;
+ DWORD CoreSize;
+ DWORD Error;
+ INT SpaceLeft;
+ DWORD ArrayLength;
+ DWORD EntriesRead;
+ JET_ERR JetError;
+ BOOL InfoError;
+ BOOL TableEnd;
+ DWORD VendorId;
+ PRPL_SESSION pSession = &RG_ApiSession;
+
+ switch( BootEnum->Level) {
+ case 2:
+ TypicalSize = CoreSize = sizeof( RPL_BOOT_INFO_2);
+ TypicalSize += 40 * sizeof( WCHAR); // typical size of BbcFile
+ NOTHING; // fall through
+ case 1:
+ if ( BootEnum->Level == 1) {
+ TypicalSize = CoreSize = sizeof( RPL_BOOT_INFO_1);
+ }
+ TypicalSize += 8 * sizeof( WCHAR); // typical size of VendorName
+ NOTHING; // fall through
+ case 0:
+ if ( BootEnum->Level == 0) {
+ TypicalSize = CoreSize = sizeof( RPL_BOOT_INFO_0);
+ }
+ TypicalSize += 20 * sizeof( WCHAR); // typical size of BootComment
+ TypicalSize += 8 * sizeof( WCHAR); // typical size of BootName
+ break;
+ default:
+ return( ERROR_INVALID_LEVEL);
+ break;
+ }
+
+ if ( PrefMaxLength == -1) {
+ //
+ // If the caller has not specified a size, calculate a size
+ // that will hold the entire enumeration.
+ //
+ SpaceLeft = DEFAULT_BUFFER_SIZE;
+ } else {
+ SpaceLeft = PrefMaxLength;
+ }
+
+ //
+ // Buffer space is shared by the array and by strings pointed at
+ // by the elements in this array. We need to decide up front how much
+ // space is allocated for array and how much for the strings.
+ //
+ ArrayLength = SpaceLeft / TypicalSize;
+ if ( ArrayLength == 0) {
+ ArrayLength = 1; // try to return at least one element
+ }
+
+ //
+ // Note that MIDL_user_allocate() returns memory which is NOT initialized
+ // to zero. Since we do NOT use allocate all nodes, this means that all
+ // fields, especially pointers, in array elements must be properly set.
+ //
+ Buffer = MIDL_user_allocate( ArrayLength * CoreSize);
+ if ( Buffer == NULL) {
+ return( ERROR_NOT_ENOUGH_MEMORY);
+ }
+ RplDump( RG_DebugLevel & RPL_DEBUG_BOOT, (
+ "BootEnum: Buffer=0x%x, ArrayLength=0x%x", Buffer, ArrayLength));
+
+ BootEnum->BootInfo.Level0->Buffer = (LPRPL_BOOT_INFO_0)Buffer;
+
+ EntriesRead = 0;
+ InfoError = FALSE;
+ Error = NO_ERROR;
+
+ EnterCriticalSection( &RG_ProtectDatabase);
+ Call( JetBeginTransaction( pSession->SesId));
+
+ if ( !BootResumeFirst( pSession, pResumeHandle, &TableEnd)) {
+ Error = NERR_RplCannotEnum;
+ goto cleanup;
+ }
+ if ( TableEnd == TRUE) {
+ goto cleanup;
+ }
+ for ( ; ; ) {
+ memset( Buffer, 0, CoreSize); // for cleanup to work properly
+ Error = BootGetInfo( pSession, &VendorId, BootEnum->Level, Buffer, &SpaceLeft);
+ if ( Error != NO_ERROR) {
+ InfoError = TRUE; // clean things up without holding crit sec
+ break;
+ }
+ EntriesRead++;
+ Buffer += CoreSize;
+ SpaceLeft -= CoreSize;
+ JetError = JetMove( pSession->SesId, pSession->BootTableId, JET_MoveNext, 0);
+ if ( JetError != JET_errSuccess) {
+ break; // assume end of table
+ }
+ if ( SpaceLeft <= 0) {
+ Error = ERROR_MORE_DATA;
+ break;
+ }
+ if ( EntriesRead >= ArrayLength) {
+ //
+ // We have space available but allocated array is not big enough.
+ // This should NOT happen often as our intent (see above) is to
+ // overestimate array length. When it happens we can still try
+ // to reallocate array to a larger size here. This is not done
+ // for now (too cumbersome) & we just stop the enumeration.
+ //
+ Error = ERROR_MORE_DATA;
+ break;
+ }
+ }
+cleanup:
+ Call( JetCommitTransaction( pSession->SesId, 0));
+ LeaveCriticalSection( &RG_ProtectDatabase);
+ if ( InfoError == TRUE) {
+ BootGetInfoCleanup( BootEnum->Level, Buffer);
+ }
+ if ( Error == NO_ERROR) {
+ *TotalEntries = EntriesRead;
+ } else if ( Error == ERROR_MORE_DATA) {
+ *TotalEntries = EntriesRead * 2; // we cheat here
+ } else {
+ //
+ // Cleanup in case of "bad" errors.
+ //
+ while ( EntriesRead > 0) {
+ EntriesRead--;
+ Buffer -= CoreSize;
+ BootGetInfoCleanup( BootEnum->Level, Buffer);
+ }
+ MIDL_user_free( Buffer);
+ }
+
+ RplDump( RG_DebugLevel & RPL_DEBUG_BOOT, ("BootEnum: EntriesRead = 0x%x", EntriesRead));
+
+ BootEnum->BootInfo.Level0->EntriesRead = EntriesRead;
+ if ( EntriesRead == 0) {
+ BootEnum->BootInfo.Level0->Buffer = NULL;
+ }
+
+ if ( ARGUMENT_PRESENT( pResumeHandle)) {
+ if ( Error == ERROR_MORE_DATA && EntriesRead > 0) {
+ EnterCriticalSection( &RG_ProtectDatabase);
+ Call( JetBeginTransaction( pSession->SesId));
+ BootResumeSave( pSession, (DWORD)ServerHandle, VendorId,
+ ((LPRPL_BOOT_INFO_0)(Buffer-CoreSize))->BootName,
+ pResumeHandle);
+ Call( JetCommitTransaction( pSession->SesId, JET_bitCommitFlush));
+ LeaveCriticalSection( &RG_ProtectDatabase);
+ } else {
+ *pResumeHandle = 0; // resume from beginning
+ }
+ }
+ return( Error);
+}
+
diff --git a/private/net/svcdlls/rpl/server/config.c b/private/net/svcdlls/rpl/server/config.c
new file mode 100644
index 000000000..698cb962e
--- /dev/null
+++ b/private/net/svcdlls/rpl/server/config.c
@@ -0,0 +1,711 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ config.c
+
+Abstract:
+
+ This module contains RPL config apis: ConfigEnum.
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 05 - November - 1993
+
+Revision History:
+
+ 05-Nov-1993 vladimv
+ Created
+
+--*/
+
+#include "local.h"
+#include "rpldb.h"
+#include "db.h"
+#include "dblib.h"
+#include "boot.h"
+#include "profile.h"
+#define RPLCONFIG_ALLOCATE
+#include "config.h"
+#undef RPLCONFIG_ALLOCATE
+#include "setup.h" // IsConfigEnabled()
+
+
+
+DWORD ConfigSetInfo(
+ IN PRPL_SESSION pSession,
+ IN DWORD Level,
+ IN LPVOID Buffer,
+ OUT LPDWORD pErrorParameter
+ )
+{
+ LPRPL_CONFIG_INFO_2 Info = Buffer;
+ switch( Level) {
+ case 2:
+ if ( Info->FitPersonal != NULL) {
+ *pErrorParameter = CONFIG_FitPersonal;
+ CallM( JetSetColumn( pSession->SesId, pSession->ConfigTableId,
+ ConfigTable[ CONFIG_FitPersonal].ColumnId,
+ Info->FitPersonal,
+ ( wcslen( Info->FitPersonal) + 1) * sizeof(WCHAR),
+ 0, NULL));
+ }
+ if ( Info->FitShared != NULL) {
+ *pErrorParameter = CONFIG_FitShared;
+ CallM( JetSetColumn( pSession->SesId, pSession->ConfigTableId,
+ ConfigTable[ CONFIG_FitShared].ColumnId,
+ Info->FitShared,
+ ( wcslen( Info->FitShared) + 1) * sizeof(WCHAR),
+ 0, NULL));
+ }
+ if ( Info->DirName4 != NULL) {
+ *pErrorParameter = CONFIG_DirName4;
+ CallM( JetSetColumn( pSession->SesId, pSession->ConfigTableId,
+ ConfigTable[ CONFIG_DirName4].ColumnId,
+ Info->DirName4,
+ ( wcslen( Info->DirName4) + 1) * sizeof(WCHAR),
+ 0, NULL));
+ }
+ if ( Info->DirName3 != NULL) {
+ *pErrorParameter = CONFIG_DirName3;
+ CallM( JetSetColumn( pSession->SesId, pSession->ConfigTableId,
+ ConfigTable[ CONFIG_DirName3].ColumnId,
+ Info->DirName3,
+ ( wcslen( Info->DirName3) + 1) * sizeof(WCHAR),
+ 0, NULL));
+ }
+ if ( Info->DirName2 != NULL) {
+ *pErrorParameter = CONFIG_DirName2;
+ CallM( JetSetColumn( pSession->SesId, pSession->ConfigTableId,
+ ConfigTable[ CONFIG_DirName2].ColumnId,
+ Info->DirName2,
+ ( wcslen( Info->DirName2) + 1) * sizeof(WCHAR),
+ 0, NULL));
+ }
+ if ( Info->DirName != NULL) {
+ *pErrorParameter = CONFIG_DirName;
+ CallM( JetSetColumn( pSession->SesId, pSession->ConfigTableId,
+ ConfigTable[ CONFIG_DirName].ColumnId,
+ Info->DirName,
+ ( wcslen( Info->DirName) + 1) * sizeof(WCHAR),
+ 0, NULL));
+ }
+ if ( Info->BootName != NULL) {
+ *pErrorParameter = CONFIG_BootName;
+ CallM( JetSetColumn( pSession->SesId, pSession->ConfigTableId,
+ ConfigTable[ CONFIG_BootName].ColumnId,
+ Info->BootName,
+ ( wcslen( Info->BootName) + 1) * sizeof(WCHAR),
+ 0, NULL));
+ }
+ NOTHING; // fall through
+ case 1:
+ if ( Info->Flags != 0) {
+ *pErrorParameter = CONFIG_Flags;
+ CallM( JetSetColumn( pSession->SesId, pSession->ConfigTableId,
+ ConfigTable[ CONFIG_Flags].ColumnId,
+ &Info->Flags, sizeof( Info->Flags), 0, NULL));
+ }
+ NOTHING; // fall through
+ case 0:
+ if ( Info->ConfigComment != NULL) {
+ *pErrorParameter = CONFIG_ConfigComment;
+ CallM( JetSetColumn( pSession->SesId, pSession->ConfigTableId,
+ ConfigTable[ CONFIG_ConfigComment].ColumnId,
+ Info->ConfigComment,
+ ( wcslen( Info->ConfigComment) + 1) * sizeof(WCHAR),
+ 0, NULL));
+ }
+ if ( Info->ConfigName != NULL) {
+ *pErrorParameter = CONFIG_ConfigName;
+ CallM( JetSetColumn( pSession->SesId, pSession->ConfigTableId,
+ ConfigTable[ CONFIG_ConfigName].ColumnId,
+ Info->ConfigName,
+ ( wcslen( Info->ConfigName) + 1) * sizeof(WCHAR),
+ 0, NULL));
+ }
+ break;
+ }
+ return( NO_ERROR);
+}
+
+
+DWORD ConfigGetField(
+ IN PRPL_SESSION pSession,
+ IN DWORD FieldIndex,
+ OUT LPVOID * pData,
+ IN OUT LPINT pSpaceLeft
+ )
+{
+ BYTE LocalBuffer[ 300];
+ PBYTE Buffer;
+ DWORD DataSize;
+ DWORD BufferSize;
+ JET_ERR JetError;
+
+ switch( FieldIndex) {
+ case CONFIG_Flags:
+ Buffer = (PBYTE)pData;
+ BufferSize = sizeof( DWORD);
+ break;
+ default:
+ Buffer = LocalBuffer;
+ BufferSize = sizeof( LocalBuffer);
+ break;
+ }
+ JetError = JetRetrieveColumn( pSession->SesId, pSession->ConfigTableId,
+ ConfigTable[ FieldIndex].ColumnId, Buffer,
+ BufferSize, &DataSize, 0, NULL);
+ if ( JetError < 0) {
+ RplDump( ++RG_Assert, ("JetError=%d", JetError));
+ return( NERR_RplConfigInfoCorrupted);
+ }
+ if ( Buffer != LocalBuffer) {
+ if ( BufferSize == DataSize) {
+ return( NO_ERROR);
+ } else {
+ RplDump( ++RG_Assert, ("Bad DataSize=0x%x", DataSize));
+ return( NERR_RplConfigInfoCorrupted);
+ }
+ }
+ //
+ // We have done with fixed data. From here on we deal with unicode
+ // strings only.
+ //
+ if ( DataSize > sizeof( LocalBuffer)) {
+ RplDump( ++RG_Assert, ( "Too big DataSize=0x%x", DataSize));
+ return( NERR_RplConfigInfoCorrupted);
+ }
+ if ( DataSize == 0) {
+ if ( JetError != JET_wrnColumnNull) {
+ RplDump( ++RG_Assert, ( "JetError=%d", JetError));
+ return( NERR_RplConfigInfoCorrupted);
+ } else {
+ *pData = NULL; // so RPC rpcrt4!_tree_size_ndr() does not bomb here
+ return( NO_ERROR);
+ }
+ }
+ if ( DataSize & 1 != 0 || wcslen((PWCHAR)LocalBuffer) + 1 != DataSize/2) {
+ RplDump( ++RG_Assert, ("LocalBuffer=0x%x, DataSize=0x%x", LocalBuffer, DataSize));
+ return( NERR_RplConfigInfoCorrupted);
+ }
+ *pData = MIDL_user_allocate( DataSize);
+ if ( *pData == NULL) {
+ RplDump( ++RG_Assert, ( "Error=%d", GetLastError()));
+ RPL_RETURN( ERROR_NOT_ENOUGH_MEMORY);
+ }
+ memcpy( *pData, LocalBuffer, DataSize);
+ *pSpaceLeft -= DataSize; // BUGBUG might become negative?
+ return( NO_ERROR);
+}
+
+
+DWORD ConfigGetInfo(
+ IN PRPL_SESSION pSession,
+ IN LPWSTR ConfigName,
+ IN DWORD Level,
+ OUT LPVOID Buffer,
+ IN OUT LPINT pSpaceLeft
+ )
+{
+ DWORD Error;
+ LPRPL_CONFIG_INFO_2 Info = Buffer;
+
+ switch( Level) {
+ case 2:
+ Error = ConfigGetField( pSession, CONFIG_FitPersonal, &Info->FitPersonal, pSpaceLeft);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ Error = ConfigGetField( pSession, CONFIG_FitShared, &Info->FitShared, pSpaceLeft);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ Error = ConfigGetField( pSession, CONFIG_DirName4, &Info->DirName4, pSpaceLeft);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ Error = ConfigGetField( pSession, CONFIG_DirName3, &Info->DirName3, pSpaceLeft);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ Error = ConfigGetField( pSession, CONFIG_DirName2, &Info->DirName2, pSpaceLeft);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ Error = ConfigGetField( pSession, CONFIG_DirName, &Info->DirName, pSpaceLeft);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ Error = ConfigGetField( pSession, CONFIG_BootName, &Info->BootName, pSpaceLeft);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ NOTHING; // fall through
+ case 1:
+ Error = ConfigGetField( pSession, CONFIG_Flags, (LPVOID *)&Info->Flags, pSpaceLeft);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ case 0:
+ Error = ConfigGetField( pSession, CONFIG_ConfigComment, &Info->ConfigComment, pSpaceLeft);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ if ( ConfigName == NULL) {
+ Error = ConfigGetField( pSession, CONFIG_ConfigName, &Info->ConfigName, pSpaceLeft);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ } else {
+ DWORD DataSize = (wcslen( ConfigName) + 1) * sizeof(WCHAR);
+ Info->ConfigName = MIDL_user_allocate( DataSize);
+ if ( Info->ConfigName == NULL) {
+ return( ERROR_NOT_ENOUGH_MEMORY);
+ }
+ RplDump( RG_DebugLevel & RPL_DEBUG_CONFIG, ( "ConfigName=0x%x", Info->ConfigName));
+ memcpy( Info->ConfigName, ConfigName, DataSize);
+ *pSpaceLeft -= DataSize;
+ }
+ break;
+ default:
+ return( ERROR_INVALID_LEVEL);
+ break;
+ }
+ return( NO_ERROR);
+}
+
+
+
+VOID ConfigGetInfoCleanup(
+ IN DWORD Level,
+ IN OUT LPVOID Buffer
+ )
+{
+ LPRPL_CONFIG_INFO_2 Info = Buffer;
+
+ switch( Level) {
+ case 2:
+ if ( Info->FitPersonal != NULL) {
+ MIDL_user_free( Info->FitPersonal);
+ }
+ if ( Info->FitShared != NULL) {
+ MIDL_user_free( Info->FitShared);
+ }
+ if ( Info->DirName4 != NULL) {
+ MIDL_user_free( Info->DirName4);
+ }
+ if ( Info->DirName3 != NULL) {
+ MIDL_user_free( Info->DirName3);
+ }
+ if ( Info->DirName2 != NULL) {
+ MIDL_user_free( Info->DirName2);
+ }
+ if ( Info->DirName != NULL) {
+ MIDL_user_free( Info->DirName);
+ }
+ if ( Info->BootName != NULL) {
+ MIDL_user_free( Info->BootName);
+ }
+ NOTHING; // fall through
+ case 1:
+ NOTHING; // fall through
+ case 0:
+ if ( Info->ConfigComment != NULL) {
+ MIDL_user_free( Info->ConfigComment);
+ }
+ if ( Info->ConfigName != NULL) {
+ MIDL_user_free( Info->ConfigName);
+ }
+ break;
+ }
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetrRplConfigAdd(
+ IN RPL_HANDLE ServerHandle,
+ IN DWORD Level,
+ OUT LPRPL_CONFIG_INFO_STRUCT ConfigInfoStruct,
+ OUT LPDWORD pErrorParameter OPTIONAL
+ )
+{
+ LPRPL_CONFIG_INFO_2 Info;
+ LPVOID Buffer;
+ DWORD Error;
+ DWORD ErrorParameter;
+ PRPL_SESSION pSession = &RG_ApiSession;
+
+ ErrorParameter = INVALID_ERROR_PARAMETER;
+ Buffer = Info = ConfigInfoStruct->ConfigInfo2;
+
+ switch( Level) {
+ case 2:
+ if ( !ValidName( Info->FitPersonal, RPL_MAX_STRING_LENGTH, TRUE)) {
+ ErrorParameter = CONFIG_FitPersonal;
+ break;
+ }
+ if ( !ValidName( Info->FitShared, RPL_MAX_STRING_LENGTH, TRUE)) {
+ ErrorParameter = CONFIG_FitShared;
+ break;
+ }
+ if ( !ValidName( Info->DirName4, RPL_MAX_STRING_LENGTH, FALSE)) {
+ ErrorParameter = CONFIG_DirName4;
+ break;
+ }
+ if ( !ValidName( Info->DirName3, RPL_MAX_STRING_LENGTH, FALSE)) {
+ ErrorParameter = CONFIG_DirName3;
+ break;
+ }
+ if ( !ValidName( Info->DirName2, RPL_MAX_STRING_LENGTH, TRUE)) {
+ ErrorParameter = CONFIG_DirName2;
+ break;
+ }
+ if ( !ValidName( Info->DirName, RPL_MAX_STRING_LENGTH, TRUE)) {
+ ErrorParameter = CONFIG_DirName;
+ break;
+ }
+ if ( !ValidName( Info->BootName, RPL_MAX_BOOT_NAME_LENGTH, TRUE)) {
+ ErrorParameter = CONFIG_BootName;
+ break;
+ }
+ _wcsupr( Info->BootName);
+ if ( Info->Flags & ~CONFIG_FLAGS_MASK_ENABLED) {
+ ErrorParameter = CONFIG_Flags;
+ break;
+ }
+ switch ( Info->Flags) {
+ case 0:
+ Info->Flags = RplConfigEnabled( Info->DirName2) == TRUE ?
+ CONFIG_FLAGS_ENABLED_TRUE : CONFIG_FLAGS_ENABLED_FALSE;
+ break;
+ case CONFIG_FLAGS_ENABLED_TRUE:
+ case CONFIG_FLAGS_ENABLED_FALSE:
+ break;
+ default:
+ ErrorParameter = CONFIG_Flags;
+ break;
+ }
+ if ( RPL_STRING_TOO_LONG( Info->ConfigComment)) {
+ ErrorParameter = CONFIG_ConfigComment;
+ break;
+ }
+ if ( !ValidName( Info->ConfigName, RPL_MAX_CONFIG_NAME_LENGTH, TRUE)) {
+ ErrorParameter = CONFIG_ConfigName;
+ break;
+ }
+ _wcsupr( Info->ConfigName);
+ break;
+ default:
+ return( ERROR_INVALID_LEVEL);
+ break;
+ }
+
+ if ( ErrorParameter != INVALID_ERROR_PARAMETER) {
+ if ( ARGUMENT_PRESENT( pErrorParameter)) {
+ *pErrorParameter = ErrorParameter;
+ }
+ return( ERROR_INVALID_PARAMETER);
+ }
+
+ EnterCriticalSection( &RG_ProtectDatabase);
+ Call( JetBeginTransaction( pSession->SesId));
+
+ //
+ // Verify that ConfigName is available in the database.
+ //
+ if ( RplFind( pSession, CONFIG_TABLE_TAG, Info->ConfigName)) {
+ Error = NERR_RplConfigNameUnavailable;
+ goto cleanup;
+ }
+
+ CallJ( JetPrepareUpdate( pSession->SesId, pSession->ConfigTableId, JET_prepInsert));
+
+ Error = ConfigSetInfo( pSession, Level, Buffer, &ErrorParameter);
+ if ( Error == ERROR_SUCCESS) {
+ ErrorParameter = 0;
+ CallJ( JetUpdate( pSession->SesId, pSession->ConfigTableId, NULL, 0, NULL));
+ }
+
+cleanup:
+ if ( Error == NO_ERROR) {
+ Call( JetCommitTransaction( pSession->SesId, JET_bitCommitFlush));
+ } else {
+ Call( JetRollback( pSession->SesId, JET_bitRollbackAll));
+ }
+ LeaveCriticalSection( &RG_ProtectDatabase);
+
+ if ( Error != ERROR_SUCCESS) {
+ if ( ARGUMENT_PRESENT( pErrorParameter)) {
+ *pErrorParameter = ErrorParameter;
+ }
+ }
+ return( Error);
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetrRplConfigDel(
+ IN RPL_HANDLE ServerHandle,
+ IN LPWSTR ConfigName
+ )
+/*++
+--*/
+{
+ DWORD Error;
+ PRPL_SESSION pSession = &RG_ApiSession;
+
+ _wcsupr( ConfigName);
+
+ EnterCriticalSection( &RG_ProtectDatabase);
+ Call( JetBeginTransaction( pSession->SesId));
+
+ if ( RplFindByField( pSession, PROFILE_TABLE_TAG,
+ PROFILE_INDEX_ConfigNameProfileName, ConfigName)) {
+ //
+ // We found a PROFILE record which uses this CONFIG.
+ //
+ Error = NERR_RplConfigNotEmpty;
+ goto cleanup;
+ }
+ if ( !RplFind( pSession, CONFIG_TABLE_TAG, ConfigName)) {
+ Error = NERR_RplConfigNotFound;
+ goto cleanup;
+ }
+ CallJ( JetDelete( pSession->SesId, pSession->ConfigTableId));
+ Error = NO_ERROR;
+
+cleanup:
+ if ( Error == NO_ERROR) {
+ Call( JetCommitTransaction( pSession->SesId, JET_bitCommitFlush));
+ } else {
+ Call( JetRollback( pSession->SesId, JET_bitRollbackAll));
+ }
+ LeaveCriticalSection( &RG_ProtectDatabase);
+ return( Error);
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetrRplConfigEnum(
+ IN RPL_RPC_HANDLE ServerHandle,
+ IN PWCHAR AdapterName,
+ IN OUT LPRPL_CONFIG_ENUM ConfigEnum,
+ IN DWORD PrefMaxLength,
+ OUT LPDWORD TotalEntries,
+ IN OUT LPDWORD pResumeHandle OPTIONAL
+ )
+/*++
+
+Routine Description:
+
+ If AdapterName is null, enumerate all configurations.
+ If AdapterName is not null, enumerate all configurations that can support
+ this adapter id.
+
+Arguments:
+
+ pServerHandle - ptr to RPL_HANDLE
+
+ InfoStruct - Pointer to a structure that contains the information that
+ RPC needs about the returned data. This structure contains the
+ following information:
+ Level - The desired information level - indicates how to
+ interpret the structure of the returned buffer.
+ EntriesRead - Indicates how many elements are returned in the
+ array of structures that are returned.
+ BufferPointer - Location for the pointer to the array of
+ structures that are being returned.
+
+ PrefMaxLen - Indicates a maximum size limit that the caller will allow
+ for (the return buffer.
+
+ TotalEntries - Pointer to a value that upon return indicates the total
+ number of entries in the "active" database.
+
+ pResumeHandle - Inidcates where to restart the enumeration. This is an
+ optional parameter and can be NULL.
+
+Return Value:
+ NO_ERROR if success.
+
+--*/
+{
+ LPBYTE Buffer;
+ DWORD TypicalSize;
+ DWORD CoreSize;
+ DWORD Error;
+ INT SpaceLeft;
+ DWORD ArrayLength;
+ DWORD EntriesRead;
+ BOOL InfoError;
+ BOOL TableEnd;
+ RPL_FILTER Filter;
+ PRPL_FILTER pFilter;
+ PRPL_SESSION pSession = &RG_ApiSession;
+
+ switch( ConfigEnum->Level) {
+ case 2:
+ TypicalSize = CoreSize = sizeof( RPL_CONFIG_INFO_2);
+ TypicalSize += 20 * sizeof( WCHAR); // typical size of FitPersonal
+ TypicalSize += 20 * sizeof( WCHAR); // typical size of FitShared
+ TypicalSize += 12 * sizeof( WCHAR); // typical size of DirName4
+ TypicalSize += 12 * sizeof( WCHAR); // typical size of DirName3
+ TypicalSize += 12 * sizeof( WCHAR); // typical size of DirName2
+ TypicalSize += 12 * sizeof( WCHAR); // typical size of DirName
+ TypicalSize += 8 * sizeof( WCHAR); // typical size of BootName
+ NOTHING; // fall through
+ case 1:
+ if ( ConfigEnum->Level == 1) {
+ TypicalSize = CoreSize = sizeof( RPL_CONFIG_INFO_1);
+ }
+ NOTHING; // fall through
+ case 0:
+ if ( ConfigEnum->Level == 0) {
+ TypicalSize = CoreSize = sizeof( RPL_CONFIG_INFO_0);
+ }
+ TypicalSize += 20 * sizeof( WCHAR); // typical size of ConfigComment
+ TypicalSize += 8 * sizeof( WCHAR); // typical size of ConfigName
+ break;
+ default:
+ return( ERROR_INVALID_LEVEL);
+ break;
+ }
+
+ if ( AdapterName != NULL) {
+ pFilter = &Filter;
+ if ( !ValidHexName( AdapterName, RPL_ADAPTER_NAME_LENGTH, TRUE)) {
+ return( ERROR_INVALID_PARAMETER);
+ }
+ pFilter->VendorId = AdapterNameToVendorId( AdapterName);
+ pFilter->FindFirst = TRUE;
+ } else {
+ pFilter = NULL;
+ }
+
+ if ( PrefMaxLength == -1) {
+ //
+ // If the caller has not specified a size, calculate a size
+ // that will hold the entire enumeration.
+ //
+ SpaceLeft = DEFAULT_BUFFER_SIZE;
+ } else {
+ SpaceLeft = PrefMaxLength;
+ }
+
+ //
+ // Buffer space is shared by the array and by strings pointed at
+ // by the elements in this array. We need to decide up front how much
+ // space is allocated for array and how much for the strings.
+ //
+ ArrayLength = SpaceLeft / TypicalSize;
+ if ( ArrayLength == 0) {
+ ArrayLength = 1; // try to return at least one element
+ }
+
+ //
+ // Note that MIDL_user_allocate() returns memory which is NOT initialized
+ // to zero. Since we do NOT use allocate all nodes, this means that all
+ // fields, especially pointers, in array elements must be properly set.
+ //
+ Buffer = MIDL_user_allocate( ArrayLength * CoreSize);
+ if ( Buffer == NULL) {
+ return( ERROR_NOT_ENOUGH_MEMORY);
+ }
+ RplDump( RG_DebugLevel & RPL_DEBUG_CONFIG, (
+ "ConfigEnum: Buffer=0x%x, ArrayLength=0x%x", Buffer, ArrayLength));
+ ConfigEnum->ConfigInfo.Level0->Buffer = (LPRPL_CONFIG_INFO_0)Buffer;
+
+ EntriesRead = 0;
+ InfoError = FALSE;
+ Error = NO_ERROR;
+
+ EnterCriticalSection( &RG_ProtectDatabase);
+ Call( JetBeginTransaction( pSession->SesId));
+
+ if ( !RplFilterFirst( pSession, CONFIG_TABLE_TAG, pFilter, pResumeHandle, &TableEnd)) {
+ Error = NERR_RplCannotEnum;
+ goto cleanup;
+ }
+ if ( TableEnd == TRUE) {
+ goto cleanup;
+ }
+ for ( ; ; ) {
+ memset( Buffer, 0, CoreSize); // for cleanup to work properly
+ Error = ConfigGetInfo( pSession, NULL, ConfigEnum->Level, Buffer, &SpaceLeft);
+ if ( Error != NO_ERROR) {
+ InfoError = TRUE; // clean things up without holding crit sec
+ break;
+ }
+ EntriesRead++;
+ Buffer += CoreSize;
+ SpaceLeft -= CoreSize;
+ if ( !RplFilterNext( pSession, pSession->ConfigTableId, pFilter, &TableEnd)) {
+ Error = NERR_RplCannotEnum;
+ goto cleanup;
+ }
+ if ( TableEnd == TRUE) {
+ goto cleanup;
+ }
+ if ( SpaceLeft <= 0) {
+ Error = ERROR_MORE_DATA;
+ break;
+ }
+ if ( EntriesRead >= ArrayLength) {
+ //
+ // We have space available but allocated array is not big enough.
+ // This should NOT happen often as our intent (see above) is to
+ // overestimate array length. When it happens we can still try
+ // to reallocate array to a larger size here. This is not done
+ // for now (too cumbersome) & we just stop the enumeration.
+ //
+ Error = ERROR_MORE_DATA;
+ break;
+ }
+ }
+cleanup:
+ Call( JetCommitTransaction( pSession->SesId, 0));
+ LeaveCriticalSection( &RG_ProtectDatabase);
+ if ( InfoError == TRUE) {
+ ConfigGetInfoCleanup( ConfigEnum->Level, Buffer);
+ }
+ if ( Error == NO_ERROR) {
+ *TotalEntries = EntriesRead;
+ } else if ( Error == ERROR_MORE_DATA) {
+ *TotalEntries = EntriesRead * 2; // we cheat here
+ } else {
+ //
+ // Cleanup in case of "bad" errors.
+ //
+ while ( EntriesRead > 0) {
+ EntriesRead--;
+ Buffer -= CoreSize;
+ ConfigGetInfoCleanup( ConfigEnum->Level, Buffer);
+ }
+ MIDL_user_free( Buffer);
+ }
+
+ RplDump( RG_DebugLevel & RPL_DEBUG_CONFIG, ("ConfigEnum: EntriesRead = 0x%x", EntriesRead));
+
+ ConfigEnum->ConfigInfo.Level0->EntriesRead = EntriesRead;
+ if ( EntriesRead == 0) {
+ ConfigEnum->ConfigInfo.Level0->Buffer = NULL;
+ }
+
+ if ( ARGUMENT_PRESENT( pResumeHandle)) {
+ if ( Error == ERROR_MORE_DATA && EntriesRead > 0) {
+ EnterCriticalSection( &RG_ProtectDatabase);
+ Call( JetBeginTransaction( pSession->SesId));
+ RplFilterSave( pSession, (DWORD)ServerHandle, pFilter,
+ ((LPRPL_CONFIG_INFO_0)(Buffer-CoreSize))->ConfigName,
+ pResumeHandle);
+ Call( JetCommitTransaction( pSession->SesId, JET_bitCommitFlush));
+ LeaveCriticalSection( &RG_ProtectDatabase);
+ } else {
+ *pResumeHandle = 0; // resume from beginning
+ }
+ }
+ return( Error);
+}
diff --git a/private/net/svcdlls/rpl/server/database.c b/private/net/svcdlls/rpl/server/database.c
new file mode 100644
index 000000000..aa96fc767
--- /dev/null
+++ b/private/net/svcdlls/rpl/server/database.c
@@ -0,0 +1,998 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ database.c
+
+Abstract:
+
+ Routines for non-api _access to the database. This includes initialization
+ and shutdown code on the database.
+
+ Exports:
+
+BOOL RplDbInit( VOID)
+VOID RplDbTerm( VOID)
+BOOL RplDbFindWksta(
+BOOL RplDbFillWksta(
+BOOL RplDbHaveWksta(
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 19 - November - 1993
+
+Environment:
+
+ User mode
+
+Revision History :
+
+--*/
+
+#include "local.h"
+#include "rpldb.h"
+#include "database.h"
+#include "db.h"
+#include "dblib.h"
+#include "adapter.h"
+#include "boot.h"
+#include "config.h"
+#include "profile.h"
+#include "resume.h"
+#include "vendor.h"
+#include "wksta.h"
+#include "report.h" // for RplReportEventEx
+#include "winsock.h" // for INADDR_NONE
+
+DWORD RplStartJet500Conversion();
+
+#define RPL_BACKUP_SUBDIR L"BACKUP"
+
+//
+// File names must be complete, i.e. extensions are not optional. (This is unlike
+// the OS/2 behavior where we would append ".FIT" if extension is absent).
+//
+
+
+
+BOOL RplDbInitColumnInfo(
+ IN PCHAR TableName,
+ IN OUT PRPL_COLUMN_INFO ColumnInfoTable,
+ IN DWORD ColumnInfoTableLength,
+ IN JET_TABLEID TableId,
+ IN JET_SESID SesId
+ )
+{
+ JET_COLUMNDEF ColumnDef;
+ DWORD index;
+
+ for ( index = 0; index < ColumnInfoTableLength; index++) {
+ CallB( JetGetTableColumnInfo( SesId, TableId,
+ ColumnInfoTable[ index].ColumnName, &ColumnDef,
+ sizeof( ColumnDef), JET_ColInfo));
+ RPL_ASSERT( ColumnInfoTable[ index].ColumnType == ColumnDef.coltyp);
+ ColumnInfoTable[ index].ColumnId = ColumnDef.columnid;
+ }
+ return( TRUE);
+}
+
+
+//
+// Code templated from WINS:WinMscDelFiles(). JonN 8/7/94
+//
+VOID RplDeleteLogFiles()
+{
+ WCHAR Path[ MAX_PATH]; // must hold terminating NULL char too
+ WIN32_FIND_DATA FileInfo;
+ HANDLE SearchHandle = INVALID_HANDLE_VALUE;
+ DWORD ErrCode = NO_ERROR;
+
+ memcpy( Path, RG_Directory, RG_DirectoryLength * sizeof(WCHAR));
+ memcpy( Path+RG_DirectoryLength, L"jet*.log", 9*sizeof(WCHAR));
+
+ SearchHandle = FindFirstFile(Path, &FileInfo);
+ if (SearchHandle == INVALID_HANDLE_VALUE)
+ {
+ RplDump( RG_DebugLevel & RPL_DEBUG_REQUEST, (
+ "RplDeleteLogFiles: FindFirstFile( %ws) returned %d",
+ Path, GetLastError()));
+ goto cleanup;
+ }
+
+ do {
+ memcpy( Path + RG_DirectoryLength,
+ FileInfo.cFileName,
+ (wcslen(FileInfo.cFileName)+1) * sizeof(WCHAR) );
+ if (!DeleteFile(Path))
+ {
+ RplDump( RG_DebugLevel & RPL_DEBUG_REQUEST, (
+ "RplDeleteLogFiles: DeleteFile( %ws) returned %d",
+ Path, GetLastError()));
+ goto cleanup;
+ }
+
+ } while(FindNextFile(SearchHandle, &FileInfo));
+ if ((ErrCode = GetLastError()) != ERROR_NO_MORE_FILES)
+ {
+ RplDump( RG_DebugLevel & RPL_DEBUG_REQUEST, (
+ "RplDeleteLogFiles: FindNextFile() returned %d",
+ ErrCode));
+
+ }
+
+cleanup:
+ if ( SearchHandle != INVALID_HANDLE_VALUE && !FindClose(SearchHandle))
+ {
+ RplDump( RG_DebugLevel & RPL_DEBUG_REQUEST, (
+ "RplDeleteLogFiles: FindClose() returned %d",
+ GetLastError()));
+ }
+
+ return;
+}
+
+
+BOOL RplDbInitPath(
+ IN OUT PWCHAR Path,
+ IN PWCHAR Name,
+ OUT PCHAR * pDbcsPath
+ )
+/*++
+
+Routine Description:
+ Allocates DBCS string corresponding to a UNICODE string obtained via
+ concatenation of Path & Name strings.
+
+Arguments:
+ Path - first UNICODE string
+ Name - second UNICODE string
+ pDbcsPath - pointer to a DBCS string corresponding to a concatenation
+ of above two UNICODE strings
+
+Return Value:
+ TRUE if success, FALSE otherwise.
+
+--*/
+{
+ DWORD NameLength;
+ DWORD PathLength; // not count terminating NULL char
+
+ NameLength = wcslen( Name);
+ PathLength = RG_DirectoryLength + NameLength;
+ if ( PathLength >= MAX_PATH) {
+ RPL_RETURN( FALSE);
+ }
+ memcpy( Path + RG_DirectoryLength, Name, (NameLength+1)*sizeof(WCHAR));
+ NameLength = RplUnicodeToDbcs( RG_MemoryHandle, Path, PathLength,
+ MAX_PATH * sizeof(WCHAR), pDbcsPath);
+ if ( NameLength == 0) {
+ RPL_RETURN( FALSE);
+ }
+ return( TRUE);
+}
+
+
+BOOL RplDbSessionInit(
+ IN BOOL MainSession,
+ OUT PRPL_SESSION pSession,
+ OUT BOOL * pErrorReported
+ )
+{
+
+ CallB( JetBeginSession( RG_Instance, &pSession->SesId, "admin", ""));
+ if ( MainSession) {
+ JET_ERR err = JetAttachDatabase( pSession->SesId, RG_Mdb, 0);
+ if ( err == JET_errSuccess ) {
+ //
+ // JonN 6/16/95 This code works around a JET bug in cases
+ // where the JET database has moved. The code fragment was
+ // suggested by Ian Jose. JetAttachDatabase will return
+ // JET_wrnDatabaseAttached under normal circumstances.
+ //
+ RplDump( RG_DebugLevel & RPL_DEBUG_REQUEST, (
+ "RplDbSessionInit: JetAttachDatabase returned JET_errSuccess, engaging workaround" ));
+ // Detach(NULL) detached all databases, new to NT 3.51 and up
+ Call( JetDetachDatabase( pSession->SesId, NULL));
+ //
+ // It is OK if this call returns JET_errSuccess since
+ // we explicitly detached. Note that we do not normally
+ // detach at all.
+ //
+ CallB( JetAttachDatabase( pSession->SesId, RG_Mdb, 0));
+ }
+ else if ( err == JET_errDatabase200Format ) {
+ //
+ // JetInit will succeed if no 200-series logs exist, and the
+ // problem will not be caught until here.
+ //
+ DWORD converr;
+ converr = RplStartJet500Conversion();
+ RplReportEvent( NELOG_RplUpgradeDBTo40, NULL, sizeof(DWORD), &converr);
+ *pErrorReported = TRUE;
+ return( FALSE);
+ }
+ else
+ {
+ CallB( err );
+ }
+ RG_DetachDatabase = TRUE;
+ }
+
+ CallB( JetOpenDatabase( pSession->SesId, RG_Mdb, NULL, &pSession->DbId, 0));
+ CallB( JetOpenTable( pSession->SesId, pSession->DbId, ADAPTER_TABLE_NAME, NULL, 0,
+ 0, &pSession->AdapterTableId));
+ CallB( JetOpenTable( pSession->SesId, pSession->DbId, BOOT_TABLE_NAME, NULL, 0,
+ 0, &pSession->BootTableId));
+ CallB( JetOpenTable( pSession->SesId, pSession->DbId, CONFIG_TABLE_NAME, NULL, 0,
+ 0, &pSession->ConfigTableId));
+ CallB( JetOpenTable( pSession->SesId, pSession->DbId, PROFILE_TABLE_NAME, NULL, 0,
+ 0, &pSession->ProfileTableId));
+ if ( MainSession) {
+ DWORD Error;
+ Error = ResumeCreateTable( pSession); // initializes ResumeTable also
+ if ( Error != NO_ERROR) {
+ RplDump( ++RG_Assert,( "Error=%d", Error));
+ return( FALSE);
+ }
+ }
+ CallB( JetOpenTable( pSession->SesId, pSession->DbId, RESUME_TABLE_NAME, NULL, 0,
+ 0, &pSession->ResumeTableId));
+ CallB( JetOpenTable( pSession->SesId, pSession->DbId, VENDOR_TABLE_NAME, NULL, 0,
+ 0, &pSession->VendorTableId));
+ CallB( JetOpenTable( pSession->SesId, pSession->DbId, WKSTA_TABLE_NAME, NULL, 0,
+ 0, &pSession->WkstaTableId));
+
+ if ( MainSession) {
+ if ( !RplDbInitColumnInfo( ADAPTER_TABLE_NAME, AdapterTable,
+ ADAPTER_TABLE_LENGTH, pSession->AdapterTableId, pSession->SesId)) {
+ return( FALSE);
+ }
+ if ( !RplDbInitColumnInfo( BOOT_TABLE_NAME, BootTable,
+ BOOT_TABLE_LENGTH, pSession->BootTableId, pSession->SesId)) {
+ return( FALSE);
+ }
+ if ( !RplDbInitColumnInfo( CONFIG_TABLE_NAME, ConfigTable,
+ CONFIG_TABLE_LENGTH, pSession->ConfigTableId, pSession->SesId)) {
+ return( FALSE);
+ }
+ if ( !RplDbInitColumnInfo( PROFILE_TABLE_NAME, ProfileTable,
+ PROFILE_TABLE_LENGTH, pSession->ProfileTableId, pSession->SesId)) {
+ return( FALSE);
+ }
+ //
+ // ColumnInfo for resume table has been initialized already.
+ //
+ if ( !RplDbInitColumnInfo( VENDOR_TABLE_NAME, VendorTable,
+ VENDOR_TABLE_LENGTH, pSession->VendorTableId, pSession->SesId)) {
+ return( FALSE);
+ }
+ if ( !RplDbInitColumnInfo( WKSTA_TABLE_NAME, WkstaTable,
+ WKSTA_TABLE_LENGTH, pSession->WkstaTableId, pSession->SesId)) {
+ return( FALSE);
+ }
+ }
+ return( TRUE);
+}
+
+
+VOID RplDbSessionTerm(
+ IN BOOL MainSession,
+ IN PRPL_SESSION pSession
+ )
+{
+ if ( pSession->AdapterTableId != 0) {
+ Call( JetCloseTable( pSession->SesId, pSession->AdapterTableId));
+ }
+ if ( pSession->BootTableId != 0) {
+ Call( JetCloseTable( pSession->SesId, pSession->BootTableId));
+ }
+ if ( pSession->ConfigTableId != 0) {
+ Call( JetCloseTable( pSession->SesId, pSession->ConfigTableId));
+ }
+ if ( pSession->ProfileTableId != 0) {
+ Call( JetCloseTable( pSession->SesId, pSession->ProfileTableId));
+ }
+ if ( pSession->ResumeTableId != 0) {
+ Call( JetCloseTable( pSession->SesId, pSession->ResumeTableId));
+ if ( MainSession) {
+ Call( JetDeleteTable( pSession->SesId, pSession->DbId, RESUME_TABLE_NAME));
+ }
+ }
+ if ( pSession->VendorTableId != 0) {
+ Call( JetCloseTable( pSession->SesId, pSession->VendorTableId));
+ }
+ if ( pSession->WkstaTableId != 0) {
+ Call( JetCloseTable( pSession->SesId, pSession->WkstaTableId));
+ }
+ if ( pSession->DbId != 0) {
+ Call( JetCloseDatabase( pSession->SesId, pSession->DbId, 0));
+ }
+ if ( pSession->SesId != 0) {
+ if ( MainSession && RG_DetachDatabase) {
+#if 0
+ //
+ // Because of JET restore bugs we are advised NOT TO
+ // detach database ever.
+ //
+ Call( JetDetachDatabase( pSession->SesId, RG_Mdb));
+#endif
+ RG_DetachDatabase = FALSE;
+ }
+ Call( JetEndSession( pSession->SesId, 0));
+ }
+}
+
+
+BOOL RplDbFindBoot(
+ IN PRPL_SESSION pSession,
+ IN PWCHAR BootName,
+ IN PWCHAR AdapterName
+ )
+/*++
+ Return TRUE if it finds server record for input BootName & AdapterName.
+ Returns FALSE otherwise.
+
+ Same comment as for RplDbFindWksta.
+--*/
+{
+ JET_ERR JetError;
+ DWORD Vendor;
+ WCHAR SaveChar;
+
+ JetError = JetSetCurrentIndex( pSession->SesId, pSession->BootTableId, BOOT_INDEX_VendorIdBootName);
+ if ( JetError != JET_errSuccess) {
+ RplDump( ++RG_Assert, ("SetCurrentIndex failed err=%d", JetError));
+ return( FALSE);
+ }
+ SaveChar = AdapterName[ RPL_VENDOR_NAME_LENGTH];
+ AdapterName[ RPL_VENDOR_NAME_LENGTH] = 0;
+ Vendor = wcstoul( AdapterName, NULL, 16);
+ AdapterName[ RPL_VENDOR_NAME_LENGTH] = SaveChar;
+ JetError = JetMakeKey( pSession->SesId, pSession->BootTableId, &Vendor, sizeof( Vendor), JET_bitNewKey);
+ if ( JetError != JET_errSuccess) {
+ RplDump( ++RG_Assert, ("MakeKey failed err=%d", JetError));
+ return( FALSE);
+ }
+ JetError = JetMakeKey( pSession->SesId, pSession->BootTableId, BootName, ( wcslen( BootName) + 1) * sizeof(WCHAR), 0);
+ if ( JetError != JET_errSuccess) {
+ RplDump( ++RG_Assert, ("MakeKey failed err=%d", JetError));
+ return( FALSE);
+ }
+ JetError = JetSeek( pSession->SesId, pSession->BootTableId, JET_bitSeekEQ);
+ if ( JetError != JET_errSuccess) {
+ if ( JetError == JET_errRecordNotFound) {
+ //
+ // This is an expected error, do not break for this.
+ //
+ RplDump( RG_DebugLevel & RPL_DEBUG_REQUEST, (
+ "DbFindWksta( %ws) failed", AdapterName));
+ } else {
+ RplDump( ++RG_Assert, ("JetSeek failed err=%d", JetError));
+ }
+ return( FALSE);
+ }
+ return( TRUE);
+}
+
+
+BOOL RplDbAddAdapterName(
+ IN PRPL_SESSION pSession,
+ IN LPWSTR AdapterName
+ )
+/*++
+ Try to add the adapter record for input AdapterName.
+
+ CODEWORK Should use different comments for different VendorName-s.
+
+Return Value:
+ TRUE if adapter record for input AdapterName was added
+ FALSE otherwise
+
+--*/
+{
+#define ADAPTER_GENERIC_COMMENT L"An unknown client network adapter id."
+ JET_ERR JetError;
+ DWORD Flags = 0;
+ BYTE LocalBuffer[ 300];
+ DWORD DataSize;
+ PWCHAR AdapterComment;
+ WCHAR VendorName[ RPL_VENDOR_NAME_LENGTH + 1];
+
+ if ( RplFind( pSession, ADAPTER_TABLE_TAG, AdapterName)) {
+ return( FALSE); // adapter record is already present
+ }
+
+ memcpy( VendorName, AdapterName, RPL_VENDOR_NAME_LENGTH*sizeof(WCHAR));
+ VendorName[ RPL_VENDOR_NAME_LENGTH] = 0;
+
+ if ( RplFind( pSession, VENDOR_TABLE_TAG, VendorName)) {
+ CallB( JetRetrieveColumn( pSession->SesId, pSession->VendorTableId,
+ VendorTable[ VENDOR_VendorComment].ColumnId, LocalBuffer,
+ sizeof( LocalBuffer), &DataSize, 0, NULL));
+ if ( DataSize > sizeof( LocalBuffer)) {
+ AdapterComment = ADAPTER_GENERIC_COMMENT;
+ } else {
+ AdapterComment = (PWCHAR)LocalBuffer;
+ }
+ } else {
+ AdapterComment = ADAPTER_GENERIC_COMMENT;
+ }
+
+ CallB( JetPrepareUpdate( pSession->SesId, pSession->AdapterTableId,
+ JET_prepInsert));
+ CallB( JetSetColumn( pSession->SesId, pSession->AdapterTableId,
+ AdapterTable[ ADAPTER_AdapterName].ColumnId, AdapterName,
+ ( wcslen( AdapterName) + 1) * sizeof(WCHAR), 0, NULL));
+ CallB( JetSetColumn( pSession->SesId, pSession->AdapterTableId,
+ AdapterTable[ ADAPTER_AdapterComment].ColumnId, AdapterComment,
+ ( wcslen( AdapterComment) + 1) * sizeof(WCHAR), 0, NULL));
+ CallB( JetSetColumn( pSession->SesId, pSession->AdapterTableId,
+ AdapterTable[ ADAPTER_Flags].ColumnId, &Flags,
+ sizeof(Flags), 0, NULL));
+ JetError = JetUpdate( pSession->SesId, pSession->AdapterTableId, NULL, 0, NULL);
+ if ( JetError < 0) {
+ if ( JetError != JET_errKeyDuplicate) {
+ RplDump( ++RG_Assert, ( "JetError=%d", JetError));
+ }
+ return( FALSE);
+ }
+ return( TRUE);
+}
+
+
+VOID RplDbInstanceTerm( VOID)
+{
+ if ( !RG_InstanceAllocated) {
+ return;
+ }
+ RplDbSessionTerm( FALSE, &RG_ApiSession);
+ RplDbSessionTerm( FALSE, &RG_WorkerSession);
+ RplDbSessionTerm( TRUE, &RG_RequestSession);
+ Call( JetTerm2( RG_Instance, JET_bitTermComplete));
+ RG_InstanceAllocated = FALSE;
+}
+
+
+BOOL RplDbInstanceInit(
+ PCHAR SystemMdb,
+ PCHAR TempMdb,
+ PCHAR LogFilePath,
+ BOOL * pErrorReported
+ )
+{
+ RG_InstanceAllocated = FALSE;
+ RG_DetachDatabase = FALSE;
+ memset( &RG_RequestSession, 0, sizeof( RG_RequestSession));
+ memset( &RG_WorkerSession, 0, sizeof( RG_WorkerSession));
+ memset( &RG_ApiSession, 0, sizeof( RG_ApiSession));
+
+#ifdef __JET500
+ CallB( JetSetSystemParameter( &RG_Instance, 0, JET_paramSystemPath, 0, LogFilePath));
+#else
+ CallB( JetSetSystemParameter( &RG_Instance, 0, JET_paramSysDbPath, 0, SystemMdb));
+#endif
+ RG_InstanceAllocated = TRUE;
+ CallB( JetSetSystemParameter( &RG_Instance, 0, JET_paramTempPath, 0, TempMdb));
+ CallB( JetSetSystemParameter( &RG_Instance, 0, JET_paramLogFilePath, 0, LogFilePath));
+ CallB( JetSetSystemParameter( &RG_Instance, 0, JET_paramMaxBuffers, 250, NULL));
+ CallB( JetSetSystemParameter( &RG_Instance, 0, JET_paramBfThrshldLowPrcnt, 0, NULL));
+ CallB( JetSetSystemParameter( &RG_Instance, 0, JET_paramBfThrshldHighPrcnt, 100, NULL));
+ CallB( JetSetSystemParameter( &RG_Instance, 0, JET_paramMaxOpenTables, 30, NULL));
+ CallB( JetSetSystemParameter( &RG_Instance, 0, JET_paramMaxOpenTableIndexes, 105, NULL));
+ CallB( JetSetSystemParameter( &RG_Instance, 0, JET_paramMaxCursors, 100, NULL));
+ CallB( JetSetSystemParameter( &RG_Instance, 0, JET_paramMaxSessions, 10, NULL));
+ CallB( JetSetSystemParameter( &RG_Instance, 0, JET_paramMaxVerPages, 64, NULL));
+ CallB( JetSetSystemParameter( &RG_Instance, 0, JET_paramMaxTemporaryTables, 5, NULL));
+ CallB( JetSetSystemParameter( &RG_Instance, 0, JET_paramLogBuffers, 41, NULL));
+#ifdef __JET500
+ CallB( JetSetSystemParameter( &RG_Instance, 0, JET_paramLogFileSize, 1000, NULL));
+#else
+ CallB( JetSetSystemParameter( &RG_Instance, 0, JET_paramLogFileSectors, 1000, NULL));
+#endif
+ CallB( JetSetSystemParameter( &RG_Instance, 0, JET_paramLogFlushThreshold, 10, NULL));
+#ifdef __JET500
+ CallB( JetSetSystemParameter( &RG_Instance, 0, JET_paramBaseName, 0, "j50"));
+ {
+ JET_ERR JetError = JetInit( &RG_Instance);
+ //
+ // JetInit will fail if 200-series logs exist.
+ //
+ if ( JetError == JET_errDatabase200Format ) {
+ DWORD converr;
+ converr = RplStartJet500Conversion();
+ RplReportEvent( NELOG_RplUpgradeDBTo40, NULL, sizeof(DWORD), &converr);
+ *pErrorReported = TRUE;
+ return( FALSE);
+ }
+ else
+ {
+ CallB( JetError );
+ }
+ }
+#else
+ CallB( JetSetSystemParameter( &RG_Instance, 0, JET_paramRecovery, 0, "on"));
+ CallB( JetInit( &RG_Instance));
+#endif
+
+ if ( !RplDbSessionInit( TRUE, &RG_RequestSession, pErrorReported)) {
+ return( FALSE);
+ }
+ if ( !RplDbSessionInit( FALSE, &RG_WorkerSession, pErrorReported)) {
+ return( FALSE);
+ }
+ if ( !RplDbSessionInit( FALSE, &RG_ApiSession, pErrorReported)) {
+ return( FALSE);
+ }
+ return( TRUE);
+}
+
+
+
+BOOL RplDbInit()
+{
+ WCHAR Path[ MAX_PATH]; // must hold terminating NULL char too
+ JET_ERR JetError;
+ PCHAR SystemMdb = NULL; // needed for cleanup below
+ PCHAR TempMdb = NULL; // needed for cleanup below
+ PCHAR LogFilePath = NULL; // needed for cleanup below
+ BOOL Success = FALSE;
+ BOOL ErrorReported = FALSE;
+
+ memcpy( Path, RG_Directory, RG_DirectoryLength * sizeof(WCHAR));
+
+ if ( !RplDbInitPath( Path, RPL_BACKUP_SUBDIR, &RG_BackupPath)) {
+ goto cleanup;
+ }
+ if ( !RplDbInitPath( Path, RPL_SERVICE_DATABASE_W, &RG_Mdb)) {
+ goto cleanup;
+ }
+ if ( !RplDbInitPath( Path, RPL_SYSTEM_DATABASE_W, &SystemMdb)) {
+ goto cleanup;
+ }
+ if ( !RplDbInitPath( Path, RPL_TEMP_DATABASE_W, &TempMdb)) {
+ goto cleanup;
+ }
+ if ( !RplDbInitPath( Path, L"", &LogFilePath)) {
+ goto cleanup;
+ }
+
+#if 1
+ if ( !RplDbInstanceInit( SystemMdb, TempMdb, LogFilePath, &ErrorReported)) {
+ if (ErrorReported) {
+ goto cleanup;
+ }
+ RplReportEvent( NELOG_RplInitDatabase, NULL, 0, NULL);
+ RplDbInstanceTerm();
+#else
+ //
+ // Used only for testing purposes (to restore without attempt to init first)
+ //
+ if ( TRUE) {
+#endif
+#ifdef __JET500
+ JetError = JetRestore( RG_BackupPath, 0);
+#else
+ JetError = JetRestore( RG_BackupPath, 0, NULL, 0);
+#endif
+ if ( JetError == JET_errBadLogVersion
+//
+// JonN 11/28/95 According to JLiem, the old BadNextLogVersion is broken up
+// into two errors and two warnings in JET500. If we get either of the
+// warnings (558 or 559) then JET took care of deleting the old log files
+// and we can continue. We handle the errors as we handled BadNextLogVersion.
+//
+#ifdef __JET500
+ || JetError == JET_errGivenLogFileHasBadSignature
+ || JetError == JET_errGivenLogFileIsNotContiguous
+#else
+ || JetError == JET_errBadNextLogVersion
+#endif
+ ) {
+ RplDeleteLogFiles();
+#ifdef __JET500
+ JetError = JetRestore( RG_BackupPath, 0);
+#else
+ JetError = JetRestore( RG_BackupPath, 0, NULL, 0);
+#endif
+ }
+#ifdef __JET500
+#ifndef JET_ATTACH_CATCHES_ERROR
+ if ( JetError == JET_errDatabase200Format ) {
+ RplReportEvent( NELOG_RplUpgradeDBTo40, NULL, sizeof(DWORD), &JetError);
+ RplDump( ++RG_Assert,( "200-fmt from JetRestore" ));
+ goto cleanup;
+ }
+#endif
+#endif
+ if ( JetError < 0) {
+ RplDump( ++RG_Assert, ("JetRestore( %s) failed err=%d", RG_BackupPath, JetError));
+ RplReportEvent( NELOG_RplRestoreDatabaseFailure, NULL, sizeof(DWORD), &JetError);
+ goto cleanup;
+ }
+ RplReportEvent( NELOG_RplRestoreDatabaseSuccess, NULL, 0, NULL);
+ if ( !RplDbInstanceInit( SystemMdb, TempMdb, LogFilePath, &ErrorReported)) {
+ if (ErrorReported) {
+ goto cleanup;
+ }
+ RplReportEvent( NELOG_RplInitRestoredDatabase, NULL, 0, NULL);
+ goto cleanup;
+ }
+ }
+ Success = TRUE;
+
+cleanup:
+ if ( SystemMdb != NULL) {
+ RplMemFree( RG_MemoryHandle, SystemMdb);
+ }
+ if ( TempMdb != NULL) {
+ RplMemFree( RG_MemoryHandle, TempMdb);
+ }
+ if ( LogFilePath != NULL) {
+ RplMemFree( RG_MemoryHandle, LogFilePath);
+ }
+ return( Success);
+}
+
+
+VOID RplDbTerm( VOID)
+/*++
+
+ Save changes that may have been made to the database. Without this
+ the database may be left in an unusable state where any subsequent
+ attempt of calling JetInit() for this database would fail.
+
+--*/
+{
+ RplDbInstanceTerm();
+ RplMemFree( RG_MemoryHandle, RG_Mdb);
+ RplMemFree( RG_MemoryHandle, RG_BackupPath);
+}
+
+
+BOOL RplDbFindWksta(
+ IN PRPL_SESSION pSession,
+ IN LPWSTR AdapterName
+ )
+/*++
+ Return TRUE if it finds wksta record for input AdapterName.
+ Returns FALSE otherwise.
+
+ This code could be make more efficient by defining AdapterName
+ to be jet currency data (it is silly now taking wcslen of
+ AdapterName since it is a fixed length string.
+
+ It is ASSUMED that the caller of this function will ensure transaction
+ processing.
+
+--*/
+{
+ JET_ERR JetError;
+
+ JetError = JetSetCurrentIndex( pSession->SesId, pSession->WkstaTableId, WKSTA_INDEX_AdapterName);
+ if ( JetError != JET_errSuccess) {
+ RplDump( ++RG_Assert, ("SetCurrentIndex failed err=%d", JetError));
+ return( FALSE);
+ }
+ JetError = JetMakeKey( pSession->SesId, pSession->WkstaTableId, AdapterName, ( wcslen( AdapterName) + 1) * sizeof(WCHAR), JET_bitNewKey);
+ if ( JetError != JET_errSuccess) {
+ RplDump( ++RG_Assert, ("MakeKey failed err=%d", JetError));
+ return( FALSE);
+ }
+ JetError = JetSeek( pSession->SesId, pSession->WkstaTableId, JET_bitSeekEQ);
+ if ( JetError != JET_errSuccess) {
+#ifdef RPL_DEBUG
+ //
+ // Do not assert for expected errors ( empty table or
+ // failure to find a record).
+ //
+ if ( JetError == JET_errNoCurrentRecord
+ || JetError == JET_errRecordNotFound) {
+ RplDump( RG_DebugLevel & RPL_DEBUG_REQUEST, (
+ "DbFindWksta( %ws) failed", AdapterName));
+ } else {
+ RplDump( ++RG_Assert, ("JetSeek failed err=%d", JetError));
+ }
+#endif
+ return( FALSE);
+ }
+ return( TRUE);
+}
+
+
+BOOL RplDbFillWksta(
+ IN PRPL_SESSION pSession,
+ IN OUT PRPL_WORKER_DATA pWorkerData
+ )
+/*++
+ Returns TRUE if it can find all the information needed to boot the client.
+ Returns FALSE otherwise.
+
+ This routine should be modified to use ConfigGetInfo() - but without the
+ penalty of memory allocations.
+--*/
+{
+ DWORD DataSize;
+ PWCHAR AdapterName;
+ WCHAR BootName[ RPL_MAX_BOOT_NAME_LENGTH + 1];
+ WCHAR FilePath[ MAX_PATH];
+ DWORD Length;
+ DWORD Flags;
+
+ AdapterName = pWorkerData->pRcb->AdapterName;
+
+ if ( !RplDbFindWksta( pSession, AdapterName)) {
+ RplDump( ++RG_Assert, ("FindWksta( %ws) failed", AdapterName));
+ pWorkerData->EventStrings[ 0] = pWorkerData->WkstaName;
+ pWorkerData->EventId = NERR_RplWkstaNotFound;
+ return( FALSE);
+ }
+ CallB( JetRetrieveColumn( pSession->SesId, pSession->WkstaTableId,
+ WkstaTable[ WKSTA_WkstaName].ColumnId, pWorkerData->WkstaName,
+ sizeof( pWorkerData->WkstaName), &DataSize, 0, NULL));
+
+ CallB( JetRetrieveColumn( pSession->SesId, pSession->WkstaTableId,
+ WkstaTable[ WKSTA_ProfileName].ColumnId, pWorkerData->ProfileName,
+ sizeof( pWorkerData->ProfileName), &DataSize, 0, NULL));
+
+ CallB( JetRetrieveColumn( pSession->SesId, pSession->WkstaTableId,
+ WkstaTable[ WKSTA_FitFile].ColumnId, FilePath,
+ sizeof( FilePath), &DataSize, 0, NULL));
+ Length = DataSize / sizeof( WCHAR) - 1;
+ if ( DataSize > sizeof( FilePath) || FilePath[ Length] != 0) {
+ RplDump( ++RG_Assert, ( "FitFile is bad %ws", FilePath));
+ return( FALSE);
+ }
+ pWorkerData->FitFile = RplMemAlloc( pWorkerData->MemoryHandle,
+ RG_DirectoryLength * sizeof(WCHAR) + DataSize);
+ if ( pWorkerData->FitFile == NULL) {
+ pWorkerData->EventStrings[ 0] = pWorkerData->WkstaName;
+ pWorkerData->EventId = NELOG_RplWkstaMemory;
+ return( FALSE);
+ }
+ memcpy( pWorkerData->FitFile, RG_Directory, RG_DirectoryLength * sizeof(WCHAR));
+ memcpy( pWorkerData->FitFile + RG_DirectoryLength, FilePath, DataSize);
+
+ CallB( JetRetrieveColumn( pSession->SesId, pSession->WkstaTableId,
+ WkstaTable[ WKSTA_BootName].ColumnId, BootName,
+ sizeof( BootName), &DataSize, 0, NULL));
+
+ CallB( JetRetrieveColumn( pSession->SesId, pSession->WkstaTableId,
+ WkstaTable[ WKSTA_Flags].ColumnId, &Flags,
+ sizeof( Flags), &DataSize, 0, NULL));
+
+ switch ( Flags & WKSTA_FLAGS_MASK_LOGON_INPUT) {
+ case WKSTA_FLAGS_LOGON_INPUT_REQUIRED:
+ pWorkerData->LogonInput = WKSTA_LOGON_INPUT_REQUIRED;
+ break;
+ case WKSTA_FLAGS_LOGON_INPUT_OPTIONAL:
+ pWorkerData->LogonInput = WKSTA_LOGON_INPUT_OPTIONAL;
+ break;
+ case WKSTA_FLAGS_LOGON_INPUT_IMPOSSIBLE:
+ pWorkerData->LogonInput = WKSTA_LOGON_INPUT_IMPOSSIBLE;
+ break;
+ default:
+ RplDump( ++RG_Assert, ("Flags=0x%x", Flags));
+ return( FALSE);
+ break;
+ }
+ switch ( Flags & WKSTA_FLAGS_MASK_DHCP) {
+ default:
+ case WKSTA_FLAGS_DHCP_TRUE:
+ pWorkerData->TcpIpAddress = INADDR_NONE;
+ pWorkerData->TcpIpSubnet = INADDR_NONE;
+ pWorkerData->TcpIpGateway = INADDR_NONE;
+ pWorkerData->DisableDhcp = WKSTA_DISABLE_DHCP_FALSE;
+ break;
+ case WKSTA_FLAGS_DHCP_FALSE:
+ CallB( JetRetrieveColumn( pSession->SesId, pSession->WkstaTableId,
+ WkstaTable[ WKSTA_TcpIpAddress].ColumnId, &pWorkerData->TcpIpAddress,
+ sizeof( pWorkerData->TcpIpAddress), &DataSize, 0, NULL));
+ CallB( JetRetrieveColumn( pSession->SesId, pSession->WkstaTableId,
+ WkstaTable[ WKSTA_TcpIpSubnet].ColumnId, &pWorkerData->TcpIpSubnet,
+ sizeof( pWorkerData->TcpIpSubnet), &DataSize, 0, NULL));
+ CallB( JetRetrieveColumn( pSession->SesId, pSession->WkstaTableId,
+ WkstaTable[ WKSTA_TcpIpGateway].ColumnId, &pWorkerData->TcpIpGateway,
+ sizeof( pWorkerData->TcpIpGateway), &DataSize, 0, NULL));
+ pWorkerData->DisableDhcp = WKSTA_DISABLE_DHCP_TRUE;
+ break;
+#if 0 // to help testing with old style records this is commented out
+ default:
+ RplDump( ++RG_Assert, ("Flags=0x%x", Flags));
+ return( FALSE);
+ break;
+#endif
+ }
+
+ CallB( JetRetrieveColumn( pSession->SesId, pSession->WkstaTableId,
+ WkstaTable[ WKSTA_TcpIpAddress].ColumnId, &pWorkerData->TcpIpAddress,
+ sizeof( pWorkerData->TcpIpAddress), &DataSize, 0, NULL));
+
+ CallB( JetRetrieveColumn( pSession->SesId, pSession->WkstaTableId,
+ WkstaTable[ WKSTA_TcpIpSubnet].ColumnId, &pWorkerData->TcpIpSubnet,
+ sizeof( pWorkerData->TcpIpSubnet), &DataSize, 0, NULL));
+
+ CallB( JetRetrieveColumn( pSession->SesId, pSession->WkstaTableId,
+ WkstaTable[ WKSTA_TcpIpGateway].ColumnId, &pWorkerData->TcpIpGateway,
+ sizeof( pWorkerData->TcpIpGateway), &DataSize, 0, NULL));
+
+ if ( !RplDbFindBoot( pSession, BootName, AdapterName)) {
+ RplDump( ++RG_Assert, ("FindBoot failed"));
+ return( FALSE);
+ }
+ CallB( JetRetrieveColumn( pSession->SesId, pSession->BootTableId,
+ BootTable[ BOOT_BbcFile].ColumnId, FilePath,
+ sizeof( FilePath), &DataSize, 0, NULL));
+ Length = DataSize / sizeof( WCHAR) - 1;
+ if ( DataSize > sizeof( FilePath) || FilePath[ Length] != 0) {
+ RplDump( ++RG_Assert, ( "BbcFile is bad %ws", FilePath));
+ return( FALSE);
+ }
+ pWorkerData->BbcFile = RplMemAlloc( pWorkerData->MemoryHandle,
+ RG_DirectoryLength * sizeof(WCHAR) + DataSize);
+ if ( pWorkerData->BbcFile == NULL) {
+ pWorkerData->EventStrings[ 0] = pWorkerData->WkstaName;
+ pWorkerData->EventId = NELOG_RplWkstaMemory;
+ return( FALSE);
+ }
+ memcpy( pWorkerData->BbcFile, RG_Directory, RG_DirectoryLength * sizeof(WCHAR));
+ memcpy( pWorkerData->BbcFile + RG_DirectoryLength, FilePath, DataSize);
+
+ CallB( JetRetrieveColumn( pSession->SesId, pSession->BootTableId,
+ BootTable[ BOOT_WindowSize].ColumnId, &pWorkerData->WindowSize,
+ sizeof( pWorkerData->WindowSize), &DataSize, 0, NULL));
+ CallB( JetRetrieveColumn( pSession->SesId, pSession->BootTableId,
+ BootTable[ BOOT_Flags].ColumnId, &Flags,
+ sizeof( Flags), &DataSize, 0, NULL));
+ switch( Flags & BOOT_FLAGS_MASK_FINAL_ACKNOWLEDGMENT) {
+ case BOOT_FLAGS_FINAL_ACKNOWLEDGMENT_TRUE:
+ pWorkerData->FinalAck = TRUE;
+ break;
+ case BOOT_FLAGS_FINAL_ACKNOWLEDGMENT_FALSE:
+ pWorkerData->FinalAck = FALSE;
+ break;
+ default:
+ RplDump( ++RG_Assert, ("Flags=0x%x", Flags));
+ return( FALSE);
+ break;
+ }
+ return( TRUE);
+}
+
+
+BOOL RplWorkerFillWksta( IN OUT PRPL_WORKER_DATA pWorkerData)
+{
+ PRPL_SESSION pSession = &RG_WorkerSession;
+ BOOL Success;
+
+ EnterCriticalSection( &RG_ProtectWorkerSession);
+ Call( JetBeginTransaction( pSession->SesId));
+ Success = RplDbFillWksta( pSession, pWorkerData);
+ JetCommitTransaction( pSession->SesId, 0);
+ LeaveCriticalSection( &RG_ProtectWorkerSession);
+ return( Success);
+}
+
+
+BOOL RplRequestHaveWksta( IN LPWSTR AdapterName)
+/*++
+
+Routine Description:
+
+ If it finds wksta record for input AdapterName then it returns TRUE.
+ Else, it tries to add the adapter record for input AdapterName, then
+ returns FALSE.
+
+ This code could be make more efficient by defining AdapterName
+ to be jet currency data (it is silly now taking wcslen of
+ AdapterName since it is a fixed length string.
+
+Return Value:
+ TRUE if wksta record for input AdapterName is found
+ FALSE otherwise
+
+--*/
+{
+ PRPL_SESSION pSession = &RG_RequestSession;
+ BOOL WkstaFound;
+ BOOL AdapterAdded;
+
+ EnterCriticalSection( &RG_ProtectRequestSession);
+ Call( JetBeginTransaction( pSession->SesId));
+
+ WkstaFound = RplDbFindWksta( pSession, AdapterName);
+
+ if ( !WkstaFound) {
+ //
+ // Failed to find it. Try to add adapter record then.
+ //
+ AdapterAdded = RplDbAddAdapterName( pSession, AdapterName);
+ } else {
+ AdapterAdded = FALSE;
+ }
+
+ if ( AdapterAdded) {
+ //
+ // We do not flush newly added adapter records since we do
+ // not want to slow down request threads.
+ //
+ Call( JetCommitTransaction( pSession->SesId, 0));
+ } else {
+ Call( JetRollback( pSession->SesId, JET_bitRollbackAll));
+ }
+ LeaveCriticalSection( &RG_ProtectRequestSession);
+ return( WkstaFound);
+}
+
+
+//
+// Trmplated from DHCP's database.c 12/13/95 JonN
+//
+DWORD
+RplStartJet500Conversion(
+ )
+/*++
+
+Routine Description:
+
+ This function starts the process to convert the jet200 version
+ database to jet500 version database. The Dhcp will terminate
+ after starting this process. When the conversion completes,
+ the dhcp service would be restarted by the convert process itself.
+
+Arguments:
+
+
+Return Value:
+
+ Windows Error.
+
+--*/
+{
+ DWORD ExLen;
+ STARTUPINFOA StartupInfo = {0};
+ PROCESS_INFORMATION ProcessInfo = {0};
+ CHAR szCmdLine[MAX_PATH];
+
+#define JET_CONV_MODULE_NAME "%SystemRoot%\\system32\\jetconv REMOTEBOOT /@"
+
+ ExLen = ExpandEnvironmentStringsA( JET_CONV_MODULE_NAME, szCmdLine, MAX_PATH );
+
+ if( (ExLen == 0) || (ExLen > MAX_PATH) ) {
+
+ if( ExLen == 0 ) {
+ return GetLastError();
+ }
+ else {
+ return ERROR_META_EXPANSION_TOO_LONG;
+ }
+
+ }
+
+
+ StartupInfo.cb = sizeof(STARTUPINFOA);
+
+ RplDump( RG_DebugLevel & RPL_DEBUG_REQUEST, ( "Calling %s\n",szCmdLine ));
+
+ if ( !CreateProcessA(
+ NULL,
+ szCmdLine,
+ NULL,
+ NULL,
+ FALSE,
+ DETACHED_PROCESS,
+// CREATE_NEW_CONSOLE,
+ NULL,
+ NULL,
+ &StartupInfo,
+ &ProcessInfo)
+ ) {
+
+ return GetLastError();
+
+
+ }
+
+ return ERROR_SUCCESS;
+}
diff --git a/private/net/svcdlls/rpl/server/database.h b/private/net/svcdlls/rpl/server/database.h
new file mode 100644
index 000000000..91811d5ff
--- /dev/null
+++ b/private/net/svcdlls/rpl/server/database.h
@@ -0,0 +1,33 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ database.h
+
+Abstract:
+
+ Exports from database.c
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 19 - November - 1993
+
+Environment:
+
+ User mode
+
+Revision History :
+
+--*/
+
+BOOL RplDbInit( VOID);
+VOID RplDbTerm( VOID);
+BOOL RplDbFindWksta(
+ IN PRPL_SESSION pSession,
+ IN LPWSTR AdapterName
+ );
+BOOL RplWorkerFillWksta( IN OUT PRPL_WORKER_DATA pWorkerData);
+BOOL RplRequestHaveWksta( IN LPWSTR AdapterName);
+
diff --git a/private/net/svcdlls/rpl/server/db.h b/private/net/svcdlls/rpl/server/db.h
new file mode 100644
index 000000000..572dc9f6b
--- /dev/null
+++ b/private/net/svcdlls/rpl/server/db.h
@@ -0,0 +1,263 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ db.h
+
+Abstract:
+
+ Main include file for JET database portion of RPL service.
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 19 - November - 1993
+
+Environment:
+
+ User mode
+
+Revision History :
+
+--*/
+
+
+//
+// jet call macros:
+// Call - jet call ignore error
+// CallR - jet call RETURN on error
+// CallB - jet call return BOOLEAN (false) on error
+// CallM - jet call return MAPPED error
+//
+
+#ifdef RPL_DEBUG
+#define Call( fn ) \
+ { \
+ int _JetError = fn; \
+ if ( _JetError != JET_errSuccess) { \
+ if ( _JetError < 0) { \
+ ++RG_Assert; \
+ } \
+ RplDebugPrint("File = %s, Line = %d, _JetError = %d\n", __FILE__, __LINE__, _JetError); \
+ } \
+ }
+
+#define CallR( fn ) \
+ { \
+ int _JetError = fn; \
+ if ( _JetError != JET_errSuccess) { \
+ if ( _JetError < 0) { \
+ ++RG_Assert; \
+ } \
+ RplDebugPrint("File = %s, Line = %d, _JetError = %d\n", __FILE__, __LINE__, _JetError); \
+ if ( _JetError < 0) { \
+ return; \
+ } \
+ } \
+ }
+
+#define CallB( fn ) \
+ { \
+ int _JetError = fn; \
+ if ( _JetError != JET_errSuccess) { \
+ if ( _JetError < 0) { \
+ ++RG_Assert; \
+ } \
+ RplDebugPrint("File = %s, Line = %d, _JetError = %d\n", __FILE__, __LINE__, _JetError); \
+ if ( _JetError < 0) { \
+ return( FALSE); \
+ } \
+ } \
+ }
+#define CallM( fn ) \
+ { \
+ int _JetError = fn; \
+ if ( _JetError != JET_errSuccess) { \
+ if ( _JetError < 0) { \
+ ++RG_Assert; \
+ } \
+ RplDebugPrint("File = %s, Line = %d, _JetError = %d\n", __FILE__, __LINE__, _JetError); \
+ if ( _JetError < 0) { \
+ return( NERR_RplInternal); \
+ } \
+ } \
+ }
+#define CallJ( fn ) \
+ { \
+ int _JetError = fn; \
+ if ( _JetError != JET_errSuccess) { \
+ if ( _JetError < 0) { \
+ ++RG_Assert; \
+ } \
+ RplDebugPrint("File = %s, Line = %d, _JetError = %d\n", __FILE__, __LINE__, _JetError); \
+ if ( _JetError < 0) { \
+ Error = NERR_RplInternal; \
+ goto cleanup; \
+ } \
+ } \
+ }
+
+#else
+#define Call( fn ) { if ( fn < 0) { NOTHING;} }
+#define CallR( fn ) { if ( fn < 0) { return;} }
+#define CallB( fn ) { if ( fn < 0) { return( FALSE);} }
+#define CallM( fn ) \
+ { \
+ int _JetError = fn; \
+ if ( _JetError < 0) { \
+ return( NERR_RplInternal); \
+ } \
+ }
+#define CallJ( fn ) \
+ { \
+ int _JetError = fn; \
+ if ( _JetError < 0) { \
+ Error = NERR_RplInternal; \
+ goto cleanup; \
+ } \
+ }
+#endif
+
+
+#define DEFAULT_BUFFER_SIZE (64 * 1024) // arbitrary choice
+
+typedef enum _RPL_TABLE_TAG {
+ ADAPTER_TABLE_TAG = 0,
+ BOOT_TABLE_TAG,
+ CONFIG_TABLE_TAG,
+ PROFILE_TABLE_TAG,
+ RESUME_TABLE_TAG,
+ VENDOR_TABLE_TAG,
+ WKSTA_TABLE_TAG
+} RPL_TABLE_TAG, *PRPL_TABLE_TAG;
+
+typedef struct _RPL_FILTER {
+ BOOL FindFirst;
+ DWORD VendorId;
+ DWORD BootNameSize;
+ WCHAR BootName[ 16];
+} RPL_FILTER, *PRPL_FILTER;
+
+
+//
+// Exports from resume.c
+//
+DWORD ResumeCreateTable( OUT PRPL_SESSION pSession);
+BOOL ResumeKeyGet(
+ IN PRPL_SESSION pSession,
+ IN DWORD ResumeHandle,
+ OUT PVOID ResumeValue,
+ IN OUT PDWORD pResumeSize
+ );
+BOOL ResumeKeySet(
+ IN PRPL_SESSION pSession,
+ IN DWORD ServerHandle,
+ IN PVOID ResumeValue,
+ IN DWORD ResumeSize,
+ OUT PDWORD pResumeHandle
+ );
+VOID ResumePrune(
+ IN PRPL_SESSION pSession,
+ IN DWORD ServerHandle
+ );
+
+//
+// Exports from boot.c
+//
+DWORD BootFilterFind(
+ IN PRPL_SESSION pSession,
+ IN OUT PRPL_FILTER pFilter,
+ IN OUT PBOOL pTableEnd
+ );
+BOOL BootFind(
+ IN PRPL_SESSION pSession,
+ IN PWCHAR BootName,
+ IN DWORD VendorId
+ );
+
+//
+// Exports from config.c
+//
+DWORD ConfigGetField(
+ IN PRPL_SESSION pSession,
+ IN DWORD FieldIndex,
+ OUT LPVOID * pData,
+ IN OUT LPINT pSpaceLeft
+ );
+
+DWORD ConfigSetInfo(
+ IN PRPL_SESSION pSession,
+ IN DWORD Level,
+ IN LPVOID Buffer,
+ OUT LPDWORD pErrorParameter
+ );
+
+//
+// Exports from profile.c
+//
+DWORD ProfileGetField(
+ IN PRPL_SESSION pSession,
+ IN DWORD FieldIndex,
+ OUT LPVOID * pData,
+ OUT LPINT pDataSize
+ );
+
+//
+// Exports from wksta.c
+//
+DWORD WkstaGetField(
+ IN PRPL_SESSION pSession,
+ IN DWORD FieldIndex,
+ OUT LPVOID * pData,
+ IN OUT LPINT pSpaceLeft
+ );
+
+BOOL WkstaFindFirst(
+ IN PRPL_SESSION pSession,
+ IN PWCHAR ProfileName
+ );
+
+//
+// Exports from disk.c
+//
+#define ADD_NEW_BRANCHES 0
+#define DEL_NEW_BRANCHES 1
+#define DEL_OLD_BRANCHES 2
+DWORD RplTreeCopy( IN PWCHAR Source, IN PWCHAR Target);
+DWORD RplTreeDelete( IN PWCHAR Target);
+DWORD RplMakeDir( IN PWCHAR Target);
+DWORD WkstaDiskAdd(
+ IN BOOL Doit,
+ IN PWCHAR WkstaName,
+ IN PWCHAR ProfileName,
+ IN DWORD Sharing
+ );
+DWORD WkstaDiskClone(
+ IN BOOL Doit,
+ IN PWCHAR SourceWkstaName,
+ IN PWCHAR TargetWkstaName
+ );
+DWORD WkstaDiskSet(
+ IN DWORD Action,
+ IN PWCHAR WkstaName,
+ IN PWCHAR ProfileName,
+ IN DWORD Sharing,
+ IN PWCHAR TargetWkstaName,
+ IN PWCHAR TargetProfileName,
+ IN DWORD TargetSharing
+ );
+DWORD ProfileDiskAdd(
+ IN BOOL Doit,
+ IN PWCHAR ProfileName,
+ IN PWCHAR DirName,
+ IN PWCHAR DirName2,
+ IN PWCHAR DirName3,
+ IN PWCHAR DirName4
+ );
+DWORD ProfileDiskClone(
+ IN BOOL Doit,
+ IN PWCHAR SourceProfileName,
+ IN PWCHAR TargetProfileName
+ );
+
diff --git a/private/net/svcdlls/rpl/server/dblib.c b/private/net/svcdlls/rpl/server/dblib.c
new file mode 100644
index 000000000..b95f88f90
--- /dev/null
+++ b/private/net/svcdlls/rpl/server/dblib.c
@@ -0,0 +1,386 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ dblib.c
+
+Abstract:
+
+ Common api worker routines.
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 19 - November - 1993
+
+Revision History:
+
+--*/
+
+#include "local.h"
+#include "rpldb.h"
+#include "db.h"
+#include "adapter.h"
+#include "boot.h"
+#include "config.h"
+#include "profile.h"
+#include "vendor.h"
+#include "wksta.h"
+#include "dblib.h"
+
+
+BOOL RplScan(
+ IN PRPL_SESSION pSession,
+ IN JET_TABLEID TableId,
+ IN BOOL FindNextBoot,
+ IN OUT PRPL_FILTER pFilter,
+ IN OUT PBOOL pTableEnd
+ )
+/*++
+ Set currency to the next profile or config.
+--*/
+{
+ DWORD Error;
+ JET_ERR JetError;
+
+ for ( ; ;) {
+ if ( FindNextBoot == TRUE) {
+ Error = BootFilterFind( pSession, pFilter, pTableEnd);
+ if ( Error != NO_ERROR) {
+ return( FALSE);
+ }
+ if ( *pTableEnd == TRUE) {
+ return( TRUE);
+ }
+ CallB( JetMakeKey( pSession->SesId, TableId, pFilter->BootName, pFilter->BootNameSize, JET_bitNewKey));
+ }
+ JetError = JetSeek( pSession->SesId, TableId, JET_bitSeekGT);
+ if ( JetError != JET_errSuccess) {
+ *pTableEnd = TRUE;
+ return( TRUE);
+ }
+ //
+ // Verify that current record has the desired boot block & at the
+ // same time set index range to be used with JetMove( Next).
+ //
+ CallB( JetMakeKey( pSession->SesId, TableId, pFilter->BootName, pFilter->BootNameSize, JET_bitNewKey));
+ JetError = JetSetIndexRange( pSession->SesId, TableId,
+ JET_bitRangeInclusive | JET_bitRangeUpperLimit);
+ if ( JetError == JET_errSuccess) {
+ return( TRUE);
+ }
+ //
+ // There are no records for current BootName.
+ // Get next BootName for VendorId.
+ //
+ FindNextBoot = TRUE;
+ }
+}
+
+
+BOOL RplFilterFirst(
+ IN PRPL_SESSION pSession,
+ IN RPL_TABLE_TAG TableTag,
+ IN OUT PRPL_FILTER pFilter OPTIONAL,
+ IN LPDWORD pResumeHandle,
+ OUT PBOOL pTableEnd
+ )
+/*++
+ Set currency to the first record for given vendor id, following
+ the record described by the resume handle.
+--*/
+{
+#define MAX_RESUME_VALUE_SIZE \
+ (RPL_MAX_BOOT_NAME_SIZE + \
+ max( RPL_MAX_PROFILE_NAME_SIZE, RPL_MAX_CONFIG_NAME_SIZE))
+#define MAX_NAME_LENGTH \
+ (JETBUG_STRING_LENGTH + \
+ max( RPL_MAX_PROFILE_NAME_LENGTH, RPL_MAX_CONFIG_NAME_LENGTH))
+ BYTE ResumeValue[ MAX_RESUME_VALUE_SIZE];
+ DWORD ResumeSize;
+ DWORD Error;
+ JET_ERR JetError;
+ DWORD NameSize;
+ WCHAR Name[ MAX_NAME_LENGTH + 1];
+ PCHAR Index;
+ JET_TABLEID TableId;
+
+ *pTableEnd = FALSE;
+
+ switch ( TableTag) {
+ case ADAPTER_TABLE_TAG:
+ TableId = pSession->AdapterTableId;
+ Index = ADAPTER_INDEX_AdapterName;
+ break;
+ case BOOT_TABLE_TAG:
+ TableId = pSession->BootTableId;
+ Index = BOOT_INDEX_BootName;
+ break;
+ case CONFIG_TABLE_TAG:
+ TableId = pSession->ConfigTableId;
+ Index = pFilter == NULL ? CONFIG_INDEX_ConfigName : CONFIG_INDEX_BootNameConfigName;
+ break;
+ case PROFILE_TABLE_TAG:
+ TableId = pSession->ProfileTableId;
+ Index = pFilter == NULL ? PROFILE_INDEX_ProfileName : PROFILE_INDEX_BootNameProfileName;
+ break;
+ case VENDOR_TABLE_TAG:
+ TableId = pSession->VendorTableId;
+ Index = VENDOR_INDEX_VendorName;
+ break;
+ case WKSTA_TABLE_TAG:
+ TableId = pSession->WkstaTableId;
+ Index = WKSTA_INDEX_WkstaName;
+ break;
+ default:
+ RPL_ASSERT( FALSE);
+ }
+
+ CallB( JetSetCurrentIndex( pSession->SesId, TableId, Index));
+
+ //
+ // The call to move to the beginning of the table is not redundant.
+ // E.g. JET_errNoCurrentRecord will be returned in case of empty table.
+ //
+ JetError = JetMove( pSession->SesId, TableId, JET_MoveFirst, 0);
+ if ( JetError < 0) {
+ if ( JetError == JET_errNoCurrentRecord) {
+ *pTableEnd = TRUE;
+ return( TRUE);
+ } else {
+ RplDump( ++RG_Assert, ("JetError=%d", JetError));
+ return( FALSE);
+ }
+ }
+
+ if ( (ARGUMENT_PRESENT( pResumeHandle)) && *pResumeHandle != 0) {
+ ResumeSize = sizeof( ResumeValue);
+ if ( !ResumeKeyGet( pSession, *pResumeHandle, ResumeValue, &ResumeSize)) {
+ return( FALSE);
+ }
+ if ( pFilter == NULL) {
+ CallB( JetMakeKey( pSession->SesId, TableId, ResumeValue, ResumeSize, JET_bitNewKey));
+ CallB( JetSeek( pSession->SesId, TableId, JET_bitSeekGT));
+ return( TRUE);
+ }
+ pFilter->BootNameSize = (wcslen( (PWCHAR)ResumeValue) + 1) * sizeof(WCHAR);
+ memcpy( pFilter->BootName, ResumeValue, pFilter->BootNameSize);
+ NameSize = (wcslen( (PWCHAR)(ResumeValue + pFilter->BootNameSize)) + 1)
+ * sizeof(WCHAR);
+ memcpy( Name, ResumeValue + pFilter->BootNameSize, NameSize);
+ RPL_ASSERT( ResumeSize == pFilter->BootNameSize + NameSize);
+ } else {
+ if ( pFilter == NULL) {
+ return( TRUE);
+ }
+ pFilter->BootNameSize = 0; // scan from the beginning
+ Error = BootFilterFind( pSession, pFilter, pTableEnd);
+ if ( Error != NO_ERROR) {
+ return( FALSE);
+ }
+ if ( *pTableEnd == TRUE) {
+ return( TRUE);
+ }
+ NameSize = 0;
+ }
+ CallB( JetMakeKey( pSession->SesId, TableId, pFilter->BootName, pFilter->BootNameSize, JET_bitNewKey));
+ if ( NameSize != 0) {
+#ifdef RPL_JETBUG
+ memcpy( (PBYTE)Name + NameSize, JETBUG_STRING, JETBUG_STRING_SIZE);
+ NameSize += JETBUG_STRING_LENGTH * sizeof(WCHAR);
+#endif
+ CallB( JetMakeKey( pSession->SesId, TableId, Name, NameSize, 0));
+ }
+ return( RplScan( pSession, TableId, FALSE, pFilter, pTableEnd));
+}
+
+
+VOID RplFilterSave(
+ IN PRPL_SESSION pSession,
+ IN DWORD ServerHandle,
+ IN PRPL_FILTER pFilter,
+ IN PWCHAR Name,
+ IN PDWORD pResumeHandle
+ )
+{
+ BYTE ResumeBuffer[ 30 * sizeof(WCHAR)]; // BUGBUG hardcoding
+ DWORD ResumeSize;
+ DWORD NameSize;
+ if ( pFilter != NULL) {
+ ResumeSize = pFilter->BootNameSize;
+ memcpy( ResumeBuffer, pFilter->BootName, ResumeSize);
+ } else {
+ ResumeSize = 0;
+ }
+ NameSize = ( wcslen( Name) + 1) * sizeof(WCHAR);
+ memcpy( ResumeBuffer + ResumeSize, Name, NameSize);
+ ResumeSize += NameSize;
+ (VOID)ResumeKeySet( pSession, ServerHandle, ResumeBuffer, ResumeSize, pResumeHandle);
+}
+
+
+BOOL RplFilterNext(
+ IN PRPL_SESSION pSession,
+ IN JET_TABLEID TableId,
+ IN OUT PRPL_FILTER pFilter,
+ OUT PBOOL pTableEnd
+ )
+{
+ JET_ERR JetError;
+
+ JetError = JetMove( pSession->SesId, TableId, JET_MoveNext, 0);
+ if ( JetError == JET_errSuccess) {
+ return( TRUE);
+ }
+ if ( pFilter == NULL) {
+ RplDump( RG_DebugLevel & RPL_DEBUG_DB, (
+ "FilterNext: TableId=0x%x, JetError=%d", TableId, JetError));
+ *pTableEnd = TRUE;
+ return( TRUE); // assume end of table
+ } else {
+ RplDump( RG_DebugLevel & RPL_DEBUG_DB, (
+ "FilterNext: TableId=0x%x, BootName=%ws, JetError=%d",
+ TableId, pFilter->BootName, JetError));
+ return( RplScan( pSession, TableId, TRUE, pFilter, pTableEnd));
+ }
+}
+
+
+BOOL RplFind(
+ IN PRPL_SESSION pSession,
+ IN RPL_TABLE_TAG TableTag,
+ IN PWCHAR Name
+ )
+{
+ JET_ERR JetError;
+ PCHAR Index;
+ JET_TABLEID TableId;
+
+ switch ( TableTag) {
+ case ADAPTER_TABLE_TAG:
+ TableId = pSession->AdapterTableId;
+ Index = ADAPTER_INDEX_AdapterName;
+ break;
+ case CONFIG_TABLE_TAG:
+ TableId = pSession->ConfigTableId;
+ Index = CONFIG_INDEX_ConfigName;
+ break;
+ case PROFILE_TABLE_TAG:
+ TableId = pSession->ProfileTableId;
+ Index = PROFILE_INDEX_ProfileName;
+ break;
+ case VENDOR_TABLE_TAG:
+ TableId = pSession->VendorTableId;
+ Index = VENDOR_INDEX_VendorName;
+ break;
+ case WKSTA_TABLE_TAG:
+ TableId = pSession->WkstaTableId;
+ Index = WKSTA_INDEX_WkstaName;
+ break;
+ default:
+ RPL_RETURN( FALSE);
+ }
+ CallB( JetSetCurrentIndex( pSession->SesId, TableId, Index));
+ CallB( JetMakeKey( pSession->SesId, TableId, Name, ( wcslen( Name) + 1) * sizeof(WCHAR), JET_bitNewKey));
+ JetError = JetSeek( pSession->SesId, TableId, JET_bitSeekEQ);
+ if ( JetError < 0) {
+#ifdef RPL_DEBUG
+ //
+ // JET_errNoCurrentRecord will be returned in case of empty table.
+ //
+ if ( JetError != JET_errRecordNotFound
+ && JetError != JET_errNoCurrentRecord) {
+ RplDump( ++RG_Assert, ("JetError=%d", JetError));
+ }
+#endif
+ return( FALSE);
+ }
+ return( TRUE);
+}
+
+
+BOOL RplFindByField(
+ IN PRPL_SESSION pSession,
+ IN RPL_TABLE_TAG TableTag,
+ IN PCHAR IndexName,
+ IN PWCHAR FieldName
+ )
+/*++
+ Returns TRUE if it can find a record (the first record) that has
+ FieldName as a value. Returns FALSE otherwise.
+
+ We cannot use SetIndexRange technique here because FieldName is only
+ the first part of an index & we would never find a match.
+--*/
+{
+ DWORD FieldNameSize;
+ JET_ERR JetError;
+ BYTE Data[ 300];
+ DWORD DataSize;
+ JET_TABLEID TableId;
+ JET_COLUMNID ColumnId;
+
+ switch ( TableTag) {
+ case CONFIG_TABLE_TAG:
+ TableId = pSession->ConfigTableId;
+ if ( strcmp( IndexName, CONFIG_INDEX_BootNameConfigName) == 0) {
+ ColumnId = ProfileTable[ CONFIG_BootName].ColumnId;
+ } else {
+ RPL_RETURN( FALSE);
+ }
+ break;
+ case PROFILE_TABLE_TAG:
+ TableId = pSession->ProfileTableId;
+ if ( strcmp( IndexName, PROFILE_INDEX_ConfigNameProfileName) == 0) {
+ ColumnId = ProfileTable[ PROFILE_ConfigName].ColumnId;
+ } else if ( strcmp( IndexName, PROFILE_INDEX_BootNameProfileName) == 0) {
+ ColumnId = ProfileTable[ PROFILE_BootName].ColumnId;
+ } else {
+ RPL_RETURN( FALSE);
+ }
+ break;
+ case WKSTA_TABLE_TAG:
+ TableId = pSession->WkstaTableId;
+ if ( strcmp( IndexName, WKSTA_INDEX_ProfileNameWkstaName) == 0) {
+ ColumnId = WkstaTable[ WKSTA_ProfileName].ColumnId;
+ } else if ( strcmp( IndexName, WKSTA_INDEX_BootNameWkstaName) == 0) {
+ ColumnId = WkstaTable[ WKSTA_BootName].ColumnId;
+ } else {
+ RPL_RETURN( FALSE);
+ }
+ break;
+ default:
+ RPL_RETURN( FALSE);
+ }
+
+ CallB( JetSetCurrentIndex( pSession->SesId, TableId, IndexName));
+ FieldNameSize = ( wcslen( FieldName) + 1) * sizeof(WCHAR);
+ CallB( JetMakeKey( pSession->SesId, TableId, FieldName, FieldNameSize, JET_bitNewKey));
+ JetError = JetSeek( pSession->SesId, TableId, JET_bitSeekGE);
+ if ( JetError < 0) {
+ return( FALSE);
+ }
+ CallB( JetRetrieveColumn( pSession->SesId, TableId, ColumnId, Data,
+ sizeof( Data), &DataSize, 0, NULL));
+ if ( FieldNameSize != DataSize) {
+ return( FALSE);
+ }
+ if ( memcmp( FieldName, Data, DataSize) != 0) {
+ return( FALSE);
+ }
+ return( TRUE); // we found record using this field
+}
+
+
+DWORD AdapterNameToVendorId( IN PWCHAR AdapterName)
+{
+ WCHAR VendorName[ RPL_VENDOR_NAME_LENGTH + 1];
+ //
+ // Yes, we could zero the char in AdapterName, calculate VendorId,
+ // then restore char. But this is more robust though.
+ //
+ memcpy( VendorName, AdapterName, RPL_VENDOR_NAME_LENGTH*sizeof(WCHAR));
+ VendorName[ RPL_VENDOR_NAME_LENGTH] = 0;
+ return( wcstoul( VendorName, NULL, 16));
+}
diff --git a/private/net/svcdlls/rpl/server/dblib.h b/private/net/svcdlls/rpl/server/dblib.h
new file mode 100644
index 000000000..463a4dea3
--- /dev/null
+++ b/private/net/svcdlls/rpl/server/dblib.h
@@ -0,0 +1,66 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ dblib.h
+
+Abstract:
+
+ Exports from dblib.c
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 19 - November - 1993
+
+Environment:
+
+ User mode
+
+Revision History :
+
+--*/
+
+
+BOOL RplScan(
+ IN PRPL_SESSION pSession,
+ IN JET_TABLEID TableId,
+ IN BOOL FindNextBoot,
+ IN OUT PRPL_FILTER pFilter,
+ IN OUT PBOOL pTableEnd
+ );
+BOOL RplFilterFirst(
+ IN PRPL_SESSION pSession,
+ IN RPL_TABLE_TAG TableTag,
+ IN OUT PRPL_FILTER pFilter,
+ IN LPDWORD pResumeHandle,
+ OUT PBOOL pTableEnd
+ );
+BOOL RplFilterNext(
+ IN PRPL_SESSION pSession,
+ IN JET_TABLEID TableId,
+ IN OUT PRPL_FILTER pFilter,
+ OUT PBOOL pTableEnd
+ );
+VOID RplFilterSave(
+ IN PRPL_SESSION pSession,
+ IN DWORD ServerHandle,
+ IN PRPL_FILTER pFilter,
+ IN PWCHAR Name,
+ IN PDWORD pResumeHandle
+ );
+BOOL RplFind(
+ IN PRPL_SESSION pSession,
+ IN RPL_TABLE_TAG TableTag,
+ IN PWCHAR Name
+ );
+BOOL RplFindByField(
+ IN PRPL_SESSION pSession,
+ IN RPL_TABLE_TAG TableTag,
+ IN PCHAR IndexName,
+ IN PWCHAR FieldName
+ );
+DWORD AdapterNameToVendorId( IN PWCHAR AdapterName);
+
+
diff --git a/private/net/svcdlls/rpl/server/debug.c b/private/net/svcdlls/rpl/server/debug.c
new file mode 100644
index 000000000..e27c49bd7
--- /dev/null
+++ b/private/net/svcdlls/rpl/server/debug.c
@@ -0,0 +1,141 @@
+/*++
+
+Copyright (c) 1991-1993 Microsoft Corporation
+
+Module Name:
+
+ debug.c
+
+Abstract:
+
+ Debugging module. RpldDebugPrint() was adapted from TelnetDebugPrint()
+ in net\sockets\tcpcmd\telnet\debug.c.
+
+Author:
+
+ Vladimir Z. Vulovic 27 - July - 1993
+
+Environment:
+
+ User mode
+
+Revision History :
+
+--*/
+
+#include "local.h"
+#include "debug.h"
+
+#ifdef RPL_DEBUG
+
+#define RPL_PROMPT "[Rpl] "
+#define DEBUG_RECORD_SIZE 0x80
+#define DEBUG_BUFFER_SIZE 0x20000 // 2*64k, must be a power of 2
+#define DEBUG_BUFFER_MASK (DEBUG_BUFFER_SIZE - 1)
+
+// int RG_DebugLevel = -1; // max debugging
+// int RG_DebugLevel = 0; // no debugging, for public use
+
+int RG_DebugLevel; // needed by other modules
+int RG_Assert; // needed by other modules
+
+PCHAR RG_DebugBuffer;
+CRITICAL_SECTION RG_ProtectDebug;
+DWORD RG_DebugOffset;
+//
+// This buffer is used even when RG_DebugBuffer is not NULL.
+//
+char RG_DebugPublicBuffer[ 120];
+
+
+VOID RplDebugDelete( VOID)
+{
+ if ( RG_DebugBuffer != NULL) {
+ (VOID)GlobalFree( RG_DebugBuffer);
+ }
+ DeleteCriticalSection( &RG_ProtectDebug);
+}
+
+#define RPL_DEFAULT_DEBUG_LEVEL ( RPL_DEBUG_FLOW | \
+ RPL_DEBUG_SERVER | \
+ RPL_DEBUG_MAJOR )
+
+#define RPL_MAXIMUM_DEBUG_LEVEL (-1L)
+
+
+
+VOID RplDebugInitialize( VOID)
+{
+ //
+ // Set RG_Assert to 0. Without this the very first use of
+ // RplDump() will lead to an assert.
+ //
+ RG_Assert = 0;
+
+// RG_DebugLevel = RPL_DEFAULT_DEBUG_LEVEL;
+ RG_DebugLevel = RPL_MAXIMUM_DEBUG_LEVEL & ~RPL_DEBUG_MEMALLOC
+ & ~RPL_DEBUG_FLOWINIT & ~RPL_DEBUG_MISC;
+// RG_DebugLevel = RPL_MAXIMUM_DEBUG_LEVEL;
+// RG_DebugLevel = RPL_DEBUG_MISC;
+
+
+ RG_DebugBuffer = (PCHAR)GlobalAlloc(
+ GMEM_FIXED,
+ DEBUG_BUFFER_SIZE + DEBUG_RECORD_SIZE
+ );
+ if ( RG_DebugBuffer != NULL) {
+ RtlFillMemory(
+ RG_DebugBuffer,
+ DEBUG_BUFFER_SIZE + DEBUG_RECORD_SIZE,
+ '*'
+ );
+ RG_DebugOffset = 0;
+ }
+ memcpy( RG_DebugPublicBuffer, RPL_PROMPT, sizeof( RPL_PROMPT) -1);
+
+ InitializeCriticalSection( &RG_ProtectDebug);
+}
+
+
+VOID _CRTAPI1 RplDebugPrint( CONST CHAR * format, ...)
+{
+ va_list arglist;
+
+ va_start( arglist, format);
+
+ EnterCriticalSection( &RG_ProtectDebug);
+
+ if ( RG_Assert == 0 && RG_DebugBuffer != NULL) {
+ RtlZeroMemory( RG_DebugBuffer + RG_DebugOffset, DEBUG_RECORD_SIZE);
+ vsprintf( RG_DebugBuffer + RG_DebugOffset, format, arglist);
+ RG_DebugOffset += DEBUG_RECORD_SIZE;
+ RG_DebugOffset &= DEBUG_BUFFER_MASK;
+ } else {
+ vsprintf( RG_DebugPublicBuffer + sizeof( RPL_PROMPT) - 1, format, arglist);
+ DbgPrint( "%s\n", RG_DebugPublicBuffer); // for kernel debugger
+ printf( "%s\n", RG_DebugPublicBuffer); // for user debugger
+ }
+
+ if ( RG_Assert != 0) {
+ ASSERT( FALSE); // break for checked build
+ DbgPrint( "[RplSvc] Running checked version of service on a free build.\n");
+ printf( "[RplSvc] Running checked version of service on a free build.\n");
+ }
+ while ( RG_Assert != 0) {
+ DbgPrint( "[RplSvc] Debug, then reset RG_Assert to 0.\n");
+ printf( "[RplSvc] Debug, then reset RG_Assert to 0.\n");
+ DbgUserBreakPoint();
+ Sleep( RG_Assert * 30000); // sleep for 30 seconds
+ }
+
+ LeaveCriticalSection( &RG_ProtectDebug);
+
+ va_end( arglist);
+
+} // RplDebugPrint
+
+
+#endif // RPL_DEBUG
+
+
+
diff --git a/private/net/svcdlls/rpl/server/debug.h b/private/net/svcdlls/rpl/server/debug.h
new file mode 100644
index 000000000..31706c1fe
--- /dev/null
+++ b/private/net/svcdlls/rpl/server/debug.h
@@ -0,0 +1,28 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ debug.h
+
+Abstract:
+
+ Exports from debug.c
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 19 - November - 1993
+
+Environment:
+
+ User mode
+
+Revision History :
+
+--*/
+
+#ifdef RPL_DEBUG
+VOID RplDebugInitialize( VOID);
+VOID RplDebugDelete( VOID);
+#endif // RPL_DEBUG
diff --git a/private/net/svcdlls/rpl/server/disk.c b/private/net/svcdlls/rpl/server/disk.c
new file mode 100644
index 000000000..1c53f8c52
--- /dev/null
+++ b/private/net/svcdlls/rpl/server/disk.c
@@ -0,0 +1,318 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ disk.c
+
+Abstract:
+
+ Routines changing disk files (other than the database).
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 19 - November - 1993
+
+Revision History:
+
+--*/
+
+#include "local.h"
+#include "db.h"
+
+
+DWORD WkstaDiskSet(
+ IN DWORD Action,
+ IN PWCHAR WkstaName,
+ IN PWCHAR ProfileName,
+ IN DWORD Sharing,
+ IN PWCHAR TargetWkstaName,
+ IN PWCHAR TargetProfileName,
+ IN DWORD TargetSharing
+ )
+{
+ WCHAR Target[ MAX_PATH + 1];
+ WCHAR Source[ MAX_PATH + 1];
+ DWORD Error;
+
+ if ( TargetWkstaName != NULL) {
+ if ( TargetSharing = 0) {
+ TargetSharing = Sharing;
+ }
+ if ( TargetProfileName == NULL) {
+ TargetProfileName = ProfileName;
+ }
+ Error = WkstaDiskAdd( Action == ADD_NEW_BRANCHES,
+ Action == DEL_OLD_BRANCHES ? WkstaName : TargetWkstaName,
+ TargetProfileName, TargetSharing);
+ if ( Action != ADD_NEW_BRANCHES) {
+ return( Error);
+ }
+ swprintf( Target, L"%ws\\TMPFILES\\%ws", RG_DiskRplfiles, TargetWkstaName);
+ swprintf( Source, L"%ws\\TMPFILES\\%ws", RG_DiskRplfiles, WkstaName);
+ Error = RplTreeCopy( Source, Target);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ swprintf( Target, L"%ws\\MACHINES\\%ws\\DATA", RG_DiskRplfiles, TargetWkstaName);
+ swprintf( Source, L"%ws\\MACHINES\\%ws\\DATA", RG_DiskRplfiles, WkstaName);
+ Error = RplTreeCopy( Source, Target);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ swprintf( Target, L"%ws\\MACHINES\\%ws\\LOGS", RG_DiskRplfiles, TargetWkstaName);
+ swprintf( Source, L"%ws\\MACHINES\\%ws\\LOGS", RG_DiskRplfiles, WkstaName);
+ Error = RplTreeCopy( Source, Target);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ } else if ( TargetProfileName != NULL) {
+ DWORD Length;
+ if ( TargetSharing == 0) {
+ TargetSharing = Sharing;
+ }
+ swprintf( Target, L"%ws\\MACHINES\\%ws\\%ws", RG_DiskRplfiles,
+ WkstaName,
+ Action == DEL_OLD_BRANCHES ? ProfileName : TargetProfileName);
+ Error = RplTreeDelete( Target);
+ if ( Action != ADD_NEW_BRANCHES) {
+ return( Error);
+ }
+ Error = RplMakeDir( Target);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ Length = wcslen( Target);
+ wcscat( Target + Length, L"\\WKSTA");
+ swprintf( Source, L"%ws\\PROFILES\\%ws\\WKSTA.PRO", RG_DiskRplfiles, TargetProfileName);
+ Error = RplTreeCopy( Source, Target);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ if ( TargetSharing == WKSTA_FLAGS_SHARING_TRUE) {
+ return( Error);
+ }
+ swprintf( Target + Length, L"\\PRO");
+ swprintf( Source, L"%ws\\PROFILES\\%ws", RG_DiskRplfiles, ProfileName);
+ Error = RplTreeCopy( Source, Target);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ } else if ( TargetSharing != 0) {
+ BOOL DeletePro;
+ BOOL CreatePro; // FALSE whenever DeletePro is FALSE
+ switch( Action) {
+ case ADD_NEW_BRANCHES:
+ DeletePro = TRUE;
+ CreatePro = (TargetSharing == WKSTA_FLAGS_SHARING_FALSE);
+ break;
+ case DEL_NEW_BRANCHES:
+ DeletePro = (TargetSharing == WKSTA_FLAGS_SHARING_FALSE);
+ CreatePro = FALSE;
+ break;
+ case DEL_OLD_BRANCHES:
+ DeletePro = (Sharing ==WKSTA_FLAGS_SHARING_FALSE);
+ CreatePro = FALSE;
+ }
+ if ( DeletePro == FALSE) {
+ RPL_ASSERT( CreatePro == FALSE);
+ return( NO_ERROR);
+ }
+ swprintf( Target, L"%ws\\MACHINES\\%ws\\%ws\\PRO", RG_DiskRplfiles,
+ WkstaName, ProfileName);
+ Error = RplTreeDelete( Target);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ if ( CreatePro == FALSE) {
+ return( NO_ERROR);
+ }
+ swprintf( Source, L"%ws\\PROFILES\\%ws", RG_DiskRplfiles, ProfileName);
+ Error = RplTreeCopy( Source, Target);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ }
+ return( NO_ERROR);
+}
+
+
+DWORD WkstaDiskAdd(
+ IN BOOL Doit,
+ IN PWCHAR WkstaName,
+ IN PWCHAR ProfileName,
+ IN DWORD Sharing
+ )
+{
+ WCHAR Target[ MAX_PATH + 1];
+ WCHAR Source[ MAX_PATH + 1];
+ DWORD Error;
+
+ swprintf( Target, L"%ws\\TMPFILES\\%ws", RG_DiskRplfiles, WkstaName);
+ RplTreeDelete( Target);
+ if ( Doit) {
+ Error = RplMakeDir( Target);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ }
+ swprintf( Target, L"%ws\\MACHINES\\%ws", RG_DiskRplfiles, WkstaName);
+ RplTreeDelete( Target);
+ if ( Doit) {
+ DWORD Length = wcslen( Target);
+ Error = RplMakeDir( Target);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ //
+ // Note that here we have "MACHINE" - unlike "MACHINES" above. What a fun!
+ //
+ swprintf( Source, L"%ws\\CONFIGS\\MACHINE", RG_DiskRplfiles);
+ Error = RplTreeCopy( Source, Target);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ //
+ // Currently RplTreeCopy fails on missing links. Therefore,
+ // we must first create directory below before RplTreeCopy
+ // can be called.
+ //
+ swprintf( Target + Length, L"\\%ws", ProfileName);
+ Error = RplMakeDir( Target);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ wcscat( Target + Length, L"\\WKSTA");
+ swprintf( Source, L"%ws\\PROFILES\\%ws\\WKSTA.PRO", RG_DiskRplfiles, ProfileName);
+ Error = RplTreeCopy( Source, Target);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ if ( Sharing == WKSTA_FLAGS_SHARING_TRUE) {
+ return( Error);
+ }
+ swprintf( Target + Length, L"\\%ws\\PRO", ProfileName);
+ swprintf( Source, L"%ws\\PROFILES\\%ws", RG_DiskRplfiles, ProfileName);
+ Error = RplTreeCopy( Source, Target);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ }
+ return( NO_ERROR);
+}
+
+
+DWORD WkstaDiskClone(
+ IN BOOL Doit,
+ IN PWCHAR SourceWkstaName,
+ IN PWCHAR TargetWkstaName
+ )
+{
+ WCHAR Target[ MAX_PATH + 1];
+ WCHAR Source[ MAX_PATH + 1];
+ DWORD Error;
+
+ //
+ // TMPFILES tree is never cloned
+ //
+ swprintf( Target, L"%ws\\TMPFILES\\%ws", RG_DiskRplfiles, TargetWkstaName);
+ RplTreeDelete( Target);
+ if ( Doit) {
+ Error = RplMakeDir( Target);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ }
+
+ //
+ // MACHINES tree is cloned except for the DATA subtree
+ //
+ swprintf( Target, L"%ws\\MACHINES\\%ws", RG_DiskRplfiles, TargetWkstaName);
+ RplTreeDelete( Target);
+ if ( Doit) {
+ swprintf( Source, L"%ws\\MACHINES\\%ws", RG_DiskRplfiles, SourceWkstaName);
+ Error = RplTreeCopy( Source, Target);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ wcscat( Target, L"\\DATA");
+ RplTreeDelete( Target);
+ Error = RplMakeDir( Target);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ }
+ return( NO_ERROR);
+}
+
+
+
+DWORD ProfileDiskAdd(
+ IN BOOL Doit,
+ IN PWCHAR ProfileName,
+ IN PWCHAR DirName,
+ IN PWCHAR DirName2,
+ IN PWCHAR DirName3,
+ IN PWCHAR DirName4
+ )
+{
+ WCHAR Target[ MAX_PATH + 1];
+ WCHAR Source[ MAX_PATH + 1];
+ DWORD Error;
+
+ swprintf( Target, L"%ws\\PROFILES\\%ws", RG_DiskRplfiles, ProfileName);
+ RplTreeDelete( Target);
+ if ( Doit) {
+ swprintf( Source, L"%ws\\CONFIGS\\%ws", RG_DiskRplfiles, DirName);
+ Error = RplTreeCopy( Source, Target);
+ if ( Error != NO_ERROR || DirName2 == NULL || *DirName2 == 0) {
+ return( Error);
+ }
+ swprintf( Source, L"%ws\\CONFIGS\\%ws", RG_DiskRplfiles, DirName2);
+ Error = RplTreeCopy( Source, Target);
+ if ( Error != NO_ERROR || DirName3 == NULL || *DirName3 == 0) {
+ return( Error);
+ }
+ swprintf( Source, L"%ws\\CONFIGS\\%ws", RG_DiskRplfiles, DirName3);
+ Error = RplTreeCopy( Source, Target);
+ if ( Error != NO_ERROR || DirName4 == NULL || *DirName4 == 0) {
+ return( Error);
+ }
+ swprintf( Source, L"%ws\\CONFIGS\\%ws", RG_DiskRplfiles, DirName4);
+ Error = RplTreeCopy( Source, Target);
+ return( Error);
+ }
+ return( NO_ERROR);
+}
+
+
+
+DWORD ProfileDiskClone(
+ IN BOOL Doit,
+ IN PWCHAR SourceProfileName,
+ IN PWCHAR TargetProfileName
+ )
+{
+ WCHAR Target[ MAX_PATH + 1];
+ WCHAR Source[ MAX_PATH + 1];
+ DWORD Error;
+
+ swprintf( Target, L"%ws\\PROFILES\\%ws", RG_DiskRplfiles, TargetProfileName);
+ RplTreeDelete( Target);
+ if ( Doit) {
+ Error = RplMakeDir( Target);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ swprintf( Source, L"%ws\\PROFILES\\%ws", RG_DiskRplfiles, SourceProfileName);
+ Error = RplTreeCopy( Source, Target);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ }
+ return( NO_ERROR);
+}
+
+
+
diff --git a/private/net/svcdlls/rpl/server/fitfile.c b/private/net/svcdlls/rpl/server/fitfile.c
new file mode 100644
index 000000000..9aa421920
--- /dev/null
+++ b/private/net/svcdlls/rpl/server/fitfile.c
@@ -0,0 +1,442 @@
+/*++
+
+Copyright (c) 1987-1993 Microsoft Corporation
+
+Module Name:
+
+ fitfile.c
+
+Abstract:
+
+ Processes the FIT file referenced in workstation record, eliminating
+ the following keywords:
+
+ (DOSFIT) defines the UNC path of the temporary files used
+ by the new DOS boot (this will probably be removed very soon).
+
+ (CNAME) the netbios cname of the workstation
+
+ (PROFILE) a directory tree for the the shareable config files
+
+ (RPLFILES) share for the configuration and per workstation files
+
+ (SWAPPATH) share for the swap and tmp files, (== (RPLFILES) by default)
+
+ (RPLBINS) share for all executable files, (== (RPLFILES) by default)
+
+ (SRVNAME) the name of RPL server, by default the current netbios name.
+ The server name is usually replaced by the share names.
+
+ The keywords are replaced by the actual parameters. CNAME and PROFILES
+ are defined in the workstation line of RPL.map and the last parameters
+ are optional lanman.ini parameters. (RPLFILES) is by default
+ \\CurrentServer\RPLFILE and (SWAPPATH) and (RPLBINS) are the same share,
+ if they have not been defined in lanman.ini.
+ Procedure supports the tilde replacement (e.g: ~~~~~~~~~~2) similar to
+ the old IBM RIPL. OEMs may define their own tilde fileds in the
+ rpl.map workstation lines, if they really want to. The default system
+ uses the tilde replacement only for per workstation (PROFILE) and (CNAME).
+ The alias keys are first translated to tildes and then they are
+ translated to wksta line fields.
+
+ Also removes all extra comments and spaces from the fit to save DOS and
+ OS/2 memory.
+
+ Provides fitfile functionality similar to that contained in rmapopen.c
+ of LANMAN 2.1 code.
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 19 - November - 1993
+
+Environment:
+
+ User mode
+
+Revision History :
+
+--*/
+
+#include "local.h"
+#include "fitfile.h"
+
+#define TAB_CHAR L'\t'
+#define LEFT_BRACKET_CHAR L'('
+#define BACK_SLASH_CHAR L'\\'
+
+
+INT RplEatSpaces( IN OUT LPWSTR Source)
+/*++
+
+Routine Description:
+
+ Deletes the extra white space characters in the fit string.
+ Only one white space or tabulator is necessary.
+
+Arguments:
+
+ Source - string to process
+
+Return Value:
+
+ Length in characters of the modified string (not counting the
+ terminating NULL character).
+
+--*/
+{
+ LPWSTR Origin = Source;
+ LPWSTR Target = Source;
+ WCHAR ch;
+
+ for (;;) {
+
+ while ( (ch = *Target++ = *Source++)!=0 && ch!=SPACE_CHAR
+ && ch!=TAB_CHAR) {
+ NOTHING; // copy characters until nul, tab or space character is found
+ }
+
+ if ( ch == 0) {
+ break; // end of string
+ }
+
+ if (ch == TAB_CHAR) {
+ *(Target - 1) = SPACE_CHAR; // replace tab with a space
+ }
+
+ while (*Source == SPACE_CHAR || *Source == TAB_CHAR) {
+ Source++; // skip extra tabs & spaces
+ }
+
+ //
+ // If the next character is a space character (e.g. newline)
+ // then this space is unnecessary, write on it in the next time.
+ //
+ if ( iswspace(*Source)) {
+ Target--;
+ }
+ }
+ return( Target-Origin - 1); // no casts are needed
+
+} // EatSpaces()
+
+
+VOID RplFitAliasInit( IN OUT PRPL_WORKER_DATA pWorkerData)
+/*++
+ Must make sure number of these entries equals FIT_ALIAS_ARRAY_LENGTH
+--*/
+{
+ DWORD Index;
+ PFIT_ALIAS pFitAlias;
+ PFIT_ALIAS FitAliasArray;
+
+ pFitAlias = FitAliasArray = &( pWorkerData->FitAliasArray[0]);
+
+ pFitAlias->Key = L"(PROFILE)"; // profile name of RPL wksta
+ pFitAlias->Value = pWorkerData->ProfileName;
+
+ (++pFitAlias)->Key = L"(CNAME)"; // computer name of RPL wksta
+ pFitAlias->Value = pWorkerData->WkstaName;
+
+//
+// (RPL_SERVER_NAME) is not documented by us nor used in our FIT files.
+// We should probably remove this entry.
+//
+ (++pFitAlias)->Key = L"(RPL_SERVER_NAME)"; // computer name of RPL server
+ pFitAlias->Value = RG_ComputerName;
+
+//
+// (RPLFILES) must be the FIRST SHARE in the array, because it is the
+// default used in (TMPFILES) and (BINFILES) if they are not defined!
+//
+ (++pFitAlias)->Key = L"(RPLFILES)";
+ pFitAlias->Value = RG_UncRplfiles;
+
+ (++pFitAlias)->Key = L"(TMPFILES)";
+ pFitAlias->Value = L"TMPFILES";
+
+ (++pFitAlias)->Key = L"(BINFILES)";
+ pFitAlias->Value = L"BINFILES";
+
+//
+// (COMPUTER_NAME) is not documented by us nor used in our FIT files.
+// It has the same purpose as (CNAME) and was kept for compatibility
+// with some old NOKIA FIT files. We should probably remove this entry.
+//
+ (++pFitAlias)->Key = L"(COMPUTER_NAME)";
+ pFitAlias->Value = RG_ComputerName;
+
+ RPL_ASSERT( FitAliasArray + FIT_ALIAS_ARRAY_LENGTH == ++pFitAlias);
+
+ for ( Index = 0; Index < FIT_ALIAS_ARRAY_LENGTH; Index++) {
+ FitAliasArray[ Index].KeyLength = wcslen( FitAliasArray[ Index].Key);
+ FitAliasArray[ Index].ValueLength = wcslen( FitAliasArray[ Index].Value);
+ }
+}
+
+
+PFIT_ALIAS RplFitFind( IN PFIT_ALIAS FitAliasArray, IN LPWSTR Key)
+/*++
+
+Routine Description:
+ Looks for FIT_ALIAS containing input key string.
+
+Arguments:
+ Key - ptr to key string
+
+Return Value:
+ Returns the pointer of a FIT alias structure or NULL if the the key was not found.
+
+--*/
+{
+ DWORD index;
+
+ for ( index = 0; index < FIT_ALIAS_ARRAY_LENGTH; index++) {
+ if ( !wcsncmp( FitAliasArray[ index].Key, Key, FitAliasArray[ index].KeyLength)) {
+ return &(FitAliasArray[ index]);
+ }
+ }
+ return( NULL);
+}
+
+
+DWORD RplFitNewLength( IN LPWSTR pFitImage, IN PFIT_ALIAS FitAliasArray)
+{
+ PWCHAR Cursor;
+ PFIT_ALIAS pFitAlias;
+ INT Length;
+
+ //
+ // Calculate the length of the new fit file. Some key replacements
+ // will increase the total length, the other decrease it.
+ //
+ for ( Cursor = pFitImage, Length = wcslen( pFitImage);
+ ( Cursor = wcschr( Cursor, LEFT_BRACKET_CHAR)) != NULL;
+ Cursor++) {
+
+ if ( (pFitAlias = RplFitFind( FitAliasArray, Cursor)) != NULL) {
+ //
+ // We found a keyword, replacement should be made, see how this
+ // would change the total length of FIT data.
+ //
+ Length += (INT)pFitAlias->ValueLength - (INT)pFitAlias->KeyLength;
+ }
+ }
+ return( Length);
+}
+
+
+VOID RplFitReplaceKeys(
+ OUT PWCHAR Target,
+ IN PWCHAR Source,
+ IN PFIT_ALIAS FitAliasArray
+ )
+/*++
+ Replace keywords.
+
+--*/
+{
+ PFIT_ALIAS pFitAlias;
+ PWCHAR Cursor;
+ INT Length;
+
+ Cursor = Source; // Initailize
+
+ while ( (Cursor = wcschr( Cursor, LEFT_BRACKET_CHAR)) != NULL) {
+
+ if ( (pFitAlias = RplFitFind( FitAliasArray, Cursor)) != NULL) {
+ //
+ // Copy stuff before the keyword, then copy the replacement string.
+ //
+ Length = Cursor - Source; // no casts are needed
+ memcpy( Target, Source, Length * sizeof( WCHAR));
+ memcpy( Target + Length, pFitAlias->Value, pFitAlias->ValueLength * sizeof( WCHAR));
+ Source += Length + pFitAlias->KeyLength;
+ Target += Length + pFitAlias->ValueLength;
+ Cursor += pFitAlias->KeyLength; // skip entire key
+ } else {
+ Cursor++; // skip LEFT_BRACKET_CHAR
+ }
+ }
+ //
+ // Here LEFT_BRACKET is NULL and we still need to copy from Source to the end of the
+ // string, including the terminal zero.
+ //
+ wcscpy( Target, Source);
+}
+
+
+VOID RplStripComments( IN OUT LPWSTR buffer)
+/*++
+
+Routine Description:
+ Skips leading spaces in each line and removes all comments lines
+ from a buffer containing FIT file.
+
+Arguments:
+ buffer - pointer to buffer containing FIT file, the buffer is NULL
+ terminated.
+
+Return Value:
+ None.
+
+--*/
+{
+ PWCHAR Cursor; // running cursor
+ PWCHAR LineEnd; // pointer to NEW_LINE_CHAR
+
+ Cursor = buffer;
+ for ( ; ;) {
+ //
+ // We are at the beginning of a line, skip leading white space characters.
+ //
+ Cursor += wcsspn( Cursor, L" \f\n\r\t\v");
+ if ( *Cursor == 0) {
+ break; // we reached the end of a string
+ }
+
+ LineEnd = wcschr( Cursor, NEW_LINE_CHAR);
+ if ( LineEnd == NULL) {
+ break; // ignore stuff without end of line
+ }
+ LineEnd++; // make it point beyond NEW_LINE_CHAR we just found
+
+ //
+ // Copy only the non comment lines. Memmove below works even
+ // for overlapping memory regions.
+ //
+ if ( *Cursor != COMMENT_CHAR) {
+ INT Length = LineEnd - Cursor;
+ buffer = (PWCHAR)memmove( buffer, Cursor, Length * sizeof(WCHAR)) + Length;
+ }
+ Cursor = LineEnd;
+ }
+ *buffer = 0; // null terminate
+}
+
+
+
+VOID RplFitStripRplfiles( IN OUT LPWSTR Buffer)
+/*++
+ The first line in a FIT file is a special case, it must contain the
+ default UNC share used in a FIT file. This UNC name need not be
+ anywhere else in a FIT file, so remove all other references.
+
+ BUGBUG: should we insist FIT file contains UNC name in the first line ??
+
+--*/
+{
+ PWCHAR Cursor;
+ INT Length;
+
+ if ( Buffer[0] == BACK_SLASH_CHAR && Buffer[1] == BACK_SLASH_CHAR) {
+ //
+ // First line contains UNC name, remember its length.
+ //
+ Cursor = wcspbrk( Buffer, L" \f\n\r\t\v");
+ Length = Cursor - Buffer; // no casts are needed
+
+ while ( (Cursor = wcsstr( Cursor, DOUBLE_BACK_SLASH_STRING)) != NULL) {
+ if ( !wcsncmp( Buffer, Cursor, Length) && Cursor[ Length] == BACK_SLASH_CHAR) {
+ memset( Cursor, SPACE_CHAR, (Length + 1) * sizeof( WCHAR));
+ Cursor += Length + 1;
+ } else {
+ Cursor += (sizeof(DOUBLE_BACK_SLASH_STRING)/sizeof(WCHAR) - 1);
+ }
+ }
+ }
+}
+
+
+BOOL RplFitFile( IN OUT PRPL_WORKER_DATA pWorkerData)
+{
+ PFIT_ALIAS FitAliasArray;
+ LPWSTR UnicodeString; // UNICODE content of FIT file
+ PWCHAR Target;
+ INT UnicodeStringLength;
+ BOOL Success;
+
+ RplDump( RG_DebugLevel & RPL_DEBUG_FLOW,( "++FitFile"));
+
+ Success = FALSE;
+ FitAliasArray = &( pWorkerData->FitAliasArray[ 0]);
+
+ //
+ // Initalize FIT_ALIAS[] array since it will be used in calculations below.
+ //
+ RplFitAliasInit( pWorkerData);
+
+ //
+ // Read FIT file data as a UNICODE string.
+ //
+ UnicodeString = RplReadTextFile( pWorkerData->MemoryHandle,
+ pWorkerData->FitFile, MAX_FIT_FILE_SIZE);
+ if ( UnicodeString == NULL) {
+ RplDump( ++RG_Assert, ( "FitFile=%ws", pWorkerData->FitFile));
+ pWorkerData->EventStrings[ 0] = pWorkerData->WkstaName;
+ pWorkerData->EventStrings[ 1] = pWorkerData->FitFile;
+ pWorkerData->EventId = NELOG_RplWkstaFileRead;
+ return( FALSE);
+ }
+
+ //
+ // Remove the comments and all unnecessary spaces. There should be not
+ // extra characters in FIT, because they eat both DOS and OS/2 memory.
+ //
+ RplStripComments( UnicodeString);
+
+ //
+ // Calculate space needed for a UNICODE version of FIT file after we make
+ // all key replacements.
+ //
+ UnicodeStringLength = RplFitNewLength( UnicodeString, FitAliasArray);
+
+ //
+ // Allocate space for UNICODE version of FIT file.
+ //
+ Target = RplMemAlloc( pWorkerData->MemoryHandle, (UnicodeStringLength+1) * sizeof( WCHAR));
+ if ( Target == NULL) {
+ pWorkerData->EventStrings[ 0] = pWorkerData->WkstaName;
+ pWorkerData->EventId = NELOG_RplWkstaMemory;
+ goto cleanup;
+ }
+
+ RplFitReplaceKeys( Target, UnicodeString, FitAliasArray);
+ RplMemFree( pWorkerData->MemoryHandle, UnicodeString);
+ UnicodeString = Target;
+
+ //
+ // Get rid of RPLFILES duplicates.
+ //
+ RplFitStripRplfiles( UnicodeString);
+
+ UnicodeStringLength = RplEatSpaces( UnicodeString); // compactify
+ _wcsupr( UnicodeString); // upppercase
+
+ //
+ // Convert UNICODE fit string into DBCS fit string.
+ //
+ pWorkerData->ClientFitSize = RplUnicodeToDbcs(
+ pWorkerData->MemoryHandle,
+ UnicodeString,
+ UnicodeStringLength,
+ MAX_FIT_FILE_SIZE,
+ &(pWorkerData->ClientFit)
+ );
+ if ( pWorkerData->ClientFitSize == 0) {
+ RplDump( ++RG_Assert, ( "UnicodeString=0x%x, UnicodeString=%ws",
+ UnicodeString, UnicodeString));
+ pWorkerData->EventStrings[ 0] = pWorkerData->WkstaName;
+ pWorkerData->EventStrings[ 1] = pWorkerData->FitFile;
+ pWorkerData->EventId = NELOG_RplWkstaFileSize;
+ goto cleanup;
+ }
+
+ Success = TRUE;
+
+cleanup:
+ if ( UnicodeString != NULL) {
+ RplMemFree( pWorkerData->MemoryHandle, UnicodeString);
+ }
+ RplDump( RG_DebugLevel & RPL_DEBUG_FLOW,( "--FitFile"));
+ return( Success);
+}
diff --git a/private/net/svcdlls/rpl/server/fitfile.h b/private/net/svcdlls/rpl/server/fitfile.h
new file mode 100644
index 000000000..4ccb4fc01
--- /dev/null
+++ b/private/net/svcdlls/rpl/server/fitfile.h
@@ -0,0 +1,27 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ fitfile.h
+
+Abstract:
+
+ Exports from fitfile.c
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 19 - November - 1993
+
+Environment:
+
+ User mode
+
+Revision History :
+
+--*/
+
+BOOL RplFitFile( IN OUT PRPL_WORKER_DATA pWorkerData);
+
+
diff --git a/private/net/svcdlls/rpl/server/library.c b/private/net/svcdlls/rpl/server/library.c
new file mode 100644
index 000000000..414c85fc2
--- /dev/null
+++ b/private/net/svcdlls/rpl/server/library.c
@@ -0,0 +1,273 @@
+/*++
+
+Copyright (c) 1987 - 1993 Microsoft Corporation
+
+Module Name:
+
+ library.c
+
+ Provides similar functionality to rpllib.c in LANMAN 2.1 code.
+
+Abstract:
+
+ Common library routines.
+
+Author:
+
+ Vladimir Z. Vulovic 25 - July - 1993
+
+Environment:
+
+ User mode
+
+Revision History :
+
+--*/
+
+#include "local.h"
+
+
+
+LPWSTR RplGetLastPathComponent( IN LPWSTR path_str)
+/*++
+
+Routine Description:
+ Returns the last component of the path.
+
+Arguments:
+ path_str - pointer to path string
+
+Return Value:
+ Pointer to the last component of the path.
+
+--*/
+{
+ LPWSTR ret_str;
+ WCHAR ch;
+
+ for( ch = *( ret_str = path_str); ch != 0; ch = *(++path_str)) {
+ if ( ch == '\\' || ch == '/') {
+ ret_str = path_str + 1; // remember most recent component
+ }
+ }
+ return( ret_str);
+}
+
+
+
+HANDLE RplFileOpen( IN LPWSTR FilePath)
+/*++
+
+Routine Description:
+
+ Open existing file for reading, at the same time denying write
+ access to others. Note that WIN32 errors are basically identical
+ to OS/2 errors!
+
+ In case of special errors we may try a number of times to open the
+ requested file.
+
+Arguments:
+
+ FilePath - of file to open
+
+Return Value:
+
+ A valid file handle if successful, else INVALID_HANDLE_VALUE if
+ unsuccessful.
+
+--*/
+{
+ DWORD status;
+ DWORD retry_count;
+ HANDLE handle;
+
+ //
+ // If file is being used by another process, we make up to 40 retries
+ // each one lasting 0.750 seconds (for a max total of 30 seconds) before
+ // we resign. Configuration programs may lock the files temporarily,
+ // but if somebody locks a file for a long time, then we must die.
+ //
+ for ( retry_count = 0; retry_count < MAX_OPEN_RETRY; retry_count++) {
+
+ handle = CreateFile( FilePath, GENERIC_READ, FILE_SHARE_READ,
+ NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0L);
+ if ( handle != INVALID_HANDLE_VALUE) {
+ return( handle); // success, all done
+ }
+
+ status = GetLastError();
+
+ if ( status != ERROR_SHARING_VIOLATION) {
+ break; // bad, unexpected case
+ }
+
+ Sleep( 750L );
+ }
+ RplReportEvent( NELOG_Init_OpenCreate_Err, FilePath, sizeof(WORD), (PBYTE)&status);
+ RplDump( RG_DebugLevel & RPL_DEBUG_MISC,( "FileOpen(%ws) failed, status = %d", FilePath, status));
+ return( INVALID_HANDLE_VALUE);
+}
+
+
+
+LPWSTR RplReadTextFile(
+ IN HANDLE MemoryHandle,
+ IN LPWSTR FileName,
+ IN DWORD MaxFileSize
+ )
+/*++
+
+Routine Description:
+
+ Reads text file, converts its content from dbcs to unicode, and returns
+ a pointer to newly allocated unicode buffer.
+
+Arguments:
+
+Return Value:
+
+ Pointer to unicode buffer table if successful, NULL otherwise.
+
+--*/
+{
+ PBYTE DbcsString = NULL;
+ DWORD DbcsSize;
+ PWCHAR UnicodeString;
+ DWORD UnicodeSize;
+ int UnicodeStringLength;
+ HANDLE FileHandle;
+ DWORD BytesRead;
+ BOOL success = FALSE;
+ PWCHAR pWchar;
+
+ RplDump( RG_DebugLevel & RPL_DEBUG_FLOW,( "++ReadTextFile:0x%x,%ws", MemoryHandle, FileName));
+
+ FileHandle = RplFileOpen( FileName);
+ if ( FileHandle == INVALID_HANDLE_VALUE) {
+ goto exit;
+ }
+ DbcsSize = GetFileSize( FileHandle, NULL); // does not include 0x1A at the file end
+ if ( DbcsSize == INVALID_FILE_SIZE || DbcsSize > MaxFileSize) {
+ goto exit;
+ }
+ DbcsString = RplMemAlloc( MemoryHandle, DbcsSize);
+ if ( DbcsString == NULL) {
+ goto exit;
+ }
+
+ UnicodeSize = ( DbcsSize + 1) * sizeof(WCHAR); // extra 1 for terminating NULL
+ UnicodeString = RplMemAlloc( MemoryHandle, UnicodeSize);
+ if ( UnicodeString == NULL) {
+ goto exit;
+ }
+
+ if ( !ReadFile( FileHandle, DbcsString, DbcsSize, &BytesRead, NULL)) {
+ goto exit;
+ }
+
+ if ( BytesRead != DbcsSize) {
+ goto exit;
+ }
+
+ UnicodeStringLength = MultiByteToWideChar(
+ CP_OEMCP, // text files stored in OEM
+ MB_PRECOMPOSED, DbcsString, DbcsSize, UnicodeString,
+ UnicodeSize);
+ if ( UnicodeStringLength == 0) {
+ goto exit;
+ }
+
+ //
+ // Null-terminate string! JonN 8/7/94
+ //
+ UnicodeString[ UnicodeStringLength] = 0;
+
+ //
+ // If file has END_OF_TEXT_FILE_CHAR, truncate the text there.
+ //
+ pWchar = wcschr( UnicodeString, END_OF_TEXT_FILE_CHAR);
+ if ( pWchar != NULL) {
+ *pWchar = 0;
+ }
+
+ success = TRUE;
+
+exit:
+
+ if ( FileHandle != INVALID_HANDLE_VALUE) {
+ (VOID)CloseHandle( FileHandle);
+ }
+ if ( DbcsString != NULL) {
+ RplMemFree( MemoryHandle, DbcsString);
+ }
+
+ if ( success != TRUE) {
+ RplMemFree( MemoryHandle, UnicodeString);
+ UnicodeString = NULL;
+ }
+
+ RplDump( RG_DebugLevel & RPL_DEBUG_FLOW,( "--ReadTextFile:0x%x,%ws", MemoryHandle, FileName));
+ return( UnicodeString);
+
+} // RplReadTextFile
+
+
+DWORD RplUnicodeToDbcs(
+ IN HANDLE MemoryHandle,
+ IN LPWSTR UnicodeString,
+ IN INT UnicodeStringLength,
+ IN DWORD MaxDbcsStringSize,
+ OUT LPSTR * pDbcsString
+ )
+/*++
+ Allocates DBCS string corresponding to a UNICODE string.
+
+ Returns size of buffer needed for DbcsString, counting space needed for
+ the terminal null byte.
+--*/
+{
+ LPSTR DbcsString;
+ DWORD ByteCount;
+
+ if ( UnicodeStringLength == -1) {
+ UnicodeStringLength = wcslen( UnicodeString);
+ }
+
+ //
+ // Assumed the worst case where every UNICODE char maps to a double
+ // byte DBCS char (except for DBCS terminating null char).
+ //
+ ByteCount = UnicodeStringLength * sizeof(WCHAR) + sizeof(CHAR);
+
+ //
+ // If caller's limit is more stringent, use it.
+ //
+ if ( ByteCount > MaxDbcsStringSize) {
+ ByteCount = MaxDbcsStringSize;
+ }
+
+ DbcsString = RplMemAlloc( MemoryHandle, ByteCount);
+ if ( DbcsString == NULL) {
+ RPL_RETURN( 0);
+ }
+
+ ByteCount = WideCharToMultiByte( // not counting terminal null byte
+ CP_OEMCP, // always use OEM
+ 0,
+ UnicodeString,
+ UnicodeStringLength,
+ DbcsString, // dbcs string
+ ByteCount,
+ NULL, // no default character
+ NULL // no default character flag
+ );
+ if ( ByteCount == 0) {
+ RplMemFree( MemoryHandle, DbcsString);
+ RPL_RETURN( 0);
+ }
+ DbcsString[ ByteCount] = 0; // this may be redundant
+ *pDbcsString = DbcsString;
+ return( ByteCount + 1); // caller expects terminal null byte to be counted
+}
+
diff --git a/private/net/svcdlls/rpl/server/local.h b/private/net/svcdlls/rpl/server/local.h
new file mode 100644
index 000000000..b21ddac9e
--- /dev/null
+++ b/private/net/svcdlls/rpl/server/local.h
@@ -0,0 +1,556 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ local.h
+
+Abstract:
+
+ Main include file for Remote initial Program Load service.
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 19 - November - 1993
+
+Environment:
+
+ User mode
+
+Revision History :
+
+--*/
+
+#include <nt.h> // ntexapi.h\NtQuerySystemTime
+#include <ntrtl.h> // RtlTimeToSecondsSince1970
+#include <nturtl.h>
+
+#include <windows.h> // DWORD, IN, File APIs, etc.
+#include <stdlib.h>
+#include <lmcons.h>
+
+#include <stdio.h> // vsprintf
+#include <ctype.h> // isspace
+
+#include <lmerr.h> // NERR_RplBootStartFailed - used to be here
+#include <lmapibuf.h> // NetApiBufferFree
+#include <netlib.h>
+
+#include <lmsvc.h>
+
+#include <lmalert.h> // STD_ALERT ALERT_OTHER_INFO
+
+#include <lmerrlog.h>
+#include <alertmsg.h>
+#include <lmserver.h>
+#include <netlib.h>
+#include <netlock.h> // Lock data types, functions, and macros.
+#include <thread.h> // FORMAT_NET_THREAD_ID, NetpCurrentThread().
+
+#include <lmshare.h> // PSHARE_INFO_2
+#include <lmaccess.h> // NetGroupGetInfo
+#include <lmconfig.h> // NetConfigGet
+#include <nb30.h> // NCB
+
+
+//
+// Global types and constants (used in all RPL server files).
+//
+
+#include <riplcons.h> // includes __JET500 flag
+#include <jet.h>
+#include <rpldebug.h>
+#include <rpldll.h> // rplnet.dll entry points & return codes
+#include <rpllib.h> // AddKey(), MapJetError(), FillTcpIpString()
+#include <ripltyps.h>
+
+#include <rpc.h> // DataTypes and runtime APIs
+#include <rpcutil.h> // Prototypes for MIDL user functions
+//
+// rplsvc_s.h includes imports.h which includes lmrpl.h. This allows us
+// to use API-related DS in the rest of the service code.
+//
+#include <rplsvc_s.h> // Generated by the MIDL complier
+
+
+//
+// Other defines
+//
+
+#define DOUBLE_BACK_SLASH_STRING L"\\\\"
+
+//
+// BITMASK to be used with Flags field in RPL_WKSTA data structure
+//
+#define RPL_WKSTA_PERSONAL 0x01
+
+#define MAX_ADAPTERS 12 // "rpl1" through "rpl12"
+
+
+#define MIN_BBLOCK_BASE 0xc00
+
+//
+// All BBC files are very small, typically around 1K.
+// Use 32K as the absolute maximum on BBC file size.
+//
+#define MAX_BBC_FILE_SIZE 0x8000 // for no compelling reason
+
+#define MAX_FIT_FILE_SIZE 0xF000 // some arbitrary value
+#define MAX_SIZEOF_DBCS_PATH (PATHLEN *sizeof(WCHAR)+sizeof(CHAR))
+
+#define SERVER_LINE_ID_LEN 12
+
+#define DEFAULT_RETRY_COUNT 3
+#define DEFAULT_RETRY_TIMEOUT 10
+#define DEFAULT_WINDOW_SIZE MAXWORD
+#define DEFAULT_SEND_FINAL_REQ FALSE
+
+#define MAX_XMIT_ERRORS 30 // max contiguous fatal xmit failures
+
+//
+// status bits of main_action_status
+//
+
+#define TERMINATE_SERVICE 1
+#define REINITIALIZE_SERVICE 2
+#define REINITIALIZATION_PENDING 4
+#define TERMINATION_PENDING 8
+
+#define ERROR_HANDLING (DWORD)(-1)
+#define NO_ERROR_HANDLING 0
+#define MAX_OPEN_RETRY 40
+#define MAX_OEM_NAME 7
+
+//
+// Constants of the actual ripl server
+//
+
+#define RPL_DLL_SKELETON "rpl"
+#define WORKER_THREAD_MIN 2
+#define MAX_FRAME_SIZE 0x1000 // exactly 1 page, or 4KB
+#define MAX_SUB_SEGMENT_SIZE 0xffe0
+#define SEND_BUF_SEG_SIZE 17000
+//
+// MAX_ADAPTER_INFO_SIZE is now defined to be the sum of upper size
+// estimates for DLC buffer (0xA000) and private adapter info (2000).
+//
+#define MAX_ADAPTER_INFO_SIZE (0xA000 + 2000)
+
+#define LAN_ADAPTER0 0
+#define CHECK_SUM_ERROR 9676
+#define SF_REQ_TIMEOUT 7 //IBM waits 3s for lost frame & (ACK lost)
+#define MAX_FILE_HANDLES_INC 20 // increment of max fhs
+#define UNINSTALL_TIME 100 // 10 seconds, actually: (1 - 150)
+
+#define RPL_KEYBOARD_DCP L"KEYBOARD.DCP"
+#define RIPL_CHECK_SUM L"rplchecksum"
+#define RPLDIR_DEF L"rpldir"
+#define RPL_DIR_1 L"rpl1"
+#define YES L"yes"
+#define RPL_LOGON_KBD L"rpllogonkbd"
+#define LANMAN_INI L"lanman.ini"
+#define RPL_REMARK L"Remoteboot service share"
+#define RPLUSER_GROUP L"RPLUSER"
+
+//
+// JETBUG entries are needed for workaround for JetMakeKey-JetSeek bug.
+// If this bug gets ever fixed, they should be removed (undef RPL_JETBUG)
+//
+#define RPL_JETBUG
+#ifdef RPL_JETBUG
+#define JETBUG_STRING L" "
+#define JETBUG_STRING_SIZE sizeof( JETBUG_STRING)
+#define JETBUG_STRING_LENGTH ((DWORD)(sizeof( JETBUG_STRING)/sizeof(WCHAR) - 1))
+#else
+#define JETBUG_STRING_LENGTH 0
+#endif
+
+//
+// TIMEOUTS
+//
+
+#define GET_SF_REQUEST_TIMEOUT 6000L // not error and quite common
+
+// implement this with fast retries
+#define LONG_WAIT_ACK_TIMEOUT 30000L // wksta sent no resend requests!
+#define WAIT_ACK_TIMEOUT 1000L // short wait for the lost frames
+
+#define MAX_ERR_COUNT 4
+#define MAX_RETRIES 5 // retry count before termination
+
+#define OFFSET_BUF_LEN 64 // space for the whole RPLBoot hdr
+
+/*++
+ RPL service must send NLS (DBCS ??) messages in a layout expected by RPL client
+ (from rplboot\rplboot.asm):
+
+ ;------------------------------------------------------------------------
+ ; NLS messages structure - used in RPLOADR and DLCBOOT
+ ;------------------------------------------------------------------------
+ nlsmsglen equ 80 ;max number of bytes per msg
+ nlsmsg struc
+ db nlsmsglen dup(' '); ;<space fill>
+ db 0dh ;<cr>
+ db 0ah ;<lr>
+ db '$' ;<terminator>
+ nlsmsg ends
+--*/
+
+#define DBCS_SINGLE_MESSAGE_BUFFER_SIZE 83
+
+//
+// Typedefs
+//
+
+typedef struct _FLIST_TBL {
+ //
+ // UNICODE name. Used for work on the server.
+ //
+ LPWSTR FileName;
+ //
+ // Same file name but without path and in DBCS. Sent to client.
+ //
+ LPSTR DbcsFileName;
+ DWORD DbcsFileNameSize;
+
+ //
+ //
+ // Size of memory needed for "param_list", not counting terminating null
+ // byte, is kept in FILE_DATA.param_len entry.
+ //
+ LPSTR param_list; // a DBCS string, UNICODE version not needed
+ LPWSTR path_name;
+ FILE_DATA FileData; // structure to be saved to dump_tbl
+} FLIST_TBL, *PFLIST_TBL;
+
+typedef struct _CONFIG_TYPE {
+ LPWSTR id;
+ DWORD type;
+} CONFIG_TYPE, *PCONFIG_TYPE;
+
+#define FIT_ALIAS_ARRAY_LENGTH 7 // poor initialization
+
+typedef struct _FIT_ALIAS {
+ LPWSTR Key; // keyword, or macro
+ LPWSTR Value; // replacement value for the keyword
+ DWORD KeyLength; // length of keyword, or macro
+ DWORD ValueLength; // length of replacement value for the keyword
+} FIT_ALIAS, *PFIT_ALIAS;
+
+
+//
+// parameters sent to worker thread
+//
+// thread_id should be initialized somewhere via GetCurrentThreadId()
+//
+
+struct _RPL_REQUEST_PARAMS;
+typedef struct _RPL_REQUEST_PARAMS RPL_REQUEST_PARAMS, *PRPL_REQUEST_PARAMS;
+
+struct _RPL_WORKER_PARAMS;
+typedef struct _RPL_WORKER_PARAMS RPL_WORKER_PARAMS, *PRPL_WORKER_PARAMS;
+
+typedef struct _RPL_REQUEST_PARAMS {
+ PRPL_REQUEST_PARAMS pRequestParams; // next in a queue of request threads
+ HANDLE ThreadHandle;
+ DWORD ThreadId;
+ PRPL_WORKER_PARAMS pWorkerParams; // first in a queue of worker threads
+ POPEN_INFO pOpenInfo; // generic rpl DLL info
+ PRCB FreeRcbList; // list of free rcb blocks
+ PRCB BusyRcbList; // list of reserved RCBs
+ BOOL Exiting;
+} RPL_REQUEST_PARAMS , *PRPL_REQUEST_PARAMS;
+
+typedef struct _RPL_WORKER_PARAMS { // worker thread parameters
+ PRPL_WORKER_PARAMS pWorkerParams; // next in a queue of worker threads
+ HANDLE ThreadHandle;
+ DWORD ThreadId;
+ PRPL_REQUEST_PARAMS pRequestParams;
+ PRCB pRcb;
+ BOOL Exiting;
+} RPL_WORKER_PARAMS, *PRPL_WORKER_PARAMS;
+
+typedef struct _RPL_WORKER_DATA {
+ //
+ // These are addresses of on the stack variables from the Request thread.
+ //
+ PPRCB pFreeRcbList; // base of free rcb list
+ PPRCB pBusyRcbList; // base of reserved rcbs
+ //
+ // The following three pointers are allocated via RG_MemoryHandle.
+ // Everything else in this data structure, including the structure itself
+ // is allocated via WORKER_DATA.MemoryHandle == WORKER_PARAMS.MemoryHandle.
+ //
+ PRCB pRcb; // resource control block
+ PRPL_REQUEST_PARAMS pRequestParams; // ptr to request threads parm block
+ POPEN_INFO pOpenInfo; // open info of the cur RPL DLL
+ //
+ // The following are the entries read from database.
+ // WkstaName & TcpIp* are sent to the client in wksta line.
+ // WkstaName & ProfileName are used to make changes in fit file.
+ //
+ WCHAR WkstaName[ RPL_MAX_WKSTA_NAME_LENGTH + 1];
+ WCHAR ProfileName[ RPL_MAX_PROFILE_NAME_LENGTH + 1];
+ DWORD TcpIpAddress;
+ DWORD TcpIpSubnet;
+ DWORD TcpIpGateway;
+ WCHAR LogonInput;
+ CHAR DisableDhcp; // '1' ('0') if DHCP is disabled (enabled)
+ DWORD WindowSize;
+ BOOL FinalAck; // SendFinalRequest;
+
+ HANDLE MemoryHandle; // for worker thread
+ LPBYTE WkstaLine; // old style, RPL.MAP line
+
+ //
+ // Fields describing the boot block configuration file.
+ //
+ LPWSTR BbcFile; // full path to BBC file
+ PFLIST_TBL flist_tbl; // files sent to RPLBOOT
+ BOOL MakePatch; // TRUE if patch to RPLBOOT.SYS is needed
+ DWORD PatchOffset; // relevant if MakePatch is TRUE
+ PRESOURCE_TBL resource_tbl; // file list given to the loader
+ PBYTE id_str; // id string of the server
+ DWORD min_wksta_buf; // min size of wksta specific data
+ DWORD file_block_base; // relative addr of file block in boot block
+ DWORD boot_block_base; // base phys addr of boot block
+ DWORD flist_tbl_len;
+ DWORD loader_i; // index of OS/2 loader in flist_tbl
+ DWORD rplboot_i; // index of rplboot.sys in file list
+ DWORD resource_tbl_size; // size of the table (in bytes)
+ LPWSTR LineBuffer; // used for parsing a line into words
+ DWORD LineBufferSize; // size of LineBuffer
+ LPWSTR * WordTable; // used for parsing a line into words
+
+ //
+ // Fields describing the file index table file.
+ //
+ // In order to get full path to fit file we need to check wksta flags
+ // to find out profile type (personal or shared) then read the
+ // corresponding file path from profile record.
+ //
+ LPWSTR FitFile; // full path to FIT file
+ LPSTR ClientFit; // string with DBCS content of FIT file sent to client
+ DWORD ClientFitSize; // size of above string
+ FIT_ALIAS FitAliasArray[ FIT_ALIAS_ARRAY_LENGTH];
+
+
+ //
+ // Fields initalized (wksta_buf also created) in RplMakeWkstaBuf()
+ //
+ //
+ // wksta_buf contains BOOT_DATA, old RPL.MAP workstation line, processed FIT file,
+ // BOOT_BLOCK_HDR and multiboot array.
+ //
+ // It does not contain files mentioned in boot block configuration file.
+ //
+ PBYTE wksta_buf; // ptr to wksta specific data
+
+ //
+ // size of wksta_buf
+ //
+ DWORD wksta_buf_len; // size in bytes of wksta specific data
+
+ //
+ //
+ //
+ DWORD base_address; // start address of buffer, first send
+ DWORD jump_address; // start address of program, last send
+ DWORD fblock_base_offset; // base offset (from 0) of file block
+
+ DWORD send_buf_len; // length of network send buffer
+
+ //
+ // The following group of fields is used while reading data from
+ // files mentioned in the boot block configuration file. These
+ // fields are initialized in RplOpenData()
+ //
+
+ //
+ // "pDataBuffer" is used to read data from disk files, then to send this
+ // data to a remote client.
+ //
+ PBYTE pDataBuffer; // read data here, then send it out
+ DWORD cur_flist_i; // current index for FLIST_TBL array
+ DWORD cur_offset; // current offset of boot block
+ DWORD cur_file_base_offset; // base offset of the current file
+ BOOL is_end_of_bblock; // set if end of boot block
+ HANDLE hFile; // current file handle
+ //
+ // ChecksumBuffer - buffer used to checksum files, when checksums are needed.
+ //
+ PBYTE ChecksumBuffer; // used to checksum files when
+ DWORD EventId;
+ PWCHAR EventStrings[ 5]; // null terminated
+} RPL_WORKER_DATA, *PRPL_WORKER_DATA;
+
+
+#define NOTIFY_WKSTA_RECORD 0 // disabled wksta record for main thread to write to RPL.map
+
+typedef struct _ADMIN_ALERT_DATA{
+ DWORD alrtad_errcode;
+ DWORD alrtad_numstrings;
+} ADMIN_ALERT_DATA;
+
+typedef struct _ADAPTER_STATUS_BUFFER {
+ DWORD id_low;
+ DWORD id_mid;
+ DWORD id_high;
+ BYTE jumper_status;
+ BYTE self_test;
+ DWORD sw_version;
+ BYTE error_statistics[48];
+ DWORD name_tbl_len;
+ BYTE name1[18];
+ BYTE name2[18];
+ BYTE name3[18];
+ BYTE name4[18];
+ BYTE name5[18];
+ BYTE name6[18];
+ BYTE name7[18];
+ BYTE name8[18];
+ BYTE name9[18];
+ BYTE name10[18];
+ BYTE name11[18];
+ BYTE name12[18];
+ BYTE name13[18];
+ BYTE name14[18];
+ BYTE name15[18];
+ BYTE name16[18];
+} ADAPTER_STATUS_BUFFER;
+
+typedef enum {IMMEDIATE_ERROR, WAIT_RESOURCES} BLOCKTYPE;
+
+#ifdef UNICODE
+
+#define RPL_TO_UNICODE( arg) arg
+#define RPL_FREE_UNICODE( arg)
+
+#else // UNICODE
+
+WCHAR * RplAsciizToUnicodez( IN CHAR * Asciiz);
+VOID RplFreeUnicodez( IN WCHAR * Unicodez);
+#define RPL_TO_UNICODE( arg) RplAsciizToUnicodez( arg)
+#define RPL_FREE_UNICODE( arg) RplFreeUnicodez( arg)
+
+#endif // UNICODE
+
+
+#define ALERT_LONG_WAIT 10000L // used to be in alert.h
+
+//
+// Bitmask used with RG_Tasks. They describe outstanding tasks to be
+// done by the main rpl service thread once it wakes up.
+//
+
+#define RPL_SERVICE_SHUTDOWN 0x0004
+
+
+#define RPL_WAIT_HINT_TIME 15000L // 15 seconds for now
+
+
+
+
+#define TMPFILES_IDX 4
+#define BINFILES_IDX 5
+
+
+//
+// termination addresses of all rpl DLLs have been saved to this list
+//
+
+typedef DWORD ( * TERMINATION_ADDRESS)( POPEN_INFO);
+
+typedef struct _TERM_LIST {
+ struct _TERM_LIST * next;
+ POPEN_INFO pOpenInfo;
+} TERM_LIST, *PTERM_LIST;
+
+
+#define INVALID_ERROR_PARAMETER ((DWORD)(-1)) // internal usage in api code
+
+typedef struct _RPL_SESSION {
+ JET_SESID SesId; // jet session identifier
+ JET_DBID DbId; // jet database identifier
+ JET_TABLEID AdapterTableId;
+ JET_TABLEID BootTableId;
+ JET_TABLEID ConfigTableId;
+ JET_TABLEID ProfileTableId;
+ JET_TABLEID ResumeTableId;
+ JET_TABLEID VendorTableId;
+ JET_TABLEID WkstaTableId;
+} RPL_SESSION, *PRPL_SESSION;
+
+
+//
+// Function Prototypes
+//
+
+//
+// memory.c
+//
+
+DWORD RplMemInit( PHANDLE pMemoryHandle);
+
+PVOID RplMemAlloc(
+ IN HANDLE MemoryHandle,
+ IN DWORD size
+ );
+
+VOID RplMemFree(
+ IN HANDLE MemoryHandle,
+ IN PVOID mem_ptr
+ );
+
+VOID RplMemClose( IN HANDLE MemoryHandle);
+
+PVOID RplMemReAlloc(
+ IN HANDLE MemoryHandle,
+ IN DWORD Flags,
+ IN PVOID old_ptr,
+ IN DWORD new_size
+ );
+
+
+//
+// library.c
+//
+
+LPWSTR RplGetLastPathComponent( IN LPWSTR FilePath);
+HANDLE RplFileOpen( IN LPWSTR FilePath);
+
+LPWSTR RplReadTextFile(
+ IN HANDLE MemoryHandle,
+ IN LPWSTR FileName,
+ IN DWORD MaxFileSize
+ );
+DWORD RplUnicodeToDbcs(
+ IN HANDLE MemoryHandle,
+ IN LPWSTR UnicodeString,
+ IN INT UnicodeStringLength,
+ IN DWORD MaxDbcsStringSize,
+ OUT LPSTR * pDbcsString
+ );
+
+//
+// rplmain.c
+//
+
+VOID RPL_main(
+ IN DWORD argc,
+ IN LPWSTR argv[]
+ );
+VOID RplInitError(
+ DWORD ErrorCode,
+ PCHAR ErrorIdString
+ );
+BOOL RplServiceAttemptStop( VOID);
+DWORD RplUpdateStatus( VOID);
+
+
+//
+// Declare/Define all global variables.
+//
+
+#include "rpldata.h" // defines/declares global data structures
diff --git a/private/net/svcdlls/rpl/server/makefile b/private/net/svcdlls/rpl/server/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/net/svcdlls/rpl/server/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/net/svcdlls/rpl/server/memory.c b/private/net/svcdlls/rpl/server/memory.c
new file mode 100644
index 000000000..15b9dc34a
--- /dev/null
+++ b/private/net/svcdlls/rpl/server/memory.c
@@ -0,0 +1,188 @@
+/*++
+
+Copyright (c) 1987-1993 Microsoft Corporation
+
+Module Name:
+
+ memory.c
+
+Abstract:
+
+ Memory management routines.
+
+Author:
+
+ Vladimir Z. Vulovic 27 - July - 1993
+
+Environment:
+
+ User mode
+
+Revision History :
+
+--*/
+
+#include "local.h"
+
+#define DEFAULT_HEAP_INITIAL_SIZE (4*1024L) // 4K or one page, should be 0 ??
+#define DEFAULT_HEAP_MAXIMUM_SIZE (64*1024L) // upper limit taken from Init_submalloc, better value would be 0 ??
+
+
+DWORD RplMemInit( PHANDLE pMemoryHandle)
+/*++
+
+Routine Description:
+ Initializes memory management
+
+Arguments:
+ pMemoryHandle - pointer to memory handle, in PRIVATE case it means something,
+ in SHARED case it is just initialized to NULL
+
+Return Value:
+ ERROR_SUCCESS if success, else last error
+
+--*/
+{
+ *pMemoryHandle = HeapCreate(
+ 0L,
+ 64 * 1024, // make it large due to shoddy code in Rtl routines
+ 0 // Let it grow without limit !
+ );
+
+ if ( *pMemoryHandle == NULL) {
+ DWORD status = GetLastError();
+ RplDump( ++RG_Assert,( "status=%d", status));
+ return( status);
+ }
+
+ RplDump( RG_DebugLevel & RPL_DEBUG_MEMORY,(
+ "MemInit: HeapCreate => handle=0x%x", *pMemoryHandle));
+
+ return( ERROR_SUCCESS);
+}
+
+
+
+PVOID RplMemAlloc(
+ IN HANDLE MemoryHandle,
+ IN DWORD size
+ )
+/*++
+
+Routine Description:
+ Alocates memory object from a memory heap. Memory heap must have a valid handle.
+
+Arguments:
+ MemoryHandle - memory handle (relevant in PRIVATE case only)
+ size - size of memory object
+
+Return Value:
+ Pointer to newly allocated memory if successful, else NULL.
+
+--*/
+{
+ PVOID ptr;
+ ptr = HeapAlloc( MemoryHandle, 0, size);
+ if ( ptr == NULL) {
+ RplDump( ++RG_Assert,( "Error=%d", GetLastError()));
+ } else {
+ RplDump( RG_DebugLevel & RPL_DEBUG_MEMALLOC,(
+ "MemAlloc( 0x%x, %d) => 0x%x", MemoryHandle, size, ptr));
+ }
+ return( ptr);
+}
+
+
+
+VOID RplMemFree(
+ HANDLE MemoryHandle,
+ PVOID pMemoryObject
+ )
+/*++
+
+Routine Description:
+ Memory object must have been allocated via MemFree() or MemReAlloc().
+ Memory object is freed.
+
+Arguments:
+ MemoryHandle - memory handle (relevant in PRIVATE case only)
+ pMemoryObject - pointer to memory object
+
+Return Value:
+ None.
+
+--*/
+{
+ if ( !HeapFree( MemoryHandle, 0, pMemoryObject)) {
+ RplDump( ++RG_Assert,( "Error=%d", GetLastError()));
+ } else {
+ RplDump( RG_DebugLevel & RPL_DEBUG_MEMORY,(
+ "MemFree( 0x%x) <= 0x%x", MemoryHandle, pMemoryObject));
+ }
+}
+
+
+
+VOID RplMemClose( HANDLE MemoryHandle)
+/*++
+
+Routine Description:
+ Destroys the heap thus releasing all the memory allocated with this handle.
+ HeapDestroy() is BOOL, here we just hide dirt under the rug!
+ See more extensive comments under MemFree().
+
+Arguments:
+ MemoryHandle - heap handle
+
+Return Value:
+ None.
+
+--*/
+{
+ if ( !HeapDestroy( MemoryHandle)) {
+ RplDump( ++RG_Assert, ,( "Error=%d", GetLastError()));
+ }
+}
+
+
+
+PVOID RplMemReAlloc(
+ IN HANDLE MemoryHandle,
+ IN DWORD Flags,
+ IN PVOID old_ptr,
+ IN DWORD new_size
+ )
+/*++
+
+Routine Description:
+
+ Reallocates data to a new size, preserving the content.
+
+Arguments:
+
+ MemoryHandle - memory handle (relevant in PRIVATE case only)
+ old_ptr - pointer to memory object (before this call)
+ Flags -
+ new_size - desired new size of memory object
+
+
+Return Value:
+
+ If successful, returns pointer to memory object whose size is (at least)
+ equal to "new_size" and whose first MIN( old_size, new_size) bytes
+ are unchanged compared to corresponding bytes at "old_ptr".
+
+ If unsuccessful, returns NULL.
+
+--*/
+{
+ PVOID ptr;
+ ptr = HeapReAlloc( MemoryHandle, Flags, old_ptr, new_size);
+ if ( ptr == NULL) {
+ RplDump( ++RG_Assert,( "Error=%d", GetLastError()));
+ } else {
+ RplDump( RG_DebugLevel & RPL_DEBUG_MEMALLOC,(
+ "MemReAlloc( 0x%x, 0x%x, %d) => 0x%x", MemoryHandle, old_ptr, new_size, ptr));
+ }
+ return( ptr);
+}
diff --git a/private/net/svcdlls/rpl/server/open.c b/private/net/svcdlls/rpl/server/open.c
new file mode 100644
index 000000000..eb4b1c4e0
--- /dev/null
+++ b/private/net/svcdlls/rpl/server/open.c
@@ -0,0 +1,600 @@
+/*++
+
+Copyright (c) 1987-1993 Microsoft Corporation
+
+Module Name:
+
+ _open.c
+
+Abstract:
+
+ Initializes workstation specific data structures and returns the handle.
+
+ Provides similar functionality to rmapopen.c in LANMAN 2.1 code.
+
+Author:
+
+ Vladimir Z. Vulovic 27 - July - 1993
+
+Environment:
+
+ User mode
+
+Revision History :
+
+--*/
+
+#include "local.h"
+#include "database.h"
+#include "bbcfile.h"
+#include "fitfile.h"
+#include "open.h"
+
+// translates 32 bit physical address to 16-bit paragraphs
+#define PHYS_TO_PARA( physaddr) ((WORD)((physaddr) >> 4))
+
+#define MAX_WKSTA_PROFILES 10 // rplmgr supports now only 2!
+#define PROF_DATA_SIZE (sizeof(MBOOTDATA)+80) // include comment size!
+#define MAX_WKSTA_BUF ((DWORD)0xff00)
+
+
+
+DWORD FillString( OUT PCHAR Buffer, IN PCHAR String)
+{
+ strcpy( Buffer, String);
+ return( strlen( Buffer));
+}
+
+
+BOOL RplCreateWkstaLine(
+ IN OUT PRPL_WORKER_DATA pWorkerData,
+ OUT PDWORD pWkstaLineSize
+ )
+/*++
+ LANMAN 2.1 wksta record in RPL.MAP contained the following information:
+
+ Field 1: AdapterId - NO
+ Field 2: WkstaName
+ Field 3: Username/Password prompting field.
+ Field 4: Fit file name. - NO
+ Field 5: Name of server used to access files for booting. - NO (DLC server)
+ Field 6: Shared or Personal profile. - NO
+ Field 7: '~'
+ Field 8: '~'
+ Field 9: '~'
+ Field 10: ',,,'
+ Field 11: '~'
+ Field 12: Tag of associated LANMAN 2.1 boot block configuration (server) record. - NO
+ Field 13: '~' (but see below)
+ Field 14: Name of associtated profile. - NO
+ Field 15: Descriptive comment. - NO
+ Field 16: Optional IP address of a workstation.
+ Field 17: Optional TCP/IP subnet mask.
+ Field 18: Optional TCP/IP gateway address.
+
+ Out of the above, client side only needs Fields # 2, 3, 15, 16, 17 & 18.
+ All the other fields can be replaced with tildas. And of course if this
+ does not work we can always go back to the original wksta line & send him
+ all then cut back on the amount of info.
+
+ All of these lines worked fine for VLADIMV7 client. Checked "dir c:",
+ logging as vladimv on Redmond, treeconnecting & dir on remote drives.
+
+#define RPLTEST_WKSTALINE "02608C1B87A5 VLADIMV7 N fits\\dos500 VLADIMV3 S ~ ~ ~ ,,, ~ RDOST ~ d5elnk2 VLADIMV1~=>~shared~dos~profile ~ ~ ~"
+#define RPLTEST_WKSTALINE "~ VLADIMV7 N fits\\dos500 VLADIMV3 S ~ ~ ~ ,,, ~ RDOST ~ d5elnk2 VLADIMV1~=>~shared~dos~profile ~ ~ ~"
+#define RPLTEST_WKSTALINE "~ VLADIMV7 N fits\\dos500 VLADIMV3 S ~ ~ ~ ,,, ~ ~ ~ d5elnk2 VLADIMV1~=>~shared~dos~profile ~ ~ ~"
+#define RPLTEST_WKSTALINE "~ VLADIMV7 N ~ VLADIMV3 S ~ ~ ~ ,,, ~ ~ ~ d5elnk2 VLADIMV1~=>~shared~dos~profile ~ ~ ~"
+#define RPLTEST_WKSTALINE "~ VLADIMV7 N ~ ~ S ~ ~ ~ ,,, ~ ~ ~ d5elnk2 VLADIMV1~=>~shared~dos~profile ~ ~ ~"
+#define RPLTEST_WKSTALINE "~ VLADIMV7 N ~ ~ S ~ ~ ~ ,,, ~ ~ ~ d5elnk2 ~ ~ ~ ~"
+#define RPLTEST_WKSTALINE "~ VLADIMV7 N ~ ~ ~ ~ ~ ~ ,,, ~ ~ ~ d5elnk2 ~ ~ ~ ~"
+#define RPLTEST_WKSTALINE "~ VLADIMV7 N ~ ~ ~ ~ ~ ~ ,,, ~ ~ ~ ~ ~ ~ ~ ~"
+#define RPLTEST_WKSTALINE "~ VLADIMV7 N ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~"
+Field 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8
+
+More changes:
+ Client side software RPLBOOT.SYS has been changed to receive value for
+ TCPIP_NO_DHCP macro in Field 13 above. Sending value 1 will disable DHCP
+ on the client. Sending value 0 will enable DCHP on the client. But the
+ value of this field matters only if TCPIP is enabled for this client by
+ appropriate changes in boot block configuration file, config.sys and
+ autoexec.bat that this client uses.
+--*/
+{
+#define BLANK_FIELD "~ "
+#define COMMON_FIELD_LENGTH 2 // except for WkstaName & TcpIp strings
+#define MAX_TCPIP_ADDRESS_LENGTH 15
+
+ CHAR Buffer[ 14 * COMMON_FIELD_LENGTH + MAX_COMPUTERNAME_LENGTH + 1
+ + 3 * (MAX_TCPIP_ADDRESS_LENGTH + 1) + 1];
+ DWORD Length;
+ DWORD ByteCount;
+ DWORD Index;
+
+ //
+ // Field 1:
+ //
+ Length = FillString( Buffer, BLANK_FIELD);
+
+ //
+ // Field 2:
+ //
+ ByteCount = WideCharToMultiByte( // this counts the terminal null byte
+ CP_OEMCP,
+ 0,
+ pWorkerData->WkstaName,
+ -1, // WkstaName is null terminated
+ Buffer + Length, // output buffer
+ MAX_COMPUTERNAME_LENGTH + 1, // output buffer size
+ NULL, // no default character
+ NULL // no default character flag
+ );
+ if ( ByteCount == 0) {
+ RplDump( ++RG_Assert, ("Error=%d", GetLastError()));
+ pWorkerData->EventStrings[ 0] = pWorkerData->WkstaName;
+ pWorkerData->EventId = NELOG_RplWkstaInternal;
+ return( FALSE);
+ }
+ Length += ByteCount - 1;
+ Buffer[ Length++] = ' ';
+
+ //
+ // Field 3:
+ //
+ Buffer[ Length++] = (char)pWorkerData->LogonInput;
+ Buffer[ Length++] = ' ';
+
+ //
+ // Fields 4-12:
+ //
+ for ( Index = 4; Index <= 12; Index++) {
+ Length += FillString( Buffer + Length, BLANK_FIELD);
+ }
+
+ //
+ // Field 13:
+ //
+ Buffer[ Length++] = pWorkerData->DisableDhcp;
+ Buffer[ Length++] = ' ';
+
+ //
+ // Fields 14-15:
+ //
+ for ( Index = 14; Index <= 15; Index++) {
+ Length += FillString( Buffer + Length, BLANK_FIELD);
+ }
+
+ Length += FillTcpIpString( Buffer + Length, pWorkerData->TcpIpAddress);
+ Length += FillTcpIpString( Buffer + Length, pWorkerData->TcpIpSubnet);
+ Length += FillTcpIpString( Buffer + Length, pWorkerData->TcpIpGateway);
+
+ Buffer[ --Length] = 0; // overwrite last space
+
+//#define RPL_ELNK
+#ifdef RPL_ELNK
+ if ( !wcscmp( pWorkerData->pRcb->AdapterName, L"02608C0A9B37")) {
+ strcpy( Buffer, "02608C0A9B37 ELNK N fits\\dos500 VLADIMV3 S ~ ~ ~ ,,, ~ RDOSL ~ DOS500L ~ ~ ~ ~ ");
+ Length = strlen( Buffer);
+ }
+#endif
+
+ pWorkerData->WkstaLine = RplMemAlloc( pWorkerData->MemoryHandle, Length + 1);
+ if ( pWorkerData->WkstaLine == NULL) {
+ pWorkerData->EventStrings[ 0] = pWorkerData->WkstaName;
+ pWorkerData->EventId = NELOG_RplWkstaMemory;
+ return( FALSE);
+ }
+ memcpy( pWorkerData->WkstaLine, Buffer, Length + 1);
+ *pWkstaLineSize = Length + 1;
+ return( TRUE);
+}
+
+
+BOOL RplMakeWkstaBuf( IN OUT PRPL_WORKER_DATA pWorkerData)
+/*++
+
+Routine Description:
+
+ Procedure allocates and initializes bblock header and wksta specific
+ data. pWorkerData->wksta_buf will be set to point to this buffer.
+
+ Procedure:
+ - allocates and shrinks the file index table
+ - setup the boot block header and saves its static parameters
+ - makes the file list table and saves the parameters of DOS EXE/SYS/COMs
+ - builds the boot data for RPL MFSD
+ - fixes up the 32 bits physical boot block base and file block base addr
+ - copies and binds the resource table to the memory
+ - binds the files in the files list to the PC memory
+
+Arguments:
+
+Return Value:
+ TRUE if successful.
+ FALSE if unsuccessful (then we usually set termination event also).
+
+--*/
+{
+ DWORD index;
+ PBOOT_DATA pBootData; // ptr to boot data structure used by RPL MFSD
+ PRESOURCE_TBL resource_tbl; // OS2LDR parameters
+ PBOOT_BLOCK_HDR pBBH; // ptr to boot block header
+ DWORD wbuf_i; // running offset from boot block header
+ PBYTE pBuf; // a misc string
+ DWORD resource_tbl_len; //
+ DWORD WkstaLineSize; // for old style rpl.map wksta line
+ PBYTE wksta_buf; // space for the wksta specific data
+ DWORD wksta_buf_len; // length of wksta specific data
+ DWORD offData;
+ DWORD cbBootDataSize;
+ //
+ // boot_block_base is offset to the boottom of the lowest file in memory
+ //
+ DWORD boot_block_base = pWorkerData->boot_block_base;
+ DWORD file_block_base = pWorkerData->file_block_base;
+ PFLIST_TBL pFlistTbl = pWorkerData->flist_tbl;
+ DWORD lenFlistTbl = pWorkerData->flist_tbl_len;
+
+ RplDump( RG_DebugLevel & RPL_DEBUG_FLOW,( "++MakeWkstaBuf(0x%x", pWorkerData));
+
+ if ( !RplFitFile( pWorkerData)) {
+ return( FALSE);
+ }
+
+ //
+ // Clients still expect old style (rpl.map) wksta line.
+ //
+ if ( !RplCreateWkstaLine( pWorkerData, &WkstaLineSize)) {
+ return( FALSE);
+ }
+
+ cbBootDataSize =
+ sizeof(BOOT_DATA) + // size of RPL MiniFSD parameters
+ WkstaLineSize + // wksta record size, DBCS data
+ pWorkerData->ClientFitSize + // FIT file image size, DBCS data
+#ifdef RPL_ELNK
+#define RPL_ELNK_7_SPACES " "
+ sizeof( RPL_ELNK_7_SPACES) + // Pad with spaces after wksta line.
+ 1 + // Include 0x1A at the end of fit file.
+#endif
+ RG_CodePageSize; // code page size
+
+ wksta_buf_len =
+ cbBootDataSize +
+ pWorkerData->min_wksta_buf +
+ sizeof(BOOT_BLOCK_HDR) + // size of standard header structure
+ PATHLEN + 1 + // space for UNC name of DOS FAT hdr
+ MAX_WKSTA_PROFILES * PROF_DATA_SIZE; // max size of multiboot data
+
+
+ if ( wksta_buf_len > MAX_WKSTA_BUF) {
+ RplDump( ++RG_Assert, ( "wksta_buf_len=%d", wksta_buf_len));
+ pWorkerData->EventStrings[ 0] = pWorkerData->WkstaName;
+ pWorkerData->EventStrings[ 1] = pWorkerData->BbcFile;
+ pWorkerData->EventId = NELOG_Files_Dont_Fit;
+ return( FALSE);
+ }
+
+ //
+ // allocate wksta specific data, set the header base pointer and
+ // the base offset of dynamic data (wbuf_i)
+ //
+
+ // DbgUserBreakPoint(); // for debugging
+
+ pBBH = RplMemAlloc( pWorkerData->MemoryHandle, wksta_buf_len );
+ if( pBBH == NULL) {
+ pWorkerData->EventStrings[ 0] = pWorkerData->WkstaName;
+ pWorkerData->EventId = NELOG_RplWkstaMemory;
+ return( FALSE);
+ }
+
+ wksta_buf = (PBYTE)pBBH;
+ memset( wksta_buf, 0, wksta_buf_len);
+ wbuf_i = sizeof(BOOT_BLOCK_HDR) + (lenFlistTbl - 1) * sizeof(FILE_DATA);
+ //
+ // "id_stamp" is the first byte of boot block sent to the client
+ //
+ pBBH->id_stamp = 0x18cd; // reboot with int 18, if called
+ pBBH->usInfoLevel = 1; // version number of boot block
+ pBBH->file_list_len = (BYTE)lenFlistTbl;
+
+ //
+ // Copy the file names and parameters of the files in the boot block.
+ //
+
+ for ( index = 0; index < lenFlistTbl; index++) {
+
+ pBBH->aFileData[ index] = pFlistTbl[ index].FileData;
+ //
+ // Copy the file names that were saved in pFlistTbl element.
+ //
+ pBBH->aFileData[ index].name_offset = (WORD)wbuf_i;
+
+ memcpy( wksta_buf + wbuf_i, pFlistTbl[ index].DbcsFileName, pFlistTbl[ index].DbcsFileNameSize);
+ wbuf_i += pFlistTbl[ index].DbcsFileNameSize;
+
+ // Copy data from Boot Block Configuration file.
+
+ pBBH->aFileData[ index].param_offset = (WORD)wbuf_i;
+
+ //
+ // exe/com/sys files must have MS-DOS parameter format
+ //
+ if ((pFlistTbl[ index].FileData.file_type & OTHER_FILES_MASK) == 0) {
+ //
+ // Get length of EXE/SYS/COM DOS parameters.
+ //
+ BYTE length = pFlistTbl[ index].FileData.param_len;
+
+ if ( length) {
+ wksta_buf[ wbuf_i++] = (BYTE)( length + 1);
+ wksta_buf[ wbuf_i++] = ' '; // space before params
+ memcpy( wksta_buf + wbuf_i, pFlistTbl[ index].param_list, length);
+ wbuf_i += length;
+ wksta_buf[ wbuf_i++] = 0xD;
+ wksta_buf[ wbuf_i++] = 0xA;
+ wksta_buf[ wbuf_i++] = 0; // null terminated
+ pBBH->aFileData[ index].param_len = (BYTE)( length + 4);
+ } else {
+ wksta_buf[ wbuf_i++] = 0;
+ if ( pFlistTbl[ index].FileData.file_type & IS_EXECUTABLE_FILE) {
+ //
+ // For device drivers with no parameters, the parameter
+ // line is terminated by 0xA not 0xD,0xA
+ // BUGBUG Should param_len for them below be 2 instead of 3 ?
+ //
+ wksta_buf[ wbuf_i++] = 0xD;
+ }
+ wksta_buf[ wbuf_i++] = 0xA;
+ wksta_buf[ wbuf_i++] = 0; // null terminated
+ pBBH->aFileData[ index].param_len = 3;
+ }
+ }
+ }
+
+ //
+ // Set up the resource table. Here we copy data that describes the
+ // loader line in Boot Block Configuration file.
+ //
+ resource_tbl = (PRESOURCE_TBL)(wksta_buf + wbuf_i);
+ pBBH->offResourceTbl = (WORD)wbuf_i;
+ pBBH->cbResourceTbl = (WORD)pWorkerData->resource_tbl_size;
+ wbuf_i += pWorkerData->resource_tbl_size;
+ memcpy( resource_tbl, pWorkerData->resource_tbl, pWorkerData->resource_tbl_size);
+
+ //
+ // Round up the boot data offset (resource table format requires it).
+ //
+ wbuf_i = (wbuf_i + 15) & 0xfff0;
+ pBBH->offBootData = (WORD)wbuf_i;
+ pBootData = (PBOOT_DATA)(wksta_buf + wbuf_i);
+ pBootData->cbSize = (WORD)cbBootDataSize; // alignment OK
+ pBuf = wksta_buf + wbuf_i;
+
+ // offData will be used to walk & copy data structures mentioned
+ // in cbBootDataSize formula above.
+
+ offData = sizeof( BOOT_DATA); // initialize
+
+ //
+ // Copy the wksta record line from RPL.map (dbcs string).
+ //
+ strcpy( pBuf + offData, pWorkerData->WkstaLine);
+
+ pBootData->offWkstaLine = (WORD)offData;
+ pBootData->cbWkstaLine = (WORD)WkstaLineSize;
+ pBBH->offRplWkstaLine = (WORD)(wbuf_i + offData);
+ offData += WkstaLineSize;
+
+#ifdef RPL_ELNK
+ //
+ // Pad with spaces after wksta line.
+ //
+ memcpy( pBuf + offData, RPL_ELNK_7_SPACES, sizeof( RPL_ELNK_7_SPACES));
+ offData += sizeof( RPL_ELNK_7_SPACES);
+ pBootData->cbWkstaLine += sizeof( RPL_ELNK_7_SPACES);
+#endif
+
+ // Copy the FIT file image.
+
+ memcpy( pBuf + offData, pWorkerData->ClientFit, pWorkerData->ClientFitSize);
+ pBootData->offFit = (WORD)offData;
+ pBootData->cbFit = (WORD)pWorkerData->ClientFitSize;
+ offData += pWorkerData->ClientFitSize;
+#ifdef RPL_ELNK
+ //
+ // Append 0x1A at the end of fit file.
+ //
+ *( pBuf + offData - 1) = 0x1A;
+ *( pBuf + offData) = 0;
+ offData++;
+ pBootData->cbFit++;
+#endif
+
+ // Copy the code page.
+
+ memcpy( pBuf + offData, RG_CodePageBuffer, RG_CodePageSize);
+ pBootData->cbCodePage = (WORD)RG_CodePageSize;
+ pBootData->offCodePage = (WORD)offData;
+
+ wbuf_i += cbBootDataSize;
+ pBBH->offMBootData = (WORD)wbuf_i;
+
+ //
+ // Send no multiboot data - i.e. send NULL multiboot data by setting the
+ // size field of the first record to zero.
+
+#ifdef RPL_ELNK
+ if ( !wcscmp( pWorkerData->pRcb->AdapterName, L"02608C0A9B37")) {
+ MBOOTDATA UNALIGNED * pMbootData = (wksta_buf + wbuf_i);
+ pMbootData->cbSize = 0x2a;
+ strcpy( pMbootData->achProfile, "DOS500L");
+ strcpy( pMbootData->achProfileComment, "DOS 5.00 3Com Etherlink");
+ wbuf_i += 0x2a;
+ wbuf_i++; // or RPLBOOT.SYS would begin at 24e
+ } else {
+ *(WORD UNALIGNED *)(wksta_buf + wbuf_i) = 0;
+ wbuf_i += sizeof(WORD);
+ }
+#else
+ *(WORD UNALIGNED *)(wksta_buf + wbuf_i) = 0;
+ wbuf_i += sizeof(WORD);
+#endif
+
+
+ //
+ // DON'T SAVE ANY MORE DATA TO WKSTA BUFFER, WE MAY BE OUT OF MEMORY
+ // (do it before RplBuildMultiBootData)
+ //
+
+ //
+ // Set up the final size.
+ //
+ wksta_buf_len = (wbuf_i + 15) & 0xfff0;
+ pBBH->cbSize = (WORD)wksta_buf_len;
+
+ //
+ // FIX UP AND CHECK THE BOOT BLOCK TO THE WORKSTATION MEMORY !!!
+ // get the base address of the file list and boot block
+ //
+
+ if ( file_block_base == 0) {
+
+ // no ORG type line in BB.CFG file
+
+ if (boot_block_base == 0) {
+
+ // no BASE type line in BB.CFG file
+
+ boot_block_base = MIN_BBLOCK_BASE;
+ }
+ file_block_base = boot_block_base + wksta_buf_len;
+
+ } else if (boot_block_base == 0) {
+
+ boot_block_base = file_block_base - wksta_buf_len;
+ }
+
+ // Verify that the boot block is valid:
+ // - there must be space for wksta specific data in the boot block
+ // - boot block base must be above or equal the minimum value
+
+ if ( file_block_base < boot_block_base + wksta_buf_len
+ || boot_block_base < MIN_BBLOCK_BASE) {
+ RplDump( ++RG_Assert, (
+ "file_block_base=%d boot_block_base=%d wksta_buf_len=%d",
+ file_block_base, boot_block_base, wksta_buf_len));
+ pWorkerData->EventStrings[ 0] = pWorkerData->WkstaName;
+ pWorkerData->EventStrings[ 1] = pWorkerData->BbcFile;
+ pWorkerData->EventId = NELOG_Files_Dont_Fit;
+ return( FALSE);
+ }
+
+ //
+ // Get the resource table pointer (table is in wksta specific data),
+ // decrement length, because BootData is not included
+
+ if ( pWorkerData->loader_i != INVALID_FILE_OFFSET) {
+
+ // locate segments of resource table
+
+ resource_tbl_len = resource_tbl->entries - 1;
+ for ( index = 0; index < resource_tbl_len; index++) {
+
+ resource_tbl->file_tbl[ index].pos_in_paras
+ += PHYS_TO_PARA( file_block_base);
+ }
+
+ // the boot data must be in the resource table,
+
+ resource_tbl->file_tbl[ index].pos_in_paras
+ = PHYS_TO_PARA( boot_block_base + pBBH->offBootData);
+ resource_tbl->file_tbl[ index].file_len = cbBootDataSize;
+ }
+
+ // locate the boot block files to the PC memory
+
+ for ( index = 0; index < lenFlistTbl; index++) {
+ pBBH->aFileData[ index].file_addr += file_block_base;
+ }
+
+ //
+ // Do not bother to reallocate wksta_buf to its new smaller size.
+ // This would be a waste of time since wksta_buf will be freed
+ // when we are done with booting this client.
+ //
+
+ pWorkerData->wksta_buf = wksta_buf;
+ pWorkerData->wksta_buf_len = (WORD)wksta_buf_len;
+
+ //
+ // get file block base offset relative from the base of the boot block
+ //
+
+ pWorkerData->fblock_base_offset = file_block_base - boot_block_base;
+
+ pWorkerData->base_address = boot_block_base;
+ pWorkerData->jump_address = pBBH->aFileData[pWorkerData->rplboot_i].file_addr;
+
+ RplDump( RG_DebugLevel & RPL_DEBUG_FLOW,( "--MakeWkstaBuf(0x%x", pWorkerData));
+ return( TRUE);
+
+} // RplMakeWkstaBuf
+
+
+
+BOOL RplOpenData( IN OUT PRPL_WORKER_DATA pWorkerData)
+/*++
+
+Routine Description:
+ Opens rpl.map data for reading by a workstation thread.
+
+Arguments:
+ pWorkerData - worker data
+
+Return Value:
+ TRUE if success, else FALSE.
+
+--*/
+{
+ RplDump( RG_DebugLevel & RPL_DEBUG_FLOW,( "++OpenData(0x%x", pWorkerData));
+
+ //
+ // Retrive wksta database info.
+ //
+ if ( !RplWorkerFillWksta( pWorkerData)) {
+ if ( pWorkerData->EventId == NO_ERROR) {
+ pWorkerData->EventStrings[ 0] = pWorkerData->WkstaName;
+ pWorkerData->EventId = NERR_RplWkstaInfoCorrupted;
+ }
+ return( FALSE);
+ }
+
+ //
+ // Process boot block configuration file.
+ //
+ if ( !RplBbcFile( pWorkerData)) {
+ return( FALSE);
+ }
+
+ //
+ // Make wksta specific data buffer.
+ //
+ if ( !RplMakeWkstaBuf( pWorkerData)) {
+ return( FALSE);
+ }
+
+ //
+ // Initialize file list table (includes all downloaded files)
+ //
+
+ pWorkerData->pDataBuffer = NULL;
+ pWorkerData->cur_flist_i = 0;
+ pWorkerData->cur_offset = INVALID_FILE_OFFSET;
+ pWorkerData->cur_file_base_offset = INVALID_FILE_OFFSET;
+ pWorkerData->is_end_of_bblock = FALSE;
+ pWorkerData->hFile = INVALID_HANDLE_VALUE;
+
+ RplDump( RG_DebugLevel & RPL_DEBUG_FLOW,( "--OpenData(0x%x", pWorkerData));
+ return( TRUE);
+}
diff --git a/private/net/svcdlls/rpl/server/open.h b/private/net/svcdlls/rpl/server/open.h
new file mode 100644
index 000000000..f9cd7e545
--- /dev/null
+++ b/private/net/svcdlls/rpl/server/open.h
@@ -0,0 +1,27 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ open.h
+
+Abstract:
+
+ Exports from open.c
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 19 - November - 1993
+
+Environment:
+
+ User mode
+
+Revision History :
+
+--*/
+
+BOOL RplOpenData( IN OUT PRPL_WORKER_DATA pWorkerData);
+
+
diff --git a/private/net/svcdlls/rpl/server/profile.c b/private/net/svcdlls/rpl/server/profile.c
new file mode 100644
index 000000000..7cc782f29
--- /dev/null
+++ b/private/net/svcdlls/rpl/server/profile.c
@@ -0,0 +1,1139 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ profile.c
+
+Abstract:
+
+ Profile APIs.
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 19 - November - 1993
+
+Revision History:
+
+--*/
+
+#include "local.h"
+#include "rpldb.h"
+#include "db.h"
+#include "dblib.h"
+#include "config.h"
+#include "wksta.h"
+#define RPLPROFILE_ALLOCATE
+#include "profile.h"
+#undef RPLPROFILE_ALLOCATE
+
+#define PROFILE_FLAGS_DISK_PRESENT_TRUE ((DWORD)0x00000001)
+#define PROFILE_FLAGS_DISK_PRESENT_FALSE ((DWORD)0x00000002)
+#define PROFILE_FLAGS_MASK_DISK_PRESENT \
+ ( PROFILE_FLAGS_DISK_PRESENT_FALSE | \
+ PROFILE_FLAGS_DISK_PRESENT_TRUE )
+
+
+
+DWORD ProfileGetField(
+ IN PRPL_SESSION pSession,
+ IN DWORD FieldIndex,
+ OUT LPVOID * pData,
+ IN OUT LPINT pSpaceLeft
+ )
+{
+ BYTE LocalBuffer[ 300];
+ PBYTE Buffer;
+ DWORD DataSize;
+ DWORD BufferSize;
+ JET_ERR JetError;
+
+ switch( FieldIndex) {
+ case PROFILE_Flags:
+ Buffer = (PBYTE)pData;
+ BufferSize = sizeof( DWORD);
+ break;
+ default:
+ Buffer = LocalBuffer;
+ BufferSize = sizeof( LocalBuffer);
+ break;
+ }
+ JetError = JetRetrieveColumn( pSession->SesId, pSession->ProfileTableId,
+ ProfileTable[ FieldIndex].ColumnId, Buffer,
+ BufferSize, &DataSize, 0, NULL);
+ if ( JetError < 0) {
+ RplDump( ++RG_Assert, ( "JetError=%d", JetError));
+ return( NERR_RplProfileInfoCorrupted);
+ }
+ if ( Buffer != LocalBuffer) {
+ if ( BufferSize == DataSize) {
+ return( NO_ERROR);
+ } else {
+ RplDump( ++RG_Assert, ("Bad DataSize=0x%x", DataSize));
+ return( NERR_RplProfileInfoCorrupted);
+ }
+ }
+ //
+ // We have done with fixed data. From here on we deal with unicode
+ // strings only.
+ //
+ if ( DataSize > sizeof( LocalBuffer)) {
+ RplDump( ++RG_Assert, ( "Too big DataSize=0x%x", DataSize));
+ return( NERR_RplProfileInfoCorrupted);
+ }
+ if ( DataSize == 0) {
+ if ( JetError != JET_wrnColumnNull) {
+ RplDump( ++RG_Assert, ( "JetError=%d", JetError));
+ return( NERR_RplProfileInfoCorrupted);
+ } else {
+ *pData = NULL; // so RPC rpcrt4!_tree_size_ndr() does not bomb here
+ return( NO_ERROR);
+ }
+ }
+ if ( DataSize & 1 != 0 || wcslen((PWCHAR)LocalBuffer) + 1 != DataSize/2) {
+ RplDump( ++RG_Assert, ("LocalBuffer=0x%x, DataSize=0x%x", LocalBuffer, DataSize));
+ return( NERR_RplProfileInfoCorrupted);
+ }
+ *pData = MIDL_user_allocate( DataSize);
+ if ( *pData == NULL) {
+ RplDump( ++RG_Assert, ( "Error=%d", GetLastError()));
+ return( ERROR_NOT_ENOUGH_MEMORY);
+ }
+ memcpy( *pData, LocalBuffer, DataSize);
+ *pSpaceLeft -= DataSize;
+ return( NO_ERROR);
+}
+
+
+DWORD ProfileGetInfo(
+ IN PRPL_SESSION pSession,
+ IN LPWSTR ProfileName,
+ IN DWORD Level,
+ OUT LPVOID Buffer,
+ OUT PINT pSpaceLeft
+ )
+{
+ DWORD Error;
+ LPRPL_PROFILE_INFO_2 Info = Buffer;
+
+ switch( Level) {
+ case 2:
+ Error = ProfileGetField( pSession, PROFILE_FitPersonal, &Info->FitPersonal, pSpaceLeft);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ Error = ProfileGetField( pSession, PROFILE_FitShared, &Info->FitShared, pSpaceLeft);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ Error = ProfileGetField( pSession, PROFILE_BootName, &Info->BootName, pSpaceLeft);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ Error = ProfileGetField( pSession, PROFILE_ConfigName, &Info->ConfigName, pSpaceLeft);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ NOTHING; // fall through
+ case 1:
+ Error = ProfileGetField( pSession, PROFILE_Flags, (LPVOID *)&Info->Flags, pSpaceLeft);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ NOTHING; // fall through
+ case 0:
+ Error = ProfileGetField( pSession, PROFILE_ProfileComment, &Info->ProfileComment, pSpaceLeft);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ if ( ProfileName == NULL) {
+ Error = ProfileGetField( pSession, PROFILE_ProfileName, &Info->ProfileName, pSpaceLeft);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ } else {
+ DWORD DataSize = (wcslen( ProfileName) + 1) * sizeof(WCHAR);
+ Info->ProfileName = MIDL_user_allocate( DataSize);
+ if ( Info->ProfileName == NULL) {
+ return( ERROR_NOT_ENOUGH_MEMORY);
+ }
+ RplDump( RG_DebugLevel & RPL_DEBUG_PROFILE, ( "ProfileName=0x%x", Info->ProfileName));
+ memcpy( Info->ProfileName, ProfileName, DataSize);
+ *pSpaceLeft -= DataSize;
+ }
+ break;
+ default:
+ return( ERROR_INVALID_LEVEL);
+ break;
+ }
+ return( NO_ERROR);
+}
+
+
+
+VOID ProfileGetInfoCleanup(
+ IN DWORD Level,
+ IN OUT LPVOID Buffer
+ )
+{
+ LPRPL_PROFILE_INFO_2 Info = Buffer;
+
+ if ( Info == NULL) {
+ return;
+ }
+ switch( Level) {
+ case 2:
+ if ( Info->FitPersonal != NULL) {
+ MIDL_user_free( Info->FitPersonal);
+ }
+ if ( Info->FitShared != NULL) {
+ MIDL_user_free( Info->FitShared);
+ }
+ if ( Info->BootName != NULL) {
+ MIDL_user_free( Info->BootName);
+ }
+ if ( Info->ConfigName != NULL) {
+ MIDL_user_free( Info->ConfigName);
+ }
+ NOTHING; // fall through
+ case 1:
+ NOTHING; // fall through
+ case 0:
+ if ( Info->ProfileComment != NULL) {
+ MIDL_user_free( Info->ProfileComment);
+ }
+ if ( Info->ProfileName != NULL) {
+ MIDL_user_free( Info->ProfileName);
+ }
+ break;
+ }
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetrRplProfileEnum(
+ IN RPL_HANDLE ServerHandle,
+ IN LPWSTR AdapterName,
+ IN OUT LPRPL_PROFILE_ENUM ProfileEnum,
+ IN DWORD PrefMaxLength,
+ OUT LPDWORD TotalEntries,
+ IN OUT LPDWORD pResumeHandle OPTIONAL
+ )
+/*++
+ For more extensive comments see related code in NetrConfigEnum.
+--*/
+{
+ LPBYTE Buffer;
+ DWORD TypicalSize;
+ DWORD CoreSize;
+ DWORD Error;
+ INT SpaceLeft;
+ DWORD ArrayLength;
+ DWORD EntriesRead;
+ BOOL InfoError;
+ BOOL TableEnd;
+ RPL_FILTER Filter;
+ PRPL_FILTER pFilter;
+ PRPL_SESSION pSession = &RG_ApiSession;
+
+ switch( ProfileEnum->Level) {
+ case 2:
+ TypicalSize = CoreSize = sizeof( RPL_PROFILE_INFO_2);
+ TypicalSize += 20 * sizeof( WCHAR); // typical size of FitPersonal
+ TypicalSize += 20 * sizeof( WCHAR); // typical size of FitShared
+ TypicalSize += 8 * sizeof( WCHAR); // typical size of BootName
+ TypicalSize += 8 * sizeof( WCHAR); // typical size of ConfigName
+ NOTHING; // fall through
+ case 1:
+ if ( ProfileEnum->Level == 1) {
+ TypicalSize = CoreSize = sizeof( RPL_PROFILE_INFO_1);
+ }
+ NOTHING; // fall through
+ case 0:
+ if ( ProfileEnum->Level == 0) {
+ TypicalSize = CoreSize = sizeof( RPL_PROFILE_INFO_0);
+ }
+ TypicalSize += 20 * sizeof( WCHAR); // typical size of ProfileComment
+ TypicalSize += 8 * sizeof( WCHAR); // typical size of ProfileName
+ break;
+ default:
+ return( ERROR_INVALID_LEVEL);
+ break;
+ }
+
+ if ( AdapterName != NULL) {
+ pFilter = &Filter;
+ if ( !ValidHexName( AdapterName, RPL_ADAPTER_NAME_LENGTH, TRUE)) {
+ return( ERROR_INVALID_PARAMETER);
+ }
+ AdapterName[ 6] = 0; // truncate all but first 6 digits
+ pFilter->VendorId = wcstoul( AdapterName, NULL, 16);
+ pFilter->FindFirst = TRUE;
+ } else {
+ pFilter = NULL;
+ }
+
+ if ( PrefMaxLength == -1) {
+ SpaceLeft = DEFAULT_BUFFER_SIZE;
+ } else {
+ SpaceLeft = PrefMaxLength;
+ }
+
+ ArrayLength = SpaceLeft / TypicalSize;
+ if ( ArrayLength == 0) {
+ ArrayLength = 1; // try to return at least one element
+ }
+
+ Buffer = MIDL_user_allocate( ArrayLength * CoreSize);
+ if ( Buffer == NULL) {
+ return( ERROR_NOT_ENOUGH_MEMORY);
+ }
+ RplDump( RG_DebugLevel & RPL_DEBUG_PROFILE, (
+ "ProfileEnum: Buffer=0x%x, ArrayLength=0x%x", Buffer, ArrayLength));
+
+ ProfileEnum->ProfileInfo.Level0->Buffer = (LPRPL_PROFILE_INFO_0)Buffer;
+
+ EntriesRead = 0;
+ InfoError = FALSE;
+ Error = NO_ERROR;
+
+ EnterCriticalSection( &RG_ProtectDatabase);
+ Call( JetBeginTransaction( pSession->SesId));
+
+ if ( !RplFilterFirst( pSession, PROFILE_TABLE_TAG, pFilter, pResumeHandle, &TableEnd)) {
+ Error = NERR_RplCannotEnum;
+ goto cleanup;
+ }
+ if ( TableEnd == TRUE) {
+ goto cleanup;
+ }
+ for ( ; ; ) {
+ memset( Buffer, 0, CoreSize); // for cleanup to work properly
+ Error = ProfileGetInfo( pSession, NULL, ProfileEnum->Level, Buffer, &SpaceLeft);
+ if ( Error != NO_ERROR) {
+ InfoError = TRUE; // clean things up without holding crit sec
+ break;
+ }
+ EntriesRead++;
+ Buffer += CoreSize;
+ SpaceLeft -= CoreSize;
+ if ( !RplFilterNext( pSession, pSession->ProfileTableId, pFilter, &TableEnd)) {
+ Error = NERR_RplCannotEnum;
+ goto cleanup;
+ }
+ if ( TableEnd == TRUE) {
+ goto cleanup;
+ }
+ if ( SpaceLeft <= 0) {
+ Error = ERROR_MORE_DATA;
+ break;
+ }
+ if ( EntriesRead >= ArrayLength) {
+ Error = ERROR_MORE_DATA;
+ break;
+ }
+ }
+cleanup:
+ Call( JetCommitTransaction( pSession->SesId, 0));
+ LeaveCriticalSection( &RG_ProtectDatabase);
+ if ( InfoError == TRUE) {
+ ProfileGetInfoCleanup( ProfileEnum->Level, Buffer);
+ }
+ if ( Error == NO_ERROR) {
+ *TotalEntries = EntriesRead;
+ } else if ( Error == ERROR_MORE_DATA) {
+ *TotalEntries = EntriesRead * 2; // we cheat here
+ } else {
+ //
+ // Cleanup in case of "bad" errors.
+ //
+ while ( EntriesRead > 0) {
+ EntriesRead--;
+ Buffer -= CoreSize;
+ ProfileGetInfoCleanup( ProfileEnum->Level, Buffer);
+ }
+ MIDL_user_free( Buffer);
+ }
+
+ RplDump( RG_DebugLevel & RPL_DEBUG_PROFILE, ("ProfileEnum: EntriesRead = 0x%x", EntriesRead));
+
+ ProfileEnum->ProfileInfo.Level0->EntriesRead = EntriesRead;
+ if ( EntriesRead == 0) {
+ ProfileEnum->ProfileInfo.Level0->Buffer = NULL;
+ }
+
+ if ( ARGUMENT_PRESENT( pResumeHandle)) {
+ if ( Error == ERROR_MORE_DATA && EntriesRead > 0) {
+ EnterCriticalSection( &RG_ProtectDatabase);
+ Call( JetBeginTransaction( pSession->SesId));
+ RplFilterSave( pSession, (DWORD)ServerHandle, pFilter,
+ ((LPRPL_PROFILE_INFO_0)(Buffer-CoreSize))->ProfileName,
+ pResumeHandle);
+ Call( JetCommitTransaction( pSession->SesId, 0));
+ LeaveCriticalSection( &RG_ProtectDatabase);
+ } else {
+ *pResumeHandle = 0; // resume from beginning
+ }
+ }
+
+ return( Error);
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetrRplProfileGetInfo(
+ IN RPL_HANDLE ServerHandle,
+ IN LPWSTR ProfileName,
+ IN DWORD Level,
+ OUT LPRPL_PROFILE_INFO_STRUCT ProfileInfoStruct
+ )
+{
+ DWORD Error;
+ LPBYTE Buffer;
+ INT SpaceLeft;
+ PRPL_SESSION pSession = &RG_ApiSession;
+
+ _wcsupr( ProfileName);
+
+ switch( Level) {
+ case 0:
+ Buffer = MIDL_user_allocate( sizeof( RPL_PROFILE_INFO_0));
+ if ( Buffer == NULL) {
+ return( ERROR_NOT_ENOUGH_MEMORY);
+ }
+ memset( Buffer, 0, sizeof( RPL_PROFILE_INFO_0));
+ ProfileInfoStruct->ProfileInfo0 = (LPRPL_PROFILE_INFO_0)Buffer;
+ break;
+ case 1:
+ Buffer = MIDL_user_allocate( sizeof( RPL_PROFILE_INFO_1));
+ if ( Buffer == NULL) {
+ return( ERROR_NOT_ENOUGH_MEMORY);
+ }
+ memset( Buffer, 0, sizeof( RPL_PROFILE_INFO_1));
+ ProfileInfoStruct->ProfileInfo1 = (LPRPL_PROFILE_INFO_1)Buffer;
+ break;
+ case 2:
+ Buffer = MIDL_user_allocate( sizeof( RPL_PROFILE_INFO_2));
+ if ( Buffer == NULL) {
+ return( ERROR_NOT_ENOUGH_MEMORY);
+ }
+ memset( Buffer, 0, sizeof( RPL_PROFILE_INFO_2));
+ ProfileInfoStruct->ProfileInfo2 = (LPRPL_PROFILE_INFO_2)Buffer;
+ break;
+ default:
+ return( ERROR_INVALID_LEVEL);
+ break;
+ }
+
+ EnterCriticalSection( &RG_ProtectDatabase);
+ Call( JetBeginTransaction( pSession->SesId));
+
+ if ( !RplFind( pSession, PROFILE_TABLE_TAG, ProfileName)) {
+ Error = NERR_RplProfileNotFound;
+ } else {
+ Error = ProfileGetInfo( pSession, ProfileName, Level, Buffer, &SpaceLeft);
+ }
+
+ Call( JetCommitTransaction( pSession->SesId, 0));
+ LeaveCriticalSection( &RG_ProtectDatabase);
+
+ if ( Error != NO_ERROR) {
+ ProfileGetInfoCleanup( Level, Buffer);
+ switch( Level) {
+ case 0:
+ MIDL_user_free( Buffer);
+ ProfileInfoStruct->ProfileInfo0 = NULL;
+ break;
+ case 1:
+ MIDL_user_free( Buffer);
+ ProfileInfoStruct->ProfileInfo1 = NULL;
+ break;
+ }
+ }
+ return( Error);
+}
+
+
+DWORD ProfileSetField(
+ IN PRPL_SESSION pSession,
+ IN DWORD FieldIndex,
+ IN LPVOID Data,
+ IN DWORD DataSize
+ )
+{
+ CallM( JetSetColumn( pSession->SesId, pSession->ProfileTableId,
+ ProfileTable[ FieldIndex].ColumnId, Data, DataSize, 0, NULL));
+ return( NO_ERROR);
+}
+
+
+DWORD ProfileSetInfo(
+ IN PRPL_SESSION pSession,
+ IN DWORD Level,
+ OUT LPVOID Buffer,
+ OUT LPDWORD pErrorParameter
+ )
+{
+ LPRPL_PROFILE_INFO_2 Info = Buffer;
+ switch( Level) {
+ case 2:
+ if ( Info->FitPersonal != NULL) {
+ *pErrorParameter = PROFILE_FitPersonal;
+ CallM( JetSetColumn( pSession->SesId, pSession->ProfileTableId,
+ ProfileTable[ PROFILE_FitPersonal].ColumnId,
+ Info->FitPersonal,
+ ( wcslen( Info->FitPersonal) + 1) * sizeof(WCHAR),
+ 0, NULL));
+ }
+ if ( Info->FitShared != NULL) {
+ *pErrorParameter = PROFILE_FitShared;
+ CallM( JetSetColumn( pSession->SesId, pSession->ProfileTableId,
+ ProfileTable[ PROFILE_FitShared].ColumnId,
+ Info->FitShared,
+ ( wcslen( Info->FitShared) + 1) * sizeof(WCHAR),
+ 0, NULL));
+ }
+ if ( Info->BootName != NULL) {
+ *pErrorParameter = PROFILE_BootName;
+ CallM( JetSetColumn( pSession->SesId, pSession->ProfileTableId,
+ ProfileTable[ PROFILE_BootName].ColumnId,
+ Info->BootName,
+ ( wcslen( Info->BootName) + 1) * sizeof(WCHAR),
+ 0, NULL));
+ }
+ if ( Info->ConfigName != NULL) {
+ *pErrorParameter = PROFILE_ConfigName;
+ CallM( JetSetColumn( pSession->SesId, pSession->ProfileTableId,
+ ProfileTable[ PROFILE_ConfigName].ColumnId,
+ Info->ConfigName,
+ ( wcslen( Info->ConfigName) + 1) * sizeof(WCHAR),
+ 0, NULL));
+ }
+ NOTHING; // fall through
+ case 1:
+ if ( Info->Flags != 0) {
+ *pErrorParameter = PROFILE_Flags;
+ CallM( JetSetColumn( pSession->SesId, pSession->ProfileTableId,
+ ProfileTable[ PROFILE_Flags].ColumnId,
+ &Info->Flags, sizeof( Info->Flags), 0, NULL));
+ }
+ NOTHING; // fall through
+ case 0:
+ if ( Info->ProfileComment != NULL) {
+ *pErrorParameter = PROFILE_ProfileComment;
+ CallM( JetSetColumn( pSession->SesId, pSession->ProfileTableId,
+ ProfileTable[ PROFILE_ProfileComment].ColumnId,
+ Info->ProfileComment,
+ ( wcslen( Info->ProfileComment) + 1) * sizeof(WCHAR),
+ 0, NULL));
+ }
+ if ( Info->ProfileName != NULL) {
+ *pErrorParameter = PROFILE_ProfileName;
+ CallM( JetSetColumn( pSession->SesId, pSession->ProfileTableId,
+ ProfileTable[ PROFILE_ProfileName].ColumnId,
+ Info->ProfileName,
+ ( wcslen( Info->ProfileName) + 1) * sizeof(WCHAR),
+ 0, NULL));
+ }
+ break;
+ }
+ return( NO_ERROR);
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetrRplProfileSetInfo(
+ IN RPL_HANDLE ServerHandle,
+ IN LPWSTR ProfileName,
+ IN DWORD Level,
+ IN LPRPL_PROFILE_INFO_STRUCT ProfileInfoStruct,
+ OUT LPDWORD pErrorParameter OPTIONAL
+ )
+/*++
+ If parameter pointer is NULL, then we do not change such parameters.
+ If parameter non-pointer has a special value, then we do not change
+ such parameters.
+
+ Some parameters cannot be changed for now. If caller deos not specify
+ a no-change value for such a parameter, we print a warning message &
+ pretend we received a no change value.
+--*/
+{
+ DWORD Error;
+ DWORD SpaceLeft;
+ DWORD ErrorParameter;
+ LPVOID Buffer;
+ LPRPL_PROFILE_INFO_2 Info;
+ PWCHAR ConfigName;
+ PRPL_SESSION pSession = &RG_ApiSession;
+
+ ErrorParameter = INVALID_ERROR_PARAMETER;
+ ConfigName = NULL;
+ _wcsupr( ProfileName);
+
+ Info = Buffer = ProfileInfoStruct->ProfileInfo2;
+ switch( Level) {
+ case 2:
+ if ( !ValidName( Info->FitPersonal, RPL_MAX_STRING_LENGTH, FALSE)) {
+ ErrorParameter = PROFILE_FitPersonal;
+ break;
+ }
+ if ( !ValidName( Info->FitShared, RPL_MAX_STRING_LENGTH, FALSE)) {
+ ErrorParameter = PROFILE_FitShared;
+ break;
+ }
+ if ( !ValidName( Info->BootName, RPL_MAX_BOOT_NAME_LENGTH, FALSE)) {
+ ErrorParameter = PROFILE_BootName;
+ break;
+ }
+ if ( Info->BootName != NULL) {
+ _wcsupr( Info->BootName);
+ }
+ if ( !ValidName( Info->ConfigName, RPL_MAX_CONFIG_NAME_LENGTH, FALSE)) {
+ ErrorParameter = PROFILE_ConfigName;
+ break;
+ }
+ if ( Info->ConfigName != NULL) {
+ _wcsupr( Info->ConfigName);
+ }
+ NOTHING; // fall through
+ case 1:
+ switch( Info->Flags) {
+ case PROFILE_FLAGS_DISK_PRESENT_TRUE:
+ case PROFILE_FLAGS_DISK_PRESENT_FALSE:
+ Info->Flags = 0;
+ NOTHING; // fall through
+ case 0:
+ break;
+ default:
+ ErrorParameter = PROFILE_Flags;
+ break;
+ }
+ if ( ErrorParameter != INVALID_ERROR_PARAMETER) {
+ break;
+ }
+ NOTHING; // fall through
+ case 0:
+ if ( RPL_STRING_TOO_LONG( Info->ProfileComment)) {
+ ErrorParameter = PROFILE_ProfileComment;
+ break;
+ }
+ if ( Info->ProfileName != NULL) {
+ _wcsupr( Info->ProfileName);
+ if ( wcscmp( Info->ProfileName, ProfileName)) {
+ RplDump( ++RG_Assert, ("Change of ProfileName not supported yet."));
+ ErrorParameter = PROFILE_ProfileName;
+ break;
+ }
+ MIDL_user_free( Info->ProfileName);
+ Info->ProfileName = NULL;
+ }
+ break;
+ default:
+ return( ERROR_INVALID_LEVEL);
+ break;
+ }
+
+ if ( ErrorParameter != INVALID_ERROR_PARAMETER) {
+ if ( ARGUMENT_PRESENT( pErrorParameter)) {
+ *pErrorParameter = ErrorParameter;
+ }
+ return( ERROR_INVALID_PARAMETER);
+ }
+ ErrorParameter = 0;
+
+ EnterCriticalSection( &RG_ProtectDatabase);
+ Call( JetBeginTransaction( pSession->SesId));
+
+ if ( !RplFind( pSession, PROFILE_TABLE_TAG, ProfileName)) {
+ Error = NERR_RplProfileNotFound;
+ goto cleanup;
+ }
+
+ if ( Info->ConfigName != NULL) {
+ Error = ProfileGetField( pSession, PROFILE_ConfigName, &ConfigName, &SpaceLeft);
+ if ( Error != NO_ERROR) {
+ goto cleanup;
+ }
+ if ( wcscmp( Info->ConfigName, ConfigName)) {
+ RplDump( ++RG_Assert, ("Change of ConfigName not supported yet."));
+ ErrorParameter = PROFILE_ConfigName;
+ Error = ERROR_INVALID_PARAMETER;
+ goto cleanup;
+ }
+ MIDL_user_free( Info->ConfigName);
+ Info->ConfigName = NULL;
+ }
+
+ CallJ( JetPrepareUpdate( pSession->SesId, pSession->ProfileTableId, JET_prepReplace));
+
+ Error = ProfileSetInfo( pSession, Level, Buffer, &ErrorParameter);
+ if ( Error != ERROR_SUCCESS) {
+ goto cleanup;
+ }
+
+ ErrorParameter = 0;
+ CallJ( JetUpdate( pSession->SesId, pSession->ProfileTableId, NULL, 0, NULL));
+
+cleanup:
+ if ( Error == NO_ERROR) {
+ Call( JetCommitTransaction( pSession->SesId, JET_bitCommitFlush));
+ } else {
+ Call( JetRollback( pSession->SesId, JET_bitRollbackAll));
+ }
+ LeaveCriticalSection( &RG_ProtectDatabase);
+
+ if ( ConfigName != NULL) {
+ MIDL_user_free( ConfigName);
+ }
+ if ( Error != ERROR_SUCCESS) {
+ if ( ARGUMENT_PRESENT( pErrorParameter)) {
+ *pErrorParameter = ErrorParameter;
+ }
+ }
+ return( Error);
+}
+
+
+DWORD AddFileExtension(
+ IN PWCHAR * pFilePath,
+ IN PWCHAR FileExtension,
+ IN BOOLEAN ExtensionOK
+ )
+{
+#define DOT_CHAR L'.'
+#define BACK_SLASH_CHAR L'\\'
+ PWCHAR FilePathEx;
+ PWCHAR pDot;
+ DWORD Length;
+
+ if ( *pFilePath == NULL) {
+ RPL_RETURN( NERR_RplConfigInfoCorrupted);
+ }
+
+ pDot = wcsrchr( *pFilePath, DOT_CHAR);
+ if ( pDot != NULL) {
+ //
+ // Found a DOT. FilePath may have an extension.
+ //
+ if ( wcschr( pDot, BACK_SLASH_CHAR) == NULL) {
+ //
+ // There is no backslash after the DOT. FilePath has an
+ // extension. Return error if caller insists that file
+ // should have no extension.
+ //
+ if ( !ExtensionOK) {
+ RPL_RETURN( NERR_RplConfigInfoCorrupted);
+ }
+ return( NO_ERROR);
+ }
+ }
+ Length = wcslen( *pFilePath) + wcslen( FileExtension);
+ FilePathEx = MIDL_user_allocate( (Length + 1) * sizeof(WCHAR));
+ if ( FilePathEx == NULL) {
+ RplDump( ++RG_Assert, ( "Error=%d", GetLastError()));
+ return( ERROR_NOT_ENOUGH_MEMORY);
+ }
+ wcscpy( FilePathEx, *pFilePath);
+ wcscat( FilePathEx, FileExtension);
+ MIDL_user_free( *pFilePath);
+ *pFilePath = FilePathEx;
+ return( NO_ERROR);
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetrRplProfileAdd(
+ IN RPL_HANDLE ServerHandle,
+ IN DWORD Level,
+ OUT LPRPL_PROFILE_INFO_STRUCT ProfileInfoStruct,
+ OUT LPDWORD pErrorParameter OPTIONAL
+ )
+{
+ LPRPL_PROFILE_INFO_2 Info;
+ LPVOID Buffer;
+ DWORD Error;
+ DWORD ErrorParameter;
+ DWORD DataSize;
+ BOOL FreeBootName = FALSE;
+ BOOL FreeFitShared = FALSE;
+ BOOL FreeFitPersonal = FALSE;
+ PWCHAR DirName = NULL;
+ PWCHAR DirName2 = NULL;
+ PWCHAR DirName3 = NULL;
+ PWCHAR DirName4 = NULL;
+ JET_ERR JetError;
+ BOOL DiskDelete = FALSE;
+ PRPL_SESSION pSession = &RG_ApiSession;
+
+ ErrorParameter = INVALID_ERROR_PARAMETER;
+ Buffer = Info = ProfileInfoStruct->ProfileInfo2;
+ switch( Level) {
+ case 2:
+ if ( !ValidName( Info->FitPersonal, RPL_MAX_STRING_LENGTH, FALSE)) {
+ ErrorParameter = PROFILE_FitPersonal;
+ break;
+ }
+ if ( !ValidName( Info->FitShared, RPL_MAX_STRING_LENGTH, FALSE)) {
+ ErrorParameter = PROFILE_FitShared;
+ break;
+ }
+ if ( !ValidName( Info->BootName, RPL_MAX_BOOT_NAME_LENGTH, FALSE)) {
+ ErrorParameter = PROFILE_BootName;
+ break;
+ }
+ if ( Info->BootName != NULL) {
+ _wcsupr( Info->BootName);
+ }
+ if ( !ValidName( Info->ConfigName, RPL_MAX_CONFIG_NAME_LENGTH, TRUE)) {
+ ErrorParameter = PROFILE_ConfigName;
+ break;
+ }
+ _wcsupr( Info->ConfigName);
+ if ( Info->Flags != 0) {
+ ErrorParameter = PROFILE_Flags;
+ break;
+ }
+ if ( RPL_STRING_TOO_LONG( Info->ProfileComment)) {
+ ErrorParameter = PROFILE_ProfileComment;
+ break;
+ }
+ if ( !ValidName( Info->ProfileName, RPL_MAX_PROFILE_NAME_LENGTH, TRUE)) {
+ ErrorParameter = PROFILE_ProfileName;
+ break;
+ }
+ _wcsupr( Info->ProfileName);
+ break;
+ default:
+ return( ERROR_INVALID_LEVEL);
+ break;
+ }
+
+ if ( ErrorParameter != INVALID_ERROR_PARAMETER) {
+ if ( ARGUMENT_PRESENT( pErrorParameter)) {
+ *pErrorParameter = ErrorParameter;
+ }
+ return( ERROR_INVALID_PARAMETER);
+ }
+
+ EnterCriticalSection( &RG_ProtectDatabase);
+ Call( JetBeginTransaction( pSession->SesId));
+
+ if ( !RplFind( pSession, CONFIG_TABLE_TAG, Info->ConfigName)) {
+ Error = NERR_RplConfigNotFound;
+ goto cleanup;
+ }
+ // BUGBUG Should make sure CONFIG is enabled
+ if ( Info->BootName == NULL) {
+ Error = ConfigGetField( pSession, CONFIG_BootName, &Info->BootName, &DataSize);
+ if ( Error != NO_ERROR) {
+ goto cleanup;
+ }
+ FreeBootName = TRUE;
+ }
+ if ( Info->FitShared == NULL) {
+ Error = ConfigGetField( pSession, CONFIG_FitShared, &Info->FitShared, &DataSize);
+ if ( Error != NO_ERROR) {
+ goto cleanup;
+ }
+ FreeFitShared = TRUE;
+ Error = AddFileExtension( &Info->FitShared, L".FIT", TRUE);
+ if ( Error != NO_ERROR) {
+ goto cleanup;
+ }
+ }
+ if ( Info->FitPersonal == NULL) {
+ Error = ConfigGetField( pSession, CONFIG_FitPersonal, &Info->FitPersonal, &DataSize);
+ if ( Error != NO_ERROR) {
+ goto cleanup;
+ }
+ FreeFitPersonal = TRUE;
+ Error = AddFileExtension( &Info->FitPersonal, L".FIT", TRUE);
+ if ( Error != NO_ERROR) {
+ goto cleanup;
+ }
+ }
+ Error = ConfigGetField( pSession, CONFIG_DirName, &DirName, &DataSize);
+ if ( Error != NO_ERROR) {
+ goto cleanup;
+ }
+ Error = ConfigGetField( pSession, CONFIG_DirName2, &DirName2, &DataSize);
+ if ( Error != NO_ERROR) {
+ goto cleanup;
+ }
+ Error = ConfigGetField( pSession, CONFIG_DirName3, &DirName3, &DataSize);
+ if ( Error != NO_ERROR) {
+ goto cleanup;
+ }
+ Error = ConfigGetField( pSession, CONFIG_DirName4, &DirName4, &DataSize);
+ if ( Error != NO_ERROR) {
+ goto cleanup;
+ }
+ //
+ // The call to ProfileSetInfo() will add PROFILE record to the database.
+ // The call may succeed but we may still fail to do the treecopy
+ // operations below. We can detect this situation any time later on
+ // by the value of the Flags bit.
+ //
+ Info->Flags = PROFILE_FLAGS_DISK_PRESENT_FALSE;
+
+ CallJ( JetPrepareUpdate( pSession->SesId, pSession->ProfileTableId, JET_prepInsert));
+
+ Error = ProfileSetInfo( pSession, 2, Buffer, &ErrorParameter);
+ if ( Error != ERROR_SUCCESS) {
+ goto cleanup;
+ }
+ ErrorParameter = 0;
+ JetError = JetUpdate( pSession->SesId, pSession->ProfileTableId, NULL, 0, NULL);
+ if ( JetError < 0) {
+ if ( JetError == JET_errKeyDuplicate) {
+ Error = NERR_RplProfileNameUnavailable;
+ } else {
+ RplDump( ++RG_Assert, ("JetError=%d", JetError));
+ Error = NERR_RplInternal;
+ }
+ goto cleanup;
+ }
+
+ DiskDelete = TRUE; // just in case we fail before we are done
+ Error = ProfileDiskAdd( TRUE, Info->ProfileName, DirName, DirName2,
+ DirName3, DirName4);
+ if ( Error != NO_ERROR) {
+ goto cleanup;
+ }
+ if ( !RplFind( pSession, PROFILE_TABLE_TAG, Info->ProfileName)) {
+ Error = NERR_RplInternal;
+ goto cleanup;
+ }
+ CallJ( JetPrepareUpdate( pSession->SesId, pSession->ProfileTableId, JET_prepReplace));
+ Info->Flags = PROFILE_FLAGS_DISK_PRESENT_TRUE;
+ Error = ProfileSetField( pSession, PROFILE_Flags, &Info->Flags, sizeof(Info->Flags));
+ if ( Error != NO_ERROR) {
+ goto cleanup;
+ }
+ JetError = JetUpdate( pSession->SesId, pSession->ProfileTableId, NULL, 0, NULL);
+ if ( JetError < 0) {
+ RplDump( ++RG_Assert, ("JetError=%d", JetError));
+ Error = NERR_RplInternal;
+ goto cleanup;
+ }
+ DiskDelete = FALSE; // success, make sure we do not delete disk data
+
+cleanup:
+ if ( DiskDelete == TRUE) {
+ //
+ // ProfileDiskAdd deletion code does not care about DirName*
+ //
+ ProfileDiskAdd( FALSE, Info->ProfileName, NULL, NULL, NULL, NULL);
+ }
+ if ( Error == NO_ERROR) {
+ Call( JetCommitTransaction( pSession->SesId, JET_bitCommitFlush));
+ } else {
+ Call( JetRollback( pSession->SesId, JET_bitRollbackAll));
+ }
+ LeaveCriticalSection( &RG_ProtectDatabase);
+
+ if ( FreeBootName == TRUE) {
+ MIDL_user_free( Info->BootName);
+ Info->BootName = NULL;
+ }
+ if ( FreeFitShared == TRUE) {
+ MIDL_user_free( Info->FitShared);
+ Info->FitShared = NULL;
+ }
+ if ( FreeFitPersonal == TRUE) {
+ MIDL_user_free( Info->FitPersonal);
+ Info->FitPersonal = NULL;
+ }
+ if ( DirName != NULL) {
+ MIDL_user_free( DirName);
+ }
+ if ( DirName2 != NULL) {
+ MIDL_user_free( DirName2);
+ }
+ if ( DirName3 != NULL) {
+ MIDL_user_free( DirName3);
+ }
+ if ( DirName4 != NULL) {
+ MIDL_user_free( DirName4);
+ }
+ if ( Error != ERROR_SUCCESS) {
+ if ( ARGUMENT_PRESENT( pErrorParameter)) {
+ *pErrorParameter = ErrorParameter;
+ }
+ }
+ return( Error);
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetrRplProfileDel(
+ IN RPL_HANDLE ServerHandle,
+ IN LPWSTR ProfileName
+ )
+{
+ DWORD Error;
+ PRPL_SESSION pSession = &RG_ApiSession;
+
+ _wcsupr( ProfileName);
+
+ EnterCriticalSection( &RG_ProtectDatabase);
+ Call( JetBeginTransaction( pSession->SesId));
+
+ if ( RplFindByField( pSession, WKSTA_TABLE_TAG,
+ WKSTA_INDEX_ProfileNameWkstaName, ProfileName)) {
+ //
+ // We found a WKSTA record which uses this PROFILE.
+ //
+ Error = NERR_RplProfileNotEmpty;
+ goto cleanup;
+ }
+ if ( !RplFind( pSession, PROFILE_TABLE_TAG, ProfileName)) {
+ Error = NERR_RplProfileNotFound;
+ goto cleanup;
+ }
+ CallJ( JetDelete( pSession->SesId, pSession->ProfileTableId));
+ ProfileDiskAdd( FALSE, ProfileName, NULL, NULL, NULL, NULL);
+ Error = NO_ERROR;
+
+cleanup:
+ if ( Error == NO_ERROR) {
+ Call( JetCommitTransaction( pSession->SesId, JET_bitCommitFlush));
+ } else {
+ Call( JetRollback( pSession->SesId, JET_bitRollbackAll));
+ }
+ LeaveCriticalSection( &RG_ProtectDatabase);
+ return( Error);
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetrRplProfileClone(
+ IN RPL_HANDLE ServerHandle,
+ IN LPWSTR SourceProfileName,
+ IN LPWSTR TargetProfileName,
+ IN LPWSTR TargetProfileComment
+ )
+{
+ RPL_PROFILE_INFO_2 Info;
+ DWORD Error;
+ DWORD ErrorParameter;
+ DWORD DataSize;
+ PWCHAR SaveProfileName;
+ PWCHAR SaveProfileComment;
+ JET_ERR JetError;
+ BOOL DiskDelete = FALSE;
+ PRPL_SESSION pSession = &RG_ApiSession;
+
+ if ( !ValidName( SourceProfileName, RPL_MAX_PROFILE_NAME_LENGTH, TRUE)) {
+ return( ERROR_INVALID_PARAMETER);
+ }
+ _wcsupr( SourceProfileName);
+ if ( !ValidName( TargetProfileName, RPL_MAX_PROFILE_NAME_LENGTH, TRUE)) {
+ return( ERROR_INVALID_PARAMETER);
+ }
+ _wcsupr( TargetProfileName);
+ if ( RPL_STRING_TOO_LONG( TargetProfileComment)) {
+ return( ERROR_INVALID_PARAMETER);
+ }
+
+ //
+ // Zero all the pointers so we can safely call ProfileGetInfoCleanup().
+ // in all the cases.
+ //
+ memset( &Info, 0, sizeof( Info));
+
+ EnterCriticalSection( &RG_ProtectDatabase);
+ Call( JetBeginTransaction( pSession->SesId));
+
+ if ( !RplFind( pSession, PROFILE_TABLE_TAG, SourceProfileName)) {
+ Error = NERR_RplProfileNotFound;
+ goto cleanup;
+ }
+ Error = ProfileGetInfo( pSession, SourceProfileName, 2, &Info, &DataSize);
+ if ( Error != NO_ERROR) {
+ goto cleanup;
+ }
+ //
+ // Save source data, then overload target data.
+ //
+ SaveProfileName = Info.ProfileName;
+ SaveProfileComment = Info.ProfileComment;
+ Info.ProfileName = TargetProfileName;
+ Info.ProfileComment = TargetProfileComment;
+
+ //
+ // The call to ProfileSetInfo() will add PROFILE record to the database.
+ // The call may succeed but we may still fail to do the treecopy
+ // operations below. We can detect this situation any time later on
+ // by the value of the Flags bit.
+ //
+ Info.Flags = PROFILE_FLAGS_DISK_PRESENT_FALSE;
+
+ CallJ( JetPrepareUpdate( pSession->SesId, pSession->ProfileTableId, JET_prepInsert));
+
+ Error = ProfileSetInfo( pSession, 2, &Info, &ErrorParameter);
+ if ( Error != ERROR_SUCCESS) {
+ goto cleanup;
+ }
+ ErrorParameter = 0;
+ JetError = JetUpdate( pSession->SesId, pSession->ProfileTableId, NULL, 0, NULL);
+ if ( JetError < 0) {
+ if ( JetError == JET_errKeyDuplicate) {
+ Error = NERR_RplProfileNameUnavailable;
+ } else {
+ RplDump( ++RG_Assert, ("JetError=%d", JetError));
+ Error = NERR_RplInternal;
+ }
+ goto cleanup;
+ }
+
+ DiskDelete = TRUE; // just in case we fail before we are done
+ Error = ProfileDiskClone( TRUE, SourceProfileName, TargetProfileName);
+ if ( Error != NO_ERROR) {
+ goto cleanup;
+ }
+ if ( !RplFind( pSession, PROFILE_TABLE_TAG, TargetProfileName)) {
+ Error = NERR_RplInternal;
+ goto cleanup;
+ }
+ CallJ( JetPrepareUpdate( pSession->SesId, pSession->ProfileTableId, JET_prepReplace));
+ Info.Flags = PROFILE_FLAGS_DISK_PRESENT_TRUE;
+ Error = ProfileSetField( pSession, PROFILE_Flags, &Info.Flags, sizeof(Info.Flags));
+ if ( Error != NO_ERROR) {
+ goto cleanup;
+ }
+ JetError = JetUpdate( pSession->SesId, pSession->ProfileTableId, NULL, 0, NULL);
+ if ( JetError < 0) {
+ RplDump( ++RG_Assert, ("JetError=%d", JetError));
+ Error = NERR_RplInternal;
+ goto cleanup;
+ }
+ DiskDelete = FALSE; // success, make sure we do not delete disk data
+
+cleanup:
+ if ( DiskDelete == TRUE) {
+ //
+ // Delete new tree. We do not need to delete the new record
+ // since we are going to rollback the transaction.
+ //
+ ProfileDiskClone( FALSE, NULL, TargetProfileName);
+ }
+ if ( Error == NO_ERROR) {
+ Call( JetCommitTransaction( pSession->SesId, JET_bitCommitFlush));
+ } else {
+ Call( JetRollback( pSession->SesId, JET_bitRollbackAll));
+ }
+ LeaveCriticalSection( &RG_ProtectDatabase);
+
+ //
+ // Restore source data, then release it.
+ //
+ Info.ProfileName = SaveProfileName;
+ Info.ProfileComment = SaveProfileComment;
+ ProfileGetInfoCleanup( 2, &Info);
+ return( Error);
+}
+
+
diff --git a/private/net/svcdlls/rpl/server/read.c b/private/net/svcdlls/rpl/server/read.c
new file mode 100644
index 000000000..b47a9af0f
--- /dev/null
+++ b/private/net/svcdlls/rpl/server/read.c
@@ -0,0 +1,409 @@
+/*++
+
+Copyright (c) 1987-1993 Microsoft Corporation
+
+Module Name:
+
+ _read.c
+
+Abstract:
+
+ Module reads next send packet from a VIRTUAL boot block.
+ THIS IS VERY COMPLICATED ALGORITHM, YOU MUST UNDERSTAND THE ALGORITHM
+ AND KNOW THE SIDE EFFECTS IF YOU DO ANY CHANGES.
+
+ Provides similar functionality to rmapread.c in LANMAN 2.1 code.
+
+Author:
+
+ Vladimir Z. Vulovic 27 - July - 1993
+
+Environment:
+
+ User mode
+
+Revision History :
+
+--*/
+
+#include "local.h"
+#include "read.h"
+
+
+VOID RplMakePatch(
+ IN PRPL_WORKER_DATA pWorkerData,
+ OUT PBYTE send_buffer,
+ IN DWORD read_len,
+ IN DWORD read_offset
+ )
+/*++
+
+Routine Description:
+ Makes NLS patch to send buffer.
+
+Arguments:
+ send_buffer - send buffer to be sent to DLL
+ read_len - length of buffer
+ read_offset - offset of buffer in bblock image
+
+Return Value:
+ None.
+
+--*/
+{
+ DWORD Length;
+ DWORD PatchOffset;
+ PBYTE Source;
+ PBYTE Target;
+ DWORD base_offset;
+
+ PatchOffset = pWorkerData->PatchOffset + pWorkerData->fblock_base_offset;
+
+ // if top 1 less that bottom 2 or bottom 1 greater that top 2
+ // => blocks do not overlap and there is nothing to patch here
+
+ if ( !(PatchOffset + DBCS_MESSAGE_BUFFER_SIZE < read_offset ||
+ PatchOffset > read_offset + read_len)) {
+
+ // blocks overlap, get copy address and length,
+ // read offset is MAX( bottom1, bottom2 )
+
+ if (read_offset < PatchOffset) {
+ Source = RG_DbcsMessageBuffer;
+ Target = send_buffer + (WORD)(PatchOffset - read_offset);
+ base_offset = PatchOffset;
+ } else {
+ Source = RG_DbcsMessageBuffer + (WORD)(read_offset - PatchOffset);
+ Target = send_buffer;
+ base_offset = read_offset;
+ }
+
+ // end offset is MIN( top1, top2 )
+ if (read_offset + read_len < PatchOffset + DBCS_MESSAGE_BUFFER_SIZE) {
+ Length = read_offset + read_len - base_offset;
+ } else {
+ Length = PatchOffset + DBCS_MESSAGE_BUFFER_SIZE - base_offset;
+ }
+
+ // copy data from source to destination
+ memcpy( Target, Source, (WORD)Length );
+ }
+
+}
+
+
+BOOL RplReadData(
+ IN PRPL_WORKER_DATA pWorkerData,
+ IN DWORD read_offset,
+ OUT PDWORD pBytesRead
+ )
+/*++
+
+Routine Description:
+
+ Returns pointer to send buffer and its length. The data is read from
+ the offset (input param) of virtual boot block. The boot block consists
+ of small wksta specific data buffer, virtual file block and unused
+ memory between them (if any). Virtual file block is a file table.
+ The names are in memory, but the data is in hard disk (or in disk cache).
+
+Arguments:
+
+ read_offset - offset of data that we need to read
+ pBuffer - address of location where pointer to send buffer is returned
+ pBytesRead - pointer to number of bytes read
+
+Return Value:
+ TRUE if success, else FALSE.
+
+--*/
+{
+ DWORD bytes_read = 0; // the length of send buffer
+ LONG sint; // signed integer for length comp
+ DWORD temp;
+ DWORD read_len;
+ DWORD file_len;
+ PBYTE wksta_buf;
+ DWORD wksta_buf_len;
+ DWORD LengthDataBuffer;
+ PBYTE pDataBuffer;
+ PFLIST_TBL flist_tbl;
+ DWORD flist_tbl_len;
+ DWORD cur_flist_i;
+ DWORD cur_offset;
+ DWORD cur_file_base_offset;
+ DWORD fblock_base_offset;
+ BOOL is_end_of_bblock;
+ BOOL LAN_err;
+
+// RplDump( RG_DebugLevel & RPL_DEBUG_FLOW,( "++RplReadData"));
+
+ //
+ // The following is set in RplWorkerThread2
+ //
+ LengthDataBuffer = pWorkerData->send_buf_len;
+
+ //
+ // The following are set in RplMakeWkstaBuf
+ //
+ wksta_buf = pWorkerData->wksta_buf;
+ wksta_buf_len = pWorkerData->wksta_buf_len;
+ fblock_base_offset = pWorkerData->fblock_base_offset;
+
+ //
+ // The following are set in RplOpenData
+ //
+ pDataBuffer = pWorkerData->pDataBuffer;
+
+ flist_tbl = pWorkerData->flist_tbl;
+ flist_tbl_len = pWorkerData->flist_tbl_len;
+
+ cur_flist_i = pWorkerData->cur_flist_i;
+ cur_offset = pWorkerData->cur_offset;
+ cur_file_base_offset = pWorkerData->cur_file_base_offset;
+ is_end_of_bblock = pWorkerData->is_end_of_bblock;
+
+
+ if ( fblock_base_offset > read_offset) {
+
+ // We have to copy at least some data from wksta buffer or/and from
+ // the empty space between "wksta_buf_len" (end of wksta buffer
+ // data) and "fblock_base_offset" (beginning of file list data).
+ // First copy the data from wksta buffer then from empty space.
+
+ if ( wksta_buf_len > read_offset) { // wksta buffer data
+
+ if ( wksta_buf_len >= read_offset + LengthDataBuffer) {
+ bytes_read = LengthDataBuffer; // all is here
+ } else {
+ // Some but not all of the leftover data is here.
+ bytes_read = wksta_buf_len - read_offset;
+ }
+
+ memcpy( pDataBuffer, &wksta_buf[read_offset], bytes_read);
+ }
+
+ if ( bytes_read < LengthDataBuffer) { // empty space data
+
+ if ( read_offset + LengthDataBuffer <= fblock_base_offset) {
+ temp = LengthDataBuffer - bytes_read; // rest is here
+ } else {
+ // Some but not all of the leftover data is here.
+ temp = fblock_base_offset - wksta_buf_len;
+ }
+
+ memset( &pDataBuffer[ bytes_read], 0, temp);
+ bytes_read += temp;
+ }
+ }
+
+ //
+ // Check if the right file is opened and the seek is OK. There may
+ // have been a LAN I/O error in which case we must read the data from
+ // a new position.
+ //
+ if ( read_offset == cur_offset || cur_offset == -1L) {
+ LAN_err = FALSE; // everything OK
+
+ } else {
+ LAN_err = TRUE; // error in LAN I/O, this is not a sequential read
+ //
+ // this flag is set if we were reading the last buffer,
+ // reset flag, otherwise the rest is not read with ReadFile()
+ //
+ is_end_of_bblock = FALSE;
+ }
+
+
+ if ( bytes_read < LengthDataBuffer && !is_end_of_bblock) {
+ //
+ // All the data has not been read yet. Read the rest from
+ // files mentioned in the file list table.
+ //
+ if ( LAN_err && pWorkerData->hFile != INVALID_HANDLE_VALUE) {
+ if ( !CloseHandle( pWorkerData->hFile)) {
+ RplDump( ++RG_Assert, ("Error=%d", GetLastError()));
+ }
+ pWorkerData->hFile = INVALID_HANDLE_VALUE;
+ }
+
+ // Check if this read is from a wrong position or there was
+ // fatal error in the first file read => start from the beginning.
+
+ if ( LAN_err && read_offset > fblock_base_offset) {
+ //
+ // error => reset bytes_read counter, reject the possible data
+ // resend an old packet, search the new file of read_offset
+ //
+ bytes_read = 0;
+ cur_file_base_offset = fblock_base_offset;
+
+ for ( cur_flist_i = 0; cur_flist_i < flist_tbl_len; cur_flist_i++) {
+ if (read_offset <
+ cur_file_base_offset + flist_tbl[cur_flist_i].FileData.file_len) {
+
+ break; // found, exit the search loop
+ }
+ cur_file_base_offset += flist_tbl[cur_flist_i].FileData.file_len;
+ }
+
+ //
+ // If wksta has sent a wrong read offset, terminate read ahead.
+ //
+ if ( cur_flist_i >= flist_tbl_len) {
+ is_end_of_bblock = TRUE;
+ goto end_of_block_exit;
+ }
+
+ //
+ // Open file for reading
+ //
+ pWorkerData->hFile = CreateFile( flist_tbl[cur_flist_i].path_name,
+ GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL, 0L);
+ if ( pWorkerData->hFile == INVALID_HANDLE_VALUE) {
+ RplDump( ++RG_Assert, ("Error=%d", GetLastError()));
+ pWorkerData->EventStrings[ 0] = pWorkerData->WkstaName;
+ pWorkerData->EventStrings[ 1] = flist_tbl[cur_flist_i].path_name;
+ pWorkerData->EventId = NELOG_RplWkstaFileOpen;
+ return( FALSE);
+ }
+
+ //
+ // seek the current position, we are probably not reading from
+ // the start of file
+ //
+ file_len = SetFilePointer( pWorkerData->hFile,
+ read_offset - cur_file_base_offset, NULL, FILE_BEGIN);
+ if ( file_len == INVALID_FILE_OFFSET) {
+ RplDump( ++RG_Assert, ("Error=%d", GetLastError()));
+ pWorkerData->EventStrings[ 0] = pWorkerData->WkstaName;
+ pWorkerData->EventStrings[ 1] = flist_tbl[cur_flist_i].path_name;
+ pWorkerData->EventId = NELOG_RplWkstaFileRead;
+ return( FALSE);
+ }
+
+ } else {
+ //
+ // Open the primary file, if it's not opened yet
+ //
+ if ( pWorkerData->hFile == INVALID_HANDLE_VALUE) {
+ if (read_offset <= fblock_base_offset) {
+ // open the first file
+ cur_flist_i = 0;
+ cur_file_base_offset = fblock_base_offset;
+ }
+ //
+ // Open file for reading
+ //
+ pWorkerData->hFile = CreateFile( flist_tbl[cur_flist_i].path_name,
+ GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL, 0L);
+ if ( pWorkerData->hFile == INVALID_HANDLE_VALUE) {
+ RplDump( ++RG_Assert, ("Error=%d", GetLastError()));
+ pWorkerData->EventStrings[ 0] = pWorkerData->WkstaName;
+ pWorkerData->EventStrings[ 1] = flist_tbl[cur_flist_i].path_name;
+ pWorkerData->EventId = NELOG_RplWkstaFileOpen;
+ return( FALSE);
+ }
+ }
+ }
+
+ //
+ // read files until send buffer is full
+ //
+ while( bytes_read < LengthDataBuffer) {
+ if ( !ReadFile( pWorkerData->hFile, &pDataBuffer[bytes_read],
+ LengthDataBuffer - bytes_read, &read_len, NULL)) {
+ RplDump( ++RG_Assert, ("Error=%d", GetLastError()));
+ pWorkerData->EventStrings[ 0] = pWorkerData->WkstaName;
+ pWorkerData->EventStrings[ 1] = flist_tbl[cur_flist_i].path_name;
+ pWorkerData->EventId = NELOG_RplWkstaFileRead;
+ return( FALSE);
+ }
+
+ bytes_read += read_len;
+
+ if (bytes_read == LengthDataBuffer) {
+ break; // we read all the data for now, exit the loop
+ }
+ //
+ // We have not read all the data we asked for. Therefore, we
+ // must have reached end of file. Therefore, close the current
+ // file handle and prepare to read the next file.
+ //
+ if ( !CloseHandle( pWorkerData->hFile)) {
+ RplDump( ++RG_Assert, ("Error=%d", GetLastError()));
+ }
+
+ pWorkerData->hFile = INVALID_HANDLE_VALUE;
+ cur_file_base_offset += flist_tbl[cur_flist_i].FileData.file_len;
+ cur_flist_i++;
+
+ // reset the last bytes in the file
+
+ temp = cur_file_base_offset - read_offset;
+
+ if ( (sint = (LONG)(temp - bytes_read)) > 0) {
+ memset( pDataBuffer + bytes_read, 0, sint );
+ }
+ bytes_read = temp;
+
+ if ( cur_flist_i >= flist_tbl_len) {
+ is_end_of_bblock = TRUE;
+ break; // end of file list
+ }
+
+ //
+ // Open file for reading
+ //
+ pWorkerData->hFile = CreateFile( flist_tbl[cur_flist_i].path_name,
+ GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL, 0L);
+ if ( pWorkerData->hFile == INVALID_HANDLE_VALUE) {
+ RplDump( ++RG_Assert, ("Error=%d", GetLastError()));
+ RplDump( ++RG_Assert, ("Error=%d", GetLastError()));
+ pWorkerData->EventStrings[ 0] = pWorkerData->WkstaName;
+ pWorkerData->EventStrings[ 1] = flist_tbl[cur_flist_i].path_name;
+ pWorkerData->EventId = NELOG_RplWkstaFileOpen;
+ return( FALSE);
+ }
+ }
+ }
+
+ cur_offset = read_offset + LengthDataBuffer;
+
+ //
+ // only for end of boot block
+ //
+
+end_of_block_exit:
+
+ //
+ // update the changed values
+ //
+
+ pWorkerData->cur_offset = cur_offset;
+ pWorkerData->cur_file_base_offset = cur_file_base_offset;
+ pWorkerData->cur_flist_i = cur_flist_i;
+ pWorkerData->is_end_of_bblock = is_end_of_bblock;
+
+ //
+ // Make NLS (DBCS ?) patch to RPLBOOT.SYS.
+ //
+
+ if ( pWorkerData->MakePatch == TRUE) {
+ RplMakePatch( pWorkerData, pDataBuffer, bytes_read, read_offset);
+ }
+
+ //
+ // return the buffer address and the length
+ //
+
+ *pBytesRead = bytes_read;
+// RplDump( RG_DebugLevel & RPL_DEBUG_FLOW,( "--RplReadData"));
+ return( TRUE);
+
+}
+
+
diff --git a/private/net/svcdlls/rpl/server/read.h b/private/net/svcdlls/rpl/server/read.h
new file mode 100644
index 000000000..ee571320d
--- /dev/null
+++ b/private/net/svcdlls/rpl/server/read.h
@@ -0,0 +1,29 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ read.h
+
+Abstract:
+
+ Exports from read.c
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 19 - November - 1993
+
+Environment:
+
+ User mode
+
+Revision History :
+
+--*/
+
+BOOL RplReadData(
+ IN PRPL_WORKER_DATA pWorkerData,
+ IN DWORD read_offset,
+ OUT PDWORD bytes_read_ptr
+ );
diff --git a/private/net/svcdlls/rpl/server/report.c b/private/net/svcdlls/rpl/server/report.c
new file mode 100644
index 000000000..7063e12ca
--- /dev/null
+++ b/private/net/svcdlls/rpl/server/report.c
@@ -0,0 +1,199 @@
+/*++
+
+Copyright (c) 1987-1993 Microsoft Corporation
+
+Module Name:
+
+ report.c
+
+Abstract:
+
+ Prints messages to the error log of Lan Manager.
+
+ Provides similar functionality to rplermsg.c in LANMAN 2.1 code.
+
+Author:
+
+ Vladimir Z. Vulovic 27 - July - 1993
+
+Environment:
+
+ User mode
+
+Revision History :
+
+--*/
+
+#include "local.h"
+#include "report.h"
+
+VOID RplAlertRaise( IN DWORD ErrorCode);
+
+
+VOID RplEnd( IN DWORD ErrorCode)
+/*++
+
+Routine Description:
+
+ This function is called under very unusual circumstances!
+ It provides a convenient way for service to log an event, send
+ an alert, then exits.
+
+Arguments:
+ ErrorCode - termination error code
+
+Return Value:
+ None.
+
+--*/
+{
+ RplReportEvent( ErrorCode, NULL, 0, NULL );
+ RplAlertRaise( (RG_ServiceStatus.dwCurrentState == SERVICE_INSTALL_PENDING)
+ ? NERR_RplBootStartFailed : NERR_RplBootServiceTerm);
+ (VOID)RplServiceAttemptStop(); // signal service to stop
+}
+
+
+
+VOID RplAlertRaise( IN DWORD ErrorCode)
+/*++
+
+Routine Description:
+
+ Sends an ADMIN alert. The input is a LanManager error message.
+
+ This is a combination of the original Send_alert() routine &&
+ RaiseAlert() routine from logonsrv\server\error.c
+
+Arguments:
+ ErrorCode - the alert to be raised, text in alertmsg.h
+
+Return Value:
+ None.
+
+Notes:
+ Failing to post an alert is considered unimportant. This is why this
+ function is VOID.
+
+--*/
+{
+ char message[ ALERTSZ + sizeof(STD_ALERT) + sizeof(ADMIN_OTHER_INFO)];
+ PSTD_ALERT alert = (PSTD_ALERT)message;
+ PADMIN_OTHER_INFO other = (PADMIN_OTHER_INFO)ALERT_OTHER_INFO( alert);
+ LARGE_INTEGER time;
+ HANDLE fileHandle;
+ DWORD inBytes;
+ DWORD outBytes;
+
+ NtQuerySystemTime( &time);
+ RtlTimeToSecondsSince1970( &time, &alert->alrt_timestamp );
+
+ // Original code used alrt_servicename == SERVICE_SERVER
+ wcscpy( alert->alrt_servicename, SERVICE_RIPL);
+ wcscpy( alert->alrt_eventname, ALERT_ADMIN_EVENT );
+
+ other->alrtad_errcode = ErrorCode;
+ other->alrtad_numstrings = 0;
+
+
+ // NetAlertRaise() is gone, must use mailslots instead. So, first
+ // open the Alerter mailslot to write to it.
+
+ fileHandle = CreateFile(
+ ALERTER_MAILSLOT,
+ GENERIC_WRITE,
+ FILE_SHARE_WRITE | FILE_SHARE_READ,
+ (LPSECURITY_ATTRIBUTES) NULL,
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL);
+ if ( fileHandle == INVALID_HANDLE_VALUE) {
+ RplDump( RG_DebugLevel & RPL_DEBUG_MISC,(
+ "AlertRaise: Error opening alerter mailslot, error=%d",
+ GetLastError()));
+ return;
+ }
+
+ inBytes = min( sizeof( message),
+ (DWORD)( (PCHAR)ALERT_VAR_DATA(other) - (PCHAR)message));
+
+ // Write alert notification to mailslot to be read by Alerter service
+ if ( !WriteFile(
+ fileHandle,
+ message,
+ inBytes,
+ &outBytes,
+ NULL) || inBytes != outBytes) {
+
+ RplDump( RG_DebugLevel & RPL_DEBUG_MISC,(
+ "AlertRaise: Error writing to alerter mailslot %d",
+ GetLastError()));
+
+ } else if ( ! CloseHandle( fileHandle)) {
+
+ RplDump( RG_DebugLevel & RPL_DEBUG_MISC,(
+ "AlertRaise: Error closing alerter mailslot %d",
+ GetLastError()
+ ));
+ }
+ (VOID)CloseHandle( fileHandle);
+}
+
+
+
+VOID RplReportEventEx(
+ IN DWORD MessageId,
+ IN LPWSTR * aStrings
+ )
+/*++
+
+Routine Description:
+
+ Writes an event in the event log.
+ A related function lives is RplReportEvent() in lib\report.c.
+ These two functions should be united.
+
+Arguments:
+
+ MessageId - Message ID
+ aStrings - a NULL terminated array of strings
+
+Return Value:
+
+ None.
+
+--*/
+{
+ WORD cStrings;
+ HANDLE logHandle;
+
+ logHandle = RegisterEventSource( NULL, RPL_EVENTLOG_NAME);
+
+ // If the event log cannot be opened, just return.
+
+ if ( logHandle == NULL) {
+ RplDump( ++RG_Assert, ("Error=%d", GetLastError()));
+ return;
+ }
+
+ for ( cStrings = 0; aStrings[ cStrings] != NULL; cStrings++) {
+ NOTHING;
+ }
+
+ if ( !ReportEvent(
+ logHandle,
+ EVENTLOG_ERROR_TYPE,
+ 0, // event category
+ MessageId, // event id
+ NULL, // user SID. We're local system - uninteresting
+ cStrings, // number of strings
+ 0, // raw data size
+ aStrings, // string array
+ NULL // raw data buffer
+ )) {
+ RplDump( ++RG_Assert, ( "Error=%d", GetLastError()));
+ }
+
+ DeregisterEventSource( logHandle);
+}
+
diff --git a/private/net/svcdlls/rpl/server/report.h b/private/net/svcdlls/rpl/server/report.h
new file mode 100644
index 000000000..be3b621e1
--- /dev/null
+++ b/private/net/svcdlls/rpl/server/report.h
@@ -0,0 +1,34 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ report.h
+
+Abstract:
+
+ Exports from report.c
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 19 - November - 1993
+
+Environment:
+
+ User mode
+
+Revision History :
+
+--*/
+
+//
+// VOID RplReportEvent( - declared in ..\inc\rpl.h
+//
+
+VOID RplEnd( IN DWORD ErrorCode);
+VOID RplReportEventEx(
+ IN DWORD MessageId,
+ IN LPWSTR * aStrings
+ );
+
diff --git a/private/net/svcdlls/rpl/server/request.c b/private/net/svcdlls/rpl/server/request.c
new file mode 100644
index 000000000..4a409dee2
--- /dev/null
+++ b/private/net/svcdlls/rpl/server/request.c
@@ -0,0 +1,697 @@
+/*++
+
+Copyright (c) 1987-1993 Microsoft Corporation
+
+Module Name:
+
+ request.c
+
+Abstract:
+
+ Request thread code.
+
+ Provides similar functionality to rplreq.c in LANMAN 2.1 code.
+
+Author:
+
+ Vladimir Z. Vulovic 27 - July - 1993
+
+Environment:
+
+ User mode
+
+Revision History :
+
+--*/
+
+#include "local.h"
+#include "worker.h"
+#include "database.h"
+#include "report.h"
+#include "request.h"
+
+typedef struct _RPL_SFR_PARAMS { // send file request thread parameters
+ POPEN_INFO pOpenInfo; // open info data of current DLL
+ PPRCB HeadRcbList; // list of RCBs used by worker threads
+ PCRITICAL_SECTION ProtectRcbList;
+} SFR_PARAMS, *PRPL_SFR_PARAMS;
+
+
+extern VOID RplInsertRcb(
+ IN OUT PPRCB HeadList,
+ IN OUT PRCB pRcb
+ );
+
+
+
+VOID SfrThread( PRPL_SFR_PARAMS pSfrParams)
+/*++
+
+Routine Description:
+
+ Procedure receives Send File Requests, searches the valid RCB
+ from list and starts the worker thread of RCB by clearing a
+ semaphore.
+
+ This thread tries to run at highest settable priority because
+ claims must always be ready to receive acknowledges (if ASCLAN
+ receive is not active, then SF Requests may be lost and there
+ is 4 seconds timeout in the workstation).
+
+Arguments:
+ pSfrParams ptr to SFR_PARAMS
+
+Return Value:
+ None.
+
+--*/
+{
+ DWORD status;
+
+ if ( !SetThreadPriority( GetCurrentThread(), THREAD_PRIORITY_HIGHEST)) {
+ status = GetLastError();
+ RplDump( ++RG_Assert, ("status=%d", status));
+ RplEnd( NELOG_RplSystem);
+ return;
+ }
+
+ //
+ // The call to SendFileRequest never returns. Not only is this thread
+ // capt captive, but the callee needlessly creates one more thread.
+ // Better interface would allow to post a receive for our two SAP-s,
+ // then from a single place read both FIND frame data (FIND_SAP)
+ // and SFR data (SFR_SAP). This could be all done from a single
+ // thread, resembling current RequestThread().
+ //
+
+ if ( RplDlcSfr( // was RPL1_Get_SF_Request()
+ pSfrParams->pOpenInfo,
+ pSfrParams->HeadRcbList,
+ pSfrParams->ProtectRcbList
+ )) {
+ return; // success
+ }
+ if ( RG_ServiceStatus.dwCurrentState == SERVICE_STOP_PENDING) {
+ return; // somebody else initiated service shutdown
+ }
+ RplDump( ++RG_Assert, ("CurrentState=0x%x", RG_ServiceStatus.dwCurrentState));
+ RplEnd( NELOG_RplSystem);
+}
+
+
+
+PRCB RplPopRcb( PPRCB HeadList)
+{
+ PRCB pRcb = *HeadList;
+ DebugCheckList( HeadList, NULL, 0);
+ if ( pRcb != NULL) {
+ *HeadList = pRcb->next_rcb;
+ }
+ RplDump( RG_DebugLevel & RPL_DEBUG_WORKER,(
+ "PopRcb: pRcb=0x%x, list=0x%x", pRcb, HeadList));
+ DebugCheckList( HeadList, pRcb, RPL_MUST_NOT_FIND);
+ return( pRcb);
+}
+
+
+PRCB GetRcbFromFreeList(
+ IN OUT PPRCB pFreeList,
+ IN DWORD lan_rcb_size
+ )
+/*++
+
+Routine Description:
+
+ Finds the first RCB from the list of free RCBs. If list is empty,
+ creates a new RCB. Before returning RCB it (re)initializes all the
+ fields in the RCB.
+
+Arguments:
+
+ pFreeList, - pointer to list of free RCB-s
+ lan_rcb_size - size of lan specific rcb
+
+Return Value:
+
+ Pointer to RCB, or NULL if we cannot get an RCB.
+
+--*/
+{
+ PRCB pRcb;
+
+ EnterCriticalSection( &RG_ProtectRcbList);
+
+ pRcb = RplPopRcb( pFreeList);
+ if ( pRcb != NULL) {
+ goto exit_GetRcbFromFreeList; // success
+ }
+
+ pRcb = RplMemAlloc( RG_MemoryHandle, sizeof(RCB));
+ if ( pRcb == NULL) {
+ goto exit_GetRcbFromFreeList;
+ }
+
+ pRcb->lan_rcb_ptr = RplMemAlloc( RG_MemoryHandle, lan_rcb_size);
+ if ( pRcb->lan_rcb_ptr == NULL) {
+ RplMemFree( RG_MemoryHandle, pRcb);
+ pRcb = NULL;
+ goto exit_GetRcbFromFreeList; // failure
+ }
+
+ // SF_wakeup was using automatic reset and RPL worker used not to
+ // reset SF_wakup before each FDR send. This did not work properly
+ // due to multiple SFR-s for a single frame. Multiple SFR-s would
+ // cause SF_wakeup to be in a signalled state immediately after
+ // worker sent an FDR, thus worker would wakeup immediately and find
+ // a wrong value for sfr_seq_number.
+ // To fix this RPL worker must make sure SF_wakeup is blocked
+ // before each FDR send. Since worker must now reset SF_wakeup
+ // anyway, SF_wakeup is now using manual reset.
+ //
+ pRcb->SF_wakeup = CreateEvent(
+ NULL, // no security attributes
+ TRUE, // use manual reset
+ TRUE, // initial value is signalled
+ NULL // event does not have a name
+ );
+ if ( pRcb->SF_wakeup == NULL) {
+ RplDump( ++RG_Assert,("CreateEvent => error=%d", GetLastError()));
+ RplMemFree( RG_MemoryHandle, pRcb->lan_rcb_ptr);
+ RplMemFree( RG_MemoryHandle, pRcb);
+ pRcb = NULL;
+ goto exit_GetRcbFromFreeList; // failure
+ }
+
+exit_GetRcbFromFreeList:
+
+ pRcb->AdapterName[ NODE_ADDRESS_LENGTH] = 0;
+ *pRcb->volume_id = 0;
+ pRcb->sfr_seq_number = 0;
+ pRcb->fdr_seq_number = 0;
+ pRcb->ReceivedSfr = FALSE;
+
+ *(PBYTE)&pRcb->flags = 0; // reset the error flags
+ pRcb->next_rcb = NULL;
+ *(PBYTE)&pRcb->flags = 0;
+
+ pRcb->Pending = TRUE;
+ pRcb->SFR = FALSE;
+
+ LeaveCriticalSection( &RG_ProtectRcbList);
+ RplDump( RG_DebugLevel & RPL_DEBUG_REQUEST,(
+ "GetRcbFromFreeList: pRcb=0x%x", pRcb));
+ return( pRcb);
+}
+
+
+PRPL_WORKER_PARAMS GetWorkerParams(
+ IN PRPL_REQUEST_PARAMS pRequestParams,
+ OUT PBOOL pShutdown
+ )
+/*++
+ Loops until number of worker threads falls below the maximum value
+ allowed, or until it gets signalled to die.
+ If it is signalled to die, waits for all worker children and returns.
+ If it is not signalled to die, waits only for children that are
+ already exiting, and returns pointer to one of available RPL_WORKER_PARAMS
+ structures.
+--*/
+{
+ HANDLE eventArray[ 2];
+ PRPL_WORKER_PARAMS pWorkerParams;
+ PRPL_WORKER_PARAMS pFoundWorkerParams;
+ DWORD status;
+ DWORD WorkerCount;
+
+ eventArray[ 0] = RG_EventWorkerCount;
+ eventArray[ 1] = RG_TerminateNowEvent;
+
+ *pShutdown = FALSE;
+
+ for ( ; ;) {
+ EnterCriticalSection( &RG_ProtectWorkerCount);
+ WorkerCount = RG_WorkerCount;
+ if ( WorkerCount < RG_MaxWorkerCount) {
+ LeaveCriticalSection( &RG_ProtectWorkerCount);
+ break;
+ }
+ //
+ // We reset event from within critical section to make sure
+ // event is never reset for wrong reasons.
+ //
+ if ( !ResetEvent( RG_EventWorkerCount)) {
+ RplDump( ++RG_Assert, ("Error=%d", GetLastError()));
+ }
+ LeaveCriticalSection( &RG_ProtectWorkerCount);
+ status = WaitForMultipleObjects(
+ 2, // count
+ eventArray, // handles
+ FALSE, // wait for at least one
+ INFINITE // wait for ever
+ );
+ if ( status == 1) {
+ *pShutdown = TRUE; // terminate now event
+ break; // to wait for children below
+ }
+ RPL_ASSERT( status == 0); // WorkerCount changed
+ continue;
+ }
+
+ //
+ // If we are shutting down, wait on all valid thread handles.
+ // Else, wait only on thread handles for children that are exiting.
+ //
+ for ( pFoundWorkerParams = NULL,
+ pWorkerParams = pRequestParams->pWorkerParams;
+ pWorkerParams != NULL;
+ pWorkerParams = pWorkerParams->pWorkerParams) {
+ if ( pWorkerParams->Exiting == TRUE ||
+ (*pShutdown == TRUE && pWorkerParams->ThreadHandle != NULL)) {
+ status = WaitForSingleObject( pWorkerParams->ThreadHandle, INFINITE);
+ if ( status != 0) {
+ RplDump( ++RG_Assert, ( "pWorkerParams=0x%x, status=0x%x",
+ pWorkerParams, status==WAIT_FAILED ? GetLastError() : status));
+ }
+ if ( !CloseHandle( pWorkerParams->ThreadHandle)) {
+ RplDump( ++RG_Assert, ( "pWorkerParams=0x%x, error=%d",
+ pWorkerParams, GetLastError()));
+ }
+ pWorkerParams->ThreadHandle = NULL; // OK to reuse
+ pWorkerParams->Exiting = FALSE;
+ }
+ if ( pWorkerParams->ThreadHandle == NULL) {
+ pFoundWorkerParams = pWorkerParams; // reuse this structure
+ }
+ }
+ if ( *pShutdown == TRUE) {
+ return( NULL);
+ }
+ if ( pFoundWorkerParams == NULL) {
+ //
+ // Allocate a brand new RPL_WORKER_PARAMS structure.
+ //
+ pFoundWorkerParams = RplMemAlloc( RG_MemoryHandle, sizeof(RPL_WORKER_PARAMS));
+ if ( pFoundWorkerParams == NULL) {
+ RplDump( ++RG_Assert, ( ""));
+ return( NULL);
+ }
+ pFoundWorkerParams->pWorkerParams = pRequestParams->pWorkerParams;
+ pRequestParams->pWorkerParams = pFoundWorkerParams;
+ pFoundWorkerParams->ThreadHandle = NULL;
+ pFoundWorkerParams->ThreadId = 0;
+ pFoundWorkerParams->pRequestParams = pRequestParams;
+ pFoundWorkerParams->pRcb = NULL;
+ pFoundWorkerParams->Exiting = FALSE;
+ }
+ return( pFoundWorkerParams);
+}
+
+
+
+VOID RequestThread( PRPL_REQUEST_PARAMS pRequestParams)
+/*++
+
+Routine Description:
+
+ Module is the RPL request thread. It must be re-entrant, because each
+ DLL has own instance of this module. Module loads rpl DLLs and gets
+ their entry point addresses. It initializes DLLs and dynamic memory
+ and starts RpldFind() loop.
+
+Arguments:
+
+ pRequestParams - string containing names of RPL1 & RPL2 DLLs
+
+Return Value:
+
+ None.
+
+Notes:
+
+ This routine must have a single exit point because it is essential
+ to decrement the number of request threads and wake up the main
+ thread on our way out.
+
+--*/
+{
+ PRPL_SFR_PARAMS pSfrParams;
+ DWORD ThreadId;
+ DWORD status;
+ POPEN_INFO pOpenInfo; // generic rpl DLL info
+ PRCB pRcb; // current Resource Control Block
+ PRPL_WORKER_PARAMS pWorkerParams;
+ BOOL Shutdown;
+ HANDLE ThreadHandle;
+ //
+ // TRUE if we inserted RCB in pending state on a worker queue.
+ // Used only when "pRcb" is not NULL i.e. while it still makes
+ // sense keeping pointer to that RCB.
+ //
+ BOOL RcbOnWorkerList;
+
+ RplDump( RG_DebugLevel & RPL_DEBUG_FLOW,( "++RequestThread(0x%x)", pRequestParams));
+
+ ThreadHandle = NULL;
+ pOpenInfo = pRequestParams->pOpenInfo;
+
+ //
+ // Build parameter block for SfrThread
+ //
+ pSfrParams = RplMemAlloc( RG_MemoryHandle, sizeof( SFR_PARAMS));
+ if( pSfrParams == NULL) {
+ status = GetLastError();
+ RplDump( ++RG_Assert, ("status=%d", status));
+ RplEnd( ERROR_NOT_ENOUGH_MEMORY);
+ goto exit_RequestThread;
+ }
+ pSfrParams->HeadRcbList = &pRequestParams->BusyRcbList;
+ pSfrParams->ProtectRcbList = &RG_ProtectRcbList;
+ pSfrParams->pOpenInfo = pOpenInfo;
+
+ //
+ // Create Get_SF_Request thread, it receives Send File Requests
+ // from workstations, searches rcb from list and clears its semaphore,
+ // that releases the worker thread.
+ //
+ ThreadHandle = CreateThread( NULL, 0,
+ (LPTHREAD_START_ROUTINE)SfrThread, pSfrParams, 0, &ThreadId);
+ if ( ThreadHandle == NULL) {
+ RplDump( ++RG_Assert, ("Error=%d", GetLastError()));
+ RplEnd( ERROR_NOT_ENOUGH_MEMORY);
+ goto exit_RequestThread;
+ }
+
+ //
+ // Read RPL request packets forever. Never create more than the
+ // maximum allowed number of worker threads.
+ //
+ for ( pRcb = NULL, pWorkerParams = NULL; NOTHING; NOTHING) {
+ //
+ // If pRcb is NULL we need to get an RCB from the free list.
+ // If pRcb is not NULL then we can still use it but we must
+ // dequeue it from worker queue if we placed it there.
+ //
+ if ( pRcb == NULL) {
+ for ( ; ;) {
+ pRcb = GetRcbFromFreeList(
+ &pRequestParams->FreeRcbList,
+ pOpenInfo->lan_rcb_size
+ );
+ if ( pRcb != NULL) {
+ break;
+ }
+ Sleep( 1000L); // wait for things to recover
+ }
+ } else if ( RcbOnWorkerList == TRUE) {
+ EnterCriticalSection( &RG_ProtectRcbList);
+ RplRemoveRcb( &pRequestParams->BusyRcbList, pRcb);
+ LeaveCriticalSection( &RG_ProtectRcbList);
+ }
+ RcbOnWorkerList = FALSE;
+
+ //
+ // Get the next RPL request. In the current design here we can
+ // block for ever if there are no FIND frames arriving. The only
+ // way then to let this thread out is for the main thread to call
+ // RPL_Term(). This would then cause RplDlcFind() to
+ // return with error, and we would exit main loop.
+ // A better interface would provide asynchronous calls, just the
+ // way DLC does. Then, ideally we would wait here for multiple
+ // events such as:
+ // - FIND frame arrival event
+ // - TerminateNowEvent
+ //
+
+ if ( !RplDlcFind( pOpenInfo, pRcb)) {
+ if ( RG_ServiceStatus.dwCurrentState == SERVICE_STOP_PENDING) {
+ goto exit_RequestThread;
+ }
+ RplDump( ++RG_Assert, ("CurrentState=0x%x", RG_ServiceStatus.dwCurrentState));
+ continue;
+ }
+
+ RplDump( RG_DebugLevel & RPL_DEBUG_REQUEST,(
+ "Request(0x%x): RplDlcFind => pRcb=0x%x, AdapterName=%ws",
+ pRequestParams, pRcb, pRcb->AdapterName));
+
+ //
+ // Send buffers must be paragraph aligned because of Jump address
+ // patching to the rplboot.sys code (dword fits always to one
+ // boot block)
+ //
+ pRcb->max_frame &= 0xFFF0;
+
+ //
+ // "max_frame" is sent to us by the client as the max value of data
+ // that the client can handle. We can go smaller than that but never
+ // larger.
+ //
+ if (pRcb->max_frame > MAX_FRAME_SIZE) {
+ pRcb->max_frame = MAX_FRAME_SIZE;
+ }
+
+ //
+ // Service this client if it is not already serviced by some of our
+ // children, and we have enough threads and we can find a matching
+ // wksta record.
+ //
+
+ EnterCriticalSection( &RG_ProtectRcbList);
+
+ if ( RplFindRcb( &pRequestParams->BusyRcbList, pRcb->NodeAddress) != NULL) {
+ //
+ // Client is already serviced by some of our children.
+ //
+ RplDump( RG_DebugLevel & RPL_DEBUG_REQUEST,(
+ "Request(0x%x): pRcb=0x%x, AdapterName=%ws client is already serviced",
+ pRequestParams, pRcb, pRcb->AdapterName));
+ LeaveCriticalSection( &RG_ProtectRcbList);
+ continue;
+ }
+
+ RcbOnWorkerList = TRUE; // to get it back in case of errors
+ pRcb->Pending = TRUE; // so SFR thread does not get confused
+ RplInsertRcb( &pRequestParams->BusyRcbList, pRcb);
+ LeaveCriticalSection( &RG_ProtectRcbList);
+
+ if ( !RplRequestHaveWksta( pRcb->AdapterName)) {
+ continue; // no wksta record for this AdapterName
+ }
+
+ if ( pWorkerParams == NULL) {
+ pWorkerParams = GetWorkerParams( pRequestParams, &Shutdown);
+ if ( Shutdown == TRUE) {
+ goto exit_RequestThread;
+ } else if ( pWorkerParams == NULL) {
+ continue;
+ }
+ }
+
+ pWorkerParams->pRcb = pRcb;
+ pWorkerParams->ThreadHandle = CreateThread( NULL, 0,
+ (LPTHREAD_START_ROUTINE)RplWorkerThread, pWorkerParams, 0,
+ &pWorkerParams->ThreadId);
+ if ( pWorkerParams->ThreadHandle == NULL) {
+ RplDump( ++RG_Assert,( "Error=%d", GetLastError()));
+ RplReportEvent( ERROR_NOT_ENOUGH_MEMORY, NULL, 0, NULL); // keep going
+ continue;
+ }
+ pRcb = NULL; // need to get a new RCB
+ pWorkerParams = NULL; // need to get a new WORKER_PARAMS
+ }
+
+exit_RequestThread:
+
+ if ( RcbOnWorkerList == TRUE) {
+ EnterCriticalSection( &RG_ProtectRcbList);
+ RplRemoveRcb( &pRequestParams->BusyRcbList, pRcb);
+ RplInsertRcb( &pRequestParams->FreeRcbList, pRcb);
+ LeaveCriticalSection( &RG_ProtectRcbList);
+ }
+
+ RplDump( RG_DebugLevel & RPL_DEBUG_REQUEST,(
+ "RequestThread(0x%x): on its way to exit", pRequestParams));
+
+ //
+ // Wait for SFR thread to exit then close its thread handle.
+ //
+ status = WaitForSingleObject( ThreadHandle, INFINITE);
+ if ( status != 0) {
+ RplDump( ++RG_Assert, ( "Params=0x%x Handle=0x%x Id=0x%x status=0x%x",
+ pSfrParams, ThreadHandle, ThreadId, status));
+ }
+ if ( !CloseHandle( ThreadHandle)) {
+ RplDump( ++RG_Assert, ( "Params=0x%x Handle=0x%x Id=0x%x error=%d",
+ pSfrParams, ThreadHandle, ThreadId, GetLastError()));
+ }
+ pRequestParams->Exiting = TRUE;
+ RplDump( RG_DebugLevel & RPL_DEBUG_FLOW,( "--RequestThread(0x%x)", pRequestParams));
+}
+
+
+BOOL AddToTermList( IN POPEN_INFO pOpenInfo)
+/*++
+
+Routine Description:
+
+Arguments:
+ pOpenInfo - info ptr for dll
+
+Return Value:
+ TRUE if success, FALSE otherwise.
+
+--*/
+{
+ PTERM_LIST pTermList;
+
+ pTermList = RplMemAlloc( RG_MemoryHandle, sizeof(TERM_LIST));
+ if ( pTermList == NULL) {
+ RG_Error = GetLastError();
+ return( FALSE);
+ }
+ pTermList->pOpenInfo = pOpenInfo;
+
+ EnterCriticalSection( &RG_ProtectTerminationList);
+ pTermList->next = RG_TerminationListBase;
+ RG_TerminationListBase = pTermList;
+
+ LeaveCriticalSection( &RG_ProtectTerminationList);
+ return( TRUE);
+}
+
+
+BOOL RplStartAdapters( VOID)
+/*++
+
+Routine Description:
+ Attempt to start all adapters.
+
+Return Value:
+ TRUE if at least one of the adapters have been started.
+ FALSE otherwise
+
+--*/
+{
+ POPEN_INFO pOpenInfo; // generic rpl DLL info
+ PRPL_REQUEST_PARAMS pRequestParams;
+ DWORD startedAdapters;
+ DWORD AdapterNumber;
+
+ RplDump( RG_DebugLevel & RPL_DEBUG_FLOW,( "++RplStartAdapters"));
+
+ pOpenInfo = NULL; // must be reinitialized every time
+ pRequestParams = NULL; // must be reinitialized every time
+
+ for ( AdapterNumber = 0, startedAdapters = 0;
+ AdapterNumber < MAX_ADAPTERS; AdapterNumber++) {
+
+ if ( pOpenInfo == NULL) {
+ pOpenInfo = RplMemAlloc( RG_MemoryHandle, sizeof(OPEN_INFO));
+ if ( pOpenInfo == NULL) {
+ RG_Error = GetLastError();
+ RPL_ASSERT( FALSE);
+ continue;
+ }
+ memset( pOpenInfo, 0, sizeof( *pOpenInfo));
+ }
+ if ( pOpenInfo->adapt_info_ptr == NULL) {
+ pOpenInfo->adapt_info_size = MAX_ADAPTER_INFO_SIZE;
+ pOpenInfo->adapt_info_ptr = RplMemAlloc( RG_MemoryHandle, MAX_ADAPTER_INFO_SIZE);
+ if ( pOpenInfo->adapt_info_ptr == NULL) {
+ RG_Error = GetLastError();
+ RPL_ASSERT( FALSE);
+ continue;
+ }
+ memset( pOpenInfo->adapt_info_ptr, 0, MAX_ADAPTER_INFO_SIZE);
+ }
+
+#ifndef RPL_NO_SERVICE
+ if ( !SetServiceStatus( RG_ServiceStatusHandle, &RG_ServiceStatus)) {
+ RplDump( ++RG_Assert, ( "Error = ", GetLastError()));
+ NOTHING; // just ignore this error
+ }
+#endif
+
+ //
+ // We try to start all adapters.
+ //
+ if ( !RplDlcInit( (POPEN_INFO)pOpenInfo, AdapterNumber)) {
+ continue; // try all adapters
+ }
+
+ //
+ // Save termination addresses of dll-s for usage by RplTerminateDlls().
+ //
+ if ( !AddToTermList( pOpenInfo)) {
+ break;
+ }
+
+ //
+ // Allocate & initialize request thread parameters.
+ //
+ if ( pRequestParams == NULL) {
+ pRequestParams = RplMemAlloc( RG_MemoryHandle, sizeof(RPL_REQUEST_PARAMS));
+ if ( pRequestParams == NULL) {
+ RG_Error = GetLastError();
+ break;
+ }
+ memset( pRequestParams, 0, sizeof( *pRequestParams));
+ pRequestParams->pOpenInfo = pOpenInfo;
+ pRequestParams->pRequestParams = RG_pRequestParams;
+ RG_pRequestParams = pRequestParams;
+
+ }
+ pRequestParams->ThreadHandle = CreateThread( NULL, 0,
+ (LPTHREAD_START_ROUTINE)RequestThread, pRequestParams, 0,
+ &pRequestParams->ThreadId);
+ if ( pRequestParams->ThreadHandle == NULL) {
+ RG_Error = GetLastError();
+ RPL_ASSERT( FALSE);
+ continue;
+ }
+ startedAdapters++;
+ pOpenInfo = NULL; // must be reinitialized every time
+ pRequestParams = NULL; // need to get new REQUEST_PARAMS
+ }
+
+ if ( pOpenInfo) {
+ if ( pOpenInfo->adapt_info_ptr) {
+ RplMemFree( RG_MemoryHandle, pOpenInfo->adapt_info_ptr);
+ }
+ RplMemFree( RG_MemoryHandle, pOpenInfo);
+ }
+
+ if ( startedAdapters == 0) {
+ RG_Error = RG_Error == NO_ERROR ? NERR_RplNoAdaptersStarted : RG_Error;
+ return( FALSE);
+ }
+
+ RplDump( RG_DebugLevel & RPL_DEBUG_FLOW,( "--RplStartAdapters"));
+ return( TRUE);
+}
+
+
+VOID RplCloseAdapters( VOID)
+/*++
+
+Routine Description:
+
+ Terminates all the DLL-s in RPL termination list.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PTERM_LIST pTerm;
+
+ for ( pTerm = RG_TerminationListBase; pTerm != NULL; pTerm = pTerm->next) {
+ RplDlcTerm( pTerm->pOpenInfo);
+ }
+}
+
diff --git a/private/net/svcdlls/rpl/server/request.h b/private/net/svcdlls/rpl/server/request.h
new file mode 100644
index 000000000..68e847db8
--- /dev/null
+++ b/private/net/svcdlls/rpl/server/request.h
@@ -0,0 +1,26 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ request.h
+
+Abstract:
+
+ Exports from request.c
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 19 - November - 1993
+
+Environment:
+
+ User mode
+
+Revision History :
+
+--*/
+
+BOOL RplStartAdapters( VOID);
+VOID RplCloseAdapters( VOID);
diff --git a/private/net/svcdlls/rpl/server/resume.c b/private/net/svcdlls/rpl/server/resume.c
new file mode 100644
index 000000000..e2175a179
--- /dev/null
+++ b/private/net/svcdlls/rpl/server/resume.c
@@ -0,0 +1,308 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ resume.c
+
+Abstract:
+
+ Module for dealing with resume keys used in enumerations.
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 19 - November - 1993
+
+Revision History:
+
+--*/
+
+#include "local.h"
+#include "rpldb.h"
+#include "db.h"
+#include "config.h"
+#include "database.h"
+#define RPLRESUME_ALLOCATE
+#include "resume.h"
+#undef RPLRESUME_ALLOCATE
+
+#ifdef RPL_DEBUG
+
+VOID ResumeList(
+ IN PRPL_SESSION pSession,
+ IN PCHAR String
+ )
+{
+ WCHAR ResumeValue[ 20];
+ DWORD NameSize;
+ JET_ERR ForJetError;
+ DWORD ResumeHandle;
+ DWORD ServerHandle;
+
+ Call( JetSetCurrentIndex( pSession->SesId, pSession->ResumeTableId, RESUME_INDEX_ResumeHandle));
+
+ //
+ // We could skip JetMove, but if we do so, we need to modify error
+ // testing for the first JetRetrieveColumn() call (it would return
+ // JET_errNoCurrentRecord for the empty table).
+ //
+
+ for ( ForJetError = JetMove( pSession->SesId, pSession->ResumeTableId, JET_MoveFirst, 0);
+ ForJetError == JET_errSuccess;
+ ForJetError = JetMove( pSession->SesId, pSession->ResumeTableId, JET_MoveNext, 0)) {
+ Call( JetRetrieveColumn( pSession->SesId, pSession->ResumeTableId,
+ ResumeTable[ RESUME_ResumeHandle].ColumnId,
+ &ResumeHandle, sizeof( ResumeHandle), &NameSize, 0, NULL));
+ Call( JetRetrieveColumn( pSession->SesId, pSession->ResumeTableId,
+ ResumeTable[ RESUME_ResumeValue].ColumnId,
+ ResumeValue, sizeof( ResumeValue), &NameSize, 0, NULL));
+ Call( JetRetrieveColumn( pSession->SesId, pSession->ResumeTableId,
+ ResumeTable[ RESUME_ServerHandle].ColumnId,
+ &ServerHandle, sizeof( ServerHandle), &NameSize, 0, NULL));
+ RplDump( RG_DebugLevel & RPL_DEBUG_RESUME, (
+ "%s ResumeH=0x%x,Value=%ws,ServerH=0x%x",
+ String, ResumeHandle, ResumeValue, ServerHandle));
+ }
+}
+#endif // RPL_DEBUG
+
+
+DWORD ResumeCreateTable( OUT PRPL_SESSION pSession)
+/*++
+ Table gets created, then closed. The reason we do not keep the table
+ open is that creator of a table holds exclusive access to that table
+ until he/she closes the table. This would prevent other threads from
+ using the table.
+--*/
+{
+ JET_COLUMNDEF ColumnDef;
+ JET_ERR JetError;
+ DWORD index;
+ DWORD Offset;
+ CHAR IndexKey[ 255];
+
+ JetError = JetCreateTable( pSession->SesId, pSession->DbId, RESUME_TABLE_NAME,
+ RESUME_TABLE_PAGE_COUNT, RESUME_TABLE_DENSITY, &pSession->ResumeTableId);
+ if ( JetError == JET_errTableDuplicate) {
+ //
+ // This would happend only if last time we did not shutdown properly
+ // so ResumeTable never got deleted.
+ //
+ CallM( JetDeleteTable( pSession->SesId, pSession->DbId, RESUME_TABLE_NAME));
+ JetError = JetCreateTable( pSession->SesId, pSession->DbId, RESUME_TABLE_NAME,
+ RESUME_TABLE_PAGE_COUNT, RESUME_TABLE_DENSITY, &pSession->ResumeTableId);
+ }
+ if ( JetError != JET_errSuccess) {
+ return( MapJetError( JetError));
+ }
+
+ //
+ // Create columns. First initalize fields that do not change between
+ // addition of columns.
+ //
+ ColumnDef.cbStruct = sizeof(ColumnDef);
+ ColumnDef.columnid = 0;
+ ColumnDef.wCountry = 1;
+ ColumnDef.langid = 0x0409; // USA english
+ ColumnDef.cp = 1200; // UNICODE codepage
+ ColumnDef.wCollate = 0;
+ ColumnDef.cbMax = 0;
+
+ for ( index = 0; index < RESUME_TABLE_LENGTH; index++) {
+
+ ColumnDef.coltyp = ResumeTable[ index].ColumnType;
+ //
+ // All columns are variable length, but resume handle is autoincrement
+ // as well.
+ //
+ ColumnDef.grbit = (index==RESUME_ResumeHandle) ?
+ JET_bitColumnAutoincrement : 0;
+
+ CallM( JetAddColumn( pSession->SesId, pSession->ResumeTableId,
+ ResumeTable[ index].ColumnName, &ColumnDef,
+ NULL, 0, &ResumeTable[ index].ColumnId));
+ }
+
+ //
+ // We need resume handle as an index, so that at Enum() time we quickly
+ // find resume value for a given resume handle. Beging primary index,
+ // this index is also unique.
+ //
+ Offset = AddKey( IndexKey, '+', ResumeTable[ RESUME_ResumeHandle].ColumnName);
+ IndexKey[ Offset++] = '\0';
+ CallM( JetCreateIndex( pSession->SesId, pSession->ResumeTableId, RESUME_INDEX_ResumeHandle,
+ JET_bitIndexPrimary, IndexKey, Offset, 50));
+
+ //
+ // We need server handle as an index, so that at Close() time we quickly
+ // enumerate all resume keys for a given server handle, then delete them.
+ // This index is NOT unique since we can have a number of resume handles
+ // per client.
+ //
+ Offset = AddKey( IndexKey, '+', ResumeTable[ RESUME_ServerHandle].ColumnName);
+ IndexKey[ Offset++] = '\0';
+ CallM( JetCreateIndex( pSession->SesId, pSession->ResumeTableId, RESUME_INDEX_ServerHandle,
+ 0, IndexKey, Offset, 50));
+
+#ifdef RPL_DEBUG
+ if ( RG_DebugLevel == (DWORD)(-1)) {
+ ResumeList( pSession, "ResumeCreateTable");
+ }
+#endif // RPL_DEBUG
+
+ CallM( JetCloseTable( pSession->SesId, pSession->ResumeTableId));
+ return( ERROR_SUCCESS);
+}
+
+
+
+DWORD ResumeDeleteTable(
+ IN PRPL_SESSION pSession
+ )
+{
+ if ( pSession->ResumeTableId != 0) {
+ CallM( JetCloseTable( pSession->SesId, pSession->ResumeTableId));
+ CallM( JetDeleteTable( pSession->SesId, pSession->DbId, RESUME_TABLE_NAME));
+ }
+ return( NO_ERROR);
+}
+
+
+BOOL ResumeKeyGet(
+ IN PRPL_SESSION pSession,
+ IN DWORD ResumeHandle,
+ OUT PVOID ResumeValue,
+ IN OUT PDWORD pResumeSize
+ )
+{
+ DWORD DataSize;
+#ifdef RPL_DEBUG_NEVER
+ if ( RG_DebugLevel == (DWORD)(-1)) {
+ ResumeList( pSession, "++ResumeKeyGet");
+ }
+#endif // RPL_DEBUG_NEVER
+ CallB( JetSetCurrentIndex( pSession->SesId, pSession->ResumeTableId, RESUME_INDEX_ResumeHandle));
+ CallB( JetMakeKey( pSession->SesId, pSession->ResumeTableId, &ResumeHandle,
+ sizeof( ResumeHandle), JET_bitNewKey));
+ CallB( JetSeek( pSession->SesId, pSession->ResumeTableId, JET_bitSeekEQ));
+ CallB( JetRetrieveColumn( pSession->SesId, pSession->ResumeTableId,
+ ResumeTable[ RESUME_ResumeValue].ColumnId, ResumeValue,
+ *pResumeSize, &DataSize, 0, NULL));
+ if ( DataSize > *pResumeSize) {
+ RplDump( ++RG_Assert, ( "DataSize=%d", DataSize));
+ return( FALSE);
+ }
+ *pResumeSize = DataSize;
+ return( TRUE);
+}
+
+
+BOOL ResumeKeySet(
+ IN PRPL_SESSION pSession,
+ IN DWORD ServerHandle,
+ IN PVOID ResumeValue,
+ IN DWORD ResumeSize,
+ OUT PDWORD pResumeHandle
+ )
+{
+ DWORD DataSize;
+
+ *pResumeHandle = 0; // just in case we fail below
+
+#ifdef RPL_DEBUG
+ if ( RG_DebugLevel == (DWORD)(-1)) {
+ ResumeList( pSession, "++ResumeKeySet");
+ }
+#endif // RPL_DEBUG
+
+ CallB( JetSetCurrentIndex( pSession->SesId, pSession->ResumeTableId, RESUME_INDEX_ResumeHandle));
+#if 0
+ //
+ // We do NOT call JetMove() here, because in the case of an empty table
+ // it returns error JET_errNoCurrentRecord. Also, if JetMove() is used
+ // here, then ordering of elements in the table is such that ResumePrune()
+ // function deletes only the OLDEST record for a given server handle.
+ //
+ CallB( JetMove( pSession->SesId, pSession->ResumeTableId, JET_MoveLast, 0));
+#endif
+ CallB( JetPrepareUpdate( pSession->SesId, pSession->ResumeTableId, JET_prepInsert));
+ CallB( JetRetrieveColumn( pSession->SesId, pSession->ResumeTableId,
+ ResumeTable[ RESUME_ResumeHandle].ColumnId, pResumeHandle,
+ sizeof( *pResumeHandle), &DataSize, JET_bitRetrieveCopy, NULL));
+ if ( DataSize != sizeof( *pResumeHandle) || *pResumeHandle == 0) {
+ RplDump( ++RG_Assert, ( "DataSize=%d", DataSize));
+ return( FALSE);
+ }
+ CallB( JetSetColumn( pSession->SesId, pSession->ResumeTableId,
+ ResumeTable[ RESUME_ResumeValue].ColumnId, ResumeValue,
+ ResumeSize, 0, NULL));
+ CallB( JetSetColumn( pSession->SesId, pSession->ResumeTableId,
+ ResumeTable[ RESUME_ServerHandle].ColumnId, &ServerHandle,
+ sizeof( ServerHandle), 0, NULL));
+ CallB( JetUpdate( pSession->SesId, pSession->ResumeTableId, NULL, 0, NULL));
+#ifdef RPL_DEBUG
+ if ( RG_DebugLevel & RPL_DEBUG_RESUME) {
+ WCHAR ValueBuffer[ 32];
+ DWORD Length;
+ PWCHAR Value;
+ Length = wcslen( (PWCHAR)ResumeValue);
+ if ( (Length + 1) * sizeof(WCHAR) < ResumeSize) {
+ wcscpy( ValueBuffer, (PWCHAR)ResumeValue);
+ wcscat( ValueBuffer, L"!");
+ wcscat( ValueBuffer, (PWCHAR)ResumeValue + Length + 1);
+ Value = ValueBuffer;
+ } else {
+ Value = (PWCHAR)ResumeValue;
+ }
+ RplDump( RG_DebugLevel & RPL_DEBUG_RESUME, (
+ "ResumeKeySet: Handle=0x%x, Value=%.20ws", *pResumeHandle, Value));
+ }
+#endif // RPL_DEBUG
+ return( TRUE);
+}
+
+
+VOID ResumePrune(
+ IN PRPL_SESSION pSession,
+ IN DWORD ServerHandle
+ )
+/*++
+ This function returns no errors, since failure to delete old resume handles
+ is not considered fatal (and there is very little we can do about this).
+--*/
+{
+ JET_ERR JetError;
+
+#ifdef RPL_DEBUG
+ if ( RG_DebugLevel == (DWORD)(-1)) {
+ ResumeList( pSession, "++ResumePrune");
+ }
+#endif // RPL_DEBUG
+ CallR( JetSetCurrentIndex( pSession->SesId, pSession->ResumeTableId, RESUME_INDEX_ServerHandle));
+ CallR( JetMakeKey( pSession->SesId, pSession->ResumeTableId, &ServerHandle,
+ sizeof( ServerHandle), JET_bitNewKey));
+#ifdef NOT_YET
+ JetError = JetSeek( pSession->SesId, pSession->ResumeTableId, JET_bitSeekEQ);
+#else
+ JetError = JetSeek( pSession->SesId, pSession->ResumeTableId, JET_bitSeekEQ | JET_bitSetIndexRange);
+#endif
+ if ( JetError == JET_errSuccess) {
+#ifdef NOT_YET
+ CallR( JetMakeKey( pSession->SesId, pSession->ResumeTableId, &ServerHandle,
+ sizeof( ServerHandle), JET_bitNewKey));
+ CallR( JetSetIndexRange( pSession->SesId, pSession->ResumeTableId,
+ JET_bitRangeInclusive | JET_bitRangeUpperLimit));
+#endif
+ do {
+ Call( JetDelete( pSession->SesId, pSession->ResumeTableId));
+ JetError = JetMove( pSession->SesId, pSession->ResumeTableId, JET_MoveNext, 0);
+ } while ( JetError == JET_errSuccess);
+ }
+#ifdef RPL_DEBUG
+ if ( RG_DebugLevel == (DWORD)(-1)) {
+ ResumeList( pSession, "--ResumePrune");
+ }
+#endif // RPL_DEBUG
+}
+
diff --git a/private/net/svcdlls/rpl/server/resume.h b/private/net/svcdlls/rpl/server/resume.h
new file mode 100644
index 000000000..12c791d4a
--- /dev/null
+++ b/private/net/svcdlls/rpl/server/resume.h
@@ -0,0 +1,61 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ resume.h
+
+Abstract:
+
+ Describes layout of JET database table used for RESUME structures.
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 19 - November - 1993
+
+Environment:
+
+ User mode
+
+Revision History :
+
+--*/
+
+#ifdef RPLRESUME_ALLOCATE
+#define EXTERN_RESUME
+#define INIT_RESUME( _x) = _x
+#else
+#define EXTERN_RESUME extern
+#define INIT_RESUME( _x)
+#endif
+
+//
+// Indices of entries in ResumeTable[] - resume key array.
+//
+#define RESUME_ResumeHandle 0
+#define RESUME_ResumeType 1
+#define RESUME_ResumeValue 2
+#define RESUME_ServerHandle 3
+#define RESUME_TABLE_LENGTH 4
+
+RPL_COLUMN_INFO ResumeTable[]
+#ifdef RPLRESUME_ALLOCATE
+ = {
+ { "ResumeHandle", JET_coltypLong, 0}, // for enumeration
+ { "ResumeType", JET_coltypLong, 0}, // type of operation
+ { "ResumeValue", JET_coltypBinary, 0}, // where to restart
+ { "ServerHandle", JET_coltypLong, 0}
+}
+#endif // RPLRESUME_ALLOCATE
+;
+
+#define RESUME_INDEX_ResumeHandle "foo" // + ResumeHandle
+#define RESUME_INDEX_ServerHandle "goo" // + ServerHandle
+
+#define RESUME_TABLE_NAME "Resume"
+#define RESUME_TABLE_PAGE_COUNT 5 // initial number of 4K pages
+#define RESUME_TABLE_DENSITY 100 // initial density
+
+EXTERN_RESUME JET_TABLEID ResumeTableId;
+
diff --git a/private/net/svcdlls/rpl/server/rpldata.h b/private/net/svcdlls/rpl/server/rpldata.h
new file mode 100644
index 000000000..26447bf68
--- /dev/null
+++ b/private/net/svcdlls/rpl/server/rpldata.h
@@ -0,0 +1,161 @@
+/*++
+
+Copyright (c) 1987-1993 Microsoft Corporation
+
+Module Name:
+
+ rpldata.h
+
+Abstract:
+
+ Remote initial Program Load service global variables (external &
+ definitions).
+
+ Provides similar functionality to rplglob.c & rmapext.h in LANMAN 2.1 code.
+
+Author:
+
+ Vladimir Z. Vulovic 27 - July - 1993
+
+Environment:
+
+ User mode
+
+Revision History :
+
+--*/
+
+//
+// rplmain.c will #include this file with RPLDATA_ALLOCATE defined.
+// That will cause each of these variables to be allocated.
+//
+#ifdef RPLDATA_ALLOCATE
+#define EXTERN
+#define INIT( _x) = _x
+#else
+#define EXTERN extern
+#define INIT( _x)
+#endif
+
+#ifdef RPL_DEBUG
+EXTERN DWORD RG_Debug;
+EXTERN DWORD RG_BootCount;
+#endif // RPL_DEBUG
+
+
+EXTERN DWORD RG_Error;
+EXTERN DWORD RG_Null INIT( 0); // used in bbcfile for empty entries in string tables
+
+//
+// Synchronization Object for RPL server code
+//
+
+EXTERN CRITICAL_SECTION RG_ProtectRcbList;
+EXTERN CRITICAL_SECTION RG_ProtectServerHandle;
+EXTERN CRITICAL_SECTION RG_ProtectCurrentStatus; // service status
+
+
+EXTERN DWORD RG_ServerHandle; // service handle returned to client
+EXTERN DWORD RG_AdapterPolicy; // whether to log AdapterName-s of unknown clients
+EXTERN DWORD RG_BackupInterval; // how often to do database backup (in hours)
+EXTERN RPL_SESSION RG_RequestSession; // jet handles for request threads
+EXTERN RPL_SESSION RG_WorkerSession; // jet handles for worker threads
+EXTERN RPL_SESSION RG_ApiSession; // jet handles for api threads
+EXTERN BOOL RG_DetachDatabase; // do we need to detach jet database
+EXTERN PCHAR RG_Mdb; // points to rplsvc.mdb
+EXTERN PCHAR RG_BackupPath; // points to database backup path
+EXTERN JET_INSTANCE RG_Instance;
+EXTERN BOOL RG_InstanceAllocated;
+EXTERN CRITICAL_SECTION RG_ProtectRequestSession;
+EXTERN CRITICAL_SECTION RG_ProtectWorkerSession;
+EXTERN CRITICAL_SECTION RG_ProtectDatabase; // used for ApiSession also
+
+
+EXTERN HANDLE RG_TerminateNowEvent;
+
+//
+// Other globals of the actual ripl server code:
+//
+
+EXTERN SERVICE_STATUS RG_ServiceStatus;
+EXTERN SERVICE_STATUS_HANDLE RG_ServiceStatusHandle;
+EXTERN SC_HANDLE RG_ServiceHandle;
+EXTERN DWORD RG_Tasks;
+EXTERN CRITICAL_SECTION RG_ProtectServiceStatus;
+
+EXTERN HANDLE RG_MemoryHandle; // for memory "owned" by main thread
+EXTERN HANDLE RG_MessageHandle;
+
+EXTERN PRPL_REQUEST_PARAMS RG_pRequestParams;
+EXTERN CRITICAL_SECTION RG_ProtectRequestList;
+
+
+EXTERN DWORD RG_MaxWorkerCount; // upper limit on number of worker threads
+EXTERN DWORD RG_WorkerCount; // number of worker threads
+EXTERN HANDLE RG_EventWorkerCount; // event for changes in the above
+EXTERN CRITICAL_SECTION RG_ProtectWorkerCount;
+
+EXTERN CRITICAL_SECTION RG_ProtectTerminationList;
+EXTERN PTERM_LIST RG_TerminationListBase; // base of termination list
+
+//
+// The following globals are defined together with their default values.
+// These values may be overriden by user in lanman.ini & command line.
+//
+
+EXTERN WCHAR RG_Directory[ MAX_PATH]; // path to rpl service disk data
+EXTERN DWORD RG_DirectoryLength; // length of the above path
+
+EXTERN BOOL RG_ReadChecksum; // status set while files chksmmed
+EXTERN PBYTE RG_CodePageBuffer; // "pCodePage"
+EXTERN DWORD RG_CodePageSize; // "cbCodePage"
+
+//
+// This value contains the current adapter policy and also the startup flags
+//
+
+EXTERN DWORD RG_AdapterPolicy;
+
+//
+// If we ever separate DLC-RPL server from FILE-RPL server, then
+// RG_ComputerName should be a pointer to FILE-RPL server name.
+//
+EXTERN WCHAR RG_ComputerName[ MAX_COMPUTERNAME_LENGTH + 1];
+
+EXTERN PWCHAR RG_UncRplfiles; // UNC name sent to rpl client
+EXTERN PWCHAR RG_DiskRplfiles; // local path used in disk operations
+
+EXTERN PWCHAR RG_DiskBinfiles; // local path to binfiles root
+
+
+//
+// The following group of globals should be constant and they are
+// initialized only once, at load exe time (not every time at
+// start service time).
+//
+
+//
+// NLS patches of RPLBOOT.SYS and DLCLOADR.COM
+//
+
+EXTERN DWORD RG_MessageTable[]
+#ifdef RPLDATA_ALLOCATE
+ = {
+ NERR_BadDosRetCode,
+ NERR_ProgNeedsExtraMem,
+ NERR_BadDosFunction,
+ NERR_RemoteBootFailed,
+ NERR_BadFileCheckSum,
+ NERR_NoRplBootSystem
+}
+#endif // RPLDATA_ALLOCATE
+;
+
+#define MESSAGE_TABLE_LENGTH 6 // number of entries in the array above
+#define DBCS_MESSAGE_BUFFER_SIZE (MESSAGE_TABLE_LENGTH * DBCS_SINGLE_MESSAGE_BUFFER_SIZE)
+
+EXTERN PBYTE RG_DbcsMessageBuffer INIT( NULL);
+
+#undef EXTERN
+
+
diff --git a/private/net/svcdlls/rpl/server/rplmain.c b/private/net/svcdlls/rpl/server/rplmain.c
new file mode 100644
index 000000000..2d8c568e3
--- /dev/null
+++ b/private/net/svcdlls/rpl/server/rplmain.c
@@ -0,0 +1,982 @@
+/*++
+
+Copyright (c) 1987-1993 Microsoft Corporation
+
+Module Name:
+
+ rplmain.c
+
+Abstract:
+
+ Contains RPL_main(), RPL service entry point. Initializes and shuts down
+ RPL service.
+
+ Provides similar functionality to rplservr.c in LANMAN 2.1 code.
+
+Author:
+
+ Vladimir Z. Vulovic 27 - July - 1993
+
+Environment:
+
+ User mode
+
+Revision History :
+
+--*/
+
+#define RPLDATA_ALLOCATE
+#include "local.h"
+#undef RPLDATA_ALLOCATE
+#include "request.h"
+#include "database.h"
+#include "debug.h"
+#include "apisec.h"
+#include <rplnames.h> // RPL_INTERFACE_NAME
+#include "rplsvc_s.h" // rplsvc_ServerIfHandle
+#include "setup.h" // SetupAction()
+
+#define WCSLEN( _x_) ((DWORD) ( sizeof(_x_)/sizeof(WCHAR) - 1))
+#define NET_MSG_FILE L"netmsg.dll"
+#define RPLFILES_STRING L"RPLFILES"
+#define BINFILES_STRING L"\\BINFILES\\"
+#define RPL_DEFAULT_DIRECTORY L"%SystemRoot%\\rpl\\"
+#define RPL_SERVICE_NAME L"RemoteBoot"
+
+#define RPL_SECURITY_OBJECT_CREATED 0x1
+#define RPL_RPC_SERVER_STARTED 0x2
+
+
+DWORD RplMessageGet(
+ IN DWORD MessageId,
+ OUT LPWSTR buffer,
+ IN DWORD Size
+ )
+/*++
+
+Routine Description:
+
+ Fills in the unicode message corresponding to a given message id,
+ provided that a message can be found and that it fits in a supplied
+ buffer.
+
+Arguments:
+
+ MessageId - message id
+ buffer - pointer to caller supplied buffer
+ Size - size (always in bytes) of supplied buffer
+
+Return Value:
+
+ Count of characters, not counting the terminating null character,
+ returned in the buffer. Zero return value indicates failure.
+
+--*/
+{
+ DWORD length;
+ LPVOID lpSource;
+ DWORD dwFlags;
+
+ if ( MessageId < NERR_BASE) {
+ //
+ // Get message from system.
+ //
+ lpSource = NULL; // redundant step according to FormatMessage() spec
+ dwFlags = FORMAT_MESSAGE_FROM_SYSTEM;
+
+ } else {
+ //
+ // Get message from netmsg.dll.
+ //
+ lpSource = (LPVOID)RG_MessageHandle;
+ dwFlags = FORMAT_MESSAGE_FROM_HMODULE;
+ }
+
+//#define RPL_ELNK
+#ifdef RPL_ELNK
+ wcscpy( buffer, L"NET2500: ");
+ buffer[ 6] = L'0' + MessageId - NERR_BadDosRetCode;
+ buffer += 9;
+ Size -= 9 * sizeof(WCHAR);
+#endif
+
+ length = FormatMessage(
+ dwFlags, // dwFlags
+ lpSource, // lpSource
+ MessageId, // MessageId
+ 0, // dwLanguageId
+ buffer, // lpBuffer
+ Size, // nSize
+ NULL // lpArguments
+ );
+
+ if ( length == 0) {
+ RG_Error = GetLastError();
+ RplReportEvent( NELOG_RplMessages, NULL, 0, NULL);
+ RPL_RETURN( 0);
+ }
+#ifdef RPL_ELNK
+ length += 9;
+#endif
+ return( length);
+}
+
+
+BOOL RplInitMessages( VOID)
+/*++
+
+Routine Description:
+ Creates an array of NLS (DBCS ??) messages for RPLBOOT.SYS.
+
+Arguments:
+ None.
+
+Return Value:
+ TRUE if success, FALSE otherwise.
+
+--*/
+{
+ WCHAR UnicodeString[ MAX_PATH];
+ DWORD UnicodeStringLength;
+ CHAR DbcsString[ MAX_PATH];
+ PBYTE pByte;
+ DWORD Index;
+ DWORD DbcsStringSize; // not including terminal null byte
+
+ RplDump( RG_DebugLevel & RPL_DEBUG_FLOWINIT,( "++InitMessages"));
+
+ RG_DbcsMessageBuffer = RplMemAlloc( RG_MemoryHandle, DBCS_MESSAGE_BUFFER_SIZE);
+ if ( RG_DbcsMessageBuffer == NULL) {
+ RG_Error = GetLastError();
+ RPL_RETURN( FALSE);
+ }
+
+ //
+ // read NLS messages from message file to patch table
+ //
+ for ( Index = 0, pByte = RG_DbcsMessageBuffer;
+ Index < MESSAGE_TABLE_LENGTH;
+ Index++, pByte += DBCS_SINGLE_MESSAGE_BUFFER_SIZE) {
+
+ UnicodeStringLength = RplMessageGet( RG_MessageTable[ Index],
+ UnicodeString, sizeof( UnicodeString));
+ if ( UnicodeStringLength == 0) {
+ return( FALSE);
+ }
+ //
+ // Null terminate the string - redundant ?
+ //
+ UnicodeString[ UnicodeStringLength] = 0;
+
+ DbcsStringSize = WideCharToMultiByte(
+ CP_OEMCP,
+ 0,
+ UnicodeString,
+ UnicodeStringLength,
+ DbcsString,
+ sizeof( DbcsString),
+ NULL, // no default character
+ NULL // no default character flag
+ );
+ if ( DbcsStringSize == 0) {
+ RG_Error = GetLastError();
+ RPL_RETURN( FALSE);
+ }
+// #define TEST_MESSAGE_TOO_LONG
+#ifdef TEST_MESSAGE_TOO_LONG
+ if ( DbcsStringSize > 48) {
+ DbcsStringSize = 48;
+ }
+#else
+ //
+ // If message is too long truncate it - leaving one char for the
+ // end of string mark of MS-DOS ('$').
+ //
+ if ( DbcsStringSize >= DBCS_SINGLE_MESSAGE_BUFFER_SIZE) {
+ DbcsStringSize = (DWORD)(DBCS_SINGLE_MESSAGE_BUFFER_SIZE-1);
+ }
+#endif
+ memcpy( pByte, DbcsString, DbcsStringSize);
+ //
+ // The messages returned by DosGetMsg are not nul terminated =>
+ // reset the whole message buffer with end of string marks of MS-DOS.
+ //
+ memset( pByte + DbcsStringSize, '$', DBCS_SINGLE_MESSAGE_BUFFER_SIZE - DbcsStringSize);
+ }
+
+ RplDump( RG_DebugLevel & RPL_DEBUG_FLOWINIT,( "--InitMessages"));
+ return( TRUE);
+}
+
+
+
+BOOL RplMakeSingleShare(
+ IN LPWSTR NetworkName,
+ IN LPWSTR Path,
+ IN LPWSTR Comment
+ )
+/*++
+
+Routine Description:
+
+ Sets up a file share. It also checks that the existing share is
+ valid (points to the right directory).
+
+Arguments:
+
+ NetworkName - network name of the shared resource
+ Path - local path of the shared resource
+ Comment - comment about the shared resource
+
+Return Value:
+
+ TRUE if successful, else FALSE.
+
+--*/
+{
+ SHARE_INFO_2 ShareInfo2;
+ PSHARE_INFO_2 pShareInfo2 = NULL;
+ BOOL useNetApiBufferFree = TRUE;
+ DWORD Error;
+
+ //
+ // Check if we have already the share.
+ //
+ Error = NetShareGetInfo( NULL, NetworkName, 2, (LPBYTE *)&pShareInfo2);
+
+ if ( Error == NO_ERROR) {
+ //
+ // If the path points to the right place, assume share is O.K.
+ // and return from here. Else, delete this invalid share.
+ //
+ if ( !wcscmp( pShareInfo2->shi2_path, Path )) {
+ Error = NO_ERROR;
+ goto cleanup;
+
+ } else if ( (Error = NetShareDel( NULL, NetworkName, 0))
+ != NO_ERROR) {
+ goto cleanup;
+ }
+
+ } else if ( Error != NERR_NetNameNotFound) {
+ // Unexpected return code from NetShareGetInfo. Bail out.
+ goto cleanup;
+ }
+
+ //
+ // If we arrive here we either did not have the share or we have
+ // just deleted the invalid share. Now set up the correct share.
+ //
+
+ ShareInfo2.shi2_netname = NetworkName;
+ ShareInfo2.shi2_path = Path;
+ ShareInfo2.shi2_remark = Comment;
+
+ ShareInfo2.shi2_type = STYPE_DISKTREE;
+ ShareInfo2.shi2_permissions = 0x3F; // no permission bit set
+ ShareInfo2.shi2_max_uses = SHI_USES_UNLIMITED;
+ ShareInfo2.shi2_current_uses = 0;
+ ShareInfo2.shi2_passwd = NULL;
+
+ Error = NetShareAdd( NULL, 2, (LPBYTE)&ShareInfo2, NULL);
+
+cleanup:
+
+ if ( pShareInfo2) {
+ NetApiBufferFree( (LPVOID) pShareInfo2);
+ }
+
+ if ( Error != NO_ERROR) {
+ RplDump( ++RG_Assert,( "Error = %d", Error));
+ return( FALSE);
+ }
+ return( TRUE);
+}
+
+
+
+BOOL RplConfigured( VOID)
+/*++
+
+Routine Description:
+
+ Verifies that FILE server has been configured to serve RPL clients
+ by checking for the existence of the group RPLUSER, a group
+ that gets created by RPLINST, the last phase of RPL configuration.
+
+Arguments:
+ None.
+
+Return Value:
+
+--*/
+{
+ LPBYTE pbyte = NULL;
+ DWORD Error;
+
+ Error = NetGroupGetInfo( NULL, RPLUSER_GROUP, 0, &pbyte);
+
+ if ( pbyte != NULL) {
+ NetApiBufferFree( pbyte);
+ }
+
+ if (Error == NERR_GroupNotFound) {
+#ifdef NOT_YET
+ Error = NERR_RplNotRplServer; // error mapping
+#else // NOT_YET
+ KdPrint(( "[RplSvc] %ws group not found\n", RPLUSER_GROUP));
+ Error = NO_ERROR; // BUGBUG trundle along for now
+#endif // NOT_YET
+ }
+
+ if ( Error != NO_ERROR) {
+ RplDump( ++RG_Assert,( "Error = %d", Error));
+ RG_Error = NERR_RplNotRplServer;
+ return( FALSE);
+ }
+ return( TRUE);
+}
+
+
+BOOL RplInit( OUT PDWORD pStartup)
+/*++
+ pStartup - Startup actions flag, read from registry.
+--*/
+{
+ DWORD Error;
+ DWORD Size;
+ SC_HANDLE ControlManagerHandle;
+
+ RplDump( RG_DebugLevel & RPL_DEBUG_FLOW,( "++RplInit"));
+
+ Error = RplMemInit( &RG_MemoryHandle);
+ if ( Error != NO_ERROR) {
+ RG_Error = Error;
+ return( FALSE);
+ }
+ RplDump( RG_DebugLevel & RPL_DEBUG_MEMORY,(
+ "MakeReinit: RG_MemoryHandle=0x%x", RG_MemoryHandle));
+
+ RG_DirectoryLength = sizeof(RG_Directory)/sizeof(WCHAR);
+ if ( RplRegRead( pStartup, &RG_BackupInterval, &RG_MaxWorkerCount,
+ RG_Directory, &RG_DirectoryLength) != NO_ERROR) {
+ RG_Error = NERR_RplBadRegistry;
+ return( FALSE);
+ }
+
+ if ( !RplDbInit()) {
+ RG_Error = NERR_RplBadDatabase;
+ return( FALSE);
+ }
+
+ ControlManagerHandle = OpenSCManager( NULL, L"ServicesActive", 0);
+ if ( ControlManagerHandle == NULL) {
+ RG_Error = GetLastError();
+ RPL_RETURN( FALSE);
+ }
+
+ RG_ServiceHandle = OpenService( ControlManagerHandle,
+ RPL_SERVICE_NAME,
+ SERVICE_STOP // desired access
+ );
+ if ( RG_ServiceHandle == NULL) {
+ RG_Error = GetLastError();
+ RPL_RETURN( FALSE);
+ }
+
+ RG_TerminateNowEvent = CreateEvent(
+ NULL, // No security attributes
+ TRUE, // Must be manually reset
+ FALSE, // Initially not signaled
+ NULL); // No name
+ if ( RG_TerminateNowEvent == NULL ) {
+ RG_Error = GetLastError();
+ RPL_RETURN( FALSE);
+ }
+
+#ifndef RPL_NO_SERVICE
+ if ( !SetServiceStatus( RG_ServiceStatusHandle, &RG_ServiceStatus)) {
+ RG_Error = GetLastError();
+ RPL_RETURN( FALSE);
+ }
+#endif
+
+ //
+ // We use manual reset. With automatic reset, when two request threads
+ // are both waiting for this event, if several worker threads exit at
+ // once, only one request thread may be awaken. Other request thread
+ // may stay asleep for a long time, until next worker thread exits.
+ //
+ RG_EventWorkerCount = CreateEvent(
+ NULL, // no security attributes
+ TRUE, // use manual reset
+ FALSE, // initial value is not-signalled
+ NULL); // no name
+ if ( RG_EventWorkerCount == NULL) {
+ RG_Error = GetLastError();
+ RPL_RETURN( FALSE);
+ }
+
+ if ( !RplConfigured()) {
+ return( FALSE);
+ }
+
+ //
+ // Note that upon successful return: Size == wcslen( RG_ComputerName)
+ //
+ Size = sizeof( RG_ComputerName);
+ if ( !GetComputerName( RG_ComputerName, &Size)) {
+ RG_Error = GetLastError();
+ RPL_RETURN( FALSE);
+ }
+ RPL_ASSERT( Size == wcslen( RG_ComputerName));
+
+ //
+ // Initialize RG_UncRplfiles which is used in fit file replacements
+ // and RG_DiskRplfiles which is used for api disk operations.
+ //
+ Size *= sizeof( WCHAR);
+ Size += sizeof(DOUBLE_BACK_SLASH_STRING) + sizeof(RPLFILES_STRING);
+ RG_UncRplfiles = RplMemAlloc( RG_MemoryHandle, Size);
+ if ( RG_UncRplfiles == NULL) {
+ RG_Error = GetLastError();
+ RPL_RETURN( FALSE);
+ }
+ wcscpy( RG_UncRplfiles, DOUBLE_BACK_SLASH_STRING);
+ wcscat( RG_UncRplfiles, RG_ComputerName);
+ wcscat( RG_UncRplfiles, L"\\");
+ wcscat( RG_UncRplfiles, RPLFILES_STRING);
+
+ Size = RG_DirectoryLength * sizeof(WCHAR) + sizeof(RPLFILES_STRING);
+ RG_DiskRplfiles = RplMemAlloc( RG_MemoryHandle, Size);
+ if ( RG_DiskRplfiles == NULL) {
+ RG_Error = GetLastError();
+ RPL_RETURN( FALSE);
+ }
+ wcscpy( RG_DiskRplfiles, RG_Directory);
+ wcscat( RG_DiskRplfiles, RPLFILES_STRING);
+
+ Size += WCSLEN( BINFILES_STRING) * sizeof(WCHAR);
+ RG_DiskBinfiles = RplMemAlloc( RG_MemoryHandle, Size);
+ if ( RG_DiskBinfiles == NULL) {
+ RG_Error = GetLastError();
+ RPL_RETURN( FALSE);
+ }
+ wcscpy( RG_DiskBinfiles, RG_DiskRplfiles);
+ wcscat( RG_DiskBinfiles, BINFILES_STRING);
+
+#ifndef RPL_NO_SERVICE
+ if ( !SetServiceStatus( RG_ServiceStatusHandle, &RG_ServiceStatus)) {
+ RG_Error = GetLastError();
+ RPL_RETURN( FALSE);
+ }
+#endif
+
+ //
+ // Load file containing network messages.
+ //
+ RG_MessageHandle = LoadLibrary( NET_MSG_FILE);
+ if ( RG_MessageHandle == NULL) {
+ RG_Error = GetLastError();
+ RplReportEvent( NELOG_Init_OpenCreate_Err, NET_MSG_FILE,
+ sizeof( RG_Error), (PBYTE)&RG_Error);
+ RPL_RETURN( FALSE);
+ }
+
+ //
+ // Share the default rplfiles share, should have been done earlier ?
+ //
+ if ( !RplMakeSingleShare( RPLFILES_STRING, RG_DiskRplfiles, RPL_REMARK)){
+ RG_Error = NERR_RplRplfilesShare;
+ return( FALSE);
+ }
+
+#ifndef RPL_NO_SERVICE
+ if ( !SetServiceStatus( RG_ServiceStatusHandle, &RG_ServiceStatus)) {
+ RG_Error = GetLastError();
+ RPL_RETURN( FALSE);
+ }
+#endif
+
+ //
+ // RplInitMessages() must be called before RplStartAdapters() because
+ // it initializes RG_DbcsMessageBuffer() which must be present as soon
+ // as adapters are started. Otherwise, a very quick boot request by
+ // RPL client would trap the service in RplMakePatch().
+ //
+ if ( !RplInitMessages()) {
+ return( FALSE);
+ }
+
+ if ( !RplStartAdapters()) {
+ return( FALSE);
+ }
+
+ RplDump( RG_DebugLevel & RPL_DEBUG_FLOW,( "--RplInit"));
+ return( TRUE);
+}
+
+
+VOID RplCleanup( VOID)
+/*++
+
+Routine Description:
+
+ Cleanup all global resources.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ DWORD ErrorCode = NO_ERROR;
+
+ RplDbTerm();
+ //
+ // Free RPL critical sections.
+ //
+ DeleteCriticalSection( &RG_ProtectRcbList);
+ DeleteCriticalSection( &RG_ProtectTerminationList);
+ DeleteCriticalSection( &RG_ProtectRequestList);
+ DeleteCriticalSection( &RG_ProtectWorkerCount);
+ DeleteCriticalSection( &RG_ProtectServiceStatus);
+ DeleteCriticalSection( &RG_ProtectServerHandle);
+ DeleteCriticalSection( &RG_ProtectRequestSession);
+ DeleteCriticalSection( &RG_ProtectWorkerSession);
+ DeleteCriticalSection( &RG_ProtectDatabase);
+}
+
+
+
+VOID RplControlHandler( IN DWORD OpCode)
+/*++
+
+Routine Description:
+
+ Process and respond to a control signal from the service controller.
+
+Arguments:
+
+ OpCode - Supplies a value which specifies the action for the RPL
+ service to perform.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ KdPrint(( "[RplSvc]++ ControlHandler\n"));
+
+ switch (OpCode) {
+
+ case SERVICE_CONTROL_PAUSE:
+#ifdef RPL_DEBUG
+ DbgUserBreakPoint(); // for debugging
+#endif
+ EnterCriticalSection( &RG_ProtectServiceStatus);
+ RG_ServiceStatus.dwCurrentState = SERVICE_PAUSED;
+ LeaveCriticalSection( &RG_ProtectServiceStatus);
+ break;
+
+ case SERVICE_CONTROL_CONTINUE:
+ EnterCriticalSection( &RG_ProtectServiceStatus);
+ RG_ServiceStatus.dwCurrentState = SERVICE_RUNNING;
+ LeaveCriticalSection( &RG_ProtectServiceStatus);
+ break;
+
+ case SERVICE_CONTROL_SHUTDOWN:
+ case SERVICE_CONTROL_STOP:
+
+ if (RG_ServiceStatus.dwCurrentState != SERVICE_STOP_PENDING) {
+
+ KdPrint(( "[RplSvc]ControlHandler: stopping remote boot service...\n"));
+
+ RG_ServiceStatus.dwCurrentState = SERVICE_STOP_PENDING;
+ RG_ServiceStatus.dwCheckPoint = 1;
+ RG_ServiceStatus.dwWaitHint = RPL_WAIT_HINT_TIME;
+
+ //
+ // Send the status response.
+ //
+#ifndef RPL_NO_SERVICE
+ if ( !SetServiceStatus( RG_ServiceStatusHandle, &RG_ServiceStatus)) {
+ RplDump( ++RG_Assert, ( "Error = ", RG_Error));
+ NOTHING; // ignore this error
+ }
+#endif
+
+ if (! SetEvent( RG_TerminateNowEvent)) {
+
+ KdPrint(( "[RplSvc]ControlHandler: error setting"
+ " TerminateNowEvent, error=%d\n",
+ GetLastError()));
+ RPL_ASSERT( FALSE);
+ }
+
+ return;
+ }
+ break;
+
+ case SERVICE_CONTROL_INTERROGATE:
+ break;
+
+ default:
+ KdPrint((
+ "[RplSvc] ControlHandler: unknown remote boot service control,"
+ " OpCode=0x%x\n",
+ OpCode));
+ break;
+ }
+
+ //
+ // Send the status response.
+ //
+#ifndef RPL_NO_SERVICE
+ if ( !SetServiceStatus( RG_ServiceStatusHandle, &RG_ServiceStatus)) {
+ RplDump( ++RG_Assert, ( "Error = ", GetLastError()));
+ NOTHING; // ignore this error
+ }
+#endif
+}
+
+
+
+VOID RplInitGlobals( VOID)
+{
+ //
+ // Initializes all global variables that are "variable".
+ //
+
+ RG_WorkerCount = 0;
+ RG_TerminationListBase = NULL;
+ RG_MessageHandle = NULL;
+#ifdef RPL_DEBUG
+ RG_Debug = (DWORD)-1;
+ RG_BootCount = 0;
+#endif // RPL_DEBUG
+
+ //
+ // Values of globals below may be overriden by user in lanman.ini & command line.
+ //
+
+ RG_Directory[ 0] = 0;
+ RG_DirectoryLength = 0;
+ RG_ReadChecksum = FALSE;
+ RG_CodePageBuffer = NULL;
+ RG_CodePageSize = 0;
+
+ RG_TerminateNowEvent = NULL;
+
+ RG_ComputerName[ 0] = 0;
+ RG_UncRplfiles = NULL;
+ RG_DiskRplfiles = NULL;
+
+ RG_ServerHandle = 0;
+
+ RG_pRequestParams = NULL;
+
+ InitializeCriticalSection( &RG_ProtectRcbList);
+ InitializeCriticalSection( &RG_ProtectTerminationList);
+ InitializeCriticalSection( &RG_ProtectRequestList);
+ InitializeCriticalSection( &RG_ProtectWorkerCount);
+ InitializeCriticalSection( &RG_ProtectServiceStatus);
+ InitializeCriticalSection( &RG_ProtectServerHandle);
+ InitializeCriticalSection( &RG_ProtectRequestSession);
+ InitializeCriticalSection( &RG_ProtectWorkerSession);
+ InitializeCriticalSection( &RG_ProtectDatabase);
+}
+
+
+VOID ShutdownRequestParams( VOID)
+{
+ PRPL_REQUEST_PARAMS pRequestParams;
+ DWORD status;
+
+ for ( pRequestParams = RG_pRequestParams;
+ pRequestParams != NULL;
+ pRequestParams = pRequestParams->pRequestParams) {
+ if ( pRequestParams->ThreadHandle != NULL) {
+ status = WaitForSingleObject( pRequestParams->ThreadHandle, INFINITE);
+ if ( status != 0) {
+ RplDump( ++RG_Assert, ( "pRequestParams=0x%x, status=0x%x",
+ pRequestParams, status==WAIT_FAILED ? GetLastError() : status));
+ }
+ if ( !CloseHandle( pRequestParams->ThreadHandle)) {
+ RplDump( ++RG_Assert, ( "pRequestParams=0x%x, error=%d",
+ pRequestParams, GetLastError()));
+ }
+ pRequestParams->ThreadHandle = NULL;
+ }
+ }
+}
+
+
+VOID RPL_main(
+ IN DWORD argc,
+ IN LPWSTR argv[]
+ )
+/*++
+
+Routine Description:
+
+ Main procedure of the program. This is the main RPL worker thread.
+ Purpose:
+
+ - start the initialization thread
+ - register signal handler
+
+Arguments:
+
+ argc - parameter count
+ argv - array of pointers to parameters
+
+Return Value:
+
+ None.
+
+--*/
+{
+ DWORD Error;
+ DWORD InitState;
+ DWORD StartupFlags;
+
+ UNREFERENCED_PARAMETER( argc);
+ UNREFERENCED_PARAMETER( argv);
+
+ InitState = 0;
+
+ RG_Error = NO_ERROR;
+ RG_ServiceStatus.dwServiceType = SERVICE_WIN32;
+ RG_ServiceStatus.dwCurrentState = SERVICE_START_PENDING;
+ RG_ServiceStatus.dwControlsAccepted = 0;
+ RG_ServiceStatus.dwCheckPoint = 1;
+ RG_ServiceStatus.dwWaitHint = RPL_WAIT_HINT_TIME;
+
+ // This should be the FIRST thing we do.
+
+#ifdef RPL_DEBUG
+ RplDebugInitialize();
+#endif // RPL_DEBUG
+
+
+ SET_SERVICE_EXITCODE(
+ RG_Error,
+ RG_ServiceStatus.dwWin32ExitCode,
+ RG_ServiceStatus.dwServiceSpecificExitCode
+ );
+
+ RplInitGlobals(); // make sure we do not trap during cleanup
+
+#ifndef RPL_NO_SERVICE
+ RG_ServiceStatusHandle = RegisterServiceCtrlHandler(
+ SERVICE_RIPL,
+ RplControlHandler
+ );
+ if( RG_ServiceStatusHandle == (SERVICE_STATUS_HANDLE)NULL) {
+ RG_Error = GetLastError();
+ RPL_ASSERT( FALSE);
+ goto main_exit;
+ }
+
+ if ( !SetServiceStatus( RG_ServiceStatusHandle, &RG_ServiceStatus)) {
+ RG_Error = GetLastError();
+ RPL_ASSERT( FALSE);
+ goto main_exit;
+ }
+#endif
+
+ if ( !RplInit( &StartupFlags)) {
+ goto main_exit;
+ }
+
+ //
+ // Create remote boot service security object
+ //
+ Error = RplCreateSecurityObject();
+ if ( Error != NO_ERROR) {
+ RG_Error = Error;
+ goto main_exit;
+ }
+ InitState |= RPL_SECURITY_OBJECT_CREATED;
+
+ //
+ // Initialize the schedule service to receive RPC requests.
+ // Note that interface name is defined in rplsvc.idl file.
+ //
+ Error = NetpStartRpcServer( RPL_INTERFACE_NAME, rplsvc_ServerIfHandle);
+ if ( Error != NO_ERROR) {
+ RG_Error = Error;
+ RPL_ASSERT( FALSE);
+ goto main_exit;
+ }
+ InitState |= RPL_RPC_SERVER_STARTED;
+
+ //
+ // We are done with starting the Remoteboot service. Tell Service
+ // Controller our new status.
+ //
+
+ RG_ServiceStatus.dwCurrentState = SERVICE_RUNNING;
+ RG_ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP |
+ SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_PAUSE_CONTINUE;
+ RG_ServiceStatus.dwCheckPoint = 0;
+ RG_ServiceStatus.dwWaitHint = 0;
+
+#ifndef RPL_NO_SERVICE
+ if ( !SetServiceStatus( RG_ServiceStatusHandle, &RG_ServiceStatus)) {
+ RG_Error = GetLastError();
+ RPL_ASSERT( FALSE);
+ goto main_exit;
+ }
+#endif
+
+ //
+ // The server is running. If the registry specified any
+ // special startup actions, perform them now. Note that
+ // it will not be possible to stop the service before these
+ // actions are complete. Also, note that SetupAction() does
+ // all necessary event logging & properly updates its argument
+ // even in case of failures.
+ //
+
+ if ( StartupFlags & RPL_SPECIAL_ACTIONS) {
+ DWORD NewStartupFlags = StartupFlags;
+ (VOID)SetupAction( &NewStartupFlags, FALSE); // partial backup if any
+ if ( NewStartupFlags != StartupFlags) {
+ (VOID)RplRegSetPolicy( NewStartupFlags);
+ }
+ }
+
+ //
+ // We started successfully. Wait for somebody to tell us it
+ // is time to die.
+ //
+
+ RplDump( RG_DebugLevel & RPL_DEBUG_SERVICE,(
+ "Successful startup. Wait on TerminateNowEvent."));
+
+#define ONE_HOUR (60 * 60 * 1000L) // in milliseconds
+ for ( ; ; ) {
+ DWORD Status;
+ Status = WaitForSingleObject( RG_TerminateNowEvent,
+ RG_BackupInterval == 0 ? INFINITE : RG_BackupInterval * ONE_HOUR);
+ if ( Status != WAIT_TIMEOUT) {
+ RPL_ASSERT( Status == 0);
+#ifdef NOT_YET
+ //
+ // Backup is disabled here because in the field users will most
+ // likely use CTRL-ALT-DEL to shutdown the system & RPL service.
+ // In that case we do no enter this code path and to keep all
+ // remoteboot shutdown sequences similar backup is disabled here
+ // as well. By not doing backup here we also avoid problems due
+ // to full backup taking too long or incremental backup being
+ // buggy (jet bugs).
+ //
+ //
+ // In case of regular shutdown we do incremental backup so that
+ // user does not get an impression we are "stuck". Also, the
+ // backup is done now & not later in RplDbTerm() just in case
+ // we get in a state where we hang for ever in
+ // ShutdownWorkerParams().
+ //
+ RplBackupDatabase( Status == 0 ? FALSE : TRUE);
+#endif
+ break;
+ }
+ //
+ // Do full backup if not on shutdown path.
+ //
+ RplBackupDatabase( TRUE);
+ }
+
+main_exit:
+
+ //
+ // If we come here, then it is time to die. Wait until all
+ // our children (request threads) have been terminated.
+ // And of course, request threads will in turn wail until all
+ // of their children (worker threads) get terminated.
+ //
+ // Because of a cumbersome inherited Rpld* interface, request
+ // threads may wait for ever on Rpld* events. The only way to get
+ // them out is to close the adapters.
+ //
+ RplCloseAdapters(); // cumbersome Rpld* interface
+
+ ShutdownRequestParams();
+
+ //
+ // Close RPC server. Jet resources must be available at this
+ // point because rundown routines need them.
+ //
+ if ( InitState & RPL_RPC_SERVER_STARTED) {
+ Error = NetpStopRpcServer( rplsvc_ServerIfHandle);
+ if ( Error != NO_ERROR) {
+ RG_Error = Error;
+ RPL_ASSERT( FALSE);
+ }
+ }
+
+ if ( InitState & RPL_SECURITY_OBJECT_CREATED) {
+ RplDeleteSecurityObject();
+ }
+
+
+ RplCleanup(); // nobody alive but us now, clean up and die
+
+#ifdef RPL_DEBUG
+ RplDebugDelete();
+#endif // RPL_DEBUG
+
+ //
+ // Set the service state to uninstalled, and tell the service controller.
+ //
+
+ RG_ServiceStatus.dwCurrentState = SERVICE_STOPPED;
+ RG_ServiceStatus.dwControlsAccepted = 0;
+
+ SET_SERVICE_EXITCODE(
+ RG_Error,
+ RG_ServiceStatus.dwWin32ExitCode,
+ RG_ServiceStatus.dwServiceSpecificExitCode
+ );
+
+ RG_ServiceStatus.dwCheckPoint = 0;
+ RG_ServiceStatus.dwWaitHint = 0;
+
+#ifndef RPL_NO_SERVICE
+ if ( !SetServiceStatus( RG_ServiceStatusHandle, &RG_ServiceStatus)) {
+ RplDump( ++RG_Assert, ( "Error = ", GetLastError()));
+ NOTHING; // ignore this error
+ }
+#endif
+}
+
+
+BOOL RplServiceAttemptStop( VOID)
+{
+ SERVICE_STATUS ServiceStatus;
+ if ( ControlService( RG_ServiceHandle, SERVICE_CONTROL_STOP, &ServiceStatus)) {
+ return( TRUE); // all done
+ }
+ RplDump( ++RG_Assert, ( "Error=%d", GetLastError()));
+ if ( ServiceStatus.dwCurrentState == SERVICE_STOP_PENDING) {
+ //
+ // We arrive here if control handler already received
+ // SERVICE_CONTROL_STOP request from somewhere else.
+ //
+ return( TRUE);
+ }
+ return( FALSE);
+}
+
+
+
+
diff --git a/private/net/svcdlls/rpl/server/rplrest.c b/private/net/svcdlls/rpl/server/rplrest.c
new file mode 100644
index 000000000..f570bb866
--- /dev/null
+++ b/private/net/svcdlls/rpl/server/rplrest.c
@@ -0,0 +1,36 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ rplrest.c
+
+Abstract:
+
+ This file contains program to test RPL JetRestore() call.
+
+Author:
+
+ Vladimiv Z. Vulovic (vladimv) 17-June-1994
+
+Environment:
+
+ User Mode - Win32
+
+Revision History:
+
+--*/
+
+#include "local.h"
+
+#define BACKUP_PATH "d:\\nt689f\\rpl\\backup"
+
+
+DWORD _CRTAPI1 main( int argc, char **argv)
+{
+ JET_ERR JetError;
+ JetError = JetRestore( BACKUP_PATH, 0, NULL, 0);
+ printf( "JetError=%d\n", JetError);
+ return(0);
+}
diff --git a/private/net/svcdlls/rpl/server/rplsvc.c b/private/net/svcdlls/rpl/server/rplsvc.c
new file mode 100644
index 000000000..34a377a11
--- /dev/null
+++ b/private/net/svcdlls/rpl/server/rplsvc.c
@@ -0,0 +1,122 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ rplsvc.c
+
+Abstract:
+
+ This is the main routine for the Remote Program Load Service.
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 10 - Februrary - 1993
+
+Environment:
+
+ User Mode - Win32
+
+Revision History:
+
+ 10-Feb-1993 vladimv
+ created
+
+--*/
+
+#include "local.h"
+#include <rpcutil.h> // NetpInitRpcServer()
+#include <secobj.h> // NetpCreateWellKnownSids()
+
+//
+// Dispatch table for all services. Passed to NetServiceStartCtrlDispatcher.
+//
+// Add new service entries here.
+//
+
+SERVICE_TABLE_ENTRY RPLServiceDispatchTable[] = {
+ { SERVICE_RIPL, RPL_main },
+ { NULL, NULL }
+};
+
+
+
+VOID _CRTAPI1
+main (
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ This is a main routine for the LANMan Remote Program Load Services.
+
+ It basically sets up the ControlDispatcher and, on return, exits from
+ this main thread. The call to NetServiceStartCtrlDispatcher does
+ not return until all services have terminated, and this process can
+ go away.
+
+ It will be up to the ControlDispatcher thread to start/stop/pause/continue
+ any services. If a service is to be started, it will create a thread
+ and then call the main routine of that service.
+
+
+Arguments:
+
+ Anything passed in from the "command line". Currently, NOTHING.
+
+Return Value:
+
+ NONE
+
+Note:
+
+
+--*/
+{
+ NTSTATUS ntstatus;
+
+ //
+ // Create well-known SIDs
+ //
+ if (! NT_SUCCESS (ntstatus = NetpCreateWellKnownSids(NULL))) {
+ KdPrint((
+ "[RplSvc] main: Failed to create well-known SIDs, ntstatus=0x%x\n",
+ ntstatus
+ ));
+ return;
+ }
+
+
+ //
+ // Initialize the RpcServer Locks.
+ //
+
+ NetpInitRpcServer();
+
+ //
+ // Call ServiceStartCtrlDispatcher to set up the control interface.
+ // The API won't return until all services have been terminated. At that
+ // point, we just exit.
+ //
+
+#ifndef RPL_NO_SERVICE
+ if ( !StartServiceCtrlDispatcher ( RPLServiceDispatchTable)) {
+ DWORD error = GetLastError();
+ //
+ // RPL service will eventually go in the same exe with all
+ // other services. Thus, no need here to log anything.
+ //
+ KdPrint((
+ "[RplSvc] main: StartServiceCtrlDispatcher() fails with error=%d\n",
+ error
+ ));
+ }
+#else
+ RPL_main( 1, (LPWSTR *)NULL);
+#endif
+
+ ExitProcess(0);
+}
diff --git a/private/net/svcdlls/rpl/server/rplsvc.rc b/private/net/svcdlls/rpl/server/rplsvc.rc
new file mode 100644
index 000000000..1fffb1423
--- /dev/null
+++ b/private/net/svcdlls/rpl/server/rplsvc.rc
@@ -0,0 +1,12 @@
+#include <windows.h>
+
+#include <ntverp.h>
+
+#define VER_FILETYPE VFT_DLL
+#define VER_FILESUBTYPE VFT2_UNKNOWN
+#define VER_FILEDESCRIPTION_STR "RemoteBoot Service DLL"
+#define VER_INTERNALNAME_STR "RPLSVC.DLL"
+#define VER_ORIGINALFILENAME_STR "RPLSVC.DLL"
+
+#include "common.ver"
+
diff --git a/private/net/svcdlls/rpl/server/security.c b/private/net/svcdlls/rpl/server/security.c
new file mode 100644
index 000000000..2827437d9
--- /dev/null
+++ b/private/net/svcdlls/rpl/server/security.c
@@ -0,0 +1,1005 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ security.c
+
+Abstract:
+
+ Security addons to the tree-manipulation module
+ for the RPL service.
+
+Author:
+
+ Jon Newman (jonn) 16 - February - 1994
+
+Revision History:
+
+ Jon Newman (jonn) 16 - February - 1994
+ Added RplGrant*Perms primitives
+
+ Vladimir Z. Vulovic (vladimv) 06 - May - 1994
+ Security fix: added new api + major reorg.
+
+--*/
+
+
+#include "local.h"
+#include "rpldb.h" // Call(), CallM()
+#include "db.h"
+#include "dblib.h" // RplFilterFirst()
+#include "tree.h"
+#include "treei.h"
+#include "wksta.h" // WKSTA_WkstaName
+#include "profile.h" // PROFILE_ProfileName
+#include <ntseapi.h>
+#include <ntrtl.h> // RtlAllocateAndInitializeSid
+#include <ntlsa.h>
+#include <ntsam.h>
+#include <nturtl.h> // see IsNTFS
+#include <lmserver.h> // NetServerGetInfo
+
+
+typedef enum _RPL_SD_BLOCK_TYPE {
+
+ RPL_SD_BLOCK_TYPE_ADMINONLY = 0,// Only RPLADMIN gets access
+
+ RPL_SD_BLOCK_TYPE_ALLUSERS, // RPLADMIN gets full access, RPLUSER
+ // gets RX access
+
+ RPL_SD_BLOCK_TYPE_ONEUSER // RPLADMIN gets full access, one
+ // wksta account gets RWXCDA access
+} RPL_SD_BLOCK_TYPE;
+
+/*
+ * This is the block of information which is retained between the recursive
+ * calls in a single RplDoTree run. It remembers the SECURITY_DESCRIPTOR
+ * which was assigned to the last file/directory, so if the next
+ * file/directory wants the same permissions (as is usually the case)
+ * we do not have to fetch everything all over again. It also contains
+ * some information about what kind of SECURITY_DESCRIPTOR is currently
+ * stored, plus items needed to get a different SECURITY_DESCRIPTOR.
+ */
+typedef struct _RPL_SD_BLOCK {
+ BOOL SkipThisTree; // must be the first element
+ PSECURITY_DESCRIPTOR psd;
+ PACL paclDacl;
+ BOOL MonitorSecurityType;
+ RPL_SD_BLOCK_TYPE SecurityType;
+ //
+ // CODEWORK do we need to keep these sids around
+ //
+ PSID psidSystem;
+ PSID psidAdministrators;
+ POLICY_ACCOUNT_DOMAIN_INFO * pinfoRPLUSERAccountDomain;
+ SAM_HANDLE hsamRPLUSERAccountDomain;
+ POLICY_ACCOUNT_DOMAIN_INFO * pinfoWkstaAccountDomain;
+ SAM_HANDLE hsamWkstaAccountDomain;
+} RPL_SD_BLOCK, *PRPL_SD_BLOCK;
+
+//
+// The DACL we assign to files has 2 or 3 ACEs.
+//
+#define RPL_INDEX_SYSTEM_ACE 0
+#define RPL_INDEX_ADMINS_ACE 1
+#define RPL_INDEX_CHANGEABLE_ACE 2
+
+
+
+DWORD IsNTFS(
+ IN PWCHAR Resource,
+ OUT BOOL * pfIsNTFS
+ )
+/*++
+
+ NAME: IsNTFS
+
+ SYNOPSIS: This function checks the given file resource and attempts to
+ determine if it is on an NTFS partition.
+
+ ENTRY: Resource - Pointer to file/directory name in the form
+ "X:\aaa\bbb\ccc
+ pfIsNTFS - Pointer to BOOL that will receive the results
+
+ RETURNS: NO_ERROR if successful, error code otherwise
+
+ NOTES:
+
+ HISTORY:
+ JonN 23-Feb-1994 Copied from acledit\ntfsacl.cxx
+
+--*/
+{
+ DWORD Error = NO_ERROR ;
+ HANDLE hDrive = NULL ;
+ WCHAR awchDosDriveName[4];
+ WCHAR awchNtDriveName[40];
+ UNICODE_STRING unistrNtDriveName;
+ BYTE buffFsInfo[ sizeof(FILE_FS_ATTRIBUTE_INFORMATION) + 200 ] ;
+ PFILE_FS_ATTRIBUTE_INFORMATION FsInfo =
+ (PFILE_FS_ATTRIBUTE_INFORMATION)buffFsInfo ;
+ OBJECT_ATTRIBUTES oa ;
+ IO_STATUS_BLOCK StatusBlock ;
+
+ RPL_ASSERT( Resource != NULL
+ && wcslen(Resource) >= 3
+ && pfIsNTFS != NULL ) ;
+
+ *pfIsNTFS = FALSE ;
+
+ //
+ // Determine the NT drive name
+ //
+
+ awchDosDriveName[0] = Resource[0];
+ awchDosDriveName[1] = Resource[1];
+ awchDosDriveName[2] = Resource[2];
+ awchDosDriveName[3] = L'\0';
+ RPL_ASSERT( awchDosDriveName[1] == L':'
+ && awchDosDriveName[2] == L'\\' );
+
+ unistrNtDriveName.Buffer = awchNtDriveName;
+ unistrNtDriveName.Length = sizeof(awchNtDriveName);
+ unistrNtDriveName.MaximumLength = sizeof(awchNtDriveName);
+
+ if (!RtlDosPathNameToNtPathName_U( awchDosDriveName,
+ &unistrNtDriveName,
+ NULL,
+ NULL)){
+ RPL_ASSERT( FALSE ) ;
+ Error = ERROR_NOT_ENOUGH_MEMORY ;
+ goto cleanup;
+ }
+ RPL_ASSERT( unistrNtDriveName.Length < unistrNtDriveName.MaximumLength );
+ unistrNtDriveName.Buffer[unistrNtDriveName.Length/sizeof(WCHAR)] = L'\0';
+
+ //
+ // Open the NT volume and query volume information
+ //
+
+ InitializeObjectAttributes( &oa,
+ &unistrNtDriveName,
+ OBJ_CASE_INSENSITIVE,
+ 0,
+ 0 );
+ Error = NtOpenFile( &hDrive,
+ SYNCHRONIZE | FILE_READ_DATA,
+ &oa,
+ &StatusBlock,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ FILE_SYNCHRONOUS_IO_ALERT);
+ if (Error != NO_ERROR) {
+ RplDump( ++RG_Assert, ( "Error=%d", Error));
+ goto cleanup;
+ }
+ Error = NtQueryVolumeInformationFile(
+ hDrive,
+ &StatusBlock,
+ (PVOID) FsInfo,
+ sizeof(buffFsInfo),
+ FileFsAttributeInformation );
+ if (Error != NO_ERROR) {
+ if (Error == ERROR_ACCESS_DENIED) {
+ RplDump( ++RG_Assert, ( "(access denied) assuming the file system is NTFS"));
+ Error = NO_ERROR ;
+ *pfIsNTFS = TRUE ;
+ } else {
+ RplDump( ++RG_Assert, ( "Error=%d", Error));
+ }
+ goto cleanup;
+ }
+
+ if ( FsInfo->FileSystemAttributes & FILE_PERSISTENT_ACLS){
+ *pfIsNTFS = TRUE ;
+ }
+
+cleanup:
+
+ /* Close the volume if we ever opened it
+ */
+ if ( hDrive != NULL){
+ NtClose( hDrive );
+ }
+
+ return( Error) ;
+}
+
+
+
+DWORD AddInheritableAce(
+ IN OUT ACL * pacl,
+ IN ACCESS_MASK mask,
+ IN SID * psid
+ )
+/*++
+ Add an ACCESS_ALLOWED_ACE to the ACL, with all inheritance flags set
+--*/
+{
+ DWORD Error = NO_ERROR;
+ ACCESS_ALLOWED_ACE * pace = NULL;
+
+ if ( !AddAccessAllowedAce( pacl, ACL_REVISION, mask, psid )) {
+ Error = GetLastError();
+ RplDump( ++RG_Assert, ( "Error=%d", Error));
+ goto cleanup;
+ }
+
+ //
+ // new ACE should be the last ACE
+ //
+ if ( !GetAce( pacl, (pacl->AceCount) - 1, &pace )) {
+ Error = GetLastError();
+ RplDump( ++RG_Assert, ( "Error=%d", Error));
+ goto cleanup;
+ }
+
+ RPL_ASSERT( EqualSid( psid, &(pace->SidStart)) );
+
+ (pace->Header.AceFlags) |= (OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE);
+
+cleanup:
+
+ return( Error);
+}
+
+
+VOID DeInitSdBlock( IN RPL_SD_BLOCK * psdblock)
+{
+ if (psdblock->psd != NULL) {
+ RplMemFree( RG_MemoryHandle, psdblock->psd );
+ }
+ if (psdblock->psidSystem != NULL) {
+ RtlFreeSid( psdblock->psidSystem );
+ }
+ if (psdblock->psidAdministrators != NULL) {
+ RtlFreeSid( psdblock->psidAdministrators );
+ }
+ if ( psdblock->pinfoRPLUSERAccountDomain != NULL) {
+ LsaFreeMemory( psdblock->pinfoRPLUSERAccountDomain);
+ }
+ if (psdblock->hsamRPLUSERAccountDomain != NULL) {
+ SamCloseHandle( psdblock->hsamRPLUSERAccountDomain);
+ }
+ if ( psdblock->pinfoWkstaAccountDomain != NULL
+ && psdblock->pinfoWkstaAccountDomain !=
+ psdblock->pinfoRPLUSERAccountDomain) {
+ LsaFreeMemory( psdblock->pinfoWkstaAccountDomain);
+ }
+ if ( psdblock->hsamWkstaAccountDomain != NULL
+ && psdblock->hsamWkstaAccountDomain !=
+ psdblock->hsamRPLUSERAccountDomain) {
+ SamCloseHandle( psdblock->hsamWkstaAccountDomain);
+ }
+}
+
+
+DWORD GetDomains(
+ OUT SAM_HANDLE * phsamWkstaAccountDomain,
+ OUT POLICY_ACCOUNT_DOMAIN_INFO ** ppinfoWkstaAccountDomain,
+ OUT SAM_HANDLE * phsamRPLUSERAccountDomain,
+ OUT POLICY_ACCOUNT_DOMAIN_INFO ** ppinfoRPLUSERAccountDomain,
+ IN ACCESS_MASK DesiredAccess
+ )
+/*--
+ Get a SAM_HANDLE to the account domain, and other information about
+ the account domain. This routine uses SAM and LSA directly.
+--*/
+{
+ DWORD Error = NO_ERROR;
+ SAM_HANDLE hsamAccountServer = NULL;
+ SAM_HANDLE hsamLocalServer = NULL;
+ LSA_HANDLE hlsaLocal = NULL;
+ LSA_HANDLE hlsaPDC = NULL;
+ OBJECT_ATTRIBUTES oa;
+ SECURITY_QUALITY_OF_SERVICE sqos;
+
+ BOOL fIsPrimaryDC = FALSE;
+ BOOL fIsBackupDC = FALSE;
+ BOOL fIsNotDC = FALSE;
+
+ SERVER_INFO_101 * psi101 = NULL;
+ POLICY_PRIMARY_DOMAIN_INFO * pinfoPrimaryDomain = NULL;
+ WCHAR awchPrimaryDomain[ DNLEN+1 ];
+ WCHAR * pwchPDC = NULL;
+ UNICODE_STRING unistrPDC;
+
+ RPL_ASSERT( phsamWkstaAccountDomain != NULL );
+ RPL_ASSERT( ppinfoWkstaAccountDomain != NULL );
+ RPL_ASSERT( phsamRPLUSERAccountDomain != NULL );
+ RPL_ASSERT( ppinfoRPLUSERAccountDomain != NULL );
+
+ //
+ // Set up object attributes (borrowed from uintlsa.cxx)
+ //
+
+ sqos.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
+ sqos.ImpersonationLevel = SecurityImpersonation;
+ sqos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
+ sqos.EffectiveOnly = FALSE;
+
+ InitializeObjectAttributes( &oa, NULL, 0L, NULL, NULL );
+ oa.SecurityQualityOfService = &sqos;
+
+ //
+ // Determine whether this is a PDC, BDC or other server.
+ //
+
+ Error = NetServerGetInfo( NULL, 101, (LPBYTE *)&psi101 );
+ if ( Error != NERR_Success) {
+ RplDump( ++RG_Assert, ( "Error=%d", Error));
+ goto cleanup;
+ }
+ RPL_ASSERT( psi101 != NULL );
+ fIsPrimaryDC = !!(psi101->sv101_type & SV_TYPE_DOMAIN_CTRL);
+ fIsBackupDC = !!(psi101->sv101_type & SV_TYPE_DOMAIN_BAKCTRL);
+ fIsNotDC = !(psi101->sv101_type & (SV_TYPE_DOMAIN_CTRL | SV_TYPE_DOMAIN_BAKCTRL));
+
+ //
+ // Get a local LSA handle
+ //
+
+ Error = LsaOpenPolicy( NULL,
+ &oa,
+ GENERIC_EXECUTE,
+ &hlsaLocal );
+ if ( Error != NERR_Success) {
+ RplDump( ++RG_Assert, ( "Error=%d", Error));
+ goto cleanup;
+ }
+
+ //
+ //
+ // If this is not a DC, get the name of the primary domain and
+ // determine whether this is a ServerNt/WinNt machine on a workgroup.
+ //
+
+ if (!fIsPrimaryDC) {
+ Error = LsaQueryInformationPolicy( hlsaLocal,
+ PolicyPrimaryDomainInformation,
+ &pinfoPrimaryDomain );
+ if ( Error != NERR_Success) {
+ RplDump( ++RG_Assert, ( "Error=%d", Error));
+ goto cleanup;
+ }
+
+ if (pinfoPrimaryDomain->Sid == NULL) {
+ //
+ // This is a ServerNt/WinNt machine on a workgroup. Pretend
+ // this is a Primary DC from now on.
+ //
+ fIsPrimaryDC = TRUE;
+ fIsBackupDC = fIsNotDC = FALSE;
+ }
+ }
+
+ //
+ // If this is not a PDC, get the name of a primary domain PDC
+ // and reload the LSA handle
+ //
+
+ if (!fIsPrimaryDC) {
+ RPL_ASSERT( DNLEN*sizeof(WCHAR) >= pinfoPrimaryDomain->Name.Length );
+ memcpy( awchPrimaryDomain,
+ pinfoPrimaryDomain->Name.Buffer,
+ pinfoPrimaryDomain->Name.Length ); // this is in bytes
+ awchPrimaryDomain[ pinfoPrimaryDomain->Name.Length / sizeof(WCHAR) ]
+ = L'\0';
+
+ Error = NetGetDCName( NULL,
+ awchPrimaryDomain,
+ (LPBYTE *)&pwchPDC );
+ if ( Error != NERR_Success) {
+ RplDump( ++RG_Assert, ( "Error=%d", Error));
+ goto cleanup;
+ }
+
+ unistrPDC.Buffer = pwchPDC;
+ unistrPDC.Length = wcslen( pwchPDC ) * sizeof(WCHAR);
+ unistrPDC.MaximumLength = unistrPDC.Length + sizeof(WCHAR);
+
+ Error = LsaOpenPolicy( &unistrPDC,
+ &oa,
+ GENERIC_EXECUTE,
+ &hlsaPDC );
+ if ( Error != NERR_Success) {
+ RplDump( ++RG_Assert, ( "Error=%d", Error));
+ goto cleanup;
+ }
+ }
+
+ //
+ // Load information on the RPLUSERAccount domain
+ //
+
+ Error = LsaQueryInformationPolicy( (fIsBackupDC) ? hlsaPDC : hlsaLocal,
+ PolicyAccountDomainInformation,
+ ppinfoRPLUSERAccountDomain );
+ if ( Error != NERR_Success) {
+ RplDump( ++RG_Assert, ( "Error=%d", Error));
+ goto cleanup;
+ }
+
+ //
+ // Load a SAM handle on the RPLUSERAccount domain
+ //
+
+ Error = SamConnect( (fIsBackupDC) ? &unistrPDC : NULL,
+ &hsamLocalServer,
+ SAM_SERVER_LOOKUP_DOMAIN,
+ NULL );
+ if ( Error != NERR_Success) {
+ RplDump( ++RG_Assert, ( "Error=%d", Error));
+ goto cleanup;
+ }
+ Error = SamOpenDomain( hsamLocalServer,
+ DesiredAccess,
+ (*ppinfoRPLUSERAccountDomain)->DomainSid,
+ phsamRPLUSERAccountDomain );
+ if ( Error != NERR_Success) {
+ RplDump( ++RG_Assert, ( "Error=%d", Error));
+ goto cleanup;
+ }
+
+ //
+ // If this is not a DC, load information and a SAM handle on
+ // the RPLUSERAccount domain
+ //
+
+ if (!fIsNotDC) {
+ *phsamWkstaAccountDomain = *phsamRPLUSERAccountDomain;
+ *ppinfoWkstaAccountDomain = *ppinfoRPLUSERAccountDomain;
+ } else {
+ Error = LsaQueryInformationPolicy( hlsaPDC,
+ PolicyAccountDomainInformation,
+ ppinfoWkstaAccountDomain );
+ if ( Error != NERR_Success) {
+ RplDump( ++RG_Assert, ( "Error=%d", Error));
+ goto cleanup;
+ }
+
+ //
+ // Get server handle
+ //
+
+ Error = SamConnect( &unistrPDC,
+ &hsamAccountServer,
+ SAM_SERVER_LOOKUP_DOMAIN,
+ NULL );
+ if ( Error != NERR_Success) {
+ RplDump( ++RG_Assert, ( "Error=%d", Error));
+ goto cleanup;
+ }
+ Error = SamOpenDomain( hsamAccountServer,
+ DesiredAccess,
+ (*ppinfoWkstaAccountDomain)->DomainSid,
+ phsamWkstaAccountDomain );
+ if ( Error != NERR_Success) {
+ RplDump( ++RG_Assert, ( "Error=%d", Error));
+ goto cleanup;
+ }
+ }
+
+
+cleanup:
+
+ if ( hsamLocalServer != NULL) {
+ SamCloseHandle( hsamLocalServer);
+ }
+
+ if ( hsamAccountServer != NULL) {
+ SamCloseHandle( hsamAccountServer);
+ }
+
+ if ( hlsaLocal != NULL) {
+ LsaClose( hlsaLocal);
+ }
+
+ if ( hlsaPDC != NULL) {
+ LsaClose( hlsaPDC);
+ }
+
+ if ( pinfoPrimaryDomain != NULL) {
+ LsaFreeMemory( pinfoPrimaryDomain);
+ pinfoPrimaryDomain = NULL;
+ }
+
+ if ( pwchPDC != NULL ) {
+ NetApiBufferFree( (LPBYTE)pwchPDC );
+ pwchPDC = NULL;
+ }
+
+ if ( psi101 != NULL ) {
+ NetApiBufferFree( (LPBYTE)psi101 );
+ psi101 = NULL;
+ }
+
+ if ( Error != NO_ERROR
+ && (*ppinfoWkstaAccountDomain) != NULL
+ && (*ppinfoWkstaAccountDomain) != (*ppinfoRPLUSERAccountDomain) ) {
+ LsaFreeMemory( (*ppinfoWkstaAccountDomain));
+ *ppinfoWkstaAccountDomain = NULL;
+ }
+
+ if ( Error != NO_ERROR
+ && (*phsamWkstaAccountDomain) != NULL
+ && (*phsamWkstaAccountDomain) != (*phsamRPLUSERAccountDomain) ) {
+ SamCloseHandle( *phsamWkstaAccountDomain);
+ *phsamWkstaAccountDomain = NULL;
+ }
+
+ if ( Error != NO_ERROR && (*ppinfoRPLUSERAccountDomain) != NULL) {
+ LsaFreeMemory( (*ppinfoRPLUSERAccountDomain));
+ *ppinfoRPLUSERAccountDomain = NULL;
+ }
+
+ if ( Error != NO_ERROR && (*phsamRPLUSERAccountDomain) != NULL) {
+ SamCloseHandle( *phsamRPLUSERAccountDomain);
+ *phsamRPLUSERAccountDomain = NULL;
+ }
+
+ return( Error);
+}
+
+
+DWORD MyBuildSid(
+ OUT PSID * ppsidAccountSid,
+ IN POLICY_ACCOUNT_DOMAIN_INFO * pinfoAccountDomain,
+ IN DWORD ulRid
+ )
+{
+ DWORD Error = 0;
+ DWORD cbNewSid = 0;
+ PUCHAR pcSubAuthorityCount = NULL;
+ PDWORD pdwLastSubAuthority = NULL;
+
+ RPL_ASSERT( ppsidAccountSid != NULL && pinfoAccountDomain != NULL && ulRid != 0 );
+
+ cbNewSid = GetLengthSid( pinfoAccountDomain->DomainSid) + sizeof(DWORD);
+ *ppsidAccountSid = RplMemAlloc( RG_MemoryHandle, cbNewSid );
+ if ( *ppsidAccountSid == NULL) {
+ Error = ERROR_NOT_ENOUGH_MEMORY;
+ goto cleanup;
+ }
+
+ if ( !CopySid( cbNewSid, *ppsidAccountSid, pinfoAccountDomain->DomainSid)) {
+ Error = GetLastError();
+ goto cleanup;
+ }
+
+ pcSubAuthorityCount = GetSidSubAuthorityCount( *ppsidAccountSid );
+ RPL_ASSERT( pcSubAuthorityCount != NULL );
+ (*pcSubAuthorityCount)++;
+ pdwLastSubAuthority = GetSidSubAuthority(
+ *ppsidAccountSid, (DWORD)((*pcSubAuthorityCount)-1) );
+ RPL_ASSERT( pdwLastSubAuthority != NULL );
+ *pdwLastSubAuthority = ulRid;
+
+cleanup:
+
+ if ( Error != NO_ERROR) {
+ if ( *ppsidAccountSid != NULL) {
+ RplMemFree( RG_MemoryHandle, *ppsidAccountSid);
+ }
+ }
+
+ return( Error);
+}
+
+
+SID_IDENTIFIER_AUTHORITY IDAuthorityNT = SECURITY_NT_AUTHORITY;
+
+DWORD InitSdBlock( OUT RPL_SD_BLOCK * psdblock)
+/*++
+ Initialize SdBlock.
+--*/
+{
+ DWORD Error;
+ DWORD cbNewDacl;
+
+ memset( psdblock, 0, sizeof(*psdblock));
+
+ //
+ // Get System SID
+ //
+ Error = RtlAllocateAndInitializeSid( &IDAuthorityNT, 1,
+ SECURITY_LOCAL_SYSTEM_RID, 0, 0, 0, 0, 0, 0, 0,
+ &(psdblock->psidSystem));
+ if (Error != NO_ERROR) {
+ return( Error);
+ }
+
+ //
+ // Get Administrators SID
+ //
+ Error = RtlAllocateAndInitializeSid(
+ &IDAuthorityNT,
+ 2,
+ SECURITY_BUILTIN_DOMAIN_RID,
+ DOMAIN_ALIAS_RID_ADMINS,
+ 0, 0, 0, 0, 0, 0,
+ &(psdblock->psidAdministrators)
+ );
+ if (Error != NO_ERROR) {
+ return( Error);
+ }
+
+ //
+ // Get information about local and (if not PDC) remote domain
+ //
+ Error = GetDomains( &(psdblock->hsamWkstaAccountDomain),
+ &(psdblock->pinfoWkstaAccountDomain),
+ &(psdblock->hsamRPLUSERAccountDomain),
+ &(psdblock->pinfoRPLUSERAccountDomain),
+ GENERIC_EXECUTE );
+ if ( Error != NO_ERROR) {
+ RplDump( ++RG_Assert, ( "Error=%d", Error));
+ return( Error);
+ }
+
+ //
+ // Start assembling DACL
+ //
+ cbNewDacl = sizeof(ACL) + (3*sizeof(ACCESS_ALLOWED_ACE))
+ + (3*GetSidLengthRequired(SID_MAX_SUB_AUTHORITIES))
+ - sizeof(DWORD);
+ psdblock->paclDacl = RplMemAlloc( RG_MemoryHandle, cbNewDacl);
+ if ( psdblock->paclDacl == NULL) {
+ Error = ERROR_NOT_ENOUGH_MEMORY;
+ return( Error);
+ }
+ if ( !InitializeAcl( psdblock->paclDacl, cbNewDacl, ACL_REVISION)) {
+ Error = GetLastError();
+ RplDump( ++RG_Assert, ( "Error=%d", Error));
+ return( Error);
+ }
+
+ //
+ // Add System ACE
+ //
+ Error = AddInheritableAce( psdblock->paclDacl, GENERIC_ALL, psdblock->psidSystem);
+ if (Error != NO_ERROR) {
+ RplDump( ++RG_Assert, ( "Error=%d", Error));
+ return( Error);
+ }
+
+ //
+ // Add Administrators ACE
+ //
+ Error = AddInheritableAce( psdblock->paclDacl, GENERIC_ALL, psdblock->psidAdministrators);
+ if (Error != NO_ERROR) {
+ RplDump( ++RG_Assert, ( "Error=%d", Error));
+ return( Error);
+ }
+
+ //
+ // Build security descriptor
+ //
+ psdblock->psd = RplMemAlloc( RG_MemoryHandle, sizeof(SECURITY_DESCRIPTOR) );
+ if ( psdblock->psd == NULL) {
+ Error = ERROR_NOT_ENOUGH_MEMORY;
+ return( Error);
+ }
+ if ( !InitializeSecurityDescriptor( psdblock->psd,
+ SECURITY_DESCRIPTOR_REVISION )) {
+ Error = GetLastError();
+ RplDump( ++RG_Assert, ( "Error=%d", Error));
+ return( Error);
+ }
+
+ if ( !SetSecurityDescriptorDacl( psdblock->psd, TRUE, psdblock->paclDacl, FALSE)) {
+ Error = GetLastError();
+ RplDump( ++RG_Assert, ( "Error=%d", Error));
+ return( Error);
+ }
+
+ //
+ // Set SecurityType in RPL_SD_BLOCK
+ //
+ psdblock->MonitorSecurityType = TRUE;
+ psdblock->SecurityType = RPL_SD_BLOCK_TYPE_ADMINONLY;
+ return( NO_ERROR);
+}
+
+
+DWORD UpdateSdBlock(
+ IN OUT RPL_SD_BLOCK * psdblock,
+ IN RPL_SD_BLOCK_TYPE SecurityType,
+ IN DWORD GrantToRid,
+ IN BOOL MonitorSecurityType
+ )
+/*--
+ Updates the changeable ace in the security descriptor
+ to reflect GrantToRid.
+
+ MonitorSecurityType - do we need to monitor (keep evaluating) security type
+ while working on this subtree
+--*/
+{
+ DWORD Error = NO_ERROR;
+ PSID pGrantToSid = NULL;
+
+ //
+ // Delete the changeable ACE if present
+ //
+ if ( psdblock->SecurityType == RPL_SD_BLOCK_TYPE_ONEUSER
+ || psdblock->SecurityType == RPL_SD_BLOCK_TYPE_ALLUSERS) {
+ if ( !DeleteAce( psdblock->paclDacl, RPL_INDEX_CHANGEABLE_ACE)) {
+ Error = GetLastError();
+ RplDump( ++RG_Assert, ( "Error=%d", Error));
+ goto cleanup;
+ }
+ psdblock->SecurityType = RPL_SD_BLOCK_TYPE_ADMINONLY;
+ }
+
+ //
+ // Rewrite the changeable ACE if needed
+ //
+ if ( GrantToRid != 0) {
+ Error = MyBuildSid( &pGrantToSid,
+ (SecurityType == RPL_SD_BLOCK_TYPE_ALLUSERS)
+ ? psdblock->pinfoRPLUSERAccountDomain
+ : psdblock->pinfoWkstaAccountDomain,
+ GrantToRid);
+ if ( Error != NO_ERROR) {
+ RplDump( ++RG_Assert, ( "Error=%d", Error));
+ goto cleanup;
+ }
+ switch( SecurityType) {
+ case RPL_SD_BLOCK_TYPE_ALLUSERS:
+ Error = AddInheritableAce( psdblock->paclDacl,
+ GENERIC_READ | GENERIC_EXECUTE,
+ pGrantToSid);
+ break;
+ case RPL_SD_BLOCK_TYPE_ONEUSER:
+ Error = AddInheritableAce( psdblock->paclDacl,
+ GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE | DELETE,
+ pGrantToSid);
+ break;
+ default:
+ RPL_ASSERT( FALSE);
+ Error = NERR_RplInternal;
+ break;
+ }
+ if ( Error != NO_ERROR) {
+ RplDump( ++RG_Assert, ( "Error=%d", Error));
+ goto cleanup;
+ }
+ psdblock->SecurityType = SecurityType;
+ }
+
+cleanup:
+ if ( Error == NO_ERROR) {
+ psdblock->MonitorSecurityType = MonitorSecurityType;
+ }
+ if ( pGrantToSid != NULL) {
+ RplMemFree( RG_MemoryHandle, pGrantToSid);
+ }
+ return( Error);
+}
+
+
+
+RPL_SD_BLOCK_TYPE SecurityType( IN PWCHAR File)
+/*++
+ Based on a position of File in %RplRoot% tree, determines what
+ SECURITY_DESCRIPTOR type is appropriate for this file.
+--*/
+{
+#define WCSLEN( _x_) ( (DWORD) (sizeof(_x_)/sizeof(WCHAR) - 1) )
+#define SEC_RPLFILES L"RPLFILES\\"
+#define SEC_BINFILES L"BINFILES"
+#define SEC_PROFILES L"PROFILES"
+#define SEC_MACHINES L"MACHINES\\"
+#define SEC_TMPFILES L"TMPFILES\\"
+
+ RPL_ASSERT( _wcsnicmp( File, RG_Directory, RG_DirectoryLength) == 0);
+
+ File += RG_DirectoryLength;
+
+ //
+ // If file is not in rplfiles, then it should be accessible to
+ // admins only.
+ //
+ if ( _wcsnicmp( File, SEC_RPLFILES, WCSLEN( SEC_RPLFILES)) != 0) {
+ return( RPL_SD_BLOCK_TYPE_ADMINONLY);
+ }
+ File += WCSLEN( SEC_RPLFILES);
+
+ //
+ // If file is in rplfiles\binfiles or rplfiles\profiles, then it
+ // should be accessible to admins & RPLUSER group.
+ //
+
+ if ( _wcsnicmp( File, SEC_BINFILES, WCSLEN( SEC_BINFILES)) == 0 ||
+ _wcsnicmp( File, SEC_PROFILES, WCSLEN( SEC_PROFILES)) == 0) {
+ return( RPL_SD_BLOCK_TYPE_ALLUSERS);
+ }
+
+ //
+ // If file is in rplfiles\machines\* or rplfiles\tmpfiles\*,
+ // then it should be accessible to admins & a particular workstation
+ // account.
+ //
+ if ( _wcsnicmp( File, SEC_MACHINES, WCSLEN( SEC_MACHINES)) == 0 ||
+ _wcsnicmp( File, SEC_TMPFILES, WCSLEN( SEC_TMPFILES)) == 0) {
+ return( RPL_SD_BLOCK_TYPE_ONEUSER);
+ }
+
+ //
+ // Return the default value. Files in rplfiles\configs will take
+ // this code path.
+ //
+ return( RPL_SD_BLOCK_TYPE_ADMINONLY);
+}
+
+
+DWORD SetRplPerms(
+ IN PWCHAR File,
+ IN OUT PBOOL pAuxiliaryBlock
+ )
+/*--
+ This is the callback routine called by RplDoTree to set permissions
+ on a specific file/directory.
+
+ File Path to file or directory.
+ pAuxiliaryBlock Pointer to RPL_SD_BLOCK the first element
+ of which must be boolean SkipThisTree
+
+--*/
+{
+ PRPL_SD_BLOCK psdblock = (PRPL_SD_BLOCK)pAuxiliaryBlock;
+
+ psdblock->SkipThisTree = FALSE; // by default we do not skip
+
+ if ( psdblock->MonitorSecurityType) {
+ //
+ // We skip subtrees that require different security information.
+ //
+ if ( SecurityType( File) != psdblock->SecurityType) {
+ psdblock->SkipThisTree = TRUE;
+ return( NO_ERROR);
+ }
+ }
+
+ //
+ // Set the actual file security. Note that this call will succeed
+ // even if the volume is not NTFS, thus we must check for
+ // partition type elsewhere.
+ //
+ if ( !SetFileSecurity( File, DACL_SECURITY_INFORMATION, psdblock->psd)) {
+ DWORD Error = GetLastError();
+ RplDump( ++RG_Assert, ( "Error=%d", Error));
+ return( Error);
+ }
+
+ return( NO_ERROR);
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetrRplSetSecurity(
+ IN RPL_RPC_HANDLE ServerHandle,
+ IN LPWSTR WkstaName,
+ IN DWORD WkstaRid,
+ IN DWORD RplUserRid
+ )
+/*++
+ There are 3 types of permissions: ADMINONLY, RPLUSER & ONEUSER.
+ For each type we first construct the appropriate security
+ description then use it.
+--*/
+{
+ DWORD Error;
+ WCHAR Directory[ MAX_PATH];
+ BOOL IsNtfs;
+ RPL_SD_BLOCK sdblock;
+
+ //
+ // Make working copy of RG_Directory, then verify it is NTFS.
+ // If it is not NTFS then there is nothing for us to do here.
+ //
+
+ wcscpy( Directory, RG_Directory);
+ Error = IsNTFS( Directory, &IsNtfs);
+ if (Error != NO_ERROR) {
+ RplDump( ++RG_Assert, ( "Error=%d", Error));
+ return( Error);
+ } else if (!IsNtfs) {
+ return( NO_ERROR);
+ }
+
+ //
+ // Initialize to ADMINONLY permissions only.
+ //
+ Error = InitSdBlock( &sdblock);
+ if ( Error != NO_ERROR) {
+ RplDump( ++RG_Assert, ( "Error=%d", Error));
+ goto cleanup;
+ }
+
+ //
+ // If RplUserRid is given, set both ADMINONLY & RPLUSER permissions
+ // (different permissions on different trees).
+ //
+ if ( RplUserRid != 0) {
+ //
+ // Set ADMINONLY permissions.
+ //
+ Error = RplDoTree( Directory, NULL, RPL_TREE_AUXILIARY,
+ SetRplPerms, (PVOID)&sdblock);
+ if ( Error != NO_ERROR) {
+ RplDump( ++RG_Assert, ( "Error=%d", Error));
+ goto cleanup;
+ }
+ //
+ // Initialize to RPLUSER permissions, then set them.
+ //
+ Error = UpdateSdBlock( &sdblock, RPL_SD_BLOCK_TYPE_ALLUSERS, RplUserRid, FALSE);
+ if ( Error != NO_ERROR) {
+ RplDump( ++RG_Assert, ( "Error=%d", Error));
+ goto cleanup;
+ }
+ wcscpy( Directory, RG_Directory);
+ wcscat( Directory, L"RPLFILES\\BINFILES");
+ Error = RplDoTree( Directory, NULL, RPL_TREE_AUXILIARY,
+ SetRplPerms, (PVOID)&sdblock);
+ if ( Error != NO_ERROR) {
+ RplDump( ++RG_Assert, ( "Error=%d", Error));
+ goto cleanup;
+ }
+ wcscpy( Directory, RG_Directory);
+ wcscat( Directory, L"RPLFILES\\PROFILES");
+ Error = RplDoTree( Directory, NULL, RPL_TREE_AUXILIARY,
+ SetRplPerms, (PVOID)&sdblock);
+ if ( Error != NO_ERROR) {
+ RplDump( ++RG_Assert, ( "Error=%d", Error));
+ goto cleanup;
+ }
+ }
+
+ //
+ // If WkstaRid is given, initilize then set ONEUSER permissions.
+ //
+ if ( WkstaRid != 0) {
+ if ( !ValidName( WkstaName, RPL_MAX_WKSTA_NAME_LENGTH, TRUE)) {
+ return( ERROR_INVALID_PARAMETER);
+ }
+ Error = UpdateSdBlock( &sdblock, RPL_SD_BLOCK_TYPE_ONEUSER, WkstaRid, FALSE);
+ if ( Error != NO_ERROR) {
+ RplDump( ++RG_Assert, ( "Error=%d", Error));
+ goto cleanup;
+ }
+ wcscpy( Directory, RG_Directory);
+ wcscat( Directory, L"RPLFILES\\MACHINES\\");
+ wcscat( Directory, WkstaName);
+ Error = RplDoTree( Directory, NULL, RPL_TREE_AUXILIARY,
+ SetRplPerms, (PVOID)&sdblock);
+ if ( Error != NO_ERROR) {
+ RplDump( ++RG_Assert, ( "Error=%d", Error));
+ goto cleanup;
+ }
+ wcscpy( Directory, RG_Directory);
+ wcscat( Directory, L"RPLFILES\\TMPFILES\\");
+ wcscat( Directory, WkstaName);
+ Error = RplDoTree( Directory, NULL, RPL_TREE_AUXILIARY,
+ SetRplPerms, (PVOID)&sdblock);
+ if ( Error != NO_ERROR) {
+ RplDump( ++RG_Assert, ( "Error=%d", Error));
+ goto cleanup;
+ }
+ }
+
+cleanup:
+ DeInitSdBlock( &sdblock);
+ if (Error != NO_ERROR) {
+ //
+ // CODEWORK Need a more appropriate event log ?!
+ //
+ RplReportEvent( NELOG_RplCheckSecurity, NULL, sizeof(DWORD), &Error);
+ }
+ return( Error);
+}
+
+
diff --git a/private/net/svcdlls/rpl/server/service.c b/private/net/svcdlls/rpl/server/service.c
new file mode 100644
index 000000000..d18e0778b
--- /dev/null
+++ b/private/net/svcdlls/rpl/server/service.c
@@ -0,0 +1,207 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ service.c
+
+Abstract:
+
+ This module contains RPL service apis: Open, Close, GetInfo & SetInfo.
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 05 - November - 1993
+
+Revision History:
+
+ 05-Nov-1993 vladimv
+ Created
+
+--*/
+
+#include "local.h"
+#include "apisec.h"
+#include <secobj.h> // NetpAccessCheckAndAuditAlarm()
+#include "db.h" // ResumePrune()
+#include "rplsvc_s.h" // RPL_RPC_HANDLE_rundown()
+#include "setup.h"
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetrRplOpen(
+ IN PWCHAR ServerName,
+ OUT LPRPL_RPC_HANDLE pServerHandle
+ )
+/*++
+
+Routine Description:
+
+Arguments:
+
+ pServerHandle - ptr to RPL_HANDLE
+
+Return Value:
+
+ NO_ERROR if success.
+
+--*/
+{
+ //
+ // ServerName got us to the server side and is uninteresting
+ // once we get here.
+ //
+ UNREFERENCED_PARAMETER( ServerName);
+
+#ifndef RPL_NO_SERVICE
+ //
+ // Perform access validation on the caller. All other
+ // operations are handle based, thus we can rely on RPC
+ // to prohibit unathorized callers.
+ //
+ if ( NetpAccessCheckAndAudit(
+ SERVICE_RIPL, // Subsystem name
+ SECURITY_OBJECT, // Object type name
+ RG_SecurityDescriptor, // Security descriptor
+ RPL_RECORD_ALL_ACCESS, // Desired access
+ &RG_SecurityMapping // Generic mapping
+ ) != NO_ERROR) {
+ return ERROR_ACCESS_DENIED;
+ }
+#endif
+
+ EnterCriticalSection( &RG_ProtectServerHandle);
+ *(PDWORD)pServerHandle = ++RG_ServerHandle;
+ LeaveCriticalSection( &RG_ProtectServerHandle);
+ return( NO_ERROR);
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetrRplClose(
+ IN OUT LPRPL_RPC_HANDLE pServerHandle
+ )
+/*++
+
+Routine Description:
+
+Arguments:
+
+ pServerHandle - ptr to RPL_HANDLE
+
+Return Value:
+
+ NO_ERROR if success.
+
+--*/
+{
+ PRPL_SESSION pSession = &RG_ApiSession;
+
+ EnterCriticalSection( &RG_ProtectDatabase);
+ ResumePrune( pSession, *(PDWORD)pServerHandle);
+ LeaveCriticalSection( &RG_ProtectDatabase);
+
+ *(PDWORD)pServerHandle = 0; // let rpc know that we are done
+ return( ERROR_SUCCESS);
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetrRplGetInfo(
+ IN RPL_RPC_HANDLE ServerHandle,
+ IN DWORD Level,
+ OUT LPRPL_INFO_STRUCT InfoStruct
+ )
+{
+ LPBYTE Buffer;
+
+ switch( Level) {
+ case 0:
+ Buffer = MIDL_user_allocate( sizeof( RPL_INFO_0));
+ if ( Buffer == NULL) {
+ return( ERROR_NOT_ENOUGH_MEMORY);
+ }
+ memset( Buffer, 0, sizeof( RPL_INFO_0));
+ InfoStruct->RplInfo0 = (LPRPL_INFO_0)Buffer;
+ InfoStruct->RplInfo0->Flags = 0;
+ break;
+ case 1:
+ Buffer = MIDL_user_allocate( sizeof( RPL_INFO_1));
+ if ( Buffer == NULL) {
+ return( ERROR_NOT_ENOUGH_MEMORY);
+ }
+ memset( Buffer, 0, sizeof( RPL_INFO_1));
+ InfoStruct->RplInfo1 = (LPRPL_INFO_1)Buffer;
+ InfoStruct->RplInfo1->AdapterPolicy = 0; // fix i_lmrpl.h
+ break;
+ default:
+ return( ERROR_INVALID_LEVEL);
+ break;
+ }
+ return( NO_ERROR);
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetrRplSetInfo(
+ IN RPL_RPC_HANDLE ServerHandle,
+ IN DWORD Level,
+ IN LPRPL_INFO_STRUCT InfoStruct,
+ IN OUT LPDWORD ErrorParameter
+ )
+{
+ DWORD Flags;
+ DWORD Error;
+
+ switch( Level) {
+ case 0:
+ Flags = InfoStruct->RplInfo0->Flags;
+ break;
+ case 1:
+ Flags = InfoStruct->RplInfo1->AdapterPolicy; // fix i_lmrpl.h
+ break;
+ default:
+ return( ERROR_INVALID_LEVEL);
+ break;
+ }
+
+ if ( Flags & ~RPL_SPECIAL_ACTIONS) {
+ return( ERROR_INVALID_PARAMETER);
+ }
+#ifdef RPL_NO_SERVICE
+ if ( Flags == 0) {
+ RplControlHandler( SERVICE_CONTROL_STOP);
+ }
+#endif
+ Error = SetupAction( &Flags, TRUE); // full backup if any
+ return( Error);
+}
+
+
+VOID
+RPL_RPC_HANDLE_rundown(
+ IN RPL_RPC_HANDLE ServerHandle
+ )
+
+/*++
+
+Routine Description:
+
+ This function is called by RPC when a connection is broken that had
+ an outstanding context handle. The value of the context handle is
+ passed in here so that we have an opportunity to clean up.
+
+Arguments:
+
+ ServerHandle - This is the handle value of the context handle that is broken.
+
+Return Value:
+
+ none.
+
+--*/
+{
+ NetrRplClose( &ServerHandle); // close the handle
+}
+
diff --git a/private/net/svcdlls/rpl/server/setup.c b/private/net/svcdlls/rpl/server/setup.c
new file mode 100644
index 000000000..36ee21738
--- /dev/null
+++ b/private/net/svcdlls/rpl/server/setup.c
@@ -0,0 +1,716 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ setup.c
+
+Abstract:
+
+ Setup primitives
+
+Author:
+
+ Jon Newman (jonn) 01-April-1994
+
+Revision History:
+
+ Jon Newman (jonn) 01 - April - 1994
+ Added RplPolicy primitives
+
+--*/
+
+
+#include "local.h"
+#include "rpldb.h" // Call(), CallM()
+#include "db.h" // ConfigGetField()
+#include "dblib.h" // RplFilterFirst()
+#include "wksta.h" // WKSTA_WkstaName
+#include "profile.h" // PROFILE_ProfileName
+#include "setup.h"
+#include "config.h"
+#include "lmwksta.h" // NetWkstaGetInfo
+
+#define RPLDISK_SYS L"BBLOCK\\RPLDISK.SYS"
+#define RPLDISK_OLD L"BBLOCK\\RPLDISK.OLD"
+#define RPLDISK_NEW L"RPLDISK.NEW"
+
+
+
+BOOL FileExists( const WCHAR * FileToFind, DWORD * ErrorPtr)
+/*++
+
+Routine Description:
+
+ This function checks whether the file exists.
+
+Arguments:
+
+
+Return Value:
+ TRUE iff file exists
+
+--*/
+{
+ BOOL BoolFileFound = FALSE;
+ DWORD TempError;
+
+ if ( ErrorPtr == NULL )
+ ErrorPtr = &TempError;
+
+ *ErrorPtr = NO_ERROR;
+
+ if (0xFFFFFFFF == GetFileAttributes( FileToFind )) {
+ *ErrorPtr = GetLastError();
+ switch (*ErrorPtr) {
+ case ERROR_FILE_NOT_FOUND:
+ case ERROR_PATH_NOT_FOUND:
+ break;
+ case NO_ERROR:
+ default:
+ RplDump( ++RG_Assert, ( "*ErrorPtr=%d", *ErrorPtr));
+ break;
+ }
+ } else {
+ BoolFileFound = TRUE;
+ }
+
+ return( BoolFileFound);
+}
+
+
+DWORD RplReplaceRPLDISK( VOID)
+/*++
+
+Routine Description:
+
+ This function checks whether the file RPLDISK.SYS has
+ been replaced with the newer version, and if not,
+ replaces it.
+
+Arguments:
+
+Return Value:
+
+--*/
+{
+ DWORD Error = NO_ERROR;
+ WCHAR RpldiskBuffer[ MAX_PATH ];
+ WCHAR SourceBuffer[ MAX_PATH ];
+ WCHAR BackupBuffer[ MAX_PATH ];
+
+ RPL_ASSERT( RG_DirectoryLength == wcslen(RG_Directory)
+ && RG_DirectoryLength != 0
+ && (RG_DirectoryLength + wcslen(RPLDISK_OLD)) < MAX_PATH );
+
+ wcscpy( RpldiskBuffer, RG_Directory );
+ wcscat( RpldiskBuffer, RPLDISK_SYS );
+
+ wcscpy( SourceBuffer, RG_Directory );
+ wcscat( SourceBuffer, RPLDISK_NEW );
+
+ //
+ // Make sure source file exists
+ //
+
+ if (!FileExists(SourceBuffer, &Error)) {
+ RplDump( ++RG_Assert, ( "Error=%d", Error));
+ goto cleanup;
+ }
+
+ wcscpy( BackupBuffer, RG_Directory );
+ wcscat( BackupBuffer, RPLDISK_OLD );
+
+ //
+ // Copy existing RPLDISK.SYS to RPLDISK.OLD, on a best-effort basis
+ //
+ (void) CopyFile( RpldiskBuffer, BackupBuffer, FALSE );
+
+ //
+ // Copy the new RPLDISK.SYS
+ //
+ if (!CopyFile( SourceBuffer, RpldiskBuffer, FALSE )) {
+ Error = GetLastError();
+ RplDump( ++RG_Assert, ( "Error=%d", Error));
+ goto cleanup;
+ }
+
+cleanup:
+
+ if (Error != NO_ERROR) {
+ RplReportEvent( NELOG_RplReplaceRPLDISK, NULL, sizeof(DWORD), &Error );
+ }
+
+ return( Error);
+}
+
+#define LANMANINI_BINFILES L"RPLFILES\\BINFILES\\LANMAN.DOS\\LANMAN.INI"
+#define LANMANINI_CONFIGSDOS L"RPLFILES\\CONFIGS\\DOS\\LANMAN.DOS\\LANMAN.INI"
+#define LANMANINI_PROFILES L"RPLFILES\\PROFILES\\%ws\\LANMAN.DOS\\LANMAN.INI"
+#define LANMANINI_WKSTAS L"RPLFILES\\MACHINES\\%ws\\%ws\\PRO\\LANMAN.DOS\\LANMAN.INI"
+#define LANMANINI_SECTION L"WORKSTATION"
+#define LANMANINI_KEY L"DOMAIN"
+
+
+DWORD RplProcessLanmanInis( VOID )
+/*++
+
+Routine Description:
+
+ This function changes the "domain =" line in every LANMAN.INI
+
+Arguments:
+
+Return Value:
+ error code
+
+--*/
+{
+ DWORD Error;
+ WCHAR achFile[ MAX_PATH ];
+ WKSTA_INFO_100 * pw100 = NULL;
+ WCHAR * pwszDomainName = NULL;
+ PRPL_SESSION pSession = &RG_ApiSession;
+ BOOL InCritSec = FALSE;
+ BOOL InEnumeration = FALSE;
+ INT SpaceLeft;
+ BOOL TableEnd = TRUE;
+ WCHAR * EnumProfileName = NULL;
+ WCHAR * EnumWkstaName = NULL;
+ DWORD EnumWkstaFlags = 0;
+
+ RPL_ASSERT( wcslen(RG_Directory) == RG_DirectoryLength
+ && RG_DirectoryLength+wcslen(LANMANINI_CONFIGSDOS) < MAX_PATH
+ && RG_DirectoryLength+wcslen(LANMANINI_BINFILES ) < MAX_PATH );
+
+ //
+ // get domain name
+ //
+ // CODEWORK OK for workgroup? Non-issue since we should be on NTAS
+ //
+ Error = NetWkstaGetInfo( NULL, 100, (LPBYTE *)&pw100);
+ if (Error != NO_ERROR) {
+ RplDump( ++RG_Assert, ( "Error=%d", Error));
+ goto cleanup;
+ }
+ RPL_ASSERT( pw100 != NULL );
+ pwszDomainName = pw100->wki100_langroup;
+ RPL_ASSERT( pwszDomainName != NULL && *pwszDomainName != L'\0' );
+
+ //
+ // update rplfiles\configs\dos\lanman.dos\lanman.ini
+ //
+ wcscpy( achFile, RG_Directory );
+ wcscat( achFile, LANMANINI_CONFIGSDOS );
+
+ if ( !WritePrivateProfileString( LANMANINI_SECTION,
+ LANMANINI_KEY,
+ pwszDomainName,
+ achFile ))
+ {
+ Error = GetLastError();
+ RplDump( ++RG_Assert, ( "Error=%d", Error));
+ goto cleanup;
+ }
+
+ //
+ // update rplfiles\binfiles\lanman.dos\lanman.ini
+ //
+ wcscpy( achFile + RG_DirectoryLength, LANMANINI_BINFILES );
+
+ if ( !WritePrivateProfileString( LANMANINI_SECTION,
+ LANMANINI_KEY,
+ pwszDomainName,
+ achFile ))
+ {
+ Error = GetLastError();
+ RplDump( ++RG_Assert, ( "Error=%d", Error));
+ goto cleanup;
+ }
+
+ //
+ // update rplfiles\profiles\<profilename>\lanman.dos\lanman.ini
+ //
+ // Code lifted from NetrRplConfigEnum
+ //
+ EnterCriticalSection( &RG_ProtectDatabase);
+ InCritSec = TRUE;
+ Call( JetBeginTransaction( pSession->SesId));
+ InEnumeration = TRUE;
+
+ if ( !RplFilterFirst( pSession, PROFILE_TABLE_TAG, NULL, NULL, &TableEnd)) {
+ Error = NERR_RplCannotEnum;
+ goto cleanup;
+ }
+
+ for ( ; ; ) {
+
+ if ( TableEnd == TRUE) {
+ break;
+ }
+
+ if (EnumProfileName != NULL) {
+ MIDL_user_free( EnumProfileName );
+ EnumProfileName = NULL;
+ }
+
+ // get profile name
+ SpaceLeft = MAX_PATH; // CODEWORK * sizeof(WCHAR)?
+ Error = ProfileGetField( pSession,
+ PROFILE_ProfileName,
+ &EnumProfileName,
+ &SpaceLeft);
+ if ( Error != NO_ERROR) {
+ RplDump( ++RG_Assert, ( "Error=%d", Error));
+ goto cleanup;
+ }
+
+ // build file name and modify file
+ if ( ( RG_DirectoryLength
+ + wcslen(LANMANINI_PROFILES)
+ + wcslen(EnumProfileName)
+ - 3) >= MAX_PATH )
+ {
+ RplDump( ++RG_Assert, ( "wkstaname too long \"%ws\"", EnumWkstaName));
+ }
+ else
+ {
+ swprintf( achFile + RG_DirectoryLength,
+ LANMANINI_PROFILES,
+ EnumProfileName );
+ if ( !WritePrivateProfileString( LANMANINI_SECTION,
+ LANMANINI_KEY,
+ pwszDomainName,
+ achFile ))
+ {
+ Error = GetLastError();
+ RplDump( ++RG_Assert, ( "Error=%d", Error));
+ goto cleanup;
+ }
+ }
+
+ if ( !RplFilterNext( pSession, pSession->ProfileTableId, NULL, &TableEnd)) {
+ Error = NERR_RplCannotEnum;
+ goto cleanup;
+ }
+ }
+
+ Call( JetCommitTransaction( pSession->SesId, 0)); // CODEWORK rollback on error?
+ InEnumeration = FALSE;
+
+ //
+ // update rplfiles\machines\<wkstaname>\<profilename>\lanman.dos\lanman.ini
+ //
+ Call( JetBeginTransaction( pSession->SesId));
+ InEnumeration = TRUE;
+ if ( !RplFilterFirst( pSession, WKSTA_TABLE_TAG, NULL, NULL, &TableEnd)) {
+ Error = NERR_RplCannotEnum;
+ goto cleanup;
+ }
+ for ( ; ; ) {
+
+ if ( TableEnd == TRUE) {
+ break;
+ }
+
+ // check whether this wksta has a personal profile
+ SpaceLeft = sizeof(DWORD);
+ Error = WkstaGetField( pSession,
+ WKSTA_Flags,
+ (LPVOID *)&EnumWkstaFlags,
+ &SpaceLeft);
+ if ( Error != NO_ERROR) {
+ RplDump( ++RG_Assert, ( "Error=%d", Error));
+ goto cleanup;
+ }
+
+ // only write for personal profile
+ if ( EnumWkstaFlags & WKSTA_FLAGS_SHARING_FALSE ) {
+
+ if (EnumWkstaName != NULL) {
+ MIDL_user_free( EnumWkstaName );
+ EnumProfileName = NULL;
+ }
+
+ // get workstation name
+ SpaceLeft = MAX_PATH; // CODEWORK * sizeof(WCHAR)?
+ Error = WkstaGetField( pSession,
+ WKSTA_WkstaName,
+ &EnumWkstaName,
+ &SpaceLeft);
+ if ( Error != NO_ERROR) {
+ RplDump( ++RG_Assert, ( "Error=%d", Error));
+ goto cleanup;
+ }
+
+ if (EnumProfileName != NULL) {
+ MIDL_user_free( EnumProfileName );
+ EnumProfileName = NULL;
+ }
+
+ // get workstation-in-profile name
+ SpaceLeft = MAX_PATH; // CODEWORK * sizeof(WCHAR)?
+ Error = WkstaGetField( pSession,
+ WKSTA_ProfileName,
+ &EnumProfileName,
+ &SpaceLeft);
+ if ( Error != NO_ERROR) {
+ RplDump( ++RG_Assert, ( "Error=%d", Error));
+ goto cleanup;
+ }
+
+ // build file name and modify file
+ if ( ( RG_DirectoryLength
+ + wcslen(LANMANINI_WKSTAS)
+ + wcslen(EnumWkstaName)
+ + wcslen(EnumProfileName)
+ - 6) >= MAX_PATH )
+ {
+ RplDump( ++RG_Assert,
+ ( "Wksta+profile too long \"%ws\", \"%ws\"",
+ EnumWkstaName,
+ EnumProfileName));
+ }
+ else
+ {
+ swprintf( achFile + RG_DirectoryLength,
+ LANMANINI_WKSTAS,
+ EnumWkstaName,
+ EnumProfileName );
+ if ( !WritePrivateProfileString( LANMANINI_SECTION,
+ LANMANINI_KEY,
+ pwszDomainName,
+ achFile ))
+ {
+ Error = GetLastError();
+ RplDump( ++RG_Assert, ( "Error=%d", Error));
+ goto cleanup;
+ }
+ }
+
+
+ }
+
+
+ if ( !RplFilterNext( pSession, pSession->WkstaTableId, NULL, &TableEnd)) {
+ Error = NERR_RplCannotEnum;
+ goto cleanup;
+ }
+ }
+
+cleanup:
+
+ if ( InEnumeration) {
+ Call( JetCommitTransaction( pSession->SesId, 0)); // CODEWORK rollback on error?
+ }
+
+ if ( InCritSec) {
+ LeaveCriticalSection( &RG_ProtectDatabase);
+ }
+ if (EnumProfileName != NULL) {
+ MIDL_user_free( EnumProfileName );
+ EnumWkstaName = NULL;
+ }
+
+ if (EnumWkstaName != NULL) {
+ MIDL_user_free( EnumWkstaName );
+ EnumWkstaName = NULL;
+ }
+
+ if ( pw100 != NULL) {
+ NetApiBufferFree( pw100);
+ }
+
+ //
+ // BUGBUG Need better event id/text?
+ //
+ if (Error != NO_ERROR) {
+ RplReportEvent( NELOG_RplCheckSecurity, NULL, sizeof(DWORD), &Error );
+ }
+
+ return( Error);
+}
+
+#define RPL_COM L"\\COMMAND.COM"
+#define WCSLEN( _x_) ((DWORD) ( sizeof(_x_)/sizeof(WCHAR) - 1))
+
+BOOL RplConfigEnabled( IN PWCHAR DirName2)
+{
+ WCHAR SearchBuffer[ MAX_PATH ];
+ DWORD SearchBufferLength;
+
+ wcscpy( SearchBuffer, RG_DiskBinfiles);
+ SearchBufferLength = wcslen( SearchBuffer);
+
+ if ( SearchBufferLength + wcslen(DirName2) + WCSLEN( RPL_COM)
+ > WCSLEN( SearchBuffer)) {
+ RplDump( ++RG_Assert, ( "DirName2=%ws", DirName2));
+ return( FALSE);
+ }
+ wcscpy( SearchBuffer + SearchBufferLength, DirName2);
+ wcscat( SearchBuffer + SearchBufferLength, RPL_COM);
+ /*
+ * The file COMMAND.COM should be in directory given by SearchBuffer
+ */
+ return( FileExists( SearchBuffer, NULL));
+}
+
+
+DWORD RplCheckConfigs( VOID)
+/*++
+
+Routine Description:
+
+ This function checks whether the file COMMAND.COM exists
+ in the proper place for each configuration, and
+ enables/disables the configuration accordingly.
+
+Arguments:
+
+Return Value:
+
+--*/
+{
+ DWORD Error = NO_ERROR;
+ INT SpaceLeft;
+ BOOL TableEnd;
+ WCHAR * DirName2 = NULL;
+ DWORD Flags;
+ WCHAR SearchBuffer[ MAX_PATH ];
+ BOOL BoolConfigEnabled = FALSE;
+ BOOL BoolFileFound = FALSE;
+ DWORD SearchBufferLength;
+ PRPL_SESSION pSession = &RG_ApiSession;
+
+ wcscpy( SearchBuffer, RG_DiskBinfiles);
+ SearchBufferLength = wcslen( SearchBuffer);
+
+ //
+ // Code lifted from NetrRplConfigEnum
+ //
+ EnterCriticalSection( &RG_ProtectDatabase);
+ Call( JetBeginTransaction( pSession->SesId));
+
+ if ( !RplFilterFirst( pSession, CONFIG_TABLE_TAG, NULL, NULL, &TableEnd)) {
+ Error = NERR_RplCannotEnum;
+ goto cleanup;
+ }
+ if ( TableEnd == TRUE) {
+ goto cleanup;
+ }
+ for ( ; ; ) {
+
+ if (DirName2 != NULL) {
+ MIDL_user_free( DirName2 );
+ DirName2 = NULL;
+ }
+
+ Error = ConfigGetField( pSession,
+ CONFIG_DirName2,
+ &DirName2,
+ &SpaceLeft);
+ if ( Error != NO_ERROR) {
+ RplDump( ++RG_Assert, ( "Error=%d", Error));
+ goto cleanup;
+ }
+
+ Error = ConfigGetField( pSession,
+ CONFIG_Flags,
+ (LPVOID *)&Flags,
+ &SpaceLeft);
+ if ( Error != NO_ERROR) {
+ RplDump( ++RG_Assert, ( "Error=%d", Error));
+ goto cleanup;
+ }
+
+ //
+ // Check whether this configuration should be enabled
+ //
+ RPL_ASSERT( (!!(Flags & CONFIG_FLAGS_ENABLED_TRUE))
+ != (!!(Flags & CONFIG_FLAGS_ENABLED_FALSE)) );
+ BoolConfigEnabled = !!(Flags & CONFIG_FLAGS_ENABLED_TRUE);
+
+ if ( SearchBufferLength + wcslen(DirName2) + WCSLEN( RPL_COM)
+ > WCSLEN( SearchBuffer)) {
+ RplDump( ++RG_Assert, ( "DirName2=%ws", DirName2));
+ continue;
+ }
+ wcscpy( SearchBuffer + SearchBufferLength, DirName2);
+ wcscat( SearchBuffer + SearchBufferLength, RPL_COM);
+ /*
+ * The file COMMAND.COM should be in directory given by SearchBuffer
+ */
+ BoolFileFound = FileExists( SearchBuffer, NULL );
+
+ if (Error == NERR_Success && BoolConfigEnabled != BoolFileFound) {
+ Flags ^= CONFIG_FLAGS_MASK_ENABLED; // XOR
+
+ // lifted from NetrRplConfigAdd and NetrWkstaSetinfo
+ CallJ( JetPrepareUpdate( pSession->SesId,
+ pSession->ConfigTableId,
+ JET_prepReplace));
+
+ // lifted from ConfigSetInfo
+ CallM( JetSetColumn( pSession->SesId, pSession->ConfigTableId,
+ ConfigTable[ CONFIG_Flags].ColumnId,
+ &Flags, sizeof( Flags), 0, NULL));
+
+ // lifted from NetrRplConfigAdd
+ CallJ( JetUpdate( pSession->SesId,
+ pSession->ConfigTableId,
+ NULL, 0, NULL));
+ }
+
+ if ( !RplFilterNext( pSession, pSession->ConfigTableId, NULL, &TableEnd)) {
+ Error = NERR_RplCannotEnum;
+ goto cleanup;
+ }
+ if ( TableEnd == TRUE) {
+ goto cleanup;
+ }
+ }
+
+cleanup:
+ Call( JetCommitTransaction( pSession->SesId, 0)); // CODEWORK rollback on error?
+ LeaveCriticalSection( &RG_ProtectDatabase);
+
+ if (DirName2 != NULL) {
+ MIDL_user_free( DirName2 );
+ DirName2 = NULL;
+ }
+
+ if (Error != NO_ERROR) {
+ RplReportEvent( NELOG_RplCheckConfigs, NULL, sizeof(DWORD), &Error );
+ }
+
+ return( Error);
+}
+
+
+DWORD RplBackupDatabase( IN BOOL FullBackup)
+/*++
+
+Routine Description:
+
+ This function backs up the JET database. FullBackup copies the
+ database file and all log files. Incremental backup copies only
+ the log files that are modified since the last backup.
+
+Arguments:
+
+ FullBackup - set to TRUE if full backup is required.
+
+Return Value:
+
+ Windows Error.
+
+--*/
+{
+ JET_ERR JetError;
+
+ FullBackup = TRUE; // to avoid JET bugs in JetRestore()
+
+ JetError = JetBackup( RG_BackupPath,
+#ifdef __JET500
+ JET_bitBackupAtomic |
+ (FullBackup ? 0
+ : JET_bitBackupIncremental),
+ NULL
+#else
+ (FullBackup ? JET_bitOverwriteExisting
+ : JET_bitBackupIncremental)
+#endif
+ );
+
+ if( JetError == JET_errFileNotFound && FullBackup == FALSE) {
+
+ //
+ // Full backup was not performed anytime, so do it now.
+ //
+
+ JetError = JetBackup( RG_BackupPath,
+#ifdef __JET500
+ JET_bitBackupAtomic,
+ NULL
+#else
+ JET_bitOverwriteExisting
+#endif
+ );
+ }
+
+ if ( JetError < 0) {
+ RplReportEvent( NELOG_RplBackupDatabase, NULL, sizeof(DWORD), &JetError);
+ return( NERR_RplBackupDatabase);
+ }
+ return( NO_ERROR);
+}
+
+
+DWORD SetupAction(
+ IN OUT PDWORD pAction,
+ IN BOOL FullBackup
+ )
+/*++
+
+Routine Description:
+
+ Performs the setup action for all RPL_SPECIAL_ACTIONS specified
+ in *pAction.
+
+Arguments:
+ pAction: points to RPL_SPECIAL_ACTIONS DWORD. On return, the
+ RPL_SPECIAL_ACTIONS flags are reset for all actions which
+ were successfully performed.
+
+Return Value:
+ error word
+
+--*/
+{
+ DWORD Error;
+
+ if ((*pAction) & RPL_REPLACE_RPLDISK) {
+#ifdef NOT_YET
+ Error = RplReplaceRPLDISK();
+ if (Error != NO_ERROR) {
+ return( Error);
+ }
+#endif
+ *pAction &= ~RPL_REPLACE_RPLDISK;
+ }
+
+ if ((*pAction) & RPL_CHECK_SECURITY) {
+ Error = RplProcessLanmanInis();
+ if (Error != NO_ERROR) {
+ return( Error);
+ }
+ *pAction &= ~RPL_CHECK_SECURITY;
+ }
+
+ if ((*pAction) & RPL_CHECK_CONFIGS) {
+ Error = RplCheckConfigs();
+ if (Error != NO_ERROR) {
+ return( Error);
+ }
+ *pAction &= ~RPL_CHECK_CONFIGS;
+ }
+
+ if ((*pAction) & RPL_CREATE_PROFILES) {
+ //
+ // CODEWORK RPL_CREATE_PROFILES not implemented
+ //
+ *pAction &= ~RPL_CREATE_PROFILES;
+ }
+
+ if ((*pAction) & RPL_BACKUP_DATABASE) {
+ Error = RplBackupDatabase( FullBackup);
+ if (Error != NO_ERROR) {
+ return( Error);
+ }
+ *pAction &= ~RPL_BACKUP_DATABASE;
+ }
+
+ return( NO_ERROR);
+}
+
diff --git a/private/net/svcdlls/rpl/server/setup.h b/private/net/svcdlls/rpl/server/setup.h
new file mode 100644
index 000000000..67993c662
--- /dev/null
+++ b/private/net/svcdlls/rpl/server/setup.h
@@ -0,0 +1,34 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ setup.h
+
+Abstract:
+
+ Exports from setup.c
+
+Author:
+
+ Jon Newman (jonn) 01-April-1994
+
+Revision History:
+
+ Jon Newman (jonn) 01 - April - 1994
+ Added setup primitives
+
+--*/
+
+#ifndef _RPL_SETUP_H_
+#define _RPL_SETUP_H_
+
+DWORD SetupAction(
+ IN OUT PDWORD pAction,
+ IN BOOL FullBackup
+ );
+DWORD RplBackupDatabase( IN BOOL FullBackup);
+BOOL RplConfigEnabled( IN PWCHAR DirName2);
+
+#endif // _RPL_SETUP_H_
diff --git a/private/net/svcdlls/rpl/server/sources b/private/net/svcdlls/rpl/server/sources
new file mode 100644
index 000000000..537309ea0
--- /dev/null
+++ b/private/net/svcdlls/rpl/server/sources
@@ -0,0 +1,83 @@
+#
+# Source file used for building RplSvc.exe in this directory.
+#
+
+MAJORCOMP=net
+MINORCOMP=rplsvc
+
+TARGETPATH=obj
+TARGETNAME=rplsvc
+TARGETTYPE=PROGRAM
+
+TARGETLIBS= \
+ ..\dll\obj\*\rplnet.lib \
+ ..\lib\obj\*\rpllib.lib \
+ $(BASEDIR)\Public\Sdk\Lib\*\dlcapi.lib \
+ $(BASEDIR)\public\sdk\lib\*\rpcrt4.lib \
+ $(BASEDIR)\public\sdk\lib\*\rpcutil.lib \
+ $(BASEDIR)\public\sdk\lib\*\rpcndr.lib \
+ $(BASEDIR)\public\sdk\lib\*\samlib.lib \
+ $(BASEDIR)\Public\Sdk\Lib\*\wsock32.lib \
+ $(BASEDIR)\public\sdk\lib\*\jet500.lib \
+ $(BASEDIR)\public\sdk\lib\*\ntdll.lib
+
+
+!IFNDEF DISABLE_NET_UNICODE
+UNICODE=1
+NET_C_DEFINES=-DUNICODE
+!ENDIF
+
+INCLUDES=.;..\inc;..\..\..\inc;..\..\..\api;..\..\..\..\inc;
+
+MSC_WARNING_LEVEL=/W3 /WX
+
+SOURCES= \
+ apisec.c \
+ dblib.c \
+ tree.c \
+ vendor.c \
+ security.c \
+ adapter.c \
+ disk.c \
+ service.c \
+ resume.c \
+ boot.c \
+ config.c \
+ profile.c \
+ setup.c \
+ wksta.c \
+ fitfile.c \
+ open.c \
+ bbcfile.c \
+ read.c \
+ database.c \
+ request.c \
+ worker.c \
+ rplsvc.c \
+ rplsvc.rc \
+ rplmain.c \
+ debug.c \
+ report.c \
+ library.c \
+ memory.c \
+ rplsvc_s.c
+
+USE_CRTDLL=1
+#
+#Add -DRPL_NO_SERVICE if you do not want RPL to run as a service
+#
+#C_DEFINES= -DINCL_32= -DNT -DRPC_NO_WINDOWS_H -DWIN32 -DRPL_NO_SERVICE
+#
+
+C_DEFINES= -DINCL_32= -DNT -DRPC_NO_WINDOWS_H -DWIN32
+
+UMTYPE=console
+UMTEST= rplrest
+UMLIBS= \
+ $(BASEDIR)\public\sdk\lib\*\rpcrt4.lib \
+ $(BASEDIR)\public\sdk\lib\*\jet500.lib \
+ $(BASEDIR)\public\sdk\lib\*\netlib.lib \
+ $(BASEDIR)\public\sdk\lib\*\netapi32.lib
+
+
+
diff --git a/private/net/svcdlls/rpl/server/tree.c b/private/net/svcdlls/rpl/server/tree.c
new file mode 100644
index 000000000..40ede9642
--- /dev/null
+++ b/private/net/svcdlls/rpl/server/tree.c
@@ -0,0 +1,353 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ tree.c
+
+Abstract:
+
+ RplTreeCopy, RplTreeDelete, RplMakeDir file-management helper routines
+ for the RPL service.
+
+Author:
+
+ Jon Newman (jonn) 23 - November - 1993
+
+Revision History:
+
+ Vladimir Z. Vulovic (vladimv) 02 - December - 1993
+ Integrated with the rest of RPL service code.
+ Jon Newman (jonn) 15 - February - 1994
+ Added RplGrant*Perms primitives
+ Jon Newman (jonn) 08 - March - 1994
+ RplDoTree now creates log entries on failure
+
+--*/
+
+#include "local.h"
+#include "tree.h"
+#include "treei.h"
+
+#define FILE_SEPARATOR L"\\"
+#define HERE_DIRECTORY L"."
+#define PARENT_DIRECTORY L".."
+#define ALLFILES_SUFFIX L"*"
+
+#define LEN_FILE_SEPARATOR 1
+#define LEN_ALLFILES_SUFFIX 1
+
+
+VOID LoadError(
+ PWCHAR FilePath,
+ DWORD ActionError,
+ DWORD ActionFlags
+ )
+/*++
+ BUGBUG if buffer inadequate, copy first part or last part?
+--*/
+{
+ DWORD Length0 = 0;
+ DWORD msgid = 0;
+
+ //
+ // Dump error log entry, where the message depends on Flags
+ //
+ if (ActionFlags == 0) {
+ msgid = 0;
+ } else if (ActionFlags & RPL_TREE_COPY) {
+ msgid = NELOG_RplFileCopy;
+ } else if (ActionFlags & RPL_TREE_DELETE) {
+ msgid = NELOG_RplFileDelete;
+ } else if (ActionFlags & RPL_TREE_AUXILIARY) {
+ msgid = NELOG_RplFilePerms;
+ } else {
+ RPL_ASSERT( FALSE );
+ }
+
+ if (msgid != 0) {
+ RplReportEvent( msgid, FilePath, sizeof(DWORD), &ActionError );
+ }
+}
+
+
+DWORD RplDoTree(
+ PWCHAR Source,
+ PWCHAR Target,
+ DWORD Flags,
+ RPL_TREE_CALLBACK AuxiliaryCallback,
+ PBOOL pAuxiliaryBlock
+ )
+/*++
+ Target should only be examined if RPL_TREE_COPY case.
+--*/
+{
+ // CODEWORK too much stack space usage (1.5K / recursive invocation)
+ DWORD Error = NO_ERROR;
+ WCHAR CurrentSource[ MAX_PATH];
+ WCHAR CurrentTarget[ MAX_PATH];
+ HANDLE Handle = INVALID_HANDLE_VALUE;
+ WIN32_FIND_DATA FindData;
+ DWORD SourceLength = 0;
+ DWORD TargetLength = 0;
+ DWORD FileLength = 0;
+ BOOL Auxiliary = ((Flags & RPL_TREE_AUXILIARY) != 0);
+ BOOL Delete = ((Flags & RPL_TREE_DELETE) != 0);
+ BOOL Copy = ((Flags & RPL_TREE_COPY) != 0);
+
+ //
+ // Delete is incompatible with Auxiliary and Copy
+ //
+ RPL_ASSERT( !Delete || (!Auxiliary && !Copy ) );
+
+ //
+ // Callback and callback data block required for Auxiliary
+ //
+ RPL_ASSERT( !Auxiliary
+ || (AuxiliaryCallback != NULL && pAuxiliaryBlock != NULL) );
+
+ if ( Source == NULL || *Source == L'\0') {
+ RPL_RETURN( ERROR_INVALID_PARAMETER);
+ }
+ SourceLength = wcslen( Source);
+ if ( SourceLength + LEN_ALLFILES_SUFFIX >= MAX_PATH) {
+ Error = ERROR_FILENAME_EXCED_RANGE;
+ RplDump( ++RG_Assert, ( "Error=%d", Error));
+ LoadError( Source, Error, Flags);
+ return( Error);
+ }
+ //
+ // Initialize buffer for source names.
+ //
+ memcpy( CurrentSource, Source, SourceLength * sizeof(WCHAR));
+ memcpy( CurrentSource + SourceLength, FILE_SEPARATOR, LEN_FILE_SEPARATOR * sizeof(WCHAR));
+ memcpy( CurrentSource + SourceLength + LEN_FILE_SEPARATOR, ALLFILES_SUFFIX, (LEN_ALLFILES_SUFFIX+1) * sizeof(WCHAR));
+
+ //
+ // Copy directory operations are done at the beginning.
+ //
+ // BUGBUG broken if source is not a directory
+ //
+ if ( Copy) {
+ TargetLength = wcslen( Target);
+ RPL_ASSERT( TargetLength < MAX_PATH);
+ //
+ // Initialize buffer for target names.
+ //
+ memcpy( CurrentTarget, Target, (TargetLength+1) * sizeof(WCHAR));
+ if ( !CreateDirectoryEx( Source, Target, NULL)) {
+ Error = GetLastError();
+ if ( Error != ERROR_ALREADY_EXISTS) {
+ RplDump( ++RG_Assert, ( "Error=%d", Error));
+ LoadError( Source, Error, RPL_TREE_COPY);
+ return( Error);
+ }
+ }
+ }
+
+ //
+ // Stop recursing this tree if we are setting permission only and if
+ // SetRplPerms tells us to quit this branch. Here we depend on the fact that
+ // RPL_SD_BLOCK - structure not visible here, contains BOOL StopRecursion
+ // as its first element.
+ //
+ if ( Auxiliary) {
+ Error = (AuxiliaryCallback)( ((Copy)?Target:Source), pAuxiliaryBlock);
+ if (Error != NERR_Success) {
+ RplDump( ++RG_Assert, ( "Error=%d", Error));
+ LoadError( Source, Error, RPL_TREE_AUXILIARY);
+ return( Error);
+ }
+ if ( !Delete && !Copy && *pAuxiliaryBlock == TRUE) {
+ return( NO_ERROR);
+ }
+ }
+
+ //
+ // Enumerate and copy/change/delete directory contents.
+ //
+ for ( Handle = INVALID_HANDLE_VALUE; NOTHING; Error = NO_ERROR) {
+
+ if ( Handle == INVALID_HANDLE_VALUE) {
+ Handle = FindFirstFile( CurrentSource, &FindData);
+ if (Handle == INVALID_HANDLE_VALUE) {
+ Error = GetLastError();
+ //
+ // ERROR_PATH_NOT_FOUND occurs for non-existing Source tree
+ //
+ if (Error == ERROR_NO_MORE_FILES || Error == ERROR_PATH_NOT_FOUND) {
+ Error = NO_ERROR;
+ } else {
+ RplDump( ++RG_Assert, ( "Error=%d, CurrentSource=%ws", Error, CurrentSource));
+ LoadError( CurrentSource, Error, Flags);
+ }
+ break;
+ }
+ //
+ // Trim ALLFILES_SUFFIX off CurrentSource for future use
+ //
+ CurrentSource[ SourceLength + LEN_FILE_SEPARATOR] = L'\0';
+ } else {
+ if ( !FindNextFile( Handle, &FindData)) {
+ Error = GetLastError();
+ if ( Error == ERROR_NO_MORE_FILES) {
+ Error = NO_ERROR;
+ break;
+ } else {
+ RplDump( ++RG_Assert, ( "Error=%d, CurrentSource=%ws", Error, CurrentSource));
+ LoadError( CurrentSource, Error, Flags);
+ }
+ }
+ }
+
+ if ( 0 == wcscmp( FindData.cFileName, HERE_DIRECTORY) ||
+ 0 == wcscmp( FindData.cFileName, PARENT_DIRECTORY)) {
+ continue; // get next entry
+ }
+ FileLength = wcslen( FindData.cFileName);
+
+ //
+ // Update source name.
+ //
+ if ( SourceLength + LEN_FILE_SEPARATOR + FileLength >= MAX_PATH) {
+ Error = ERROR_FILENAME_EXCED_RANGE;
+ RplDump(++RG_Assert,("Error=%d, SourceLength=%d, FileName=%ws",
+ Error, SourceLength, FindData.cFileName));
+ LoadError( Source, Error, Flags);
+ break;
+ }
+ memcpy( CurrentSource+SourceLength+LEN_FILE_SEPARATOR,
+ FindData.cFileName, (FileLength+1) * sizeof(WCHAR));
+
+ //
+ // Update target name (this is needed both for the file case &
+ // the directory case below).
+ //
+ if ( Copy) {
+ if ( TargetLength + LEN_FILE_SEPARATOR + FileLength >= MAX_PATH) {
+ Error = ERROR_FILENAME_EXCED_RANGE;
+ RplDump(++RG_Assert,("Error=%d, TargetLength=%d, FileName=%ws",
+ Error, TargetLength, FindData.cFileName));
+ LoadError( Target, Error, RPL_TREE_COPY);
+ break;
+ }
+ memcpy( CurrentTarget+TargetLength,
+ FILE_SEPARATOR, sizeof(FILE_SEPARATOR));
+ memcpy( CurrentTarget+TargetLength+LEN_FILE_SEPARATOR,
+ FindData.cFileName, (FileLength+1) * sizeof(WCHAR));
+ }
+
+ if ( !(FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
+ if ( Copy) {
+ if ( !CopyFile( CurrentSource, CurrentTarget, FALSE)) {
+ Error = GetLastError();
+ RplDump(++RG_Assert,("Error=%d CurrentSource=%ws CurrentTarget=%ws",
+ Error, CurrentSource, CurrentTarget));
+ LoadError( CurrentSource, Error, RPL_TREE_COPY);
+ break;
+ }
+ } else if ( Delete){
+ //
+ // If file has readonly attribute, we need to reset this
+ // attribute, otherwise DeleteFile() with fail with error
+ // access denied.
+ //
+ if ( !(FindData.dwFileAttributes & FILE_ATTRIBUTE_NORMAL)) {
+ (VOID)SetFileAttributes( CurrentSource, FILE_ATTRIBUTE_NORMAL);
+ }
+ if ( !DeleteFile( CurrentSource)) {
+ Error = GetLastError();
+ RplDump(++RG_Assert,("Error=%d CurrentSource=%ws", Error, CurrentSource));
+ LoadError( CurrentSource, Error, RPL_TREE_DELETE);
+ break;
+ }
+ }
+
+ //
+ // Set permissions after file is created (in Copy && Auxiliary case)
+ //
+ if ( Auxiliary) {
+ Error = (AuxiliaryCallback)( ( (Copy) ? CurrentTarget
+ : CurrentSource ),
+ pAuxiliaryBlock );
+ if (Error != NERR_Success) {
+ RplDump( ++RG_Assert, ( "Error=%d", Error));
+ LoadError( Source, Error, RPL_TREE_AUXILIARY);
+ break;
+ }
+ }
+
+ } else {
+ Error = RplDoTree( CurrentSource,
+ CurrentTarget,
+ Flags,
+ AuxiliaryCallback,
+ pAuxiliaryBlock);
+ }
+ }
+
+ if (Handle != INVALID_HANDLE_VALUE) {
+ FindClose( Handle );
+ }
+
+ //
+ // Delete is the only directory operation that is done at the end.
+ //
+ if ( Delete && Error == NO_ERROR) {
+ //
+ // If directory has readonly attribute, we need to reset this
+ // attribute, otherwise RemoveDirectory() with fail with error
+ // access denied.
+ //
+ (VOID)SetFileAttributes( Source, FILE_ATTRIBUTE_DIRECTORY);
+ if ( !RemoveDirectory( Source)) {
+ Error = GetLastError();
+ if (Error == ERROR_FILE_NOT_FOUND || Error == ERROR_PATH_NOT_FOUND) {
+ Error = NO_ERROR;
+ } else {
+ RplDump( ++RG_Assert, ( "Error=%d", Error));
+ LoadError( Source, Error, RPL_TREE_DELETE);
+ }
+ }
+ }
+ return( Error);
+}
+
+
+DWORD RplTreeCopy( IN PWCHAR Source, IN PWCHAR Target)
+{
+ DWORD Error;
+
+ Error = RplDoTree( Source, Target, RPL_TREE_COPY,
+ NULL, NULL);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ return( NO_ERROR);
+}
+
+
+DWORD RplTreeDelete( IN PWCHAR Source)
+{
+ DWORD Error;
+
+ Error = RplDoTree( Source, L"", RPL_TREE_DELETE,
+ NULL, NULL);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ return( NO_ERROR);
+}
+
+
+DWORD RplMakeDir( IN PWCHAR Source)
+{
+ DWORD Error = NO_ERROR;
+ if ( !CreateDirectory( Source, NULL)) {
+ Error = GetLastError();
+ RplDump( ++RG_Assert, ("Error=%d", Error));
+ }
+ return( Error);
+}
+
diff --git a/private/net/svcdlls/rpl/server/tree.h b/private/net/svcdlls/rpl/server/tree.h
new file mode 100644
index 000000000..d68d34917
--- /dev/null
+++ b/private/net/svcdlls/rpl/server/tree.h
@@ -0,0 +1,28 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ tree.h
+
+Abstract:
+
+ Exports from tree.c
+
+Author:
+
+ Jon Newman (jonn) 23 - November - 1993
+
+Revision History:
+
+ Vladimir Z. Vulovic (vladimv) 02 - December - 1993
+ Integrated with the rest of RPL service code.
+
+--*/
+
+DWORD RplTreeCopy( IN PWCHAR Source, IN PWCHAR Target);
+DWORD RplTreeDelete( IN PWCHAR Target);
+DWORD RplMakeDir( IN PWCHAR Target);
+
+
diff --git a/private/net/svcdlls/rpl/server/treei.h b/private/net/svcdlls/rpl/server/treei.h
new file mode 100644
index 000000000..019dee4d1
--- /dev/null
+++ b/private/net/svcdlls/rpl/server/treei.h
@@ -0,0 +1,47 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ tree.h
+
+Abstract:
+
+ Internal definitions from tree.c
+
+Author:
+
+ Jon Newman (jonn) 16 - February - 1994
+
+Revision History:
+
+ Jon Newman (jonn) 16 - February - 1994
+ Added RplGrant*Perms primitives
+
+--*/
+
+typedef DWORD (RPL_TREE_CALLBACK)( PWCHAR Path, PBOOL pAuxiliaryBlock);
+
+#define RPL_TREE_AUXILIARY 0x2000 // perform callback action
+#define RPL_TREE_COPY 0x4000
+#define RPL_TREE_DELETE 0x8000
+
+//
+// Forward declarations
+//
+
+DWORD RplDoTree(
+ PWCHAR Source,
+ PWCHAR Target,
+ DWORD Flags,
+ RPL_TREE_CALLBACK AuxiliaryCallback,
+ PBOOL pAuxiliaryBlock
+ );
+
+VOID LoadError(
+ PWCHAR FilePath,
+ DWORD ActionError,
+ DWORD ActionFlags
+ );
+
diff --git a/private/net/svcdlls/rpl/server/vendor.c b/private/net/svcdlls/rpl/server/vendor.c
new file mode 100644
index 000000000..708a1b26c
--- /dev/null
+++ b/private/net/svcdlls/rpl/server/vendor.c
@@ -0,0 +1,528 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ config.c
+
+Abstract:
+
+ This module contains RPL config apis: ConfigEnum.
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 05 - November - 1993
+
+Revision History:
+
+ 05-Nov-1993 vladimv
+ Created
+
+--*/
+
+#include "local.h"
+#include "rpldb.h"
+#include "db.h"
+#include "dblib.h"
+#define RPLVENDOR_ALLOCATE
+#include "vendor.h"
+#undef RPLVENDOR_ALLOCATE
+
+
+DWORD VendorSetInfo(
+ IN PRPL_SESSION pSession,
+ IN DWORD Level,
+ IN LPVOID Buffer,
+ OUT LPDWORD pErrorParameter
+ )
+{
+ LPRPL_VENDOR_INFO_1 Info = Buffer;
+ switch( Level) {
+ case 1:
+ //
+ // Must initialize Flags - or will trap in GetField.
+ //
+ {
+ *pErrorParameter = VENDOR_Flags;
+ CallM( JetSetColumn( pSession->SesId, pSession->VendorTableId,
+ VendorTable[ VENDOR_Flags].ColumnId,
+ &Info->Flags, sizeof( Info->Flags), 0, NULL));
+ }
+ NOTHING; // fall through
+ case 0:
+ if ( Info->VendorComment != NULL) {
+ *pErrorParameter = VENDOR_VendorComment;
+ CallM( JetSetColumn( pSession->SesId, pSession->VendorTableId,
+ VendorTable[ VENDOR_VendorComment].ColumnId,
+ Info->VendorComment,
+ ( wcslen( Info->VendorComment) + 1) * sizeof(WCHAR),
+ 0, NULL));
+ }
+ if ( Info->VendorName != NULL) {
+ *pErrorParameter = VENDOR_VendorName;
+ CallM( JetSetColumn( pSession->SesId, pSession->VendorTableId,
+ VendorTable[ VENDOR_VendorName].ColumnId,
+ Info->VendorName,
+ ( wcslen( Info->VendorName) + 1) * sizeof(WCHAR),
+ 0, NULL));
+ }
+ break;
+ }
+ return( NO_ERROR);
+}
+
+
+DWORD VendorGetField(
+ IN PRPL_SESSION pSession,
+ IN DWORD FieldIndex,
+ OUT LPVOID * pData,
+ IN OUT LPINT pSpaceLeft
+ )
+{
+ BYTE LocalBuffer[ 300];
+ PBYTE Buffer;
+ DWORD DataSize;
+ DWORD BufferSize;
+ JET_ERR JetError;
+
+ switch( FieldIndex) {
+ case VENDOR_Flags:
+ Buffer = (PBYTE)pData;
+ BufferSize = sizeof( DWORD);
+ break;
+ default:
+ Buffer = LocalBuffer;
+ BufferSize = sizeof( LocalBuffer);
+ break;
+ }
+ JetError = JetRetrieveColumn( pSession->SesId, pSession->VendorTableId,
+ VendorTable[ FieldIndex].ColumnId, Buffer,
+ BufferSize, &DataSize, 0, NULL);
+ if ( JetError < 0) {
+ RplDump( ++RG_Assert, ("JetError=%d", JetError));
+ return( NERR_RplVendorInfoCorrupted);
+ }
+ if ( Buffer != LocalBuffer) {
+ if ( BufferSize == DataSize) {
+ return( NO_ERROR);
+ } else {
+ RplDump( ++RG_Assert, ("Bad DataSize=0x%x", DataSize));
+ return( NERR_RplVendorInfoCorrupted);
+ }
+ }
+ //
+ // We have done with fixed data. From here on we deal with unicode
+ // strings only.
+ //
+ if ( DataSize > sizeof( LocalBuffer)) {
+ RplDump( ++RG_Assert, ( "Too big DataSize=0x%x", DataSize));
+ return( NERR_RplVendorInfoCorrupted);
+ }
+ if ( DataSize == 0) {
+ if ( JetError != JET_wrnColumnNull) {
+ RplDump( ++RG_Assert, ( "JetError=%d", JetError));
+ return( NERR_RplVendorInfoCorrupted);
+ } else {
+ *pData = NULL; // so RPC rpcrt4!_tree_size_ndr() does not bomb here
+ return( NO_ERROR);
+ }
+ }
+ if ( DataSize & 1 != 0 || wcslen((PWCHAR)LocalBuffer) + 1 != DataSize/2) {
+ RplDump( ++RG_Assert, ("LocalBuffer=0x%x, DataSize=0x%x", LocalBuffer, DataSize));
+ return( NERR_RplVendorInfoCorrupted);
+ }
+ *pData = MIDL_user_allocate( DataSize);
+ if ( *pData == NULL) {
+ RplDump( ++RG_Assert, ( "Error=%d", GetLastError()));
+ return( ERROR_NOT_ENOUGH_MEMORY);
+ }
+ memcpy( *pData, LocalBuffer, DataSize);
+ *pSpaceLeft -= DataSize;
+ return( NO_ERROR);
+}
+
+
+DWORD VendorGetInfo(
+ IN PRPL_SESSION pSession,
+ IN LPWSTR VendorName,
+ IN DWORD Level,
+ OUT LPVOID Buffer,
+ IN OUT PINT pSpaceLeft
+ )
+{
+ DWORD Error;
+ LPRPL_VENDOR_INFO_1 Info = Buffer;
+
+ switch( Level) {
+ case 1:
+ Error = VendorGetField( pSession, VENDOR_Flags, (LPVOID *)&Info->Flags, pSpaceLeft);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ NOTHING; // fall through
+ case 0:
+ Error = VendorGetField( pSession, VENDOR_VendorComment, &Info->VendorComment, pSpaceLeft);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ if ( VendorName == NULL) {
+ Error = VendorGetField( pSession, VENDOR_VendorName, &Info->VendorName, pSpaceLeft);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ } else {
+ DWORD DataSize = (wcslen( VendorName) + 1) * sizeof(WCHAR);
+ Info->VendorName = MIDL_user_allocate( DataSize);
+ if ( Info->VendorName == NULL) {
+ return( ERROR_NOT_ENOUGH_MEMORY);
+ }
+ RplDump( RG_DebugLevel & RPL_DEBUG_VENDOR, ( "VendorName=0x%x", Info->VendorName));
+ memcpy( Info->VendorName, VendorName, DataSize);
+ *pSpaceLeft -= DataSize;
+ }
+ break;
+ default:
+ return( ERROR_INVALID_LEVEL);
+ break;
+ }
+ return( NO_ERROR);
+}
+
+
+
+VOID VendorGetInfoCleanup(
+ IN DWORD Level,
+ IN OUT LPVOID Buffer
+ )
+{
+ LPRPL_VENDOR_INFO_1 Info = Buffer;
+
+ switch( Level) {
+ case 1:
+ NOTHING; // fall through
+ case 0:
+ if ( Info->VendorComment != NULL) {
+ MIDL_user_free( Info->VendorComment);
+ }
+ if ( Info->VendorName != NULL) {
+ MIDL_user_free( Info->VendorName);
+ }
+ break;
+ }
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetrRplVendorAdd(
+ IN RPL_HANDLE ServerHandle,
+ IN DWORD Level,
+ OUT LPRPL_VENDOR_INFO_STRUCT VendorInfoStruct,
+ OUT LPDWORD pErrorParameter OPTIONAL
+ )
+{
+ LPRPL_VENDOR_INFO_1 Info;
+ LPVOID Buffer;
+ DWORD Error;
+ DWORD ErrorParameter;
+ PRPL_SESSION pSession = &RG_ApiSession;
+
+ ErrorParameter = INVALID_ERROR_PARAMETER;
+ Buffer = Info = VendorInfoStruct->VendorInfo1;
+
+ switch( Level) {
+ case 1:
+ if ( Info->Flags != 0) {
+ ErrorParameter = VENDOR_Flags;
+ break;
+ }
+ if ( RPL_STRING_TOO_LONG( Info->VendorComment)) {
+ ErrorParameter = VENDOR_VendorComment;
+ break;
+ }
+ if ( !ValidHexName( Info->VendorName, RPL_VENDOR_NAME_LENGTH, TRUE)) {
+ ErrorParameter = VENDOR_VendorName;
+ break;
+ }
+ _wcsupr( Info->VendorName);
+ break;
+ default:
+ return( ERROR_INVALID_LEVEL);
+ break;
+ }
+
+ if ( ErrorParameter != INVALID_ERROR_PARAMETER) {
+ if ( ARGUMENT_PRESENT( pErrorParameter)) {
+ *pErrorParameter = ErrorParameter;
+ }
+ return( ERROR_INVALID_PARAMETER);
+ }
+
+ EnterCriticalSection( &RG_ProtectDatabase);
+ Call( JetBeginTransaction( pSession->SesId));
+
+ //
+ // Verify that VendorName is available in the database.
+ //
+ if ( RplFind( pSession, VENDOR_TABLE_TAG, Info->VendorName)) {
+ Error = NERR_RplVendorNameUnavailable;
+ goto cleanup;
+ }
+
+ CallJ( JetPrepareUpdate( pSession->SesId, pSession->VendorTableId, JET_prepInsert));
+
+ Error = VendorSetInfo( pSession, Level, Buffer, &ErrorParameter);
+ if ( Error == ERROR_SUCCESS) {
+ ErrorParameter = 0;
+ CallJ( JetUpdate( pSession->SesId, pSession->VendorTableId, NULL, 0, NULL));
+ }
+
+cleanup:
+ if ( Error == NO_ERROR) {
+ Call( JetCommitTransaction( pSession->SesId, JET_bitCommitFlush));
+ } else {
+ Call( JetRollback( pSession->SesId, JET_bitRollbackAll));
+ }
+ LeaveCriticalSection( &RG_ProtectDatabase);
+
+ if ( Error != ERROR_SUCCESS) {
+ if ( ARGUMENT_PRESENT( pErrorParameter)) {
+ *pErrorParameter = ErrorParameter;
+ }
+ }
+ return( Error);
+}
+
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetrRplVendorDel(
+ IN RPL_HANDLE ServerHandle,
+ IN LPWSTR VendorName
+ )
+/*++
+ If VendorName is provided then delete matching record, else delete all
+ adapter records.
+--*/
+{
+ DWORD Error;
+ PRPL_SESSION pSession = &RG_ApiSession;
+
+ if ( !ValidHexName( VendorName, RPL_VENDOR_NAME_LENGTH, TRUE)) {
+ return( ERROR_INVALID_PARAMETER);
+ }
+ _wcsupr( VendorName);
+
+ EnterCriticalSection( &RG_ProtectDatabase);
+ Call( JetBeginTransaction( pSession->SesId));
+
+ if ( !RplFind( pSession, VENDOR_TABLE_TAG, VendorName)) {
+ Error = NERR_RplVendorNotFound;
+ goto cleanup;
+ }
+ CallJ( JetDelete( pSession->SesId, pSession->VendorTableId));
+ Error = NO_ERROR;
+
+cleanup:
+ if ( Error == NO_ERROR) {
+ Call( JetCommitTransaction( pSession->SesId, JET_bitCommitFlush));
+ } else {
+ Call( JetRollback( pSession->SesId, JET_bitRollbackAll));
+ }
+ LeaveCriticalSection( &RG_ProtectDatabase);
+ return( Error);
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetrRplVendorEnum(
+ IN RPL_RPC_HANDLE ServerHandle,
+ IN OUT LPRPL_VENDOR_ENUM VendorEnum,
+ IN DWORD PrefMaxLength,
+ OUT LPDWORD TotalEntries,
+ IN OUT LPDWORD pResumeHandle OPTIONAL
+ )
+/*++
+
+Routine Description:
+
+Arguments:
+
+ pServerHandle - ptr to RPL_HANDLE
+
+ InfoStruct - Pointer to a structure that contains the information that
+ RPC needs about the returned data. This structure contains the
+ following information:
+ Level - The desired information level - indicates how to
+ interpret the structure of the returned buffer.
+ EntriesRead - Indicates how many elements are returned in the
+ array of structures that are returned.
+ BufferPointer - Location for the pointer to the array of
+ structures that are being returned.
+
+ PrefMaxLen - Indicates a maximum size limit that the caller will allow
+ for (the return buffer.
+
+ TotalEntries - Pointer to a value that upon return indicates the total
+ number of entries in the "active" database.
+
+ pResumeHandle - Inidcates where to restart the enumeration. This is an
+ optional parameter and can be NULL.
+
+Return Value:
+ NO_ERROR if success.
+
+--*/
+{
+ LPBYTE Buffer;
+ DWORD TypicalSize;
+ DWORD CoreSize;
+ DWORD Error;
+ INT SpaceLeft;
+ DWORD ArrayLength;
+ DWORD EntriesRead;
+ BOOL InfoError;
+ BOOL TableEnd;
+ PRPL_SESSION pSession = &RG_ApiSession;
+
+ switch( VendorEnum->Level) {
+ case 1:
+ TypicalSize = CoreSize = sizeof( RPL_VENDOR_INFO_1);
+ NOTHING; // fall through
+ case 0:
+ if ( VendorEnum->Level == 0) {
+ TypicalSize = CoreSize = sizeof( RPL_VENDOR_INFO_0);
+ }
+ TypicalSize += 20 * sizeof( WCHAR); // typical size of VendorComment
+ TypicalSize += 8 * sizeof( WCHAR); // typical size of VendorName
+ break;
+ default:
+ return( ERROR_INVALID_LEVEL);
+ break;
+ }
+
+ if ( PrefMaxLength == -1) {
+ //
+ // If the caller has not specified a size, calculate a size
+ // that will hold the entire enumeration.
+ //
+ SpaceLeft = DEFAULT_BUFFER_SIZE;
+ } else {
+ SpaceLeft = PrefMaxLength;
+ }
+
+ //
+ // Buffer space is shared by the array and by strings pointed at
+ // by the elements in this array. We need to decide up front how much
+ // space is allocated for array and how much for the strings.
+ //
+ ArrayLength = SpaceLeft / TypicalSize;
+ if ( ArrayLength == 0) {
+ ArrayLength = 1; // try to return at least one element
+ }
+
+ //
+ // Note that MIDL_user_allocate() returns memory which is NOT initialized
+ // to zero. Since we do NOT use allocate all nodes, this means that all
+ // fields, especially pointers, in array elements must be properly set.
+ //
+ Buffer = MIDL_user_allocate( ArrayLength * CoreSize);
+ if ( Buffer == NULL) {
+ return( ERROR_NOT_ENOUGH_MEMORY);
+ }
+ RplDump( RG_DebugLevel & RPL_DEBUG_VENDOR, (
+ "VendorEnum: Buffer=0x%x, ArrayLength=0x%x", Buffer, ArrayLength));
+
+ VendorEnum->VendorInfo.Level0->Buffer = (LPRPL_VENDOR_INFO_0)Buffer;
+
+ EntriesRead = 0;
+ InfoError = FALSE;
+ Error = NO_ERROR;
+
+ EnterCriticalSection( &RG_ProtectDatabase);
+ Call( JetBeginTransaction( pSession->SesId));
+
+ if ( !RplFilterFirst( pSession, VENDOR_TABLE_TAG, NULL, pResumeHandle, &TableEnd)) {
+ Error = NERR_RplCannotEnum;
+ goto cleanup;
+ }
+ if ( TableEnd == TRUE) {
+ goto cleanup;
+ }
+ for ( ; ; ) {
+ memset( Buffer, 0, CoreSize); // for cleanup to work properly
+ Error = VendorGetInfo( pSession, NULL, VendorEnum->Level, Buffer, &SpaceLeft);
+ if ( Error != NO_ERROR) {
+ InfoError = TRUE; // clean things up without holding crit sec
+ break;
+ }
+ EntriesRead++;
+ Buffer += CoreSize;
+ SpaceLeft -= CoreSize;
+ if ( !RplFilterNext( pSession, pSession->VendorTableId, NULL, &TableEnd)) {
+ Error = NERR_RplCannotEnum;
+ goto cleanup;
+ }
+ if ( TableEnd == TRUE) {
+ goto cleanup;
+ }
+ if ( SpaceLeft <= 0) {
+ Error = ERROR_MORE_DATA;
+ break;
+ }
+ if ( EntriesRead >= ArrayLength) {
+ //
+ // We have space available but allocated array is not big enough.
+ // This should NOT happen often as our intent (see above) is to
+ // overestimate array length. When it happens we can still try
+ // to reallocate array to a larger size here. This is not done
+ // for now (too cumbersome) & we just stop the enumeration.
+ //
+ Error = ERROR_MORE_DATA;
+ break;
+ }
+ }
+cleanup:
+ Call( JetCommitTransaction( pSession->SesId, 0));
+ LeaveCriticalSection( &RG_ProtectDatabase);
+ if ( InfoError == TRUE) {
+ VendorGetInfoCleanup( VendorEnum->Level, Buffer);
+ }
+ if ( Error == NO_ERROR) {
+ *TotalEntries = EntriesRead;
+ } else if ( Error == ERROR_MORE_DATA) {
+ *TotalEntries = EntriesRead * 2; // we cheat here
+ } else {
+ //
+ // Cleanup in case of "bad" errors.
+ //
+ while ( EntriesRead > 0) {
+ EntriesRead--;
+ Buffer -= CoreSize;
+ VendorGetInfoCleanup( VendorEnum->Level, Buffer);
+ }
+ MIDL_user_free( Buffer);
+ }
+
+ RplDump( RG_DebugLevel & RPL_DEBUG_VENDOR, ("VendorEnum: EntriesRead = 0x%x", EntriesRead));
+
+ VendorEnum->VendorInfo.Level0->EntriesRead = EntriesRead;
+ if ( EntriesRead == 0) {
+ VendorEnum->VendorInfo.Level0->Buffer = NULL;
+ }
+
+ if ( ARGUMENT_PRESENT( pResumeHandle)) {
+ if ( Error == ERROR_MORE_DATA && EntriesRead > 0) {
+ EnterCriticalSection( &RG_ProtectDatabase);
+ Call( JetBeginTransaction( pSession->SesId));
+ RplFilterSave( pSession, (DWORD)ServerHandle, NULL,
+ ((LPRPL_VENDOR_INFO_0)(Buffer-CoreSize))->VendorName,
+ pResumeHandle);
+ Call( JetCommitTransaction( pSession->SesId, JET_bitCommitFlush));
+ LeaveCriticalSection( &RG_ProtectDatabase);
+ } else {
+ *pResumeHandle = 0; // resume from beginning
+ }
+ }
+ return( Error);
+}
diff --git a/private/net/svcdlls/rpl/server/wcst.c b/private/net/svcdlls/rpl/server/wcst.c
new file mode 100644
index 000000000..2b9aa40be
--- /dev/null
+++ b/private/net/svcdlls/rpl/server/wcst.c
@@ -0,0 +1,54 @@
+/*++
+
+Module Name:
+
+ wcst.c - to verify a fix for "D0H" strings.
+
+Abstract:
+
+
+--*/
+
+
+
+#include "local.h"
+
+DWORD StringToDword( IN PWCHAR String)
+/*++
+ We would like to use generic base (0) but it does not work for
+ strings like "D0H". That is the reason why we first check if
+ the last character is 'H' or 'h'.
+--*/
+{
+ DWORD Length;
+
+ Length = wcslen( String);
+ if ( Length == 0) {
+ return( 0);
+ }
+ if ( String[ Length-1] == L'H' || String[ Length-1] == L'h') {
+ return( wcstoul( String, NULL, 16));
+ } else {
+ return( wcstoul( String, NULL, 0));
+ }
+}
+
+void report ( IN PWCHAR String, IN DWORD Base)
+{
+ PWCHAR End;
+ DWORD Number;
+ Number = wcstoul( String, &End, Base);
+ printf( "String = %ws, End = %ws, Base = %d, Number = 0x%x\n", String, End, Base, Number);
+ printf( "StringToDword(%ws)= 0x%x\n", String, StringToDword( String));
+}
+
+VOID _CRTAPI1 main ( VOID)
+{
+ report( L"D0H", 0); // End = D0H, Number = 0x0
+ report( L"D0H", 16); // End = H, Number = 0xd0
+ report( L"D0", 0); // End = D0, Number = 0x0
+ report( L"D0", 16); // End = , Number = 0xd0
+ report( L"0xD0", 0); // End = , Number = 0xd0
+ report( L"0xD0", 16); // End = , Number = 0xd0
+}
+
diff --git a/private/net/svcdlls/rpl/server/wksta.c b/private/net/svcdlls/rpl/server/wksta.c
new file mode 100644
index 000000000..ef65e9174
--- /dev/null
+++ b/private/net/svcdlls/rpl/server/wksta.c
@@ -0,0 +1,1471 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ wksta.c
+
+Abstract:
+
+ Wksta APIs.
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 19 - November - 1993
+
+Revision History:
+
+--*/
+
+#include "local.h"
+#include "rpldb.h"
+#include "database.h" // RplDbFindWksta()
+#include "db.h"
+#include "dblib.h"
+#include "profile.h"
+#define RPLWKSTA_ALLOCATE
+#include "wksta.h"
+#undef RPLWKSTA_ALLOCATE
+
+
+DWORD WkstaSessionDel( IN PWCHAR WkstaName)
+{
+ DWORD Error;
+ WCHAR UncWkstaName[ MAX_PATH + sizeof(DOUBLE_BACK_SLASH_STRING)];
+
+ memcpy( UncWkstaName, DOUBLE_BACK_SLASH_STRING, sizeof(DOUBLE_BACK_SLASH_STRING));
+ wcscat( UncWkstaName, WkstaName);
+
+ Error = NetSessionDel( NULL, UncWkstaName, NULL);
+ if ( Error == NO_ERROR || Error == NERR_ClientNameNotFound) {
+ return( NO_ERROR);
+ }
+ RplDump( ++RG_Assert, ("Error=%d", Error));
+ return( Error);
+}
+
+
+DWORD WkstaGetField(
+ IN PRPL_SESSION pSession,
+ IN DWORD FieldIndex,
+ OUT LPVOID * pData,
+ IN OUT LPINT pSpaceLeft
+ )
+/*++
+ Currently this routine always allocates buffer for variable length data.
+ It would be nice to relax this assumption - so that buffer gets allocated
+ if and only if *pData is NULL. We would then have to redefine meaning of
+ pSpaceLeft: rename it into pDataSize which on entry points to the size of
+ data buffer & on return to the size of data returned. The caller would
+ then have the responsibility of subtracting the amount of space left.
+
+ Also, various GetField routines should be unified.
+
+ BUGBUG This is too much of a change for this late moment.
+--*/
+{
+ BYTE LocalBuffer[ 300];
+ PBYTE Buffer;
+ DWORD DataSize;
+ DWORD BufferSize;
+ JET_ERR JetError;
+
+ switch( FieldIndex) {
+ case WKSTA_TcpIpAddress:
+ case WKSTA_TcpIpSubnet:
+ case WKSTA_TcpIpGateway:
+ case WKSTA_Flags:
+ Buffer = (PBYTE)pData;
+ BufferSize = sizeof( DWORD);
+ break;
+ default:
+#ifdef NOT_YET
+ if ( *pData == NULL) {
+#endif
+ Buffer = LocalBuffer;
+ BufferSize = sizeof( LocalBuffer);
+#ifdef NOT_YET
+ } else {
+ Buffer = *pData;
+ BufferSize = *pSpaceLeft;
+ }
+#endif
+ break;
+ }
+ JetError = JetRetrieveColumn( pSession->SesId, pSession->WkstaTableId,
+ WkstaTable[ FieldIndex].ColumnId, Buffer,
+ BufferSize, &DataSize, 0, NULL);
+ if ( JetError < 0) {
+ RplDump( ++RG_Assert, ("JetError=%d", JetError));
+ return( NERR_RplWkstaInfoCorrupted);
+ }
+ if ( Buffer != LocalBuffer) {
+ if ( BufferSize == DataSize) {
+ return( NO_ERROR);
+ } else if ( DataSize == 0) {
+ //
+ // This happens if we never defined this value. Set (-1)
+ // as an invalid tcp/ip address.
+ //
+ *(PDWORD)pData = (DWORD)-1;
+ return( NO_ERROR);
+ } else {
+ RplDump( ++RG_Assert, ("Bad DataSize=0x%x", DataSize));
+ return( NERR_RplWkstaInfoCorrupted);
+ }
+ }
+ //
+ // We have done with fixed data. From here on we deal with unicode
+ // strings only.
+ //
+ if ( DataSize > sizeof( LocalBuffer)) {
+ RplDump( ++RG_Assert, ( "Too big DataSize=0x%x", DataSize));
+ return( NERR_RplWkstaInfoCorrupted);
+ }
+ if ( DataSize == 0) {
+ if ( JetError != JET_wrnColumnNull) {
+ RplDump( ++RG_Assert, ( "JetError=%d", JetError));
+ return( NERR_RplWkstaInfoCorrupted);
+ } else {
+ *pData = NULL; // so RPC rpcrt4!_tree_size_ndr() does not bomb here
+ return( NO_ERROR);
+ }
+ }
+ if ( DataSize & 1 != 0 || wcslen((PWCHAR)LocalBuffer) + 1 != DataSize/2) {
+ RplDump( ++RG_Assert, ("LocalBuffer=0x%x, DataSize=0x%x", LocalBuffer, DataSize));
+ return( NERR_RplWkstaInfoCorrupted);
+ }
+ *pData = MIDL_user_allocate( DataSize);
+ if ( *pData == NULL) {
+ RplDump( ++RG_Assert, ( "Error=%d", GetLastError()));
+ return( ERROR_NOT_ENOUGH_MEMORY);
+ }
+ memcpy( *pData, LocalBuffer, DataSize);
+ *pSpaceLeft -= DataSize;
+ return( NO_ERROR);
+}
+
+
+DWORD WkstaGetInfo(
+ IN PRPL_SESSION pSession,
+ IN LPWSTR WkstaName,
+ IN DWORD Level,
+ OUT LPVOID Buffer,
+ IN OUT LPINT pSpaceLeft
+ )
+{
+ DWORD Error;
+ LPRPL_WKSTA_INFO_2 Info = Buffer;
+
+ switch( Level) {
+ case 2:
+ Error = WkstaGetField( pSession, WKSTA_TcpIpGateway, (LPVOID *)&Info->TcpIpGateway, pSpaceLeft);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ Error = WkstaGetField( pSession, WKSTA_TcpIpSubnet, (LPVOID *)&Info->TcpIpSubnet, pSpaceLeft);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ Error = WkstaGetField( pSession, WKSTA_TcpIpAddress, (LPVOID *)&Info->TcpIpAddress, pSpaceLeft);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ Error = WkstaGetField( pSession, WKSTA_AdapterName, &Info->AdapterName, pSpaceLeft);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ Error = WkstaGetField( pSession, WKSTA_FitFile, &Info->FitFile, pSpaceLeft);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ Error = WkstaGetField( pSession, WKSTA_BootName, &Info->BootName, pSpaceLeft);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ NOTHING; // fall through
+ case 1:
+ Error = WkstaGetField( pSession, WKSTA_ProfileName, &Info->ProfileName, pSpaceLeft);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ Error = WkstaGetField( pSession, WKSTA_Flags, (LPVOID *)&Info->Flags, pSpaceLeft);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ NOTHING; // fall through
+ case 0:
+ Error = WkstaGetField( pSession, WKSTA_WkstaComment, &Info->WkstaComment, pSpaceLeft);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ if ( WkstaName == NULL) {
+ Error = WkstaGetField( pSession, WKSTA_WkstaName, &Info->WkstaName, pSpaceLeft);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ } else {
+ DWORD DataSize = (wcslen( WkstaName) + 1) * sizeof(WCHAR);
+ Info->WkstaName = MIDL_user_allocate( DataSize);
+ if ( Info->WkstaName == NULL) {
+ return( ERROR_NOT_ENOUGH_MEMORY);
+ }
+ RplDump( RG_DebugLevel & RPL_DEBUG_WKSTA, ( "WkstaName=0x%x", Info->WkstaName));
+ memcpy( Info->WkstaName, WkstaName, DataSize);
+ *pSpaceLeft -= DataSize;
+ }
+ break;
+ default:
+ return( ERROR_INVALID_LEVEL);
+ break;
+ }
+ return( NO_ERROR);
+}
+
+
+
+VOID WkstaGetInfoCleanup(
+ IN DWORD Level,
+ IN OUT LPVOID Buffer
+ )
+{
+ LPRPL_WKSTA_INFO_2 Info = Buffer;
+ switch( Level) {
+ case 2:
+ if ( Info->AdapterName != NULL) {
+ MIDL_user_free( Info->AdapterName);
+ }
+ if ( Info->FitFile != NULL) {
+ MIDL_user_free( Info->FitFile);
+ }
+ if ( Info->BootName != NULL) {
+ MIDL_user_free( Info->BootName);
+ }
+ NOTHING; // fall through
+ case 1:
+ if ( Info->ProfileName != NULL) {
+ MIDL_user_free( Info->ProfileName);
+ }
+ NOTHING; // fall through
+ case 0:
+ if ( Info->WkstaComment != NULL) {
+ MIDL_user_free( Info->WkstaComment);
+ }
+ if ( Info->WkstaName != NULL) {
+ MIDL_user_free( Info->WkstaName);
+ }
+ break;
+ }
+}
+
+
+
+BOOL WkstaScan(
+ IN PRPL_SESSION pSession,
+ IN LPWSTR ProfileName,
+ IN DWORD ProfileNameSize,
+ IN OUT PBOOL pTableEnd
+ )
+{
+ JET_ERR JetError;
+
+ JetError = JetSeek( pSession->SesId, pSession->WkstaTableId, JET_bitSeekGT);
+ if ( JetError != JET_errSuccess) {
+ RplDump( RG_DebugLevel & RPL_DEBUG_WKSTA, ("WkstaScan: JetSeek => %d", JetError));
+ *pTableEnd = TRUE;
+ return( TRUE);
+ }
+ //
+ // Verify that current wksta has the desired profile & at the
+ // same time set index range to be used with JetMove( Next).
+ //
+ CallB( JetMakeKey( pSession->SesId, pSession->WkstaTableId, ProfileName,
+ ProfileNameSize, JET_bitNewKey));
+ JetError = JetSetIndexRange( pSession->SesId, pSession->WkstaTableId,
+ JET_bitRangeInclusive | JET_bitRangeUpperLimit);
+ if ( JetError != JET_errSuccess) {
+ *pTableEnd = TRUE;
+ }
+ return( TRUE);
+}
+
+
+
+BOOL WkstaResumeFirst(
+ IN PRPL_SESSION pSession,
+ IN LPWSTR ProfileName,
+ IN LPDWORD pResumeHandle,
+ OUT PBOOL pTableEnd
+ )
+/*++
+ Set currency to the first wksta for this profile, following
+ the wksta described by the resume handle.
+--*/
+{
+ BYTE ResumeValue[ RPL_MAX_PROFILE_NAME_SIZE + RPL_MAX_WKSTA_NAME_SIZE];
+ DWORD ResumeSize;
+ JET_ERR JetError;
+ DWORD ProfileNameSize;
+ DWORD WkstaNameSize;
+ WCHAR WkstaName[ RPL_MAX_WKSTA_NAME_LENGTH + 1 + JETBUG_STRING_LENGTH];
+
+ *pTableEnd = FALSE;
+
+ if ( ProfileName == NULL) {
+ CallB( JetSetCurrentIndex( pSession->SesId, pSession->WkstaTableId, WKSTA_INDEX_WkstaName));
+ } else {
+ ProfileNameSize = (wcslen( ProfileName) + 1) * sizeof(WCHAR);
+ CallB( JetSetCurrentIndex( pSession->SesId, pSession->WkstaTableId, WKSTA_INDEX_ProfileNameWkstaName));
+ }
+ //
+ // The call to move to the beginning of the table is not redundant.
+ // E.g. JET_errNoCurrentRecord will be returned in case of empty table.
+ //
+ JetError = JetMove( pSession->SesId, pSession->WkstaTableId, JET_MoveFirst, 0);
+ if ( JetError < 0) {
+ if ( JetError == JET_errRecordNotFound
+ || JetError == JET_errNoCurrentRecord) {
+ *pTableEnd = TRUE;
+ return( TRUE);
+ } else {
+ RplDump( ++RG_Assert, ("JetError=%d", JetError));
+ return( FALSE);
+ }
+ }
+ if ( (ARGUMENT_PRESENT( pResumeHandle)) && *pResumeHandle != 0) {
+ ResumeSize = sizeof( ResumeValue);
+ if ( !ResumeKeyGet( pSession, *pResumeHandle, ResumeValue, &ResumeSize)) {
+ return( FALSE);
+ }
+ if ( ProfileName == NULL) {
+ CallB( JetMakeKey( pSession->SesId, pSession->WkstaTableId, ResumeValue, ResumeSize, JET_bitNewKey));
+ CallB( JetSeek( pSession->SesId, pSession->WkstaTableId, JET_bitSeekGT));
+ return( TRUE);
+ }
+ if ( ProfileNameSize != (wcslen( (PWCHAR)ResumeValue) + 1) * sizeof( WCHAR)
+ || memcmp( ProfileName, ResumeValue, ProfileNameSize)) {
+ RplDump( ++RG_Assert, ("ResumeValue=0x%x"));
+ return( FALSE);
+ }
+ WkstaNameSize = (wcslen( (PWCHAR)(ResumeValue + ProfileNameSize)) + 1) * sizeof(WCHAR);
+ memcpy( WkstaName, ResumeValue + ProfileNameSize, WkstaNameSize);
+ } else {
+ if ( ProfileName == NULL) {
+ return( TRUE);
+ }
+ WkstaNameSize = 0;
+ }
+ CallB( JetMakeKey( pSession->SesId, pSession->WkstaTableId, ProfileName, ProfileNameSize, JET_bitNewKey));
+ if ( WkstaNameSize != 0) {
+#ifdef RPL_JETBUG
+ memcpy( (PBYTE)WkstaName + WkstaNameSize, JETBUG_STRING, JETBUG_STRING_SIZE);
+ WkstaNameSize += JETBUG_STRING_LENGTH * sizeof(WCHAR);
+#endif
+ CallB( JetMakeKey( pSession->SesId, pSession->WkstaTableId, WkstaName, WkstaNameSize, 0));
+ }
+ return( WkstaScan( pSession, ProfileName, ProfileNameSize, pTableEnd));
+}
+
+
+VOID WkstaResumeSave(
+ IN PRPL_SESSION pSession,
+ IN DWORD ServerHandle,
+ IN PWCHAR ProfileName,
+ IN PWCHAR WkstaName,
+ IN PDWORD pResumeHandle
+ )
+{
+ BYTE ResumeBuffer[ 30 * sizeof(WCHAR)];
+ DWORD ResumeSize;
+ DWORD WkstaSize;
+ if ( ProfileName != NULL) {
+ ResumeSize = (wcslen( ProfileName) + 1) * sizeof(WCHAR);
+ memcpy( ResumeBuffer, ProfileName, ResumeSize);
+ } else {
+ ResumeSize = 0;
+ }
+ WkstaSize = ( wcslen( WkstaName) + 1) * sizeof(WCHAR);
+ memcpy( ResumeBuffer + ResumeSize, WkstaName, WkstaSize);
+ ResumeSize += WkstaSize;
+ (VOID)ResumeKeySet( pSession, (DWORD)ServerHandle, ResumeBuffer, ResumeSize, pResumeHandle);
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetrRplWkstaEnum(
+ IN RPL_HANDLE ServerHandle,
+ IN LPWSTR ProfileName,
+ IN OUT LPRPL_WKSTA_ENUM WkstaEnum,
+ IN DWORD PrefMaxLength,
+ OUT LPDWORD TotalEntries,
+ OUT LPDWORD pResumeHandle OPTIONAL
+ )
+/*++
+ For more extensive comments see related code in NetrConfigEnum.
+--*/
+{
+ LPBYTE Buffer;
+ DWORD TypicalSize;
+ DWORD CoreSize;
+ DWORD Error;
+ INT SpaceLeft;
+ DWORD ArrayLength;
+ DWORD EntriesRead;
+ JET_ERR JetError;
+ BOOL InfoError;
+ BOOL TableEnd;
+ PRPL_SESSION pSession = &RG_ApiSession;
+
+ if ( ProfileName != NULL) {
+ _wcsupr( ProfileName);
+ }
+
+ switch( WkstaEnum->Level) {
+ case 2:
+ TypicalSize = CoreSize = sizeof( RPL_WKSTA_INFO_2);
+ TypicalSize += 14 * sizeof( WCHAR); // typical size of AdapterName
+ TypicalSize += 20 * sizeof( WCHAR); // typical size of FitFile
+ TypicalSize += 8 * sizeof( WCHAR); // typical size of BootName
+ NOTHING; // fall through
+ case 1:
+ if ( WkstaEnum->Level == 1) {
+ TypicalSize = CoreSize = sizeof( RPL_WKSTA_INFO_1);
+ }
+ TypicalSize += 8 * sizeof( WCHAR); // typical size of ProfileName
+ NOTHING; // fall through
+ case 0:
+ if ( WkstaEnum->Level == 0) {
+ TypicalSize = CoreSize = sizeof( RPL_WKSTA_INFO_0);
+ }
+ TypicalSize += 8 * sizeof( WCHAR); // typical size of WkstaName
+ TypicalSize += 20 * sizeof( WCHAR); // typical size of WkstaComment
+ break;
+ default:
+ return( ERROR_INVALID_LEVEL);
+ break;
+ }
+
+ if ( PrefMaxLength == -1) {
+ SpaceLeft = DEFAULT_BUFFER_SIZE;
+ } else {
+ SpaceLeft = PrefMaxLength;
+ }
+
+ ArrayLength = SpaceLeft / TypicalSize;
+ if ( ArrayLength == 0) {
+ ArrayLength = 1; // try to return at least one element
+ }
+
+ Buffer = MIDL_user_allocate( ArrayLength * CoreSize);
+ if ( Buffer == NULL) {
+ return( ERROR_NOT_ENOUGH_MEMORY);
+ }
+ RplDump( RG_DebugLevel & RPL_DEBUG_WKSTA, (
+ "WkstaEnum: Buffer=0x%x, ArrayLength=0x%x", Buffer, ArrayLength));
+
+ WkstaEnum->WkstaInfo.Level0->Buffer = (LPRPL_WKSTA_INFO_0)Buffer;
+
+ EntriesRead = 0;
+ InfoError = FALSE;
+ Error = NO_ERROR;
+
+ EnterCriticalSection( &RG_ProtectDatabase);
+ Call( JetBeginTransaction( pSession->SesId));
+
+ if ( !WkstaResumeFirst( pSession, ProfileName, pResumeHandle, &TableEnd)) {
+ Error = NERR_RplCannotEnum;
+ goto cleanup;
+ }
+ if ( TableEnd == TRUE) {
+ goto cleanup;
+ }
+ for ( ; ; ) {
+ memset( Buffer, 0, CoreSize); // for cleanup to work properly
+ Error = WkstaGetInfo( pSession, NULL, WkstaEnum->Level, Buffer, &SpaceLeft);
+ if ( Error != NO_ERROR) {
+ InfoError = TRUE; // clean things up without holding crit sec
+ break;
+ }
+ EntriesRead++;
+ Buffer += CoreSize;
+ SpaceLeft -= CoreSize;
+ JetError = JetMove( pSession->SesId, pSession->WkstaTableId, JET_MoveNext, 0);
+ if ( JetError != JET_errSuccess) {
+ break; // assume end of table
+ }
+ if ( SpaceLeft <= 0) {
+ Error = ERROR_MORE_DATA;
+ break;
+ }
+ if ( EntriesRead >= ArrayLength) {
+ Error = ERROR_MORE_DATA;
+ break;
+ }
+ }
+cleanup:
+ Call( JetCommitTransaction( pSession->SesId, 0));
+ LeaveCriticalSection( &RG_ProtectDatabase);
+ if ( InfoError == TRUE) {
+ WkstaGetInfoCleanup( WkstaEnum->Level, Buffer);
+ }
+ if ( Error == NO_ERROR) {
+ *TotalEntries = EntriesRead;
+ } else if ( Error == ERROR_MORE_DATA) {
+ *TotalEntries = EntriesRead * 2; // we cheat here
+ } else {
+ //
+ // Cleanup in case of "bad" errors.
+ //
+ while ( EntriesRead > 0) {
+ EntriesRead--;
+ Buffer -= CoreSize;
+ WkstaGetInfoCleanup( WkstaEnum->Level, Buffer);
+ }
+ MIDL_user_free( Buffer);
+ }
+
+ RplDump( RG_DebugLevel & RPL_DEBUG_WKSTA, ("WkstaEnum: EntriesRead = 0x%x", EntriesRead));
+
+ WkstaEnum->WkstaInfo.Level0->EntriesRead = EntriesRead;
+ if ( EntriesRead == 0) {
+ WkstaEnum->WkstaInfo.Level0->Buffer = NULL;
+ }
+
+ if ( ARGUMENT_PRESENT( pResumeHandle)) {
+ if ( Error == ERROR_MORE_DATA && EntriesRead > 0) {
+ EnterCriticalSection( &RG_ProtectDatabase);
+ Call( JetBeginTransaction( pSession->SesId));
+ WkstaResumeSave( pSession, (DWORD)ServerHandle, ProfileName,
+ ((LPRPL_WKSTA_INFO_0)(Buffer-CoreSize))->WkstaName,
+ pResumeHandle
+ );
+ Call( JetCommitTransaction( pSession->SesId, JET_bitCommitFlush));
+ LeaveCriticalSection( &RG_ProtectDatabase);
+ } else {
+ *pResumeHandle = 0; // resume from beginning
+ }
+ }
+
+ return( Error);
+}
+
+
+BOOL WkstaFindFirst(
+ IN PRPL_SESSION pSession,
+ IN PWCHAR ProfileName
+ )
+/*++
+ Returns TRUE if it can find a record (the first record) using input
+ profile name. Returns FALSE otherwise.
+
+ We cannot use SetIndexRange technique here because ProfileName is only
+ the first part of an index & we would never find a match.
+--*/
+{
+ DWORD ProfileNameSize;
+ JET_ERR JetError;
+ BYTE Data[ 300];
+ DWORD DataSize;
+
+ CallB( JetSetCurrentIndex( pSession->SesId, pSession->WkstaTableId, WKSTA_INDEX_ProfileNameWkstaName));
+ ProfileNameSize = ( wcslen( ProfileName) + 1) * sizeof(WCHAR);
+ CallB( JetMakeKey( pSession->SesId, pSession->WkstaTableId, ProfileName, ProfileNameSize, JET_bitNewKey));
+ JetError = JetSeek( pSession->SesId, pSession->WkstaTableId, JET_bitSeekGE);
+ if ( JetError < 0) {
+ return( FALSE);
+ }
+ CallB( JetRetrieveColumn( pSession->SesId, pSession->WkstaTableId,
+ WkstaTable[ WKSTA_ProfileName].ColumnId, Data,
+ sizeof( Data), &DataSize, 0, NULL));
+ if ( ProfileNameSize != DataSize) {
+ return( FALSE);
+ }
+ if ( memcmp( ProfileName, Data, DataSize) != 0) {
+ return( FALSE);
+ }
+ return( TRUE); // we found wksta record using this profile
+}
+
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetrRplWkstaGetInfo(
+ IN RPL_HANDLE ServerHandle,
+ IN LPWSTR WkstaName,
+ IN DWORD Level,
+ OUT LPRPL_WKSTA_INFO_STRUCT WkstaInfoStruct
+ )
+{
+ DWORD Error;
+ LPBYTE Buffer;
+ DWORD DataSize;
+ PRPL_SESSION pSession = &RG_ApiSession;
+
+ _wcsupr( WkstaName);
+
+ switch( Level) {
+ case 0:
+ Buffer = MIDL_user_allocate( sizeof( RPL_WKSTA_INFO_0));
+ if ( Buffer == NULL) {
+ return( ERROR_NOT_ENOUGH_MEMORY);
+ }
+ memset( Buffer, 0, sizeof( RPL_WKSTA_INFO_0));
+ WkstaInfoStruct->WkstaInfo0 = (LPRPL_WKSTA_INFO_0)Buffer;
+ break;
+ case 1:
+ Buffer = MIDL_user_allocate( sizeof( RPL_WKSTA_INFO_1));
+ if ( Buffer == NULL) {
+ return( ERROR_NOT_ENOUGH_MEMORY);
+ }
+ memset( Buffer, 0, sizeof( RPL_WKSTA_INFO_1));
+ WkstaInfoStruct->WkstaInfo1 = (LPRPL_WKSTA_INFO_1)Buffer;
+ break;
+ case 2:
+ Buffer = MIDL_user_allocate( sizeof( RPL_WKSTA_INFO_2));
+ if ( Buffer == NULL) {
+ return( ERROR_NOT_ENOUGH_MEMORY);
+ }
+ memset( Buffer, 0, sizeof( RPL_WKSTA_INFO_2));
+ WkstaInfoStruct->WkstaInfo2 = (LPRPL_WKSTA_INFO_2)Buffer;
+ break;
+ default:
+ return( ERROR_INVALID_LEVEL);
+ break;
+ }
+
+ EnterCriticalSection( &RG_ProtectDatabase);
+ Call( JetBeginTransaction( pSession->SesId));
+
+ if ( !RplFind( pSession, WKSTA_TABLE_TAG, WkstaName)) {
+ Error = NERR_RplWkstaNotFound;
+ } else {
+ Error = WkstaGetInfo( pSession, WkstaName, Level, Buffer, &DataSize);
+ }
+
+ Call( JetCommitTransaction( pSession->SesId, 0));
+ LeaveCriticalSection( &RG_ProtectDatabase);
+
+ if ( Error != NO_ERROR) {
+ WkstaGetInfoCleanup( Level, Buffer);
+ switch( Level) {
+ case 0:
+ MIDL_user_free( Buffer);
+ WkstaInfoStruct->WkstaInfo0 = NULL;
+ break;
+ case 1:
+ MIDL_user_free( Buffer);
+ WkstaInfoStruct->WkstaInfo1 = NULL;
+ break;
+ case 2:
+ MIDL_user_free( Buffer);
+ WkstaInfoStruct->WkstaInfo2 = NULL;
+ break;
+ }
+ }
+ return( Error);
+}
+
+
+DWORD WkstaSetField(
+ IN PRPL_SESSION pSession,
+ IN DWORD FieldIndex,
+ IN LPVOID Data,
+ IN DWORD DataSize
+ )
+{
+ CallM( JetSetColumn( pSession->SesId, pSession->WkstaTableId,
+ WkstaTable[ FieldIndex].ColumnId, Data, DataSize, 0, NULL));
+ return( NO_ERROR);
+}
+
+
+DWORD WkstaSetInfo(
+ IN PRPL_SESSION pSession,
+ IN DWORD Level,
+ IN LPVOID Buffer,
+ OUT LPDWORD pErrorParameter
+ )
+/*++
+ -1 used to be a special "do not change value" for TcpIp columns.
+ This was discontinued. The reason is, the only way for an admin
+ to disable TCP/IP on the client, is to set TcpIp columns to -1 which
+ then causes rpl service not to send TCP/IP addresss to rpl client.
+
+ BUGBUG Whole TCP/IP approach has to be revisited, taking DHCP into account.
+--*/
+{
+ LPRPL_WKSTA_INFO_2 Info = Buffer;
+ switch( Level) {
+ case 2:
+// if ( Info->TcpIpGateway != -1) {
+ {
+ *pErrorParameter = WKSTA_TcpIpGateway;
+ CallM( JetSetColumn( pSession->SesId, pSession->WkstaTableId,
+ WkstaTable[ WKSTA_TcpIpGateway].ColumnId,
+ &Info->TcpIpGateway, sizeof( Info->TcpIpGateway), 0, NULL));
+ }
+// if ( Info->TcpIpSubnet != -1) {
+ {
+ *pErrorParameter = WKSTA_TcpIpSubnet;
+ CallM( JetSetColumn( pSession->SesId, pSession->WkstaTableId,
+ WkstaTable[ WKSTA_TcpIpSubnet].ColumnId,
+ &Info->TcpIpSubnet, sizeof( Info->TcpIpSubnet), 0, NULL));
+ }
+// if ( Info->TcpIpAddress != -1) {
+ {
+ *pErrorParameter = WKSTA_TcpIpAddress;
+ CallM( JetSetColumn( pSession->SesId, pSession->WkstaTableId,
+ WkstaTable[ WKSTA_TcpIpAddress].ColumnId,
+ &Info->TcpIpAddress, sizeof( Info->TcpIpAddress), 0, NULL));
+ }
+ if ( Info->AdapterName != NULL) {
+ *pErrorParameter = WKSTA_AdapterName;
+ CallM( JetSetColumn( pSession->SesId, pSession->WkstaTableId,
+ WkstaTable[ WKSTA_AdapterName].ColumnId,
+ Info->AdapterName,
+ ( wcslen( Info->AdapterName) + 1) * sizeof(WCHAR),
+ 0, NULL));
+ }
+ if ( Info->FitFile != NULL) {
+ *pErrorParameter = WKSTA_FitFile;
+ CallM( JetSetColumn( pSession->SesId, pSession->WkstaTableId,
+ WkstaTable[ WKSTA_FitFile].ColumnId,
+ Info->FitFile,
+ ( wcslen( Info->FitFile) + 1) * sizeof(WCHAR),
+ 0, NULL));
+ }
+ if ( Info->BootName != NULL) {
+ *pErrorParameter = WKSTA_BootName;
+ CallM( JetSetColumn( pSession->SesId, pSession->WkstaTableId,
+ WkstaTable[ WKSTA_BootName].ColumnId,
+ Info->BootName,
+ ( wcslen( Info->BootName) + 1) * sizeof(WCHAR),
+ 0, NULL));
+ }
+ NOTHING; // fall through
+ case 1:
+ if ( Info->ProfileName != NULL) {
+ *pErrorParameter = WKSTA_ProfileName;
+ CallM( JetSetColumn( pSession->SesId, pSession->WkstaTableId,
+ WkstaTable[ WKSTA_ProfileName].ColumnId,
+ Info->ProfileName,
+ ( wcslen( Info->ProfileName) + 1) * sizeof(WCHAR),
+ 0, NULL));
+ }
+ if ( Info->Flags != 0) {
+ *pErrorParameter = WKSTA_Flags;
+ CallM( JetSetColumn( pSession->SesId, pSession->WkstaTableId,
+ WkstaTable[ WKSTA_Flags].ColumnId,
+ &Info->Flags, sizeof( Info->Flags), 0, NULL));
+ }
+ NOTHING; // fall through
+ case 0:
+ if ( Info->WkstaComment != NULL) {
+ *pErrorParameter = WKSTA_WkstaComment;
+ CallM( JetSetColumn( pSession->SesId, pSession->WkstaTableId,
+ WkstaTable[ WKSTA_WkstaComment].ColumnId,
+ Info->WkstaComment,
+ ( wcslen( Info->WkstaComment) + 1) * sizeof(WCHAR),
+ 0, NULL));
+ }
+ if ( Info->WkstaName != NULL) {
+ *pErrorParameter = WKSTA_WkstaName;
+ CallM( JetSetColumn( pSession->SesId, pSession->WkstaTableId,
+ WkstaTable[ WKSTA_WkstaName].ColumnId,
+ Info->WkstaName,
+ ( wcslen( Info->WkstaName) + 1) * sizeof(WCHAR),
+ 0, NULL));
+ }
+ break;
+ }
+ return( NO_ERROR);
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetrRplWkstaSetInfo(
+ IN RPL_HANDLE ServerHandle,
+ IN LPWSTR WkstaName,
+ IN DWORD Level,
+ IN LPRPL_WKSTA_INFO_STRUCT WkstaInfoStruct,
+ OUT LPDWORD pErrorParameter OPTIONAL
+ )
+/*++
+ Note that changes at info level 1 (ProfileName) imply that we need to
+ make changes to info level 2 (BootName & FitFile) parameters.
+
+ Here we pay for the redundancy in wksta records.
+--*/
+{
+ DWORD Error;
+ DWORD ErrorParameter;
+ LPVOID Buffer;
+ LPRPL_WKSTA_INFO_2 Info;
+ DWORD Flags;
+ DWORD Sharing;
+ DWORD LogonInput;
+ DWORD Dhcp;
+ DWORD DeleteUserAccount;
+ PWCHAR ProfileName;
+ DWORD TargetSharing;
+ DWORD TargetLogonInput;
+ DWORD TargetDhcp;
+ DWORD TargetDeleteUserAccount;
+ PWCHAR TargetProfileName;
+ PWCHAR TargetWkstaName;
+ INT SpaceLeft;
+ BOOL TreeModified;
+ PWCHAR FitFile;
+ PWCHAR BootName;
+ PRPL_SESSION pSession = &RG_ApiSession;
+
+ ErrorParameter = INVALID_ERROR_PARAMETER;
+ FitFile = NULL;
+ BootName = NULL;
+ TreeModified = FALSE;
+ Sharing = 0;
+ TargetSharing = 0;
+ ProfileName = NULL;
+ TargetProfileName = NULL;
+ _wcsupr( WkstaName);
+ TargetWkstaName = NULL;
+
+ Info = Buffer = WkstaInfoStruct->WkstaInfo2;
+ switch( Level) {
+ case 2:
+ if ( !ValidName( Info->FitFile, RPL_MAX_STRING_LENGTH, FALSE)) {
+ ErrorParameter = WKSTA_FitFile;
+ break;
+ }
+ if ( !ValidHexName( Info->AdapterName, RPL_ADAPTER_NAME_LENGTH, FALSE)) {
+ ErrorParameter = WKSTA_AdapterName;
+ break;
+ }
+ if ( Info->AdapterName != NULL) {
+ _wcsupr( Info->AdapterName);
+ }
+ NOTHING; // fall through
+ case 1:
+ if ( !ValidName( Info->ProfileName, RPL_MAX_PROFILE_NAME_LENGTH, FALSE)) {
+ ErrorParameter = WKSTA_ProfileName;
+ break;
+ }
+ if ( Info->ProfileName != NULL) {
+ _wcsupr( Info->ProfileName);
+ }
+ TargetProfileName = Info->ProfileName;
+ if ( Info->Flags & ~WKSTA_FLAGS_MASK) {
+ ErrorParameter = WKSTA_Flags;
+ break;
+ }
+ TargetSharing = Info->Flags & WKSTA_FLAGS_MASK_SHARING;
+ switch ( TargetSharing) {
+ case 0:
+ case WKSTA_FLAGS_SHARING_TRUE:
+ case WKSTA_FLAGS_SHARING_FALSE:
+ break;
+ default:
+ ErrorParameter = WKSTA_Flags;
+ break;
+ }
+ TargetLogonInput = Info->Flags & WKSTA_FLAGS_MASK_LOGON_INPUT;
+ switch ( TargetLogonInput) {
+ case 0:
+ case WKSTA_FLAGS_LOGON_INPUT_REQUIRED:
+ case WKSTA_FLAGS_LOGON_INPUT_OPTIONAL:
+ case WKSTA_FLAGS_LOGON_INPUT_IMPOSSIBLE:
+ break;
+ default:
+ ErrorParameter = WKSTA_Flags;
+ break;
+ }
+ TargetDhcp = Info->Flags & WKSTA_FLAGS_MASK_DHCP;
+ switch( TargetDhcp) {
+ case 0:
+ case WKSTA_FLAGS_DHCP_TRUE:
+ case WKSTA_FLAGS_DHCP_FALSE:
+ break;
+ default:
+ ErrorParameter = WKSTA_Flags;
+ break;
+ }
+ TargetDeleteUserAccount = Info->Flags & WKSTA_FLAGS_MASK_DELETE;
+ switch( TargetDeleteUserAccount) {
+ case 0:
+ case WKSTA_FLAGS_DELETE_TRUE:
+ case WKSTA_FLAGS_DELETE_FALSE:
+ break;
+ default:
+ ErrorParameter = WKSTA_Flags;
+ break;
+ }
+ if ( ErrorParameter != INVALID_ERROR_PARAMETER) {
+ break;
+ }
+ NOTHING; // fall through
+ case 0:
+ if ( RPL_STRING_TOO_LONG( Info->WkstaComment)) {
+ ErrorParameter = WKSTA_WkstaComment;
+ break;
+ }
+ if ( !ValidName( Info->WkstaName, RPL_MAX_WKSTA_NAME_LENGTH, FALSE)) {
+ ErrorParameter = WKSTA_WkstaName;
+ break;
+ }
+ if ( Info->WkstaName != NULL) {
+ _wcsupr( Info->WkstaName);
+ //
+ // Take new wksta name into consideration only if it is different
+ // than old wksta name (we ignore NOOP requests).
+ //
+ if ( wcscmp( Info->WkstaName, WkstaName) != 0) {
+ TargetWkstaName = Info->WkstaName;
+ }
+ }
+ break;
+ default:
+ return( ERROR_INVALID_LEVEL);
+ break;
+ }
+
+ if ( ErrorParameter != INVALID_ERROR_PARAMETER) {
+ if ( ARGUMENT_PRESENT( pErrorParameter)) {
+ *pErrorParameter = ErrorParameter;
+ }
+ return( ERROR_INVALID_PARAMETER);
+ }
+ ErrorParameter = 0;
+
+ EnterCriticalSection( &RG_ProtectDatabase);
+ Call( JetBeginTransaction( pSession->SesId));
+
+ if ( TargetWkstaName != NULL) {
+ //
+ // Verify that TargetWkstaName is available in the database.
+ // Once we pass this check, disk routines below will nuke any
+ // old stuff in existing TargetWkstaName trees.
+ //
+ if ( RplFind( pSession, WKSTA_TABLE_TAG, TargetWkstaName)) {
+ Error = NERR_RplWkstaNameUnavailable;
+ goto cleanup;
+ }
+ }
+
+ if ( !RplFind( pSession, WKSTA_TABLE_TAG, WkstaName)) {
+ Error = NERR_RplWkstaNotFound;
+ goto cleanup;
+ }
+ Error = WkstaGetField( pSession, WKSTA_Flags, (LPVOID *)&Flags, &SpaceLeft);
+ if ( Error != NO_ERROR) {
+ goto cleanup;
+ }
+ Dhcp = Flags & WKSTA_FLAGS_MASK_DHCP;
+ DeleteUserAccount = Flags & WKSTA_FLAGS_MASK_DELETE;
+ LogonInput = Flags & WKSTA_FLAGS_MASK_LOGON_INPUT;
+ Sharing = Flags & WKSTA_FLAGS_MASK_SHARING;
+
+ if ( TargetSharing != 0 || TargetProfileName != NULL
+ || TargetWkstaName != NULL) {
+ Error = WkstaSessionDel( WkstaName);
+ if ( Error != NO_ERROR) {
+ goto cleanup;
+ }
+ if ( TargetSharing == Sharing) {
+ TargetSharing = 0; // ignore NOOP requests
+ }
+ Error = WkstaGetField( pSession, WKSTA_ProfileName, &ProfileName, &SpaceLeft);
+ if ( Error != NO_ERROR) {
+ goto cleanup;
+ }
+ if ( TargetProfileName != NULL && wcscmp( TargetProfileName, ProfileName) == 0) {
+ TargetProfileName = NULL; // ignore NOOP requests
+ }
+ }
+
+ if ( TargetSharing != 0 || TargetProfileName != NULL) {
+ if ( !RplFind( pSession, PROFILE_TABLE_TAG,
+ TargetProfileName != NULL ? TargetProfileName : ProfileName)) {
+ Error = NERR_RplProfileNotFound;
+ goto cleanup;
+ }
+ if ( TargetProfileName != NULL) {
+ Error = ProfileGetField( pSession, PROFILE_BootName, &BootName, &SpaceLeft);
+ if ( Error != NO_ERROR) {
+ goto cleanup;
+ }
+ if ( !BootFind( pSession, BootName,
+ AdapterNameToVendorId( Info->AdapterName))) {
+ Error = NERR_RplIncompatibleProfile;
+ goto cleanup;
+ }
+ }
+ Error = ProfileGetField( pSession, (TargetSharing != 0 ? TargetSharing : Sharing)
+ == WKSTA_FLAGS_SHARING_TRUE ? PROFILE_FitShared : PROFILE_FitPersonal,
+ &FitFile, &SpaceLeft);
+ if ( Error != NO_ERROR) {
+ goto cleanup;
+ }
+ }
+
+ if ( TargetSharing != 0 || TargetProfileName != NULL
+ || TargetWkstaName != NULL) {
+ //
+ // Create new tree, or add new branches to the current tree.
+ //
+ TreeModified = TRUE; // to undo tree changes in case of errors below
+ Error = WkstaDiskSet( ADD_NEW_BRANCHES, WkstaName, ProfileName, Sharing,
+ TargetWkstaName, TargetProfileName, TargetSharing);
+ if ( Error != NO_ERROR) {
+ goto cleanup;
+ }
+ }
+
+ //
+ // Tree is ready and we can call jet to modify database wksta record.
+ //
+
+ CallJ( JetPrepareUpdate( pSession->SesId, pSession->WkstaTableId, JET_prepReplace));
+ if ( TargetSharing == 0) {
+ //
+ // TargetSharing may have been reset to 0 due to being equal
+ // to Sharing. If so, then the line below is a no-op.
+ //
+ Info->Flags |= Sharing;
+ }
+ if ( TargetLogonInput == 0) {
+ Info->Flags |= LogonInput;
+ }
+ if ( TargetDhcp == 0) {
+ Info->Flags |= Dhcp;
+ }
+ if ( TargetDeleteUserAccount == 0) {
+ Info->Flags |= DeleteUserAccount;
+ }
+ Error = WkstaSetInfo( pSession, Level, Buffer, &ErrorParameter);
+ if ( Error != ERROR_SUCCESS) {
+ goto cleanup;
+ }
+ if ( BootName != NULL) {
+ Error = WkstaSetField( pSession, WKSTA_BootName, BootName,
+ (wcslen(BootName)+1)*sizeof(WCHAR));
+ if ( Error != ERROR_SUCCESS) {
+ goto cleanup;
+ }
+ }
+ if ( FitFile != NULL) {
+ Error = WkstaSetField( pSession, WKSTA_FitFile, FitFile,
+ (wcslen(FitFile)+1)*sizeof(WCHAR));
+ if ( Error != ERROR_SUCCESS) {
+ goto cleanup;
+ }
+ }
+ CallJ( JetUpdate( pSession->SesId, pSession->WkstaTableId, NULL, 0, NULL));
+
+ //
+ // Remove old tree, or old branches in the current tree.
+ //
+ WkstaDiskSet( DEL_OLD_BRANCHES, WkstaName, ProfileName, Sharing,
+ TargetWkstaName, TargetProfileName, TargetSharing);
+
+cleanup:
+ if ( Error != NO_ERROR && TreeModified == TRUE) {
+ //
+ // Remove new tree, or new branches in the current tree.
+ //
+ WkstaDiskSet( DEL_NEW_BRANCHES, WkstaName, ProfileName, Sharing,
+ TargetWkstaName, TargetProfileName, TargetSharing);
+ }
+
+ if ( Error == NO_ERROR) {
+ Call( JetCommitTransaction( pSession->SesId, JET_bitCommitFlush));
+ } else {
+ Call( JetRollback( pSession->SesId, JET_bitRollbackAll));
+ }
+ LeaveCriticalSection( &RG_ProtectDatabase);
+
+ if ( FitFile != NULL) {
+ MIDL_user_free( FitFile);
+ }
+ if ( BootName != NULL) {
+ MIDL_user_free( BootName);
+ }
+ if ( ProfileName != NULL) {
+ MIDL_user_free( ProfileName);
+ }
+ if ( Error != ERROR_SUCCESS) {
+ if ( ARGUMENT_PRESENT( pErrorParameter)) {
+ *pErrorParameter = ErrorParameter;
+ }
+ }
+ return( Error);
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetrRplWkstaAdd(
+ IN RPL_HANDLE ServerHandle,
+ IN DWORD Level,
+ IN LPRPL_WKSTA_INFO_STRUCT WkstaInfoStruct,
+ OUT LPDWORD pErrorParameter OPTIONAL
+ )
+{
+ LPRPL_WKSTA_INFO_2 Info;
+ LPVOID Buffer;
+ DWORD Error;
+ DWORD ErrorParameter;
+ DWORD DataSize;
+ DWORD Sharing;
+ BOOL FreeBootName = FALSE;
+ BOOL FreeFitFile = FALSE;
+ PRPL_SESSION pSession = &RG_ApiSession;
+
+ ErrorParameter = INVALID_ERROR_PARAMETER;
+
+ Buffer = Info = WkstaInfoStruct->WkstaInfo2;
+ switch( Level) {
+ case 2:
+ if ( !ValidHexName( Info->AdapterName, RPL_ADAPTER_NAME_LENGTH, TRUE)) {
+ ErrorParameter = WKSTA_AdapterName;
+ break;
+ }
+ _wcsupr( Info->AdapterName);
+ if ( !ValidName( Info->ProfileName, RPL_MAX_PROFILE_NAME_LENGTH, TRUE)) {
+ ErrorParameter = WKSTA_ProfileName;
+ break;
+ }
+ _wcsupr( Info->ProfileName);
+ if ( Info->Flags & ~WKSTA_FLAGS_MASK) {
+ ErrorParameter = WKSTA_Flags;
+ break;
+ }
+ Sharing = Info->Flags & WKSTA_FLAGS_MASK_SHARING;
+ switch ( Sharing) {
+ case WKSTA_FLAGS_SHARING_TRUE:
+ case WKSTA_FLAGS_SHARING_FALSE:
+ break;
+ default:
+ ErrorParameter = WKSTA_Flags;
+ break;
+ }
+ switch( Info->Flags & WKSTA_FLAGS_MASK_LOGON_INPUT) {
+ case WKSTA_FLAGS_LOGON_INPUT_REQUIRED:
+ case WKSTA_FLAGS_LOGON_INPUT_OPTIONAL:
+ case WKSTA_FLAGS_LOGON_INPUT_IMPOSSIBLE:
+ break;
+ default:
+ ErrorParameter = WKSTA_Flags;
+ break;
+ }
+ switch( Info->Flags & WKSTA_FLAGS_MASK_DHCP) {
+ case WKSTA_FLAGS_DHCP_TRUE:
+ case WKSTA_FLAGS_DHCP_FALSE:
+ break;
+ default:
+ ErrorParameter = WKSTA_Flags;
+ break;
+ }
+ switch( Info->Flags & WKSTA_FLAGS_MASK_DELETE) {
+ case WKSTA_FLAGS_DELETE_TRUE:
+ case WKSTA_FLAGS_DELETE_FALSE:
+ break;
+ default:
+ ErrorParameter = WKSTA_Flags;
+ break;
+ }
+ if ( ErrorParameter != INVALID_ERROR_PARAMETER) {
+ break;
+ }
+ if ( RPL_STRING_TOO_LONG( Info->WkstaComment)) {
+ ErrorParameter = WKSTA_WkstaComment;
+ break;
+ }
+ if ( !ValidName( Info->WkstaName, RPL_MAX_WKSTA_NAME_LENGTH, TRUE)) {
+ ErrorParameter = WKSTA_WkstaName;
+ break;
+ }
+ _wcsupr( Info->WkstaName);
+ break;
+ default:
+ return( ERROR_INVALID_LEVEL);
+ break;
+ }
+
+ if ( ErrorParameter != INVALID_ERROR_PARAMETER) {
+ if ( ARGUMENT_PRESENT( pErrorParameter)) {
+ *pErrorParameter = ErrorParameter;
+ }
+ return( ERROR_INVALID_PARAMETER);
+ }
+
+ EnterCriticalSection( &RG_ProtectDatabase);
+ Call( JetBeginTransaction( pSession->SesId));
+
+ //
+ // Verify that WkstaName is available in the database.
+ //
+ if ( RplFind( pSession, WKSTA_TABLE_TAG, Info->WkstaName)) {
+ Error = NERR_RplWkstaNameUnavailable;
+ goto cleanup;
+ }
+ //
+ // Verify that AdapterName is available in the database.
+ //
+ if ( RplDbFindWksta( pSession, Info->AdapterName)) {
+ Error = NERR_RplAdapterNameUnavailable;
+ goto cleanup;
+ }
+ if ( !RplFind( pSession, PROFILE_TABLE_TAG, Info->ProfileName)) {
+ Error = NERR_RplProfileNotFound;
+ goto cleanup;
+ }
+ //
+ // Verify that there is a boot block record for (AdapterName, ProfileName)
+ // pair. This in turn "validates" AdapterName.
+ //
+ if ( Info->BootName == NULL) {
+ Error = ProfileGetField( pSession, PROFILE_BootName, &Info->BootName, &DataSize);
+ if ( Error != NO_ERROR) {
+ goto cleanup;
+ }
+ FreeBootName = TRUE;
+ }
+ if ( !BootFind( pSession, Info->BootName, AdapterNameToVendorId( Info->AdapterName))) {
+ Error = NERR_RplIncompatibleProfile;
+ goto cleanup;
+ }
+
+ if ( Info->FitFile == NULL) {
+ Error = ProfileGetField( pSession, Sharing == WKSTA_FLAGS_SHARING_TRUE
+ ? PROFILE_FitShared : PROFILE_FitPersonal, &Info->FitFile,
+ &DataSize);
+ if ( Error != NO_ERROR) {
+ goto cleanup;
+ }
+ FreeFitFile = TRUE;
+ }
+
+ CallJ( JetPrepareUpdate( pSession->SesId, pSession->WkstaTableId, JET_prepInsert));
+
+ Error = WkstaSetInfo( pSession, Level, Buffer, &ErrorParameter);
+ if ( Error == ERROR_SUCCESS) {
+ ErrorParameter = 0;
+ CallJ( JetUpdate( pSession->SesId, pSession->WkstaTableId, NULL, 0, NULL));
+ }
+
+ Error = WkstaDiskAdd( TRUE, Info->WkstaName, Info->ProfileName, Sharing);
+ if ( Error != NO_ERROR) {
+ //
+ // WkstaDiskAdd deletion code does not care about ProfileName or Sharing
+ // We do not need to delete the new record since we are going to rollback
+ // the transaction.
+ //
+ WkstaDiskAdd( FALSE, Info->WkstaName, NULL, FALSE);
+ }
+
+cleanup:
+ if ( Error == NO_ERROR) {
+ Call( JetCommitTransaction( pSession->SesId, JET_bitCommitFlush));
+ } else {
+ Call( JetRollback( pSession->SesId, JET_bitRollbackAll));
+ }
+ LeaveCriticalSection( &RG_ProtectDatabase);
+
+ if ( FreeBootName == TRUE) {
+ MIDL_user_free( Info->BootName);
+ Info->BootName = NULL;
+ }
+ if ( FreeFitFile == TRUE) {
+ MIDL_user_free( Info->FitFile);
+ Info->FitFile = NULL;
+ }
+ if ( Error != ERROR_SUCCESS) {
+ if ( ARGUMENT_PRESENT( pErrorParameter)) {
+ *pErrorParameter = ErrorParameter;
+ }
+ }
+ return( Error);
+}
+
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetrRplWkstaDel(
+ IN RPL_HANDLE ServerHandle,
+ IN LPWSTR WkstaName
+ )
+{
+ DWORD Error;
+ PRPL_SESSION pSession = &RG_ApiSession;
+
+ _wcsupr( WkstaName);
+
+ EnterCriticalSection( &RG_ProtectDatabase);
+ Call( JetBeginTransaction( pSession->SesId));
+
+ if ( !RplFind( pSession, WKSTA_TABLE_TAG, WkstaName)) {
+ Error = NERR_RplWkstaNotFound;
+ goto cleanup;
+ }
+ Error = WkstaSessionDel( WkstaName);
+ if ( Error != NO_ERROR) {
+ goto cleanup;
+ }
+ CallJ( JetDelete( pSession->SesId, pSession->WkstaTableId));
+ //
+ // WkstaDiskAdd deletion code does not care about ProfileName or Sharing
+ //
+ WkstaDiskAdd( FALSE, WkstaName, NULL, FALSE);
+
+cleanup:
+ if ( Error == NO_ERROR) {
+ Call( JetCommitTransaction( pSession->SesId, JET_bitCommitFlush));
+ } else {
+ Call( JetRollback( pSession->SesId, JET_bitRollbackAll));
+ }
+ LeaveCriticalSection( &RG_ProtectDatabase);
+ return( Error);
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetrRplWkstaClone(
+ IN RPL_HANDLE ServerHandle,
+ IN LPWSTR SourceWkstaName,
+ IN LPWSTR TargetWkstaName,
+ IN LPWSTR TargetWkstaComment,
+ IN LPWSTR TargetAdapterName,
+ IN DWORD TargetTcpIpAddress
+ )
+{
+ RPL_WKSTA_INFO_2 Info;
+ DWORD Error;
+ DWORD ErrorParameter;
+ DWORD DataSize;
+ PWCHAR SaveWkstaName;
+ PWCHAR SaveWkstaComment;
+ PWCHAR SaveAdapterName;
+ PWCHAR BootName;
+ BOOL FreeBootName = FALSE;
+ INT SpaceLeft;
+ PRPL_SESSION pSession = &RG_ApiSession;
+
+ if ( !ValidName( SourceWkstaName, RPL_MAX_WKSTA_NAME_LENGTH, TRUE)) {
+ return( ERROR_INVALID_PARAMETER);
+ }
+ _wcsupr( SourceWkstaName);
+ if ( !ValidName( TargetWkstaName, RPL_MAX_WKSTA_NAME_LENGTH, TRUE)) {
+ return( ERROR_INVALID_PARAMETER);
+ }
+ _wcsupr( TargetWkstaName);
+ if ( !ValidHexName( TargetAdapterName, RPL_ADAPTER_NAME_LENGTH, TRUE)) {
+ return( ERROR_INVALID_PARAMETER);
+ }
+ _wcsupr( TargetAdapterName);
+ if ( RPL_STRING_TOO_LONG( TargetWkstaComment)) {
+ return( ERROR_INVALID_PARAMETER);
+ }
+
+ //
+ // Zero all the pointers so we can safely call WkstaGetInfoClenaup().
+ // in all the cases.
+ //
+ memset( &Info, 0, sizeof( Info));
+ SaveWkstaName = NULL;
+ SaveWkstaComment = NULL;
+ SaveAdapterName = NULL;
+
+ EnterCriticalSection( &RG_ProtectDatabase);
+ Call( JetBeginTransaction( pSession->SesId));
+
+ //
+ // Verify that TargetWkstaName is available in the database.
+ //
+ if ( RplFind( pSession, WKSTA_TABLE_TAG, TargetWkstaName)) {
+ Error = NERR_RplWkstaNameUnavailable;
+ goto cleanup;
+ }
+ //
+ // Verify that TargetAdapterName is available in the database.
+ //
+ if ( RplDbFindWksta( pSession, TargetAdapterName)) {
+ Error = NERR_RplAdapterNameUnavailable;
+ goto cleanup;
+ }
+ //
+ // Verify that SourceWkstaName exists.
+ //
+ if ( !RplFind( pSession, WKSTA_TABLE_TAG, SourceWkstaName)) {
+ Error = NERR_RplWkstaNotFound;
+ goto cleanup;
+ }
+ Error = WkstaGetInfo( pSession, SourceWkstaName, 2, &Info, &DataSize);
+ if ( Error != NO_ERROR) {
+ goto cleanup;
+ }
+ //
+ // Verify that Source profile is compatible with Target workstation.
+ //
+ if ( !RplFind( pSession, PROFILE_TABLE_TAG, Info.ProfileName)) {
+ Error = NERR_RplProfileNotFound;
+ goto cleanup;
+ }
+ Error = ProfileGetField( pSession, PROFILE_BootName, &BootName, &SpaceLeft);
+ if ( Error != NO_ERROR) {
+ goto cleanup;
+ }
+ FreeBootName = TRUE;
+ if ( !BootFind( pSession, BootName,
+ AdapterNameToVendorId( TargetAdapterName))) {
+ Error = NERR_RplIncompatibleProfile;
+ goto cleanup;
+ }
+ //
+ // Save source data, then overload target data.
+ //
+ SaveWkstaName = Info.WkstaName;
+ SaveWkstaComment = Info.WkstaComment;
+ SaveAdapterName = Info.AdapterName;
+ Info.WkstaName = TargetWkstaName;
+ Info.WkstaComment = TargetWkstaComment;
+ Info.AdapterName = TargetAdapterName;
+ Info.TcpIpAddress = TargetTcpIpAddress;
+
+ CallJ( JetPrepareUpdate( pSession->SesId, pSession->WkstaTableId, JET_prepInsert));
+
+ Error = WkstaSetInfo( pSession, 2, &Info, &ErrorParameter);
+ if ( Error == ERROR_SUCCESS) {
+ CallJ( JetUpdate( pSession->SesId, pSession->WkstaTableId, NULL, 0, NULL));
+ }
+
+ Error = WkstaDiskClone( TRUE, SourceWkstaName, TargetWkstaName);
+ if ( Error != NO_ERROR) {
+ //
+ // Delete new tree. We do not need to delete the new record
+ // since we are going to rollback the transaction.
+ //
+ WkstaDiskClone( FALSE, SourceWkstaName, TargetWkstaName);
+ }
+
+cleanup:
+ if ( Error == NO_ERROR) {
+ Call( JetCommitTransaction( pSession->SesId, JET_bitCommitFlush));
+ } else {
+ Call( JetRollback( pSession->SesId, JET_bitRollbackAll));
+ }
+ LeaveCriticalSection( &RG_ProtectDatabase);
+
+ //
+ // Restore source data, then release it.
+ //
+ Info.WkstaName = SaveWkstaName;
+ Info.WkstaComment = SaveWkstaComment;
+ Info.AdapterName = SaveAdapterName;
+ WkstaGetInfoCleanup( 2, &Info);
+ if ( FreeBootName == TRUE) {
+ MIDL_user_free( BootName);
+ }
+ return( Error);
+}
+
+
diff --git a/private/net/svcdlls/rpl/server/worker.c b/private/net/svcdlls/rpl/server/worker.c
new file mode 100644
index 000000000..8b2b74a55
--- /dev/null
+++ b/private/net/svcdlls/rpl/server/worker.c
@@ -0,0 +1,572 @@
+/*++
+
+Copyright (c) 1987-1993 Microsoft Corporation
+
+Module Name:
+
+ worker.c
+
+Abstract:
+
+ Module is the code of RPL worker thread. Worker thread initializes
+ the connection to a workstation, sends a boot block (defined in
+ in a boot block definition file) to it and dies.
+
+ Provides similar functionality to rplwork.c in LANMAN 2.1 code.
+
+Author:
+
+ Vladimir Z. Vulovic 27 - July - 1993
+
+Environment:
+
+ User mode
+
+Revision History :
+
+--*/
+
+#include "local.h"
+#include "report.h"
+#include "read.h"
+#include "open.h"
+#ifdef RPL_DEBUG
+#include "request.h" // need DebugCheckDList()
+#endif
+#include "worker.h"
+
+DWORD usCurWinSize = 0; // the current window size
+
+
+BOOL Worker( IN OUT PRPL_WORKER_DATA pWorkerData)
+/*++
+
+Routine Description:
+ xxx
+
+Arguments:
+ pWorkerData - ptr to WORKER_DATA structure
+
+Return Value:
+ FALSE if fatal error occurs (configuration errors are fatal), in this
+ case we also set termination event
+ TRUE if success or some of expected network errors occur
+
+--*/
+{
+ POPEN_INFO pOpenInfo;
+ PRCB pRcb;
+ DWORD status;
+ DWORD max_buf_len;
+ DWORD offset;
+ DWORD bytes_read;
+ DWORD wait_ack_timeout; // the timeout of the ack wait
+ DWORD retries;
+ DWORD patch_offset;
+
+ pOpenInfo = pWorkerData->pOpenInfo;
+ pRcb = pWorkerData->pRcb;
+ offset = 0;
+ retries = 0;
+
+ RplDump( RG_DebugLevel & RPL_DEBUG_FLOW,(
+ "++Worker(0x%x): pRcb=0x%x", pWorkerData, pRcb));
+
+ *(PBYTE)&pRcb->flags = 0; // reset the error flags
+
+ //
+ // Send FOUND frame, telling the client that FIND has been received
+ // and we are ready to receive SEND.FILE.REQUEST
+ //
+
+ RplDump( RG_DebugLevel & RPL_DEBUG_WORKER,(
+ "Worker(0x%x): claim client pRcb=0x%x", pWorkerData, pRcb));
+
+ if ( !RplDlcFound( pOpenInfo, pRcb)) {
+ //
+ // Timeout in sending FOUND frames is not fatal. Terminate this
+ // thread but leave RPL server live and well.
+ //
+ RplDump( RG_DebugLevel & RPL_DEBUG_WORKER,(
+ "Worker(0x%x): Found() fails pRcb=0x%x AdapterName=%ws",
+ pWorkerData, pRcb, pRcb->AdapterName));
+ return( TRUE); // so we do not post any events
+ }
+
+ RplDump( RG_DebugLevel & RPL_DEBUG_WORKER,(
+ "Worker(0x%x): RpldFound() => pRcb=0x%x AdapterName=%ws",
+ pWorkerData, pRcb, pRcb->AdapterName));
+
+ //
+ // Wait until client sends Send File Request or we time out.
+ //
+ status = WaitForSingleObject( pRcb->SF_wakeup, GET_SF_REQUEST_TIMEOUT);
+ if ( status == WAIT_TIMEOUT) {
+ //
+ // Non-critical error, client probably chose another server.
+ //
+ RplDump( RG_DebugLevel & RPL_DEBUG_WORKER,(
+ "--Worker(0x%x): timeout while waiting for SFR, pRcb=0x%x",
+ pWorkerData, pRcb));
+ return( TRUE);
+ } else if ( status != ERROR_SUCCESS) {
+ RplDump( ++RG_Assert,( "pRcb=0x%x status=%d", pRcb, status));
+ return( FALSE); // there is nothing we can do here
+ }
+ if ( pRcb->sfr_seq_number != 0) {
+ //
+ // Ignore idiot clients.
+ //
+ RplDump( ++RG_Assert,( "pRcb=0x%x sfr_seq_number=%d", pRcb, pRcb->sfr_seq_number));
+ return( FALSE);
+ }
+ if ( !RplOpenData( pWorkerData)) { // Get this client's data
+ return( FALSE);
+ }
+
+ //
+ // Set up the offset for patching of the boot block header to
+ // address to the rplboot header (CPU independent boot).
+ //
+
+ patch_offset =
+ (DWORD)&(((PRPLBOOT_HEADER)(pWorkerData->jump_address))->phBootBlockHeader)
+ + OFFSET_RPLBOOT_HDR - pWorkerData->base_address;
+
+ // Round max frame down to the next paragraph (otherwise algorithms of
+ // the normal read would be too complicated). Then copy this value to
+ // stack, it's faster.
+
+ max_buf_len = pRcb->max_frame &= 0xfff0;
+ RplDump( RG_DebugLevel & RPL_DEBUG_WORKER,(
+ "Worker(0x%x): pRcb=0x%x max_buf_len=%d",
+ pWorkerData, pRcb, max_buf_len));
+
+ //
+ // Initialize client specific send buffers.
+ //
+ pWorkerData->pDataBuffer = RplMemAlloc( pWorkerData->MemoryHandle, max_buf_len);
+ if ( pWorkerData->pDataBuffer == NULL) {
+ return( FALSE);
+ }
+
+ pWorkerData->send_buf_len = max_buf_len;
+
+ *(PUCHAR)&pRcb->send_flags = 0; // reset all bits in send_flags
+ pRcb->send_flags.locate_enable = TRUE; // for first frame
+
+ //
+ // Read boot block and sent it wksta until all data has been sent.
+ // Only if we succeed we get beyond the bottom of this loop.
+ //
+ for ( ; ;) {
+ LONG tmp_long;
+
+ offset = pRcb->fdr_seq_number * max_buf_len;
+
+ if( !RplReadData( pWorkerData, offset, &bytes_read)) {
+ return( FALSE);
+ }
+ //
+ // RplReadData() uses PDWORD argument but we never expect to read more
+ // than MAXWORD of data.
+ //
+ RPL_ASSERT( HIWORD( bytes_read) == 0);
+
+ // Patch the boot block base address to the rplboot.sys header
+ // (in this way we need no code patching and rplservr can
+ // support any CPU architecture (in principle))
+
+ if ( (DWORD)(tmp_long = patch_offset - offset) < bytes_read
+ && tmp_long >= 0) {
+ *((PDWORD)(pWorkerData->pDataBuffer + (WORD)tmp_long)) = pWorkerData->base_address;
+ }
+
+ //
+ // First frame goes to a specified address, all other frames
+ // are stacked after the last frame received.
+ //
+ pRcb->send_flags.locate_enable = (pRcb->fdr_seq_number == 0);
+
+ //
+ // Check if this is the last frame. Note that last frame with
+ // bytes_read equal to zero is a valid frame to send.
+ //
+ if ( bytes_read < max_buf_len) {
+
+ //
+ // Request acknowledgments for the last packet to make sure
+ // that client really received all the packets. Note that
+ // some clients never acknowledge the last frame and for those
+ // clients this thread will time out after a long wait below.
+ //
+ pRcb->send_flags.ack_request = pWorkerData->FinalAck;
+
+ //
+ // This is the last frame set set transfer control & end of
+ // data bits.
+ //
+ pRcb->send_flags.xfer_enable = TRUE;
+ pRcb->send_flags.end_of_file = TRUE;
+
+ } else {
+
+ //
+ // If WindowSize is nonzero use the adaptive algorithm to decide
+ // when to actually ask client for an acknowledgement.
+ //
+ if ( pWorkerData->WindowSize != 0 && pRcb->fdr_seq_number <
+ pRcb->sfr_seq_number + pWorkerData->WindowSize) {
+ pRcb->send_flags.ack_request = FALSE;
+ } else {
+ pRcb->send_flags.ack_request = TRUE;
+ }
+ //
+ // This is not the last frame so clear transfer control & end of
+ // data bits. These bits could be set otherwise if we've sent
+ // the last frame & now have to resend a "not the last frame".
+ //
+ pRcb->send_flags.xfer_enable = FALSE;
+ pRcb->send_flags.end_of_file = FALSE;
+ }
+
+ //
+ // Check the error status that Send (SendFileRequest) thread may have
+ // set
+ //
+ if ( pRcb->flags.alerted || pRcb->flags.rcberr) {
+ RplDump( ++RG_Assert,( "pRcb=0x%x flags=0x%x", pRcb, pRcb->flags));
+ pWorkerData->EventStrings[ 0] = pWorkerData->WkstaName;
+ pWorkerData->EventId = NELOG_RplWkstaNetwork;
+ return( FALSE);
+ }
+
+ //
+ // We may call FDR twice, if we need an ack and fail to get it
+ // after a short timeout.
+ //
+ for ( wait_ack_timeout = WAIT_ACK_TIMEOUT; ; ) {
+
+ if ( !RplDlcFdr( // was RPL1_Send_Rpl_Data()
+ pOpenInfo,
+ pRcb,
+ pWorkerData->pDataBuffer, // address of send buffer
+ (WORD)bytes_read, // length of data to send
+ pWorkerData->base_address,
+ pWorkerData->jump_address
+ )) {
+ if ( RG_ServiceStatus.dwCurrentState == SERVICE_STOP_PENDING) {
+ return( TRUE); // somebody else initiated service shutdown
+ }
+ pWorkerData->EventStrings[ 0] = pWorkerData->WkstaName;
+ pWorkerData->EventId = NELOG_RplWkstaNetwork;
+ return( FALSE);
+ }
+ if ( !pRcb->send_flags.ack_request) {
+ //
+ // No need to wait for an acknowledgement. Update send
+ // sequence number & retry count.
+ //
+ retries = 0;
+ pRcb->fdr_seq_number++;
+ break;
+ }
+
+ //
+ // Need to wait for an acknowledgement. Wait for a very short
+ // time, then retry & wait for a very long time.
+ //
+ //
+ status = WaitForSingleObject( pRcb->SF_wakeup, wait_ack_timeout);
+ if ( status == 0) {
+ //
+ // Received an acknowledgment. Update send sequence
+ // number & retry count.
+ //
+ if ( pRcb->fdr_seq_number + 1 == pRcb->sfr_seq_number) {
+ //
+ // Case of orderly progression.
+ //
+ retries = 0;
+ pRcb->fdr_seq_number++;
+ } else {
+ if ( ++retries > MAX_RETRIES) {
+ RplDump( ++RG_Assert,( "pRcb=0x%x", pRcb));
+ pWorkerData->EventStrings[ 0] = pWorkerData->WkstaName;
+ pWorkerData->EventId = NELOG_RplWkstaNetwork;
+ return( FALSE); // out of sync for too long
+ }
+ RplDump( RG_DebugLevel & RPL_DEBUG_MISC,(
+ "Worker(0x%x): pRcb=0x%x, sfr=%d fdr=%d",
+ pWorkerData, pRcb, pRcb->sfr_seq_number,
+ pRcb->fdr_seq_number));
+ pRcb->fdr_seq_number = pRcb->sfr_seq_number;
+ }
+ break;
+ }
+
+ if ( status != WAIT_TIMEOUT) {
+ RplDump( ++RG_Assert,( "pRcb=0x%x, status=0x%x", pRcb, status));
+ pWorkerData->EventStrings[ 0] = pWorkerData->WkstaName;
+ pWorkerData->EventId = NELOG_RplWkstaInternal;
+ return( FALSE);
+ }
+
+ if ( wait_ack_timeout == LONG_WAIT_ACK_TIMEOUT) {
+ //
+ // Some RPL rom chips such as LANWORKS BootWare chip never
+ // acknowledge the last frame. This is why we log an event
+ // only if not the last frame.
+ // For now we do not assert here because some clients
+ // may fail to send an ack from time to time.
+ //
+ if ( bytes_read >= max_buf_len) {
+ RplDump( RG_DebugLevel & RPL_DEBUG_SFR,(
+ "--Worker(0x%x): pRcb=0x%x Name=%ws sfr=0x%x fdr=0x%x",
+ pWorkerData, pRcb, pWorkerData->WkstaName,
+ pRcb->sfr_seq_number, pRcb->fdr_seq_number));
+ pWorkerData->EventStrings[ 0] = pWorkerData->WkstaName;
+ pWorkerData->EventId = NELOG_RplWkstaTimeout;
+ }
+ return( FALSE);
+ }
+
+ //
+ // Resend the packet again & then wait with a larger timeout.
+ //
+ wait_ack_timeout = LONG_WAIT_ACK_TIMEOUT;
+ }
+
+ if ( bytes_read < max_buf_len) {
+ break; // we have just sent the last frame
+ }
+ }
+ RplDump( RG_DebugLevel & RPL_DEBUG_FLOW,(
+ "--Worker(0x%x): pRcb=0x%x", pWorkerData, pRcb));
+ return( TRUE);
+}
+
+
+
+VOID RplWorkerThread( IN OUT PRPL_WORKER_PARAMS pWorkerParams)
+/*++
+
+Routine Description:
+ Worker thread. It sends a boot block to a workstation.
+
+ This thread depends on Request thread being around.
+ This is why at shutdown time each request thread must wait for
+ all of its worker threads to die before it dies itself.
+
+Arguments:
+ pWorkerParams - pointer to worker parameters structure
+
+Return Value:
+ None.
+
+--*/
+{
+ PRPL_REQUEST_PARAMS pRequestParams;
+ HANDLE MemoryHandle;
+ PRPL_WORKER_DATA pWorkerData;
+ PRCB pRcb;
+ DWORD status;
+
+ RplDump( RG_DebugLevel & RPL_DEBUG_FLOW,( "++WorkerThread(0x%x)", pWorkerParams));
+
+ pRcb = pWorkerParams->pRcb;
+ pRequestParams = pWorkerParams->pRequestParams;
+ pWorkerData = NULL;
+ MemoryHandle = NULL;
+
+ EnterCriticalSection( &RG_ProtectWorkerCount);
+ RG_WorkerCount++;
+#ifdef RPL_DEBUG
+ RG_BootCount++;
+#endif
+ LeaveCriticalSection( &RG_ProtectWorkerCount);
+
+ //
+ // Let each worker have its own memory space.
+ //
+ status = RplMemInit( &MemoryHandle);
+ if ( status != ERROR_SUCCESS) {
+ RplReportEvent( NELOG_RplAdapterResource, pRcb->AdapterName, 0, NULL);
+ goto cleanup;
+ }
+ //
+ // Build up the data block for worker.
+ //
+ pWorkerData = RplMemAlloc( MemoryHandle, sizeof(RPL_WORKER_DATA));
+ if( pWorkerData == NULL) {
+ RplReportEvent( NELOG_RplAdapterResource, pRcb->AdapterName, 0, NULL);
+ goto cleanup;
+ }
+ memset( pWorkerData, 0, sizeof(RPL_WORKER_DATA));
+
+ pWorkerData->pRcb = pRcb;
+ pWorkerData->pRequestParams = pRequestParams;
+ pWorkerData->pOpenInfo = pRequestParams->pOpenInfo;
+ pWorkerData->MemoryHandle = MemoryHandle;
+
+ pRcb->Pending = FALSE; // allow SFR thread to work on this RCB
+
+ //
+ // Transmit semaphore must be created before OK Claim !!!!
+ //
+ pRcb->txsemhdl = CreateEvent(
+ NULL, // no security attributes
+ FALSE, // use automatic reset
+ TRUE, // initial state is signaled
+ NULL // event has no name
+ );
+ if ( pRcb->txsemhdl == NULL) {
+ RplDump( ++RG_Assert,( "CreateEvent() => status=%d", GetLastError()));
+ RplReportEvent( NELOG_RplAdapterResource, pRcb->AdapterName, 0, NULL);
+ goto cleanup;
+ }
+
+ if ( !Worker( pWorkerData)) {
+ if ( pWorkerData->EventId != NO_ERROR) {
+ RplReportEventEx( pWorkerData->EventId, pWorkerData->EventStrings);
+ }
+ goto cleanup;
+ }
+
+cleanup:
+
+ //
+ // It is OK to remove RCB from BusyRcbList only if SFR thread is not
+ // working on it.
+ //
+ for ( ; ;) {
+ EnterCriticalSection( &RG_ProtectRcbList);
+ if ( pRcb->SFR == FALSE) {
+ RplRemoveRcb( &pRequestParams->BusyRcbList, pRcb);
+ RplInsertRcb( &pRequestParams->FreeRcbList, pRcb);
+ LeaveCriticalSection( &RG_ProtectRcbList);
+ break;
+ }
+ LeaveCriticalSection( &RG_ProtectRcbList);
+ Sleep( 100); // wait 0.1 sec, give SFR thread a chance to finish up
+ }
+
+ if ( pRcb->txsemhdl != NULL) {
+ if ( !CloseHandle( pRcb->txsemhdl)) {
+ RplDump( ++RG_Assert,( "pRcb=0x%x, status=%d", pRcb, GetLastError()));
+ // There is nothing meaningfull to log here.
+ }
+ pRcb->txsemhdl = NULL;
+ }
+
+ if ( MemoryHandle != NULL) {
+ if ( pWorkerData != NULL) {
+ //
+ // Close open file handles and system semaphores which may be
+ // left open after an error.
+ //
+ if ( pWorkerData->hFile != INVALID_HANDLE_VALUE) {
+ (VOID)CloseHandle( pWorkerData->hFile);
+ }
+ RplMemFree( MemoryHandle, pWorkerData);
+ }
+ RplMemClose( MemoryHandle);
+ }
+
+ EnterCriticalSection( &RG_ProtectWorkerCount);
+ RG_WorkerCount--;
+ LeaveCriticalSection( &RG_ProtectWorkerCount);
+
+ //
+ // We have to set RG_EventWorkerCount too, otherwise request thread may
+ // wait for ever to be notified that RG_WorkerCount has been decreased
+ // below RG_MaxWorkerCount. Reseting this event after we exit critical
+ // section does not lead to deadlocks (and is also better for performance).
+ //
+ if ( !SetEvent( RG_EventWorkerCount)) {
+ RplDump( ++RG_Assert, ("Error=%d", GetLastError()));
+ }
+
+ RplDump( RG_DebugLevel & RPL_DEBUG_FLOW,( "--WorkerThread(0x%x)", pWorkerData));
+
+ //
+ // Let request thread know that we will be out shortly.
+ //
+ pWorkerParams->Exiting = TRUE;
+}
+
+
+
+VOID RplRemoveRcb(
+ IN OUT PPRCB HeadList,
+ IN OUT PRCB pRcb
+ )
+/*++
+
+Routine Description:
+ Dequeus RCB.
+
+Arguments:
+ HeadList - head of a queue
+ pRcb - rcb to me dequeued
+
+Return Value:
+ None.
+
+--*/
+{
+ PRCB pTempRcb;
+
+ RplDump( RG_DebugLevel & RPL_DEBUG_WORKER,(
+ "RemoveRcb: pRcb=0x%x, list=0x%x", pRcb, HeadList));
+
+ DebugCheckList( HeadList, pRcb, RPL_MUST_FIND);
+
+ if (*HeadList == pRcb) {
+
+ *HeadList = pRcb->next_rcb;
+
+ } else {
+
+ for ( pTempRcb = *HeadList; pTempRcb != NULL; pTempRcb = pTempRcb->next_rcb) {
+
+ if ( pTempRcb->next_rcb == pRcb) {
+ pTempRcb->next_rcb = pRcb->next_rcb;
+ break;
+ }
+ }
+ }
+ DebugCheckList( HeadList, pRcb, RPL_MUST_NOT_FIND);
+}
+
+
+
+VOID RplInsertRcb(
+ IN OUT PPRCB HeadList,
+ IN OUT PRCB pRcb
+ )
+/*++
+
+Routine Description:
+ Queues RCB.
+
+Arguments:
+ HeadList - head of a queue
+ pRcb - rcb to me dequeued
+
+Return Value:
+ None.
+
+--*/
+{
+ RplDump( RG_DebugLevel & RPL_DEBUG_WORKER,(
+ "InsertRcb: pRcb=0x%x, list=0x%x", pRcb, HeadList));
+
+ DebugCheckList( HeadList, pRcb, RPL_MUST_NOT_FIND);
+ pRcb->next_rcb = *HeadList;
+ *HeadList = pRcb;
+ DebugCheckList( HeadList, pRcb, RPL_MUST_FIND);
+}
+
+
+
diff --git a/private/net/svcdlls/rpl/server/worker.h b/private/net/svcdlls/rpl/server/worker.h
new file mode 100644
index 000000000..e6d4fe334
--- /dev/null
+++ b/private/net/svcdlls/rpl/server/worker.h
@@ -0,0 +1,35 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ worker.h
+
+Abstract:
+
+ Exports from worker.c
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 19 - November - 1993
+
+Environment:
+
+ User mode
+
+Revision History :
+
+--*/
+
+VOID RplRemoveRcb(
+ IN OUT PPRCB HeadList,
+ IN OUT PRCB pRcb
+ );
+VOID RplInsertRcb(
+ IN OUT PPRCB HeadList,
+ IN OUT PRCB pRcb
+ );
+
+VOID RplWorkerThread( IN OUT PRPL_WORKER_PARAMS pWorkerParams);
+
diff --git a/private/net/svcdlls/srvsvc/adtcomn.h b/private/net/svcdlls/srvsvc/adtcomn.h
new file mode 100644
index 000000000..1bdc54eab
--- /dev/null
+++ b/private/net/svcdlls/srvsvc/adtcomn.h
@@ -0,0 +1,92 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ adtdbg.h
+
+Abstract:
+
+ Contains definitions used in debugging the messenger service.
+
+Author:
+
+ Dan Lafferty (danl) 25-Mar-1993
+
+Environment:
+
+ User Mode -Win32
+
+Revision History:
+
+
+--*/
+
+#ifndef _ADTDBG_INCLUDED
+#define _ADTDBG_INCLUDED
+
+//
+// Debug macros and constants.
+//
+#if DBG
+
+#define STATIC
+
+#else
+
+#define STATIC static
+
+#endif
+
+extern DWORD AdtsvcDebugLevel;
+
+//
+// The following allow debug print syntax to look like:
+//
+// SC_LOG1(DEBUG_TRACE, "An error occured %x\n",status)
+//
+
+#if DBG
+#define ADT_LOG0(level,string) \
+ if( AdtsvcDebugLevel & (DEBUG_ ## level)){ \
+ (VOID) KdPrint(("[ADT]")); \
+ (VOID) KdPrint((string)); \
+ }
+#define ADT_LOG1(level,string,var) \
+ if( AdtsvcDebugLevel & (DEBUG_ ## level)){ \
+ (VOID)KdPrint(("[ADT]")); \
+ (VOID)KdPrint((string,var)); \
+ }
+#else
+
+#define ADT_LOG0(level,string)
+#define ADT_LOG1(level,string,var)
+
+#endif
+
+#define DEBUG_NONE 0x00000000
+#define DEBUG_ERROR 0x00000001
+#define DEBUG_TRACE 0x00000002
+#define DEBUG_LOCKS 0x00000004
+
+#define DEBUG_ALL 0xffffffff
+
+
+DWORD
+PrivateGetFileSecurity (
+ LPWSTR FileName,
+ SECURITY_INFORMATION RequestedInfo,
+ PSECURITY_DESCRIPTOR *pSDBuffer,
+ LPDWORD pBufSize
+ );
+
+DWORD
+PrivateSetFileSecurity (
+ LPWSTR FileName,
+ SECURITY_INFORMATION SecurityInfo,
+ PSECURITY_DESCRIPTOR pSecurityDescriptor
+ );
+
+#endif // _ADTDBG_INCLUDED
+
diff --git a/private/net/svcdlls/srvsvc/client/adtwrap.c b/private/net/svcdlls/srvsvc/client/adtwrap.c
new file mode 100644
index 000000000..3b594d68e
--- /dev/null
+++ b/private/net/svcdlls/srvsvc/client/adtwrap.c
@@ -0,0 +1,561 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ adtwrap.c
+
+Abstract:
+
+ These are the Admin Tools Service API RPC client stubs.
+
+Author:
+
+ Dan Lafferty (danl) 25-Mar-1993
+
+Environment:
+
+ User Mode - Win32
+
+Revision History:
+
+ 25-Mar-1993 Danl
+ Created
+
+--*/
+
+//
+// INCLUDES
+//
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h> // needed for windows.h when I have nt.h
+#include <windows.h>
+
+#include <srvsvc.h> // MIDL generated - includes windows.h & rpc.h
+
+#include <rpc.h>
+#include <lmcons.h>
+#include <lmerr.h> // NERR_ error codes
+#include <lmuse.h> // LPUSE_INFO_0
+#include <lmapibuf.h> // NetApiBufferFree
+#include <adtcomn.h>
+
+//
+// GLOBALS
+//
+ DWORD AdtsvcDebugLevel = DEBUG_ERROR;
+
+//
+// LOCAL PROTOTYPES
+//
+
+DWORD
+AdtParsePathName(
+ LPWSTR lpPathName,
+ LPWSTR *pNewFileName,
+ LPWSTR *pServerName,
+ LPWSTR *pShareName
+ );
+
+LPWSTR
+AdtFindNextToken(
+ WCHAR Token,
+ LPWSTR String,
+ LPDWORD pNumChars
+ );
+
+
+DWORD
+NetpGetFileSecurity(
+ IN LPWSTR lpFileName,
+ IN SECURITY_INFORMATION RequestedInformation,
+ OUT PSECURITY_DESCRIPTOR *pSecurityDescriptor,
+ OUT LPDWORD pnLength
+ )
+
+/*++
+
+Routine Description:
+
+ This function returns to the caller a copy of the security descriptor
+ protecting a file or directory.
+
+ NOTE: The buffer containing the security descriptor is allocated for
+ the caller. It is the caller's responsibility to free the buffer by
+ calling the NetApiBufferFree() function.
+
+Arguments:
+
+ lpFileName - A pointer to the name fo the file or directory whose
+ security is being retrieved.
+
+ SecurityInformation - security information being requested.
+
+ pSecurityDescriptor - A pointer to a location where the pointer
+ to the security descriptor is to be placed. The security
+ descriptor is returned in the self-relative format.
+
+ pnLength - The size, in bytes, of the returned security descriptor.
+
+
+Return Value:
+
+ NO_ERROR - The operation was successful.
+
+ ERROR_NOT_ENOUGH_MEMORY - Unable to allocate memory for the security
+ descriptor.
+
+ This function can also return any error that GetFileSecurity can
+ return.
+
+--*/
+{
+ NET_API_STATUS status;
+ ADT_SECURITY_DESCRIPTOR returnedSD;
+ PADT_SECURITY_DESCRIPTOR pReturnedSD;
+ LPWSTR pServerName;
+ LPWSTR pShareName;
+ LPWSTR pNewFileName;
+
+ RpcTryExcept {
+ //
+ // Pick the server name out of the filename. Or translate the
+ // local drive name into a \\servername\sharename.
+ //
+
+ status = AdtParsePathName(lpFileName,&pNewFileName,&pServerName,&pShareName);
+ }
+ RpcExcept (1) {
+ //
+ // Get RPC exception code.
+ //
+ status = RpcExceptionCode();
+
+ }
+ RpcEndExcept
+ if (status != NO_ERROR) {
+ LocalFree(pServerName);
+ return(status);
+ }
+
+ if (pServerName == NULL) {
+ //
+ // Call Locally.
+ //
+ ADT_LOG0(TRACE,"Call Local version (PrivateGetFileSecurity)\n");
+
+ status = PrivateGetFileSecurity (
+ lpFileName,
+ RequestedInformation,
+ pSecurityDescriptor,
+ pnLength
+ );
+ return(status);
+ }
+ //
+ // This is a remote call - - use RPC
+ //
+ //
+ // Initialize the fields in the structure so that RPC does not
+ // attempt to marshall anything on input.
+ //
+ ADT_LOG0(TRACE,"Call Remote version (NetrpGetFileSecurity)\n");
+ returnedSD.Length = 0;
+ returnedSD.Buffer = NULL;
+
+ RpcTryExcept {
+
+ pReturnedSD = NULL;
+ status = NetrpGetFileSecurity (
+ pServerName,
+ pShareName,
+ pNewFileName,
+ RequestedInformation,
+ &pReturnedSD);
+
+ }
+ RpcExcept (1) {
+ //
+ // Get RPC exception code.
+ //
+ status = RpcExceptionCode();
+
+ }
+ RpcEndExcept
+
+ if (status == NO_ERROR) {
+ *pSecurityDescriptor = pReturnedSD->Buffer;
+ *pnLength = pReturnedSD->Length;
+ }
+ LocalFree(pServerName);
+
+ return (status);
+}
+
+DWORD
+NetpSetFileSecurity (
+ IN LPWSTR lpFileName,
+ IN SECURITY_INFORMATION SecurityInformation,
+ IN PSECURITY_DESCRIPTOR pSecurityDescriptor
+ )
+
+/*++
+
+Routine Description:
+
+ This function can be used to set the security of a file or directory.
+
+Arguments:
+
+ ServerName - A pointer to a string containing the name of the remote
+ server on which the function is to execute. A NULL pointer or
+ string specifies the local machine.
+
+ lpFileName - A pointer to the name of the file or directory whose
+ security is being changed.
+
+ SecurityInformation - information describing the contents
+ of the Security Descriptor.
+
+ pSecurityDescriptor - A pointer to a well formed Security Descriptor.
+
+Return Value:
+
+ NO_ERROR - The operation was successful.
+
+ This function can also return any error that SetFileSecurity can
+ return.
+
+--*/
+{
+ DWORD status= NO_ERROR;
+ NTSTATUS ntStatus=STATUS_SUCCESS;
+ ADT_SECURITY_DESCRIPTOR descriptorToPass;
+ DWORD nSDLength;
+ LPWSTR pNewFileName=NULL;
+ LPWSTR pServerName=NULL;
+ LPWSTR pShareName;
+
+ nSDLength = 0;
+
+ RpcTryExcept {
+ //
+ // Pick the server name out of the filename. Or translate the
+ // local drive name into a \\servername\sharename.
+ //
+
+ status = AdtParsePathName(lpFileName,&pNewFileName,&pServerName,&pShareName);
+ }
+ RpcExcept (1) {
+ //
+ // Get RPC exception code.
+ //
+ status = RpcExceptionCode();
+
+ }
+ RpcEndExcept
+
+ if (status != NO_ERROR) {
+ if (pServerName != NULL) {
+ LocalFree(pServerName);
+ }
+ return(status);
+ }
+
+ if (pServerName == NULL) {
+ //
+ // Call Locally and return result.
+ //
+ status = PrivateSetFileSecurity (
+ lpFileName,
+ SecurityInformation,
+ pSecurityDescriptor);
+ return(status);
+ }
+
+ //
+ // Call remotely
+ //
+
+ RpcTryExcept {
+ //
+ // Force the Security Descriptor to be self-relative if it is not
+ // already.
+ // The first call to RtlMakeSelfRelativeSD is used to determine the
+ // size.
+ //
+ ntStatus = RtlMakeSelfRelativeSD(
+ pSecurityDescriptor,
+ NULL,
+ &nSDLength);
+
+ if (ntStatus != STATUS_BUFFER_TOO_SMALL) {
+ status = RtlNtStatusToDosError(ntStatus);
+ goto CleanExit;
+ }
+ descriptorToPass.Length = nSDLength;
+ descriptorToPass.Buffer = LocalAlloc (LMEM_FIXED,nSDLength);
+
+ if (descriptorToPass.Buffer == NULL) {
+ status = ERROR_NOT_ENOUGH_MEMORY;
+ goto CleanExit;
+ }
+ //
+ // Make an appropriate self-relative security descriptor.
+ //
+ ntStatus = RtlMakeSelfRelativeSD(
+ pSecurityDescriptor,
+ descriptorToPass.Buffer,
+ &nSDLength);
+
+ if (ntStatus != NO_ERROR) {
+ LocalFree (descriptorToPass.Buffer);
+ status = RtlNtStatusToDosError(ntStatus);
+ goto CleanExit;
+ }
+
+ status = NetrpSetFileSecurity (
+ pServerName,
+ pShareName,
+ pNewFileName,
+ SecurityInformation,
+ &descriptorToPass);
+
+ LocalFree (descriptorToPass.Buffer);
+
+CleanExit:
+ ;
+ }
+ RpcExcept (1) {
+ //
+ // Get RPC exception code.
+ //
+ status = RpcExceptionCode();
+
+ }
+ RpcEndExcept
+ LocalFree(pServerName);
+ return (status);
+
+}
+
+
+DWORD
+AdtParsePathName(
+ LPWSTR lpPathName,
+ LPWSTR *pNewFileName,
+ LPWSTR *pServerName,
+ LPWSTR *pShareName
+ )
+
+/*++
+
+Routine Description:
+
+ NOTE: This function allocates memory when the path contains a remote name.
+ The pShareName and pServerName strings are in a single buffer that is
+ to be freed at pServerName.
+
+ pNewFileName is NOT allocate by this routine. It points to a substring
+ within the passed in lpPathName.
+
+Arguments:
+
+ lpPathName - This is a pointer to the filename-path string. It can have
+ any of the following formats:
+
+ x:\filedir\file.nam (remote)
+ \\myserver\myshare\filedir\file.nam (remote)
+ filedir\file.nam (local)
+
+ This could also just contain a directory name (and not a filename).
+
+ pNewFileName - This is a location where a pointer to the buffer
+ containing the file name can be placed. This will just contain the
+ filename or directory name relative to the root directory.
+
+ pServerName - This is a location where a pointer to the buffer containing
+ the server name can be placed. If this is for the local machine, then
+ a NULL will be placed in this location.
+
+ pShareName - This is a location where a pointer to a buffer containing
+ the share name can be placed. If this is for the local machine, then
+ a NULL will be placed in this location.
+
+Return Value:
+
+
+--*/
+#define REMOTE_DRIVE 0
+#define REMOTE_PATH 1
+#define LOCAL 2
+{
+ DWORD status = NO_ERROR;
+ NET_API_STATUS netStatus=NERR_Success;
+ WCHAR useName[4];
+ LPUSE_INFO_0 pUseInfo=NULL;
+ LPWSTR pNewPathName=NULL;
+ DWORD DeviceType = LOCAL;
+ LPWSTR pPrivateServerName;
+ LPWSTR pPrivateShareName;
+ LPWSTR pEnd;
+ DWORD numServerChars;
+ DWORD numChars;
+ WCHAR token;
+
+ *pServerName = NULL;
+ *pShareName = NULL;
+ //
+ // If the fileName starts with a drive letter, then use NetUseGetInfo
+ // to get the remote name.
+ //
+ if (lpPathName[1] == L':') {
+ if (((L'a' <= lpPathName[0]) && (lpPathName[0] <= L'z')) ||
+ ((L'A' <= lpPathName[0]) && (lpPathName[0] <= L'Z'))) {
+ //
+ // This is in the form of a local device. Get the server/sharename
+ // associated with this device.
+ //
+ wcsncpy(useName, lpPathName, 2);
+ useName[2]=L'\0';
+ netStatus = NetUseGetInfo(
+ NULL, // server name
+ useName, // use name
+ 0, // level
+ (LPBYTE *)&pUseInfo); // buffer
+
+ if (netStatus != NERR_Success) {
+ //
+ // if we get NERR_UseNotFound back, then this must be
+ // a local drive letter, and not a redirected one.
+ // In this case we return success.
+ //
+ if (netStatus == NERR_UseNotFound) {
+ return(NERR_Success);
+ }
+ return(netStatus);
+ }
+ DeviceType = REMOTE_DRIVE;
+ pNewPathName = pUseInfo->ui0_remote;
+ }
+ }
+ else {
+ if (wcsncmp(lpPathName,L"\\\\",2) == 0) {
+ DeviceType = REMOTE_PATH;
+ pNewPathName = lpPathName;
+ }
+ }
+ if (DeviceType != LOCAL) {
+
+ //
+ // Figure out how many characters for the server and share portion
+ // of the string.
+ // Add 2 characters for the leading "\\\\", allocate a buffer, and
+ // copy the characters.
+ //
+ numChars = 2;
+ pPrivateShareName = AdtFindNextToken(L'\\',pNewPathName+2,&numChars);
+ if (pPrivateShareName == NULL) {
+ status = ERROR_BAD_PATHNAME;
+ goto CleanExit;
+ }
+ numServerChars = numChars;
+
+ token = L'\\';
+ if (DeviceType == REMOTE_DRIVE) {
+ token = L'\0';
+ }
+ pEnd = AdtFindNextToken(token,pPrivateShareName+1,&numChars);
+ if (pEnd == NULL) {
+ status = ERROR_BAD_PATHNAME;
+ goto CleanExit;
+ }
+ //
+ // If this is a remotepath name, then the share name portion will
+ // also contain the '\' token. Remove this by decrementing the
+ // count.
+ //
+ if (DeviceType == REMOTE_PATH) {
+ numChars--;
+ }
+ pPrivateServerName = LocalAlloc(LMEM_FIXED,(numChars+1) * sizeof(WCHAR));
+ if (pPrivateServerName == NULL) {
+ status = GetLastError();
+ goto CleanExit;
+ }
+
+ //
+ // Copy the the "\\servername\sharename" to the new buffer and
+ // place NUL characters in place of the single '\'.
+ //
+ wcsncpy(pPrivateServerName, pNewPathName, numChars);
+ pPrivateShareName = pPrivateServerName + numServerChars;
+
+ *(pPrivateShareName -1) = L'\0'; // NUL terminate the server name
+ pPrivateServerName[ numChars ] = L'\0'; // NUL terminate the share name
+
+ if (DeviceType == REMOTE_PATH) {
+ *pNewFileName = pEnd;
+ }
+ else {
+ *pNewFileName = lpPathName+2;
+ }
+ *pServerName = pPrivateServerName;
+ *pShareName = pPrivateShareName;
+ }
+CleanExit:
+ if (pUseInfo != NULL) {
+ NetApiBufferFree(pUseInfo);
+ }
+ return(status);
+}
+
+LPWSTR
+AdtFindNextToken(
+ WCHAR Token,
+ LPWSTR pString,
+ LPDWORD pNumChars
+ )
+
+/*++
+
+Routine Description:
+
+ Finds the first occurance of Token in the pString.
+
+Arguments:
+
+ Token - This is the unicode character that we are searching for in the
+ string.
+
+ pString - This is a pointer to the string in which the token is to be
+ found.
+
+ pNumChars - This is a pointer to a DWORD that will upon exit increment
+ by the number of characters found in the string (including the
+ token).
+
+Return Value:
+
+ If the token is found this returns a pointer to the Token.
+ Otherwise, it returns NULL.
+
+--*/
+{
+ DWORD saveNum=*pNumChars;
+
+ while ((*pString != Token) && (*pString != L'\0')) {
+ pString++;
+ (*pNumChars)++;
+ }
+ if (*pString != Token) {
+ *pNumChars = saveNum;
+ return(NULL);
+ }
+ (*pNumChars)++;
+ return(pString);
+}
+
diff --git a/private/net/svcdlls/srvsvc/client/dfsstub.c b/private/net/svcdlls/srvsvc/client/dfsstub.c
new file mode 100644
index 000000000..6b0f0adcd
--- /dev/null
+++ b/private/net/svcdlls/srvsvc/client/dfsstub.c
@@ -0,0 +1,342 @@
+/*++
+
+Copyright (c) 1991-1996 Microsoft Corporation
+
+Module Name:
+
+ dfsstub.c
+
+Abstract:
+
+ These are the server service API RPC client stubs for DFS operations
+
+Environment:
+
+ User Mode - Win32
+
+--*/
+
+//
+// INCLUDES
+//
+
+#include <nt.h> // DbgPrint prototype
+
+#include <ntrtl.h> // DbgPrint
+#include <rpc.h> // DataTypes and runtime APIs
+
+#include <srvsvc.h> // generated by the MIDL complier
+#include <lmcons.h> // NET_API_STATUS
+#include <debuglib.h> // (needed by netrpc.h)
+#include <lmsvc.h> // (needed by netrpc.h)
+#include <netdebug.h> // (needed by netrpc.h)
+#include <lmerr.h> // NetError codes
+#include <netrpc.h> // NET_REMOTE_ macros.
+
+
+NET_API_STATUS NET_API_FUNCTION
+I_NetDfsGetVersion(
+ IN LPWSTR servername,
+ OUT LPDWORD Version)
+{
+ NET_API_STATUS apiStatus;
+
+ NET_REMOTE_TRY_RPC
+
+ apiStatus = NetrDfsGetVersion( servername, Version );
+
+ NET_REMOTE_RPC_FAILED(
+ "I_NetDfsGetVersion",
+ servername,
+ apiStatus,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_SERVER)
+
+ apiStatus = ERROR_NOT_SUPPORTED;
+
+ NET_REMOTE_END
+
+ return(apiStatus);
+
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+I_NetDfsCreateLocalPartition (
+ IN LPWSTR servername,
+ IN LPWSTR ShareName,
+ IN LPGUID EntryUid,
+ IN LPWSTR EntryPrefix,
+ IN LPWSTR ShortName,
+ IN LPNET_DFS_ENTRY_ID_CONTAINER RelationInfo,
+ IN BOOL Force
+ )
+{
+ NET_API_STATUS apiStatus;
+
+ NET_REMOTE_TRY_RPC
+
+ apiStatus = NetrDfsCreateLocalPartition (
+ servername,
+ ShareName,
+ EntryUid,
+ EntryPrefix,
+ ShortName,
+ RelationInfo,
+ Force
+ );
+
+ NET_REMOTE_RPC_FAILED(
+ "NetDfsCreateLocalPartition",
+ servername,
+ apiStatus,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_SERVER)
+
+ apiStatus = ERROR_NOT_SUPPORTED;
+
+ NET_REMOTE_END
+
+ return(apiStatus);
+
+}
+
+NET_API_STATUS NET_API_FUNCTION
+I_NetDfsDeleteLocalPartition (
+ IN LPWSTR servername OPTIONAL,
+ IN LPGUID Uid,
+ IN LPWSTR Prefix
+ )
+{
+ NET_API_STATUS apiStatus;
+
+ NET_REMOTE_TRY_RPC
+
+ apiStatus = NetrDfsDeleteLocalPartition (
+ servername,
+ Uid,
+ Prefix
+ );
+
+ NET_REMOTE_RPC_FAILED(
+ "NetDfsDeleteLocalPartition",
+ servername,
+ apiStatus,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_SERVER)
+
+ apiStatus = ERROR_NOT_SUPPORTED;
+
+ NET_REMOTE_END;
+
+ return apiStatus;
+}
+
+NET_API_STATUS NET_API_FUNCTION
+I_NetDfsSetLocalVolumeState (
+ IN LPWSTR servername OPTIONAL,
+ IN LPGUID Uid,
+ IN LPWSTR Prefix,
+ IN ULONG State
+ )
+{
+ NET_API_STATUS apiStatus;
+
+ NET_REMOTE_TRY_RPC
+
+ apiStatus = NetrDfsSetLocalVolumeState (
+ servername,
+ Uid,
+ Prefix,
+ State
+ );
+
+ NET_REMOTE_RPC_FAILED(
+ "NetDfsSetLocalVolumeState",
+ servername,
+ apiStatus,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_SERVER)
+
+ apiStatus = ERROR_NOT_SUPPORTED;
+
+ NET_REMOTE_END;
+
+ return apiStatus;
+}
+
+NET_API_STATUS NET_API_FUNCTION
+I_NetDfsSetServerInfo (
+ IN LPWSTR servername OPTIONAL,
+ IN LPGUID Uid,
+ IN LPWSTR Prefix
+ )
+{
+ NET_API_STATUS apiStatus;
+
+ NET_REMOTE_TRY_RPC
+
+ apiStatus = NetrDfsSetServerInfo (
+ servername,
+ Uid,
+ Prefix
+ );
+
+ NET_REMOTE_RPC_FAILED(
+ "NetDfsSetServerInfo",
+ servername,
+ apiStatus,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_SERVER)
+
+ apiStatus = ERROR_NOT_SUPPORTED;
+
+ NET_REMOTE_END;
+
+ return apiStatus;
+}
+
+NET_API_STATUS NET_API_FUNCTION
+I_NetDfsCreateExitPoint (
+ IN LPWSTR servername OPTIONAL,
+ IN LPGUID Uid,
+ IN LPWSTR Prefix,
+ IN ULONG Type,
+ IN ULONG ShortPrefixSize,
+ OUT LPWSTR ShortPrefix
+ )
+{
+ NET_API_STATUS apiStatus;
+
+ NET_REMOTE_TRY_RPC
+
+ apiStatus = NetrDfsCreateExitPoint (
+ servername,
+ Uid,
+ Prefix,
+ Type,
+ ShortPrefixSize,
+ ShortPrefix
+ );
+
+ NET_REMOTE_RPC_FAILED(
+ "NetDfsCreateExitPoint",
+ servername,
+ apiStatus,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_SERVER)
+
+ apiStatus = ERROR_NOT_SUPPORTED;
+
+ NET_REMOTE_END;
+
+ return apiStatus;
+}
+
+NET_API_STATUS NET_API_FUNCTION
+I_NetDfsDeleteExitPoint (
+ IN LPWSTR servername OPTIONAL,
+ IN LPGUID Uid,
+ IN LPWSTR Prefix,
+ IN ULONG Type
+ )
+{
+ NET_API_STATUS apiStatus;
+
+ NET_REMOTE_TRY_RPC
+
+ apiStatus = NetrDfsDeleteExitPoint (
+ servername,
+ Uid,
+ Prefix,
+ Type
+ );
+
+ NET_REMOTE_RPC_FAILED(
+ "NetDfsDeleteExitPoint",
+ servername,
+ apiStatus,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_SERVER)
+
+ apiStatus = ERROR_NOT_SUPPORTED;
+
+ NET_REMOTE_END;
+
+ return apiStatus;
+}
+
+NET_API_STATUS NET_API_FUNCTION
+I_NetDfsModifyPrefix (
+ IN LPWSTR servername OPTIONAL,
+ IN LPGUID Uid,
+ IN LPWSTR Prefix
+ )
+{
+ NET_API_STATUS apiStatus;
+
+ NET_REMOTE_TRY_RPC
+
+ apiStatus = NetrDfsModifyPrefix (
+ servername,
+ Uid,
+ Prefix
+ );
+
+ NET_REMOTE_RPC_FAILED(
+ "NetDfsModifyPrefix",
+ servername,
+ apiStatus,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_SERVER)
+
+ apiStatus = ERROR_NOT_SUPPORTED;
+
+ NET_REMOTE_END;
+
+ return apiStatus;
+}
+
+NET_API_STATUS NET_API_FUNCTION
+I_NetDfsFixLocalVolume (
+ IN LPWSTR servername OPTIONAL,
+ IN LPWSTR VolumeName,
+ IN ULONG EntryType,
+ IN ULONG ServiceType,
+ IN LPWSTR StgId,
+ IN LPGUID EntryUid, // unique id for this partition
+ IN LPWSTR EntryPrefix, // path prefix for this partition
+ IN LPNET_DFS_ENTRY_ID_CONTAINER RelationInfo,
+ IN ULONG CreateDisposition
+ )
+{
+ NET_API_STATUS apiStatus;
+
+ NET_REMOTE_TRY_RPC
+
+ apiStatus = NetrDfsFixLocalVolume (
+ servername,
+ VolumeName,
+ EntryType,
+ ServiceType,
+ StgId,
+ EntryUid,
+ EntryPrefix,
+ RelationInfo,
+ CreateDisposition
+ );
+
+ NET_REMOTE_RPC_FAILED(
+ "NetDfsFixLocalVolume",
+ servername,
+ apiStatus,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_SERVER)
+
+ apiStatus = ERROR_NOT_SUPPORTED;
+
+ NET_REMOTE_END;
+
+ return apiStatus;
+}
+
diff --git a/private/net/svcdlls/srvsvc/client/makefile b/private/net/svcdlls/srvsvc/client/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/net/svcdlls/srvsvc/client/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/net/svcdlls/srvsvc/client/radmin.c b/private/net/svcdlls/srvsvc/client/radmin.c
new file mode 100644
index 000000000..6ace6f221
--- /dev/null
+++ b/private/net/svcdlls/srvsvc/client/radmin.c
@@ -0,0 +1,556 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ radmin.c (remote admin)
+
+Abstract:
+
+ This file exercises the various NetAdminTools API.
+
+Author:
+
+ Dan Lafferty (danl) 19-Sept-1991
+
+Environment:
+
+ User Mode -Win32
+
+Revision History:
+
+
+--*/
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h> // needed for windows.h when I have nt.h
+#include <windows.h>
+
+#include <stdlib.h> // atoi
+#include <stdio.h> // printf
+#include <tstr.h> // STRICMP
+
+#include <ntseapi.h> // SECURITY_DESCRIPTOR_CONTROL
+#include <srvsvc.h>
+#include <filesec.h> // NetpGetFileSecurity, NetpSetFileSecurity
+
+//
+// DataStructures
+//
+
+typedef struct _TEST_SID {
+ UCHAR Revision;
+ UCHAR SubAuthorityCount;
+ UCHAR IdentifierAuthority[6];
+ ULONG SubAuthority[10];
+} TEST_SID, *PTEST_SID, *LPTEST_SID;
+
+typedef struct _TEST_ACL {
+ UCHAR AclRevision;
+ UCHAR Sbz1;
+ USHORT AclSize;
+ UCHAR Dummy1[];
+} TEST_ACL, *PTEST_ACL;
+
+typedef struct _TEST_SECURITY_DESCRIPTOR {
+ UCHAR Revision;
+ UCHAR Sbz1;
+ SECURITY_DESCRIPTOR_CONTROL Control;
+ PTEST_SID Owner;
+ PTEST_SID Group;
+ PTEST_ACL Sacl;
+ PTEST_ACL Dacl;
+} TEST_SECURITY_DESCRIPTOR, *PTEST_SECURITY_DESCRIPTOR;
+
+//
+// GLOBALS
+//
+
+ TEST_SID OwnerSid = {
+ 1, 5,
+ 1,2,3,4,5,6,
+ 0x999, 0x888, 0x777, 0x666, 0x12345678};
+
+ TEST_SID GroupSid = {
+ 1, 5,
+ 1,2,3,4,5,6,
+ 0x999, 0x888, 0x777, 0x666, 0x12345678};
+
+ TEST_ACL SaclAcl = { 1, 2, 4+1, 3};
+ TEST_ACL DaclAcl = { 1, 2, 4+5, 4, 4, 4, 4, 4, };
+
+ TEST_SECURITY_DESCRIPTOR TestSd = {
+ 1, 2, 0x3333,
+ &OwnerSid,
+ &GroupSid,
+ &SaclAcl,
+ NULL };
+
+
+
+//
+// Function Prototypes
+//
+
+NET_API_STATUS
+TestGetFileSec(
+ LPTSTR ServerName,
+ LPTSTR FileName
+ );
+
+NET_API_STATUS
+TestSetFileSec(
+ LPTSTR ServerName,
+ LPTSTR FileName
+ );
+
+VOID
+Usage(VOID);
+
+
+VOID
+DisplaySecurityDescriptor(
+ PTEST_SECURITY_DESCRIPTOR pSecDesc
+ );
+
+BOOL
+MakeArgsUnicode (
+ DWORD argc,
+ PCHAR argv[]
+ );
+
+BOOL
+ConvertToUnicode(
+ OUT LPWSTR *UnicodeOut,
+ IN LPSTR AnsiIn
+ );
+
+
+
+
+
+VOID _CRTAPI1
+main (
+ DWORD argc,
+ PUCHAR argv[]
+ )
+
+/*++
+
+Routine Description:
+
+ Allows manual testing of the AdminTools API.
+
+ radmin GetNameFromSid - calls NetpGetNameFromSid
+ radmin SetFileSec - calls NetpSetFileSecurity
+
+ etc...
+
+
+Arguments:
+
+
+
+Return Value:
+
+
+
+--*/
+
+{
+ DWORD status;
+ LPTSTR FileName;
+ LPTSTR *FixArgv;
+ LPTSTR pServerName;
+ DWORD argIndex;
+
+ //
+ // Make the arguments unicode if necessary.
+ //
+#ifdef UNICODE
+
+ if (!MakeArgsUnicode(argc, argv)) {
+ return;
+ }
+
+#endif
+ FixArgv = (LPTSTR *)argv;
+ argIndex = 1;
+ pServerName = NULL;
+
+ if (STRNCMP (FixArgv[1], TEXT("\\\\"), 2) == 0) {
+ pServerName = FixArgv[1];
+ argIndex = 2;
+ }
+ if (argc < 2) {
+ printf("ERROR: \n");
+ Usage();
+ return;
+ }
+
+ if (STRICMP (FixArgv[argIndex], TEXT("GetFileSec")) == 0) {
+ if (argc > argIndex ) {
+ FileName = FixArgv[argIndex+1];
+ }
+ else {
+ FileName = NULL;
+ }
+ status = TestGetFileSec(pServerName,FileName);
+ }
+
+ else if (STRICMP (FixArgv[argIndex], TEXT("SetFileSec")) == 0) {
+ if (argc > argIndex ) {
+ FileName = FixArgv[argIndex+1];
+ }
+ else {
+ FileName = NULL;
+ }
+ status = TestSetFileSec(pServerName,FileName);
+ }
+
+ else {
+ printf("[sc] Unrecognized Command\n");
+ Usage();
+ }
+
+ return;
+}
+
+
+
+
+NET_API_STATUS
+TestGetFileSec(
+ LPTSTR ServerName,
+ LPTSTR FileName
+ )
+{
+ NET_API_STATUS status;
+ SECURITY_INFORMATION secInfo;
+ PTEST_SECURITY_DESCRIPTOR pSecurityDescriptor;
+ LPBYTE pDest;
+ DWORD Length;
+
+ if (FileName == NULL ) {
+ FileName = TEXT("Dan.txt");
+ }
+
+// secInfo = (OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION |
+// DACL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION);
+ secInfo = (OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION |
+ DACL_SECURITY_INFORMATION );
+
+ status = NetpGetFileSecurity(
+ FileName, // FileName,
+ secInfo, // pRequestedInformation,
+ (PSECURITY_DESCRIPTOR *)&pSecurityDescriptor, // pSecurityDescriptor,
+ &Length); // pnLength
+
+ if (status != NO_ERROR) {
+ printf("NetpGetFileSecurity Failed %d,0x%x\n",status,status);
+ }
+ else{
+
+ pDest = (LPBYTE) pSecurityDescriptor;
+
+ if (!IsValidSecurityDescriptor(pSecurityDescriptor)) {
+ printf("FAILURE: SECURITY DESCRIPTOR IS INVALID\n");
+ }
+ else {
+ printf("SUCCESS: SECURITY DESCRIPTOR IS VALID\n");
+ }
+
+ //
+ // Make the self-releative SD absolute for display.
+ //
+ pSecurityDescriptor->Owner = (PTEST_SID)(pDest + (DWORD)pSecurityDescriptor->Owner);
+ pSecurityDescriptor->Group = (PTEST_SID)(pDest + (DWORD)pSecurityDescriptor->Group);
+ pSecurityDescriptor->Sacl = (PTEST_ACL)(pDest + (DWORD)pSecurityDescriptor->Sacl);
+ pSecurityDescriptor->Dacl = (PTEST_ACL)(pDest + (DWORD)pSecurityDescriptor->Dacl);
+ pSecurityDescriptor->Control &= (~SE_SELF_RELATIVE);
+
+ if (pSecurityDescriptor->Sacl == (PTEST_ACL)pDest) {
+ pSecurityDescriptor->Sacl = NULL;
+ }
+ if (pSecurityDescriptor->Dacl == (PTEST_ACL)pDest) {
+ pSecurityDescriptor->Dacl = NULL;
+ }
+
+ printf("Size of Security Descriptor = %ld \n",Length);
+ DisplaySecurityDescriptor(pSecurityDescriptor);
+ }
+
+ return (NO_ERROR);
+}
+
+NET_API_STATUS
+TestSetFileSec(
+ LPTSTR ServerName,
+ LPTSTR FileName
+ )
+{
+ NET_API_STATUS status;
+ SECURITY_INFORMATION secInfo;
+
+
+ if (FileName == NULL ) {
+ FileName = TEXT("Dan.txt");
+ }
+
+ secInfo = 0x55555555;
+
+ status = NetpSetFileSecurity(
+ FileName, // FileName,
+ secInfo, // pRequestedInformation,
+ (PSECURITY_DESCRIPTOR)&TestSd); // pSecurityDescriptor,
+
+ if (status != NO_ERROR) {
+ printf("NetpSetFileSecurity Failed %d,0x%x\n",status,status);
+ }
+ return (NO_ERROR);
+}
+
+
+VOID
+Usage(VOID)
+{
+
+ printf("USAGE:\n");
+ printf("radmin <server> <function>\n");
+ printf("Functions: GetFileSec, SetFileSec...\n\n");
+
+ printf("SYNTAX EXAMPLES \n");
+
+ printf("radmin \\\\DANL2 GetFileSec - calls NetpGetFileSecurity on \\DANL2\n");
+ printf("radmin \\\\DANL2 SetFileSec - calls NetpSetFileSecurity on \\DANL2\n");
+}
+
+
+
+// ***************************************************************************
+VOID
+DisplaySecurityDescriptor(
+ PTEST_SECURITY_DESCRIPTOR pSecDesc
+ )
+{
+
+ DWORD i;
+ DWORD numAces;
+
+ if (!IsValidSecurityDescriptor(pSecDesc)) {
+ printf("FAILURE: SECURITY DESCRIPTOR IS INVALID\n");
+ }
+
+ printf("[ADT]:Security Descriptor Received\n");
+ printf("\tSECURITY_DESCRIPTOR HEADER:\n");
+ printf("\tRevision: %d\n", pSecDesc->Revision);
+ printf("\tSbz1: 0x%x\n", pSecDesc->Sbz1);
+ printf("\tControl: 0x%x\n", pSecDesc->Control);
+
+ //-------------------
+ // OWNER SID
+ //-------------------
+ printf("\n\tOWNER_SID\n");
+ printf("\t\tRevision: %u\n",pSecDesc->Owner->Revision);
+ printf("\t\tSubAuthorityCount: %u\n",pSecDesc->Owner->SubAuthorityCount);
+
+ printf("\t\tIdentifierAuthority: ");
+ for(i=0; i<6; i++) {
+ printf("%u ",pSecDesc->Owner->IdentifierAuthority[i]);
+ }
+ printf("\n");
+
+ printf("\t\tSubAuthority: ");
+ for(i=0; i<pSecDesc->Group->SubAuthorityCount; i++) {
+ printf("0x%x ",pSecDesc->Owner->SubAuthority[i]);
+ }
+ printf("\n");
+
+ //-------------------
+ // GROUP SID
+ //-------------------
+ printf("\n\tGROUP_SID\n");
+ printf("\t\tRevision: %u\n",pSecDesc->Group->Revision);
+ printf("\t\tSubAuthorityCount: %u\n",pSecDesc->Group->SubAuthorityCount);
+
+ printf("\t\tIdentifierAuthority: ");
+ for(i=0; i<6; i++) {
+ printf("%u ",pSecDesc->Group->IdentifierAuthority[i]);
+ }
+ printf("\n");
+
+ printf("\t\tSubAuthority: ");
+ for(i=0; i<pSecDesc->Group->SubAuthorityCount; i++) {
+ printf("0x%x ",pSecDesc->Group->SubAuthority[i]);
+ }
+ printf("\n");
+
+ if (pSecDesc->Sacl != NULL) {
+ printf("\n\tSYSTEM_ACL\n");
+ printf("\t\tRevision: %d\n",pSecDesc->Sacl->AclRevision);
+ printf("\t\tSbz1: %d\n",pSecDesc->Sacl->Sbz1);
+ printf("\t\tAclSize: %d\n",pSecDesc->Sacl->AclSize);
+ printf("\t\tACE: %u\n",(unsigned short)pSecDesc->Sacl->Dummy1[0]);
+ }
+ else {
+ printf("\n\tSYSTEM_ACL = NULL\n");
+ }
+
+ if (pSecDesc->Dacl != NULL) {
+ printf("\n\tDISCRETIONARY_ACL\n");
+ printf("\t\tRevision: %d\n",pSecDesc->Dacl->AclRevision);
+ printf("\t\tSbz1: %d\n",pSecDesc->Dacl->Sbz1);
+ printf("\t\tAclSize: %d\n",pSecDesc->Dacl->AclSize);
+
+ numAces = pSecDesc->Dacl->AclSize - 4;
+
+ for (i=0; i<numAces; i++) {
+ //
+ // NOTE: I couldn't get this to print out the right value in DOS16.
+ // So I gave up. It puts the 04 into the AL register and then
+ // clears the AH register, then pushes AX (both parts). But when
+ // it prints, it only prints 0.
+ //
+ printf("\t\tACE%u: %u\n",i,(unsigned short)pSecDesc->Dacl->Dummy1[i]);
+ }
+ }
+ else {
+ printf("\n\tDISCRETIONARY_ACL = NULL\n");
+ }
+}
+
+
+BOOL
+MakeArgsUnicode (
+ DWORD argc,
+ PCHAR argv[]
+ )
+
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+Note:
+
+
+--*/
+{
+ DWORD i;
+
+ //
+ // ScConvertToUnicode allocates storage for each string.
+ // We will rely on process termination to free the memory.
+ //
+ for(i=0; i<argc; i++) {
+
+ if(!ConvertToUnicode( (LPWSTR *)&(argv[i]), argv[i])) {
+ printf("Couldn't convert argv[%d] to unicode\n",i);
+ return(FALSE);
+ }
+
+
+ }
+ return(TRUE);
+}
+
+BOOL
+ConvertToUnicode(
+ OUT LPWSTR *UnicodeOut,
+ IN LPSTR AnsiIn
+ )
+
+/*++
+
+Routine Description:
+
+ This function translates an AnsiString into a Unicode string.
+ A new string buffer is created by this function. If the call to
+ this function is successful, the caller must take responsibility for
+ the unicode string buffer that was allocated by this function.
+ The allocated buffer should be free'd with a call to LocalFree.
+
+ NOTE: This function allocates memory for the Unicode String.
+
+ BUGBUG: This should be changed to return either
+ ERROR_NOT_ENOUGH_MEMORY or ERROR_INVALID_PARAMETER
+
+Arguments:
+
+ AnsiIn - This is a pointer to an ansi string that is to be converted.
+
+ UnicodeOut - This is a pointer to a location where the pointer to the
+ unicode string is to be placed.
+
+Return Value:
+
+ TRUE - The conversion was successful.
+
+ FALSE - The conversion was unsuccessful. In this case a buffer for
+ the unicode string was not allocated.
+
+--*/
+{
+
+ NTSTATUS ntStatus;
+ DWORD bufSize;
+ UNICODE_STRING unicodeString;
+ ANSI_STRING ansiString;
+
+ //
+ // Allocate a buffer for the unicode string.
+ //
+
+ bufSize = (strlen(AnsiIn)+1) * sizeof(WCHAR);
+
+ *UnicodeOut = (LPWSTR)LocalAlloc(LMEM_ZEROINIT, (UINT)bufSize);
+
+ if (*UnicodeOut == NULL) {
+ printf("ScConvertToUnicode:LocalAlloc Failure %ld\n",GetLastError());
+ return(FALSE);
+ }
+
+ //
+ // Initialize the string structures
+ //
+ RtlInitAnsiString( &ansiString, AnsiIn);
+
+ unicodeString.Buffer = *UnicodeOut;
+ unicodeString.MaximumLength = (USHORT)bufSize;
+ unicodeString.Length = 0;
+
+ //
+ // Call the conversion function.
+ //
+ ntStatus = RtlAnsiStringToUnicodeString (
+ &unicodeString, // Destination
+ &ansiString, // Source
+ (BOOLEAN)FALSE); // Allocate the destination
+
+ if (!NT_SUCCESS(ntStatus)) {
+
+ printf("ScConvertToUnicode:RtlAnsiStringToUnicodeString Failure %lx\n",
+ ntStatus);
+
+ return(FALSE);
+ }
+
+ //
+ // Fill in the pointer location with the unicode string buffer pointer.
+ //
+ *UnicodeOut = unicodeString.Buffer;
+
+ return(TRUE);
+
+}
+
+
diff --git a/private/net/svcdlls/srvsvc/client/sources b/private/net/svcdlls/srvsvc/client/sources
new file mode 100644
index 000000000..5ee63c11d
--- /dev/null
+++ b/private/net/svcdlls/srvsvc/client/sources
@@ -0,0 +1,57 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+
+Author:
+
+ Steve Wood (stevewo) 12-Apr-1989
+
+
+Revision History:
+
+!ENDIF
+
+MAJORCOMP = srvsvc
+MINORCOMP = client
+
+
+NTPROFILEINPUT=YES
+
+TARGETNAME=srvsvc
+TARGETPATH=obj
+TARGETTYPE=LIBRARY
+
+INCLUDES=.;..;..\..\..\inc;..\..\..\..\inc
+
+!IFNDEF DISABLE_NET_UNICODE
+UNICODE=1
+NET_C_DEFINES=-DUNICODE
+!ENDIF
+
+SOURCES=srvstub.c \
+ srvbind.c \
+ adtwrap.c \
+ dfsstub.c \
+ srvsvc_c.c
+
+UMTYPE=console
+UMLIBS= $(BASEDIR)\Public\Sdk\Lib\*\netlib.lib \
+ $(BASEDIR)\public\sdk\lib\*\rpcrt4.lib \
+ $(BASEDIR)\public\sdk\lib\*\rpcndr.lib \
+ $(BASEDIR)\public\sdk\lib\*\netapi32.lib \
+ ..\lib\obj\*\srvcomn.lib
+
+C_DEFINES=-DRPC_NO_WINDOWS_H
+
diff --git a/private/net/svcdlls/srvsvc/client/srvbind.c b/private/net/svcdlls/srvsvc/client/srvbind.c
new file mode 100644
index 000000000..9c6c5345d
--- /dev/null
+++ b/private/net/svcdlls/srvsvc/client/srvbind.c
@@ -0,0 +1,111 @@
+/*++
+
+Copyright (c) 1990 Microsoft Corporation
+
+Module Name:
+
+ srvbind.c
+
+Abstract:
+
+ Contains the RPC bind and un-bind routines for the Server
+ Service.
+
+Author:
+
+ Dan Lafferty (danl) 01-Mar-1991
+
+Environment:
+
+ User Mode -Win32
+
+Revision History:
+
+ 01-Mar-1991 danl
+ created
+ 07-Jun-1991 JohnRo
+ Allowed debug output of failures.
+
+--*/
+
+//
+// INCLUDES
+//
+#include <nt.h> // DbgPrint prototype
+#include <rpc.h> // DataTypes and runtime APIs
+#include <srvsvc.h> // generated by the MIDL complier
+#include <rpcutil.h> // NetRpc utils
+#include <netlib.h> // UNUSED macro
+#include <srvnames.h> // SERVER_INTERFACE_NAME
+
+
+
+handle_t
+SRVSVC_HANDLE_bind (
+ SRVSVC_HANDLE ServerName)
+
+/*++
+
+Routine Description:
+ This routine calls a common bind routine that is shared by all services.
+ This routine is called from the server service client stubs when
+ it is necessary to bind to a server.
+
+Arguments:
+
+ ServerName - A pointer to a string containing the name of the server
+ to bind with.
+
+Return Value:
+
+ The binding handle is returned to the stub routine. If the
+ binding is unsuccessful, a NULL will be returned.
+
+--*/
+{
+ handle_t bindingHandle;
+ RPC_STATUS status;
+
+ status = NetpBindRpc (
+ ServerName,
+ SERVER_INTERFACE_NAME,
+ TEXT("Security=Impersonation Dynamic False"),
+ &bindingHandle);
+
+ return( bindingHandle);
+}
+
+
+
+void
+SRVSVC_HANDLE_unbind (
+ SRVSVC_HANDLE ServerName,
+ handle_t BindingHandle)
+
+/*++
+
+Routine Description:
+
+ This routine calls a common unbind routine that is shared by
+ all services.
+ This routine is called from the server service client stubs when
+ it is necessary to unbind to a server.
+
+
+Arguments:
+
+ ServerName - This is the name of the server from which to unbind.
+
+ BindingHandle - This is the binding handle that is to be closed.
+
+Return Value:
+
+ none.
+
+--*/
+{
+ UNUSED(ServerName); // This parameter is not used
+
+ NetpUnbindRpc ( BindingHandle);
+ return;
+}
diff --git a/private/net/svcdlls/srvsvc/client/srvstub.c b/private/net/svcdlls/srvsvc/client/srvstub.c
new file mode 100644
index 000000000..e10305558
--- /dev/null
+++ b/private/net/svcdlls/srvsvc/client/srvstub.c
@@ -0,0 +1,3573 @@
+/*++
+
+Copyright (c) 1991-1992 Microsoft Corporation
+
+Module Name:
+
+ SrvStub.C
+
+Abstract:
+
+ These are the server service API RPC client stubs.
+
+Author:
+
+ Dan Lafferty (danl) 06-Feb-1991
+
+Environment:
+
+ User Mode - Win32
+
+Revision History:
+
+ 06-Feb-1991 Danl
+ Created
+ 07-Jun-1991 JohnRo
+ Added downlevel support for NetServer APIs.
+ Added NET_API_FUNCTION where necessary.
+ 15-Jul-1991 RFirth
+ Integrated RxNetShare routines into NetShare stubs
+ 24-Jul-1991 JohnRo
+ Implement downlevel NetConnectionEnum. Try using <netrpc.h> macros.
+ 25-Jul-1991 JohnRo
+ Quiet DLL stub debug output. Use NetRpc.h macros for NetServer APIs.
+ 06-Sep-1991 JohnRo
+ Downlevel NetFile APIs.
+ 25-Sep-1991 JohnRo
+ Use NetRpc.h macros for all other APIs, to quiet normal debug output.
+ 07-Oct-1991 JohnRo
+ RAID 3210: "NET FILE 0" causes assertion. (Was bug in NetFileGetInfo
+ DLL stub.)
+ 16-Oct-1991 JohnRo
+ Implement remote NetSession APIs. Changed LPSTR to LPTSTR.
+ 07-Nov-1991 JohnRo
+ RAID 4186: assert in RxNetShareAdd and other DLL stub problems.
+ 12-Nov-1991 JohnRo
+ APIs in this file need SERVICE_SERVER started to run locally.
+ 04-Dec-1991 JohnRo
+ Change RxNetServerSetInfo() to new-style interface.
+ Fixed bug in calling RxNetShareSetInfo().
+ 09-May-1992 rfirth
+ Resurrect NetStatisticsGet as NetServerStatisticsGet
+ 5-Aug-1992 JohnsonA
+ Added new share info level 502 to enable passing of security
+ descriptors.
+ 08-Sep-1992 JohnRo
+ Fix NET_API_FUNCTION references.
+--*/
+
+//
+// INCLUDES
+//
+
+#include <nt.h> // DbgPrint prototype
+
+#include <ntrtl.h> // DbgPrint
+#include <rpc.h> // DataTypes and runtime APIs
+
+#include <srvsvc.h> // generated by the MIDL complier
+#include <rpcutil.h> // GENERIC_ENUM_STRUCT
+#include <lmcons.h> // NET_API_STATUS
+#include <debuglib.h> // (needed by netrpc.h)
+#include <lmsvc.h> // (needed by netrpc.h)
+#include <netdebug.h> // (needed by netrpc.h)
+#include <lmerr.h> // NetError codes
+#include <netlib.h> // NetpIsServiceStarted().
+#include <netlibnt.h> // NetpNtStatusToApiStatus
+#include <netrpc.h> // NET_REMOTE_ macros.
+#include <lmremutl.h> // SUPPORTS_RPC
+#include <lmshare.h> // Required by rxsess.h.
+#include <rap.h> // Needed by <rxserver.h>.
+#include <rxconn.h> // RxNetConnection routines.
+#include <rxfile.h> // RxNetFile routines.
+#include <rxremutl.h> // RxNetRemoteTOD
+#include <rxserver.h> // RxNetServer routines.
+#include <rxsess.h> // RxNetSession routines.
+#include <rxshare.h> // RxNetShare routines
+#include <icanon.h> // NetpIsRemote
+#include <netstats.h> // NetServerStatisticsGet private prototype
+#include <rxstats.h> // RxNetStatisticsGet (down-level)
+#include <netcan.h> // prototypes for Netps canonicalization functions
+#include <rxcanon.h> // prototypes for down-level canonicalization functions
+#include <tstr.h>
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetCharDevControl (
+ IN LPCWSTR servername,
+ IN LPCWSTR devname,
+ IN DWORD opcode
+ )
+/*++
+
+Routine Description:
+
+ This is the DLL entrypoint for NetCharDevControl.
+
+Arguments:
+
+ servername --A pointer to an ASCIIZ string containing the name of
+ the remote server on which the function is to execute. A NULL
+ pointer or string specifies the local machine.
+
+ devname --A pointer to the ASCIIZ string containing the name of
+ the device to control
+
+ opcode --Control opcode: currently defined are:
+ CHARDEV_CLOSE for the device closed.
+
+Return Value:
+
+--*/
+
+{
+ NET_API_STATUS apiStatus;
+
+ NET_REMOTE_TRY_RPC
+
+ apiStatus = NetrCharDevControl (
+ (LPWSTR)servername,
+ (LPWSTR)devname,
+ opcode);
+
+ NET_REMOTE_RPC_FAILED(
+ "NetCharDevControl",
+ servername,
+ apiStatus,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_SERVER)
+
+ //
+ // BUGBUG: Put Downlevel Call Here!
+ //
+ apiStatus = ERROR_NOT_SUPPORTED;
+
+ NET_REMOTE_END
+
+ return(apiStatus);
+
+} // NetCharDevControl
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetCharDevEnum (
+ IN LPCWSTR servername,
+ IN DWORD level,
+ OUT LPBYTE *bufptr,
+ IN DWORD prefmaxlen,
+ OUT LPDWORD entriesread,
+ OUT LPDWORD totalentries,
+ IN OUT LPDWORD resume_handle
+ )
+
+/*++
+
+Routine Description:
+
+ This is the DLL entrypoint for NetCharDevEnum.
+
+Arguments:
+
+ servername --A pointer to an ASCIIZ string containing the name of
+ the remote server on which the function is to execute. A NULL
+ pointer or string specifies the local machine.
+
+ level --Level of information required. 0 and 1 are valid.
+
+ bufptr --On return a pointer to the return information structure
+ is returned in the address pointed to by bufptr.
+
+ prefmaxlen --Prefered maximum length of returned data (in 8-bit
+ bytes). 0xffffffff specifies no limit.
+
+ entriesread --On return the actual enumerated element count is
+ located in the DWORD pointed to by entriesread.
+
+ totalentries --On return the total entries available to be
+ enumerated is located in the DWORD pointed to by
+ totalentries.
+
+ resumehandle --On return, a resume handle is stored in the DWORD
+ pointed to by resumehandle, and is used to continue an
+ existing character device search. The handle should be zero
+ on the first call and left unchanged for subsequent calls. If
+ resumehandle is NULL, then no resume handle is stored..
+
+Return Value:
+
+
+
+--*/
+
+{
+ NET_API_STATUS apiStatus;
+ GENERIC_INFO_CONTAINER genericInfoContainer;
+ GENERIC_ENUM_STRUCT infoStruct;
+
+ genericInfoContainer.Buffer = NULL;
+ genericInfoContainer.EntriesRead = 0;
+
+ infoStruct.Container = &genericInfoContainer;
+ infoStruct.Level = level;
+
+ NET_REMOTE_TRY_RPC
+
+ apiStatus = NetrCharDevEnum (
+ (LPWSTR)servername,
+ (LPCHARDEV_ENUM_STRUCT)&infoStruct,
+ prefmaxlen,
+ totalentries,
+ resume_handle);
+
+ if (genericInfoContainer.Buffer != NULL) {
+ *bufptr = (LPBYTE)genericInfoContainer.Buffer;
+ *entriesread = genericInfoContainer.EntriesRead;
+ } else {
+ *bufptr = NULL;
+ *entriesread = 0;
+ }
+
+ NET_REMOTE_RPC_FAILED(
+ "NetCharDevEnum",
+ servername,
+ apiStatus,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_SERVER)
+
+ //
+ // BUGBUG: Put Downlevel Call Here!
+ //
+ apiStatus = ERROR_NOT_SUPPORTED;
+
+ NET_REMOTE_END
+
+ return(apiStatus);
+
+} // NetCharDevEnum
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetCharDevGetInfo (
+ IN LPCWSTR servername,
+ IN LPCWSTR devname,
+ IN DWORD level,
+ OUT LPBYTE *bufptr
+ )
+/*++
+
+Routine Description:
+
+ This is the DLL entrypoint for NetCharDevGetInfo.
+
+Arguments:
+
+ servername --A pointer to an ASCIIZ string containing the name of
+ the remote server on which the function is to execute. A NULL
+ pointer or string specifies the local machine.
+
+ devname --A pointer to the ASCIIZ string containing the name of
+ the device to return information on.
+
+ level --Level of information required. 0 and 1 are valid.
+
+ bufptr --On return a pointer to the return information structure
+ is returned in the address pointed to by bufptr.
+
+Return Value:
+
+
+
+--*/
+
+{
+ NET_API_STATUS apiStatus;
+
+
+ *bufptr = NULL; // Must be NULL so RPC knows to till it in.
+
+ NET_REMOTE_TRY_RPC
+
+ apiStatus = NetrCharDevGetInfo (
+ (LPWSTR)servername,
+ (LPWSTR)devname,
+ level,
+ (LPCHARDEV_INFO) bufptr);
+
+ NET_REMOTE_RPC_FAILED(
+ "NetCharDevGetInfo",
+ servername,
+ apiStatus,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_SERVER)
+
+ //
+ // BUGBUG: Put Downlevel Call Here!
+ //
+ apiStatus = ERROR_NOT_SUPPORTED;
+
+ NET_REMOTE_END
+
+ return(apiStatus);
+
+} // NetCharDevGetInfo
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetCharDevQEnum (
+ IN LPCWSTR servername,
+ IN LPCWSTR username,
+ IN DWORD level,
+ OUT LPBYTE *bufptr,
+ IN DWORD prefmaxlen,
+ OUT LPDWORD entriesread,
+ OUT LPDWORD totalentries,
+ IN OUT LPDWORD resume_handle
+ )
+
+/*++
+
+Routine Description:
+
+ This is the DLL entrypoint for NetCharDevQEnum.
+
+Arguments:
+
+ servername --A pointer to an ASCIIZ string containing the name of
+ the remote server on which the function is to execute. A NULL
+ pointer or string specifies the local machine.
+
+ username --A pointer to an ASCIIZ string containing an a username
+ for the active queues of interest. This parameter is
+ optional, if NULL then all device queues are enumerated.
+
+ level --Level of information required. 0 and 1 are valid.
+
+ bufptr --On return a pointer to the return information structure
+ is returned in the address pointed to by bufptr.
+
+ prefmaxlen --Prefered maximum length of returned data (in 8-bit
+ bytes). 0xffffffff specifies no limit.
+
+ entriesread --On return the actual enumerated element count is
+ located in the DWORD pointed to by entriesread.
+
+ totalentries --On return the total entries available to be
+ enumerated is located in the DWORD pointed to by
+ totalentries.
+
+ resumehandle --On return, a resume handle is stored in the DWORD
+ pointed to by resumehandle, and is used to continue an
+ existing character device queue search. The handle should be
+
+Return Value:
+
+
+--*/
+
+{
+ NET_API_STATUS apiStatus;
+ GENERIC_INFO_CONTAINER genericInfoContainer;
+ GENERIC_ENUM_STRUCT infoStruct;
+
+
+ genericInfoContainer.Buffer = NULL;
+ genericInfoContainer.EntriesRead = 0;
+
+ infoStruct.Container = &genericInfoContainer;
+ infoStruct.Level = level;
+
+ NET_REMOTE_TRY_RPC
+
+ apiStatus = NetrCharDevQEnum (
+ (LPWSTR)servername,
+ (LPWSTR)username,
+ (LPCHARDEVQ_ENUM_STRUCT) &infoStruct,
+ prefmaxlen,
+ totalentries,
+ resume_handle);
+
+ if (genericInfoContainer.Buffer != NULL) {
+ *bufptr = (LPBYTE)genericInfoContainer.Buffer;
+ *entriesread = genericInfoContainer.EntriesRead;
+ } else {
+ *bufptr = NULL;
+ *entriesread = 0;
+ }
+
+ NET_REMOTE_RPC_FAILED(
+ "NetCharDevQEnum",
+ servername,
+ apiStatus,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_SERVER)
+
+ //
+ // BUGBUG: Put Downlevel Call Here!
+ //
+ apiStatus = ERROR_NOT_SUPPORTED;
+
+ NET_REMOTE_END
+
+ return(apiStatus);
+
+} // NetCharDevQEnum
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetCharDevQGetInfo (
+ IN LPCWSTR servername,
+ IN LPCWSTR queuename,
+ IN LPCWSTR username,
+ IN DWORD level,
+ OUT LPBYTE *bufptr
+ )
+/*++
+
+Routine Description:
+
+ This is the DLL entrypoint for NetCharDevQGetInfo.
+
+Arguments:
+
+ servername --A pointer to an ASCIIZ string containing the name of
+ the remote server on which the function is to execute. A NULL
+ pointer or string specifies the local machine.
+
+ queuename --A pointer to an ASCIIZ string containing the name of
+ the queue to return information on.
+
+ username --A pointer to an ASCIIZ string containing the username
+ of the a user whose job of of interest for the cq1_numahead
+ count.
+
+ level --Level of information required. 0 and 1 are valid.
+
+ bufptr --On return a pointer to the return information structure
+ is returned in the address pointed to by bufptr.
+
+Return Value:
+
+--*/
+
+{
+ NET_API_STATUS apiStatus;
+
+ *bufptr = NULL; // Must be NULL so RPC knows to till it in.
+
+ NET_REMOTE_TRY_RPC
+
+ apiStatus = NetrCharDevQGetInfo (
+ (LPWSTR)servername,
+ (LPWSTR)queuename,
+ (LPWSTR)username,
+ level,
+ (LPCHARDEVQ_INFO) bufptr);
+
+ NET_REMOTE_RPC_FAILED(
+ "NetCharDevQGetInfo",
+ servername,
+ apiStatus,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_SERVER)
+
+ //
+ // BUGBUG: Put Downlevel Call Here!
+ //
+ apiStatus = ERROR_NOT_SUPPORTED;
+
+ NET_REMOTE_END
+
+ return(apiStatus);
+
+} // NetCharDevQGetInfo
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetCharDevQPurge (
+ IN LPCWSTR servername,
+ IN LPCWSTR queuename
+ )
+/*++
+
+Routine Description:
+
+ This is the DLL entrypoint for NetCharDevQPurge.
+
+Arguments:
+
+ servername --A pointer to an ASCIIZ string containing the name of
+ the remote server on which the function is to execute. A NULL
+ pointer or string specifies the local machine.
+
+ queuename --A pointer to an ASCIIZ string containing the name of
+ the queue to be purged.
+
+Return Value:
+
+
+--*/
+
+{
+ NET_API_STATUS apiStatus;
+
+ NET_REMOTE_TRY_RPC
+
+ apiStatus = NetrCharDevQPurge (
+ (LPWSTR)servername,
+ (LPWSTR)queuename);
+
+ NET_REMOTE_RPC_FAILED(
+ "NetCharDevQPurge",
+ servername,
+ apiStatus,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_SERVER)
+
+ //
+ // BUGBUG: Put Downlevel Call Here!
+ //
+ apiStatus = ERROR_NOT_SUPPORTED;
+
+ NET_REMOTE_END
+
+ return(apiStatus);
+
+} // NetCharDevQPurge
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetCharDevQPurgeSelf (
+ IN LPCWSTR servername,
+ IN LPCWSTR queuename,
+ IN LPCWSTR computername
+ )
+/*++
+
+Routine Description:
+
+ This is the DLL entrypoint for NetCharDevQPurgeSelf.
+
+Arguments:
+
+ servername --A pointer to an ASCIIZ string containing the name of
+ the remote server on which the function is to execute. A NULL
+ pointer or string specifies the local machine.
+
+ queuename --A pointer to an ASCIIZ string containing the name of
+ the queue to be purged of pending entries from the specified
+ computer.
+
+Return Value:
+
+--*/
+
+{
+ NET_API_STATUS apiStatus;
+
+ NET_REMOTE_TRY_RPC
+
+ apiStatus = NetrCharDevQPurgeSelf (
+ (LPWSTR)servername,
+ (LPWSTR)queuename,
+ (LPWSTR)computername);
+
+ NET_REMOTE_RPC_FAILED(
+ "NetCharDevQPurgeSelf",
+ servername,
+ apiStatus,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_SERVER)
+
+ //
+ // BUGBUG: Put Downlevel Call Here!
+ //
+ apiStatus = ERROR_NOT_SUPPORTED;
+
+ NET_REMOTE_END
+
+ return(apiStatus);
+
+} // NetCharDevQPurgeSelf
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetCharDevQSetInfo (
+ IN LPCWSTR servername,
+ IN LPCWSTR queuename,
+ IN DWORD level,
+ IN LPBYTE buf,
+ OUT LPDWORD parm_err
+ )
+/*++
+
+Routine Description:
+
+ This is the DLL entrypoint for NetCharDevQSetInfo.
+
+Arguments:
+
+ servername --A pointer to an ASCIIZ string containing the name of
+ the remote server on which the function is to execute. A NULL
+ pointer or string specifies the local machine.
+
+ queuename --A pointer to an ASCIIZ string containing the name of
+ the queue to set information on.
+
+ level --Level of information to set.
+
+ buf --A pointer to a buffer containing the chardev information.
+ If parmnum is non zero then the buffer contains only the
+ appropriate data for the specific element. If parmnum is
+ zero, then the buffer contains the whole chardev information
+ structure.
+
+ parm_err --Optional pointer to a DWORD to return the index of the
+ first parameter in error when ERROR_INVALID_PARAMETER is
+ returned. If NULL the parameter is not returned on error.
+
+Return Value:
+
+--*/
+
+{
+ NET_API_STATUS apiStatus;
+
+ NET_REMOTE_TRY_RPC
+
+ apiStatus = NetrCharDevQSetInfo (
+ (LPWSTR)servername,
+ (LPWSTR)queuename,
+ level,
+ (LPCHARDEVQ_INFO) &buf,
+ parm_err);
+
+ NET_REMOTE_RPC_FAILED(
+ "NetCharDevQSetInfo",
+ servername,
+ apiStatus,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_SERVER)
+
+ //
+ // BUGBUG: Put Downlevel Call Here!
+ //
+ apiStatus = ERROR_NOT_SUPPORTED;
+
+ NET_REMOTE_END
+
+ return(apiStatus);
+
+} // NetCharDevQSetInfo
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetConnectionEnum (
+ IN LPTSTR servername,
+ IN LPTSTR qualifier,
+ IN DWORD level,
+ OUT LPBYTE *bufptr,
+ IN DWORD prefmaxlen,
+ OUT LPDWORD entriesread,
+ OUT LPDWORD totalentries,
+ IN OUT LPDWORD resume_handle
+ )
+/*++
+
+Routine Description:
+
+ This is the DLL entrypoint for NetConnectionEnum.
+
+Arguments:
+
+ servername --A pointer to an ASCIIZ string containing the name of
+ the remote server on which the function is to execute. A NULL
+ pointer or string specifies the local machine.
+
+ qualifier --A pointer to an ASCIIZ string containing a sharename
+ or computername for the connections of interest. If it is a
+ sharename, then all the connections made to that sharename
+ are listed. If it is a computername (i.e. it starts with two
+ backslash characters), then NetConnectionEnum lists all
+ connections made from that computer to the server specified.
+
+ level --Level of information required. 0 and 1 are valid.
+
+ bufptr --On return a pointer to the return information structure
+ is returned in the address pointed to by bufptr.
+
+ prefmaxlen --Prefered maximum length of returned data (in 8-bit
+ bytes). 0xffffffff specifies no limit.
+
+ entriesread --On return the actual enumerated element count is
+ located in the DWORD pointed to by entriesread.
+
+Return Value:
+
+--*/
+
+{
+ NET_API_STATUS apiStatus;
+ GENERIC_INFO_CONTAINER genericInfoContainer;
+ GENERIC_ENUM_STRUCT infoStruct;
+
+ genericInfoContainer.Buffer = NULL;
+ genericInfoContainer.EntriesRead = 0;
+
+ infoStruct.Container = &genericInfoContainer;
+ infoStruct.Level = level;
+
+ NET_REMOTE_TRY_RPC
+
+ apiStatus = NetrConnectionEnum (
+ servername,
+ qualifier,
+ (LPCONNECT_ENUM_STRUCT)&infoStruct,
+ prefmaxlen,
+ totalentries,
+ resume_handle);
+
+ if (genericInfoContainer.Buffer != NULL) {
+ *bufptr = (LPBYTE)genericInfoContainer.Buffer;
+ *entriesread = genericInfoContainer.EntriesRead;
+ } else {
+ *bufptr = NULL;
+ *entriesread = 0;
+ }
+
+ NET_REMOTE_RPC_FAILED(
+ "NetConnectionEnum",
+ servername,
+ apiStatus,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_SERVER)
+
+ //
+ // Try call to downlevel.
+ //
+ apiStatus = RxNetConnectionEnum(
+ servername,
+ qualifier,
+ level,
+ bufptr,
+ prefmaxlen,
+ entriesread,
+ totalentries,
+ resume_handle
+ );
+
+ NET_REMOTE_END
+
+ return(apiStatus);
+
+} // NetConnectionEnum
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetFileClose (
+ IN LPTSTR servername,
+ IN DWORD fileid
+ )
+/*++
+
+Routine Description:
+
+ This is the DLL entrypoint for NetFileClose.
+
+Arguments:
+
+ servername --A pointer to an ASCIIZ string containing the name of
+ the remote server on which the function is to execute. A NULL
+ pointer or string specifies the local machine.
+
+ fileid --The fileid of the opened resource instance to be closed.
+
+Return Value:
+
+--*/
+
+{
+ NET_API_STATUS apiStatus;
+
+ NET_REMOTE_TRY_RPC
+
+ apiStatus = NetrFileClose (
+ servername,
+ fileid);
+
+ NET_REMOTE_RPC_FAILED(
+ "NetFileClose",
+ servername,
+ apiStatus,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_SERVER)
+
+ apiStatus = RxNetFileClose (
+ servername,
+ fileid);
+
+ NET_REMOTE_END
+
+ return(apiStatus);
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetFileEnum (
+ IN LPTSTR servername,
+ IN LPTSTR basepath,
+ IN LPTSTR username,
+ IN DWORD level,
+ OUT LPBYTE *bufptr,
+ IN DWORD prefmaxlen,
+ OUT LPDWORD entriesread,
+ OUT LPDWORD totalentries,
+ IN OUT LPDWORD resume_handle
+ )
+/*++
+
+Routine Description:
+
+ This is the DLL entrypoint for NetFileEnum.
+
+Arguments:
+
+ servername --A pointer to an ASCIIZ string containing the name of
+ the remote server on which the function is to execute. A NULL
+ pointer or string specifies the local machine.
+
+ basepath --A pointer to an ASCIIZ string containing a qualifier
+ for the returned information. If NULL then all open resources
+ are enumerated, else only resources which have basepath as a
+ prefix are enumerated.
+
+ username --A pointer to an ASCIIZ string that specifies the name
+ of the user. If not NULL, username serves as a qualifier to
+ the ennumeration. The files returned are limited to those
+ that have usernames matching the qualifier. If username is
+ NULL, no username qualifier is used.
+
+ level --Level of information required. 2 and 3 are valid.
+
+ bufptr --On return a pointer to the return information structure
+ is returned in the address pointed to by bufptr.
+
+ prefmaxlen --Prefered maximum length of returned data (in 8-bit
+ bytes). 0xffffffff specifies no limit.
+
+ entriesread --On return the actual enumerated element count is
+ located in the DWORD pointed to by entriesread.
+
+ totalentries --On return the total entries available to be
+ enumerated is located in the DWORD pointed to by
+ totalentries.
+
+ resumehandle --On return, a resume handle is stored in the DWORD
+ pointed to by resumehandle, and is used to continue an
+ existing file search. The handle should be zero on the first
+ call and left unchanged for subsequent calls. If resumehandle
+ is NULL, then no resume handle is stored..
+
+Return Value:
+
+--*/
+
+{
+ NET_API_STATUS apiStatus;
+ GENERIC_INFO_CONTAINER genericInfoContainer;
+ GENERIC_ENUM_STRUCT infoStruct;
+
+
+ genericInfoContainer.Buffer = NULL;
+ genericInfoContainer.EntriesRead = 0;
+
+ infoStruct.Container = &genericInfoContainer;
+ infoStruct.Level = level;
+
+ NET_REMOTE_TRY_RPC
+
+ apiStatus = NetrFileEnum (
+ servername,
+ basepath,
+ username,
+ (LPFILE_ENUM_STRUCT) &infoStruct,
+ prefmaxlen,
+ totalentries,
+ resume_handle);
+
+ if (genericInfoContainer.Buffer != NULL) {
+ *bufptr = (LPBYTE)genericInfoContainer.Buffer;
+ *entriesread = genericInfoContainer.EntriesRead;
+ } else {
+ *bufptr = NULL;
+ *entriesread = 0;
+ }
+
+ NET_REMOTE_RPC_FAILED(
+ "NetFileEnum",
+ servername,
+ apiStatus,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_SERVER)
+
+ apiStatus = RxNetFileEnum(
+ servername,
+ basepath,
+ username,
+ level,
+ bufptr,
+ prefmaxlen,
+ entriesread,
+ totalentries,
+ resume_handle);
+
+ NET_REMOTE_END
+
+ return(apiStatus);
+
+} // NetFileEnum
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetFileGetInfo (
+ IN LPTSTR servername,
+ IN DWORD fileid,
+ IN DWORD level,
+ OUT LPBYTE *bufptr
+ )
+/*++
+
+Routine Description:
+
+ This is the DLL entrypoint for NetFileGetInfo.
+
+Arguments:
+
+ servername --A pointer to an ASCIIZ string containing the name of
+ the remote server on which the function is to execute. A NULL
+ pointer or string specifies the local machine.
+
+ fileid --The fileid of the open resource to return information
+ on. The fileid value must be that returned in a previous
+ enumeration call.
+
+ level --Level of information required. 2 and 3 are valid.
+
+ bufptr --On return a pointer to the return information structure
+ is returned in the address pointed to by bufptr.
+
+Return Value:
+
+--*/
+
+{
+ NET_API_STATUS apiStatus;
+
+ *bufptr = NULL; // Must be NULL so RPC knows to fill it in.
+
+ NET_REMOTE_TRY_RPC
+
+ apiStatus = NetrFileGetInfo (
+ servername,
+ fileid,
+ level,
+ (LPFILE_INFO) bufptr);
+
+ NET_REMOTE_RPC_FAILED(
+ "NetFileGetInfo",
+ servername,
+ apiStatus,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_SERVER)
+
+ apiStatus = RxNetFileGetInfo (
+ servername,
+ fileid,
+ level,
+ bufptr);
+
+ NET_REMOTE_END
+
+ return(apiStatus);
+
+} // NetFileGetInfo
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetSessionDel (
+ IN LPTSTR servername,
+ IN LPTSTR clientname,
+ IN LPTSTR username
+ )
+/*++
+
+Routine Description:
+
+ This is the DLL entrypoint for NetSessionDel.
+
+Arguments:
+
+ servername --A pointer to an ASCIIZ string containing the name of
+ the remote server on which the function is to execute. A NULL
+ pointer or string specifies the local machine.
+
+ clientname --A pointer to an ASCIIZ string containing the
+ computername of the client to disconnect.
+
+ username --A pointer to an ASCIIZ string containing the name of
+ the user whose session is to be terminated. A NULL indicates
+ that all users' sessions from the computername specified are
+ to be terminated.
+
+Return Value:
+
+--*/
+
+{
+ NET_API_STATUS apiStatus;
+
+ NET_REMOTE_TRY_RPC
+
+ apiStatus = NetrSessionDel (
+ servername,
+ clientname,
+ username);
+
+ NET_REMOTE_RPC_FAILED("NetSessionDel", servername, apiStatus,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_SERVER )
+
+ //
+ // Call downlevel version of the API.
+ //
+
+ apiStatus = RxNetSessionDel (
+ servername,
+ clientname,
+ username);
+
+ NET_REMOTE_END
+
+ return(apiStatus);
+
+} // NetSessionDel
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetSessionEnum (
+ IN LPTSTR servername,
+ IN LPTSTR clientname,
+ IN LPTSTR username,
+ IN DWORD level,
+ OUT LPBYTE *bufptr,
+ IN DWORD prefmaxlen,
+ OUT LPDWORD entriesread,
+ OUT LPDWORD totalentries,
+ IN OUT LPDWORD resume_handle
+ )
+
+/*++
+
+Routine Description:
+
+ This is the DLL entrypoint for NetSessionEnum.
+
+Arguments:
+
+ servername --A pointer to an ASCIIZ string containing the name of
+ the remote server on which the function is to execute. A NULL
+ pointer or string specifies the local machine.
+
+ clientname --A pointer to an ASCIIZ string containing the name of
+ the computer session for which information is to be returned.
+ A NULL pointer or string specifies that all computer sessions
+ on the server are to be ennumerated.
+
+ username --A pointer to an ASCIIZ string containing the name of
+ the the user for which to ennumerate the sessions. A NULL
+ pointer or string specifies that sessions for all users are
+ to be ennumerated.
+
+ level --Level of information required. 0, 1, 2 and 10 are valid.
+
+ bufptr --On return a pointer to the return information structure
+ is returned in the address pointed to by bufptr.
+
+ prefmaxlen --Prefered maximum length of returned data (in 8-bit
+ bytes). 0xffffffff specifies no limit.
+
+ entriesread --On return the actual enumerated element count is
+ located in the DWORD pointed to by entriesread.
+
+ totalentries --On return the total entries available to be
+ enumerated is located in the DWORD pointed to by
+ totalentries.
+
+ resumehandle --On return, a resume handle is stored in the DWORD
+ pointed to by resumehandle, and is used to continue an
+ existing session search. The handle should be zero on the
+ first call and left unchanged for subsequent calls. If
+ resumehandle is NULL, then no resume handle is stored.
+
+Return Value:
+
+--*/
+
+{
+ NET_API_STATUS apiStatus;
+ GENERIC_INFO_CONTAINER genericInfoContainer;
+ GENERIC_ENUM_STRUCT infoStruct;
+
+ genericInfoContainer.Buffer = NULL;
+ genericInfoContainer.EntriesRead = 0;
+
+ infoStruct.Container = &genericInfoContainer;
+ infoStruct.Level = level;
+
+ NET_REMOTE_TRY_RPC
+
+ apiStatus = NetrSessionEnum (
+ servername,
+ clientname,
+ username,
+ (PSESSION_ENUM_STRUCT) &infoStruct,
+ prefmaxlen,
+ totalentries,
+ resume_handle);
+
+ if (genericInfoContainer.Buffer != NULL) {
+ *bufptr = (LPBYTE)genericInfoContainer.Buffer;
+ *entriesread = genericInfoContainer.EntriesRead;
+ } else {
+ *bufptr = NULL;
+ *entriesread = 0;
+ }
+
+ NET_REMOTE_RPC_FAILED("NetSessionEnum", servername, apiStatus,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_SERVER )
+
+ //
+ // Call downlevel version of the API.
+ //
+
+ apiStatus = RxNetSessionEnum (
+ servername,
+ clientname,
+ username,
+ level,
+ bufptr,
+ prefmaxlen,
+ entriesread,
+ totalentries,
+ resume_handle);
+
+ NET_REMOTE_END
+
+ return(apiStatus);
+
+} // NetSessionEnum
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetSessionGetInfo (
+ IN LPTSTR servername,
+ IN LPTSTR clientname,
+ IN LPTSTR username,
+ IN DWORD level,
+ OUT LPBYTE *bufptr
+ )
+
+/*++
+
+Routine Description:
+
+ This is the DLL entrypoint for NetSessionEnum.
+
+Arguments:
+
+ servername --A pointer to an ASCIIZ string containing the name of
+ the remote server on which the function is to execute. A NULL
+ pointer or string specifies the local machine.
+
+ clientname --A pointer to an ASCIIZ string containing the name of
+ the computer session for which information is to be returned.
+ This field cannot be NULL.
+
+ username --A pointer to an ASCIIZ string containing the name of
+ the the user for which to ennumerate the sessions. This field
+ cannot be NULL.
+
+ level --Level of information required. 0, 1, 2 and 10 are valid.
+
+ bufptr --On return a pointer to the return information structure
+ is returned in the address pointed to by bufptr.
+
+Return Value:
+
+--*/
+
+{
+ NET_API_STATUS apiStatus;
+ GENERIC_INFO_CONTAINER genericInfoContainer;
+ GENERIC_ENUM_STRUCT infoStruct;
+ DWORD totalentries;
+
+ if ( clientname == NULL || username == NULL ) {
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ genericInfoContainer.Buffer = NULL;
+ genericInfoContainer.EntriesRead = 0;
+
+ infoStruct.Container = &genericInfoContainer;
+ infoStruct.Level = level;
+
+ NET_REMOTE_TRY_RPC
+
+ apiStatus = NetrSessionEnum (
+ servername,
+ clientname,
+ username,
+ (PSESSION_ENUM_STRUCT) &infoStruct,
+ (DWORD)-1,
+ &totalentries,
+ NULL);
+
+ if (genericInfoContainer.Buffer != NULL) {
+ *bufptr = (LPBYTE)genericInfoContainer.Buffer;
+ } else {
+ *bufptr = NULL;
+ if ( apiStatus == NO_ERROR ) {
+ return NERR_ClientNameNotFound;
+ }
+ }
+
+ NET_REMOTE_RPC_FAILED("NetSessionGetInfo", servername, apiStatus,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_SERVER )
+
+ //
+ // Call downlevel version of the API.
+ //
+
+ apiStatus = RxNetSessionGetInfo (
+ servername,
+ clientname,
+ username,
+ level,
+ bufptr);
+
+ NET_REMOTE_END
+
+ return(apiStatus);
+
+} // NetSessionGetInfo
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetShareAdd (
+ IN LPTSTR servername,
+ IN DWORD level,
+ IN LPBYTE buf,
+ OUT LPDWORD parm_err
+ )
+/*++
+
+Routine Description:
+
+ This is the DLL entrypoint for NetShareAdd. Only levels 2 and 502
+ are allowed.
+
+Arguments:
+
+ servername --A pointer to an ASCIIZ string containing the name of
+ the remote server on which the function is to execute. A NULL
+ pointer or string specifies the local machine.
+
+ level --Level of information provided. Must be 2.
+
+ buf --A pointer to a buffer containing the share information
+ structure.
+
+ parm_err --Optional pointer to a DWORD to return the index of the
+ first parameter in error when ERROR_INVALID_PARAMETER is
+ returned. If NULL the parameter is not returned on error.
+
+Return Value:
+
+
+
+--*/
+
+{
+ NET_API_STATUS apiStatus;
+ NTSTATUS status;
+ ULONG SDLength = 0;
+ ULONG oldSDLength;
+ PSECURITY_DESCRIPTOR securityDescriptor = NULL;
+ PSECURITY_DESCRIPTOR oldSecurityDescriptor = NULL;
+
+
+ //
+ // do the parameter validation here - this way we only need do it once and
+ // in a centralized place
+ //
+
+ if (level != 2 && level != 502) {
+ return ERROR_INVALID_LEVEL;
+ }
+
+ NET_REMOTE_TRY_RPC
+
+ if ( level == 502 ) {
+
+ PSHARE_INFO_502 shi502 = (LPSHARE_INFO_502) buf;
+
+ //
+ // Save this. We need to restore this later.
+ //
+
+ oldSecurityDescriptor = shi502->shi502_security_descriptor;
+ oldSDLength = shi502->shi502_reserved;
+
+ if ( oldSecurityDescriptor != NULL ) {
+
+ if ( !RtlValidSecurityDescriptor( oldSecurityDescriptor) ) {
+
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ //
+ // Make a self relative security descriptor for use in the
+ // RPC call..
+ //
+
+ status = RtlMakeSelfRelativeSD(
+ oldSecurityDescriptor,
+ NULL,
+ &SDLength
+ );
+
+ if (status != STATUS_BUFFER_TOO_SMALL) {
+
+ return(ERROR_INVALID_PARAMETER);
+
+ } else {
+
+ securityDescriptor = MIDL_user_allocate( SDLength );
+
+ if ( securityDescriptor == NULL) {
+
+ return ERROR_NOT_ENOUGH_MEMORY;
+
+ } else {
+
+ //
+ // make an appropriate self-relative security descriptor
+ //
+
+ status = RtlMakeSelfRelativeSD(
+ oldSecurityDescriptor,
+ (PSECURITY_DESCRIPTOR) securityDescriptor,
+ &SDLength
+ );
+
+ if ( !NT_SUCCESS(status) ) {
+ MIDL_user_free( securityDescriptor );
+ return(ERROR_INVALID_PARAMETER);
+ }
+
+ shi502->shi502_security_descriptor = securityDescriptor;
+ shi502->shi502_reserved = SDLength;
+
+ }
+ }
+
+ } else {
+
+ shi502->shi502_reserved = 0;
+
+ }
+ }
+
+ apiStatus = NetrShareAdd (
+ servername,
+ level,
+ (LPSHARE_INFO) &buf,
+ parm_err);
+
+ if ( securityDescriptor != NULL ) {
+
+ //
+ // restore old values
+ //
+
+ PSHARE_INFO_502 shi502 = (LPSHARE_INFO_502) buf;
+ shi502->shi502_security_descriptor = oldSecurityDescriptor;
+ shi502->shi502_reserved = oldSDLength;
+ MIDL_user_free( securityDescriptor );
+ }
+
+ NET_REMOTE_RPC_FAILED(
+ "NetShareAdd",
+ servername,
+ apiStatus,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_SERVER)
+
+ //
+ // Call downlevel server.
+ //
+
+ if ( level != 502 ) {
+ apiStatus = RxNetShareAdd(
+ servername,
+ 2,
+ buf,
+ parm_err
+ );
+ } else {
+ apiStatus = ERROR_NOT_SUPPORTED;
+ }
+
+ NET_REMOTE_END
+
+ return(apiStatus);
+
+} // NetShareAdd
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetShareCheck (
+ IN LPTSTR servername,
+ IN LPTSTR device,
+ OUT LPDWORD type
+ )
+
+/*++
+
+Routine Description:
+
+ This is the DLL entrypoint for NetShareCheck
+
+Arguments:
+
+ servername --A pointer to an ASCIIZ string containing the name of
+ the remote server on which the function is to execute. A NULL
+ pointer or string specifies the local machine.
+
+ device --A pointer to an ASCIIZ string containing the name of the
+ device to check for shared access.
+
+ type --On return the address pointed to by the type parameter
+ contains the type of share the device is offered with. This
+ field is only set if success was returned.
+
+Return Value:
+
+--*/
+
+{
+ NET_API_STATUS apiStatus;
+
+
+ if (!(device && *device) || !type) {
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ NET_REMOTE_TRY_RPC
+
+ apiStatus = NetrShareCheck (
+ servername,
+ device,
+ type);
+
+ NET_REMOTE_RPC_FAILED(
+ "NetShareCheck",
+ servername,
+ apiStatus,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_SERVER)
+
+ //
+ // Call downlevel server.
+ //
+
+ apiStatus = RxNetShareCheck(servername, device, type);
+
+ NET_REMOTE_END
+
+ return(apiStatus);
+
+} // NetShareCheck
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetShareDel (
+ IN LPTSTR servername,
+ IN LPTSTR netname,
+ IN DWORD reserved
+ )
+/*++
+
+Routine Description:
+
+ This is the DLL entrypoint for NetShareDel.
+
+Arguments:
+
+ servername --A pointer to an ASCIIZ string containing the name of
+ the remote server on which the function is to execute. A NULL
+ pointer or string specifies the local machine.
+
+ netname --A pointer to an ASCIIZ string containing the netname of
+ the share to delete.
+
+ reserved --Reserved, must be zero.
+
+Return Value:
+
+--*/
+
+{
+ NET_API_STATUS apiStatus;
+ BOOL committingIpcDelete = FALSE;
+ SHARE_DEL_HANDLE handle;
+ BOOL tryDownLevel = FALSE;
+
+ if ( !netname || (*netname == 0) || reserved ) {
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ RpcTryExcept {
+
+ if ( STRICMP( netname, TEXT("IPC$") ) != 0 ) {
+
+ apiStatus = NetrShareDel(
+ servername,
+ netname,
+ reserved
+ );
+
+ } else {
+
+ apiStatus = NetrShareDelStart(
+ servername,
+ netname,
+ reserved,
+ &handle
+ );
+
+ if ( apiStatus == NERR_Success ) {
+ committingIpcDelete = TRUE;
+ apiStatus = NetrShareDelCommit( &handle );
+ }
+
+ }
+
+ } RpcExcept ( 1 ) {
+
+ RPC_STATUS rpcStatus;
+
+ rpcStatus = RpcExceptionCode( );
+
+ if ( committingIpcDelete && (rpcStatus == RPC_S_CALL_FAILED) ) {
+
+ apiStatus = NERR_Success;
+
+ } else {
+
+ apiStatus = NetpHandleRpcFailure(
+ "NetShareDel",
+ rpcStatus,
+ servername,
+ SERVICE_SERVER,
+ NET_REMOTE_FLAG_NORMAL,
+ &tryDownLevel
+ );
+
+ }
+
+ }
+
+ RpcEndExcept
+
+ if (apiStatus == NERR_TryDownLevel) {
+ tryDownLevel = TRUE;
+ }
+
+ if ( tryDownLevel ) {
+
+ //
+ // Call downlevel server.
+ //
+ // note: push value 0 instead of real reserved
+ //
+
+ apiStatus = RxNetShareDel(servername, netname, 0);
+
+ }
+
+ return apiStatus;
+
+} // NetShareDel
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetShareDelSticky (
+ IN LPTSTR servername,
+ IN LPTSTR netname,
+ IN DWORD reserved
+ )
+/*++
+
+Routine Description:
+
+ This is the DLL entrypoint for NetShareDelSticky.
+
+Arguments:
+
+ servername --A pointer to an ASCIIZ string containing the name of
+ the remote server on which the function is to execute. A NULL
+ pointer or string specifies the local machine.
+
+ netname --A pointer to an ASCIIZ string containing the netname of
+ the share to delete.
+
+ reserved --Reserved, must be zero.
+
+Return Value:
+
+--*/
+
+{
+ NET_API_STATUS apiStatus;
+
+
+ if (!(netname && *netname) || reserved) {
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ NET_REMOTE_TRY_RPC
+
+ apiStatus = NetrShareDelSticky (
+ servername,
+ netname,
+ reserved);
+
+ NET_REMOTE_RPC_FAILED(
+ "NetShareDelSticky",
+ servername,
+ apiStatus,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_SERVER)
+
+ //
+ // No downlevel call.
+ //
+
+ apiStatus = ERROR_NOT_SUPPORTED;
+
+ NET_REMOTE_END
+
+ return(apiStatus);
+
+} // NetShareDelSticky
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetShareEnum (
+ IN LPTSTR servername,
+ IN DWORD level,
+ OUT LPBYTE *bufptr,
+ IN DWORD prefmaxlen,
+ OUT LPDWORD entriesread,
+ OUT LPDWORD totalentries,
+ IN OUT LPDWORD resume_handle
+ )
+
+/*++
+
+Routine Description:
+
+ This is the DLL entrypoint for NetShareEnum
+
+Arguments:
+
+ servername --A pointer to an ASCIIZ string containing the name of
+ the remote server on which the function is to execute. A NULL
+ pointer or string specifies the local machine.
+
+ level --Level of information required. 0, 1 and 2 are valid.
+
+ bufptr --On return a pointer to the return information structure
+ is returned in the address pointed to by bufptr.
+
+ prefmaxlen --Prefered maximum length of returned data (in 8-bit
+ bytes). 0xffffffff specifies no limit.
+
+ entriesread --On return the actual enumerated element count is
+ located in the DWORD pointed to by entriesread.
+
+ totalentries --On return the total entries available to be
+ enumerated is located in the DWORD pointed to by
+ totalentries.
+
+ resumehandle --On return, a resume handle is stored in the DWORD
+ pointed to by resumehandle, and is used to continue an
+ existing share search. The handle should be zero on the first
+ call and left unchanged for subsequent calls. If resumehandle
+ is NULL, then no resume handle is stored..
+
+Return Value:
+
+
+--*/
+
+{
+ NET_API_STATUS apiStatus;
+ GENERIC_INFO_CONTAINER genericInfoContainer;
+ GENERIC_ENUM_STRUCT infoStruct;
+
+
+ //
+ // check the caller's parameters
+ //
+
+ *totalentries = *entriesread = 0;
+ *bufptr = NULL;
+
+ if ( (level > 2) && (level != 502) ) {
+ return ERROR_INVALID_LEVEL;
+ }
+
+ genericInfoContainer.Buffer = NULL;
+ genericInfoContainer.EntriesRead = 0;
+
+ infoStruct.Container = &genericInfoContainer;
+ infoStruct.Level = level;
+
+ NET_REMOTE_TRY_RPC
+
+ apiStatus = NetrShareEnum (
+ servername,
+ (LPSHARE_ENUM_STRUCT) &infoStruct,
+ prefmaxlen,
+ totalentries,
+ resume_handle);
+
+ if (genericInfoContainer.Buffer != NULL) {
+ *bufptr = (LPBYTE)genericInfoContainer.Buffer;
+ *entriesread = genericInfoContainer.EntriesRead;
+ } else {
+ *bufptr = NULL;
+ *entriesread = 0;
+ }
+
+ NET_REMOTE_RPC_FAILED(
+ "NetShareEnum",
+ servername,
+ apiStatus,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_SERVER)
+
+ //
+ // Call downlevel server.
+ //
+
+ if ( level != 502 ) {
+ apiStatus = RxNetShareEnum(servername, level, bufptr,
+ prefmaxlen, entriesread, totalentries, resume_handle);
+ } else {
+ apiStatus = ERROR_NOT_SUPPORTED;
+ }
+
+ NET_REMOTE_END
+
+ return(apiStatus);
+
+} // NetShareEnum
+
+NET_API_STATUS NET_API_FUNCTION
+NetShareEnumSticky (
+ IN LPTSTR servername,
+ IN DWORD level,
+ OUT LPBYTE *bufptr,
+ IN DWORD prefmaxlen,
+ OUT LPDWORD entriesread,
+ OUT LPDWORD totalentries,
+ IN OUT LPDWORD resume_handle
+ )
+
+/*++
+
+Routine Description:
+
+ This is the DLL entrypoint for NetShareEnumSticky
+
+Arguments:
+
+ servername --A pointer to an ASCIIZ string containing the name of
+ the remote server on which the function is to execute. A NULL
+ pointer or string specifies the local machine.
+
+ level --Level of information required. 0, 1 and 2 are valid.
+
+ bufptr --On return a pointer to the return information structure
+ is returned in the address pointed to by bufptr.
+
+ prefmaxlen --Prefered maximum length of returned data (in 8-bit
+ bytes). 0xffffffff specifies no limit.
+
+ entriesread --On return the actual enumerated element count is
+ located in the DWORD pointed to by entriesread.
+
+ totalentries --On return the total entries available to be
+ enumerated is located in the DWORD pointed to by
+ totalentries.
+
+ resumehandle --On return, a resume handle is stored in the DWORD
+ pointed to by resumehandle, and is used to continue an
+ existing share search. The handle should be zero on the first
+ call and left unchanged for subsequent calls. If resumehandle
+ is NULL, then no resume handle is stored..
+
+Return Value:
+
+
+--*/
+
+{
+ NET_API_STATUS apiStatus;
+ GENERIC_INFO_CONTAINER genericInfoContainer;
+ GENERIC_ENUM_STRUCT infoStruct;
+
+
+ //
+ // check the caller's parameters
+ //
+
+ *totalentries = *entriesread = 0;
+ *bufptr = NULL;
+
+ if ( (level > 2) && (level != 502) ) {
+ return ERROR_INVALID_LEVEL;
+ }
+
+ genericInfoContainer.Buffer = NULL;
+ genericInfoContainer.EntriesRead = 0;
+
+ infoStruct.Container = &genericInfoContainer;
+ infoStruct.Level = level;
+
+ NET_REMOTE_TRY_RPC
+
+ apiStatus = NetrShareEnumSticky (
+ servername,
+ (LPSHARE_ENUM_STRUCT) &infoStruct,
+ prefmaxlen,
+ totalentries,
+ resume_handle);
+
+ if (genericInfoContainer.Buffer != NULL) {
+ *bufptr = (LPBYTE)genericInfoContainer.Buffer;
+ *entriesread = genericInfoContainer.EntriesRead;
+ } else {
+ *bufptr = NULL;
+ *entriesread = 0;
+ }
+
+ NET_REMOTE_RPC_FAILED(
+ "NetShareEnum",
+ servername,
+ apiStatus,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_SERVER)
+
+ //
+ // No downlevel support
+ //
+
+ apiStatus = ERROR_NOT_SUPPORTED;
+
+ NET_REMOTE_END
+
+ return(apiStatus);
+
+} // NetShareEnumSticky
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetShareGetInfo (
+ IN LPTSTR servername,
+ IN LPTSTR netname,
+ IN DWORD level,
+ OUT LPBYTE *bufptr
+ )
+/*++
+
+Routine Description:
+
+ NetShareGetInfo
+
+Arguments:
+
+ servername --A pointer to an ASCIIZ string containing the name of
+ the remote server on which the function is to execute. A NULL
+ pointer or string specifies the local machine.
+
+ netname --A pointer to an ASCIIZ string containing the netname of
+ the share to return information on.
+
+ level --Level of information required. 0, 1 and 2 are valid.
+
+ bufptr --On return a pointer to the return information structure
+ is returned in the address pointed to by bufptr.
+
+Return Value:
+
+--*/
+
+{
+ NET_API_STATUS apiStatus;
+
+ *bufptr = NULL; // Must be NULL so RPC knows to till it in.
+
+ if ( (level > 2) && (level != 502) && (level != 1005)) {
+ return ERROR_INVALID_LEVEL;
+ }
+
+ if (!(netname && *netname)) {
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ NET_REMOTE_TRY_RPC
+
+ apiStatus = NetrShareGetInfo (
+ servername,
+ netname,
+ level,
+ (LPSHARE_INFO) bufptr);
+
+ NET_REMOTE_RPC_FAILED(
+ "NetShareGetInfo",
+ servername,
+ apiStatus,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_SERVER)
+
+ //
+ // Call downlevel server.
+ //
+
+ if ( level != 502 && level != 1005 ) {
+ apiStatus = RxNetShareGetInfo(servername, netname, level, bufptr);
+ } else {
+ apiStatus = ERROR_NOT_SUPPORTED;
+ }
+
+ NET_REMOTE_END
+
+ return(apiStatus);
+
+} // NetShareGetInfo
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetShareSetInfo (
+ IN LPTSTR servername,
+ IN LPTSTR netname,
+ IN DWORD level,
+ IN LPBYTE buf,
+ OUT LPDWORD parm_err
+ )
+
+/*++
+
+Routine Description:
+
+ This is the DLL entrypoint for NetShareSetInfo
+
+Arguments:
+
+ servername --A pointer to an ASCIIZ string containing the name of
+ the remote server on which the function is to execute. A NULL
+ pointer or string specifies the local machine.
+
+ netname --A pointer to an ASCIIZ string containing the netname of
+ the share to set information on.
+
+ level --Level of information to set.
+
+ buf --A pointer to a buffer containing the share information. If
+ parmnum is non zero then the buffer contains only the
+ appropriate data for the specific element.
+
+ parm_err --Optional pointer to a DWORD to return the index of the
+ first parameter in error when ERROR_INVALID_PARAMETER is
+ returned. If NULL the parameter is not returned on error.
+
+Return Value:
+
+--*/
+
+{
+
+ NET_API_STATUS apiStatus;
+ NTSTATUS status;
+ ULONG sdLength = 0;
+ ULONG oldSdLength;
+ PSECURITY_DESCRIPTOR securityDescriptor = NULL;
+ PSECURITY_DESCRIPTOR oldSecurityDescriptor = NULL;
+ LPSHARE_INFO_1501 shi1501 = NULL;
+
+ NET_REMOTE_TRY_RPC
+
+ //
+ // If the info level can change the security descriptor, get
+ // the necessary information.
+ //
+ // *** Note that this code expects the layout of the reserved
+ // and security_descriptor fields in the 502 struct to
+ // match the 1501 struct.
+ //
+
+ if ( level == 502 ) {
+
+ shi1501 =
+ (LPSHARE_INFO_1501)&((LPSHARE_INFO_502)buf)->shi502_reserved;
+
+ } else if ( level == SHARE_FILE_SD_INFOLEVEL ) {
+
+ shi1501 = (LPSHARE_INFO_1501)buf;
+
+ }
+
+ if ( shi1501 != NULL ) {
+
+ oldSdLength = shi1501->shi1501_reserved;
+ oldSecurityDescriptor = shi1501->shi1501_security_descriptor;
+
+ if ( oldSecurityDescriptor != NULL ) {
+
+ //
+ // Make a self relative security descriptor for use in the
+ // RPC call.
+ //
+
+ if ( !RtlValidSecurityDescriptor( oldSecurityDescriptor) ) {
+
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ status = RtlMakeSelfRelativeSD(
+ oldSecurityDescriptor,
+ NULL,
+ &sdLength
+ );
+
+ if ( status != STATUS_BUFFER_TOO_SMALL ) {
+
+ return ERROR_INVALID_PARAMETER;
+
+ } else {
+
+ securityDescriptor = MIDL_user_allocate( sdLength );
+
+ if ( securityDescriptor == NULL) {
+
+ return ERROR_NOT_ENOUGH_MEMORY;
+
+ } else {
+
+ //
+ // Make an appropriate self-relative security
+ // descriptor.
+ //
+
+ status = RtlMakeSelfRelativeSD(
+ oldSecurityDescriptor,
+ securityDescriptor,
+ &sdLength
+ );
+
+ if ( !NT_SUCCESS(status) ) {
+ MIDL_user_free( securityDescriptor );
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ shi1501->shi1501_reserved = sdLength;
+ shi1501->shi1501_security_descriptor =
+ securityDescriptor;
+
+ }
+
+ }
+
+ } else {
+
+ shi1501->shi1501_reserved = 0;
+
+ }
+ }
+
+ apiStatus = NetrShareSetInfo(
+ servername,
+ netname,
+ level,
+ (LPSHARE_INFO) &buf,
+ parm_err);
+
+ if ( shi1501 != NULL ) {
+
+ //
+ // restore old values
+ //
+
+ shi1501->shi1501_reserved = oldSdLength;
+ shi1501->shi1501_security_descriptor = oldSecurityDescriptor;
+
+ MIDL_user_free( securityDescriptor );
+
+ }
+
+
+ NET_REMOTE_RPC_FAILED(
+ "NetShareSetInfo",
+ servername,
+ apiStatus,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_SERVER)
+
+ //
+ // Call downlevel server.
+ //
+
+ if ( (level != 502) && (level != SHARE_FILE_SD_INFOLEVEL) && (level != 1005 ) ) {
+
+ apiStatus = RxNetShareSetInfo(
+ servername,
+ netname,
+ level,
+ buf,
+ parm_err);
+ } else {
+ apiStatus = ERROR_NOT_SUPPORTED;
+ }
+
+ NET_REMOTE_END
+
+ return(apiStatus);
+
+} // NetShareSetInfo
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetServerDiskEnum (
+ IN LPTSTR servername,
+ IN DWORD level,
+ OUT LPBYTE *bufptr,
+ IN DWORD prefmaxlen,
+ OUT LPDWORD entriesread,
+ OUT LPDWORD totalentries,
+ IN OUT LPDWORD resume_handle
+ )
+
+/*++
+
+Routine Description:
+
+ This is the DLL entrypoint for NetServerDiskEnum.
+
+Arguments:
+
+ servername --A pointer to an ASCIIZ string containing the name of
+ the remote server on which the function is to execute. A NULL
+ pointer or string specifies the local machine.
+
+ level --Level of information required. 0 is the only valid level.
+
+ bufptr --On return a pointer to the return information structure
+ is returned in the address pointed to by bufptr.
+
+ prefmaxlen --Prefered maximum length of returned data (in 8-bit
+ bytes). 0xffffffff specifies no limit.
+
+ entriesread --On return the actual enumerated element count is
+ located in the DWORD pointed to by entriesread.
+
+ totalentries --On return the total entries available to be
+ enumerated is located in the DWORD pointed to by totalentries
+
+ resumehandle --On return, a resume handle is stored in the DWORD
+ pointed to by resumehandle, and is used to continue an
+ existing server disk search. The handle should be zero on the
+ first call and left unchanged for subsequent calls. If
+ resumehandle is NULL, then no resume handle is stored..
+
+Return Value:
+
+--*/
+{
+ NET_API_STATUS apiStatus;
+ DISK_ENUM_CONTAINER diskEnumContainer;
+
+
+ diskEnumContainer.Buffer = NULL;
+
+ NET_REMOTE_TRY_RPC
+
+ apiStatus = NetrServerDiskEnum (
+ servername,
+ level,
+ &diskEnumContainer,
+ prefmaxlen,
+ totalentries,
+ resume_handle);
+
+ if (diskEnumContainer.Buffer != NULL) {
+ *bufptr = (LPBYTE)diskEnumContainer.Buffer;
+ } else {
+ *bufptr = NULL;
+ }
+
+ if (diskEnumContainer.EntriesRead > 0) {
+
+ //
+ // We must subtract out the extra count that we added so
+ // that RPC would buffer the extra NUL at the end of the list.
+ //
+
+ *entriesread = diskEnumContainer.EntriesRead - 1;
+
+ } else {
+ *entriesread = 0;
+ }
+
+ NET_REMOTE_RPC_FAILED(
+ "NetServerDiskEnum",
+ servername,
+ apiStatus,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_SERVER)
+
+ //
+ // Call downlevel version of the API.
+ //
+
+ apiStatus = RxNetServerDiskEnum(
+ servername,
+ level,
+ bufptr,
+ prefmaxlen,
+ entriesread,
+ totalentries,
+ resume_handle);
+
+ NET_REMOTE_END
+
+ return(apiStatus);
+
+} // NetServerDiskEnum
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetServerGetInfo (
+ IN LPTSTR servername,
+ IN DWORD level,
+ OUT LPBYTE *bufptr
+ )
+/*++
+
+Routine Description:
+
+ This is the DLL entrypoint for NetServerGetInfo
+
+Arguments:
+
+ servername --A pointer to an ASCIIZ string containing the name of
+ the remote server on which the function is to execute. A NULL
+ pointer or string specifies the local machine.
+
+ level --Level of information required. 100, 101 and 102 are valid
+ for all platforms. 302, 402, 403, 502 are valid for the
+ appropriate platform.
+
+ bufptr --On return a pointer to the return information structure
+ is returned in the address pointed to by bufptr.
+
+Return Value:
+
+--*/
+
+{
+ NET_API_STATUS apiStatus;
+
+ *bufptr = NULL; // Must be NULL so RPC knows to fill it in.
+
+ NET_REMOTE_TRY_RPC
+
+ apiStatus = NetrServerGetInfo (
+ servername,
+ level,
+ (LPSERVER_INFO) bufptr);
+
+ NET_REMOTE_RPC_FAILED(
+ "NetServerGetInfo",
+ servername,
+ apiStatus,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_SERVER)
+
+ //
+ // Call downlevel version of the API.
+ //
+ apiStatus = RxNetServerGetInfo (
+ servername,
+ level,
+ bufptr);
+
+ NET_REMOTE_END
+
+ return(apiStatus);
+
+} // NetServerGetInfo
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetServerSetInfo (
+ IN LPTSTR servername,
+ IN DWORD level,
+ IN LPBYTE buf,
+ OUT LPDWORD parm_err
+ )
+
+/*++
+
+Routine Description:
+
+ This is the DLL entrypoint for NetServerSetInfo.
+
+Arguments:
+
+ servername --A pointer to an ASCIIZ string containing the name of
+ the remote server on which the function is to execute. A NULL
+ pointer or string specifies the local machine.
+
+ level --Level of information to set.
+
+ buf --A pointer to a buffer containing the server information. If
+ parmnum is non zero then the buffer contains only the
+ appropriate data for the specific element.
+
+ parm_err --Optional pointer to a DWORD to return the index of the
+ first parameter in error when ERROR_INVALID_PARAMETER is
+ returned. If NULL the parameter is not returned on error.
+
+Return Value:
+
+--*/
+{
+ NET_API_STATUS apiStatus;
+
+ NET_REMOTE_TRY_RPC
+
+ apiStatus = NetrServerSetInfo (
+ servername,
+ level,
+ (LPSERVER_INFO ) &buf,
+ parm_err);
+
+ NET_REMOTE_RPC_FAILED(
+ "NetServerSetInfo",
+ servername,
+ apiStatus,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_SERVER)
+
+ //
+ // Call downlevel server.
+ //
+
+ apiStatus = RxNetServerSetInfo(
+ servername,
+ level,
+ buf,
+ parm_err);
+
+ NET_REMOTE_END
+
+ return(apiStatus);
+
+} // NetServerSetInfo
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetServerStatisticsGet (
+ IN LPTSTR servername,
+ IN DWORD level,
+ IN DWORD options,
+ OUT LPBYTE *bufptr
+ )
+
+/*++
+
+Routine Description:
+
+ This is the DLL entrypoint for NetStatisticsGet.
+
+Arguments:
+
+ servername --Points to an ASCIIZ string that contains the name of the
+ server on which to execute NetStatisticsGet. A NULL pointer or
+ NULL string specifies the local computer.
+
+ level --Specifies the level of detail requested; must be 0.
+
+ options --Specifies the options flags.
+
+ Bit(s) Meaning
+ 0 Clear statistics.
+ 1-31 Reserved; must be 0.
+
+ bufptr --On return a pointer to the returned information is
+ returned in the address pointed to by bufptr.
+
+Return Value:
+
+--*/
+{
+ NET_API_STATUS apiStatus;
+
+ *bufptr = NULL; // Must be NULL so RPC knows to fill it in.
+
+ NET_REMOTE_TRY_RPC
+
+ //
+ // BUGBUG - at some point, remove this redundant service name parameter
+ // - this will change MIDL interface
+ //
+
+ apiStatus = NetrServerStatisticsGet (
+ servername,
+ SERVICE_SERVER,
+ level,
+ options,
+ (LPSTAT_SERVER_0 *) bufptr);
+
+ NET_REMOTE_RPC_FAILED(
+ "NetServerStatisticsGet",
+ servername,
+ apiStatus,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_SERVER)
+
+ //
+ // RPC call didn't work - try down-level routine
+ //
+
+ apiStatus = RxNetStatisticsGet(
+ servername,
+ SERVICE_SERVER,
+ level,
+ options,
+ bufptr
+ );
+
+ NET_REMOTE_END
+
+ return(apiStatus);
+
+} // NetServerStatisticsGet
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetServerTransportAdd (
+ IN LPTSTR servername,
+ IN DWORD level,
+ IN LPBYTE bufptr
+ )
+
+/*++
+
+Routine Description:
+
+ This is the DLL entrypoint for NetServerTransportAdd
+
+Arguments:
+
+Return Value:
+
+--*/
+
+{
+ NET_API_STATUS apiStatus;
+
+ NET_REMOTE_TRY_RPC
+
+ apiStatus = NetrServerTransportAdd (
+ servername,
+ level,
+ (LPSERVER_TRANSPORT_INFO_0) bufptr);
+
+ NET_REMOTE_RPC_FAILED(
+ "NetServerTransportAdd",
+ servername,
+ apiStatus,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_SERVER)
+
+ //
+ // No downlevel call.
+ //
+
+ apiStatus = ERROR_NOT_SUPPORTED;
+
+ NET_REMOTE_END
+
+ return(apiStatus);
+
+} // NetServerTransportAdd
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetServerTransportDel (
+ IN LPTSTR servername,
+ IN DWORD level,
+ IN LPBYTE bufptr
+ )
+
+/*++
+
+Routine Description:
+
+ This is the DLL entrypoint for NetServerTransportAdd
+
+Arguments:
+
+Return Value:
+
+--*/
+
+{
+ NET_API_STATUS apiStatus;
+
+ NET_REMOTE_TRY_RPC
+
+ apiStatus = NetrServerTransportDel (
+ servername,
+ level,
+ (LPSERVER_TRANSPORT_INFO_0) bufptr);
+
+ NET_REMOTE_RPC_FAILED(
+ "NetServerTransportDel",
+ servername,
+ apiStatus,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_SERVER)
+
+ //
+ // No downlevel call.
+ //
+
+ apiStatus = ERROR_NOT_SUPPORTED;
+
+ NET_REMOTE_END
+
+ return(apiStatus);
+
+} // NetServerTransportDel
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetServerTransportEnum (
+ IN LPTSTR servername,
+ IN DWORD level,
+ OUT LPBYTE *bufptr,
+ IN DWORD prefmaxlen,
+ OUT LPDWORD entriesread,
+ OUT LPDWORD totalentries,
+ IN OUT LPDWORD resume_handle
+ )
+
+/*++
+
+Routine Description:
+
+ This is the DLL entrypoint for NetServerTransportEnum
+
+Arguments:
+
+Return Value:
+
+--*/
+
+{
+ NET_API_STATUS apiStatus;
+ GENERIC_INFO_CONTAINER genericInfoContainer;
+ GENERIC_ENUM_STRUCT infoStruct;
+
+ genericInfoContainer.Buffer = NULL;
+ genericInfoContainer.EntriesRead = 0;
+
+ infoStruct.Container = &genericInfoContainer;
+ infoStruct.Level = level;
+
+ NET_REMOTE_TRY_RPC
+
+ apiStatus = NetrServerTransportEnum (
+ servername,
+ (LPSERVER_XPORT_ENUM_STRUCT) &infoStruct,
+ prefmaxlen,
+ totalentries,
+ resume_handle);
+
+ if (genericInfoContainer.Buffer != NULL) {
+ *bufptr = (LPBYTE)genericInfoContainer.Buffer;
+ *entriesread = genericInfoContainer.EntriesRead;
+ } else {
+ *bufptr = NULL;
+ *entriesread = 0;
+ }
+
+ NET_REMOTE_RPC_FAILED(
+ "NetServerTransportEnum",
+ servername,
+ apiStatus,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_SERVER)
+
+ //
+ // No downlevel call.
+ //
+
+ apiStatus = ERROR_NOT_SUPPORTED;
+
+ NET_REMOTE_END
+
+ return(apiStatus);
+
+} // NetServerTransportEnum
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetRemoteTOD (
+ IN LPCWSTR servername,
+ OUT LPBYTE *bufptr
+ )
+/*++
+
+Routine Description:
+
+ This is the DLL entrypoint for NetRemoteTOD
+
+Arguments:
+
+ servername - name of the server on which the API so to be executed.
+ bufptr - the location where the address of the buffer allocated
+ for the time-of-day information is placed.
+
+Return Value:
+
+ NERR_SUCCESS if there was no error. Otherwise, the error code is
+ returned.
+
+
+--*/
+{
+ NET_API_STATUS apiStatus;
+
+ //
+ // Call API
+ //
+ *bufptr = NULL; // Must be NULL so RPC knows to fill it in.
+
+ NET_REMOTE_TRY_RPC
+
+ apiStatus = NetrRemoteTOD (
+ (LPWSTR)servername,
+ (TIME_OF_DAY_INFO **) bufptr);
+
+ NET_REMOTE_RPC_FAILED(
+ "NetRemoteTOD",
+ servername,
+ apiStatus,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_TIMESOURCE )
+
+ apiStatus = RxNetRemoteTOD (
+ (LPWSTR)servername,
+ (LPBYTE *) bufptr);
+
+ NET_REMOTE_END
+
+ return(apiStatus);
+}
+
+
+NET_API_STATUS
+I_NetServerSetServiceBitsEx (
+ IN LPWSTR ServerName,
+ IN LPWSTR EmulatedServerName OPTIONAL,
+ IN LPTSTR TransportName OPTIONAL,
+ IN DWORD ServiceBitsOfInterest,
+ IN DWORD ServiceBits,
+ IN DWORD UpdateImmediately
+ )
+
+/*++
+
+Routine Description:
+
+ This is the DLL entrypoint for I_NetServerSetServiceBitsEx. This
+ routine sets the value of the Server Type as sent in server
+ announcement messages. It is an internal API used only by the
+ service controller.
+
+Arguments:
+
+ ServerName - Used by RPC to direct the call. This API may only be
+ issued locally. This is enforced by the client stub.
+
+ EmulatedServerName - the name server using for accepting connections
+ on the network and for announcements. If null, use the priamary
+ server name.
+
+ TransportName - the name of one of the transports the server is bound
+ on. If null, set the bits for all the transports.
+
+ ServiceBitsOfInterest - a mask indicating which bits are significant
+ in 'ServiceBits'
+
+ ServiceBits - Bits (preassigned to various components by Microsoft)
+ indicating which services are active. This field is not
+ interpreted by the server service.
+
+Return Value:
+
+ NET_API_STATUS - NO_ERROR or ERROR_NOT_SUPPORTED.
+
+--*/
+
+{
+ NET_API_STATUS apiStatus;
+ DWORD localOrRemote;
+
+ //
+ // Don't let this API go remote.
+ //
+
+ if ((ServerName != NULL) && (*ServerName != '\0')) {
+ apiStatus = NetpIsRemote(ServerName, &localOrRemote, NULL, 0);
+ if (apiStatus != NERR_Success) {
+ return apiStatus;
+ }
+ if (localOrRemote == ISREMOTE) {
+ return ERROR_NOT_SUPPORTED;
+ }
+ }
+
+ //
+ // Do the call.
+ //
+
+ NET_REMOTE_TRY_RPC
+
+ apiStatus = I_NetrServerSetServiceBitsEx (
+ ServerName,
+ EmulatedServerName,
+ TransportName,
+ ServiceBitsOfInterest,
+ ServiceBits,
+ UpdateImmediately);
+
+ NET_REMOTE_RPC_FAILED(
+ "I_NetServerSetServiceBitsEx",
+ ServerName,
+ apiStatus,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_SERVER)
+
+ //
+ // No downlevel call.
+ //
+
+ apiStatus = ERROR_NOT_SUPPORTED;
+
+ NET_REMOTE_END
+
+ return(apiStatus);
+
+} // I_NetServerSetServiceBitsEx
+
+NET_API_STATUS
+I_NetServerSetServiceBits (
+ IN LPTSTR servername,
+ IN LPTSTR transportname,
+ IN DWORD servicebits,
+ IN DWORD updateimmediately
+ )
+
+/*++
+
+Routine Description:
+
+ This is the DLL entrypoint for I_NetServerSetServiceBits. This
+ routine sets the value of the Server Type as sent in server
+ announcement messages. It is an internal API used only by the
+ service controller.
+
+Arguments:
+
+ ServerName - Used by RPC to direct the call. This API may only be
+ issued locally. This is enforced by the client stub.
+
+ ServiceBits - Bits (preassigned to various components by Microsoft)
+ indicating which services are active. This field is not
+ interpreted by the server service.
+
+Return Value:
+
+ NET_API_STATUS - NO_ERROR or ERROR_NOT_SUPPORTED.
+
+--*/
+
+{
+ NET_API_STATUS apiStatus;
+ DWORD localOrRemote;
+
+ //
+ // Don't let this API go remote.
+ //
+
+ if ((servername != NULL) && (*servername != '\0')) {
+ apiStatus = NetpIsRemote(servername, &localOrRemote, NULL, 0);
+ if (apiStatus != NERR_Success) {
+ return apiStatus;
+ }
+ if (localOrRemote == ISREMOTE) {
+ return ERROR_NOT_SUPPORTED;
+ }
+ }
+
+ //
+ // Do the call.
+ //
+
+ NET_REMOTE_TRY_RPC
+
+ apiStatus = I_NetrServerSetServiceBits (
+ servername,
+ transportname,
+ servicebits,
+ updateimmediately);
+
+ NET_REMOTE_RPC_FAILED(
+ "I_NetServerSetServiceBits",
+ servername,
+ apiStatus,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_SERVER)
+
+ //
+ // No downlevel call.
+ //
+
+ apiStatus = ERROR_NOT_SUPPORTED;
+
+ NET_REMOTE_END
+
+ return(apiStatus);
+
+} // I_NetServerSetServiceBits
+//
+// Netps canonicalization functions. These are essentially private functions
+// and are called from the API stubs in canonapi.c. The canonicalization
+// functions have to be usable locally without going via the server service
+// hence they live in NETAPI.DLL, but call local functions in NETLIB if the
+// ServerName parameter is NULL (or designates the local machine). If the
+// ServerName parameter is not NULL and designates a remote computer then the
+// RPC function (here) will be called, hence the remote server must be
+// running in order to make remote canonicalization requests
+//
+
+NET_API_STATUS
+NET_API_FUNCTION
+NetpsNameCanonicalize(
+ IN LPTSTR ServerName,
+ IN LPTSTR Name,
+ OUT LPTSTR Outbuf,
+ IN DWORD OutbufLen,
+ IN DWORD NameType,
+ IN DWORD Flags
+ )
+
+/*++
+
+Routine Description:
+
+ Canonicalizes a name
+
+Arguments:
+
+ ServerName - where to run this API
+ Name - name to canonicalize
+ Outbuf - where to put canonicalized name
+ OutbufLen - length of Outbuf
+ NameType - type of name to canonicalize
+ Flags - control flags
+
+Return Value:
+
+ NET_API_STATUS
+
+--*/
+
+{
+ NET_API_STATUS apiStatus;
+
+ NET_REMOTE_TRY_RPC
+
+ apiStatus = NetprNameCanonicalize(ServerName,
+ Name,
+ Outbuf,
+ OutbufLen,
+ NameType,
+ Flags
+ );
+
+ NET_REMOTE_RPC_FAILED("NetpsNameCanonicalize",
+ ServerName,
+ apiStatus,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_SERVER)
+
+ //
+ // RPC call didn't work - try down-level routine
+ //
+
+ apiStatus = RxNetpNameCanonicalize(ServerName,
+ Name,
+ Outbuf,
+ OutbufLen,
+ NameType,
+ Flags
+ );
+
+ NET_REMOTE_END
+
+ return apiStatus;
+}
+
+LONG
+NET_API_FUNCTION
+NetpsNameCompare(
+ IN LPTSTR ServerName,
+ IN LPTSTR Name1,
+ IN LPTSTR Name2,
+ IN DWORD NameType,
+ IN DWORD Flags
+ )
+
+/*++
+
+Routine Description:
+
+ Compares two names. Must be of same type
+
+Arguments:
+
+ ServerName - where to run this API
+ Name1 - 1st name to compare
+ Name2 - 2nd
+ NameType - type of names
+ Flags - control flags
+
+Return Value:
+
+ LONG
+
+--*/
+
+{
+ NET_API_STATUS apiStatus;
+
+ NET_REMOTE_TRY_RPC
+
+ apiStatus = NetprNameCompare(ServerName, Name1, Name2, NameType, Flags);
+
+ NET_REMOTE_RPC_FAILED("NetpsNameCompare",
+ ServerName,
+ apiStatus,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_SERVER)
+
+ //
+ // RPC call didn't work - try down-level routine
+ //
+
+ apiStatus = RxNetpNameCompare(ServerName, Name1, Name2, NameType, Flags);
+
+ NET_REMOTE_END
+
+ return apiStatus;
+}
+
+NET_API_STATUS
+NET_API_FUNCTION
+NetpsNameValidate(
+ IN LPTSTR ServerName,
+ IN LPTSTR Name,
+ IN DWORD NameType,
+ IN DWORD Flags
+ )
+
+/*++
+
+Routine Description:
+
+ Validates a name - checks whether a name of a certain type conforms to
+ canonicalization rules for that name type. Canonicalization rules mean
+ character set, name syntax and length
+
+Arguments:
+
+ ServerName - where to perform this function
+ Name - name to validate
+ NameType - what type of name it is
+ Flags - MBZ
+
+Return Value:
+
+ NET_API_STATUS
+
+--*/
+
+{
+ NET_API_STATUS apiStatus;
+
+ NET_REMOTE_TRY_RPC
+
+ apiStatus = NetprNameValidate(ServerName, Name, NameType, Flags);
+
+ NET_REMOTE_RPC_FAILED("NetpsNameValidate",
+ ServerName,
+ apiStatus,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_SERVER)
+
+ //
+ // RPC call didn't work - try down-level routine
+ //
+
+ apiStatus = RxNetpNameValidate(ServerName, Name, NameType, Flags);
+
+ NET_REMOTE_END
+
+ return apiStatus;
+}
+
+NET_API_STATUS
+NET_API_FUNCTION
+NetpsPathCanonicalize(
+ IN LPTSTR ServerName,
+ IN LPTSTR PathName,
+ OUT LPTSTR Outbuf,
+ IN DWORD OutbufLen,
+ IN LPTSTR Prefix OPTIONAL,
+ IN OUT LPDWORD PathType,
+ IN DWORD Flags
+ )
+
+/*++
+
+Routine Description:
+
+ Canonicalizes a directory path or a device name
+
+Arguments:
+
+ ServerName - where to run this API
+ PathName - path to canonicalize
+ Outbuf - where to write the canonicalized version
+ OutbufLen - length of Outbuf in bytes
+ Prefix - optional prefix which will be prepended to Path
+ PathType - the type of path to canonicalize. May be different at output
+ Flags - control flags
+
+Return Value:
+
+ NET_API_STATUS
+
+--*/
+
+{
+ NET_API_STATUS apiStatus;
+
+ NET_REMOTE_TRY_RPC
+
+ apiStatus = NetprPathCanonicalize(ServerName,
+ PathName,
+ (LPBYTE)Outbuf,
+ OutbufLen,
+ Prefix,
+ PathType,
+ Flags
+ );
+
+ NET_REMOTE_RPC_FAILED("NetpsPathCanonicalize",
+ ServerName,
+ apiStatus,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_SERVER)
+
+ //
+ // RPC call didn't work - try down-level routine
+ //
+
+ apiStatus = RxNetpPathCanonicalize(ServerName,
+ PathName,
+ Outbuf,
+ OutbufLen,
+ Prefix,
+ PathType,
+ Flags
+ );
+
+ NET_REMOTE_END
+
+ return apiStatus;
+}
+
+LONG
+NET_API_FUNCTION
+NetpsPathCompare(
+ IN LPTSTR ServerName,
+ IN LPTSTR PathName1,
+ IN LPTSTR PathName2,
+ IN DWORD PathType,
+ IN DWORD Flags
+ )
+
+/*++
+
+Routine Description:
+
+ Compares two paths. The paths are assumed to be of the same type
+
+Arguments:
+
+ ServerName - where to run this API
+ PathName1 - 1st path to compare
+ PathName2 - 2nd
+ PathType - types of paths
+ Flags - control flags
+
+Return Value:
+
+ LONG
+
+--*/
+
+{
+ NET_API_STATUS apiStatus;
+
+ NET_REMOTE_TRY_RPC
+
+ apiStatus = NetprPathCompare(ServerName,
+ PathName1,
+ PathName2,
+ PathType,
+ Flags
+ );
+
+ NET_REMOTE_RPC_FAILED("NetpsPathCompare",
+ ServerName,
+ apiStatus,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_SERVER)
+
+ //
+ // RPC call didn't work - try down-level routine
+ //
+
+ apiStatus = RxNetpPathCompare(ServerName,
+ PathName1,
+ PathName2,
+ PathType,
+ Flags
+ );
+
+ NET_REMOTE_END
+
+ return apiStatus;
+}
+
+NET_API_STATUS
+NET_API_FUNCTION
+NetpsPathType(
+ IN LPTSTR ServerName,
+ IN LPTSTR PathName,
+ OUT LPDWORD PathType,
+ IN DWORD Flags
+ )
+
+/*++
+
+Routine Description:
+
+ Determines the type of a path
+
+Arguments:
+
+ ServerName - where to run this API
+ PathName - to find type of
+ PathType - returned path type
+ Flags - control flags
+
+Return Value:
+
+ NET_API_STATUS
+
+--*/
+
+{
+ NET_API_STATUS apiStatus;
+
+ NET_REMOTE_TRY_RPC
+
+ apiStatus = NetprPathType(ServerName, PathName, PathType, Flags);
+
+ NET_REMOTE_RPC_FAILED("NetpsPathType",
+ ServerName,
+ apiStatus,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_SERVER)
+
+ //
+ // RPC call didn't work - try down-level routine
+ //
+
+ apiStatus = RxNetpPathType(ServerName, PathName, PathType, Flags);
+
+ NET_REMOTE_END
+
+ return apiStatus;
+}
+
+NET_API_STATUS NET_API_FUNCTION
+NetServerTransportAddEx (
+ IN LPTSTR servername,
+ IN DWORD level,
+ IN LPBYTE bufptr
+ )
+
+/*++
+
+Routine Description:
+
+ This is the DLL entrypoint for NetServerTransportAddEx. It functions
+ just like NetServerTransportAdd, but it supports level 1 as well as 0
+
+--*/
+
+{
+ NET_API_STATUS apiStatus;
+
+ NET_REMOTE_TRY_RPC
+
+ apiStatus = NetrServerTransportAddEx (
+ servername,
+ level,
+ (LPTRANSPORT_INFO) bufptr);
+
+ NET_REMOTE_RPC_FAILED(
+ "NetServerTransportAddEx",
+ servername,
+ apiStatus,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_SERVER)
+
+ //
+ // No downlevel call.
+ //
+
+ apiStatus = ERROR_NOT_SUPPORTED;
+
+ NET_REMOTE_END
+
+ if( apiStatus == RPC_NT_PROCNUM_OUT_OF_RANGE ) {
+ apiStatus = NERR_InvalidAPI;
+ }
+
+ return(apiStatus);
+
+} // NetServerTransportAddEx
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetServerComputerNameAdd(
+ IN LPWSTR ServerName OPTIONAL,
+ IN LPWSTR EmulatedDomainName OPTIONAL,
+ IN LPWSTR EmulatedServerName
+)
+/*++
+
+Routine Description:
+
+ This is the DLL entrypoint for NetServerComputerNameAdd. This api
+ causes 'ServerName' to respond to requests for 'EmulatedServerName'.
+
+Arguments:
+
+ servername --A pointer to an ASCIIZ string containing the name of
+ the remote server on which the function is to execute. A NULL
+ pointer or string specifies the local machine.
+
+ EmulatedServerName --A pointer to the ASCIIZ string containing the
+ name which the server should stop supporting
+
+ EmulatedDomainName --A pointer to the ASCIIZ string containing the
+ domain name the server should use when announcing the presence of
+ 'EmulatedServerName'
+
+Return Value:
+
+ NERR_Success, or reason for failure
+
+--*/
+{
+ DWORD resumehandle = 0;
+ NET_API_STATUS retval;
+ DWORD entriesread, totalentries;
+ DWORD i, j;
+ BOOLEAN AddedOne = FALSE;
+ UCHAR NetBiosName[ MAX_PATH ];
+ OEM_STRING NetBiosNameString;
+ UNICODE_STRING UniName;
+
+ //
+ // Ensure a valid EmulatedServerName was passed in
+ //
+ if( EmulatedServerName == NULL ) {
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ //
+ // Convert the EmulatedServerName to an OEM string
+ //
+ RtlInitUnicodeString( &UniName, EmulatedServerName );
+ NetBiosNameString.Buffer = (PCHAR)NetBiosName;
+ NetBiosNameString.MaximumLength = sizeof( NetBiosName );
+ (VOID) RtlUpcaseUnicodeStringToOemString(
+ &NetBiosNameString,
+ &UniName,
+ FALSE
+ );
+
+ if( ARGUMENT_PRESENT( EmulatedDomainName ) ) {
+
+ //
+ // The caller wants to set a new computer name and domain name to the
+ // server. This requires level 1, which is new in 4.0
+ //
+ PSERVER_TRANSPORT_INFO_1 psti1;
+
+ //
+ // Enumerate all the transports so we can add the name and domain
+ // to each one.
+ //
+ retval = NetServerTransportEnum ( ServerName,
+ 1,
+ (LPBYTE *)&psti1,
+ (DWORD)-1,
+ &entriesread,
+ &totalentries,
+ &resumehandle );
+ if( retval == NERR_Success ) {
+ //
+ // Add the new name and domain to all of the transports
+ //
+ for( i=0; i < entriesread; i++ ) {
+
+ //
+ // Make sure we haven't already added to this transport
+ //
+ for( j = 0; j < i; j++ ) {
+ if( wcscmp( psti1[j].svti1_transportname, psti1[i].svti1_transportname ) == 0 )
+ break;
+ }
+
+ if( i != j )
+ continue;
+
+ psti1[i].svti1_transportaddress = NetBiosName;
+ psti1[i].svti1_transportaddresslength = strlen( NetBiosName );
+ psti1[i].svti1_domain = EmulatedDomainName;
+
+ retval = NetServerTransportAddEx( ServerName, 1, (LPBYTE)&psti1[ i ] );
+
+ if( retval == NERR_Success ) {
+ AddedOne = TRUE;
+ }
+ }
+
+ MIDL_user_free( psti1 );
+ }
+
+
+ } else {
+
+ //
+ // The caller just wants to set an alternate server name. Use level 0,
+ // since 3.51 servers support level 0
+ //
+ PSERVER_TRANSPORT_INFO_0 psti0;
+
+ //
+ // Enumerate all the transports so we can add the name and domain
+ // to each one.
+ //
+ retval = NetServerTransportEnum ( ServerName,
+ 0,
+ (LPBYTE *)&psti0,
+ (DWORD)-1,
+ &entriesread,
+ &totalentries,
+ &resumehandle );
+ if( retval == NERR_Success ) {
+ //
+ // Add the new name to all of the transports
+ //
+ for( i=0; i < entriesread; i++ ) {
+
+ //
+ // Make sure we haven't already added to this transport
+ //
+ for( j = 0; j < i; j++ ) {
+ if( wcscmp( psti0[j].svti0_transportname, psti0[i].svti0_transportname ) == 0 )
+ break;
+ }
+
+ if( i != j )
+ continue;
+
+ psti0[i].svti0_transportaddress = NetBiosName;
+ psti0[i].svti0_transportaddresslength = strlen( NetBiosName );
+
+ retval = NetServerTransportAdd( ServerName, 0, (LPBYTE)&psti0[ i ] );
+
+ if( retval == NERR_Success ) {
+ AddedOne = TRUE;
+ }
+ }
+
+ MIDL_user_free( psti0 );
+ }
+ }
+
+ return AddedOne ? NERR_Success : retval;
+}
+
+NET_API_STATUS NET_API_FUNCTION
+NetServerComputerNameDel (
+ IN LPWSTR ServerName OPTIONAL,
+ IN LPWSTR EmulatedServerName
+)
+/*++
+
+Routine Description:
+
+ This is the DLL entrypoint for NetServerComputerNameDel. This api
+ causes 'ServerName' to cease responding to requests for 'EmulatedServerName'
+
+Arguments:
+
+ servername --A pointer to an ASCIIZ string containing the name of
+ the remote server on which the function is to execute. A NULL
+ pointer or string specifies the local machine.
+
+ EmulatedServerName --A pointer to the ASCIIZ string containing the
+ name which the server should stop supporting
+
+Return Value:
+
+ NERR_Success, or reason for failure
+
+--*/
+{
+ DWORD resumehandle = 0;
+ NET_API_STATUS retval, tmpretval;
+ DWORD entriesread, totalentries;
+ DWORD i;
+ UCHAR NetBiosName[MAX_PATH];
+ OEM_STRING NetBiosNameString;
+ UNICODE_STRING UniName;
+ PSERVER_TRANSPORT_INFO_0 psti0;
+ BOOLEAN nameFound = FALSE;
+
+ //
+ // Ensure a valid EmulatedServerName was passed in
+ //
+ if( EmulatedServerName == NULL ) {
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ //
+ // Convert the EmulatedServerName to an OEM string
+ //
+ RtlInitUnicodeString( &UniName, EmulatedServerName );
+ NetBiosNameString.Buffer = (PCHAR)NetBiosName;
+ NetBiosNameString.MaximumLength = sizeof( NetBiosName );
+ (VOID) RtlUpcaseUnicodeStringToOemString(
+ &NetBiosNameString,
+ &UniName,
+ FALSE
+ );
+
+ //
+ // Enumerate all the transports so we can delete the name from each one
+ //
+ retval = NetServerTransportEnum ( ServerName,
+ 0,
+ (LPBYTE *)&psti0,
+ (DWORD)-1,
+ &entriesread,
+ &totalentries,
+ &resumehandle );
+ if( retval != NERR_Success ) {
+ return retval;
+ }
+
+ if( entriesread == 0 ) {
+ return ERROR_BAD_NET_NAME;
+ }
+
+ //
+ // Delete the name from all of the transports
+ //
+ for( i=0; i < entriesread; i++ ) {
+
+ if( psti0[i].svti0_transportaddresslength != NetBiosNameString.Length ) {
+ continue;
+ }
+
+ if( RtlCompareMemory( psti0[i].svti0_transportaddress,
+ NetBiosName,
+ NetBiosNameString.Length) != NetBiosNameString.Length ) {
+ continue;
+ }
+
+ nameFound = TRUE;
+
+ tmpretval = NetServerTransportDel( ServerName, 0, (LPBYTE)&psti0[ i ] );
+
+ if( tmpretval != NERR_Success && retval == NERR_Success ) {
+ retval = tmpretval;
+ }
+ }
+
+ MIDL_user_free( psti0 );
+
+ return nameFound == TRUE ? retval : ERROR_BAD_NET_NAME;
+}
diff --git a/private/net/svcdlls/srvsvc/client/tsrvsvc.c b/private/net/svcdlls/srvsvc/client/tsrvsvc.c
new file mode 100644
index 000000000..0eb7715aa
--- /dev/null
+++ b/private/net/svcdlls/srvsvc/client/tsrvsvc.c
@@ -0,0 +1,90 @@
+#include <windows.h>
+#include <lmcons.h>
+#include <lmerr.h>
+#include <lmshare.h>
+#include <lmserver.h>
+
+int
+main (
+ int argc,
+ char *argv[]
+ )
+{
+ LPBYTE buffer;
+ DWORD entries;
+ DWORD totalEntries;
+ NET_API_STATUS status;
+
+#if 0
+ status = I_NetServerSetServiceBits( NULL, 0x5555AAAA );
+
+ if ( status != NERR_Success ) {
+
+ printf( "I_NetServerSetServiceBits failed: %ld\n", status );
+
+ }
+#endif
+
+#if 0
+ status = NetServerDiskEnum(
+ NULL,
+ 0,
+ &buffer,
+ -1,
+ &entries,
+ &totalEntries,
+ NULL
+ );
+
+ if ( status != NERR_Success ) {
+
+ printf( "NetServerDiskEnum failed: %ld\n", status );
+
+ } else {
+
+ PSZ p = buffer;
+ DWORD i = 0;
+
+ while ( *p != 0 ) {
+ printf( "Disk %ld is %s\n", i, p );
+ while ( *(++p) != 0 ) ;
+ p++;
+ i++;
+ }
+
+ if ( i != entries ) {
+ printf( "Incorrect entry count returned: %ld\n", entries );
+ }
+
+ }
+
+ return status;
+#endif
+
+#if 1
+ status = NetShareEnum(
+ "",
+ 0,
+ &buffer,
+ 8192,
+ &entries,
+ &totalEntries,
+ NULL
+ );
+
+ if ( status != NERR_Success ) {
+
+ printf( "NetShareEnum failed: %ld\n", status );
+
+ } else {
+
+ printf( "NetShareEnum worked.\n" );
+ printf( " entries = %ld, totalEntries = %ld\n",
+ entries, totalEntries );
+
+ }
+
+ return status;
+#endif
+
+} // main
diff --git a/private/net/svcdlls/srvsvc/dirs b/private/net/svcdlls/srvsvc/dirs
new file mode 100644
index 000000000..87ef360df
--- /dev/null
+++ b/private/net/svcdlls/srvsvc/dirs
@@ -0,0 +1,30 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ dirs.
+
+Abstract:
+
+ This file specifies the subdirectories of the current directory that
+ contain component makefiles.
+
+
+Author:
+
+ Steve Wood (stevewo) 17-Apr-1990
+
+NOTE: Commented description of this file is in \nt\bak\bin\dirs.tpl
+
+!ENDIF
+
+
+DIRS=lib \
+ server \
+ client
+
+
+#OPTIONAL_DIRS=dir8 \
+# dir9
diff --git a/private/net/svcdlls/srvsvc/import.h b/private/net/svcdlls/srvsvc/import.h
new file mode 100644
index 000000000..1610997c6
--- /dev/null
+++ b/private/net/svcdlls/srvsvc/import.h
@@ -0,0 +1,55 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ import.h
+
+Abstract:
+
+ This file allows us to include standard system header files in the
+ .idl file. The main .idl file imports a file called import.idl.
+ This allows the .idl file to use the types defined in these header
+ files. It also causes the following line to be added in the
+ MIDL generated header file:
+
+ #include "import.h"
+
+ Thus these types are available to the RPC stub routines as well.
+
+Author:
+
+ Dan Lafferty (danl) 07-May-1991
+
+Revision History:
+
+
+--*/
+
+//#include <windef.h>
+#include <nt.h>
+#include <ntrtl.h>
+
+// #include <rpc.h>
+#include <windef.h>
+#include <winerror.h>
+
+#include <lmcons.h>
+
+#ifdef MIDL_PASS
+#ifdef UNICODE
+#define LPTSTR [string] wchar_t*
+#else
+#define LPTSTR [string] LPTSTR
+#endif
+#define LPSTR [string] LPSTR
+#define BOOL DWORD
+#endif
+
+#include <lmchdev.h>
+#include <lmremutl.h>
+#include <lmserver.h>
+#include <lmshare.h>
+#include <lmstats.h>
+#include <dfspriv.h>
diff --git a/private/net/svcdlls/srvsvc/import.idl b/private/net/svcdlls/srvsvc/import.idl
new file mode 100644
index 000000000..c032eb6fe
--- /dev/null
+++ b/private/net/svcdlls/srvsvc/import.idl
@@ -0,0 +1,58 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ import.idl
+
+Abstract:
+
+ This file is useful for creating RPC interfaces that require the use
+ of windef types. The .idl file for the RPC product should contain a
+ line in the interface body that imports this file. For example:
+
+ import "import.h";
+
+ Doing this causes the MIDL generated header file to contain the
+ following line:
+
+ #include "import.h"
+
+ If this technique is not used, and instead the .idl file for the RPC
+ product simply contains #include <import.h>, then the contents of
+ import.h will be expanded in the MIDL generated header file. This
+ can lead to duplicate definition problems later when the RPC client
+ or RPC server code needs to include both the MIDL generated header file
+ and a file that is included in import.h.
+
+Author:
+
+ Dan Lafferty (danl) 20-Mar-1991
+
+Environment:
+
+ User Mode - Win32 - for use with the MIDL compiler
+
+
+Revision History:
+
+ 03-Apr-1991 danl
+ created
+
+--*/
+
+[
+ uuid(12345678-1234-ABCD-EF00-9948756789AB),
+#ifdef __midl
+ ms_union,
+#endif // __midl
+ version(1.0)
+]
+interface imports
+
+{
+#define MIDL_PASS
+#include "import.h"
+
+}
diff --git a/private/net/svcdlls/srvsvc/lib/adtcomn.c b/private/net/svcdlls/srvsvc/lib/adtcomn.c
new file mode 100644
index 000000000..730c55b10
--- /dev/null
+++ b/private/net/svcdlls/srvsvc/lib/adtcomn.c
@@ -0,0 +1,216 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ adtcomn.c
+
+Abstract:
+
+ AdminTools common Routines.
+
+ This file contains the calls to GetFileSecurity and
+ SetFileSecurity that is used on both the client and server
+ sides of this RPC server.
+
+Author:
+
+ Dan Lafferty (danl) 23-Mar-1993
+
+Environment:
+
+ User Mode - Win32
+
+
+Revision History:
+
+ 23-Mar-1993 danl
+ Created
+
+--*/
+
+//
+// Includes
+//
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windows.h>
+#include <lmcons.h>
+#include <lmerr.h>
+
+#include <rpc.h>
+#include <srvsvc.h>
+#include <netlibnt.h> // NetpNtStatusToApiStatus
+
+#include "adtcomn.h"
+
+//
+// LOCAL FUNCTIONS
+//
+
+
+DWORD
+PrivateGetFileSecurity (
+ LPWSTR FileName,
+ SECURITY_INFORMATION RequestedInfo,
+ PSECURITY_DESCRIPTOR *pSDBuffer,
+ LPDWORD pBufSize
+ )
+
+/*++
+
+Routine Description:
+
+ This function returns to the caller a copy of the security descriptor
+ protecting a file or directory. It calls GetFileSecurity. The
+ Security Descriptor is always returned in the self-relative format.
+
+ NOTE: This function allocates storage for the pSDBuffer. Therefore,
+ this pointer must be free'd by the caller.
+
+Arguments:
+
+ FileName - A pointer to the name fo the file or directory whose
+ security is being retrieved.
+
+ RequestedInfo - The type of security information being requested.
+
+ pSDBuffer - A pointer to a location where a pointer for the
+ security descriptor and a length field for the security descriptor.
+
+ pBufSize - A pointer to the location where the size, in bytes, of
+ the returned security descriptor is to be placed.
+
+
+Return Value:
+
+ NERR_Success - The operation was successful.
+
+ ERROR_NOT_ENOUGH_MEMORY - Unable to allocate memory for the security
+ descriptor.
+
+ This function can also return any error that GetFileSecurity can
+ return.
+
+
+--*/
+{
+
+ NET_API_STATUS status;
+ DWORD sizeNeeded;
+
+ *pSDBuffer = NULL;
+ //
+ // Determine the buffer size for the Descriptor
+ //
+ if (GetFileSecurityW(
+ FileName, // File whose security is being retrieved
+ RequestedInfo, // security info being requested
+ *pSDBuffer, // buffer to receive security descriptor
+ 0, // size of the buffer
+ &sizeNeeded)) { // size of buffer required
+
+ //
+ // We should have a failed due to a buffer size being too small.
+ //
+ status = ERROR_INVALID_PARAMETER;
+ goto CleanExit;
+ }
+
+ status = GetLastError();
+
+ if ((status == ERROR_INSUFFICIENT_BUFFER) && (sizeNeeded > 0)) {
+
+ *pSDBuffer = MIDL_user_allocate(sizeNeeded);
+
+ if (pSDBuffer == NULL) {
+ status = GetLastError();
+ ADT_LOG1(ERROR,"NetrpGetFileSecurity:MIDL_user_alloc1 failed %d\n",status);
+ goto CleanExit;
+ }
+ *pBufSize = sizeNeeded;
+
+ if (!GetFileSecurityW(
+ FileName, // File whose security is being retrieved
+ RequestedInfo, // security info being requested
+ *pSDBuffer, // buffer to receive security descriptor
+ sizeNeeded, // size of the buffer
+ &sizeNeeded)) { // size of buffer required
+
+ //
+ // The call with the proper buffer size failed.
+ //
+ status = GetLastError();
+ ADT_LOG1(ERROR, "GetFileSecurity Failed %d\n", status);
+ MIDL_user_free(*pSDBuffer);
+ goto CleanExit;
+ }
+
+ ADT_LOG0(TRACE,"NetrpGetFileSecurity:GetFileSecurity Success\n");
+
+ if (!IsValidSecurityDescriptor(*pSDBuffer)) {
+ ADT_LOG0(TRACE,"FAILURE: SECURITY DESCRIPTOR IS INVALID\n");
+ }
+ else {
+ ADT_LOG0(TRACE,"SUCCESS: SECURITY DESCRIPTOR IS GOOD\n");
+ }
+ status = NO_ERROR;
+ }
+
+CleanExit:
+ return(status);
+}
+
+
+DWORD
+PrivateSetFileSecurity (
+ LPWSTR FileName,
+ SECURITY_INFORMATION SecurityInfo,
+ PSECURITY_DESCRIPTOR pSecurityDescriptor
+ )
+
+/*++
+
+Routine Description:
+
+ This function can be used to set the security of a file or directory.
+ It calls SetFileSecurity().
+
+Arguments:
+
+ FileName - A pointer to the name of the file or directory whose
+ security is being changed.
+
+ SecurityInfo - Information describing the contents
+ of the Security Descriptor.
+
+ pSecurityDescriptor - A pointer to a structure that contains a
+ self-relative security descriptor and a length.
+
+Return Value:
+
+ NERR_Success - The operation was successful.
+
+ This function can also return any error that GetFileSecurity can
+ return.
+
+--*/
+{
+ DWORD status=NO_ERROR;
+
+ //
+ // Call SetFileSecurity
+ //
+ if (!SetFileSecurityW (
+ FileName,
+ SecurityInfo,
+ pSecurityDescriptor)) {
+
+ status = GetLastError();
+ return(status);
+ }
+ return(NO_ERROR);
+}
+
diff --git a/private/net/svcdlls/srvsvc/lib/makefile b/private/net/svcdlls/srvsvc/lib/makefile
new file mode 100644
index 000000000..f0db8e4a7
--- /dev/null
+++ b/private/net/svcdlls/srvsvc/lib/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS LINE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/net/svcdlls/srvsvc/lib/sources b/private/net/svcdlls/srvsvc/lib/sources
new file mode 100644
index 000000000..d894e1513
--- /dev/null
+++ b/private/net/svcdlls/srvsvc/lib/sources
@@ -0,0 +1,38 @@
+!IF 0
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+
+Author:
+
+ Dan Lafferty (danl) 13-Jan-1992
+
+
+Revision History:
+
+!ENDIF
+
+MAJORCOMP = srvsvc
+MINORCOMP = srvcomn
+
+
+NTPROFILEINPUT=YES
+
+TARGETNAME=srvcomn
+TARGETPATH=obj
+TARGETTYPE=LIBRARY
+
+INCLUDES=.;..;..\..\..\inc;..\..\..\..\inc;
+
+SOURCES= adtcomn.c
diff --git a/private/net/svcdlls/srvsvc/makefil0 b/private/net/svcdlls/srvsvc/makefil0
new file mode 100644
index 000000000..a5182db48
--- /dev/null
+++ b/private/net/svcdlls/srvsvc/makefil0
@@ -0,0 +1,60 @@
+#
+# this is the midl compile phase of the build process.
+#
+# The following is where you put the name of your .idl file without
+# the .idl extension:
+#
+
+!INCLUDE $(NTMAKEENV)\makefile.plt
+
+IDL_NAME = srvsvc
+
+!IFNDEF DISABLE_NET_UNICODE
+UNICODE=1
+NET_C_DEFINES=-DUNICODE
+!ENDIF
+
+SDKINC = $(BASEDIR)\public\sdk\inc
+SDKCRTINC = $(BASEDIR)\public\sdk\inc\crt
+INCS = -I$(SDKINC) -I$(SDKCRTINC) -I..\..\inc -I$(BASEDIR)\private\inc
+
+CPP = -cpp_cmd "$(MIDL_CPP)" $(MIDL_FLAGS) $(C_DEFINES) $(NET_C_DEFINES)
+
+CLIENT_TARGETS = client\$(IDL_NAME)_c.c \
+ .\$(IDL_NAME).h
+
+SERVER_TARGETS = server\$(IDL_NAME)_s.c
+
+EXTRN_DEPENDS = $(SDKINC)\lmcons.h \
+ $(SDKINC)\windef.h \
+ $(SDKINC)\lmchdev.h \
+ $(SDKINC)\lmshare.h \
+ $(SDKINC)\lmserver.h \
+ $(SDKINC)\lmstats.h \
+ $(IDL_NAME).acf
+
+#
+# Define Products and Dependencies
+#
+
+all: $(CLIENT_TARGETS) $(SERVER_TARGETS) $(EXTRN_DEPENDS)
+!IF "$(BUILDMSG)" != ""
+ @ech ; $(BUILDMSG) ;
+!ENDIF
+
+clean: delete_source all
+
+delete_source:
+ erase $(CLIENT_TARGETS) $(SERVER_TARGETS)
+
+#
+# MIDL COMPILE
+#
+
+$(CLIENT_TARGETS) : $(IDL_NAME).idl $(EXTRN_DEPENDS)
+ midl -Oi -server none -oldnames -error allocation -error ref -ms_ext -c_ext $(CPP) $(IDL_NAME).idl $(INCS)
+ IF EXIST $(IDL_NAME)_c.c copy $(IDL_NAME)_c.c .\client & del $(IDL_NAME)_c.c
+
+$(SERVER_TARGETS) : $(IDL_NAME).idl $(EXTRN_DEPENDS)
+ midl -client none -oldnames -error stub_data -error allocation -error ref -ms_ext -c_ext $(CPP) $(IDL_NAME).idl $(INCS)
+ IF EXIST $(IDL_NAME)_s.c copy $(IDL_NAME)_s.c .\server & del $(IDL_NAME)_s.c
diff --git a/private/net/svcdlls/srvsvc/server/adtsrv.c b/private/net/svcdlls/srvsvc/server/adtsrv.c
new file mode 100644
index 000000000..e15205abd
--- /dev/null
+++ b/private/net/svcdlls/srvsvc/server/adtsrv.c
@@ -0,0 +1,450 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ adtsrv.c
+
+Abstract:
+
+ AdminTools Server functions.
+
+ This file contains the remote interface for NetpGetFileSecurity and
+ NetpSetFileSecurity API.
+
+Author:
+
+ Dan Lafferty (danl) 25-Mar-1993
+
+Environment:
+
+ User Mode - Win32
+
+
+Revision History:
+
+ 27-Oct-1994 IsaacHe
+ Make sure the share permissions allow these operations.
+ 05-Sep-1994 Danl
+ Free memory and NULL the pointer to the SecurityDescriptor if
+ a failure occurs. Also free the buffer returned from
+ NetShareGetInfo.
+ 25-Mar-1993 danl
+ Created
+
+--*/
+
+//
+// Includes
+//
+
+#include "srvsvcp.h"
+#include "ssdata.h"
+
+#include <lmerr.h>
+#include <adtcomn.h>
+#include <tstr.h>
+
+//
+// GLOBALS
+//
+ DWORD AdtsvcDebugLevel = DEBUG_ERROR;
+
+//
+// LOCAL FUNCTIONS
+//
+
+NET_API_STATUS
+AdtCheckShareAccessAndGetFullPath(
+ LPWSTR pShare,
+ LPWSTR pFileName,
+ LPWSTR *pPath,
+ ACCESS_MASK DesiredAccess
+ );
+
+NET_API_STATUS NET_API_FUNCTION
+NetrpGetFileSecurity (
+ IN LPWSTR ServerName,
+ IN LPWSTR ShareName,
+ IN LPWSTR FileName,
+ IN SECURITY_INFORMATION RequestedInfo,
+ OUT PADT_SECURITY_DESCRIPTOR *pSecurityDescriptor
+ )
+
+/*++
+
+Routine Description:
+
+ This function returns to the caller a copy of the security descriptor
+ protecting a file or directory. It calls GetFileSecurity. The
+ Security Descriptor is always returned in the self-relative format.
+
+ This function is called only when accessing remote files. In this case,
+ the filename is broken into ServerName, ShareName, and FileName components.
+ The ServerName gets the request to this routine. The ShareName must be
+ expanded to find the local path associated with it. This is combined
+ with the FileName to create a fully qualified pathname that is local
+ to this machine.
+
+Arguments:
+
+ ServerName - A pointer to a string containing the name of the remote
+ server on which the function is to execute. A NULL pointer or
+ string specifies the local machine.
+
+ ShareName - A pointer to a string that identifies the share name
+ on which the file is found.
+
+ FileName - A pointer to the name fo the file or directory whose
+ security is being retrieved.
+
+ RequestedInfo - The type of security information being requested.
+
+ pSecurityDescriptor - A pointer to a pointer to a structure which
+ contains the buffer pointer for the security descriptor and
+ a length field for the security descriptor.
+
+Return Value:
+
+ NERR_Success - The operation was successful.
+
+ ERROR_NOT_ENOUGH_MEMORY - Unable to allocate memory for the security
+ descriptor.
+
+ Other - This function can also return any error that
+ GetFileSecurity,
+ RpcImpersonateClient, or
+ ShareEnumCommon
+ can return.
+
+
+--*/
+{
+ NET_API_STATUS status;
+ PSECURITY_DESCRIPTOR pNewSecurityDescriptor;
+ DWORD bufSize;
+ LPWSTR FullPath=NULL;
+ ACCESS_MASK DesiredAccess = 0;
+
+ *pSecurityDescriptor = MIDL_user_allocate(sizeof(ADT_SECURITY_DESCRIPTOR));
+
+ if (*pSecurityDescriptor == NULL) {
+ ADT_LOG0( ERROR, "NetrpGetFileSecurity:MIDL_user_alloc failed\n" );
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ //
+ // Figure out accesses needed to perform the indicated operation(s).
+ // This code is taken from ntos\se\semethod.c
+ //
+ if ((RequestedInfo & OWNER_SECURITY_INFORMATION) ||
+ (RequestedInfo & GROUP_SECURITY_INFORMATION) ||
+ (RequestedInfo & DACL_SECURITY_INFORMATION)) {
+ DesiredAccess |= READ_CONTROL;
+ }
+
+ if ((RequestedInfo & SACL_SECURITY_INFORMATION)) {
+ DesiredAccess |= ACCESS_SYSTEM_SECURITY;
+ }
+
+ //
+ // Check share perms and create a full path string by getting
+ // the path for the share name, and adding the FileName string to it.
+ //
+ status = AdtCheckShareAccessAndGetFullPath(
+ ShareName,
+ FileName,
+ &FullPath,
+ DesiredAccess
+ );
+
+ if( status == NO_ERROR ) {
+ if( (status = RpcImpersonateClient(NULL)) == NO_ERROR ) {
+ //
+ // Get the File Security information
+ //
+ status = PrivateGetFileSecurity(
+ FullPath,
+ RequestedInfo,
+ &pNewSecurityDescriptor,
+ &bufSize);
+
+ if ( status == NO_ERROR ) {
+ (*pSecurityDescriptor)->Length = bufSize;
+ (*pSecurityDescriptor)->Buffer = pNewSecurityDescriptor;
+ }
+
+ (VOID)RpcRevertToSelf();
+ }
+ MIDL_user_free( FullPath );
+ }
+
+ if ( status != NO_ERROR ) {
+ MIDL_user_free(*pSecurityDescriptor);
+ *pSecurityDescriptor = NULL;
+ }
+
+ return status;
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetrpSetFileSecurity (
+ IN LPWSTR ServerName OPTIONAL,
+ IN LPWSTR ShareName,
+ IN LPWSTR FileName,
+ IN SECURITY_INFORMATION SecurityInfo,
+ IN PADT_SECURITY_DESCRIPTOR pSecurityDescriptor
+ )
+
+/*++
+
+Routine Description:
+
+ This function can be used to set the security of a file or directory.
+ It calls SetFileSecurity().
+
+Arguments:
+
+ ServerName - A pointer to a string containing the name of the remote
+ server on which the function is to execute. A NULL pointer or
+ string specifies the local machine.
+
+ ShareName - A pointer to a string that identifies the share name
+ on which the file or directory is found.
+
+ FileName - A pointer to the name of the file or directory whose
+ security is being changed.
+
+ SecurityInfo - Information describing the contents
+ of the Security Descriptor.
+
+ pSecurityDescriptor - A pointer to a structure that contains a
+ self-relative security descriptor and a length.
+
+Return Value:
+
+ NERR_Success - The operation was successful.
+
+ Other - This function can also return any error that
+ SetFileSecurity,
+ RpcImpersonateClient, or
+ ShareEnumCommon
+ can return.
+
+--*/
+{
+ NET_API_STATUS status;
+ LPWSTR FullPath=NULL;
+ ACCESS_MASK DesiredAccess = 0;
+
+ UNREFERENCED_PARAMETER(ServerName);
+
+ //
+ // Figure out accesses needed to perform the indicated operation(s).
+ // This code is taken from ntos\se\semethod.c
+ //
+ if ((SecurityInfo & OWNER_SECURITY_INFORMATION) ||
+ (SecurityInfo & GROUP_SECURITY_INFORMATION) ) {
+ DesiredAccess |= WRITE_OWNER;
+ }
+
+ if (SecurityInfo & DACL_SECURITY_INFORMATION) {
+ DesiredAccess |= WRITE_DAC;
+ }
+
+ if (SecurityInfo & SACL_SECURITY_INFORMATION) {
+ DesiredAccess |= ACCESS_SYSTEM_SECURITY;
+ }
+
+ //
+ // Check perms and create a full path string by getting the path
+ // for the share name, and adding the FileName string to it.
+ //
+ status = AdtCheckShareAccessAndGetFullPath(
+ ShareName,
+ FileName,
+ &FullPath,
+ DesiredAccess
+ );
+
+ if ( status == NO_ERROR ) {
+ if( (status = RpcImpersonateClient(NULL)) == NO_ERROR ) {
+ //
+ // Call SetFileSecurity
+ //
+ status = PrivateSetFileSecurity(
+ FullPath,
+ SecurityInfo,
+ pSecurityDescriptor->Buffer);
+
+ (VOID)RpcRevertToSelf();
+ }
+ MIDL_user_free(FullPath);
+ }
+
+ return(status);
+}
+
+NET_API_STATUS
+AdtCheckShareAccessAndGetFullPath(
+ LPWSTR pShare,
+ LPWSTR pFileName,
+ LPWSTR *pPath,
+ ACCESS_MASK DesiredAccess
+ )
+
+/*++
+
+Routine Description:
+
+ This function ensures the DesiredAccess is allowed and finds the
+ path associated with the share name, combines this with the
+ file name, and creates a fully qualified path name.
+
+ NOTE: This function allocates storage for the pPath string.
+
+Arguments:
+
+ pShare - This is a pointer to the share name string.
+
+ pFileName - This is a pointer to the file name (or path) string.
+
+ pPath - This is a pointer to a location where the pointer to the
+ complete file path string can be stored. This pointer needs to
+ be free'd with MIDL_user_free when the caller is finished with it.
+
+ DesiredAccess - what we'd like to do through the share
+
+Return Value:
+
+ NO_ERROR - if The operation was completely successful.
+
+ Other - Errors returned from ShareEnumCommon, and MIDL_user_allocate may be
+ returned from this routine.
+
+Comments:
+ The share access checking is complicated by the fact that the share ACL has
+ had the owner and group SIDs stripped out. We need to put them back
+ in, or the SsCheckAccess() call will fail.
+
+--*/
+{
+ NET_API_STATUS status;
+ PSHARE_INFO_502 pshi502 = NULL;
+ DWORD bufSize;
+ DWORD fileNameSize;
+ LPWSTR pLastChar;
+ DWORD entriesRead;
+ DWORD totalEntries;
+ PSECURITY_DESCRIPTOR NewDescriptor = NULL;
+ SECURITY_DESCRIPTOR ModificationDescriptor;
+ GENERIC_MAPPING Mapping;
+ SRVSVC_SECURITY_OBJECT SecurityObject;
+ HANDLE token;
+
+ status = ShareEnumCommon(
+ 502,
+ (LPBYTE *)&pshi502,
+ (DWORD)-1,
+ &entriesRead,
+ &totalEntries,
+ NULL,
+ pShare
+ );
+
+ if( status != NO_ERROR )
+ return status;
+
+ if( entriesRead == 0 || pshi502 == NULL ) {
+ status = NERR_NetNameNotFound;
+
+ } else if( pshi502->shi502_path == NULL ) {
+ status = ERROR_BAD_DEV_TYPE;
+
+ } else if( pshi502->shi502_security_descriptor != NULL ) {
+
+ status = RtlCopySecurityDescriptor( pshi502->shi502_security_descriptor, &NewDescriptor );
+ if( status != STATUS_SUCCESS )
+ goto getout;
+
+ RtlCreateSecurityDescriptor( &ModificationDescriptor, SECURITY_DESCRIPTOR_REVISION );
+
+ RtlSetOwnerSecurityDescriptor( &ModificationDescriptor, SsLmsvcsGlobalData->LocalSystemSid, FALSE );
+
+ RtlSetGroupSecurityDescriptor( &ModificationDescriptor, SsLmsvcsGlobalData->LocalSystemSid, FALSE );
+
+ Mapping.GenericRead = FILE_GENERIC_READ;
+ Mapping.GenericWrite = FILE_GENERIC_WRITE;
+ Mapping.GenericExecute = FILE_GENERIC_EXECUTE;
+ Mapping.GenericAll = FILE_ALL_ACCESS;
+
+ if( ImpersonateSelf( SecurityImpersonation ) == FALSE ) {
+ status = GetLastError();
+ goto getout;
+ }
+
+ status = NtOpenThreadToken( NtCurrentThread(), TOKEN_QUERY, TRUE, &token );
+
+ RevertToSelf();
+
+ if( status != STATUS_SUCCESS )
+ goto getout;
+
+ status = RtlSetSecurityObject (
+ GROUP_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION,
+ &ModificationDescriptor,
+ &NewDescriptor,
+ &Mapping,
+ token
+ );
+
+ NtClose( token );
+
+ if( status == STATUS_SUCCESS ) {
+
+ SecurityObject.ObjectName = pShare;
+ SecurityObject.Mapping = &Mapping;
+ SecurityObject.SecurityDescriptor = NewDescriptor;
+
+ //
+ // SsCheckAccess does an RpcImpersonateClient()...
+ //
+ status = SsCheckAccess( &SecurityObject, DesiredAccess );
+ }
+ }
+
+ if( status == STATUS_SUCCESS ) {
+
+ //
+ // If the last character is a '\', then we must remove it.
+ //
+ pLastChar = pshi502->shi502_path + wcslen(pshi502->shi502_path);
+ pLastChar--;
+ if (*pLastChar == L'\\') {
+ *pLastChar = L'\0';
+ }
+
+ bufSize = STRSIZE(pshi502->shi502_path);
+ fileNameSize = STRSIZE(pFileName);
+
+ *pPath = MIDL_user_allocate( bufSize + fileNameSize );
+
+ if (*pPath != NULL) {
+ wcscpy (*pPath, pshi502->shi502_path);
+ wcscat (*pPath, pFileName);
+ } else {
+ status = ERROR_NOT_ENOUGH_MEMORY;
+ }
+ }
+
+getout:
+ if( NewDescriptor != NULL )
+ RtlDeleteSecurityObject( &NewDescriptor );
+
+ if( pshi502 != NULL )
+ MIDL_user_free( pshi502 );
+
+ return status;
+}
diff --git a/private/net/svcdlls/srvsvc/server/canon.c b/private/net/svcdlls/srvsvc/server/canon.c
new file mode 100644
index 000000000..b3dd64e8a
--- /dev/null
+++ b/private/net/svcdlls/srvsvc/server/canon.c
@@ -0,0 +1,259 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ canon.c
+
+Abstract:
+
+ Stubs for server side of internal canonicalization RPC. These routines
+ just call the local versions of the routines which live in NetLib.LIB
+
+ Moved from cansvc
+
+Author:
+
+ Richard Firth (rfirth) 20-May-1991
+
+Revision History:
+
+
+--*/
+
+#include <windows.h>
+#include <lmcons.h>
+#include <netcan.h>
+
+NET_API_STATUS
+NetprPathType(
+ IN LPTSTR ServerName,
+ IN LPTSTR PathName,
+ IN LPDWORD PathType,
+ IN DWORD Flags
+ )
+
+/*++
+
+Routine Description:
+
+ Stub function for NetpPathType - calls local version
+
+Arguments:
+
+ ServerName - identifies this server
+ PathName - path name to check
+ PathType - assumed type of PathName
+ Flags - controlling flags for NetpPathType
+
+Return Value:
+
+ NET_API_STATUS
+ Success = 0
+ Failure = (return code from NetpPathType)
+
+--*/
+
+{
+ UNREFERENCED_PARAMETER(ServerName);
+
+ return NetpwPathType(PathName, PathType, Flags);
+}
+
+NET_API_STATUS
+NetprPathCanonicalize(
+ IN LPTSTR ServerName,
+ IN LPTSTR PathName,
+ OUT LPTSTR Outbuf,
+ IN DWORD OutbufLen,
+ IN LPTSTR Prefix,
+ OUT LPDWORD PathType,
+ IN DWORD Flags
+ )
+
+/*++
+
+Routine Description:
+
+ Stub function for NetpPathCanonicalize - calls local version
+
+Arguments:
+
+ ServerName - identifies this server
+ PathName - path name to canonicalize
+ Outbuf - where to place canonicalized path
+ OutbufLen - size of Outbuf
+ Prefix - (historical) prefix for path
+ PathType - type of PathName
+ Flags - controlling flags for NetpPathCanonicalize
+
+Return Value:
+
+ NET_API_STATUS
+ Success = 0
+ Failure = (return code from NetpPathCanonicalize)
+
+--*/
+
+{
+ UNREFERENCED_PARAMETER(ServerName);
+
+ return NetpwPathCanonicalize(PathName,
+ Outbuf,
+ OutbufLen,
+ Prefix,
+ PathType,
+ Flags
+ );
+}
+
+LONG
+NetprPathCompare(
+ IN LPTSTR ServerName,
+ IN LPTSTR PathName1,
+ IN LPTSTR PathName2,
+ IN DWORD PathType,
+ IN DWORD Flags
+ )
+
+/*++
+
+Routine Description:
+
+ Stub function for NetpPathCompare - calls local version
+
+Arguments:
+
+ ServerName - identifies this server
+ PathName1 - path name to compare
+ PathName2 - path name to compare
+ PathType - type of PathName1, PathName2
+ Flags - controlling flags for NetpPathCompare
+
+Return Value:
+
+ LONG
+ -1 - Name1 < Name2
+ 0 - Name1 = Name2
+ +1 - Name1 > Name2
+
+--*/
+
+{
+ UNREFERENCED_PARAMETER(ServerName);
+
+ return NetpwPathCompare(PathName1, PathName2, PathType, Flags);
+}
+
+NET_API_STATUS
+NetprNameValidate(
+ IN LPTSTR ServerName,
+ IN LPTSTR Name,
+ IN DWORD NameType,
+ IN DWORD Flags
+ )
+
+/*++
+
+Routine Description:
+
+ Stub function for NetpNameValidate - calls local version
+
+Arguments:
+
+ ServerName - identifies this server
+ Name - to validate
+ NameType - type of Name
+ Flags - controlling flags for NetpNameValidate
+
+Return Value:
+
+ NET_API_STATUS
+ Success = 0
+ Failure = (return code from NetpNameValidate)
+
+--*/
+
+{
+ UNREFERENCED_PARAMETER(ServerName);
+
+ return NetpwNameValidate(Name, NameType, Flags);
+}
+
+NET_API_STATUS
+NetprNameCanonicalize(
+ IN LPTSTR ServerName,
+ IN LPTSTR Name,
+ OUT LPTSTR Outbuf,
+ IN DWORD OutbufLen,
+ IN DWORD NameType,
+ IN DWORD Flags
+ )
+
+/*++
+
+Routine Description:
+
+ Stub function for NetpNameCanonicalize - calls local version
+
+Arguments:
+
+ ServerName - identifies this server
+ Name - to canonicalize
+ Outbuf - where to place canonicalized name
+ OutbufLen - size of Outbuf
+ NameType - type of Name
+ Flags - controlling flags for NetpNameCanonicalize
+
+Return Value:
+
+ NET_API_STATUS
+ Success = 0
+ Failure = (return code from NetpNameCanonicalize)
+
+--*/
+
+{
+ UNREFERENCED_PARAMETER(ServerName);
+
+ return NetpwNameCanonicalize(Name, Outbuf, OutbufLen, NameType, Flags);
+}
+
+LONG
+NetprNameCompare(
+ IN LPTSTR ServerName,
+ IN LPTSTR Name1,
+ IN LPTSTR Name2,
+ IN DWORD NameType,
+ IN DWORD Flags
+ )
+
+/*++
+
+Routine Description:
+
+ Stub function for NetpNameCompare - calls local version
+
+Arguments:
+
+ ServerName - identifies this server
+ Name1 - name to compare
+ Name2 - "
+ NameType - type of names
+ Flags - controlling flags for NetpNameCompare
+
+Return Value:
+
+ LONG
+ -1 - Name1 < Name2
+ 0 - Name1 = Name2
+ +1 - Name1 > Name2
+
+--*/
+
+{
+ UNREFERENCED_PARAMETER(ServerName);
+
+ return NetpwNameCompare(Name1, Name2, NameType, Flags);
+}
diff --git a/private/net/svcdlls/srvsvc/server/cdev.c b/private/net/svcdlls/srvsvc/server/cdev.c
new file mode 100644
index 000000000..adacfb2c1
--- /dev/null
+++ b/private/net/svcdlls/srvsvc/server/cdev.c
@@ -0,0 +1,341 @@
+/*++
+
+Copyright (c) 1991-1992 Microsoft Corporation
+
+Module Name:
+
+ CDev.c
+
+Abstract:
+
+ This module contains support for the Character Device catagory of
+ APIs for the NT server service.
+
+Author:
+
+ David Treadwell (davidtr) 20-Dec-1991
+
+Revision History:
+
+--*/
+
+#include "srvsvcp.h"
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetrCharDevControl (
+ IN LPTSTR ServerName,
+ IN LPTSTR DeviceName,
+ IN DWORD OpCode
+ )
+
+/*++
+
+Routine Description:
+
+ This routine communicates with the server FSP to implement the
+ NetCharDevControl function.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NET_API_STATUS - NO_ERROR or reason for failure.
+
+--*/
+
+{
+#ifdef SRV_COMM_DEVICES
+ NET_API_STATUS error;
+ PSERVER_REQUEST_PACKET srp;
+
+ ServerName;
+
+ //
+ // Make sure that the caller has the access necessary for this
+ // operation.
+ //
+
+ error = SsCheckAccess(
+ &SsCharDevSecurityObject,
+ SRVSVC_CHARDEV_INFO_SET
+ );
+
+ if ( error != NO_ERROR ) {
+ return ERROR_ACCESS_DENIED;
+ }
+
+ //
+ // Make sure that the opcode is legitimate. Only CHARDEV_CLOSE, used
+ // to close the current open of the device, is possible.
+ //
+
+ if ( OpCode != CHARDEV_CLOSE ) {
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ //
+ // Set up the request packet.
+ //
+
+ srp = SsAllocateSrp( );
+ if ( srp == NULL ) {
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+#ifdef UNICODE
+ RtlInitUnicodeString( &srp->Name1, DeviceName );
+#else
+ {
+ OEM_STRING ansiString;
+ NTSTATUS status;
+ NetpInitOemString( &ansiString, DeviceName );
+ status = RtlOemStringToUnicodeString( &srp->Name1, &ansiString, TRUE );
+ SS_ASSERT( NT_SUCCESS(status) );
+ }
+#endif
+
+ //
+ // Simply send the request on to the server.
+ //
+
+ error = SsServerFsControl( NULL, FSCTL_SRV_NET_CHARDEV_CONTROL, srp, NULL, 0 );
+
+#ifndef UNICODE
+ RtlFreeUnicodeString( &srp->Name1 );
+#endif
+
+ SsFreeSrp( srp );
+
+ return error;
+#else
+ ServerName, DeviceName, OpCode;
+ return ERROR_NOT_SUPPORTED;
+#endif
+
+} // NetrCharDevControl
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetrCharDevEnum (
+ SRVSVC_HANDLE ServerName,
+ LPCHARDEV_ENUM_STRUCT InfoStruct,
+ DWORD PreferedMaximumLength,
+ LPDWORD TotalEntries,
+ LPDWORD ResumeHandle
+ )
+
+/*++
+
+Routine Description:
+
+ This routine communicates with the server FSD to implement the
+ NetCharDevEnum function.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NET_API_STATUS - NO_ERROR or reason for failure.
+
+--*/
+
+{
+#ifdef SRV_COMM_DEVICES
+ NET_API_STATUS error;
+ PSERVER_REQUEST_PACKET srp;
+
+ ServerName;
+
+ //
+ // Make sure that the caller has the access necessary for this
+ // operation.
+ //
+
+ error = SsCheckAccess(
+ &SsCharDevSecurityObject,
+ SRVSVC_CHARDEV_ADMIN_INFO_GET
+ );
+
+ if ( error != NO_ERROR ) {
+ return ERROR_ACCESS_DENIED;
+ }
+
+ //
+ // Make sure that the level is valid.
+ //
+
+ if ( InfoStruct->Level != 0 && InfoStruct->Level != 1 ) {
+ return ERROR_INVALID_LEVEL;
+ }
+
+ //
+ // Set up the input parameters in the request buffer.
+ //
+
+ srp = SsAllocateSrp( );
+ if ( srp == NULL ) {
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+ srp->Level = InfoStruct->Level;
+
+ if ( ARGUMENT_PRESENT( ResumeHandle ) ) {
+ srp->Parameters.Get.ResumeHandle = *ResumeHandle;
+ } else {
+ srp->Parameters.Get.ResumeHandle = 0;
+ }
+
+ //
+ // Get the data from the server. This routine will allocate the
+ // return buffer and handle the case where PreferredMaximumLength ==
+ // -1.
+ //
+
+ error = SsServerFsControlGetInfo(
+ FSCTL_SRV_NET_CHARDEV_ENUM,
+ srp,
+ (PVOID *)&InfoStruct->CharDevInfo.Level1->Buffer,
+ PreferedMaximumLength
+ );
+
+ //
+ // Set up return information.
+ //
+
+ InfoStruct->CharDevInfo.Level1->EntriesRead =
+ srp->Parameters.Get.EntriesRead;
+
+ if ( ARGUMENT_PRESENT( TotalEntries ) ) {
+ *TotalEntries = srp->Parameters.Get.TotalEntries;
+ }
+
+ if ( srp->Parameters.Get.EntriesRead > 0 &&
+ ARGUMENT_PRESENT( ResumeHandle ) ) {
+
+ *ResumeHandle = srp->Parameters.Get.ResumeHandle;
+ }
+
+ SsFreeSrp( srp );
+
+ return error;
+#else
+ ServerName, InfoStruct, PreferedMaximumLength, TotalEntries, ResumeHandle;
+ return ERROR_NOT_SUPPORTED;
+#endif
+
+} // NetrCharDevEnum
+
+
+NET_API_STATUS
+NetrCharDevGetInfo (
+ IN LPTSTR ServerName,
+ IN LPTSTR DeviceName,
+ IN DWORD Level,
+ OUT LPCHARDEV_INFO CharDevInfo
+ )
+
+/*++
+
+Routine Description:
+
+ This routine communicates with the server FSD to implement the
+ NetCharDevGetInfo function.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NET_API_STATUS - NO_ERROR or reason for failure.
+
+--*/
+
+{
+#ifdef SRV_COMM_DEVICES
+ NET_API_STATUS error;
+ PSERVER_REQUEST_PACKET srp;
+
+ ServerName;
+
+ //
+ // Make sure that the caller has the access necessary for this
+ // operation.
+ //
+
+ error = SsCheckAccess(
+ &SsCharDevSecurityObject,
+ SRVSVC_CHARDEV_ADMIN_INFO_GET
+ );
+
+ if ( error != NO_ERROR ) {
+ return ERROR_ACCESS_DENIED;
+ }
+
+ //
+ // Make sure that the level is valid.
+ //
+
+ if ( Level != 0 && Level != 1 ) {
+ return ERROR_INVALID_LEVEL;
+ }
+
+ //
+ // Set up the input parameters in the request buffer.
+ //
+
+ srp = SsAllocateSrp( );
+ if ( srp == NULL ) {
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+ srp->Level = Level;
+ srp->Flags = SRP_RETURN_SINGLE_ENTRY;
+ srp->Parameters.Get.ResumeHandle = 0;
+
+#ifdef UNICODE
+ RtlInitUnicodeString( &srp->Name1, DeviceName );
+#else
+ {
+ NTSTATUS status;
+ OEM_STRING ansiString;
+ RtlInitString( &ansiString, DeviceName );
+ status = RtlOemStringToUnicodeString( &srp->Name1, &ansiString, TRUE );
+ SS_ASSERT( NT_SUCCESS(status) );
+ }
+#endif
+
+ //
+ // Get the data from the server. This routine will allocate the
+ // return buffer and handle the case where PreferredMaximumLength ==
+ // -1.
+ //
+
+ error = SsServerFsControlGetInfo(
+ FSCTL_SRV_NET_CHARDEV_ENUM,
+ srp,
+ (PVOID *)CharDevInfo,
+ -1
+ );
+
+ //
+ // Free resources and return.
+ //
+
+#ifndef UNICODE
+ RtlFreeUnicodeString( &srp->Name1 );
+#endif
+
+ SsFreeSrp( srp );
+
+ return error;
+#else
+ ServerName, DeviceName, Level, CharDevInfo;
+ return ERROR_NOT_SUPPORTED;
+#endif
+
+} // NetrCharDevGetInfo
+
diff --git a/private/net/svcdlls/srvsvc/server/cdevq.c b/private/net/svcdlls/srvsvc/server/cdevq.c
new file mode 100644
index 000000000..0c40d2d18
--- /dev/null
+++ b/private/net/svcdlls/srvsvc/server/cdevq.c
@@ -0,0 +1,215 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ CDevQ.c
+
+Abstract:
+
+ This module contains support for the Character Device Queue catagory
+ of APIs for the NT server service.
+
+Author:
+
+ David Treadwell (davidtr) 31-Dec-1991
+
+Revision History:
+
+--*/
+
+#include "srvsvcp.h"
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetrCharDevQEnum (
+ IN LPTSTR ServerName,
+ IN LPTSTR UserName,
+ IN OUT LPCHARDEVQ_ENUM_STRUCT InfoStruct,
+ IN DWORD PreferedMaximumLength,
+ OUT LPDWORD TotalEntries,
+ IN OUT LPDWORD ResumeHandle
+ )
+
+/*++
+
+Routine Description:
+
+ This routine communicates with the server FSD to implement the
+ NetCharDevEnum function.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NET_API_STATUS - NO_ERROR or reason for failure.
+
+--*/
+
+{
+#ifdef SRV_COMM_DEVICES
+ NET_API_STATUS error;
+ PSERVER_REQUEST_PACKET srp;
+
+ ServerName, UserName;
+
+ //
+ // Make sure that the caller has the access necessary for this
+ // operation.
+ //
+
+ error = SsCheckAccess(
+ &SsCharDevSecurityObject,
+ SRVSVC_CHARDEV_ADMIN_INFO_GET
+ );
+
+ if ( error != NO_ERROR ) {
+ return ERROR_ACCESS_DENIED;
+ }
+
+ //
+ // Make sure that the level is valid.
+ //
+
+ if ( InfoStruct->Level != 0 && InfoStruct->Level != 1 ) {
+ return ERROR_INVALID_LEVEL;
+ }
+
+ //
+ // Set up the input parameters in the request buffer.
+ //
+
+ srp = SsAllocateSrp( );
+ if ( srp == NULL ) {
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+ srp->Level = InfoStruct->Level;
+
+ if ( ARGUMENT_PRESENT( ResumeHandle ) ) {
+ srp->Parameters.Get.ResumeHandle = *ResumeHandle;
+ } else {
+ srp->Parameters.Get.ResumeHandle = 0;
+ }
+
+ //
+ // Get the data from the server. This routine will allocate the
+ // return buffer and handle the case where PreferredMaximumLength ==
+ // -1.
+ //
+
+ error = SsServerFsControlGetInfo(
+ FSCTL_SRV_NET_CHARDEVQ_ENUM,
+ srp,
+ (PVOID *)&InfoStruct->CharDevQInfo.Level1->Buffer,
+ PreferedMaximumLength
+ );
+
+ //
+ // Set up return information.
+ //
+
+ InfoStruct->CharDevQInfo.Level1->EntriesRead =
+ srp->Parameters.Get.EntriesRead;
+
+ if ( ARGUMENT_PRESENT( TotalEntries ) ) {
+ *TotalEntries = srp->Parameters.Get.TotalEntries;
+ }
+
+ if ( srp->Parameters.Get.EntriesRead > 0 &&
+ ARGUMENT_PRESENT( ResumeHandle ) ) {
+
+ *ResumeHandle = srp->Parameters.Get.ResumeHandle;
+ }
+
+ SsFreeSrp( srp );
+
+ return error;
+#else
+ ServerName;
+ UserName;
+ InfoStruct;
+ PreferedMaximumLength;
+ TotalEntries;
+ ResumeHandle;
+
+ return ERROR_NOT_SUPPORTED;
+#endif
+
+} // NetrCharDevQEnum
+
+
+/****************************************************************************/
+NET_API_STATUS
+NetrCharDevQGetInfo (
+ IN LPTSTR ServerName,
+ IN LPTSTR QueueName,
+ IN LPTSTR UserName,
+ IN DWORD Level,
+ OUT LPCHARDEVQ_INFO CharDevQInfo
+ )
+
+{
+ ServerName;
+ QueueName;
+ UserName;
+ Level;
+ CharDevQInfo;
+
+ return ERROR_NOT_SUPPORTED;
+}
+
+
+/****************************************************************************/
+NET_API_STATUS
+NetrCharDevQSetInfo (
+ IN LPTSTR ServerName,
+ IN LPTSTR QueueName,
+ IN DWORD Level,
+ IN LPCHARDEVQ_INFO CharDevQInfo,
+ OUT LPDWORD ParmErr
+ )
+{
+ ServerName;
+ QueueName;
+ Level;
+ CharDevQInfo;
+ ParmErr;
+
+ return ERROR_NOT_SUPPORTED;
+}
+
+
+/****************************************************************************/
+NET_API_STATUS
+NetrCharDevQPurge (
+ IN LPTSTR ServerName,
+ IN LPTSTR QueueName
+ )
+
+{
+ ServerName;
+ QueueName;
+
+ return ERROR_NOT_SUPPORTED;
+}
+
+
+
+/****************************************************************************/
+NET_API_STATUS
+NetrCharDevQPurgeSelf (
+ IN LPTSTR ServerName,
+ IN LPTSTR QueueName,
+ IN LPTSTR ComputerName
+ )
+{
+ ServerName;
+ QueueName;
+ ComputerName;
+
+ return ERROR_NOT_SUPPORTED;
+}
+
diff --git a/private/net/svcdlls/srvsvc/server/cmdline.c b/private/net/svcdlls/srvsvc/server/cmdline.c
new file mode 100644
index 000000000..891a67292
--- /dev/null
+++ b/private/net/svcdlls/srvsvc/server/cmdline.c
@@ -0,0 +1,335 @@
+/*++
+
+Copyright (c) 1991-1992 Microsoft Corporation
+
+Module Name:
+
+ CmdLine.c
+
+Abstract:
+
+ This module contains support routines for processing server service
+ command-line arguments.
+
+Author:
+
+ David Treadwell (davidtr) 10-Mar-1991
+
+Revision History:
+
+--*/
+
+#include "srvsvcp.h"
+#include "ssdata.h"
+
+#include <netlibnt.h>
+#include <tstr.h>
+
+
+//
+// Forward declarations.
+//
+
+PFIELD_DESCRIPTOR
+FindSwitchMatch (
+ IN LPWCH Argument,
+ IN BOOLEAN Starting
+ );
+
+NET_API_STATUS
+SetField (
+ IN PFIELD_DESCRIPTOR SwitchDesc,
+ IN LPWCH Argument
+ );
+
+
+NET_API_STATUS
+SsParseCommandLine (
+ IN DWORD argc,
+ IN LPWSTR argv[],
+ IN BOOLEAN Starting
+ )
+
+/*++
+
+Routine Description:
+
+ This routine sets server parameters using a command line. It parses
+ the command line, changing one parameter at a time as it comes up.
+
+Arguments:
+
+ argc - the number of command-line arguments.
+
+ argv - an arrray of pointers to the arguments.
+
+ Starting - TRUE if the command line is from server startup, i.e.
+ net start server. This is needed because some fields may only
+ be set at startup.
+
+Return Value:
+
+ NET_API_STATUS - 0 or reason for failure.
+
+--*/
+
+{
+ NET_API_STATUS error;
+ DWORD i;
+ PFIELD_DESCRIPTOR switchDesc;
+ PSERVER_SERVICE_DATA saveSsData;
+
+ //
+ // Save the service data in case there is an invalid param and we have
+ // to back out.
+ //
+
+ saveSsData = MIDL_user_allocate( sizeof(SERVER_SERVICE_DATA) );
+ if ( saveSsData == NULL ) {
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ RtlCopyMemory( saveSsData, &SsData, sizeof(SERVER_SERVICE_DATA) );
+
+ //
+ // Loop through the command-line arguments, setting as we go.
+ //
+
+ for ( i = 0; i < argc; i++ ) {
+
+ LPWCH arg;
+
+ arg = argv[i];
+
+ //
+ // A hack to aid debugging.
+ //
+
+ if ( _wcsnicmp( L"/debug", arg, 6 ) == 0 ) {
+ continue;
+ }
+
+ //
+ // Try to match the switch against the legal switches.
+ //
+
+ switchDesc = FindSwitchMatch( arg, Starting );
+ if ( switchDesc == NULL ) {
+ error = ERROR_INVALID_PARAMETER;
+ goto err_exit;
+ }
+
+ //
+ // Set the value in the field.
+ //
+
+ error = SetField( switchDesc, arg );
+ if ( error != NO_ERROR ) {
+ IF_DEBUG(INITIALIZATION_ERRORS) {
+ SS_PRINT(( "SsParseCommandLine: SetField failed for switch "
+ "\"%ws\": %ld\n", arg, error ));
+ }
+ goto err_exit;
+ }
+ }
+
+ error = NO_ERROR;
+ goto normal_exit;
+
+err_exit:
+
+ //
+ // Restore the original server settings.
+ //
+
+ RtlCopyMemory( &SsData, saveSsData, sizeof(SERVER_SERVICE_DATA) );
+
+normal_exit:
+
+ MIDL_user_free( saveSsData );
+
+ return error;
+
+} // SsParseCommandLine
+
+
+PFIELD_DESCRIPTOR
+FindSwitchMatch (
+ IN LPWCH Argument,
+ IN BOOLEAN Starting
+ )
+
+/*++
+
+Routine Description:
+
+ This routine tries to match a given switch against the possible
+ switch values.
+
+Arguments:
+
+ Argument - a pointer to the text argument.
+
+ Starting - TRUE if the command line is from server startup, i.e.
+ net start server. This is needed because some fields may only
+ be set at startup.
+
+Return Value:
+
+ A pointer to a FIELD_DESCRIPTOR field from SsServerInfoFields[], or NULL if
+ no valid match could be found.
+
+--*/
+
+{
+ SHORT i;
+ PFIELD_DESCRIPTOR foundSwitch = NULL;
+ ULONG switchLength;
+ LPWCH s;
+
+ //
+ // Ignore the leading /.
+ //
+
+ if ( *Argument != '/' ) {
+ SS_PRINT(( "Invalid switch: %ws\n", Argument ));
+ return NULL;
+ }
+
+ Argument++;
+
+ //
+ // Find out how long the passed-in switch is.
+ //
+
+ for ( s = Argument, switchLength = 0;
+ *s != ':' && *s != '\0';
+ s++, switchLength++ );
+
+ //
+ // Compare at most that many bytes. We allow a minimal matching--
+ // as long as the specified switch uniquely identifies a switch, then
+ // is is usable.
+ //
+
+ for ( i = 0; SsServerInfoFields[i].FieldName != NULL; i++ ) {
+
+ if ( _wcsnicmp( Argument, SsServerInfoFields[i].FieldName, switchLength ) == 0 ) {
+
+ if ( SsServerInfoFields[i].Settable == NOT_SETTABLE ||
+ ( !Starting && SsServerInfoFields[i].Settable == SET_ON_STARTUP ) ) {
+
+ SS_PRINT(( "Cannot set field %ws at this time.\n",
+ SsServerInfoFields[i].FieldName ));
+
+ return NULL;
+ }
+
+ if ( foundSwitch != NULL ) {
+ SS_PRINT(( "Ambiguous switch name: %ws (matches %ws and %ws)\n",
+ Argument-1, foundSwitch->FieldName,
+ SsServerInfoFields[i].FieldName ));
+ return NULL;
+ }
+
+ foundSwitch = &SsServerInfoFields[i];
+ }
+ }
+
+ if ( foundSwitch == NULL ) {
+ SS_PRINT(( "Unknown argument: %ws\n", Argument-1 ));
+ }
+
+ return foundSwitch;
+
+} // FindSwitchMatch
+
+
+NET_API_STATUS
+SetField (
+ IN PFIELD_DESCRIPTOR Field,
+ IN LPWCH Argument
+ )
+
+/*++
+
+Routine Description:
+
+ This routine sets the value of a server info parameter.
+
+Arguments:
+
+ Field - a pointer to the appropriate FIELD_DESCRIPTOR field
+ from SsServerInfoFields[].
+
+ Argument - a pointer to the text argument. It should be of the form
+ "/switch:value".
+
+Return Value:
+
+ NET_API_STATUS - NO_ERROR or reason for failure.
+
+--*/
+
+{
+ LPWCH valueStart;
+ DWORD value;
+
+ //
+ // Find out where the ':' is in the argument.
+ //
+
+ valueStart = wcschr( Argument, L':' );
+
+ if ( valueStart == NULL && Field->FieldType != BOOLEAN_FIELD ) {
+ SS_PRINT(( "Invalid argument: %s\n", Argument ));
+ }
+
+ switch ( Field->FieldType ) {
+
+ case BOOLEAN_FIELD:
+
+ //
+ // If the first character of the value is Y or there is no
+ // value specified, set the field to TRUE, otherwise set it
+ // to FALSE.
+ //
+
+ if ( valueStart == NULL || *(valueStart+1) == L'y' ||
+ *(valueStart+1) == L'Y' ) {
+ value = TRUE;
+ } else if ( *(valueStart+1) == L'n' || *(valueStart+1) == L'N' ) {
+ value = FALSE;
+ } else {
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ break;
+
+ case DWORD_FIELD:
+ {
+ NTSTATUS status;
+ UNICODE_STRING unicodeString;
+
+ RtlInitUnicodeString( &unicodeString, valueStart + 1 );
+ status = RtlUnicodeStringToInteger( &unicodeString, 0, &value );
+ if ( !NT_SUCCESS(status) ) {
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ break;
+ }
+ case LPSTR_FIELD:
+
+ value = (DWORD)( valueStart + 1 );
+ break;
+ }
+
+ //
+ // Call SsSetField to actually set the field.
+ //
+
+ return SsSetField( Field, &value, TRUE, NULL );
+
+} // SetField
+
diff --git a/private/net/svcdlls/srvsvc/server/conn.c b/private/net/svcdlls/srvsvc/server/conn.c
new file mode 100644
index 000000000..de1e68110
--- /dev/null
+++ b/private/net/svcdlls/srvsvc/server/conn.c
@@ -0,0 +1,152 @@
+/*++
+
+Copyright (c) 1991-1992 Microsoft Corporation
+
+Module Name:
+
+ Conn.c
+
+Abstract:
+
+ This module contains support for the Connection catagory of APIs for
+ the NT server service.
+
+Author:
+
+ David Treadwell (davidtr) 23-Feb-1991
+
+Revision History:
+
+--*/
+
+#include "srvsvcp.h"
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetrConnectionEnum (
+ IN LPTSTR ServerName,
+ IN LPTSTR Qualifier,
+ IN LPCONNECT_ENUM_STRUCT InfoStruct,
+ IN DWORD PreferredMaximumLength,
+ OUT LPDWORD TotalEntries,
+ IN OUT LPDWORD ResumeHandle OPTIONAL
+ )
+
+/*++
+
+Routine Description:
+
+ This routine communicates with the server FSD to implement the
+ NetConnectionEnum function.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NET_API_STATUS - NO_ERROR or reason for failure.
+
+--*/
+
+{
+ NET_API_STATUS error;
+ PSERVER_REQUEST_PACKET srp;
+
+ ServerName;
+
+ //
+ // Make sure that the level is valid. Since it is an unsigned
+ // value, it can never be less than 0.
+ //
+
+ if ( InfoStruct->Level > 1 ) {
+ return ERROR_INVALID_LEVEL;
+ }
+
+ //
+ // The qualifier cannot be null or can it be a null string
+ //
+
+ if ( Qualifier == NULL || *Qualifier == L'\0' ) {
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ //
+ // Make sure that the caller is allowed to get connection
+ // information in the server.
+ //
+
+ error = SsCheckAccess(
+ &SsConnectionSecurityObject,
+ SRVSVC_CONNECTION_INFO_GET
+ );
+
+ if ( error != NO_ERROR ) {
+ return ERROR_ACCESS_DENIED;
+ }
+
+ //
+ // Set up the input parameters in the request buffer.
+ //
+
+ srp = SsAllocateSrp( );
+ if ( srp == NULL ) {
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+ srp->Level = InfoStruct->Level;
+
+#ifdef UNICODE
+ RtlInitUnicodeString( &srp->Name1, Qualifier );
+#else
+ {
+ OEM_STRING ansiString;
+ NTSTATUS status;
+ NetpInitOemString( &ansiString, Qualifier );
+ status = RtlOemStringToUnicodeString( &srp->Name1, &ansiString, TRUE );
+ SS_ASSERT( NT_SUCCESS(status) );
+ }
+#endif
+
+ if ( ARGUMENT_PRESENT( ResumeHandle ) ) {
+ srp->Parameters.Get.ResumeHandle = *ResumeHandle;
+ } else {
+ srp->Parameters.Get.ResumeHandle = 0;
+ }
+
+ //
+ // Get the data from the server. This routine will allocate the
+ // return buffer and handle the case where PreferredMaximumLength ==
+ // -1.
+ //
+
+ error = SsServerFsControlGetInfo(
+ FSCTL_SRV_NET_CONNECTION_ENUM,
+ srp,
+ (PVOID *)&InfoStruct->ConnectInfo.Level1->Buffer,
+ PreferredMaximumLength
+ );
+
+ //
+ // Set up return information.
+ //
+
+ InfoStruct->ConnectInfo.Level1->EntriesRead =
+ srp->Parameters.Get.EntriesRead;
+ *TotalEntries = srp->Parameters.Get.TotalEntries;
+
+ if ( srp->Parameters.Get.EntriesRead > 0 &&
+ ARGUMENT_PRESENT( ResumeHandle ) ) {
+ *ResumeHandle = srp->Parameters.Get.ResumeHandle;
+ }
+
+#ifndef UNICODE
+ RtlFreeUnicodeString( &srp->Name1 );
+#endif
+
+ SsFreeSrp( srp );
+
+ return error;
+
+} // NetrConnectionEnum
+
diff --git a/private/net/svcdlls/srvsvc/server/daytona/makefile b/private/net/svcdlls/srvsvc/server/daytona/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/net/svcdlls/srvsvc/server/daytona/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/net/svcdlls/srvsvc/server/daytona/sources b/private/net/svcdlls/srvsvc/server/daytona/sources
new file mode 100644
index 000000000..97a22b441
--- /dev/null
+++ b/private/net/svcdlls/srvsvc/server/daytona/sources
@@ -0,0 +1,89 @@
+!IF 0
+
+Copyright (c) 1989-1993 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+
+Author:
+
+ Steve Wood (stevewo) 12-Apr-1989
+
+
+Revision History:
+
+!ENDIF
+
+MAJORCOMP=net
+MINORCOMP=srvsvcsrv
+
+TARGETNAME=srvsvc
+TARGETPATH=$(BASEDIR)\public\sdk\lib
+TARGETTYPE=DYNLINK
+
+DLLDEF=obj\*\srvsvc.def
+
+TARGETLIBS= \
+ $(BASEDIR)\public\sdk\lib\*\kernel32.lib \
+ $(BASEDIR)\public\sdk\lib\*\advapi32.lib \
+ $(BASEDIR)\public\sdk\lib\*\netapi32.lib \
+ $(BASEDIR)\public\sdk\lib\*\ntlsapi.lib \
+ $(BASEDIR)\public\sdk\lib\*\rpcutil.lib \
+ $(BASEDIR)\public\sdk\lib\*\rpcndr.lib \
+ $(BASEDIR)\public\sdk\lib\*\rpcrt4.lib \
+ $(BASEDIR)\public\sdk\lib\*\winspool.lib \
+ $(BASEDIR)\public\sdk\lib\*\xactsrv.lib \
+ $(BASEDIR)\public\sdk\lib\*\ntdll.lib \
+ ..\..\lib\obj\*\srvcomn.lib
+
+INCLUDES=.;..;..\..;..\..\..\..\inc;$(BASEDIR)\private\inc;..\..\..\..\xactsrv;..\..\..\..\..\ntos\inc
+
+USE_CRTDLL=1
+
+!IFNDEF DISABLE_NET_UNICODE
+UNICODE=1
+NET_C_DEFINES=-DUNICODE
+!ENDIF
+
+SOURCES= \
+ ..\adtsrv.c \
+ ..\canon.c \
+ ..\cdev.c \
+ ..\cdevq.c \
+ ..\cmdline.c \
+ ..\conn.c \
+ ..\dfs.c \
+ ..\disk.c \
+ ..\file.c \
+ ..\internal.c \
+ ..\registry.c \
+ ..\scavengr.c \
+ ..\security.c \
+ ..\sess.c \
+ ..\share.c \
+ ..\srvinfo.c \
+ ..\srvmain.c \
+ ..\srvsvc.rc \
+ ..\srvsvc_s.c \
+ ..\ssdata.c \
+ ..\ssinit.c \
+ ..\sssubs.c \
+ ..\stats.c \
+ ..\tod.c \
+ ..\xport.c \
+ ..\xsdata.c \
+ ..\xsinit.c \
+ ..\xsproc.c
+
+C_DEFINES=-DRPC_NO_WINDOWS_H
+
+C_DEFINES=$(C_DEFINES) -D_PNP_POWER=1
diff --git a/private/net/svcdlls/srvsvc/server/dfs.c b/private/net/svcdlls/srvsvc/server/dfs.c
new file mode 100644
index 000000000..0b668b8e8
--- /dev/null
+++ b/private/net/svcdlls/srvsvc/server/dfs.c
@@ -0,0 +1,835 @@
+/*++
+
+Copyright (c) 1991-1992 Microsoft Corporation
+
+Module Name:
+
+ Share.c
+
+Abstract:
+
+ This module contains support for the DFS catagory of APIs for the
+ NT server service.
+
+Revision History:
+
+--*/
+
+#include "srvsvcp.h"
+#include "ssdata.h"
+#include "lmerr.h"
+#include <dfsfsctl.h>
+
+#define CAPTURE_STRING( Name ) \
+ if( Name != NULL ) { \
+ ULONG _size = SIZE_WSTR( Name ); \
+ capture->Name = (LPWSTR)variableData; \
+ RtlCopyMemory( capture->Name, Name, _size ); \
+ variableData += _size; \
+ POINTER_TO_OFFSET( capture->Name, capture ); \
+ }
+
+#define RELATION_INFO_SIZE( RelInfo ) \
+ (sizeof( NET_DFS_ENTRY_ID_CONTAINER ) + \
+ (RelInfo->Count * sizeof(NET_DFS_ENTRY_ID)))
+
+NET_API_STATUS
+DfsFsctl(
+ IN ULONG FsControlCode,
+ IN PVOID InputBuffer,
+ IN ULONG InputBufferLength,
+ OUT PVOID OutputBuffer,
+ IN ULONG OutputBufferLength
+)
+{
+ NTSTATUS status;
+ OBJECT_ATTRIBUTES objectAttributes;
+ IO_STATUS_BLOCK ioStatus;
+ HANDLE dfsHandle;
+ UNICODE_STRING deviceName;
+
+ deviceName.Buffer = DFS_SERVER_NAME;
+ deviceName.MaximumLength = sizeof( DFS_SERVER_NAME );
+ deviceName.Length = deviceName.MaximumLength - sizeof(UNICODE_NULL);
+
+ InitializeObjectAttributes(
+ &objectAttributes,
+ &deviceName,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL
+ );
+
+ if( SsInitialized ) {
+ (VOID)RpcImpersonateClient( NULL );
+ }
+
+ status = NtCreateFile(
+ &dfsHandle,
+ SYNCHRONIZE,
+ &objectAttributes,
+ &ioStatus,
+ NULL,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ FILE_OPEN_IF,
+ FILE_CREATE_TREE_CONNECTION | FILE_SYNCHRONOUS_IO_NONALERT,
+ NULL,
+ 0);
+
+ if ( SsInitialized ) {
+ (VOID)RpcRevertToSelf( );
+ }
+
+ if ( NT_SUCCESS(status) ) {
+ status = ioStatus.Status;
+ }
+
+ if( !NT_SUCCESS( status ) ) {
+ return (NET_API_STATUS)status;
+ }
+
+ status = NtFsControlFile(
+ dfsHandle,
+ NULL, // Event,
+ NULL, // ApcRoutine,
+ NULL, // ApcContext,
+ &ioStatus,
+ FsControlCode,
+ InputBuffer,
+ InputBufferLength,
+ OutputBuffer,
+ OutputBufferLength
+ );
+
+ if(NT_SUCCESS(status)) {
+ status = ioStatus.Status;
+ }
+
+ NtClose( dfsHandle );
+
+ return (NET_API_STATUS)status;
+}
+
+NET_API_STATUS NET_API_FUNCTION
+NetrDfsGetVersion(
+ IN SRVSVC_HANDLE ServerName,
+ OUT LPDWORD Version)
+{
+ DFS_GET_VERSION_ARG arg;
+ NET_API_STATUS error;
+
+ RtlZeroMemory( &arg, sizeof(arg) );
+
+ error = DfsFsctl( FSCTL_DFS_GET_VERSION, &arg, sizeof( arg ), NULL, 0 );
+
+ if (error == NERR_Success) {
+
+ *Version = arg.Version;
+
+ } else {
+
+ error = ERROR_FILE_NOT_FOUND;
+
+ }
+
+ return( error );
+
+}
+
+NET_API_STATUS NET_API_FUNCTION
+NetrDfsCreateLocalPartition (
+ IN SRVSVC_HANDLE ServerName, // Name of server for this API
+ IN LPWSTR ShareName, // Name of share to add to the DFS
+ IN LPGUID EntryUid, // unique id for this partition
+ IN LPWSTR EntryPrefix, // DFS entry path for this volume
+ IN LPWSTR ShortName, // 8.3 format of EntryPrefix
+ IN LPNET_DFS_ENTRY_ID_CONTAINER RelationInfo,
+ IN BOOL Force // Force knowledge into consistent state?
+ )
+{
+ NET_API_STATUS error;
+ PDFS_CREATE_LOCAL_PARTITION_ARG capture;
+ ULONG size = sizeof( *capture );
+ ULONG i;
+ PCHAR variableData;
+ PSERVER_REQUEST_PACKET srp;
+ LPSHARE_INFO_2 shareInfo2 = NULL;
+ UNICODE_STRING ntSharePath;
+
+ if( ShareName == NULL || EntryUid == NULL ||
+ EntryPrefix == NULL || RelationInfo == NULL ) {
+
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ //
+ // Make a call to the SMB server to find the pathname for the share.
+ //
+ srp = SsAllocateSrp();
+ if( srp == NULL ) {
+ return ERROR_NOT_ENOUGH_MEMORY;
+ };
+ srp->Level = 2;
+ srp->Flags = SRP_RETURN_SINGLE_ENTRY;
+ srp->Parameters.Get.ResumeHandle = 0;
+ RtlInitUnicodeString( &srp->Name1, ShareName );
+ error = SsServerFsControlGetInfo(
+ FSCTL_SRV_NET_SHARE_ENUM,
+ srp,
+ &shareInfo2,
+ 10000
+ );
+ if( error != NO_ERROR ) {
+ SsFreeSrp( srp );
+ return error;
+ }
+
+ if( srp->Parameters.Get.EntriesRead == 0 ||
+ shareInfo2 == NULL ||
+ shareInfo2->shi2_path == NULL ) {
+
+ SsFreeSrp( srp );
+ if( shareInfo2 != NULL ) {
+ MIDL_user_free( shareInfo2 );
+ }
+ return ERROR_BAD_NET_NAME;
+ }
+
+ if( (shareInfo2->shi2_type & ~STYPE_SPECIAL) != STYPE_DISKTREE ) {
+ SsFreeSrp( srp );
+ MIDL_user_free( shareInfo2 );
+ return ERROR_BAD_DEV_TYPE;
+ }
+
+ //
+ // Now we need to convert the share's Win32 style pathname to an
+ // NT pathname
+ //
+ ntSharePath.Buffer = NULL;
+
+ if( !RtlDosPathNameToNtPathName_U(
+ shareInfo2->shi2_path,
+ &ntSharePath,
+ NULL,
+ NULL ) ) {
+
+ SsFreeSrp( srp );
+ MIDL_user_free( shareInfo2 );
+ return ERROR_INVALID_PARAMETER;
+ }
+ MIDL_user_free( shareInfo2 );
+
+ //
+ // Pack the data into an fsctl that can be sent to the local Dfs driver:
+ //
+ // First find the size...
+ //
+ size += SIZE_WSTR( ShareName );
+ size += ntSharePath.Length + sizeof( WCHAR );
+ size += SIZE_WSTR( EntryPrefix );
+ size += SIZE_WSTR( ShortName );
+
+ if( ARGUMENT_PRESENT( RelationInfo ) ) {
+ size += RELATION_INFO_SIZE(RelationInfo);
+ for( i = 0; i < RelationInfo->Count; i++ ) {
+ size += SIZE_WSTR( RelationInfo->Buffer[i].Prefix );
+ }
+ }
+
+ //
+ // Now allocate the memory
+ //
+ capture = MIDL_user_allocate( size );
+ if( capture == NULL ) {
+ SsFreeSrp( srp );
+ RtlFreeUnicodeString( &ntSharePath );
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ RtlZeroMemory( capture, size );
+
+ //
+ // Put the fixed parameters in the capture buffer
+ //
+ capture->EntryUid = *EntryUid;
+ capture->Force = Force;
+
+ //
+ // Put the variable data in the capture buffer.
+ //
+
+ variableData = (PCHAR)(capture + 1);
+
+ if( ARGUMENT_PRESENT( RelationInfo ) ) {
+ capture->RelationInfo = (LPNET_DFS_ENTRY_ID_CONTAINER)variableData;
+ capture->RelationInfo->Buffer = (LPNET_DFS_ENTRY_ID)
+ (capture->RelationInfo + 1);
+ variableData += RELATION_INFO_SIZE( RelationInfo );
+ for( i=0; i < RelationInfo->Count; i++ ) {
+ CAPTURE_STRING( RelationInfo->Buffer[i].Prefix );
+ capture->RelationInfo->Buffer[i].Uid = RelationInfo->Buffer[i].Uid;
+ }
+
+ POINTER_TO_OFFSET( capture->RelationInfo->Buffer, capture );
+ POINTER_TO_OFFSET( capture->RelationInfo, capture );
+
+ }
+
+ CAPTURE_STRING( ShareName );
+ CAPTURE_STRING( EntryPrefix );
+ CAPTURE_STRING( ShortName );
+
+ //
+ // Capture the nt version of the share path
+ //
+ capture->SharePath = (LPWSTR)variableData;
+ RtlCopyMemory( capture->SharePath, ntSharePath.Buffer, ntSharePath.Length );
+ variableData += ntSharePath.Length;
+ POINTER_TO_OFFSET( capture->SharePath, capture );
+
+ (WCHAR *)variableData = 0; // Null terminate the name
+ variableData += sizeof( WCHAR );
+
+ RtlFreeUnicodeString( &ntSharePath );
+
+ //
+ // First, tell the server to mark this share as being in Dfs. Note that
+ // the share name is already in srp->Name1. If we later run into an
+ // error, we'll undo the state change.
+ //
+
+ srp->Flags = SRP_SET_SHARE_IN_DFS;
+ error = SsServerFsControl(
+ NULL,
+ FSCTL_SRV_SHARE_STATE_CHANGE,
+ srp,
+ NULL,
+ 0
+ );
+ if( error != NO_ERROR ) {
+ SsFreeSrp( srp );
+ MIDL_user_free( capture );
+ return error;
+ }
+
+ //
+ // Tell the Dfs driver!
+ //
+ error = DfsFsctl(
+ FSCTL_DFS_CREATE_LOCAL_PARTITION,
+ capture,
+ size,
+ NULL,
+ 0
+ );
+
+ MIDL_user_free( capture );
+
+ if (error != NO_ERROR) {
+
+ //
+ // An error occured changing the Dfs state. So, try to undo the
+ // server share state change.
+ //
+
+ NET_API_STATUS error2;
+
+ srp->Flags = SRP_CLEAR_SHARE_IN_DFS;
+ error2 = SsServerFsControl(
+ NULL,
+ FSCTL_SRV_SHARE_STATE_CHANGE,
+ srp,
+ NULL,
+ 0);
+
+ }
+
+ SsFreeSrp( srp );
+
+ return error;
+}
+
+NET_API_STATUS NET_API_FUNCTION
+NetrDfsDeleteLocalPartition (
+ IN SRVSVC_HANDLE ServerName,
+ IN LPGUID Uid,
+ IN LPWSTR Prefix
+ )
+{
+ NET_API_STATUS error;
+ PDFS_DELETE_LOCAL_PARTITION_ARG capture;
+ ULONG size = sizeof( *capture );
+ PCHAR variableData;
+
+ //
+ // Pack the args into a single buffer that can be sent to
+ // the dfs driver:
+ //
+
+ //
+ // First find the size...
+ //
+ size += SIZE_WSTR( Prefix );
+
+ //
+ // Now allocate the memory
+ //
+ capture = MIDL_user_allocate( size );
+ if( capture == NULL ) {
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ RtlZeroMemory( capture, size );
+
+ //
+ // Put the fixed parameters into the capture buffer
+ //
+ capture->Uid = *Uid;
+
+ //
+ // Put the variable data in the capture buffer
+ //
+ variableData = (PCHAR)(capture + 1 );
+
+ CAPTURE_STRING( Prefix );
+
+ //
+ // Tell the driver!
+ //
+ error = DfsFsctl(
+ FSCTL_DFS_DELETE_LOCAL_PARTITION,
+ capture,
+ size,
+ NULL,
+ 0
+ );
+
+ MIDL_user_free( capture );
+
+ //
+ // If there was no error, tell the server that this share
+ // is no longer in the Dfs
+ //
+
+
+ return error;
+}
+
+NET_API_STATUS NET_API_FUNCTION
+NetrDfsSetLocalVolumeState (
+ IN SRVSVC_HANDLE ServerName,
+ IN LPGUID Uid,
+ IN LPWSTR Prefix,
+ IN ULONG State
+ )
+{
+ NET_API_STATUS error;
+ PDFS_SET_LOCAL_VOLUME_STATE_ARG capture;
+ ULONG size = sizeof( *capture );
+ PCHAR variableData;
+
+ //
+ // Pack the args into a single buffer that can be sent to
+ // the dfs driver:
+ //
+
+ //
+ // First find the size...
+ //
+ size += SIZE_WSTR( Prefix );
+
+ //
+ // Now allocate the memory
+ //
+ capture = MIDL_user_allocate( size );
+ if( capture == NULL ) {
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ RtlZeroMemory( capture, size );
+
+ //
+ // Put the fixed parameters into the capture buffer
+ //
+ capture->Uid = *Uid;
+ capture->State = State;
+
+ //
+ // Put the variable data in the capture buffer
+ //
+ variableData = (PCHAR)(capture + 1 );
+
+ CAPTURE_STRING( Prefix );
+
+ //
+ // Tell the driver!
+ //
+ error = DfsFsctl(
+ FSCTL_DFS_SET_LOCAL_VOLUME_STATE,
+ capture,
+ size,
+ NULL,
+ 0
+ );
+
+ MIDL_user_free( capture );
+
+ return error;
+}
+
+NET_API_STATUS NET_API_FUNCTION
+NetrDfsSetServerInfo (
+ IN SRVSVC_HANDLE ServerName,
+ IN LPGUID Uid,
+ IN LPWSTR Prefix
+ )
+{
+ NET_API_STATUS error;
+ PDFS_SET_SERVER_INFO_ARG capture;
+ ULONG size = sizeof( *capture );
+ PCHAR variableData;
+
+ //
+ // Pack the args into a single buffer that can be sent to
+ // the dfs driver:
+ //
+
+ //
+ // First find the size...
+ //
+ size += SIZE_WSTR( Prefix );
+
+ //
+ // Now allocate the memory
+ //
+ capture = MIDL_user_allocate( size );
+ if( capture == NULL ) {
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ RtlZeroMemory( capture, size );
+
+ //
+ // Put the fixed parameters into the capture buffer
+ //
+ capture->Uid = *Uid;
+
+ //
+ // Put the variable data in the capture buffer
+ //
+ variableData = (PCHAR)(capture + 1 );
+
+ CAPTURE_STRING( Prefix );
+
+ //
+ // Tell the driver!
+ //
+ error = DfsFsctl(
+ FSCTL_DFS_SET_SERVER_INFO,
+ capture,
+ size,
+ NULL,
+ 0
+ );
+
+ MIDL_user_free( capture );
+
+ return error;
+}
+
+NET_API_STATUS NET_API_FUNCTION
+NetrDfsCreateExitPoint (
+ IN SRVSVC_HANDLE ServerName,
+ IN LPGUID Uid,
+ IN LPWSTR Prefix,
+ IN ULONG Type,
+ IN ULONG ShortPrefixLen,
+ OUT LPWSTR ShortPrefix
+ )
+{
+ NET_API_STATUS error;
+ PDFS_CREATE_EXIT_POINT_ARG capture;
+ ULONG size = sizeof( *capture );
+ PCHAR variableData;
+
+ //
+ // Pack the args into a single buffer that can be sent to
+ // the dfs driver:
+ //
+
+ //
+ // First find the size...
+ //
+ size += SIZE_WSTR( Prefix );
+
+ //
+ // Now allocate the memory
+ //
+ capture = MIDL_user_allocate( size );
+ if( capture == NULL ) {
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ RtlZeroMemory( capture, size );
+
+ //
+ // Put the fixed parameters into the capture buffer
+ //
+ capture->Uid = *Uid;
+ capture->Type = Type;
+
+ //
+ // Put the variable data in the capture buffer
+ //
+ variableData = (PCHAR)(capture + 1 );
+
+ CAPTURE_STRING( Prefix );
+
+ //
+ // Tell the driver!
+ //
+ error = DfsFsctl(
+ FSCTL_DFS_CREATE_EXIT_POINT,
+ capture,
+ size,
+ ShortPrefix,
+ ShortPrefixLen * sizeof(WCHAR)
+ );
+
+ MIDL_user_free( capture );
+
+ return error;
+}
+
+NET_API_STATUS NET_API_FUNCTION
+NetrDfsDeleteExitPoint (
+ IN SRVSVC_HANDLE ServerName,
+ IN LPGUID Uid,
+ IN LPWSTR Prefix,
+ IN ULONG Type
+ )
+{
+ NET_API_STATUS error;
+ PDFS_DELETE_EXIT_POINT_ARG capture;
+ ULONG size = sizeof( *capture );
+ PCHAR variableData;
+
+ //
+ // Pack the args into a single buffer that can be sent to
+ // the dfs driver:
+ //
+
+ //
+ // First find the size...
+ //
+ size += SIZE_WSTR( Prefix );
+
+ //
+ // Now allocate the memory
+ //
+ capture = MIDL_user_allocate( size );
+ if( capture == NULL ) {
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ RtlZeroMemory( capture, size );
+
+ //
+ // Put the fixed parameters into the capture buffer
+ //
+ capture->Uid = *Uid;
+ capture->Type = Type;
+
+ //
+ // Put the variable data in the capture buffer
+ //
+ variableData = (PCHAR)(capture + 1 );
+
+ CAPTURE_STRING( Prefix );
+
+ //
+ // Tell the driver!
+ //
+ error = DfsFsctl(
+ FSCTL_DFS_DELETE_EXIT_POINT,
+ capture,
+ size,
+ NULL,
+ 0
+ );
+
+ MIDL_user_free( capture );
+
+ return error;
+}
+
+NET_API_STATUS NET_API_FUNCTION
+NetrDfsModifyPrefix (
+ IN SRVSVC_HANDLE ServerName,
+ IN LPGUID Uid,
+ IN LPWSTR Prefix
+ )
+{
+ NET_API_STATUS error;
+ PDFS_DELETE_LOCAL_PARTITION_ARG capture;
+ ULONG size = sizeof( *capture );
+ PCHAR variableData;
+
+ //
+ // Pack the args into a single buffer that can be sent to
+ // the dfs driver:
+ //
+
+ //
+ // First find the size...
+ //
+ size += SIZE_WSTR( Prefix );
+
+ //
+ // Now allocate the memory
+ //
+ capture = MIDL_user_allocate( size );
+ if( capture == NULL ) {
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ RtlZeroMemory( capture, size );
+
+ //
+ // Put the fixed parameters into the capture buffer
+ //
+ capture->Uid = *Uid;
+
+ //
+ // Put the variable data in the capture buffer
+ //
+ variableData = (PCHAR)(capture + 1 );
+
+ CAPTURE_STRING( Prefix );
+
+ //
+ // Tell the driver!
+ //
+ error = DfsFsctl(
+ FSCTL_DFS_MODIFY_PREFIX,
+ capture,
+ size,
+ NULL,
+ 0
+ );
+
+ MIDL_user_free( capture );
+
+ return error;
+}
+
+NET_API_STATUS NET_API_FUNCTION
+NetrDfsFixLocalVolume (
+ IN SRVSVC_HANDLE ServerName,
+ IN LPWSTR VolumeName,
+ IN ULONG EntryType,
+ IN ULONG ServiceType,
+ IN LPWSTR StgId,
+ IN LPGUID EntryUid, // unique id for this partition
+ IN LPWSTR EntryPrefix, // path prefix for this partition
+ IN LPNET_DFS_ENTRY_ID_CONTAINER RelationInfo,
+ IN ULONG CreateDisposition
+ )
+{
+ NET_API_STATUS error;
+ PDFS_FIX_LOCAL_VOLUME_ARG capture;
+ ULONG size = sizeof( *capture );
+ ULONG i;
+ PCHAR variableData;
+
+ //
+ // Pack the args into a single buffer that can be sent to the
+ // dfs driver:
+ //
+
+ //
+ // First find the size...
+ //
+ size += SIZE_WSTR( VolumeName );
+ size += SIZE_WSTR( StgId );
+ size += SIZE_WSTR( EntryPrefix );
+
+ if( ARGUMENT_PRESENT( RelationInfo ) ) {
+ size += RELATION_INFO_SIZE( RelationInfo );
+ for( i = 0; i < RelationInfo->Count; i++ ) {
+ size += SIZE_WSTR( RelationInfo->Buffer[i].Prefix );
+ }
+ }
+
+ //
+ // Now allocate the memory
+ //
+ capture = MIDL_user_allocate( size );
+ if( capture == NULL ) {
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ RtlZeroMemory( capture, size );
+
+ //
+ // Put the fixed parameters in the capture buffer
+ //
+ capture->EntryType = EntryType;
+ capture->ServiceType = ServiceType;
+ capture->EntryUid = *EntryUid;
+ capture->CreateDisposition = CreateDisposition;
+
+ //
+ // Put the variable data in the capture buffer.
+ //
+
+ variableData = (PCHAR)(capture + 1);
+
+ if( ARGUMENT_PRESENT( RelationInfo ) ) {
+ capture->RelationInfo = (LPNET_DFS_ENTRY_ID_CONTAINER)variableData;
+ capture->RelationInfo->Buffer = (LPNET_DFS_ENTRY_ID)
+ (capture->RelationInfo + 1);
+ variableData += RELATION_INFO_SIZE( RelationInfo );
+ for( i=0; i < RelationInfo->Count; i++ ) {
+ CAPTURE_STRING( RelationInfo->Buffer[i].Prefix );
+ capture->RelationInfo->Buffer[i].Uid = RelationInfo->Buffer[i].Uid;
+ }
+
+ POINTER_TO_OFFSET( capture->RelationInfo->Buffer, capture );
+ POINTER_TO_OFFSET( capture->RelationInfo, capture );
+ }
+
+ CAPTURE_STRING( VolumeName );
+ CAPTURE_STRING( StgId );
+ CAPTURE_STRING( EntryPrefix );
+
+ //
+ // Tell the driver!
+ //
+ error = DfsFsctl(
+ FSCTL_DFS_FIX_LOCAL_VOLUME,
+ capture,
+ size,
+ NULL,
+ 0
+ );
+
+ MIDL_user_free( capture );
+
+ return error;
+}
+
+
+//
+// This routine returns TRUE if this machine is the root of a DFS, FALSE otherwise
+//
+VOID
+SsSetDfsRoot()
+{
+ NET_API_STATUS error;
+
+ error = DfsFsctl( FSCTL_DFS_IS_ROOT, NULL, 0, NULL, 0 );
+
+ SsData.IsDfsRoot = (error == NO_ERROR);
+}
diff --git a/private/net/svcdlls/srvsvc/server/dirs b/private/net/svcdlls/srvsvc/server/dirs
new file mode 100644
index 000000000..bf1044c7a
--- /dev/null
+++ b/private/net/svcdlls/srvsvc/server/dirs
@@ -0,0 +1,3 @@
+DIRS=daytona
+
+OPTIONAL_DIRS=
diff --git a/private/net/svcdlls/srvsvc/server/disk.c b/private/net/svcdlls/srvsvc/server/disk.c
new file mode 100644
index 000000000..1342b050c
--- /dev/null
+++ b/private/net/svcdlls/srvsvc/server/disk.c
@@ -0,0 +1,173 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ disk.c
+
+Abstract:
+
+ This module contains support for the NetServerDiskEnum API for the NT
+ OS/2 server service.
+
+Author:
+
+ Johnson Apacible (johnsona) 19-March-1992
+
+Revision History:
+
+--*/
+
+#include "srvsvcp.h"
+
+#include "nturtl.h"
+
+#include "winbase.h"
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetrServerDiskEnum(
+ IN LPTSTR ServerName,
+ IN DWORD Level,
+ IN OUT DISK_ENUM_CONTAINER *DiskInfoStruct,
+ IN DWORD PrefMaxLen,
+ OUT LPDWORD TotalEntries,
+ IN OUT LPDWORD ResumeHandle
+ )
+
+/*++
+
+Routine Description:
+
+ This routine communicates with the server FSD to implement the
+ server half of the NetServerDiskEnum function.
+
+Arguments:
+
+ ServerName - optional name of server.
+ Level - must be 0
+ DiskInfoStruct - the output buffer.
+ PrefMaxLen - the preferred maximum length of the output buffer.
+ TotalEntries - total number of drive entries in the output buffer.
+ ResumeHandle - ignored.
+
+Return Value:
+
+ NET_API_STATUS - NO_ERROR or reason for failure.
+
+--*/
+
+{
+ NET_API_STATUS error;
+ TCHAR diskName[4];
+ UINT i;
+ UINT driveType;
+ UINT totalBytes = 0;
+ LPTSTR tempBuffer;
+ LPTSTR currentDiskInfo;
+
+ ServerName, PrefMaxLen, ResumeHandle;
+
+ //
+ // The only valid level is 0.
+ //
+
+ if ( Level != 0 ) {
+ return ERROR_INVALID_LEVEL;
+ }
+
+ //
+ // Make sure that the caller is allowed to get disk information from
+ // the server.
+ //
+
+ error = SsCheckAccess(
+ &SsDiskSecurityObject,
+ SRVSVC_DISK_ENUM
+ );
+
+ if ( error != NO_ERROR ) {
+ return ERROR_ACCESS_DENIED;
+ }
+
+ //
+ // Go through all the driver letters, get those that does not return
+ // an error.
+ //
+
+ tempBuffer = MIDL_user_allocate(
+ (SRVSVC_MAX_NUMBER_OF_DISKS * (3 * sizeof(TCHAR))) +
+ sizeof(TCHAR)
+ );
+
+ if ( tempBuffer == NULL ) {
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ currentDiskInfo = tempBuffer;
+
+ diskName[0] = 'A';
+ diskName[1] = ':';
+ diskName[2] = '\\';
+ diskName[3] = '\0';
+
+ *TotalEntries = 0;
+
+ for ( i = 0; i < SRVSVC_MAX_NUMBER_OF_DISKS; i++ ) {
+
+ driveType = SsGetDriveType( diskName );
+
+ if ( driveType == DRIVE_FIXED ||
+ driveType == DRIVE_CDROM ||
+ driveType == DRIVE_REMOVABLE ||
+ driveType == DRIVE_RAMDISK ) {
+
+ //
+ // This is a valid disk
+ //
+
+ (*TotalEntries)++;
+ *(currentDiskInfo++) = diskName[0];
+ *(currentDiskInfo++) = ':';
+ *(currentDiskInfo++) = '\0';
+
+ }
+
+ diskName[0]++;
+
+ }
+
+#ifdef UNICODE
+ *currentDiskInfo = UNICODE_NULL;
+#else
+ *currentDiskInfo = '\0';
+#endif
+
+ //
+ // EntriesRead must be one greater than TotalEntries so RPC can
+ // marshal the output strings back to the client correctly.
+ //
+
+ totalBytes = ((*TotalEntries) * (3 * sizeof(TCHAR))) + sizeof(TCHAR);
+
+ DiskInfoStruct->EntriesRead = (*TotalEntries) + 1;
+ DiskInfoStruct->Buffer = MIDL_user_allocate( totalBytes );
+
+ if ( DiskInfoStruct->Buffer != NULL ) {
+ RtlCopyMemory(
+ DiskInfoStruct->Buffer,
+ tempBuffer,
+ totalBytes
+ );
+ } else {
+ MIDL_user_free(tempBuffer);
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ MIDL_user_free( tempBuffer );
+
+ return NO_ERROR;
+
+} // NetrServerDiskEnum
+
diff --git a/private/net/svcdlls/srvsvc/server/file.c b/private/net/svcdlls/srvsvc/server/file.c
new file mode 100644
index 000000000..fdfb0c9ae
--- /dev/null
+++ b/private/net/svcdlls/srvsvc/server/file.c
@@ -0,0 +1,329 @@
+/*++
+
+Copyright (c) 1991-1992 Microsoft Corporation
+
+Module Name:
+
+ File.c
+
+Abstract:
+
+ This module contains support for the File catagory of APIs for the
+ NT server service.
+
+Author:
+
+ David Treadwell (davidtr) 13-Feb-1991
+
+Revision History:
+
+--*/
+
+#include "srvsvcp.h"
+
+//
+// Forward declarations.
+//
+
+NET_API_STATUS
+FileEnumCommon (
+ IN LPTSTR BasePath,
+ IN LPTSTR UserName,
+ IN DWORD Level,
+ OUT LPBYTE *Buffer,
+ IN DWORD PreferredMaximumLength,
+ OUT LPDWORD EntriesRead,
+ OUT LPDWORD TotalEntries,
+ IN OUT LPDWORD ResumeHandle OPTIONAL,
+ IN BOOLEAN IsGetInfo
+ );
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetrFileClose (
+ IN LPTSTR ServerName,
+ IN DWORD FileId
+ )
+
+/*++
+
+Routine Description:
+
+ This routine communicates with the server FSD and FSP to implement the
+ NetFileClose function.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NET_API_STATUS - NO_ERROR or reason for failure.
+
+--*/
+
+{
+ NET_API_STATUS error;
+ PSERVER_REQUEST_PACKET srp;
+
+ ServerName;
+
+ //
+ // Make sure that the caller is allowed to close files in the server.
+ //
+
+ error = SsCheckAccess( &SsFileSecurityObject, SRVSVC_FILE_CLOSE );
+
+ if ( error != NO_ERROR ) {
+ return ERROR_ACCESS_DENIED;
+ }
+
+ //
+ // Set up the request packet. We use the name buffer pointer to
+ // hold the file ID of the file to close.
+ //
+
+ srp = SsAllocateSrp( );
+ if ( srp == NULL ) {
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+ srp->Parameters.Get.ResumeHandle = FileId;
+
+ //
+ // Simply send the request on to the server.
+ //
+
+ error = SsServerFsControl( NULL, FSCTL_SRV_NET_FILE_CLOSE, srp, NULL, 0 );
+
+ SsFreeSrp( srp );
+
+ return error;
+
+} // NetrFileClose
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetrFileEnum (
+ IN LPTSTR ServerName,
+ IN LPTSTR BasePath,
+ IN LPTSTR UserName,
+ OUT PFILE_ENUM_STRUCT InfoStruct,
+ IN DWORD PreferredMaximumLength,
+ OUT LPDWORD TotalEntries,
+ IN OUT LPDWORD ResumeHandle OPTIONAL
+ )
+
+/*++
+
+Routine Description:
+
+ This routine communicates with the server FSD to implement the
+ NetFileEnum function.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NET_API_STATUS - NO_ERROR or reason for failure.
+
+--*/
+
+{
+ NET_API_STATUS error;
+
+ ServerName;
+
+ //
+ // Make sure that the caller is allowed to set share information in the
+ // server.
+ //
+
+ error = SsCheckAccess(
+ &SsFileSecurityObject,
+ SRVSVC_FILE_INFO_GET
+ );
+
+ if ( error != NO_ERROR ) {
+ return ERROR_ACCESS_DENIED;
+ }
+
+ return FileEnumCommon(
+ BasePath,
+ UserName,
+ InfoStruct->Level,
+ (LPBYTE *)&InfoStruct->FileInfo.Level3->Buffer,
+ PreferredMaximumLength,
+ &InfoStruct->FileInfo.Level3->EntriesRead,
+ TotalEntries,
+ ResumeHandle,
+ FALSE
+ );
+
+} // NetrFileEnum
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetrFileGetInfo (
+ IN LPTSTR ServerName,
+ IN DWORD FileId,
+ IN DWORD Level,
+ OUT LPFILE_INFO InfoStruct
+ )
+
+/*++
+
+Routine Description:
+
+ This routine communicates with the server FSD to implement the
+ NetFileGetInfo function.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NET_API_STATUS - NO_ERROR or reason for failure.
+
+--*/
+
+{
+ NET_API_STATUS error;
+ ULONG entriesRead;
+ ULONG totalEntries;
+ ULONG resumeHandle = FileId;
+
+ ServerName;
+
+ //
+ // Make sure that the caller is allowed to get file information in the
+ // server.
+ //
+
+ error = SsCheckAccess(
+ &SsFileSecurityObject,
+ SRVSVC_FILE_INFO_GET
+ );
+
+ if ( error != NO_ERROR ) {
+ return ERROR_ACCESS_DENIED;
+ }
+
+ error = FileEnumCommon(
+ NULL,
+ NULL,
+ Level,
+ (LPBYTE *)InfoStruct,
+ (DWORD)-1,
+ &entriesRead,
+ &totalEntries,
+ &resumeHandle,
+ TRUE
+ );
+
+ if ( entriesRead == 0 ) {
+ return ERROR_FILE_NOT_FOUND;
+ }
+
+ SS_ASSERT( error != NO_ERROR || entriesRead == 1 );
+
+ return error;
+
+} // NetrFileGetInfo
+
+
+NET_API_STATUS
+FileEnumCommon (
+ IN LPTSTR BasePath,
+ IN LPTSTR UserName,
+ IN DWORD Level,
+ OUT LPBYTE *Buffer,
+ IN DWORD PreferredMaximumLength,
+ OUT LPDWORD EntriesRead,
+ OUT LPDWORD TotalEntries,
+ IN OUT LPDWORD ResumeHandle OPTIONAL,
+ IN BOOLEAN IsGetInfo
+ )
+
+{
+ NET_API_STATUS error;
+ PSERVER_REQUEST_PACKET srp;
+
+ //
+ // Make sure that the level is valid.
+ //
+
+ if ( Level != 2 && Level != 3 ) {
+ return ERROR_INVALID_LEVEL;
+ }
+
+ //
+ // Set up the input parameters in the request buffer.
+ //
+
+ srp = SsAllocateSrp( );
+ if ( srp == NULL ) {
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+#ifdef UNICODE
+ RtlInitUnicodeString( &srp->Name1, BasePath );
+ RtlInitUnicodeString( &srp->Name2, UserName );
+#else
+ {
+ NTSTATUS status;
+ OEM_STRING ansiString;
+ RtlInitString( &ansiString, BasePath );
+ status = RtlOemStringToUnicodeString( &srp->Name1, &ansiString, TRUE );
+ RtlInitString( &ansiString, UserName );
+ status = RtlOemStringToUnicodeString( &srp->Name2, &ansiString, TRUE );
+ }
+#endif
+
+ srp->Level = Level;
+ if ( IsGetInfo ) {
+ srp->Flags = SRP_RETURN_SINGLE_ENTRY;
+ }
+
+ if ( ARGUMENT_PRESENT( ResumeHandle ) ) {
+ srp->Parameters.Get.ResumeHandle = *ResumeHandle;
+ } else {
+ srp->Parameters.Get.ResumeHandle = 0;
+ }
+
+ //
+ // Get the data from the server. This routine will allocate the
+ // return buffer and handle the case where PreferredMaximumLength ==
+ // -1.
+ //
+
+ error = SsServerFsControlGetInfo(
+ FSCTL_SRV_NET_FILE_ENUM,
+ srp,
+ (PVOID *)Buffer,
+ PreferredMaximumLength
+ );
+
+ //
+ // Set up return information. Only change the resume handle if at
+ // least one entry was returned.
+ //
+
+ *EntriesRead = srp->Parameters.Get.EntriesRead;
+ *TotalEntries = srp->Parameters.Get.TotalEntries;
+ if ( *EntriesRead > 0 && ARGUMENT_PRESENT( ResumeHandle ) ) {
+ *ResumeHandle = srp->Parameters.Get.ResumeHandle;
+ }
+
+#ifndef UNICODE
+ RtlFreeUnicodeString( &srp->Name1 );
+ RtlFreeUnicodeString( &srp->Name2 );
+#endif
+
+ SsFreeSrp( srp );
+
+ return error;
+
+} // FileEnumCommon
diff --git a/private/net/svcdlls/srvsvc/server/internal.c b/private/net/svcdlls/srvsvc/server/internal.c
new file mode 100644
index 000000000..d038cfa85
--- /dev/null
+++ b/private/net/svcdlls/srvsvc/server/internal.c
@@ -0,0 +1,214 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ Internal.c
+
+Abstract:
+
+ This module contains "internal" APIs exported by the server service.
+
+--*/
+
+#include "srvsvcp.h"
+#include "ssdata.h"
+
+#include <debugfmt.h>
+#include <tstr.h>
+#include <lmerr.h>
+
+
+NET_API_STATUS NET_API_FUNCTION
+I_NetrServerSetServiceBitsEx (
+ IN LPTSTR ServerName,
+ IN LPTSTR EmulatedServerName OPTIONAL,
+ IN LPTSTR TransportName OPTIONAL,
+ IN DWORD ServiceBitsOfInterest,
+ IN DWORD ServiceBits,
+ IN DWORD UpdateImmediately
+ )
+
+/*++
+
+Routine Description:
+
+ This routine sets the value of the Server Type as sent in server
+ announcement messages. It is an internal API used only by the
+ service controller.
+
+Arguments:
+
+ ServerName - Used by RPC to direct the call. This API may only be
+ issued locally. This is enforced by the client stub.
+
+ EmulatedServerName - server name being emulated on this computer
+
+ TransportName - parameter optionally giving specific transport for which
+ to set the bits
+
+ ServiceBitsOfInterest - bit mask indicating significant 'ServiceBits'
+
+ ServiceBits - Bits (preassigned to various components by Microsoft)
+ indicating which services are active. This field is not
+ interpreted by the server service.
+
+Return Value:
+
+ NET_API_STATUS - NO_ERROR or ERROR_NOT_SUPPORTED.
+
+--*/
+
+{
+ BOOL changed = FALSE;
+ PNAME_LIST_ENTRY Service;
+ PTRANSPORT_LIST_ENTRY transport;
+ DWORD newBits;
+ NET_API_STATUS error;
+ CHAR serverNameBuf[ MAX_PATH ];
+ PCHAR emulatedName;
+ ULONG namelen;
+
+ ServerName; // avoid compiler warnings
+
+ if( ARGUMENT_PRESENT( EmulatedServerName ) ) {
+ UNICODE_STRING name;
+
+ RtlInitUnicodeString( &name, EmulatedServerName );
+
+ error = ConvertStringToTransportAddress( &name, serverNameBuf, &namelen );
+ if( error != NERR_Success ) {
+ return error;
+ }
+
+ emulatedName = serverNameBuf;
+
+ } else {
+
+ emulatedName = SsServerTransportAddress;
+ namelen = SsServerTransportAddressLength;
+ }
+
+ //
+ // Don't let bits that are controlled by the server be set.
+ //
+
+ ServiceBitsOfInterest &= ~SERVER_TYPE_INTERNAL_BITS;
+ ServiceBits &= ServiceBitsOfInterest;
+
+ //
+ // Make the modifications under control of the service resource.
+ //
+
+ (VOID)RtlAcquireResourceExclusive( &SsServerInfoResource, TRUE );
+
+#ifdef SRV_PNP_POWER
+ if( SsServerNameList == NULL && !ARGUMENT_PRESENT( TransportName ) ) {
+
+ //
+ // We have not bound to any transports yet.
+ // Remember the setting which is being asked for so we can use it later
+ //
+
+ SsData.ServiceBits &= ~ServiceBitsOfInterest;
+ SsData.ServiceBits |= ServiceBits;
+ RtlReleaseResource( &SsServerInfoResource );
+ return NO_ERROR;
+ }
+#endif
+
+ //
+ // Find the entry for the server name of interest
+ //
+ for( Service = SsServerNameList; Service != NULL; Service = Service->Next ) {
+
+ if( Service->TransportAddressLength != namelen ) {
+ continue;
+ }
+
+ if( RtlEqualMemory( emulatedName, Service->TransportAddress, namelen ) ) {
+ break;
+ }
+ }
+
+ if( Service == NULL ) {
+ RtlReleaseResource( &SsServerInfoResource );
+ return NERR_NetNameNotFound;
+ }
+
+#ifdef SRV_PNP_POWER
+
+ if( SsData.ServiceBits != 0 && Service->PrimaryName ) {
+ Service->ServiceBits = SsData.ServiceBits;
+ SsData.ServiceBits = 0;
+ }
+
+#endif
+
+ if( ARGUMENT_PRESENT( TransportName ) ) {
+ //
+ // Transport name specified. Set the bits for that transport only.
+ //
+
+ for( transport = Service->Transports; transport != NULL; transport = transport->Next ) {
+ if( !STRCMPI( TransportName, transport->TransportName ) ) {
+ //
+ // This is the transport of interest!
+ //
+ if( (transport->ServiceBits & ServiceBitsOfInterest) != ServiceBits ) {
+ transport->ServiceBits &= ~ServiceBitsOfInterest;
+ transport->ServiceBits |= ServiceBits;
+ changed = TRUE;
+ }
+ break;
+ }
+ }
+ if( transport == NULL ) {
+ //
+ // The requested transport was not found.
+ //
+ RtlReleaseResource( &SsServerInfoResource );
+ return ERROR_PATH_NOT_FOUND;
+ }
+
+ } else {
+ //
+ // No transport name specified. Change the bits for the whole server
+ //
+
+ if( ( Service->ServiceBits & ServiceBitsOfInterest ) != ServiceBits ) {
+ Service->ServiceBits &= ~ServiceBitsOfInterest;
+ Service->ServiceBits |= ServiceBits;
+ changed = TRUE;
+
+ }
+ }
+
+ RtlReleaseResource( &SsServerInfoResource );
+
+ if ( changed ) {
+ SsSetExportedServerType( NULL, TRUE, (BOOL)UpdateImmediately );
+ }
+
+ return NO_ERROR;
+
+} // I_NetrServerSetServiceBits
+
+NET_API_STATUS NET_API_FUNCTION
+I_NetrServerSetServiceBits (
+ IN LPTSTR ServerName,
+ IN LPTSTR TransportName OPTIONAL,
+ IN DWORD ServiceBits,
+ IN DWORD UpdateImmediately
+ )
+{
+ return I_NetrServerSetServiceBitsEx (
+ ServerName,
+ NULL,
+ TransportName,
+ 0xFFFFFFFF, // All bits are of interest (just overlay the old bits)
+ ServiceBits,
+ UpdateImmediately
+ );
+}
diff --git a/private/net/svcdlls/srvsvc/server/registry.c b/private/net/svcdlls/srvsvc/server/registry.c
new file mode 100644
index 000000000..92ce6ad6e
--- /dev/null
+++ b/private/net/svcdlls/srvsvc/server/registry.c
@@ -0,0 +1,2799 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ registry.c
+
+Abstract:
+
+ This module contains registry _access routines for the NT server
+ service.
+
+Author:
+
+ Chuck Lenzmeier (chuckl) 19-Mar-1992
+
+Revision History:
+
+--*/
+
+#include "srvsvcp.h"
+#include "ssdata.h"
+#include "ssreg.h"
+#include "srvconfg.h"
+
+#include <tstr.h>
+
+#include <netevent.h>
+
+//
+// Simple MIN and MAX macros. Watch out for side effects!
+//
+
+#define MIN(a,b) ( ((a) < (b)) ? (a) : (b) )
+#define MAX(a,b) ( ((a) < (b)) ? (b) : (a) )
+
+#define MAX_INTEGER_STRING 32
+
+#define MB * 1024 * 1024
+#define INF 0xffffffff
+
+//
+// ( u, n )
+// u is units to allocate for every n megabytes on a medium server.
+//
+
+#define CONFIG_TUPLE_SIZE 2
+typedef struct {
+ DWORD initworkitems[CONFIG_TUPLE_SIZE];
+ DWORD maxworkitems[CONFIG_TUPLE_SIZE];
+ DWORD rawworkitems[CONFIG_TUPLE_SIZE];
+ DWORD maxrawworkitems[CONFIG_TUPLE_SIZE];
+ DWORD maxpagedmemoryusage[CONFIG_TUPLE_SIZE];
+ DWORD maxnonpagedmemoryusage[CONFIG_TUPLE_SIZE];
+} CONFIG_SERVER_TABLE;
+
+CONFIG_SERVER_TABLE MedSrvCfgTbl = {
+
+//
+// ** NOTE ** : If the second column is greater than 4, then
+// you will need to add a check to make sure the statistic
+// did not drop to zero.
+//
+// Units / MB
+// Parameter
+// ---------
+//
+/* initworkitems */ { 1 , 4 },
+/* maxworkitems */ { 4 , 1 },
+/* rawworkitems */ { 1 , 4 },
+/* maxrawworkitems */ { 4 , 1 },
+/* maxpagedmemoryusage */ { 1 , 1 },
+/* maxnonpagedmemoryusage */ { 1 , 8 },
+
+};
+
+//
+// Minimum configuration system size is 8MB. Anything lower treated
+// as if 8 MB.
+//
+
+#define MIN_SYSTEM_SIZE 8
+
+//
+// A medium server reaches its max at 32M. A small server at 16M.
+//
+
+#define MAX_SMALL_SIZE 16
+#define MAX_MEDIUM_SIZE 32
+
+//
+// Note that the user limit is always -1 (unlimited). Autodisconnect
+// always defaults to 15 minutes.
+//
+
+//
+// Forward declarations
+//
+
+NTSTATUS
+EnumerateStickyShare (
+ IN PWSTR ValueName,
+ IN ULONG ValueType,
+ IN PVOID ValueData,
+ IN ULONG ValueLength,
+ IN PVOID Context,
+ IN PVOID EntryContext
+ );
+
+NET_API_STATUS
+FillStickyShareInfo(
+ IN PSRVSVC_SHARE_ENUM_INFO ShareEnumInfo,
+ IN PSHARE_INFO_502 Shi502
+ );
+
+NTSTATUS
+GetSdFromRegistry(
+ IN PWSTR ValueName,
+ IN ULONG ValueType,
+ IN PVOID ValueData,
+ IN ULONG ValueLength,
+ IN PVOID Context,
+ IN PVOID EntryContext
+ );
+
+BOOLEAN
+GetStickyShareInfo (
+ IN PWSTR ValueName,
+ IN ULONG ValueType,
+ IN PVOID ValueData,
+ IN PUNICODE_STRING RemarkString,
+ IN PUNICODE_STRING PathString,
+ OUT PSHARE_INFO_502 shi502
+ );
+
+LONG
+LoadParameters (
+ PWCH Path
+ );
+
+LONG
+LoadSizeParameter (
+ VOID
+ );
+
+NTSTATUS
+RecreateStickyShare (
+ IN PWSTR ValueName,
+ IN ULONG ValueType,
+ IN PVOID ValueData,
+ IN ULONG ValueLength,
+ IN PVOID Context,
+ IN PVOID EntryContext
+ );
+
+NTSTATUS
+SaveSdToRegistry(
+ IN PSECURITY_DESCRIPTOR SecurityDescriptor,
+ IN PWSTR ShareName
+ );
+
+NTSTATUS
+SetSizeParameters (
+ IN PWSTR ValueName,
+ IN ULONG ValueType,
+ IN PVOID ValueData,
+ IN ULONG ValueLength,
+ IN PVOID Context,
+ IN PVOID EntryContext
+ );
+
+NTSTATUS
+SetStickyParameter (
+ IN PWSTR ValueName,
+ IN ULONG ValueType,
+ IN PVOID ValueData,
+ IN ULONG ValueLength,
+ IN PVOID Context,
+ IN PVOID EntryContext
+ );
+
+
+ULONG
+SsRtlQueryEnvironmentLength (
+ IN PVOID Environment
+ )
+{
+ PWCH p;
+ ULONG length;
+
+ p = Environment;
+ ASSERT( p != NULL );
+
+ //
+ // The environment variable block consists of zero or more null
+ // terminated ASCII strings. Each string is of the form:
+ //
+ // name=value
+ //
+ // where the null termination is after the value.
+ //
+
+ while ( *p ) {
+ while ( *p ) {
+ p++;
+ }
+ p++;
+ }
+ p++;
+ length = (PCHAR)p - (PCHAR)Environment;
+
+ //
+ // Return accumulated length.
+ //
+
+ return length;
+}
+
+
+VOID
+SsAddParameterToRegistry (
+ PFIELD_DESCRIPTOR Field,
+ PVOID Value
+ )
+{
+ NTSTATUS status;
+ PWCH valueName;
+ DWORD valueType;
+ LPBYTE valuePtr;
+ DWORD valueDataLength;
+
+ //
+ // The value name is the parameter name and the value data is the
+ // parameter value.
+ //
+
+ valueName = Field->FieldName;
+
+ switch ( Field->FieldType ) {
+
+ case BOOLEAN_FIELD:
+ case DWORD_FIELD:
+ valueType = REG_DWORD;
+ valuePtr = Value;
+ valueDataLength = sizeof(DWORD);
+ break;
+
+ case LPSTR_FIELD:
+ valueType = REG_SZ;
+ valuePtr = *(LPBYTE *)Value;
+ if ( valuePtr != NULL ) {
+ valueDataLength = SIZE_WSTR( (PWCH)valuePtr );
+ } else {
+ valueDataLength = 0;
+ }
+ break;
+
+ }
+
+ //
+ // Set the value into the Parameters key.
+ //
+
+ status = RtlWriteRegistryValue(
+ RTL_REGISTRY_SERVICES,
+ PARAMETERS_REGISTRY_PATH,
+ valueName,
+ valueType,
+ valuePtr,
+ valueDataLength
+ );
+ if ( !NT_SUCCESS(status) ) {
+ IF_DEBUG(REGISTRY) {
+ SS_PRINT(( "SsAddParameterToRegistry: SetValue failed: %lx; "
+ "parameter %ws won't stick\n", status, valueName ));
+ }
+ }
+
+ return;
+
+} // SsAddParameterToRegistry
+
+
+VOID
+SsAddShareToRegistry (
+ IN PSHARE_INFO_2 ShareInfo2,
+ IN PSECURITY_DESCRIPTOR SecurityDescriptor OPTIONAL
+ )
+{
+ NTSTATUS status;
+ PWCH valueName;
+ PVOID environment;
+ UNICODE_STRING nameString;
+ UNICODE_STRING valueString;
+ WCHAR integerString[MAX_INTEGER_STRING + 1];
+ ULONG environmentLength;
+
+ //
+ // Build the value name and data strings. The value name is the
+ // share name (netname), while the value data is share information
+ // in REG_MULTI_SZ format. To build the value data, we use the
+ // RTL environment routines.
+ //
+
+ valueName = ShareInfo2->shi2_netname;
+
+ status = RtlCreateEnvironment( FALSE, &environment );
+ if ( !NT_SUCCESS(status) ) {
+ IF_DEBUG(REGISTRY) {
+ SS_PRINT(( "SsAddShareToRegistry: CreateEnvironment failed: %lx; "
+ "share %ws won't stick\n", status, valueName ));
+ }
+ goto exit1;
+ }
+
+ RtlInitUnicodeString( &nameString, PATH_VARIABLE_NAME );
+ RtlInitUnicodeString( &valueString, ShareInfo2->shi2_path );
+
+ status = RtlSetEnvironmentVariable(
+ &environment,
+ &nameString,
+ &valueString
+ );
+ if ( !NT_SUCCESS(status) ) {
+ IF_DEBUG(REGISTRY) {
+ SS_PRINT(( "SsAddShareToRegistry: SetEnvironment failed: %lx; "
+ "share %s won't stick\n", status, valueName ));
+ }
+ goto exit2;
+ }
+
+ if ( ShareInfo2->shi2_remark != NULL ) {
+
+ RtlInitUnicodeString( &nameString, REMARK_VARIABLE_NAME );
+ RtlInitUnicodeString( &valueString, ShareInfo2->shi2_remark );
+
+ status = RtlSetEnvironmentVariable(
+ &environment,
+ &nameString,
+ &valueString
+ );
+ if ( !NT_SUCCESS(status) ) {
+ IF_DEBUG(REGISTRY) {
+ SS_PRINT(( "SsAddShareToRegistry: SetEnvironment failed: %lx; "
+ "share %s won't stick\n", status, valueName ));
+ }
+ goto exit2;
+ }
+
+ }
+
+ RtlInitUnicodeString( &nameString, TYPE_VARIABLE_NAME );
+ valueString.Buffer = integerString;
+ valueString.MaximumLength = (MAX_INTEGER_STRING + 1) * sizeof(WCHAR);
+ status = RtlIntegerToUnicodeString(
+ ShareInfo2->shi2_type,
+ 10,
+ &valueString
+ );
+ if ( !NT_SUCCESS(status) ) {
+ IF_DEBUG(REGISTRY) {
+ SS_PRINT(( "SsAddShareToRegistry: IntegerToUnicode failed: %lx; "
+ "share %ws won't stick\n", status, valueName ));
+ }
+ goto exit2;
+ }
+ status = RtlSetEnvironmentVariable(
+ &environment,
+ &nameString,
+ &valueString
+ );
+ if ( !NT_SUCCESS(status) ) {
+ IF_DEBUG(REGISTRY) {
+ SS_PRINT(( "SsAddShareToRegistry: SetEnvironment failed: %lx; "
+ "share %s won't stick\n", status, valueName ));
+ }
+ goto exit2;
+ }
+
+ RtlInitUnicodeString( &nameString, PERMISSIONS_VARIABLE_NAME );
+ valueString.Buffer = integerString;
+ valueString.MaximumLength = (MAX_INTEGER_STRING + 1) * sizeof(WCHAR);
+ status = RtlIntegerToUnicodeString(
+ ShareInfo2->shi2_permissions,
+ 10,
+ &valueString
+ );
+ if ( !NT_SUCCESS(status) ) {
+ IF_DEBUG(REGISTRY) {
+ SS_PRINT(( "SsAddShareToRegistry: IntegerToUnicode failed: %lx; "
+ "share %ws won't stick\n", status, valueName ));
+ }
+ goto exit2;
+ }
+ status = RtlSetEnvironmentVariable(
+ &environment,
+ &nameString,
+ &valueString
+ );
+ if ( !NT_SUCCESS(status) ) {
+ IF_DEBUG(REGISTRY) {
+ SS_PRINT(( "SsAddShareToRegistry: SetEnvironment failed: %lx; "
+ "share %s won't stick\n", status, valueName ));
+ }
+ goto exit2;
+ }
+
+ RtlInitUnicodeString( &nameString, MAXUSES_VARIABLE_NAME );
+ valueString.Buffer = integerString;
+ valueString.MaximumLength = (MAX_INTEGER_STRING + 1) * sizeof(WCHAR);
+ status = RtlIntegerToUnicodeString(
+ ShareInfo2->shi2_max_uses,
+ 10,
+ &valueString
+ );
+ if ( !NT_SUCCESS(status) ) {
+ IF_DEBUG(REGISTRY) {
+ SS_PRINT(( "SsAddShareToRegistry: IntegerToUnicode failed: %lx; "
+ "share %ws won't stick\n", status, valueName ));
+ }
+ goto exit2;
+ }
+ status = RtlSetEnvironmentVariable(
+ &environment,
+ &nameString,
+ &valueString
+ );
+ if ( !NT_SUCCESS(status) ) {
+ IF_DEBUG(REGISTRY) {
+ SS_PRINT(( "SsAddShareToRegistry: SetEnvironment failed: %lx; "
+ "share %s won't stick\n", status, valueName ));
+ }
+ goto exit2;
+ }
+
+ //
+ // Set the value into the Shares key.
+ //
+
+ environmentLength = SsRtlQueryEnvironmentLength( environment );
+ status = RtlWriteRegistryValue(
+ RTL_REGISTRY_SERVICES,
+ SHARES_REGISTRY_PATH,
+ valueName,
+ REG_MULTI_SZ,
+ (LPBYTE)environment,
+ environmentLength
+ );
+ if ( !NT_SUCCESS(status) ) {
+ IF_DEBUG(REGISTRY) {
+ SS_PRINT(( "SsAddShareToRegistry: SetValue failed: %lx; share %ws "
+ "won't stick\n", status, valueName ));
+ }
+ }
+
+ //
+ // Save the file security descriptor
+ //
+
+ if ( ARGUMENT_PRESENT( SecurityDescriptor ) ) {
+
+ status = SaveSdToRegistry(
+ SecurityDescriptor,
+ valueName
+ );
+
+ if ( !NT_SUCCESS(status) ) {
+ IF_DEBUG(REGISTRY) {
+ SS_PRINT(( "SsAddShareToRegistry: SaveSd failed: %lx; share %ws\n"
+ , status, valueName ));
+ }
+ }
+ }
+
+exit2:
+ RtlDestroyEnvironment( environment );
+
+exit1:
+
+ return;
+
+} // SsAddShareToRegistry
+
+
+NET_API_STATUS
+SsBindToTransports (
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ Reads the registry to bind to specified transports.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NET_API_STATUS - success/failure of the operation.
+
+--*/
+
+{
+ NTSTATUS status;
+ ULONG numberOfBindings = 0;
+ RTL_QUERY_REGISTRY_TABLE queryTable[2];
+
+ //
+ // Ask the RTL to call us back for each subvalue in the MULTI_SZ
+ // value \LanmanServer\Linkage\Bind.
+ //
+
+ queryTable[0].QueryRoutine = BindToTransport;
+ queryTable[0].Flags = 0;
+ queryTable[0].Name = BIND_VALUE_NAME;
+ queryTable[0].EntryContext = NULL;
+ queryTable[0].DefaultType = REG_NONE;
+ queryTable[0].DefaultData = NULL;
+ queryTable[0].DefaultLength = 0;
+
+ queryTable[1].QueryRoutine = NULL;
+ queryTable[1].Flags = 0;
+ queryTable[1].Name = NULL;
+
+ status = RtlQueryRegistryValues(
+ RTL_REGISTRY_SERVICES,
+ LINKAGE_REGISTRY_PATH,
+ queryTable,
+ &numberOfBindings,
+ NULL
+ );
+
+ //
+ // If the above failed to bind to any transports, fail to start the
+ // server.
+ //
+
+ if ( !NT_SUCCESS(status) ) {
+ IF_DEBUG(INITIALIZATION) {
+ SS_PRINT(( "SsBindToTransports: RtlQueryRegistryValues failed: "
+ "%lx\n", status ));
+ }
+ return RtlNtStatusToDosError( status );
+ }
+
+ if ( numberOfBindings == 0 ) {
+
+ SsLogEvent(
+ EVENT_SRV_NO_TRANSPORTS_BOUND,
+ 0,
+ NULL,
+ NO_ERROR
+ );
+
+ IF_DEBUG(INITIALIZATION) {
+ SS_PRINT(( "SsBindToTransports: no bindings created\n" ));
+ }
+ return ERROR_INVALID_PARAMETER; // !!! Need better error
+ }
+
+ //
+ // See if there any optional bindings we should perform
+ //
+ queryTable[0].QueryRoutine = BindOptionalNames;
+ queryTable[0].Flags = 0;
+ queryTable[0].Name = BIND_VALUE_NAME;
+ queryTable[0].EntryContext = NULL;
+ queryTable[0].DefaultType = REG_NONE;
+ queryTable[0].DefaultData = NULL;
+ queryTable[0].DefaultLength = 0;
+
+ queryTable[1].QueryRoutine = NULL;
+ queryTable[1].Flags = 0;
+ queryTable[1].Name = NULL;
+
+ (void)RtlQueryRegistryValues(
+ RTL_REGISTRY_SERVICES,
+ LINKAGE_REGISTRY_PATH,
+ queryTable,
+ NULL,
+ NULL
+ );
+
+ return NO_ERROR;
+}
+
+NET_API_STATUS
+SsCheckRegistry (
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ This function verifies that the keys used by the server exist.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NET_API_STATUS - success/failure of the operation.
+
+--*/
+
+{
+ NTSTATUS status;
+ LPWSTR subStrings[1];
+
+ //
+ // Verify the existence of the main server service key. If this
+ // fails, the server service fails to start.
+ //
+
+ status = RtlCheckRegistryKey(
+ RTL_REGISTRY_SERVICES,
+ SERVER_REGISTRY_PATH
+ );
+
+ if ( !NT_SUCCESS(status) ) {
+
+ subStrings[0] = SERVER_REGISTRY_PATH;
+ SsLogEvent(
+ EVENT_SRV_KEY_NOT_FOUND,
+ 1,
+ subStrings,
+ RtlNtStatusToDosError( status )
+ );
+
+ IF_DEBUG(INITIALIZATION) {
+ SS_PRINT(( "SsCheckRegistry: main key doesn't exist\n" ));
+ }
+ return ERROR_INVALID_PARAMETER; // !!! Need better error
+
+ }
+
+ //
+ // Verify the existence of the Linkage subkey. If this fails, the
+ // server service fails to start.
+ //
+
+ status = RtlCheckRegistryKey(
+ RTL_REGISTRY_SERVICES,
+ LINKAGE_REGISTRY_PATH
+ );
+
+ if ( !NT_SUCCESS(status) ) {
+
+ subStrings[0] = LINKAGE_REGISTRY_PATH;
+ SsLogEvent(
+ EVENT_SRV_KEY_NOT_FOUND,
+ 1,
+ subStrings,
+ RtlNtStatusToDosError( status )
+ );
+
+ IF_DEBUG(INITIALIZATION) {
+ SS_PRINT(( "SsCheckRegistry: Linkage subkey doesn't exist\n" ));
+ }
+ return ERROR_INVALID_PARAMETER; // !!! Need better error
+
+ }
+
+ //
+ // If the Parameters subkey doesn't exist, create it. If it can't
+ // be created, fail to start the server.
+ //
+
+ status = RtlCheckRegistryKey(
+ RTL_REGISTRY_SERVICES,
+ PARAMETERS_REGISTRY_PATH
+ );
+
+ if ( !NT_SUCCESS(status) ) {
+
+ status = RtlCreateRegistryKey(
+ RTL_REGISTRY_SERVICES,
+ PARAMETERS_REGISTRY_PATH
+ );
+
+ if ( !NT_SUCCESS(status) ) {
+
+ subStrings[0] = PARAMETERS_REGISTRY_PATH;
+ SsLogEvent(
+ EVENT_SRV_KEY_NOT_CREATED,
+ 1,
+ subStrings,
+ RtlNtStatusToDosError( status )
+ );
+
+ IF_DEBUG(INITIALIZATION) {
+ SS_PRINT(( "SsCheckRegistry: Can't create Parameters subkey: "
+ "%lx\n", status ));
+ }
+ return RtlNtStatusToDosError( status );
+
+ }
+
+ }
+
+ //
+ // If the AutotunedParameters subkey doesn't exist, create it. If
+ // it can't be created, fail to start the server.
+ //
+
+ status = RtlCheckRegistryKey(
+ RTL_REGISTRY_SERVICES,
+ AUTOTUNED_REGISTRY_PATH
+ );
+
+ if ( !NT_SUCCESS(status) ) {
+
+ status = RtlCreateRegistryKey(
+ RTL_REGISTRY_SERVICES,
+ AUTOTUNED_REGISTRY_PATH
+ );
+
+ if ( !NT_SUCCESS(status) ) {
+
+ subStrings[0] = AUTOTUNED_REGISTRY_PATH;
+ SsLogEvent(
+ EVENT_SRV_KEY_NOT_CREATED,
+ 1,
+ subStrings,
+ RtlNtStatusToDosError( status )
+ );
+
+ IF_DEBUG(INITIALIZATION) {
+ SS_PRINT(( "SsCheckRegistry: Can't create AutotunedParameters "
+ "subkey: %lx\n", status ));
+ }
+ return RtlNtStatusToDosError( status );
+
+ }
+
+ }
+
+ //
+ // If the Shares subkey doesn't exist, create it. If it can't be
+ // created, fail to start the server.
+ //
+
+ status = RtlCheckRegistryKey(
+ RTL_REGISTRY_SERVICES,
+ SHARES_REGISTRY_PATH
+ );
+
+ if ( !NT_SUCCESS(status) ) {
+
+ status = RtlCreateRegistryKey(
+ RTL_REGISTRY_SERVICES,
+ SHARES_REGISTRY_PATH
+ );
+
+ if ( !NT_SUCCESS(status) ) {
+
+ subStrings[0] = SHARES_REGISTRY_PATH;
+ SsLogEvent(
+ EVENT_SRV_KEY_NOT_CREATED,
+ 1,
+ subStrings,
+ RtlNtStatusToDosError( status )
+ );
+
+ IF_DEBUG(INITIALIZATION) {
+ SS_PRINT(( "SsCheckRegistry: Can't create Shares subkey: "
+ "%lx\n", status ));
+ }
+ return RtlNtStatusToDosError( status );
+
+ }
+
+ }
+
+ //
+ // If the Shares Security subkey doesn't exist, create it. If it
+ // can't be created, fail to start the server.
+ //
+
+ status = RtlCheckRegistryKey(
+ RTL_REGISTRY_SERVICES,
+ SHARES_SECURITY_REGISTRY_PATH
+ );
+
+ if ( !NT_SUCCESS(status) ) {
+
+ status = RtlCreateRegistryKey(
+ RTL_REGISTRY_SERVICES,
+ SHARES_SECURITY_REGISTRY_PATH
+ );
+
+ if ( !NT_SUCCESS(status) ) {
+
+ subStrings[0] = SHARES_SECURITY_REGISTRY_PATH;
+ SsLogEvent(
+ EVENT_SRV_KEY_NOT_CREATED,
+ 1,
+ subStrings,
+ RtlNtStatusToDosError( status )
+ );
+
+ IF_DEBUG(INITIALIZATION) {
+ SS_PRINT(( "SsCheckRegistry: Can't create Shares Security subkey: "
+ "%lx\n", status ));
+ }
+ return RtlNtStatusToDosError( status );
+
+ }
+
+ }
+
+ //
+ // All keys successfully checked.
+ //
+
+ return NO_ERROR;
+
+} // SsCheckRegistry
+
+
+NET_API_STATUS
+SsEnumerateStickyShares (
+ IN OUT PSRVSVC_SHARE_ENUM_INFO ShareEnumInfo
+ )
+
+/*++
+
+Routine Description:
+
+ Reads the registry to find and return sticky shares.
+
+Arguments:
+
+ ShareEnumInfo - points to a structure that contains the parameters
+ to the NetShareEnumSticky call.
+
+Return Value:
+
+ NET_API_STATUS - success/failure of the operation.
+
+--*/
+
+{
+ NTSTATUS status;
+ PRTL_QUERY_REGISTRY_TABLE queryTable;
+
+ ShareEnumInfo->TotalBytesNeeded = 0;
+ ShareEnumInfo->TotalEntries = 0;
+ ShareEnumInfo->EntriesRead = 0;
+
+ //
+ // Initialize the reserve fields. This tells the callback routine,
+ // how many times it has been called.
+ //
+
+ ShareEnumInfo->ShareEnumIndex = 0;
+ ShareEnumInfo->StartOfFixedData = (PCHAR)ShareEnumInfo->OutputBuffer;
+ ShareEnumInfo->EndOfVariableData = (PCHAR)ShareEnumInfo->OutputBuffer +
+ ShareEnumInfo->OutputBufferLength;
+
+ //
+ // We need to align it since we deal with unicode strings.
+ //
+
+ ShareEnumInfo->EndOfVariableData =
+ (PCHAR)((ULONG)ShareEnumInfo->EndOfVariableData & ~1);
+
+ //
+ // Ask the RTL to call us back for each value in the Shares key.
+ //
+
+ queryTable = MIDL_user_allocate( sizeof(RTL_QUERY_REGISTRY_TABLE) * 2 );
+
+ if ( queryTable != NULL ) {
+
+ queryTable[0].QueryRoutine = EnumerateStickyShare;
+ queryTable[0].Flags = RTL_QUERY_REGISTRY_NOEXPAND;
+ queryTable[0].Name = NULL;
+ queryTable[0].EntryContext = NULL;
+ queryTable[0].DefaultType = REG_NONE;
+ queryTable[0].DefaultData = NULL;
+ queryTable[0].DefaultLength = 0;
+
+ queryTable[1].QueryRoutine = NULL;
+ queryTable[1].Flags = 0;
+ queryTable[1].Name = NULL;
+
+ status = RtlQueryRegistryValues(
+ RTL_REGISTRY_SERVICES | RTL_REGISTRY_OPTIONAL,
+ SHARES_REGISTRY_PATH,
+ queryTable,
+ ShareEnumInfo,
+ NULL
+ );
+
+ MIDL_user_free( queryTable );
+
+ } else {
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ if ( !NT_SUCCESS(status) ) {
+ IF_DEBUG(INITIALIZATION) {
+ SS_PRINT(( "SsEnumerateStickyShares: RtlQueryRegistryValues "
+ "failed: %lx\n", status ));
+ }
+ return RtlNtStatusToDosError( status );
+ }
+
+ return NO_ERROR;
+
+} // SsEnumerateStickyShares
+
+
+NET_API_STATUS
+SsLoadConfigurationParameters (
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ Reads the registry to get server configuration parameters. These
+ server parameters must be set before the server FSP has been
+ started.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NET_API_STATUS - success/failure of the operation.
+
+--*/
+
+{
+ LONG error;
+
+ //
+ // Get the basic Size parameter, then load autotuned parameters,
+ // then load manually set parameters. This ordering allows manual
+ // settings to override autotuning.
+ //
+
+ error = LoadSizeParameter( );
+
+ if ( error == NO_ERROR ) {
+
+ error = LoadParameters( AUTOTUNED_REGISTRY_PATH );
+
+ if ( error == NO_ERROR ) {
+
+ error = LoadParameters( PARAMETERS_REGISTRY_PATH );
+
+ }
+
+ }
+
+ //
+ // The copy read to MDL read switchover must occur at or below the
+ // SMB buffer size.
+ //
+
+ SsData.ServerInfo598.sv598_mdlreadswitchover =
+ MIN(
+ SsData.ServerInfo598.sv598_mdlreadswitchover,
+ SsData.ServerInfo599.sv599_sizreqbuf);
+
+ //
+ // Override parameters that cannot be set on WinNT (vs. NTAS).
+ //
+ // The server itself also performs most of these overrides, in case
+ // somebody figures out the FSCTL that changes parameters. We also
+ // override in the service in order to keep the service's view
+ // consistent with the server's. If you make any changes here, also
+ // make them in srv\svcsrv.c.
+ //
+ if ( SsData.ServerInfo598.sv598_producttype == NtProductWinNt ) {
+
+ //
+ // On WinNT, the maximum value of certain parameters is fixed at
+ // build time. These include: concurrent users, SMB buffers,
+ // and threads.
+ //
+
+#define MINIMIZE(_param,_max) _param = MIN( _param, _max );
+
+ MINIMIZE( SsData.ServerInfo102.sv102_users, MAX_USERS_WKSTA );
+ MINIMIZE( SsData.ServerInfo599.sv599_maxworkitems, MAX_MAXWORKITEMS_WKSTA );
+ MINIMIZE( SsData.ServerInfo598.sv598_maxthreadsperqueue, MAX_THREADS_WKSTA );
+
+ //
+ // On WinNT, we do not cache closed RFCBs.
+ //
+
+ SsData.ServerInfo598.sv598_cachedopenlimit = 0;
+
+ //
+ // Sharing of redirected drives is not allowed on WinNT.
+ //
+
+ SsData.ServerInfo599.sv599_enablesharednetdrives = FALSE;
+
+ }
+
+ return error;
+
+} // SsLoadConfigurationParameters
+
+
+NET_API_STATUS
+SsRecreateStickyShares (
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ Reads the registry to find and create sticky shares.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NET_API_STATUS - success/failure of the operation.
+
+--*/
+
+{
+ NTSTATUS status;
+ PRTL_QUERY_REGISTRY_TABLE queryTable;
+ ULONG IterationCount = 0;
+
+ //
+ // Ask the RTL to call us back for each value in the Shares key.
+ //
+
+ queryTable = MIDL_user_allocate( sizeof(RTL_QUERY_REGISTRY_TABLE) * 2 );
+
+ if ( queryTable != NULL ) {
+
+ queryTable[0].QueryRoutine = RecreateStickyShare;
+ queryTable[0].Flags = RTL_QUERY_REGISTRY_NOEXPAND;
+ queryTable[0].Name = NULL;
+ queryTable[0].EntryContext = NULL;
+ queryTable[0].DefaultType = REG_NONE;
+ queryTable[0].DefaultData = NULL;
+ queryTable[0].DefaultLength = 0;
+
+ queryTable[1].QueryRoutine = NULL;
+ queryTable[1].Flags = 0;
+ queryTable[1].Name = NULL;
+
+ status = RtlQueryRegistryValues(
+ RTL_REGISTRY_SERVICES | RTL_REGISTRY_OPTIONAL,
+ SHARES_REGISTRY_PATH,
+ queryTable,
+ &IterationCount,
+ NULL
+ );
+
+ MIDL_user_free( queryTable );
+
+ } else {
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ if ( !NT_SUCCESS(status) ) {
+ IF_DEBUG(INITIALIZATION) {
+ SS_PRINT(( "SsRecreateStickyShares: RtlQueryRegistryValues "
+ "failed: %lx\n", status ));
+ }
+ return RtlNtStatusToDosError( status );
+ }
+
+ return NO_ERROR;
+
+} // SsRecreateStickyShares
+
+
+NET_API_STATUS
+SsRemoveShareFromRegistry (
+ LPWSTR NetName
+ )
+{
+ NET_API_STATUS error = NO_ERROR;
+ NTSTATUS status;
+ PWCH valueName;
+
+ //
+ // The value name is the share name. Remove that value from the
+ // Shares key.
+ //
+
+ valueName = NetName;
+
+ //
+ // Delete the share security
+ //
+
+ status = RtlDeleteRegistryValue(
+ RTL_REGISTRY_SERVICES,
+ SHARES_SECURITY_REGISTRY_PATH,
+ valueName
+ );
+
+ if ( !NT_SUCCESS(status) ) {
+ IF_DEBUG(REGISTRY) {
+ SS_PRINT(( "SsRemoveShareFromRegistry: Delete Security value failed: %lx; "
+ "share %ws will return\n", status, valueName ));
+ }
+ }
+
+ //
+ // Delete the share
+ //
+
+ status = RtlDeleteRegistryValue(
+ RTL_REGISTRY_SERVICES,
+ SHARES_REGISTRY_PATH,
+ valueName
+ );
+ if ( !NT_SUCCESS(status) ) {
+ IF_DEBUG(REGISTRY) {
+ SS_PRINT(( "SsRemoveShareFromRegistry: DeleteValue failed: %lx; "
+ "share %ws will return\n", status, valueName ));
+ }
+
+ error = RtlNtStatusToDosError( status );
+ }
+
+ return error;
+
+} // SsRemoveShareFromRegistry
+
+
+NTSTATUS
+BindToTransport (
+ IN PWSTR ValueName,
+ IN ULONG ValueType,
+ IN PVOID ValueData,
+ IN ULONG ValueLength,
+ IN PVOID Context,
+ IN PVOID EntryContext
+ )
+{
+ NTSTATUS status;
+ NET_API_STATUS error;
+ PULONG numberOfBindings = Context;
+ SERVER_TRANSPORT_INFO_0 svti0;
+ LPWSTR subStrings[2];
+
+ ValueName, ValueLength, EntryContext;
+
+ //
+ // The value type must be REG_SZ (translated from REG_MULTI_SZ by
+ // the RTL).
+ //
+
+ if ( ValueType != REG_SZ ) {
+
+ subStrings[0] = ValueName;
+ subStrings[1] = LINKAGE_REGISTRY_PATH;
+ SsLogEvent(
+ EVENT_SRV_INVALID_REGISTRY_VALUE,
+ 2,
+ subStrings,
+ NO_ERROR
+ );
+
+ IF_DEBUG(REGISTRY) {
+ SS_PRINT(( "BindToTransports: skipping invalid value %ws\n",
+ ValueName ));
+ }
+ return STATUS_SUCCESS;
+
+ }
+
+ //
+ // The value data is the name of the transport device object.
+ //
+
+ RtlZeroMemory( &svti0, sizeof( svti0 ) );
+ svti0.svti0_transportname = ValueData;
+ svti0.svti0_transportaddress = SsServerTransportAddress;
+ svti0.svti0_transportaddresslength =
+ ComputeTransportAddressClippedLength(
+ SsServerTransportAddress,
+ SsServerTransportAddressLength );
+
+ //
+ // Bind to the transport.
+ //
+
+ IF_DEBUG(INITIALIZATION) {
+ SS_PRINT(( "SsBindToTransports: binding to transport %ws\n",
+ ValueData ));
+ }
+
+ AnnounceServiceStatus( 1 );
+
+ error = I_NetrServerTransportAddEx( 0, (LPTRANSPORT_INFO)&svti0 );
+
+ if ( error != NO_ERROR ) {
+
+ DWORD eventId;
+
+ //
+ // In general, we do not fail server startup just because we
+ // failed to bind to a single transport. Instead, we keep a
+ // count of the number of bindings we have, and in
+ // SsBindToTransports, after attempting all transports listed in
+ // the registry, only if no bindings were made do we fail server
+ // startup.
+ //
+ // The exception to the rule is that we do not start the server
+ // service if we get a duplicate name error for any transport.
+ // This is considered to be a "user error".
+ //
+
+ IF_DEBUG(INITIALIZATION_ERRORS) {
+ SS_PRINT(( "SsBindToTransports: failed to bind to %ws: "
+ "%ld\n", ValueData, error ));
+ }
+
+ eventId = EVENT_SRV_CANT_BIND_TO_TRANSPORT;
+ status = STATUS_SUCCESS; // assume not duplicate name
+
+ if ( error == ERROR_DUP_NAME ) {
+ eventId = EVENT_SRV_CANT_BIND_DUP_NAME;
+ status = STATUS_DUPLICATE_NAME;
+ *numberOfBindings = 0;
+ }
+
+ subStrings[0] = ValueData;
+ SsLogEvent(
+ eventId,
+ 1,
+ subStrings,
+ error
+ );
+
+ return status;
+
+ }
+
+ (*numberOfBindings)++;
+ return STATUS_SUCCESS;
+
+} // BindToTransport
+
+NTSTATUS
+BindNameToTransport (
+ IN PWSTR ValueName,
+ IN ULONG ValueType,
+ IN PVOID ValueData,
+ IN ULONG ValueLength,
+ IN PVOID Context,
+ IN PVOID EntryContext
+ )
+{
+ SERVER_TRANSPORT_INFO_0 sti;
+ UCHAR serverName[ MAX_PATH ];
+ UNICODE_STRING UnicodeName;
+ NET_API_STATUS error;
+ LPWSTR subStrings[2];
+ ULONG namelen;
+
+ subStrings[0] = (LPWSTR)ValueData;
+ subStrings[1] = OPTIONAL_NAMES_VALUE_NAME;
+
+ if( ValueType != REG_SZ ) {
+
+ //
+ // Not a string!
+ //
+
+ SsLogEvent(
+ EVENT_SRV_INVALID_REGISTRY_VALUE,
+ 2,
+ subStrings,
+ NO_ERROR
+ );
+
+ return STATUS_SUCCESS;
+ }
+
+ UnicodeName.Length = wcslen( (LPWSTR)ValueData ) * sizeof( WCHAR );
+ UnicodeName.MaximumLength = UnicodeName.Length + sizeof( WCHAR );
+ UnicodeName.Buffer = (LPWSTR)ValueData;
+
+ error = ConvertStringToTransportAddress( &UnicodeName, serverName, &namelen );
+
+ if( error != NO_ERROR ) {
+
+ //
+ // Invalid server name!
+ //
+
+ SsLogEvent(
+ EVENT_SRV_INVALID_REGISTRY_VALUE,
+ 2,
+ subStrings,
+ error
+ );
+
+ return STATUS_SUCCESS;
+ }
+
+ RtlZeroMemory( &sti, sizeof(sti) );
+ sti.svti0_transportname = (LPWSTR)Context;
+ sti.svti0_transportaddress = serverName;
+ sti.svti0_transportaddresslength = namelen;
+
+ //
+ // Adding the transport make take some time. Better announce our status
+ //
+ AnnounceServiceStatus( 1 );
+
+ error = I_NetrServerTransportAddEx( 0, (LPTRANSPORT_INFO)&sti );
+
+ if ( error != NO_ERROR ) {
+
+ //
+ // Could not register the name!
+ //
+
+ subStrings[0] = (LPWSTR)ValueData;
+ subStrings[1] = OPTIONAL_NAMES_VALUE_NAME;
+
+ SsLogEvent(
+ EVENT_SRV_INVALID_REGISTRY_VALUE,
+ 2,
+ subStrings,
+ error
+ );
+ }
+
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS
+BindOptionalNames (
+ IN PWSTR ValueName,
+ IN ULONG ValueType,
+ IN PVOID ValueData,
+ IN ULONG ValueLength,
+ IN PVOID Context,
+ IN PVOID EntryContext
+ )
+{
+ RTL_QUERY_REGISTRY_TABLE queryTable[2];
+ NTSTATUS status;
+
+ ValueName, ValueLength, EntryContext;
+
+ //
+ // The value type must be REG_SZ (translated from REG_MULTI_SZ by
+ // the RTL).
+ //
+ if ( ValueType != REG_SZ ) {
+ //
+ // This error was already logged in BindToTransport
+ //
+ return STATUS_SUCCESS;
+ }
+
+ //
+ // The value data is the name of the transport device object. Now
+ // we need to iterate over the optional names and bind them to this
+ // transport.
+ //
+
+ //
+ // Now see if there any optional bindings we should perform
+ //
+ queryTable[0].QueryRoutine = BindNameToTransport;
+ queryTable[0].Flags = 0;
+ queryTable[0].Name = OPTIONAL_NAMES_VALUE_NAME;
+ queryTable[0].EntryContext = NULL;
+ queryTable[0].DefaultType = REG_NONE;
+ queryTable[0].DefaultData = NULL;
+ queryTable[0].DefaultLength = 0;
+
+ queryTable[1].QueryRoutine = NULL;
+ queryTable[1].Flags = 0;
+ queryTable[1].Name = NULL;
+
+
+ (void)RtlQueryRegistryValues(
+ RTL_REGISTRY_SERVICES | RTL_REGISTRY_OPTIONAL,
+ PARAMETERS_REGISTRY_PATH,
+ queryTable,
+ ValueData, // pass the transport name on down
+ NULL
+ );
+
+ return STATUS_SUCCESS;
+
+} // BindToTransport
+
+NTSTATUS
+EnumerateStickyShare (
+ IN PWSTR ValueName,
+ IN ULONG ValueType,
+ IN PVOID ValueData,
+ IN ULONG ValueLength,
+ IN PVOID Context,
+ IN PVOID EntryContext
+ )
+
+/*++
+
+Routine Description:
+
+ Callback routine for SsEnumerateStickyShare. Routine will get information
+ on share and fill in the output buffer.
+
+Arguments:
+
+ ValueName - Name of the share
+ ValueType - Value type of the share name.
+ ValueData - Data associated with the ValueName.
+ Context - Pointer to our enum information structure.
+
+Return Value:
+
+ NET_API_STATUS - success/failure of the operation.
+
+--*/
+{
+
+ NET_API_STATUS error;
+ SHARE_INFO_502 shi502;
+ UNICODE_STRING pathString;
+ UNICODE_STRING remarkString;
+ PSRVSVC_SHARE_ENUM_INFO enumInfo = (PSRVSVC_SHARE_ENUM_INFO) Context;
+
+ ValueLength, EntryContext;
+
+ remarkString.Buffer = NULL;
+ pathString.Buffer = NULL;
+
+ if ( GetStickyShareInfo(
+ ValueName,
+ ValueType,
+ ValueData,
+ &remarkString,
+ &pathString,
+ &shi502
+ ) ) {
+
+ //
+ // Do the actual add of the share.
+ //
+
+ IF_DEBUG(REGISTRY) {
+ SS_PRINT(( "EnumerateStickyShares: adding share %ws\n", ValueName ));
+ }
+
+ shi502.shi502_remark = remarkString.Buffer;
+ shi502.shi502_path = pathString.Buffer;
+
+ //
+ // Skip until we have the right share to resume from
+ //
+
+ if ( (enumInfo->TotalEntries == 0) &&
+ (enumInfo->ShareEnumIndex < enumInfo->ResumeHandle) ) {
+
+ enumInfo->ShareEnumIndex++;
+
+ } else {
+
+ enumInfo->TotalEntries++;
+ error = FillStickyShareInfo( enumInfo, &shi502 );
+
+ if ( error != NO_ERROR ) {
+
+ IF_DEBUG(REGISTRY) {
+ SS_PRINT(( "EnumerateStickyShares: failed to add share "
+ "%ws = %wZ: %ld\n", ValueName, &pathString, error ));
+ }
+ } else {
+ enumInfo->EntriesRead++;
+ enumInfo->ResumeHandle++;
+ }
+ }
+
+ //
+ // free buffers allocated by GetStickyShareInfo
+ //
+
+ if ( remarkString.Buffer != NULL ) {
+ RtlFreeUnicodeString( &remarkString );
+ }
+
+ if ( pathString.Buffer != NULL ) {
+ RtlFreeUnicodeString( &pathString );
+ }
+
+ if ( shi502.shi502_security_descriptor != NULL ) {
+ MIDL_user_free( shi502.shi502_security_descriptor );
+ }
+
+ }
+
+ return STATUS_SUCCESS;
+
+} // EnumerateStickyShare
+
+
+NTSTATUS
+GetSdFromRegistry(
+ IN PWSTR ValueName,
+ IN ULONG ValueType,
+ IN PVOID ValueData,
+ IN ULONG ValueLength,
+ IN PVOID Context,
+ IN PVOID EntryContext
+ )
+
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ PSECURITY_DESCRIPTOR fileSD = NULL;
+ PSHARE_INFO_502 shi502 = (PSHARE_INFO_502) Context;
+ LPWSTR subStrings[1];
+
+ EntryContext, ValueName, ValueType;
+
+ if ( ValueLength > 0 ) {
+
+ fileSD = MIDL_user_allocate( ValueLength );
+
+ if ( fileSD == NULL) {
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ } else {
+
+ RtlCopyMemory(
+ fileSD,
+ ValueData,
+ ValueLength
+ );
+
+ if ( !RtlValidSecurityDescriptor( fileSD ) ) {
+
+ subStrings[0] = ValueName;
+ SsLogEvent(
+ EVENT_SRV_INVALID_SD,
+ 1,
+ subStrings,
+ RtlNtStatusToDosError( status )
+ );
+
+ MIDL_user_free( fileSD );
+ status = STATUS_INVALID_SECURITY_DESCR;
+ }
+ }
+ }
+
+ shi502->shi502_security_descriptor = fileSD;
+ return(status);
+
+} // GetSdFromRegistry
+
+
+BOOLEAN
+GetStickyShareInfo (
+ IN PWSTR ValueName,
+ IN ULONG ValueType,
+ IN PVOID ValueData,
+ OUT PUNICODE_STRING RemarkString,
+ OUT PUNICODE_STRING PathString,
+ OUT PSHARE_INFO_502 shi502
+ )
+
+/*++
+
+Routine Description:
+
+ Gets share information from the registry.
+
+Arguments:
+
+ ValueName - Name of the share
+ ValueType - Value type of the share name.
+ ValueData - Data associated with the ValueName.
+ RemarkString - Upon return, points to a unicode string containing the
+ user remark for this share.
+ PathString - Upon return, points to a unicode string containing the
+ path for this share.
+ shi502 - Upon return, points to a unicode string containing a
+ SHARE_INFO_502 structure.
+
+Return Value:
+
+ TRUE, if share information successfully retrieved.
+ FALSE, otherwise.
+
+--*/
+
+{
+
+ NTSTATUS status;
+ UNICODE_STRING variableNameString;
+ WCHAR integerStringBuffer[35];
+ UNICODE_STRING unicodeString;
+ LPWSTR subStrings[2];
+
+ PathString->Buffer = NULL;
+ RemarkString->Buffer = NULL;
+
+ shi502->shi502_security_descriptor = NULL;
+ shi502->shi502_path = NULL;
+ shi502->shi502_remark = NULL;
+ shi502->shi502_reserved = 0;
+
+ //
+ // Because the NT server doesn't support share-level security, the
+ // password is always NULL.
+ //
+
+ shi502->shi502_passwd = NULL;
+
+ //
+ // The value type must be REG_MULTI_SZ, and the value name must not
+ // be null.
+ //
+
+ if ( (ValueType != REG_MULTI_SZ) ||
+ (wcslen(ValueName) == 0) ) {
+
+ subStrings[0] = ValueName;
+ subStrings[1] = SHARES_REGISTRY_PATH;
+ SsLogEvent(
+ EVENT_SRV_INVALID_REGISTRY_VALUE,
+ 2,
+ subStrings,
+ NO_ERROR
+ );
+
+ IF_DEBUG(REGISTRY) {
+ SS_PRINT(( "GetStickyShareInfo: skipping invalid value %ws\n",
+ ValueName ));
+ }
+ goto errorexit;
+
+ }
+
+ //
+ // The share name is the value name. The value data describes the
+ // rest of the information about the share.
+ //
+
+ shi502->shi502_netname = ValueName;
+
+ //
+ // The REG_MULTI_SZ format is the same as that used for storing
+ // environment variables. Find known share parameters in the data.
+ //
+ // Get the share path. It must be present.
+ //
+
+ RtlInitUnicodeString( &variableNameString, PATH_VARIABLE_NAME );
+
+ PathString->MaximumLength = 0;
+ status = RtlQueryEnvironmentVariable_U(
+ ValueData,
+ &variableNameString,
+ PathString
+ );
+ if ( status != STATUS_BUFFER_TOO_SMALL ) {
+
+ //
+ // The path is not specified. Ignore this share.
+ //
+
+ subStrings[0] = ValueName;
+ subStrings[1] = SHARES_REGISTRY_PATH;
+ SsLogEvent(
+ EVENT_SRV_INVALID_REGISTRY_VALUE,
+ 2,
+ subStrings,
+ RtlNtStatusToDosError( status )
+ );
+
+ IF_DEBUG(REGISTRY) {
+ SS_PRINT(( "GetStickyShareInfo: No path; ignoring share.\n" ));
+ }
+ goto errorexit;
+
+ }
+
+ PathString->MaximumLength = (USHORT)(PathString->Length + sizeof(WCHAR));
+ PathString->Buffer = MIDL_user_allocate( PathString->MaximumLength );
+
+ if ( PathString->Buffer == NULL ) {
+
+ //
+ // No space for path. Ignore this share.
+ //
+
+ subStrings[0] = ValueName;
+ subStrings[1] = SHARES_REGISTRY_PATH;
+ SsLogEvent(
+ EVENT_SRV_INVALID_REGISTRY_VALUE,
+ 2,
+ subStrings,
+ ERROR_NOT_ENOUGH_MEMORY
+ );
+
+ IF_DEBUG(REGISTRY) {
+ SS_PRINT(( "GetStickyShareInfo: MIDL_user_allocate failed; ignoring "
+ "share.\n" ));
+ }
+ goto errorexit;
+
+ }
+
+ status = RtlQueryEnvironmentVariable_U(
+ ValueData,
+ &variableNameString,
+ PathString
+ );
+ if ( !NT_SUCCESS(status) ) {
+
+ //
+ // Huh? The second attempt failed. Ignore this share.
+ //
+
+ subStrings[0] = ValueName;
+ subStrings[1] = SHARES_REGISTRY_PATH;
+ SsLogEvent(
+ EVENT_SRV_INVALID_REGISTRY_VALUE,
+ 2,
+ subStrings,
+ RtlNtStatusToDosError( status )
+ );
+
+ IF_DEBUG(REGISTRY) {
+ SS_PRINT(( "GetStickyShareInfo: Second query failed! Ignoring "
+ "share.\n" ));
+ }
+ goto errorexit;
+
+ }
+
+ //
+ // Get the remark. It may be omitted.
+ //
+
+ RtlInitUnicodeString( &variableNameString, REMARK_VARIABLE_NAME );
+
+ RemarkString->MaximumLength = 0;
+ status = RtlQueryEnvironmentVariable_U(
+ ValueData,
+ &variableNameString,
+ RemarkString
+ );
+ if ( status == STATUS_BUFFER_TOO_SMALL ) {
+
+ RemarkString->MaximumLength =
+ (USHORT)(RemarkString->Length + sizeof(WCHAR));
+ RemarkString->Buffer =
+ MIDL_user_allocate( RemarkString->MaximumLength );
+ if ( RemarkString->Buffer == NULL ) {
+
+ //
+ // No space for remark. Ignore this share.
+ //
+
+ subStrings[0] = ValueName;
+ subStrings[1] = SHARES_REGISTRY_PATH;
+ SsLogEvent(
+ EVENT_SRV_INVALID_REGISTRY_VALUE,
+ 2,
+ subStrings,
+ ERROR_NOT_ENOUGH_MEMORY
+ );
+
+ IF_DEBUG(REGISTRY) {
+ SS_PRINT(( "GetStickyShareInfo: MIDL_user_allocate failed; ignoring "
+ "share.\n" ));
+ }
+ goto errorexit;
+
+ }
+
+ status = RtlQueryEnvironmentVariable_U(
+ ValueData,
+ &variableNameString,
+ RemarkString
+ );
+ if ( !NT_SUCCESS(status) ) {
+
+ //
+ // Huh? The second attempt failed. Ignore this share.
+ //
+
+ subStrings[0] = ValueName;
+ subStrings[1] = SHARES_REGISTRY_PATH;
+ SsLogEvent(
+ EVENT_SRV_INVALID_REGISTRY_VALUE,
+ 2,
+ subStrings,
+ RtlNtStatusToDosError( status )
+ );
+
+ IF_DEBUG(REGISTRY) {
+ SS_PRINT(( "GetStickyShareInfo: Second query failed! "
+ "Ignoring share.\n" ));
+ }
+ goto errorexit;
+
+ }
+
+ }
+
+ //
+ // Get the share type. It may be omitted.
+ //
+
+ RtlInitUnicodeString( &variableNameString, TYPE_VARIABLE_NAME );
+
+ unicodeString.Buffer = integerStringBuffer;
+ unicodeString.MaximumLength = 35;
+ status = RtlQueryEnvironmentVariable_U(
+ ValueData,
+ &variableNameString,
+ &unicodeString
+ );
+ if ( !NT_SUCCESS(status) ) {
+
+ shi502->shi502_type = STYPE_DISKTREE;
+
+ } else {
+
+ status = RtlUnicodeStringToInteger(
+ &unicodeString,
+ 0,
+ &shi502->shi502_type
+ );
+ if ( !NT_SUCCESS(status) ) {
+
+ subStrings[0] = ValueName;
+ subStrings[1] = SHARES_REGISTRY_PATH;
+ SsLogEvent(
+ EVENT_SRV_INVALID_REGISTRY_VALUE,
+ 2,
+ subStrings,
+ RtlNtStatusToDosError( status )
+ );
+
+ IF_DEBUG(REGISTRY) {
+ SS_PRINT(( "GetStickyShareInfo: UnicodeToInteger failed: "
+ "%lx\n", status ));
+ }
+ goto errorexit;
+
+ }
+
+ }
+
+ //
+ // Get the share permissions. It may be omitted.
+ //
+
+ RtlInitUnicodeString( &variableNameString, PERMISSIONS_VARIABLE_NAME );
+
+ status = RtlQueryEnvironmentVariable_U(
+ ValueData,
+ &variableNameString,
+ &unicodeString
+ );
+ if ( !NT_SUCCESS(status) ) {
+
+ shi502->shi502_permissions = 0;
+
+ } else {
+
+ status = RtlUnicodeStringToInteger(
+ &unicodeString,
+ 0,
+ &shi502->shi502_permissions
+ );
+ if ( !NT_SUCCESS(status) ) {
+
+ subStrings[0] = ValueName;
+ subStrings[1] = SHARES_REGISTRY_PATH;
+ SsLogEvent(
+ EVENT_SRV_INVALID_REGISTRY_VALUE,
+ 2,
+ subStrings,
+ RtlNtStatusToDosError( status )
+ );
+
+ IF_DEBUG(REGISTRY) {
+ SS_PRINT(( "GetStickyShareInfo: UnicodeToInteger failed: "
+ "%lx\n", status ));
+ }
+ goto errorexit;
+
+ }
+
+ }
+
+ //
+ // Get the maximum number of uses allowed. It may be omitted.
+ //
+
+ RtlInitUnicodeString( &variableNameString, MAXUSES_VARIABLE_NAME );
+
+ status = RtlQueryEnvironmentVariable_U(
+ ValueData,
+ &variableNameString,
+ &unicodeString
+ );
+ if ( !NT_SUCCESS(status) ) {
+
+ shi502->shi502_max_uses = (DWORD)SHI_USES_UNLIMITED;
+
+ } else {
+
+ status = RtlUnicodeStringToInteger(
+ &unicodeString,
+ 0,
+ &shi502->shi502_max_uses
+ );
+ if ( !NT_SUCCESS(status) ) {
+
+ subStrings[0] = ValueName;
+ subStrings[1] = SHARES_REGISTRY_PATH;
+ SsLogEvent(
+ EVENT_SRV_INVALID_REGISTRY_VALUE,
+ 2,
+ subStrings,
+ RtlNtStatusToDosError( status )
+ );
+
+ goto errorexit;
+
+ }
+
+ }
+
+ {
+ //
+ // Get the Share file security descriptor
+ //
+
+ RTL_QUERY_REGISTRY_TABLE shareQueryTable[2];
+
+ //
+ // Fill up the query table
+ //
+
+ shareQueryTable[0].QueryRoutine = GetSdFromRegistry;
+ shareQueryTable[0].Flags = RTL_QUERY_REGISTRY_REQUIRED;
+ shareQueryTable[0].Name = shi502->shi502_netname;
+ shareQueryTable[0].EntryContext = NULL;
+ shareQueryTable[0].DefaultType = REG_NONE;
+
+ shareQueryTable[1].QueryRoutine = NULL;
+ shareQueryTable[1].Flags = 0;
+ shareQueryTable[1].Name = NULL;
+
+
+ status = RtlQueryRegistryValues(
+ RTL_REGISTRY_SERVICES | RTL_REGISTRY_OPTIONAL,
+ SHARES_SECURITY_REGISTRY_PATH,
+ shareQueryTable,
+ shi502,
+ NULL
+ );
+
+ if ( !NT_SUCCESS( status) &&
+ ( status != STATUS_OBJECT_NAME_NOT_FOUND ) ) {
+ ASSERT(0);
+ IF_DEBUG(REGISTRY) {
+ SS_PRINT(( "GetStickyShareInfo: Get file SD: "
+ "%lx\n", status ));
+ }
+ goto errorexit;
+ }
+ }
+
+ return TRUE;
+
+errorexit:
+
+ if ( RemarkString->Buffer != NULL ) {
+ RtlFreeUnicodeString( RemarkString );
+ }
+
+ if ( PathString->Buffer != NULL ) {
+ RtlFreeUnicodeString( PathString );
+ }
+
+ if ( shi502->shi502_security_descriptor != NULL ) {
+ MIDL_user_free( shi502->shi502_security_descriptor );
+ }
+
+ return FALSE;
+
+} // GetStickyShareInfo
+
+
+LONG
+LoadParameters (
+ PWCH Path
+ )
+
+/*++
+
+Routine Description:
+
+ Reads the registry to get server parameters.
+
+Arguments:
+
+ Path - PARAMETERS_REGISTRY_PATH or AUTOTUNED_REGISTRY_PATH
+
+Return Value:
+
+ LONG - success/failure of the operation.
+
+--*/
+
+{
+ NTSTATUS status;
+ PRTL_QUERY_REGISTRY_TABLE queryTable;
+ ULONG numberOfBindings = 0;
+
+ //
+ // Ask the RTL to call us back for each value in the appropriate
+ // key.
+ //
+
+ queryTable = MIDL_user_allocate( sizeof(RTL_QUERY_REGISTRY_TABLE) * 2 );
+
+ if ( queryTable != NULL ) {
+
+ queryTable[0].QueryRoutine = SetStickyParameter;
+ queryTable[0].Flags = RTL_QUERY_REGISTRY_NOEXPAND;
+ queryTable[0].Name = NULL;
+ queryTable[0].EntryContext = NULL;
+ queryTable[0].DefaultType = REG_NONE;
+ queryTable[0].DefaultData = NULL;
+ queryTable[0].DefaultLength = 0;
+
+ queryTable[1].QueryRoutine = NULL;
+ queryTable[1].Flags = 0;
+ queryTable[1].Name = NULL;
+
+ status = RtlQueryRegistryValues(
+ RTL_REGISTRY_SERVICES | RTL_REGISTRY_OPTIONAL,
+ Path,
+ queryTable,
+ Path, // Context for SetStickyParameter
+ NULL
+ );
+
+ MIDL_user_free( queryTable );
+
+ } else {
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ if ( !NT_SUCCESS(status) ) {
+ IF_DEBUG(INITIALIZATION) {
+ SS_PRINT(( "LoadParameters: RtlQueryRegistryValues failed: "
+ "%lx\n", status ));
+ }
+ return RtlNtStatusToDosError( status );
+ }
+
+ return NO_ERROR;
+
+} // LoadParameters
+
+
+LONG
+LoadSizeParameter (
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ Reads the registry to get the basic server Size parameter.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ LONG - success/failure of the operation.
+
+--*/
+
+{
+ NTSTATUS status;
+ PRTL_QUERY_REGISTRY_TABLE queryTable;
+ ULONG numberOfBindings = 0;
+
+ //
+ // Ask the RTL to call us back if the Size parameter exists.
+ //
+
+ queryTable = MIDL_user_allocate( sizeof(RTL_QUERY_REGISTRY_TABLE) * 2 );
+
+ if ( queryTable != NULL ) {
+
+ queryTable[0].QueryRoutine = SetSizeParameters;
+ queryTable[0].Flags = RTL_QUERY_REGISTRY_NOEXPAND;
+ queryTable[0].Name = SIZE_VALUE_NAME;
+ queryTable[0].EntryContext = NULL;
+ queryTable[0].DefaultType = REG_NONE;
+ queryTable[0].DefaultData = NULL;
+ queryTable[0].DefaultLength = 0;
+
+ queryTable[1].QueryRoutine = NULL;
+ queryTable[1].Flags = 0;
+ queryTable[1].Name = NULL;
+
+ status = RtlQueryRegistryValues(
+ RTL_REGISTRY_SERVICES | RTL_REGISTRY_OPTIONAL,
+ PARAMETERS_REGISTRY_PATH,
+ queryTable,
+ NULL,
+ NULL
+ );
+
+ MIDL_user_free( queryTable );
+
+ } else {
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ if ( !NT_SUCCESS(status) ) {
+ IF_DEBUG(INITIALIZATION) {
+ SS_PRINT(( "LoadSizeParameter: RtlQueryRegistryValues failed: "
+ "%lx\n", status ));
+ }
+ return RtlNtStatusToDosError( status );
+ }
+
+ return NO_ERROR;
+
+} // LoadSizeParameter
+
+VOID
+PrintShareAnnounce (
+ DWORD event
+ )
+{
+ ULONG i;
+
+ //
+ // Announce ourselves and then wait for awhile.
+ // If the event gets signaled, terminate the loop and this thread.
+ // But don't do this forever, since the print subsystem may actually
+ // get stuck
+ //
+
+ //
+ // Do it for 15 minutes
+ //
+ for( i=0; i < 60; i++ ) {
+
+ AnnounceServiceStatus( 1 );
+
+ if( WaitForSingleObject( (HANDLE)event, 15*1000 ) != WAIT_TIMEOUT ) {
+ break;
+ }
+ }
+}
+
+
+NTSTATUS
+RecreateStickyShare (
+ IN PWSTR ValueName,
+ IN ULONG ValueType,
+ IN PVOID ValueData,
+ IN ULONG ValueLength,
+ IN PULONG IterationCount,
+ IN PVOID EntryContext
+ )
+{
+
+ NET_API_STATUS error;
+ SHARE_INFO_502 shi502;
+ SHARE_INFO shareInfo;
+ UNICODE_STRING pathString;
+ UNICODE_STRING remarkString;
+ HANDLE threadHandle = NULL;
+ HANDLE event = NULL;
+
+ ValueLength, EntryContext;
+
+ remarkString.Buffer = NULL;
+ pathString.Buffer = NULL;
+
+
+ if ( GetStickyShareInfo(
+ ValueName,
+ ValueType,
+ ValueData,
+ &remarkString,
+ &pathString,
+ &shi502
+ ) ) {
+
+ //
+ // Do the actual add of the share.
+ //
+
+ IF_DEBUG(INITIALIZATION) {
+ SS_PRINT(( "RecreateStickyShares: adding share %ws\n", ValueName ));
+ }
+
+ shi502.shi502_remark = remarkString.Buffer;
+ shi502.shi502_path = pathString.Buffer;
+
+ shareInfo.ShareInfo502 = (LPSHARE_INFO_502_I)&shi502;
+
+ if( shi502.shi502_type == STYPE_PRINTQ ) {
+ //
+ // A really big problem is that FAX printers can take aribitrarily long to
+ // complete the eventual OpenPrinter() call which the server will make back
+ // up to srvsvc. And if we don't announce ourselves in the interval, the
+ // service controller will presume that we got stuck on startup. Since
+ // NetrShareAdd() is synchronous, we need to get a different thread to
+ // announce our service status until NetrShareAdd returns. So, start it
+ // now. This is most unfortunate.
+
+ event = CreateEvent( NULL, TRUE, FALSE, NULL );
+
+ if( event != NULL ) {
+ DWORD threadId;
+
+ threadHandle = CreateThread(
+ NULL,
+ 0,
+ (LPTHREAD_START_ROUTINE)PrintShareAnnounce,
+ (LPVOID)event,
+ 0,
+ &threadId
+ );
+ if( threadHandle == NULL ) {
+ CloseHandle( event );
+ }
+ }
+ }
+
+ //
+ // RecreateStickyShare is called during server initialization. The service
+ // controller will presume that we're stuck if we don't update our status
+ // with it often enough. So every 64 recreated shares we call back to it.
+ // There's nothing magic about the 64 -- easy to check for, and not too often.
+ //
+ if( (shi502.shi502_type == STYPE_PRINTQ && threadHandle == NULL) ||
+ (++(*IterationCount) & 63 ) == 0 ) {
+
+ AnnounceServiceStatus( 1 );
+ }
+
+ error = NetrShareAdd( NULL, 502, &shareInfo, NULL );
+
+ if( event != NULL ) {
+ //
+ // We created an announcement thread, set the event telling it to terminate
+ //
+ SetEvent( event );
+
+ //
+ // Wait for the thread to terminate
+ //
+ if( WaitForSingleObject( threadHandle, INFINITE ) == WAIT_FAILED ) {
+ error = GetLastError();
+ }
+
+ //
+ // Close the handles
+ //
+ CloseHandle( event );
+ CloseHandle( threadHandle );
+ }
+
+ if ( error != NO_ERROR ) {
+
+ IF_DEBUG(INITIALIZATION_ERRORS) {
+ SS_PRINT(( "RecreateStickyShares: failed to add share "
+ "%ws = %wZ: %ld\n", ValueName, &pathString, error ));
+ }
+ }
+
+ //
+ // free buffers allocated by GetStickyShareInfo
+ //
+
+ if ( remarkString.Buffer != NULL ) {
+ RtlFreeUnicodeString( &remarkString );
+ }
+
+ if ( pathString.Buffer != NULL ) {
+ RtlFreeUnicodeString( &pathString );
+ }
+
+ if ( shi502.shi502_security_descriptor != NULL ) {
+ MIDL_user_free( shi502.shi502_security_descriptor );
+ }
+
+ }
+
+ return NO_ERROR;
+
+} // RecreateStickyShare
+
+
+NTSTATUS
+SaveSdToRegistry(
+ IN PSECURITY_DESCRIPTOR SecurityDescriptor,
+ IN PWSTR ShareName
+ )
+/*++
+
+Routine Description:
+
+ Stores the share file security descriptor in the registry.
+
+Arguments:
+
+ SecurityDescriptor - Points to a self-relative security descriptor
+ describing the access rights for files under this share.
+
+ ShareName - Points to a string containing the share name under
+ which the SD is to be stored.
+
+Return Value:
+
+ Status of the operation.
+
+--*/
+{
+ NTSTATUS status;
+
+ //
+ // Store the security descriptor
+ //
+
+ ULONG fileSDLength;
+
+ if ( !RtlValidSecurityDescriptor( SecurityDescriptor ) ) {
+
+ status = STATUS_INVALID_SECURITY_DESCR;
+
+ } else {
+
+ fileSDLength = RtlLengthSecurityDescriptor( SecurityDescriptor );
+
+ status = RtlWriteRegistryValue(
+ RTL_REGISTRY_SERVICES,
+ SHARES_SECURITY_REGISTRY_PATH,
+ ShareName,
+ REG_BINARY,
+ (LPBYTE)SecurityDescriptor,
+ fileSDLength
+ );
+
+ }
+
+ return status;
+
+} // SaveSdToRegistry
+
+
+NTSTATUS
+SetSizeParameters (
+ IN PWSTR ValueName,
+ IN ULONG ValueType,
+ IN PVOID ValueData,
+ IN ULONG ValueLength,
+ IN PVOID Context,
+ IN PVOID EntryContext
+ )
+{
+ NT_PRODUCT_TYPE productType;
+ DWORD size;
+
+ LPWSTR subStrings[2];
+
+ ValueLength, Context, EntryContext;
+
+ //
+ // Get the product type.
+ //
+
+ if ( !RtlGetNtProductType( &productType ) ) {
+ productType = NtProductWinNt;
+ }
+
+ SsData.ServerInfo598.sv598_producttype = productType;
+
+ //
+ // Make sure that we got called for the right value.
+ //
+
+ ASSERT( _wcsicmp( ValueName, SIZE_VALUE_NAME ) == 0 );
+
+ //
+ // The Size value must be a DWORD, and must be in the following
+ // range:
+ //
+ // 0 -> use defaults
+ // 1 -> small server (minimize memory usage)
+ // 2 -> medium server (balance)
+ // 3 -> large server (maximize connections)
+ //
+
+ if ( ValueType == REG_DWORD ) {
+ ASSERT( ValueLength == sizeof(DWORD) );
+ size = *(LPDWORD)ValueData;
+ }
+
+ if ( (ValueType != REG_DWORD) || (size > 3) ) {
+
+ subStrings[0] = ValueName;
+ subStrings[1] = PARAMETERS_REGISTRY_PATH;
+ SsLogEvent(
+ EVENT_SRV_INVALID_REGISTRY_VALUE,
+ 2,
+ subStrings,
+ NO_ERROR
+ );
+
+ IF_DEBUG(REGISTRY) {
+ SS_PRINT(( "SetSizeParameters: skipping invalid value "
+ "%ws\n", ValueName ));
+ }
+ return STATUS_SUCCESS;
+
+ }
+
+ SsData.ServerInfo598.sv598_serversize = size;
+
+ //
+ // Set appropriate fields based on the product type (Windows NT or
+ // Advanced Server) and the selected Size. Note that a Size of 0
+ // doesn't change any of the defaults.
+ //
+ // Note that the user limit is always -1 (unlimited). Autodisconnect
+ // always defaults to 15 minutes.
+ //
+
+ if ( size != 0 ) {
+
+ SYSTEM_BASIC_INFORMATION basicInfo;
+ NTSTATUS status;
+ ULONG noOfMb;
+ ULONG factor;
+ ULONG asFactor;
+
+ //
+ // Get system memory size.
+ //
+
+ status = NtQuerySystemInformation(
+ SystemBasicInformation,
+ &basicInfo,
+ sizeof( SYSTEM_BASIC_INFORMATION ),
+ NULL
+ );
+
+
+ if ( status != STATUS_SUCCESS ) {
+
+ subStrings[0] = ValueName;
+ subStrings[1] = PARAMETERS_REGISTRY_PATH;
+ SsLogEvent(
+ EVENT_SRV_INVALID_REGISTRY_VALUE,
+ 2,
+ subStrings,
+ NO_ERROR
+ );
+
+ IF_DEBUG(REGISTRY) {
+ SS_PRINT(( "SetSizeParameters: NtQuerySystemInfo failed %x\n",
+ status ));
+ }
+ return STATUS_SUCCESS;
+
+ } else {
+
+ //
+ // Note that we first divide the page size by 512 in order to
+ // allow for physical memory sizes above 2^32-1. With this
+ // calculation, we can handle up to two terabytes of physical
+ // memory. The calculation assumes that the page size is at
+ // least 512, and is not very accurate if the page size is not
+ // a power of 2 (very unlikely).
+ //
+
+ ASSERT( basicInfo.PageSize >= 512 );
+
+ noOfMb = (((basicInfo.PageSize / 512) *
+ basicInfo.NumberOfPhysicalPages) +
+ (1 MB / 512 - 1)) / (1 MB / 512);
+
+ //
+ // Minimum is 8 MB
+ //
+
+ noOfMb = MAX( MIN_SYSTEM_SIZE, noOfMb );
+
+ //
+ // Set the maximum for the different sizes
+ //
+
+ if ( size == 1 ) {
+ noOfMb = MIN( noOfMb, MAX_SMALL_SIZE );
+ } else if ( size == 2 ) {
+ noOfMb = MIN( noOfMb, MAX_MEDIUM_SIZE );
+ }
+ }
+
+ //
+ // If small, assume the system size is half of the real one.
+ // This should give us half the paramater values of a medium server.
+ // If large, double it. Also set the free connection count.
+ //
+
+ if ( size == 1 ) {
+
+ //
+ // Small
+ //
+
+ factor = (noOfMb + 1) / 2;
+
+ SsData.ServerInfo599.sv599_minfreeconnections = 2;
+ SsData.ServerInfo599.sv599_maxfreeconnections = 2;
+
+ } else if ( size == 2 ) {
+
+ //
+ // Balanced
+ //
+
+ factor = noOfMb;
+
+ SsData.ServerInfo599.sv599_minfreeconnections = 2;
+ SsData.ServerInfo599.sv599_maxfreeconnections = 4;
+
+ } else {
+
+ //
+ // Large
+ //
+
+ factor = noOfMb * 2;
+
+ SsData.ServerInfo599.sv599_minfreeconnections = 4;
+ SsData.ServerInfo599.sv599_maxfreeconnections = 8;
+ }
+
+ //
+ // If this is an Advanced Server with at least 24M, some
+ // parameter will need to be even bigger.
+ //
+
+ asFactor = 1;
+ if ( (productType != NtProductWinNt) && (noOfMb >= 24) ) asFactor = 2;
+
+ //
+ // Now set the values for a medium server with this much memory.
+ //
+
+ SsData.ServerInfo599.sv599_maxworkitems =
+ MedSrvCfgTbl.maxworkitems[0] * factor * asFactor /
+ MedSrvCfgTbl.maxworkitems[1];
+
+ SsData.ServerInfo599.sv599_initworkitems =
+ MedSrvCfgTbl.initworkitems[0] * factor * asFactor /
+ MedSrvCfgTbl.initworkitems[1];
+
+ SsData.ServerInfo599.sv599_rawworkitems =
+ MedSrvCfgTbl.rawworkitems[0] * factor /
+ MedSrvCfgTbl.rawworkitems[1];
+
+ SsData.ServerInfo598.sv598_maxrawworkitems =
+ MedSrvCfgTbl.maxrawworkitems[0] * factor * asFactor /
+ MedSrvCfgTbl.maxrawworkitems[1];
+
+ SsData.ServerInfo599.sv599_maxworkitems =
+ MIN( SsData.ServerInfo599.sv599_maxworkitems, MAX_MAXWORKITEMS );
+ SsData.ServerInfo599.sv599_initworkitems =
+ MIN( SsData.ServerInfo599.sv599_initworkitems, MAX_INITWORKITEMS/4 );
+ SsData.ServerInfo599.sv599_rawworkitems =
+ MIN( SsData.ServerInfo599.sv599_rawworkitems, MAX_RAWWORKITEMS/4 );
+ SsData.ServerInfo598.sv598_maxrawworkitems =
+ MIN( SsData.ServerInfo598.sv598_maxrawworkitems, MAX_MAXRAWWORKITEMS );
+
+ if ( (productType != NtProductWinNt) || (size == 3) ) {
+ SsData.ServerInfo599.sv599_maxpagedmemoryusage = INF;
+ SsData.ServerInfo599.sv599_maxnonpagedmemoryusage = INF;
+ } else {
+ SsData.ServerInfo599.sv599_maxpagedmemoryusage =
+ MedSrvCfgTbl.maxpagedmemoryusage[0] * factor /
+ MedSrvCfgTbl.maxpagedmemoryusage[1] MB;
+
+ SsData.ServerInfo599.sv599_maxpagedmemoryusage =
+ MAX( SsData.ServerInfo599.sv599_maxpagedmemoryusage,
+ MIN_MAXPAGEDMEMORYUSAGE);
+
+ SsData.ServerInfo599.sv599_maxnonpagedmemoryusage =
+ MedSrvCfgTbl.maxnonpagedmemoryusage[0] * factor /
+ MedSrvCfgTbl.maxnonpagedmemoryusage[1] MB;
+
+ SsData.ServerInfo599.sv599_maxnonpagedmemoryusage =
+ MAX( SsData.ServerInfo599.sv599_maxnonpagedmemoryusage,
+ MIN_MAXNONPAGEDMEMORYUSAGE);
+ }
+ }
+
+ return STATUS_SUCCESS;
+
+} // SetSizeParameters
+
+
+NTSTATUS
+SetStickyParameter (
+ IN PWSTR ValueName,
+ IN ULONG ValueType,
+ IN PVOID ValueData,
+ IN ULONG ValueLength,
+ IN PVOID Context,
+ IN PVOID EntryContext
+ )
+{
+ NET_API_STATUS error;
+ DWORD i;
+ PFIELD_DESCRIPTOR foundField = NULL;
+ LPWSTR subStrings[2];
+
+ ValueLength, EntryContext;
+
+ //
+ // Ignore several parameters, since they are handled elsewhere
+ //
+ if( (_wcsicmp( ValueName, SIZE_VALUE_NAME ) == 0) ||
+ (_wcsicmp( ValueName, NULL_SESSION_SHARES_VALUE_NAME ) == 0) ||
+ (_wcsicmp( ValueName, NULL_SESSION_PIPES_VALUE_NAME ) == 0) ||
+ (_wcsicmp( ValueName, PIPES_NEED_LICENSE_VALUE_NAME ) == 0) ||
+ (_wcsicmp( ValueName, ERROR_LOG_IGNORE_VALUE_NAME ) == 0) ||
+ (_wcsicmp( ValueName, OPTIONAL_NAMES_VALUE_NAME ) == 0 ) ) {
+
+ return STATUS_SUCCESS;
+ }
+
+ //
+ // Determine which field we need to set, based on the value
+ // name.
+ //
+ // NOTE: For Daytona, disc and comment are now invalid registry names.
+ // We use their more famous aliases autodisconnect and srvcomment
+ // instead. If we get more of these cases, we should consider adding
+ // a field to the FIELD_DESCRIPTOR structure that indicates whether
+ // the names are should appear on the registry or not. Any change
+ // here should also be made to SsSetField().
+ //
+
+ if ( (_wcsicmp( ValueName, DISC_VALUE_NAME ) != 0) &&
+ (_wcsicmp( ValueName, COMMENT_VALUE_NAME ) != 0) ) {
+
+ for ( i = 0;
+ SsServerInfoFields[i].FieldName != NULL;
+ i++ ) {
+
+ if ( _wcsicmp( ValueName, SsServerInfoFields[i].FieldName ) == 0 ) {
+ foundField = &SsServerInfoFields[i];
+ break;
+ }
+ }
+ }
+
+ if ( foundField == NULL || foundField->Settable == NOT_SETTABLE ) {
+
+ subStrings[0] = ValueName;
+ subStrings[1] = Context;
+ SsLogEvent(
+ EVENT_SRV_INVALID_REGISTRY_VALUE,
+ 2,
+ subStrings,
+ NO_ERROR
+ );
+
+ IF_DEBUG(REGISTRY) {
+ SS_PRINT(( "SetStickyParameter: ignoring %s \"%ws\"\n",
+ (foundField == NULL ? "unknown value name" :
+ "unsettable value"), ValueName ));
+ }
+ return STATUS_SUCCESS;
+
+ }
+
+ switch ( foundField->FieldType ) {
+
+ case BOOLEAN_FIELD:
+ case DWORD_FIELD:
+
+ if ( ValueType != REG_DWORD ) {
+
+ subStrings[0] = ValueName;
+ subStrings[1] = Context;
+ SsLogEvent(
+ EVENT_SRV_INVALID_REGISTRY_VALUE,
+ 2,
+ subStrings,
+ NO_ERROR
+ );
+
+ IF_DEBUG(REGISTRY) {
+ SS_PRINT(( "SetStickyParameter: skipping invalid value "
+ "%ws\n", ValueName ));
+ }
+ return STATUS_SUCCESS;
+
+ }
+
+ i = *(LPDWORD)ValueData;
+ break;
+
+ case LPSTR_FIELD:
+
+ if ( ValueType != REG_SZ ) {
+
+ subStrings[0] = ValueName;
+ subStrings[1] = Context;
+ SsLogEvent(
+ EVENT_SRV_INVALID_REGISTRY_VALUE,
+ 2,
+ subStrings,
+ NO_ERROR
+ );
+
+ IF_DEBUG(REGISTRY) {
+ SS_PRINT(( "SetStickyParameter: skipping invalid value "
+ "%ws\n", ValueName ));
+ }
+ return STATUS_SUCCESS;
+
+ }
+
+ i = (DWORD)ValueData;
+ break;
+
+ }
+
+ //
+ // Set the field.
+ //
+
+ error = SsSetField( foundField, &i, FALSE, NULL );
+
+ if ( error != NO_ERROR ) {
+
+ subStrings[0] = ValueName;
+ subStrings[1] = Context;
+ SsLogEvent(
+ EVENT_SRV_INVALID_REGISTRY_VALUE,
+ 2,
+ subStrings,
+ error
+ );
+
+ IF_DEBUG(REGISTRY) {
+ SS_PRINT(( "SetStickyParameter: error %ld ignored in setting "
+ "parameter \"%ws\"n", error, ValueName ));
+ }
+ }
+
+ return STATUS_SUCCESS;
+
+} // SetStickyParameter
+
diff --git a/private/net/svcdlls/srvsvc/server/scavengr.c b/private/net/svcdlls/srvsvc/server/scavengr.c
new file mode 100644
index 000000000..87bb309d6
--- /dev/null
+++ b/private/net/svcdlls/srvsvc/server/scavengr.c
@@ -0,0 +1,1319 @@
+/*++
+
+Copyright (c) 1991-1992 Microsoft Corporation
+
+Module Name:
+
+ Scavengr.c
+
+Abstract:
+
+ This module contains the code for the server service scavenger
+ thread. This thread handles announcements and configuration
+ changes. (Although originally written to run in a separate thread,
+ this code now runs in the initial thread of the server service.
+
+Author:
+
+ David Treadwell (davidtr) 17-Apr-1991
+
+Revision History:
+
+--*/
+
+#include "srvsvcp.h"
+#include "ssdata.h"
+#include "ssreg.h"
+
+#include <netlibnt.h>
+#include <tstr.h>
+
+#define INCLUDE_SMB_TRANSACTION
+#undef NT_PIPE_PREFIX
+#include <smbtypes.h>
+#include <smb.h>
+#include <smbtrans.h>
+#include <smbgtpt.h>
+
+#include <hostannc.h>
+#include <ntddbrow.h>
+#include <lmerr.h>
+
+#define TERMINATION_SIGNALED 0
+#define ANNOUNCE_SIGNALED 1
+#define STATUS_CHANGED 2
+#define REGISTRY_CHANGED 3 // Must be the last one!
+
+#define NUMBER_OF_EVENTS 4
+
+
+//
+// Bias request announcements by SERVER_REQUEST_ANNOUNCE_DELTA SECONDS
+//
+
+#define SERVER_REQUEST_ANNOUNCE_DELTA 30
+
+//
+// Forward declarations.
+//
+
+VOID
+Announce (
+ IN BOOL DoNtAnnouncement,
+ IN DWORD NtInterval,
+ IN BOOL DoLmAnnouncement,
+ IN BOOL TerminationAnnouncement
+ );
+
+NET_API_STATUS
+SendSecondClassMailslot (
+ IN LPTSTR Transport OPTIONAL,
+ IN PVOID Message,
+ IN DWORD MessageLength,
+ IN LPTSTR Domain,
+ IN LPSTR MailslotNameText,
+ IN UCHAR SignatureByte
+ );
+
+NET_API_STATUS
+SsBrowserIoControl (
+ IN DWORD IoControlCode,
+ IN PVOID Buffer,
+ IN DWORD BufferLength,
+ IN PLMDR_REQUEST_PACKET Packet,
+ IN DWORD PacketLength
+ );
+
+
+DWORD
+ComputeAnnounceTime (
+ IN DWORD LastAnnouncementTime,
+ IN DWORD Interval
+ )
+
+/*++
+
+Routine Description:
+
+ Compute the time to wait (in milliseconds) until the next announcement should
+ be made.
+
+Arguments:
+
+ LastAnnouncementTime - Time (in milliseconds since reboot) when the last
+ announcement was made.
+
+ Interval - Interval (in seconds) between announcements
+
+Return Value:
+
+ Timeout period (in milliseconds)
+
+--*/
+
+{
+ DWORD AnnounceDelta;
+ DWORD Timeout;
+ DWORD CurrentTime;
+
+ //
+ // Get the current time.
+ //
+
+ CurrentTime = GetTickCount();
+
+ //
+ // If the clock has gone backward,
+ // send an announcement now.
+ //
+
+ if ( LastAnnouncementTime > CurrentTime ) {
+ return 0;
+ }
+
+ //
+ // Convert the announcement period from seconds to milliseconds.
+ //
+
+ Timeout = Interval * 1000;
+
+ //
+ // Add in the random announce delta which helps prevent lots of
+ // servers from announcing at the same time.
+ //
+
+ AnnounceDelta = SsData.ServerInfo102.sv102_anndelta;
+
+ Timeout += ((rand( ) * AnnounceDelta * 2) / RAND_MAX) -
+ AnnounceDelta;
+
+ //
+ // If our time has expired,
+ // send an announcement now.
+ //
+
+ if ( (CurrentTime - LastAnnouncementTime) >= Timeout ) {
+ return 0;
+ }
+
+ //
+ // Adjust our timeout period for time already elapsed.
+ //
+
+ return Timeout - (CurrentTime - LastAnnouncementTime);
+
+}
+
+
+DWORD
+SsScavengerThread (
+ IN LPVOID lpThreadParameter
+ )
+
+/*++
+
+Routine Description:
+
+ This routine implements the server service scavenger thread.
+
+Arguments:
+
+ lpThreadParameter - ignored.
+
+Return Value:
+
+ NET_API_STATUS - thread termination result.
+
+--*/
+
+{
+ HANDLE events[ NUMBER_OF_EVENTS ];
+ ULONG numEvents = NUMBER_OF_EVENTS-1;
+ UNICODE_STRING unicodeEventName;
+ OBJECT_ATTRIBUTES obja;
+ DWORD waitStatus;
+ DWORD timeout;
+
+ DWORD LmTimeout;
+ BOOL DoLmAnnouncement;
+ DWORD LmLastAnnouncementTime;
+
+ DWORD NtTimeout;
+ BOOL DoNtAnnouncement;
+ DWORD NtLastAnnouncementTime;
+ DWORD NtInterval;
+
+ NTSTATUS status;
+ BOOL hidden = TRUE;
+ HKEY hParameters = INVALID_HANDLE_VALUE;
+
+ lpThreadParameter;
+
+ //
+ // Use the scavenger termination event to know when we're supposed
+ // to wake up and kill ourselves.
+ //
+
+ events[TERMINATION_SIGNALED] = SsTerminationEvent;
+
+ //
+ // Initialize the NT announcement interval to the LM announcement interval
+ //
+
+ NtInterval = SsData.ServerInfo102.sv102_announce;
+ DoLmAnnouncement = TRUE;
+ DoNtAnnouncement = TRUE;
+
+ //
+ // Create the announce event. When this gets signaled, we wake up
+ // and do an announcement. We use a synchronization event rather
+ // than a notification event so that we don't have to worry about
+ // resetting the event after we wake up.
+ //
+
+ //
+ // Please note that we create this event with OBJ_OPENIF. We do this
+ // to allow the browser to signal the server to force an announcement.
+ //
+ // The bowser will create this event as a part of the bowser
+ // initialization, and will set it to the signalled state when it needs
+ // to have the server announce.
+ //
+
+
+ RtlInitUnicodeString( &unicodeEventName, SERVER_ANNOUNCE_EVENT_W );
+ InitializeObjectAttributes( &obja, &unicodeEventName, OBJ_OPENIF, NULL, NULL );
+
+ status = NtCreateEvent(
+ &SsAnnouncementEvent,
+ SYNCHRONIZE | EVENT_QUERY_STATE | EVENT_MODIFY_STATE,
+ &obja,
+ SynchronizationEvent,
+ FALSE
+ );
+
+ if ( !NT_SUCCESS(status) ) {
+ SS_PRINT(( "SsScavengerThread: NtCreateEvent failed: %X\n",
+ status ));
+ return NetpNtStatusToApiStatus( status );
+ }
+
+ events[ANNOUNCE_SIGNALED] = SsAnnouncementEvent;
+
+ //
+ // Create an unnamed event to be set to the signalled state when the
+ // service status changes (or a local application requests an
+ // announcement)
+ //
+
+ InitializeObjectAttributes( &obja, NULL, OBJ_OPENIF, NULL, NULL );
+
+ status = NtCreateEvent(
+ &SsStatusChangedEvent,
+ SYNCHRONIZE | EVENT_QUERY_STATE | EVENT_MODIFY_STATE,
+ &obja,
+ SynchronizationEvent,
+ FALSE
+ );
+
+ if ( !NT_SUCCESS(status) ) {
+ SS_PRINT(( "SsScavengerThread: NtCreateEvent failed: %X\n",
+ status ));
+
+ NtClose( SsAnnouncementEvent );
+ SsAnnouncementEvent = NULL;
+
+ return NetpNtStatusToApiStatus( status );
+ }
+
+ events[STATUS_CHANGED] = SsStatusChangedEvent;
+
+ //
+ // Put a watch on the registry for any changes that happen in the
+ // null session share or pipe list. Don't bail out if this fails,
+ // because we've done this as a convenience in adding new null
+ // session-reliant servers. It doesn't really affect the normal
+ // operation of the server if this doesn't work.
+ //
+ events[ REGISTRY_CHANGED ] = INVALID_HANDLE_VALUE;
+ status = NtCreateEvent(
+ &events[ REGISTRY_CHANGED ],
+ SYNCHRONIZE | EVENT_QUERY_STATE | EVENT_MODIFY_STATE,
+ NULL,
+ SynchronizationEvent,
+ FALSE
+ );
+
+ if ( NT_SUCCESS(status) ) {
+ status = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
+ FULL_PARAMETERS_REGISTRY_PATH,
+ 0,
+ KEY_NOTIFY,
+ &hParameters
+ );
+
+ if( status == ERROR_SUCCESS ) {
+ (void)RegNotifyChangeKeyValue( hParameters,
+ TRUE,
+ REG_NOTIFY_CHANGE_LAST_SET,
+ events[ REGISTRY_CHANGED ],
+ TRUE
+ );
+ //
+ // Add this event to the list of events we're waiting for
+ //
+ ++numEvents;
+ }
+ }
+
+ //
+ // Seed the random number generator. We use it to generate random
+ // announce deltas.
+ //
+
+ srand( (int)SsAnnouncementEvent );
+
+ //
+ // Do an announcement immediately for startup, then loop announcing
+ // based on the announce interval.
+ //
+
+ waitStatus = WAIT_TIMEOUT;
+
+ do {
+
+
+
+ //
+ // Act according to whether the termination event, the announce
+ // event, or the timeout caused us to wake up.
+ //
+ // !!! Or the configuration event indicating a configuration
+ // change notification.
+
+ if ( waitStatus == WAIT_OBJECT_0 + TERMINATION_SIGNALED ) {
+
+ SS_PRINT(( "Scavenger: termination event signaled\n" ));
+
+ //
+ // The scavenger termination event was signaled, so we have
+ // to gracefully kill this thread. If this is not a hidden
+ // server, announce the fact that we're going down.
+ //
+
+ if ( !hidden ) {
+ Announce( TRUE, NtInterval, TRUE, TRUE );
+ }
+
+ //
+ // Close the announcement event.
+ //
+
+ NtClose( SsAnnouncementEvent );
+
+ SsAnnouncementEvent = NULL;
+
+ //
+ // Close the Registry watch event.
+ //
+ if( events[ REGISTRY_CHANGED ] != INVALID_HANDLE_VALUE )
+ NtClose( events[ REGISTRY_CHANGED ] );
+
+ //
+ // Close the Registry handle
+ //
+ if( hParameters != INVALID_HANDLE_VALUE )
+ RegCloseKey( hParameters );
+
+ //
+ // Return to caller.
+ //
+
+ return NO_ERROR;
+
+ } else if( waitStatus == WAIT_OBJECT_0 + REGISTRY_CHANGED ) {
+ //
+ // Somebody changed some server parameters. Tell the driver
+ //
+ SS_PRINT(( "SsScavengerThread: Server parameters changed\n" ));
+
+ //
+ // Tell the server FSD to look for a change in the registry
+ //
+ (void)SsServerFsControl( NULL, FSCTL_SRV_REGISTRY_CHANGE, NULL, NULL, 0 );
+
+ //
+ // Turn it back on so we get future changes, too
+ //
+ (void)RegNotifyChangeKeyValue( hParameters,
+ TRUE,
+ REG_NOTIFY_CHANGE_LAST_SET,
+ events[ REGISTRY_CHANGED ],
+ TRUE
+ );
+
+ } else {
+
+ SS_ASSERT( waitStatus == WAIT_TIMEOUT ||
+ waitStatus == WAIT_OBJECT_0 + ANNOUNCE_SIGNALED ||
+ waitStatus == WAIT_OBJECT_0 + STATUS_CHANGED );
+
+ //
+ // If we've timed out,
+ // we've already set the flags indicating whether to announce
+ // on to lanman to NT browsers
+ //
+
+ if ( waitStatus != WAIT_TIMEOUT ) {
+ DoLmAnnouncement = TRUE;
+ DoNtAnnouncement = TRUE;
+ }
+
+ //
+ // If we're not a hidden server, announce ourselves.
+ //
+
+ // Hold the database resource while we do the announcement so
+ // that we get a consistent view of the database.
+ //
+
+ (VOID)RtlAcquireResourceShared( &SsServerInfoResource, TRUE );
+
+ if ( !SsData.ServerInfo102.sv102_hidden ) {
+
+ hidden = FALSE;
+
+ Announce( DoNtAnnouncement, NtInterval, DoLmAnnouncement, FALSE );
+
+
+
+ //
+ // If we were not hidden last time through the loop but
+ // we're hidden now, we've changed to hidden, so announce
+ // that we're going down. This causes clients in the domain
+ // to take us out of their server enumerations.
+ //
+
+ } else if ( !hidden ) {
+
+ hidden = TRUE;
+ Announce( TRUE, NtInterval, TRUE, TRUE );
+
+ }
+
+ //
+ // If the server is hidden, the wait timeout is infinite. We'll
+ // be woken up by the announce event if the server becomes
+ // unhidden.
+ //
+
+ if ( SsData.ServerInfo102.sv102_hidden ) {
+
+ timeout = 0xffffffff;
+
+ } else {
+
+ //
+ // Remember when the last announcement was.
+ //
+
+ if ( DoNtAnnouncement ) {
+ NtLastAnnouncementTime = GetTickCount();
+ }
+
+ if ( DoLmAnnouncement ) {
+ LmLastAnnouncementTime = GetTickCount();
+ }
+
+ //
+ // Compute the time delta to the next announcement
+ //
+ // For NtInterval,
+ // use a local copy of the interval since we compute the correct
+ // value.
+ //
+ // For the Lanman interval,
+ // use the global copy to allow the interval to be changed.
+ //
+
+ NtTimeout = ComputeAnnounceTime(
+ NtLastAnnouncementTime,
+ NtInterval );
+
+ if (SsData.ServerInfo599.sv599_lmannounce) {
+ LmTimeout = ComputeAnnounceTime(
+ LmLastAnnouncementTime,
+ SsData.ServerInfo102.sv102_announce );
+ } else {
+ // Don't awaken this thread to do nothing.
+ LmTimeout = 0xffffffff;
+ }
+
+
+ //
+ // If our NT announcement frequency is less than 12 minutes,
+ // increase our announcement frequency by 4 minutes.
+ //
+
+ if ( NtInterval < 12 * 60 ) {
+
+ NtInterval += 4 * 60;
+
+ if ( NtInterval > 12 * 60) {
+ NtInterval = 12 * 60;
+ }
+ }
+
+ //
+ // Determine which timeout we're actually going to use.
+ //
+
+ if ( NtTimeout == LmTimeout ) {
+ timeout = NtTimeout;
+ DoLmAnnouncement = TRUE;
+ DoNtAnnouncement = TRUE;
+ } else if ( NtTimeout < LmTimeout ) {
+ timeout = NtTimeout;
+ DoLmAnnouncement = FALSE;
+ DoNtAnnouncement = TRUE;
+ } else {
+ timeout = LmTimeout;
+ DoLmAnnouncement = TRUE;
+ DoNtAnnouncement = FALSE;
+ }
+
+ }
+
+ RtlReleaseResource( &SsServerInfoResource );
+ }
+
+
+ //
+ // Wait for one of the events to be signaled or for the timeout
+ // to elapse.
+ //
+
+ waitStatus = WaitForMultipleObjects( numEvents , events, FALSE, timeout );
+
+ if ( waitStatus == WAIT_OBJECT_0 + ANNOUNCE_SIGNALED ) {
+
+ //
+ // We were awakened because an announce was signalled.
+ // Unless we are a master browser on at least one transport,
+ // delay for a random delta to stagger announcements a bit
+ // to prevent lots of servers from announcing
+ // simultaneously.
+ //
+
+ BOOL isMasterBrowser = FALSE;
+ PNAME_LIST_ENTRY service = SsServerNameList;
+ PTRANSPORT_LIST_ENTRY transport;
+
+ for( service = SsServerNameList;
+ isMasterBrowser == FALSE && service != NULL;
+ service = service->Next ) {
+
+ if( service->ServiceBits & SV_TYPE_MASTER_BROWSER ) {
+ isMasterBrowser = TRUE;
+ break;
+ }
+
+ for( transport=service->Transports; transport != NULL; transport=transport->Next ) {
+ if( transport->ServiceBits & SV_TYPE_MASTER_BROWSER ) {
+ isMasterBrowser = TRUE;
+ break;
+ }
+ }
+ }
+
+ if ( !isMasterBrowser ) {
+ Sleep( ((rand( ) * (SERVER_REQUEST_ANNOUNCE_DELTA * 1000)) / RAND_MAX) );
+ }
+
+ }
+
+ } while ( TRUE );
+
+ return NO_ERROR;
+
+} // SsScavengerThread
+
+
+VOID
+SsAnnounce (
+ IN POEM_STRING OemAnnounceName,
+ IN LPWSTR EmulatedDomainName,
+ IN BOOL DoNtAnnouncement,
+ IN DWORD NtInterval,
+ IN BOOL DoLmAnnouncement,
+ IN BOOL TerminationAnnouncement,
+ IN LPTSTR Transport,
+ IN DWORD serviceType
+ )
+
+/*++
+
+Routine Description:
+
+ This routine sends a broadcast datagram as a second-class mailslot
+ that announces the presence of this server on the network.
+
+Arguments:
+
+ OemAnnounceName - The name we're announcing to the network
+
+ EmulatedDomainName - The name of the domain this announcement is happening for.
+
+ DoNtAnnouncement - Do an Nt-style announcement.
+
+ NtInterval - NT announcement interval (in seconds)
+
+ DoLmAnnouncement - Do an Lm-style announcement.
+
+ TerminationAnnouncement - if TRUE, send the announcement that
+ indicates that this server is going away. Otherwise, send
+ the normal message that tells clients that we're here.
+
+ Transport - Ssupplies the transport to issue the announcement
+ on.
+
+ serviceType - Service bits being announced
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ DWORD messageSize;
+ PHOST_ANNOUNCE_PACKET packet;
+ PBROWSE_ANNOUNCE_PACKET browsePacket;
+
+ LPSTR serverName;
+ DWORD oemServerNameLength; // includes the null terminator
+
+ LPSTR serverComment;
+ DWORD serverCommentLength; // includes the null terminator
+ OEM_STRING oemCommentString;
+
+ UNICODE_STRING unicodeCommentString;
+
+ NET_API_STATUS status;
+
+ //
+ // Fill in the necessary information.
+ //
+
+ if( TerminationAnnouncement ) {
+ serviceType &= ~SV_TYPE_SERVER; // since the server is going away!
+ }
+
+ SS_PRINT(( "SsScavengerThread: Announcing for transport %s, Bits: %lx\n",
+ Transport, serviceType ));
+
+ //
+ // Get the length of the oem equivalent of the server name
+ //
+
+ oemServerNameLength = OemAnnounceName->Length + 1;
+
+ //
+ // Convert server comment to a unicode string
+ //
+
+ if ( *SsData.ServerCommentBuffer == '\0' ) {
+ serverCommentLength = 1;
+ } else {
+ unicodeCommentString.Length =
+ (USHORT)(STRLEN( SsData.ServerCommentBuffer ) * sizeof(WCHAR));
+ unicodeCommentString.MaximumLength =
+ (USHORT)(unicodeCommentString.Length + sizeof(WCHAR));
+ unicodeCommentString.Buffer = SsData.ServerCommentBuffer;
+ serverCommentLength =
+ RtlUnicodeStringToOemSize( &unicodeCommentString );
+ }
+
+ oemCommentString.MaximumLength = (USHORT)serverCommentLength;
+
+ messageSize = max(sizeof(HOST_ANNOUNCE_PACKET) + oemServerNameLength +
+ serverCommentLength,
+ sizeof(BROWSE_ANNOUNCE_PACKET) + serverCommentLength);
+
+ //
+ // Get memory to hold the message. If we can't allocate enough
+ // memory, don't send an announcement.
+ //
+
+ packet = MIDL_user_allocate( messageSize );
+ if ( packet == NULL ) {
+ return;
+ }
+
+ //
+ // If we are announcing as a Lan Manager server, broadcast the
+ // announcement.
+ //
+
+ if (SsData.ServerInfo599.sv599_lmannounce && DoLmAnnouncement ) {
+
+ packet->AnnounceType = HostAnnouncement ;
+
+ SmbPutUlong( &packet->HostAnnouncement.Type, serviceType );
+
+ packet->HostAnnouncement.CompatibilityPad = 0;
+
+ packet->HostAnnouncement.VersionMajor =
+ (BYTE)SsData.ServerInfo102.sv102_version_major;
+ packet->HostAnnouncement.VersionMinor =
+ (BYTE)SsData.ServerInfo102.sv102_version_minor;
+
+ SmbPutUshort(
+ &packet->HostAnnouncement.Periodicity,
+ (WORD)SsData.ServerInfo102.sv102_announce
+ );
+
+ //
+ // Convert the server name from unicode to oem
+ //
+
+ serverName = (LPSTR)( &packet->HostAnnouncement.NameComment );
+
+ RtlCopyMemory( serverName, OemAnnounceName->Buffer, OemAnnounceName->Length );
+ serverName[OemAnnounceName->Length] = '\0';
+
+ serverComment = serverName + oemServerNameLength;
+
+ if ( serverCommentLength == 1 ) {
+ *serverComment = '\0';
+ } else {
+
+ oemCommentString.Buffer = serverComment;
+ (VOID) RtlUnicodeStringToOemString(
+ &oemCommentString,
+ &unicodeCommentString,
+ FALSE
+ );
+ }
+
+ SendSecondClassMailslot(
+ Transport,
+ packet,
+ FIELD_OFFSET(HOST_ANNOUNCE_PACKET, HostAnnouncement.NameComment) +
+ oemServerNameLength + serverCommentLength,
+ EmulatedDomainName,
+ "\\MAILSLOT\\LANMAN",
+ 0x00
+ );
+ }
+
+ //
+ // Now announce the server as a Winball server.
+ //
+
+ if ( DoNtAnnouncement ) {
+ browsePacket = (PBROWSE_ANNOUNCE_PACKET)packet;
+
+ browsePacket->BrowseType = ( serviceType & SV_TYPE_MASTER_BROWSER ?
+ LocalMasterAnnouncement :
+ HostAnnouncement );
+
+ browsePacket->BrowseAnnouncement.UpdateCount = 0;
+
+ SmbPutUlong( &browsePacket->BrowseAnnouncement.CommentPointer, (ULONG)((0xaa55 << 16) + (BROWSER_VERSION_MAJOR << 8) + BROWSER_VERSION_MINOR));
+
+ SmbPutUlong( &browsePacket->BrowseAnnouncement.Periodicity, NtInterval * 1000 );
+
+ SmbPutUlong( &browsePacket->BrowseAnnouncement.Type, serviceType );
+
+ browsePacket->BrowseAnnouncement.VersionMajor =
+ (BYTE)SsData.ServerInfo102.sv102_version_major;
+ browsePacket->BrowseAnnouncement.VersionMinor =
+ (BYTE)SsData.ServerInfo102.sv102_version_minor;
+
+ RtlCopyMemory( &browsePacket->BrowseAnnouncement.ServerName,
+ OemAnnounceName->Buffer,
+ OemAnnounceName->Length );
+ browsePacket->BrowseAnnouncement.ServerName[OemAnnounceName->Length] = '\0';
+
+ serverComment = (LPSTR)&browsePacket->BrowseAnnouncement.Comment;
+
+ if ( serverCommentLength == 1 ) {
+ *serverComment = '\0';
+ } else {
+
+ oemCommentString.Buffer = serverComment;
+ (VOID) RtlUnicodeStringToOemString(
+ &oemCommentString,
+ &unicodeCommentString,
+ FALSE
+ );
+ }
+
+#ifdef KLUDGE_NOT_NEEDED // BUGBUG: SendSecondClassMailslot always sends "from" primary computername
+ status = SendSecondClassMailslot(
+ Transport,
+ packet,
+ FIELD_OFFSET(BROWSE_ANNOUNCE_PACKET, BrowseAnnouncement.Comment) +
+ serverCommentLength,
+ EmulatedDomainName,
+ "\\MAILSLOT\\BROWSE",
+ (UCHAR)(serviceType & SV_TYPE_MASTER_BROWSER ?
+ BROWSER_ELECTION_SIGNATURE :
+ MASTER_BROWSER_SIGNATURE)
+ );
+#else
+ status = ERROR_NOT_SUPPORTED;
+#endif // KLUDGE_NOT_NEEDED
+
+ if ( status != NERR_Success ) {
+ UCHAR packetBuffer[sizeof(LMDR_REQUEST_PACKET)+(MAX_PATH)*sizeof(WCHAR)];
+ PLMDR_REQUEST_PACKET requestPacket = (PLMDR_REQUEST_PACKET)packetBuffer;
+ UNICODE_STRING TransportString;
+
+ RtlInitUnicodeString(&TransportString, Transport);
+
+ requestPacket->Version = LMDR_REQUEST_PACKET_VERSION;
+
+ requestPacket->TransportName = TransportString;
+#ifdef _CAIRO_
+ RtlInitUnicodeString( &requestPacket->EmulatedDomainName, EmulatedDomainName );
+#endif // _CAIRO_
+
+ requestPacket->Type = Datagram;
+
+ requestPacket->Parameters.SendDatagram.DestinationNameType = (serviceType & SV_TYPE_MASTER_BROWSER ? BrowserElection : MasterBrowser);
+
+ requestPacket->Parameters.SendDatagram.MailslotNameLength = 0;
+
+ //
+ // The domain announcement name is special, so we don't have to specify
+ // a destination name for it.
+ //
+
+ requestPacket->Parameters.SendDatagram.NameLength = STRLEN(EmulatedDomainName)*sizeof(TCHAR);
+
+ STRCPY(requestPacket->Parameters.SendDatagram.Name, EmulatedDomainName);
+
+ //
+ // This is a simple IoControl - It just sends the datagram.
+ //
+
+ status = SsBrowserIoControl(IOCTL_LMDR_WRITE_MAILSLOT,
+ packet,
+ FIELD_OFFSET(BROWSE_ANNOUNCE_PACKET, BrowseAnnouncement.Comment) +
+ serverCommentLength,
+ requestPacket,
+ FIELD_OFFSET(LMDR_REQUEST_PACKET, Parameters.SendDatagram.Name)+
+ requestPacket->Parameters.SendDatagram.NameLength);
+ }
+ }
+
+ MIDL_user_free( packet );
+
+} // SsAnnounce
+
+
+ULONG
+ComputeTransportAddressClippedLength(
+ IN PCHAR TransportAddress,
+ IN ULONG TransportAddressLength
+ )
+
+/*++
+
+Routine Description:
+
+ This routine returns the length of the transport address with the trailing
+ blanks removed.
+
+Arguments:
+
+ TransportAddress - Transport address with potentially trailing blanks
+
+ TransportAddressLength - Length of the Transport Address including trailing
+ blanks
+
+Return Value:
+
+ Length of the Transport Address excluding trailing blanks.
+
+--*/
+
+{
+ PCHAR p;
+
+ //
+ // Cut off any trailing spaces
+ //
+ p = &TransportAddress[ TransportAddressLength ];
+ for( ; p > TransportAddress && *(p-1) == ' '; p-- )
+ ;
+
+ return p - TransportAddress;
+}
+
+
+
+VOID
+Announce (
+ IN BOOL DoNtAnnouncement,
+ IN DWORD NtInterval,
+ IN BOOL DoLmAnnouncement,
+ IN BOOL TerminationAnnouncement
+ )
+
+/*++
+
+Routine Description:
+
+ This routine sends a broadcast datagram as a second-class mailslot
+ that announces the presence of this server using all
+ of the configured server names and all networks.
+
+Arguments:
+
+ DoNtAnnouncement - Do an Nt-style announcement.
+
+ NtInterval - NT announcement interval (in seconds)
+
+ DoLmAnnouncement - Do an Lm-style announcement.
+
+ TerminationAnnouncement - if TRUE, send the announcement that
+ indicates that this server is going away. Otherwise, send
+ the normal message that tells clients that we're here.
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PNAME_LIST_ENTRY Service;
+ PTRANSPORT_LIST_ENTRY Transport;
+ NTSTATUS status;
+ OEM_STRING OemAnnounceName;
+
+ (VOID)RtlAcquireResourceShared( &SsServerInfoResource, TRUE );
+
+ //
+ // Loop through each emulated server name announcing on each.
+ //
+
+ for( Service = SsServerNameList; Service != NULL; Service = Service->Next ) {
+
+ //
+ // Save the AnnounceName without trailing blanks.
+ //
+
+ OemAnnounceName.Length = (USHORT) ComputeTransportAddressClippedLength(
+ Service->TransportAddress,
+ Service->TransportAddressLength );
+
+ OemAnnounceName.MaximumLength = OemAnnounceName.Length;
+ OemAnnounceName.Buffer = Service->TransportAddress;
+
+ if( OemAnnounceName.Length == 0 ) {
+ //
+ // A blank name???
+ //
+ continue;
+ }
+
+
+ //
+ // Loop through each transport announcing on each.
+ //
+
+ for( Transport = Service->Transports; Transport != NULL; Transport = Transport->Next ) {
+
+
+ //
+ // Do the actual announcement
+ //
+
+ SsAnnounce( &OemAnnounceName,
+ Service->DomainName,
+ DoNtAnnouncement,
+ NtInterval,
+ DoLmAnnouncement,
+ TerminationAnnouncement,
+ Transport->TransportName,
+ Service->ServiceBits | Transport->ServiceBits );
+
+ }
+ }
+
+ RtlReleaseResource( &SsServerInfoResource );
+}
+
+
+
+NET_API_STATUS
+SendSecondClassMailslot (
+ IN LPTSTR Transport OPTIONAL,
+ IN PVOID Message,
+ IN DWORD MessageLength,
+ IN LPTSTR Domain,
+ IN LPSTR MailslotNameText,
+ IN UCHAR SignatureByte
+ )
+{
+ NET_API_STATUS status;
+ DWORD dataSize;
+ DWORD smbSize;
+ PSMB_HEADER header;
+ PSMB_TRANSACT_MAILSLOT parameters;
+ LPSTR mailslotName;
+ DWORD mailslotNameLength;
+ PVOID message;
+ DWORD domainLength;
+ CHAR domainName[NETBIOS_NAME_LEN];
+ PCHAR domainNamePointer;
+ PSERVER_REQUEST_PACKET srp;
+
+ UNICODE_STRING domainString;
+ OEM_STRING oemDomainString;
+
+ srp = SsAllocateSrp();
+
+ if ( srp == NULL ) {
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ RtlInitUnicodeString(&domainString, Domain);
+
+ oemDomainString.Buffer = domainName;
+ oemDomainString.MaximumLength = sizeof(domainName);
+
+ status = RtlUpcaseUnicodeStringToOemString(
+ &oemDomainString,
+ &domainString,
+ FALSE
+ );
+
+ if (!NT_SUCCESS(status)) {
+ return RtlNtStatusToDosError(status);
+ }
+
+ domainLength = oemDomainString.Length;
+
+ domainNamePointer = &domainName[domainLength];
+
+ for ( ; domainLength < NETBIOS_NAME_LEN - 1 ; domainLength++ ) {
+ *domainNamePointer++ = ' ';
+ }
+
+ //
+ // Append the signature byte to the end of the name.
+ //
+
+ *domainNamePointer = SignatureByte;
+
+ domainLength += 1;
+
+ srp->Name1.Buffer = (PWSTR)domainName;
+ srp->Name1.Length = (USHORT)domainLength;
+ srp->Name1.MaximumLength = (USHORT)domainLength;
+
+ if ( ARGUMENT_PRESENT ( Transport ) ) {
+ RtlInitUnicodeString( &srp->Name2, Transport );
+
+ } else {
+
+ srp->Name2.Buffer = NULL;
+ srp->Name2.Length = 0;
+ srp->Name2.MaximumLength = 0;
+ }
+
+ //
+ // Determine the sizes of various fields that will go in the SMB
+ // and the total size of the SMB.
+ //
+
+ mailslotNameLength = strlen( MailslotNameText );
+
+ dataSize = mailslotNameLength + 1 + MessageLength;
+ smbSize = sizeof(SMB_HEADER) + sizeof(SMB_TRANSACT_MAILSLOT) - 1 + dataSize;
+
+ //
+ // Allocate enough memory to hold the SMB. If we can't allocate the
+ // memory, don't do an announcement.
+ //
+
+ header = MIDL_user_allocate( smbSize );
+ if ( header == NULL ) {
+
+ SsFreeSrp( srp );
+
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ //
+ // Fill in the header. Most of the fields don't matter and are
+ // zeroed.
+ //
+
+ RtlZeroMemory( header, smbSize );
+
+ header->Protocol[0] = 0xFF;
+ header->Protocol[1] = 'S';
+ header->Protocol[2] = 'M';
+ header->Protocol[3] = 'B';
+ header->Command = SMB_COM_TRANSACTION;
+
+ //
+ // Get the pointer to the params and fill them in.
+ //
+
+ parameters = (PSMB_TRANSACT_MAILSLOT)( header + 1 );
+ mailslotName = (LPSTR)( parameters + 1 ) - 1;
+ message = mailslotName + mailslotNameLength + 1;
+
+ parameters->WordCount = 0x11;
+ SmbPutUshort( &parameters->TotalDataCount, (WORD)MessageLength );
+ SmbPutUlong( &parameters->Timeout, 0x3E8 ); // !!! fix
+ SmbPutUshort( &parameters->DataCount, (WORD)MessageLength );
+ SmbPutUshort(
+ &parameters->DataOffset,
+ (WORD)( (DWORD)message - (DWORD)header )
+ );
+ parameters->SetupWordCount = 3;
+ SmbPutUshort( &parameters->Opcode, MS_WRITE_OPCODE );
+ SmbPutUshort( &parameters->Class, 2 );
+ SmbPutUshort( &parameters->ByteCount, (WORD)dataSize );
+
+ RtlCopyMemory( mailslotName, MailslotNameText, mailslotNameLength + 1 );
+
+ RtlCopyMemory( message, Message, MessageLength );
+
+ status = SsServerFsControl(
+ NULL,
+ FSCTL_SRV_SEND_DATAGRAM,
+ srp,
+ header,
+ smbSize
+ );
+
+ if ( status != NERR_Success ) {
+ SS_PRINT(( "SendSecondClassMailslot: NtFsControlFile failed: %X\n",
+ status ));
+ }
+
+ MIDL_user_free( header );
+
+ SsFreeSrp( srp );
+
+ return status;
+
+} // SendSecondClassMailslot
+
+ NTSTATUS
+OpenBrowser(
+ OUT PHANDLE BrowserHandle
+ )
+/*++
+
+Routine Description:
+
+ This function opens a handle to the bowser device driver.
+
+Arguments:
+
+ OUT PHANDLE BrowserHandle - Returns the handle to the browser.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NTSTATUS ntstatus;
+
+ UNICODE_STRING deviceName;
+
+ IO_STATUS_BLOCK ioStatusBlock;
+ OBJECT_ATTRIBUTES objectAttributes;
+
+
+ //
+ // Open the redirector device.
+ //
+ RtlInitUnicodeString(&deviceName, DD_BROWSER_DEVICE_NAME_U);
+
+ InitializeObjectAttributes(
+ &objectAttributes,
+ &deviceName,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL
+ );
+
+ ntstatus = NtOpenFile(
+ BrowserHandle,
+ SYNCHRONIZE | GENERIC_READ | GENERIC_WRITE,
+ &objectAttributes,
+ &ioStatusBlock,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ FILE_SYNCHRONOUS_IO_NONALERT
+ );
+
+ if (NT_SUCCESS(ntstatus)) {
+ ntstatus = ioStatusBlock.Status;
+ }
+
+ return ntstatus;
+
+}
+
+ NET_API_STATUS
+SsBrowserIoControl (
+ IN DWORD IoControlCode,
+ IN PVOID Buffer,
+ IN DWORD BufferLength,
+ IN PLMDR_REQUEST_PACKET Packet,
+ IN DWORD PacketLength
+ )
+{
+ HANDLE browserHandle;
+ NTSTATUS status;
+ PLMDR_REQUEST_PACKET realPacket;
+ DWORD RealPacketSize;
+ DWORD bytesReturned;
+ LPBYTE Where;
+
+ //
+ // Open the browser device driver.
+ //
+
+ if ( !NT_SUCCESS(status = OpenBrowser(&browserHandle)) ) {
+ return RtlNtStatusToDosError(status);
+ }
+
+ //
+ // Now copy the request packet to a new buffer to allow us to pack the
+ // transport name to the end of the buffer we pass to the driver.
+ //
+
+ RealPacketSize = PacketLength+Packet->TransportName.MaximumLength;
+#ifdef _CAIRO_
+ RealPacketSize += Packet->EmulatedDomainName.MaximumLength;
+#endif // _CAIRO_
+ realPacket = MIDL_user_allocate( RealPacketSize );
+ if (realPacket == NULL) {
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ RtlCopyMemory(realPacket, Packet, PacketLength);
+ Where = ((LPBYTE)realPacket)+PacketLength;
+
+ if (Packet->TransportName.Length != 0) {
+
+ realPacket->TransportName.Buffer = (LPWSTR) Where;
+
+ realPacket->TransportName.MaximumLength = Packet->TransportName.MaximumLength;
+
+ RtlCopyUnicodeString(&realPacket->TransportName, &Packet->TransportName);
+
+ Where += Packet->TransportName.MaximumLength;
+ }
+
+#ifdef _CAIRO_
+ if (Packet->EmulatedDomainName.Length != 0) {
+
+ realPacket->EmulatedDomainName.Buffer = (LPWSTR) Where;
+
+ realPacket->EmulatedDomainName.MaximumLength = Packet->EmulatedDomainName.MaximumLength;
+
+ RtlCopyUnicodeString(&realPacket->EmulatedDomainName, &Packet->EmulatedDomainName);
+
+ Where += Packet->EmulatedDomainName.MaximumLength;
+ }
+#endif // _CAIRO_
+
+ //
+ // Send the request to the Datagram Receiver DD.
+ //
+
+ if (!DeviceIoControl(
+ browserHandle,
+ IoControlCode,
+ realPacket,
+ RealPacketSize,
+ Buffer,
+ BufferLength,
+ &bytesReturned,
+ NULL
+ )) {
+ status = GetLastError();
+ }
+
+ MIDL_user_free(realPacket);
+
+ CloseHandle(browserHandle);
+
+ return status;
+
+}
diff --git a/private/net/svcdlls/srvsvc/server/security.c b/private/net/svcdlls/srvsvc/server/security.c
new file mode 100644
index 000000000..970eaacac
--- /dev/null
+++ b/private/net/svcdlls/srvsvc/server/security.c
@@ -0,0 +1,902 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ security.c
+
+Abstract:
+
+ Data and routines for managing API security in the server service.
+
+Author:
+
+ David Treadwell (davidtr) 28-Aug-1991
+
+Revision History:
+
+--*/
+
+#include "srvsvcp.h"
+#include "ssdata.h"
+
+#include <lmsname.h>
+#include <netlibnt.h>
+
+#include <debugfmt.h>
+
+//
+// Global security objects.
+//
+// CharDev - NetCharDev APIs
+// ConfigInfo - NetServerGetInfo, NetServerSetInfo
+// Connection - NetConnectionEnum
+// Disk - NetServerDiskEnum
+// File - NetFile APIs
+// Session - NetSession APIs
+// Share - NetShare APIs (file, print, and admin types)
+// Statistics - NetStatisticsGet, NetStatisticsClear
+//
+
+SRVSVC_SECURITY_OBJECT SsCharDevSecurityObject = {0};
+SRVSVC_SECURITY_OBJECT SsConfigInfoSecurityObject = {0};
+SRVSVC_SECURITY_OBJECT SsConnectionSecurityObject = {0};
+SRVSVC_SECURITY_OBJECT SsDiskSecurityObject;
+SRVSVC_SECURITY_OBJECT SsFileSecurityObject = {0};
+SRVSVC_SECURITY_OBJECT SsSessionSecurityObject = {0};
+SRVSVC_SECURITY_OBJECT SsShareFileSecurityObject = {0};
+SRVSVC_SECURITY_OBJECT SsSharePrintSecurityObject = {0};
+SRVSVC_SECURITY_OBJECT SsShareAdminSecurityObject = {0};
+SRVSVC_SECURITY_OBJECT SsShareConnectSecurityObject = {0};
+SRVSVC_SECURITY_OBJECT SsShareAdmConnectSecurityObject = {0};
+SRVSVC_SECURITY_OBJECT SsStatisticsSecurityObject = {0};
+
+#ifdef SRV_COMM_DEVICES
+GENERIC_MAPPING SsCharDevMapping = {
+ STANDARD_RIGHTS_READ | // Generic read
+ SRVSVC_CHARDEV_USER_INFO_GET |
+ SRVSVC_CHARDEV_ADMIN_INFO_GET,
+ STANDARD_RIGHTS_WRITE | // Generic write
+ SRVSVC_CHARDEV_INFO_SET,
+ STANDARD_RIGHTS_EXECUTE, // Generic execute
+ SRVSVC_CHARDEV_ALL_ACCESS // Generic all
+ };
+#endif // def SRV_COMM_DEVICES
+
+GENERIC_MAPPING SsConfigInfoMapping = {
+ STANDARD_RIGHTS_READ | // Generic read
+ SRVSVC_CONFIG_USER_INFO_GET |
+ SRVSVC_CONFIG_ADMIN_INFO_GET,
+ STANDARD_RIGHTS_WRITE | // Generic write
+ SRVSVC_CONFIG_INFO_SET,
+ STANDARD_RIGHTS_EXECUTE, // Generic execute
+ SRVSVC_CONFIG_ALL_ACCESS // Generic all
+ };
+
+GENERIC_MAPPING SsConnectionMapping = {
+ STANDARD_RIGHTS_READ | // Generic read
+ SRVSVC_CONNECTION_INFO_GET,
+ STANDARD_RIGHTS_WRITE | // Generic write
+ 0,
+ STANDARD_RIGHTS_EXECUTE, // Generic execute
+ SRVSVC_CONNECTION_ALL_ACCESS // Generic all
+ };
+
+GENERIC_MAPPING SsDiskMapping = {
+ STANDARD_RIGHTS_READ | // Generic read
+ SRVSVC_DISK_ENUM,
+ STANDARD_RIGHTS_WRITE | // Generic write
+ 0,
+ STANDARD_RIGHTS_EXECUTE, // Generic execute
+ SRVSVC_DISK_ALL_ACCESS // Generic all
+ };
+
+GENERIC_MAPPING SsFileMapping = {
+ STANDARD_RIGHTS_READ | // Generic read
+ SRVSVC_FILE_INFO_GET,
+ STANDARD_RIGHTS_WRITE | // Generic write
+ SRVSVC_FILE_CLOSE,
+ STANDARD_RIGHTS_EXECUTE, // Generic execute
+ SRVSVC_FILE_ALL_ACCESS // Generic all
+ };
+
+GENERIC_MAPPING SsSessionMapping = {
+ STANDARD_RIGHTS_READ | // Generic read
+ SRVSVC_SESSION_USER_INFO_GET |
+ SRVSVC_SESSION_ADMIN_INFO_GET,
+ STANDARD_RIGHTS_WRITE | // Generic write
+ SRVSVC_SESSION_DELETE,
+ STANDARD_RIGHTS_EXECUTE, // Generic execute
+ SRVSVC_SESSION_ALL_ACCESS // Generic all
+ };
+
+GENERIC_MAPPING SsShareMapping = {
+ STANDARD_RIGHTS_READ | // Generic read
+ SRVSVC_SHARE_USER_INFO_GET |
+ SRVSVC_SHARE_ADMIN_INFO_GET,
+ STANDARD_RIGHTS_WRITE | // Generic write
+ SRVSVC_SHARE_INFO_SET,
+ STANDARD_RIGHTS_EXECUTE | // Generic execute
+ SRVSVC_SHARE_CONNECT,
+ SRVSVC_SHARE_ALL_ACCESS // Generic all
+ };
+
+GENERIC_MAPPING SsShareConnectMapping = GENERIC_SHARE_CONNECT_MAPPING;
+
+GENERIC_MAPPING SsStatisticsMapping = {
+ STANDARD_RIGHTS_READ | // Generic read
+ SRVSVC_STATISTICS_GET,
+ STANDARD_RIGHTS_WRITE, // Generic write
+ STANDARD_RIGHTS_EXECUTE, // Generic execute
+ SRVSVC_STATISTICS_ALL_ACCESS // Generic all
+ };
+
+//
+// Forward declarations.
+//
+
+NET_API_STATUS
+CreateSecurityObject (
+ PSRVSVC_SECURITY_OBJECT SecurityObject,
+ LPTSTR ObjectName,
+ PGENERIC_MAPPING Mapping,
+ PACE_DATA AceData,
+ ULONG AceDataLength
+ );
+
+#ifdef SRV_COMM_DEVICES
+NET_API_STATUS
+CreateCharDevSecurityObject (
+ VOID
+ );
+#endif
+
+NET_API_STATUS
+CreateConfigInfoSecurityObject (
+ VOID
+ );
+
+NET_API_STATUS
+CreateConnectionSecurityObject (
+ VOID
+ );
+
+NET_API_STATUS
+CreateDiskSecurityObject (
+ VOID
+ );
+
+NET_API_STATUS
+CreateFileSecurityObject (
+ VOID
+ );
+
+NET_API_STATUS
+CreateSessionSecurityObject (
+ VOID
+ );
+
+NET_API_STATUS
+CreateShareSecurityObjects (
+ VOID
+ );
+
+NET_API_STATUS
+CreateStatisticsSecurityObject (
+ VOID
+ );
+
+VOID
+DeleteSecurityObject (
+ PSRVSVC_SECURITY_OBJECT SecurityObject
+ );
+
+
+NET_API_STATUS
+SsCreateSecurityObjects (
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ Sets up the objects that will be used for security in the server
+ service APIs.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NET_API_STATUS - NO_ERROR or reason for failure.
+
+--*/
+
+{
+ NET_API_STATUS status;
+
+#ifdef SRV_COMM_DEVICES
+ //
+ // Create CharDev security object.
+ //
+
+ status = CreateCharDevSecurityObject( );
+ if ( status != NO_ERROR ) {
+ return status;
+ }
+#endif
+
+ //
+ // Create ConfigInfo security object.
+ //
+
+ status = CreateConfigInfoSecurityObject( );
+ if ( status != NO_ERROR ) {
+ return status;
+ }
+
+ //
+ // Create Connection security object.
+ //
+
+ status = CreateConnectionSecurityObject( );
+ if ( status != NO_ERROR ) {
+ return status;
+ }
+
+ //
+ // Create Disk security object.
+ //
+
+ status = CreateDiskSecurityObject( );
+ if ( status != NO_ERROR ) {
+ return status;
+ }
+
+ //
+ // Create File security object.
+ //
+
+ status = CreateFileSecurityObject( );
+ if ( status != NO_ERROR ) {
+ return status;
+ }
+
+ //
+ // Create Session security object.
+ //
+
+ status = CreateSessionSecurityObject( );
+ if ( status != NO_ERROR ) {
+ return status;
+ }
+
+ //
+ // Create Share security object.
+ //
+
+ status = CreateShareSecurityObjects( );
+ if ( status != NO_ERROR ) {
+ return status;
+ }
+
+ //
+ // Create Statistics security object.
+ //
+
+ status = CreateStatisticsSecurityObject( );
+ if ( status != NO_ERROR ) {
+ return status;
+ }
+
+ return NO_ERROR;
+
+} // SsCreateSecurityObjects
+
+
+VOID
+SsDeleteSecurityObjects (
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ Deletes server service security objects.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+#ifdef SRV_COMM_DEVICES
+ //
+ // Delete CharDev security object.
+ //
+
+ DeleteSecurityObject( &SsCharDevSecurityObject );
+#endif
+
+ //
+ // Delete ConfigInfo security object.
+ //
+
+ DeleteSecurityObject( &SsConfigInfoSecurityObject );
+
+ //
+ // Delete Connection security object.
+ //
+
+ DeleteSecurityObject( &SsConnectionSecurityObject );
+
+ //
+ // Delete File security object.
+ //
+
+ DeleteSecurityObject( &SsFileSecurityObject );
+
+ //
+ // Delete Session security object.
+ //
+
+ DeleteSecurityObject( &SsSessionSecurityObject );
+
+ //
+ // Delete Share security objects.
+ //
+
+ DeleteSecurityObject( &SsShareFileSecurityObject );
+ DeleteSecurityObject( &SsSharePrintSecurityObject );
+ DeleteSecurityObject( &SsShareAdminSecurityObject );
+ DeleteSecurityObject( &SsShareConnectSecurityObject );
+ DeleteSecurityObject( &SsShareAdmConnectSecurityObject );
+
+ //
+ // Delete Statistics security object.
+ //
+
+ DeleteSecurityObject( &SsStatisticsSecurityObject );
+
+ return;
+
+} // SsDeleteSecurityObjects
+
+
+NET_API_STATUS
+SsCheckAccess (
+ IN PSRVSVC_SECURITY_OBJECT SecurityObject,
+ IN ACCESS_MASK DesiredAccess
+ )
+
+/*++
+
+Routine Description:
+
+ Calls NetpAccessCheckAndAudit to verify that the caller of an API
+ has the necessary access to perform the requested operation.
+
+Arguments:
+
+ SecurityObject - a pointer to the server service security object
+ that describes the security on the relevant object.
+
+ DesiredAccess - the access needed to perform the requested operation.
+
+Return Value:
+
+ NET_API_STATUS - NO_ERROR or reason for failure.
+
+--*/
+
+{
+ NET_API_STATUS error;
+
+ IF_DEBUG(SECURITY) {
+ SS_PRINT(( "SsCheckAccess: validating object " FORMAT_LPTSTR ", "
+ "access %lx\n",
+ SecurityObject->ObjectName, DesiredAccess ));
+ }
+
+ error = NetpAccessCheckAndAudit(
+ SERVER_DISPLAY_NAME,
+ SecurityObject->ObjectName,
+ SecurityObject->SecurityDescriptor,
+ DesiredAccess,
+ SecurityObject->Mapping
+ );
+
+ if ( error != NO_ERROR ) {
+ IF_DEBUG(ACCESS_DENIED) {
+ SS_PRINT(( "SsCheckAccess: NetpAccessCheckAndAudit failed for "
+ "object " FORMAT_LPTSTR ", access %lx: %ld\n",
+ SecurityObject->ObjectName, DesiredAccess, error ));
+ }
+ } else {
+ IF_DEBUG(SECURITY) {
+ SS_PRINT(( "SsCheckAccess: allowed access for " FORMAT_LPTSTR ", "
+ "access %lx\n",
+ SecurityObject->ObjectName, DesiredAccess ));
+ }
+ }
+
+ return error;
+
+} // SsCheckAccess
+
+
+NET_API_STATUS
+CreateSecurityObject (
+ PSRVSVC_SECURITY_OBJECT SecurityObject,
+ LPTSTR ObjectName,
+ PGENERIC_MAPPING Mapping,
+ PACE_DATA AceData,
+ ULONG AceDataLength
+ )
+{
+ NTSTATUS status;
+
+ //
+ // Set up security object.
+ //
+
+ SecurityObject->ObjectName = ObjectName;
+ SecurityObject->Mapping = Mapping;
+
+ //
+ // Create security descriptor.
+ //
+
+ status = NetpCreateSecurityObject(
+ AceData,
+ AceDataLength,
+ SsLmsvcsGlobalData->LocalSystemSid,
+ SsLmsvcsGlobalData->LocalSystemSid,
+ Mapping,
+ &SecurityObject->SecurityDescriptor
+ );
+
+ if ( !NT_SUCCESS(status) ) {
+
+ IF_DEBUG(INITIALIZATION_ERRORS) {
+ SS_PRINT(( "CreateSecurityObject: failed to create " FORMAT_LPTSTR
+ " object: %lx\n", ObjectName, status ));
+ }
+
+ return NetpNtStatusToApiStatus( status );
+ }
+
+ IF_DEBUG(INITIALIZATION) {
+ SS_PRINT(( "CreateSecurityObject: created " FORMAT_LPTSTR " object.\n",
+ ObjectName ));
+ }
+
+ return NO_ERROR;
+
+} // CreateSecurityObject
+
+
+#ifdef SRV_COMM_DEVICES
+NET_API_STATUS
+CreateCharDevSecurityObject (
+ VOID
+ )
+{
+ //
+ // Required access for getting and setting character device information.
+ //
+
+ ACE_DATA CharDevAceData[] = {
+ {ACCESS_ALLOWED_ACE_TYPE, 0, 0,
+ GENERIC_ALL, &SsLmsvcsGlobalData->AliasAdminsSid},
+ {ACCESS_ALLOWED_ACE_TYPE, 0, 0,
+ GENERIC_ALL, &SsLmsvcsGlobalData->AliasSystemOpsSid},
+ {ACCESS_ALLOWED_ACE_TYPE, 0, 0,
+ GENERIC_ALL, &SsLmsvcsGlobalData->AliasCommOpsSid},
+ {ACCESS_ALLOWED_ACE_TYPE, 0, 0,
+ SRVSVC_CHARDEV_USER_INFO_GET, &SsLmsvcsGlobalData->WorldSid}
+ };
+
+ //
+ // Create CharDev security object.
+ //
+
+ return CreateSecurityObject(
+ &SsCharDevSecurityObject,
+ SRVSVC_CHARDEV_OBJECT,
+ &SsCharDevMapping,
+ &CharDevAceData,
+ sizeof(CharDevAceData) / sizeof(ACE_DATA)
+ );
+
+} // CreateCharDevSecurityObject
+#endif
+
+
+NET_API_STATUS
+CreateConfigInfoSecurityObject (
+ VOID
+ )
+{
+ //
+ // Required access for getting and setting server information.
+ //
+
+ ACE_DATA ConfigInfoAceData[] = {
+
+ {ACCESS_ALLOWED_ACE_TYPE, 0, 0,
+ GENERIC_ALL, &SsLmsvcsGlobalData->AliasAdminsSid},
+ {ACCESS_ALLOWED_ACE_TYPE, 0, 0,
+ GENERIC_ALL, &SsLmsvcsGlobalData->AliasSystemOpsSid},
+ {ACCESS_ALLOWED_ACE_TYPE, 0, 0,
+ SRVSVC_CONFIG_USER_INFO_GET | SRVSVC_CONFIG_POWER_INFO_GET,
+ &SsLmsvcsGlobalData->AliasPowerUsersSid},
+ {ACCESS_ALLOWED_ACE_TYPE, 0, 0,
+ SRVSVC_CONFIG_USER_INFO_GET, &SsLmsvcsGlobalData->WorldSid}
+ };
+
+ //
+ // Create ConfigInfo security object.
+ //
+
+ return CreateSecurityObject(
+ &SsConfigInfoSecurityObject,
+ SRVSVC_CONFIG_INFO_OBJECT,
+ &SsConfigInfoMapping,
+ ConfigInfoAceData,
+ sizeof(ConfigInfoAceData) / sizeof(ACE_DATA)
+ );
+
+} // CreateConfigInfoSecurityObject
+
+
+NET_API_STATUS
+CreateConnectionSecurityObject (
+ VOID
+ )
+{
+ //
+ // Required access for getting and setting Connection information.
+ //
+
+ ACE_DATA ConnectionAceData[] = {
+
+ {ACCESS_ALLOWED_ACE_TYPE, 0, 0,
+ GENERIC_ALL, &SsLmsvcsGlobalData->AliasAdminsSid},
+ {ACCESS_ALLOWED_ACE_TYPE, 0, 0,
+ GENERIC_ALL, &SsLmsvcsGlobalData->AliasSystemOpsSid},
+ {ACCESS_ALLOWED_ACE_TYPE, 0, 0,
+ SRVSVC_CONNECTION_INFO_GET, &SsLmsvcsGlobalData->AliasPrintOpsSid},
+ {ACCESS_ALLOWED_ACE_TYPE, 0, 0,
+ SRVSVC_CONNECTION_INFO_GET, &SsLmsvcsGlobalData->AliasPowerUsersSid}
+ };
+
+ //
+ // Create Connection security object.
+ //
+
+ return CreateSecurityObject(
+ &SsConnectionSecurityObject,
+ SRVSVC_CONNECTION_OBJECT,
+ &SsConnectionMapping,
+ ConnectionAceData,
+ sizeof(ConnectionAceData) / sizeof(ACE_DATA)
+ );
+
+ return NO_ERROR;
+
+} // CreateConnectionSecurityObject
+
+
+NET_API_STATUS
+CreateDiskSecurityObject (
+ VOID
+ )
+{
+ //
+ // Required access for doing Disk enums
+ //
+
+ ACE_DATA DiskAceData[] = {
+
+ {ACCESS_ALLOWED_ACE_TYPE, 0, 0,
+ GENERIC_ALL, &SsLmsvcsGlobalData->AliasAdminsSid},
+ {ACCESS_ALLOWED_ACE_TYPE, 0, 0,
+ GENERIC_ALL, &SsLmsvcsGlobalData->AliasSystemOpsSid}
+ };
+
+ //
+ // Create Disk security object.
+ //
+
+ return CreateSecurityObject(
+ &SsDiskSecurityObject,
+ SRVSVC_DISK_OBJECT,
+ &SsDiskMapping,
+ DiskAceData,
+ sizeof(DiskAceData) / sizeof(ACE_DATA)
+ );
+
+} // CreateDiskSecurityObject
+
+
+NET_API_STATUS
+CreateFileSecurityObject (
+ VOID
+ )
+{
+ //
+ // Required access for getting and setting File information.
+ //
+
+ ACE_DATA FileAceData[] = {
+
+ {ACCESS_ALLOWED_ACE_TYPE, 0, 0,
+ GENERIC_ALL, &SsLmsvcsGlobalData->AliasAdminsSid},
+ {ACCESS_ALLOWED_ACE_TYPE, 0, 0,
+ GENERIC_ALL, &SsLmsvcsGlobalData->AliasSystemOpsSid},
+ {ACCESS_ALLOWED_ACE_TYPE, 0, 0,
+ GENERIC_ALL, &SsLmsvcsGlobalData->AliasPowerUsersSid}
+ };
+
+ //
+ // Create File security object.
+ //
+
+ return CreateSecurityObject(
+ &SsFileSecurityObject,
+ SRVSVC_FILE_OBJECT,
+ &SsFileMapping,
+ FileAceData,
+ sizeof(FileAceData) / sizeof(ACE_DATA)
+ );
+
+} // CreateFileSecurityObject
+
+
+NET_API_STATUS
+CreateSessionSecurityObject (
+ VOID
+ )
+{
+ //
+ // Required access for getting and setting session information.
+ //
+
+ ACE_DATA SessionAceData[] = {
+
+ {ACCESS_ALLOWED_ACE_TYPE, 0, 0,
+ GENERIC_ALL, &SsLmsvcsGlobalData->AliasAdminsSid},
+ {ACCESS_ALLOWED_ACE_TYPE, 0, 0,
+ GENERIC_ALL, &SsLmsvcsGlobalData->AliasSystemOpsSid},
+ {ACCESS_ALLOWED_ACE_TYPE, 0, 0,
+ GENERIC_ALL, &SsLmsvcsGlobalData->AliasPowerUsersSid},
+ {ACCESS_ALLOWED_ACE_TYPE, 0, 0,
+ SRVSVC_SESSION_USER_INFO_GET, &SsLmsvcsGlobalData->WorldSid}
+ };
+
+ //
+ // Create Session security object.
+ //
+
+ return CreateSecurityObject(
+ &SsSessionSecurityObject,
+ SRVSVC_SESSION_OBJECT,
+ &SsSessionMapping,
+ SessionAceData,
+ sizeof(SessionAceData) / sizeof(ACE_DATA)
+ );
+
+} // CreateSessionSecurityObject
+
+
+NET_API_STATUS
+CreateShareSecurityObjects (
+ VOID
+ )
+{
+ NET_API_STATUS status;
+
+ //
+ // Required access for getting and setting share information.
+ //
+
+ ACE_DATA ShareFileAceData[] = {
+
+ {ACCESS_ALLOWED_ACE_TYPE, 0, 0,
+ GENERIC_ALL, &SsLmsvcsGlobalData->AliasAdminsSid},
+ {ACCESS_ALLOWED_ACE_TYPE, 0, 0,
+ GENERIC_ALL, &SsLmsvcsGlobalData->AliasSystemOpsSid},
+ {ACCESS_ALLOWED_ACE_TYPE, 0, 0,
+ GENERIC_ALL, &SsLmsvcsGlobalData->AliasPowerUsersSid},
+ {ACCESS_ALLOWED_ACE_TYPE, 0, 0,
+ SRVSVC_SHARE_USER_INFO_GET, &SsLmsvcsGlobalData->WorldSid}
+ };
+
+ ACE_DATA SharePrintAceData[] = {
+
+ {ACCESS_ALLOWED_ACE_TYPE, 0, 0,
+ GENERIC_ALL, &SsLmsvcsGlobalData->AliasAdminsSid},
+ {ACCESS_ALLOWED_ACE_TYPE, 0, 0,
+ GENERIC_ALL, &SsLmsvcsGlobalData->AliasSystemOpsSid},
+ {ACCESS_ALLOWED_ACE_TYPE, 0, 0,
+ GENERIC_ALL, &SsLmsvcsGlobalData->AliasPrintOpsSid},
+ {ACCESS_ALLOWED_ACE_TYPE, 0, 0,
+ GENERIC_ALL, &SsLmsvcsGlobalData->AliasPowerUsersSid},
+ {ACCESS_ALLOWED_ACE_TYPE, 0, 0,
+ SRVSVC_SHARE_USER_INFO_GET, &SsLmsvcsGlobalData->WorldSid}
+ };
+
+ ACE_DATA ShareAdminAceData[] = {
+
+ {ACCESS_ALLOWED_ACE_TYPE, 0, 0,
+ GENERIC_ALL, &SsLmsvcsGlobalData->AliasAdminsSid},
+ {ACCESS_ALLOWED_ACE_TYPE, 0, 0,
+ SRVSVC_SHARE_ADMIN_INFO_GET,
+ &SsLmsvcsGlobalData->AliasSystemOpsSid},
+ {ACCESS_ALLOWED_ACE_TYPE, 0, 0,
+ SRVSVC_SHARE_ADMIN_INFO_GET,
+ &SsLmsvcsGlobalData->AliasPowerUsersSid},
+ {ACCESS_ALLOWED_ACE_TYPE, 0, 0,
+ SRVSVC_SHARE_USER_INFO_GET, &SsLmsvcsGlobalData->WorldSid}
+ };
+
+ ACE_DATA ShareConnectAceData[] = {
+
+ {ACCESS_ALLOWED_ACE_TYPE, 0, 0,
+ GENERIC_ALL, &SsLmsvcsGlobalData->AliasAdminsSid},
+ {ACCESS_ALLOWED_ACE_TYPE, 0, 0,
+ GENERIC_ALL, &SsLmsvcsGlobalData->AliasSystemOpsSid},
+ {ACCESS_ALLOWED_ACE_TYPE, 0, 0,
+ GENERIC_ALL, &SsLmsvcsGlobalData->AliasBackupOpsSid},
+ {ACCESS_ALLOWED_ACE_TYPE, 0, 0,
+ SRVSVC_SHARE_CONNECT, &SsLmsvcsGlobalData->WorldSid}
+ };
+
+ ACE_DATA ShareAdmConnectAceData[] = {
+
+ {ACCESS_ALLOWED_ACE_TYPE, 0, 0,
+ GENERIC_ALL, &SsLmsvcsGlobalData->AliasAdminsSid},
+ {ACCESS_ALLOWED_ACE_TYPE, 0, 0,
+ GENERIC_ALL, &SsLmsvcsGlobalData->AliasSystemOpsSid},
+ {ACCESS_ALLOWED_ACE_TYPE, 0, 0,
+ GENERIC_ALL, &SsLmsvcsGlobalData->AliasBackupOpsSid}
+ };
+
+ //
+ // Create Share security objects.
+ //
+
+ status = CreateSecurityObject(
+ &SsShareFileSecurityObject,
+ SRVSVC_SHARE_FILE_OBJECT,
+ &SsShareMapping,
+ ShareFileAceData,
+ sizeof(ShareFileAceData) / sizeof(ACE_DATA)
+ );
+ if ( status != NO_ERROR ) {
+ return status;
+ }
+
+ status = CreateSecurityObject(
+ &SsSharePrintSecurityObject,
+ SRVSVC_SHARE_PRINT_OBJECT,
+ &SsShareMapping,
+ SharePrintAceData,
+ sizeof(SharePrintAceData) / sizeof(ACE_DATA)
+ );
+ if ( status != NO_ERROR ) {
+ return status;
+ }
+
+ status = CreateSecurityObject(
+ &SsShareAdminSecurityObject,
+ SRVSVC_SHARE_ADMIN_OBJECT,
+ &SsShareMapping,
+ ShareAdminAceData,
+ sizeof(ShareAdminAceData) / sizeof(ACE_DATA)
+ );
+ if ( status != NO_ERROR ) {
+ return status;
+ }
+
+ status = CreateSecurityObject(
+ &SsShareConnectSecurityObject,
+ SRVSVC_SHARE_CONNECT_OBJECT,
+ &SsShareConnectMapping,
+ ShareConnectAceData,
+ sizeof(ShareConnectAceData) / sizeof(ACE_DATA)
+ );
+ if ( status != NO_ERROR ) {
+ return status;
+ }
+
+ return CreateSecurityObject(
+ &SsShareAdmConnectSecurityObject,
+ SRVSVC_SHARE_ADM_CONNECT_OBJECT,
+ &SsShareConnectMapping,
+ ShareAdmConnectAceData,
+ sizeof(ShareAdmConnectAceData) / sizeof(ACE_DATA)
+ );
+
+} // CreateShareSecurityObjects
+
+
+NET_API_STATUS
+CreateStatisticsSecurityObject (
+ VOID
+ )
+{
+ //
+ // Required access for getting and setting Statistics information.
+ //
+
+ ACE_DATA StatisticsAceData[] = {
+
+ {ACCESS_ALLOWED_ACE_TYPE, 0, 0,
+ GENERIC_ALL, &SsLmsvcsGlobalData->AliasAdminsSid},
+ {ACCESS_ALLOWED_ACE_TYPE, 0, 0,
+ GENERIC_ALL, &SsLmsvcsGlobalData->AliasSystemOpsSid},
+ {ACCESS_ALLOWED_ACE_TYPE, 0, 0,
+ SRVSVC_STATISTICS_GET, &SsLmsvcsGlobalData->LocalSid}
+ };
+
+ //
+ // Create Statistics security object.
+ //
+
+ return CreateSecurityObject(
+ &SsStatisticsSecurityObject,
+ SRVSVC_STATISTICS_OBJECT,
+ &SsStatisticsMapping,
+ StatisticsAceData,
+ sizeof(StatisticsAceData) / sizeof(ACE_DATA)
+ );
+
+} // CreateStatisticsSecurityObject
+
+
+VOID
+DeleteSecurityObject (
+ PSRVSVC_SECURITY_OBJECT SecurityObject
+ )
+{
+ NTSTATUS status;
+
+ if ( SecurityObject->SecurityDescriptor != NULL ) {
+
+ status = NetpDeleteSecurityObject(
+ &SecurityObject->SecurityDescriptor
+ );
+ SecurityObject->SecurityDescriptor = NULL;
+
+ if ( !NT_SUCCESS(status) ) {
+ IF_DEBUG(TERMINATION_ERRORS) {
+ SS_PRINT(( "DeleteSecurityObject: failed to delete "
+ FORMAT_LPTSTR " object: %X\n",
+ SecurityObject->ObjectName,
+ status ));
+ }
+ } else {
+ IF_DEBUG(TERMINATION) {
+ SS_PRINT(( "DeleteSecurityObject: deleted " FORMAT_LPTSTR
+ " object.\n",
+ SecurityObject->ObjectName ));
+ }
+ }
+
+ }
+
+ return;
+
+} // DeleteSecurityObject
+
diff --git a/private/net/svcdlls/srvsvc/server/sess.c b/private/net/svcdlls/srvsvc/server/sess.c
new file mode 100644
index 000000000..f3b7aaba0
--- /dev/null
+++ b/private/net/svcdlls/srvsvc/server/sess.c
@@ -0,0 +1,293 @@
+/*++
+
+Copyright (c) 1991-1992 Microsoft Corporation
+
+Module Name:
+
+ Sess.c
+
+Abstract:
+
+ This module contains support for the Session catagory of APIs for the
+ NT server service.
+
+Author:
+
+ David Treadwell (davidtr) 30-Jan-1991
+
+Revision History:
+
+--*/
+
+#include "srvsvcp.h"
+#include <lmerr.h>
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetrSessionDel (
+ IN LPTSTR ServerName,
+ IN LPTSTR ClientName OPTIONAL,
+ IN LPTSTR UserName OPTIONAL
+ )
+
+/*++
+
+Routine Description:
+
+ This routine communicates with the server FSD to implement the
+ NetSessionDel function.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NET_API_STATUS - NO_ERROR or reason for failure.
+
+--*/
+
+{
+ NET_API_STATUS error;
+ PSERVER_REQUEST_PACKET srp;
+
+ ServerName;
+
+ //
+ // Make sure that the caller has the access necessary for this
+ // operation.
+ //
+
+ error = SsCheckAccess(
+ &SsSessionSecurityObject,
+ SRVSVC_SESSION_DELETE
+ );
+
+ if ( error != NO_ERROR ) {
+ return ERROR_ACCESS_DENIED;
+ }
+
+ //
+ // Translate zero-length strings to NULL pointers.
+ //
+
+ if ( (ClientName != NULL) && (*ClientName == L'\0') ) {
+ ClientName = NULL;
+ }
+
+ if ( (UserName != NULL) && (*UserName == L'\0') ) {
+ UserName = NULL;
+ }
+
+ //
+ // Either the client name or the user name must be specified. It
+ // is not legal to leave both NULL, as this would imply "log out all
+ // users." If that's what you want, stop the server.
+ //
+
+ if ( ClientName == NULL && UserName == NULL ) {
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ //
+ // Set up the request packet.
+ //
+
+ srp = SsAllocateSrp( );
+ if ( srp == NULL ) {
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ RtlInitUnicodeString( &srp->Name1, ClientName );
+ RtlInitUnicodeString( &srp->Name2, UserName );
+
+ //
+ // Simply send the request on to the server.
+ //
+
+ error = SsServerFsControl( NULL, FSCTL_SRV_NET_SESSION_DEL, srp, NULL, 0 );
+
+ SsFreeSrp( srp );
+
+ return error;
+
+} // NetrSessionDel
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetrSessionEnum (
+ IN LPTSTR ServerName,
+ IN LPTSTR ClientName OPTIONAL,
+ IN LPTSTR UserName OPTIONAL,
+ OUT PSESSION_ENUM_STRUCT InfoStruct,
+ IN DWORD PreferredMaximumLength,
+ OUT LPDWORD TotalEntries,
+ IN OUT LPDWORD ResumeHandle OPTIONAL
+ )
+
+/*++
+
+Routine Description:
+
+ This routine communicates with the server FSD to implement the
+ NetSessionEnum function.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NET_API_STATUS - NO_ERROR or reason for failure.
+
+--*/
+
+{
+ NET_API_STATUS error;
+ PSERVER_REQUEST_PACKET srp;
+ ACCESS_MASK desiredAccess;
+
+ ServerName;
+
+ //
+ // Make sure we have basic sanity on our input parameters
+ //
+ if( !ARGUMENT_PRESENT( InfoStruct ) ) {
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ //
+ // Make sure that the level is valid and determine the access
+ // necessary for the level.
+ //
+
+ switch ( InfoStruct->Level ) {
+
+ case 0:
+ case 10:
+ desiredAccess = SRVSVC_SESSION_USER_INFO_GET;
+ break;
+
+ case 1:
+ case 2:
+ case 502:
+ desiredAccess = SRVSVC_SESSION_ADMIN_INFO_GET;
+ break;
+
+ default:
+
+ return ERROR_INVALID_LEVEL;
+ }
+
+ if( InfoStruct->SessionInfo.Level2 == NULL ) {
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ //
+ // Make sure that the caller has the access necessary for this
+ // operation.
+ //
+
+ error = SsCheckAccess(
+ &SsSessionSecurityObject,
+ desiredAccess
+ );
+
+ if ( error != NO_ERROR ) {
+ return ERROR_ACCESS_DENIED;
+ }
+
+ //
+ // Translate zero-length strings to NULL pointers.
+ //
+
+ if ( (ClientName != NULL) && (*ClientName == L'\0') ) {
+ ClientName = NULL;
+ }
+
+ if ( (UserName != NULL) && (*UserName == L'\0') ) {
+ UserName = NULL;
+ }
+
+ //
+ // Is a client name was specified, make sure client name starts with "\\"
+ //
+
+ if ( ARGUMENT_PRESENT( ClientName ) &&
+ (ClientName[0] != L'\\' || ClientName[1] != L'\\' ) ) {
+
+ return(NERR_InvalidComputer);
+ }
+
+ //
+ // Set up the input parameters in the request buffer.
+ //
+
+ srp = SsAllocateSrp( );
+ if ( srp == NULL ) {
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+ srp->Level = InfoStruct->Level;
+
+ RtlInitUnicodeString( &srp->Name1, ClientName );
+ RtlInitUnicodeString( &srp->Name2, UserName );
+
+ if ( ARGUMENT_PRESENT( ResumeHandle ) ) {
+ srp->Parameters.Get.ResumeHandle = *ResumeHandle;
+ } else {
+ srp->Parameters.Get.ResumeHandle = 0;
+ }
+
+ //
+ // Get the data from the server. This routine will allocate the
+ // return buffer and handle the case where PreferredMaximumLength ==
+ // -1.
+ //
+
+ error = SsServerFsControlGetInfo(
+ FSCTL_SRV_NET_SESSION_ENUM,
+ srp,
+ (PVOID *)&InfoStruct->SessionInfo.Level2->Buffer,
+ PreferredMaximumLength
+ );
+
+ //
+ // Set up return information.
+ //
+
+ InfoStruct->SessionInfo.Level2->EntriesRead =
+ srp->Parameters.Get.EntriesRead;
+
+ if ( ARGUMENT_PRESENT( TotalEntries ) ) {
+ *TotalEntries = srp->Parameters.Get.TotalEntries;
+ }
+
+ if ( srp->Parameters.Get.EntriesRead > 0 ) {
+
+ if ( ARGUMENT_PRESENT( ResumeHandle ) ) {
+ *ResumeHandle = srp->Parameters.Get.ResumeHandle;
+ }
+
+ } else if ( *TotalEntries == 0 ) {
+
+ //
+ // Entries read and total entries is 0. If a client name or
+ // username was specified, return the appropriate error.
+ //
+
+ if ( ARGUMENT_PRESENT( UserName ) ) {
+
+ error = NERR_UserNotFound;
+
+ } else if ( ARGUMENT_PRESENT( ClientName ) ) {
+
+ error = NERR_ClientNameNotFound;
+ }
+ }
+
+ SsFreeSrp( srp );
+
+ return error;
+
+} // NetrSessionEnum
+
diff --git a/private/net/svcdlls/srvsvc/server/share.c b/private/net/svcdlls/srvsvc/server/share.c
new file mode 100644
index 000000000..3422c1ca3
--- /dev/null
+++ b/private/net/svcdlls/srvsvc/server/share.c
@@ -0,0 +1,2897 @@
+/*++
+
+Copyright (c) 1991-1992 Microsoft Corporation
+
+Module Name:
+
+ Share.c
+
+Abstract:
+
+ This module contains support for the Share catagory of APIs for the
+ NT server service.
+
+Author:
+
+ David Treadwell (davidtr) 10-Jan-1991
+
+Revision History:
+
+--*/
+
+#include "srvsvcp.h"
+#include "ssdata.h"
+#include "ssreg.h"
+
+#include <lmaccess.h>
+#include <lmerr.h>
+#include <ntddnfs.h>
+#include <tstr.h>
+#include <netevent.h>
+#include <icanon.h>
+
+#define SET_ERROR_PARAMETER(a) \
+ if ( ARGUMENT_PRESENT( ErrorParameter ) ) { *ErrorParameter = a; }
+
+//
+// Use the same directory separator as the object system uses.
+//
+
+#define IS_SLASH_SLASH_NAME( _x ) \
+ ( IS_PATH_SEPARATOR( _x[0] ) && \
+ IS_PATH_SEPARATOR( _x[1] ) && \
+ _x[2] == L'.' && \
+ IS_PATH_SEPARATOR( _x[3] ) )
+
+
+GENERIC_MAPPING SrvShareFileGenericMapping = GENERIC_SHARE_FILE_ACCESS_MAPPING;
+
+//
+// Local types.
+//
+
+typedef struct _SHARE_DEL_CONTEXT {
+ SERVER_REQUEST_PACKET Srp;
+ BOOL IsPrintShare;
+ BOOL IsSpecial;
+ //WCHAR NetName[];
+} SHARE_DEL_CONTEXT, *PSHARE_DEL_CONTEXT;
+
+//
+// Forward declarations.
+//
+
+PVOID
+CaptureShareInfo (
+ IN DWORD Level,
+ IN PSHARE_INFO_2 Shi2,
+ IN DWORD ShareType,
+ IN LPWSTR Path,
+ IN LPWSTR Remark,
+ IN PSECURITY_DESCRIPTOR ConnectSecurityDescriptor,
+ IN PSECURITY_DESCRIPTOR FileSecurityDescriptor OPTIONAL,
+ OUT PULONG CapturedBufferLength
+ );
+
+NET_API_STATUS
+DisallowSharedLanmanNetDrives(
+ IN PUNICODE_STRING NtSharePath
+ );
+
+NET_API_STATUS
+ShareAssignSecurityDescriptor(
+ IN PSECURITY_DESCRIPTOR PassedSecurityDescriptor,
+ OUT PSECURITY_DESCRIPTOR *NewSecurityDescriptor
+ );
+
+NET_API_STATUS
+ShareEnumCommon (
+ IN DWORD Level,
+ OUT LPBYTE *Buffer,
+ IN DWORD PreferredMaximumLength,
+ OUT LPDWORD EntriesRead,
+ OUT LPDWORD TotalEntries,
+ IN OUT LPDWORD ResumeHandle OPTIONAL,
+ IN LPWSTR NetName OPTIONAL
+ );
+
+NET_API_STATUS
+ShareEnumSticky (
+ IN DWORD Level,
+ OUT LPBYTE *Buffer,
+ IN DWORD PreferredMaximumLength,
+ OUT LPDWORD EntriesRead,
+ OUT LPDWORD TotalEntries,
+ IN OUT LPDWORD ResumeHandle OPTIONAL
+ );
+
+ULONG
+SizeShares (
+ IN ULONG Level,
+ IN PSHARE_INFO_502 Shi502
+ );
+
+BOOLEAN
+ValidSharePath(
+ IN LPWSTR SharePath
+ );
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetrShareAdd (
+ IN LPWSTR ServerName,
+ IN DWORD Level,
+ IN LPSHARE_INFO Buffer,
+ OUT LPDWORD ErrorParameter
+ )
+
+/*++
+
+Routine Description:
+
+ This routine communicates with the server FSD to implement the
+ NetShareAdd function. Only levels 2 and 502 are valid.
+
+Arguments:
+
+ ServerName - name of the server.
+ Level - Request level.
+ Buffer - Contains the information about the share. If this is a level
+ 502 request, will also contain a valid security descriptor in
+ self-relative form.
+ ErrorParameter - status of the FsControl call.
+
+Return Value:
+
+ NET_API_STATUS - NO_ERROR or reason for failure.
+
+--*/
+
+{
+ NET_API_STATUS error;
+ NTSTATUS status;
+ PSERVER_REQUEST_PACKET srp;
+ PVOID capturedBuffer;
+ LPWSTR path;
+ LPWSTR netName;
+ LPWSTR remark;
+ ULONG bufferLength;
+ UNICODE_STRING dosSharePath;
+ UNICODE_STRING ntSharePath;
+
+ PSRVSVC_SECURITY_OBJECT securityObject;
+ PSECURITY_DESCRIPTOR connectSecurityDescriptor;
+ PSECURITY_DESCRIPTOR fileSecurityDescriptor = NULL;
+ PSECURITY_DESCRIPTOR newFileSecurityDescriptor = NULL;
+
+ UINT driveType = DRIVE_FIXED;
+ DWORD shareType;
+
+ BOOL isIpc;
+ BOOL isAdmin;
+ BOOL isDiskAdmin;
+ BOOL isPrintShare;
+ BOOL isSpecial;
+
+ PSHARE_INFO_2 shi2;
+ PSHARE_INFO_502 shi502;
+
+ ServerName;
+
+ //
+ // Check that user input buffer is not NULL
+ //
+ if (Buffer->ShareInfo2 == NULL) {
+ SET_ERROR_PARAMETER(PARM_ERROR_UNKNOWN);
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ //
+ // Set up for error cleanup.
+ //
+
+
+ srp = NULL;
+ dosSharePath.Buffer = NULL;
+ ntSharePath.Buffer = NULL;
+ capturedBuffer = NULL;
+
+ //
+ // Extract Internal buffer information.
+ //
+
+ shi2 = Buffer->ShareInfo2;
+
+ //
+ // 502 may contain a security descriptor.
+ //
+
+ if ( Level == 502 ) {
+
+ shi502 = (LPSHARE_INFO_502) Buffer->ShareInfo502;
+ fileSecurityDescriptor = shi502->shi502_security_descriptor;
+
+ //
+ // check the reserved field. If it is zero, this was called from
+ // inside the srvsvc. If not, it was through rpc.
+ //
+
+ if ( fileSecurityDescriptor != NULL ) {
+
+ if ( shi502->shi502_reserved != 0 ) {
+
+ error = ShareAssignSecurityDescriptor(
+ fileSecurityDescriptor,
+ &newFileSecurityDescriptor
+ );
+
+ if ( error != NO_ERROR ) {
+
+ SS_PRINT(( "NetrShareAdd: ShareAssignSecurityDescriptor "
+ "error: %d\n", error ));
+
+ SET_ERROR_PARAMETER( SHARE_FILE_SD_PARMNUM );
+ error = ERROR_INVALID_PARAMETER;
+ goto exit;
+ }
+
+ } else {
+ newFileSecurityDescriptor = fileSecurityDescriptor;
+ }
+ }
+
+ } else if ( Level != 2 ) {
+
+ //
+ // The only valid levels are 2 and 502. 2 is a subset of 502.
+ //
+
+ error = ERROR_INVALID_LEVEL;
+ goto exit;
+ }
+
+ //
+ // A share name must be specified.
+ //
+
+ netName = shi2->shi2_netname;
+
+ if ( (netName == NULL) || (*netName == '\0') ) {
+ SET_ERROR_PARAMETER( SHARE_NETNAME_PARMNUM );
+ error = ERROR_INVALID_PARAMETER;
+ goto exit;
+ }
+
+ //
+ // Limit it to NNLEN
+ //
+
+ if ( wcslen(netName) > NNLEN ) {
+ SET_ERROR_PARAMETER( SHARE_NETNAME_PARMNUM );
+ error = ERROR_INVALID_PARAMETER;
+ goto exit;
+ }
+
+ //
+ // If this is the IPC$ share, or the ADMIN$ share, no path
+ // may be specified. No path is needed for the IPC$ share, while a path
+ // is supplied internally for the ADMIN$ share.
+ //
+
+ path = shi2->shi2_path;
+ remark = shi2->shi2_remark;
+ shareType = shi2->shi2_type;
+
+ //
+ // Figure out which kind of share this is.
+ //
+
+ isIpc = (BOOL)(STRICMP( netName, IPC_SHARE_NAME ) == 0);
+ isAdmin = (BOOL)(STRICMP( netName, ADMIN_SHARE_NAME ) == 0);
+
+ //
+ // We have an administrative disk share if the share name is a drive letter
+ // followed by $, and if the path name is the root of that same drive.
+ //
+ if( wcslen( netName ) == 2 && netName[1] == L'$' &&
+ TOUPPER( netName[0] ) >= L'A' && TOUPPER( netName[0]) <= L'Z' &&
+ path != NULL && wcslen( path ) == 3 &&
+ TOUPPER( path[0] ) == TOUPPER( netName[0] ) &&
+ path[1] == L':' && path[2] == L'\\' ) {
+ //
+ // The share name and path look to be an administrative disk share.
+ // If the path refers to a fixed drive, then it really is one.
+ //
+ isDiskAdmin = (SsGetDriveType( path ) == DRIVE_FIXED);
+ } else {
+ isDiskAdmin = FALSE;
+ }
+
+ isPrintShare = (BOOL)(shareType == STYPE_PRINTQ);
+
+ isSpecial = isIpc || isAdmin || isDiskAdmin;
+
+ if ( isIpc ) {
+
+ if ( path != NULL ) {
+ SET_ERROR_PARAMETER( SHARE_PATH_PARMNUM );
+ error = ERROR_INVALID_PARAMETER;
+ goto exit;
+ }
+ path = NULL;
+
+ //
+ // Let the caller specify a remark if they want. If they don't,
+ // supply a default remark.
+ //
+
+ if ( remark == NULL ) {
+ remark = SsIPCShareRemark;
+ }
+
+ shareType = STYPE_IPC;
+
+ } else if ( isAdmin ) {
+
+ if ( path != NULL ) {
+ SET_ERROR_PARAMETER( SHARE_PATH_PARMNUM );
+ error = ERROR_INVALID_PARAMETER;
+ goto exit;
+ }
+
+ //
+ // Let the caller specify a remark if they want. If they don't,
+ // supply a default remark.
+ //
+
+ if ( remark == NULL ) {
+ remark = SsAdminShareRemark;
+ }
+
+ shareType = STYPE_DISKTREE;
+
+ //
+ // For the ADMIN$ share, we set the path to the system root
+ // directory. We get this from from the kernel via the
+ // read-only shared page (USER_SHARED_DATA)
+ //
+
+ path = USER_SHARED_DATA->NtSystemRoot;
+
+ } else {
+
+ //
+ // For all shares other than IPC$ and ADMIN$, a path must be
+ // specified and must not have .. and . as directory names.
+ //
+
+ if ( (path == NULL) || (*path == '\0') || !ValidSharePath( path ) ) {
+ SET_ERROR_PARAMETER( SHARE_PATH_PARMNUM );
+ error = ERROR_INVALID_NAME;
+ goto exit;
+ }
+
+ //
+ // If we've got a disk admin share and they didn't supply a
+ // comment, use the built in one
+ //
+ if( isDiskAdmin && remark == NULL ) {
+ remark = SsDiskAdminShareRemark;
+ }
+ }
+
+ //
+ // The remark must be no longer than MAXCOMMENTSZ.
+ //
+
+ if ( (remark != NULL) && (STRLEN(remark) > MAXCOMMENTSZ) ) {
+ SET_ERROR_PARAMETER( SHARE_REMARK_PARMNUM );
+ error = ERROR_INVALID_PARAMETER;
+ goto exit;
+ }
+
+ //
+ // If the server service is fully started, make sure that the caller
+ // is allowed to set share information in the server. We only do
+ // this if the service is started--the default share and configured
+ // share creations done during initialization do not need any
+ // special access.
+ //
+
+ if ( SsInitialized ) {
+
+ if ( isSpecial ) {
+ securityObject = &SsShareAdminSecurityObject;
+ } else if ( isPrintShare ) {
+ securityObject = &SsSharePrintSecurityObject;
+ } else {
+ securityObject = &SsShareFileSecurityObject;
+ }
+
+ error = SsCheckAccess( securityObject, SRVSVC_SHARE_INFO_SET );
+
+ if ( error != NO_ERROR ) {
+ SET_ERROR_PARAMETER( 0 );
+ goto exit;
+ }
+
+ }
+
+ //
+ // If this is a disk share, make sure that the drive is a type that
+ // can be shared.
+ //
+
+ if ( (shareType == STYPE_DISKTREE) && !isAdmin ) {
+
+ DWORD pathType;
+
+ //
+ // Check the path type. It should be an absolute directory path.
+ //
+
+ error = NetpPathType(
+ NULL,
+ path,
+ &pathType,
+ 0
+ );
+
+ if ( (error != NO_ERROR) || (pathType != ITYPE_PATH_ABSD) ) {
+ error = ERROR_INVALID_NAME;
+ SET_ERROR_PARAMETER( SHARE_PATH_PARMNUM );
+ goto exit;
+ }
+
+ driveType = SsGetDriveType( path );
+
+ if ( driveType == DRIVE_REMOVABLE ) {
+
+ shareType = STYPE_REMOVABLE;
+
+ } else if ( driveType == DRIVE_CDROM ) {
+
+ shareType = STYPE_CDROM;
+
+ } else if ( !(driveType == DRIVE_REMOTE &&
+ SsData.ServerInfo599.sv599_enablesharednetdrives) &&
+ driveType != DRIVE_FIXED &&
+ driveType != DRIVE_RAMDISK ) {
+
+ if ( driveType == DRIVE_REMOTE ) {
+ error = NERR_RedirectedPath;
+ } else {
+ error = NERR_UnknownDevDir;
+ }
+ SET_ERROR_PARAMETER( SHARE_PATH_PARMNUM );
+ goto exit;
+ }
+ }
+
+ //
+ // Set up the request packet.
+ //
+
+ srp = SsAllocateSrp( );
+ if ( srp == NULL ) {
+ error = ERROR_NOT_ENOUGH_MEMORY;
+ goto exit;
+ }
+ srp->Level = Level;
+
+ //
+ // Get the path name in NT format and put it in the SRP.
+ //
+
+ if ( path != NULL ) {
+
+ RtlInitUnicodeString( &dosSharePath, path );
+
+ if ( !RtlDosPathNameToNtPathName_U(
+ dosSharePath.Buffer,
+ &ntSharePath,
+ NULL,
+ NULL ) ) {
+ SET_ERROR_PARAMETER( SHARE_PATH_PARMNUM );
+ error = ERROR_INVALID_PARAMETER;
+ goto exit;
+ }
+
+ //
+ // If this is a redirected drive, make sure the redir is not
+ // LanMan.
+ //
+
+ if ( driveType == DRIVE_REMOTE ) {
+
+ error = DisallowSharedLanmanNetDrives( &ntSharePath );
+
+ if ( error != NERR_Success ) {
+ SET_ERROR_PARAMETER( SHARE_PATH_PARMNUM );
+ goto exit;
+ }
+
+ } // if remote drive
+
+ srp->Name1 = ntSharePath;
+ }
+
+ //
+ // Determine whether this is an admin share and use the appropriate
+ // security descriptor.
+ //
+
+ if ( isAdmin || isDiskAdmin ) {
+ connectSecurityDescriptor = SsShareAdmConnectSecurityObject.SecurityDescriptor;
+ } else {
+ connectSecurityDescriptor = SsShareConnectSecurityObject.SecurityDescriptor;
+ }
+
+ //
+ // If this is a disk share, verify that the directory to be shared
+ // exists and that the caller has access. (Don't do the access
+ // check during server startup.) Don't check the admin$ share -- we
+ // know it exists. Skip removable type disks.
+ //
+
+ if ( !isAdmin &&
+ (shareType == STYPE_DISKTREE) &&
+ (shi2->shi2_path != NULL) ) {
+
+ OBJECT_ATTRIBUTES objectAttributes;
+ IO_STATUS_BLOCK iosb;
+ HANDLE handle = INVALID_HANDLE_VALUE;
+ NTSTATUS status;
+
+ if ( SsInitialized ) {
+ (VOID)RpcImpersonateClient( NULL );
+ }
+
+ InitializeObjectAttributes(
+ &objectAttributes,
+ &ntSharePath,
+ OBJ_CASE_INSENSITIVE,
+ 0,
+ NULL
+ );
+
+ status = NtOpenFile(
+ &handle,
+ FILE_LIST_DIRECTORY,
+ &objectAttributes,
+ &iosb,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ FILE_DIRECTORY_FILE
+ );
+
+ if ( SsInitialized ) {
+ (VOID)RpcRevertToSelf( );
+ }
+
+ if ( !NT_SUCCESS(status) ) {
+
+ if ( SsInitialized || (status != STATUS_ACCESS_DENIED) ) {
+
+ //
+ // During startup, if the directory does not
+ // exist (renamed/deleted), log an event.
+ //
+
+ if ( !SsInitialized &&
+ ((status == STATUS_OBJECT_NAME_NOT_FOUND) ||
+ (status == STATUS_OBJECT_PATH_NOT_FOUND)) ) {
+
+ LPWSTR subStrings[2];
+ subStrings[0] = netName;
+ subStrings[1] = shi2->shi2_path;
+
+ SsLogEvent(
+ EVENT_SRV_CANT_RECREATE_SHARE,
+ 2,
+ subStrings,
+ NO_ERROR
+ );
+
+ }
+
+ SET_ERROR_PARAMETER( 0 );
+ error = RtlNtStatusToDosError( status );
+ goto exit;
+ }
+
+ } else if( !IS_SLASH_SLASH_NAME( path ) ) {
+
+ if ( SsInitialized ) {
+
+ PFILE_NAME_INFORMATION fileNameInformation;
+ ULONG fileInfoSize;
+ ULONG fileNameLength;
+
+ fileInfoSize = sizeof(FILE_NAME_INFORMATION) + SIZE_WSTR( path );
+
+ fileNameInformation = MIDL_user_allocate( fileInfoSize );
+
+ if ( fileNameInformation == NULL ) {
+ error = ERROR_NOT_ENOUGH_MEMORY;
+ NtClose( handle );
+ goto exit;
+ }
+
+ //
+ // Query the name from the file system. This is because some
+ // fs like fat uses only upper case oem names. This can cause
+ // a problem because some oem characters do not have upper case
+ // equivalents and thus get mapped to something funny.
+ //
+
+ status = NtQueryInformationFile(
+ handle,
+ &iosb,
+ fileNameInformation,
+ fileInfoSize,
+ FileNameInformation
+ );
+
+
+ if ( status == STATUS_SUCCESS ) {
+
+ //
+ // The file name returned is expected to be
+ // 3 characters shorter than the share path length.
+ // These 3 characters are "X", ":", "\0".
+ //
+
+ fileNameLength = fileNameInformation->FileNameLength;
+
+ if ((fileNameLength+3*sizeof(WCHAR)) <= SIZE_WSTR(path)) {
+
+ //
+ // Copy the path name
+ //
+
+ RtlCopyMemory(
+ (LPBYTE) path + 2*sizeof(WCHAR),
+ fileNameInformation->FileName,
+ fileNameLength
+ );
+
+ path[fileNameLength/sizeof(WCHAR)+2] = L'\0';
+ }
+ }
+
+ MIDL_user_free( fileNameInformation );
+
+ }
+
+ NtClose( handle );
+
+ } else {
+
+ NtClose( handle );
+ }
+ }
+
+ //
+ // Capture the share data structure passed in.
+ //
+
+ if ( isSpecial ) {
+ shareType |= STYPE_SPECIAL;
+ }
+
+ capturedBuffer = CaptureShareInfo(
+ Level,
+ shi2,
+ shareType,
+ path,
+ remark,
+ connectSecurityDescriptor,
+ newFileSecurityDescriptor,
+ &bufferLength
+ );
+
+ if ( capturedBuffer == NULL ) {
+ SET_ERROR_PARAMETER( 0 );
+ error = ERROR_NOT_ENOUGH_MEMORY;
+ goto exit;
+ }
+
+ //
+ // Send the request on to the server.
+ //
+
+ error = SsServerFsControl(
+ NULL,
+ FSCTL_SRV_NET_SHARE_ADD,
+ srp,
+ capturedBuffer,
+ bufferLength
+ );
+
+ SET_ERROR_PARAMETER( srp->Parameters.Set.ErrorParameter );
+
+ //
+ // If the request succeeded, add a value to the Shares key, thus
+ // effecting a sticky share. We only do this if the server is fully
+ // started -- the default share and configured share creations done
+ // during initialization should not be added to the registry.
+ //
+ // Don't do this if this is an admin share being [re]created.
+ //
+
+ if ( SsInitialized &&
+ (error == NO_ERROR) &&
+ !isSpecial ) {
+ SsAddShareToRegistry( shi2, newFileSecurityDescriptor );
+ }
+
+ //
+ // If a print share was successfully added, increment the number
+ // of print shares and update the exported (announced) server type.
+ //
+
+ if ( isPrintShare ) {
+ InterlockedIncrement( &SsData.NumberOfPrintShares );
+ SsSetExportedServerType( NULL, FALSE, TRUE );
+ }
+
+exit:
+
+ //
+ // Clean up. Free the share data structure that was allocated by
+ // CaptureShareInfo2. Free the server request packet. If there was
+ // an NT path name allocated by RtlDosPathNameToNtPathName, free it.
+ // If we created ADMIN$, free the system path string and the system
+ // path information buffer.
+ //
+
+ if ( (newFileSecurityDescriptor != NULL) &&
+ (shi502->shi502_reserved != 0) ) {
+
+ (VOID) RtlDeleteSecurityObject ( &newFileSecurityDescriptor );
+ }
+
+ if ( capturedBuffer != NULL ) {
+ MIDL_user_free( capturedBuffer );
+ }
+
+ if ( srp != NULL ) {
+ SsFreeSrp( srp );
+ }
+
+ if ( ntSharePath.Buffer != NULL ) {
+ RtlFreeUnicodeString( &ntSharePath );
+ }
+ return error;
+
+} // NetrShareAdd
+
+
+NET_API_STATUS
+NetrShareCheck (
+ IN LPWSTR ServerName,
+ IN LPWSTR Device,
+ OUT LPDWORD Type
+ )
+
+/*++
+
+Routine Description:
+
+ This routine implements NetShareCheck by calling NetrShareEnum.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NET_API_STATUS - NO_ERROR or reason for failure.
+
+--*/
+
+{
+ DWORD totalEntries;
+ DWORD entriesRead;
+ ULONG i;
+ PSHARE_INFO_2 shi2;
+ NET_API_STATUS error;
+ LPBYTE buffer;
+
+ ServerName;
+
+ //
+ // Call ShareEnumCommon to actually get the information about what
+ // the shares are on the server. We use this routine rather than
+ // calling NetrShareEnum because NetShareCheck requires no privilege
+ // to execute, and we don't want the security checks in
+ // NetrShareEnum.
+ //
+
+ error = ShareEnumCommon(
+ 2,
+ &buffer,
+ (DWORD)-1,
+ &entriesRead,
+ &totalEntries,
+ NULL,
+ NULL
+ );
+
+ if ( error != NO_ERROR ) {
+ return error;
+ }
+
+ SS_ASSERT( totalEntries == entriesRead );
+
+ //
+ // Attempt to find the drive letter in a share's path name.
+ //
+
+ for ( shi2 = (PSHARE_INFO_2)buffer, i = 0; i < totalEntries; shi2++, i++ ) {
+
+ if ( shi2->shi2_path != NULL && *Device == *shi2->shi2_path ) {
+
+ //
+ // Something on the specified disk is shared--free the buffer
+ // and return the type of share.
+ //
+
+ *Type = shi2->shi2_type & ~STYPE_SPECIAL;
+ MIDL_user_free( buffer );
+
+ return NO_ERROR;
+ }
+ }
+
+ //
+ // Nothing on the specified disk is shared. Return an error.
+ //
+
+ MIDL_user_free( buffer );
+ return NERR_DeviceNotShared;
+
+} // NetrShareCheck
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetrShareDel (
+ IN LPWSTR ServerName,
+ IN LPWSTR NetName,
+ IN DWORD Reserved
+ )
+
+/*++
+
+Routine Description:
+
+ This routine communicates with the server FSD to implement the
+ NetShareDel function.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NET_API_STATUS - NO_ERROR or reason for failure.
+
+--*/
+
+{
+ NET_API_STATUS error;
+ SHARE_DEL_HANDLE handle;
+
+ error = NetrShareDelStart( ServerName, NetName, Reserved, &handle );
+
+ if ( error == NO_ERROR ) {
+ error = NetrShareDelCommit( &handle );
+ }
+
+ return error;
+
+} // NetrShareDel
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetrShareDelStart (
+ IN LPWSTR ServerName,
+ IN LPWSTR NetName,
+ IN DWORD Reserved,
+ IN PSHARE_DEL_HANDLE ContextHandle
+ )
+
+/*++
+
+Routine Description:
+
+ This routine implements the first phase of the share deletion
+ function, which simply remembers that the specified share is to be
+ deleted. The NetrShareDelCommit function actually deletes the
+ share. This two-phase deletion is used to delete IPC$, which is
+ the share used for named pipes, so that RPC can be used to delete
+ the IPC$ share without receiving RPC errors.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NET_API_STATUS - NO_ERROR or reason for failure.
+
+--*/
+
+{
+ NET_API_STATUS error;
+ PSHARE_INFO_2 shareInfo;
+ DWORD entriesRead;
+ DWORD totalEntries;
+ DWORD shareType;
+ BOOL isPrintShare;
+ BOOL isSpecial;
+ PSRVSVC_SECURITY_OBJECT securityObject;
+ PSHARE_DEL_CONTEXT context;
+
+ ServerName, Reserved;
+
+ //
+ // A share name must be specified.
+ //
+
+ if ( (NetName == NULL) || (*NetName == '\0') ) {
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ //
+ // First determine what kind of share is being deleted.
+ //
+
+ error = ShareEnumCommon(
+ 2,
+ (LPBYTE *)&shareInfo,
+ (DWORD)-1,
+ &entriesRead,
+ &totalEntries,
+ NULL,
+ NetName
+ );
+ if ( error != NO_ERROR ) {
+ return error;
+ }
+ if ( entriesRead == 0 ) {
+ return NERR_NetNameNotFound;
+ }
+
+ shareType = shareInfo->shi2_type & ~STYPE_SPECIAL;
+ isSpecial = (BOOL)((shareInfo->shi2_type & STYPE_SPECIAL) != 0);
+
+ isPrintShare = (BOOL)(shareType == STYPE_PRINTQ);
+
+ MIDL_user_free( shareInfo );
+
+ //
+ // Make sure that the caller is allowed to delete this share.
+ //
+
+ if ( isSpecial ) {
+ securityObject = &SsShareAdminSecurityObject;
+ } else if ( isPrintShare ) {
+ securityObject = &SsSharePrintSecurityObject;
+ } else {
+ securityObject = &SsShareFileSecurityObject;
+ }
+
+ error = SsCheckAccess( securityObject, SRVSVC_SHARE_INFO_SET );
+
+ if ( error != NO_ERROR ) {
+ return error;
+ }
+
+ //
+ // Set up context for the commit phase.
+ //
+
+ context = MIDL_user_allocate(
+ sizeof(SHARE_DEL_CONTEXT) +
+ wcslen(NetName)*sizeof(WCHAR) + sizeof(WCHAR)
+ );
+
+ if ( context == NULL ) {
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ RtlZeroMemory(
+ context,
+ sizeof(SHARE_DEL_CONTEXT) +
+ wcslen(NetName)*sizeof(WCHAR) + sizeof(WCHAR)
+ );
+
+ context->IsPrintShare = isPrintShare;
+ context->IsSpecial = isSpecial;
+
+ wcscpy( (LPWSTR)(context + 1), NetName );
+
+ RtlInitUnicodeString( &context->Srp.Name1, (LPWSTR)(context + 1) );
+
+ //
+ // Return the context pointer as an RPC context handle.
+ //
+
+ *ContextHandle = context;
+
+ return NO_ERROR;
+
+} // NetrShareDelStart
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetrShareDelCommit (
+ IN PSHARE_DEL_HANDLE ContextHandle
+ )
+
+/*++
+
+Routine Description:
+
+ This routine implements the second phase of the share deletion
+ function, which actually deletes the share. The first phase,
+ NetrShareDelStart simply remembers that the share is to be deleted.
+ This two-phase deletion is used to delete IPC$, which is the share
+ used for named pipes, so that RPC can be used to delete the IPC$
+ share without receiving RPC errors.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NET_API_STATUS - NO_ERROR or reason for failure.
+
+--*/
+
+{
+ NET_API_STATUS error;
+ PSHARE_DEL_CONTEXT context;
+
+ //
+ // The context handle is a pointer to allocated storage containing
+ // the name of the share being deleted and other useful information.
+ // Copy the pointer, then clear the context handle.
+ //
+
+ context = *ContextHandle;
+ *ContextHandle = NULL;
+
+ //
+ // Send the request on to the server.
+ //
+
+ error = SsServerFsControl(
+ NULL,
+ FSCTL_SRV_NET_SHARE_DEL,
+ &context->Srp,
+ NULL,
+ 0
+ );
+
+ //
+ // If the request succeeded, remove the value corresponding to the
+ // share from the Shares key, thus effecting a sticky share
+ // deletion.
+ //
+ // We don't do this if this is an admin share being deleted. No
+ // registry information is kept for these shares.
+ //
+
+ if ( (error == NO_ERROR) && !context->IsSpecial ) {
+ SsRemoveShareFromRegistry( (LPWSTR)(context + 1) );
+ }
+
+ //
+ // If a print share was successfully deleted, decrement the number
+ // of print shares and update the exported (announced) server type.
+ //
+
+ if ( context->IsPrintShare ) {
+ InterlockedDecrement( &SsData.NumberOfPrintShares );
+ SsSetExportedServerType( NULL, FALSE, TRUE );
+ }
+
+ //
+ // Free the context.
+ //
+
+ MIDL_user_free( context );
+
+ return error;
+
+} // NetrShareDelCommit
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetrShareDelSticky (
+ IN LPWSTR ServerName,
+ IN LPWSTR NetName,
+ IN DWORD Reserved
+ )
+
+/*++
+
+Routine Description:
+
+ This routine implements the NetShareDelSticky function. It removes
+ the named share from the sticky share list in the registry. The
+ primary use of this function is to delete a sticky share whose
+ root directory has been deleted, thus preventing actual recreation
+ of the share, but whose entry still exists in the registry. It can
+ also be used to remove the persistence of a share without deleting
+ the current incarnation of the share.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NET_API_STATUS - NO_ERROR or reason for failure.
+
+--*/
+
+{
+ NET_API_STATUS error;
+ PSHARE_INFO_2 shareInfo, shi2;
+ DWORD entriesRead;
+ DWORD totalEntries;
+ DWORD shareType;
+ ULONG i;
+ BOOL isPrintShare;
+ PSRVSVC_SECURITY_OBJECT securityObject;
+
+ ServerName, Reserved;
+
+
+ //
+ // A share name must be specified.
+ //
+
+ if ( (NetName == NULL) || (*NetName == '\0') ) {
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ //
+ // First determine what kind of share is being deleted.
+ //
+
+ error = ShareEnumSticky(
+ 2,
+ (LPBYTE *)&shareInfo,
+ (DWORD)-1,
+ &entriesRead,
+ &totalEntries,
+ NULL
+ );
+
+ if ( error != NO_ERROR ) {
+
+ return error;
+
+ } else if ( entriesRead == 0 ) {
+
+ return NERR_NetNameNotFound;
+ }
+
+ for ( shi2 = shareInfo, i = 0 ; i < entriesRead; i++, shi2++ ) {
+
+ if ( _wcsicmp( shi2->shi2_netname, NetName ) == 0 ) {
+ break;
+ }
+ }
+
+ //
+ // Does it exist?
+ //
+
+ if ( i == entriesRead ) {
+ MIDL_user_free( shareInfo );
+ return NERR_NetNameNotFound;
+ }
+
+ //
+ // Use appropriate security object based on whether it is a print
+ // share or not. Admin shares are not sticky.
+ //
+
+ shareType = shi2->shi2_type & ~STYPE_SPECIAL;
+ isPrintShare = (BOOL)(shareType == STYPE_PRINTQ);
+
+ MIDL_user_free( shareInfo );
+
+ //
+ // Make sure that the caller is allowed to delete this share.
+ //
+
+ if ( isPrintShare ) {
+ securityObject = &SsSharePrintSecurityObject;
+ } else {
+ securityObject = &SsShareFileSecurityObject;
+ }
+
+ error = SsCheckAccess(
+ securityObject,
+ SRVSVC_SHARE_INFO_SET
+ );
+
+ if ( error != NO_ERROR ) {
+ return error;
+ }
+
+ //
+ // Remove the value corresponding to the share from the Shares key,
+ // thus effecting a sticky share deletion.
+ //
+
+ error = SsRemoveShareFromRegistry( NetName );
+
+ if ( error == ERROR_FILE_NOT_FOUND ) {
+ error = NERR_NetNameNotFound;
+ }
+
+ return error;
+
+} // NetrShareDelSticky
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetrShareEnum (
+ SRVSVC_HANDLE ServerName,
+ LPSHARE_ENUM_STRUCT InfoStruct,
+ DWORD PreferredMaximumLength,
+ LPDWORD TotalEntries,
+ LPDWORD ResumeHandle
+ )
+
+/*++
+
+Routine Description:
+
+ This routine communicates with the server FSD to implement the
+ NetShareEnum function.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NET_API_STATUS - NO_ERROR or reason for failure.
+
+--*/
+
+{
+ NET_API_STATUS error;
+ ACCESS_MASK desiredAccess;
+
+ ServerName;
+
+ //
+ // Determine the desired access.
+ //
+
+ switch ( InfoStruct->Level ) {
+
+ case 0:
+ case 1:
+ desiredAccess = SRVSVC_SHARE_USER_INFO_GET;
+ break;
+
+ case 2:
+ case 502:
+ desiredAccess = SRVSVC_SHARE_ADMIN_INFO_GET;
+ break;
+
+ default:
+
+ return ERROR_INVALID_LEVEL;
+ }
+
+ //
+ // Make sure that the caller has the access necessary for this
+ // operation.
+ //
+
+ error = SsCheckAccess(
+ &SsSharePrintSecurityObject,
+ desiredAccess
+ );
+
+ if ( error != NO_ERROR ) {
+ return error;
+ }
+
+ //
+ // Use the common routine to get the information.
+ //
+
+ return ShareEnumCommon(
+ InfoStruct->Level,
+ (LPBYTE *)&InfoStruct->ShareInfo.Level2->Buffer,
+ PreferredMaximumLength,
+ &InfoStruct->ShareInfo.Level2->EntriesRead,
+ TotalEntries,
+ ResumeHandle,
+ NULL
+ );
+
+} // NetrShareEnum
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetrShareEnumSticky (
+ SRVSVC_HANDLE ServerName,
+ LPSHARE_ENUM_STRUCT InfoStruct,
+ DWORD PreferredMaximumLength,
+ LPDWORD TotalEntries,
+ LPDWORD ResumeHandle OPTIONAL
+ )
+
+/*++
+
+Routine Description:
+
+ This routine communicates with the server FSD to implement the
+ NetShareEnumSticky function.
+
+Arguments:
+
+ ServerName - the name of the server whose shares we want to enumerate.
+ InfoStruct - pointer to a PSHARE_ENUM_STRUCT that will contain the
+ output buffer upon completion.
+ PreferredMaximumLength - an advisory value that specifies the maximum
+ number of bytes the client is expecting to be returned. If -1, the
+ client expects the whole list to be returned.
+ TotalEntries - Upon return, will contain the number of entries that
+ were available.
+ ResumeHandle - is not NULL, will contain the resume handle that can be
+ used to continue a search.
+
+Return Value:
+
+ NET_API_STATUS - NO_ERROR or reason for failure.
+
+--*/
+
+{
+ NET_API_STATUS error;
+ ACCESS_MASK desiredAccess;
+
+ ServerName;
+
+ //
+ // Determine the desired access.
+ //
+
+ switch ( InfoStruct->Level ) {
+
+ case 0:
+ case 1:
+ desiredAccess = SRVSVC_SHARE_USER_INFO_GET;
+ break;
+
+ case 2:
+ case 502:
+ desiredAccess = SRVSVC_SHARE_ADMIN_INFO_GET;
+ break;
+
+ default:
+
+ return ERROR_INVALID_LEVEL;
+ }
+
+ //
+ // Make sure that the caller has the access necessary for this
+ // operation.
+ //
+
+ error = SsCheckAccess(
+ &SsSharePrintSecurityObject,
+ desiredAccess
+ );
+
+ if ( error != NO_ERROR ) {
+ return error;
+ }
+
+ //
+ // Use the common routine to get the information.
+ //
+
+ return ShareEnumSticky(
+ InfoStruct->Level,
+ (LPBYTE *)&InfoStruct->ShareInfo.Level2->Buffer,
+ PreferredMaximumLength,
+ &InfoStruct->ShareInfo.Level2->EntriesRead,
+ TotalEntries,
+ ResumeHandle
+ );
+
+} // NetrShareEnumSticky
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetrShareGetInfo (
+ IN LPWSTR ServerName,
+ IN LPWSTR NetName,
+ IN DWORD Level,
+ OUT LPSHARE_INFO Buffer
+ )
+
+/*++
+
+Routine Description:
+
+ This routine communicates with the server FSD to implement the
+ NetShareGetInfo function.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NET_API_STATUS - NO_ERROR or reason for failure.
+
+--*/
+
+{
+ NET_API_STATUS error;
+ PSHARE_INFO_2 shareInfo;
+ ULONG entriesRead;
+ ULONG totalEntries;
+ ACCESS_MASK desiredAccess;
+
+ ServerName;
+
+ //
+ // A share name must be specified.
+ //
+
+ if ( (NetName == NULL) || (*NetName == '\0') ) {
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ //
+ // Determine the desired access.
+ //
+
+ switch ( Level ) {
+
+ case 0:
+ case 1:
+ case 1005:
+ desiredAccess = SRVSVC_SHARE_USER_INFO_GET;
+ break;
+
+ case 2:
+ case 502:
+ desiredAccess = SRVSVC_SHARE_ADMIN_INFO_GET;
+ break;
+
+ default:
+
+ return ERROR_INVALID_LEVEL;
+ }
+
+ //
+ // Make sure that the caller has the access necessary for this
+ // operation.
+ //
+
+ error = SsCheckAccess(
+ &SsSharePrintSecurityObject,
+ desiredAccess
+ );
+
+ if ( error != NO_ERROR ) {
+ return error;
+ }
+
+ //
+ // Use the common routine to get the information.
+ //
+
+ error = ShareEnumCommon(
+ Level,
+ (LPBYTE *)&shareInfo,
+ (DWORD)-1,
+ &entriesRead,
+ &totalEntries,
+ NULL,
+ NetName
+ );
+
+ if ( error != NO_ERROR ) {
+ return error;
+ }
+ if ( entriesRead == 0 ) {
+ return NERR_NetNameNotFound;
+ }
+ SS_ASSERT( entriesRead == 1 );
+
+ //
+ // Make sure that the caller is allowed to get share information on
+ // this share.
+ //
+
+ if ( Level == 502 ) {
+ Buffer->ShareInfo502 = (LPSHARE_INFO_502_I)shareInfo;
+ } else {
+ Buffer->ShareInfo2 = (LPSHARE_INFO_2)shareInfo;
+ }
+ return NO_ERROR;
+
+} // NetrShareGetInfo
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetrShareSetInfo (
+ IN LPWSTR ServerName,
+ IN LPWSTR NetName,
+ IN DWORD Level,
+ IN LPSHARE_INFO Buffer,
+ OUT LPDWORD ErrorParameter
+ )
+
+/*++
+
+Routine Description:
+
+ This routine communicates with the server FSD to implement the
+ NetShareSetInfo function.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NET_API_STATUS - NO_ERROR or reason for failure.
+
+--*/
+
+{
+ NET_API_STATUS error;
+ PSERVER_REQUEST_PACKET srp;
+ DWORD entriesRead;
+ DWORD totalEntries;
+ DWORD shareType;
+ BOOL isPrintShare;
+ BOOL isSpecial;
+ PSRVSVC_SECURITY_OBJECT securityObject;
+ PVOID capturedBuffer;
+ ULONG bufferLength;
+
+ LPWSTR remark = NULL;
+ ULONG maxUses = 0;
+ PSECURITY_DESCRIPTOR fileSd = NULL;
+ PSECURITY_DESCRIPTOR newFileSd = NULL;
+
+ BOOL setRemark;
+ BOOL setFileSd;
+ BOOL set1005Info = FALSE;
+
+ DWORD set1005Value = 0;
+
+ PSHARE_INFO_2 shi2;
+ SHARE_INFO_2 localShi2;
+
+ ServerName;
+
+ //
+ // Check that user input buffer is not NULL
+ //
+ if (Buffer->ShareInfo2 == NULL) {
+ SET_ERROR_PARAMETER(PARM_ERROR_UNKNOWN);
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ //
+ // Determine what the caller is trying to set.
+ //
+
+ switch ( Level ) {
+
+ case 502:
+
+ fileSd = Buffer->ShareInfo502->shi502_security_descriptor;
+
+ // *** lack of break is intentional!
+
+ case 2:
+
+ maxUses = Buffer->ShareInfo2->shi2_max_uses;
+
+ // *** lack of break is intentional!
+
+ case 1:
+
+ remark = Buffer->ShareInfo2->shi2_remark;
+
+ break;
+
+ case SHARE_REMARK_INFOLEVEL:
+
+ remark = Buffer->ShareInfo1004->shi1004_remark;
+
+ break;
+
+ case SHARE_MAX_USES_INFOLEVEL:
+
+ maxUses = Buffer->ShareInfo1006->shi1006_max_uses;
+
+ break;
+
+ case SHARE_FILE_SD_INFOLEVEL:
+
+ fileSd = Buffer->ShareInfo1501->shi1501_security_descriptor;
+
+ break;
+
+#if 0
+ case 1005:
+ set1005Info = TRUE;
+ set1005Value = Buffer->ShareInfo1005->shi1005_flags;
+ break;
+#endif
+
+ default:
+
+ SS_PRINT(( "NetrShareSetInfo: invalid level: %ld\n", Level ));
+ SET_ERROR_PARAMETER( 0 );
+ return ERROR_INVALID_LEVEL;
+ }
+
+ setRemark = (BOOLEAN)( remark != NULL );
+ setFileSd = (BOOLEAN)( fileSd != NULL );
+
+ //
+ // A share name must be specified.
+ //
+
+ if ( (NetName == NULL) || (*NetName == '\0') ) {
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ //
+ // Determine what kind of share is being modified.
+ //
+
+ error = ShareEnumCommon(
+ 2,
+ (LPBYTE *)&shi2,
+ (DWORD)-1,
+ &entriesRead,
+ &totalEntries,
+ NULL,
+ NetName
+ );
+ if ( error != NO_ERROR ) {
+ return error;
+ }
+ if ( entriesRead == 0 ) {
+ return NERR_NetNameNotFound;
+ }
+
+ shareType = shi2->shi2_type & ~STYPE_SPECIAL;
+ isSpecial = (BOOL)((shi2->shi2_type & STYPE_SPECIAL) != 0);
+
+ MIDL_user_free( shi2 );
+
+ //
+ // The share ACL cannot be changed on admin shares.
+ //
+ if ( isSpecial && setFileSd ) {
+ SET_ERROR_PARAMETER( SHARE_FILE_SD_PARMNUM );
+ error = ERROR_INVALID_PARAMETER;
+ goto exit;
+ }
+
+ //
+ // Figure out which kind of share this is.
+ //
+
+ isPrintShare = (BOOL)(shareType == STYPE_PRINTQ);
+
+ //
+ // Only disk shares can be put into the Dfs
+ //
+ if( set1005Info && shareType != STYPE_DISKTREE ) {
+ error = ERROR_BAD_DEV_TYPE;
+ goto exit;
+ }
+
+ //
+ // Make sure that the caller is allowed to set share information on
+ // this share.
+ //
+
+ if ( isSpecial || set1005Info ) {
+ securityObject = &SsShareAdminSecurityObject;
+ } else if ( isPrintShare ) {
+ securityObject = &SsSharePrintSecurityObject;
+ } else {
+ securityObject = &SsShareFileSecurityObject;
+ }
+
+ error = SsCheckAccess( securityObject, SRVSVC_SHARE_INFO_SET );
+
+ if ( error != NO_ERROR ) {
+ return error;
+ }
+
+ //
+ // Just return success if not trying to set anything.
+ //
+
+ if ( !setRemark && (maxUses == 0) && !setFileSd && !set1005Info ) {
+ return NO_ERROR;
+ }
+
+ //
+ // The remark must be no longer than MAXCOMMENTSZ.
+ //
+
+ if ( setRemark ) {
+ if ( STRLEN(remark) > MAXCOMMENTSZ ) {
+ SET_ERROR_PARAMETER( SHARE_REMARK_PARMNUM );
+ return ERROR_INVALID_PARAMETER;
+ }
+ }
+
+ //
+ // Mapped the security descriptor to remove the generic permissions
+ //
+
+ if ( setFileSd ) {
+
+ error = ShareAssignSecurityDescriptor(
+ fileSd,
+ &newFileSd
+ );
+
+ if ( error != NO_ERROR ) {
+ SS_PRINT(( "NetrShareSetInfo: ShareAssignSecurityDescriptor "
+ "error: %d\n", error ));
+ SET_ERROR_PARAMETER( SHARE_FILE_SD_PARMNUM );
+ return ERROR_INVALID_PARAMETER;
+ }
+ }
+
+ //
+ // Allocate a request packet.
+ //
+
+ srp = SsAllocateSrp( );
+ if ( srp == NULL ) {
+ error = ERROR_NOT_ENOUGH_MEMORY;
+ goto exit;
+ }
+
+ srp->Level = Level;
+
+ //
+ // Set up the share name.
+ //
+
+ RtlInitUnicodeString( &srp->Name1, NetName );
+
+ //
+ // Set up the MaxUses field. If equal to 0, then it won't be changed
+ // by the server.
+ //
+
+ srp->Parameters.Set.Api.ShareInfo.MaxUses = maxUses;
+
+ //
+ // Capture the share data structure passed in.
+ //
+
+ localShi2.shi2_netname = NetName;
+
+ if( set1005Info == TRUE ) {
+ srp->Flags = set1005Value;
+ capturedBuffer = NULL;
+ bufferLength = 0;
+
+ } else {
+ capturedBuffer = CaptureShareInfo(
+ Level,
+ &localShi2,
+ 0, // ShareType, unused for SHARE_SET_INFO
+ NULL,
+ remark,
+ NULL,
+ newFileSd,
+ &bufferLength
+ );
+
+ if ( capturedBuffer == NULL ) {
+ SET_ERROR_PARAMETER( 0 );
+ error = ERROR_NOT_ENOUGH_MEMORY;
+ goto exit;
+ }
+ }
+
+ //
+ // Send the request to the server.
+ //
+
+ error = SsServerFsControl(
+ NULL,
+ FSCTL_SRV_NET_SHARE_SET_INFO,
+ srp,
+ capturedBuffer,
+ bufferLength
+ );
+
+ //
+ // If the request succeeded, modify the share's value in the Shares
+ // key, thus effecting a sticky change.
+ //
+ // We don't do this if this is an admin share being modified. No
+ // registry information is kept for these shares.
+ //
+
+ if ( (error == NO_ERROR) && !isSpecial && !set1005Info ) {
+
+ DWORD entriesRead;
+ DWORD totalEntries;
+ NET_API_STATUS error2;
+
+ error2 = ShareEnumCommon(
+ 2,
+ (LPBYTE *)&shi2,
+ (DWORD)-1,
+ &entriesRead,
+ &totalEntries,
+ NULL,
+ NetName
+ );
+ if ( error2 == NO_ERROR ) {
+ SsAddShareToRegistry( shi2, newFileSd );
+ MIDL_user_free( shi2 );
+ }
+
+ }
+
+ //
+ // Set up the error parameter if requested and return.
+ //
+
+ SET_ERROR_PARAMETER( srp->Parameters.Set.ErrorParameter );
+
+ SsFreeSrp( srp );
+
+exit:
+
+ if ( newFileSd != NULL ) {
+ (VOID)RtlDeleteSecurityObject( &newFileSd );
+ }
+
+ if( capturedBuffer != NULL ) {
+ MIDL_user_free( capturedBuffer );
+ }
+
+ return error;
+
+} // NetrShareSetInfo
+
+
+PVOID
+CaptureShareInfo (
+ IN DWORD Level,
+ IN PSHARE_INFO_2 Shi2,
+ IN DWORD ShareType OPTIONAL,
+ IN LPWSTR Path OPTIONAL,
+ IN LPWSTR Remark OPTIONAL,
+ IN PSECURITY_DESCRIPTOR ConnectSecurityDescriptor OPTIONAL,
+ IN PSECURITY_DESCRIPTOR FileSecurityDescriptor OPTIONAL,
+ OUT PULONG CapturedBufferLength
+ )
+
+{
+ PSHARE_INFO_502 capturedShi502;
+ ULONG capturedBufferLength;
+ PCHAR variableData;
+ ULONG pathNameLength;
+ ULONG shareNameLength;
+ ULONG remarkLength;
+ ULONG connectSDLength = 0;
+ ULONG fileSdLength = 0;
+
+ //
+ // Determine the lengths of the strings in the buffer and the total
+ // length of the buffer.
+ //
+
+ if ( Shi2->shi2_netname == NULL ) {
+ shareNameLength = 0;
+ } else {
+ shareNameLength = SIZE_WSTR( Shi2->shi2_netname );
+ }
+
+ if ( Path == NULL ) {
+ pathNameLength = 0;
+ } else {
+ pathNameLength = SIZE_WSTR( Path );
+ }
+
+ if ( Remark == NULL ) {
+ remarkLength = 0;
+ } else {
+ remarkLength = SIZE_WSTR( Remark );
+ }
+
+ if ( ARGUMENT_PRESENT( ConnectSecurityDescriptor ) ) {
+
+ //
+ // Allocate extra space for the security descriptor since it needs
+ // to be longword-aligned and there may be padding in front of it.
+ //
+
+ connectSDLength =
+ RtlLengthSecurityDescriptor( ConnectSecurityDescriptor ) +
+ sizeof(ULONG);
+ }
+
+ if ( ARGUMENT_PRESENT( FileSecurityDescriptor ) ) {
+ //
+ // ULONG added for alignment.
+ //
+
+ fileSdLength = RtlLengthSecurityDescriptor( FileSecurityDescriptor ) +
+ sizeof(ULONG);
+ }
+
+ //
+ // Allocate a buffer in which to capture the share information.
+ //
+
+ capturedBufferLength = sizeof(SHARE_INFO_502) +
+ shareNameLength +
+ remarkLength +
+ pathNameLength +
+ connectSDLength +
+ fileSdLength;
+
+ //
+ // Allocate a buffer to hold the input information.
+ //
+
+ capturedShi502 = MIDL_user_allocate( capturedBufferLength );
+
+ if ( capturedShi502 == NULL ) {
+ *CapturedBufferLength = 0;
+ return NULL;
+ }
+
+ //
+ // Copy over the share info structure.
+ //
+
+ *((PSHARE_INFO_2) capturedShi502) = *Shi2;
+
+ //
+ // Optionally override the share type.
+ //
+
+ if ( ShareType != 0 ) {
+ capturedShi502->shi502_type = ShareType;
+ }
+
+ //
+ // Capture the share name.
+ //
+
+ variableData = (PCHAR)( capturedShi502 + 1 );
+
+ if ( shareNameLength != 0 ) {
+ capturedShi502->shi502_netname = (LPWSTR)variableData;
+ RtlCopyMemory( variableData, Shi2->shi2_netname, shareNameLength );
+ variableData += shareNameLength;
+ } else {
+ capturedShi502->shi502_netname = NULL;
+ }
+
+ //
+ // Capture the remark.
+ //
+
+ if ( remarkLength != 0 ) {
+ capturedShi502->shi502_remark = (LPWSTR)variableData;
+ RtlCopyMemory( variableData, Remark, remarkLength );
+ variableData += remarkLength;
+ } else {
+ capturedShi502->shi502_remark = NULL;
+ }
+
+ //
+ // Capture the path.
+ //
+
+ if ( pathNameLength > 0 ) {
+ capturedShi502->shi502_path = (LPWSTR)variableData;
+ RtlCopyMemory( variableData, Path, pathNameLength );
+ variableData += pathNameLength;
+ } else {
+ capturedShi502->shi502_path = NULL;
+ }
+
+ //
+ // Capture the security descriptor. Use the shi502_permissions field
+ // to contain the offset to the security descriptor in the buffer.
+ //
+
+ if ( ARGUMENT_PRESENT( ConnectSecurityDescriptor ) ) {
+
+ variableData = (PCHAR)( ((ULONG)variableData + 3) & ~3 );
+ capturedShi502->shi502_permissions = (ULONG)variableData;
+ variableData += (connectSDLength - sizeof(ULONG));
+
+ RtlCopyMemory(
+ (PVOID)capturedShi502->shi502_permissions,
+ ConnectSecurityDescriptor,
+ connectSDLength - sizeof(ULONG)
+ );
+ } else {
+ capturedShi502->shi502_permissions = 0;
+ }
+
+ //
+ // Capture the self relative form of the file security descriptor.
+ //
+
+ if ( ARGUMENT_PRESENT( FileSecurityDescriptor ) ) {
+
+ variableData = (PCHAR)( ((ULONG)variableData + 3) & ~3 );
+ capturedShi502->shi502_security_descriptor = (LPBYTE) variableData;
+ variableData += ( fileSdLength - sizeof(ULONG)) ;
+
+ RtlCopyMemory(
+ (PVOID)capturedShi502->shi502_security_descriptor,
+ FileSecurityDescriptor,
+ fileSdLength - sizeof(ULONG)
+ );
+
+ } else {
+ capturedShi502->shi502_security_descriptor = (LPBYTE) NULL;
+ }
+
+
+ //
+ // Convert all the pointers in the structure to offsets from the
+ // beginning of the structure.
+ //
+
+ POINTER_TO_OFFSET( capturedShi502->shi502_netname, capturedShi502 );
+ POINTER_TO_OFFSET( capturedShi502->shi502_remark, capturedShi502 );
+ POINTER_TO_OFFSET( capturedShi502->shi502_path, capturedShi502 );
+ POINTER_TO_OFFSET( (PCHAR)capturedShi502->shi502_permissions, capturedShi502 );
+ POINTER_TO_OFFSET( (PCHAR)capturedShi502->shi502_security_descriptor, capturedShi502 );
+
+ //
+ // Set up the length of the captured buffer to return to the caller
+ // and return the captures structure.
+ //
+
+ *CapturedBufferLength = capturedBufferLength;
+
+ return capturedShi502;
+
+} // CaptureShareInfo
+
+NET_API_STATUS
+DisallowSharedLanmanNetDrives(
+ IN PUNICODE_STRING NtSharePath
+ )
+{
+ NET_API_STATUS error = NERR_Success;
+ NTSTATUS status;
+ HANDLE linkHandle;
+ OBJECT_ATTRIBUTES objAttr;
+ UNICODE_STRING linkTarget;
+ ULONG returnedLength = 0;
+ UNICODE_STRING tempNtPath;
+
+ linkTarget.Buffer = NULL;
+ linkTarget.MaximumLength = 0;
+ linkTarget.Length = 0;
+ tempNtPath = *NtSharePath;
+
+ //
+ // Remove the trailing '\\'
+ //
+
+ tempNtPath.Length -= 2;
+
+ InitializeObjectAttributes(
+ &objAttr,
+ &tempNtPath,
+ OBJ_CASE_INSENSITIVE,
+ 0,
+ NULL
+ );
+
+ status = NtOpenSymbolicLinkObject(
+ &linkHandle,
+ SYMBOLIC_LINK_QUERY,
+ &objAttr
+ );
+
+
+ if ( !NT_SUCCESS(status) ) {
+ error = ERROR_INVALID_PARAMETER;
+ goto exit;
+ }
+
+ //
+ // Get the size of the buffer needed.
+ //
+
+ status = NtQuerySymbolicLinkObject(
+ linkHandle,
+ &linkTarget,
+ &returnedLength
+ );
+
+
+ if ( !NT_SUCCESS(status) && status != STATUS_BUFFER_TOO_SMALL ) {
+ NtClose( linkHandle );
+ error = ERROR_INVALID_PARAMETER;
+ goto exit;
+ }
+
+ //
+ // Allocate our buffer
+ //
+
+ linkTarget.Length = (USHORT)returnedLength;
+ linkTarget.MaximumLength = (USHORT)(returnedLength + sizeof(WCHAR));
+ linkTarget.Buffer = MIDL_user_allocate( linkTarget.MaximumLength );
+
+ if ( linkTarget.Buffer == NULL ) {
+ NtClose( linkHandle );
+ error = ERROR_NOT_ENOUGH_MEMORY;
+ goto exit;
+ }
+
+ status = NtQuerySymbolicLinkObject(
+ linkHandle,
+ &linkTarget,
+ &returnedLength
+ );
+
+ NtClose( linkHandle );
+ if ( !NT_SUCCESS(status) ) {
+ error = ERROR_INVALID_PARAMETER;
+ goto exit;
+ }
+
+ //
+ // See if this is a lanman drive
+ //
+
+ if (_wcsnicmp(
+ linkTarget.Buffer,
+ DD_NFS_DEVICE_NAME_U,
+ wcslen(DD_NFS_DEVICE_NAME_U)) == 0) {
+
+ error = NERR_RedirectedPath;
+ goto exit;
+ }
+
+exit:
+
+ if ( linkTarget.Buffer != NULL ) {
+ MIDL_user_free( linkTarget.Buffer );
+ }
+
+ return(error);
+
+} // DisallowSharedLanmanNetDrives
+
+NET_API_STATUS
+FillStickyShareInfo(
+ IN PSRVSVC_SHARE_ENUM_INFO ShareEnumInfo,
+ IN PSHARE_INFO_502 Shi502
+ )
+
+/*++
+
+Routine Description:
+
+ This routine fills in the output buffer with data from the shi502
+ structure.
+
+Arguments:
+
+ ShareEnumInfo - contains the parameters passed in through the
+ NetShareEnumSticky api.
+ Shi502 - pointer to a shi502 structure
+
+Return Value:
+
+ status of operation.
+
+--*/
+
+{
+
+ PSHARE_INFO_502 newShi502;
+ PCHAR endOfVariableData;
+
+ ShareEnumInfo->TotalBytesNeeded += SizeShares(
+ ShareEnumInfo->Level,
+ Shi502
+ );
+
+
+ //
+ // If we have more data but ran out of space, return ERROR_MORE_DATA
+ //
+
+ if ( ShareEnumInfo->TotalBytesNeeded >
+ ShareEnumInfo->OutputBufferLength ) {
+ return(ERROR_MORE_DATA);
+ }
+
+ //
+ // Transfer data from the share info 502 structure to the output
+ // buffer.
+ //
+
+ newShi502 = (PSHARE_INFO_502)ShareEnumInfo->StartOfFixedData;
+ ShareEnumInfo->StartOfFixedData += FIXED_SIZE_OF_SHARE(ShareEnumInfo->Level);
+
+ endOfVariableData = ShareEnumInfo->EndOfVariableData;
+
+ //
+ // Case on the level to fill in the fixed structure appropriately.
+ // We fill in actual pointers in the output structure. This is
+ // possible because we are in the server FSD, hence the server
+ // service's process and address space.
+ //
+ // *** This routine assumes that the fixed structure will fit in the
+ // buffer!
+ //
+ // *** Using the switch statement in this fashion relies on the fact
+ // that the first fields on the different share structures are
+ // identical.
+ //
+
+ switch( ShareEnumInfo->Level ) {
+
+ case 502:
+
+ if ( Shi502->shi502_security_descriptor != NULL ) {
+
+ ULONG fileSDLength;
+ fileSDLength = RtlLengthSecurityDescriptor(
+ Shi502->shi502_security_descriptor
+ );
+
+ //
+ // DWord Align
+ //
+
+ endOfVariableData = (PCHAR) ( (ULONG) ( endOfVariableData -
+ fileSDLength ) & ~3 );
+
+ newShi502->shi502_security_descriptor = endOfVariableData;
+ newShi502->shi502_reserved = fileSDLength;
+
+ RtlMoveMemory(
+ newShi502->shi502_security_descriptor,
+ Shi502->shi502_security_descriptor,
+ fileSDLength
+ );
+
+ } else {
+ newShi502->shi502_security_descriptor = NULL;
+ newShi502->shi502_reserved = 0;
+ }
+
+ case 2:
+
+ //
+ // Set level 2 specific fields in the buffer. Since this server
+ // can only have user-level security, share permissions are
+ // meaningless.
+ //
+
+ newShi502->shi502_permissions = 0;
+ newShi502->shi502_max_uses = Shi502->shi502_max_uses;
+
+ //
+ // To get the current uses, we need to query the server for this
+ //
+
+ {
+ PSHARE_INFO_2 shareInfo;
+ NET_API_STATUS error;
+ DWORD entriesRead;
+ DWORD totalEntries;
+
+ error = ShareEnumCommon(
+ 2,
+ (LPBYTE *)&shareInfo,
+ (DWORD)-1,
+ &entriesRead,
+ &totalEntries,
+ NULL,
+ Shi502->shi502_netname
+ );
+
+ if ( error != NO_ERROR || entriesRead == 0 ) {
+ newShi502->shi502_current_uses = 0;
+ } else {
+ newShi502->shi502_current_uses = shareInfo->shi2_current_uses;
+ MIDL_user_free( shareInfo );
+ }
+ }
+
+ //
+ // Copy the DOS path name to the buffer.
+ //
+
+ if ( Shi502->shi502_path != NULL ) {
+ endOfVariableData -= SIZE_WSTR( Shi502->shi502_path );
+ newShi502->shi502_path = (LPTSTR) endOfVariableData;
+ wcscpy( newShi502->shi502_path, Shi502->shi502_path );
+ } else {
+ newShi502->shi502_path = NULL;
+ }
+
+ //
+ // We don't have per-share passwords (share-level security)
+ // so set the password pointer to NULL.
+ //
+
+ newShi502->shi502_passwd = NULL;
+
+ // *** Lack of break is intentional!
+
+ case 1:
+
+ newShi502->shi502_type = Shi502->shi502_type;
+
+ //
+ // Copy the remark to the buffer. The routine will handle the
+ // case where there is no remark on the share and put a pointer
+ // to a zero terminator in the buffer.
+ //
+
+ if ( Shi502->shi502_remark != NULL ) {
+ endOfVariableData -= SIZE_WSTR( Shi502->shi502_remark );
+ newShi502->shi502_remark = (LPTSTR) endOfVariableData;
+ wcscpy( newShi502->shi502_remark, Shi502->shi502_remark );
+ } else {
+ newShi502->shi502_remark = NULL;
+ }
+
+ // *** Lack of break is intentional!
+
+ case 0:
+
+ //
+ // Copy the share name to the buffer.
+ //
+
+ if ( Shi502->shi502_netname != NULL ) {
+ endOfVariableData -= SIZE_WSTR( Shi502->shi502_netname );
+ newShi502->shi502_netname = (LPTSTR) endOfVariableData;
+ wcscpy( newShi502->shi502_netname, Shi502->shi502_netname );
+ } else {
+ newShi502->shi502_remark = NULL;
+ }
+ break;
+ }
+
+ ShareEnumInfo->EndOfVariableData = endOfVariableData;
+
+ return NO_ERROR;
+
+} // FillStickyShareInfo
+
+
+NET_API_STATUS
+ShareAssignSecurityDescriptor(
+ IN PSECURITY_DESCRIPTOR PassedSecurityDescriptor,
+ OUT PSECURITY_DESCRIPTOR *NewSecurityDescriptor
+ )
+
+/*++
+
+Routine Description:
+
+ This routine converts a the generic mappings in an sd to
+ standards and specifics.
+
+Arguments:
+
+ PassedSecurityDescriptor - Security descriptor passed from the client.
+ NewSecurityDescriptor - Pointer to a buffer to receive the new sd.
+
+Return Value:
+
+ NET_API_STATUS - NO_ERROR or reason for failure.
+
+--*/
+
+{
+
+ NTSTATUS status;
+ HANDLE token;
+ PISECURITY_DESCRIPTOR tempSD;
+
+ //
+ // Since we don't trust the client, check if sd is valid.
+ //
+
+ if ( !RtlValidSecurityDescriptor( PassedSecurityDescriptor) ) {
+ return(ERROR_INVALID_PARAMETER);
+ }
+
+ //
+ // NULL out the owner, group, and sacl
+ //
+
+ tempSD = PassedSecurityDescriptor;
+ tempSD->Owner = NULL;
+ tempSD->Group = NULL;
+ tempSD->Sacl = NULL;
+ tempSD->Control &=
+ (SE_DACL_DEFAULTED | SE_DACL_PRESENT | SE_SELF_RELATIVE);
+
+
+ //
+ // Impersonate client
+ //
+
+ (VOID)RpcImpersonateClient( NULL );
+
+ status = NtOpenThreadToken(
+ NtCurrentThread(),
+ TOKEN_QUERY,
+ TRUE,
+ &token
+ );
+
+ (VOID)RpcRevertToSelf( );
+
+ if ( !NT_SUCCESS(status) ) {
+ return RtlNtStatusToDosError( status );
+ }
+
+ //
+ // Get a new sd which has the generics mapped to specifics.
+ // the returned sd is in self-relative form.
+ //
+
+ status = RtlNewSecurityObject(
+ NULL,
+ PassedSecurityDescriptor,
+ NewSecurityDescriptor,
+ FALSE,
+ token,
+ &SrvShareFileGenericMapping
+ );
+
+ ASSERT( RtlValidSecurityDescriptor( *NewSecurityDescriptor ) );
+
+ NtClose( token );
+ return RtlNtStatusToDosError( status );
+
+} // ShareAssignSecurityDescriptor
+
+
+void
+SHARE_DEL_HANDLE_rundown (
+ SHARE_DEL_HANDLE ContextHandle
+ )
+{
+ (VOID)NetrShareDelCommit( &ContextHandle );
+
+ return;
+
+} // SHARE_DEL_HANDLE_rundown
+
+
+NET_API_STATUS
+ShareEnumCommon (
+ IN DWORD Level,
+ OUT LPBYTE *Buffer,
+ IN DWORD PreferredMaximumLength,
+ OUT LPDWORD EntriesRead,
+ OUT LPDWORD TotalEntries,
+ IN OUT LPDWORD ResumeHandle OPTIONAL,
+ IN LPWSTR NetName OPTIONAL
+ )
+
+{
+ NET_API_STATUS error;
+ PSERVER_REQUEST_PACKET srp;
+
+ //
+ // Make sure that the level is valid. Since it is an unsigned
+ // value, it can never be less than 0.
+ //
+
+ if ( (Level > 2) && (Level != 502) && (Level != 1005)) {
+ return ERROR_INVALID_LEVEL;
+ }
+
+ //
+ // Set up the input parameters in the request buffer.
+ //
+
+ srp = SsAllocateSrp( );
+ if ( srp == NULL ) {
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+ srp->Level = Level;
+ if ( ARGUMENT_PRESENT( NetName ) ) {
+ srp->Flags = SRP_RETURN_SINGLE_ENTRY;
+ }
+
+ if ( ARGUMENT_PRESENT( ResumeHandle ) ) {
+ srp->Parameters.Get.ResumeHandle = *ResumeHandle;
+ } else {
+ srp->Parameters.Get.ResumeHandle = 0;
+ }
+
+ RtlInitUnicodeString( &srp->Name1, NetName );
+
+ //
+ // Get the data from the server. This routine will allocate the
+ // return buffer and handle the case where PreferredMaximumLength ==
+ // -1.
+ //
+
+ error = SsServerFsControlGetInfo(
+ FSCTL_SRV_NET_SHARE_ENUM,
+ srp,
+ (PVOID *)Buffer,
+ PreferredMaximumLength
+ );
+
+ //
+ // Set up return information.
+ //
+
+ *EntriesRead = srp->Parameters.Get.EntriesRead;
+ *TotalEntries = srp->Parameters.Get.TotalEntries;
+ if ( *EntriesRead > 0 && ARGUMENT_PRESENT( ResumeHandle ) ) {
+ *ResumeHandle = srp->Parameters.Get.ResumeHandle;
+ }
+
+ SsFreeSrp( srp );
+
+ //
+ // We need to null out the owner, group, and sacl.
+ //
+
+ if ( Level == 502 ) {
+
+ PSHARE_INFO_502 shi502 = (PSHARE_INFO_502) *Buffer;
+ PSECURITY_DESCRIPTOR fileSD;
+ ULONG i;
+
+ for ( i = 0 ; i < *EntriesRead; i++, shi502++ ) {
+
+ fileSD = shi502->shi502_security_descriptor;
+ if ( fileSD != NULL ) {
+
+ PISECURITY_DESCRIPTOR SD = fileSD;
+
+ SD->Owner = NULL;
+ SD->Group = NULL;
+ SD->Sacl = NULL;
+ SD->Control &=
+ (SE_DACL_DEFAULTED | SE_DACL_PRESENT | SE_SELF_RELATIVE);
+ ASSERT( RtlValidSecurityDescriptor( fileSD ) );
+ }
+
+ } // for
+ }
+
+ return error;
+
+} // ShareEnumCommon
+
+
+NET_API_STATUS
+ShareEnumSticky (
+ IN DWORD Level,
+ OUT LPBYTE *Buffer,
+ IN DWORD PreferredMaximumLength,
+ OUT LPDWORD EntriesRead,
+ OUT LPDWORD TotalEntries,
+ IN OUT LPDWORD ResumeHandle OPTIONAL
+ )
+
+/*++
+
+Routine Description:
+
+ This routine enumerates all the shares kept in the registry.
+
+Arguments:
+
+ Same as NetShareEnumSticky api.
+
+Return Value:
+
+ status of request.
+
+--*/
+
+{
+ NET_API_STATUS error;
+ BOOLEAN getEverything;
+ ULONG oldResumeHandle;
+ SRVSVC_SHARE_ENUM_INFO enumInfo;
+
+ //
+ // Set up the input parameters in the request buffer.
+ //
+
+ enumInfo.Level = Level;
+ if ( ARGUMENT_PRESENT( ResumeHandle ) ) {
+ enumInfo.ResumeHandle = *ResumeHandle;
+ } else {
+ enumInfo.ResumeHandle = 0;
+ }
+
+ oldResumeHandle = enumInfo.ResumeHandle;
+
+ //
+ // If the length of the second buffer is specified as -1, then we
+ // are supposed to get all the information, regardless of size.
+ // Allocate space for the output buffer and try to use it. If this
+ // fails, the SsEnumerateStickyShares will tell us just how much we
+ // really need to allocate.
+ //
+
+ if ( PreferredMaximumLength == 0xFFFFFFFF ) {
+
+ enumInfo.OutputBufferLength = INITIAL_BUFFER_SIZE;
+ getEverything = TRUE;
+
+ } else {
+
+ enumInfo.OutputBufferLength = PreferredMaximumLength;
+ getEverything = FALSE;
+ }
+
+ enumInfo.OutputBuffer = MIDL_user_allocate( enumInfo.OutputBufferLength );
+
+ if ( enumInfo.OutputBuffer == NULL ) {
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ //
+ // Make the request
+ //
+
+ error = SsEnumerateStickyShares( &enumInfo );
+
+ //
+ // If the call was successful, or there was an error other than
+ // ERROR_MORE_DATA (which indicates that the buffer wasn't large
+ // enough), or the passed in buffer size was all we're allowed to
+ // allocate, return to the caller.
+ //
+
+ if ( (error != ERROR_MORE_DATA && error != NERR_BufTooSmall) ||
+ !getEverything ) {
+
+ //
+ // If no entries were found, free the buffer and set the pointer
+ // to NULL.
+ //
+
+ if ( enumInfo.EntriesRead == 0 ) {
+ MIDL_user_free( enumInfo.OutputBuffer );
+ enumInfo.OutputBuffer = NULL;
+ }
+
+ goto exit;
+ }
+
+ //
+ // The initial buffer wasn't large enough, and we're allowed to
+ // allocate more. Free the first buffer.
+ //
+
+ MIDL_user_free( enumInfo.OutputBuffer );
+
+ //
+ // Allocate a buffer large enough to hold all the information, plus
+ // a fudge factor in case the amount of information has increased.
+ // If the amount of information increased more than the fudge factor,
+ // then we give up. This should almost never happen.
+ //
+
+ enumInfo.OutputBufferLength = enumInfo.TotalBytesNeeded + EXTRA_ALLOCATION;
+
+ enumInfo.OutputBuffer = MIDL_user_allocate( enumInfo.OutputBufferLength );
+
+ if ( enumInfo.OutputBuffer == NULL ) {
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ //
+ // Reset the resume handle in the SRP. It was altered by the first
+ // Enum attempt.
+ //
+
+ enumInfo.ResumeHandle = oldResumeHandle;
+
+ //
+ // Try again to get the information from the server, this time with the
+ // larger buffer.
+ //
+
+ error = SsEnumerateStickyShares( &enumInfo );
+
+exit:
+ //
+ // Set up return information.
+ //
+
+ *Buffer = enumInfo.OutputBuffer;
+ *EntriesRead = enumInfo.EntriesRead;
+ *TotalEntries = enumInfo.TotalEntries;
+ if ( *EntriesRead > 0 && ARGUMENT_PRESENT( ResumeHandle ) ) {
+ *ResumeHandle = enumInfo.ResumeHandle;
+ }
+
+ return error;
+
+} // ShareEnumSticky
+
+
+ULONG
+SizeShares (
+ IN ULONG Level,
+ IN PSHARE_INFO_502 Shi502
+ )
+
+/*++
+
+Routine Description:
+
+ This routine returns the size the passed-in share would take up in
+ an API output buffer.
+
+Arguments:
+
+ Level - level of request
+ Shi502 - pointer to a shi502 structure
+
+Return Value:
+
+ ULONG - The number of bytes the share would take up in the
+ output buffer.
+
+--*/
+
+{
+ ULONG shareSize = 0;
+
+ switch ( Level ) {
+ case 502:
+ if ( Shi502->shi502_security_descriptor != NULL ) {
+
+ //
+ // add 4 bytes for possible padding
+ //
+
+ shareSize = sizeof( ULONG ) +
+ RtlLengthSecurityDescriptor( Shi502->shi502_security_descriptor );
+ }
+
+ case 2:
+ shareSize += SIZE_WSTR( Shi502->shi502_path );
+
+ case 1:
+ shareSize += SIZE_WSTR( Shi502->shi502_remark );
+
+ case 0:
+ shareSize += SIZE_WSTR( Shi502->shi502_netname );
+
+ }
+
+ return ( shareSize + FIXED_SIZE_OF_SHARE( Level ) );
+
+} // SizeShares
+
+
+BOOLEAN
+ValidSharePath(
+ IN LPWSTR SharePath
+ )
+/*++
+
+Routine Description:
+
+ This routine checks to see if .. and . exists on the path. If they do,
+ then we reject this path name.
+
+Arguments:
+
+ SharePath - The share path to be checked.
+
+Return Value:
+
+ TRUE, if path is ok.
+
+--*/
+
+{
+
+ LPWCH source = SharePath;
+
+ //
+ // Walk through the pathname until we reach the zero terminator. At
+ // the start of this loop, source points to the first charaecter
+ // after a directory separator or the first character of the
+ // pathname.
+ //
+
+ //
+ // Allow the NT naming convention of slash slash . slash through here
+ //
+ if( IS_SLASH_SLASH_NAME( source ) ) {
+
+ //
+ // We have a path which starts with slash slash
+ // Set the buffer ptr so we start checking the pathname after
+ // the slash slash dot
+
+ source += 3;
+ }
+
+ while ( *source != L'\0' ) {
+
+ if ( *source == L'.') {
+ source++;
+ if ( ( IS_PATH_SEPARATOR(*source) ) ||
+ ( (*source++ == L'.') &&
+ IS_PATH_SEPARATOR(*source) ) ) {
+
+ //
+ // '.' and '..' appear as a directory names. Reject.
+ //
+
+ return(FALSE);
+ }
+ }
+
+ //
+ // source does not point to a dot, so continue until we see
+ // another directory separator
+ //
+
+ while ( *source != L'\0' ) {
+ if ( *source++ == L'\\' ) {
+ break;
+ }
+ }
+ }
+
+ return TRUE;
+
+} // ValidSharePath
diff --git a/private/net/svcdlls/srvsvc/server/srvconfg.h b/private/net/svcdlls/srvsvc/server/srvconfg.h
new file mode 100644
index 000000000..d9b3ee22a
--- /dev/null
+++ b/private/net/svcdlls/srvsvc/server/srvconfg.h
@@ -0,0 +1,648 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ SrvConfg.h
+
+Abstract:
+
+ This file contains default server settings for startup.
+
+Author:
+
+ David Treadwell (davidtr) 1-Mar-1991
+
+Revision History:
+
+--*/
+
+#ifndef _SRVCONFG_
+#define _SRVCONFG_
+
+//
+// The platform ID for NT servers. This indicates the info level that
+// should be called for platform-specific information.
+//
+
+#define DEF_PLATFORM_ID SV_PLATFORM_ID_NT
+#define MIN_PLATFORM_ID SV_PLATFORM_ID_NT
+#define MAX_PLATFORM_ID SV_PLATFORM_ID_NT
+
+//
+// The default name is used for the server set and get info APIs as well
+// as the transport name if no overriding transport name is specified.
+//
+
+#define DEF_NAME L"NTSERVER"
+
+//
+// Version definitions--these indicate LM 3.0.
+//
+
+#define DEF_VERSION_MAJOR 3
+#define MIN_VERSION_MAJOR 3
+#define MAX_VERSION_MAJOR 3
+
+#define DEF_VERSION_MINOR 10
+#define MIN_VERSION_MINOR 10
+#define MAX_VERSION_MINOR 10
+
+//
+// The server type.
+//
+
+#define DEF_TYPE 0
+#define MIN_TYPE 0
+#define MAX_TYPE 0xFFFFFFFF
+
+//
+// The server comment is used only for the server get and set info APIs.
+//
+
+#define DEF_COMMENT L""
+
+//
+// The maximum number of users that may be logged on to the server
+// simultaneously.
+//
+
+#define DEF_USERS 0xFFFFFFFF
+#define MIN_USERS 1
+#define MAX_USERS 0xFFFFFFFF
+
+//
+// The autodisconnect time: when a client is idle for this many minutes,
+// the server closes the connection, but only if the client has no open
+// files/pipes.
+//
+
+#define DEF_DISC 15
+#define MIN_DISC 0 // zero minutes -- disconnect ASAP
+#define MAX_DISC 0xFFFFFFFF
+
+//
+// The IPX autodisconnect time: when a client doesn't send any SMBs
+// for this many minutes, the server closes the 'connection', even if
+// the client has open files/pipes.
+//
+
+#define DEF_CONNECTIONLESSAUTODISC 15
+#define MIN_CONNECTIONLESSAUTODISC 15
+#define MAX_CONNECTIONLESSAUTODISC 0xFFFFFFFF
+
+//
+// Parameters dealing with server announcements: whether the server is
+// hidden (no announcements), the announce interval, the randomization
+// factor for the accounce interval, and whether the server announces
+// itself as a time source.
+//
+
+#define DEF_HIDDEN FALSE
+
+#define DEF_ANNOUNCE 4 * 60
+#define MIN_ANNOUNCE 1
+#define MAX_ANNOUNCE 65535
+
+#define DEF_ANNDELTA 3000
+#define MIN_ANNDELTA 0
+#define MAX_ANNDELTA 65535
+
+#define DEF_TIMESOURCE FALSE
+#define DEF_ACCEPTDOWNLEVELAPIS TRUE
+#define DEF_LMANNOUNCE FALSE
+
+//
+// A fully qualified path to the user directories.
+//
+
+#define DEF_USERPATH L"c:\\"
+
+//
+// The domain name to which to send server announcements.
+//
+
+#define DEF_DOMAIN L"DOMAIN"
+
+//
+// Server "heuristics", enabling various capabilities.
+//
+
+#define DEF_ENABLEOPLOCKS TRUE
+#define DEF_ENABLEFCBOPENS TRUE
+#define DEF_ENABLESOFTCOMPAT TRUE
+#define DEF_ENABLERAW TRUE
+#define DEF_ENABLESHAREDNETDRIVES FALSE
+#define DEF_ENABLEFORCEDLOGOFF TRUE
+#define DEF_ENABLEOPLOCKFORCECLOSE FALSE
+#define DEF_REMOVEDUPLICATESEARCHES TRUE
+#define DEF_RESTRICTNULLSESSACCESS TRUE
+#define DEF_ENABLEWFW311DIRECTIPX TRUE
+
+//
+// Receive buffer size, receive work item count, and receive IRP stack
+// size.
+//
+
+#define DEF_SIZREQBUF 4356
+#define MIN_SIZREQBUF 512
+#define MAX_SIZREQBUF 65535
+
+#define DEF_INITWORKITEMS 4
+#define MIN_INITWORKITEMS 1
+#define MAX_INITWORKITEMS 512
+
+#define DEF_MAXWORKITEMS 128
+#define MIN_MAXWORKITEMS 1
+#define MAX_MAXWORKITEMS 65535 // arbitrary
+
+#define DEF_RAWWORKITEMS 4
+#define MIN_RAWWORKITEMS 1
+#define MAX_RAWWORKITEMS 512
+
+#define DEF_MAXRAWWORKITEMS 16
+#define MIN_MAXRAWWORKITEMS 1
+#define MAX_MAXRAWWORKITEMS 512
+
+#define DEF_IRPSTACKSIZE 5
+#define MIN_IRPSTACKSIZE 1
+#define MAX_IRPSTACKSIZE 12
+
+//
+// Maximum raw mode buffer size. (This isn't actually configurable --
+// the server must always be prepared to receive raw requests for up to
+// 65535 bytes.)
+//
+
+#define DEF_MAXRAWBUFLEN 65535
+#define MIN_MAXRAWBUFLEN 65535
+#define MAX_MAXRAWBUFLEN 65535
+
+//
+// Cache-related parameters.
+//
+
+#define DEF_MAXCOPYREADLEN 8192
+#define MIN_MAXCOPYREADLEN 0
+#define MAX_MAXCOPYREADLEN 0xFFFFFFFF
+
+#define DEF_MAXCOPYWRITELEN 0
+#define MIN_MAXCOPYWRITELEN 0
+#define MAX_MAXCOPYWRITELEN 0xFFFFFFFF
+
+//
+// Free connection count.
+//
+
+#define DEF_MAXFREECONNECTIONS 2
+#define MIN_MAXFREECONNECTIONS 2
+#define MAX_MAXFREECONNECTIONS 100 // arbitrary
+
+#define DEF_MINFREECONNECTIONS 2
+#define MIN_MINFREECONNECTIONS 2
+#define MAX_MINFREECONNECTIONS 32 // arbitrary
+
+//
+// Initial and maximum table sizes.
+//
+
+#define DEF_INITSESSTABLE 4
+#define MIN_INITSESSTABLE 1
+#define MAX_INITSESSTABLE 64
+
+#define DEF_SESSUSERS 2048
+#define MIN_SESSUSERS 1
+#define MAX_SESSUSERS 2048
+#define ABSOLUTE_MAX_SESSION_TABLE_SIZE 2048 // Limited by index bits
+
+#define DEF_INITCONNTABLE 8
+#define MIN_INITCONNTABLE 1
+#define MAX_INITCONNTABLE 128
+
+#define DEF_SESSCONNS 2048
+#define MIN_SESSCONNS 1
+#define MAX_SESSCONNS 2048
+#define ABSOLUTE_MAX_TREE_TABLE_SIZE 2048 // Limited by index bits
+
+#define DEF_INITFILETABLE 16
+#define MIN_INITFILETABLE 1
+#define MAX_INITFILETABLE 256
+
+#define DEF_SESSOPENS 2048
+#define MIN_SESSOPENS 1
+#define MAX_SESSOPENS 2048
+#define ABSOLUTE_MAX_FILE_TABLE_SIZE 2048 // Limited by index bits
+
+#define DEF_INITSEARCHTABLE 8
+#define MIN_INITSEARCHTABLE 1
+#define MAX_INITSEARCHTABLE 2048
+
+#define DEF_OPENSEARCH 2048
+#define MIN_OPENSEARCH 1
+#define MAX_OPENSEARCH 2048
+#define ABSOLUTE_MAX_SEARCH_TABLE_SIZE 2048 // Limited by index bits
+
+#define DEF_MAXGLOBALOPENSEARCH 4096
+#define MIN_MAXGLOBALOPENSEARCH 1
+#define MAX_MAXGLOBALOPENSEARCH 0xFFFFFFFF
+
+#define DEF_INITCOMMTABLE 4
+#define MIN_INITCOMMTABLE 1
+#define MAX_INITCOMMTABLE 32
+
+#define DEF_CHDEVS 32
+#define MIN_CHDEVS 1
+#define MAX_CHDEVS 32
+#define ABSOLUTE_MAX_COMM_DEVICE_TABLE_SIZE 32
+
+//
+// Core search timeouts. The first is for active core searches, the second
+// is for core searches where we have returned STATUS_NO_MORE_FILES. The
+// second should be shorter, as these are presumably complete. All values
+// are specified in seconds.
+//
+
+#define DEF_MAXKEEPSEARCH (60 * 60)
+#define MIN_MAXKEEPSEARCH 10
+#define MAX_MAXKEEPSEARCH 10000
+
+//
+// *** These 3 parameters are no longer used.
+//
+
+#define DEF_MINKEEPSEARCH (60 * 8)
+#define MIN_MINKEEPSEARCH 5
+#define MAX_MINKEEPSEARCH 5000
+
+#define DEF_MAXKEEPCOMPLSEARCH (60 * 10)
+#define MIN_MAXKEEPCOMPLSEARCH 2
+#define MAX_MAXKEEPCOMPLSEARCH 10000
+
+#define DEF_MINKEEPCOMPLSEARCH (60 * 4)
+#define MIN_MINKEEPCOMPLSEARCH 1
+#define MAX_MINKEEPCOMPLSEARCH 1000
+
+//
+// SrvWorkerThreadCountAdd is added to the system CPU count to determine
+// how many worker threads the server will have.
+//
+// *** THIS PARAMETER IS NO LONGER USED!
+//
+
+#define DEF_THREADCOUNTADD 2
+#define MIN_THREADCOUNTADD 0
+#define MAX_THREADCOUNTADD 10
+
+//
+// SrvBlockingThreadCount is the number of blocking threads that get
+// started at server initialization.
+//
+// *** THIS PARAMETER IS NO LONGER USED!
+//
+
+#define DEF_NUMBLOCKTHREADS 2
+#define MIN_NUMBLOCKTHREADS 1
+#define MAX_NUMBLOCKTHREADS 10
+
+//
+// This is the maximum number of threads for each server queue per processor
+//
+#define DEF_MAXTHREADSPERQUEUE 10
+#define MIN_MAXTHREADSPERQUEUE 1
+#define MAX_MAXTHREADSPERQUEUE 65535
+
+//
+// Scavenger thread idle wait time.
+//
+
+#define DEF_SCAVTIMEOUT 30
+#define MIN_SCAVTIMEOUT 1
+#define MAX_SCAVTIMEOUT 300
+
+//
+// The server periodically recomputes the average work queue depth.
+// This is how often the recomputation is done (in secs)
+//
+#define DEF_QUEUESAMPLESECS 5
+#define MIN_QUEUESAMPLESECS 1
+#define MAX_QUEUESAMPLESECS 65535
+
+//
+// For multiprocessor systems, the server tries to dynamically
+// balance the workload over the processors in the system. The
+// processor handling the client's dpc's is known as the preferred
+// processor for this client. The server looks at the average work
+// queue depth for each of the processors in the system. If the
+// client is currently on the preferred processor, but some other
+// processor's average work queue length + current queue length is
+// OTHERQUEUEAFFINITY shorter, then the client is reassigned to
+// that processor
+//
+#define DEF_OTHERQUEUEAFFINITY 3
+#define MIN_OTHERQUEUEAFFINITY 1
+#define MAX_OTHERQUEUEAFFINITY 65535
+
+//
+// If the client is not currently its preferred processor, but the
+// preferred processor's average work queue length + current queue
+// length is no more than PREFERREDAFFINITY items longer, then this
+// client is reassigned to its preferred processor
+//
+#define DEF_PREFERREDAFFINITY 1
+#define MIN_PREFERREDAFFINITY 0
+#define MAX_PREFERREDAFFINITY 65535
+
+//
+// Each client looks at the other processor queues every BALANCECOUNT
+// operations to see if it would be better served by a different
+// processor
+//
+#define DEF_BALANCECOUNT 1500
+#define MIN_BALANCECOUNT 10
+#define MAX_BALANCECOUNT 65535
+
+//
+// If a client is not currently assigned to its preferred processor, we've
+// found that cpu utilization can drop if server responses are sent from the
+// client's preferred processor (most certainly because transport data is not
+// sloshed between cpus). Unfortunately this can adversely affect throughput
+// by a couple of percentage points on some platforms. This setting affects
+// whether these responses are requeued to the preferred processor.
+//
+// OBSOLETE!
+//
+#define DEF_SENDSFROMPREFERREDPROCESSOR TRUE
+
+//
+// Does the server support the bulk transfer SMBs?
+//
+#define DEF_ENABLEBULKTRANSFER FALSE
+
+//
+// Does the server support compressed bulk transfers?
+//
+#define DEF_ENABLECOMPRESSION FALSE
+
+//
+// If an NTAS, should the server automatically create the drive$ shares?
+//
+#define DEF_AUTOSHARESERVER TRUE
+
+//
+// If a workstation, should the server automaticaly create the drive$ shares?
+//
+#define DEF_AUTOSHAREWKS TRUE
+
+//
+// Various information variables for the server.
+//
+
+#define DEF_MAXMPXCT 50
+#define MIN_MAXMPXCT 1
+#define MAX_MAXMPXCT 65535 // limited by SMB word field
+
+//
+// Time server waits for an an oplock to break before failing an open
+// request
+//
+
+#define DEF_OPLOCKBREAKWAIT 35
+#define MIN_OPLOCKBREAKWAIT 10
+#define MAX_OPLOCKBREAKWAIT 180
+
+//
+// Time server waits for an oplock break response.
+//
+
+#define DEF_OPLOCKBREAKRESPONSEWAIT 35
+#define MIN_OPLOCKBREAKRESPONSEWAIT 10
+#define MAX_OPLOCKBREAKRESPONSEWAIT 180
+
+//
+// This is supposed to indicate how many virtual connections are allowed
+// between this server and client machines. It should always be set to
+// one, though more VCs can be established. This duplicates the LM 2.0
+// server's behavior.
+//
+
+#define DEF_SESSVCS 1
+#define MIN_SESSVCS 1
+#define MAX_SESSVCS 1
+
+//
+// Receive work item thresholds
+//
+
+//
+// The minimum desirable number of free receive workitems
+//
+
+#define DEF_MINRCVQUEUE 2
+#define MIN_MINRCVQUEUE 0
+#define MAX_MINRCVQUEUE 10
+
+//
+// The minimum number of free receive work items available before
+// the server will start processing a potentially blocking SMB.
+//
+
+#define DEF_MINFREEWORKITEMS 2
+#define MIN_MINFREEWORKITEMS 0
+#define MAX_MINFREEWORKITEMS 10
+
+//
+// The maximum amount of time an "extra" work item can be idle before it
+// is freed back to the system.
+//
+
+#define DEF_MAXWORKITEMIDLETIME 30 // seconds
+#define MIN_MAXWORKITEMIDLETIME 10
+#define MAX_MAXWORKITEMIDLETIME 1800
+
+//
+// Size of the shared memory section used for communication between the
+// server and XACTSRV.
+//
+
+#define DEF_XACTMEMSIZE 0x100000 // 1 MB
+#define MIN_XACTMEMSIZE 0x10000 // 64k
+#define MAX_XACTMEMSIZE 0x1000000 // 16 MB
+
+//
+// Priority of server FSP threads. Specified relative to the base
+// priority of the process. Valid values are between -2 and 2, or 15.
+//
+
+#define DEF_THREADPRIORITY 1
+#define MIN_THREADPRIORITY 0
+#define MAX_THREADPRIORITY THREAD_BASE_PRIORITY_LOWRT
+
+//
+// Limits on server memory usage.
+//
+
+#define DEF_MAXPAGEDMEMORYUSAGE 0xFFFFFFFF
+#define MIN_MAXPAGEDMEMORYUSAGE 0x100000 // 1MB
+#define MAX_MAXPAGEDMEMORYUSAGE 0xFFFFFFFF
+
+#define DEF_MAXNONPAGEDMEMORYUSAGE 0xFFFFFFFF
+#define MIN_MAXNONPAGEDMEMORYUSAGE 0x100000 // 1MB
+#define MAX_MAXNONPAGEDMEMORYUSAGE 0xFFFFFFFF
+
+//
+// The server keeps a small list of free RFCB structures to avoid hitting
+// the heap. This is the number in that list, per processor
+//
+#define DEF_MAXFREERFCBS 20
+#define MIN_MAXFREERFCBS 0
+#define MAX_MAXFREERFCBS 65535
+
+//
+// The server keeps a small list of free MFCB structures to avoid hitting
+// the heap. This is the number in that list, per processor
+//
+#define DEF_MAXFREEMFCBS 20
+#define MIN_MAXFREEMFCBS 0
+#define MAX_MAXFREEMFCBS 65535
+
+//
+// The server keeps a small list of free LFCB structures to avoid hitting
+// the heap. This is the number in that list, per processor
+//
+#define DEF_MAXFREELFCBS 20
+#define MIN_MAXFREELFCBS 0
+#define MAX_MAXFREELFCBS 65535
+
+//
+// The server keeps a small list of freed pool memory chunks to avoid hitting
+// the heap. This is the number in that list, per processor
+//
+#define DEF_MAXFREEPAGEDPOOLCHUNKS 50
+#define MIN_MAXFREEPAGEDPOOLCHUNKS 0
+#define MAX_MAXFREEPAGEDPOOLCHUNKS 65535
+
+//
+// The chunks that are kept in the free pool list must be at least this size:
+//
+#define DEF_MINPAGEDPOOLCHUNKSIZE 128
+#define MIN_MINPAGEDPOOLCHUNKSIZE 0
+#define MAX_MINPAGEDPOOLCHUNKSIZE 65535
+
+//
+// The chunks must be no larger than this
+//
+#define DEF_MAXPAGEDPOOLCHUNKSIZE 512
+#define MIN_MAXPAGEDPOOLCHUNKSIZE 0
+#define MAX_MAXPAGEDPOOLCHUNKSIZE 65535
+
+//
+// Alert information
+//
+
+#define DEF_ALERTSCHEDULE 5 // 5 minutes
+#define MIN_ALERTSCHEDULE 1
+#define MAX_ALERTSCHEDULE 65535
+
+#define DEF_ERRORTHRESHOLD 10 // 10 errors
+#define MIN_ERRORTHRESHOLD 1
+#define MAX_ERRORTHRESHOLD 65535
+
+#define DEF_NETWORKERRORTHRESHOLD 5 // 5% errors
+#define MIN_NETWORKERRORTHRESHOLD 1
+#define MAX_NETWORKERRORTHRESHOLD 100
+
+#define DEF_DISKSPACETHRESHOLD 10 // 10% free disk space
+#define MIN_DISKSPACETHRESHOLD 0
+#define MAX_DISKSPACETHRESHOLD 99
+
+//
+// Link speed parameters
+//
+
+#define DEF_MAXLINKDELAY 60 // seconds
+#define MIN_MAXLINKDELAY 0
+#define MAX_MAXLINKDELAY 0x100000
+
+#define DEF_MINLINKTHROUGHPUT 0 // bytes per second
+#define MIN_MINLINKTHROUGHPUT 0
+#define MAX_MINLINKTHROUGHPUT 0xFFFFFFFF
+
+#define DEF_LINKINFOVALIDTIME 60 // seconds
+#define MIN_LINKINFOVALIDTIME 0
+#define MAX_LINKINFOVALIDTIME 0x100000
+
+#define DEF_SCAVQOSINFOUPDATETIME 300 // seconds
+#define MIN_SCAVQOSINFOUPDATETIME 0
+#define MAX_SCAVQOSINFOUPDATETIME 0x100000
+
+//
+// Sharing violation retry delays/count
+//
+
+#define DEF_SHARINGVIOLATIONRETRIES 5 // number of retries
+#define MIN_SHARINGVIOLATIONRETRIES 0
+#define MAX_SHARINGVIOLATIONRETRIES 1000
+
+#define DEF_SHARINGVIOLATIONDELAY 200 // milliseconds
+#define MIN_SHARINGVIOLATIONDELAY 0
+#define MAX_SHARINGVIOLATIONDELAY 1000
+
+//
+// Lock violation delay
+//
+
+#define DEF_LOCKVIOLATIONDELAY 250 // milliseconds
+#define MIN_LOCKVIOLATIONDELAY 0
+#define MAX_LOCKVIOLATIONDELAY 1000
+
+#define DEF_LOCKVIOLATIONOFFSET 0xEF000000
+#define MIN_LOCKVIOLATIONOFFSET 0
+#define MAX_LOCKVIOLATIONOFFSET 0xFFFFFFFF
+
+//
+// length to switchover to mdl reads
+//
+
+#define DEF_MDLREADSWITCHOVER 1024
+#define MIN_MDLREADSWITCHOVER 512
+#define MAX_MDLREADSWITCHOVER 65535
+
+//
+// Number of closed RFCBs that can be cached
+//
+
+#define DEF_CACHEDOPENLIMIT 5
+#define MIN_CACHEDOPENLIMIT 0
+#define MAX_CACHEDOPENLIMIT 65535
+
+//
+// Number of directory names to cache for quick checkpath calls
+//
+
+#define DEF_CACHEDDIRECTORYLIMIT 5
+#define MIN_CACHEDDIRECTORYLIMIT 0
+#define MAX_CACHEDDIRECTORYLIMIT 65535
+
+
+//
+// Max length for a copy buffer operation, as opposed to taking the
+// NDIS buffer in total.
+//
+
+#define DEF_MAXCOPYLENGTH 512
+#define MIN_MAXCOPYLENGTH 40
+#define MAX_MAXCOPYLENGTH 65535
+
+//
+// *** Change the following defines to limit WinNT (vs. NTAS) parameters.
+//
+// *** If you make a change here, you need to make the same change in
+// srv\srvconfg.h!
+
+#define MAX_USERS_WKSTA 10
+#define MAX_MAXWORKITEMS_WKSTA 64
+#define MAX_THREADS_WKSTA 5
+
+#endif // ifndef _SRVCONFG_
+
diff --git a/private/net/svcdlls/srvsvc/server/srvinfo.c b/private/net/svcdlls/srvsvc/server/srvinfo.c
new file mode 100644
index 000000000..c0fd5032b
--- /dev/null
+++ b/private/net/svcdlls/srvsvc/server/srvinfo.c
@@ -0,0 +1,677 @@
+/*++
+
+Copyright (c) 1991-1992 Microsoft Corporation
+
+Module Name:
+
+ SrvInfo.c
+
+Abstract:
+
+ This module contains support for the server get and set info APIs
+ in the server service.
+
+Author:
+
+ David Treadwell (davidtr) 7-Mar-1991
+
+Revision History:
+
+--*/
+
+#include "srvsvcp.h"
+#include "ssdata.h"
+#include "ssreg.h"
+
+#include <netlibnt.h>
+
+#include <tstr.h>
+#include <lmerr.h>
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetrServerGetInfo (
+ IN LPWSTR ServerName,
+ IN DWORD Level,
+ OUT LPSERVER_INFO InfoStruct
+ )
+
+/*++
+
+Routine Description:
+
+ This routine uses the server parameters stored in the server service
+ to return the server information.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NET_API_STATUS - NO_ERROR or reason for failure.
+
+--*/
+
+{
+ ULONG outputBufferLength;
+ NET_API_STATUS error;
+ ACCESS_MASK desiredAccess;
+ LPWSTR DomainName;
+ PNAME_LIST_ENTRY service;
+ PTRANSPORT_LIST_ENTRY transport;
+ UCHAR serverNameBuf[ MAX_PATH ];
+ UNICODE_STRING ServerNameUnicode;
+ NTSTATUS status;
+ ULONG namelen;
+
+ //
+ // Determine the access required for the requested level of
+ // information.
+ //
+
+ switch ( Level ) {
+
+ case 100:
+ case 101:
+
+ desiredAccess = SRVSVC_CONFIG_USER_INFO_GET;
+ break;
+
+ case 102:
+ case 502:
+
+ desiredAccess = SRVSVC_CONFIG_POWER_INFO_GET;
+ break;
+
+ case 503:
+
+ desiredAccess = SRVSVC_CONFIG_ADMIN_INFO_GET;
+ break;
+
+ default:
+ return ERROR_INVALID_LEVEL;
+ }
+
+ //
+ // Make sure that the caller has that level of access.
+ //
+
+ error = SsCheckAccess(
+ &SsConfigInfoSecurityObject,
+ desiredAccess
+ );
+
+ if ( error != NO_ERROR ) {
+ return ERROR_ACCESS_DENIED;
+ }
+
+ //
+ // Acquire the resource that protects server information. Since
+ // we'll only read the information, get shared access to the
+ // resource.
+ //
+
+ (VOID)RtlAcquireResourceShared( &SsServerInfoResource, TRUE );
+
+ if( ServerName == NULL ) {
+ ServerName = SsData.ServerNameBuffer;
+ }
+
+ //
+ // Convert the server name
+ //
+
+ if( ServerName[0] == L'\\' && ServerName[1] == L'\\' ) {
+ ServerName += 2;
+ }
+
+ RtlInitUnicodeString( &ServerNameUnicode, ServerName );
+ error = ConvertStringToTransportAddress( &ServerNameUnicode, serverNameBuf, &namelen );
+ if( error != NERR_Success ) {
+ RtlReleaseResource( &SsServerInfoResource );
+ return error;
+ }
+
+ //
+ // Look for the NAME_LIST_ENTRY entry that represents the name of the server
+ // the client referred to.
+ //
+
+ DomainName = SsData.DomainNameBuffer;
+
+ for( service = SsServerNameList; service != NULL; service = service->Next ) {
+
+ if( service->TransportAddressLength != namelen ) {
+ continue;
+ }
+
+
+ if( RtlEqualMemory( serverNameBuf, service->TransportAddress, namelen ) ) {
+ DomainName = service->DomainName;
+ break;
+ }
+ }
+
+ //
+ // If we didn't find an entry, find and use the primary entry
+ //
+ if( service == NULL ) {
+ for( service = SsServerNameList; service != NULL; service = service->Next ) {
+ if( service->PrimaryName ) {
+ DomainName = service->DomainName;
+ break;
+ }
+ }
+ }
+
+ //
+ // Use the level parameter to determine how much space to allocate
+ // and how to fill it in.
+ //
+
+ switch ( Level ) {
+
+ case 100: {
+
+ PSERVER_INFO_100 sv100;
+
+ //
+ // All we copy is the server name.
+ //
+
+ outputBufferLength = sizeof(SERVER_INFO_100) +
+ STRSIZE( ServerName);
+
+ sv100 = MIDL_user_allocate( outputBufferLength );
+ if ( sv100 == NULL ) {
+ RtlReleaseResource( &SsServerInfoResource );
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ //
+ // Copy over the fixed portion of the buffer.
+ //
+
+ RtlCopyMemory( sv100, &SsData.ServerInfo102, sizeof(SERVER_INFO_100) );
+
+ //
+ // Set up the name string.
+ //
+
+ sv100->sv100_name = (LPWSTR)( sv100 + 1 );
+ STRCPY( sv100->sv100_name, ServerName );
+
+ //
+ // Set up the output buffer pointer.
+ //
+
+ InfoStruct->ServerInfo100 = sv100;
+
+ break;
+ }
+
+ case 101: {
+
+ PSERVER_INFO_101 sv101;
+
+ //
+ // All we copy is the server name.
+ //
+
+ outputBufferLength = sizeof(SERVER_INFO_101) +
+ STRSIZE( ServerName ) +
+ STRSIZE( SsData.ServerCommentBuffer ) ;
+
+ sv101 = MIDL_user_allocate( outputBufferLength );
+ if ( sv101 == NULL ) {
+ RtlReleaseResource( &SsServerInfoResource );
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ //
+ // Copy over the fixed portion of the buffer.
+ //
+
+ RtlCopyMemory( sv101, &SsData.ServerInfo102, sizeof(SERVER_INFO_101) );
+
+ if( service != NULL ) {
+ sv101->sv101_type = service->ServiceBits;
+ for( transport = service->Transports; transport; transport = transport->Next ) {
+ sv101->sv101_type |= transport->ServiceBits;
+ }
+ } else {
+ //
+ // If there are no transports,
+ // return the global information.
+ //
+
+ sv101->sv101_type = SsGetServerType() | SsData.ServiceBits;
+ }
+
+
+ //
+ // Set up the variable portion of the buffer.
+ //
+
+ sv101->sv101_name = (LPWSTR)( sv101 + 1 );
+ STRCPY( sv101->sv101_name, ServerName );
+
+ sv101->sv101_comment = (LPWSTR)( (PCHAR)sv101->sv101_name +
+ STRSIZE( ServerName ));
+ STRCPY( sv101->sv101_comment, SsData.ServerCommentBuffer );
+
+ //
+ // Set up the output buffer pointer.
+ //
+
+ InfoStruct->ServerInfo101 = sv101;
+
+ break;
+ }
+
+ case 102: {
+
+ PSERVER_INFO_102 sv102;
+
+ //
+ // We copy the server name, server comment, and user path
+ // buffer.
+ //
+
+ outputBufferLength = sizeof(SERVER_INFO_102) +
+ STRSIZE( ServerName ) +
+ STRSIZE( SsData.ServerCommentBuffer ) +
+ STRSIZE( SsData.UserPathBuffer ) ;
+
+ sv102 = MIDL_user_allocate( outputBufferLength );
+ if ( sv102 == NULL ) {
+ RtlReleaseResource( &SsServerInfoResource );
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ //
+ // Copy over the fixed portion of the buffer.
+ //
+
+ RtlCopyMemory( sv102, &SsData.ServerInfo102, sizeof(SERVER_INFO_102) );
+
+ if( service != NULL ) {
+ sv102->sv102_type = service->ServiceBits;
+ for( transport = service->Transports; transport; transport = transport->Next ) {
+ sv102->sv102_type |= transport->ServiceBits;
+ }
+ } else {
+ //
+ // If there are no transports,
+ // return the global information.
+ //
+
+ sv102->sv102_type = SsGetServerType() | SsData.ServiceBits;
+ }
+
+ //
+ // Set up the server name.
+ //
+
+ sv102->sv102_name = (LPWSTR)( sv102 + 1 );
+ STRCPY( sv102->sv102_name, ServerName );
+
+ //
+ // Set up the server comment.
+ //
+
+ sv102->sv102_comment = (LPWSTR)( (PCHAR)sv102->sv102_name + STRSIZE( ServerName ));
+ STRCPY( sv102->sv102_comment, SsData.ServerCommentBuffer );
+
+ //
+ // Set up the user path.
+ //
+
+ sv102->sv102_userpath = (LPWSTR)( (PCHAR)sv102->sv102_comment +
+ STRSIZE( sv102->sv102_comment ) );
+ STRCPY( sv102->sv102_userpath, SsData.UserPathBuffer );
+
+ //
+ // Set up the output buffer pointer.
+ //
+
+ InfoStruct->ServerInfo102 = sv102;
+
+ break;
+ }
+
+ case 502:
+
+ //
+ // Allocate enough space to hold the fixed structure. This level has
+ // no variable structure.
+ //
+
+ InfoStruct->ServerInfo502 = MIDL_user_allocate( sizeof(SERVER_INFO_502) );
+ if ( InfoStruct->ServerInfo502 == NULL ) {
+ RtlReleaseResource( &SsServerInfoResource );
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ //
+ // Copy the data from the server service buffer to the user buffer.
+ //
+
+ RtlCopyMemory(
+ InfoStruct->ServerInfo502,
+ &SsData.ServerInfo599,
+ sizeof(SERVER_INFO_502)
+ );
+
+ break;
+
+ case 503: {
+
+ PSERVER_INFO_503 sv503;
+
+ outputBufferLength = sizeof( *sv503 ) + STRSIZE( DomainName );
+
+ sv503 = MIDL_user_allocate( outputBufferLength );
+
+ if ( sv503 == NULL ) {
+ RtlReleaseResource( &SsServerInfoResource );
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ //
+ // Copy the data from the server service buffer to the user buffer.
+ //
+
+ RtlCopyMemory( sv503, &SsData.ServerInfo599, sizeof( *sv503 ) );
+
+ //
+ // Copy the domain name
+ //
+ sv503->sv503_domain = (LPWSTR)( sv503 + 1 );
+ STRCPY( sv503->sv503_domain, DomainName );
+
+ InfoStruct->ServerInfo503 = sv503;
+
+ break;
+ }
+
+ default:
+
+ RtlReleaseResource( &SsServerInfoResource );
+ return ERROR_INVALID_LEVEL;
+ }
+
+ RtlReleaseResource( &SsServerInfoResource );
+
+ return NO_ERROR;
+
+} // NetrServerGetInfo
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetrServerSetInfo (
+ IN LPWSTR ServerName,
+ IN DWORD Level,
+ IN LPSERVER_INFO InfoStruct,
+ OUT LPDWORD ErrorParameter OPTIONAL
+ )
+
+/*++
+
+Routine Description:
+
+ This routine sets information in the server service and server.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NET_API_STATUS - NO_ERROR or reason for failure.
+
+--*/
+
+{
+ NET_API_STATUS error;
+ ULONG i;
+ LONG parmnum;
+ BOOLEAN validLevel = FALSE;
+ PSERVER_REQUEST_PACKET srp;
+ LPBYTE buffer = (LPBYTE)InfoStruct->ServerInfo100;
+ BOOLEAN announcementInformationChanged = FALSE;
+
+ ServerName;
+
+ //
+ // Check that user input buffer is not NULL
+ //
+ if (buffer == NULL) {
+ if ( ARGUMENT_PRESENT( ErrorParameter ) ) {
+ *ErrorParameter = PARM_ERROR_UNKNOWN;
+ }
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ parmnum = (LONG)(Level - PARMNUM_BASE_INFOLEVEL);
+
+ if ( ARGUMENT_PRESENT( ErrorParameter ) ) {
+ *ErrorParameter = parmnum;
+ }
+
+ //
+ // Make sure that the caller is allowed to set information in the
+ // server.
+ //
+
+ error = SsCheckAccess(
+ &SsConfigInfoSecurityObject,
+ SRVSVC_CONFIG_INFO_SET
+ );
+
+ if ( error != NO_ERROR ) {
+ return ERROR_ACCESS_DENIED;
+ }
+
+ //
+ // Acquire the resource that protects server information. Since
+ // we're going to be writing to the information, we need exclusive
+ // access to the reqource.
+ //
+
+
+ //
+ // If a parameter number was specified, set that one field.
+ //
+
+ if ( parmnum >= 0 ) {
+
+ //
+ // Walk through the field descriptors looking for an
+ // equivalent parameter number.
+ //
+
+ for ( i = 0; SsServerInfoFields[i].FieldName != NULL; i++ ) {
+
+ if ( (ULONG)parmnum == SsServerInfoFields[i].ParameterNumber ) {
+
+ //
+ // Verify that the field is settable.
+ //
+ // !!! We should also reject levels above 502?
+ //
+
+ if ( SsServerInfoFields[i].Settable != ALWAYS_SETTABLE ) {
+ return ERROR_INVALID_LEVEL;
+ }
+
+ (VOID)RtlAcquireResourceExclusive( &SsServerInfoResource, TRUE );
+
+ //
+ // Set the field.
+ //
+
+ error = SsSetField(
+ &SsServerInfoFields[i],
+ buffer,
+ TRUE,
+ &announcementInformationChanged
+ );
+
+ RtlReleaseResource( &SsServerInfoResource );
+
+ //
+ // If a relevant parameter changed, call
+ // SsSetExportedServerType. This will cause an
+ // announcement to be sent.
+ //
+
+ if ( announcementInformationChanged ) {
+ SsSetExportedServerType( NULL, TRUE, TRUE );
+ }
+
+ return error;
+ }
+ }
+
+ //
+ // If a match had been found we would have returned by now.
+ // Indicate that the parameter number was illegal.
+ //
+
+ return ERROR_INVALID_LEVEL;
+ }
+
+ //
+ // A full input structure was specified. Walk through all the
+ // server data field descriptors, looking for fields that should be
+ // set.
+ //
+
+ for ( i = 0; SsServerInfoFields[i].FieldName != NULL; i++ ) {
+
+ ULONG fieldLevel;
+
+ //
+ // We need to set this field if:
+ //
+ // o the level specified on input is the same order as the
+ // level of the field. They have the same order if
+ // they are in the same century (e.g. 101 and 102 are
+ // in the same order); AND
+ //
+ // o the specified level is greater than or equal to the
+ // level of the field. For example, if the input
+ // level is 101 and the field level is 102, don't set
+ // the field. If the input level is 102 and the field
+ // level is 101, set it; AND
+ //
+ // o the field is settable. If the field is not settable
+ // by NetServerSetInfo, just ignore the value in the
+ // input structure.
+ //
+ // Note that level 598 doesn't follow the first rule above. It
+ // is NOT a superset of 50x, and it is NOT a subset of 599.
+ //
+
+ fieldLevel = SsServerInfoFields[i].Level;
+
+ if ( Level / 100 == fieldLevel / 100 &&
+ ((fieldLevel != 598) && (Level >= fieldLevel) ||
+ (fieldLevel == 598) && (Level == 598)) &&
+ SsServerInfoFields[i].Settable == ALWAYS_SETTABLE ) {
+
+ //
+ // We found a match, so the specified level number must have
+ // been valid.
+ //
+ // !!! Reject levels above 502?
+
+ validLevel = TRUE;
+
+ //
+ // Set this field.
+ //
+
+ (VOID)RtlAcquireResourceExclusive( &SsServerInfoResource, TRUE );
+
+ error = SsSetField(
+ &SsServerInfoFields[i],
+ buffer + SsServerInfoFields[i].FieldOffset,
+ TRUE,
+ &announcementInformationChanged
+ );
+
+ RtlReleaseResource( &SsServerInfoResource );
+
+ if ( error != NO_ERROR ) {
+
+ //
+ // Set the parameter in error if we need to.
+ //
+
+ if ( ARGUMENT_PRESENT(ErrorParameter) ) {
+ *ErrorParameter = SsServerInfoFields[i].ParameterNumber;
+ }
+
+ return error;
+ }
+
+ }
+
+ }
+
+ //
+ // If no match was ever found, then an invalid level was passed in.
+ //
+
+ if ( !validLevel ) {
+ return ERROR_INVALID_LEVEL;
+ }
+
+ //
+ // Get an SRP and set it up with the appropriate level.
+ //
+
+ srp = SsAllocateSrp( );
+ if ( srp == NULL ) {
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+ srp->Level = 0xFFFFFFFF;
+
+ (VOID)RtlAcquireResourceShared( &SsServerInfoResource, TRUE );
+
+ //
+ // Send the request on to the server.
+ //
+
+ error = SsServerFsControl(
+ NULL,
+ FSCTL_SRV_NET_SERVER_SET_INFO,
+ srp,
+ &SsData.ServerInfo102,
+ sizeof(SERVER_INFO_102) + sizeof(SERVER_INFO_599) +
+ sizeof(SERVER_INFO_598)
+ );
+
+ //
+ // Release the resource and free the SRP.
+ //
+
+ RtlReleaseResource( &SsServerInfoResource );
+
+ SsFreeSrp( srp );
+
+ //
+ // If a relevant parameter changed, call SsSetExportedServerType.
+ // This will cause an announcement to be sent.
+ //
+
+ if ( announcementInformationChanged ) {
+ SsSetExportedServerType( NULL, TRUE, TRUE );
+ }
+
+ return error;
+
+} // NetrServerSetInfo
diff --git a/private/net/svcdlls/srvsvc/server/srvmain.c b/private/net/svcdlls/srvsvc/server/srvmain.c
new file mode 100644
index 000000000..5101001db
--- /dev/null
+++ b/private/net/svcdlls/srvsvc/server/srvmain.c
@@ -0,0 +1,680 @@
+/*++
+
+Copyright (c) 1990-91 Microsoft Corporation
+
+Module Name:
+
+ srvmain.c
+
+Abstract:
+
+ This is the main routine for the NT LAN Manager Server Service.
+
+ !!! Does service controller guarantee no controls will be issued
+ while we are initializing? Also, does it serialize controls?
+ If not, we need some synchronization in here.
+
+Author:
+
+ David Treadwell (davidtr) 05-10-1991
+
+Revision History:
+
+ 19-Jan-1993 Danl
+ Removed the old long endpoint name "LanmanServer".
+
+ 07-Jan-1993 Danl
+ Added an RPC endpoint name using "srvsvc", since "LanmanServer" is
+ too long for DOS machines to _access.
+ For a short time we will support both names.
+
+ 18-Feb-1992 ritaw
+ Convert to Win32 service control APIs.
+
+--*/
+
+#include "srvsvcp.h"
+#include "ssdata.h"
+
+#include <lmerr.h>
+#include <lmsname.h>
+#include <tstr.h>
+#include <wincon.h>
+#include <winsvc.h>
+
+#include <netlib.h>
+#include <netlibnt.h> // NetpNtStatusToApiStatus
+#include <netdebug.h> // NetpKdPrint
+#include <rpcutil.h>
+#include <services.h>
+#include <srvann.h>
+#include <srvnames.h> // SERVER_INTERFACE_NAME
+
+
+SERVICE_STATUS SsServiceStatus;
+SERVICE_STATUS_HANDLE SsServiceStatusHandle;
+
+VOID
+ControlResponse(
+ DWORD opCode
+ );
+
+
+VOID
+LMSVCS_ENTRY_POINT ( // (SRVSVC_main)
+ IN DWORD argc,
+ IN LPWSTR argv[],
+ IN PLMSVCS_GLOBAL_DATA pGlobalData,
+ IN HANDLE SvcRefHandle
+ )
+
+/*++
+
+Routine Description:
+
+ This is the "main" routine for the server service. The containing
+ process will call this routine when we're supposed to start up.
+
+Arguments:
+
+Return Value:
+
+ None.
+
+--*/
+{
+ RPC_STATUS rpcStatus;
+ NET_API_STATUS error;
+ NET_API_STATUS terminationError;
+ BOOLEAN rpcServerStarted = FALSE;
+
+ NTSTATUS Status;
+ HANDLE EventHandle;
+ OBJECT_ATTRIBUTES EventAttributes;
+ UNICODE_STRING EventNameString;
+ LARGE_INTEGER LocalTimeout;
+
+ UNREFERENCED_PARAMETER(SvcRefHandle);
+
+ //
+ // Save the LMSVCS global data for future use.
+ //
+
+ SsLmsvcsGlobalData = pGlobalData;
+
+ //
+ // Skip the Service Name in the argument list.
+ //
+ if (argc > 0) {
+ argc--;
+ if (argc > 0) {
+ argv = &(argv[1]);
+ }
+ }
+
+
+#if DBG
+ //
+ // Set up for debugging--the first command line argument may be
+ // "/debug:X" where SsDebug gets set to X.
+ //
+
+ if ( argc > 0 && STRNICMP( TEXT("/debug:"), (LPWSTR)argv[0], 7 ) == 0 ) {
+#ifdef UNICODE
+ UNICODE_STRING ustr;
+ RtlInitUnicodeString( &ustr, (PWSTR)argv[0] + 7 );
+ RtlUnicodeStringToInteger( &ustr, 16, &SsDebug );
+#else
+ SsDebug = 0;
+ RtlCharToInteger( argv[0] + 7, 16, &SsDebug );
+#endif
+ }
+
+
+#ifndef USE_DEBUGGER
+ //SsDebug = 0xffff;
+ if ( SsDebug != 0 ) {
+ CONSOLE_SCREEN_BUFFER_INFO csbi;
+ COORD coord;
+ (VOID)AllocConsole( );
+ (VOID)GetConsoleScreenBufferInfo(
+ GetStdHandle(STD_OUTPUT_HANDLE),
+ &csbi
+ );
+ coord.X = (SHORT)(csbi.srWindow.Right - csbi.srWindow.Left + 1);
+ coord.Y = (SHORT)((csbi.srWindow.Bottom - csbi.srWindow.Top + 1) * 20);
+ (VOID)SetConsoleScreenBufferSize(
+ GetStdHandle(STD_OUTPUT_HANDLE),
+ coord
+ );
+ }
+#endif
+#endif
+
+ IF_DEBUG(INITIALIZATION) {
+ SS_PRINT(( "SRVSVC_main: server service starting.\n" ));
+ }
+
+ IF_DEBUG(INITIALIZATION_BREAKPOINT) {
+ DbgUserBreakPoint( );
+ }
+
+
+ //
+ // Initialize all the status fields so that subsequent calls to
+ // SetServiceStatus need to only update fields that changed.
+ //
+
+ SsServiceStatus.dwServiceType = SERVICE_WIN32;
+ SsServiceStatus.dwCurrentState = SERVICE_START_PENDING;
+ SsServiceStatus.dwControlsAccepted = 0;
+ SsServiceStatus.dwCheckPoint = 1;
+ SsServiceStatus.dwWaitHint = 30000; // 30 seconds
+
+ SET_SERVICE_EXITCODE(
+ NO_ERROR,
+ SsServiceStatus.dwWin32ExitCode,
+ SsServiceStatus.dwServiceSpecificExitCode
+ );
+
+ //
+ // Initialize server to receive service requests by registering the
+ // control handler.
+ //
+
+ SsServiceStatusHandle = RegisterServiceCtrlHandler(
+ SERVICE_SERVER,
+ ControlResponse
+ );
+
+ if ( SsServiceStatusHandle == 0 ) {
+
+ error = GetLastError();
+
+ IF_DEBUG(INITIALIZATION_ERRORS) {
+ SS_PRINT(( "SRVSVC_main: RegisterServiceCtrlHandler failed: "
+ "%ld\n", error ));
+ }
+ goto exit;
+
+ }
+
+ IF_DEBUG(INITIALIZATION) {
+ SS_PRINT(( "SRVSVC_main: Control handler registered.\n" ));
+ }
+
+
+ //
+ // Wait for the Sam service to start.
+ //
+ // Later, when we initialize the server driver, it is going to create a
+ // "NULL Session" token by calling LsaLogonUser. That call waits until
+ // SAM is initialized. However, we don't have an opportunity to give
+ // wait hints to the service controller, so we'll wait here.
+ //
+ // Create the event to wait on.
+ //
+
+ RtlInitUnicodeString( &EventNameString, L"\\SAM_SERVICE_STARTED" );
+ InitializeObjectAttributes( &EventAttributes, &EventNameString, 0, 0, NULL);
+
+ Status = NtCreateEvent(
+ &EventHandle,
+ SYNCHRONIZE,
+ &EventAttributes,
+ NotificationEvent,
+ (BOOLEAN) FALSE // The event is initially not signaled
+ );
+
+ if ( !NT_SUCCESS(Status)) {
+
+ //
+ // If the event already exists, SAM beat us to creating it.
+ // Just open it.
+ //
+
+ if( Status == STATUS_OBJECT_NAME_EXISTS ||
+ Status == STATUS_OBJECT_NAME_COLLISION ) {
+
+ Status = NtOpenEvent( &EventHandle,
+ SYNCHRONIZE,
+ &EventAttributes );
+
+ }
+ if ( !NT_SUCCESS(Status)) {
+ error = NetpNtStatusToApiStatus(Status);
+
+ IF_DEBUG(INITIALIZATION_ERRORS) {
+ SS_PRINT(( "SRVSVC_main: Can't open SAM_SERVICE_STARTED event: %lx\n",
+ Status ));
+ }
+
+ goto exit;
+ }
+ }
+
+ //
+ // Wait for SAM to finish initializing.
+ //
+
+ LocalTimeout = RtlEnlargedIntegerMultiply( SsServiceStatus.dwWaitHint/2, -10000 );
+
+ do {
+
+ IF_DEBUG(INITIALIZATION) {
+ SS_PRINT(( "SRVSVC_main: Wait for SAM to init.\n" ));
+ }
+ AnnounceServiceStatus( 1 );
+ Status = NtWaitForSingleObject( EventHandle,
+ (BOOLEAN)FALSE,
+ &LocalTimeout);
+ } while ( Status == STATUS_TIMEOUT );
+
+ (VOID) NtClose( EventHandle );
+
+ if ( !NT_SUCCESS(Status) ) {
+ error = NetpNtStatusToApiStatus(Status);
+
+ IF_DEBUG(INITIALIZATION_ERRORS) {
+ SS_PRINT(( "SRVSVC_main: Wait for SAM_SERVICE_STARTED event failed: %lx\n",
+ Status ));
+ }
+
+ goto exit;
+ }
+
+ IF_DEBUG(INITIALIZATION) {
+ SS_PRINT(( "SRVSVC_main: Done waiting for SAM to init.\n" ));
+ }
+
+ AnnounceServiceStatus( 1 );
+
+ //
+ // Initialize server service data and the Lanman server FSP in kernel
+ // mode.
+ //
+
+ error = SsInitialize( argc, argv );
+
+ if ( error != NO_ERROR ) {
+ goto exit;
+ }
+
+ //
+ // Set the variable that indicates that the server is fully
+ // initialized.
+ //
+
+ SS_ASSERT( !SsInitialized );
+ SsInitialized = TRUE;
+
+ //
+ // Start the RPC server. Because other services may reside in this
+ // process, the actual RPC server may already have been started;
+ // this routine will track this for us.
+ //
+ // NOTE: Now all RPC servers in services.exe share the same pipe name.
+ // However, in order to support communication with version 1.0 of WinNt,
+ // it is necessary for the Client Pipe name to remain the same as
+ // it was in version 1.0. Mapping to the new name is performed in
+ // the Named Pipe File System code.
+ //
+
+ rpcStatus = SsLmsvcsGlobalData->StartRpcServer(
+ SsLmsvcsGlobalData->SvcsRpcPipeName,
+ srvsvc_ServerIfHandle
+ );
+
+ if ( rpcStatus != 0 ) {
+ IF_DEBUG(INITIALIZATION_ERRORS) {
+ SS_PRINT(( "SRVSVC_main: NetpStartRpcServer failed: %ld\n",
+ rpcStatus ));
+ }
+ goto exit;
+ }
+
+ IF_DEBUG(INITIALIZATION) {
+ SS_PRINT(( "SRVSVC_main: RPC server started.\n" ));
+ }
+
+ rpcServerStarted = TRUE;
+
+#ifdef SRV_PNP_POWER
+ //
+ // Start getting PNP transport notifications from the server
+ //
+ error = StartPnpNotifications();
+ if( error != NO_ERROR ) {
+ goto exit;
+ }
+
+#endif
+
+ //
+ // Announce that we have successfully started.
+ //
+
+ SsServiceStatus.dwCurrentState = SERVICE_RUNNING;
+ SsServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP |
+ SERVICE_ACCEPT_PAUSE_CONTINUE;
+ SsServiceStatus.dwCheckPoint = 0;
+ SsServiceStatus.dwWaitHint = 0;
+
+ AnnounceServiceStatus( 0 );
+
+ IF_DEBUG(INITIALIZATION) {
+ SS_PRINT(( "SRVSVC_main: initialization successfully completed.\n" ));
+ }
+
+ if (!I_ScSetServiceBits(SsServiceStatusHandle, SV_TYPE_SERVER, TRUE, TRUE, FALSE)) {
+ error = GetLastError();
+
+ IF_DEBUG(INITIALIZATION_ERRORS) {
+ SS_PRINT(( "SRVSVC_main: I_ScSetServiceBits failed: %ld\n",
+ error ));
+ }
+ goto exit;
+
+ }
+
+ //
+ // Use this thread as the scavenger thread to send server
+ // announcements and watch the registry for configuration changes.
+ //
+
+ SS_ASSERT( SsInitialized );
+ (VOID)SsScavengerThread( NULL );
+ SS_ASSERT( SsInitialized );
+
+exit:
+
+ IF_DEBUG(TERMINATION) {
+ SS_PRINT(( "SRVSVC_main: terminating.\n" ));
+ }
+
+ IF_DEBUG(TERMINATION_BREAKPOINT) {
+ DbgUserBreakPoint( );
+ }
+
+ //
+ // Set the initialization variable to indicate that the server
+ // service is not started.
+ //
+
+ SsInitialized = FALSE;
+
+ //
+ // Announce that we're going down.
+ //
+
+ terminationError = error;
+
+ SsServiceStatus.dwCurrentState = SERVICE_STOP_PENDING;
+ SsServiceStatus.dwCheckPoint = 1;
+ SsServiceStatus.dwWaitHint = 20000; // 20 seconds
+
+ SET_SERVICE_EXITCODE(
+ terminationError,
+ SsServiceStatus.dwWin32ExitCode,
+ SsServiceStatus.dwServiceSpecificExitCode
+ );
+
+ AnnounceServiceStatus( 0 );
+
+ //
+ // Shut down our connection to the RPC server, if the RPC server
+ // was started successfully.
+ //
+
+ if ( rpcServerStarted ) {
+ rpcStatus = SsLmsvcsGlobalData->StopRpcServer (
+ srvsvc_ServerIfHandle
+ );
+ if ( rpcStatus != NO_ERROR ) {
+ IF_DEBUG(TERMINATION_ERRORS) {
+ SS_PRINT(( "SRVSVC_main: unable to terminate RPC server: %X\n",
+ rpcStatus ));
+ }
+ } else {
+ IF_DEBUG(TERMINATION) {
+ SS_PRINT(( "SRVSVC_main: RPC server successfully shut down.\n" ));
+ }
+ }
+ }
+
+ //
+ // Clean up previously initialized state.
+ //
+
+ IF_DEBUG(TERMINATION) {
+ SS_PRINT(( "SRVSVC_main: cleaning up.\n" ));
+ }
+
+ error = SsTerminate( );
+ if ( terminationError == NO_ERROR ) {
+ terminationError = error;
+ }
+
+ //
+ // Announce that we're down.
+ //
+
+ SsServiceStatus.dwCurrentState = SERVICE_STOPPED;
+ SsServiceStatus.dwControlsAccepted = 0;
+ SsServiceStatus.dwCheckPoint = 0;
+ SsServiceStatus.dwWaitHint = 0;
+
+ SET_SERVICE_EXITCODE(
+ terminationError,
+ SsServiceStatus.dwWin32ExitCode,
+ SsServiceStatus.dwServiceSpecificExitCode
+ );
+
+ AnnounceServiceStatus( 0 );
+
+ IF_DEBUG(TERMINATION) {
+ SS_PRINT(( "SRVSVC_main: the server service is terminated.\n" ));
+ }
+
+ return;
+
+} // LMSVCS_ENTRY_POINT (SRVSVC_main)
+
+
+VOID
+AnnounceServiceStatus (
+ DWORD increment
+ )
+
+/*++
+
+Routine Description:
+
+ Announces the service's status to the service controller.
+ Add 'increment' to the checkpoint value.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ //
+ // Service status handle is NULL if RegisterServiceCtrlHandler failed.
+ //
+
+ if ( SsServiceStatusHandle == 0 ) {
+ SS_PRINT(( "AnnounceServiceStatus: Cannot call SetServiceStatus, "
+ "no status handle.\n" ));
+
+ return;
+ }
+
+ if( SsServiceStatus.dwCurrentState == SERVICE_RUNNING && increment ) {
+ //
+ // No need to tell the service controller about another checkpoint
+ // since it already knows we're running
+ //
+ return;
+ }
+
+ SsServiceStatus.dwCheckPoint += increment;
+
+ IF_DEBUG(ANNOUNCE) {
+ SS_PRINT(( "AnnounceServiceStatus: CurrentState %lx\n"
+ " ControlsAccepted %lx\n"
+ " Win32ExitCode %lu\n"
+ " ServiceSpecificExitCode %lu\n"
+ " CheckPoint %lu\n"
+ " WaitHint %lu\n",
+ SsServiceStatus.dwCurrentState,
+ SsServiceStatus.dwControlsAccepted,
+ SsServiceStatus.dwWin32ExitCode,
+ SsServiceStatus.dwServiceSpecificExitCode,
+ SsServiceStatus.dwCheckPoint,
+ SsServiceStatus.dwWaitHint ));
+ }
+
+ //
+ // Call SetServiceStatus, ignoring any errors.
+ //
+
+ SetServiceStatus(SsServiceStatusHandle, &SsServiceStatus);
+
+} // AnnounceServiceStatus
+
+
+VOID
+ControlResponse(
+ DWORD opCode
+ )
+
+{
+ NET_API_STATUS error;
+ BOOL announce = TRUE;
+
+ //
+ // Determine the type of service control message and modify the
+ // service status, if necessary.
+ //
+
+ switch( opCode ) {
+
+ case SERVICE_CONTROL_STOP:
+
+ IF_DEBUG(CONTROL_MESSAGES) {
+ SS_PRINT(( "ControlResponse: STOP control received.\n" ));
+ }
+
+ //
+ // Announce that we are in the process of stopping.
+ //
+
+ SsServiceStatus.dwCurrentState = SERVICE_STOP_PENDING;
+ AnnounceServiceStatus( 0 );
+
+ //
+ // Set the event that will wake up the scavenger thread.
+ // That thread will wake up and kill the server.
+ //
+
+ if ( !SetEvent( SsTerminationEvent ) ) {
+ IF_DEBUG(TERMINATION_ERRORS) {
+ SS_PRINT(( "ControlResponse: SetEvent failed: %ld\n",
+ GetLastError( ) ));
+ }
+ }
+
+ //
+ // Let the main thread announce when the stop is done.
+ //
+
+ announce = FALSE;
+
+ break;
+
+ case SERVICE_CONTROL_PAUSE:
+
+ IF_DEBUG(CONTROL_MESSAGES) {
+ SS_PRINT(( "ControlResponse: PAUSE control received.\n" ));
+ }
+
+ //
+ // Announce that we are in the process of pausing.
+ //
+
+ SsServiceStatus.dwCurrentState = SERVICE_PAUSE_PENDING;
+ AnnounceServiceStatus( 0 );
+
+ //
+ // Send the request on to the server.
+ //
+
+ error = SsServerFsControl( NULL, FSCTL_SRV_PAUSE, NULL, NULL, 0L );
+ SS_ASSERT( error == NO_ERROR );
+
+ //
+ // Announce that we're now paused.
+ //
+
+ SsServiceStatus.dwCurrentState = SERVICE_PAUSED;
+
+ break;
+
+ case SERVICE_CONTROL_CONTINUE:
+
+ IF_DEBUG(CONTROL_MESSAGES) {
+ SS_PRINT(( "ControlResponse: CONTINUE control received.\n" ));
+ }
+
+ //
+ // Announce that continue is pending.
+ //
+
+ SsServiceStatus.dwCurrentState = SERVICE_CONTINUE_PENDING;
+ AnnounceServiceStatus( 0 );
+
+ //
+ // Send the request on to the server.
+ //
+
+ error = SsServerFsControl( NULL, FSCTL_SRV_CONTINUE, NULL, NULL, 0L );
+ SS_ASSERT( error == NO_ERROR );
+
+ //
+ // Announce that we're active now.
+ //
+
+ SsServiceStatus.dwCurrentState = SERVICE_RUNNING;
+
+ break;
+
+ case SERVICE_CONTROL_INTERROGATE:
+
+ IF_DEBUG(CONTROL_MESSAGES) {
+ SS_PRINT(( "ControlResponse: INTERROGATE control received.\n" ));
+ }
+
+
+ break;
+
+ default:
+
+ IF_DEBUG(CONTROL_MESSAGES) {
+ SS_PRINT(( "ControlResponse: unknown code received.\n" ));
+ }
+
+ break;
+ }
+
+ if ( announce ) {
+ AnnounceServiceStatus( 0 );
+ }
+
+} // ControlResponse
+
diff --git a/private/net/svcdlls/srvsvc/server/srvsvc.def b/private/net/svcdlls/srvsvc/server/srvsvc.def
new file mode 100644
index 000000000..9f7b307f3
--- /dev/null
+++ b/private/net/svcdlls/srvsvc/server/srvsvc.def
@@ -0,0 +1,6 @@
+NAME srvsvc.dll
+
+DESCRIPTION 'NT LAN Manager Server Service'
+
+EXPORTS
+ ServiceEntry
diff --git a/private/net/svcdlls/srvsvc/server/srvsvc.rc b/private/net/svcdlls/srvsvc/server/srvsvc.rc
new file mode 100644
index 000000000..d68f43a40
--- /dev/null
+++ b/private/net/svcdlls/srvsvc/server/srvsvc.rc
@@ -0,0 +1,12 @@
+#include <windows.h>
+
+#include <ntverp.h>
+
+#define VER_FILETYPE VFT_DLL
+#define VER_FILESUBTYPE VFT2_UNKNOWN
+#define VER_FILEDESCRIPTION_STR "Server Service DLL"
+#define VER_INTERNALNAME_STR "SRVSVC.DLL"
+#define VER_ORIGINALFILENAME_STR "SRVSVC.DLL"
+
+#include "common.ver"
+
diff --git a/private/net/svcdlls/srvsvc/server/srvsvcp.h b/private/net/svcdlls/srvsvc/server/srvsvcp.h
new file mode 100644
index 000000000..c8324657e
--- /dev/null
+++ b/private/net/svcdlls/srvsvc/server/srvsvcp.h
@@ -0,0 +1,406 @@
+/*++
+
+Copyright (c) 1991-1992 Microsoft Corporation
+
+Module Name:
+
+ SrvSvcP.h
+
+Abstract:
+
+ This is the header file for the NT server service.
+
+Author:
+
+ David Treadwell (davidtr) 10-Jan-1991
+
+Revision History:
+
+--*/
+
+#ifndef _SRVSVCP_
+#define _SRVSVCP_
+
+#if _PNP_POWER
+#define SRV_PNP_POWER 1
+#endif
+
+#include <nt.h>
+#include <ntrtl.h>
+
+#include <rpc.h>
+#include <windef.h>
+#include <winerror.h>
+
+#include <lmcons.h>
+#include <secobj.h>
+
+#include <srvfsctl.h>
+
+#include <srvsvc.h>
+
+#include "ssdebug.h"
+#include "sssec.h"
+
+//
+// String constants.
+//
+
+#define IPC_SHARE_NAME TEXT("IPC$")
+#define ADMIN_SHARE_NAME TEXT("ADMIN$")
+
+#define SRVSVC_MAX_NUMBER_OF_DISKS 26
+
+//
+// Internationalizable strings
+//
+extern LPWSTR SsAdminShareRemark ;
+extern LPWSTR SsIPCShareRemark ;
+extern LPWSTR SsDiskAdminShareRemark ;
+
+//
+// Bits of server type (in announcement messages) that can only be set
+// by the server itself -- not by services via the internal API
+// I_NetServerSetServiceBits.
+//
+
+#define SERVER_TYPE_INTERNAL_BITS (SV_TYPE_SERVER | \
+ SV_TYPE_TIME_SOURCE | \
+ SV_TYPE_PRINTQ_SERVER | \
+ SV_TYPE_NT | \
+ SV_TYPE_DFS)
+
+//
+// INITIAL_BUFFER_SIZE is the buffer size that GetInfo and Enum requests
+// first try to fill. If this buffer isn't large enough, they allocate
+// a buffer large enough to hold all the information plus a fudge factor,
+// EXTRA_ALLOCATION.
+//
+
+#define INITIAL_BUFFER_SIZE (ULONG)8192
+#define EXTRA_ALLOCATION 1024
+
+//
+// ServerProductName in SERVER_SERVICE_DATA is the name passed to the
+// Licensing DLL as the name of this service. MAXPRODNAME is the max
+// number of characters in the service name.
+
+#define SERVER_PRODUCT_NAME L"SMBServer"
+
+// szVersionNumber in SERVER_SERVICE_DATA is the version string passed
+// to the Licensing DLL as the vesion of this service. MAXVERSIONSZ
+// is the max number of characters for the version string
+
+#define MAXVERSIONSZ 10
+
+//
+// Structure for server service global data.
+//
+
+typedef struct _SERVER_SERVICE_DATA {
+ SERVER_INFO_102 ServerInfo102;
+ SERVER_INFO_599 ServerInfo599;
+ SERVER_INFO_598 ServerInfo598;
+
+#if SRV_PNP_POWER
+ //
+ // If we are asked to set some service bits before we've bound to
+ // any transports, we need to save those bits here and use them later
+ // when we finally do bind to transports.
+ //
+ DWORD ServiceBits;
+#endif
+
+ BOOLEAN IsDfsRoot; // TRUE if we are the root of a DFS tree
+ UNICODE_STRING ServerAnnounceName;
+ LONG NumberOfPrintShares;
+ WCHAR ServerNameBuffer[MAX_PATH];
+ WCHAR AnnounceNameBuffer[MAX_PATH];
+ WCHAR ServerCommentBuffer[MAXCOMMENTSZ+1];
+ WCHAR UserPathBuffer[MAX_PATH+1];
+ WCHAR DomainNameBuffer[MAX_PATH];
+ WCHAR LongDomainNameBuffer[MAX_PATH];
+ WCHAR ServerProductName[ sizeof( SERVER_PRODUCT_NAME ) ];
+ WCHAR szVersionNumber[ MAXVERSIONSZ+1 ];
+} SERVER_SERVICE_DATA, *PSERVER_SERVICE_DATA;
+
+//
+// Structures used to hold transport specific server type bits
+//
+typedef struct _TRANSPORT_LIST_ENTRY {
+ struct _TRANSPORT_LIST_ENTRY *Next;
+ LPWSTR TransportName; // device name for xport
+ DWORD ServiceBits; // SV... announce bits
+} TRANSPORT_LIST_ENTRY, *PTRANSPORT_LIST_ENTRY;
+
+typedef struct _NAME_LIST_ENTRY {
+ struct _NAME_LIST_ENTRY *Next;
+ CHAR TransportAddress[ MAX_PATH ]; // address of this server
+ ULONG TransportAddressLength;
+ LPWSTR DomainName; // name of the domain
+ DWORD ServiceBits; // SV... announce bits
+ struct {
+ ULONG PrimaryName: 1; // Is this the server's primary name?
+ };
+ PTRANSPORT_LIST_ENTRY Transports;
+} NAME_LIST_ENTRY, *PNAME_LIST_ENTRY;
+
+
+//
+// Structure type used for generalized switch matching.
+//
+
+typedef struct _FIELD_DESCRIPTOR {
+ LPWCH FieldName;
+ ULONG FieldType;
+ ULONG FieldOffset;
+ ULONG Level;
+ DWORD ParameterNumber;
+ ULONG Settable;
+ DWORD DefaultValue;
+ DWORD MinimumValue;
+ DWORD MaximumValue;
+} FIELD_DESCRIPTOR, *PFIELD_DESCRIPTOR;
+
+//
+// Used by NetrShareEnumSticky to get share information from the registry.
+//
+
+typedef struct _SRVSVC_SHARE_ENUM_INFO {
+ ULONG Level;
+ ULONG ResumeHandle;
+ ULONG EntriesRead;
+ ULONG TotalEntries;
+ ULONG TotalBytesNeeded;
+ PVOID OutputBuffer;
+ ULONG OutputBufferLength;
+
+ //
+ // Scratch fields used by SsEnumerateStickyShares
+ //
+
+ ULONG ShareEnumIndex;
+ PCHAR StartOfFixedData;
+ PCHAR EndOfVariableData;
+} SRVSVC_SHARE_ENUM_INFO, *PSRVSVC_SHARE_ENUM_INFO;
+
+//#include "ssdata.h"
+
+//
+// Macros.
+//
+
+#define POINTER_TO_OFFSET(val,start) \
+ (val) = (val) == NULL ? NULL : (PVOID)( (PCHAR)(val) - (ULONG)(start) )
+
+#define OFFSET_TO_POINTER(val,start) \
+ (val) = (val) == NULL ? NULL : (PVOID)( (PCHAR)(val) + (ULONG)(start) )
+
+#define FIXED_SIZE_OF_SHARE(level) \
+ ( (level) == 0 ? sizeof(SHARE_INFO_0) : \
+ (level) == 1 ? sizeof(SHARE_INFO_1) : \
+ (level) == 2 ? sizeof(SHARE_INFO_2) : \
+ sizeof(SHARE_INFO_502) )
+
+#define SIZE_WSTR( Str ) \
+ ( ( Str ) == NULL ? 0 : ((wcslen( Str ) + 1) * sizeof(WCHAR)) )
+
+//
+// Internal routine prototypes.
+//
+
+PSERVER_REQUEST_PACKET
+SsAllocateSrp (
+ VOID
+ );
+
+NET_API_STATUS
+SsCheckAccess (
+ IN PSRVSVC_SECURITY_OBJECT SecurityObject,
+ IN ACCESS_MASK DesiredAccess
+ );
+
+VOID
+SsCloseServer (
+ VOID
+ );
+
+VOID
+SsControlCHandler (
+ IN ULONG CtrlType
+ );
+
+NET_API_STATUS
+SsCreateSecurityObjects (
+ VOID
+ );
+
+VOID
+SsDeleteSecurityObjects (
+ VOID
+ );
+
+VOID
+SsFreeSrp (
+ IN PSERVER_REQUEST_PACKET Srp
+ );
+
+NET_API_STATUS
+SsInitialize (
+ IN DWORD argc,
+ IN LPTSTR argv[]
+ );
+
+VOID
+SsLogEvent(
+ IN DWORD MessageId,
+ IN DWORD NumberOfSubStrings,
+ IN LPWSTR *SubStrings,
+ IN DWORD ErrorCode
+ );
+
+NET_API_STATUS
+SsOpenServer (
+ PHANDLE handle OPTIONAL
+ );
+
+NET_API_STATUS
+SsParseCommandLine (
+ IN DWORD argc,
+ IN LPTSTR argv[],
+ IN BOOLEAN Starting
+ );
+
+DWORD
+SsScavengerThread (
+ IN LPVOID lpThreadParameter
+ );
+
+NET_API_STATUS
+SsServerFsControlGetInfo (
+ IN ULONG ServerControlCode,
+ IN PSERVER_REQUEST_PACKET Srp,
+ IN OUT PVOID *OutputBuffer,
+ IN OUT ULONG OutputBufferLength
+ );
+
+NET_API_STATUS
+SsServerFsControl (
+ IN HANDLE SrvHandle OPTIONAL,
+ IN ULONG ServerControlCode,
+ IN PSERVER_REQUEST_PACKET Srp,
+ IN PVOID Buffer OPTIONAL,
+ IN ULONG BufferLength
+ );
+
+DWORD
+SsGetServerType (
+ VOID
+ );
+
+VOID
+SsSetExportedServerType (
+ IN PNAME_LIST_ENTRY Service OPTIONAL,
+ IN BOOL ExternalBitsAlreadyChanged,
+ IN BOOL UpdateImmediately
+ );
+
+NET_API_STATUS
+SsSetField (
+ IN PFIELD_DESCRIPTOR Field,
+ IN PVOID Value,
+ IN BOOLEAN WriteToRegistry,
+ OUT BOOLEAN *AnnouncementInformationChanged OPTIONAL
+ );
+
+UINT
+SsGetDriveType (
+ IN LPWSTR path
+ );
+
+NET_API_STATUS
+SsTerminate (
+ VOID
+ );
+
+DWORD
+SsAtol (
+ IN LPTSTR Input
+ );
+
+VOID
+AnnounceServiceStatus (
+ DWORD increment
+ );
+
+NTSTATUS
+BindToTransport (
+ IN PWSTR ValueName,
+ IN ULONG ValueType,
+ IN PVOID ValueData,
+ IN ULONG ValueLength,
+ IN PVOID Context,
+ IN PVOID EntryContext
+ );
+
+NTSTATUS
+BindOptionalNames (
+ IN PWSTR ValueName,
+ IN ULONG ValueType,
+ IN PVOID ValueData,
+ IN ULONG ValueLength,
+ IN PVOID Context,
+ IN PVOID EntryContext
+ );
+
+NET_API_STATUS NET_API_FUNCTION
+I_NetrServerTransportAddEx (
+ IN DWORD Level,
+ IN LPTRANSPORT_INFO Buffer
+ );
+
+#if SRV_PNP_POWER
+
+NET_API_STATUS
+StartPnpNotifications (
+ VOID
+ );
+
+#endif
+
+//
+// XACTSRV functions.
+//
+
+DWORD
+XsStartXactsrv (
+ VOID
+ );
+
+VOID
+XsStopXactsrv (
+ VOID
+ );
+
+NET_API_STATUS
+ShareEnumCommon (
+ IN DWORD Level,
+ OUT LPBYTE *Buffer,
+ IN DWORD PreferredMaximumLength,
+ OUT LPDWORD EntriesRead,
+ OUT LPDWORD TotalEntries,
+ IN OUT LPDWORD ResumeHandle OPTIONAL,
+ IN LPWSTR NetName OPTIONAL
+ );
+
+NET_API_STATUS
+ConvertStringToTransportAddress (
+ IN PUNICODE_STRING InputName,
+ OUT CHAR TransportAddress[MAX_PATH],
+ OUT PULONG TransportAddressLength
+ );
+
+VOID
+SsSetDfsRoot();
+
+#endif // ndef _SRVSVCP_
diff --git a/private/net/svcdlls/srvsvc/server/ssdata.c b/private/net/svcdlls/srvsvc/server/ssdata.c
new file mode 100644
index 000000000..326eaf58e
--- /dev/null
+++ b/private/net/svcdlls/srvsvc/server/ssdata.c
@@ -0,0 +1,229 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ SsData.c
+
+Abstract:
+
+ This module contains declarations for global data used by the server
+ service.
+
+Author:
+
+ David Treadwell (davidtr) 7-Mar-1991
+
+Revision History:
+
+--*/
+
+#include "srvsvcp.h"
+#include "srvconfg.h"
+#include "ssdata.h"
+
+//
+// Handle for accessing the server.
+//
+
+HANDLE SsServerDeviceHandle = NULL;
+
+//
+// Global server service data.
+//
+
+SERVER_SERVICE_DATA SsData;
+
+//
+// Pointer to global data made available by LMSVCS main image.
+//
+
+PLMSVCS_GLOBAL_DATA SsLmsvcsGlobalData;
+
+//
+// Macros for simplifying field generation.
+//
+
+#define MAKE_BOOL_FIELD(_name_,_lower_,_upper_,_level_,_set_) \
+ L#_name_, BOOLEAN_FIELD, FIELD_OFFSET( SERVER_INFO_ ## _level_, \
+ sv ## _level_ ## _ ## _lower_ ), _level_, SV_ ## _upper_ ## _PARMNUM, \
+ _set_, DEF_ ## _upper_, FALSE, TRUE
+#define MAKE_DWORD_FIELD(_name_,_lower_,_upper_,_level_,_set_) \
+ L#_name_, DWORD_FIELD, FIELD_OFFSET( SERVER_INFO_ ## _level_, \
+ sv ## _level_ ## _ ## _lower_ ), _level_, SV_ ## _upper_ ## _PARMNUM, \
+ _set_, DEF_ ## _upper_, MIN_ ## _upper_, MAX_ ## _upper_
+#define MAKE_LPSTR_FIELD(_name_,_lower_,_upper_,_level_,_set_) \
+ L#_name_, LPSTR_FIELD, FIELD_OFFSET( SERVER_INFO_ ## _level_, \
+ sv ## _level_ ## _ ## _lower_ ), _level_, SV_ ## _upper_ ## _PARMNUM, \
+ _set_, (DWORD)DEF_ ## _upper_, 0, 0
+
+//
+// Data for all server info fields.
+//
+
+FIELD_DESCRIPTOR SsServerInfoFields[] = {
+ MAKE_DWORD_FIELD( platform_id, platform_id, PLATFORM_ID, 100, NOT_SETTABLE ),
+ MAKE_LPSTR_FIELD( name, name, NAME, 100, NOT_SETTABLE ),
+ MAKE_DWORD_FIELD( version_major, version_major, VERSION_MAJOR, 101, NOT_SETTABLE ),
+ MAKE_DWORD_FIELD( version_minor, version_minor, VERSION_MINOR, 101, NOT_SETTABLE ),
+ MAKE_DWORD_FIELD( type, type, TYPE, 101, NOT_SETTABLE ),
+ MAKE_LPSTR_FIELD( comment, comment, COMMENT, 101, ALWAYS_SETTABLE ),
+ MAKE_LPSTR_FIELD( srvcomment, comment, COMMENT, 101, ALWAYS_SETTABLE ),
+ MAKE_DWORD_FIELD( users, users, USERS, 102, ALWAYS_SETTABLE ),
+ MAKE_DWORD_FIELD( disc, disc, DISC, 102, ALWAYS_SETTABLE ),
+ MAKE_DWORD_FIELD( autodisconnect, disc, DISC, 102, ALWAYS_SETTABLE ),
+ MAKE_BOOL_FIELD( hidden, hidden, HIDDEN, 102, ALWAYS_SETTABLE ),
+ MAKE_DWORD_FIELD( announce, announce, ANNOUNCE, 102, ALWAYS_SETTABLE ),
+ MAKE_DWORD_FIELD( anndelta, anndelta, ANNDELTA, 102, ALWAYS_SETTABLE ),
+ MAKE_LPSTR_FIELD( userpath, userpath, USERPATH, 102, ALWAYS_SETTABLE ),
+ MAKE_DWORD_FIELD( sessopens, sessopens, SESSOPENS, 502, ALWAYS_SETTABLE ),
+ MAKE_DWORD_FIELD( sessvcs, sessvcs, SESSVCS, 502, ALWAYS_SETTABLE ),
+ MAKE_DWORD_FIELD( opensearch, opensearch, OPENSEARCH, 502, ALWAYS_SETTABLE ),
+ MAKE_DWORD_FIELD( sizreqbuf, sizreqbuf, SIZREQBUF, 502, SET_ON_STARTUP ),
+ MAKE_DWORD_FIELD( initworkitems, initworkitems, INITWORKITEMS, 502, SET_ON_STARTUP ),
+ MAKE_DWORD_FIELD( maxworkitems, maxworkitems, MAXWORKITEMS, 502, ALWAYS_SETTABLE ),
+ MAKE_DWORD_FIELD( rawworkitems, rawworkitems, RAWWORKITEMS, 502, SET_ON_STARTUP ),
+ MAKE_DWORD_FIELD( irpstacksize, irpstacksize, IRPSTACKSIZE, 502, SET_ON_STARTUP ),
+ MAKE_DWORD_FIELD( maxrawbuflen, maxrawbuflen, MAXRAWBUFLEN, 502, ALWAYS_SETTABLE ),
+ MAKE_DWORD_FIELD( sessusers, sessusers, SESSUSERS, 502, ALWAYS_SETTABLE ),
+ MAKE_DWORD_FIELD( sessconns, sessconns, SESSCONNS, 502, ALWAYS_SETTABLE ),
+ MAKE_DWORD_FIELD( maxpagedmemoryusage, maxpagedmemoryusage, MAXPAGEDMEMORYUSAGE, 502, ALWAYS_SETTABLE ),
+ MAKE_DWORD_FIELD( maxnonpagedmemoryusage, maxnonpagedmemoryusage, MAXNONPAGEDMEMORYUSAGE, 502, ALWAYS_SETTABLE ),
+ MAKE_BOOL_FIELD( enablesoftcompat, enablesoftcompat, ENABLESOFTCOMPAT, 502, ALWAYS_SETTABLE ),
+ MAKE_BOOL_FIELD( enableforcedlogoff, enableforcedlogoff, ENABLEFORCEDLOGOFF, 502, ALWAYS_SETTABLE ),
+ MAKE_BOOL_FIELD( timesource, timesource, TIMESOURCE, 502, ALWAYS_SETTABLE ),
+ MAKE_BOOL_FIELD( acceptdownlevelapis, acceptdownlevelapis, ACCEPTDOWNLEVELAPIS, 502, SET_ON_STARTUP ),
+ MAKE_BOOL_FIELD( lmannounce, lmannounce, LMANNOUNCE, 502, ALWAYS_SETTABLE ),
+ MAKE_LPSTR_FIELD( domain, domain, DOMAIN, 503, NOT_SETTABLE ),
+ MAKE_DWORD_FIELD( maxcopyreadlen, maxcopyreadlen, MAXCOPYREADLEN, 503, ALWAYS_SETTABLE ),
+ MAKE_DWORD_FIELD( maxcopywritelen, maxcopywritelen, MAXCOPYWRITELEN, 503, ALWAYS_SETTABLE ),
+ MAKE_DWORD_FIELD( minkeepsearch, minkeepsearch, MINKEEPSEARCH, 503, ALWAYS_SETTABLE ),
+ MAKE_DWORD_FIELD( maxkeepsearch, maxkeepsearch, MAXKEEPSEARCH, 503, ALWAYS_SETTABLE ),
+ MAKE_DWORD_FIELD( minkeepcomplsearch, minkeepcomplsearch, MINKEEPCOMPLSEARCH, 503, ALWAYS_SETTABLE ),
+ MAKE_DWORD_FIELD( maxkeepcomplsearch, maxkeepcomplsearch, MAXKEEPCOMPLSEARCH, 503, ALWAYS_SETTABLE ),
+ MAKE_DWORD_FIELD( threadcountadd, threadcountadd, THREADCOUNTADD, 503, SET_ON_STARTUP ),
+ MAKE_DWORD_FIELD( numblockthreads, numblockthreads, NUMBLOCKTHREADS, 503, SET_ON_STARTUP ),
+ MAKE_DWORD_FIELD( scavtimeout, scavtimeout, SCAVTIMEOUT, 503, ALWAYS_SETTABLE ),
+ MAKE_DWORD_FIELD( minrcvqueue, minrcvqueue, MINRCVQUEUE, 503, ALWAYS_SETTABLE ),
+ MAKE_DWORD_FIELD( minfreeworkitems, minfreeworkitems, MINFREEWORKITEMS, 503, ALWAYS_SETTABLE ),
+ MAKE_DWORD_FIELD( xactmemsize, xactmemsize, XACTMEMSIZE, 503, SET_ON_STARTUP ),
+ MAKE_DWORD_FIELD( threadpriority, threadpriority, THREADPRIORITY, 503, SET_ON_STARTUP ),
+ MAKE_DWORD_FIELD( maxmpxct, maxmpxct, MAXMPXCT, 503, ALWAYS_SETTABLE ),
+ MAKE_DWORD_FIELD( oplockbreakwait, oplockbreakwait, OPLOCKBREAKWAIT, 503, ALWAYS_SETTABLE ),
+ MAKE_DWORD_FIELD( oplockbreakresponsewait, oplockbreakresponsewait, OPLOCKBREAKRESPONSEWAIT, 503, ALWAYS_SETTABLE ),
+ MAKE_BOOL_FIELD( enableoplocks, enableoplocks, ENABLEOPLOCKS, 503, ALWAYS_SETTABLE ),
+ MAKE_BOOL_FIELD( enableoplockforceclose, enableoplockforceclose, ENABLEOPLOCKFORCECLOSE, 503, ALWAYS_SETTABLE ),
+ MAKE_BOOL_FIELD( enablefcbopens, enablefcbopens, ENABLEFCBOPENS, 503, ALWAYS_SETTABLE ),
+ MAKE_BOOL_FIELD( enableraw, enableraw, ENABLERAW, 503, ALWAYS_SETTABLE ),
+ MAKE_BOOL_FIELD( enablesharednetdrives, enablesharednetdrives, ENABLESHAREDNETDRIVES, 503, ALWAYS_SETTABLE ),
+ MAKE_DWORD_FIELD( minfreeconnections, minfreeconnections, MINFREECONNECTIONS, 503, ALWAYS_SETTABLE ),
+ MAKE_DWORD_FIELD( maxfreeconnections, maxfreeconnections, MAXFREECONNECTIONS, 503, ALWAYS_SETTABLE ),
+ MAKE_DWORD_FIELD( initsesstable, initsesstable, INITSESSTABLE, 599, ALWAYS_SETTABLE ),
+ MAKE_DWORD_FIELD( initconntable, initconntable, INITCONNTABLE, 599, ALWAYS_SETTABLE ),
+ MAKE_DWORD_FIELD( initfiletable, initfiletable, INITFILETABLE, 599, ALWAYS_SETTABLE ),
+ MAKE_DWORD_FIELD( initsearchtable, initsearchtable, INITSEARCHTABLE, 599, ALWAYS_SETTABLE ),
+ MAKE_DWORD_FIELD( alertschedule, alertschedule, ALERTSCHEDULE, 599, ALWAYS_SETTABLE ),
+ MAKE_DWORD_FIELD( errorthreshold, errorthreshold, ERRORTHRESHOLD, 599, ALWAYS_SETTABLE ),
+ MAKE_DWORD_FIELD( networkerrorthreshold, networkerrorthreshold, NETWORKERRORTHRESHOLD, 599, ALWAYS_SETTABLE ),
+ MAKE_DWORD_FIELD( diskspacethreshold, diskspacethreshold, DISKSPACETHRESHOLD, 599, ALWAYS_SETTABLE ),
+ MAKE_DWORD_FIELD( maxlinkdelay, maxlinkdelay, MAXLINKDELAY, 599, ALWAYS_SETTABLE ),
+ MAKE_DWORD_FIELD( minlinkthroughput, minlinkthroughput, MINLINKTHROUGHPUT, 599, ALWAYS_SETTABLE ),
+ MAKE_DWORD_FIELD( linkinfovalidtime, linkinfovalidtime, LINKINFOVALIDTIME, 599, ALWAYS_SETTABLE ),
+ MAKE_DWORD_FIELD( scavqosinfoupdatetime, scavqosinfoupdatetime, SCAVQOSINFOUPDATETIME, 599, ALWAYS_SETTABLE ),
+ MAKE_DWORD_FIELD( maxworkitemidletime, maxworkitemidletime, MAXWORKITEMIDLETIME, 599, ALWAYS_SETTABLE ),
+ MAKE_DWORD_FIELD( maxrawworkitems, maxrawworkitems, MAXRAWWORKITEMS, 598, ALWAYS_SETTABLE ),
+ MAKE_DWORD_FIELD( maxthreadsperqueue, maxthreadsperqueue, MAXTHREADSPERQUEUE, 598, SET_ON_STARTUP ),
+ MAKE_DWORD_FIELD( connectionlessautodisc, connectionlessautodisc, CONNECTIONLESSAUTODISC, 598, ALWAYS_SETTABLE ),
+ MAKE_DWORD_FIELD( sharingviolationretries, sharingviolationretries, SHARINGVIOLATIONRETRIES, 598, ALWAYS_SETTABLE ),
+ MAKE_DWORD_FIELD( sharingviolationdelay, sharingviolationdelay, SHARINGVIOLATIONDELAY, 598, ALWAYS_SETTABLE ),
+ MAKE_DWORD_FIELD( maxglobalopensearch, maxglobalopensearch, MAXGLOBALOPENSEARCH, 598, ALWAYS_SETTABLE ),
+ MAKE_BOOL_FIELD( removeduplicatesearches, removeduplicatesearches, REMOVEDUPLICATESEARCHES, 598, ALWAYS_SETTABLE ),
+ MAKE_DWORD_FIELD( lockviolationoffset, lockviolationoffset, LOCKVIOLATIONOFFSET, 598, ALWAYS_SETTABLE ),
+ MAKE_DWORD_FIELD( lockviolationdelay, lockviolationdelay, LOCKVIOLATIONDELAY, 598, ALWAYS_SETTABLE ),
+ MAKE_DWORD_FIELD( mdlreadswitchover, mdlreadswitchover, MDLREADSWITCHOVER, 598, SET_ON_STARTUP ),
+ MAKE_DWORD_FIELD( cachedopenlimit, cachedopenlimit, CACHEDOPENLIMIT, 598, SET_ON_STARTUP ),
+ MAKE_DWORD_FIELD( cacheddirectorylimit, cacheddirectorylimit, CACHEDDIRECTORYLIMIT, 598, SET_ON_STARTUP ),
+ MAKE_DWORD_FIELD( maxcopylength, maxcopylength, MAXCOPYLENGTH, 598, ALWAYS_SETTABLE ),
+ MAKE_BOOL_FIELD( restrictnullsessaccess, restrictnullsessaccess, RESTRICTNULLSESSACCESS, 598, ALWAYS_SETTABLE ),
+ MAKE_BOOL_FIELD( enablewfw311directipx, enablewfw311directipx, ENABLEWFW311DIRECTIPX, 598, ALWAYS_SETTABLE ),
+ MAKE_DWORD_FIELD( otherqueueaffinity, otherqueueaffinity, OTHERQUEUEAFFINITY, 598, SET_ON_STARTUP ),
+ MAKE_DWORD_FIELD( queuesamplesecs, queuesamplesecs, QUEUESAMPLESECS, 598, SET_ON_STARTUP ),
+ MAKE_DWORD_FIELD( balancecount, balancecount, BALANCECOUNT, 598, SET_ON_STARTUP ),
+ MAKE_DWORD_FIELD( preferredaffinity, preferredaffinity, PREFERREDAFFINITY, 598, SET_ON_STARTUP ),
+ MAKE_DWORD_FIELD( maxfreerfcbs, maxfreerfcbs, MAXFREERFCBS, 598, SET_ON_STARTUP ),
+ MAKE_DWORD_FIELD( maxfreemfcbs, maxfreemfcbs, MAXFREEMFCBS, 598, SET_ON_STARTUP ),
+ MAKE_DWORD_FIELD( maxfreelfcbs, maxfreelfcbs, MAXFREELFCBS, 598, SET_ON_STARTUP ),
+ MAKE_DWORD_FIELD( maxfreepagedpoolchunks, maxfreepagedpoolchunks, MAXFREEPAGEDPOOLCHUNKS, 598, SET_ON_STARTUP ),
+ MAKE_DWORD_FIELD( minpagedpoolchunksize, minpagedpoolchunksize, MINPAGEDPOOLCHUNKSIZE, 598, SET_ON_STARTUP ),
+ MAKE_DWORD_FIELD( maxpagedpoolchunksize, maxpagedpoolchunksize, MAXPAGEDPOOLCHUNKSIZE, 598, SET_ON_STARTUP ),
+ MAKE_BOOL_FIELD( sendsfrompreferredprocessor, sendsfrompreferredprocessor, SENDSFROMPREFERREDPROCESSOR, 598, SET_ON_STARTUP ),
+ MAKE_BOOL_FIELD( enablebulktransfer, enablebulktransfer, ENABLEBULKTRANSFER, 598, SET_ON_STARTUP ),
+ MAKE_BOOL_FIELD( enablecompression, enablecompression, ENABLECOMPRESSION, 598, SET_ON_STARTUP ),
+ MAKE_BOOL_FIELD( autosharewks, autosharewks, AUTOSHAREWKS, 598, SET_ON_STARTUP ),
+ MAKE_BOOL_FIELD( autoshareserver, autoshareserver, AUTOSHARESERVER, 598, SET_ON_STARTUP ),
+
+ NULL, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+//
+// Resource for synchronizing access to server info.
+//
+
+RTL_RESOURCE SsServerInfoResource;
+
+BOOL SsServerInfoResourceInitialized = FALSE;
+
+//
+// Boolean indicating whether the server service is initialized.
+//
+
+BOOL SsInitialized = FALSE;
+
+//
+// Boolean indicating whether the kernel-mode server FSP has been
+// started.
+//
+
+BOOL SsServerFspStarted = FALSE;
+
+//
+// Event used for synchronizing server service termination.
+//
+
+HANDLE SsTerminationEvent = NULL;
+
+//
+// Event used for forcing the server to announce itself on the network from
+// remote machines
+//
+
+HANDLE SsAnnouncementEvent = NULL;
+
+//
+// Event used for forcing the server service to announce itself on the network.
+//
+
+HANDLE SsStatusChangedEvent = NULL;
+
+//
+// Name of this computer in OEM format
+//
+
+CHAR SsServerTransportAddress[ MAX_PATH ];
+ULONG SsServerTransportAddressLength;
+
+//
+// List containing transport specific service type bits and server names
+//
+
+PNAME_LIST_ENTRY SsServerNameList = NULL;
+
+//
+// Variable to control DbgPrints for debugging.
+//
+
+#if DBG
+DWORD SsDebug = SSDEBUG_DEFAULT;
+#endif
+
diff --git a/private/net/svcdlls/srvsvc/server/ssdata.h b/private/net/svcdlls/srvsvc/server/ssdata.h
new file mode 100644
index 000000000..6434f1732
--- /dev/null
+++ b/private/net/svcdlls/srvsvc/server/ssdata.h
@@ -0,0 +1,125 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ SsData.h
+
+Abstract:
+
+ This module contains declarations for global data used by the server
+ service.
+
+Author:
+
+ David Treadwell (davidtr) 7-Mar-1991
+
+Revision History:
+
+--*/
+
+#ifndef _SSDATA_
+#define _SSDATA_
+
+#include <services.h>
+
+#include <nturtl.h>
+#include <winbase.h>
+#include <winreg.h>
+
+//
+// Handle for accessing the server.
+//
+
+extern HANDLE SsServerDeviceHandle;
+
+//
+// Global server service data.
+//
+
+extern SERVER_SERVICE_DATA SsData;
+
+//
+// Pointer to global data made available by LMSVCS main image.
+//
+
+extern PLMSVCS_GLOBAL_DATA SsLmsvcsGlobalData;
+
+//
+// Manifests that determine field type.
+//
+
+#define BOOLEAN_FIELD 0
+#define DWORD_FIELD 1
+#define LPSTR_FIELD 2
+
+//
+// Manifests that determine when a field may be set.
+//
+
+#define NOT_SETTABLE 0
+#define SET_ON_STARTUP 1
+#define ALWAYS_SETTABLE 2
+
+//
+// Data for all server info fields.
+//
+
+extern FIELD_DESCRIPTOR SsServerInfoFields[];
+
+//
+// Resource for synchronizing access to server info.
+//
+
+extern RTL_RESOURCE SsServerInfoResource;
+
+extern BOOL SsServerInfoResourceInitialized;
+
+//
+// Boolean indicating whether the server service is initialized.
+//
+
+extern BOOL SsInitialized;
+
+//
+// Boolean indicating whether the kernel-mode server FSP has been
+// started.
+//
+
+extern BOOL SsServerFspStarted;
+
+//
+// Event used for synchronizing server service termination.
+//
+
+extern HANDLE SsTerminationEvent;
+
+//
+// Event used for forcing the server to announce itself on the network from
+// remote clients.
+//
+
+extern HANDLE SsAnnouncementEvent;
+
+//
+// Event used for forcing the server to announce itself on the network from
+// inside the server service.
+//
+
+extern HANDLE SsStatusChangedEvent;
+
+//
+// Name of this computer in OEM format.
+//
+
+extern CHAR SsServerTransportAddress[ MAX_PATH ];
+extern ULONG SsServerTransportAddressLength;
+
+//
+// List containing transport specific service names and bits
+//
+
+extern PNAME_LIST_ENTRY SsServerNameList;
+
+#endif // ndef _SSDATA_
diff --git a/private/net/svcdlls/srvsvc/server/ssdebug.h b/private/net/svcdlls/srvsvc/server/ssdebug.h
new file mode 100644
index 000000000..a13bba86e
--- /dev/null
+++ b/private/net/svcdlls/srvsvc/server/ssdebug.h
@@ -0,0 +1,90 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ SsDebug.h
+
+Abstract:
+
+ Header file for various server service debugging aids.
+
+Author:
+
+ David Treadwell (davidtr) 10-Jan-1991
+
+Revision History:
+
+--*/
+
+#ifndef _SSDEBUG_
+#define _SSDEBUG_
+
+#if DBG
+
+#ifndef SSDEBUG_DEFAULT
+#define SSDEBUG_DEFAULT 0
+#endif
+
+#define DEBUG_INITIALIZATION 0x00000001
+#define DEBUG_INITIALIZATION_ERRORS 0x00000002
+#define DEBUG_TERMINATION 0x00000004
+#define DEBUG_TERMINATION_ERRORS 0x00000008
+
+#define DEBUG_API_ERRORS 0x00000010
+#define DEBUG_FS_CONTROL 0x00000020
+#define DEBUG_REGISTRY 0x00000040
+#define DEBUG_8 0x00000080
+
+#define DEBUG_ANNOUNCE 0x00000100
+#define DEBUG_CONTROL_MESSAGES 0x00000200
+#define DEBUG_11 0x00000400
+#define DEBUG_12 0x00000800
+
+#define DEBUG_SECURITY 0x00001000
+#define DEBUG_ACCESS_DENIED 0x00002000
+#define DEBUG_INITIALIZATION_BREAKPOINT 0x00004000
+#define DEBUG_TERMINATION_BREAKPOINT 0x00008000
+
+extern ULONG SsDebug;
+
+#define DEBUG if ( TRUE )
+#define IF_DEBUG(flag) if (SsDebug & (DEBUG_ ## flag))
+
+VOID
+SsPrintf (
+ char *Format,
+ ...
+ );
+
+#ifdef USE_DEBUGGER
+#define SS_PRINT(args) DbgPrint args
+#else
+#define SS_PRINT(args) SsPrintf args
+#endif
+
+#ifdef USE_DEBUGGER
+#define SS_ASSERT(exp) ASSERT(exp)
+#else
+VOID
+SsAssert(
+ IN PVOID FailedAssertion,
+ IN PVOID FileName,
+ IN ULONG LineNumber
+ );
+#define SS_ASSERT(exp) if (!(exp)) SsAssert( #exp, __FILE__, __LINE__ )
+#endif
+
+#else
+
+#define DEBUG if ( FALSE )
+#define IF_DEBUG(flag) if (FALSE)
+
+#define SS_PRINT(args)
+
+#define SS_ASSERT(exp)
+
+#endif
+
+#endif // ndef _SSDEBUG_
diff --git a/private/net/svcdlls/srvsvc/server/ssinit.c b/private/net/svcdlls/srvsvc/server/ssinit.c
new file mode 100644
index 000000000..6b753391d
--- /dev/null
+++ b/private/net/svcdlls/srvsvc/server/ssinit.c
@@ -0,0 +1,1524 @@
+/*++
+
+Copyright (c) 1991-1992 Microsoft Corporation
+
+Module Name:
+
+ SsInit.c
+
+Abstract:
+
+ This module contains initialization routines for the NT server
+ service.
+
+Author:
+
+ David Treadwell (davidtr) 6-Mar-1991
+
+Revision History:
+
+ ChuckC 20-May-93 Load share remarks from messagefile so it
+ can be internationalized.
+
+--*/
+
+#include "srvsvcp.h"
+#if DBG
+#include "srvconfg.h"
+#endif
+#include "ssdata.h"
+#include "ssreg.h"
+
+#include <netevent.h>
+
+#include <lmapibuf.h> // NetApiBufferFree().
+#include <netlib.h>
+#include <apperr2.h>
+
+#include <debugfmt.h>
+#include <tstr.h>
+
+#ifdef _CAIRO_
+#include <windows.h>
+#include <ole2.h>
+#include <tdi.h>
+#include <gluon.h>
+#include <dsapi.h>
+#include <dfsapi.h>
+#endif // _CAIRO_
+
+#define SERVICE_REGISTRY_KEY L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\"
+#define SERVER_DRIVER_NAME L"Srv"
+
+//
+// Internationalizable share remarks.
+//
+
+#define NETMSG_DLL TEXT("NETMSG.DLL")
+LPWSTR SsDefaultRemark = TEXT("") ; // if all else fails
+
+LPWSTR SsAdminShareRemark = NULL ;
+LPWSTR SsIPCShareRemark = NULL ;
+LPWSTR SsDiskAdminShareRemark = NULL ;
+
+//
+// Forward declarations.
+//
+
+NET_API_STATUS
+CreateDefaultShares (
+ VOID
+ );
+
+VOID
+InitializeDefaultData(
+ VOID
+ );
+
+VOID
+InitializeStrings(
+ VOID
+ );
+
+VOID
+FreeStrings(
+ VOID
+ );
+
+NET_API_STATUS
+InitializeServer (
+ VOID
+ );
+
+NET_API_STATUS
+LoadServer (
+ VOID
+ );
+
+VOID
+SetServerName (
+ VOID
+ );
+
+VOID
+SetDomainName (
+ VOID
+ );
+
+DWORD
+DiscoverDrives (
+ VOID
+ );
+
+NET_API_STATUS
+TerminateServer (
+ VOID
+ );
+
+VOID
+UnloadServer (
+ VOID
+ );
+
+
+NET_API_STATUS
+SsInitialize (
+ IN DWORD argc,
+ IN LPWSTR argv[]
+ )
+
+/*++
+
+Routine Description:
+
+ This routine controls initialization of the server service and
+ server driver. It sets up server data stored in the server
+ service, parses the command line parameters in case any data needs
+ to be changed, and then starts the file server.
+
+Arguments:
+
+ argc - the count of command-line arguments.
+
+ argv - an array of pointers to the command line arguments.
+
+Return Value:
+
+ NET_API_STATUS - results of operation.
+
+--*/
+
+{
+ NET_API_STATUS error;
+
+ //
+ // Initialize the resource that protects access to global server
+ // information.
+ //
+
+ SS_ASSERT( !SsServerInfoResourceInitialized );
+ RtlInitializeResource( &SsServerInfoResource );
+
+ //
+ // We hold this resource while we are doing announcements, and when
+ // we communicate with the FSD. These ought to be quick operations,
+ // but it's really unpredictable under load. Indicate to the RTL
+ // that we really don't know how long it'll take.
+ //
+ SsServerInfoResource.Flags |= RTL_RESOURCE_FLAG_LONG_TERM;
+
+ SsServerInfoResourceInitialized = TRUE;
+
+ //
+ // Get the internationalizable special share remarks
+ //
+ InitializeStrings( );
+
+ //
+ // Initialize the server name list bits list.
+ //
+
+ SsServerNameList = NULL;
+
+ IF_DEBUG(INITIALIZATION) {
+ SS_PRINT(( "SsInitialize: resource initialized.\n" ));
+ }
+
+ //
+ // Create the event used for termination synchronization.
+ //
+
+ SS_ASSERT( SsTerminationEvent == NULL );
+ SsTerminationEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
+ if ( SsTerminationEvent == NULL ) {
+ error = GetLastError( );
+ SS_PRINT(( "SsInitialize: CreateEvent failed: %ld\n", error ));
+ return error;
+ }
+
+ //
+ // Initialize the server data to its default values stored in
+ // srvconfg.h.
+ //
+
+ InitializeDefaultData( );
+
+ IF_DEBUG(INITIALIZATION) {
+ SS_PRINT(( "SsInitialize: default data initialized.\n" ));
+ }
+
+ //
+ // Get the computer name.
+ //
+
+ SetServerName( );
+
+ //
+ // Get the primary domain for this computer.
+ //
+
+ SetDomainName( );
+
+ //
+ // See if we are the top of a DFS tree
+ //
+ SsSetDfsRoot();
+
+ //
+ // Verify that the various registry keys under the main server
+ // service key exist.
+ //
+
+ error = SsCheckRegistry( );
+ if ( error != NO_ERROR ) {
+ IF_DEBUG(INITIALIZATION_ERRORS) {
+ SS_PRINT(( "SsInitialize: SsCheckRegistry failed: %ld\n", error ));
+ }
+ return error;
+ }
+
+ IF_DEBUG(INITIALIZATION) {
+ SS_PRINT(( "SsInitialize: registry keys verified.\n" ));
+ }
+
+ //
+ // Load server configuration data from the registry.
+ //
+
+ error = SsLoadConfigurationParameters( );
+ if ( error != NO_ERROR ) {
+ IF_DEBUG(INITIALIZATION_ERRORS) {
+ SS_PRINT(( "SsInitialize: SsLoadConfigurationParameters failed: "
+ "%ld\n", error ));
+ }
+ return error;
+ }
+
+ IF_DEBUG(INITIALIZATION) {
+ SS_PRINT(( "SsInitialize: configuration parameters loaded.\n" ));
+ }
+
+ //
+ // Parse the command line. This will change server data values as
+ // specified.
+ //
+
+ error = SsParseCommandLine( argc, argv, TRUE );
+ if ( error != NO_ERROR ) {
+ IF_DEBUG(INITIALIZATION_ERRORS) {
+ SS_PRINT(( "SsInitialize: SsParseCommandLine failed: %ld\n",
+ error ));
+ }
+ return error;
+ }
+
+ IF_DEBUG(INITIALIZATION) {
+ SS_PRINT(( "SsInitialize: command line parsed.\n" ));
+ }
+
+ //
+ // Set up the security objects that will be used to validate access
+ // for APIs.
+ //
+
+ error = SsCreateSecurityObjects( );
+ if ( error != NO_ERROR ) {
+ return error;
+ }
+
+ IF_DEBUG(INITIALIZATION) {
+ SS_PRINT(( "SsInitialize: security initialized.\n" ));
+ }
+
+ //
+ // Start the file server driver.
+ //
+
+ error = InitializeServer( );
+ if ( error != NO_ERROR ) {
+ return error;
+ }
+
+ IF_DEBUG(INITIALIZATION) {
+ SS_PRINT(( "SsInitialize: server FSP initialized.\n" ));
+ }
+
+ //
+ // Start XACTSRV, if requested.
+ //
+ // *** This must be done AFTER the server driver is started, but
+ // BEFORE sticky shares are recreated, otherwise downlevel print
+ // shares are lost.
+ //
+
+ if ( SsData.ServerInfo599.sv599_acceptdownlevelapis ) {
+ error = XsStartXactsrv( );
+ if ( error != NO_ERROR ) {
+ return error;
+ }
+ }
+
+ //
+ // Create the default shares needed by the server.
+ //
+
+ error = CreateDefaultShares( );
+ if ( error != NO_ERROR ) {
+ return error;
+ }
+
+ IF_DEBUG(INITIALIZATION) {
+ SS_PRINT(( "SsInitialize: default shares created.\n" ));
+ }
+
+ //
+ // Complete loading the configuration--sticky shares and transports.
+ // These must be done after the server FSP has started.
+ //
+
+ error = SsRecreateStickyShares( );
+ if ( error != NO_ERROR ) {
+ return error;
+ }
+
+ IF_DEBUG(INITIALIZATION) {
+ SS_PRINT(( "SsInitialize: sticky shares reloaded.\n" ));
+ }
+
+#ifndef SRV_PNP_POWER
+ error = SsBindToTransports( );
+
+ if ( error != NO_ERROR ) {
+ return error;
+ }
+
+ IF_DEBUG(INITIALIZATION) {
+ SS_PRINT(( "SsInitialize: transports bound.\n" ));
+ }
+#endif
+
+ //
+ // Set information used in server announcements.
+ //
+
+ SsSetExportedServerType( NULL, FALSE, FALSE );
+
+ //
+ // Server initialization was successful.
+ //
+
+ return NO_ERROR;
+
+} // SsInitialize
+
+
+NET_API_STATUS
+SsTerminate (
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ This routine sends the FSCTL_SRV_SHUTDOWN control code to the server
+ FSD to tell it to terminate its FSP.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NET_API_STATUS error;
+ PNAME_LIST_ENTRY Service;
+ PTRANSPORT_LIST_ENTRY Transport;
+
+ //
+ // Shut the server FSD/FSP down.
+ //
+
+ error = TerminateServer( );
+
+ //
+ // Shut down XACTSRV.
+ //
+
+ XsStopXactsrv( );
+
+ //
+ // Delete security objects.
+ //
+
+ SsDeleteSecurityObjects( );
+
+ //
+ // Close the network announcement event.
+ //
+
+ if (SsAnnouncementEvent != NULL) {
+ CloseHandle( SsAnnouncementEvent );
+ SsAnnouncementEvent = NULL;
+ }
+
+ //
+ // Close the local announcement event.
+ //
+
+ if (SsStatusChangedEvent != NULL) {
+ CloseHandle( SsStatusChangedEvent );
+ SsStatusChangedEvent = NULL;
+ }
+
+ //
+ // Close the termination event.
+ //
+
+ if ( SsTerminationEvent != NULL ) {
+ CloseHandle( SsTerminationEvent );
+ SsTerminationEvent = NULL;
+ }
+
+ //
+ // Free up the transport service list.
+ //
+
+ while( SsServerNameList != NULL ) {
+
+ PNAME_LIST_ENTRY Service = SsServerNameList;
+
+ while( Service->Transports != NULL ) {
+ PTRANSPORT_LIST_ENTRY Next = Service->Transports->Next;
+ MIDL_user_free( Service->Transports );
+ Service->Transports = Next;
+ }
+
+ SsServerNameList = Service->Next;
+
+ MIDL_user_free( Service );
+ }
+
+ //
+ // Delete the server info resource.
+ //
+
+ if ( SsServerInfoResourceInitialized ) {
+ RtlDeleteResource( &SsServerInfoResource );
+ SsServerInfoResourceInitialized = FALSE;
+ }
+
+ //
+ // Free up any string relate memory
+ //
+ FreeStrings() ;
+
+ return error;
+
+} // SsTerminate
+
+
+NET_API_STATUS
+CreateDefaultShares (
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ This routine sends the NetShareAdd API to the server to add the
+ default server shares using the data above.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NET_API_STATUS - results of operation.
+
+--*/
+
+{
+ NET_API_STATUS error;
+ SHARE_INFO_2 shareInfo;
+ SHARE_INFO shInfo;
+ WCHAR diskShareName[3];
+ WCHAR diskSharePath[4];
+ ULONG i;
+ DWORD diskMask;
+ DWORD diskconfiguration;
+
+ //
+ // Create IPC$.
+ //
+ // !!! Need to verify the remarks for these default shares.
+ //
+
+ shareInfo.shi2_netname = IPC_SHARE_NAME;
+ shareInfo.shi2_type = STYPE_IPC;
+ shareInfo.shi2_remark = NULL;
+ shareInfo.shi2_permissions = 0;
+ shareInfo.shi2_max_uses = SHI_USES_UNLIMITED;
+ shareInfo.shi2_current_uses = 0;
+ shareInfo.shi2_path = NULL;
+ shareInfo.shi2_passwd = NULL;
+
+ shInfo.ShareInfo2 = &shareInfo;
+ error = NetrShareAdd( NULL, 2, &shInfo, NULL );
+ if ( error != NO_ERROR ) {
+ IF_DEBUG(INITIALIZATION_ERRORS) {
+ SS_PRINT(( "CreateDefaultShares: failed to add " FORMAT_LPWSTR
+ ": %X\n", shareInfo.shi2_netname, error ));
+ }
+ } else {
+ IF_DEBUG(INITIALIZATION) {
+ SS_PRINT(( "CreateDefaultShares: added default share "
+ FORMAT_LPWSTR "\n", shareInfo.shi2_netname, error ));
+ }
+ }
+
+ //
+ // If this is a workstation, and the AutoShareWks key is set to TRUE then
+ // automatically create the admin$ and drive$ shares.
+ //
+ //
+ // If this is a server, and the AutoShareServer key is set to TRUE then
+ // automatically create the admin$ and drive$ shares.
+ //
+
+ if( (SsData.ServerInfo598.sv598_producttype == NtProductWinNt &&
+ SsData.ServerInfo598.sv598_autosharewks) ||
+
+ (SsData.ServerInfo598.sv598_producttype != NtProductWinNt &&
+ SsData.ServerInfo598.sv598_autoshareserver ) ) {
+
+ //
+ // Create ADMIN$.
+ //
+
+ shareInfo.shi2_netname = ADMIN_SHARE_NAME;
+ shareInfo.shi2_type = STYPE_DISKTREE;
+ shareInfo.shi2_remark = NULL;
+ shareInfo.shi2_permissions = 1;
+ shareInfo.shi2_max_uses = SHI_USES_UNLIMITED;
+ shareInfo.shi2_current_uses = 0;
+ shareInfo.shi2_path = NULL;
+ shareInfo.shi2_passwd = NULL;
+
+ error = NetrShareAdd( NULL, 2, &shInfo, NULL );
+ if ( error != NO_ERROR ) {
+ IF_DEBUG(INITIALIZATION_ERRORS) {
+ SS_PRINT(( "CreateDefaultShares: failed to add " FORMAT_LPWSTR
+ ": %X\n", shareInfo.shi2_netname, error ));
+ }
+ } else {
+ IF_DEBUG(INITIALIZATION) {
+ SS_PRINT(( "CreateDefaultShares: added default share "
+ FORMAT_LPWSTR "\n", shareInfo.shi2_netname, error ));
+ }
+ }
+
+ //
+ // Loop through available drives, creating an admin share for each
+ // one. Note that we allow "holes" in the drive letter space.
+ //
+
+ diskShareName[0] = 'A';
+ diskShareName[1] = '$';
+ diskShareName[2] = '\0';
+
+ diskSharePath[0] = diskShareName[0];
+ diskSharePath[1] = ':';
+ diskSharePath[2] = '\\';
+ diskSharePath[3] = '\0';
+
+ shareInfo.shi2_netname = diskShareName;
+ shareInfo.shi2_type = STYPE_DISKTREE;
+ shareInfo.shi2_remark = SsDiskAdminShareRemark;
+ shareInfo.shi2_permissions = 1;
+ shareInfo.shi2_max_uses = SHI_USES_UNLIMITED;
+ shareInfo.shi2_current_uses = 0;
+ shareInfo.shi2_path = diskSharePath;
+ shareInfo.shi2_passwd = NULL;
+
+ diskconfiguration = DiscoverDrives();
+
+ for ( i = 0, diskMask = 0x80000000;
+ (i < SRVSVC_MAX_NUMBER_OF_DISKS) && (diskShareName[0] <= 'Z');
+ i++, diskShareName[0]++, diskSharePath[0]++, diskMask >>= 1 ) {
+
+
+ if ( (diskconfiguration & diskMask) != 0) {
+
+ error = NetrShareAdd( NULL, 2, &shInfo, NULL );
+
+ if ( error != NO_ERROR ) {
+ IF_DEBUG(INITIALIZATION_ERRORS) {
+ SS_PRINT(( "CreateDefaultShares: failed to add "
+ FORMAT_LPWSTR ": %X\n",
+ shareInfo.shi2_netname, error ));
+ }
+ } else {
+ IF_DEBUG(INITIALIZATION) {
+ SS_PRINT(( "CreateDefaultShares: added default share "
+ FORMAT_LPWSTR "\n",
+ shareInfo.shi2_netname, error ));
+ }
+ }
+ }
+ }
+ }
+
+ return NO_ERROR;
+
+} // CreateDefaultShares
+
+
+DWORD
+DiscoverDrives (
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ This routine returns a bit mask representing the local drives present
+ on the system.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ DrivesAvailable - A 32 bit field representing the available drives on
+ the system. The MSB represents drive A, the next represents drive
+ B, etc. The extra 6 bits are currently unsed.
+
+--*/
+
+{
+ WCHAR rootDirPath[4];
+ WCHAR driveLetter;
+ DWORD drivesAvailable = 0;
+ DWORD driveMask = 0x80000000;
+ UINT driveType;
+
+
+ rootDirPath[1] = ':';
+ rootDirPath[2] = '\\';
+ rootDirPath[3] = '\0';
+
+ for ( driveLetter = 'A';
+ driveLetter <= 'Z';
+ driveLetter++ ) {
+
+ rootDirPath[0] = driveLetter;
+
+ driveType = SsGetDriveType( rootDirPath );
+
+ //
+ // We only put fixed disk drives into the mask. We used to put
+ // removables, CD-ROMs, and RAM disks into the list. But this
+ // list is used for two purposes: creation of X$ shares (for
+ // backup purposes), and free disk space checking (for admin
+ // purposes). Neither of these uses applies very well to these
+ // devices.
+ //
+
+ if ( driveType == DRIVE_FIXED
+ //|| driveType == DRIVE_REMOVABLE
+ //|| driveType == DRIVE_CDROM
+ //|| driveType == DRIVE_RAMDISK
+ ) {
+
+ //
+ // This is a valid drive letter
+ //
+
+ drivesAvailable |= driveMask;
+ }
+
+ //
+ // Update drive mask for the next drive
+ //
+
+ driveMask /= 2;
+
+ }
+
+ return drivesAvailable;
+}
+
+
+VOID
+InitializeDefaultData(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ This routine sets up the default data in the server service by using
+ the values in srvconfg.h.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NET_API_STATUS error;
+ CSHORT i;
+ OSVERSIONINFO VersionInformation;
+ WCHAR szNumber[ sizeof( SsData.szVersionNumber ) / sizeof( WCHAR ) ], *p;
+
+ //
+ // Loop through all the defined fields, setting them as we go.
+ //
+
+ for ( i = 0; SsServerInfoFields[i].FieldName != NULL; i++ ) {
+
+ error = SsSetField(
+ &SsServerInfoFields[i],
+ &SsServerInfoFields[i].DefaultValue,
+ FALSE,
+ NULL
+ );
+ SS_ASSERT( error == NO_ERROR );
+ }
+
+ SsData.NumberOfPrintShares = 0;
+
+ //
+ // Get the system version and product name
+ //
+ VersionInformation.dwOSVersionInfoSize = sizeof( VersionInformation );
+ i = GetVersionEx( &VersionInformation );
+
+ SS_ASSERT( i == TRUE );
+
+ SsData.ServerInfo102.sv102_version_major = VersionInformation.dwMajorVersion;
+ SsData.ServerInfo102.sv102_version_minor = VersionInformation.dwMinorVersion;
+
+ wcscpy( SsData.ServerProductName, SERVER_PRODUCT_NAME );
+
+ //
+ // Convert the version number to a version number string...
+ //
+ szNumber[ sizeof( szNumber ) / sizeof( szNumber[0] ) - 1 ] = L'\0';
+ for( p = &szNumber[ sizeof( szNumber ) / sizeof( szNumber[0] ) - 2 ]; p > &szNumber[0]; p-- ) {
+ *p = L"0123456789"[ VersionInformation.dwMinorVersion % 10 ];
+ VersionInformation.dwMinorVersion /= 10;
+ if( VersionInformation.dwMinorVersion == 0 )
+ break;
+ }
+
+ *(--p) = L'.';
+
+ do {
+ *(--p) = L"0123456789"[ VersionInformation.dwMajorVersion % 10 ];
+ VersionInformation.dwMajorVersion /= 10;
+ } while( VersionInformation.dwMajorVersion && p > &szNumber[0] );
+
+ //
+ // ... and store it in SsData
+ //
+ wcscpy( SsData.szVersionNumber, p );
+
+} // InitializeDefaultData
+
+
+NET_API_STATUS
+InitializeServer (
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ This routine sends the FSCTL_SRV_STARTUP control code to the server
+ FSD to tell it to start and initialize its FSP.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NET_API_STATUS - results of operation.
+
+--*/
+
+{
+ NET_API_STATUS error;
+ PSERVER_REQUEST_PACKET srp;
+
+ SS_ASSERT( !SsServerFspStarted );
+
+ //
+ // Load the server driver.
+ //
+
+ error = LoadServer( );
+
+ //
+ // Get a handle to the server.
+ //
+
+ error = SsOpenServer( NULL );
+ if ( error != NO_ERROR ) {
+ return error;
+ }
+
+ //
+ // Get an SRP and set it up with the appropriate level.
+ //
+
+ srp = SsAllocateSrp( );
+ if ( srp == NULL ) {
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+ srp->Level = (ULONG)SS_STARTUP_LEVEL;
+
+ //
+ // Pass domain name to the server.
+ //
+
+ RtlInitUnicodeString( &srp->Name1, SsData.LongDomainNameBuffer[0] ?
+ SsData.LongDomainNameBuffer :
+ SsData.DomainNameBuffer );
+
+ //
+ // Pass server name to the server.
+ //
+
+ RtlInitUnicodeString( &srp->Name2, SsData.ServerNameBuffer );
+
+ //
+ // Send the request on to the server.
+ //
+
+ error = SsServerFsControl(
+ NULL,
+ FSCTL_SRV_STARTUP,
+ srp,
+ &SsData.ServerInfo102,
+ sizeof(SERVER_INFO_102) + sizeof(SERVER_INFO_599) +
+ sizeof(SERVER_INFO_598)
+ );
+
+ if ( error == NO_ERROR ) {
+ SsServerFspStarted = TRUE;
+ }
+
+ //
+ // Free the SRP and return.
+ //
+
+ SsFreeSrp( srp );
+
+ return error;
+
+} // InitializeServer
+
+#ifdef SRV_PNP_POWER
+
+NET_API_STATUS
+StartPnpNotifications (
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ This routine sends the FSCTL_SRV_BEGIN_PNP_NOTIFICATIONS control code to the server
+ FSD to tell it to start monitoring transport PNP notifications
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NET_API_STATUS - results of operation.
+
+--*/
+
+{
+ NET_API_STATUS error;
+
+ //
+ // Send the request on to the server.
+ //
+
+ error = SsServerFsControl(
+ NULL,
+ FSCTL_SRV_BEGIN_PNP_NOTIFICATIONS,
+ NULL,
+ NULL,
+ 0
+ );
+
+ IF_DEBUG(INITIALIZATION) {
+ if( error != NO_ERROR ) {
+ SS_PRINT(( "StartPnpNotifications: error %X\n", error ));
+ }
+ }
+
+ return error;
+
+} // InitializeServer
+#endif
+
+
+
+NET_API_STATUS
+LoadServer (
+ VOID
+ )
+{
+ NTSTATUS status;
+ NET_API_STATUS error;
+ LPWSTR registryPathBuffer;
+ UNICODE_STRING registryPath;
+ ULONG privileges[1];
+ LPWSTR subString[1];
+
+ IF_DEBUG(INITIALIZATION) {
+ SS_PRINT(( "LoadServer: entered\n" ));
+ }
+ registryPathBuffer = (LPWSTR)MIDL_user_allocate(
+ sizeof(SERVICE_REGISTRY_KEY) +
+ sizeof(SERVER_DRIVER_NAME)
+ );
+ if ( registryPathBuffer == NULL ) {
+ IF_DEBUG(INITIALIZATION_ERRORS) {
+ SS_PRINT(( "LoadServer: Unable to allocate memory\n" ));
+ }
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ privileges[0] = SE_LOAD_DRIVER_PRIVILEGE;
+
+ error = NetpGetPrivilege( 1, privileges );
+ if ( error != NO_ERROR ) {
+ IF_DEBUG(INITIALIZATION_ERRORS) {
+ SS_PRINT(( "LoadServer: Unable to enable privilege: %ld\n",
+ error ));
+ }
+ MIDL_user_free( registryPathBuffer );
+ return error;
+ }
+
+ wcscpy( registryPathBuffer, SERVICE_REGISTRY_KEY );
+ wcscat( registryPathBuffer, SERVER_DRIVER_NAME );
+
+ RtlInitUnicodeString( &registryPath, registryPathBuffer );
+
+ status = NtLoadDriver( &registryPath );
+
+ MIDL_user_free( registryPathBuffer );
+
+ NetpReleasePrivilege( );
+
+ if ( status == STATUS_IMAGE_ALREADY_LOADED ) {
+ status = STATUS_SUCCESS;
+ }
+
+ if ( !NT_SUCCESS(status) ) {
+
+ IF_DEBUG(INITIALIZATION_ERRORS) {
+ SS_PRINT(( "LoadServer: Unable to load driver: %lx\n",
+ status ));
+ }
+
+ subString[0] = SERVER_DRIVER_NAME;
+ SsLogEvent(
+ EVENT_SRV_CANT_LOAD_DRIVER,
+ 1,
+ subString,
+ status
+ );
+
+ }
+
+ IF_DEBUG(INITIALIZATION) {
+ SS_PRINT(( "LoadServer: returning\n" ));
+ }
+ return RtlNtStatusToDosError(status);
+
+} // LoadServer
+
+
+NET_API_STATUS
+ConvertStringToTransportAddress (
+ IN PUNICODE_STRING InputName,
+ OUT CHAR TransportAddress[ MAX_PATH ],
+ OUT PULONG TransportAddressLength
+ )
+{
+ OEM_STRING computerName;
+
+ if( InputName == NULL || InputName->Length == 0 ) {
+ RtlCopyMemory( TransportAddress,
+ SsServerTransportAddress,
+ SsServerTransportAddressLength );
+
+ *TransportAddressLength = SsServerTransportAddressLength;
+ return NO_ERROR;
+ }
+
+ if( InputName->Length > (MAX_PATH - 1 ) * sizeof( WCHAR ) ) {
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ //
+ // Write directly to the output buffer
+ //
+
+ computerName.Buffer = TransportAddress;
+ computerName.MaximumLength = MAX_PATH;
+
+ //
+ // Convert To Oem Name
+ //
+
+ (VOID) RtlUpcaseUnicodeStringToOemString(
+ &computerName,
+ InputName,
+ FALSE
+ );
+
+ //
+ // Make sure it is exactly NETBIOS_NAME_LEN characters
+ //
+ if( computerName.Length < NETBIOS_NAME_LEN ) {
+
+ RtlCopyMemory( TransportAddress + computerName.Length,
+ " ",
+ NETBIOS_NAME_LEN - computerName.Length
+ );
+
+ *TransportAddressLength = NETBIOS_NAME_LEN;
+
+ } else {
+
+ *TransportAddressLength = NETBIOS_NAME_LEN;
+
+ }
+
+ return NO_ERROR;
+
+} // ConvertStringToTransportAddress
+
+
+VOID
+SetDomainName (
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ Tries to get the Cairo domain. If it can't then:
+ Calls NetpGetDomainName to determine the domain name the server
+ should use.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NET_API_STATUS error;
+ LPWSTR domainName;
+#ifdef _CAIRO_
+ WCHAR szDomainName[MAX_PATH];
+ DWORD dwSize = MAX_PATH;
+
+
+ if(SUCCEEDED(DSGetDomainName(szDomainName, &dwSize))
+ &&
+ (szDomainName[0] == L'\\'))
+ {
+ STRCPY( SsData.LongDomainNameBuffer, szDomainName );
+ }
+#endif
+
+ //
+ // Get the domain name.
+ //
+
+ error = NetpGetDomainName( &domainName );
+ SS_ASSERT( error == NO_ERROR );
+
+ //
+ // Copy the name into our name buffer.
+ //
+
+ STRCPY( SsData.DomainNameBuffer, domainName );
+
+ //
+ // Free the storage allocated by NetpGetComputerName.
+ //
+
+ (VOID)NetApiBufferFree( domainName );
+
+ IF_DEBUG(INITIALIZATION) {
+ SS_PRINT(( "SetDomainName: domain name set to " FORMAT_LPWSTR
+ "(could be overridden later!)\n",
+ SsData.DomainNameBuffer ));
+ }
+
+ return;
+
+} // SetDomainName
+
+
+VOID
+SetServerName (
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ Calls NetpGetComputerName to determine the name the server should use
+ to register itself on the network.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NET_API_STATUS error;
+ LPWSTR computerName;
+
+ //
+ // Get the computer name.
+ //
+
+ error = NetpGetComputerName( &computerName );
+ SS_ASSERT( error == NO_ERROR );
+
+ //
+ // Copy the name into our name buffer. This name is returned to
+ // our apis.
+ //
+
+ STRCPY( SsData.ServerNameBuffer, computerName );
+
+ //
+ // Free the storage allocated by NetpGetComputerName.
+ //
+
+ (void) NetApiBufferFree( computerName );
+
+ //
+ // Uppercase the server name. This name is used for announcements.
+ //
+
+ {
+ UNICODE_STRING serverName;
+
+ SsData.ServerAnnounceName.Length =
+ serverName.Length =
+ (USHORT) (STRLEN( SsData.ServerNameBuffer ) * sizeof(WCHAR));
+
+ SsData.ServerAnnounceName.MaximumLength =
+ serverName.MaximumLength =
+ (USHORT) (serverName.Length + sizeof(WCHAR));
+
+ serverName.Buffer = SsData.ServerNameBuffer;
+ SsData.ServerAnnounceName.Buffer = SsData.AnnounceNameBuffer;
+
+ (VOID)RtlUpcaseUnicodeString(
+ &SsData.ServerAnnounceName,
+ &serverName,
+ FALSE
+ );
+
+ //
+ // Make the server name in Netbios format.
+ //
+
+ error = ConvertStringToTransportAddress(
+ &serverName,
+ SsServerTransportAddress,
+ &SsServerTransportAddressLength
+ );
+
+ SS_ASSERT( error == NO_ERROR );
+ }
+
+
+ IF_DEBUG(INITIALIZATION) {
+ SS_PRINT(( "SetServerName: server name set to " FORMAT_LPWSTR
+ " (could be overridden later!)\n",
+ SsData.ServerNameBuffer ));
+ }
+
+ return;
+
+} // SetServerName
+
+
+NET_API_STATUS
+TerminateServer (
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ This routine sends the FSCTL_SRV_SHUTDOWN control code to the server
+ FSD to tell it to shutdown operations.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NET_API_STATUS error = NO_ERROR;
+
+ if ( SsServerFspStarted ) {
+
+ SsServerFspStarted = FALSE;
+
+ //
+ // Send the request on to the server.
+ //
+
+ error = SsServerFsControl(
+ NULL,
+ FSCTL_SRV_SHUTDOWN,
+ NULL,
+ NULL,
+ 0
+ );
+ if ( (error != NO_ERROR) &&
+ (error != ERROR_SERVER_HAS_OPEN_HANDLES) ) {
+ IF_DEBUG(TERMINATION_ERRORS) {
+ SS_PRINT(( "TerminateServer: FSCTL_SRV_SHUTDOWN failed: %ld\n",
+ error ));
+ }
+ }
+
+ //
+ // Unload the server driver, unless there are other open handles
+ // to the server. We don't unload the driver in this case
+ // because the driver won't actually go away until the
+ // additional handles are closed, so the driver will not be
+ // fully unloaded. This would cause a subsequent server startup
+ // to fail.
+ //
+
+ if ( error != ERROR_SERVER_HAS_OPEN_HANDLES ) {
+ IF_DEBUG(TERMINATION) {
+ SS_PRINT(( "TerminateServer: Unloading server\n" ));
+ }
+ UnloadServer( );
+ }
+
+ }
+
+ //
+ // Close the handle to the server.
+ //
+
+ SsCloseServer( );
+
+ return error;
+
+} // TerminateServer
+
+
+VOID
+UnloadServer (
+ VOID
+ )
+{
+ NTSTATUS status;
+ NET_API_STATUS error;
+ LPWSTR registryPathBuffer;
+ UNICODE_STRING registryPath;
+ ULONG privileges[1];
+ LPWSTR subString[1];
+
+ registryPathBuffer = (LPWSTR)MIDL_user_allocate(
+ sizeof(SERVICE_REGISTRY_KEY) +
+ sizeof(SERVER_DRIVER_NAME)
+ );
+ if ( registryPathBuffer == NULL ) {
+ IF_DEBUG(TERMINATION_ERRORS) {
+ SS_PRINT(( "UnloadServer: Unable to allocate memory\n" ));
+ }
+ return;
+ }
+
+ privileges[0] = SE_LOAD_DRIVER_PRIVILEGE;
+
+ error = NetpGetPrivilege( 1, privileges );
+ if ( error != NO_ERROR ) {
+ IF_DEBUG(TERMINATION_ERRORS) {
+ SS_PRINT(( "UnloadServer: Unable to enable privilege: %ld\n",
+ error ));
+ }
+ MIDL_user_free( registryPathBuffer );
+ return;
+ }
+
+ wcscpy( registryPathBuffer, SERVICE_REGISTRY_KEY );
+ wcscat( registryPathBuffer, SERVER_DRIVER_NAME );
+
+ RtlInitUnicodeString( &registryPath, registryPathBuffer );
+
+ status = NtUnloadDriver( &registryPath );
+
+ MIDL_user_free( registryPathBuffer );
+
+ NetpReleasePrivilege( );
+
+ if ( !NT_SUCCESS(status) ) {
+
+ IF_DEBUG(TERMINATION_ERRORS) {
+ SS_PRINT(( "UnloadServer: Unable to unload driver: %lx\n",
+ status ));
+ }
+
+ subString[0] = SERVER_DRIVER_NAME;
+ SsLogEvent(
+ EVENT_SRV_CANT_UNLOAD_DRIVER,
+ 1,
+ subString,
+ status
+ );
+
+ }
+
+ return;
+
+} // UnloadServer
+
+
+
+VOID
+InitializeStrings(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ Retrieve internationalizable strings from NETMSG.DLL. They
+ are used for share comments for IPC$, ADMIN$, C$, etc.
+ Routine does not report any errors. If there are problems,
+ the strings will be empty ones.
+
+ FreeStrings should be called to free the memory allocated
+ by format message and the
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ DWORD dwRet, dwFlags ;
+ HMODULE hModule ;
+
+ //
+ // init the strings to the default empty remark.
+ //
+ SsAdminShareRemark = SsDefaultRemark ;
+ SsIPCShareRemark = SsDefaultRemark ;
+ SsDiskAdminShareRemark = SsDefaultRemark ;
+
+ //
+ // load NETMSG.DLL - if we cannot, just return.
+ //
+ hModule = LoadLibrary(NETMSG_DLL) ;
+ if(!hModule)
+ return ;
+
+ //
+ // hit FormatMessage 3 times for the real thing...
+ //
+ dwFlags = FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_HMODULE ;
+
+ dwRet = FormatMessage(dwFlags,
+ hModule,
+ APE2_SERVER_IPC_SHARE_REMARK,
+ 0,
+ (LPWSTR) &SsIPCShareRemark,
+ 1,
+ NULL) ;
+ if (dwRet == 0)
+ SsIPCShareRemark = SsDefaultRemark ;
+
+ dwRet = FormatMessage(dwFlags,
+ hModule,
+ APE2_SERVER_ADMIN_SHARE_REMARK,
+ 0,
+ (LPWSTR) &SsAdminShareRemark,
+ 1,
+ NULL) ;
+ if (dwRet == 0)
+ SsAdminShareRemark = SsDefaultRemark ;
+
+ dwRet = FormatMessage(dwFlags,
+ hModule,
+ APE2_SERVER_DISK_ADMIN_SHARE_REMARK,
+ 0,
+ (LPWSTR) &SsDiskAdminShareRemark,
+ 1,
+ NULL) ;
+ if (dwRet == 0)
+ SsDiskAdminShareRemark = SsDefaultRemark ;
+
+ FreeLibrary(hModule) ;
+}
+
+
+VOID
+FreeStrings(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ Free the memory used by server comment strings (allocated by
+ FormatMessage).
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ //
+ // as long as the strings do not point to the default (static data),
+ // free them.
+ //
+
+ if (SsAdminShareRemark && SsAdminShareRemark != SsDefaultRemark)
+ LocalFree(SsAdminShareRemark) ;
+ SsAdminShareRemark = SsDefaultRemark ;
+
+ if (SsIPCShareRemark && SsIPCShareRemark != SsDefaultRemark)
+ LocalFree(SsIPCShareRemark) ;
+ SsIPCShareRemark = SsDefaultRemark ;
+
+ if (SsDiskAdminShareRemark && SsDiskAdminShareRemark != SsDefaultRemark)
+ LocalFree(SsDiskAdminShareRemark) ;
+ SsDiskAdminShareRemark = SsDefaultRemark ;
+}
diff --git a/private/net/svcdlls/srvsvc/server/ssreg.h b/private/net/svcdlls/srvsvc/server/ssreg.h
new file mode 100644
index 000000000..d225b760f
--- /dev/null
+++ b/private/net/svcdlls/srvsvc/server/ssreg.h
@@ -0,0 +1,116 @@
+
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ ssreg.h
+
+Abstract:
+
+ Manifests for Registry usage in the server service.
+
+Author:
+
+ Chuck Lenzmeier (chuckl) 21-Mar-1992
+
+Revision History:
+
+--*/
+
+#ifndef _SSREG_
+#define _SSREG_
+
+//
+// Names of server service keys.
+//
+
+#define SERVER_REGISTRY_PATH L"LanmanServer"
+
+#define PARAMETERS_REGISTRY_PATH L"LanmanServer\\Parameters"
+#define AUTOTUNED_REGISTRY_PATH L"LanmanServer\\AutotunedParameters"
+#define SHARES_REGISTRY_PATH L"LanmanServer\\Shares"
+#define SHARES_SECURITY_REGISTRY_PATH L"LanmanServer\\Shares\\Security"
+#define LINKAGE_REGISTRY_PATH L"LanmanServer\\Linkage"
+
+#define BIND_VALUE_NAME L"Bind"
+#define SIZE_VALUE_NAME L"Size"
+#define DISC_VALUE_NAME L"Disc"
+#define COMMENT_VALUE_NAME L"Comment"
+#define NULL_SESSION_PIPES_VALUE_NAME L"NullSessionPipes"
+#define NULL_SESSION_SHARES_VALUE_NAME L"NullSessionShares"
+#define PIPES_NEED_LICENSE_VALUE_NAME L"PipesNeedLicense"
+#define ERROR_LOG_IGNORE_VALUE_NAME L"ErrorLogIgnore"
+#define OPTIONAL_NAMES_VALUE_NAME L"OptionalNames"
+
+#define FULL_PARAMETERS_REGISTRY_PATH L"SYSTEM\\CurrentControlSet\\Services\\" PARAMETERS_REGISTRY_PATH
+
+//
+// Names of share "environment variables".
+//
+
+#define MAXUSES_VARIABLE_NAME L"MaxUses"
+#define PATH_VARIABLE_NAME L"Path"
+#define PERMISSIONS_VARIABLE_NAME L"Permissions"
+#define REMARK_VARIABLE_NAME L"Remark"
+#define TYPE_VARIABLE_NAME L"Type"
+
+//
+// Functions exported by registry.c.
+//
+
+VOID
+SsAddParameterToRegistry (
+ PFIELD_DESCRIPTOR Field,
+ PVOID Value
+ );
+
+VOID
+SsAddShareToRegistry (
+ IN PSHARE_INFO_2 ShareInfo2,
+ IN PSECURITY_DESCRIPTOR SecurityDescriptor OPTIONAL
+ );
+
+NET_API_STATUS
+SsBindToTransports (
+ VOID
+ );
+
+NET_API_STATUS
+SsCheckRegistry (
+ VOID
+ );
+
+NET_API_STATUS
+SsEnumerateStickyShares (
+ IN OUT PSRVSVC_SHARE_ENUM_INFO ShareEnumInfo
+ );
+
+NET_API_STATUS
+SsLoadConfigurationParameters (
+ VOID
+ );
+
+NET_API_STATUS
+SsRecreateStickyShares (
+ VOID
+ );
+
+NET_API_STATUS
+SsRemoveShareFromRegistry (
+ LPTSTR NetName
+ );
+
+//
+// Functions used by registry.c that are elsewhere but there is no convenient
+// place to put the prototype
+//
+
+DWORD
+ComputeTransportAddressClippedLength(
+ IN PCHAR TransportAddress,
+ IN ULONG TransportAddressLength
+ );
+
+#endif // ndef _SSREG_
diff --git a/private/net/svcdlls/srvsvc/server/sssec.h b/private/net/svcdlls/srvsvc/server/sssec.h
new file mode 100644
index 000000000..10e4266b8
--- /dev/null
+++ b/private/net/svcdlls/srvsvc/server/sssec.h
@@ -0,0 +1,174 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ SsSec.h
+
+Abstract:
+
+ Manifests for API security in the server service.
+
+Author:
+
+ David Treadwell (davidtr) 28-Aug-1991
+
+Revision History:
+
+--*/
+
+#ifndef _SSSEC_
+#define _SSSEC_
+
+//
+// Structure that holds all security information for a single server
+// service security object.
+//
+
+typedef struct _SRVSVC_SECURITY_OBJECT {
+ LPTSTR ObjectName;
+ PGENERIC_MAPPING Mapping;
+ PSECURITY_DESCRIPTOR SecurityDescriptor;
+} SRVSVC_SECURITY_OBJECT, *PSRVSVC_SECURITY_OBJECT;
+
+//
+// Security objects used by the server service.
+//
+
+#ifdef SRV_COMM_DEVICES
+extern SRVSVC_SECURITY_OBJECT SsCharDevSecurityObject;
+#endif
+extern SRVSVC_SECURITY_OBJECT SsConfigInfoSecurityObject;
+extern SRVSVC_SECURITY_OBJECT SsConnectionSecurityObject;
+extern SRVSVC_SECURITY_OBJECT SsDiskSecurityObject;
+extern SRVSVC_SECURITY_OBJECT SsFileSecurityObject;
+extern SRVSVC_SECURITY_OBJECT SsSessionSecurityObject;
+extern SRVSVC_SECURITY_OBJECT SsShareFileSecurityObject;
+extern SRVSVC_SECURITY_OBJECT SsSharePrintSecurityObject;
+extern SRVSVC_SECURITY_OBJECT SsShareAdminSecurityObject;
+extern SRVSVC_SECURITY_OBJECT SsShareConnectSecurityObject;
+extern SRVSVC_SECURITY_OBJECT SsShareAdmConnectSecurityObject;
+extern SRVSVC_SECURITY_OBJECT SsStatisticsSecurityObject;
+
+//
+// Object type names for audit alarm tracking.
+//
+
+#ifdef SRV_COMM_DEVICES
+#define SRVSVC_CHARDEV_OBJECT TEXT( "SrvsvcCharDev" )
+#endif
+#define SRVSVC_CONFIG_INFO_OBJECT TEXT( "SrvsvcConfigInfo" )
+#define SRVSVC_CONNECTION_OBJECT TEXT( "SrvsvcConnection" )
+#define SRVSVC_DISK_OBJECT TEXT( "SrvsvcServerDiskEnum" )
+#define SRVSVC_FILE_OBJECT TEXT( "SrvsvcFile" )
+#define SRVSVC_SESSION_OBJECT TEXT( "SrvsvcSessionInfo" )
+#define SRVSVC_SHARE_FILE_OBJECT TEXT( "SrvsvcShareFileInfo" )
+#define SRVSVC_SHARE_PRINT_OBJECT TEXT( "SrvsvcSharePrintInfo" )
+#define SRVSVC_SHARE_ADMIN_OBJECT TEXT( "SrvsvcShareAdminInfo" )
+#define SRVSVC_SHARE_CONNECT_OBJECT TEXT( "SrvsvcShareConnect" )
+#define SRVSVC_SHARE_ADM_CONNECT_OBJECT TEXT( "SrvsvcShareAdminConnect" )
+#define SRVSVC_STATISTICS_OBJECT TEXT( "SrvsvcStatisticsInfo" )
+
+#ifdef SRV_COMM_DEVICES
+//
+// Access masks for character device APIs.
+//
+
+#define SRVSVC_CHARDEV_USER_INFO_GET 0x0001
+#define SRVSVC_CHARDEV_ADMIN_INFO_GET 0x0002
+#define SRVSVC_CHARDEV_INFO_SET 0x0010
+
+#define SRVSVC_CHARDEV_ALL_ACCESS ( STANDARD_RIGHTS_REQUIRED | \
+ SRVSVC_CHARDEV_USER_INFO_GET | \
+ SRVSVC_CHARDEV_ADMIN_INFO_GET | \
+ SRVSVC_CHARDEV_INFO_SET )
+#endif // def SRV_COMM_DEVICES
+
+//
+// Access masks for configuration information (NetServer{Get,Set}Info).
+//
+
+#define SRVSVC_CONFIG_USER_INFO_GET 0x0001
+#define SRVSVC_CONFIG_POWER_INFO_GET 0x0002
+#define SRVSVC_CONFIG_ADMIN_INFO_GET 0x0004
+#define SRVSVC_CONFIG_INFO_SET 0x0010
+
+#define SRVSVC_CONFIG_ALL_ACCESS ( STANDARD_RIGHTS_REQUIRED | \
+ SRVSVC_CONFIG_USER_INFO_GET | \
+ SRVSVC_CONFIG_POWER_INFO_GET | \
+ SRVSVC_CONFIG_ADMIN_INFO_GET | \
+ SRVSVC_CONFIG_INFO_SET )
+
+//
+// Access masks for connection information (NetConnectionEnum).
+//
+
+#define SRVSVC_CONNECTION_INFO_GET 0x0001
+
+#define SRVSVC_CONNECTION_ALL_ACCESS ( STANDARD_RIGHTS_REQUIRED | \
+ SRVSVC_CONNECTION_INFO_GET )
+
+//
+// Access masks for disk information (NetServerDiskEnum).
+//
+
+#define SRVSVC_DISK_ENUM 0x0001
+
+#define SRVSVC_DISK_ALL_ACCESS ( STANDARD_RIGHTS_REQUIRED | \
+ SRVSVC_DISK_ENUM )
+
+//
+// Access masks for file information (NetFileEnum, NetFileGetInfo,
+// NetFileClose).
+//
+
+#define SRVSVC_FILE_INFO_GET 0x0001
+#define SRVSVC_FILE_CLOSE 0x0010
+
+#define SRVSVC_FILE_ALL_ACCESS ( STANDARD_RIGHTS_REQUIRED | \
+ SRVSVC_FILE_INFO_GET | \
+ SRVSVC_FILE_CLOSE )
+
+//
+// Access masks for session information (NetSessionEnum,
+// NetSessionGetInfo, NetSessionDel).
+//
+
+#define SRVSVC_SESSION_USER_INFO_GET 0x0001
+#define SRVSVC_SESSION_ADMIN_INFO_GET 0x0002
+#define SRVSVC_SESSION_DELETE 0x0010
+
+#define SRVSVC_SESSION_ALL_ACCESS ( STANDARD_RIGHTS_REQUIRED | \
+ SRVSVC_SESSION_USER_INFO_GET | \
+ SRVSVC_SESSION_ADMIN_INFO_GET | \
+ SRVSVC_SESSION_DELETE )
+
+//
+// Access masks for share information (NetShareAdd, NetShareDel,
+// NetShareEnum, NetShareGetInfo, NetShareCheck, NetShareSetInfo).
+//
+// Access masks for connecting to shares are defined in srvfsctl.h,
+// since they must be shared between the server and server service.
+//
+
+#define SRVSVC_SHARE_USER_INFO_GET 0x0001
+#define SRVSVC_SHARE_ADMIN_INFO_GET 0x0002
+#define SRVSVC_SHARE_INFO_SET 0x0010
+
+#define SRVSVC_SHARE_ALL_ACCESS ( STANDARD_RIGHTS_REQUIRED | \
+ SRVSVC_SHARE_USER_INFO_GET | \
+ SRVSVC_SHARE_ADMIN_INFO_GET | \
+ SRVSVC_SHARE_INFO_SET )
+
+//
+// Access masks for statistics information (NetStatisticsGet,
+// NetStatisticsClear).
+//
+
+#define SRVSVC_STATISTICS_GET 0x0001
+
+#define SRVSVC_STATISTICS_ALL_ACCESS ( STANDARD_RIGHTS_REQUIRED | \
+ SRVSVC_STATISTICS_GET )
+
+#endif // _SSSEC_
diff --git a/private/net/svcdlls/srvsvc/server/sssubs.c b/private/net/svcdlls/srvsvc/server/sssubs.c
new file mode 100644
index 000000000..71e82f239
--- /dev/null
+++ b/private/net/svcdlls/srvsvc/server/sssubs.c
@@ -0,0 +1,1064 @@
+/*++
+
+Copyright (c) 1991-1992 Microsoft Corporation
+
+Module Name:
+
+ SsSubs.c
+
+Abstract:
+
+ This module contains support routines for the NT server service.
+
+Author:
+
+ David Treadwell (davidtr) 10-Jan-1991
+
+Revision History:
+
+--*/
+
+#include "srvsvcp.h"
+#include "ssdata.h"
+#include "ssreg.h"
+
+#include <lmerr.h>
+#include <lmsname.h>
+#include <netlibnt.h>
+#include <tstr.h>
+
+#include <ctype.h>
+#include <stdarg.h>
+#include <stdio.h>
+
+
+PSERVER_REQUEST_PACKET
+SsAllocateSrp (
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ This routine allocates a serer request packet so that an API can
+ communicate with the kernel-mode server. Any general initialization
+ in performed here.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ PSERVER_REQUEST_PACKET - a pointer to the allocated SRP.
+
+--*/
+
+{
+ PSERVER_REQUEST_PACKET srp;
+
+ srp = MIDL_user_allocate( sizeof(SERVER_REQUEST_PACKET) );
+ if ( srp != NULL ) {
+ RtlZeroMemory( srp, sizeof(SERVER_REQUEST_PACKET) );
+ }
+
+ return srp;
+
+} // SsAllocateSrp
+
+#if DBG
+
+VOID
+SsAssert(
+ IN PVOID FailedAssertion,
+ IN PVOID FileName,
+ IN ULONG LineNumber
+ )
+{
+ BOOL ok;
+ CHAR choice[16];
+ DWORD bytes;
+ DWORD error;
+
+ SsPrintf( "\nAssertion failed: %s\n at line %ld of %s\n",
+ FailedAssertion, LineNumber, FileName );
+ do {
+ SsPrintf( "Break or Ignore [bi]? " );
+ bytes = sizeof(choice);
+ ok = ReadFile(
+ GetStdHandle(STD_INPUT_HANDLE),
+ &choice,
+ bytes,
+ &bytes,
+ NULL
+ );
+ if ( ok ) {
+ if ( toupper(choice[0]) == 'I' ) {
+ break;
+ }
+ if ( toupper(choice[0]) == 'B' ) {
+ DbgUserBreakPoint( );
+ }
+ } else {
+ error = GetLastError( );
+ }
+ } while ( TRUE );
+
+ return;
+
+} // SsAssert
+#endif
+
+
+VOID
+SsCloseServer (
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ This routine closes the server file system device, if it has been
+ opened.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ //
+ // Close the server device, if it has been opened.
+ //
+
+ if ( SsServerDeviceHandle != NULL ) {
+ NtClose( SsServerDeviceHandle );
+ SsServerDeviceHandle = NULL;
+ }
+
+} // SsCloseServer
+
+
+VOID
+SsControlCHandler (
+ IN ULONG CtrlType
+ )
+
+/*++
+
+Routine Description:
+
+ Captures and ignores a kill signal. Without this, any ^C pressed in
+ the window that started the server service will result in this
+ process being killed, and then the server can't function properly.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ CtrlType;
+
+ return;
+
+} // SsControlCHandler
+
+
+VOID
+SsFreeSrp (
+ IN PSERVER_REQUEST_PACKET Srp
+ )
+
+/*++
+
+Routine Description:
+
+ Frees an SRP allocated by SsAllocateSrp.
+
+Arguments:
+
+ Srp - a pointer to the SRP to free.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ MIDL_user_free( Srp );
+
+} // SsFreeSrp
+
+
+VOID
+SsLogEvent(
+ IN DWORD MessageId,
+ IN DWORD NumberOfSubStrings,
+ IN LPWSTR *SubStrings,
+ IN DWORD ErrorCode
+ )
+{
+ HANDLE logHandle;
+ DWORD dataSize = 0;
+ LPVOID rawData = NULL;
+
+ logHandle = RegisterEventSource(
+ NULL,
+ SERVER_DISPLAY_NAME
+ );
+
+ if ( logHandle == NULL ) {
+ SS_PRINT(( "SRVSVC: RegisterEventSource failed: %lu\n",
+ GetLastError() ));
+ return;
+ }
+
+ if ( ErrorCode != NERR_Success ) {
+
+ //
+ // An error code was specified.
+ //
+
+ dataSize = sizeof(ErrorCode);
+ rawData = (LPVOID)&ErrorCode;
+
+ }
+
+ //
+ // Log the error.
+ //
+
+ if ( !ReportEventW(
+ logHandle,
+ EVENTLOG_ERROR_TYPE,
+ 0, // event category
+ MessageId,
+ NULL, // user SID
+ (WORD)NumberOfSubStrings,
+ dataSize,
+ SubStrings,
+ rawData
+ ) ) {
+ SS_PRINT(( "SRVSVC: ReportEvent failed: %lu\n",
+ GetLastError() ));
+ }
+
+ if ( !DeregisterEventSource( logHandle ) ) {
+ SS_PRINT(( "SRVSVC: DeregisterEventSource failed: %lu\n",
+ GetLastError() ));
+ }
+
+ return;
+
+} // SsLogEvent
+
+
+NET_API_STATUS
+SsOpenServer (
+ PHANDLE pHandle OPTIONAL
+ )
+
+/*++
+
+Routine Description:
+
+ This routine opens the server file system device, allowing the
+ server service to send FS controls to it.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NET_API_STATUS - results of operation.
+
+--*/
+
+{
+ NTSTATUS status;
+ UNICODE_STRING unicodeServerName;
+ OBJECT_ATTRIBUTES objectAttributes;
+ IO_STATUS_BLOCK ioStatusBlock;
+
+ if( !ARGUMENT_PRESENT( pHandle ) ) {
+ SS_ASSERT( SsServerDeviceHandle == NULL );
+ pHandle = &SsServerDeviceHandle;
+ }
+
+
+ //
+ // Open the server device.
+ //
+
+ RtlInitUnicodeString( &unicodeServerName, SERVER_DEVICE_NAME );
+
+ InitializeObjectAttributes(
+ &objectAttributes,
+ &unicodeServerName,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL
+ );
+
+ //
+ // Opening the server with desired access = SYNCHRONIZE and open
+ // options = FILE_SYNCHRONOUS_IO_NONALERT means that we don't have
+ // to worry about waiting for the NtFsControlFile to complete--this
+ // makes all IO system calls that use this handle synchronous.
+ //
+
+ status = NtOpenFile(
+ pHandle,
+ SYNCHRONIZE,
+ &objectAttributes,
+ &ioStatusBlock,
+ 0,
+ FILE_SYNCHRONOUS_IO_NONALERT
+ );
+
+ if ( NT_SUCCESS(status) ) {
+ status = ioStatusBlock.Status;
+ }
+
+ if ( !NT_SUCCESS(status) ) {
+ IF_DEBUG(INITIALIZATION_ERRORS) {
+ SS_PRINT(( "SsOpenServer: NtOpenFile (server device object) "
+ "failed: %X\n", status ));
+ }
+ return NetpNtStatusToApiStatus( status );
+ }
+
+ //
+ // We're now ready to talk to the server.
+ //
+
+ return NO_ERROR;
+
+} // SsOpenServer
+
+#if DBG
+
+VOID
+SsPrintf (
+ char *Format,
+ ...
+ )
+
+{
+ va_list arglist;
+ char OutputBuffer[1024];
+ ULONG length;
+
+ va_start( arglist, Format );
+
+ vsprintf( OutputBuffer, Format, arglist );
+
+ va_end( arglist );
+
+ length = strlen( OutputBuffer );
+
+ WriteFile( GetStdHandle(STD_OUTPUT_HANDLE), (LPVOID )OutputBuffer, length, &length, NULL );
+
+} // SsPrintf
+#endif
+
+
+NET_API_STATUS
+SsServerFsControlGetInfo (
+ IN ULONG ServerControlCode,
+ IN PSERVER_REQUEST_PACKET Srp,
+ IN OUT PVOID *OutputBuffer,
+ IN ULONG PreferredMaximumLength
+ )
+
+/*++
+
+Routine Description:
+
+ This routine sends an SRP to the server for an API that retrieves
+ information from the server and takes a PreferredMaximumLength
+ parameter.
+
+Arguments:
+
+ ServerControlCode - the FSCTL code for the operation.
+
+ Srp - a pointer to the SRP for the operation.
+
+ OutputBuffer - a pointer to receive a pointer to the buffer
+ allocated by this routine to hold the output information.
+
+ PreferredMaximumLength - the PreferredMaximumLength parameter.
+
+Return Value:
+
+ NET_API_STATUS - results of operation.
+
+--*/
+
+{
+ NET_API_STATUS status = STATUS_SUCCESS;
+ ULONG resumeHandle = Srp->Parameters.Get.ResumeHandle;
+ ULONG BufLen = min( INITIAL_BUFFER_SIZE, PreferredMaximumLength );
+ ULONG i;
+
+ *OutputBuffer = NULL;
+
+ //
+ // Normally, we should only go through this loop at most 2 times. But
+ // if the amount of data the server needs to return is growing, then
+ // we might be forced to run through it a couple of more times. '5'
+ // is an arbitrary number just to ensure we don't get stuck.
+ //
+
+ for( i=0; i < 5; i++ ) {
+
+ if( *OutputBuffer ) {
+ MIDL_user_free( *OutputBuffer );
+ }
+
+ *OutputBuffer = MIDL_user_allocate( BufLen );
+
+ if( *OutputBuffer == NULL ) {
+ status = ERROR_NOT_ENOUGH_MEMORY;
+ break;
+ }
+
+ //
+ // Make the request of the server.
+ //
+
+ Srp->Parameters.Get.ResumeHandle = resumeHandle;
+
+ status = SsServerFsControl(
+ NULL,
+ ServerControlCode,
+ Srp,
+ *OutputBuffer,
+ BufLen
+ );
+
+ //
+ // If we were successful, or we got an error other than our buffer
+ // being too small, break out.
+ //
+ if ( status != ERROR_MORE_DATA && status != NERR_BufTooSmall ) {
+ break;
+ }
+
+ //
+ // We've been told that our buffer isn't big enough. But if we've hit
+ // the caller's PreferredMaximumLength, break out.
+ //
+ if( BufLen >= PreferredMaximumLength ) {
+ break;
+ }
+
+ //
+ // Let's try again. EXTRA_ALLOCATION is here to cover the case where the
+ // amount of space required grows between the previous FsControl call and
+ // the next one.
+ //
+ BufLen = min( Srp->Parameters.Get.TotalBytesNeeded + EXTRA_ALLOCATION,
+ PreferredMaximumLength );
+
+ }
+
+ if ( *OutputBuffer && Srp->Parameters.Get.EntriesRead == 0 ) {
+ MIDL_user_free( *OutputBuffer );
+ *OutputBuffer = NULL;
+ }
+
+ return status;
+
+} // SsServerFsControlGetInfo
+
+
+NET_API_STATUS
+SsServerFsControl (
+ IN HANDLE Handle OPTIONAL,
+ IN ULONG ServerControlCode,
+ IN PSERVER_REQUEST_PACKET Srp OPTIONAL,
+ IN PVOID Buffer,
+ IN ULONG BufferLength
+ )
+
+/*++
+
+Routine Description:
+
+ This routine sends an FSCTL to the server. It first opens the server
+ device object, sends the FSCTL, then closes the server handle.
+
+Arguments:
+
+ ServerControlCode - the FSCTL code to send to the server.
+
+ Srp - a pointer to the SRP for the operation.
+
+ Buffer - a pointer to the buffer to pass to the server as the
+ "OutputBuffer" parameter of NtFsControlFile.
+
+ BufferLength - the size of this buffer.
+
+Return Value:
+
+ NET_API_STATUS - results of the operation.
+
+--*/
+
+{
+ NTSTATUS status;
+ NET_API_STATUS error;
+ IO_STATUS_BLOCK ioStatusBlock;
+ PSERVER_REQUEST_PACKET sendSrp;
+ ULONG sendSrpLength;
+ PWCH name1Buffer, name2Buffer;
+
+ if( !ARGUMENT_PRESENT( Handle ) ) {
+ SS_ASSERT( SsServerDeviceHandle != NULL );
+ Handle = SsServerDeviceHandle;
+ }
+
+
+ //
+ // If a name was specified, we must capture the SRP along with the
+ // name in order to avoid sending embedded input pointers.
+ //
+
+ if ( Srp != NULL ) {
+
+ name1Buffer = Srp->Name1.Buffer;
+ name2Buffer = Srp->Name2.Buffer;
+
+ if ( Srp->Name1.Buffer != NULL || Srp->Name2.Buffer != NULL ) {
+
+ PCHAR nextStringLocation;
+
+ //
+ // Allocate enough space to hold the SRP + name.
+ //
+
+ sendSrpLength = sizeof(SERVER_REQUEST_PACKET);
+
+ if ( Srp->Name1.Buffer != NULL ) {
+ sendSrpLength += Srp->Name1.MaximumLength;
+ }
+
+ if ( Srp->Name2.Buffer != NULL ) {
+ sendSrpLength += Srp->Name2.MaximumLength;
+ }
+
+ sendSrp = MIDL_user_allocate( sendSrpLength );
+
+ if ( sendSrp == NULL ) {
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ //
+ // Copy over the SRP.
+ //
+
+ RtlCopyMemory( sendSrp, Srp, sizeof(SERVER_REQUEST_PACKET) );
+
+ //
+ // Set up the names in the new SRP.
+ //
+
+ nextStringLocation = (PCHAR)( sendSrp + 1 );
+
+ if ( Srp->Name1.Buffer != NULL ) {
+
+ sendSrp->Name1.Length = Srp->Name1.Length;
+ sendSrp->Name1.MaximumLength = Srp->Name1.MaximumLength;
+ sendSrp->Name1.Buffer = (PWCH)nextStringLocation;
+
+ RtlCopyMemory(
+ sendSrp->Name1.Buffer,
+ Srp->Name1.Buffer,
+ Srp->Name1.MaximumLength
+ );
+
+ nextStringLocation += Srp->Name1.MaximumLength;
+
+ POINTER_TO_OFFSET( sendSrp->Name1.Buffer, sendSrp );
+ }
+
+ if ( Srp->Name2.Buffer != NULL ) {
+
+ sendSrp->Name2.Length = Srp->Name2.Length;
+ sendSrp->Name2.MaximumLength = Srp->Name2.MaximumLength;
+ sendSrp->Name2.Buffer = (PWCH)nextStringLocation;
+
+ RtlCopyMemory(
+ sendSrp->Name2.Buffer,
+ Srp->Name2.Buffer,
+ Srp->Name2.MaximumLength
+ );
+
+ POINTER_TO_OFFSET( sendSrp->Name2.Buffer, sendSrp );
+ }
+
+ } else {
+
+ //
+ // There was no name in the SRP, so just send the SRP that was
+ // passed in.
+ //
+
+ sendSrp = Srp;
+ sendSrpLength = sizeof(SERVER_REQUEST_PACKET);
+ }
+
+ } else {
+
+ //
+ // This request has no SRP.
+ //
+
+ sendSrp = NULL;
+ sendSrpLength = 0;
+
+ }
+
+ //
+ // Send the request to the server FSD.
+ //
+
+ status = NtFsControlFile(
+ Handle,
+ NULL,
+ NULL,
+ NULL,
+ &ioStatusBlock,
+ ServerControlCode,
+ sendSrp,
+ sendSrpLength,
+ Buffer,
+ BufferLength
+ );
+
+ //
+ // If an error code was set in the SRP, use it. Otherwise, if
+ // an error was returned or set in the IO status block, use that.
+ //
+
+ if ( (sendSrp != NULL) && (sendSrp->ErrorCode != NO_ERROR) ) {
+ error = sendSrp->ErrorCode;
+ IF_DEBUG(API_ERRORS) {
+ SS_PRINT(( "SsServerFsControl: (1) API call %lx to srv failed, "
+ "err = %ld\n", ServerControlCode, error ));
+ }
+ } else {
+ if ( NT_SUCCESS(status) ) {
+ status = ioStatusBlock.Status;
+ }
+ if ( status == STATUS_SERVER_HAS_OPEN_HANDLES ) {
+ error = ERROR_SERVER_HAS_OPEN_HANDLES;
+ } else {
+ error = NetpNtStatusToApiStatus( status );
+ }
+ if ( error != NO_ERROR ) {
+ IF_DEBUG(API_ERRORS) {
+ SS_PRINT(( "SsServerFsControl: (2) API call %lx to srv "
+ "failed, err = %ld, status = %X\n",
+ ServerControlCode, error, status ));
+ }
+ }
+ }
+
+ //
+ // If a separate buffer was allocated to capture the name, copy
+ // over the new SRP and free it.
+ //
+
+ if ( sendSrp != Srp ) {
+ RtlCopyMemory( Srp, sendSrp, sizeof(SERVER_REQUEST_PACKET) );
+ Srp->Name1.Buffer = name1Buffer;
+ Srp->Name2.Buffer = name2Buffer;
+ MIDL_user_free( sendSrp );
+ }
+
+ return error;
+
+} // SsServerFsControl
+
+
+DWORD
+SsGetServerType (
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ Return the ServiceBits of all the services implemented by this service.
+
+ Enter with SsServerInfoResource locked.
+
+Arguments:
+
+ None
+
+Return Value:
+
+ SV_TYPE service bits.
+
+--*/
+{
+ DWORD serviceBits;
+
+ serviceBits = SV_TYPE_SERVER | SV_TYPE_NT;
+
+ if( SsData.IsDfsRoot ) {
+ serviceBits |= SV_TYPE_DFS;
+ }
+
+ if ( SsData.ServerInfo599.sv599_timesource ) {
+ serviceBits |= SV_TYPE_TIME_SOURCE;
+ }
+
+ if ( SsData.ServerInfo598.sv598_producttype == NtProductServer ) {
+ serviceBits |= SV_TYPE_SERVER_NT;
+ }
+
+ if ( SsData.NumberOfPrintShares != 0 ) {
+ serviceBits |= SV_TYPE_PRINTQ_SERVER;
+ }
+
+ return serviceBits;
+
+}
+
+
+VOID
+SsSetExportedServerType (
+ IN PNAME_LIST_ENTRY service OPTIONAL,
+ IN BOOL ExternalBitsAlreadyChanged,
+ IN BOOL UpdateImmediately
+ )
+{
+ DWORD serviceBits;
+ DWORD newServiceBits;
+ BOOL changed = ExternalBitsAlreadyChanged;
+
+ //
+ // The value returned in the sv102_type field is an amalgam of the
+ // following:
+ //
+ // 1) The internal server type bits SV_TYPE_SERVER (always set),
+ // SV_TYPE_NT (always set), SV_TYPE_TIME_SOURCE (set if the
+ // parameter TimeSource is TRUE), and SV_TYPE_PRINTQ_SERVER (set
+ // if there are any print shares).
+ //
+ // 2) SV_TYPE_DFS if this machine is the root of a DFS tree
+ //
+ // 3) The bits set by the service controller calling I_NetServerSetServiceBits.
+ //
+ // 4) The logical OR of all per-transport server type bits set by
+ // the Browser calling I_NetServerSetServiceBits.
+ //
+
+ (VOID)RtlAcquireResourceExclusive( &SsServerInfoResource, TRUE );
+
+ serviceBits = SsGetServerType();
+
+ if( ARGUMENT_PRESENT( service ) ) {
+ //
+ // Change the bits for the passed-in NAME_LIST_ENTRY only
+ //
+
+ newServiceBits = service->ServiceBits;
+ newServiceBits &= ~(SV_TYPE_TIME_SOURCE | SV_TYPE_SERVER_NT | SV_TYPE_PRINTQ_SERVER | SV_TYPE_DFS);
+ newServiceBits |= serviceBits;
+
+ if( service->ServiceBits != newServiceBits ) {
+ service->ServiceBits |= newServiceBits;
+ changed = TRUE;
+ }
+
+ } else {
+ //
+ // Change the bits for each NAME_LIST_ENTRY
+ //
+ for ( service = SsServerNameList; service != NULL; service = service->Next ) {
+
+ newServiceBits = service->ServiceBits;
+ newServiceBits &= ~(SV_TYPE_TIME_SOURCE | SV_TYPE_SERVER_NT | SV_TYPE_PRINTQ_SERVER | SV_TYPE_DFS );
+ newServiceBits |= serviceBits;
+
+ if( service->ServiceBits != newServiceBits ) {
+ service->ServiceBits |= newServiceBits;
+ changed = TRUE;
+ }
+ }
+ }
+
+ RtlReleaseResource( &SsServerInfoResource );
+
+ if ( changed && UpdateImmediately ) {
+ if ( !SetEvent( SsStatusChangedEvent ) ) {
+ SS_PRINT(( "SsSetExportedServerType: SetEvent failed: %ld\n",
+ GetLastError( ) ));
+ }
+ }
+
+ return;
+
+} // SsSetExportedServerType
+
+
+NET_API_STATUS
+SsSetField (
+ IN PFIELD_DESCRIPTOR Field,
+ IN PVOID Value,
+ IN BOOLEAN WriteToRegistry,
+ OUT BOOLEAN *AnnouncementInformationChanged OPTIONAL
+ )
+{
+ PCHAR structure;
+
+ //
+ // *** We do not initialize *AnnouncementInformationChanged to
+ // FALSE! We leave it alone, unless interesting information is
+ // changed, in which case we set it to TRUE. This is to allow a
+ // caller to initialize it itself, then call this function
+ // multiple times, with the resulting value in the parameter
+ // being TRUE if at least one of the calls changed an
+ // interesting parameter.
+ //
+
+ //
+ // Determine the structure that will be set.
+ //
+
+ if ( Field->Level / 100 == 5 ) {
+ if ( Field->Level != 598 ) {
+ structure = (PCHAR)&SsData.ServerInfo599;
+ } else {
+ structure = (PCHAR)&SsData.ServerInfo598;
+ }
+ } else {
+ structure = (PCHAR)&SsData.ServerInfo102;
+ }
+
+ //
+ // Set the value in the field based on the field type.
+ //
+
+ switch ( Field->FieldType ) {
+
+ case BOOLEAN_FIELD: {
+
+ BOOLEAN value = *(PBOOLEAN)Value;
+ PBOOLEAN valueLocation;
+
+ //
+ // BOOLEANs may only be TRUE (1) or FALSE (0).
+ //
+
+ if ( value != TRUE && value != FALSE ) {
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ valueLocation = (PBOOLEAN)( structure + Field->FieldOffset );
+
+ //
+ // If we're turning off Hidden (i.e., making the server public),
+ // indicate that an announcment-related parameter has changed.
+ // This will cause an announcement to be sent immediately.
+ //
+
+ if ( (Field->FieldOffset ==
+ FIELD_OFFSET( SERVER_INFO_102, sv102_hidden )) &&
+ (value && !(*valueLocation)) &&
+ (ARGUMENT_PRESENT(AnnouncementInformationChanged)) ) {
+ *AnnouncementInformationChanged = TRUE;
+ }
+
+ *valueLocation = value;
+
+ break;
+ }
+
+ case DWORD_FIELD: {
+
+ DWORD value = *(PDWORD)Value;
+ PDWORD valueLocation;
+
+ //
+ // Make sure that the specified value is in the range of
+ // legal values for the Field.
+ //
+
+ if ( value > Field->MaximumValue || value < Field->MinimumValue ) {
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ valueLocation = (PDWORD)( structure + Field->FieldOffset );
+ *valueLocation = value;
+
+ break;
+ }
+
+ case LPSTR_FIELD: {
+
+ LPWCH value = *(LPWCH *)Value;
+ LPWSTR valueLocation;
+ ULONG maxLength;
+
+ //
+ // We are setting the name, comment, or userpath for the server.
+ // Use the field offset to determine which.
+ //
+
+ if ( Field->FieldOffset ==
+ FIELD_OFFSET( SERVER_INFO_102, sv102_name ) ) {
+ valueLocation = SsData.ServerNameBuffer;
+ maxLength = sizeof( SsServerTransportAddress );
+ } else if ( Field->FieldOffset ==
+ FIELD_OFFSET( SERVER_INFO_102, sv102_comment ) ) {
+ valueLocation = SsData.ServerCommentBuffer;
+ maxLength = MAXCOMMENTSZ;
+ } else if ( Field->FieldOffset ==
+ FIELD_OFFSET( SERVER_INFO_102, sv102_userpath ) ) {
+ valueLocation = SsData.UserPathBuffer;
+ maxLength = MAX_PATH;
+ } else if ( Field->FieldOffset ==
+ FIELD_OFFSET( SERVER_INFO_599, sv599_domain ) ) {
+ valueLocation = SsData.DomainNameBuffer;
+ maxLength = DNLEN;
+ } else {
+ SS_ASSERT( FALSE );
+ }
+
+ //
+ // If the string is too long, return an error.
+ //
+
+ if ( (value != NULL) && (STRLEN(value) > maxLength) ) {
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ //
+ // If we're changing the server comment, indicate that an
+ // announcment-related parameter has changed. This will cause
+ // an announcement to be sent immediately.
+ //
+
+ if ( (Field->FieldOffset ==
+ FIELD_OFFSET( SERVER_INFO_102, sv102_comment )) &&
+ ( ((value == NULL) && (*valueLocation != '\0')) ||
+ ((value != NULL) && (wcscmp(value,valueLocation) != 0)) ) &&
+ (ARGUMENT_PRESENT(AnnouncementInformationChanged)) ) {
+ *AnnouncementInformationChanged = TRUE;
+ }
+
+ //
+ // If the input is NULL, make the string zero length.
+ //
+
+ if ( value == NULL ) {
+
+ *valueLocation = '\0';
+ *(valueLocation+1) = '\0';
+
+ } else {
+
+ wcscpy( valueLocation, value );
+
+ }
+
+ break;
+ }
+
+ } // end switch
+
+ //
+ // The change worked. If requested, add the parameter to the
+ // registry, thus effecting a sticky change. Don't write it
+ // to the registry if this is xxx_comment or xxx_disc since
+ // we already write out their more well known aliases
+ // srvcomment and autodisconnect. Changes here should also be
+ // made to SetStickyParameters().
+ //
+
+ if ( WriteToRegistry &&
+ (_wcsicmp( Field->FieldName, DISC_VALUE_NAME ) != 0) &&
+ (_wcsicmp( Field->FieldName, COMMENT_VALUE_NAME ) != 0) ) {
+
+ SsAddParameterToRegistry( Field, Value );
+ }
+
+ return NO_ERROR;
+
+} // SsSetField
+
+UINT
+SsGetDriveType (
+ IN LPWSTR path
+)
+/*++
+
+Routine Description:
+
+ This routine calls GetDriveType, attempting to eliminate
+ the DRIVE_NO_ROOT_DIR type
+
+Arguments:
+
+ A path
+
+Return Value:
+
+ The drive type
+
+--*/
+{
+ UINT driveType = GetDriveType( path );
+
+ if( driveType == DRIVE_NO_ROOT_DIR ) {
+
+ if( path[0] != UNICODE_NULL && path[1] == L':' ) {
+
+ WCHAR shortPath[ 4 ];
+
+ shortPath[0] = path[0];
+ shortPath[1] = L':';
+ shortPath[2] = L'\\';
+ shortPath[3] = L'\0';
+
+ driveType = GetDriveType( shortPath );
+
+ } else {
+
+ ULONG len = wcslen( path );
+ LPWSTR pathWithSlash = MIDL_user_allocate( (len + 2) * sizeof( *path ) );
+
+ if( pathWithSlash != NULL ) {
+ RtlCopyMemory( pathWithSlash, path, len * sizeof( *path ) );
+ pathWithSlash[ len ] = L'\\';
+ pathWithSlash[ len+1 ] = L'\0';
+ driveType = GetDriveType( pathWithSlash );
+ MIDL_user_free( pathWithSlash );
+ }
+ }
+ }
+
+ return driveType;
+}
diff --git a/private/net/svcdlls/srvsvc/server/stats.c b/private/net/svcdlls/srvsvc/server/stats.c
new file mode 100644
index 000000000..06a9d5211
--- /dev/null
+++ b/private/net/svcdlls/srvsvc/server/stats.c
@@ -0,0 +1,113 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ stats.c
+
+Abstract:
+
+ This module contains support for the NetStatisticsGet API for the NT
+ OS/2 server service.
+
+Author:
+
+ David Treadwell (davidtr) 12-Apr-1991
+
+Revision History:
+
+--*/
+
+#include "srvsvcp.h"
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetrServerStatisticsGet (
+ IN LPTSTR ServerName,
+ IN LPTSTR Service,
+ IN DWORD Level,
+ IN DWORD Options,
+ OUT LPSTAT_SERVER_0 *InfoStruct
+ )
+
+/*++
+
+Routine Description:
+
+ This routine communicates with the server FSD to implement the
+ server half of the NetStatisticsGet function.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NET_API_STATUS - NO_ERROR or reason for failure.
+
+--*/
+
+{
+ NET_API_STATUS error;
+ PSERVER_REQUEST_PACKET srp;
+
+ ServerName, Service;
+
+ //
+ // The only valid level is 0.
+ //
+
+ if ( Level != 0 ) {
+ return ERROR_INVALID_LEVEL;
+ }
+
+ //
+ // No options supported.
+ //
+
+ if ( Options != 0 ) {
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ //
+ // Make sure that the caller has the access necessary for this
+ // operation.
+ //
+
+ error = SsCheckAccess(
+ &SsStatisticsSecurityObject,
+ SRVSVC_STATISTICS_GET
+ );
+
+ if ( error != NO_ERROR ) {
+ return ERROR_ACCESS_DENIED;
+ }
+
+ //
+ // Set up the request packet.
+ //
+
+ srp = SsAllocateSrp( );
+ if ( srp == NULL ) {
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ //
+ // Send the request to the server.
+ //
+
+ error = SsServerFsControlGetInfo(
+ FSCTL_SRV_NET_STATISTICS_GET,
+ srp,
+ (PVOID *)InfoStruct,
+ (ULONG)-1
+ );
+
+ SsFreeSrp( srp );
+
+ return error;
+
+} // NetrServerStatisticsGet
+
+
diff --git a/private/net/svcdlls/srvsvc/server/tod.c b/private/net/svcdlls/srvsvc/server/tod.c
new file mode 100644
index 000000000..769ca36e5
--- /dev/null
+++ b/private/net/svcdlls/srvsvc/server/tod.c
@@ -0,0 +1,204 @@
+/*++
+
+Copyright (c) 1991-1993 Microsoft Corporation
+
+Module Name:
+
+ tod.c
+
+Abstract:
+
+ This module contains the server end of the NetRemoteTOD API.
+
+
+Author:
+
+ Rajen Shah (rajens) 02-Apr-1991
+
+[Environment:]
+
+ User Mode - Mixed NT and Win32
+
+Revision History:
+
+ 02-Apr-1991 RajenS
+ Created
+ 02-Mar-1992 JohnRo
+ Disable DbgPrints for normal APIs (caused loss of elapsed time!)
+ 08-Apr-1992 ChuckL
+ Moved into server service
+ 10-Jun-1993 JohnRo
+ RAID 13081: NetRemoteTOD should return timezone info.
+ 16-Jun-1993 JohnRo
+ RAID 13080: Fix GP fault if MIDL_user_allocate returns NULL ptr or
+ caller passes us NULL ptr.
+
+--*/
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+
+#include <windows.h>
+#include <lmcons.h>
+#include <netlibnt.h>
+#include <lmremutl.h>
+#include <rpcutil.h>
+#include <ssdebug.h> // SS_PRINT() macro.
+#include <timelib.h>
+
+//
+// Some defines that are used as defaults.
+// BUGBUG These should go into a common file, e.g. lmcons.h
+//
+#define TOD_DEFAULT_INTERVAL 310 // 310-milisecond interval
+
+
+
+NTSTATUS
+timesvc_RemoteTimeOfDay(
+ OUT LPTIME_OF_DAY_INFO *lpTimeOfDayInfo
+ )
+
+/*++
+
+Routine Description:
+
+ This routine calls the Win32 and NT base timer APIs to get the
+ relevant time/date information. It also calls the Rtl routine to
+ convert the time elapsed since 1-1-1970.
+
+ The routine allocates a buffer to contain the time of day information
+ and returns a pointer to that buffer to the caller.
+
+Arguments:
+
+ lpTimeOfDayInfo - Location of where to place pointer to buffer.
+
+Return Value:
+
+ NTSTATUS - STATUS_SUCCESS or reason for failure.
+
+--*/
+
+{
+ SYSTEMTIME SystemTime;
+ LARGE_INTEGER Time;
+ DWORD TickCount;
+ LPTIME_OF_DAY_INFO lpTimeOfDay;
+ LONG LocalTimeZoneOffsetSecs; // offset (+ for West of GMT, etc).
+
+ if (lpTimeOfDayInfo == NULL) {
+ return (STATUS_INVALID_PARAMETER);
+ }
+
+ //
+ // Call the appropriate routines to collect the time information
+ //
+
+ GetSystemTime(&SystemTime);
+
+ //
+ // Get number of seconds from UTC. Positive values for west of Greenwich,
+ // negative values for east of Greenwich.
+ //
+ LocalTimeZoneOffsetSecs = NetpLocalTimeZoneOffset();
+
+ //
+ // Allocate a TimeOfDay_INFO structure that is to be returned to the
+ // caller and fill it with the relevant data.
+ //
+
+ *lpTimeOfDayInfo = (TIME_OF_DAY_INFO *) MIDL_user_allocate(
+ sizeof (struct _TIME_OF_DAY_INFO)
+ );
+
+ if (*lpTimeOfDayInfo == NULL) {
+ SS_PRINT((
+ "SRVSVC: timesvc_RemoteTimeOfDay"
+ "got NULL from MIDL_user_allocate!\n" ));
+ return(STATUS_NO_MEMORY);
+ }
+
+ lpTimeOfDay = (LPTIME_OF_DAY_INFO)(*lpTimeOfDayInfo);
+
+ lpTimeOfDay->tod_hours = SystemTime.wHour;
+ lpTimeOfDay->tod_mins = SystemTime.wMinute;
+ lpTimeOfDay->tod_secs = SystemTime.wSecond;
+ lpTimeOfDay->tod_hunds = SystemTime.wMilliseconds/10;
+ lpTimeOfDay->tod_tinterval = TOD_DEFAULT_INTERVAL;
+ lpTimeOfDay->tod_day = SystemTime.wDay;
+ lpTimeOfDay->tod_month = SystemTime.wMonth;
+ lpTimeOfDay->tod_year = SystemTime.wYear;
+ lpTimeOfDay->tod_weekday = SystemTime.wDayOfWeek;
+
+ // tod_timezone is + for west of GMT, - for east of it.
+ // tod_timezone is in minutes.
+ lpTimeOfDay->tod_timezone = LocalTimeZoneOffsetSecs / 60;
+
+ // Get the 64-bit system time.
+ // Convert the system time to the number of miliseconds
+ // since 1-1-1970.
+ //
+
+ NtQuerySystemTime(&Time);
+ RtlTimeToSecondsSince1970(&Time,
+ &(lpTimeOfDay->tod_elapsedt)
+ );
+
+ // Get the free running counter value
+ //
+ TickCount = GetTickCount();
+ lpTimeOfDay->tod_msecs = TickCount;
+
+ return(STATUS_SUCCESS);
+
+} // timesvc_RemoteTimeOfDay
+
+
+NET_API_STATUS
+NetrRemoteTOD (
+ IN LPSTR ServerName,
+ OUT LPTIME_OF_DAY_INFO *lpTimeOfDayInfo
+ )
+
+/*++
+
+Routine Description:
+
+ This is the RPC server entry point for the NetRemoteTOD API.
+
+Arguments:
+
+ ServerName - Name of server on which this API is to be executed.
+ It should match this server's name - no checking is
+ done since it is assumed that RPC did the right thing.
+
+ lpTimeOfDayInfo - On return takes a pointer to a TIME_OF_DAY_INFO
+ structure - the memory is allocated here.
+
+
+Return Value:
+
+ Returns a NET_API_STATUS code.
+ Also returns a pointer to the TIME_OF_DAY_INFO data buffer that was
+ allocated, if there is no error.
+
+
+--*/
+{
+ NTSTATUS status;
+
+ //
+ // Call the worker routine to collect all the time/date information
+ //
+ status = timesvc_RemoteTimeOfDay(lpTimeOfDayInfo);
+
+ //
+ // Translate the NTSTATUS to a NET_API_STATUS error, and return it.
+ //
+
+ return(NetpNtStatusToApiStatus(status));
+
+ UNREFERENCED_PARAMETER( ServerName );
+}
diff --git a/private/net/svcdlls/srvsvc/server/xport.c b/private/net/svcdlls/srvsvc/server/xport.c
new file mode 100644
index 000000000..f72415fd0
--- /dev/null
+++ b/private/net/svcdlls/srvsvc/server/xport.c
@@ -0,0 +1,689 @@
+/*++
+
+Copyright (c) 1991-1992 Microsoft Corporation
+
+Module Name:
+
+ Xport.c
+
+Abstract:
+
+ This module contains support for the ServerTransport catagory of
+ APIs for the NT server service.
+
+Author:
+
+ David Treadwell (davidtr) 10-Mar-1991
+
+Revision History:
+
+--*/
+
+#include "srvsvcp.h"
+#include "ssdata.h"
+#include "ssreg.h"
+
+#include <tstr.h>
+
+//
+// Forward declarations.
+//
+
+LPSERVER_TRANSPORT_INFO_1
+CaptureSvti1 (
+ IN DWORD Level,
+ IN LPTRANSPORT_INFO Svti,
+ OUT PULONG CapturedSvtiLength
+ );
+
+
+
+NET_API_STATUS NET_API_FUNCTION
+I_NetrServerTransportAddEx (
+ IN DWORD Level,
+ IN LPTRANSPORT_INFO Buffer
+ )
+{
+ NET_API_STATUS error;
+ LPSERVER_TRANSPORT_INFO_1 capturedSvti1;
+ LPSTR TransportAddress; // Pointer to transport address within capturedSvti1
+ ULONG capturedSvtiLength;
+ PSERVER_REQUEST_PACKET srp;
+ PNAME_LIST_ENTRY service;
+ PTRANSPORT_LIST_ENTRY transport;
+ BOOLEAN serviceAllocated = FALSE;
+ LPTSTR DomainName = SsData.DomainNameBuffer;
+ HANDLE h;
+
+ if( Level == 1 && Buffer->Transport1.svti1_domain != NULL ) {
+ DomainName = Buffer->Transport1.svti1_domain;
+ }
+
+ //
+ // Capture the transport request buffer and form the full transport
+ // address.
+ //
+
+ capturedSvti1 = CaptureSvti1( Level, Buffer, &capturedSvtiLength );
+
+ if ( capturedSvti1 == NULL ) {
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ TransportAddress = capturedSvti1->svti1_transportaddress;
+ OFFSET_TO_POINTER( TransportAddress, capturedSvti1 );
+
+
+ //
+ // Make sure this name isn't already bound for the transport
+ //
+ (VOID)RtlAcquireResourceExclusive( &SsServerInfoResource, TRUE );
+
+ for( service = SsServerNameList; service != NULL; service = service->Next ) {
+
+ if( service->TransportAddressLength != capturedSvti1->svti1_transportaddresslength ) {
+ continue;
+ }
+
+ if( !RtlEqualMemory( service->TransportAddress,
+ TransportAddress,
+ capturedSvti1->svti1_transportaddresslength
+ ) ) {
+ continue;
+ }
+
+ for( transport=service->Transports; transport != NULL; transport=transport->Next ) {
+
+ if( !STRCMPI( transport->TransportName, Buffer->Transport0.svti0_transportname ) ) {
+ //
+ // Error... this transport is already bound to the address
+ //
+ RtlReleaseResource( &SsServerInfoResource );
+ MIDL_user_free( capturedSvti1 );
+ return ERROR_DUP_NAME;
+ }
+ }
+
+ break;
+ }
+
+ //
+ // Counting on success, ensure we can allocate space for the new entry
+ //
+ if( service == NULL ) {
+
+ service = MIDL_user_allocate( sizeof( *service ) + (STRLEN( DomainName ) + 1) * sizeof( TCHAR ) );
+
+ if( service == NULL ) {
+ RtlReleaseResource( &SsServerInfoResource );
+ MIDL_user_free( capturedSvti1 );
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ RtlZeroMemory( service, sizeof( *service ) );
+
+ service->DomainName = (LPTSTR)( service + 1 );
+
+ serviceAllocated = TRUE;
+ }
+
+ transport = MIDL_user_allocate( sizeof( *transport ) +
+ (STRLEN( Buffer->Transport0.svti0_transportname ) + sizeof(CHAR)) * sizeof( TCHAR ) );
+
+ if( transport == NULL ) {
+
+ RtlReleaseResource( &SsServerInfoResource );
+ if( serviceAllocated ) {
+ MIDL_user_free( service );
+ }
+ MIDL_user_free( capturedSvti1 );
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ RtlZeroMemory( transport, sizeof( *transport ) );
+
+ //
+ // Get a SRP in which to send the request.
+ //
+
+ srp = SsAllocateSrp( );
+ if ( srp == NULL ) {
+ RtlReleaseResource( &SsServerInfoResource );
+ if( serviceAllocated ) {
+ MIDL_user_free( service );
+ }
+ MIDL_user_free( capturedSvti1 );
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ //
+ // Check if this is the primary machine name
+ //
+
+ if((capturedSvti1->svti1_transportaddresslength ==
+ SsServerTransportAddressLength)
+ &&
+ RtlEqualMemory(SsServerTransportAddress,
+ TransportAddress,
+ SsServerTransportAddressLength) )
+ {
+ srp->Flags |= SRP_XADD_PRIMARY_MACHINE;
+ }
+
+ //
+ // Send the request on to the server.
+ //
+ if( (error = SsOpenServer( &h )) == NO_ERROR ) {
+ error = SsServerFsControl(
+ h,
+ FSCTL_SRV_NET_SERVER_XPORT_ADD,
+ srp,
+ capturedSvti1,
+ capturedSvtiLength
+ );
+
+ NtClose( h );
+ }
+
+ //
+ // Free the SRP
+ //
+
+ SsFreeSrp( srp );
+
+ if( error != NO_ERROR ) {
+ RtlReleaseResource( &SsServerInfoResource );
+ if( serviceAllocated ) {
+ MIDL_user_free( service );
+ }
+ MIDL_user_free( transport );
+ MIDL_user_free( capturedSvti1 );
+ return error;
+ }
+
+ //
+ // Everything worked. Add it to the NAME_LIST
+ //
+ transport->TransportName = (LPTSTR)(transport + 1 );
+ STRCPY( transport->TransportName, Buffer->Transport0.svti0_transportname );
+ transport->Next = service->Transports;
+ service->Transports = transport;
+
+ if( serviceAllocated ) {
+
+ RtlCopyMemory( service->TransportAddress,
+ TransportAddress,
+ capturedSvti1->svti1_transportaddresslength );
+
+ service->TransportAddress[ capturedSvti1->svti1_transportaddresslength ] = '\0';
+ service->TransportAddressLength = capturedSvti1->svti1_transportaddresslength;
+
+ STRCPY( service->DomainName, DomainName );
+
+ service->Next = SsServerNameList;
+
+ //
+ // If this is the first transport and name added to the server, it must be the primary
+ // name
+ //
+ if( SsServerNameList == NULL ) {
+ service->PrimaryName = 1;
+ }
+
+ SsServerNameList = service;
+ }
+
+ RtlReleaseResource( &SsServerInfoResource );
+ MIDL_user_free( capturedSvti1 );
+ SsSetExportedServerType( service, FALSE, FALSE );
+
+ return NO_ERROR;
+}
+
+NET_API_STATUS NET_API_FUNCTION
+NetrServerTransportAddEx (
+ IN LPTSTR ServerName,
+ IN DWORD Level,
+ IN LPTRANSPORT_INFO Buffer
+ )
+{
+ NET_API_STATUS error;
+ LPTSTR DomainName = SsData.DomainNameBuffer;
+ PNAME_LIST_ENTRY service;
+
+ ServerName;
+
+ //
+ // Make sure that the level is valid.
+ //
+
+ if ( Level != 0 && Level != 1 ) {
+ return ERROR_INVALID_LEVEL;
+ }
+
+ if( Buffer->Transport0.svti0_transportname == NULL ||
+ Buffer->Transport0.svti0_transportaddress == NULL ||
+ Buffer->Transport0.svti0_transportaddresslength == 0 ||
+ Buffer->Transport0.svti0_transportaddresslength >= sizeof(service->TransportAddress) ) {
+
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ if( Level == 1 && Buffer->Transport1.svti1_domain != NULL ) {
+
+ DomainName = Buffer->Transport1.svti1_domain;
+
+ if( STRLEN( DomainName ) > DNLEN ) {
+ return ERROR_INVALID_DOMAINNAME;
+ }
+ }
+
+ //
+ // Make sure that the caller is allowed to set information in the
+ // server.
+ //
+
+ if( SsInitialized ) {
+ error = SsCheckAccess(
+ &SsConfigInfoSecurityObject,
+ SRVSVC_CONFIG_INFO_SET
+ );
+
+ if ( error != NO_ERROR ) {
+ return ERROR_ACCESS_DENIED;
+ }
+ }
+
+ return I_NetrServerTransportAddEx ( Level, Buffer );
+
+} // NetrServerTransportAddEx
+
+NET_API_STATUS NET_API_FUNCTION
+NetrServerTransportAdd (
+ IN LPTSTR ServerName,
+ IN DWORD Level,
+ IN LPSERVER_TRANSPORT_INFO_0 Buffer
+)
+{
+ if( Level != 0 ) {
+ return ERROR_INVALID_LEVEL;
+ }
+
+ return NetrServerTransportAddEx( ServerName, 0, (LPTRANSPORT_INFO)Buffer );
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetrServerTransportDelEx (
+ IN LPTSTR ServerName,
+ IN DWORD Level,
+ IN LPTRANSPORT_INFO Buffer
+ )
+
+{
+ NET_API_STATUS error;
+ LPSERVER_TRANSPORT_INFO_1 capturedSvti1;
+ LPSTR TransportAddress; // Pointer to transport address within capturedSvti1
+ ULONG capturedSvtiLength;
+ PSERVER_REQUEST_PACKET srp;
+ PNAME_LIST_ENTRY service;
+ PNAME_LIST_ENTRY sbackp = NULL;
+ PTRANSPORT_LIST_ENTRY transport;
+ PTRANSPORT_LIST_ENTRY tbackp = NULL;
+
+ ServerName;
+
+ //
+ // Make sure that the level is valid.
+ //
+
+ if ( Level != 0 && Level != 1 ) {
+ return ERROR_INVALID_LEVEL;
+ }
+
+ if( Buffer->Transport0.svti0_transportname == NULL ||
+ Buffer->Transport0.svti0_transportaddress == NULL ||
+ Buffer->Transport0.svti0_transportaddresslength == 0 ||
+ Buffer->Transport0.svti0_transportaddresslength >= sizeof(service->TransportAddress) ) {
+
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ //
+ // Make sure that the caller is allowed to set information in the
+ // server.
+ //
+
+ if( SsInitialized ) {
+ error = SsCheckAccess(
+ &SsConfigInfoSecurityObject,
+ SRVSVC_CONFIG_INFO_SET
+ );
+
+ if ( error != NO_ERROR ) {
+ return ERROR_ACCESS_DENIED;
+ }
+ }
+
+ //
+ // Capture the transport request buffer and form the full transport
+ // address.
+ //
+
+ capturedSvti1 = CaptureSvti1( Level, Buffer, &capturedSvtiLength );
+
+ if ( capturedSvti1 == NULL ) {
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ TransportAddress = capturedSvti1->svti1_transportaddress;
+ OFFSET_TO_POINTER( TransportAddress, capturedSvti1 );
+
+ //
+ // Get an SRP in which to send the request.
+ //
+
+ srp = SsAllocateSrp( );
+ if ( srp == NULL ) {
+ MIDL_user_free( capturedSvti1 );
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+
+ //
+ // Send the request on to the server.
+ //
+ error = SsServerFsControl(
+ NULL,
+ FSCTL_SRV_NET_SERVER_XPORT_DEL,
+ srp,
+ capturedSvti1,
+ capturedSvtiLength
+ );
+
+ //
+ // Free the SRP and svti
+ //
+
+ SsFreeSrp( srp );
+
+ if( error != NO_ERROR ) {
+ MIDL_user_free( capturedSvti1 );
+ return error;
+ }
+
+ (VOID)RtlAcquireResourceExclusive( &SsServerInfoResource, TRUE );
+
+
+ //
+ // Remove the entry from the SsServerNameList. If it's the last transport for
+ // the NAME_LIST_ENTRY, delete the NAME_LIST_ENTRY as well.
+ //
+ for( service = SsServerNameList; service != NULL; sbackp = service, service = service->Next ) {
+
+ //
+ // Walk the list until we find the NAME_LIST_ENTRY having the transportaddress
+ // of interest
+ //
+ if( service->TransportAddressLength != capturedSvti1->svti1_transportaddresslength ) {
+ continue;
+ }
+
+ if( !RtlEqualMemory( service->TransportAddress,
+ TransportAddress,
+ capturedSvti1->svti1_transportaddresslength ) ) {
+ continue;
+ }
+
+ //
+ // This is the correct NAME_LIST_ENTRY, now find the TRANSPORT_LIST_ENTRY of interest
+ //
+ for( transport=service->Transports; transport != NULL; tbackp=transport, transport=transport->Next ) {
+
+ if( STRCMPI( transport->TransportName, Buffer->Transport0.svti0_transportname ) ) {
+ continue;
+ }
+
+ //
+ // This is the one...remove it from the list
+ //
+
+ if( tbackp == NULL ) {
+ service->Transports = transport->Next;
+ } else {
+ tbackp->Next = transport->Next;
+ }
+
+ MIDL_user_free( transport );
+
+ break;
+ }
+
+ //
+ // If this NAME_LIST_ENTRY no longer has any transports, delete it
+ //
+ if( service->Transports == NULL ) {
+ if( sbackp == NULL ) {
+ SsServerNameList = service->Next;
+ } else {
+ sbackp->Next = service->Next;
+ }
+ MIDL_user_free( service );
+ }
+
+ break;
+ }
+
+ RtlReleaseResource( &SsServerInfoResource );
+ MIDL_user_free( capturedSvti1 );
+
+ return NO_ERROR;
+
+} // NetrServerTransportDelEx
+
+NET_API_STATUS NET_API_FUNCTION
+NetrServerTransportDel (
+ IN LPTSTR ServerName,
+ IN DWORD Level,
+ IN LPSERVER_TRANSPORT_INFO_0 Buffer
+)
+{
+ return NetrServerTransportDelEx( ServerName, Level, (LPTRANSPORT_INFO)Buffer );
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetrServerTransportEnum (
+ IN LPTSTR ServerName,
+ IN LPSERVER_XPORT_ENUM_STRUCT InfoStruct,
+ IN DWORD PreferredMaximumLength,
+ OUT LPDWORD TotalEntries,
+ IN OUT LPDWORD ResumeHandle OPTIONAL
+ )
+{
+ NET_API_STATUS error;
+ PSERVER_REQUEST_PACKET srp;
+
+ ServerName;
+
+ //
+ // Make sure that the level is valid.
+ //
+
+ if ( InfoStruct->Level != 0 && InfoStruct->Level != 1 ) {
+ return ERROR_INVALID_LEVEL;
+ }
+
+ //
+ // Set up the input parameters in the request buffer.
+ //
+
+ srp = SsAllocateSrp( );
+ if ( srp == NULL ) {
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+ srp->Level = InfoStruct->Level;
+
+ if ( ARGUMENT_PRESENT( ResumeHandle ) ) {
+ srp->Parameters.Get.ResumeHandle = *ResumeHandle;
+ } else {
+ srp->Parameters.Get.ResumeHandle = 0;
+ }
+
+ //
+ // Get the data from the server. This routine will allocate the
+ // return buffer and handle the case where PreferredMaximumLength ==
+ // -1.
+ //
+
+ error = SsServerFsControlGetInfo(
+ FSCTL_SRV_NET_SERVER_XPORT_ENUM,
+ srp,
+ (PVOID *)&InfoStruct->XportInfo.Level0->Buffer,
+ PreferredMaximumLength
+ );
+
+ //
+ // Set up return information.
+ //
+
+ InfoStruct->XportInfo.Level0->EntriesRead = srp->Parameters.Get.EntriesRead;
+ *TotalEntries = srp->Parameters.Get.TotalEntries;
+ if ( srp->Parameters.Get.EntriesRead > 0 && ARGUMENT_PRESENT( ResumeHandle ) ) {
+ *ResumeHandle = srp->Parameters.Get.ResumeHandle;
+ }
+
+ SsFreeSrp( srp );
+
+ return error;
+
+} // NetrServerTransportEnum
+
+
+LPSERVER_TRANSPORT_INFO_1
+CaptureSvti1 (
+ IN DWORD Level,
+ IN LPTRANSPORT_INFO Svti,
+ OUT PULONG CapturedSvtiLength
+ )
+{
+ LPSERVER_TRANSPORT_INFO_1 capturedSvti;
+ PCHAR variableData;
+ ULONG transportNameLength;
+ CHAR TransportAddressBuffer[MAX_PATH];
+ LPBYTE TransportAddress;
+ DWORD TransportAddressLength;
+ LPTSTR DomainName;
+ DWORD domainLength;
+
+ //
+ // If a server transport name is specified, use it, otherwise
+ // use the default server name on the transport.
+ //
+ // Either way, the return transport address is normalized into a netbios address
+ //
+
+ if ( Svti->Transport0.svti0_transportaddress == NULL ) {
+ TransportAddress = SsServerTransportAddress;
+ TransportAddressLength = SsServerTransportAddressLength;
+ Svti->Transport0.svti0_transportaddresslength = TransportAddressLength;
+ } else {
+
+
+ //
+ // Normalize the transport address.
+ //
+
+ TransportAddress = TransportAddressBuffer;
+ TransportAddressLength = min( Svti->Transport0.svti0_transportaddresslength,
+ sizeof( TransportAddressBuffer ));
+
+ RtlCopyMemory( TransportAddress,
+ Svti->Transport0.svti0_transportaddress,
+ TransportAddressLength );
+
+ if ( TransportAddressLength < NETBIOS_NAME_LEN ) {
+
+ RtlCopyMemory( TransportAddress + TransportAddressLength,
+ " ",
+ NETBIOS_NAME_LEN - TransportAddressLength );
+
+ TransportAddressLength = NETBIOS_NAME_LEN;
+
+ } else {
+
+ TransportAddressLength = NETBIOS_NAME_LEN;
+
+ }
+
+ }
+
+ transportNameLength = SIZE_WSTR( Svti->Transport0.svti0_transportname );
+
+ if( Level == 0 || Svti->Transport1.svti1_domain == NULL ) {
+ DomainName = SsData.DomainNameBuffer;
+ } else {
+ DomainName = Svti->Transport1.svti1_domain;
+ }
+
+ domainLength = SIZE_WSTR( DomainName );
+
+ //
+ // Allocate enough space to hold the captured buffer, including the
+ // full transport name/address and domain name
+ //
+
+ *CapturedSvtiLength = sizeof(*capturedSvti) +
+ transportNameLength + TransportAddressLength + domainLength;
+
+ capturedSvti = MIDL_user_allocate( *CapturedSvtiLength );
+
+ if ( capturedSvti == NULL ) {
+ return NULL;
+ }
+
+ //
+ // This field is not used...
+ //
+ capturedSvti->svti1_numberofvcs = 0;
+
+ //
+ // Copy in the domain name
+ //
+ variableData = (PCHAR)( capturedSvti + 1 );
+ capturedSvti->svti1_domain = (PWCH)variableData;
+ RtlCopyMemory( variableData,
+ DomainName,
+ domainLength
+ );
+ variableData += domainLength;
+ POINTER_TO_OFFSET( capturedSvti->svti1_domain, capturedSvti );
+
+ //
+ // Copy the transport name
+ //
+ capturedSvti->svti1_transportname = (PWCH)variableData;
+ RtlCopyMemory(
+ variableData,
+ Svti->Transport1.svti1_transportname,
+ transportNameLength
+ );
+ variableData += transportNameLength;
+ POINTER_TO_OFFSET( capturedSvti->svti1_transportname, capturedSvti );
+
+ //
+ // Copy the transport address
+ //
+ capturedSvti->svti1_transportaddress = variableData;
+ capturedSvti->svti1_transportaddresslength = TransportAddressLength;
+ RtlCopyMemory(
+ variableData,
+ TransportAddress,
+ TransportAddressLength
+ );
+ variableData += TransportAddressLength;
+ POINTER_TO_OFFSET( capturedSvti->svti1_transportaddress, capturedSvti );
+
+ return capturedSvti;
+
+} // CaptureSvti
diff --git a/private/net/svcdlls/srvsvc/server/xsdata.c b/private/net/svcdlls/srvsvc/server/xsdata.c
new file mode 100644
index 000000000..3517109e7
--- /dev/null
+++ b/private/net/svcdlls/srvsvc/server/xsdata.c
@@ -0,0 +1,301 @@
+/*+
+
+Copyright (c) 1991-1993 Microsoft Corporation
+
+Module Name:
+
+ xsdata.c
+
+Abstract:
+
+ Global data declarations for XACTSRV.
+
+Author:
+
+ David Treadwell (davidtr) 05-Jan-1991
+ Shanku Niyogi (w-shanku)
+
+Revision History:
+
+ Chuck Lenzmeier (chuckl) 17-Jun-1992
+ Moved from xssvc to srvsvc\server
+
+--*/
+
+//
+// Includes.
+//
+
+#include "srvsvcp.h"
+#include "xsdata.h"
+
+#include <remdef.h> // from net\inc
+#include <xsprocs.h>
+
+#include <xsconst.h> // from xactsrv
+#include <xsprocsp.h>
+
+#undef DEBUG
+#undef DEBUG_API_ERRORS
+#include <xsdebug.h>
+
+//
+// Debugging flags.
+//
+
+DWORD XsDebug = 0;
+
+//
+// Number of XACTSRV worker threads.
+//
+
+LONG XsThreads = 0;
+
+//
+// Event signalled when the last XACTSRV worker thread terminates.
+//
+
+HANDLE XsAllThreadsTerminatedEvent = NULL;
+
+//
+// Boolean indicating whether XACTSRV is active or terminating.
+//
+
+BOOL XsTerminating = FALSE;
+
+//
+// Handle for the LPC port used for communication between the file server
+// and XACTSRV.
+//
+
+HANDLE XsConnectionPortHandle = NULL;
+HANDLE XsCommunicationPortHandle = NULL;
+
+//
+// Table of information necessary for dispatching API requests.
+//
+// ImpersonateClient specifies whether XACTSRV should impersonate the caller
+// before invoking the API handler.
+//
+// Handler specifies the function XACTSRV should call to handle the API.
+//
+
+XS_API_TABLE_ENTRY XsApiTable[XS_SIZE_OF_API_TABLE] = {
+ TRUE, &XsNetShareEnum, REMSmb_NetShareEnum_P, // 0
+ TRUE, &XsNetShareGetInfo, REMSmb_NetShareGetInfo_P,
+ TRUE, &XsNetShareSetInfo, REMSmb_NetShareSetInfo_P,
+ TRUE, &XsNetShareAdd, REMSmb_NetShareAdd_P,
+ TRUE, &XsNetShareDel, REMSmb_NetShareDel_P,
+ TRUE, &XsNetShareCheck, REMSmb_NetShareCheck_P,
+ TRUE, &XsNetSessionEnum, REMSmb_NetSessionEnum_P,
+ TRUE, &XsNetSessionGetInfo, REMSmb_NetSessionGetInfo_P,
+ TRUE, &XsNetSessionDel, REMSmb_NetSessionDel_P,
+ TRUE, &XsNetConnectionEnum, REMSmb_NetConnectionEnum_P,
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P, // 10
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P,
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P,
+ TRUE, &XsNetServerGetInfo, REMSmb_NetServerGetInfo_P,
+ TRUE, &XsNetServerSetInfo, REMSmb_NetServerSetInfo_P,
+ TRUE, &XsNetServerDiskEnum, REMSmb_NetServerDiskEnum_P,
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P,
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P,
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P,
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P,
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P, // 20
+ TRUE, &XsNetCharDevEnum, REMSmb_NetCharDevEnum_P,
+ TRUE, &XsNetCharDevGetInfo, REMSmb_NetCharDevGetInfo_P,
+ TRUE, &XsNetCharDevControl, REMSmb_NetCharDevControl_P,
+ TRUE, &XsNetCharDevQEnum, REMSmb_NetCharDevQEnum_P,
+ TRUE, &XsNetCharDevQGetInfo, REMSmb_NetCharDevQGetInfo_P,
+ TRUE, &XsNetCharDevQSetInfo, REMSmb_NetCharDevQSetInfo_P,
+ TRUE, &XsNetCharDevQPurge, REMSmb_NetCharDevQPurge_P,
+ TRUE, &XsNetCharDevQPurgeSelf, REMSmb_NetCharDevQPurgeSelf_P,
+ TRUE, &XsNetMessageNameEnum, REMSmb_NetMessageNameEnum_P,
+ TRUE, &XsNetMessageNameGetInfo, REMSmb_NetMessageNameGetInfo_P, // 30
+ TRUE, &XsNetMessageNameAdd, REMSmb_NetMessageNameAdd_P,
+ TRUE, &XsNetMessageNameDel, REMSmb_NetMessageNameDel_P,
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P,
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P,
+ TRUE, &XsNetMessageBufferSend, REMSmb_NetMessageBufferSend_P,
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P,
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P,
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P,
+ TRUE, &XsNetServiceEnum, REMSmb_NetServiceEnum_P,
+ TRUE, &XsNetServiceInstall, REMSmb_NetServiceInstall_P, // 40
+ TRUE, &XsNetServiceControl, REMSmb_NetServiceControl_P,
+ TRUE, &XsNetAccessEnum, REMSmb_NetAccessEnum_P,
+ TRUE, &XsNetAccessGetInfo, REMSmb_NetAccessGetInfo_P,
+ TRUE, &XsNetAccessSetInfo, REMSmb_NetAccessSetInfo_P,
+ TRUE, &XsNetAccessAdd, REMSmb_NetAccessAdd_P,
+ TRUE, &XsNetAccessDel, REMSmb_NetAccessDel_P,
+ TRUE, &XsNetGroupEnum, REMSmb_NetGroupEnum_P,
+ TRUE, &XsNetGroupAdd, REMSmb_NetGroupAdd_P,
+ TRUE, &XsNetGroupDel, REMSmb_NetGroupDel_P,
+ TRUE, &XsNetGroupAddUser, REMSmb_NetGroupAddUser_P, // 50
+ TRUE, &XsNetGroupDelUser, REMSmb_NetGroupDelUser_P,
+ TRUE, &XsNetGroupGetUsers, REMSmb_NetGroupGetUsers_P,
+ TRUE, &XsNetUserEnum, REMSmb_NetUserEnum_P,
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P,
+ TRUE, &XsNetUserDel, REMSmb_NetUserDel_P,
+ TRUE, &XsNetUserGetInfo, REMSmb_NetUserGetInfo_P,
+ TRUE, &XsNetUserSetInfo, REMSmb_NetUserSetInfo_P,
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P,
+ TRUE, &XsNetUserGetGroups, REMSmb_NetUserGetGroups_P,
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P, // 60
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P,
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P,
+ TRUE, &XsNetWkstaGetInfo, REMSmb_NetWkstaGetInfo_P,
+ TRUE, &XsNetWkstaSetInfo, REMSmb_NetWkstaSetInfo_P,
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P,
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P,
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P,
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P,
+ FALSE, &XsNetPrintQEnum, REMSmb_DosPrintQEnum_P,
+ FALSE, &XsNetPrintQGetInfo, REMSmb_DosPrintQGetInfo_P, // 70
+ TRUE, &XsNetPrintQSetInfo, REMSmb_DosPrintQSetInfo_P,
+ TRUE, &XsNetPrintQAdd, REMSmb_DosPrintQAdd_P,
+ TRUE, &XsNetPrintQDel, REMSmb_DosPrintQDel_P,
+ TRUE, &XsNetPrintQPause, REMSmb_DosPrintQPause_P,
+ TRUE, &XsNetPrintQContinue, REMSmb_DosPrintQContinue_P,
+ FALSE, &XsNetPrintJobEnum, REMSmb_DosPrintJobEnum_P,
+ FALSE, &XsNetPrintJobGetInfo, REMSmb_DosPrintJobGetInfo_P,
+ TRUE, &XsNetPrintJobSetInfo, REMSmb_DosPrintJobSetInfo_P,
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P,
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P, // 80
+ TRUE, &XsNetPrintJobDel, REMSmb_DosPrintJobDel_P,
+ TRUE, &XsNetPrintJobPause, REMSmb_DosPrintJobPause_P,
+ TRUE, &XsNetPrintJobContinue, REMSmb_DosPrintJobContinue_P,
+ TRUE, &XsNetPrintDestEnum, REMSmb_DosPrintDestEnum_P,
+ TRUE, &XsNetPrintDestGetInfo, REMSmb_DosPrintDestGetInfo_P,
+ TRUE, &XsNetPrintDestControl, REMSmb_DosPrintDestControl_P,
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P,
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P,
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P,
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P, // 90
+ TRUE, &XsNetRemoteTOD, REMSmb_NetRemoteTOD_P,
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P,
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P,
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P,
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P,
+ TRUE, &XsNetServiceGetInfo, REMSmb_NetServiceGetInfo_P,
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P,
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P,
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P,
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P, // 100
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P,
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P,
+ TRUE, &XsNetPrintQPurge, REMSmb_DosPrintQPurge_P,
+ FALSE, &XsNetServerEnum2, REMSmb_NetServerEnum2_P,
+ TRUE, &XsNetAccessGetUserPerms, REMSmb_NetAccessGetUserPerms_P,
+ TRUE, &XsNetGroupGetInfo, REMSmb_NetGroupGetInfo_P,
+ TRUE, &XsNetGroupSetInfo, REMSmb_NetGroupSetInfo_P,
+ TRUE, &XsNetGroupSetUsers, REMSmb_NetGroupSetUsers_P,
+ TRUE, &XsNetUserSetGroups, REMSmb_NetUserSetGroups_P,
+ TRUE, &XsNetUserModalsGet, REMSmb_NetUserModalsGet_P, // 110
+ TRUE, &XsNetUserModalsSet, REMSmb_NetUserModalsSet_P,
+ TRUE, &XsNetFileEnum2, REMSmb_NetFileEnum2_P,
+ TRUE, &XsNetUserAdd2, REMSmb_NetUserAdd2_P,
+ TRUE, &XsNetUserSetInfo2, REMSmb_NetUserSetInfo2_P,
+ TRUE, &XsNetUserPasswordSet2, REMSmb_NetUserPasswordSet2_P,
+ FALSE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P,
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P,
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P,
+ TRUE, &XsNetGetDCName, REMSmb_NetGetDCName_P,
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P, // 120
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P,
+ TRUE, &XsNetStatisticsGet2, REMSmb_NetStatisticsGet2_P,
+ TRUE, &XsNetBuildGetInfo, REMSmb_NetBuildGetInfo_P,
+ TRUE, &XsNetFileGetInfo2, REMSmb_NetFileGetInfo2_P,
+ TRUE, &XsNetFileClose2, REMSmb_NetFileClose2_P,
+ FALSE, &XsNetServerReqChallenge, REMSmb_NetServerReqChalleng_P,
+ FALSE, &XsNetServerAuthenticate, REMSmb_NetServerAuthenticat_P,
+ FALSE, &XsNetServerPasswordSet, REMSmb_NetServerPasswordSet_P,
+ FALSE, &XsNetAccountDeltas, REMSmb_NetAccountDeltas_P,
+ FALSE, &XsNetAccountSync, REMSmb_NetAccountSync_P, // 130
+ TRUE, &XsNetUserEnum2, REMSmb_NetUserEnum2_P,
+ TRUE, &XsNetWkstaUserLogon, REMSmb_NetWkstaUserLogon_P,
+ TRUE, &XsNetWkstaUserLogoff, REMSmb_NetWkstaUserLogoff_P,
+ TRUE, &XsNetLogonEnum, REMSmb_NetLogonEnum_P,
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P,
+ TRUE, &XsI_NetPathType, REMSmb_I_NetPathType_P,
+ TRUE, &XsI_NetPathCanonicalize, REMSmb_I_NetPathCanonicalize_P,
+ TRUE, &XsI_NetPathCompare, REMSmb_I_NetPathCompare_P,
+ TRUE, &XsI_NetNameValidate, REMSmb_I_NetNameValidate_P,
+ TRUE, &XsI_NetNameCanonicalize, REMSmb_I_NetNameCanonicalize_P, //140
+ TRUE, &XsI_NetNameCompare, REMSmb_I_NetNameCompare_P,
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P,
+ TRUE, &XsNetPrintDestAdd, REMSmb_DosPrintDestAdd_P,
+ TRUE, &XsNetPrintDestSetInfo, REMSmb_DosPrintDestSetInfo_P,
+ TRUE, &XsNetPrintDestDel, REMSmb_DosPrintDestDel_P,
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P,
+ TRUE, &XsNetPrintJobSetInfo, REMSmb_DosPrintJobSetInfo_P,
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P,
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P,
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P, // 150
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P,
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P,
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P,
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P,
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P,
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P,
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P,
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P,
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P,
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P, // 160
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P,
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P,
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P,
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P,
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P,
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P,
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P,
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P,
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P,
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P, // 170
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P,
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P,
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P,
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P,
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P,
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P,
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P,
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P,
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P,
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P, // 180
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P,
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P,
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P,
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P,
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P,
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P,
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P,
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P,
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P,
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P, // 190
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P,
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P,
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P,
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P,
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P,
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P,
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P,
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P,
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P,
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P, // 200
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P,
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P,
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P,
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P,
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P,
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P,
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P,
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P,
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P,
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P, // 210
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P,
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P,
+ TRUE, &XsNetUnsupportedApi, REMSmb_NetUnsupportedApi_P,
+ TRUE, &XsSamOEMChangePasswordUser2_P, REM32_SamOEMChgPasswordUser2_P,
+ FALSE, &XsNetServerEnum3, REMSmb_NetServerEnum3_P
+};
+
diff --git a/private/net/svcdlls/srvsvc/server/xsdata.h b/private/net/svcdlls/srvsvc/server/xsdata.h
new file mode 100644
index 000000000..873fa22bf
--- /dev/null
+++ b/private/net/svcdlls/srvsvc/server/xsdata.h
@@ -0,0 +1,69 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ xsdata.h
+
+Abstract:
+
+ Header file for XACTSRV global data.
+
+Author:
+
+ David Treadwell (davidtr) 05-Jan-1991
+ Shanku Niyogi (w-shanku)
+
+Revision History:
+
+--*/
+
+#ifndef _XSDATA_
+#define _XSDATA_
+
+#include <nturtl.h>
+
+#include <winbase.h>
+
+#include <rap.h>
+#include <xstypes.h>
+#include <ntmsv1_0.h>
+
+//
+// Number of XACTSRV threads
+//
+extern LONG XsThreads;
+
+//
+// Event signalled when the last XACTSRV worker thread terminates.
+//
+
+extern HANDLE XsAllThreadsTerminatedEvent;
+
+//
+// Boolean indicating whether XACTSRV is active or terminating.
+//
+
+extern BOOL XsTerminating;
+
+//
+// Handle for the LPC port used for communication between the file server
+// and XACTSRV.
+//
+
+extern HANDLE XsConnectionPortHandle;
+extern HANDLE XsCommunicationPortHandle;
+
+//
+// Table of information necessary for dispatching API requests.
+// XsProcessApis uses the API number in the request transaction find
+// the appropriate entry.
+//
+
+#define XS_SIZE_OF_API_TABLE 216
+
+extern XS_API_TABLE_ENTRY XsApiTable[XS_SIZE_OF_API_TABLE];
+
+#endif // ndef _XSDATA_
+
diff --git a/private/net/svcdlls/srvsvc/server/xsinit.c b/private/net/svcdlls/srvsvc/server/xsinit.c
new file mode 100644
index 000000000..dcb495f50
--- /dev/null
+++ b/private/net/svcdlls/srvsvc/server/xsinit.c
@@ -0,0 +1,536 @@
+/*++
+
+Copyright (c) 1991-1992 Microsoft Corporation
+
+Module Name:
+
+ xsinit.c
+
+Abstract:
+
+ This module contains the initialization and termination code for
+ the XACTSRV component of the server service.
+
+Author:
+
+ David Treadwell (davidtr) 05-Jan-1991
+ Shanku Niyogi (w-shanku)
+
+Revision History:
+
+ Chuck Lenzmeier (chuckl) 17-Jun-1992
+ Merged xactsrv.c into xsinit.c and moved from xssvc to
+ srvsvc\server
+
+--*/
+
+//
+// Includes.
+//
+
+#include "srvsvcp.h"
+#include "ssdata.h"
+#include "xsdata.h"
+
+#include <xsprocs.h> // from net\inc
+
+#include <xactsrv2.h> // from private\inc
+#include <srvfsctl.h>
+
+#include <xsconst.h> // from xactsrv
+#include <xsprocsp.h>
+
+#undef DEBUG
+#undef DEBUG_API_ERRORS
+#include <xsdebug.h>
+
+
+DWORD
+XsStartXactsrv (
+ VOID
+ )
+{
+ NTSTATUS status;
+ DWORD error;
+ DWORD i;
+ HANDLE threadHandle;
+ DWORD threadId;
+ HANDLE eventHandle;
+ HANDLE serverHandle;
+ ANSI_STRING ansiName;
+ UNICODE_STRING unicodeName;
+ IO_STATUS_BLOCK ioStatusBlock;
+ OBJECT_ATTRIBUTES objectAttributes;
+ PORT_MESSAGE connectionRequest;
+ REMOTE_PORT_VIEW clientView;
+ BOOL waitForEvent;
+
+ //
+ // Set up variables so that we'll know how to shut down in case of
+ // an error.
+ //
+
+ serverHandle = NULL;
+ eventHandle = NULL;
+ waitForEvent = FALSE;
+
+ //
+ // Create a event that will be set by the last thread to exit.
+ //
+
+ IF_DEBUG(INIT) {
+ SS_PRINT(( "XsStartXactsrv: Creating termination event.\n" ));
+ }
+ SS_ASSERT( XsAllThreadsTerminatedEvent == NULL );
+
+ status = NtCreateEvent(
+ &XsAllThreadsTerminatedEvent,
+ EVENT_ALL_ACCESS,
+ NULL,
+ NotificationEvent,
+ FALSE
+ );
+
+ if ( !NT_SUCCESS(status) ) {
+ IF_DEBUG(ERRORS) {
+ SS_PRINT(( "XsStartXactsrv: NtCreateEvent failed: %X\n",
+ status ));
+ }
+
+ XsAllThreadsTerminatedEvent = NULL;
+ goto exit;
+ }
+
+ //
+ // Open the server device. Note that we need this handle because
+ // the handle used by the main server service is synchronous. We
+ // need to to do the XACTSRV_CONNECT FSCTL asynchronously.
+ //
+
+ RtlInitUnicodeString( &unicodeName, XS_SERVER_DEVICE_NAME_W );
+
+ InitializeObjectAttributes(
+ &objectAttributes,
+ &unicodeName,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL
+ );
+
+ status = NtOpenFile(
+ &serverHandle,
+ FILE_READ_DATA, // DesiredAccess
+ &objectAttributes,
+ &ioStatusBlock,
+ 0L, // ShareAccess
+ 0L // OpenOptions
+ );
+
+ if ( NT_SUCCESS(status) ) {
+ status = ioStatusBlock.Status;
+ }
+ if ( !NT_SUCCESS(status) ) {
+ IF_DEBUG(ERRORS) {
+ SS_PRINT(( "XsStartXactsrv: NtOpenFile (server device object) "
+ "failed: %X\n", status ));
+ }
+ goto exit;
+ }
+
+ //
+ // Create the LPC port.
+ //
+ // !!! Right now this only tries a single port name. If, for some
+ // bizarre reason, somebody already has a port by this name,
+ // then this will fail. It might make sense to try different
+ // names if this fails.
+ //
+ // !!! We might want to make the port name somewhat random for
+ // slightly enhanced security.
+
+ RtlInitUnicodeString( &unicodeName, XS_PORT_NAME_W );
+ RtlInitAnsiString( &ansiName, XS_PORT_NAME_A );
+
+ InitializeObjectAttributes(
+ &objectAttributes,
+ &unicodeName,
+ 0,
+ NULL,
+ NULL
+ );
+
+ IF_DEBUG(LPC) {
+ SS_PRINT(( "XsInitialize: creating port %Z\n", &ansiName ));
+ }
+
+ SS_ASSERT( XsConnectionPortHandle == NULL );
+
+ status = NtCreatePort(
+ &XsConnectionPortHandle,
+ &objectAttributes,
+ 0,
+ XS_PORT_MAX_MESSAGE_LENGTH,
+ XS_PORT_MAX_MESSAGE_LENGTH * 32
+ );
+
+ if ( ! NT_SUCCESS(status) ) {
+
+ IF_DEBUG(ERRORS) {
+ if ( status == STATUS_OBJECT_NAME_COLLISION ) {
+ SS_PRINT(( "XsStartXactsrv: The XACTSRV port already "
+ "exists\n"));
+
+ } else {
+ SS_PRINT(( "XsStartXactsrv: Failed to create port %Z: %X\n",
+ &ansiName, status ));
+ }
+ }
+
+ XsConnectionPortHandle = NULL;
+ goto exit;
+ }
+
+ //
+ // Set up an event so that we'll know when IO completes, then send
+ // the FSCTL to the server indicating that it should now connect to
+ // us. We'll set up the port while the IO is outstanding, then wait
+ // on the event when the port setup is complete.
+ //
+
+ status = NtCreateEvent(
+ &eventHandle,
+ EVENT_ALL_ACCESS,
+ NULL, // ObjectAttributes
+ NotificationEvent,
+ FALSE
+ );
+
+ if ( !NT_SUCCESS(status) ) {
+ IF_DEBUG(ERRORS) {
+ SS_PRINT(( "XsStartXactsrv: NtCreateEvent failed: %X\n",
+ status ));
+ }
+ goto exit;
+ }
+
+ IF_DEBUG(LPC) {
+ SS_PRINT(( "XsStartXactsrv: sending FSCTL_SRV_XACTSRV_CONNECT.\n" ));
+ }
+
+ status = NtFsControlFile(
+ serverHandle,
+ eventHandle,
+ NULL, // ApcRoutine
+ NULL, // ApcContext
+ &ioStatusBlock,
+ FSCTL_SRV_XACTSRV_CONNECT,
+ ansiName.Buffer,
+ ansiName.Length,
+ NULL, // OutputBuffer
+ 0L // OutputBufferLength
+ );
+
+ if ( !NT_SUCCESS(status) ) {
+ IF_DEBUG(ERRORS) {
+ SS_PRINT(( "XsStartXactsrv: NtFsControlFile failed: %X\n",
+ status ));
+ }
+ goto exit;
+ }
+
+ waitForEvent = TRUE;
+
+ //
+ // Start listening for the server's connection to the port. Note
+ // that it is OK if the server happens to call NtConnectPort
+ // first--it will simply block until this call to NtListenPort
+ // occurs.
+ //
+
+ IF_DEBUG(LPC) {
+ SS_PRINT(( "XsStartXactsrv: listening to port.\n" ));
+ }
+
+ connectionRequest.u1.s1.TotalLength = sizeof(connectionRequest);
+ connectionRequest.u1.s1.DataLength = (CSHORT)0;
+ status = NtListenPort(
+ XsConnectionPortHandle,
+ &connectionRequest
+ );
+
+ if ( !NT_SUCCESS(status) ) {
+ IF_DEBUG(ERRORS) {
+ SS_PRINT(( "XsStartXactsrv: NtListenPort failed: %X\n", status ));
+ }
+ goto exit;
+ }
+
+ //
+ // The server has initiated the connection. Accept the connection.
+ //
+ // !!! We probably need some security check here.
+ //
+
+ clientView.Length = sizeof(clientView);
+ clientView.ViewSize = 0;
+ clientView.ViewBase = 0;
+
+ IF_DEBUG(LPC) {
+ SS_PRINT(( "XsStartXactsrv: Accepting connection to port.\n" ));
+ }
+
+ SS_ASSERT( XsCommunicationPortHandle == NULL );
+
+ status = NtAcceptConnectPort(
+ &XsCommunicationPortHandle,
+ NULL, // PortContext
+ &connectionRequest,
+ TRUE, // AcceptConnection
+ NULL, // ServerView
+ &clientView
+ );
+
+ if ( !NT_SUCCESS(status) ) {
+ IF_DEBUG(ERRORS) {
+ SS_PRINT(( "XsStartXactsrv: NtAcceptConnectPort failed: %X\n",
+ status ));
+ }
+
+ XsCommunicationPortHandle = NULL;
+ goto exit;
+ }
+
+ IF_DEBUG(LPC) {
+ SS_PRINT(( "XsStartXactsrv: client view size: %ld, base: %lx\n",
+ clientView.ViewSize, clientView.ViewBase ));
+ }
+
+ //
+ // Complete the connection to the port, thereby releasing the server
+ // thread waiting in NtConnectPort.
+ //
+
+ IF_DEBUG(LPC) {
+ SS_PRINT(( "XsStartXactsrv: Completing connection to port.\n" ));
+ }
+
+ status = NtCompleteConnectPort( XsCommunicationPortHandle );
+
+ if ( !NT_SUCCESS(status) ) {
+ IF_DEBUG(ERRORS) {
+ SS_PRINT(( "XsStartXactsrv: NtCompleteConnectPort failed: %X\n",
+ status ));
+ }
+ goto exit;
+ }
+
+ status = STATUS_SUCCESS;
+
+exit:
+
+ //
+ // Wait for the IO to complete, then close the event handle.
+ //
+
+ if ( waitForEvent ) {
+
+ NTSTATUS waitStatus;
+
+ SS_ASSERT( eventHandle != NULL );
+
+ waitStatus = NtWaitForSingleObject( eventHandle, FALSE, NULL );
+
+ if ( !NT_SUCCESS(waitStatus) ) {
+
+ IF_DEBUG(ERRORS) {
+ SS_PRINT(( "XsStartXactsrv: NtWaitForSingleObject failed: "
+ "%X\n", waitStatus ));
+ }
+
+ //
+ // If another error has already occurred, don't report this
+ // one.
+ //
+
+ if ( NT_SUCCESS(status) ) {
+ status = waitStatus;
+ }
+ }
+
+ //
+ // Check the status in the IO status block. If it is bad, then
+ // there was some problem on the server side of the port setup.
+ //
+
+ if ( !NT_SUCCESS(ioStatusBlock.Status) ) {
+ IF_DEBUG(ERRORS) {
+ SS_PRINT(( "XsStartXactsrv: bad status in IO status block: "
+ "%X\n", ioStatusBlock.Status ));
+ }
+
+ //
+ // If another error has already occurred, don't report this
+ // one.
+ //
+
+ if ( NT_SUCCESS(status) ) {
+ status = ioStatusBlock.Status;
+ }
+
+ }
+
+ CloseHandle( eventHandle );
+
+ }
+
+ //
+ // Close the handle to the server.
+ //
+
+ if ( serverHandle != NULL ) {
+ CloseHandle( serverHandle );
+ }
+
+ //
+ // If the above failed, return to caller now.
+ //
+
+ if ( !NT_SUCCESS(status) ) {
+ return RtlNtStatusToDosError( status );
+ }
+
+ //
+ // Start one API processing thread. It will spawn others if needed
+ //
+
+ XsThreads = 1;
+
+ threadHandle = CreateThread(
+ NULL,
+ 0,
+ (LPTHREAD_START_ROUTINE)XsProcessApisWrapper,
+ (LPVOID)XsThreads,
+ 0,
+ &threadId
+ );
+
+ if ( threadHandle != 0 ) {
+
+ IF_DEBUG(THREADS) {
+ SS_PRINT(( "XsStartXactsrv: Created thread %ld for "
+ "processing APIs\n", XsThreads ));
+ }
+
+ CloseHandle( threadHandle );
+
+ } else {
+
+ //
+ // Thread creation failed. Return an error to the caller.
+ // It is the responsibility of the caller to call
+ // XsStopXactsrv to clean up.
+ //
+
+ XsThreads = 0;
+
+ error = GetLastError( );
+ return error;
+
+ }
+
+
+ //
+ // Initialization succeeded.
+ //
+
+ return NO_ERROR;
+
+} // XsStartXactsrv
+
+
+/*
+ * This routine is called to stop the transaction processor once the
+ * server driver has terminated.
+ */
+VOID
+XsStopXactsrv (
+ VOID
+ )
+{
+ NTSTATUS status;
+ static XACTSRV_REQUEST_MESSAGE requestMessage;
+ LONG i;
+
+ //
+ // Stop all the xs worker threads, and release resources
+ //
+
+ if ( XsConnectionPortHandle != NULL ) {
+
+ //
+ // Indicate that XACTSRV is terminating.
+ //
+ XsTerminating = TRUE;
+
+ IF_DEBUG(TERMINATION) {
+ SS_PRINT(("XsStopXactsrv: queueing termination messages\n"));
+ }
+
+ //
+ // Queue a message to kill off the worker thereads
+ //
+ RtlZeroMemory( &requestMessage, sizeof( requestMessage ));
+ requestMessage.PortMessage.u1.s1.DataLength =
+ (USHORT)( sizeof(requestMessage) - sizeof(PORT_MESSAGE) );
+ requestMessage.PortMessage.u1.s1.TotalLength = sizeof(requestMessage);
+ requestMessage.MessageType = XACTSRV_MESSAGE_WAKEUP;
+
+ status = NtRequestPort(
+ XsConnectionPortHandle,
+ (PPORT_MESSAGE)&requestMessage
+ );
+
+ IF_DEBUG(ERRORS) {
+ if ( !NT_SUCCESS(status) ) {
+ SS_PRINT(( "SrvXsDisconnect: NtRequestPort failed: %X\n",
+ status ));
+ }
+ }
+
+ //
+ // The above will cause all worker threads to wake up then die.
+ //
+
+ if ( XsThreads != 0 ) {
+ BOOL ok;
+ ok = WaitForSingleObject( XsAllThreadsTerminatedEvent, (DWORD)-1 );
+ IF_DEBUG(ERRORS) {
+ if ( !ok ) {
+ SS_PRINT(( "XsStopXactsrv: WaitForSingleObject failed: "
+ "%ld\n", GetLastError() ));
+ }
+ }
+ }
+
+ SS_ASSERT( XsThreads == 0 );
+
+ CloseHandle( XsConnectionPortHandle );
+ }
+
+ if( XsCommunicationPortHandle != NULL ) {
+ CloseHandle( XsCommunicationPortHandle );
+ XsCommunicationPortHandle = NULL;
+ }
+
+ //
+ // Close the termination event.
+ //
+
+ if ( XsAllThreadsTerminatedEvent != NULL ) {
+ CloseHandle( XsAllThreadsTerminatedEvent );
+ XsAllThreadsTerminatedEvent = NULL;
+ }
+
+ return;
+
+} // XsStopXactsrv
diff --git a/private/net/svcdlls/srvsvc/server/xsproc.c b/private/net/svcdlls/srvsvc/server/xsproc.c
new file mode 100644
index 000000000..d60a75d2a
--- /dev/null
+++ b/private/net/svcdlls/srvsvc/server/xsproc.c
@@ -0,0 +1,1092 @@
+/*++
+
+Copyright (c) 1991-1992 Microsoft Corporation
+
+Module Name:
+
+ xsproc.c
+
+Abstract:
+
+ This module contains the main processing loop for XACTSRV.
+
+Author:
+
+ David Treadwell (davidtr) 05-Jan-1991
+ Shanku Niyogi (w-shanku)
+
+Revision History:
+
+ 02-Jun-1992 JohnRo
+ RAID 9829: Avoid SERVICE_ equate conflicts.
+
+ Chuck Lenzmeier (chuckl) 17-Jun-1992
+ Moved from xssvc to srvsvc\server
+
+--*/
+
+//
+// Includes.
+//
+
+#include "srvsvcp.h"
+#include "xsdata.h"
+#include "ssdata.h"
+
+#include <netevent.h>
+
+#include <windows.h> // from sdk\inc
+#include <winspool.h>
+
+#include <xsprocs.h> // from net\inc
+#include <apinums.h> // from net\inc
+#include <netlib.h> // from net\inc (NetpGetComputerName)
+
+#include <xactsrv2.h> // from private\inc
+#include <smbgtpt.h>
+
+#include <xsconst.h> // from xactsrv
+#include <xsprocsp.h>
+
+#include <lmsname.h> // from \sdk\inc
+#include <lmerr.h> // from \sdk\inc
+#include <lmapibuf.h> // from \sdk\inc (NetApiBufferFree)
+#include <lmmsg.h> // from \sdk\inc (NetMessageBufferSend)
+#include <winsvc.h> // from \sdk\inc
+
+#include <ntlsapi.h> // from \sdk\inc (License Server APIs)
+
+#if DBG
+#include <stdio.h>
+#include <lmbrowsr.h>
+#endif
+
+#undef DEBUG
+#undef DEBUG_API_ERRORS
+#include <xsdebug.h>
+
+VOID
+ConvertApiStatusToDosStatus(
+ LPXS_PARAMETER_HEADER header
+ );
+
+VOID
+XsProcessApis (
+ DWORD ThreadNum
+ );
+
+//
+// This is the number of Xs threads blocked waiting for an LPC request.
+// When it drops to zero, all threads are active and another thread is
+// created.
+//
+LONG XsWaitingApiThreads = 0;
+
+
+VOID
+XsProcessApisWrapper (
+ DWORD ThreadNum
+ )
+
+/*++
+
+Routine Description:
+
+ This routine provides multithreaded capability for main processing
+ routine, XsProcessApis.
+
+Arguments:
+
+ ThreadNum - thread number for debugging purposes.
+
+
+--*/
+
+{
+ XACTSRV_REQUEST_MESSAGE requestMessage;
+
+ //
+ // Increase the priority of this thread to just above foreground (the
+ // same as the rest of the server).
+ //
+
+ SetThreadPriority( GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL );
+
+ //
+ // Do the APIs
+ //
+ XsProcessApis( ThreadNum );
+
+ IF_DEBUG(THREADS) {
+ SS_PRINT(( "Thread %ld exiting, active count %ld\n", ThreadNum,
+ XsThreads ));
+ }
+
+ //
+ // Decrement the count of threads. If the count goes to
+ // zero, set the All Threads Terminated event.
+ //
+
+ if( InterlockedDecrement( &XsThreads ) == 0 ) {
+
+ SetEvent( XsAllThreadsTerminatedEvent );
+
+ } else if( XsTerminating ) {
+
+ //
+ // There are still threads left, and we are trying to terminate. Queue
+ // another message to the queue so the next thread will get it and
+ // notice that we're trying to quit.
+ //
+ RtlZeroMemory( &requestMessage, sizeof( requestMessage ));
+ requestMessage.PortMessage.u1.s1.DataLength =
+ (USHORT)( sizeof(requestMessage) - sizeof(PORT_MESSAGE) );
+ requestMessage.PortMessage.u1.s1.TotalLength = sizeof(requestMessage);
+ requestMessage.MessageType = XACTSRV_MESSAGE_WAKEUP;
+
+ NtRequestPort(
+ XsConnectionPortHandle,
+ (PPORT_MESSAGE)&requestMessage
+ );
+ }
+
+
+ ExitThread( NO_ERROR );
+
+} // XsProcessApisWrapper
+
+
+VOID
+XsProcessApis (
+ DWORD ThreadNum
+ )
+
+/*++
+
+Routine Description:
+
+ This routine waits for messages to come through the LPC port to
+ the server. When one does, it calls the appropriate routine to
+ handle the API, then replies to the server indicating that the
+ API has completed.
+
+Arguments:
+
+ ThreadNum - thread number for debugging purposes.
+
+Return Value:
+
+ NTSTATUS - STATUS_SUCCESS or reason for failure.
+
+--*/
+
+{
+ NTSTATUS status;
+ NET_API_STATUS error;
+ XACTSRV_REQUEST_MESSAGE request;
+ XACTSRV_REPLY_MESSAGE reply;
+ BOOL sendReply = FALSE;
+ LPTRANSACTION transaction;
+ WORD apiNumber;
+ LPXS_PARAMETER_HEADER header;
+ LPVOID parameters;
+ LPDESC structureDesc;
+ LPDESC auxStructureDesc;
+ LPDESC paramStructureDesc;
+ DWORD newThreadCount;
+#if 0
+ LARGE_INTEGER XactSrvStartTime;
+ LARGE_INTEGER XactSrvEndTime;
+ LARGE_INTEGER PerformanceFrequency;
+#endif
+
+ //
+ // Loop dispatching API requests.
+ //
+ while ( XsTerminating == FALSE ) {
+
+ //
+ // We're waiting to handle another API...
+ //
+ InterlockedIncrement( &XsWaitingApiThreads );
+
+ //
+ // Send the reply to the last message and wait for the next
+ // message. The first time through the loop, there will be
+ // no last message -- reply will be NULL.
+ //
+
+ status = NtReplyWaitReceivePort(
+ XsCommunicationPortHandle,
+ NULL, // PortContext
+ sendReply ? (PPORT_MESSAGE)&reply : NULL,
+ (PPORT_MESSAGE)&request
+ );
+
+ sendReply = TRUE;
+
+ //
+ // Set up the response message to be sent on the next call to
+ // NtReplyWaitReceivePort.
+ //
+ reply.PortMessage.u1.s1.DataLength =
+ sizeof(reply) - sizeof(PORT_MESSAGE);
+ reply.PortMessage.u1.s1.TotalLength = sizeof(reply);
+ reply.PortMessage.u2.ZeroInit = 0;
+ reply.PortMessage.ClientId = request.PortMessage.ClientId;
+ reply.PortMessage.MessageId = request.PortMessage.MessageId;
+
+ IF_DEBUG(THREADS) {
+ SS_PRINT(( "XsProcessApis: Thread %d: NtReplyWaitReceivePort %X, msg %X\n",
+ ThreadNum, status, &request ));
+ }
+
+ if ( status == STATUS_INVALID_PORT_HANDLE
+ || status == STATUS_PORT_DISCONNECTED
+ || status == STATUS_INVALID_HANDLE
+ || XsTerminating
+ || request.PortMessage.u2.s2.Type == LPC_PORT_CLOSED ) {
+
+ //
+ // The port is no longer valid, or XACTSRV is terminating.
+ //
+
+ IF_DEBUG(THREADS) {
+ SS_PRINT(( "XsProcessApis: %s. Thread %ld quitting\n",
+ XsTerminating ?
+ "XACTSRV terminating" : "Port invalid",
+ ThreadNum ));
+ }
+
+ InterlockedDecrement( &XsWaitingApiThreads );
+ return;
+
+ } else if ( !NT_SUCCESS(status) ) {
+
+ IF_DEBUG(ERRORS) {
+ SS_PRINT(( "XsProcessApis: NtReplyWaitReceivePort "
+ "failed: %X\n", status ));
+ }
+ InterlockedDecrement( &XsWaitingApiThreads );
+ return;
+
+ }
+
+ IF_DEBUG(THREADS) {
+ SS_PRINT(( "XsProcessApis: Thread %ld responding to request, "
+ " MessageType %d, XsTerminating %d",
+ ThreadNum, request.MessageType, XsTerminating ));
+ }
+
+ if( InterlockedDecrement( &XsWaitingApiThreads ) == 0 ) {
+
+ HANDLE threadHandle;
+ DWORD threadId;
+
+ //
+ // Are there other threads ready to handle new requests? If not, then
+ // we should spawn a new thread. Since the server synchronously sends
+ // requests to xactsrv, we will never end up with more than
+ // the maximum number of server worker threads + 1.
+ //
+
+ InterlockedIncrement( &XsThreads );
+
+ threadHandle = CreateThread(
+ NULL,
+ 0,
+ (LPTHREAD_START_ROUTINE)XsProcessApisWrapper,
+ (LPVOID)XsThreads,
+ 0,
+ &threadId
+ );
+
+ if ( threadHandle != 0 ) {
+
+ IF_DEBUG(THREADS) {
+ SS_PRINT(( "XsStartXactsrv: Created thread %ld for "
+ "processing APIs\n", XsThreads ));
+ }
+
+ CloseHandle( threadHandle );
+
+ } else {
+
+ IF_DEBUG(THREADS) {
+ SS_PRINT(( "XsStartXactsrv: Unable to create thread %ld for "
+ "processing APIs\n", XsThreads ));
+ }
+
+ InterlockedDecrement( &XsThreads );
+ }
+ }
+
+ switch ( request.MessageType ) {
+
+ case XACTSRV_MESSAGE_SERVER_THREAD_EXIT:
+ //
+ // If we aren't the last thread, we can go away too.
+ //
+ if( XsThreads != 1 ) {
+ //
+ // But first we need to send a response
+ //
+ NtReplyPort(
+ XsCommunicationPortHandle,
+ (PPORT_MESSAGE)&reply );
+
+ return;
+ }
+
+ //
+ // We are the last thread. We must not go away.
+ //
+ break;
+
+ case XACTSRV_MESSAGE_DOWN_LEVEL_API:
+
+ //
+ // Get a pointer to the transaction block from the message.
+ // It is the file server's responsibility to set up this
+ // pointer correctly, and since he is a trusted entity, we
+ // do no checking on the pointer value.
+ //
+
+ transaction = request.Message.DownLevelApi.Transaction;
+ ASSERT( transaction != NULL );
+
+#if 0
+ NtQueryPerformanceCounter(&XactSrvStartTime, &PerformanceFrequency);
+
+ //
+ // Convert frequency from ticks/second to ticks/millisecond
+ //
+
+ PerformanceFrequency = LiXDiv(PerformanceFrequency, 1000);
+
+ if (LiGeq(XactSrvStartTime, transaction->XactSrvTime)) {
+ CHAR Buffer[200];
+ LARGE_INTEGER LpcTime = LiSub(XactSrvStartTime, transaction->XactSrvTime);
+
+ LpcTime = LiDiv(LpcTime, PerformanceFrequency);
+
+ sprintf(Buffer, "XactSrv: LPC Time: %ld milliseconds (%ld)\n", LpcTime.LowPart, LpcTime.HighPart);
+
+ I_BrowserDebugTrace(NULL, Buffer);
+ }
+#endif
+ //
+ // The API number is the first word in the parameters
+ // section, and it is followed by the parameter descriptor
+ // string. After that comes the data descriptor.
+ //
+
+ apiNumber = SmbGetUshort( (LPWORD)transaction->InParameters );
+ paramStructureDesc = (LPDESC)( transaction->InParameters + 2 );
+ structureDesc = paramStructureDesc
+ + strlen( paramStructureDesc ) + 1;
+
+ //
+ // Make sure the API number is in range.
+ //
+
+ if ( apiNumber >=
+ (sizeof(XsApiTable) / sizeof(XS_API_TABLE_ENTRY)) ) {
+ reply.Message.DownLevelApi.Status =
+ STATUS_INVALID_SYSTEM_SERVICE;
+ break;
+ }
+
+ //
+ // Check if the parameter descriptor is valid. If not,
+ // there is obviously something very wrong about this
+ // request.
+ //
+
+ if ( XsApiTable[apiNumber].Params != NULL &&
+ !XsCheckSmbDescriptor( paramStructureDesc,
+ XsApiTable[apiNumber].Params )) {
+ reply.Message.DownLevelApi.Status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ //
+ // Capture the input parameters into a buffer. The API
+ // handler will treat this data as passed-in parameters.
+ //
+
+ header = XsCaptureParameters( transaction, &auxStructureDesc );
+
+ if ( header == NULL ) {
+ reply.Message.DownLevelApi.Status = STATUS_NO_MEMORY;
+ break;
+ }
+
+ //
+ // Initialize header to default values.
+ //
+
+ header->Converter = 0;
+ header->Status = NO_ERROR;
+ header->ClientMachineName =
+ request.Message.DownLevelApi.ClientMachineName;
+
+ header->ClientTransportName = request.Message.DownLevelApi.TransportName;
+
+ header->EncryptionKey = request.Message.DownLevelApi.LanmanSessionKey;
+
+ header->Flags = request.Message.DownLevelApi.Flags;
+
+ header->ServerName = request.Message.DownLevelApi.ServerName;
+
+ parameters = header + 1;
+
+ IF_DEBUG(LPC) {
+
+ SS_PRINT(( "XsProcessApis: received message from %ws at %lx, "
+ "transaction %lx, API %ld on transport %ws\n",
+ header->ClientMachineName, &request,
+ transaction, apiNumber,
+ header->ClientTransportName ));
+ }
+
+ IF_DEBUG(DESC_STRINGS) {
+
+ SS_PRINT(( "XsProcessApis: API %ld, parameters %s, data %s\n",
+ apiNumber, paramStructureDesc, structureDesc ));
+ }
+
+ //
+ // Impersonate the client before calling the API.
+ //
+
+ if ( XsApiTable[apiNumber].ImpersonateClient ) {
+
+ //
+ // BUGBUG: Fail here if request came over a null session!
+ // Otherwise, forging API requests over null sessions
+ // would be a great way for unprivileged clients
+ // to execute privileged APIs.
+ //
+
+ status = NtImpersonateClientOfPort(
+ XsCommunicationPortHandle,
+ (PPORT_MESSAGE)&request
+ );
+
+ if ( !NT_SUCCESS(status) ) {
+
+ IF_DEBUG(ERRORS) {
+ SS_PRINT(( "XsProcessApis: NtImpersonateClientOfPort "
+ "failed: %X\n", status ));
+ }
+
+ reply.Message.DownLevelApi.Status = ERROR_ACCESS_DENIED;
+ break;
+ }
+ }
+
+ //
+ // Call the API processing routine to perform the actual API call.
+ // The called routine should set up parameters, make the actual API
+ // call, and return the status to us.
+ //
+
+ reply.Message.DownLevelApi.Status =
+ XsApiTable[apiNumber].Handler(
+ header,
+ parameters,
+ structureDesc,
+ auxStructureDesc
+ );
+
+ //
+ // Discontinue client impersonation.
+ //
+
+ if ( XsApiTable[apiNumber].ImpersonateClient ) {
+
+ PVOID dummy = NULL;
+
+ status = NtSetInformationThread(
+ NtCurrentThread( ),
+ ThreadImpersonationToken,
+ &dummy, // discontinue impersonation
+ sizeof(PVOID)
+ );
+
+ if ( !NT_SUCCESS(status)) {
+ IF_DEBUG(ERRORS) {
+ SS_PRINT(( "XsProcessApis: NtSetInformationThread "
+ "(revert) failed: %X\n", status ));
+ }
+ // *** Ignore the error.
+ }
+ }
+
+ //
+ // Make sure we return the right error codes
+ //
+
+ if ( header->Status != NERR_Success ) {
+ ConvertApiStatusToDosStatus( header );
+ }
+
+ //
+ // Put the parameters in the transaction and free the parameter
+ // buffer.
+ //
+
+ XsSetParameters( transaction, header, parameters );
+
+ break;
+
+ case XACTSRV_MESSAGE_OPEN_PRINTER: {
+
+ UNICODE_STRING printerName;
+
+ RtlInitUnicodeString(
+ &printerName,
+ (PWCH)request.Message.OpenPrinter.PrinterName
+ );
+
+ if (!OpenPrinter( printerName.Buffer,
+ &reply.Message.OpenPrinter.hPrinter, NULL)) {
+
+ reply.Message.OpenPrinter.Error = GetLastError();
+ SS_PRINT(( "XsProcessApis: OpenPrinter failed: %ld\n",
+ reply.Message.OpenPrinter.Error ));
+ break;
+ }
+
+
+ reply.Message.OpenPrinter.Error = NO_ERROR;
+ break;
+ }
+
+ case XACTSRV_MESSAGE_ADD_JOB_PRINTER:
+ {
+ LPADDJOB_INFO_1 addJob;
+ PRINTER_DEFAULTS prtDefault;
+ DWORD bufferLength;
+ UNICODE_STRING dosName;
+ UNICODE_STRING ntName;
+ BOOL ok;
+ PVOID dummy = NULL;
+
+ //
+ // Allocate space for the add job structure. This buffer
+ // will get the JobId and the spool file path name.
+ //
+
+ bufferLength = sizeof(ADDJOB_INFO_1) +
+ (MAXIMUM_FILENAME_LENGTH * sizeof(TCHAR));
+
+ addJob = (LPADDJOB_INFO_1) LocalAlloc( LPTR, bufferLength );
+ if ( addJob == NULL ) {
+ reply.Message.AddPrintJob.Error = ERROR_NOT_ENOUGH_MEMORY;
+ break;
+ }
+
+ //
+ // Impersonate the client before calling the API.
+ //
+
+ status = NtImpersonateClientOfPort(
+ XsCommunicationPortHandle,
+ (PPORT_MESSAGE)&request
+ );
+
+ if ( !NT_SUCCESS(status) ) {
+
+ IF_DEBUG(ERRORS) {
+ SS_PRINT(( "XsProcessApis: NtImpersonateClientOfPort "
+ "failed: %X\n", status ));
+ }
+
+ LocalFree( addJob );
+ reply.Message.DownLevelApi.Status = ERROR_ACCESS_DENIED;
+ break;
+ }
+
+ //
+ // call ResetJob so that we will pick up the new printer defaults
+ //
+
+ prtDefault.pDatatype = (LPWSTR)-1;
+ prtDefault.pDevMode = (LPDEVMODEW)-1;
+ prtDefault.DesiredAccess = 0;
+
+ ok = ResetPrinter(
+ request.Message.AddPrintJob.hPrinter,
+ &prtDefault
+ );
+
+ if ( !ok ) {
+
+ //
+ // *** Ignore the error. AddJob will use the old defaults
+ // in this case.
+ //
+
+ IF_DEBUG(ERRORS) {
+ DWORD error;
+ error = GetLastError( );
+ SS_PRINT(( "XsProcessApis: ResetPrinter "
+ "failed: %ld\n", error ));
+ }
+ }
+
+ //
+ // Call AddJob to set up the print job and get a job ID
+ // and spool file name.
+ //
+
+ ok = AddJob(
+ request.Message.AddPrintJob.hPrinter,
+ 1,
+ (LPBYTE)addJob,
+ bufferLength,
+ &bufferLength
+ );
+
+ if ( !ok ) {
+ reply.Message.AddPrintJob.Error = GetLastError( );
+ }
+
+ //
+ // Discontinue client impersonation.
+ //
+
+ status = NtSetInformationThread(
+ NtCurrentThread( ),
+ ThreadImpersonationToken,
+ &dummy, // discontinue impersonation
+ sizeof(PVOID)
+ );
+
+ if ( !NT_SUCCESS(status)) {
+ IF_DEBUG(ERRORS) {
+ SS_PRINT(( "XsProcessApis: NtSetInformationThread "
+ "(revert) failed: %X\n", status ));
+ }
+ // *** Ignore the error.
+ }
+
+ if ( !ok ) {
+ SS_PRINT(( "XsProcessApis: AddJob failed, %ld\n",
+ reply.Message.AddPrintJob.Error ));
+ LocalFree( addJob );
+ break;
+ }
+
+ //
+ // Set up the information in the return buffer.
+ //
+
+ reply.Message.AddPrintJob.JobId = addJob->JobId;
+
+ RtlInitUnicodeString( &dosName, addJob->Path );
+
+ status = RtlDosPathNameToNtPathName_U(
+ dosName.Buffer,
+ &ntName,
+ NULL,
+ NULL
+ );
+ if ( !NT_SUCCESS(status) ) {
+ IF_DEBUG(ERRORS) {
+ SS_PRINT(( "XsProcessApis: Dos-to-NT path failed: %X\n",
+ status ));
+ }
+ ntName.Buffer = NULL;
+ ntName.Length = 0;
+ }
+
+ //
+ // Set up return data.
+ //
+
+ reply.Message.AddPrintJob.BufferLength = ntName.Length;
+ reply.Message.AddPrintJob.Error = NO_ERROR;
+ RtlCopyMemory(
+ request.Message.AddPrintJob.Buffer,
+ ntName.Buffer,
+ ntName.Length
+ );
+
+ //
+ // Free allocated resources.
+ //
+
+ LocalFree( addJob );
+ if ( ntName.Buffer != NULL ) {
+ RtlFreeHeap( RtlProcessHeap( ), 0, ntName.Buffer );
+ }
+
+ break;
+ }
+
+ case XACTSRV_MESSAGE_SCHD_JOB_PRINTER:
+
+ //
+ // Call ScheduleJob( ) to indicate that we're done writing to
+ // the spool file.
+ //
+
+ if ( !ScheduleJob(
+ request.Message.SchedulePrintJob.hPrinter,
+ request.Message.SchedulePrintJob.JobId ) ) {
+
+ reply.Message.SchedulePrintJob.Error = GetLastError( );
+ SS_PRINT(( "XsProcessApis: ScheduleJob failed, %ld\n",
+ reply.Message.SchedulePrintJob.Error ));
+ break;
+ }
+
+ reply.Message.SchedulePrintJob.Error = NO_ERROR;
+ break;
+
+ case XACTSRV_MESSAGE_CLOSE_PRINTER:
+
+ if ( !ClosePrinter( request.Message.ClosePrinter.hPrinter ) ) {
+ reply.Message.ClosePrinter.Error = GetLastError( );
+ SS_PRINT(( "XsProcessApis: ClosePrinter failed: %ld\n",
+ reply.Message.ClosePrinter.Error ));
+ break;
+ }
+
+ reply.Message.ClosePrinter.Error = NO_ERROR;
+ break;
+
+ case XACTSRV_MESSAGE_MESSAGE_SEND:
+ {
+ LPTSTR sender;
+
+ error = NetpGetComputerName( &sender );
+
+ if ( error != NO_ERROR ) {
+ SS_PRINT(( "XsProcessApis: NetpGetComputerName failed: %ld\n",
+ error ));
+ reply.Message.MessageBufferSend.Error = error;
+ break;
+ }
+
+ error = NetMessageBufferSend(
+ NULL,
+
+ //
+ // BUGBUG - the following LPTSTR typecast is WRONG -
+ // it must be fixed in ntos\srv\scavengr.c which
+ // should pass in a LPWSTR if built for unicode or
+ // convert the UNICODE_STRING to an OEM_STRING and
+ // pass a pointer to the buffer field, as it does
+ // now
+ //
+
+ //FIXFIX
+ (LPTSTR)request.Message.MessageBufferSend.Receipient,
+ //request.Message.MessageBufferSend.Receipient,
+ //ENDFIX
+ sender,
+ request.Message.MessageBufferSend.Buffer,
+ request.Message.MessageBufferSend.BufferLength
+ );
+
+ if ( error != NO_ERROR ) {
+ SS_PRINT(( "XsProcessApis: NetMessageBufferSend failed: %ld\n",
+ error ));
+ }
+
+ (void) NetApiBufferFree( sender );
+
+ reply.Message.MessageBufferSend.Error = error;
+ break;
+ }
+
+ case XACTSRV_MESSAGE_LSREQUEST:
+ SS_PRINT(( "LSREQUEST User: %ws\n", request.Message.LSRequest.UserName ));
+ {
+ NT_LS_DATA NtLSData;
+
+ NtLSData.DataType = NT_LS_USER_NAME;
+ NtLSData.Data = request.Message.LSRequest.UserName;
+ NtLSData.IsAdmin = request.Message.LSRequest.IsAdmin;
+
+ reply.Message.LSRequest.Status = NtLicenseRequest (
+ SsData.ServerProductName,
+ SsData.szVersionNumber,
+ (LS_HANDLE *)&reply.Message.LSRequest.hLicense,
+ &NtLSData
+ );
+
+ if( !NT_SUCCESS( reply.Message.LSRequest.Status ) ) {
+ //
+ // We need to return the 'same old' error code that clients are used to
+ // getting for when the server is full
+ //
+ SS_PRINT(("LSREQUEST returns status %X, mapping to %X\n",
+ reply.Message.LSRequest.Status, STATUS_REQUEST_NOT_ACCEPTED ));
+ reply.Message.LSRequest.Status = STATUS_REQUEST_NOT_ACCEPTED;
+ }
+
+ break;
+ }
+
+ case XACTSRV_MESSAGE_LSRELEASE:
+
+ SS_PRINT(( "LSRELEASE Handle: %X\n", request.Message.LSRelease.hLicense ));
+ NtLSFreeHandle( (LS_HANDLE)request.Message.LSRelease.hLicense );
+ break;
+
+#ifdef SRV_PNP_POWER
+
+ case XACTSRV_MESSAGE_PNP:
+ {
+
+ UNICODE_STRING transportName;
+ ULONG numberOfBindings = 0;
+ PVOID dummy = NULL;
+
+ transportName.MaximumLength =
+ transportName.Length = request.Message.Pnp.TransportName.Length;
+
+ transportName.Buffer = MIDL_user_allocate( transportName.Length );
+
+ if( transportName.Buffer == NULL ) {
+ break;
+ }
+
+ RtlCopyMemory( transportName.Buffer,
+ request.Message.Pnp.TransportName.Buffer,
+ transportName.Length
+ );
+
+ status = NtImpersonateClientOfPort(
+ XsCommunicationPortHandle,
+ (PPORT_MESSAGE)&request
+ );
+
+ //
+ // Now process the PNP command
+ //
+ if( request.Message.Pnp.Bind == TRUE ) {
+
+ //
+ // Bind to the transport. First bind the primary server name, then bind all
+ // of the secondary names. These calls will log errors as necessary.
+ //
+ BindToTransport(
+ NULL, // ValueName
+ REG_SZ, // ValueType
+ transportName.Buffer, // ValueData (transport name)
+ transportName.Length, // ValueLength
+ &numberOfBindings, // Context
+ NULL // EntryContext
+ );
+
+ BindOptionalNames(
+ NULL, // ValueName
+ REG_SZ, // ValueType
+ transportName.Buffer, // ValueData (transport name)
+ transportName.Length, // ValueLength
+ &numberOfBindings, // Context
+ NULL // EntryContext
+ );
+
+ } else {
+ //
+ // Unbind from the transport
+ //
+ DbgPrint( "SRVSVC: PNP unbind from %wZ\n", &transportName );
+ }
+
+ //
+ // Discontinue client impersonation.
+ //
+ status = NtSetInformationThread(
+ NtCurrentThread( ),
+ ThreadImpersonationToken,
+ &dummy, // discontinue impersonation
+ sizeof(PVOID)
+ );
+
+ MIDL_user_free( transportName.Buffer );
+
+ break;
+ }
+#endif
+
+ default:
+
+ SS_ASSERT( FALSE );
+
+ }
+
+#if 0
+
+ if ( request.MessageType == XACTSRV_MESSAGE_DOWN_LEVEL_API ) {
+ NtQueryPerformanceCounter(&XactSrvEndTime, NULL);
+
+ if (LiGeq(XactSrvEndTime, XactSrvStartTime)) {
+ CHAR Buffer[200];
+ LARGE_INTEGER XsTime = LiSub(XactSrvEndTime, XactSrvStartTime);
+
+ XsTime = LiDiv(XsTime, PerformanceFrequency);
+
+ sprintf(Buffer, "XactSrv: Xactsrv Time: %ld milliseconds (%ld)\n", XsTime.LowPart/10000, XsTime.HighPart);
+
+ I_BrowserDebugTrace(NULL, Buffer);
+ }
+
+ transaction->XactSrvTime = XactSrvEndTime;
+ }
+
+#endif
+ }
+
+} // XsProcessApis
+
+
+
+VOID
+ConvertApiStatusToDosStatus(
+ LPXS_PARAMETER_HEADER Header
+ )
+/*++
+
+Routine Description:
+
+ This routine converts an api return status to status expected by
+ downlevel.
+
+Arguments:
+
+ Header - structure containing the status.
+
+Return Value:
+
+--*/
+{
+ WORD dosStatus;
+
+ switch ( Header->Status ) {
+ case ERROR_SPECIAL_ACCOUNT:
+ case ERROR_SPECIAL_GROUP:
+ case ERROR_SPECIAL_USER:
+ case ERROR_INVALID_LOGON_TYPE:
+ dosStatus = ERROR_INVALID_PARAMETER;
+ break;
+
+ case ERROR_DEPENDENT_SERVICES_RUNNING:
+ dosStatus = NERR_ServiceCtlNotValid;
+ break;
+
+ case ERROR_INVALID_DOMAINNAME:
+ dosStatus = NERR_NotLocalDomain;
+ break;
+
+ case ERROR_NO_SUCH_USER:
+ dosStatus = NERR_UserNotFound;
+ break;
+
+ case ERROR_ALIAS_EXISTS:
+ dosStatus = NERR_GroupExists;
+ break;
+
+ case NERR_BadServiceName:
+ dosStatus = NERR_ServiceNotInstalled;
+ break;
+
+ case ERROR_ILL_FORMED_PASSWORD:
+ case NERR_PasswordTooRecent:
+ dosStatus = ERROR_INVALID_PASSWORD;
+ break;
+
+ case ERROR_PASSWORD_RESTRICTION:
+ dosStatus = NERR_PasswordHistConflict;
+ break;
+
+ case ERROR_ACCOUNT_RESTRICTION:
+ dosStatus = NERR_PasswordTooRecent;
+ break;
+
+ case ERROR_PASSWORD_EXPIRED:
+ case ERROR_PASSWORD_MUST_CHANGE:
+ dosStatus = NERR_PasswordExpired;
+ break;
+
+ case ERROR_INVALID_PRINTER_NAME:
+ dosStatus = NERR_QNotFound;
+ break;
+
+ case ERROR_NO_BROWSER_SERVERS_FOUND:
+
+ //
+ // Down level clients don't understand how to deal with
+ // the "No browser server" error, so we turn it into success.
+ //
+ // This seems wrong to me, but it is what WfW does in the
+ // same circumstance.
+ //
+
+ if ( !(Header->Flags & XS_FLAGS_NT_CLIENT) ) {
+ dosStatus = NERR_Success;
+ } else {
+ dosStatus = Header->Status;
+ }
+ break;
+
+ default:
+
+ //
+ // make sure it's a valid lm error code
+ //
+
+ if ( (Header->Status > ERROR_VC_DISCONNECTED) &&
+ ((Header->Status < NERR_BASE) ||
+ (Header->Status > MAX_NERR)) ) {
+
+ NTSTATUS status;
+ LPWSTR substring[1];
+ WCHAR errorString[10];
+ UNICODE_STRING unicodeString;
+
+ substring[0] = errorString;
+ unicodeString.MaximumLength = 10 * sizeof(WCHAR);
+ unicodeString.Buffer = errorString;
+
+ status = RtlIntegerToUnicodeString(
+ (ULONG) Header->Status,
+ 10,
+ &unicodeString
+ );
+
+ if ( NT_SUCCESS( status ) ) {
+ SsLogEvent(
+ EVENT_SRV_CANT_MAP_ERROR,
+ 1,
+ substring,
+ NO_ERROR
+ );
+ }
+
+ dosStatus = ERROR_UNEXP_NET_ERR;
+ SS_PRINT(( "srvsvc: unmapped error %d from xactsrv.\n",
+ Header->Status )) ;
+
+ } else {
+
+ //
+ // No change
+ //
+
+ return;
+ }
+ }
+
+ Header->Status = dosStatus;
+ return;
+
+} // ConvertApiStatusToDosStatus
+
diff --git a/private/net/svcdlls/srvsvc/srvnames.h b/private/net/svcdlls/srvsvc/srvnames.h
new file mode 100644
index 000000000..61ba4fecd
--- /dev/null
+++ b/private/net/svcdlls/srvsvc/srvnames.h
@@ -0,0 +1,21 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ srvnames.h
+
+Abstract:
+
+ Private header file which defines the Server Service names.
+
+Author:
+
+ Dan Lafferty (danl) 07-Jan-1993
+
+Revision History:
+
+--*/
+
+#define SERVER_INTERFACE_NAME TEXT("srvsvc")
diff --git a/private/net/svcdlls/srvsvc/srvsvc.acf b/private/net/svcdlls/srvsvc/srvsvc.acf
new file mode 100644
index 000000000..5027f010a
--- /dev/null
+++ b/private/net/svcdlls/srvsvc/srvsvc.acf
@@ -0,0 +1,44 @@
+[ implicit_handle( handle_t srvsvc_bhandle ) ]
+
+interface srvsvc
+
+{
+
+typedef [allocate(all_nodes)] LPCHARDEV_INFO_0;
+typedef [allocate(all_nodes)] LPCHARDEV_INFO_1;
+
+typedef [allocate(all_nodes)] LPCHARDEVQ_INFO_0;
+typedef [allocate(all_nodes)] LPCHARDEVQ_INFO_1;
+
+typedef [allocate(all_nodes)] LPCONNECTION_INFO_0;
+typedef [allocate(all_nodes)] LPCONNECTION_INFO_1;
+
+typedef [allocate(all_nodes)] LPFILE_INFO_2;
+typedef [allocate(all_nodes)] LPFILE_INFO_3;
+
+typedef [allocate(all_nodes)] LPSESSION_INFO_0;
+typedef [allocate(all_nodes)] LPSESSION_INFO_1;
+typedef [allocate(all_nodes)] LPSESSION_INFO_2;
+typedef [allocate(all_nodes)] LPSESSION_INFO_10;
+typedef [allocate(all_nodes)] LPSESSION_INFO_502;
+
+typedef [allocate(all_nodes)] LPSHARE_INFO_0;
+typedef [allocate(all_nodes)] LPSHARE_INFO_1;
+typedef [allocate(all_nodes)] LPSHARE_INFO_2;
+typedef [allocate(all_nodes)] LPSHARE_INFO_502_I;
+
+typedef [allocate(all_nodes)] LPSERVER_INFO_100;
+typedef [allocate(all_nodes)] LPSERVER_INFO_101;
+typedef [allocate(all_nodes)] LPSERVER_INFO_102;
+typedef [allocate(all_nodes)] LPSERVER_INFO_402;
+typedef [allocate(all_nodes)] LPSERVER_INFO_403;
+typedef [allocate(all_nodes)] LPSERVER_INFO_502;
+typedef [allocate(all_nodes)] LPSERVER_INFO_503;
+typedef [allocate(all_nodes)] LPSERVER_INFO_599;
+
+typedef [allocate(all_nodes)] LPDISK_INFO;
+
+typedef [allocate(all_nodes)] LPSERVER_TRANSPORT_INFO_0;
+typedef [allocate(all_nodes)] LPSERVER_TRANSPORT_INFO_1;
+
+}
diff --git a/private/net/svcdlls/srvsvc/srvsvc.idl b/private/net/svcdlls/srvsvc/srvsvc.idl
new file mode 100644
index 000000000..49c31347e
--- /dev/null
+++ b/private/net/svcdlls/srvsvc/srvsvc.idl
@@ -0,0 +1,1078 @@
+/*++
+
+Copyright (c) 1990 Microsoft Corporation
+
+Module Name:
+
+ SRVSVC.IDL
+
+Abstract:
+
+ Contains the Netr (Net Remote) RPC interface specification for the
+ API associated with the Server Service. This includes API from the
+ following catagories:
+
+ NetCharDev
+ NetCharDevQ
+ NetConnection
+ NetFile
+ NetRemoteTOD
+ NetServer
+ NetServerTransport
+ NetSession
+ NetShare
+ NetStatisticsGet (server half)
+
+ Also contains the RPC specific data structures for these API.
+
+Author:
+
+ Dan Lafferty (danl) 06-Feb-1991
+
+Environment:
+
+ User Mode - Win32 - MIDL
+
+Revision History:
+
+ 07-May-1991 danl
+ Updated with RPC unionSs and latest structures.
+
+ 06-Feb-1991 danl
+ created
+
+ 08-Aug-1992 johnsona
+ added share info level 502.
+
+--*/
+
+
+//
+// Interface Attributes
+//
+
+[
+ uuid(4B324FC8-1670-01D3-1278-5A47BF6EE188),
+ version(3.0),
+#ifdef __midl
+ ms_union,
+#endif // __midl
+ pointer_default(unique)
+]
+
+
+interface srvsvc
+
+
+{
+
+import "import.idl";
+#include <lmcons.h>
+
+//
+// BUGBUG - take this definition out when midl understands LPWSTR etc
+//
+
+#ifdef UNICODE
+#define LPTSTR wchar_t*
+#define TCHAR wchar_t
+#else
+#define TCHAR char
+#endif
+
+typedef [handle] LPTSTR SRVSVC_HANDLE;
+
+//
+// CharDev API
+//
+
+//
+// Structures - NetrCharDev
+//
+typedef struct _CHARDEV_INFO_0_CONTAINER {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] LPCHARDEV_INFO_0 Buffer;
+} CHARDEV_INFO_0_CONTAINER, *PCHARDEV_INFO_0_CONTAINER, *LPCHARDEV_INFO_0_CONTAINER;
+
+typedef struct _CHARDEV_INFO_1_CONTAINER {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] LPCHARDEV_INFO_1 Buffer;
+} CHARDEV_INFO_1_CONTAINER, *PCHARDEV_INFO_1_CONTAINER, *LPCHARDEV_INFO_1_CONTAINER;
+
+typedef struct _CHARDEV_ENUM_STRUCT {
+ DWORD Level;
+ [switch_is(Level)] union _CHARDEV_ENUM_UNION{
+ [case(0)]
+ CHARDEV_INFO_0_CONTAINER *Level0;
+ [case(1)]
+ CHARDEV_INFO_1_CONTAINER *Level1;
+ [default]
+ ;
+ } CharDevInfo;
+
+}CHARDEV_ENUM_STRUCT, *PCHARDEV_ENUM_STRUCT, *LPCHARDEV_ENUM_STRUCT;
+
+typedef [switch_type(unsigned long)] union _CHARDEV_INFO { // for Get & Set Info
+ [case(0)]
+ LPCHARDEV_INFO_0 CharDevInfo0;
+ [case(1)]
+ LPCHARDEV_INFO_1 CharDevInfo1;
+ [default]
+ ;
+} CHARDEV_INFO, *PCHARDEV_INFO, *LPCHARDEV_INFO;
+
+//
+// Function Prototypes - NetrCharDev
+//
+
+NET_API_STATUS
+NetrCharDevEnum (
+ [in,string,unique] SRVSVC_HANDLE ServerName,
+ [in,out] LPCHARDEV_ENUM_STRUCT InfoStruct,
+ [in] DWORD PreferedMaximumLength,
+ [out] LPDWORD TotalEntries,
+ [in,out,unique] LPDWORD ResumeHandle
+ );
+
+NET_API_STATUS
+NetrCharDevGetInfo (
+ [in,string,unique] SRVSVC_HANDLE ServerName,
+ [in,string] LPTSTR DevName,
+ [in] DWORD Level,
+ [out, switch_is(Level)] LPCHARDEV_INFO InfoStruct
+ );
+
+NET_API_STATUS
+NetrCharDevControl (
+ [in,string,unique] SRVSVC_HANDLE ServerName,
+ [in,string] LPTSTR DevName,
+ [in] DWORD Opcode
+ );
+
+
+//
+// CharDevQ API
+//
+
+//
+// Structures - NetrCharDevQ
+//
+
+typedef struct _CHARDEVQ_INFO_0_CONTAINER {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] LPCHARDEVQ_INFO_0 Buffer;
+} CHARDEVQ_INFO_0_CONTAINER, *PCHARDEVQ_INFO_0_CONTAINER, *LPCHARDEVQ_INFO_0_CONTAINER;
+
+typedef struct _CHARDEVQ_INFO_1_CONTAINER {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] LPCHARDEVQ_INFO_1 Buffer;
+} CHARDEVQ_INFO_1_CONTAINER, *PCHARDEVQ_INFO_1_CONTAINER, *LPCHARDEVQ_INFO_1_CONTAINER;
+
+typedef struct _CHARDEVQ_ENUM_STRUCT {
+ DWORD Level;
+ [switch_is(Level)] union _CHARDEVQ_ENUM_UNION{
+ [case(0)]
+ CHARDEVQ_INFO_0_CONTAINER *Level0;
+ [case(1)]
+ CHARDEVQ_INFO_1_CONTAINER *Level1;
+ [default]
+ ;
+ } CharDevQInfo;
+
+}CHARDEVQ_ENUM_STRUCT, *PCHARDEVQ_ENUM_STRUCT, *LPCHARDEVQ_ENUM_STRUCT;
+
+typedef [switch_type(unsigned long)] union _CHARDEVQ_INFO { // for Get & Set Info
+ [case(0)]
+ LPCHARDEVQ_INFO_0 CharDevQInfo0;
+ [case(1)]
+ LPCHARDEVQ_INFO_1 CharDevQInfo1;
+ [case(1002)]
+ LPCHARDEVQ_INFO_1002 CharDevQInfo11002;
+ [case(1003)]
+ LPCHARDEVQ_INFO_1003 CharDevQInfo1003;
+ [default]
+ ;
+} CHARDEVQ_INFO, *PCHARDEVQ_INFO, *LPCHARDEVQ_INFO;
+
+//
+// Function Prototypes - NetrCharDevQ
+//
+
+NET_API_STATUS
+NetrCharDevQEnum (
+ [in,string,unique] SRVSVC_HANDLE ServerName,
+ [in,string,unique] LPTSTR UserName,
+ [in,out] LPCHARDEVQ_ENUM_STRUCT InfoStruct,
+ [in] DWORD PreferedMaximumLength,
+ [out] LPDWORD TotalEntries,
+ [in,out,unique] LPDWORD ResumeHandle
+ );
+
+NET_API_STATUS
+NetrCharDevQGetInfo (
+ [in,string,unique] SRVSVC_HANDLE ServerName,
+ [in,string] LPTSTR QueueName,
+ [in,string] LPTSTR UserName,
+ [in] DWORD Level,
+ [out, switch_is(Level)] LPCHARDEVQ_INFO InfoStruct
+ );
+
+NET_API_STATUS
+NetrCharDevQSetInfo (
+ [in,string,unique] SRVSVC_HANDLE ServerName,
+ [in,string] LPTSTR QueueName,
+ [in] DWORD Level,
+ [in, switch_is(Level)] LPCHARDEVQ_INFO CharDevQInfo,
+ [in,out,unique] LPDWORD ParmErr
+ );
+
+NET_API_STATUS
+NetrCharDevQPurge (
+ [in,string,unique] SRVSVC_HANDLE ServerName,
+ [in,string] LPTSTR QueueName
+ );
+
+NET_API_STATUS
+NetrCharDevQPurgeSelf (
+ [in,string,unique] SRVSVC_HANDLE ServerName,
+ [in,string] LPTSTR QueueName,
+ [in,string] LPTSTR ComputerName
+ );
+
+//
+// Connection API
+//
+
+//
+// Structures - NetrConnection
+//
+
+typedef struct _CONNECT_INFO_0_CONTAINER {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] LPCONNECTION_INFO_0 Buffer;
+} CONNECT_INFO_0_CONTAINER, *PCONNECT_INFO_0_CONTAINER, *LPCONNECT_INFO_0_CONTAINER;
+
+typedef struct _CONNECT_INFO_1_CONTAINER {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] LPCONNECTION_INFO_1 Buffer;
+} CONNECT_INFO_1_CONTAINER, *PCONNECT_INFO_1_CONTAINER, *LPCONNECT_INFO_1_CONTAINER;
+
+typedef struct _CONNECT_ENUM_STRUCT {
+ DWORD Level;
+ [switch_is(Level)] union _CONNECT_ENUM_UNION{
+ [case(0)]
+ CONNECT_INFO_0_CONTAINER *Level0;
+ [case(1)]
+ CONNECT_INFO_1_CONTAINER *Level1;
+ [default]
+ ;
+ } ConnectInfo;
+
+}CONNECT_ENUM_STRUCT, *PCONNECT_ENUM_STRUCT, *LPCONNECT_ENUM_STRUCT;
+
+
+//
+// Function Prototypes - NetrConnection
+//
+
+NET_API_STATUS
+NetrConnectionEnum (
+ [in,string,unique] SRVSVC_HANDLE ServerName,
+ [in,string,unique] LPTSTR Qualifier,
+ [in,out] LPCONNECT_ENUM_STRUCT InfoStruct,
+ [in] DWORD PreferedMaximumLength,
+ [out] LPDWORD TotalEntries,
+ [in,out,unique] LPDWORD ResumeHandle
+ );
+
+//
+// File API
+//
+
+//
+// Structures - NetrFile
+//
+
+typedef struct _FILE_INFO_2_CONTAINER {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] LPFILE_INFO_2 Buffer;
+} FILE_INFO_2_CONTAINER, *PFILE_INFO_2_CONTAINER, *LPFILE_INFO_2_CONTAINER;
+
+typedef struct _FILE_INFO_3_CONTAINER {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] LPFILE_INFO_3 Buffer;
+} FILE_INFO_3_CONTAINER, *PFILE_INFO_3_CONTAINER, *LPFILE_INFO_3_CONTAINER;
+
+
+typedef struct _FILE_ENUM_STRUCT {
+ DWORD Level;
+ [switch_is(Level)] union _FILE_ENUM_UNION {
+ [case(2)]
+ FILE_INFO_2_CONTAINER *Level2;
+ [case(3)]
+ FILE_INFO_3_CONTAINER *Level3;
+ [default]
+ ;
+ } FileInfo;
+
+}FILE_ENUM_STRUCT, *PFILE_ENUM_STRUCT, *LPFILE_ENUM_STRUCT;
+
+typedef [switch_type(unsigned long)] union _FILE_INFO { // for Get & Set Info
+ [case(2)]
+ LPFILE_INFO_2 FileInfo2;
+ [case(3)]
+ LPFILE_INFO_3 FileInfo3;
+ [default]
+ ;
+} FILE_INFO, *PFILE_INFO, *LPFILE_INFO;
+
+//
+// Function Prototypes - NetrFile
+//
+
+NET_API_STATUS
+NetrFileEnum (
+ [in,string,unique] SRVSVC_HANDLE ServerName,
+ [in,string,unique] LPTSTR BasePath,
+ [in,string,unique] LPTSTR UserName,
+ [in,out] PFILE_ENUM_STRUCT InfoStruct,
+ [in] DWORD PreferedMaximumLength,
+ [out] LPDWORD TotalEntries,
+ [in,out,unique] LPDWORD ResumeHandle
+ );
+
+NET_API_STATUS
+NetrFileGetInfo (
+ [in,string,unique] SRVSVC_HANDLE ServerName,
+ [in] DWORD FileId,
+ [in] DWORD Level,
+ [out, switch_is(Level)] LPFILE_INFO InfoStruct
+ );
+
+NET_API_STATUS
+NetrFileClose (
+ [in,string,unique] SRVSVC_HANDLE ServerName,
+ [in] DWORD FileId
+ );
+
+//
+// Session API
+//
+
+//
+// Structures - NetrSession
+//
+
+typedef struct _SESSION_INFO_0_CONTAINER {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] LPSESSION_INFO_0 Buffer;
+} SESSION_INFO_0_CONTAINER, *PSESSION_INFO_0_CONTAINER, *LPSESSION_INFO_0_CONTAINER;
+
+typedef struct _SESSION_INFO_1_CONTAINER {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] LPSESSION_INFO_1 Buffer;
+} SESSION_INFO_1_CONTAINER, *PSESSION_INFO_1_CONTAINER, *LPSESSION_INFO_1_CONTAINER;
+
+typedef struct _SESSION_INFO_2_CONTAINER {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] LPSESSION_INFO_2 Buffer;
+} SESSION_INFO_2_CONTAINER, *PSESSION_INFO_2_CONTAINER, *LPSESSION_INFO_2_CONTAINER;
+
+typedef struct _SESSION_INFO_10_CONTAINER {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] LPSESSION_INFO_10 Buffer;
+} SESSION_INFO_10_CONTAINER, *PSESSION_INFO_10_CONTAINER, *LPSESSION_INFO_10_CONTAINER;
+
+typedef struct _SESSION_INFO_502_CONTAINER {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] LPSESSION_INFO_502 Buffer;
+} SESSION_INFO_502_CONTAINER, *PSESSION_INFO_502_CONTAINER, *LPSESSION_INFO_502_CONTAINER;
+
+typedef struct _SESSION_ENUM_STRUCT {
+ DWORD Level;
+ [switch_is(Level)] union _SESSION_ENUM_UNION {
+ [case(0)]
+ SESSION_INFO_0_CONTAINER *Level0;
+ [case(1)]
+ SESSION_INFO_1_CONTAINER *Level1;
+ [case(2)]
+ SESSION_INFO_2_CONTAINER *Level2;
+ [case(10)]
+ SESSION_INFO_10_CONTAINER *Level10;
+ [case(502)]
+ SESSION_INFO_502_CONTAINER *Level502;
+ [default]
+ ;
+ } SessionInfo;
+
+}SESSION_ENUM_STRUCT, *PSESSION_ENUM_STRUCT, *LPSESSION_ENUM_STRUCT;
+
+//
+// Function Prototypes - NetrSession
+//
+
+NET_API_STATUS
+NetrSessionEnum (
+ [in,string,unique] SRVSVC_HANDLE ServerName,
+ [in,string,unique] LPTSTR ClientName,
+ [in,string,unique] LPTSTR UserName,
+ [in,out] PSESSION_ENUM_STRUCT InfoStruct,
+ [in] DWORD PreferedMaximumLength,
+ [out] LPDWORD TotalEntries,
+ [in,out,unique] LPDWORD ResumeHandle
+ );
+
+
+NET_API_STATUS
+NetrSessionDel (
+ [in,string,unique] SRVSVC_HANDLE ServerName,
+ [in,string,unique] LPTSTR ClientName,
+ [in,string,unique] LPTSTR UserName
+ );
+
+//
+// Share API
+//
+
+//
+// Structures - NetrShare
+//
+
+//
+// Internal 502 and 1501 structures used for passing and/or returning
+// self-relative security descriptors.
+//
+
+typedef struct _SHARE_INFO_502_I {
+ [string] LPTSTR shi502_netname;
+ DWORD shi502_type;
+ [string] LPTSTR shi502_remark;
+ DWORD shi502_permissions;
+ DWORD shi502_max_uses;
+ DWORD shi502_current_uses;
+ [string] LPTSTR shi502_path;
+ [string] LPTSTR shi502_passwd;
+ DWORD shi502_reserved;
+ [size_is(shi502_reserved)] PUCHAR shi502_security_descriptor;
+} SHARE_INFO_502_I, *PSHARE_INFO_502_I, *LPSHARE_INFO_502_I;
+
+typedef struct _SHARE_INFO_1501_I {
+ DWORD shi1501_reserved;
+ [size_is(shi1501_reserved)] PUCHAR shi1501_security_descriptor;
+} SHARE_INFO_1501_I, *PSHARE_INFO_1501_I, *LPSHARE_INFO_1501_I;
+
+//
+// Structures for NetShareEnum
+//
+
+typedef struct _SHARE_INFO_0_CONTAINER {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] LPSHARE_INFO_0 Buffer;
+} SHARE_INFO_0_CONTAINER;
+
+typedef struct _SHARE_INFO_1_CONTAINER {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] LPSHARE_INFO_1 Buffer;
+} SHARE_INFO_1_CONTAINER;
+
+typedef struct _SHARE_INFO_2_CONTAINER {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] LPSHARE_INFO_2 Buffer;
+} SHARE_INFO_2_CONTAINER, *PSHARE_INFO_2_CONTAINER, *LPSHARE_INFO_2_CONTAINER;
+
+typedef struct _SHARE_INFO_502_CONTAINER {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] LPSHARE_INFO_502_I Buffer;
+} SHARE_INFO_502_CONTAINER, *PSHARE_INFO_502_CONTAINER, *LPSHARE_INFO_502_CONTAINER;
+
+
+typedef struct _SHARE_ENUM_STRUCT {
+ DWORD Level;
+ [switch_is(Level)] union _SHARE_ENUM_UNION {
+ [case(0)]
+ SHARE_INFO_0_CONTAINER *Level0;
+ [case(1)]
+ SHARE_INFO_1_CONTAINER *Level1;
+ [case(2)]
+ SHARE_INFO_2_CONTAINER *Level2;
+ [case(502)]
+ SHARE_INFO_502_CONTAINER *Level502;
+ [default]
+ ;
+ } ShareInfo;
+
+}SHARE_ENUM_STRUCT, *PSHARE_ENUM_STRUCT, *LPSHARE_ENUM_STRUCT;
+
+typedef [switch_type(unsigned long)] union _SHARE_INFO { // for Get & Set Info
+ [case(0)]
+ LPSHARE_INFO_0 ShareInfo0;
+ [case(1)]
+ LPSHARE_INFO_1 ShareInfo1;
+ [case(2)]
+ LPSHARE_INFO_2 ShareInfo2;
+ [case(502)]
+ LPSHARE_INFO_502_I ShareInfo502;
+ [case(1004)]
+ LPSHARE_INFO_1004 ShareInfo1004;
+ [case(1006)]
+ LPSHARE_INFO_1006 ShareInfo1006;
+ [case(1501)]
+ LPSHARE_INFO_1501_I ShareInfo1501;
+ [default]
+ ;
+ [case(1005)]
+ LPSHARE_INFO_1005 ShareInfo1005;
+} SHARE_INFO, *PSHARE_INFO, *LPSHARE_INFO;
+
+//
+// Function Prototypes - NetrShare
+//
+
+NET_API_STATUS
+NetrShareAdd (
+ [in,string,unique] SRVSVC_HANDLE ServerName,
+ [in] DWORD Level,
+ [in, switch_is(Level)] LPSHARE_INFO InfoStruct,
+ [in,out,unique] LPDWORD ParmErr
+ );
+
+NET_API_STATUS
+NetrShareEnum (
+ [in,string,unique] SRVSVC_HANDLE ServerName,
+ [in,out] LPSHARE_ENUM_STRUCT InfoStruct,
+ [in] DWORD PreferedMaximumLength,
+ [out] LPDWORD TotalEntries,
+ [in,out,unique] LPDWORD ResumeHandle
+ );
+
+NET_API_STATUS
+NetrShareGetInfo (
+ [in,string,unique] SRVSVC_HANDLE ServerName,
+ [in,string] LPTSTR NetName,
+ [in] DWORD Level,
+ [out, switch_is(Level)] LPSHARE_INFO InfoStruct
+ );
+
+NET_API_STATUS
+NetrShareSetInfo (
+ [in,string,unique] SRVSVC_HANDLE ServerName,
+ [in,string] LPTSTR NetName,
+ [in] DWORD Level,
+ [in, switch_is(Level)] LPSHARE_INFO ShareInfo,
+ [in,out,unique] LPDWORD ParmErr
+ );
+
+NET_API_STATUS
+NetrShareDel (
+ [in,string,unique] SRVSVC_HANDLE ServerName,
+ [in,string] LPTSTR NetName,
+ [in] DWORD Reserved
+ );
+
+NET_API_STATUS
+NetrShareDelSticky (
+ [in,string,unique] SRVSVC_HANDLE ServerName,
+ [in,string] LPTSTR NetName,
+ [in] DWORD Reserved
+ );
+
+NET_API_STATUS
+NetrShareCheck (
+ [in,string,unique] SRVSVC_HANDLE ServerName,
+ [in,string] LPTSTR Device,
+ [out] LPDWORD Type
+ );
+
+//
+// Server API
+//
+
+//
+// Structures - NetrServer
+//
+
+typedef [switch_type(unsigned long)] union _SERVER_INFO { // For Get & Set Info
+ [case(100)]
+ LPSERVER_INFO_100 ServerInfo100;
+ [case(101)]
+ LPSERVER_INFO_101 ServerInfo101;
+ [case(102)]
+ LPSERVER_INFO_102 ServerInfo102;
+ [case(402)]
+ LPSERVER_INFO_402 ServerInfo402;
+ [case(403)]
+ LPSERVER_INFO_403 ServerInfo403;
+ [case(502)]
+ LPSERVER_INFO_502 ServerInfo502;
+ [case(503)]
+ LPSERVER_INFO_503 ServerInfo503;
+ [case(599)]
+ LPSERVER_INFO_599 ServerInfo599;
+ [case(1005)]
+ LPSERVER_INFO_1005 ServerInfo1005;
+ [case(1107)]
+ LPSERVER_INFO_1107 ServerInfo1107;
+ [case(1010)]
+ LPSERVER_INFO_1010 ServerInfo1010;
+ [case(1016)]
+ LPSERVER_INFO_1016 ServerInfo1016;
+ [case(1017)]
+ LPSERVER_INFO_1017 ServerInfo1017;
+ [case(1018)]
+ LPSERVER_INFO_1018 ServerInfo1018;
+ [case(1501)]
+ LPSERVER_INFO_1501 ServerInfo1501;
+ [case(1502)]
+ LPSERVER_INFO_1502 ServerInfo1502;
+ [case(1503)]
+ LPSERVER_INFO_1503 ServerInfo1503;
+ [case(1506)]
+ LPSERVER_INFO_1506 ServerInfo1506;
+ [case(1509)]
+ LPSERVER_INFO_1509 ServerInfo1509;
+ [case(1510)]
+ LPSERVER_INFO_1510 ServerInfo1510;
+ [case(1511)]
+ LPSERVER_INFO_1511 ServerInfo1511;
+ [case(1512)]
+ LPSERVER_INFO_1512 ServerInfo1512;
+ [case(1513)]
+ LPSERVER_INFO_1513 ServerInfo1513;
+ [case(1514)]
+ LPSERVER_INFO_1514 ServerInfo1514;
+ [case(1515)]
+ LPSERVER_INFO_1515 ServerInfo1515;
+ [case(1516)]
+ LPSERVER_INFO_1516 ServerInfo1516;
+ [case(1518)]
+ LPSERVER_INFO_1518 ServerInfo1518;
+ [case(1520)]
+ LPSERVER_INFO_1520 ServerInfo1520;
+ [case(1521)]
+ LPSERVER_INFO_1521 ServerInfo1521;
+ [case(1522)]
+ LPSERVER_INFO_1522 ServerInfo1522;
+ [case(1523)]
+ LPSERVER_INFO_1523 ServerInfo1523;
+ [case(1524)]
+ LPSERVER_INFO_1524 ServerInfo1524;
+ [case(1525)]
+ LPSERVER_INFO_1525 ServerInfo1525;
+ [case(1528)]
+ LPSERVER_INFO_1528 ServerInfo1528;
+ [case(1529)]
+ LPSERVER_INFO_1529 ServerInfo1529;
+ [case(1530)]
+ LPSERVER_INFO_1530 ServerInfo1530;
+ [case(1533)]
+ LPSERVER_INFO_1533 ServerInfo1533;
+ [case(1534)]
+ LPSERVER_INFO_1534 ServerInfo1534;
+ [case(1535)]
+ LPSERVER_INFO_1535 ServerInfo1535;
+ [case(1536)]
+ LPSERVER_INFO_1536 ServerInfo1536;
+ [case(1537)]
+ LPSERVER_INFO_1537 ServerInfo1537;
+ [case(1538)]
+ LPSERVER_INFO_1538 ServerInfo1538;
+ [case(1539)]
+ LPSERVER_INFO_1539 ServerInfo1539;
+ [case(1540)]
+ LPSERVER_INFO_1540 ServerInfo1540;
+ [case(1541)]
+ LPSERVER_INFO_1541 ServerInfo1541;
+ [case(1542)]
+ LPSERVER_INFO_1542 ServerInfo1542;
+ [case(1543)]
+ LPSERVER_INFO_1543 ServerInfo1543;
+ [case(1544)]
+ LPSERVER_INFO_1544 ServerInfo1544;
+ [case(1545)]
+ LPSERVER_INFO_1545 ServerInfo1545;
+ [case(1546)]
+ LPSERVER_INFO_1546 ServerInfo1546;
+ [case(1547)]
+ LPSERVER_INFO_1547 ServerInfo1547;
+ [case(1548)]
+ LPSERVER_INFO_1548 ServerInfo1548;
+ [case(1549)]
+ LPSERVER_INFO_1549 ServerInfo1549;
+ [case(1550)]
+ LPSERVER_INFO_1550 ServerInfo1550;
+ [case(1552)]
+ LPSERVER_INFO_1552 ServerInfo1552;
+ [case(1553)]
+ LPSERVER_INFO_1553 ServerInfo1553;
+ [case(1554)]
+ LPSERVER_INFO_1554 ServerInfo1554;
+ [case(1555)]
+ LPSERVER_INFO_1555 ServerInfo1555;
+ [case(1556)]
+ LPSERVER_INFO_1556 ServerInfo1556;
+ [default]
+ ;
+} SERVER_INFO, *PSERVER_INFO, *LPSERVER_INFO;
+
+//
+// DiskEnum
+//
+// NOTE: The buffer pointer is supposed to point to an array of strings.
+// each string is fixed size with two characters followed by a NUL.
+// A:\0B:\0c:\0\0
+
+typedef struct _DISK_INFO {
+ [string] TCHAR Disk[3];
+} DISK_INFO, *PDISK_INFO, *LPDISK_INFO;
+
+typedef struct _DISK_ENUM_CONTAINER {
+ DWORD EntriesRead;
+ [size_is(EntriesRead), length_is(EntriesRead)] LPDISK_INFO Buffer;
+} DISK_ENUM_CONTAINER;
+
+//
+// Function Prototypes - NetrServer
+//
+
+NET_API_STATUS
+NetrServerGetInfo (
+ [in,string,unique] SRVSVC_HANDLE ServerName,
+ [in] DWORD Level,
+ [out, switch_is(Level)] LPSERVER_INFO InfoStruct
+ );
+
+NET_API_STATUS
+NetrServerSetInfo (
+ [in,string,unique] SRVSVC_HANDLE ServerName,
+ [in] DWORD Level,
+ [in, switch_is(Level)] LPSERVER_INFO ServerInfo,
+ [in,out,unique] LPDWORD ParmErr
+ );
+
+NET_API_STATUS
+NetrServerDiskEnum (
+ [in,string,unique] SRVSVC_HANDLE ServerName,
+ [in] DWORD Level,
+ [in,out] DISK_ENUM_CONTAINER *DiskInfoStruct,
+ [in] DWORD PreferredMaximumLength,
+ [out] LPDWORD TotalEntries,
+ [in,out,unique] LPDWORD ResumeHandle
+ );
+
+//
+// Function Prototype - NetrServerStatisticsGet
+//
+
+NET_API_STATUS
+NetrServerStatisticsGet (
+ [in,string,unique] SRVSVC_HANDLE ServerName,
+ [in,string,unique] LPTSTR Service,
+ [in] DWORD Level,
+ [in] DWORD Options,
+ [out] LPSTAT_SERVER_0 *InfoStruct
+ );
+
+//
+// Server Transport API
+//
+
+//
+// Structures - NetrServerTransport
+//
+
+typedef struct _SERVER_XPORT_INFO_0_CONTAINER {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] LPSERVER_TRANSPORT_INFO_0 Buffer;
+} SERVER_XPORT_INFO_0_CONTAINER, *PSERVER_XPORT_INFO_0_CONTAINER;
+
+typedef struct _SERVER_XPORT_INFO_1_CONTAINER {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] LPSERVER_TRANSPORT_INFO_1 Buffer;
+} SERVER_XPORT_INFO_1_CONTAINER, *PSERVER_XPORT_INFO_1_CONTAINER;
+
+typedef [switch_type(unsigned long)] union _TRANSPORT_INFO { // for Add and Del
+ [case(0)]
+ SERVER_TRANSPORT_INFO_0 Transport0;
+ [case(1)]
+ SERVER_TRANSPORT_INFO_1 Transport1;
+ [default]
+ ;
+} TRANSPORT_INFO, *PTRANSPORT_INFO, *LPTRANSPORT_INFO;
+
+typedef struct _SERVER_XPORT_ENUM_STRUCT {
+ DWORD Level;
+ [switch_is(Level)] union _SERVER_XPORT_ENUM_UNION {
+ [case(0)]
+ PSERVER_XPORT_INFO_0_CONTAINER Level0;
+ [case(1)]
+ PSERVER_XPORT_INFO_1_CONTAINER Level1;
+ [default]
+ ;
+ } XportInfo;
+
+} SERVER_XPORT_ENUM_STRUCT, *PSERVER_XPORT_ENUM_STRUCT, *LPSERVER_XPORT_ENUM_STRUCT;
+
+//
+// Function Prototypes - NetrServerTransport
+//
+NET_API_STATUS
+NetrServerTransportAdd (
+ [in,string,unique] SRVSVC_HANDLE ServerName,
+ [in] DWORD Level,
+ [in] LPSERVER_TRANSPORT_INFO_0 Buffer
+ );
+
+
+NET_API_STATUS
+NetrServerTransportEnum (
+ [in,string,unique] SRVSVC_HANDLE ServerName,
+ [in,out] LPSERVER_XPORT_ENUM_STRUCT InfoStruct,
+ [in] DWORD PreferedMaximumLength,
+ [out] LPDWORD TotalEntries,
+ [in,out,unique] LPDWORD ResumeHandle
+ );
+
+NET_API_STATUS
+NetrServerTransportDel (
+ [in,string,unique] SRVSVC_HANDLE ServerName,
+ [in] DWORD Level,
+ [in] LPSERVER_TRANSPORT_INFO_0 Buffer
+ );
+
+
+//
+// Function Prototype - NetrRemoteTOD
+//
+
+NET_API_STATUS
+NetrRemoteTOD (
+ [in,string,unique] SRVSVC_HANDLE ServerName,
+ [out] LPTIME_OF_DAY_INFO *BufferPtr
+ );
+
+//
+// Function Prototype - I_NetrServerSetServiceBits (internal API)
+//
+
+NET_API_STATUS
+I_NetrServerSetServiceBits (
+ [in,string,unique] SRVSVC_HANDLE ServerName,
+ [in,string,unique] LPTSTR TransportName,
+ [in] DWORD ServiceBits,
+ [in] DWORD UpdateImmediately
+ );
+
+//
+// Function Prototypes - Canonicalization functions
+//
+
+NET_API_STATUS
+NetprPathType(
+ [in,string,unique] SRVSVC_HANDLE ServerName,
+ [in,string] LPTSTR PathName,
+ [out] LPDWORD PathType,
+ [in] DWORD Flags
+ );
+
+NET_API_STATUS
+NetprPathCanonicalize(
+ [in,string,unique] SRVSVC_HANDLE ServerName,
+ [in,string] LPTSTR PathName,
+ [out,size_is(OutbufLen)] LPBYTE Outbuf,
+ [in] DWORD OutbufLen,
+ [in,string] LPTSTR Prefix,
+ [in,out] LPDWORD PathType,
+ [in] DWORD Flags
+ );
+
+LONG
+NetprPathCompare(
+ [in,string,unique] SRVSVC_HANDLE ServerName,
+ [in,string] LPTSTR PathName1,
+ [in,string] LPTSTR PathName2,
+ [in] DWORD PathType,
+ [in] DWORD Flags
+ );
+
+NET_API_STATUS
+NetprNameValidate(
+ [in,string,unique] SRVSVC_HANDLE ServerName,
+ [in,string] LPTSTR Name,
+ [in] DWORD NameType,
+ [in] DWORD Flags
+ );
+
+NET_API_STATUS
+NetprNameCanonicalize(
+ [in,string,unique] SRVSVC_HANDLE ServerName,
+ [in,string] LPTSTR Name,
+ [out,size_is(OutbufLen)] LPTSTR Outbuf,
+ [in] DWORD OutbufLen,
+ [in] DWORD NameType,
+ [in] DWORD Flags
+ );
+
+LONG
+NetprNameCompare(
+ [in,string,unique] SRVSVC_HANDLE ServerName,
+ [in,string] LPTSTR Name1,
+ [in,string] LPTSTR Name2,
+ [in] DWORD NameType,
+ [in] DWORD Flags
+ );
+
+NET_API_STATUS
+NetrShareEnumSticky (
+ [in,string,unique] SRVSVC_HANDLE ServerName,
+ [in,out] LPSHARE_ENUM_STRUCT InfoStruct,
+ [in] DWORD PreferedMaximumLength,
+ [out] LPDWORD TotalEntries,
+ [in,out,unique] LPDWORD ResumeHandle
+ );
+
+//
+// Two-phase share deletion used to delete IPC$
+//
+
+typedef [context_handle] void *SHARE_DEL_HANDLE;
+typedef SHARE_DEL_HANDLE *PSHARE_DEL_HANDLE;
+
+NET_API_STATUS
+NetrShareDelStart (
+ [in,string,unique] SRVSVC_HANDLE ServerName,
+ [in,string] LPTSTR NetName,
+ [in] DWORD Reserved,
+ [out] PSHARE_DEL_HANDLE ContextHandle
+ );
+
+NET_API_STATUS
+NetrShareDelCommit (
+ [in, out] PSHARE_DEL_HANDLE ContextHandle
+ );
+
+//
+// AdminTools types and functions
+//
+typedef struct _ADT_SECURITY_DESCRIPTOR {
+ DWORD Length;
+ [size_is(Length)] LPBYTE Buffer;
+} ADT_SECURITY_DESCRIPTOR, *PADT_SECURITY_DESCRIPTOR;
+
+DWORD
+NetrpGetFileSecurity (
+ [in,string,unique] SRVSVC_HANDLE ServerName,
+ [in,string,unique] LPWSTR ShareName,
+ [in,string] LPWSTR lpFileName,
+ [in] SECURITY_INFORMATION RequestedInformation,
+ [out] PADT_SECURITY_DESCRIPTOR *SecurityDescriptor
+ );
+
+DWORD
+NetrpSetFileSecurity (
+ [in,string,unique] SRVSVC_HANDLE ServerName,
+ [in,string,unique] LPWSTR ShareName,
+ [in,string] LPWSTR lpFileName,
+ [in] SECURITY_INFORMATION SecurityInformation,
+ [in] PADT_SECURITY_DESCRIPTOR SecurityDescriptor
+ );
+
+NET_API_STATUS
+NetrServerTransportAddEx (
+ [in,string,unique] SRVSVC_HANDLE ServerName,
+ [in] DWORD Level,
+ [in, switch_is(Level)] LPTRANSPORT_INFO Buffer
+ );
+
+NET_API_STATUS
+I_NetrServerSetServiceBitsEx (
+ [in,string,unique] SRVSVC_HANDLE ServerName,
+ [in,string,unique] LPWSTR EmulatedServerName,
+ [in,string,unique] LPTSTR TransportName,
+ [in] DWORD ServiceBitsOfInterest,
+ [in] DWORD ServiceBits,
+ [in] DWORD UpdateImmediately
+ );
+
+
+//
+// Definitions for DFS operations
+//
+
+NET_API_STATUS NET_API_FUNCTION
+NetrDfsGetVersion(
+ [in,string,unique] SRVSVC_HANDLE ServerName,
+ [out] LPDWORD Version
+ );
+
+NET_API_STATUS NET_API_FUNCTION
+NetrDfsCreateLocalPartition (
+ [in,string,unique] SRVSVC_HANDLE ServerName,
+ [in,string] LPWSTR ShareName,
+ [in] LPGUID EntryUid, // unique id for this partition
+ [in,string] LPWSTR EntryPrefix, // path prefix for this partition
+ [in,string] LPWSTR ShortName, // 8.3 format of EntryPrefix
+ [in] LPNET_DFS_ENTRY_ID_CONTAINER RelationInfo,
+ [in] BOOL Force
+ );
+
+NET_API_STATUS NET_API_FUNCTION
+NetrDfsDeleteLocalPartition (
+ [in,string,unique] SRVSVC_HANDLE ServerName,
+ [in] LPGUID Uid,
+ [in,string] LPWSTR Prefix
+ );
+
+NET_API_STATUS NET_API_FUNCTION
+NetrDfsSetLocalVolumeState (
+ [in,string,unique] SRVSVC_HANDLE ServerName,
+ [in] LPGUID Uid,
+ [in,string] LPWSTR Prefix,
+ [in] ULONG State
+ );
+
+NET_API_STATUS NET_API_FUNCTION
+NetrDfsSetServerInfo (
+ [in,string,unique] SRVSVC_HANDLE ServerName,
+ [in] LPGUID Uid,
+ [in,string] LPWSTR Prefix
+ );
+
+NET_API_STATUS NET_API_FUNCTION
+NetrDfsCreateExitPoint (
+ [in,string,unique] SRVSVC_HANDLE ServerName,
+ [in] LPGUID Uid,
+ [in,string] LPWSTR Prefix,
+ [in] ULONG Type,
+ [in] DWORD ShortPrefixLen,
+ [out,size_is(ShortPrefixLen)] LPWSTR ShortPrefix
+ );
+
+NET_API_STATUS NET_API_FUNCTION
+NetrDfsDeleteExitPoint (
+ [in,string,unique] SRVSVC_HANDLE ServerName,
+ [in] LPGUID Uid,
+ [in,string] LPWSTR Prefix,
+ [in] ULONG Type
+ );
+
+NET_API_STATUS NET_API_FUNCTION
+NetrDfsModifyPrefix (
+ [in,string,unique] SRVSVC_HANDLE ServerName,
+ [in] LPGUID Uid,
+ [in,string] LPWSTR Prefix
+ );
+
+NET_API_STATUS NET_API_FUNCTION
+NetrDfsFixLocalVolume (
+ [in,string,unique] SRVSVC_HANDLE ServerName,
+ [in,string] LPWSTR VolumeName,
+ [in] ULONG EntryType,
+ [in] ULONG ServiceType,
+ [in,string] LPWSTR StgId,
+ [in] LPGUID EntryUid, // unique id for this partition
+ [in,string] LPWSTR EntryPrefix, // path prefix for this partition
+ [in] LPNET_DFS_ENTRY_ID_CONTAINER RelationInfo,
+ [in] ULONG CreateDisposition
+ );
+
+}
diff --git a/private/net/svcdlls/upssvc/install.c b/private/net/svcdlls/upssvc/install.c
new file mode 100644
index 000000000..958ae43ea
--- /dev/null
+++ b/private/net/svcdlls/upssvc/install.c
@@ -0,0 +1,57 @@
+#include <nt.h> /*Dbgprint prototype*/
+#include <ntrtl.h> /*Dbgprint prototype*/
+#include <windef.h>
+#include <nturtl.h>
+#include <winsvc.h>
+#include <tstr.h> /*Unicode string macros*/
+#include <winbase.h>
+
+void _CRTAPI1 main(DWORD argc, LPTSTR *argv);
+void errout(LPTSTR string);
+
+/* install the net service by registering with the local registry */
+/* may want to change it to install the service remotely */
+
+void _CRTAPI1 main(DWORD argc, LPTSTR *argv)
+{
+
+ SC_HANDLE scman;
+ SC_LOCK sclock;
+ SC_HANDLE serv;
+
+ if (argc != 3) {
+ DbgPrint("Install [name of service] [path]\n",GetLastError());
+ ExitProcess(0);
+ }
+
+ DbgPrint("%s %s %s\n", argv[0], argv[1], argv[2]);
+
+ scman = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
+ if (scman == NULL)
+ errout("Open Service Control Manager Failed\n");
+
+ serv = CreateService( scman,
+ argv[1],
+ GENERIC_READ,
+ SERVICE_WIN32_OWN_PROCESS,
+ SERVICE_AUTO_START,
+ SERVICE_ERROR_NORMAL,
+ argv[2],
+ "",
+ NULL,
+ "",
+ "localsystem", /*"localsystem",*/
+ NULL);
+
+ if (serv != NULL){
+ printf("Service installed successfully\n");
+ }
+ else errout("add Service failed");
+}
+
+void errout(LPTSTR string)
+{
+ printf("Install failed %s - %ld\n",string,GetLastError());
+ DbgPrint("Install failed %s - %ld\n",string,GetLastError());
+ ExitProcess(0);
+}
diff --git a/private/net/svcdlls/upssvc/makefile b/private/net/svcdlls/upssvc/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/net/svcdlls/upssvc/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/net/svcdlls/upssvc/ntshutio.c b/private/net/svcdlls/upssvc/ntshutio.c
new file mode 100644
index 000000000..d3e0f7a79
--- /dev/null
+++ b/private/net/svcdlls/upssvc/ntshutio.c
@@ -0,0 +1,94 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ ntshutio.c
+
+Abstract:
+
+ Simple test module for shutdown.
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv)
+
+Revision History:
+
+ Who When What
+ -------- -------- ----------------------------------------------
+ vladimv Oct. 20 92 Created.
+
+Notes:
+
+
+--*/
+
+#include "ups.h"
+
+void _CRTAPI1
+main(
+ void
+ )
+{
+ BOOL success;
+ NTSTATUS ntStatus;
+ HANDLE CommPort;
+
+ CommPort = CreateFile(
+ "COM1",
+ GENERIC_READ | GENERIC_WRITE,
+ 0,
+ NULL,
+ OPEN_EXISTING,
+ FILE_FLAG_OVERLAPPED,
+ NULL
+ );
+
+ if ( CommPort == INVALID_HANDLE_VALUE) {
+ printf(
+ "CreateFile( COM1) fails with winError = %d\n",
+ GetLastError()
+ );
+ return;
+ }
+
+ ntStatus = RtlAdjustPrivilege(
+ SE_SHUTDOWN_PRIVILEGE,
+ TRUE,
+ FALSE,
+ &success // was it enabled or not
+ );
+
+ if ( ntStatus != STATUS_SUCCESS) {
+ printf(
+ "RtlAdjustPrivilege() returns ntStatus = 0x%x "
+ "(wasEnabled = 0x%x)\n",
+ ntStatus,
+ success
+ );
+ }
+
+ printf( "wasEnabled = 0x%x\n", success);
+
+ ntStatus = NtShutdownSystem( FALSE);
+
+ printf(
+ "NtShutdownSystem( FALSE) returns ntStatus = 0x%x\n",
+ ntStatus
+ );
+
+ success = EscapeCommFunction( CommPort, SETDTR);
+
+ if ( success == TRUE) {
+ printf( " EscapeCommFunction( ..., SETDTR) succeeds\n");
+ } else {
+ printf(
+ " EscapeCommFunction( ..., SETDTR) fails with winError\n",
+ GetLastError()
+ );
+ }
+}
+
+
diff --git a/private/net/svcdlls/upssvc/remove.c b/private/net/svcdlls/upssvc/remove.c
new file mode 100644
index 000000000..66ae0eff9
--- /dev/null
+++ b/private/net/svcdlls/upssvc/remove.c
@@ -0,0 +1,47 @@
+#include <nt.h> /*Dbgprint prototype*/
+#include <ntrtl.h> /*Dbgprint prototype*/
+#include <windef.h>
+#include <nturtl.h>
+#include <winsvc.h>
+#include <tstr.h> /*Unicode string macros*/
+#include <winbase.h>
+
+void _CRTAPI1 main(DWORD argc, LPTSTR *argv);
+void errout(LPTSTR string);
+
+/* install the net service by registering with the local registry */
+/* may want to change it to install the service remotely */
+
+void _CRTAPI1 main(DWORD argc, LPTSTR *argv)
+{
+
+ SC_HANDLE scman;
+ SC_LOCK sclock;
+ SC_HANDLE serv;
+
+ if (argc != 2) {
+ DbgPrint("%s [name of service]\n",argv[0]);
+ ExitProcess(0);
+ }
+
+ DbgPrint("%s %s\n", argv[0], argv[1]);
+
+ scman = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
+ if (scman == NULL)
+ errout("Open Service Control Manager Failed\n");
+
+
+ serv = OpenService(scman, argv[1], GENERIC_ALL);
+
+ if (! DeleteService(serv))
+ errout("Delete Service Failed\n");
+
+ printf("Service removed successfully\n");
+}
+
+void errout(LPTSTR string)
+{
+ printf("Service remove failed %s - %ld\n",string,GetLastError());
+ DbgPrint("Service remove failed %s - %ld\n",string,GetLastError());
+ ExitProcess(0);
+}
diff --git a/private/net/svcdlls/upssvc/sources b/private/net/svcdlls/upssvc/sources
new file mode 100644
index 000000000..a65301515
--- /dev/null
+++ b/private/net/svcdlls/upssvc/sources
@@ -0,0 +1,31 @@
+MAJORCOMP=win32
+MINORCOMP=ups
+
+TARGETNAME=ups
+TARGETPATH=obj
+TARGETTYPE=PROGRAM
+TARGETLIBS=\
+ $(BASEDIR)\public\sdk\lib\*\netapi32.lib\
+ $(BASEDIR)\public\sdk\lib\*\advapi32.lib\
+ $(BASEDIR)\public\sdk\lib\*\user32.lib\
+ $(BASEDIR)\Public\Sdk\Lib\*\netlib.lib\
+ $(BASEDIR)\Public\Sdk\Lib\*\ntdll.lib
+
+INCLUDES=.;..\..\inc;..\..\..\inc
+
+MSC_WARNING_LEVEL=/W3 /WX
+
+SOURCES=ups.c subr.c upssvc.rc
+
+UMTYPE=console
+
+USE_CRTDLL=1
+
+
+# In order to build say shut.exe, just type "build shut"
+# or "nmake UMTEST=shut".
+#
+NTTEST=
+OPTIONAL_NTTEST=
+UMTYPE=console
+UMTEST=ntshutio
diff --git a/private/net/svcdlls/upssvc/spec b/private/net/svcdlls/upssvc/spec
new file mode 100644
index 000000000..7e945c595
--- /dev/null
+++ b/private/net/svcdlls/upssvc/spec
@@ -0,0 +1,195 @@
+Overview
+
+UPS provides protection against power failure. Upon power interruption,
+users with active sessions to the affected server are notified of the
+impending shutdown and an orderly server shutdown is performed. UPS is
+implemented as an NT service.
+
+Functional Description
+
+In the standard configuration, signalling is accomplished by connecting
+the uninterruptable power supply to the server via a dedicated serial port.
+The uninterruptible power supply generates signals corresponding to various
+fault conditions. The serial port will block until a signal is received.
+When a signal is received, the UPS service take appropriate actions.
+
+The UPS service has the following configurable parameter:
+
+Installed: A bit in the registry specifying whether a UPS
+ is installed or. UPS service would not start
+ if this bit is not set.
+
+Signal on Line Fail: A bit in the registry specifying whether the installed
+ UPS can signal upon power line fail. The physical
+ signal is the CTS line of the Comm Port.
+
+Signal on Low Battery: A bit in the registry specifying whether the installed
+ UPS can signal when the battery can supply less than
+ 2 minutes of power. The physical signal is the RLSD
+ line of the Comm Port.
+
+UPS turn off by
+computer : A bit in the registry specifying whether the installed
+ UPS can be told to turn itself off by the computer.
+ The physical signal is the DTR line of the Comm Port.
+ (Currently not implemented... can't do a system
+ shutdown without power, but shutdown would kill
+ all running processes in the first place. Talk to
+ scottlu)
+
+Singal voltages: Three bits in the registry specifying the assertion
+ voltage level of the three signals: signal on line
+ fail, signal on low battery, and UPS turn off.
+
+This is the definition of the bit mask for the "OPTIONS" field.
+#define UPS_INSTALLED 0x00000001
+#define UPS_POWERFAILSIGNAL 0x00000002
+#define UPS_LOWBATTERYSIGNAL 0x00000004
+#define UPS_CANTURNOFF 0x00000008
+#define UPS_POSSIGONPOWERFAIL 0x00000010
+#define UPS_POSSIGONLOWBATTERY 0x00000020
+#define UPS_POSSIGSHUTOFF 0x00000040
+
+Port: name of the serial port the UPS is connected to.
+ (e.g. COM1)
+Battery life: the life of the UPS backup battery when fully charged.
+ Configurable from 0 to 480 minutes.
+Rechare rate: the recharge rate of the UPS backup battery, in terms
+ of X minutes per miunte of power. Configurable from
+ 5 minutes to 250 minutes.
+First Message Delay: Specifies the number of seconds between initial power
+ failure and first message sent to the users.
+ (If power is retored within first message delay, no
+ messages are sent, although the event is logged.)
+ Configurable from 0 to 120s.
+
+Warning Interval: Specifies the number of seconds between messages sent
+ to users informing them of power failure.
+ Configurable from 30 to 300 seconds.
+
+The UPS service do a range check on the above parameters (except for Port).
+The service would not be started unless all parameters are present in
+the registry, and all of them are within the specified range.
+In which case, the error code NERR_UPSInvalidConfig would be
+returned. The UPS service also fails to start if it can't find
+the configuration key in the registry.
+
+System configuration can be done through the control panel of NT,
+which would both update the registry and restart the UPS service.
+Update through registry editor is also possible.
+
+If an invalid port is specified (either a non-existing port or a port that
+is already used by another process), the error code NERR_UPSInvalidCommPort
+would be returned.
+
+The UPS service would not be started if any valid signals (as defined by
+ the signal low batt and singal power out) is asserted (as defined by
+their voltage levels). This helps to eliminate system shutdown due to
+incorrect configuration of the UPS. The error code NERR_UPSSignalAsserted
+would be returned.
+
+The UPS service would return UPSShutdownFailed if the UPS service failed
+to perform a shutdown. The error code returned is NERR_UPSShutdownFailed.
+
+The above four error codes would all be logged to the event logger.
+
+The following lines are added to the files nt\public\sdk\inc\lmerr.h
+#define NERR_UPSInvalidConfig (NERR_BASE+381) /* The UPS service is not correctly configured. */
+#define NERR_UPSInvalidCommPort (NERR_BASE+382) /* The UPS service could not access the specified Comm Port. */
+#define NERR_UPSSignalAsserted (NERR_BASE+383) /* The UPS indicated a line fail or low battery situation. Service not started. */
+#define NERR_UPSShutdownFailed (NERR_BASE+384) /* The UPS service failed to perform a system shutdown. */
+
+Service Flow of Control
+
+ The UPS service runs as a time-critical process. Under normal
+conditions this should not affect the system, since the UPS service
+thread is blocked indefinitely (by WaitForSingleObject() of
+WaitCommEvent() ). However, when UPS awakens, it needs to have high
+priority so that it can complete its tasks before battery power runs
+out.
+
+ After the service is installed and running, the following assertion
+of singals are done:
+
+a.TXD is set to permanently low.
+b.RTS is set to permanently hight.
+c.DTR is set to the level opposite to UPSShutdownFailed level if the UPS
+supports UPS shutdown.
+
+ a. and b. are for supporting contact closure UPS by supplying
+the power supply. c. is for avoiding turning off the UPS.
+
+ One of the two signals may then be received from the device
+driver -- either power failure or low battery. (Low battery actions are
+described below.)
+
+If a power failure is received, the following happens:
+
+ 1. The server is paused.
+ 2. The following error is logged:
+
+ NELOG_UPS_PowerOut
+
+ 3. The following alert is raised:
+
+ ALERT_PowerOut
+
+If the power is restored before messdelay expires, the following happends:
+
+ 1. The server is continued.
+ 2. The following alert is raised:
+
+ ALERT_PowerBack
+
+If the power is not restored before messdelay expires, the following happens:
+
+ 1. The following message is being sent to the local user and
+ all users with session on the server every messtime:
+
+ APE2_UPS_POWER_OUT
+
+ 2. The following alert is raised every messtime:
+
+ ALERT_PowerOut
+
+If the power is restored the following happens:
+
+ 1. The following message is being sent to the local user and
+ all users with session on the server:
+
+ APE2_UPS_POWER_BACK
+
+ 2. The following alert is raised:
+
+ ALERT_PowerBack
+
+If the power does not return, either the low battery signal will
+be received, or the battery timer will expire.
+
+For the confoguration where both the low battery signal and the power
+fail signal are supported, the service would not expire the battery
+timer (the service shuts down only when low battery signal is asserted).
+
+Whenever a low battery signal is received (even if the line fail has not
+been signaled) or batterytime equals zero (in the case where low battery
+is not supported), the following happens:
+
+ 1. The server is stopped.
+
+ 2. The following message is being sent to the local user and
+ all users with session on the server:
+
+ APE2_UPS_POWER_SHUTDOWN_FINAL
+
+ 3. The following entry is written to the error log:
+
+ NELOG_UPS_SHUTDOWN
+
+ 4. The following alert is raised:
+
+ ALERT_PowerShutDown
+
+
+
+
+
diff --git a/private/net/svcdlls/upssvc/subr.c b/private/net/svcdlls/upssvc/subr.c
new file mode 100644
index 000000000..60f06cf0c
--- /dev/null
+++ b/private/net/svcdlls/upssvc/subr.c
@@ -0,0 +1,686 @@
+/*++
+
+Copyright (c) 1992-1996 Microsoft Corporation
+
+Module Name:
+
+ subr.c
+
+Abstract:
+
+ This module contains the subrountines for the UPS service.
+
+Author:
+
+ Kin Hong Kan (t-kinh)
+
+Revision History:
+
+ Who When What
+ -------- -------- ----------------------------------------------
+ t-kinh 8/20/92 Created.
+ vladimv 1992 Big reorgs. Make it look like a real service.
+ ericb 10/25/95 fix bug 8133 - make shutdown wait configurable
+
+Notes:
+
+
+--*/
+
+#include "ups.h"
+
+
+extern HANDLE UpsGlobalLogFileHandle;
+extern UPS_CONFIG UpsGlobalConfig;
+extern UPS_TIME UpsGlobalBatteryTime;
+extern CHAR UpsGlobalCommand[ MAX_PATH];
+
+BOOL
+UpsGetKeyValue(
+ HKEY MyKey,
+ LPTSTR SubKey,
+ DWORD min,
+ DWORD max,
+ LPDWORD ret
+ );
+
+
+// these values are also defined in the UPS applet...
+
+#define REGISTRY_UPS_DIRECTORY "System\\CurrentControlSet\\Services\\UPS"
+#define REGISTRY_PORT "Port"
+#define REGISTRY_OPTIONS "Options"
+#define REGISTRY_BATTERY_LIFE "BatteryLife"
+#define REGISTRY_RECHARGE_RATE "RechargeRate"
+#define REGISTRY_FIRST_MESSAGE_DELAY "FirstMessageDelay"
+#define REGISTRY_MESSAGE_INTERVAL "MessageInterval"
+#define REGISTRY_COMMAND_FILE "CommandFile"
+
+// additional ones for sending out messages and alerts
+
+#define REGISTRY_COMPUTER_NAME "ComputerName"
+#define REGISTRY_COMPUTER_NAME_DIRECTORY \
+ "System\\CurrentControlSet\\Control\\ComputerName\\ComputerName"
+
+#define REGISTRY_SHUTDOWN_WAIT "ShutdownWait"
+
+
+VOID
+UpsAlertRaise(
+ DWORD MessageId
+ )
+/*++
+
+Routine Description:
+
+ Sends an alert.
+
+Arguments:
+
+ MessageId - The Message Id of the alert as defined in netmsg.h
+
+Return Value:
+
+ None.
+
+Notes:
+
+ NetAlertRaise() only supports unicode version, that results in
+ the Computer name is hardwired to wchat_t.
+
+--*/
+
+{
+ CHAR buff[ (sizeof(ADMIN_OTHER_INFO) + 2 * (MAX_PATH+1))];
+ LPTSTR CompName;
+ LPADMIN_OTHER_INFO OInfo;
+ DWORD status;
+
+ OInfo = (LPADMIN_OTHER_INFO)buff;
+ OInfo->alrtad_errcode = MessageId;
+ OInfo->alrtad_numstrings = 1;
+
+ CompName = (buff + sizeof(ADMIN_OTHER_INFO));
+
+ wcscpy(
+ (LPWSTR)CompName,
+ (LPWSTR)UpsGlobalConfig.ComputerName
+ );
+
+ status = NetAlertRaiseEx(
+ L"ADMIN", // alert type
+ buff, // info buffer
+ sizeof(buff), // info size
+ L"UPS" // service name
+ );
+ if ( status != 0) {
+ // This occurs if for example alerter service has not been started.
+ KdPrint(("[UPS] NetAlertRaise() returns status = %ld\n", status));
+ }
+}
+
+
+BOOL
+UpsGetCommand(
+ HKEY UpsKey,
+ PCHAR CommandFileBuffer,
+ DWORD CommandFileBufferSize
+ )
+/*++
+
+Routine Description:
+
+ Reads registry to find out the value of CommandFile and initialize
+ UpsGlobalCommand.
+
+ It returns TRUE if it does not find an entry for CommandFile, or if
+ it finds a valid entry for CommandFile. In this latter case it
+ initializes UpsGlobalCommand to point to a command exec string.
+
+
+ It returns FALSE if it finds an invalid entry for CommandFile, or fails
+ initializing UpsGlobalCommand for any other reason.
+
+Arguments:
+
+ UpsKey - Registry key to UPS service configuration.
+ CommandFileBuffer - Buffer for command file.
+ CommandFileBufferSize - Size in bytes of CommandFileBuffer.
+
+Return Value:
+
+ TRUE - Did not find an entry or found a valid entry.
+ FALSE - Found an invalid entry.
+
+--*/
+ {
+ DWORD status;
+ DWORD length;
+ DWORD type;
+ DWORD SpaceLeft;
+ DWORD CommandFileLength;
+
+ status = RegQueryValueEx(
+ UpsKey,
+ REGISTRY_COMMAND_FILE,
+ NULL,
+ &type,
+ (LPBYTE)CommandFileBuffer,
+ &CommandFileBufferSize
+ );
+ if ( status == ERROR_FILE_NOT_FOUND) {
+ return( TRUE); // value is not present in the registry
+ }
+
+ if ( status != ERROR_SUCCESS || type != REG_SZ ||
+ CommandFileBufferSize == 0) {
+ return( FALSE); // an invalid error or invalid type
+ }
+
+ // subtract terminating null
+ CommandFileLength = CommandFileBufferSize - sizeof( CommandFileBuffer[0]);
+
+ if ( CommandFileLength == 0) {
+ return( TRUE); // an empty string is OK
+ }
+
+ SpaceLeft = sizeof( UpsGlobalCommand);
+
+ length = GetSystemDirectory(
+ UpsGlobalCommand,
+ SpaceLeft
+ );
+ if ( length == 0 || length >= SpaceLeft) {
+ return( FALSE);
+ }
+
+ if ( UpsGlobalCommand[ length - 1] != '\\') {
+ UpsGlobalCommand[ length] = '\\';
+ if ( ++length >= SpaceLeft) {
+ return( FALSE);
+ }
+ UpsGlobalCommand[ length] = '\0';
+ }
+
+ if ( length + CommandFileLength + 1 >= SpaceLeft) {
+ return( FALSE);
+ }
+
+ strcpy( UpsGlobalCommand + length, CommandFileBuffer);
+
+ type = GetFileAttributes( UpsGlobalCommand);
+ if ( (type & FILE_ATTRIBUTE_DIRECTORY) != 0) {
+ return( FALSE);
+ }
+
+ return( TRUE);
+}
+
+
+BOOL
+UpsGetConfig(
+ VOID
+ )
+/*++
+
+Routine Descrption:
+
+ Open the required keys to the registry to read in the configuration of
+ the UPS service and the name of the local system. Range checks are being
+ done for numerical entries of the UPS configuation.
+
+Return Value:
+
+ TRUE - all values are put in the Config structure.
+
+ FALSE - either one of the following cases:
+ a. Can't open registry key.
+ b. Can't read any required value.
+ c. The values are not within the defined range.
+--*/
+{
+
+ DWORD status;
+ HKEY RegistryKey;
+ DWORD value;
+ CHAR temp[ MAX_PATH];
+ DWORD size;
+ DWORD type;
+
+ status = RegOpenKeyEx(
+ HKEY_LOCAL_MACHINE,
+ REGISTRY_UPS_DIRECTORY,
+ 0,
+ KEY_READ,
+ &RegistryKey // key to UPS service configuration
+ );
+ if ( status != ERROR_SUCCESS){
+ KdPrint(("[UPS] Cannot open registry key, error_code %d\n", status));
+ return( FALSE);
+ }
+
+ if (FALSE == UpsGetKeyValue(
+ RegistryKey,
+ REGISTRY_OPTIONS,
+ 0,
+ 0XFFFFFFFF, // no range check for mask
+ &value)) {
+ return( FALSE);
+ }
+ UpsGlobalConfig.Options = value; // Options value
+
+ if (! (UpsGlobalConfig.Options & UPS_INSTALLED)) {
+ KdPrint(("[UPS] UPS not installed\n"));
+ return( FALSE); // UPS must be installed
+ }
+ if (!(UpsGlobalConfig.Options & (UPS_LOWBATTERYSIGNAL | UPS_POWERFAILSIGNAL))) {
+ KdPrint(("[UPS] no signal lines supported - Options %d\n", UpsGlobalConfig.Options));
+ return( FALSE); // UPS must support signalling
+ }
+
+ if (FALSE == UpsGetKeyValue(
+ RegistryKey,
+ REGISTRY_BATTERY_LIFE,
+ MINBATTERYLIFE,
+ MAXBATTERYLIFE,
+ &value)) {
+ return( FALSE);
+ }
+ UpsGlobalConfig.BatteryLife = value; // BatterLife value
+
+ if (FALSE == UpsGetKeyValue(
+ RegistryKey,
+ REGISTRY_RECHARGE_RATE,
+ MINRECHARGEPERMINUTE,
+ MAXRECHARGEPERMINUTE,
+ &value)) {
+ return( FALSE);
+ }
+ UpsGlobalConfig.RechargeRate = value; // RechargeRate value
+
+ if (FALSE == UpsGetKeyValue(
+ RegistryKey,
+ REGISTRY_FIRST_MESSAGE_DELAY,
+ MINFIRSTWARNING,
+ MAXFIRSTWARNING,
+ &value)) {
+ return( FALSE);
+ }
+ UpsGlobalConfig.FirstMessageDelay = value; // FirstMessageDelay
+
+ if (FALSE == UpsGetKeyValue(
+ RegistryKey,
+ REGISTRY_MESSAGE_INTERVAL,
+ MINWARNINGINTERVAL,
+ MAXWARNINGINTERVAL,
+ &value)) {
+ return( FALSE);
+ }
+ UpsGlobalConfig.MessageInterval = value; // MessageInterval
+
+ //
+ // configurable shutdown wait interval
+ //
+ size = sizeof(value);
+ status = RegQueryValueEx(RegistryKey,
+ REGISTRY_SHUTDOWN_WAIT,
+ NULL,
+ NULL,
+ (LPBYTE)&value,
+ &size);
+ if (status != ERROR_SUCCESS)
+ { // if a value is not stored in the registry, use the default;
+ value = DEFAULTSHUTDOWNWAIT;
+ }
+ else
+ {
+ if (MAXSHUTDOWNWAIT < value)
+ {
+ value = MAXSHUTDOWNWAIT;
+ }
+ }
+ UpsGlobalConfig.ShutdownWait = value;
+ KdPrint(("[UPS] ShutdownWait set to %d\n", value));
+
+ size = sizeof(temp);
+ status = RegQueryValueEx(
+ RegistryKey,
+ REGISTRY_PORT,
+ NULL,
+ &type,
+ (LPBYTE)&temp,
+ &size
+ );
+ if (status != ERROR_SUCCESS) {
+ KdPrint((
+ "[UPS] Can't get value of %s error_code - %d\n",
+ REGISTRY_PORT,
+ status
+ ));
+ return( FALSE);
+ }
+ // Copy the comm port string, stripping the colon at the end.
+ strncpy(
+ (PCHAR)UpsGlobalConfig.Port,
+ temp,
+ strlen( temp)-1
+ );
+
+ if ( UpsGlobalConfig.Options & UPS_COMMANDFILE
+ && UpsGetCommand( RegistryKey, temp, sizeof( temp)) == FALSE) {
+ KdPrint((
+ "[UPS] Bad value of %s \n",
+ REGISTRY_COMMAND_FILE
+ ));
+ //
+ // Just log an event and send an alert. This problem is assumed
+ // to be benign enough so that we do not want to abort the startup
+ // on this account.
+ //
+ UpsReportEvent( NELOG_UPS_CmdFileConfig, NULL, ERROR_SUCCESS);
+ UpsAlertRaise( ALERT_CmdFileConfig);
+ }
+
+ RegCloseKey( RegistryKey); // done with service configuration
+
+ status = RegOpenKeyEx(
+ HKEY_LOCAL_MACHINE, //Open Reg Key
+ REGISTRY_COMPUTER_NAME_DIRECTORY,
+ 0,
+ KEY_READ,
+ &RegistryKey // key for computer name
+ );
+ if (status != ERROR_SUCCESS){
+ KdPrint(("[UPS] Can't open registry key, error_code %d\n", status));
+ return( FALSE);
+ }
+
+
+ // ComputerName is hardwired UNICODE string, since NetAlertRaise()
+ // and UPSNotifyUsers() don't take in ANSI string... Remove conversion
+ // when ANSI is supported....
+
+ size = sizeof(temp);
+ status = RegQueryValueEx(
+ RegistryKey,
+ REGISTRY_COMPUTER_NAME,
+ NULL,
+ &type,
+ (LPBYTE)&temp,
+ &size
+ );
+ if (strlen(temp) > sizeof( UpsGlobalConfig.ComputerName ) / sizeof( UpsGlobalConfig.ComputerName[0])) {
+ KdPrint(("[UPS] Computer name too long\n"));
+ return( FALSE);
+ }
+
+ if (0 == MultiByteToWideChar(
+ CP_ACP,
+ MB_PRECOMPOSED,
+ temp,
+ strlen(temp)+1, //copy null char also
+ (LPWSTR) UpsGlobalConfig.ComputerName,
+ sizeof(UpsGlobalConfig.ComputerName)/sizeof(UpsGlobalConfig.ComputerName[0]))) {
+ KdPrint(("[UPS]CompName not translated to Unicode, %ld\n",
+ GetLastError()));
+ return( FALSE);
+ }
+
+ if (status != ERROR_SUCCESS) {
+ KdPrint((
+ "[UPS] Can't get value of %s error_code - %d\n",
+ REGISTRY_COMPUTER_NAME,
+ status
+ ));
+ return( FALSE);
+ }
+
+ return( TRUE);
+}
+
+BOOL
+UpsGetKeyValue(
+ HKEY RegistryKey,
+ LPTSTR SubKey,
+ DWORD min,
+ DWORD max,
+ LPDWORD ret
+ )
+/*++
+
+Routine Description:
+
+ Get a numerical key value from the registry when given a key and
+ subkey to the registry. The numeric value is returned if the value
+ passes the range check.
+
+Arguments:
+
+ RegistryKey - Opened key to the registry.
+ SubKey - Name of the subkey to be read from registry.
+ min - minimum value for the range check (exclusive)
+ max - maximum value for the range check (exclusive)
+ ret - pointer to a DWORD to return the numeric value
+
+Return Value:
+
+ TRUE - the value is successfully read from the registry and
+ passed the range check
+ FALSE - the value is not read from the registry, or the value
+ doesn't pass the range test.
+--*/
+
+{
+ DWORD temp;
+ LONG status;
+ DWORD size;
+ DWORD type;
+
+ size = sizeof(DWORD);
+
+ status = RegQueryValueEx(
+ RegistryKey,
+ SubKey,
+ NULL,
+ &type,
+ (LPBYTE)&temp,
+ &size
+ );
+
+ if (status != ERROR_SUCCESS) {
+ KdPrint((
+ "[UPS] Can't get value of %s error_code - %d\n",
+ SubKey,
+ status
+ ));
+ return( FALSE);
+ }
+
+ if ( ((DWORD)temp < min) || ((DWORD)temp > max)) {
+ KdPrint((
+ "[UPS] Value of %s not in range - %d\n",
+ SubKey,
+ (DWORD)temp
+ ));
+ return( FALSE);
+ }
+ *ret = temp;
+ return( TRUE);
+}
+
+
+BOOL
+UpsLineAsserted(
+ DWORD ModemStatus,
+ DWORD Line
+ )
+/*++
+Routine Descriptions:
+
+ Checking if a specified signal, either LINE_FAIL or LOW_POWER, is
+ asserted or not. This is tricky, since:
+
+ 1. assertion can be either positive voltage level, or negative voltage
+ level, as defined by UpsGlobalConfig.
+ 2. SET means positive voltage in GetCommModemStatus.
+
+Arguments:
+ ModemStatus- CommPort as returned by GetCommModemStatus()
+ Line - either LINE_FAIL or LOW_POWER
+
+Return Value: TRUE if asserted
+ FALSE if not asserted
+--*/
+
+{
+
+ DWORD status, assertion;
+
+ status = Line & ModemStatus;
+ assertion = (Line == LINE_FAIL) ?
+ (UpsGlobalConfig.Options & UPS_POSSIGONPOWERFAIL) :
+ (UpsGlobalConfig.Options & UPS_POSSIGONLOWBATTERY);
+
+ if (status) { //line positive
+ if (assertion){ //positive asssertion
+ return TRUE;}
+ else{ //negative assertion
+ return FALSE;}
+ }
+
+ else { //line negative
+ if (assertion){ //positive assertion
+ return FALSE;}
+ else{
+ return TRUE;} //negative assertion
+ }
+}
+
+
+
+VOID
+UpsReportEvent(
+ DWORD MessageId,
+ PCHAR SingleString,
+ DWORD Error
+ )
+/*++
+
+Routine Descriptions:
+
+ Calls ReportEvent to write to a system event log.
+
+Arguments:
+
+ MessageId - The Message Id of the event defined in lmerrlog.h
+ String - Pointer to a single insertion string - or NULL if there
+ is no string to insert.
+ Error - Error code to log as data - or ERROR_SUCCESS if there
+ is no error code to log.
+
+
+Return Value:
+
+ None.
+
+--*/
+{
+ WORD eventType;
+ WORD cStrings; // count of insertion strings
+ LPTSTR Strings[1]; // array of insertion strings
+ LPTSTR * pStrings; // pointer to array of insertion strings
+ DWORD cbData; // count of data (in bytes)
+ LPVOID pData; // pointer to data
+
+ eventType = (MessageId > ERRLOG_BASE) ?
+ EVENTLOG_WARNING_TYPE : EVENTLOG_ERROR_TYPE;
+
+ if ( Error == ERROR_SUCCESS) {
+ pData = NULL;
+ cbData = 0;
+ } else {
+ pData = &Error;
+ cbData = sizeof( Error);
+ }
+
+ if ( SingleString == NULL) {
+ pStrings = NULL;
+ cStrings = 0;
+ } else {
+ Strings[ 0] = SingleString;
+ pStrings = Strings;
+ cStrings = 1;
+ }
+
+ KdPrint(("[UPS] ReportEvent(): MessageId = (dec)%d\n", MessageId));
+
+ if ( !ReportEvent(
+ UpsGlobalLogFileHandle, // handle
+ eventType, // event type
+ 0, // event category,
+ MessageId, // message id
+ NULL, // user id
+ cStrings, // number of strings
+ cbData, // number of data bytes
+ pStrings, // array of strings
+ pData // data buffer
+ )) {
+ KdPrint(("[UPS] ReportEvent: error = (dec)%d\n", GetLastError()));
+ }
+}
+
+
+
+VOID
+UpsUpdateTime(
+ DWORD Status
+ )
+/*++
+
+Routine Descriptions:
+
+ Update the BatteryTime sturcture. If Status is passed DISCHARGE, the
+ amount of time that between "MarkTime" and now is subtrated from
+ "StoredTime". If Status is passed CHARGE, the time would be divided by
+ the recharge rate in the Config and added to the Stored time.
+
+Arguments:
+
+ Status - indicating what happened for the time elapsed
+ can either be CHARGE or DISCHARGE.
+
+Return Value:
+
+ None.
+
+--*/
+{
+
+ time_t CurrentTime, TimeElapsed, MaxLife;
+
+ MaxLife = UpsGlobalConfig.BatteryLife * 60;
+ CurrentTime = time((time_t *)NULL);
+ TimeElapsed = CurrentTime - UpsGlobalBatteryTime.MarkTime;
+
+ ASSERT( TimeElapsed >=0 && (Status == CHARGE || Status == DISCHARGE));
+
+ UpsGlobalBatteryTime.MarkTime = CurrentTime;
+
+ if ( Status == CHARGE) {
+
+ if (UpsGlobalConfig.RechargeRate == 0) { // just in case
+ UpsGlobalBatteryTime.StoredTime = MaxLife;
+ } else {
+ UpsGlobalBatteryTime.StoredTime +=
+ (TimeElapsed / UpsGlobalConfig.RechargeRate);
+ if ( UpsGlobalBatteryTime.StoredTime > MaxLife) {
+ UpsGlobalBatteryTime.StoredTime = MaxLife;
+ }
+ }
+ } else {
+
+ UpsGlobalBatteryTime.StoredTime =
+ (UpsGlobalBatteryTime.StoredTime > TimeElapsed) ?
+ (UpsGlobalBatteryTime.StoredTime - TimeElapsed) : 0;
+ }
+}
diff --git a/private/net/svcdlls/upssvc/toggle.c b/private/net/svcdlls/upssvc/toggle.c
new file mode 100644
index 000000000..e8befdf68
--- /dev/null
+++ b/private/net/svcdlls/upssvc/toggle.c
@@ -0,0 +1,118 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ toggle.c
+
+Abstract:
+
+ This module contains the rountine needed to toggle CTS and DCD,
+ for the testing of UPS service.
+
+ The module assumes that RTS is connected to CTS, and
+ DCD is connected to DTR.
+
+ The module also assume a SETCTS means high voltage,
+ CLRCTS means clear volrage.
+
+Author:
+
+ Kin Hong Kan (t-kinh)
+
+Revision History:
+
+ Who When What
+ -------- -------- ----------------------------------------------
+ t-kinh 8/10/92 Created.
+
+Notes:
+
+
+--*/
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <windef.h>
+#include <nturtl.h>
+#include <windows.h>
+#include <lmcons.h>
+#include <stdio.h>
+#include <io.h>
+#include "upssvr.h"
+
+HANDLE CommPort;
+
+void _CRTAPI1 main(DWORD argc, LPTSTR *argv);
+
+void _CRTAPI1 main(DWORD argc, LPTSTR *argv)
+{
+ char temp[255];
+ DWORD ModemStatus;
+
+ printf("I am started\n");
+
+ if (argc != 2) {
+ printf("incorrect number of arguments\n");
+ Sleep(30000);
+ ExitProcess(0);
+ }
+
+ CommPort = atol(argv[1]);
+ printf("CommPort = %d\n", CommPort);
+
+ if (CommPort == 0) {
+ printf("CommPort Handle not valid\n");
+ ExitProcess(0);
+ }
+
+ if (!GetCommModemStatus(CommPort, &ModemStatus)) {
+ printf("Can't get initial state of modem - %d\n",
+ GetLastError());
+ ExitProcess(0);
+ }
+
+ while(1) {
+
+ printf("hc - high CTS, lc - low CTS\n");
+ printf("hd - high CD , ld - low CD \n");
+ printf("command:");
+ gets(temp);
+
+
+ if (strcmp(temp, "hc") == 0) {
+ if (!EscapeCommFunction(CommPort, SETRTS))
+ printf("FAIL %d\n", GetLastError());
+ else
+ printf("RTS set\n");
+ }
+
+ else if (strcmp(temp, "lc") == 0) {
+ if (!EscapeCommFunction(CommPort, CLRRTS))
+ printf("FAIL %d\n", GetLastError());
+ else
+ printf("RTS reset\n");
+ }
+
+ else if (strcmp(temp, "hd") == 0) {
+
+ if (!EscapeCommFunction(CommPort, SETDTR))
+ printf("FAIL %d\n", GetLastError());
+ else
+ printf("DTR set\n");
+ }
+
+ else if (strcmp(temp, "ld") == 0) {
+ if (!EscapeCommFunction(CommPort, CLRDTR))
+ printf("FAIL %d\n", GetLastError());
+ else
+ printf("DTR reset\n");
+ }
+
+
+ else printf("illegal command\n");
+
+ }
+}
+
diff --git a/private/net/svcdlls/upssvc/ups.c b/private/net/svcdlls/upssvc/ups.c
new file mode 100644
index 000000000..0c3fa7edc
--- /dev/null
+++ b/private/net/svcdlls/upssvc/ups.c
@@ -0,0 +1,1303 @@
+/*++
+
+Copyright (c) 1992-1996 Microsoft Corporation
+
+Module Name:
+
+ ups.c
+
+Abstract:
+
+ This module contains the body of the NT UPS service.
+
+Author:
+
+ Kin Hong Kan (t-kinh)
+
+Revision History:
+
+ Who When What
+ -------- -------- ----------------------------------------------
+ t-kinh 8/25/92 Created.
+ vladimv 1992 Big reorgs. Make it look like a real service.
+ ericb 8-23-94 fix bug 23704 - assert shutdown serial line longer
+ ericb 8-24-95 fix bugs 14506, 16195 - improperly closing handles
+ ericb 9/19/95 fix bug 1262 - end message thread gracefully
+ ericb 10/25/95 fix bug 8133 - make shutdown wait configurable
+
+
+Notes:
+
+--*/
+#include "ups.h"
+
+//#define UPS_TOGGLER //define to fork debug toggler
+
+#define UPS_DONE_EVENT_CREATED 0x1
+#define UPS_OVERLAP_EVENT_CREATED 0x2
+typedef enum _UPS_ERROR_CONDITION {
+ UpsNoError = 0,
+ UpsErrorRegisterControlHandler,
+ UpsErrorCreateEvent,
+ UpsErrorNotifyServiceController,
+ UpsErrorCtrlHandler,
+ UpsErrorShutdownParameters,
+ UpsErrorLoadLibrary,
+ UpsErrorRegisterEventSource,
+ UpsErrorGetConfig,
+ UpsErrorRegisterService,
+ UpsErrorOpenCommPort,
+ UpsErrorModemStatus,
+ UpsErrorSignalAsserted,
+ UpsErrorDtr,
+ UpsErrorSetRts,
+ UpsErrorSetTx,
+ UpsErrorWait,
+ UpsErrorSetCommMask,
+ UpsErrorAdjustPrivilege,
+ UpsErrorExitWindowsEx,
+ UpsErrorSystemShutdown
+} UPS_ERROR_CONDITION, *PUPS_ERROR_CONDITION;
+
+
+VOID _CRTAPI1 main(VOID);
+VOID UPS_main( VOID);
+
+
+VOID UpsControlHandler( DWORD Opcode);
+VOID UpsHandleError(
+ UPS_ERROR_CONDITION failingCondition,
+ DWORD majorError,
+ DWORD minorError
+ );
+VOID UpsInitialize( VOID );
+VOID UpsSendMessage( PBOOL pFirstMessageSent);
+VOID UpsShutdown( DWORD status);
+VOID UpsSystemShutdown( VOID);
+BOOL UpsTurnOff( DWORD ControlType);
+DWORD UpsUpdateStatus( VOID);
+
+
+CHAR UpsGlobalCommand[ MAX_PATH];
+DWORD UpsGlobalCommMask;
+DWORD UpsGlobalActiveSignals;
+UPS_TIME UpsGlobalBatteryTime;
+UPS_CONFIG UpsGlobalConfig;
+HANDLE UpsGlobalCommPort;
+DWORD UpsGlobalModemStatus;
+BOOL UpsGlobalTurnOff;
+OVERLAPPED UpsGlobalOverlap;
+SERVICE_STATUS UpsGlobalServiceStatus;
+
+SERVICE_STATUS_HANDLE UpsGlobalServiceStatusHandle;
+HANDLE UpsGlobalDoneEvent;
+HANDLE UpsGlobalMessageThread;
+HANDLE UpsGlobalMainThread;
+HANDLE UpsGlobalLogFileHandle;
+HANDLE UpsGlobalMessageFileHandle;
+HANDLE g_hMessageDone = NULL;
+
+
+VOID _CRTAPI1
+main(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ Dispatch all the service thread to the service controller.
+
+--*/
+{
+ SERVICE_TABLE_ENTRY DispatchTable[] = {
+ { SERVICE_UPS, (LPSERVICE_MAIN_FUNCTION)UPS_main },
+ { NULL, NULL }
+ };
+ StartServiceCtrlDispatcher( DispatchTable); // waits till all threads end
+ KdPrint(( "[UPS] The Service is terminating....\n"));
+ ExitProcess(0);
+}
+
+
+VOID
+UPS_main(
+ VOID
+ )
+/*++
+Routine Description:
+
+ Register the control handler with the service controller, and create
+ the thread for the main UPS service. The Routine would then wait on
+ an event which tells it to terminate. This is needed since we don't
+ want to do ExitProcess() during the UPS service, and we want all threads
+ and handles to be terminated cleanly.
+
+Arguments:
+
+ None.
+
+Return value:
+
+ None.
+
+--*/
+{
+ DWORD ThreadId;
+ DWORD ModemStatus;
+ BOOL FirstMessageSent = FALSE;
+ DWORD status;
+ HANDLE event[ 2];
+
+ UpsInitialize();
+
+ event[ 0] = UpsGlobalOverlap.hEvent;
+ event[ 1] = UpsGlobalDoneEvent;
+
+ for ( ; ;) {
+
+ // A while loop waiting for a power to fail. Note that Comm Port
+ // event that triggers the waiting object may be different from the
+ // result we read from GetCommModemStatus() (e.g. a glitch in the
+ // signal line. The while loop is used to eliminate effects of
+ // glitches.
+
+ // Note that WaitCommEvent() is triggered on the changing of level of
+ // the modem signals, but not the actual level. We want the wait
+ // event to be independent of the current state of the signal lines,
+ // and going through the loop once ensures that no signal lines are
+ // asserted before we start a wait.
+
+ for ( ; ;) {
+
+ BOOL ForkMessage = FALSE;
+
+ UpsUpdateTime( CHARGE); // Update charge time
+
+ if ( !GetCommModemStatus( UpsGlobalCommPort, &ModemStatus)) {
+ UpsHandleError( UpsErrorModemStatus, NERR_UPSInvalidCommPort, GetLastError());
+ }
+
+ switch( UpsGlobalActiveSignals) {
+
+ case UPS_POWERFAILSIGNAL:
+
+ if ( UpsLineAsserted( ModemStatus, LINE_FAIL)) {
+ if ( UpsGlobalBatteryTime.StoredTime > (time_t)UpsGlobalConfig.ShutdownWait) {
+ ForkMessage = TRUE; // enough time to send a warning message
+ } else {
+ //
+ // Log an event even if we have little time left.
+ //
+ UpsReportEvent( NELOG_UPS_PowerOut, NULL, ERROR_SUCCESS);
+ UpsSystemShutdown();
+ }
+ }
+ break;
+
+ case UPS_LOWBATTERYSIGNAL:
+
+ if ( UpsLineAsserted( ModemStatus, LOW_BATT)) {
+ UpsSystemShutdown(); // no double log & alert for immediate shutdown
+ }
+ break;
+
+ case UPS_LOWBATTERYSIGNAL|UPS_POWERFAILSIGNAL:
+
+ // UpsSystemShutdown(); // for debugging purposes only - BUGBUG
+
+ if ( UpsLineAsserted( ModemStatus, LOW_BATT)){
+ UpsSystemShutdown();
+ }
+ if ( UpsLineAsserted( ModemStatus, LINE_FAIL)) {
+ ForkMessage = TRUE;
+ }
+ break;
+ }
+
+
+ if ( ForkMessage == TRUE) {
+
+ UpsReportEvent( NELOG_UPS_PowerOut, NULL, ERROR_SUCCESS);
+ UpsAlertRaise( ALERT_PowerOut);
+
+ KdPrint(("[UPS] UPS_main: send PowerOut message\n"));
+
+ UpsNotifyUsers(
+ 0,
+ UpsGlobalMessageFileHandle,
+ UPS_ACTION_PAUSE_SERVER,
+ L""
+ );
+
+ UpsGlobalMessageThread = CreateThread(
+ NULL,
+ 0,
+ (LPTHREAD_START_ROUTINE)UpsSendMessage,
+ (LPVOID)&FirstMessageSent,
+ 0,
+ (LPDWORD)&ThreadId
+ );
+
+ if ( UpsGlobalMessageThread == NULL) {
+ //
+ // We do not care too much if message thread failed.
+ // BUGBUG We should probably LOG an event though.
+ //
+ KdPrint(("[UPS] Create message thread error %d\n", GetLastError()));
+ }
+
+ break; // start loop - waiting for power to come back
+ }
+
+ // The return value for the WaitCommEvent is not important
+ // since we need to read the CommPort in the loop anyway.
+
+ WaitCommEvent( UpsGlobalCommPort, &ModemStatus, &UpsGlobalOverlap);
+ status = WaitForMultipleObjects( 2, event, FALSE, INFINITE);
+ if ( status != 0) {
+ UpsHandleError( UpsErrorWait, status == 1 ? NO_ERROR : status, status);
+ }
+ } // end of loop - waiting for power to fail
+
+
+ // While loop for waiting for power to come back. This depends on
+ // the fact that UpsGlobalActiveSignals is either UPS_POWERFAILSIGNAL or
+ // UPS_POWERFAILSIGNAL|UPS_LOWBATTERY, and that UpdateTime() has been
+ // called recently.
+
+ for ( ; ; ) {
+
+ DWORD WaitTime;
+ BOOL PowerBack = FALSE;
+
+ // Battery time is taken into account only if the configuration
+ // is UPS_POWERFAILSIGNAL. Also, in this case we wait only if
+ // battery time is larger than UpsGlobalConfig.ShutdownWait.
+
+ if ( UpsGlobalActiveSignals != UPS_POWERFAILSIGNAL ||
+ UpsGlobalBatteryTime.StoredTime > (time_t)UpsGlobalConfig.ShutdownWait) {
+
+ WaitTime = (UpsGlobalActiveSignals != UPS_POWERFAILSIGNAL) ? INFINITE :
+ ((UpsGlobalBatteryTime.StoredTime-UpsGlobalConfig.ShutdownWait) * 1000);
+
+ WaitCommEvent( UpsGlobalCommPort, &ModemStatus, &UpsGlobalOverlap);
+ status = WaitForMultipleObjects( 2, event, FALSE, WaitTime);
+
+ // If there was a change in signals (status 0) or wait expired
+ // (status WAIT_TIMEOUT && WaitTime finite) we look at signals.
+ // Else, we die here either becuse the service was ordered to
+ // die (status 1) or becuse of an unexpected error.
+
+ if ( !( status == 0 ||
+ status == WAIT_TIMEOUT && WaitTime != INFINITE)) {
+ UpsHandleError( UpsErrorWait, status == 1 ? NO_ERROR : status, status);
+ }
+ }
+
+ if ( !GetCommModemStatus( UpsGlobalCommPort, &ModemStatus)) {
+ UpsHandleError( UpsErrorModemStatus, NERR_UPSInvalidCommPort, GetLastError());
+ }
+
+ switch( UpsGlobalActiveSignals) {
+
+ case UPS_POWERFAILSIGNAL:
+
+ if ( !UpsLineAsserted( ModemStatus, LINE_FAIL)){
+ PowerBack = TRUE;
+ } else if ( status == WAIT_TIMEOUT) {
+ UpsSystemShutdown();
+ } else {
+ // a very unlikely case; used to remove glitches ?
+ UpsUpdateTime( DISCHARGE);
+ }
+ break;
+
+ case UPS_LOWBATTERYSIGNAL|UPS_POWERFAILSIGNAL:
+
+ if( UpsLineAsserted( ModemStatus, LOW_BATT)) {
+ UpsSystemShutdown();
+ }
+
+ if( !UpsLineAsserted( ModemStatus, LINE_FAIL)) {
+ PowerBack = TRUE;
+ }
+ break;
+ }
+
+
+ if ( PowerBack == TRUE) {
+
+ UpsUpdateTime( DISCHARGE);
+ if (!SetEvent(g_hMessageDone))
+ {
+ KdPrint(("[UPS] Error setting Message Done Event: %d\n", GetLastError()));
+ ASSERT( FALSE);
+ }
+ //
+ // note: the INFINITE wait time relies on the fact that the
+ // message thread will exit "quickly." If the message thread
+ // is changed to block on some event in addition to
+ // g_hMessageDone, then the wait time should be set to some
+ // non-INFINITE value to avoid deadlock.
+ //
+ status = WaitForSingleObject(UpsGlobalMessageThread, INFINITE);
+ if (status != WAIT_OBJECT_0)
+ {
+ KdPrint(("[UPS] Error waiting for Message thread exit Event: %d\n", status));
+ ASSERT( FALSE);
+ }
+ CloseHandle(UpsGlobalMessageThread);
+ UpsGlobalMessageThread = NULL;
+
+ // FirstMessageSent is TRUE (set by the sent message thread)
+ // if the first message has ever been sent to the users on
+ // the server. If the users haven't been told that power
+ // has been down, don't bother telling them that power has
+ // returned.
+
+ if ( FirstMessageSent == TRUE) {
+ UpsNotifyUsers(
+ APE2_UPS_POWER_BACK,
+ UpsGlobalMessageFileHandle,
+ UPS_ACTION_SEND_MESSAGE | UPS_ACTION_CONTINUE_SERVER,
+ UpsGlobalConfig.ComputerName,
+ L""
+ );
+ FirstMessageSent = FALSE;
+
+ } else {
+ UpsNotifyUsers(
+ 0,
+ UpsGlobalMessageFileHandle,
+ UPS_ACTION_CONTINUE_SERVER,
+ L""
+ );
+ }
+ UpsReportEvent( NELOG_UPS_PowerBack, NULL, ERROR_SUCCESS);
+ UpsAlertRaise( ALERT_PowerBack);
+
+ if ( !SetCommMask( UpsGlobalCommPort, UpsGlobalCommMask)) {
+ UpsHandleError( UpsErrorSetCommMask, NERR_UPSInvalidCommPort, GetLastError());
+ }
+
+ break; // start loop - waiting for power to fail
+ }
+ } // end of loop - waiting for power to come back
+ }
+}
+
+
+VOID
+UpsControlHandler(
+ DWORD OpCode
+ )
+/*++
+Routine Description:
+
+ The controller handler being passed to the service controller.
+--*/
+{
+ switch( OpCode) {
+
+ case SERVICE_CONTROL_STOP:
+
+ if ( UpsGlobalServiceStatus.dwCurrentState != SERVICE_STOP_PENDING) {
+
+ KdPrint(("[UPS] ControlHandler: service stop\n"));
+ UpsGlobalServiceStatus.dwWin32ExitCode = 1;
+ UpsGlobalServiceStatus.dwCurrentState = SERVICE_STOP_PENDING;
+
+ (VOID)UpsUpdateStatus();
+
+ if ( !SetEvent( UpsGlobalDoneEvent)) {
+ KdPrint(("[UPS] Error setting DoneEvent: %d\n", GetLastError()));
+ ASSERT( FALSE);
+ }
+ return;
+ }
+ break;
+
+ case SERVICE_CONTROL_PAUSE:
+ KdPrint(("[UPS] ControlHandler: service not pausible\n"));
+ break;
+
+ case SERVICE_CONTROL_CONTINUE:
+ KdPrint(("[UPS] ControlHandler: service not continuible\n"));
+ break;
+
+ case SERVICE_CONTROL_INTERROGATE:
+ break;
+
+ default:
+ KdPrint(("[UPS] ControlHandler: unknown OpCode=%d\n", OpCode));
+ break;
+ }
+
+ (VOID)UpsUpdateStatus();
+}
+
+
+
+VOID
+UpsCreateProcess(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This function executes shutdown command process & waits for it to
+ complete. If shutdown command process fails to complete in 30
+ seconds, then we write an event and kill the shutdown command process.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PROCESS_INFORMATION ProcessInformation;
+ STARTUPINFO StartupInfo;
+ BOOL success;
+ DWORD status;
+
+ if ( UpsGlobalCommand[ 0] == '\0') {
+ return;
+ }
+
+ GetStartupInfo( &StartupInfo);
+ StartupInfo.lpTitle = NULL;
+
+ //
+ // It makes no difference whether dwCreationFlags are "0" or
+ // "CREATE_NEW_CONSOLE".
+ //
+ success = CreateProcess(
+ NULL, // image name is imbedded in the command line
+ UpsGlobalCommand, // command line
+ NULL, // pSecAttrProcess
+ NULL, // pSecAttrThread
+ FALSE, // this process will not inherit our handles
+ 0, // dwCreationFlags
+// CREATE_NEW_CONSOLE, // dwCreationFlags
+ NULL, // pEnvironment
+ NULL, // pCurrentDirectory
+ &StartupInfo,
+ &ProcessInformation
+ );
+
+ if ( success == FALSE) {
+
+ DWORD Error = GetLastError();
+
+ KdPrint((
+ "[UPS] CreateProcess: CreateProcess( %s) fails with Error=(dec)%d\n",
+ UpsGlobalCommand,
+ Error
+ ));
+
+ UpsReportEvent( NELOG_UPS_CmdFileExec, UpsGlobalCommand, Error);
+ return;
+ }
+
+ CloseHandle( ProcessInformation.hThread);
+
+ status = WaitForSingleObject(
+ ProcessInformation.hProcess,
+ COMMAND_WAIT_TIME * 1000
+ );
+
+ if ( status != WAIT_OBJECT_0) {
+ KdPrint((
+ "[UPS] CreateProcess: WaitForSingleObject( %s) fails with "
+ "status = 0x%x\n",
+ UpsGlobalCommand,
+ status
+ ));
+ UpsReportEvent( NELOG_UPS_CmdFileError, NULL, ERROR_SUCCESS);
+#ifdef NOT_YET
+ //
+ // Terminating the process does not guarantee successful shutdown
+ // since it may have children processes which still hang around.
+ // (e.g. a case of a batch file).
+ //
+ if ( !TerminateProcess( ProcessInformation.hProcess, NO_ERROR)) {
+ KdPrint((
+ "[UPS] CreateProcess: TerminateProcess( 0x%x) fails with"
+ "error = (dec)%d\n",
+ ProcessInformation.hProcess,
+ GetLastError()
+ ));
+ }
+#endif // NOT_YET
+ }
+
+ CloseHandle( ProcessInformation.hProcess);
+}
+
+
+
+VOID
+UpsHandleError(
+ IN UPS_ERROR_CONDITION failingCondition,
+ IN DWORD majorError,
+ IN DWORD minorError
+ )
+/*++
+
+Routine Description:
+
+ This function handles a Schedule service error condition. If the error
+ condition is fatal, it shuts down the Schedule service.
+
+Arguments:
+
+ failingCondition - Supplies a value which indicates what the failure is.
+
+ majorError - supplies the major error code for the failure
+
+ minorError - supplies the minor error code for the failure. If minorError
+ equals NO_ERROR then its value is irrelevant. This argument
+ is used in the debugging build only.
+
+Return Value:
+
+ None.
+
+--*/
+{
+
+#ifdef UPS_DEBUG
+ PCHAR string;
+#endif // UPS_DEBUG
+
+ UpsGlobalServiceStatus.dwWin32ExitCode = 1;
+ UpsGlobalServiceStatus.dwCurrentState = SERVICE_STOP_PENDING;
+ (VOID)UpsUpdateStatus();
+
+#ifdef UPS_DEBUG
+ switch (failingCondition) {
+
+ case UpsNoError:
+ string = "Success";
+ break;
+
+ case UpsErrorRegisterControlHandler:
+ string = "Cannot register control handler";
+ break;
+
+ case UpsErrorCreateEvent:
+ string = "CreateEvent() error";
+ break;
+
+ case UpsErrorNotifyServiceController:
+ string = "SetServiceStatus() error";
+ break;
+
+ case UpsErrorCtrlHandler:
+ string = "SetConsoleCtrlHandler() error";
+ break;
+
+ case UpsErrorShutdownParameters:
+ string = "SetProcessShutdownParameters() error";
+ break;
+
+ case UpsErrorLoadLibrary:
+ string = "LoadLibrary() error";
+ break;
+
+ case UpsErrorRegisterEventSource:
+ string = "Cannot register event source";
+ break;
+
+ case UpsErrorGetConfig:
+ string = "UpsGetConfig() error";
+ break;
+
+ case UpsErrorModemStatus:
+ string = "GetCommModemStatus() error";
+ break;
+
+ case UpsErrorSignalAsserted:
+ string = "Signal line is being asserted, error";
+ break;
+
+ case UpsErrorRegisterService:
+ string = "RegisterServiceCtrlHandler() error";
+ break;
+
+ case UpsErrorOpenCommPort:
+ string = "OpenCommPort() error";
+ break;
+
+ case UpsErrorWait:
+ string = "WaitForMultipleObject() returns status ";
+ break;
+
+ case UpsErrorDtr:
+ string = "Set/Clear DTR error";
+ break;
+
+ case UpsErrorSetRts:
+ string = "Set RTS error";
+ break;
+
+ case UpsErrorSetTx:
+ string = "Set TX error";
+ break;
+
+ case UpsErrorSetCommMask:
+ string = "SetCommMask() error";
+ break;
+
+ case UpsErrorAdjustPrivilege:
+ string = "RtlAdjustPrivilege() error";
+ break;
+
+ case UpsErrorExitWindowsEx:
+ string = "ExitWindowsEx() error";
+ break;
+
+ case UpsErrorSystemShutdown:
+ string = "UpsSystemShutdown() error";
+ break;
+
+ default:
+ string = "Unknown error condition";
+ ASSERT( FALSE);
+ break;
+ }
+
+ KdPrint((
+ "[UPS] %s %u (dec)\n",
+ string,
+ minorError != NO_ERROR ? minorError : majorError
+ ));
+#endif // UPS_DEBUG
+
+ UpsShutdown( majorError);
+}
+
+
+
+VOID
+UpsInitialize(
+ VOID
+ )
+{
+ SECURITY_ATTRIBUTES SecurityAttributes;
+ DWORD ModemStatus;
+ BOOL asserted;
+ DWORD status;
+#ifdef UPS_TOGGLER
+ char temp[17];
+ char line[256];
+ STARTUPINFO SInfo;
+ PROCESS_INFORMATION PInfo;
+#endif // UPS_TOGGLER
+
+ UpsGlobalCommPort = NULL;
+ UpsGlobalLogFileHandle = NULL;
+ UpsGlobalMessageFileHandle = NULL;
+ UpsGlobalDoneEvent = NULL;
+ UpsGlobalOverlap.hEvent = NULL;
+ UpsGlobalMessageThread = NULL;
+ UpsGlobalCommand[ 0] = '\0';
+
+ UpsGlobalServiceStatus.dwServiceType = SERVICE_WIN32;
+ UpsGlobalServiceStatus.dwCurrentState = SERVICE_START_PENDING;
+ UpsGlobalServiceStatus.dwControlsAccepted = 0;
+ UpsGlobalServiceStatus.dwCheckPoint = 1;
+ UpsGlobalServiceStatus.dwWaitHint = 15000; // 15 seconds
+
+ SET_SERVICE_EXITCODE(
+ NO_ERROR,
+ UpsGlobalServiceStatus.dwWin32ExitCode,
+ UpsGlobalServiceStatus.dwServiceSpecificExitCode
+ );
+
+ UpsGlobalServiceStatusHandle = RegisterServiceCtrlHandler(
+ SERVICE_UPS,
+ UpsControlHandler
+ );
+ if ( UpsGlobalServiceStatusHandle == (SERVICE_STATUS_HANDLE)NULL) {
+ UpsHandleError( UpsErrorRegisterService, GetLastError(), NO_ERROR);
+ }
+
+ if ( ( status = UpsUpdateStatus()) != NO_ERROR) {
+ UpsHandleError( UpsErrorNotifyServiceController, status, NO_ERROR);
+ }
+
+ UpsGlobalDoneEvent = CreateEvent(
+ NULL, //security
+ FALSE, //autoreset
+ FALSE, //initial-state
+ NULL //event name
+ );
+ if ( UpsGlobalDoneEvent == NULL) {
+ UpsHandleError( UpsErrorCreateEvent, GetLastError(), NO_ERROR);
+ }
+
+ UpsGlobalOverlap.hEvent = CreateEvent(
+ NULL, //Security
+ FALSE, //AutoReset
+ FALSE, //InitialState
+ NULL //Name of Event
+ );
+ if ( UpsGlobalOverlap.hEvent == NULL) {
+ UpsHandleError( UpsErrorCreateEvent, GetLastError(), NO_ERROR);
+ }
+
+ g_hMessageDone = CreateEvent(NULL, FALSE, FALSE, NULL);
+
+ if (g_hMessageDone == NULL)
+ {
+ UpsHandleError(UpsErrorCreateEvent, GetLastError(), NO_ERROR);
+ }
+
+ UpsGlobalBatteryTime.MarkTime = time((time_t *)NULL);
+ UpsGlobalBatteryTime.StoredTime = (time_t)0;
+
+ UpsGlobalLogFileHandle = RegisterEventSource( NULL, SERVICE_UPS);
+ if ( UpsGlobalLogFileHandle == NULL) {
+ UpsHandleError( UpsErrorRegisterEventSource, GetLastError(), NO_ERROR);
+ }
+
+ if ( ( status = UpsUpdateStatus()) != NO_ERROR) {
+ UpsHandleError( UpsErrorNotifyServiceController, status, NO_ERROR);
+ }
+
+ if ( UpsGetConfig() != TRUE){
+ UpsHandleError( UpsErrorGetConfig, NERR_UPSInvalidConfig, NO_ERROR);
+ }
+
+ if ( ( UpsGlobalMessageFileHandle = LoadLibrary( MODULENAME)) == NULL) {
+ UpsHandleError( UpsErrorLoadLibrary, NERR_UPSInvalidConfig, GetLastError());
+ }
+
+ //Setting Security Attribute for inheritable handle
+ SecurityAttributes.nLength = sizeof(SECURITY_ATTRIBUTES);
+ SecurityAttributes.lpSecurityDescriptor = NULL;
+
+#ifdef UPS_TOGGLER
+ SecurityAttributes.bInheritHandle = TRUE;
+#else // UPS_TOGGLER
+ SecurityAttributes.bInheritHandle = FALSE;
+#endif // UPS_TOGGLER
+
+ UpsGlobalCommPort = CreateFile(
+ (LPCSTR)UpsGlobalConfig.Port, // comm port to open
+ GENERIC_READ | GENERIC_WRITE,
+ 0,
+ &SecurityAttributes,
+ OPEN_EXISTING,
+ FILE_FLAG_OVERLAPPED,
+ NULL
+ );
+ if ( UpsGlobalCommPort == INVALID_HANDLE_VALUE) {
+ UpsHandleError( UpsErrorOpenCommPort, NERR_UPSInvalidCommPort, GetLastError());
+ }
+
+#ifndef UPS_TOGGLER
+
+ // This is needed for supporting UPS turnoff. We write a value to
+ // the Comm Port to keep the UPS battery up. The value is written
+ // even if UPS_CANTURNOFF is clear. This corresponds to two cases:
+ // (a) UPS that really cannot be turned off
+ // (b) UPS that can be turned off but user does not want it turned off
+ // In case (a) we hope that it will not hurt anything if we do a
+ // meaningless action of SETDTR/CLRDTR.
+ // In case (b) a call to SETDTR/CLRDTR will keep UPS battery up
+ // after power failure. Without this call, UPS battery may immediately
+ // shut down after power failure.
+ // This is a workaround for the current UPS applet interface.
+ // Ideally, we should have an extra bit to distinguish between the two
+ // cases above (say, UPS_SHOULDTURNOFF bit).
+
+ if ( UpsGlobalConfig.Options & UPS_CANTURNOFF) {
+
+ if ( UpsGlobalConfig.Options & UPS_POSSIGSHUTOFF) {
+ ModemStatus = CLRDTR;
+ UpsGlobalModemStatus = SETDTR;
+ } else {
+ ModemStatus = SETDTR;
+ UpsGlobalModemStatus = CLRDTR;
+ }
+ UpsGlobalTurnOff = FALSE;
+
+ if ( !EscapeCommFunction( UpsGlobalCommPort, ModemStatus)) {
+ UpsHandleError( UpsErrorDtr, NERR_UPSInvalidCommPort, GetLastError());
+ }
+
+ if ( !SetConsoleCtrlHandler( UpsTurnOff, TRUE)) {
+ UpsHandleError( UpsErrorCtrlHandler, GetLastError(), NO_ERROR);
+ }
+
+ if ( !SetProcessShutdownParameters( 0, SHUTDOWN_NORETRY)) {
+ UpsHandleError( UpsErrorShutdownParameters, GetLastError(), NO_ERROR);
+ }
+
+ } else {
+ ModemStatus = UpsGlobalConfig.Options & UPS_POSSIGSHUTOFF ? CLRDTR : SETDTR;
+ (VOID)EscapeCommFunction( UpsGlobalCommPort, ModemStatus); // see above
+ }
+
+ // This is needed for supporting contact closure configuration.
+ // The two signals are needed to supply the power sources for
+ // contact closure. RTS - positive, TX - negative
+
+ if ( !EscapeCommFunction( UpsGlobalCommPort, SETRTS)) {
+ UpsHandleError( UpsErrorSetRts, NERR_UPSInvalidCommPort, GetLastError());
+ }
+
+ if ( !EscapeCommFunction(UpsGlobalCommPort, SETXOFF)) {
+ UpsHandleError( UpsErrorSetTx, NERR_UPSInvalidCommPort, GetLastError());
+ }
+
+#else // UPS_TOGGLER
+
+ strcpy(line,"c:/nt/private/net/svcdlls/upssvc/obj/i386/toggle.exe ");
+ _itoa( (DWORD)UpsGlobalCommPort, temp, 10); //passing handle to be inherited
+ strcat(line,temp);
+ GetStartupInfo(&SInfo);
+ SInfo.lpTitle = "toggle";
+
+ if (!CreateProcess(
+ NULL, //application name
+ line, //command line
+ NULL, //Process Attribute
+ NULL, //Thread Attribute
+ TRUE, //Inherit Handle
+ CREATE_NEW_CONSOLE, //Creation Flag
+ NULL, //Environment,
+ NULL, //CurrentDirectory
+ &SInfo, //Startup info, share the same as parent
+ &PInfo //Process info
+ )) {
+ UpsHandleError( UpsErrorCreateProcess, GetLastError());
+ }
+
+ if ( ( status = UpsUpdateStatus()) != NO_ERROR) {
+ UpsHandleError( UpsErrorNotifyServiceController, status, NO_ERROR);
+ }
+
+ Sleep( 10000); //10 seconds to reset the signals
+
+#endif // UPS_TOGGLER
+
+ // This is required for contact closure configuration. The power
+ // of the contact closure inputs come from the signal pins, and
+ // they require time to settle. This is at least true for TrippLite
+ // UPS. (by experiment) 3 seconds is kind of arbitary.
+
+ if ( ( status = UpsUpdateStatus()) != NO_ERROR) {
+ UpsHandleError( UpsErrorNotifyServiceController, status, NO_ERROR);
+ }
+
+ Sleep( 3000);
+
+ // don't start if any of the relevant lines is asserted
+
+ if ( !GetCommModemStatus( UpsGlobalCommPort, &ModemStatus)) {
+ UpsHandleError( UpsErrorModemStatus, NERR_UPSInvalidCommPort, GetLastError());
+ }
+
+ // Determing the Comm mask from the configuration. Only monitor
+ // the valid signals on the comm port. Do not start if any of these
+ // valid signals is asserted.
+
+ UpsGlobalActiveSignals =
+ ( UpsGlobalConfig.Options & ( UPS_POWERFAILSIGNAL | UPS_LOWBATTERYSIGNAL));
+
+ switch( UpsGlobalActiveSignals) {
+
+ case UPS_POWERFAILSIGNAL:
+ UpsGlobalCommMask = LINE_FAIL_MASK;
+ asserted = UpsLineAsserted( ModemStatus, LINE_FAIL);
+ break;
+
+ case UPS_LOWBATTERYSIGNAL:
+ UpsGlobalCommMask = LOW_BATT_MASK;
+ asserted = UpsLineAsserted( ModemStatus, LOW_BATT);
+ break;
+
+ case (UPS_LOWBATTERYSIGNAL | UPS_POWERFAILSIGNAL):
+ UpsGlobalCommMask = (LINE_FAIL_MASK | LOW_BATT_MASK);
+ asserted = UpsLineAsserted( ModemStatus, LINE_FAIL) ||
+ UpsLineAsserted( ModemStatus, LOW_BATT);
+ break;
+ }
+
+ if ( asserted) {
+ UpsHandleError( UpsErrorSignalAsserted, NERR_UPSSignalAsserted, NO_ERROR);
+ }
+
+ if ( !SetCommMask( UpsGlobalCommPort, UpsGlobalCommMask)) {
+ UpsHandleError( UpsErrorSetCommMask, NERR_UPSInvalidCommPort, GetLastError());
+ }
+
+ // we don't accept pause or continue
+ UpsGlobalServiceStatus.dwCurrentState = SERVICE_RUNNING;
+ UpsGlobalServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
+
+ if ((status = UpsUpdateStatus()) != NO_ERROR) {
+ UpsHandleError( UpsErrorNotifyServiceController, status, NO_ERROR);
+ }
+ KdPrint(("[UPS] Init completed successfully, waiting for comm port events.\n"));
+}
+
+
+VOID
+UpsSendMessage(
+ PBOOL pFirstMessageSent
+ )
+/*++
+
+Routine Description
+
+ This is the message thread spawned by the main UPS service thread.
+ The first message would be sent out after the FirstMessDelay in the
+ config field. Other messages show up after every MessageInterval.
+ The BOOL FirstMessageSent is used to tell the UPS service thread whether
+ a message has been sent to every users with sessions, so that users
+ won't be notified about power coming back if they don't know that power
+ has been down.
+
+--*/
+{
+ DWORD dwRet;
+ dwRet = WaitForSingleObject(g_hMessageDone,
+ UpsGlobalConfig.FirstMessageDelay * 1000);
+ if (dwRet == WAIT_OBJECT_0)
+ {
+ ExitThread(0);
+ }
+
+ *pFirstMessageSent = TRUE;
+
+ for( ; ;) {
+ UpsNotifyUsers(
+ APE2_UPS_POWER_OUT,
+ UpsGlobalMessageFileHandle,
+ UPS_ACTION_SEND_MESSAGE,
+ UpsGlobalConfig.ComputerName,
+ L""
+ );
+ dwRet = WaitForSingleObject(g_hMessageDone,
+ UpsGlobalConfig.MessageInterval * 1000);
+ if (dwRet == WAIT_OBJECT_0)
+ {
+ ExitThread(0);
+ }
+ }
+}
+
+
+
+VOID
+UpsShutdown(
+ DWORD status
+ )
+{
+ if ( status != NO_ERROR && UpsGlobalLogFileHandle != NULL) {
+ UpsReportEvent( status, NULL, ERROR_SUCCESS);
+ }
+
+ if (UpsGlobalMessageThread != NULL)
+ {
+ if (!SetEvent(g_hMessageDone))
+ {
+ KdPrint(("[UPS] Error setting Message Done Event: %d\n", GetLastError()));
+ }
+ WaitForSingleObject(UpsGlobalMessageThread, INFINITE);
+ CloseHandle(UpsGlobalMessageThread);
+ }
+
+ if ( UpsGlobalDoneEvent != NULL) {
+ CloseHandle( UpsGlobalDoneEvent);
+ }
+
+ if ( UpsGlobalOverlap.hEvent != NULL) {
+ CloseHandle( UpsGlobalOverlap.hEvent);
+ }
+
+ if (g_hMessageDone != NULL)
+ {
+ CloseHandle(g_hMessageDone);
+ }
+
+ if ( UpsGlobalCommPort != NULL) {
+ CloseHandle( UpsGlobalCommPort);
+ }
+
+ if (UpsGlobalMessageFileHandle != NULL)
+ {
+ FreeLibrary(UpsGlobalMessageFileHandle);
+ }
+
+ if (UpsGlobalLogFileHandle != NULL)
+ {
+ DeregisterEventSource(UpsGlobalLogFileHandle);
+ }
+
+ // We are done with cleaning up. Tell Service Controller that we are
+ // stopped.
+ //
+ UpsGlobalServiceStatus.dwCurrentState = SERVICE_STOPPED;
+ UpsGlobalServiceStatus.dwControlsAccepted = 0;
+
+
+ SET_SERVICE_EXITCODE(
+ status,
+ UpsGlobalServiceStatus.dwWin32ExitCode,
+ UpsGlobalServiceStatus.dwServiceSpecificExitCode
+ );
+
+ UpsGlobalServiceStatus.dwCheckPoint = 0;
+ UpsGlobalServiceStatus.dwWaitHint = 0;
+
+ (VOID) UpsUpdateStatus();
+ ExitThread( status);
+}
+
+
+VOID
+UpsSystemShutdown(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ Notify users of a shutdown, log the shutdown, and do a shutdown of
+ the system. Once this routine is called, it won't accept a net stop
+ command from the service controller. This ensures a clean shutdown.
+ The UPS service terminates if the ExitWindosEx() call fails.
+
+ It is assumed that when we enter this routine we have at least
+ UpsGlobalConfig.ShutdownWait seconds before UPS battery gives up on us.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+Notes:
+
+ We should really post some additional logs in case of errors. These
+ logs should use the actual errors (e.g. ntStatus or GetLastError())
+ rather than "wrapper" type of error such as NERR_UPSShutdownFailed.
+
+--*/
+{
+ NTSTATUS ntStatus;
+ BOOLEAN myBoolean;
+ DWORD StartTickCount;
+
+ // KdPrint(("[UPS] SystemShutdown: You have 1 minute to setup debugging\n"));
+ // Sleep( 60000); // BUGBUG
+
+ StartTickCount = GetTickCount();
+ KdPrint(("[UPS] SystemShutdown: StartTickCount=0x%x\n", StartTickCount));
+
+ // service don't accept STOP once reaches here.
+ // wants a clean shutdown... nothing should kill this
+ UpsGlobalServiceStatus.dwControlsAccepted = 0;
+ SetServiceStatus(UpsGlobalServiceStatusHandle, &UpsGlobalServiceStatus);
+
+ // kill message thread if started
+ if (UpsGlobalMessageThread != NULL)
+ {
+ if (!SetEvent(g_hMessageDone))
+ {
+ KdPrint(("[UPS] Error setting Message Done Event: %d\n", GetLastError()));
+ }
+ WaitForSingleObject(UpsGlobalMessageThread, INFINITE);
+ CloseHandle(UpsGlobalMessageThread);
+ UpsGlobalMessageThread = NULL;
+ }
+
+ UpsReportEvent( NELOG_UPS_Shutdown, NULL, ERROR_SUCCESS);
+ UpsAlertRaise( ALERT_PowerShutdown);
+
+ UpsCreateProcess();
+
+ // Sleep for a few seconds to allow alerter a little extra time to
+ // process the shutdown alert before we stop the server.
+
+ Sleep( 3000);
+
+ UpsNotifyUsers(
+ APE2_UPS_POWER_SHUTDOWN_FINAL,
+ UpsGlobalMessageFileHandle,
+ UPS_ACTION_SEND_MESSAGE | UPS_ACTION_STOP_SERVER,
+ UpsGlobalConfig.ComputerName,
+ L""
+ );
+
+ // RtlAdjustPrivilege() & ExitWindowsEx() mods below == fix for bug #6032
+
+ ntStatus = RtlAdjustPrivilege(
+ SE_SHUTDOWN_PRIVILEGE,
+ TRUE,
+ FALSE,
+ &myBoolean // was it enabled or not
+ );
+ if ( ntStatus != STATUS_SUCCESS) {
+ KdPrint((
+ "[UPS] SystemShutdown: RtlAdjustPrivilege() failed: ntStatus = 0x%x\n",
+ ntStatus
+ ));
+ UpsHandleError( UpsErrorAdjustPrivilege, NERR_UPSShutdownFailed,
+ RtlNtStatusToDosError( ntStatus));
+ }
+
+ UpsGlobalTurnOff = TRUE;
+
+ if ( !ExitWindowsEx( EWX_SHUTDOWN | EWX_FORCE, (DWORD)-1)) {
+ UpsHandleError( UpsErrorExitWindowsEx, NERR_UPSShutdownFailed, GetLastError());
+ }
+
+ if ( UpsGlobalConfig.Options & UPS_CANTURNOFF) {
+ //
+ // TickCount == elapsed time in milliseconds, is correct even if
+ // GetTickCount() wrapped around once.
+ //
+ DWORD EndTickCount = GetTickCount();
+ DWORD TickCount = EndTickCount - StartTickCount;
+ DWORD ntStatus;
+
+ KdPrint(("[UPS] SystemShutdown: EndTickCount=0x%x\n", EndTickCount));
+
+ if (UpsGlobalConfig.ShutdownWait * 1000 > TickCount) {
+ KdPrint(("[UPS] SystemShutdown: sleep for (dec)%d milliseconds\n",
+ UpsGlobalConfig.ShutdownWait * 1000 - TickCount));
+ Sleep( UpsGlobalConfig.ShutdownWait * 1000 - TickCount);
+ }
+
+ KdPrint(("[UPS] SystemShutdown: no timely callback. "
+ "Turn the power off within main thread.\n"));
+
+ ntStatus = NtShutdownSystem( FALSE);
+
+ KdPrint(("[UPS] SystemShutdown: NtShutdownSystem( FALSE) returns "
+ "ntStatus = 0x%x\n", ntStatus));
+
+ if ( !EscapeCommFunction( UpsGlobalCommPort, UpsGlobalModemStatus)) {
+ DWORD error = GetLastError();
+ KdPrint(("[UPS] SystemShutdown: DTR error, %d\n", error));
+ UpsHandleError( UpsErrorSystemShutdown, NERR_UPSShutdownFailed, error);
+ } else {
+ Sleep(10000); // wait more than 4.5 secs before exiting so that
+ // the serial line remains asserted long enough for
+ // the UPS to act on it.
+ UpsHandleError( UpsErrorSystemShutdown, NERR_UPSShutdownFailed, NO_ERROR);
+ }
+ } else {
+ UpsHandleError( UpsNoError, NO_ERROR, NO_ERROR);
+ }
+}
+
+
+BOOL
+UpsTurnOff(
+ DWORD ControlType
+ )
+/*++
+
+Routine Description:
+
+ Processes CTRL_SHUTDOWN_EVENT signal in case of UPS service initiated
+ shutdown.
+
+Arguments:
+
+ ControlType - control type
+
+Return Value:
+
+ TRUE - for processed signal
+ FALSE - for signal that was not processed
+
+--*/
+{
+ DWORD ntStatus;
+
+ // return( FALSE); // BUGBUG to test error path in UpsSystemShutdown()
+
+ KdPrint(("[UPS] TurnOff: enter with ControlType=(dec)%d\n", ControlType));
+
+ if ( UpsGlobalTurnOff != TRUE) {
+ return( FALSE); // somebody else initiated shutdown
+ }
+
+ if ( ControlType != CTRL_SHUTDOWN_EVENT) {
+ if ( ControlType != CTRL_LOGOFF_EVENT) {
+ KdPrint(("[UPS] TurnOff: unexpected ControlType=%d\n", ControlType));
+ }
+ return( FALSE); // not a signal we process here
+ }
+
+ KdPrint(("[UPS] TurnOff: Turn the power off, GetTickCount=0x%x\n", GetTickCount()));
+
+ ntStatus = NtShutdownSystem( FALSE);
+
+ KdPrint(("[UPS] TurnOff: NtShutdownSystem( FALSE) returns ntStatus = 0x%x\n",
+ ntStatus));
+
+ if ( !EscapeCommFunction(UpsGlobalCommPort, UpsGlobalModemStatus)) {
+ KdPrint(("[UPS] TurnOff: DTR error, %d\n", GetLastError()));
+ return( FALSE); // signal not processed completely
+ }
+ Sleep(10000); // wait more than 4.5 secs before exiting
+
+ // Turning the power of is not instantenous, it takes a second or so.
+ // Thus we may actually succeed in printing the log below!
+
+ KdPrint(("[UPS] TurnOff: Power was turned off.\n"));
+ return( TRUE); // we did it
+}
+
+
+
+DWORD
+UpsUpdateStatus(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This function updates the Schedule service status with the Service
+ Controller.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NO_ERROR or reason for failure.
+
+--*/
+{
+ DWORD status = NO_ERROR;
+
+ if ( UpsGlobalServiceStatusHandle == (SERVICE_STATUS_HANDLE) NULL) {
+ return( ERROR_INVALID_HANDLE);
+ }
+
+ if ( ! SetServiceStatus( UpsGlobalServiceStatusHandle, &UpsGlobalServiceStatus)) {
+ return( GetLastError());
+ }
+
+ return( NO_ERROR);
+}
diff --git a/private/net/svcdlls/upssvc/ups.h b/private/net/svcdlls/upssvc/ups.h
new file mode 100644
index 000000000..81391fec7
--- /dev/null
+++ b/private/net/svcdlls/upssvc/ups.h
@@ -0,0 +1,130 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ ups.h
+
+Abstract:
+
+ This module contains the header files for the UPS service.
+
+Author:
+
+ Kin Hong Kan (t-kinh)
+
+Revision History:
+
+ Who When What
+ -------- -------- ----------------------------------------------
+ t-kinh 8/20/92 Created.
+ vladimv 1992 Big reorgs. Make it look like a real service.
+
+Notes:
+
+
+--*/
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <windef.h>
+#include <nturtl.h>
+#include <windows.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <io.h>
+#include <stdlib.h>
+#include <time.h>
+
+#include <lmcons.h>
+#include <lmalert.h> // ADMIN_OTHER_INFO
+#include <lmerr.h> // NERR_Success used by SET_SERVICE_EXITCODE
+#include <lmerrlog.h> // ERRLOG_BASE
+#include <lmsname.h> // SERVICE_UPS
+#include <alertmsg.h> // ALERT_PowerOut
+#include <apperr2.h> // APE2_UPS_POWER_BACK
+#include <netlib.h> // SET_SERVICE_EXITCODE
+
+#include <upsfunc.h> // UPS_ACTION_STOP_SERVER, ...
+
+#if DBG
+#define UPS_DEBUG
+#endif // DBG
+
+typedef struct _UPS_CONFIG {
+ WCHAR ComputerName[MAX_PATH+1];
+ DWORD Options;
+ DWORD BatteryLife; // in minutes
+ DWORD RechargeRate; // minutes per minute of battery life
+ DWORD FirstMessageDelay; // in seconds
+ DWORD MessageInterval; // in seconds
+ DWORD ShutdownWait; // in seconds
+ CHAR Port[ MAX_PATH]; // e.g. COM1
+} UPS_CONFIG, *PUPS_CONFIG;
+
+typedef struct _UPS_TIME{
+ time_t StoredTime; // seconds stored
+ time_t MarkTime; // seconds elapsed since Dec31, 1899
+} UPS_TIME, *PUPS_TIME;
+
+
+#define MODULENAME "netmsg.dll"
+
+//
+// 2 minutes (120 seconds) is default amount of time assumed for a shutdown
+//
+#define DEFAULTSHUTDOWNWAIT 120
+#define MINSHUTDOWNWAIT 0
+#define MAXSHUTDOWNWAIT 600
+
+//
+// 30 seconds is max amount of time we wait for shutdown command to complete
+//
+#define COMMAND_WAIT_TIME 30
+
+
+// use by UpdateTime() subrountine
+#define CHARGE 0
+#define DISCHARGE 1
+
+// use positive logic here; use LineAsserted() to determine real value
+#define LINE_FAIL MS_CTS_ON
+#define LOW_BATT MS_RLSD_ON
+#define LINE_FAIL_MASK EV_CTS
+#define LOW_BATT_MASK EV_RLSD
+
+// these values are not used by ups applet, talk to markcl
+#define MINBATTERYLIFE 0
+#define MAXBATTERYLIFE 720
+#define MINRECHARGEPERMINUTE 1
+#define MAXRECHARGEPERMINUTE 250
+#define MINFIRSTWARNING 0
+#define MAXFIRSTWARNING 120
+#define MINWARNINGINTERVAL 5
+#define MAXWARNINGINTERVAL 300
+
+// these values are repeated from windows\shell\control\ups\ups.h
+// remove them eventually
+#define UPS_INSTALLED 0x00000001
+#define UPS_POWERFAILSIGNAL 0x00000002
+#define UPS_LOWBATTERYSIGNAL 0x00000004
+#define UPS_CANTURNOFF 0x00000008
+#define UPS_POSSIGONPOWERFAIL 0x00000010
+#define UPS_POSSIGONLOWBATTERY 0x00000020
+#define UPS_POSSIGSHUTOFF 0x00000040
+#define UPS_COMMANDFILE 0x00000080
+
+//
+// Routines exported by subr.c
+//
+
+VOID UpsReportEvent( DWORD MessageId, PCHAR SingleString, DWORD Error);
+BOOL UpsGetConfig( VOID);
+BOOL UpsLineAsserted( DWORD ModemStatus, DWORD Line);
+VOID UpsAlertRaise( DWORD MessageId);
+VOID UpsUpdateTime( DWORD status);
+
+
+
diff --git a/private/net/svcdlls/upssvc/upssvc.rc b/private/net/svcdlls/upssvc/upssvc.rc
new file mode 100644
index 000000000..23225d562
--- /dev/null
+++ b/private/net/svcdlls/upssvc/upssvc.rc
@@ -0,0 +1,11 @@
+#include <windows.h>
+#include <ntverp.h>
+
+#define VER_FILETYPE VFT_APP
+#define VER_FILESUBTYPE VFT2_UNKNOWN
+#define VER_FILEDESCRIPTION_STR "UPS service"
+#define VER_INTERNALNAME_STR "UPS.EXE"
+#define VER_ORIGINALFILENAME_STR "UPS.EXE"
+
+#include "common.ver"
+
diff --git a/private/net/svcdlls/wkssvc/client/makefile b/private/net/svcdlls/wkssvc/client/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/net/svcdlls/wkssvc/client/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/net/svcdlls/wkssvc/client/sources b/private/net/svcdlls/wkssvc/client/sources
new file mode 100644
index 000000000..bb1295a3b
--- /dev/null
+++ b/private/net/svcdlls/wkssvc/client/sources
@@ -0,0 +1,56 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+
+Author:
+
+ Steve Wood (stevewo) 12-Apr-1989
+
+
+Revision History:
+
+!ENDIF
+
+MAJORCOMP = net
+MINORCOMP = wksclient
+
+
+NTPROFILEINPUT=YES
+
+TARGETNAME=wkssvc
+TARGETPATH=obj
+TARGETTYPE=LIBRARY
+
+
+INCLUDES=..;..\..\..\inc;..\..\..\..\inc
+
+
+!IFNDEF DISABLE_NET_UNICODE
+UNICODE=1
+NET_C_DEFINES=-DUNICODE
+!ENDIF
+
+SOURCES=wksstub.c \
+ wksbind.c \
+ wkssvc_c.c
+
+C_DEFINES=-DRPC_NO_WINDOWS_H
+
+WARNING_LEVEL=-W4
+
+UMTYPE=console
+UMTEST=wstest*wstsend*wstsenum*wstinv
+UMLIBS=\nt\public\sdk\lib\*\netapi32.lib \
+ $(BASEDIR)\Public\Sdk\Lib\*\netlib.lib
diff --git a/private/net/svcdlls/wkssvc/client/wksbind.c b/private/net/svcdlls/wkssvc/client/wksbind.c
new file mode 100644
index 000000000..8604bff51
--- /dev/null
+++ b/private/net/svcdlls/wkssvc/client/wksbind.c
@@ -0,0 +1,195 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ wksbind.c
+
+Abstract:
+
+ Routines which use RPC to bind and unbind the client to the Workstaion
+ service.
+
+Author:
+
+ Rita Wong (ritaw) 14-May-1991
+
+Environment:
+
+ User Mode -Win32
+
+Revision History:
+
+--*/
+
+#include "wsclient.h"
+
+
+handle_t
+WKSSVC_IMPERSONATE_HANDLE_bind(
+ WKSSVC_IMPERSONATE_HANDLE ServerName
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is called from the Workstation service client stubs when
+ it is necessary create an RPC binding to the server end with
+ impersonation level of impersonation.
+
+Arguments:
+
+ ServerName - A pointer to a string containing the name of the server
+ to bind with.
+
+Return Value:
+
+ The binding handle is returned to the stub routine. If the bind is
+ unsuccessful, a NULL will be returned.
+
+--*/
+{
+ handle_t BindHandle;
+ RPC_STATUS RpcStatus;
+
+ RpcStatus = NetpBindRpc (
+ ServerName,
+ WORKSTATION_INTERFACE_NAME,
+ TEXT("Security=Impersonation Dynamic False"),
+ &BindHandle
+ );
+
+ if (RpcStatus != RPC_S_OK) {
+ NetpKdPrint((
+ "WKSSVC_IMPERSONATE_HANDLE_bind failed: " FORMAT_NTSTATUS "\n",
+ RpcStatus
+ ));
+ }
+
+ return BindHandle;
+}
+
+
+
+handle_t
+WKSSVC_IDENTIFY_HANDLE_bind(
+ WKSSVC_IDENTIFY_HANDLE ServerName
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is called from the Workstation service client stubs when
+ it is necessary create an RPC binding to the server end with
+ identification level of impersonation.
+
+Arguments:
+
+ ServerName - A pointer to a string containing the name of the server
+ to bind with.
+
+Return Value:
+
+ The binding handle is returned to the stub routine. If the bind is
+ unsuccessful, a NULL will be returned.
+
+--*/
+{
+ handle_t BindHandle;
+ RPC_STATUS RpcStatus;
+
+ RpcStatus = NetpBindRpc (
+ ServerName,
+ WORKSTATION_INTERFACE_NAME,
+ TEXT("Security=Identification Dynamic False"),
+ &BindHandle
+ );
+
+ if (RpcStatus != RPC_S_OK) {
+ NetpKdPrint((
+ "WKSSVC_IDENTIFY_HANDLE_bind failed: " FORMAT_NTSTATUS "\n",
+ RpcStatus
+ ));
+ }
+
+ return BindHandle;
+}
+
+
+
+void
+WKSSVC_IMPERSONATE_HANDLE_unbind(
+ WKSSVC_IMPERSONATE_HANDLE ServerName,
+ handle_t BindHandle
+ )
+
+/*++
+
+Routine Description:
+
+ This routine calls a common unbind routine that is shared by all services.
+ This routine is called from the Workstation service client stubs when it is
+ necessary to unbind from the server end.
+
+Arguments:
+
+ ServerName - This is the name of the server from which to unbind.
+
+ BindingHandle - This is the binding handle that is to be closed.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ UNREFERENCED_PARAMETER(ServerName);
+
+ IF_DEBUG(RPCBIND) {
+ NetpKdPrint(("WKSSVC_IMPERSONATE_HANDLE_unbind: handle="
+ FORMAT_HEX_DWORD "\n", BindHandle));
+ }
+
+ NetpUnbindRpc(BindHandle);
+}
+
+
+
+void
+WKSSVC_IDENTIFY_HANDLE_unbind(
+ WKSSVC_IDENTIFY_HANDLE ServerName,
+ handle_t BindHandle
+ )
+
+/*++
+
+Routine Description:
+
+ This routine calls a common unbind routine that is shared by all services.
+ This routine is called from the server service client stubs when it is
+ necessary to unbind from a server.
+
+Arguments:
+
+ ServerName - This is the name of the server from which to unbind.
+
+ BindingHandle - This is the binding handle that is to be closed.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ UNREFERENCED_PARAMETER(ServerName);
+
+ IF_DEBUG(RPCBIND) {
+ NetpKdPrint(("WKSSVC_IDENTIFY_HANDLE_unbind: handle="
+ FORMAT_HEX_DWORD "\n", BindHandle));
+ }
+
+ NetpUnbindRpc(BindHandle);
+}
diff --git a/private/net/svcdlls/wkssvc/client/wksstub.c b/private/net/svcdlls/wkssvc/client/wksstub.c
new file mode 100644
index 000000000..fae7d7511
--- /dev/null
+++ b/private/net/svcdlls/wkssvc/client/wksstub.c
@@ -0,0 +1,1425 @@
+/*++
+
+Copyright (c) 1991-1993 Microsoft Corporation
+
+Module Name:
+
+ wksstub.c
+
+Abstract:
+
+ Client stubs of the Workstation service APIs.
+
+Author:
+
+ Rita Wong (ritaw) 10-May-1991
+
+Environment:
+
+ User Mode - Win32
+
+Revision History:
+
+ 18-Jun-1991 JohnRo
+ Remote NetUse APIs to downlevel servers.
+ 24-Jul-1991 JohnRo
+ Use NET_REMOTE_TRY_RPC etc macros for NetUse APIs.
+ Moved NetpIsServiceStarted() into NetLib.
+ 25-Jul-1991 JohnRo
+ Quiet DLL stub debug output.
+ 19-Aug-1991 JohnRo
+ Implement downlevel NetWksta APIs. Use NetRpc.h for NetWksta APIs.
+ 07-Nov-1991 JohnRo
+ RAID 4186: assert in RxNetShareAdd and other DLL stub problems.
+ 19-Nov-1991 JohnRo
+ Make sure status is correct for APIs not supported on downlevel.
+ Implement remote NetWkstaUserEnum().
+ 21-Jan-1991 rfirth
+ Added NetWkstaStatisticsGet wrapper
+ 19-Apr-1993 JohnRo
+ Fix NET_API_FUNCTION references.
+--*/
+
+#include "wsclient.h"
+#undef IF_DEBUG // avoid wsclient.h vs. debuglib.h conflicts.
+#include <debuglib.h> // IF_DEBUG() (needed by netrpc.h).
+#include <lmserver.h>
+#include <lmsvc.h>
+#include <rxuse.h> // RxNetUse APIs.
+#include <rxwksta.h> // RxNetWksta and RxNetWkstaUser APIs.
+#include <rap.h> // Needed by rxserver.h
+#include <rxserver.h> // RxNetServerEnum API.
+#include <netlib.h> // NetpServiceIsStarted() (needed by netrpc.h).
+#include <netrpc.h> // NET_REMOTE macros.
+#include <lmstats.h>
+#include <netstats.h> // NetWkstaStatisticsGet prototype
+#include <rxstats.h>
+#include <accessp.h> // UaspFlush()
+
+STATIC
+DWORD
+WsMapRpcError(
+ IN DWORD RpcError
+ );
+
+//-------------------------------------------------------------------//
+// //
+// Global variables //
+// //
+//-------------------------------------------------------------------//
+
+#if DBG
+
+DWORD WorkstationClientTrace = 0;
+
+#endif // DBG
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetWkstaGetInfo(
+ IN LPTSTR servername OPTIONAL,
+ IN DWORD level,
+ OUT LPBYTE *bufptr
+ )
+{
+ NET_API_STATUS status;
+
+
+ *bufptr = NULL; // Must be NULL so RPC knows to fill it in.
+
+ NET_REMOTE_TRY_RPC
+
+ //
+ // Try RPC (local or remote) version of API.
+ //
+ status = NetrWkstaGetInfo(
+ servername,
+ level,
+ (LPWKSTA_INFO) bufptr
+ );
+
+ NET_REMOTE_RPC_FAILED("NetWkstaGetInfo",
+ servername,
+ status,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_WORKSTATION )
+
+ //
+ // Call downlevel version of the API.
+ //
+ status = RxNetWkstaGetInfo(
+ servername,
+ level,
+ bufptr
+ );
+
+ NET_REMOTE_END
+
+ return status;
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetWkstaSetInfo(
+ IN LPTSTR servername OPTIONAL,
+ IN DWORD level,
+ IN LPBYTE buf,
+ OUT LPDWORD parm_err OPTIONAL
+ )
+
+/*++
+
+Routine Description:
+
+ This is the DLL entrypoint for NetWkstaSetInfo.
+
+Arguments:
+
+ servername - Supplies the name of server to execute this function
+
+ level - Supplies the level of information.
+
+ buf - Supplies a buffer which contains the information structure of fields
+ to set. The level denotes the structure in this buffer.
+
+ parm_err - Returns the identifier to the invalid parameter in buf if this
+ function returns ERROR_INVALID_PARAMETER.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NET_API_STATUS status;
+
+ NET_REMOTE_TRY_RPC
+
+ //
+ // Try RPC (local or remote) version of API.
+ //
+ status = NetrWkstaSetInfo(
+ servername,
+ level,
+ (LPWKSTA_INFO) &buf,
+ parm_err
+ );
+
+ NET_REMOTE_RPC_FAILED("NetWkstaSetInfo",
+ servername,
+ status,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_WORKSTATION )
+
+ //
+ // Call downlevel version of the API.
+ //
+ status = RxNetWkstaSetInfo(
+ servername,
+ level,
+ buf,
+ parm_err
+ );
+
+ NET_REMOTE_END
+
+ return status;
+}
+
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetWkstaUserEnum(
+ IN LPTSTR servername OPTIONAL,
+ IN DWORD level,
+ OUT LPBYTE *bufptr,
+ IN DWORD prefmaxlen,
+ OUT LPDWORD entriesread,
+ OUT LPDWORD totalentries,
+ IN OUT LPDWORD resume_handle OPTIONAL
+ )
+
+/*++
+
+Routine Description:
+
+ This is the DLL entrypoint for NetWkstaUserEnum.
+
+Arguments:
+
+ servername - Supplies the name of server to execute this function
+
+ level - Supplies the requested level of information.
+
+ bufptr - Returns a pointer to the buffer which contains a sequence of
+ information structure of the specified information level. This
+ pointer is set to NULL if return code is not NERR_Success or
+ ERROR_MORE_DATA, or if EntriesRead returned is 0.
+
+ prefmaxlen - Supplies the number of bytes of information to return in the
+ buffer. If this value is MAXULONG, all available information will
+ be returned.
+
+ entriesread - Returns the number of entries read into the buffer. This
+ value is only valid if the return code is NERR_Success or
+ ERROR_MORE_DATA.
+
+ totalentries - Returns the total number of entries available. This value
+ is only valid if the return code is NERR_Success or ERROR_MORE_DATA.
+
+ resume_handle - Supplies a handle to resume the enumeration from where it
+ left off the last time through. Returns the resume handle if return
+ code is ERROR_MORE_DATA.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NET_API_STATUS status;
+ GENERIC_INFO_CONTAINER GenericInfoContainer;
+ GENERIC_ENUM_STRUCT InfoStruct;
+
+
+ GenericInfoContainer.Buffer = NULL;
+ GenericInfoContainer.EntriesRead = 0;
+
+ InfoStruct.Container = &GenericInfoContainer;
+ InfoStruct.Level = level;
+
+ NET_REMOTE_TRY_RPC
+
+ //
+ // Try RPC (local or remote) version of API.
+ //
+ status = NetrWkstaUserEnum(
+ servername,
+ (LPWKSTA_USER_ENUM_STRUCT) &InfoStruct,
+ prefmaxlen,
+ totalentries,
+ resume_handle
+ );
+
+ if (status == NERR_Success || status == ERROR_MORE_DATA) {
+ *bufptr = (LPBYTE) GenericInfoContainer.Buffer;
+ *entriesread = GenericInfoContainer.EntriesRead;
+ }
+
+ NET_REMOTE_RPC_FAILED("NetWkstaUserEnum",
+ servername,
+ status,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_WORKSTATION )
+
+ //
+ // Call downlevel version.
+ //
+ status = RxNetWkstaUserEnum(
+ servername,
+ level,
+ bufptr,
+ prefmaxlen,
+ entriesread,
+ totalentries,
+ resume_handle);
+
+ NET_REMOTE_END
+
+ return status;
+}
+
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetWkstaUserGetInfo(
+ IN LPTSTR reserved,
+ IN DWORD level,
+ OUT LPBYTE *bufptr
+ )
+/*++
+
+Routine Description:
+
+ This is the DLL entrypoint for NetWkstaUserGetInfo.
+
+Arguments:
+
+ reserved - Must be NULL.
+
+ level - Supplies the requested level of information.
+
+ bufptr - Returns a pointer to a buffer which contains the requested
+ user information.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NET_API_STATUS status;
+
+
+ if (reserved != NULL) {
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ *bufptr = NULL; // Must be NULL so RPC knows to fill it in.
+
+ NET_REMOTE_TRY_RPC
+
+ //
+ // Try RPC (local only) version of API.
+ //
+ status = NetrWkstaUserGetInfo(
+ NULL,
+ level,
+ (LPWKSTA_USER_INFO) bufptr
+ );
+
+ NET_REMOTE_RPC_FAILED("NetWkstaUserGetInfo",
+ NULL,
+ status,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_WORKSTATION )
+
+ //
+ // No downlevel version to call
+ //
+ status = ERROR_NOT_SUPPORTED;
+
+ NET_REMOTE_END
+
+ return status;
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetWkstaUserSetInfo(
+ IN LPTSTR reserved,
+ IN DWORD level,
+ OUT LPBYTE buf,
+ OUT LPDWORD parm_err OPTIONAL
+ )
+/*++
+
+Routine Description:
+
+ This is the DLL entrypoint for NetWkstaUserSetInfo.
+
+Arguments:
+
+ reserved - Must be NULL.
+
+ level - Supplies the level of information.
+
+ buf - Supplies a buffer which contains the information structure of fields
+ to set. The level denotes the structure in this buffer.
+
+ parm_err - Returns the identifier to the invalid parameter in buf if this
+ function returns ERROR_INVALID_PARAMETER.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NET_API_STATUS status;
+
+
+ if (reserved != NULL) {
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ NET_REMOTE_TRY_RPC
+
+ //
+ // Try RPC (local only) version of API.
+ //
+ status = NetrWkstaUserSetInfo(
+ NULL,
+ level,
+ (LPWKSTA_USER_INFO) &buf,
+ parm_err
+ );
+
+ NET_REMOTE_RPC_FAILED("NetWkstaUserSetInfo",
+ NULL,
+ status,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_WORKSTATION )
+
+ //
+ // No downlevel version to call
+ //
+ status = ERROR_NOT_SUPPORTED;
+
+ NET_REMOTE_END
+
+ return status;
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetWkstaTransportEnum(
+ IN LPTSTR servername OPTIONAL,
+ IN DWORD level,
+ OUT LPBYTE *bufptr,
+ IN DWORD prefmaxlen,
+ OUT LPDWORD entriesread,
+ OUT LPDWORD totalentries,
+ IN OUT LPDWORD resume_handle OPTIONAL
+ )
+
+/*++
+
+Routine Description:
+
+ This is the DLL entrypoint for NetWkstaTransportEnum.
+
+Arguments:
+
+ servername - Supplies the name of server to execute this function
+
+ level - Supplies the requested level of information.
+
+ bufptr - Returns a pointer to the buffer which contains a sequence of
+ information structure of the specified information level. This
+ pointer is set to NULL if return code is not NERR_Success or
+ ERROR_MORE_DATA, or if EntriesRead returned is 0.
+
+ prefmaxlen - Supplies the number of bytes of information to return in the
+ buffer. If this value is MAXULONG, all available information will
+ be returned.
+
+ entriesread - Returns the number of entries read into the buffer. This
+ value is only valid if the return code is NERR_Success or
+ ERROR_MORE_DATA.
+
+ totalentries - Returns the total number of entries available. This value
+ is only valid if the return code is NERR_Success or ERROR_MORE_DATA.
+
+ resume_handle - Supplies a handle to resume the enumeration from where it
+ left off the last time through. Returns the resume handle if return
+ code is ERROR_MORE_DATA.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NET_API_STATUS status;
+ GENERIC_INFO_CONTAINER GenericInfoContainer;
+ GENERIC_ENUM_STRUCT InfoStruct;
+
+
+ GenericInfoContainer.Buffer = NULL;
+ GenericInfoContainer.EntriesRead = 0;
+
+ InfoStruct.Container = &GenericInfoContainer;
+ InfoStruct.Level = level;
+
+ NET_REMOTE_TRY_RPC
+
+ //
+ // Try RPC (local or remote) version of API.
+ //
+ status = NetrWkstaTransportEnum(
+ servername,
+ (LPWKSTA_TRANSPORT_ENUM_STRUCT) &InfoStruct,
+ prefmaxlen,
+ totalentries,
+ resume_handle
+ );
+
+ if (status == NERR_Success || status == ERROR_MORE_DATA) {
+ *bufptr = (LPBYTE) GenericInfoContainer.Buffer;
+ *entriesread = GenericInfoContainer.EntriesRead;
+ }
+
+ NET_REMOTE_RPC_FAILED("NetWkstaTransportEnum",
+ servername,
+ status,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_WORKSTATION )
+
+ //
+ // No downlevel version to call
+ //
+ status = ERROR_NOT_SUPPORTED;
+
+ NET_REMOTE_END
+
+ return status;
+}
+
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetWkstaTransportAdd(
+ IN LPTSTR servername OPTIONAL,
+ IN DWORD level,
+ IN LPBYTE buf,
+ OUT LPDWORD parm_err OPTIONAL
+ )
+
+/*++
+
+Routine Description:
+
+ This is the DLL entrypoint for NetWkstaTransportAdd.
+
+Arguments:
+
+ servername - Supplies the name of server to execute this function
+
+ level - Supplies the level of information.
+
+ buf - Supplies a buffer which contains the information of transport to add.
+
+ parm_err - Returns the identifier to the invalid parameter in buf if this
+ function returns ERROR_INVALID_PARAMETER.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NET_API_STATUS status;
+
+ NET_REMOTE_TRY_RPC
+
+ //
+ // Try RPC (local or remote) version of API.
+ //
+ status = NetrWkstaTransportAdd(
+ servername,
+ level,
+ (LPWKSTA_TRANSPORT_INFO_0) buf,
+ parm_err
+ );
+
+ NET_REMOTE_RPC_FAILED("NetWkstaTransportAdd",
+ servername,
+ status,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_WORKSTATION )
+
+
+ //
+ // No downlevel version to call
+ //
+ status = ERROR_NOT_SUPPORTED;
+
+ NET_REMOTE_END
+
+ return status;
+}
+
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetWkstaTransportDel(
+ IN LPTSTR servername OPTIONAL,
+ IN LPTSTR transportname,
+ IN DWORD ucond
+ )
+
+/*++
+
+Routine Description:
+
+ This is the DLL entrypoint for NetWkstaTransportDel.
+
+Arguments:
+
+ servername - Supplies the name of server to execute this function
+
+ transportname - Supplies the name of the transport to delete.
+
+ ucond - Supplies a value which specifies the force level of disconnection
+ for existing use on the transport.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NET_API_STATUS status;
+
+ NET_REMOTE_TRY_RPC
+
+ //
+ // Try RPC (local or remote) version of API.
+ //
+ status = NetrWkstaTransportDel(
+ servername,
+ transportname,
+ ucond
+ );
+
+ NET_REMOTE_RPC_FAILED("NetWkstaTransportDel",
+ servername,
+ status,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_WORKSTATION )
+
+ //
+ // No downlevel version to try
+ //
+ status = ERROR_NOT_SUPPORTED;
+
+ NET_REMOTE_END
+
+ return status;
+}
+
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetUseAdd(
+ IN LPTSTR servername OPTIONAL,
+ IN DWORD level,
+ IN LPBYTE buf,
+ OUT LPDWORD parm_err OPTIONAL
+ )
+/*++
+
+Routine Description:
+
+ This is the DLL entrypoint for NetUseAdd.
+
+Arguments:
+
+ servername - Supplies the name of server to execute this function
+
+ level - Supplies the requested level of information.
+
+ buf - Supplies a buffer which contains the information of use to add.
+
+ parm_err - Returns the identifier to the invalid parameter in buf if this
+ function returns ERROR_INVALID_PARAMETER.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NET_API_STATUS status;
+ UNICODE_STRING EncodedPassword;
+#define NETR_USE_ADD_PASSWORD_SEED 0x56 // Pick a non-zero seed.
+
+ DWORD OptionsSupported;
+
+
+ status = NetRemoteComputerSupports(
+ servername,
+ SUPPORTS_RPC | SUPPORTS_LOCAL, // options wanted
+ &OptionsSupported
+ );
+
+ if (status != NERR_Success) {
+ //
+ // This is where machine not found gets handled.
+ //
+ return status;
+ }
+
+ if (OptionsSupported & SUPPORTS_LOCAL) {
+
+ //
+ // Local case
+ //
+
+ RtlInitUnicodeString( &EncodedPassword, NULL );
+
+ RpcTryExcept {
+
+ //
+ // Obfuscate the password so it won't end up in the pagefile
+ //
+ if ( level >= 1 ) {
+
+ if ( ((PUSE_INFO_1)buf)->ui1_password != NULL ) {
+ UCHAR Seed = NETR_USE_ADD_PASSWORD_SEED;
+
+ RtlInitUnicodeString( &EncodedPassword,
+ ((PUSE_INFO_1)buf)->ui1_password );
+
+ RtlRunEncodeUnicodeString( &Seed, &EncodedPassword );
+ }
+ }
+
+ status = NetrUseAdd(
+ NULL,
+ level,
+ (LPUSE_INFO) &buf,
+ parm_err
+ );
+ }
+ RpcExcept(1) {
+ status = WsMapRpcError(RpcExceptionCode());
+ }
+ RpcEndExcept
+
+ //
+ // Put the password back the way we found it.
+ //
+ if ( EncodedPassword.Length != 0 ) {
+ RtlRunDecodeUnicodeString( NETR_USE_ADD_PASSWORD_SEED, &EncodedPassword );
+ }
+ }
+ else {
+
+ //
+ // Remote servername specified. Only allow remoting to downlevel.
+ //
+
+ if (OptionsSupported & SUPPORTS_RPC) {
+ status = ERROR_NOT_SUPPORTED;
+ }
+ else {
+
+ //
+ // Call downlevel version of the API.
+ //
+ status = RxNetUseAdd(
+ servername,
+ level,
+ buf,
+ parm_err
+ );
+
+ }
+ }
+
+ return status;
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetUseDel(
+ IN LPTSTR servername OPTIONAL,
+ IN LPTSTR usename,
+ IN DWORD ucond
+ )
+
+/*++
+
+Routine Description:
+
+ This is the DLL entrypoint for NetUseDel.
+
+Arguments:
+
+ servername - Supplies the name of server to execute this function
+
+ transportname - Supplies the name of the transport to delete.
+
+ ucond - Supplies a value which specifies the force level of disconnection
+ for the use.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NET_API_STATUS status;
+
+ DWORD OptionsSupported;
+
+
+ //
+ // Flush the NetUser/NetGroup API cache since it may have a pipe open.
+ //
+
+ UaspFlush();
+
+
+ status = NetRemoteComputerSupports(
+ servername,
+ SUPPORTS_RPC | SUPPORTS_LOCAL, // options wanted
+ &OptionsSupported
+ );
+
+ if (status != NERR_Success) {
+ //
+ // This is where machine not found gets handled.
+ //
+ return status;
+ }
+
+ if (OptionsSupported & SUPPORTS_LOCAL) {
+
+ //
+ // Local case
+ //
+
+ RpcTryExcept {
+
+ status = NetrUseDel(
+ NULL,
+ usename,
+ ucond
+ );
+
+ }
+ RpcExcept(1) {
+ status = WsMapRpcError(RpcExceptionCode());
+ }
+ RpcEndExcept
+
+ }
+ else {
+
+ //
+ // Remote servername specified. Only allow remoting to downlevel.
+ //
+
+ if (OptionsSupported & SUPPORTS_RPC) {
+ status = ERROR_NOT_SUPPORTED;
+ }
+ else {
+
+ //
+ // Call downlevel version of the API.
+ //
+ status = RxNetUseDel(
+ servername,
+ usename,
+ ucond
+ );
+ }
+ }
+
+ return status;
+}
+
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetUseGetInfo(
+ IN LPTSTR servername OPTIONAL,
+ IN LPTSTR usename,
+ IN DWORD level,
+ OUT LPBYTE *bufptr
+ )
+/*++
+
+Routine Description:
+
+ This is the DLL entrypoint for NetUseGetInfo.
+
+Arguments:
+
+ servername - Supplies the name of server to execute this function
+
+ level - Supplies the requested level of information.
+
+ bufptr - Returns a pointer to a buffer which contains the requested
+ use information.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NET_API_STATUS status;
+
+ DWORD OptionsSupported;
+
+
+ *bufptr = NULL; // Must be NULL so RPC knows to fill it in.
+
+
+ status = NetRemoteComputerSupports(
+ servername,
+ SUPPORTS_RPC | SUPPORTS_LOCAL, // options wanted
+ &OptionsSupported
+ );
+
+ if (status != NERR_Success) {
+ //
+ // This is where machine not found gets handled.
+ //
+ return status;
+ }
+
+ if (OptionsSupported & SUPPORTS_LOCAL) {
+
+ //
+ // Local case
+ //
+
+ RpcTryExcept {
+
+ status = NetrUseGetInfo(
+ NULL,
+ usename,
+ level,
+ (LPUSE_INFO) bufptr
+ );
+
+ }
+ RpcExcept(1) {
+ status = WsMapRpcError(RpcExceptionCode());
+ }
+ RpcEndExcept
+
+ }
+ else {
+
+ //
+ // Remote servername specified. Only allow remoting to downlevel.
+ //
+
+ if (OptionsSupported & SUPPORTS_RPC) {
+ status = ERROR_NOT_SUPPORTED;
+ }
+ else {
+
+ //
+ // Call downlevel version of the API.
+ //
+ status = RxNetUseGetInfo(
+ servername,
+ usename,
+ level,
+ bufptr
+ );
+
+ }
+ }
+
+ return status;
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetUseEnum(
+ IN LPTSTR servername OPTIONAL,
+ IN DWORD level,
+ OUT LPBYTE *bufptr,
+ IN DWORD prefmaxlen,
+ OUT LPDWORD entriesread,
+ OUT LPDWORD totalentries,
+ IN OUT LPDWORD resume_handle OPTIONAL
+ )
+
+/*++
+
+Routine Description:
+
+ This is the DLL entrypoint for NetUseEnum.
+
+Arguments:
+
+ servername - Supplies the name of server to execute this function
+
+ level - Supplies the requested level of information.
+
+ bufptr - Returns a pointer to the buffer which contains a sequence of
+ information structure of the specified information level. This
+ pointer is set to NULL if return code is not NERR_Success or
+ ERROR_MORE_DATA, or if EntriesRead returned is 0.
+
+ prefmaxlen - Supplies the number of bytes of information to return in the
+ buffer. If this value is MAXULONG, all available information will
+ be returned.
+
+ entriesread - Returns the number of entries read into the buffer. This
+ value is only valid if the return code is NERR_Success or
+ ERROR_MORE_DATA.
+
+ totalentries - Returns the total number of entries available. This value
+ is only valid if the return code is NERR_Success or ERROR_MORE_DATA.
+
+ resume_handle - Supplies a handle to resume the enumeration from where it
+ left off the last time through. Returns the resume handle if return
+ code is ERROR_MORE_DATA.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NET_API_STATUS status;
+ GENERIC_INFO_CONTAINER GenericInfoContainer;
+ GENERIC_ENUM_STRUCT InfoStruct;
+
+ DWORD OptionsSupported;
+
+
+ GenericInfoContainer.Buffer = NULL;
+ GenericInfoContainer.EntriesRead = 0;
+
+ InfoStruct.Container = &GenericInfoContainer;
+ InfoStruct.Level = level;
+
+
+ status = NetRemoteComputerSupports(
+ servername,
+ SUPPORTS_RPC | SUPPORTS_LOCAL, // options wanted
+ &OptionsSupported
+ );
+
+ if (status != NERR_Success) {
+ //
+ // This is where machine not found gets handled.
+ //
+ return status;
+ }
+
+ if (OptionsSupported & SUPPORTS_LOCAL) {
+
+ //
+ // Local case
+ //
+
+ RpcTryExcept {
+
+ status = NetrUseEnum(
+ NULL,
+ (LPUSE_ENUM_STRUCT) &InfoStruct,
+ prefmaxlen,
+ totalentries,
+ resume_handle
+ );
+
+ if (status == NERR_Success || status == ERROR_MORE_DATA) {
+ *bufptr = (LPBYTE) GenericInfoContainer.Buffer;
+ *entriesread = GenericInfoContainer.EntriesRead;
+ }
+
+ }
+ RpcExcept(1) {
+ status = WsMapRpcError(RpcExceptionCode());
+ }
+ RpcEndExcept
+
+ }
+ else {
+
+ //
+ // Remote servername specified. Only allow remoting to downlevel.
+ //
+
+ if (OptionsSupported & SUPPORTS_RPC) {
+ status = ERROR_NOT_SUPPORTED;
+ }
+ else {
+
+ //
+ // Call downlevel version of the API.
+ //
+ status = RxNetUseEnum(
+ servername,
+ level,
+ bufptr,
+ prefmaxlen,
+ entriesread,
+ totalentries,
+ resume_handle
+ );
+
+ }
+ }
+
+ return status;
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetMessageBufferSend (
+ IN LPCWSTR servername OPTIONAL,
+ IN LPCWSTR msgname,
+ IN LPCWSTR fromname,
+ IN LPBYTE buf,
+ IN DWORD buflen
+ )
+/*++
+
+Routine Description:
+
+ This is the DLL entrypoint for NetMessageBufferSend.
+
+Arguments:
+
+ servername - Supplies the name of server to execute this function
+
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+#define MAX_MESSAGE_SIZE 1792
+
+ NET_API_STATUS status;
+
+ //
+ // Truncate messages greater than (2K - 1/8th) = 1792 due to the 2K LPC
+ // port data size max. The messenger server receiving this message uses
+ // the MessageBox() api with the MB_SERVICE_NOTIFICATION flag to display
+ // this message. The MB_SERVICE_NOTIFICATION flag instructs MessageBox()
+ // to piggyback the hard error mechanism to get the UI on the console;
+ // otherwise the UI would never be seen. This is where the LPC port data
+ // size limitation comes into play.
+ //
+ // Why subtract an 1/8th from 2K? The messenger server prepends a string
+ // to the message (e.g., "Message from Joe to Linda on 3/7/96 12:04PM").
+ // In English, this string is 67 characters max (max user/computer name
+ // is 15 chars).
+ // 67 * 1.5 (other languages) * 2 (sizeof(WCHAR)) = 201 bytes.
+ // An 1/8th of 2K is 256.
+ //
+ if (buflen > MAX_MESSAGE_SIZE) {
+ buf[MAX_MESSAGE_SIZE - 2] = '\0';
+ buf[MAX_MESSAGE_SIZE - 1] = '\0';
+ buflen = MAX_MESSAGE_SIZE;
+ }
+
+ NET_REMOTE_TRY_RPC
+
+ //
+ // Try RPC (local or remote) version of API.
+ //
+ status = NetrMessageBufferSend(
+ (LPWSTR)servername,
+ (LPWSTR)msgname,
+ (LPWSTR)fromname,
+ buf,
+ buflen
+ );
+
+ NET_REMOTE_RPC_FAILED("NetMessageBufferSend",
+ (LPWSTR)servername,
+ status,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_WORKSTATION )
+
+ //
+ // Call downlevel version of the API.
+ //
+ status = ERROR_NOT_SUPPORTED;
+
+ NET_REMOTE_END
+
+ return status;
+}
+
+
+
+NET_API_STATUS NET_API_FUNCTION
+I_NetLogonDomainNameAdd(
+ IN LPTSTR logondomain
+ )
+
+/*++
+
+Routine Description:
+
+ This is the DLL entrypoint for the internal API I_NetLogonDomainNameAdd.
+
+Arguments:
+
+ logondomain - Supplies the name of the logon domain to add to the Browser.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NET_API_STATUS status;
+
+
+ NET_REMOTE_TRY_RPC
+
+ //
+ // Try RPC (local only) version of API.
+ //
+ status = I_NetrLogonDomainNameAdd(
+ logondomain
+ );
+
+ NET_REMOTE_RPC_FAILED(
+ "I_NetLogonDomainNameAdd",
+ NULL,
+ status,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_WORKSTATION
+ )
+
+ //
+ // No downlevel version to try
+ //
+ status = ERROR_NOT_SUPPORTED;
+
+ NET_REMOTE_END
+
+ return status;
+}
+
+
+
+NET_API_STATUS NET_API_FUNCTION
+I_NetLogonDomainNameDel(
+ IN LPTSTR logondomain
+ )
+
+/*++
+
+Routine Description:
+
+ This is the DLL entrypoint for the internal API I_NetLogonDomainNameDel.
+
+Arguments:
+
+ logondomain - Supplies the name of the logon domain to delete from the
+ Browser.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NET_API_STATUS status;
+
+
+ NET_REMOTE_TRY_RPC
+
+ //
+ // Try RPC (local only) version of API.
+ //
+ status = I_NetrLogonDomainNameDel(
+ logondomain
+ );
+
+ NET_REMOTE_RPC_FAILED(
+ "I_NetLogonDomainNameDel",
+ NULL,
+ status,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_WORKSTATION
+ )
+
+ //
+ // No downlevel version to try
+ //
+ status = ERROR_NOT_SUPPORTED;
+
+ NET_REMOTE_END
+
+ return status;
+
+}
+
+
+
+NET_API_STATUS
+NetWkstaStatisticsGet(
+ IN LPTSTR ServerName,
+ IN DWORD Level,
+ IN DWORD Options,
+ OUT LPBYTE* Buffer
+ )
+
+/*++
+
+Routine Description:
+
+ Wrapper for workstation statistics retrieval routine - either calls the
+ client-side RPC function or calls RxNetStatisticsGet to retrieve the
+ statistics from a down-level workstation service
+
+Arguments:
+
+ ServerName - where to remote this function
+ Level - of information required (MBZ)
+ Options - flags. Currently MBZ
+ Buffer - pointer to pointer to returned buffer
+
+Return Value:
+
+ NET_API_STATUS
+ Success - NERR_Success
+ Failure - ERROR_INVALID_LEVEL
+ Level not 0
+ ERROR_INVALID_PARAMETER
+ Unsupported options requested
+ ERROR_NOT_SUPPORTED
+ Service is not SERVER or WORKSTATION
+ ERROR_ACCESS_DENIED
+ Caller doesn't have necessary access rights for request
+
+--*/
+
+{
+ NET_API_STATUS status;
+
+ //
+ // set the caller's buffer pointer to known value. This will kill the
+ // calling app if it gave us a bad pointer and didn't use try...except
+ //
+
+ *Buffer = NULL;
+
+ //
+ // validate parms
+ //
+
+ if (Level) {
+ return ERROR_INVALID_LEVEL;
+ }
+
+ //
+ // we don't even allow clearing of stats any more
+ //
+
+ if (Options) {
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ //
+ // BUGBUG - remove redundant service name parameter
+ //
+
+ NET_REMOTE_TRY_RPC
+ status = NetrWorkstationStatisticsGet(ServerName,
+ SERVICE_WORKSTATION,
+ Level,
+ Options,
+ (LPSTAT_WORKSTATION_0*)Buffer
+ );
+
+ NET_REMOTE_RPC_FAILED("NetrWorkstationStatisticsGet",
+ ServerName,
+ status,
+ NET_REMOTE_FLAG_NORMAL,
+ SERVICE_WORKSTATION
+ )
+
+ status = RxNetStatisticsGet(ServerName,
+ SERVICE_LM20_WORKSTATION,
+ Level,
+ Options,
+ Buffer
+ );
+
+ NET_REMOTE_END
+
+ return status;
+}
+
+
+STATIC
+DWORD
+WsMapRpcError(
+ IN DWORD RpcError
+ )
+/*++
+
+Routine Description:
+
+ This routine maps the RPC error into a more meaningful net
+ error for the caller.
+
+Arguments:
+
+ RpcError - Supplies the exception error raised by RPC
+
+Return Value:
+
+ Returns the mapped error.
+
+--*/
+{
+
+ switch (RpcError) {
+
+ case RPC_S_SERVER_UNAVAILABLE:
+ return NERR_WkstaNotStarted;
+
+ case RPC_X_NULL_REF_POINTER:
+ return ERROR_INVALID_PARAMETER;
+
+ case EXCEPTION_ACCESS_VIOLATION:
+ return ERROR_INVALID_ADDRESS;
+
+ default:
+ return RpcError;
+ }
+
+}
diff --git a/private/net/svcdlls/wkssvc/client/wksstub.def b/private/net/svcdlls/wkssvc/client/wksstub.def
new file mode 100644
index 000000000..b83406b6b
--- /dev/null
+++ b/private/net/svcdlls/wkssvc/client/wksstub.def
@@ -0,0 +1,19 @@
+NAME WKSSTUB
+
+DESCRIPTION 'WKSSTUB'
+
+EXPORTS
+
+ NetWkstaGetInfo
+ NetWkstaSetInfo
+ NetWkstaUserEnum
+ NetWkstaUserGetInfo
+ NetWkstaTransportEnum
+ NetWkstaTransportAdd
+ NetWkstaTransportDel
+ NetUseAdd
+ NetUseDel
+ NetUseGetInfo
+ NetUseEnum
+
+DATA SINGLE SHARED
diff --git a/private/net/svcdlls/wkssvc/client/wsclient.h b/private/net/svcdlls/wkssvc/client/wsclient.h
new file mode 100644
index 000000000..68230af1b
--- /dev/null
+++ b/private/net/svcdlls/wkssvc/client/wsclient.h
@@ -0,0 +1,81 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ wsclient.h
+
+Abstract:
+
+ Private header file for the client end of the Workstation service
+ modules.
+
+Author:
+
+ Rita Wong (ritaw) 10-May-1991
+
+Revision History:
+
+--*/
+
+#include <nt.h> // DbgPrint prototype
+#include <ntrtl.h> // DbgPrint
+#include <nturtl.h> // Needed by winbase.h
+
+#include <windef.h> // DWORD
+#include <winbase.h> // LocalFree
+
+#include <rpc.h> // DataTypes and runtime APIs
+#include <rpcutil.h> // GENERIC_ENUM_STRUCT
+
+#include <lmcons.h> // NET_API_STATUS
+#include <lmerr.h> // NetError codes
+#include <lmremutl.h> // SUPPORTS_RPC
+
+#include <netlibnt.h> // NetpNtStatusToApiStatus
+#include <netdebug.h> // NetpDbgPrint
+
+#include <wkssvc.h> // generated by the MIDL complier
+#include <wsnames.h> // Service and interface names
+
+//
+// Debug trace level bits for turning on/off trace statements in the client
+// end of the Workstation service
+//
+
+//
+// Client stub trace output
+//
+#define WKSTA_DEBUG_CLIENTSTUBS 0x00000001
+
+//
+// Client RPC binding trace output
+//
+#define WKSTA_DEBUG_RPCBIND 0x00000002
+
+//
+// All debug flags on
+//
+#define WKSTA_DEBUG_ALL 0xFFFFFFFF
+
+
+#if DBG
+
+#define STATIC
+
+extern DWORD WorkstationClientTrace;
+
+#define DEBUG if (TRUE)
+
+#define IF_DEBUG(Function) if (WorkstationClientTrace & WKSTA_DEBUG_ ## Function)
+
+#else
+
+#define STATIC static
+
+#define DEBUG if (FALSE)
+
+#define IF_DEBUG(Function) if (FALSE)
+
+#endif // DBG
diff --git a/private/net/svcdlls/wkssvc/client/wstest.c b/private/net/svcdlls/wkssvc/client/wstest.c
new file mode 100644
index 000000000..b2fff0ca9
--- /dev/null
+++ b/private/net/svcdlls/wkssvc/client/wstest.c
@@ -0,0 +1,981 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ wstest.c
+
+Abstract:
+
+ Test program for the NetWksta and NetUse APIs. Run this test after
+ starting the Workstation service.
+
+Author:
+
+ Rita Wong (ritaw) 12-Mar-1991
+
+Revision History:
+
+--*/
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+
+#include <winerror.h>
+#include <windef.h> // Win32 type definitions
+#include <winbase.h> // Win32 base API prototypes
+
+#include <lm.h> // LAN Man definitions
+#include <netdebug.h> // NetpDbgDisplayWksta()
+#include <netlib.h>
+
+#include <tstring.h>
+
+#ifdef UNICODE
+#define FORMAT_STR "ws "
+#else
+#define FORMAT_STR "s "
+#endif
+
+
+#define CHANGE_HEURISTICS \
+ for (i = 0, ptr = &(redir->wki502_use_opportunistic_locking); i < 14; \
+ i++, ptr++) { \
+ if (*ptr) { \
+ *ptr = FALSE; \
+ } \
+ else { \
+ *ptr = TRUE; \
+ } \
+ }
+
+CHAR WorkBuffer[(DEVLEN + RMLEN + PWLEN + UNLEN + DNLEN + 5) * sizeof(TCHAR)
+ + sizeof(PUSE_INFO_2)];
+
+
+LPWSTR TargetMachine = NULL;
+
+VOID
+WsTestWkstaInfo(
+ VOID
+ );
+
+VOID
+WsTestWkstaTransportEnum(
+ VOID
+ );
+
+VOID
+WsTestUse(
+ VOID
+ );
+
+VOID
+TestUseAdd(
+ IN LPBYTE Buffer,
+ IN DWORD ExpectedStatus
+ );
+
+VOID
+TestUseDel(
+ IN LPTSTR UseName,
+ IN DWORD ForceLevel,
+ IN DWORD ExpectedStatus
+ );
+
+VOID
+TestUseEnum(
+ DWORD PreferedMaximumLength,
+ IN OUT LPDWORD ResumeHandle OPTIONAL
+ );
+
+VOID
+TestUseGetInfo(
+ LPTSTR UseName,
+ DWORD ExpectedStatus
+ );
+
+VOID
+PrintUseInfo1(
+ PUSE_INFO_1 UseInfo
+ );
+
+VOID
+PrintUseInfo2(
+ PUSE_INFO_2 UseInfo
+ );
+
+
+
+VOID _CRTAPI1
+main(
+ int argc,
+ char *argv[]
+ )
+{
+ if (argc > 1) {
+
+ if (argc == 3) {
+ TargetMachine = NetpAllocWStrFromStr(argv[2]);
+
+ if (TargetMachine != NULL) {
+
+ printf("New Target is %ws\n", TargetMachine);
+ }
+ }
+
+ WsTestWkstaInfo();
+
+ WsTestWkstaTransportEnum();
+
+ if (TargetMachine != NULL) {
+ NetApiBufferFree(TargetMachine);
+ }
+ }
+
+ WsTestUse();
+}
+
+
+VOID
+WsTestWkstaInfo(
+ VOID
+ )
+{
+ NET_API_STATUS status;
+
+ PWKSTA_INFO_502 redir;
+
+ PWKSTA_INFO_102 systeminfo102;
+ PWKSTA_INFO_100 systeminfo100;
+ PWKSTA_INFO_101 systeminfo101;
+
+ PWKSTA_USER_INFO_0 UserInfo0;
+ PWKSTA_USER_INFO_1 UserInfo;
+
+ DWORD ErrorParameter;
+ DWORD i = 0;
+ BOOL *ptr = NULL;
+
+ DWORD ResumeHandle = 0;
+ DWORD EntriesRead;
+ DWORD TotalEntries;
+
+
+
+ printf("In wstest.exe\n\n");
+
+ status = NetWkstaGetInfo(
+ TargetMachine,
+ 502,
+ (LPBYTE *) &redir
+ );
+
+ printf("NetWkstaGetInfo Test:status=%lu\n", status);
+
+ if (status != NERR_Success) {
+ goto TrySomethingElse;
+ }
+
+
+ NetpDbgDisplayWksta(502, (LPVOID) redir);
+
+
+ redir->wki502_char_wait++;
+ redir->wki502_maximum_collection_count++;
+ redir->wki502_collection_time++;
+
+ redir->wki502_keep_conn++;
+ redir->wki502_siz_char_buf++;
+
+ redir->wki502_max_cmds++; // Not settable: should be ignored
+
+ redir->wki502_sess_timeout++;
+ redir->wki502_lock_quota++;
+ redir->wki502_lock_increment++;
+ redir->wki502_lock_maximum++;
+ redir->wki502_pipe_increment++;
+ redir->wki502_pipe_maximum++;
+
+ redir->wki502_cache_file_timeout++;
+
+ CHANGE_HEURISTICS;
+
+ status = NetWkstaSetInfo(
+ TargetMachine,
+ 502,
+ (LPBYTE) redir,
+ &ErrorParameter
+ );
+
+ printf("NetWkstaSetInfo Test:status=%lu\n", status);
+
+ //
+ // Free the get info buffer. We are getting another buffer from the
+ // next get.
+ //
+ NetApiBufferFree(redir);
+
+ if (status != NERR_Success) {
+ if (status == ERROR_INVALID_PARAMETER) {
+ printf(
+ "NetWkstaSetInfo parameter %lu causes ERROR_INVALID_PARAMETER\n",
+ ErrorParameter
+ );
+ }
+ goto TrySomethingElse;
+ }
+
+ status = NetWkstaGetInfo(
+ TargetMachine,
+ 502,
+ (LPBYTE *) &redir
+ );
+
+ printf("NetWkstaGetInfo again: status=%lu\n", status);
+
+ if (status != NERR_Success) {
+ goto TrySomethingElse;
+ }
+
+ printf("\nAfter NetWkstaSetInfo, all values should be 1 extra"
+ " maxcmds\n");
+
+ NetpDbgDisplayWksta(502, (LPVOID) redir);
+
+
+ redir->wki502_char_wait--;
+ redir->wki502_collection_time--;
+ redir->wki502_maximum_collection_count--;
+
+ redir->wki502_keep_conn--;
+ redir->wki502_siz_char_buf--;
+
+ //redir->wki502_max_cmds--; // Not settable
+
+ redir->wki502_sess_timeout--;
+ redir->wki502_lock_quota--;
+ redir->wki502_lock_increment--;
+ redir->wki502_lock_maximum--;
+ redir->wki502_pipe_increment--;
+ redir->wki502_pipe_maximum--;
+
+ redir->wki502_cache_file_timeout--;
+
+ CHANGE_HEURISTICS;
+
+ status = NetWkstaSetInfo(
+ TargetMachine,
+ 502,
+ (LPBYTE) redir,
+ NULL
+ );
+
+ NetApiBufferFree(redir);
+
+ printf("NetWkstaGetInfo to reset to original values: status=%lu\n", status);
+
+
+TrySomethingElse:
+ //
+ // Get system info 102
+ //
+ status = NetWkstaGetInfo(
+ TargetMachine,
+ 102,
+ (LPBYTE *) &systeminfo102
+ );
+
+ if (status == NERR_Success) {
+ NetpDbgDisplayWksta(102, (LPVOID) systeminfo102);
+ NetApiBufferFree(systeminfo102);
+ }
+ else {
+ printf("NetWkstaGetInfo level 102: FAILED %lu\n", status);
+ }
+
+ //
+ // Get system info 100
+ //
+ status = NetWkstaGetInfo(
+ TargetMachine,
+ 100,
+ (LPBYTE *) &systeminfo100
+ );
+
+ if (status == NERR_Success) {
+ NetpDbgDisplayWksta(100, (LPVOID) systeminfo100);
+ NetApiBufferFree(systeminfo100);
+ }
+ else {
+ printf("NetWkstaGetInfo level 100: FAILED %lu\n", status);
+ }
+
+ //
+ // Get system info 101
+ //
+ status = NetWkstaGetInfo(
+ TargetMachine,
+ 101,
+ (LPBYTE *) &systeminfo101
+ );
+
+ if (status == NERR_Success) {
+ NetpDbgDisplayWksta(101, (LPVOID) systeminfo101);
+ NetApiBufferFree(systeminfo101);
+ }
+ else {
+ printf("NetWkstaGetInfo level 101: FAILED %lu\n", status);
+ }
+
+
+ //
+ // Get user info level 1
+ //
+ status = NetWkstaUserGetInfo(
+ NULL,
+ 1,
+ (LPBYTE *) &UserInfo
+ );
+
+ if (status == NERR_Success) {
+ printf("NetWkstaUserGetInfo level 1:\n"
+ "username=" FORMAT_LPTSTR "\nlogon domain=" FORMAT_LPTSTR "\nother domains=" FORMAT_LPTSTR "\nlogon server=" FORMAT_LPTSTR "\n",
+ UserInfo->wkui1_username,
+ UserInfo->wkui1_logon_domain,
+ UserInfo->wkui1_oth_domains,
+ UserInfo->wkui1_logon_server
+ );
+
+ NetApiBufferFree(UserInfo);
+ }
+ else {
+ printf("NetWkstaUserGetInfo level 1: FAILED %lu", status);
+ }
+
+ //
+ // Get user info level 0
+ //
+ status = NetWkstaUserGetInfo(
+ NULL,
+ 0,
+ (LPBYTE *) &UserInfo0
+ );
+
+ if (status == NERR_Success) {
+ printf("NetWkstaUserGetInfo level 0:\nusername=" FORMAT_LPTSTR "\n",
+ UserInfo0->wkui0_username
+ );
+
+ NetApiBufferFree(UserInfo0);
+ }
+ else {
+ printf("NetWkstaUserGetInfo level 0: FAILED %lu", status);
+ }
+
+ status = NetWkstaUserEnum (
+ TargetMachine,
+ 1,
+ (LPBYTE *) &UserInfo,
+ MAXULONG,
+ &EntriesRead,
+ &TotalEntries,
+ &ResumeHandle
+ );
+
+
+ if (status == NERR_Success) {
+
+ PWKSTA_USER_INFO_1 TmpPtr = UserInfo;
+
+
+ printf("NetWkstaUserEnum level 1: EntriesRead=%lu, TotalEntries=%lu\n",
+ EntriesRead, TotalEntries);
+
+ for (i = 0; i < EntriesRead; i++, UserInfo++) {
+
+ printf(" username=" FORMAT_LPTSTR "\nlogon domain=" FORMAT_LPTSTR "\nother domains=" FORMAT_LPTSTR "\nlogon server=" FORMAT_LPTSTR "\n",
+ UserInfo->wkui1_username,
+ UserInfo->wkui1_logon_domain,
+ UserInfo->wkui1_oth_domains,
+ UserInfo->wkui1_logon_server
+ );
+ }
+
+ NetApiBufferFree(TmpPtr);
+ }
+ else {
+ printf("NetWkstaUserEnum level 1: FAILED %lu", status);
+ }
+
+}
+
+
+VOID
+WsTestWkstaTransportEnum(
+ VOID
+ )
+{
+ NET_API_STATUS status;
+ LPBYTE Buffer;
+ DWORD EntriesRead,
+ TotalEntries,
+ ResumeHandle = 0;
+
+
+ status = NetWkstaTransportEnum(
+ NULL,
+ 0,
+ &Buffer,
+ MAXULONG,
+ &EntriesRead,
+ &TotalEntries,
+ &ResumeHandle
+ );
+
+ printf("NetWkstaTransportEnum Test:status=%lu\n", status);
+
+ if (status == NERR_Success) {
+
+ printf(" EntriesRead=%lu, TotalEntries=%lu\n", EntriesRead,
+ TotalEntries);
+
+ NetApiBufferFree(Buffer);
+ }
+}
+
+
+VOID
+WsTestUse(
+ VOID
+ )
+{
+ PUSE_INFO_2 UseInfo = (PUSE_INFO_2) WorkBuffer;
+ DWORD ResumeHandle = 0;
+
+ DWORD i;
+
+ LPTSTR PasswordSavePtr;
+ LPTSTR UserNameSavePtr;
+ LPTSTR DomainNameSavePtr;
+
+
+ //
+ // Initialize string pointers. Local device points to the bottom
+ // of Info 2 structure; Shared resource points to the middle of
+ // buffer (away from everything so there's no chance of overwriting
+ // or being overwritten.
+ //
+ UseInfo->ui2_local = (LPTSTR) &WorkBuffer[sizeof(USE_INFO_2)];
+
+ UseInfo->ui2_remote = (LPTSTR) ((DWORD) UseInfo->ui2_local) +
+ (DEVLEN + 1) * sizeof(TCHAR);
+
+ UseInfo->ui2_password = NULL;
+ PasswordSavePtr = (LPTSTR) ((DWORD) UseInfo->ui2_remote) +
+ (RMLEN + 1) * sizeof(TCHAR);
+
+ UseInfo->ui2_username = NULL;
+ UserNameSavePtr = (LPTSTR) ((DWORD) PasswordSavePtr) +
+ (PWLEN + 1) * sizeof(TCHAR);
+
+ UseInfo->ui2_domainname = NULL;
+ DomainNameSavePtr = (LPTSTR) ((DWORD) UserNameSavePtr) +
+ (DNLEN + 1) * sizeof(TCHAR);
+
+ UseInfo->ui2_asg_type = USE_DISKDEV;
+
+
+ //
+ // Test with explicit username and password
+ //
+ UseInfo->ui2_username = UserNameSavePtr;
+ UseInfo->ui2_password = PasswordSavePtr;
+ UseInfo->ui2_domainname = DomainNameSavePtr;
+
+ STRCPY(UseInfo->ui2_username, TEXT("NTBUILD"));
+ STRCPY(UseInfo->ui2_password, TEXT("NTBUILD"));
+ STRCPY(UseInfo->ui2_domainname, TEXT("NtWins"));
+ STRCPY(UseInfo->ui2_local, TEXT("k:"));
+ STRCPY(UseInfo->ui2_remote, TEXT("\\\\kernel\\razzle2"));
+ TestUseAdd(WorkBuffer, NERR_Success);
+ TestUseGetInfo(TEXT("K:"), NERR_Success);
+
+
+ UseInfo->ui2_password = NULL;
+ UseInfo->ui2_domainname = NULL;
+
+ //
+ // Connect to \\kernel\razzle2 again with only the username
+ //
+ STRCPY(UseInfo->ui2_local, TEXT("j:"));
+ TestUseAdd(WorkBuffer, NERR_Success);
+
+ //
+ // Add 5 \\ritaw2\public
+ //
+ UseInfo->ui2_username = NULL;
+
+ STRCPY(UseInfo->ui2_local, TEXT(""));
+ STRCPY(UseInfo->ui2_remote, TEXT("\\\\ritaw2\\public"));
+ for (i = 0; i < 5; i++) {
+ TestUseAdd(WorkBuffer, NERR_Success);
+ }
+
+ TestUseDel(
+ TEXT("j:"),
+ USE_LOTS_OF_FORCE,
+ NERR_Success
+ );
+
+ TestUseDel(
+ TEXT("k:"),
+ USE_LOTS_OF_FORCE,
+ NERR_Success
+ );
+
+ //
+ // Add p: \\ritaw2\public
+ //
+ STRCPY(UseInfo->ui2_local, TEXT("p:"));
+ TestUseAdd(WorkBuffer, NERR_Success);
+
+ //
+ // Add U: \\ritaw2\public
+ //
+ STRCPY(UseInfo->ui2_local, TEXT("U:"));
+ TestUseAdd(WorkBuffer, NERR_Success);
+
+ //
+ // Add s: \\ritaw2\testdir
+ //
+ STRCPY(UseInfo->ui2_local, TEXT("s:"));
+ STRCPY(UseInfo->ui2_remote, TEXT("\\\\ritaw2\\testdir"));
+ TestUseAdd(WorkBuffer, NERR_Success);
+ TestUseAdd(WorkBuffer, ERROR_ALREADY_ASSIGNED);
+ TestUseGetInfo(TEXT("\\\\ritaw2\\testdir"), NERR_UseNotFound);
+
+ //
+ // Add 3 \\ritaw2\testdir
+ //
+ STRCPY(UseInfo->ui2_local, TEXT(""));
+ for (i = 0; i < 3; i++) {
+ TestUseAdd(WorkBuffer, NERR_Success);
+ }
+
+ //
+ // Create implicit connections
+ //
+ system("ls \\\\ritaw2\\pub2");
+ system("ls \\\\ritaw2\\tmp");
+
+ //
+ // Delete implicit connection \\ritaw2\tmp USE_NOFORCE.
+ //
+ TestUseDel(
+ TEXT("\\\\ritaw2\\tmp"),
+ USE_NOFORCE,
+ NERR_Success
+ );
+
+ //
+ // Enumerate all connections
+ //
+ TestUseEnum(MAXULONG, NULL);
+
+ TestUseEnum(150, &ResumeHandle);
+
+ TestUseEnum(100, &ResumeHandle);
+
+ for (i = 0; i < 3; i++) {
+ TestUseEnum(50, &ResumeHandle);
+ }
+
+ TestUseEnum(150, NULL);
+
+
+ //
+ // Get info
+ //
+ TestUseGetInfo(TEXT("\\\\ritaw2\\public"), NERR_Success);
+
+ TestUseGetInfo(TEXT("p:"), NERR_Success);
+
+ TestUseGetInfo(TEXT("\\\\ritaw2\\Z"), NERR_UseNotFound);
+
+ TestUseGetInfo(TEXT("\\\\ritaw2\\Testdir"), NERR_Success);
+
+ TestUseGetInfo(TEXT("S:"), NERR_Success);
+
+
+ //
+ // Delete \\ritaw2\public USE_NOFORCE. Decrements usecount from 5 to 4.
+ //
+ TestUseDel(
+ TEXT("\\\\ritaw2\\public"),
+ USE_NOFORCE,
+ NERR_Success
+ );
+
+ //
+ // Delete \\ritaw2\public USE_FORCE. This should delete all 4 usecounts.
+ //
+ TestUseDel(
+ TEXT("\\\\ritaw2\\public"),
+ USE_FORCE,
+ NERR_Success
+ );
+
+ TestUseDel(
+ TEXT("\\\\ritaw2\\public"),
+ USE_FORCE,
+ NERR_UseNotFound
+ );
+
+ //
+ // Delete s: USE_FORCE
+ //
+ TestUseDel(
+ TEXT("s:"),
+ USE_LOTS_OF_FORCE,
+ NERR_Success
+ );
+
+ //
+ // Add s: \\ritaw2\z
+ //
+ STRCPY(UseInfo->ui2_local, TEXT("s:"));
+ STRCPY(UseInfo->ui2_remote, TEXT("\\\\ritaw2\\z"));
+ TestUseAdd(WorkBuffer, NERR_Success);
+
+ //
+ // Delete p: USE_NOFORCE. Second time should get NERR_UseNotFound.
+ //
+ TestUseDel(
+ TEXT("p:"),
+ USE_LOTS_OF_FORCE,
+ NERR_Success
+ );
+
+ TestUseDel(
+ TEXT("p:"),
+ USE_LOTS_OF_FORCE,
+ NERR_UseNotFound
+ );
+
+ //
+ // Delete \\ritaw2\testdir USE_NOFORCE. 4th time should be
+ // NERR_UseNotFound.
+ //
+ for (i = 0; i < 3; i++) {
+ TestUseDel(
+ TEXT("\\\\ritaw2\\testdir"),
+ USE_NOFORCE,
+ NERR_Success
+ );
+ }
+
+ TestUseDel(
+ TEXT("\\\\ritaw2\\testdir"),
+ USE_NOFORCE,
+ NERR_UseNotFound
+ );
+
+
+ //
+ // Add prn: \\ritaw2\prn
+ //
+
+ UseInfo->ui2_asg_type = USE_SPOOLDEV;
+
+ STRCPY(UseInfo->ui2_local, TEXT("prn"));
+ STRCPY(UseInfo->ui2_remote, TEXT("\\\\prt21088\\laserii"));
+ TestUseAdd(WorkBuffer, NERR_Success);
+
+ //
+ // Add lpt1: \\ritaw2\laser, should get ERROR_ALREADY_ASSIGNED because prn:
+ // is converted to lpt1.
+ //
+ STRCPY(UseInfo->ui2_local, TEXT("lpt1:"));
+ STRCPY(UseInfo->ui2_remote, TEXT("\\\\prt21088\\laserii"));
+ TestUseAdd(WorkBuffer, ERROR_ALREADY_ASSIGNED);
+
+ //
+ // Delete LPT1 USE_LOTS_OF_FORCE, should succeed
+ //
+ TestUseDel(
+ TEXT("prn:"),
+ USE_LOTS_OF_FORCE,
+ NERR_Success
+ );
+
+ //
+ // Bad device type
+ //
+ STRCPY(UseInfo->ui2_local, TEXT(""));
+ STRCPY(UseInfo->ui2_remote, TEXT("\\\\ritaw2\\public"));
+ UseInfo->ui2_asg_type = 12345678;
+ TestUseAdd(WorkBuffer, NERR_BadAsgType);
+
+ TestUseDel(
+ TEXT("S:"),
+ USE_LOTS_OF_FORCE,
+ NERR_Success
+ );
+
+
+ TestUseDel(
+ TEXT("U:"),
+ USE_LOTS_OF_FORCE,
+ NERR_Success
+ );
+}
+
+VOID
+TestUseAdd(
+ IN LPBYTE Buffer,
+ IN DWORD ExpectedStatus
+ )
+{
+ NET_API_STATUS status;
+ DWORD ErrorParameter;
+
+ status = NetUseAdd(
+ NULL,
+ 2,
+ Buffer,
+ &ErrorParameter
+ );
+
+
+ printf("NetUseAdd %-5" FORMAT_STR "%-25" FORMAT_STR, ((PUSE_INFO_2) Buffer)->ui2_local,
+ ((PUSE_INFO_2) Buffer)->ui2_remote);
+
+ if (status != ExpectedStatus) {
+ printf("FAILED: Got %lu, expected %lu\n", status, ExpectedStatus);
+ }
+ else {
+ printf("OK: Got expected status %lu\n", status);
+ }
+
+ if (status == ERROR_INVALID_PARAMETER) {
+ printf("NetUseAdd parameter %lu is cause of ERROR_INVALID_PARAMETER\n",
+ ErrorParameter);
+ }
+}
+
+
+VOID
+TestUseDel(
+ IN LPTSTR UseName,
+ IN DWORD ForceLevel,
+ IN DWORD ExpectedStatus
+ )
+{
+ NET_API_STATUS status;
+
+ PWCHAR Force[3] = {
+ L"NOFORCE",
+ L"FORCE",
+ L"LOTS_OF_FORCE"
+ };
+
+
+ printf("NetUseDel %-17" FORMAT_STR "%-13" FORMAT_STR, UseName, Force[ForceLevel]);
+
+ status = NetUseDel(
+ NULL,
+ UseName,
+ ForceLevel
+ );
+
+ if (status != ExpectedStatus) {
+ printf("FAILED: Got %lu, expected %lu\n", status, ExpectedStatus);
+ }
+ else {
+ printf("OK: Got expected status %lu\n", status);
+ }
+}
+
+
+VOID
+TestUseGetInfo(
+ LPTSTR UseName,
+ DWORD ExpectedStatus
+ )
+{
+
+ NET_API_STATUS status;
+ PUSE_INFO_2 UseInfo;
+
+ printf("NetUseGetInfo %-27" FORMAT_STR, UseName);
+
+ status = NetUseGetInfo(
+ NULL,
+ UseName,
+ 2,
+ (LPBYTE *) &UseInfo
+ );
+
+ if (status != ExpectedStatus) {
+ printf("FAILED: Got %lu, expected %lu\n", status, ExpectedStatus);
+ }
+ else {
+ printf("OK: Got expected status %lu\n", status);
+ }
+
+ if (status == NERR_Success) {
+ PrintUseInfo2(UseInfo);
+ NetApiBufferFree(UseInfo);
+ }
+}
+
+
+VOID
+TestUseEnum(
+ IN DWORD PreferedMaximumLength,
+ IN OUT LPDWORD ResumeHandle OPTIONAL
+ )
+{
+ DWORD i;
+ NET_API_STATUS status;
+ DWORD EntriesRead,
+ TotalEntries;
+
+ PUSE_INFO_1 UseInfo, saveptr;
+
+ if (ARGUMENT_PRESENT(ResumeHandle)) {
+ printf("\nInput ResumeHandle=x%08lx\n", *ResumeHandle);
+ }
+ status = NetUseEnum(
+ NULL,
+ 1,
+ (LPBYTE *) &UseInfo,
+ PreferedMaximumLength,
+ &EntriesRead,
+ &TotalEntries,
+ ResumeHandle
+ );
+
+ saveptr = UseInfo;
+
+ if (status != NERR_Success && status != ERROR_MORE_DATA) {
+ printf("NetUseEnum FAILED %lu\n", status);
+ }
+ else {
+ printf("Return code from NetUseEnum %lu\n", status);
+
+ printf("EntriesRead=%lu, TotalEntries=%lu\n",
+ EntriesRead, TotalEntries);
+
+ if (ARGUMENT_PRESENT(ResumeHandle)) {
+ printf("Output ResumeHandle=x%08lx\n", *ResumeHandle);
+ }
+
+ for (i = 0; i < EntriesRead; i++, UseInfo++) {
+ PrintUseInfo1(UseInfo);
+ }
+
+ //
+ // Free buffer allocated for us.
+ //
+ NetApiBufferFree(saveptr);
+ }
+
+}
+
+VOID
+PrintUseInfo1(
+ PUSE_INFO_1 UseInfo
+ )
+{
+
+ switch(UseInfo->ui1_status) {
+
+ case USE_OK:
+ printf("OK ");
+ break;
+
+ case USE_PAUSED:
+ printf("Paused ");
+ break;
+
+ case USE_SESSLOST:
+ printf("Disconnected ");
+ break;
+
+ case USE_NETERR:
+ printf("Net error ");
+ break;
+
+ case USE_CONN:
+ printf("Connecting ");
+ break;
+
+ case USE_RECONN:
+ printf("Reconnecting ");
+ break;
+
+ default:
+ printf("Unknown ");
+ }
+
+ printf(" %-7" FORMAT_STR "%-25" FORMAT_STR, UseInfo->ui1_local,
+ UseInfo->ui1_remote);
+ printf("usecount=%lu, refcount=%lu\n",
+ UseInfo->ui1_usecount, UseInfo->ui1_refcount);
+
+}
+
+VOID
+PrintUseInfo2(
+ PUSE_INFO_2 UseInfo
+ )
+{
+
+ switch(UseInfo->ui2_status) {
+
+ case USE_OK:
+ printf("OK ");
+ break;
+
+ case USE_PAUSED:
+ printf("Paused ");
+ break;
+
+ case USE_SESSLOST:
+ printf("Disconnected ");
+ break;
+
+ case USE_NETERR:
+ printf("Net error ");
+ break;
+
+ case USE_CONN:
+ printf("Connecting ");
+ break;
+
+ case USE_RECONN:
+ printf("Reconnecting ");
+ break;
+
+ default:
+ printf("Unknown ");
+ }
+
+ printf(" %-7" FORMAT_STR "%-" FORMAT_STR, UseInfo->ui2_local,
+ UseInfo->ui2_remote);
+
+ printf("\n %-25" FORMAT_STR "%-" FORMAT_STR, UseInfo->ui2_username,
+ UseInfo->ui2_domainname);
+
+ printf("\n usecount=%02lu, refcount=%02lu\n",
+ UseInfo->ui2_usecount, UseInfo->ui2_refcount);
+
+}
diff --git a/private/net/svcdlls/wkssvc/client/wstinv.c b/private/net/svcdlls/wkssvc/client/wstinv.c
new file mode 100644
index 000000000..4b2d9b0c7
--- /dev/null
+++ b/private/net/svcdlls/wkssvc/client/wstinv.c
@@ -0,0 +1,449 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ wstinv.c
+
+Abstract:
+
+ This module tests invalid parameters to NetUse APIs.
+
+Author:
+
+ Rita Wong (ritaw) 12-Mar-1991
+
+Revision History:
+
+--*/
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+
+#include <winerror.h>
+#include <windef.h> // Win32 type definitions
+#include <winbase.h> // Win32 base API prototypes
+
+#include <lm.h> // LAN Man definitions
+#include <netdebug.h> // NetpDbgDisplayWksta()
+
+#include <tstring.h>
+
+
+VOID
+WsTestUse(
+ VOID
+ );
+
+VOID
+TestUseAdd(
+ IN LPBYTE Buffer,
+ IN DWORD ExpectedStatus
+ );
+
+VOID
+TestUseDel(
+ IN LPTSTR UseName,
+ IN DWORD ForceLevel,
+ IN DWORD ExpectedStatus
+ );
+
+VOID
+TestUseEnum(
+ DWORD PreferedMaximumLength,
+ IN OUT LPDWORD ResumeHandle OPTIONAL
+ );
+
+VOID
+TestUseGetInfo(
+ LPTSTR UseName,
+ DWORD ExpectedStatus
+ );
+
+VOID
+PrintUseInfo(
+ PUSE_INFO_1 UseInfo
+ );
+
+
+VOID _CRTAPI1
+main(
+ int argc,
+ char *argv[]
+ )
+{
+ WsTestUse();
+}
+
+
+VOID
+WsTestUse(
+ VOID
+ )
+{
+ CHAR Buffer[1024];
+ PUSE_INFO_2 UseInfo = (PUSE_INFO_2) Buffer;
+ DWORD ResumeHandle = 0;
+
+ DWORD i;
+
+
+ //
+ // Initialize string pointers. Local device points to the bottom
+ // of Info 2 structure; Shared resource points to the middle of
+ // buffer (away from everything so there's no chance of overwriting
+ // or being overwritten.
+ //
+ UseInfo->ui2_local = (LPTSTR) ((DWORD) UseInfo + sizeof(USE_INFO_2));
+
+ UseInfo->ui2_remote = (LPTSTR) &Buffer[601];
+
+ UseInfo->ui2_password = NULL;
+ UseInfo->ui2_username = NULL;
+ UseInfo->ui2_domainname = NULL;
+
+ UseInfo->ui2_asg_type = USE_DISKDEV;
+
+
+ //
+ // Add \\ritaw2\public
+ //
+ UseInfo->ui2_local = NULL;
+ STRCPY(UseInfo->ui2_remote, L"\\\\ritaw2\\public");
+ TestUseAdd(Buffer, NERR_Success);
+
+
+ UseInfo->ui2_local = (LPTSTR) ((DWORD) UseInfo + sizeof(USE_INFO_2));
+
+ //
+ // Add &: \\ritaw2\public
+ //
+ STRCPY(UseInfo->ui2_local, L"&:");
+ TestUseAdd(Buffer, ERROR_INVALID_PARAMETER);
+
+ //
+ // Add 5: \\ritaw2\public
+ //
+ STRCPY(UseInfo->ui2_local, L"5:");
+ TestUseAdd(Buffer, ERROR_INVALID_PARAMETER);
+
+ //
+ // Add x: \\ritaw2\public\tmp
+ //
+ STRCPY(UseInfo->ui2_local, L"x:");
+ STRCPY(UseInfo->ui2_remote, L"\\\\ritaw2\\public\\tmp");
+ TestUseAdd(Buffer, ERROR_INVALID_PARAMETER);
+
+ //
+ // Add x: \\\
+ //
+ STRCPY(UseInfo->ui2_local, L"x:");
+ STRCPY(UseInfo->ui2_remote, L"\\\\\\");
+ TestUseAdd(Buffer, ERROR_INVALID_PARAMETER);
+
+ //
+ // Add *: \\ritaw2\testdir
+ //
+ STRCPY(UseInfo->ui2_local, L"*:");
+ STRCPY(UseInfo->ui2_remote, L"\\\\ritaw2\\testdir");
+ TestUseAdd(Buffer, ERROR_INVALID_PARAMETER);
+
+ //
+ // Get info
+ //
+ TestUseGetInfo(L"$:", NERR_UseNotFound);
+
+ TestUseGetInfo(L"", NERR_UseNotFound);
+
+ TestUseGetInfo(NULL, ERROR_INVALID_ADDRESS);
+
+ TestUseGetInfo(L"\\\\ritaw2\\public\\tmp", NERR_UseNotFound);
+
+ TestUseGetInfo(L"\\\\\\", NERR_UseNotFound);
+
+
+ //
+ // Delete %: USE_NOFORCE.
+ //
+ TestUseDel(
+ L"%:",
+ USE_NOFORCE,
+ NERR_UseNotFound
+ );
+
+
+ //
+ // Delete \\ritaw2\public with invalid force level.
+ //
+ TestUseDel(
+ L"\\\\ritaw2\\public",
+ 999,
+ ERROR_INVALID_PARAMETER
+ );
+
+ //
+ // Delete \\ritaw2\public USE_FORCE.
+ //
+ TestUseDel(
+ L"\\\\ritaw2\\public",
+ USE_FORCE,
+ NERR_Success
+ );
+
+
+ //
+ // Add prn: \\sparkle\laserjet
+ //
+ UseInfo->ui2_asg_type = USE_SPOOLDEV;
+
+ STRCPY(UseInfo->ui2_local, L"prn");
+ STRCPY(UseInfo->ui2_remote, L"\\\\sparkle\\laserjet");
+ TestUseAdd(Buffer, NERR_Success);
+
+ //
+ // Add aux: \\sparkle\laserjet
+ //
+ UseInfo->ui2_asg_type = USE_CHARDEV;
+
+ STRCPY(UseInfo->ui2_local, L"aux");
+ TestUseAdd(Buffer, ERROR_BAD_DEV_TYPE);
+
+ //
+ // Add lpt1: \\ritaw2\laser, should get ERROR_ALREADY_ASSIGNED because prn:
+ // is converted to lpt1.
+ //
+ UseInfo->ui2_asg_type = USE_SPOOLDEV;
+
+ STRCPY(UseInfo->ui2_local, L"lpt1:");
+ STRCPY(UseInfo->ui2_remote, L"\\\\ritaw2\\printer");
+ TestUseAdd(Buffer, ERROR_ALREADY_ASSIGNED);
+
+ //
+ // Delete LPT1 USE_LOTS_OF_FORCE, should succeed
+ //
+ TestUseDel(
+ L"prn:",
+ USE_LOTS_OF_FORCE,
+ NERR_Success
+ );
+
+ //
+ // Bad device type
+ //
+ STRCPY(UseInfo->ui2_local, L"");
+ STRCPY(UseInfo->ui2_remote, L"\\\\ritaw2\\public");
+ UseInfo->ui2_asg_type = 12345678;
+ TestUseAdd(Buffer, NERR_BadAsgType);
+
+}
+
+VOID
+TestUseAdd(
+ IN LPBYTE Buffer,
+ IN DWORD ExpectedStatus
+ )
+{
+ NET_API_STATUS status;
+ DWORD ErrorParameter;
+
+ status = NetUseAdd(
+ NULL,
+ 2,
+ Buffer,
+ &ErrorParameter
+ );
+
+ printf("NetUseAdd %-5ws %-25ws ", ((PUSE_INFO_2) Buffer)->ui2_local,
+ ((PUSE_INFO_2) Buffer)->ui2_remote);
+
+ if (status != ExpectedStatus) {
+ printf("FAILED: Got %lu, expected %lu\n", status, ExpectedStatus);
+ }
+ else {
+ printf("OK: Got expected status %lu\n", status);
+ }
+
+ if (status == ERROR_INVALID_PARAMETER) {
+ printf("NetUseAdd parameter %lu is cause of ERROR_INVALID_PARAMETER\n",
+ ErrorParameter);
+ }
+}
+
+
+VOID
+TestUseDel(
+ IN LPTSTR UseName,
+ IN DWORD ForceLevel,
+ IN DWORD ExpectedStatus
+ )
+{
+ NET_API_STATUS status;
+ DWORD Level;
+
+ PWCHAR Force[4] = {
+ L"NOFORCE",
+ L"FORCE",
+ L"LOTS_OF_FORCE",
+ L"INVALID FORCE"
+ };
+
+
+ if (ForceLevel > 2) {
+ Level = 3;
+ }
+ else {
+ Level = ForceLevel;
+ }
+
+ printf("NetUseDel %-17ws %-13ws ", UseName, Force[Level]);
+
+ status = NetUseDel(
+ NULL,
+ UseName,
+ ForceLevel
+ );
+
+ if (status != ExpectedStatus) {
+ printf("FAILED: Got %lu, expected %lu\n", status, ExpectedStatus);
+ }
+ else {
+ printf("OK: Got expected status %lu\n", status);
+ }
+}
+
+
+VOID
+TestUseGetInfo(
+ LPTSTR UseName,
+ DWORD ExpectedStatus
+ )
+{
+
+ NET_API_STATUS status;
+ PUSE_INFO_1 UseInfo;
+
+ printf("NetUseGetInfo %-27ws ", UseName);
+
+ status = NetUseGetInfo(
+ NULL,
+ UseName,
+ 2,
+ (LPBYTE *) &UseInfo
+ );
+
+ if (status != ExpectedStatus) {
+ printf("FAILED: Got %lu, expected %lu\n", status, ExpectedStatus);
+ }
+ else {
+ printf("OK: Got expected status %lu\n", status);
+ }
+
+ if (status == NERR_Success) {
+ PrintUseInfo(UseInfo);
+ NetApiBufferFree(UseInfo);
+ }
+}
+
+
+VOID
+TestUseEnum(
+ IN DWORD PreferedMaximumLength,
+ IN OUT LPDWORD ResumeHandle OPTIONAL
+ )
+{
+ DWORD i;
+ NET_API_STATUS status;
+ DWORD EntriesRead,
+ TotalEntries;
+
+ PUSE_INFO_1 UseInfo, saveptr;
+
+ if (ARGUMENT_PRESENT(ResumeHandle)) {
+ printf("\nInput ResumeHandle=x%08lx\n", *ResumeHandle);
+ }
+ status = NetUseEnum(
+ NULL,
+ 1,
+ (LPBYTE *) &UseInfo,
+ PreferedMaximumLength,
+ &EntriesRead,
+ &TotalEntries,
+ ResumeHandle
+ );
+
+ saveptr = UseInfo;
+
+ if (status != NERR_Success && status != ERROR_MORE_DATA) {
+ printf("NetUseEnum FAILED %lu\n", status);
+ }
+ else {
+ printf("Return code from NetUseEnum %lu\n", status);
+
+ printf("EntriesRead=%lu, TotalEntries=%lu\n",
+ EntriesRead, TotalEntries);
+
+ if (ARGUMENT_PRESENT(ResumeHandle)) {
+ printf("Output ResumeHandle=x%08lx\n", *ResumeHandle);
+ }
+
+ for (i = 0; i < EntriesRead; i++, UseInfo++) {
+ PrintUseInfo(UseInfo);
+ }
+
+ //
+ // Free buffer allocated for us.
+ //
+ NetApiBufferFree(saveptr);
+ }
+
+}
+
+
+VOID
+PrintUseInfo(
+ PUSE_INFO_1 UseInfo
+ )
+{
+
+ switch(UseInfo->ui1_status) {
+
+ case USE_OK:
+ printf("OK ");
+ break;
+
+ case USE_PAUSED:
+ printf("Paused ");
+ break;
+
+ case USE_SESSLOST:
+ printf("Disconnected ");
+ break;
+
+ case USE_NETERR:
+ printf("Net error ");
+ break;
+
+ case USE_CONN:
+ printf("Connecting ");
+ break;
+
+ case USE_RECONN:
+ printf("Reconnecting ");
+ break;
+
+ default:
+ printf("Unknown ");
+ }
+
+ printf(" %-7ws %-25ws", UseInfo->ui1_local,
+ UseInfo->ui1_remote);
+ printf("usecount=%lu, refcount=%lu\n",
+ UseInfo->ui1_usecount, UseInfo->ui1_refcount);
+
+}
diff --git a/private/net/svcdlls/wkssvc/client/wstsend.c b/private/net/svcdlls/wkssvc/client/wstsend.c
new file mode 100644
index 000000000..8e105e986
--- /dev/null
+++ b/private/net/svcdlls/wkssvc/client/wstsend.c
@@ -0,0 +1,102 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ wstsend.c
+
+Abstract:
+
+ Test program for the NetMessageBufferSend APIs. Run this test after
+ starting the Workstation service.
+
+ wstsend <recipient> <message>
+
+Author:
+
+ Rita Wong (ritaw) 12-Aug-1991
+
+Revision History:
+
+--*/
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+
+#include <winerror.h>
+#include <windef.h> // Win32 type definitions
+#include <winbase.h> // Win32 base API prototypes
+
+#include <lm.h> // LAN Man definitions
+
+#include <tstring.h>
+
+
+VOID
+main(
+ int argc,
+ char *argv[]
+ )
+{
+ DWORD i;
+ NET_API_STATUS status;
+#ifdef UNICODE
+ LPWSTR ToName;
+ LPWSTR Message;
+#else
+ LPSTR ToName;
+ LPSTR Message;
+#endif
+
+ if (argc != 3) {
+ printf("Usage: wstsend <recipient> <message>.\n");
+ return;
+ }
+
+#ifdef UNICODE
+ ToName = NetpAllocWStrFromStr(_strupr(argv[1]));
+
+ if (ToName == NULL) {
+ printf("Could not convert the receipient name.\n");
+ return;
+ }
+
+ Message = NetpAllocWStrFromStr(argv[2]);
+
+ if (ToName == NULL) {
+ printf("Could not convert the message.\n");
+ NetApiBufferFree(ToName);
+ return;
+ }
+#else
+ ToName = _strupr(argv[1]);
+ Message = argv[2];
+#endif
+
+
+ if ((status = NetMessageBufferSend(
+ NULL,
+ ToName, // To
+ NULL,
+ Message, // Message to send
+ STRLEN(Message) * sizeof(TCHAR)
+ )) != NERR_Success) {
+
+ printf("Failed in sending message to %s %lu\n", argv[1], status);
+ printf("Message is %s\n", argv[2]);
+ }
+ else {
+ printf("Message sent successfully\n");
+ }
+
+#ifdef UNICODE
+ NetApiBufferFree(ToName);
+ NetApiBufferFree(Message);
+#endif
+}
diff --git a/private/net/svcdlls/wkssvc/client/wstsenum.c b/private/net/svcdlls/wkssvc/client/wstsenum.c
new file mode 100644
index 000000000..0dabcceb3
--- /dev/null
+++ b/private/net/svcdlls/wkssvc/client/wstsenum.c
@@ -0,0 +1,580 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ wstsend.c
+
+Abstract:
+
+ Test program for the NetServerEnum API. Run this test after
+ starting the Workstation service.
+
+ wstenum [domain]
+
+Author:
+
+ Rita Wong (ritaw) 24-Oct-1991
+
+Revision History:
+
+--*/
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+
+#include <winerror.h>
+#include <windef.h> // Win32 type definitions
+#include <winbase.h> // Win32 base API prototypes
+
+#include <lm.h> // LAN Man definitions
+#include <lmserver.h>
+
+#include <tstring.h>
+#include <netdebug.h> // NetpDbgDisplay routines.
+#include <dlserver.h>
+
+#define FIXED_WIDTH_STRING "%-30ws: "
+#define INDENT " "
+
+VOID
+DisplayServerInfo(
+ IN DWORD Level,
+ IN LPVOID Info
+ );
+
+VOID
+DisplayDword(
+ IN LPTSTR Tag,
+ IN DWORD Value
+ );
+
+VOID
+DisplayBool(
+ IN LPTSTR Tag,
+ IN BOOL Value
+ );
+
+VOID
+TestServerEnum(
+ IN LPTSTR DomainName OPTIONAL,
+ IN DWORD PreferedMaximumLength,
+ IN OUT LPDWORD ResumeHandle OPTIONAL
+ );
+
+
+
+VOID _CRTAPI1
+main(
+ int argc,
+ char *argv[]
+ )
+{
+ LPTSTR DomainName = NULL;
+
+ if (argc > 2) {
+ printf("Usage: wstenum [domain].\n");
+ return;
+ }
+
+ if (argc == 2) {
+#ifdef UNICODE
+ DomainName = NetpAllocWStrFromStr(argv[1]);
+#else
+ DomainName = argv[1];
+#endif
+ }
+
+ //
+ // Enumerate all servers
+ //
+ TestServerEnum(DomainName, MAXULONG, NULL);
+
+#ifdef UNICODE
+ NetApiBufferFree(DomainName);
+#endif
+}
+
+
+
+
+
+VOID
+TestServerEnum(
+ IN LPTSTR DomainName OPTIONAL,
+ IN DWORD PreferedMaximumLength,
+ IN OUT LPDWORD ResumeHandle OPTIONAL
+ )
+{
+ DWORD i;
+ NET_API_STATUS status;
+ DWORD EntriesRead,
+ TotalEntries;
+
+ PSERVER_INFO_101 ServerInfo, saveptr;
+
+
+ if (ARGUMENT_PRESENT(ResumeHandle)) {
+ printf("\nInput ResumeHandle=x%08lx\n", *ResumeHandle);
+ }
+
+ status = NetServerEnum(
+ NULL,
+ 101,
+ (LPBYTE *) &ServerInfo,
+ PreferedMaximumLength,
+ &EntriesRead,
+ &TotalEntries,
+ SV_TYPE_ALL,
+ DomainName,
+ ResumeHandle
+ );
+
+ saveptr = ServerInfo;
+
+ if (status != NERR_Success && status != ERROR_MORE_DATA) {
+ printf("NetServerEnum FAILED %lu\n", status);
+ }
+ else {
+ printf("Return code from NetServerEnum %lu\n", status);
+
+ printf("EntriesRead=%lu, TotalEntries=%lu\n",
+ EntriesRead, TotalEntries);
+
+ if (ARGUMENT_PRESENT(ResumeHandle)) {
+ printf("Output ResumeHandle=x%08lx\n", *ResumeHandle);
+ }
+
+ for (i = 0; i < EntriesRead; i++, ServerInfo++) {
+ DisplayServerInfo(101, ServerInfo);
+ }
+
+ //
+ // Free buffer allocated for us.
+ //
+ NetApiBufferFree(saveptr);
+ }
+}
+VOID
+DisplayTag(
+ IN LPTSTR Tag
+ )
+{
+ printf(INDENT FIXED_WIDTH_STRING, Tag);
+
+} // NetpDbgDisplayTag
+VOID
+DisplayString(
+ IN LPTSTR Tag,
+ IN LPTSTR Value
+ )
+{
+ DisplayTag( Tag );
+ if (Value != NULL) {
+ printf(FORMAT_LPTSTR "\n", Value);
+ } else {
+ printf("(none)\n");
+ }
+
+} // NetpDbgDisplayString
+
+VOID
+DisplayDwordHex(
+ IN LPTSTR Tag,
+ IN DWORD Value
+ )
+{
+ DisplayTag( Tag );
+ printf(FORMAT_HEX_DWORD, Value);
+ printf("\n");
+
+} // NetpDbgDisplayDwordHex
+
+DBGSTATIC VOID
+DisplayServerType(
+ IN DWORD Type
+ )
+{
+ // Longest name is "DOMAIN_BAKCTRL" (14 chars)
+ TCHAR str[(14+2)*11]; // 14 chars per name, 2 spaces, for 11 names.
+ str[0] = '\0';
+
+ // BUGBUG: Use TEXT() macro somehow below?
+#define DO(name) \
+ if (Type & SV_TYPE_ ## name) { \
+ (void) STRCAT(str, # name); \
+ (void) STRCAT(str, L" "); \
+ Type &= ~(SV_TYPE_ ## name); \
+ }
+
+ NetpAssert(Type != 0);
+ DO(WORKSTATION);
+ DO(SERVER);
+ DO(SQLSERVER);
+ DO(DOMAIN_CTRL);
+ DO(DOMAIN_BAKCTRL);
+ DO(TIME_SOURCE);
+ DO(AFP);
+ DO(NOVELL);
+ DO(DOMAIN_MEMBER);
+ DO(PRINTQ_SERVER);
+ DO(DIALIN_SERVER);
+
+ DisplayString(L"server type", str);
+ if (Type != 0) {
+ DisplayDwordHex( L"UNEXPECTED TYPE BIT(S)", Type );
+ }
+
+} // DisplayServerType
+VOID
+DisplayLanManVersion(
+ IN DWORD MajorVersion,
+ IN DWORD MinorVersion
+ )
+{
+ DisplayTag( L"LanMan version" );
+ printf(FORMAT_DWORD "." FORMAT_DWORD "\n",
+ (DWORD) (MajorVersion & (MAJOR_VERSION_MASK)),
+ (DWORD) (MinorVersion) );
+
+} // DisplayLanManVersion
+
+DBGSTATIC VOID
+DisplayDisconnectTime(
+ IN LONG DiscTime
+ )
+{
+ DisplayTag(L"Idle session time (min)" );
+ if (DiscTime == SV_NODISC) {
+ printf("infinite\n" );
+ } else {
+ printf(FORMAT_LONG "\n", DiscTime );
+ }
+} // NetpDbgDisplayDisconnectTime
+
+DBGSTATIC VOID
+DisplayLicenses(
+ IN DWORD MajorVersion,
+ IN DWORD Licenses
+ )
+{
+ // BUGBUG: Eventually, use <srvver.h> stuff to handle unlimited and other
+ // types of licenses. This is indicated in MajorVersion.
+ UNREFERENCED_PARAMETER( MajorVersion );
+
+ DisplayDword(L"Licenses (NOT users)", Licenses );
+} // NetpDbgDisplayLicenses
+
+VOID
+DisplayPlatformId(
+ IN DWORD Value
+ )
+{
+ LPTSTR String;
+
+ switch (Value) {
+ case PLATFORM_ID_DOS : String =L"DOS"; break;
+ case PLATFORM_ID_OS2 : String =L"OS2"; break;
+ case PLATFORM_ID_NT : String =L"NT"; break;
+ default : String =L"unknown"; break;
+ }
+
+ DisplayString(L"Platform ID", String );
+}
+VOID
+DisplayBool(
+ IN LPTSTR Tag,
+ IN BOOL Value
+ )
+{
+ DisplayTag( Tag );
+ printf(Value ? "Yes" : "No");
+ printf("\n");
+
+}
+
+VOID
+DisplayDword(
+ IN LPTSTR Tag,
+ IN DWORD Value
+ )
+{
+ DisplayTag( Tag );
+ printf(FORMAT_DWORD, Value);
+ printf("\n");
+
+} // DbgDisplayDword
+VOID
+DisplayServerInfo(
+ IN DWORD Level,
+ IN LPVOID Info
+ )
+{
+ printf("server info (level " FORMAT_DWORD ") at "
+ FORMAT_LPVOID ":\n", Level, (LPVOID) Info);
+ NetpAssert(Info != NULL);
+
+ switch (Level) {
+ case 0 :
+ {
+ LPSERVER_INFO_0 psv0 = Info;
+ DisplayString(L"name", psv0->sv0_name);
+ }
+ break;
+
+ case 1 :
+ {
+ LPSERVER_INFO_1 psv1 = Info;
+ DisplayString(L"name", psv1->sv1_name);
+ DisplayLanManVersion(
+ psv1->sv1_version_major,
+ psv1->sv1_version_minor);
+ DisplayServerType( psv1->sv1_type );
+ DisplayString(L"comment", psv1->sv1_comment);
+ }
+ break;
+
+ case 2 :
+ {
+ LPSERVER_INFO_2 psv2 = Info;
+ DisplayString(L"name", psv2->sv2_name);
+ DisplayLanManVersion(
+ psv2->sv2_version_major,
+ psv2->sv2_version_minor);
+ DisplayServerType( psv2->sv2_type );
+ DisplayString(L"comment", psv2->sv2_comment);
+ DisplayDword(L"ulist_mtime", psv2->sv2_ulist_mtime);
+ DisplayDword(L"glist_mtime", psv2->sv2_glist_mtime);
+ DisplayDword(L"alist_mtime", psv2->sv2_alist_mtime);
+ DisplayDword(L"users", psv2->sv2_users);
+ DisplayDisconnectTime( psv2->sv2_disc);
+ DisplayString(L"alerts", psv2->sv2_alerts);
+ DisplayDword(L"security", psv2->sv2_security);
+ DisplayDword(L"auditing", psv2->sv2_auditing);
+ DisplayDword(L"numadmin", psv2->sv2_numadmin);
+ DisplayDword(L"lanmask", psv2->sv2_lanmask);
+ DisplayDword(L"hidden", psv2->sv2_hidden);
+ DisplayDword(L"announce", psv2->sv2_announce);
+ DisplayDword(L"anndelta", psv2->sv2_anndelta);
+ DisplayString(L"guestacct", psv2->sv2_guestacct);
+ DisplayLicenses(
+ psv2->sv2_version_major,
+ psv2->sv2_licenses );
+ DisplayString(L"userpath", psv2->sv2_userpath);
+ DisplayDword(L"chdevs", psv2->sv2_chdevs);
+ DisplayDword(L"chdevq", psv2->sv2_chdevq);
+ DisplayDword(L"chdevjobs", psv2->sv2_chdevjobs);
+ DisplayDword(L"connections", psv2->sv2_connections);
+ DisplayDword(L"shares", psv2->sv2_shares);
+ DisplayDword(L"openfiles", psv2->sv2_openfiles);
+ DisplayDword(L"sessopens", psv2->sv2_sessopens);
+ DisplayDword(L"sessvcs", psv2->sv2_sessvcs);
+ DisplayDword(L"sessreqs", psv2->sv2_sessreqs);
+ DisplayDword(L"opensearch", psv2->sv2_opensearch);
+ DisplayDword(L"activelocks", psv2->sv2_activelocks);
+ DisplayDword(L"numreqbuf", psv2->sv2_numreqbuf);
+ DisplayDword(L"sizreqbuf", psv2->sv2_sizreqbuf);
+ DisplayDword(L"numbigbuf", psv2->sv2_numbigbuf);
+ DisplayDword(L"numfiletasks", psv2->sv2_numfiletasks);
+ DisplayDword(L"alertsched", psv2->sv2_alertsched);
+ DisplayDword(L"erroralert", psv2->sv2_erroralert);
+ DisplayDword(L"logonalert", psv2->sv2_logonalert);
+ DisplayDword(L"accessalert", psv2->sv2_accessalert);
+ DisplayDword(L"diskalert", psv2->sv2_diskalert);
+ DisplayDword(L"netioalert", psv2->sv2_netioalert);
+ DisplayDword(L"maxauditsz", psv2->sv2_maxauditsz);
+ DisplayString(L"srvheuristics", psv2->sv2_srvheuristics);
+ }
+ break;
+
+ case 3 :
+ {
+ LPSERVER_INFO_3 psv3 = Info;
+ DisplayString(L"name", psv3->sv3_name);
+ DisplayLanManVersion(
+ psv3->sv3_version_major,
+ psv3->sv3_version_minor);
+ DisplayServerType( psv3->sv3_type );
+ DisplayString(L"comment", psv3->sv3_comment);
+ DisplayDword(L"ulist_mtime", psv3->sv3_ulist_mtime);
+ DisplayDword(L"glist_mtime", psv3->sv3_glist_mtime);
+ DisplayDword(L"alist_mtime", psv3->sv3_alist_mtime);
+ DisplayDword(L"users", psv3->sv3_users);
+ DisplayDisconnectTime( psv3->sv3_disc );
+ DisplayString(L"alerts", psv3->sv3_alerts);
+ DisplayDword(L"security", psv3->sv3_security);
+ DisplayDword(L"auditing", psv3->sv3_auditing);
+ DisplayDword(L"numadmin", psv3->sv3_numadmin);
+ DisplayDword(L"lanmask", psv3->sv3_lanmask);
+ DisplayDword(L"hidden", psv3->sv3_hidden);
+ DisplayDword(L"announce", psv3->sv3_announce);
+ DisplayDword(L"anndelta", psv3->sv3_anndelta);
+ DisplayString(L"guestacct", psv3->sv3_guestacct);
+ DisplayLicenses(
+ psv3->sv3_version_major,
+ psv3->sv3_licenses );
+ DisplayString(L"userpath", psv3->sv3_userpath);
+ DisplayDword(L"chdevs", psv3->sv3_chdevs);
+ DisplayDword(L"chdevq", psv3->sv3_chdevq);
+ DisplayDword(L"chdevjobs", psv3->sv3_chdevjobs);
+ DisplayDword(L"connections", psv3->sv3_connections);
+ DisplayDword(L"shares", psv3->sv3_shares);
+ DisplayDword(L"openfiles", psv3->sv3_openfiles);
+ DisplayDword(L"sessopens", psv3->sv3_sessopens);
+ DisplayDword(L"sessvcs", psv3->sv3_sessvcs);
+ DisplayDword(L"sessreqs", psv3->sv3_sessreqs);
+ DisplayDword(L"opensearch", psv3->sv3_opensearch);
+ DisplayDword(L"activelocks", psv3->sv3_activelocks);
+ DisplayDword(L"numreqbuf", psv3->sv3_numreqbuf);
+ DisplayDword(L"sizreqbuf", psv3->sv3_sizreqbuf);
+ DisplayDword(L"numbigbuf", psv3->sv3_numbigbuf);
+ DisplayDword(L"numfiletasks", psv3->sv3_numfiletasks);
+ DisplayDword(L"alertsched", psv3->sv3_alertsched);
+ DisplayDword(L"erroralert", psv3->sv3_erroralert);
+ DisplayDword(L"logonalert", psv3->sv3_logonalert);
+ DisplayDword(L"accessalert", psv3->sv3_accessalert);
+ DisplayDword(L"diskalert", psv3->sv3_diskalert);
+ DisplayDword(L"netioalert", psv3->sv3_netioalert);
+ DisplayDword(L"maxauditsz", psv3->sv3_maxauditsz);
+ DisplayString(L"srvheuristics", psv3->sv3_srvheuristics);
+ DisplayDword(L"auditedevents", psv3->sv3_auditedevents);
+ DisplayDword(L"autoprofile", psv3->sv3_autoprofile);
+ DisplayString(L"autopath", psv3->sv3_autopath);
+ }
+ break;
+
+ case 100 :
+ {
+ LPSERVER_INFO_100 psv100 = Info;
+ DisplayPlatformId( psv100->sv100_platform_id );
+ DisplayString(L"Server Name", psv100->sv100_name);
+ }
+ break;
+
+ case 101 :
+ {
+ LPSERVER_INFO_101 psv101 = Info;
+ DisplayPlatformId( psv101->sv101_platform_id );
+ DisplayString(L"Server Name", psv101->sv101_name);
+ DisplayLanManVersion(
+ psv101->sv101_version_major,
+ psv101->sv101_version_minor);
+ DisplayServerType( psv101->sv101_type );
+ DisplayString(L"Server Comment", psv101->sv101_comment);
+ }
+ break;
+
+ case 102 :
+ {
+ LPSERVER_INFO_102 psv102 = Info;
+ DisplayPlatformId( psv102->sv102_platform_id );
+ DisplayString(L"Server Name", psv102->sv102_name);
+ DisplayLanManVersion(
+ psv102->sv102_version_major,
+ psv102->sv102_version_minor );
+ DisplayServerType( psv102->sv102_type );
+ DisplayString(L"Server Comment", psv102->sv102_comment );
+ DisplayDword(L"users", psv102->sv102_users );
+ DisplayBool(L"Server hidden", psv102->sv102_hidden );
+ DisplayDword(L"announce", psv102->sv102_announce );
+ DisplayDword(L"announce delta", psv102->sv102_anndelta );
+ DisplayLicenses(
+ psv102->sv102_version_major,
+ psv102->sv102_licenses );
+ DisplayString(L"user path", psv102->sv102_userpath );
+ }
+ break;
+
+ case 402 :
+ {
+ LPSERVER_INFO_402 psv402 = Info;
+ DisplayDword(L"ulist mtime", psv402->sv402_ulist_mtime);
+ DisplayDword(L"glist mtime", psv402->sv402_glist_mtime);
+ DisplayDword(L"alist mtime", psv402->sv402_alist_mtime);
+ DisplayString(L"alerts", psv402->sv402_alerts);
+ DisplayDword(L"security", psv402->sv402_security);
+ DisplayDword(L"numadmin", psv402->sv402_numadmin);
+ DisplayDwordHex(L"lanmask", psv402->sv402_lanmask);
+ DisplayString(L"guestacct", psv402->sv402_guestacct);
+ DisplayDword(L"chdevs", psv402->sv402_chdevs);
+ DisplayDword(L"chdevq", psv402->sv402_chdevq);
+ DisplayDword(L"chdevjobs", psv402->sv402_chdevjobs);
+ DisplayDword(L"connections", psv402->sv402_connections);
+ DisplayDword(L"shares", psv402->sv402_shares);
+ DisplayDword(L"openfiles", psv402->sv402_openfiles);
+ DisplayDword(L"sessopens", psv402->sv402_sessopens);
+ DisplayDword(L"sessvcs", psv402->sv402_sessvcs);
+ DisplayDword(L"sessreqs", psv402->sv402_sessreqs);
+ DisplayDword(L"opensearch", psv402->sv402_opensearch);
+ DisplayDword(L"activelocks", psv402->sv402_activelocks);
+ DisplayDword(L"numreqbuf", psv402->sv402_numreqbuf);
+ DisplayDword(L"sizreqbuf", psv402->sv402_sizreqbuf);
+ DisplayDword(L"numbigbuf", psv402->sv402_numbigbuf);
+ DisplayDword(L"numfiletasks", psv402->sv402_numfiletasks);
+ DisplayDword(L"alertsched", psv402->sv402_alertsched);
+ DisplayDword(L"erroralert", psv402->sv402_erroralert);
+ DisplayDword(L"logonalert", psv402->sv402_logonalert);
+ DisplayDword(L"diskalert", psv402->sv402_diskalert);
+ DisplayDword(L"accessalert", psv402->sv402_accessalert);
+ DisplayDword(L"diskalert", psv402->sv402_diskalert);
+ DisplayDword(L"netioalert", psv402->sv402_netioalert);
+ DisplayDword(L"maxauditsz", psv402->sv402_maxauditsz);
+ DisplayString(L"srvheuristics", psv402->sv402_srvheuristics);
+ }
+ break;
+
+ case 403 :
+ {
+ LPSERVER_INFO_403 psv403 = Info;
+ DisplayDword(L"ulist mtime", psv403->sv403_ulist_mtime);
+ DisplayDword(L"glist mtime", psv403->sv403_glist_mtime);
+ DisplayDword(L"alist mtime", psv403->sv403_alist_mtime);
+ DisplayString(L"alerts", psv403->sv403_alerts);
+ DisplayDword(L"security", psv403->sv403_security);
+ DisplayDword(L"numadmin", psv403->sv403_numadmin);
+ DisplayDwordHex(L"lanmask", psv403->sv403_lanmask);
+ DisplayString(L"guestacct", psv403->sv403_guestacct);
+ DisplayDword(L"chdevs", psv403->sv403_chdevs);
+ DisplayDword(L"chdevq", psv403->sv403_chdevq);
+ DisplayDword(L"chdevjobs", psv403->sv403_chdevjobs);
+ DisplayDword(L"connections", psv403->sv403_connections);
+ DisplayDword(L"shares", psv403->sv403_shares);
+ DisplayDword(L"openfiles", psv403->sv403_openfiles);
+ DisplayDword(L"sessopens", psv403->sv403_sessopens);
+ DisplayDword(L"sessvcs", psv403->sv403_sessvcs);
+ DisplayDword(L"sessreqs", psv403->sv403_sessreqs);
+ DisplayDword(L"opensearch", psv403->sv403_opensearch);
+ DisplayDword(L"activelocks", psv403->sv403_activelocks);
+ DisplayDword(L"numreqbuf", psv403->sv403_numreqbuf);
+ DisplayDword(L"sizreqbuf", psv403->sv403_sizreqbuf);
+ DisplayDword(L"numbigbuf", psv403->sv403_numbigbuf);
+ DisplayDword(L"numfiletasks", psv403->sv403_numfiletasks);
+ DisplayDword(L"alertsched", psv403->sv403_alertsched);
+ DisplayDword(L"erroralert", psv403->sv403_erroralert);
+ DisplayDword(L"logonalert", psv403->sv403_logonalert);
+ DisplayDword(L"diskalert", psv403->sv403_diskalert);
+ DisplayDword(L"accessalert", psv403->sv403_accessalert);
+ DisplayDword(L"diskalert", psv403->sv403_diskalert);
+ DisplayDword(L"netioalert", psv403->sv403_netioalert);
+ DisplayDword(L"maxauditsz", psv403->sv403_maxauditsz);
+ DisplayString(L"srvheuristics", psv403->sv403_srvheuristics);
+ DisplayDword(L"auditedevents", psv403->sv403_auditedevents);
+ DisplayDword(L"autoprofile", psv403->sv403_autoprofile);
+ DisplayString(L"autopath", psv403->sv403_autopath);
+ }
+ break;
+
+ // BUGBUG: RpcXlate doesn't need support for info levels 502, 503, 599.
+ // Feel free to add them here if you need them.
+
+ default :
+ NetpAssert(FALSE);
+ }
+
+} // DisplayServerInfo
diff --git a/private/net/svcdlls/wkssvc/dirs b/private/net/svcdlls/wkssvc/dirs
new file mode 100644
index 000000000..c79f585b8
--- /dev/null
+++ b/private/net/svcdlls/wkssvc/dirs
@@ -0,0 +1,29 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ dirs.
+
+Abstract:
+
+ This file specifies the subdirectories of the current directory that
+ contain component makefiles.
+
+
+Author:
+
+ Steve Wood (stevewo) 17-Apr-1990
+
+NOTE: Commented description of this file is in \nt\bak\bin\dirs.tpl
+
+!ENDIF
+
+
+DIRS=server \
+ client
+
+
+#OPTIONAL_DIRS=dir8 \
+# dir9
diff --git a/private/net/svcdlls/wkssvc/imports.h b/private/net/svcdlls/wkssvc/imports.h
new file mode 100644
index 000000000..5e7a45a8d
--- /dev/null
+++ b/private/net/svcdlls/wkssvc/imports.h
@@ -0,0 +1,46 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ imports.h
+
+Abstract:
+
+ This file allows us to include standard system header files in the
+ .idl file. The main .idl file imports a file called import.idl.
+ This allows the .idl file to use the types defined in these header
+ files. It also causes the following line to be added in the
+ MIDL generated header file:
+
+ #include "imports.h"
+
+ Thus these types are available to the RPC stub routines as well.
+
+Author:
+
+ Dan Lafferty (danl) 07-May-1991
+
+Revision History:
+
+
+--*/
+
+
+#include <windef.h>
+#include <lmcons.h>
+
+#ifdef MIDL_PASS
+#ifdef UNICODE
+#define LPTSTR [string] wchar_t*
+#else
+#define LPTSTR [string] LPTSTR
+#endif
+#define LPSTR [string] LPSTR
+#define BOOL DWORD
+#endif
+
+#include <lmwksta.h>
+#include <lmuse.h>
+#include <lmstats.h>
diff --git a/private/net/svcdlls/wkssvc/imports.idl b/private/net/svcdlls/wkssvc/imports.idl
new file mode 100644
index 000000000..f8ce9137f
--- /dev/null
+++ b/private/net/svcdlls/wkssvc/imports.idl
@@ -0,0 +1,67 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ imports.idl
+
+Abstract:
+
+ This file is useful for creating RPC interfaces that require the use
+ of windef types. The .idl file for the RPC product should contain a
+ line in the interface body that imports this file. For example:
+
+ import "imports.h";
+
+ Doing this causes the MIDL generated header file to contain the
+ following line:
+
+ #include "imports.h"
+
+ If this technique is not used, and instead the .idl file for the RPC
+ product simply contains #include <importsf.h>, then the contents of
+ imports.h will be expanded in the MIDL generated header file. This
+ can lead to duplicate definition problems later when the RPC client
+ or RPC server code needs to include both the MIDL generated header file
+ and a file that is included in imports.h.
+
+Author:
+
+ Dan Lafferty (danl) 20-Mar-1991
+
+Environment:
+
+ User Mode - Win32 - for use with the MIDL compiler
+
+
+Revision History:
+
+ 03-Apr-1991 danl
+ created
+
+--*/
+
+[
+ uuid(12345678-1234-ABCD-EF00-9948756789AB),
+#ifdef __midl
+ ms_union,
+#endif // __midl
+ version(1.0)
+]
+interface imports
+
+{
+#define MIDL_PASS
+#include "imports.h"
+
+//
+// All .idl files need to contain at least one function prototype
+//
+
+DWORD
+Dummy(
+ [in] DWORD DummyParm);
+
+
+}
diff --git a/private/net/svcdlls/wkssvc/makefil0 b/private/net/svcdlls/wkssvc/makefil0
new file mode 100644
index 000000000..82ec1beab
--- /dev/null
+++ b/private/net/svcdlls/wkssvc/makefil0
@@ -0,0 +1,62 @@
+#
+# This is the MIDL compile phase of the build process.
+#
+# The following is where you put the name of your .idl file without
+# the .idl extension:
+#
+
+!INCLUDE $(NTMAKEENV)\makefile.plt
+
+IDL_NAME = wkssvc
+
+!IFNDEF DISABLE_NET_UNICODE
+UNICODE=1
+NET_C_DEFINES=-DUNICODE
+!ENDIF
+
+SDKINC = $(BASEDIR)\public\sdk\inc
+NETINC = $(BASEDIR)\private\net\inc
+SDKCRTINC = $(BASEDIR)\public\sdk\inc\crt
+PRIVINC = $(BASEDIR)\private\inc
+
+INCS = -I$(SDKINC) -I$(SDKCRTINC) -I$(PRIVINC) -I$(NETINC)
+
+CLIENT_TARGETS = client\$(IDL_NAME)_c.c \
+ .\$(IDL_NAME).h
+
+SERVER_TARGETS = server\$(IDL_NAME)_s.c
+
+EXTRN_DEPENDS = $(SDKINC)\lmcons.h \
+ $(SDKINC)\windef.h \
+ $(SDKINC)\lmuse.h \
+ $(SDKINC)\lmwksta.h \
+ $(SDKINC)\lmstats.h \
+ $(IDL_NAME).acf
+
+CPP = -cpp_cmd "$(MIDL_CPP)" $(MIDL_FLAGS) $(C_DEFINES) $(NET_C_DEFINES)
+
+#
+# Define Products and Dependencies
+#
+
+all: $(CLIENT_TARGETS) $(SERVER_TARGETS) $(EXTRN_DEPENDS)
+!IF "$(BUILDMSG)" != ""
+ @ech ; $(BUILDMSG) ;
+!ENDIF
+
+clean: delsrc all
+
+delsrc:
+ erase $(CLIENT_TARGETS) $(SERVER_TARGETS)
+
+#
+# MIDL COMPILE
+#
+
+$(CLIENT_TARGETS) : .\$(IDL_NAME).idl $(EXTRN_DEPENDS)
+ midl -Oi -server none -oldnames -error allocation -error ref -ms_ext -c_ext $(CPP) .\$(IDL_NAME).idl $(INCS)
+ IF EXIST $(IDL_NAME)_c.c copy $(IDL_NAME)_c.c .\client & del $(IDL_NAME)_c.c
+
+$(SERVER_TARGETS) : .\$(IDL_NAME).idl $(EXTRN_DEPENDS)
+ midl -client none -oldnames -error stub_data -error allocation -error ref -ms_ext -c_ext $(CPP) .\$(IDL_NAME).idl $(INCS)
+ IF EXIST $(IDL_NAME)_s.c copy $(IDL_NAME)_s.c .\server & del $(IDL_NAME)_s.c
diff --git a/private/net/svcdlls/wkssvc/server/daytona/makefile b/private/net/svcdlls/wkssvc/server/daytona/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/net/svcdlls/wkssvc/server/daytona/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/net/svcdlls/wkssvc/server/daytona/sources b/private/net/svcdlls/wkssvc/server/daytona/sources
new file mode 100644
index 000000000..0f6114710
--- /dev/null
+++ b/private/net/svcdlls/wkssvc/server/daytona/sources
@@ -0,0 +1,84 @@
+!IF 0
+
+Copyright (c) 1989-92 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+
+Author:
+
+ Steve Wood (stevewo) 12-Apr-1990
+
+NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl
+
+!ENDIF
+
+MAJORCOMP=net
+MINORCOMP=wksserver
+
+TARGETPATH=\nt\public\sdk\lib
+TARGETNAME=wkssvc
+TARGETTYPE=DYNLINK
+DLLDEF=..\wkssvc.def
+
+TARGETLIBS= \
+ $(BASEDIR)\public\sdk\lib\*\kernel32.lib \
+ $(BASEDIR)\public\sdk\lib\*\advapi32.lib \
+ $(BASEDIR)\public\sdk\lib\*\lsadll.lib \
+ $(BASEDIR)\public\sdk\lib\*\netapi32.lib \
+ $(BASEDIR)\public\sdk\lib\*\rpcutil.lib \
+ $(BASEDIR)\public\sdk\lib\*\rpcndr.lib \
+# $(BASEDIR)\Public\Sdk\Lib\*\netlib.lib \
+ $(BASEDIR)\public\sdk\lib\*\rpcrt4.lib
+
+INCLUDES=..\..;..\..\..\..\inc;..\..\..\..\..\inc;..\..\..\..\api
+
+!IFNDEF DISABLE_NET_UNICODE
+UNICODE=1
+NET_C_DEFINES=-DUNICODE
+!ENDIF
+
+MSC_WARNING_LEVEL=/W3 /WX
+
+SOURCES= \
+ ..\wsmain.c \
+ ..\wkssvc.rc \
+ ..\wksta.c \
+ ..\user.c \
+ ..\wsdevice.c \
+ ..\wsutil.c \
+ ..\wsconfig.c \
+ ..\useaddel.c \
+ ..\usegenum.c \
+ ..\useutil.c \
+ ..\message.c \
+ ..\wssend.c \
+ ..\msgutil.c \
+ ..\wssec.c \
+ ..\wslsa.c \
+ ..\wsstats.c \
+ ..\wslogon.c \
+ ..\wsdfs.c \
+ ..\dominfo.c \
+ ..\dfsmrshl.c \
+ ..\wkssvc_s.c
+
+C_DEFINES= -DINCL_32= -DNT -DRPC_NO_WINDOWS_H -D_PNP_POWER=1
+
+USE_CRTDLL=1
+
+#386_WARNING_LEVEL=-W4
+
+UMTYPE=windows
+
+UMLIBS=
+
diff --git a/private/net/svcdlls/wkssvc/server/dfsgluon.h b/private/net/svcdlls/wkssvc/server/dfsgluon.h
new file mode 100644
index 000000000..ab20ca625
--- /dev/null
+++ b/private/net/svcdlls/wkssvc/server/dfsgluon.h
@@ -0,0 +1,139 @@
+//+----------------------------------------------------------------------------
+//
+// Copyright (C) 1992, Microsoft Corporation
+//
+// File: DfsGluon.h
+//
+// Contents: Declarations for dfs use of gluons
+//
+// Classes:
+//
+// Functions:
+//
+// History: March 24, 1994 Milans Created
+//
+//-----------------------------------------------------------------------------
+
+
+#ifndef _DFS_GLUON_
+#define _DFS_GLUON_
+
+#include <gluon.h>
+
+
+//
+// Marshalling info for TAddress
+//
+
+extern MARSHAL_INFO MiTAddress;
+
+#define INIT_TADDRESS_MARSHAL_INFO() \
+ static MARSHAL_TYPE_INFO _MCode_TAddress[] = { \
+ _MCode_conformant(TA_ADDRESS, Address, AddressLength), \
+ _MCode_ush(TA_ADDRESS, AddressLength), \
+ _MCode_ush(TA_ADDRESS, AddressType), \
+ _MCode_cauch(TA_ADDRESS, Address, AddressLength) \
+ }; \
+ MARSHAL_INFO MiTAddress = _mkMarshalInfo(TA_ADDRESS, _MCode_TAddress);
+
+//
+// Marshalling info for DS_TRANSPORT
+//
+
+extern MARSHAL_INFO MiDSTransport;
+
+#define INIT_DS_TRANSPORT_MARSHAL_INFO() \
+ static MARSHAL_TYPE_INFO _MCode_DSTransport[] = { \
+ _MCode_conformant(DS_TRANSPORT, taddr.Address, taddr.AddressLength), \
+ _MCode_ush(DS_TRANSPORT, usFileProtocol), \
+ _MCode_ush(DS_TRANSPORT, iPrincipal), \
+ _MCode_ush(DS_TRANSPORT, grfModifiers), \
+ _MCode_struct(DS_TRANSPORT, taddr, &MiTAddress) \
+ }; \
+ MARSHAL_INFO MiDSTransport = _mkMarshalInfo(DS_TRANSPORT, _MCode_DSTransport);
+
+
+//
+// The following is needed to define an array of pointers to DS_TRANSPORT
+//
+
+typedef struct _DS_TRANSPORT_P {
+ PDS_TRANSPORT pDSTransport;
+} DS_TRANSPORT_P;
+
+#define INIT_DS_TRANSPORT_P_MARSHAL_INFO() \
+ static MARSHAL_TYPE_INFO _MCode_DSTransportP[] = { \
+ _MCode_pstruct(DS_TRANSPORT_P, pDSTransport, &MiDSTransport) \
+ }; \
+ MARSHAL_INFO MiDSTransportP = _mkMarshalInfo(DS_TRANSPORT_P, _MCode_DSTransportP);
+
+extern MARSHAL_INFO MiDSTransportP;
+
+//
+// Marshalling info for DS_MACHINE
+//
+
+extern MARSHAL_INFO MiDSMachine;
+
+#define INIT_DS_MACHINE_MARSHAL_INFO() \
+ static MARSHAL_TYPE_INFO _MCode_DSMachine[] = { \
+ _MCode_conformant(DS_MACHINE, rpTrans, cTransports), \
+ _MCode_guid(DS_MACHINE, guidSite), \
+ _MCode_guid(DS_MACHINE, guidMachine), \
+ _MCode_ul(DS_MACHINE, grfFlags), \
+ _MCode_pwstr(DS_MACHINE, pwszShareName), \
+ _MCode_ul(DS_MACHINE, cPrincipals), \
+ _MCode_pcapwstr(DS_MACHINE, prgpwszPrincipals, cPrincipals), \
+ _MCode_ul(DS_MACHINE, cTransports), \
+ _MCode_castruct(DS_MACHINE, rpTrans, cTransports, &MiDSTransportP) \
+ }; \
+ MARSHAL_INFO MiDSMachine = _mkMarshalInfo(DS_MACHINE, _MCode_DSMachine);
+
+//
+// The following is needed to define an array of pointers to DS_MACHINE
+//
+
+typedef struct _DS_MACHINE_P {
+ PDS_MACHINE pDSMachine;
+} DS_MACHINE_P;
+
+#define INIT_DS_MACHINE_P_MARSHAL_INFO() \
+ static MARSHAL_TYPE_INFO _MCode_DSMachineP[] = { \
+ _MCode_pstruct(DS_MACHINE_P, pDSMachine, &MiDSMachine) \
+ }; \
+ MARSHAL_INFO MiDSMachineP = _mkMarshalInfo(DS_MACHINE_P, _MCode_DSMachineP);
+
+extern MARSHAL_INFO MiDSMachineP;
+//
+// Marshalling info for DS_GLUON
+//
+
+extern MARSHAL_INFO MiDSGluon;
+
+#define INIT_DS_GLUON_MARSHAL_INFO() \
+ static MARSHAL_TYPE_INFO _MCode_DSGluon[] = { \
+ _MCode_conformant(DS_GLUON, rpMachines, cMachines), \
+ _MCode_guid(DS_GLUON, guidThis), \
+ _MCode_pwstr(DS_GLUON, pwszName), \
+ _MCode_ul(DS_GLUON, grfFlags), \
+ _MCode_ul(DS_GLUON, cMachines), \
+ _MCode_castruct(DS_GLUON, rpMachines, cMachines, &MiDSMachineP) \
+ }; \
+ MARSHAL_INFO MiDSGluon = _mkMarshalInfo(DS_GLUON, _MCode_DSGluon);
+
+typedef struct _DS_GLUON_P {
+ PDS_GLUON pDSGluon;
+} DS_GLUON_P;
+
+extern MARSHAL_INFO MiDSGluonP;
+
+#define INIT_DS_GLUON_P_MARSHAL_INFO() \
+ static MARSHAL_TYPE_INFO _MCode_DSGluonP[] = { \
+ _MCode_pstruct(DS_GLUON_P, pDSGluon, &MiDSGluon) \
+ }; \
+ MARSHAL_INFO MiDSGluonP = _mkMarshalInfo(DS_GLUON_P, _MCode_DSGluonP);
+
+#endif // _DFS_GLUON_
+
+
+
diff --git a/private/net/svcdlls/wkssvc/server/dfsmrshl.c b/private/net/svcdlls/wkssvc/server/dfsmrshl.c
new file mode 100644
index 000000000..13fa096b7
--- /dev/null
+++ b/private/net/svcdlls/wkssvc/server/dfsmrshl.c
@@ -0,0 +1,1429 @@
+//+----------------------------------------------------------------------------
+//
+// Copyright (C) 1992, Microsoft Corporation
+//
+// File: DfsMrshl.c
+//
+// Contents: Routines to handle marshalling of data structures. This file
+// has been specifically created so that user level code can
+// avail of the marshalling code simply by including this file.
+//
+// Classes:
+//
+// Functions:
+//
+// History: March 29, 1994 Milans created from PeterCo's routines
+//
+//-----------------------------------------------------------------------------
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windows.h>
+
+#include "dfsmrshl.h"
+
+#ifndef ExRaiseStatus
+#define ExRaiseStatus(x) RtlRaiseStatus(x)
+#endif // ExRaiseStatus
+
+#ifndef try_return
+#define try_return(s) {s; goto try_exit;}
+#endif // try_return
+
+#ifndef DebugTrace
+#define DebugTrace(i,l,f,s)
+#endif // DebugTrace
+
+
+NTSTATUS
+DfsRtlGetpwString(
+ IN OUT PMARSHAL_BUFFER MarshalBuffer,
+ OUT PWSTR *ppwszString
+);
+
+NTSTATUS
+DfsRtlPutpwString(
+ IN OUT PMARSHAL_BUFFER MarshalBuffer,
+ OUT PWSTR *ppwszString
+);
+
+NTSTATUS
+DfsRtlGetString(
+ IN OUT PMARSHAL_BUFFER MarshalBuffer,
+ OUT PSTRING String
+);
+
+NTSTATUS
+DfsRtlPutString(
+ IN OUT PMARSHAL_BUFFER MarshalBuffer,
+ OUT PSTRING String
+);
+
+
+#ifdef ALLOC_PRAGMA
+
+#pragma alloc_text( PAGE, DfsRtlGetpwString )
+#pragma alloc_text( PAGE, DfsRtlPutpwString )
+#pragma alloc_text( PAGE, DfsRtlGetString )
+#pragma alloc_text( PAGE, DfsRtlPutString )
+#pragma alloc_text( PAGE, DfsRtlGet )
+#pragma alloc_text( PAGE, DfsRtlPut )
+#pragma alloc_text( PAGE, DfsRtlSize )
+#pragma alloc_text( PAGE, DfsRtlUnwindGet )
+
+#endif //ALLOC_PRAGMA
+
+#define UNREFERENCED_LABEL(label)\
+ if(0) goto label;
+
+//
+// Defines and functions for unmarshalling base types.
+// Regarding the BYTE macros below we dont care whether we are on
+// LITTLE ENDIAN or BIG ENDIAN. It just does not matter.
+//
+
+#define BYTE_0_MASK 0xFF
+
+#define BYTE_0(Value) (UCHAR)( (Value) & BYTE_0_MASK)
+#define BYTE_1(Value) (UCHAR)( ((Value) >> 8) & BYTE_0_MASK)
+#define BYTE_2(Value) (UCHAR)( ((Value) >> 16) & BYTE_0_MASK)
+#define BYTE_3(Value) (UCHAR)( ((Value) >> 24) & BYTE_0_MASK)
+
+
+#define DfsRtlGetUchar(MarshalBuffer, pValue) ( \
+ ((MarshalBuffer)->Current + 1 <= (MarshalBuffer)->Last) ? \
+ *(pValue) = (UCHAR)((MarshalBuffer)->Current[0] ), \
+ (MarshalBuffer)->Current += 1, \
+ STATUS_SUCCESS \
+ : STATUS_DATA_ERROR \
+ )
+
+#define DfsRtlPutUchar(MarshalBuffer, pValue) ( \
+ ((MarshalBuffer)->Current + 1 <= (MarshalBuffer)->Last) ? \
+ (MarshalBuffer)->Current[0] = BYTE_0(*pValue), \
+ (MarshalBuffer)->Current += 1, \
+ STATUS_SUCCESS \
+ : STATUS_BUFFER_TOO_SMALL \
+ )
+
+#define DfsRtlGetUshort(MarshalBuffer, pValue) ( \
+ ((MarshalBuffer)->Current + 2 <= (MarshalBuffer)->Last) ? \
+ *(pValue) = (USHORT)((MarshalBuffer)->Current[0] ) | \
+ ((MarshalBuffer)->Current[1] << 8), \
+ (MarshalBuffer)->Current += 2, \
+ STATUS_SUCCESS \
+ : STATUS_DATA_ERROR \
+ )
+
+#define DfsRtlPutUshort(MarshalBuffer, pValue) ( \
+ ((MarshalBuffer)->Current + 2 <= (MarshalBuffer)->Last) ? \
+ (MarshalBuffer)->Current[0] = BYTE_0(*pValue), \
+ (MarshalBuffer)->Current[1] = BYTE_1(*pValue), \
+ (MarshalBuffer)->Current += 2, \
+ STATUS_SUCCESS \
+ : STATUS_BUFFER_TOO_SMALL \
+ )
+
+#define DfsRtlGetUlong(MarshalBuffer, pValue) ( \
+ ((MarshalBuffer)->Current + 4 <= (MarshalBuffer)->Last) ? \
+ *(pValue) = (ULONG) ((MarshalBuffer)->Current[0] ) | \
+ ((MarshalBuffer)->Current[1] << 8) | \
+ ((MarshalBuffer)->Current[2] << 16) | \
+ ((MarshalBuffer)->Current[3] << 24), \
+ (MarshalBuffer)->Current += 4, \
+ STATUS_SUCCESS \
+ : STATUS_DATA_ERROR \
+ )
+
+#define DfsRtlPutUlong(MarshalBuffer, pValue) ( \
+ ((MarshalBuffer)->Current + 4 <= (MarshalBuffer)->Last) ? \
+ (MarshalBuffer)->Current[0] = BYTE_0(*pValue), \
+ (MarshalBuffer)->Current[1] = BYTE_1(*pValue), \
+ (MarshalBuffer)->Current[2] = BYTE_2(*pValue), \
+ (MarshalBuffer)->Current[3] = BYTE_3(*pValue), \
+ (MarshalBuffer)->Current += 4, \
+ STATUS_SUCCESS \
+ : STATUS_BUFFER_TOO_SMALL \
+ )
+
+#define DfsRtlGetGuid(MarshalBuffer, pValue) ( \
+ ((MarshalBuffer)->Current + 16 <= (MarshalBuffer)->Last) ? \
+ (pValue)->Data1 = (ULONG) ((MarshalBuffer)->Current[0] ) | \
+ ((MarshalBuffer)->Current[1] << 8) | \
+ ((MarshalBuffer)->Current[2] << 16) | \
+ ((MarshalBuffer)->Current[3] << 24) , \
+ (pValue)->Data2 = (USHORT)((MarshalBuffer)->Current[4] ) | \
+ ((MarshalBuffer)->Current[5] << 8) , \
+ (pValue)->Data3 = (USHORT)((MarshalBuffer)->Current[6] ) | \
+ ((MarshalBuffer)->Current[7] << 8) , \
+ memcpy((pValue)->Data4, &(MarshalBuffer)->Current[8], 8), \
+ (MarshalBuffer)->Current += 16, \
+ STATUS_SUCCESS \
+ : STATUS_DATA_ERROR \
+ )
+
+#define DfsRtlPutGuid(MarshalBuffer, pValue) ( \
+ ((MarshalBuffer)->Current + 16 <= (MarshalBuffer)->Last) ? \
+ (MarshalBuffer)->Current[0] = BYTE_0((pValue)->Data1), \
+ (MarshalBuffer)->Current[1] = BYTE_1((pValue)->Data1), \
+ (MarshalBuffer)->Current[2] = BYTE_2((pValue)->Data1), \
+ (MarshalBuffer)->Current[3] = BYTE_3((pValue)->Data1), \
+ (MarshalBuffer)->Current[4] = BYTE_0((pValue)->Data2), \
+ (MarshalBuffer)->Current[5] = BYTE_1((pValue)->Data2), \
+ (MarshalBuffer)->Current[6] = BYTE_0((pValue)->Data3), \
+ (MarshalBuffer)->Current[7] = BYTE_1((pValue)->Data3), \
+ memcpy(&(MarshalBuffer)->Current[8], (pValue)->Data4, 8), \
+ (MarshalBuffer)->Current += 16, \
+ STATUS_SUCCESS \
+ : STATUS_BUFFER_TOO_SMALL \
+ )
+
+
+
+#define DfsRtlSizeString(pString, pSize) ( \
+ ((pString)->Length > 0) ? ( \
+ ((pString)->Buffer != NULL) ? \
+ (*(pSize)) += (2 + (pString)->Length), \
+ STATUS_SUCCESS \
+ : STATUS_DATA_ERROR \
+ ) \
+ : ((*(pSize)) += 2, \
+ STATUS_SUCCESS) \
+ )
+
+#define DfsRtlSizepwString(pString, pSize) ( \
+ (*pString != NULL) ? \
+ (*(pSize)) += ((1 + wcslen(*pString))*sizeof(WCHAR)), \
+ STATUS_SUCCESS \
+ : ((*(pSize)) += 2, \
+ STATUS_SUCCESS) \
+ )
+
+#define DfsRtlSizeUnicodeString(pUnicodeString, pSize) \
+ DfsRtlSizeString(pUnicodeString, pSize)
+
+#define DfsRtlUnwindStringGet(s) { \
+ if((s)->Length != 0) \
+ MarshalBufferFree(s); \
+}
+
+#define DfsRtlUnwindpwStringGet(s) { \
+ if(s != NULL) \
+ MarshalBufferFree(s); \
+}
+
+#define DfsRtlUnwindUnicodeStringGet(s) \
+ DfsRtlUnwindStringGet(s)
+
+
+NTSTATUS
+DfsRtlGetArrayUchar(
+ IN OUT PMARSHAL_BUFFER MarshalBuffer,
+ IN ULONG cbArray,
+ OUT PUCHAR pArray)
+{
+ NTSTATUS status = STATUS_SUCCESS;
+
+ DebugTrace(+1, Dbg, "DfsRtlGetArrayUchar: Entered\n", 0);
+
+ ASSERT(ARGUMENT_PRESENT(MarshalBuffer));
+ ASSERT(ARGUMENT_PRESENT(pArray));
+
+ try {
+ UNREFERENCED_LABEL(try_exit);
+ if (MarshalBuffer->Current + cbArray <= MarshalBuffer->Last) {
+ memcpy(pArray, MarshalBuffer->Current, cbArray);
+ MarshalBuffer->Current += cbArray;
+ } else {
+ status = STATUS_DATA_ERROR;
+ }
+ try_exit: NOTHING;
+ } finally {
+ if (AbnormalTermination()) {
+ DebugTrace(0, Dbg,
+ "DfsRtlGetArrayUchar - Abnormal termination!\n", 0);
+ status = STATUS_DATA_ERROR;
+ }
+
+ }
+
+ DebugTrace(-1, Dbg, "DfsRtlGetArrayUchar: Exited %08lx\n", status);
+ return(status);
+}
+
+NTSTATUS
+DfsRtlPutArrayUchar(
+ IN OUT PMARSHAL_BUFFER MarshalBuffer,
+ IN ULONG cbArray,
+ OUT PUCHAR pArray)
+{
+ NTSTATUS status = STATUS_SUCCESS;
+
+ DebugTrace(+1, Dbg, "DfsRtlGetArrayUchar: Entered\n", 0);
+
+ ASSERT(ARGUMENT_PRESENT(MarshalBuffer));
+ ASSERT(ARGUMENT_PRESENT(pArray));
+
+ try {
+ UNREFERENCED_LABEL(try_exit);
+ if (MarshalBuffer->Current + cbArray <= MarshalBuffer->Last) {
+ if (cbArray) {
+ memcpy(MarshalBuffer->Current, pArray, cbArray);
+ MarshalBuffer->Current += cbArray;
+ }
+ } else {
+ status = STATUS_BUFFER_TOO_SMALL;
+ }
+ try_exit: NOTHING;
+ } finally {
+ if (AbnormalTermination()) {
+ DebugTrace(0, Dbg,
+ "DfsRtlPutArrayUchar - Abnormal termination!\n", 0);
+ status = STATUS_DATA_ERROR;
+ }
+
+ }
+
+ DebugTrace(-1, Dbg, "DfsRtlPutArrayUchar: Exited %08lx\n", status);
+ return(status);
+}
+
+
+NTSTATUS
+DfsRtlGetpwString(
+ IN OUT PMARSHAL_BUFFER MarshalBuffer,
+ OUT PWSTR *ppwszString
+)
+{
+ USHORT size;
+ PCHAR cp = NULL;
+ NTSTATUS status = STATUS_SUCCESS;
+
+ DebugTrace(+1, Dbg, "DfsRtlGetpwString: Entered\n", 0);
+
+ ASSERT(ARGUMENT_PRESENT(MarshalBuffer));
+ ASSERT(ARGUMENT_PRESENT(ppwszString));
+
+ try {
+ UNREFERENCED_LABEL(try_exit);
+ *ppwszString = NULL;
+ if(NT_SUCCESS(status = DfsRtlGetUshort(MarshalBuffer, &size))) {
+ if(size > 0) {
+ if(MarshalBuffer->Current + size <= MarshalBuffer->Last) {
+ if((cp = MarshalBufferAllocate(size+sizeof(WCHAR))) != NULL) {
+ memcpy(cp, MarshalBuffer->Current, size);
+ *((WCHAR *) (cp + size)) = UNICODE_NULL;
+ *ppwszString = (PWCHAR) cp;
+ MarshalBuffer->Current += size;
+ } else
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ } else
+ status = STATUS_DATA_ERROR;
+ }
+ }
+ try_exit: NOTHING;
+ } finally {
+ if(AbnormalTermination()) {
+ DebugTrace(0, Dbg,
+ "DfsRtlGetpwString: Abnormal Termination!\n", 0);
+ status = STATUS_DATA_ERROR;
+ }
+
+ if(!NT_SUCCESS(status) && cp)
+ MarshalBufferFree(cp);
+ }
+ DebugTrace(-1, Dbg, "DfsRtlGetpwString: Exit -> %08lx\n",status);
+ return status;
+}
+
+
+NTSTATUS
+DfsRtlPutpwString(
+ IN OUT PMARSHAL_BUFFER MarshalBuffer,
+ OUT PWSTR *ppwszString
+)
+{
+ USHORT size;
+ NTSTATUS status = STATUS_SUCCESS;
+
+ DebugTrace(+1, Dbg, "DfsRtlPutpwString: Entered\n", 0);
+
+ ASSERT(ARGUMENT_PRESENT(MarshalBuffer));
+ ASSERT(ARGUMENT_PRESENT(ppwszString));
+
+
+ try {
+ UNREFERENCED_LABEL(try_exit);
+ if (*ppwszString != NULL)
+ size = wcslen(*ppwszString)*sizeof(WCHAR);
+ else
+ size = 0;
+ if(NT_SUCCESS(status = DfsRtlPutUshort(MarshalBuffer, &size))) {
+ if(size > 0) {
+ if(MarshalBuffer->Current + size <= MarshalBuffer->Last) {
+ memcpy(MarshalBuffer->Current, *ppwszString, size);
+ MarshalBuffer->Current += size;
+ } else
+ status = STATUS_BUFFER_TOO_SMALL;
+ }
+ }
+ try_exit: NOTHING;
+ } finally {
+ if(AbnormalTermination()) {
+ DebugTrace(0, Dbg,
+ "DfsRtlPutString: Abnormal Termination!\n", 0);
+ status = STATUS_DATA_ERROR;
+ }
+ }
+ DebugTrace(-1, Dbg, "DfsRtlPutString: Exit -> %08lx\n",status);
+ return status;
+}
+
+
+
+NTSTATUS
+DfsRtlGetString(
+ IN OUT PMARSHAL_BUFFER MarshalBuffer,
+ OUT PSTRING String
+)
+{
+ USHORT size;
+ PCHAR cp = NULL;
+ NTSTATUS status = STATUS_SUCCESS;
+
+ DebugTrace(+1, Dbg, "DfsRtlGetString: Entered\n", 0);
+
+ ASSERT(ARGUMENT_PRESENT(MarshalBuffer));
+ ASSERT(ARGUMENT_PRESENT(String));
+
+ try {
+ UNREFERENCED_LABEL(try_exit);
+ String->Length = String->MaximumLength = 0;
+ String->Buffer = NULL;
+ if(NT_SUCCESS(status = DfsRtlGetUshort(MarshalBuffer, &size))) {
+ if(size > 0) {
+ if(MarshalBuffer->Current + size <= MarshalBuffer->Last) {
+ if((cp = MarshalBufferAllocate(size+sizeof(WCHAR))) != NULL) {
+ memcpy(cp, MarshalBuffer->Current, size);
+ *((WCHAR *) (cp + size)) = UNICODE_NULL;
+ String->Length = size;
+ String->MaximumLength = size + sizeof(WCHAR);
+ String->Buffer = cp;
+ MarshalBuffer->Current += size;
+ } else
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ } else
+ status = STATUS_DATA_ERROR;
+ }
+ }
+ try_exit: NOTHING;
+ } finally {
+ if(AbnormalTermination()) {
+ DebugTrace(0, Dbg,
+ "DfsRtlGetString: Abnormal Termination!\n", 0);
+ status = STATUS_DATA_ERROR;
+ }
+
+ if(!NT_SUCCESS(status) && cp)
+ MarshalBufferFree(cp);
+ }
+ DebugTrace(-1, Dbg, "DfsRtlGetString: Exit -> %08lx\n",status);
+ return status;
+}
+
+
+
+NTSTATUS
+DfsRtlPutString(
+ IN OUT PMARSHAL_BUFFER MarshalBuffer,
+ OUT PSTRING String
+)
+{
+ USHORT size;
+ NTSTATUS status = STATUS_SUCCESS;
+
+ DebugTrace(+1, Dbg, "DfsRtlPutString: Entered\n", 0);
+
+ ASSERT(ARGUMENT_PRESENT(MarshalBuffer));
+ ASSERT(ARGUMENT_PRESENT(String));
+
+
+ try {
+ UNREFERENCED_LABEL(try_exit);
+ size = String->Length;
+ if(NT_SUCCESS(status = DfsRtlPutUshort(MarshalBuffer, &size))) {
+ if(size > 0) {
+ if(MarshalBuffer->Current + size <= MarshalBuffer->Last) {
+ if(String->Buffer != NULL) {
+ memcpy(MarshalBuffer->Current, String->Buffer, size);
+ MarshalBuffer->Current += size;
+ } else
+ status = STATUS_DATA_ERROR;
+ } else
+ status = STATUS_BUFFER_TOO_SMALL;
+ }
+ }
+ try_exit: NOTHING;
+ } finally {
+ if(AbnormalTermination()) {
+ DebugTrace(0, Dbg,
+ "DfsRtlPutString: Abnormal Termination!\n", 0);
+ status = STATUS_DATA_ERROR;
+ }
+ }
+ DebugTrace(-1, Dbg, "DfsRtlPutString: Exit -> %08lx\n",status);
+ return status;
+}
+
+
+
+#ifdef NOT_NEEDED
+
+NTSTATUS
+DfsRtlGetUnicodeString(
+ IN OUT PMARSHAL_BUFFER MarshalBuffer,
+ OUT PUNICODE_STRING UnicodeString
+)
+{
+ USHORT size;
+ PWCHAR wcp = NULL;
+ NTSTATUS status = STATUS_SUCCESS;
+
+ DebugTrace(+1, Dbg, "DfsRtlGetUnicodeString: Entered\n", 0);
+
+ ASSERT(ARGUMENT_PRESENT(MarshalBuffer));
+ ASSERT(ARGUMENT_PRESENT(UnicodeString));
+
+ try {
+ UnicodeString->Length = UnicodeString->MaximumLength = 0;
+ UnicodeString->Buffer = NULL;
+ if(NT_SUCCESS(status = DfsRtlGetUshort(MarshalBuffer, &size))) {
+ if(size > 0) {
+ if(MarshalBuffer->Current + size <= MarshalBuffer->Last) {
+ if((wcp = (MarshalBufferAllocate)(size+sizeof(WCHAR))) != NULL) {
+ memcpy(wcp, MarshalBuffer->Current, size);
+ wcp[size/sizeof(WCHAR)] = UNICODE_NULL;
+ UnicodeString->Length = size;
+ UnicodeString->MaximumLength = size + sizeof(WCHAR);
+ UnicodeString->Buffer = wcp;
+ MarshalBuffer->Current += size;
+ } else
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ } else
+ status = STATUS_DATA_ERROR;
+ }
+ }
+ try_exit: NOTHING;
+ } finally {
+ if(AbnormalTermination()) {
+ DebugTrace(0, Dbg,
+ "DfsRtlGetUnicodeString: Abnormal Termination!\n", 0);
+ status = STATUS_DATA_ERROR;
+ }
+
+ if(!NT_SUCCESS(status) && wcp)
+ MarshalBufferFree(wcp);
+ }
+ DebugTrace(-1, Dbg, "DfsRtlGetUnicodeString: Exit -> %08lx\n",
+ status);
+ return status;
+}
+
+
+
+NTSTATUS
+DfsRtlPutUnicodeString(
+ IN OUT PMARSHAL_BUFFER MarshalBuffer,
+ OUT PUNICODE_STRING UnicodeString
+)
+{
+ USHORT size;
+ NTSTATUS status = STATUS_SUCCESS;
+
+ DebugTrace(+1, Dbg, "DfsRtlPutUnicodeString: Entered\n", 0);
+
+ ASSERT(ARGUMENT_PRESENT(MarshalBuffer));
+ ASSERT(ARGUMENT_PRESENT(UnicodeString));
+
+ try {
+ size = UnicodeString->Length;
+ if(NT_SUCCESS(status = DfsRtlPutUshort(MarshalBuffer, &size))) {
+ if(size > 0) {
+ if(MarshalBuffer->Current + size <= MarshalBuffer->Last) {
+ if(UnicodeString->Buffer != NULL) {
+ memcpy(
+ MarshalBuffer->Current,
+ UnicodeString->Buffer,
+ size
+ );
+ MarshalBuffer->Current += size;
+ } else
+ status = STATUS_DATA_ERROR;
+ } else
+ status = STATUS_BUFFER_TOO_SMALL;
+ }
+ }
+ try_exit: NOTHING;
+ } finally {
+ if(AbnormalTermination()) {
+ DebugTrace(0, Dbg,
+ "DfsRtlPutUnicodeString: Abnormal Termination!\n", 0);
+ status = STATUS_DATA_ERROR;
+ }
+ }
+ DebugTrace(-1, Dbg, "DfsRtlPutUnicodeString: Exit -> %08lx\n",
+ status);
+ return status;
+}
+
+#else // NOT_NEEDED
+
+#define DfsRtlGetUnicodeString(b, s)\
+ DfsRtlGetString(b, (PSTRING)(s))
+
+#define DfsRtlPutUnicodeString(b, s)\
+ DfsRtlPutString(b, (PSTRING)(s))
+
+#endif // NOT_NEEDED
+
+
+
+
+
+NTSTATUS
+DfsRtlGet(
+ IN OUT PMARSHAL_BUFFER MarshalBuffer,
+ IN PMARSHAL_INFO MarshalInfo,
+ OUT PVOID Item
+)
+{
+ PMARSHAL_TYPE_INFO typeInfo;
+ PVOID subItem;
+ PVOID subItemElem;
+ ULONG cnt;
+ ULONG unwindcnt;
+ PUCHAR cntptr;
+ ULONG itemSize;
+ PMARSHAL_INFO subInfo;
+ NTSTATUS status = STATUS_SUCCESS;
+
+ DebugTrace(+1, Dbg, "DfsRtlGet: Entered\n", 0);
+
+ ASSERT(ARGUMENT_PRESENT(MarshalBuffer));
+ ASSERT(ARGUMENT_PRESENT(MarshalInfo));
+ ASSERT(ARGUMENT_PRESENT(Item));
+
+ try {
+
+ RtlZeroMemory(Item, MarshalInfo->_size);
+
+ for(typeInfo = &MarshalInfo->_typeInfo[0];
+ typeInfo < &MarshalInfo->_typeInfo[MarshalInfo->_typecnt];
+ typeInfo++) {
+
+ switch(typeInfo->_type & MTYPE_BASE_TYPE) {
+ case MTYPE_COMPOUND:
+ subInfo = typeInfo->_subinfo;
+ itemSize = subInfo->_size;
+ //
+ // If this compound type is a conformant structure, then
+ // we need to adjust the size field here.
+ //
+ if (subInfo->_typecnt > 0 &&
+ subInfo->_typeInfo[0]._type == MTYPE_CONFORMANT_CNT) {
+ MARSHAL_BUFFER tempMarshalBuffer = *MarshalBuffer;
+ ULONG extraSize = 0;
+
+ DfsRtlGetUlong(&tempMarshalBuffer, &extraSize);
+ itemSize += extraSize;
+ }
+ break;
+ case MTYPE_CONFORMANT_CNT:
+ itemSize = 0;
+ break;
+ case MTYPE_GUID:
+ itemSize = sizeof(GUID);
+ break;
+ case MTYPE_STRING:
+ itemSize = sizeof(STRING);
+ break;
+ case MTYPE_UNICODE_STRING:
+ itemSize = sizeof(UNICODE_STRING);
+ break;
+ case MTYPE_PWSTR:
+ itemSize = sizeof(PWSTR);
+ break;
+ case MTYPE_ULONG:
+ itemSize = sizeof(ULONG);
+ break;
+ case MTYPE_USHORT:
+ itemSize = sizeof(USHORT);
+ break;
+ case MTYPE_UCHAR:
+ itemSize = sizeof(UCHAR);
+ break;
+ default:
+ try_return(status = STATUS_DATA_ERROR);
+ }
+
+ if(typeInfo->_type & MTYPE_COUNTED_ARRAY) {
+ cntptr = ((PUCHAR)Item + typeInfo->_cntoff);
+ switch(typeInfo->_cntsize) {
+ case sizeof(UCHAR):
+ cnt = *(PUCHAR)cntptr;
+ break;
+ case sizeof(USHORT):
+ cnt = *(PUSHORT)cntptr;
+ break;
+ case sizeof(ULONG):
+ cnt = *(PULONG)cntptr;
+ break;
+ default:
+ try_return(status = STATUS_DATA_ERROR);
+ }
+ } else if (typeInfo->_type & MTYPE_STATIC_ARRAY) {
+ cnt = typeInfo->_cntoff;
+ } else {
+ cnt = 1;
+ }
+
+ if(typeInfo->_type & MTYPE_INDIRECT) {
+ if((typeInfo->_type & MTYPE_COUNTED_ARRAY) && cnt == 0)
+ subItem = NULL;
+ else {
+ subItem = MarshalBufferAllocate(itemSize * cnt);
+ if(subItem == NULL)
+ try_return(status = STATUS_INSUFFICIENT_RESOURCES);
+ }
+ *(PVOID *)((PUCHAR)Item + typeInfo->_off) = subItem;
+ }
+ else
+ subItem = ((PUCHAR)Item + typeInfo->_off);
+
+ switch(typeInfo->_type & ~MTYPE_INDIRECT) {
+
+ case MTYPE_COMPOUND:
+ status = DfsRtlGet(
+ MarshalBuffer,
+ subInfo,
+ subItem
+ );
+ break;
+ case MTYPE_CONFORMANT_CNT:
+ //
+ // this field is used only when sizing a conformant
+ // structure. As such, there is no place to unmarshal
+ // it into. So, simply eat the ulong.
+ //
+ status = DfsRtlGetUlong(MarshalBuffer, &itemSize);
+ break;
+ case (MTYPE_COMPOUND|MTYPE_COUNTED_ARRAY):
+ case (MTYPE_COMPOUND|MTYPE_STATIC_ARRAY):
+ subItemElem = (PUCHAR)subItem;
+ unwindcnt = cnt;
+ while(cnt--) {
+ status = DfsRtlGet(
+ MarshalBuffer,
+ subInfo,
+ subItemElem
+ );
+ if(!NT_SUCCESS(status)) {
+ while((++cnt) < unwindcnt) {
+ ((PUCHAR)subItemElem) -= itemSize;
+ DfsRtlUnwindGet(
+ subInfo,
+ &subInfo->_typeInfo[subInfo->_typecnt],
+ subItemElem
+ );
+ }
+ if(typeInfo->_type & MTYPE_INDIRECT)
+ MarshalBufferFree(subItem);
+ break;
+ }
+ ((PUCHAR)subItemElem) += itemSize;
+ }
+ break;
+ case MTYPE_GUID:
+ status = DfsRtlGetGuid(
+ MarshalBuffer,
+ (GUID *)subItem
+ );
+ break;
+ case MTYPE_STRING:
+ status = DfsRtlGetString(
+ MarshalBuffer,
+ (PSTRING)subItem
+ );
+ break;
+ case MTYPE_UNICODE_STRING:
+ status = DfsRtlGetUnicodeString(
+ MarshalBuffer,
+ (PUNICODE_STRING)subItem
+ );
+ break;
+
+ case MTYPE_PWSTR:
+ status = DfsRtlGetpwString(
+ MarshalBuffer,
+ (PWSTR *)subItem
+ );
+ break;
+
+ case (MTYPE_PWSTR|MTYPE_COUNTED_ARRAY):
+ case (MTYPE_PWSTR|MTYPE_STATIC_ARRAY):
+ subItemElem = (PUCHAR)subItem;
+ unwindcnt = cnt;
+ while (cnt--) {
+ status = DfsRtlGetpwString(
+ MarshalBuffer,
+ (PWSTR *)subItemElem);
+ if (!NT_SUCCESS(status)) {
+ while ((+cnt) < unwindcnt) {
+ ((PUCHAR)subItemElem) -= itemSize;
+ MarshalBufferFree((PVOID)*(PWSTR *)subItemElem);
+ }
+ if (typeInfo->_type & MTYPE_INDIRECT) {
+ MarshalBufferFree(subItem);
+ }
+ break;
+ }
+ ((PUCHAR)subItemElem) += itemSize;
+ }
+ break;
+ case (MTYPE_UNICODE_STRING|MTYPE_COUNTED_ARRAY):
+ case (MTYPE_UNICODE_STRING|MTYPE_STATIC_ARRAY):
+ subItemElem = (PUCHAR)subItem;
+ unwindcnt = cnt;
+ while(cnt--) {
+ status = DfsRtlGetUnicodeString(
+ MarshalBuffer,
+ (PUNICODE_STRING)subItemElem
+ );
+ if(!NT_SUCCESS(status)) {
+ while((++cnt) < unwindcnt) {
+ ((PUCHAR)subItemElem) -= itemSize;
+ DfsRtlUnwindUnicodeStringGet(
+ (PUNICODE_STRING)subItemElem
+ );
+ }
+ if(typeInfo->_type & MTYPE_INDIRECT)
+ MarshalBufferFree(subItem);
+ break;
+ }
+ ((PUCHAR)subItemElem) += itemSize;
+ }
+ break;
+ case MTYPE_ULONG:
+ status = DfsRtlGetUlong(
+ MarshalBuffer,
+ (PULONG)subItem
+ );
+ break;
+ case MTYPE_USHORT:
+ status = DfsRtlGetUshort(
+ MarshalBuffer,
+ (PUSHORT)subItem);
+ break;
+ case MTYPE_UCHAR:
+ status = DfsRtlGetUchar(
+ MarshalBuffer,
+ (PUCHAR)subItem);
+ break;
+ case MTYPE_UCHAR|MTYPE_COUNTED_ARRAY:
+ case MTYPE_UCHAR|MTYPE_STATIC_ARRAY:
+ status = DfsRtlGetArrayUchar(
+ MarshalBuffer,
+ cnt,
+ (PUCHAR)subItem);
+ break;
+ default:
+ status = STATUS_DATA_ERROR;
+ break;
+ };
+ if(!NT_SUCCESS(status))
+ break;
+ }
+
+ try_exit: NOTHING;
+ } finally {
+ if(AbnormalTermination()) {
+ DebugTrace(0, Dbg, "DfsRtlGet: Abnormal Termination!\n", 0);
+ status = STATUS_DATA_ERROR;
+ }
+
+ if(!NT_SUCCESS(status))
+ DfsRtlUnwindGet(MarshalInfo, typeInfo, Item);
+ }
+
+ DebugTrace(-1, Dbg, "DfsRtlGet: Exit -> %08lx\n", status);
+ return status;
+}
+
+
+
+NTSTATUS
+DfsRtlPut(
+ IN OUT PMARSHAL_BUFFER MarshalBuffer,
+ IN PMARSHAL_INFO MarshalInfo,
+ OUT PVOID Item
+)
+{
+ PMARSHAL_TYPE_INFO typeInfo;
+ PVOID subItem;
+ PVOID subItemElem;
+ ULONG cnt;
+ PUCHAR cntptr;
+ ULONG itemSize;
+ PMARSHAL_INFO subInfo;
+ NTSTATUS status = STATUS_SUCCESS;
+
+ DebugTrace(+1, Dbg, "DfsRtlPut: Entered\n", 0);
+
+ ASSERT(ARGUMENT_PRESENT(MarshalBuffer));
+ ASSERT(ARGUMENT_PRESENT(MarshalInfo));
+ ASSERT(ARGUMENT_PRESENT(Item));
+
+ try {
+ for(typeInfo = &MarshalInfo->_typeInfo[0];
+ typeInfo < &MarshalInfo->_typeInfo[MarshalInfo->_typecnt];
+ typeInfo++) {
+
+ switch(typeInfo->_type & MTYPE_BASE_TYPE) {
+ case MTYPE_COMPOUND:
+ subInfo = typeInfo->_subinfo;
+ itemSize = subInfo->_size;
+ break;
+ case MTYPE_CONFORMANT_CNT:
+ itemSize = typeInfo->_off;
+ break;
+ case MTYPE_GUID:
+ itemSize = sizeof(GUID);
+ break;
+ case MTYPE_STRING:
+ itemSize = sizeof(STRING);
+ break;
+ case MTYPE_UNICODE_STRING:
+ itemSize = sizeof(UNICODE_STRING);
+ break;
+ case MTYPE_PWSTR:
+ itemSize = sizeof(PWSTR);
+ break;
+ case MTYPE_ULONG:
+ itemSize = sizeof(ULONG);
+ break;
+ case MTYPE_USHORT:
+ itemSize = sizeof(USHORT);
+ break;
+ case MTYPE_UCHAR:
+ itemSize = sizeof(UCHAR);
+ break;
+ default:
+ try_return(status = STATUS_DATA_ERROR);
+ }
+
+ if(typeInfo->_type & MTYPE_COUNTED_ARRAY ||
+ typeInfo->_type == MTYPE_CONFORMANT_CNT) {
+ cntptr = ((PUCHAR)Item + typeInfo->_cntoff);
+ switch(typeInfo->_cntsize) {
+ case sizeof(UCHAR):
+ cnt = *(PUCHAR)cntptr;
+ break;
+ case sizeof(USHORT):
+ cnt = *(PUSHORT)cntptr;
+ break;
+ case sizeof(ULONG):
+ cnt = *(PULONG)cntptr;
+ break;
+ default:
+ try_return(status = STATUS_DATA_ERROR);
+ }
+ } else
+ cnt = typeInfo->_cntoff;
+
+ if(typeInfo->_type & MTYPE_INDIRECT) {
+ subItem = *(PUCHAR *)((PUCHAR)Item + typeInfo->_off);
+ if(subItem == NULL &&
+ !((typeInfo->_type & MTYPE_COUNTED_ARRAY) && cnt == 0))
+ try_return(status = STATUS_DATA_ERROR);
+ } else
+ subItem = ((PUCHAR)Item + typeInfo->_off);
+
+ switch(typeInfo->_type & ~MTYPE_INDIRECT) {
+
+ case MTYPE_COMPOUND:
+ status = DfsRtlPut(
+ MarshalBuffer,
+ subInfo,
+ subItem
+ );
+ break;
+ case MTYPE_CONFORMANT_CNT:
+ cnt *= itemSize;
+ status = DfsRtlPutUlong(
+ MarshalBuffer,
+ &cnt);
+ break;
+ case (MTYPE_COMPOUND|MTYPE_COUNTED_ARRAY):
+ case (MTYPE_COMPOUND|MTYPE_STATIC_ARRAY):
+ //
+ // No sense in having an array of conformant structures
+ // ASSERT this fact
+ //
+
+ ASSERT(subInfo->_typecnt == 0
+ ||
+ subInfo->_typeInfo[0]._type != MTYPE_CONFORMANT_CNT
+ );
+
+ subItemElem = (PUCHAR)subItem;
+ while(cnt--) {
+ status = DfsRtlPut(
+ MarshalBuffer,
+ subInfo,
+ subItemElem
+ );
+ if(!NT_SUCCESS(status))
+ break;
+ ((PUCHAR)subItemElem) += itemSize;
+ }
+ break;
+ case MTYPE_GUID:
+ status = DfsRtlPutGuid(
+ MarshalBuffer,
+ (GUID *)subItem
+ );
+ break;
+ case MTYPE_STRING:
+ status = DfsRtlPutString(
+ MarshalBuffer,
+ (PSTRING)subItem
+ );
+ break;
+ case MTYPE_UNICODE_STRING:
+ status = DfsRtlPutUnicodeString(
+ MarshalBuffer,
+ (PUNICODE_STRING)subItem
+ );
+ break;
+ case MTYPE_PWSTR:
+ status = DfsRtlPutpwString(
+ MarshalBuffer,
+ (PWSTR *)subItem
+ );
+ break;
+
+ case (MTYPE_PWSTR|MTYPE_COUNTED_ARRAY):
+ case (MTYPE_PWSTR|MTYPE_STATIC_ARRAY):
+ subItemElem = (PWSTR *)subItem;
+ while(cnt--) {
+ status = DfsRtlPutpwString(
+ MarshalBuffer,
+ (PWSTR *)subItemElem);
+ if (!NT_SUCCESS(status))
+ break;
+ ((PUCHAR)subItemElem) += itemSize;
+ }
+ break;
+ case (MTYPE_UNICODE_STRING|MTYPE_COUNTED_ARRAY):
+ case (MTYPE_UNICODE_STRING|MTYPE_STATIC_ARRAY):
+ subItemElem = (PUCHAR)subItem;
+ while(cnt--) {
+ status = DfsRtlPutUnicodeString(
+ MarshalBuffer,
+ (PUNICODE_STRING)subItemElem
+ );
+ if(!NT_SUCCESS(status))
+ break;
+ ((PUCHAR)subItemElem) += itemSize;
+ }
+ break;
+ case MTYPE_ULONG:
+ status = DfsRtlPutUlong(
+ MarshalBuffer,
+ (PULONG)subItem
+ );
+ break;
+ case MTYPE_USHORT:
+ status = DfsRtlPutUshort(
+ MarshalBuffer,
+ (PUSHORT)subItem);
+ break;
+ case MTYPE_UCHAR:
+ status = DfsRtlPutUchar(
+ MarshalBuffer,
+ (PUCHAR)subItem);
+ break;
+ case (MTYPE_UCHAR|MTYPE_COUNTED_ARRAY):
+ case (MTYPE_UCHAR|MTYPE_STATIC_ARRAY):
+ status = DfsRtlPutArrayUchar(
+ MarshalBuffer,
+ cnt,
+ (PUCHAR)subItem);
+ break;
+ default:
+ status = STATUS_DATA_ERROR;
+ break;
+ }
+
+ if(!NT_SUCCESS(status))
+ break;
+ }
+
+ try_exit: NOTHING;
+ } finally {
+ if(AbnormalTermination()) {
+ DebugTrace(0, Dbg, "DfsRtlPut: Abnormal Termination!\n", 0);
+ status = STATUS_DATA_ERROR;
+ }
+ }
+
+ DebugTrace(-1, Dbg, "DfsRtlPut: Exit -> %08lx\n", status);
+ return status;
+}
+
+
+
+
+NTSTATUS
+DfsRtlSize(
+ IN PMARSHAL_INFO MarshalInfo,
+ IN PVOID Item,
+ OUT PULONG Size
+)
+{
+ PMARSHAL_TYPE_INFO typeInfo;
+ PVOID subItem;
+ PVOID subItemElem;
+ ULONG cnt;
+ PUCHAR cntptr;
+ ULONG itemSize;
+ PMARSHAL_INFO subInfo;
+ NTSTATUS status = STATUS_SUCCESS;
+
+ DebugTrace(+1, Dbg, "DfsRtlSize: Entered\n", 0);
+
+ ASSERT(ARGUMENT_PRESENT(MarshalInfo));
+ ASSERT(ARGUMENT_PRESENT(Item));
+ ASSERT(ARGUMENT_PRESENT(Size));
+
+ try {
+ for(typeInfo = &MarshalInfo->_typeInfo[0];
+ typeInfo < &MarshalInfo->_typeInfo[MarshalInfo->_typecnt];
+ typeInfo++) {
+
+ switch(typeInfo->_type & MTYPE_BASE_TYPE) {
+ case MTYPE_COMPOUND:
+ subInfo = typeInfo->_subinfo;
+ itemSize = subInfo->_size;
+ break;
+ case MTYPE_CONFORMANT_CNT:
+ //
+ // For conformant structures, _offset is sizeof each
+ // element, _cntsize is sizeof cnt field, and _cntoff is
+ // offset of cnt field.
+ //
+ itemSize = typeInfo->_off;
+ break;
+ case MTYPE_GUID:
+ itemSize = sizeof(GUID);
+ break;
+ case MTYPE_STRING:
+ itemSize = sizeof(STRING);
+ break;
+ case MTYPE_UNICODE_STRING:
+ itemSize = sizeof(UNICODE_STRING);
+ break;
+ case MTYPE_PWSTR:
+ itemSize = sizeof(PWCHAR);
+ break;
+ case MTYPE_ULONG:
+ itemSize = sizeof(ULONG);
+ break;
+ case MTYPE_USHORT:
+ itemSize = sizeof(USHORT);
+ break;
+ case MTYPE_UCHAR:
+ itemSize = sizeof(UCHAR);
+ break;
+ default:
+ try_return(status = STATUS_DATA_ERROR);
+ }
+
+ if(typeInfo->_type & MTYPE_COUNTED_ARRAY ||
+ typeInfo->_type == MTYPE_CONFORMANT_CNT) {
+ cntptr = ((PUCHAR)Item + typeInfo->_cntoff);
+ switch(typeInfo->_cntsize) {
+ case sizeof(UCHAR):
+ cnt = *(PUCHAR)cntptr;
+ break;
+ case sizeof(USHORT):
+ cnt = *(PUSHORT)cntptr;
+ break;
+ case sizeof(ULONG):
+ cnt = *(PULONG)cntptr;
+ break;
+ default:
+ try_return(status = STATUS_DATA_ERROR);
+ }
+ } else
+ cnt = typeInfo->_cntoff;
+
+ if(typeInfo->_type & MTYPE_INDIRECT) {
+ subItem = *(PUCHAR *)((PUCHAR)Item + typeInfo->_off);
+ if(subItem == NULL &&
+ !((typeInfo->_type & MTYPE_COUNTED_ARRAY) && cnt == 0))
+ try_return(status = STATUS_DATA_ERROR);
+ } else
+ subItem = ((PUCHAR)Item + typeInfo->_off);
+
+ switch(typeInfo->_type & ~MTYPE_INDIRECT) {
+
+ case MTYPE_COMPOUND:
+ status = DfsRtlSize(
+ subInfo,
+ subItem,
+ Size
+ );
+ break;
+ case MTYPE_CONFORMANT_CNT:
+ (*Size) += sizeof(ULONG);
+ break;
+ case (MTYPE_COMPOUND|MTYPE_COUNTED_ARRAY):
+ case (MTYPE_COMPOUND|MTYPE_STATIC_ARRAY):
+ //
+ // No sense in having an array of conformant structures
+ // ASSERT this fact
+ //
+
+ ASSERT(subInfo->_typecnt == 0
+ ||
+ subInfo->_typeInfo[0]._type != MTYPE_CONFORMANT_CNT
+ );
+
+ subItemElem = (PUCHAR)subItem;
+ while(cnt--) {
+ status = DfsRtlSize(
+ subInfo,
+ subItemElem,
+ Size
+ );
+ if(!NT_SUCCESS(status))
+ break;
+ ((PUCHAR)subItemElem) += itemSize;
+ }
+ break;
+ case MTYPE_GUID:
+ (*Size) += 16;
+ break;
+ case MTYPE_STRING:
+ status = DfsRtlSizeString(
+ (PSTRING)subItem,
+ Size
+ );
+ break;
+ case MTYPE_UNICODE_STRING:
+ status = DfsRtlSizeUnicodeString(
+ (PUNICODE_STRING)subItem,
+ Size
+ );
+ break;
+
+ case MTYPE_PWSTR:
+ status = DfsRtlSizepwString(
+ (PWSTR *)subItem,
+ Size
+ );
+ break;
+ case (MTYPE_PWSTR|MTYPE_COUNTED_ARRAY):
+ case (MTYPE_PWSTR|MTYPE_STATIC_ARRAY):
+ subItemElem = (PUCHAR)subItem;
+ while (cnt--) {
+ status = DfsRtlSizepwString(
+ (PWSTR *)subItemElem,
+ Size);
+ if (!NT_SUCCESS(status)) {
+ break;
+ }
+ ((PUCHAR)subItemElem) += itemSize;
+ }
+ break;
+ case (MTYPE_UNICODE_STRING|MTYPE_COUNTED_ARRAY):
+ case (MTYPE_UNICODE_STRING|MTYPE_STATIC_ARRAY):
+ subItemElem = (PUCHAR)subItem;
+ while(cnt--) {
+ status = DfsRtlSizeUnicodeString(
+ (PUNICODE_STRING)subItemElem,
+ Size
+ );
+ if(!NT_SUCCESS(status))
+ break;
+ ((PUCHAR)subItemElem) += itemSize;
+ }
+ break;
+ case MTYPE_ULONG:
+ (*Size) += 4;
+ break;
+ case MTYPE_USHORT:
+ (*Size) += 2;
+ break;
+ case MTYPE_UCHAR:
+ (*Size) += 1;
+ break;
+ case MTYPE_UCHAR|MTYPE_COUNTED_ARRAY:
+ case MTYPE_UCHAR|MTYPE_STATIC_ARRAY:
+ (*Size) += (cnt * sizeof(UCHAR));
+ break;
+ default:
+ status = STATUS_DATA_ERROR;
+ break;
+ };
+ if(!NT_SUCCESS(status))
+ break;
+ }
+
+ try_exit: NOTHING;
+ } finally {
+ if(AbnormalTermination()) {
+ DebugTrace(0, Dbg, "DfsRtlSize: Abnormal Termination!\n", 0);
+ status = STATUS_DATA_ERROR;
+ }
+ }
+
+ DebugTrace(0, Dbg, "DfsRtlSize: (*Size) = %ld\n", (*Size));
+ DebugTrace(-1, Dbg, "DfsRtlSize: Exit -> %08lx\n", status);
+ return status;
+}
+
+
+
+
+VOID
+DfsRtlUnwindGet(
+ IN PMARSHAL_INFO MarshalInfo,
+ IN PMARSHAL_TYPE_INFO LastTypeInfo,
+ IN PVOID Item
+)
+{
+ PMARSHAL_TYPE_INFO typeInfo;
+ PVOID subItem;
+ PVOID subItemElem;
+ ULONG cnt;
+ PUCHAR cntptr;
+ ULONG itemSize;
+ PMARSHAL_INFO subInfo;
+ NTSTATUS status = STATUS_SUCCESS;
+
+ DebugTrace(+1, Dbg, "DfsRtlUnwindGet: Entered\n", 0);
+
+ for(typeInfo = &MarshalInfo->_typeInfo[0];
+ typeInfo < LastTypeInfo;
+ typeInfo++) {
+
+ switch(typeInfo->_type & MTYPE_BASE_TYPE) {
+ case MTYPE_COMPOUND:
+ subInfo = typeInfo->_subinfo;
+ itemSize = subInfo->_size;
+ break;
+ case MTYPE_GUID:
+ itemSize = sizeof(GUID);
+ break;
+ case MTYPE_STRING:
+ itemSize = sizeof(STRING);
+ break;
+ case MTYPE_UNICODE_STRING:
+ itemSize = sizeof(UNICODE_STRING);
+ break;
+ case MTYPE_ULONG:
+ itemSize = sizeof(ULONG);
+ break;
+ case MTYPE_USHORT:
+ itemSize = sizeof(USHORT);
+ break;
+ case MTYPE_UCHAR:
+ itemSize = sizeof(UCHAR);
+ break;
+ default:
+ ExRaiseStatus(STATUS_DATA_ERROR);
+ }
+
+ if(typeInfo->_type & MTYPE_COUNTED_ARRAY) {
+ cntptr = ((PUCHAR)Item + typeInfo->_cntoff);
+ switch(typeInfo->_cntsize) {
+ case sizeof(UCHAR):
+ cnt = *(PUCHAR)cntptr;
+ break;
+ case sizeof(USHORT):
+ cnt = *(PUSHORT)cntptr;
+ break;
+ case sizeof(ULONG):
+ cnt = *(PULONG)cntptr;
+ break;
+ default:
+ ExRaiseStatus(STATUS_DATA_ERROR);
+ }
+ } else
+ cnt = typeInfo->_cntoff;
+
+ if(typeInfo->_type & MTYPE_INDIRECT) {
+ subItem = *(PUCHAR *)((PUCHAR)Item + typeInfo->_off);
+ if(subItem == NULL &&
+ !((typeInfo->_type & MTYPE_COUNTED_ARRAY) && cnt == 0))
+ ExRaiseStatus(STATUS_DATA_ERROR);
+ } else
+ subItem = ((PUCHAR)Item + typeInfo->_off);
+
+ switch(typeInfo->_type & ~MTYPE_INDIRECT) {
+
+ case MTYPE_COMPOUND:
+ DfsRtlUnwindGet(
+ subInfo,
+ &subInfo->_typeInfo[subInfo->_typecnt],
+ subItem
+ );
+ break;
+ case (MTYPE_COMPOUND|MTYPE_COUNTED_ARRAY):
+ case (MTYPE_COMPOUND|MTYPE_STATIC_ARRAY):
+ subItemElem = (PUCHAR)subItem;
+ while(cnt--) {
+ DfsRtlUnwindGet(
+ subInfo,
+ &subInfo->_typeInfo[subInfo->_typecnt],
+ subItemElem
+ );
+ ((PUCHAR)subItemElem) += itemSize;
+ }
+ break;
+ case MTYPE_STRING:
+ DfsRtlUnwindStringGet((PSTRING)subItem);
+ break;
+ case MTYPE_UNICODE_STRING:
+ DfsRtlUnwindUnicodeStringGet((PUNICODE_STRING)subItem);
+ break;
+ case (MTYPE_UNICODE_STRING|MTYPE_COUNTED_ARRAY):
+ case (MTYPE_UNICODE_STRING|MTYPE_STATIC_ARRAY):
+ subItemElem = (PUCHAR)subItem;
+ while(cnt--) {
+ DfsRtlUnwindUnicodeStringGet((PUNICODE_STRING)subItemElem);
+ ((PUCHAR)subItemElem) += itemSize;
+ }
+ break;
+ case MTYPE_GUID:
+ case MTYPE_ULONG:
+ case MTYPE_USHORT:
+ case MTYPE_UCHAR:
+ break;
+ default:
+ ExRaiseStatus(STATUS_DATA_ERROR);
+ };
+
+ if(typeInfo->_type & MTYPE_INDIRECT) {
+ MarshalBufferFree(subItem);
+ *(PUCHAR *)((PUCHAR)Item + typeInfo->_off) = NULL;
+ }
+ }
+ DebugTrace(-1, Dbg, "DfsRtlUnwindGet: Exit -> VOID\n", 0);
+}
+
+
+
diff --git a/private/net/svcdlls/wkssvc/server/dfsmrshl.h b/private/net/svcdlls/wkssvc/server/dfsmrshl.h
new file mode 100644
index 000000000..ca618ea01
--- /dev/null
+++ b/private/net/svcdlls/wkssvc/server/dfsmrshl.h
@@ -0,0 +1,406 @@
+//+----------------------------------------------------------------------------
+//
+// Copyright (C) 1992, Microsoft Corporation
+//
+// File: DfsMrshl.h
+//
+// Contents: Defines for Dfs marshalling routines
+//
+// Classes:
+//
+// Functions:
+//
+// History: March 29, 1994 Milans Created from Peterco's dfsrtl.h
+//
+//-----------------------------------------------------------------------------
+
+#ifndef _DFSMRSHL_
+#define _DFSMRSHL_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stddef.h>
+
+//
+// MARSHALLING AND UNMARSHALLING SUPPORT
+//
+
+#include <stdlib.h>
+#define MarshalBufferAllocate(x) malloc(x)
+#define MarshalBufferFree(x) free(x)
+
+#define DfsAllocate MarshalBufferAllocate
+#define DfsFree MarshalBufferFree
+
+//
+// BUGBUG - we should really pull this from RPC.H
+//
+
+#ifndef GUID_DEFINED
+#define GUID_DEFINED
+typedef struct _GUID
+{
+ unsigned long Data1;
+ unsigned short Data2;
+ unsigned short Data3;
+ unsigned char Data4[8];
+} GUID;
+#endif /* GUID_DEFINED */
+
+
+//
+// Structure used when marshalling and unmarhalling
+//
+typedef struct _MARSHAL_BUFFER {
+
+ PUCHAR First;
+ PUCHAR Current;
+ PUCHAR Last;
+
+} MARSHAL_BUFFER, *PMARSHAL_BUFFER;
+
+
+typedef struct _MARSHAL_TYPE_INFO {
+
+ ULONG _type; // the type of item to be marshalled
+ ULONG _off; // offset of item (in the struct)
+ ULONG _cntsize; // size of counter for counted array
+ ULONG _cntoff; // else, offset count item (in the struct)
+ struct _MARSHAL_INFO * _subinfo;// if compound type, need info
+
+} MARSHAL_TYPE_INFO, *PMARSHAL_TYPE_INFO;
+
+
+typedef struct _MARSHAL_INFO {
+
+ ULONG _size; // size of item
+ ULONG _typecnt; // number of type infos
+ PMARSHAL_TYPE_INFO _typeInfo; // type infos
+
+} MARSHAL_INFO, *PMARSHAL_INFO;
+
+#define _mkMarshalInfo(s, i)\
+ {(ULONG)sizeof(s),(ULONG)(sizeof(i)/sizeof(MARSHAL_TYPE_INFO)),i}
+
+
+#define MTYPE_BASE_TYPE (0x0000ffffL)
+
+#define MTYPE_COMPOUND (0x00000001L)
+#define MTYPE_GUID (0x00000002L)
+#define MTYPE_STRING (0x00000003L)
+#define MTYPE_UNICODE_STRING (0x00000004L)
+#define MTYPE_ULONG (0x00000005L)
+#define MTYPE_USHORT (0x00000006L)
+#define MTYPE_PWSTR (0x00000007L)
+#define MTYPE_UCHAR (0x00000008L)
+#define MTYPE_CONFORMANT_CNT (0x00000009L)
+
+#define MTYPE_INDIRECT (0x80000000L)
+
+#define MTYPE_COMPLEX_TYPE (0x7fff0000L)
+
+#define MTYPE_STATIC_ARRAY (0x00010000L)
+#define MTYPE_COUNTED_ARRAY (0x00020000L)
+
+
+#define _MCode_conformant(s,m,c)\
+ {MTYPE_CONFORMANT_CNT, sizeof(((s *) 0)->m[0]), sizeof(((s *) 0)->c), offsetof(s,c), 0L}
+
+#define _MCode_Base(t,s,m,i)\
+ {t,offsetof(s,m),0L,0L,i}
+
+#define _MCode_struct(s,m,i)\
+ _MCode_Base(MTYPE_COMPOUND,s,m,i)
+#define _MCode_guid(s,m)\
+ _MCode_Base(MTYPE_GUID,s,m,NULL)
+#define _MCode_str(s,m)\
+ _MCode_Base(MTYPE_STRING,s,m,NULL)
+#define _MCode_ustr(s,m)\
+ _MCode_Base(MTYPE_UNICODE_STRING,s,m,NULL)
+#define _MCode_pwstr(s,m)\
+ _MCode_Base(MTYPE_PWSTR,s,m,NULL)
+#define _MCode_ul(s,m)\
+ _MCode_Base(MTYPE_ULONG,s,m,NULL)
+#define _MCode_ush(s,m)\
+ _MCode_Base(MTYPE_USHORT,s,m,NULL)
+#define _MCode_uch(s,m)\
+ _MCode_Base(MTYPE_UCHAR,s,m,NULL)
+
+#define _MCode_pstruct(s,m,i)\
+ _MCode_Base(MTYPE_COMPOUND|MTYPE_INDIRECT,s,m,i)
+#define _MCode_pguid(s,m)\
+ _MCode_Base(MTYPE_GUID|MTYPE_INDIRECT,s,m,NULL)
+#define _MCode_pstr(s,m)\
+ _MCode_Base(MTYPE_STRING|MTYPE_INDIRECT,s,m,NULL)
+#define _MCode_pustr(s,m)\
+ _MCode_Base(MTYPE_UNICODE_STRING|MTYPE_INDIRECT,s,m,NULL)
+#define _MCode_pul(s,m)\
+ _MCode_Base(MTYPE_ULONG|MTYPE_INDIRECT,s,m,NULL)
+#define _MCode_push(s,m)\
+ _MCode_Base(MTYPE_USHORT|MTYPE_INDIRECT,s,m,NULL)
+#define _MCode_puch(s,m)\
+ _MCode_Base(MTYPE_UCHAR|MTYPE_INDIRECT,s,m,NULL)
+
+#define _MCode_aStatic(t,s,m,c,i)\
+ {t|MTYPE_STATIC_ARRAY,offsetof(s,m),0L,c,i}
+
+#define _MCode_astruct(s,m,c,i)\
+ _MCode_aStatic(MTYPE_COMPOUND,s,m,c,i)
+#define _MCode_aguid(s,m,c)\
+ _MCode_aStatic(MTYPE_GUID,s,m,c,NULL)
+#define _MCode_astr(s,m,c)\
+ _MCode_aStatic(MTYPE_STRING,s,m,c,NULL)
+#define _MCode_austr(s,m,c)\
+ _MCode_aStatic(MTYPE_UNICODE_STRING,s,m,c,NULL)
+#define _MCode_aul(s,m,c)\
+ _MCode_aStatic(MTYPE_ULONG,s,m,c,NULL)
+#define _MCode_aush(s,m,c)\
+ _MCode_aStatic(MTYPE_USHORT,s,m,c,NULL)
+#define _MCode_auch(s,m,c)\
+ _MCode_aStatic(MTYPE_UCHAR,s,m,c,NULL)
+
+#define _MCode_pastruct(s,m,c,i)\
+ _MCode_aStatic(MTYPE_COMPOUND|MTYPE_INDIRECT,s,m,c,i)
+#define _MCode_paguid(s,m,c)\
+ _MCode_aStatic(MTYPE_GUID|MTYPE_INDIRECT,s,m,c,NULL)
+#define _MCode_pastr(s,m,c)\
+ _MCode_aStatic(MTYPE_STRING|MTYPE_INDIRECT,s,m,c,NULL)
+#define _MCode_paustr(s,m,c)\
+ _MCode_aStatic(MTYPE_UNICODE_STRING|MTYPE_INDIRECT,s,m,c,NULL)
+#define _MCode_paul(s,m,c)\
+ _MCode_aStatic(MTYPE_ULONG|MTYPE_INDIRECT,s,m,c,NULL)
+#define _MCode_paush(s,m,c)\
+ _MCode_aStatic(MTYPE_USHORT|MTYPE_INDIRECT,s,m,c,NULL)
+#define _MCode_pauch(s,m,c)\
+ _MCode_aStatic(MTYPE_UCHAR|MTYPE_INDIRECT,s,m,c,NULL)
+
+#define _MCode_aCounted(t,s,m,c,i) {\
+ t|MTYPE_COUNTED_ARRAY,\
+ offsetof(s,m),\
+ sizeof(((s *)0)->c),\
+ offsetof(s,c),\
+ i\
+ }
+
+#define _MCode_castruct(s,m,c,i)\
+ _MCode_aCounted(MTYPE_COMPOUND,s,m,c,i)
+#define _MCode_caguid(s,m,c)\
+ _MCode_aCounted(MTYPE_GUID,s,m,c,NULL)
+#define _MCode_capwstr(s,m,c)\
+ _MCode_aCounted(MTYPE_PWSTR,s,m,c,NULL)
+#define _MCode_castr(s,m,c)\
+ _MCode_aCounted(MTYPE_STRING,s,m,c,NULL)
+#define _MCode_caustr(s,m,c)\
+ _MCode_aCounted(MTYPE_UNICODE_STRING,s,m,c,NULL)
+#define _MCode_caul(s,m,c)\
+ _MCode_aCounted(MTYPE_ULONG,s,m,c,NULL)
+#define _MCode_caush(s,m,c)\
+ _MCode_aCounted(MTYPE_USHORT,s,m,c,NULL)
+#define _MCode_cauch(s,m,c)\
+ _MCode_aCounted(MTYPE_UCHAR,s,m,c,NULL)
+
+#define _MCode_pcastruct(s,m,c,i)\
+ _MCode_aCounted(MTYPE_COMPOUND|MTYPE_INDIRECT,s,m,c,i)
+#define _MCode_pcaguid(s,m,c)\
+ _MCode_aCounted(MTYPE_GUID|MTYPE_INDIRECT,s,m,c,NULL)
+#define _MCode_pcapwstr(s,m,c)\
+ _MCode_aCounted(MTYPE_PWSTR|MTYPE_INDIRECT,s,m,c,NULL)
+#define _MCode_pcastr(s,m,c)\
+ _MCode_aCounted(MTYPE_STRING|MTYPE_INDIRECT,s,m,c,NULL)
+#define _MCode_pcaustr(s,m,c)\
+ _MCode_aCounted(MTYPE_UNICODE_STRING|MTYPE_INDIRECT,s,m,c,NULL)
+#define _MCode_pcaul(s,m,c)\
+ _MCode_aCounted(MTYPE_ULONG|MTYPE_INDIRECT,s,m,c,NULL)
+#define _MCode_pcaush(s,m,c)\
+ _MCode_aCounted(MTYPE_USHORT|MTYPE_INDIRECT,s,m,c,NULL)
+
+
+
+#define MarshalBufferInitialize( MarshalBuffer, BufferLength, Buffer ) {\
+ (MarshalBuffer)->First = (PUCHAR)(Buffer); \
+ (MarshalBuffer)->Current = (PUCHAR)(Buffer); \
+ (MarshalBuffer)->Last = &(MarshalBuffer)->Current[(BufferLength)]; \
+ }
+
+
+//
+// Defines and functions for unmarshalling base types.
+// The BYTE masks are perfectly fine and they dont care if
+// we are on working on LITTLE_ENDIAN or BIG_ENDIAN etc.
+//
+
+#define BYTE_0_MASK 0xFF
+
+#define BYTE_0(Value) (UCHAR)( (Value) & BYTE_0_MASK)
+#define BYTE_1(Value) (UCHAR)( ((Value) >> 8) & BYTE_0_MASK)
+#define BYTE_2(Value) (UCHAR)( ((Value) >> 16) & BYTE_0_MASK)
+#define BYTE_3(Value) (UCHAR)( ((Value) >> 24) & BYTE_0_MASK)
+
+
+#define DfsRtlGetUshort(MarshalBuffer, pValue) ( \
+ ((MarshalBuffer)->Current + 2 <= (MarshalBuffer)->Last) ? \
+ *(pValue) = (USHORT)((MarshalBuffer)->Current[0] ) | \
+ ((MarshalBuffer)->Current[1] << 8), \
+ (MarshalBuffer)->Current += 2, \
+ STATUS_SUCCESS \
+ : STATUS_DATA_ERROR \
+ )
+
+#define DfsRtlPutUshort(MarshalBuffer, pValue) ( \
+ ((MarshalBuffer)->Current + 2 <= (MarshalBuffer)->Last) ? \
+ (MarshalBuffer)->Current[0] = BYTE_0(*pValue), \
+ (MarshalBuffer)->Current[1] = BYTE_1(*pValue), \
+ (MarshalBuffer)->Current += 2, \
+ STATUS_SUCCESS \
+ : STATUS_BUFFER_TOO_SMALL \
+ )
+
+#define DfsRtlGetUlong(MarshalBuffer, pValue) ( \
+ ((MarshalBuffer)->Current + 4 <= (MarshalBuffer)->Last) ? \
+ *(pValue) = (ULONG) ((MarshalBuffer)->Current[0] ) | \
+ ((MarshalBuffer)->Current[1] << 8) | \
+ ((MarshalBuffer)->Current[2] << 16) | \
+ ((MarshalBuffer)->Current[3] << 24), \
+ (MarshalBuffer)->Current += 4, \
+ STATUS_SUCCESS \
+ : STATUS_DATA_ERROR \
+ )
+
+#define DfsRtlPutUlong(MarshalBuffer, pValue) ( \
+ ((MarshalBuffer)->Current + 4 <= (MarshalBuffer)->Last) ? \
+ (MarshalBuffer)->Current[0] = BYTE_0(*pValue), \
+ (MarshalBuffer)->Current[1] = BYTE_1(*pValue), \
+ (MarshalBuffer)->Current[2] = BYTE_2(*pValue), \
+ (MarshalBuffer)->Current[3] = BYTE_3(*pValue), \
+ (MarshalBuffer)->Current += 4, \
+ STATUS_SUCCESS \
+ : STATUS_BUFFER_TOO_SMALL \
+ )
+
+#define DfsRtlGetGuid(MarshalBuffer, pValue) ( \
+ ((MarshalBuffer)->Current + 16 <= (MarshalBuffer)->Last) ? \
+ (pValue)->Data1 = (ULONG) ((MarshalBuffer)->Current[0] ) | \
+ ((MarshalBuffer)->Current[1] << 8) | \
+ ((MarshalBuffer)->Current[2] << 16) | \
+ ((MarshalBuffer)->Current[3] << 24) , \
+ (pValue)->Data2 = (USHORT)((MarshalBuffer)->Current[4] ) | \
+ ((MarshalBuffer)->Current[5] << 8) , \
+ (pValue)->Data3 = (USHORT)((MarshalBuffer)->Current[6] ) | \
+ ((MarshalBuffer)->Current[7] << 8) , \
+ memcpy((pValue)->Data4, &(MarshalBuffer)->Current[8], 8), \
+ (MarshalBuffer)->Current += 16, \
+ STATUS_SUCCESS \
+ : STATUS_DATA_ERROR \
+ )
+
+
+//
+// This routine is being used for DFS_UPD_REFERRAL_BUFFER. These
+// routines will continue to be used in the future as well since
+// we want to keep the structure of DFS_UPD_REFERRAL_BUFFER well
+// defined rather than using the Marshalling routines provided here.
+//
+#define _PutGuid(cp, pguid) \
+ cp[0] = BYTE_0((pguid)->Data1), \
+ cp[1] = BYTE_1((pguid)->Data1), \
+ cp[2] = BYTE_2((pguid)->Data1), \
+ cp[3] = BYTE_3((pguid)->Data1), \
+ cp[4] = BYTE_0((pguid)->Data2), \
+ cp[5] = BYTE_1((pguid)->Data2), \
+ cp[6] = BYTE_0((pguid)->Data3), \
+ cp[7] = BYTE_1((pguid)->Data3), \
+ memcpy(&cp[8], (pguid)->Data4, 8)
+
+
+#define _PutULong(cp, ularg) \
+ cp[0] = BYTE_0(ularg), \
+ cp[1] = BYTE_1(ularg), \
+ cp[2] = BYTE_2(ularg), \
+ cp[3] = BYTE_3(ularg)
+
+
+#define _GetULong(cp, ularg) \
+ ularg = (ULONG) (cp[0]) | \
+ (cp[1] << 8) | \
+ (cp[2] << 16) | \
+ (cp[3] << 24)
+
+
+#define DfsRtlPutGuid(MarshalBuffer, pValue) ( \
+ ((MarshalBuffer)->Current + 16 <= (MarshalBuffer)->Last) ? \
+ (MarshalBuffer)->Current[0] = BYTE_0((pValue)->Data1), \
+ (MarshalBuffer)->Current[1] = BYTE_1((pValue)->Data1), \
+ (MarshalBuffer)->Current[2] = BYTE_2((pValue)->Data1), \
+ (MarshalBuffer)->Current[3] = BYTE_3((pValue)->Data1), \
+ (MarshalBuffer)->Current[4] = BYTE_0((pValue)->Data2), \
+ (MarshalBuffer)->Current[5] = BYTE_1((pValue)->Data2), \
+ (MarshalBuffer)->Current[6] = BYTE_0((pValue)->Data3), \
+ (MarshalBuffer)->Current[7] = BYTE_1((pValue)->Data3), \
+ memcpy(&(MarshalBuffer)->Current[8], (pValue)->Data4, 8), \
+ (MarshalBuffer)->Current += 16, \
+ STATUS_SUCCESS \
+ : STATUS_BUFFER_TOO_SMALL \
+ )
+
+
+
+#define DfsRtlSizeString(pString, pSize) ( \
+ ((pString)->Length > 0) ? ( \
+ ((pString)->Buffer != NULL) ? \
+ (*(pSize)) += (2 + (pString)->Length), \
+ STATUS_SUCCESS \
+ : STATUS_DATA_ERROR \
+ ) \
+ : ((*(pSize)) += 2, \
+ STATUS_SUCCESS) \
+ )
+
+#define DfsRtlSizepwString(pString, pSize) ( \
+ (*pString != NULL) ? \
+ (*(pSize)) += ((1 + wcslen(*pString))*sizeof(WCHAR)), \
+ STATUS_SUCCESS \
+ : ((*(pSize)) += 2, \
+ STATUS_SUCCESS) \
+ )
+
+#define DfsRtlSizeUnicodeString(pUnicodeString, pSize) \
+ DfsRtlSizeString(pUnicodeString, pSize)
+
+
+NTSTATUS
+DfsRtlGet(
+ IN OUT PMARSHAL_BUFFER MarshalBuffer,
+ IN PMARSHAL_INFO MarshalInfo,
+ OUT PVOID Item
+);
+
+
+NTSTATUS
+DfsRtlPut(
+ IN OUT PMARSHAL_BUFFER MarshalBuffer,
+ IN PMARSHAL_INFO MarshalInfo,
+ OUT PVOID Item
+);
+
+
+NTSTATUS
+DfsRtlSize(
+ IN PMARSHAL_INFO MarshalInfo,
+ IN PVOID Item,
+ OUT PULONG Size
+);
+
+VOID
+DfsRtlUnwindGet(
+ IN PMARSHAL_INFO MarshalInfo,
+ IN PMARSHAL_TYPE_INFO LastTypeInfo,
+ IN PVOID Item
+);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _DFSMRSHL_
diff --git a/private/net/svcdlls/wkssvc/server/dirs b/private/net/svcdlls/wkssvc/server/dirs
new file mode 100644
index 000000000..d6f158a31
--- /dev/null
+++ b/private/net/svcdlls/wkssvc/server/dirs
@@ -0,0 +1,32 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ dirs.
+
+Abstract:
+
+ This file specifies the subdirectories of the current directory that
+ contain component makefiles.
+
+
+Author:
+
+ Steve Wood (stevewo) 17-Apr-1990
+
+NOTE: Commented description of this file is in \nt\bak\bin\dirs.tpl
+
+!ENDIF
+
+#
+# The real important directory
+
+DIRS=daytona
+
+#
+# The future cool directory
+#
+
+OPTIONAL_DIRS=
diff --git a/private/net/svcdlls/wkssvc/server/dominfo.c b/private/net/svcdlls/wkssvc/server/dominfo.c
new file mode 100644
index 000000000..ae07024c5
--- /dev/null
+++ b/private/net/svcdlls/wkssvc/server/dominfo.c
@@ -0,0 +1,1628 @@
+//+----------------------------------------------------------------------------
+//
+// Copyright (C) 1996, Microsoft Corporation
+//
+// File: dominfo.h
+//
+// Contents: Code to figure out domain dfs addresses
+//
+// Classes: None
+//
+// Functions: DfsInitDomainList
+// DfsGetDomainReferral
+//
+// DfspInitDomainListFromRegistry
+// DfspInitDomainListFromLSA
+// DfspInsertLsaDomainList
+// DfspIsThisADomainName
+// DfspGetDomainDCs
+// DfspArrayFromServerName
+// DfspDuplicateArray
+// DfspSetupNullSession
+// DfspGetDomainGluon
+// DfspGetDSMachine
+// DfspFreeDSGluon
+// DfspSetDomainInfo
+//
+// History: Feb 7, 1996 Milans created
+//
+//-----------------------------------------------------------------------------
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <ntlsa.h> // LsaEnumerateTrustedDomains
+#include <dfsfsctl.h>
+#include <tdi.h>
+#include <gluon.h>
+#include <windows.h>
+
+#include <lm.h> // NetGetAnyDC, NetUseAdd
+#include <netlogon.h> // Needed by logonp.h
+#include <logonp.h> // I_NetGetDCList
+
+#include "dfsmrshl.h"
+#include "dfsgluon.h"
+#include "dominfo.h"
+
+//
+// Registry key and value name for retrieving list of trusted domain names
+//
+
+#define REG_KEY_TRUSTED_DOMAINS L"SYSTEM\\CurrentControlSet\\Services\\NetLogon\\Parameters"
+#define REG_VALUE_TRUSTED_DOMAINS L"TrustedDomainList"
+
+//
+// The share to connect with to get a referral
+//
+
+#define ROOT_SHARE_NAME L"\\IPC$"
+
+//
+// Commonly used strings and characters
+//
+
+#define UNICODE_PATH_SEP_STR L"\\"
+#define UNICODE_PATH_SEP L'\\'
+
+//
+// Global structure describing list of trusted domains
+//
+
+static struct {
+ ULONG cDomains;
+ UNICODE_STRING ustrThisDomain;
+ PUNICODE_STRING rgustrDomains;
+ ULONG cbNameBuffer;
+ PWSTR wszNameBuffer;
+} DfsDomainInfo;
+
+//
+// Private functions
+//
+
+DWORD
+DfspInitDomainListFromRegistry(
+ IN HKEY hkey);
+
+DWORD
+DfspInitDomainListFromLSA();
+
+NTSTATUS
+DfspInsertLsaDomainList(
+ PLSA_TRUST_INFORMATION rglsaDomainList,
+ ULONG cDomains);
+
+DWORD
+DfspIsThisADomainName(
+ LPWSTR wszName);
+
+DWORD
+DfspGetDomainDCs(
+ LPWSTR wszDomain,
+ PUNICODE_STRING *prgustrDCNames,
+ PULONG pcDCCount);
+
+DWORD
+DfspValidateDomainShare(
+ LPWSTR wszDomain,
+ LPWSTR wszShare,
+ PUNICODE_STRING rgustrDCNames,
+ DWORD cDCs);
+
+DWORD
+DfspArrayFromServerName(
+ LPWSTR wszServer,
+ PUNICODE_STRING *prgustrDCNames);
+
+DWORD
+DfspDuplicateArray(
+ PUNICODE_STRING prgustrDCNames,
+ PULONG pcDCs,
+ PUNICODE_STRING *prgustrDest);
+
+DWORD
+DfspSetupNullSession(
+ LPWSTR wszDCName);
+
+BOOL DfspGetDomainGluon(
+ LPWSTR wszDomainName,
+ LPWSTR wszShareName,
+ ULONG cDC,
+ PUNICODE_STRING pustrDCNames,
+ PDS_GLUON *ppglDomain);
+
+PDS_MACHINE
+DfspGetDSMachine(
+ PUNICODE_STRING pustrDomain,
+ PUNICODE_STRING pustrName,
+ PUNICODE_STRING pustrShare);
+
+VOID
+DfspFreeDSGluon(
+ PDS_GLUON pdsGluon);
+
+BOOL
+DfspSetDomainInfo (
+ IN PDS_GLUON pglDomain);
+
+INIT_TADDRESS_MARSHAL_INFO()
+INIT_DS_TRANSPORT_P_MARSHAL_INFO()
+INIT_DS_TRANSPORT_MARSHAL_INFO()
+INIT_DS_MACHINE_P_MARSHAL_INFO()
+INIT_DS_MACHINE_MARSHAL_INFO()
+INIT_DS_GLUON_MARSHAL_INFO()
+INIT_DS_GLUON_P_MARSHAL_INFO()
+
+
+//+----------------------------------------------------------------------------
+//
+// Function: DfsInitDomainList
+//
+// Synopsis: Initializes the list of trusted domains so that their Dfs's
+// may be accessed.
+//
+// Arguments: None
+//
+// Returns: Nothing.
+//
+//-----------------------------------------------------------------------------
+
+VOID
+DfsInitDomainList()
+{
+ PWKSTA_INFO_100 wki100;
+ DWORD dwErr;
+ HKEY hkey;
+
+ ZeroMemory( &DfsDomainInfo, sizeof(DfsDomainInfo) );
+
+ //
+ // Get our own domain name
+ //
+
+ dwErr = NetWkstaGetInfo( NULL, 100, (LPBYTE *) &wki100 );
+
+ if (dwErr == ERROR_SUCCESS) {
+
+ if (wki100->wki100_langroup != NULL) {
+
+ DfsDomainInfo.ustrThisDomain.Length =
+ wcslen(wki100->wki100_langroup) * sizeof(WCHAR);
+
+ DfsDomainInfo.ustrThisDomain.MaximumLength =
+ DfsDomainInfo.ustrThisDomain.Length + sizeof(UNICODE_NULL);
+
+ DfsDomainInfo.ustrThisDomain.Buffer =
+ malloc( DfsDomainInfo.ustrThisDomain.MaximumLength );
+
+ if (DfsDomainInfo.ustrThisDomain.Buffer != NULL) {
+
+ wcscpy(
+ DfsDomainInfo.ustrThisDomain.Buffer,
+ wki100->wki100_langroup);
+
+ }
+
+ }
+
+ NetApiBufferFree( wki100 );
+
+ }
+
+ //
+ // Next try to open the trusted domain list in the registry...
+ //
+
+ dwErr = RegOpenKey( HKEY_LOCAL_MACHINE, REG_KEY_TRUSTED_DOMAINS, &hkey);
+
+ if (dwErr == ERROR_SUCCESS) {
+
+ dwErr = DfspInitDomainListFromRegistry( hkey );
+
+ RegCloseKey( hkey );
+
+ }
+
+ //
+ // If either the key was not found, or DfspInitDomainListFromRegistry
+ // returned ERROR_FILE_NOT_FOUND, try to initialize the list from the
+ // LSA.
+ //
+
+ if (dwErr == ERROR_FILE_NOT_FOUND) {
+
+ dwErr = DfspInitDomainListFromLSA();
+
+ }
+
+}
+
+
+//+----------------------------------------------------------------------------
+//
+// Function: DfsGetDomainReferral
+//
+// Synopsis: Given the name of a domain, this routine will try to figure
+// out the names of DCs serving that domain. If it can, it will
+// attempt to stick a pkt entry for the domain's dfs into the
+// driver.
+//
+// Arguments: [wszDomain] -- Name of domain.
+//
+// Returns: [STATUS_SUCCESS] -- Successfully created domain pkt entry.
+//
+// [STATUS_INSUFFICIENT_RESOURCES] -- Out of memory condition.
+//
+// [STATUS_OBJECT_NAME_NOT_FOUND] -- wszDomain is not a trusted
+// domain.
+//
+// [STATUS_UNEXPECTED_NETWORK_ERROR] -- Unable to get DC for
+// domain.
+//
+//-----------------------------------------------------------------------------
+
+NTSTATUS
+DfsGetDomainReferral(
+ IN LPWSTR wszDomain,
+ IN LPWSTR wszShare)
+{
+ NTSTATUS Status;
+ DWORD dwErr;
+ ULONG cDCs;
+ PUNICODE_STRING rgustrDCNames;
+ PDS_GLUON pgl;
+
+ dwErr = DfspIsThisADomainName( wszDomain );
+
+ if (dwErr == ERROR_SUCCESS) {
+
+ dwErr = DfspGetDomainDCs( wszDomain, &rgustrDCNames, &cDCs);
+
+ if (dwErr == ERROR_SUCCESS) {
+
+ dwErr = DfspValidateDomainShare(
+ wszDomain,
+ wszShare,
+ rgustrDCNames,
+ cDCs);
+
+ if (dwErr != ERROR_SUCCESS)
+ free( rgustrDCNames );
+
+ }
+
+ if (dwErr == ERROR_SUCCESS) {
+
+ if (DfspGetDomainGluon(
+ wszDomain, wszShare, cDCs, rgustrDCNames, &pgl)) {
+
+ if (DfspSetDomainInfo(pgl)) {
+
+ dwErr = ERROR_SUCCESS;
+
+ } else {
+
+ dwErr = ERROR_UNEXP_NET_ERR;
+
+ }
+
+ DfspFreeDSGluon( pgl );
+
+ } else {
+
+ dwErr = ERROR_NOT_ENOUGH_MEMORY;
+
+ }
+
+ free( rgustrDCNames );
+
+ }
+
+ }
+
+ switch (dwErr) {
+ case ERROR_SUCCESS:
+ Status = STATUS_SUCCESS;
+ break;
+
+ case ERROR_NOT_ENOUGH_MEMORY:
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ break;
+
+ case ERROR_FILE_NOT_FOUND:
+ Status = STATUS_OBJECT_NAME_NOT_FOUND;
+ break;
+
+ default:
+ Status = STATUS_UNEXPECTED_NETWORK_ERROR;
+ break;
+
+ }
+
+ return( Status );
+
+}
+
+
+//+----------------------------------------------------------------------------
+//
+// Function: DfspValidateDomainShare
+//
+// Synopsis: Once a list of DCs for a domain name is obtained, this routine
+// validates that the share is valid.
+//
+// Arguments: [wszDomain] -- Name of domain
+// [wszShare] -- Name of share
+// [rgustrDCNames] -- List of DCs
+// [cDCs] -- Number of DCs in rgustrDCNames
+//
+// Returns: [ERROR_SUCCESS] -- Share exists
+//
+// [ERROR_FILE_NOT_FOUND] -- Share does not exist
+//
+//-----------------------------------------------------------------------------
+
+DWORD
+DfspValidateDomainShare(
+ LPWSTR wszDomain,
+ LPWSTR wszShare,
+ PUNICODE_STRING rgustrDCNames,
+ DWORD cDCs)
+{
+ DWORD i;
+ NET_API_STATUS netStatus;
+ PSHARE_INFO_1005 pSHI1005;
+ BOOLEAN isDfs;
+
+ for (i = 0; i < cDCs; i++) {
+
+ netStatus = NetShareGetInfo(
+ rgustrDCNames[i].Buffer,
+ wszShare,
+ 1005,
+ (PBYTE *) &pSHI1005);
+
+ if (netStatus == ERROR_SUCCESS) {
+
+ isDfs = ((pSHI1005->shi1005_flags & SHI1005_FLAGS_DFS) != 0);
+
+ NetApiBufferFree( pSHI1005 );
+
+ if (isDfs) {
+ return( ERROR_SUCCESS );
+ } else {
+ return( ERROR_FILE_NOT_FOUND );
+ }
+
+ } else if (netStatus == NERR_NetNameNotFound) {
+
+ return( ERROR_FILE_NOT_FOUND );
+
+ }
+
+ }
+
+ return( ERROR_FILE_NOT_FOUND );
+
+}
+
+
+//+----------------------------------------------------------------------------
+//
+// Function: DfspInitDomainListFromRegistry
+//
+// Synopsis: Reads the trusted domain list from the registry.
+//
+// Arguments: [hkey] -- Handle to the key that contains the
+// TrustedDomainList value.
+//
+// Returns: [ERROR_SUCCESS] -- Successfully read in the list
+//
+// [ERROR_NOT_ENOUGH_MEMORY] -- Unable to allocate memory for
+// list.
+//
+// [ERROR_FILE_NOT_FOUND] -- Unable to find the
+// TrustedDomainList value in the registry.
+//
+//-----------------------------------------------------------------------------
+
+DWORD
+DfspInitDomainListFromRegistry(
+ IN HKEY hkey)
+{
+ DWORD dwErr, dwType, cbSize;
+ PBYTE pBuffer = NULL;
+
+ cbSize = 1024;
+
+ do {
+
+ if (pBuffer)
+ free( pBuffer );
+
+ pBuffer = (PBYTE) malloc( cbSize );
+
+ if (pBuffer != NULL) {
+
+ dwErr = RegQueryValueEx(
+ hkey,
+ REG_VALUE_TRUSTED_DOMAINS,
+ NULL,
+ &dwType,
+ pBuffer,
+ &cbSize);
+
+ } else {
+
+ dwErr = ERROR_NOT_ENOUGH_MEMORY;
+
+ }
+
+ } while ( dwErr == ERROR_MORE_DATA );
+
+ if (dwErr == ERROR_SUCCESS) {
+
+ //
+ // Good, we have the data. Now, we simply have to put them in the
+ // rgustrDomains list. The returned buffer contains a list of
+ // NULL terminated domain names, with the last one doubly NULL
+ // terminated.
+ //
+
+ PWCHAR pwch = (PWCHAR) pBuffer;
+
+ while (*pwch != UNICODE_NULL) {
+
+ if (*(pwch+1) == UNICODE_NULL) {
+
+ DfsDomainInfo.cDomains++;
+
+ pwch++;
+
+ }
+
+ pwch++;
+
+ }
+
+ DfsDomainInfo.rgustrDomains = (PUNICODE_STRING) malloc(
+ DfsDomainInfo.cDomains *
+ sizeof(UNICODE_STRING));
+
+ if (DfsDomainInfo.rgustrDomains != NULL) {
+
+ ULONG i;
+
+ DfsDomainInfo.wszNameBuffer = (LPWSTR) pBuffer;
+
+ DfsDomainInfo.cbNameBuffer = cbSize;
+
+ for (i = 0, pwch = (PWCHAR) pBuffer;
+ i < DfsDomainInfo.cDomains;
+ i++) {
+
+ RtlInitUnicodeString(
+ &DfsDomainInfo.rgustrDomains[i],
+ pwch);
+
+ pwch += (DfsDomainInfo.rgustrDomains[i].Length +
+ sizeof(UNICODE_NULL)) / sizeof(WCHAR);
+
+ }
+
+ } else {
+
+ dwErr = ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ }
+
+ if ((dwErr != ERROR_SUCCESS) && (pBuffer != NULL))
+ free( pBuffer );
+
+ return( dwErr );
+
+}
+
+
+//+----------------------------------------------------------------------------
+//
+// Function: DfspInitDomainListFromLSA
+//
+// Synopsis: Retrieves the list of trusted domains from the LSA.
+//
+// Arguments: None
+//
+// Returns: [ERROR_SUCCESS] -- Successfully inited list.
+//
+// [ERROR_NOT_ENOUGH_MEMORY] -- Out of memory condition
+//
+//-----------------------------------------------------------------------------
+
+DWORD
+DfspInitDomainListFromLSA()
+{
+ DWORD dwErr;
+ NTSTATUS status;
+ OBJECT_ATTRIBUTES oa;
+ LSA_HANDLE hlsa;
+ LSA_ENUMERATION_HANDLE hEnum = (LSA_ENUMERATION_HANDLE) NULL;
+ PLSA_TRUST_INFORMATION rglsaDomainInfo = NULL;
+
+ ZeroMemory( &oa, sizeof(OBJECT_ATTRIBUTES) );
+
+ status = LsaOpenPolicy(
+ NULL, // SystemName
+ &oa, // LSA Object Attributes
+ POLICY_VIEW_LOCAL_INFORMATION, // Desired Access
+ &hlsa);
+
+ if (NT_SUCCESS(status)) {
+
+ do {
+
+ ULONG cEnum;
+
+ status = LsaEnumerateTrustedDomains(
+ hlsa,
+ &hEnum,
+ (PVOID) &rglsaDomainInfo,
+ LSA_MAXIMUM_ENUMERATION_LENGTH,
+ &cEnum);
+
+ if (NT_SUCCESS(status)) {
+
+ status = DfspInsertLsaDomainList(
+ rglsaDomainInfo,
+ cEnum);
+
+ LsaFreeReturnBuffer( rglsaDomainInfo );
+
+ }
+
+ } while ( status == STATUS_SUCCESS );
+
+ }
+
+ switch (status) {
+ case STATUS_SUCCESS:
+ case STATUS_NO_MORE_ENTRIES:
+ dwErr = ERROR_SUCCESS;
+ break;
+
+ case STATUS_INSUFFICIENT_RESOURCES:
+ dwErr = ERROR_NOT_ENOUGH_MEMORY;
+ break;
+
+ default:
+ dwErr = ERROR_UNEXP_NET_ERR;
+ break;
+
+ }
+
+ return( dwErr );
+
+}
+
+
+//+----------------------------------------------------------------------------
+//
+// Function: DfspInsertLsaDomainList
+//
+// Synopsis: Helper function to insert a part of the trusted domain list
+// into the DfsDomainInfo.
+//
+// Arguments: [rglsaDomainList] -- Array of LSA_TRUST_INFORMATIONs.
+// [cDomains] -- Number of elements in rglsaDomainList
+//
+// Returns: [STATUS_SUCCESS] -- Successfully appended domain list to
+// DfsDomainInfo.
+//
+// [STATUS_INSUFFICIENT_RESOURCES] -- Unable to allocate memory
+// for new list.
+//
+//-----------------------------------------------------------------------------
+
+NTSTATUS
+DfspInsertLsaDomainList(
+ PLSA_TRUST_INFORMATION rglsaDomainList,
+ ULONG cDomains)
+{
+ PUNICODE_STRING rgustrDomains = NULL;
+ PWSTR wszNameBuffer = NULL, pwch;
+ ULONG cTotalDomains, cbNameBuffer, i, j;
+
+ cTotalDomains = DfsDomainInfo.cDomains + cDomains;
+
+ cbNameBuffer = DfsDomainInfo.cbNameBuffer;
+
+ for (i = 0; i < cDomains; i++) {
+
+ cbNameBuffer += rglsaDomainList[i].Name.Length + sizeof(UNICODE_NULL);
+
+ }
+
+ wszNameBuffer = (PWSTR) malloc( cbNameBuffer );
+
+ if (wszNameBuffer == NULL) {
+
+ return( STATUS_INSUFFICIENT_RESOURCES );
+
+ }
+
+ rgustrDomains = (PUNICODE_STRING) malloc(
+ cTotalDomains * sizeof(UNICODE_STRING));
+
+ if (rgustrDomains == NULL) {
+
+ free( wszNameBuffer );
+
+ return( STATUS_INSUFFICIENT_RESOURCES );
+
+ }
+
+ //
+ // Copy over the existing DfsDomainInfo
+ //
+
+ if (DfsDomainInfo.cDomains != 0) {
+
+ CopyMemory(
+ wszNameBuffer,
+ DfsDomainInfo.wszNameBuffer,
+ DfsDomainInfo.cbNameBuffer);
+
+ CopyMemory(
+ rgustrDomains,
+ DfsDomainInfo.rgustrDomains,
+ DfsDomainInfo.cDomains * sizeof(UNICODE_STRING));
+
+ }
+
+ pwch = (PWSTR) (((PCHAR) wszNameBuffer) + DfsDomainInfo.cbNameBuffer);
+
+ for (j = 0, i = DfsDomainInfo.cDomains; j < cDomains; j++, i++) {
+
+ CopyMemory(
+ pwch,
+ rglsaDomainList[j].Name.Buffer,
+ rglsaDomainList[j].Name.Length);
+
+ pwch[ rglsaDomainList[j].Name.Length / sizeof(WCHAR) ] = UNICODE_NULL;
+
+ RtlInitUnicodeString( &rgustrDomains[i], pwch);
+
+ pwch += (rglsaDomainList[j].Name.Length / sizeof(WCHAR) + 1);
+
+ }
+
+ if (DfsDomainInfo.cDomains != 0) {
+
+ free( DfsDomainInfo.rgustrDomains );
+
+ free( DfsDomainInfo.wszNameBuffer );
+
+ }
+
+ DfsDomainInfo.cDomains = cTotalDomains;
+
+ DfsDomainInfo.rgustrDomains = rgustrDomains;
+
+ DfsDomainInfo.wszNameBuffer = wszNameBuffer;
+
+ DfsDomainInfo.cbNameBuffer = cbNameBuffer;
+
+ return( STATUS_SUCCESS );
+
+}
+
+
+//+----------------------------------------------------------------------------
+//
+// Function: DfspIsThisADomainName
+//
+// Synopsis: Runs down the list of domains in DfsDomainInfo and tries
+// to match the given name with one of the entries in the list.
+//
+// Arguments: [wszName] -- Name to find in DfsDomainInfo
+//
+// Returns: [ERROR_SUCCESS] -- Name is indeed a domain name.
+//
+// [ERROR_FILE_NOT_FOUND] -- Name is not in the DfsDomainInfo
+// list.
+//
+//-----------------------------------------------------------------------------
+
+DWORD
+DfspIsThisADomainName(
+ LPWSTR wszName)
+{
+ USHORT cbLength, i;
+ BOOL fFound = FALSE;
+
+ cbLength = wcslen( wszName ) * sizeof(WCHAR);
+
+ if (cbLength == DfsDomainInfo.ustrThisDomain.Length) {
+
+ fFound = (_wcsnicmp(
+ wszName,
+ DfsDomainInfo.ustrThisDomain.Buffer,
+ DfsDomainInfo.ustrThisDomain.Length/sizeof(WCHAR))
+ == 0);
+
+ }
+
+ for (i = 0; i < DfsDomainInfo.cDomains && !fFound; i++) {
+
+ if (cbLength == DfsDomainInfo.rgustrDomains[i].Length) {
+
+ fFound = (_wcsnicmp(
+ wszName,
+ DfsDomainInfo.rgustrDomains[i].Buffer,
+ cbLength/sizeof(WCHAR)) == 0);
+
+ }
+
+ }
+
+ return( fFound ? ERROR_SUCCESS : ERROR_FILE_NOT_FOUND );
+
+}
+
+
+//+----------------------------------------------------------------------------
+//
+// Function: DfspGetDomainDCs
+//
+// Synopsis: Given a domain name, this routine will figure out the name
+// of an available DC. This routine has been based on the routine
+// UaspOpenDomainWithDomainName in \nt\private\net\access\uasp.c
+//
+// The main purpose of this routine is to try and find the best
+// DC for this machine. Simply calling I_NetGetDCList will work,
+// but in reality, it will typically contain a large list of DCs,
+// most of which will be unavailable.
+//
+// The algorithm is as follows:
+//
+// - Try NetGetAnyDCName. This will work if this machine has a
+// secure channel to the target domain.
+// - If this machine is not a DC, try remoting NetGetAnyDCName
+// to our DC. This will work if our DC has a secure
+// channel to the target domain.
+// - If step 2 fails with ERROR_ACCESS_DENIED, setup a NULL
+// session to our DC and try remoting NetGetAnyDCName
+// again.
+// - If all the steps above fail, use I_NetGetDCList to get a
+// list of all the DCs. When we stick this into the
+// driver, it will randomize the list and try to find an
+// active DC.
+//
+// Arguments: [wszDomain] -- Name of domain.
+// [prgustrDCNames] -- On successful return, contains a pointer
+// to an array of DC names. Free this pointer using
+// free. Do NOT free the buffers of the member strings.
+// [pcDCCount] -- On successful return, number of DCs in
+// prgustrDCNames.
+//
+// Returns: [ERROR_SUCCESS] -- Successfully returning DC names.
+//
+// [ERROR_CANT_ACCESS_DOMAIN_INFO] -- Unable to get DC names.
+//
+// [ERROR_NOT_ENOUGH_MEMORY] -- Out of memory situation.
+//
+//-----------------------------------------------------------------------------
+
+DWORD
+DfspGetDomainDCs(
+ LPWSTR wszDomain,
+ PUNICODE_STRING *prgustrDCNames,
+ PULONG pcDCCount)
+{
+ DWORD dwErr;
+ LPWSTR wszServer, wszMyDomainDC;
+ PUNICODE_STRING pustrDCNames;
+
+ //
+ // 1. Try NetGetAnyDCName
+ //
+
+ dwErr = NetGetAnyDCName( NULL, wszDomain, (LPBYTE *) &wszServer);
+
+ if (dwErr == ERROR_SUCCESS) {
+
+ dwErr = DfspArrayFromServerName( wszServer, prgustrDCNames );
+
+ NetApiBufferFree( wszServer );
+
+ *pcDCCount = 1;
+
+ return( dwErr );
+
+ }
+
+ //
+ // 2. If this machine is not a DC, try remoting NetGetAnyDC to our DC.
+ //
+
+ dwErr = NetGetAnyDCName( NULL, NULL, (LPBYTE *) &wszMyDomainDC );
+
+ if (dwErr == ERROR_SUCCESS) {
+
+ dwErr = NetGetAnyDCName(
+ wszMyDomainDC,
+ wszDomain,
+ (LPBYTE *) &wszServer );
+
+ if (dwErr == ERROR_ACCESS_DENIED) {
+
+ //
+ // 3. Setup a NULL session to our DC, and retry the call.
+ //
+
+ DWORD dwErrNullSession;
+
+ dwErrNullSession = DfspSetupNullSession( wszMyDomainDC );
+
+ if (dwErrNullSession == ERROR_SUCCESS ) {
+
+ dwErr = NetGetAnyDCName(
+ wszMyDomainDC,
+ wszDomain,
+ (LPBYTE *) &wszServer);
+
+ }
+
+ }
+
+ NetApiBufferFree( wszMyDomainDC );
+
+ if (dwErr == ERROR_SUCCESS) {
+
+ dwErr = DfspArrayFromServerName( wszServer, prgustrDCNames );
+
+ NetApiBufferFree( wszServer );
+
+ *pcDCCount = 1;
+
+ return( dwErr );
+
+ }
+
+ } else if (dwErr == ERROR_NO_LOGON_SERVERS) {
+
+ return( ERROR_CANT_ACCESS_DOMAIN_INFO );
+
+ }
+
+ //
+ // 4. Simply call I_NetGetDCList to retrieve set of all DCs.
+ //
+
+ dwErr = I_NetGetDCList(
+ NULL,
+ wszDomain,
+ pcDCCount,
+ &pustrDCNames);
+
+ if (dwErr != ERROR_SUCCESS) {
+
+ return( ERROR_CANT_ACCESS_DOMAIN_INFO );
+
+ }
+
+ if (*pcDCCount == 0) {
+
+ return( ERROR_CANT_ACCESS_DOMAIN_INFO );
+
+ }
+
+ //
+ // We'll have to duplicate the array because NetApiXXX might be using a
+ // different allocator.
+ //
+
+ dwErr = DfspDuplicateArray( pustrDCNames, pcDCCount, prgustrDCNames );
+
+ NetApiBufferFree( pustrDCNames );
+
+ //
+ // I_NetGetDCList returns a NULL UNICODE_STRING if the PDC is down.
+ // DfspDuplicateArray would have stripped this entry out, so our
+ // DC Count may have dropped down to 0. Check for this here.
+ //
+
+ if (dwErr == ERROR_SUCCESS && *pcDCCount == 0) {
+
+ free( *prgustrDCNames );
+
+ dwErr = ERROR_CANT_ACCESS_DOMAIN_INFO;
+
+ }
+
+ return( dwErr );
+
+}
+
+
+//+----------------------------------------------------------------------------
+//
+// Function: DfspArrayFromServerName
+//
+// Synopsis: Helper function for DfspGetDomainDCs. Stuffs a ServerName into
+// an 1 element array of UNICODE_STRINGs. Allocates the array
+// and space for the ServerName using malloc
+//
+// Arguments: [wszServer] -- Name of Server.
+// [prgustrDCName] -- On successful return, contains pointer to
+// array of DC names.
+//
+// Returns: [ERROR_SUCCESS] -- Successfully completed operation.
+//
+// [ERROR_NOT_ENOUGH_MEMORY] -- Unable to allocate memory for
+// array.
+//
+//-----------------------------------------------------------------------------
+
+DWORD
+DfspArrayFromServerName(
+ LPWSTR wszServer,
+ PUNICODE_STRING *prgustrDCNames)
+{
+ PUNICODE_STRING rgustrDCNames;
+ LPWSTR wszNameBuffer;
+
+ rgustrDCNames = (PUNICODE_STRING) malloc(
+ sizeof(UNICODE_STRING) +
+ wcslen(wszServer) * sizeof(WCHAR) +
+ sizeof(UNICODE_NULL));
+
+ if (rgustrDCNames != NULL) {
+
+ wszNameBuffer = (LPWSTR) (rgustrDCNames + 1);
+
+ wcscpy( wszNameBuffer, wszServer );
+
+ RtlInitUnicodeString( &rgustrDCNames[0], wszNameBuffer );
+
+ *prgustrDCNames = rgustrDCNames;
+
+ return( ERROR_SUCCESS );
+
+ } else {
+
+ return( ERROR_NOT_ENOUGH_MEMORY );
+
+ }
+
+}
+
+
+//+----------------------------------------------------------------------------
+//
+// Function: DfspDuplicateArray
+//
+// Synopsis: Helper function for DfspGetDomainDCs. This function duplicates
+// the array returned by I_NetGetDCList.
+//
+// Note that I_NetGetDCList returns a NULL UNICODE_STRING for
+// the PDC if the PDC is down. DfspDuplicateArray will NOT copy
+// this NULL string to the destination. On return, *pcDCs will be
+// suitably adjusted.
+//
+// Arguments: [prgustrDCNames] -- Array of DC names.
+// [pcDCs] -- On entry, count of DCs in prgustrDCNames. On
+// successful return, count of DCs in prgustrDest.
+// [prgustrDest] -- On successful return, contains a pointer
+// to the array of DC names.
+//
+// Returns: [ERROR_SUCCESS] -- Successfully duplicate array.
+//
+// [ERROR_NOT_ENOUGH_MEMORY] -- Out of memory condition
+//
+//-----------------------------------------------------------------------------
+
+DWORD
+DfspDuplicateArray(
+ PUNICODE_STRING prgustrDCNames,
+ PULONG pcDCs,
+ PUNICODE_STRING *prgustrDest)
+{
+ ULONG i, j, cbBuffer, cDCs;
+ PUNICODE_STRING rgustrNew;
+ LPWSTR wszNames;
+
+ cDCs = *pcDCs;
+
+ cbBuffer = cDCs * sizeof(UNICODE_STRING);
+
+ for(i = 0; i < cDCs; i++) {
+
+ cbBuffer += prgustrDCNames[i].Length + sizeof(WCHAR);
+
+ }
+
+ rgustrNew = (PUNICODE_STRING) malloc( cbBuffer );
+
+ if (rgustrNew != NULL) {
+
+ wszNames = (LPWSTR) &rgustrNew[cDCs];
+
+ for (i = 0, j = 0; i < cDCs; i++) {
+
+ if (prgustrDCNames[i].Length == 0) {
+ continue;
+ }
+
+ CopyMemory(
+ wszNames,
+ prgustrDCNames[i].Buffer,
+ prgustrDCNames[i].Length);
+
+ wszNames[ prgustrDCNames[i].Length/sizeof(WCHAR) ] = UNICODE_NULL;
+
+ RtlInitUnicodeString( &rgustrNew[j], wszNames );
+
+ j++;
+
+ wszNames += (prgustrDCNames[i].Length / sizeof(WCHAR)) + 1;
+
+ }
+
+ *prgustrDest = rgustrNew;
+
+ *pcDCs = j;
+
+ return( ERROR_SUCCESS );
+
+ } else {
+
+ return( ERROR_NOT_ENOUGH_MEMORY );
+
+ }
+
+}
+
+
+//+----------------------------------------------------------------------------
+//
+// Function: DfspSetupNullSession
+//
+// Synopsis: Helper function for DfspGetDomainDCs. This one sets up a NULL
+// session to a DC's IPC$ share.
+//
+// Arguments: [wszDCName] -- Name of DC to setup a NULL session with.
+//
+// Returns: Result of NetUseAdd
+//
+//-----------------------------------------------------------------------------
+
+DWORD
+DfspSetupNullSession(
+ LPWSTR wszDCName)
+{
+ DWORD dwErr;
+ USE_INFO_2 ui2;
+ LPWSTR wszDCIPCShare;
+
+ wszDCIPCShare = (LPWSTR) malloc(
+ wcslen(wszDCName) * sizeof(WCHAR) +
+ sizeof(ROOT_SHARE_NAME));
+
+ if (wszDCIPCShare != NULL) {
+
+ wcscpy( wszDCIPCShare, wszDCName );
+
+ wcscat( wszDCIPCShare, ROOT_SHARE_NAME );
+
+ ui2.ui2_local = NULL;
+ ui2.ui2_remote = wszDCIPCShare;
+ ui2.ui2_password = L"";
+ ui2.ui2_asg_type = USE_IPC;
+ ui2.ui2_username = L"";
+ ui2.ui2_domainname = L"";
+
+ dwErr = NetUseAdd( NULL, 2, (LPBYTE) &ui2, NULL );
+
+ free( wszDCIPCShare );
+
+ } else {
+
+ dwErr = ERROR_NOT_ENOUGH_MEMORY;
+
+ }
+
+ return( dwErr );
+
+}
+
+
+//+----------------------------------------------------------------------------
+//
+// Function: DfspGetDomainGluon
+//
+// Synopsis: Constructs a DS_GLUON for the domain volume, given the
+// name of the domain and the list of DCs in that domain.
+//
+// Arguments: [wszDomainName] -- Name of domain
+// [wszShareName] -- Name of share at root of domain Dfs
+// [cDC] -- Count of DCs in the domain
+// [pustrDCNames] -- Array of count DC names.
+// [pglDomain] -- The constructed DS_GLUON
+//
+// Returns: TRUE if success, FALSE otherwise.
+//
+//-----------------------------------------------------------------------------
+
+BOOL DfspGetDomainGluon(
+ LPWSTR wszDomainName,
+ LPWSTR wszShareName,
+ ULONG cDC,
+ PUNICODE_STRING pustrDCNames,
+ PDS_GLUON *ppglDomain)
+{
+
+ LPWSTR pwszDomainPrefix;
+ PDS_GLUON pglDomain = NULL;
+ UNICODE_STRING ustrDomain, ustrShare;
+ ULONG i;
+ BOOL fSuccess = TRUE;
+
+ RtlInitUnicodeString( &ustrDomain, wszDomainName );
+ RtlInitUnicodeString( &ustrShare, wszShareName );
+
+ pwszDomainPrefix = malloc(
+ sizeof(UNICODE_PATH_SEP) +
+ wcslen(wszDomainName) * sizeof(WCHAR) +
+ sizeof(UNICODE_PATH_SEP) +
+ wcslen(wszShareName) * sizeof(WCHAR) +
+ sizeof(UNICODE_NULL));
+
+ if (pwszDomainPrefix != NULL) {
+
+ wcscpy( pwszDomainPrefix, UNICODE_PATH_SEP_STR );
+ wcscat( pwszDomainPrefix, wszDomainName );
+ wcscat( pwszDomainPrefix, UNICODE_PATH_SEP_STR );
+ wcscat( pwszDomainPrefix, wszShareName );
+
+ } else {
+
+ fSuccess = FALSE;
+
+ }
+
+ if (fSuccess) {
+
+ pglDomain = malloc(sizeof(DS_GLUON) + cDC * sizeof(PDS_MACHINE));
+
+ }
+
+ if (pglDomain != NULL) {
+
+ ZeroMemory( &pglDomain->guidThis, sizeof(DS_GLUON) +
+ cDC * sizeof(PDS_MACHINE));
+
+ pglDomain->pwszName = pwszDomainPrefix;
+
+ pglDomain->cMachines = cDC;
+
+ for (i = 0; (i < cDC) && fSuccess; i++) {
+
+ //
+ // Get rid of the leading double backslashes
+ //
+
+ if (pustrDCNames[i].Length > 2 * sizeof(WCHAR) &&
+ pustrDCNames[i].Buffer[0] == UNICODE_PATH_SEP &&
+ pustrDCNames[i].Buffer[1] == UNICODE_PATH_SEP) {
+
+ pustrDCNames[i].Length -= 2 * sizeof(WCHAR);
+
+ MoveMemory(
+ pustrDCNames[i].Buffer,
+ &pustrDCNames[i].Buffer[2],
+ pustrDCNames[i].Length);
+
+ }
+
+ pglDomain->rpMachines[i] = DfspGetDSMachine(
+ &ustrDomain,
+ &pustrDCNames[i],
+ &ustrShare);
+
+ if (pglDomain->rpMachines[i] == NULL) {
+
+ pglDomain->cMachines = i; // To facilitate cleanup
+
+ fSuccess = FALSE;
+
+ }
+ }
+
+ } else {
+
+ fSuccess = FALSE;
+
+ }
+
+ //
+ // Cleanup if error
+ //
+
+ if (!fSuccess) {
+
+ if (pglDomain != NULL) {
+
+ DfspFreeDSGluon( pglDomain );
+
+ } else if (pwszDomainPrefix != NULL) {
+
+ free( pwszDomainPrefix );
+
+ }
+
+ *ppglDomain = NULL;
+
+ } else {
+
+ *ppglDomain = pglDomain;
+
+ }
+
+ return( fSuccess );
+
+}
+
+
+//+----------------------------------------------------------------------------
+//
+// Function: DfspGetDSMachine
+//
+// Synopsis: Given a NetBIOS name, this routine constructs a DS_MACHINE
+// struct with 1 transport.
+//
+// Arguments: [pustrDomain] -- The Domain name of the machine.
+// [pustrName] -- The NetBIOS name of the machine.
+// [pustrShare] -- The share name to connect to on the machine
+//
+// Returns: Pointer to allocated DS_MACHINE, or NULL if failure.
+//
+//-----------------------------------------------------------------------------
+
+#define SIZE_OF_DS_MACHINE_WITH_1_ADDR \
+ (sizeof(DS_MACHINE) + sizeof(LPWSTR) + sizeof(DS_TRANSPORT) + sizeof(TDI_ADDRESS_NETBIOS))
+
+PDS_MACHINE
+DfspGetDSMachine(
+ PUNICODE_STRING pustrDomain,
+ PUNICODE_STRING pustrName,
+ PUNICODE_STRING pustrShare)
+{
+
+ PDS_MACHINE pdsMachine;
+ PDS_TRANSPORT pdsTransport;
+ PTDI_ADDRESS_NETBIOS ptdiNB;
+
+ LPWSTR wszPrincipalName;
+ LPWSTR wszShareName;
+
+ pdsMachine = malloc( SIZE_OF_DS_MACHINE_WITH_1_ADDR +
+ pustrName->Length +
+ sizeof(UNICODE_NULL) +
+ pustrShare->Length +
+ sizeof(UNICODE_NULL));
+
+ if (pdsMachine != NULL) {
+
+ ZeroMemory( pdsMachine, sizeof(DS_MACHINE) );
+
+ //
+ // Insert the principal name - simply machine name
+ //
+
+ pdsMachine->cPrincipals = 1;
+
+ wszPrincipalName = (LPWSTR) (((PCHAR) pdsMachine) +
+ SIZE_OF_DS_MACHINE_WITH_1_ADDR);
+ CopyMemory(
+ wszPrincipalName,
+ pustrName->Buffer,
+ pustrName->Length);
+
+ wszPrincipalName[pustrName->Length / sizeof(WCHAR)] = UNICODE_NULL;
+
+ pdsMachine->prgpwszPrincipals = (LPWSTR *) (pdsMachine + 1);
+
+ pdsMachine->prgpwszPrincipals[0] = wszPrincipalName;
+
+ wszShareName = &wszPrincipalName[pustrName->Length/sizeof(WCHAR) + 1];
+
+ CopyMemory(
+ wszShareName,
+ pustrShare->Buffer,
+ pustrShare->Length);
+
+ wszShareName[pustrShare->Length/sizeof(WCHAR)] = UNICODE_NULL;
+
+ pdsMachine->pwszShareName = wszShareName;
+
+ //
+ // Build the NetBIOS DS_TRANSPORT structure
+ //
+
+ pdsMachine->cTransports = 1;
+
+ pdsTransport = (PDS_TRANSPORT) (pdsMachine + 1);
+
+ pdsTransport = (PDS_TRANSPORT)
+ (((PUCHAR) pdsTransport) + sizeof(LPWSTR));
+
+ pdsMachine->rpTrans[0] = pdsTransport;
+
+ pdsTransport->usFileProtocol = FSP_SMB;
+
+ pdsTransport->iPrincipal = 0;
+
+ pdsTransport->grfModifiers = 0;
+
+ //
+ // Build the TA_ADDRESS_NETBIOS
+ //
+
+ pdsTransport->taddr.AddressLength = sizeof(TDI_ADDRESS_NETBIOS);
+
+ pdsTransport->taddr.AddressType = TDI_ADDRESS_TYPE_NETBIOS;
+
+ ptdiNB = (PTDI_ADDRESS_NETBIOS) &pdsTransport->taddr.Address[0];
+
+ ptdiNB->NetbiosNameType = TDI_ADDRESS_NETBIOS_TYPE_UNIQUE;
+
+ FillMemory( &ptdiNB->NetbiosName[0], 16, ' ' );
+
+ wcstombs(
+ &ptdiNB->NetbiosName[0],
+ pustrName->Buffer,
+ pustrName->Length/sizeof(WCHAR));
+
+ }
+
+ return( pdsMachine );
+
+}
+
+
+//+----------------------------------------------------------------------------
+//
+// Function: DfspFreeDSGluon
+//
+// Synopsis: Frees memory associated with a DS_GLUON constructed by
+// DfspGetDomainGluon
+//
+// Arguments: [pdsGluon] -- The victim DS_GLUON
+//
+// Returns: Nothing
+//
+//-----------------------------------------------------------------------------
+
+VOID
+DfspFreeDSGluon(
+ PDS_GLUON pdsGluon)
+{
+
+ ULONG i;
+
+ if (pdsGluon->pwszName != NULL) {
+
+ free( pdsGluon->pwszName );
+
+ }
+
+ for (i = 0; i < pdsGluon->cMachines; i++) {
+
+ if (pdsGluon->rpMachines[i] != NULL) {
+
+ free( pdsGluon->rpMachines[i] );
+
+ }
+
+ }
+
+ free( pdsGluon );
+
+}
+
+
+//+----------------------------------------------------------------------------
+//
+// Function: DfspSetDomainInfo
+//
+// Synopsis: Takes a gluon for the DCs of the domain and primes the Dfs
+// driver with it.
+//
+// Arguments: [pglDomain] -- gluon for the machine domain's DCs
+// [iConnectedDC] -- index into gluon of the particular DC that
+// is to be preferred. If this index is 0xFFFF,
+// no DC is given preference.
+//
+// Returns: TRUE if Dfs successfully primed with Domain Info, FALSE
+// otherwise.
+//
+//-----------------------------------------------------------------------------
+
+BOOL
+DfspSetDomainInfo (
+ IN PDS_GLUON pglDomain)
+{
+ NTSTATUS Status;
+ HANDLE hDfs;
+ MARSHAL_BUFFER marshalBuffer;
+ PUCHAR pBuffer = NULL;
+ ULONG cbSize;
+ DS_GLUON_P glDomainP;
+ BOOL fSuccess = FALSE;
+
+ glDomainP.pDSGluon = pglDomain;
+ cbSize = 0;
+ Status = DfsRtlSize(&MiDSGluonP, &glDomainP, &cbSize);
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+
+ pBuffer = malloc( cbSize );
+
+ if (pBuffer == NULL) {
+ goto Cleanup;
+ }
+
+ MarshalBufferInitialize(&marshalBuffer, cbSize, pBuffer);
+
+ Status = DfsRtlPut(&marshalBuffer, &MiDSGluonP, &glDomainP);
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+
+ //
+ // Good, now that we have everything needed to setup the domain
+ // pkt entry. We simply fsctl to the driver to setup the Pkt Entry.
+ //
+
+ Status = DfsOpen( &hDfs, NULL );
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+
+ Status = DfsFsctl(
+ hDfs,
+ FSCTL_DFS_SET_DOMAIN_GLUON,
+ pBuffer,
+ cbSize,
+ NULL,
+ 0L);
+
+ NtClose( hDfs );
+
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+
+ fSuccess = TRUE;
+
+Cleanup:
+
+ if (pBuffer != NULL) {
+ free( pBuffer );
+ }
+
+ return(fSuccess);
+}
+
+
+UNICODE_STRING LocalDfsName = {
+ sizeof(DFS_DRIVER_NAME)-sizeof(UNICODE_NULL),
+ sizeof(DFS_DRIVER_NAME)-sizeof(UNICODE_NULL),
+ DFS_DRIVER_NAME
+};
+
+//+-------------------------------------------------------------------------
+//
+// Function: DfsOpen, public
+//
+// Synopsis:
+//
+// Arguments:
+//
+// Returns:
+//
+//--------------------------------------------------------------------------
+
+NTSTATUS
+DfsOpen(
+ IN OUT PHANDLE DfsHandle,
+ IN PUNICODE_STRING DfsName OPTIONAL
+)
+{
+ NTSTATUS status;
+ OBJECT_ATTRIBUTES objectAttributes;
+ IO_STATUS_BLOCK ioStatus;
+ PUNICODE_STRING name;
+
+ if (ARGUMENT_PRESENT(DfsName)) {
+ name = DfsName;
+ } else {
+ name = &LocalDfsName;
+ }
+
+ InitializeObjectAttributes(
+ &objectAttributes,
+ name,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL
+ );
+
+ status = NtCreateFile(
+ DfsHandle,
+ SYNCHRONIZE,
+ &objectAttributes,
+ &ioStatus,
+ NULL,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ FILE_OPEN_IF,
+ FILE_CREATE_TREE_CONNECTION | FILE_SYNCHRONOUS_IO_NONALERT,
+ NULL,
+ 0);
+
+ if (NT_SUCCESS(status))
+ status = ioStatus.Status;
+
+ return status;
+}
+
+
+//+-------------------------------------------------------------------------
+//
+// Function: DfsFsctl, public
+//
+// Synopsis:
+//
+// Arguments:
+//
+// Returns:
+//
+//--------------------------------------------------------------------------
+
+NTSTATUS
+DfsFsctl(
+ IN HANDLE DfsHandle,
+ IN ULONG FsControlCode,
+ IN PVOID InputBuffer OPTIONAL,
+ IN ULONG InputBufferLength,
+ OUT PVOID OutputBuffer OPTIONAL,
+ IN ULONG OutputBufferLength
+)
+{
+ NTSTATUS status;
+ IO_STATUS_BLOCK ioStatus;
+
+ status = NtFsControlFile(
+ DfsHandle,
+ NULL, // Event,
+ NULL, // ApcRoutine,
+ NULL, // ApcContext,
+ &ioStatus,
+ FsControlCode,
+ InputBuffer,
+ InputBufferLength,
+ OutputBuffer,
+ OutputBufferLength
+ );
+
+ if(NT_SUCCESS(status))
+ status = ioStatus.Status;
+
+ return status;
+}
+
+
diff --git a/private/net/svcdlls/wkssvc/server/dominfo.h b/private/net/svcdlls/wkssvc/server/dominfo.h
new file mode 100644
index 000000000..1b1b367c5
--- /dev/null
+++ b/private/net/svcdlls/wkssvc/server/dominfo.h
@@ -0,0 +1,42 @@
+//+----------------------------------------------------------------------------
+//
+// Copyright (C) 1996, Microsoft Corporation
+//
+// File: dominfo.h
+//
+// Contents: Code to figure out domain dfs addresses
+//
+// Classes: None
+//
+// Functions: DfsGetDomainReferral
+//
+// History: Feb 7, 1996 Milans created
+//
+//-----------------------------------------------------------------------------
+
+#ifndef _DOMINFO_
+#define _DOMINFO_
+
+VOID
+DfsInitDomainList();
+
+NTSTATUS
+DfsGetDomainReferral(
+ LPWSTR wszDomainName,
+ LPWSTR wszShareName);
+
+NTSTATUS
+DfsFsctl(
+ IN HANDLE DfsHandle,
+ IN ULONG FsControlCode,
+ IN PVOID InputBuffer OPTIONAL,
+ IN ULONG InputBufferLength,
+ OUT PVOID OutputBuffer OPTIONAL,
+ IN ULONG OutputBufferLength);
+
+NTSTATUS
+DfsOpen(
+ IN OUT PHANDLE DfsHandle,
+ IN PUNICODE_STRING DfsName OPTIONAL);
+
+#endif // _DOMINFO_
diff --git a/private/net/svcdlls/wkssvc/server/message.c b/private/net/svcdlls/wkssvc/server/message.c
new file mode 100644
index 000000000..e88cb0700
--- /dev/null
+++ b/private/net/svcdlls/wkssvc/server/message.c
@@ -0,0 +1,458 @@
+/*++
+
+Copyright (c) 1991-1992 Microsoft Corporation
+
+Module Name:
+
+ message.c
+
+Abstract:
+
+ This module contains the worker routines for the NetMessageBufferSend
+ API implemented in the Workstation service.
+
+Author:
+
+ Rita Wong (ritaw) 29-July-1991
+
+Revision History:
+ Terence Kwan (terryk) 20-Oct-1993
+ Initialize the system inside NetrMessageBufferSend for the first send
+
+--*/
+
+#include "wsutil.h"
+#include "wsconfig.h" // WsInfo.WsComputerName
+#include "wsmsg.h" // Send worker routines
+#include "wssec.h" // Security object
+#include <lmwksta.h> // NetWkstaUserGetInfo
+
+STATIC
+NET_API_STATUS
+WsGetSenderName(
+ OUT LPTSTR Sender
+ );
+
+STATIC
+NET_API_STATUS
+WsSendDirectedMessage(
+ IN LPTSTR To,
+ IN LPTSTR Sender,
+ IN LPBYTE Message,
+ IN DWORD MessageSize
+ );
+
+
+NET_API_STATUS
+NetrMessageBufferSend (
+ IN LPTSTR ServerName,
+ IN LPTSTR MessageName,
+ IN LPTSTR FromName OPTIONAL,
+ IN LPBYTE Message,
+ IN DWORD MessageSize
+ )
+/*++
+
+Routine Description:
+
+ This function is the NetMessageBufferSend entry point in the
+ Workstation service.
+
+Arguments:
+
+ ServerName - Supplies the name of server to execute this function
+
+ MessageName - Supplies the message alias to send the message to.
+
+ FromName - Supplies the message alias of sender. If NULL, the sender
+ alias will default to the currently logged on user.
+
+ Message - Supplies a pointer to the message to send.
+
+ MessageSize - Supplies the size of the message in number of bytes.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NET_API_STATUS status;
+ DWORD i;
+
+ TCHAR Sender[UNLEN + 1];
+ TCHAR To[NCBNAMSZ + 2];
+
+ LPTSTR Asterix = NULL;
+
+ NTSTATUS ntstatus;
+ UNICODE_STRING UnicodeMessage;
+ OEM_STRING OemMessage;
+
+ static BOOL fInitialize = FALSE;
+
+ // Initialize the system if this this the first time.
+
+ if ( !fInitialize )
+ {
+ if (( ntstatus = WsInitializeMessageSend()) != NERR_Success )
+ {
+ return(ntstatus);
+ }
+ fInitialize = TRUE;
+ }
+
+ UNREFERENCED_PARAMETER(ServerName);
+
+ IF_DEBUG(MESSAGE) {
+ NetpKdPrint(("[Wksta] NetMessageBufferSend MessageSize=%lu\n",
+ MessageSize));
+ }
+
+ //
+ // Any local user, and domain admins and operators are allowed to
+ // send messages. Remote users besides domain admins, and operators
+ // are denied access.
+ //
+ if (NetpAccessCheckAndAudit(
+ WORKSTATION_DISPLAY_NAME, // Subsystem name
+ (LPTSTR) MESSAGE_SEND_OBJECT, // Object type name
+ MessageSendSd, // Security descriptor
+ WKSTA_MESSAGE_SEND, // Desired access
+ &WsMessageSendMapping // Generic mapping
+ ) != NERR_Success) {
+
+ return ERROR_ACCESS_DENIED;
+ }
+
+ if (! ARGUMENT_PRESENT(FromName)) {
+
+ //
+ // Get the caller's username
+ //
+ if ((status = WsGetSenderName(Sender)) != NERR_Success) {
+ return status;
+ }
+ }
+ else {
+ STRCPY(Sender, FromName);
+ }
+
+ //
+ // Convert the Unicode message to OEM character set (very similar
+ // to ANSI)
+ //
+ UnicodeMessage.Buffer = (PWCHAR) Message;
+ UnicodeMessage.Length = (USHORT) MessageSize;
+ UnicodeMessage.MaximumLength = (USHORT) MessageSize;
+
+ ntstatus = RtlUnicodeStringToOemString(
+ &OemMessage,
+ &UnicodeMessage,
+ TRUE
+ );
+
+ if (! NT_SUCCESS(ntstatus)) {
+ NetpKdPrint(("[Wksta] NetrMessageBufferSend: RtlUnicodeStringToOemString failed "
+ FORMAT_NTSTATUS "\n", ntstatus));
+ return NetpNtStatusToApiStatus(ntstatus);
+ }
+
+
+ //
+ // If message name is longer than the permitted NetBIOS name length + 1
+ // (to allow for trailing '*' in case of sending to domain name),
+ // truncate the name.
+ //
+ if (STRLEN(MessageName) > NCBNAMSZ) {
+ STRNCPY(To, MessageName, NCBNAMSZ);
+ To[NCBNAMSZ] = TCHAR_EOS;
+ }
+ else {
+ STRCPY(To, MessageName);
+ }
+
+ //
+ // Remove any trailing blanks from the "To" Name.
+ //
+ for (i = STRLEN(To)-1 ;i != 0 ;i--) {
+ if (To[i] != TEXT(' ')) {
+ To[i+1]= TEXT('\0');
+ break;
+ }
+ }
+
+
+ //
+ // Don't allow broadcasts anymore.
+ //
+ if (STRNCMP(To, TEXT("*"), 2) == 0) {
+ status = ERROR_INVALID_PARAMETER;
+
+ goto CleanExit;
+ }
+
+
+ //
+ // Send message to a domain. Recipient name should be in the form of
+ // "DomainName*".
+ //
+ Asterix = STRRCHR(To, TCHAR_STAR);
+
+ if ((Asterix) && (*(Asterix + 1) == TCHAR_EOS)) {
+
+ *Asterix = TCHAR_EOS; // Overwrite trailing '*'
+
+ //
+ // If message size is too long to fit into a mailslot message,
+ // truncate it.
+ //
+ if (OemMessage.Length > MAX_GROUP_MESSAGE_SIZE) {
+
+ if ((status = WsSendToGroup(
+ To,
+ Sender,
+ OemMessage.Buffer,
+ MAX_GROUP_MESSAGE_SIZE
+ )) == NERR_Success) {
+
+ status = NERR_TruncatedBroadcast;
+ goto CleanExit;
+ }
+
+ } else {
+ status = WsSendToGroup(
+ To,
+ Sender,
+ OemMessage.Buffer,
+ (WORD) OemMessage.Length
+ );
+
+ goto CleanExit;
+ }
+ }
+
+ //
+ // Send a directed message
+ //
+ if (Asterix) {
+ RtlFreeOemString(&OemMessage);
+ return NERR_NameNotFound;
+ }
+
+ status = WsSendDirectedMessage(
+ To,
+ Sender,
+ OemMessage.Buffer,
+ OemMessage.Length
+ );
+
+CleanExit:
+
+ RtlFreeOemString(&OemMessage);
+ return status;
+}
+
+
+STATIC
+NET_API_STATUS
+WsGetSenderName(
+ OUT LPTSTR Sender
+ )
+/*++
+
+Routine Description:
+
+ This function retrives the username of person who called
+ NetMessageBufferSend API. If the caller is not logged on, he/she has
+ no name; in this case, we return the computer name as the sender name.
+
+Arguments:
+
+ Sender - Returns the username of the caller of the NetMessageBufferSend
+ API.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ //
+ // No username, sender is computer name
+ //
+ STRCPY(Sender, WsInfo.WsComputerName);
+
+
+ (VOID) I_NetNameCanonicalize(
+ NULL,
+ Sender,
+ Sender,
+ (UNLEN + 1) * sizeof(TCHAR),
+ NAMETYPE_MESSAGEDEST,
+ 0
+ );
+
+ return NERR_Success;
+}
+
+
+STATIC
+NET_API_STATUS
+WsSendDirectedMessage(
+ IN LPTSTR To,
+ IN LPTSTR Sender,
+ IN LPBYTE Message,
+ IN DWORD MessageSize
+ )
+/*++
+
+Routine Description:
+
+ This function sends the specified message as a directed message
+ to the specified recipient. A call to the recipient is sent
+ out on each LAN adapter. If there is no response we try the
+ next LAN adapter until we hear from the targeted recipient.
+
+Arguments:
+
+ To - Supplies the message alias of the recipient.
+
+ Sender - Supplies the message alias of sender.
+
+ Message - Supplies a pointer to the message to send.
+
+ MessageSize - Supplies the size of the message in number of bytes.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NET_API_STATUS status;
+ UCHAR i;
+
+ BOOL NameFound = FALSE;
+
+ UCHAR SessionNumber;
+ short MessageId;
+
+
+
+ //
+ // Try each network until someone answers the call. Only the first name
+ // found will receive the message. The same name on any other network
+ // will never see the message. This is to remain consistent with all
+ // other session based algorithms in LAN Man.
+ //
+ for (i = 0; i < WsNetworkInfo.LanAdapterNumbers.length; i++) {
+
+ //
+ // Attempt to establish a session
+ //
+ if ((status = NetpNetBiosCall(
+ WsNetworkInfo.LanAdapterNumbers.lana[i],
+ To,
+ Sender,
+ &SessionNumber
+ )) == NERR_Success) {
+
+ NameFound = TRUE;
+
+ IF_DEBUG(MESSAGE) {
+ NetpKdPrint(("[Wksta] Successfully called %ws\n", To));
+ }
+
+ if (MessageSize <= MAX_SINGLE_MESSAGE_SIZE) {
+
+ //
+ // Send single block message if possible
+ //
+ status = WsSendSingleBlockMessage(
+ WsNetworkInfo.LanAdapterNumbers.lana[i],
+ SessionNumber,
+ To,
+ Sender,
+ Message,
+ (WORD) MessageSize
+ );
+
+ }
+ else {
+
+ //
+ // Message too long, got to send multi-block message
+ //
+
+ //
+ // Send the begin message
+ //
+ if ((status = WsSendMultiBlockBegin(
+ WsNetworkInfo.LanAdapterNumbers.lana[i],
+ SessionNumber,
+ To,
+ Sender,
+ &MessageId
+ )) == NERR_Success) {
+
+
+ //
+ // Send the body of the message in as many blocks as necessary
+ //
+ for (; MessageSize > MAX_SINGLE_MESSAGE_SIZE;
+ Message += MAX_SINGLE_MESSAGE_SIZE,
+ MessageSize -= MAX_SINGLE_MESSAGE_SIZE) {
+
+ if ((status = WsSendMultiBlockText(
+ WsNetworkInfo.LanAdapterNumbers.lana[i],
+ SessionNumber,
+ Message,
+ MAX_SINGLE_MESSAGE_SIZE,
+ MessageId
+ )) != NERR_Success) {
+ break;
+ }
+ }
+
+ if (status == NERR_Success && MessageSize > 0) {
+ //
+ // Send the remaining message body
+ //
+ status = WsSendMultiBlockText(
+ WsNetworkInfo.LanAdapterNumbers.lana[i],
+ SessionNumber,
+ Message,
+ (WORD) MessageSize,
+ MessageId
+ );
+ }
+
+ //
+ // Send the end message
+ //
+ if (status == NERR_Success) {
+ status = WsSendMultiBlockEnd(
+ WsNetworkInfo.LanAdapterNumbers.lana[i],
+ SessionNumber,
+ MessageId
+ );
+ }
+
+ }
+ }
+
+ (VOID) NetpNetBiosHangup(
+ WsNetworkInfo.LanAdapterNumbers.lana[i],
+ SessionNumber
+ );
+
+ } // Call successful
+
+ if (NameFound) {
+ break;
+ }
+ }
+
+ return status;
+}
diff --git a/private/net/svcdlls/wkssvc/server/msgutil.c b/private/net/svcdlls/wkssvc/server/msgutil.c
new file mode 100644
index 000000000..4dde22397
--- /dev/null
+++ b/private/net/svcdlls/wkssvc/server/msgutil.c
@@ -0,0 +1,497 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ msgutil.c
+
+Abstract:
+
+ This module contains the common utility routines for needed to
+ implement the NetMessageBufferSend API.
+
+Author:
+
+ Rita Wong (ritaw) 26-July-1991
+
+Revision History:
+ Terence Kwan (terryk) 20-Oct-1993
+ Shut down the system iff we initiailize the system successfully.
+
+--*/
+
+#include "ws.h"
+#include "wsconfig.h" // WsInfo.WsComputerName
+#include "wsmsg.h"
+#include "wsmain.h"
+#include <stdarg.h>
+
+//
+// Global variables
+//
+
+//
+// Information structure which contains the number of networks, the adapter
+// numbers of the networks, an array of computer name numbers, and an array
+// of broadcast name numbers.
+//
+WSNETWORKS WsNetworkInfo;
+// Flag for initialization
+BOOL fInitialize = FALSE;
+
+
+NET_API_STATUS
+WsInitializeMessageSend(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This function initializes the Workstation service to send messages using
+ NetBIOS by adding the computername to every network adapter (both logical
+ and physical network).
+
+Arguments:
+
+ None
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NET_API_STATUS status;
+ UCHAR Index;
+
+ CHAR NetBiosName[NCBNAMSZ];
+
+
+
+ //
+ // Get the adapter numbers of networks
+ //
+ status = NetpNetBiosGetAdapterNumbers(
+ &(WsNetworkInfo.LanAdapterNumbers),
+ sizeof(LANA_ENUM)
+ );
+
+ if (status != NERR_Success) {
+ //
+ // Fatal error: Log error with NELOG_NetBios
+ //
+ IF_DEBUG(MESSAGE) {
+ NetpKdPrint((
+ "[Wksta] Error enumerating LAN adapters. "
+ "Ignore if no UB card.\n"
+ ));
+ }
+ return status;
+ }
+
+ //
+ // Make the computer name a message type NetBIOS name
+ //
+ if ((status = NetpStringToNetBiosName(
+ NetBiosName,
+ WsInfo.WsComputerName,
+ NAMETYPE_MESSAGEDEST,
+ WKSTA_TO_MESSAGE_ALIAS_TYPE
+ )) != NERR_Success) {
+ return status;
+ }
+
+ //
+ // Add the computer name (message alias) to every network managed by
+ // the redirector, excluding the loopback network.
+ //
+ WsLmsvcsGlobalData->NetBiosOpen();
+
+ for (Index = 0; Index < WsNetworkInfo.LanAdapterNumbers.length; Index++) {
+
+ //
+ // Reset the adapter first
+ //
+ if (WsLmsvcsGlobalData->NetBiosReset(WsNetworkInfo.LanAdapterNumbers.lana[Index])
+ != NERR_Success) {
+ IF_DEBUG(MESSAGE) {
+ NetpKdPrint((
+ "[Wksta] Error reseting LAN adapter number %u.\n"
+ " Ignore if no UB card.\n",
+ WsNetworkInfo.LanAdapterNumbers.lana[Index]
+ ));
+ }
+ continue;
+ }
+
+ IF_DEBUG(MESSAGE) {
+ NetpKdPrint(("[Wksta] About to add name on adapter number %u\n",
+ WsNetworkInfo.LanAdapterNumbers.lana[Index]));
+ }
+
+ status = NetpNetBiosAddName(
+ NetBiosName,
+ WsNetworkInfo.LanAdapterNumbers.lana[Index],
+ &WsNetworkInfo.ComputerNameNumbers[Index]
+ );
+
+ if (status != NERR_Success && status != NERR_AlreadyExists) {
+ //
+ // Fatal error: Log error with NELOG_NetBios
+ //
+ IF_DEBUG(MESSAGE) {
+ NetpKdPrint((
+ "[Wksta] Error adding computername to LAN "
+ "Adapter number %u.\n Ignore if no UB card.\n",
+ WsNetworkInfo.LanAdapterNumbers.lana[Index]
+ ));
+ }
+ return status;
+ }
+ }
+
+ // Initialize okay
+ fInitialize = TRUE;
+ return NERR_Success;
+}
+
+
+VOID
+WsShutdownMessageSend(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This function shuts down the Workstation service message send
+ functionality by asking NetBIOS to delete the computername that
+ was added to every network adapter.
+
+Arguments:
+
+ None
+
+Return Value:
+
+ None.
+
+--*/
+{
+ // We shut down the component if and only if we successfully initialize
+ // the system
+ if ( fInitialize )
+ {
+ NET_API_STATUS status;
+ UCHAR Index;
+
+ CHAR NetBiosName[NCBNAMSZ];
+
+
+ //
+ // Make the computer name a message type NetBIOS name
+ //
+ if ((status = NetpStringToNetBiosName(
+ NetBiosName,
+ WsInfo.WsComputerName,
+ NAMETYPE_MESSAGEDEST,
+ WKSTA_TO_MESSAGE_ALIAS_TYPE
+ )) != NERR_Success) {
+ return;
+ }
+
+ //
+ // Delete the computer name (message alias) from every network.
+ //
+ for (Index = 0; Index < WsNetworkInfo.LanAdapterNumbers.length; Index++) {
+
+ (void) NetpNetBiosDelName(
+ NetBiosName,
+ WsNetworkInfo.LanAdapterNumbers.lana[Index]
+ );
+ }
+ WsLmsvcsGlobalData->NetBiosClose();
+ }
+}
+
+
+WORD
+WsMakeSmb(
+ OUT PUCHAR SmbBuffer,
+ IN UCHAR SmbFunctionCode,
+ IN WORD NumberOfParameters,
+ IN PCHAR FieldsDopeVector,
+ ...
+ )
+/*++
+
+Routine Description:
+
+ This function builds a Server Message Block. It takes a variable
+ number of arguments, but the first 4 are required to be present.
+ If NumberOfParameters is some non-zero value, n, then immediately
+ following the 4 required arguments there will be n WORD
+ parameters.
+
+Arguments:
+
+ SmbBuffer - Returns the Server Message Block in the supplied buffer.
+
+ SmbFunctionCode - Supplies the function code for the command.
+
+ NumberOfParameters - Supplies the number of WORD parameters passed
+ to this routine immediately following the first 4 required parameters.
+
+ FieldsDopeVector - Supplies an ASCIIZ string where each character of the
+ string describes the remaining parameters:
+
+ 's' - the next argument is a pointer to a null-terminated string
+ which is to be copied into the SMB prefixed by a byte
+ containing '\004'.
+
+ 'b' - the next argument is a WORD specifying a length,
+ and it is followed by a pointer to a buffer whose contents
+ are to be placed in the SMB prefixed by a byte containing
+ '\001' and a WORD containing the length.
+
+ 't' - the next argument is a WORD specifying a length,
+ and it is followed by a pointer to a text buffer whose
+ contents are to be placed in the SMB prefixed by a byte
+ containing '\001' and a WORD containg the length.
+ This is the same as 'b' except that <CRLF>,<LFCR>,<CR>,<LF>
+ are all converted to a single '\024' character.
+
+Return Value:
+
+ Returns the length in bytes of the SMB created in SmbBuffer.
+
+Assumptions:
+
+ The supplied SmbBuffer is large enough for the SMB created.
+
+--*/
+{
+ va_list ArgList; // Argument List
+ PSMB_HEADER Smb; // SMB header pointer
+ PUCHAR SmbBufferPointer;
+
+ PUCHAR LengthPointer; // length pointer
+ PCHAR TextPointer; // Text pointer
+ WORD TextBufferSize; // Size of SMB data to send
+
+ WORD i; // Text loop index
+ WORD Length; // Length after text conversion or
+ // length of the buffer portion
+
+
+
+ va_start(ArgList, FieldsDopeVector); // Init ArgList
+
+ RtlZeroMemory((PVOID) SmbBuffer, WS_SMB_BUFFER_SIZE);
+
+ Smb = (PSMB_HEADER) SmbBuffer;
+
+ Smb->Protocol[0] = 0xff; // Message type
+ Smb->Protocol[1] = 'S'; // Server
+ Smb->Protocol[2] = 'M'; // Message
+ Smb->Protocol[3] = 'B'; // Block
+
+ Smb->Command = SmbFunctionCode; // Set function code
+
+ //
+ // Skip over SMB header
+ //
+ SmbBufferPointer = &SmbBuffer[sizeof(SMB_HEADER)];
+
+ //
+ // Set parameter count
+ //
+ *SmbBufferPointer++ = (UCHAR) NumberOfParameters;
+
+ while (NumberOfParameters--) {
+
+ short Parameters = va_arg(ArgList, short);
+
+ //
+ // Put parameters in the SMB
+ //
+
+ //
+ // Assign message group id
+ //
+ *(SmbBufferPointer)++ = ((PUCHAR) &Parameters)[0];
+ *(SmbBufferPointer)++ = ((PUCHAR) &Parameters)[1];
+ }
+
+ //
+ // Save the pointer
+ //
+ Smb = (PSMB_HEADER) SmbBufferPointer;
+
+ //
+ // Skip data length field. After the rest of buffer is filled
+ // in, we will come back to set the length of the data.
+ //
+ SmbBufferPointer += sizeof(WORD);
+
+ while (*FieldsDopeVector != '\0') {
+
+ switch (*FieldsDopeVector++) {
+
+ case 's':
+ //
+ // Null-terminated string
+ //
+
+ //
+ // Set buffer type code
+ //
+ *SmbBufferPointer++ = '\004';
+
+ //
+ // Copy string into SMB buffer
+ //
+ strcpy(SmbBufferPointer, va_arg(ArgList, LPSTR));
+
+ //
+ // Increment pointer past string and null terminator
+ //
+ SmbBufferPointer += strlen(SmbBufferPointer) + 1;
+
+ break;
+
+ case 'b':
+ //
+ // Length-prefixed buffer
+ //
+
+ //
+ // Set buffer type code
+ //
+ *SmbBufferPointer++ = '\001';
+
+ //
+ // Get buffer size
+ //
+ TextBufferSize = va_arg(ArgList, WORD);
+
+ //
+ // Set the buffer length
+ //
+ *(SmbBufferPointer)++ = ((PUCHAR) &TextBufferSize)[0];
+ *(SmbBufferPointer)++ = ((PUCHAR) &TextBufferSize)[1];
+
+ //
+ // Move data into SMB buffer
+ //
+ memcpy(SmbBufferPointer, va_arg(ArgList, PUCHAR), TextBufferSize);
+
+ //
+ // Increment buffer pointer
+ //
+ SmbBufferPointer += TextBufferSize;
+
+ break;
+
+ case 't':
+
+ //
+ // Length-prefixed text buffer
+ //
+ *SmbBufferPointer++ = '\001';
+
+ //
+ // Get non converted text length
+ //
+ TextBufferSize = va_arg(ArgList, WORD);
+
+ IF_DEBUG(MESSAGE) {
+ NetpKdPrint(("[Wksta] WsMakeSmb TexBufferSize=%u\n",
+ TextBufferSize));
+ }
+
+
+ TextPointer = va_arg(ArgList, PCHAR);
+
+ //
+ // Where to put modified text length
+ //
+ LengthPointer = SmbBufferPointer;
+ SmbBufferPointer += sizeof(WORD);
+
+ //
+ // Now copy the text into the buffer converting all occurences
+ // of <CRLF>, <LFCR>, <CR>, <LF> to '\024'
+ //
+ for (i = 0, Length = 0; i < TextBufferSize; i++) {
+
+ if (*TextPointer == '\n') {
+
+ //
+ // Convert to IBM end of line
+ //
+ *SmbBufferPointer++ = '\024';
+ TextPointer++;
+ Length++;
+
+ //
+ // Ignore LF following CR
+ //
+ if (*TextPointer == '\r') {
+ TextPointer++;
+ i++;
+ }
+
+ }
+ else if (*TextPointer == '\r') {
+
+ //
+ // Convert to IBM end of line
+ //
+ *SmbBufferPointer++ = '\024';
+ TextPointer++;
+ Length++;
+
+ //
+ // Ignore CR following LF
+ //
+ if (*(TextPointer) == '\n') {
+ TextPointer++;
+ i++;
+ }
+
+ }
+ else {
+
+ *SmbBufferPointer++ = *TextPointer++;
+ Length++;
+ }
+
+ }
+
+ //
+ // Set the buffer length
+ //
+ *(LengthPointer)++ = ((PUCHAR) &Length)[0];
+ *(LengthPointer)++ = ((PUCHAR) &Length)[1];
+
+ break;
+ }
+ }
+
+ va_end(ArgList);
+
+ //
+ // Set length of buffer portion
+ //
+ Length = (WORD) ((DWORD) (SmbBufferPointer - (PUCHAR) Smb) - sizeof(WORD));
+ *((PUCHAR) Smb)++ = ((PUCHAR) &Length)[0];
+ *((PUCHAR) Smb)++ = ((PUCHAR) &Length)[1];
+
+ //
+ // Return length of SMB
+ //
+ return (WORD) (SmbBufferPointer - SmbBuffer);
+}
diff --git a/private/net/svcdlls/wkssvc/server/useaddel.c b/private/net/svcdlls/wkssvc/server/useaddel.c
new file mode 100644
index 000000000..4e75f3119
--- /dev/null
+++ b/private/net/svcdlls/wkssvc/server/useaddel.c
@@ -0,0 +1,1554 @@
+/*++
+
+Copyright (c) 1991-92 Microsoft Corporation
+
+Module Name:
+
+ useaddel.c
+
+Abstract:
+
+ This module contains the worker routines for the NetUseAdd and
+ NetUseDel APIs implemented in the Workstation service.
+
+Author:
+
+ Rita Wong (ritaw) 4-Mar-1991
+
+Revision History:
+
+--*/
+
+#include "wsutil.h"
+#include "wsdevice.h"
+#include "wsuse.h"
+
+//-------------------------------------------------------------------//
+// //
+// Local function prototypes //
+// //
+//-------------------------------------------------------------------//
+
+STATIC
+NET_API_STATUS
+WsAddUse(
+ IN PLUID LogonId,
+ IN HANDLE TreeConnection,
+ IN LPTSTR Local OPTIONAL,
+ IN DWORD LocalLength,
+ IN LPTSTR UncName,
+ IN DWORD UncNameLength,
+ IN PUNICODE_STRING TreeConnectStr
+ );
+
+STATIC
+NET_API_STATUS
+WsDeleteUse(
+ IN PLUID LogonId,
+ IN DWORD ForceLevel,
+ IN PUSE_ENTRY MatchedPointer
+ );
+
+
+STATIC
+NET_API_STATUS
+WsCreateNewEntry(
+ OUT PUSE_ENTRY *NewUse,
+ IN HANDLE TreeConnection,
+ IN LPTSTR Local OPTIONAL,
+ IN DWORD LocalLength,
+ IN LPTSTR UncName OPTIONAL,
+ IN DWORD UncNameLength,
+ IN PUNICODE_STRING TreeConnectStr
+ );
+
+STATIC
+NET_API_STATUS
+WsCheckLocalAndDeviceType(
+ IN LPTSTR Local,
+ IN DWORD DeviceType,
+ OUT LPDWORD ErrorParameter OPTIONAL
+ );
+
+STATIC
+NET_API_STATUS
+WsCheckEstablishedDeviceType(
+ IN HANDLE TreeConnection,
+ IN DWORD RequestedDeviceType
+ );
+
+STATIC
+NET_API_STATUS
+WsAllocateUseWorkBuffer(
+ IN PUSE_INFO_2 UseInfo,
+ IN DWORD Level,
+ OUT LPTSTR *UncName,
+ OUT LPTSTR *Local,
+ OUT LPTSTR *UserName,
+ OUT LPTSTR *DomainName
+ );
+
+#if DBG
+
+STATIC
+VOID
+DumpUseList(
+ DWORD Index
+ );
+
+#endif
+
+//-------------------------------------------------------------------//
+// //
+// Global variables //
+// //
+//-------------------------------------------------------------------//
+
+//
+// Monotonically incrementing integer. A unique value is assigned to
+// each new use entry created so that we can provide an enumeration
+// resume handle.
+//
+STATIC DWORD GlobalResumeKey = 0;
+
+//-------------------------------------------------------------------//
+// //
+// Macros //
+// //
+//-------------------------------------------------------------------//
+
+#define GET_USE_INFO_POINTER(UseInfo, InfoStruct) \
+ UseInfo = InfoStruct->UseInfo3;
+
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetrUseAdd(
+ IN LPTSTR ServerName OPTIONAL,
+ IN DWORD Level,
+ IN LPUSE_INFO InfoStruct,
+ OUT LPDWORD ErrorParameter OPTIONAL
+ )
+/*++
+
+Routine Description:
+
+ This function is the NetUseAdd entry point in the Workstation service.
+
+Arguments:
+
+ Level - Supplies the level of information specified in Buffer.
+
+ Buffer - Supplies the parameters to create the new tree connection with.
+
+ ErrorParameter - Returns the identifier to the invalid parameter in Buffer
+ if this function returns ERROR_INVALID_PARAMETER.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ LUID LogonId;
+ NET_API_STATUS status;
+
+ LPTSTR UncName = NULL;
+ DWORD UncNameLength = 0;
+ PTSTR Local = NULL;
+ DWORD LocalLength = 0;
+
+ LPTSTR UserName = NULL;
+ LPTSTR DomainName = NULL;
+ LPTSTR Password = NULL;
+ UNICODE_STRING EncodedPassword;
+ ULONG CreateFlags;
+
+ HANDLE TreeConnection;
+ UNICODE_STRING TreeConnectStr;
+
+ PUSE_INFO_3 pUseInfo;
+ PUSE_INFO_2 UseInfo;
+
+
+
+ UNREFERENCED_PARAMETER(ServerName);
+
+#define NETR_USE_ADD_PASSWORD_SEED 0x56 // Pick a non-zero seed
+ RtlInitUnicodeString( &EncodedPassword, NULL );
+
+ if (Level < 1 || Level > 3) {
+ return ERROR_INVALID_LEVEL;
+ }
+
+ GET_USE_INFO_POINTER(pUseInfo, InfoStruct);
+
+ if (pUseInfo == NULL) {
+ RETURN_INVALID_PARAMETER(ErrorParameter, PARM_ERROR_UNKNOWN);
+ }
+
+ //
+ // Cast a pointer to USE_INFO_2 to make things easy ...
+ //
+
+ UseInfo = &pUseInfo->ui3_ui2;
+
+ if(Level == 3)
+ {
+ CreateFlags = pUseInfo->ui3_flags;
+ }
+ else
+ {
+ CreateFlags = 0;
+ }
+
+ //
+ // UNC name can never be NULL or empty string.
+ //
+ if ((UseInfo->ui2_remote == NULL) ||
+ (UseInfo->ui2_remote[0] == TCHAR_EOS)) {
+ RETURN_INVALID_PARAMETER(ErrorParameter, USE_REMOTE_PARMNUM);
+ }
+
+ //
+ // Allocate one large buffer for storing the UNC name, local device name,
+ // username, and domain name.
+ //
+ if ((status = WsAllocateUseWorkBuffer(
+ UseInfo,
+ Level,
+ &UncName, // Free using this pointer
+ &Local,
+ &UserName,
+ &DomainName
+ )) != NERR_Success) {
+ return status;
+ }
+
+ //
+ // If local device is a NULL string, it will be treated as a pointer to
+ // NULL.
+ //
+ if ((UseInfo->ui2_local != NULL) &&
+ (UseInfo->ui2_local[0] != TCHAR_EOS)) {
+
+ //
+ // Local device name is not NULL, canonicalize it
+ //
+ if (WsUseCheckLocal(
+ UseInfo->ui2_local,
+ Local,
+ &LocalLength
+ ) != NERR_Success) {
+ (void) LocalFree(UncName);
+ RETURN_INVALID_PARAMETER(ErrorParameter, USE_LOCAL_PARMNUM);
+ }
+ }
+
+ //
+ // Check the format of the shared resource name.
+ //
+ if (WsUseCheckRemote(
+ UseInfo->ui2_remote,
+ UncName,
+ &UncNameLength
+ ) != NERR_Success) {
+ (void) LocalFree(UncName);
+ RETURN_INVALID_PARAMETER(ErrorParameter, USE_REMOTE_PARMNUM);
+ }
+
+ if ((Level >= 2) &&
+ (UseInfo->ui2_password != NULL) &&
+ (UseInfo->ui2_password[0] == TCHAR_EOS) &&
+ (UseInfo->ui2_username != NULL) &&
+ (UseInfo->ui2_username[0] == TCHAR_EOS) &&
+ (UseInfo->ui2_domainname != NULL) &&
+ (UseInfo->ui2_domainname[0] == TCHAR_EOS)) {
+
+ //
+ // The user explicitly specified an empty password, username, and
+ // domain. This means they want a null session.
+ //
+
+ *UserName = TCHAR_EOS;
+ *DomainName = TCHAR_EOS;
+ Password = TEXT("");
+
+ } else {
+
+ //
+ // Canonicalize user and domain names.
+ //
+
+ if (UserName != NULL) {
+
+ //
+ // Canonicalize username
+ //
+ if ((status = I_NetNameCanonicalize(
+ NULL,
+ UseInfo->ui2_username,
+ UserName,
+ (UNLEN + 1) * sizeof(TCHAR),
+ NAMETYPE_USER,
+ 0
+ )) != NERR_Success) {
+ (void) LocalFree(UncName);
+ RETURN_INVALID_PARAMETER(ErrorParameter, USE_USERNAME_PARMNUM);
+ }
+ }
+
+ if (DomainName != NULL) {
+ //
+ // Canonicalize domain name
+ // Canonicalize as a computername since a computername can be
+ // a valid domain (on the workstation to which you are connecting.
+ // This allows computernames with spaces to work.
+ //
+ if ((status = I_NetNameCanonicalize(
+ NULL,
+ UseInfo->ui2_domainname,
+ DomainName,
+ (DNLEN + 1) * sizeof(TCHAR),
+ NAMETYPE_COMPUTER,
+ 0
+ )) != NERR_Success) {
+ (void) LocalFree(UncName);
+ RETURN_INVALID_PARAMETER(ErrorParameter, USE_DOMAINNAME_PARMNUM);
+ }
+ }
+
+ //
+ // Make sure password length is not too long
+ //
+ if (UseInfo->ui2_password != NULL) {
+
+ Password = UseInfo->ui2_password;
+
+ if (STRLEN(Password) > PWLEN) {
+ (void) LocalFree(UncName);
+ RETURN_INVALID_PARAMETER(ErrorParameter, USE_PASSWORD_PARMNUM);
+ }
+
+ //
+ // Decode the password (the client obfuscated it.)
+ //
+
+ RtlInitUnicodeString( &EncodedPassword, Password );
+
+ RtlRunDecodeUnicodeString( NETR_USE_ADD_PASSWORD_SEED,
+ &EncodedPassword );
+
+ }
+
+ }
+ IF_DEBUG(USE) {
+ NetpKdPrint(("[Wksta] NetrUseAdd %ws %ws\n", Local, UncName));
+ }
+
+ //
+ // Check to see if the format of the local device name is correct based
+ // on the shared resource type to be accessed. This function also checks
+ // to see if the device is shared.
+ //
+ if ((status = WsCheckLocalAndDeviceType(
+ Local,
+ UseInfo->ui2_asg_type,
+ ErrorParameter
+ )) != NERR_Success) {
+ IF_DEBUG(USE) {
+ NetpKdPrint(("[Wksta] WsCheckLocalAndDeviceType return %lu\n", status));
+ }
+ goto FreeWorkBuffer;
+ }
+
+ //
+ // Replace \\ with \Device\LanmanRedirector in UncName, and create
+ // the NT-style tree connection names (without password or user name)
+ //
+ if ((status = WsCreateTreeConnectName(
+ UncName,
+ UncNameLength,
+ Local,
+ &TreeConnectStr
+ )) != NERR_Success) {
+ IF_DEBUG(USE) {
+ NetpKdPrint(("[Wksta] NetrUseAdd Bad tree connect name: %lu\n",
+ status));
+ }
+ goto FreeWorkBuffer;
+ }
+
+ //
+ // Impersonate caller and get the logon id
+ //
+ if ((status = WsImpersonateAndGetLogonId(&LogonId)) != NERR_Success) {
+ goto FreeAllocatedBuffers;
+ }
+
+ //
+ // Don't redirect comm or spooled devices if redirection is paused.
+ //
+ if( Local != NULL && WsRedirectionPaused(Local) ) {
+ IF_DEBUG(USE) {
+ NetpKdPrint(("[Wksta] NetrUseAdd Redirector paused\n"));
+ }
+ status = ERROR_REDIR_PAUSED;
+ goto FreeAllocatedBuffers;
+ }
+
+ if (Local != NULL) {
+
+ PUSE_ENTRY UseList;
+ DWORD Index;
+
+ //
+ // Lock Use Table so nobody will do anything destructive to it while
+ // we're in the middle of all this. If multiple threads are trying
+ // to redirect the same drive, only one will succeed creating the
+ // symbolic link, and the others will fail.
+ //
+
+ if (! RtlAcquireResourceShared(&Use.TableResource, TRUE)) {
+ status = NERR_InternalError;
+ goto FreeAllocatedBuffers;
+ }
+
+ //
+ // Look for the matching LogonId in the Use Table, if none matched
+ // create a new entry.
+ //
+ if (WsGetUserEntry(
+ &Use,
+ &LogonId,
+ &Index,
+ FALSE
+ ) != NERR_Success) {
+ UseList = NULL;
+ }
+ else {
+ UseList = Use.Table[Index].List;
+ }
+
+ //
+ // Create symbolic link for local device name. If there are multiple
+ // threads trying to do this, only one will succeed.
+ //
+ if ((status = WsCreateSymbolicLink(
+ Local,
+ UseInfo->ui2_asg_type,
+ TreeConnectStr.Buffer,
+ UseList
+ )) != NERR_Success) {
+
+ if ((ARGUMENT_PRESENT(ErrorParameter)) &&
+ (status == ERROR_INVALID_PARAMETER)) {
+ *ErrorParameter = USE_LOCAL_PARMNUM;
+ }
+ }
+
+ RtlReleaseResource(&Use.TableResource);
+
+ if( status )
+ goto FreeAllocatedBuffers;
+ }
+
+ //
+ // Create the tree connection if none already exists; otherwise, open it.
+ //
+ status = WsOpenCreateConnection(
+ &TreeConnectStr,
+ UserName,
+ DomainName,
+ Password,
+ CreateFlags,
+ FILE_OPEN_IF,
+ UseInfo->ui2_asg_type,
+ &TreeConnection,
+ NULL
+ );
+
+ if (status != NERR_Success) {
+ if (status == NERR_UseNotFound) {
+ status = ERROR_DEV_NOT_EXIST;
+ }
+ WsDeleteSymbolicLink( Local, TreeConnectStr.Buffer );
+ goto FreeAllocatedBuffers;
+ }
+
+ //
+ // Make sure user was correct about the shared resource type
+ //
+ if ((status = WsCheckEstablishedDeviceType(
+ TreeConnection,
+ UseInfo->ui2_asg_type
+ )) != NERR_Success) {
+ (void) NtClose(TreeConnection);
+ WsDeleteSymbolicLink( Local, TreeConnectStr.Buffer );
+ goto FreeAllocatedBuffers;
+ }
+
+ //
+ // Add use to the Use Table.
+ //
+ status = WsAddUse(
+ &LogonId,
+ TreeConnection,
+ Local,
+ LocalLength,
+ UncName,
+ UncNameLength,
+ &TreeConnectStr
+ );
+
+ if( status ) {
+ WsDeleteSymbolicLink( Local, TreeConnectStr.Buffer );
+ }
+
+FreeAllocatedBuffers:
+ //
+ // Free tree connection name buffer and work buffer
+ //
+ (void) LocalFree(TreeConnectStr.Buffer);
+
+FreeWorkBuffer:
+ (void) LocalFree(UncName);
+
+ //
+ // Put the password back the way we found it.
+ //
+
+ if ( EncodedPassword.Length != 0 ) {
+ UCHAR Seed = NETR_USE_ADD_PASSWORD_SEED;
+ RtlRunEncodeUnicodeString( &Seed, &EncodedPassword );
+ }
+
+ IF_DEBUG(USE) {
+ NetpKdPrint(("[Wksta] NetrUseAdd: about to return status=%lu\n",
+ status));
+ }
+
+ return status;
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetrUseDel (
+ IN LPTSTR ServerName OPTIONAL,
+ IN LPTSTR UseName,
+ IN DWORD ForceLevel
+ )
+/*++
+
+Routine Description:
+
+ This function is the NetUseDel entry point in the Workstation service.
+
+Arguments:
+
+ UseName - Supplies the local device name or shared resource name of
+ the tree connection to be deleted.
+
+ ForceLevel - Supplies the level of force to delete the tree connection.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NET_API_STATUS status;
+
+ LUID LogonId; // Logon Id of user
+ DWORD Index; // Index to user entry in Use Table
+
+ PUSE_ENTRY MatchedPointer; // Points to found use entry
+ PUSE_ENTRY BackPointer; // Points to node previous to
+ // found use entry
+ HANDLE TreeConnection; // Handle to connection
+
+ TCHAR FormattedUseName[MAX_PATH + 1];
+ // For canonicalizing a local device
+ // name
+ DWORD PathType = 0;
+
+ PUSE_ENTRY UseList;
+
+
+ UNREFERENCED_PARAMETER(ServerName);
+
+ //
+ // Check that ForceLevel parameter is valid
+ //
+ switch (ForceLevel) {
+
+ case USE_NOFORCE:
+ case USE_LOTS_OF_FORCE:
+ case USE_FORCE:
+ break;
+
+ default:
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ //
+ // Check to see if UseName is valid, and canonicalize it.
+ //
+ if (I_NetPathCanonicalize(
+ NULL,
+ UseName,
+ FormattedUseName,
+ sizeof( FormattedUseName ),
+ NULL,
+ &PathType,
+ 0
+ ) != NERR_Success) {
+ return NERR_UseNotFound;
+ }
+
+ IF_DEBUG(USE) {
+ NetpKdPrint(("\n[Wksta] NetrUseDel %ws %u, formatted use name %ws\n",
+ UseName, ForceLevel, FormattedUseName));
+ }
+
+ //
+ // Impersonate caller and get the logon id
+ //
+ if ((status = WsImpersonateAndGetLogonId(&LogonId)) != NERR_Success) {
+ return status;
+ }
+
+ //
+ // Lock Use Table while looking for entry to delete.
+ //
+ if (! RtlAcquireResourceExclusive(&Use.TableResource, TRUE)) {
+ return NERR_InternalError;
+ }
+
+ //
+ // See if the use entry is an explicit connection.
+ //
+ status = WsGetUserEntry(
+ &Use,
+ &LogonId,
+ &Index,
+ FALSE
+ );
+
+ UseList = (status == NERR_Success) ? (PUSE_ENTRY) Use.Table[Index].List :
+ NULL;
+
+ if ((status = WsFindUse(
+ &LogonId,
+ UseList,
+ FormattedUseName,
+ &TreeConnection,
+ &MatchedPointer,
+ &BackPointer
+ )) != NERR_Success) {
+ RtlReleaseResource(&Use.TableResource);
+ return status;
+ }
+
+ if (MatchedPointer == NULL) {
+
+ //
+ // UseName specified has an implicit connection. Don't need to hold
+ // on to Use Table anymore.
+ //
+ RtlReleaseResource(&Use.TableResource);
+
+ status = WsDeleteConnection(&LogonId, TreeConnection, ForceLevel);
+
+ //
+ // Close the connection handle if the API failed.
+ //
+
+ if (status != NERR_Success) {
+
+ NtClose(TreeConnection);
+
+ }
+
+ return status;
+
+ }
+ else if ((MatchedPointer->Local != NULL) &&
+ (MatchedPointer->LocalLength > 2)) {
+
+ //
+ // Don't allow delete on comm or spooled devices if redirection is
+ // paused for the current user.
+ //
+ if (WsRedirectionPaused(MatchedPointer->Local)) {
+ RtlReleaseResource(&Use.TableResource);
+ return ERROR_REDIR_PAUSED;
+ }
+ }
+
+ //
+ // Delete tree connection and remove use entry from Use Table. This function
+ // releases the TableResource
+ //
+ status = WsDeleteUse(
+ &LogonId,
+ ForceLevel,
+ MatchedPointer
+ );
+
+ IF_DEBUG(USE) {
+ NetpKdPrint(("[Wksta] NetrUseDel: about to return status=%lu\n", status));
+ }
+
+ return status;
+}
+
+
+
+STATIC
+NET_API_STATUS
+WsAddUse(
+ IN PLUID LogonId,
+ IN HANDLE TreeConnection,
+ IN LPTSTR Local OPTIONAL,
+ IN DWORD LocalLength,
+ IN LPTSTR UncName,
+ IN DWORD UncNameLength,
+ IN PUNICODE_STRING TreeConnectStr
+ )
+/*++
+
+Routine Description:
+
+ This function adds a use (tree connection) entry into the Use Table for
+ the user specified by the Logon Id. There is a linked list of uses for
+ each user. Each new use entry is inserted into the end of the linked
+ list so that enumeration of the list is resumable.
+
+ NOTE: This function locks the Use Table.
+ It also closes the tree connection in case of a error, or if a tree
+ connection to the same shared resource already exists.
+
+Arguments:
+
+ LogonId - Supplies a pointer to the user's Logon Id.
+
+ TreeConnection - Supplies the handle to the tree connection created.
+
+ Local - Supplies the string of the local device name.
+
+ LocalLength - Supplies the length of the local device name.
+
+ UncName - Supplies the name of the shared resource (UNC name).
+
+ UncNameLength - Supplies the length of the shared resource.
+
+ TreeConnectStr - Supplies the string of UNC name in NT-style format
+ (\Device\LanmanRedirector\X:\Orville\Razzle).
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NET_API_STATUS status;
+ DWORD Index; // Index to user entry in Use Table
+
+ PUSE_ENTRY MatchedPointer = NULL; // Points to matching shared resource
+ PUSE_ENTRY InsertPointer = NULL; // Point of insertion into use list
+ PUSE_ENTRY NewUse; // Pointer to the new use entry
+
+ if (! RtlAcquireResourceExclusive(&Use.TableResource, TRUE)) {
+ (void) NtClose(TreeConnection);
+ return NERR_InternalError;
+ }
+
+ //
+ // Look for the matching LogonId in the Use Table, if none matched
+ // create a new entry.
+ //
+ if ((status = WsGetUserEntry(
+ &Use,
+ LogonId,
+ &Index,
+ TRUE
+ )) != NERR_Success) {
+ RtlReleaseResource(&Use.TableResource);
+ (void) NtClose(TreeConnection);
+ return status;
+ }
+
+ if (Use.Table[Index].List != NULL) {
+
+ //
+ // Traverse use list to look for location to insert new use entry.
+ //
+ WsFindInsertLocation(
+ (PUSE_ENTRY) Use.Table[Index].List,
+ UncName,
+ &MatchedPointer,
+ &InsertPointer
+ );
+ }
+
+ if (MatchedPointer == NULL) {
+
+ //
+ // No matching UNC name found. Create a new entry with a
+ // corresponding remote entry.
+ //
+ if ((status = WsCreateNewEntry(
+ &NewUse,
+ TreeConnection,
+ Local,
+ LocalLength,
+ UncName,
+ UncNameLength,
+ TreeConnectStr
+ )) != NERR_Success) {
+ RtlReleaseResource(&Use.TableResource);
+ (void) NtClose(TreeConnection);
+ return status;
+ }
+ }
+ else {
+
+ //
+ // Matching UNC name found.
+ //
+
+ //
+ // It may be unnecessary to create a new use entry if the use
+ // we are adding has a NULL local device and a NULL local device
+ // entry already exists.
+ //
+ if (Local == NULL) {
+
+ if (MatchedPointer->Local == NULL) {
+
+ //
+ // Yes, there is a NULL local device entry already.
+ // Increment the use count and we are done.
+ //
+ MatchedPointer->UseCount++;
+ MatchedPointer->Remote->TotalUseCount++;
+
+#if DBG
+ DumpUseList(Index);
+#endif
+
+ RtlReleaseResource(&Use.TableResource);
+
+ //
+ // Close newly opened handle to the same tree connection because
+ // one already exists.
+ //
+ (void) NtClose(TreeConnection);
+
+ return NERR_Success;
+ }
+ }
+
+ //
+ // If we get here means we need to create a new use entry but not
+ // a corresponding remote entry because a use with the same UNC
+ // name already exists.
+ //
+ if ((status = WsCreateNewEntry(
+ &NewUse,
+ TreeConnection,
+ Local,
+ LocalLength,
+ NULL,
+ 0,
+ TreeConnectStr
+ )) != NERR_Success) {
+ RtlReleaseResource(&Use.TableResource);
+ (void) NtClose(TreeConnection);
+ return status;
+ }
+
+ NewUse->Remote = MatchedPointer->Remote;
+ NewUse->Remote->TotalUseCount++;
+ }
+
+ //
+ // Insert the new use entry into use list
+ //
+ if (InsertPointer == NULL) {
+ //
+ // Inserting into the head of list
+ //
+ Use.Table[Index].List = (PVOID) NewUse;
+ }
+ else {
+ InsertPointer->Next = NewUse;
+ }
+
+#if DBG
+ DumpUseList(Index);
+#endif
+
+ RtlReleaseResource(&Use.TableResource);
+ return NERR_Success;
+}
+
+
+
+STATIC
+NET_API_STATUS
+WsDeleteUse(
+ IN PLUID LogonId,
+ IN DWORD ForceLevel,
+ IN PUSE_ENTRY MatchedPointer
+ )
+/*++
+
+Routine Description:
+
+ This function removes the use entry pointed by MatchedPointer and
+ free it memory if it is a UNC connection deleted with force, or
+ it is a UNC connection deleted with no force and the use count is
+ decremented to 0, or it is a connection mapped to a local device.
+
+ WARNING: This function assumes that the Use.TableResource is claimed.
+ And it releases it on exit.
+
+Arguments:
+
+ LogonId - Supplies a pointer to the user's Logon Id.
+
+ ForceLevel - Supplies the level of force to delete.
+
+ MatchedPointer - Supplies the pointer to the use entry to be deleted.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ IN DWORD Index;
+ PUSE_ENTRY BackPointer;
+ NET_API_STATUS status;
+ DWORD ResumeKey;
+ HANDLE h;
+
+ //
+ // No need to remove entry if UNC connection is deleted with USE_NOFORCE
+ // level, and use count is not 0 after the deletion.
+ //
+ if ((MatchedPointer->Local == NULL) &&
+ (ForceLevel == USE_NOFORCE) &&
+ ((MatchedPointer->UseCount - 1) > 0)) {
+
+ MatchedPointer->UseCount--;
+ MatchedPointer->Remote->TotalUseCount--;
+ NetpAssert(MatchedPointer->Remote->TotalUseCount);
+
+ RtlReleaseResource(&Use.TableResource);
+ return NERR_Success;
+ }
+
+ ResumeKey = MatchedPointer->ResumeKey;
+ h = MatchedPointer->TreeConnection;
+ RtlReleaseResource(&Use.TableResource);
+
+ //
+ // Delete the tree connection and close the handle. We release the
+ // lock before calling because this can be a lengthy operation.
+ //
+ if ((status = WsDeleteConnection( LogonId, h, ForceLevel )) != NERR_Success) {
+ return status;
+ }
+
+ //
+ // Get the resource back and try to find our entry
+ //
+ if (! RtlAcquireResourceExclusive(&Use.TableResource, TRUE)) {
+ return NERR_InternalError;
+ }
+
+ status = WsGetUserEntry(
+ &Use,
+ LogonId,
+ &Index,
+ FALSE
+ );
+
+ if( status != NERR_Success ) {
+ //
+ // We were able to get the entry before, but we're unable to
+ // get it now. This must mean that it's gone away, so we'll
+ // return success
+ //
+ RtlReleaseResource( &Use.TableResource );
+ return NERR_Success;
+ }
+
+ //
+ // Now find the entry having the same ResumeKey that we (used to) have
+ //
+ BackPointer = MatchedPointer = (PUSE_ENTRY) Use.Table[Index].List ;
+ while( MatchedPointer != NULL ) {
+ if( MatchedPointer->ResumeKey == ResumeKey ) {
+ break;
+ } else {
+ BackPointer = MatchedPointer;
+ MatchedPointer = MatchedPointer->Next;
+ }
+ }
+
+ if( MatchedPointer == NULL ) {
+ //
+ // The entry is gone -- somebody must have deleted it. Return Success.
+ //
+ RtlReleaseResource(&Use.TableResource);
+ return NERR_Success;
+ }
+
+ //
+ // Successfully deleted connection, and refound our entry.
+ // Delete symbolic link, if any.
+ //
+ WsDeleteSymbolicLink(
+ MatchedPointer->Local,
+ MatchedPointer->TreeConnectStr
+ );
+
+ //
+ // Remove use entry from Use Table
+ //
+ if (MatchedPointer == BackPointer) {
+
+ //
+ // Use entry is the first one on the use list
+ //
+ Use.Table[Index].List = (PVOID) MatchedPointer->Next;
+ }
+ else {
+ BackPointer->Next = MatchedPointer->Next;
+ }
+
+ MatchedPointer->Remote->TotalUseCount -= MatchedPointer->UseCount;
+
+ if (MatchedPointer->Remote->TotalUseCount == 0) {
+ (void) LocalFree((HLOCAL) MatchedPointer->Remote);
+ }
+
+ (void) LocalFree((HLOCAL) MatchedPointer);
+
+ RtlReleaseResource(&Use.TableResource);
+ return status;
+}
+
+
+STATIC
+NET_API_STATUS
+WsCreateNewEntry(
+ OUT PUSE_ENTRY *NewUse,
+ IN HANDLE TreeConnection,
+ IN LPTSTR Local OPTIONAL,
+ IN DWORD LocalLength,
+ IN LPTSTR UncName OPTIONAL,
+ IN DWORD UncNameLength,
+ IN PUNICODE_STRING TreeConnectStr
+ )
+/*++
+
+Routine Description:
+
+ This function creates and initializes a new use entry. If the UncName
+ is specified, a new remote entry is created and initialized with
+ UncName.
+
+Arguments:
+
+ NewUse - Returns a pointer to the newly allocated and initialized use
+ entry.
+
+ TreeConnection - Supplies the handle to the tree connection to set in
+ the new use entry.
+
+ Local - Supplies the local device name string to be copied into the new
+ use entry.
+
+ LocalLength - Supplies the length of the local device name string.
+
+ UncName - Supplies the UNC name string to be copied into the new use entry.
+
+ UncNameLength - Supplies the length of the UNC name string.
+
+ TreeConnectStr - Supplies the string of UNC name in NT-style format
+ (\Device\LanmanRedirector\X:\Orville\Razzle).
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ PUNC_NAME NewRemoteEntry; // Common extension to use entries which
+ // share the same UNC connection.
+
+
+ //
+ // Allocate memory for new use. String length does not include zero
+ // terminator so add that.
+ //
+ if ((*NewUse = (PUSE_ENTRY) LocalAlloc(
+ LMEM_ZEROINIT,
+ ROUND_UP_COUNT(
+ sizeof(USE_ENTRY) + (LocalLength + 1)
+ * sizeof(TCHAR),
+ ALIGN_WCHAR
+ ) +
+ (ARGUMENT_PRESENT(Local) ?
+ TreeConnectStr->MaximumLength :
+ 0
+ )
+ )) == NULL) {
+ return GetLastError();
+ }
+
+ //
+ // Put use information into the new use node
+ //
+ (*NewUse)->Next = NULL;
+ (*NewUse)->LocalLength = LocalLength;
+ (*NewUse)->UseCount = 1;
+ (*NewUse)->TreeConnection = TreeConnection;
+ (*NewUse)->ResumeKey = GlobalResumeKey++;
+
+ //
+ // GlobalResumeKey wraps back to 0 if it is 0x80000000 because we use the
+ // high bit to indicate whether the resume handle for NetUseEnum comes
+ // from the workstation service or from the redirector.
+ //
+ GlobalResumeKey &= ~(REDIR_LIST);
+
+ //
+ // Copy local device name into use entry after the LocalLength field,
+ // if it is specified.
+ //
+ if (ARGUMENT_PRESENT(Local)) {
+ (*NewUse)->Local = (LPTSTR) ((DWORD) *NewUse + sizeof(USE_ENTRY));
+ STRCPY((*NewUse)->Local, Local);
+
+ (*NewUse)->TreeConnectStr = (LPWSTR) ROUND_UP_COUNT(
+ ((DWORD) *NewUse +
+ sizeof(USE_ENTRY) +
+ (LocalLength + 1) *
+ sizeof(TCHAR)),
+ ALIGN_WCHAR
+ );
+
+ wcscpy((*NewUse)->TreeConnectStr, TreeConnectStr->Buffer);
+ }
+ else {
+ (*NewUse)->Local = NULL;
+ (*NewUse)->TreeConnectStr = NULL;
+ }
+
+ //
+ // If shared resource name is specified, create a new remote entry to hold
+ // the UNC name, the tree connection handle, and total number of uses on
+ // this shared resource.
+ //
+ if (ARGUMENT_PRESENT(UncName)) {
+
+ if ((NewRemoteEntry = (PUNC_NAME) LocalAlloc(
+ LMEM_ZEROINIT,
+ (UINT) (sizeof(UNC_NAME) +
+ UncNameLength * sizeof(TCHAR))
+ )) == NULL) {
+ (void) LocalFree((HLOCAL) *NewUse);
+ return GetLastError();
+ }
+
+ STRCPY((LPWSTR) NewRemoteEntry->UncName, UncName);
+ NewRemoteEntry->UncNameLength = UncNameLength;
+ NewRemoteEntry->TotalUseCount = 1;
+// NewRemoteEntry->RedirUseInfo = NULL;
+
+ (*NewUse)->Remote = NewRemoteEntry;
+ }
+
+ return NERR_Success;
+}
+
+
+
+STATIC
+NET_API_STATUS
+WsCheckLocalAndDeviceType(
+ IN OUT LPTSTR Local,
+ IN DWORD DeviceType,
+ OUT LPDWORD ErrorParameter OPTIONAL
+ )
+/*++
+
+Routine Description:
+
+ This function checks the format of the specified local device name
+ based on the device type of shared resource to be accessed, and at
+ the same time verifies that the device type is valid.
+
+Arguments:
+
+ Local - Supplies the local device name. Returns its canonicalized
+ form.
+
+ DeviceType - Supplies the shared resource device type.
+
+ ErrorParameter - Returns the identifier to the invalid parameter in Buffer
+ if this function returns ERROR_INVALID_PARAMETER.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+
+ //
+ // Validate local device name based on the shared resource type.
+ //
+
+ //
+ // Check for wild card device type outside of the switch statement
+ // below because compiler complains about constant too big.
+ //
+ if (DeviceType == USE_WILDCARD || DeviceType == USE_IPC) {
+
+ //
+ // Local device name must be NULL for wild card or IPC connection.
+ //
+ if (Local == NULL) {
+ return NERR_Success;
+ }
+ else {
+ RETURN_INVALID_PARAMETER(ErrorParameter, USE_LOCAL_PARMNUM);
+ }
+ }
+
+ switch (DeviceType) {
+
+ case USE_DISKDEV:
+
+ if (Local == NULL) {
+ return NERR_Success;
+ }
+
+ //
+ // Local device name must have "<drive>:" format for disk
+ // device.
+ //
+ if (STRLEN(Local) != 2 || Local[1] != TCHAR_COLON) {
+ RETURN_INVALID_PARAMETER(ErrorParameter, USE_LOCAL_PARMNUM);
+ }
+
+ break;
+
+ case USE_SPOOLDEV:
+
+ if (Local == NULL) {
+ return NERR_Success;
+ }
+
+ //
+ // Local device name must have "LPTn:" or "PRN:" format
+ // for a print device.
+ //
+ if ((STRNICMP(Local, TEXT("PRN"), 3) != 0) &&
+ (STRNICMP(Local, TEXT("LPT"), 3) != 0)) {
+ RETURN_INVALID_PARAMETER(ErrorParameter, USE_LOCAL_PARMNUM);
+ }
+ break;
+
+ case USE_CHARDEV:
+
+ if (Local == NULL) {
+ return NERR_Success;
+ }
+
+ //
+ // Local device name must have "COMn:" or "AUX:" format
+ // for a comm device.
+ //
+ if ((STRNICMP(Local, TEXT("AUX"), 3) != 0) &&
+ (STRNICMP(Local, TEXT("COM"), 3) != 0)) {
+ RETURN_INVALID_PARAMETER(ErrorParameter, USE_LOCAL_PARMNUM);
+ }
+ break;
+
+
+ default:
+ IF_DEBUG(USE) {
+ NetpKdPrint((
+ "[Wksta] NetrUseAdd: Unknown shared resource type %lu\n",
+ DeviceType));
+ }
+
+ return NERR_BadAsgType;
+ }
+
+ return NERR_Success;
+}
+
+
+STATIC
+NET_API_STATUS
+WsCheckEstablishedDeviceType(
+ IN HANDLE TreeConnection,
+ IN DWORD RequestedDeviceType
+ )
+/*++
+
+Routine Description:
+
+ This function verifies that the device type of the shared resource we
+ have connected to is the same as the requested device type.
+
+Arguments:
+
+ TreeConnection - Supplies handle to established tree connection.
+
+ RequestedDeviceType - Supplies the shared resource device type specified
+ by the user to create the tree connection.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NTSTATUS ntstatus;
+ FILE_FS_DEVICE_INFORMATION FileInformation;
+ IO_STATUS_BLOCK IoStatusBlock;
+
+
+ ntstatus = NtQueryVolumeInformationFile(
+ TreeConnection,
+ &IoStatusBlock,
+ (PVOID) &FileInformation,
+ sizeof(FILE_FS_DEVICE_INFORMATION),
+ FileFsDeviceInformation
+ );
+
+ if (! NT_SUCCESS(ntstatus) || ! NT_SUCCESS(IoStatusBlock.Status)) {
+ return NERR_InternalError;
+ }
+
+ //
+ // Check for wild card device type outside of the switch statement
+ // below because compiler complains about constant too big.
+ //
+ if (RequestedDeviceType == USE_WILDCARD) {
+ return NERR_Success;
+ }
+
+ switch (RequestedDeviceType) {
+ case USE_DISKDEV:
+ if (FileInformation.DeviceType != FILE_DEVICE_DISK) {
+ return ERROR_BAD_DEV_TYPE;
+ }
+ break;
+
+ case USE_SPOOLDEV:
+ if (FileInformation.DeviceType != FILE_DEVICE_PRINTER) {
+ return ERROR_BAD_DEV_TYPE;
+ }
+ break;
+
+ case USE_CHARDEV:
+ if (FileInformation.DeviceType != FILE_DEVICE_SERIAL_PORT) {
+ return ERROR_BAD_DEV_TYPE;
+ }
+ break;
+
+ case USE_IPC:
+ if (FileInformation.DeviceType != FILE_DEVICE_NAMED_PIPE) {
+ return ERROR_BAD_DEV_TYPE;
+ }
+ break;
+
+ default:
+ //
+ // This should have been error checked earlier.
+ //
+ NetpKdPrint((
+ "WsCheckEstablishedDeviceType: Unknown device type.\n"
+ ));
+ NetpAssert(FALSE);
+ return ERROR_BAD_DEV_TYPE;
+ }
+
+ return NERR_Success;
+}
+
+
+STATIC
+NET_API_STATUS
+WsAllocateUseWorkBuffer(
+ IN PUSE_INFO_2 UseInfo,
+ IN DWORD Level,
+ OUT LPTSTR *UncName,
+ OUT LPTSTR *Local,
+ OUT LPTSTR *UserName,
+ OUT LPTSTR *DomainName
+ )
+/*++
+
+Routine Description:
+
+ This function allocates the work buffer for NetrUseAdd. The buffer
+ is the maximum need for canonicalizing and storing the strings
+ described below. If any of the strings is NULL, no memory is allocated
+ for it.
+
+ UncName - UNC name of remote resource. Cannot be NULL.
+
+ Local - local device name specified in the NetUseAdd. May be NULL.
+
+ UserName - username to establish connection with. May be NULL.
+
+ DomainName - domain name. Must be specified if UserName is,
+ otherwise if UserName is NULL this string is ignored.
+
+
+Arguments:
+
+ UseInfo - Supplies the input structure for NetUseAdd.
+
+ Level - Supplies the use info level.
+
+ Output pointers are set to point into allocated work buffer if its
+ corresponding input string is not NULL or empty.
+
+Return Value:
+
+ Error from LocalAlloc.
+
+--*/
+{
+ DWORD WorkBufferSize = (MAX_PATH + 1) * sizeof(TCHAR);
+ LPBYTE WorkBuffer;
+
+
+ if ((UseInfo->ui2_local != NULL) &&
+ (UseInfo->ui2_local[0] != TCHAR_EOS)) {
+ WorkBufferSize += (DEVLEN + 1) * sizeof(TCHAR);
+ }
+
+ if (Level >= 2) {
+ if (UseInfo->ui2_username != NULL) {
+ WorkBufferSize += (UNLEN + 1) * sizeof(TCHAR);
+ }
+
+ if (UseInfo->ui2_domainname != NULL) {
+ WorkBufferSize += (DNLEN + 1) * sizeof(TCHAR);
+ }
+ }
+
+
+ if ((WorkBuffer = (LPBYTE) LocalAlloc(
+ LMEM_ZEROINIT,
+ (UINT) WorkBufferSize
+ )) == NULL) {
+ return GetLastError();
+ }
+
+ *UncName = (LPTSTR) WorkBuffer;
+
+ IF_DEBUG(USE) {
+ NetpKdPrint((" Remote x%08lx\n", *UncName));
+ }
+
+ (DWORD) WorkBuffer += (MAX_PATH + 1) * sizeof(TCHAR);
+
+ if ((UseInfo->ui2_local != NULL) &&
+ (UseInfo->ui2_local[0] != TCHAR_EOS)) {
+ *Local = (LPTSTR) WorkBuffer;
+ (DWORD) WorkBuffer += (DEVLEN + 1) * sizeof(TCHAR);
+ }
+ else {
+ *Local = NULL;
+ }
+
+ IF_DEBUG(USE) {
+ NetpKdPrint((" Local x%08lx\n", *Local));
+ }
+
+
+ if (Level >= 2) {
+
+ if (UseInfo->ui2_username != NULL) {
+ *UserName = (LPTSTR) WorkBuffer;
+ (DWORD) WorkBuffer += (UNLEN + 1) * sizeof(TCHAR);
+ }
+ else {
+ *UserName = NULL;
+ }
+
+ if (UseInfo->ui2_domainname != NULL) {
+ *DomainName = (LPTSTR) WorkBuffer;
+ }
+ else {
+ *DomainName = NULL;
+ }
+ }
+
+ IF_DEBUG(USE) {
+ NetpKdPrint((" UserName x%08lx, DomainName x%08lx\n",
+ *UserName, *DomainName));
+ }
+
+ return NERR_Success;
+}
+
+
+#if DBG
+
+STATIC
+VOID
+DumpUseList(
+ DWORD Index
+ )
+/*++
+
+Routine Description:
+
+ This function dumps the user's use list for debugging purposes.
+
+Arguments:
+
+ Index - Supplies the index to the user entry in the Use Table.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PUSE_ENTRY UseList = (PUSE_ENTRY) Use.Table[Index].List;
+
+ IF_DEBUG(USE) {
+ NetpKdPrint(("\nDump Use List @%08lx\n", UseList));
+
+ while (UseList != NULL) {
+ NetpKdPrint(("%ws %ws\n", UseList->Local,
+ UseList->Remote->UncName));
+
+ NetpKdPrint(("usecount=%lu, totalusecount=%lu\n",
+ UseList->UseCount, UseList->Remote->TotalUseCount));
+ NetpKdPrint(("Connection handle %08lx, resume key=%lu\n",
+ UseList->TreeConnection, UseList->ResumeKey));
+
+ UseList = UseList->Next;
+ }
+ }
+}
+
+#endif
diff --git a/private/net/svcdlls/wkssvc/server/usegenum.c b/private/net/svcdlls/wkssvc/server/usegenum.c
new file mode 100644
index 000000000..c565a4c2d
--- /dev/null
+++ b/private/net/svcdlls/wkssvc/server/usegenum.c
@@ -0,0 +1,1364 @@
+/*++
+
+Copyright (c) 1991-1992 Microsoft Corporation
+
+Module Name:
+
+ usegenum.c
+
+Abstract:
+
+ This module contains the worker routines for the NetUseGetInfo and
+ NetUseEnum APIs implemented in the Workstation service.
+
+Author:
+
+ Rita Wong (ritaw) 13-Mar-1991
+
+Revision History:
+
+--*/
+
+#include "wsutil.h"
+#include "wsdevice.h"
+#include "wsuse.h"
+
+//-------------------------------------------------------------------//
+// //
+// Local function prototypes //
+// //
+//-------------------------------------------------------------------//
+
+STATIC
+NET_API_STATUS
+WsGetUseInfo(
+ IN PLUID LogonId,
+ IN DWORD Level,
+ IN HANDLE TreeConnection,
+ IN PUSE_ENTRY UseEntry,
+ OUT LPBYTE *OutputBuffer
+ );
+
+STATIC
+NET_API_STATUS
+WsEnumUseInfo(
+ IN PLUID LogonId,
+ IN DWORD Level,
+ IN PUSE_ENTRY UseList,
+ IN LPBYTE ImplicitList,
+ IN DWORD TotalImplicit,
+ OUT LPBYTE *OutputBuffer,
+ IN DWORD PreferedMaximumLength,
+ OUT LPDWORD EntriesRead,
+ OUT LPDWORD TotalEntries,
+ IN OUT LPDWORD ResumeHandle OPTIONAL
+ );
+
+STATIC
+NET_API_STATUS
+WsEnumCombinedUseInfo(
+ IN PLUID LogonId,
+ IN DWORD Level,
+ IN LPBYTE ImplicitList,
+ IN DWORD TotalImplicit,
+ IN PUSE_ENTRY UseList,
+ OUT LPBYTE OutputBuffer,
+ IN DWORD OutputBufferLength,
+ OUT LPDWORD EntriesRead,
+ OUT LPDWORD TotalEntries,
+ IN OUT LPDWORD ResumeHandle OPTIONAL
+ );
+
+STATIC
+NET_API_STATUS
+WsGetRedirUseInfo(
+ IN PLUID LogonId,
+ IN DWORD Level,
+ IN HANDLE TreeConnection,
+ OUT LPBYTE *OutputBuffer
+ );
+
+STATIC
+NET_API_STATUS
+WsGetCombinedUseInfo(
+ IN DWORD Level,
+ IN DWORD UseFixedLength,
+ IN PUSE_ENTRY UseEntry,
+ IN PLMR_CONNECTION_INFO_2 UncEntry,
+ IN OUT LPBYTE *FixedPortion,
+ IN OUT LPTSTR *EndOfVariableData,
+ IN OUT LPDWORD EntriesRead OPTIONAL
+ );
+
+STATIC
+BOOL
+WsFillUseBuffer(
+ IN DWORD Level,
+ IN PUSE_ENTRY UseEntry,
+ IN PLMR_CONNECTION_INFO_2 UncEntry,
+ IN OUT LPBYTE *FixedPortion,
+ IN OUT LPTSTR *EndOfVariableData,
+ IN DWORD UseFixedLength
+ );
+
+//-------------------------------------------------------------------//
+// //
+// Macros //
+// //
+//-------------------------------------------------------------------//
+
+#define SET_USE_INFO_POINTER(InfoStruct, ResultBuffer) \
+ InfoStruct->UseInfo2 = (PUSE_INFO_2) ResultBuffer;
+
+#define SET_USE_ENUM_POINTER(InfoStruct, ResultBuffer, NumRead) \
+ {InfoStruct->UseInfo.Level2->Buffer = (PUSE_INFO_2) ResultBuffer;\
+ InfoStruct->UseInfo.Level2->EntriesRead = NumRead;}
+
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetrUseGetInfo(
+ IN LPTSTR ServerName OPTIONAL,
+ IN LPTSTR UseName,
+ IN DWORD Level,
+ OUT LPUSE_INFO InfoStruct
+ )
+/*++
+
+Routine Description:
+
+ This function is the NetUseGetInfo entry point in the Workstation service.
+
+ This function assumes that UseName has been error checked and
+ canonicalized.
+
+Arguments:
+
+ UseName - Supplies the local device name or shared resource name of
+ the tree connection.
+
+ Level - Supplies the level of information to be returned regarding the
+ specified tree connection.
+
+ BufferPointer - Returns a pointer to the buffer allocated by the
+ Workstation service which contains the requested information.
+ This pointer is set to NULL if return code is not NERR_Success
+ or ERROR_MORE_DATA.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+
+{
+ NET_API_STATUS status;
+
+ LUID LogonId; // Logon Id of user
+ DWORD Index; // Index to user entry in Use Table
+
+ PUSE_ENTRY MatchedPointer; // Points to found use entry
+ HANDLE TreeConnection; // Handle to connection
+
+ TCHAR FormattedUseName[MAX_PATH + 1];
+ // For canonicalizing a local device
+ // name
+ DWORD PathType = 0;
+
+ LPBYTE Buffer = NULL;
+
+ PUSE_ENTRY UseList;
+
+ SET_USE_INFO_POINTER(InfoStruct, NULL);
+
+ UNREFERENCED_PARAMETER(ServerName);
+
+ if (Level > 2) {
+ return ERROR_INVALID_LEVEL;
+ }
+
+ //
+ // Check to see if UseName is valid, and canonicalize it.
+ //
+ if (I_NetPathCanonicalize(
+ NULL,
+ UseName,
+ FormattedUseName,
+ sizeof( FormattedUseName ),
+ NULL,
+ &PathType,
+ 0
+ ) != NERR_Success) {
+ return NERR_UseNotFound;
+ }
+
+ IF_DEBUG(USE) {
+ NetpKdPrint(("[Wksta] NetUseGetInfo %ws %lu\n", FormattedUseName, Level));
+ }
+
+ //
+ // Impersonate caller and get the logon id
+ //
+ if ((status = WsImpersonateAndGetLogonId(&LogonId)) != NERR_Success) {
+ return status;
+ }
+
+ //
+ // Lock Use Table for read access
+ //
+ if (! RtlAcquireResourceShared(&Use.TableResource, TRUE)) {
+ return NERR_InternalError;
+ }
+
+ //
+ // See if the use entry is an explicit connection.
+ //
+ status = WsGetUserEntry(
+ &Use,
+ &LogonId,
+ &Index,
+ FALSE
+ );
+
+ UseList = (status == NERR_Success) ? (PUSE_ENTRY) Use.Table[Index].List :
+ NULL;
+
+ if ((status = WsFindUse(
+ &LogonId,
+ UseList,
+ FormattedUseName,
+ &TreeConnection,
+ &MatchedPointer,
+ NULL
+ )) != NERR_Success) {
+ RtlReleaseResource(&Use.TableResource);
+ return status;
+ }
+
+ if (MatchedPointer == NULL) {
+
+ //
+ // UseName specified has an implicit connection. Don't need to hold
+ // on to Use Table anymore.
+ //
+ RtlReleaseResource(&Use.TableResource);
+ }
+
+ status = WsGetUseInfo(
+ &LogonId,
+ Level,
+ TreeConnection,
+ MatchedPointer,
+ &Buffer
+ );
+
+ if (MatchedPointer == NULL) {
+ //
+ // Close temporary handle to implicit connection.
+ //
+ NtClose(TreeConnection);
+ }
+ else {
+ RtlReleaseResource(&Use.TableResource);
+ }
+
+ SET_USE_INFO_POINTER(InfoStruct, Buffer);
+
+ IF_DEBUG(USE) {
+ NetpKdPrint(("[Wksta] NetrUseGetInfo: about to return status=%lu\n",
+ status));
+ }
+
+ return status;
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetrUseEnum(
+ IN LPTSTR ServerName OPTIONAL,
+ IN OUT LPUSE_ENUM_STRUCT InfoStruct,
+ IN DWORD PreferedMaximumLength,
+ OUT LPDWORD TotalEntries,
+ IN OUT LPDWORD ResumeHandle OPTIONAL
+ )
+/*++
+
+Routine Description:
+
+ This function is the NetUseEnum entry point in the Workstation service.
+
+Arguments:
+
+ ServerName - Supplies the name of server to execute this function
+
+ InfoStruct - This structure supplies the level of information requested,
+ returns a pointer to the buffer allocated by the Workstation service
+ which contains a sequence of information structure of the specified
+ information level, and returns the number of entries read. The buffer
+ pointer is set to NULL if return code is not NERR_Success or
+ ERROR_MORE_DATA, or if EntriesRead returned is 0. The EntriesRead
+ value is only valid if the return code is NERR_Success or
+ ERROR_MORE_DATA.
+
+ PreferedMaximumLength - Supplies the number of bytes of information
+ to return in the buffer. If this value is MAXULONG, all available
+ information will be returned.
+
+ TotalEntries - Returns the total number of entries available. This value
+ is only valid if the return code is NERR_Success or ERROR_MORE_DATA.
+
+ ResumeHandle - Supplies a handle to resume the enumeration from where it
+ left off the last time through. Returns the resume handle if return
+ code is ERROR_MORE_DATA.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NET_API_STATUS status;
+
+ LUID LogonId; // Logon Id of user
+ DWORD Index; // Index to user entry in Use Table
+ PUSE_ENTRY UseList; // Pointer to user's use list
+
+ DWORD EnumConnectionHint = 0; // Hint size from redirector
+ LMR_REQUEST_PACKET Rrp; // Redirector request packet
+
+ DWORD TotalImplicit; // Length of ImplicitList
+ LPBYTE ImplicitList; // List of information on implicit
+ // connections
+ LPBYTE Buffer = NULL;
+ DWORD EntriesRead = 0;
+ DWORD Level = InfoStruct->Level;
+
+ SET_USE_ENUM_POINTER(InfoStruct, NULL, 0);
+
+ UNREFERENCED_PARAMETER(ServerName);
+
+ if (Level > 2) {
+ return ERROR_INVALID_LEVEL;
+ }
+
+ //
+ // Impersonate caller and get the logon id
+ //
+ if ((status = WsImpersonateAndGetLogonId(&LogonId)) != NERR_Success) {
+ return status;
+ }
+
+ //
+ // Ask the redirector to enumerate the information of implicit connections
+ // established by the caller.
+ //
+ Rrp.Type = GetConnectionInfo;
+ Rrp.Version = REQUEST_PACKET_VERSION;
+ RtlCopyLuid(&Rrp.LogonId, &LogonId);
+ Rrp.Level = Level;
+ Rrp.Parameters.Get.ResumeHandle = 0;
+
+ if ((status = WsDeviceControlGetInfo(
+ Redirector,
+ WsRedirDeviceHandle,
+ FSCTL_LMR_ENUMERATE_CONNECTIONS,
+ &Rrp,
+ sizeof(LMR_REQUEST_PACKET),
+ (LPBYTE *) &ImplicitList,
+ MAXULONG,
+ EnumConnectionHint,
+ NULL
+ )) != NERR_Success) {
+ return status;
+ }
+
+ //
+ // If successful in getting all the implicit connection info from the
+ // redirector, expect the total entries available to be equal to entries
+ // read.
+ //
+ TotalImplicit = Rrp.Parameters.Get.TotalEntries;
+ NetpAssert(TotalImplicit == Rrp.Parameters.Get.EntriesRead);
+
+ //
+ // Serialize access to Use Table.
+ //
+ if (! RtlAcquireResourceShared(&Use.TableResource, TRUE)) {
+ status = NERR_InternalError;
+ goto CleanUp;
+ }
+
+ //
+ // See if the user has explicit connection entries in the Use Table.
+ //
+ status = WsGetUserEntry(
+ &Use,
+ &LogonId,
+ &Index,
+ FALSE
+ );
+
+ UseList = (status == NERR_Success) ? (PUSE_ENTRY) Use.Table[Index].List :
+ NULL;
+
+ //
+ // User has no connections if both implicit and explicit lists are empty.
+ //
+ if (TotalImplicit == 0 && UseList == NULL) {
+ *TotalEntries = 0;
+ status = NERR_Success;
+ goto CleanUp;
+ }
+
+ status = WsEnumUseInfo(
+ &LogonId,
+ Level,
+ UseList,
+ ImplicitList,
+ TotalImplicit,
+ &Buffer,
+ PreferedMaximumLength,
+ &EntriesRead,
+ TotalEntries,
+ ResumeHandle
+ );
+
+CleanUp:
+ MIDL_user_free(ImplicitList);
+
+ RtlReleaseResource(&Use.TableResource);
+
+ SET_USE_ENUM_POINTER(InfoStruct, Buffer, EntriesRead);
+
+ IF_DEBUG(USE) {
+ NetpKdPrint(("[Wksta] NetrUseEnum: about to return status=%lu\n",
+ status));
+ }
+
+ return status;
+}
+
+
+STATIC
+NET_API_STATUS
+WsGetUseInfo(
+ IN PLUID LogonId,
+ IN DWORD Level,
+ IN HANDLE TreeConnection,
+ IN PUSE_ENTRY UseEntry,
+ OUT LPBYTE *OutputBuffer
+ )
+/*++
+
+Routine Description:
+
+ This function allocates the output buffer of exactly the required size
+ and fill it with the use information that is requested by the caller of
+ NetUseGetInfo.
+
+Arguments:
+
+ LogonId - Supplies a pointer to the user's Logon Id.
+
+ Level - Supplies the level of information to be returned.
+
+ TreeConnection - Supplies the handle to the tree connection which user is
+ requesting information about.
+
+ UseEntry - Supplies a pointer to the use entry if the tree connection is
+ an explicit connection.
+
+ OutputBuffer - Returns a pointer to the buffer allocated by this
+ routine which contains the use information requested. This pointer
+ is set to NULL if return code is not NERR_Success.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NET_API_STATUS status;
+
+ DWORD OutputBufferLength;
+
+ LPBYTE FixedPortion;
+ LPTSTR EndOfVariableData;
+
+ PLMR_CONNECTION_INFO_2 ConnectionInfo;
+
+
+ //
+ // Get information of the requested connection from redirector
+ //
+ if ((status = WsGetRedirUseInfo(
+ LogonId,
+ Level,
+ TreeConnection,
+ (LPBYTE *) &ConnectionInfo
+ )) != NERR_Success) {
+ return status;
+ }
+
+ OutputBufferLength =
+ USE_TOTAL_LENGTH(
+ Level,
+ ((UseEntry != NULL) ?
+ (UseEntry->LocalLength + UseEntry->Remote->UncNameLength +
+ 2) * sizeof(TCHAR) :
+ ConnectionInfo->UNCName.Length + (2 * sizeof(TCHAR))),
+ ConnectionInfo->UserName.Length + sizeof(TCHAR)
+ );
+
+ if( Level >= 2 && ConnectionInfo->DomainName.Length != 0 ) {
+ OutputBufferLength += ConnectionInfo->DomainName.Length + sizeof(TCHAR);
+ }
+
+ //
+ // Allocate output buffer to be filled in and returned to user
+ //
+ if ((*OutputBuffer = MIDL_user_allocate(OutputBufferLength)) == NULL) {
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+ RtlZeroMemory((PVOID) *OutputBuffer, OutputBufferLength);
+
+ FixedPortion = *OutputBuffer;
+ EndOfVariableData = (LPTSTR) ((DWORD) FixedPortion + OutputBufferLength);
+
+ //
+ // Combine the redirector information (if any) with the use entry
+ // information into one output buffer.
+ //
+ status = WsGetCombinedUseInfo(
+ Level,
+ USE_FIXED_LENGTH(Level),
+ UseEntry,
+ ConnectionInfo,
+ &FixedPortion,
+ &EndOfVariableData,
+ NULL
+ );
+
+ //
+ // We should have allocated enough memory for all the data
+ //
+ NetpAssert(status == NERR_Success);
+
+ //
+ // If not successful in getting any data, free the output buffer and set
+ // it to NULL.
+ //
+ if (status != NERR_Success) {
+ MIDL_user_free(*OutputBuffer);
+ *OutputBuffer = NULL;
+ }
+
+ MIDL_user_free(ConnectionInfo);
+
+ return status;
+}
+
+
+STATIC
+NET_API_STATUS
+WsEnumUseInfo(
+ IN PLUID LogonId,
+ IN DWORD Level,
+ IN PUSE_ENTRY UseList,
+ IN LPBYTE ImplicitList,
+ IN DWORD TotalImplicit,
+ OUT LPBYTE *OutputBuffer,
+ IN DWORD PreferedMaximumLength,
+ OUT LPDWORD EntriesRead,
+ OUT LPDWORD TotalEntries,
+ IN OUT LPDWORD ResumeHandle OPTIONAL
+ )
+/*++
+
+Routine Description:
+
+ This function allocates the output buffer of exactly the required size
+ and fill it with the use information that is requested by the caller of
+ NetUseEnum.
+
+Arguments:
+
+ LogonId - Supplies a pointer to the user's Logon Id.
+
+ Level - Supplies the level of information to be returned.
+
+ UseList - Supplies a pointer to the use list.
+
+ ImplicitList - Supplies an array of information from the redirector
+ about each implicit connection.
+
+ TotalImplicit - Supplies the number of entries in ImplicitList.
+
+ OutputBuffer - Returns a pointer to the buffer allocated by this
+ routine which contains the use information requested. This pointer
+ is set to NULL if return code is not NERR_Success.
+
+ PreferedMaximumLength - Supplies the number of bytes of information
+ to return in the buffer. If this value is MAXULONG, we will try
+ to return all available information if there is enough memory
+ resource.
+
+ EntriesRead - Returns the number of entries read into the buffer. This
+ value is returned only if the return code is NERR_Success or
+ ERROR_MORE_DATA.
+
+ TotalEntries - Returns the remaining total number of entries that would
+ be read into output buffer if it has enough memory to hold all entries.
+ This value is returned only if the return code is NERR_Success or
+ ERROR_MORE_DATA.
+
+ ResumeHandle - Supplies the resume key to begin enumeration, and returns
+ the key to the next entry to resume the enumeration if the current
+ call returns ERROR_MORE_DATA.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NET_API_STATUS status;
+ DWORD i;
+ DWORD OutputBufferLength = 0;
+ PUSE_ENTRY UseEntry = UseList;
+ DWORD TotalExplicit = 0;
+
+ //
+ // Get the use information from the redirector for each explicit connection
+ //
+ while (UseEntry != NULL) {
+
+ PLMR_CONNECTION_INFO_2 ci2;
+
+ //
+ // Get tree connection information from the redirector.
+ //
+
+ ci2 = NULL;
+
+ if ((status = WsGetRedirUseInfo(
+ LogonId,
+ Level,
+ UseEntry->TreeConnection,
+ (LPBYTE *) &ci2
+ )) != NERR_Success) {
+
+ if( ci2 != NULL )
+ MIDL_user_free( ci2 );
+
+ return status;
+ }
+
+
+ if( ci2 == NULL ) {
+ return NERR_InternalError;
+ }
+
+ //
+ // While we are here, add up the amount of memory needed to hold the
+ // explicit connection entries including information from the redir
+ // like username.
+ //
+ if (PreferedMaximumLength == MAXULONG) {
+ OutputBufferLength +=
+ USE_TOTAL_LENGTH(
+ Level,
+ (UseEntry->LocalLength + UseEntry->Remote->UncNameLength +
+ 2) * sizeof(TCHAR),
+ ci2->UserName.Length +
+ sizeof(TCHAR)
+ );
+
+ if( Level >= 2 && ci2->DomainName.Length != 0 ) {
+ OutputBufferLength += ci2->DomainName.Length + sizeof(TCHAR);
+ }
+ }
+
+ MIDL_user_free( ci2 );
+
+ //
+ // Sum up the number of explicit connections.
+ //
+ TotalExplicit++;
+
+ UseEntry = UseEntry->Next;
+ }
+
+ IF_DEBUG(USE) {
+ NetpKdPrint(("[Wksta] NetrUseEnum: length of explicit info %lu\n",
+ OutputBufferLength));
+ }
+
+ //
+ // If the user requests to enumerate all use entries with
+ // PreferedMaximumLength == MAXULONG, add up the total number of bytes
+ // we need to allocate for the output buffer. We know the amount we
+ // need for explicit connections from above; now add the lengths of
+ // implicit connection information.
+ //
+
+ if (PreferedMaximumLength == MAXULONG) {
+
+ //
+ // Pointer to the next entry in the ImplicitList is computed based
+ // on the level of information requested from the redirector.
+ //
+ LPBYTE ImplicitEntry;
+ DWORD ImplicitEntryLength = REDIR_ENUM_INFO_FIXED_LENGTH(Level);
+
+
+ //
+ // Add up the buffer size needed to hold the implicit connection
+ // information
+ //
+ for (ImplicitEntry = ImplicitList, i = 0; i < TotalImplicit;
+ (DWORD) ImplicitEntry += ImplicitEntryLength, i++) {
+
+ OutputBufferLength +=
+ USE_TOTAL_LENGTH(
+ Level,
+ ((PLMR_CONNECTION_INFO_2) ImplicitEntry)->UNCName.Length
+ + (2 * sizeof(TCHAR)),
+ ((PLMR_CONNECTION_INFO_2) ImplicitEntry)->UserName.Length
+ + sizeof(TCHAR)
+ );
+
+ if( Level >= 2 ) {
+ OutputBufferLength += (DNLEN + 1)*sizeof(TCHAR);
+ }
+ }
+
+ IF_DEBUG(USE) {
+ NetpKdPrint((
+ "[Wksta] NetrUseEnum: length of implicit & explicit info %lu\n",
+ OutputBufferLength));
+ }
+
+ }
+ else {
+
+ //
+ // We will return as much as possible that fits into this specified
+ // buffer size.
+ //
+ OutputBufferLength = ROUND_UP_COUNT(PreferedMaximumLength, ALIGN_WCHAR);
+
+ if (OutputBufferLength < USE_FIXED_LENGTH(Level)) {
+
+ *OutputBuffer = NULL;
+ *EntriesRead = 0;
+ *TotalEntries = TotalExplicit + TotalImplicit;
+
+ return ERROR_MORE_DATA;
+ }
+ }
+
+
+ //
+ // Allocate the output buffer
+ //
+ if ((*OutputBuffer = MIDL_user_allocate(OutputBufferLength)) == NULL) {
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+ RtlZeroMemory((PVOID) *OutputBuffer, OutputBufferLength);
+
+
+ //
+ // Get the information
+ //
+ status = WsEnumCombinedUseInfo(
+ LogonId,
+ Level,
+ ImplicitList,
+ TotalImplicit,
+ UseList,
+ *OutputBuffer,
+ OutputBufferLength,
+ EntriesRead,
+ TotalEntries,
+ ResumeHandle
+ );
+
+ //
+ // WsEnumCombinedUseInfo returns in *TotalEntries the number of
+ // remaining unread entries. Therefore, the real total is the
+ // sum of this returned value and the number of entries read.
+ //
+ (*TotalEntries) += (*EntriesRead);
+
+ //
+ // If the caller asked for all available data with
+ // PreferedMaximumLength == MAXULONG and our buffer overflowed, free the
+ // output buffer and set its pointer to NULL.
+ //
+ if (PreferedMaximumLength == MAXULONG && status == ERROR_MORE_DATA) {
+
+ MIDL_user_free(*OutputBuffer);
+ *OutputBuffer = NULL;
+
+ //
+ // PreferedMaximumLength == MAXULONG and buffer overflowed means
+ // we do not have enough memory to satisfy the request.
+ //
+ if (status == ERROR_MORE_DATA) {
+ status = ERROR_NOT_ENOUGH_MEMORY;
+ }
+ }
+ else {
+
+ if (*EntriesRead == 0) {
+ MIDL_user_free(*OutputBuffer);
+ *OutputBuffer = NULL;
+ }
+ }
+
+ return status;
+}
+
+
+STATIC
+NET_API_STATUS
+WsEnumCombinedUseInfo(
+ IN PLUID LogonId,
+ IN DWORD Level,
+ IN LPBYTE ImplicitList,
+ IN DWORD TotalImplicit,
+ IN PUSE_ENTRY UseList,
+ OUT LPBYTE OutputBuffer,
+ IN DWORD OutputBufferLength,
+ OUT LPDWORD EntriesRead,
+ OUT LPDWORD EntriesUnread,
+ IN OUT LPDWORD ResumeHandle OPTIONAL
+ )
+/*++
+
+Routine Description:
+
+ This function lists all existing connections by going through the
+ the explicit connections in the Use Table, and the implicit connections
+ from the redirector.
+
+Arguments:
+
+ Level - Supplies the level of information to be returned.
+
+ ImplicitList - Supplies an array implicit connections from the redirector.
+
+ TotalImplicit - Supplies the number of entries in ImplicitList.
+
+ UseList - Supplies a pointer to the use list.
+
+ OutputBuffer - Supplies the output buffer which receives the requested
+ information.
+
+ OutputBufferLength - Supplies the length of the output buffer.
+
+ EntriesRead - Returns the number of entries written into the output
+ buffer.
+
+ EntriesUnread - Returns the remaining total number of unread entries.
+ This value is returned only if the return code is NERR_Success or
+ ERROR_MORE_DATA.
+
+ ResumeHandle - Supplies the resume key to begin enumeration, and returns
+ the key to the next entry to resume the enumeration if the current
+ call returns ERROR_MORE_DATA.
+
+Return Value:
+
+ NERR_Success - All entries fit into the output buffer.
+
+ ERROR_MORE_DATA - 0 or more entries were written into the output buffer
+ but not all entries fit.
+
+--*/
+{
+ DWORD i;
+ NET_API_STATUS status;
+ DWORD UseFixedLength = USE_FIXED_LENGTH(Level);
+
+ LPBYTE FixedPortion = OutputBuffer;
+ LPTSTR EndOfVariableData = (LPTSTR) ((DWORD) FixedPortion +
+ OutputBufferLength);
+ //
+ // Pointer to the next entry in the ImplicitList is computed based on the
+ // level of information requested from the redirector.
+ //
+ LPBYTE ImplicitEntry;
+ DWORD ImplicitEntryLength = REDIR_ENUM_INFO_FIXED_LENGTH(Level);
+
+ DWORD StartEnumeration = 0;
+ BOOL OnlyRedirectorList = FALSE;
+
+
+ if (ARGUMENT_PRESENT(ResumeHandle)) {
+ StartEnumeration = *ResumeHandle & ~(REDIR_LIST);
+ OnlyRedirectorList = *ResumeHandle & REDIR_LIST;
+ }
+
+ IF_DEBUG(USE) {
+ NetpKdPrint(("\nStartEnumeration=%lu\n, OnlyRedir=%u\n",
+ StartEnumeration, OnlyRedirectorList));
+ }
+
+ *EntriesRead = 0;
+
+ //
+ // Enumerate explicit connections. This is done only if resume handle
+ // says to start enumeration from the explicit list.
+ //
+ if (! OnlyRedirectorList) {
+
+ for( ; UseList != NULL; UseList = UseList->Next ) {
+
+ PLMR_CONNECTION_INFO_2 ci2;
+
+ if( StartEnumeration > UseList->ResumeKey ) {
+ continue;
+ }
+
+ //
+ // Get tree connection information from the redirector.
+ //
+
+ ci2 = NULL;
+
+ status = WsGetRedirUseInfo( LogonId, Level, UseList->TreeConnection, (LPBYTE *) &ci2 );
+
+ if( status != NERR_Success || ci2 == NULL ) {
+ if( ci2 != NULL )
+ MIDL_user_free( ci2 );
+ continue;
+ }
+
+ status = WsGetCombinedUseInfo(
+ Level,
+ UseFixedLength,
+ UseList,
+ ci2,
+ &FixedPortion,
+ &EndOfVariableData,
+ EntriesRead );
+
+ MIDL_user_free( ci2 );
+
+ if( status == ERROR_MORE_DATA ) {
+
+ if (ARGUMENT_PRESENT(ResumeHandle)) {
+ *ResumeHandle = UseList->ResumeKey;
+ }
+
+ *EntriesUnread = TotalImplicit;
+
+ while (UseList != NULL) {
+ (*EntriesUnread)++;
+ UseList = UseList->Next;
+ }
+
+ return status;
+ }
+ }
+
+ //
+ // Finished the explicit list. Start from the beginning of implicit
+ // list.
+ //
+ StartEnumeration = 0;
+ }
+
+ //
+ // Enumerate implicit connections
+ //
+ for (ImplicitEntry = ImplicitList, i = 0; i < TotalImplicit;
+ (DWORD) ImplicitEntry += ImplicitEntryLength, i++) {
+
+ IF_DEBUG(USE) {
+ NetpKdPrint(("RedirList->ResumeKey=%lu\n",
+ ((PLMR_CONNECTION_INFO_2) ImplicitEntry)->ResumeKey));
+ }
+
+ if (StartEnumeration <=
+ ((PLMR_CONNECTION_INFO_2) ImplicitEntry)->ResumeKey) {
+
+ if (WsGetCombinedUseInfo(
+ Level,
+ UseFixedLength,
+ NULL,
+ (PLMR_CONNECTION_INFO_2) ImplicitEntry,
+ &FixedPortion,
+ &EndOfVariableData,
+ EntriesRead
+ ) == ERROR_MORE_DATA) {
+
+ if (ARGUMENT_PRESENT(ResumeHandle)) {
+ *ResumeHandle = ((PLMR_CONNECTION_INFO_2)
+ ImplicitEntry)->ResumeKey;
+ *ResumeHandle |= REDIR_LIST;
+ }
+
+ *EntriesUnread = TotalImplicit - i;
+
+ return ERROR_MORE_DATA;
+ }
+ }
+ }
+
+ //
+ // Successful enumeration. Reset the resume handle to start from the
+ // beginning.
+ //
+ if (ARGUMENT_PRESENT(ResumeHandle)) {
+ *ResumeHandle = 0;
+ }
+
+ //
+ // There are no more remaining entries.
+ //
+ *EntriesUnread = 0;
+
+ return NERR_Success;
+}
+
+
+STATIC
+NET_API_STATUS
+WsGetRedirUseInfo(
+ IN PLUID LogonId,
+ IN DWORD Level,
+ IN HANDLE TreeConnection,
+ OUT LPBYTE *OutputBuffer
+ )
+/*++
+
+Routine Description:
+
+ This function gets the connection information from the redirector given
+ the handle to the connection.
+
+Arguments:
+
+ LogonId - Supplies a pointer to the user's Logon Id.
+
+ Level - Supplies the level of information to be returned.
+
+ TreeConnection - Supplies the handle to the tree connection which user is
+ requesting information about.
+
+ OutputBuffer - Returns a pointer to the buffer allocated by this
+ routine which contains the connection information requested. This
+ pointer is set to NULL if return code is not NERR_Success.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ LMR_REQUEST_PACKET Rrp;
+
+ //
+ // Get information of the requested connection from redirector
+ //
+ Rrp.Type = GetConnectionInfo;
+ Rrp.Version = REQUEST_PACKET_VERSION;
+ RtlCopyLuid(&Rrp.LogonId, LogonId);
+ Rrp.Level = Level;
+
+ return WsDeviceControlGetInfo(
+ Redirector,
+ TreeConnection,
+ FSCTL_LMR_GET_CONNECTION_INFO,
+ &Rrp,
+ sizeof(LMR_REQUEST_PACKET),
+ OutputBuffer,
+ MAXULONG,
+ HINT_REDIR_INFO(Level),
+ NULL
+ );
+}
+
+
+
+STATIC
+NET_API_STATUS
+WsGetCombinedUseInfo(
+ IN DWORD Level,
+ IN DWORD UseFixedLength,
+ IN PUSE_ENTRY UseEntry,
+ IN PLMR_CONNECTION_INFO_2 UncEntry,
+ IN OUT LPBYTE *FixedPortion,
+ IN OUT LPTSTR *EndOfVariableData,
+ IN OUT LPDWORD EntriesRead OPTIONAL
+ )
+/*++
+
+Routine Description:
+
+ This function puts together the use information from redirector and from
+ the Use Table (if any) into the output buffer. It increments the
+ EntriesRead variable when a use entry is written into the output buffer.
+
+Arguments:
+
+ Level - Supplies the level of information to be returned.
+
+ UseFixedLength - Supplies the length of the fixed portion of the use
+ information returned.
+
+ UseEntry - Supplies the pointer to the use entry in the Use Table if it
+ is an explicit connection.
+
+ UncEntry - Supplies a pointer to the use information retrieved from the
+ redirector.
+
+ FixedPortion - Supplies a pointer to the output buffer where the next
+ entry of the fixed portion of the use information will be written.
+ This pointer is updated to point to the next fixed portion entry
+ after a use entry is written.
+
+ EndOfVariableData - Supplies a pointer just off the last available byte
+ in the output buffer. This is because the variable portion of the use
+ information is written into the output buffer starting from the end.
+ This pointer is updated after any variable length information is
+ written to the output buffer.
+
+ EntriesRead - Supplies a running total of the number of entries read
+ into the buffer. This value is incremented every time a use entry is
+ successfully written into the output buffer.
+
+Return Value:
+
+ NERR_Success - The current entry fits into the output buffer.
+
+ ERROR_MORE_DATA - The current entry does not fit into the output buffer.
+
+--*/
+{
+ if (((DWORD) *FixedPortion + UseFixedLength) >=
+ (DWORD) *EndOfVariableData) {
+
+ //
+ // Fixed length portion does not fit.
+ //
+ return ERROR_MORE_DATA;
+ }
+
+ if (! WsFillUseBuffer(
+ Level,
+ UseEntry,
+ UncEntry,
+ FixedPortion,
+ EndOfVariableData,
+ UseFixedLength
+ )) {
+
+ //
+ // Variable length portion does not fit.
+ //
+ return ERROR_MORE_DATA;
+ }
+
+ if (ARGUMENT_PRESENT(EntriesRead)) {
+ (*EntriesRead)++;
+ }
+
+ return NERR_Success;
+}
+
+
+STATIC
+BOOL
+WsFillUseBuffer(
+ IN DWORD Level,
+ IN PUSE_ENTRY UseEntry,
+ IN PLMR_CONNECTION_INFO_2 UncEntry,
+ IN OUT LPBYTE *FixedPortion,
+ IN OUT LPTSTR *EndOfVariableData,
+ IN DWORD UseFixedLength
+ )
+/*++
+
+Routine Description:
+
+ This function fills an entry in the output buffer with the supplied use
+ information, and updates the FixedPortion and EndOfVariableData pointers.
+
+ NOTE: This function assumes that the fixed size portion will fit into
+ the output buffer.
+
+ It also assumes that info structure level 2 is a superset of
+ info structure level 1, which in turn is a superset of info
+ structure level 0, and that the offset to each common field is
+ exactly the same. This allows us to take advantage of a switch
+ statement without a break between the levels.
+
+Arguments:
+
+ Level - Supplies the level of information to be returned.
+
+ UseEntry - Supplies the pointer to the use entry in the Use Table if it is
+ an explicit connection.
+
+ UncEntry - Supplies a pointer to the use information retrieved from the
+ redirector.
+
+ FixedPortion - Supplies a pointer to the output buffer where the next
+ entry of the fixed portion of the use information will be written.
+ This pointer is updated after a use entry is written to the
+ output buffer.
+
+ EndOfVariableData - Supplies a pointer just off the last available byte
+ in the output buffer. This is because the variable portion of the use
+ information is written into the output buffer starting from the end.
+ This pointer is updated after any variable length information is
+ written to the output buffer.
+
+ UseFixedLength - Supplies the number of bytes needed to hold the fixed
+ size portion.
+
+Return Value:
+
+ Returns TRUE if entire entry fits into output buffer, FALSE otherwise.
+
+--*/
+{
+ PUSE_INFO_2 UseInfo = (PUSE_INFO_2) *FixedPortion;
+
+
+ (DWORD) (*FixedPortion) += UseFixedLength;
+
+ switch (Level) {
+
+ case 2:
+ if (! NetpCopyStringToBuffer(
+ UncEntry->UserName.Buffer,
+ UncEntry->UserName.Length / sizeof(TCHAR),
+ *FixedPortion,
+ EndOfVariableData,
+ &UseInfo->ui2_username
+ )) {
+ return FALSE;
+ }
+
+ if( UncEntry->DomainName.Length != 0 ) {
+ if(! NetpCopyStringToBuffer(
+ UncEntry->DomainName.Buffer,
+ UncEntry->DomainName.Length / sizeof(TCHAR),
+ *FixedPortion,
+ EndOfVariableData,
+ &UseInfo->ui2_domainname
+ )) {
+ return FALSE;
+ }
+ }
+
+ case 1:
+
+ UseInfo->ui2_password = NULL;
+
+ UseInfo->ui2_status = UncEntry->ConnectionStatus;
+
+ if ((UseEntry != NULL) && (UseEntry->Local != NULL)
+ && (UseEntry->LocalLength > 2)) {
+
+ //
+ // Reassign the status of the connection if it is paused
+ //
+ if (WsRedirectionPaused(UseEntry->Local)) {
+ UseInfo->ui2_status = USE_PAUSED;
+ }
+ }
+
+ switch (UncEntry->SharedResourceType) {
+
+ case FILE_DEVICE_DISK:
+ UseInfo->ui2_asg_type = USE_DISKDEV;
+ break;
+
+ case FILE_DEVICE_PRINTER:
+ UseInfo->ui2_asg_type = USE_SPOOLDEV;
+ break;
+
+ case FILE_DEVICE_SERIAL_PORT:
+ UseInfo->ui2_asg_type = USE_CHARDEV;
+ break;
+
+ case FILE_DEVICE_NAMED_PIPE:
+ UseInfo->ui2_asg_type = USE_IPC;
+ break;
+
+ default:
+ NetpKdPrint((
+ "WsFillUseBuffer: Unknown shared resource type %d.\n",
+ UncEntry->SharedResourceType
+ ));
+
+ case FILE_DEVICE_UNKNOWN:
+ UseInfo->ui2_asg_type = USE_WILDCARD;
+ break;
+ }
+
+ UseInfo->ui2_refcount = UncEntry->NumberFilesOpen;
+
+ UseInfo->ui2_usecount = (UseEntry == NULL) ? 0 :
+ UseEntry->Remote->TotalUseCount;
+
+ case 0:
+
+ if (UseEntry != NULL) {
+ //
+ // Explicit connection
+ //
+ if (! NetpCopyStringToBuffer(
+ UseEntry->Local,
+ UseEntry->LocalLength,
+ *FixedPortion,
+ EndOfVariableData,
+ &UseInfo->ui2_local
+ )) {
+ return FALSE;
+ }
+
+ }
+ else {
+ //
+ // Implicit connection
+ //
+ if (! NetpCopyStringToBuffer(
+ NULL,
+ 0,
+ *FixedPortion,
+ EndOfVariableData,
+ &UseInfo->ui2_local
+ )) {
+ return FALSE;
+ }
+ }
+
+ if (! NetpCopyStringToBuffer(
+ UncEntry->UNCName.Buffer,
+ UncEntry->UNCName.Length / sizeof(TCHAR),
+ *FixedPortion,
+ EndOfVariableData,
+ &UseInfo->ui2_remote
+ )) {
+ return FALSE;
+ }
+
+ break;
+
+ default:
+ //
+ // This should never happen.
+ //
+ NetpKdPrint(("WsFillUseBuffer: Invalid level %u.\n", Level));
+ NetpAssert(FALSE);
+ }
+
+ return TRUE;
+}
diff --git a/private/net/svcdlls/wkssvc/server/user.c b/private/net/svcdlls/wkssvc/server/user.c
new file mode 100644
index 000000000..9b03b21de
--- /dev/null
+++ b/private/net/svcdlls/wkssvc/server/user.c
@@ -0,0 +1,1623 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ user.c
+
+Abstract:
+
+ This module contains the worker routines for the NetWkstaUser
+ APIs implemented in the Workstation service.
+
+Author:
+
+ Rita Wong (ritaw) 20-Feb-1991
+
+Revision History:
+
+--*/
+
+#include "wsutil.h"
+#include "wsdevice.h"
+#include "wssec.h"
+#include "wsconfig.h"
+#include "wslsa.h"
+#include "wswksta.h"
+
+#include <strarray.h>
+#include <config.h> // NT config file helpers in netlib
+#include <configp.h> // USE_WIN32_CONFIG (if defined), etc.
+#include <confname.h> // Section and keyword equates.
+
+#define WS_OTH_DOMAIN_DELIMITER_STR L" "
+#define WS_OTH_DOMAIN_DELIMITER_CHAR L' '
+
+//-------------------------------------------------------------------//
+// //
+// Local function prototypes //
+// //
+//-------------------------------------------------------------------//
+
+STATIC
+NET_API_STATUS
+WsGetUserInfo(
+ IN PLUID LogonId,
+ IN DWORD Level,
+ OUT PMSV1_0_GETUSERINFO_RESPONSE *UserInfoResponse,
+ OUT PDGRECEIVE_NAMES *DgrNames,
+ OUT LPDWORD DgrNamesCount,
+ IN OUT LPDWORD TotalBytesNeeded OPTIONAL
+ );
+
+STATIC
+NET_API_STATUS
+WsGetActiveDgrNames(
+ IN PLUID LogonId,
+ OUT PDGRECEIVE_NAMES *DgrNames,
+ OUT LPDWORD DgrNamesCount,
+ IN OUT LPDWORD TotalBytesNeeded OPTIONAL
+ );
+
+STATIC
+NET_API_STATUS
+WsSetOtherDomains(
+ IN DWORD Level,
+ IN LPBYTE Buffer
+ );
+
+STATIC
+NET_API_STATUS
+WsEnumUserInfo(
+ IN DWORD Level,
+ IN DWORD PreferedMaximumLength,
+ IN PMSV1_0_ENUMUSERS_RESPONSE EnumUsersResponse,
+ OUT LPBYTE *OutputBuffer,
+ OUT LPDWORD EntriesRead,
+ OUT LPDWORD TotalEntries,
+ IN OUT LPDWORD ResumeHandle OPTIONAL
+ );
+
+STATIC
+NET_API_STATUS
+WsPackageUserInfo(
+ IN DWORD Level,
+ IN DWORD UserInfoFixedLength,
+ IN PMSV1_0_GETUSERINFO_RESPONSE UserInfoResponse,
+ IN PDGRECEIVE_NAMES DgrNames,
+ IN DWORD DgrNamesCount,
+ IN OUT LPBYTE *FixedPortion,
+ IN OUT LPTSTR *EndOfVariableData,
+ IN OUT LPDWORD EntriesRead OPTIONAL
+ );
+
+STATIC
+BOOL
+WsFillUserInfoBuffer(
+ IN DWORD Level,
+ IN PMSV1_0_GETUSERINFO_RESPONSE UserInfo,
+ IN PDGRECEIVE_NAMES DgrNames,
+ IN DWORD DgrNamesCount,
+ IN OUT LPBYTE *FixedPortion,
+ IN OUT LPTSTR *EndOfVariableData,
+ IN DWORD UserInfoFixedLength
+ );
+
+STATIC
+VOID
+WsWriteOtherDomains(
+ IN PDGRECEIVE_NAMES DgrNames,
+ IN DWORD DgrNamesCount,
+ IN OUT LPBYTE *FixedPortion,
+ IN OUT LPTSTR *EndOfVariableData,
+ IN DWORD UserInfoFixedLength,
+ OUT LPWSTR *OtherDomainsPointer
+ );
+
+//-------------------------------------------------------------------//
+// //
+// Global variables //
+// //
+//-------------------------------------------------------------------//
+
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetrWkstaUserGetInfo(
+ IN LPTSTR Reserved,
+ IN DWORD Level,
+ OUT LPWKSTA_USER_INFO UserInfo
+ )
+/*++
+
+Routine Description:
+
+ This function is the NetWkstaUserGetInfo entry point in the Workstation
+ service. It calls the LSA subsystem and the MSV1_0 authentication
+ package to get per user information.
+
+Arguments:
+
+ Reserved - Must be 0.
+
+ Level - Supplies the requested level of information.
+
+ UserInfo - Returns, in this structure, a pointer to a buffer which
+ contains the requested user information.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NET_API_STATUS status;
+ LUID LogonId;
+
+ PMSV1_0_GETUSERINFO_RESPONSE UserInfoResponse = NULL;
+ LPBYTE FixedPortion;
+ LPTSTR EndOfVariableData;
+
+ DWORD UserInfoFixedLength = USER_FIXED_LENGTH(Level);
+ LPBYTE OutputBuffer;
+
+ DWORD TotalBytesNeeded = 0;
+
+ PDGRECEIVE_NAMES DgrNames = NULL;
+ DWORD DgrNamesCount;
+
+
+ if (Reserved != NULL) {
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ //
+ // Levels 0, 1, and 1101 are valid
+ //
+ if (Level > 1 && Level != 1101) {
+ return ERROR_INVALID_LEVEL;
+ }
+
+ //
+ // Impersonate caller and get the logon id
+ //
+ if ((status = WsImpersonateAndGetLogonId(&LogonId)) != NERR_Success) {
+ return status;
+ }
+
+ if ((status = WsGetUserInfo(
+ &LogonId,
+ Level,
+ &UserInfoResponse,
+ &DgrNames,
+ &DgrNamesCount,
+ &TotalBytesNeeded
+ )) != NERR_Success) {
+ return status;
+ }
+
+ if ((OutputBuffer = MIDL_user_allocate(TotalBytesNeeded)) == NULL) {
+ status = ERROR_NOT_ENOUGH_MEMORY;
+ goto FreeBuffers;
+ }
+ RtlZeroMemory((PVOID) OutputBuffer, TotalBytesNeeded);
+
+ SET_USER_INFO_POINTER(UserInfo, OutputBuffer);
+
+
+ //
+ // Write the user information into output buffer.
+ //
+ FixedPortion = OutputBuffer;
+ EndOfVariableData = (LPTSTR) ((DWORD) FixedPortion + TotalBytesNeeded);
+
+ status = WsPackageUserInfo(
+ Level,
+ UserInfoFixedLength,
+ UserInfoResponse,
+ DgrNames,
+ DgrNamesCount,
+ &FixedPortion,
+ &EndOfVariableData,
+ NULL
+ );
+
+ NetpAssert(status == NERR_Success);
+
+FreeBuffers:
+ if (UserInfoResponse != NULL) {
+ (void) LsaFreeReturnBuffer((PVOID) UserInfoResponse);
+ }
+
+ if (DgrNames != NULL) {
+ MIDL_user_free((PVOID) DgrNames);
+ }
+
+ return status;
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetrWkstaUserSetInfo(
+ IN LPTSTR Reserved,
+ IN DWORD Level,
+ IN LPWKSTA_USER_INFO UserInfo,
+ OUT LPDWORD ErrorParameter OPTIONAL
+ )
+/*++
+
+Routine Description:
+
+ This function is the NetWkstaUserSetInfo entry point in the Workstation
+ service. It sets the other domains for the current user.
+
+Arguments:
+
+ Reserved - Must be NULL.
+
+ Level - Supplies the level of information.
+
+ UserInfo - Supplies a pointer to union structure of pointers to
+ buffer of fields to set. The level denotes the fields supplied in
+ this buffer.
+
+ ErrorParameter - Returns the identifier to the invalid parameter if
+ this function returns ERROR_INVALID_PARAMETER.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NET_API_STATUS status = NERR_Success;
+
+
+ if (Reserved != NULL) {
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ //
+ // Check for NULL input buffer
+ //
+ if (UserInfo->UserInfo1 == NULL) {
+ RETURN_INVALID_PARAMETER(ErrorParameter, PARM_ERROR_UNKNOWN);
+ }
+
+ //
+ // Serialize write access
+ //
+ if (! RtlAcquireResourceExclusive(&WsInfo.ConfigResource, TRUE)) {
+ return NERR_InternalError;
+ }
+
+ switch (Level) {
+
+ //
+ // Other domains is the only settable field in the entire
+ // system-wide info structure
+ //
+ case 1:
+ case 1101:
+
+ if ((status = WsSetOtherDomains(
+ Level,
+ (LPBYTE) UserInfo->UserInfo1
+ )) == ERROR_INVALID_PARAMETER) {
+ if (ARGUMENT_PRESENT(ErrorParameter)) {
+ *ErrorParameter = WKSTA_OTH_DOMAINS_PARMNUM;
+ }
+ }
+ break;
+
+ default:
+ status = ERROR_INVALID_LEVEL;
+ }
+
+ RtlReleaseResource(&WsInfo.ConfigResource);
+ return status;
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetrWkstaUserEnum(
+ IN LPTSTR ServerName OPTIONAL,
+ IN OUT LPWKSTA_USER_ENUM_STRUCT UserInfo,
+ IN DWORD PreferedMaximumLength,
+ OUT LPDWORD TotalEntries,
+ IN OUT LPDWORD ResumeHandle OPTIONAL
+ )
+/*++
+
+Routine Description:
+
+ This function is the NetWkstaUserEnum entry point in the Workstation
+ service.
+
+Arguments:
+
+ ServerName - Supplies the name of server to execute this function
+
+ UserInfo - This structure supplies the level of information requested,
+ returns a pointer to the buffer allocated by the Workstation service
+ which contains a sequence of information structure of the specified
+ information level, and returns the number of entries read. The buffer
+ pointer is set to NULL if return code is not NERR_Success or
+ ERROR_MORE_DATA, or if EntriesRead returned is 0. The EntriesRead
+ value is only valid if the return code is NERR_Success or
+ ERROR_MORE_DATA.
+
+ PreferedMaximumLength - Supplies the number of bytes of information
+ to return in the buffer. If this value is MAXULONG, all available
+ information will be returned.
+
+ TotalEntries - Returns the total number of entries available. This value
+ is only valid if the return code is NERR_Success or ERROR_MORE_DATA.
+
+ ResumeHandle - Supplies a handle to resume the enumeration from where it
+ left off the last time through. Returns the resume handle if return
+ code is ERROR_MORE_DATA.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NET_API_STATUS status;
+ PMSV1_0_ENUMUSERS_RESPONSE EnumUsersResponse;
+
+
+ UNREFERENCED_PARAMETER(ServerName);
+
+ //
+ // Only levels 0 and 1
+ //
+ if (UserInfo->Level > 1) {
+ return ERROR_INVALID_LEVEL;
+ }
+
+ //
+ // Ask authentication package to enumerate users who are physically
+ // logged to the local machine.
+ //
+ if ((status = WsLsaEnumUsers(
+ (LPBYTE *) &EnumUsersResponse
+ )) != NERR_Success) {
+ return status;
+ }
+
+ //
+ // If no users are logged on, set appropriate fields and return success.
+ //
+ if (EnumUsersResponse->NumberOfLoggedOnUsers == 0) {
+ UserInfo->WkstaUserInfo.Level1->Buffer = NULL;
+ UserInfo->WkstaUserInfo.Level1->EntriesRead = 0;
+ *TotalEntries = 0;
+ return NERR_Success;
+ }
+
+ status = WsEnumUserInfo(
+ UserInfo->Level,
+ PreferedMaximumLength,
+ EnumUsersResponse,
+ (LPBYTE *) &(UserInfo->WkstaUserInfo.Level1->Buffer),
+ (LPDWORD) &(UserInfo->WkstaUserInfo.Level1->EntriesRead),
+ TotalEntries,
+ ResumeHandle
+ );
+
+ (void) LsaFreeReturnBuffer((PVOID) EnumUsersResponse);
+
+ return status;
+}
+
+
+
+STATIC
+NET_API_STATUS
+WsEnumUserInfo(
+ IN DWORD Level,
+ IN DWORD PreferedMaximumLength,
+ IN PMSV1_0_ENUMUSERS_RESPONSE EnumUsersResponse,
+ OUT LPBYTE *OutputBuffer,
+ OUT LPDWORD EntriesRead,
+ OUT LPDWORD TotalEntries,
+ IN OUT LPDWORD ResumeHandle OPTIONAL
+ )
+/*++
+
+Routine Description:
+
+ This function takes the logon IDs returned by MS V1.0 Authentication
+ Package to call it again to get information about each user.
+
+Arguments:
+
+ Level - Supplies the level of information to be returned.
+
+ PreferedMaximumLength - Supplies the number of bytes of information
+ to return in the buffer. If this value is MAXULONG, all available
+ information will be returned.
+
+ EnumUsersResponse - Supplies the structure returned from calling the MS
+ V1.0 Authentication Package to enumerate logged on users.
+
+ OutputBuffer - Returns a pointer to the enumerated user information.
+
+ TotalEntries - Returns the total number of entries available. This value
+ is only valid if the return code is NERR_Success or ERROR_MORE_DATA.
+
+ EntriesRead - Supplies a running total of the number of entries read
+ into the output buffer. This value is incremented every time a
+ user entry is successfully written into the output buffer.
+
+ ResumeHandle - Returns the handle to continue with the enumeration if
+ this function returns ERROR_MORE_DATA.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NET_API_STATUS status = NERR_Success;
+
+ //
+ // Array of per user info entries, each entry contains a pointer
+ // LSA info, and a pointer to active datagram receiver names.
+ //
+ PWSPER_USER_INFO UserInfoArray;
+
+ DWORD UserInfoFixedLength = USER_FIXED_LENGTH(Level);
+ DWORD i;
+ DWORD OutputBufferLength = 0;
+ DWORD UserEntriesRead = 0;
+ DWORD OtherDomainsSize = 0;
+
+ LPDWORD PointerToOutputBufferLength = &OutputBufferLength;
+
+ LPBYTE FixedPortion;
+ LPTSTR EndOfVariableData;
+
+ DWORD StartEnumeration = 0;
+
+
+ if (PreferedMaximumLength != MAXULONG) {
+
+ //
+ // We will return as much as possible that fits into this specified
+ // buffer size.
+ //
+ OutputBufferLength =
+ ROUND_UP_COUNT(PreferedMaximumLength, ALIGN_WCHAR);
+
+ if (OutputBufferLength < UserInfoFixedLength) {
+ *OutputBuffer = NULL;
+ *EntriesRead = 0;
+ *TotalEntries = EnumUsersResponse->NumberOfLoggedOnUsers;
+
+ return ERROR_MORE_DATA;
+ }
+
+ //
+ // This indicates that we should not bother calculating the
+ // total output buffer size needed.
+ //
+ PointerToOutputBufferLength = NULL;
+ }
+
+ //
+ // Allocate a temporary array to save pointers to user information
+ // we retrieve from the LSA and datagram receiver. This is because we
+ // need to go through the list of users twice: the first time to add
+ // up the number of bytes to allocate for the output buffer; the second
+ // time to write the user information into the output buffer.
+ //
+ if ((UserInfoArray = (PWSPER_USER_INFO) LocalAlloc(
+ LMEM_ZEROINIT,
+ EnumUsersResponse->NumberOfLoggedOnUsers *
+ sizeof(WSPER_USER_INFO)
+ )) == NULL) {
+ return GetLastError();
+ }
+
+ //
+ // Get the info for each user and calculate the amount of memory to
+ // allocate for the output buffer if PointerToOutputBufferLength is
+ // not set to NULL. If it was set to NULL, we will allocate the
+ // output buffer size as specified by the caller.
+ //
+ for (i = 0; i < EnumUsersResponse->NumberOfLoggedOnUsers; i++) {
+
+ if ((status = WsGetUserInfo(
+ &(EnumUsersResponse->LogonIds[i]),
+ Level,
+ (PMSV1_0_GETUSERINFO_RESPONSE *) &(UserInfoArray[i].LsaUserInfo),
+ (PDGRECEIVE_NAMES *) &UserInfoArray[i].DgrNames,
+ (LPDWORD) &UserInfoArray[i].DgrNamesCount,
+ PointerToOutputBufferLength
+ )) != NERR_Success) {
+ goto FreeBuffers;
+ }
+
+ }
+
+ IF_DEBUG(INFO) {
+ NetpKdPrint(("[Wksta] NetrWkstaUserEnum: OutputBufferLength=%lu\n",
+ OutputBufferLength));
+ }
+
+ //
+ // Allocate the output buffer
+ //
+ if ((*OutputBuffer = MIDL_user_allocate(OutputBufferLength)) == NULL) {
+ status = ERROR_NOT_ENOUGH_MEMORY;
+ goto FreeBuffers;
+ }
+
+ RtlZeroMemory((PVOID) *OutputBuffer, OutputBufferLength);
+
+ FixedPortion = *OutputBuffer;
+ EndOfVariableData = (LPTSTR) ((DWORD) FixedPortion + OutputBufferLength);
+
+ //
+ // Get the enumeration starting point.
+ //
+ if (ARGUMENT_PRESENT(ResumeHandle)) {
+ StartEnumeration = *ResumeHandle;
+ }
+
+ //
+ // Enumerate the user information
+ //
+ for (i = 0; i < EnumUsersResponse->NumberOfLoggedOnUsers &&
+ status == NERR_Success; i++) {
+
+ IF_DEBUG(INFO) {
+ NetpKdPrint(("LsaList->ResumeKey=%lu\n",
+ EnumUsersResponse->EnumHandles[i]));
+ }
+
+ if (StartEnumeration <= EnumUsersResponse->EnumHandles[i]) {
+
+ status = WsPackageUserInfo(
+ Level,
+ UserInfoFixedLength,
+ UserInfoArray[i].LsaUserInfo,
+ UserInfoArray[i].DgrNames,
+ UserInfoArray[i].DgrNamesCount,
+ &FixedPortion,
+ &EndOfVariableData,
+ &UserEntriesRead
+ );
+
+ if (status == ERROR_MORE_DATA) {
+ *TotalEntries = (EnumUsersResponse->NumberOfLoggedOnUsers
+ - i) + UserEntriesRead;
+ }
+ }
+ }
+
+ //
+ // Return entries read and total entries. We can only get NERR_Success
+ // or ERROR_MORE_DATA from WsPackageUserInfo.
+ //
+ *EntriesRead = UserEntriesRead;
+
+ if (status == NERR_Success) {
+ *TotalEntries = UserEntriesRead;
+ }
+
+ if (status == ERROR_MORE_DATA && ARGUMENT_PRESENT(ResumeHandle)) {
+ *ResumeHandle = EnumUsersResponse->EnumHandles[i - 1];
+ }
+
+ if (*EntriesRead == 0) {
+ MIDL_user_free(*OutputBuffer);
+ *OutputBuffer = NULL;
+ }
+
+FreeBuffers:
+ for (i = 0; i < EnumUsersResponse->NumberOfLoggedOnUsers; i++) {
+
+ if (UserInfoArray[i].DgrNames != NULL) {
+ MIDL_user_free((PVOID) UserInfoArray[i].DgrNames);
+ }
+
+ if (UserInfoArray[i].LsaUserInfo != NULL) {
+ (void) LsaFreeReturnBuffer((PVOID) UserInfoArray[i].LsaUserInfo);
+ }
+ }
+
+ (void) LocalFree((HLOCAL) UserInfoArray);
+
+ return status;
+}
+
+
+STATIC
+NET_API_STATUS
+WsGetUserInfo(
+ IN PLUID LogonId,
+ IN DWORD Level,
+ OUT PMSV1_0_GETUSERINFO_RESPONSE *UserInfoResponse,
+ OUT PDGRECEIVE_NAMES *DgrNames,
+ OUT LPDWORD DgrNamesCount,
+ IN OUT LPDWORD TotalBytesNeeded OPTIONAL
+ )
+/*++
+
+Routine Description:
+
+ This function gets the other domains for the current user from
+ the datagram receiver.
+
+Arguments:
+
+ LogonId - Supplies a pointer to the user's Logon Id.
+
+ Level - Supplies the level of information to be returned.
+
+ UserInfoResponse - Returns a pointer to the user information from the
+ authentication package.
+
+ DgrNames - Returns a pointer an array of active datagram receiver
+ names.
+
+ DgrNamesCount - Returns the number of entries in DgrNames.
+
+ TotalBytesNeeded - Returns the number of bytes required to in the
+ output buffer for writing the other domains to.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NET_API_STATUS status;
+
+ ULONG UserInfoResponseLength;
+
+ DWORD OtherDomainsSize = 0;
+
+
+ //
+ // Ask the datagram receiver for the other domains
+ //
+ if (Level == 1 || Level == 1101) {
+ if ((status = WsGetActiveDgrNames(
+ LogonId,
+ DgrNames,
+ DgrNamesCount,
+ TotalBytesNeeded
+ )) != NERR_Success) {
+ return status;
+ }
+ }
+
+ //
+ // Don't get user info from authentication package if level
+ // is 1101 since only other domains are returned in this level.
+ //
+ if (Level != 1101) {
+
+ //
+ // Ask authentication package for user information.
+ //
+ if ((status = WsLsaGetUserInfo(
+ LogonId,
+ (LPBYTE *) UserInfoResponse,
+ &UserInfoResponseLength
+ )) != NERR_Success) {
+
+ MIDL_user_free((PVOID) *DgrNames);
+ *DgrNames = NULL;
+ *DgrNamesCount = 0;
+ return status;
+ }
+
+ //
+ // Calculate the amount of memory needed to hold the user information
+ // and allocate the return buffer of that size.
+ //
+ if (ARGUMENT_PRESENT(TotalBytesNeeded)) {
+ (*TotalBytesNeeded) +=
+ FIXED_PLUS_LSA_SIZE(
+ Level,
+ (*UserInfoResponse)->UserName.Length +
+ sizeof(TCHAR),
+ (*UserInfoResponse)->LogonDomainName.Length +
+ sizeof(TCHAR),
+ (*UserInfoResponse)->LogonServer.Length +
+ sizeof(TCHAR)
+ );
+ }
+ }
+ else {
+ *TotalBytesNeeded += USER_FIXED_LENGTH(Level);
+ }
+
+ return NERR_Success;
+}
+
+
+STATIC
+NET_API_STATUS
+WsGetActiveDgrNames(
+ IN PLUID LogonId,
+ OUT PDGRECEIVE_NAMES *DgrNames,
+ OUT LPDWORD DgrNamesCount,
+ IN OUT LPDWORD TotalBytesNeeded OPTIONAL
+ )
+/*++
+
+Routine Description:
+
+ This function gets the other domains for the current user from
+ the datagram receiver.
+
+Arguments:
+
+ LogonId - Supplies a pointer to the user's Logon Id.
+
+ DgrNames - Returns a pointer an array of active datagram receiver
+ names.
+
+ DgrNamesCount - Returns the number of entries in DgrNames.
+
+ TotalBytesNeeded - Returns the number of bytes required to in the
+ output buffer for writing the other domains to.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NET_API_STATUS status;
+
+ LMDR_REQUEST_PACKET Drp; // Datagram receiver request packet
+ DWORD EnumDgNamesHintSize = 0;
+ DWORD i;
+
+ Drp.Version = LMDR_REQUEST_PACKET_VERSION;
+ Drp.Type = EnumerateNames;
+ RtlCopyLuid(&Drp.LogonId, LogonId);
+
+ //
+ // Get the other domains from the datagram receiver.
+ //
+ if ((status = WsDeviceControlGetInfo(
+ DatagramReceiver,
+ WsDgReceiverDeviceHandle,
+ IOCTL_LMDR_ENUMERATE_NAMES,
+ (PVOID) &Drp,
+ sizeof(LMDR_REQUEST_PACKET),
+ (LPBYTE *) DgrNames,
+ MAXULONG,
+ EnumDgNamesHintSize,
+ NULL
+ )) != NERR_Success) {
+ return status;
+ }
+
+ //
+ // Include room for NULL character, in case there are no
+ // other domains
+ //
+ if (ARGUMENT_PRESENT(TotalBytesNeeded)) {
+ (*TotalBytesNeeded) += sizeof(TCHAR);
+ }
+
+
+ *DgrNamesCount = Drp.Parameters.EnumerateNames.EntriesRead;
+
+ if (*DgrNamesCount == 0 && *DgrNames != NULL) {
+ MIDL_user_free((PVOID) *DgrNames);
+ *DgrNames = NULL;
+ }
+
+ //
+ // Calculate the amount of memory to allocate for the output buffer
+ //
+ if (ARGUMENT_PRESENT(TotalBytesNeeded)) {
+ for (i = 0; i < *DgrNamesCount; i++) {
+
+ //
+ // Add up the lengths of all the other domain names
+ //
+ if ((*DgrNames)[i].Type == OtherDomain) {
+ (*TotalBytesNeeded) += (*DgrNames)[i].DGReceiverName.Length +
+ sizeof(TCHAR);
+ }
+ }
+ }
+
+ return NERR_Success;
+}
+
+
+STATIC
+NET_API_STATUS
+WsSetOtherDomains(
+ IN DWORD Level,
+ IN LPBYTE Buffer
+ )
+/*++
+
+Routine Description:
+
+ This function sets the other domains for the current user in
+ the datagram receiver.
+
+Arguments:
+
+ Level - Supplies the level of information.
+
+ Buffer - Supplies a buffer which contains the information structure
+ if Parameter is WkstaSetAllParm. Otherwise Buffer contains the
+ individual field to set.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NET_API_STATUS status = NERR_Success;
+
+ PCHAR DrpBuffer[sizeof(LMDR_REQUEST_PACKET) +
+ DNLEN * sizeof(TCHAR)];
+ PLMDR_REQUEST_PACKET Drp = (PLMDR_REQUEST_PACKET) DrpBuffer;
+ DWORD DrpSize = sizeof(LMDR_REQUEST_PACKET) +
+ DNLEN * sizeof(TCHAR);
+
+ PDGRECEIVE_NAMES DgrNames;
+ DWORD DgrNamesCount;
+ DWORD EnumDgNamesHintSize = 0;
+
+ DWORD NumberOfOtherDomains = 0;
+ DWORD i, j, k;
+ DWORD IndexToList = 0;
+
+ PWSNAME_RECORD OtherDomainsInfo = NULL;
+ LPTSTR OtherDomainsPointer;
+ LPTSTR OtherDomains;
+
+ LPTSTR CanonBuffer;
+ DWORD CanonBufferSize;
+
+
+ if (Level == 1101) {
+ OtherDomains = ((PWKSTA_USER_INFO_1101) Buffer)->wkui1101_oth_domains;
+ }
+ else {
+ OtherDomains = ((PWKSTA_USER_INFO_1) Buffer)->wkui1_oth_domains;
+ }
+
+ //
+ // NULL pointer means leave the other domains unmodified
+ //
+ if (OtherDomains == NULL) {
+ return NERR_Success;
+ }
+
+ IF_DEBUG(INFO) {
+ NetpKdPrint(("WsSetOtherDomains: Input other domain is %ws\n", OtherDomains));
+ }
+
+ //
+ // Before canonicalizing the input buffer, we have to find out how
+ // many other domain entries are there so that we can supply a
+ // buffer of the right size to the canonicalize routine.
+ //
+ OtherDomainsPointer = OtherDomains;
+ while (*OtherDomainsPointer != TCHAR_EOS) {
+ if (*(OtherDomainsPointer + 1) == WS_OTH_DOMAIN_DELIMITER_CHAR ||
+ *(OtherDomainsPointer + 1) == TCHAR_EOS) {
+ NumberOfOtherDomains++;
+ }
+
+ OtherDomainsPointer++;
+ }
+
+ //
+ // Allocate the buffer to put the canonicalized other domain names
+ //
+ CanonBufferSize = NumberOfOtherDomains * (DNLEN + 1) * sizeof(TCHAR) +
+ sizeof(TCHAR); // One more char for the NULL terminator
+
+ if ((CanonBuffer = (LPTSTR) LocalAlloc(
+ LMEM_ZEROINIT,
+ (UINT) CanonBufferSize
+ )) == NULL) {
+ return GetLastError();
+ }
+
+ //
+ // Canonicalize the input other domains separated by NULLs and put
+ // into CanonBuffer, each other domain name separated by NULL, and
+ // the buffer itself terminated by another NULL.
+ //
+ status = I_NetListCanonicalize(
+ NULL,
+ OtherDomains,
+ WS_OTH_DOMAIN_DELIMITER_STR,
+ CanonBuffer,
+ CanonBufferSize,
+ &NumberOfOtherDomains,
+ NULL,
+ 0,
+ OUTLIST_TYPE_NULL_NULL |
+ NAMETYPE_DOMAIN |
+ INLC_FLAGS_CANONICALIZE
+ );
+
+ if (status != NERR_Success) {
+ NetpKdPrint(("[Wksta] Error in canonicalizing other domains %lu",
+ status));
+ status = ERROR_INVALID_PARAMETER;
+ goto FreeCanonBuffer;
+ }
+
+ //
+ // Initialize datagram receiver packet to add or delete
+ // other domains.
+ //
+ Drp->Version = LMDR_REQUEST_PACKET_VERSION;
+ Drp->Type = EnumerateNames;
+ Drp->Parameters.AddDelName.Type = OtherDomain;
+
+ if ((status = WsImpersonateAndGetLogonId(
+ &Drp->LogonId
+ )) != NERR_Success) {
+ goto FreeCanonBuffer;
+ }
+
+ //
+ // Get all datagram receiver names from the datagram receiver.
+ //
+ if ((status = WsDeviceControlGetInfo(
+ DatagramReceiver,
+ WsDgReceiverDeviceHandle,
+ IOCTL_LMDR_ENUMERATE_NAMES,
+ (PVOID) Drp,
+ DrpSize,
+ (LPBYTE *) &DgrNames,
+ MAXULONG,
+ EnumDgNamesHintSize,
+ NULL
+ )) != NERR_Success) {
+ goto FreeCanonBuffer;
+ }
+
+ DgrNamesCount = Drp->Parameters.EnumerateNames.EntriesRead;
+
+ //
+ // The other domains the user wants to set has to be merged with the
+ // one that is maintained by the datagram receiver. We will attempt
+ // to add all the other domains first. If it already exists, we ignore
+ // the error. If it was added successfully, we mark it as such so that
+ // if we run into an error other than the already exist error, we can
+ // back out the ones we've already added.
+ //
+ // This requires that we allocate a structure to keep track of the ones
+ // we've added.
+ //
+ if (NumberOfOtherDomains != 0) {
+ if ((OtherDomainsInfo = (PWSNAME_RECORD) LocalAlloc(
+ LMEM_ZEROINIT,
+ (UINT) (NumberOfOtherDomains *
+ sizeof(WSNAME_RECORD))
+ )) == NULL) {
+ status = GetLastError();
+ goto FreeDgrNames;
+ }
+
+
+ //
+ // Add all other domains specified. If any already exist, we ignore
+ // the error from the datagram receiver.
+ //
+ OtherDomains = CanonBuffer;
+ while ((OtherDomainsPointer = I_NetListTraverse(
+ NULL,
+ &OtherDomains,
+ 0
+ )) != NULL) {
+
+ OtherDomainsInfo[IndexToList].Name = OtherDomainsPointer;
+ OtherDomainsInfo[IndexToList].Size =
+ STRLEN(OtherDomainsPointer) * sizeof(TCHAR);
+
+ for (j = 0; j < DgrNamesCount; j++) {
+
+ if (DgrNames[j].Type == OtherDomain) {
+
+ if (WsCompareStringU(
+ DgrNames[j].DGReceiverName.Buffer,
+ DgrNames[j].DGReceiverName.Length / sizeof(TCHAR),
+ OtherDomainsPointer,
+ OtherDomainsInfo[IndexToList].Size / sizeof(TCHAR)
+ ) == 0) {
+
+ break;
+ }
+ }
+ }
+
+ //
+ // User-specified domain does not already exist, so add it.
+ //
+ if (j == DgrNamesCount) {
+
+ Drp->Parameters.AddDelName.Type = OtherDomain;
+ status = WsAddDomainName(
+ Drp,
+ DrpSize,
+ OtherDomainsPointer,
+ OtherDomainsInfo[IndexToList].Size
+ );
+
+ if (status == NERR_Success) {
+
+ OtherDomainsInfo[IndexToList].IsAdded = TRUE;
+
+ IF_DEBUG(INFO) {
+ NetpKdPrint((
+ "[Wksta] Successfully added other domain %ws\n",
+ OtherDomainsPointer
+ ));
+ }
+
+ }
+ else {
+
+ //
+ // We're ran into trouble and have to delete those
+ // we've just added.
+ //
+ IF_DEBUG(INFO) {
+ NetpKdPrint((
+ "[Wksta] Trouble with adding other domain %ws %lu\n",
+ OtherDomainsPointer, status
+ ));
+ }
+
+ for (i = 0; i < IndexToList; i++) {
+ if (OtherDomainsInfo[i].IsAdded) {
+
+ IF_DEBUG(INFO) {
+ NetpKdPrint(("[Wksta] About to remove %ws\n",
+ OtherDomainsInfo[i].Name));
+ }
+
+ Drp->Parameters.AddDelName.Type = OtherDomain;
+ (void) WsDeleteDomainName(
+ Drp,
+ DrpSize,
+ OtherDomainsInfo[i].Name,
+ OtherDomainsInfo[i].Size
+ );
+ }
+ }
+ goto FreeDomainInfo;
+
+ } // back out added domains
+
+ } // attempted to add a non-existing domain name
+
+ IndexToList++;
+
+ } // while there is a user-specified domain name
+
+ } // if NumberOfOtherDomains != 0
+
+ //
+ // Now we need to delete an active domain name from the Datagram
+ // Receiver if it is not in the input other domain list.
+ //
+ for (i = 0; i < DgrNamesCount; i++) {
+
+ if (DgrNames[i].Type == OtherDomain) {
+
+ for (j = 0; j < NumberOfOtherDomains; j++) {
+
+ if (WsCompareStringU(
+ DgrNames[i].DGReceiverName.Buffer,
+ DgrNames[i].DGReceiverName.Length / sizeof(TCHAR),
+ OtherDomainsInfo[j].Name,
+ OtherDomainsInfo[j].Size / sizeof(TCHAR)
+ ) == 0) {
+
+ break;
+ }
+ }
+
+ //
+ // Did not find the active other domain name in the
+ // input list. We have to delete it.
+ //
+ if (j == NumberOfOtherDomains) {
+
+ Drp->Parameters.AddDelName.Type = OtherDomain;
+ status = WsDeleteDomainName(
+ Drp,
+ DrpSize,
+ DgrNames[i].DGReceiverName.Buffer,
+ DgrNames[i].DGReceiverName.Length
+ );
+
+ //
+ // Save the delete status of the other domain name away
+ // because we might run into a problem and have to back
+ // out the deletion later. What a mess!
+ //
+ if (status == NERR_Success) {
+
+ IF_DEBUG(INFO) {
+ NetpKdPrint((
+ "[Wksta] Successfully deleted other domain\n"));
+ //"[Wksta] Successfully deleted other domain %wZ\n",
+ //DgrNames[i].DGReceiverName);
+ }
+
+ DgrNames[i].Type = DGR_NAME_DELETED;
+ }
+ else {
+
+ //
+ // Could not delete the name. Back all successful
+ // changes so far--this includes adding the names
+ // that were deleted, and removing the names that
+ // were added.
+ //
+ IF_DEBUG(INFO) {
+ NetpKdPrint((
+ "[Wksta] Trouble with deleting other domain %lu\n",
+ status));
+ //"[Wksta] Trouble with deleting other domain %wZ %lu\n",
+ //DgrNames[i].DGReceiverName, status);
+ }
+
+ //
+ // Add back all deleted names
+ //
+ for (k = 0; k < i; k++) {
+ if (DgrNames[k].Type == DGR_NAME_DELETED) {
+
+ IF_DEBUG(INFO) {
+ NetpKdPrint(("[Wksta] About to add back %wZ\n",
+ DgrNames[k].DGReceiverName));
+ }
+
+ Drp->Parameters.AddDelName.Type = OtherDomain;
+ (void) WsAddDomainName(
+ Drp,
+ DrpSize,
+ DgrNames[k].DGReceiverName.Buffer,
+ DgrNames[k].DGReceiverName.Length
+ );
+
+ }
+
+ } // back out deletions
+
+ //
+ // Remove all added names
+ //
+ for (k = 0; k < NumberOfOtherDomains; k++) {
+ if (OtherDomainsInfo[k].IsAdded) {
+
+ IF_DEBUG(INFO) {
+ NetpKdPrint(("[Wksta] About to remove %ws\n",
+ OtherDomainsInfo[k].Name));
+ }
+
+ Drp->Parameters.AddDelName.Type = OtherDomain;
+ (void) WsDeleteDomainName(
+ Drp,
+ DrpSize,
+ OtherDomainsInfo[k].Name,
+ OtherDomainsInfo[k].Size
+ );
+ }
+
+ } // back out additions
+
+ goto FreeDomainInfo;
+
+ } // back out all changes so far
+
+ } // delete the active other domain
+ }
+ }
+
+
+ //
+ // Make other domains persistent by writing to the registry
+ //
+ if (status == NERR_Success) {
+
+ LPNET_CONFIG_HANDLE SectionHandle = NULL;
+
+
+ if (NetpOpenConfigData(
+ &SectionHandle,
+ NULL, // no server name
+ SECT_NT_WKSTA,
+ FALSE // not read-only
+ ) != NERR_Success) {
+
+ //
+ // Ignore the error if the config section couldn't be found.
+ //
+ goto FreeDomainInfo;
+ }
+
+ //
+ // Set value for OtherDomains keyword in the wksta section.
+ // This is a "NULL-NULL" array (which corresponds to REG_MULTI_SZ).
+ // Ignore error if not set properly in the registry.
+ //
+ (void) NetpSetConfigTStrArray(
+ SectionHandle,
+ WKSTA_KEYWORD_OTHERDOMAINS,
+ CanonBuffer
+ );
+
+ (void) NetpCloseConfigData(SectionHandle);
+ }
+
+
+FreeDomainInfo:
+ if (OtherDomainsInfo != NULL) {
+ (void) LocalFree((HLOCAL) OtherDomainsInfo);
+ }
+
+FreeDgrNames:
+ MIDL_user_free((PVOID) DgrNames);
+
+FreeCanonBuffer:
+ (void) LocalFree((HLOCAL) CanonBuffer);
+
+ IF_DEBUG(INFO) {
+ NetpKdPrint(("WsSetOtherDomains: about to return %lu\n", status));
+ }
+
+ return status;
+}
+
+
+
+STATIC
+NET_API_STATUS
+WsPackageUserInfo(
+ IN DWORD Level,
+ IN DWORD UserInfoFixedLength,
+ IN PMSV1_0_GETUSERINFO_RESPONSE UserInfoResponse,
+ IN PDGRECEIVE_NAMES DgrNames,
+ IN DWORD DgrNamesCount,
+ IN OUT LPBYTE *FixedPortion,
+ IN OUT LPTSTR *EndOfVariableData,
+ IN OUT LPDWORD EntriesRead OPTIONAL
+ )
+/*++
+
+Routine Description:
+
+ This function writes the user information from LSA and datagram
+ receiver into the output buffer. It increments the EntriesRead
+ variable when a user entry is written into the output buffer.
+
+Arguments:
+
+ Level - Supplies the level of information to be returned.
+
+ UserInfoFixedLength - Supplies the length of the fixed portion of the
+ information structure.
+
+ UserInfoResponse - Supplies a pointer to the user information from the
+ authentication package.
+
+ DgrNames - Supplies an array of active datagram receiver names.
+
+ DgrNamesCount - Supplies the number of entries in DgrNames.
+
+ FixedPortion - Supplies a pointer to the output buffer where the next
+ entry of the fixed portion of the use information will be written.
+ This pointer is updated to point to the next fixed portion entry
+ after a user entry is written.
+
+ EndOfVariableData - Supplies a pointer just off the last available byte
+ in the output buffer. This is because the variable portion of the
+ user information is written into the output buffer starting from
+ the end.
+
+ This pointer is updated after any variable length information is
+ written to the output buffer.
+
+ EntriesRead - Supplies a running total of the number of entries read
+ into the output buffer. This value is incremented every time a
+ user entry is successfully written into the output buffer.
+
+Return Value:
+
+ NERR_Success - The current entry fits into the output buffer.
+
+ ERROR_MORE_DATA - The current entry does not fit into the output buffer.
+
+--*/
+{
+ if (((DWORD) *FixedPortion + UserInfoFixedLength) >=
+ (DWORD) *EndOfVariableData) {
+
+ //
+ // Fixed length portion does not fit.
+ //
+ return ERROR_MORE_DATA;
+ }
+
+ if (! WsFillUserInfoBuffer(
+ Level,
+ UserInfoResponse,
+ DgrNames,
+ DgrNamesCount,
+ FixedPortion,
+ EndOfVariableData,
+ UserInfoFixedLength
+ )) {
+ //
+ // Variable length portion does not fit.
+ //
+ return ERROR_MORE_DATA;
+ }
+
+ if (ARGUMENT_PRESENT(EntriesRead)) {
+ (*EntriesRead)++;
+ }
+
+ return NERR_Success;
+}
+
+
+
+
+STATIC
+BOOL
+WsFillUserInfoBuffer(
+ IN DWORD Level,
+ IN PMSV1_0_GETUSERINFO_RESPONSE UserInfo,
+ IN PDGRECEIVE_NAMES DgrNames,
+ IN DWORD DgrNamesCount,
+ IN OUT LPBYTE *FixedPortion,
+ IN OUT LPTSTR *EndOfVariableData,
+ IN DWORD UserInfoFixedLength
+ )
+/*++
+
+Routine Description:
+
+ This function fills an entry in the output buffer with the supplied user
+ information.
+
+ NOTE: This function assumes that the fixed size portion will fit into
+ the output buffer.
+
+ It also assumes that info structure level 1 is a superset of info
+ structure level 0, and that the offset to each common field is
+ exactly the same. This allows us to take advantage of a switch
+ statement without a break between the levels.
+
+Arguments:
+
+ Level - Supplies the level of information to be returned.
+
+ UserInfo - Supplies a pointer to the user information from the
+ authentication package.
+
+ DgrNames - Supplies an array of active datagram receiver names.
+
+ DgrNamesCount - Supplies the number of entries in DgrNames.
+
+ FixedPortion - Supplies a pointer to the output buffer where the next
+ entry of the fixed portion of the use information will be written.
+ This pointer is updated after a user entry is written to the
+ output buffer.
+
+ EndOfVariableData - Supplies a pointer just off the last available byte
+ in the output buffer. This is because the variable portion of the use
+ information is written into the output buffer starting from the end.
+ This pointer is updated after any variable length information is
+ written to the output buffer.
+
+ UserInfoFixedLength - Supplies the number of bytes needed to hold the
+ fixed size portion.
+
+Return Value:
+
+ Returns TRUE if entire entry fits into output buffer, FALSE otherwise.
+
+--*/
+{
+ PWKSTA_USER_INFO_1 WkstaUserInfo = (PWKSTA_USER_INFO_1) *FixedPortion;
+ PWKSTA_USER_INFO_1101 UserInfo1101 = (PWKSTA_USER_INFO_1101) *FixedPortion;
+
+
+ (DWORD) (*FixedPortion) += UserInfoFixedLength;
+
+ switch (Level) {
+
+ case 1:
+
+ //
+ // Logon server from authentication package
+ //
+
+ if (! WsCopyStringToBuffer(
+ &UserInfo->LogonServer,
+ *FixedPortion,
+ EndOfVariableData,
+ (LPWSTR *) &WkstaUserInfo->wkui1_logon_server
+ )) {
+ return FALSE;
+ }
+
+
+ //
+ // Logon Domain from authentication package
+ //
+ if (! WsCopyStringToBuffer(
+ &UserInfo->LogonDomainName,
+ *FixedPortion,
+ EndOfVariableData,
+ (LPWSTR *) &WkstaUserInfo->wkui1_logon_domain
+ )) {
+ return FALSE;
+ }
+
+
+ WsWriteOtherDomains(
+ DgrNames,
+ DgrNamesCount,
+ FixedPortion,
+ EndOfVariableData,
+ UserInfoFixedLength,
+ (LPWSTR *) &WkstaUserInfo->wkui1_oth_domains
+ );
+
+ //
+ // Fall through because level 1 is a superset of level 0
+ //
+
+ case 0:
+
+ //
+ // User name from authentication package
+ //
+
+ if (! WsCopyStringToBuffer(
+ &UserInfo->UserName,
+ *FixedPortion,
+ EndOfVariableData,
+ (LPWSTR *) &WkstaUserInfo->wkui1_username
+ )) {
+ return FALSE;
+ }
+
+ break;
+
+ case 1101:
+
+ WsWriteOtherDomains(
+ DgrNames,
+ DgrNamesCount,
+ FixedPortion,
+ EndOfVariableData,
+ UserInfoFixedLength,
+ (LPWSTR *) &UserInfo1101->wkui1101_oth_domains
+ );
+
+ break;
+
+ default:
+ //
+ // This should never happen.
+ //
+ NetpKdPrint(("WsFillUserInfoBuffer: Invalid level %u.\n", Level));
+ NetpAssert(FALSE);
+ }
+
+ return TRUE;
+}
+
+
+STATIC
+VOID
+WsWriteOtherDomains(
+ IN PDGRECEIVE_NAMES DgrNames,
+ IN DWORD DgrNamesCount,
+ IN OUT LPBYTE *FixedPortion,
+ IN OUT LPTSTR *EndOfVariableData,
+ IN DWORD UserInfoFixedLength,
+ OUT LPWSTR *OtherDomainsPointer
+ )
+/*++
+
+Routine Description:
+
+ This function writes to the output buffer the other domains field.
+
+Arguments:
+
+ DgrNames - Supplies an array of active datagram receiver names.
+
+ DgrNamesCount - Supplies the number of entries in DgrNames.
+
+ FixedPortion - Supplies a pointer to the output buffer where the next
+ entry of the fixed portion of the use information will be written.
+ This pointer is updated after a user entry is written to the
+ output buffer.
+
+ EndOfVariableData - Supplies a pointer just off the last available byte
+ in the output buffer. This is because the variable portion of the use
+ information is written into the output buffer starting from the end.
+ This pointer is updated after any variable length information is
+ written to the output buffer.
+
+ UserInfoFixedLength - Supplies the number of bytes needed to hold the
+ fixed size portion.
+
+Return Value:
+
+ Returns TRUE if entire entry fits into output buffer, FALSE otherwise.
+
+--*/
+{
+ DWORD i;
+ DWORD OtherDomainsCount = 0;
+
+
+ //
+ // Other domain names form a NULL terminated string each
+ // separated by a space.
+ //
+ for (i = 0; i < DgrNamesCount; i++) {
+
+ if (DgrNames[i].Type == OtherDomain) {
+
+ WsCopyStringToBuffer(
+ &DgrNames[i].DGReceiverName,
+ *FixedPortion,
+ EndOfVariableData,
+ OtherDomainsPointer
+ );
+
+ OtherDomainsCount++;
+
+ if (OtherDomainsCount > 1) {
+ (*OtherDomainsPointer)[
+ STRLEN(*OtherDomainsPointer)
+ ] = WS_OTH_DOMAIN_DELIMITER_CHAR;
+ }
+
+ IF_DEBUG(INFO) {
+ NetpKdPrint(("[Wksta] Other domains is %ws\n",
+ *OtherDomainsPointer));
+ }
+ }
+ }
+
+ if (OtherDomainsCount == 0) {
+ NetpCopyStringToBuffer(
+ NULL,
+ 0,
+ *FixedPortion,
+ EndOfVariableData,
+ OtherDomainsPointer
+ );
+ }
+}
diff --git a/private/net/svcdlls/wkssvc/server/useutil.c b/private/net/svcdlls/wkssvc/server/useutil.c
new file mode 100644
index 000000000..3d55f9057
--- /dev/null
+++ b/private/net/svcdlls/wkssvc/server/useutil.c
@@ -0,0 +1,1683 @@
+/*++
+
+Copyright (c) 1991-1992 Microsoft Corporation
+
+Module Name:
+
+ useutil.c
+
+Abstract:
+
+ This module contains the common utility routines for needed to
+ implement the NetUse APIs.
+
+Author:
+
+ Rita Wong (ritaw) 10-Mar-1991
+
+Revision History:
+
+--*/
+
+#include "wsutil.h"
+#include "wsdevice.h"
+#include "wsuse.h"
+#include "wsmain.h"
+#include <names.h>
+
+//-------------------------------------------------------------------//
+// //
+// Local function prototypes //
+// //
+//-------------------------------------------------------------------//
+
+STATIC
+NET_API_STATUS
+WsGrowUseTable(
+ VOID
+ );
+
+STATIC
+VOID
+WsFindLocal(
+ IN PUSE_ENTRY UseList,
+ IN LPTSTR Local,
+ OUT PUSE_ENTRY *MatchedPointer,
+ OUT PUSE_ENTRY *BackPointer
+ );
+
+
+//-------------------------------------------------------------------//
+// //
+// Global variables //
+// //
+//-------------------------------------------------------------------//
+
+//
+// Redirector name in NT string format
+//
+UNICODE_STRING RedirectorDeviceName;
+
+//
+// Use Table
+//
+USERS_OBJECT Use;
+
+
+
+NET_API_STATUS
+WsInitUseStructures(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This function creates the Use Table, and initialize the NT-style string
+ of the redirector device name.
+
+Arguments:
+
+ None
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ //
+ // Initialize NT-style redirector device name string.
+ //
+ RtlInitUnicodeString(&RedirectorDeviceName, DD_NFS_DEVICE_NAME_U);
+
+
+ //
+ // Allocate and initialize the Use Table which is an array of logged
+ // on user entries, with a linked list of use entries for each user.
+ //
+ return WsInitializeUsersObject(&Use);
+}
+
+
+VOID
+WsDestroyUseStructures(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This function destroys the Use Table.
+
+Arguments:
+
+ None
+
+Return Value:
+
+ None.
+
+--*/
+{
+ DWORD i;
+ PUSE_ENTRY UseEntry;
+ PUSE_ENTRY PreviousEntry;
+
+
+ //
+ // Close handles for every use entry that still exist and free the memory
+ // allocated for the use entry.
+ //
+ for (i = 0; i < Use.TableSize; i++) {
+
+ UseEntry = Use.Table[i].List;
+
+ while (UseEntry != NULL) {
+
+ (void) WsDeleteConnection(
+ &Use.Table[i].LogonId,
+ UseEntry->TreeConnection,
+ USE_NOFORCE
+ );
+
+ WsDeleteSymbolicLink(
+ UseEntry->Local,
+ UseEntry->TreeConnectStr
+ );
+
+ UseEntry->Remote->TotalUseCount -= UseEntry->UseCount;
+
+ if (UseEntry->Remote->TotalUseCount == 0) {
+ (void) LocalFree((HLOCAL) UseEntry->Remote);
+ }
+
+ PreviousEntry = UseEntry;
+ UseEntry = UseEntry->Next;
+
+ (void) LocalFree((HLOCAL) PreviousEntry);
+ }
+ }
+
+ //
+ // Free the array of logged on user entries, and delete the resource
+ // created to serialize access to the array.
+ //
+ WsDestroyUsersObject(&Use);
+}
+
+
+NET_API_STATUS
+WsFindUse(
+ IN PLUID LogonId,
+ IN PUSE_ENTRY UseList,
+ IN LPTSTR UseName,
+ OUT PHANDLE TreeConnection,
+ OUT PUSE_ENTRY *MatchedPointer,
+ OUT PUSE_ENTRY *BackPointer OPTIONAL
+ )
+/*++
+
+Routine Description:
+
+ This function searches the Use Table for the specified tree connection.
+ If the connection is found, NERR_Success is returned.
+
+ If the UseName is found in the Use Table (explicit connection), a
+ pointer to the matching use entry is returned. Otherwise, MatchedPointer
+ is set to NULL.
+
+ WARNING: This function assumes that the Use.TableResource is claimed.
+
+Arguments:
+
+ LogonId - Supplies a pointer to the user's Logon Id.
+
+ UseList - Supplies the use list of the user.
+
+ UseName - Supplies the name of the tree connection, this is either a
+ local device name or a UNC name.
+
+ TreeConnection - Returns a handle to the found tree connection.
+
+ MatchedPointer - Returns the pointer to the matching use entry. This
+ pointer is set to NULL if the specified use is an implicit
+ connection.
+
+ BackPointer - Returns the pointer to the entry previous to the matching
+ use entry if MatchedPointer is not NULL.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ PUSE_ENTRY Back;
+
+ IF_DEBUG(USE) {
+ NetpKdPrint(("[Wksta] WsFindUse: Usename is %ws\n", UseName));
+ }
+
+ //
+ // Look for use entry depending on whether the local device name or
+ // UNC name is specified.
+ //
+ if (UseName[1] != TCHAR_BACKSLASH) {
+
+ //
+ // Local device name is specified.
+ //
+ WsFindLocal(
+ UseList,
+ UseName,
+ MatchedPointer,
+ &Back
+ );
+
+ if (*MatchedPointer == NULL) {
+ return NERR_UseNotFound;
+ }
+ else {
+ *TreeConnection = (*MatchedPointer)->TreeConnection;
+ if (ARGUMENT_PRESENT(BackPointer)) {
+ *BackPointer = Back;
+ }
+ return NERR_Success;
+ }
+
+ }
+ else {
+
+ //
+ // UNC name is specified, need to find matching shared resource
+ // in use list.
+ //
+ WsFindUncName(
+ UseList,
+ UseName,
+ MatchedPointer,
+ &Back
+ );
+
+ if (*MatchedPointer == NULL) {
+
+ NET_API_STATUS status;
+
+ DWORD EnumConnectionHint = 0; // Hint size from redirector
+ LMR_REQUEST_PACKET Rrp; // Redirector request packet
+
+ PLMR_CONNECTION_INFO_0 UncList; // List of information on UNC
+ // connections
+ PLMR_CONNECTION_INFO_0 SavePtr;
+
+ DWORD i;
+ BOOL FoundImplicitEntry = FALSE;
+ DWORD UseNameLength = STRLEN(UseName);
+
+ UNICODE_STRING TreeConnectStr;
+
+
+ IF_DEBUG(USE) {
+ NetpKdPrint(("[Wksta] WsFindUse: No explicit entry\n"));
+ }
+
+ //
+ // Did not find an explicit connection, see if there is an
+ // implicit connection by enumerating all implicit connections
+ //
+ Rrp.Type = GetConnectionInfo;
+ Rrp.Version = REQUEST_PACKET_VERSION;
+ RtlCopyLuid(&Rrp.LogonId, LogonId);
+ Rrp.Level = 0;
+ Rrp.Parameters.Get.ResumeHandle = 0;
+
+ if ((status = WsDeviceControlGetInfo(
+ Redirector,
+ WsRedirDeviceHandle,
+ FSCTL_LMR_ENUMERATE_CONNECTIONS,
+ (PVOID) &Rrp,
+ sizeof(LMR_REQUEST_PACKET),
+ (LPBYTE *) &UncList,
+ MAXULONG,
+ EnumConnectionHint,
+ NULL
+ )) != NERR_Success) {
+ return status;
+ }
+
+ SavePtr = UncList;
+
+ for (i = 0; i < Rrp.Parameters.Get.TotalEntries &&
+ FoundImplicitEntry == FALSE; i++, UncList++) {
+ if (WsCompareStringU(
+ UncList->UNCName.Buffer,
+ UncList->UNCName.Length / sizeof(WCHAR),
+ UseName,
+ UseNameLength
+ ) == 0) {
+ FoundImplicitEntry = TRUE;
+ }
+ }
+
+ MIDL_user_free((PVOID) SavePtr);
+
+ //
+ // Fail if no such connection.
+ //
+ if (! FoundImplicitEntry) {
+ IF_DEBUG(USE) {
+ NetpKdPrint(("[Wksta] WsFindUse: No implicit entry\n"));
+ }
+ return NERR_UseNotFound;
+ }
+
+ //
+ // Otherwise open the connection and return the handle
+ //
+
+ //
+ // Replace \\ with \Device\LanmanRedirector\ in UseName
+ //
+ if ((status = WsCreateTreeConnectName(
+ UseName,
+ STRLEN(UseName),
+ NULL,
+ &TreeConnectStr
+ )) != NERR_Success) {
+ return status;
+ }
+
+ //
+ // Redirector will pick up the logon username and password
+ // from the LSA if the authentication package is loaded.
+ //
+ status = WsOpenCreateConnection(
+ &TreeConnectStr,
+ NULL,
+ NULL,
+ NULL,
+ 0, // no special flags
+ FILE_OPEN,
+ USE_WILDCARD,
+ TreeConnection,
+ NULL
+ );
+
+ (void) LocalFree(TreeConnectStr.Buffer);
+
+ return status;
+ }
+ else {
+
+ IF_DEBUG(USE) {
+ NetpKdPrint(("[Wksta] WsFindUse: Found an explicit entry\n"));
+ }
+
+ //
+ // Found an explicit UNC connection (NULL local device name).
+ //
+ NetpAssert((*MatchedPointer)->Local == NULL);
+
+ *TreeConnection = (*MatchedPointer)->TreeConnection;
+
+ if (ARGUMENT_PRESENT(BackPointer)) {
+ *BackPointer = Back;
+ }
+
+ return NERR_Success;
+ }
+ }
+}
+
+
+
+VOID
+WsFindInsertLocation(
+ IN PUSE_ENTRY UseList,
+ IN LPTSTR UncName,
+ OUT PUSE_ENTRY *MatchedPointer,
+ OUT PUSE_ENTRY *InsertPointer
+ )
+/*++
+
+Routine Description:
+
+ This function searches the use list for the location to insert a new use
+ entry. The use entry is inserted to the end of the use list so the
+ pointer to the last node in the use list is returned via InsertPointer.
+ We also have to save a pointer to the node with the same UNC name so that
+ the new use entry can be set to point to the same remote node (where the
+ UNC name is stored). This pointer is returned as MatchedPointer.
+
+ WARNING: This function assumes that the Use.TableResource has been claimed.
+
+Arguments:
+
+ UseList - Supplies the pointer to the use list.
+
+ UncName - Supplies the pointer to the shared resource (UNC name).
+
+ MatchedPointer - Returns a pointer to the node that holds the matching
+ UncName. If no matching UncName is found, this pointer is set to
+ NULL. If there are more than one node that has the same UNC name,
+ this pointer will point to the node with the NULL local device name,
+ if any; otherwise, if all nodes with matching UNC names have non-null
+ local device names, the pointer to the last matching node will be
+ returned.
+
+ InsertPointer - Returns a pointer to the last use entry, after which the
+ new entry is to be inserted.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ BOOL IsMatchWithNullDevice = FALSE;
+
+
+ *MatchedPointer = NULL;
+
+ while (UseList != NULL) {
+
+ //
+ // Do the string comparison only if we haven't found a matching UNC
+ // name with a NULL local device name.
+ //
+ if (! IsMatchWithNullDevice &&
+ (STRICMP((LPWSTR) UseList->Remote->UncName, UncName) == 0)) {
+
+ //
+ // Found matching entry
+ //
+ *MatchedPointer = UseList;
+
+ IsMatchWithNullDevice = (UseList->Local == NULL);
+ }
+
+ *InsertPointer = UseList;
+ UseList = UseList->Next;
+ }
+}
+
+
+
+VOID
+WsFindUncName(
+ IN PUSE_ENTRY UseList,
+ IN LPTSTR UncName,
+ OUT PUSE_ENTRY *MatchedPointer,
+ OUT PUSE_ENTRY *BackPointer
+ )
+/*++
+
+Routine Description:
+
+ This function searches the use list for the use entry with the specified
+ UNC name with a NULL local device name.
+
+ WARNING: This function assumes that the Use.TableResource has been claimed.
+
+Arguments:
+
+ UseList - Supplies the pointer to the use list.
+
+ UncName - Supplies the pointer to the shared resource (UNC name).
+
+ MatchedPointer - Returns a pointer to the node that holds the matching
+ UncName. If no matching UncName is found, this pointer is set to
+ NULL.
+
+ BackPointer - Returns a pointer to the entry previous to the found entry.
+ If UncName is not found, this pointer is set to NULL.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ *BackPointer = UseList;
+
+ while (UseList != NULL) {
+
+ if ((UseList->Local == NULL) &&
+ (STRICMP((LPWSTR) UseList->Remote->UncName, UncName) == 0)) {
+
+ //
+ // Found matching entry
+ //
+ *MatchedPointer = UseList;
+ return;
+ }
+ else {
+ *BackPointer = UseList;
+ UseList = UseList->Next;
+ }
+ }
+
+ //
+ // Did not find matching UNC name with a NULL local device name in the
+ // entire list.
+ //
+ *MatchedPointer = NULL;
+ *BackPointer = NULL;
+}
+
+
+STATIC
+VOID
+WsFindLocal(
+ IN PUSE_ENTRY UseList,
+ IN LPTSTR Local,
+ OUT PUSE_ENTRY *MatchedPointer,
+ OUT PUSE_ENTRY *BackPointer
+ )
+/*++
+
+Routine Description:
+
+ This function searches the use list for the specified local device name.
+
+ WARNING: This function assumes that the Use.TableResource has been claimed.
+
+Arguments:
+
+ UseList - Supplies the pointer to the use list.
+
+ Local - Supplies the local device name.
+
+ MatchedPointer - Returns a pointer to the use entry that holds the matching
+ local device name. If no matching local device name is found, this
+ pointer is set to NULL.
+
+ BackPointer - Returns a pointer to the entry previous to the found entry.
+ If the local device name is not found, this pointer is set to NULL.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ *BackPointer = UseList;
+
+ while (UseList != NULL) {
+
+ if ((UseList->Local != NULL) &&
+ (STRICMP(UseList->Local, Local) == 0)) {
+
+ //
+ // Found matching entry
+ //
+ *MatchedPointer = UseList;
+ return;
+ }
+ else {
+ *BackPointer = UseList;
+ UseList = UseList->Next;
+ }
+ }
+
+ //
+ // Did not find matching local device name in the entire list.
+ //
+ *MatchedPointer = NULL;
+ *BackPointer = NULL;
+}
+
+
+NET_API_STATUS
+WsCreateTreeConnectName(
+ IN LPTSTR UncName,
+ IN DWORD UncNameLength,
+ IN LPTSTR LocalName OPTIONAL,
+ OUT PUNICODE_STRING TreeConnectStr
+ )
+/*++
+
+Routine Description:
+
+ This function replaces \\ with \Device\LanmanRedirector\DEVICE: in the
+ UncName to form the NT-style tree connection name. A buffer is allocated
+ by this function and returned as the output string.
+
+Arguments:
+
+ UncName - Supplies the UNC name of the shared resource.
+
+ UncNameLength - Supplies the length of the UNC name.
+
+ LocalName - Supplies the local device name for the redirection.
+
+ TreeConnectStr - Returns a string with a newly allocated buffer that
+ contains the NT-style tree connection name.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ BOOLEAN IsDeviceName = FALSE;
+
+
+ if (ARGUMENT_PRESENT(LocalName)) {
+ IsDeviceName = ((STRNICMP(LocalName, TEXT("LPT"), 3) == 0) ||
+ (STRNICMP(LocalName, TEXT("COM"), 3) == 0));
+ }
+
+
+ //
+ // Initialize tree connect string maximum length to hold
+ // \Device\LanmanRedirector\DEVICE:\SERVER\SHARE
+ //
+ // The new redirector requires an additional character for name
+ // canonicalization.
+
+ if (!LoadedMRxSmbInsteadOfRdr) {
+ // The old redirector
+ TreeConnectStr->MaximumLength = (USHORT)(RedirectorDeviceName.Length +
+ (USHORT) (UncNameLength * sizeof(WCHAR)) +
+ (ARGUMENT_PRESENT(LocalName) ? (STRLEN(LocalName)*sizeof(WCHAR)) : 0) +
+ sizeof(WCHAR) + // For "\"
+ (IsDeviceName ? sizeof(WCHAR) : 0));
+ } else {
+ // The new redirector
+ TreeConnectStr->MaximumLength = (USHORT)(RedirectorDeviceName.Length +
+ (USHORT) (UncNameLength * sizeof(WCHAR)) +
+ (ARGUMENT_PRESENT(LocalName) ? ((STRLEN(LocalName)+1)*sizeof(WCHAR)) //+1 for ';'
+ : 0) +
+ sizeof(WCHAR) + // For "\"
+ (IsDeviceName ? sizeof(WCHAR) : 0));
+ }
+
+ if ((TreeConnectStr->Buffer = (PWSTR) LocalAlloc(
+ LMEM_ZEROINIT,
+ (UINT) TreeConnectStr->MaximumLength
+ )) == NULL) {
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ //
+ // Copy \Device\LanmanRedirector
+ //
+ RtlCopyUnicodeString(TreeConnectStr, &RedirectorDeviceName);
+
+ //
+ // Concatenate \DEVICE:
+ //
+ if (ARGUMENT_PRESENT(LocalName)) {
+ wcscat(TreeConnectStr->Buffer, L"\\");
+
+ TreeConnectStr->Length += sizeof(WCHAR);
+
+ // Concatenate the ; required by the new redirector for canonicalization
+ if (LoadedMRxSmbInsteadOfRdr) {
+
+ wcscat(TreeConnectStr->Buffer, L";");
+
+ TreeConnectStr->Length += sizeof(WCHAR);
+
+ }
+
+ wcscat(TreeConnectStr->Buffer, LocalName);
+
+ TreeConnectStr->Length += (USHORT)(STRLEN(LocalName)*sizeof(WCHAR));
+
+ if (IsDeviceName) {
+ wcscat(TreeConnectStr->Buffer, L":");
+
+ TreeConnectStr->Length += sizeof(WCHAR);
+
+ }
+
+ }
+
+ //
+ // Concatenate \SERVER\SHARE
+ //
+ wcscat(TreeConnectStr->Buffer, &UncName[1]);
+
+ TreeConnectStr->Length += (USHORT)((UncNameLength - 1) * sizeof(WCHAR));
+
+ return NERR_Success;
+}
+
+
+NET_API_STATUS
+WsOpenCreateConnection(
+ IN PUNICODE_STRING TreeConnectionName,
+ IN LPTSTR UserName OPTIONAL,
+ IN LPTSTR DomainName OPTIONAL,
+ IN LPTSTR Password OPTIONAL,
+ IN ULONG CreateFlags,
+ IN ULONG CreateDisposition,
+ IN ULONG ConnectionType,
+ OUT PHANDLE TreeConnectionHandle,
+ OUT PULONG Information OPTIONAL
+ )
+/*++
+
+Routine Description:
+
+ This function asks the redirector to either open an existing tree
+ connection (CreateDisposition == FILE_OPEN), or create a new tree
+ connection if one does not exist (CreateDisposition == FILE_OPEN_IF).
+
+ The password and user name passed to the redirector via the EA buffer
+ in the NtCreateFile call. The EA buffer is NULL if neither password
+ or user name is specified.
+
+ The redirector expects the EA descriptor string to be in Unicode
+ but the password and username strings to be in ANSI.
+
+Arguments:
+
+ TreeConnectionName - Supplies the name of the tree connection in NT-style
+ file name format: \Device\LanmanRedirector\SERVER\SHARE
+
+ UserName - Supplies the user name to create the tree connection with.
+
+ DomainName - Supplies the name of the domain to get user credentials from.
+
+ Password - Supplies the password to create the tree connection with.
+
+ CreateDisposition - Supplies the create disposition value to either
+ open or create the tree connection.
+
+ ConnectionType - Supplies the type of the connection (USE_xxx)
+
+ TreeConnectionHandle - Returns the handle to the tree connection
+ created/opened by the redirector.
+
+ Information - Returns the information field of the I/O status block.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NET_API_STATUS status;
+ NTSTATUS ntstatus;
+
+ OBJECT_ATTRIBUTES UncNameAttributes;
+ IO_STATUS_BLOCK IoStatusBlock;
+
+ PFILE_FULL_EA_INFORMATION EaBuffer = NULL;
+ PFILE_FULL_EA_INFORMATION Ea;
+ ULONG EaBufferSize = 0;
+
+ UCHAR EaNamePasswordSize = (UCHAR) (ROUND_UP_COUNT(
+ strlen(EA_NAME_PASSWORD) + sizeof(CHAR),
+ ALIGN_WCHAR
+ ) - sizeof(CHAR));
+ UCHAR EaNameUserNameSize = (UCHAR) (ROUND_UP_COUNT(
+ strlen(EA_NAME_USERNAME) + sizeof(CHAR),
+ ALIGN_WCHAR
+ ) - sizeof(CHAR));
+
+ UCHAR EaNameDomainNameSize = (UCHAR) (ROUND_UP_COUNT(
+ strlen(EA_NAME_DOMAIN) + sizeof(CHAR),
+ ALIGN_WCHAR
+ ) - sizeof(CHAR));
+
+ UCHAR EaNameTypeSize = (UCHAR) (ROUND_UP_COUNT(
+ strlen(EA_NAME_TYPE) + sizeof(CHAR),
+ ALIGN_DWORD
+ ) - sizeof(CHAR));
+
+ UCHAR EaNameConnectSize = (UCHAR) (ROUND_UP_COUNT(
+ strlen(EA_NAME_CONNECT) + sizeof(CHAR),
+ ALIGN_DWORD
+ ) - sizeof(CHAR));
+
+
+ USHORT PasswordSize = 0;
+ USHORT UserNameSize = 0;
+ USHORT DomainNameSize = 0;
+ USHORT TypeSize = sizeof(ULONG);
+
+
+
+ InitializeObjectAttributes(
+ &UncNameAttributes,
+ TreeConnectionName,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL
+ );
+
+ //
+ // Calculate the number of bytes needed for the EA buffer to put the
+ // password or user name.
+ //
+ if (ARGUMENT_PRESENT(Password)) {
+
+ PasswordSize = (USHORT) (wcslen(Password) * sizeof(WCHAR));
+
+ EaBufferSize = ROUND_UP_COUNT(
+ FIELD_OFFSET( FILE_FULL_EA_INFORMATION, EaName[0]) +
+ EaNamePasswordSize + sizeof(CHAR) +
+ PasswordSize,
+ ALIGN_DWORD
+ );
+ }
+
+ if (ARGUMENT_PRESENT(UserName)) {
+
+ UserNameSize = (USHORT) (wcslen(UserName) * sizeof(WCHAR));
+
+ EaBufferSize += ROUND_UP_COUNT(
+ FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName[0]) +
+ EaNameUserNameSize + sizeof(CHAR) +
+ UserNameSize,
+ ALIGN_DWORD
+ );
+ }
+
+ if (ARGUMENT_PRESENT(DomainName)) {
+
+ DomainNameSize = (USHORT) (wcslen(DomainName) * sizeof(WCHAR));
+
+ EaBufferSize += ROUND_UP_COUNT(
+ FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName[0]) +
+ EaNameDomainNameSize + sizeof(CHAR) +
+ DomainNameSize,
+ ALIGN_DWORD
+ );
+ }
+
+ if(CreateFlags & CREATE_NO_CONNECT)
+ {
+ EaBufferSize += ROUND_UP_COUNT(
+ FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName[0]) +
+ EaNameConnectSize + sizeof(CHAR),
+ ALIGN_DWORD
+ );
+ }
+
+
+ EaBufferSize += FIELD_OFFSET( FILE_FULL_EA_INFORMATION, EaName[0]) +
+ EaNameTypeSize + sizeof(CHAR) +
+ TypeSize;
+
+
+ //
+ // Allocate the EA buffer
+ //
+ if ((EaBuffer = (PFILE_FULL_EA_INFORMATION) LocalAlloc(
+ LMEM_ZEROINIT,
+ (UINT) EaBufferSize
+ )) == NULL) {
+ status = GetLastError();
+ goto FreeMemory;
+ }
+
+ Ea = EaBuffer;
+
+ if(CreateFlags & CREATE_NO_CONNECT)
+ {
+ //
+ // Copy the EA name into EA buffer. EA name length does not
+ // include the zero terminator.
+ //
+ strcpy((LPSTR) Ea->EaName, EA_NAME_CONNECT);
+ Ea->EaNameLength = EaNameConnectSize;
+
+ Ea->EaValueLength = 0;
+
+ Ea->NextEntryOffset = ROUND_UP_COUNT(
+ FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName[0]) +
+ EaNameConnectSize + sizeof(CHAR) +
+ 0,
+ ALIGN_DWORD
+ );
+
+ IF_DEBUG(USE) {
+ NetpKdPrint(("[Wksta] OpenCreate: After round, NextEntryOffset=%lu\n",
+ Ea->NextEntryOffset));
+ }
+
+ Ea->Flags = 0;
+
+ (ULONG) Ea += Ea->NextEntryOffset;
+ }
+
+ if (ARGUMENT_PRESENT(Password)) {
+
+ //
+ // Copy the EA name into EA buffer. EA name length does not
+ // include the zero terminator.
+ //
+ strcpy((LPSTR) Ea->EaName, EA_NAME_PASSWORD);
+ Ea->EaNameLength = EaNamePasswordSize;
+
+ //
+ // Copy the EA value into EA buffer. EA value length does not
+ // include the zero terminator.
+ //
+ wcscpy(
+ (LPWSTR) &(Ea->EaName[EaNamePasswordSize + sizeof(CHAR)]),
+ Password
+ );
+
+ Ea->EaValueLength = PasswordSize;
+
+ Ea->NextEntryOffset = ROUND_UP_COUNT(
+ FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName[0]) +
+ EaNamePasswordSize + sizeof(CHAR) +
+ PasswordSize,
+ ALIGN_DWORD
+ );
+
+ IF_DEBUG(USE) {
+ NetpKdPrint(("[Wksta] OpenCreate: After round, NextEntryOffset=%lu\n",
+ Ea->NextEntryOffset));
+ }
+
+ Ea->Flags = 0;
+
+ (ULONG) Ea += Ea->NextEntryOffset;
+ }
+
+ if (ARGUMENT_PRESENT(UserName)) {
+
+ //
+ // Copy the EA name into EA buffer. EA name length does not
+ // include the zero terminator.
+ //
+ strcpy((LPSTR) Ea->EaName, EA_NAME_USERNAME);
+ Ea->EaNameLength = EaNameUserNameSize;
+
+ //
+ // Copy the EA value into EA buffer. EA value length does not
+ // include the zero terminator.
+ //
+ wcscpy(
+ (LPWSTR) &(Ea->EaName[EaNameUserNameSize + sizeof(CHAR)]),
+ UserName
+ );
+
+ Ea->EaValueLength = UserNameSize;
+
+ Ea->NextEntryOffset = ROUND_UP_COUNT(
+ FIELD_OFFSET( FILE_FULL_EA_INFORMATION, EaName[0]) +
+ EaNameUserNameSize + sizeof(CHAR) +
+ UserNameSize,
+ ALIGN_DWORD
+ );
+ Ea->Flags = 0;
+
+ (ULONG) Ea += Ea->NextEntryOffset;
+ }
+
+ if (ARGUMENT_PRESENT(DomainName)) {
+
+ //
+ // Copy the EA name into EA buffer. EA name length does not
+ // include the zero terminator.
+ //
+ strcpy((LPSTR) Ea->EaName, EA_NAME_DOMAIN);
+ Ea->EaNameLength = EaNameDomainNameSize;
+
+ //
+ // Copy the EA value into EA buffer. EA value length does not
+ // include the zero terminator.
+ //
+ wcscpy(
+ (LPWSTR) &(Ea->EaName[EaNameDomainNameSize + sizeof(CHAR)]),
+ DomainName
+ );
+
+ Ea->EaValueLength = DomainNameSize;
+
+ Ea->NextEntryOffset = ROUND_UP_COUNT(
+ FIELD_OFFSET( FILE_FULL_EA_INFORMATION, EaName[0]) +
+ EaNameDomainNameSize + sizeof(CHAR) +
+ DomainNameSize,
+ ALIGN_DWORD
+ );
+ Ea->Flags = 0;
+
+ (ULONG) Ea += Ea->NextEntryOffset;
+ }
+
+ //
+ // Copy the EA for the connection type name into EA buffer. EA name length
+ // does not include the zero terminator.
+ //
+ strcpy((LPSTR) Ea->EaName, EA_NAME_TYPE);
+ Ea->EaNameLength = EaNameTypeSize;
+
+ *((PULONG) &(Ea->EaName[EaNameTypeSize + sizeof(CHAR)])) = ConnectionType;
+
+ Ea->EaValueLength = TypeSize;
+
+ Ea->NextEntryOffset = 0;
+ Ea->Flags = 0;
+
+ if ((status = WsImpersonateClient()) != NERR_Success) {
+ goto FreeMemory;
+ }
+
+ //
+ // Create or open a tree connection
+ //
+ ntstatus = NtCreateFile(
+ TreeConnectionHandle,
+ SYNCHRONIZE,
+ &UncNameAttributes,
+ &IoStatusBlock,
+ NULL,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ | FILE_SHARE_WRITE |
+ FILE_SHARE_DELETE,
+ CreateDisposition,
+ FILE_CREATE_TREE_CONNECTION
+ | FILE_SYNCHRONOUS_IO_NONALERT,
+ (PVOID) EaBuffer,
+ EaBufferSize
+ );
+
+ WsRevertToSelf();
+
+ if (NT_SUCCESS(ntstatus)) {
+ ntstatus = IoStatusBlock.Status;
+ }
+
+ if (ARGUMENT_PRESENT(Information)) {
+ *Information = IoStatusBlock.Information;
+ }
+
+ IF_DEBUG(USE) {
+ NetpKdPrint(("[Wksta] NtCreateFile returns %lx\n", ntstatus));
+ }
+
+ status = WsMapStatus(ntstatus);
+
+FreeMemory:
+ if (EaBuffer != NULL) {
+ // Prevent password from making it to pagefile.
+ RtlZeroMemory( EaBuffer, EaBufferSize );
+ (void) LocalFree((HLOCAL) EaBuffer);
+ }
+
+ return status;
+}
+
+
+NET_API_STATUS
+WsDeleteConnection(
+ IN PLUID LogonId,
+ IN HANDLE TreeConnection,
+ IN DWORD ForceLevel
+ )
+/*++
+
+Routine Description:
+
+ This function asks the redirector to delete the tree connection
+ associated with the tree connection handle, and closes the handle.
+
+Arguments:
+
+ LogonId - Supplies a pointer to the user's Logon Id.
+
+ TreeConnection - Supplies the handle to the tree connection created.
+
+ ForceLevel - Supplies the level of force to delete the tree connection.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NET_API_STATUS status;
+ LMR_REQUEST_PACKET Rrp; // Redirector request packet
+
+
+ //
+ // Map force level to values the redirector understand
+ //
+ switch (ForceLevel) {
+
+ case USE_NOFORCE:
+ case USE_LOTS_OF_FORCE:
+ Rrp.Level = ForceLevel;
+ break;
+
+ case USE_FORCE:
+ Rrp.Level = USE_NOFORCE;
+ break;
+
+ default:
+ NetpKdPrint(("[Wksta] Invalid force level %lu should never happen!\n",
+ ForceLevel));
+ NetpAssert(FALSE);
+ }
+
+ //
+ // Tell the redirector to delete the tree connection
+ //
+ Rrp.Version = REQUEST_PACKET_VERSION;
+ RtlCopyLuid(&Rrp.LogonId, LogonId);
+
+ status = WsRedirFsControl(
+ TreeConnection,
+ FSCTL_LMR_DELETE_CONNECTION,
+ &Rrp,
+ sizeof(LMR_REQUEST_PACKET),
+ NULL,
+ 0,
+ NULL
+ );
+
+ //
+ // Close the connection handle
+ //
+
+ if(status == NERR_Success)
+ {
+ (void) NtClose(TreeConnection);
+ }
+
+ return status;
+}
+
+
+BOOL
+WsRedirectionPaused(
+ IN LPTSTR LocalDeviceName
+ )
+/*++
+
+Routine Description:
+
+ This function checks to see if the redirection for the print and comm
+ devices are paused for the system. Since we are only checking a global
+ flag, there's no reason to protect it with a RESOURCE.
+
+Arguments:
+
+ LocalDeviceName - Supplies the name of the local device.
+
+Return Value:
+
+ Returns TRUE redirection is paused; FALSE otherwise
+
+--*/
+{
+
+ if ((STRNICMP(LocalDeviceName, TEXT("LPT"), 3) == 0) ||
+ (STRNICMP(LocalDeviceName, TEXT("COM"), 3) == 0)) {
+
+ //
+ // Redirection of print and comm devices are paused if
+ // workstation service is paused.
+ //
+ return (WsGlobalData.Status.dwCurrentState == SERVICE_PAUSED);
+
+ } else {
+
+ //
+ // Redirection of disk devices cannot be paused.
+ //
+ return FALSE;
+ }
+}
+
+
+VOID
+WsPauseOrContinueRedirection(
+ IN REDIR_OPERATION OperationType
+ )
+/*++
+
+Routine Description:
+
+ This function pauses or unpauses (based on OperationType) the redirection
+ of print or comm devices.
+
+Arguments:
+
+ OperationType - Supplies a value that causes redirection to be paused or
+ continued.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ DWORD Index; // Index to user entry in Use Table
+ PUSE_ENTRY UseEntry;
+
+
+ //
+ // Lock Use Table
+ //
+ if (! RtlAcquireResourceExclusive(&Use.TableResource, TRUE)) {
+ return;
+ }
+
+ //
+ // If we want to pause and we are already paused, or if we want to
+ // continue and we have not paused, just return.
+ //
+ if ((OperationType == PauseRedirection &&
+ WsGlobalData.Status.dwCurrentState == SERVICE_PAUSED) ||
+ (OperationType == ContinueRedirection &&
+ WsGlobalData.Status.dwCurrentState == SERVICE_RUNNING)) {
+
+ RtlReleaseResource(&Use.TableResource);
+ return;
+ }
+
+ //
+ // Pause or continue for all users
+ //
+ for (Index = 0; Index < Use.TableSize; Index++) {
+ UseEntry = Use.Table[Index].List;
+
+ while (UseEntry != NULL) {
+
+ if ((UseEntry->Local != NULL) &&
+ ((STRNICMP(TEXT("LPT"), UseEntry->Local, 3) == 0) ||
+ (STRNICMP(TEXT("COM"), UseEntry->Local, 3) == 0))) {
+
+ if (OperationType == PauseRedirection) {
+
+ //
+ // Pause the redirection
+ //
+
+ //
+ // Delete the symbolic link
+ //
+ WsDeleteSymbolicLink(
+ UseEntry->Local,
+ UseEntry->TreeConnectStr
+ );
+
+ }
+ else {
+
+ //
+ // Continue the redirection
+ //
+
+ if (WsCreateSymbolicLink(
+ UseEntry->Local,
+ USE_SPOOLDEV, // USE_CHARDEV is just as good
+ UseEntry->TreeConnectStr,
+ NULL
+ ) != NERR_Success) {
+
+ PUSE_ENTRY RestoredEntry = Use.Table[Index].List;
+
+
+ //
+ // Could not continue completely. Delete all
+ // symbolic links restored so far
+ //
+ while (RestoredEntry != UseEntry) {
+
+ if ((UseEntry->Local != NULL) &&
+ ((STRNICMP(TEXT("LPT"), UseEntry->Local, 3) == 0) ||
+ (STRNICMP(TEXT("COM"), UseEntry->Local, 3) == 0))) {
+
+ WsDeleteSymbolicLink(
+ RestoredEntry->Local,
+ RestoredEntry->TreeConnectStr
+ );
+ }
+
+ RestoredEntry = RestoredEntry->Next;
+ }
+
+ RtlReleaseResource(&Use.TableResource);
+ return;
+ }
+
+ }
+
+ }
+
+ UseEntry = UseEntry->Next;
+ }
+
+ } // for all users
+
+ if (OperationType == PauseRedirection) {
+ WsGlobalData.Status.dwCurrentState = SERVICE_PAUSED;
+ }
+ else {
+ WsGlobalData.Status.dwCurrentState = SERVICE_RUNNING;
+ }
+
+ //
+ // Use the same resource to protect access to the RedirectionPaused flag
+ // in WsGlobalData
+ //
+ RtlReleaseResource(&Use.TableResource);
+}
+
+
+
+NET_API_STATUS
+WsCreateSymbolicLink(
+ IN LPWSTR Local,
+ IN DWORD DeviceType,
+ IN LPWSTR TreeConnectStr,
+ IN PUSE_ENTRY UseList
+ )
+/*++
+
+Routine Description:
+
+ This function creates a symbolic link object for the specified local
+ device name which is linked to the tree connection name that has a
+ format of \Device\LanmanRedirector\Device:\Server\Share.
+
+Arguments:
+
+ Local - Supplies the local device name.
+
+ DeviceType - Supplies the shared resource device type.
+
+ TreeConnectStr - Supplies the tree connection name string which is
+ the link target of the symbolick link object.
+
+ UseList - Supplies the pointer to the use list.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NET_API_STATUS status = NERR_Success;
+ WCHAR TempBuf[64];
+
+
+ //
+ // To redirect a comm or print device, we need to see if we have
+ // redirected it once before by searching through all existing
+ // redirections.
+ //
+ if ((DeviceType == USE_CHARDEV) || (DeviceType == USE_SPOOLDEV)) {
+
+ PUSE_ENTRY MatchedPointer;
+ PUSE_ENTRY BackPointer;
+
+
+ WsFindLocal(
+ UseList,
+ Local,
+ &MatchedPointer,
+ &BackPointer
+ );
+
+ if (MatchedPointer != NULL) {
+ //
+ // Already redirected
+ //
+ return ERROR_ALREADY_ASSIGNED;
+ }
+ }
+ else {
+
+ if (! QueryDosDeviceW(
+ Local,
+ TempBuf,
+ 64
+ )) {
+
+ if (GetLastError() != ERROR_FILE_NOT_FOUND) {
+
+ //
+ // Most likely failure occurred because our output
+ // buffer is too small. It still means someone already
+ // has an existing symbolic link for this device.
+ //
+
+ return ERROR_ALREADY_ASSIGNED;
+ }
+
+ //
+ // ERROR_FILE_NOT_FOUND (translated from OBJECT_NAME_NOT_FOUND)
+ // means it does not exist and we can redirect this device.
+ //
+ }
+ else {
+
+ //
+ // QueryDosDevice successfully an existing symbolic link--
+ // somebody is already using this device.
+ //
+ return ERROR_ALREADY_ASSIGNED;
+ }
+ }
+
+ //
+ // Create a symbolic link object to the device we are redirecting
+ //
+ if (! DefineDosDeviceW(
+ DDD_RAW_TARGET_PATH |
+ DDD_NO_BROADCAST_SYSTEM,
+ Local,
+ TreeConnectStr
+ )) {
+
+ return GetLastError();
+ }
+ else {
+
+ return NERR_Success;
+ }
+}
+
+
+
+VOID
+WsDeleteSymbolicLink(
+ IN LPWSTR LocalDeviceName,
+ IN LPWSTR TreeConnectStr
+ )
+/*++
+
+Routine Description:
+
+ This function deletes the symbolic link we had created earlier for
+ the device.
+
+Arguments:
+
+ LocalDeviceName - Supplies the local device name string of which the
+ symbolic link object is created.
+
+ TreeConnectStr - Supplies a pointer to the Unicode string which
+ contains the link target string we want to match and delete.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ if (LocalDeviceName != NULL) {
+
+ if (! DefineDosDeviceW(
+ DDD_REMOVE_DEFINITION |
+ DDD_RAW_TARGET_PATH |
+ DDD_EXACT_MATCH_ON_REMOVE |
+ DDD_NO_BROADCAST_SYSTEM,
+ LocalDeviceName,
+ TreeConnectStr
+ )) {
+
+#if DBG
+ NetpKdPrint(("DefineDosDevice DEL of %ws %ws returned %ld\n",
+ LocalDeviceName, TreeConnectStr, GetLastError()));
+#endif
+
+ }
+ }
+}
+
+
+
+NET_API_STATUS
+WsUseCheckRemote(
+ IN LPTSTR RemoteResource,
+ OUT LPTSTR UncName,
+ OUT LPDWORD UncNameLength
+ )
+/*++
+
+Routine Description:
+
+ This function checks the validity of the remote resource name
+ specified to NetUseAdd.
+
+Arguments:
+
+ RemoteResource - Supplies the remote resource name specified by the API
+ caller.
+
+ UncName - Returns the canonicalized remote resource name.
+
+ UncNameLength - Returns the length of the canonicalized name.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NET_API_STATUS status;
+ DWORD PathType = 0;
+ LPTSTR Ptr;
+
+
+ if ((status = I_NetPathType(
+ NULL,
+ RemoteResource,
+ &PathType,
+ 0)) == NERR_Success) {
+
+ //
+ // Check for UNC type
+ //
+ if (PathType != ITYPE_UNC) {
+ IF_DEBUG(USE) {
+ NetpKdPrint(("[Wksta] WsUseCheckRemote not UNC type\n"));
+ }
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ //
+ // Canonicalize the name
+ //
+ status = I_NetPathCanonicalize(
+ NULL,
+ RemoteResource,
+ UncName,
+ (MAX_PATH) * sizeof(TCHAR),
+ NULL,
+ &PathType,
+ 0
+ );
+
+ if (status != NERR_Success) {
+ IF_DEBUG(USE) {
+ NetpKdPrint((
+ "[Wksta] WsUseCheckRemote: I_NetPathCanonicalize return %lu\n",
+ status
+ ));
+ }
+ return status;
+ }
+
+ IF_DEBUG(USE) {
+ NetpKdPrint(("[Wksta] WsUseCheckRemote: %ws\n", UncName));
+ }
+ }
+ else {
+ NetpKdPrint(("[Wksta] WsUseCheckRemote: I_NetPathType return %lu\n",
+ status));
+ return status;
+ }
+
+ //
+ // Detect illegal remote name in the form of \\XXX\YYY\zzz. We assume
+ // that the UNC name begins with exactly two leading backslashes.
+ //
+ if ((Ptr = STRCHR(UncName + 2, TCHAR_BACKSLASH)) == NULL) {
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ if (!LoadedMRxSmbInsteadOfRdr && STRCHR(Ptr + 1, TCHAR_BACKSLASH) != NULL) {
+ //
+ // There should not be anymore backslashes
+ //
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ *UncNameLength = STRLEN(UncName);
+ return NERR_Success;
+}
+
+
+NET_API_STATUS
+WsUseCheckLocal(
+ IN LPTSTR LocalDevice,
+ OUT LPTSTR Local,
+ OUT LPDWORD LocalLength
+ )
+/*++
+
+Routine Description:
+
+ This function checks the validity of the local device name
+ specified to NetUseAdd.
+
+Arguments:
+
+ LocalDevice - Supplies the local device name specified by the API
+ caller.
+
+ Local - Returns the canonicalized local device name.
+
+ LocalLength - Returns the length of the canonicalized name.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NET_API_STATUS status;
+ DWORD PathType = 0;
+
+
+ if ((status = I_NetPathType(
+ NULL,
+ LocalDevice,
+ &PathType,
+ 0)) == NERR_Success) {
+
+ //
+ // Check for DEVICE type
+ //
+ if ((PathType != (ITYPE_DEVICE | ITYPE_DISK)) &&
+ (PathType != (ITYPE_DEVICE | ITYPE_LPT)) &&
+ (PathType != (ITYPE_DEVICE | ITYPE_COM))) {
+ IF_DEBUG(USE) {
+ NetpKdPrint(("[Wksta] WsUseCheckLocal not DISK, LPT, or COM type\n"));
+ }
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ //
+ // Canonicalize the name
+ //
+ status = I_NetPathCanonicalize(
+ NULL,
+ LocalDevice,
+ Local,
+ (DEVLEN + 1) * sizeof(TCHAR),
+ NULL,
+ &PathType,
+ 0
+ );
+
+ if (status != NERR_Success) {
+ IF_DEBUG(USE) {
+ NetpKdPrint((
+ "[Wksta] WsUseCheckLocal: I_NetPathCanonicalize return %lu\n",
+ status
+ ));
+ }
+ return status;
+ }
+
+ IF_DEBUG(USE) {
+ NetpKdPrint(("[Wksta] WsUseCheckLocal: %ws\n", Local));
+ }
+
+ }
+ else {
+ NetpKdPrint(("[Wksta] WsUseCheckLocal: I_NetPathType return %lu\n",
+ status));
+ return status;
+ }
+
+ *LocalLength = STRLEN(Local);
+ return NERR_Success;
+}
diff --git a/private/net/svcdlls/wkssvc/server/wkssvc.def b/private/net/svcdlls/wkssvc/server/wkssvc.def
new file mode 100644
index 000000000..735407714
--- /dev/null
+++ b/private/net/svcdlls/wkssvc/server/wkssvc.def
@@ -0,0 +1,6 @@
+NAME wkssvc.dll
+
+DESCRIPTION 'NT LAN Manager Workstation Service'
+
+EXPORTS
+ ServiceEntry
diff --git a/private/net/svcdlls/wkssvc/server/wkssvc.rc b/private/net/svcdlls/wkssvc/server/wkssvc.rc
new file mode 100644
index 000000000..462b3f087
--- /dev/null
+++ b/private/net/svcdlls/wkssvc/server/wkssvc.rc
@@ -0,0 +1,12 @@
+#include <windows.h>
+
+#include <ntverp.h>
+
+#define VER_FILETYPE VFT_DLL
+#define VER_FILESUBTYPE VFT2_UNKNOWN
+#define VER_FILEDESCRIPTION_STR "Workstation Service DLL"
+#define VER_INTERNALNAME_STR "WKSSVC.DLL"
+#define VER_ORIGINALFILENAME_STR "WKSSVC.DLL"
+
+#include "common.ver"
+
diff --git a/private/net/svcdlls/wkssvc/server/wksta.c b/private/net/svcdlls/wkssvc/server/wksta.c
new file mode 100644
index 000000000..7f20a7cb1
--- /dev/null
+++ b/private/net/svcdlls/wkssvc/server/wksta.c
@@ -0,0 +1,1392 @@
+/*++
+
+Copyright (c) 1991-1992 Microsoft Corporation
+
+Module Name:
+
+ wksta.c
+
+Abstract:
+
+ This module contains the worker routines for the NetWksta APIs
+ implemented in the Workstation service.
+
+Author:
+
+ Rita Wong (ritaw) 20-Feb-1991
+
+Revision History:
+
+ 14-May-1992 JohnRo
+ Implemented "sticky set info" and registry watch.
+ Corrected an info level: 1015 should be 1013.
+--*/
+
+#include "wsutil.h"
+#include "wsdevice.h"
+#include "wssec.h"
+#include "wslsa.h"
+#include "wsconfig.h"
+#include "wswksta.h"
+#include <config.h>
+#include <confname.h>
+#include <prefix.h>
+
+//-------------------------------------------------------------------//
+// //
+// Local function prototypes //
+// //
+//-------------------------------------------------------------------//
+
+STATIC
+NET_API_STATUS
+WsValidateAndSetWksta(
+ IN DWORD Level,
+ IN LPBYTE Buffer,
+ OUT LPDWORD ErrorParameter OPTIONAL,
+ OUT LPDWORD Parmnum
+ );
+
+STATIC
+NET_API_STATUS
+WsGetSystemInfo(
+ IN DWORD Level,
+ OUT LPBYTE *BufferPointer
+ );
+
+STATIC
+NET_API_STATUS
+WsGetPlatformInfo(
+ IN DWORD Level,
+ OUT LPBYTE *BufferPointer
+ );
+
+STATIC
+NET_API_STATUS
+WsFillSystemBufferInfo(
+ IN DWORD Level,
+ IN DWORD NumberOfLoggedOnUsers,
+ OUT LPBYTE *OutputBuffer
+ );
+
+STATIC
+VOID
+WsUpdateRegistryToMatchWksta(
+ IN DWORD Level,
+ IN LPBYTE Buffer,
+ OUT LPDWORD ErrorParameter OPTIONAL
+ );
+
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetrWkstaGetInfo(
+ IN LPTSTR ServerName OPTIONAL,
+ IN DWORD Level,
+ OUT LPWKSTA_INFO WkstaInfo
+ )
+/*++
+
+Routine Description:
+
+ This function is the NetWkstaGetInfo entry point in the Workstation
+ service. It checks the security access of the caller before returning
+ one the information requested which is either system-wide, or redirector
+ specific.
+
+Arguments:
+
+ ServerName - Supplies the name of server to execute this function
+
+ Level - Supplies the requested level of information.
+
+ WkstaInfo - Returns a pointer to a buffer which contains the requested
+ workstation information.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NET_API_STATUS status = NERR_Success;
+ LPBYTE Buffer;
+
+ ACCESS_MASK DesiredAccess;
+
+
+ UNREFERENCED_PARAMETER(ServerName);
+
+ //
+ // Determine the desired access based on the specified info level.
+ //
+ switch (Level) {
+
+ //
+ // Guest access
+ //
+ case 100:
+ DesiredAccess = WKSTA_CONFIG_GUEST_INFO_GET;
+ break;
+
+ //
+ // User access
+ //
+ case 101:
+ DesiredAccess = WKSTA_CONFIG_USER_INFO_GET;
+ break;
+
+ //
+ // Admin or operator access
+ //
+ case 102:
+ case 502:
+ DesiredAccess = WKSTA_CONFIG_ADMIN_INFO_GET;
+ break;
+
+ default:
+ return ERROR_INVALID_LEVEL;
+ }
+
+ //
+ // Perform access validation on the caller.
+ //
+ if (NetpAccessCheckAndAudit(
+ WORKSTATION_DISPLAY_NAME, // Subsystem name
+ (LPTSTR) CONFIG_INFO_OBJECT, // Object type name
+ ConfigurationInfoSd, // Security descriptor
+ DesiredAccess, // Desired access
+ &WsConfigInfoMapping // Generic mapping
+ ) != NERR_Success) {
+
+ return ERROR_ACCESS_DENIED;
+ }
+
+ //
+ // Only allowed to proceed with get info if no one else is setting
+ //
+ if (! RtlAcquireResourceShared(&WsInfo.ConfigResource, TRUE)) {
+ return NERR_InternalError;
+ }
+
+ switch (Level) {
+
+ //
+ // System-wide information
+ //
+ case 100:
+ case 101:
+ case 102:
+ status = WsGetSystemInfo(Level, &Buffer);
+ if (status == NERR_Success) {
+ SET_SYSTEM_INFO_POINTER(WkstaInfo, Buffer);
+ }
+ break;
+
+ //
+ // Platform specific information
+ //
+ case 502:
+ status = WsGetPlatformInfo(
+ Level,
+ (LPBYTE *) &(WkstaInfo->WkstaInfo502)
+ );
+ break;
+
+ //
+ // This should have been caught earlier.
+ //
+ default:
+ NetpAssert(FALSE);
+ status = ERROR_INVALID_LEVEL;
+ }
+
+ RtlReleaseResource(&WsInfo.ConfigResource);
+ return status;
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetrWkstaSetInfo(
+ IN LPTSTR ServerName OPTIONAL,
+ IN DWORD Level,
+ IN LPWKSTA_INFO WkstaInfo,
+ OUT LPDWORD ErrorParameter OPTIONAL
+ )
+/*++
+
+Routine Description:
+
+ This function is the NetWkstaSetInfo entry point in the Workstation
+ service. It checks the security access of the caller to make sure
+ that the caller is allowed to set specific workstation information.
+
+Arguments:
+
+ ServerName - Supplies the name of server to execute this function
+
+ Level - Supplies the level of information.
+
+ WkstaInfo - Supplies a pointer to union structure of pointers to
+ buffer of fields to set. The level denotes the fields supplied in
+ this buffer.
+
+ ErrorParameter - Returns the identifier to the invalid parameter if
+ this function returns ERROR_INVALID_PARAMETER.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ WKSTA_INFO_502 OriginalWksta = WSBUF;
+ NET_API_STATUS status = NERR_Success;
+ DWORD Parmnum;
+
+ UNREFERENCED_PARAMETER(ServerName);
+
+ //
+ // Check for NULL input buffer
+ //
+ if (WkstaInfo->WkstaInfo502 == NULL) {
+ RETURN_INVALID_PARAMETER(ErrorParameter, PARM_ERROR_UNKNOWN);
+ }
+
+ //
+ // Only admins can set redirector configurable fields. Validate access.
+ //
+ if (NetpAccessCheckAndAudit(
+ WORKSTATION_DISPLAY_NAME, // Subsystem name
+ (LPTSTR) CONFIG_INFO_OBJECT, // Object type name
+ ConfigurationInfoSd, // Security descriptor
+ WKSTA_CONFIG_INFO_SET, // Desired access
+ &WsConfigInfoMapping // Generic mapping
+ ) != NERR_Success) {
+
+ return ERROR_ACCESS_DENIED;
+ }
+
+ //
+ // Serialize write access
+ //
+ if (! RtlAcquireResourceExclusive(&WsInfo.ConfigResource, TRUE)) {
+ return NERR_InternalError;
+ }
+
+ status = WsValidateAndSetWksta(
+ Level,
+ (LPBYTE) WkstaInfo->WkstaInfo502,
+ ErrorParameter,
+ &Parmnum
+ );
+
+ if (status != NERR_Success) {
+ goto CleanExit;
+ }
+
+ //
+ // Set NT redirector specific fields
+ //
+ status = WsUpdateRedirToMatchWksta(
+ Parmnum,
+ ErrorParameter
+ );
+
+ if (status != NERR_Success) {
+ goto CleanExit;
+ }
+
+ //
+ // Make updates "sticky" (update registry to match wksta).
+ //
+ WsUpdateRegistryToMatchWksta(
+ Level,
+ (LPBYTE) WkstaInfo->WkstaInfo502,
+ ErrorParameter
+ );
+
+CleanExit:
+ if (status != NERR_Success) {
+ WSBUF = OriginalWksta;
+ }
+ RtlReleaseResource(&WsInfo.ConfigResource);
+ return status;
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetrWkstaTransportEnum(
+ IN LPTSTR ServerName OPTIONAL,
+ IN OUT LPWKSTA_TRANSPORT_ENUM_STRUCT TransportInfo,
+ IN DWORD PreferedMaximumLength,
+ OUT LPDWORD TotalEntries,
+ IN OUT LPDWORD ResumeHandle OPTIONAL
+ )
+/*++
+
+Routine Description:
+
+ This function is the NetWkstaTransportEnum entry point in the
+ Workstation service.
+
+Arguments:
+
+ ServerName - Supplies the name of server to execute this function
+
+ TransportInfo - This structure supplies the level of information requested,
+ returns a pointer to the buffer allocated by the Workstation service
+ which contains a sequence of information structure of the specified
+ information level, and returns the number of entries read. The buffer
+ pointer is set to NULL if return code is not NERR_Success or
+ ERROR_MORE_DATA, or if EntriesRead returned is 0. The EntriesRead
+ value is only valid if the return code is NERR_Success or
+ ERROR_MORE_DATA.
+
+ PreferedMaximumLength - Supplies the number of bytes of information
+ to return in the buffer. If this value is MAXULONG, all available
+ information will be returned.
+
+ TotalEntries - Returns the total number of entries available. This value
+ is only valid if the return code is NERR_Success or ERROR_MORE_DATA.
+
+ ResumeHandle - Supplies a handle to resume the enumeration from where it
+ left off the last time through. Returns the resume handle if return
+ code is ERROR_MORE_DATA.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NET_API_STATUS status;
+ LMR_REQUEST_PACKET Rrp; // Redirector request packet
+ DWORD EnumTransportHintSize = 0; // Hint size from redirector
+
+ LPBYTE Buffer;
+
+
+ UNREFERENCED_PARAMETER(ServerName);
+
+ //
+ // Only level 0 is valid
+ //
+ if (TransportInfo->Level != 0) {
+ return ERROR_INVALID_LEVEL;
+ }
+
+ //
+ // Set up request packet. Output buffer structure is of enumerate
+ // transport type.
+ //
+ Rrp.Version = REQUEST_PACKET_VERSION;
+ Rrp.Type = EnumerateTransports;
+ Rrp.Level = TransportInfo->Level;
+ Rrp.Parameters.Get.ResumeHandle = (ARGUMENT_PRESENT(ResumeHandle)) ?
+ *ResumeHandle : 0;
+
+ //
+ // Get the requested information from the redirector.
+ //
+ status = WsDeviceControlGetInfo(
+ Redirector,
+ WsRedirDeviceHandle,
+ FSCTL_LMR_ENUMERATE_TRANSPORTS,
+ &Rrp,
+ sizeof(LMR_REQUEST_PACKET),
+ &Buffer,
+ PreferedMaximumLength,
+ EnumTransportHintSize,
+ NULL
+ );
+
+ //
+ // Return output parameters
+ //
+ if (status == NERR_Success || status == ERROR_MORE_DATA) {
+
+ SET_TRANSPORT_ENUM_POINTER(
+ TransportInfo,
+ Buffer,
+ Rrp.Parameters.Get.EntriesRead
+ );
+
+ *TotalEntries = Rrp.Parameters.Get.TotalEntries;
+
+ if (status == ERROR_MORE_DATA && ARGUMENT_PRESENT(ResumeHandle)) {
+ *ResumeHandle = Rrp.Parameters.Get.ResumeHandle;
+ }
+ }
+
+ return status;
+}
+
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetrWkstaTransportAdd (
+ IN LPTSTR ServerName OPTIONAL,
+ IN DWORD Level,
+ IN LPWKSTA_TRANSPORT_INFO_0 TransportInfo,
+ OUT LPDWORD ErrorParameter OPTIONAL
+ )
+/*++
+
+Routine Description:
+
+ This function is the NetWkstaTransportAdd entry point in the
+ Workstation service.
+
+Arguments:
+
+ ServerName - Supplies the name of server to execute this function
+
+ Level - Supplies the requested level of information.
+
+ TransportInfo - Supplies the information structure to add a new transport.
+
+ ErrorParameter - Returns the identifier to the invalid parameter if this
+ function returns ERROR_INVALID_PARAMETER.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+
+ UNREFERENCED_PARAMETER(ServerName);
+
+ //
+ // Only admins can add a transport. Validate access.
+ //
+ if (NetpAccessCheckAndAudit(
+ WORKSTATION_DISPLAY_NAME, // Subsystem name
+ (LPTSTR) CONFIG_INFO_OBJECT, // Object type name
+ ConfigurationInfoSd, // Security descriptor
+ WKSTA_CONFIG_INFO_SET, // Desired access
+ &WsConfigInfoMapping // Generic mapping
+ ) != NERR_Success) {
+
+ return ERROR_ACCESS_DENIED;
+ }
+
+
+ //
+ // 0 is the only valid level
+ //
+ if (Level != 0) {
+ return ERROR_INVALID_LEVEL;
+ }
+
+ if (TransportInfo->wkti0_transport_name == NULL) {
+ RETURN_INVALID_PARAMETER(ErrorParameter, TRANSPORT_NAME_PARMNUM);
+ }
+
+ return WsBindTransport(
+ TransportInfo->wkti0_transport_name,
+ TransportInfo->wkti0_quality_of_service,
+ ErrorParameter
+ );
+}
+
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetrWkstaTransportDel (
+ IN LPTSTR ServerName OPTIONAL,
+ IN LPTSTR TransportName,
+ IN DWORD ForceLevel
+ )
+/*++
+
+Routine Description:
+
+ This function is the NetWkstaTransportDel entry point in the
+ Workstation service.
+
+Arguments:
+
+ ServerName - Supplies the name of server to execute this function
+
+ TransportName - Supplies the name of the transport to delete.
+
+ ForceLevel - Supplies the level of force to delete the tree connections on
+ the transport we are unbinding from.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+
+ UNREFERENCED_PARAMETER(ServerName);
+
+ //
+ // Only admins can delete a transport. Validate access.
+ //
+ if (NetpAccessCheckAndAudit(
+ WORKSTATION_DISPLAY_NAME, // Subsystem name
+ (LPTSTR) CONFIG_INFO_OBJECT, // Object type name
+ ConfigurationInfoSd, // Security descriptor
+ WKSTA_CONFIG_INFO_SET, // Desired access
+ &WsConfigInfoMapping // Generic mapping
+ ) != NERR_Success) {
+
+ return ERROR_ACCESS_DENIED;
+ }
+
+ if (TransportName == NULL) {
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ //
+ // Check that ForceLevel parameter is valid, which the redirector and
+ // browser use to delete the connections on the transport we are
+ // unbinding from.
+ //
+ switch (ForceLevel) {
+
+ case USE_FORCE:
+ ForceLevel = USE_NOFORCE;
+ break;
+
+ case USE_NOFORCE:
+ case USE_LOTS_OF_FORCE:
+ break;
+
+ default:
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ return WsUnbindTransport(TransportName, ForceLevel);
+}
+
+
+
+STATIC
+NET_API_STATUS
+WsGetSystemInfo(
+ IN DWORD Level,
+ OUT LPBYTE *BufferPointer
+ )
+
+/*++
+
+Routine Description:
+
+ This function calls the Redirector FSD, the LSA subsystem and the
+ MSV1_0 authentication package, and the Datagram Receiver DD to get
+ the system wide information returned by NetWkstaGetInfo API.
+
+Arguments:
+
+ Level - Supplies the requested level of information.
+
+ BufferPointer - Returns a pointer to a buffer which contains the
+ requested workstation information.
+
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NET_API_STATUS status;
+ DWORD NumberOfLoggedOnUsers = 1;
+
+ //
+ // Get number of logged on users from the MSV1_0 authentication package
+ // if Level == 102.
+ //
+ if (Level == 102) {
+
+ PMSV1_0_ENUMUSERS_RESPONSE EnumUsersResponse;
+
+ //
+ // Ask authentication package to enumerate users who are physically
+ // logged to the local machine.
+ //
+ if ((status = WsLsaEnumUsers(
+ (LPBYTE *) &EnumUsersResponse
+ )) != NERR_Success) {
+ return status;
+ }
+
+ NumberOfLoggedOnUsers = EnumUsersResponse->NumberOfLoggedOnUsers;
+
+ (VOID) LsaFreeReturnBuffer(EnumUsersResponse);
+ }
+
+ //
+ // Put all the data collected into output buffer allocated by this routine.
+ //
+ return WsFillSystemBufferInfo(
+ Level,
+ NumberOfLoggedOnUsers,
+ BufferPointer
+ );
+}
+
+
+
+STATIC
+NET_API_STATUS
+WsGetPlatformInfo(
+ IN DWORD Level,
+ OUT LPBYTE *BufferPointer
+ )
+/*++
+
+Routine Description:
+
+ This function calls the Redirector FSD to get the Redirector platform
+ specific information returned by NetWkstaGetInfo API.
+
+Arguments:
+
+ Level - Supplies the requested level of information.
+
+ BufferPointer - Returns the pointer a buffer which contains the requested
+ redirector specific information.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NET_API_STATUS status;
+ LMR_REQUEST_PACKET Rrp; // Redirector request packet
+ PWKSTA_INFO_502 Info;
+
+ //
+ // There is only one redirector info level: 502.
+ //
+ NetpAssert(Level == 502);
+
+ //
+ // Set up request packet. Output buffer structure is of configuration
+ // information type.
+ //
+ Rrp.Version = REQUEST_PACKET_VERSION;
+ Rrp.Level = Level;
+ Rrp.Type = ConfigInformation;
+
+ //
+ // Get the requested information from the Redirector. This routine
+ // allocates the returned buffer.
+ //
+ status = WsDeviceControlGetInfo(
+ Redirector,
+ WsRedirDeviceHandle,
+ FSCTL_LMR_GET_CONFIG_INFO,
+ &Rrp,
+ sizeof(LMR_REQUEST_PACKET),
+ BufferPointer,
+ sizeof(WKSTA_INFO_502),
+ 0,
+ NULL
+ );
+
+ if (status == NERR_Success) {
+ Info = (PWKSTA_INFO_502) *BufferPointer;
+
+ //
+ // Fill datagram receiver fields in level 502 structure from global
+ // Workstation buffer (WSBUF). There are no FSCtl APIs to get or
+ // set them in the datagram receiver.
+ //
+ Info->wki502_num_mailslot_buffers =
+ WSBUF.wki502_num_mailslot_buffers;
+ Info->wki502_num_srv_announce_buffers =
+ WSBUF.wki502_num_srv_announce_buffers;
+ Info->wki502_max_illegal_datagram_events =
+ WSBUF.wki502_max_illegal_datagram_events;
+ Info->wki502_illegal_datagram_event_reset_frequency =
+ WSBUF.wki502_illegal_datagram_event_reset_frequency;
+ Info->wki502_log_election_packets =
+ WSBUF.wki502_log_election_packets;;
+ }
+
+ return status;
+}
+
+
+NET_API_STATUS
+WsValidateAndSetWksta(
+ IN DWORD Level,
+ IN LPBYTE Buffer,
+ OUT LPDWORD ErrorParameter OPTIONAL,
+ OUT LPDWORD Parmnum
+ )
+/*++
+
+Routine Description:
+
+ This function sets the user specified config fields into the global
+ WsInfo.WsConfigBuf (WSBUF) buffer and validates that the fields
+ are valid.
+
+ It returns the associated parmnum value so that the caller can
+ specify it to the redirector.
+
+Arguments:
+
+ Level - Supplies the requested level of information.
+
+ Buffer - Supplies a buffer which contains the user specified config
+ fields.
+
+ ErrorParameter - Receives the parmnum value of the field that is
+ invalid if ERROR_INVALID_PARAMETER is returned.
+
+ Parmnum - Receives the parmnum for the field(s) being set.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+
+ DWORD i;
+
+ //
+ // Perform range checking
+ //
+
+ switch (Level) {
+
+ case 502: // Set all fields
+ WSBUF = *((PWKSTA_INFO_502) Buffer);
+ *Parmnum = PARMNUM_ALL;
+ break;
+
+ case 1010: // char_wait
+ WSBUF.wki502_char_wait = *((LPDWORD) Buffer);
+ *Parmnum = WKSTA_CHARWAIT_PARMNUM;
+ break;
+
+ case 1011: // collection_time
+ WSBUF.wki502_collection_time = *((LPDWORD) Buffer);
+ *Parmnum = WKSTA_CHARTIME_PARMNUM;
+ break;
+
+ case 1012: // maximum_collection_count
+ WSBUF.wki502_maximum_collection_count = *((LPDWORD) Buffer);
+ *Parmnum = WKSTA_CHARCOUNT_PARMNUM;
+ break;
+
+ case 1013: // keep_conn
+ WSBUF.wki502_keep_conn = *((LPDWORD) Buffer);
+ *Parmnum = WKSTA_KEEPCONN_PARMNUM;
+ break;
+
+ case 1018: // sess_timeout
+ WSBUF.wki502_sess_timeout = *((LPDWORD) Buffer);
+ *Parmnum = WKSTA_SESSTIMEOUT_PARMNUM;
+ break;
+
+ case 1023: // siz_char_buf
+ WSBUF.wki502_siz_char_buf = *((LPDWORD) Buffer);
+ *Parmnum = WKSTA_SIZCHARBUF_PARMNUM;
+ break;
+
+ case 1033: // max_threads
+ WSBUF.wki502_max_threads = *((LPDWORD) Buffer);
+ *Parmnum = WKSTA_MAXTHREADS_PARMNUM;
+ break;
+
+ case 1041: // lock_quota
+ WSBUF.wki502_lock_quota = *((LPDWORD) Buffer);
+ *Parmnum = WKSTA_LOCKQUOTA_PARMNUM;
+ break;
+
+ case 1042: // lock_increment
+ WSBUF.wki502_lock_increment = *((LPDWORD) Buffer);
+ *Parmnum = WKSTA_LOCKINCREMENT_PARMNUM;
+ break;
+
+ case 1043: // lock_maximum
+ WSBUF.wki502_lock_maximum = *((LPDWORD) Buffer);
+ *Parmnum = WKSTA_LOCKMAXIMUM_PARMNUM;
+ break;
+
+ case 1044: // pipe_increment
+ WSBUF.wki502_pipe_increment = *((LPDWORD) Buffer);
+ *Parmnum = WKSTA_PIPEINCREMENT_PARMNUM;
+ break;
+
+ case 1045: // pipe_maximum
+ WSBUF.wki502_pipe_maximum = *((LPDWORD) Buffer);
+ *Parmnum = WKSTA_PIPEMAXIMUM_PARMNUM;
+ break;
+
+ case 1046: // dormant_file_limit
+ WSBUF.wki502_dormant_file_limit = *((LPDWORD) Buffer);
+ *Parmnum = WKSTA_DORMANTFILELIMIT_PARMNUM;
+ break;
+
+ case 1047: // cache_file_timeout
+ WSBUF.wki502_cache_file_timeout = *((LPDWORD) Buffer);
+ *Parmnum = WKSTA_CACHEFILETIMEOUT_PARMNUM;
+ break;
+
+ case 1048: // use_opportunistic_locking
+ WSBUF.wki502_use_opportunistic_locking = *((LPBOOL) Buffer);
+ *Parmnum = WKSTA_USEOPPORTUNISTICLOCKING_PARMNUM;
+ break;
+
+ case 1049: // use_unlock_behind
+ WSBUF.wki502_use_unlock_behind = *((LPBOOL) Buffer);
+ *Parmnum = WKSTA_USEUNLOCKBEHIND_PARMNUM;
+ break;
+
+ case 1050: // use_close_behind
+ WSBUF.wki502_use_close_behind = *((LPBOOL) Buffer);
+ *Parmnum = WKSTA_USECLOSEBEHIND_PARMNUM;
+ break;
+
+ case 1051: // buf_named_pipes
+ WSBUF.wki502_buf_named_pipes = *((LPBOOL) Buffer);
+ *Parmnum = WKSTA_BUFFERNAMEDPIPES_PARMNUM;
+ break;
+
+ case 1052: // use_lock_read_unlock
+ WSBUF.wki502_use_lock_read_unlock = *((LPBOOL) Buffer);
+ *Parmnum = WKSTA_USELOCKANDREADANDUNLOCK_PARMNUM;
+ break;
+
+ case 1053: // utilize_nt_caching
+ WSBUF.wki502_utilize_nt_caching = *((LPBOOL) Buffer);
+ *Parmnum = WKSTA_UTILIZENTCACHING_PARMNUM;
+ break;
+
+ case 1054: // use_raw_read
+ WSBUF.wki502_use_raw_read = *((LPBOOL) Buffer);
+ *Parmnum = WKSTA_USERAWREAD_PARMNUM;
+ break;
+
+ case 1055: // use_raw_write
+ WSBUF.wki502_use_raw_write = *((LPBOOL) Buffer);
+ *Parmnum = WKSTA_USERAWWRITE_PARMNUM;
+ break;
+
+ case 1056: // use_write_raw_data
+ WSBUF.wki502_use_write_raw_data = *((LPBOOL) Buffer);
+ *Parmnum = WKSTA_USEWRITERAWWITHDATA_PARMNUM;
+ break;
+
+ case 1057: // use_encryption
+ WSBUF.wki502_use_encryption = *((LPBOOL) Buffer);
+ *Parmnum = WKSTA_USEENCRYPTION_PARMNUM;
+ break;
+
+ case 1058: // buf_files_deny_write
+ WSBUF.wki502_buf_files_deny_write = *((LPBOOL) Buffer);
+ *Parmnum = WKSTA_BUFFILESWITHDENYWRITE_PARMNUM;
+ break;
+
+ case 1059: // buf_read_only_files
+ WSBUF.wki502_buf_read_only_files = *((LPBOOL) Buffer);
+ *Parmnum = WKSTA_BUFFERREADONLYFILES_PARMNUM;
+ break;
+
+ case 1060: // force_core_create_mode
+ WSBUF.wki502_force_core_create_mode = *((LPBOOL) Buffer);
+ *Parmnum = WKSTA_FORCECORECREATEMODE_PARMNUM;
+ break;
+
+ case 1061: // use_512_byte_max_transfer
+ WSBUF.wki502_use_512_byte_max_transfer = *((LPBOOL) Buffer);
+ *Parmnum = WKSTA_USE512BYTESMAXTRANSFER_PARMNUM;
+ break;
+
+ case 1062: // read_ahead_throughput
+ WSBUF.wki502_read_ahead_throughput = *((LPDWORD) Buffer);
+ *Parmnum = WKSTA_READAHEADTHRUPUT_PARMNUM;
+ break;
+
+ default:
+ if (ErrorParameter != NULL) {
+ *ErrorParameter = PARM_ERROR_UNKNOWN;
+ }
+ return ERROR_INVALID_LEVEL;
+ }
+
+
+ for (i = 0; WsInfo.WsConfigFields[i].Keyword != NULL; i++) {
+
+ //
+ // Check the range of all fields. If any fail, we return
+ // ERROR_INVALID_PARAMETER.
+ //
+ if (((WsInfo.WsConfigFields[i].DataType == DWordType) &&
+ (*(WsInfo.WsConfigFields[i].FieldPtr) <
+ WsInfo.WsConfigFields[i].Minimum ||
+ *(WsInfo.WsConfigFields[i].FieldPtr) >
+ WsInfo.WsConfigFields[i].Maximum))
+ ||
+ ((WsInfo.WsConfigFields[i].DataType == BooleanType) &&
+ (*(WsInfo.WsConfigFields[i].FieldPtr) != TRUE &&
+ *(WsInfo.WsConfigFields[i].FieldPtr) != FALSE))) {
+
+ //
+ // We have a problem if this is not a field we want
+ // to set, and we still happen to find a bad value.
+ //
+ NetpAssert((*Parmnum == PARMNUM_ALL) ||
+ (*Parmnum == WsInfo.WsConfigFields[i].Parmnum));
+
+ IF_DEBUG(INFO) {
+ NetpKdPrint((
+ PREFIX_WKSTA "Parameter %s has bad value %u, parmnum %u\n",
+ WsInfo.WsConfigFields[i].Keyword,
+ *(WsInfo.WsConfigFields[i].FieldPtr),
+ WsInfo.WsConfigFields[i].Parmnum
+ ));
+ }
+
+ RETURN_INVALID_PARAMETER(
+ ErrorParameter,
+ WsInfo.WsConfigFields[i].Parmnum
+ );
+
+ }
+ }
+
+ return NERR_Success;
+
+}
+
+
+NET_API_STATUS
+WsUpdateRedirToMatchWksta(
+ IN DWORD Parmnum,
+ OUT LPDWORD ErrorParameter OPTIONAL
+ )
+/*++
+
+Routine Description:
+
+ This function calls the redirector to set the redirector platform specific
+ information with the values found in the global WsInfo.WsConfigBuf (WSBUF)
+ buffer.
+
+Arguments:
+
+ Parmnum - Supplies the parameter number of the field if a single field
+ is being set. If all fields are being set, this value is PARMNUM_ALL.
+
+
+ ErrorParameter - Returns the identifier to the invalid parameter in Buffer
+ if this function returns ERROR_INVALID_PARAMETER.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NET_API_STATUS ApiStatus;
+ LMR_REQUEST_PACKET Rrp; // Redirector request packet
+
+
+ //
+ // Set up request packet. Input buffer structure is of configuration
+ // information type.
+ //
+ Rrp.Version = REQUEST_PACKET_VERSION;
+ Rrp.Level = 502;
+ Rrp.Type = ConfigInformation;
+ Rrp.Parameters.Set.WkstaParameter = Parmnum;
+
+ //
+ // Set the information in the Redirector.
+ //
+ ApiStatus = WsRedirFsControl(
+ WsRedirDeviceHandle,
+ FSCTL_LMR_SET_CONFIG_INFO,
+ &Rrp,
+ sizeof(LMR_REQUEST_PACKET),
+ (PVOID) &WSBUF,
+ sizeof(WSBUF),
+ NULL
+ );
+
+ if (ApiStatus == ERROR_INVALID_PARAMETER && ARGUMENT_PRESENT(ErrorParameter)) {
+
+ IF_DEBUG(INFO) {
+ NetpKdPrint((
+ PREFIX_WKSTA "NetrWkstaSetInfo: invalid parameter is %lu\n",
+ Rrp.Parameters.Set.WkstaParameter));
+ }
+
+ *ErrorParameter = Rrp.Parameters.Set.WkstaParameter;
+ }
+
+ return ApiStatus;
+
+}
+
+
+VOID
+WsUpdateRegistryToMatchWksta(
+ IN DWORD Level,
+ IN LPBYTE Buffer,
+ OUT LPDWORD ErrorParameter OPTIONAL
+ )
+/*++
+
+Routine Description:
+
+ This function calls the registry to update the platform specific
+ information in the registry.
+
+ If any write operation to the registry fails, there is nothing we can
+ do about it because chances are good that we will not be able to back
+ out the changes since that requires more writes to the registry. When
+ this happens, the discrepancy between the registry and the redirector
+ will be straightened out when next key change notify occurs.
+
+Arguments:
+
+ Level - Supplies the level of information.
+
+ Buffer - Supplies a buffer which contains the information structure
+ to set.
+
+ ErrorParameter - Returns the identifier to the invalid parameter in Buffer
+ if this function returns ERROR_INVALID_PARAMETER.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ NET_API_STATUS ApiStatus;
+ LPNET_CONFIG_HANDLE SectionHandle = NULL;
+ DWORD i;
+
+
+ //
+ // Open section of config data.
+ //
+ ApiStatus = NetpOpenConfigData(
+ &SectionHandle,
+ NULL, // Local server.
+ SECT_NT_WKSTA, // Section name.
+ FALSE // Don't want read-only access.
+ );
+
+ if (ApiStatus != NERR_Success) {
+ return;
+ }
+
+ //
+ // Macro to update one value in the registry.
+ // Assumes that Buffer starts with the new value for this field.
+ //
+
+#define WRITE_ONE_PARM_TO_REGISTRY( KeyNamePart, TypeFlag ) \
+ { \
+ if (TypeFlag == BooleanType) { \
+ (void) NetpSetConfigBool( \
+ SectionHandle, \
+ WKSTA_KEYWORD_ ## KeyNamePart, \
+ * ((LPBOOL) Buffer) ); \
+ } else { \
+ NetpAssert( TypeFlag == DWordType ); \
+ (void) NetpSetConfigDword( \
+ SectionHandle, \
+ WKSTA_KEYWORD_ ## KeyNamePart, \
+ * ((LPDWORD) Buffer) ); \
+ } \
+ }
+
+ //
+ // Update field based on the info level.
+ //
+
+ switch (Level) {
+
+ case 502: // Set all fields
+
+ for (i = 0; WsInfo.WsConfigFields[i].Keyword != NULL; i++) {
+ //
+ // Write this field to the registry.
+ //
+ if (WsInfo.WsConfigFields[i].DataType == DWordType) {
+
+ (void) NetpSetConfigDword(
+ SectionHandle,
+ WsInfo.WsConfigFields[i].Keyword,
+ * ((LPDWORD) WsInfo.WsConfigFields[i].FieldPtr)
+ );
+
+ } else {
+
+ NetpAssert(WsInfo.WsConfigFields[i].DataType == BooleanType);
+
+ (void) NetpSetConfigBool(
+ SectionHandle,
+ WsInfo.WsConfigFields[i].Keyword,
+ * ((LPBOOL) WsInfo.WsConfigFields[i].FieldPtr)
+ );
+ }
+ }
+
+ break;
+
+ case 1010: // char_wait
+ WRITE_ONE_PARM_TO_REGISTRY( CHARWAIT, DWordType );
+ break;
+
+ case 1011: // collection_time
+ WRITE_ONE_PARM_TO_REGISTRY( COLLECTIONTIME, DWordType );
+ break;
+
+ case 1012: // maximum_collection_count
+ WRITE_ONE_PARM_TO_REGISTRY( MAXCOLLECTIONCOUNT, DWordType );
+ break;
+
+ case 1013: // keep_conn
+ WRITE_ONE_PARM_TO_REGISTRY( KEEPCONN, DWordType );
+ break;
+
+ case 1018: // sess_timeout
+ WRITE_ONE_PARM_TO_REGISTRY( SESSTIMEOUT, DWordType );
+ break;
+
+ case 1023: // siz_char_buf
+ WRITE_ONE_PARM_TO_REGISTRY( SIZCHARBUF, DWordType );
+ break;
+
+ case 1033: // max_threads
+ WRITE_ONE_PARM_TO_REGISTRY( MAXTHREADS, DWordType );
+ break;
+
+ case 1041: // lock_quota
+ WRITE_ONE_PARM_TO_REGISTRY( LOCKQUOTA, DWordType );
+ break;
+
+ case 1042: // lock_increment
+ WRITE_ONE_PARM_TO_REGISTRY( LOCKINCREMENT, DWordType );
+ break;
+
+ case 1043: // lock_maximum
+ WRITE_ONE_PARM_TO_REGISTRY( LOCKMAXIMUM, DWordType );
+ break;
+
+ case 1044: // pipe_increment
+ WRITE_ONE_PARM_TO_REGISTRY( PIPEINCREMENT, DWordType );
+ break;
+
+ case 1045: // pipe_maximum
+ WRITE_ONE_PARM_TO_REGISTRY( PIPEMAXIMUM, DWordType );
+ break;
+
+ case 1046: // dormant_file_limit
+ WRITE_ONE_PARM_TO_REGISTRY( DORMANTFILELIMIT, DWordType );
+ break;
+
+ case 1047: // cache_file_timeout
+ WRITE_ONE_PARM_TO_REGISTRY( CACHEFILETIMEOUT, DWordType );
+ break;
+
+ case 1048: // use_opportunistic_locking
+ WRITE_ONE_PARM_TO_REGISTRY( USEOPLOCKING, BooleanType );
+ break;
+
+ case 1049: // use_unlock_behind
+ WRITE_ONE_PARM_TO_REGISTRY( USEUNLOCKBEHIND, BooleanType );
+ break;
+
+ case 1050: // use_close_behind
+ WRITE_ONE_PARM_TO_REGISTRY( USECLOSEBEHIND, BooleanType );
+ break;
+
+ case 1051: // buf_named_pipes
+ WRITE_ONE_PARM_TO_REGISTRY( BUFNAMEDPIPES, BooleanType );
+ break;
+
+ case 1052: // use_lock_read_unlock
+ WRITE_ONE_PARM_TO_REGISTRY( USELOCKREADUNLOCK, BooleanType );
+ break;
+
+ case 1053: // utilize_nt_caching
+ WRITE_ONE_PARM_TO_REGISTRY( UTILIZENTCACHING, BooleanType );
+ break;
+
+ case 1054: // use_raw_read
+ WRITE_ONE_PARM_TO_REGISTRY( USERAWREAD, BooleanType );
+ break;
+
+ case 1055: // use_raw_write
+ WRITE_ONE_PARM_TO_REGISTRY( USERAWWRITE, BooleanType );
+ break;
+
+ case 1056: // use_write_raw_data
+ WRITE_ONE_PARM_TO_REGISTRY( USEWRITERAWDATA, BooleanType );
+ break;
+
+ case 1057: // use_encryption
+ WRITE_ONE_PARM_TO_REGISTRY( USEENCRYPTION, BooleanType );
+ break;
+
+ case 1058: // buf_files_deny_write
+ WRITE_ONE_PARM_TO_REGISTRY( BUFFILESDENYWRITE, BooleanType );
+ break;
+
+ case 1059: // buf_read_only_files
+ WRITE_ONE_PARM_TO_REGISTRY( BUFREADONLYFILES, BooleanType );
+ break;
+
+ case 1060: // force_core_create_mode
+ WRITE_ONE_PARM_TO_REGISTRY( FORCECORECREATE, BooleanType );
+ break;
+
+ case 1061: // use_512_byte_max_transfer
+ WRITE_ONE_PARM_TO_REGISTRY( USE512BYTEMAXTRANS, BooleanType );
+ break;
+
+ case 1062: // read_ahead_throughput
+ WRITE_ONE_PARM_TO_REGISTRY( READAHEADTHRUPUT, DWordType );
+ break;
+
+
+ default:
+ //
+ // This should never happen
+ //
+ NetpAssert(FALSE);
+ }
+
+ if (SectionHandle != NULL) {
+ (VOID) NetpCloseConfigData( SectionHandle );
+ }
+}
+
+
+STATIC
+NET_API_STATUS
+WsFillSystemBufferInfo(
+ IN DWORD Level,
+ IN DWORD NumberOfLoggedOnUsers,
+ OUT LPBYTE *OutputBuffer
+ )
+/*++
+
+Routine Description:
+
+ This function calculates the exact length of the output buffer needed,
+ allocates that amount, and fill the output buffer with all the requested
+ system-wide workstation information.
+
+ NOTE: This function assumes that info structure level 102 is a superset
+ of info structure level 100 and 101, and that the offset to each
+ common field is exactly the same. This allows us to take
+ advantage of a switch statement without a break between the levels.
+
+Arguments:
+
+ Level - Supplies the level of information to be returned.
+
+ NumberOfLoggedOnUsers - Supplies the number of users logged on
+ interactively.
+
+ OutputBuffer - Returns a pointer to the buffer allocated by this routine
+ which is filled with the requested system-wide workstation
+ information.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failing to allocate the
+ output buffer.
+
+--*/
+{
+ PWKSTA_INFO_102 WkstaSystemInfo;
+
+ LPBYTE FixedDataEnd;
+ LPTSTR EndOfVariableData;
+
+ DWORD SystemInfoFixedLength = SYSTEM_INFO_FIXED_LENGTH(Level);
+ DWORD TotalBytesNeeded = SystemInfoFixedLength +
+ (WsInfo.WsComputerNameLength +
+ WsInfo.WsPrimaryDomainNameLength +
+ 3) * sizeof(TCHAR); // include NULL character
+ // for LAN root
+
+
+ if ((*OutputBuffer = MIDL_user_allocate(TotalBytesNeeded)) == NULL) {
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+ RtlZeroMemory((PVOID) *OutputBuffer, TotalBytesNeeded);
+
+ WkstaSystemInfo = (PWKSTA_INFO_102) *OutputBuffer;
+
+ FixedDataEnd = (LPBYTE) ((DWORD) *OutputBuffer + SystemInfoFixedLength);
+ EndOfVariableData = (LPTSTR) ((DWORD) *OutputBuffer + TotalBytesNeeded);
+
+ //
+ // Put the data into the output buffer.
+ //
+ switch (Level) {
+
+ case 102:
+
+ WkstaSystemInfo->wki102_logged_on_users = NumberOfLoggedOnUsers;
+
+ case 101:
+
+ //
+ // LAN root is set to NULL on NT.
+ //
+ NetpCopyStringToBuffer(
+ NULL,
+ 0,
+ FixedDataEnd,
+ &EndOfVariableData,
+ &WkstaSystemInfo->wki102_lanroot
+ );
+
+ case 100:
+
+ WkstaSystemInfo->wki102_platform_id = WsInfo.RedirectorPlatform;
+ WkstaSystemInfo->wki102_ver_major = WsInfo.MajorVersion;
+ WkstaSystemInfo->wki102_ver_minor = WsInfo.MinorVersion;
+
+ NetpCopyStringToBuffer(
+ WsInfo.WsComputerName,
+ WsInfo.WsComputerNameLength,
+ FixedDataEnd,
+ &EndOfVariableData,
+ &WkstaSystemInfo->wki102_computername
+ );
+
+ NetpCopyStringToBuffer(
+ WsInfo.WsPrimaryDomainName,
+ WsInfo.WsPrimaryDomainNameLength,
+ FixedDataEnd,
+ &EndOfVariableData,
+ &WkstaSystemInfo->wki102_langroup
+ );
+
+ break;
+
+ default:
+ //
+ // This should never happen.
+ //
+ NetpKdPrint(("WsFillSystemBufferInfo: Invalid level %lu\n", Level));
+ NetpAssert(FALSE);
+ }
+
+ return NERR_Success;
+}
diff --git a/private/net/svcdlls/wkssvc/server/ws.h b/private/net/svcdlls/wkssvc/server/ws.h
new file mode 100644
index 000000000..89c68cb2d
--- /dev/null
+++ b/private/net/svcdlls/wkssvc/server/ws.h
@@ -0,0 +1,137 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ ws.h
+
+Abstract:
+
+ Private header file for the NT Workstation service included by every
+ module of the Workstation service.
+
+Author:
+
+ Rita Wong (ritaw) 15-Feb-1991
+
+Revision History:
+
+--*/
+
+#ifndef _WS_INCLUDED_
+#define _WS_INCLUDED_
+
+
+#include <nt.h> // NT definitions
+#include <ntrtl.h> // NT runtime library definitions
+#include <nturtl.h>
+
+#include <windef.h> // Win32 type definitions
+#include <winbase.h> // Win32 base API prototypes
+#include <winsvc.h> // Win32 service control APIs
+
+#include <lmcons.h> // LAN Manager common definitions
+#include <lmerr.h> // LAN Manager network error definitions
+#include <lmsname.h> // LAN Manager service names
+#include <lmapibuf.h> // NetApiBufferFree
+
+#include <netlib.h> // LAN Man utility routines
+#include <netlibnt.h> // NetpNtStatusToApiStatus
+#include <netdebug.h> // NetpDbgPrint
+#include <tstring.h> // Transitional string functions
+#include <icanon.h> // I_Net canonicalize functions
+#include <align.h> // ROUND_UP_COUNT macro
+
+#include <rpc.h> // DataTypes and runtime APIs
+#include <rpcutil.h> // Prototypes for MIDL user functions
+#include <wkssvc.h> // Generated by the MIDL complier
+
+
+//
+// Debug trace level bits for turning on/off trace statements in the
+// Workstation service
+//
+
+//
+// NetWksta[Get|Set]Info APIs
+//
+#define WKSTA_DEBUG_INFO 0x00000001
+
+//
+// NetWkstaUser APIs
+//
+#define WKSTA_DEBUG_USER 0x00000002
+
+//
+// NetUse APIs
+//
+#define WKSTA_DEBUG_USE 0x00000004
+
+//
+// NetAlert APIs
+//
+#define WKSTA_DEBUG_ALERT 0x00000008
+
+//
+// NetServerEnum
+//
+#define WKSTA_DEBUG_SERVER_ENUM 0x00000010
+
+//
+// Utility trace statements
+//
+#define WKSTA_DEBUG_UTIL 0x00000020
+
+//
+// Configuration trace statements
+//
+#define WKSTA_DEBUG_CONFIG 0x00000040
+
+//
+// Main service functionality
+//
+#define WKSTA_DEBUG_MAIN 0x00000080
+
+//
+// NetMessageBufferSend
+//
+#define WKSTA_DEBUG_MESSAGE 0x00000100
+
+//
+// Logon support trace statements
+//
+#define WKSTA_DEBUG_LOGON 0x00000200
+
+//
+// All debug flags on
+//
+#define WKSTA_DEBUG_ALL 0xFFFFFFFF
+
+
+#if DBG
+
+#define STATIC
+
+extern DWORD WorkstationTrace;
+
+#define DEBUG if (TRUE)
+
+#define IF_DEBUG(Function) if (WorkstationTrace & WKSTA_DEBUG_ ## Function)
+
+#else
+
+#define STATIC static
+
+#define DEBUG if (FALSE)
+
+#define IF_DEBUG(Function) if (FALSE)
+
+#endif // DBG
+
+extern NET_API_STATUS
+WsUpdateStatus(
+ VOID
+ );
+
+#endif // ifdef _WS_INCLUDED_
diff --git a/private/net/svcdlls/wkssvc/server/wsbind.h b/private/net/svcdlls/wkssvc/server/wsbind.h
new file mode 100644
index 000000000..3fbdb983a
--- /dev/null
+++ b/private/net/svcdlls/wkssvc/server/wsbind.h
@@ -0,0 +1,64 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ wsbind.h
+
+Abstract:
+
+ Private header file to be included by Workstation service modules that
+ need to call into the NT Redirector and the NT Datagram Receiver.
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) August - 08 -1991
+
+Revision History:
+
+--*/
+
+
+#ifndef _WSBIND_INCLUDED_
+#define _WSBIND_INCLUDED_
+
+typedef struct _WS_BIND_REDIR {
+ HANDLE EventHandle;
+ BOOL Bound;
+ IO_STATUS_BLOCK IoStatusBlock;
+ LMR_REQUEST_PACKET Packet;
+} WS_BIND_REDIR, *PWS_BIND_REDIR;
+
+typedef struct _WS_BIND_DGREC {
+ HANDLE EventHandle;
+ BOOL Bound;
+ IO_STATUS_BLOCK IoStatusBlock;
+ LMDR_REQUEST_PACKET Packet;
+} WS_BIND_DGREC, *PWS_BIND_DGREC;
+
+typedef struct _WS_BIND {
+ LIST_ENTRY ListEntry;
+ PWS_BIND_REDIR Redir;
+ PWS_BIND_DGREC Dgrec;
+ ULONG TransportNameLength; // not including terminator
+ WCHAR TransportName[1]; // Name of transport provider
+} WS_BIND, *PWS_BIND;
+
+
+NET_API_STATUS
+WsAsyncBindTransport(
+ IN LPTSTR transportName,
+ IN DWORD qualityOfService,
+ IN PLIST_ENTRY pHeader
+ );
+
+VOID
+WsUnbindTransport2(
+ IN PWS_BIND pBind
+ );
+
+extern HANDLE WsRedirAsyncDeviceHandle; // redirector
+extern HANDLE WsDgrecAsyncDeviceHandle; // datagram receiver or "bowser"
+
+#endif // ifndef _WSBIND_INCLUDED_
diff --git a/private/net/svcdlls/wkssvc/server/wsconfig.c b/private/net/svcdlls/wkssvc/server/wsconfig.c
new file mode 100644
index 000000000..a638324ce
--- /dev/null
+++ b/private/net/svcdlls/wkssvc/server/wsconfig.c
@@ -0,0 +1,1385 @@
+/*++
+
+Copyright (c) 1991-1992 Microsoft Corporation
+
+Module Name:
+
+ wsconfig.c
+
+Abstract:
+
+ This module contains the Workstation service configuration routines.
+
+Author:
+
+ Rita Wong (ritaw) 22-May-1991
+
+Revision History:
+
+ 08-May-1992 JohnRo
+ Wksta transports are just an array of values for one key,
+ not an entire section. Ditto for other domains for the browser.
+ 13-May-1992 JohnRo
+ Reworked to share code with registry watch code.
+
+--*/
+
+#include "ws.h"
+#include <ntlsa.h> // LsaQueryInformationPolicy
+#include "wsdevice.h"
+#include "wsconfig.h"
+#include "wsbind.h"
+#include "wsutil.h"
+#include "wsmain.h"
+
+#include <config.h> // NT config file helpers in netlib
+#include <configp.h> // USE_WIN32_CONFIG (if defined), etc.
+#include <confname.h> // Section and keyword equates.
+#include <lmapibuf.h> // NetApiBufferFree().
+#include <lmsname.h> // WORKSTATION_DISPLAY_NAME
+#include <prefix.h> // PREFIX_ equates.
+#include <strarray.h> // LPTSTR_ARRAY, etc.
+#include <stdlib.h> // wcscpy().
+
+#include <apperr.h> // Eventlog message IDs
+#include <lmerrlog.h> // Eventlog message IDs
+
+#define WS_LINKAGE_REGISTRY_PATH L"LanmanWorkstation\\Linkage"
+#define WS_BIND_VALUE_NAME L"Bind"
+
+//-------------------------------------------------------------------//
+// //
+// Local Function Prototypes //
+// //
+//-------------------------------------------------------------------//
+
+STATIC
+NTSTATUS
+WsBindATransport(
+ IN PWSTR ValueName,
+ IN ULONG ValueType,
+ IN PVOID ValueData,
+ IN ULONG ValueLength,
+ IN PVOID Context,
+ IN PVOID EntryContext
+ );
+
+//-------------------------------------------------------------------//
+// //
+// Global variables //
+// //
+//-------------------------------------------------------------------//
+
+
+//
+// Workstation configuration information structure which holds the
+// computername, primary domain, wksta config buffer, and a resource
+// to serialize access to the whole thing.
+//
+WSCONFIGURATION_INFO WsInfo;
+
+STATIC WS_REDIR_FIELDS WsFields[] = {
+
+ {WKSTA_KEYWORD_CHARWAIT,
+ (LPDWORD) &WSBUF.wki502_char_wait,
+ 3600, 0, 65535, DWordType, WKSTA_CHARWAIT_PARMNUM},
+ {WKSTA_KEYWORD_MAXCOLLECTIONCOUNT,
+ (LPDWORD) &WSBUF.wki502_maximum_collection_count,
+ 16, 0, 65535, DWordType, WKSTA_CHARCOUNT_PARMNUM},
+ {WKSTA_KEYWORD_COLLECTIONTIME,
+ (LPDWORD) &WSBUF.wki502_collection_time,
+ 250, 0, 65535000, DWordType, WKSTA_CHARTIME_PARMNUM},
+ {WKSTA_KEYWORD_KEEPCONN,
+ (LPDWORD) &WSBUF.wki502_keep_conn,
+ 600, 1, 65535, DWordType, WKSTA_KEEPCONN_PARMNUM},
+ {WKSTA_KEYWORD_MAXCMDS,
+ (LPDWORD) &WSBUF.wki502_max_cmds,
+ 5, 0, 255, DWordType, PARMNUM_ALL}, // Not settable
+ {WKSTA_KEYWORD_SESSTIMEOUT,
+ (LPDWORD) &WSBUF.wki502_sess_timeout,
+ 45, 10, 65535, DWordType, WKSTA_SESSTIMEOUT_PARMNUM},
+ {WKSTA_KEYWORD_SIZCHARBUF,
+ (LPDWORD) &WSBUF.wki502_siz_char_buf,
+ 512, 64, 4096, DWordType, WKSTA_SIZCHARBUF_PARMNUM},
+ {WKSTA_KEYWORD_MAXTHREADS,
+ (LPDWORD) &WSBUF.wki502_max_threads,
+ 17, 1, 256, DWordType, WKSTA_MAXTHREADS_PARMNUM},
+
+ {WKSTA_KEYWORD_LOCKQUOTA,
+ (LPDWORD) &WSBUF.wki502_lock_quota,
+ 6144, 0, MAXULONG, DWordType, WKSTA_LOCKQUOTA_PARMNUM},
+ {WKSTA_KEYWORD_LOCKINCREMENT,
+ (LPDWORD) &WSBUF.wki502_lock_increment,
+ 10, 0, MAXULONG, DWordType, WKSTA_LOCKINCREMENT_PARMNUM},
+ {WKSTA_KEYWORD_LOCKMAXIMUM,
+ (LPDWORD) &WSBUF.wki502_lock_maximum,
+ 500, 0, MAXULONG, DWordType, WKSTA_LOCKMAXIMUM_PARMNUM},
+ {WKSTA_KEYWORD_PIPEINCREMENT,
+ (LPDWORD) &WSBUF.wki502_pipe_increment,
+ 10, 0, MAXULONG, DWordType, WKSTA_PIPEINCREMENT_PARMNUM},
+ {WKSTA_KEYWORD_PIPEMAXIMUM,
+ (LPDWORD) &WSBUF.wki502_pipe_maximum,
+ 500, 0, MAXULONG, DWordType, WKSTA_PIPEMAXIMUM_PARMNUM},
+ {WKSTA_KEYWORD_CACHEFILETIMEOUT,
+ (LPDWORD) &WSBUF.wki502_cache_file_timeout,
+ 40, 0, MAXULONG, DWordType, WKSTA_CACHEFILETIMEOUT_PARMNUM},
+ {WKSTA_KEYWORD_DORMANTFILELIMIT,
+ (LPDWORD) &WSBUF.wki502_dormant_file_limit,
+ 45, 0, MAXULONG, DWordType, WKSTA_DORMANTFILELIMIT_PARMNUM},
+
+ {WKSTA_KEYWORD_READAHEADTHRUPUT,
+ (LPDWORD) &WSBUF.wki502_read_ahead_throughput,
+ MAXULONG,0, MAXULONG, DWordType, WKSTA_READAHEADTHRUPUT_PARMNUM},
+
+
+ {WKSTA_KEYWORD_MAILSLOTBUFFERS,
+ (LPDWORD) &WSBUF.wki502_num_mailslot_buffers,
+ 3, 0, MAXULONG, DWordType, PARMNUM_ALL}, // Not settable
+
+ {WKSTA_KEYWORD_SERVERANNOUNCEBUFS,
+ (LPDWORD) &WSBUF.wki502_num_srv_announce_buffers,
+ 20, 0, MAXULONG, DWordType, PARMNUM_ALL}, // Not settable
+ {WKSTA_KEYWORD_NUM_ILLEGAL_DG_EVENTS,
+ (LPDWORD) &WSBUF.wki502_max_illegal_datagram_events,
+ 5, 0, MAXULONG, DWordType, PARMNUM_ALL}, // Not settable
+ {WKSTA_KEYWORD_ILLEGAL_DG_RESET_TIME,
+ (LPDWORD) &WSBUF.wki502_illegal_datagram_event_reset_frequency,
+ 60, 0, MAXULONG, DWordType, PARMNUM_ALL}, // Not settable
+ {WKSTA_KEYWORD_LOG_ELECTION_PACKETS,
+ (LPDWORD) &WSBUF.wki502_log_election_packets,
+ FALSE, 0, MAXULONG, BooleanType, PARMNUM_ALL}, // Not settable
+
+ {WKSTA_KEYWORD_USEOPLOCKING,
+ (LPDWORD) &WSBUF.wki502_use_opportunistic_locking,
+ TRUE, 0, 0, BooleanType, WKSTA_USEOPPORTUNISTICLOCKING_PARMNUM},
+ {WKSTA_KEYWORD_USEUNLOCKBEHIND,
+ (LPDWORD) &WSBUF.wki502_use_unlock_behind,
+ TRUE, 0, 0, BooleanType, WKSTA_USEUNLOCKBEHIND_PARMNUM},
+ {WKSTA_KEYWORD_USECLOSEBEHIND,
+ (LPDWORD) &WSBUF.wki502_use_close_behind,
+ TRUE, 0, 0, BooleanType, WKSTA_USECLOSEBEHIND_PARMNUM},
+ {WKSTA_KEYWORD_BUFNAMEDPIPES,
+ (LPDWORD) &WSBUF.wki502_buf_named_pipes,
+ TRUE, 0, 0, BooleanType, WKSTA_BUFFERNAMEDPIPES_PARMNUM},
+ {WKSTA_KEYWORD_USELOCKREADUNLOCK,
+ (LPDWORD) &WSBUF.wki502_use_lock_read_unlock,
+ TRUE, 0, 0, BooleanType, WKSTA_USELOCKANDREADANDUNLOCK_PARMNUM},
+ {WKSTA_KEYWORD_UTILIZENTCACHING,
+ (LPDWORD) &WSBUF.wki502_utilize_nt_caching,
+ TRUE, 0, 0, BooleanType, WKSTA_UTILIZENTCACHING_PARMNUM},
+ {WKSTA_KEYWORD_USERAWREAD,
+ (LPDWORD) &WSBUF.wki502_use_raw_read,
+ TRUE, 0, 0, BooleanType, WKSTA_USERAWREAD_PARMNUM},
+ {WKSTA_KEYWORD_USERAWWRITE,
+ (LPDWORD) &WSBUF.wki502_use_raw_write,
+ TRUE, 0, 0, BooleanType, WKSTA_USERAWWRITE_PARMNUM},
+ {WKSTA_KEYWORD_USEWRITERAWDATA,
+ (LPDWORD) &WSBUF.wki502_use_write_raw_data,
+ TRUE, 0, 0, BooleanType, WKSTA_USEWRITERAWWITHDATA_PARMNUM},
+ {WKSTA_KEYWORD_USEENCRYPTION,
+ (LPDWORD) &WSBUF.wki502_use_encryption,
+ TRUE, 0, 0, BooleanType, WKSTA_USEENCRYPTION_PARMNUM},
+ {WKSTA_KEYWORD_BUFFILESDENYWRITE,
+ (LPDWORD) &WSBUF.wki502_buf_files_deny_write,
+ TRUE, 0, 0, BooleanType, WKSTA_BUFFILESWITHDENYWRITE_PARMNUM},
+ {WKSTA_KEYWORD_BUFREADONLYFILES,
+ (LPDWORD) &WSBUF.wki502_buf_read_only_files,
+ TRUE, 0, 0, BooleanType, WKSTA_BUFFERREADONLYFILES_PARMNUM},
+ {WKSTA_KEYWORD_FORCECORECREATE,
+ (LPDWORD) &WSBUF.wki502_force_core_create_mode,
+ TRUE, 0, 0, BooleanType, WKSTA_FORCECORECREATEMODE_PARMNUM},
+ {WKSTA_KEYWORD_USE512BYTEMAXTRANS,
+ (LPDWORD) &WSBUF.wki502_use_512_byte_max_transfer,
+ FALSE, 0, 0, BooleanType, WKSTA_USE512BYTESMAXTRANSFER_PARMNUM},
+
+ {NULL, NULL, 0, 0, BooleanType}
+
+ };
+
+//
+// For specifying the importance of a transport when binding to it.
+// The higher the number means that the transport will be searched
+// first.
+//
+STATIC DWORD QualityOfService = 65536;
+
+
+DWORD
+WsInAWorkgroup(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This function determines whether we are a member of a domain, or of
+ a workgroup. First it checks to make sure we're running on a Windows NT
+ system (otherwise we're obviously in a domain) and if so, queries LSA
+ to get the Primary domain SID, if this is NULL, we're in a workgroup.
+
+ If we fail for some random unexpected reason, we'll pretend we're in a
+ domain (it's more restrictive).
+
+Arguments:
+ None
+
+Return Value:
+
+ TRUE - We're in a workgroup
+ FALSE - We're in a domain
+
+--*/
+{
+ NT_PRODUCT_TYPE ProductType;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ LSA_HANDLE Handle;
+ NTSTATUS Status;
+ PPOLICY_PRIMARY_DOMAIN_INFO PolicyPrimaryDomainInfo = NULL;
+ DWORD Result = FALSE;
+
+
+ Status = RtlGetNtProductType(&ProductType);
+
+ if (!NT_SUCCESS(Status)) {
+ NetpKdPrint((
+ PREFIX_WKSTA "Could not get Product type\n"));
+ return FALSE;
+ }
+
+ if (ProductType == NtProductLanManNt) {
+ return(FALSE);
+ }
+
+ InitializeObjectAttributes(&ObjectAttributes, NULL, 0, 0, NULL);
+
+ Status = LsaOpenPolicy(NULL,
+ &ObjectAttributes,
+ POLICY_VIEW_LOCAL_INFORMATION,
+ &Handle);
+
+ if (!NT_SUCCESS(Status)) {
+ NetpKdPrint((
+ PREFIX_WKSTA "Could not open LSA Policy Database\n"));
+ return FALSE;
+ }
+
+ Status = LsaQueryInformationPolicy(Handle, PolicyPrimaryDomainInformation,
+ (PVOID *) &PolicyPrimaryDomainInfo);
+
+ if (NT_SUCCESS(Status)) {
+
+ if (PolicyPrimaryDomainInfo->Sid == NULL) {
+ Result = TRUE;
+ }
+ else {
+ Result = FALSE;
+ }
+ }
+
+ if (PolicyPrimaryDomainInfo) {
+ LsaFreeMemory((PVOID)PolicyPrimaryDomainInfo);
+ }
+
+ LsaClose(Handle);
+
+ return(Result);
+}
+
+
+NET_API_STATUS
+WsGetWorkstationConfiguration(
+ VOID
+ )
+{
+ NET_API_STATUS status;
+ LPNET_CONFIG_HANDLE WorkstationSection;
+ LPTSTR ComputerName;
+ LPTSTR DomainNameT;
+ DWORD version;
+
+ BYTE Buffer[max(sizeof(LMR_REQUEST_PACKET) + (MAX_PATH + 1) * sizeof(WCHAR) +
+ (DNLEN + 1) * sizeof(WCHAR),
+ sizeof(LMDR_REQUEST_PACKET))];
+
+ PLMR_REQUEST_PACKET Rrp = (PLMR_REQUEST_PACKET) Buffer;
+ PLMDR_REQUEST_PACKET Drrp = (PLMDR_REQUEST_PACKET) Buffer;
+
+
+ //
+ // Lock config information structure for write access since we are
+ // initializing the data in the structure.
+ //
+ if (! RtlAcquireResourceExclusive(&WsInfo.ConfigResource, TRUE)) {
+ return NERR_InternalError;
+ }
+
+ //
+ // Set pointer to configuration fields structure
+ //
+ WsInfo.WsConfigFields = WsFields;
+
+ //
+ // Get the version name.
+ //
+
+ version = GetVersion( );
+ WsInfo.MajorVersion = version & 0xff;
+ WsInfo.MinorVersion = (version >> 8) & 0xff;
+ WsInfo.RedirectorPlatform = PLATFORM_ID_NT;
+
+ //
+ // Get the configured computer name. NetpGetComputerName allocates
+ // the memory to hold the computername string using NetApiBufferAllocate().
+ //
+ if ((status = NetpGetComputerName(
+ &ComputerName
+ )) != NERR_Success) {
+ RtlReleaseResource(&WsInfo.ConfigResource);
+ return status;
+ }
+
+ if ((status = I_NetNameCanonicalize(
+ NULL,
+ ComputerName,
+ (LPTSTR) WsInfo.WsComputerName,
+ sizeof( WsInfo.WsComputerName ),
+ NAMETYPE_COMPUTER,
+ 0
+ )) != NERR_Success) {
+
+ LPWSTR SubString[1];
+
+ NetpKdPrint((
+ PREFIX_WKSTA FORMAT_LPTSTR " is an invalid computername.\n",
+ ComputerName
+ ));
+
+ SubString[0] = ComputerName;
+
+ WsLogEvent(
+ APE_BAD_COMPNAME,
+ 1,
+ SubString,
+ NERR_Success
+ );
+
+ (void) NetApiBufferFree((PVOID) ComputerName);
+ RtlReleaseResource(&WsInfo.ConfigResource);
+ return status;
+ }
+
+ //
+ // Free memory allocated by NetpGetComputerName.
+ //
+ (void) NetApiBufferFree(ComputerName);
+
+ WsInfo.WsComputerNameLength = STRLEN((LPWSTR) WsInfo.WsComputerName);
+
+ //
+ // Open config file and get handle to the [LanmanWorkstation] section
+ //
+
+ if ((status = NetpOpenConfigData(
+ &WorkstationSection,
+ NULL, // local (no server name)
+ SECT_NT_WKSTA,
+ TRUE // want read-only access
+ )) != NERR_Success) {
+ RtlReleaseResource(&WsInfo.ConfigResource);
+ return status;
+ }
+
+ IF_DEBUG(CONFIG) {
+ NetpKdPrint((PREFIX_WKSTA "ComputerName " FORMAT_LPTSTR ", length %lu\n",
+ WsInfo.WsComputerName, WsInfo.WsComputerNameLength));
+ }
+
+ //
+ // Get the primary domain name from the configuration file
+ //
+ if ((status = NetpGetDomainName(&DomainNameT)) != NERR_Success) {
+ goto CloseConfigFile;
+ }
+ NetpAssert( DomainNameT != NULL );
+
+ if ((status = I_NetNameCanonicalize(
+ NULL,
+ DomainNameT,
+ (LPWSTR) WsInfo.WsPrimaryDomainName,
+ (DNLEN + 1) * sizeof(TCHAR),
+ WsInAWorkgroup() ? NAMETYPE_WORKGROUP : NAMETYPE_DOMAIN,
+ 0
+ )) != NERR_Success) {
+
+ LPWSTR SubString[1];
+
+
+ NetpKdPrint((PREFIX_WKSTA FORMAT_LPTSTR
+ " is an invalid primary domain name.\n", DomainNameT));
+
+ SubString[0] = DomainNameT;
+
+ WsLogEvent(
+ APE_CS_InvalidDomain,
+ 1,
+ SubString,
+ NERR_Success
+ );
+
+ (void) NetApiBufferFree(DomainNameT);
+ goto CloseConfigFile;
+ }
+
+ //
+ // Free memory allocated by NetpGetDomainName.
+ //
+ (void) NetApiBufferFree(DomainNameT);
+
+ WsInfo.WsPrimaryDomainNameLength = STRLEN((LPWSTR) WsInfo.WsPrimaryDomainName);
+
+ //
+ // Read the redirector configuration fields
+ //
+ WsUpdateWkstaToMatchRegistry(WorkstationSection, TRUE);
+
+ //
+ // Initialize redirector configuration
+ //
+
+ Rrp->Type = ConfigInformation;
+ Rrp->Version = REQUEST_PACKET_VERSION;
+
+ STRCPY((LPWSTR) Rrp->Parameters.Start.RedirectorName,
+ (LPWSTR) WsInfo.WsComputerName);
+ Rrp->Parameters.Start.RedirectorNameLength =
+ WsInfo.WsComputerNameLength*sizeof(TCHAR);
+
+ Rrp->Parameters.Start.DomainNameLength = WsInfo.WsPrimaryDomainNameLength*sizeof(TCHAR);
+ STRCPY((LPWSTR) (Rrp->Parameters.Start.RedirectorName+WsInfo.WsComputerNameLength),
+ (LPWSTR) WsInfo.WsPrimaryDomainName
+ );
+
+ if ((status = WsRedirFsControl(
+ WsRedirDeviceHandle,
+ FSCTL_LMR_START,
+ Rrp,
+ sizeof(LMR_REQUEST_PACKET) +
+ Rrp->Parameters.Start.RedirectorNameLength+
+ Rrp->Parameters.Start.DomainNameLength,
+ (LPBYTE) &WSBUF,
+ sizeof(WKSTA_INFO_502),
+ NULL
+ )) != NERR_Success) {
+
+ LPWSTR SubString[1];
+
+
+ NetpKdPrint((PREFIX_WKSTA "Start redirector failed %lu\n", status));
+
+ SubString[0] = L"redirector";
+
+ WsLogEvent(
+ NELOG_Service_Fail,
+ 1,
+ SubString,
+ status
+ );
+
+ goto CloseConfigFile;
+ }
+
+ //
+ // If we still have the default value for number of mailslot buffers,
+ // pick a "reasonable" value based on the amount of physical memory
+ // available in the system.
+ //
+
+ if (WSBUF.wki502_num_mailslot_buffers == MAXULONG) {
+ MEMORYSTATUS MemoryStatus;
+
+ GlobalMemoryStatus(&MemoryStatus);
+
+ //
+ // Lets take up 1/40th of 1% of physical memory for mailslot buffers
+ //
+
+ WSBUF.wki502_num_mailslot_buffers = MemoryStatus.dwTotalPhys / (100 * 40 * 512);
+
+
+ }
+
+
+ //
+ // Initialize datagram receiver configuration
+ //
+
+ Drrp->Version = LMDR_REQUEST_PACKET_VERSION;
+
+ Drrp->Parameters.Start.NumberOfMailslotBuffers =
+ WSBUF.wki502_num_mailslot_buffers;
+ Drrp->Parameters.Start.NumberOfServerAnnounceBuffers =
+ WSBUF.wki502_num_srv_announce_buffers;
+ Drrp->Parameters.Start.IllegalDatagramThreshold =
+ WSBUF.wki502_max_illegal_datagram_events;
+ Drrp->Parameters.Start.EventLogResetFrequency =
+ WSBUF.wki502_illegal_datagram_event_reset_frequency;
+ Drrp->Parameters.Start.LogElectionPackets =
+ WSBUF.wki502_log_election_packets;
+
+ if ((status = WsDgReceiverIoControl(
+ WsDgReceiverDeviceHandle,
+ IOCTL_LMDR_START,
+ Drrp,
+ sizeof(LMDR_REQUEST_PACKET),
+ NULL,
+ 0,
+ NULL
+ )) != NERR_Success) {
+
+ LPWSTR SubString[1];
+
+
+ NetpKdPrint((PREFIX_WKSTA "Start datagram receiver failed %lu\n", status));
+
+ SubString[0] = L"datagram receiver";
+
+ WsLogEvent(
+ NELOG_Service_Fail,
+ 1,
+ SubString,
+ status
+ );
+
+ goto CloseConfigFile;
+ }
+
+ status = NERR_Success;
+
+CloseConfigFile:
+ (void) NetpCloseConfigData(WorkstationSection);
+ RtlReleaseResource(&WsInfo.ConfigResource);
+
+ return status;
+}
+
+
+
+VOID
+WsUpdateWkstaToMatchRegistry(
+ IN LPNET_CONFIG_HANDLE WorkstationSection,
+ IN BOOL IsWkstaInit
+ )
+/*++
+
+Routine Description:
+
+ This function reads each redirector configuration field into the
+ WsInfo.WsConfigBuf (WSBUF) buffer so that the values are ready to be
+ set in the redirector. If a field is not found or is invalid, the
+ default value is set.
+
+Arguments:
+
+ WorkstationSection - Supplies a handle to read the Workstation
+ configuration parameters.
+
+ IsWkstaInit - Supplies a flag which if TRUE indicates that this
+ routine is called at workstation init time so the non-settable
+ fields are acceptable and set in WSBUF. If FALSE, the
+ non-settable fields are ignored.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ DWORD i;
+ NET_API_STATUS status;
+ DWORD TempDwordValue;
+
+//
+// BUGBUG: Invalid keyword value. How to report error?
+//
+#define REPORT_KEYWORD_IGNORED( lptstrKeyword ) \
+ { \
+ NetpKdPrint(( \
+ PREFIX_WKSTA "*ERROR* Tried to set keyword '" FORMAT_LPTSTR \
+ "' with invalid value.\n" \
+ "This error is ignored.\n", \
+ lptstrKeyword )); \
+ }
+
+ for (i = 0; WsInfo.WsConfigFields[i].Keyword != NULL; i++) {
+
+ //
+ // This is to handle fields that are not settable via
+ // NetWkstaSetInfo. However, these non-settable fields,
+ // designated by Parmnum == PARMNUM_ALL, can be assigned when
+ // the workstation is starting up.
+ //
+ if ((WsInfo.WsConfigFields[i].Parmnum != PARMNUM_ALL) ||
+ IsWkstaInit) {
+
+ //
+ // Depending on data type, get the appropriate kind of value.
+ //
+
+ switch (WsInfo.WsConfigFields[i].DataType) {
+
+ case BooleanType:
+
+ status = NetpGetConfigBool(
+ WorkstationSection,
+ WsInfo.WsConfigFields[i].Keyword,
+ WsInfo.WsConfigFields[i].Default,
+ (LPBOOL) (WsInfo.WsConfigFields[i].FieldPtr)
+ );
+
+ if ((status != NO_ERROR) && (status != NERR_CfgParamNotFound)) {
+
+ REPORT_KEYWORD_IGNORED( WsInfo.WsConfigFields[i].Keyword );
+
+ }
+ break;
+
+
+ case DWordType:
+
+ status = NetpGetConfigDword(
+ WorkstationSection,
+ WsInfo.WsConfigFields[i].Keyword,
+ WsInfo.WsConfigFields[i].Default,
+ &TempDwordValue
+ );
+
+ if ((status == NO_ERROR) || (status == NERR_CfgParamNotFound)) {
+
+ //
+ // Make sure keyword is in range.
+ //
+ if (TempDwordValue < WsInfo.WsConfigFields[i].Minimum ||
+ TempDwordValue > WsInfo.WsConfigFields[i].Maximum) {
+
+ //
+ // BUGBUG: Better way to report error?
+ //
+ NetpKdPrint((
+ PREFIX_WKSTA FORMAT_LPTSTR
+ " value out of range %lu (%lu-%lu)\n",
+ WsInfo.WsConfigFields[i].Keyword,
+ TempDwordValue,
+ WsInfo.WsConfigFields[i].Minimum,
+ WsInfo.WsConfigFields[i].Maximum
+ ));
+ //
+ // Set back to default.
+ //
+ *(WsInfo.WsConfigFields[i].FieldPtr)
+ = WsInfo.WsConfigFields[i].Default;
+
+ }
+ else {
+
+ *(WsInfo.WsConfigFields[i].FieldPtr) = TempDwordValue;
+
+ }
+ }
+ else {
+
+ REPORT_KEYWORD_IGNORED( WsInfo.WsConfigFields[i].Keyword );
+
+ }
+ break;
+
+ default:
+ NetpAssert(FALSE);
+
+ } // switch
+ }
+ }
+}
+
+
+
+NET_API_STATUS
+WsBindToTransports(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This function binds the transports specified in the registry to the
+ redirector. The order of priority for the transports follows the order
+ they are listed by the "Bind=" valuename.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NET_API_STATUS status;
+ NET_API_STATUS tempStatus;
+ NTSTATUS ntstatus;
+ DWORD transportsBound;
+ PRTL_QUERY_REGISTRY_TABLE queryTable;
+ LIST_ENTRY header;
+ PLIST_ENTRY pListEntry;
+ PWS_BIND pBind;
+
+
+ //
+ // Ask the RTL to call us back for each subvalue in the MULTI_SZ
+ // value \LanmanWorkstation\Linkage\Bind.
+ //
+ queryTable = (PVOID)LocalAlloc(
+ 0,
+ sizeof(RTL_QUERY_REGISTRY_TABLE) * 2
+ );
+
+ if (queryTable == NULL) {
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ InitializeListHead( &header);
+
+ queryTable[0].QueryRoutine = WsBindATransport;
+ queryTable[0].Flags = 0;
+ queryTable[0].Name = WS_BIND_VALUE_NAME;
+ queryTable[0].EntryContext = NULL;
+ queryTable[0].DefaultType = REG_NONE;
+ queryTable[0].DefaultData = NULL;
+ queryTable[0].DefaultLength = 0;
+
+ queryTable[1].QueryRoutine = NULL;
+ queryTable[1].Flags = 0;
+ queryTable[1].Name = NULL;
+
+ ntstatus = RtlQueryRegistryValues(
+ RTL_REGISTRY_SERVICES, // path relative to ...
+ WS_LINKAGE_REGISTRY_PATH,
+ queryTable,
+ (PVOID) &header, // context
+ NULL
+ );
+
+ if ( !NT_SUCCESS( ntstatus)) {
+ NetpKdPrint((
+ PREFIX_WKSTA "WsBindToTransports: RtlQueryRegistryValues Failed:"
+ FORMAT_NTSTATUS "\n",
+ ntstatus
+ ));
+ status = NetpNtStatusToApiStatus( ntstatus);
+ } else {
+ status = NO_ERROR;
+ }
+
+
+ //
+ // First process all the data, then clean up.
+ //
+
+ for ( pListEntry = header.Flink;
+ pListEntry != &header;
+ pListEntry = pListEntry->Flink) {
+
+ pBind = CONTAINING_RECORD(
+ pListEntry,
+ WS_BIND,
+ ListEntry
+ );
+
+ tempStatus = NO_ERROR;
+
+ if ( pBind->Redir->EventHandle != INVALID_HANDLE_VALUE) {
+ WaitForSingleObject(
+ pBind->Redir->EventHandle,
+ INFINITE
+ );
+ tempStatus = WsMapStatus( pBind->Redir->IoStatusBlock.Status);
+ pBind->Redir->Bound = (tempStatus == NO_ERROR);
+ if (tempStatus == ERROR_DUP_NAME) {
+ status = tempStatus;
+ }
+ }
+
+ if ( pBind->Dgrec->EventHandle != INVALID_HANDLE_VALUE) {
+ WaitForSingleObject(
+ pBind->Dgrec->EventHandle,
+ INFINITE
+ );
+ tempStatus = WsMapStatus( pBind->Dgrec->IoStatusBlock.Status);
+ pBind->Dgrec->Bound = (tempStatus == NO_ERROR);
+ if (tempStatus == ERROR_DUP_NAME) {
+ status = tempStatus;
+ }
+ }
+
+ if ( tempStatus == ERROR_DUP_NAME) {
+ NetpKdPrint((
+ PREFIX_WKSTA "Computername " FORMAT_LPTSTR
+ " already exists on network " FORMAT_LPTSTR "\n",
+ WsInfo.WsComputerName,
+ pBind->TransportName
+ ));
+ }
+
+ //
+ // If one is installed but the other is not, clean up the other.
+ //
+
+ if ( pBind->Dgrec->Bound != pBind->Redir->Bound) {
+ WsUnbindTransport2( pBind);
+ }
+ }
+
+
+ if ( status != NO_ERROR) {
+
+ if (status == ERROR_DUP_NAME) {
+ WsLogEvent(
+ NERR_DupNameReboot,
+ 0,
+ NULL,
+ NERR_Success
+ );
+ }
+
+ for ( pListEntry = header.Flink;
+ pListEntry != &header;
+ pListEntry = pListEntry->Flink) {
+
+ pBind = CONTAINING_RECORD(
+ pListEntry,
+ WS_BIND,
+ ListEntry
+ );
+
+ WsUnbindTransport2( pBind);
+ }
+ }
+
+ for ( transportsBound = 0;
+ IsListEmpty( &header) == FALSE;
+ LocalFree((HLOCAL) pBind)) {
+
+ pListEntry = RemoveHeadList( &header);
+
+ pBind = CONTAINING_RECORD(
+ pListEntry,
+ WS_BIND,
+ ListEntry
+ );
+
+ if ( pBind->Redir->EventHandle != INVALID_HANDLE_VALUE) {
+ CloseHandle( pBind->Redir->EventHandle);
+ }
+
+ if ( pBind->Redir->Bound == TRUE) {
+ transportsBound++;
+ }
+
+ if ( pBind->Dgrec->EventHandle != INVALID_HANDLE_VALUE) {
+ CloseHandle( pBind->Dgrec->EventHandle);
+ }
+
+ }
+
+ (void) LocalFree((HLOCAL) queryTable);
+
+ if ( WsRedirAsyncDeviceHandle != NULL) {
+ (VOID)NtClose( WsRedirAsyncDeviceHandle);
+ WsRedirAsyncDeviceHandle = NULL;
+ }
+
+ if ( WsDgrecAsyncDeviceHandle != NULL) {
+ (VOID)NtClose( WsDgrecAsyncDeviceHandle);
+ WsDgrecAsyncDeviceHandle = NULL;
+ }
+
+ if (transportsBound == 0) {
+
+ NetpKdPrint((
+ PREFIX_WKSTA "WsBindToTransports: Failed to bind to any"
+ " transports" FORMAT_API_STATUS "\n",
+ status
+ ));
+
+ if ( status != ERROR_DUP_NAME) {
+ WsLogEvent(
+ NELOG_NoTranportLoaded,
+ 0,
+ NULL,
+ NERR_Success
+ );
+
+ status = NELOG_NoTranportLoaded;
+ }
+ }
+
+ return status;
+}
+
+
+
+STATIC
+NTSTATUS
+WsBindATransport(
+ IN PWSTR ValueName,
+ IN ULONG ValueType,
+ IN PVOID ValueData,
+ IN ULONG ValueLength,
+ IN PVOID Context,
+ IN PVOID EntryContext
+ )
+/*++
+ This routine always returns SUCCESS because we want all transports
+ to be processed fully.
+--*/
+{
+ NET_API_STATUS status;
+
+ DBG_UNREFERENCED_PARAMETER( ValueName);
+ DBG_UNREFERENCED_PARAMETER( ValueLength);
+ DBG_UNREFERENCED_PARAMETER( EntryContext);
+
+
+ //
+ // The value type must be REG_SZ (translated from REG_MULTI_SZ by the RTL).
+ //
+ if (ValueType != REG_SZ) {
+ NetpKdPrint((
+ PREFIX_WKSTA "WsBindATransport: ignored invalid value "
+ FORMAT_LPWSTR "\n",
+ ValueName
+ ));
+ return STATUS_SUCCESS;
+ }
+
+ //
+ // Bind transport
+ //
+
+ status = WsAsyncBindTransport(
+ ValueData, // name of transport device object
+ --QualityOfService,
+ (PLIST_ENTRY)Context
+ );
+
+ if ( status != NERR_Success) {
+ NetpKdPrint((
+ PREFIX_WKSTA "WsAsyncBindTransport " FORMAT_LPTSTR
+ " returns " FORMAT_API_STATUS "\n",
+ ValueData,
+ status
+ ));
+ }
+
+ return STATUS_SUCCESS;
+}
+
+
+NET_API_STATUS
+WsAddDomains(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This function tells the datagram receiver the names to listen on for
+ datagrams. The names include the computer name, the primary domain,
+ name and the other domains. The logon domain is not added here; it is
+ made known to the datagram receiver whenever a user logs on.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+Warning:
+
+ This routine is UNICODE only.
+
+--*/
+{
+ NET_API_STATUS status;
+ LPNET_CONFIG_HANDLE SectionHandle = NULL;
+ LPTSTR OtherDomainName = NULL;
+ LPTSTR_ARRAY ArrayStart = NULL;
+ NT_PRODUCT_TYPE NtProductType;
+
+ BYTE Buffer[sizeof(LMDR_REQUEST_PACKET) +
+ (max(MAX_PATH, DNLEN) + 1) * sizeof(WCHAR)];
+ PLMDR_REQUEST_PACKET Drrp = (PLMDR_REQUEST_PACKET) Buffer;
+
+ //
+ // First tell the datagram receiver about the computer name.
+ //
+
+ Drrp->Version = LMDR_REQUEST_PACKET_VERSION;
+ Drrp->Parameters.AddDelName.Type = ComputerName;
+ Drrp->Parameters.AddDelName.DgReceiverNameLength =
+ WsInfo.WsComputerNameLength * sizeof(WCHAR);
+
+ (VOID)STRCPY((LPWSTR) Drrp->Parameters.AddDelName.Name,
+ (LPWSTR) WsInfo.WsComputerName);
+
+ if ((status = WsDgReceiverIoControl(
+ WsDgReceiverDeviceHandle,
+ IOCTL_LMDR_ADD_NAME,
+ Drrp,
+ sizeof(LMDR_REQUEST_PACKET) +
+ Drrp->Parameters.AddDelName.DgReceiverNameLength,
+ NULL,
+ 0,
+ NULL
+ )) != NERR_Success) {
+
+ LPWSTR SubString[1];
+
+ NetpKdPrint((PREFIX_WKSTA "Add Workstation name failed %lu\n", status));
+ SubString[0] = (LPWSTR) WsInfo.WsComputerName;
+ WsLogEvent(
+ APE_BAD_COMPNAME,
+ 1,
+ SubString,
+ status
+ );
+ goto DomainsCleanup;
+ }
+
+ //
+ // Service install still pending. Update checkpoint counter and the
+ // status with the Service Controller.
+ //
+ WsGlobalData.Status.dwCheckPoint++;
+ WsUpdateStatus();
+
+ //
+ // Next tell the datagram receiver about the primary domain.
+ //
+ Drrp->Version = LMDR_REQUEST_PACKET_VERSION;
+
+ Drrp->Parameters.AddDelName.Type = PrimaryDomain;
+
+ Drrp->Parameters.AddDelName.DgReceiverNameLength =
+ WsInfo.WsPrimaryDomainNameLength * sizeof(WCHAR);
+
+ (VOID)STRCPY((LPWSTR) Drrp->Parameters.AddDelName.Name,
+ (LPWSTR) WsInfo.WsPrimaryDomainName);
+
+ if ((status = WsDgReceiverIoControl(
+ WsDgReceiverDeviceHandle,
+ IOCTL_LMDR_ADD_NAME,
+ Drrp,
+ sizeof(LMDR_REQUEST_PACKET) +
+ Drrp->Parameters.AddDelName.DgReceiverNameLength,
+ NULL,
+ 0,
+ NULL
+ )) != NERR_Success) {
+
+ LPWSTR SubString[1];
+
+
+ if (status == ERROR_DUP_NAME) {
+ status = ERROR_DUP_DOMAINNAME;
+ }
+
+ NetpKdPrint((
+ PREFIX_WKSTA "Add Primary domain name" FORMAT_LPTSTR
+ " failed with error code %lu\n",
+ WsInfo.WsPrimaryDomainName,
+ status
+ ));
+ SubString[0] = (LPWSTR) WsInfo.WsPrimaryDomainName;
+ WsLogEvent(
+ APE_CS_InvalidDomain,
+ 1,
+ SubString,
+ status
+ );
+ goto DomainsCleanup;
+ }
+
+ //
+ // Service install still pending. Update checkpoint counter and the
+ // status with the Service Controller.
+ //
+ WsGlobalData.Status.dwCheckPoint++;
+ WsUpdateStatus();
+
+ //
+ // On Lan Manager/NT machines, we want to add the special "domain"
+ // signature name as well.
+ //
+
+
+ RtlGetNtProductType(&NtProductType);
+
+ if (NtProductType == NtProductLanManNt) {
+
+ //
+ // Next tell the datagram receiver about the LM/NT primary domain.
+ //
+
+ Drrp->Version = LMDR_REQUEST_PACKET_VERSION;
+
+ Drrp->Parameters.AddDelName.Type = DomainName;
+
+ Drrp->Parameters.AddDelName.DgReceiverNameLength =
+ WsInfo.WsPrimaryDomainNameLength * sizeof(WCHAR);
+
+ (VOID)STRCPY((LPWSTR) Drrp->Parameters.AddDelName.Name,
+ (LPWSTR) WsInfo.WsPrimaryDomainName);
+
+ if ((status = WsDgReceiverIoControl(
+ WsDgReceiverDeviceHandle,
+ IOCTL_LMDR_ADD_NAME,
+ Drrp,
+ sizeof(LMDR_REQUEST_PACKET) +
+ Drrp->Parameters.AddDelName.DgReceiverNameLength,
+ NULL,
+ 0,
+ NULL
+ )) != NERR_Success) {
+
+ LPWSTR SubString[1];
+
+ NetpKdPrint((
+ PREFIX_WKSTA "Add Primary domain name" FORMAT_LPTSTR
+ " failed with error code %lu\n",
+ WsInfo.WsPrimaryDomainName,
+ status
+ ));
+ SubString[0] = (LPWSTR) WsInfo.WsPrimaryDomainName;
+ WsLogEvent(
+ APE_CS_InvalidDomain,
+ 1,
+ SubString,
+ status
+ );
+ goto DomainsCleanup;
+ }
+
+ //
+ // Service install still pending. Update checkpoint counter and the
+ // status with the Service Controller.
+ //
+ WsGlobalData.Status.dwCheckPoint++;
+ WsUpdateStatus();
+ }
+
+
+ //
+ // Now loop through and add all the other domains.
+ //
+
+ //
+ // Open registry section listing the other domains. Note that this
+ // is workstation servive parameter, NOT the browser service parameter.
+ //
+ if ((status = NetpOpenConfigData(
+ &SectionHandle,
+ NULL, // no server name
+ SECT_NT_WKSTA,
+ TRUE // read-only
+ )) != NERR_Success) {
+
+ //
+ // Ignore the error if the config section couldn't be found.
+ //
+ status = NERR_Success;
+ goto DomainsCleanup;
+ }
+
+ //
+ // Get value for OtherDomains keyword in the wksta section.
+ // This is a "NULL-NULL" array (which corresponds to REG_MULTI_SZ).
+ //
+ status = NetpGetConfigTStrArray(
+ SectionHandle,
+ WKSTA_KEYWORD_OTHERDOMAINS,
+ &ArrayStart // Must be freed by NetApiBufferFree().
+ );
+
+ if (status != NERR_Success) {
+ status = NERR_Success; // other domain is optional
+ goto DomainsCleanup;
+ }
+
+ NetpAssert(ArrayStart != NULL);
+ if (NetpIsTStrArrayEmpty(ArrayStart)) {
+ goto DomainsCleanup;
+ }
+
+ OtherDomainName = ArrayStart;
+ while (! NetpIsTStrArrayEmpty(OtherDomainName)) {
+
+ if ((status = I_NetNameCanonicalize(
+ NULL,
+ OtherDomainName,
+ (LPWSTR) Drrp->Parameters.AddDelName.Name,
+ (DNLEN + 1) * sizeof(TCHAR),
+ NAMETYPE_DOMAIN,
+ 0
+ )) != NERR_Success) {
+
+ LPWSTR SubString[1];
+
+
+ NetpKdPrint((PREFIX_WKSTA FORMAT_LPTSTR
+ " is an invalid other domain name.\n", OtherDomainName));
+
+ SubString[0] = OtherDomainName;
+ WsLogEvent(
+ APE_CS_InvalidDomain,
+ 1,
+ SubString,
+ NERR_Success
+ );
+
+ status = NERR_Success; // loading other domains is optional
+ goto NextOtherDomain;
+ }
+
+ //
+ // Tell the datagram receiver about an other domain name
+ //
+ Drrp->Version = LMDR_REQUEST_PACKET_VERSION;
+ Drrp->Parameters.AddDelName.Type = OtherDomain;
+ Drrp->Parameters.AddDelName.DgReceiverNameLength =
+ STRLEN(OtherDomainName) * sizeof(TCHAR);
+
+ status = WsDgReceiverIoControl(
+ WsDgReceiverDeviceHandle,
+ IOCTL_LMDR_ADD_NAME,
+ Drrp,
+ sizeof(LMDR_REQUEST_PACKET) +
+ Drrp->Parameters.AddDelName.DgReceiverNameLength,
+ NULL,
+ 0,
+ NULL
+ );
+
+ //
+ // Service install still pending. Update checkpoint counter and the
+ // status with the Service Controller.
+ //
+ WsGlobalData.Status.dwCheckPoint++;
+ WsUpdateStatus();
+
+ if (status != NERR_Success) {
+
+ LPWSTR SubString[1];
+
+
+ NetpKdPrint((
+ PREFIX_WKSTA "Add Other domain name " FORMAT_LPTSTR
+ " failed with error code %lu\n",
+ OtherDomainName,
+ status
+ ));
+
+ SubString[0] = OtherDomainName;
+ WsLogEvent(
+ APE_CS_InvalidDomain,
+ 1,
+ SubString,
+ status
+ );
+ status = NERR_Success; // loading other domains is optional
+ }
+
+NextOtherDomain:
+ OtherDomainName = NetpNextTStrArrayEntry(OtherDomainName);
+ }
+
+DomainsCleanup:
+ //
+ // Done with reading from config file. Close file, free memory, etc.
+ //
+ if (ArrayStart != NULL) {
+ (VOID) NetApiBufferFree(ArrayStart);
+ }
+ if (SectionHandle != NULL) {
+ (VOID) NetpCloseConfigData(SectionHandle);
+ }
+
+ return status;
+}
+
+
+VOID
+WsLogEvent(
+ DWORD MessageId,
+ DWORD NumberOfSubStrings,
+ LPWSTR *SubStrings,
+ DWORD ErrorCode
+ )
+{
+
+ HANDLE LogHandle;
+
+ PSID UserSid = NULL;
+
+
+ LogHandle = RegisterEventSourceW (
+ NULL,
+ WORKSTATION_DISPLAY_NAME
+ );
+
+ if (LogHandle == NULL) {
+ NetpKdPrint((PREFIX_WKSTA "RegisterEventSourceW failed %lu\n",
+ GetLastError()));
+ return;
+ }
+
+ if (ErrorCode == NERR_Success) {
+
+ //
+ // No error codes were specified
+ //
+ (void) ReportEventW(
+ LogHandle,
+ EVENTLOG_ERROR_TYPE,
+ 0, // event category
+ MessageId,
+ UserSid,
+ (WORD)NumberOfSubStrings,
+ 0,
+ SubStrings,
+ (PVOID) NULL
+ );
+
+ }
+ else {
+
+ //
+ // Log the error code specified
+ //
+ (void) ReportEventW(
+ LogHandle,
+ EVENTLOG_ERROR_TYPE,
+ 0, // event category
+ MessageId,
+ UserSid,
+ (WORD)NumberOfSubStrings,
+ sizeof(DWORD),
+ SubStrings,
+ (PVOID) &ErrorCode
+ );
+ }
+
+ DeregisterEventSource(LogHandle);
+}
diff --git a/private/net/svcdlls/wkssvc/server/wsconfig.h b/private/net/svcdlls/wkssvc/server/wsconfig.h
new file mode 100644
index 000000000..47f266ce4
--- /dev/null
+++ b/private/net/svcdlls/wkssvc/server/wsconfig.h
@@ -0,0 +1,99 @@
+/*++
+
+Copyright (c) 1991-92 Microsoft Corporation
+
+Module Name:
+
+ wsconfig.h
+
+Abstract:
+
+ Private header file to be included by Workstation service modules that
+ need to load Workstation configuration information.
+
+Author:
+
+ Rita Wong (ritaw) 22-May-1991
+
+Revision History:
+
+--*/
+
+
+#ifndef _WSCONFIG_INCLUDED_
+#define _WSCONFIG_INCLUDED_
+
+#include <config.h> // LPNET_CONFIG_HANDLE.
+
+typedef enum _DATATYPE {
+ BooleanType,
+ DWordType
+} DATATYPE, *PDATATYPE;
+
+typedef struct _WS_REDIR_FIELDS {
+ LPTSTR Keyword;
+ LPDWORD FieldPtr;
+ DWORD Default;
+ DWORD Minimum;
+ DWORD Maximum;
+ DATATYPE DataType;
+ DWORD Parmnum;
+} WS_REDIR_FIELDS, *PWS_REDIR_FIELDS;
+
+//
+// Configuration information. Reading and writing to this global
+// structure requires that the resource be acquired first.
+//
+typedef struct _WSCONFIGURATION_INFO {
+
+ RTL_RESOURCE ConfigResource; // To serialize access to config
+ // fields.
+
+ TCHAR WsComputerName[MAX_PATH + 1];
+ DWORD WsComputerNameLength;
+ TCHAR WsPrimaryDomainName[DNLEN + 1];
+ DWORD WsPrimaryDomainNameLength;
+ DWORD RedirectorPlatform;
+ DWORD MajorVersion;
+ DWORD MinorVersion;
+ WKSTA_INFO_502 WsConfigBuf;
+ PWS_REDIR_FIELDS WsConfigFields;
+
+} WSCONFIGURATION_INFO, *PWSCONFIGURATION_INFO;
+
+extern WSCONFIGURATION_INFO WsInfo;
+
+#define WSBUF WsInfo.WsConfigBuf
+
+extern BOOLEAN WsBrowserPresent;
+
+NET_API_STATUS
+WsGetWorkstationConfiguration(
+ VOID
+ );
+
+NET_API_STATUS
+WsBindToTransports(
+ VOID
+ );
+
+NET_API_STATUS
+WsAddDomains(
+ VOID
+ );
+
+VOID
+WsUpdateWkstaToMatchRegistry(
+ IN LPNET_CONFIG_HANDLE WorkstationSection,
+ IN BOOL IsWkstaInit
+ );
+
+VOID
+WsLogEvent(
+ DWORD MessageId,
+ DWORD NumberOfSubStrings,
+ LPWSTR *SubStrings,
+ DWORD ErrorCode
+ );
+
+#endif
diff --git a/private/net/svcdlls/wkssvc/server/wsdevice.c b/private/net/svcdlls/wkssvc/server/wsdevice.c
new file mode 100644
index 000000000..0e3dd7fba
--- /dev/null
+++ b/private/net/svcdlls/wkssvc/server/wsdevice.c
@@ -0,0 +1,1822 @@
+/*++
+
+Copyright (c) 1991-1992 Microsoft Corporation
+
+Module Name:
+
+ wsdevice.c
+
+Abstract:
+
+ This module contains the support routines for the APIs that call
+ into the redirector or the datagram receiver.
+
+Author:
+
+ Rita Wong (ritaw) 20-Feb-1991
+
+Revision History:
+
+--*/
+
+#include "wsutil.h"
+#include "wsconfig.h"
+#include "wsdevice.h"
+#include "wsbind.h"
+#include <lmerrlog.h> // Eventlog message IDs
+#include "winreg.h" // registry API's
+#include <prefix.h> // PREFIX_ equates.
+
+
+//
+// Buffer allocation size for enumeration output buffer.
+//
+#define INITIAL_ALLOCATION_SIZE 4096 // First attempt size
+#define FUDGE_FACTOR_SIZE 1024 // Second try TotalBytesNeeded
+ // plus this amount
+
+//-------------------------------------------------------------------//
+// //
+// Local Function Prototypes //
+// //
+//-------------------------------------------------------------------//
+
+STATIC
+NET_API_STATUS
+WsOpenRedirector(
+ VOID
+ );
+
+STATIC
+NET_API_STATUS
+WsOpenDgReceiver (
+ VOID
+ );
+
+//-------------------------------------------------------------------//
+// //
+// Global variables //
+// //
+//-------------------------------------------------------------------//
+
+//
+// Handle to the Redirector FSD
+//
+HANDLE WsRedirDeviceHandle = NULL;
+HANDLE WsRedirAsyncDeviceHandle = NULL;
+
+BOOLEAN LoadedMRxSmbInsteadOfRdr = FALSE;
+
+//
+// Handle to the Datagram Receiver DD
+//
+HANDLE WsDgReceiverDeviceHandle = NULL;
+HANDLE WsDgrecAsyncDeviceHandle = NULL;
+
+
+
+NET_API_STATUS
+WsDeviceControlGetInfo(
+ IN DDTYPE DeviceDriverType,
+ IN HANDLE FileHandle,
+ IN ULONG DeviceControlCode,
+ IN PVOID RequestPacket,
+ IN ULONG RequestPacketLength,
+ OUT LPBYTE *OutputBuffer,
+ IN ULONG PreferedMaximumLength,
+ IN ULONG BufferHintSize,
+ OUT PULONG Information OPTIONAL
+ )
+/*++
+
+Routine Description:
+
+ This function allocates the buffer and fill it with the information
+ that is retrieved from the redirector or datagram receiver.
+
+Arguments:
+
+ DeviceDriverType - Supplies the value which indicates whether to call
+ the redirector or the datagram receiver.
+
+ FileHandle - Supplies a handle to the file or device of which to get
+ information about.
+
+ DeviceControlCode - Supplies the NtFsControlFile or NtIoDeviceControlFile
+ function control code.
+
+ RequestPacket - Supplies a pointer to the device request packet.
+
+ RrequestPacketLength - Supplies the length of the device request packet.
+
+ OutputBuffer - Returns a pointer to the buffer allocated by this routine
+ which contains the use information requested. This pointer is set to
+ NULL if return code is not NERR_Success.
+
+ PreferedMaximumLength - Supplies the number of bytes of information to
+ return in the buffer. If this value is MAXULONG, we will try to
+ return all available information if there is enough memory resource.
+
+ BufferHintSize - Supplies the hint size of the output buffer so that the
+ memory allocated for the initial buffer will most likely be large
+ enough to hold all requested data.
+
+ Information - Returns the information code from the NtFsControlFile or
+ NtIoDeviceControlFile call.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NET_API_STATUS status;
+ DWORD OutputBufferLength;
+ DWORD TotalBytesNeeded = 1;
+ ULONG OriginalResumeKey;
+
+
+ //
+ // If PreferedMaximumLength is MAXULONG, then we are supposed to get all
+ // the information, regardless of size. Allocate the output buffer of a
+ // reasonable size and try to use it. If this fails, the Redirector FSD
+ // will say how much we need to allocate.
+ //
+ if (PreferedMaximumLength == MAXULONG) {
+ OutputBufferLength = (BufferHintSize) ?
+ BufferHintSize :
+ INITIAL_ALLOCATION_SIZE;
+ }
+ else {
+ OutputBufferLength = PreferedMaximumLength;
+ }
+
+ OutputBufferLength = ROUND_UP_COUNT(OutputBufferLength, ALIGN_WCHAR);
+
+ if ((*OutputBuffer = MIDL_user_allocate(OutputBufferLength)) == NULL) {
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+ RtlZeroMemory((PVOID) *OutputBuffer, OutputBufferLength);
+
+ if (DeviceDriverType == Redirector) {
+ PLMR_REQUEST_PACKET Rrp = (PLMR_REQUEST_PACKET) RequestPacket;
+
+ OriginalResumeKey = Rrp->Parameters.Get.ResumeHandle;
+
+ //
+ // Make the request of the Redirector
+ //
+
+ status = WsRedirFsControl(
+ FileHandle,
+ DeviceControlCode,
+ Rrp,
+ RequestPacketLength,
+ *OutputBuffer,
+ OutputBufferLength,
+ Information
+ );
+
+ if (status == ERROR_MORE_DATA) {
+ TotalBytesNeeded = Rrp->Parameters.Get.TotalBytesNeeded;
+
+ }
+
+ }
+ else {
+ PLMDR_REQUEST_PACKET Drrp = (PLMDR_REQUEST_PACKET) RequestPacket;
+
+ OriginalResumeKey = Drrp->Parameters.EnumerateNames.ResumeHandle;
+
+ //
+ // Make the request of the Datagram Receiver
+ //
+ status = WsDgReceiverIoControl(
+ FileHandle,
+ DeviceControlCode,
+ Drrp,
+ RequestPacketLength,
+ *OutputBuffer,
+ OutputBufferLength,
+ NULL
+ );
+
+ if (status == ERROR_MORE_DATA) {
+
+ NetpAssert(
+ FIELD_OFFSET(
+ LMDR_REQUEST_PACKET,
+ Parameters.EnumerateNames.TotalBytesNeeded
+ ) ==
+ FIELD_OFFSET(
+ LMDR_REQUEST_PACKET,
+ Parameters.EnumerateServers.TotalBytesNeeded
+ )
+ );
+
+ TotalBytesNeeded = Drrp->Parameters.EnumerateNames.TotalBytesNeeded;
+ }
+ }
+
+ if ((TotalBytesNeeded > OutputBufferLength) &&
+ (PreferedMaximumLength == MAXULONG)) {
+
+ //
+ // Initial output buffer allocated was too small and we need to return
+ // all data. First free the output buffer before allocating the
+ // required size plus a fudge factor just in case the amount of data
+ // grew.
+ //
+
+ MIDL_user_free(*OutputBuffer);
+
+ OutputBufferLength =
+ ROUND_UP_COUNT((TotalBytesNeeded + FUDGE_FACTOR_SIZE),
+ ALIGN_WCHAR);
+
+ if ((*OutputBuffer = MIDL_user_allocate(OutputBufferLength)) == NULL) {
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+ RtlZeroMemory((PVOID) *OutputBuffer, OutputBufferLength);
+
+ //
+ // Try again to get the information from the redirector or datagram
+ // receiver
+ //
+ if (DeviceDriverType == Redirector) {
+ PLMR_REQUEST_PACKET Rrp = (PLMR_REQUEST_PACKET) RequestPacket;
+
+ Rrp->Parameters.Get.ResumeHandle = OriginalResumeKey;
+
+ //
+ // Make the request of the Redirector
+ //
+ status = WsRedirFsControl(
+ FileHandle,
+ DeviceControlCode,
+ Rrp,
+ RequestPacketLength,
+ *OutputBuffer,
+ OutputBufferLength,
+ Information
+ );
+ }
+ else {
+ PLMDR_REQUEST_PACKET Drrp = (PLMDR_REQUEST_PACKET) RequestPacket;
+
+ NetpAssert(
+ FIELD_OFFSET(
+ LMDR_REQUEST_PACKET,
+ Parameters.EnumerateNames.ResumeHandle
+ ) ==
+ FIELD_OFFSET(
+ LMDR_REQUEST_PACKET,
+ Parameters.EnumerateServers.ResumeHandle
+ )
+ );
+
+ Drrp->Parameters.EnumerateNames.ResumeHandle = OriginalResumeKey;
+
+ //
+ // Make the request of the Datagram Receiver
+ //
+
+ status = WsDgReceiverIoControl(
+ FileHandle,
+ DeviceControlCode,
+ Drrp,
+ RequestPacketLength,
+ *OutputBuffer,
+ OutputBufferLength,
+ NULL
+ );
+ }
+ }
+
+
+ //
+ // If not successful in getting any data, or if the caller asked for
+ // all available data with PreferedMaximumLength == MAXULONG and
+ // our buffer overflowed, free the output buffer and set its pointer
+ // to NULL.
+ //
+ if ((status != NERR_Success && status != ERROR_MORE_DATA) ||
+ (TotalBytesNeeded == 0) ||
+ (PreferedMaximumLength == MAXULONG && status == ERROR_MORE_DATA)) {
+
+ MIDL_user_free(*OutputBuffer);
+ *OutputBuffer = NULL;
+
+ //
+ // PreferedMaximumLength == MAXULONG and buffer overflowed means
+ // we do not have enough memory to satisfy the request.
+ //
+ if (status == ERROR_MORE_DATA) {
+ status = ERROR_NOT_ENOUGH_MEMORY;
+ }
+ }
+
+ return status;
+}
+
+
+NET_API_STATUS
+WsInitializeRedirector(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This routine opens the NT LAN Man redirector. It then reads in
+ the configuration information and initializes to redirector.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NET_API_STATUS status;
+
+
+ status = WsLoadRedirector();
+
+
+ if (status != NERR_Success && status != ERROR_SERVICE_ALREADY_RUNNING) {
+ return status;
+ }
+
+ //
+ // Open redirector FSD to get handle to it
+ //
+
+ if ((status = WsOpenRedirector()) != NERR_Success) {
+ return status;
+ }
+
+
+// if ((status = WsLoadDriver(L"DGRcvr")) != NERR_Success) {
+// return status;
+// }
+
+ //
+ // Open datagram receiver FSD to get handle to it.
+ //
+ if ((status = WsOpenDgReceiver()) != NERR_Success) {
+ return status;
+ }
+
+ //
+ // Initialize the resource for serializing access to configuration
+ // information.
+ //
+ RtlInitializeResource(&WsInfo.ConfigResource);
+
+ //
+ // Load redirector and datagram receiver configuration
+ //
+ if ((status = WsGetWorkstationConfiguration()) != NERR_Success) {
+
+ (void) WsShutdownRedirector();
+ return status;
+ }
+
+ return NERR_Success;
+}
+
+
+STATIC
+NET_API_STATUS
+WsOpenRedirector(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This routine opens the NT LAN Man redirector FSD.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NTSTATUS ntstatus;
+ UNICODE_STRING DeviceName;
+ IO_STATUS_BLOCK IoStatusBlock;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+
+ //
+ // Open the redirector device.
+ //
+ RtlInitUnicodeString(&DeviceName,DD_NFS_DEVICE_NAME_U);
+
+ InitializeObjectAttributes(
+ &ObjectAttributes,
+ &DeviceName,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL
+ );
+
+ ntstatus = NtOpenFile(
+ &WsRedirDeviceHandle,
+ SYNCHRONIZE,
+ &ObjectAttributes,
+ &IoStatusBlock,
+ FILE_SHARE_VALID_FLAGS,
+ FILE_SYNCHRONOUS_IO_NONALERT
+ );
+
+ if (NT_SUCCESS(ntstatus)) {
+ ntstatus = IoStatusBlock.Status;
+ }
+
+ if (! NT_SUCCESS(ntstatus)) {
+ NetpKdPrint(("[Wksta] NtOpenFile redirector failed: 0x%08lx\n",
+ ntstatus));
+ WsRedirDeviceHandle = NULL;
+ return NetpNtStatusToApiStatus( ntstatus);
+ }
+
+ ntstatus = NtOpenFile(
+ &WsRedirAsyncDeviceHandle,
+ FILE_READ_DATA | FILE_WRITE_DATA,
+ &ObjectAttributes,
+ &IoStatusBlock,
+ FILE_SHARE_VALID_FLAGS,
+ 0L
+ );
+
+ if (NT_SUCCESS(ntstatus)) {
+ ntstatus = IoStatusBlock.Status;
+ }
+
+ if (! NT_SUCCESS(ntstatus)) {
+ NetpKdPrint(("[Wksta] NtOpenFile redirector ASYNC failed: 0x%08lx\n",
+ ntstatus));
+ WsRedirAsyncDeviceHandle = NULL;
+ }
+
+ return NetpNtStatusToApiStatus(ntstatus);
+}
+
+
+STATIC
+NET_API_STATUS
+WsOpenDgReceiver(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This routine opens the NT LAN Man Datagram Receiver driver.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NTSTATUS ntstatus;
+ UNICODE_STRING DeviceName;
+ IO_STATUS_BLOCK IoStatusBlock;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+
+ //
+ // Open the redirector device.
+ //
+
+ RtlInitUnicodeString( &DeviceName, DD_BROWSER_DEVICE_NAME_U);
+
+ InitializeObjectAttributes(
+ &ObjectAttributes,
+ &DeviceName,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL
+ );
+
+ ntstatus = NtOpenFile(
+ &WsDgReceiverDeviceHandle,
+ SYNCHRONIZE,
+ &ObjectAttributes,
+ &IoStatusBlock,
+ FILE_SHARE_VALID_FLAGS,
+ FILE_SYNCHRONOUS_IO_NONALERT
+ );
+
+ if (NT_SUCCESS(ntstatus)) {
+ ntstatus = IoStatusBlock.Status;
+ }
+
+ if (! NT_SUCCESS(ntstatus)) {
+ NetpKdPrint(("[Wksta] NtOpenFile datagram receiver failed: 0x%08lx\n",
+ ntstatus));
+
+ WsDgReceiverDeviceHandle = NULL;
+ return NetpNtStatusToApiStatus(ntstatus);
+ }
+
+ ntstatus = NtOpenFile(
+ &WsDgrecAsyncDeviceHandle,
+ FILE_READ_DATA | FILE_WRITE_DATA,
+ &ObjectAttributes,
+ &IoStatusBlock,
+ FILE_SHARE_VALID_FLAGS,
+ 0L
+ );
+
+ if (NT_SUCCESS(ntstatus)) {
+ ntstatus = IoStatusBlock.Status;
+ }
+
+ if (! NT_SUCCESS(ntstatus)) {
+ NetpKdPrint(("[Wksta] NtOpenFile datagram receiver ASYNC failed: 0x%08lx\n",
+ ntstatus));
+
+ WsDgrecAsyncDeviceHandle = NULL;
+ }
+
+ return NetpNtStatusToApiStatus(ntstatus);
+}
+
+
+NET_API_STATUS
+WsUnloadDriver(
+ IN LPWSTR DriverNameString
+ )
+{
+ ULONG Privileges[1];
+ LPWSTR DriverRegistryName;
+ UNICODE_STRING DriverRegistryString;
+ NET_API_STATUS Status;
+ NTSTATUS ntstatus;
+
+
+ DriverRegistryName = (LPWSTR) LocalAlloc(
+ LMEM_FIXED,
+ (UINT) (sizeof(SERVICE_REGISTRY_KEY) +
+ (wcslen(DriverNameString) *
+ sizeof(WCHAR)))
+ );
+
+ if (DriverRegistryName == NULL) {
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+
+ Privileges[0] = SE_LOAD_DRIVER_PRIVILEGE;
+
+ Status = NetpGetPrivilege(1, Privileges);
+
+ if (Status != NERR_Success) {
+ LocalFree(DriverRegistryName);
+ return Status;
+ }
+
+ wcscpy(DriverRegistryName, SERVICE_REGISTRY_KEY);
+ wcscat(DriverRegistryName, DriverNameString);
+
+ RtlInitUnicodeString(&DriverRegistryString, DriverRegistryName);
+
+ ntstatus = NtUnloadDriver(&DriverRegistryString);
+
+ LocalFree(DriverRegistryName);
+
+ NetpReleasePrivilege();
+
+ return(WsMapStatus(ntstatus));
+}
+
+
+NET_API_STATUS
+WsLoadDriver(
+ IN LPWSTR DriverNameString
+ )
+{
+ ULONG Privileges[1];
+ LPWSTR DriverRegistryName;
+ UNICODE_STRING DriverRegistryString;
+ NET_API_STATUS Status;
+ NTSTATUS ntstatus;
+
+
+
+ DriverRegistryName = (LPWSTR) LocalAlloc(
+ LMEM_FIXED,
+ (UINT) (sizeof(SERVICE_REGISTRY_KEY) +
+ (wcslen(DriverNameString) *
+ sizeof(WCHAR)))
+ );
+
+ if (DriverRegistryName == NULL) {
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ Privileges[0] = SE_LOAD_DRIVER_PRIVILEGE;
+
+ Status = NetpGetPrivilege(1, Privileges);
+
+ if (Status != NERR_Success) {
+ LocalFree(DriverRegistryName);
+ return Status;
+ }
+
+ wcscpy(DriverRegistryName, SERVICE_REGISTRY_KEY);
+ wcscat(DriverRegistryName, DriverNameString);
+
+ RtlInitUnicodeString(&DriverRegistryString, DriverRegistryName);
+
+ ntstatus = NtLoadDriver(&DriverRegistryString);
+
+ NetpReleasePrivilege();
+
+ LocalFree(DriverRegistryName);
+
+ if (ntstatus != STATUS_SUCCESS && ntstatus != STATUS_IMAGE_ALREADY_LOADED) {
+
+ LPWSTR subString[1];
+
+
+ subString[0] = DriverNameString;
+
+ WsLogEvent(
+ NELOG_DriverNotLoaded,
+ 1,
+ subString,
+ ntstatus
+ );
+ }
+
+ return(WsMapStatus(ntstatus));
+}
+
+
+NET_API_STATUS
+WsShutdownRedirector(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This routine close the LAN Man Redirector device.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+
+
+--*/
+{
+ LMR_REQUEST_PACKET Rrp;
+ LMDR_REQUEST_PACKET Drp;
+ NET_API_STATUS Status;
+
+ Rrp.Version = REQUEST_PACKET_VERSION;
+
+ Status = WsRedirFsControl(
+ WsRedirDeviceHandle,
+ FSCTL_LMR_STOP,
+ &Rrp,
+ sizeof(LMR_REQUEST_PACKET),
+ NULL,
+ 0,
+ NULL
+ );
+
+ (void) NtClose(WsRedirDeviceHandle);
+
+ WsRedirDeviceHandle = NULL;
+
+ if (Status != ERROR_REDIRECTOR_HAS_OPEN_HANDLES) {
+
+ //
+ // After the workstation has been stopped, we want to unload our
+ // dependant drivers (the RDR and BOWSER).
+ //
+ WsUnloadRedirector();
+ } else {
+ NET_API_STATUS TempStatus;
+ HKEY hRedirectorKey;
+ DWORD FinalStatus;
+
+ TempStatus = RegOpenKeyEx(
+ HKEY_LOCAL_MACHINE,
+ MRXSMB_REGISTRY_KEY,
+ 0,
+ KEY_ALL_ACCESS,
+ &hRedirectorKey);
+
+ if (TempStatus == ERROR_SUCCESS) {
+ // if this is a controlled shutdown and the driver could not be
+ // unloaded mark the status in the registry for resumption
+
+ FinalStatus = ERROR_SUCCESS;
+ TempStatus = RegSetValueEx(
+ hRedirectorKey,
+ LAST_LOAD_STATUS,
+ 0,
+ REG_DWORD,
+ (PCHAR)&FinalStatus,
+ sizeof(DWORD));
+
+ if (TempStatus == ERROR_SUCCESS) {
+ NetpKdPrint((PREFIX_WKSTA "New RDR will be loaded on restart\n"));
+ }
+
+ RegCloseKey(hRedirectorKey);
+ }
+ }
+
+ //
+ // Delete resource for serializing access to config information
+ //
+ RtlDeleteResource(&WsInfo.ConfigResource);
+
+ if (WsDgReceiverDeviceHandle != NULL) {
+
+ Drp.Version = LMDR_REQUEST_PACKET_VERSION;
+
+ (void) WsDgReceiverIoControl(
+ WsDgReceiverDeviceHandle,
+ IOCTL_LMDR_STOP,
+ &Drp,
+ sizeof(LMDR_REQUEST_PACKET),
+ NULL,
+ 0,
+ NULL
+ );
+
+ (void) NtClose(WsDgReceiverDeviceHandle);
+ WsDgReceiverDeviceHandle = NULL;
+//
+// WsUnloadDriver(L"DGRcvr");
+//
+ }
+
+ return Status;
+}
+
+
+NET_API_STATUS
+WsRedirFsControl(
+ IN HANDLE FileHandle,
+ IN ULONG RedirControlCode,
+ IN PLMR_REQUEST_PACKET Rrp,
+ IN ULONG RrpLength,
+ IN PVOID SecondBuffer OPTIONAL,
+ IN ULONG SecondBufferLength,
+ OUT PULONG Information OPTIONAL
+ )
+/*++
+
+Routine Description:
+
+Arguments:
+
+ FileHandle - Supplies a handle to the file or device on which the service
+ is being performed.
+
+ RedirControlCode - Supplies the NtFsControlFile function code given to
+ the redirector.
+
+ Rrp - Supplies the redirector request packet.
+
+ RrpLength - Supplies the length of the redirector request packet.
+
+ SecondBuffer - Supplies the second buffer in call to NtFsControlFile.
+
+ SecondBufferLength - Supplies the length of the second buffer.
+
+ Information - Returns the information field of the I/O status block.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+
+{
+ NTSTATUS ntstatus;
+ IO_STATUS_BLOCK IoStatusBlock;
+
+ //
+ // Send the request to the Redirector FSD.
+ //
+ ntstatus = NtFsControlFile(
+ FileHandle,
+ NULL,
+ NULL,
+ NULL,
+ &IoStatusBlock,
+ RedirControlCode,
+ Rrp,
+ RrpLength,
+ SecondBuffer,
+ SecondBufferLength
+ );
+
+ if (NT_SUCCESS(ntstatus)) {
+ ntstatus = IoStatusBlock.Status;
+ }
+
+ if (ARGUMENT_PRESENT(Information)) {
+ *Information = IoStatusBlock.Information;
+ }
+
+ IF_DEBUG(UTIL) {
+ if (! NT_SUCCESS(ntstatus)) {
+ NetpKdPrint(("[Wksta] fsctl to redir returns %08lx\n", ntstatus));
+ }
+ }
+
+ return WsMapStatus(ntstatus);
+}
+
+
+
+NET_API_STATUS
+WsDgReceiverIoControl(
+ IN HANDLE FileHandle,
+ IN ULONG DgReceiverControlCode,
+ IN PLMDR_REQUEST_PACKET Drp,
+ IN ULONG DrpSize,
+ IN PVOID SecondBuffer OPTIONAL,
+ IN ULONG SecondBufferLength,
+ OUT PULONG Information OPTIONAL
+ )
+/*++
+
+Routine Description:
+
+Arguments:
+
+ FileHandle - Supplies a handle to the file or device on which the service
+ is being performed.
+
+ DgReceiverControlCode - Supplies the NtDeviceIoControlFile function code
+ given to the datagram receiver.
+
+ Drp - Supplies the datagram receiver request packet.
+
+ DrpSize - Supplies the length of the datagram receiver request packet.
+
+ SecondBuffer - Supplies the second buffer in call to NtDeviceIoControlFile.
+
+ SecondBufferLength - Supplies the length of the second buffer.
+
+ Information - Returns the information field of the I/O status block.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+
+{
+ NTSTATUS ntstatus;
+ IO_STATUS_BLOCK IoStatusBlock;
+
+
+ if (FileHandle == NULL) {
+ IF_DEBUG(UTIL) {
+ NetpKdPrint(("[Wksta] Datagram receiver not implemented\n"));
+ }
+ return ERROR_NOT_SUPPORTED;
+ }
+
+ Drp->TransportName.Length = 0;
+#ifdef _CAIRO_
+ RtlInitUnicodeString( &Drp->EmulatedDomainName, NULL );
+#endif // _CAIRO_
+
+ //
+ // Send the request to the Datagram Receiver DD.
+ //
+ ntstatus = NtDeviceIoControlFile(
+ FileHandle,
+ NULL,
+ NULL,
+ NULL,
+ &IoStatusBlock,
+ DgReceiverControlCode,
+ Drp,
+ DrpSize,
+ SecondBuffer,
+ SecondBufferLength
+ );
+
+ if (NT_SUCCESS(ntstatus)) {
+ ntstatus = IoStatusBlock.Status;
+ }
+
+ if (ARGUMENT_PRESENT(Information)) {
+ *Information = IoStatusBlock.Information;
+ }
+
+ IF_DEBUG(UTIL) {
+ if (! NT_SUCCESS(ntstatus)) {
+ NetpKdPrint(("[Wksta] fsctl to dgreceiver returns %08lx\n", ntstatus));
+ }
+ }
+
+ return WsMapStatus(ntstatus);
+}
+
+
+
+NET_API_STATUS
+WsAsyncBindTransport(
+ IN LPWSTR transportName,
+ IN DWORD qualityOfService,
+ IN PLIST_ENTRY pHeader
+ )
+/*++
+
+Routine Description:
+
+ This function async binds the specified transport to the redirector
+ and the datagram receiver.
+
+ NOTE: The transport name length pass to the redirector and
+ datagram receiver is the number of bytes.
+
+Arguments:
+
+ transportName - Supplies the name of the transport to bind to.
+
+ qualityOfService - Supplies a value which specifies the search
+ order of the transport with respect to other transports. The
+ highest value is searched first.
+
+Return Value:
+
+ NO_ERROR
+
+--*/
+{
+ NTSTATUS ntStatus;
+ NET_API_STATUS status;
+ DWORD size;
+ DWORD redirSize;
+ DWORD dgrecSize;
+ DWORD nameLength;
+ PWS_BIND pBind;
+ PWS_BIND_REDIR pBindRedir;
+ PWS_BIND_DGREC pBindDgrec;
+#ifdef _CAIRO_
+ LPBYTE Where;
+#endif // _CAIRO_
+
+ nameLength = STRLEN( transportName);
+
+ //
+ // Make sure *Size-s are DWORD aligned.
+ //
+
+ dgrecSize = redirSize = size = (nameLength & 1) ? sizeof( WCHAR) : 0;
+
+ //
+ // Then add the fixed part to *Size-s.
+ //
+
+ size += sizeof( WS_BIND) + nameLength * sizeof( WCHAR);
+ redirSize += sizeof( WS_BIND_REDIR) + nameLength * sizeof( WCHAR);
+ dgrecSize += sizeof( WS_BIND_DGREC) +
+ nameLength * sizeof( WCHAR)
+#ifdef _CAIRO_
+ + WsInfo.WsPrimaryDomainNameLength * sizeof(WCHAR) + sizeof(WCHAR)
+#endif // _CAIRO_
+ ;
+
+
+ pBind = (PVOID) LocalAlloc(
+ LMEM_ZEROINIT,
+ (UINT) (size + redirSize + dgrecSize)
+ );
+
+ if ( pBind == NULL) {
+ NetpKdPrint(( "[Wksta] Failed to allocate pBind memory\n"));
+ return GetLastError();
+ }
+
+ pBind->TransportNameLength = nameLength * sizeof( WCHAR);
+ STRCPY( pBind->TransportName, transportName);
+ pBind->Redir = pBindRedir = (PWS_BIND_REDIR)( (PCHAR)pBind + size);
+ pBind->Dgrec = pBindDgrec = (PWS_BIND_DGREC)( (PCHAR)pBindRedir + redirSize);
+
+ pBindRedir->EventHandle = INVALID_HANDLE_VALUE;
+ pBindRedir->Bound = FALSE;
+ pBindRedir->Packet.Version = REQUEST_PACKET_VERSION;
+ pBindRedir->Packet.Parameters.Bind.QualityOfService = qualityOfService;
+ pBindRedir->Packet.Parameters.Bind.TransportNameLength =
+ nameLength * sizeof( WCHAR);
+ STRCPY( pBindRedir->Packet.Parameters.Bind.TransportName, transportName);
+
+ pBindDgrec->EventHandle = INVALID_HANDLE_VALUE;
+ pBindDgrec->Bound = FALSE;
+ pBindDgrec->Packet.Version = LMDR_REQUEST_PACKET_VERSION;
+ pBindDgrec->Packet.Level = 0; // Indicate computername doesn't follow transport name
+ pBindDgrec->Packet.Parameters.Bind.TransportNameLength =
+ nameLength * sizeof( WCHAR);
+ STRCPY( pBindDgrec->Packet.Parameters.Bind.TransportName, transportName);
+
+#ifdef _CAIRO_
+ Where = ((LPBYTE)&pBindDgrec->Packet.Parameters.Bind.TransportName) +
+ pBindDgrec->Packet.Parameters.Bind.TransportNameLength;
+ wcscpy( (LPWSTR) Where, WsInfo.WsPrimaryDomainName);
+ RtlInitUnicodeString( &pBindDgrec->Packet.EmulatedDomainName, (LPWSTR) Where );
+#endif // _CAIRO_
+
+
+ pBindRedir->EventHandle = CreateEvent(
+ NULL,
+ TRUE,
+ FALSE,
+ NULL
+ );
+
+ if ( pBindRedir->EventHandle == INVALID_HANDLE_VALUE) {
+ NetpKdPrint(( "[Wksta] Failed to allocate event handle\n"));
+ status = GetLastError();
+ goto tail_exit;
+ }
+
+ ntStatus = NtFsControlFile(
+ WsRedirAsyncDeviceHandle,
+ pBindRedir->EventHandle,
+ NULL, // apc routine
+ NULL, // apc context
+ &pBindRedir->IoStatusBlock,
+ FSCTL_LMR_BIND_TO_TRANSPORT, // control code
+ &pBindRedir->Packet,
+ sizeof( LMR_REQUEST_PACKET) +
+ pBindRedir->Packet.Parameters.Bind.TransportNameLength,
+ NULL,
+ 0
+ );
+
+ if ( ntStatus != STATUS_PENDING) {
+ CloseHandle( pBindRedir->EventHandle);
+ pBindRedir->EventHandle = INVALID_HANDLE_VALUE;
+ pBindRedir->Bound = NT_SUCCESS( ntStatus );
+ }
+
+
+ pBindDgrec->EventHandle = CreateEvent(
+ NULL,
+ TRUE,
+ FALSE,
+ NULL
+ );
+
+ if ( pBindDgrec->EventHandle == INVALID_HANDLE_VALUE) {
+ status = GetLastError();
+ goto tail_exit;
+ }
+
+#ifdef RDR_PNP_POWER
+ ntStatus = STATUS_SUCCESS;
+#else
+ ntStatus = NtDeviceIoControlFile(
+ WsDgrecAsyncDeviceHandle,
+ pBindDgrec->EventHandle,
+ NULL,
+ NULL,
+ &pBindDgrec->IoStatusBlock,
+ IOCTL_LMDR_BIND_TO_TRANSPORT,
+ &pBindDgrec->Packet,
+ dgrecSize - FIELD_OFFSET( WS_BIND_DGREC, Packet ),
+ NULL,
+ 0
+ );
+#endif
+
+ if ( ntStatus != STATUS_PENDING) {
+ CloseHandle( pBindDgrec->EventHandle);
+ pBindDgrec->EventHandle = INVALID_HANDLE_VALUE;
+ pBindDgrec->Bound = NT_SUCCESS( ntStatus );
+ }
+
+tail_exit:
+ InsertTailList( pHeader, &pBind->ListEntry);
+ return NO_ERROR;
+}
+
+
+
+NET_API_STATUS
+WsBindTransport(
+ IN LPTSTR TransportName,
+ IN DWORD QualityOfService,
+ OUT LPDWORD ErrorParameter OPTIONAL
+ )
+/*++
+
+Routine Description:
+
+ This function binds the specified transport to the redirector
+ and the datagram receiver.
+
+ NOTE: The transport name length pass to the redirector and
+ datagram receiver is the number of bytes.
+
+Arguments:
+
+ TransportName - Supplies the name of the transport to bind to.
+
+ QualityOfService - Supplies a value which specifies the search
+ order of the transport with respect to other transports. The
+ highest value is searched first.
+
+ ErrorParameter - Returns the identifier to the invalid parameter if
+ this function returns ERROR_INVALID_PARAMETER.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NET_API_STATUS status;
+ DWORD RequestPacketSize;
+ DWORD TransportNameSize = STRLEN(TransportName) * sizeof(TCHAR);
+
+ PLMR_REQUEST_PACKET Rrp;
+ PLMDR_REQUEST_PACKET Drrp;
+#ifdef _CAIRO_
+ LPBYTE Where;
+#endif // _CAIRO_
+
+
+ //
+ // Size of request packet buffer
+ //
+ RequestPacketSize = STRLEN(TransportName) * sizeof(WCHAR) +
+#ifdef _CAIRO_
+ WsInfo.WsPrimaryDomainNameLength*sizeof(WCHAR)+sizeof(WCHAR)+
+#endif // _CAIRO_
+ max(sizeof(LMR_REQUEST_PACKET),
+ sizeof(LMDR_REQUEST_PACKET));
+
+ //
+ // Allocate memory for redirector/datagram receiver request packet
+ //
+ if ((Rrp = (PVOID) LocalAlloc(LMEM_ZEROINIT, (UINT) RequestPacketSize)) == NULL) {
+ return GetLastError();
+ }
+
+ //
+ // Get redirector to bind to transport
+ //
+ Rrp->Version = REQUEST_PACKET_VERSION;
+ Rrp->Parameters.Bind.QualityOfService = QualityOfService;
+
+ Rrp->Parameters.Bind.TransportNameLength = TransportNameSize;
+ STRCPY(Rrp->Parameters.Bind.TransportName, TransportName);
+
+ if ((status = WsRedirFsControl(
+ WsRedirDeviceHandle,
+ FSCTL_LMR_BIND_TO_TRANSPORT,
+ Rrp,
+ sizeof(LMR_REQUEST_PACKET) +
+ Rrp->Parameters.Bind.TransportNameLength,
+ //Rrp, BUGBUG: rdr bugchecks!
+ //RequestPacketSize, specify NULL for now
+ NULL,
+ 0,
+ NULL
+ )) != NERR_Success) {
+
+ if (status == ERROR_INVALID_PARAMETER &&
+ ARGUMENT_PRESENT(ErrorParameter)) {
+
+ IF_DEBUG(INFO) {
+ NetpKdPrint((
+ "[Wksta] NetrWkstaTransportAdd: invalid parameter is %lu\n",
+ Rrp->Parameters.Bind.WkstaParameter));
+ }
+
+ *ErrorParameter = Rrp->Parameters.Bind.WkstaParameter;
+ }
+
+ (void) LocalFree(Rrp);
+ return status;
+ }
+
+
+ //
+ // Get dgrec to bind to transport
+ //
+
+ //
+ // Make use of the same request packet buffer as FSCtl to
+ // redirector.
+ //
+ Drrp = (PLMDR_REQUEST_PACKET) Rrp;
+
+ Drrp->Version = LMDR_REQUEST_PACKET_VERSION;
+
+ Drrp->Parameters.Bind.TransportNameLength = TransportNameSize;
+ STRCPY(Drrp->Parameters.Bind.TransportName, TransportName);
+
+#ifdef _CAIRO_
+ Where = ((LPBYTE)&Drrp->Parameters.Bind.TransportName) +
+ Drrp->Parameters.Bind.TransportNameLength;
+ wcscpy( (LPWSTR) Where, WsInfo.WsPrimaryDomainName);
+ RtlInitUnicodeString( &Drrp->EmulatedDomainName, (LPWSTR) Where );
+#endif // _CAIRO_
+
+ status = WsDgReceiverIoControl(
+ WsDgReceiverDeviceHandle,
+ IOCTL_LMDR_BIND_TO_TRANSPORT,
+ Drrp,
+ RequestPacketSize,
+ NULL,
+ 0,
+ NULL
+ );
+
+ (void) LocalFree(Rrp);
+ return status;
+}
+
+
+VOID
+WsUnbindTransport2(
+ IN PWS_BIND pBind
+ )
+/*++
+
+Routine Description:
+
+ This function unbinds the specified transport from the redirector
+ and the datagram receiver.
+
+Arguments:
+
+ pBind - structure constructed via WsAsyncBindTransport()
+
+Return Value:
+
+ None.
+
+--*/
+{
+// NET_API_STATUS status;
+ PWS_BIND_REDIR pBindRedir = pBind->Redir;
+ PWS_BIND_DGREC pBindDgrec = pBind->Dgrec;
+
+
+ //
+ // Get redirector to unbind from transport
+ //
+
+ if ( pBindRedir->Bound == TRUE) {
+ pBindRedir->Packet.Parameters.Unbind.TransportNameLength
+ = pBind->TransportNameLength;
+ memcpy(
+ pBindRedir->Packet.Parameters.Unbind.TransportName,
+ pBind->TransportName,
+ pBind->TransportNameLength
+ );
+
+ (VOID)NtFsControlFile(
+ WsRedirDeviceHandle,
+ NULL,
+ NULL, // apc routine
+ NULL, // apc context
+ &pBindRedir->IoStatusBlock,
+ FSCTL_LMR_UNBIND_FROM_TRANSPORT, // control code
+ &pBindRedir->Packet,
+ sizeof( LMR_REQUEST_PACKET) +
+ pBindRedir->Packet.Parameters.Unbind.TransportNameLength,
+ NULL,
+ 0
+ );
+ pBindRedir->Bound = FALSE;
+ }
+
+ //
+ // Get datagram receiver to unbind from transport
+ //
+
+ if ( pBindDgrec->Bound == TRUE) {
+
+ pBindDgrec->Packet.Parameters.Unbind.TransportNameLength
+ = pBind->TransportNameLength;
+ memcpy(
+ pBindDgrec->Packet.Parameters.Unbind.TransportName,
+ pBind->TransportName,
+ pBind->TransportNameLength
+ );
+
+ (VOID)NtDeviceIoControlFile(
+ WsDgReceiverDeviceHandle,
+ NULL,
+ NULL, // apc routine
+ NULL, // apc context
+ &pBindDgrec->IoStatusBlock,
+ FSCTL_LMR_UNBIND_FROM_TRANSPORT, // control code
+ &pBindDgrec->Packet,
+ sizeof( LMR_REQUEST_PACKET) +
+ pBindDgrec->Packet.Parameters.Unbind.TransportNameLength,
+ NULL,
+ 0
+ );
+ pBindDgrec->Bound = FALSE;
+ }
+}
+
+
+NET_API_STATUS
+WsUnbindTransport(
+ IN LPTSTR TransportName,
+ IN DWORD ForceLevel
+ )
+/*++
+
+Routine Description:
+
+ This function unbinds the specified transport from the redirector
+ and the datagram receiver.
+
+ NOTE: The transport name length pass to the redirector and
+ datagram receiver is the number of bytes.
+
+Arguments:
+
+ TransportName - Supplies the name of the transport to bind to.
+
+ ForceLevel - Supplies the force level to delete active connections
+ on the specified transport.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NET_API_STATUS status;
+ DWORD RequestPacketSize;
+ DWORD TransportNameSize = STRLEN(TransportName) * sizeof(TCHAR);
+
+ PLMR_REQUEST_PACKET Rrp;
+ PLMDR_REQUEST_PACKET Drrp;
+#ifdef _CAIRO_
+ LPBYTE Where;
+#endif // _CAIRO_
+
+
+ //
+ // Size of request packet buffer
+ //
+ RequestPacketSize = STRLEN(TransportName) * sizeof(WCHAR) +
+#ifdef _CAIRO_
+ WsInfo.WsPrimaryDomainNameLength*sizeof(WCHAR)+sizeof(WCHAR)+
+#endif // _CAIRO_
+ max(sizeof(LMR_REQUEST_PACKET),
+ sizeof(LMDR_REQUEST_PACKET));
+
+ //
+ // Allocate memory for redirector/datagram receiver request packet
+ //
+ if ((Rrp = (PVOID) LocalAlloc(LMEM_ZEROINIT, (UINT) RequestPacketSize)) == NULL) {
+ return GetLastError();
+ }
+
+
+ //
+ // Get redirector to unbind from transport
+ //
+ Rrp->Version = REQUEST_PACKET_VERSION;
+ Rrp->Level = ForceLevel;
+
+ Rrp->Parameters.Unbind.TransportNameLength = TransportNameSize;
+ STRCPY(Rrp->Parameters.Unbind.TransportName, TransportName);
+
+ if ((status = WsRedirFsControl(
+ WsRedirDeviceHandle,
+ FSCTL_LMR_UNBIND_FROM_TRANSPORT,
+ Rrp,
+ sizeof(LMR_REQUEST_PACKET) +
+ Rrp->Parameters.Unbind.TransportNameLength,
+ NULL,
+ 0,
+ NULL
+ )) != NERR_Success) {
+ (void) LocalFree(Rrp);
+ return status;
+ }
+
+ //
+ // Get datagram receiver to unbind from transport
+ //
+
+ //
+ // Make use of the same request packet buffer as FSCtl to
+ // redirector.
+ //
+ Drrp = (PLMDR_REQUEST_PACKET) Rrp;
+
+ Drrp->Version = LMDR_REQUEST_PACKET_VERSION;
+ Drrp->Level = ForceLevel;
+
+ Drrp->Parameters.Unbind.TransportNameLength = TransportNameSize;
+ STRCPY(Drrp->Parameters.Unbind.TransportName, TransportName);
+
+#ifdef _CAIRO_
+ Where = ((LPBYTE)&Drrp->Parameters.Unbind.TransportName) +
+ Drrp->Parameters.Unbind.TransportNameLength;
+ wcscpy( (LPWSTR) Where, WsInfo.WsPrimaryDomainName);
+ RtlInitUnicodeString( &Drrp->EmulatedDomainName, (LPWSTR) Where );
+#endif // _CAIRO_
+
+ if ((status = WsDgReceiverIoControl(
+ WsDgReceiverDeviceHandle,
+ IOCTL_LMDR_UNBIND_FROM_TRANSPORT,
+ Drrp,
+ RequestPacketSize,
+ NULL,
+ 0,
+ NULL
+ )) != NERR_Success) {
+
+// BUGBUG: This is a hack until the bowser supports XNS and LOOP.
+
+ if (status == NERR_UseNotFound) {
+ status = NERR_Success;
+ }
+ }
+
+ (void) LocalFree(Rrp);
+ return status;
+}
+
+
+NET_API_STATUS
+WsDeleteDomainName(
+ IN PLMDR_REQUEST_PACKET Drp,
+ IN DWORD DrpSize,
+ IN LPTSTR DomainName,
+ IN DWORD DomainNameSize
+ )
+/*++
+
+Routine Description:
+
+ This function deletes a domain name from the datagram receiver for
+ the current user. It assumes that enough memory is allocate for the
+ request packet that is passed in.
+
+Arguments:
+
+ Drp - Pointer to a datagram receiver request packet with the
+ request packet version, and name type initialized.
+
+ DrpSize - Length of datagram receiver request packet in bytes.
+
+ DomainName - Pointer to the domain name to delete.
+
+ DomainNameSize - Length of the domain name in bytes.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ Drp->Parameters.AddDelName.DgReceiverNameLength = DomainNameSize;
+
+ memcpy(
+ (LPBYTE) Drp->Parameters.AddDelName.Name,
+ (LPBYTE) DomainName,
+ DomainNameSize
+ );
+
+ return WsDgReceiverIoControl(
+ WsDgReceiverDeviceHandle,
+ IOCTL_LMDR_DELETE_NAME,
+ Drp,
+ DrpSize,
+ NULL,
+ 0,
+ NULL
+ );
+}
+
+
+NET_API_STATUS
+WsAddDomainName(
+ IN PLMDR_REQUEST_PACKET Drp,
+ IN DWORD DrpSize,
+ IN LPTSTR DomainName,
+ IN DWORD DomainNameSize
+ )
+/*++
+
+Routine Description:
+
+ This function adds a domain name to the datagram receiver for the
+ current user. It assumes that enough memory is allocate for the
+ request packet that is passed in.
+
+Arguments:
+
+ Drp - Pointer to a datagram receiver request packet with the
+ request packet version, and name type initialized.
+
+ DrpSize - Length of datagram receiver request packet in bytes.
+
+ DomainName - Pointer to the domain name to delete.
+
+ DomainNameSize - Length of the domain name in bytes.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ Drp->Parameters.AddDelName.DgReceiverNameLength = DomainNameSize;
+
+ memcpy(
+ (LPBYTE) Drp->Parameters.AddDelName.Name,
+ (LPBYTE) DomainName,
+ DomainNameSize
+ );
+
+ return WsDgReceiverIoControl(
+ WsDgReceiverDeviceHandle,
+ IOCTL_LMDR_ADD_NAME,
+ Drp,
+ DrpSize,
+ NULL,
+ 0,
+ NULL
+ );
+}
+
+NET_API_STATUS
+WsTryToLoadSmbMiniRedirector(
+ VOID
+ );
+NET_API_STATUS
+WsLoadRedirector(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This routine loads rdr.sys or mrxsmb.sys et al depending on whether
+ the conditions for loading mrxsmb.sys are met.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+Notes:
+
+ The new redirector consists of two parts -- the RDBSS (redirected drive buffering
+ subsystem ) and the corresponding smb mini redirectors. Only the minirdr is loaded here;
+ the minirdr loads the RDBSS itself.
+
+ As a stopgap measure the old redirector is loaded in the event of any problem
+ associated with loading the new redirector.
+
+--*/
+{
+ NET_API_STATUS status;
+
+ status = WsTryToLoadSmbMiniRedirector();
+ if ((status != ERROR_SUCCESS) &&
+ (status != ERROR_SERVICE_ALREADY_RUNNING)) {
+ // Either the new redirector load did not succeed or it does not exist.
+ // Load the old redirector.
+
+ LoadedMRxSmbInsteadOfRdr = FALSE;
+ status = WsLoadDriver(REDIRECTOR);
+ }
+
+ return(status);
+}
+
+VOID
+WsUnloadRedirector(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This routine unloads the drivers that we loaded above.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ NET_API_STATUS status;
+ DWORD NameLength,NameOffset;
+ HKEY hRedirectorKey;
+ DWORD FinalStatus;
+
+ if (!LoadedMRxSmbInsteadOfRdr) {
+ WsUnloadDriver(REDIRECTOR);
+ return;
+ }
+
+ WsUnloadDriver(SMB_MINIRDR);
+ //WsUnloadDriver(RDBSS);
+
+ status = RegOpenKeyEx(
+ HKEY_LOCAL_MACHINE,
+ MRXSMB_REGISTRY_KEY,
+ 0,
+ KEY_ALL_ACCESS,
+ &hRedirectorKey);
+
+ if (status == ERROR_SUCCESS) {
+ // if the unloading was successful, reset the LastLoadStatus so that
+ // the new redirector will be loaded on the next attempt as well.
+ FinalStatus = ERROR_SUCCESS;
+ status = RegSetValueEx(
+ hRedirectorKey,
+ LAST_LOAD_STATUS,
+ 0,
+ REG_DWORD,
+ (PCHAR)&FinalStatus,
+ sizeof(DWORD));
+
+ if (status == ERROR_SUCCESS) {
+ NetpKdPrint((PREFIX_WKSTA "New RDR will be loaded on restart\n"));
+ }
+
+ RegCloseKey(hRedirectorKey);
+ }
+
+ return;
+}
+
+////////////////////// minirdr stuff
+NET_API_STATUS
+WsTryToLoadSmbMiniRedirector(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This routine loads rdr.sys or mrxsmb.sys et al depending on whether
+ the conditions for loading mrxsmb.sys are met.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+Notes:
+
+ The new redirector consists of two parts -- the RDBSS (redirected drive buffering
+ subsystem ) and the corresponding smb mini redirectors. Only the minirdr is loaded here;
+ the minirdr loads the RDBSS itself.
+
+ As a stopgap measure the old redirector is loaded in the event of any problem
+ associated with loading the new redirector.
+
+--*/
+{
+ NET_API_STATUS status;
+ ULONG Attributes;
+ DWORD ValueType;
+ DWORD ValueSize;
+ DWORD NameLength,NameOffset;
+ HKEY hRedirectorKey;
+
+ DWORD FinalStatus; // Temporary till the new rdr is the default
+ DWORD LastLoadStatus;
+
+ //try to open the minirdr's registry key....if fails GET OUT IMMEDIATELY!!!!
+ status = RegOpenKeyEx(
+ HKEY_LOCAL_MACHINE,
+ MRXSMB_REGISTRY_KEY,
+ 0,
+ KEY_ALL_ACCESS,
+ &hRedirectorKey);
+
+ if (status != ERROR_SUCCESS) {
+ return(status);
+ }
+
+ ValueSize = sizeof(DWORD);
+ status = RegQueryValueEx(
+ hRedirectorKey,
+ LAST_LOAD_STATUS,
+ NULL,
+ &ValueType,
+ (PCHAR)&LastLoadStatus,
+ &ValueSize);
+
+ if (status == ERROR_SUCCESS) {
+ DWORD LoadStatus = ERROR_DEVICE_IN_USE;
+
+ status = RegSetValueEx(
+ hRedirectorKey,
+ LAST_LOAD_STATUS,
+ 0,
+ REG_DWORD,
+ (PCHAR)&LoadStatus,
+ sizeof(DWORD));
+ }
+
+ if (status == ERROR_SUCCESS) {
+ if (LastLoadStatus == ERROR_SUCCESS) {
+ // Either the service was unloaded successfully or the workstation
+ // service was stopped without unloading the redirector previously.
+
+ status = WsLoadDriver(RDBSS);
+
+ if ((status == ERROR_SUCCESS) || (status == ERROR_SERVICE_ALREADY_RUNNING)) {
+
+ status = WsLoadDriver(SMB_MINIRDR);
+
+ if (status == ERROR_SUCCESS) {
+ LoadedMRxSmbInsteadOfRdr = TRUE;
+ } else if (status == ERROR_SERVICE_ALREADY_RUNNING) {
+ NetpKdPrint((PREFIX_WKSTA "Reactivating Previously Loaded Service\n"));
+ LoadedMRxSmbInsteadOfRdr = TRUE;
+ status = ERROR_SUCCESS;
+ } else {
+ // error loading the minirdr
+ //WsUnloadDriver(RDBSS);
+ NetpKdPrint((PREFIX_WKSTA "Error Loading MRxSmb\n"));
+ }
+ }
+
+ } else {
+ status = LastLoadStatus;
+ }
+
+ NetpKdPrint((PREFIX_WKSTA "New redirector(RDR2) load status %lx\n",status));
+
+ // update the registry with the status of loading the new redirector.
+ if (LastLoadStatus == ERROR_SUCCESS) {
+ LastLoadStatus = status;
+ }
+
+ // BUGBUG -- Temporarily the new redirector loading mechanism is designed as
+ // one shot loading mechanism. Therefore the LastLoadStatus is set to an
+ // erroneous value so as to manually control the loading of the new redirector.
+ FinalStatus = ERROR_NOT_READY;
+ status = RegSetValueEx(
+ hRedirectorKey,
+ LAST_LOAD_STATUS,
+ 0,
+ REG_DWORD,
+ (PCHAR)&FinalStatus,
+ sizeof(DWORD));
+
+ if (status == ERROR_SUCCESS) {
+ status = LastLoadStatus;
+ }
+ }
+
+ // Close the handle to the registry key irrespective of the result.
+ RegCloseKey(hRedirectorKey);
+
+ return(status);
+}
+
diff --git a/private/net/svcdlls/wkssvc/server/wsdevice.h b/private/net/svcdlls/wkssvc/server/wsdevice.h
new file mode 100644
index 000000000..8a66e1227
--- /dev/null
+++ b/private/net/svcdlls/wkssvc/server/wsdevice.h
@@ -0,0 +1,178 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ wsdevice.h
+
+Abstract:
+
+ Private header file to be included by Workstation service modules that
+ need to call into the NT Redirector and the NT Datagram Receiver.
+
+Author:
+
+ Rita Wong (ritaw) 15-Feb-1991
+
+Revision History:
+
+--*/
+
+#ifndef _WSDEVICE_INCLUDED_
+#define _WSDEVICE_INCLUDED_
+
+#include <ntddnfs.h> // Redirector include file
+
+#include <ntddbrow.h> // Datagram receiver include file
+
+//-------------------------------------------------------------------//
+// //
+// Type definitions //
+// //
+//-------------------------------------------------------------------//
+
+typedef enum _DDTYPE {
+ Redirector,
+ DatagramReceiver
+} DDTYPE, *PDDTYPE;
+
+
+//-------------------------------------------------------------------//
+// //
+// Function prototypes of support routines found in wsdevice.c //
+// //
+//-------------------------------------------------------------------//
+
+NET_API_STATUS
+WsDeviceControlGetInfo(
+ IN DDTYPE DeviceDriverType,
+ IN HANDLE FileHandle,
+ IN ULONG DeviceControlCode,
+ IN PVOID RequestPacket,
+ IN ULONG RequestPacketLength,
+ OUT LPBYTE *OutputBuffer,
+ IN ULONG PreferedMaximumLength,
+ IN ULONG BufferHintSize,
+ OUT PULONG Information OPTIONAL
+ );
+
+NET_API_STATUS
+WsInitializeRedirector(
+ VOID
+ );
+
+NET_API_STATUS
+WsShutdownRedirector(
+ VOID
+ );
+
+NET_API_STATUS
+WsRedirFsControl (
+ IN HANDLE FileHandle,
+ IN ULONG RedirControlCode,
+ IN PLMR_REQUEST_PACKET Rrp,
+ IN ULONG RrpLength,
+ IN PVOID SecondBuffer OPTIONAL,
+ IN ULONG SecondBufferLength,
+ OUT PULONG Information OPTIONAL
+ );
+
+NET_API_STATUS
+WsDgReceiverIoControl(
+ IN HANDLE FileHandle,
+ IN ULONG DgReceiverControlCode,
+ IN PLMDR_REQUEST_PACKET Drp,
+ IN ULONG DrpLength,
+ IN PVOID SecondBuffer OPTIONAL,
+ IN ULONG SecondBufferLength,
+ OUT PULONG Information OPTIONAL
+ );
+
+NET_API_STATUS
+WsBindTransport(
+ IN LPTSTR TransportName,
+ IN DWORD QualityOfService,
+ OUT LPDWORD ErrorParameter OPTIONAL
+ );
+
+NET_API_STATUS
+WsUnbindTransport(
+ IN LPTSTR TransportName,
+ IN DWORD ForceLevel
+ );
+
+NET_API_STATUS
+WsDeleteDomainName(
+ IN PLMDR_REQUEST_PACKET Drp,
+ IN DWORD DrpLength,
+ IN LPTSTR DomainName,
+ IN DWORD DomainNameSize
+ );
+
+NET_API_STATUS
+WsAddDomainName(
+ IN PLMDR_REQUEST_PACKET Drp,
+ IN DWORD DrpLength,
+ IN LPTSTR DomainName,
+ IN DWORD DomainNameSize
+ );
+
+NET_API_STATUS
+WsUnloadDriver(
+ IN LPTSTR DriverNameString
+ );
+
+NET_API_STATUS
+WsLoadDriver(
+ IN LPWSTR DriverNameString
+ );
+
+//-------------------------------------------------------------------//
+// //
+// Global variables //
+// //
+//-------------------------------------------------------------------//
+
+// Global Registry key definitions for the new Redirector
+
+#define SERVICE_REGISTRY_KEY L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\"
+
+#define SMB_MINIRDR L"MRxSmb"
+#define RDBSS L"Rdbss"
+
+// the key definition is relative to HKEY_LOCAL_MACHINE
+#define MRXSMB_REGISTRY_KEY L"System\\CurrentControlSet\\Services\\MRxSmb"
+#define LAST_LOAD_STATUS L"LastLoadStatus"
+
+#define REDIRECTOR L"RDR"
+
+//
+// Handle to the Redirector FSD
+//
+extern HANDLE WsRedirDeviceHandle;
+
+//
+// This variable is used to remember whether we loaded rdr.sys or mrxsmb.sys
+// and to act accordingly. at this stage, we only load mrxsmb.sys when certain
+// conditions are met.
+//
+extern BOOLEAN LoadedMRxSmbInsteadOfRdr;
+
+
+extern NET_API_STATUS
+WsLoadRedirector(
+ VOID
+ );
+
+extern VOID
+WsUnloadRedirector(
+ VOID
+ );
+
+//
+// Handle to the Datagram Receiver DD
+//
+extern HANDLE WsDgReceiverDeviceHandle;
+
+#endif // ifndef _WSDEVICE_INCLUDED_
diff --git a/private/net/svcdlls/wkssvc/server/wsdfs.c b/private/net/svcdlls/wkssvc/server/wsdfs.c
new file mode 100644
index 000000000..01dd6b9b0
--- /dev/null
+++ b/private/net/svcdlls/wkssvc/server/wsdfs.c
@@ -0,0 +1,298 @@
+//+----------------------------------------------------------------------------
+//
+// Copyright (C) 1996, Microsoft Corporation
+//
+// File: wsdfs.c
+//
+// Contents: Code to communicate with the Dfs driver. The Dfs driver
+// sends two primary messages to the user level -
+// 1. Requests to resolve a name to either a domain or
+// computer based Dfs name.
+// 2. Requests to handle knowledge-inconsistency reports from
+// clients.
+//
+// The code here receives these messages from the driver and
+// takes appropriate action.
+//
+// Classes: None
+//
+// Functions: DfsProcessDriverMessage
+//
+// History: Feb 1, 1996 Milans Created
+//
+//-----------------------------------------------------------------------------
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <dfsfsctl.h>
+#include <stdlib.h>
+#include <windows.h>
+#include <lm.h>
+
+#include "wsdfs.h"
+#include "dominfo.h"
+
+#define DFS_MESSAGE_PIPE L"\\\\.\\pipe\\DfsMessage"
+
+static HANDLE hPipe = INVALID_HANDLE_VALUE;
+
+DWORD DfsProcessDriverMessage(
+ LPVOID lpThreadParams);
+
+
+//+----------------------------------------------------------------------------
+//
+// Function: WsInitializeDfs
+//
+// Synopsis: Initializes the Dfs thread that waits for calls from the
+// driver to map Domain names into DC lists
+//
+// Arguments: None
+//
+// Returns: WIN32 error from CreateThread
+//
+//-----------------------------------------------------------------------------
+
+NET_API_STATUS
+WsInitializeDfs()
+{
+ HANDLE hIPC;
+ DWORD idThread;
+
+ hIPC = CreateThread(
+ NULL, // Security attributes
+ 0, // Use default stack size
+ DfsProcessDriverMessage, // Thread entry procedure
+ NULL, // Thread context parameter
+ 0, // Start immediately
+ &idThread); // Thread ID
+
+ if (hIPC == NULL) {
+ return( GetLastError() );
+ } else {
+ CloseHandle( hIPC );
+ return( NERR_Success );
+ }
+}
+
+//+----------------------------------------------------------------------------
+//
+// Function: WsShutdownDfs
+//
+// Synopsis: Stops the thread created by WsInitializeDfs
+//
+// Arguments: None
+//
+// Returns: Nothing
+//
+//-----------------------------------------------------------------------------
+
+VOID
+WsShutdownDfs()
+{
+ DWORD dwReturn, cbRead;
+ NTSTATUS Status;
+ HANDLE hDfs;
+
+ Status = DfsOpen( &hDfs, NULL );
+ if (NT_SUCCESS(Status)) {
+
+ Status = DfsFsctl(
+ hDfs,
+ FSCTL_DFS_STOP_DFS,
+ NULL,
+ 0L,
+ NULL,
+ 0L);
+
+ NtClose( hDfs );
+
+ }
+
+ if (hPipe != INVALID_HANDLE_VALUE) {
+
+ HANDLE hClientPipe;
+ DWORD nMessage = 0;
+ DWORD cbWritten;
+
+ hClientPipe = CreateFile(
+ DFS_MESSAGE_PIPE,
+ GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL,
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL);
+
+ if (hClientPipe != INVALID_HANDLE_VALUE) {
+
+ WriteFile(hClientPipe, &nMessage, sizeof(nMessage), &cbWritten, NULL);
+
+ CloseHandle( hClientPipe );
+
+ }
+
+ }
+
+}
+
+//+----------------------------------------------------------------------------
+//
+// Function: DfsProcessDriverMessage
+//
+// Synopsis: Sets up a named-pipe via which the Dfs driver can transmit
+// messages.
+//
+// This routine is intended to be called as the entry proc for a
+// thread.
+//
+// Arguments: [lpThreadParams] -- Unused parameter.
+//
+// Returns: 0 or Win32 error from CreateNamedPipe.
+//
+//-----------------------------------------------------------------------------
+
+DWORD DfsProcessDriverMessage(
+ LPVOID lpThreadParams)
+{
+ NTSTATUS Status;
+ LPWSTR wszDomain, wszShare;
+
+ ULONG cbMessage, cbRead, ulMessageType;
+ PBYTE Buffer;
+
+ BOOL fStatus;
+ BOOL fConnected;
+ BOOL fDomainListInited = FALSE;
+
+ //
+ // Set up the named pipe
+ //
+
+ hPipe = CreateNamedPipeW(
+ DFS_MESSAGE_PIPE,
+ PIPE_ACCESS_DUPLEX,
+ PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
+ 1,
+ 1024,
+ 1024,
+ 0,
+ NULL);
+
+ if (hPipe == INVALID_HANDLE_VALUE) {
+ return( GetLastError() );
+ }
+
+ //
+ // Initialize the trusted Domain list
+ //
+
+ while (TRUE) {
+
+ fConnected = ConnectNamedPipe(hPipe, NULL) ?
+ TRUE : (GetLastError() == ERROR_PIPE_CONNECTED);
+
+ if (!fConnected)
+ break;
+
+ //
+ // The driver connected to the pipe, so lets see what is in store
+ // for us.
+ //
+
+ fStatus = ReadFile(hPipe, &cbMessage, sizeof(cbMessage), &cbRead, NULL);
+
+ if (!fStatus || cbRead == 0) {
+ //
+ // Looks like the driver had trouble writing to the pipe.
+ // We disconnect the driver and go on.
+ // We must log that a knowledge sync event has been lost.
+ //
+
+ DisconnectNamedPipe(hPipe);
+ continue;
+ }
+
+ if (cbMessage == 0) {
+
+ DisconnectNamedPipe(hPipe);
+ break;
+
+ }
+
+ Buffer = (LPBYTE) malloc(cbMessage);
+
+ if (Buffer == NULL) {
+
+ DisconnectNamedPipe(hPipe);
+
+ continue;
+
+ }
+
+ fStatus = ReadFile(hPipe, Buffer, cbMessage, &cbRead, NULL);
+
+ if (!fStatus) {
+
+ DisconnectNamedPipe(hPipe);
+
+ free( Buffer );
+
+ continue;
+
+ } else if (cbRead != cbMessage) {
+
+ WriteFile(hPipe, &cbMessage, sizeof(cbMessage), &cbRead, NULL);
+ FlushFileBuffers(hPipe);
+
+ DisconnectNamedPipe(hPipe);
+ free( Buffer );
+ continue;
+
+ }
+
+ ulMessageType = *((ULONG *) Buffer);
+ Buffer += sizeof(ULONG);
+
+ switch (ulMessageType) {
+
+ case DFS_MSGTYPE_GET_DOMAIN_REFERRAL:
+ if (!fDomainListInited) {
+ DfsInitDomainList();
+ fDomainListInited = TRUE;
+ }
+ wszDomain = (LPWSTR) Buffer;
+ wszShare = &wszDomain[ wcslen(wszDomain) + 1 ];
+ Status = DfsGetDomainReferral( wszDomain, wszShare );
+ break;
+
+ default:
+ ASSERT( FALSE && "Invalid Message from driver!\n" );
+ break;
+ }
+
+ Buffer -= sizeof(ULONG);
+
+ //
+ // Write the status code back to the pipe.
+ //
+
+ WriteFile(hPipe, &Status, sizeof(Status), &cbRead, NULL);
+
+ FlushFileBuffers(hPipe);
+
+ DisconnectNamedPipe(hPipe);
+
+ free( Buffer );
+
+ }
+
+ CloseHandle(hPipe);
+
+ hPipe = INVALID_HANDLE_VALUE;
+
+ return(0);
+
+}
+
diff --git a/private/net/svcdlls/wkssvc/server/wsdfs.h b/private/net/svcdlls/wkssvc/server/wsdfs.h
new file mode 100644
index 000000000..4d7ae9e15
--- /dev/null
+++ b/private/net/svcdlls/wkssvc/server/wsdfs.h
@@ -0,0 +1,34 @@
+/*++
+
+Copyright (c) 1996 Microsoft Corporation
+
+Module Name:
+
+ wsdfs.h
+
+Abstract:
+
+ Private header file to be included by Workstation service modules that
+ interact with the Dfs server thread.
+
+Author:
+
+ Milan Shah (milans) 29-Feb-1996
+
+Revision History:
+
+--*/
+
+#ifndef _WSDFS_INCLUDED_
+#define _WSDFS_INCLUDED_
+
+#define DFS_MSGTYPE_TERMINATE 0x0001
+#define DFS_MSGTYPE_GET_DOMAIN_REFERRAL 0x0002
+
+NET_API_STATUS
+WsInitializeDfs();
+
+VOID
+WsShutdownDfs();
+
+#endif // ifndef _WSUSE_INCLUDED_
diff --git a/private/net/svcdlls/wkssvc/server/wslogon.c b/private/net/svcdlls/wkssvc/server/wslogon.c
new file mode 100644
index 000000000..0b19522b1
--- /dev/null
+++ b/private/net/svcdlls/wkssvc/server/wslogon.c
@@ -0,0 +1,101 @@
+/*++
+
+Copyright (c) 1991-92 Microsoft Corporation
+
+Module Name:
+
+ wslogon.c
+
+Abstract:
+
+ This module provides the Workstation service logon support, which
+ include sending logoff message for users on the local machine that
+ got reset unexpectedly, respond to a relogon request, and respond
+ to an interrogation request.
+
+Author:
+
+ Rita Wong (ritaw) 20-Aug-1991
+
+Environment:
+
+ User Mode - Win32
+
+Revision History:
+ terryk 10-18-1993 Removed WsInitializeLogon stuff
+
+--*/
+
+#include <stdlib.h> // C Runtime: rand()
+
+#include "wsutil.h"
+#include "wsdevice.h"
+#include "wsconfig.h"
+#include "wslsa.h"
+
+#include <netlogon.h> // Mailslot message definitions
+#include <logonp.h> // NetpLogon routines
+
+//-------------------------------------------------------------------//
+// //
+// Local function prototypes //
+// //
+//-------------------------------------------------------------------//
+
+
+NET_API_STATUS NET_API_FUNCTION
+I_NetrLogonDomainNameAdd(
+ IN LPTSTR LogonDomainName
+ )
+/*++
+
+Routine Description:
+
+ This function asks the Datagram Receiver to add the specified
+ logon domain for the current user.
+
+Arguments:
+
+ LogonDomainName - Supplies the name of the logon domain to add.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ // terryk added this on 10-15-1993
+ // WsInitialLogon is never be called and So WsLogonDomainMutex is
+ // never initialize properly.
+ return ERROR_NOT_SUPPORTED;
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+I_NetrLogonDomainNameDel(
+ IN LPTSTR LogonDomainName
+ )
+/*++
+
+Routine Description:
+
+ This function asks the Datagram Receiver to delete the specified
+ logon domain for the current user.
+
+Arguments:
+
+ LogonDomainName - Supplies the name of the logon domain to
+ delete.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ // terryk added this on 10-15-1993
+ // WsInitialLogon is never be called and So WsLogonDomainMutex is
+ // never initialize properly.
+ return ERROR_NOT_SUPPORTED;
+}
+
diff --git a/private/net/svcdlls/wkssvc/server/wslsa.c b/private/net/svcdlls/wkssvc/server/wslsa.c
new file mode 100644
index 000000000..0be368e39
--- /dev/null
+++ b/private/net/svcdlls/wkssvc/server/wslsa.c
@@ -0,0 +1,349 @@
+/*++
+
+Copyright (c) 1991-1992 Microsoft Corporation
+
+Module Name:
+
+ wslsa.c
+
+Abstract:
+
+ This module contains the interfaces to the Local Security Authority
+ MS V 1.0 authentication package.
+
+Author:
+
+ Rita Wong (ritaw) 15-May-1991
+
+Revision History:
+
+--*/
+
+
+#include "wsutil.h"
+#include "wslsa.h"
+
+//-------------------------------------------------------------------//
+// //
+// Global variables //
+// //
+//-------------------------------------------------------------------//
+
+STATIC HANDLE LsaHandle = NULL;
+STATIC ULONG AuthPackageId = 0;
+
+
+NET_API_STATUS
+WsInitializeLsa(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This function registers the Workstation service as a logon process and
+ gets a handle to the MS V1.0 authentication package.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failing.
+
+--*/
+{
+
+ NTSTATUS ntstatus;
+
+ STRING InputString;
+ LSA_OPERATIONAL_MODE SecurityMode = 0;
+
+
+
+ //
+ // Register the Workstation service as a logon process
+ //
+ RtlInitString(&InputString, "LAN Manager Workstation Service");
+
+ ntstatus = LsaRegisterLogonProcess(
+ &InputString,
+ &LsaHandle,
+ &SecurityMode
+ );
+
+ IF_DEBUG(INFO) {
+ NetpKdPrint(("[Wksta] LsaRegisterLogonProcess returns x%08lx, "
+ "SecurityMode=x%08lx\n", ntstatus, SecurityMode));
+ }
+
+ if (! NT_SUCCESS(ntstatus)) {
+ return WsMapStatus(ntstatus);
+ }
+
+
+ //
+ // Look up the MS V1.0 authentication package
+ //
+ RtlInitString(&InputString,
+ "MICROSOFT_AUTHENTICATION_PACKAGE_V1_0");
+
+ ntstatus = LsaLookupAuthenticationPackage(
+ LsaHandle,
+ &InputString,
+ &AuthPackageId
+ );
+
+
+ if (! NT_SUCCESS(ntstatus)) {
+
+ IF_DEBUG(INFO) {
+ NetpKdPrint(("[Wksta] LsaLookupAuthenticationPackage returns x%08lx, "
+ "AuthPackageId=%lu\n", ntstatus, AuthPackageId));
+ }
+
+ }
+
+ return WsMapStatus(ntstatus);
+}
+
+
+VOID
+WsShutdownLsa(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This function deregisters the Workstation service as a logon process.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ (void) LsaDeregisterLogonProcess(
+ LsaHandle
+ );
+}
+
+
+NET_API_STATUS
+WsLsaEnumUsers(
+ OUT LPBYTE *EnumUsersResponse
+ )
+/*++
+
+Routine Description:
+
+ This function asks the MS V1.0 Authentication Package to list all users
+ who are physically logged on to the local computer.
+
+Arguments:
+
+ EnumUsersResponse - Returns a pointer to a list of user logon ids. This
+ memory is allocated by the authentication package and must be freed
+ with LsaFreeReturnBuffer when done with it.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NTSTATUS ntstatus;
+ NTSTATUS AuthPackageStatus;
+
+ MSV1_0_ENUMUSERS_REQUEST EnumUsersRequest;
+ ULONG EnumUsersResponseLength;
+
+
+ //
+ // Ask authentication package to enumerate users who are physically
+ // logged to the local machine.
+ //
+ EnumUsersRequest.MessageType = MsV1_0EnumerateUsers;
+
+ ntstatus = LsaCallAuthenticationPackage(
+ LsaHandle,
+ AuthPackageId,
+ &EnumUsersRequest,
+ sizeof(MSV1_0_ENUMUSERS_REQUEST),
+ (PVOID *)EnumUsersResponse,
+ &EnumUsersResponseLength,
+ &AuthPackageStatus
+ );
+
+ if (ntstatus == STATUS_SUCCESS) {
+ ntstatus = AuthPackageStatus;
+ }
+
+ if (ntstatus != STATUS_SUCCESS) {
+ return WsMapStatus(ntstatus);
+ }
+
+ return(NERR_Success);
+}
+
+
+NET_API_STATUS
+WsLsaGetUserInfo(
+ IN PLUID LogonId,
+ OUT LPBYTE *UserInfoResponse,
+ OUT LPDWORD UserInfoResponseLength
+ )
+/*++
+
+Routine Description:
+
+ This function asks the MS V1.0 Authentication Package for information on
+ a specific user.
+
+Arguments:
+
+ LogonId - Supplies the logon id of the user we want information about.
+
+ UserInfoResponse - Returns a pointer to a structure of information about
+ the user. This memory is allocated by the authentication package
+ and must be freed with LsaFreeReturnBuffer when done with it.
+
+ UserInfoResponseLength - Returns the length of the returned information
+ in number of bytes.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NTSTATUS ntstatus;
+ NTSTATUS AuthPackageStatus;
+
+ MSV1_0_GETUSERINFO_REQUEST UserInfoRequest;
+
+
+ //
+ // Ask authentication package for user information.
+ //
+ UserInfoRequest.MessageType = MsV1_0GetUserInfo;
+ RtlCopyLuid(&UserInfoRequest.LogonId, LogonId);
+
+ ntstatus = LsaCallAuthenticationPackage(
+ LsaHandle,
+ AuthPackageId,
+ &UserInfoRequest,
+ sizeof(MSV1_0_GETUSERINFO_REQUEST),
+ (PVOID *)UserInfoResponse,
+ UserInfoResponseLength,
+ &AuthPackageStatus
+ );
+
+ if (ntstatus == STATUS_SUCCESS) {
+ ntstatus = AuthPackageStatus;
+ }
+
+ if (ntstatus != STATUS_SUCCESS) {
+ return WsMapStatus(ntstatus);
+ }
+
+ return(NERR_Success);
+}
+
+
+NET_API_STATUS
+WsLsaRelogonUsers(
+ IN LPTSTR LogonServer
+ )
+/*++
+
+Routine Description:
+
+ This function asks the MS V1.0 Authentication Package to relogon users
+ that are logged on by the specified logon server. This is because the
+ server had been reset and need to restore the database of users logged
+ on by it before it went down.
+
+Arguments:
+
+ LogonServer - Name of logon server which requests that all its previously
+ logged on users be relogged on.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NTSTATUS ntstatus;
+ NTSTATUS AuthPackageStatus;
+
+ OEM_STRING AnsiLogonServerName;
+
+ PMSV1_0_RELOGON_REQUEST RelogonUsersRequest;
+ ULONG RelogonUsersRequestLength = sizeof(MSV1_0_RELOGON_REQUEST) +
+ (STRLEN(LogonServer) + 1) * sizeof(WCHAR);
+
+ //
+ // BUGBUG: Since we cannot yet use optional parameters in call to
+ // LsaCallAuthentication package, provide these variables for now.
+ //
+ PVOID RelogonUsersResponse;
+ ULONG ResponseLength;
+
+
+ //
+ // Allocate the relogon request package dynamically because the logon
+ // server name length is dynamic
+ //
+ if ((RelogonUsersRequest = (PMSV1_0_RELOGON_REQUEST)
+ LocalAlloc(
+ LMEM_ZEROINIT,
+ (UINT) RelogonUsersRequestLength
+ )) == NULL) {
+ return GetLastError();
+ }
+
+ RelogonUsersRequest->LogonServer.Buffer = (LPWSTR)
+ ((DWORD) RelogonUsersRequest) +
+ sizeof(MSV1_0_RELOGON_REQUEST);
+
+ RtlInitUnicodeString(&RelogonUsersRequest->LogonServer, LogonServer);
+
+ //
+ // Ask authentication package to relogon users for the specified
+ // logon server.
+ //
+ RelogonUsersRequest->MessageType = MsV1_0ReLogonUsers;
+
+ ntstatus = LsaCallAuthenticationPackage(
+ LsaHandle,
+ AuthPackageId,
+ &RelogonUsersRequest,
+ RelogonUsersRequestLength,
+ &RelogonUsersResponse, // should be NULL if OPTIONAL
+ &ResponseLength, // should be NULL if OPTIONAL
+ &AuthPackageStatus
+ );
+
+ //
+ // Free memory allocated for request package
+ //
+ (void) LocalFree(RelogonUsersRequest);
+
+ if (ntstatus == STATUS_SUCCESS) {
+ ntstatus = AuthPackageStatus;
+ }
+
+ if (ntstatus != STATUS_SUCCESS) {
+ return WsMapStatus(ntstatus);
+ }
+
+ return(NERR_Success);
+}
diff --git a/private/net/svcdlls/wkssvc/server/wslsa.h b/private/net/svcdlls/wkssvc/server/wslsa.h
new file mode 100644
index 000000000..5367e256e
--- /dev/null
+++ b/private/net/svcdlls/wkssvc/server/wslsa.h
@@ -0,0 +1,54 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ wswksta.h
+
+Abstract:
+
+ Private header file to be included by Workstation service modules
+ need information from the LSA authentication package.
+
+Author:
+
+ Rita Wong (ritaw) 15-May-1991
+
+Revision History:
+
+--*/
+
+#ifndef _WSLSA_INCLUDED_
+#define _WSLSA_INCLUDED_
+
+#include <ntmsv1_0.h>
+
+NET_API_STATUS
+WsInitializeLsa(
+ VOID
+ );
+
+VOID
+WsShutdownLsa(
+ VOID
+ );
+
+NET_API_STATUS
+WsLsaEnumUsers(
+ OUT LPBYTE *EnumUsersResponse
+ );
+
+NET_API_STATUS
+WsLsaGetUserInfo(
+ IN PLUID LogonId,
+ OUT LPBYTE *UserInfoResponse,
+ OUT LPDWORD UserInfoResponseLength
+ );
+
+NET_API_STATUS
+WsLsaRelogonUsers(
+ IN LPTSTR LogonServer
+ );
+
+#endif // _WSLSA_INCLUDED_
diff --git a/private/net/svcdlls/wkssvc/server/wsmain.c b/private/net/svcdlls/wkssvc/server/wsmain.c
new file mode 100644
index 000000000..12e9fce55
--- /dev/null
+++ b/private/net/svcdlls/wkssvc/server/wsmain.c
@@ -0,0 +1,1694 @@
+/*++
+
+Copyright (c) 1991-1992 Microsoft Corporation
+
+Module Name:
+
+ wsmain.c
+
+Abstract:
+
+ This is the main routine for the NT LAN Manager Workstation service.
+
+Author:
+
+ Rita Wong (ritaw) 06-May-1991
+
+Environment:
+
+ User Mode - Win32
+
+Revision History:
+
+ 15-May-1992 JohnRo
+ Implement registry watch.
+ 11-Jun-1992 JohnRo
+ Ifdef-out winreg notify stuff until we can fix logoff problem.
+ Added assertion checks on registry watch stuff.
+ 18-Oct-1993 terryk
+ Removed WsInitializeLogon stuff
+ 20-Oct-1993 terryk
+ Remove WsInitializeMessage stuff
+
+--*/
+
+#include "wsutil.h" // Common routines and data
+#include "wssec.h" // WkstaObjects create & destroy
+#include "wsdevice.h" // Device init & shutdown
+#include "wsuse.h" // UseStructures create & destroy
+#include "wsconfig.h" // Configuration loading
+#include "wslsa.h" // Lsa initialization
+#include "wsmsg.h" // Message send initialization
+#include "wswksta.h" // WsUpdateRedirToMatchWksta
+#include "wsmain.h" // Service related global definitions
+#include "wsdfs.h" // Dfs related routines
+
+#include <lmserver.h> // SV_TYPE_WORKSTATION
+#include <srvann.h> // I_ScSetServiceBits
+
+#include <services.h> // LMSVCS_ENTRY_POINT, LMSVCS_GLOBAL_DATA
+
+#include <configp.h> // Need NET_CONFIG_HANDLE typedef
+#include <confname.h> // NetpAllocConfigName().
+#include <prefix.h> // PREFIX_ equates.
+
+#ifdef WS_SET_TIME
+#include <lmremutl.h> // NetRemoteTOD
+#endif
+
+
+//-------------------------------------------------------------------//
+// //
+// String Definitions //
+// //
+//-------------------------------------------------------------------//
+
+#ifdef WS_SET_TIME
+#define CURRENT_CTRL_SET TEXT("system\\CurrentControlSet")
+#define WKSTA_KEY TEXT("Services\\LanmanWorkstation\\Parameters")
+#define SET_TIME_VALUE_NAME TEXT("SetTime")
+#endif
+
+//-------------------------------------------------------------------//
+// //
+// Structures
+// //
+//-------------------------------------------------------------------//
+typedef struct _REG_NOTIFY_INFO {
+ HANDLE NotifyEventHandle;
+ DWORD Timeout;
+ HANDLE WorkItemHandle;
+ HANDLE RegistryHandle;
+} REG_NOTIFY_INFO, *PREG_NOTIFY_INFO, *LPREG_NOTIFY_INFO;
+
+//-------------------------------------------------------------------//
+// //
+// Global variables //
+// //
+//-------------------------------------------------------------------//
+
+ WS_GLOBAL_DATA WsGlobalData;
+
+ PLMSVCS_GLOBAL_DATA WsLmsvcsGlobalData;
+
+ HANDLE WsDllRefHandle;
+
+ REG_NOTIFY_INFO RegNotifyInfo = {0};
+
+ HANDLE TerminateWorkItem = NULL;
+
+ CRITICAL_SECTION WsWorkerCriticalSection;
+
+ BOOL WsIsTerminating=FALSE;
+ DWORD WsNumWorkerThreads=0;
+
+// Used by the termination routine:
+
+ BOOL ConfigHandleOpened = FALSE;
+ HKEY ConfigHandle;
+ HANDLE RegistryChangeEvent = NULL;
+ LPTSTR RegPathToWatch = NULL;
+ DWORD WsInitState = 0;
+
+
+//-------------------------------------------------------------------//
+// //
+// Function prototypes //
+// //
+//-------------------------------------------------------------------//
+
+STATIC
+NET_API_STATUS
+WsInitializeWorkstation(
+ OUT LPDWORD WsInitState
+ );
+
+STATIC
+VOID
+WsShutdownWorkstation(
+ IN NET_API_STATUS ErrorCode,
+ IN DWORD WsInitState
+ );
+
+STATIC
+VOID
+WsHandleError(
+ IN WS_ERROR_CONDITION FailingCondition,
+ IN NET_API_STATUS Status,
+ IN DWORD WsInitState
+ );
+
+STATIC
+NET_API_STATUS
+WsCreateApiStructures(
+ IN OUT LPDWORD WsInitState
+ );
+
+STATIC
+VOID
+WsDestroyApiStructures(
+ IN DWORD WsInitState
+ );
+
+VOID
+WkstaControlHandler(
+ IN DWORD Opcode
+ );
+
+DWORD
+WsInitChangeNotify(
+ PVOID pData,
+ DWORD dwWaitStatus
+ );
+
+BOOL
+WsReInitChangeNotify(
+ PREG_NOTIFY_INFO pNotifyInfo
+ );
+
+DWORD
+WsRegistryNotify(
+ LPVOID pParms,
+ DWORD dwWaitStatus
+ );
+
+DWORD
+WsTerminationNotify(
+ LPVOID pParms,
+ DWORD dwWaitStatus
+ );
+
+
+#ifdef WS_SET_TIME
+STATIC
+VOID
+WsSetTime(
+ VOID
+ );
+
+STATIC
+DWORD
+WsFindTimeServer(
+ LPTSTR *pServerName
+ );
+
+STATIC
+BOOL
+WsShouldSetTime(
+ VOID
+ );
+#endif
+
+
+
+VOID
+LMSVCS_ENTRY_POINT( // (WORKSTATION_main)
+ DWORD NumArgs,
+ LPTSTR *ArgsArray,
+ PLMSVCS_GLOBAL_DATA LmsvcsGlobalData,
+ HANDLE SvcRefHandle
+ )
+/*++
+
+Routine Description:
+
+ This is the main routine of the Workstation Service which registers
+ itself as an RPC server and notifies the Service Controller of the
+ Workstation service control entry point.
+
+ After the workstation is started, this thread is used (since it's
+ otherwise unused) to watch for changes to the registry.
+
+Arguments:
+
+ NumArgs - Supplies the number of strings specified in ArgsArray.
+
+ ArgsArray - Supplies string arguments that are specified in the
+ StartService API call. This parameter is ignored by the
+ Workstation service.
+
+Return Value:
+
+ None.
+
+--*/
+{
+
+ NET_API_STATUS ApiStatus;
+
+
+ UNREFERENCED_PARAMETER(NumArgs);
+ UNREFERENCED_PARAMETER(ArgsArray);
+
+
+ WsDllRefHandle = SvcRefHandle;
+
+ //
+ // Save the LMSVCS global data for future use.
+ //
+ WsLmsvcsGlobalData = LmsvcsGlobalData;
+
+ InitializeCriticalSection(&WsWorkerCriticalSection);
+
+ //
+ // Initialize the workstation.
+ //
+ if (WsInitializeWorkstation(&WsInitState) != NERR_Success) {
+ return;
+ }
+
+ //
+ // Set up to wait for registry change or terminate event.
+ //
+ ApiStatus = NetpAllocConfigName(
+ SERVICES_ACTIVE_DATABASE,
+ SERVICE_WORKSTATION,
+ NULL, // default area ("Parameters")
+ &RegPathToWatch
+ );
+
+ if (ApiStatus != NERR_Success) {
+ goto Cleanup;
+ }
+
+ NetpAssert(RegPathToWatch != NULL && *RegPathToWatch != TCHAR_EOS);
+
+ ApiStatus = (NET_API_STATUS) RegOpenKeyEx(
+ HKEY_LOCAL_MACHINE, // hKey
+ RegPathToWatch, // lpSubKey
+ 0L, // ulOptions (reserved)
+ KEY_READ | KEY_NOTIFY, // desired access
+ &ConfigHandle // Newly Opened Key Handle
+ );
+ if (ApiStatus != NO_ERROR) {
+ goto Cleanup;
+ }
+
+ ConfigHandleOpened = TRUE;
+
+ RegistryChangeEvent = CreateEvent(
+ NULL, // no security descriptor
+ FALSE, // use automatic reset
+ FALSE, // initial state: not signalled
+ NULL // no name
+ );
+
+ if (RegistryChangeEvent == NULL) {
+ ApiStatus = (NET_API_STATUS) GetLastError();
+ goto Cleanup;
+ }
+
+ TerminateWorkItem = WsLmsvcsGlobalData->SvcsAddWorkItem (
+ WsGlobalData.TerminateNowEvent, // wait handle
+ WsTerminationNotify, // callback fcn
+ NULL, // parameter
+ SVC_QUEUE_WORK_ITEM, // flags
+ INFINITE, // timeout
+ WsDllRefHandle); // dll ref handle
+
+ //
+ // Setup to monitor registry changes.
+ //
+ RegNotifyInfo.NotifyEventHandle = RegistryChangeEvent;
+ RegNotifyInfo.Timeout = INFINITE;
+ RegNotifyInfo.WorkItemHandle = NULL;
+ RegNotifyInfo.RegistryHandle = ConfigHandle;
+
+
+ EnterCriticalSection(&WsWorkerCriticalSection);
+
+ if (!WsReInitChangeNotify(&RegNotifyInfo)) {
+ ApiStatus = GetLastError();
+ WsLmsvcsGlobalData->SvcsRemoveWorkItem(TerminateWorkItem);
+ LeaveCriticalSection(&WsWorkerCriticalSection);
+ goto Cleanup;
+ }
+
+ LeaveCriticalSection(&WsWorkerCriticalSection);
+
+ //
+ // This thread has done all that it can do. So we can return it
+ // to the service controller.
+ //
+ return;
+
+Cleanup:
+ WsTerminationNotify(NULL, NO_ERROR);
+ return;
+}
+
+
+DWORD
+WsInitChangeNotify(
+ PVOID pData,
+ DWORD dwWaitStatus
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+{
+ UNREFERENCED_PARAMETER(pData);
+ UNREFERENCED_PARAMETER(dwWaitStatus);
+
+ return((NET_API_STATUS) RegNotifyChangeKeyValue (
+ ConfigHandle,
+ TRUE, // watch a subtree
+ REG_NOTIFY_CHANGE_LAST_SET,
+ RegistryChangeEvent,
+ TRUE // async call
+ ));
+
+}
+
+BOOL
+WsReInitChangeNotify(
+ PREG_NOTIFY_INFO pNotifyInfo
+ )
+
+/*++
+
+Routine Description:
+
+
+ NOTE: This function should only be called when in the
+ WsWorkerCriticalSection.
+
+Arguments:
+
+
+Return Value:
+
+
+
+--*/
+{
+ BOOL bStat = TRUE;
+
+ pNotifyInfo->WorkItemHandle = WsLmsvcsGlobalData->SvcsAddWorkItem(
+ NULL,
+ WsInitChangeNotify,
+ (PVOID)pNotifyInfo,
+ SVC_IMMEDIATE_CALLBACK,
+ INFINITE,
+ NULL);
+
+ if (pNotifyInfo->WorkItemHandle == NULL) {
+ NetpKdPrint((PREFIX_WKSTA "Couldn't Initialize Registry Notify %d\n",GetLastError()));
+ bStat = FALSE;
+ goto CleanExit;
+ }
+ //
+ // Add the work item that is to be called when the
+ // RegistryChangeEvent is signalled.
+ //
+ pNotifyInfo->WorkItemHandle = WsLmsvcsGlobalData->SvcsAddWorkItem(
+ pNotifyInfo->NotifyEventHandle,
+ WsRegistryNotify,
+ (PVOID)pNotifyInfo,
+ SVC_QUEUE_WORK_ITEM,
+ pNotifyInfo->Timeout,
+ WsDllRefHandle);
+
+ if (pNotifyInfo->WorkItemHandle == NULL) {
+ NetpKdPrint((PREFIX_WKSTA "Couldn't add Reg Notify work item\n"));
+ bStat = FALSE;
+ }
+
+CleanExit:
+ if (bStat) {
+ if (WsNumWorkerThreads == 0) {
+ WsNumWorkerThreads++;
+ }
+ }
+ else {
+ if (WsNumWorkerThreads == 1) {
+ WsNumWorkerThreads--;
+ }
+ }
+
+ return(bStat);
+
+}
+
+DWORD
+WsRegistryNotify(
+ LPVOID pParms,
+ DWORD dwWaitStatus
+ )
+
+/*++
+
+Routine Description:
+
+ Handles Workstation Registry Notification. This function is called by a
+ Services Worker thread when the event used for registry notification is
+ signaled.
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+{
+ NET_API_STATUS ApiStatus;
+ PREG_NOTIFY_INFO pNotifyinfo=(PREG_NOTIFY_INFO)pParms;
+ NET_CONFIG_HANDLE NetConfigHandle;
+
+ UNREFERENCED_PARAMETER(dwWaitStatus);
+
+
+ EnterCriticalSection(&WsWorkerCriticalSection);
+ if (WsIsTerminating) {
+ WsNumWorkerThreads--;
+ SetEvent(WsGlobalData.TerminateNowEvent);
+
+ LeaveCriticalSection(&WsWorkerCriticalSection);
+ return(NO_ERROR);
+ }
+
+ //
+ // Serialize write access to config information
+ //
+ if (RtlAcquireResourceExclusive(&WsInfo.ConfigResource, TRUE)) {
+
+ //
+ // Update the redir fields based on change notify.
+ // WsUpdateWkstaToMatchRegistry expects a NET_CONFIG_HANDLE
+ // handle, so we conjure up one from the HKEY handle.
+ //
+ NetConfigHandle.WinRegKey = ConfigHandle;
+
+ WsUpdateWkstaToMatchRegistry(&NetConfigHandle, FALSE);
+
+ ApiStatus = WsUpdateRedirToMatchWksta(
+ PARMNUM_ALL,
+ NULL
+ );
+
+ NetpAssert( ApiStatus == NO_ERROR );
+
+ RtlReleaseResource(&WsInfo.ConfigResource);
+ }
+
+ if (!WsReInitChangeNotify(&RegNotifyInfo)) {
+ //
+ // If we can't add the work item, then we just won't
+ // listen for registry changes. There's not a whole
+ // lot we can do here.
+ //
+ ApiStatus = GetLastError();
+ }
+
+ LeaveCriticalSection(&WsWorkerCriticalSection);
+
+ return(NO_ERROR);
+}
+
+
+DWORD
+WsTerminationNotify(
+ LPVOID pParms,
+ DWORD dwWaitStatus
+ )
+
+/*++
+
+Routine Description:
+
+ This function gets called by a services worker thread when the
+ termination event gets signaled.
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+{
+ UNREFERENCED_PARAMETER(pParms);
+ UNREFERENCED_PARAMETER(dwWaitStatus);
+
+ IF_DEBUG(MAIN) {
+ NetpKdPrint((PREFIX_WKSTA "WORKSTATION_main: cleaning up, "
+ "api status.\n"));
+ }
+
+ EnterCriticalSection(&WsWorkerCriticalSection);
+ WsIsTerminating = TRUE;
+
+ //
+ // Must close winreg handle (which turns off notify) before event handle.
+ // Closing the regkey handle generates a change notify event!
+ //
+ if (ConfigHandleOpened) {
+ (VOID) RegCloseKey(ConfigHandle);
+#if DBG
+ //
+ // Workaround for a benign winreg assertion caused by us
+ // closing the RegistryChangeEvent handle which it wants
+ // to signal.
+ //
+ Sleep(2000);
+#endif
+ }
+ if (RegPathToWatch != NULL) {
+ (VOID) NetApiBufferFree(RegPathToWatch);
+ }
+ if ((RegistryChangeEvent != NULL) && (WsNumWorkerThreads != 0)) {
+
+ //
+ // There is still a RegistryNotify Work Item in the system, we
+ // will attempt to remove it by setting the event to wake it up.
+ //
+
+ ResetEvent(WsGlobalData.TerminateNowEvent);
+
+ LeaveCriticalSection(&WsWorkerCriticalSection);
+ SetEvent(RegistryChangeEvent);
+ //
+ // Wait until the WsRegistryNotify Thread is finished.
+ // We will give it 60 seconds. If the thread isn't
+ // finished in that time frame, we will go on anyway.
+ //
+ WaitForSingleObject(
+ WsGlobalData.TerminateNowEvent,
+ 60000);
+
+ if (WsNumWorkerThreads != 0) {
+ NetpKdPrint((PREFIX_WKSTA "WsTerminationNotify: "
+ "Registry Notification thread didn't terminate\n"));
+ }
+
+ EnterCriticalSection(&WsWorkerCriticalSection);
+ }
+ (VOID) CloseHandle(RegistryChangeEvent);
+
+ //
+ // Shutting down
+ //
+ // NOTE: We must synchronize with the RegistryNotification Thread.
+ //
+ WsShutdownWorkstation(
+ NERR_Success,
+ WsInitState
+ );
+
+ return(NO_ERROR);
+}
+
+STATIC
+NET_API_STATUS
+WsInitializeWorkstation(
+ OUT LPDWORD WsInitState
+ )
+/*++
+
+Routine Description:
+
+ This function initializes the Workstation service.
+
+Arguments:
+
+ WsInitState - Returns a flag to indicate how far we got with initializing
+ the Workstation service before an error occured.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NET_API_STATUS status;
+
+
+ //
+ // Initialize all the status fields so that subsequent calls to
+ // SetServiceStatus need to only update fields that changed.
+ //
+ WsGlobalData.Status.dwServiceType = SERVICE_WIN32;
+ WsGlobalData.Status.dwCurrentState = SERVICE_START_PENDING;
+ WsGlobalData.Status.dwControlsAccepted = 0;
+ WsGlobalData.Status.dwCheckPoint = 1;
+ WsGlobalData.Status.dwWaitHint = WS_WAIT_HINT_TIME;
+
+ SET_SERVICE_EXITCODE(
+ NO_ERROR,
+ WsGlobalData.Status.dwWin32ExitCode,
+ WsGlobalData.Status.dwServiceSpecificExitCode
+ );
+
+ //
+ // Initialize workstation to receive service requests by registering the
+ // control handler.
+ //
+ if ((WsGlobalData.StatusHandle = RegisterServiceCtrlHandler(
+ SERVICE_WORKSTATION,
+ WkstaControlHandler
+ )) == (SERVICE_STATUS_HANDLE) NULL) {
+
+ status = GetLastError();
+ WS_HANDLE_ERROR(WsErrorRegisterControlHandler);
+ return status;
+ }
+
+ //
+ // Create an event which is used by the service control handler to notify
+ // the Workstation service that it is time to terminate.
+ //
+ if ((WsGlobalData.TerminateNowEvent =
+ CreateEvent(
+ NULL, // Event attributes
+ TRUE, // Event must be manually reset
+ FALSE,
+ NULL // Initial state not signalled
+ )) == NULL) {
+
+ status = GetLastError();
+ WS_HANDLE_ERROR(WsErrorCreateTerminateEvent);
+ return status;
+ }
+ (*WsInitState) |= WS_TERMINATE_EVENT_CREATED;
+
+ //
+ // Notify the Service Controller for the first time that we are alive
+ // and we are start pending
+ //
+ if ((status = WsUpdateStatus()) != NERR_Success) {
+
+ WS_HANDLE_ERROR(WsErrorNotifyServiceController);
+ return status;
+ }
+
+ //
+ // Initialize the workstation as a logon process with LSA, and
+ // get the MS V 1.0 authentication package ID.
+ //
+ if ((status = WsInitializeLsa()) != NERR_Success) {
+
+ WS_HANDLE_ERROR(WsErrorInitLsa);
+ return status;
+ }
+ (*WsInitState) |= WS_LSA_INITIALIZED;
+
+ //
+ // Read the configuration information to initialize the redirector and
+ // datagram receiver
+ //
+ if ((status = WsInitializeRedirector()) != NERR_Success) {
+
+ WS_HANDLE_ERROR(WsErrorStartRedirector);
+ return status;
+ }
+ (*WsInitState) |= WS_DEVICES_INITIALIZED;
+
+ //
+ // Service install still pending. Update checkpoint counter and the
+ // status with the Service Controller.
+ //
+ (WsGlobalData.Status.dwCheckPoint)++;
+ (void) WsUpdateStatus();
+
+ //
+ // Bind to transports
+ //
+ if ((status = WsBindToTransports()) != NERR_Success) {
+
+ WS_HANDLE_ERROR(WsErrorBindTransport);
+ return status;
+ }
+
+ //
+ // Service install still pending. Update checkpoint counter and the
+ // status with the Service Controller.
+ //
+ (WsGlobalData.Status.dwCheckPoint)++;
+ (void) WsUpdateStatus();
+
+ //
+ // Add domain names.
+ //
+ if ((status = WsAddDomains()) != NERR_Success) {
+
+ WS_HANDLE_ERROR(WsErrorAddDomains);
+ return status;
+ }
+
+ //
+ // Service start still pending. Update checkpoint counter and the
+ // status with the Service Controller.
+ //
+ (WsGlobalData.Status.dwCheckPoint)++;
+ (void) WsUpdateStatus();
+
+ //
+ // Create Workstation service API data structures
+ //
+ if ((status = WsCreateApiStructures(WsInitState)) != NERR_Success) {
+
+ WS_HANDLE_ERROR(WsErrorCreateApiStructures);
+ return status;
+ }
+
+ //
+ // Initialize the workstation service to receive RPC requests
+ //
+ // NOTE: Now all RPC servers in services.exe share the same pipe name.
+ // However, in order to support communication with version 1.0 of WinNt,
+ // it is necessary for the Client Pipe name to remain the same as
+ // it was in version 1.0. Mapping to the new name is performed in
+ // the Named Pipe File System code.
+ //
+ if ((status = WsLmsvcsGlobalData->StartRpcServer(
+ WsLmsvcsGlobalData->SvcsRpcPipeName,
+ wkssvc_ServerIfHandle
+ )) != NERR_Success) {
+
+ WS_HANDLE_ERROR(WsErrorStartRpcServer);
+ return status;
+ }
+
+ (*WsInitState) |= WS_RPC_SERVER_STARTED;
+
+ //
+ // Lastly, we create a thread to communicate with the
+ // Dfs-enabled MUP driver.
+ //
+
+ if ((status = WsInitializeDfs()) != NERR_Success) {
+ WS_HANDLE_ERROR(WsErrorStartRedirector);
+ return status;
+ }
+
+ (*WsInitState) |= WS_DFS_THREAD_STARTED;
+
+ (void) I_ScSetServiceBits(
+ WsGlobalData.StatusHandle,
+ SV_TYPE_WORKSTATION,
+ TRUE,
+ TRUE,
+ NULL
+ );
+
+
+ //
+ // We are done with starting the Workstation service. Tell Service
+ // Controller our new status.
+ //
+ WsGlobalData.Status.dwCurrentState = SERVICE_RUNNING;
+ WsGlobalData.Status.dwControlsAccepted = SERVICE_ACCEPT_STOP |
+ SERVICE_ACCEPT_PAUSE_CONTINUE |
+ SERVICE_ACCEPT_SHUTDOWN;
+ WsGlobalData.Status.dwCheckPoint = 0;
+ WsGlobalData.Status.dwWaitHint = 0;
+
+ if ((status = WsUpdateStatus()) != NERR_Success) {
+
+ WS_HANDLE_ERROR(WsErrorNotifyServiceController);
+ return status;
+ }
+
+#ifdef WS_SET_TIME
+ //
+ // Set the Time
+ //
+ WsSetTime();
+#endif
+
+
+ IF_DEBUG(MAIN) {
+ NetpKdPrint(("[Wksta] Successful Initialization\n"));
+ }
+
+ return NERR_Success;
+}
+
+
+
+VOID
+WsShutdownWorkstation(
+ IN NET_API_STATUS ErrorCode,
+ IN DWORD WsInitState
+ )
+/*++
+
+Routine Description:
+
+ This function shuts down the Workstation service.
+
+Arguments:
+
+ ErrorCode - Supplies the error code of the failure
+
+ WsInitState - Supplies a flag to indicate how far we got with initializing
+ the Workstation service before an error occured, thus the amount of
+ clean up needed.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ NET_API_STATUS status = NERR_Success;
+
+
+ //
+ // Service stop still pending. Update checkpoint counter and the
+ // status with the Service Controller.
+ //
+ (WsGlobalData.Status.dwCheckPoint)++;
+ (void) WsUpdateStatus();
+
+ if (WsInitState & WS_DFS_THREAD_STARTED) {
+ //
+ // Stop the Dfs thread
+ //
+ WsShutdownDfs();
+ }
+
+ if (WsInitState & WS_RPC_SERVER_STARTED) {
+ //
+ // Stop the RPC server
+ //
+ WsLmsvcsGlobalData->StopRpcServer(wkssvc_ServerIfHandle);
+ }
+
+ if (WsInitState & WS_API_STRUCTURES_CREATED) {
+ //
+ // Destroy data structures created for Workstation APIs
+ //
+ WsDestroyApiStructures(WsInitState);
+ }
+
+ WsShutdownMessageSend();
+
+ //
+ // Don't need to ask redirector to unbind from its transports when
+ // cleaning up because the redirector will tear down the bindings when
+ // it stops.
+ //
+
+ if (WsInitState & WS_DEVICES_INITIALIZED) {
+ //
+ // Shut down the redirector and datagram receiver
+ //
+ status = WsShutdownRedirector();
+ }
+
+ if (WsInitState & WS_LSA_INITIALIZED) {
+ //
+ // Deregister workstation as logon process
+ //
+ WsShutdownLsa();
+ }
+
+ if (WsInitState & WS_TERMINATE_EVENT_CREATED) {
+ //
+ // Close handle to termination event
+ //
+ CloseHandle(WsGlobalData.TerminateNowEvent);
+ }
+
+ I_ScSetServiceBits(
+ WsGlobalData.StatusHandle,
+ SV_TYPE_WORKSTATION,
+ FALSE,
+ TRUE,
+ NULL
+ );
+
+ //
+ // We are done with cleaning up. Tell Service Controller that we are
+ // stopped.
+ //
+ WsGlobalData.Status.dwCurrentState = SERVICE_STOPPED;
+ WsGlobalData.Status.dwControlsAccepted = 0;
+
+
+ if ((ErrorCode == NERR_Success) &&
+ (status == ERROR_REDIRECTOR_HAS_OPEN_HANDLES)) {
+ ErrorCode = status;
+ }
+
+ SET_SERVICE_EXITCODE(
+ ErrorCode,
+ WsGlobalData.Status.dwWin32ExitCode,
+ WsGlobalData.Status.dwServiceSpecificExitCode
+ );
+
+ WsGlobalData.Status.dwCheckPoint = 0;
+ WsGlobalData.Status.dwWaitHint = 0;
+
+ (void) WsUpdateStatus();
+}
+
+
+STATIC
+VOID
+WsHandleError(
+ IN WS_ERROR_CONDITION FailingCondition,
+ IN NET_API_STATUS Status,
+ IN DWORD WsInitState
+ )
+/*++
+
+Routine Description:
+
+ This function handles a Workstation service error condition. If the error
+ condition is fatal, it shuts down the Workstation service.
+
+Arguments:
+
+ FailingCondition - Supplies a value which indicates what the failure is.
+
+ Status - Supplies the status code for the failure.
+
+ WsInitState - Supplies a flag to indicate how far we got with initializing
+ the Workstation service before an error occured, thus the amount of
+ clean up needed.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ switch (FailingCondition) {
+
+ case WsErrorRegisterControlHandler:
+
+ NetpKdPrint(("Workstation cannot register control handler "
+ FORMAT_API_STATUS "\n", Status));
+
+ WS_SHUTDOWN_WORKSTATION(Status);
+
+ break;
+
+ case WsErrorCreateTerminateEvent:
+
+ NetpKdPrint(("[Wksta] Cannot create done event "
+ FORMAT_API_STATUS "\n", Status));
+
+ WS_SHUTDOWN_WORKSTATION(Status);
+
+ break;
+
+ case WsErrorNotifyServiceController:
+
+ NetpKdPrint(("[Wksta] SetServiceStatus error "
+ FORMAT_API_STATUS "\n", Status));
+
+ WS_SHUTDOWN_WORKSTATION(Status);
+
+ break;
+
+ case WsErrorInitLsa:
+
+ NetpKdPrint(("[Wksta] LSA initialization error "
+ FORMAT_API_STATUS "\n", Status));
+
+ WS_SHUTDOWN_WORKSTATION(Status);
+
+ break;
+
+ case WsErrorStartRedirector:
+
+ NetpKdPrint(("[Wksta] Cannot start redirector "
+ FORMAT_API_STATUS "\n", Status));
+
+ WS_SHUTDOWN_WORKSTATION(Status);
+
+ break;
+
+ case WsErrorBindTransport:
+
+ if (Status == NERR_ItemNotFound) {
+ NetpKdPrint(("[Wksta] Did not bind to any transport driver\n"));
+ }
+
+ WS_SHUTDOWN_WORKSTATION(Status);
+
+ break;
+
+ case WsErrorAddDomains:
+
+ NetpKdPrint(("[Wksta] Could not add domain names "
+ FORMAT_API_STATUS "\n", Status));
+
+ WS_SHUTDOWN_WORKSTATION(Status);
+
+ break;
+
+ case WsErrorStartRpcServer:
+
+ NetpKdPrint(("[Wksta] Cannot start RPC server "
+ FORMAT_API_STATUS "\n", Status));
+
+ WS_SHUTDOWN_WORKSTATION(Status);
+
+ break;
+
+ case WsErrorCreateApiStructures:
+
+ NetpKdPrint(("[Wksta] Error in creating API structures "
+ FORMAT_API_STATUS "\n", Status));
+
+ WS_SHUTDOWN_WORKSTATION(Status);
+
+ break;
+
+ default:
+ NetpKdPrint(("[Wksta] WsHandleError: unknown error condition %lu\n",
+ FailingCondition));
+
+ NetpAssert(FALSE);
+ }
+
+}
+
+
+NET_API_STATUS
+WsUpdateStatus(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This function updates the Workstation service status with the Service
+ Controller.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NET_API_STATUS status = NERR_Success;
+
+ if (WsGlobalData.StatusHandle == (SERVICE_STATUS_HANDLE) NULL) {
+ NetpKdPrint((
+ "[Wksta] Cannot call SetServiceStatus, no status handle.\n"
+ ));
+
+ return ERROR_INVALID_HANDLE;
+ }
+
+ if (! SetServiceStatus(WsGlobalData.StatusHandle, &WsGlobalData.Status)) {
+
+ status = GetLastError();
+
+ IF_DEBUG(MAIN) {
+ NetpKdPrint(("[Wksta] SetServiceStatus error %lu\n", status));
+ }
+ }
+
+ return status;
+}
+
+
+
+STATIC
+NET_API_STATUS
+WsCreateApiStructures(
+ IN OUT LPDWORD WsInitState
+ )
+/*++
+
+Routine Description:
+
+ This function creates and initializes all the data structures required
+ for the Workstation APIs.
+
+Arguments:
+
+ WsInitState - Returns the supplied flag of how far we got in the
+ Workstation service initialization process.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NET_API_STATUS status;
+
+
+ //
+ // Create workstation security objects
+ //
+ if ((status = WsCreateWkstaObjects()) != NERR_Success) {
+ return status;
+ }
+ (*WsInitState) |= WS_SECURITY_OBJECTS_CREATED;
+
+ //
+ // Create Use Table
+ //
+ if ((status = WsInitUseStructures()) != NERR_Success) {
+ return status;
+ }
+ (*WsInitState) |= WS_USE_TABLE_CREATED;
+
+ return NERR_Success;
+}
+
+
+
+STATIC
+VOID
+WsDestroyApiStructures(
+ IN DWORD WsInitState
+ )
+/*++
+
+Routine Description:
+
+ This function destroys the data structures created for the Workstation
+ APIs.
+
+Arguments:
+
+ WsInitState - Supplies a flag which tells us what API structures
+ were created in the initialization process and now have to be
+ cleaned up.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ if (WsInitState & WS_USE_TABLE_CREATED) {
+ //
+ // Destroy Use Table
+ //
+ WsDestroyUseStructures();
+ }
+
+ if (WsInitState & WS_SECURITY_OBJECTS_CREATED) {
+ //
+ // Destroy workstation security objects
+ //
+ WsDestroyWkstaObjects();
+ }
+}
+
+
+VOID
+WkstaControlHandler(
+ IN DWORD Opcode
+ )
+/*++
+
+Routine Description:
+
+ This is the service control handler of the Workstation service.
+
+Arguments:
+
+ Opcode - Supplies a value which specifies the action for the Workstation
+ service to perform.
+
+ Arg - Supplies a value which tells a service specifically what to do
+ for an operation specified by Opcode.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ IF_DEBUG(MAIN) {
+ NetpKdPrint(("[Wksta] In Control Handler\n"));
+ }
+
+ switch (Opcode) {
+
+ case SERVICE_CONTROL_PAUSE:
+
+ //
+ // Pause redirection of print and comm devices
+ //
+ WsPauseOrContinueRedirection(
+ PauseRedirection
+ );
+
+ break;
+
+ case SERVICE_CONTROL_CONTINUE:
+
+ //
+ // Resume redirection of print and comm devices
+ //
+ WsPauseOrContinueRedirection(
+ ContinueRedirection
+ );
+
+ break;
+
+ case SERVICE_CONTROL_STOP:
+
+ if (WsGlobalData.Status.dwCurrentState != SERVICE_STOP_PENDING) {
+
+ IF_DEBUG(MAIN) {
+ NetpKdPrint(("[Wksta] Stopping workstation...\n"));
+ }
+
+ WsGlobalData.Status.dwCurrentState = SERVICE_STOP_PENDING;
+ WsGlobalData.Status.dwCheckPoint = 1;
+ WsGlobalData.Status.dwWaitHint = WS_WAIT_HINT_TIME;
+
+ //
+ // Send the status response.
+ //
+ (void) WsUpdateStatus();
+
+ if (! SetEvent(WsGlobalData.TerminateNowEvent)) {
+
+ //
+ // Problem with setting event to terminate Workstation
+ // service.
+ //
+ NetpKdPrint(("[Wksta] Error setting TerminateNowEvent "
+ FORMAT_API_STATUS "\n", GetLastError()));
+ NetpAssert(FALSE);
+ }
+
+ return;
+ }
+ break;
+
+ case SERVICE_CONTROL_SHUTDOWN:
+ {
+ if (LoadedMRxSmbInsteadOfRdr) {
+ NET_API_STATUS status;
+ HKEY hRedirectorKey;
+ DWORD FinalStatus;
+
+ status = RegOpenKeyEx(
+ HKEY_LOCAL_MACHINE,
+ MRXSMB_REGISTRY_KEY,
+ 0,
+ KEY_ALL_ACCESS,
+ &hRedirectorKey);
+
+ if (status == ERROR_SUCCESS) {
+ DWORD ValueType;
+ DWORD ValueSize;
+ // if this is a controlled shutdown reload the new redirector on
+ // restart if it is running currently.
+
+ ValueSize = sizeof(FinalStatus);
+ status = RegQueryValueEx(
+ hRedirectorKey,
+ LAST_LOAD_STATUS,
+ NULL,
+ &ValueType,
+ (PCHAR)&FinalStatus,
+ &ValueSize);
+
+
+ // If the query value was successful and the value is the same
+ // as the original value written during load time, ensure that
+ // the registry is updated for loading the new RDR.
+ if ((status == ERROR_SUCCESS) &&
+ (FinalStatus == ERROR_NOT_READY)) {
+ FinalStatus = ERROR_SUCCESS;
+ status = RegSetValueEx(
+ hRedirectorKey,
+ LAST_LOAD_STATUS,
+ 0,
+ REG_DWORD,
+ (PCHAR)&FinalStatus,
+ sizeof(DWORD));
+ }
+
+ if (status == ERROR_SUCCESS) {
+ NetpKdPrint((PREFIX_WKSTA "New RDR will be loaded on restart\n"));
+ }
+
+ RegCloseKey(hRedirectorKey);
+ }
+ }
+ }
+ break;
+ case SERVICE_CONTROL_INTERROGATE:
+ break;
+
+ default:
+ IF_DEBUG(MAIN) {
+ NetpKdPrint(("Unknown workstation opcode " FORMAT_HEX_DWORD
+ "\n", Opcode));
+ }
+ }
+
+ //
+ // Send the status response.
+ //
+ (void) WsUpdateStatus();
+}
+
+#ifdef WS_SET_TIME
+
+STATIC
+VOID
+WsSetTime(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ This function sets the time on the local machine.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ NTSTATUS ntStatus;
+ NET_API_STATUS status;
+ PTIME_OF_DAY_INFO pTod;
+ LPTSTR pServerName;
+ LARGE_INTEGER systemTime;
+ LARGE_INTEGER previousTime;
+ ULONG privileges[1];
+
+ //
+ // Look in registry to see if we are to set the time.
+ //
+ if (!WsShouldSetTime()) {
+ IF_DEBUG(MAIN) {
+ NetpKdPrint(("[Wksta] Time update is NOT requested\n",
+ status));
+ }
+ return;
+ }
+
+ //
+ // Find a server to get the time from.
+ //
+ status = WsFindTimeServer(&pServerName);
+ if (status != NERR_Success) {
+ IF_DEBUG(MAIN) {
+ NetpKdPrint(("[Wksta] WsFindTimeServer Failed "FORMAT_API_STATUS" \n",
+ status));
+ }
+ return;
+ }
+
+ //
+ // Get the time
+ //
+ status = NetRemoteTOD(
+ pServerName,
+ (LPBYTE *)pTod
+ );
+
+ if (status != NERR_Success) {
+ IF_DEBUG(MAIN) {
+ NetpKdPrint(("[Wksta] NetRemoteTOD Failed "FORMAT_API_STATUS" \n",
+ status));
+ }
+ NetApiBufferFree(pServerName);
+ return;
+ }
+
+ NetApiBufferFree(pServerName);
+
+ //
+ // Convert the time to NT time.
+ //
+ RtlSecondsSince1970ToTime(
+ pTod->tod_elapsedt, // ULONG
+ &systemTime // PLARGE_INTEGER
+ );
+
+ //
+ // Set the NT system time. (first get privilege)
+ //
+
+ privileges[0] = SE_SYSTEMTIME_PRIVILEGE;
+ status = NetpGetPrivilege(1,privileges);
+ if (status != NO_ERROR) {
+ IF_DEBUG(MAIN) {
+ NetpKdPrint(("[Wksta] NetpGetPrivilege Failed "FORMAT_DWORD" \n",
+ status));
+ }
+ NetApiBufferFree(pServerName);
+ return;
+ }
+
+ ntStatus = NtSetSystemTime(
+ &systemTime, // IN PLARGE_INTEGER
+ &previousTime // OUT PLARGE_INTEGER
+ );
+
+ (VOID)NetpReleasePrivilege();
+
+ if (!NT_SUCCESS(ntStatus)) {
+ IF_DEBUG(MAIN) {
+ NetpKdPrint(("[Wksta] NtSetSystemTime Failed "FORMAT_NTSTATUS" \n",
+ ntStatus));
+ }
+ }
+
+ return;
+}
+
+
+STATIC
+DWORD
+WsFindTimeServer(
+ LPTSTR *pServerName
+ )
+
+/*++
+
+Routine Description:
+
+ This function finds the server name for a TimeSource server.
+
+ This function allocates storage for the name whose pointer is stored in
+ pServerName.
+
+Arguments:
+
+ pServerName - This is a pointer to a location where the pointer to
+ the name of the TimeSource Server is to placed.
+
+Return Value:
+
+ NERR_Success - If the operation was completely successful.
+
+ assorted errors - if the operation failed in any way.
+
+--*/
+
+#define SERVER_INFO_BUF_SIZE 512
+
+{
+
+ NET_API_STATUS status;
+ DWORD entriesRead;
+ DWORD totalEntries;
+ DWORD resumeHandle;
+ LPSERVER_INFO_100 pServerInfo = NULL;
+ LPTSTR pDomainName;
+
+
+ //
+ // Get the name of our domain.
+ //
+ status = NetpGetDomainName(&pDomainName);
+ if (status != NERR_Success) {
+ return(status);
+ }
+
+ //
+ // Get a short enumerated list of the timesource servers out there.
+ //
+
+ status = NetServerEnum (
+ NULL, // Local Server
+ 100,
+ (LPBYTE *)&pServerInfo,
+ SERVER_INFO_BUF_SIZE,
+ &entriesRead,
+ &totalEntries,
+ SV_TYPE_TIME_SOURCE,
+ pDomainName, // PrimaryDomain
+ &resumeHandle
+ );
+
+ NetApiBufferFree(pDomainName);
+
+ if (status != NERR_Success) {
+ return(status);
+ }
+
+ if (entriesRead == 0) {
+ IF_DEBUG(MAIN) {
+ NetpKdPrint(("[Wksta]FindTimeServer: No TimeSource Servers "
+ "in Domain\n"));
+ }
+ if (pServerInfo != NULL) {
+ NetApiBufferFree(pServerInfo);
+ }
+ return(ERROR_GEN_FAILURE);
+ }
+ //
+ // Allocate storage and copy the Time Source Server name there
+ //
+ *pServerName = (LPTSTR) LocalAlloc(
+ LMEM_ZEROINIT,
+ (UINT) STRSIZE(pServerInfo->sv100_name)
+ );
+
+ if (*pServerName == NULL) {
+ NetApiBufferFree(pServerInfo);
+ return(GetLastError());
+ }
+ STRCPY(*pServerName, pServerInfo->sv100_name);
+
+ NetApiBufferFree(pServerInfo);
+
+ return(NERR_Success);
+}
+
+
+STATIC
+BOOL
+WsShouldSetTime(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ This function looks in the registry to determine if the workstation
+ service is to go out and find the time so it can set the time on
+ this machine.
+
+Arguments:
+
+ none
+
+Return Value:
+
+ TRUE - The Workstation should set the time.
+
+ FALSE - The Workstation should not set the time.
+
+--*/
+
+{
+
+ HKEY systemKey;
+ HKEY wkstaKey;
+ DWORD status;
+ DWORD setTimeFlag;
+ DWORD bufferSize;
+
+ //
+ // Get the key for the current system control set.
+ //
+
+ status = RegOpenKeyEx(
+ HKEY_LOCAL_MACHINE, // hKey
+ CURRENT_CTRL_SET, // lpSubKey
+ 0L, // ulOptions (reserved)
+ KEY_READ, // desired access
+ &systemKey); // Newly Opened Key Handle
+
+ if (status != NO_ERROR) {
+ IF_DEBUG(MAIN) {
+ NetpKdPrint(("[Wksta] RegOpenKeyEx (system key) failed "
+ "FORMAT_API_STATUS" "\n",status));
+ }
+ return (FALSE);
+ }
+
+ //
+ // Get the Workstation Service Key
+ //
+ status = RegOpenKeyEx(
+ systemKey, // hKey
+ WKSTA_KEY, // lpSubKey
+ 0L, // ulOptions (reserved)
+ KEY_READ, // desired access
+ &wkstaKey // Newly Opened Key Handle
+ );
+
+ if (status != NO_ERROR) {
+ IF_DEBUG(MAIN) {
+ NetpKdPrint(("[Wksta] RegOpenKeyEx (wksta key) failed "
+ "FORMAT_API_STATUS" "\n",status));
+ }
+ RegCloseKey(systemKey);
+ return (FALSE);
+ }
+
+
+ //
+ // Read the SetTime Value. This is a DWORD sized object that is
+ // expected to be non-zero if we are to read the time, or zero
+ // if we are not.
+ //
+ bufferSize = sizeof(DWORD);
+ status = RegQueryValueEx (
+ wkstaKey, // hKey
+ SET_TIME_VALUE_NAME, // lpValueName
+ NULL, // lpTitleIndex
+ NULL, // lpType
+ (LPBYTE)&setTimeFlag, // lpData
+ &bufferSize // lpcbData
+ );
+
+ RegCloseKey(systemKey);
+ RegCloseKey(wkstaKey);
+
+ if (status != NO_ERROR) {
+ IF_DEBUG(MAIN) {
+ NetpKdPrint(("[Wksta] RegQueryValueEx(SetTimeValue) failed "
+ FORMAT_API_STATUS "\n",status));
+ }
+ return(FALSE);
+ }
+
+ //
+ // BUGBUG: The Set Time feature is currently disabled until I can
+ // test it completely. I wanted to check in the code in order to
+ // minimize merge problems.
+ //
+ return(FALSE);
+// return(setTimeFlag);
+
+}
+#endif
+
diff --git a/private/net/svcdlls/wkssvc/server/wsmain.h b/private/net/svcdlls/wkssvc/server/wsmain.h
new file mode 100644
index 000000000..5aebb307d
--- /dev/null
+++ b/private/net/svcdlls/wkssvc/server/wsmain.h
@@ -0,0 +1,135 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ wsmain.h
+
+Abstract:
+
+ Private header file which defines the global data which is used for
+ communication between the service control handler and the
+ rest of the NT Workstation service.
+
+Author:
+
+ Rita Wong (ritaw) 06-May-1991
+
+Revision History:
+ terryk 10-18-1993 Remove WsErrorInitializeLogon
+
+--*/
+
+#ifndef _WSMAIN_INCLUDED_
+#define _WSMAIN_INCLUDED_
+
+#include <wsnames.h> // Service interface names
+
+#include <services.h> // LMSVCS_GLOBAL_DATA
+
+
+//
+// Time for the sender of a start or stop request to the Workstation
+// service to wait (in milliseconds) before checking on the
+// Workstation service again to see if it is done.
+//
+#define WS_WAIT_HINT_TIME 90000 // 90 seconds
+
+//
+// Defines to indicate how far we managed to initialize the Workstation
+// service before an error is encountered and the extent of clean up needed
+//
+
+#define WS_TERMINATE_EVENT_CREATED 0x00000001
+#define WS_DEVICES_INITIALIZED 0x00000002
+#define WS_MESSAGE_SEND_INITIALIZED 0x00000004
+#define WS_RPC_SERVER_STARTED 0x00000008
+#define WS_LOGON_INITIALIZED 0x00000010
+#define WS_LSA_INITIALIZED 0x00000020
+#define WS_DFS_THREAD_STARTED 0x00000040
+
+#define WS_SECURITY_OBJECTS_CREATED 0x10000000
+#define WS_USE_TABLE_CREATED 0x20000000
+
+#define WS_API_STRUCTURES_CREATED (WS_SECURITY_OBJECTS_CREATED | \
+ WS_USE_TABLE_CREATED )
+
+//
+// This macro is called after the redirection of print or comm device
+// has been paused or continued. If either the print or comm device is
+// paused the service is considered paused.
+//
+#define WS_RESET_PAUSE_STATE(WsStatus) { \
+ WsStatus &= ~(SERVICE_PAUSE_STATE); \
+ WsStatus |= (WsStatus & SERVICE_REDIR_PAUSED) ? SERVICE_PAUSED : \
+ SERVICE_ACTIVE; \
+ }
+
+
+
+//
+// Call WsHandleError with the appropriate error condition
+//
+#define WS_HANDLE_ERROR(ErrorCondition) \
+ WsHandleError( \
+ ErrorCondition, \
+ status, \
+ *WsInitState \
+ );
+
+//
+// Call WsShutdownWorkstation with the exit code
+//
+#define WS_SHUTDOWN_WORKSTATION(ErrorCode) \
+ WsShutdownWorkstation( \
+ ErrorCode, \
+ WsInitState \
+ );
+
+
+//-------------------------------------------------------------------//
+// //
+// Type definitions //
+// //
+//-------------------------------------------------------------------//
+
+typedef enum _WS_ERROR_CONDITION {
+ WsErrorRegisterControlHandler = 0,
+ WsErrorCreateTerminateEvent,
+ WsErrorNotifyServiceController,
+ WsErrorInitLsa,
+ WsErrorStartRedirector,
+ WsErrorBindTransport,
+ WsErrorAddDomains,
+ WsErrorStartRpcServer,
+ WsErrorInitMessageSend,
+ WsErrorCreateApiStructures
+} WS_ERROR_CONDITION, *PWS_ERROR_CONDITION;
+
+typedef struct _WS_GLOBAL_DATA {
+
+ //
+ // Workstation service status
+ //
+ SERVICE_STATUS Status;
+
+ //
+ // Service status handle
+ //
+ SERVICE_STATUS_HANDLE StatusHandle;
+
+ //
+ // When the control handler is asked to stop the Workstation service,
+ // it signals this event to notify all threads of the Workstation
+ // service to terminate.
+ //
+ HANDLE TerminateNowEvent;
+
+} WS_GLOBAL_DATA, *PWS_GLOBAL_DATA;
+
+extern WS_GLOBAL_DATA WsGlobalData;
+
+extern PLMSVCS_GLOBAL_DATA WsLmsvcsGlobalData;
+
+#endif // ifndef _WSMAIN_INCLUDED_
diff --git a/private/net/svcdlls/wkssvc/server/wsmsg.h b/private/net/svcdlls/wkssvc/server/wsmsg.h
new file mode 100644
index 000000000..6730284a6
--- /dev/null
+++ b/private/net/svcdlls/wkssvc/server/wsmsg.h
@@ -0,0 +1,116 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ wsmsg.h
+
+Abstract:
+
+ Private header file to be included by Workstation service modules that
+ implement the NetMessageBufferSend API.
+
+Author:
+
+ Rita Wong (ritaw) 25-July-1991
+
+Revision History:
+
+--*/
+
+#ifndef _WSMSG_INCLUDED_
+#define _WSMSG_INCLUDED_
+
+#include <lmmsg.h> // LAN Man Message API definitions
+#include <nb30.h> // NetBIOS 3.0 definitions
+
+#include <smbtypes.h> // Type definitions needed by smb.h
+#include <smb.h> // SMB structures
+
+#include <msgrutil.h> // Netlib helpers for message send
+
+#define MAX_GROUP_MESSAGE_SIZE 128
+#define WS_SMB_BUFFER_SIZE 200
+
+#define MESSENGER_MAILSLOT_W L"\\MAILSLOT\\MESSNGR"
+
+typedef struct _WSNETWORKS {
+ LANA_ENUM LanAdapterNumbers;
+ UCHAR ComputerNameNumbers[MAX_LANA];
+} WSNETWORKS, *PWSNETWORKS;
+
+extern WSNETWORKS WsNetworkInfo;
+
+NET_API_STATUS
+WsInitializeMessageSend(
+ VOID
+ );
+
+VOID
+WsShutdownMessageSend(
+ VOID
+ );
+
+NET_API_STATUS
+WsBroadcastMessage(
+ IN UCHAR LanAdapterNumber,
+ IN UCHAR ComputerNameNumber,
+ IN LPBYTE Message,
+ IN WORD MessageSize,
+ IN LPTSTR Sender
+ );
+
+NET_API_STATUS
+WsSendToGroup(
+ IN LPTSTR DomainName,
+ IN LPTSTR FromName,
+ IN LPBYTE Message,
+ IN WORD MessageSize
+ );
+
+NET_API_STATUS
+WsSendMultiBlockBegin(
+ IN UCHAR LanAdapterNumber,
+ IN UCHAR SessionNumber,
+ IN LPTSTR ToName,
+ IN LPTSTR FromName,
+ OUT short *MessageId
+ );
+
+NET_API_STATUS
+WsSendMultiBlockEnd(
+ IN UCHAR LanAdapterNumber,
+ IN UCHAR SessionNumber,
+ IN short MessageId
+ );
+
+NET_API_STATUS
+WsSendMultiBlockText(
+ IN UCHAR LanAdapterNumber,
+ IN UCHAR SessionNumber,
+ IN PCHAR TextBuffer,
+ IN WORD TextBufferSize,
+ IN short MessageId
+ );
+
+NET_API_STATUS
+WsSendSingleBlockMessage(
+ IN UCHAR LanAdapterNumber,
+ IN UCHAR SessionNumber,
+ IN LPTSTR ToName,
+ IN LPTSTR FromName,
+ IN PCHAR TextBuffer,
+ IN WORD TextBufferSize
+ );
+
+WORD
+WsMakeSmb(
+ OUT PUCHAR SmbBuffer, // Buffer to build SMB in
+ IN UCHAR SmdFunctionCode, // SMB function code
+ IN WORD NumberOfParameters, // Number of parameters
+ IN PCHAR FieldsDopeVector, // Fields dope vector
+ ...
+ );
+
+#endif // ifndef _WSMSG_INCLUDED_
diff --git a/private/net/svcdlls/wkssvc/server/wssec.c b/private/net/svcdlls/wkssvc/server/wssec.c
new file mode 100644
index 000000000..e8a576607
--- /dev/null
+++ b/private/net/svcdlls/wkssvc/server/wssec.c
@@ -0,0 +1,389 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ wssec.c
+
+Abstract:
+
+ This module contains the Workstation service support routines
+ which create security objects and enforce security _access checking.
+
+Author:
+
+ Rita Wong (ritaw) 19-Feb-1991
+
+Revision History:
+
+--*/
+
+#include "wsutil.h"
+#include "wsmain.h"
+#include "wssec.h"
+
+//-------------------------------------------------------------------//
+// //
+// Local function prototypes //
+// //
+//-------------------------------------------------------------------//
+
+STATIC
+NTSTATUS
+WsCreateConfigInfoObject(
+ VOID
+ );
+
+STATIC
+NTSTATUS
+WsCreateMessageSendObject(
+ VOID
+ );
+
+#if 0
+STATIC
+NTSTATUS
+WsCreateLogonSupportObject(
+ VOID
+ );
+#endif
+
+//-------------------------------------------------------------------//
+// //
+// Global variables //
+// //
+//-------------------------------------------------------------------//
+
+//
+// Security descriptors of workstation objects to control user accesses
+// to the workstation configuration information, sending messages, and the
+// logon support functions.
+//
+PSECURITY_DESCRIPTOR ConfigurationInfoSd;
+PSECURITY_DESCRIPTOR MessageSendSd;
+#if 0
+PSECURITY_DESCRIPTOR LogonSupportSd;
+#endif
+
+//
+// Structure that describes the mapping of Generic access rights to
+// object specific access rights for the ConfigurationInfo object.
+//
+GENERIC_MAPPING WsConfigInfoMapping = {
+ STANDARD_RIGHTS_READ | // Generic read
+ WKSTA_CONFIG_GUEST_INFO_GET |
+ WKSTA_CONFIG_USER_INFO_GET |
+ WKSTA_CONFIG_ADMIN_INFO_GET,
+ STANDARD_RIGHTS_WRITE | // Generic write
+ WKSTA_CONFIG_INFO_SET,
+ STANDARD_RIGHTS_EXECUTE, // Generic execute
+ WKSTA_CONFIG_ALL_ACCESS // Generic all
+ };
+
+//
+// Structure that describes the mapping of generic access rights to
+// object specific access rights for the MessageSend object.
+//
+GENERIC_MAPPING WsMessageSendMapping = {
+ STANDARD_RIGHTS_READ, // Generic read
+ STANDARD_RIGHTS_WRITE | // Generic write
+ WKSTA_MESSAGE_SEND,
+ STANDARD_RIGHTS_EXECUTE, // Generic execute
+ WKSTA_MESSAGE_ALL_ACCESS // Generic all
+ };
+
+#if 0
+//
+// Structure that describes the mapping of generic access rights to
+// object specific access rights for the LogonSupport object.
+//
+GENERIC_MAPPING WsLogonSupportMapping = {
+ STANDARD_RIGHTS_READ, // Generic read
+ STANDARD_RIGHTS_WRITE | // Generic write
+ WKSTA_LOGON_REQUEST_BROADCAST |
+ WKSTA_LOGON_DOMAIN_WRITE,
+ STANDARD_RIGHTS_EXECUTE, // Generic execute
+ WKSTA_LOGON_ALL_ACCESS // Generic all
+ };
+#endif
+
+
+
+NET_API_STATUS
+WsCreateWkstaObjects(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This function creates the workstation user-mode objects which are
+ represented by security descriptors.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NTSTATUS ntstatus;
+
+
+ //
+ // Create ConfigurationInfo object
+ //
+ if (! NT_SUCCESS (ntstatus = WsCreateConfigInfoObject())) {
+ IF_DEBUG(UTIL) {
+ NetpKdPrint(("[Wksta] Failure to create ConfigurationInfo object\n"));
+ }
+ return NetpNtStatusToApiStatus(ntstatus);
+ }
+
+ //
+ // Create MessageSend object
+ //
+ if (! NT_SUCCESS (ntstatus = WsCreateMessageSendObject())) {
+ IF_DEBUG(UTIL) {
+ NetpKdPrint(("[Wksta] Failure to create MessageSend object\n"));
+ }
+ return NetpNtStatusToApiStatus(ntstatus);
+ }
+
+#if 0
+ //
+ // Create LogonSupport object
+ //
+ if (! NT_SUCCESS (ntstatus = WsCreateLogonSupportObject())) {
+ IF_DEBUG(UTIL) {
+ NetpKdPrint(("[Wksta] Failure to create LogonSupport object\n"));
+ }
+ return NetpNtStatusToApiStatus(ntstatus);
+ }
+#endif
+
+ return NERR_Success;
+}
+
+
+
+STATIC
+NTSTATUS
+WsCreateConfigInfoObject(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This function creates the workstation configuration information object.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NTSTATUS - status returned from NetpCreateSecurityObject.
+
+--*/
+{
+ //
+ // Order matters! These ACEs are inserted into the DACL in the
+ // following order. Security access is granted or denied based on
+ // the order of the ACEs in the DACL.
+ //
+ // Local users, admins, and operators are allowed to get all information.
+ // Only admins are allowed to set information. Users are allowed to get
+ // user and guest info; guests are allowed to get guest info only.
+ //
+
+#define CONFIG_INFO_ACES 7 // Number of ACEs in this DACL
+
+ ACE_DATA AceData[CONFIG_INFO_ACES] = {
+ {ACCESS_ALLOWED_ACE_TYPE, 0, 0,
+ WKSTA_CONFIG_GUEST_INFO_GET |
+ WKSTA_CONFIG_USER_INFO_GET |
+ WKSTA_CONFIG_ADMIN_INFO_GET, &WsLmsvcsGlobalData->LocalSid},
+
+ {ACCESS_ALLOWED_ACE_TYPE, 0, 0,
+ GENERIC_ALL, &WsLmsvcsGlobalData->AliasAdminsSid},
+
+ {ACCESS_ALLOWED_ACE_TYPE, 0, 0,
+ WKSTA_CONFIG_GUEST_INFO_GET |
+ WKSTA_CONFIG_USER_INFO_GET |
+ WKSTA_CONFIG_ADMIN_INFO_GET, &WsLmsvcsGlobalData->AliasAccountOpsSid},
+
+ {ACCESS_ALLOWED_ACE_TYPE, 0, 0,
+ WKSTA_CONFIG_GUEST_INFO_GET |
+ WKSTA_CONFIG_USER_INFO_GET |
+ WKSTA_CONFIG_ADMIN_INFO_GET, &WsLmsvcsGlobalData->AliasSystemOpsSid},
+
+ {ACCESS_ALLOWED_ACE_TYPE, 0, 0,
+ WKSTA_CONFIG_GUEST_INFO_GET |
+ WKSTA_CONFIG_USER_INFO_GET |
+ WKSTA_CONFIG_ADMIN_INFO_GET, &WsLmsvcsGlobalData->AliasPrintOpsSid},
+
+ {ACCESS_ALLOWED_ACE_TYPE, 0, 0,
+ WKSTA_CONFIG_GUEST_INFO_GET |
+ WKSTA_CONFIG_USER_INFO_GET, &WsLmsvcsGlobalData->AliasUsersSid},
+
+ {ACCESS_ALLOWED_ACE_TYPE, 0, 0,
+ WKSTA_CONFIG_GUEST_INFO_GET, &WsLmsvcsGlobalData->WorldSid}
+ };
+
+
+ return NetpCreateSecurityObject(
+ AceData,
+ CONFIG_INFO_ACES,
+ WsLmsvcsGlobalData->LocalSystemSid,
+ WsLmsvcsGlobalData->LocalSystemSid,
+ &WsConfigInfoMapping,
+ &ConfigurationInfoSd
+ );
+}
+
+
+
+STATIC
+NTSTATUS
+WsCreateMessageSendObject(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This function creates the workstation message send object.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NTSTATUS - status returned from NetpCreateSecurityObject.
+
+--*/
+{
+ //
+ // Order matters! These ACEs are inserted into the DACL in the
+ // following order. Security access is granted or denied based on
+ // the order of the ACEs in the DACL.
+ //
+ // Any local user, and domain admins and operators are allowed to
+ // send messages. Remote users besides domain admins, and operators
+ // are not allowed to send messages.
+ //
+
+#define MESSAGE_SEND_ACES 5 // Number of ACEs in this DACL
+
+ ACE_DATA AceData[MESSAGE_SEND_ACES] = {
+ {ACCESS_ALLOWED_ACE_TYPE, 0, 0,
+ GENERIC_ALL, &WsLmsvcsGlobalData->LocalSid},
+
+ {ACCESS_ALLOWED_ACE_TYPE, 0, 0,
+ GENERIC_ALL, &WsLmsvcsGlobalData->AliasAdminsSid},
+
+ {ACCESS_ALLOWED_ACE_TYPE, 0, 0,
+ WKSTA_MESSAGE_SEND, &WsLmsvcsGlobalData->AliasAccountOpsSid},
+
+ {ACCESS_ALLOWED_ACE_TYPE, 0, 0,
+ WKSTA_MESSAGE_SEND, &WsLmsvcsGlobalData->AliasSystemOpsSid},
+
+ {ACCESS_ALLOWED_ACE_TYPE, 0, 0,
+ WKSTA_MESSAGE_SEND, &WsLmsvcsGlobalData->AliasPrintOpsSid}
+
+ };
+
+
+ return NetpCreateSecurityObject(
+ AceData,
+ MESSAGE_SEND_ACES,
+ WsLmsvcsGlobalData->LocalSystemSid,
+ WsLmsvcsGlobalData->LocalSystemSid,
+ &WsMessageSendMapping,
+ &MessageSendSd
+ );
+}
+
+#if 0
+
+STATIC
+NTSTATUS
+WsCreateLogonSupportObject(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This function creates the workstation logon support object.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NTSTATUS - status returned from NetpCreateSecurityObject.
+
+--*/
+{
+ //
+ // These ACEs can be inserted into the DACL in any order.
+ //
+
+#define LOGON_ACES 1 // Number of ACEs in this DACL
+
+ ACE_DATA AceData[LOGON_ACES] = {
+ {ACCESS_ALLOWED_ACE_TYPE, 0, 0,
+ WKSTA_LOGON_REQUEST_BROADCAST | WKSTA_LOGON_DOMAIN_WRITE,
+ &WsLmsvcsGlobalData->LocalSystemSid},
+
+ };
+
+ return NetpCreateSecurityObject(
+ AceData,
+ LOGON_ACES,
+ WsLmsvcsGlobalData->LocalSystemSid,
+ WsLmsvcsGlobalData->LocalSystemSid,
+ &WsLogonSupportMapping,
+ &LogonSupportSd
+ );
+}
+#endif
+
+
+VOID
+WsDestroyWkstaObjects(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This function destroys the workstation user-mode objects which are
+ represented by security descriptors.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ (void) NetpDeleteSecurityObject(&ConfigurationInfoSd);
+ (void) NetpDeleteSecurityObject(&MessageSendSd);
+#if 0
+ (void) NetpDeleteSecurityObject(&LogonSupportSd);
+#endif
+
+}
diff --git a/private/net/svcdlls/wkssvc/server/wssec.h b/private/net/svcdlls/wkssvc/server/wssec.h
new file mode 100644
index 000000000..f77b4e482
--- /dev/null
+++ b/private/net/svcdlls/wkssvc/server/wssec.h
@@ -0,0 +1,107 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ wssec.h
+
+Abstract:
+
+ Private header file to be included by Workstation service modules that
+ need to enforce security.
+
+Author:
+
+ Rita Wong (ritaw) 19-Feb-1991
+
+Revision History:
+
+--*/
+
+#ifndef _WSSEC_INCLUDED_
+#define _WSSEC_INCLUDED_
+
+#include <secobj.h>
+
+//-------------------------------------------------------------------//
+// //
+// Object specific access masks //
+// //
+//-------------------------------------------------------------------//
+
+//
+// ConfigurationInfo specific access masks
+//
+#define WKSTA_CONFIG_GUEST_INFO_GET 0x0001
+#define WKSTA_CONFIG_USER_INFO_GET 0x0002
+#define WKSTA_CONFIG_ADMIN_INFO_GET 0x0004
+#define WKSTA_CONFIG_INFO_SET 0x0008
+
+#define WKSTA_CONFIG_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | \
+ WKSTA_CONFIG_GUEST_INFO_GET | \
+ WKSTA_CONFIG_USER_INFO_GET | \
+ WKSTA_CONFIG_ADMIN_INFO_GET | \
+ WKSTA_CONFIG_INFO_SET)
+
+//
+// MessageSend specific access masks
+//
+#define WKSTA_MESSAGE_SEND 0x0001
+
+#define WKSTA_MESSAGE_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | \
+ WKSTA_MESSAGE_SEND)
+
+#if 0
+//
+// LogonSupport specific access masks
+//
+#define WKSTA_LOGON_REQUEST_BROADCAST 0x0001
+#define WKSTA_LOGON_DOMAIN_WRITE 0x0002
+
+#define WKSTA_LOGON_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | \
+ WKSTA_LOGON_REQUEST_BROADCAST | \
+ WKSTA_LOGON_DOMAIN_WRITE)
+#endif
+
+//
+// Object type names for audit alarm tracking
+//
+#define CONFIG_INFO_OBJECT TEXT("WkstaConfigurationInfo")
+#define MESSAGE_SEND_OBJECT TEXT("WkstaMessageSend")
+#if 0
+#define LOGON_SUPPORT_OBJECT TEXT("WkstaLogonSupport")
+#endif
+
+//
+// Security descriptors of workstation objects to control user accesses
+// to the workstation configuration information, sending messages, and the
+// logon support functions.
+//
+extern PSECURITY_DESCRIPTOR ConfigurationInfoSd;
+extern PSECURITY_DESCRIPTOR MessageSendSd;
+#if 0
+extern PSECURITY_DESCRIPTOR LogonSupportSd;
+#endif
+
+//
+// Generic mapping for each workstation object
+//
+extern GENERIC_MAPPING WsConfigInfoMapping;
+extern GENERIC_MAPPING WsMessageSendMapping;
+#if 0
+extern GENERIC_MAPPING WsLogonSupportMapping;
+#endif
+
+
+NET_API_STATUS
+WsCreateWkstaObjects(
+ VOID
+ );
+
+VOID
+WsDestroyWkstaObjects(
+ VOID
+ );
+
+#endif // ifndef _WSSEC_INCLUDED_
diff --git a/private/net/svcdlls/wkssvc/server/wssend.c b/private/net/svcdlls/wkssvc/server/wssend.c
new file mode 100644
index 000000000..faf65cd8e
--- /dev/null
+++ b/private/net/svcdlls/wkssvc/server/wssend.c
@@ -0,0 +1,814 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ wssend.c
+
+Abstract:
+
+ This module contains the worker routines for sending
+ domain wide and directed messages, which are used to implement
+ NetMessageBufferSend API.
+
+Author:
+
+ Rita Wong (ritaw) 29-July-1991
+
+Revision History:
+
+--*/
+
+
+#include "wsutil.h"
+#include "wsmsg.h"
+
+//-------------------------------------------------------------------//
+// //
+// Local function prototypes //
+// //
+//-------------------------------------------------------------------//
+
+STATIC
+BOOL
+WsVerifySmb(
+ IN PUCHAR SmbBuffer,
+ IN WORD SmbBufferSize,
+ IN UCHAR SmbFunctionCode,
+ OUT PUCHAR SmbReturnClass,
+ OUT PUSHORT SmbReturnCode
+ );
+
+STATIC
+NET_API_STATUS
+WsMapSmbStatus(
+ UCHAR SmbReturnClass,
+ USHORT SmbReturnCode
+ );
+
+
+NET_API_STATUS
+WsSendToGroup(
+ IN LPTSTR DomainName,
+ IN LPTSTR Sender,
+ IN LPBYTE Message,
+ IN WORD MessageSize
+ )
+/*++
+
+Routine Description:
+
+ This function writes a datagram to the \\DomainName\MAILSLOT\MESSNGR
+ mailslot which is read by every Messenger service of workstations
+ that have the domain name as the primary domain. Reception is not
+ guaranteed.
+
+ The DomainName may be a computername. This is acceptable because the
+ Datagram Receiver listens on the computername (besides the primary domain)
+ for datagrams. When a computername is specified, the message is sent
+ to that one computer alone.
+
+Arguments:
+
+ DomainName - Supplies the name of the target domain. This actually
+ can be a computername, in which case, the datagram only reaches
+ one recipient.
+
+ Sender - Supplies the name of the sender.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NET_API_STATUS status = NERR_Success;
+ HANDLE MessengerMailslot;
+ DWORD NumberOfBytesWritten;
+
+ BYTE MailslotBuffer[MAX_GROUP_MESSAGE_SIZE + MAX_PATH + MAX_PATH + 4];
+ LPSTR AnsiSender;
+ LPSTR AnsiReceiver;
+
+ LPBYTE CurrentPos;
+
+
+ //
+ // Canonicalize the domain name
+ //
+ status = I_NetNameCanonicalize(
+ NULL,
+ DomainName,
+ DomainName,
+ (NCBNAMSZ + 2) * sizeof(TCHAR),
+ NAMETYPE_DOMAIN,
+ 0
+ );
+
+ if (status != NERR_Success) {
+ NetpKdPrint(("[Wksta] Error canonicalizing domain name %ws %lu\n",
+ DomainName, status));
+ return status;
+ }
+
+ //
+ // Open \\DomainName\MAILSLOT\MESSNGR mailslot to
+ // send message to.
+ //
+ if ((status = WsOpenDestinationMailslot(
+ DomainName,
+ MESSENGER_MAILSLOT_W,
+ &MessengerMailslot
+ )) != NERR_Success) {
+ return status;
+ }
+
+ //
+ // Package the message to be sent. It consists of:
+ // Sender (must be ANSI)
+ // DomainName (must be ANSI)
+ // Message
+ //
+
+ //
+ // Convert the names to ANSI
+ //
+ AnsiSender = NetpAllocStrFromWStr(Sender);
+ if (AnsiSender == NULL) {
+ (void) CloseHandle(MessengerMailslot);
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ AnsiReceiver = NetpAllocStrFromWStr(DomainName);
+ if (AnsiReceiver == NULL) {
+ NetApiBufferFree(AnsiSender);
+ (void) CloseHandle(MessengerMailslot);
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ RtlZeroMemory(MailslotBuffer, sizeof( MailslotBuffer ) );
+
+ //
+ // Copy Sender into mailslot buffer
+ //
+ strcpy(MailslotBuffer, AnsiSender);
+ CurrentPos = (LPBYTE) ((DWORD) MailslotBuffer +
+ strlen(AnsiSender) + 1);
+
+ //
+ // Copy DomainName into mailslot buffer
+ //
+ strcpy(CurrentPos, AnsiReceiver);
+ (DWORD) CurrentPos += (strlen(AnsiReceiver) + 1);
+
+ //
+ // Copy Message into mailslot buffer
+ //
+ strncpy(CurrentPos, Message, MessageSize);
+ (DWORD) CurrentPos += MessageSize;
+ *CurrentPos = '\0';
+
+ //
+ // Send the datagram to the domain
+ //
+ if (WriteFile(
+ MessengerMailslot,
+ MailslotBuffer,
+ (DWORD) (CurrentPos - MailslotBuffer + 1),
+ &NumberOfBytesWritten,
+ NULL
+ ) == FALSE) {
+
+ status = GetLastError();
+ NetpKdPrint(("[Wksta] Error sending datagram to %ws %lu\n",
+ AnsiReceiver, status));
+
+ if (status == ERROR_PATH_NOT_FOUND ||
+ status == ERROR_BAD_NET_NAME) {
+ status = NERR_NameNotFound;
+ }
+ }
+ else {
+ NetpAssert(NumberOfBytesWritten ==
+ (DWORD) (CurrentPos - MailslotBuffer + 1));
+ }
+
+ NetApiBufferFree(AnsiSender);
+ NetApiBufferFree(AnsiReceiver);
+
+ (void) CloseHandle(MessengerMailslot);
+
+ return status;
+}
+
+
+NET_API_STATUS
+WsSendMultiBlockBegin(
+ IN UCHAR LanAdapterNumber,
+ IN UCHAR SessionNumber,
+ IN LPTSTR ToName,
+ IN LPTSTR FromName,
+ OUT short *MessageId
+ )
+/*++
+
+Routine Description:
+
+ This function sends the header of a multi-block directed message on a
+ session we had established earlier. It waits for an acknowlegement from
+ the recipient. If the recipient got the message successfully, it
+ sends back a message group id which is returned by this function for
+ subsequent use in sending the body and trailer of a multi-block message.
+
+Arguments:
+
+ LanAdapterNumber - Supplies the number of the LAN adapter.
+
+ SessionNumber - Supplies the session number of a session established with
+ NetBIOS CALL and LISTEN commands.
+
+ ToName - Supplies the name of the recipient.
+
+ FromName - Supplies the name of the sender.
+
+ MessageId - Returns the message group id.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NET_API_STATUS status;
+
+ UCHAR SmbBuffer[WS_SMB_BUFFER_SIZE];
+ WORD SmbSize;
+
+ char SendName[NCBNAMSZ + 1];
+
+ UCHAR SmbReturnClass;
+ USHORT SmbReturnCode;
+
+ LPSTR AnsiToName;
+ LPSTR AnsiFromName;
+
+ AnsiToName = NetpAllocStrFromWStr(ToName);
+ if (AnsiToName == NULL) {
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ AnsiFromName = NetpAllocStrFromWStr(FromName);
+ if (AnsiFromName == NULL) {
+ NetApiBufferFree(AnsiToName);
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ strncpy(SendName, AnsiToName, sizeof(SendName) );
+
+ SendName[NCBNAMSZ - 1] = '\0'; // Null terminate at max size
+
+ //
+ // Make and send the SMB
+ //
+ SmbSize = WsMakeSmb(
+ SmbBuffer,
+ SMB_COM_SEND_START_MB_MESSAGE,
+ 0,
+ "ss",
+ AnsiFromName,
+ SendName
+ );
+
+ NetApiBufferFree(AnsiToName);
+ NetApiBufferFree(AnsiFromName);
+
+ IF_DEBUG(MESSAGE) {
+ NetpKdPrint(("[Wksta] Send start multi-block message. Size=%u\n",
+ SmbSize));
+#if DBG
+ NetpHexDump(SmbBuffer, SmbSize);
+#endif
+ }
+
+ if ((status = NetpNetBiosSend(
+ LanAdapterNumber,
+ SessionNumber,
+ SmbBuffer,
+ SmbSize
+ )) != NERR_Success) {
+ NetpKdPrint(("[Wksta] Failed to send start of multi-block message %lu\n",
+ status));
+ return status;
+ }
+
+ //
+ // Get response
+ //
+ if ((status = NetpNetBiosReceive(
+ LanAdapterNumber,
+ SessionNumber,
+ SmbBuffer,
+ WS_SMB_BUFFER_SIZE,
+ (HANDLE) NULL,
+ &SmbSize
+ )) != NERR_Success) {
+ NetpKdPrint(("[Wksta] Failed to receive verification to multi-"
+ "block message start %lu\n", status));
+ return status;
+ }
+
+ if (! WsVerifySmb(
+ SmbBuffer,
+ SmbSize,
+ SMB_COM_SEND_START_MB_MESSAGE,
+ &SmbReturnClass,
+ &SmbReturnCode
+ )) {
+
+ //
+ // Unexpected behaviour
+ //
+ return NERR_NetworkError;
+ }
+
+ //
+ // Set the message group id
+ //
+
+ *MessageId = *((UNALIGNED short *) &SmbBuffer[sizeof(SMB_HEADER) + 1]);
+
+ IF_DEBUG(MESSAGE) {
+ NetpKdPrint(("[Wksta] Message Id=x%x\n", *MessageId));
+ }
+
+ return WsMapSmbStatus(SmbReturnClass, SmbReturnCode);
+}
+
+
+NET_API_STATUS
+WsSendMultiBlockEnd(
+ IN UCHAR LanAdapterNumber,
+ IN UCHAR SessionNumber,
+ IN short MessageId
+ )
+/*++
+
+Routine Description:
+
+ This function sends the end marker of a multi-block directed message on
+ a session we had establised earlier. It waits for an acknowlegement from
+ the recipient.
+
+Arguments:
+
+ LanAdapterNumber - Supplies the number of the LAN adapter.
+
+ SessionNumber - Supplies the session number of a session established with
+ NetBIOS CALL and LISTEN commands.
+
+ MessageId - Supplies the message group id gotten from
+ WsSendMultiBlockBegin.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+
+ NET_API_STATUS status;
+
+ UCHAR SmbBuffer[WS_SMB_BUFFER_SIZE];
+ WORD SmbSize; // Size of SMB data
+
+ UCHAR SmbReturnClass;
+ USHORT SmbReturnCode;
+
+
+ SmbSize = WsMakeSmb(
+ SmbBuffer,
+ SMB_COM_SEND_END_MB_MESSAGE,
+ 1,
+ "",
+ MessageId
+ );
+
+ IF_DEBUG(MESSAGE) {
+ NetpKdPrint(("[Wksta] Send end multi-block message. Size=%u\n",
+ SmbSize));
+#if DBG
+ NetpHexDump(SmbBuffer, SmbSize);
+#endif
+ }
+
+ if ((status = NetpNetBiosSend(
+ LanAdapterNumber,
+ SessionNumber,
+ SmbBuffer,
+ SmbSize
+ )) != NERR_Success) {
+ NetpKdPrint(("[Wksta] Failed to send end of multi-block message %lu\n",
+ status));
+ return status;
+ }
+
+ //
+ // Get response
+ //
+ if ((status = NetpNetBiosReceive(
+ LanAdapterNumber,
+ SessionNumber,
+ SmbBuffer,
+ WS_SMB_BUFFER_SIZE,
+ (HANDLE) NULL,
+ &SmbSize
+ )) != NERR_Success) {
+ NetpKdPrint(("[Wksta] Failed to receive verification to multi-"
+ "block message end %lu\n", status));
+ return status;
+ }
+
+ if (! WsVerifySmb(
+ SmbBuffer,
+ SmbSize,
+ SMB_COM_SEND_END_MB_MESSAGE,
+ &SmbReturnClass,
+ &SmbReturnCode
+ )) {
+ return NERR_NetworkError; // Unexpected behaviour
+ }
+
+ return WsMapSmbStatus(SmbReturnClass,SmbReturnCode);
+}
+
+
+
+NET_API_STATUS
+WsSendMultiBlockText(
+ IN UCHAR LanAdapterNumber,
+ IN UCHAR SessionNumber,
+ IN PCHAR TextBuffer,
+ IN WORD TextBufferSize,
+ IN short MessageId
+ )
+/*++
+
+Routine Description:
+
+ This function sends the body of a multi-block directed message on a
+ session we had established earlier. It waits for an acknowlegement from
+ the recipient.
+
+Arguments:
+
+ LanAdapterNumber - Supplies the number of the LAN adapter.
+
+ SessionNumber - Supplies the session number of a session established with
+ NetBIOS CALL and LISTEN commands.
+
+ TextBuffer - Supplies the buffer of the message to be sent.
+
+ TextBufferSize - Supplies the size of the message buffer.
+
+ MessageId - Supplies the message group id gotten from
+ WsSendMultiBlockBegin.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NET_API_STATUS status;
+
+ UCHAR SmbBuffer[WS_SMB_BUFFER_SIZE];
+ WORD SmbSize; // Buffer length
+
+ UCHAR SmbReturnClass;
+ USHORT SmbReturnCode;
+
+
+ IF_DEBUG(MESSAGE) {
+ NetpKdPrint(("[Wksta] Send body multi-block message. Size=%u\n",
+ TextBufferSize));
+ }
+
+ SmbSize = WsMakeSmb(
+ SmbBuffer,
+ SMB_COM_SEND_TEXT_MB_MESSAGE,
+ 1,
+ "t",
+ MessageId,
+ TextBufferSize,
+ TextBuffer
+ );
+
+ IF_DEBUG(MESSAGE) {
+ NetpKdPrint(("[Wksta] SMB for body of multi-block message. Size=%u\n",
+ SmbSize));
+ }
+
+ if ((status = NetpNetBiosSend(
+ LanAdapterNumber,
+ SessionNumber,
+ SmbBuffer,
+ SmbSize
+ )) != NERR_Success) {
+ NetpKdPrint(("[Wksta] Failed to send body of multi-block message %lu\n",
+ status));
+ return status;
+ }
+
+ //
+ // Get response
+ //
+ if ((status = NetpNetBiosReceive(
+ LanAdapterNumber,
+ SessionNumber,
+ SmbBuffer,
+ WS_SMB_BUFFER_SIZE,
+ (HANDLE) NULL,
+ &SmbSize
+ )) != NERR_Success) {
+ NetpKdPrint(("[Wksta] Failed to receive verification to multi-"
+ "block message body %lu\n", status));
+ return status;
+ }
+
+ if (! WsVerifySmb(
+ SmbBuffer,
+ SmbSize,
+ SMB_COM_SEND_TEXT_MB_MESSAGE,
+ &SmbReturnClass,
+ &SmbReturnCode
+ )) {
+ return NERR_NetworkError; // Unexpected behaviour
+ }
+
+ return WsMapSmbStatus(SmbReturnClass, SmbReturnCode);
+}
+
+
+NET_API_STATUS
+WsSendSingleBlockMessage(
+ IN UCHAR LanAdapterNumber,
+ IN UCHAR SessionNumber,
+ IN LPTSTR ToName,
+ IN LPTSTR FromName,
+ IN PCHAR Message,
+ IN WORD MessageSize
+ )
+/*++
+
+Routine Description:
+
+ This function sends a directed message in one SMB on a session we had
+ established earlier. It waits for an acknowlegement from the recipient.
+
+Arguments:
+
+ LanAdapterNumber - Supplies the number of the LAN adapter.
+
+ SessionNumber - Supplies the session number of a session established with
+ NetBIOS CALL and LISTEN commands.
+
+ ToName - Supplies the name of the recipient.
+
+ FromName - Supplies the name of the sender.
+
+ Message - Supplies the buffer of the message to be sent.
+
+ MessageSize - Supplies the size of the message.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NET_API_STATUS status;
+
+ UCHAR SmbBuffer[WS_SMB_BUFFER_SIZE];
+ WORD SmbSize; // Buffer length
+
+ UCHAR SmbReturnClass;
+ USHORT SmbReturnCode;
+
+ LPSTR AnsiToName;
+ LPSTR AnsiFromName;
+
+
+ AnsiToName = NetpAllocStrFromWStr(ToName);
+ if (AnsiToName == NULL) {
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ AnsiFromName = NetpAllocStrFromWStr(FromName);
+ if (AnsiFromName == NULL) {
+ NetApiBufferFree(AnsiToName);
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ SmbSize = WsMakeSmb(
+ SmbBuffer,
+ SMB_COM_SEND_MESSAGE,
+ 0,
+ "sst",
+ AnsiFromName,
+ AnsiToName,
+ MessageSize,
+ Message
+ );
+
+ NetApiBufferFree(AnsiToName);
+ NetApiBufferFree(AnsiFromName);
+
+ IF_DEBUG(MESSAGE) {
+ NetpKdPrint(("[Wksta] Send single block message. Size=%u\n", SmbSize));
+#if DBG
+ NetpHexDump(SmbBuffer, SmbSize);
+#endif
+ }
+
+ //
+ // Send SMB
+ //
+ if ((status = NetpNetBiosSend(
+ LanAdapterNumber,
+ SessionNumber,
+ SmbBuffer,
+ SmbSize
+ )) != NERR_Success) {
+ NetpKdPrint(("[Wksta] Failed to send single block message %lu\n",
+ status));
+ return status;
+ }
+
+ //
+ // Get response
+ //
+ if ((status = NetpNetBiosReceive(
+ LanAdapterNumber,
+ SessionNumber,
+ SmbBuffer,
+ WS_SMB_BUFFER_SIZE,
+ (HANDLE) NULL,
+ &SmbSize
+ )) != NERR_Success) {
+ NetpKdPrint(("[Wksta] Failed to receive verification to single"
+ " block message %lu\n", status));
+ return status;
+ }
+
+ if (! WsVerifySmb(
+ SmbBuffer,
+ SmbSize,
+ SMB_COM_SEND_MESSAGE,
+ &SmbReturnClass,
+ &SmbReturnCode
+ )) {
+ return NERR_NetworkError; // Unexpected behaviour
+ }
+
+ return WsMapSmbStatus(SmbReturnClass, SmbReturnCode);
+}
+
+
+
+STATIC
+BOOL
+WsVerifySmb(
+ IN PUCHAR SmbBuffer,
+ IN WORD SmbBufferSize,
+ IN UCHAR SmbFunctionCode,
+ OUT PUCHAR SmbReturnClass,
+ OUT PUSHORT SmbReturnCode
+ )
+/*++
+
+Routine Description:
+
+ This function checks the format of a received SMB; it returns TRUE if
+ if the SMB format is valid, and FALSE otherwise.
+
+Arguments:
+
+ SmbBuffer - Supplies the SMB buffer
+
+ SmbBufferSize - Supplies the size of SmbBuffer in bytes
+
+ SmbFunctionCode - Supplies the function code for which the SMB is received
+ to determine the proper SMB format.
+
+ SmbReturnClass - Returns the class of the SMB only if the SMB format is
+ valid.
+
+ SmbReturnCode - Returns the error code of the SMB.
+
+Return Value:
+
+ TRUE if SMB is valid; FALSE otherwise.
+
+--*/
+{
+ PSMB_HEADER Smb = (PSMB_HEADER) SmbBuffer; // Pointer to SMB header
+ int SmbCheckCode;
+ int ParameterCount;
+
+ //
+ // Assume error
+ //
+ *SmbReturnClass = (UCHAR) 0xff;
+
+ *SmbReturnCode = Smb->Error;
+
+ switch (SmbFunctionCode) {
+ case SMB_COM_SEND_MESSAGE: // Single-block message
+ case SMB_COM_SEND_TEXT_MB_MESSAGE: // Text of multi-block message
+ case SMB_COM_SEND_END_MB_MESSAGE: // End of multi-block message
+ ParameterCount = 0;
+ break;
+
+ case SMB_COM_SEND_START_MB_MESSAGE: // Beginning of multi-block message
+ ParameterCount = 1;
+ break;
+
+ default: // Unknown SMB
+ NetpKdPrint(("[Wksta] WsVerifySmb unknown SMB\n"));
+ return FALSE;
+ }
+
+ if (! (SmbCheckCode = NetpSmbCheck(
+ SmbBuffer,
+ SmbBufferSize,
+ SmbFunctionCode,
+ ParameterCount,
+ ""
+ ))) {
+
+ //
+ // Set the return class if valid SMB
+ //
+ *SmbReturnClass = Smb->ErrorClass;
+ return TRUE;
+
+ }
+ else {
+ //
+ // Invalid SMB
+ //
+ NetpKdPrint(("[Wksta] WsVerifySmb invalid SMB %d\n", SmbCheckCode));
+ return FALSE;
+ }
+}
+
+
+STATIC
+NET_API_STATUS
+WsMapSmbStatus(
+ UCHAR SmbReturnClass,
+ USHORT SmbReturnCode
+ )
+/*++
+
+Routine Description:
+
+ This function converts an SMB status to API status.
+
+Arguments:
+
+ SmbReturnClass - Supplies the SMB class
+
+ SmbReturnCode - Supplies the SMB return code.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ switch (SmbReturnClass) {
+
+ case SMB_ERR_SUCCESS:
+ return NERR_Success;
+
+ case SMB_ERR_CLASS_SERVER:
+ //
+ // SMB error
+ //
+ NetpKdPrint(("[Wksta] SMB error SmbReturnCode=%u\n", SmbReturnCode));
+
+ if (SmbReturnCode == SMB_ERR_SERVER_PAUSED) {
+ return NERR_PausedRemote; // Server paused
+ }
+ else {
+ return NERR_BadReceive; // Send not received
+ }
+
+ break;
+
+ default:
+ return NERR_BadReceive;
+ }
+}
diff --git a/private/net/svcdlls/wkssvc/server/wsstats.c b/private/net/svcdlls/wkssvc/server/wsstats.c
new file mode 100644
index 000000000..56f8d331d
--- /dev/null
+++ b/private/net/svcdlls/wkssvc/server/wsstats.c
@@ -0,0 +1,251 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ WsStats.c
+
+Abstract:
+
+ Contains workstation service half of the Net statistics routine:
+
+ NetrWorkstationStatisticsGet
+ (GetStatisticsFromRedir)
+
+Author:
+
+ Richard L Firth (rfirth) 12-05-1991
+
+Revision History:
+
+ 12-05-1991 rfirth
+ Created
+
+--*/
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windows.h>
+#include <lmcons.h>
+#include <lmerr.h>
+#include <lmwksta.h>
+#include <lmstats.h>
+#include <ntddnfs.h>
+#include <memory.h>
+#include <netlibnt.h>
+#include <ntrpcp.h>
+
+#include "wsdevice.h"
+
+//
+// debugging
+//
+
+#ifdef DBG
+#define STATIC
+#ifdef DBGSTATS
+BOOL DbgStats = TRUE;
+#else
+BOOL DbgStats = FALSE;
+#endif
+#ifdef UNICODE
+#define PERCENT_S "%ws"
+#else
+#define PERCENT_S "%s"
+#endif
+#else
+#define STATIC static
+#endif
+
+//
+// private prototypes
+//
+
+static
+NTSTATUS
+GetStatisticsFromRedir(
+ OUT PREDIR_STATISTICS pStats
+ );
+
+//
+// functions
+//
+
+NET_API_STATUS
+NET_API_FUNCTION
+NetrWorkstationStatisticsGet(
+ IN LPTSTR ServerName,
+ IN LPTSTR ServiceName,
+ IN DWORD Level,
+ IN DWORD Options,
+ OUT LPBYTE* Buffer
+ )
+
+/*++
+
+Routine Description:
+
+ Returns workstation statistics to the caller. This is the server part of
+ the request. Parameters have been validated by the client routine
+
+Arguments:
+
+ ServerName - IGNORED
+ ServiceName - IGNORED
+ Level - of information required. MBZ (IGNORED)
+ Options - MBZ
+ Buffer - pointer to pointer to returned buffer
+
+Return Value:
+
+ NET_API_STATUS
+ Success - NERR_Success
+
+ Failure - ERROR_INVALID_LEVEL
+ Level not 0
+
+ ERROR_INVALID_PARAMETER
+ Unsupported options requested
+
+ ERROR_NOT_ENOUGH_MEMORY
+ For API buffer
+
+--*/
+
+{
+ NET_API_STATUS status;
+ NTSTATUS ntStatus;
+ PREDIR_STATISTICS stats;
+
+ UNREFERENCED_PARAMETER(ServerName);
+ UNREFERENCED_PARAMETER(ServiceName);
+
+#if DBG
+ if (DbgStats) {
+ DbgPrint("NetrWorkstationStatisticsGet: ServerName=" PERCENT_S "\n"
+ "ServiceName=" PERCENT_S "\n"
+ "Level=%d\n"
+ "Options=%x\n",
+ ServerName,
+ ServiceName,
+ Level,
+ Options
+ );
+ }
+#endif
+
+ if (Level) {
+ return ERROR_INVALID_LEVEL;
+ }
+
+ //
+ // we don't even allow clearing of stats any more
+ //
+
+ if (Options) {
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ //
+ // get the redir statistics then munge them into API format
+ //
+
+ stats = (PREDIR_STATISTICS)MIDL_user_allocate(sizeof(*stats));
+ if (stats == NULL) {
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+ ntStatus = GetStatisticsFromRedir(stats);
+ if (NT_SUCCESS(ntStatus)) {
+ *Buffer = (LPBYTE)stats;
+ status = NERR_Success;
+ } else {
+ MIDL_user_free(stats);
+ status = NetpNtStatusToApiStatus(ntStatus);
+ }
+
+#if DBG
+ if (DbgStats) {
+ DbgPrint("NetrWorkstationStatisticsGet: returning %x\n", status);
+ }
+#endif
+
+ return status;
+}
+
+static
+NTSTATUS
+GetStatisticsFromRedir(
+ OUT PREDIR_STATISTICS pStats
+ )
+
+/*++
+
+Routine Description:
+
+ Reads the redir statistics from the Redirector File System Device
+
+Arguments:
+
+ pStats - place to store statistics (fixed length buffer)
+
+Return Value:
+
+ NTSTATUS
+ Success - STATUS_SUCCESS
+ *pStats contains redirector statistics
+
+ Failure -
+
+--*/
+
+{
+ HANDLE FileHandle;
+ NTSTATUS Status;
+ IO_STATUS_BLOCK IoStatusBlock;
+
+ OBJECT_ATTRIBUTES Obja;
+ UNICODE_STRING FileName;
+
+ RtlInitUnicodeString(&FileName,DD_NFS_DEVICE_NAME_U);
+
+ InitializeObjectAttributes(
+ &Obja,
+ &FileName,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL
+ );
+ Status = NtCreateFile(
+ &FileHandle,
+ SYNCHRONIZE,
+ &Obja,
+ &IoStatusBlock,
+ NULL,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ FILE_OPEN_IF,
+ FILE_SYNCHRONOUS_IO_NONALERT,
+ NULL,
+ 0
+ );
+ if ( NT_SUCCESS(Status) ) {
+ Status = NtFsControlFile(
+ FileHandle,
+ NULL,
+ NULL,
+ NULL,
+ &IoStatusBlock,
+ FSCTL_LMR_GET_STATISTICS,
+ NULL,
+ 0,
+ pStats,
+ sizeof(*pStats)
+ );
+ }
+
+ NtClose(FileHandle);
+
+ return Status;
+}
diff --git a/private/net/svcdlls/wkssvc/server/wsuse.h b/private/net/svcdlls/wkssvc/server/wsuse.h
new file mode 100644
index 000000000..92c097153
--- /dev/null
+++ b/private/net/svcdlls/wkssvc/server/wsuse.h
@@ -0,0 +1,293 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ wsuse.h
+
+Abstract:
+
+ Private header file to be included by Workstation service modules that
+ implement the NetUse APIs.
+
+Author:
+
+ Rita Wong (ritaw) 05-Mar-1991
+
+Revision History:
+
+--*/
+
+#ifndef _WSUSE_INCLUDED_
+#define _WSUSE_INCLUDED_
+
+#include <lmuse.h> // LAN Man Use API definitions
+
+
+//
+// Length of fixed size portion of a use info structure
+//
+#define USE_FIXED_LENGTH(Level) \
+ (DWORD) \
+ ((Level == 0) ? sizeof(USE_INFO_0) : \
+ ((Level == 1) ? sizeof(USE_INFO_1) : \
+ sizeof(USE_INFO_2)))
+
+
+//
+// Total length of a use info structure (fixed and variable length portions)
+//
+#define USE_TOTAL_LENGTH(Level, LocalandUncNameLength, UserNameLength) \
+ (DWORD) \
+ ((Level == 2) ? sizeof(USE_INFO_2) + (LocalandUncNameLength) + \
+ (UserNameLength) : \
+ (LocalandUncNameLength) + ((Level == 1) ? \
+ sizeof(USE_INFO_1) : \
+ sizeof(USE_INFO_0)))
+
+//
+// Hint size of an entry of use information from redirector
+//
+#define HINT_REDIR_INFO(Level) \
+ (DWORD) \
+ ((Level == 1) ? sizeof(LMR_CONNECTION_INFO_1) + \
+ MAX_PATH * sizeof(WCHAR) : \
+ sizeof(LMR_CONNECTION_INFO_2) + \
+ (MAX_PATH + MAX_PATH) * sizeof(WCHAR))
+//
+// Length of fixed size portion of a redirector enumerate info structure
+//
+#define REDIR_ENUM_INFO_FIXED_LENGTH(Level) \
+ (DWORD) \
+ ((Level == 0) ? sizeof(LMR_CONNECTION_INFO_0) : \
+ ((Level == 1) ? sizeof(LMR_CONNECTION_INFO_1) : \
+ sizeof(LMR_CONNECTION_INFO_2)))
+
+#define REDIR_LIST 0x80000000
+
+
+//-----------------------------------------------------------------------//
+// //
+// Use Table //
+// //
+// +-----------------+ +-----------------+ //
+// |TotalUseCount = 6| |TotalUseCount = 1| //
+// +-----------------+ +-----------------+ //
+// | RedirUseInfo | | RedirUseInfo | //
+// +-----------------+ +-----------------+ //
+// | UncNameLength | | UncNameLength | //
+// +-----------------+ +-----------------+ //
+// |\\POPCORN\RAZZLE | |\\FUZZY\PRINTER | //
+// +-----------------+ +-----------------+ //
+// ^ ^ ^ //
+// | | | //
+// +---+ +---+ | //
+// | | | //
+// +------------+ +--|--+------+ +--|--+------+ +--|--+------+ //
+// | | | * | *---->| * | *---->| * | *---->... //
+// | *-------->+-----+------+ +-----+------+ +-----+------+ //
+// | | | P: |Local | |NULL |Local | |LPT1 |Local | //
+// +------------+ | |Length| | |Length| | |Length| //
+// | | +-----+------+ +-----+------+ +-----+------+ //
+// | | |UseCount = 1| |UseCount = 5| |UseCount = 1| //
+// 0 | LogonId | +------------+ +------------+ +------------+ //
+// | | |Tree | |Tree | |Tree | //
+// | | |Connection | |Connection | |Connection | //
+// | | +------------+ +------------+ +------------+ //
+// | | |ResumeKey | |ResumeKey | |ResumeKey | //
+// | | +------------+ +------------+ +------------+ //
+// | | |TreeConnStr | | NULL | |TreeConnStr | //
+// | | +------------+ +------------+ +------------+ //
+// | | //
+// +============+ //
+// | | //
+// | *--------> ... //
+// | | //
+// +------------+ //
+// | | //
+// | LogonId | //
+// 1 | | //
+// | | //
+// | | //
+// | | //
+// | | //
+// | | //
+// | | //
+// | | //
+// +============+ //
+// | . | //
+// | . | //
+// | . | //
+// //
+// //
+// The Use Table maintained by the Workstation service keeps a list of //
+// explicit connections established by each user. A use entry is always //
+// inserted into the end of the list. //
+// //
+// Implicit connections are not maintained in the Use Table. The //
+// Workstation service has to ask the redirector to list all established //
+// implicit connections when enumerating all active connections for a //
+// user. //
+// //
+//-----------------------------------------------------------------------//
+
+//
+// The structure definition for the per user entry, which consists of a
+// Logon Id and a pointer to a list, is defined in wsutil.h
+//
+
+//
+// A remote entry for every unique shared resource name (\\server\share)
+// of explicit connections.
+//
+typedef struct _UNC_NAME {
+ DWORD TotalUseCount;
+ DWORD UncNameLength;
+ LPTSTR UncName[1];
+} UNC_NAME, *PUNC_NAME;
+
+//
+// A use entry in the linked list of connections.
+//
+typedef struct _USE_ENTRY {
+ struct _USE_ENTRY *Next;
+ PUNC_NAME Remote;
+ LPTSTR Local;
+ DWORD LocalLength;
+ DWORD UseCount;
+ HANDLE TreeConnection;
+ DWORD ResumeKey;
+ LPTSTR TreeConnectStr;
+} USE_ENTRY, *PUSE_ENTRY;
+
+
+//
+// Enumerated data type to say whether to pause or continue the redirection
+//
+typedef enum _REDIR_OPERATION {
+ PauseRedirection,
+ ContinueRedirection
+} REDIR_OPERATION;
+
+
+//-------------------------------------------------------------------//
+// //
+// Utility functions from useutil.c //
+// //
+//-------------------------------------------------------------------//
+
+NET_API_STATUS
+WsInitUseStructures(
+ VOID
+ );
+
+VOID
+WsDestroyUseStructures(
+ VOID
+ );
+
+VOID
+WsFindInsertLocation(
+ IN PUSE_ENTRY UseList,
+ IN LPTSTR UncName,
+ OUT PUSE_ENTRY *MatchedPointer,
+ OUT PUSE_ENTRY *InsertPointer
+ );
+
+NET_API_STATUS
+WsFindUse(
+ IN PLUID LogonId,
+ IN PUSE_ENTRY UseList,
+ IN LPTSTR UseName,
+ OUT PHANDLE TreeConnection,
+ OUT PUSE_ENTRY *MatchedPointer,
+ OUT PUSE_ENTRY *BackPointer OPTIONAL
+ );
+
+VOID
+WsFindUncName(
+ IN PUSE_ENTRY UseList,
+ IN LPTSTR UncName,
+ OUT PUSE_ENTRY *MatchedPointer,
+ OUT PUSE_ENTRY *BackPointer
+ );
+
+NET_API_STATUS
+WsCreateTreeConnectName(
+ IN LPTSTR UncName,
+ IN DWORD UncNameLength,
+ IN LPTSTR LocalName,
+ OUT PUNICODE_STRING TreeConnectStr
+ );
+
+NET_API_STATUS
+WsOpenCreateConnection(
+ IN PUNICODE_STRING TreeConnectionName,
+ IN LPTSTR UserName OPTIONAL,
+ IN LPTSTR DomainName OPTIONAL,
+ IN LPTSTR Password OPTIONAL,
+ IN ULONG CreateFlags,
+ IN ULONG CreateDisposition,
+ IN ULONG ConnectionType,
+ OUT PHANDLE TreeConnectionHandle,
+ OUT PULONG Information OPTIONAL
+ );
+
+NET_API_STATUS
+WsDeleteConnection(
+ IN PLUID LogonId,
+ IN HANDLE TreeConnection,
+ IN DWORD ForceLevel
+ );
+
+BOOL
+WsRedirectionPaused(
+ IN LPTSTR LocalDeviceName
+ );
+
+VOID
+WsPauseOrContinueRedirection(
+ IN REDIR_OPERATION OperationType
+ );
+
+NET_API_STATUS
+WsCreateSymbolicLink(
+ IN LPWSTR Local,
+ IN DWORD DeviceType,
+ IN LPWSTR TreeConnectStr,
+ IN PUSE_ENTRY UseList
+ );
+
+VOID
+WsDeleteSymbolicLink(
+ IN LPWSTR LocalDeviceName,
+ IN LPWSTR TreeConnectStr
+ );
+
+NET_API_STATUS
+WsUseCheckRemote(
+ IN LPTSTR RemoteResource,
+ OUT LPTSTR UncName,
+ OUT LPDWORD UncNameLength
+ );
+
+NET_API_STATUS
+WsUseCheckLocal(
+ IN LPTSTR LocalDevice,
+ OUT LPTSTR Local,
+ OUT LPDWORD LocalLength
+ );
+
+//-------------------------------------------------------------------//
+// //
+// External global variables //
+// //
+//-------------------------------------------------------------------//
+
+//
+// Use Table
+//
+extern USERS_OBJECT Use;
+
+#endif // ifndef _WSUSE_INCLUDED_
diff --git a/private/net/svcdlls/wkssvc/server/wsutil.c b/private/net/svcdlls/wkssvc/server/wsutil.c
new file mode 100644
index 000000000..01335af97
--- /dev/null
+++ b/private/net/svcdlls/wkssvc/server/wsutil.c
@@ -0,0 +1,692 @@
+/*++
+
+Copyright (c) 1991-1992 Microsoft Corporation
+
+Module Name:
+
+ wsutil.c
+
+Abstract:
+
+ This module contains miscellaneous utility routines used by the
+ Workstation service.
+
+Author:
+
+ Rita Wong (ritaw) 01-Mar-1991
+
+Revision History:
+
+--*/
+
+#include "wsutil.h"
+
+//-------------------------------------------------------------------//
+// //
+// Local function prototypes //
+// //
+//-------------------------------------------------------------------//
+
+STATIC
+NET_API_STATUS
+WsGrowTable(
+ IN PUSERS_OBJECT Users
+ );
+
+//-------------------------------------------------------------------//
+// //
+// Global variables //
+// //
+//-------------------------------------------------------------------//
+
+//
+// Debug trace flag for selecting which trace statements to output
+//
+#if DBG
+
+DWORD WorkstationTrace = 0;
+
+#endif // DBG
+
+
+
+NET_API_STATUS
+WsInitializeUsersObject(
+ IN PUSERS_OBJECT Users
+ )
+/*++
+
+Routine Description:
+
+ This function allocates the table of users, and initializes the resource
+ to serialize access to this table.
+
+Arguments:
+
+ Users - Supplies a pointer to the users object.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ //
+ // Allocate the users table memory so that it can be grown (reallocated)
+ // as more entries are needed.
+ //
+ if ((Users->TableMemory = (HANDLE) LocalAlloc(
+ LMEM_ZEROINIT | LMEM_MOVEABLE,
+ INITIAL_USER_COUNT * sizeof(PER_USER_ENTRY)
+ )) == NULL) {
+ return GetLastError();
+ }
+
+ Users->TableSize = INITIAL_USER_COUNT;
+
+ //
+ // Keep the memory from moving by locking it to a specific location in
+ // virtual memory. When it is necessary to grow this table, which may
+ // result in the virtual memory being relocated, it will be unlocked.
+ //
+ if ((Users->Table = (PPER_USER_ENTRY)
+ LocalLock(Users->TableMemory)) == NULL) {
+ return GetLastError();
+ }
+
+ //
+ // Initialize the resource for the users table.
+ //
+ RtlInitializeResource(&Users->TableResource);
+
+ return NERR_Success;
+}
+
+
+VOID
+WsDestroyUsersObject(
+ IN PUSERS_OBJECT Users
+ )
+/*++
+
+Routine Description:
+
+ This function free the table allocated for logged on users, and deletes
+ the resource used to serialize access to this table.
+
+Arguments:
+
+ Users - Supplies a pointer to the users object.
+
+Return Value:
+
+ None.
+
+--*/
+{
+
+ //
+ // Unlock the memory holding the table to allow us to free it.
+ //
+
+ LocalUnlock(Users->TableMemory);
+
+ (void) LocalFree(Users->TableMemory);
+ RtlDeleteResource(&(Users->TableResource));
+}
+
+
+
+NET_API_STATUS
+WsGetUserEntry(
+ IN PUSERS_OBJECT Users,
+ IN PLUID LogonId,
+ OUT PULONG Index,
+ IN BOOL IsAdd
+ )
+/*++
+
+Routine Description:
+
+ This function searches the table of user entries for one that matches the
+ specified LogonId, and returns the index to the entry found. If none is
+ found, an error is returned if IsAdd is FALSE. If IsAdd is TRUE a new
+ entry in the users table is created for the user and the index to this
+ new entry is returned.
+
+ WARNING: This function assumes that the users table resource has been
+ claimed.
+
+Arguments:
+
+ Users - Supplies a pointer to the users object.
+
+ LogonId - Supplies the pointer to the current user's Logon Id.
+
+ Index - Returns the index to the users table of entry belonging to the
+ current user.
+
+ IsAdd - Supplies flag to indicate whether to add a new entry for the
+ current user if none is found.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NET_API_STATUS status;
+ DWORD i;
+ ULONG FreeEntryIndex = MAXULONG;
+
+
+ for (i = 0; i < Users->TableSize; i++) {
+
+ //
+ // If the LogonId matches the entry in the UsersTable, we've found the
+ // correct user entry.
+ //
+ if (RtlEqualLuid(LogonId, &Users->Table[i].LogonId)) {
+
+ *Index = i;
+ return NERR_Success;
+
+ }
+ else if (FreeEntryIndex == MAXULONG && Users->Table[i].List == NULL) {
+ //
+ // Save away first unused entry in table.
+ //
+ FreeEntryIndex = i;
+ }
+ }
+
+ if (! IsAdd) {
+ //
+ // Current user is not found in users table and we are told not to
+ // create a new entry
+ //
+ return NERR_UserNotFound;
+ }
+
+ //
+ // Could not find an empty entry in the UsersTable, need to grow
+ //
+ if (FreeEntryIndex == MAXULONG) {
+
+ if ((status = WsGrowTable(Users)) != NERR_Success) {
+ return status;
+ }
+
+ FreeEntryIndex = i;
+ }
+
+ //
+ // Create a new entry for current user
+ //
+ RtlCopyLuid(&Users->Table[FreeEntryIndex].LogonId, LogonId);
+ *Index = FreeEntryIndex;
+
+ return NERR_Success;
+}
+
+
+
+STATIC
+NET_API_STATUS
+WsGrowTable(
+ IN PUSERS_OBJECT Users
+ )
+/*++
+
+Routine Description:
+
+ This function grows the users table to accomodate more users.
+
+ WARNING: This function assumes that the users table resource has been
+ claimed.
+
+Arguments:
+
+ Users - Supplies a pointer to the users object.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ //
+ // Unlock the Use Table virtual memory so that Win32 can move it
+ // around to find a larger piece of contiguous virtual memory if
+ // necessary.
+ //
+ LocalUnlock(Users->TableMemory);
+
+ //
+ // Grow users table
+ //
+ Users->TableMemory = LocalReAlloc(
+ Users->TableMemory,
+ (Users->TableSize + GROW_USER_COUNT)
+ * sizeof(PER_USER_ENTRY),
+ LMEM_ZEROINIT | LMEM_MOVEABLE
+ );
+
+ if (Users->TableMemory == NULL) {
+ return GetLastError();
+ }
+
+ //
+ // Update new size of Use Table
+ //
+ Users->TableSize += GROW_USER_COUNT;
+
+ //
+ // Lock Use Table virtual memory so that it cannot be moved
+ //
+ if ((Users->Table = (PPER_USER_ENTRY)
+ LocalLock(Users->TableMemory)) == NULL) {
+ return GetLastError();
+ }
+
+ return NERR_Success;
+}
+
+
+
+NET_API_STATUS
+WsMapStatus(
+ IN NTSTATUS NtStatus
+ )
+/*++
+
+Routine Description:
+
+ This function takes an NT status code and maps it to the appropriate
+ error code expected from calling a LAN Man API.
+
+Arguments:
+
+ NtStatus - Supplies the NT status.
+
+Return Value:
+
+ Returns the appropriate LAN Man error code for the NT status.
+
+--*/
+{
+ //
+ // A small optimization for the most common case.
+ //
+ if (NtStatus == STATUS_SUCCESS) {
+ return NERR_Success;
+ }
+
+ switch (NtStatus) {
+ case STATUS_OBJECT_NAME_COLLISION:
+ return ERROR_ALREADY_ASSIGNED;
+
+ case STATUS_OBJECT_NAME_NOT_FOUND:
+ return NERR_UseNotFound;
+
+ case STATUS_IMAGE_ALREADY_LOADED:
+ case STATUS_REDIRECTOR_STARTED:
+ return ERROR_SERVICE_ALREADY_RUNNING;
+
+ case STATUS_REDIRECTOR_HAS_OPEN_HANDLES:
+ return ERROR_REDIRECTOR_HAS_OPEN_HANDLES;
+
+ default:
+ return NetpNtStatusToApiStatus(NtStatus);
+ }
+
+}
+
+
+
+int
+WsCompareString(
+ IN LPTSTR String1,
+ IN DWORD Length1,
+ IN LPTSTR String2,
+ IN DWORD Length2
+ )
+/*++
+
+Routine Description:
+
+ This function compares two strings based on their lengths. The return
+ value indicates if the strings are equal or String1 is less than String2
+ or String1 is greater than String2.
+
+ This function is a modified version of RtlCompareString.
+
+Arguments:
+
+ String1 - Supplies the pointer to the first string.
+
+ Length1 - Supplies the length of String1 in characters.
+
+ String2 - Supplies the pointer to the second string.
+
+ Length2 - Supplies the length of String2 in characters.
+
+Return Value:
+
+ Signed value that gives the results of the comparison:
+
+ 0 - String1 equals String2
+
+ < 0 - String1 less than String2
+
+ > 0 - String1 greater than String2
+
+
+--*/
+{
+ TCHAR Char1, Char2;
+ int CharDiff;
+
+ while (Length1 && Length2) {
+
+ Char1 = *String1++;
+ Char2 = *String2++;
+
+ if ((CharDiff = (Char1 - Char2)) != 0) {
+ return CharDiff;
+ }
+
+ Length1--;
+ Length2--;
+ }
+
+ return Length1 - Length2;
+}
+
+int
+WsCompareStringU(
+ IN LPWSTR String1,
+ IN DWORD Length1,
+ IN LPTSTR String2,
+ IN DWORD Length2
+ )
+{
+ UNICODE_STRING S1;
+ UNICODE_STRING S2;
+ int rValue;
+
+
+ S1.Length =
+ S1.MaximumLength = (USHORT) (Length1 * sizeof(WCHAR));
+ S1.Buffer = String1;
+
+ S2.Length =
+ S2.MaximumLength = (USHORT) (Length2 * sizeof(WCHAR));
+ S2.Buffer = String2;
+
+ rValue = RtlCompareUnicodeString(&S1, &S2, TRUE);
+
+ return(rValue);
+}
+
+
+BOOL
+WsCopyStringToBuffer(
+ IN PUNICODE_STRING SourceString,
+ IN LPBYTE FixedPortion,
+ IN OUT LPTSTR *EndOfVariableData,
+ OUT LPTSTR *DestinationStringPointer
+ )
+/*++
+
+Routine Description:
+
+ This function converts the unicode source string to ANSI string (if
+ we haven't flipped the unicode switch yet) and calls
+ NetpCopyStringToBuffer.
+
+Arguments:
+
+ SourceString - Supplies a pointer to the source string to copy into the
+ output buffer. If String is null then a pointer to a zero terminator
+ is inserted into output buffer.
+
+ FixedDataEnd - Supplies a pointer to just after the end of the last
+ fixed structure in the buffer.
+
+ EndOfVariableData - Supplies an address to a pointer to just after the
+ last position in the output buffer that variable data can occupy.
+ Returns a pointer to the string written in the output buffer.
+
+ DestinationStringPointer - Supplies a pointer to the place in the fixed
+ portion of the output buffer where a pointer to the variable data
+ should be written.
+
+Return Value:
+
+ Returns TRUE if string fits into output buffer, FALSE otherwise.
+
+--*/
+{
+ if (! NetpCopyStringToBuffer(
+ SourceString->Buffer,
+ SourceString->Length / sizeof(WCHAR),
+ FixedPortion,
+ EndOfVariableData,
+ DestinationStringPointer
+ )) {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+NET_API_STATUS
+WsImpersonateClient(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This function calls RpcImpersonateClient to impersonate the current caller
+ of an API.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NET_API_STATUS status;
+
+ if ((status = RpcImpersonateClient(NULL)) != NERR_Success) {
+ NetpKdPrint(("[Wksta] Fail to impersonate client 0x%x\n", status));
+ }
+
+ return status;
+}
+
+
+NET_API_STATUS
+WsRevertToSelf(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This function calls RpcRevertToSelf to undo an impersonation.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NET_API_STATUS status;
+
+
+ if (( status = RpcRevertToSelf()) != NERR_Success) {
+ NetpKdPrint(("[Wksta] Fail to revert to self 0x%x\n", status));
+ NetpAssert(FALSE);
+ }
+
+ return status;
+}
+
+
+NET_API_STATUS
+WsImpersonateAndGetLogonId(
+ OUT PLUID LogonId
+ )
+/*++
+
+Routine Description:
+
+ This function gets the logon id of the current thread.
+
+Arguments:
+
+ LogonId - Returns the logon id of the current process.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NET_API_STATUS status;
+ NTSTATUS ntstatus;
+ HANDLE CurrentThreadToken;
+ TOKEN_STATISTICS TokenStats;
+ ULONG ReturnLength;
+
+
+ if ((status = WsImpersonateClient()) != NERR_Success) {
+ return status;
+ }
+
+ ntstatus = NtOpenThreadToken(
+ NtCurrentThread(),
+ TOKEN_QUERY,
+ TRUE, // Use workstation service's security
+ // context to open thread token
+ &CurrentThreadToken
+ );
+
+ status = NetpNtStatusToApiStatus(ntstatus);
+
+ if (! NT_SUCCESS(ntstatus)) {
+ NetpKdPrint(("[Wksta] Cannot open the current thread token %08lx\n",
+ ntstatus));
+ goto RevertToSelf;
+ }
+
+ //
+ // Get the logon id of the current thread
+ //
+ ntstatus = NtQueryInformationToken(
+ CurrentThreadToken,
+ TokenStatistics,
+ (PVOID) &TokenStats,
+ sizeof(TokenStats),
+ &ReturnLength
+ );
+
+ status = NetpNtStatusToApiStatus(ntstatus);
+
+ if (! NT_SUCCESS(ntstatus)) {
+ NetpKdPrint(("[Wksta] Cannot query current thread's token %08lx\n",
+ ntstatus));
+ NtClose(CurrentThreadToken);
+ goto RevertToSelf;
+ }
+
+ RtlCopyLuid(LogonId, &TokenStats.AuthenticationId);
+
+ NtClose(CurrentThreadToken);
+
+
+RevertToSelf:
+
+ WsRevertToSelf();
+
+ return status;
+}
+
+
+NET_API_STATUS
+WsOpenDestinationMailslot(
+ IN LPWSTR TargetName,
+ IN LPWSTR MailslotName,
+ OUT PHANDLE MailslotHandle
+ )
+/*++
+
+Routine Description:
+
+ This function combines the target domain or computer name and the mailslot
+ name to form the destination mailslot name. It then opens this destination
+ mailslot and returns a handle to it.
+
+Arguments:
+
+ TargetName - Supplies the name of a domain or computer which we want to
+ target when sending a mailslot message.
+
+ MailslotName - Supplies the name of the mailslot.
+
+ MailslotHandle - Returns the handle to the destination mailslot of
+ \\TargetName\MailslotName.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NET_API_STATUS status = NERR_Success;
+ LPWSTR DestinationMailslot;
+
+
+ if ((DestinationMailslot = (LPWSTR) LocalAlloc(
+ LMEM_ZEROINIT,
+ (UINT) (wcslen(TargetName) +
+ wcslen(MailslotName) +
+ 3) * sizeof(WCHAR)
+ )) == NULL) {
+ return GetLastError();
+ }
+
+ wcscpy(DestinationMailslot, L"\\\\");
+ wcscat(DestinationMailslot, TargetName);
+ wcscat(DestinationMailslot, MailslotName);
+
+ if ((*MailslotHandle = (HANDLE) CreateFileW(
+ DestinationMailslot,
+ GENERIC_WRITE,
+ FILE_SHARE_WRITE | FILE_SHARE_READ,
+ (LPSECURITY_ATTRIBUTES) NULL,
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL
+ )) == (HANDLE) MAXULONG) {
+ status = GetLastError();
+ NetpKdPrint(("[Wksta] Error opening mailslot %s %lu",
+ DestinationMailslot, status));
+ }
+
+ (void) LocalFree(DestinationMailslot);
+
+ return status;
+}
diff --git a/private/net/svcdlls/wkssvc/server/wsutil.h b/private/net/svcdlls/wkssvc/server/wsutil.h
new file mode 100644
index 000000000..a0494757e
--- /dev/null
+++ b/private/net/svcdlls/wkssvc/server/wsutil.h
@@ -0,0 +1,157 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ wsutil.h
+
+Abstract:
+
+ Private header file for the NT Workstation service included by every module
+ module of the Workstation service.
+
+Author:
+
+ Rita Wong (ritaw) 15-Feb-1991
+
+Revision History:
+
+--*/
+
+#ifndef _WSUTIL_INCLUDED_
+#define _WSUTIL_INCLUDED_
+
+#if _PNP_POWER
+#define RDR_PNP_POWER 1
+#endif
+
+//
+// This include file will be included by tstring.h if Unicode
+// is defined.
+//
+#ifndef UNICODE
+#include <stdlib.h> // Unicode string functions
+#endif
+
+#include "ws.h"
+
+
+#define INITIAL_USER_COUNT 10 // Initial table size is for
+ // number of logged on users
+
+#define GROW_USER_COUNT 5 // When initial size is not enough,
+ // grow table for additional users
+
+
+#define MAX_SINGLE_MESSAGE_SIZE 128 // Maximum size of a datagram message
+
+
+//
+// An invalid parameter is encountered. Return the value to identify
+// the parameter at fault.
+//
+#define RETURN_INVALID_PARAMETER(ErrorParameter, ParameterId) \
+ if (ARGUMENT_PRESENT(ErrorParameter)) { \
+ *ErrorParameter = ParameterId; \
+ } \
+ return ERROR_INVALID_PARAMETER;
+
+
+
+//-------------------------------------------------------------------//
+// //
+// Type definitions //
+// //
+//-------------------------------------------------------------------//
+
+typedef struct _PER_USER_ENTRY {
+ PVOID List; // Pointer to linked list of user data
+ LUID LogonId; // Logon Id of user
+} PER_USER_ENTRY, *PPER_USER_ENTRY;
+
+typedef struct _USERS_OBJECT {
+ PPER_USER_ENTRY Table; // Table of users
+ RTL_RESOURCE TableResource; // To serialize access to Table
+ HANDLE TableMemory; // Relocatable Table memory
+ DWORD TableSize; // Size of Table
+} USERS_OBJECT, *PUSERS_OBJECT;
+
+
+//-------------------------------------------------------------------//
+// //
+// Function prototypes of utility routines found in wsutil.c //
+// //
+//-------------------------------------------------------------------//
+
+NET_API_STATUS
+WsInitializeUsersObject(
+ IN PUSERS_OBJECT Users
+ );
+
+VOID
+WsDestroyUsersObject(
+ IN PUSERS_OBJECT Users
+ );
+
+NET_API_STATUS
+WsGetUserEntry(
+ IN PUSERS_OBJECT Users,
+ IN PLUID LogonId,
+ OUT PULONG Index,
+ IN BOOL IsAdd
+ );
+
+NET_API_STATUS
+WsMapStatus(
+ IN NTSTATUS NtStatus
+ );
+
+int
+WsCompareString(
+ IN LPTSTR String1,
+ IN DWORD Length1,
+ IN LPTSTR String2,
+ IN DWORD Length2
+ );
+
+int
+WsCompareStringU(
+ IN LPWSTR String1,
+ IN DWORD Length1,
+ IN LPTSTR String2,
+ IN DWORD Length2
+ );
+
+BOOL
+WsCopyStringToBuffer(
+ IN PUNICODE_STRING SourceString,
+ IN LPBYTE FixedPortion,
+ IN OUT LPTSTR *EndOfVariableData,
+ OUT LPTSTR *DestinationStringPointer
+ );
+
+NET_API_STATUS
+WsImpersonateClient(
+ VOID
+ );
+
+NET_API_STATUS
+WsRevertToSelf(
+ VOID
+ );
+
+NET_API_STATUS
+WsImpersonateAndGetLogonId(
+ OUT PLUID LogonId
+ );
+
+NET_API_STATUS
+WsOpenDestinationMailslot(
+ IN LPWSTR TargetName,
+ IN LPWSTR MailslotName,
+ OUT PHANDLE MailslotHandle
+ );
+
+
+#endif // ifndef _WSUTIL_INCLUDED_
diff --git a/private/net/svcdlls/wkssvc/server/wswksta.h b/private/net/svcdlls/wkssvc/server/wswksta.h
new file mode 100644
index 000000000..cefae8d3e
--- /dev/null
+++ b/private/net/svcdlls/wkssvc/server/wswksta.h
@@ -0,0 +1,80 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ wswksta.h
+
+Abstract:
+
+ Private header file to be included by Workstation service module that
+ implement the NetWksta APIs.
+
+Author:
+
+ Rita Wong (ritaw) 05-Mar-1991
+
+Revision History:
+
+--*/
+
+#ifndef _WSWKSTA_INCLUDED_
+#define _WSWKSTA_INCLUDED_
+
+typedef struct _WSNAME_RECORD {
+ LPTSTR Name;
+ DWORD Size;
+ BOOL IsAdded;
+} WSNAME_RECORD, *PWSNAME_RECORD;
+
+typedef struct _WSPER_USER_INFO {
+ PMSV1_0_GETUSERINFO_RESPONSE LsaUserInfo;
+ PDGRECEIVE_NAMES DgrNames;
+ DWORD DgrNamesCount;
+} WSPER_USER_INFO, *PWSPER_USER_INFO;
+
+#define DGR_NAME_DELETED (DGRECEIVER_NAME_TYPE) MAXULONG
+
+
+#define SYSTEM_INFO_FIXED_LENGTH(Level) \
+ (DWORD)((Level == 102) ? sizeof(WKSTA_INFO_102) : \
+ sizeof(WKSTA_INFO_101))
+
+
+#define SET_SYSTEM_INFO_POINTER(WkstaInfo, ResultBuffer) \
+ WkstaInfo->WkstaInfo100 = (PWKSTA_INFO_100) ResultBuffer;
+
+#define SET_USER_INFO_POINTER(UserInfo, ResultBuffer) \
+ UserInfo->UserInfo0 = (PWKSTA_USER_INFO_0) ResultBuffer;
+
+#define SET_TRANSPORT_ENUM_POINTER(TransportInfo, ResultBuffer, NumRead) \
+ {TransportInfo->WkstaTransportInfo.Level0->Buffer = \
+ (PWKSTA_TRANSPORT_INFO_0) ResultBuffer; \
+ TransportInfo->WkstaTransportInfo.Level0->EntriesRead = NumRead; }
+
+//
+// Length of fixed size portion of a user info structure
+//
+#define USER_FIXED_LENGTH(Level) \
+ (DWORD) \
+ ((Level == 0) ? sizeof(WKSTA_USER_INFO_0) : \
+ ((Level == 1) ? sizeof(WKSTA_USER_INFO_1) : \
+ sizeof(WKSTA_USER_INFO_1101)))
+
+#define FIXED_PLUS_LSA_SIZE(Level, UserNameSize, LogonDomainSize, \
+ LogonServerSize) \
+ (DWORD) \
+ ((Level == 0) ? UserNameSize + sizeof(WKSTA_USER_INFO_0) : \
+ UserNameSize + LogonDomainSize + LogonServerSize + \
+ sizeof(WKSTA_USER_INFO_1))
+
+
+NET_API_STATUS
+WsUpdateRedirToMatchWksta(
+ IN DWORD Parmnum,
+ OUT LPDWORD ErrorParameter OPTIONAL
+ );
+
+
+#endif // _WSWKSTA_INCLUDED_
diff --git a/private/net/svcdlls/wkssvc/wkssvc.acf b/private/net/svcdlls/wkssvc/wkssvc.acf
new file mode 100644
index 000000000..d8aa94d84
--- /dev/null
+++ b/private/net/svcdlls/wkssvc/wkssvc.acf
@@ -0,0 +1,53 @@
+[ implicit_handle( handle_t wkssvc_bhandle )]
+
+interface wkssvc
+
+{
+
+typedef [allocate(all_nodes)] LPWKSTA_INFO_100;
+typedef [allocate(all_nodes)] LPWKSTA_INFO_101;
+typedef [allocate(all_nodes)] LPWKSTA_INFO_102;
+typedef [allocate(all_nodes)] LPWKSTA_INFO_502;
+
+typedef [allocate(all_nodes)] LPWKSTA_INFO_1010;
+typedef [allocate(all_nodes)] LPWKSTA_INFO_1011;
+typedef [allocate(all_nodes)] LPWKSTA_INFO_1012;
+typedef [allocate(all_nodes)] LPWKSTA_INFO_1013;
+typedef [allocate(all_nodes)] LPWKSTA_INFO_1018;
+typedef [allocate(all_nodes)] LPWKSTA_INFO_1023;
+typedef [allocate(all_nodes)] LPWKSTA_INFO_1033;
+
+typedef [allocate(all_nodes)] LPWKSTA_INFO_1041;
+typedef [allocate(all_nodes)] LPWKSTA_INFO_1042;
+typedef [allocate(all_nodes)] LPWKSTA_INFO_1043;
+typedef [allocate(all_nodes)] LPWKSTA_INFO_1044;
+typedef [allocate(all_nodes)] LPWKSTA_INFO_1045;
+typedef [allocate(all_nodes)] LPWKSTA_INFO_1046;
+typedef [allocate(all_nodes)] LPWKSTA_INFO_1047;
+typedef [allocate(all_nodes)] LPWKSTA_INFO_1048;
+typedef [allocate(all_nodes)] LPWKSTA_INFO_1049;
+typedef [allocate(all_nodes)] LPWKSTA_INFO_1050;
+typedef [allocate(all_nodes)] LPWKSTA_INFO_1051;
+typedef [allocate(all_nodes)] LPWKSTA_INFO_1052;
+typedef [allocate(all_nodes)] LPWKSTA_INFO_1053;
+typedef [allocate(all_nodes)] LPWKSTA_INFO_1054;
+typedef [allocate(all_nodes)] LPWKSTA_INFO_1055;
+typedef [allocate(all_nodes)] LPWKSTA_INFO_1056;
+typedef [allocate(all_nodes)] LPWKSTA_INFO_1057;
+typedef [allocate(all_nodes)] LPWKSTA_INFO_1058;
+typedef [allocate(all_nodes)] LPWKSTA_INFO_1059;
+typedef [allocate(all_nodes)] LPWKSTA_INFO_1060;
+typedef [allocate(all_nodes)] LPWKSTA_INFO_1061;
+typedef [allocate(all_nodes)] LPWKSTA_INFO_1062;
+
+typedef [allocate(all_nodes)] LPWKSTA_USER_INFO_0;
+typedef [allocate(all_nodes)] LPWKSTA_USER_INFO_1;
+typedef [allocate(all_nodes)] LPWKSTA_USER_INFO_1101;
+
+typedef [allocate(all_nodes)] LPWKSTA_TRANSPORT_INFO_0;
+
+typedef [allocate(all_nodes)] LPUSE_INFO_0;
+typedef [allocate(all_nodes)] LPUSE_INFO_1;
+typedef [allocate(all_nodes)] LPUSE_INFO_2;
+
+}
diff --git a/private/net/svcdlls/wkssvc/wkssvc.idl b/private/net/svcdlls/wkssvc/wkssvc.idl
new file mode 100644
index 000000000..5bb4d060b
--- /dev/null
+++ b/private/net/svcdlls/wkssvc/wkssvc.idl
@@ -0,0 +1,420 @@
+/*++
+
+Copyright (c) 1990 Microsoft Corporation
+
+Module Name:
+
+ wkssvc.idl
+
+Abstract:
+
+ Contains the Netr (Net Remote) RPC interface specification for the APIs
+ associated with the Workstation service. This includes APIs from the
+ following catagories:
+ NetWksta
+ NetUse
+ Also contains the RPC specific data structures for these API.
+
+Author:
+
+ Rita Wong (ritaw) 06-May-1991
+
+Environment:
+
+ User Mode - Win32 - MIDL
+
+Revision History:
+
+ 21-Jan-1992 rfirth
+ * Removed NET_API_FUNCTION from all interfaces
+--*/
+
+//
+// Interface Attributes
+//
+
+[
+ uuid(6BFFD098-A112-3610-9833-46C3F87E345A),
+ version(1.0),
+#ifdef __midl
+ ms_union,
+#endif // __midl
+ pointer_default(unique)
+]
+
+
+//
+// Interface Keyword
+//
+
+interface wkssvc
+
+
+//
+// Interface Body
+//
+
+{
+
+import "imports.idl";
+#include <lmcons.h>
+
+//
+// BUGBUG - take this definition out when midl understands LPWSTR etc
+//
+
+#ifdef UNICODE
+#define LPTSTR wchar_t*
+#endif
+
+
+//
+// ---------------------------------------------------------------//
+//
+
+
+typedef [handle] LPTSTR WKSSVC_IMPERSONATE_HANDLE;
+
+typedef [handle] LPTSTR WKSSVC_IDENTIFY_HANDLE;
+
+
+//
+// Workstation APIs
+//
+
+
+//
+// NetrWkstaGetInfo and NetrWkstaSetInfo
+//
+typedef [switch_type(unsigned long)] union _WKSTA_INFO {
+ [case(100)]
+ LPWKSTA_INFO_100 WkstaInfo100;
+ [case(101)]
+ LPWKSTA_INFO_101 WkstaInfo101;
+ [case(102)]
+ LPWKSTA_INFO_102 WkstaInfo102;
+ [case(1010)]
+ LPWKSTA_INFO_1010 WkstaInfo1010;
+ [case(1011)]
+ LPWKSTA_INFO_1011 WkstaInfo1011;
+ [case(1012)]
+ LPWKSTA_INFO_1012 WkstaInfo1012;
+ [case(1013)]
+ LPWKSTA_INFO_1013 WkstaInfo1013;
+ [case(1018)]
+ LPWKSTA_INFO_1018 WkstaInfo1018;
+ [case(1023)]
+ LPWKSTA_INFO_1023 WkstaInfo1023;
+ [case(1033)]
+ LPWKSTA_INFO_1033 WkstaInfo1033;
+ [case(1041)]
+ LPWKSTA_INFO_1041 WkstaInfo1041;
+ [case(1042)]
+ LPWKSTA_INFO_1042 WkstaInfo1042;
+ [case(1043)]
+ LPWKSTA_INFO_1043 WkstaInfo1043;
+ [case(1044)]
+ LPWKSTA_INFO_1044 WkstaInfo1044;
+ [case(1045)]
+ LPWKSTA_INFO_1045 WkstaInfo1045;
+ [case(1046)]
+ LPWKSTA_INFO_1046 WkstaInfo1046;
+ [case(1047)]
+ LPWKSTA_INFO_1047 WkstaInfo1047;
+ [case(1048)]
+ LPWKSTA_INFO_1048 WkstaInfo1048;
+ [case(1049)]
+ LPWKSTA_INFO_1049 WkstaInfo1049;
+ [case(1050)]
+ LPWKSTA_INFO_1050 WkstaInfo1050;
+ [case(1051)]
+ LPWKSTA_INFO_1051 WkstaInfo1051;
+ [case(1052)]
+ LPWKSTA_INFO_1052 WkstaInfo1052;
+ [case(1053)]
+ LPWKSTA_INFO_1053 WkstaInfo1053;
+ [case(1054)]
+ LPWKSTA_INFO_1054 WkstaInfo1054;
+ [case(1055)]
+ LPWKSTA_INFO_1055 WkstaInfo1055;
+ [case(1056)]
+ LPWKSTA_INFO_1056 WkstaInfo1056;
+ [case(1057)]
+ LPWKSTA_INFO_1057 WkstaInfo1057;
+ [case(1058)]
+ LPWKSTA_INFO_1058 WkstaInfo1058;
+ [case(1059)]
+ LPWKSTA_INFO_1059 WkstaInfo1059;
+ [case(1060)]
+ LPWKSTA_INFO_1060 WkstaInfo1060;
+ [case(1061)]
+ LPWKSTA_INFO_1061 WkstaInfo1061;
+ [case(1062)]
+ LPWKSTA_INFO_1062 WkstaInfo1062;
+ [case(502)]
+ LPWKSTA_INFO_502 WkstaInfo502;
+ [default]
+ ;
+} WKSTA_INFO, *PWKSTA_INFO, *LPWKSTA_INFO;
+
+NET_API_STATUS
+NetrWkstaGetInfo (
+ [in,string,unique] WKSSVC_IDENTIFY_HANDLE ServerName,
+ [in] DWORD Level,
+ [out, switch_is(Level)] LPWKSTA_INFO WkstaInfo
+ );
+
+NET_API_STATUS
+NetrWkstaSetInfo (
+ [in,string,unique] WKSSVC_IDENTIFY_HANDLE ServerName,
+ [in] DWORD Level,
+ [in, switch_is(Level)] LPWKSTA_INFO WkstaInfo,
+ [in,out,unique] LPDWORD ErrorParameter
+ );
+
+
+
+//
+// NetrWkstaUserEnum
+//
+typedef struct _WKSTA_USER_INFO_0_CONTAINER {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] LPWKSTA_USER_INFO_0 Buffer;
+} WKSTA_USER_INFO_0_CONTAINER, *PWKSTA_USER_INFO_0_CONTAINER,
+ *LPWKSTA_USER_INFO_0_CONTAINER;
+
+
+typedef struct _WKSTA_USER_INFO_1_CONTAINER {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] LPWKSTA_USER_INFO_1 Buffer;
+} WKSTA_USER_INFO_1_CONTAINER, *PWKSTA_USER_INFO_1_CONTAINER,
+ *LPWKSTA_USER_INFO_1_CONTAINER;
+
+typedef struct _WKSTA_USER_ENUM_STRUCT {
+ DWORD Level;
+ [switch_is(Level)] union _WKSTA_USER_ENUM_UNION {
+ [case(0)]
+ LPWKSTA_USER_INFO_0_CONTAINER Level0;
+ [case(1)]
+ LPWKSTA_USER_INFO_1_CONTAINER Level1;
+ [default]
+ ;
+ } WkstaUserInfo;
+} WKSTA_USER_ENUM_STRUCT, *PWKSTA_USER_ENUM_STRUCT, *LPWKSTA_USER_ENUM_STRUCT;
+
+NET_API_STATUS
+NetrWkstaUserEnum (
+ [in,string,unique] WKSSVC_IDENTIFY_HANDLE ServerName,
+ [in,out] LPWKSTA_USER_ENUM_STRUCT UserInfo,
+ [in] DWORD PreferredMaximumLength,
+ [out] LPDWORD TotalEntries,
+ [in,out,unique] LPDWORD ResumeHandle
+ );
+
+
+
+//
+// NetrWkstaUserGetInfo and NetrWkstaUserSetInfo
+//
+typedef [switch_type(unsigned long)] union _WKSTA_USER_INFO {
+ [case(0)]
+ LPWKSTA_USER_INFO_0 UserInfo0;
+ [case(1)]
+ LPWKSTA_USER_INFO_1 UserInfo1;
+ [case(1101)]
+ LPWKSTA_USER_INFO_1101 UserInfo1101;
+ [default]
+ ;
+} WKSTA_USER_INFO, *PWKSTA_USER_INFO, *LPWKSTA_USER_INFO;
+
+NET_API_STATUS
+NetrWkstaUserGetInfo (
+ [in,string,unique] WKSSVC_IDENTIFY_HANDLE Reserved,
+ [in] DWORD Level,
+ [out, switch_is(Level)] LPWKSTA_USER_INFO UserInfo
+ );
+
+NET_API_STATUS
+NetrWkstaUserSetInfo (
+ [in,string,unique] WKSSVC_IDENTIFY_HANDLE Reserved,
+ [in] DWORD Level,
+ [in, switch_is(Level)] LPWKSTA_USER_INFO UserInfo,
+ [in,out,unique] LPDWORD ErrorParameter
+ );
+
+//
+// NetrWkstaTransportEnum
+//
+typedef struct _WKSTA_TRANSPORT_INFO_0_CONTAINER {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] LPWKSTA_TRANSPORT_INFO_0 Buffer;
+} WKSTA_TRANSPORT_INFO_0_CONTAINER, *PWKSTA_TRANSPORT_INFO_0_CONTAINER,
+ *LPWKSTA_TRANSPORT_INFO_0_CONTAINER;
+
+typedef struct _WKSTA_TRANSPORT_ENUM_STRUCT {
+ DWORD Level;
+ [switch_is(Level)] union _WKSTA_TRANSPORT_ENUM_UNION {
+ [case(0)]
+ LPWKSTA_TRANSPORT_INFO_0_CONTAINER Level0;
+ [default]
+ ;
+ } WkstaTransportInfo;
+} WKSTA_TRANSPORT_ENUM_STRUCT, *PWKSTA_TRANSPORT_ENUM_STRUCT,
+ *LPWKSTA_TRANSPORT_ENUM_STRUCT;
+
+NET_API_STATUS
+NetrWkstaTransportEnum (
+ [in,string,unique] WKSSVC_IDENTIFY_HANDLE ServerName,
+ [in,out] LPWKSTA_TRANSPORT_ENUM_STRUCT TransportInfo,
+ [in] DWORD PreferredMaximumLength,
+ [out] LPDWORD TotalEntries,
+ [in,out,unique] LPDWORD ResumeHandle
+ );
+
+//
+// NetrWkstaTransportAdd
+//
+NET_API_STATUS
+NetrWkstaTransportAdd (
+ [in,string,unique] WKSSVC_IDENTIFY_HANDLE ServerName,
+ [in] DWORD Level,
+ [in] LPWKSTA_TRANSPORT_INFO_0 TransportInfo,
+ [in,out,unique] LPDWORD ErrorParameter
+ );
+
+//
+// NetrWkstaTransportDel
+//
+NET_API_STATUS
+NetrWkstaTransportDel (
+ [in,string,unique] WKSSVC_IDENTIFY_HANDLE ServerName,
+ [in,string,unique] LPTSTR TransportName,
+ [in] DWORD ForceLevel
+ );
+
+
+
+//
+// NetrUseAdd and NetrUseGetInfo
+//
+typedef [switch_type(unsigned long)] union _USE_INFO {
+ [case(0)]
+ LPUSE_INFO_0 UseInfo0;
+ [case(1)]
+ LPUSE_INFO_1 UseInfo1;
+ [case(2)]
+ LPUSE_INFO_2 UseInfo2;
+ [case(3)]
+ LPUSE_INFO_3 UseInfo3;
+ [default]
+ ;
+} USE_INFO, *PUSE_INFO, *LPUSE_INFO;
+
+NET_API_STATUS
+NetrUseAdd (
+ [in,string,unique] WKSSVC_IMPERSONATE_HANDLE ServerName,
+ [in] DWORD Level,
+ [in, switch_is(Level)] LPUSE_INFO InfoStruct,
+ [in,out,unique] LPDWORD ErrorParameter
+ );
+
+NET_API_STATUS
+NetrUseGetInfo (
+ [in,string,unique] WKSSVC_IMPERSONATE_HANDLE ServerName,
+ [in,string] LPTSTR UseName,
+ [in] DWORD Level,
+ [out, switch_is(Level)] LPUSE_INFO InfoStruct
+ );
+
+
+
+//
+// NetrUseDel
+//
+NET_API_STATUS
+NetrUseDel (
+ [in,string,unique] WKSSVC_IMPERSONATE_HANDLE ServerName,
+ [in,string] LPTSTR UseName,
+ [in] DWORD ForceLevel
+ );
+
+
+//
+// NetrUseEnum
+//
+typedef struct _USE_INFO_0_CONTAINER {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] LPUSE_INFO_0 Buffer;
+} USE_INFO_0_CONTAINER, *PUSE_INFO_0_CONTAINER, *LPUSE_INFO_0_CONTAINER;
+
+typedef struct _USE_INFO_1_CONTAINER {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] LPUSE_INFO_1 Buffer;
+} USE_INFO_1_CONTAINER, *PUSE_INFO_1_CONTAINER, *LPUSE_INFO_1_CONTAINER;
+
+typedef struct _USE_INFO_2_CONTAINER {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] LPUSE_INFO_2 Buffer;
+} USE_INFO_2_CONTAINER, *PUSE_INFO_2_CONTAINER, *LPUSE_INFO_2_CONTAINER;
+
+typedef struct _USE_ENUM_STRUCT {
+ DWORD Level;
+ [switch_is(Level)] union _USE_ENUM_UNION {
+ [case(0)]
+ LPUSE_INFO_0_CONTAINER Level0;
+ [case(1)]
+ LPUSE_INFO_1_CONTAINER Level1;
+ [case(2)]
+ LPUSE_INFO_2_CONTAINER Level2;
+ [default]
+ ;
+ } UseInfo;
+
+}USE_ENUM_STRUCT, *PUSE_ENUM_STRUCT, *LPUSE_ENUM_STRUCT;
+
+NET_API_STATUS
+NetrUseEnum (
+ [in,string,unique] WKSSVC_IDENTIFY_HANDLE ServerName,
+ [in,out] LPUSE_ENUM_STRUCT InfoStruct,
+ [in] DWORD PreferedMaximumLength,
+ [out] LPDWORD TotalEntries,
+ [in,out,unique] LPDWORD ResumeHandle
+ );
+
+//
+// NetrMessageBufferSend
+//
+NET_API_STATUS
+NetrMessageBufferSend (
+ [in,string,unique] WKSSVC_IMPERSONATE_HANDLE ServerName,
+ [in,string] LPTSTR MessageName,
+ [in,string,unique] LPTSTR FromName,
+ [in,size_is(MessageSize)] LPBYTE Message,
+ [in] DWORD MessageSize
+ );
+
+NET_API_STATUS
+NetrWorkstationStatisticsGet(
+ [in,string,unique] WKSSVC_IDENTIFY_HANDLE ServerName,
+ [in,string,unique] LPTSTR ServiceName,
+ [in] DWORD Level,
+ [in] DWORD Options,
+ [out] LPSTAT_WORKSTATION_0* Buffer
+ );
+
+//
+// I_NetrLogonDomainNameAdd
+//
+NET_API_STATUS
+I_NetrLogonDomainNameAdd(
+ [in,string] WKSSVC_IDENTIFY_HANDLE LogonDomain
+ );
+
+//
+// I_NetrLogonDomainNameDel
+//
+NET_API_STATUS
+I_NetrLogonDomainNameDel(
+ [in,string] WKSSVC_IDENTIFY_HANDLE LogonDomain
+ );
+}
diff --git a/private/net/svcdlls/wkssvc/wsnames.h b/private/net/svcdlls/wkssvc/wsnames.h
new file mode 100644
index 000000000..365b4b3c1
--- /dev/null
+++ b/private/net/svcdlls/wkssvc/wsnames.h
@@ -0,0 +1,21 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ wsnames.h
+
+Abstract:
+
+ Private header file which defines the Workstation names.
+
+Author:
+
+ Rita Wong (ritaw) 06-May-1991
+
+Revision History:
+
+--*/
+
+#define WORKSTATION_INTERFACE_NAME TEXT("wkssvc")